Merge tag 'module-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rusty/linux-2.6-for-linus

Pull module patches from Rusty Russell, who really sells them:
 "Three trivial patches of no real utility.  Modules are boring."

But to make things slightly more exciting, he adds:
 "Fortunately David Howells is looking to change this, with his module
  signing patchset.  But that's for next merge window...

  Cheers,
  Rusty."

* tag 'module-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rusty/linux-2.6-for-linus:
  Guard check in module loader against integer overflow
  modpost: use proper kernel style for autogenerated files
  modpost: Stop grab_file() from leaking filedescriptors if fstat() fails
diff --git a/Documentation/00-INDEX b/Documentation/00-INDEX
index 2214f12..49c0513 100644
--- a/Documentation/00-INDEX
+++ b/Documentation/00-INDEX
@@ -218,8 +218,6 @@
 	- directory with info about Linux on Motorola 68k architecture.
 magic-number.txt
 	- list of magic numbers used to mark/protect kernel data structures.
-mca.txt
-	- info on supporting Micro Channel Architecture (e.g. PS/2) systems.
 md.txt
 	- info on boot arguments for the multiple devices driver.
 memory-barriers.txt
diff --git a/Documentation/ABI/testing/debugfs-pfo-nx-crypto b/Documentation/ABI/testing/debugfs-pfo-nx-crypto
new file mode 100644
index 0000000..685d5a4
--- /dev/null
+++ b/Documentation/ABI/testing/debugfs-pfo-nx-crypto
@@ -0,0 +1,45 @@
+What:		/sys/kernel/debug/nx-crypto/*
+Date:		March 2012
+KernelVersion:	3.4
+Contact:	Kent Yoder <key@linux.vnet.ibm.com>
+Description:
+
+  These debugfs interfaces are built by the nx-crypto driver, built in
+arch/powerpc/crypto/nx.
+
+Error Detection
+===============
+
+errors:
+- A u32 providing a total count of errors since the driver was loaded. The
+only errors counted here are those returned from the hcall, H_COP_OP.
+
+last_error:
+- The most recent non-zero return code from the H_COP_OP hcall. -EBUSY is not
+recorded here (the hcall will retry until -EBUSY goes away).
+
+last_error_pid:
+- The process ID of the process who received the most recent error from the
+hcall.
+
+Device Use
+==========
+
+aes_bytes:
+- The total number of bytes encrypted using AES in any of the driver's
+supported modes.
+
+aes_ops:
+- The total number of AES operations submitted to the hardware.
+
+sha256_bytes:
+- The total number of bytes hashed by the hardware using SHA-256.
+
+sha256_ops:
+- The total number of SHA-256 operations submitted to the hardware.
+
+sha512_bytes:
+- The total number of bytes hashed by the hardware using SHA-512.
+
+sha512_ops:
+- The total number of SHA-512 operations submitted to the hardware.
diff --git a/Documentation/ABI/testing/sysfs-devices-power b/Documentation/ABI/testing/sysfs-devices-power
index 840f7d6..45000f0 100644
--- a/Documentation/ABI/testing/sysfs-devices-power
+++ b/Documentation/ABI/testing/sysfs-devices-power
@@ -96,16 +96,26 @@
 		is read-only.  If the device is not enabled to wake up the
 		system from sleep states, this attribute is not present.
 
-What:		/sys/devices/.../power/wakeup_hit_count
-Date:		September 2010
+What:		/sys/devices/.../power/wakeup_abort_count
+Date:		February 2012
 Contact:	Rafael J. Wysocki <rjw@sisk.pl>
 Description:
-		The /sys/devices/.../wakeup_hit_count attribute contains the
+		The /sys/devices/.../wakeup_abort_count attribute contains the
 		number of times the processing of a wakeup event associated with
-		the device might prevent the system from entering a sleep state.
-		This attribute is read-only.  If the device is not enabled to
-		wake up the system from sleep states, this attribute is not
-		present.
+		the device might have aborted system transition into a sleep
+		state in progress.  This attribute is read-only.  If the device
+		is not enabled to wake up the system from sleep states, this
+		attribute is not present.
+
+What:		/sys/devices/.../power/wakeup_expire_count
+Date:		February 2012
+Contact:	Rafael J. Wysocki <rjw@sisk.pl>
+Description:
+		The /sys/devices/.../wakeup_expire_count attribute contains the
+		number of times a wakeup event associated with the device has
+		been reported with a timeout that expired.  This attribute is
+		read-only.  If the device is not enabled to wake up the system
+		from sleep states, this attribute is not present.
 
 What:		/sys/devices/.../power/wakeup_active
 Date:		September 2010
@@ -148,6 +158,17 @@
 		not enabled to wake up the system from sleep states, this
 		attribute is not present.
 
+What:		/sys/devices/.../power/wakeup_prevent_sleep_time_ms
+Date:		February 2012
+Contact:	Rafael J. Wysocki <rjw@sisk.pl>
+Description:
+		The /sys/devices/.../wakeup_prevent_sleep_time_ms attribute
+		contains the total time the device has been preventing
+		opportunistic transitions to sleep states from occuring.
+		This attribute is read-only.  If the device is not enabled to
+		wake up the system from sleep states, this attribute is not
+		present.
+
 What:		/sys/devices/.../power/autosuspend_delay_ms
 Date:		September 2010
 Contact:	Alan Stern <stern@rowland.harvard.edu>
diff --git a/Documentation/ABI/testing/sysfs-power b/Documentation/ABI/testing/sysfs-power
index b464d1276..31725ff 100644
--- a/Documentation/ABI/testing/sysfs-power
+++ b/Documentation/ABI/testing/sysfs-power
@@ -172,3 +172,62 @@
 
 		Reading from this file will display the current value, which is
 		set to 1 MB by default.
+
+What:		/sys/power/autosleep
+Date:		April 2012
+Contact:	Rafael J. Wysocki <rjw@sisk.pl>
+Description:
+		The /sys/power/autosleep file can be written one of the strings
+		returned by reads from /sys/power/state.  If that happens, a
+		work item attempting to trigger a transition of the system to
+		the sleep state represented by that string is queued up.  This
+		attempt will only succeed if there are no active wakeup sources
+		in the system at that time.  After every execution, regardless
+		of whether or not the attempt to put the system to sleep has
+		succeeded, the work item requeues itself until user space
+		writes "off" to /sys/power/autosleep.
+
+		Reading from this file causes the last string successfully
+		written to it to be returned.
+
+What:		/sys/power/wake_lock
+Date:		February 2012
+Contact:	Rafael J. Wysocki <rjw@sisk.pl>
+Description:
+		The /sys/power/wake_lock file allows user space to create
+		wakeup source objects and activate them on demand (if one of
+		those wakeup sources is active, reads from the
+		/sys/power/wakeup_count file block or return false).  When a
+		string without white space is written to /sys/power/wake_lock,
+		it will be assumed to represent a wakeup source name.  If there
+		is a wakeup source object with that name, it will be activated
+		(unless active already).  Otherwise, a new wakeup source object
+		will be registered, assigned the given name and activated.
+		If a string written to /sys/power/wake_lock contains white
+		space, the part of the string preceding the white space will be
+		regarded as a wakeup source name and handled as descrived above.
+		The other part of the string will be regarded as a timeout (in
+		nanoseconds) such that the wakeup source will be automatically
+		deactivated after it has expired.  The timeout, if present, is
+		set regardless of the current state of the wakeup source object
+		in question.
+
+		Reads from this file return a string consisting of the names of
+		wakeup sources created with the help of it that are active at
+		the moment, separated with spaces.
+
+
+What:		/sys/power/wake_unlock
+Date:		February 2012
+Contact:	Rafael J. Wysocki <rjw@sisk.pl>
+Description:
+		The /sys/power/wake_unlock file allows user space to deactivate
+		wakeup sources created with the help of /sys/power/wake_lock.
+		When a string is written to /sys/power/wake_unlock, it will be
+		assumed to represent the name of a wakeup source to deactivate.
+		If a wakeup source object of that name exists and is active at
+		the moment, it will be deactivated.
+
+		Reads from this file return a string consisting of the names of
+		wakeup sources created with the help of /sys/power/wake_lock
+		that are inactive at the moment, separated with spaces.
diff --git a/Documentation/DocBook/Makefile b/Documentation/DocBook/Makefile
index 66725a3..bc3d9f8c 100644
--- a/Documentation/DocBook/Makefile
+++ b/Documentation/DocBook/Makefile
@@ -6,7 +6,7 @@
 # To add a new book the only step required is to add the book to the
 # list of DOCBOOKS.
 
-DOCBOOKS := z8530book.xml mcabook.xml device-drivers.xml \
+DOCBOOKS := z8530book.xml device-drivers.xml \
 	    kernel-hacking.xml kernel-locking.xml deviceiobook.xml \
 	    writing_usb_driver.xml networking.xml \
 	    kernel-api.xml filesystems.xml lsm.xml usb.xml kgdb.xml \
diff --git a/Documentation/DocBook/kernel-api.tmpl b/Documentation/DocBook/kernel-api.tmpl
index 7160652..00687ee 100644
--- a/Documentation/DocBook/kernel-api.tmpl
+++ b/Documentation/DocBook/kernel-api.tmpl
@@ -212,19 +212,6 @@
      <sect1><title>PCI Hotplug Support Library</title>
 !Edrivers/pci/hotplug/pci_hotplug_core.c
      </sect1>
-     <sect1><title>MCA Architecture</title>
-	<sect2><title>MCA Device Functions</title>
-           <para>
-              Refer to the file arch/x86/kernel/mca_32.c for more information.
-           </para>
-<!-- FIXME: Removed for now since no structured comments in source
-X!Earch/x86/kernel/mca_32.c
--->
-	</sect2>
-	<sect2><title>MCA Bus DMA</title>
-!Iarch/x86/include/asm/mca_dma.h
-	</sect2>
-     </sect1>
   </chapter>
 
   <chapter id="firmware">
diff --git a/Documentation/DocBook/mcabook.tmpl b/Documentation/DocBook/mcabook.tmpl
deleted file mode 100644
index 467ccac..0000000
--- a/Documentation/DocBook/mcabook.tmpl
+++ /dev/null
@@ -1,107 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
-	"http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" []>
-
-<book id="MCAGuide">
- <bookinfo>
-  <title>MCA Driver Programming Interface</title>
-  
-  <authorgroup>
-   <author>
-    <firstname>Alan</firstname>
-    <surname>Cox</surname>
-    <affiliation>
-     <address>
-      <email>alan@lxorguk.ukuu.org.uk</email>
-     </address>
-    </affiliation>
-   </author>
-   <author>
-    <firstname>David</firstname>
-    <surname>Weinehall</surname>
-   </author>
-   <author>
-    <firstname>Chris</firstname>
-    <surname>Beauregard</surname>
-   </author>
-  </authorgroup>
-
-  <copyright>
-   <year>2000</year>
-   <holder>Alan Cox</holder>
-   <holder>David Weinehall</holder>
-   <holder>Chris Beauregard</holder>
-  </copyright>
-
-  <legalnotice>
-   <para>
-     This documentation is free software; you can redistribute
-     it and/or modify it under the terms of the GNU General Public
-     License as published by the Free Software Foundation; either
-     version 2 of the License, or (at your option) any later
-     version.
-   </para>
-      
-   <para>
-     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.
-   </para>
-      
-   <para>
-     You should have received a copy of the GNU General Public
-     License along with this program; if not, write to the Free
-     Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
-     MA 02111-1307 USA
-   </para>
-      
-   <para>
-     For more details see the file COPYING in the source
-     distribution of Linux.
-   </para>
-  </legalnotice>
- </bookinfo>
-
-<toc></toc>
-
-  <chapter id="intro">
-      <title>Introduction</title>
-  <para>
-	The MCA bus functions provide a generalised interface to find MCA
-	bus cards, to claim them for a driver, and to read and manipulate POS 
-	registers without being aware of the motherboard internals or 
-	certain deep magic specific to onboard devices.
-  </para>
-  <para>
-	The basic interface to the MCA bus devices is the slot. Each slot
-	is numbered and virtual slot numbers are assigned to the internal
-	devices. Using a pci_dev as other busses do does not really make
-	sense in the MCA context as the MCA bus resources require card
-	specific interpretation.
-  </para>
-  <para>
-	Finally the MCA bus functions provide a parallel set of DMA
-	functions mimicing the ISA bus DMA functions as closely as possible,
-	although also supporting the additional DMA functionality on the
-	MCA bus controllers.
-  </para>
-  </chapter>
-  <chapter id="bugs">
-     <title>Known Bugs And Assumptions</title>
-  <para>
-	None.	
-  </para>
-  </chapter>
-
-  <chapter id="pubfunctions">
-     <title>Public Functions Provided</title>
-!Edrivers/mca/mca-legacy.c
-  </chapter>
-
-  <chapter id="dmafunctions">
-     <title>DMA Functions Provided</title>
-!Iarch/x86/include/asm/mca_dma.h
-  </chapter>
-
-</book>
diff --git a/Documentation/devices.txt b/Documentation/devices.txt
index 5941f51..47a154f 100644
--- a/Documentation/devices.txt
+++ b/Documentation/devices.txt
@@ -847,13 +847,7 @@
 		    ...
 		 31 = /dev/tap15	16th Ethertap device
 
- 36 block	MCA ESDI hard disk
-		  0 = /dev/eda		First ESDI disk whole disk
-		 64 = /dev/edb		Second ESDI disk whole disk
-		    ...
-
-		Partitions are handled in the same way as IDE disks
-		(see major number 3).
+ 36 block	OBSOLETE (was MCA ESDI hard disk)
 
  37 char	IDE tape
 		  0 = /dev/ht0		First IDE tape
diff --git a/Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt b/Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt
new file mode 100644
index 0000000..e4acdd8
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt
@@ -0,0 +1,49 @@
+Freescale i.MX audio complex with SGTL5000 codec
+
+Required properties:
+- compatible : "fsl,imx-audio-sgtl5000"
+- model : The user-visible name of this sound complex
+- ssi-controller : The phandle of the i.MX SSI controller
+- audio-codec : The phandle of the SGTL5000 audio codec
+- audio-routing : A list of the connections between audio components.
+  Each entry is a pair of strings, the first being the connection's sink,
+  the second being the connection's source. Valid names could be power
+  supplies, SGTL5000 pins, and the jacks on the board:
+
+  Power supplies:
+   * Mic Bias
+
+  SGTL5000 pins:
+   * MIC_IN
+   * LINE_IN
+   * HP_OUT
+   * LINE_OUT
+
+  Board connectors:
+   * Mic Jack
+   * Line In Jack
+   * Headphone Jack
+   * Line Out Jack
+   * Ext Spk
+
+- mux-int-port : The internal port of the i.MX audio muxer (AUDMUX)
+- mux-ext-port : The external port of the i.MX audio muxer
+
+Note: The AUDMUX port numbering should start at 1, which is consistent with
+hardware manual.
+
+Example:
+
+sound {
+	compatible = "fsl,imx51-babbage-sgtl5000",
+		     "fsl,imx-audio-sgtl5000";
+	model = "imx51-babbage-sgtl5000";
+	ssi-controller = <&ssi1>;
+	audio-codec = <&sgtl5000>;
+	audio-routing =
+		"MIC_IN", "Mic Jack",
+		"Mic Jack", "Mic Bias",
+		"Headphone Jack", "HP_OUT";
+	mux-int-port = <1>;
+	mux-ext-port = <3>;
+};
diff --git a/Documentation/devicetree/bindings/sound/mxs-audio-sgtl5000.txt b/Documentation/devicetree/bindings/sound/mxs-audio-sgtl5000.txt
new file mode 100644
index 0000000..601c518
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/mxs-audio-sgtl5000.txt
@@ -0,0 +1,17 @@
+* Freescale MXS audio complex with SGTL5000 codec
+
+Required properties:
+- compatible: "fsl,mxs-audio-sgtl5000"
+- model: The user-visible name of this sound complex
+- saif-controllers: The phandle list of the MXS SAIF controller
+- audio-codec: The phandle of the SGTL5000 audio codec
+
+Example:
+
+sound {
+	compatible = "fsl,imx28-evk-sgtl5000",
+		     "fsl,mxs-audio-sgtl5000";
+	model = "imx28-evk-sgtl5000";
+	saif-controllers = <&saif0 &saif1>;
+	audio-codec = <&sgtl5000>;
+};
diff --git a/Documentation/devicetree/bindings/sound/mxs-saif.txt b/Documentation/devicetree/bindings/sound/mxs-saif.txt
new file mode 100644
index 0000000..c37ba61
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/mxs-saif.txt
@@ -0,0 +1,36 @@
+* Freescale MXS Serial Audio Interface (SAIF)
+
+Required properties:
+- compatible: Should be "fsl,<chip>-saif"
+- reg: Should contain registers location and length
+- interrupts: Should contain ERROR and DMA interrupts
+- fsl,saif-dma-channel: APBX DMA channel for the SAIF
+
+Optional properties:
+- fsl,saif-master: phandle to the master SAIF.  It's only required for
+  the slave SAIF.
+
+Note: Each SAIF controller should have an alias correctly numbered
+in "aliases" node.
+
+Example:
+
+aliases {
+	saif0 = &saif0;
+	saif1 = &saif1;
+};
+
+saif0: saif@80042000 {
+	compatible = "fsl,imx28-saif";
+	reg = <0x80042000 2000>;
+	interrupts = <59 80>;
+	fsl,saif-dma-channel = <4>;
+};
+
+saif1: saif@80046000 {
+	compatible = "fsl,imx28-saif";
+	reg = <0x80046000 2000>;
+	interrupts = <58 81>;
+	fsl,saif-dma-channel = <5>;
+	fsl,saif-master = <&saif0>;
+};
diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra30-ahub.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra30-ahub.txt
new file mode 100644
index 0000000..1ac7b16
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/nvidia,tegra30-ahub.txt
@@ -0,0 +1,32 @@
+NVIDIA Tegra30 AHUB (Audio Hub)
+
+Required properties:
+- compatible : "nvidia,tegra30-ahub"
+- reg : Should contain the register physical address and length for each of
+  the AHUB's APBIF registers and the AHUB's own registers.
+- interrupts : Should contain AHUB interrupt
+- nvidia,dma-request-selector : The Tegra DMA controller's phandle and
+  request selector for the first APBIF channel.
+- ranges : The bus address mapping for the configlink register bus.
+  Can be empty since the mapping is 1:1.
+- #address-cells : For the configlink bus. Should be <1>;
+- #size-cells : For the configlink bus. Should be <1>.
+
+AHUB client modules need to specify the IDs of their CIFs (Client InterFaces).
+For RX CIFs, the numbers indicate the register number within AHUB routing
+register space (APBIF 0..3 RX, I2S 0..5 RX, DAM 0..2 RX 0..1, SPDIF RX 0..1).
+For TX CIFs, the numbers indicate the bit position within the AHUB routing
+registers (APBIF 0..3 TX, I2S 0..5 TX, DAM 0..2 TX, SPDIF TX 0..1).
+
+Example:
+
+ahub@70080000 {
+	compatible = "nvidia,tegra30-ahub";
+	reg = <0x70080000 0x200 0x70080200 0x100>;
+	interrupts = < 0 103 0x04 >;
+	nvidia,dma-request-selector = <&apbdma 1>;
+
+	ranges;
+	#address-cells = <1>;
+	#size-cells = <1>;
+};
diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra30-i2s.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra30-i2s.txt
new file mode 100644
index 0000000..dfa6c03
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/nvidia,tegra30-i2s.txt
@@ -0,0 +1,15 @@
+NVIDIA Tegra30 I2S controller
+
+Required properties:
+- compatible : "nvidia,tegra30-i2s"
+- reg : Should contain I2S registers location and length
+- nvidia,ahub-cif-ids : The list of AHUB CIF IDs for this port, rx (playback)
+  first, tx (capture) second. See nvidia,tegra30-ahub.txt for values.
+
+Example:
+
+i2s@70002800 {
+	compatible = "nvidia,tegra30-i2s";
+	reg = <0x70080300 0x100>;
+	nvidia,ahub-cif-ids = <4 4>;
+};
diff --git a/Documentation/devicetree/bindings/sound/tegra-audio-trimslice.txt b/Documentation/devicetree/bindings/sound/tegra-audio-trimslice.txt
new file mode 100644
index 0000000..04b14cf
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/tegra-audio-trimslice.txt
@@ -0,0 +1,14 @@
+NVIDIA Tegra audio complex for TrimSlice
+
+Required properties:
+- compatible : "nvidia,tegra-audio-trimslice"
+- nvidia,i2s-controller : The phandle of the Tegra I2S1 controller
+- nvidia,audio-codec : The phandle of the WM8903 audio codec
+
+Example:
+
+sound {
+	compatible = "nvidia,tegra-audio-trimslice";
+	nvidia,i2s-controller = <&tegra_i2s1>;
+	nvidia,audio-codec = <&codec>;
+};
diff --git a/Documentation/devicetree/bindings/sound/tegra-audio-wm8753.txt b/Documentation/devicetree/bindings/sound/tegra-audio-wm8753.txt
new file mode 100644
index 0000000..c4dd39c
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/tegra-audio-wm8753.txt
@@ -0,0 +1,54 @@
+NVIDIA Tegra audio complex
+
+Required properties:
+- compatible : "nvidia,tegra-audio-wm8753"
+- nvidia,model : The user-visible name of this sound complex.
+- nvidia,audio-routing : A list of the connections between audio components.
+  Each entry is a pair of strings, the first being the connection's sink,
+  the second being the connection's source. Valid names for sources and
+  sinks are the WM8753's pins, and the jacks on the board:
+
+  WM8753 pins:
+
+  * LOUT1
+  * LOUT2
+  * ROUT1
+  * ROUT2
+  * MONO1
+  * MONO2
+  * OUT3
+  * OUT4
+  * LINE1
+  * LINE2
+  * RXP
+  * RXN
+  * ACIN
+  * ACOP
+  * MIC1N
+  * MIC1
+  * MIC2N
+  * MIC2
+  * Mic Bias
+
+  Board connectors:
+
+  * Headphone Jack
+  * Mic Jack
+
+- nvidia,i2s-controller : The phandle of the Tegra I2S1 controller
+- nvidia,audio-codec : The phandle of the WM8753 audio codec
+Example:
+
+sound {
+	compatible = "nvidia,tegra-audio-wm8753-whistler",
+		     "nvidia,tegra-audio-wm8753"
+	nvidia,model = "tegra-wm8753-harmony";
+
+	nvidia,audio-routing =
+		"Headphone Jack", "LOUT1",
+		"Headphone Jack", "ROUT1";
+
+	nvidia,i2s-controller = <&i2s1>;
+	nvidia,audio-codec = <&wm8753>;
+};
+
diff --git a/Documentation/devicetree/bindings/watchdog/pnx4008-wdt.txt b/Documentation/devicetree/bindings/watchdog/pnx4008-wdt.txt
new file mode 100644
index 0000000..7c7f688
--- /dev/null
+++ b/Documentation/devicetree/bindings/watchdog/pnx4008-wdt.txt
@@ -0,0 +1,13 @@
+* NXP PNX watchdog timer
+
+Required properties:
+- compatible: must be "nxp,pnx4008-wdt"
+- reg: physical base address of the controller and length of memory mapped
+  region.
+
+Example:
+
+	watchdog@4003C000 {
+		compatible = "nxp,pnx4008-wdt";
+		reg = <0x4003C000 0x1000>;
+	};
diff --git a/Documentation/eisa.txt b/Documentation/eisa.txt
index 38cf0c7..a55e491 100644
--- a/Documentation/eisa.txt
+++ b/Documentation/eisa.txt
@@ -179,7 +179,7 @@
 
 Converting an EISA driver to the new API mostly involves *deleting*
 code (since probing is now in the core EISA code). Unfortunately, most
-drivers share their probing routine between ISA, MCA and EISA. Special
+drivers share their probing routine between ISA, and EISA. Special
 care must be taken when ripping out the EISA code, so other busses
 won't suffer from these surgical strikes...
 
diff --git a/Documentation/hwmon/ina2xx b/Documentation/hwmon/ina2xx
new file mode 100644
index 0000000..f50a6cc
--- /dev/null
+++ b/Documentation/hwmon/ina2xx
@@ -0,0 +1,29 @@
+Kernel driver ina2xx
+====================
+
+Supported chips:
+  * Texas Instruments INA219
+    Prefix: 'ina219'
+    Addresses: I2C 0x40 - 0x4f
+    Datasheet: Publicly available at the Texas Instruments website
+               http://www.ti.com/
+
+  * Texas Instruments INA226
+    Prefix: 'ina226'
+    Addresses: I2C 0x40 - 0x4f
+    Datasheet: Publicly available at the Texas Instruments website
+               http://www.ti.com/
+
+Author: Lothar Felten <l-felten@ti.com>
+
+Description
+-----------
+
+The INA219 is a high-side current shunt and power monitor with an I2C
+interface. The INA219 monitors both shunt drop and supply voltage, with
+programmable conversion times and filtering.
+
+The INA226 is a current shunt and power monitor with an I2C interface.
+The INA226 monitors both a shunt voltage drop and bus supply voltage.
+
+The shunt value in micro-ohms can be set via platform data.
diff --git a/Documentation/hwmon/it87 b/Documentation/hwmon/it87
index fba7455..87850d8 100644
--- a/Documentation/hwmon/it87
+++ b/Documentation/hwmon/it87
@@ -30,6 +30,14 @@
     Prefix: 'it8728'
     Addresses scanned: from Super I/O config space (8 I/O ports)
     Datasheet: Not publicly available
+  * IT8782F
+    Prefix: 'it8782'
+    Addresses scanned: from Super I/O config space (8 I/O ports)
+    Datasheet: Not publicly available
+  * IT8783E/F
+    Prefix: 'it8783'
+    Addresses scanned: from Super I/O config space (8 I/O ports)
+    Datasheet: Not publicly available
   * SiS950   [clone of IT8705F]
     Prefix: 'it87'
     Addresses scanned: from Super I/O config space (8 I/O ports)
@@ -75,7 +83,8 @@
 -----------
 
 This driver implements support for the IT8705F, IT8712F, IT8716F,
-IT8718F, IT8720F, IT8721F, IT8726F, IT8728F, IT8758E and SiS950 chips.
+IT8718F, IT8720F, IT8721F, IT8726F, IT8728F, IT8758E, IT8781F, IT8782F,
+IT8783E/F, and SiS950 chips.
 
 These chips are 'Super I/O chips', supporting floppy disks, infrared ports,
 joysticks and other miscellaneous stuff. For hardware monitoring, they
@@ -99,11 +108,11 @@
 have support for 2 additional fans. The additional fans are supported by the
 driver.
 
-The IT8716F, IT8718F, IT8720F and IT8721F/IT8758E, and late IT8712F and
-IT8705F also have optional 16-bit tachometer counters for fans 1 to 3. This
-is better (no more fan clock divider mess) but not compatible with the older
-chips and revisions. The 16-bit tachometer mode is enabled by the driver when
-one of the above chips is detected.
+The IT8716F, IT8718F, IT8720F, IT8721F/IT8758E, IT8782F, IT8783E/F, and late
+IT8712F and IT8705F also have optional 16-bit tachometer counters for fans 1 to
+3. This is better (no more fan clock divider mess) but not compatible with the
+older chips and revisions. The 16-bit tachometer mode is enabled by the driver
+when one of the above chips is detected.
 
 The IT8726F is just bit enhanced IT8716F with additional hardware
 for AMD power sequencing. Therefore the chip will appear as IT8716F
@@ -131,9 +140,10 @@
 0.016 volt (except IT8721F/IT8758E and IT8728F: 0.012 volt.) The battery
 voltage in8 does not have limit registers.
 
-On the IT8721F/IT8758E, some voltage inputs are internal and scaled inside
-the chip (in7, in8 and optionally in3). The driver handles this transparently
-so user-space doesn't have to care.
+On the IT8721F/IT8758E, IT8782F, and IT8783E/F, some voltage inputs are
+internal and scaled inside the chip (in7 (optional for IT8782F and IT8783E/F),
+in8 and optionally in3). The driver handles this transparently so user-space
+doesn't have to care.
 
 The VID lines (IT8712F/IT8716F/IT8718F/IT8720F) encode the core voltage value:
 the voltage level your processor should work with. This is hardcoded by
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 62aba89..5b6e584 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -70,7 +70,6 @@
 	M68k	M68k architecture is enabled.
 			These options have more detailed description inside of
 			Documentation/m68k/kernel-options.txt.
-	MCA	MCA bus support is enabled.
 	MDA	MDA console support is enabled.
 	MIPS	MIPS architecture is enabled.
 	MOUSE	Appropriate mouse support is enabled.
@@ -2463,6 +2462,8 @@
 
 	resume=		[SWSUSP]
 			Specify the partition device for software suspend
+			Format:
+			{/dev/<dev> | PARTUUID=<uuid> | <int>:<int> | <hex>}
 
 	resume_offset=	[SWSUSP]
 			Specify the offset from the beginning of the partition
diff --git a/Documentation/mca.txt b/Documentation/mca.txt
deleted file mode 100644
index dfd130c..0000000
--- a/Documentation/mca.txt
+++ /dev/null
@@ -1,313 +0,0 @@
-i386 Micro Channel Architecture Support
-=======================================
-
-MCA support is enabled using the CONFIG_MCA define.  A machine with a MCA
-bus will have the kernel variable MCA_bus set, assuming the BIOS feature
-bits are set properly (see arch/i386/boot/setup.S for information on
-how this detection is done).
-
-Adapter Detection
-=================
-
-The ideal MCA adapter detection is done through the use of the
-Programmable Option Select registers.  Generic functions for doing
-this have been added in include/linux/mca.h and arch/x86/kernel/mca_32.c.
-Everything needed to detect adapters and read (and write) configuration
-information is there.  A number of MCA-specific drivers already use
-this.  The typical probe code looks like the following:
-
-	#include <linux/mca.h>
-
-	unsigned char pos2, pos3, pos4, pos5;
-	struct net_device* dev;
-	int slot;
-
-	if( MCA_bus ) {
-		slot = mca_find_adapter( ADAPTER_ID, 0 );
-		if( slot == MCA_NOTFOUND ) {
-			return -ENODEV;
-		}
-		/* optional - see below */
-		mca_set_adapter_name( slot, "adapter name & description" );
-		mca_set_adapter_procfn( slot, dev_getinfo, dev );
-
-		/* read the POS registers.  Most devices only use 2 and 3 */
-		pos2 = mca_read_stored_pos( slot, 2 );
-		pos3 = mca_read_stored_pos( slot, 3 );
-		pos4 = mca_read_stored_pos( slot, 4 );
-		pos5 = mca_read_stored_pos( slot, 5 );
-	} else {
-		return -ENODEV;
-	}
-
-	/* extract configuration from pos[2345] and set everything up */
-
-Loadable modules should modify this to test that the specified IRQ and
-IO ports (plus whatever other stuff) match.  See 3c523.c for example
-code (actually, smc-mca.c has a slightly more complex example that can
-handle a list of adapter ids).
-
-Keep in mind that devices should never directly access the POS registers
-(via inb(), outb(), etc).  While it's generally safe, there is a small
-potential for blowing up hardware when it's done at the wrong time.
-Furthermore, accessing a POS register disables a device temporarily.
-This is usually okay during startup, but do _you_ want to rely on it?
-During initial configuration, mca_init() reads all the POS registers
-into memory.  mca_read_stored_pos() accesses that data.  mca_read_pos()
-and mca_write_pos() are also available for (safer) direct POS access,
-but their use is _highly_ discouraged.  mca_write_pos() is particularly
-dangerous, as it is possible for adapters to be put in inconsistent
-states (i.e. sharing IO address, etc) and may result in crashes, toasted
-hardware, and blindness.
-
-User level drivers (such as the AGX X server) can use /proc/mca/pos to
-find adapters (see below).
-
-Some MCA adapters can also be detected via the usual ISA-style device
-probing (many SCSI adapters, for example).  This sort of thing is highly
-discouraged.  Perfectly good information is available telling you what's
-there, so there's no excuse for messing with random IO ports.  However,
-we MCA people still appreciate any ISA-style driver that will work with
-our hardware.  You take what you can get...
-
-Level-Triggered Interrupts
-==========================
-
-Because MCA uses level-triggered interrupts, a few problems arise with
-what might best be described as the ISA mindset and its effects on
-drivers.  These sorts of problems are expected to become less common as
-more people use shared IRQs on PCI machines.
-
-In general, an interrupt must be acknowledged not only at the ICU (which
-is done automagically by the kernel), but at the device level.  In
-particular, IRQ 0 must be reset after a timer interrupt (now done in
-arch/x86/kernel/time.c) or the first timer interrupt hangs the system.
-There were also problems with the 1.3.x floppy drivers, but that seems
-to have been fixed.
-
-IRQs are also shareable, and most MCA-specific devices should be coded
-with shared IRQs in mind.
-
-/proc/mca
-=========
-
-/proc/mca is a directory containing various files for adapters and
-other stuff.
-
-	/proc/mca/pos		Straight listing of POS registers
-	/proc/mca/slot[1-8]	Information on adapter in specific slot
-	/proc/mca/video		Same for integrated video
-	/proc/mca/scsi		Same for integrated SCSI
-	/proc/mca/machine	Machine information
-
-See Appendix A for a sample.
-
-Device drivers can easily add their own information function for
-specific slots (including integrated ones) via the
-mca_set_adapter_procfn() call.  Drivers that support this are ESDI, IBM
-SCSI, and 3c523.  If a device is also a module, make sure that the proc
-function is removed in the module cleanup.  This will require storing
-the slot information in a private structure somewhere.  See the 3c523
-driver for details.
-
-Your typical proc function will look something like this:
-
-	static int
-	dev_getinfo( char* buf, int slot, void* d ) {
-		struct net_device* dev = (struct net_device*) d;
-		int len = 0;
-
-		len += sprintf( buf+len, "Device: %s\n", dev->name );
-		len += sprintf( buf+len, "IRQ: %d\n", dev->irq );
-		len += sprintf( buf+len, "IO Port: %#lx-%#lx\n", ... );
-		...
-
-		return len;
-	}
-
-Some of the standard MCA information will already be printed, so don't
-bother repeating it.  Don't try putting in more than 3K of information.
-
-Enable this function with:
-	mca_set_adapter_procfn( slot, dev_getinfo, dev );
-
-Disable it with:
-	mca_set_adapter_procfn( slot, NULL, NULL );
-
-It is also recommended that, even if you don't write a proc function, to
-set the name of the adapter (i.e. "PS/2 ESDI Controller") via
-mca_set_adapter_name( int slot, char* name ).
-
-MCA Device Drivers
-==================
-
-Currently, there are a number of MCA-specific device drivers.
-
-1) PS/2 SCSI
-	drivers/scsi/ibmmca.c
-	drivers/scsi/ibmmca.h
-   The driver for the IBM SCSI subsystem.  Includes both integrated
-   controllers and adapter cards.  May require command-line arg
-   "ibmmcascsi=io_port" to force detection of an adapter.  If you have a
-   machine with a front-panel display (i.e. model 95), you can use
-   "ibmmcascsi=display" to enable a drive activity indicator.
-
-2) 3c523
-	drivers/net/3c523.c
-	drivers/net/3c523.h
-   3Com 3c523 Etherlink/MC ethernet driver.
-
-3) SMC Ultra/MCA and IBM Adapter/A
-	drivers/net/smc-mca.c
-	drivers/net/smc-mca.h
-	Driver for the MCA version of the SMC Ultra and various other
-	OEM'ed and work-alike cards (Elite, Adapter/A, etc).
-
-4) NE/2
-	driver/net/ne2.c
-	driver/net/ne2.h
-	The NE/2 is the MCA version of the NE2000.  This may not work
-	with clones that have a different adapter id than the original
-	NE/2.
-
-5) Future Domain MCS-600/700, OEM'd IBM Fast SCSI Adapter/A and
-   Reply Sound Blaster/SCSI (SCSI part)
-	Better support for these cards than the driver for ISA.
-   Supports multiple cards with IRQ sharing.
-
-Also added boot time option of scsi-probe, which can do reordering of
-SCSI host adapters. This will direct the kernel on the order which
-SCSI adapter should be detected. Example:
-  scsi-probe=ibmmca,fd_mcs,adaptec1542,buslogic
-
-The serial drivers were modified to support the extended IO port range
-of the typical MCA system (also #ifdef CONFIG_MCA).
-
-The following devices work with existing drivers:
-1) Token-ring
-2) Future Domain SCSI (MCS-600, MCS-700, not MCS-350, OEM'ed IBM SCSI)
-3) Adaptec 1640 SCSI (using the aha1542 driver)
-4) Bustek/Buslogic SCSI (various)
-5) Probably all Arcnet cards.
-6) Some, possibly all, MCA IDE controllers.
-7) 3Com 3c529 (MCA version of 3c509) (patched)
-
-8) Intel EtherExpressMC  (patched version)
-   You need to have CONFIG_MCA defined to have EtherExpressMC support.
-9) Reply Sound Blaster/SCSI (SB part) (patched version)
-
-Bugs & Other Weirdness
-======================
-
-NMIs tend to occur with MCA machines because of various hardware
-weirdness, bus timeouts, and many other non-critical things.  Some basic
-code to handle them (inspired by the NetBSD MCA code) has been added to
-detect the guilty device, but it's pretty incomplete.  If NMIs are a
-persistent problem (on some model 70 or 80s, they occur every couple
-shell commands), the CONFIG_IGNORE_NMI flag will take care of that.
-
-Various Pentium machines have had serious problems with the FPU test in
-bugs.h.  Basically, the machine hangs after the HLT test.  This occurs,
-as far as we know, on the Pentium-equipped 85s, 95s, and some PC Servers.
-The PCI/MCA PC 750s are fine as far as I can tell.  The ``mca-pentium''
-boot-prompt flag will disable the FPU bug check if this is a problem
-with your machine.
-
-The model 80 has a raft of problems that are just too weird and unique
-to get into here.  Some people have no trouble while others have nothing
-but problems.  I'd suspect some problems are related to the age of the
-average 80 and accompanying hardware deterioration, although others
-are definitely design problems with the hardware.  Among the problems
-include SCSI controller problems, ESDI controller problems, and serious
-screw-ups in the floppy controller.  Oh, and the parallel port is also
-pretty flaky.  There were about 5 or 6 different model 80 motherboards
-produced to fix various obscure problems.  As far as I know, it's pretty
-much impossible to tell which bugs a particular model 80 has (other than
-triggering them, that is).
-
-Drivers are required for some MCA memory adapters.  If you're suddenly
-short a few megs of RAM, this might be the reason.  The (I think) Enhanced
-Memory Adapter commonly found on the model 70 is one.  There's a very
-alpha driver floating around, but it's pretty ugly (disassembled from
-the DOS driver, actually).  See the MCA Linux web page (URL below)
-for more current memory info.
-
-The Thinkpad 700 and 720 will work, but various components are either
-non-functional, flaky, or we don't know anything about them.  The
-graphics controller is supposed to be some WD, but we can't get things
-working properly.  The PCMCIA slots don't seem to work.  Ditto for APM.
-The serial ports work, but detection seems to be flaky.
-
-Credits
-=======
-A whole pile of people have contributed to the MCA code.  I'd include
-their names here, but I don't have a list handy.  Check the MCA Linux
-home page (URL below) for a perpetually out-of-date list.
-
-=====================================================================
-MCA Linux Home Page: http://www.dgmicro.com/mca/
-
-Christophe Beauregard
-chrisb@truespectra.com
-cpbeaure@calum.csclub.uwaterloo.ca
-
-=====================================================================
-Appendix A: Sample /proc/mca
-
-This is from my model 8595.  Slot 1 contains the standard IBM SCSI
-adapter, slot 3 is an Adaptec AHA-1640, slot 5 is a XGA-1 video adapter,
-and slot 7 is the 3c523 Etherlink/MC.
-
-/proc/mca/machine:
-Model Id: 0xf8
-Submodel Id: 0x14
-BIOS Revision: 0x5
-
-/proc/mca/pos:
-Slot 1: ff 8e f1 fc a0 ff ff ff  IBM SCSI Adapter w/Cache
-Slot 2: ff ff ff ff ff ff ff ff  
-Slot 3: 1f 0f 81 3b bf b6 ff ff  
-Slot 4: ff ff ff ff ff ff ff ff  
-Slot 5: db 8f 1d 5e fd c0 00 00  
-Slot 6: ff ff ff ff ff ff ff ff  
-Slot 7: 42 60 ff 08 ff ff ff ff  3Com 3c523 Etherlink/MC
-Slot 8: ff ff ff ff ff ff ff ff  
-Video : ff ff ff ff ff ff ff ff  
-SCSI  : ff ff ff ff ff ff ff ff  
-
-/proc/mca/slot1:
-Slot: 1
-Adapter Name: IBM SCSI Adapter w/Cache
-Id: 8eff
-Enabled: Yes
-POS: ff 8e f1 fc a0 ff ff ff 
-Subsystem PUN: 7
-Detected at boot: Yes
-
-/proc/mca/slot3:
-Slot: 3
-Adapter Name: Unknown
-Id: 0f1f
-Enabled: Yes
-POS: 1f 0f 81 3b bf b6 ff ff 
-
-/proc/mca/slot5:
-Slot: 5
-Adapter Name: Unknown
-Id: 8fdb
-Enabled: Yes
-POS: db 8f 1d 5e fd c0 00 00 
-
-/proc/mca/slot7:
-Slot: 7
-Adapter Name: 3Com 3c523 Etherlink/MC
-Id: 6042
-Enabled: Yes
-POS: 42 60 ff 08 ff ff ff ff 
-Revision: 0xe
-IRQ: 9
-IO Address: 0x3300-0x3308
-Memory: 0xd8000-0xdbfff
-Transceiver: External
-Device: eth0
-Hardware Address: 02 60 8c 45 c4 2a
diff --git a/Documentation/power/suspend-and-cpuhotplug.txt b/Documentation/power/suspend-and-cpuhotplug.txt
index f28f9a6..e13dafc 100644
--- a/Documentation/power/suspend-and-cpuhotplug.txt
+++ b/Documentation/power/suspend-and-cpuhotplug.txt
@@ -29,7 +29,7 @@
 
                                   Write 'mem' to
                                 /sys/power/state
-                                    syfs file
+                                    sysfs file
                                         |
                                         v
                                Acquire pm_mutex lock
diff --git a/Documentation/scsi/00-INDEX b/Documentation/scsi/00-INDEX
index b7dd650..9b0787f 100644
--- a/Documentation/scsi/00-INDEX
+++ b/Documentation/scsi/00-INDEX
@@ -56,8 +56,6 @@
 	- info on driver for NCR5380 and NCR53c400 based adapters
 hptiop.txt
 	- HIGHPOINT ROCKETRAID 3xxx RAID DRIVER
-ibmmca.txt
-	- info on driver for IBM adapters with MCA bus
 in2000.txt
 	- info on in2000 driver
 libsas.txt
diff --git a/Documentation/scsi/ibmmca.txt b/Documentation/scsi/ibmmca.txt
deleted file mode 100644
index ac41a9f..0000000
--- a/Documentation/scsi/ibmmca.txt
+++ /dev/null
@@ -1,1402 +0,0 @@
-
-               -=< The IBM Microchannel SCSI-Subsystem >=-
-	       
-	                 for the IBM PS/2 series
-		 
-	  	   Low Level Software-Driver for Linux
-		 
-     Copyright (c) 1995 Strom Systems, Inc. under the terms of the GNU 
-  General Public License. Originally written by Martin Kolinek, December 1995.
-   Officially modified and maintained by Michael Lang since January 1999.
-	   
- 	                       Version 4.0a
-	
-   Last update: January 3, 2001
-   
-   Before you Start
-   ----------------
-   This is the common README.ibmmca file for all driver releases of the 
-   IBM MCA SCSI driver for Linux. Please note, that driver releases 4.0
-   or newer do not work with kernel versions older than 2.4.0, while driver
-   versions older than 4.0 do not work with kernels 2.4.0 or later! If you
-   try to compile your kernel with the wrong driver source, the 
-   compilation is aborted and you get a corresponding error message. This is
-   no bug in the driver; it prevents you from using the wrong source code
-   with the wrong kernel version.
-
-   Authors of this Driver
-   ----------------------
-    - Chris Beauregard (improvement of the SCSI-device mapping by the driver)
-    - Martin Kolinek (origin, first release of this driver)
-    - Klaus Kudielka (multiple SCSI-host management/detection, adaption to
-                      Linux Kernel 2.1.x, module support)
-    - Michael Lang (assigning original pun/lun mapping, dynamical ldn
-                    assignment, rewritten adapter detection, this file, 
-		    patches, official driver maintenance and subsequent 
-		    debugging, related with the driver)
-
-   Table of Contents
-   -----------------
-   1 Abstract
-   2 Driver Description
-     2.1  IBM SCSI-Subsystem Detection
-     2.2  Physical Units, Logical Units, and Logical Devices
-     2.3  SCSI-Device Recognition and dynamical ldn Assignment
-     2.4  SCSI-Device Order
-     2.5  Regular SCSI-Command-Processing
-     2.6  Abort & Reset Commands
-     2.7  Disk Geometry
-     2.8  Kernel Boot Option
-     2.9  Driver Module Support
-     2.10 Multiple Hostadapter Support
-     2.11 /proc/scsi-Filesystem Information
-     2.12 /proc/mca-Filesystem Information
-     2.13 Supported IBM SCSI-Subsystems
-     2.14 Linux Kernel Versions
-   3 Code History
-   4 To do
-   5 Users' Manual
-     5.1 Commandline Parameters
-     5.2 Troubleshooting
-     5.3 Bug reports
-     5.4 Support WWW-page
-   6 References
-   7 Credits to
-     7.1 People
-     7.2 Sponsors & Supporters
-   8 Trademarks
-   9 Disclaimer
-
-                              * * *
-
-   1 Abstract
-   ----------
-   This README-file describes the IBM SCSI-subsystem low level driver for
-   Linux. The descriptions which were formerly kept in the source code have
-   been taken out of this file to simplify the codes readability. The driver
-   description has been updated, as most of the former description was already
-   quite outdated. The history of the driver development is also kept inside
-   here. Multiple historical developments have been summarized to shorten the
-   text size a bit. At the end of this file you can find a small manual for
-   this driver and hints to get it running on your machine.
-
-   2 Driver Description
-   --------------------
-   2.1 IBM SCSI-Subsystem Detection
-   --------------------------------
-   This is done in the ibmmca_detect() function. It first checks, if the
-   Microchannel-bus support is enabled, as the IBM SCSI-subsystem needs the
-   Microchannel. In a next step, a free interrupt is chosen and the main
-   interrupt handler is connected to it to handle answers of the SCSI-
-   subsystem(s). If the F/W SCSI-adapter is forced by the BIOS to use IRQ11
-   instead of IRQ14, IRQ11 is used for the IBM SCSI-2 F/W adapter. In a 
-   further step it is checked, if the adapter gets detected by force from
-   the kernel commandline, where the I/O port and the SCSI-subsystem id can 
-   be specified. The next step checks if there is an integrated SCSI-subsystem
-   installed. This register area is fixed through all IBM PS/2 MCA-machines 
-   and appears as something like a virtual slot 10 of the MCA-bus. On most
-   PS/2 machines, the POS registers of slot 10 are set to 0xff or 0x00 if not
-   integrated SCSI-controller is available. But on certain PS/2s, like model 
-   9595, this slot 10 is used to store other information which at earlier
-   stage confused the driver and resulted in the detection of some ghost-SCSI. 
-   If POS-register 2 and 3 are not 0x00 and not 0xff, but all other POS
-   registers are either 0xff or 0x00, there must be an integrated SCSI-
-   subsystem present and it will be registered as IBM Integrated SCSI-
-   Subsystem. The next step checks, if there is a slot-adapter installed on 
-   the MCA-bus. To get this, the first two POS-registers, that represent the 
-   adapter ID are checked. If they fit to one of the ids, stored in the 
-   adapter list, a SCSI-subsystem is assumed to be found in a slot and will be 
-   registered. This check is done through all possible MCA-bus slots to allow 
-   more than one SCSI-adapter to be present in the PS/2-system and this is 
-   already the first point of problems. Looking into the technical reference 
-   manual for the IBM PS/2 common interfaces, the POS2 register must have 
-   different interpretation of its single bits to avoid overlapping I/O
-   regions. While one can assume, that the integrated subsystem has a fix 
-   I/O-address at 0x3540 - 0x3547, further installed IBM SCSI-adapters must 
-   use a different I/O-address. This is expressed by bit 1 to 3 of POS2 
-   (multiplied by 8 + 0x3540). Bits 2 and 3 are reserved for the integrated 
-   subsystem, but not for the adapters! The following list shows, how the 
-   bits of POS2 and POS3 should be interpreted.
-   
-   The POS2-register of all PS/2 models' integrated SCSI-subsystems has the 
-   following interpretation of bits:
-                           Bit 7 - 4 : Chip Revision ID (Release)
-                           Bit 3 - 2 : Reserved
-                           Bit 1     : 8k NVRAM Disabled
-                           Bit 0     : Chip Enable (EN-Signal)
-   The POS3-register is interpreted as follows (for most IBM SCSI-subsys.):
-                           Bit 7 - 5 : SCSI ID
-                           Bit 4 - 0 : Reserved = 0
-   The slot-adapters have different interpretation of these bits. The IBM SCSI
-   adapter (w/Cache) and the IBM SCSI-2 F/W adapter use the following
-   interpretation of the POS2 register:
-                           Bit 7 - 4 : ROM Segment Address Select
-			   Bit 3 - 1 : Adapter I/O Address Select (*8+0x3540)
-			   Bit 0     : Adapter Enable (EN-Signal)
-   and for the POS3 register:
-                           Bit 7 - 5 : SCSI ID 
-			   Bit 4     : Fairness Enable (SCSI ID3 f. F/W)
-			   Bit 3 - 0 : Arbitration Level
-   The most modern product of the series is the IBM SCSI-2 F/W adapter, it 
-   allows dual-bus SCSI and SCSI-wide addressing, which means, PUNs may be
-   between 0 and 15. Here, Bit 4 is the high-order bit of the 4-bit wide
-   adapter PUN expression. In short words, this means, that IBM PS/2 machines 
-   can only support 1 single integrated subsystem by default. Additional
-   slot-adapters get ports assigned by the automatic configuration tool.
-
-   One day I found a patch in ibmmca_detect(), forcing the I/O-address to be 
-   0x3540 for integrated SCSI-subsystems, there was a remark placed, that on 
-   integrated IBM SCSI-subsystems of model 56, the POS2 register was showing 5.
-   This means, that really for these models, POS2 has to be interpreted
-   sticking to the technical reference guide. In this case, the bit 2 (4) is 
-   a reserved bit and may not be interpreted. These differences between the 
-   adapters and the integrated controllers are taken into account by the 
-   detection routine of the driver on from version >3.0g. 
-
-   Every time, a SCSI-subsystem is discovered, the ibmmca_register() function
-   is called. This function checks first, if the requested area for the I/O-
-   address of this SCSI-subsystem is still available and assigns this I/O-
-   area to the SCSI-subsystem. There are always 8 sequential I/O-addresses
-   taken for each individual SCSI-subsystem found, which are:
-   
-     Offset            Type                  Permissions
-       0     Command Interface Register 1    Read/Write
-       1     Command Interface Register 2    Read/Write
-       2     Command Interface Register 3    Read/Write
-       3     Command Interface Register 4    Read/Write
-       4     Attention Register              Read/Write
-       5     Basic Control Register          Read/Write
-       6     Interrupt Status Register       Read
-       7     Basic Status Register           Read
-   
-   After the I/O-address range is assigned, the host-adapter is assigned
-   to a local structure which keeps all adapter information needed for the
-   driver itself and the mid- and higher-level SCSI-drivers. The SCSI pun/lun
-   and the adapters' ldn tables are initialized and get probed afterwards by
-   the check_devices() function. If no further adapters are found, 
-   ibmmca_detect() quits.
-   
-   2.2 Physical Units, Logical Units, and Logical Devices
-   ------------------------------------------------------
-   There can be up to 56 devices on the SCSI bus (besides the adapter):
-   there are up to 7 "physical units" (each identified by physical unit 
-   number or pun, also called the scsi id, this is the number you select
-   with hardware jumpers), and each physical unit can have up to 8 
-   "logical units" (each identified by logical unit number, or lun, 
-   between 0 and 7). The IBM SCSI-2 F/W adapter offers this on up to two
-   busses and provides support for 30 logical devices at the same time, where
-   in wide-addressing mode you can have 16 puns with 32 luns on each device.
-   This section describes the handling of devices on non-F/W adapters.
-   Just imagine, that you can have 16 * 32 = 512 devices on a F/W adapter
-   which means a lot of possible devices for such a small machine.
-
-   Typically the adapter has pun=7, so puns of other physical units
-   are between 0 and 6(15). On a wide-adapter a pun higher than 7 is
-   possible, but is normally not used. Almost all physical units have only 
-   one logical unit, with lun=0. A CD-ROM jukebox would be an example of a 
-   physical unit with more than one logical unit.
-
-   The embedded microprocessor of the IBM SCSI-subsystem hides the complex
-   two-dimensional (pun,lun) organization from the operating system.
-   When the machine is powered-up (or rebooted), the embedded microprocessor 
-   checks, on its own, all 56 possible (pun,lun) combinations, and the first 
-   15 devices found are assigned into a one-dimensional array of so-called 
-   "logical devices", identified by "logical device numbers" or ldn. The last 
-   ldn=15 is reserved for the subsystem itself. Wide adapters may have 
-   to check up to 15 * 8 = 120 pun/lun combinations.
-   
-   2.3 SCSI-Device Recognition and Dynamical ldn Assignment
-   --------------------------------------------------------
-   One consequence of information hiding is that the real (pun,lun)    
-   numbers are also hidden. The two possibilities to get around this problem
-   are to offer fake pun/lun combinations to the operating system or to 
-   delete the whole mapping of the adapter and to reassign the ldns, using
-   the immediate assign command of the SCSI-subsystem for probing through
-   all possible pun/lun combinations.  An ldn is a "logical device number"
-   which is used by IBM SCSI-subsystems to access some valid SCSI-device.
-   At the beginning of the development of this driver, the following approach 
-   was used:
-   
-   First, the driver checked the ldn's (0 to 6) to find out which ldn's
-   have devices assigned. This was done by the functions check_devices() and
-   device_exists(). The interrupt handler has a special paragraph of code
-   (see local_checking_phase_flag) to assist in the checking. Assume, for
-   example, that three logical devices were found assigned at ldn 0, 1, 2.
-   These are presented to the upper layer of Linux SCSI driver
-   as devices with bogus (pun, lun) equal to (0,0), (1,0), (2,0). 
-   On the other hand, if the upper layer issues a command to device
-   say (4,0), this driver returns DID_NO_CONNECT error.
-
-   In a second step of the driver development, the following improvement has
-   been applied: The first approach limited the number of devices to 7, far
-   fewer than the 15 that it could use, then it just mapped ldn -> 
-   (ldn/8,ldn%8) for pun,lun.  We ended up with a real mishmash of puns
-   and luns, but it all seemed to work.
-
-   The latest development, which is implemented from the driver version 3.0
-   and later, realizes the device recognition in the following way:
-   The physical SCSI-devices on the SCSI-bus are probed via immediate_assign- 
-   and device_inquiry-commands, that is all implemented in a completely new
-   made check_devices() subroutine. This delivers an exact map of the physical
-   SCSI-world that is now stored in the get_scsi[][]-array. This means,
-   that the once hidden pun,lun assignment is now known to this driver.
-   It no longer believes in default-settings of the subsystem and maps all
-   ldns to existing pun,lun "by foot". This assures full control of the ldn
-   mapping and allows dynamical remapping of ldns to different pun,lun, if
-   there are more SCSI-devices installed than ldns available (n>15). The
-   ldns from 0 to 6 get 'hardwired' by this driver to puns 0 to 7 at lun=0,
-   excluding the pun of the subsystem. This assures, that at least simple 
-   SCSI-installations have optimum access-speed and are not touched by
-   dynamical remapping. The ldns 7 to 14 are put to existing devices with 
-   lun>0 or to non-existing devices, in order to satisfy the subsystem, if 
-   there are less than 15 SCSI-devices connected. In the case of more than 15 
-   devices, the dynamical mapping goes active. If the get_scsi[][] reports a 
-   device to be existent, but it has no ldn assigned, it gets an ldn out of 7
-   to 14. The numbers are assigned in cyclic order, therefore it takes 8 
-   dynamical reassignments on the SCSI-devices until a certain device 
-   loses its ldn again. This assures that dynamical remapping is avoided 
-   during intense I/O between up to 15 SCSI-devices (means pun,lun 
-   combinations). A further advantage of this method is that people who
-   build their kernel without probing on all luns will get what they expect,
-   because the driver just won't assign everything with lun>0 when 
-   multiple lun probing is inactive.
- 
-   2.4 SCSI-Device Order
-   ---------------------
-   Because of the now correct recognition of physical pun,lun, and 
-   their report to mid-level- and higher-level-drivers, the new reported puns
-   can be different from the old, faked puns. Therefore, Linux will eventually
-   change /dev/sdXXX assignments and prompt you for corrupted superblock
-   repair on boottime. In this case DO NOT PANIC, YOUR DISKS ARE STILL OK!!!
-   You have to reboot (CTRL-D) with an old kernel and set the /etc/fstab-file
-   entries right. After that, the system should come up as errorfree as before.
-   If your boot-partition is not coming up, also edit the /etc/lilo.conf-file
-   in a Linux session booted on old kernel and run lilo before reboot. Check
-   lilo.conf anyway to get boot on other partitions with foreign OSes right
-   again. But there exists a feature of this driver that allows you to change
-   the assignment order of the SCSI-devices by flipping the PUN-assignment.
-   See the next paragraph for a description.
- 
-   The problem for this is, that Linux does not assign the SCSI-devices in the
-   way as described in the ANSI-SCSI-standard. Linux assigns /dev/sda to 
-   the device with at minimum id 0. But the first drive should be at id 6,
-   because for historical reasons, drive at id 6 has, by hardware, the highest
-   priority and a drive at id 0 the lowest. IBM was one of the rare producers,
-   where the BIOS assigns drives belonging to the ANSI-SCSI-standard. Most 
-   other producers' BIOS does not (I think even Adaptec-BIOS). The 
-   IBMMCA_SCSI_ORDER_STANDARD flag, which you set while configuring the
-   kernel enables to choose the preferred way of SCSI-device-assignment. 
-   Defining this flag would result in Linux determining the devices in the 
-   same order as DOS and OS/2 does on your MCA-machine. This is also standard 
-   on most industrial computers and OSes, like e.g. OS-9. Leaving this flag 
-   undefined will get your devices ordered in the default way of Linux. See 
-   also the remarks of Chris Beauregard from Dec 15, 1997 and the followups 
-   in section 3.
-   
-   2.5 Regular SCSI-Command-Processing
-   -----------------------------------
-   Only three functions get involved: ibmmca_queuecommand(), issue_cmd(),
-   and interrupt_handler().
-
-   The upper layer issues a scsi command by calling function 
-   ibmmca_queuecommand(). This function fills a "subsystem control block"
-   (scb) and calls a local function issue_cmd(), which writes a scb 
-   command into subsystem I/O ports. Once the scb command is carried out, 
-   the interrupt_handler() is invoked. If a device is determined to be 
-   existent and it has not assigned any ldn, it gets one dynamically.
-   For this, the whole stuff is done in ibmmca_queuecommand().
-
-   2.6 Abort & Reset Commands
-   --------------------------
-   These are implemented with busy waiting for interrupt to arrive.
-   ibmmca_reset() and ibmmca_abort() do not work sufficiently well
-   up to now and need still a lot of development work. This seems
-   to be a problem with other low-level SCSI drivers too, however
-   this should be no excuse.
-
-   2.7 Disk Geometry
-   -----------------
-   The ibmmca_biosparams() function should return the same disk geometry 
-   as the bios. This is needed for fdisk, etc. The returned geometry is 
-   certainly correct for disks smaller than 1 gigabyte. In the meantime,
-   it has been proved, that this works fine even with disks larger than
-   1 gigabyte.
-
-   2.8 Kernel Boot Option
-   ----------------------
-   The function ibmmca_scsi_setup() is called if option ibmmcascsi=n 
-   is passed to the kernel. See file linux/init/main.c for details.
-   
-   2.9 Driver Module Support
-   -------------------------
-   Is implemented and tested by K. Kudielka. This could probably not work
-   on kernels <2.1.0.
-  
-   2.10 Multiple Hostadapter Support
-   ---------------------------------
-   This driver supports up to eight interfaces of type IBM-SCSI-Subsystem. 
-   Integrated-, and MCA-adapters are automatically recognized. Unrecognizable
-   IBM-SCSI-Subsystem interfaces can be specified as kernel-parameters.
- 
-   2.11 /proc/scsi-Filesystem Information
-   --------------------------------------
-   Information about the driver condition is given in 
-   /proc/scsi/ibmmca/<host_no>. ibmmca_proc_info() provides this information.
-   
-   This table is quite informative for interested users. It shows the load
-   of commands on the subsystem and whether you are running the bypassed
-   (software) or integrated (hardware) SCSI-command set (see below). The
-   amount of accesses is shown. Read, write, modeselect is shown separately
-   in order to help debugging problems with CD-ROMs or tapedrives.
-   
-   The following table shows the list of 15 logical device numbers, that are
-   used by the SCSI-subsystem. The load on each ldn is shown in the table,
-   again, read and write commands are split. The last column shows the amount
-   of reassignments, that have been applied to the ldns, if you have more than
-   15 pun/lun combinations available on the SCSI-bus.
-   
-   The last two tables show the pun/lun map and the positions of the ldns
-   on this pun/lun map. This may change during operation, when a ldn is
-   reassigned to another pun/lun combination. If the necessity for dynamical
-   assignments is set to 'no', the ldn structure keeps static.
-   
-   2.12 /proc/mca-Filesystem Information
-   -------------------------------------
-   The slot-file contains all default entries and in addition chip and I/O-
-   address information of the SCSI-subsystem. This information is provided
-   by ibmmca_getinfo().
-   
-   2.13 Supported IBM SCSI-Subsystems
-   ----------------------------------
-   The following IBM SCSI-subsystems are supported by this driver:
-   
-     - IBM Fast/Wide SCSI-2 Adapter
-     - IBM 7568 Industrial Computer SCSI Adapter w/Cache
-     - IBM Expansion Unit SCSI Controller
-     - IBM SCSI Adapter w/Cache
-     - IBM SCSI Adapter
-     - IBM Integrated SCSI Controller
-     - All clones, 100% compatible with the chipset and subsystem command
-       system of IBM SCSI-adapters (forced detection)
-     
-   2.14 Linux Kernel Versions
-   --------------------------
-   The IBM SCSI-subsystem low level driver is prepared to be used with
-   all versions of Linux between 2.0.x and 2.4.x. The compatibility checks
-   are fully implemented up from version 3.1e of the driver. This means, that
-   you just need the latest ibmmca.h and ibmmca.c file and copy it in the
-   linux/drivers/scsi directory. The code is automatically adapted during 
-   kernel compilation. This is different from kernel 2.4.0! Here version 
-   4.0 or later of the driver must be used for kernel 2.4.0 or later. Version
-   4.0 or later does not work together with older kernels! Driver versions
-   older than 4.0 do not work together with kernel 2.4.0 or later. They work
-   on all older kernels.
-
-   3 Code History
-   --------------
-   Jan 15 1996:  First public release.
-   - Martin Kolinek
-
-   Jan 23 1996:  Scrapped code which reassigned scsi devices to logical
-   device numbers. Instead, the existing assignment (created
-   when the machine is powered-up or rebooted) is used. 
-   A side effect is that the upper layer of Linux SCSI 
-   device driver gets bogus scsi ids (this is benign), 
-   and also the hard disks are ordered under Linux the 
-   same way as they are under dos (i.e., C: disk is sda, 
-   D: disk is sdb, etc.).
-   - Martin Kolinek
-
-   I think that the CD-ROM is now detected only if a CD is 
-   inside CD_ROM while Linux boots. This can be fixed later,
-   once the driver works on all types of PS/2's.
-   - Martin Kolinek
-
-   Feb 7 1996:   Modified biosparam function. Fixed the CD-ROM detection. 
-   For now, devices other than harddisk and CD_ROM are 
-   ignored. Temporarily modified abort() function 
-   to behave like reset().
-   - Martin Kolinek
-
-   Mar 31 1996:  The integrated scsi subsystem is correctly found
-   in PS/2 models 56,57, but not in model 76. Therefore
-   the ibmmca_scsi_setup() function has been added today.
-   This function allows the user to force detection of
-   scsi subsystem. The kernel option has format
-   ibmmcascsi=n
-   where n is the scsi_id (pun) of the subsystem. Most likely, n is 7.
-   - Martin Kolinek
-
-   Aug 21 1996:  Modified the code which maps ldns to (pun,0).  It was
-   insufficient for those of us with CD-ROM changers.
-   - Chris Beauregard
- 
-   Dec 14 1996: More improvements to the ldn mapping.  See check_devices
-   for details.  Did more fiddling with the integrated SCSI detection,
-   but I think it's ultimately hopeless without actually testing the
-   model of the machine.  The 56, 57, 76 and 95 (ultimedia) all have
-   different integrated SCSI register configurations.  However, the 56
-   and 57 are the only ones that have problems with forced detection.
-   - Chris Beauregard
- 
-   Mar 8-16 1997: Modified driver to run as a module and to support 
-   multiple adapters. A structure, called ibmmca_hostdata, is now
-   present, containing all the variables, that were once only
-   available for one single adapter. The find_subsystem-routine has vanished.
-   The hardware recognition is now done in ibmmca_detect directly.
-   This routine checks for presence of MCA-bus, checks the interrupt
-   level and continues with checking the installed hardware.
-   Certain PS/2-models do not recognize a SCSI-subsystem automatically.
-   Hence, the setup defined by command-line-parameters is checked first.
-   Thereafter, the routine probes for an integrated SCSI-subsystem.
-   Finally, adapters are checked. This method has the advantage to cover all
-   possible combinations of multiple SCSI-subsystems on one MCA-board. Up to
-   eight SCSI-subsystems can be recognized and announced to the upper-level
-   drivers with this improvement. A set of defines made changes to other
-   routines as small as possible.
-   - Klaus Kudielka
-   
-   May 30 1997: (v1.5b)
-   1) SCSI-command capability enlarged by the recognition of MODE_SELECT.
-      This needs the RD-Bit to be disabled on IM_OTHER_SCSI_CMD_CMD which 
-      allows data to be written from the system to the device. It is a
-      necessary step to be allowed to set blocksize of SCSI-tape-drives and 
-      the tape-speed, without confusing the SCSI-Subsystem.
-   2) The recognition of a tape is included in the check_devices routine.
-      This is done by checking for TYPE_TAPE, that is already defined in
-      the kernel-scsi-environment. The markup of a tape is done in the 
-      global ldn_is_tape[] array. If the entry on index ldn 
-      is 1, there is a tapedrive connected.
-   3) The ldn_is_tape[] array is necessary to distinguish between tape- and 
-      other devices. Fixed blocklength devices should not cause a problem
-      with the SCB-command for read and write in the ibmmca_queuecommand
-      subroutine. Therefore, I only derivate the READ_XX, WRITE_XX for
-      the tape-devices, as recommended by IBM in this Technical Reference,
-      mentioned below. (IBM recommends to avoid using the read/write of the
-      subsystem, but the fact was, that read/write causes a command error from
-      the subsystem and this causes kernel-panic.)
-   4) In addition, I propose to use the ldn instead of a fix char for the
-      display of PS2_DISK_LED_ON(). On 95, one can distinguish between the
-      devices that are accessed. It shows activity and easyfies debugging.   
-   The tape-support has been tested with a SONY SDT-5200 and a HP DDS-2
-   (I do not know yet the type). Optimization and CD-ROM audio-support, 
-   I am working on ...
-   - Michael Lang
-   
-   June 19 1997: (v1.6b)
-   1) Submitting the extra-array ldn_is_tape[] -> to the local ld[]
-      device-array. 
-   2) CD-ROM Audio-Play seems to work now.
-   3) When using DDS-2 (120M) DAT-Tapes, mtst shows still density-code
-      0x13 for ordinary DDS (61000 BPM) instead 0x24 for DDS-2. This appears 
-      also on Adaptec 2940 adaptor in a PCI-System. Therefore, I assume that 
-      the problem is independent of the low-level-driver/bus-architecture.
-   4) Hexadecimal ldn on PS/2-95 LED-display.
-   5) Fixing of the PS/2-LED on/off that it works right with tapedrives and
-      does not confuse the disk_rw_in_progress counter.
-   - Michael Lang
-  
-   June 21 1997: (v1.7b)
-   1) Adding of a proc_info routine to inform in /proc/scsi/ibmmca/<host> the
-      outer-world about operational load statistics on the different ldns,
-      seen by the driver. Everybody that has more than one IBM-SCSI should
-      test this, because I only have one and cannot see what happens with more
-      than one IBM-SCSI hosts.
-   2) Definition of a driver version-number to have a better recognition of 
-      the source when there are existing too much releases that may confuse
-      the user, when reading about release-specific problems. Up to know,
-      I calculated the version-number to be 1.7. Because we are in BETA-test
-      yet, it is today 1.7b.
-   3) Sorry for the heavy bug I programmed on June 19 1997! After that, the
-      CD-ROM did not work any more! The C7-command was a fake impression
-      I got while programming. Now, the READ and WRITE commands for CD-ROM are
-      no longer running over the subsystem, but just over 
-      IM_OTHER_SCSI_CMD_CMD. On my observations (PS/2-95), now CD-ROM mounts
-      much faster(!) and hopefully all fancy multimedia-functions, like direct
-      digital recording from audio-CDs also work. (I tried it with cdda2wav
-      from the cdwtools-package and it filled up the harddisk immediately :-).)
-      To easify boolean logics, a further local device-type in ld[], called
-      is_cdrom has been included.
-   4) If one uses a SCSI-device of unsupported type/commands, one
-      immediately runs into a kernel-panic caused by Command Error. To better
-      understand which SCSI-command caused the problem, I extended this
-      specific panic-message slightly.
-   - Michael Lang
- 
-   June 25 1997: (v1.8b)
-   1) Some cosmetic changes for the handling of SCSI-device-types.
-      Now, also CD-Burners / WORMs and SCSI-scanners should work. For
-      MO-drives I have no experience, therefore not yet supported.
-      In logical_devices I changed from different type-variables to one
-      called 'device_type' where the values, corresponding to scsi.h,
-      of a SCSI-device are stored.
-   2) There existed a small bug, that maps a device, coming after a SCSI-tape
-      wrong. Therefore, e.g. a CD-ROM changer would have been mapped wrong
-      -> problem removed.
-   3) Extension of the logical_device structure. Now it contains also device,
-      vendor and revision-level of a SCSI-device for internal usage.
-   - Michael Lang
-
-   June 26-29 1997: (v2.0b)
-   1) The release number 2.0b is necessary because of the completely new done
-      recognition and handling of SCSI-devices with the adapter. As I got
-      from Chris the hint, that the subsystem can reassign ldns dynamically,
-      I remembered this immediate_assign-command, I found once in the handbook.
-      Now, the driver first kills all ldn assignments that are set by default
-      on the SCSI-subsystem. After that, it probes on all puns and luns for
-      devices by going through all combinations with immediate_assign and
-      probing for devices, using device_inquiry. The found physical(!) pun,lun
-      structure is stored in get_scsi[][] as device types. This is followed
-      by the assignment of all ldns to existing SCSI-devices. If more ldns
-      than devices are available, they are assigned to non existing pun,lun
-      combinations to satisfy the adapter. With this, the dynamical mapping
-      was possible to implement. (For further info see the text in the 
-      source code and in the description below. Read the description
-      below BEFORE installing this driver on your system!)
-   2) Changed the name IBMMCA_DRIVER_VERSION to IBMMCA_SCSI_DRIVER_VERSION.
-   3) The LED-display shows on PS/2-95 no longer the ldn, but the SCSI-ID
-      (pun) of the accessed SCSI-device. This is now senseful, because the 
-      pun known within the driver is exactly the pun of the physical device
-      and no longer a fake one.
-   4) The /proc/scsi/ibmmca/<host_no> consists now of the first part, where
-      hit-statistics of ldns is shown and a second part, where the maps of 
-      physical and logical SCSI-devices are displayed. This could be very 
-      interesting, when one is using more than 15 SCSI-devices in order to 
-      follow the dynamical remapping of ldns.
-   - Michael Lang
- 
-   June 26-29 1997: (v2.0b-1)
-   1) I forgot to switch the local_checking_phase_flag to 1 and back to 0
-      in the dynamical remapping part in ibmmca_queuecommand for the 
-      device_exist routine. Sorry.
-   - Michael Lang
- 
-   July 1-13 1997: (v3.0b,c)
-   1) Merging of the driver-developments of Klaus Kudielka and Michael Lang 
-      in order to get a optimum and unified driver-release for the 
-      IBM-SCSI-Subsystem-Adapter(s).
-         For people, using the Kernel-release >=2.1.0, module-support should 
-      be no problem. For users, running under <2.1.0, module-support may not 
-      work, because the methods have changed between 2.0.x and 2.1.x.
-   2) Added some more effective statistics for /proc-output.
-   3) Change typecasting at necessary points from (unsigned long) to
-      virt_to_bus().
-   4) Included #if... at special points to have specific adaption of the
-      driver to kernel 2.0.x and 2.1.x. It should therefore also run with 
-      later releases.
-   5) Magneto-Optical drives and medium-changers are also recognized, now.
-      Therefore, we have a completely gapfree recognition of all SCSI-
-      device-types, that are known by Linux up to kernel 2.1.31.
-   6) The flag SCSI_IBMMCA_DEV_RESET has been inserted. If it is set within
-      the configuration, each connected SCSI-device will get a reset command
-      during boottime. This can be necessary for some special SCSI-devices.
-      This flag should be included in Config.in.
-      (See also the new Config.in file.)
-   Probable next improvement: bad disk handler.
-   - Michael Lang
- 
-   Sept 14 1997: (v3.0c)
-   1) Some debugging and speed optimization applied.
-   - Michael Lang
-
-   Dec 15, 1997
-    - chrisb@truespectra.com
-    - made the front panel display thingy optional, specified from the
-    command-line via ibmmcascsi=display.  Along the lines of the /LED
-    option for the OS/2 driver.
-    - fixed small bug in the LED display that would hang some machines.
-    - reversed ordering of the drives (using the
-    IBMMCA_SCSI_ORDER_STANDARD define).  This is necessary for two main
-    reasons:
-	- users who've already installed Linux won't be screwed.  Keep
-	in mind that not everyone is a kernel hacker.
-	- be consistent with the BIOS ordering of the drives.  In the
-	BIOS, id 6 is C:, id 0 might be D:.  With this scheme, they'd be
-	backwards.  This confuses the crap out of those heathens who've
-	got a impure Linux installation (which, <wince>, I'm one of).
-    This whole problem arises because IBM is actually non-standard with
-    the id to BIOS mappings.  You'll find, in fdomain.c, a similar
-    comment about a few FD BIOS revisions.  The Linux (and apparently
-    industry) standard is that C: maps to scsi id (0,0).  Let's stick
-    with that standard.
-    - Since this is technically a branch of my own, I changed the
-    version number to 3.0e-cpb.
-
-   Jan 17, 1998: (v3.0f)
-   1) Addition of some statistical info for /proc in proc_info.
-   2) Taking care of the SCSI-assignment problem, dealed by Chris at Dec 15
-      1997. In fact, IBM is right, concerning the assignment of SCSI-devices 
-      to driveletters. It is conform to the ANSI-definition of the SCSI-
-      standard to assign drive C: to SCSI-id 6, because it is the highest
-      hardware priority after the hostadapter (that has still today by
-      default everywhere id 7). Also realtime-operating systems that I use, 
-      like LynxOS and OS9, which are quite industrial systems use top-down
-      numbering of the harddisks, that is also starting at id 6. Now, one
-      sits a bit between two chairs. On one hand side, using the define
-      IBMMCA_SCSI_ORDER_STANDARD makes Linux assigning disks conform to
-      the IBM- and ANSI-SCSI-standard and keeps this driver downward
-      compatible to older releases, on the other hand side, people is quite
-      habituated in believing that C: is assigned to (0,0) and much other
-      SCSI-BIOS do so. Therefore, I moved the IBMMCA_SCSI_ORDER_STANDARD 
-      define out of the driver and put it into Config.in as subitem of 
-      'IBM SCSI support'. A help, added to Documentation/Configure.help 
-      explains the differences between saying 'y' or 'n' to the user, when 
-      IBMMCA_SCSI_ORDER_STANDARD prompts, so the ordinary user is enabled to 
-      choose the way of assignment, depending on his own situation and gusto.
-   3) Adapted SCSI_IBMMCA_DEV_RESET to the local naming convention, so it is
-      now called IBMMCA_SCSI_DEV_RESET.
-   4) Optimization of proc_info and its subroutines.
-   5) Added more in-source-comments and extended the driver description by
-      some explanation about the SCSI-device-assignment problem.
-   - Michael Lang
-   
-   Jan 18, 1998: (v3.0g)
-   1) Correcting names to be absolutely conform to the later 2.1.x releases.
-      This is necessary for 
-            IBMMCA_SCSI_DEV_RESET -> CONFIG_IBMMCA_SCSI_DEV_RESET
-            IBMMCA_SCSI_ORDER_STANDARD -> CONFIG_IBMMCA_SCSI_ORDER_STANDARD
-   - Michael Lang
- 
-   Jan 18, 1999: (v3.1 MCA-team internal)
-   1) The multiple hosts structure is accessed from every subroutine, so there
-      is no longer the address of the device structure passed from function
-      to function, but only the hostindex. A call by value, nothing more. This
-      should really be understood by the compiler and the subsystem should get
-      the right values and addresses.
-   2) The SCSI-subsystem detection was not complete and quite hugely buggy up
-      to now, compared to the technical manual. The interpretation of the pos2
-      register is not as assumed by people before, therefore, I dropped a note
-      in the ibmmca_detect function to show the registers' interpretation.
-      The pos-registers of integrated SCSI-subsystems do not contain any 
-      information concerning the IO-port offset, really. Instead, they contain
-      some info about the adapter, the chip, the NVRAM .... The I/O-port is
-      fixed to 0x3540 - 0x3547. There can be more than one adapters in the 
-      slots and they get an offset for the I/O area in order to get their own
-      I/O-address area. See chapter 2 for detailed description. At least, the 
-      detection should now work right, even on models other than 95. The 95ers
-      came happily around the bug, as their pos2 register contains always 0 
-      in the critical area. Reserved bits are not allowed to be interpreted, 
-      therefore, IBM is allowed to set those bits as they like and they may 
-      really vary between different PS/2 models. So, now, no interpretation 
-      of reserved bits - hopefully no trouble here anymore.
-   3) The command error, which you may get on models 55, 56, 57, 70, 77 and
-      P70 may have been caused by the fact, that adapters of older design do
-      not like sending commands to non-existing SCSI-devices and will react
-      with a command error as a sign of protest. While this error is not
-      present on IBM SCSI Adapter w/cache, it appears on IBM Integrated SCSI
-      Adapters. Therefore, I implemented a workaround to forgive those 
-      adapters their protests, but it is marked up in the statistics, so
-      after a successful boot, you can see in /proc/scsi/ibmmca/<host_number>
-      how often the command errors have been forgiven to the SCSI-subsystem.
-      If the number is bigger than 0, you have a SCSI subsystem of older
-      design, what should no longer matter.
-   4) ibmmca_getinfo() has been adapted very carefully, so it shows in the 
-      slotn file really, what is senseful to be presented.
-   5) ibmmca_register() has been extended in its parameter list in order to
-      pass the right name of the SCSI-adapter to Linux.
-   - Michael Lang
-
-   Feb 6, 1999: (v3.1)
-   1) Finally, after some 3.1Beta-releases, the 3.1 release. Sorry, for 
-      the delayed release, but it was not finished with the release of 
-      Kernel 2.2.0.
-   - Michael Lang
-   
-   Feb 10, 1999 (v3.1)
-   1) Added a new commandline parameter called 'bypass' in order to bypass
-      every integrated subsystem SCSI-command consequently in case of
-      troubles.
-   2) Concatenated read_capacity requests to the harddisks. It gave a lot
-      of troubles with some controllers and after I wanted to apply some
-      extensions, it jumped out in the same situation, on my w/cache, as like 
-      on D. Weinehalls' Model 56, having integrated SCSI. This gave me the 
-      decisive hint to move the code-part out and declare it global. Now
-      it seems to work far better and more stable. Let us see what
-      the world thinks of it...
-   3) By the way, only Sony DAT-drives seem to show density code 0x13. A
-      test with a HP drive gave right results, so the problem is vendor-
-      specific and not a problem of the OS or the driver.
-   - Michael Lang
-   
-   Feb 18, 1999 (v3.1d)
-   1) The abort command and the reset function have been checked for 
-      inconsistencies. From the logical point of thinking, they work
-      at their optimum, now, but as the subsystem does not answer with an
-      interrupt, abort never finishes, sigh...
-   2) Everything, that is accessed by a busmaster request from the adapter
-      is now declared as global variable, even the return-buffer in the
-      local checking phase. This assures, that no accesses to undefined memory
-      areas are performed.
-   3) In ibmmca.h, the line unchecked_isa_dma is added with 1 in order to
-      avoid memory-pointers for the areas higher than 16MByte in order to
-      be sure, it also works on 16-Bit Microchannel bus systems.
-   4) A lot of small things have been found, but nothing that endangered the
-      driver operations. Just it should be more stable, now.
-   - Michael Lang
-      
-   Feb 20, 1999 (v3.1e)
-   1) I took the warning from the Linux Kernel Hackers Guide serious and 
-      checked the cmd->result return value to the done-function very carefully.
-      It is obvious, that the IBM SCSI only delivers the tsb.dev_status, if
-      some error appeared, else it is undefined. Now, this is fixed. Before
-      any SCB command gets queued, the tsb.dev_status is set to 0, so the 
-      cmd->result won't screw up Linux higher level drivers.
-   2) The reset-function has slightly improved. This is still planned for 
-      abort. During the abort and the reset function, no interrupts are 
-      allowed. This is however quite hard to cope with, so the INT-status
-      register is read. When the interrupt gets queued, one can find its
-      status immediately on that register and is enabled to continue in the
-      reset function. I had no chance to test this really, only in a bogus 
-      situation, I got this function running, but the situation was too much
-      worse for Linux :-(, so tests will continue.
-   3) Buffers got now consistent. No open address mapping, as before and
-      therefore no further troubles with the unassigned memory segmentation
-      faults that scrambled probes on 95XX series and even on 85XX series,
-      when the kernel is done in a not so perfectly fitting way.
-   4) Spontaneous interrupts from the subsystem, appearing without any
-      command previously queued are answered with a DID_BAD_INTR result.
-   5) Taken into account ZP Gus' proposals to reverse the SCSI-device
-      scan order. As it does not work on Kernel 2.1.x or 2.2.x, as proposed
-      by him, I implemented it in a slightly derived way, which offers in 
-      addition more flexibility.
-   - Michael Lang
-
-   Apr 23, 2000 (v3.2pre1)
-   1) During a very long time, I collected a huge amount of bug reports from
-      various people, trying really quite different things on their SCSI-
-      PS/2s. Today, all these bug reports are taken into account and should be
-      mostly solved. The major topics were:
-      - Driver crashes during boottime by no obvious reason.
-      - Driver panics while the midlevel-SCSI-driver is trying to inquire
-        the SCSI-device properties, even though hardware is in perfect state.
-      - Displayed info for the various slot-cards is interpreted wrong.
-      The main reasons for the crashes were two:
-      1) The commands to check for device information like INQUIRY, 
-         TEST_UNIT_READY, REQUEST_SENSE and MODE_SENSE cause the devices
-	 to deliver information of up to 255 bytes. Midlevel drivers offer
-	 1024 bytes of space for the answer, but the IBM-SCSI-adapters do
-	 not accept this, as they stick quite near to ANSI-SCSI and report
-	 a COMMAND_ERROR message which causes the driver to panic. The main
-	 problem was located around the INQUIRY command. Now, for all the
-	 mentioned commands, the buffersize sent to the adapter is at 
-	 maximum 255 which seems to be a quite reasonable solution. 
-	 TEST_UNIT_READY gets a buffersize of 0 to make sure that no 
-	 data is transferred in order to avoid any possible command failure.
-      2) On unsuccessful TEST_UNIT_READY, the mid-level driver has to send
-         a REQUEST_SENSE in order to see where the problem is located. This
-	 REQUEST_SENSE may have various length in its answer-buffer. IBM
-	 SCSI-subsystems report a command failure if the returned buffersize
-	 is different from the sent buffersize, but this can be suppressed by
-	 a special bit, which is now done and problems seem to be solved.
-   2) Code adaption to all kernel-releases. Now, the 3.2 code compiles on 
-      2.0.x, 2.1.x, 2.2.x and 2.3.x kernel releases without any code-changes.
-   3) Commandline-parameters are recognized again, even under Kernel 2.3.x or
-      higher.
-   - Michael Lang   
-
-   April 27, 2000 (v3.2pre2)
-   1) Bypassed commands get read by the adapter by one cycle instead of two.
-      This increases SCSI-performance.
-   2) Synchronous datatransfer is provided for sure to be 5 MHz on older
-      SCSI and 10 MHz on internal F/W SCSI-adapter.
-   3) New commandline parameters allow to force the adapter to slow down while
-      in synchronous transfer. Could be helpful for very old devices.
-   - Michael Lang
-   
-   June 2, 2000 (v3.2pre5)
-   1) Added Jim Shorney's contribution to make the activity indicator
-      flashing in addition to the LED-alphanumeric display-panel on
-      models 95A. To be enabled to choose this feature freely, a new
-      commandline parameter is added, called 'activity'.
-   2) Added the READ_CONTROL bit for test_unit_ready SCSI-command.
-   3) Added some suppress_exception bits to read_device_capacity and
-      all device_inquiry occurrences in the driver code.
-   4) Complaints about the various KERNEL_VERSION implementations are
-      taken into account. Every local_LinuxKernelVersion occurrence is
-      now replaced by KERNEL_VERSION, defined in linux/version.h. 
-      Corresponding changes were applied to ibmmca.h, too. This was a
-      contribution to all kernel-parts by Philipp Hahn.
-   - Michael Lang
-   
-   July 17, 2000 (v3.2pre8)
-   A long period of collecting bug reports from all corners of the world
-   now lead to the following corrections to the code:
-   1) SCSI-2 F/W support crashed with a COMMAND ERROR. The reason for this 
-      was that it is possible to disable Fast-SCSI for the external bus.
-      The feature-control command, where this crash appeared regularly, tried
-      to set the maximum speed of 10MHz synchronous transfer speed and that
-      reports a COMMAND ERROR if external bus Fast-SCSI is disabled. Now,
-      the feature-command probes down from maximum speed until the adapter 
-      stops to complain, which is at the same time the maximum possible
-      speed selected in the reference program. So, F/W external can run at
-      5 MHz (slow-) or 10 MHz (fast-SCSI). During feature probing, the 
-      COMMAND ERROR message is used to detect if the adapter does not complain.
-   2) Up to now, only combined busmode is supported, if you use external
-      SCSI-devices, attached to the F/W-controller. If dual bus is selected,
-      only the internal SCSI-devices get accessed by Linux. For most 
-      applications, this should do fine. 
-   3) Wide-SCSI-addressing (16-Bit) is now possible for the internal F/W
-      bus on the F/W adapter. If F/W adapter is detected, the driver
-      automatically uses the extended PUN/LUN <-> LDN mapping tables, which
-      are now new from 3.2pre8. This allows PUNs between 0 and 15 and should
-      provide more fun with the F/W adapter.
-   4) Several machines use the SCSI: POS registers for internal/undocumented
-      storage of system relevant info. This confused the driver, mainly on
-      models 9595, as it expected no onboard SCSI only, if all POS in
-      the integrated SCSI-area are set to 0x00 or 0xff. Now, the mechanism
-      to check for integrated SCSI is much more restrictive and these problems
-      should be history.
-   - Michael Lang          
-
-   July 18, 2000 (v3.2pre9)
-   This develop rather quickly at the moment. Two major things were still
-   missing in 3.2pre8:
-   1) The adapter PUN for F/W adapters has 4-bits, while all other adapters
-      have 3-bits. This is now taken into account for F/W.
-   2) When you select CONFIG_IBMMCA_SCSI_ORDER_STANDARD, you should 
-      normally get the inverse probing order of your devices on the SCSI-bus.
-      The ANSI device order gets scrambled in version 3.2pre8!! Now, a new
-      and tested algorithm inverts the device-order on the SCSI-bus and
-      automatically avoids accidental access to whatever SCSI PUN the adapter 
-      is set and works with SCSI- and Wide-SCSI-addressing.
-   - Michael Lang
-
-   July 23, 2000 (v3.2pre10 unpublished) 
-   1) LED panel display supports wide-addressing in ibmmca=display mode.
-   2) Adapter-information and autoadaption to address-space is done.
-   3) Auto-probing for maximum synchronous SCSI transfer rate is working.
-   4) Optimization to some embedded function calls is applied.
-   5) Added some comment for the user to wait for SCSI-devices being probed.
-   6) Finished version 3.2 for Kernel 2.4.0. It least, I thought it is but...
-   - Michael Lang
-   
-   July 26, 2000 (v3.2pre11)
-   1) I passed a horrible weekend getting mad with NMIs on kernel 2.2.14 and
-      a model 9595. Asking around in the community, nobody except of me has
-      seen such errors. Weird, but I am trying to recompile everything on
-      the model 9595. Maybe, as I use a specially modified gcc, that could
-      cause problems. But, it was not the reason. The true background was,
-      that the kernel was compiled for i386 and the 9595 has a 486DX-2. 
-      Normally, no troubles should appear, but for this special machine,
-      only the right processor support is working fine!
-   2) Previous problems with synchronous speed, slowing down from one adapter 
-      to the next during probing are corrected. Now, local variables store
-      the synchronous bitmask for every single adapter found on the MCA bus.
-   3) LED alphanumeric panel support for XX95 systems is now showing some
-      alive rotator during boottime. This makes sense, when no monitor is 
-      connected to the system. You can get rid of all display activity, if
-      you do not use any parameter or just ibmmcascsi=activity, for the 
-      harddrive activity LED, existent on all PS/2, except models 8595-XXX.
-      If no monitor is available, please use ibmmcascsi=display, which works
-      fine together with the linuxinfo utility for the LED-panel.
-   - Michael Lang
-   
-   July 29, 2000 (v3.2)
-   1) Submission of this driver for kernel 2.4test-XX and 2.2.17.
-   - Michael Lang
-   
-   December 28, 2000 (v3.2d / v4.0)
-   1) The interrupt handler had some wrong statement to wait for. This
-      was done due to experimental reasons during 3.2 development but it
-      has shown that this is not stable enough. Going back to wait for the
-      adapter to be not busy is best.
-   2) Inquiry requests can be shorter than 255 bytes of return buffer. Due
-      to a bug in the ibmmca_queuecommand routine, this buffer was forced
-      to 255 at minimum. If the memory address, this return buffer is pointing
-      to does not offer more space, invalid memory accesses destabilized the
-      kernel.
-   3) version 4.0 is only valid for kernel 2.4.0 or later. This is necessary
-      to remove old kernel version dependent waste from the driver. 3.2d is
-      only distributed with older kernels but keeps compatibility with older
-      kernel versions. 4.0 and higher versions cannot be used with older 
-      kernels anymore!! You must have at least kernel 2.4.0!!
-   4) The commandline argument 'bypass' and all its functionality got removed
-      in version 4.0. This was never really necessary, as all troubles were
-      based on non-command related reasons up to now, so bypassing commands
-      did not help to avoid any bugs. It is kept in 3.2X for debugging reasons.
-   5) Dynamic reassignment of ldns was again verified and analyzed to be
-      completely inoperational. This is corrected and should work now.
-   6) All commands that get sent to the SCSI adapter were verified and
-      completed in such a way, that they are now completely conform to the
-      demands in the technical description of IBM. Main candidates were the
-      DEVICE_INQUIRY, REQUEST_SENSE and DEVICE_CAPACITY commands. They must
-      be transferred by bypassing the internal command buffer of the adapter
-      or else the response can be a random result. GET_POS_INFO would be more
-      safe in usage, if one could use the SUPRESS_EXCEPTION_SHORT, but this
-      is not allowed by the technical references of IBM. (Sorry, folks, the
-      model 80 problem is still a task to be solved in a different way.)
-   7) v3.2d is still hold back for some days for testing, while 4.0 is 
-      released.
-   - Michael Lang
-   
-   January 3, 2001 (v4.0a)
-   1) A lot of complains after the 2.4.0-prerelease kernel came in about
-      the impossibility to compile the driver as a module. This problem is
-      solved. In combination with that problem, some unprecise declaration
-      of the function option_setup() gave some warnings during compilation.
-      This is solved, too by a forward declaration in ibmmca.c.
-   2) #ifdef argument concerning CONFIG_SCSI_IBMMCA is no longer needed and
-      was entirely removed.
-   3) Some switch statements got optimized in code, as some minor variables
-      in internal SCSI-command handlers.
-   - Michael Lang
-
-   4 To do
-   -------
-        - IBM SCSI-2 F/W external SCSI bus support in separate mode!
-	- It seems that the handling of bad disks is really bad -
-	  non-existent, in fact. However, a low-level driver cannot help
-	  much, if such things happen.
-
-   5 Users' Manual
-   ---------------
-   5.1 Commandline Parameters
-   --------------------------
-   There exist several features for the IBM SCSI-subsystem driver.
-   The commandline parameter format is:
-   
-         ibmmcascsi=<command1>,<command2>,<command3>,...
-	 
-   where commandN can be one of the following:
-   
-         display    Owners of a model 95 or other PS/2 systems with an
-	            alphanumeric LED display may set this to have their
-		    display showing the following output of the 8 digits:
-		      
-		                ------DA
-				
-		    where '-' stays dark, 'D' shows the SCSI-device id
-		    and 'A' shows the SCSI hostindex, being currently 
-		    accessed. During boottime, this will give the message
-		    
-		                SCSIini*
-				
-                    on the LED-panel, where the * represents a rotator, 
-		    showing the activity during the probing phase of the
-		    driver which can take up to two minutes per SCSI-adapter.
-	 adisplay   This works like display, but gives more optical overview 
-	            of the activities on the SCSI-bus. The display will have
-		    the following output:
-		    
-		                6543210A
-				
-		    where the numbers 0 to 6 light up at the shown position,
-		    when the SCSI-device is accessed. 'A' shows again the SCSI
-		    hostindex. If display nor adisplay is set, the internal
-		    PS/2 harddisk LED is used for media-activities. So, if
-		    you really do not have a system with a LED-display, you
-		    should not set display or adisplay. Keep in mind, that
-		    display and adisplay can only be used alternatively. It
-		    is not recommended to use this option, if you have some
-		    wide-addressed devices e.g. at the SCSI-2 F/W adapter in
-		    your system. In addition, the usage of the display for
-		    other tasks in parallel, like the linuxinfo-utility makes 
-		    no sense with this option.
-	 activity   This enables the PS/2 harddisk LED activity indicator.
-	            Most PS/2 have no alphanumeric LED display, but some
-		    indicator. So you should use this parameter to activate it.
-		    If you own model 9595 (Server95), you can have both, the 
-		    LED panel and the activity indicator in parallel. However,
-		    some PS/2s, like the 8595 do not have any harddisk LED 
-		    activity indicator, which means, that you must use the
-		    alphanumeric LED display if you want to monitor SCSI-
-		    activity.
-	 bypass     This is obsolete from driver version 4.0, as the adapters
-	            got that far understood, that the selection between 
-		    integrated and bypassed commands should now work completely
-		    correct! For historical reasons, the old description is
-		    kept here:
-	            This commandline parameter forces the driver never to use
-	            SCSI-subsystems' integrated SCSI-command set. Except of
-		    the immediate assign, which is of vital importance for
-		    every IBM SCSI-subsystem to set its ldns right. Instead,
-		    the ordinary ANSI-SCSI-commands are used and passed by the
-		    controller to the SCSI-devices, therefore 'bypass'. The
-		    effort, done by the subsystem is quite bogus and at a
-		    minimum and therefore it should work everywhere. This
-		    could maybe solve troubles with old or integrated SCSI-
-		    controllers and nasty harddisks. Keep in mind, that using 
-		    this flag will slow-down SCSI-accesses slightly, as the 
-		    software generated commands are always slower than the 
-		    hardware. Non-harddisk devices always get read/write-
-		    commands in bypass mode. On the most recent releases of 
-		    the Linux IBM-SCSI-driver, the bypass command should be
-		    no longer a necessary thing, if you are sure about your
-		    SCSI-hardware!
-	 normal     This is the parameter, introduced on the 2.0.x development
-	            rail by ZP Gu. This parameter defines the SCSI-device
-		    scan order in the new industry standard. This means, that
-		    the first SCSI-device is the one with the lowest pun.
-		    E.g. harddisk at pun=0 is scanned before harddisk at
-		    pun=6, which means, that harddisk at pun=0 gets sda
-		    and the one at pun=6 gets sdb.
-	 ansi       The ANSI-standard for the right scan order, as done by
-	            IBM, Microware and Microsoft, scans SCSI-devices starting
-		    at the highest pun, which means, that e.g. harddisk at
-		    pun=6 gets sda and a harddisk at pun=0 gets sdb. If you
-		    like to have the same SCSI-device order, as in DOS, OS-9
-		    or OS/2, just use this parameter.
-         fast       SCSI-I/O in synchronous mode is done at 5 MHz for IBM-
-                    SCSI-devices. SCSI-2 Fast/Wide Adapter/A external bus
-                    should then run at 10 MHz if Fast-SCSI is enabled,
-                    and at 5 MHz if Fast-SCSI is disabled on the external
-                    bus. This is the default setting when nothing is 
-                    specified here.
-         medium     Synchronous rate is at 50% approximately, which means
-                    2.5 MHz for IBM SCSI-adapters and 5.0 MHz for F/W ext.
-                    SCSI-bus (when Fast-SCSI speed enabled on external bus).
-         slow       The slowest possible synchronous transfer rate is set. 
-                    This means 1.82 MHz for IBM SCSI-adapters and 2.0 MHz
-                    for F/W external bus at Fast-SCSI speed on the external
-		    bus.
-		    
-   A further option is that you can force the SCSI-driver to accept a SCSI-
-   subsystem at a certain I/O-address with a predefined adapter PUN. This
-   is done by entering 
-
-                  commandN   = I/O-base
-		  commandN+1 = adapter PUN
-		  
-   e.g. ibmmcascsi=0x3540,7 will force the driver to detect a SCSI-subsystem 
-   at I/O-address 0x3540 with adapter PUN 7. Please only use this method, if
-   the driver does really not recognize your SCSI-adapter! With driver version
-   3.2, this recognition of various adapters was hugely improved and you
-   should try first to remove your commandline arguments of such type with a 
-   newer driver. I bet, it will be recognized correctly. Even multiple and 
-   different types of IBM SCSI-adapters should be recognized correctly, too.
-   Use the forced detection method only as last solution!
-   
-   Examples:
-   
-        ibmmcascsi=adisplay
-	
-   This will use the advanced display mode for the model 95 LED alphanumeric
-   display.
-   
-        ibmmcascsi=display,0x3558,7
-	
-   This will activate the default display mode for the model 95 LED display
-   and will force the driver to accept a SCSI-subsystem at I/O-base 0x3558
-   with adapter PUN 7.
-   
-   5.2 Troubleshooting
-   -------------------
-   The following FAQs should help you to solve some major problems with this
-   driver.
-   
-     Q: "Reset SCSI-devices at boottime" halts the system at boottime, why?
-     A: This is only tested with the IBM SCSI Adapter w/cache. It is not
-        yet proven to run on other adapters, however you may be lucky.
-	In version 3.1d this has been hugely improved and should work better,
-	now. Normally you really won't need to activate this flag in the
-	kernel configuration, as all post 1989 SCSI-devices should accept
-	the reset-signal, when the computer is switched on. The SCSI-
-	subsystem generates this reset while being initialized. This flag
-	is really reserved for users with very old, very strange or self-made
-	SCSI-devices.
-     Q: Why is the SCSI-order of my drives mirrored to the device-order
-        seen from OS/2 or DOS ?
-     A: It depends on the operating system, if it looks at the devices in
-        ANSI-SCSI-standard (starting from pun 6 and going down to pun 0) or
-	if it just starts at pun 0 and counts up. If you want to be conform
-	with OS/2 and DOS, you have to activate this flag in the kernel
-	configuration or you should set 'ansi' as parameter for the kernel.
-	The parameter 'normal' sets the new industry standard, starting
-	from pun 0, scanning up to pun 6. This allows you to change your 
-	opinion still after having already compiled the kernel.
-     Q: Why can't I find IBM MCA SCSI support in the config menu?
-     A: You have to activate MCA bus support, first.
-     Q: Where can I find the latest info about this driver?
-     A: See the file MAINTAINERS for the current WWW-address, which offers
-        updates, info and Q/A lists. At this file's origin, the webaddress
-	was: http://www.staff.uni-mainz.de/mlang/linux.html
-     Q: My SCSI-adapter is not recognized by the driver, what can I do?
-     A: Just force it to be recognized by kernel parameters. See section 5.1.
-        If this really happens, do also send e-mail to the maintainer, as
-	forced detection should be never necessary. Forced detection is in
-	principal some flaw of the driver adapter detection and goes into 
-	bug reports.
-     Q: The driver screws up, if it starts to probe SCSI-devices, is there
-        some way out of it?
-     A: Yes, that was some recognition problem of the correct SCSI-adapter
-        and its I/O base addresses. Upgrade your driver to the latest release
-	and it should be fine again.
-     Q: I get a message: panic IBM MCA SCSI: command error .... , what can
-        I do against this?
-     A: Previously, I followed the way by ignoring command errors by using
-        ibmmcascsi=forgiveall, but this command no longer exists and is
-	obsolete. If such a problem appears, it is caused by some segmentation
-	fault of the driver, which maps to some unallowed area. The latest 
-	version of the driver should be ok, as most bugs have been solved.
-     Q: There are still kernel panics, even after having set 
-        ibmmcascsi=forgiveall. Are there other possibilities to prevent
-	such panics?
-     A: No, get just the latest release of the driver and it should work 
-        better and better with increasing version number. Forget about this
-	ibmmcascsi=forgiveall, as also ignorecmd are obsolete.!
-     Q: Linux panics or stops without any comment, but it is probable, that my
-        harddisk(s) have bad blocks.
-     A: Sorry, the bad-block handling is still a feeble point of this driver,
-        but is on the schedule for development in the near future.
-     Q: Linux panics while dynamically assigning SCSI-ids or ldns.
-     A: If you disconnect a SCSI-device from the machine, while Linux is up
-        and the driver uses dynamical reassignment of logical device numbers
-	(ldn), it really gets "angry" if it won't find devices, that were still
-	present at boottime and stops Linux.
-     Q: The system does not recover after an abort-command has been generated.
-     A: This is regrettably true, as it is not yet understood, why the 
-        SCSI-adapter does really NOT generate any interrupt at the end of
-	the abort-command. As no interrupt is generated, the abort command
-	cannot get finished and the system hangs, sorry, but checks are 
-	running to hunt down this problem. If there is a real pending command,
-	the interrupt MUST get generated after abort. In this case, it
-	should finish well.
-     Q: The system gets in bad shape after a SCSI-reset, is this known?
-     A: Yes, as there are a lot of prescriptions (see the Linux Hackers'
-        Guide) what has to be done for reset, we still share the bad shape of
-	the reset functions with all other low level SCSI-drivers. 
-	Astonishingly, reset works in most cases quite ok, but the harddisks
-	won't run in synchronous mode anymore after a reset, until you reboot.
-     Q: Why does my XXX w/Cache adapter not use read-prefetch?
-     A: Ok, that is not completely possible. If a cache is present, the 
-        adapter tries to use it internally. Explicitly, one can use the cache
-	with a read prefetch command, maybe in future, but this requires
-	some major overhead of SCSI-commands that risks the performance to
-	go down more than it gets improved. Tests with that are running.
-     Q: I have a IBM SCSI-2 Fast/Wide adapter, it boots in some way and hangs.
-     A: Yes, that is understood, as for sure, your SCSI-2 Fast/Wide adapter
-        was in such a case recognized as integrated SCSI-adapter or something 
-	else, but not as the correct adapter. As the I/O-ports get assigned 
-	wrongly by that reason, the system should crash in most cases. You 
-	should upgrade to the latest release of the SCSI-driver. The 
-	recommended version is 3.2 or later. Here, the F/W support is in
-	a stable and reliable condition. Wide-addressing is in addition 
-	supported.
-     Q: I get an Oops message and something like "killing interrupt".
-     A: The reason for this is that the IBM SCSI-subsystem only sends a 
-        termination status back, if some error appeared. In former releases
-	of the driver, it was not checked, if the termination status block
-	is NULL. From version 3.2, it is taken care of this.
-     Q: I have a F/W adapter and the driver sees my internal SCSI-devices,
-        but ignores the external ones.
-     A: Select combined busmode in the IBM config-program and check for that
-        no SCSI-id on the external devices appears on internal devices.
-        Reboot afterwards. Dual busmode is supported, but works only for the
-	internal bus, yet. External bus is still ignored. Take care for your
-	SCSI-ids. If combined bus-mode is activated, on some adapters, 
-	the wide-addressing is not possible, so devices with ids between 8 
-	and 15 get ignored by the driver & adapter!
-     Q: I have a 9595 and I get a NMI during heavy SCSI I/O e.g. during fsck.
-        A COMMAND ERROR is reported and characters on the screen are missing.
-	Warm reboot is not possible. Things look like quite weird.
-     A: Check the processor type of your 9595. If you have an 80486 or 486DX-2
-        processor complex on your mainboard and you compiled a kernel that
-	supports 80386 processors, it is possible, that the kernel cannot
-	keep track of the PS/2 interrupt handling and stops on an NMI. Just
-	compile a kernel for the correct processor type of your PS/2 and
-	everything should be fine. This is necessary even if one assumes,
-	that some 80486 system should be downward compatible to 80386
-	software.
-     Q: Some commands hang and interrupts block the machine. After some
-        timeout, the syslog reports that it tries to call abort, but the
-	machine is frozen.
-     A: This can be a busy wait bug in the interrupt handler of driver 
-        version 3.2. You should at least upgrade to 3.2c if you use 
-	kernel < 2.4.0 and driver version 4.0 if you use kernel 2.4.0 or 
-	later (including all test releases).
-     Q: I have a PS/2 model 80 and more than 16 MBytes of RAM. The driver
-        completely refuses to work, reports NMIs, COMMAND ERRORs or other
-	ambiguous stuff. When reducing the RAM size down below 16 MB, 
-	everything is running smoothly.
-     A: No real answer, yet. In any case, one should force the kernel to
-        present SCBs only below the 16 MBytes barrier. Maybe this solves the
-	problem. Not yet tried, but guessing that it could work. To get this,
-	set unchecked_isa_dma argument of ibmmca.h from 0 to 1.
-
-   5.3 Bug reports
-   --------------
-   If you really find bugs in the source code or the driver will successfully
-   refuse to work on your machine, you should send a bug report to me. The
-   best for this is to follow the instructions on the WWW-page for this
-   driver. Fill out the bug-report form, placed on the WWW-page and ship it,
-   so the bugs can be taken into account with maximum efforts. But, please
-   do not send bug reports about this driver to Linus Torvalds or Leonard
-   Zubkoff, as Linus is buried in E-Mail and Leonard is supervising all
-   SCSI-drivers and won't have the time left to look inside every single
-   driver to fix a bug and especially DO NOT send modified code to Linus
-   Torvalds or Alan J. Cox which has not been checked here!!! They are both
-   quite buried in E-mail (as me, sometimes, too) and one should first check
-   for problems on my local teststand. Recently, I got a lot of 
-   bug reports for errors in the ibmmca.c code, which I could not imagine, but
-   a look inside some Linux-distribution showed me quite often some modified
-   code, which did no longer work on most other machines than the one of the
-   modifier. Ok, so now that there is maintenance service available for this
-   driver, please use this address first in order to keep the level of
-   confusion low. Thank you!
-   
-   When you get a SCSI-error message that panics your system, a list of
-   register-entries of the SCSI-subsystem is shown (from Version 3.1d). With 
-   this list, it is very easy for the maintainer to localize the problem in 
-   the driver or in the configuration of the user. Please write down all the 
-   values from this report and send them to the maintainer. This would really 
-   help a lot and makes life easier concerning misunderstandings.
-   
-   Use the bug-report form (see 5.4 for its address) to send all the bug-
-   stuff to the maintainer or write e-mail with the values from the table. 
-   
-   5.4 Support WWW-page
-   --------------------
-   The address of the IBM SCSI-subsystem supporting WWW-page is:
-   
-        http://www.staff.uni-mainz.de/mlang/linux.html
-	
-   Here you can find info about the background of this driver, patches,
-   troubleshooting support, news and a bugreport form. Please check that
-   WWW-page regularly for latest hints. If ever this URL changes, please 
-   refer to the MAINTAINERS file in order to get the latest address.
-   
-   For the bugreport, please fill out the formular on the corresponding
-   WWW-page. Read the dedicated instructions and write as much as you
-   know about your problem. If you do not like such formulars, please send
-   some e-mail directly, but at least with the same information as required by
-   the formular.
-   
-   If you have extensive bug reports, including Oops messages and
-   screen-shots, please feel free to send it directly to the address
-   of the maintainer, too. The current address of the maintainer is:
-   
-            Michael Lang <langa2@kph.uni-mainz.de>
-   
-   6 References
-   ------------
-   IBM Corp., "Update for the PS/2 Hardware Interface Technical Reference, 
-   Common Interfaces", Armonk, September 1991, PN 04G3281, 
-   (available in the U.S. for $21.75 at 1-800-IBM-PCTB or in Germany for
-   around 40,-DM at "Hallo IBM").
-  
-   IBM Corp., "Personal System/2 Micro Channel SCSI
-   Adapter with Cache Technical Reference", Armonk, March 1990, PN 68X2365.
-
-   IBM Corp., "Personal System/2 Micro Channel SCSI
-   Adapter Technical Reference", Armonk, March 1990, PN 68X2397.
-
-   IBM Corp., "SCSI-2 Fast/Wide Adapter/A Technical Reference - Dual Bus",
-   Armonk, March 1994, PN 83G7545.
- 
-   Friedhelm Schmidt, "SCSI-Bus und IDE-Schnittstelle - Moderne Peripherie-
-   Schnittstellen: Hardware, Protokollbeschreibung und Anwendung", 2. Aufl.
-   Addison Wesley, 1996.
-   
-   Michael K. Johnson, "The Linux Kernel Hackers' Guide", Version 0.6, Chapel
-   Hill - North Carolina, 1995
-   
-   Andreas Kaiser, "SCSI TAPE BACKUP for OS/2 2.0", Version 2.12, Stuttgart
-   1993
-   
-   Helmut Rompel, "IBM Computerwelt GUIDE", What is what bei IBM., Systeme *
-   Programme * Begriffe, IWT-Verlag GmbH - Muenchen, 1988
-   
-   7 Credits to
-   ------------
-   7.1 People
-   ----------
-   Klaus Grimm
-                who already a long time ago gave me the old code from the
-		SCSI-driver in order to get it running for some old machine
-		in our institute.
-   Martin Kolinek
-                who wrote the first release of the IBM SCSI-subsystem driver.
-   Chris Beauregard
-                who for a long time maintained MCA-Linux and the SCSI-driver
-		in the beginning. Chris, wherever you are: Cheers to you!
-   Klaus Kudielka
-                with whom in the 2.1.x times, I had a quite fruitful
-                cooperation to get the driver running as a module and to get
-		it running with multiple SCSI-adapters.
-   David Weinehall
-                for his excellent maintenance of the MCA-stuff and the quite 
-		detailed bug reports and ideas for this driver (and his 
-		patience ;-)).
-   Alan J. Cox  
-                for his bug reports and his bold activities in cross-checking
-		the driver-code with his teststand.
-		
-   7.2 Sponsors & Supporters
-   -------------------------
-   "Hallo IBM",
-   IBM-Deutschland GmbH
-                the service of IBM-Deutschland for customers. Their E-Mail
-		service is unbeatable. Whatever old stuff I asked for, I 
-		always got some helpful answers.
-   Karl-Otto Reimers,
-   IBM Klub - Sparte IBM Geschichte, Sindelfingen
-                for sending me a copy of the w/Cache manual from the 
-		IBM-Deutschland archives.
-   Harald Staiger
-                for his extensive hardware donations which allows me today
-		still to test the driver in various constellations.
-   Erich Fritscher
-                for his very kind sponsoring.
-   Louis Ohland,
-   Charles Lasitter
-                for support by shipping me an IBM SCSI-2 Fast/Wide manual.
-                In addition, the contribution of various hardware is quite 
-                decessive and will make it possible to add FWSR (RAID)
-                adapter support to the driver in the near future! So,
-                complaints about no RAID support won't remain forever.
-                Yes, folks, that is no joke, RAID support is going to rise!
-   Erik Weber
-                for the great deal we made about a model 9595 and the nice
-                surrounding equipment and the cool trip to Mannheim
-                second-hand computer market. In addition, I would like
-		to thank him for his exhaustive SCSI-driver testing on his 
-		95er PS/2 park.
-   Anthony Hogbin
-                for his direct shipment of a SCSI F/W adapter, which allowed
-                me immediately on the first stage to try it on model 8557
-                together with onboard SCSI adapter and some SCSI w/Cache.
-   Andreas Hotz
-                for his support by memory and an IBM SCSI-adapter. Collecting
-                all this together now allows me to try really things with
-                the driver at maximum load and variety on various models in
-                a very quick and efficient way.
-   Peter Jennewein
-                for his model 30, which serves me as part of my teststand
-		and his cool remark about how you make an ordinary diskette
-		drive working and how to connect it to an IBM-diskette port.
-   Johannes Gutenberg-Universitaet, Mainz &
-   Institut fuer Kernphysik, Mainz Microtron (MAMI)
-                for the offered space, the link, placed on the central
-                homepage and the space to store and offer the driver and 
-		related material and the free working times, which allow
-                me to answer all your e-mail.
-                   
-   8 Trademarks
-   ------------
-   IBM, PS/2, OS/2, Microchannel are registered trademarks of International 
-   Business Machines Corporation
-   
-   MS-DOS is a registered trademark of Microsoft Corporation
-   
-   Microware, OS-9 are registered trademarks of Microware Systems
-   
-   9 Disclaimer
-   ------------
-   Beside the GNU General Public License and the dependent disclaimers and disclaimers
-   concerning the Linux-kernel in special, this SCSI-driver comes without any
-   warranty. Its functionality is tested as good as possible on certain 
-   machines and combinations of computer hardware, which does not exclude,
-   that data loss or severe damage of hardware is possible while using this
-   part of software on some arbitrary computer hardware or in combination 
-   with other software packages. It is highly recommended to make backup
-   copies of your data before using this software. Furthermore, personal
-   injuries by hardware defects, that could be caused by this SCSI-driver are
-   not excluded and it is highly recommended to handle this driver with a
-   maximum of carefulness.
-   
-   This driver supports hardware, produced by International Business Machines
-   Corporation (IBM).
-   
-------
-Michael Lang 
-(langa2@kph.uni-mainz.de)
diff --git a/Documentation/scsi/scsi-parameters.txt b/Documentation/scsi/scsi-parameters.txt
index 21e5798..2bfd6f6 100644
--- a/Documentation/scsi/scsi-parameters.txt
+++ b/Documentation/scsi/scsi-parameters.txt
@@ -37,9 +37,6 @@
 
 	eata=		[HW,SCSI]
 
-	fd_mcs=		[HW,SCSI]
-			See header of drivers/scsi/fd_mcs.c.
-
 	fdomain=	[HW,SCSI]
 			See header of drivers/scsi/fdomain.c.
 
@@ -48,9 +45,6 @@
 
 	gvp11=		[HW,SCSI]
 
-	ibmmcascsi=	[HW,MCA,SCSI] IBM MicroChannel SCSI adapter
-			See Documentation/mca.txt.
-
 	in2000=		[HW,SCSI]
 			See header of drivers/scsi/in2000.c.
 
diff --git a/Documentation/scsi/scsi_mid_low_api.txt b/Documentation/scsi/scsi_mid_low_api.txt
index a340b18..2b06aba 100644
--- a/Documentation/scsi/scsi_mid_low_api.txt
+++ b/Documentation/scsi/scsi_mid_low_api.txt
@@ -30,7 +30,7 @@
 and thus represent two hosts. Like most modern HBAs, each aic7xxx host
 has its own PCI device address. [The one-to-one correspondence between
 a SCSI host and a PCI device is common but not required (e.g. with
-ISA or MCA adapters).]
+ISA adapters).]
 
 The SCSI mid level isolates an LLD from other layers such as the SCSI
 upper layer drivers and the block layer.
diff --git a/Documentation/serial/stallion.txt b/Documentation/serial/stallion.txt
index 5509091..4d798c0 100644
--- a/Documentation/serial/stallion.txt
+++ b/Documentation/serial/stallion.txt
@@ -20,10 +20,10 @@
 multiport serial boards. One is for the Stallion smart boards - that is
 EasyIO, EasyConnection 8/32 and EasyConnection 8/64-PCI, the other for
 the true Stallion intelligent multiport boards - EasyConnection 8/64
-(ISA, EISA, MCA), EasyConnection/RA-PCI, ONboard and Brumby.
+(ISA, EISA), EasyConnection/RA-PCI, ONboard and Brumby.
 
 If you are using any of the Stallion intelligent multiport boards (Brumby,
-ONboard, EasyConnection 8/64 (ISA, EISA, MCA), EasyConnection/RA-PCI) with
+ONboard, EasyConnection 8/64 (ISA, EISA), EasyConnection/RA-PCI) with
 Linux you will need to get the driver utility package.  This contains a
 firmware loader and the firmware images necessary to make the devices operate.
 
@@ -40,7 +40,7 @@
 boards then you don't need this package, although it does have a serial stats
 display program.
 
-If you require DIP switch settings, EISA or MCA configuration files, or any
+If you require DIP switch settings, or EISA configuration files, or any
 other information related to Stallion boards then have a look at Stallion's
 web pages at http://www.stallion.com.
 
@@ -51,13 +51,13 @@
 The drivers can be used as loadable modules or compiled into the kernel.
 You can choose which when doing a "config" on the kernel.
 
-All ISA, EISA and MCA boards that you want to use need to be configured into
+All ISA, and EISA boards that you want to use need to be configured into
 the driver(s). All PCI boards will be automatically detected when you load
 the driver - so they do not need to be entered into the driver(s)
 configuration structure. Note that kernel PCI support is required to use PCI
 boards.
 
-There are two methods of configuring ISA, EISA and MCA boards into the drivers.
+There are two methods of configuring ISA and EISA boards into the drivers.
 If using the driver as a loadable module then the simplest method is to pass
 the driver configuration as module arguments. The other method is to modify
 the driver source to add configuration lines for each board in use.
@@ -71,12 +71,12 @@
 2.1 MODULE DRIVER CONFIGURATION:
 
 The simplest configuration for modules is to use the module load arguments
-to configure any ISA, EISA or MCA boards. PCI boards are automatically
+to configure any ISA or EISA boards. PCI boards are automatically
 detected, so do not need any additional configuration at all.
 
-If using EasyIO, EasyConnection 8/32 ISA or MCA, or EasyConnection 8/63-PCI
+If using EasyIO, EasyConnection 8/32 ISA, or EasyConnection 8/63-PCI
 boards then use the "stallion" driver module, Otherwise if you are using
-an EasyConnection 8/64 ISA, EISA or MCA, EasyConnection/RA-PCI, ONboard,
+an EasyConnection 8/64 ISA or EISA, EasyConnection/RA-PCI, ONboard,
 Brumby or original Stallion board then use the "istallion" driver module.
 
 Typically to load up the smart board driver use:
@@ -146,7 +146,7 @@
 2.2 STATIC DRIVER CONFIGURATION:
 
 For static driver configuration you need to modify the driver source code.
-Entering ISA, EISA and MCA boards into the driver(s) configuration structure
+Entering ISA and EISA boards into the driver(s) configuration structure
 involves editing the driver(s) source file. It's pretty easy if you follow
 the instructions below. Both drivers can support up to 4 boards. The smart
 card driver (the stallion.c driver) supports any combination of EasyIO and
@@ -157,7 +157,7 @@
 To set up the driver(s) for the boards that you want to use you need to
 edit the appropriate driver file and add configuration entries.
 
-If using EasyIO or EasyConnection 8/32 ISA or MCA boards,
+If using EasyIO or EasyConnection 8/32 ISA boards,
    In drivers/char/stallion.c:
       - find the definition of the stl_brdconf array (of structures)
         near the top of the file
@@ -243,7 +243,7 @@
 On EasyIO and EasyConnection 8/32 boards the IRQ is software programmable, so
 if there is a conflict you may need to change the IRQ used for a board. There
 are no interrupts to worry about for ONboard, Brumby or EasyConnection 8/64
-(ISA, EISA and MCA) boards. The memory region on EasyConnection 8/64 and
+(ISA and EISA) boards. The memory region on EasyConnection 8/64 and
 ONboard boards is software programmable, but not on the Brumby boards.
 
 
diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt
index 8c16d50..221b810 100644
--- a/Documentation/sound/alsa/ALSA-Configuration.txt
+++ b/Documentation/sound/alsa/ALSA-Configuration.txt
@@ -1545,7 +1545,7 @@
 
     Module for sound cards based on the C-Media CMI8786/8787/8788 chip:
     * Asound A-8788
-    * Asus Xonar DG
+    * Asus Xonar DG/DGX
     * AuzenTech X-Meridian
     * AuzenTech X-Meridian 2G
     * Bgears b-Enspirer
diff --git a/Documentation/watchdog/src/watchdog-test.c b/Documentation/watchdog/src/watchdog-test.c
index 63fdc34..73ff5cc 100644
--- a/Documentation/watchdog/src/watchdog-test.c
+++ b/Documentation/watchdog/src/watchdog-test.c
@@ -7,6 +7,7 @@
 #include <string.h>
 #include <unistd.h>
 #include <fcntl.h>
+#include <signal.h>
 #include <sys/ioctl.h>
 #include <linux/types.h>
 #include <linux/watchdog.h>
@@ -29,6 +30,14 @@
  * The main program.  Run the program with "-d" to disable the card,
  * or "-e" to enable the card.
  */
+
+void term(int sig)
+{
+    close(fd);
+    fprintf(stderr, "Stopping watchdog ticks...\n");
+    exit(0);
+}
+
 int main(int argc, char *argv[])
 {
     int flags;
@@ -47,26 +56,31 @@
 	    ioctl(fd, WDIOC_SETOPTIONS, &flags);
 	    fprintf(stderr, "Watchdog card disabled.\n");
 	    fflush(stderr);
-	    exit(0);
+	    goto end;
 	} else if (!strncasecmp(argv[1], "-e", 2)) {
 	    flags = WDIOS_ENABLECARD;
 	    ioctl(fd, WDIOC_SETOPTIONS, &flags);
 	    fprintf(stderr, "Watchdog card enabled.\n");
 	    fflush(stderr);
-	    exit(0);
+	    goto end;
 	} else {
 	    fprintf(stderr, "-d to disable, -e to enable.\n");
 	    fprintf(stderr, "run by itself to tick the card.\n");
 	    fflush(stderr);
-	    exit(0);
+	    goto end;
 	}
     } else {
 	fprintf(stderr, "Watchdog Ticking Away!\n");
 	fflush(stderr);
     }
 
+    signal(SIGINT, term);
+
     while(1) {
 	keep_alive();
 	sleep(1);
     }
+end:
+    close(fd);
+    return 0;
 }
diff --git a/Documentation/watchdog/watchdog-kernel-api.txt b/Documentation/watchdog/watchdog-kernel-api.txt
index 227f6cd..25fe430 100644
--- a/Documentation/watchdog/watchdog-kernel-api.txt
+++ b/Documentation/watchdog/watchdog-kernel-api.txt
@@ -59,7 +59,7 @@
 * bootstatus: status of the device after booting (reported with watchdog
   WDIOF_* status bits).
 * driver_data: a pointer to the drivers private data of a watchdog device.
-  This data should only be accessed via the watchdog_set_drvadata and
+  This data should only be accessed via the watchdog_set_drvdata and
   watchdog_get_drvdata routines.
 * status: this field contains a number of status bits that give extra
   information about the status of the device (Like: is the watchdog timer
diff --git a/MAINTAINERS b/MAINTAINERS
index 591a10c..27a1d3c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2776,6 +2776,15 @@
 S:	Maintained
 F:	sound/firewire/
 
+FIREWIRE SBP-2 TARGET
+M:	Chris Boot <bootc@bootc.net>
+L:	linux-scsi@vger.kernel.org
+L:	target-devel@vger.kernel.org
+L:	linux1394-devel@lists.sourceforge.net
+T:	git git://git.kernel.org/pub/scm/linux/kernel/git/nab/lio-core-2.6.git master
+S:	Maintained
+F:	drivers/target/sbp/
+
 FIREWIRE SUBSYSTEM
 M:	Stefan Richter <stefanr@s5r6.in-berlin.de>
 L:	linux1394-devel@lists.sourceforge.net
@@ -3338,12 +3347,6 @@
 S:	Maintained
 F:	arch/ia64/
 
-IBM MCA SCSI SUBSYSTEM DRIVER
-M:	Michael Lang <langa2@kph.uni-mainz.de>
-W:	http://www.uni-mainz.de/~langm000/linux.html
-S:	Maintained
-F:	drivers/scsi/ibmmca.c
-
 IBM Power Linux RAID adapter
 M:	Brian King <brking@us.ibm.com>
 S:	Supported
@@ -4449,13 +4452,6 @@
 S:	Supported
 F:	arch/microblaze/
 
-MICROCHANNEL ARCHITECTURE (MCA)
-M:	James Bottomley <James.Bottomley@HansenPartnership.com>
-S:	Maintained
-F:	Documentation/mca.txt
-F:	drivers/mca/
-F:	include/linux/mca*
-
 MICROTEK X6 SCANNER
 M:	Oliver Neukum <oliver@neukum.name>
 S:	Maintained
@@ -6573,7 +6569,7 @@
 L:	linux-sh@vger.kernel.org
 W:	http://www.linux-sh.org
 Q:	http://patchwork.kernel.org/project/linux-sh/list/
-T:	git git://git.kernel.org/pub/scm/linux/kernel/git/lethal/sh-2.6.git sh-latest
+T:	git git://github.com/pmundt/linux-sh.git sh-latest
 S:	Supported
 F:	Documentation/sh/
 F:	arch/sh/
@@ -6691,6 +6687,12 @@
 F:	drivers/mmc/host/tifm_sd.c
 F:	include/linux/tifm.h
 
+TI LM49xxx FAMILY ASoC CODEC DRIVERS
+M:	M R Swami Reddy <mr.swami.reddy@ti.com>
+L:	alsa-devel@alsa-project.org (moderated for non-subscribers)
+S:	Maintained
+F:	sound/soc/codecs/lm49453*
+
 TI TWL4030 SERIES SOC CODEC DRIVER
 M:	Peter Ujfalusi <peter.ujfalusi@ti.com>
 L:	alsa-devel@alsa-project.org (moderated for non-subscribers)
diff --git a/Makefile b/Makefile
index 546515c..b62c1e0 100644
--- a/Makefile
+++ b/Makefile
@@ -566,6 +566,16 @@
 KBUILD_CFLAGS	+= -O2
 endif
 
+ifdef CONFIG_READABLE_ASM
+# Disable optimizations that make assembler listings hard to read.
+# reorder blocks reorders the control in the function
+# ipa clone creates specialized cloned functions
+# partial inlining inlines only parts of functions
+KBUILD_CFLAGS += $(call cc-option,-fno-reorder-blocks,) \
+                 $(call cc-option,-fno-ipa-cp-clone,) \
+                 $(call cc-option,-fno-partial-inlining)
+endif
+
 include $(srctree)/arch/$(SRCARCH)/Makefile
 
 ifneq ($(CONFIG_FRAME_WARN),0)
@@ -786,6 +796,10 @@
 quiet_cmd_sysmap = SYSMAP
       cmd_sysmap = $(CONFIG_SHELL) $(srctree)/scripts/mksysmap
 
+# Sort exception table at build time
+quiet_cmd_sortextable = SORTEX
+      cmd_sortextable = $(objtree)/scripts/sortextable
+
 # Link of vmlinux
 # If CONFIG_KALLSYMS is set .version is already updated
 # Generate System.map and verify that the content is consistent
@@ -798,6 +812,12 @@
 	$(call cmd,vmlinux__)
 	$(Q)echo 'cmd_$@ := $(cmd_vmlinux__)' > $(@D)/.$(@F).cmd
 
+	$(if $(CONFIG_BUILDTIME_EXTABLE_SORT),				\
+	  $(Q)$(if $($(quiet)cmd_sortextable),				\
+	    echo '  $($(quiet)cmd_sortextable)  vmlinux' &&)		\
+	  $(cmd_sortextable)  vmlinux)
+
+
 	$(Q)$(if $($(quiet)cmd_sysmap),                                      \
 	  echo '  $($(quiet)cmd_sysmap)  System.map' &&)                     \
 	$(cmd_sysmap) $@ System.map;                                         \
diff --git a/arch/alpha/include/asm/processor.h b/arch/alpha/include/asm/processor.h
index 94afe58..e37b887 100644
--- a/arch/alpha/include/asm/processor.h
+++ b/arch/alpha/include/asm/processor.h
@@ -49,9 +49,6 @@
 /* Free all resources held by a thread. */
 extern void release_thread(struct task_struct *);
 
-/* Prepare to copy thread state - unlazy all lazy status */
-#define prepare_to_copy(tsk)	do { } while (0)
-
 /* Create a kernel thread without removing it from tasklists.  */
 extern long kernel_thread(int (*fn)(void *), void *arg, unsigned long flags);
 
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 76edcfe..4f4c811 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -107,14 +107,6 @@
 config SBUS
 	bool
 
-config MCA
-	bool
-	help
-	  MicroChannel Architecture is found in some IBM PS/2 machines and
-	  laptops.  It is a bus system similar to PCI or ISA. See
-	  <file:Documentation/mca.txt> (and especially the web page given
-	  there) before attempting to build an MCA bus kernel.
-
 config STACKTRACE_SUPPORT
 	bool
 	default y
diff --git a/arch/arm/include/asm/processor.h b/arch/arm/include/asm/processor.h
index d7038fa..99afa74 100644
--- a/arch/arm/include/asm/processor.h
+++ b/arch/arm/include/asm/processor.h
@@ -77,9 +77,6 @@
 /* Free all resources held by a thread. */
 extern void release_thread(struct task_struct *);
 
-/* Prepare to copy thread state - unlazy all lazy status */
-#define prepare_to_copy(tsk)	do { } while (0)
-
 unsigned long get_wchan(struct task_struct *p);
 
 #if __LINUX_ARM_ARCH__ == 6 || defined(CONFIG_ARM_ERRATA_754327)
diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig
index 7d6322c..cca8c0c 100644
--- a/arch/arm/mach-imx/Kconfig
+++ b/arch/arm/mach-imx/Kconfig
@@ -151,6 +151,7 @@
 	select IMX_HAVE_PLATFORM_IMX2_WDT
 	select IMX_HAVE_PLATFORM_IMXDI_RTC
 	select IMX_HAVE_PLATFORM_IMX_I2C
+	select IMX_HAVE_PLATFORM_IMX_SSI
 	select IMX_HAVE_PLATFORM_IMX_FB
 	select IMX_HAVE_PLATFORM_IMX_KEYPAD
 	select IMX_HAVE_PLATFORM_IMX_UART
@@ -495,6 +496,7 @@
 	select IMX_HAVE_PLATFORM_FSL_USB2_UDC
 	select IMX_HAVE_PLATFORM_IMX2_WDT
 	select IMX_HAVE_PLATFORM_IMX_I2C
+	select IMX_HAVE_PLATFORM_IMX_SSI
 	select IMX_HAVE_PLATFORM_IMX_UART
 	select IMX_HAVE_PLATFORM_IPU_CORE
 	select IMX_HAVE_PLATFORM_MXC_EHCI
diff --git a/arch/arm/mach-imx/mach-mx31_3ds.c b/arch/arm/mach-imx/mach-mx31_3ds.c
index 4d1aab1..4eafdf2 100644
--- a/arch/arm/mach-imx/mach-mx31_3ds.c
+++ b/arch/arm/mach-imx/mach-mx31_3ds.c
@@ -156,6 +156,11 @@
 	MX31_PIN_CSI_VSYNC__CSI_VSYNC,
 	MX31_PIN_CSI_D5__GPIO3_5, /* CMOS PWDN */
 	IOMUX_MODE(MX31_PIN_RI_DTE1, IOMUX_CONFIG_GPIO), /* CMOS reset */
+	/* SSI */
+	MX31_PIN_STXD4__STXD4,
+	MX31_PIN_SRXD4__SRXD4,
+	MX31_PIN_SCK4__SCK4,
+	MX31_PIN_SFS4__SFS4,
 };
 
 /*
@@ -488,12 +493,23 @@
 };
 
 /* MC13783 */
+static struct mc13xxx_codec_platform_data mx31_3ds_codec = {
+	.dac_ssi_port = MC13783_SSI1_PORT,
+	.adc_ssi_port = MC13783_SSI1_PORT,
+};
+
 static struct mc13xxx_platform_data mc13783_pdata = {
 	.regulators = {
 		.regulators = mx31_3ds_regulators,
 		.num_regulators = ARRAY_SIZE(mx31_3ds_regulators),
 	},
-	.flags  = MC13XXX_USE_TOUCHSCREEN | MC13XXX_USE_RTC,
+	.codec = &mx31_3ds_codec,
+	.flags  = MC13XXX_USE_TOUCHSCREEN | MC13XXX_USE_RTC | MC13XXX_USE_CODEC,
+
+};
+
+static struct imx_ssi_platform_data mx31_3ds_ssi_pdata = {
+	.flags = IMX_SSI_DMA | IMX_SSI_NET,
 };
 
 /* SPI */
@@ -741,6 +757,10 @@
 	}
 
 	mx31_3ds_init_camera();
+
+	imx31_add_imx_ssi(0, &mx31_3ds_ssi_pdata);
+
+	imx_add_platform_device("imx_mc13783", 0, NULL, 0, NULL, 0);
 }
 
 static void __init mx31_3ds_timer_init(void)
diff --git a/arch/arm/mach-imx/mach-mx31moboard.c b/arch/arm/mach-imx/mach-mx31moboard.c
index 1dfe3c7..016791f 100644
--- a/arch/arm/mach-imx/mach-mx31moboard.c
+++ b/arch/arm/mach-imx/mach-mx31moboard.c
@@ -47,6 +47,7 @@
 #include <mach/hardware.h>
 #include <mach/iomux-mx3.h>
 #include <mach/ulpi.h>
+#include <mach/ssi.h>
 
 #include "devices-imx31.h"
 
@@ -102,6 +103,9 @@
 	MX31_PIN_CSPI3_MOSI__MOSI, MX31_PIN_CSPI3_MISO__MISO,
 	MX31_PIN_CSPI3_SCLK__SCLK, MX31_PIN_CSPI3_SPI_RDY__SPI_RDY,
 	MX31_PIN_CSPI2_SS1__CSPI3_SS1,
+	/* SSI */
+	MX31_PIN_STXD4__STXD4, MX31_PIN_SRXD4__SRXD4,
+	MX31_PIN_SCK4__SCK4, MX31_PIN_SFS4__SFS4,
 };
 
 static struct physmap_flash_data mx31moboard_flash_data = {
@@ -276,6 +280,11 @@
 	.b1on_key = KEY_POWER,
 };
 
+static struct mc13xxx_codec_platform_data moboard_codec = {
+	.dac_ssi_port = MC13783_SSI1_PORT,
+	.adc_ssi_port = MC13783_SSI1_PORT,
+};
+
 static struct mc13xxx_platform_data moboard_pmic = {
 	.regulators = {
 		.regulators = moboard_regulators,
@@ -283,7 +292,12 @@
 	},
 	.leds = &moboard_leds,
 	.buttons = &moboard_buttons,
-	.flags = MC13XXX_USE_RTC | MC13XXX_USE_ADC,
+	.codec = &moboard_codec,
+	.flags = MC13XXX_USE_RTC | MC13XXX_USE_ADC | MC13XXX_USE_CODEC,
+};
+
+static struct imx_ssi_platform_data moboard_ssi_pdata = {
+	.flags = IMX_SSI_DMA | IMX_SSI_NET,
 };
 
 static struct spi_board_info moboard_spi_board_info[] __initdata = {
@@ -554,6 +568,10 @@
 
 	moboard_usbh2_init();
 
+	imx31_add_imx_ssi(0, &moboard_ssi_pdata);
+
+	imx_add_platform_device("imx_mc13783", 0, NULL, 0, NULL, 0);
+
 	pm_power_off = mx31moboard_poweroff;
 
 	switch (mx31moboard_baseboard) {
diff --git a/arch/arm/mach-shmobile/Kconfig b/arch/arm/mach-shmobile/Kconfig
index 98327b7..f31383c 100644
--- a/arch/arm/mach-shmobile/Kconfig
+++ b/arch/arm/mach-shmobile/Kconfig
@@ -64,6 +64,7 @@
 	depends on ARCH_SH7372
 	select ARCH_REQUIRE_GPIOLIB
 	select SH_LCD_MIPI_DSI
+	select SND_SOC_AK4642 if SND_SIMPLE_CARD
 
 choice
 	prompt "AP4EVB LCD panel selection"
@@ -88,6 +89,7 @@
 	bool "mackerel board"
 	depends on ARCH_SH7372
 	select ARCH_REQUIRE_GPIOLIB
+	select SND_SOC_AK4642 if SND_SIMPLE_CARD
 
 config MACH_KOTA2
 	bool "KOTA2 board"
diff --git a/arch/arm/mach-shmobile/board-ap4evb.c b/arch/arm/mach-shmobile/board-ap4evb.c
index 0c3caeb..b540b8e 100644
--- a/arch/arm/mach-shmobile/board-ap4evb.c
+++ b/arch/arm/mach-shmobile/board-ap4evb.c
@@ -50,6 +50,7 @@
 #include <media/soc_camera.h>
 
 #include <sound/sh_fsi.h>
+#include <sound/simple_card.h>
 
 #include <video/sh_mobile_hdmi.h>
 #include <video/sh_mobile_lcdc.h>
@@ -785,17 +786,25 @@
 	},
 };
 
-static struct fsi_ak4642_info fsi2_ak4643_info = {
+static struct asoc_simple_dai_init_info fsi2_ak4643_init_info = {
+	.fmt		= SND_SOC_DAIFMT_LEFT_J,
+	.codec_daifmt	= SND_SOC_DAIFMT_CBM_CFM,
+	.cpu_daifmt	= SND_SOC_DAIFMT_CBS_CFS,
+	.sysclk		= 11289600,
+};
+
+static struct asoc_simple_card_info fsi2_ak4643_info = {
 	.name		= "AK4643",
 	.card		= "FSI2A-AK4643",
 	.cpu_dai	= "fsia-dai",
 	.codec		= "ak4642-codec.0-0013",
 	.platform	= "sh_fsi2",
-	.id		= FSI_PORT_A,
+	.codec_dai	= "ak4642-hifi",
+	.init		= &fsi2_ak4643_init_info,
 };
 
 static struct platform_device fsi_ak4643_device = {
-	.name	= "fsi-ak4642-audio",
+	.name	= "asoc-simple-card",
 	.dev	= {
 		.platform_data	= &fsi2_ak4643_info,
 	},
@@ -900,8 +909,26 @@
 	},
 };
 
+static struct asoc_simple_dai_init_info fsi2_hdmi_init_info = {
+	.cpu_daifmt	= SND_SOC_DAIFMT_CBM_CFM,
+};
+
+static struct asoc_simple_card_info fsi2_hdmi_info = {
+	.name		= "HDMI",
+	.card		= "FSI2B-HDMI",
+	.cpu_dai	= "fsib-dai",
+	.codec		= "sh-mobile-hdmi",
+	.platform	= "sh_fsi2",
+	.codec_dai	= "sh_mobile_hdmi-hifi",
+	.init		= &fsi2_hdmi_init_info,
+};
+
 static struct platform_device fsi_hdmi_device = {
-	.name		= "sh_fsi2_b_hdmi",
+	.name	= "asoc-simple-card",
+	.id	= 1,
+	.dev	= {
+		.platform_data	= &fsi2_hdmi_info,
+	},
 };
 
 static struct gpio_led ap4evb_leds[] = {
diff --git a/arch/arm/mach-shmobile/board-mackerel.c b/arch/arm/mach-shmobile/board-mackerel.c
index aae2e24..50c67b2 100644
--- a/arch/arm/mach-shmobile/board-mackerel.c
+++ b/arch/arm/mach-shmobile/board-mackerel.c
@@ -53,6 +53,7 @@
 #include <media/soc_camera.h>
 #include <media/soc_camera_platform.h>
 #include <sound/sh_fsi.h>
+#include <sound/simple_card.h>
 
 #include <mach/common.h>
 #include <mach/irqs.h>
@@ -502,8 +503,26 @@
 	},
 };
 
+static struct asoc_simple_dai_init_info fsi2_hdmi_init_info = {
+	.cpu_daifmt	= SND_SOC_DAIFMT_CBM_CFM,
+};
+
+static struct asoc_simple_card_info fsi2_hdmi_info = {
+	.name		= "HDMI",
+	.card		= "FSI2B-HDMI",
+	.cpu_dai	= "fsib-dai",
+	.codec		= "sh-mobile-hdmi",
+	.platform	= "sh_fsi2",
+	.codec_dai	= "sh_mobile_hdmi-hifi",
+	.init		= &fsi2_hdmi_init_info,
+};
+
 static struct platform_device fsi_hdmi_device = {
-	.name		= "sh_fsi2_b_hdmi",
+	.name	= "asoc-simple-card",
+	.id	= 1,
+	.dev	= {
+		.platform_data	= &fsi2_hdmi_info,
+	},
 };
 
 static void __init hdmi_init_pm_clock(void)
@@ -945,17 +964,25 @@
 	},
 };
 
-static struct fsi_ak4642_info fsi2_ak4643_info = {
+static struct asoc_simple_dai_init_info fsi2_ak4643_init_info = {
+	.fmt		= SND_SOC_DAIFMT_LEFT_J,
+	.codec_daifmt	= SND_SOC_DAIFMT_CBM_CFM,
+	.cpu_daifmt	= SND_SOC_DAIFMT_CBS_CFS,
+	.sysclk		= 11289600,
+};
+
+static struct asoc_simple_card_info fsi2_ak4643_info = {
 	.name		= "AK4643",
 	.card		= "FSI2A-AK4643",
 	.cpu_dai	= "fsia-dai",
 	.codec		= "ak4642-codec.0-0013",
 	.platform	= "sh_fsi2",
-	.id		= FSI_PORT_A,
+	.codec_dai	= "ak4642-hifi",
+	.init		= &fsi2_ak4643_init_info,
 };
 
 static struct platform_device fsi_ak4643_device = {
-	.name	= "fsi-ak4642-audio",
+	.name	= "asoc-simple-card",
 	.dev	= {
 		.platform_data	= &fsi2_ak4643_info,
 	},
diff --git a/arch/arm/mach-tegra/board-dt-tegra20.c b/arch/arm/mach-tegra/board-dt-tegra20.c
index 8351c4c..fac3eb1 100644
--- a/arch/arm/mach-tegra/board-dt-tegra20.c
+++ b/arch/arm/mach-tegra/board-dt-tegra20.c
@@ -55,9 +55,9 @@
 	OF_DEV_AUXDATA("nvidia,tegra20-i2c", TEGRA_I2C2_BASE, "tegra-i2c.1", NULL),
 	OF_DEV_AUXDATA("nvidia,tegra20-i2c", TEGRA_I2C3_BASE, "tegra-i2c.2", NULL),
 	OF_DEV_AUXDATA("nvidia,tegra20-i2c-dvc", TEGRA_DVC_BASE, "tegra-i2c.3", NULL),
-	OF_DEV_AUXDATA("nvidia,tegra20-i2s", TEGRA_I2S1_BASE, "tegra-i2s.0", NULL),
-	OF_DEV_AUXDATA("nvidia,tegra20-i2s", TEGRA_I2S2_BASE, "tegra-i2s.1", NULL),
-	OF_DEV_AUXDATA("nvidia,tegra20-das", TEGRA_APB_MISC_DAS_BASE, "tegra-das", NULL),
+	OF_DEV_AUXDATA("nvidia,tegra20-i2s", TEGRA_I2S1_BASE, "tegra20-i2s.0", NULL),
+	OF_DEV_AUXDATA("nvidia,tegra20-i2s", TEGRA_I2S2_BASE, "tegra20-i2s.1", NULL),
+	OF_DEV_AUXDATA("nvidia,tegra20-das", TEGRA_APB_MISC_DAS_BASE, "tegra20-das", NULL),
 	OF_DEV_AUXDATA("nvidia,tegra20-ehci", TEGRA_USB_BASE, "tegra-ehci.0",
 		       &tegra_ehci1_pdata),
 	OF_DEV_AUXDATA("nvidia,tegra20-ehci", TEGRA_USB2_BASE, "tegra-ehci.1",
diff --git a/arch/arm/mach-tegra/board-harmony.c b/arch/arm/mach-tegra/board-harmony.c
index 222182e..b906b3b 100644
--- a/arch/arm/mach-tegra/board-harmony.c
+++ b/arch/arm/mach-tegra/board-harmony.c
@@ -124,7 +124,6 @@
 	&tegra_ehci3_device,
 	&tegra_i2s_device1,
 	&tegra_das_device,
-	&tegra_pcm_device,
 	&harmony_audio_device,
 };
 
diff --git a/arch/arm/mach-tegra/board-seaboard.c b/arch/arm/mach-tegra/board-seaboard.c
index 20743bc..79064c7 100644
--- a/arch/arm/mach-tegra/board-seaboard.c
+++ b/arch/arm/mach-tegra/board-seaboard.c
@@ -156,7 +156,6 @@
 	&seaboard_gpio_keys_device,
 	&tegra_i2s_device1,
 	&tegra_das_device,
-	&tegra_pcm_device,
 	&seaboard_audio_device,
 };
 
diff --git a/arch/arm/mach-tegra/board-trimslice.c b/arch/arm/mach-tegra/board-trimslice.c
index 0a00183..bc59b37 100644
--- a/arch/arm/mach-tegra/board-trimslice.c
+++ b/arch/arm/mach-tegra/board-trimslice.c
@@ -89,7 +89,6 @@
 	&tegra_sdhci_device4,
 	&tegra_i2s_device1,
 	&tegra_das_device,
-	&tegra_pcm_device,
 	&trimslice_audio_device,
 };
 
diff --git a/arch/arm/mach-tegra/devices.c b/arch/arm/mach-tegra/devices.c
index bd3035e..2d8dfa2 100644
--- a/arch/arm/mach-tegra/devices.c
+++ b/arch/arm/mach-tegra/devices.c
@@ -674,14 +674,14 @@
 };
 
 struct platform_device tegra_i2s_device1 = {
-	.name		= "tegra-i2s",
+	.name		= "tegra20-i2s",
 	.id		= 0,
 	.resource	= i2s_resource1,
 	.num_resources	= ARRAY_SIZE(i2s_resource1),
 };
 
 struct platform_device tegra_i2s_device2 = {
-	.name		= "tegra-i2s",
+	.name		= "tegra20-i2s",
 	.id		= 1,
 	.resource	= i2s_resource2,
 	.num_resources	= ARRAY_SIZE(i2s_resource2),
@@ -696,13 +696,8 @@
 };
 
 struct platform_device tegra_das_device = {
-	.name		= "tegra-das",
+	.name		= "tegra20-das",
 	.id		= -1,
 	.num_resources	= ARRAY_SIZE(tegra_das_resources),
 	.resource	= tegra_das_resources,
 };
-
-struct platform_device tegra_pcm_device = {
-	.name = "tegra-pcm-audio",
-	.id = -1,
-};
diff --git a/arch/arm/mach-tegra/devices.h b/arch/arm/mach-tegra/devices.h
index ec45567..138c642 100644
--- a/arch/arm/mach-tegra/devices.h
+++ b/arch/arm/mach-tegra/devices.h
@@ -52,6 +52,5 @@
 extern struct platform_device tegra_i2s_device1;
 extern struct platform_device tegra_i2s_device2;
 extern struct platform_device tegra_das_device;
-extern struct platform_device tegra_pcm_device;
 
 #endif
diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c
index 2cae5cb..bae09b8 100644
--- a/arch/arm/mach-tegra/tegra2_clocks.c
+++ b/arch/arm/mach-tegra/tegra2_clocks.c
@@ -2148,8 +2148,8 @@
 	PERIPH_CLK("apbdma",	"tegra-dma",		NULL,	34,	0,	108000000, mux_pclk,			0),
 	PERIPH_CLK("rtc",	"rtc-tegra",		NULL,	4,	0,	32768,     mux_clk_32k,			PERIPH_NO_RESET),
 	PERIPH_CLK("timer",	"timer",		NULL,	5,	0,	26000000,  mux_clk_m,			0),
-	PERIPH_CLK("i2s1",	"tegra-i2s.0",		NULL,	11,	0x100,	26000000,  mux_pllaout0_audio2x_pllp_clkm,	MUX | DIV_U71),
-	PERIPH_CLK("i2s2",	"tegra-i2s.1",		NULL,	18,	0x104,	26000000,  mux_pllaout0_audio2x_pllp_clkm,	MUX | DIV_U71),
+	PERIPH_CLK("i2s1",	"tegra20-i2s.0",	NULL,	11,	0x100,	26000000,  mux_pllaout0_audio2x_pllp_clkm,	MUX | DIV_U71),
+	PERIPH_CLK("i2s2",	"tegra20-i2s.1",	NULL,	18,	0x104,	26000000,  mux_pllaout0_audio2x_pllp_clkm,	MUX | DIV_U71),
 	PERIPH_CLK("spdif_out",	"spdif_out",		NULL,	10,	0x108,	100000000, mux_pllaout0_audio2x_pllp_clkm,	MUX | DIV_U71),
 	PERIPH_CLK("spdif_in",	"spdif_in",		NULL,	10,	0x10c,	100000000, mux_pllp_pllc_pllm,		MUX | DIV_U71),
 	PERIPH_CLK("pwm",	"pwm",			NULL,	17,	0x110,	432000000, mux_pllp_pllc_audio_clkm_clk32,	MUX | DIV_U71),
diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c
index 4bc0cbc..f943687 100644
--- a/arch/arm/mach-ux500/board-mop500.c
+++ b/arch/arm/mach-ux500/board-mop500.c
@@ -47,6 +47,7 @@
 #include <mach/setup.h>
 #include <mach/devices.h>
 #include <mach/irqs.h>
+#include <mach/crypto-ux500.h>
 
 #include "ste-dma40-db8500.h"
 #include "devices-db8500.h"
@@ -417,6 +418,45 @@
 	regulator_put(prox_regulator);
 }
 
+static struct cryp_platform_data u8500_cryp1_platform_data = {
+		.mem_to_engine = {
+				.dir = STEDMA40_MEM_TO_PERIPH,
+				.src_dev_type = STEDMA40_DEV_SRC_MEMORY,
+				.dst_dev_type = DB8500_DMA_DEV48_CAC1_TX,
+				.src_info.data_width = STEDMA40_WORD_WIDTH,
+				.dst_info.data_width = STEDMA40_WORD_WIDTH,
+				.mode = STEDMA40_MODE_LOGICAL,
+				.src_info.psize = STEDMA40_PSIZE_LOG_4,
+				.dst_info.psize = STEDMA40_PSIZE_LOG_4,
+		},
+		.engine_to_mem = {
+				.dir = STEDMA40_PERIPH_TO_MEM,
+				.src_dev_type = DB8500_DMA_DEV48_CAC1_RX,
+				.dst_dev_type = STEDMA40_DEV_DST_MEMORY,
+				.src_info.data_width = STEDMA40_WORD_WIDTH,
+				.dst_info.data_width = STEDMA40_WORD_WIDTH,
+				.mode = STEDMA40_MODE_LOGICAL,
+				.src_info.psize = STEDMA40_PSIZE_LOG_4,
+				.dst_info.psize = STEDMA40_PSIZE_LOG_4,
+		}
+};
+
+static struct stedma40_chan_cfg u8500_hash_dma_cfg_tx = {
+		.dir = STEDMA40_MEM_TO_PERIPH,
+		.src_dev_type = STEDMA40_DEV_SRC_MEMORY,
+		.dst_dev_type = DB8500_DMA_DEV50_HAC1_TX,
+		.src_info.data_width = STEDMA40_WORD_WIDTH,
+		.dst_info.data_width = STEDMA40_WORD_WIDTH,
+		.mode = STEDMA40_MODE_LOGICAL,
+		.src_info.psize = STEDMA40_PSIZE_LOG_16,
+		.dst_info.psize = STEDMA40_PSIZE_LOG_16,
+};
+
+static struct hash_platform_data u8500_hash1_platform_data = {
+		.mem_to_engine = &u8500_hash_dma_cfg_tx,
+		.dma_filter = stedma40_filter,
+};
+
 /* add any platform devices here - TODO */
 static struct platform_device *mop500_platform_devs[] __initdata = {
 	&mop500_gpio_keys_device,
@@ -624,6 +664,12 @@
 	db8500_add_uart2(parent, &uart2_plat);
 }
 
+static void __init u8500_cryp1_hash1_init(struct device *parent)
+{
+	db8500_add_cryp1(parent, &u8500_cryp1_platform_data);
+	db8500_add_hash1(parent, &u8500_hash1_platform_data);
+}
+
 static struct platform_device *snowball_platform_devs[] __initdata = {
 	&snowball_led_dev,
 	&snowball_key_dev,
@@ -654,6 +700,8 @@
 	mop500_msp_init(parent);
 	mop500_uart_init(parent);
 
+	u8500_cryp1_hash1_init(parent);
+
 	i2c0_devs = ARRAY_SIZE(mop500_i2c0_devices);
 
 	i2c_register_board_info(0, mop500_i2c0_devices, i2c0_devs);
diff --git a/arch/arm/mach-ux500/clock.c b/arch/arm/mach-ux500/clock.c
index a121cb4..1762c47 100644
--- a/arch/arm/mach-ux500/clock.c
+++ b/arch/arm/mach-ux500/clock.c
@@ -381,14 +381,15 @@
 /* Peripheral Cluster #6 */
 
 /* MTU ID in data */
-static DEFINE_PRCC_CLK_CUSTOM(6, mtu1, 8, -1, NULL, clk_mtu_get_rate, 1);
-static DEFINE_PRCC_CLK_CUSTOM(6, mtu0, 7, -1, NULL, clk_mtu_get_rate, 0);
-static DEFINE_PRCC_CLK(6, cfgreg,	6,  6, NULL);
-static DEFINE_PRCC_CLK(6, hash1,	5, -1, NULL);
-static DEFINE_PRCC_CLK(6, unipro,	4,  1, &clk_uniproclk);
-static DEFINE_PRCC_CLK(6, pka,		3, -1, NULL);
-static DEFINE_PRCC_CLK(6, hash0,	2, -1, NULL);
-static DEFINE_PRCC_CLK(6, cryp0,	1, -1, NULL);
+static DEFINE_PRCC_CLK_CUSTOM(6, mtu1, 9, -1, NULL, clk_mtu_get_rate, 1);
+static DEFINE_PRCC_CLK_CUSTOM(6, mtu0, 8, -1, NULL, clk_mtu_get_rate, 0);
+static DEFINE_PRCC_CLK(6, cfgreg,	7,  7, NULL);
+static DEFINE_PRCC_CLK(6, hash1,	6, -1, NULL);
+static DEFINE_PRCC_CLK(6, unipro,	5,  1, &clk_uniproclk);
+static DEFINE_PRCC_CLK(6, pka,		4, -1, NULL);
+static DEFINE_PRCC_CLK(6, hash0,	3, -1, NULL);
+static DEFINE_PRCC_CLK(6, cryp0,	2, -1, NULL);
+static DEFINE_PRCC_CLK(6, cryp1,    1, -1, NULL);
 static DEFINE_PRCC_CLK(6, rng,	0,  0, &clk_rngclk);
 
 static struct clk clk_dummy_apb_pclk = {
@@ -430,6 +431,7 @@
 	CLK(pka,	"pka",		NULL),
 	CLK(hash0,	"hash0",	NULL),
 	CLK(cryp0,	"cryp0",	NULL),
+	CLK(cryp1,  "cryp1",    NULL),
 
 	/* PRCMU level clock gating */
 
diff --git a/arch/arm/mach-ux500/devices-common.h b/arch/arm/mach-ux500/devices-common.h
index 7cbccfd..6e47065 100644
--- a/arch/arm/mach-ux500/devices-common.h
+++ b/arch/arm/mach-ux500/devices-common.h
@@ -13,6 +13,7 @@
 #include <linux/sys_soc.h>
 #include <linux/amba/bus.h>
 #include <plat/i2c.h>
+#include <mach/crypto-ux500.h>
 
 struct spi_master_cntlr;
 
@@ -85,6 +86,55 @@
 				0, NULL, 0);
 }
 
+struct cryp_platform_data;
+
+static inline struct platform_device *
+dbx500_add_cryp1(struct device *parent, int id, resource_size_t base, int irq,
+		struct cryp_platform_data *pdata)
+{
+	struct resource res[] = {
+			DEFINE_RES_MEM(base, SZ_4K),
+			DEFINE_RES_IRQ(irq),
+	};
+
+	struct platform_device_info pdevinfo = {
+			.parent = parent,
+			.name = "cryp1",
+			.id = id,
+			.res = res,
+			.num_res = ARRAY_SIZE(res),
+			.data = pdata,
+			.size_data = sizeof(*pdata),
+			.dma_mask = DMA_BIT_MASK(32),
+	};
+
+	return platform_device_register_full(&pdevinfo);
+}
+
+struct hash_platform_data;
+
+static inline struct platform_device *
+dbx500_add_hash1(struct device *parent, int id, resource_size_t base,
+		struct hash_platform_data *pdata)
+{
+	struct resource res[] = {
+			DEFINE_RES_MEM(base, SZ_4K),
+	};
+
+	struct platform_device_info pdevinfo = {
+			.parent = parent,
+			.name = "hash1",
+			.id = id,
+			.res = res,
+			.num_res = ARRAY_SIZE(res),
+			.data = pdata,
+			.size_data = sizeof(*pdata),
+			.dma_mask = DMA_BIT_MASK(32),
+	};
+
+	return platform_device_register_full(&pdevinfo);
+}
+
 struct nmk_gpio_platform_data;
 
 void dbx500_add_gpios(struct device *parent, resource_size_t *base, int num,
diff --git a/arch/arm/mach-ux500/devices-db8500.c b/arch/arm/mach-ux500/devices-db8500.c
index 6e66d37..91754a8 100644
--- a/arch/arm/mach-ux500/devices-db8500.c
+++ b/arch/arm/mach-ux500/devices-db8500.c
@@ -104,6 +104,8 @@
 	[DB8500_DMA_DEV14_MSP2_TX] = U8500_MSP2_BASE + MSP_TX_RX_REG_OFFSET,
 	[DB8500_DMA_DEV30_MSP1_TX] = U8500_MSP1_BASE + MSP_TX_RX_REG_OFFSET,
 	[DB8500_DMA_DEV31_MSP0_TX_SLIM0_CH0_TX] = U8500_MSP0_BASE + MSP_TX_RX_REG_OFFSET,
+	[DB8500_DMA_DEV48_CAC1_TX] = U8500_CRYP1_BASE + CRYP1_TX_REG_OFFSET,
+	[DB8500_DMA_DEV50_HAC1_TX] = U8500_HASH1_BASE + HASH1_TX_REG_OFFSET,
 };
 
 /* Mapping between source event lines and physical device address */
@@ -139,6 +141,7 @@
 	[DB8500_DMA_DEV14_MSP2_RX] = U8500_MSP2_BASE + MSP_TX_RX_REG_OFFSET,
 	[DB8500_DMA_DEV30_MSP3_RX] = U8500_MSP3_BASE + MSP_TX_RX_REG_OFFSET,
 	[DB8500_DMA_DEV31_MSP0_RX_SLIM0_CH0_RX] = U8500_MSP0_BASE + MSP_TX_RX_REG_OFFSET,
+	[DB8500_DMA_DEV48_CAC1_RX] = U8500_CRYP1_BASE + CRYP1_RX_REG_OFFSET,
 };
 
 /* Reserved event lines for memcpy only */
diff --git a/arch/arm/mach-ux500/devices-db8500.h b/arch/arm/mach-ux500/devices-db8500.h
index 0b9677a..3c8010f 100644
--- a/arch/arm/mach-ux500/devices-db8500.h
+++ b/arch/arm/mach-ux500/devices-db8500.h
@@ -114,4 +114,8 @@
 	dbx500_add_uart(parent, "uart2", U8500_UART2_BASE, \
 			IRQ_DB8500_UART2, pdata)
 
+#define db8500_add_cryp1(parent, pdata) \
+	dbx500_add_cryp1(parent, -1, U8500_CRYP1_BASE, IRQ_DB8500_CRYP1, pdata)
+#define db8500_add_hash1(parent, pdata) \
+	dbx500_add_hash1(parent, -1, U8500_HASH1_BASE, pdata)
 #endif
diff --git a/arch/arm/mach-ux500/include/mach/crypto-ux500.h b/arch/arm/mach-ux500/include/mach/crypto-ux500.h
new file mode 100644
index 0000000..5b2d081
--- /dev/null
+++ b/arch/arm/mach-ux500/include/mach/crypto-ux500.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * Author: Joakim Bech <joakim.xx.bech@stericsson.com> for ST-Ericsson
+ * License terms: GNU General Public License (GPL) version 2
+ */
+#ifndef _CRYPTO_UX500_H
+#define _CRYPTO_UX500_H
+#include <linux/dmaengine.h>
+#include <plat/ste_dma40.h>
+
+struct hash_platform_data {
+	void *mem_to_engine;
+	bool (*dma_filter)(struct dma_chan *chan, void *filter_param);
+};
+
+struct cryp_platform_data {
+	struct stedma40_chan_cfg mem_to_engine;
+	struct stedma40_chan_cfg engine_to_mem;
+};
+
+#endif
diff --git a/arch/arm/mach-ux500/include/mach/devices.h b/arch/arm/mach-ux500/include/mach/devices.h
index 9b5eb69..cbc6f1e 100644
--- a/arch/arm/mach-ux500/include/mach/devices.h
+++ b/arch/arm/mach-ux500/include/mach/devices.h
@@ -14,6 +14,9 @@
 
 extern struct amba_device ux500_pl031_device;
 
+extern struct platform_device ux500_hash1_device;
+extern struct platform_device ux500_cryp1_device;
+
 extern struct platform_device u8500_dma40_device;
 extern struct platform_device ux500_ske_keypad_device;
 
diff --git a/arch/arm/mach-ux500/include/mach/hardware.h b/arch/arm/mach-ux500/include/mach/hardware.h
index 808c1d6..28d16e7 100644
--- a/arch/arm/mach-ux500/include/mach/hardware.h
+++ b/arch/arm/mach-ux500/include/mach/hardware.h
@@ -33,6 +33,9 @@
 #include <mach/db8500-regs.h>
 
 #define MSP_TX_RX_REG_OFFSET	0
+#define CRYP1_RX_REG_OFFSET	0x10
+#define CRYP1_TX_REG_OFFSET	0x8
+#define HASH1_TX_REG_OFFSET	0x4
 
 #ifndef __ASSEMBLY__
 
diff --git a/arch/avr32/include/asm/processor.h b/arch/avr32/include/asm/processor.h
index 108502b..87d8bac 100644
--- a/arch/avr32/include/asm/processor.h
+++ b/arch/avr32/include/asm/processor.h
@@ -145,9 +145,6 @@
 /* Create a kernel thread without removing it from tasklists */
 extern int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags);
 
-/* Prepare to copy thread state - unlazy all lazy status */
-#define prepare_to_copy(tsk) do { } while(0)
-
 /* Return saved PC of a blocked thread */
 #define thread_saved_pc(tsk)    ((tsk)->thread.cpu_context.pc)
 
diff --git a/arch/blackfin/include/asm/processor.h b/arch/blackfin/include/asm/processor.h
index 8af7772..4ef7cfe 100644
--- a/arch/blackfin/include/asm/processor.h
+++ b/arch/blackfin/include/asm/processor.h
@@ -75,8 +75,6 @@
 {
 }
 
-#define prepare_to_copy(tsk)	do { } while (0)
-
 extern int kernel_thread(int (*fn) (void *), void *arg, unsigned long flags);
 
 /*
diff --git a/arch/c6x/include/asm/processor.h b/arch/c6x/include/asm/processor.h
index 3ff7fab..c50af7e 100644
--- a/arch/c6x/include/asm/processor.h
+++ b/arch/c6x/include/asm/processor.h
@@ -92,9 +92,6 @@
 {
 }
 
-/* Prepare to copy thread state - unlazy all lazy status */
-#define prepare_to_copy(tsk)	do { } while (0)
-
 extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
 
 #define copy_segments(tsk, mm)		do { } while (0)
diff --git a/arch/cris/include/asm/processor.h b/arch/cris/include/asm/processor.h
index 8dc56ef..ef4e1bc 100644
--- a/arch/cris/include/asm/processor.h
+++ b/arch/cris/include/asm/processor.h
@@ -49,10 +49,6 @@
 #define task_pt_regs(task) user_regs(task_thread_info(task))
 #define current_regs() task_pt_regs(current)
 
-static inline void prepare_to_copy(struct task_struct *tsk)
-{
-}
-
 extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
 
 unsigned long get_wchan(struct task_struct *p);
diff --git a/arch/frv/include/asm/processor.h b/arch/frv/include/asm/processor.h
index 9b1a92b..dccb9d1 100644
--- a/arch/frv/include/asm/processor.h
+++ b/arch/frv/include/asm/processor.h
@@ -54,7 +54,6 @@
  * Bus types
  */
 #define EISA_bus 0
-#define MCA_bus 0
 
 struct thread_struct {
 	struct pt_regs		*frame;		/* [GR28] exception frame ptr for this thread */
@@ -103,8 +102,6 @@
 	__frame->sp	= (_usp);			\
 } while(0)
 
-extern void prepare_to_copy(struct task_struct *tsk);
-
 /* Free all resources held by a thread. */
 static inline void release_thread(struct task_struct *dead_task)
 {
diff --git a/arch/frv/kernel/process.c b/arch/frv/kernel/process.c
index ed09e9e..ff95f50 100644
--- a/arch/frv/kernel/process.c
+++ b/arch/frv/kernel/process.c
@@ -165,17 +165,6 @@
 	return do_fork(clone_flags, newsp, __frame, 0, parent_tidptr, child_tidptr);
 } /* end sys_clone() */
 
-/*****************************************************************************/
-/*
- * This gets called before we allocate a new thread and copy
- * the current task into it.
- */
-void prepare_to_copy(struct task_struct *tsk)
-{
-	//unlazy_fpu(tsk);
-} /* end prepare_to_copy() */
-
-/*****************************************************************************/
 /*
  * set up the kernel stack and exception frames for a new process
  */
diff --git a/arch/h8300/include/asm/processor.h b/arch/h8300/include/asm/processor.h
index 61fabf1..4c9f6f8 100644
--- a/arch/h8300/include/asm/processor.h
+++ b/arch/h8300/include/asm/processor.h
@@ -109,8 +109,6 @@
 
 extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
 
-#define prepare_to_copy(tsk)	do { } while (0)
-
 /*
  * Free current thread data structures etc..
  */
diff --git a/arch/hexagon/Kconfig b/arch/hexagon/Kconfig
index 22615dd..bc979f7 100644
--- a/arch/hexagon/Kconfig
+++ b/arch/hexagon/Kconfig
@@ -13,8 +13,8 @@
 	# select ARCH_REQUIRE_GPIOLIB
 	# select HAVE_CLK
 	# select IRQ_PER_CPU
-	select HAVE_IRQ_WORK
 	# select GENERIC_PENDING_IRQ if SMP
+	select HAVE_IRQ_WORK
 	select GENERIC_ATOMIC64
 	select HAVE_PERF_EVENTS
 	select HAVE_GENERIC_HARDIRQS
@@ -26,7 +26,7 @@
 	select NO_IOPORT
 	select GENERIC_IOMAP
 	select GENERIC_SMP_IDLE_THREAD
-	# mostly generic routines, with some accelerated ones
+	select STACKTRACE_SUPPORT
 	---help---
 	  Qualcomm Hexagon is a processor architecture designed for high
 	  performance and low power across a wide variety of applications.
@@ -73,15 +73,6 @@
 config GENERIC_IRQ_PROBE
 	def_bool y
 
-#config ZONE_DMA
-#	bool
-#	default y
-
-config HAS_DMA
-	bool
-	select HAVE_DMA_ATTRS
-	default y
-
 config NEED_SG_DMA_LENGTH
 	def_bool y
 
@@ -114,14 +105,11 @@
 	def_bool y
 	depends on BUG
 
-config BUG
-	def_bool y
-
 menu "Machine selection"
 
 choice
 	prompt "System type"
-	default HEXAGON_ARCH_V2
+	default HEXAGON_COMET
 
 config HEXAGON_COMET
 	bool "Comet Board"
@@ -194,8 +182,7 @@
 source "kernel/time/Kconfig"
 
 config GENERIC_GPIO
-	bool "Generic GPIO support"
-	default n
+	def_bool n
 
 endmenu
 
diff --git a/arch/hexagon/Makefile b/arch/hexagon/Makefile
index e27d030..d00d900 100644
--- a/arch/hexagon/Makefile
+++ b/arch/hexagon/Makefile
@@ -50,8 +50,3 @@
 core-y += arch/hexagon/kernel/ \
 	arch/hexagon/mm/ \
 	arch/hexagon/lib/
-
-#	arch/hexagon/platform/common/
-#
-#core-$(CONFIG_HEXAGON_COMET)		+= arch/hexagon/platform/comet/
-#machine-$(CONFIG_HEXAGON_COMET)		:= comet
diff --git a/arch/hexagon/include/asm/processor.h b/arch/hexagon/include/asm/processor.h
index 20c5dda..e8ea459 100644
--- a/arch/hexagon/include/asm/processor.h
+++ b/arch/hexagon/include/asm/processor.h
@@ -59,13 +59,6 @@
 #define cpu_relax() __vmyield()
 
 /*
- * "Unlazying all lazy status" occurs here.
- */
-static inline void prepare_to_copy(struct task_struct *tsk)
-{
-}
-
-/*
  * Decides where the kernel will search for a free chunk of vm space during
  * mmaps.
  * See also arch_get_unmapped_area.
diff --git a/arch/hexagon/include/asm/spinlock_types.h b/arch/hexagon/include/asm/spinlock_types.h
index 5e937af..99b5a75 100644
--- a/arch/hexagon/include/asm/spinlock_types.h
+++ b/arch/hexagon/include/asm/spinlock_types.h
@@ -21,8 +21,6 @@
 #ifndef _ASM_SPINLOCK_TYPES_H
 #define _ASM_SPINLOCK_TYPES_H
 
-#include <linux/version.h>
-
 #ifndef __LINUX_SPINLOCK_TYPES_H
 # error "please don't include this file directly"
 #endif
diff --git a/arch/hexagon/kernel/dma.c b/arch/hexagon/kernel/dma.c
index 0f2367c..2b48751 100644
--- a/arch/hexagon/kernel/dma.c
+++ b/arch/hexagon/kernel/dma.c
@@ -54,7 +54,7 @@
 
 /* Allocates from a pool of uncached memory that was reserved at boot time */
 
-void *hexagon_dma_alloc_coherent(struct device *dev, size_t size,
+static void *hexagon_dma_alloc_coherent(struct device *dev, size_t size,
 				 dma_addr_t *dma_addr, gfp_t flag,
 				 struct dma_attrs *attrs)
 {
diff --git a/arch/hexagon/kernel/time.c b/arch/hexagon/kernel/time.c
index 5d9b33b..36ba641 100644
--- a/arch/hexagon/kernel/time.c
+++ b/arch/hexagon/kernel/time.c
@@ -201,12 +201,10 @@
 		resource = rtos_timer_device.resource;
 
 	/*  ioremap here means this has to run later, after paging init  */
-	rtos_timer = ioremap(resource->start, resource->end
-		- resource->start + 1);
+	rtos_timer = ioremap(resource->start, resource_size(resource));
 
 	if (!rtos_timer) {
-		release_mem_region(resource->start, resource->end
-			- resource->start + 1);
+		release_mem_region(resource->start, resource_size(resource));
 	}
 	clocksource_register_khz(&hexagon_clocksource, pcycle_freq_mhz * 1000);
 
diff --git a/arch/hexagon/mm/vm_fault.c b/arch/hexagon/mm/vm_fault.c
index c10b76f..06695cc 100644
--- a/arch/hexagon/mm/vm_fault.c
+++ b/arch/hexagon/mm/vm_fault.c
@@ -53,6 +53,8 @@
 	int si_code = SEGV_MAPERR;
 	int fault;
 	const struct exception_table_entry *fixup;
+	unsigned int flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE |
+				 (cause > 0 ? FAULT_FLAG_WRITE : 0);
 
 	/*
 	 * If we're in an interrupt or have no user context,
@@ -63,6 +65,7 @@
 
 	local_irq_enable();
 
+retry:
 	down_read(&mm->mmap_sem);
 	vma = find_vma(mm, address);
 	if (!vma)
@@ -96,14 +99,23 @@
 		break;
 	}
 
-	fault = handle_mm_fault(mm, vma, address, (cause > 0));
+	fault = handle_mm_fault(mm, vma, address, flags);
+
+	if ((fault & VM_FAULT_RETRY) && fatal_signal_pending(current))
+		return;
 
 	/* The most common case -- we are done. */
 	if (likely(!(fault & VM_FAULT_ERROR))) {
-		if (fault & VM_FAULT_MAJOR)
-			current->maj_flt++;
-		else
-			current->min_flt++;
+		if (flags & FAULT_FLAG_ALLOW_RETRY) {
+			if (fault & VM_FAULT_MAJOR)
+				current->maj_flt++;
+			else
+				current->min_flt++;
+			if (fault & VM_FAULT_RETRY) {
+				flags &= ~FAULT_FLAG_ALLOW_RETRY;
+				goto retry;
+			}
+		}
 
 		up_read(&mm->mmap_sem);
 		return;
diff --git a/arch/ia64/include/asm/Kbuild b/arch/ia64/include/asm/Kbuild
index 241d1c5..d4eb938 100644
--- a/arch/ia64/include/asm/Kbuild
+++ b/arch/ia64/include/asm/Kbuild
@@ -1,6 +1,7 @@
 include include/asm-generic/Kbuild.asm
 
 header-y += break.h
+header-y += cmpxchg.h
 header-y += fpu.h
 header-y += gcc_intrin.h
 header-y += ia64regs.h
diff --git a/arch/ia64/include/asm/processor.h b/arch/ia64/include/asm/processor.h
index f92f67a..832dd37 100644
--- a/arch/ia64/include/asm/processor.h
+++ b/arch/ia64/include/asm/processor.h
@@ -34,8 +34,7 @@
  * each (assuming 8KB page size), for a total of 8TB of user virtual
  * address space.
  */
-#define TASK_SIZE_OF(tsk)	((tsk)->thread.task_size)
-#define TASK_SIZE       	TASK_SIZE_OF(current)
+#define TASK_SIZE       	DEFAULT_TASK_SIZE
 
 /*
  * This decides where the kernel will search for a free chunk of vm
@@ -280,7 +279,6 @@
 	__u8 pad[3];
 	__u64 ksp;			/* kernel stack pointer */
 	__u64 map_base;			/* base address for get_unmapped_area() */
-	__u64 task_size;		/* limit for task size */
 	__u64 rbs_bot;			/* the base address for the RBS */
 	int last_fph_cpu;		/* CPU that may hold the contents of f32-f127 */
 
@@ -303,7 +301,6 @@
 	.ksp =		0,					\
 	.map_base =	DEFAULT_MAP_BASE,			\
 	.rbs_bot =	STACK_TOP - DEFAULT_USER_STACK_SIZE,	\
-	.task_size =	DEFAULT_TASK_SIZE,			\
 	.last_fph_cpu =  -1,					\
 	INIT_THREAD_PM						\
 	.dbr =		{0, },					\
@@ -343,9 +340,6 @@
  */
 #define release_thread(dead_task)
 
-/* Prepare to copy thread state - unlazy all lazy status */
-#define prepare_to_copy(tsk)	do { } while (0)
-
 /*
  * This is the mechanism for creating a new kernel thread.
  *
diff --git a/arch/ia64/kernel/fsys.S b/arch/ia64/kernel/fsys.S
index cc26eda..e662f17 100644
--- a/arch/ia64/kernel/fsys.S
+++ b/arch/ia64/kernel/fsys.S
@@ -372,175 +372,6 @@
 END(fsys_clock_gettime)
 
 /*
- * long fsys_rt_sigprocmask (int how, sigset_t *set, sigset_t *oset, size_t sigsetsize).
- */
-#if _NSIG_WORDS != 1
-# error Sorry, fsys_rt_sigprocmask() needs to be updated for _NSIG_WORDS != 1.
-#endif
-ENTRY(fsys_rt_sigprocmask)
-	.prologue
-	.altrp b6
-	.body
-
-	add r2=IA64_TASK_BLOCKED_OFFSET,r16
-	add r9=TI_FLAGS+IA64_TASK_SIZE,r16
-	cmp4.ltu p6,p0=SIG_SETMASK,r32
-
-	cmp.ne p15,p0=r0,r34			// oset != NULL?
-	tnat.nz p8,p0=r34
-	add r31=IA64_TASK_SIGHAND_OFFSET,r16
-	;;
-	ld8 r3=[r2]				// read/prefetch current->blocked
-	ld4 r9=[r9]
-	tnat.nz.or p6,p0=r35
-
-	cmp.ne.or p6,p0=_NSIG_WORDS*8,r35
-	tnat.nz.or p6,p0=r32
-(p6)	br.spnt.few .fail_einval		// fail with EINVAL
-	;;
-#ifdef CONFIG_SMP
-	ld8 r31=[r31]				// r31 <- current->sighand
-#endif
-	and r9=TIF_ALLWORK_MASK,r9
-	tnat.nz.or p8,p0=r33
-	;;
-	cmp.ne p7,p0=0,r9
-	cmp.eq p6,p0=r0,r33			// set == NULL?
-	add r31=IA64_SIGHAND_SIGLOCK_OFFSET,r31	// r31 <- current->sighand->siglock
-(p8)	br.spnt.few .fail_efault		// fail with EFAULT
-(p7)	br.spnt.many fsys_fallback_syscall	// got pending kernel work...
-(p6)	br.dpnt.many .store_mask		// -> short-circuit to just reading the signal mask
-
-	/* Argh, we actually have to do some work and _update_ the signal mask: */
-
-EX(.fail_efault, probe.r.fault r33, 3)		// verify user has read-access to *set
-EX(.fail_efault, ld8 r14=[r33])			// r14 <- *set
-	mov r17=(1 << (SIGKILL - 1)) | (1 << (SIGSTOP - 1))
-	;;
-
-	RSM_PSR_I(p0, r18, r19)			// mask interrupt delivery
-	andcm r14=r14,r17			// filter out SIGKILL & SIGSTOP
-	mov r8=EINVAL			// default to EINVAL
-
-#ifdef CONFIG_SMP
-	// __ticket_spin_trylock(r31)
-	ld4 r17=[r31]
-	;;
-	mov.m ar.ccv=r17
-	extr.u r9=r17,17,15
-	adds r19=1,r17
-	extr.u r18=r17,0,15
-	;;
-	cmp.eq p6,p7=r9,r18
-	;;
-(p6)	cmpxchg4.acq r9=[r31],r19,ar.ccv
-(p6)	dep.z r20=r19,1,15		// next serving ticket for unlock
-(p7)	br.cond.spnt.many .lock_contention
-	;;
-	cmp4.eq p0,p7=r9,r17
-	adds r31=2,r31
-(p7)	br.cond.spnt.many .lock_contention
-	ld8 r3=[r2]			// re-read current->blocked now that we hold the lock
-	;;
-#else
-	ld8 r3=[r2]			// re-read current->blocked now that we hold the lock
-#endif
-	add r18=IA64_TASK_PENDING_OFFSET+IA64_SIGPENDING_SIGNAL_OFFSET,r16
-	add r19=IA64_TASK_SIGNAL_OFFSET,r16
-	cmp4.eq p6,p0=SIG_BLOCK,r32
-	;;
-	ld8 r19=[r19]			// r19 <- current->signal
-	cmp4.eq p7,p0=SIG_UNBLOCK,r32
-	cmp4.eq p8,p0=SIG_SETMASK,r32
-	;;
-	ld8 r18=[r18]			// r18 <- current->pending.signal
-	.pred.rel.mutex p6,p7,p8
-(p6)	or r14=r3,r14			// SIG_BLOCK
-(p7)	andcm r14=r3,r14		// SIG_UNBLOCK
-
-(p8)	mov r14=r14			// SIG_SETMASK
-(p6)	mov r8=0			// clear error code
-	// recalc_sigpending()
-	add r17=IA64_SIGNAL_GROUP_STOP_COUNT_OFFSET,r19
-
-	add r19=IA64_SIGNAL_SHARED_PENDING_OFFSET+IA64_SIGPENDING_SIGNAL_OFFSET,r19
-	;;
-	ld4 r17=[r17]		// r17 <- current->signal->group_stop_count
-(p7)	mov r8=0		// clear error code
-
-	ld8 r19=[r19]		// r19 <- current->signal->shared_pending
-	;;
-	cmp4.gt p6,p7=r17,r0	// p6/p7 <- (current->signal->group_stop_count > 0)?
-(p8)	mov r8=0		// clear error code
-
-	or r18=r18,r19		// r18 <- current->pending | current->signal->shared_pending
-	;;
-	// r18 <- (current->pending | current->signal->shared_pending) & ~current->blocked:
-	andcm r18=r18,r14
-	add r9=TI_FLAGS+IA64_TASK_SIZE,r16
-	;;
-
-(p7)	cmp.ne.or.andcm p6,p7=r18,r0		// p6/p7 <- signal pending
-	mov r19=0					// i must not leak kernel bits...
-(p6)	br.cond.dpnt.many .sig_pending
-	;;
-
-1:	ld4 r17=[r9]				// r17 <- current->thread_info->flags
-	;;
-	mov ar.ccv=r17
-	and r18=~_TIF_SIGPENDING,r17		// r18 <- r17 & ~(1 << TIF_SIGPENDING)
-	;;
-
-	st8 [r2]=r14				// update current->blocked with new mask
-	cmpxchg4.acq r8=[r9],r18,ar.ccv		// current->thread_info->flags <- r18
-	;;
-	cmp.ne p6,p0=r17,r8			// update failed?
-(p6)	br.cond.spnt.few 1b			// yes -> retry
-
-#ifdef CONFIG_SMP
-	// __ticket_spin_unlock(r31)
-	st2.rel [r31]=r20
-	mov r20=0					// i must not leak kernel bits...
-#endif
-	SSM_PSR_I(p0, p9, r31)
-	;;
-
-	srlz.d					// ensure psr.i is set again
-	mov r18=0					// i must not leak kernel bits...
-
-.store_mask:
-EX(.fail_efault, (p15) probe.w.fault r34, 3)	// verify user has write-access to *oset
-EX(.fail_efault, (p15) st8 [r34]=r3)
-	mov r2=0					// i must not leak kernel bits...
-	mov r3=0					// i must not leak kernel bits...
-	mov r8=0				// return 0
-	mov r9=0					// i must not leak kernel bits...
-	mov r14=0					// i must not leak kernel bits...
-	mov r17=0					// i must not leak kernel bits...
-	mov r31=0					// i must not leak kernel bits...
-	FSYS_RETURN
-
-.sig_pending:
-#ifdef CONFIG_SMP
-	// __ticket_spin_unlock(r31)
-	st2.rel [r31]=r20			// release the lock
-#endif
-	SSM_PSR_I(p0, p9, r17)
-	;;
-	srlz.d
-	br.sptk.many fsys_fallback_syscall	// with signal pending, do the heavy-weight syscall
-
-#ifdef CONFIG_SMP
-.lock_contention:
-	/* Rather than spinning here, fall back on doing a heavy-weight syscall.  */
-	SSM_PSR_I(p0, p9, r17)
-	;;
-	srlz.d
-	br.sptk.many fsys_fallback_syscall
-#endif
-END(fsys_rt_sigprocmask)
-
-/*
  * fsys_getcpu doesn't use the third parameter in this implementation. It reads
  * current_thread_info()->cpu and corresponding node in cpu_to_node_map.
  */
@@ -559,11 +390,15 @@
 	;;
 	tnat.nz p7,p0 = r33			// I guard against NaT argument
 (p7)    br.cond.spnt.few .fail_einval		// B
+	;;
+	cmp.ne p6,p0=r32,r0
+	cmp.ne p7,p0=r33,r0
+	;;
 #ifdef CONFIG_NUMA
 	movl r17=cpu_to_node_map
 	;;
-EX(.fail_efault, probe.w.fault r32, 3)		// M This takes 5 cycles
-EX(.fail_efault, probe.w.fault r33, 3)		// M This takes 5 cycles
+EX(.fail_efault, (p6) probe.w.fault r32, 3)		// M This takes 5 cycles
+EX(.fail_efault, (p7) probe.w.fault r33, 3)		// M This takes 5 cycles
 	shladd r18=r3,1,r17
 	;;
 	ld2 r20=[r18]				// r20 = cpu_to_node_map[cpu]
@@ -573,20 +408,20 @@
 (p8)	br.spnt.many fsys_fallback_syscall
 	;;
 	;;
-EX(.fail_efault, st4 [r32] = r3)
-EX(.fail_efault, st2 [r33] = r20)
+EX(.fail_efault, (p6) st4 [r32] = r3)
+EX(.fail_efault, (p7) st2 [r33] = r20)
 	mov r8=0
 	;;
 #else
-EX(.fail_efault, probe.w.fault r32, 3)		// M This takes 5 cycles
-EX(.fail_efault, probe.w.fault r33, 3)		// M This takes 5 cycles
+EX(.fail_efault, (p6) probe.w.fault r32, 3)		// M This takes 5 cycles
+EX(.fail_efault, (p7) probe.w.fault r33, 3)		// M This takes 5 cycles
 	and r2 = TIF_ALLWORK_MASK,r2
 	;;
 	cmp.ne p8,p0=0,r2
 (p8)	br.spnt.many fsys_fallback_syscall
 	;;
-EX(.fail_efault, st4 [r32] = r3)
-EX(.fail_efault, st2 [r33] = r0)
+EX(.fail_efault, (p6) st4 [r32] = r3)
+EX(.fail_efault, (p7) st2 [r33] = r0)
 	mov r8=0
 	;;
 #endif
@@ -916,7 +751,7 @@
 	data8 0				// sigaltstack
 	data8 0				// rt_sigaction
 	data8 0				// rt_sigpending
-	data8 fsys_rt_sigprocmask	// rt_sigprocmask
+	data8 0				// rt_sigprocmask
 	data8 0				// rt_sigqueueinfo	// 1180
 	data8 0				// rt_sigreturn
 	data8 0				// rt_sigsuspend
diff --git a/arch/m32r/include/asm/processor.h b/arch/m32r/include/asm/processor.h
index e1f46d7..da17253 100644
--- a/arch/m32r/include/asm/processor.h
+++ b/arch/m32r/include/asm/processor.h
@@ -118,8 +118,6 @@
 /* Free all resources held by a thread. */
 extern void release_thread(struct task_struct *);
 
-#define prepare_to_copy(tsk)	do { } while (0)
-
 /*
  * create a kernel thread without removing it from tasklists
  */
diff --git a/arch/m68k/include/asm/processor.h b/arch/m68k/include/asm/processor.h
index 46460fa..f17c42a 100644
--- a/arch/m68k/include/asm/processor.h
+++ b/arch/m68k/include/asm/processor.h
@@ -153,9 +153,6 @@
 {
 }
 
-/* Prepare to copy thread state - unlazy all lazy status */
-#define prepare_to_copy(tsk)	do { } while (0)
-
 extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
 
 /*
diff --git a/arch/microblaze/include/asm/processor.h b/arch/microblaze/include/asm/processor.h
index bffb545..af2bb96 100644
--- a/arch/microblaze/include/asm/processor.h
+++ b/arch/microblaze/include/asm/processor.h
@@ -23,7 +23,6 @@
 
 # define cpu_relax()		barrier()
 # define cpu_sleep()		do {} while (0)
-# define prepare_to_copy(tsk)	do {} while (0)
 
 #define task_pt_regs(tsk) \
 		(((struct pt_regs *)(THREAD_SIZE + task_stack_page(tsk))) - 1)
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index f5e1212..85aad03 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -30,6 +30,7 @@
 	select HAVE_MEMBLOCK_NODE_MAP
 	select ARCH_DISCARD_MEMBLOCK
 	select GENERIC_SMP_IDLE_THREAD
+	select BUILDTIME_EXTABLE_SORT
 
 menu "Machine selection"
 
diff --git a/arch/mips/include/asm/processor.h b/arch/mips/include/asm/processor.h
index 20e9dcf..5e33fab 100644
--- a/arch/mips/include/asm/processor.h
+++ b/arch/mips/include/asm/processor.h
@@ -310,9 +310,6 @@
 /* Free all resources held by a thread. */
 #define release_thread(thread) do { } while(0)
 
-/* Prepare to copy thread state - unlazy all lazy status */
-#define prepare_to_copy(tsk)	do { } while (0)
-
 extern long kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
 
 extern unsigned long thread_saved_pc(struct task_struct *tsk);
diff --git a/arch/mn10300/include/asm/processor.h b/arch/mn10300/include/asm/processor.h
index f7b3c9a..247928c 100644
--- a/arch/mn10300/include/asm/processor.h
+++ b/arch/mn10300/include/asm/processor.h
@@ -139,9 +139,6 @@
 /* Free all resources held by a thread. */
 extern void release_thread(struct task_struct *);
 
-/* Prepare to copy thread state - unlazy all lazy status */
-extern void prepare_to_copy(struct task_struct *tsk);
-
 /*
  * create a kernel thread without removing it from tasklists
  */
diff --git a/arch/mn10300/kernel/process.c b/arch/mn10300/kernel/process.c
index 14707f2..7dab0cd 100644
--- a/arch/mn10300/kernel/process.c
+++ b/arch/mn10300/kernel/process.c
@@ -208,12 +208,14 @@
 }
 
 /*
- * this gets called before we allocate a new thread and copy the current task
- * into it so that we can store lazy state into memory
+ * this gets called so that we can store lazy state into memory and copy the
+ * current task into the new thread.
  */
-void prepare_to_copy(struct task_struct *tsk)
+int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)
 {
-	unlazy_fpu(tsk);
+	unlazy_fpu(src);
+	*dst = *src;
+	return 0;
 }
 
 /*
diff --git a/arch/openrisc/include/asm/processor.h b/arch/openrisc/include/asm/processor.h
index f7516fa..30462f1 100644
--- a/arch/openrisc/include/asm/processor.h
+++ b/arch/openrisc/include/asm/processor.h
@@ -72,10 +72,6 @@
 #define task_pt_regs(task) user_regs(task_thread_info(task))
 #define current_regs() user_regs(current_thread_info())
 
-extern inline void prepare_to_copy(struct task_struct *tsk)
-{
-}
-
 #define INIT_SP         (sizeof(init_stack) + (unsigned long) &init_stack)
 
 #define INIT_THREAD  { }
diff --git a/arch/parisc/include/asm/kbdleds.h b/arch/parisc/include/asm/kbdleds.h
new file mode 100644
index 0000000..2e2e75a
--- /dev/null
+++ b/arch/parisc/include/asm/kbdleds.h
@@ -0,0 +1,19 @@
+#ifndef _ASM_PARISC_KBDLEDS_H
+#define _ASM_PARISC_KBDLEDS_H
+
+/*
+ * On HIL keyboards of PARISC machines there is no NumLock key and
+ * everyone expects the keypad to be used for numbers. That's why
+ * we can safely turn on the NUMLOCK bit.
+ */
+
+static inline int kbd_defleds(void)
+{
+#if defined(CONFIG_KEYBOARD_HIL) || defined(CONFIG_KEYBOARD_HIL_OLD)
+	return 1 << VC_NUMLOCK;
+#else
+	return 0;
+#endif
+}
+
+#endif /* _ASM_PARISC_KBDLEDS_H */
diff --git a/arch/parisc/include/asm/processor.h b/arch/parisc/include/asm/processor.h
index acdf4ca..0e8b7b8 100644
--- a/arch/parisc/include/asm/processor.h
+++ b/arch/parisc/include/asm/processor.h
@@ -328,9 +328,6 @@
 extern void release_thread(struct task_struct *);
 extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
 
-/* Prepare to copy thread state - unlazy all lazy status */
-#define prepare_to_copy(tsk)	do { } while (0)
-
 extern void map_hpux_gateway_page(struct task_struct *tsk, struct mm_struct *mm);
 
 extern unsigned long get_wchan(struct task_struct *p);
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index 8a01098..0a947bd 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -350,7 +350,7 @@
 
 config KEXEC
 	bool "kexec system call (EXPERIMENTAL)"
-	depends on (PPC_BOOK3S || FSL_BOOKE || (44x && !SMP && !PPC_47x)) && EXPERIMENTAL
+	depends on (PPC_BOOK3S || FSL_BOOKE || (44x && !SMP)) && EXPERIMENTAL
 	help
 	  kexec is a system call that implements the ability to shutdown your
 	  current kernel, and to start another kernel.  It is like a reboot
@@ -367,7 +367,7 @@
 
 config CRASH_DUMP
 	bool "Build a kdump crash kernel"
-	depends on PPC64 || 6xx || FSL_BOOKE || (44x && !SMP && !PPC_47x)
+	depends on PPC64 || 6xx || FSL_BOOKE || (44x && !SMP)
 	select RELOCATABLE if PPC64 || 44x
 	select DYNAMIC_MEMSTART if FSL_BOOKE
 	help
diff --git a/arch/powerpc/Makefile b/arch/powerpc/Makefile
index 6524c6e..950d1f7 100644
--- a/arch/powerpc/Makefile
+++ b/arch/powerpc/Makefile
@@ -69,6 +69,16 @@
 
 CFLAGS-$(CONFIG_PPC64)	:= -mminimal-toc -mtraceback=no -mcall-aixdesc
 CFLAGS-$(CONFIG_PPC32)	:= -ffixed-r2 -mmultiple
+
+CFLAGS-$(CONFIG_GENERIC_CPU) += $(call cc-option,-mtune=power7,-mtune=power4)
+CFLAGS-$(CONFIG_CELL_CPU) += $(call cc-option,-mcpu=cell)
+CFLAGS-$(CONFIG_POWER4_CPU) += $(call cc-option,-mcpu=power4)
+CFLAGS-$(CONFIG_POWER5_CPU) += $(call cc-option,-mcpu=power5)
+CFLAGS-$(CONFIG_POWER6_CPU) += $(call cc-option,-mcpu=power6)
+CFLAGS-$(CONFIG_POWER7_CPU) += $(call cc-option,-mcpu=power7)
+
+CFLAGS-$(CONFIG_TUNE_CELL) += $(call cc-option,-mtune=cell)
+
 KBUILD_CPPFLAGS	+= -Iarch/$(ARCH)
 KBUILD_AFLAGS	+= -Iarch/$(ARCH)
 KBUILD_CFLAGS	+= -msoft-float -pipe -Iarch/$(ARCH) $(CFLAGS-y)
@@ -76,32 +86,11 @@
 
 CHECKFLAGS	+= -m$(CONFIG_WORD_SIZE) -D__powerpc__ -D__powerpc$(CONFIG_WORD_SIZE)__
 
-ifeq ($(CONFIG_PPC64),y)
-GCC_BROKEN_VEC	:= $(call cc-ifversion, -lt, 0400, y)
-
-ifeq ($(CONFIG_POWER4_ONLY),y)
-ifeq ($(CONFIG_ALTIVEC),y)
-ifeq ($(GCC_BROKEN_VEC),y)
-	KBUILD_CFLAGS += $(call cc-option,-mcpu=970)
-else
-	KBUILD_CFLAGS += $(call cc-option,-mcpu=power4)
-endif
-else
-	KBUILD_CFLAGS += $(call cc-option,-mcpu=power4)
-endif
-else
-	KBUILD_CFLAGS += $(call cc-option,-mtune=power4)
-endif
-endif
-
 KBUILD_LDFLAGS_MODULE += arch/powerpc/lib/crtsavres.o
 
-ifeq ($(CONFIG_TUNE_CELL),y)
-	KBUILD_CFLAGS += $(call cc-option,-mtune=cell)
-endif
-
-# No AltiVec instruction when building kernel
+# No AltiVec or VSX instructions when building kernel
 KBUILD_CFLAGS += $(call cc-option,-mno-altivec)
+KBUILD_CFLAGS += $(call cc-option,-mno-vsx)
 
 # No SPE instruction when building kernel
 # (We use all available options to help semi-broken compilers)
@@ -160,6 +149,7 @@
 core-$(CONFIG_PERF_EVENTS)	+= arch/powerpc/perf/
 
 drivers-$(CONFIG_OPROFILE)	+= arch/powerpc/oprofile/
+drivers-$(CONFIG_CRYPTO_DEV_NX) += drivers/crypto/nx/
 
 # Default to zImage, override when needed
 all: zImage
@@ -234,10 +224,11 @@
 # Use the file '.tmp_gas_check' for binutils tests, as gas won't output
 # to stdout and these checks are run even on install targets.
 TOUT	:= .tmp_gas_check
-# Ensure this is binutils 2.12.1 (or 2.12.90.0.7) or later for altivec
-# instructions.
-# gcc-3.4 and binutils-2.14 are a fatal combination.
 
+# Check gcc and binutils versions:
+# - gcc-3.4 and binutils-2.14 are a fatal combination
+# - Require gcc 4.0 or above on 64-bit
+# - gcc-4.2.0 has issues compiling modules on 64-bit
 checkbin:
 	@if test "$(call cc-version)" = "0304" ; then \
 		if ! /bin/echo mftb 5 | $(AS) -v -mppc -many -o $(TOUT) >/dev/null 2>&1 ; then \
@@ -247,6 +238,12 @@
 			false; \
 		fi ; \
 	fi
+	@if test "$(call cc-version)" -lt "0400" \
+	    && test "x${CONFIG_PPC64}" = "xy" ; then \
+                echo -n "Sorry, GCC v4.0 or above is required to build " ; \
+                echo "the 64-bit powerpc kernel." ; \
+                false ; \
+        fi
 	@if test "$(call cc-fullversion)" = "040200" \
 	    && test "x${CONFIG_MODULES}${CONFIG_PPC64}" = "xyy" ; then \
 		echo -n '*** GCC-4.2.0 cannot compile the 64-bit powerpc ' ; \
diff --git a/arch/powerpc/boot/dts/bluestone.dts b/arch/powerpc/boot/dts/bluestone.dts
index 7bda373..9d4917a 100644
--- a/arch/powerpc/boot/dts/bluestone.dts
+++ b/arch/powerpc/boot/dts/bluestone.dts
@@ -373,5 +373,30 @@
 				0x0 0x0 0x0 0x3 &UIC3 0xe 0x4 /* swizzled int C */
 				0x0 0x0 0x0 0x4 &UIC3 0xf 0x4 /* swizzled int D */>;
 		};
+
+		MSI: ppc4xx-msi@C10000000 {
+			compatible = "amcc,ppc4xx-msi", "ppc4xx-msi";
+			reg = < 0xC 0x10000000 0x100
+				0xC 0x10000000 0x100>;
+			sdr-base = <0x36C>;
+			msi-data = <0x00004440>;
+			msi-mask = <0x0000ffe0>;
+			interrupts =<0 1 2 3 4 5 6 7>;
+			interrupt-parent = <&MSI>;
+			#interrupt-cells = <1>;
+			#address-cells = <0>;
+			#size-cells = <0>;
+			msi-available-ranges = <0x0 0x100>;
+			interrupt-map = <
+				0 &UIC3 0x18 1
+				1 &UIC3 0x19 1
+				2 &UIC3 0x1A 1
+				3 &UIC3 0x1B 1
+				4 &UIC3 0x1C 1
+				5 &UIC3 0x1D 1
+				6 &UIC3 0x1E 1
+				7 &UIC3 0x1F 1
+			>;
+		};
 	};
 };
diff --git a/arch/powerpc/configs/86xx/mpc8610_hpcd_defconfig b/arch/powerpc/configs/86xx/mpc8610_hpcd_defconfig
index 0db9ba0..c09598b 100644
--- a/arch/powerpc/configs/86xx/mpc8610_hpcd_defconfig
+++ b/arch/powerpc/configs/86xx/mpc8610_hpcd_defconfig
@@ -100,6 +100,7 @@
 CONFIG_SND_PCM_OSS=y
 # CONFIG_SND_SUPPORT_OLD_API is not set
 CONFIG_SND_SOC=y
+CONFIG_SND_POWERPC_SOC=y
 CONFIG_RTC_CLASS=y
 CONFIG_RTC_DRV_CMOS=y
 CONFIG_EXT2_FS=y
diff --git a/arch/powerpc/configs/g5_defconfig b/arch/powerpc/configs/g5_defconfig
index 1196c34..07b7f2a 100644
--- a/arch/powerpc/configs/g5_defconfig
+++ b/arch/powerpc/configs/g5_defconfig
@@ -1,5 +1,4 @@
 CONFIG_PPC64=y
-CONFIG_POWER4_ONLY=y
 CONFIG_ALTIVEC=y
 CONFIG_SMP=y
 CONFIG_NR_CPUS=4
diff --git a/arch/powerpc/configs/maple_defconfig b/arch/powerpc/configs/maple_defconfig
index 2244d37..02ac96b 100644
--- a/arch/powerpc/configs/maple_defconfig
+++ b/arch/powerpc/configs/maple_defconfig
@@ -1,5 +1,4 @@
 CONFIG_PPC64=y
-CONFIG_POWER4_ONLY=y
 CONFIG_SMP=y
 CONFIG_NR_CPUS=4
 CONFIG_EXPERIMENTAL=y
diff --git a/arch/powerpc/configs/mpc85xx_defconfig b/arch/powerpc/configs/mpc85xx_defconfig
index d6b6df5..62bb723 100644
--- a/arch/powerpc/configs/mpc85xx_defconfig
+++ b/arch/powerpc/configs/mpc85xx_defconfig
@@ -141,6 +141,7 @@
 # CONFIG_SND_PPC is not set
 # CONFIG_SND_USB is not set
 CONFIG_SND_SOC=y
+CONFIG_SND_POWERPC_SOC=y
 CONFIG_HID_A4TECH=y
 CONFIG_HID_APPLE=y
 CONFIG_HID_BELKIN=y
diff --git a/arch/powerpc/configs/mpc85xx_smp_defconfig b/arch/powerpc/configs/mpc85xx_smp_defconfig
index 5b0e292..d182842 100644
--- a/arch/powerpc/configs/mpc85xx_smp_defconfig
+++ b/arch/powerpc/configs/mpc85xx_smp_defconfig
@@ -143,6 +143,7 @@
 # CONFIG_SND_PPC is not set
 # CONFIG_SND_USB is not set
 CONFIG_SND_SOC=y
+CONFIG_SND_POWERPC_SOC=y
 CONFIG_HID_A4TECH=y
 CONFIG_HID_APPLE=y
 CONFIG_HID_BELKIN=y
diff --git a/arch/powerpc/configs/pasemi_defconfig b/arch/powerpc/configs/pasemi_defconfig
index f4deb0b..840a2c2 100644
--- a/arch/powerpc/configs/pasemi_defconfig
+++ b/arch/powerpc/configs/pasemi_defconfig
@@ -1,5 +1,4 @@
 CONFIG_PPC64=y
-CONFIG_POWER4_ONLY=y
 CONFIG_ALTIVEC=y
 # CONFIG_VIRT_CPU_ACCOUNTING is not set
 CONFIG_SMP=y
diff --git a/arch/powerpc/configs/ps3_defconfig b/arch/powerpc/configs/ps3_defconfig
index ded8678..c2f4b4a 100644
--- a/arch/powerpc/configs/ps3_defconfig
+++ b/arch/powerpc/configs/ps3_defconfig
@@ -6,7 +6,6 @@
 CONFIG_EXPERIMENTAL=y
 CONFIG_SYSVIPC=y
 CONFIG_POSIX_MQUEUE=y
-CONFIG_SPARSE_IRQ=y
 CONFIG_BLK_DEV_INITRD=y
 CONFIG_CC_OPTIMIZE_FOR_SIZE=y
 CONFIG_EMBEDDED=y
@@ -25,7 +24,6 @@
 CONFIG_PS3_ROM=y
 CONFIG_PS3_FLASH=y
 CONFIG_PS3_VRAM=m
-CONFIG_PS3_LPM=m
 # CONFIG_PPC_OF_BOOT_TRAMPOLINE is not set
 CONFIG_HIGH_RES_TIMERS=y
 # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
@@ -53,8 +51,6 @@
 # CONFIG_INET_DIAG is not set
 CONFIG_IPV6=y
 CONFIG_BT=m
-CONFIG_BT_L2CAP=y
-CONFIG_BT_SCO=y
 CONFIG_BT_RFCOMM=m
 CONFIG_BT_RFCOMM_TTY=y
 CONFIG_BT_BNEP=m
@@ -63,7 +59,6 @@
 CONFIG_BT_HIDP=m
 CONFIG_BT_HCIBTUSB=m
 CONFIG_CFG80211=m
-# CONFIG_WIRELESS_EXT_SYSFS is not set
 CONFIG_MAC80211=m
 CONFIG_MAC80211_RC_PID=y
 # CONFIG_MAC80211_RC_MINSTREL is not set
@@ -181,7 +176,6 @@
 CONFIG_DEBUG_WRITECOUNT=y
 CONFIG_DEBUG_MEMORY_INIT=y
 CONFIG_DEBUG_LIST=y
-CONFIG_SYSCTL_SYSCALL_CHECK=y
 # CONFIG_FTRACE is not set
 CONFIG_DEBUG_STACKOVERFLOW=y
 CONFIG_CRYPTO_CCM=m
diff --git a/arch/powerpc/include/asm/asm-compat.h b/arch/powerpc/include/asm/asm-compat.h
index decad95..5d7fbe1 100644
--- a/arch/powerpc/include/asm/asm-compat.h
+++ b/arch/powerpc/include/asm/asm-compat.h
@@ -29,18 +29,9 @@
 #define PPC_LLARX(t, a, b, eh)	PPC_LDARX(t, a, b, eh)
 #define PPC_STLCX	stringify_in_c(stdcx.)
 #define PPC_CNTLZL	stringify_in_c(cntlzd)
+#define PPC_MTOCRF(FXM, RS) MTOCRF((FXM), (RS))
 #define PPC_LR_STKOFF	16
 #define PPC_MIN_STKFRM	112
-
-/* Move to CR, single-entry optimized version. Only available
- * on POWER4 and later.
- */
-#ifdef CONFIG_POWER4_ONLY
-#define PPC_MTOCRF	stringify_in_c(mtocrf)
-#else
-#define PPC_MTOCRF	stringify_in_c(mtcrf)
-#endif
-
 #else /* 32-bit */
 
 /* operations for longs and pointers */
diff --git a/arch/powerpc/include/asm/cputhreads.h b/arch/powerpc/include/asm/cputhreads.h
index ce516e5..ac3eedb 100644
--- a/arch/powerpc/include/asm/cputhreads.h
+++ b/arch/powerpc/include/asm/cputhreads.h
@@ -9,7 +9,7 @@
  * Note: This implementation is limited to a power of 2 number of
  * threads per core and the same number for each core in the system
  * (though it would work if some processors had less threads as long
- * as the CPU numbers are still allocated, just not brought offline).
+ * as the CPU numbers are still allocated, just not brought online).
  *
  * However, the API allows for a different implementation in the future
  * if needed, as long as you only use the functions and not the variables
diff --git a/arch/powerpc/include/asm/hvcall.h b/arch/powerpc/include/asm/hvcall.h
index 1c324ff..6122523 100644
--- a/arch/powerpc/include/asm/hvcall.h
+++ b/arch/powerpc/include/asm/hvcall.h
@@ -77,8 +77,27 @@
 #define H_MR_CONDITION  -43
 #define H_NOT_ENOUGH_RESOURCES -44
 #define H_R_STATE       -45
-#define H_RESCINDEND    -46
-#define H_MULTI_THREADS_ACTIVE -9005
+#define H_RESCINDED     -46
+#define H_P2		-55
+#define H_P3		-56
+#define H_P4		-57
+#define H_P5		-58
+#define H_P6		-59
+#define H_P7		-60
+#define H_P8		-61
+#define H_P9		-62
+#define H_TOO_BIG	-64
+#define H_OVERLAP	-68
+#define H_INTERRUPT	-69
+#define H_BAD_DATA	-70
+#define H_NOT_ACTIVE	-71
+#define H_SG_LIST	-72
+#define H_OP_MODE	-73
+#define H_COP_HW	-74
+#define H_UNSUPPORTED_FLAG_START	-256
+#define H_UNSUPPORTED_FLAG_END		-511
+#define H_MULTI_THREADS_ACTIVE	-9005
+#define H_OUTSTANDING_COP_OPS	-9006
 
 
 /* Long Busy is a condition that can be returned by the firmware
@@ -240,6 +259,8 @@
 #define H_GET_MPP		0x2D4
 #define H_HOME_NODE_ASSOCIATIVITY 0x2EC
 #define H_BEST_ENERGY		0x2F4
+#define H_RANDOM		0x300
+#define H_COP			0x304
 #define H_GET_MPP_X		0x314
 #define MAX_HCALL_OPCODE	H_GET_MPP_X
 
diff --git a/arch/powerpc/include/asm/lppaca.h b/arch/powerpc/include/asm/lppaca.h
index a76254a..531fe0c 100644
--- a/arch/powerpc/include/asm/lppaca.h
+++ b/arch/powerpc/include/asm/lppaca.h
@@ -20,18 +20,16 @@
 #define _ASM_POWERPC_LPPACA_H
 #ifdef __KERNEL__
 
-/* These definitions relate to hypervisors that only exist when using
+/*
+ * These definitions relate to hypervisors that only exist when using
  * a server type processor
  */
 #ifdef CONFIG_PPC_BOOK3S
 
-//=============================================================================
-//
-//	This control block contains the data that is shared between the
-//	hypervisor (PLIC) and the OS.
-//
-//
-//----------------------------------------------------------------------------
+/*
+ * This control block contains the data that is shared between the
+ * hypervisor and the OS.
+ */
 #include <linux/cache.h>
 #include <linux/threads.h>
 #include <asm/types.h>
@@ -43,123 +41,65 @@
  */
 #define NR_LPPACAS	1
 
-
-/* The Hypervisor barfs if the lppaca crosses a page boundary.  A 1k
- * alignment is sufficient to prevent this */
+/*
+ * The Hypervisor barfs if the lppaca crosses a page boundary.  A 1k
+ * alignment is sufficient to prevent this
+ */
 struct lppaca {
-//=============================================================================
-// CACHE_LINE_1 0x0000 - 0x007F Contains read-only data
-// NOTE: The xDynXyz fields are fields that will be dynamically changed by
-// PLIC when preparing to bring a processor online or when dispatching a
-// virtual processor!
-//=============================================================================
-	u32	desc;			// Eye catcher 0xD397D781	x00-x03
-	u16	size;			// Size of this struct		x04-x05
-	u16	reserved1;		// Reserved			x06-x07
-	u16	reserved2:14;		// Reserved			x08-x09
-	u8	shared_proc:1;		// Shared processor indicator	...
-	u8	secondary_thread:1;	// Secondary thread indicator	...
-	volatile u8 dyn_proc_status:8;	// Dynamic Status of this proc	x0A-x0A
-	u8	secondary_thread_count;	// Secondary thread count	x0B-x0B
-	volatile u16 dyn_hv_phys_proc_index;// Dynamic HV Physical Proc Index0C-x0D
-	volatile u16 dyn_hv_log_proc_index;// Dynamic HV Logical Proc Indexx0E-x0F
-	u32	decr_val;   		// Value for Decr programming 	x10-x13
-	u32	pmc_val;       		// Value for PMC regs         	x14-x17
-	volatile u32 dyn_hw_node_id;	// Dynamic Hardware Node id	x18-x1B
-	volatile u32 dyn_hw_proc_id;	// Dynamic Hardware Proc Id	x1C-x1F
-	volatile u32 dyn_pir;		// Dynamic ProcIdReg value	x20-x23
-	u32	dsei_data;           	// DSEI data                  	x24-x27
-	u64	sprg3;               	// SPRG3 value                	x28-x2F
-	u8	reserved3[40];		// Reserved			x30-x57
-	volatile u8 vphn_assoc_counts[8]; // Virtual processor home node
-					// associativity change counters x58-x5F
-	u8	reserved4[32];		// Reserved			x60-x7F
+	/* cacheline 1 contains read-only data */
 
-//=============================================================================
-// CACHE_LINE_2 0x0080 - 0x00FF Contains local read-write data
-//=============================================================================
-	// This Dword contains a byte for each type of interrupt that can occur.
-	// The IPI is a count while the others are just a binary 1 or 0.
-	union {
-		u64	any_int;
-		struct {
-			u16	reserved;	// Reserved - cleared by #mpasmbl
-			u8	xirr_int;	// Indicates xXirrValue is valid or Immed IO
-			u8	ipi_cnt;	// IPI Count
-			u8	decr_int;	// DECR interrupt occurred
-			u8	pdc_int;	// PDC interrupt occurred
-			u8	quantum_int;	// Interrupt quantum reached
-			u8	old_plic_deferred_ext_int;	// Old PLIC has a deferred XIRR pending
-		} fields;
-	} int_dword;
+	u32	desc;			/* Eye catcher 0xD397D781 */
+	u16	size;			/* Size of this struct */
+	u16	reserved1;
+	u16	reserved2:14;
+	u8	shared_proc:1;		/* Shared processor indicator */
+	u8	secondary_thread:1;	/* Secondary thread indicator */
+	u8	reserved3[14];
+	volatile u32 dyn_hw_node_id;	/* Dynamic hardware node id */
+	volatile u32 dyn_hw_proc_id;	/* Dynamic hardware proc id */
+	u8	reserved4[56];
+	volatile u8 vphn_assoc_counts[8]; /* Virtual processor home node */
+					  /* associativity change counters */
+	u8	reserved5[32];
 
-	// Whenever any fields in this Dword are set then PLIC will defer the
-	// processing of external interrupts.  Note that PLIC will store the
-	// XIRR directly into the xXirrValue field so that another XIRR will
-	// not be presented until this one clears.  The layout of the low
-	// 4-bytes of this Dword is up to SLIC - PLIC just checks whether the
-	// entire Dword is zero or not.  A non-zero value in the low order
-	// 2-bytes will result in SLIC being granted the highest thread
-	// priority upon return.  A 0 will return to SLIC as medium priority.
-	u64	plic_defer_ints_area;	// Entire Dword
+	/* cacheline 2 contains local read-write data */
 
-	// Used to pass the real SRR0/1 from PLIC to SLIC as well as to
-	// pass the target SRR0/1 from SLIC to PLIC on a SetAsrAndRfid.
-	u64	saved_srr0;		// Saved SRR0                   x10-x17
-	u64	saved_srr1;		// Saved SRR1                   x18-x1F
+	u8	reserved6[48];
+	u8	cede_latency_hint;
+	u8	reserved7[7];
+	u8	dtl_enable_mask;	/* Dispatch Trace Log mask */
+	u8	donate_dedicated_cpu;	/* Donate dedicated CPU cycles */
+	u8	fpregs_in_use;
+	u8	pmcregs_in_use;
+	u8	reserved8[28];
+	u64	wait_state_cycles;	/* Wait cycles for this proc */
+	u8	reserved9[28];
+	u16	slb_count;		/* # of SLBs to maintain */
+	u8	idle;			/* Indicate OS is idle */
+	u8	vmxregs_in_use;
 
-	// Used to pass parms from the OS to PLIC for SetAsrAndRfid
-	u64	saved_gpr3;		// Saved GPR3                   x20-x27
-	u64	saved_gpr4;		// Saved GPR4                   x28-x2F
-	union {
-		u64	saved_gpr5;	/* Saved GPR5               x30-x37 */
-		struct {
-			u8	cede_latency_hint;  /*			x30 */
-			u8	reserved[7];        /*		    x31-x36 */
-		} fields;
-	} gpr5_dword;
+	/* cacheline 3 is shared with other processors */
 
+	/*
+	 * This is the yield_count.  An "odd" value (low bit on) means that
+	 * the processor is yielded (either because of an OS yield or a
+	 * hypervisor preempt).  An even value implies that the processor is
+	 * currently executing.
+	 * NOTE: This value will ALWAYS be zero for dedicated processors and
+	 * will NEVER be zero for shared processors (ie, initialized to a 1).
+	 */
+	volatile u32 yield_count;
+	volatile u32 dispersion_count;	/* dispatch changed physical cpu */
+	volatile u64 cmo_faults;	/* CMO page fault count */
+	volatile u64 cmo_fault_time;	/* CMO page fault time */
+	u8	reserved10[104];
 
-	u8	dtl_enable_mask;	// Dispatch Trace Log mask	x38-x38
-	u8	donate_dedicated_cpu;	// Donate dedicated CPU cycles  x39-x39
-	u8	fpregs_in_use;		// FP regs in use               x3A-x3A
-	u8	pmcregs_in_use;		// PMC regs in use              x3B-x3B
-	volatile u32 saved_decr;	// Saved Decr Value             x3C-x3F
-	volatile u64 emulated_time_base;// Emulated TB for this thread  x40-x47
-	volatile u64 cur_plic_latency;	// Unaccounted PLIC latency     x48-x4F
-	u64	tot_plic_latency;	// Accumulated PLIC latency     x50-x57
-	u64	wait_state_cycles;	// Wait cycles for this proc    x58-x5F
-	u64	end_of_quantum;		// TB at end of quantum         x60-x67
-	u64	pdc_saved_sprg1;	// Saved SPRG1 for PMC int      x68-x6F
-	u64	pdc_saved_srr0;		// Saved SRR0 for PMC int       x70-x77
-	volatile u32 virtual_decr;	// Virtual DECR for shared procsx78-x7B
-	u16	slb_count;		// # of SLBs to maintain        x7C-x7D
-	u8	idle;			// Indicate OS is idle          x7E
-	u8	vmxregs_in_use;		// VMX registers in use         x7F
+	/* cacheline 4-5 */
 
-
-//=============================================================================
-// CACHE_LINE_3 0x0100 - 0x017F: This line is shared with other processors
-//=============================================================================
-	// This is the yield_count.  An "odd" value (low bit on) means that
-	// the processor is yielded (either because of an OS yield or a PLIC
-	// preempt).  An even value implies that the processor is currently
-	// executing.
-	// NOTE: This value will ALWAYS be zero for dedicated processors and
-	// will NEVER be zero for shared processors (ie, initialized to a 1).
-	volatile u32 yield_count;	// PLIC increments each dispatchx00-x03
-	volatile u32 dispersion_count;	// dispatch changed phys cpu    x04-x07
-	volatile u64 cmo_faults;	// CMO page fault count         x08-x0F
-	volatile u64 cmo_fault_time;	// CMO page fault time          x10-x17
-	u8	reserved7[104];		// Reserved                     x18-x7F
-
-//=============================================================================
-// CACHE_LINE_4-5 0x0180 - 0x027F Contains PMC interrupt data
-//=============================================================================
-	u32	page_ins;		// CMO Hint - # page ins by OS  x00-x03
-	u8	reserved8[148];		// Reserved                     x04-x97
-	volatile u64 dtl_idx;		// Dispatch Trace Log head idx	x98-x9F
-	u8	reserved9[96];		// Reserved                     xA0-xFF
+	u32	page_ins;		/* CMO Hint - # page ins by OS */
+	u8	reserved11[148];
+	volatile u64 dtl_idx;		/* Dispatch Trace Log head index */
+	u8	reserved12[96];
 } __attribute__((__aligned__(0x400)));
 
 extern struct lppaca lppaca[];
@@ -172,13 +112,13 @@
  * ESID is stored in the lower 64bits, then the VSID.
  */
 struct slb_shadow {
-	u32	persistent;		// Number of persistent SLBs	x00-x03
-	u32	buffer_length;		// Total shadow buffer length	x04-x07
-	u64	reserved;		// Alignment			x08-x0f
+	u32	persistent;		/* Number of persistent SLBs */
+	u32	buffer_length;		/* Total shadow buffer length */
+	u64	reserved;
 	struct	{
 		u64     esid;
 		u64	vsid;
-	} save_area[SLB_NUM_BOLTED];	//				x10-x40
+	} save_area[SLB_NUM_BOLTED];
 } ____cacheline_aligned;
 
 extern struct slb_shadow slb_shadow[];
diff --git a/arch/powerpc/include/asm/lv1call.h b/arch/powerpc/include/asm/lv1call.h
index 233f9ec..f511767 100644
--- a/arch/powerpc/include/asm/lv1call.h
+++ b/arch/powerpc/include/asm/lv1call.h
@@ -265,8 +265,8 @@
 LV1_CALL(set_spe_privilege_state_area_1_register,       3, 0,  79 )
 LV1_CALL(create_repository_node,                        6, 0,  90 )
 LV1_CALL(read_repository_node,                          5, 2,  91 )
-LV1_CALL(modify_repository_node_value,                  6, 0,  92 )
-LV1_CALL(remove_repository_node,                        4, 0,  93 )
+LV1_CALL(write_repository_node,                         6, 0,  92 )
+LV1_CALL(delete_repository_node,                        4, 0,  93 )
 LV1_CALL(read_htab_entries,                             2, 5,  95 )
 LV1_CALL(set_dabr,                                      2, 0,  96 )
 LV1_CALL(get_total_execution_time,                      2, 1, 103 )
diff --git a/arch/powerpc/include/asm/pSeries_reconfig.h b/arch/powerpc/include/asm/pSeries_reconfig.h
index 23cd6cc..c07edfe 100644
--- a/arch/powerpc/include/asm/pSeries_reconfig.h
+++ b/arch/powerpc/include/asm/pSeries_reconfig.h
@@ -13,6 +13,18 @@
 #define PSERIES_RECONFIG_REMOVE		0x0002
 #define PSERIES_DRCONF_MEM_ADD		0x0003
 #define PSERIES_DRCONF_MEM_REMOVE	0x0004
+#define PSERIES_UPDATE_PROPERTY		0x0005
+
+/**
+ * pSeries_reconfig_notify - Notifier value structure for OFDT property updates
+ *
+ * @node: Device tree node which owns the property being updated
+ * @property: Updated property
+ */
+struct pSeries_reconfig_prop_update {
+	struct device_node *node;
+	struct property *property;
+};
 
 #ifdef CONFIG_PPC_PSERIES
 extern int pSeries_reconfig_notifier_register(struct notifier_block *);
diff --git a/arch/powerpc/include/asm/ppc_asm.h b/arch/powerpc/include/asm/ppc_asm.h
index 50f73aa..1544420 100644
--- a/arch/powerpc/include/asm/ppc_asm.h
+++ b/arch/powerpc/include/asm/ppc_asm.h
@@ -369,7 +369,15 @@
 END_FTR_SECTION_IFCLR(CPU_FTR_601)
 #endif
 
-	
+#ifdef CONFIG_PPC64
+#define MTOCRF(FXM, RS)			\
+	BEGIN_FTR_SECTION_NESTED(848);	\
+	mtcrf	(FXM), (RS);		\
+	FTR_SECTION_ELSE_NESTED(848);	\
+	mtocrf (FXM), (RS);		\
+	ALT_FTR_SECTION_END_NESTED_IFCLR(CPU_FTR_NOEXECUTE, 848)
+#endif
+
 /*
  * This instruction is not implemented on the PPC 603 or 601; however, on
  * the 403GCX and 405GP tlbia IS defined and tlbie is not.
diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h
index 48a26d379..55e8563 100644
--- a/arch/powerpc/include/asm/processor.h
+++ b/arch/powerpc/include/asm/processor.h
@@ -74,9 +74,6 @@
 void start_thread(struct pt_regs *regs, unsigned long fdptr, unsigned long sp);
 void release_thread(struct task_struct *);
 
-/* Prepare to copy thread state - unlazy all lazy status */
-extern void prepare_to_copy(struct task_struct *tsk);
-
 /* Create a new kernel thread. */
 extern long kernel_thread(int (*fn)(void *), void *arg, unsigned long flags);
 
diff --git a/arch/powerpc/include/asm/ptrace.h b/arch/powerpc/include/asm/ptrace.h
index 84cc784..9c21ed4 100644
--- a/arch/powerpc/include/asm/ptrace.h
+++ b/arch/powerpc/include/asm/ptrace.h
@@ -354,12 +354,6 @@
 #define PTRACE_GETREGS64	  22
 #define PTRACE_SETREGS64	  23
 
-/* (old) PTRACE requests with inverted arguments */
-#define PPC_PTRACE_GETREGS	0x99	/* Get GPRs 0 - 31 */
-#define PPC_PTRACE_SETREGS	0x98	/* Set GPRs 0 - 31 */
-#define PPC_PTRACE_GETFPREGS	0x97	/* Get FPRs 0 - 31 */
-#define PPC_PTRACE_SETFPREGS	0x96	/* Set FPRs 0 - 31 */
-
 /* Calls to trace a 64bit program from a 32bit program */
 #define PPC_PTRACE_PEEKTEXT_3264 0x95
 #define PPC_PTRACE_PEEKDATA_3264 0x94
diff --git a/arch/powerpc/include/asm/switch_to.h b/arch/powerpc/include/asm/switch_to.h
index caf82d0..1a63202 100644
--- a/arch/powerpc/include/asm/switch_to.h
+++ b/arch/powerpc/include/asm/switch_to.h
@@ -21,7 +21,6 @@
 extern void enable_kernel_fp(void);
 extern void flush_fp_to_thread(struct task_struct *);
 extern void enable_kernel_altivec(void);
-extern void giveup_altivec(struct task_struct *);
 extern void load_up_altivec(struct task_struct *);
 extern int emulate_altivec(struct pt_regs *);
 extern void __giveup_vsx(struct task_struct *);
@@ -40,10 +39,15 @@
 
 #ifdef CONFIG_ALTIVEC
 extern void flush_altivec_to_thread(struct task_struct *);
+extern void giveup_altivec(struct task_struct *);
+extern void giveup_altivec_notask(void);
 #else
 static inline void flush_altivec_to_thread(struct task_struct *t)
 {
 }
+static inline void giveup_altivec(struct task_struct *t)
+{
+}
 #endif
 
 #ifdef CONFIG_VSX
diff --git a/arch/powerpc/include/asm/thread_info.h b/arch/powerpc/include/asm/thread_info.h
index 1a1bb00..a556ccc 100644
--- a/arch/powerpc/include/asm/thread_info.h
+++ b/arch/powerpc/include/asm/thread_info.h
@@ -113,7 +113,6 @@
 #define _TIF_NOERROR		(1<<TIF_NOERROR)
 #define _TIF_NOTIFY_RESUME	(1<<TIF_NOTIFY_RESUME)
 #define _TIF_SYSCALL_TRACEPOINT	(1<<TIF_SYSCALL_TRACEPOINT)
-#define _TIF_RUNLATCH		(1<<TIF_RUNLATCH)
 #define _TIF_SYSCALL_T_OR_A	(_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | \
 				 _TIF_SECCOMP | _TIF_SYSCALL_TRACEPOINT)
 
diff --git a/arch/powerpc/include/asm/vio.h b/arch/powerpc/include/asm/vio.h
index 6bfd5ff..b19adf7 100644
--- a/arch/powerpc/include/asm/vio.h
+++ b/arch/powerpc/include/asm/vio.h
@@ -46,6 +46,48 @@
 
 struct iommu_table;
 
+/*
+ * Platform Facilities Option (PFO)-specific data
+ */
+
+/* Starting unit address for PFO devices on the VIO BUS */
+#define VIO_BASE_PFO_UA	0x50000000
+
+/**
+ * vio_pfo_op - PFO operation parameters
+ *
+ * @flags: h_call subfunctions and modifiers
+ * @in: Input data block logical real address
+ * @inlen: If non-negative, the length of the input data block.  If negative,
+ *	the length of the input data descriptor list in bytes.
+ * @out: Output data block logical real address
+ * @outlen: If non-negative, the length of the input data block.  If negative,
+ *	the length of the input data descriptor list in bytes.
+ * @csbcpb: Logical real address of the 4k naturally-aligned storage block
+ *	containing the CSB & optional FC field specific CPB
+ * @timeout: # of milliseconds to retry h_call, 0 for no timeout.
+ * @hcall_err: pointer to return the h_call return value, else NULL
+ */
+struct vio_pfo_op {
+	u64 flags;
+	s64 in;
+	s64 inlen;
+	s64 out;
+	s64 outlen;
+	u64 csbcpb;
+	void *done;
+	unsigned long handle;
+	unsigned int timeout;
+	long hcall_err;
+};
+
+/* End PFO specific data */
+
+enum vio_dev_family {
+	VDEVICE,	/* The OF node is a child of /vdevice */
+	PFO,		/* The OF node is a child of /ibm,platform-facilities */
+};
+
 /**
  * vio_dev - This structure is used to describe virtual I/O devices.
  *
@@ -58,6 +100,7 @@
 	const char *name;
 	const char *type;
 	uint32_t unit_address;
+	uint32_t resource_id;
 	unsigned int irq;
 	struct {
 		size_t desired;
@@ -65,6 +108,7 @@
 		size_t allocated;
 		atomic_t allocs_failed;
 	} cmo;
+	enum vio_dev_family family;
 	struct device dev;
 };
 
@@ -95,6 +139,8 @@
 
 extern void __devinit vio_unregister_device(struct vio_dev *dev);
 
+extern int vio_h_cop_sync(struct vio_dev *vdev, struct vio_pfo_op *op);
+
 struct device_node;
 
 extern struct vio_dev *vio_register_device_node(
diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c
index 34b8afe9..4554dc2 100644
--- a/arch/powerpc/kernel/asm-offsets.c
+++ b/arch/powerpc/kernel/asm-offsets.c
@@ -188,10 +188,6 @@
 	DEFINE(SLBSHADOW_STACKESID,
 	       offsetof(struct slb_shadow, save_area[SLB_NUM_BOLTED - 1].esid));
 	DEFINE(SLBSHADOW_SAVEAREA, offsetof(struct slb_shadow, save_area));
-	DEFINE(LPPACASRR0, offsetof(struct lppaca, saved_srr0));
-	DEFINE(LPPACASRR1, offsetof(struct lppaca, saved_srr1));
-	DEFINE(LPPACAANYINT, offsetof(struct lppaca, int_dword.any_int));
-	DEFINE(LPPACADECRINT, offsetof(struct lppaca, int_dword.fields.decr_int));
 	DEFINE(LPPACA_PMCINUSE, offsetof(struct lppaca, pmcregs_in_use));
 	DEFINE(LPPACA_DTLIDX, offsetof(struct lppaca, dtl_idx));
 	DEFINE(LPPACA_YIELDCOUNT, offsetof(struct lppaca, yield_count));
diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S
index ef2074c..ed1718f 100644
--- a/arch/powerpc/kernel/entry_64.S
+++ b/arch/powerpc/kernel/entry_64.S
@@ -63,15 +63,9 @@
 	std	r0,GPR0(r1)
 	std	r10,GPR1(r1)
 	ACCOUNT_CPU_USER_ENTRY(r10, r11)
-	/*
-	 * This "crclr so" clears CR0.SO, which is the error indication on
-	 * return from this system call.  There must be no cmp instruction
-	 * between it and the "mfcr r9" below, otherwise if XER.SO is set,
-	 * CR0.SO will get set, causing all system calls to appear to fail.
-	 */
-	crclr	so
 	std	r2,GPR2(r1)
 	std	r3,GPR3(r1)
+	mfcr	r2
 	std	r4,GPR4(r1)
 	std	r5,GPR5(r1)
 	std	r6,GPR6(r1)
@@ -82,18 +76,20 @@
 	std	r11,GPR10(r1)
 	std	r11,GPR11(r1)
 	std	r11,GPR12(r1)
+	std	r11,_XER(r1)
+	std	r11,_CTR(r1)
 	std	r9,GPR13(r1)
-	mfcr	r9
 	mflr	r10
+	/*
+	 * This clears CR0.SO (bit 28), which is the error indication on
+	 * return from this system call.
+	 */
+	rldimi	r2,r11,28,(63-28)
 	li	r11,0xc01
-	std	r9,_CCR(r1)
 	std	r10,_LINK(r1)
 	std	r11,_TRAP(r1)
-	mfxer	r9
-	mfctr	r10
-	std	r9,_XER(r1)
-	std	r10,_CTR(r1)
 	std	r3,ORIG_GPR3(r1)
+	std	r2,_CCR(r1)
 	ld	r2,PACATOC(r13)
 	addi	r9,r1,STACK_FRAME_OVERHEAD
 	ld	r11,exception_marker@toc(r2)
@@ -154,7 +150,7 @@
 	ld	r10,TI_FLAGS(r11)
 	andi.	r11,r10,_TIF_SYSCALL_T_OR_A
 	bne-	syscall_dotrace
-syscall_dotrace_cont:
+.Lsyscall_dotrace_cont:
 	cmpldi	0,r0,NR_syscalls
 	bge-	syscall_enosys
 
@@ -211,7 +207,7 @@
 	cmpld	r3,r11
 	ld	r5,_CCR(r1)
 	bge-	syscall_error
-syscall_error_cont:
+.Lsyscall_error_cont:
 	ld	r7,_NIP(r1)
 BEGIN_FTR_SECTION
 	stdcx.	r0,0,r1			/* to clear the reservation */
@@ -246,7 +242,7 @@
 	oris	r5,r5,0x1000	/* Set SO bit in CR */
 	neg	r3,r3
 	std	r5,_CCR(r1)
-	b	syscall_error_cont
+	b	.Lsyscall_error_cont
 	
 /* Traced system call support */
 syscall_dotrace:
@@ -268,7 +264,7 @@
 	addi	r9,r1,STACK_FRAME_OVERHEAD
 	clrrdi	r10,r1,THREAD_SHIFT
 	ld	r10,TI_FLAGS(r10)
-	b	syscall_dotrace_cont
+	b	.Lsyscall_dotrace_cont
 
 syscall_enosys:
 	li	r3,-ENOSYS
diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S
index 8f880bc..f7bed44 100644
--- a/arch/powerpc/kernel/exceptions-64s.S
+++ b/arch/powerpc/kernel/exceptions-64s.S
@@ -94,12 +94,10 @@
 data_access_pSeries:
 	HMT_MEDIUM
 	SET_SCRATCH0(r13)
-#ifndef CONFIG_POWER4_ONLY
 BEGIN_FTR_SECTION
 	b	data_access_check_stab
 data_access_not_stab:
 END_MMU_FTR_SECTION_IFCLR(MMU_FTR_SLB)
-#endif
 	EXCEPTION_PROLOG_PSERIES(PACA_EXGEN, data_access_common, EXC_STD,
 				 KVMTEST, 0x300)
 
@@ -301,7 +299,6 @@
 				 EXC_STD, KVMTEST, 0x200)
 	KVM_HANDLER_SKIP(PACA_EXMC, EXC_STD, 0x200)
 
-#ifndef CONFIG_POWER4_ONLY
 	/* moved from 0x300 */
 data_access_check_stab:
 	GET_PACA(r13)
@@ -328,7 +325,6 @@
 	GET_SCRATCH0(r10)
 	std	r10,PACA_EXSLB+EX_R13(r13)
 	EXCEPTION_PROLOG_PSERIES_1(.do_stab_bolted, EXC_STD)
-#endif /* CONFIG_POWER4_ONLY */
 
 	KVM_HANDLER_SKIP(PACA_EXGEN, EXC_STD, 0x300)
 	KVM_HANDLER_SKIP(PACA_EXSLB, EXC_STD, 0x380)
diff --git a/arch/powerpc/kernel/head_44x.S b/arch/powerpc/kernel/head_44x.S
index 7dd2981..22d608e 100644
--- a/arch/powerpc/kernel/head_44x.S
+++ b/arch/powerpc/kernel/head_44x.S
@@ -778,14 +778,6 @@
 	blr
 
 /*
- * extern void giveup_altivec(struct task_struct *prev)
- *
- * The 44x core does not have an AltiVec unit.
- */
-_GLOBAL(giveup_altivec)
-	blr
-
-/*
  * extern void giveup_fpu(struct task_struct *prev)
  *
  * The 44x core does not have an FPU.
diff --git a/arch/powerpc/kernel/head_fsl_booke.S b/arch/powerpc/kernel/head_fsl_booke.S
index 28e6259..de80e0f 100644
--- a/arch/powerpc/kernel/head_fsl_booke.S
+++ b/arch/powerpc/kernel/head_fsl_booke.S
@@ -874,14 +874,6 @@
 	sync
 	blr
 
-/*
- * extern void giveup_altivec(struct task_struct *prev)
- *
- * The e500 core does not have an AltiVec unit.
- */
-_GLOBAL(giveup_altivec)
-	blr
-
 #ifdef CONFIG_SPE
 /*
  * extern void giveup_spe(struct task_struct *prev)
diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c
index 641da9e..7835a5e 100644
--- a/arch/powerpc/kernel/irq.c
+++ b/arch/powerpc/kernel/irq.c
@@ -587,7 +587,7 @@
 {
 	int cpuid;
 
-	if (cpumask_equal(mask, cpu_all_mask)) {
+	if (cpumask_equal(mask, cpu_online_mask)) {
 		static int irq_rover;
 		static DEFINE_RAW_SPINLOCK(irq_rover_lock);
 		unsigned long flags;
diff --git a/arch/powerpc/kernel/misc_32.S b/arch/powerpc/kernel/misc_32.S
index 7cd07b4..386d57f 100644
--- a/arch/powerpc/kernel/misc_32.S
+++ b/arch/powerpc/kernel/misc_32.S
@@ -738,8 +738,23 @@
 	mr      r5, r31
 
 	li	r0, 0
-#elif defined(CONFIG_44x)  && !defined(CONFIG_PPC_47x)
+#elif defined(CONFIG_44x)
 
+	/* Save our parameters */
+	mr	r29, r3
+	mr	r30, r4
+	mr	r31, r5
+
+#ifdef CONFIG_PPC_47x
+	/* Check for 47x cores */
+	mfspr	r3,SPRN_PVR
+	srwi	r3,r3,16
+	cmplwi	cr0,r3,PVR_476@h
+	beq	setup_map_47x
+	cmplwi	cr0,r3,PVR_476_ISS@h
+	beq	setup_map_47x
+#endif /* CONFIG_PPC_47x */
+	
 /*
  * Code for setting up 1:1 mapping for PPC440x for KEXEC
  *
@@ -753,16 +768,15 @@
  * 5) Invalidate the tmp mapping.
  *
  * - Based on the kexec support code for FSL BookE
- * - Doesn't support 47x yet.
  *
  */
-	/* Save our parameters */
-	mr	r29, r3
-	mr	r30, r4
-	mr	r31, r5
 
-	/* Load our MSR_IS and TID to MMUCR for TLB search */
-	mfspr	r3,SPRN_PID
+	/* 
+	 * Load the PID with kernel PID (0).
+	 * Also load our MSR_IS and TID to MMUCR for TLB search.
+	 */
+	li	r3, 0
+	mtspr	SPRN_PID, r3
 	mfmsr	r4
 	andi.	r4,r4,MSR_IS@l
 	beq	wmmucr
@@ -900,6 +914,179 @@
 	li	r3, 0
 	tlbwe	r3, r24, PPC44x_TLB_PAGEID
 	sync
+	b	ppc44x_map_done
+
+#ifdef CONFIG_PPC_47x
+
+	/* 1:1 mapping for 47x */
+
+setup_map_47x:
+
+	/*
+	 * Load the kernel pid (0) to PID and also to MMUCR[TID].
+	 * Also set the MSR IS->MMUCR STS
+	 */
+	li	r3, 0
+	mtspr	SPRN_PID, r3			/* Set PID */
+	mfmsr	r4				/* Get MSR */
+	andi.	r4, r4, MSR_IS@l		/* TS=1? */
+	beq	1f				/* If not, leave STS=0 */
+	oris	r3, r3, PPC47x_MMUCR_STS@h	/* Set STS=1 */
+1:	mtspr	SPRN_MMUCR, r3			/* Put MMUCR */
+	sync
+
+	/* Find the entry we are running from */
+	bl	2f
+2:	mflr	r23
+	tlbsx	r23, 0, r23
+	tlbre	r24, r23, 0			/* TLB Word 0 */
+	tlbre	r25, r23, 1			/* TLB Word 1 */
+	tlbre	r26, r23, 2			/* TLB Word 2 */
+
+
+	/*
+	 * Invalidates all the tlb entries by writing to 256 RPNs(r4)
+	 * of 4k page size in all  4 ways (0-3 in r3).
+	 * This would invalidate the entire UTLB including the one we are
+	 * running from. However the shadow TLB entries would help us 
+	 * to continue the execution, until we flush them (rfi/isync).
+	 */
+	addis	r3, 0, 0x8000			/* specify the way */
+	addi	r4, 0, 0			/* TLB Word0 = (EPN=0, VALID = 0) */
+	addi	r5, 0, 0
+	b	clear_utlb_entry
+
+	/* Align the loop to speed things up. from head_44x.S */
+	.align	6
+
+clear_utlb_entry:
+
+	tlbwe	r4, r3, 0
+	tlbwe	r5, r3, 1
+	tlbwe	r5, r3, 2
+	addis	r3, r3, 0x2000			/* Increment the way */
+	cmpwi	r3, 0
+	bne	clear_utlb_entry
+	addis	r3, 0, 0x8000
+	addis	r4, r4, 0x100			/* Increment the EPN */
+	cmpwi	r4, 0
+	bne	clear_utlb_entry
+
+	/* Create the entries in the other address space */
+	mfmsr	r5
+	rlwinm	r7, r5, 27, 31, 31		/* Get the TS (Bit 26) from MSR */
+	xori	r7, r7, 1			/* r7 = !TS */
+
+	insrwi	r24, r7, 1, 21			/* Change the TS in the saved TLB word 0 */
+
+	/* 
+	 * write out the TLB entries for the tmp mapping
+	 * Use way '0' so that we could easily invalidate it later.
+	 */
+	lis	r3, 0x8000			/* Way '0' */ 
+
+	tlbwe	r24, r3, 0
+	tlbwe	r25, r3, 1
+	tlbwe	r26, r3, 2
+
+	/* Update the msr to the new TS */
+	insrwi	r5, r7, 1, 26
+
+	bl	1f
+1:	mflr	r6
+	addi	r6, r6, (2f-1b)
+
+	mtspr	SPRN_SRR0, r6
+	mtspr	SPRN_SRR1, r5
+	rfi
+
+	/* 
+	 * Now we are in the tmp address space.
+	 * Create a 1:1 mapping for 0-2GiB in the original TS.
+	 */
+2:
+	li	r3, 0
+	li	r4, 0				/* TLB Word 0 */
+	li	r5, 0				/* TLB Word 1 */
+	li	r6, 0
+	ori	r6, r6, PPC47x_TLB2_S_RWX	/* TLB word 2 */
+
+	li	r8, 0				/* PageIndex */
+
+	xori	r7, r7, 1			/* revert back to original TS */
+
+write_utlb:
+	rotlwi	r5, r8, 28			/* RPN = PageIndex * 256M */
+						/* ERPN = 0 as we don't use memory above 2G */
+
+	mr	r4, r5				/* EPN = RPN */
+	ori	r4, r4, (PPC47x_TLB0_VALID | PPC47x_TLB0_256M)
+	insrwi	r4, r7, 1, 21			/* Insert the TS to Word 0 */
+
+	tlbwe	r4, r3, 0			/* Write out the entries */
+	tlbwe	r5, r3, 1
+	tlbwe	r6, r3, 2
+	addi	r8, r8, 1
+	cmpwi	r8, 8				/* Have we completed ? */
+	bne	write_utlb
+
+	/* make sure we complete the TLB write up */
+	isync
+
+	/* 
+	 * Prepare to jump to the 1:1 mapping.
+	 * 1) Extract page size of the tmp mapping
+	 *    DSIZ = TLB_Word0[22:27]
+	 * 2) Calculate the physical address of the address
+	 *    to jump to.
+	 */
+	rlwinm	r10, r24, 0, 22, 27
+
+	cmpwi	r10, PPC47x_TLB0_4K
+	bne	0f
+	li	r10, 0x1000			/* r10 = 4k */
+	bl	1f
+
+0:
+	/* Defaults to 256M */
+	lis	r10, 0x1000
+	
+	bl	1f
+1:	mflr	r4
+	addi	r4, r4, (2f-1b)			/* virtual address  of 2f */
+
+	subi	r11, r10, 1			/* offsetmask = Pagesize - 1 */
+	not	r10, r11			/* Pagemask = ~(offsetmask) */
+
+	and	r5, r25, r10			/* Physical page */
+	and	r6, r4, r11			/* offset within the current page */
+
+	or	r5, r5, r6			/* Physical address for 2f */
+
+	/* Switch the TS in MSR to the original one */
+	mfmsr	r8
+	insrwi	r8, r7, 1, 26
+
+	mtspr	SPRN_SRR1, r8
+	mtspr	SPRN_SRR0, r5
+	rfi
+
+2:
+	/* Invalidate the tmp mapping */
+	lis	r3, 0x8000			/* Way '0' */
+
+	clrrwi	r24, r24, 12			/* Clear the valid bit */
+	tlbwe	r24, r3, 0
+	tlbwe	r25, r3, 1
+	tlbwe	r26, r3, 2
+
+	/* Make sure we complete the TLB write and flush the shadow TLB */
+	isync
+
+#endif
+
+ppc44x_map_done:
+
 
 	/* Restore the parameters */
 	mr	r3, r29
diff --git a/arch/powerpc/kernel/paca.c b/arch/powerpc/kernel/paca.c
index 0bb1f98..fbe1a12 100644
--- a/arch/powerpc/kernel/paca.c
+++ b/arch/powerpc/kernel/paca.c
@@ -36,10 +36,7 @@
 	[0 ... (NR_LPPACAS-1)] = {
 		.desc = 0xd397d781,	/* "LpPa" */
 		.size = sizeof(struct lppaca),
-		.dyn_proc_status = 2,
-		.decr_val = 0x00ff0000,
 		.fpregs_in_use = 1,
-		.end_of_quantum = 0xfffffffffffffffful,
 		.slb_count = 64,
 		.vmxregs_in_use = 0,
 		.page_ins = 0,
diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c
index aa05935..710f400 100644
--- a/arch/powerpc/kernel/process.c
+++ b/arch/powerpc/kernel/process.c
@@ -124,7 +124,7 @@
 	if (current->thread.regs && (current->thread.regs->msr & MSR_VEC))
 		giveup_altivec(current);
 	else
-		giveup_altivec(NULL);	/* just enable AltiVec for kernel - force */
+		giveup_altivec_notask();
 #else
 	giveup_altivec(last_task_used_altivec);
 #endif /* CONFIG_SMP */
@@ -711,18 +711,21 @@
 }
 
 /*
- * This gets called before we allocate a new thread and copy
- * the current task into it.
+ * this gets called so that we can store coprocessor state into memory and
+ * copy the current task into the new thread.
  */
-void prepare_to_copy(struct task_struct *tsk)
+int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)
 {
-	flush_fp_to_thread(current);
-	flush_altivec_to_thread(current);
-	flush_vsx_to_thread(current);
-	flush_spe_to_thread(current);
+	flush_fp_to_thread(src);
+	flush_altivec_to_thread(src);
+	flush_vsx_to_thread(src);
+	flush_spe_to_thread(src);
 #ifdef CONFIG_HAVE_HW_BREAKPOINT
-	flush_ptrace_hw_breakpoint(tsk);
+	flush_ptrace_hw_breakpoint(src);
 #endif /* CONFIG_HAVE_HW_BREAKPOINT */
+
+	*dst = *src;
+	return 0;
 }
 
 /*
diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c
index 9986027..1b488e5 100644
--- a/arch/powerpc/kernel/prom_init.c
+++ b/arch/powerpc/kernel/prom_init.c
@@ -680,6 +680,9 @@
 #define OV3_VMX			0x40	/* VMX/Altivec */
 #define OV3_DFP			0x20	/* decimal FP */
 
+/* Option vector 4: IBM PAPR implementation */
+#define OV4_MIN_ENT_CAP		0x01	/* minimum VP entitled capacity */
+
 /* Option vector 5: PAPR/OF options supported */
 #define OV5_LPAR		0x80	/* logical partitioning supported */
 #define OV5_SPLPAR		0x40	/* shared-processor LPAR supported */
@@ -701,6 +704,8 @@
 #define OV5_XCMO			0x00
 #endif
 #define OV5_TYPE1_AFFINITY	0x80	/* Type 1 NUMA affinity */
+#define OV5_PFO_HW_RNG		0x80	/* PFO Random Number Generator */
+#define OV5_PFO_HW_ENCR		0x20	/* PFO Encryption Accelerator */
 
 /* Option Vector 6: IBM PAPR hints */
 #define OV6_LINUX		0x02	/* Linux is our OS */
@@ -744,11 +749,12 @@
 	OV3_FP | OV3_VMX | OV3_DFP,
 
 	/* option vector 4: IBM PAPR implementation */
-	2 - 2,				/* length */
+	3 - 2,				/* length */
 	0,				/* don't halt */
+	OV4_MIN_ENT_CAP,		/* minimum VP entitled capacity */
 
 	/* option vector 5: PAPR/OF options */
-	13 - 2,				/* length */
+	18 - 2,				/* length */
 	0,				/* don't ignore, don't halt */
 	OV5_LPAR | OV5_SPLPAR | OV5_LARGE_PAGES | OV5_DRCONF_MEMORY |
 	OV5_DONATE_DEDICATE_CPU | OV5_MSI,
@@ -762,8 +768,13 @@
 	 * must match by the macro below. Update the definition if
 	 * the structure layout changes.
 	 */
-#define IBM_ARCH_VEC_NRCORES_OFFSET	100
+#define IBM_ARCH_VEC_NRCORES_OFFSET	101
 	W(NR_CPUS),			/* number of cores supported */
+	0,
+	0,
+	0,
+	0,
+	OV5_PFO_HW_RNG | OV5_PFO_HW_ENCR,
 
 	/* option vector 6: IBM PAPR hints */
 	4 - 2,				/* length */
diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c
index dd5e214..c10fc28 100644
--- a/arch/powerpc/kernel/ptrace.c
+++ b/arch/powerpc/kernel/ptrace.c
@@ -1432,40 +1432,6 @@
 #endif
 }
 
-/*
- * Here are the old "legacy" powerpc specific getregs/setregs ptrace calls,
- * we mark them as obsolete now, they will be removed in a future version
- */
-static long arch_ptrace_old(struct task_struct *child, long request,
-			    unsigned long addr, unsigned long data)
-{
-	void __user *datavp = (void __user *) data;
-
-	switch (request) {
-	case PPC_PTRACE_GETREGS:	/* Get GPRs 0 - 31. */
-		return copy_regset_to_user(child, &user_ppc_native_view,
-					   REGSET_GPR, 0, 32 * sizeof(long),
-					   datavp);
-
-	case PPC_PTRACE_SETREGS:	/* Set GPRs 0 - 31. */
-		return copy_regset_from_user(child, &user_ppc_native_view,
-					     REGSET_GPR, 0, 32 * sizeof(long),
-					     datavp);
-
-	case PPC_PTRACE_GETFPREGS:	/* Get FPRs 0 - 31. */
-		return copy_regset_to_user(child, &user_ppc_native_view,
-					   REGSET_FPR, 0, 32 * sizeof(double),
-					   datavp);
-
-	case PPC_PTRACE_SETFPREGS:	/* Set FPRs 0 - 31. */
-		return copy_regset_from_user(child, &user_ppc_native_view,
-					     REGSET_FPR, 0, 32 * sizeof(double),
-					     datavp);
-	}
-
-	return -EPERM;
-}
-
 long arch_ptrace(struct task_struct *child, long request,
 		 unsigned long addr, unsigned long data)
 {
@@ -1687,14 +1653,6 @@
 					     datavp);
 #endif
 
-	/* Old reverse args ptrace callss */
-	case PPC_PTRACE_GETREGS: /* Get GPRs 0 - 31. */
-	case PPC_PTRACE_SETREGS: /* Set GPRs 0 - 31. */
-	case PPC_PTRACE_GETFPREGS: /* Get FPRs 0 - 31. */
-	case PPC_PTRACE_SETFPREGS: /* Get FPRs 0 - 31. */
-		ret = arch_ptrace_old(child, request, addr, data);
-		break;
-
 	default:
 		ret = ptrace_request(child, request, addr, data);
 		break;
diff --git a/arch/powerpc/kernel/ptrace32.c b/arch/powerpc/kernel/ptrace32.c
index 469349d..8c21658 100644
--- a/arch/powerpc/kernel/ptrace32.c
+++ b/arch/powerpc/kernel/ptrace32.c
@@ -39,30 +39,6 @@
  * in exit.c or in signal.c.
  */
 
-/*
- * Here are the old "legacy" powerpc specific getregs/setregs ptrace calls,
- * we mark them as obsolete now, they will be removed in a future version
- */
-static long compat_ptrace_old(struct task_struct *child, long request,
-			      long addr, long data)
-{
-	switch (request) {
-	case PPC_PTRACE_GETREGS:	/* Get GPRs 0 - 31. */
-		return copy_regset_to_user(child,
-					   task_user_regset_view(current), 0,
-					   0, 32 * sizeof(compat_long_t),
-					   compat_ptr(data));
-
-	case PPC_PTRACE_SETREGS:	/* Set GPRs 0 - 31. */
-		return copy_regset_from_user(child,
-					     task_user_regset_view(current), 0,
-					     0, 32 * sizeof(compat_long_t),
-					     compat_ptr(data));
-	}
-
-	return -EPERM;
-}
-
 /* Macros to workout the correct index for the FPR in the thread struct */
 #define FPRNUMBER(i) (((i) - PT_FPR0) >> 1)
 #define FPRHALF(i) (((i) - PT_FPR0) & 1)
@@ -308,8 +284,6 @@
 	case PTRACE_SETVSRREGS:
 	case PTRACE_GETREGS64:
 	case PTRACE_SETREGS64:
-	case PPC_PTRACE_GETFPREGS:
-	case PPC_PTRACE_SETFPREGS:
 	case PTRACE_KILL:
 	case PTRACE_SINGLESTEP:
 	case PTRACE_DETACH:
@@ -322,12 +296,6 @@
 		ret = arch_ptrace(child, request, addr, data);
 		break;
 
-	/* Old reverse args ptrace callss */
-	case PPC_PTRACE_GETREGS: /* Get GPRs 0 - 31. */
-	case PPC_PTRACE_SETREGS: /* Set GPRs 0 - 31. */
-		ret = compat_ptrace_old(child, request, addr, data);
-		break;
-
 	default:
 		ret = compat_ptrace_request(child, request, addr, data);
 		break;
diff --git a/arch/powerpc/kernel/vector.S b/arch/powerpc/kernel/vector.S
index 4d5a3ed..e830289 100644
--- a/arch/powerpc/kernel/vector.S
+++ b/arch/powerpc/kernel/vector.S
@@ -89,6 +89,16 @@
 	/* restore registers and return */
 	blr
 
+_GLOBAL(giveup_altivec_notask)
+	mfmsr	r3
+	andis.	r4,r3,MSR_VEC@h
+	bnelr				/* Already enabled? */
+	oris	r3,r3,MSR_VEC@h
+	SYNC
+	MTMSRD(r3)			/* enable use of VMX now */
+	isync
+	blr
+
 /*
  * giveup_altivec(tsk)
  * Disable VMX for the task given as the argument,
diff --git a/arch/powerpc/kernel/vio.c b/arch/powerpc/kernel/vio.c
index a3a9990..cb87301 100644
--- a/arch/powerpc/kernel/vio.c
+++ b/arch/powerpc/kernel/vio.c
@@ -14,7 +14,9 @@
  *      2 of the License, or (at your option) any later version.
  */
 
+#include <linux/cpu.h>
 #include <linux/types.h>
+#include <linux/delay.h>
 #include <linux/stat.h>
 #include <linux/device.h>
 #include <linux/init.h>
@@ -709,13 +711,26 @@
 	struct vio_driver *viodrv = to_vio_driver(dev->driver);
 	unsigned long flags;
 	size_t size;
+	bool dma_capable = false;
 
-	/*
-	 * Check to see that device has a DMA window and configure
-	 * entitlement for the device.
-	 */
-	if (of_get_property(viodev->dev.of_node,
-	                    "ibm,my-dma-window", NULL)) {
+	/* A device requires entitlement if it has a DMA window property */
+	switch (viodev->family) {
+	case VDEVICE:
+		if (of_get_property(viodev->dev.of_node,
+					"ibm,my-dma-window", NULL))
+			dma_capable = true;
+		break;
+	case PFO:
+		dma_capable = false;
+		break;
+	default:
+		dev_warn(dev, "unknown device family: %d\n", viodev->family);
+		BUG();
+		break;
+	}
+
+	/* Configure entitlement for the device. */
+	if (dma_capable) {
 		/* Check that the driver is CMO enabled and get desired DMA */
 		if (!viodrv->get_desired_dma) {
 			dev_err(dev, "%s: device driver does not support CMO\n",
@@ -1050,6 +1065,94 @@
 EXPORT_SYMBOL(vio_cmo_entitlement_update);
 EXPORT_SYMBOL(vio_cmo_set_dev_desired);
 
+
+/*
+ * Platform Facilities Option (PFO) support
+ */
+
+/**
+ * vio_h_cop_sync - Perform a synchronous PFO co-processor operation
+ *
+ * @vdev - Pointer to a struct vio_dev for device
+ * @op - Pointer to a struct vio_pfo_op for the operation parameters
+ *
+ * Calls the hypervisor to synchronously perform the PFO operation
+ * described in @op.  In the case of a busy response from the hypervisor,
+ * the operation will be re-submitted indefinitely unless a non-zero timeout
+ * is specified or an error occurs. The timeout places a limit on when to
+ * stop re-submitting a operation, the total time can be exceeded if an
+ * operation is in progress.
+ *
+ * If op->hcall_ret is not NULL, this will be set to the return from the
+ * last h_cop_op call or it will be 0 if an error not involving the h_call
+ * was encountered.
+ *
+ * Returns:
+ *	0 on success,
+ *	-EINVAL if the h_call fails due to an invalid parameter,
+ *	-E2BIG if the h_call can not be performed synchronously,
+ *	-EBUSY if a timeout is specified and has elapsed,
+ *	-EACCES if the memory area for data/status has been rescinded, or
+ *	-EPERM if a hardware fault has been indicated
+ */
+int vio_h_cop_sync(struct vio_dev *vdev, struct vio_pfo_op *op)
+{
+	struct device *dev = &vdev->dev;
+	unsigned long deadline = 0;
+	long hret = 0;
+	int ret = 0;
+
+	if (op->timeout)
+		deadline = jiffies + msecs_to_jiffies(op->timeout);
+
+	while (true) {
+		hret = plpar_hcall_norets(H_COP, op->flags,
+				vdev->resource_id,
+				op->in, op->inlen, op->out,
+				op->outlen, op->csbcpb);
+
+		if (hret == H_SUCCESS ||
+		    (hret != H_NOT_ENOUGH_RESOURCES &&
+		     hret != H_BUSY && hret != H_RESOURCE) ||
+		    (op->timeout && time_after(deadline, jiffies)))
+			break;
+
+		dev_dbg(dev, "%s: hcall ret(%ld), retrying.\n", __func__, hret);
+	}
+
+	switch (hret) {
+	case H_SUCCESS:
+		ret = 0;
+		break;
+	case H_OP_MODE:
+	case H_TOO_BIG:
+		ret = -E2BIG;
+		break;
+	case H_RESCINDED:
+		ret = -EACCES;
+		break;
+	case H_HARDWARE:
+		ret = -EPERM;
+		break;
+	case H_NOT_ENOUGH_RESOURCES:
+	case H_RESOURCE:
+	case H_BUSY:
+		ret = -EBUSY;
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	if (ret)
+		dev_dbg(dev, "%s: Sync h_cop_op failure (ret:%d) (hret:%ld)\n",
+				__func__, ret, hret);
+
+	op->hcall_err = hret;
+	return ret;
+}
+EXPORT_SYMBOL(vio_h_cop_sync);
+
 static struct iommu_table *vio_build_iommu_table(struct vio_dev *dev)
 {
 	const unsigned char *dma_window;
@@ -1211,35 +1314,87 @@
 struct vio_dev *vio_register_device_node(struct device_node *of_node)
 {
 	struct vio_dev *viodev;
+	struct device_node *parent_node;
 	const unsigned int *unit_address;
+	const unsigned int *pfo_resid = NULL;
+	enum vio_dev_family family;
+	const char *of_node_name = of_node->name ? of_node->name : "<unknown>";
 
-	/* we need the 'device_type' property, in order to match with drivers */
-	if (of_node->type == NULL) {
-		printk(KERN_WARNING "%s: node %s missing 'device_type'\n",
-				__func__,
-				of_node->name ? of_node->name : "<unknown>");
+	/*
+	 * Determine if this node is a under the /vdevice node or under the
+	 * /ibm,platform-facilities node.  This decides the device's family.
+	 */
+	parent_node = of_get_parent(of_node);
+	if (parent_node) {
+		if (!strcmp(parent_node->full_name, "/ibm,platform-facilities"))
+			family = PFO;
+		else if (!strcmp(parent_node->full_name, "/vdevice"))
+			family = VDEVICE;
+		else {
+			pr_warn("%s: parent(%s) of %s not recognized.\n",
+					__func__,
+					parent_node->full_name,
+					of_node_name);
+			of_node_put(parent_node);
+			return NULL;
+		}
+		of_node_put(parent_node);
+	} else {
+		pr_warn("%s: could not determine the parent of node %s.\n",
+				__func__, of_node_name);
 		return NULL;
 	}
 
-	unit_address = of_get_property(of_node, "reg", NULL);
-	if (unit_address == NULL) {
-		printk(KERN_WARNING "%s: node %s missing 'reg'\n",
-				__func__,
-				of_node->name ? of_node->name : "<unknown>");
-		return NULL;
+	if (family == PFO) {
+		if (of_get_property(of_node, "interrupt-controller", NULL)) {
+			pr_debug("%s: Skipping the interrupt controller %s.\n",
+					__func__, of_node_name);
+			return NULL;
+		}
 	}
 
 	/* allocate a vio_dev for this node */
 	viodev = kzalloc(sizeof(struct vio_dev), GFP_KERNEL);
-	if (viodev == NULL)
+	if (viodev == NULL) {
+		pr_warn("%s: allocation failure for VIO device.\n", __func__);
 		return NULL;
+	}
 
-	viodev->irq = irq_of_parse_and_map(of_node, 0);
+	/* we need the 'device_type' property, in order to match with drivers */
+	viodev->family = family;
+	if (viodev->family == VDEVICE) {
+		if (of_node->type != NULL)
+			viodev->type = of_node->type;
+		else {
+			pr_warn("%s: node %s is missing the 'device_type' "
+					"property.\n", __func__, of_node_name);
+			goto out;
+		}
 
-	dev_set_name(&viodev->dev, "%x", *unit_address);
+		unit_address = of_get_property(of_node, "reg", NULL);
+		if (unit_address == NULL) {
+			pr_warn("%s: node %s missing 'reg'\n",
+					__func__, of_node_name);
+			goto out;
+		}
+		dev_set_name(&viodev->dev, "%x", *unit_address);
+		viodev->irq = irq_of_parse_and_map(of_node, 0);
+		viodev->unit_address = *unit_address;
+	} else {
+		/* PFO devices need their resource_id for submitting COP_OPs
+		 * This is an optional field for devices, but is required when
+		 * performing synchronous ops */
+		pfo_resid = of_get_property(of_node, "ibm,resource-id", NULL);
+		if (pfo_resid != NULL)
+			viodev->resource_id = *pfo_resid;
+
+		unit_address = NULL;
+		dev_set_name(&viodev->dev, "%s", of_node_name);
+		viodev->type = of_node_name;
+		viodev->irq = 0;
+	}
+
 	viodev->name = of_node->name;
-	viodev->type = of_node->type;
-	viodev->unit_address = *unit_address;
 	viodev->dev.of_node = of_node_get(of_node);
 
 	if (firmware_has_feature(FW_FEATURE_CMO))
@@ -1267,16 +1422,51 @@
 	}
 
 	return viodev;
+
+out:	/* Use this exit point for any return prior to device_register */
+	kfree(viodev);
+
+	return NULL;
 }
 EXPORT_SYMBOL(vio_register_device_node);
 
+/*
+ * vio_bus_scan_for_devices - Scan OF and register each child device
+ * @root_name - OF node name for the root of the subtree to search.
+ *		This must be non-NULL
+ *
+ * Starting from the root node provide, register the device node for
+ * each child beneath the root.
+ */
+static void vio_bus_scan_register_devices(char *root_name)
+{
+	struct device_node *node_root, *node_child;
+
+	if (!root_name)
+		return;
+
+	node_root = of_find_node_by_name(NULL, root_name);
+	if (node_root) {
+
+		/*
+		 * Create struct vio_devices for each virtual device in
+		 * the device tree. Drivers will associate with them later.
+		 */
+		node_child = of_get_next_child(node_root, NULL);
+		while (node_child) {
+			vio_register_device_node(node_child);
+			node_child = of_get_next_child(node_root, node_child);
+		}
+		of_node_put(node_root);
+	}
+}
+
 /**
  * vio_bus_init: - Initialize the virtual IO bus
  */
 static int __init vio_bus_init(void)
 {
 	int err;
-	struct device_node *node_vroot;
 
 	if (firmware_has_feature(FW_FEATURE_CMO))
 		vio_cmo_sysfs_init();
@@ -1301,19 +1491,8 @@
 	if (firmware_has_feature(FW_FEATURE_CMO))
 		vio_cmo_bus_init();
 
-	node_vroot = of_find_node_by_name(NULL, "vdevice");
-	if (node_vroot) {
-		struct device_node *of_node;
-
-		/*
-		 * Create struct vio_devices for each virtual device in
-		 * the device tree. Drivers will associate with them later.
-		 */
-		for (of_node = node_vroot->child; of_node != NULL;
-				of_node = of_node->sibling)
-			vio_register_device_node(of_node);
-		of_node_put(node_vroot);
-	}
+	vio_bus_scan_register_devices("vdevice");
+	vio_bus_scan_register_devices("ibm,platform-facilities");
 
 	return 0;
 }
@@ -1436,12 +1615,28 @@
 {
 	const uint32_t *unit_address;
 	char kobj_name[20];
+	struct device_node *vnode_parent;
+	const char *dev_type;
+
+	vnode_parent = of_get_parent(vnode);
+	if (!vnode_parent)
+		return NULL;
+
+	dev_type = of_get_property(vnode_parent, "device_type", NULL);
+	of_node_put(vnode_parent);
+	if (!dev_type)
+		return NULL;
 
 	/* construct the kobject name from the device node */
-	unit_address = of_get_property(vnode, "reg", NULL);
-	if (!unit_address)
+	if (!strcmp(dev_type, "vdevice")) {
+		unit_address = of_get_property(vnode, "reg", NULL);
+		if (!unit_address)
+			return NULL;
+		snprintf(kobj_name, sizeof(kobj_name), "%x", *unit_address);
+	} else if (!strcmp(dev_type, "ibm,platform-facilities"))
+		snprintf(kobj_name, sizeof(kobj_name), "%s", vnode->name);
+	else
 		return NULL;
-	snprintf(kobj_name, sizeof(kobj_name), "%x", *unit_address);
 
 	return vio_find_name(kobj_name);
 }
diff --git a/arch/powerpc/lib/copyuser_64.S b/arch/powerpc/lib/copyuser_64.S
index 773d38f..d73a590 100644
--- a/arch/powerpc/lib/copyuser_64.S
+++ b/arch/powerpc/lib/copyuser_64.S
@@ -30,7 +30,7 @@
 	dcbt	0,r4
 	beq	.Lcopy_page_4K
 	andi.	r6,r6,7
-	PPC_MTOCRF	0x01,r5
+	PPC_MTOCRF(0x01,r5)
 	blt	cr1,.Lshort_copy
 /* Below we want to nop out the bne if we're on a CPU that has the
  * CPU_FTR_UNALIGNED_LD_STD bit set and the CPU_FTR_CP_USE_DCBTZ bit
@@ -186,7 +186,7 @@
 	blr
 
 .Ldst_unaligned:
-	PPC_MTOCRF	0x01,r6		/* put #bytes to 8B bdry into cr7 */
+	PPC_MTOCRF(0x01,r6)		/* put #bytes to 8B bdry into cr7 */
 	subf	r5,r6,r5
 	li	r7,0
 	cmpldi	cr1,r5,16
@@ -201,7 +201,7 @@
 2:	bf	cr7*4+1,3f
 37:	lwzx	r0,r7,r4
 83:	stwx	r0,r7,r3
-3:	PPC_MTOCRF	0x01,r5
+3:	PPC_MTOCRF(0x01,r5)
 	add	r4,r6,r4
 	add	r3,r6,r3
 	b	.Ldst_aligned
diff --git a/arch/powerpc/lib/mem_64.S b/arch/powerpc/lib/mem_64.S
index 11ce045..f4fcb0b 100644
--- a/arch/powerpc/lib/mem_64.S
+++ b/arch/powerpc/lib/mem_64.S
@@ -19,7 +19,7 @@
 	rlwimi	r4,r4,16,0,15
 	cmplw	cr1,r5,r0		/* do we get that far? */
 	rldimi	r4,r4,32,0
-	PPC_MTOCRF	1,r0
+	PPC_MTOCRF(1,r0)
 	mr	r6,r3
 	blt	cr1,8f
 	beq+	3f			/* if already 8-byte aligned */
@@ -49,7 +49,7 @@
 	bdnz	4b
 5:	srwi.	r0,r5,3
 	clrlwi	r5,r5,29
-	PPC_MTOCRF	1,r0
+	PPC_MTOCRF(1,r0)
 	beq	8f
 	bf	29,6f
 	std	r4,0(r6)
@@ -65,7 +65,7 @@
 	std	r4,0(r6)
 	addi	r6,r6,8
 8:	cmpwi	r5,0
-	PPC_MTOCRF	1,r5
+	PPC_MTOCRF(1,r5)
 	beqlr+
 	bf	29,9f
 	stw	r4,0(r6)
diff --git a/arch/powerpc/lib/memcpy_64.S b/arch/powerpc/lib/memcpy_64.S
index e178922..82fea39 100644
--- a/arch/powerpc/lib/memcpy_64.S
+++ b/arch/powerpc/lib/memcpy_64.S
@@ -12,7 +12,7 @@
 	.align	7
 _GLOBAL(memcpy)
 	std	r3,48(r1)	/* save destination pointer for return value */
-	PPC_MTOCRF	0x01,r5
+	PPC_MTOCRF(0x01,r5)
 	cmpldi	cr1,r5,16
 	neg	r6,r3		# LS 3 bits = # bytes to 8-byte dest bdry
 	andi.	r6,r6,7
@@ -154,7 +154,7 @@
 	blr
 
 .Ldst_unaligned:
-	PPC_MTOCRF	0x01,r6		# put #bytes to 8B bdry into cr7
+	PPC_MTOCRF(0x01,r6)		# put #bytes to 8B bdry into cr7
 	subf	r5,r6,r5
 	li	r7,0
 	cmpldi	cr1,r5,16
@@ -169,7 +169,7 @@
 2:	bf	cr7*4+1,3f
 	lwzx	r0,r7,r4
 	stwx	r0,r7,r3
-3:	PPC_MTOCRF	0x01,r5
+3:	PPC_MTOCRF(0x01,r5)
 	add	r4,r6,r4
 	add	r3,r6,r3
 	b	.Ldst_aligned
diff --git a/arch/powerpc/platforms/44x/Kconfig b/arch/powerpc/platforms/44x/Kconfig
index 2e4e64a..8abf6fb8 100644
--- a/arch/powerpc/platforms/44x/Kconfig
+++ b/arch/powerpc/platforms/44x/Kconfig
@@ -23,6 +23,8 @@
 	default n
 	select PPC44x_SIMPLE
 	select APM821xx
+	select PCI_MSI
+	select PPC4xx_MSI
 	select PPC4xx_PCI_EXPRESS
 	select IBM_EMAC_RGMII
 	help
diff --git a/arch/powerpc/platforms/Kconfig.cputype b/arch/powerpc/platforms/Kconfig.cputype
index 9c80fc0..61c9550 100644
--- a/arch/powerpc/platforms/Kconfig.cputype
+++ b/arch/powerpc/platforms/Kconfig.cputype
@@ -78,6 +78,36 @@
 
 endchoice
 
+choice
+	prompt "CPU selection"
+	depends on PPC64
+	default GENERIC_CPU
+	help
+	  This will create a kernel which is optimised for a particular CPU.
+	  The resulting kernel may not run on other CPUs, so use this with care.
+
+	  If unsure, select Generic.
+
+config GENERIC_CPU
+	bool "Generic"
+
+config CELL_CPU
+	bool "Cell Broadband Engine"
+
+config POWER4_CPU
+	bool "POWER4"
+
+config POWER5_CPU
+	bool "POWER5"
+
+config POWER6_CPU
+	bool "POWER6"
+
+config POWER7_CPU
+	bool "POWER7"
+
+endchoice
+
 config PPC_BOOK3S
 	def_bool y
 	depends on PPC_BOOK3S_32 || PPC_BOOK3S_64
@@ -86,15 +116,6 @@
 	def_bool y
 	depends on PPC_BOOK3E_64
 
-config POWER4_ONLY
-	bool "Optimize for POWER4"
-	depends on PPC64 && PPC_BOOK3S
-	default n
-	---help---
-	  Cause the compiler to optimize for POWER4/POWER5/PPC970 processors.
-	  The resulting binary will not work on POWER3 or RS64 processors
-	  when compiled with binutils 2.15 or later.
-
 config 6xx
 	def_bool y
 	depends on PPC32 && PPC_BOOK3S
diff --git a/arch/powerpc/platforms/powermac/low_i2c.c b/arch/powerpc/platforms/powermac/low_i2c.c
index 03685a3..fc536f2 100644
--- a/arch/powerpc/platforms/powermac/low_i2c.c
+++ b/arch/powerpc/platforms/powermac/low_i2c.c
@@ -1503,6 +1503,7 @@
 		if (bus->platform_dev == NULL)
 			return -ENOMEM;
 		bus->platform_dev->dev.platform_data = bus;
+		bus->platform_dev->dev.of_node = bus->busnode;
 		platform_device_add(bus->platform_dev);
 	}
 
diff --git a/arch/powerpc/platforms/ps3/Kconfig b/arch/powerpc/platforms/ps3/Kconfig
index 476d9d9..46b7f02 100644
--- a/arch/powerpc/platforms/ps3/Kconfig
+++ b/arch/powerpc/platforms/ps3/Kconfig
@@ -7,7 +7,6 @@
 	select USB_OHCI_BIG_ENDIAN_MMIO
 	select USB_ARCH_HAS_EHCI
 	select USB_EHCI_BIG_ENDIAN_MMIO
-	select MEMORY_HOTPLUG
 	select PPC_PCI_CHOICE
 	help
 	  This option enables support for the Sony PS3 game console
@@ -74,7 +73,7 @@
 	help
 	  Include support for the PS3 AV Settings driver.
 
-	  This support is required for graphics and sound. In
+	  This support is required for PS3 graphics and sound. In
 	  general, all users will say Y or M.
 
 config PS3_SYS_MANAGER
@@ -85,9 +84,22 @@
 	help
 	  Include support for the PS3 System Manager.
 
-	  This support is required for system control.  In
+	  This support is required for PS3 system control.  In
 	  general, all users will say Y or M.
 
+config PS3_REPOSITORY_WRITE
+	bool "PS3 Repository write support" if PS3_ADVANCED
+	depends on PPC_PS3
+	default n
+	help
+	  Enables support for writing to the PS3 System Repository.
+
+	  This support is intended for bootloaders that need to store data
+	  in the repository for later boot stages.
+
+	  If in doubt, say N here and reduce the size of the kernel by a
+	  small amount.
+
 config PS3_STORAGE
 	depends on PPC_PS3
 	tristate
@@ -122,7 +134,7 @@
 
 	  This support is required to access the PS3 FLASH ROM, which
 	  contains the boot loader and some boot options.
-	  In general, all users will say Y or M.
+	  In general, PS3 OtherOS users will say Y or M.
 
 	  As this driver needs a fixed buffer of 256 KiB of memory, it can
 	  be disabled on the kernel command line using "ps3flash=off", to
@@ -156,7 +168,7 @@
 	  via the Ethernet port (UDP port number 18194).
 
 	  This driver uses a trivial implementation and is independent
-	  from the main network driver.
+	  from the main PS3 gelic network driver.
 
 	  If in doubt, say N here.
 
diff --git a/arch/powerpc/platforms/ps3/mm.c b/arch/powerpc/platforms/ps3/mm.c
index de2aea4..0c9f643 100644
--- a/arch/powerpc/platforms/ps3/mm.c
+++ b/arch/powerpc/platforms/ps3/mm.c
@@ -20,7 +20,6 @@
 
 #include <linux/kernel.h>
 #include <linux/export.h>
-#include <linux/memory_hotplug.h>
 #include <linux/memblock.h>
 #include <linux/slab.h>
 
@@ -79,12 +78,14 @@
  * @base: base address
  * @size: size in bytes
  * @offset: difference between base and rm.size
+ * @destroy: flag if region should be destroyed upon shutdown
  */
 
 struct mem_region {
 	u64 base;
 	u64 size;
 	unsigned long offset;
+	int destroy;
 };
 
 /**
@@ -96,7 +97,7 @@
  * The HV virtual address space (vas) allows for hotplug memory regions.
  * Memory regions can be created and destroyed in the vas at runtime.
  * @rm: real mode (bootmem) region
- * @r1: hotplug memory region(s)
+ * @r1: highmem region(s)
  *
  * ps3 addresses
  * virt_addr: a cpu 'translated' effective address
@@ -222,10 +223,6 @@
 	}
 }
 
-/*============================================================================*/
-/* memory hotplug routines                                                    */
-/*============================================================================*/
-
 /**
  * ps3_mm_region_create - create a memory region in the vas
  * @r: pointer to a struct mem_region to accept initialized values
@@ -262,6 +259,7 @@
 		goto zero_region;
 	}
 
+	r->destroy = 1;
 	r->offset = r->base - map.rm.size;
 	return result;
 
@@ -279,7 +277,14 @@
 {
 	int result;
 
+	if (!r->destroy) {
+		pr_info("%s:%d: Not destroying high region: %llxh %llxh\n",
+			__func__, __LINE__, r->base, r->size);
+		return;
+	}
+
 	DBG("%s:%d: r->base = %llxh\n", __func__, __LINE__, r->base);
+
 	if (r->base) {
 		result = lv1_release_memory(r->base);
 		BUG_ON(result);
@@ -288,50 +293,36 @@
 	}
 }
 
-/**
- * ps3_mm_add_memory - hot add memory
- */
-
-static int __init ps3_mm_add_memory(void)
+static int ps3_mm_get_repository_highmem(struct mem_region *r)
 {
 	int result;
-	unsigned long start_addr;
-	unsigned long start_pfn;
-	unsigned long nr_pages;
 
-	if (!firmware_has_feature(FW_FEATURE_PS3_LV1))
-		return -ENODEV;
+	/* Assume a single highmem region. */
 
-	BUG_ON(!mem_init_done);
-
-	start_addr = map.rm.size;
-	start_pfn = start_addr >> PAGE_SHIFT;
-	nr_pages = (map.r1.size + PAGE_SIZE - 1) >> PAGE_SHIFT;
-
-	DBG("%s:%d: start_addr %lxh, start_pfn %lxh, nr_pages %lxh\n",
-		__func__, __LINE__, start_addr, start_pfn, nr_pages);
-
-	result = add_memory(0, start_addr, map.r1.size);
-
-	if (result) {
-		pr_err("%s:%d: add_memory failed: (%d)\n",
-			__func__, __LINE__, result);
-		return result;
-	}
-
-	memblock_add(start_addr, map.r1.size);
-
-	result = online_pages(start_pfn, nr_pages);
+	result = ps3_repository_read_highmem_info(0, &r->base, &r->size);
 
 	if (result)
-		pr_err("%s:%d: online_pages failed: (%d)\n",
-			__func__, __LINE__, result);
+		goto zero_region;
 
+	if (!r->base || !r->size) {
+		result = -1;
+		goto zero_region;
+	}
+
+	r->offset = r->base - map.rm.size;
+
+	DBG("%s:%d: Found high region in repository: %llxh %llxh\n",
+	    __func__, __LINE__, r->base, r->size);
+
+	return 0;
+
+zero_region:
+	DBG("%s:%d: No high region in repository.\n", __func__, __LINE__);
+
+	r->size = r->base = r->offset = 0;
 	return result;
 }
 
-device_initcall(ps3_mm_add_memory);
-
 /*============================================================================*/
 /* dma routines                                                               */
 /*============================================================================*/
@@ -1217,13 +1208,23 @@
 	BUG_ON(map.rm.base);
 	BUG_ON(!map.rm.size);
 
+	/* Check if we got the highmem region from an earlier boot step */
 
-	/* arrange to do this in ps3_mm_add_memory */
-	ps3_mm_region_create(&map.r1, map.total - map.rm.size);
+	if (ps3_mm_get_repository_highmem(&map.r1))
+		ps3_mm_region_create(&map.r1, map.total - map.rm.size);
 
 	/* correct map.total for the real total amount of memory we use */
 	map.total = map.rm.size + map.r1.size;
 
+	if (!map.r1.size) {
+		DBG("%s:%d: No highmem region found\n", __func__, __LINE__);
+	} else {
+		DBG("%s:%d: Adding highmem region: %llxh %llxh\n",
+			__func__, __LINE__, map.rm.size,
+			map.total - map.rm.size);
+		memblock_add(map.rm.size, map.total - map.rm.size);
+	}
+
 	DBG(" <- %s:%d\n", __func__, __LINE__);
 }
 
diff --git a/arch/powerpc/platforms/ps3/platform.h b/arch/powerpc/platforms/ps3/platform.h
index 1a633ed..d71329a 100644
--- a/arch/powerpc/platforms/ps3/platform.h
+++ b/arch/powerpc/platforms/ps3/platform.h
@@ -188,6 +188,22 @@
 int ps3_repository_read_region_total(u64 *region_total);
 int ps3_repository_read_mm_info(u64 *rm_base, u64 *rm_size,
 	u64 *region_total);
+int ps3_repository_read_highmem_region_count(unsigned int *region_count);
+int ps3_repository_read_highmem_base(unsigned int region_index,
+	u64 *highmem_base);
+int ps3_repository_read_highmem_size(unsigned int region_index,
+	u64 *highmem_size);
+int ps3_repository_read_highmem_info(unsigned int region_index,
+	u64 *highmem_base, u64 *highmem_size);
+
+int ps3_repository_write_highmem_region_count(unsigned int region_count);
+int ps3_repository_write_highmem_base(unsigned int region_index,
+	u64 highmem_base);
+int ps3_repository_write_highmem_size(unsigned int region_index,
+	u64 highmem_size);
+int ps3_repository_write_highmem_info(unsigned int region_index,
+	u64 highmem_base, u64 highmem_size);
+int ps3_repository_delete_highmem_info(unsigned int region_index);
 
 /* repository pme info */
 
diff --git a/arch/powerpc/platforms/ps3/repository.c b/arch/powerpc/platforms/ps3/repository.c
index 7bdfea3..9b47ba7 100644
--- a/arch/powerpc/platforms/ps3/repository.c
+++ b/arch/powerpc/platforms/ps3/repository.c
@@ -779,6 +779,72 @@
 }
 
 /**
+ * ps3_repository_read_highmem_region_count - Read the number of highmem regions
+ *
+ * Bootloaders must arrange the repository nodes such that regions are indexed
+ * with a region_index from 0 to region_count-1.
+ */
+
+int ps3_repository_read_highmem_region_count(unsigned int *region_count)
+{
+	int result;
+	u64 v1 = 0;
+
+	result = read_node(PS3_LPAR_ID_CURRENT,
+		make_first_field("highmem", 0),
+		make_field("region", 0),
+		make_field("count", 0),
+		0,
+		&v1, NULL);
+	*region_count = v1;
+	return result;
+}
+
+
+int ps3_repository_read_highmem_base(unsigned int region_index,
+	u64 *highmem_base)
+{
+	return read_node(PS3_LPAR_ID_CURRENT,
+		make_first_field("highmem", 0),
+		make_field("region", region_index),
+		make_field("base", 0),
+		0,
+		highmem_base, NULL);
+}
+
+int ps3_repository_read_highmem_size(unsigned int region_index,
+	u64 *highmem_size)
+{
+	return read_node(PS3_LPAR_ID_CURRENT,
+		make_first_field("highmem", 0),
+		make_field("region", region_index),
+		make_field("size", 0),
+		0,
+		highmem_size, NULL);
+}
+
+/**
+ * ps3_repository_read_highmem_info - Read high memory region info
+ * @region_index: Region index, {0,..,region_count-1}.
+ * @highmem_base: High memory base address.
+ * @highmem_size: High memory size.
+ *
+ * Bootloaders that preallocate highmem regions must place the
+ * region info into the repository at these well known nodes.
+ */
+
+int ps3_repository_read_highmem_info(unsigned int region_index,
+	u64 *highmem_base, u64 *highmem_size)
+{
+	int result;
+
+	*highmem_base = 0;
+	result = ps3_repository_read_highmem_base(region_index, highmem_base);
+	return result ? result
+		: ps3_repository_read_highmem_size(region_index, highmem_size);
+}
+
+/**
  * ps3_repository_read_num_spu_reserved - Number of physical spus reserved.
  * @num_spu: Number of physical spus.
  */
@@ -1002,6 +1068,138 @@
 			    lpar, rights);
 }
 
+#if defined(CONFIG_PS3_REPOSITORY_WRITE)
+
+static int create_node(u64 n1, u64 n2, u64 n3, u64 n4, u64 v1, u64 v2)
+{
+	int result;
+
+	dump_node(0, n1, n2, n3, n4, v1, v2);
+
+	result = lv1_create_repository_node(n1, n2, n3, n4, v1, v2);
+
+	if (result) {
+		pr_devel("%s:%d: lv1_create_repository_node failed: %s\n",
+			__func__, __LINE__, ps3_result(result));
+		return -ENOENT;
+	}
+
+	return 0;
+}
+
+static int delete_node(u64 n1, u64 n2, u64 n3, u64 n4)
+{
+	int result;
+
+	dump_node(0, n1, n2, n3, n4, 0, 0);
+
+	result = lv1_delete_repository_node(n1, n2, n3, n4);
+
+	if (result) {
+		pr_devel("%s:%d: lv1_delete_repository_node failed: %s\n",
+			__func__, __LINE__, ps3_result(result));
+		return -ENOENT;
+	}
+
+	return 0;
+}
+
+static int write_node(u64 n1, u64 n2, u64 n3, u64 n4, u64 v1, u64 v2)
+{
+	int result;
+
+	result = create_node(n1, n2, n3, n4, v1, v2);
+
+	if (!result)
+		return 0;
+
+	result = lv1_write_repository_node(n1, n2, n3, n4, v1, v2);
+
+	if (result) {
+		pr_devel("%s:%d: lv1_write_repository_node failed: %s\n",
+			__func__, __LINE__, ps3_result(result));
+		return -ENOENT;
+	}
+
+	return 0;
+}
+
+int ps3_repository_write_highmem_region_count(unsigned int region_count)
+{
+	int result;
+	u64 v1 = (u64)region_count;
+
+	result = write_node(
+		make_first_field("highmem", 0),
+		make_field("region", 0),
+		make_field("count", 0),
+		0,
+		v1, 0);
+	return result;
+}
+
+int ps3_repository_write_highmem_base(unsigned int region_index,
+	u64 highmem_base)
+{
+	return write_node(
+		make_first_field("highmem", 0),
+		make_field("region", region_index),
+		make_field("base", 0),
+		0,
+		highmem_base, 0);
+}
+
+int ps3_repository_write_highmem_size(unsigned int region_index,
+	u64 highmem_size)
+{
+	return write_node(
+		make_first_field("highmem", 0),
+		make_field("region", region_index),
+		make_field("size", 0),
+		0,
+		highmem_size, 0);
+}
+
+int ps3_repository_write_highmem_info(unsigned int region_index,
+	u64 highmem_base, u64 highmem_size)
+{
+	int result;
+
+	result = ps3_repository_write_highmem_base(region_index, highmem_base);
+	return result ? result
+		: ps3_repository_write_highmem_size(region_index, highmem_size);
+}
+
+static int ps3_repository_delete_highmem_base(unsigned int region_index)
+{
+	return delete_node(
+		make_first_field("highmem", 0),
+		make_field("region", region_index),
+		make_field("base", 0),
+		0);
+}
+
+static int ps3_repository_delete_highmem_size(unsigned int region_index)
+{
+	return delete_node(
+		make_first_field("highmem", 0),
+		make_field("region", region_index),
+		make_field("size", 0),
+		0);
+}
+
+int ps3_repository_delete_highmem_info(unsigned int region_index)
+{
+	int result;
+
+	result = ps3_repository_delete_highmem_base(region_index);
+	result += ps3_repository_delete_highmem_size(region_index);
+
+	return result ? -1 : 0;
+}
+
+#endif /* defined(CONFIG_PS3_WRITE_REPOSITORY) */
+
 #if defined(DEBUG)
 
 int ps3_repository_dump_resource_info(const struct ps3_repository_device *repo)
diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c
index a75e37d..ecd394c 100644
--- a/arch/powerpc/platforms/pseries/eeh.c
+++ b/arch/powerpc/platforms/pseries/eeh.c
@@ -489,7 +489,7 @@
 	 * a stack trace will help the device-driver authors figure
 	 * out what happened.  So print that out.
 	 */
-	dump_stack();
+	WARN(1, "EEH: failure detected\n");
 	return 1;
 
 dn_unlock:
diff --git a/arch/powerpc/platforms/pseries/plpar_wrappers.h b/arch/powerpc/platforms/pseries/plpar_wrappers.h
index 342797f..13e8cc4 100644
--- a/arch/powerpc/platforms/pseries/plpar_wrappers.h
+++ b/arch/powerpc/platforms/pseries/plpar_wrappers.h
@@ -22,12 +22,12 @@
 
 static inline u8 get_cede_latency_hint(void)
 {
-	return get_lppaca()->gpr5_dword.fields.cede_latency_hint;
+	return get_lppaca()->cede_latency_hint;
 }
 
 static inline void set_cede_latency_hint(u8 latency_hint)
 {
-	get_lppaca()->gpr5_dword.fields.cede_latency_hint = latency_hint;
+	get_lppaca()->cede_latency_hint = latency_hint;
 }
 
 static inline long cede_processor(void)
diff --git a/arch/powerpc/platforms/pseries/reconfig.c b/arch/powerpc/platforms/pseries/reconfig.c
index 168651a..7b3bf76 100644
--- a/arch/powerpc/platforms/pseries/reconfig.c
+++ b/arch/powerpc/platforms/pseries/reconfig.c
@@ -103,11 +103,13 @@
 {
 	return blocking_notifier_chain_register(&pSeries_reconfig_chain, nb);
 }
+EXPORT_SYMBOL_GPL(pSeries_reconfig_notifier_register);
 
 void pSeries_reconfig_notifier_unregister(struct notifier_block *nb)
 {
 	blocking_notifier_chain_unregister(&pSeries_reconfig_chain, nb);
 }
+EXPORT_SYMBOL_GPL(pSeries_reconfig_notifier_unregister);
 
 int pSeries_reconfig_notify(unsigned long action, void *p)
 {
@@ -426,6 +428,7 @@
 static int do_update_property(char *buf, size_t bufsize)
 {
 	struct device_node *np;
+	struct pSeries_reconfig_prop_update upd_value;
 	unsigned char *value;
 	char *name, *end, *next_prop;
 	int rc, length;
@@ -454,6 +457,10 @@
 		return -ENODEV;
 	}
 
+	upd_value.node = np;
+	upd_value.property = newprop;
+	pSeries_reconfig_notify(PSERIES_UPDATE_PROPERTY, &upd_value);
+
 	rc = prom_update_property(np, newprop, oldprop);
 	if (rc)
 		return rc;
diff --git a/arch/powerpc/sysdev/ppc4xx_msi.c b/arch/powerpc/sysdev/ppc4xx_msi.c
index 1c2d7af..82c6702 100644
--- a/arch/powerpc/sysdev/ppc4xx_msi.c
+++ b/arch/powerpc/sysdev/ppc4xx_msi.c
@@ -28,10 +28,11 @@
 #include <linux/of_platform.h>
 #include <linux/interrupt.h>
 #include <linux/export.h>
+#include <linux/kernel.h>
 #include <asm/prom.h>
 #include <asm/hw_irq.h>
 #include <asm/ppc-pci.h>
-#include <boot/dcr.h>
+#include <asm/dcr.h>
 #include <asm/dcr-regs.h>
 #include <asm/msi_bitmap.h>
 
@@ -43,13 +44,14 @@
 #define PEIH_FLUSH0	0x30
 #define PEIH_FLUSH1	0x38
 #define PEIH_CNTRST	0x48
-#define NR_MSI_IRQS	4
+
+static int msi_irqs;
 
 struct ppc4xx_msi {
 	u32 msi_addr_lo;
 	u32 msi_addr_hi;
 	void __iomem *msi_regs;
-	int msi_virqs[NR_MSI_IRQS];
+	int *msi_virqs;
 	struct msi_bitmap bitmap;
 	struct device_node *msi_dev;
 };
@@ -61,7 +63,7 @@
 {
 	int err;
 
-	err = msi_bitmap_alloc(&msi_data->bitmap, NR_MSI_IRQS,
+	err = msi_bitmap_alloc(&msi_data->bitmap, msi_irqs,
 			      dev->dev.of_node);
 	if (err)
 		return err;
@@ -83,6 +85,11 @@
 	struct msi_desc *entry;
 	struct ppc4xx_msi *msi_data = &ppc4xx_msi;
 
+	msi_data->msi_virqs = kmalloc((msi_irqs) * sizeof(int),
+					    GFP_KERNEL);
+	if (!msi_data->msi_virqs)
+		return -ENOMEM;
+
 	list_for_each_entry(entry, &dev->msi_list, list) {
 		int_no = msi_bitmap_alloc_hwirqs(&msi_data->bitmap, 1);
 		if (int_no >= 0)
@@ -150,12 +157,11 @@
 	if (!sdr_addr)
 		return -1;
 
-	SDR0_WRITE(sdr_addr, (u64)res.start >> 32);	 /*HIGH addr */
-	SDR0_WRITE(sdr_addr + 1, res.start & 0xFFFFFFFF); /* Low addr */
-
+	mtdcri(SDR0, *sdr_addr, upper_32_bits(res.start));	/*HIGH addr */
+	mtdcri(SDR0, *sdr_addr + 1, lower_32_bits(res.start));	/* Low addr */
 
 	msi->msi_dev = of_find_node_by_name(NULL, "ppc4xx-msi");
-	if (msi->msi_dev)
+	if (!msi->msi_dev)
 		return -ENODEV;
 
 	msi->msi_regs = of_iomap(msi->msi_dev, 0);
@@ -167,9 +173,12 @@
 		(u32) (msi->msi_regs + PEIH_TERMADH), (u32) (msi->msi_regs));
 
 	msi_virt = dma_alloc_coherent(&dev->dev, 64, &msi_phys, GFP_KERNEL);
-	msi->msi_addr_hi = 0x0;
-	msi->msi_addr_lo = (u32) msi_phys;
-	dev_dbg(&dev->dev, "PCIE-MSI: msi address 0x%x\n", msi->msi_addr_lo);
+	if (!msi_virt)
+		return -ENOMEM;
+	msi->msi_addr_hi = upper_32_bits(msi_phys);
+	msi->msi_addr_lo = lower_32_bits(msi_phys & 0xffffffff);
+	dev_dbg(&dev->dev, "PCIE-MSI: msi address high 0x%x, low 0x%x\n",
+		msi->msi_addr_hi, msi->msi_addr_lo);
 
 	/* Progam the Interrupt handler Termination addr registers */
 	out_be32(msi->msi_regs + PEIH_TERMADH, msi->msi_addr_hi);
@@ -185,6 +194,8 @@
 	out_be32(msi->msi_regs + PEIH_MSIED, *msi_data);
 	out_be32(msi->msi_regs + PEIH_MSIMK, *msi_mask);
 
+	dma_free_coherent(&dev->dev, 64, msi_virt, msi_phys);
+
 	return 0;
 }
 
@@ -194,7 +205,7 @@
 	int i;
 	int virq;
 
-	for (i = 0; i < NR_MSI_IRQS; i++) {
+	for (i = 0; i < msi_irqs; i++) {
 		virq = msi->msi_virqs[i];
 		if (virq != NO_IRQ)
 			irq_dispose_mapping(virq);
@@ -215,8 +226,6 @@
 	struct resource res;
 	int err = 0;
 
-	msi = &ppc4xx_msi;/*keep the msi data for further use*/
-
 	dev_dbg(&dev->dev, "PCIE-MSI: Setting up MSI support...\n");
 
 	msi = kzalloc(sizeof(struct ppc4xx_msi), GFP_KERNEL);
@@ -234,6 +243,10 @@
 		goto error_out;
 	}
 
+	msi_irqs = of_irq_count(dev->dev.of_node);
+	if (!msi_irqs)
+		return -ENODEV;
+
 	if (ppc4xx_setup_pcieh_hw(dev, res, msi))
 		goto error_out;
 
@@ -242,6 +255,7 @@
 		dev_err(&dev->dev, "Error allocating MSI bitmap\n");
 		goto error_out;
 	}
+	ppc4xx_msi = *msi;
 
 	ppc_md.setup_msi_irqs = ppc4xx_setup_msi_irqs;
 	ppc_md.teardown_msi_irqs = ppc4xx_teardown_msi_irqs;
diff --git a/arch/s390/include/asm/processor.h b/arch/s390/include/asm/processor.h
index d499b30..6cbf313 100644
--- a/arch/s390/include/asm/processor.h
+++ b/arch/s390/include/asm/processor.h
@@ -141,9 +141,6 @@
 extern void release_thread(struct task_struct *);
 extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
 
-/* Prepare to copy thread state - unlazy all lazy status */
-#define prepare_to_copy(tsk)	do { } while (0)
-
 /*
  * Return saved PC of a blocked thread.
  */
diff --git a/arch/score/include/asm/processor.h b/arch/score/include/asm/processor.h
index 7e22f21..ab3aceb 100644
--- a/arch/score/include/asm/processor.h
+++ b/arch/score/include/asm/processor.h
@@ -26,7 +26,6 @@
 
 #define cpu_relax()		barrier()
 #define release_thread(thread)	do {} while (0)
-#define prepare_to_copy(tsk)	do {} while (0)
 
 /*
  * User space process size: 2GB. This is hardcoded into a few places,
diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig
index 04a8cb4..3e723aa 100644
--- a/arch/sh/Kconfig
+++ b/arch/sh/Kconfig
@@ -155,7 +155,8 @@
 
 config NO_IOPORT
 	def_bool !PCI
-	depends on !SH_CAYMAN && !SH_SH4202_MICRODEV && !SH_SHMIN
+	depends on !SH_CAYMAN && !SH_SH4202_MICRODEV && !SH_SHMIN && \
+		   !SH_HP6XX && !SH_SOLUTION_ENGINE
 
 config IO_TRAPPED
 	bool
@@ -286,6 +287,20 @@
 	select SYS_SUPPORTS_CMT
 	select SYS_SUPPORTS_MTU2
 
+config CPU_SUBTYPE_SH7264
+	bool "Support SH7264 processor"
+	select CPU_SH2A
+	select CPU_HAS_FPU
+	select SYS_SUPPORTS_CMT
+	select SYS_SUPPORTS_MTU2
+
+config CPU_SUBTYPE_SH7269
+	bool "Support SH7269 processor"
+	select CPU_SH2A
+	select CPU_HAS_FPU
+	select SYS_SUPPORTS_CMT
+	select SYS_SUPPORTS_MTU2
+
 config CPU_SUBTYPE_MXG
 	bool "Support MX-G processor"
 	select CPU_SH2A
@@ -425,6 +440,16 @@
 	help
 	  Select SH7724 if you have an SH-MobileR2R CPU.
 
+config CPU_SUBTYPE_SH7734
+	bool "Support SH7734 processor"
+	select CPU_SH4A
+	select CPU_SHX2
+	select ARCH_WANT_OPTIONAL_GPIOLIB
+	select USB_ARCH_HAS_OHCI
+	select USB_ARCH_HAS_EHCI
+	help
+	  Select SH7734 if you have a SH4A SH7734 CPU.
+
 config CPU_SUBTYPE_SH7757
 	bool "Support SH7757 processor"
 	select CPU_SH4A
@@ -582,7 +607,9 @@
 config SH_CLK_CPG_LEGACY
 	depends on SH_CLK_CPG
 	def_bool y if !CPU_SUBTYPE_SH7785 && !ARCH_SHMOBILE && \
-		      !CPU_SHX3 && !CPU_SUBTYPE_SH7757
+		      !CPU_SHX3 && !CPU_SUBTYPE_SH7757 && \
+		      !CPU_SUBTYPE_SH7734 && !CPU_SUBTYPE_SH7264 && \
+		      !CPU_SUBTYPE_SH7269
 
 source "kernel/time/Kconfig"
 
@@ -683,6 +710,20 @@
 
 	  If unsure, say N.
 
+config CC_STACKPROTECTOR
+	bool "Enable -fstack-protector buffer overflow detection (EXPERIMENTAL)"
+	depends on SUPERH32 && EXPERIMENTAL
+	help
+	  This option turns on the -fstack-protector GCC feature. This
+	  feature puts, at the beginning of functions, a canary value on
+	  the stack just before the return address, and validates
+	  the value just before actually returning.  Stack based buffer
+	  overflows (that need to overwrite this return address) now also
+	  overwrite the canary, which gets detected and the attack is then
+	  neutralized via a kernel panic.
+
+	  This feature requires gcc version 4.2 or above.
+
 config SMP
 	bool "Symmetric multi-processing support"
 	depends on SYS_SUPPORTS_SMP
diff --git a/arch/sh/Makefile b/arch/sh/Makefile
index e14a676..46edf07 100644
--- a/arch/sh/Makefile
+++ b/arch/sh/Makefile
@@ -199,6 +199,10 @@
   KBUILD_CFLAGS += -fasynchronous-unwind-tables
 endif
 
+ifeq ($(CONFIG_CC_STACKPROTECTOR),y)
+  KBUILD_CFLAGS += -fstack-protector
+endif
+
 libs-$(CONFIG_SUPERH32)		:= arch/sh/lib/	$(libs-y)
 libs-$(CONFIG_SUPERH64)		:= arch/sh/lib64/ $(libs-y)
 
diff --git a/arch/sh/boards/Kconfig b/arch/sh/boards/Kconfig
index d893411..1f56b35 100644
--- a/arch/sh/boards/Kconfig
+++ b/arch/sh/boards/Kconfig
@@ -54,6 +54,7 @@
 	select SOLUTION_ENGINE
 	depends on CPU_SUBTYPE_SH7724
 	select ARCH_REQUIRE_GPIOLIB
+	select SND_SOC_AK4642 if SND_SIMPLE_CARD
 	help
 	  Select 7724 SolutionEngine if configuring for a Hitachi SH7724
 	  evaluation board.
@@ -133,7 +134,8 @@
 
 config SH_RSK
 	bool "Renesas Starter Kit"
-	depends on CPU_SUBTYPE_SH7201 || CPU_SUBTYPE_SH7203
+	depends on CPU_SUBTYPE_SH7201 || CPU_SUBTYPE_SH7203 || \
+	  CPU_SUBTYPE_SH7264 || CPU_SUBTYPE_SH7269
 	help
 	 Select this option if configuring for any of the RSK+ MCU
 	 evaluation platforms.
@@ -223,6 +225,7 @@
 	bool "EcoVec"
 	depends on CPU_SUBTYPE_SH7724
 	select ARCH_REQUIRE_GPIOLIB
+	select SND_SOC_DA7210 if SND_SIMPLE_CARD
 	help
 	  Renesas "R0P7724LC0011/21RL (EcoVec)" support.
 
@@ -338,8 +341,6 @@
 	help
 	  Select AP-SH4AD-0A if configuring for an ALPHAPROJECT AP-SH4AD-0A.
 
-endmenu
-
 source "arch/sh/boards/mach-r2d/Kconfig"
 source "arch/sh/boards/mach-highlander/Kconfig"
 source "arch/sh/boards/mach-sdk7780/Kconfig"
@@ -359,3 +360,5 @@
 endmenu
 
 endif
+
+endmenu
diff --git a/arch/sh/boards/board-edosk7705.c b/arch/sh/boards/board-edosk7705.c
index 541d8a2..5e24c17 100644
--- a/arch/sh/boards/board-edosk7705.c
+++ b/arch/sh/boards/board-edosk7705.c
@@ -13,6 +13,7 @@
 #include <linux/platform_device.h>
 #include <linux/interrupt.h>
 #include <linux/smc91x.h>
+#include <linux/sh_intc.h>
 #include <asm/machvec.h>
 #include <asm/sizes.h>
 
@@ -20,7 +21,7 @@
 #define SMC_IO_OFFSET	0x300
 #define SMC_IOADDR	(SMC_IOBASE + SMC_IO_OFFSET)
 
-#define ETHERNET_IRQ	0x09
+#define ETHERNET_IRQ	evt2irq(0x320)
 
 static void __init sh_edosk7705_init_irq(void)
 {
@@ -73,6 +74,5 @@
  */
 static struct sh_machine_vector mv_edosk7705 __initmv = {
 	.mv_name		= "EDOSK7705",
-	.mv_nr_irqs		= 80,
 	.mv_init_irq		= sh_edosk7705_init_irq,
 };
diff --git a/arch/sh/boards/board-edosk7760.c b/arch/sh/boards/board-edosk7760.c
index e9656a2..bab5b95 100644
--- a/arch/sh/boards/board-edosk7760.c
+++ b/arch/sh/boards/board-edosk7760.c
@@ -23,6 +23,7 @@
 #include <linux/platform_device.h>
 #include <linux/smc91x.h>
 #include <linux/interrupt.h>
+#include <linux/sh_intc.h>
 #include <linux/i2c.h>
 #include <linux/mtd/physmap.h>
 #include <asm/machvec.h>
@@ -40,8 +41,6 @@
 #define SMC_IO_OFFSET	0x300
 #define SMC_IOADDR	(SMC_IOBASE + SMC_IO_OFFSET)
 
-#define ETHERNET_IRQ	5
-
 /* NOR flash */
 static struct mtd_partition edosk7760_nor_flash_partitions[] = {
 	{
@@ -99,8 +98,8 @@
 		.end	= SH7760_I2C1_MMIOEND,
 		.flags	= IORESOURCE_MEM,
 	},{
-		.start	= SH7760_I2C1_IRQ,
-		.end	= SH7760_I2C1_IRQ,
+		.start	= evt2irq(0x9e0),
+		.end	= evt2irq(0x9e0),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -122,8 +121,8 @@
 		.end	= SH7760_I2C0_MMIOEND,
 		.flags	= IORESOURCE_MEM,
 	}, {
-		.start	= SH7760_I2C0_IRQ,
-		.end	= SH7760_I2C0_IRQ,
+		.start	= evt2irq(0x9c0),
+		.end	= evt2irq(0x9c0),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -150,8 +149,8 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= ETHERNET_IRQ,
-		.end	= ETHERNET_IRQ,
+		.start	= evt2irq(0x2a0),
+		.end	= evt2irq(0x2a0),
 		.flags	= IORESOURCE_IRQ ,
 	}
 };
@@ -189,5 +188,4 @@
  */
 struct sh_machine_vector mv_edosk7760 __initmv = {
 	.mv_name	= "EDOSK7760",
-	.mv_nr_irqs	= 128,
 };
diff --git a/arch/sh/boards/board-espt.c b/arch/sh/boards/board-espt.c
index b3ae9d3..6cba0a7 100644
--- a/arch/sh/boards/board-espt.c
+++ b/arch/sh/boards/board-espt.c
@@ -14,6 +14,7 @@
 #include <linux/mtd/physmap.h>
 #include <linux/io.h>
 #include <linux/sh_eth.h>
+#include <linux/sh_intc.h>
 #include <asm/machvec.h>
 #include <asm/sizes.h>
 
@@ -71,7 +72,7 @@
 		.flags  = IORESOURCE_MEM,
 	}, {
 
-		.start  = 57,   /* irq number */
+		.start  = evt2irq(0x920),   /* irq number */
 		.flags  = IORESOURCE_IRQ,
 	},
 };
diff --git a/arch/sh/boards/board-magicpanelr2.c b/arch/sh/boards/board-magicpanelr2.c
index b2ca1d9..90568f9 100644
--- a/arch/sh/boards/board-magicpanelr2.c
+++ b/arch/sh/boards/board-magicpanelr2.c
@@ -19,6 +19,7 @@
 #include <linux/mtd/partitions.h>
 #include <linux/mtd/physmap.h>
 #include <linux/mtd/map.h>
+#include <linux/sh_intc.h>
 #include <mach/magicpanelr2.h>
 #include <asm/heartbeat.h>
 #include <cpu/sh7720.h>
@@ -245,8 +246,8 @@
 		.flags		= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start		= 35,
-		.end		= 35,
+		.start		= evt2irq(0x660),
+		.end		= evt2irq(0x660),
 		.flags		= IORESOURCE_IRQ,
 	},
 };
@@ -358,17 +359,17 @@
 {
 	plat_irq_setup_pins(IRQ_MODE_IRQ); /* install handlers for IRQ0-5 */
 
-	irq_set_irq_type(32, IRQ_TYPE_LEVEL_LOW);    /* IRQ0 CAN1 */
-	irq_set_irq_type(33, IRQ_TYPE_LEVEL_LOW);    /* IRQ1 CAN2 */
-	irq_set_irq_type(34, IRQ_TYPE_LEVEL_LOW);    /* IRQ2 CAN3 */
-	irq_set_irq_type(35, IRQ_TYPE_LEVEL_LOW);    /* IRQ3 SMSC9115 */
-	irq_set_irq_type(36, IRQ_TYPE_EDGE_RISING);  /* IRQ4 touchscreen */
-	irq_set_irq_type(37, IRQ_TYPE_EDGE_FALLING); /* IRQ5 touchscreen */
+	irq_set_irq_type(evt2irq(0x600), IRQ_TYPE_LEVEL_LOW);    /* IRQ0 CAN1 */
+	irq_set_irq_type(evt2irq(0x620), IRQ_TYPE_LEVEL_LOW);    /* IRQ1 CAN2 */
+	irq_set_irq_type(evt2irq(0x640), IRQ_TYPE_LEVEL_LOW);    /* IRQ2 CAN3 */
+	irq_set_irq_type(evt2irq(0x660), IRQ_TYPE_LEVEL_LOW);    /* IRQ3 SMSC9115 */
+	irq_set_irq_type(evt2irq(0x680), IRQ_TYPE_EDGE_RISING);  /* IRQ4 touchscreen */
+	irq_set_irq_type(evt2irq(0x6a0), IRQ_TYPE_EDGE_FALLING); /* IRQ5 touchscreen */
 
-	intc_set_priority(32, 13);		/* IRQ0 CAN1 */
-	intc_set_priority(33, 13);		/* IRQ0 CAN2 */
-	intc_set_priority(34, 13);		/* IRQ0 CAN3 */
-	intc_set_priority(35, 6);		/* IRQ3 SMSC9115 */
+	intc_set_priority(evt2irq(0x600), 13);		/* IRQ0 CAN1 */
+	intc_set_priority(evt2irq(0x620), 13);		/* IRQ0 CAN2 */
+	intc_set_priority(evt2irq(0x640), 13);		/* IRQ0 CAN3 */
+	intc_set_priority(evt2irq(0x660), 6);		/* IRQ3 SMSC9115 */
 }
 
 /*
diff --git a/arch/sh/boards/board-polaris.c b/arch/sh/boards/board-polaris.c
index 5948663..37d03c0 100644
--- a/arch/sh/boards/board-polaris.c
+++ b/arch/sh/boards/board-polaris.c
@@ -141,6 +141,5 @@
 
 static struct sh_machine_vector mv_polaris __initmv = {
 	.mv_name		= "Polaris",
-	.mv_nr_irqs		= 61,
 	.mv_init_irq		= init_polaris_irq,
 };
diff --git a/arch/sh/boards/board-secureedge5410.c b/arch/sh/boards/board-secureedge5410.c
index 03820c3..98b3620 100644
--- a/arch/sh/boards/board-secureedge5410.c
+++ b/arch/sh/boards/board-secureedge5410.c
@@ -71,6 +71,5 @@
  */
 static struct sh_machine_vector mv_snapgear __initmv = {
 	.mv_name		= "SnapGear SecureEdge5410",
-	.mv_nr_irqs		= 72,
 	.mv_init_irq		= init_snapgear_IRQ,
 };
diff --git a/arch/sh/boards/board-sh7757lcr.c b/arch/sh/boards/board-sh7757lcr.c
index 24b1ee4..5087f8b 100644
--- a/arch/sh/boards/board-sh7757lcr.c
+++ b/arch/sh/boards/board-sh7757lcr.c
@@ -19,6 +19,7 @@
 #include <linux/mmc/sh_mmcif.h>
 #include <linux/mmc/sh_mobile_sdhi.h>
 #include <linux/sh_eth.h>
+#include <linux/sh_intc.h>
 #include <linux/usb/renesas_usbhs.h>
 #include <cpu/sh7757.h>
 #include <asm/heartbeat.h>
@@ -65,8 +66,8 @@
 		.end    = 0xfef001ff,
 		.flags  = IORESOURCE_MEM,
 	}, {
-		.start  = 84,
-		.end    = 84,
+		.start  = evt2irq(0xc80),
+		.end    = evt2irq(0xc80),
 		.flags  = IORESOURCE_IRQ,
 	},
 };
@@ -94,8 +95,8 @@
 		.end    = 0xfef009ff,
 		.flags  = IORESOURCE_MEM,
 	}, {
-		.start  = 84,
-		.end    = 84,
+		.start  = evt2irq(0xc80),
+		.end    = evt2irq(0xc80),
 		.flags  = IORESOURCE_IRQ,
 	},
 };
@@ -139,8 +140,8 @@
 		.end    = 0xfee01fff,
 		.flags  = IORESOURCE_MEM,
 	}, {
-		.start  = 315,
-		.end    = 315,
+		.start  = evt2irq(0x2960),
+		.end    = evt2irq(0x2960),
 		.flags  = IORESOURCE_IRQ,
 	},
 };
@@ -174,8 +175,8 @@
 		.end    = 0xfee01fff,
 		.flags  = IORESOURCE_MEM,
 	}, {
-		.start  = 316,
-		.end    = 316,
+		.start  = evt2irq(0x2980),
+		.end    = evt2irq(0x2980),
 		.flags  = IORESOURCE_IRQ,
 	},
 };
@@ -206,11 +207,11 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 211,
+		.start	= evt2irq(0x1c60),
 		.flags	= IORESOURCE_IRQ,
 	},
 	[2] = {
-		.start	= 212,
+		.start	= evt2irq(0x1c80),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -248,7 +249,7 @@
 		.flags  = IORESOURCE_MEM,
 	},
 	[1] = {
-		.start  = 20,
+		.start  = evt2irq(0x480),
 		.flags  = IORESOURCE_IRQ,
 	},
 };
@@ -284,8 +285,8 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 50,
-		.end	= 50,
+		.start	= evt2irq(0x840),
+		.end	= evt2irq(0x840),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
diff --git a/arch/sh/boards/board-sh7785lcr.c b/arch/sh/boards/board-sh7785lcr.c
index d0d6221..2c4771e 100644
--- a/arch/sh/boards/board-sh7785lcr.c
+++ b/arch/sh/boards/board-sh7785lcr.c
@@ -20,6 +20,7 @@
 #include <linux/i2c-pca-platform.h>
 #include <linux/i2c-algo-pca.h>
 #include <linux/usb/r8a66597.h>
+#include <linux/sh_intc.h>
 #include <linux/irq.h>
 #include <linux/io.h>
 #include <linux/clk.h>
@@ -105,8 +106,8 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 2,
-		.end	= 2,
+		.start	= evt2irq(0x240),
+		.end	= evt2irq(0x240),
 		.flags	= IORESOURCE_IRQ | IRQF_TRIGGER_LOW,
 	},
 };
@@ -135,7 +136,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[2]	= {
-		.start	= 10,
+		.start	= evt2irq(0x340),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -223,8 +224,8 @@
 		.flags	= IORESOURCE_MEM | IORESOURCE_MEM_8BIT,
 	},
 	[1] = {
-		.start	= 12,
-		.end	= 12,
+		.start	= evt2irq(0x380),
+		.end	= evt2irq(0x380),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -236,8 +237,8 @@
 		.flags	= IORESOURCE_MEM | IORESOURCE_MEM_8BIT,
 	},
 	[1] = {
-		.start	= 12,
-		.end	= 12,
+		.start	= evt2irq(0x380),
+		.end	= evt2irq(0x380),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
diff --git a/arch/sh/boards/board-urquell.c b/arch/sh/boards/board-urquell.c
index 24e3316..b52abcc 100644
--- a/arch/sh/boards/board-urquell.c
+++ b/arch/sh/boards/board-urquell.c
@@ -20,6 +20,7 @@
 #include <linux/gpio.h>
 #include <linux/irq.h>
 #include <linux/clk.h>
+#include <linux/sh_intc.h>
 #include <mach/urquell.h>
 #include <cpu/sh7786.h>
 #include <asm/heartbeat.h>
@@ -78,7 +79,7 @@
 		.flags  = IORESOURCE_MEM,
 	},
 	[1] = {
-		.start  = 11,
+		.start  = evt2irq(0x360),
 		.flags  = IORESOURCE_IRQ,
 	},
 };
diff --git a/arch/sh/boards/mach-ap325rxa/setup.c b/arch/sh/boards/mach-ap325rxa/setup.c
index 8cf02e3..f33ebf4 100644
--- a/arch/sh/boards/mach-ap325rxa/setup.c
+++ b/arch/sh/boards/mach-ap325rxa/setup.c
@@ -23,6 +23,7 @@
 #include <linux/smsc911x.h>
 #include <linux/gpio.h>
 #include <linux/videodev2.h>
+#include <linux/sh_intc.h>
 #include <media/ov772x.h>
 #include <media/soc_camera.h>
 #include <media/soc_camera_platform.h>
@@ -47,8 +48,8 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 35,
-		.end	= 35,
+		.start	= evt2irq(0x660),
+		.end	= evt2irq(0x660),
 		.flags	= IORESOURCE_IRQ,
 	}
 };
@@ -166,7 +167,7 @@
 		__raw_writew(0, FPGA_BKLREG);
 		gpio_set_value(GPIO_PTS3, 1);
 	}
-	
+
 	return 0;
 }
 
@@ -236,7 +237,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 28,
+		.start	= evt2irq(0x580),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -404,7 +405,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start  = 52,
+		.start  = evt2irq(0x880),
 		.flags  = IORESOURCE_IRQ,
 	},
 	[2] = {
@@ -430,7 +431,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 100,
+		.start	= evt2irq(0xe80),
 		.flags  = IORESOURCE_IRQ,
 	},
 };
@@ -457,7 +458,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 23,
+		.start	= evt2irq(0x4e0),
 		.flags  = IORESOURCE_IRQ,
 	},
 };
diff --git a/arch/sh/boards/mach-cayman/setup.c b/arch/sh/boards/mach-cayman/setup.c
index e89e8e1..340fd40 100644
--- a/arch/sh/boards/mach-cayman/setup.c
+++ b/arch/sh/boards/mach-cayman/setup.c
@@ -181,7 +181,6 @@
 
 static struct sh_machine_vector mv_cayman __initmv = {
 	.mv_name		= "Hitachi Cayman",
-	.mv_nr_irqs		= 64,
 	.mv_ioport_map		= cayman_ioport_map,
 	.mv_init_irq		= init_cayman_irq,
 };
diff --git a/arch/sh/boards/mach-ecovec24/setup.c b/arch/sh/boards/mach-ecovec24/setup.c
index d12fe9d..4158d70 100644
--- a/arch/sh/boards/mach-ecovec24/setup.c
+++ b/arch/sh/boards/mach-ecovec24/setup.c
@@ -29,9 +29,11 @@
 #include <linux/input.h>
 #include <linux/input/sh_keysc.h>
 #include <linux/sh_eth.h>
+#include <linux/sh_intc.h>
 #include <linux/videodev2.h>
 #include <video/sh_mobile_lcdc.h>
 #include <sound/sh_fsi.h>
+#include <sound/simple_card.h>
 #include <media/sh_mobile_ceu.h>
 #include <media/soc_camera.h>
 #include <media/tw9910.h>
@@ -137,7 +139,7 @@
 		.flags = IORESOURCE_MEM,
 	},
 	[1] = {
-		.start = 91,
+		.start = evt2irq(0xd60),
 		.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
 	},
 };
@@ -178,8 +180,8 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 65,
-		.end	= 65,
+		.start	= evt2irq(0xa20),
+		.end	= evt2irq(0xa20),
 		.flags	= IORESOURCE_IRQ | IRQF_TRIGGER_LOW,
 	},
 };
@@ -214,8 +216,8 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 66,
-		.end	= 66,
+		.start	= evt2irq(0xa40),
+		.end	= evt2irq(0xa40),
 		.flags	= IORESOURCE_IRQ | IRQF_TRIGGER_LOW,
 	},
 };
@@ -261,8 +263,8 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 66,
-		.end	= 66,
+		.start	= evt2irq(0xa40),
+		.end	= evt2irq(0xa40),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -348,7 +350,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 106,
+		.start	= evt2irq(0xf40),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -375,7 +377,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start  = 52,
+		.start  = evt2irq(0x880),
 		.flags  = IORESOURCE_IRQ,
 	},
 	[2] = {
@@ -406,7 +408,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start  = 63,
+		.start  = evt2irq(0x9e0),
 		.flags  = IORESOURCE_IRQ,
 	},
 	[2] = {
@@ -437,7 +439,7 @@
 	},
 	{
 		I2C_BOARD_INFO("lis3lv02d", 0x1c),
-		.irq = 33,
+		.irq = evt2irq(0x620),
 	}
 };
 
@@ -463,7 +465,7 @@
 		.flags  = IORESOURCE_MEM,
 	},
 	[1] = {
-		.start  = 79,
+		.start  = evt2irq(0xbe0),
 		.flags  = IORESOURCE_IRQ,
 	},
 };
@@ -479,7 +481,8 @@
 };
 
 /* TouchScreen */
-#define IRQ0 32
+#define IRQ0 evt2irq(0x600)
+
 static int ts_get_pendown_state(void)
 {
 	int val = 0;
@@ -544,7 +547,7 @@
 		.flags  = IORESOURCE_MEM,
 	},
 	[1] = {
-		.start  = 100,
+		.start  = evt2irq(0xe80),
 		.flags  = IORESOURCE_IRQ,
 	},
 };
@@ -588,7 +591,7 @@
 		.flags  = IORESOURCE_MEM,
 	},
 	[1] = {
-		.start  = 23,
+		.start  = evt2irq(0x4e0),
 		.flags  = IORESOURCE_IRQ,
 	},
 };
@@ -653,7 +656,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 84,
+		.start	= evt2irq(0xc80),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -794,7 +797,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start  = 108,
+		.start  = evt2irq(0xf80),
 		.flags  = IORESOURCE_IRQ,
 	},
 };
@@ -809,6 +812,30 @@
 	},
 };
 
+static struct asoc_simple_dai_init_info fsi_da7210_init_info = {
+	.fmt		= SND_SOC_DAIFMT_I2S,
+	.codec_daifmt	= SND_SOC_DAIFMT_CBM_CFM,
+	.cpu_daifmt	= SND_SOC_DAIFMT_CBS_CFS,
+};
+
+static struct asoc_simple_card_info fsi_da7210_info = {
+	.name		= "DA7210",
+	.card		= "FSIB-DA7210",
+	.cpu_dai	= "fsib-dai",
+	.codec		= "da7210.0-001a",
+	.platform	= "sh_fsi.0",
+	.codec_dai	= "da7210-hifi",
+	.init		= &fsi_da7210_init_info,
+};
+
+static struct platform_device fsi_da7210_device = {
+	.name	= "asoc-simple-card",
+	.dev	= {
+		.platform_data	= &fsi_da7210_info,
+	},
+};
+
+
 /* IrDA */
 static struct resource irda_resources[] = {
 	[0] = {
@@ -818,7 +845,7 @@
 		.flags  = IORESOURCE_MEM,
 	},
 	[1] = {
-		.start  = 20,
+		.start  = evt2irq(0x480),
 		.flags  = IORESOURCE_IRQ,
 	},
 };
@@ -855,7 +882,7 @@
 		.flags  = IORESOURCE_MEM,
 	},
 	[1] = {
-		.start  = 55,
+		.start  = evt2irq(0x8e0),
 		.flags  = IORESOURCE_IRQ,
 	},
 };
@@ -891,12 +918,12 @@
 	},
 	[1] = {
 		/* MMC2I */
-		.start	= 29,
+		.start	= evt2irq(0x5a0),
 		.flags	= IORESOURCE_IRQ,
 	},
 	[2] = {
 		/* MMC3I */
-		.start	= 30,
+		.start	= evt2irq(0x5c0),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -945,6 +972,7 @@
 	&camera_devices[1],
 	&camera_devices[2],
 	&fsi_device,
+	&fsi_da7210_device,
 	&irda_device,
 	&vou_device,
 #if defined(CONFIG_MMC_SH_MMCIF) || defined(CONFIG_MMC_SH_MMCIF_MODULE)
diff --git a/arch/sh/boards/mach-hp6xx/setup.c b/arch/sh/boards/mach-hp6xx/setup.c
index 8c9add5..05797b3 100644
--- a/arch/sh/boards/mach-hp6xx/setup.c
+++ b/arch/sh/boards/mach-hp6xx/setup.c
@@ -13,6 +13,7 @@
 #include <linux/init.h>
 #include <linux/platform_device.h>
 #include <linux/irq.h>
+#include <linux/sh_intc.h>
 #include <sound/sh_dac_audio.h>
 #include <asm/hd64461.h>
 #include <asm/io.h>
@@ -35,7 +36,7 @@
 		.flags = IORESOURCE_MEM,
 	},
 	[2] = {
-		.start = 77,
+		.start = evt2irq(0xba0),
 		.flags = IORESOURCE_IRQ,
 	},
 };
@@ -168,8 +169,6 @@
 static struct sh_machine_vector mv_hp6xx __initmv = {
 	.mv_name = "hp6xx",
 	.mv_setup = hp6xx_setup,
-	/* IRQ's : CPU(64) + CCHIP(16) + FREE_TO_USE(6) */
-	.mv_nr_irqs = HD64461_IRQBASE + HD64461_IRQ_NUM + 6,
 	/* Enable IRQ0 -> IRQ3 in IRQ_MODE */
 	.mv_init_irq = hp6xx_init_irq,
 };
diff --git a/arch/sh/boards/mach-kfr2r09/setup.c b/arch/sh/boards/mach-kfr2r09/setup.c
index d04a55d..158c917 100644
--- a/arch/sh/boards/mach-kfr2r09/setup.c
+++ b/arch/sh/boards/mach-kfr2r09/setup.c
@@ -23,6 +23,7 @@
 #include <linux/i2c.h>
 #include <linux/usb/r8a66597.h>
 #include <linux/videodev2.h>
+#include <linux/sh_intc.h>
 #include <media/rj54n1cb0c.h>
 #include <media/soc_camera.h>
 #include <media/sh_mobile_ceu.h>
@@ -110,7 +111,7 @@
 		.flags  = IORESOURCE_MEM,
 	},
 	[1] = {
-		.start  = 79,
+		.start  = evt2irq(0xbe0),
 		.flags  = IORESOURCE_IRQ,
 	},
 };
@@ -175,7 +176,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 106,
+		.start	= evt2irq(0xf40),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -200,8 +201,8 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 65,
-		.end	= 65,
+		.start	= evtirq(0xa20),
+		.end	= evtirq(0xa20),
 		.flags	= IORESOURCE_IRQ | IRQF_TRIGGER_LOW,
 	},
 };
@@ -230,8 +231,8 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start  = 52,
-		.end  = 52,
+		.start  = evt2irq(0x880),
+		.end	= evt2irq(0x880),
 		.flags  = IORESOURCE_IRQ,
 	},
 	[2] = {
@@ -348,7 +349,7 @@
 		.flags  = IORESOURCE_MEM,
 	},
 	[1] = {
-		.start  = 100,
+		.start  = evt2irq(0xe80),
 		.flags  = IORESOURCE_IRQ,
 	},
 };
diff --git a/arch/sh/boards/mach-lboxre2/setup.c b/arch/sh/boards/mach-lboxre2/setup.c
index 79b4e0d7..6660622 100644
--- a/arch/sh/boards/mach-lboxre2/setup.c
+++ b/arch/sh/boards/mach-lboxre2/setup.c
@@ -79,6 +79,5 @@
  */
 static struct sh_machine_vector mv_lboxre2 __initmv = {
 	.mv_name		= "L-BOX RE2",
-	.mv_nr_irqs		= 72,
 	.mv_init_irq		= init_lboxre2_IRQ,
 };
diff --git a/arch/sh/boards/mach-microdev/setup.c b/arch/sh/boards/mach-microdev/setup.c
index d8a7472..6c66ee4 100644
--- a/arch/sh/boards/mach-microdev/setup.c
+++ b/arch/sh/boards/mach-microdev/setup.c
@@ -194,7 +194,6 @@
  */
 static struct sh_machine_vector mv_sh4202_microdev __initmv = {
 	.mv_name		= "SH4-202 MicroDev",
-	.mv_nr_irqs		= 72,
 	.mv_ioport_map		= microdev_ioport_map,
 	.mv_init_irq		= init_microdev_irq,
 };
diff --git a/arch/sh/boards/mach-migor/setup.c b/arch/sh/boards/mach-migor/setup.c
index ff6f69c..34cd0c5 100644
--- a/arch/sh/boards/mach-migor/setup.c
+++ b/arch/sh/boards/mach-migor/setup.c
@@ -22,6 +22,7 @@
 #include <linux/clk.h>
 #include <linux/gpio.h>
 #include <linux/videodev2.h>
+#include <linux/sh_intc.h>
 #include <video/sh_mobile_lcdc.h>
 #include <media/sh_mobile_ceu.h>
 #include <media/ov772x.h>
@@ -54,7 +55,7 @@
 		.flags  = IORESOURCE_MEM,
 	},
 	[1] = {
-		.start  = 32, /* IRQ0 */
+		.start  = evt2irq(0x600), /* IRQ0 */
 		.flags  = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
 	},
 };
@@ -88,7 +89,7 @@
 		.flags  = IORESOURCE_MEM,
 	},
 	[1] = {
-		.start  = 79,
+		.start  = evt2irq(0xbe0),
 		.flags  = IORESOURCE_IRQ,
 	},
 };
@@ -285,7 +286,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 28,
+		.start	= evt2irq(0x580),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -368,7 +369,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start  = 52,
+		.start  = evt2irq(0x880),
 		.flags  = IORESOURCE_IRQ,
 	},
 	[2] = {
@@ -394,7 +395,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 100,
+		.start	= evt2irq(0xe80),
 		.flags  = IORESOURCE_IRQ,
 	},
 };
@@ -420,7 +421,7 @@
 	},
 	{
 		I2C_BOARD_INFO("migor_ts", 0x51),
-		.irq = 38, /* IRQ6 */
+		.irq = evt2irq(0x6c0), /* IRQ6 */
 	},
 	{
 		I2C_BOARD_INFO("wm8978", 0x1a),
diff --git a/arch/sh/boards/mach-rsk/Kconfig b/arch/sh/boards/mach-rsk/Kconfig
index aeff3b0..458a11f 100644
--- a/arch/sh/boards/mach-rsk/Kconfig
+++ b/arch/sh/boards/mach-rsk/Kconfig
@@ -13,6 +13,16 @@
 	select ARCH_REQUIRE_GPIOLIB
 	depends on CPU_SUBTYPE_SH7203
 
+config SH_RSK7264
+	bool "RSK2+SH7264"
+	select ARCH_REQUIRE_GPIOLIB
+	depends on CPU_SUBTYPE_SH7264
+
+config SH_RSK7269
+	bool "RSK2+SH7269"
+	select ARCH_REQUIRE_GPIOLIB
+	depends on CPU_SUBTYPE_SH7269
+
 endchoice
 
 endif
diff --git a/arch/sh/boards/mach-rsk/Makefile b/arch/sh/boards/mach-rsk/Makefile
index 498da75..6a4e1b5 100644
--- a/arch/sh/boards/mach-rsk/Makefile
+++ b/arch/sh/boards/mach-rsk/Makefile
@@ -1,2 +1,4 @@
 obj-y				:= setup.o
 obj-$(CONFIG_SH_RSK7203)	+= devices-rsk7203.o
+obj-$(CONFIG_SH_RSK7264)	+= devices-rsk7264.o
+obj-$(CONFIG_SH_RSK7269)	+= devices-rsk7269.o
diff --git a/arch/sh/boards/mach-rsk/devices-rsk7264.c b/arch/sh/boards/mach-rsk/devices-rsk7264.c
new file mode 100644
index 0000000..7251e37
--- /dev/null
+++ b/arch/sh/boards/mach-rsk/devices-rsk7264.c
@@ -0,0 +1,58 @@
+/*
+ * RSK+SH7264 Support.
+ *
+ * Copyright (C) 2012 Renesas Electronics Europe
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/smsc911x.h>
+#include <asm/machvec.h>
+#include <asm/io.h>
+
+static struct smsc911x_platform_config smsc911x_config = {
+	.phy_interface	= PHY_INTERFACE_MODE_MII,
+	.irq_polarity	= SMSC911X_IRQ_POLARITY_ACTIVE_LOW,
+	.irq_type	= SMSC911X_IRQ_TYPE_OPEN_DRAIN,
+	.flags		= SMSC911X_USE_16BIT | SMSC911X_SWAP_FIFO,
+};
+
+static struct resource smsc911x_resources[] = {
+	[0] = {
+		.start		= 0x28000000,
+		.end		= 0x280000ff,
+		.flags		= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start		= 65,
+		.end		= 65,
+		.flags		= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device smsc911x_device = {
+	.name		= "smsc911x",
+	.id		= -1,
+	.num_resources	= ARRAY_SIZE(smsc911x_resources),
+	.resource	= smsc911x_resources,
+	.dev		= {
+		.platform_data = &smsc911x_config,
+	},
+};
+
+static struct platform_device *rsk7264_devices[] __initdata = {
+	&smsc911x_device,
+};
+
+static int __init rsk7264_devices_setup(void)
+{
+	return platform_add_devices(rsk7264_devices,
+				    ARRAY_SIZE(rsk7264_devices));
+}
+device_initcall(rsk7264_devices_setup);
diff --git a/arch/sh/boards/mach-rsk/devices-rsk7269.c b/arch/sh/boards/mach-rsk/devices-rsk7269.c
new file mode 100644
index 0000000..4a54459
--- /dev/null
+++ b/arch/sh/boards/mach-rsk/devices-rsk7269.c
@@ -0,0 +1,60 @@
+/*
+ * RSK+SH7269 Support
+ *
+ * Copyright (C) 2012  Renesas Electronics Europe Ltd
+ * Copyright (C) 2012  Phil Edworthy
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/smsc911x.h>
+#include <linux/gpio.h>
+#include <asm/machvec.h>
+#include <asm/io.h>
+
+static struct smsc911x_platform_config smsc911x_config = {
+	.phy_interface	= PHY_INTERFACE_MODE_MII,
+	.irq_polarity	= SMSC911X_IRQ_POLARITY_ACTIVE_LOW,
+	.irq_type	= SMSC911X_IRQ_TYPE_PUSH_PULL,
+	.flags		= SMSC911X_USE_16BIT | SMSC911X_SWAP_FIFO,
+};
+
+static struct resource smsc911x_resources[] = {
+	[0] = {
+		.start		= 0x24000000,
+		.end		= 0x240000ff,
+		.flags		= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start		= 85,
+		.end		= 85,
+		.flags		= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device smsc911x_device = {
+	.name		= "smsc911x",
+	.id		= -1,
+	.num_resources	= ARRAY_SIZE(smsc911x_resources),
+	.resource	= smsc911x_resources,
+	.dev		= {
+		.platform_data = &smsc911x_config,
+	},
+};
+
+static struct platform_device *rsk7269_devices[] __initdata = {
+	&smsc911x_device,
+};
+
+static int __init rsk7269_devices_setup(void)
+{
+	return platform_add_devices(rsk7269_devices,
+				    ARRAY_SIZE(rsk7269_devices));
+}
+device_initcall(rsk7269_devices_setup);
diff --git a/arch/sh/boards/mach-sdk7780/setup.c b/arch/sh/boards/mach-sdk7780/setup.c
index 4da38db..2241659 100644
--- a/arch/sh/boards/mach-sdk7780/setup.c
+++ b/arch/sh/boards/mach-sdk7780/setup.c
@@ -94,7 +94,6 @@
 static struct sh_machine_vector mv_se7780 __initmv = {
 	.mv_name        = "Renesas SDK7780-R3" ,
 	.mv_setup		= sdk7780_setup,
-	.mv_nr_irqs		= 111,
 	.mv_init_irq	= init_sdk7780_IRQ,
 };
 
diff --git a/arch/sh/boards/mach-se/7206/setup.c b/arch/sh/boards/mach-se/7206/setup.c
index 8ab8330..68883ec 100644
--- a/arch/sh/boards/mach-se/7206/setup.c
+++ b/arch/sh/boards/mach-se/7206/setup.c
@@ -90,7 +90,6 @@
 
 static struct sh_machine_vector mv_se __initmv = {
 	.mv_name		= "SolutionEngine",
-	.mv_nr_irqs		= 256,
 	.mv_init_irq		= init_se7206_IRQ,
 	.mv_mode_pins		= se7206_mode_pins,
 };
diff --git a/arch/sh/boards/mach-se/770x/setup.c b/arch/sh/boards/mach-se/770x/setup.c
index 31330c6..9759d6b 100644
--- a/arch/sh/boards/mach-se/770x/setup.c
+++ b/arch/sh/boards/mach-se/770x/setup.c
@@ -184,16 +184,5 @@
 static struct sh_machine_vector mv_se __initmv = {
 	.mv_name		= "SolutionEngine",
 	.mv_setup		= smsc_setup,
-#if defined(CONFIG_CPU_SH4)
-	.mv_nr_irqs		= 48,
-#elif defined(CONFIG_CPU_SUBTYPE_SH7708)
-	.mv_nr_irqs		= 32,
-#elif defined(CONFIG_CPU_SUBTYPE_SH7709)
-	.mv_nr_irqs		= 61,
-#elif defined(CONFIG_CPU_SUBTYPE_SH7705)
-	.mv_nr_irqs		= 86,
-#elif defined(CONFIG_CPU_SUBTYPE_SH7710) || defined(CONFIG_CPU_SUBTYPE_SH7712)
-	.mv_nr_irqs             = 104,
-#endif
 	.mv_init_irq		= init_se_IRQ,
 };
diff --git a/arch/sh/boards/mach-se/7721/setup.c b/arch/sh/boards/mach-se/7721/setup.c
index 7416ad7..a0b3dba 100644
--- a/arch/sh/boards/mach-se/7721/setup.c
+++ b/arch/sh/boards/mach-se/7721/setup.c
@@ -92,6 +92,5 @@
 struct sh_machine_vector mv_se7721 __initmv = {
 	.mv_name		= "Solution Engine 7721",
 	.mv_setup		= se7721_setup,
-	.mv_nr_irqs		= 109,
 	.mv_init_irq		= init_se7721_IRQ,
 };
diff --git a/arch/sh/boards/mach-se/7722/setup.c b/arch/sh/boards/mach-se/7722/setup.c
index e1963fe..8f7f055 100644
--- a/arch/sh/boards/mach-se/7722/setup.c
+++ b/arch/sh/boards/mach-se/7722/setup.c
@@ -16,6 +16,7 @@
 #include <linux/input.h>
 #include <linux/input/sh_keysc.h>
 #include <linux/smc91x.h>
+#include <linux/sh_intc.h>
 #include <mach-se/mach/se7722.h>
 #include <mach-se/mach/mrshpc.h>
 #include <asm/machvec.h>
@@ -114,7 +115,7 @@
 		.flags  = IORESOURCE_MEM,
 	},
 	[1] = {
-		.start  = 79,
+		.start  = evt2irq(0xbe0),
 		.flags  = IORESOURCE_IRQ,
 	},
 };
diff --git a/arch/sh/boards/mach-se/7724/setup.c b/arch/sh/boards/mach-se/7724/setup.c
index c540b16..ffbf5bc 100644
--- a/arch/sh/boards/mach-se/7724/setup.c
+++ b/arch/sh/boards/mach-se/7724/setup.c
@@ -24,10 +24,12 @@
 #include <linux/input/sh_keysc.h>
 #include <linux/usb/r8a66597.h>
 #include <linux/sh_eth.h>
+#include <linux/sh_intc.h>
 #include <linux/videodev2.h>
 #include <video/sh_mobile_lcdc.h>
 #include <media/sh_mobile_ceu.h>
 #include <sound/sh_fsi.h>
+#include <sound/simple_card.h>
 #include <asm/io.h>
 #include <asm/heartbeat.h>
 #include <asm/clock.h>
@@ -197,7 +199,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 106,
+		.start	= evt2irq(0xf40),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -224,7 +226,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start  = 52,
+		.start  = evt2irq(0x880),
 		.flags  = IORESOURCE_IRQ,
 	},
 	[2] = {
@@ -255,7 +257,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start  = 63,
+		.start  = evt2irq(0x9e0),
 		.flags  = IORESOURCE_IRQ,
 	},
 	[2] = {
@@ -289,7 +291,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start  = 108,
+		.start  = evt2irq(0xf80),
 		.flags  = IORESOURCE_IRQ,
 	},
 };
@@ -304,17 +306,25 @@
 	},
 };
 
-static struct fsi_ak4642_info fsi_ak4642_info = {
+static struct asoc_simple_dai_init_info fsi2_ak4642_init_info = {
+	.fmt		= SND_SOC_DAIFMT_LEFT_J,
+	.codec_daifmt	= SND_SOC_DAIFMT_CBM_CFM,
+	.cpu_daifmt	= SND_SOC_DAIFMT_CBS_CFS,
+	.sysclk		= 11289600,
+};
+
+static struct asoc_simple_card_info fsi_ak4642_info = {
 	.name		= "AK4642",
 	.card		= "FSIA-AK4642",
 	.cpu_dai	= "fsia-dai",
 	.codec		= "ak4642-codec.0-0012",
 	.platform	= "sh_fsi.0",
-	.id		= FSI_PORT_A,
+	.codec_dai	= "ak4642-hifi",
+	.init		= &fsi2_ak4642_init_info,
 };
 
 static struct platform_device fsi_ak4642_device = {
-	.name	= "fsi-ak4642-audio",
+	.name	= "asoc-simple-card",
 	.dev	= {
 		.platform_data	= &fsi_ak4642_info,
 	},
@@ -343,7 +353,7 @@
 		.flags  = IORESOURCE_MEM,
 	},
 	[1] = {
-		.start  = 79,
+		.start  = evt2irq(0xbe0),
 		.flags  = IORESOURCE_IRQ,
 	},
 };
@@ -366,7 +376,7 @@
 		.flags = IORESOURCE_MEM,
 	},
 	[1] = {
-		.start = 91,
+		.start = evt2irq(0xd60),
 		.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
 	},
 };
@@ -397,8 +407,8 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 65,
-		.end	= 65,
+		.start	= evt2irq(0xa20),
+		.end	= evt2irq(0xa20),
 		.flags	= IORESOURCE_IRQ | IRQF_TRIGGER_LOW,
 	},
 };
@@ -426,8 +436,8 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 66,
-		.end	= 66,
+		.start	= evt2irq(0xa40),
+		.end	= evt2irq(0xa40),
 		.flags	= IORESOURCE_IRQ | IRQF_TRIGGER_LOW,
 	},
 };
@@ -452,7 +462,7 @@
 		.flags  = IORESOURCE_MEM,
 	},
 	[1] = {
-		.start  = 100,
+		.start  = evt2irq(0xe80),
 		.flags  = IORESOURCE_IRQ,
 	},
 };
@@ -481,7 +491,7 @@
 		.flags  = IORESOURCE_MEM,
 	},
 	[1] = {
-		.start  = 23,
+		.start  = evt2irq(0x4e0),
 		.flags  = IORESOURCE_IRQ,
 	},
 };
@@ -511,7 +521,7 @@
 		.flags  = IORESOURCE_MEM,
 	},
 	[1] = {
-		.start  = 20,
+		.start  = evt2irq(0x480),
 		.flags  = IORESOURCE_IRQ,
 	},
 };
@@ -549,7 +559,7 @@
 		.flags  = IORESOURCE_MEM,
 	},
 	[1] = {
-		.start  = 55,
+		.start  = evt2irq(0x8e0),
 		.flags  = IORESOURCE_IRQ,
 	},
 };
@@ -595,6 +605,7 @@
 #define EEPROM_DATA 0xBA20600C
 #define EEPROM_STAT 0xBA206010
 #define EEPROM_STRT 0xBA206014
+
 static int __init sh_eth_is_eeprom_ready(void)
 {
 	int t = 10000;
@@ -651,7 +662,6 @@
 extern char ms7724se_sdram_leave_start;
 extern char ms7724se_sdram_leave_end;
 
-
 static int __init arch_setup(void)
 {
 	/* enable I2C device */
@@ -928,5 +938,4 @@
 static struct sh_machine_vector mv_ms7724se __initmv = {
 	.mv_name	= "ms7724se",
 	.mv_init_irq	= init_se7724_IRQ,
-	.mv_nr_irqs	= SE7724_FPGA_IRQ_BASE + SE7724_FPGA_IRQ_NR,
 };
diff --git a/arch/sh/boards/mach-se/7751/setup.c b/arch/sh/boards/mach-se/7751/setup.c
index 4ed60c5..820f4e7 100644
--- a/arch/sh/boards/mach-se/7751/setup.c
+++ b/arch/sh/boards/mach-se/7751/setup.c
@@ -55,6 +55,5 @@
  */
 static struct sh_machine_vector mv_7751se __initmv = {
 	.mv_name		= "7751 SolutionEngine",
-	.mv_nr_irqs		= 72,
 	.mv_init_irq		= init_7751se_IRQ,
 };
diff --git a/arch/sh/boards/mach-se/7780/setup.c b/arch/sh/boards/mach-se/7780/setup.c
index 6f7c207..ae5a1d8 100644
--- a/arch/sh/boards/mach-se/7780/setup.c
+++ b/arch/sh/boards/mach-se/7780/setup.c
@@ -110,6 +110,5 @@
 static struct sh_machine_vector mv_se7780 __initmv = {
 	.mv_name                = "Solution Engine 7780" ,
 	.mv_setup               = se7780_setup ,
-	.mv_nr_irqs		= 111 ,
 	.mv_init_irq		= init_se7780_IRQ,
 };
diff --git a/arch/sh/boards/mach-se/board-se7619.c b/arch/sh/boards/mach-se/board-se7619.c
index 82b6d4a..958bcd7 100644
--- a/arch/sh/boards/mach-se/board-se7619.c
+++ b/arch/sh/boards/mach-se/board-se7619.c
@@ -22,6 +22,5 @@
 
 static struct sh_machine_vector mv_se __initmv = {
 	.mv_name		= "SolutionEngine",
-	.mv_nr_irqs		= 108,
 	.mv_mode_pins		= se7619_mode_pins,
 };
diff --git a/arch/sh/boards/mach-sh03/setup.c b/arch/sh/boards/mach-sh03/setup.c
index d4f79b2..f582dab 100644
--- a/arch/sh/boards/mach-sh03/setup.c
+++ b/arch/sh/boards/mach-sh03/setup.c
@@ -101,6 +101,5 @@
 static struct sh_machine_vector mv_sh03 __initmv = {
 	.mv_name		= "Interface (CTP/PCI-SH03)",
 	.mv_setup		= sh03_setup,
-	.mv_nr_irqs		= 48,
 	.mv_init_irq		= init_sh03_IRQ,
 };
diff --git a/arch/sh/boards/mach-sh7763rdp/setup.c b/arch/sh/boards/mach-sh7763rdp/setup.c
index dd036f1..b7c7529 100644
--- a/arch/sh/boards/mach-sh7763rdp/setup.c
+++ b/arch/sh/boards/mach-sh7763rdp/setup.c
@@ -18,6 +18,7 @@
 #include <linux/fb.h>
 #include <linux/io.h>
 #include <linux/sh_eth.h>
+#include <linux/sh_intc.h>
 #include <mach/sh7763rdp.h>
 #include <asm/sh7760fb.h>
 
@@ -67,7 +68,7 @@
  * SH-Ether
  *
  * SH Ether of SH7763 has multi IRQ handling.
- * (57,58,59 -> 57)
+ * (0x920,0x940,0x960 -> 0x920)
  */
 static struct resource sh_eth_resources[] = {
 	{
@@ -79,7 +80,7 @@
 		.end    = 0xFEE01FFF,
 		.flags  = IORESOURCE_MEM,
 	}, {
-		.start  = 57,   /* irq number */
+		.start  = evt2irq(0x920),   /* irq number */
 		.flags  = IORESOURCE_IRQ,
 	},
 };
@@ -213,6 +214,5 @@
 static struct sh_machine_vector mv_sh7763rdp __initmv = {
 	.mv_name = "sh7763drp",
 	.mv_setup = sh7763rdp_setup,
-	.mv_nr_irqs = 112,
 	.mv_init_irq = init_sh7763rdp_IRQ,
 };
diff --git a/arch/sh/configs/rsk7264_defconfig b/arch/sh/configs/rsk7264_defconfig
new file mode 100644
index 0000000..1600426
--- /dev/null
+++ b/arch/sh/configs/rsk7264_defconfig
@@ -0,0 +1,80 @@
+CONFIG_LOCALVERSION="uClinux RSK2+SH7264"
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_SYSVIPC=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_IKCONFIG=y
+CONFIG_LOG_BUF_SHIFT=14
+CONFIG_NAMESPACES=y
+CONFIG_SYSFS_DEPRECATED=y
+CONFIG_SYSFS_DEPRECATED_V2=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_KALLSYMS_ALL=y
+CONFIG_EMBEDDED=y
+CONFIG_PERF_COUNTERS=y
+# CONFIG_VM_EVENT_COUNTERS is not set
+CONFIG_SLAB=y
+CONFIG_MMAP_ALLOW_UNINITIALIZED=y
+CONFIG_PROFILING=y
+# CONFIG_LBDAF is not set
+# CONFIG_BLK_DEV_BSG is not set
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_IOSCHED_DEADLINE is not set
+# CONFIG_IOSCHED_CFQ is not set
+CONFIG_CPU_SUBTYPE_SH7264=y
+CONFIG_MEMORY_START=0x0c000000
+CONFIG_FLATMEM_MANUAL=y
+CONFIG_CPU_BIG_ENDIAN=y
+CONFIG_SH_RSK=y
+# CONFIG_SH_TIMER_MTU2 is not set
+CONFIG_BINFMT_FLAT=y
+CONFIG_NET=y
+CONFIG_INET=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
+# CONFIG_INET_DIAG is not set
+# CONFIG_IPV6 is not set
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+# CONFIG_PREVENT_FIRMWARE_BUILD is not set
+# CONFIG_FW_LOADER is not set
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=4
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_NETDEVICES=y
+CONFIG_SMSC911X=y
+CONFIG_SMSC_PHY=y
+CONFIG_INPUT_FF_MEMLESS=y
+# CONFIG_INPUT_MOUSEDEV is not set
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_SERIO is not set
+CONFIG_VT_HW_CONSOLE_BINDING=y
+CONFIG_SERIAL_SH_SCI=y
+CONFIG_SERIAL_SH_SCI_NR_UARTS=8
+CONFIG_SERIAL_SH_SCI_CONSOLE=y
+# CONFIG_HWMON is not set
+CONFIG_USB=y
+CONFIG_USB_DEBUG=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+# CONFIG_USB_DEVICE_CLASS is not set
+CONFIG_USB_R8A66597_HCD=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_STORAGE_DEBUG=y
+CONFIG_USB_LIBUSUAL=y
+CONFIG_EXT2_FS=y
+CONFIG_EXT3_FS=y
+# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
+CONFIG_VFAT_FS=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+CONFIG_ROOT_NFS=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ISO8859_1=y
+# CONFIG_ENABLE_MUST_CHECK is not set
+# CONFIG_FTRACE is not set
diff --git a/arch/sh/configs/rsk7269_defconfig b/arch/sh/configs/rsk7269_defconfig
new file mode 100644
index 0000000..9f062b5
--- /dev/null
+++ b/arch/sh/configs/rsk7269_defconfig
@@ -0,0 +1,65 @@
+CONFIG_LOG_BUF_SHIFT=14
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_EMBEDDED=y
+# CONFIG_VM_EVENT_COUNTERS is not set
+CONFIG_SLAB=y
+# CONFIG_LBDAF is not set
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_IOSCHED_DEADLINE is not set
+# CONFIG_IOSCHED_CFQ is not set
+CONFIG_SWAP_IO_SPACE=y
+CONFIG_CPU_SUBTYPE_SH7269=y
+CONFIG_MEMORY_START=0x0c000000
+CONFIG_MEMORY_SIZE=0x02000000
+CONFIG_FLATMEM_MANUAL=y
+CONFIG_CPU_BIG_ENDIAN=y
+CONFIG_SH_RSK=y
+# CONFIG_SH_TIMER_MTU2 is not set
+CONFIG_SH_PCLK_FREQ=66700000
+CONFIG_BINFMT_FLAT=y
+CONFIG_NET=y
+CONFIG_INET=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
+# CONFIG_INET_DIAG is not set
+# CONFIG_IPV6 is not set
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+# CONFIG_FW_LOADER is not set
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_NETDEVICES=y
+CONFIG_SMSC911X=y
+CONFIG_SMSC_PHY=y
+# CONFIG_INPUT_MOUSEDEV is not set
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_SERIO is not set
+CONFIG_SERIAL_SH_SCI=y
+CONFIG_SERIAL_SH_SCI_NR_UARTS=8
+CONFIG_SERIAL_SH_SCI_CONSOLE=y
+# CONFIG_HWMON is not set
+CONFIG_USB=y
+CONFIG_USB_DEBUG=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+# CONFIG_USB_DEVICE_CLASS is not set
+CONFIG_USB_R8A66597_HCD=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_STORAGE_DEBUG=y
+CONFIG_USB_LIBUSUAL=y
+CONFIG_EXT2_FS=y
+CONFIG_EXT3_FS=y
+# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
+CONFIG_VFAT_FS=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+CONFIG_ROOT_NFS=y
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ISO8859_1=y
+# CONFIG_ENABLE_MUST_CHECK is not set
+CONFIG_SYSCTL_SYSCALL_CHECK=y
+# CONFIG_FTRACE is not set
diff --git a/arch/sh/drivers/dma/Kconfig b/arch/sh/drivers/dma/Kconfig
index 4d58eb0..cfd5b90 100644
--- a/arch/sh/drivers/dma/Kconfig
+++ b/arch/sh/drivers/dma/Kconfig
@@ -40,23 +40,6 @@
 	  DMAC supports. This will be 4 for SH7750/SH7751/Sh7750S/SH7091 and 8 for the
 	  SH7750R/SH7751R/SH7760, 12 for the SH7723/SH7780/SH7785/SH7724, default is 6.
 
-config NR_DMA_CHANNELS_BOOL
-	depends on SH_DMA
-	bool "Override default number of maximum DMA channels"
-	help
-	  This allows you to forcibly update the maximum number of supported
-	  DMA channels for a given board. If this is unset, this will default
-	  to the number of channels that the on-chip DMAC has.
-
-config NR_DMA_CHANNELS
-	int "Maximum number of DMA channels"
-	depends on SH_DMA && NR_DMA_CHANNELS_BOOL
-	default NR_ONCHIP_DMA_CHANNELS
-	help
-	  This allows you to specify the maximum number of DMA channels to
-	  support. Setting this to a higher value allows for cascading DMACs
-	  with additional channels.
-
 config SH_DMABRG
 	bool "SH7760 DMABRG support"
 	depends on CPU_SUBTYPE_SH7760
diff --git a/arch/sh/drivers/dma/dma-sh.c b/arch/sh/drivers/dma/dma-sh.c
index a60da6d..4c171f1 100644
--- a/arch/sh/drivers/dma/dma-sh.c
+++ b/arch/sh/drivers/dma/dma-sh.c
@@ -14,35 +14,72 @@
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/module.h>
+#include <linux/io.h>
 #include <mach-dreamcast/mach/dma.h>
 #include <asm/dma.h>
-#include <asm/io.h>
-#include <asm/dma-sh.h>
+#include <asm/dma-register.h>
+#include <cpu/dma-register.h>
+#include <cpu/dma.h>
 
-#if defined(DMAE1_IRQ)
-#define NR_DMAE		2
-#else
-#define NR_DMAE		1
+/*
+ * Define the default configuration for dual address memory-memory transfer.
+ * The 0x400 value represents auto-request, external->external.
+ */
+#define RS_DUAL	(DM_INC | SM_INC | 0x400 | TS_INDEX2VAL(XMIT_SZ_32BIT))
+
+static unsigned long dma_find_base(unsigned int chan)
+{
+	unsigned long base = SH_DMAC_BASE0;
+
+#ifdef SH_DMAC_BASE1
+	if (chan >= 6)
+		base = SH_DMAC_BASE1;
 #endif
 
-static const char *dmae_name[] = {
-	"DMAC Address Error0", "DMAC Address Error1"
+	return base;
+}
+
+static unsigned long dma_base_addr(unsigned int chan)
+{
+	unsigned long base = dma_find_base(chan);
+
+	/* Normalize offset calculation */
+	if (chan >= 9)
+		chan -= 6;
+	if (chan >= 4)
+		base += 0x10;
+
+	return base + (chan * 0x10);
+}
+
+#ifdef CONFIG_SH_DMA_IRQ_MULTI
+static inline unsigned int get_dmte_irq(unsigned int chan)
+{
+	return chan >= 6 ? DMTE6_IRQ : DMTE0_IRQ;
+}
+#else
+
+static unsigned int dmte_irq_map[] = {
+	DMTE0_IRQ, DMTE0_IRQ + 1, DMTE0_IRQ + 2, DMTE0_IRQ + 3,
+
+#ifdef DMTE4_IRQ
+	DMTE4_IRQ, DMTE4_IRQ + 1,
+#endif
+
+#ifdef DMTE6_IRQ
+	DMTE6_IRQ, DMTE6_IRQ + 1,
+#endif
+
+#ifdef DMTE8_IRQ
+	DMTE8_IRQ, DMTE9_IRQ, DMTE10_IRQ, DMTE11_IRQ,
+#endif
 };
 
 static inline unsigned int get_dmte_irq(unsigned int chan)
 {
-	unsigned int irq = 0;
-	if (chan < ARRAY_SIZE(dmte_irq_map))
-		irq = dmte_irq_map[chan];
-
-#if defined(CONFIG_SH_DMA_IRQ_MULTI)
-	if (irq > DMTE6_IRQ)
-		return DMTE6_IRQ;
-	return DMTE0_IRQ;
-#else
-	return irq;
-#endif
+	return dmte_irq_map[chan];
 }
+#endif
 
 /*
  * We determine the correct shift size based off of the CHCR transmit size
@@ -53,9 +90,10 @@
  * iterations to complete the transfer.
  */
 static unsigned int ts_shift[] = TS_SHIFT;
+
 static inline unsigned int calc_xmit_shift(struct dma_channel *chan)
 {
-	u32 chcr = __raw_readl(dma_base_addr[chan->chan] + CHCR);
+	u32 chcr = __raw_readl(dma_base_addr(chan->chan) + CHCR);
 	int cnt = ((chcr & CHCR_TS_LOW_MASK) >> CHCR_TS_LOW_SHIFT) |
 		((chcr & CHCR_TS_HIGH_MASK) >> CHCR_TS_HIGH_SHIFT);
 
@@ -73,13 +111,13 @@
 	struct dma_channel *chan = dev_id;
 	u32 chcr;
 
-	chcr = __raw_readl(dma_base_addr[chan->chan] + CHCR);
+	chcr = __raw_readl(dma_base_addr(chan->chan) + CHCR);
 
 	if (!(chcr & CHCR_TE))
 		return IRQ_NONE;
 
 	chcr &= ~(CHCR_IE | CHCR_DE);
-	__raw_writel(chcr, (dma_base_addr[chan->chan] + CHCR));
+	__raw_writel(chcr, (dma_base_addr(chan->chan) + CHCR));
 
 	wake_up(&chan->wait_queue);
 
@@ -91,13 +129,8 @@
 	if (unlikely(!(chan->flags & DMA_TEI_CAPABLE)))
 		return 0;
 
-	return request_irq(get_dmte_irq(chan->chan), dma_tei,
-#if defined(CONFIG_SH_DMA_IRQ_MULTI)
-				IRQF_SHARED,
-#else
-				0,
-#endif
-				chan->dev_id, chan);
+	return request_irq(get_dmte_irq(chan->chan), dma_tei, IRQF_SHARED,
+			   chan->dev_id, chan);
 }
 
 static void sh_dmac_free_dma(struct dma_channel *chan)
@@ -118,7 +151,7 @@
 		chan->flags &= ~DMA_TEI_CAPABLE;
 	}
 
-	__raw_writel(chcr, (dma_base_addr[chan->chan] + CHCR));
+	__raw_writel(chcr, (dma_base_addr(chan->chan) + CHCR));
 
 	chan->flags |= DMA_CONFIGURED;
 	return 0;
@@ -129,13 +162,13 @@
 	int irq;
 	u32 chcr;
 
-	chcr = __raw_readl(dma_base_addr[chan->chan] + CHCR);
+	chcr = __raw_readl(dma_base_addr(chan->chan) + CHCR);
 	chcr |= CHCR_DE;
 
 	if (chan->flags & DMA_TEI_CAPABLE)
 		chcr |= CHCR_IE;
 
-	__raw_writel(chcr, (dma_base_addr[chan->chan] + CHCR));
+	__raw_writel(chcr, (dma_base_addr(chan->chan) + CHCR));
 
 	if (chan->flags & DMA_TEI_CAPABLE) {
 		irq = get_dmte_irq(chan->chan);
@@ -153,9 +186,9 @@
 		disable_irq(irq);
 	}
 
-	chcr = __raw_readl(dma_base_addr[chan->chan] + CHCR);
+	chcr = __raw_readl(dma_base_addr(chan->chan) + CHCR);
 	chcr &= ~(CHCR_DE | CHCR_TE | CHCR_IE);
-	__raw_writel(chcr, (dma_base_addr[chan->chan] + CHCR));
+	__raw_writel(chcr, (dma_base_addr(chan->chan) + CHCR));
 }
 
 static int sh_dmac_xfer_dma(struct dma_channel *chan)
@@ -186,13 +219,13 @@
 	 */
 	if (chan->sar || (mach_is_dreamcast() &&
 			  chan->chan == PVR2_CASCADE_CHAN))
-		__raw_writel(chan->sar, (dma_base_addr[chan->chan]+SAR));
+		__raw_writel(chan->sar, (dma_base_addr(chan->chan) + SAR));
 	if (chan->dar || (mach_is_dreamcast() &&
 			  chan->chan == PVR2_CASCADE_CHAN))
-		__raw_writel(chan->dar, (dma_base_addr[chan->chan] + DAR));
+		__raw_writel(chan->dar, (dma_base_addr(chan->chan) + DAR));
 
 	__raw_writel(chan->count >> calc_xmit_shift(chan),
-		(dma_base_addr[chan->chan] + TCR));
+		(dma_base_addr(chan->chan) + TCR));
 
 	sh_dmac_enable_dma(chan);
 
@@ -201,13 +234,32 @@
 
 static int sh_dmac_get_dma_residue(struct dma_channel *chan)
 {
-	if (!(__raw_readl(dma_base_addr[chan->chan] + CHCR) & CHCR_DE))
+	if (!(__raw_readl(dma_base_addr(chan->chan) + CHCR) & CHCR_DE))
 		return 0;
 
-	return __raw_readl(dma_base_addr[chan->chan] + TCR)
+	return __raw_readl(dma_base_addr(chan->chan) + TCR)
 		 << calc_xmit_shift(chan);
 }
 
+/*
+ * DMAOR handling
+ */
+#if defined(CONFIG_CPU_SUBTYPE_SH7723)	|| \
+    defined(CONFIG_CPU_SUBTYPE_SH7724)	|| \
+    defined(CONFIG_CPU_SUBTYPE_SH7780)	|| \
+    defined(CONFIG_CPU_SUBTYPE_SH7785)
+#define NR_DMAOR	2
+#else
+#define NR_DMAOR	1
+#endif
+
+/*
+ * DMAOR bases are broken out amongst channel groups. DMAOR0 manages
+ * channels 0 - 5, DMAOR1 6 - 11 (optional).
+ */
+#define dmaor_read_reg(n)		__raw_readw(dma_find_base((n)*6))
+#define dmaor_write_reg(n, data)	__raw_writew(data, dma_find_base(n)*6)
+
 static inline int dmaor_reset(int no)
 {
 	unsigned long dmaor = dmaor_read_reg(no);
@@ -228,36 +280,86 @@
 	return 0;
 }
 
-#if defined(CONFIG_CPU_SH4)
+/*
+ * DMAE handling
+ */
+#ifdef CONFIG_CPU_SH4
+
+#if defined(DMAE1_IRQ)
+#define NR_DMAE		2
+#else
+#define NR_DMAE		1
+#endif
+
+static const char *dmae_name[] = {
+	"DMAC Address Error0",
+	"DMAC Address Error1"
+};
+
+#ifdef CONFIG_SH_DMA_IRQ_MULTI
+static inline unsigned int get_dma_error_irq(int n)
+{
+	return get_dmte_irq(n * 6);
+}
+#else
+
+static unsigned int dmae_irq_map[] = {
+	DMAE0_IRQ,
+
+#ifdef DMAE1_IRQ
+	DMAE1_IRQ,
+#endif
+};
+
+static inline unsigned int get_dma_error_irq(int n)
+{
+	return dmae_irq_map[n];
+}
+#endif
+
 static irqreturn_t dma_err(int irq, void *dummy)
 {
-#if defined(CONFIG_SH_DMA_IRQ_MULTI)
-	int cnt = 0;
-	switch (irq) {
-#if defined(DMTE6_IRQ) && defined(DMAE1_IRQ)
-	case DMTE6_IRQ:
-		cnt++;
-#endif
-	case DMTE0_IRQ:
-		if (dmaor_read_reg(cnt) & (DMAOR_NMIF | DMAOR_AE)) {
-			disable_irq(irq);
-			/* DMA multi and error IRQ */
-			return IRQ_HANDLED;
-		}
-	default:
-		return IRQ_NONE;
-	}
-#else
-	dmaor_reset(0);
-#if defined(CONFIG_CPU_SUBTYPE_SH7723)	|| \
-		defined(CONFIG_CPU_SUBTYPE_SH7780)	|| \
-		defined(CONFIG_CPU_SUBTYPE_SH7785)
-	dmaor_reset(1);
-#endif
+	int i;
+
+	for (i = 0; i < NR_DMAOR; i++)
+		dmaor_reset(i);
+
 	disable_irq(irq);
 
 	return IRQ_HANDLED;
-#endif
+}
+
+static int dmae_irq_init(void)
+{
+	int n;
+
+	for (n = 0; n < NR_DMAE; n++) {
+		int i = request_irq(get_dma_error_irq(n), dma_err,
+				    IRQF_SHARED, dmae_name[n], NULL);
+		if (unlikely(i < 0)) {
+			printk(KERN_ERR "%s request_irq fail\n", dmae_name[n]);
+			return i;
+		}
+	}
+
+	return 0;
+}
+
+static void dmae_irq_free(void)
+{
+	int n;
+
+	for (n = 0; n < NR_DMAE; n++)
+		free_irq(get_dma_error_irq(n), NULL);
+}
+#else
+static inline int dmae_irq_init(void)
+{
+	return 0;
+}
+
+static void dmae_irq_free(void)
+{
 }
 #endif
 
@@ -276,72 +378,34 @@
 	.flags		= DMAC_CHANNELS_TEI_CAPABLE,
 };
 
-#ifdef CONFIG_CPU_SH4
-static unsigned int get_dma_error_irq(int n)
-{
-#if defined(CONFIG_SH_DMA_IRQ_MULTI)
-	return (n == 0) ? get_dmte_irq(0) : get_dmte_irq(6);
-#else
-	return (n == 0) ? DMAE0_IRQ :
-#if defined(DMAE1_IRQ)
-				DMAE1_IRQ;
-#else
-				-1;
-#endif
-#endif
-}
-#endif
-
 static int __init sh_dmac_init(void)
 {
 	struct dma_info *info = &sh_dmac_info;
-	int i;
+	int i, rc;
 
-#ifdef CONFIG_CPU_SH4
-	int n;
-
-	for (n = 0; n < NR_DMAE; n++) {
-		i = request_irq(get_dma_error_irq(n), dma_err,
-#if defined(CONFIG_SH_DMA_IRQ_MULTI)
-				IRQF_SHARED,
-#else
-				0,
-#endif
-				dmae_name[n], (void *)dmae_name[n]);
-		if (unlikely(i < 0)) {
-			printk(KERN_ERR "%s request_irq fail\n", dmae_name[n]);
-			return i;
-		}
-	}
-#endif /* CONFIG_CPU_SH4 */
+	/*
+	 * Initialize DMAE, for parts that support it.
+	 */
+	rc = dmae_irq_init();
+	if (unlikely(rc != 0))
+		return rc;
 
 	/*
 	 * Initialize DMAOR, and clean up any error flags that may have
 	 * been set.
 	 */
-	i = dmaor_reset(0);
-	if (unlikely(i != 0))
-		return i;
-#if defined(CONFIG_CPU_SUBTYPE_SH7723)	|| \
-		defined(CONFIG_CPU_SUBTYPE_SH7780)	|| \
-		defined(CONFIG_CPU_SUBTYPE_SH7785)
-	i = dmaor_reset(1);
-	if (unlikely(i != 0))
-		return i;
-#endif
+	for (i = 0; i < NR_DMAOR; i++) {
+		rc = dmaor_reset(i);
+		if (unlikely(rc != 0))
+			return rc;
+	}
 
 	return register_dmac(info);
 }
 
 static void __exit sh_dmac_exit(void)
 {
-#ifdef CONFIG_CPU_SH4
-	int n;
-
-	for (n = 0; n < NR_DMAE; n++) {
-		free_irq(get_dma_error_irq(n), (void *)dmae_name[n]);
-	}
-#endif /* CONFIG_CPU_SH4 */
+	dmae_irq_free();
 	unregister_dmac(&sh_dmac_info);
 }
 
diff --git a/arch/sh/drivers/dma/dma-sysfs.c b/arch/sh/drivers/dma/dma-sysfs.c
index 67ee956..4b15fed 100644
--- a/arch/sh/drivers/dma/dma-sysfs.c
+++ b/arch/sh/drivers/dma/dma-sysfs.c
@@ -29,7 +29,7 @@
 	ssize_t len = 0;
 	int i;
 
-	for (i = 0; i < MAX_DMA_CHANNELS; i++) {
+	for (i = 0; i < 16; i++) {
 		struct dma_info *info = get_dma_info(i);
 		struct dma_channel *channel = get_dma_channel(i);
 
diff --git a/arch/sh/drivers/pci/fixups-landisk.c b/arch/sh/drivers/pci/fixups-landisk.c
index ecb1d10..db5b40a 100644
--- a/arch/sh/drivers/pci/fixups-landisk.c
+++ b/arch/sh/drivers/pci/fixups-landisk.c
@@ -14,6 +14,7 @@
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/pci.h>
+#include <linux/sh_intc.h>
 #include "pci-sh4.h"
 
 #define PCIMCR_MRSET_OFF	0xBFFFFFFF
@@ -27,7 +28,7 @@
 	 * slot2: pin1-4 = irq7,8,5,6
 	 * slot3: pin1-4 = irq8,5,6,7
 	 */
-	int irq = ((slot + pin - 1) & 0x3) + 5;
+	int irq = ((slot + pin - 1) & 0x3) + evt2irq(0x2a0);
 
 	if ((slot | (pin - 1)) > 0x3) {
 		printk(KERN_WARNING "PCI: Bad IRQ mapping request for slot %d pin %c\n",
diff --git a/arch/sh/drivers/pci/fixups-r7780rp.c b/arch/sh/drivers/pci/fixups-r7780rp.c
index f9370dce..57ed3f0 100644
--- a/arch/sh/drivers/pci/fixups-r7780rp.c
+++ b/arch/sh/drivers/pci/fixups-r7780rp.c
@@ -12,13 +12,10 @@
  */
 #include <linux/pci.h>
 #include <linux/io.h>
+#include <linux/sh_intc.h>
 #include "pci-sh4.h"
 
-static char irq_tab[] __initdata = {
-	65, 66, 67, 68,
-};
-
 int __init pcibios_map_platform_irq(const struct pci_dev *pdev, u8 slot, u8 pin)
 {
-	return irq_tab[slot];
+	return evt2irq(0xa20) + slot;
 }
diff --git a/arch/sh/drivers/pci/fixups-sdk7780.c b/arch/sh/drivers/pci/fixups-sdk7780.c
index 0b84725..c0a015a 100644
--- a/arch/sh/drivers/pci/fixups-sdk7780.c
+++ b/arch/sh/drivers/pci/fixups-sdk7780.c
@@ -13,18 +13,28 @@
  */
 #include <linux/pci.h>
 #include <linux/io.h>
+#include <linux/sh_intc.h>
 #include "pci-sh4.h"
 
+#define IRQ_INTA	evt2irq(0xa20)
+#define IRQ_INTB	evt2irq(0xa40)
+#define IRQ_INTC	evt2irq(0xa60)
+#define IRQ_INTD	evt2irq(0xa80)
+
 /* IDSEL [16][17][18][19][20][21][22][23][24][25][26][27][28][29][30][31] */
 static char sdk7780_irq_tab[4][16] __initdata = {
 	/* INTA */
-	{ 65, 68, 67, 68, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
+	{ IRQ_INTA, IRQ_INTD, IRQ_INTC, IRQ_INTD, -1, -1, -1, -1, -1, -1,
+	  -1, -1, -1, -1, -1, -1 },
 	/* INTB */
-	{ 66, 65, -1, 65, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
+	{ IRQ_INTB, IRQ_INTA, -1, IRQ_INTA, -1, -1, -1, -1, -1, -1, -1, -1,
+	  -1, -1, -1, -1 },
 	/* INTC */
-	{ 67, 66, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
+	{ IRQ_INTC, IRQ_INTB, -1, IRQ_INTB, -1, -1, -1, -1, -1, -1, -1, -1,
+	  -1, -1, -1, -1 },
 	/* INTD */
-	{ 68, 67, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
+	{ IRQ_INTD, IRQ_INTC, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+	  -1, -1, -1 },
 };
 
 int __init pcibios_map_platform_irq(const struct pci_dev *pdev, u8 slot, u8 pin)
diff --git a/arch/sh/drivers/pci/fixups-se7751.c b/arch/sh/drivers/pci/fixups-se7751.c
index 2ec146c..84a88ca 100644
--- a/arch/sh/drivers/pci/fixups-se7751.c
+++ b/arch/sh/drivers/pci/fixups-se7751.c
@@ -4,13 +4,14 @@
 #include <linux/delay.h>
 #include <linux/pci.h>
 #include <linux/io.h>
+#include <linux/sh_intc.h>
 #include "pci-sh4.h"
 
 int __init pcibios_map_platform_irq(const struct pci_dev *, u8 slot, u8 pin)
 {
         switch (slot) {
-        case 0: return 13;
-        case 1: return 13;	/* AMD Ethernet controller */
+        case 0: return evt2irq(0x3a0);
+        case 1: return evt2irq(0x3a0);	/* AMD Ethernet controller */
         case 2: return -1;
         case 3: return -1;
         case 4: return -1;
diff --git a/arch/sh/drivers/pci/fixups-sh03.c b/arch/sh/drivers/pci/fixups-sh03.c
index 1615e59..16207be 100644
--- a/arch/sh/drivers/pci/fixups-sh03.c
+++ b/arch/sh/drivers/pci/fixups-sh03.c
@@ -2,6 +2,7 @@
 #include <linux/init.h>
 #include <linux/types.h>
 #include <linux/pci.h>
+#include <linux/sh_intc.h>
 
 int __init pcibios_map_platform_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
@@ -9,21 +10,21 @@
 
 	if (dev->bus->number == 0) {
 		switch (slot) {
-		case 4: return 5;	/* eth0       */
-		case 8: return 5;	/* eth1       */
-		case 6: return 2;	/* PCI bridge */
+		case 4: return evt2irq(0x2a0);	/* eth0       */
+		case 8: return evt2irq(0x2a0);	/* eth1       */
+		case 6: return evt2irq(0x240);	/* PCI bridge */
 		default:
 			printk(KERN_ERR "PCI: Bad IRQ mapping request "
 					"for slot %d\n", slot);
-			return 2;
+			return evt2irq(0x240);
 		}
 	} else {
 		switch (pin) {
-		case 0:   irq =  2; break;
-		case 1:   irq =  2; break;
-		case 2:   irq =  2; break;
-		case 3:   irq =  2; break;
-		case 4:   irq =  2; break;
+		case 0:   irq =  evt2irq(0x240); break;
+		case 1:   irq =  evt2irq(0x240); break;
+		case 2:   irq =  evt2irq(0x240); break;
+		case 3:   irq =  evt2irq(0x240); break;
+		case 4:   irq =  evt2irq(0x240); break;
 		default:  irq = -1; break;
 		}
 	}
diff --git a/arch/sh/drivers/pci/fixups-snapgear.c b/arch/sh/drivers/pci/fixups-snapgear.c
index 4a093c6..6e33ba4 100644
--- a/arch/sh/drivers/pci/fixups-snapgear.c
+++ b/arch/sh/drivers/pci/fixups-snapgear.c
@@ -16,6 +16,7 @@
 #include <linux/types.h>
 #include <linux/init.h>
 #include <linux/pci.h>
+#include <linux/sh_intc.h>
 #include "pci-sh4.h"
 
 int __init pcibios_map_platform_irq(const struct pci_dev *pdev, u8 slot, u8 pin)
@@ -24,11 +25,11 @@
 
 	switch (slot) {
 	case 8:  /* the PCI bridge */ break;
-	case 11: irq = 8;  break; /* USB    */
-	case 12: irq = 11; break; /* PCMCIA */
-	case 13: irq = 5;  break; /* eth0   */
-	case 14: irq = 8;  break; /* eth1   */
-	case 15: irq = 11; break; /* safenet (unused) */
+	case 11: irq = evt2irq(0x300); break; /* USB    */
+	case 12: irq = evt2irq(0x360); break; /* PCMCIA */
+	case 13: irq = evt2irq(0x2a0); break; /* eth0   */
+	case 14: irq = evt2irq(0x300); break; /* eth1   */
+	case 15: irq = evt2irq(0x360); break; /* safenet (unused) */
 	}
 
 	printk("PCI: Mapping SnapGear IRQ for slot %d, pin %c to irq %d\n",
diff --git a/arch/sh/drivers/pci/pcie-sh7786.c b/arch/sh/drivers/pci/pcie-sh7786.c
index 4df27c4f..c045142 100644
--- a/arch/sh/drivers/pci/pcie-sh7786.c
+++ b/arch/sh/drivers/pci/pcie-sh7786.c
@@ -18,6 +18,7 @@
 #include <linux/slab.h>
 #include <linux/clk.h>
 #include <linux/sh_clk.h>
+#include <linux/sh_intc.h>
 #include "pcie-sh7786.h"
 #include <asm/sizes.h>
 
@@ -468,7 +469,7 @@
 
 int __init pcibios_map_platform_irq(const struct pci_dev *pdev, u8 slot, u8 pin)
 {
-        return 71;
+        return evt2irq(0xae0);
 }
 
 static int __init sh7786_pcie_core_init(void)
diff --git a/arch/sh/include/asm/dma-sh.h b/arch/sh/include/asm/dma-sh.h
deleted file mode 100644
index f3acb8e..0000000
--- a/arch/sh/include/asm/dma-sh.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * arch/sh/include/asm/dma-sh.h
- *
- * Copyright (C) 2000  Takashi YOSHII
- * Copyright (C) 2003  Paul Mundt
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file "COPYING" in the main directory of this archive
- * for more details.
- */
-#ifndef __DMA_SH_H
-#define __DMA_SH_H
-
-#include <asm/dma-register.h>
-#include <cpu/dma-register.h>
-#include <cpu/dma.h>
-
-/* DMAOR contorl: The DMAOR access size is different by CPU.*/
-#if defined(CONFIG_CPU_SUBTYPE_SH7723)	|| \
-    defined(CONFIG_CPU_SUBTYPE_SH7724)	|| \
-    defined(CONFIG_CPU_SUBTYPE_SH7780)	|| \
-    defined(CONFIG_CPU_SUBTYPE_SH7785)
-#define dmaor_read_reg(n) \
-    (n ? __raw_readw(SH_DMAC_BASE1 + DMAOR) \
-	: __raw_readw(SH_DMAC_BASE0 + DMAOR))
-#define dmaor_write_reg(n, data) \
-    (n ? __raw_writew(data, SH_DMAC_BASE1 + DMAOR) \
-    : __raw_writew(data, SH_DMAC_BASE0 + DMAOR))
-#else /* Other CPU */
-#define dmaor_read_reg(n) __raw_readw(SH_DMAC_BASE0 + DMAOR)
-#define dmaor_write_reg(n, data) __raw_writew(data, SH_DMAC_BASE0 + DMAOR)
-#endif
-
-static int dmte_irq_map[] __maybe_unused = {
-#if (MAX_DMA_CHANNELS >= 4)
-    DMTE0_IRQ,
-    DMTE0_IRQ + 1,
-    DMTE0_IRQ + 2,
-    DMTE0_IRQ + 3,
-#endif
-#if (MAX_DMA_CHANNELS >= 6)
-    DMTE4_IRQ,
-    DMTE4_IRQ + 1,
-#endif
-#if (MAX_DMA_CHANNELS >= 8)
-    DMTE6_IRQ,
-    DMTE6_IRQ + 1,
-#endif
-#if (MAX_DMA_CHANNELS >= 12)
-    DMTE8_IRQ,
-    DMTE9_IRQ,
-    DMTE10_IRQ,
-    DMTE11_IRQ,
-#endif
-};
-
-/*
- * Define the default configuration for dual address memory-memory transfer.
- * The 0x400 value represents auto-request, external->external.
- */
-#define RS_DUAL	(DM_INC | SM_INC | 0x400 | TS_INDEX2VAL(XMIT_SZ_32BIT))
-
-/* DMA base address */
-static u32 dma_base_addr[] __maybe_unused = {
-#if (MAX_DMA_CHANNELS >= 4)
-	SH_DMAC_BASE0 + 0x00,	/* channel 0 */
-	SH_DMAC_BASE0 + 0x10,
-	SH_DMAC_BASE0 + 0x20,
-	SH_DMAC_BASE0 + 0x30,
-#endif
-#if (MAX_DMA_CHANNELS >= 6)
-	SH_DMAC_BASE0 + 0x50,
-	SH_DMAC_BASE0 + 0x60,
-#endif
-#if (MAX_DMA_CHANNELS >= 8)
-	SH_DMAC_BASE1 + 0x00,
-	SH_DMAC_BASE1 + 0x10,
-#endif
-#if (MAX_DMA_CHANNELS >= 12)
-	SH_DMAC_BASE1 + 0x20,
-	SH_DMAC_BASE1 + 0x30,
-	SH_DMAC_BASE1 + 0x50,
-	SH_DMAC_BASE1 + 0x60, /* channel 11 */
-#endif
-};
-
-#endif /* __DMA_SH_H */
diff --git a/arch/sh/include/asm/dma.h b/arch/sh/include/asm/dma.h
index 6aa2080..fb6e4f7 100644
--- a/arch/sh/include/asm/dma.h
+++ b/arch/sh/include/asm/dma.h
@@ -15,17 +15,8 @@
 #include <linux/wait.h>
 #include <linux/sched.h>
 #include <linux/device.h>
-#include <cpu/dma.h>
 #include <asm-generic/dma.h>
 
-#ifdef CONFIG_NR_DMA_CHANNELS
-#  define MAX_DMA_CHANNELS	(CONFIG_NR_DMA_CHANNELS)
-#elif defined(CONFIG_NR_ONCHIP_DMA_CHANNELS)
-#  define MAX_DMA_CHANNELS	(CONFIG_NR_ONCHIP_DMA_CHANNELS)
-#else
-#  define MAX_DMA_CHANNELS	0
-#endif
-
 /*
  * Read and write modes can mean drastically different things depending on the
  * channel configuration. Consult your DMAC documentation and module
diff --git a/arch/sh/include/asm/fixmap.h b/arch/sh/include/asm/fixmap.h
index bd7e79a..cbe0186 100644
--- a/arch/sh/include/asm/fixmap.h
+++ b/arch/sh/include/asm/fixmap.h
@@ -96,7 +96,7 @@
 #ifdef CONFIG_SUPERH32
 #define FIXADDR_TOP	(P4SEG - PAGE_SIZE)
 #else
-#define FIXADDR_TOP	(0xff000000 - PAGE_SIZE)
+#define FIXADDR_TOP	((unsigned long)(-PAGE_SIZE))
 #endif
 #define FIXADDR_SIZE	(__end_of_fixed_addresses << PAGE_SHIFT)
 #define FIXADDR_START	(FIXADDR_TOP - FIXADDR_SIZE)
diff --git a/arch/sh/include/asm/i2c-sh7760.h b/arch/sh/include/asm/i2c-sh7760.h
index 2418211..69fee12 100644
--- a/arch/sh/include/asm/i2c-sh7760.h
+++ b/arch/sh/include/asm/i2c-sh7760.h
@@ -9,11 +9,9 @@
 
 #define SH7760_I2C0_MMIO	0xFE140000
 #define SH7760_I2C0_MMIOEND	0xFE14003B
-#define SH7760_I2C0_IRQ		62
 
 #define SH7760_I2C1_MMIO	0xFE150000
 #define SH7760_I2C1_MMIOEND	0xFE15003B
-#define SH7760_I2C1_IRQ		63
 
 struct sh7760_i2c_platdata {
 	unsigned int speed_khz;
diff --git a/arch/sh/include/asm/io.h b/arch/sh/include/asm/io.h
index ec464a6..0cf60a6 100644
--- a/arch/sh/include/asm/io.h
+++ b/arch/sh/include/asm/io.h
@@ -218,8 +218,13 @@
 __BUILD_IOPORT_STRING(l, u32)
 __BUILD_IOPORT_STRING(q, u64)
 
+#else /* !CONFIG_HAS_IOPORT */
+
+#include <asm/io_noioport.h>
+
 #endif
 
+
 #define IO_SPACE_LIMIT 0xffffffff
 
 /* synco on SH-4A, otherwise a nop */
diff --git a/arch/sh/include/asm/io_noioport.h b/arch/sh/include/asm/io_noioport.h
new file mode 100644
index 0000000..e136d28
--- /dev/null
+++ b/arch/sh/include/asm/io_noioport.h
@@ -0,0 +1,41 @@
+#ifndef __ASM_SH_IO_NOIOPORT_H
+#define __ASM_SH_IO_NOIOPORT_H
+
+static inline u8 inb(unsigned long addr)
+{
+	BUG();
+	return -1;
+}
+
+static inline u16 inw(unsigned long addr)
+{
+	BUG();
+	return -1;
+}
+
+static inline u32 inl(unsigned long addr)
+{
+	BUG();
+	return -1;
+}
+
+#define outb(x, y)	BUG()
+#define outw(x, y)	BUG()
+#define outl(x, y)	BUG()
+
+#define inb_p(addr)	inb(addr)
+#define inw_p(addr)	inw(addr)
+#define inl_p(addr)	inl(addr)
+#define outb_p(x, addr)	outb((x), (addr))
+#define outw_p(x, addr)	outw((x), (addr))
+#define outl_p(x, addr)	outl((x), (addr))
+
+#define insb(a, b, c)	BUG()
+#define insw(a, b, c)	BUG()
+#define insl(a, b, c)	BUG()
+
+#define outsb(a, b, c)	BUG()
+#define outsw(a, b, c)	BUG()
+#define outsl(a, b, c)	BUG()
+
+#endif /* __ASM_SH_IO_NOIOPORT_H */
diff --git a/arch/sh/include/asm/irq.h b/arch/sh/include/asm/irq.h
index 2a62017..0e4f532 100644
--- a/arch/sh/include/asm/irq.h
+++ b/arch/sh/include/asm/irq.h
@@ -5,12 +5,15 @@
 #include <asm/machvec.h>
 
 /*
- * A sane default based on a reasonable vector table size, platforms are
- * advised to cap this at the hard limit that they're interested in
- * through the machvec.
+ * Only legacy non-sparseirq platforms have to set a reasonably sane
+ * value here. sparseirq platforms allocate their irq_descs on the fly,
+ * so will expand automatically based on the number of registered IRQs.
  */
-#define NR_IRQS			512
-#define NR_IRQS_LEGACY		8	/* Legacy external IRQ0-7 */
+#ifdef CONFIG_SPARSE_IRQ
+# define NR_IRQS		8
+#else
+# define NR_IRQS		512
+#endif
 
 /*
  * This is a special IRQ number for indicating that no IRQ has been
diff --git a/arch/sh/include/asm/kdebug.h b/arch/sh/include/asm/kdebug.h
index 5f6d2e9..a6201f1 100644
--- a/arch/sh/include/asm/kdebug.h
+++ b/arch/sh/include/asm/kdebug.h
@@ -10,4 +10,6 @@
 	DIE_SSTEP,
 };
 
+extern void printk_address(unsigned long address, int reliable);
+
 #endif /* __ASM_SH_KDEBUG_H */
diff --git a/arch/sh/include/asm/kgdb.h b/arch/sh/include/asm/kgdb.h
index f361395..9e7d2d1 100644
--- a/arch/sh/include/asm/kgdb.h
+++ b/arch/sh/include/asm/kgdb.h
@@ -4,18 +4,6 @@
 #include <asm/cacheflush.h>
 #include <asm/ptrace.h>
 
-/* Same as pt_regs but has vbr in place of syscall_nr */
-struct kgdb_regs {
-        unsigned long regs[16];
-        unsigned long pc;
-        unsigned long pr;
-        unsigned long sr;
-        unsigned long gbr;
-        unsigned long mach;
-        unsigned long macl;
-        unsigned long vbr;
-};
-
 enum regnames {
 	GDB_R0, GDB_R1, GDB_R2, GDB_R3, GDB_R4, GDB_R5, GDB_R6, GDB_R7,
 	GDB_R8, GDB_R9, GDB_R10, GDB_R11, GDB_R12, GDB_R13, GDB_R14, GDB_R15,
@@ -23,17 +11,27 @@
 	GDB_PC, GDB_PR, GDB_SR, GDB_GBR, GDB_MACH, GDB_MACL, GDB_VBR,
 };
 
-#define NUMREGBYTES    ((GDB_VBR + 1) * 4)
+#define _GP_REGS	16
+#define _EXTRA_REGS	7
+#define GDB_SIZEOF_REG	sizeof(u32)
+
+#define DBG_MAX_REG_NUM	(_GP_REGS + _EXTRA_REGS)
+#define NUMREGBYTES	(DBG_MAX_REG_NUM * sizeof(GDB_SIZEOF_REG))
 
 static inline void arch_kgdb_breakpoint(void)
 {
 	__asm__ __volatile__ ("trapa #0x3c\n");
 }
 
-#define BUFMAX                 2048
-
-#define CACHE_FLUSH_IS_SAFE	1
 #define BREAK_INSTR_SIZE	2
+#define BUFMAX			2048
+
+#ifdef CONFIG_SMP
+# define CACHE_FLUSH_IS_SAFE	0
+#else
+# define CACHE_FLUSH_IS_SAFE	1
+#endif
+
 #define GDB_ADJUSTS_BREAK_OFFSET
 
 #endif /* __ASM_SH_KGDB_H */
diff --git a/arch/sh/include/asm/machvec.h b/arch/sh/include/asm/machvec.h
index 57c5c3d..eb9c20d 100644
--- a/arch/sh/include/asm/machvec.h
+++ b/arch/sh/include/asm/machvec.h
@@ -17,7 +17,6 @@
 struct sh_machine_vector {
 	void (*mv_setup)(char **cmdline_p);
 	const char *mv_name;
-	int mv_nr_irqs;
 
 	int (*mv_irq_demux)(int irq);
 	void (*mv_init_irq)(void);
diff --git a/arch/sh/include/asm/pgtable_64.h b/arch/sh/include/asm/pgtable_64.h
index 42cb9dd..dda8c82 100644
--- a/arch/sh/include/asm/pgtable_64.h
+++ b/arch/sh/include/asm/pgtable_64.h
@@ -87,9 +87,6 @@
 #define pte_unmap(pte)		do { } while (0)
 
 #ifndef __ASSEMBLY__
-#define IOBASE_VADDR	0xff000000
-#define IOBASE_END	0xffffffff
-
 /*
  * PTEL coherent flags.
  * See Chapter 17 ST50 CPU Core Volume 1, Architecture.
diff --git a/arch/sh/include/asm/processor.h b/arch/sh/include/asm/processor.h
index 6dbc1be..3d14aea 100644
--- a/arch/sh/include/asm/processor.h
+++ b/arch/sh/include/asm/processor.h
@@ -18,7 +18,8 @@
 	CPU_SH7619,
 
 	/* SH-2A types */
-	CPU_SH7201, CPU_SH7203, CPU_SH7206, CPU_SH7263, CPU_MXG,
+	CPU_SH7201, CPU_SH7203, CPU_SH7206, CPU_SH7263, CPU_SH7264, CPU_SH7269,
+	CPU_MXG,
 
 	/* SH-3 types */
 	CPU_SH7705, CPU_SH7706, CPU_SH7707,
@@ -32,7 +33,7 @@
 
 	/* SH-4A types */
 	CPU_SH7763, CPU_SH7770, CPU_SH7780, CPU_SH7781, CPU_SH7785, CPU_SH7786,
-	CPU_SH7723, CPU_SH7724, CPU_SH7757, CPU_SHX3,
+	CPU_SH7723, CPU_SH7724, CPU_SH7757, CPU_SH7734, CPU_SHX3,
 
 	/* SH4AL-DSP types */
 	CPU_SH7343, CPU_SH7722, CPU_SH7366, CPU_SH7372,
diff --git a/arch/sh/include/asm/processor_32.h b/arch/sh/include/asm/processor_32.h
index 900f8d7..b6311fd 100644
--- a/arch/sh/include/asm/processor_32.h
+++ b/arch/sh/include/asm/processor_32.h
@@ -126,9 +126,6 @@
 /* Free all resources held by a thread. */
 extern void release_thread(struct task_struct *);
 
-/* Prepare to copy thread state - unlazy all lazy status */
-void prepare_to_copy(struct task_struct *tsk);
-
 /*
  * create a kernel thread without removing it from tasklists
  */
diff --git a/arch/sh/include/asm/processor_64.h b/arch/sh/include/asm/processor_64.h
index e25c4c7..cd6029f 100644
--- a/arch/sh/include/asm/processor_64.h
+++ b/arch/sh/include/asm/processor_64.h
@@ -121,7 +121,6 @@
 	   NULL for a kernel thread. */
 	struct pt_regs *uregs;
 
-	unsigned long trap_no, error_code;
 	unsigned long address;
 	/* Hardware debugging registers may come here */
 
@@ -138,8 +137,6 @@
 	.pc		= 0,			\
         .kregs		= &fake_swapper_regs,	\
 	.uregs	        = NULL,			\
-	.trap_no	= 0,			\
-	.error_code	= 0,			\
 	.address	= 0,			\
 	.flags		= 0,			\
 }
@@ -172,7 +169,6 @@
 #define copy_segments(p, mm)	do { } while (0)
 #define release_segments(mm)	do { } while (0)
 #define forget_segments()	do { } while (0)
-#define prepare_to_copy(tsk)	do { } while (0)
 /*
  * FPU lazy state save handling.
  */
diff --git a/arch/sh/include/asm/stackprotector.h b/arch/sh/include/asm/stackprotector.h
new file mode 100644
index 0000000..d9df3a7
--- /dev/null
+++ b/arch/sh/include/asm/stackprotector.h
@@ -0,0 +1,27 @@
+#ifndef __ASM_SH_STACKPROTECTOR_H
+#define __ASM_SH_STACKPROTECTOR_H
+
+#include <linux/random.h>
+#include <linux/version.h>
+
+extern unsigned long __stack_chk_guard;
+
+/*
+ * Initialize the stackprotector canary value.
+ *
+ * NOTE: this must only be called from functions that never return,
+ * and it must always be inlined.
+ */
+static __always_inline void boot_init_stack_canary(void)
+{
+	unsigned long canary;
+
+	/* Try to get a semi random initial value. */
+	get_random_bytes(&canary, sizeof(canary));
+	canary ^= LINUX_VERSION_CODE;
+
+	current->stack_canary = canary;
+	__stack_chk_guard = current->stack_canary;
+}
+
+#endif /* __ASM_SH_STACKPROTECTOR_H */
diff --git a/arch/sh/include/asm/thread_info.h b/arch/sh/include/asm/thread_info.h
index b690206..0c04ffc 100644
--- a/arch/sh/include/asm/thread_info.h
+++ b/arch/sh/include/asm/thread_info.h
@@ -10,8 +10,18 @@
  *  - Incorporating suggestions made by Linus Torvalds and Dave Miller
  */
 #ifdef __KERNEL__
+
 #include <asm/page.h>
 
+/*
+ * Page fault error code bits
+ */
+#define FAULT_CODE_WRITE	(1 << 0)	/* write access */
+#define FAULT_CODE_INITIAL	(1 << 1)	/* initial page write */
+#define FAULT_CODE_ITLB		(1 << 2)	/* ITLB miss */
+#define FAULT_CODE_PROT		(1 << 3)	/* protection fault */
+#define FAULT_CODE_USER		(1 << 4)	/* user-mode access */
+
 #ifndef __ASSEMBLY__
 #include <asm/processor.h>
 
@@ -98,10 +108,13 @@
 #endif /* __ASSEMBLY__ */
 
 /*
- * thread information flags
- * - these are process state flags that various assembly files may need to access
- * - pending work-to-be-done flags are in LSW
- * - other flags in MSW
+ * Thread information flags
+ *
+ * - Limited to 24 bits, upper byte used for fault code encoding.
+ *
+ * - _TIF_ALLWORK_MASK and _TIF_WORK_MASK need to fit within 2 bytes, or
+ *   we blow the tst immediate size constraints and need to fix up
+ *   arch/sh/kernel/entry-common.S.
  */
 #define TIF_SYSCALL_TRACE	0	/* syscall trace active */
 #define TIF_SIGPENDING		1	/* signal pending */
@@ -124,12 +137,6 @@
 #define _TIF_SYSCALL_TRACEPOINT	(1 << TIF_SYSCALL_TRACEPOINT)
 #define _TIF_POLLING_NRFLAG	(1 << TIF_POLLING_NRFLAG)
 
-/*
- * _TIF_ALLWORK_MASK and _TIF_WORK_MASK need to fit within 2 bytes, or we
- * blow the tst immediate size constraints and need to fix up
- * arch/sh/kernel/entry-common.S.
- */
-
 /* work to do in syscall trace */
 #define _TIF_WORK_SYSCALL_MASK	(_TIF_SYSCALL_TRACE | _TIF_SINGLESTEP | \
 				 _TIF_SYSCALL_AUDIT | _TIF_SECCOMP    | \
@@ -156,6 +163,7 @@
 #define TS_USEDFPU		0x0002	/* FPU used by this task this quantum */
 
 #ifndef __ASSEMBLY__
+
 #define HAVE_SET_RESTORE_SIGMASK	1
 static inline void set_restore_sigmask(void)
 {
@@ -163,6 +171,24 @@
 	ti->status |= TS_RESTORE_SIGMASK;
 	set_bit(TIF_SIGPENDING, (unsigned long *)&ti->flags);
 }
+
+#define TI_FLAG_FAULT_CODE_SHIFT	24
+
+/*
+ * Additional thread flag encoding
+ */
+static inline void set_thread_fault_code(unsigned int val)
+{
+	struct thread_info *ti = current_thread_info();
+	ti->flags = (ti->flags & (~0 >> (32 - TI_FLAG_FAULT_CODE_SHIFT)))
+		| (val << TI_FLAG_FAULT_CODE_SHIFT);
+}
+
+static inline unsigned int get_thread_fault_code(void)
+{
+	struct thread_info *ti = current_thread_info();
+	return ti->flags >> TI_FLAG_FAULT_CODE_SHIFT;
+}
 #endif	/* !__ASSEMBLY__ */
 
 #endif /* __KERNEL__ */
diff --git a/arch/sh/include/asm/traps_64.h b/arch/sh/include/asm/traps_64.h
index c52d7f9..ef5eff91 100644
--- a/arch/sh/include/asm/traps_64.h
+++ b/arch/sh/include/asm/traps_64.h
@@ -10,8 +10,22 @@
 #ifndef __ASM_SH_TRAPS_64_H
 #define __ASM_SH_TRAPS_64_H
 
+#include <cpu/registers.h>
+
 extern void phys_stext(void);
 
+#define lookup_exception_vector()		\
+({						\
+	unsigned long _vec;			\
+						\
+	__asm__ __volatile__ (			\
+		"getcon " __EXPEVT ", %0\n\t"	\
+		: "=r" (_vec)			\
+	);					\
+						\
+	_vec;					\
+})
+
 static inline void trigger_address_error(void)
 {
 	phys_stext();
diff --git a/arch/sh/include/cpu-sh2/cpu/dma.h b/arch/sh/include/cpu-sh2/cpu/dma.h
deleted file mode 100644
index d66b43c..0000000
--- a/arch/sh/include/cpu-sh2/cpu/dma.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Definitions for the SH-2 DMAC.
- *
- * Copyright (C) 2003  Paul Mundt
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file "COPYING" in the main directory of this archive
- * for more details.
- */
-#ifndef __ASM_CPU_SH2_DMA_H
-#define __ASM_CPU_SH2_DMA_H
-
-#define SH_MAX_DMA_CHANNELS	2
-
-#define SAR	((unsigned long[]){ 0xffffff80, 0xffffff90 })
-#define DAR	((unsigned long[]){ 0xffffff84, 0xffffff94 })
-#define DMATCR	((unsigned long[]){ 0xffffff88, 0xffffff98 })
-#define CHCR	((unsigned long[]){ 0xfffffffc, 0xffffff9c })
-
-#define DMAOR	0xffffffb0
-
-#endif /* __ASM_CPU_SH2_DMA_H */
-
diff --git a/arch/sh/include/cpu-sh2a/cpu/dma.h b/arch/sh/include/cpu-sh2a/cpu/dma.h
deleted file mode 100644
index 27a13ef..0000000
--- a/arch/sh/include/cpu-sh2a/cpu/dma.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <cpu-sh2/cpu/dma.h>
diff --git a/arch/sh/include/cpu-sh2a/cpu/sh7264.h b/arch/sh/include/cpu-sh2a/cpu/sh7264.h
new file mode 100644
index 0000000..4d1ef6d
--- /dev/null
+++ b/arch/sh/include/cpu-sh2a/cpu/sh7264.h
@@ -0,0 +1,176 @@
+#ifndef __ASM_SH7264_H__
+#define __ASM_SH7264_H__
+
+enum {
+	/* Port A */
+	GPIO_PA3, GPIO_PA2, GPIO_PA1, GPIO_PA0,
+
+	/* Port B */
+	GPIO_PB22, GPIO_PB21, GPIO_PB20,
+	GPIO_PB19, GPIO_PB18, GPIO_PB17, GPIO_PB16,
+	GPIO_PB15, GPIO_PB14, GPIO_PB13, GPIO_PB12,
+	GPIO_PB11, GPIO_PB10, GPIO_PB9, GPIO_PB8,
+	GPIO_PB7, GPIO_PB6, GPIO_PB5, GPIO_PB4,
+	GPIO_PB3, GPIO_PB2, GPIO_PB1,
+
+	/* Port C */
+	GPIO_PC10, GPIO_PC9, GPIO_PC8,
+	GPIO_PC7, GPIO_PC6, GPIO_PC5, GPIO_PC4,
+	GPIO_PC3, GPIO_PC2, GPIO_PC1, GPIO_PC0,
+
+	/* Port D */
+	GPIO_PD15, GPIO_PD14, GPIO_PD13, GPIO_PD12,
+	GPIO_PD11, GPIO_PD10, GPIO_PD9, GPIO_PD8,
+	GPIO_PD7, GPIO_PD6, GPIO_PD5, GPIO_PD4,
+	GPIO_PD3, GPIO_PD2, GPIO_PD1, GPIO_PD0,
+
+	/* Port E */
+	GPIO_PE5, GPIO_PE4,
+	GPIO_PE3, GPIO_PE2, GPIO_PE1, GPIO_PE0,
+
+	/* Port F */
+	GPIO_PF12,
+	GPIO_PF11, GPIO_PF10, GPIO_PF9, GPIO_PF8,
+	GPIO_PF7, GPIO_PF6, GPIO_PF5, GPIO_PF4,
+	GPIO_PF3, GPIO_PF2, GPIO_PF1, GPIO_PF0,
+
+	/* Port G */
+	GPIO_PG24,
+	GPIO_PG23, GPIO_PG22, GPIO_PG21, GPIO_PG20,
+	GPIO_PG19, GPIO_PG18, GPIO_PG17, GPIO_PG16,
+	GPIO_PG15, GPIO_PG14, GPIO_PG13, GPIO_PG12,
+	GPIO_PG11, GPIO_PG10, GPIO_PG9, GPIO_PG8,
+	GPIO_PG7, GPIO_PG6, GPIO_PG5, GPIO_PG4,
+	GPIO_PG3, GPIO_PG2, GPIO_PG1, GPIO_PG0,
+
+	/* Port H */
+	GPIO_PH7, GPIO_PH6, GPIO_PH5, GPIO_PH4,
+	GPIO_PH3, GPIO_PH2, GPIO_PH1, GPIO_PH0,
+
+	/* Port I - not on device */
+
+	/* Port J */
+	GPIO_PJ11, GPIO_PJ10, GPIO_PJ9, GPIO_PJ8,
+	GPIO_PJ7, GPIO_PJ6, GPIO_PJ5, GPIO_PJ4,
+	GPIO_PJ3, GPIO_PJ2, GPIO_PJ1, GPIO_PJ0,
+
+	/* Port K */
+	GPIO_PK11, GPIO_PK10, GPIO_PK9, GPIO_PK8,
+	GPIO_PK7, GPIO_PK6, GPIO_PK5, GPIO_PK4,
+	GPIO_PK3, GPIO_PK2, GPIO_PK1, GPIO_PK0,
+
+	/* INTC: IRQ and PINT on PB/PD/PE */
+	GPIO_FN_PINT7_PG, GPIO_FN_PINT6_PG, GPIO_FN_PINT5_PG, GPIO_FN_PINT4_PG,
+	GPIO_FN_PINT3_PG, GPIO_FN_PINT2_PG, GPIO_FN_PINT1_PG,
+
+	GPIO_FN_IRQ7_PC, GPIO_FN_IRQ6_PC, GPIO_FN_IRQ5_PC, GPIO_FN_IRQ4_PC,
+	GPIO_FN_IRQ3_PG, GPIO_FN_IRQ2_PG, GPIO_FN_IRQ1_PJ, GPIO_FN_IRQ0_PJ,
+	GPIO_FN_IRQ3_PE, GPIO_FN_IRQ2_PE, GPIO_FN_IRQ1_PE, GPIO_FN_IRQ0_PE,
+
+	/* WDT */
+	GPIO_FN_WDTOVF,
+
+	/* CAN */
+	GPIO_FN_CTX1, GPIO_FN_CRX1, GPIO_FN_CTX0, GPIO_FN_CTX0_CTX1,
+	GPIO_FN_CRX0, GPIO_FN_CRX0_CRX1,
+
+	/* DMAC */
+	GPIO_FN_TEND0, GPIO_FN_DACK0, GPIO_FN_DREQ0,
+	GPIO_FN_TEND1, GPIO_FN_DACK1, GPIO_FN_DREQ1,
+
+	/* ADC */
+	GPIO_FN_ADTRG,
+
+	/* BSC */
+
+	GPIO_FN_A25, GPIO_FN_A24,
+	GPIO_FN_A23, GPIO_FN_A22, GPIO_FN_A21, GPIO_FN_A20,
+	GPIO_FN_A19, GPIO_FN_A18, GPIO_FN_A17, GPIO_FN_A16,
+	GPIO_FN_A15, GPIO_FN_A14, GPIO_FN_A13, GPIO_FN_A12,
+	GPIO_FN_A11, GPIO_FN_A10, GPIO_FN_A9, GPIO_FN_A8,
+	GPIO_FN_A7, GPIO_FN_A6, GPIO_FN_A5, GPIO_FN_A4,
+	GPIO_FN_A3, GPIO_FN_A2, GPIO_FN_A1, GPIO_FN_A0,
+	GPIO_FN_D15, GPIO_FN_D14, GPIO_FN_D13, GPIO_FN_D12,
+	GPIO_FN_D11, GPIO_FN_D10, GPIO_FN_D9, GPIO_FN_D8,
+	GPIO_FN_D7, GPIO_FN_D6, GPIO_FN_D5, GPIO_FN_D4,
+	GPIO_FN_D3, GPIO_FN_D2, GPIO_FN_D1, GPIO_FN_D0,
+
+	GPIO_FN_BS,
+	GPIO_FN_CS4, GPIO_FN_CS3, GPIO_FN_CS2, GPIO_FN_CS1, GPIO_FN_CS0,
+	GPIO_FN_CS6CE1B, GPIO_FN_CS5CE1A,
+	GPIO_FN_CE2A, GPIO_FN_CE2B,
+	GPIO_FN_RD, GPIO_FN_RDWR,
+	GPIO_FN_ICIOWRAH, GPIO_FN_ICIORD,
+	GPIO_FN_WE1DQMUWE, GPIO_FN_WE0DQML,
+	GPIO_FN_RAS, GPIO_FN_CAS, GPIO_FN_CKE,
+	GPIO_FN_WAIT, GPIO_FN_BREQ, GPIO_FN_BACK,
+	GPIO_FN_IOIS16,
+
+	/* TMU */
+	GPIO_FN_TIOC4D, GPIO_FN_TIOC4C, GPIO_FN_TIOC4B, GPIO_FN_TIOC4A,
+	GPIO_FN_TIOC3D, GPIO_FN_TIOC3C, GPIO_FN_TIOC3B, GPIO_FN_TIOC3A,
+	GPIO_FN_TIOC2B, GPIO_FN_TIOC1B, GPIO_FN_TIOC2A, GPIO_FN_TIOC1A,
+	GPIO_FN_TIOC0D, GPIO_FN_TIOC0C, GPIO_FN_TIOC0B, GPIO_FN_TIOC0A,
+	GPIO_FN_TCLKD, GPIO_FN_TCLKC, GPIO_FN_TCLKB, GPIO_FN_TCLKA,
+
+	/* SSU */
+	GPIO_FN_SCS0_PD, GPIO_FN_SSO0_PD, GPIO_FN_SSI0_PD, GPIO_FN_SSCK0_PD,
+	GPIO_FN_SCS0_PF, GPIO_FN_SSO0_PF, GPIO_FN_SSI0_PF, GPIO_FN_SSCK0_PF,
+	GPIO_FN_SCS1_PD, GPIO_FN_SSO1_PD, GPIO_FN_SSI1_PD, GPIO_FN_SSCK1_PD,
+	GPIO_FN_SCS1_PF, GPIO_FN_SSO1_PF, GPIO_FN_SSI1_PF, GPIO_FN_SSCK1_PF,
+
+	/* SCIF */
+	GPIO_FN_SCK0, GPIO_FN_SCK1, GPIO_FN_SCK2, GPIO_FN_SCK3,
+	GPIO_FN_RXD0, GPIO_FN_RXD1, GPIO_FN_RXD2, GPIO_FN_RXD3,
+	GPIO_FN_TXD0, GPIO_FN_TXD1, GPIO_FN_TXD2, GPIO_FN_TXD3,
+	GPIO_FN_RXD4, GPIO_FN_RXD5, GPIO_FN_RXD6, GPIO_FN_RXD7,
+	GPIO_FN_TXD4, GPIO_FN_TXD5, GPIO_FN_TXD6, GPIO_FN_TXD7,
+	GPIO_FN_RTS1, GPIO_FN_RTS3, GPIO_FN_CTS1, GPIO_FN_CTS3,
+
+	/* RSPI */
+	GPIO_FN_RSPCK0, GPIO_FN_MOSI0,
+	GPIO_FN_MISO0_PF12, GPIO_FN_MISO1,
+	GPIO_FN_SSL00,
+	GPIO_FN_RSPCK1, GPIO_FN_MOSI1,
+	GPIO_FN_MISO1_PG19, GPIO_FN_SSL10,
+
+	/* IIC3 */
+	GPIO_FN_SCL0, GPIO_FN_SCL1, GPIO_FN_SCL2,
+	GPIO_FN_SDA2, GPIO_FN_SDA1, GPIO_FN_SDA0,
+
+	/* SSI */
+	GPIO_FN_SSISCK0, GPIO_FN_SSIWS0, GPIO_FN_SSITXD0, GPIO_FN_SSIRXD0,
+	GPIO_FN_SSIWS1, GPIO_FN_SSIWS2, GPIO_FN_SSIWS3,
+	GPIO_FN_SSISCK1, GPIO_FN_SSISCK2, GPIO_FN_SSISCK3,
+	GPIO_FN_SSIDATA1, GPIO_FN_SSIDATA2, GPIO_FN_SSIDATA3,
+	GPIO_FN_AUDIO_CLK,
+
+	/* SIOF */
+	GPIO_FN_SIOFTXD, GPIO_FN_SIOFRXD, GPIO_FN_SIOFSYNC, GPIO_FN_SIOFSCK,
+
+	/* SPDIF */
+	GPIO_FN_SPDIF_IN,
+	GPIO_FN_SPDIF_OUT,
+
+	/* NANDFMC */ /* NOTE Controller is not available in boot mode 0 */
+	GPIO_FN_FCE,
+	GPIO_FN_FRB,
+
+	/* VDC3 */
+	GPIO_FN_DV_CLK, GPIO_FN_DV_VSYNC, GPIO_FN_DV_HSYNC,
+	GPIO_FN_DV_DATA7, GPIO_FN_DV_DATA6, GPIO_FN_DV_DATA5, GPIO_FN_DV_DATA4,
+	GPIO_FN_DV_DATA3, GPIO_FN_DV_DATA2, GPIO_FN_DV_DATA1, GPIO_FN_DV_DATA0,
+	GPIO_FN_LCD_CLK, GPIO_FN_LCD_EXTCLK,
+	GPIO_FN_LCD_VSYNC, GPIO_FN_LCD_HSYNC, GPIO_FN_LCD_DE,
+	GPIO_FN_LCD_DATA15, GPIO_FN_LCD_DATA14,
+	GPIO_FN_LCD_DATA13, GPIO_FN_LCD_DATA12,
+	GPIO_FN_LCD_DATA11, GPIO_FN_LCD_DATA10,
+	GPIO_FN_LCD_DATA9, GPIO_FN_LCD_DATA8,
+	GPIO_FN_LCD_DATA7, GPIO_FN_LCD_DATA6,
+	GPIO_FN_LCD_DATA5, GPIO_FN_LCD_DATA4,
+	GPIO_FN_LCD_DATA3, GPIO_FN_LCD_DATA2,
+	GPIO_FN_LCD_DATA1, GPIO_FN_LCD_DATA0,
+	GPIO_FN_LCD_M_DISP,
+};
+
+#endif /* __ASM_SH7264_H__ */
diff --git a/arch/sh/include/cpu-sh2a/cpu/sh7269.h b/arch/sh/include/cpu-sh2a/cpu/sh7269.h
new file mode 100644
index 0000000..48d1449
--- /dev/null
+++ b/arch/sh/include/cpu-sh2a/cpu/sh7269.h
@@ -0,0 +1,201 @@
+#ifndef __ASM_SH7269_H__
+#define __ASM_SH7269_H__
+
+enum {
+	/* Port A */
+	GPIO_PA1, GPIO_PA0,
+
+	/* Port B */
+	GPIO_PB22, GPIO_PB21, GPIO_PB20,
+	GPIO_PB19, GPIO_PB18, GPIO_PB17, GPIO_PB16,
+	GPIO_PB15, GPIO_PB14, GPIO_PB13, GPIO_PB12,
+	GPIO_PB11, GPIO_PB10, GPIO_PB9, GPIO_PB8,
+	GPIO_PB7, GPIO_PB6, GPIO_PB5, GPIO_PB4,
+	GPIO_PB3, GPIO_PB2, GPIO_PB1,
+
+	/* Port C */
+	GPIO_PC8,
+	GPIO_PC7, GPIO_PC6, GPIO_PC5, GPIO_PC4,
+	GPIO_PC3, GPIO_PC2, GPIO_PC1, GPIO_PC0,
+
+	/* Port D */
+	GPIO_PD15, GPIO_PD14, GPIO_PD13, GPIO_PD12,
+	GPIO_PD11, GPIO_PD10, GPIO_PD9, GPIO_PD8,
+	GPIO_PD7, GPIO_PD6, GPIO_PD5, GPIO_PD4,
+	GPIO_PD3, GPIO_PD2, GPIO_PD1, GPIO_PD0,
+
+	/* Port E */
+	GPIO_PE7, GPIO_PE6, GPIO_PE5, GPIO_PE4,
+	GPIO_PE3, GPIO_PE2, GPIO_PE1, GPIO_PE0,
+
+	/* Port F */
+	GPIO_PF23, GPIO_PF22, GPIO_PF21, GPIO_PF20,
+	GPIO_PF19, GPIO_PF18, GPIO_PF17, GPIO_PF16,
+	GPIO_PF15, GPIO_PF14, GPIO_PF13, GPIO_PF12,
+	GPIO_PF11, GPIO_PF10, GPIO_PF9, GPIO_PF8,
+	GPIO_PF7, GPIO_PF6, GPIO_PF5, GPIO_PF4,
+	GPIO_PF3, GPIO_PF2, GPIO_PF1, GPIO_PF0,
+
+	/* Port G */
+	GPIO_PG27, GPIO_PG26, GPIO_PG25, GPIO_PG24,
+	GPIO_PG23, GPIO_PG22, GPIO_PG21, GPIO_PG20,
+	GPIO_PG19, GPIO_PG18, GPIO_PG17, GPIO_PG16,
+	GPIO_PG15, GPIO_PG14, GPIO_PG13, GPIO_PG12,
+	GPIO_PG11, GPIO_PG10, GPIO_PG9, GPIO_PG8,
+	GPIO_PG7, GPIO_PG6, GPIO_PG5, GPIO_PG4,
+	GPIO_PG3, GPIO_PG2, GPIO_PG1, GPIO_PG0,
+
+	/* Port H */
+	GPIO_PH7, GPIO_PH6, GPIO_PH5, GPIO_PH4,
+	GPIO_PH3, GPIO_PH2, GPIO_PH1, GPIO_PH0,
+
+	/* Port I - not on device */
+
+	/* Port J */
+	GPIO_PJ31, GPIO_PJ30, GPIO_PJ29, GPIO_PJ28,
+	GPIO_PJ27, GPIO_PJ26, GPIO_PJ25, GPIO_PJ24,
+	GPIO_PJ23, GPIO_PJ22, GPIO_PJ21, GPIO_PJ20,
+	GPIO_PJ19, GPIO_PJ18, GPIO_PJ17, GPIO_PJ16,
+	GPIO_PJ15, GPIO_PJ14, GPIO_PJ13, GPIO_PJ12,
+	GPIO_PJ11, GPIO_PJ10, GPIO_PJ9, GPIO_PJ8,
+	GPIO_PJ7, GPIO_PJ6, GPIO_PJ5, GPIO_PJ4,
+	GPIO_PJ3, GPIO_PJ2, GPIO_PJ1, GPIO_PJ0,
+
+	/* INTC: IRQ and PINT */
+	GPIO_FN_IRQ7_PG, GPIO_FN_IRQ6_PG, GPIO_FN_IRQ5_PG, GPIO_FN_IRQ4_PG,
+	GPIO_FN_IRQ3_PG, GPIO_FN_IRQ2_PG, GPIO_FN_IRQ1_PG, GPIO_FN_IRQ0_PG,
+	GPIO_FN_IRQ7_PF, GPIO_FN_IRQ6_PF, GPIO_FN_IRQ5_PF, GPIO_FN_IRQ4_PF,
+	GPIO_FN_IRQ3_PJ, GPIO_FN_IRQ2_PJ, GPIO_FN_IRQ1_PJ, GPIO_FN_IRQ0_PJ,
+	GPIO_FN_IRQ1_PC, GPIO_FN_IRQ0_PC,
+
+	GPIO_FN_PINT7_PG, GPIO_FN_PINT6_PG, GPIO_FN_PINT5_PG, GPIO_FN_PINT4_PG,
+	GPIO_FN_PINT3_PG, GPIO_FN_PINT2_PG, GPIO_FN_PINT1_PG, GPIO_FN_PINT0_PG,
+	GPIO_FN_PINT7_PH, GPIO_FN_PINT6_PH, GPIO_FN_PINT5_PH, GPIO_FN_PINT4_PH,
+	GPIO_FN_PINT3_PH, GPIO_FN_PINT2_PH, GPIO_FN_PINT1_PH, GPIO_FN_PINT0_PH,
+	GPIO_FN_PINT7_PJ, GPIO_FN_PINT6_PJ, GPIO_FN_PINT5_PJ, GPIO_FN_PINT4_PJ,
+	GPIO_FN_PINT3_PJ, GPIO_FN_PINT2_PJ, GPIO_FN_PINT1_PJ, GPIO_FN_PINT0_PJ,
+
+	/* WDT */
+	GPIO_FN_WDTOVF,
+
+	/* CAN */
+	GPIO_FN_CTX1, GPIO_FN_CRX1, GPIO_FN_CTX0, GPIO_FN_CTX0_CTX1,
+	GPIO_FN_CRX0, GPIO_FN_CRX0_CRX1, GPIO_FN_CRX0_CRX1_CRX2,
+
+	/* DMAC */
+	GPIO_FN_TEND0, GPIO_FN_DACK0, GPIO_FN_DREQ0,
+	GPIO_FN_TEND1, GPIO_FN_DACK1, GPIO_FN_DREQ1,
+
+	/* ADC */
+	GPIO_FN_ADTRG,
+
+	/* BSC */
+	GPIO_FN_A25, GPIO_FN_A24,
+	GPIO_FN_A23, GPIO_FN_A22, GPIO_FN_A21, GPIO_FN_A20,
+	GPIO_FN_A19, GPIO_FN_A18, GPIO_FN_A17, GPIO_FN_A16,
+	GPIO_FN_A15, GPIO_FN_A14, GPIO_FN_A13, GPIO_FN_A12,
+	GPIO_FN_A11, GPIO_FN_A10, GPIO_FN_A9, GPIO_FN_A8,
+	GPIO_FN_A7, GPIO_FN_A6, GPIO_FN_A5, GPIO_FN_A4,
+	GPIO_FN_A3, GPIO_FN_A2, GPIO_FN_A1, GPIO_FN_A0,
+	GPIO_FN_D15, GPIO_FN_D14, GPIO_FN_D13, GPIO_FN_D12,
+	GPIO_FN_D11, GPIO_FN_D10, GPIO_FN_D9, GPIO_FN_D8,
+	GPIO_FN_D7, GPIO_FN_D6, GPIO_FN_D5, GPIO_FN_D4,
+	GPIO_FN_D3, GPIO_FN_D2, GPIO_FN_D1, GPIO_FN_D0,
+
+	GPIO_FN_BS,
+	GPIO_FN_CS4, GPIO_FN_CS3, GPIO_FN_CS2, GPIO_FN_CS1, GPIO_FN_CS0,
+	GPIO_FN_CS5CE1A,
+	GPIO_FN_CE2A, GPIO_FN_CE2B,
+	GPIO_FN_RD, GPIO_FN_RDWR,
+	GPIO_FN_WE3ICIOWRAHDQMUU, GPIO_FN_WE2ICIORDDQMUL,
+	GPIO_FN_WE1DQMUWE, GPIO_FN_WE0DQML,
+	GPIO_FN_RAS, GPIO_FN_CAS, GPIO_FN_CKE,
+	GPIO_FN_WAIT, GPIO_FN_BREQ, GPIO_FN_BACK,
+	GPIO_FN_IOIS16,
+
+	/* TMU */
+	GPIO_FN_TIOC4D, GPIO_FN_TIOC4C, GPIO_FN_TIOC4B, GPIO_FN_TIOC4A,
+	GPIO_FN_TIOC3D, GPIO_FN_TIOC3C, GPIO_FN_TIOC3B, GPIO_FN_TIOC3A,
+	GPIO_FN_TIOC2B, GPIO_FN_TIOC1B, GPIO_FN_TIOC2A, GPIO_FN_TIOC1A,
+	GPIO_FN_TIOC0D, GPIO_FN_TIOC0C, GPIO_FN_TIOC0B, GPIO_FN_TIOC0A,
+	GPIO_FN_TCLKD, GPIO_FN_TCLKC, GPIO_FN_TCLKB, GPIO_FN_TCLKA,
+
+	/* SSU */
+	GPIO_FN_SCS0_PD, GPIO_FN_SSO0_PD, GPIO_FN_SSI0_PD, GPIO_FN_SSCK0_PD,
+	GPIO_FN_SCS0_PF, GPIO_FN_SSO0_PF, GPIO_FN_SSI0_PF, GPIO_FN_SSCK0_PF,
+	GPIO_FN_SCS1_PD, GPIO_FN_SSO1_PD, GPIO_FN_SSI1_PD, GPIO_FN_SSCK1_PD,
+	GPIO_FN_SCS1_PF, GPIO_FN_SSO1_PF, GPIO_FN_SSI1_PF, GPIO_FN_SSCK1_PF,
+
+	/* SCIF */
+	GPIO_FN_SCK0, GPIO_FN_RXD0, GPIO_FN_TXD0,
+	GPIO_FN_SCK1, GPIO_FN_RXD1, GPIO_FN_TXD1, GPIO_FN_RTS1, GPIO_FN_CTS1,
+	GPIO_FN_SCK2, GPIO_FN_RXD2, GPIO_FN_TXD2,
+	GPIO_FN_SCK3, GPIO_FN_RXD3, GPIO_FN_TXD3,
+	GPIO_FN_SCK4, GPIO_FN_RXD4, GPIO_FN_TXD4,
+	GPIO_FN_SCK5, GPIO_FN_RXD5, GPIO_FN_TXD5, GPIO_FN_RTS5, GPIO_FN_CTS5,
+	GPIO_FN_SCK6, GPIO_FN_RXD6, GPIO_FN_TXD6,
+	GPIO_FN_SCK7, GPIO_FN_RXD7, GPIO_FN_TXD7, GPIO_FN_RTS7, GPIO_FN_CTS7,
+
+	/* RSPI */
+	GPIO_FN_MISO0_PJ19, GPIO_FN_MISO0_PB20,
+	GPIO_FN_MOSI0_PJ18, GPIO_FN_MOSI0_PB19,
+	GPIO_FN_SSL00_PJ17, GPIO_FN_SSL00_PB18,
+	GPIO_FN_RSPCK0_PJ16, GPIO_FN_RSPCK0_PB17,
+	GPIO_FN_RSPCK1, GPIO_FN_MOSI1,
+	GPIO_FN_MISO1, GPIO_FN_SSL10,
+
+	/* IIC3 */
+	GPIO_FN_SCL0, GPIO_FN_SCL1, GPIO_FN_SCL2,
+	GPIO_FN_SDA2, GPIO_FN_SDA1, GPIO_FN_SDA0,
+
+	/* SSI */
+	GPIO_FN_SSISCK0, GPIO_FN_SSIWS0, GPIO_FN_SSITXD0, GPIO_FN_SSIRXD0,
+	GPIO_FN_SSIWS1, GPIO_FN_SSIWS2, GPIO_FN_SSIWS3,
+	GPIO_FN_SSISCK1, GPIO_FN_SSISCK2, GPIO_FN_SSISCK3,
+	GPIO_FN_SSIDATA1, GPIO_FN_SSIDATA2, GPIO_FN_SSIDATA3,
+	GPIO_FN_AUDIO_CLK,
+	GPIO_FN_AUDIO_XOUT,
+
+	/* SIOF */
+	GPIO_FN_SIOFTXD, GPIO_FN_SIOFRXD, GPIO_FN_SIOFSYNC, GPIO_FN_SIOFSCK,
+
+	/* SPDIF */
+	GPIO_FN_SPDIF_IN,
+	GPIO_FN_SPDIF_OUT,
+
+	/* NANDFMC  */ /* NOTE Controller is not available in boot mode 0 */
+	GPIO_FN_FCE,
+	GPIO_FN_FRB,
+
+	/* VDC */
+	GPIO_FN_DV_CLK, GPIO_FN_DV_VSYNC, GPIO_FN_DV_HSYNC,
+	GPIO_FN_DV_DATA23, GPIO_FN_DV_DATA22,
+	GPIO_FN_DV_DATA21, GPIO_FN_DV_DATA20,
+	GPIO_FN_DV_DATA19, GPIO_FN_DV_DATA18,
+	GPIO_FN_DV_DATA17, GPIO_FN_DV_DATA16,
+	GPIO_FN_DV_DATA15, GPIO_FN_DV_DATA14,
+	GPIO_FN_DV_DATA13, GPIO_FN_DV_DATA12,
+	GPIO_FN_DV_DATA11, GPIO_FN_DV_DATA10,
+	GPIO_FN_DV_DATA9, GPIO_FN_DV_DATA8,
+	GPIO_FN_DV_DATA7, GPIO_FN_DV_DATA6,
+	GPIO_FN_DV_DATA5, GPIO_FN_DV_DATA4,
+	GPIO_FN_DV_DATA3, GPIO_FN_DV_DATA2,
+	GPIO_FN_DV_DATA1, GPIO_FN_DV_DATA0,
+	GPIO_FN_LCD_CLK, GPIO_FN_LCD_EXTCLK,
+	GPIO_FN_LCD_VSYNC, GPIO_FN_LCD_HSYNC, GPIO_FN_LCD_DE,
+	GPIO_FN_LCD_DATA23, GPIO_FN_LCD_DATA22,
+	GPIO_FN_LCD_DATA21, GPIO_FN_LCD_DATA20,
+	GPIO_FN_LCD_DATA19, GPIO_FN_LCD_DATA18,
+	GPIO_FN_LCD_DATA17, GPIO_FN_LCD_DATA16,
+	GPIO_FN_LCD_DATA15, GPIO_FN_LCD_DATA14,
+	GPIO_FN_LCD_DATA13, GPIO_FN_LCD_DATA12,
+	GPIO_FN_LCD_DATA11, GPIO_FN_LCD_DATA10,
+	GPIO_FN_LCD_DATA9, GPIO_FN_LCD_DATA8,
+	GPIO_FN_LCD_DATA7, GPIO_FN_LCD_DATA6,
+	GPIO_FN_LCD_DATA5, GPIO_FN_LCD_DATA4,
+	GPIO_FN_LCD_DATA3, GPIO_FN_LCD_DATA2,
+	GPIO_FN_LCD_DATA1, GPIO_FN_LCD_DATA0,
+	GPIO_FN_LCD_M_DISP,
+};
+
+#endif /* __ASM_SH7269_H__ */
diff --git a/arch/sh/include/cpu-sh3/cpu/dma.h b/arch/sh/include/cpu-sh3/cpu/dma.h
index 24e28b9..bccb414 100644
--- a/arch/sh/include/cpu-sh3/cpu/dma.h
+++ b/arch/sh/include/cpu-sh3/cpu/dma.h
@@ -1,6 +1,8 @@
 #ifndef __ASM_CPU_SH3_DMA_H
 #define __ASM_CPU_SH3_DMA_H
 
+#include <linux/sh_intc.h>
+
 #if defined(CONFIG_CPU_SUBTYPE_SH7720) || \
     defined(CONFIG_CPU_SUBTYPE_SH7721) || \
     defined(CONFIG_CPU_SUBTYPE_SH7710) || \
@@ -10,14 +12,7 @@
 #define SH_DMAC_BASE0	0xa4000020
 #endif
 
-#define DMTE0_IRQ	48
-#define DMTE4_IRQ	76
-
-/* Definitions for the SuperH DMAC */
-#define TM_BURST	0x00000020
-#define TS_8		0x00000000
-#define TS_16		0x00000008
-#define TS_32		0x00000010
-#define TS_128		0x00000018
+#define DMTE0_IRQ	evt2irq(0x800)
+#define DMTE4_IRQ	evt2irq(0xb80)
 
 #endif /* __ASM_CPU_SH3_DMA_H */
diff --git a/arch/sh/include/cpu-sh4/cpu/dma-sh4a.h b/arch/sh/include/cpu-sh4/cpu/dma-sh4a.h
deleted file mode 100644
index 9647e68..0000000
--- a/arch/sh/include/cpu-sh4/cpu/dma-sh4a.h
+++ /dev/null
@@ -1,83 +0,0 @@
-#ifndef __ASM_SH_CPU_SH4_DMA_SH7780_H
-#define __ASM_SH_CPU_SH4_DMA_SH7780_H
-
-#if defined(CONFIG_CPU_SUBTYPE_SH7343) || \
-	defined(CONFIG_CPU_SUBTYPE_SH7730)
-#define DMTE0_IRQ	48
-#define DMTE4_IRQ	76
-#define DMAE0_IRQ	78	/* DMA Error IRQ*/
-#define SH_DMAC_BASE0	0xFE008020
-#define SH_DMARS_BASE0	0xFE009000
-#elif defined(CONFIG_CPU_SUBTYPE_SH7722)
-#define DMTE0_IRQ	48
-#define DMTE4_IRQ	76
-#define DMAE0_IRQ	78	/* DMA Error IRQ*/
-#define SH_DMAC_BASE0	0xFE008020
-#define SH_DMARS_BASE0	0xFE009000
-#elif defined(CONFIG_CPU_SUBTYPE_SH7763) || \
-	defined(CONFIG_CPU_SUBTYPE_SH7764)
-#define DMTE0_IRQ	34
-#define DMTE4_IRQ	44
-#define DMAE0_IRQ	38
-#define SH_DMAC_BASE0	0xFF608020
-#define SH_DMARS_BASE0	0xFF609000
-#elif defined(CONFIG_CPU_SUBTYPE_SH7723)
-#define DMTE0_IRQ	48	/* DMAC0A*/
-#define DMTE4_IRQ	76	/* DMAC0B */
-#define DMTE6_IRQ	40
-#define DMTE8_IRQ	42	/* DMAC1A */
-#define DMTE9_IRQ	43
-#define DMTE10_IRQ	72	/* DMAC1B */
-#define DMTE11_IRQ	73
-#define DMAE0_IRQ	78	/* DMA Error IRQ*/
-#define DMAE1_IRQ	74	/* DMA Error IRQ*/
-#define SH_DMAC_BASE0	0xFE008020
-#define SH_DMAC_BASE1	0xFDC08020
-#define SH_DMARS_BASE0	0xFDC09000
-#elif defined(CONFIG_CPU_SUBTYPE_SH7724)
-#define DMTE0_IRQ	48	/* DMAC0A*/
-#define DMTE4_IRQ	76	/* DMAC0B */
-#define DMTE6_IRQ	40
-#define DMTE8_IRQ	42	/* DMAC1A */
-#define DMTE9_IRQ	43
-#define DMTE10_IRQ	72	/* DMAC1B */
-#define DMTE11_IRQ	73
-#define DMAE0_IRQ	78	/* DMA Error IRQ*/
-#define DMAE1_IRQ	74	/* DMA Error IRQ*/
-#define SH_DMAC_BASE0	0xFE008020
-#define SH_DMAC_BASE1	0xFDC08020
-#define SH_DMARS_BASE0	0xFE009000
-#define SH_DMARS_BASE1	0xFDC09000
-#elif defined(CONFIG_CPU_SUBTYPE_SH7780)
-#define DMTE0_IRQ	34
-#define DMTE4_IRQ	44
-#define DMTE6_IRQ	46
-#define DMTE8_IRQ	92
-#define DMTE9_IRQ	93
-#define DMTE10_IRQ	94
-#define DMTE11_IRQ	95
-#define DMAE0_IRQ	38	/* DMA Error IRQ */
-#define SH_DMAC_BASE0	0xFC808020
-#define SH_DMAC_BASE1	0xFC818020
-#define SH_DMARS_BASE0	0xFC809000
-#else /* SH7785 */
-#define DMTE0_IRQ	33
-#define DMTE4_IRQ	37
-#define DMTE6_IRQ	52
-#define DMTE8_IRQ	54
-#define DMTE9_IRQ	55
-#define DMTE10_IRQ	56
-#define DMTE11_IRQ	57
-#define DMAE0_IRQ	39	/* DMA Error IRQ0 */
-#define DMAE1_IRQ	58	/* DMA Error IRQ1 */
-#define SH_DMAC_BASE0	0xFC808020
-#define SH_DMAC_BASE1	0xFCC08020
-#define SH_DMARS_BASE0	0xFC809000
-#endif
-
-#define REQ_HE		0x000000C0
-#define REQ_H		0x00000080
-#define REQ_LE		0x00000040
-#define TM_BURST	0x00000020
-
-#endif /* __ASM_SH_CPU_SH4_DMA_SH7780_H */
diff --git a/arch/sh/include/cpu-sh4/cpu/dma.h b/arch/sh/include/cpu-sh4/cpu/dma.h
index ca747e9..a520eb2 100644
--- a/arch/sh/include/cpu-sh4/cpu/dma.h
+++ b/arch/sh/include/cpu-sh4/cpu/dma.h
@@ -1,32 +1,17 @@
 #ifndef __ASM_CPU_SH4_DMA_H
 #define __ASM_CPU_SH4_DMA_H
 
-/* SH7751/7760/7780 DMA IRQ sources */
+#include <linux/sh_intc.h>
 
-#ifdef CONFIG_CPU_SH4A
-
-#include <cpu/dma-sh4a.h>
-
-#else /* CONFIG_CPU_SH4A */
 /*
  * SH7750/SH7751/SH7760
  */
-#define DMTE0_IRQ	34
-#define DMTE4_IRQ	44
-#define DMTE6_IRQ	46
-#define DMAE0_IRQ	38
+#define DMTE0_IRQ	evt2irq(0x640)
+#define DMTE4_IRQ	evt2irq(0x780)
+#define DMTE6_IRQ	evt2irq(0x7c0)
+#define DMAE0_IRQ	evt2irq(0x6c0)
 
 #define SH_DMAC_BASE0	0xffa00000
 #define SH_DMAC_BASE1	0xffa00070
-/* Definitions for the SuperH DMAC */
-#define TM_BURST	0x00000080
-#define TS_8		0x00000010
-#define TS_16		0x00000020
-#define TS_32		0x00000030
-#define TS_64		0x00000000
-
-#define DMAOR_COD	0x00000008
-
-#endif
 
 #endif /* __ASM_CPU_SH4_DMA_H */
diff --git a/arch/sh/include/cpu-sh4/cpu/freq.h b/arch/sh/include/cpu-sh4/cpu/freq.h
index cffd25e..1631fc2 100644
--- a/arch/sh/include/cpu-sh4/cpu/freq.h
+++ b/arch/sh/include/cpu-sh4/cpu/freq.h
@@ -47,6 +47,11 @@
 #define MSTPCR1			0xa4150034
 #define MSTPCR2			0xa4150038
 
+#elif defined(CONFIG_CPU_SUBTYPE_SH7734)
+#define FRQCR0			0xffc80000
+#define FRQCR2			0xffc80008
+#define FRQMR1			0xffc80014
+#define FRQMR2			0xffc80018
 #elif defined(CONFIG_CPU_SUBTYPE_SH7785)
 #define FRQCR0			0xffc80000
 #define FRQCR1			0xffc80004
diff --git a/arch/sh/include/cpu-sh4/cpu/sh7734.h b/arch/sh/include/cpu-sh4/cpu/sh7734.h
new file mode 100644
index 0000000..2fb9a7b
--- /dev/null
+++ b/arch/sh/include/cpu-sh4/cpu/sh7734.h
@@ -0,0 +1,306 @@
+#ifndef __ASM_SH7734_H__
+#define __ASM_SH7734_H__
+
+/* Pin Function Controller:
+ * GPIO_FN_xx - GPIO used to select pin function
+ * GPIO_GP_x_x - GPIO mapped to real I/O pin on CPU
+ */
+enum {
+	GPIO_GP_0_0, GPIO_GP_0_1, GPIO_GP_0_2, GPIO_GP_0_3,
+	GPIO_GP_0_4, GPIO_GP_0_5, GPIO_GP_0_6, GPIO_GP_0_7,
+	GPIO_GP_0_8, GPIO_GP_0_9, GPIO_GP_0_10, GPIO_GP_0_11,
+	GPIO_GP_0_12, GPIO_GP_0_13, GPIO_GP_0_14, GPIO_GP_0_15,
+	GPIO_GP_0_16, GPIO_GP_0_17, GPIO_GP_0_18, GPIO_GP_0_19,
+	GPIO_GP_0_20, GPIO_GP_0_21, GPIO_GP_0_22, GPIO_GP_0_23,
+	GPIO_GP_0_24, GPIO_GP_0_25, GPIO_GP_0_26, GPIO_GP_0_27,
+	GPIO_GP_0_28, GPIO_GP_0_29, GPIO_GP_0_30, GPIO_GP_0_31,
+
+	GPIO_GP_1_0, GPIO_GP_1_1, GPIO_GP_1_2, GPIO_GP_1_3,
+	GPIO_GP_1_4, GPIO_GP_1_5, GPIO_GP_1_6, GPIO_GP_1_7,
+	GPIO_GP_1_8, GPIO_GP_1_9, GPIO_GP_1_10, GPIO_GP_1_11,
+	GPIO_GP_1_12, GPIO_GP_1_13, GPIO_GP_1_14, GPIO_GP_1_15,
+	GPIO_GP_1_16, GPIO_GP_1_17, GPIO_GP_1_18, GPIO_GP_1_19,
+	GPIO_GP_1_20, GPIO_GP_1_21, GPIO_GP_1_22, GPIO_GP_1_23,
+	GPIO_GP_1_24, GPIO_GP_1_25, GPIO_GP_1_26, GPIO_GP_1_27,
+	GPIO_GP_1_28, GPIO_GP_1_29, GPIO_GP_1_30, GPIO_GP_1_31,
+
+	GPIO_GP_2_0, GPIO_GP_2_1, GPIO_GP_2_2, GPIO_GP_2_3,
+	GPIO_GP_2_4, GPIO_GP_2_5, GPIO_GP_2_6, GPIO_GP_2_7,
+	GPIO_GP_2_8, GPIO_GP_2_9, GPIO_GP_2_10, GPIO_GP_2_11,
+	GPIO_GP_2_12, GPIO_GP_2_13, GPIO_GP_2_14, GPIO_GP_2_15,
+	GPIO_GP_2_16, GPIO_GP_2_17, GPIO_GP_2_18, GPIO_GP_2_19,
+	GPIO_GP_2_20, GPIO_GP_2_21, GPIO_GP_2_22, GPIO_GP_2_23,
+	GPIO_GP_2_24, GPIO_GP_2_25, GPIO_GP_2_26, GPIO_GP_2_27,
+	GPIO_GP_2_28, GPIO_GP_2_29, GPIO_GP_2_30, GPIO_GP_2_31,
+
+	GPIO_GP_3_0, GPIO_GP_3_1, GPIO_GP_3_2, GPIO_GP_3_3,
+	GPIO_GP_3_4, GPIO_GP_3_5, GPIO_GP_3_6, GPIO_GP_3_7,
+	GPIO_GP_3_8, GPIO_GP_3_9, GPIO_GP_3_10, GPIO_GP_3_11,
+	GPIO_GP_3_12, GPIO_GP_3_13, GPIO_GP_3_14, GPIO_GP_3_15,
+	GPIO_GP_3_16, GPIO_GP_3_17, GPIO_GP_3_18, GPIO_GP_3_19,
+	GPIO_GP_3_20, GPIO_GP_3_21, GPIO_GP_3_22, GPIO_GP_3_23,
+	GPIO_GP_3_24, GPIO_GP_3_25, GPIO_GP_3_26, GPIO_GP_3_27,
+	GPIO_GP_3_28, GPIO_GP_3_29, GPIO_GP_3_30, GPIO_GP_3_31,
+
+	GPIO_GP_4_0, GPIO_GP_4_1, GPIO_GP_4_2, GPIO_GP_4_3,
+	GPIO_GP_4_4, GPIO_GP_4_5, GPIO_GP_4_6, GPIO_GP_4_7,
+	GPIO_GP_4_8, GPIO_GP_4_9, GPIO_GP_4_10, GPIO_GP_4_11,
+	GPIO_GP_4_12, GPIO_GP_4_13, GPIO_GP_4_14, GPIO_GP_4_15,
+	GPIO_GP_4_16, GPIO_GP_4_17, GPIO_GP_4_18, GPIO_GP_4_19,
+	GPIO_GP_4_20, GPIO_GP_4_21, GPIO_GP_4_22, GPIO_GP_4_23,
+	GPIO_GP_4_24, GPIO_GP_4_25, GPIO_GP_4_26, GPIO_GP_4_27,
+	GPIO_GP_4_28, GPIO_GP_4_29, GPIO_GP_4_30, GPIO_GP_4_31,
+
+	GPIO_GP_5_0, GPIO_GP_5_1, GPIO_GP_5_2, GPIO_GP_5_3,
+	GPIO_GP_5_4, GPIO_GP_5_5, GPIO_GP_5_6, GPIO_GP_5_7,
+	GPIO_GP_5_8, GPIO_GP_5_9, GPIO_GP_5_10, GPIO_GP_5_11,
+
+	GPIO_FN_CLKOUT, GPIO_FN_BS, GPIO_FN_CS0, GPIO_FN_EX_CS0, GPIO_FN_RD,
+		GPIO_FN_WE0, GPIO_FN_WE1,
+
+	GPIO_FN_SCL0, GPIO_FN_PENC0, GPIO_FN_USB_OVC0,
+
+	GPIO_FN_IRQ2_B, GPIO_FN_IRQ3_B,
+
+	/* IPSR0 */
+	GPIO_FN_A15, GPIO_FN_ST0_VCO_CLKIN, GPIO_FN_LCD_DATA15_A,
+		GPIO_FN_TIOC3D_C,
+	GPIO_FN_A14, GPIO_FN_LCD_DATA14_A, GPIO_FN_TIOC3C_C,
+	GPIO_FN_A13, GPIO_FN_LCD_DATA13_A, GPIO_FN_TIOC3B_C,
+	GPIO_FN_A12, GPIO_FN_LCD_DATA12_A, GPIO_FN_TIOC3A_C,
+	GPIO_FN_A11, GPIO_FN_ST0_D7, GPIO_FN_LCD_DATA11_A,
+		GPIO_FN_TIOC2B_C,
+	GPIO_FN_A10, GPIO_FN_ST0_D6, GPIO_FN_LCD_DATA10_A,
+		GPIO_FN_TIOC2A_C,
+	GPIO_FN_A9, GPIO_FN_ST0_D5, GPIO_FN_LCD_DATA9_A,
+		GPIO_FN_TIOC1B_C,
+	GPIO_FN_A8, GPIO_FN_ST0_D4, GPIO_FN_LCD_DATA8_A,
+		GPIO_FN_TIOC1A_C,
+	GPIO_FN_A7, GPIO_FN_ST0_D3, GPIO_FN_LCD_DATA7_A, GPIO_FN_TIOC0D_C,
+	GPIO_FN_A6, GPIO_FN_ST0_D2, GPIO_FN_LCD_DATA6_A, GPIO_FN_TIOC0C_C,
+	GPIO_FN_A5, GPIO_FN_ST0_D1, GPIO_FN_LCD_DATA5_A, GPIO_FN_TIOC0B_C,
+	GPIO_FN_A4, GPIO_FN_ST0_D0, GPIO_FN_LCD_DATA4_A, GPIO_FN_TIOC0A_C,
+	GPIO_FN_A3, GPIO_FN_ST0_VLD, GPIO_FN_LCD_DATA3_A, GPIO_FN_TCLKD_C,
+	GPIO_FN_A2, GPIO_FN_ST0_SYC, GPIO_FN_LCD_DATA2_A, GPIO_FN_TCLKC_C,
+	GPIO_FN_A1, GPIO_FN_ST0_REQ, GPIO_FN_LCD_DATA1_A, GPIO_FN_TCLKB_C,
+	GPIO_FN_A0, GPIO_FN_ST0_CLKIN, GPIO_FN_LCD_DATA0_A, GPIO_FN_TCLKA_C,
+
+	/* IPSR1 */
+	GPIO_FN_D3, GPIO_FN_SD0_DAT3_A, GPIO_FN_MMC_D3_A, GPIO_FN_ST1_D6,
+		GPIO_FN_FD3_A,
+	GPIO_FN_D2, GPIO_FN_SD0_DAT2_A, GPIO_FN_MMC_D2_A, GPIO_FN_ST1_D5,
+		GPIO_FN_FD2_A,
+	GPIO_FN_D1, GPIO_FN_SD0_DAT1_A, GPIO_FN_MMC_D1_A, GPIO_FN_ST1_D4,
+		GPIO_FN_FD1_A,
+	GPIO_FN_D0, GPIO_FN_SD0_DAT0_A, GPIO_FN_MMC_D0_A, GPIO_FN_ST1_D3,
+		GPIO_FN_FD0_A,
+	GPIO_FN_A25, GPIO_FN_TX2_D, GPIO_FN_ST1_D2,
+	GPIO_FN_A24, GPIO_FN_RX2_D, GPIO_FN_ST1_D1,
+	GPIO_FN_A23, GPIO_FN_ST1_D0, GPIO_FN_LCD_M_DISP_A,
+	GPIO_FN_A22, GPIO_FN_ST1_VLD, GPIO_FN_LCD_VEPWC_A,
+	GPIO_FN_A21, GPIO_FN_ST1_SYC, GPIO_FN_LCD_VCPWC_A,
+	GPIO_FN_A20, GPIO_FN_ST1_REQ, GPIO_FN_LCD_FLM_A,
+	GPIO_FN_A19, GPIO_FN_ST1_CLKIN, GPIO_FN_LCD_CLK_A, GPIO_FN_TIOC4D_C,
+	GPIO_FN_A18, GPIO_FN_ST1_PWM, GPIO_FN_LCD_CL2_A, GPIO_FN_TIOC4C_C,
+	GPIO_FN_A17, GPIO_FN_ST1_VCO_CLKIN, GPIO_FN_LCD_CL1_A, GPIO_FN_TIOC4B_C,
+	GPIO_FN_A16, GPIO_FN_ST0_PWM, GPIO_FN_LCD_DON_A, GPIO_FN_TIOC4A_C,
+
+	/* IPSR2 */
+	GPIO_FN_D14, GPIO_FN_TX2_B, GPIO_FN_FSE_A, GPIO_FN_ET0_TX_CLK_B,
+	GPIO_FN_D13, GPIO_FN_RX2_B, GPIO_FN_FRB_A,	GPIO_FN_ET0_ETXD6_B,
+	GPIO_FN_D12, GPIO_FN_FWE_A, GPIO_FN_ET0_ETXD5_B,
+	GPIO_FN_D11, GPIO_FN_RSPI_MISO_A, GPIO_FN_QMI_QIO1_A,
+		GPIO_FN_FRE_A, GPIO_FN_ET0_ETXD3_B,
+	GPIO_FN_D10, GPIO_FN_RSPI_MOSI_A, GPIO_FN_QMO_QIO0_A,
+		GPIO_FN_FALE_A, GPIO_FN_ET0_ETXD2_B,
+	GPIO_FN_D9, GPIO_FN_SD0_CMD_A, GPIO_FN_MMC_CMD_A, GPIO_FN_QIO3_A,
+		GPIO_FN_FCLE_A, GPIO_FN_ET0_ETXD1_B,
+	GPIO_FN_D8, GPIO_FN_SD0_CLK_A, GPIO_FN_MMC_CLK_A, GPIO_FN_QIO2_A,
+		GPIO_FN_FCE_A, GPIO_FN_ET0_GTX_CLK_B,
+	GPIO_FN_D7, GPIO_FN_RSPI_SSL_A, GPIO_FN_MMC_D7_A, GPIO_FN_QSSL_A,
+		GPIO_FN_FD7_A,
+	GPIO_FN_D6, GPIO_FN_RSPI_RSPCK_A, GPIO_FN_MMC_D6_A, GPIO_FN_QSPCLK_A,
+		GPIO_FN_FD6_A,
+	GPIO_FN_D5, GPIO_FN_SD0_WP_A, GPIO_FN_MMC_D5_A, GPIO_FN_FD5_A,
+	GPIO_FN_D4, GPIO_FN_SD0_CD_A, GPIO_FN_MMC_D4_A, GPIO_FN_ST1_D7,
+		GPIO_FN_FD4_A,
+
+	/* IPSR3 */
+	GPIO_FN_DRACK0, GPIO_FN_SD1_DAT2_A, GPIO_FN_ATAG, GPIO_FN_TCLK1_A,
+		GPIO_FN_ET0_ETXD7,
+	GPIO_FN_EX_WAIT2, GPIO_FN_SD1_DAT1_A, GPIO_FN_DACK2, GPIO_FN_CAN1_RX_C,
+		GPIO_FN_ET0_MAGIC_C, GPIO_FN_ET0_ETXD6_A,
+	GPIO_FN_EX_WAIT1, GPIO_FN_SD1_DAT0_A, GPIO_FN_DREQ2, GPIO_FN_CAN1_TX_C,
+		GPIO_FN_ET0_LINK_C, GPIO_FN_ET0_ETXD5_A,
+	GPIO_FN_EX_WAIT0, GPIO_FN_TCLK1_B,
+	GPIO_FN_RD_WR, GPIO_FN_TCLK0,
+	GPIO_FN_EX_CS5, GPIO_FN_SD1_CMD_A, GPIO_FN_ATADIR, GPIO_FN_QSSL_B,
+		GPIO_FN_ET0_ETXD3_A,
+	GPIO_FN_EX_CS4, GPIO_FN_SD1_WP_A, GPIO_FN_ATAWR, GPIO_FN_QMI_QIO1_B,
+		GPIO_FN_ET0_ETXD2_A,
+	GPIO_FN_EX_CS3, GPIO_FN_SD1_CD_A, GPIO_FN_ATARD, GPIO_FN_QMO_QIO0_B,
+		GPIO_FN_ET0_ETXD1_A,
+	GPIO_FN_EX_CS2, GPIO_FN_TX3_B, GPIO_FN_ATACS1, GPIO_FN_QSPCLK_B,
+		GPIO_FN_ET0_GTX_CLK_A,
+	GPIO_FN_EX_CS1, GPIO_FN_RX3_B, GPIO_FN_ATACS0, GPIO_FN_QIO2_B,
+		GPIO_FN_ET0_ETXD0,
+	GPIO_FN_CS1_A26, GPIO_FN_QIO3_B,
+	GPIO_FN_D15, GPIO_FN_SCK2_B,
+
+	/* IPSR4 */
+	GPIO_FN_SCK2_A, GPIO_FN_VI0_G3,
+	GPIO_FN_RTS1_B, GPIO_FN_VI0_G2,
+	GPIO_FN_CTS1_B, GPIO_FN_VI0_DATA7_VI0_G1,
+	GPIO_FN_TX1_B, GPIO_FN_VI0_DATA6_VI0_G0, GPIO_FN_ET0_PHY_INT_A,
+	GPIO_FN_RX1_B, GPIO_FN_VI0_DATA5_VI0_B5, GPIO_FN_ET0_MAGIC_A,
+	GPIO_FN_SCK1_B, GPIO_FN_VI0_DATA4_VI0_B4, GPIO_FN_ET0_LINK_A,
+	GPIO_FN_RTS0_B, GPIO_FN_VI0_DATA3_VI0_B3, GPIO_FN_ET0_MDIO_A,
+	GPIO_FN_CTS0_B, GPIO_FN_VI0_DATA2_VI0_B2, GPIO_FN_RMII0_MDIO_A,
+		GPIO_FN_ET0_MDC,
+	GPIO_FN_HTX0_A, GPIO_FN_TX1_A, GPIO_FN_VI0_DATA1_VI0_B1,
+		GPIO_FN_RMII0_MDC_A, GPIO_FN_ET0_COL,
+	GPIO_FN_HRX0_A, GPIO_FN_RX1_A, GPIO_FN_VI0_DATA0_VI0_B0,
+		GPIO_FN_RMII0_CRS_DV_A, GPIO_FN_ET0_CRS,
+	GPIO_FN_HSCK0_A, GPIO_FN_SCK1_A, GPIO_FN_VI0_VSYNC,
+		GPIO_FN_RMII0_RX_ER_A, GPIO_FN_ET0_RX_ER,
+	GPIO_FN_HRTS0_A, GPIO_FN_RTS1_A, GPIO_FN_VI0_HSYNC,
+		GPIO_FN_RMII0_TXD_EN_A, GPIO_FN_ET0_RX_DV,
+	GPIO_FN_HCTS0_A, GPIO_FN_CTS1_A, GPIO_FN_VI0_FIELD,
+		GPIO_FN_RMII0_RXD1_A, GPIO_FN_ET0_ERXD7,
+
+	/* IPSR5 */
+	GPIO_FN_SD2_CLK_A, GPIO_FN_RX2_A, GPIO_FN_VI0_G4, GPIO_FN_ET0_RX_CLK_B,
+	GPIO_FN_SD2_CMD_A, GPIO_FN_TX2_A, GPIO_FN_VI0_G5, GPIO_FN_ET0_ERXD2_B,
+	GPIO_FN_SD2_DAT0_A, GPIO_FN_RX3_A, GPIO_FN_VI0_R0, GPIO_FN_ET0_ERXD3_B,
+	GPIO_FN_SD2_DAT1_A, GPIO_FN_TX3_A, GPIO_FN_VI0_R1, GPIO_FN_ET0_MDIO_B,
+	GPIO_FN_SD2_DAT2_A, GPIO_FN_RX4_A, GPIO_FN_VI0_R2, GPIO_FN_ET0_LINK_B,
+	GPIO_FN_SD2_DAT3_A, GPIO_FN_TX4_A, GPIO_FN_VI0_R3, GPIO_FN_ET0_MAGIC_B,
+	GPIO_FN_SD2_CD_A, GPIO_FN_RX5_A, GPIO_FN_VI0_R4, GPIO_FN_ET0_PHY_INT_B,
+	GPIO_FN_SD2_WP_A, GPIO_FN_TX5_A, GPIO_FN_VI0_R5,
+	GPIO_FN_REF125CK, GPIO_FN_ADTRG, GPIO_FN_RX5_C,
+	GPIO_FN_REF50CK, GPIO_FN_CTS1_E, GPIO_FN_HCTS0_D,
+
+	/* IPSR6 */
+	GPIO_FN_DU0_DR0, GPIO_FN_SCIF_CLK_B, GPIO_FN_HRX0_D, GPIO_FN_IETX_A,
+		GPIO_FN_TCLKA_A, GPIO_FN_HIFD00,
+	GPIO_FN_DU0_DR1, GPIO_FN_SCK0_B, GPIO_FN_HTX0_D, GPIO_FN_IERX_A,
+		GPIO_FN_TCLKB_A, GPIO_FN_HIFD01,
+	GPIO_FN_DU0_DR2, GPIO_FN_RX0_B, GPIO_FN_TCLKC_A, GPIO_FN_HIFD02,
+	GPIO_FN_DU0_DR3, GPIO_FN_TX0_B, GPIO_FN_TCLKD_A, GPIO_FN_HIFD03,
+	GPIO_FN_DU0_DR4, GPIO_FN_CTS0_C, GPIO_FN_TIOC0A_A, GPIO_FN_HIFD04,
+	GPIO_FN_DU0_DR5, GPIO_FN_RTS0_C, GPIO_FN_TIOC0B_A, GPIO_FN_HIFD05,
+	GPIO_FN_DU0_DR6, GPIO_FN_SCK1_C, GPIO_FN_TIOC0C_A, GPIO_FN_HIFD06,
+	GPIO_FN_DU0_DR7, GPIO_FN_RX1_C, GPIO_FN_TIOC0D_A, GPIO_FN_HIFD07,
+	GPIO_FN_DU0_DG0, GPIO_FN_TX1_C, GPIO_FN_HSCK0_D, GPIO_FN_IECLK_A,
+		GPIO_FN_TIOC1A_A, GPIO_FN_HIFD08,
+	GPIO_FN_DU0_DG1, GPIO_FN_CTS1_C, GPIO_FN_HRTS0_D, GPIO_FN_TIOC1B_A,
+		GPIO_FN_HIFD09,
+
+	/* IPSR7 */
+	GPIO_FN_DU0_DG2, GPIO_FN_RTS1_C, GPIO_FN_RMII0_MDC_B, GPIO_FN_TIOC2A_A,
+		GPIO_FN_HIFD10,
+	GPIO_FN_DU0_DG3, GPIO_FN_SCK2_C, GPIO_FN_RMII0_MDIO_B, GPIO_FN_TIOC2B_A,
+		GPIO_FN_HIFD11,
+	GPIO_FN_DU0_DG4, GPIO_FN_RX2_C, GPIO_FN_RMII0_CRS_DV_B,
+		GPIO_FN_TIOC3A_A, GPIO_FN_HIFD12,
+	GPIO_FN_DU0_DG5, GPIO_FN_TX2_C, GPIO_FN_RMII0_RX_ER_B,
+		GPIO_FN_TIOC3B_A, GPIO_FN_HIFD13,
+	GPIO_FN_DU0_DG6, GPIO_FN_RX3_C, GPIO_FN_RMII0_RXD0_B,
+		GPIO_FN_TIOC3C_A, GPIO_FN_HIFD14,
+	GPIO_FN_DU0_DG7, GPIO_FN_TX3_C, GPIO_FN_RMII0_RXD1_B,
+		GPIO_FN_TIOC3D_A, GPIO_FN_HIFD15,
+	GPIO_FN_DU0_DB0, GPIO_FN_RX4_C, GPIO_FN_RMII0_TXD_EN_B,
+		GPIO_FN_TIOC4A_A, GPIO_FN_HIFCS,
+	GPIO_FN_DU0_DB1, GPIO_FN_TX4_C, GPIO_FN_RMII0_TXD0_B,
+		GPIO_FN_TIOC4B_A, GPIO_FN_HIFRS,
+	GPIO_FN_DU0_DB2, GPIO_FN_RX5_B, GPIO_FN_RMII0_TXD1_B,
+		GPIO_FN_TIOC4C_A, GPIO_FN_HIFWR,
+	GPIO_FN_DU0_DB3, GPIO_FN_TX5_B, GPIO_FN_TIOC4D_A, GPIO_FN_HIFRD,
+	GPIO_FN_DU0_DB4, GPIO_FN_HIFINT,
+
+	/* IPSR8 */
+	GPIO_FN_DU0_DB5, GPIO_FN_HIFDREQ,
+	GPIO_FN_DU0_DB6, GPIO_FN_HIFRDY,
+	GPIO_FN_DU0_DB7, GPIO_FN_SSI_SCK0_B, GPIO_FN_HIFEBL_B,
+	GPIO_FN_DU0_DOTCLKIN, GPIO_FN_HSPI_CS0_C, GPIO_FN_SSI_WS0_B,
+	GPIO_FN_DU0_DOTCLKOUT, GPIO_FN_HSPI_CLK0_C, GPIO_FN_SSI_SDATA0_B,
+	GPIO_FN_DU0_EXHSYNC_DU0_HSYNC, GPIO_FN_HSPI_TX0_C, GPIO_FN_SSI_SCK1_B,
+	GPIO_FN_DU0_EXVSYNC_DU0_VSYNC, GPIO_FN_HSPI_RX0_C, GPIO_FN_SSI_WS1_B,
+	GPIO_FN_DU0_EXODDF_DU0_ODDF, GPIO_FN_CAN0_RX_B, GPIO_FN_HSCK0_B,
+		GPIO_FN_SSI_SDATA1_B,
+	GPIO_FN_DU0_DISP, GPIO_FN_CAN0_TX_B, GPIO_FN_HRX0_B,
+		GPIO_FN_AUDIO_CLKA_B,
+	GPIO_FN_DU0_CDE, GPIO_FN_HTX0_B, GPIO_FN_AUDIO_CLKB_B,
+		GPIO_FN_LCD_VCPWC_B,
+	GPIO_FN_IRQ0_A, GPIO_FN_HSPI_TX_B, GPIO_FN_RX3_E, GPIO_FN_ET0_ERXD0,
+	GPIO_FN_IRQ1_A, GPIO_FN_HSPI_RX_B, GPIO_FN_TX3_E, GPIO_FN_ET0_ERXD1,
+	GPIO_FN_IRQ2_A, GPIO_FN_CTS0_A, GPIO_FN_HCTS0_B, GPIO_FN_ET0_ERXD2_A,
+	GPIO_FN_IRQ3_A, GPIO_FN_RTS0_A, GPIO_FN_HRTS0_B, GPIO_FN_ET0_ERXD3_A,
+
+	/* IPSR9 */
+	GPIO_FN_VI1_CLK_A, GPIO_FN_FD0_B, GPIO_FN_LCD_DATA0_B,
+	GPIO_FN_VI1_0_A, GPIO_FN_FD1_B, GPIO_FN_LCD_DATA1_B,
+	GPIO_FN_VI1_1_A, GPIO_FN_FD2_B, GPIO_FN_LCD_DATA2_B,
+	GPIO_FN_VI1_2_A, GPIO_FN_FD3_B, GPIO_FN_LCD_DATA3_B,
+	GPIO_FN_VI1_3_A, GPIO_FN_FD4_B, GPIO_FN_LCD_DATA4_B,
+	GPIO_FN_VI1_4_A, GPIO_FN_FD5_B, GPIO_FN_LCD_DATA5_B,
+	GPIO_FN_VI1_5_A, GPIO_FN_FD6_B, GPIO_FN_LCD_DATA6_B,
+	GPIO_FN_VI1_6_A, GPIO_FN_FD7_B, GPIO_FN_LCD_DATA7_B,
+	GPIO_FN_VI1_7_A, GPIO_FN_FCE_B, GPIO_FN_LCD_DATA8_B,
+	GPIO_FN_SSI_SCK0_A, GPIO_FN_TIOC1A_B, GPIO_FN_LCD_DATA9_B,
+	GPIO_FN_SSI_WS0_A, GPIO_FN_TIOC1B_B, GPIO_FN_LCD_DATA10_B,
+	GPIO_FN_SSI_SDATA0_A, GPIO_FN_VI1_0_B, GPIO_FN_TIOC2A_B,
+		GPIO_FN_LCD_DATA11_B,
+	GPIO_FN_SSI_SCK1_A, GPIO_FN_VI1_1_B, GPIO_FN_TIOC2B_B,
+		GPIO_FN_LCD_DATA12_B,
+	GPIO_FN_SSI_WS1_A, GPIO_FN_VI1_2_B, GPIO_FN_LCD_DATA13_B,
+	GPIO_FN_SSI_SDATA1_A, GPIO_FN_VI1_3_B, GPIO_FN_LCD_DATA14_B,
+
+	/* IPSR10 */
+	GPIO_FN_SSI_SCK23, GPIO_FN_VI1_4_B, GPIO_FN_RX1_D, GPIO_FN_FCLE_B,
+		GPIO_FN_LCD_DATA15_B,
+	GPIO_FN_SSI_WS23, GPIO_FN_VI1_5_B, GPIO_FN_TX1_D, GPIO_FN_HSCK0_C,
+		GPIO_FN_FALE_B, GPIO_FN_LCD_DON_B,
+	GPIO_FN_SSI_SDATA2, GPIO_FN_VI1_6_B, GPIO_FN_HRX0_C, GPIO_FN_FRE_B,
+		GPIO_FN_LCD_CL1_B,
+	GPIO_FN_SSI_SDATA3, GPIO_FN_VI1_7_B, GPIO_FN_HTX0_C, GPIO_FN_FWE_B,
+		GPIO_FN_LCD_CL2_B,
+	GPIO_FN_AUDIO_CLKA_A, GPIO_FN_VI1_CLK_B, GPIO_FN_SCK1_D,
+		GPIO_FN_IECLK_B, GPIO_FN_LCD_FLM_B,
+	GPIO_FN_AUDIO_CLKB_A, GPIO_FN_LCD_CLK_B,
+	GPIO_FN_AUDIO_CLKC, GPIO_FN_SCK1_E, GPIO_FN_HCTS0_C, GPIO_FN_FRB_B,
+		GPIO_FN_LCD_VEPWC_B,
+	GPIO_FN_AUDIO_CLKOUT, GPIO_FN_TX1_E, GPIO_FN_HRTS0_C, GPIO_FN_FSE_B,
+		GPIO_FN_LCD_M_DISP_B,
+	GPIO_FN_CAN_CLK_A, GPIO_FN_RX4_D,
+	GPIO_FN_CAN0_TX_A, GPIO_FN_TX4_D, GPIO_FN_MLB_CLK,
+	GPIO_FN_CAN1_RX_A, GPIO_FN_IRQ1_B,
+	GPIO_FN_CAN0_RX_A, GPIO_FN_IRQ0_B, GPIO_FN_MLB_SIG,
+	GPIO_FN_CAN1_TX_A, GPIO_FN_TX5_C, GPIO_FN_MLB_DAT,
+
+	/* IPSR11 */
+	GPIO_FN_SCL1, GPIO_FN_SCIF_CLK_C,
+	GPIO_FN_SDA1, GPIO_FN_RX1_E,
+	GPIO_FN_SDA0, GPIO_FN_HIFEBL_A,
+	GPIO_FN_SDSELF, GPIO_FN_RTS1_E,
+	GPIO_FN_SCIF_CLK_A, GPIO_FN_HSPI_CLK_A, GPIO_FN_VI0_CLK,
+		GPIO_FN_RMII0_TXD0_A, GPIO_FN_ET0_ERXD4,
+	GPIO_FN_SCK0_A, GPIO_FN_HSPI_CS_A, GPIO_FN_VI0_CLKENB,
+		GPIO_FN_RMII0_TXD1_A, GPIO_FN_ET0_ERXD5,
+	GPIO_FN_RX0_A, GPIO_FN_HSPI_RX_A, GPIO_FN_RMII0_RXD0_A,
+		GPIO_FN_ET0_ERXD6,
+	GPIO_FN_TX0_A, GPIO_FN_HSPI_TX_A,
+	GPIO_FN_PENC1, GPIO_FN_TX3_D, GPIO_FN_CAN1_TX_B, GPIO_FN_TX5_D,
+		GPIO_FN_IETX_B,
+	GPIO_FN_USB_OVC1, GPIO_FN_RX3_D, GPIO_FN_CAN1_RX_B, GPIO_FN_RX5_D,
+		GPIO_FN_IERX_B,
+	GPIO_FN_DREQ0, GPIO_FN_SD1_CLK_A, GPIO_FN_ET0_TX_EN,
+	GPIO_FN_DACK0, GPIO_FN_SD1_DAT3_A, GPIO_FN_ET0_TX_ER,
+	GPIO_FN_DREQ1, GPIO_FN_HSPI_CLK_B, GPIO_FN_RX4_B, GPIO_FN_ET0_PHY_INT_C,
+		GPIO_FN_ET0_TX_CLK_A,
+	GPIO_FN_DACK1, GPIO_FN_HSPI_CS_B, GPIO_FN_TX4_B, GPIO_FN_ET0_RX_CLK_A,
+	GPIO_FN_PRESETOUT, GPIO_FN_ST_CLKOUT,
+
+};
+
+#endif /* __ASM_SH7734_H__ */
diff --git a/arch/sh/include/cpu-sh4a/cpu/dma.h b/arch/sh/include/cpu-sh4a/cpu/dma.h
new file mode 100644
index 0000000..89afb65
--- /dev/null
+++ b/arch/sh/include/cpu-sh4a/cpu/dma.h
@@ -0,0 +1,72 @@
+#ifndef __ASM_SH_CPU_SH4_DMA_SH7780_H
+#define __ASM_SH_CPU_SH4_DMA_SH7780_H
+
+#include <linux/sh_intc.h>
+
+#if defined(CONFIG_CPU_SUBTYPE_SH7343) || \
+	defined(CONFIG_CPU_SUBTYPE_SH7730)
+#define DMTE0_IRQ	evt2irq(0x800)
+#define DMTE4_IRQ	evt2irq(0xb80)
+#define DMAE0_IRQ	evt2irq(0xbc0)	/* DMA Error IRQ*/
+#define SH_DMAC_BASE0	0xFE008020
+#elif defined(CONFIG_CPU_SUBTYPE_SH7722)
+#define DMTE0_IRQ	evt2irq(0x800)
+#define DMTE4_IRQ	evt2irq(0xb80)
+#define DMAE0_IRQ	evt2irq(0xbc0)	/* DMA Error IRQ*/
+#define SH_DMAC_BASE0	0xFE008020
+#elif defined(CONFIG_CPU_SUBTYPE_SH7763) || \
+	defined(CONFIG_CPU_SUBTYPE_SH7764)
+#define DMTE0_IRQ	evt2irq(0x640)
+#define DMTE4_IRQ	evt2irq(0x780)
+#define DMAE0_IRQ	evt2irq(0x6c0)
+#define SH_DMAC_BASE0	0xFF608020
+#elif defined(CONFIG_CPU_SUBTYPE_SH7723)
+#define DMTE0_IRQ	evt2irq(0x800)	/* DMAC0A*/
+#define DMTE4_IRQ	evt2irq(0xb80)	/* DMAC0B */
+#define DMTE6_IRQ	evt2irq(0x700)
+#define DMTE8_IRQ	evt2irq(0x740)	/* DMAC1A */
+#define DMTE9_IRQ	evt2irq(0x760)
+#define DMTE10_IRQ	evt2irq(0xb00)	/* DMAC1B */
+#define DMTE11_IRQ	evt2irq(0xb20)
+#define DMAE0_IRQ	evt2irq(0xbc0)	/* DMA Error IRQ*/
+#define DMAE1_IRQ	evt2irq(0xb40)	/* DMA Error IRQ*/
+#define SH_DMAC_BASE0	0xFE008020
+#define SH_DMAC_BASE1	0xFDC08020
+#elif defined(CONFIG_CPU_SUBTYPE_SH7724)
+#define DMTE0_IRQ	evt2irq(0x800)	/* DMAC0A*/
+#define DMTE4_IRQ	evt2irq(0xb80)	/* DMAC0B */
+#define DMTE6_IRQ	evt2irq(0x700)
+#define DMTE8_IRQ	evt2irq(0x740)	/* DMAC1A */
+#define DMTE9_IRQ	evt2irq(0x760)
+#define DMTE10_IRQ	evt2irq(0xb00)	/* DMAC1B */
+#define DMTE11_IRQ	evt2irq(0xb20)
+#define DMAE0_IRQ	evt2irq(0xbc0)	/* DMA Error IRQ*/
+#define DMAE1_IRQ	evt2irq(0xb40)	/* DMA Error IRQ*/
+#define SH_DMAC_BASE0	0xFE008020
+#define SH_DMAC_BASE1	0xFDC08020
+#elif defined(CONFIG_CPU_SUBTYPE_SH7780)
+#define DMTE0_IRQ	evt2irq(0x640)
+#define DMTE4_IRQ	evt2irq(0x780)
+#define DMTE6_IRQ	evt2irq(0x7c0)
+#define DMTE8_IRQ	evt2irq(0xd80)
+#define DMTE9_IRQ	evt2irq(0xda0)
+#define DMTE10_IRQ	evt2irq(0xdc0)
+#define DMTE11_IRQ	evt2irq(0xde0)
+#define DMAE0_IRQ	evt2irq(0x6c0)	/* DMA Error IRQ */
+#define SH_DMAC_BASE0	0xFC808020
+#define SH_DMAC_BASE1	0xFC818020
+#else /* SH7785 */
+#define DMTE0_IRQ	evt2irq(0x620)
+#define DMTE4_IRQ	evt2irq(0x6a0)
+#define DMTE6_IRQ	evt2irq(0x880)
+#define DMTE8_IRQ	evt2irq(0x8c0)
+#define DMTE9_IRQ	evt2irq(0x8e0)
+#define DMTE10_IRQ	evt2irq(0x900)
+#define DMTE11_IRQ	evt2irq(0x920)
+#define DMAE0_IRQ	evt2irq(0x6e0)	/* DMA Error IRQ0 */
+#define DMAE1_IRQ	evt2irq(0x940)	/* DMA Error IRQ1 */
+#define SH_DMAC_BASE0	0xFC808020
+#define SH_DMAC_BASE1	0xFCC08020
+#endif
+
+#endif /* __ASM_SH_CPU_SH4_DMA_SH7780_H */
diff --git a/arch/sh/include/cpu-sh5/cpu/dma.h b/arch/sh/include/cpu-sh5/cpu/dma.h
deleted file mode 100644
index 7bf6bb3..0000000
--- a/arch/sh/include/cpu-sh5/cpu/dma.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef __ASM_SH_CPU_SH5_DMA_H
-#define __ASM_SH_CPU_SH5_DMA_H
-
-/* Nothing yet */
-
-#endif /* __ASM_SH_CPU_SH5_DMA_H */
diff --git a/arch/sh/include/mach-common/mach/hp6xx.h b/arch/sh/include/mach-common/mach/hp6xx.h
index bcc301a..6aaaf85 100644
--- a/arch/sh/include/mach-common/mach/hp6xx.h
+++ b/arch/sh/include/mach-common/mach/hp6xx.h
@@ -9,10 +9,11 @@
  * for more details.
  *
  */
+#include <linux/sh_intc.h>
 
-#define HP680_BTN_IRQ		32	/* IRQ0_IRQ */
-#define HP680_TS_IRQ		35	/* IRQ3_IRQ */
-#define HP680_HD64461_IRQ	36	/* IRQ4_IRQ */
+#define HP680_BTN_IRQ		evt2irq(0x600)	/* IRQ0_IRQ */
+#define HP680_TS_IRQ		evt2irq(0x660)	/* IRQ3_IRQ */
+#define HP680_HD64461_IRQ	evt2irq(0x680)	/* IRQ4_IRQ */
 
 #define DAC_LCD_BRIGHTNESS	0
 #define DAC_SPEAKER_VOLUME	1
diff --git a/arch/sh/include/mach-common/mach/lboxre2.h b/arch/sh/include/mach-common/mach/lboxre2.h
index e6d1605..3a4dcc5 100644
--- a/arch/sh/include/mach-common/mach/lboxre2.h
+++ b/arch/sh/include/mach-common/mach/lboxre2.h
@@ -11,13 +11,14 @@
  * for more details.
  *
  */
+#include <linux/sh_intc.h>
 
-#define IRQ_CF1		9	/* CF1 */
-#define IRQ_CF0		10	/* CF0 */
-#define IRQ_INTD	11	/* INTD */
-#define IRQ_ETH1	12	/* Ether1 */
-#define IRQ_ETH0	13	/* Ether0 */
-#define IRQ_INTA	14	/* INTA */
+#define IRQ_CF1		evt2irq(0x320)	/* CF1 */
+#define IRQ_CF0		evt2irq(0x340)	/* CF0 */
+#define IRQ_INTD	evt2irq(0x360)	/* INTD */
+#define IRQ_ETH1	evt2irq(0x380)	/* Ether1 */
+#define IRQ_ETH0	evt2irq(0x3a0)	/* Ether0 */
+#define IRQ_INTA	evt2irq(0x3c0)	/* INTA */
 
 void init_lboxre2_IRQ(void);
 
diff --git a/arch/sh/include/mach-common/mach/sdk7780.h b/arch/sh/include/mach-common/mach/sdk7780.h
index 697dc86..ce64e02 100644
--- a/arch/sh/include/mach-common/mach/sdk7780.h
+++ b/arch/sh/include/mach-common/mach/sdk7780.h
@@ -11,6 +11,7 @@
  * License.  See the file "COPYING" in the main directory of this archive
  * for more details.
  */
+#include <linux/sh_intc.h>
 #include <asm/addrspace.h>
 
 /* Box specific addresses.  */
@@ -67,9 +68,9 @@
 
 #define SDK7780_NR_IRL			15
 /* IDE/ATA interrupt */
-#define IRQ_CFCARD				14
+#define IRQ_CFCARD			evt2irq(0x3c0)
 /* SMC interrupt */
-#define IRQ_ETHERNET			6
+#define IRQ_ETHERNET			evt2irq(0x2c0)
 
 
 /* arch/sh/boards/renesas/sdk7780/irq.c */
diff --git a/arch/sh/include/mach-common/mach/titan.h b/arch/sh/include/mach-common/mach/titan.h
index 4a674d2..fa3cd80 100644
--- a/arch/sh/include/mach-common/mach/titan.h
+++ b/arch/sh/include/mach-common/mach/titan.h
@@ -4,14 +4,16 @@
 #ifndef _ASM_SH_TITAN_H
 #define _ASM_SH_TITAN_H
 
+#include <linux/sh_intc.h>
+
 #define __IO_PREFIX titan
 #include <asm/io_generic.h>
 
 /* IRQ assignments */
-#define TITAN_IRQ_WAN		2	/* eth0 (WAN) */
-#define TITAN_IRQ_LAN		5	/* eth1 (LAN) */
-#define TITAN_IRQ_MPCIA		8	/* mPCI A */
-#define TITAN_IRQ_MPCIB		11	/* mPCI B */
-#define TITAN_IRQ_USB		11	/* USB */
+#define TITAN_IRQ_WAN		evt2irq(0x240)	/* eth0 (WAN) */
+#define TITAN_IRQ_LAN		evt2irq(0x2a0)	/* eth1 (LAN) */
+#define TITAN_IRQ_MPCIA		evt2irq(0x300)	/* mPCI A */
+#define TITAN_IRQ_MPCIB		evt2irq(0x360)	/* mPCI B */
+#define TITAN_IRQ_USB		evt2irq(0x360)	/* USB */
 
 #endif /* __ASM_SH_TITAN_H */
diff --git a/arch/sh/include/mach-dreamcast/mach/dma.h b/arch/sh/include/mach-dreamcast/mach/dma.h
index ddd68e7..1dbfdf7 100644
--- a/arch/sh/include/mach-dreamcast/mach/dma.h
+++ b/arch/sh/include/mach-dreamcast/mach/dma.h
@@ -11,9 +11,7 @@
 #define __ASM_SH_DREAMCAST_DMA_H
 
 /* Number of DMA channels */
-#define ONCHIP_NR_DMA_CHANNELS	4
 #define G2_NR_DMA_CHANNELS	4
-#define PVR2_NR_DMA_CHANNELS	1
 
 /* Channels for cascading */
 #define PVR2_CASCADE_CHAN	2
diff --git a/arch/sh/include/mach-landisk/mach/iodata_landisk.h b/arch/sh/include/mach-landisk/mach/iodata_landisk.h
index f432773..ceeea48 100644
--- a/arch/sh/include/mach-landisk/mach/iodata_landisk.h
+++ b/arch/sh/include/mach-landisk/mach/iodata_landisk.h
@@ -8,6 +8,7 @@
  *
  * IO-DATA LANDISK support
  */
+#include <linux/sh_intc.h>
 
 /* Box specific addresses.  */
 
@@ -25,15 +26,15 @@
 #define PA_PIDE_OFFSET	0x40		/* CF IDE Offset */
 #define PA_SIDE_OFFSET	0x40		/* HDD IDE Offset */
 
-#define IRQ_PCIINTA	5		/* PCI INTA IRQ */
-#define IRQ_PCIINTB	6		/* PCI INTB IRQ */
-#define IRQ_PCIINTC	7		/* PCI INTC IRQ */
-#define IRQ_PCIINTD	8		/* PCI INTD IRQ */
-#define IRQ_ATA		9		/* ATA IRQ */
-#define IRQ_FATA	10		/* FATA IRQ */
-#define IRQ_POWER	11		/* Power Switch IRQ */
-#define IRQ_BUTTON	12		/* USL-5P Button IRQ */
-#define IRQ_FAULT	13		/* USL-5P Fault  IRQ */
+#define IRQ_PCIINTA	evt2irq(0x2a0)	/* PCI INTA IRQ */
+#define IRQ_PCIINTB	evt2irq(0x2c0)	/* PCI INTB IRQ */
+#define IRQ_PCIINTC	evt2irq(0x2e0)	/* PCI INTC IRQ */
+#define IRQ_PCIINTD	evt2irq(0x300)	/* PCI INTD IRQ */
+#define IRQ_ATA		evt2irq(0x320)	/* ATA IRQ */
+#define IRQ_FATA	evt2irq(0x340)	/* FATA IRQ */
+#define IRQ_POWER	evt2irq(0x360)	/* Power Switch IRQ */
+#define IRQ_BUTTON	evt2irq(0x380)	/* USL-5P Button IRQ */
+#define IRQ_FAULT	evt2irq(0x3a0)	/* USL-5P Fault  IRQ */
 
 void init_landisk_IRQ(void);
 
diff --git a/arch/sh/include/mach-se/mach/se.h b/arch/sh/include/mach-se/mach/se.h
index 14be91c..8a6d44b 100644
--- a/arch/sh/include/mach-se/mach/se.h
+++ b/arch/sh/include/mach-se/mach/se.h
@@ -8,6 +8,7 @@
  *
  * Hitachi SolutionEngine support
  */
+#include <linux/sh_intc.h>
 
 /* Box specific addresses.  */
 
@@ -82,16 +83,16 @@
 #define INTC_IPRD       0xa4000018UL
 #define INTC_IPRE       0xa400001aUL
 
-#define IRQ0_IRQ        32
-#define IRQ1_IRQ        33
+#define IRQ0_IRQ        evt2irq(0x600)
+#define IRQ1_IRQ        evt2irq(0x620)
 #endif
 
 #if defined(CONFIG_CPU_SUBTYPE_SH7705)
-#define IRQ_STNIC	12
-#define IRQ_CFCARD	14
+#define IRQ_STNIC	evt2irq(0x380)
+#define IRQ_CFCARD	evt2irq(0x3c0)
 #else
-#define IRQ_STNIC	10
-#define IRQ_CFCARD	7
+#define IRQ_STNIC	evt2irq(0x340)
+#define IRQ_CFCARD	evt2irq(0x2e0)
 #endif
 
 /* SH Ether support (SH7710/SH7712) */
@@ -105,9 +106,9 @@
 # define PHY_ID 0x01
 #endif
 /* Ether IRQ */
-#define SH_ETH0_IRQ	80
-#define SH_ETH1_IRQ	81
-#define SH_TSU_IRQ	82
+#define SH_ETH0_IRQ	evt2irq(0xc00)
+#define SH_ETH1_IRQ	evt2irq(0xc20)
+#define SH_TSU_IRQ	evt2irq(0xc40)
 
 void init_se_IRQ(void);
 
diff --git a/arch/sh/include/mach-se/mach/se7343.h b/arch/sh/include/mach-se/mach/se7343.h
index 8d8170d..50b5d57 100644
--- a/arch/sh/include/mach-se/mach/se7343.h
+++ b/arch/sh/include/mach-se/mach/se7343.h
@@ -8,6 +8,7 @@
  *
  * SH-Mobile SolutionEngine 7343 support
  */
+#include <linux/sh_intc.h>
 
 /* Box specific addresses.  */
 
@@ -118,10 +119,10 @@
 #define FPGA_IN		0xb1400000
 #define FPGA_OUT	0xb1400002
 
-#define IRQ0_IRQ        32
-#define IRQ1_IRQ        33
-#define IRQ4_IRQ        36
-#define IRQ5_IRQ        37
+#define IRQ0_IRQ        evt2irq(0x600)
+#define IRQ1_IRQ        evt2irq(0x620)
+#define IRQ4_IRQ        evt2irq(0x680)
+#define IRQ5_IRQ        evt2irq(0x6a0)
 
 #define SE7343_FPGA_IRQ_MRSHPC0	0
 #define SE7343_FPGA_IRQ_MRSHPC1	1
diff --git a/arch/sh/include/mach-se/mach/se7721.h b/arch/sh/include/mach-se/mach/se7721.h
index b957f60..eabd053 100644
--- a/arch/sh/include/mach-se/mach/se7721.h
+++ b/arch/sh/include/mach-se/mach/se7721.h
@@ -11,6 +11,8 @@
 
 #ifndef __ASM_SH_SE7721_H
 #define __ASM_SH_SE7721_H
+
+#include <linux/sh_intc.h>
 #include <asm/addrspace.h>
 
 /* Box specific addresses. */
@@ -49,9 +51,9 @@
 #define MRSHPC_PCIC_INFO	(PA_MRSHPC + 30)
 
 #define PA_LED		0xB6800000	/* 8bit LED */
-#define PA_FPGA		0xB7000000 	/* FPGA base address */
+#define PA_FPGA		0xB7000000	/* FPGA base address */
 
-#define MRSHPC_IRQ0	10
+#define MRSHPC_IRQ0	evt2irq(0x340)
 
 #define FPGA_ILSR1	(PA_FPGA + 0x02)
 #define FPGA_ILSR2	(PA_FPGA + 0x03)
diff --git a/arch/sh/include/mach-se/mach/se7722.h b/arch/sh/include/mach-se/mach/se7722.h
index 16505bf..201081e 100644
--- a/arch/sh/include/mach-se/mach/se7722.h
+++ b/arch/sh/include/mach-se/mach/se7722.h
@@ -13,6 +13,7 @@
  * for more details.
  *
  */
+#include <linux/sh_intc.h>
 #include <asm/addrspace.h>
 
 /* Box specific addresses.  */
@@ -31,7 +32,7 @@
 
 #define PA_PERIPHERAL	0xB0000000
 
-#define PA_PCIC         PA_PERIPHERAL   		/* MR-SHPC-01 PCMCIA */
+#define PA_PCIC         PA_PERIPHERAL		/* MR-SHPC-01 PCMCIA */
 #define PA_MRSHPC       (PA_PERIPHERAL + 0x003fffe0)    /* MR-SHPC-01 PCMCIA controller */
 #define PA_MRSHPC_MW1   (PA_PERIPHERAL + 0x00400000)    /* MR-SHPC-01 memory window base */
 #define PA_MRSHPC_MW2   (PA_PERIPHERAL + 0x00500000)    /* MR-SHPC-01 attribute window base */
@@ -51,7 +52,7 @@
 #define MRSHPC_PCIC_INFO (PA_MRSHPC + 30)
 
 #define PA_LED		(PA_PERIPHERAL + 0x00800000)	/* 8bit LED */
-#define PA_FPGA		(PA_PERIPHERAL + 0x01800000) 	/* FPGA base address */
+#define PA_FPGA		(PA_PERIPHERAL + 0x01800000)	/* FPGA base address */
 
 #define PA_LAN		(PA_AREA6_IO + 0)		/* SMC LAN91C111 */
 /* GPIO */
@@ -77,8 +78,8 @@
 #define PORT_HIZCRC     0xA405015CUL
 
 /* IRQ */
-#define IRQ0_IRQ        32
-#define IRQ1_IRQ        33
+#define IRQ0_IRQ        evt2irq(0x600)
+#define IRQ1_IRQ        evt2irq(0x620)
 
 #define IRQ01_MODE      0xb1800000
 #define IRQ01_STS       0xb1800004
diff --git a/arch/sh/include/mach-se/mach/se7724.h b/arch/sh/include/mach-se/mach/se7724.h
index 29514a3..be842dd 100644
--- a/arch/sh/include/mach-se/mach/se7724.h
+++ b/arch/sh/include/mach-se/mach/se7724.h
@@ -18,6 +18,7 @@
  * for more details.
  *
  */
+#include <linux/sh_intc.h>
 #include <asm/addrspace.h>
 
 /* SH Eth */
@@ -35,9 +36,9 @@
 #define IRQ2_MR		(0xba200028)
 
 /* IRQ */
-#define IRQ0_IRQ        32
-#define IRQ1_IRQ        33
-#define IRQ2_IRQ        34
+#define IRQ0_IRQ        evt2irq(0x600)
+#define IRQ1_IRQ        evt2irq(0x620)
+#define IRQ2_IRQ        evt2irq(0x640)
 
 /* Bits in IRQ012 registers */
 #define SE7724_FPGA_IRQ_BASE	220
diff --git a/arch/sh/include/mach-se/mach/se7751.h b/arch/sh/include/mach-se/mach/se7751.h
index b36792a..2718717 100644
--- a/arch/sh/include/mach-se/mach/se7751.h
+++ b/arch/sh/include/mach-se/mach/se7751.h
@@ -11,6 +11,7 @@
  * Modified for 7751 Solution Engine by
  * Ian da Silva and Jeremy Siegel, 2001.
  */
+#include <linux/sh_intc.h>
 
 /* Box specific addresses.  */
 
@@ -63,7 +64,7 @@
 #define BCR_ILCRF	(PA_BCR + 10)
 #define BCR_ILCRG	(PA_BCR + 12)
 
-#define IRQ_79C973	13
+#define IRQ_79C973	evt2irq(0x3a0)
 
 void init_7751se_IRQ(void);
 
diff --git a/arch/sh/include/mach-se/mach/se7780.h b/arch/sh/include/mach-se/mach/se7780.h
index 40e9b41..bde357c 100644
--- a/arch/sh/include/mach-se/mach/se7780.h
+++ b/arch/sh/include/mach-se/mach/se7780.h
@@ -12,6 +12,7 @@
  * License.  See the file "COPYING" in the main directory of this archive
  * for more details.
  */
+#include <linux/sh_intc.h>
 #include <asm/addrspace.h>
 
 /* Box specific addresses.  */
@@ -80,13 +81,13 @@
 #define IRQPOS_PCCPW            (0 * 4)
 
 /* IDE interrupt */
-#define IRQ_IDE0                67 /* iVDR */
+#define IRQ_IDE0                evt2irq(0xa60) /* iVDR */
 
 /* SMC interrupt */
-#define SMC_IRQ                 8
+#define SMC_IRQ                 evt2irq(0x300)
 
 /* SM501 interrupt */
-#define SM501_IRQ               0
+#define SM501_IRQ               evt2irq(0x200)
 
 /* interrupt pin */
 #define IRQPIN_EXTINT1          0 /* IRQ0 pin */
diff --git a/arch/sh/kernel/cpu/proc.c b/arch/sh/kernel/cpu/proc.c
index f47be87..9e6624c 100644
--- a/arch/sh/kernel/cpu/proc.c
+++ b/arch/sh/kernel/cpu/proc.c
@@ -7,6 +7,7 @@
 static const char *cpu_name[] = {
 	[CPU_SH7201]	= "SH7201",
 	[CPU_SH7203]	= "SH7203",	[CPU_SH7263]	= "SH7263",
+	[CPU_SH7264]	= "SH7264",	[CPU_SH7269]	= "SH7269",
 	[CPU_SH7206]	= "SH7206",	[CPU_SH7619]	= "SH7619",
 	[CPU_SH7705]	= "SH7705",	[CPU_SH7706]	= "SH7706",
 	[CPU_SH7707]	= "SH7707",	[CPU_SH7708]	= "SH7708",
@@ -25,7 +26,8 @@
 	[CPU_SH5_101]	= "SH5-101",	[CPU_SH5_103]	= "SH5-103",
 	[CPU_MXG]	= "MX-G",	[CPU_SH7723]	= "SH7723",
 	[CPU_SH7366]	= "SH7366",	[CPU_SH7724]	= "SH7724",
-	[CPU_SH7372]	= "SH7372",	[CPU_SH_NONE]	= "Unknown"
+	[CPU_SH7372]	= "SH7372",	[CPU_SH7734]	= "SH7734",
+	[CPU_SH_NONE]	= "Unknown"
 };
 
 const char *get_cpu_subtype(struct sh_cpuinfo *c)
diff --git a/arch/sh/kernel/cpu/sh2/setup-sh7619.c b/arch/sh/kernel/cpu/sh2/setup-sh7619.c
index 0f8befc..e0b740c 100644
--- a/arch/sh/kernel/cpu/sh2/setup-sh7619.c
+++ b/arch/sh/kernel/cpu/sh2/setup-sh7619.c
@@ -65,7 +65,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCIF,
-	.irqs		= { 88, 88, 88, 88 },
+	.irqs		= SCIx_IRQ_MUXED(88),
 };
 
 static struct platform_device scif0_device = {
@@ -82,7 +82,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCIF,
-	.irqs		= { 92, 92, 92, 92 },
+	.irqs		= SCIx_IRQ_MUXED(92),
 };
 
 static struct platform_device scif1_device = {
@@ -99,7 +99,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCIF,
-	.irqs		= { 96, 96, 96, 96 },
+	.irqs		= SCIx_IRQ_MUXED(96),
 };
 
 static struct platform_device scif2_device = {
diff --git a/arch/sh/kernel/cpu/sh2a/Makefile b/arch/sh/kernel/cpu/sh2a/Makefile
index 45f85c7..7fdc102 100644
--- a/arch/sh/kernel/cpu/sh2a/Makefile
+++ b/arch/sh/kernel/cpu/sh2a/Makefile
@@ -11,10 +11,14 @@
 obj-$(CONFIG_CPU_SUBTYPE_SH7201)	+= setup-sh7201.o clock-sh7201.o
 obj-$(CONFIG_CPU_SUBTYPE_SH7203)	+= setup-sh7203.o clock-sh7203.o
 obj-$(CONFIG_CPU_SUBTYPE_SH7263)	+= setup-sh7203.o clock-sh7203.o
+obj-$(CONFIG_CPU_SUBTYPE_SH7264)	+= setup-sh7264.o clock-sh7264.o
 obj-$(CONFIG_CPU_SUBTYPE_SH7206)	+= setup-sh7206.o clock-sh7206.o
+obj-$(CONFIG_CPU_SUBTYPE_SH7269)	+= setup-sh7269.o clock-sh7269.o
 obj-$(CONFIG_CPU_SUBTYPE_MXG)		+= setup-mxg.o clock-sh7206.o
 
 # Pinmux setup
 pinmux-$(CONFIG_CPU_SUBTYPE_SH7203)	:= pinmux-sh7203.o
+pinmux-$(CONFIG_CPU_SUBTYPE_SH7264)	:= pinmux-sh7264.o
+pinmux-$(CONFIG_CPU_SUBTYPE_SH7269)	:= pinmux-sh7269.o
 
 obj-$(CONFIG_GENERIC_GPIO)	+= $(pinmux-y)
diff --git a/arch/sh/kernel/cpu/sh2a/clock-sh7264.c b/arch/sh/kernel/cpu/sh2a/clock-sh7264.c
new file mode 100644
index 0000000..fdf585c
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh2a/clock-sh7264.c
@@ -0,0 +1,153 @@
+/*
+ * arch/sh/kernel/cpu/sh2a/clock-sh7264.c
+ *
+ * SH7264 clock framework support
+ *
+ * Copyright (C) 2012  Phil Edworthy
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/clkdev.h>
+#include <asm/clock.h>
+
+/* SH7264 registers */
+#define FRQCR		0xfffe0010
+#define STBCR3		0xfffe0408
+#define STBCR4		0xfffe040c
+#define STBCR5		0xfffe0410
+#define STBCR6		0xfffe0414
+#define STBCR7		0xfffe0418
+#define STBCR8		0xfffe041c
+
+static const unsigned int pll1rate[] = {8, 12};
+
+static unsigned int pll1_div;
+
+/* Fixed 32 KHz root clock for RTC */
+static struct clk r_clk = {
+	.rate           = 32768,
+};
+
+/*
+ * Default rate for the root input clock, reset this with clk_set_rate()
+ * from the platform code.
+ */
+static struct clk extal_clk = {
+	.rate		= 18000000,
+};
+
+static unsigned long pll_recalc(struct clk *clk)
+{
+	unsigned long rate = clk->parent->rate / pll1_div;
+	return rate * pll1rate[(__raw_readw(FRQCR) >> 8) & 1];
+}
+
+static struct sh_clk_ops pll_clk_ops = {
+	.recalc		= pll_recalc,
+};
+
+static struct clk pll_clk = {
+	.ops		= &pll_clk_ops,
+	.parent		= &extal_clk,
+	.flags		= CLK_ENABLE_ON_INIT,
+};
+
+struct clk *main_clks[] = {
+	&r_clk,
+	&extal_clk,
+	&pll_clk,
+};
+
+static int div2[] = { 1, 2, 3, 4, 6, 8, 12 };
+
+static struct clk_div_mult_table div4_div_mult_table = {
+	.divisors = div2,
+	.nr_divisors = ARRAY_SIZE(div2),
+};
+
+static struct clk_div4_table div4_table = {
+	.div_mult_table = &div4_div_mult_table,
+};
+
+enum { DIV4_I, DIV4_P,
+       DIV4_NR };
+
+#define DIV4(_reg, _bit, _mask, _flags) \
+  SH_CLK_DIV4(&pll_clk, _reg, _bit, _mask, _flags)
+
+/* The mask field specifies the div2 entries that are valid */
+struct clk div4_clks[DIV4_NR] = {
+	[DIV4_I] = DIV4(FRQCR, 4, 0x7,  CLK_ENABLE_REG_16BIT
+					| CLK_ENABLE_ON_INIT),
+	[DIV4_P] = DIV4(FRQCR, 0, 0x78, CLK_ENABLE_REG_16BIT),
+};
+
+enum {	MSTP77, MSTP74, MSTP72,
+	MSTP60,
+	MSTP35, MSTP34, MSTP33, MSTP32, MSTP30,
+	MSTP_NR };
+
+static struct clk mstp_clks[MSTP_NR] = {
+	[MSTP77] = SH_CLK_MSTP8(&div4_clks[DIV4_P], STBCR7, 7, 0), /* SCIF */
+	[MSTP74] = SH_CLK_MSTP8(&div4_clks[DIV4_P], STBCR7, 4, 0), /* VDC */
+	[MSTP72] = SH_CLK_MSTP8(&div4_clks[DIV4_P], STBCR7, 2, 0), /* CMT */
+	[MSTP60] = SH_CLK_MSTP8(&div4_clks[DIV4_P], STBCR6, 0, 0), /* USB */
+	[MSTP35] = SH_CLK_MSTP8(&div4_clks[DIV4_P], STBCR3, 6, 0), /* MTU2 */
+	[MSTP34] = SH_CLK_MSTP8(&div4_clks[DIV4_P], STBCR3, 4, 0), /* SDHI0 */
+	[MSTP33] = SH_CLK_MSTP8(&div4_clks[DIV4_P], STBCR3, 3, 0), /* SDHI1 */
+	[MSTP32] = SH_CLK_MSTP8(&div4_clks[DIV4_P], STBCR3, 2, 0), /* ADC */
+	[MSTP30] = SH_CLK_MSTP8(&r_clk, STBCR3, 0, 0),	/* RTC */
+};
+
+static struct clk_lookup lookups[] = {
+	/* main clocks */
+	CLKDEV_CON_ID("rclk", &r_clk),
+	CLKDEV_CON_ID("extal", &extal_clk),
+	CLKDEV_CON_ID("pll_clk", &pll_clk),
+
+	/* DIV4 clocks */
+	CLKDEV_CON_ID("cpu_clk", &div4_clks[DIV4_I]),
+	CLKDEV_CON_ID("peripheral_clk", &div4_clks[DIV4_P]),
+
+	/* MSTP clocks */
+	CLKDEV_CON_ID("sci_ick", &mstp_clks[MSTP77]),
+	CLKDEV_CON_ID("vdc3", &mstp_clks[MSTP74]),
+	CLKDEV_CON_ID("cmt_fck", &mstp_clks[MSTP72]),
+	CLKDEV_CON_ID("usb0", &mstp_clks[MSTP60]),
+	CLKDEV_CON_ID("mtu2_fck", &mstp_clks[MSTP35]),
+	CLKDEV_CON_ID("sdhi0", &mstp_clks[MSTP34]),
+	CLKDEV_CON_ID("sdhi1", &mstp_clks[MSTP33]),
+	CLKDEV_CON_ID("adc0", &mstp_clks[MSTP32]),
+	CLKDEV_CON_ID("rtc0", &mstp_clks[MSTP30]),
+};
+
+int __init arch_clk_init(void)
+{
+	int k, ret = 0;
+
+	if (test_mode_pin(MODE_PIN0)) {
+		if (test_mode_pin(MODE_PIN1))
+			pll1_div = 3;
+		else
+			pll1_div = 4;
+	} else
+		pll1_div = 1;
+
+	for (k = 0; !ret && (k < ARRAY_SIZE(main_clks)); k++)
+		ret = clk_register(main_clks[k]);
+
+	clkdev_add_table(lookups, ARRAY_SIZE(lookups));
+
+	if (!ret)
+		ret = sh_clk_div4_register(div4_clks, DIV4_NR, &div4_table);
+
+	if (!ret)
+		ret = sh_clk_mstp_register(mstp_clks, MSTP_NR);
+
+	return ret;
+}
diff --git a/arch/sh/kernel/cpu/sh2a/clock-sh7269.c b/arch/sh/kernel/cpu/sh2a/clock-sh7269.c
new file mode 100644
index 0000000..6b78762
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh2a/clock-sh7269.c
@@ -0,0 +1,184 @@
+/*
+ * arch/sh/kernel/cpu/sh2a/clock-sh7269.c
+ *
+ * SH7269 clock framework support
+ *
+ * Copyright (C) 2012  Phil Edworthy
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/clkdev.h>
+#include <asm/clock.h>
+
+/* SH7269 registers */
+#define FRQCR		0xfffe0010
+#define STBCR3 		0xfffe0408
+#define STBCR4 		0xfffe040c
+#define STBCR5 		0xfffe0410
+#define STBCR6 		0xfffe0414
+#define STBCR7 		0xfffe0418
+
+#define PLL_RATE 20
+
+/* Fixed 32 KHz root clock for RTC */
+static struct clk r_clk = {
+	.rate           = 32768,
+};
+
+/*
+ * Default rate for the root input clock, reset this with clk_set_rate()
+ * from the platform code.
+ */
+static struct clk extal_clk = {
+	.rate		= 13340000,
+};
+
+static unsigned long pll_recalc(struct clk *clk)
+{
+	return clk->parent->rate * PLL_RATE;
+}
+
+static struct sh_clk_ops pll_clk_ops = {
+	.recalc		= pll_recalc,
+};
+
+static struct clk pll_clk = {
+	.ops		= &pll_clk_ops,
+	.parent		= &extal_clk,
+	.flags		= CLK_ENABLE_ON_INIT,
+};
+
+static unsigned long peripheral0_recalc(struct clk *clk)
+{
+	return clk->parent->rate / 8;
+}
+
+static struct sh_clk_ops peripheral0_clk_ops = {
+	.recalc		= peripheral0_recalc,
+};
+
+static struct clk peripheral0_clk = {
+	.ops		= &peripheral0_clk_ops,
+	.parent		= &pll_clk,
+	.flags		= CLK_ENABLE_ON_INIT,
+};
+
+static unsigned long peripheral1_recalc(struct clk *clk)
+{
+	return clk->parent->rate / 4;
+}
+
+static struct sh_clk_ops peripheral1_clk_ops = {
+	.recalc		= peripheral1_recalc,
+};
+
+static struct clk peripheral1_clk = {
+	.ops		= &peripheral1_clk_ops,
+	.parent		= &pll_clk,
+	.flags		= CLK_ENABLE_ON_INIT,
+};
+
+struct clk *main_clks[] = {
+	&r_clk,
+	&extal_clk,
+	&pll_clk,
+	&peripheral0_clk,
+	&peripheral1_clk,
+};
+
+static int div2[] = { 1, 2, 0, 4 };
+
+static struct clk_div_mult_table div4_div_mult_table = {
+	.divisors = div2,
+	.nr_divisors = ARRAY_SIZE(div2),
+};
+
+static struct clk_div4_table div4_table = {
+	.div_mult_table = &div4_div_mult_table,
+};
+
+enum { DIV4_I, DIV4_B,
+       DIV4_NR };
+
+#define DIV4(_reg, _bit, _mask, _flags) \
+  SH_CLK_DIV4(&pll_clk, _reg, _bit, _mask, _flags)
+
+/* The mask field specifies the div2 entries that are valid */
+struct clk div4_clks[DIV4_NR] = {
+	[DIV4_I]  = DIV4(FRQCR, 8, 0xB, CLK_ENABLE_REG_16BIT
+					| CLK_ENABLE_ON_INIT),
+	[DIV4_B]  = DIV4(FRQCR, 4, 0xA, CLK_ENABLE_REG_16BIT
+					| CLK_ENABLE_ON_INIT),
+};
+
+enum { MSTP72,
+	MSTP60,
+	MSTP47, MSTP46, MSTP45, MSTP44, MSTP43, MSTP42, MSTP41, MSTP40,
+	MSTP35, MSTP32, MSTP30,
+	MSTP_NR };
+
+static struct clk mstp_clks[MSTP_NR] = {
+	[MSTP72] = SH_CLK_MSTP8(&peripheral0_clk, STBCR7, 2, 0), /* CMT */
+	[MSTP60] = SH_CLK_MSTP8(&peripheral1_clk, STBCR6, 0, 0), /* USB */
+	[MSTP47] = SH_CLK_MSTP8(&peripheral1_clk, STBCR4, 7, 0), /* SCIF0 */
+	[MSTP46] = SH_CLK_MSTP8(&peripheral1_clk, STBCR4, 6, 0), /* SCIF1 */
+	[MSTP45] = SH_CLK_MSTP8(&peripheral1_clk, STBCR4, 5, 0), /* SCIF2 */
+	[MSTP44] = SH_CLK_MSTP8(&peripheral1_clk, STBCR4, 4, 0), /* SCIF3 */
+	[MSTP43] = SH_CLK_MSTP8(&peripheral1_clk, STBCR4, 3, 0), /* SCIF4 */
+	[MSTP42] = SH_CLK_MSTP8(&peripheral1_clk, STBCR4, 2, 0), /* SCIF5 */
+	[MSTP41] = SH_CLK_MSTP8(&peripheral1_clk, STBCR4, 1, 0), /* SCIF6 */
+	[MSTP40] = SH_CLK_MSTP8(&peripheral1_clk, STBCR4, 0, 0), /* SCIF7 */
+	[MSTP35] = SH_CLK_MSTP8(&peripheral0_clk, STBCR3, 5, 0), /* MTU2 */
+	[MSTP32] = SH_CLK_MSTP8(&peripheral1_clk, STBCR3, 2, 0), /* ADC */
+	[MSTP30] = SH_CLK_MSTP8(&r_clk, STBCR3, 0, 0), /* RTC */
+};
+
+static struct clk_lookup lookups[] = {
+	/* main clocks */
+	CLKDEV_CON_ID("rclk", &r_clk),
+	CLKDEV_CON_ID("extal", &extal_clk),
+	CLKDEV_CON_ID("pll_clk", &pll_clk),
+	CLKDEV_CON_ID("peripheral_clk", &peripheral1_clk),
+
+	/* DIV4 clocks */
+	CLKDEV_CON_ID("cpu_clk", &div4_clks[DIV4_I]),
+	CLKDEV_CON_ID("bus_clk", &div4_clks[DIV4_B]),
+
+	/* MSTP clocks */
+	CLKDEV_ICK_ID("sci_fck", "sh-sci.0", &mstp_clks[MSTP47]),
+	CLKDEV_ICK_ID("sci_fck", "sh-sci.1", &mstp_clks[MSTP46]),
+	CLKDEV_ICK_ID("sci_fck", "sh-sci.2", &mstp_clks[MSTP45]),
+	CLKDEV_ICK_ID("sci_fck", "sh-sci.3", &mstp_clks[MSTP44]),
+	CLKDEV_ICK_ID("sci_fck", "sh-sci.4", &mstp_clks[MSTP43]),
+	CLKDEV_ICK_ID("sci_fck", "sh-sci.5", &mstp_clks[MSTP42]),
+	CLKDEV_ICK_ID("sci_fck", "sh-sci.6", &mstp_clks[MSTP41]),
+	CLKDEV_ICK_ID("sci_fck", "sh-sci.7", &mstp_clks[MSTP40]),
+	CLKDEV_CON_ID("cmt_fck", &mstp_clks[MSTP72]),
+	CLKDEV_CON_ID("usb0", &mstp_clks[MSTP60]),
+	CLKDEV_CON_ID("mtu2_fck", &mstp_clks[MSTP35]),
+	CLKDEV_CON_ID("adc0", &mstp_clks[MSTP32]),
+	CLKDEV_CON_ID("rtc0", &mstp_clks[MSTP30]),
+};
+
+int __init arch_clk_init(void)
+{
+	int k, ret = 0;
+
+	for (k = 0; !ret && (k < ARRAY_SIZE(main_clks)); k++)
+		ret = clk_register(main_clks[k]);
+
+	clkdev_add_table(lookups, ARRAY_SIZE(lookups));
+
+	if (!ret)
+		ret = sh_clk_div4_register(div4_clks, DIV4_NR, &div4_table);
+
+	if (!ret)
+		ret = sh_clk_mstp_register(mstp_clks, MSTP_NR);
+
+	return ret;
+}
diff --git a/arch/sh/kernel/cpu/sh2a/pinmux-sh7264.c b/arch/sh/kernel/cpu/sh2a/pinmux-sh7264.c
new file mode 100644
index 0000000..b055b55
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh2a/pinmux-sh7264.c
@@ -0,0 +1,2136 @@
+/*
+ * SH7264 Pinmux
+ *
+ *  Copyright (C) 2012  Renesas Electronics Europe Ltd
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <cpu/sh7264.h>
+
+enum {
+	PINMUX_RESERVED = 0,
+
+	PINMUX_DATA_BEGIN,
+	/* Port A */
+	PA3_DATA, PA2_DATA, PA1_DATA, PA0_DATA,
+	/* Port B */
+	PB22_DATA, PB21_DATA, PB20_DATA,
+	PB19_DATA, PB18_DATA, PB17_DATA, PB16_DATA,
+	PB15_DATA, PB14_DATA, PB13_DATA, PB12_DATA,
+	PB11_DATA, PB10_DATA, PB9_DATA, PB8_DATA,
+	PB7_DATA, PB6_DATA, PB5_DATA, PB4_DATA,
+	PB3_DATA, PB2_DATA, PB1_DATA,
+	/* Port C */
+	PC10_DATA, PC9_DATA, PC8_DATA,
+	PC7_DATA, PC6_DATA, PC5_DATA, PC4_DATA,
+	PC3_DATA, PC2_DATA, PC1_DATA, PC0_DATA,
+	/* Port D */
+	PD15_DATA, PD14_DATA, PD13_DATA, PD12_DATA,
+	PD11_DATA, PD10_DATA, PD9_DATA, PD8_DATA,
+	PD7_DATA, PD6_DATA, PD5_DATA, PD4_DATA,
+	PD3_DATA, PD2_DATA, PD1_DATA, PD0_DATA,
+	/* Port E */
+	PE5_DATA, PE4_DATA,
+	PE3_DATA, PE2_DATA, PE1_DATA, PE0_DATA,
+	/* Port F */
+	PF12_DATA,
+	PF11_DATA, PF10_DATA, PF9_DATA, PF8_DATA,
+	PF7_DATA, PF6_DATA, PF5_DATA, PF4_DATA,
+	PF3_DATA, PF2_DATA, PF1_DATA, PF0_DATA,
+	/* Port G */
+	PG24_DATA,
+	PG23_DATA, PG22_DATA, PG21_DATA, PG20_DATA,
+	PG19_DATA, PG18_DATA, PG17_DATA, PG16_DATA,
+	PG15_DATA, PG14_DATA, PG13_DATA, PG12_DATA,
+	PG11_DATA, PG10_DATA, PG9_DATA, PG8_DATA,
+	PG7_DATA, PG6_DATA, PG5_DATA, PG4_DATA,
+	PG3_DATA, PG2_DATA, PG1_DATA, PG0_DATA,
+	/* Port H */
+	/* NOTE - Port H does not have a Data Register, but PH Data is
+	   connected to PH Port Register */
+	PH7_DATA, PH6_DATA, PH5_DATA, PH4_DATA,
+	PH3_DATA, PH2_DATA, PH1_DATA, PH0_DATA,
+	/* Port I - not on device */
+	/* Port J */
+	PJ12_DATA,
+	PJ11_DATA, PJ10_DATA, PJ9_DATA, PJ8_DATA,
+	PJ7_DATA, PJ6_DATA, PJ5_DATA, PJ4_DATA,
+	PJ3_DATA, PJ2_DATA, PJ1_DATA, PJ0_DATA,
+	/* Port K */
+	PK12_DATA,
+	PK11_DATA, PK10_DATA, PK9_DATA, PK8_DATA,
+	PK7_DATA, PK6_DATA, PK5_DATA, PK4_DATA,
+	PK3_DATA, PK2_DATA, PK1_DATA, PK0_DATA,
+	PINMUX_DATA_END,
+
+	PINMUX_INPUT_BEGIN,
+	FORCE_IN,
+	/* Port A */
+	PA3_IN, PA2_IN, PA1_IN, PA0_IN,
+	/* Port B */
+	PB22_IN, PB21_IN, PB20_IN,
+	PB19_IN, PB18_IN, PB17_IN, PB16_IN,
+	PB15_IN, PB14_IN, PB13_IN, PB12_IN,
+	PB11_IN, PB10_IN, PB9_IN, PB8_IN,
+	PB7_IN, PB6_IN, PB5_IN, PB4_IN,
+	PB3_IN, PB2_IN, PB1_IN,
+	/* Port C */
+	PC10_IN, PC9_IN, PC8_IN,
+	PC7_IN, PC6_IN, PC5_IN, PC4_IN,
+	PC3_IN, PC2_IN, PC1_IN, PC0_IN,
+	/* Port D */
+	PD15_IN, PD14_IN, PD13_IN, PD12_IN,
+	PD11_IN, PD10_IN, PD9_IN, PD8_IN,
+	PD7_IN, PD6_IN, PD5_IN, PD4_IN,
+	PD3_IN, PD2_IN, PD1_IN, PD0_IN,
+	/* Port E */
+	PE5_IN, PE4_IN,
+	PE3_IN, PE2_IN, PE1_IN, PE0_IN,
+	/* Port F */
+	PF12_IN,
+	PF11_IN, PF10_IN, PF9_IN, PF8_IN,
+	PF7_IN, PF6_IN, PF5_IN, PF4_IN,
+	PF3_IN, PF2_IN, PF1_IN, PF0_IN,
+	/* Port G */
+	PG24_IN,
+	PG23_IN, PG22_IN, PG21_IN, PG20_IN,
+	PG19_IN, PG18_IN, PG17_IN, PG16_IN,
+	PG15_IN, PG14_IN, PG13_IN, PG12_IN,
+	PG11_IN, PG10_IN, PG9_IN, PG8_IN,
+	PG7_IN, PG6_IN, PG5_IN, PG4_IN,
+	PG3_IN, PG2_IN, PG1_IN, PG0_IN,
+	/* Port H - Port H does not have a Data Register */
+	/* Port I - not on device */
+	/* Port J */
+	PJ12_IN,
+	PJ11_IN, PJ10_IN, PJ9_IN, PJ8_IN,
+	PJ7_IN, PJ6_IN, PJ5_IN, PJ4_IN,
+	PJ3_IN, PJ2_IN, PJ1_IN, PJ0_IN,
+	/* Port K */
+	PK12_IN,
+	PK11_IN, PK10_IN, PK9_IN, PK8_IN,
+	PK7_IN, PK6_IN, PK5_IN, PK4_IN,
+	PK3_IN, PK2_IN, PK1_IN, PK0_IN,
+	PINMUX_INPUT_END,
+
+	PINMUX_OUTPUT_BEGIN,
+	FORCE_OUT,
+	/* Port A */
+	PA3_OUT, PA2_OUT, PA1_OUT, PA0_OUT,
+	/* Port B */
+	PB22_OUT, PB21_OUT, PB20_OUT,
+	PB19_OUT, PB18_OUT, PB17_OUT, PB16_OUT,
+	PB15_OUT, PB14_OUT, PB13_OUT, PB12_OUT,
+	PB11_OUT, PB10_OUT, PB9_OUT, PB8_OUT,
+	PB7_OUT, PB6_OUT, PB5_OUT, PB4_OUT,
+	PB3_OUT, PB2_OUT, PB1_OUT,
+	/* Port C */
+	PC10_OUT, PC9_OUT, PC8_OUT,
+	PC7_OUT, PC6_OUT, PC5_OUT, PC4_OUT,
+	PC3_OUT, PC2_OUT, PC1_OUT, PC0_OUT,
+	/* Port D */
+	PD15_OUT, PD14_OUT, PD13_OUT, PD12_OUT,
+	PD11_OUT, PD10_OUT, PD9_OUT, PD8_OUT,
+	PD7_OUT, PD6_OUT, PD5_OUT, PD4_OUT,
+	PD3_OUT, PD2_OUT, PD1_OUT, PD0_OUT,
+	/* Port E */
+	PE5_OUT, PE4_OUT,
+	PE3_OUT, PE2_OUT, PE1_OUT, PE0_OUT,
+	/* Port F */
+	PF12_OUT,
+	PF11_OUT, PF10_OUT, PF9_OUT, PF8_OUT,
+	PF7_OUT, PF6_OUT, PF5_OUT, PF4_OUT,
+	PF3_OUT, PF2_OUT, PF1_OUT, PF0_OUT,
+	/* Port G */
+	PG24_OUT,
+	PG23_OUT, PG22_OUT, PG21_OUT, PG20_OUT,
+	PG19_OUT, PG18_OUT, PG17_OUT, PG16_OUT,
+	PG15_OUT, PG14_OUT, PG13_OUT, PG12_OUT,
+	PG11_OUT, PG10_OUT, PG9_OUT, PG8_OUT,
+	PG7_OUT, PG6_OUT, PG5_OUT, PG4_OUT,
+	PG3_OUT, PG2_OUT, PG1_OUT, PG0_OUT,
+	/* Port H - Port H does not have a Data Register */
+	/* Port I - not on device */
+	/* Port J */
+	PJ12_OUT,
+	PJ11_OUT, PJ10_OUT, PJ9_OUT, PJ8_OUT,
+	PJ7_OUT, PJ6_OUT, PJ5_OUT, PJ4_OUT,
+	PJ3_OUT, PJ2_OUT, PJ1_OUT, PJ0_OUT,
+	/* Port K */
+	PK12_OUT,
+	PK11_OUT, PK10_OUT, PK9_OUT, PK8_OUT,
+	PK7_OUT, PK6_OUT, PK5_OUT, PK4_OUT,
+	PK3_OUT, PK2_OUT, PK1_OUT, PK0_OUT,
+	PINMUX_OUTPUT_END,
+
+	PINMUX_FUNCTION_BEGIN,
+	/* Port A */
+	PA3_IOR_IN, PA3_IOR_OUT,
+	PA2_IOR_IN, PA2_IOR_OUT,
+	PA1_IOR_IN, PA1_IOR_OUT,
+	PA0_IOR_IN, PA0_IOR_OUT,
+
+	/* Port B */
+	PB11_IOR_IN, PB11_IOR_OUT,
+	PB10_IOR_IN, PB10_IOR_OUT,
+	PB9_IOR_IN, PB9_IOR_OUT,
+	PB8_IOR_IN, PB8_IOR_OUT,
+
+	PB22MD_00, PB22MD_01, PB22MD_10,
+	PB21MD_0, PB21MD_1,
+	PB20MD_0, PB20MD_1,
+	PB19MD_00, PB19MD_01, PB19MD_10, PB19MD_11,
+	PB18MD_00, PB18MD_01, PB18MD_10, PB18MD_11,
+	PB17MD_00, PB17MD_01, PB17MD_10, PB17MD_11,
+	PB16MD_00, PB16MD_01, PB16MD_10, PB16MD_11,
+	PB15MD_00, PB15MD_01, PB15MD_10, PB15MD_11,
+	PB14MD_00, PB14MD_01, PB14MD_10, PB14MD_11,
+	PB13MD_00, PB13MD_01, PB13MD_10, PB13MD_11,
+	PB12MD_00, PB12MD_01, PB12MD_10, PB12MD_11,
+	PB11MD_00, PB11MD_01, PB11MD_10, PB11MD_11,
+	PB10MD_00, PB10MD_01, PB10MD_10, PB10MD_11,
+	PB9MD_00, PB9MD_01, PB9MD_10, PB9MD_11,
+	PB8MD_00, PB8MD_01, PB8MD_10, PB8MD_11,
+	PB7MD_00, PB7MD_01, PB7MD_10, PB7MD_11,
+	PB6MD_00, PB6MD_01, PB6MD_10, PB6MD_11,
+	PB5MD_00, PB5MD_01, PB5MD_10, PB5MD_11,
+	PB4MD_00, PB4MD_01, PB4MD_10, PB4MD_11,
+	PB3MD_0, PB3MD_1,
+	PB2MD_0, PB2MD_1,
+	PB1MD_0, PB1MD_1,
+
+	/* Port C */
+	PC14_IOR_IN, PC14_IOR_OUT,
+	PC13_IOR_IN, PC13_IOR_OUT,
+	PC12_IOR_IN, PC12_IOR_OUT,
+	PC11_IOR_IN, PC11_IOR_OUT,
+	PC10_IOR_IN, PC10_IOR_OUT,
+	PC9_IOR_IN, PC9_IOR_OUT,
+	PC8_IOR_IN, PC8_IOR_OUT,
+	PC7_IOR_IN, PC7_IOR_OUT,
+	PC6_IOR_IN, PC6_IOR_OUT,
+	PC5_IOR_IN, PC5_IOR_OUT,
+	PC4_IOR_IN, PC4_IOR_OUT,
+	PC3_IOR_IN, PC3_IOR_OUT,
+	PC2_IOR_IN, PC2_IOR_OUT,
+	PC1_IOR_IN, PC1_IOR_OUT,
+	PC0_IOR_IN, PC0_IOR_OUT,
+
+	PC10MD_0, PC10MD_1,
+	PC9MD_0, PC9MD_1,
+	PC8MD_00, PC8MD_01, PC8MD_10, PC8MD_11,
+	PC7MD_00, PC7MD_01, PC7MD_10, PC7MD_11,
+	PC6MD_00, PC6MD_01, PC6MD_10, PC6MD_11,
+	PC5MD_00, PC5MD_01, PC5MD_10, PC5MD_11,
+	PC4MD_0, PC4MD_1,
+	PC3MD_0, PC3MD_1,
+	PC2MD_0, PC2MD_1,
+	PC1MD_0, PC1MD_1,
+	PC0MD_0, PC0MD_1,
+
+	/* Port D */
+	PD15_IOR_IN, PD15_IOR_OUT,
+	PD14_IOR_IN, PD14_IOR_OUT,
+	PD13_IOR_IN, PD13_IOR_OUT,
+	PD12_IOR_IN, PD12_IOR_OUT,
+	PD11_IOR_IN, PD11_IOR_OUT,
+	PD10_IOR_IN, PD10_IOR_OUT,
+	PD9_IOR_IN, PD9_IOR_OUT,
+	PD8_IOR_IN, PD8_IOR_OUT,
+	PD7_IOR_IN, PD7_IOR_OUT,
+	PD6_IOR_IN, PD6_IOR_OUT,
+	PD5_IOR_IN, PD5_IOR_OUT,
+	PD4_IOR_IN, PD4_IOR_OUT,
+	PD3_IOR_IN, PD3_IOR_OUT,
+	PD2_IOR_IN, PD2_IOR_OUT,
+	PD1_IOR_IN, PD1_IOR_OUT,
+	PD0_IOR_IN, PD0_IOR_OUT,
+
+	PD15MD_00, PD15MD_01, PD15MD_10, PD15MD_11,
+	PD14MD_00, PD14MD_01, PD14MD_10, PD14MD_11,
+	PD13MD_00, PD13MD_01, PD13MD_10, PD13MD_11,
+	PD12MD_00, PD12MD_01, PD12MD_10, PD12MD_11,
+	PD11MD_00, PD11MD_01, PD11MD_10, PD11MD_11,
+	PD10MD_00, PD10MD_01, PD10MD_10, PD10MD_11,
+	PD9MD_00, PD9MD_01, PD9MD_10, PD9MD_11,
+	PD8MD_00, PD8MD_01, PD8MD_10, PD8MD_11,
+	PD7MD_00, PD7MD_01, PD7MD_10, PD7MD_11,
+	PD6MD_00, PD6MD_01, PD6MD_10, PD6MD_11,
+	PD5MD_00, PD5MD_01, PD5MD_10, PD5MD_11,
+	PD4MD_00, PD4MD_01, PD4MD_10, PD4MD_11,
+	PD3MD_00, PD3MD_01, PD3MD_10, PD3MD_11,
+	PD2MD_00, PD2MD_01, PD2MD_10, PD2MD_11,
+	PD1MD_00, PD1MD_01, PD1MD_10, PD1MD_11,
+	PD0MD_00, PD0MD_01, PD0MD_10, PD0MD_11,
+
+	/* Port E */
+	PE5_IOR_IN, PE5_IOR_OUT,
+	PE4_IOR_IN, PE4_IOR_OUT,
+	PE3_IOR_IN, PE3_IOR_OUT,
+	PE2_IOR_IN, PE2_IOR_OUT,
+	PE1_IOR_IN, PE1_IOR_OUT,
+	PE0_IOR_IN, PE0_IOR_OUT,
+
+	PE5MD_00, PE5MD_01, PE5MD_10, PE5MD_11,
+	PE4MD_00, PE4MD_01, PE4MD_10, PE4MD_11,
+	PE3MD_00, PE3MD_01, PE3MD_10, PE3MD_11,
+	PE2MD_00, PE2MD_01, PE2MD_10, PE2MD_11,
+	PE1MD_000, PE1MD_001, PE1MD_010, PE1MD_011,
+	PE1MD_100, PE1MD_101, PE1MD_110, PE1MD_111,
+	PE0MD_00, PE0MD_01, PE0MD_10, PE0MD_11,
+
+	/* Port F */
+	PF12_IOR_IN, PF12_IOR_OUT,
+	PF11_IOR_IN, PF11_IOR_OUT,
+	PF10_IOR_IN, PF10_IOR_OUT,
+	PF9_IOR_IN, PF9_IOR_OUT,
+	PF8_IOR_IN, PF8_IOR_OUT,
+	PF7_IOR_IN, PF7_IOR_OUT,
+	PF6_IOR_IN, PF6_IOR_OUT,
+	PF5_IOR_IN, PF5_IOR_OUT,
+	PF4_IOR_IN, PF4_IOR_OUT,
+	PF3_IOR_IN, PF3_IOR_OUT,
+	PF2_IOR_IN, PF2_IOR_OUT,
+	PF1_IOR_IN, PF1_IOR_OUT,
+	PF0_IOR_IN, PF0_IOR_OUT,
+
+	PF12MD_000, PF12MD_001, PF12MD_010, PF12MD_011,
+	PF12MD_100, PF12MD_101, PF12MD_110, PF12MD_111,
+	PF11MD_000, PF11MD_001, PF11MD_010, PF11MD_011,
+	PF11MD_100, PF11MD_101, PF11MD_110, PF11MD_111,
+	PF10MD_000, PF10MD_001, PF10MD_010, PF10MD_011,
+	PF10MD_100, PF10MD_101, PF10MD_110, PF10MD_111,
+	PF9MD_000, PF9MD_001, PF9MD_010, PF9MD_011,
+	PF9MD_100, PF9MD_101, PF9MD_110, PF9MD_111,
+	PF8MD_00, PF8MD_01, PF8MD_10, PF8MD_11,
+	PF7MD_000, PF7MD_001, PF7MD_010, PF7MD_011,
+	PF7MD_100, PF7MD_101, PF7MD_110, PF7MD_111,
+	PF6MD_000, PF6MD_001, PF6MD_010, PF6MD_011,
+	PF6MD_100, PF6MD_101, PF6MD_110, PF6MD_111,
+	PF5MD_000, PF5MD_001, PF5MD_010, PF5MD_011,
+	PF5MD_100, PF5MD_101, PF5MD_110, PF5MD_111,
+	PF4MD_000, PF4MD_001, PF4MD_010, PF4MD_011,
+	PF4MD_100, PF4MD_101, PF4MD_110, PF4MD_111,
+	PF3MD_000, PF3MD_001, PF3MD_010, PF3MD_011,
+	PF3MD_100, PF3MD_101, PF3MD_110, PF3MD_111,
+	PF2MD_000, PF2MD_001, PF2MD_010, PF2MD_011,
+	PF2MD_100, PF2MD_101, PF2MD_110, PF2MD_111,
+	PF1MD_000, PF1MD_001, PF1MD_010, PF1MD_011,
+	PF1MD_100, PF1MD_101, PF1MD_110, PF1MD_111,
+	PF0MD_000, PF0MD_001, PF0MD_010, PF0MD_011,
+	PF0MD_100, PF0MD_101, PF0MD_110, PF0MD_111,
+
+	/* Port G */
+	PG24_IOR_IN, PG24_IOR_OUT,
+	PG23_IOR_IN, PG23_IOR_OUT,
+	PG22_IOR_IN, PG22_IOR_OUT,
+	PG21_IOR_IN, PG21_IOR_OUT,
+	PG20_IOR_IN, PG20_IOR_OUT,
+	PG19_IOR_IN, PG19_IOR_OUT,
+	PG18_IOR_IN, PG18_IOR_OUT,
+	PG17_IOR_IN, PG17_IOR_OUT,
+	PG16_IOR_IN, PG16_IOR_OUT,
+	PG15_IOR_IN, PG15_IOR_OUT,
+	PG14_IOR_IN, PG14_IOR_OUT,
+	PG13_IOR_IN, PG13_IOR_OUT,
+	PG12_IOR_IN, PG12_IOR_OUT,
+	PG11_IOR_IN, PG11_IOR_OUT,
+	PG10_IOR_IN, PG10_IOR_OUT,
+	PG9_IOR_IN, PG9_IOR_OUT,
+	PG8_IOR_IN, PG8_IOR_OUT,
+	PG7_IOR_IN, PG7_IOR_OUT,
+	PG6_IOR_IN, PG6_IOR_OUT,
+	PG5_IOR_IN, PG5_IOR_OUT,
+	PG4_IOR_IN, PG4_IOR_OUT,
+	PG3_IOR_IN, PG3_IOR_OUT,
+	PG2_IOR_IN, PG2_IOR_OUT,
+	PG1_IOR_IN, PG1_IOR_OUT,
+	PG0_IOR_IN, PG0_IOR_OUT,
+
+	PG24MD_00, PG24MD_01, PG24MD_10, PG24MD_11,
+	PG23MD_00, PG23MD_01, PG23MD_10, PG23MD_11,
+	PG22MD_00, PG22MD_01, PG22MD_10, PG22MD_11,
+	PG21MD_00, PG21MD_01, PG21MD_10, PG21MD_11,
+	PG20MD_000, PG20MD_001, PG20MD_010, PG20MD_011,
+	PG20MD_100, PG20MD_101, PG20MD_110, PG20MD_111,
+	PG19MD_000, PG19MD_001, PG19MD_010, PG19MD_011,
+	PG19MD_100, PG19MD_101, PG19MD_110, PG19MD_111,
+	PG18MD_000, PG18MD_001, PG18MD_010, PG18MD_011,
+	PG18MD_100, PG18MD_101, PG18MD_110, PG18MD_111,
+	PG17MD_000, PG17MD_001, PG17MD_010, PG17MD_011,
+	PG17MD_100, PG17MD_101, PG17MD_110, PG17MD_111,
+	PG16MD_000, PG16MD_001, PG16MD_010, PG16MD_011,
+	PG16MD_100, PG16MD_101, PG16MD_110, PG16MD_111,
+	PG15MD_000, PG15MD_001, PG15MD_010, PG15MD_011,
+	PG15MD_100, PG15MD_101, PG15MD_110, PG15MD_111,
+	PG14MD_000, PG14MD_001, PG14MD_010, PG14MD_011,
+	PG14MD_100, PG14MD_101, PG14MD_110, PG14MD_111,
+	PG13MD_000, PG13MD_001, PG13MD_010, PG13MD_011,
+	PG13MD_100, PG13MD_101, PG13MD_110, PG13MD_111,
+	PG12MD_000, PG12MD_001, PG12MD_010, PG12MD_011,
+	PG12MD_100, PG12MD_101, PG12MD_110, PG12MD_111,
+	PG11MD_000, PG11MD_001, PG11MD_010, PG11MD_011,
+	PG11MD_100, PG11MD_101, PG11MD_110, PG11MD_111,
+	PG10MD_000, PG10MD_001, PG10MD_010, PG10MD_011,
+	PG10MD_100, PG10MD_101, PG10MD_110, PG10MD_111,
+	PG9MD_000, PG9MD_001, PG9MD_010, PG9MD_011,
+	PG9MD_100, PG9MD_101, PG9MD_110, PG9MD_111,
+	PG8MD_000, PG8MD_001, PG8MD_010, PG8MD_011,
+	PG8MD_100, PG8MD_101, PG8MD_110, PG8MD_111,
+	PG7MD_00, PG7MD_01, PG7MD_10, PG7MD_11,
+	PG6MD_00, PG6MD_01, PG6MD_10, PG6MD_11,
+	PG5MD_00, PG5MD_01, PG5MD_10, PG5MD_11,
+	PG4MD_00, PG4MD_01, PG4MD_10, PG4MD_11,
+	PG3MD_00, PG3MD_01, PG3MD_10, PG3MD_11,
+	PG2MD_00, PG2MD_01, PG2MD_10, PG2MD_11,
+	PG1MD_00, PG1MD_01, PG1MD_10, PG1MD_11,
+	PG0MD_000, PG0MD_001, PG0MD_010, PG0MD_011,
+	PG0MD_100, PG0MD_101, PG0MD_110, PG0MD_111,
+
+	/* Port H */
+	PH7MD_0, PH7MD_1,
+	PH6MD_0, PH6MD_1,
+	PH5MD_0, PH5MD_1,
+	PH4MD_0, PH4MD_1,
+	PH3MD_0, PH3MD_1,
+	PH2MD_0, PH2MD_1,
+	PH1MD_0, PH1MD_1,
+	PH0MD_0, PH0MD_1,
+
+	/* Port I - not on device */
+
+	/* Port J */
+	PJ11_IOR_IN, PJ11_IOR_OUT,
+	PJ10_IOR_IN, PJ10_IOR_OUT,
+	PJ9_IOR_IN, PJ9_IOR_OUT,
+	PJ8_IOR_IN, PJ8_IOR_OUT,
+	PJ7_IOR_IN, PJ7_IOR_OUT,
+	PJ6_IOR_IN, PJ6_IOR_OUT,
+	PJ5_IOR_IN, PJ5_IOR_OUT,
+	PJ4_IOR_IN, PJ4_IOR_OUT,
+	PJ3_IOR_IN, PJ3_IOR_OUT,
+	PJ2_IOR_IN, PJ2_IOR_OUT,
+	PJ1_IOR_IN, PJ1_IOR_OUT,
+	PJ0_IOR_IN, PJ0_IOR_OUT,
+
+	PJ11MD_00, PJ11MD_01, PJ11MD_10, PJ11MD_11,
+	PJ10MD_00, PJ10MD_01, PJ10MD_10, PJ10MD_11,
+	PJ9MD_00, PJ9MD_01, PJ9MD_10, PJ9MD_11,
+	PJ8MD_00, PJ8MD_01, PJ8MD_10, PJ8MD_11,
+	PJ7MD_00, PJ7MD_01, PJ7MD_10, PJ7MD_11,
+	PJ6MD_00, PJ6MD_01, PJ6MD_10, PJ6MD_11,
+	PJ5MD_00, PJ5MD_01, PJ5MD_10, PJ5MD_11,
+	PJ4MD_00, PJ4MD_01, PJ4MD_10, PJ4MD_11,
+	PJ3MD_00, PJ3MD_01, PJ3MD_10, PJ3MD_11,
+	PJ2MD_000, PJ2MD_001, PJ2MD_010, PJ2MD_011,
+	PJ2MD_100, PJ2MD_101, PJ2MD_110, PJ2MD_111,
+	PJ1MD_000, PJ1MD_001, PJ1MD_010, PJ1MD_011,
+	PJ1MD_100, PJ1MD_101, PJ1MD_110, PJ1MD_111,
+	PJ0MD_000, PJ0MD_001, PJ0MD_010, PJ0MD_011,
+	PJ0MD_100, PJ0MD_101, PJ0MD_110, PJ0MD_111,
+
+	/* Port K */
+	PK11_IOR_IN, PK11_IOR_OUT,
+	PK10_IOR_IN, PK10_IOR_OUT,
+	PK9_IOR_IN, PK9_IOR_OUT,
+	PK8_IOR_IN, PK8_IOR_OUT,
+	PK7_IOR_IN, PK7_IOR_OUT,
+	PK6_IOR_IN, PK6_IOR_OUT,
+	PK5_IOR_IN, PK5_IOR_OUT,
+	PK4_IOR_IN, PK4_IOR_OUT,
+	PK3_IOR_IN, PK3_IOR_OUT,
+	PK2_IOR_IN, PK2_IOR_OUT,
+	PK1_IOR_IN, PK1_IOR_OUT,
+	PK0_IOR_IN, PK0_IOR_OUT,
+
+	PK11MD_00, PK11MD_01, PK11MD_10, PK11MD_11,
+	PK10MD_00, PK10MD_01, PK10MD_10, PK10MD_11,
+	PK9MD_00, PK9MD_01, PK9MD_10, PK9MD_11,
+	PK8MD_00, PK8MD_01, PK8MD_10, PK8MD_11,
+	PK7MD_00, PK7MD_01, PK7MD_10, PK7MD_11,
+	PK6MD_00, PK6MD_01, PK6MD_10, PK6MD_11,
+	PK5MD_00, PK5MD_01, PK5MD_10, PK5MD_11,
+	PK4MD_00, PK4MD_01, PK4MD_10, PK4MD_11,
+	PK3MD_00, PK3MD_01, PK3MD_10, PK3MD_11,
+	PK2MD_00, PK2MD_01, PK2MD_10, PK2MD_11,
+	PK1MD_00, PK1MD_01, PK1MD_10, PK1MD_11,
+	PK0MD_00, PK0MD_01, PK0MD_10, PK0MD_11,
+	PINMUX_FUNCTION_END,
+
+	PINMUX_MARK_BEGIN,
+	/* Port A */
+
+	/* Port B */
+
+	/* Port C */
+
+	/* Port D */
+
+	/* Port E */
+
+	/* Port F */
+
+	/* Port G */
+
+	/* Port H */
+	PHAN7_MARK, PHAN6_MARK, PHAN5_MARK, PHAN4_MARK,
+	PHAN3_MARK, PHAN2_MARK, PHAN1_MARK, PHAN0_MARK,
+
+	/* Port I - not on device */
+
+	/* Port J */
+
+	/* Port K */
+
+	IRQ7_PC_MARK, IRQ6_PC_MARK, IRQ5_PC_MARK, IRQ4_PC_MARK,
+	IRQ3_PG_MARK, IRQ2_PG_MARK, IRQ1_PJ_MARK, IRQ0_PJ_MARK,
+	IRQ3_PE_MARK, IRQ2_PE_MARK, IRQ1_PE_MARK, IRQ0_PE_MARK,
+
+	PINT7_PG_MARK, PINT6_PG_MARK, PINT5_PG_MARK, PINT4_PG_MARK,
+	PINT3_PG_MARK, PINT2_PG_MARK, PINT1_PG_MARK, PINT0_PG_MARK,
+
+	SD_CD_MARK, SD_D0_MARK, SD_D1_MARK, SD_D2_MARK, SD_D3_MARK,
+	SD_WP_MARK, SD_CLK_MARK, SD_CMD_MARK,
+	CRX0_MARK, CRX1_MARK,
+	CTX0_MARK, CTX1_MARK,
+
+	PWM1A_MARK, PWM1B_MARK, PWM1C_MARK, PWM1D_MARK,
+	PWM1E_MARK, PWM1F_MARK, PWM1G_MARK, PWM1H_MARK,
+	PWM2A_MARK, PWM2B_MARK, PWM2C_MARK, PWM2D_MARK,
+	PWM2E_MARK, PWM2F_MARK, PWM2G_MARK, PWM2H_MARK,
+	IERXD_MARK, IETXD_MARK,
+	CRX0CRX1_MARK,
+	WDTOVF_MARK,
+
+	CRX0X1_MARK,
+
+	/* DMAC */
+	TEND0_MARK, DACK0_MARK, DREQ0_MARK,
+	TEND1_MARK, DACK1_MARK, DREQ1_MARK,
+
+	/* ADC */
+	ADTRG_MARK,
+
+	/* BSC */
+	A25_MARK, A24_MARK,
+	A23_MARK, A22_MARK, A21_MARK, A20_MARK,
+	A19_MARK, A18_MARK, A17_MARK, A16_MARK,
+	A15_MARK, A14_MARK, A13_MARK, A12_MARK,
+	A11_MARK, A10_MARK, A9_MARK, A8_MARK,
+	A7_MARK, A6_MARK, A5_MARK, A4_MARK,
+	A3_MARK, A2_MARK, A1_MARK, A0_MARK,
+	D15_MARK, D14_MARK, D13_MARK, D12_MARK,
+	D11_MARK, D10_MARK, D9_MARK, D8_MARK,
+	D7_MARK, D6_MARK, D5_MARK, D4_MARK,
+	D3_MARK, D2_MARK, D1_MARK, D0_MARK,
+	BS_MARK,
+	CS4_MARK, CS3_MARK, CS2_MARK, CS1_MARK, CS0_MARK,
+	CS6CE1B_MARK, CS5CE1A_MARK,
+	CE2A_MARK, CE2B_MARK,
+	RD_MARK, RDWR_MARK,
+	ICIOWRAH_MARK,
+	ICIORD_MARK,
+	WE1DQMUWE_MARK,
+	WE0DQML_MARK,
+	RAS_MARK, CAS_MARK, CKE_MARK,
+	WAIT_MARK, BREQ_MARK, BACK_MARK, IOIS16_MARK,
+
+	/* TMU */
+	TIOC0A_MARK, TIOC0B_MARK, TIOC0C_MARK, TIOC0D_MARK,
+	TIOC1A_MARK, TIOC1B_MARK,
+	TIOC2A_MARK, TIOC2B_MARK,
+	TIOC3A_MARK, TIOC3B_MARK, TIOC3C_MARK, TIOC3D_MARK,
+	TIOC4A_MARK, TIOC4B_MARK, TIOC4C_MARK, TIOC4D_MARK,
+	TCLKA_MARK,	TCLKB_MARK, TCLKC_MARK, TCLKD_MARK,
+
+	/* SCIF */
+	SCK0_MARK, SCK1_MARK, SCK2_MARK, SCK3_MARK,
+	RXD0_MARK, RXD1_MARK, RXD2_MARK, RXD3_MARK,
+	TXD0_MARK, TXD1_MARK, TXD2_MARK, TXD3_MARK,
+	RXD4_MARK, RXD5_MARK, RXD6_MARK, RXD7_MARK,
+	TXD4_MARK, TXD5_MARK, TXD6_MARK, TXD7_MARK,
+	RTS1_MARK, RTS3_MARK,
+	CTS1_MARK, CTS3_MARK,
+
+	/* RSPI */
+	RSPCK0_MARK, RSPCK1_MARK,
+	MOSI0_MARK, MOSI1_MARK,
+	MISO0_PF12_MARK, MISO1_MARK, MISO1_PG19_MARK,
+	SSL00_MARK, SSL10_MARK,
+
+	/* IIC3 */
+	SCL0_MARK, SCL1_MARK, SCL2_MARK,
+	SDA0_MARK, SDA1_MARK, SDA2_MARK,
+
+	/* SSI */
+	SSISCK0_MARK,
+	SSIWS0_MARK,
+	SSITXD0_MARK,
+	SSIRXD0_MARK,
+	SSIWS1_MARK, SSIWS2_MARK, SSIWS3_MARK,
+	SSISCK1_MARK, SSISCK2_MARK, SSISCK3_MARK,
+	SSIDATA1_MARK, SSIDATA2_MARK, SSIDATA3_MARK,
+	AUDIO_CLK_MARK,
+
+	/* SIOF */ /* NOTE Shares AUDIO_CLK with SSI */
+	SIOFTXD_MARK, SIOFRXD_MARK, SIOFSYNC_MARK, SIOFSCK_MARK,
+
+	/* SPDIF */ /* NOTE Shares AUDIO_CLK with SSI */
+	SPDIF_IN_MARK, SPDIF_OUT_MARK,
+
+	/* NANDFMC */ /* NOTE Controller is not available in boot mode 0 */
+	FCE_MARK,
+	FRB_MARK,
+
+	/* VDC3 */
+	DV_CLK_MARK,
+	DV_VSYNC_MARK, DV_HSYNC_MARK,
+	DV_DATA7_MARK, DV_DATA6_MARK, DV_DATA5_MARK, DV_DATA4_MARK,
+	DV_DATA3_MARK, DV_DATA2_MARK, DV_DATA1_MARK, DV_DATA0_MARK,
+	LCD_CLK_MARK, LCD_EXTCLK_MARK,
+	LCD_VSYNC_MARK, LCD_HSYNC_MARK, LCD_DE_MARK,
+	LCD_DATA15_MARK, LCD_DATA14_MARK, LCD_DATA13_MARK, LCD_DATA12_MARK,
+	LCD_DATA11_MARK, LCD_DATA10_MARK, LCD_DATA9_MARK, LCD_DATA8_MARK,
+	LCD_DATA7_MARK, LCD_DATA6_MARK, LCD_DATA5_MARK, LCD_DATA4_MARK,
+	LCD_DATA3_MARK, LCD_DATA2_MARK, LCD_DATA1_MARK, LCD_DATA0_MARK,
+	LCD_M_DISP_MARK,
+	PINMUX_MARK_END,
+};
+
+static pinmux_enum_t pinmux_data[] = {
+
+	/* Port A */
+	PINMUX_DATA(PA3_DATA, PA3_IN),
+	PINMUX_DATA(PA2_DATA, PA2_IN),
+	PINMUX_DATA(PA1_DATA, PA1_IN),
+	PINMUX_DATA(PA0_DATA, PA0_IN),
+
+	/* Port B */
+	PINMUX_DATA(PB22_DATA, PB22MD_00, PB22_IN, PB22_OUT),
+	PINMUX_DATA(A22_MARK, PB22MD_01),
+	PINMUX_DATA(CS4_MARK, PB22MD_10),
+
+	PINMUX_DATA(PB21_DATA, PB21MD_0, PB21_IN, PB21_OUT),
+	PINMUX_DATA(A21_MARK, PB21MD_1),
+	PINMUX_DATA(A20_MARK, PB20MD_1),
+	PINMUX_DATA(A19_MARK, PB19MD_01),
+	PINMUX_DATA(A18_MARK, PB18MD_01),
+	PINMUX_DATA(A17_MARK, PB17MD_01),
+	PINMUX_DATA(A16_MARK, PB16MD_01),
+	PINMUX_DATA(A15_MARK, PB15MD_01),
+	PINMUX_DATA(A14_MARK, PB14MD_01),
+	PINMUX_DATA(A13_MARK, PB13MD_01),
+	PINMUX_DATA(A12_MARK, PB12MD_01),
+	PINMUX_DATA(A11_MARK, PB11MD_01),
+	PINMUX_DATA(A10_MARK, PB10MD_01),
+	PINMUX_DATA(A9_MARK, PB9MD_01),
+	PINMUX_DATA(A8_MARK, PB8MD_01),
+	PINMUX_DATA(A7_MARK, PB7MD_01),
+	PINMUX_DATA(A6_MARK, PB6MD_01),
+	PINMUX_DATA(A5_MARK, PB5MD_01),
+	PINMUX_DATA(A4_MARK, PB4MD_01),
+	PINMUX_DATA(A3_MARK, PB3MD_1),
+	PINMUX_DATA(A2_MARK, PB2MD_1),
+	PINMUX_DATA(A1_MARK, PB1MD_1),
+
+	/* Port C */
+	PINMUX_DATA(PC10_DATA, PC10MD_0),
+	PINMUX_DATA(TIOC2B_MARK, PC1MD_1),
+	PINMUX_DATA(PC9_DATA, PC9MD_0),
+	PINMUX_DATA(TIOC2A_MARK, PC9MD_1),
+	PINMUX_DATA(PC8_DATA, PC8MD_00),
+	PINMUX_DATA(CS3_MARK, PC8MD_01),
+	PINMUX_DATA(TIOC4D_MARK, PC8MD_10),
+	PINMUX_DATA(IRQ7_PC_MARK, PC8MD_11),
+	PINMUX_DATA(PC7_DATA, PC7MD_00),
+	PINMUX_DATA(CKE_MARK, PC7MD_01),
+	PINMUX_DATA(TIOC4C_MARK, PC7MD_10),
+	PINMUX_DATA(IRQ6_PC_MARK, PC7MD_11),
+	PINMUX_DATA(PC6_DATA, PC6MD_00),
+	PINMUX_DATA(CAS_MARK, PC6MD_01),
+	PINMUX_DATA(TIOC4B_MARK, PC6MD_10),
+	PINMUX_DATA(IRQ5_PC_MARK, PC6MD_11),
+	PINMUX_DATA(PC5_DATA, PC5MD_00),
+	PINMUX_DATA(RAS_MARK, PC5MD_01),
+	PINMUX_DATA(TIOC4A_MARK, PC5MD_10),
+	PINMUX_DATA(IRQ4_PC_MARK, PC5MD_11),
+	PINMUX_DATA(PC4_DATA, PC4MD_0),
+	PINMUX_DATA(WE1DQMUWE_MARK, PC4MD_1),
+	PINMUX_DATA(PC3_DATA, PC3MD_0),
+	PINMUX_DATA(WE0DQML_MARK, PC3MD_1),
+	PINMUX_DATA(PC2_DATA, PC2MD_0),
+	PINMUX_DATA(RDWR_MARK, PC2MD_1),
+	PINMUX_DATA(PC1_DATA, PC1MD_0),
+	PINMUX_DATA(RD_MARK, PC1MD_1),
+	PINMUX_DATA(PC0_DATA, PC0MD_0),
+	PINMUX_DATA(CS0_MARK, PC0MD_1),
+
+	/* Port D */
+	PINMUX_DATA(D15_MARK, PD15MD_01),
+	PINMUX_DATA(D14_MARK, PD14MD_01),
+	PINMUX_DATA(D13_MARK, PD13MD_01),
+	PINMUX_DATA(D12_MARK, PD12MD_01),
+	PINMUX_DATA(D11_MARK, PD11MD_01),
+	PINMUX_DATA(D10_MARK, PD10MD_01),
+	PINMUX_DATA(D9_MARK, PD9MD_01),
+	PINMUX_DATA(D8_MARK, PD8MD_01),
+	PINMUX_DATA(D7_MARK, PD7MD_01),
+	PINMUX_DATA(D6_MARK, PD6MD_01),
+	PINMUX_DATA(D5_MARK, PD5MD_01),
+	PINMUX_DATA(D4_MARK, PD4MD_01),
+	PINMUX_DATA(D3_MARK, PD3MD_01),
+	PINMUX_DATA(D2_MARK, PD2MD_01),
+	PINMUX_DATA(D1_MARK, PD1MD_01),
+	PINMUX_DATA(D0_MARK, PD0MD_01),
+
+	/* Port E */
+	PINMUX_DATA(PE5_DATA, PE5MD_00),
+	PINMUX_DATA(SDA2_MARK, PE5MD_01),
+	PINMUX_DATA(DV_HSYNC_MARK, PE5MD_11),
+
+	PINMUX_DATA(PE4_DATA, PE4MD_00),
+	PINMUX_DATA(SCL2_MARK, PE4MD_01),
+	PINMUX_DATA(DV_VSYNC_MARK, PE4MD_11),
+
+	PINMUX_DATA(PE3_DATA, PE3MD_00),
+	PINMUX_DATA(SDA1_MARK, PE3MD_01),
+	PINMUX_DATA(IRQ3_PE_MARK, PE3MD_11),
+
+	PINMUX_DATA(PE2_DATA, PE2MD_00),
+	PINMUX_DATA(SCL1_MARK, PE2MD_01),
+	PINMUX_DATA(IRQ2_PE_MARK, PE2MD_11),
+
+	PINMUX_DATA(PE1_DATA, PE1MD_000),
+	PINMUX_DATA(SDA0_MARK, PE1MD_001),
+	PINMUX_DATA(IOIS16_MARK, PE1MD_010),
+	PINMUX_DATA(IRQ1_PE_MARK, PE1MD_011),
+	PINMUX_DATA(TCLKA_MARK, PE1MD_100),
+	PINMUX_DATA(ADTRG_MARK, PE1MD_101),
+
+	PINMUX_DATA(PE0_DATA, PE0MD_00),
+	PINMUX_DATA(SCL0_MARK, PE0MD_01),
+	PINMUX_DATA(AUDIO_CLK_MARK, PE0MD_10),
+	PINMUX_DATA(IRQ0_PE_MARK, PE0MD_11),
+
+	/* Port F */
+	PINMUX_DATA(PF12_DATA, PF12MD_000),
+	PINMUX_DATA(BS_MARK, PF12MD_001),
+	PINMUX_DATA(MISO0_PF12_MARK, PF12MD_011),
+	PINMUX_DATA(TIOC3D_MARK, PF12MD_100),
+	PINMUX_DATA(SPDIF_OUT_MARK, PF12MD_101),
+
+	PINMUX_DATA(PF11_DATA, PF11MD_000),
+	PINMUX_DATA(A25_MARK, PF11MD_001),
+	PINMUX_DATA(SSIDATA3_MARK, PF11MD_010),
+	PINMUX_DATA(MOSI0_MARK, PF11MD_011),
+	PINMUX_DATA(TIOC3C_MARK, PF11MD_100),
+	PINMUX_DATA(SPDIF_IN_MARK, PF11MD_101),
+
+	PINMUX_DATA(PF10_DATA, PF10MD_000),
+	PINMUX_DATA(A24_MARK, PF10MD_001),
+	PINMUX_DATA(SSIWS3_MARK, PF10MD_010),
+	PINMUX_DATA(SSL00_MARK, PF10MD_011),
+	PINMUX_DATA(TIOC3B_MARK, PF10MD_100),
+	PINMUX_DATA(FCE_MARK, PF10MD_101),
+
+	PINMUX_DATA(PF9_DATA, PF9MD_000),
+	PINMUX_DATA(A23_MARK, PF9MD_001),
+	PINMUX_DATA(SSISCK3_MARK, PF9MD_010),
+	PINMUX_DATA(RSPCK0_MARK, PF9MD_011),
+	PINMUX_DATA(TIOC3A_MARK, PF9MD_100),
+	PINMUX_DATA(FRB_MARK, PF9MD_101),
+
+	PINMUX_DATA(PF8_DATA, PF8MD_00),
+	PINMUX_DATA(CE2B_MARK, PF8MD_01),
+	PINMUX_DATA(SSIDATA3_MARK, PF8MD_10),
+	PINMUX_DATA(DV_CLK_MARK, PF8MD_11),
+
+	PINMUX_DATA(PF7_DATA, PF7MD_000),
+	PINMUX_DATA(CE2A_MARK, PF7MD_001),
+	PINMUX_DATA(SSIWS3_MARK, PF7MD_010),
+	PINMUX_DATA(DV_DATA7_MARK, PF7MD_011),
+	PINMUX_DATA(TCLKD_MARK, PF7MD_100),
+
+	PINMUX_DATA(PF6_DATA, PF6MD_000),
+	PINMUX_DATA(CS6CE1B_MARK, PF6MD_001),
+	PINMUX_DATA(SSISCK3_MARK, PF6MD_010),
+	PINMUX_DATA(DV_DATA6_MARK, PF6MD_011),
+	PINMUX_DATA(TCLKB_MARK, PF6MD_100),
+
+	PINMUX_DATA(PF5_DATA, PF5MD_000),
+	PINMUX_DATA(CS5CE1A_MARK, PF5MD_001),
+	PINMUX_DATA(SSIDATA2_MARK, PF5MD_010),
+	PINMUX_DATA(DV_DATA5_MARK, PF5MD_011),
+	PINMUX_DATA(TCLKC_MARK, PF5MD_100),
+
+	PINMUX_DATA(PF4_DATA, PF4MD_000),
+	PINMUX_DATA(ICIOWRAH_MARK, PF4MD_001),
+	PINMUX_DATA(SSIWS2_MARK, PF4MD_010),
+	PINMUX_DATA(DV_DATA4_MARK, PF4MD_011),
+	PINMUX_DATA(TXD3_MARK, PF4MD_100),
+
+	PINMUX_DATA(PF3_DATA, PF3MD_000),
+	PINMUX_DATA(ICIORD_MARK, PF3MD_001),
+	PINMUX_DATA(SSISCK2_MARK, PF3MD_010),
+	PINMUX_DATA(DV_DATA3_MARK, PF3MD_011),
+	PINMUX_DATA(RXD3_MARK, PF3MD_100),
+
+	PINMUX_DATA(PF2_DATA, PF2MD_000),
+	PINMUX_DATA(BACK_MARK, PF2MD_001),
+	PINMUX_DATA(SSIDATA1_MARK, PF2MD_010),
+	PINMUX_DATA(DV_DATA2_MARK, PF2MD_011),
+	PINMUX_DATA(TXD2_MARK, PF2MD_100),
+	PINMUX_DATA(DACK0_MARK, PF2MD_101),
+
+	PINMUX_DATA(PF1_DATA, PF1MD_000),
+	PINMUX_DATA(BREQ_MARK, PF1MD_001),
+	PINMUX_DATA(SSIWS1_MARK, PF1MD_010),
+	PINMUX_DATA(DV_DATA1_MARK, PF1MD_011),
+	PINMUX_DATA(RXD2_MARK, PF1MD_100),
+	PINMUX_DATA(DREQ0_MARK, PF1MD_101),
+
+	PINMUX_DATA(PF0_DATA, PF0MD_000),
+	PINMUX_DATA(WAIT_MARK, PF0MD_001),
+	PINMUX_DATA(SSISCK1_MARK, PF0MD_010),
+	PINMUX_DATA(DV_DATA0_MARK, PF0MD_011),
+	PINMUX_DATA(SCK2_MARK, PF0MD_100),
+	PINMUX_DATA(TEND0_MARK, PF0MD_101),
+
+	/* Port G */
+	PINMUX_DATA(PG24_DATA, PG24MD_00),
+	PINMUX_DATA(MOSI0_MARK, PG24MD_01),
+	PINMUX_DATA(TIOC0D_MARK, PG24MD_10),
+
+	PINMUX_DATA(PG23_DATA, PG23MD_00),
+	PINMUX_DATA(MOSI1_MARK, PG23MD_01),
+	PINMUX_DATA(TIOC0C_MARK, PG23MD_10),
+
+	PINMUX_DATA(PG22_DATA, PG22MD_00),
+	PINMUX_DATA(SSL10_MARK, PG22MD_01),
+	PINMUX_DATA(TIOC0B_MARK, PG22MD_10),
+
+	PINMUX_DATA(PG21_DATA, PG21MD_00),
+	PINMUX_DATA(RSPCK1_MARK, PG21MD_01),
+	PINMUX_DATA(TIOC0A_MARK, PG21MD_10),
+
+	PINMUX_DATA(PG20_DATA, PG20MD_000),
+	PINMUX_DATA(LCD_EXTCLK_MARK, PG20MD_001),
+	PINMUX_DATA(MISO1_MARK, PG20MD_011),
+	PINMUX_DATA(TXD7_MARK, PG20MD_100),
+
+	PINMUX_DATA(PG19_DATA, PG19MD_000),
+	PINMUX_DATA(LCD_CLK_MARK, PG19MD_001),
+	PINMUX_DATA(TIOC2B_MARK, PG19MD_010),
+	PINMUX_DATA(MISO1_PG19_MARK, PG19MD_011),
+	PINMUX_DATA(RXD7_MARK, PG19MD_100),
+
+	PINMUX_DATA(PG18_DATA, PG18MD_000),
+	PINMUX_DATA(LCD_DE_MARK, PG18MD_001),
+	PINMUX_DATA(TIOC2A_MARK, PG18MD_010),
+	PINMUX_DATA(SSL10_MARK, PG18MD_011),
+	PINMUX_DATA(TXD6_MARK, PG18MD_100),
+
+	PINMUX_DATA(PG17_DATA, PG17MD_000),
+	PINMUX_DATA(LCD_HSYNC_MARK, PG17MD_001),
+	PINMUX_DATA(TIOC1B_MARK, PG17MD_010),
+	PINMUX_DATA(RSPCK1_MARK, PG17MD_011),
+	PINMUX_DATA(RXD6_MARK, PG17MD_100),
+
+	PINMUX_DATA(PG16_DATA, PG16MD_000),
+	PINMUX_DATA(LCD_VSYNC_MARK, PG16MD_001),
+	PINMUX_DATA(TIOC1A_MARK, PG16MD_010),
+	PINMUX_DATA(TXD3_MARK, PG16MD_011),
+	PINMUX_DATA(CTS1_MARK, PG16MD_100),
+
+	PINMUX_DATA(PG15_DATA, PG15MD_000),
+	PINMUX_DATA(LCD_DATA15_MARK, PG15MD_001),
+	PINMUX_DATA(TIOC0D_MARK, PG15MD_010),
+	PINMUX_DATA(RXD3_MARK, PG15MD_011),
+	PINMUX_DATA(RTS1_MARK, PG15MD_100),
+
+	PINMUX_DATA(PG14_DATA, PG14MD_000),
+	PINMUX_DATA(LCD_DATA14_MARK, PG14MD_001),
+	PINMUX_DATA(TIOC0C_MARK, PG14MD_010),
+	PINMUX_DATA(SCK1_MARK, PG14MD_100),
+
+	PINMUX_DATA(PG13_DATA, PG13MD_000),
+	PINMUX_DATA(LCD_DATA13_MARK, PG13MD_001),
+	PINMUX_DATA(TIOC0B_MARK, PG13MD_010),
+	PINMUX_DATA(TXD1_MARK, PG13MD_100),
+
+	PINMUX_DATA(PG12_DATA, PG12MD_000),
+	PINMUX_DATA(LCD_DATA12_MARK, PG12MD_001),
+	PINMUX_DATA(TIOC0A_MARK, PG12MD_010),
+	PINMUX_DATA(RXD1_MARK, PG12MD_100),
+
+	PINMUX_DATA(PG11_DATA, PG11MD_000),
+	PINMUX_DATA(LCD_DATA11_MARK, PG11MD_001),
+	PINMUX_DATA(SSITXD0_MARK, PG11MD_010),
+	PINMUX_DATA(IRQ3_PG_MARK, PG11MD_011),
+	PINMUX_DATA(TXD5_MARK, PG11MD_100),
+	PINMUX_DATA(SIOFTXD_MARK, PG11MD_101),
+
+	PINMUX_DATA(PG10_DATA, PG10MD_000),
+	PINMUX_DATA(LCD_DATA10_MARK, PG10MD_001),
+	PINMUX_DATA(SSIRXD0_MARK, PG10MD_010),
+	PINMUX_DATA(IRQ2_PG_MARK, PG10MD_011),
+	PINMUX_DATA(RXD5_MARK, PG10MD_100),
+	PINMUX_DATA(SIOFRXD_MARK, PG10MD_101),
+
+	PINMUX_DATA(PG9_DATA, PG9MD_000),
+	PINMUX_DATA(LCD_DATA9_MARK, PG9MD_001),
+	PINMUX_DATA(SSIWS0_MARK, PG9MD_010),
+	PINMUX_DATA(TXD4_MARK, PG9MD_100),
+	PINMUX_DATA(SIOFSYNC_MARK, PG9MD_101),
+
+	PINMUX_DATA(PG8_DATA, PG8MD_000),
+	PINMUX_DATA(LCD_DATA8_MARK, PG8MD_001),
+	PINMUX_DATA(SSISCK0_MARK, PG8MD_010),
+	PINMUX_DATA(RXD4_MARK, PG8MD_100),
+	PINMUX_DATA(SIOFSCK_MARK, PG8MD_101),
+
+	PINMUX_DATA(PG7_DATA, PG7MD_00),
+	PINMUX_DATA(LCD_DATA7_MARK, PG7MD_01),
+	PINMUX_DATA(SD_CD_MARK, PG7MD_10),
+	PINMUX_DATA(PINT7_PG_MARK, PG7MD_11),
+
+	PINMUX_DATA(PG6_DATA, PG7MD_00),
+	PINMUX_DATA(LCD_DATA6_MARK, PG7MD_01),
+	PINMUX_DATA(SD_WP_MARK, PG7MD_10),
+	PINMUX_DATA(PINT6_PG_MARK, PG7MD_11),
+
+	PINMUX_DATA(PG5_DATA, PG5MD_00),
+	PINMUX_DATA(LCD_DATA5_MARK, PG5MD_01),
+	PINMUX_DATA(SD_D1_MARK, PG5MD_10),
+	PINMUX_DATA(PINT5_PG_MARK, PG5MD_11),
+
+	PINMUX_DATA(PG4_DATA, PG4MD_00),
+	PINMUX_DATA(LCD_DATA4_MARK, PG4MD_01),
+	PINMUX_DATA(SD_D0_MARK, PG4MD_10),
+	PINMUX_DATA(PINT4_PG_MARK, PG4MD_11),
+
+	PINMUX_DATA(PG3_DATA, PG3MD_00),
+	PINMUX_DATA(LCD_DATA3_MARK, PG3MD_01),
+	PINMUX_DATA(SD_CLK_MARK, PG3MD_10),
+	PINMUX_DATA(PINT3_PG_MARK, PG3MD_11),
+
+	PINMUX_DATA(PG2_DATA, PG2MD_00),
+	PINMUX_DATA(LCD_DATA2_MARK, PG2MD_01),
+	PINMUX_DATA(SD_CMD_MARK, PG2MD_10),
+	PINMUX_DATA(PINT2_PG_MARK, PG2MD_11),
+
+	PINMUX_DATA(PG1_DATA, PG1MD_00),
+	PINMUX_DATA(LCD_DATA1_MARK, PG1MD_01),
+	PINMUX_DATA(SD_D3_MARK, PG1MD_10),
+	PINMUX_DATA(PINT1_PG_MARK, PG1MD_11),
+
+	PINMUX_DATA(PG0_DATA, PG0MD_000),
+	PINMUX_DATA(LCD_DATA0_MARK, PG0MD_001),
+	PINMUX_DATA(SD_D2_MARK, PG0MD_010),
+	PINMUX_DATA(PINT0_PG_MARK, PG0MD_011),
+	PINMUX_DATA(WDTOVF_MARK, PG0MD_100),
+
+	/* Port H */
+	PINMUX_DATA(PH7_DATA, PH7MD_0),
+	PINMUX_DATA(PHAN7_MARK, PH7MD_1),
+
+	PINMUX_DATA(PH6_DATA, PH6MD_0),
+	PINMUX_DATA(PHAN6_MARK, PH6MD_1),
+
+	PINMUX_DATA(PH5_DATA, PH5MD_0),
+	PINMUX_DATA(PHAN5_MARK, PH5MD_1),
+
+	PINMUX_DATA(PH4_DATA, PH4MD_0),
+	PINMUX_DATA(PHAN4_MARK, PH4MD_1),
+
+	PINMUX_DATA(PH3_DATA, PH3MD_0),
+	PINMUX_DATA(PHAN3_MARK, PH3MD_1),
+
+	PINMUX_DATA(PH2_DATA, PH2MD_0),
+	PINMUX_DATA(PHAN2_MARK, PH2MD_1),
+
+	PINMUX_DATA(PH1_DATA, PH1MD_0),
+	PINMUX_DATA(PHAN1_MARK, PH1MD_1),
+
+	PINMUX_DATA(PH0_DATA, PH0MD_0),
+	PINMUX_DATA(PHAN0_MARK, PH0MD_1),
+
+	/* Port I - not on device */
+
+	/* Port J */
+	PINMUX_DATA(PJ11_DATA, PJ11MD_00),
+	PINMUX_DATA(PWM2H_MARK, PJ11MD_01),
+	PINMUX_DATA(DACK1_MARK, PJ11MD_10),
+
+	PINMUX_DATA(PJ10_DATA, PJ10MD_00),
+	PINMUX_DATA(PWM2G_MARK, PJ10MD_01),
+	PINMUX_DATA(DREQ1_MARK, PJ10MD_10),
+
+	PINMUX_DATA(PJ9_DATA, PJ9MD_00),
+	PINMUX_DATA(PWM2F_MARK, PJ9MD_01),
+	PINMUX_DATA(TEND1_MARK, PJ9MD_10),
+
+	PINMUX_DATA(PJ8_DATA, PJ8MD_00),
+	PINMUX_DATA(PWM2E_MARK, PJ8MD_01),
+	PINMUX_DATA(RTS3_MARK, PJ8MD_10),
+
+	PINMUX_DATA(PJ7_DATA, PJ7MD_00),
+	PINMUX_DATA(TIOC1B_MARK, PJ7MD_01),
+	PINMUX_DATA(CTS3_MARK, PJ7MD_10),
+
+	PINMUX_DATA(PJ6_DATA, PJ6MD_00),
+	PINMUX_DATA(TIOC1A_MARK, PJ6MD_01),
+	PINMUX_DATA(SCK3_MARK, PJ6MD_10),
+
+	PINMUX_DATA(PJ5_DATA, PJ5MD_00),
+	PINMUX_DATA(IERXD_MARK, PJ5MD_01),
+	PINMUX_DATA(TXD3_MARK, PJ5MD_10),
+
+	PINMUX_DATA(PJ4_DATA, PJ4MD_00),
+	PINMUX_DATA(IETXD_MARK, PJ4MD_01),
+	PINMUX_DATA(RXD3_MARK, PJ4MD_10),
+
+	PINMUX_DATA(PJ3_DATA, PJ3MD_00),
+	PINMUX_DATA(CRX1_MARK, PJ3MD_01),
+	PINMUX_DATA(CRX0X1_MARK, PJ3MD_10),
+	PINMUX_DATA(IRQ1_PJ_MARK, PJ3MD_11),
+
+	PINMUX_DATA(PJ2_DATA, PJ2MD_000),
+	PINMUX_DATA(CTX1_MARK, PJ2MD_001),
+	PINMUX_DATA(CRX0CRX1_MARK, PJ2MD_010),
+	PINMUX_DATA(CS2_MARK, PJ2MD_011),
+	PINMUX_DATA(SCK0_MARK, PJ2MD_100),
+	PINMUX_DATA(LCD_M_DISP_MARK, PJ2MD_101),
+
+	PINMUX_DATA(PJ1_DATA, PJ1MD_000),
+	PINMUX_DATA(CRX0_MARK, PJ1MD_001),
+	PINMUX_DATA(IERXD_MARK, PJ1MD_010),
+	PINMUX_DATA(IRQ0_PJ_MARK, PJ1MD_011),
+	PINMUX_DATA(RXD0_MARK, PJ1MD_100),
+
+	PINMUX_DATA(PJ0_DATA, PJ0MD_000),
+	PINMUX_DATA(CTX0_MARK, PJ0MD_001),
+	PINMUX_DATA(IERXD_MARK, PJ0MD_010),
+	PINMUX_DATA(CS1_MARK, PJ0MD_011),
+	PINMUX_DATA(TXD0_MARK, PJ0MD_100),
+	PINMUX_DATA(A0_MARK, PJ0MD_101),
+
+	/* Port K */
+	PINMUX_DATA(PK11_DATA, PK11MD_00),
+	PINMUX_DATA(PWM2D_MARK, PK11MD_01),
+	PINMUX_DATA(SSITXD0_MARK, PK11MD_10),
+
+	PINMUX_DATA(PK10_DATA, PK10MD_00),
+	PINMUX_DATA(PWM2C_MARK, PK10MD_01),
+	PINMUX_DATA(SSIRXD0_MARK, PK10MD_10),
+
+	PINMUX_DATA(PK9_DATA, PK9MD_00),
+	PINMUX_DATA(PWM2B_MARK, PK9MD_01),
+	PINMUX_DATA(SSIWS0_MARK, PK9MD_10),
+
+	PINMUX_DATA(PK8_DATA, PK8MD_00),
+	PINMUX_DATA(PWM2A_MARK, PK8MD_01),
+	PINMUX_DATA(SSISCK0_MARK, PK8MD_10),
+
+	PINMUX_DATA(PK7_DATA, PK7MD_00),
+	PINMUX_DATA(PWM1H_MARK, PK7MD_01),
+	PINMUX_DATA(SD_CD_MARK, PK7MD_10),
+
+	PINMUX_DATA(PK6_DATA, PK6MD_00),
+	PINMUX_DATA(PWM1G_MARK, PK6MD_01),
+	PINMUX_DATA(SD_WP_MARK, PK6MD_10),
+
+	PINMUX_DATA(PK5_DATA, PK5MD_00),
+	PINMUX_DATA(PWM1F_MARK, PK5MD_01),
+	PINMUX_DATA(SD_D1_MARK, PK5MD_10),
+
+	PINMUX_DATA(PK4_DATA, PK4MD_00),
+	PINMUX_DATA(PWM1E_MARK, PK4MD_01),
+	PINMUX_DATA(SD_D0_MARK, PK4MD_10),
+
+	PINMUX_DATA(PK3_DATA, PK3MD_00),
+	PINMUX_DATA(PWM1D_MARK, PK3MD_01),
+	PINMUX_DATA(SD_CLK_MARK, PK3MD_10),
+
+	PINMUX_DATA(PK2_DATA, PK2MD_00),
+	PINMUX_DATA(PWM1C_MARK, PK2MD_01),
+	PINMUX_DATA(SD_CMD_MARK, PK2MD_10),
+
+	PINMUX_DATA(PK1_DATA, PK1MD_00),
+	PINMUX_DATA(PWM1B_MARK, PK1MD_01),
+	PINMUX_DATA(SD_D3_MARK, PK1MD_10),
+
+	PINMUX_DATA(PK0_DATA, PK0MD_00),
+	PINMUX_DATA(PWM1A_MARK, PK0MD_01),
+	PINMUX_DATA(SD_D2_MARK, PK0MD_10),
+};
+
+static struct pinmux_gpio pinmux_gpios[] = {
+
+	/* Port A */
+	PINMUX_GPIO(GPIO_PA3, PA3_DATA),
+	PINMUX_GPIO(GPIO_PA2, PA2_DATA),
+	PINMUX_GPIO(GPIO_PA1, PA1_DATA),
+	PINMUX_GPIO(GPIO_PA0, PA0_DATA),
+
+	/* Port B */
+	PINMUX_GPIO(GPIO_PB22, PB22_DATA),
+	PINMUX_GPIO(GPIO_PB21, PB21_DATA),
+	PINMUX_GPIO(GPIO_PB20, PB20_DATA),
+	PINMUX_GPIO(GPIO_PB19, PB19_DATA),
+	PINMUX_GPIO(GPIO_PB18, PB18_DATA),
+	PINMUX_GPIO(GPIO_PB17, PB17_DATA),
+	PINMUX_GPIO(GPIO_PB16, PB16_DATA),
+	PINMUX_GPIO(GPIO_PB15, PB15_DATA),
+	PINMUX_GPIO(GPIO_PB14, PB14_DATA),
+	PINMUX_GPIO(GPIO_PB13, PB13_DATA),
+	PINMUX_GPIO(GPIO_PB12, PB12_DATA),
+	PINMUX_GPIO(GPIO_PB11, PB11_DATA),
+	PINMUX_GPIO(GPIO_PB10, PB10_DATA),
+	PINMUX_GPIO(GPIO_PB9, PB9_DATA),
+	PINMUX_GPIO(GPIO_PB8, PB8_DATA),
+	PINMUX_GPIO(GPIO_PB7, PB7_DATA),
+	PINMUX_GPIO(GPIO_PB6, PB6_DATA),
+	PINMUX_GPIO(GPIO_PB5, PB5_DATA),
+	PINMUX_GPIO(GPIO_PB4, PB4_DATA),
+	PINMUX_GPIO(GPIO_PB3, PB3_DATA),
+	PINMUX_GPIO(GPIO_PB2, PB2_DATA),
+	PINMUX_GPIO(GPIO_PB1, PB1_DATA),
+
+	/* Port C */
+	PINMUX_GPIO(GPIO_PC10, PC10_DATA),
+	PINMUX_GPIO(GPIO_PC9, PC9_DATA),
+	PINMUX_GPIO(GPIO_PC8, PC8_DATA),
+	PINMUX_GPIO(GPIO_PC7, PC7_DATA),
+	PINMUX_GPIO(GPIO_PC6, PC6_DATA),
+	PINMUX_GPIO(GPIO_PC5, PC5_DATA),
+	PINMUX_GPIO(GPIO_PC4, PC4_DATA),
+	PINMUX_GPIO(GPIO_PC3, PC3_DATA),
+	PINMUX_GPIO(GPIO_PC2, PC2_DATA),
+	PINMUX_GPIO(GPIO_PC1, PC1_DATA),
+	PINMUX_GPIO(GPIO_PC0, PC0_DATA),
+
+	/* Port D */
+	PINMUX_GPIO(GPIO_PD15, PD15_DATA),
+	PINMUX_GPIO(GPIO_PD14, PD14_DATA),
+	PINMUX_GPIO(GPIO_PD13, PD13_DATA),
+	PINMUX_GPIO(GPIO_PD12, PD12_DATA),
+	PINMUX_GPIO(GPIO_PD11, PD11_DATA),
+	PINMUX_GPIO(GPIO_PD10, PD10_DATA),
+	PINMUX_GPIO(GPIO_PD9, PD9_DATA),
+	PINMUX_GPIO(GPIO_PD8, PD8_DATA),
+	PINMUX_GPIO(GPIO_PD7, PD7_DATA),
+	PINMUX_GPIO(GPIO_PD6, PD6_DATA),
+	PINMUX_GPIO(GPIO_PD5, PD5_DATA),
+	PINMUX_GPIO(GPIO_PD4, PD4_DATA),
+	PINMUX_GPIO(GPIO_PD3, PD3_DATA),
+	PINMUX_GPIO(GPIO_PD2, PD2_DATA),
+	PINMUX_GPIO(GPIO_PD1, PD1_DATA),
+	PINMUX_GPIO(GPIO_PD0, PD0_DATA),
+
+	/* Port E */
+	PINMUX_GPIO(GPIO_PE5, PE5_DATA),
+	PINMUX_GPIO(GPIO_PE4, PE4_DATA),
+	PINMUX_GPIO(GPIO_PE3, PE3_DATA),
+	PINMUX_GPIO(GPIO_PE2, PE2_DATA),
+	PINMUX_GPIO(GPIO_PE1, PE1_DATA),
+	PINMUX_GPIO(GPIO_PE0, PE0_DATA),
+
+	/* Port F */
+	PINMUX_GPIO(GPIO_PF12, PF12_DATA),
+	PINMUX_GPIO(GPIO_PF11, PF11_DATA),
+	PINMUX_GPIO(GPIO_PF10, PF10_DATA),
+	PINMUX_GPIO(GPIO_PF9, PF9_DATA),
+	PINMUX_GPIO(GPIO_PF8, PF8_DATA),
+	PINMUX_GPIO(GPIO_PF7, PF7_DATA),
+	PINMUX_GPIO(GPIO_PF6, PF6_DATA),
+	PINMUX_GPIO(GPIO_PF5, PF5_DATA),
+	PINMUX_GPIO(GPIO_PF4, PF4_DATA),
+	PINMUX_GPIO(GPIO_PF3, PF3_DATA),
+	PINMUX_GPIO(GPIO_PF2, PF2_DATA),
+	PINMUX_GPIO(GPIO_PF1, PF1_DATA),
+	PINMUX_GPIO(GPIO_PF0, PF0_DATA),
+
+	/* Port G */
+	PINMUX_GPIO(GPIO_PG24, PG24_DATA),
+	PINMUX_GPIO(GPIO_PG23, PG23_DATA),
+	PINMUX_GPIO(GPIO_PG22, PG22_DATA),
+	PINMUX_GPIO(GPIO_PG21, PG21_DATA),
+	PINMUX_GPIO(GPIO_PG20, PG20_DATA),
+	PINMUX_GPIO(GPIO_PG19, PG19_DATA),
+	PINMUX_GPIO(GPIO_PG18, PG18_DATA),
+	PINMUX_GPIO(GPIO_PG17, PG17_DATA),
+	PINMUX_GPIO(GPIO_PG16, PG16_DATA),
+	PINMUX_GPIO(GPIO_PG15, PG15_DATA),
+	PINMUX_GPIO(GPIO_PG14, PG14_DATA),
+	PINMUX_GPIO(GPIO_PG13, PG13_DATA),
+	PINMUX_GPIO(GPIO_PG12, PG12_DATA),
+	PINMUX_GPIO(GPIO_PG11, PG11_DATA),
+	PINMUX_GPIO(GPIO_PG10, PG10_DATA),
+	PINMUX_GPIO(GPIO_PG9, PG9_DATA),
+	PINMUX_GPIO(GPIO_PG8, PG8_DATA),
+	PINMUX_GPIO(GPIO_PG7, PG7_DATA),
+	PINMUX_GPIO(GPIO_PG6, PG6_DATA),
+	PINMUX_GPIO(GPIO_PG5, PG5_DATA),
+	PINMUX_GPIO(GPIO_PG4, PG4_DATA),
+	PINMUX_GPIO(GPIO_PG3, PG3_DATA),
+	PINMUX_GPIO(GPIO_PG2, PG2_DATA),
+	PINMUX_GPIO(GPIO_PG1, PG1_DATA),
+	PINMUX_GPIO(GPIO_PG0, PG0_DATA),
+
+	/* Port H - Port H does not have a Data Register */
+
+	/* Port I - not on device */
+
+	/* Port J */
+	PINMUX_GPIO(GPIO_PJ11, PJ11_DATA),
+	PINMUX_GPIO(GPIO_PJ10, PJ10_DATA),
+	PINMUX_GPIO(GPIO_PJ9, PJ9_DATA),
+	PINMUX_GPIO(GPIO_PJ8, PJ8_DATA),
+	PINMUX_GPIO(GPIO_PJ7, PJ7_DATA),
+	PINMUX_GPIO(GPIO_PJ6, PJ6_DATA),
+	PINMUX_GPIO(GPIO_PJ5, PJ5_DATA),
+	PINMUX_GPIO(GPIO_PJ4, PJ4_DATA),
+	PINMUX_GPIO(GPIO_PJ3, PJ3_DATA),
+	PINMUX_GPIO(GPIO_PJ2, PJ2_DATA),
+	PINMUX_GPIO(GPIO_PJ1, PJ1_DATA),
+	PINMUX_GPIO(GPIO_PJ0, PJ0_DATA),
+
+	/* Port K */
+	PINMUX_GPIO(GPIO_PK11, PK11_DATA),
+	PINMUX_GPIO(GPIO_PK10, PK10_DATA),
+	PINMUX_GPIO(GPIO_PK9, PK9_DATA),
+	PINMUX_GPIO(GPIO_PK8, PK8_DATA),
+	PINMUX_GPIO(GPIO_PK7, PK7_DATA),
+	PINMUX_GPIO(GPIO_PK6, PK6_DATA),
+	PINMUX_GPIO(GPIO_PK5, PK5_DATA),
+	PINMUX_GPIO(GPIO_PK4, PK4_DATA),
+	PINMUX_GPIO(GPIO_PK3, PK3_DATA),
+	PINMUX_GPIO(GPIO_PK2, PK2_DATA),
+	PINMUX_GPIO(GPIO_PK1, PK1_DATA),
+	PINMUX_GPIO(GPIO_PK0, PK0_DATA),
+
+	/* INTC */
+	PINMUX_GPIO(GPIO_FN_PINT7_PG, PINT7_PG_MARK),
+	PINMUX_GPIO(GPIO_FN_PINT6_PG, PINT6_PG_MARK),
+	PINMUX_GPIO(GPIO_FN_PINT5_PG, PINT5_PG_MARK),
+	PINMUX_GPIO(GPIO_FN_PINT4_PG, PINT4_PG_MARK),
+	PINMUX_GPIO(GPIO_FN_PINT3_PG, PINT3_PG_MARK),
+	PINMUX_GPIO(GPIO_FN_PINT2_PG, PINT2_PG_MARK),
+	PINMUX_GPIO(GPIO_FN_PINT1_PG, PINT1_PG_MARK),
+
+	PINMUX_GPIO(GPIO_FN_IRQ7_PC, IRQ7_PC_MARK),
+	PINMUX_GPIO(GPIO_FN_IRQ6_PC, IRQ6_PC_MARK),
+	PINMUX_GPIO(GPIO_FN_IRQ5_PC, IRQ5_PC_MARK),
+	PINMUX_GPIO(GPIO_FN_IRQ4_PC, IRQ4_PC_MARK),
+	PINMUX_GPIO(GPIO_FN_IRQ3_PG, IRQ3_PG_MARK),
+	PINMUX_GPIO(GPIO_FN_IRQ2_PG, IRQ2_PG_MARK),
+	PINMUX_GPIO(GPIO_FN_IRQ1_PJ, IRQ1_PJ_MARK),
+	PINMUX_GPIO(GPIO_FN_IRQ0_PJ, IRQ0_PJ_MARK),
+	PINMUX_GPIO(GPIO_FN_IRQ3_PE, IRQ3_PE_MARK),
+	PINMUX_GPIO(GPIO_FN_IRQ2_PE, IRQ2_PE_MARK),
+	PINMUX_GPIO(GPIO_FN_IRQ1_PE, IRQ1_PE_MARK),
+	PINMUX_GPIO(GPIO_FN_IRQ0_PE, IRQ0_PE_MARK),
+
+	/* WDT */
+	PINMUX_GPIO(GPIO_FN_WDTOVF, WDTOVF_MARK),
+
+	/* CAN */
+	PINMUX_GPIO(GPIO_FN_CTX1, CTX1_MARK),
+	PINMUX_GPIO(GPIO_FN_CRX1, CRX1_MARK),
+	PINMUX_GPIO(GPIO_FN_CTX0, CTX0_MARK),
+	PINMUX_GPIO(GPIO_FN_CRX0, CRX0_MARK),
+	PINMUX_GPIO(GPIO_FN_CRX0_CRX1, CRX0CRX1_MARK),
+
+	/* DMAC */
+	PINMUX_GPIO(GPIO_FN_TEND0, TEND0_MARK),
+	PINMUX_GPIO(GPIO_FN_DACK0, DACK0_MARK),
+	PINMUX_GPIO(GPIO_FN_DREQ0, DREQ0_MARK),
+	PINMUX_GPIO(GPIO_FN_TEND1, TEND1_MARK),
+	PINMUX_GPIO(GPIO_FN_DACK1, DACK1_MARK),
+	PINMUX_GPIO(GPIO_FN_DREQ1, DREQ1_MARK),
+
+	/* ADC */
+	PINMUX_GPIO(GPIO_FN_ADTRG, ADTRG_MARK),
+
+	/* BSCh */
+	PINMUX_GPIO(GPIO_FN_A25, A25_MARK),
+	PINMUX_GPIO(GPIO_FN_A24, A24_MARK),
+	PINMUX_GPIO(GPIO_FN_A23, A23_MARK),
+	PINMUX_GPIO(GPIO_FN_A22, A22_MARK),
+	PINMUX_GPIO(GPIO_FN_A21, A21_MARK),
+	PINMUX_GPIO(GPIO_FN_A20, A20_MARK),
+	PINMUX_GPIO(GPIO_FN_A19, A19_MARK),
+	PINMUX_GPIO(GPIO_FN_A18, A18_MARK),
+	PINMUX_GPIO(GPIO_FN_A17, A17_MARK),
+	PINMUX_GPIO(GPIO_FN_A16, A16_MARK),
+	PINMUX_GPIO(GPIO_FN_A15, A15_MARK),
+	PINMUX_GPIO(GPIO_FN_A14, A14_MARK),
+	PINMUX_GPIO(GPIO_FN_A13, A13_MARK),
+	PINMUX_GPIO(GPIO_FN_A12, A12_MARK),
+	PINMUX_GPIO(GPIO_FN_A11, A11_MARK),
+	PINMUX_GPIO(GPIO_FN_A10, A10_MARK),
+	PINMUX_GPIO(GPIO_FN_A9, A9_MARK),
+	PINMUX_GPIO(GPIO_FN_A8, A8_MARK),
+	PINMUX_GPIO(GPIO_FN_A7, A7_MARK),
+	PINMUX_GPIO(GPIO_FN_A6, A6_MARK),
+	PINMUX_GPIO(GPIO_FN_A5, A5_MARK),
+	PINMUX_GPIO(GPIO_FN_A4, A4_MARK),
+	PINMUX_GPIO(GPIO_FN_A3, A3_MARK),
+	PINMUX_GPIO(GPIO_FN_A2, A2_MARK),
+	PINMUX_GPIO(GPIO_FN_A1, A1_MARK),
+	PINMUX_GPIO(GPIO_FN_A0, A0_MARK),
+
+	PINMUX_GPIO(GPIO_FN_D15, D15_MARK),
+	PINMUX_GPIO(GPIO_FN_D14, D14_MARK),
+	PINMUX_GPIO(GPIO_FN_D13, D13_MARK),
+	PINMUX_GPIO(GPIO_FN_D12, D12_MARK),
+	PINMUX_GPIO(GPIO_FN_D11, D11_MARK),
+	PINMUX_GPIO(GPIO_FN_D10, D10_MARK),
+	PINMUX_GPIO(GPIO_FN_D9, D9_MARK),
+	PINMUX_GPIO(GPIO_FN_D8, D8_MARK),
+	PINMUX_GPIO(GPIO_FN_D7, D7_MARK),
+	PINMUX_GPIO(GPIO_FN_D6, D6_MARK),
+	PINMUX_GPIO(GPIO_FN_D5, D5_MARK),
+	PINMUX_GPIO(GPIO_FN_D4, D4_MARK),
+	PINMUX_GPIO(GPIO_FN_D3, D3_MARK),
+	PINMUX_GPIO(GPIO_FN_D2, D2_MARK),
+	PINMUX_GPIO(GPIO_FN_D1, D1_MARK),
+	PINMUX_GPIO(GPIO_FN_D0, D0_MARK),
+
+	PINMUX_GPIO(GPIO_FN_BS, BS_MARK),
+	PINMUX_GPIO(GPIO_FN_CS4, CS4_MARK),
+	PINMUX_GPIO(GPIO_FN_CS3, CS3_MARK),
+	PINMUX_GPIO(GPIO_FN_CS2, CS2_MARK),
+	PINMUX_GPIO(GPIO_FN_CS1, CS1_MARK),
+	PINMUX_GPIO(GPIO_FN_CS0, CS0_MARK),
+	PINMUX_GPIO(GPIO_FN_CS6CE1B, CS6CE1B_MARK),
+	PINMUX_GPIO(GPIO_FN_CS5CE1A, CS5CE1A_MARK),
+	PINMUX_GPIO(GPIO_FN_CE2A, CE2A_MARK),
+	PINMUX_GPIO(GPIO_FN_CE2B, CE2B_MARK),
+	PINMUX_GPIO(GPIO_FN_RD, RD_MARK),
+	PINMUX_GPIO(GPIO_FN_RDWR, RDWR_MARK),
+	PINMUX_GPIO(GPIO_FN_ICIOWRAH, ICIOWRAH_MARK),
+	PINMUX_GPIO(GPIO_FN_ICIORD, ICIORD_MARK),
+	PINMUX_GPIO(GPIO_FN_WE1DQMUWE, WE1DQMUWE_MARK),
+	PINMUX_GPIO(GPIO_FN_WE0DQML, WE0DQML_MARK),
+	PINMUX_GPIO(GPIO_FN_RAS, RAS_MARK),
+	PINMUX_GPIO(GPIO_FN_CAS, CAS_MARK),
+	PINMUX_GPIO(GPIO_FN_CKE, CKE_MARK),
+	PINMUX_GPIO(GPIO_FN_WAIT, WAIT_MARK),
+	PINMUX_GPIO(GPIO_FN_BREQ, BREQ_MARK),
+	PINMUX_GPIO(GPIO_FN_BACK, BACK_MARK),
+	PINMUX_GPIO(GPIO_FN_IOIS16, IOIS16_MARK),
+
+	/* TMU */
+	PINMUX_GPIO(GPIO_FN_TIOC4D, TIOC4D_MARK),
+	PINMUX_GPIO(GPIO_FN_TIOC4C, TIOC4C_MARK),
+	PINMUX_GPIO(GPIO_FN_TIOC4B, TIOC4B_MARK),
+	PINMUX_GPIO(GPIO_FN_TIOC4A, TIOC4A_MARK),
+	PINMUX_GPIO(GPIO_FN_TIOC3D, TIOC3D_MARK),
+	PINMUX_GPIO(GPIO_FN_TIOC3C, TIOC3C_MARK),
+	PINMUX_GPIO(GPIO_FN_TIOC3B, TIOC3B_MARK),
+	PINMUX_GPIO(GPIO_FN_TIOC3A, TIOC3A_MARK),
+	PINMUX_GPIO(GPIO_FN_TIOC2B, TIOC2B_MARK),
+	PINMUX_GPIO(GPIO_FN_TIOC1B, TIOC1B_MARK),
+	PINMUX_GPIO(GPIO_FN_TIOC2A, TIOC2A_MARK),
+	PINMUX_GPIO(GPIO_FN_TIOC1A, TIOC1A_MARK),
+	PINMUX_GPIO(GPIO_FN_TIOC0D, TIOC0D_MARK),
+	PINMUX_GPIO(GPIO_FN_TIOC0C, TIOC0C_MARK),
+	PINMUX_GPIO(GPIO_FN_TIOC0B, TIOC0B_MARK),
+	PINMUX_GPIO(GPIO_FN_TIOC0A, TIOC0A_MARK),
+	PINMUX_GPIO(GPIO_FN_TCLKD, TCLKD_MARK),
+	PINMUX_GPIO(GPIO_FN_TCLKC, TCLKC_MARK),
+	PINMUX_GPIO(GPIO_FN_TCLKB, TCLKB_MARK),
+	PINMUX_GPIO(GPIO_FN_TCLKA, TCLKA_MARK),
+
+	/* SCIF */
+	PINMUX_GPIO(GPIO_FN_TXD0, TXD0_MARK),
+	PINMUX_GPIO(GPIO_FN_RXD0, RXD0_MARK),
+	PINMUX_GPIO(GPIO_FN_SCK0, SCK0_MARK),
+	PINMUX_GPIO(GPIO_FN_TXD1, TXD1_MARK),
+	PINMUX_GPIO(GPIO_FN_RXD1, RXD1_MARK),
+	PINMUX_GPIO(GPIO_FN_SCK1, SCK1_MARK),
+	PINMUX_GPIO(GPIO_FN_TXD2, TXD2_MARK),
+	PINMUX_GPIO(GPIO_FN_RXD2, RXD2_MARK),
+	PINMUX_GPIO(GPIO_FN_SCK2, SCK2_MARK),
+	PINMUX_GPIO(GPIO_FN_RTS3, RTS3_MARK),
+	PINMUX_GPIO(GPIO_FN_CTS3, CTS3_MARK),
+	PINMUX_GPIO(GPIO_FN_TXD3, TXD3_MARK),
+	PINMUX_GPIO(GPIO_FN_RXD3, RXD3_MARK),
+	PINMUX_GPIO(GPIO_FN_SCK3, SCK3_MARK),
+	PINMUX_GPIO(GPIO_FN_TXD4, TXD4_MARK),
+	PINMUX_GPIO(GPIO_FN_RXD4, RXD4_MARK),
+	PINMUX_GPIO(GPIO_FN_TXD5, TXD5_MARK),
+	PINMUX_GPIO(GPIO_FN_RXD5, RXD5_MARK),
+	PINMUX_GPIO(GPIO_FN_TXD6, TXD6_MARK),
+	PINMUX_GPIO(GPIO_FN_RXD6, RXD6_MARK),
+	PINMUX_GPIO(GPIO_FN_TXD7, TXD7_MARK),
+	PINMUX_GPIO(GPIO_FN_RXD7, RXD7_MARK),
+	PINMUX_GPIO(GPIO_FN_RTS1, RTS1_MARK),
+	PINMUX_GPIO(GPIO_FN_CTS1, CTS1_MARK),
+
+	/* RSPI */
+	PINMUX_GPIO(GPIO_FN_RSPCK0, RSPCK0_MARK),
+	PINMUX_GPIO(GPIO_FN_MOSI0, MOSI0_MARK),
+	PINMUX_GPIO(GPIO_FN_MISO0_PF12, MISO0_PF12_MARK),
+	PINMUX_GPIO(GPIO_FN_MISO1, MISO1_MARK),
+	PINMUX_GPIO(GPIO_FN_SSL00, SSL00_MARK),
+	PINMUX_GPIO(GPIO_FN_RSPCK1, RSPCK1_MARK),
+	PINMUX_GPIO(GPIO_FN_MOSI1, MOSI1_MARK),
+	PINMUX_GPIO(GPIO_FN_MISO1_PG19, MISO1_PG19_MARK),
+	PINMUX_GPIO(GPIO_FN_SSL10, SSL10_MARK),
+
+	/* IIC3 */
+	PINMUX_GPIO(GPIO_FN_SCL0, SCL0_MARK),
+	PINMUX_GPIO(GPIO_FN_SCL1, SCL1_MARK),
+	PINMUX_GPIO(GPIO_FN_SCL2, SCL2_MARK),
+	PINMUX_GPIO(GPIO_FN_SDA0, SDA0_MARK),
+	PINMUX_GPIO(GPIO_FN_SDA1, SDA1_MARK),
+	PINMUX_GPIO(GPIO_FN_SDA2, SDA2_MARK),
+
+	/* SSI */
+	PINMUX_GPIO(GPIO_FN_SSISCK0, SSISCK0_MARK),
+	PINMUX_GPIO(GPIO_FN_SSIWS0, SSIWS0_MARK),
+	PINMUX_GPIO(GPIO_FN_SSITXD0, SSITXD0_MARK),
+	PINMUX_GPIO(GPIO_FN_SSIRXD0, SSIRXD0_MARK),
+	PINMUX_GPIO(GPIO_FN_SSIWS1, SSIWS1_MARK),
+	PINMUX_GPIO(GPIO_FN_SSIWS2, SSIWS2_MARK),
+	PINMUX_GPIO(GPIO_FN_SSIWS3, SSIWS3_MARK),
+	PINMUX_GPIO(GPIO_FN_SSISCK1, SSISCK1_MARK),
+	PINMUX_GPIO(GPIO_FN_SSISCK2, SSISCK2_MARK),
+	PINMUX_GPIO(GPIO_FN_SSISCK3, SSISCK3_MARK),
+	PINMUX_GPIO(GPIO_FN_SSIDATA1, SSIDATA1_MARK),
+	PINMUX_GPIO(GPIO_FN_SSIDATA2, SSIDATA2_MARK),
+	PINMUX_GPIO(GPIO_FN_SSIDATA3, SSIDATA3_MARK),
+	PINMUX_GPIO(GPIO_FN_AUDIO_CLK, AUDIO_CLK_MARK),
+
+	/* SIOF */ /* NOTE Shares AUDIO_CLK with SSI */
+	PINMUX_GPIO(GPIO_FN_SIOFTXD, SIOFTXD_MARK),
+	PINMUX_GPIO(GPIO_FN_SIOFRXD, SIOFRXD_MARK),
+	PINMUX_GPIO(GPIO_FN_SIOFSYNC, SIOFSYNC_MARK),
+	PINMUX_GPIO(GPIO_FN_SIOFSCK, SIOFSCK_MARK),
+
+	/* SPDIF */ /* NOTE Shares AUDIO_CLK with SSI */
+	PINMUX_GPIO(GPIO_FN_SPDIF_IN, SPDIF_IN_MARK),
+	PINMUX_GPIO(GPIO_FN_SPDIF_OUT, SPDIF_OUT_MARK),
+
+	/* NANDFMC */ /* NOTE Controller is not available in boot mode 0 */
+	PINMUX_GPIO(GPIO_FN_FCE, FCE_MARK),
+	PINMUX_GPIO(GPIO_FN_FRB, FRB_MARK),
+
+	/* VDC3 */
+	PINMUX_GPIO(GPIO_FN_DV_CLK, DV_CLK_MARK),
+	PINMUX_GPIO(GPIO_FN_DV_VSYNC, DV_VSYNC_MARK),
+	PINMUX_GPIO(GPIO_FN_DV_HSYNC, DV_HSYNC_MARK),
+
+	PINMUX_GPIO(GPIO_FN_DV_DATA7, DV_DATA7_MARK),
+	PINMUX_GPIO(GPIO_FN_DV_DATA6, DV_DATA6_MARK),
+	PINMUX_GPIO(GPIO_FN_DV_DATA5, DV_DATA5_MARK),
+	PINMUX_GPIO(GPIO_FN_DV_DATA4, DV_DATA4_MARK),
+	PINMUX_GPIO(GPIO_FN_DV_DATA3, DV_DATA3_MARK),
+	PINMUX_GPIO(GPIO_FN_DV_DATA2, DV_DATA2_MARK),
+	PINMUX_GPIO(GPIO_FN_DV_DATA1, DV_DATA1_MARK),
+	PINMUX_GPIO(GPIO_FN_DV_DATA0, DV_DATA0_MARK),
+
+	PINMUX_GPIO(GPIO_FN_LCD_CLK, LCD_CLK_MARK),
+	PINMUX_GPIO(GPIO_FN_LCD_EXTCLK, LCD_EXTCLK_MARK),
+	PINMUX_GPIO(GPIO_FN_LCD_VSYNC, LCD_VSYNC_MARK),
+	PINMUX_GPIO(GPIO_FN_LCD_HSYNC, LCD_HSYNC_MARK),
+	PINMUX_GPIO(GPIO_FN_LCD_DE, LCD_DE_MARK),
+
+	PINMUX_GPIO(GPIO_FN_LCD_DATA15, LCD_DATA15_MARK),
+	PINMUX_GPIO(GPIO_FN_LCD_DATA14, LCD_DATA14_MARK),
+	PINMUX_GPIO(GPIO_FN_LCD_DATA13, LCD_DATA13_MARK),
+	PINMUX_GPIO(GPIO_FN_LCD_DATA12, LCD_DATA12_MARK),
+	PINMUX_GPIO(GPIO_FN_LCD_DATA11, LCD_DATA11_MARK),
+	PINMUX_GPIO(GPIO_FN_LCD_DATA10, LCD_DATA10_MARK),
+	PINMUX_GPIO(GPIO_FN_LCD_DATA9, LCD_DATA9_MARK),
+	PINMUX_GPIO(GPIO_FN_LCD_DATA8, LCD_DATA8_MARK),
+	PINMUX_GPIO(GPIO_FN_LCD_DATA7, LCD_DATA7_MARK),
+	PINMUX_GPIO(GPIO_FN_LCD_DATA6, LCD_DATA6_MARK),
+	PINMUX_GPIO(GPIO_FN_LCD_DATA5, LCD_DATA5_MARK),
+	PINMUX_GPIO(GPIO_FN_LCD_DATA4, LCD_DATA4_MARK),
+	PINMUX_GPIO(GPIO_FN_LCD_DATA3, LCD_DATA3_MARK),
+	PINMUX_GPIO(GPIO_FN_LCD_DATA2, LCD_DATA2_MARK),
+	PINMUX_GPIO(GPIO_FN_LCD_DATA1, LCD_DATA1_MARK),
+	PINMUX_GPIO(GPIO_FN_LCD_DATA0, LCD_DATA0_MARK),
+
+	PINMUX_GPIO(GPIO_FN_LCD_M_DISP, LCD_M_DISP_MARK),
+};
+
+static struct pinmux_cfg_reg pinmux_config_regs[] = {
+	{ PINMUX_CFG_REG("PAIOR0", 0xfffe3812, 16, 1) {
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PA3_IN, PA3_OUT,
+		PA2_IN, PA2_OUT,
+		PA1_IN, PA1_OUT,
+		PA0_IN,	PA0_OUT }
+	},
+
+	{ PINMUX_CFG_REG("PBCR5", 0xfffe3824, 16, 4) {
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PB22MD_00, PB22MD_01, PB22MD_10, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PB21MD_0, PB21MD_1, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, PB20MD_1, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+
+	},
+	{ PINMUX_CFG_REG("PBCR4", 0xfffe3826, 16, 4) {
+		0, PB19MD_01, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, PB18MD_01, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, PB17MD_01, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, PB16MD_01, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG("PBCR3", 0xfffe3828, 16, 4) {
+		0, PB15MD_01, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, PB14MD_01, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, PB13MD_01, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, PB12MD_01, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG("PBCR2", 0xfffe382a, 16, 4) {
+		0, PB11MD_01, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, PB10MD_01, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, PB9MD_01, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, PB8MD_01, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG("PBCR1", 0xfffe382c, 16, 4) {
+		0, PB7MD_01, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, PB6MD_01, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, PB5MD_01, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, PB4MD_01, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG("PBCR0", 0xfffe382e, 16, 4) {
+		0, PB3MD_1, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, PB2MD_1, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, PB1MD_1, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+
+	{ PINMUX_CFG_REG("PBIOR1", 0xfffe3830, 16, 1) {
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0,
+		PB22_IN, PB22_OUT,
+		PB21_IN, PB21_OUT,
+		PB20_IN, PB20_OUT,
+		PB19_IN, PB19_OUT,
+		PB18_IN, PB18_OUT,
+		PB17_IN, PB17_OUT,
+		PB16_IN, PB16_OUT }
+	},
+
+	{ PINMUX_CFG_REG("PBIOR0", 0xfffe3832, 16, 1) {
+		PB15_IN, PB15_OUT,
+		PB14_IN, PB14_OUT,
+		PB13_IN, PB13_OUT,
+		PB12_IN, PB12_OUT,
+		PB11_IN, PB11_OUT,
+		PB10_IN, PB10_OUT,
+		PB9_IN, PB9_OUT,
+		PB8_IN, PB8_OUT,
+		PB7_IN, PB7_OUT,
+		PB6_IN, PB6_OUT,
+		PB5_IN, PB5_OUT,
+		PB4_IN, PB4_OUT,
+		PB3_IN, PB3_OUT,
+		PB2_IN, PB2_OUT,
+		PB1_IN, PB1_OUT,
+		0, 0 }
+	},
+
+	{ PINMUX_CFG_REG("PCCR2", 0xfffe384a, 16, 4) {
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PC10MD_0, PC10MD_1, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PC9MD_0, PC9MD_1, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PC8MD_00, PC8MD_01, PC8MD_10, PC8MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG("PCCR1", 0xfffe384c, 16, 4) {
+		PC7MD_00, PC7MD_01, PC7MD_10, PC7MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PC6MD_00, PC6MD_01, PC6MD_10, PC6MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PC5MD_00, PC5MD_01, PC5MD_10, PC5MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PC4MD_0, PC4MD_1, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG("PCCR0", 0xfffe384e, 16, 4) {
+		PC3MD_0, PC3MD_1, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PC2MD_0, PC2MD_1, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PC1MD_0, PC1MD_1, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PC0MD_0, PC0MD_1, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+
+	{ PINMUX_CFG_REG("PCIOR0", 0xfffe3852, 16, 1) {
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+		PC10_IN, PC10_OUT,
+		PC9_IN, PC9_OUT,
+		PC8_IN, PC8_OUT,
+		PC7_IN, PC7_OUT,
+		PC6_IN, PC6_OUT,
+		PC5_IN, PC5_OUT,
+		PC4_IN, PC4_OUT,
+		PC3_IN, PC3_OUT,
+		PC2_IN, PC2_OUT,
+		PC1_IN, PC1_OUT,
+		PC0_IN, PC0_OUT
+	 }
+	},
+
+	{ PINMUX_CFG_REG("PDCR3", 0xfffe3868, 16, 4) {
+		0, PD15MD_01, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, PD14MD_01, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, PD13MD_01, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, PD12MD_01, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG("PDCR2", 0xfffe386a, 16, 4) {
+		0, PD11MD_01, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, PD10MD_01, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, PD9MD_01, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, PD8MD_01, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG("PDCR1", 0xfffe386c, 16, 4) {
+		0, PD7MD_01, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, PD6MD_01, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, PD5MD_01, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, PD4MD_01, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG("PDCR0", 0xfffe386e, 16, 4) {
+		0, PD3MD_01, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, PD2MD_01, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, PD1MD_01, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, PD0MD_01, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+
+	{ PINMUX_CFG_REG("PDIOR0", 0xfffe3872, 16, 1) {
+		PD15_IN, PD15_OUT,
+		PD14_IN, PD14_OUT,
+		PD13_IN, PD13_OUT,
+		PD12_IN, PD12_OUT,
+		PD11_IN, PD11_OUT,
+		PD10_IN, PD10_OUT,
+		PD9_IN, PD9_OUT,
+		PD8_IN, PD8_OUT,
+		PD7_IN, PD7_OUT,
+		PD6_IN, PD6_OUT,
+		PD5_IN, PD5_OUT,
+		PD4_IN, PD4_OUT,
+		PD3_IN, PD3_OUT,
+		PD2_IN, PD2_OUT,
+		PD1_IN, PD1_OUT,
+		PD0_IN, PD0_OUT }
+	},
+
+	{ PINMUX_CFG_REG("PECR1", 0xfffe388c, 16, 4) {
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PE5MD_00, PE5MD_01, 0, PE5MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PE4MD_00, PE4MD_01, 0, PE4MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+
+	{ PINMUX_CFG_REG("PECR0", 0xfffe388e, 16, 4) {
+		PE3MD_00, PE3MD_01, 0, PE3MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PE2MD_00, PE2MD_01, 0, PE2MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PE1MD_000, PE1MD_001, PE1MD_010, PE1MD_011,
+		PE1MD_100, PE1MD_101, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PE0MD_00, PE0MD_01, PE0MD_10, PE0MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+
+	{ PINMUX_CFG_REG("PEIOR0", 0xfffe3892, 16, 1) {
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0,
+		PE5_IN, PE5_OUT,
+		PE4_IN, PE4_OUT,
+		PE3_IN, PE3_OUT,
+		PE2_IN, PE2_OUT,
+		PE1_IN, PE1_OUT,
+		PE0_IN, PE0_OUT }
+	},
+
+	{ PINMUX_CFG_REG("PFCR3", 0xfffe38a8, 16, 4) {
+		PF12MD_000, PF12MD_001, 0, PF12MD_011,
+		PF12MD_100, PF12MD_101, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+
+	{ PINMUX_CFG_REG("PFCR2", 0xfffe38aa, 16, 4) {
+		PF11MD_000, PF11MD_001, PF11MD_010, PF11MD_011,
+		PF11MD_100, PF11MD_101, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PF10MD_000, PF10MD_001, PF10MD_010, PF10MD_011,
+		PF10MD_100, PF10MD_101, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PF9MD_000, PF9MD_001, PF9MD_010, PF9MD_011,
+		PF9MD_100, PF9MD_101, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PF8MD_00, PF8MD_01, PF8MD_10, PF8MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+
+	{ PINMUX_CFG_REG("PFCR1", 0xfffe38ac, 16, 4) {
+		PF7MD_000, PF7MD_001, PF7MD_010, PF7MD_011,
+		PF7MD_100, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PF6MD_000, PF6MD_001, PF6MD_010, PF6MD_011,
+		PF6MD_100, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PF5MD_000, PF5MD_001, PF5MD_010, PF5MD_011,
+		PF5MD_100, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PF4MD_000, PF4MD_001, PF4MD_010, PF4MD_011,
+		PF4MD_100, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+
+	{ PINMUX_CFG_REG("PFCR0", 0xfffe38ae, 16, 4) {
+		PF3MD_000, PF3MD_001, PF3MD_010, PF3MD_011,
+		PF3MD_100, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PF2MD_000, PF2MD_001, PF2MD_010, PF2MD_011,
+		PF2MD_100, PF2MD_101, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PF1MD_000, PF1MD_001, PF1MD_010, PF1MD_011,
+		PF1MD_100, PF1MD_101, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0
+	 }
+	},
+
+	{ PINMUX_CFG_REG("PFIOR0", 0xfffe38b2, 16, 1) {
+		0, 0, 0, 0, 0, 0,
+		PF12_IN, PF12_OUT,
+		PF11_IN, PF11_OUT,
+		PF10_IN, PF10_OUT,
+		PF9_IN, PF9_OUT,
+		PF8_IN, PF8_OUT,
+		PF7_IN, PF7_OUT,
+		PF6_IN, PF6_OUT,
+		PF5_IN, PF5_OUT,
+		PF4_IN, PF4_OUT,
+		PF3_IN, PF3_OUT,
+		PF2_IN, PF2_OUT,
+		PF1_IN, PF1_OUT,
+		PF0_IN, PF0_OUT }
+	},
+
+	{ PINMUX_CFG_REG("PGCR7", 0xfffe38c0, 16, 4) {
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PG0MD_000, PG0MD_001, PG0MD_010, PG0MD_011,
+		PG0MD_100, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+
+	{ PINMUX_CFG_REG("PGCR6", 0xfffe38c2, 16, 4) {
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PG24MD_00, PG24MD_01, PG24MD_10, PG24MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+
+	{ PINMUX_CFG_REG("PGCR5", 0xfffe38c4, 16, 4) {
+		PG23MD_00, PG23MD_01, PG23MD_10, PG23MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PG22MD_00, PG22MD_01, PG22MD_10, PG22MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PG21MD_00, PG21MD_01, PG21MD_10, PG21MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PG20MD_000, PG20MD_001, PG20MD_010, PG20MD_011,
+		PG20MD_100, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+
+	{ PINMUX_CFG_REG("PGCR4", 0xfffe38c6, 16, 4) {
+		PG19MD_000, PG19MD_001, PG19MD_010, PG19MD_011,
+		PG19MD_100, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PG18MD_000, PG18MD_001, PG18MD_010, PG18MD_011,
+		PG18MD_100, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PG17MD_000, PG17MD_001, PG17MD_010, PG17MD_011,
+		PG17MD_100, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PG16MD_000, PG16MD_001, PG16MD_010, PG16MD_011,
+		PG16MD_100, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+
+	{ PINMUX_CFG_REG("PGCR3", 0xfffe38c8, 16, 4) {
+		PG15MD_000, PG15MD_001, PG15MD_010, PG15MD_011,
+		PG15MD_100, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PG14MD_000, PG14MD_001, PG14MD_010, 0,
+		PG14MD_100, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PG13MD_000, PG13MD_001, PG13MD_010, 0,
+		PG13MD_100, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PG12MD_000, PG12MD_001, PG12MD_010, 0,
+		PG12MD_100, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG("PGCR2", 0xfffe38ca, 16, 4) {
+		PG11MD_000, PG11MD_001, PG11MD_010, PG11MD_011,
+		PG11MD_100, PG11MD_101, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PG10MD_000, PG10MD_001, PG10MD_010, PG10MD_011,
+		PG10MD_100, PG10MD_101, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PG9MD_000, PG9MD_001, PG9MD_010, PG9MD_011,
+		PG9MD_100, PG9MD_101, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PG8MD_000, PG8MD_001, PG8MD_010, PG8MD_011,
+		PG8MD_100, PG8MD_101, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+
+	{ PINMUX_CFG_REG("PGCR1", 0xfffe38cc, 16, 4) {
+		PG7MD_00, PG7MD_01, PG7MD_10, PG7MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PG6MD_00, PG6MD_01, PG6MD_10, PG6MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PG5MD_00, PG5MD_01, PG5MD_10, PG5MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PG4MD_00, PG4MD_01, PG4MD_10, PG4MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG("PGCR0", 0xfffe38ce, 16, 4) {
+		PG3MD_00, PG3MD_01, PG3MD_10, PG3MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PG2MD_00, PG2MD_01, PG2MD_10, PG2MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PG1MD_00, PG1MD_01, PG1MD_10, PG1MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG("PGIOR1", 0xfffe38d0, 16, 1) {
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0,
+		PG24_IN, PG24_OUT,
+		PG23_IN, PG23_OUT,
+		PG22_IN, PG22_OUT,
+		PG21_IN, PG21_OUT,
+		PG20_IN, PG20_OUT,
+		PG19_IN, PG19_OUT,
+		PG18_IN, PG18_OUT,
+		PG17_IN, PG17_OUT,
+		PG16_IN, PG16_OUT }
+	},
+
+	{ PINMUX_CFG_REG("PGIOR0", 0xfffe38d2, 16, 1) {
+		PG15_IN, PG15_OUT,
+		PG14_IN, PG14_OUT,
+		PG13_IN, PG13_OUT,
+		PG12_IN, PG12_OUT,
+		PG11_IN, PG11_OUT,
+		PG10_IN, PG10_OUT,
+		PG9_IN, PG9_OUT,
+		PG8_IN, PG8_OUT,
+		PG7_IN, PG7_OUT,
+		PG6_IN, PG6_OUT,
+		PG5_IN, PG5_OUT,
+		PG4_IN, PG4_OUT,
+		PG3_IN, PG3_OUT,
+		PG2_IN, PG2_OUT,
+		PG1_IN, PG1_OUT,
+		PG0_IN, PG0_OUT
+	 }
+	},
+
+	{ PINMUX_CFG_REG("PHCR1", 0xfffe38ec, 16, 4) {
+		PH7MD_0, PH7MD_1, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PH6MD_0, PH6MD_1, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PH5MD_0, PH5MD_1, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PH4MD_0, PH4MD_1, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+
+	{ PINMUX_CFG_REG("PHCR0", 0xfffe38ee, 16, 4) {
+		PH3MD_0, PH3MD_1, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PH2MD_0, PH2MD_1, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PH1MD_0, PH1MD_1, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PH0MD_0, PH0MD_1, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+
+	{ PINMUX_CFG_REG("PJCR2", 0xfffe390a, 16, 4) {
+		PJ11MD_00, PJ11MD_01, PJ11MD_10, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PJ10MD_00, PJ10MD_01, PJ10MD_10, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PJ9MD_00, PJ9MD_01, PJ9MD_10, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PJ8MD_00, PJ8MD_01, PJ8MD_10, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG("PJCR1", 0xfffe390c, 16, 4) {
+		PJ7MD_00, PJ7MD_01, PJ7MD_10, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PJ6MD_00, PJ6MD_01, PJ6MD_10, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PJ5MD_00, PJ5MD_01, PJ5MD_10, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PJ4MD_00, PJ4MD_01, PJ4MD_10, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG("PJCR0", 0xfffe390e, 16, 4) {
+		PJ3MD_00, PJ3MD_01, PJ3MD_10, PJ3MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PJ2MD_000, PJ2MD_001, PJ2MD_010, PJ2MD_011,
+		PJ2MD_100, PJ2MD_101, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PJ1MD_000, PJ1MD_001, PJ1MD_010, PJ1MD_011,
+		PJ1MD_100, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PJ0MD_000, PJ0MD_001, PJ0MD_010, PJ0MD_011,
+		PJ0MD_100, PJ0MD_101, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0, }
+	},
+	{ PINMUX_CFG_REG("PJIOR0", 0xfffe3912, 16, 1) {
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PJ11_IN, PJ11_OUT,
+		PJ10_IN, PJ10_OUT,
+		PJ9_IN, PJ9_OUT,
+		PJ8_IN, PJ8_OUT,
+		PJ7_IN, PJ7_OUT,
+		PJ6_IN, PJ6_OUT,
+		PJ5_IN, PJ5_OUT,
+		PJ4_IN, PJ4_OUT,
+		PJ3_IN, PJ3_OUT,
+		PJ2_IN, PJ2_OUT,
+		PJ1_IN, PJ1_OUT,
+		PJ0_IN, PJ0_OUT }
+	},
+
+	{ PINMUX_CFG_REG("PKCR2", 0xfffe392a, 16, 4) {
+		PK11MD_00, PK11MD_01, PK11MD_10, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PK10MD_00, PK10MD_01, PK10MD_10, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PK9MD_00, PK9MD_01, PK9MD_10, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PK8MD_00, PK8MD_01, PK8MD_10, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+
+	{ PINMUX_CFG_REG("PKCR1", 0xfffe392c, 16, 4) {
+		PK7MD_00, PK7MD_01, PK7MD_10, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PK6MD_00, PK6MD_01, PK6MD_10, 0,  0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PK5MD_00, PK5MD_01, PK5MD_10, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PK4MD_00, PK4MD_01, PK4MD_10, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG("PKCR0", 0xfffe392e, 16, 4) {
+		PK3MD_00, PK3MD_01, PK3MD_10, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PK2MD_00, PK2MD_01, PK2MD_10, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PK1MD_00, PK1MD_01, PK1MD_10, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PK0MD_00, PK0MD_01, PK0MD_10, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+
+	{ PINMUX_CFG_REG("PKIOR0", 0xfffe3932, 16, 1) {
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PJ11_IN, PJ11_OUT,
+		PJ10_IN, PJ10_OUT,
+		PJ9_IN, PJ9_OUT,
+		PJ8_IN, PJ8_OUT,
+		PJ7_IN, PJ7_OUT,
+		PJ6_IN, PJ6_OUT,
+		PJ5_IN, PJ5_OUT,
+		PJ4_IN, PJ4_OUT,
+		PJ3_IN, PJ3_OUT,
+		PJ2_IN, PJ2_OUT,
+		PJ1_IN, PJ1_OUT,
+		PJ0_IN, PJ0_OUT }
+	},
+	{}
+};
+
+static struct pinmux_data_reg pinmux_data_regs[] = {
+	{ PINMUX_DATA_REG("PADR1", 0xfffe3814, 16) {
+		0, 0, 0, 0, 0, 0, 0, PA3_DATA,
+		0, 0, 0, 0, 0, 0, 0, PA2_DATA }
+	},
+
+	{ PINMUX_DATA_REG("PADR0", 0xfffe3816, 16) {
+		0, 0, 0, 0, 0, 0, 0, PA1_DATA,
+		0, 0, 0, 0, 0, 0, 0, PA0_DATA }
+	},
+
+	{ PINMUX_DATA_REG("PBDR1", 0xfffe3834, 16) {
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, PB22_DATA, PB21_DATA, PB20_DATA,
+		PB19_DATA, PB18_DATA, PB17_DATA, PB16_DATA }
+	},
+
+	{ PINMUX_DATA_REG("PBDR0", 0xfffe3836, 16) {
+		PB15_DATA, PB14_DATA, PB13_DATA, PB12_DATA,
+		PB11_DATA, PB10_DATA, PB9_DATA, PB8_DATA,
+		PB7_DATA, PB6_DATA, PB5_DATA, PB4_DATA,
+		PB3_DATA, PB2_DATA, PB1_DATA, 0 }
+	},
+
+	{ PINMUX_DATA_REG("PCDR0", 0xfffe3856, 16) {
+		0, 0, 0, 0,
+		0, PC10_DATA, PC9_DATA, PC8_DATA,
+		PC7_DATA, PC6_DATA, PC5_DATA, PC4_DATA,
+		PC3_DATA, PC2_DATA, PC1_DATA, PC0_DATA }
+	},
+
+	{ PINMUX_DATA_REG("PDDR0", 0xfffe3876, 16) {
+		PD15_DATA, PD14_DATA, PD13_DATA, PD12_DATA,
+		PD11_DATA, PD10_DATA, PD9_DATA, PD8_DATA,
+		PD7_DATA, PD6_DATA, PD5_DATA, PD4_DATA,
+		PD3_DATA, PD2_DATA, PD1_DATA, PD0_DATA }
+	},
+
+	{ PINMUX_DATA_REG("PEDR0", 0xfffe3896, 16) {
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, PE5_DATA, PE4_DATA,
+		PE3_DATA, PE2_DATA, PE1_DATA, PE0_DATA }
+	},
+
+	{ PINMUX_DATA_REG("PFDR0", 0xfffe38b6, 16) {
+		0, 0, 0, PF12_DATA,
+		PF11_DATA, PF10_DATA, PF9_DATA, PF8_DATA,
+		PF7_DATA, PF6_DATA, PF5_DATA, PF4_DATA,
+		PF3_DATA, PF2_DATA, PF1_DATA, PF0_DATA }
+	},
+
+	{ PINMUX_DATA_REG("PGDR1", 0xfffe38d4, 16) {
+		0, 0, 0, 0, 0, 0, 0, PG24_DATA,
+		PG23_DATA, PG22_DATA, PG21_DATA, PG20_DATA,
+		PG19_DATA, PG18_DATA, PG17_DATA, PG16_DATA }
+	},
+
+	{ PINMUX_DATA_REG("PGDR0", 0xfffe38d6, 16) {
+		PG15_DATA, PG14_DATA, PG13_DATA, PG12_DATA,
+		PG11_DATA, PG10_DATA, PG9_DATA, PG8_DATA,
+		PG7_DATA, PG6_DATA, PG5_DATA, PG4_DATA,
+		PG3_DATA, PG2_DATA, PG1_DATA, PG0_DATA }
+	},
+	{ PINMUX_DATA_REG("PJDR0", 0xfffe3916, 16) {
+		0, 0, 0, PJ12_DATA,
+		PJ11_DATA, PJ10_DATA, PJ9_DATA, PJ8_DATA,
+		PJ7_DATA, PJ6_DATA, PJ5_DATA, PJ4_DATA,
+		PJ3_DATA, PJ2_DATA, PJ1_DATA, PJ0_DATA }
+	},
+	{ PINMUX_DATA_REG("PKDR0", 0xfffe3936, 16) {
+		0, 0, 0, PK12_DATA,
+		PK11_DATA, PK10_DATA, PK9_DATA, PK8_DATA,
+		PK7_DATA, PK6_DATA, PK5_DATA, PK4_DATA,
+		PK3_DATA, PK2_DATA, PK1_DATA, PK0_DATA }
+	},
+	{ }
+};
+
+static struct pinmux_info sh7264_pinmux_info = {
+	.name = "sh7264_pfc",
+	.reserved_id = PINMUX_RESERVED,
+	.data = { PINMUX_DATA_BEGIN, PINMUX_DATA_END },
+	.input = { PINMUX_INPUT_BEGIN, PINMUX_INPUT_END, FORCE_IN },
+	.output = { PINMUX_OUTPUT_BEGIN, PINMUX_OUTPUT_END, FORCE_OUT },
+	.mark = { PINMUX_MARK_BEGIN, PINMUX_MARK_END },
+	.function = { PINMUX_FUNCTION_BEGIN, PINMUX_FUNCTION_END },
+
+	.first_gpio = GPIO_PA3,
+	.last_gpio = GPIO_FN_LCD_M_DISP,
+
+	.gpios = pinmux_gpios,
+	.cfg_regs = pinmux_config_regs,
+	.data_regs = pinmux_data_regs,
+
+	.gpio_data = pinmux_data,
+	.gpio_data_size = ARRAY_SIZE(pinmux_data),
+};
+
+static int __init plat_pinmux_setup(void)
+{
+	return register_pinmux(&sh7264_pinmux_info);
+}
+arch_initcall(plat_pinmux_setup);
diff --git a/arch/sh/kernel/cpu/sh2a/pinmux-sh7269.c b/arch/sh/kernel/cpu/sh2a/pinmux-sh7269.c
new file mode 100644
index 0000000..f25127c
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh2a/pinmux-sh7269.c
@@ -0,0 +1,2800 @@
+/*
+ * SH7269 Pinmux
+ *
+ * Copyright (C) 2012  Renesas Electronics Europe Ltd
+ * Copyright (C) 2012  Phil Edworthy
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <cpu/sh7269.h>
+
+enum {
+	PINMUX_RESERVED = 0,
+
+	PINMUX_DATA_BEGIN,
+	/* Port A */
+	PA1_DATA, PA0_DATA,
+	/* Port B */
+	PB22_DATA, PB21_DATA, PB20_DATA,
+	PB19_DATA, PB18_DATA, PB17_DATA, PB16_DATA,
+	PB15_DATA, PB14_DATA, PB13_DATA, PB12_DATA,
+	PB11_DATA, PB10_DATA, PB9_DATA, PB8_DATA,
+	PB7_DATA, PB6_DATA, PB5_DATA, PB4_DATA,
+	PB3_DATA, PB2_DATA, PB1_DATA,
+	/* Port C */
+	PC8_DATA,
+	PC7_DATA, PC6_DATA, PC5_DATA, PC4_DATA,
+	PC3_DATA, PC2_DATA, PC1_DATA, PC0_DATA,
+	/* Port D */
+	PD15_DATA, PD14_DATA, PD13_DATA, PD12_DATA,
+	PD11_DATA, PD10_DATA, PD9_DATA, PD8_DATA,
+	PD7_DATA, PD6_DATA, PD5_DATA, PD4_DATA,
+	PD3_DATA, PD2_DATA, PD1_DATA, PD0_DATA,
+	/* Port E */
+	PE7_DATA, PE6_DATA, PE5_DATA, PE4_DATA,
+	PE3_DATA, PE2_DATA, PE1_DATA, PE0_DATA,
+	/* Port F */
+	PF23_DATA, PF22_DATA, PF21_DATA, PF20_DATA,
+	PF19_DATA, PF18_DATA, PF17_DATA, PF16_DATA,
+	PF15_DATA, PF14_DATA, PF13_DATA, PF12_DATA,
+	PF11_DATA, PF10_DATA, PF9_DATA, PF8_DATA,
+	PF7_DATA, PF6_DATA, PF5_DATA, PF4_DATA,
+	PF3_DATA, PF2_DATA, PF1_DATA, PF0_DATA,
+	/* Port G */
+	PG27_DATA, PG26_DATA, PG25_DATA, PG24_DATA,
+	PG23_DATA, PG22_DATA, PG21_DATA, PG20_DATA,
+	PG19_DATA, PG18_DATA, PG17_DATA, PG16_DATA,
+	PG15_DATA, PG14_DATA, PG13_DATA, PG12_DATA,
+	PG11_DATA, PG10_DATA, PG9_DATA, PG8_DATA,
+	PG7_DATA, PG6_DATA, PG5_DATA, PG4_DATA,
+	PG3_DATA, PG2_DATA, PG1_DATA, PG0_DATA,
+	/* Port H */
+	/* NOTE - Port H does not have a Data Register, but PH Data is
+	   connected to PH Port Register */
+	PH7_DATA, PH6_DATA, PH5_DATA, PH4_DATA,
+	PH3_DATA, PH2_DATA, PH1_DATA, PH0_DATA,
+	/* Port I - not on device */
+	/* Port J */
+	PJ31_DATA, PJ30_DATA, PJ29_DATA, PJ28_DATA,
+	PJ27_DATA, PJ26_DATA, PJ25_DATA, PJ24_DATA,
+	PJ23_DATA, PJ22_DATA, PJ21_DATA, PJ20_DATA,
+	PJ19_DATA, PJ18_DATA, PJ17_DATA, PJ16_DATA,
+	PJ15_DATA, PJ14_DATA, PJ13_DATA, PJ12_DATA,
+	PJ11_DATA, PJ10_DATA, PJ9_DATA, PJ8_DATA,
+	PJ7_DATA, PJ6_DATA, PJ5_DATA, PJ4_DATA,
+	PJ3_DATA, PJ2_DATA, PJ1_DATA, PJ0_DATA,
+	PINMUX_DATA_END,
+
+	PINMUX_INPUT_BEGIN,
+	FORCE_IN,
+	/* Port A */
+	PA1_IN, PA0_IN,
+	/* Port B */
+	PB22_IN, PB21_IN, PB20_IN,
+	PB19_IN, PB18_IN, PB17_IN, PB16_IN,
+	PB15_IN, PB14_IN, PB13_IN, PB12_IN,
+	PB11_IN, PB10_IN, PB9_IN, PB8_IN,
+	PB7_IN, PB6_IN, PB5_IN, PB4_IN,
+	PB3_IN, PB2_IN, PB1_IN,
+	/* Port C */
+	PC8_IN,
+	PC7_IN, PC6_IN, PC5_IN, PC4_IN,
+	PC3_IN, PC2_IN, PC1_IN, PC0_IN,
+	/* Port D */
+	PD15_IN, PD14_IN, PD13_IN, PD12_IN,
+	PD11_IN, PD10_IN, PD9_IN, PD8_IN,
+	PD7_IN, PD6_IN, PD5_IN, PD4_IN,
+	PD3_IN, PD2_IN, PD1_IN, PD0_IN,
+	/* Port E */
+	PE7_IN, PE6_IN, PE5_IN, PE4_IN,
+	PE3_IN, PE2_IN, PE1_IN, PE0_IN,
+	/* Port F */
+	PF23_IN, PF22_IN, PF21_IN, PF20_IN,
+	PF19_IN, PF18_IN, PF17_IN, PF16_IN,
+	PF15_IN, PF14_IN, PF13_IN, PF12_IN,
+	PF11_IN, PF10_IN, PF9_IN, PF8_IN,
+	PF7_IN, PF6_IN, PF5_IN, PF4_IN,
+	PF3_IN, PF2_IN, PF1_IN, PF0_IN,
+	/* Port G */
+	PG27_IN, PG26_IN, PG25_IN, PG24_IN,
+	PG23_IN, PG22_IN, PG21_IN, PG20_IN,
+	PG19_IN, PG18_IN, PG17_IN, PG16_IN,
+	PG15_IN, PG14_IN, PG13_IN, PG12_IN,
+	PG11_IN, PG10_IN, PG9_IN, PG8_IN,
+	PG7_IN, PG6_IN, PG5_IN, PG4_IN,
+	PG3_IN, PG2_IN, PG1_IN, PG0_IN,
+	/* Port H - Port H does not have a Data Register */
+	/* Port I - not on device */
+	/* Port J */
+	PJ31_IN, PJ30_IN, PJ29_IN, PJ28_IN,
+	PJ27_IN, PJ26_IN, PJ25_IN, PJ24_IN,
+	PJ23_IN, PJ22_IN, PJ21_IN, PJ20_IN,
+	PJ19_IN, PJ18_IN, PJ17_IN, PJ16_IN,
+	PJ15_IN, PJ14_IN, PJ13_IN, PJ12_IN,
+	PJ11_IN, PJ10_IN, PJ9_IN, PJ8_IN,
+	PJ7_IN, PJ6_IN, PJ5_IN, PJ4_IN,
+	PJ3_IN, PJ2_IN, PJ1_IN, PJ0_IN,
+	PINMUX_INPUT_END,
+
+	PINMUX_OUTPUT_BEGIN,
+	FORCE_OUT,
+	/* Port A */
+	PA1_OUT, PA0_OUT,
+	/* Port B */
+	PB22_OUT, PB21_OUT, PB20_OUT,
+	PB19_OUT, PB18_OUT, PB17_OUT, PB16_OUT,
+	PB15_OUT, PB14_OUT, PB13_OUT, PB12_OUT,
+	PB11_OUT, PB10_OUT, PB9_OUT, PB8_OUT,
+	PB7_OUT, PB6_OUT, PB5_OUT, PB4_OUT,
+	PB3_OUT, PB2_OUT, PB1_OUT,
+	/* Port C */
+	PC8_OUT,
+	PC7_OUT, PC6_OUT, PC5_OUT, PC4_OUT,
+	PC3_OUT, PC2_OUT, PC1_OUT, PC0_OUT,
+	/* Port D */
+	PD15_OUT, PD14_OUT, PD13_OUT, PD12_OUT,
+	PD11_OUT, PD10_OUT, PD9_OUT, PD8_OUT,
+	PD7_OUT, PD6_OUT, PD5_OUT, PD4_OUT,
+	PD3_OUT, PD2_OUT, PD1_OUT, PD0_OUT,
+	/* Port E */
+	PE7_OUT, PE6_OUT, PE5_OUT, PE4_OUT,
+	PE3_OUT, PE2_OUT, PE1_OUT, PE0_OUT,
+	/* Port F */
+	PF23_OUT, PF22_OUT, PF21_OUT, PF20_OUT,
+	PF19_OUT, PF18_OUT, PF17_OUT, PF16_OUT,
+	PF15_OUT, PF14_OUT, PF13_OUT, PF12_OUT,
+	PF11_OUT, PF10_OUT, PF9_OUT, PF8_OUT,
+	PF7_OUT, PF6_OUT, PF5_OUT, PF4_OUT,
+	PF3_OUT, PF2_OUT, PF1_OUT, PF0_OUT,
+	/* Port G */
+	PG27_OUT, PG26_OUT, PG25_OUT, PG24_OUT,
+	PG23_OUT, PG22_OUT, PG21_OUT, PG20_OUT,
+	PG19_OUT, PG18_OUT, PG17_OUT, PG16_OUT,
+	PG15_OUT, PG14_OUT, PG13_OUT, PG12_OUT,
+	PG11_OUT, PG10_OUT, PG9_OUT, PG8_OUT,
+	PG7_OUT, PG6_OUT, PG5_OUT, PG4_OUT,
+	PG3_OUT, PG2_OUT, PG1_OUT, PG0_OUT,
+	/* Port H - Port H does not have a Data Register */
+	/* Port I - not on device */
+	/* Port J */
+	PJ31_OUT, PJ30_OUT, PJ29_OUT, PJ28_OUT,
+	PJ27_OUT, PJ26_OUT, PJ25_OUT, PJ24_OUT,
+	PJ23_OUT, PJ22_OUT, PJ21_OUT, PJ20_OUT,
+	PJ19_OUT, PJ18_OUT, PJ17_OUT, PJ16_OUT,
+	PJ15_OUT, PJ14_OUT, PJ13_OUT, PJ12_OUT,
+	PJ11_OUT, PJ10_OUT, PJ9_OUT, PJ8_OUT,
+	PJ7_OUT, PJ6_OUT, PJ5_OUT, PJ4_OUT,
+	PJ3_OUT, PJ2_OUT, PJ1_OUT, PJ0_OUT,
+	PINMUX_OUTPUT_END,
+
+	PINMUX_FUNCTION_BEGIN,
+	/* Port A */
+	PA1_IOR_IN, PA1_IOR_OUT,
+	PA0_IOR_IN, PA0_IOR_OUT,
+
+	/* Port B */
+	PB22_IOR_IN, PB22_IOR_OUT,
+	PB21_IOR_IN, PB21_IOR_OUT,
+	PB20_IOR_IN, PB20_IOR_OUT,
+	PB19_IOR_IN, PB19_IOR_OUT,
+	PB18_IOR_IN, PB18_IOR_OUT,
+	PB17_IOR_IN, PB17_IOR_OUT,
+	PB16_IOR_IN, PB16_IOR_OUT,
+
+	PB15_IOR_IN, PB15_IOR_OUT,
+	PB14_IOR_IN, PB14_IOR_OUT,
+	PB13_IOR_IN, PB13_IOR_OUT,
+	PB12_IOR_IN, PB12_IOR_OUT,
+	PB11_IOR_IN, PB11_IOR_OUT,
+	PB10_IOR_IN, PB10_IOR_OUT,
+	PB9_IOR_IN, PB9_IOR_OUT,
+	PB8_IOR_IN, PB8_IOR_OUT,
+
+	PB7_IOR_IN, PB7_IOR_OUT,
+	PB6_IOR_IN, PB6_IOR_OUT,
+	PB5_IOR_IN, PB5_IOR_OUT,
+	PB4_IOR_IN, PB4_IOR_OUT,
+	PB3_IOR_IN, PB3_IOR_OUT,
+	PB2_IOR_IN, PB2_IOR_OUT,
+	PB1_IOR_IN, PB1_IOR_OUT,
+	PB0_IOR_IN, PB0_IOR_OUT,
+
+	PB22MD_000, PB22MD_001, PB22MD_010, PB22MD_011,
+	PB22MD_100, PB22MD_101, PB22MD_110, PB22MD_111,
+	PB21MD_00, PB21MD_01, PB21MD_10, PB21MD_11,
+	PB20MD_000, PB20MD_001, PB20MD_010, PB20MD_011,
+	PB20MD_100, PB20MD_101, PB20MD_110, PB20MD_111,
+	PB19MD_000, PB19MD_001, PB19MD_010, PB19MD_011,
+	PB19MD_100, PB19MD_101, PB19MD_110, PB19MD_111,
+	PB18MD_000, PB18MD_001, PB18MD_010, PB18MD_011,
+	PB18MD_100, PB18MD_101, PB18MD_110, PB18MD_111,
+	PB17MD_000, PB17MD_001, PB17MD_010, PB17MD_011,
+	PB17MD_100, PB17MD_101, PB17MD_110, PB17MD_111,
+	PB16MD_000, PB16MD_001, PB16MD_010, PB16MD_011,
+	PB16MD_100, PB16MD_101, PB16MD_110, PB16MD_111,
+	PB15MD_000, PB15MD_001, PB15MD_010, PB15MD_011,
+	PB15MD_100, PB15MD_101, PB15MD_110, PB15MD_111,
+	PB14MD_000, PB14MD_001, PB14MD_010, PB14MD_011,
+	PB14MD_100, PB14MD_101, PB14MD_110, PB14MD_111,
+	PB13MD_000, PB13MD_001, PB13MD_010, PB13MD_011,
+	PB13MD_100, PB13MD_101, PB13MD_110, PB13MD_111,
+	PB12MD_00, PB12MD_01, PB12MD_10, PB12MD_11,
+
+	PB11MD_00, PB11MD_01, PB11MD_10, PB11MD_11,
+	PB10MD_00, PB10MD_01, PB10MD_10, PB10MD_11,
+	PB9MD_00, PB9MD_01, PB9MD_10, PB9MD_11,
+	PB8MD_00, PB8MD_01, PB8MD_10, PB8MD_11,
+
+	PB7MD_00, PB7MD_01, PB7MD_10, PB7MD_11,
+	PB6MD_00, PB6MD_01, PB6MD_10, PB6MD_11,
+	PB5MD_00, PB5MD_01, PB5MD_10, PB5MD_11,
+	PB4MD_00, PB4MD_01, PB4MD_10, PB4MD_11,
+
+	PB3MD_00, PB3MD_01, PB3MD_10, PB3MD_11,
+	PB2MD_00, PB2MD_01, PB2MD_10, PB2MD_11,
+	PB1MD_00, PB1MD_01, PB1MD_10, PB1MD_11,
+
+	/* Port C */
+	PC8_IOR_IN, PC8_IOR_OUT,
+	PC7_IOR_IN, PC7_IOR_OUT,
+	PC6_IOR_IN, PC6_IOR_OUT,
+	PC5_IOR_IN, PC5_IOR_OUT,
+	PC4_IOR_IN, PC4_IOR_OUT,
+	PC3_IOR_IN, PC3_IOR_OUT,
+	PC2_IOR_IN, PC2_IOR_OUT,
+	PC1_IOR_IN, PC1_IOR_OUT,
+	PC0_IOR_IN, PC0_IOR_OUT,
+
+	PC8MD_000, PC8MD_001, PC8MD_010, PC8MD_011,
+	PC8MD_100, PC8MD_101, PC8MD_110, PC8MD_111,
+	PC7MD_000, PC7MD_001, PC7MD_010, PC7MD_011,
+	PC7MD_100, PC7MD_101, PC7MD_110, PC7MD_111,
+	PC6MD_000, PC6MD_001, PC6MD_010, PC6MD_011,
+	PC6MD_100, PC6MD_101, PC6MD_110, PC6MD_111,
+	PC5MD_000, PC5MD_001, PC5MD_010, PC5MD_011,
+	PC5MD_100, PC5MD_101, PC5MD_110, PC5MD_111,
+	PC4MD_00, PC4MD_01, PC4MD_10, PC4MD_11,
+
+	PC3MD_00, PC3MD_01, PC3MD_10, PC3MD_11,
+	PC2MD_00, PC2MD_01, PC2MD_10, PC2MD_11,
+	PC1MD_0, PC1MD_1,
+	PC0MD_0, PC0MD_1,
+
+	/* Port D */
+	PD15_IOR_IN, PD15_IOR_OUT,
+	PD14_IOR_IN, PD14_IOR_OUT,
+	PD13_IOR_IN, PD13_IOR_OUT,
+	PD12_IOR_IN, PD12_IOR_OUT,
+	PD11_IOR_IN, PD11_IOR_OUT,
+	PD10_IOR_IN, PD10_IOR_OUT,
+	PD9_IOR_IN, PD9_IOR_OUT,
+	PD8_IOR_IN, PD8_IOR_OUT,
+	PD7_IOR_IN, PD7_IOR_OUT,
+	PD6_IOR_IN, PD6_IOR_OUT,
+	PD5_IOR_IN, PD5_IOR_OUT,
+	PD4_IOR_IN, PD4_IOR_OUT,
+	PD3_IOR_IN, PD3_IOR_OUT,
+	PD2_IOR_IN, PD2_IOR_OUT,
+	PD1_IOR_IN, PD1_IOR_OUT,
+	PD0_IOR_IN, PD0_IOR_OUT,
+
+	PD15MD_00, PD15MD_01, PD15MD_10, PD15MD_11,
+	PD14MD_00, PD14MD_01, PD14MD_10, PD14MD_11,
+	PD13MD_00, PD13MD_01, PD13MD_10, PD13MD_11,
+	PD12MD_00, PD12MD_01, PD12MD_10, PD12MD_11,
+
+	PD11MD_00, PD11MD_01, PD11MD_10, PD11MD_11,
+	PD10MD_00, PD10MD_01, PD10MD_10, PD10MD_11,
+	PD9MD_00, PD9MD_01, PD9MD_10, PD9MD_11,
+	PD8MD_00, PD8MD_01, PD8MD_10, PD8MD_11,
+
+	PD7MD_00, PD7MD_01, PD7MD_10, PD7MD_11,
+	PD6MD_00, PD6MD_01, PD6MD_10, PD6MD_11,
+	PD5MD_00, PD5MD_01, PD5MD_10, PD5MD_11,
+	PD4MD_00, PD4MD_01, PD4MD_10, PD4MD_11,
+
+	PD3MD_00, PD3MD_01, PD3MD_10, PD3MD_11,
+	PD2MD_00, PD2MD_01, PD2MD_10, PD2MD_11,
+	PD1MD_00, PD1MD_01, PD1MD_10, PD1MD_11,
+	PD0MD_00, PD0MD_01, PD0MD_10, PD0MD_11,
+
+	/* Port E */
+	PE7_IOR_IN, PE7_IOR_OUT,
+	PE6_IOR_IN, PE6_IOR_OUT,
+	PE5_IOR_IN, PE5_IOR_OUT,
+	PE4_IOR_IN, PE4_IOR_OUT,
+	PE3_IOR_IN, PE3_IOR_OUT,
+	PE2_IOR_IN, PE2_IOR_OUT,
+	PE1_IOR_IN, PE1_IOR_OUT,
+	PE0_IOR_IN, PE0_IOR_OUT,
+
+	PE7MD_00, PE7MD_01, PE7MD_10, PE7MD_11,
+	PE6MD_00, PE6MD_01, PE6MD_10, PE6MD_11,
+	PE5MD_00, PE5MD_01, PE5MD_10, PE5MD_11,
+	PE4MD_00, PE4MD_01, PE4MD_10, PE4MD_11,
+
+	PE3MD_000, PE3MD_001, PE3MD_010, PE3MD_011,
+	PE3MD_100, PE3MD_101, PE3MD_110, PE3MD_111,
+	PE2MD_000, PE2MD_001, PE2MD_010, PE2MD_011,
+	PE2MD_100, PE2MD_101, PE2MD_110, PE2MD_111,
+	PE1MD_000, PE1MD_001, PE1MD_010, PE1MD_011,
+	PE1MD_100, PE1MD_101, PE1MD_110, PE1MD_111,
+	PE0MD_00, PE0MD_01, PE0MD_10, PE0MD_11,
+
+	/* Port F */
+	PF23_IOR_IN, PF23_IOR_OUT,
+	PF22_IOR_IN, PF22_IOR_OUT,
+	PF21_IOR_IN, PF21_IOR_OUT,
+	PF20_IOR_IN, PF20_IOR_OUT,
+	PF19_IOR_IN, PF19_IOR_OUT,
+	PF18_IOR_IN, PF18_IOR_OUT,
+	PF17_IOR_IN, PF17_IOR_OUT,
+	PF16_IOR_IN, PF16_IOR_OUT,
+	PF15_IOR_IN, PF15_IOR_OUT,
+	PF14_IOR_IN, PF14_IOR_OUT,
+	PF13_IOR_IN, PF13_IOR_OUT,
+	PF12_IOR_IN, PF12_IOR_OUT,
+	PF11_IOR_IN, PF11_IOR_OUT,
+	PF10_IOR_IN, PF10_IOR_OUT,
+	PF9_IOR_IN, PF9_IOR_OUT,
+	PF8_IOR_IN, PF8_IOR_OUT,
+	PF7_IOR_IN, PF7_IOR_OUT,
+	PF6_IOR_IN, PF6_IOR_OUT,
+	PF5_IOR_IN, PF5_IOR_OUT,
+	PF4_IOR_IN, PF4_IOR_OUT,
+	PF3_IOR_IN, PF3_IOR_OUT,
+	PF2_IOR_IN, PF2_IOR_OUT,
+	PF1_IOR_IN, PF1_IOR_OUT,
+	PF0_IOR_IN, PF0_IOR_OUT,
+
+	PF23MD_000, PF23MD_001, PF23MD_010, PF23MD_011,
+	PF23MD_100, PF23MD_101, PF23MD_110, PF23MD_111,
+	PF22MD_000, PF22MD_001, PF22MD_010, PF22MD_011,
+	PF22MD_100, PF22MD_101, PF22MD_110, PF22MD_111,
+	PF21MD_000, PF21MD_001, PF21MD_010, PF21MD_011,
+	PF21MD_100, PF21MD_101, PF21MD_110, PF21MD_111,
+	PF20MD_000, PF20MD_001, PF20MD_010, PF20MD_011,
+	PF20MD_100, PF20MD_101, PF20MD_110, PF20MD_111,
+
+	PF19MD_000, PF19MD_001, PF19MD_010, PF19MD_011,
+	PF19MD_100, PF19MD_101, PF19MD_110, PF19MD_111,
+	PF18MD_000, PF18MD_001, PF18MD_010, PF18MD_011,
+	PF18MD_100, PF18MD_101, PF18MD_110, PF18MD_111,
+	PF17MD_000, PF17MD_001, PF17MD_010, PF17MD_011,
+	PF17MD_100, PF17MD_101, PF17MD_110, PF17MD_111,
+	PF16MD_000, PF16MD_001, PF16MD_010, PF16MD_011,
+	PF16MD_100, PF16MD_101, PF16MD_110, PF16MD_111,
+
+	PF15MD_000, PF15MD_001, PF15MD_010, PF15MD_011,
+	PF15MD_100, PF15MD_101, PF15MD_110, PF15MD_111,
+	PF14MD_000, PF14MD_001, PF14MD_010, PF14MD_011,
+	PF14MD_100, PF14MD_101, PF14MD_110, PF14MD_111,
+	PF13MD_000, PF13MD_001, PF13MD_010, PF13MD_011,
+	PF13MD_100, PF13MD_101, PF13MD_110, PF13MD_111,
+	PF12MD_000, PF12MD_001, PF12MD_010, PF12MD_011,
+	PF12MD_100, PF12MD_101, PF12MD_110, PF12MD_111,
+
+	PF11MD_000, PF11MD_001, PF11MD_010, PF11MD_011,
+	PF11MD_100, PF11MD_101, PF11MD_110, PF11MD_111,
+	PF10MD_000, PF10MD_001, PF10MD_010, PF10MD_011,
+	PF10MD_100, PF10MD_101, PF10MD_110, PF10MD_111,
+	PF9MD_000, PF9MD_001, PF9MD_010, PF9MD_011,
+	PF9MD_100, PF9MD_101, PF9MD_110, PF9MD_111,
+	PF8MD_000, PF8MD_001, PF8MD_010, PF8MD_011,
+	PF8MD_100, PF8MD_101, PF8MD_110, PF8MD_111,
+
+	PF7MD_000, PF7MD_001, PF7MD_010, PF7MD_011,
+	PF7MD_100, PF7MD_101, PF7MD_110, PF7MD_111,
+	PF6MD_000, PF6MD_001, PF6MD_010, PF6MD_011,
+	PF6MD_100, PF6MD_101, PF6MD_110, PF6MD_111,
+	PF5MD_000, PF5MD_001, PF5MD_010, PF5MD_011,
+	PF5MD_100, PF5MD_101, PF5MD_110, PF5MD_111,
+	PF4MD_000, PF4MD_001, PF4MD_010, PF4MD_011,
+	PF4MD_100, PF4MD_101, PF4MD_110, PF4MD_111,
+
+	PF3MD_000, PF3MD_001, PF3MD_010, PF3MD_011,
+	PF3MD_100, PF3MD_101, PF3MD_110, PF3MD_111,
+	PF2MD_000, PF2MD_001, PF2MD_010, PF2MD_011,
+	PF2MD_100, PF2MD_101, PF2MD_110, PF2MD_111,
+	PF1MD_000, PF1MD_001, PF1MD_010, PF1MD_011,
+	PF1MD_100, PF1MD_101, PF1MD_110, PF1MD_111,
+	PF0MD_000, PF0MD_001, PF0MD_010, PF0MD_011,
+	PF0MD_100, PF0MD_101, PF0MD_110, PF0MD_111,
+
+	/* Port G */
+	PG27_IOR_IN, PG27_IOR_OUT,
+	PG26_IOR_IN, PG26_IOR_OUT,
+	PG25_IOR_IN, PG25_IOR_OUT,
+	PG24_IOR_IN, PG24_IOR_OUT,
+	PG23_IOR_IN, PG23_IOR_OUT,
+	PG22_IOR_IN, PG22_IOR_OUT,
+	PG21_IOR_IN, PG21_IOR_OUT,
+	PG20_IOR_IN, PG20_IOR_OUT,
+	PG19_IOR_IN, PG19_IOR_OUT,
+	PG18_IOR_IN, PG18_IOR_OUT,
+	PG17_IOR_IN, PG17_IOR_OUT,
+	PG16_IOR_IN, PG16_IOR_OUT,
+	PG15_IOR_IN, PG15_IOR_OUT,
+	PG14_IOR_IN, PG14_IOR_OUT,
+	PG13_IOR_IN, PG13_IOR_OUT,
+	PG12_IOR_IN, PG12_IOR_OUT,
+	PG11_IOR_IN, PG11_IOR_OUT,
+	PG10_IOR_IN, PG10_IOR_OUT,
+	PG9_IOR_IN, PG9_IOR_OUT,
+	PG8_IOR_IN, PG8_IOR_OUT,
+	PG7_IOR_IN, PG7_IOR_OUT,
+	PG6_IOR_IN, PG6_IOR_OUT,
+	PG5_IOR_IN, PG5_IOR_OUT,
+	PG4_IOR_IN, PG4_IOR_OUT,
+	PG3_IOR_IN, PG3_IOR_OUT,
+	PG2_IOR_IN, PG2_IOR_OUT,
+	PG1_IOR_IN, PG1_IOR_OUT,
+	PG0_IOR_IN, PG0_IOR_OUT,
+
+	PG27MD_00, PG27MD_01, PG27MD_10, PG27MD_11,
+	PG26MD_00, PG26MD_01, PG26MD_10, PG26MD_11,
+	PG25MD_00, PG25MD_01, PG25MD_10, PG25MD_11,
+	PG24MD_00, PG24MD_01, PG24MD_10, PG24MD_11,
+
+	PG23MD_000, PG23MD_001, PG23MD_010, PG23MD_011,
+	PG23MD_100, PG23MD_101, PG23MD_110, PG23MD_111,
+	PG22MD_000, PG22MD_001, PG22MD_010, PG22MD_011,
+	PG22MD_100, PG22MD_101, PG22MD_110, PG22MD_111,
+	PG21MD_000, PG21MD_001, PG21MD_010, PG21MD_011,
+	PG21MD_100, PG21MD_101, PG21MD_110, PG21MD_111,
+	PG20MD_000, PG20MD_001, PG20MD_010, PG20MD_011,
+	PG20MD_100, PG20MD_101, PG20MD_110, PG20MD_111,
+
+	PG19MD_000, PG19MD_001, PG19MD_010, PG19MD_011,
+	PG19MD_100, PG19MD_101, PG19MD_110, PG19MD_111,
+	PG18MD_000, PG18MD_001, PG18MD_010, PG18MD_011,
+	PG18MD_100, PG18MD_101, PG18MD_110, PG18MD_111,
+	PG17MD_00, PG17MD_01, PG17MD_10, PG17MD_11,
+	PG16MD_00, PG16MD_01, PG16MD_10, PG16MD_11,
+
+	PG15MD_00, PG15MD_01, PG15MD_10, PG15MD_11,
+	PG14MD_00, PG14MD_01, PG14MD_10, PG14MD_11,
+	PG13MD_00, PG13MD_01, PG13MD_10, PG13MD_11,
+	PG12MD_00, PG12MD_01, PG12MD_10, PG12MD_11,
+
+	PG11MD_000, PG11MD_001, PG11MD_010, PG11MD_011,
+	PG11MD_100, PG11MD_101, PG11MD_110, PG11MD_111,
+	PG10MD_000, PG10MD_001, PG10MD_010, PG10MD_011,
+	PG10MD_100, PG10MD_101, PG10MD_110, PG10MD_111,
+	PG9MD_000, PG9MD_001, PG9MD_010, PG9MD_011,
+	PG9MD_100, PG9MD_101, PG9MD_110, PG9MD_111,
+	PG8MD_000, PG8MD_001, PG8MD_010, PG8MD_011,
+	PG8MD_100, PG8MD_101, PG8MD_110, PG8MD_111,
+
+	PG7MD_000, PG7MD_001, PG7MD_010, PG7MD_011,
+	PG7MD_100, PG7MD_101, PG7MD_110, PG7MD_111,
+	PG6MD_000, PG6MD_001, PG6MD_010, PG6MD_011,
+	PG6MD_100, PG6MD_101, PG6MD_110, PG6MD_111,
+	PG5MD_000, PG5MD_001, PG5MD_010, PG5MD_011,
+	PG5MD_100, PG5MD_101, PG5MD_110, PG5MD_111,
+	PG4MD_000, PG4MD_001, PG4MD_010, PG4MD_011,
+	PG4MD_100, PG4MD_101, PG4MD_110, PG4MD_111,
+
+	PG3MD_000, PG3MD_001, PG3MD_010, PG3MD_011,
+	PG3MD_100, PG3MD_101, PG3MD_110, PG3MD_111,
+	PG2MD_000, PG2MD_001, PG2MD_010, PG2MD_011,
+	PG2MD_100, PG2MD_101, PG2MD_110, PG2MD_111,
+	PG1MD_000, PG1MD_001, PG1MD_010, PG1MD_011,
+	PG1MD_100, PG1MD_101, PG1MD_110, PG1MD_111,
+	PG0MD_000, PG0MD_001, PG0MD_010, PG0MD_011,
+	PG0MD_100, PG0MD_101, PG0MD_110, PG0MD_111,
+
+	/* Port H */
+	PH7MD_00, PH7MD_01, PH7MD_10, PH7MD_11,
+	PH6MD_00, PH6MD_01, PH6MD_10, PH6MD_11,
+	PH5MD_00, PH5MD_01, PH5MD_10, PH5MD_11,
+	PH4MD_00, PH4MD_01, PH4MD_10, PH4MD_11,
+
+	PH3MD_00, PH3MD_01, PH3MD_10, PH3MD_11,
+	PH2MD_00, PH2MD_01, PH2MD_10, PH2MD_11,
+	PH1MD_00, PH1MD_01, PH1MD_10, PH1MD_11,
+	PH0MD_00, PH0MD_01, PH0MD_10, PH0MD_11,
+
+	/* Port I - not on device */
+
+	/* Port J */
+	PJ31_IOR_IN, PJ31_IOR_OUT,
+	PJ30_IOR_IN, PJ30_IOR_OUT,
+	PJ29_IOR_IN, PJ29_IOR_OUT,
+	PJ28_IOR_IN, PJ28_IOR_OUT,
+	PJ27_IOR_IN, PJ27_IOR_OUT,
+	PJ26_IOR_IN, PJ26_IOR_OUT,
+	PJ25_IOR_IN, PJ25_IOR_OUT,
+	PJ24_IOR_IN, PJ24_IOR_OUT,
+	PJ23_IOR_IN, PJ23_IOR_OUT,
+	PJ22_IOR_IN, PJ22_IOR_OUT,
+	PJ21_IOR_IN, PJ21_IOR_OUT,
+	PJ20_IOR_IN, PJ20_IOR_OUT,
+	PJ19_IOR_IN, PJ19_IOR_OUT,
+	PJ18_IOR_IN, PJ18_IOR_OUT,
+	PJ17_IOR_IN, PJ17_IOR_OUT,
+	PJ16_IOR_IN, PJ16_IOR_OUT,
+	PJ15_IOR_IN, PJ15_IOR_OUT,
+	PJ14_IOR_IN, PJ14_IOR_OUT,
+	PJ13_IOR_IN, PJ13_IOR_OUT,
+	PJ12_IOR_IN, PJ12_IOR_OUT,
+	PJ11_IOR_IN, PJ11_IOR_OUT,
+	PJ10_IOR_IN, PJ10_IOR_OUT,
+	PJ9_IOR_IN, PJ9_IOR_OUT,
+	PJ8_IOR_IN, PJ8_IOR_OUT,
+	PJ7_IOR_IN, PJ7_IOR_OUT,
+	PJ6_IOR_IN, PJ6_IOR_OUT,
+	PJ5_IOR_IN, PJ5_IOR_OUT,
+	PJ4_IOR_IN, PJ4_IOR_OUT,
+	PJ3_IOR_IN, PJ3_IOR_OUT,
+	PJ2_IOR_IN, PJ2_IOR_OUT,
+	PJ1_IOR_IN, PJ1_IOR_OUT,
+	PJ0_IOR_IN, PJ0_IOR_OUT,
+
+	PJ31MD_0, PJ31MD_1,
+	PJ30MD_000, PJ30MD_001, PJ30MD_010, PJ30MD_011,
+	PJ30MD_100, PJ30MD_101, PJ30MD_110, PJ30MD_111,
+	PJ29MD_000, PJ29MD_001, PJ29MD_010, PJ29MD_011,
+	PJ29MD_100, PJ29MD_101, PJ29MD_110, PJ29MD_111,
+	PJ28MD_000, PJ28MD_001, PJ28MD_010, PJ28MD_011,
+	PJ28MD_100, PJ28MD_101, PJ28MD_110, PJ28MD_111,
+
+	PJ27MD_000, PJ27MD_001, PJ27MD_010, PJ27MD_011,
+	PJ27MD_100, PJ27MD_101, PJ27MD_110, PJ27MD_111,
+	PJ26MD_000, PJ26MD_001, PJ26MD_010, PJ26MD_011,
+	PJ26MD_100, PJ26MD_101, PJ26MD_110, PJ26MD_111,
+	PJ25MD_000, PJ25MD_001, PJ25MD_010, PJ25MD_011,
+	PJ25MD_100, PJ25MD_101, PJ25MD_110, PJ25MD_111,
+	PJ24MD_000, PJ24MD_001, PJ24MD_010, PJ24MD_011,
+	PJ24MD_100, PJ24MD_101, PJ24MD_110, PJ24MD_111,
+
+	PJ23MD_000, PJ23MD_001, PJ23MD_010, PJ23MD_011,
+	PJ23MD_100, PJ23MD_101, PJ23MD_110, PJ23MD_111,
+	PJ22MD_000, PJ22MD_001, PJ22MD_010, PJ22MD_011,
+	PJ22MD_100, PJ22MD_101, PJ22MD_110, PJ22MD_111,
+	PJ21MD_000, PJ21MD_001, PJ21MD_010, PJ21MD_011,
+	PJ21MD_100, PJ21MD_101, PJ21MD_110, PJ21MD_111,
+	PJ20MD_000, PJ20MD_001, PJ20MD_010, PJ20MD_011,
+	PJ20MD_100, PJ20MD_101, PJ20MD_110, PJ20MD_111,
+
+	PJ19MD_000, PJ19MD_001, PJ19MD_010, PJ19MD_011,
+	PJ19MD_100, PJ19MD_101, PJ19MD_110, PJ19MD_111,
+	PJ18MD_000, PJ18MD_001, PJ18MD_010, PJ18MD_011,
+	PJ18MD_100, PJ18MD_101, PJ18MD_110, PJ18MD_111,
+	PJ17MD_000, PJ17MD_001, PJ17MD_010, PJ17MD_011,
+	PJ17MD_100, PJ17MD_101, PJ17MD_110, PJ17MD_111,
+	PJ16MD_000, PJ16MD_001, PJ16MD_010, PJ16MD_011,
+	PJ16MD_100, PJ16MD_101, PJ16MD_110, PJ16MD_111,
+
+	PJ15MD_000, PJ15MD_001, PJ15MD_010, PJ15MD_011,
+	PJ15MD_100, PJ15MD_101, PJ15MD_110, PJ15MD_111,
+	PJ14MD_000, PJ14MD_001, PJ14MD_010, PJ14MD_011,
+	PJ14MD_100, PJ14MD_101, PJ14MD_110, PJ14MD_111,
+	PJ13MD_000, PJ13MD_001, PJ13MD_010, PJ13MD_011,
+	PJ13MD_100, PJ13MD_101, PJ13MD_110, PJ13MD_111,
+	PJ12MD_000, PJ12MD_001, PJ12MD_010, PJ12MD_011,
+	PJ12MD_100, PJ12MD_101, PJ12MD_110, PJ12MD_111,
+
+	PJ11MD_000, PJ11MD_001, PJ11MD_010, PJ11MD_011,
+	PJ11MD_100, PJ11MD_101, PJ11MD_110, PJ11MD_111,
+	PJ10MD_000, PJ10MD_001, PJ10MD_010, PJ10MD_011,
+	PJ10MD_100, PJ10MD_101, PJ10MD_110, PJ10MD_111,
+	PJ9MD_000, PJ9MD_001, PJ9MD_010, PJ9MD_011,
+	PJ9MD_100, PJ9MD_101, PJ9MD_110, PJ9MD_111,
+	PJ8MD_000, PJ8MD_001, PJ8MD_010, PJ8MD_011,
+	PJ8MD_100, PJ8MD_101, PJ8MD_110, PJ8MD_111,
+
+	PJ7MD_000, PJ7MD_001, PJ7MD_010, PJ7MD_011,
+	PJ7MD_100, PJ7MD_101, PJ7MD_110, PJ7MD_111,
+	PJ6MD_000, PJ6MD_001, PJ6MD_010, PJ6MD_011,
+	PJ6MD_100, PJ6MD_101, PJ6MD_110, PJ6MD_111,
+	PJ5MD_000, PJ5MD_001, PJ5MD_010, PJ5MD_011,
+	PJ5MD_100, PJ5MD_101, PJ5MD_110, PJ5MD_111,
+	PJ4MD_000, PJ4MD_001, PJ4MD_010, PJ4MD_011,
+	PJ4MD_100, PJ4MD_101, PJ4MD_110, PJ4MD_111,
+
+	PJ3MD_000, PJ3MD_001, PJ3MD_010, PJ3MD_011,
+	PJ3MD_100, PJ3MD_101, PJ3MD_110, PJ3MD_111,
+	PJ2MD_000, PJ2MD_001, PJ2MD_010, PJ2MD_011,
+	PJ2MD_100, PJ2MD_101, PJ2MD_110, PJ2MD_111,
+	PJ1MD_000, PJ1MD_001, PJ1MD_010, PJ1MD_011,
+	PJ1MD_100, PJ1MD_101, PJ1MD_110, PJ1MD_111,
+	PJ0MD_000, PJ0MD_001, PJ0MD_010, PJ0MD_011,
+	PJ0MD_100, PJ0MD_101, PJ0MD_110, PJ0MD_111,
+
+	PINMUX_FUNCTION_END,
+
+	PINMUX_MARK_BEGIN,
+	/* Port H */
+	PHAN7_MARK, PHAN6_MARK, PHAN5_MARK, PHAN4_MARK,
+	PHAN3_MARK, PHAN2_MARK, PHAN1_MARK, PHAN0_MARK,
+
+	/* IRQs */
+	IRQ7_PG_MARK, IRQ6_PG_MARK, IRQ5_PG_MARK, IRQ4_PG_MARK,
+	IRQ3_PG_MARK, IRQ2_PG_MARK, IRQ1_PG_MARK, IRQ0_PG_MARK,
+	IRQ7_PF_MARK, IRQ6_PF_MARK, IRQ5_PF_MARK, IRQ4_PF_MARK,
+	IRQ3_PJ_MARK, IRQ2_PJ_MARK, IRQ1_PJ_MARK, IRQ0_PJ_MARK,
+	IRQ1_PC_MARK, IRQ0_PC_MARK,
+
+	PINT7_PG_MARK, PINT6_PG_MARK, PINT5_PG_MARK, PINT4_PG_MARK,
+	PINT3_PG_MARK, PINT2_PG_MARK, PINT1_PG_MARK, PINT0_PG_MARK,
+	PINT7_PH_MARK, PINT6_PH_MARK, PINT5_PH_MARK, PINT4_PH_MARK,
+	PINT3_PH_MARK, PINT2_PH_MARK, PINT1_PH_MARK, PINT0_PH_MARK,
+	PINT7_PJ_MARK, PINT6_PJ_MARK, PINT5_PJ_MARK, PINT4_PJ_MARK,
+	PINT3_PJ_MARK, PINT2_PJ_MARK, PINT1_PJ_MARK, PINT0_PJ_MARK,
+
+	/* SD */
+	SD_D0_MARK, SD_D1_MARK, SD_D2_MARK, SD_D3_MARK,
+	SD_WP_MARK, SD_CLK_MARK, SD_CMD_MARK, SD_CD_MARK,
+
+	/* MMC */
+	MMC_D0_MARK, MMC_D1_MARK, MMC_D2_MARK, MMC_D3_MARK,
+	MMC_D4_MARK, MMC_D5_MARK, MMC_D6_MARK, MMC_D7_MARK,
+	MMC_CLK_MARK, MMC_CMD_MARK, MMC_CD_MARK,
+
+	/* PWM */
+	PWM1A_MARK, PWM1B_MARK, PWM1C_MARK, PWM1D_MARK,
+	PWM1E_MARK, PWM1F_MARK, PWM1G_MARK, PWM1H_MARK,
+	PWM2A_MARK, PWM2B_MARK, PWM2C_MARK, PWM2D_MARK,
+	PWM2E_MARK, PWM2F_MARK, PWM2G_MARK, PWM2H_MARK,
+
+	/* IEBus */
+	IERXD_MARK, IETXD_MARK,
+
+	/* WDT */
+	WDTOVF_MARK,
+
+	/* DMAC */
+	TEND0_MARK, DACK0_MARK, DREQ0_MARK,
+	TEND1_MARK, DACK1_MARK, DREQ1_MARK,
+
+	/* ADC */
+	ADTRG_MARK,
+
+	/* BSC */
+	A25_MARK, A24_MARK,
+	A23_MARK, A22_MARK, A21_MARK, A20_MARK,
+	A19_MARK, A18_MARK, A17_MARK, A16_MARK,
+	A15_MARK, A14_MARK, A13_MARK, A12_MARK,
+	A11_MARK, A10_MARK, A9_MARK, A8_MARK,
+	A7_MARK, A6_MARK, A5_MARK, A4_MARK,
+	A3_MARK, A2_MARK, A1_MARK, A0_MARK,
+	D31_MARK, D30_MARK, D29_MARK, D28_MARK,
+	D27_MARK, D26_MARK, D25_MARK, D24_MARK,
+	D23_MARK, D22_MARK, D21_MARK, D20_MARK,
+	D19_MARK, D18_MARK, D17_MARK, D16_MARK,
+	D15_MARK, D14_MARK, D13_MARK, D12_MARK,
+	D11_MARK, D10_MARK, D9_MARK, D8_MARK,
+	D7_MARK, D6_MARK, D5_MARK, D4_MARK,
+	D3_MARK, D2_MARK, D1_MARK, D0_MARK,
+	BS_MARK,
+	CS4_MARK, CS3_MARK, CS2_MARK, CS1_MARK, CS0_MARK,
+	CS5CE1A_MARK,
+	CE2A_MARK, CE2B_MARK,
+	RD_MARK, RDWR_MARK,
+	WE3ICIOWRAHDQMUU_MARK,
+	WE2ICIORDDQMUL_MARK,
+	WE1DQMUWE_MARK,
+	WE0DQML_MARK,
+	RAS_MARK, CAS_MARK, CKE_MARK,
+	WAIT_MARK, BREQ_MARK, BACK_MARK, IOIS16_MARK,
+
+	/* TMU */
+	TIOC0A_MARK, TIOC0B_MARK, TIOC0C_MARK, TIOC0D_MARK,
+	TIOC1A_MARK, TIOC1B_MARK,
+	TIOC2A_MARK, TIOC2B_MARK,
+	TIOC3A_MARK, TIOC3B_MARK, TIOC3C_MARK, TIOC3D_MARK,
+	TIOC4A_MARK, TIOC4B_MARK, TIOC4C_MARK, TIOC4D_MARK,
+	TCLKA_MARK, TCLKB_MARK, TCLKC_MARK, TCLKD_MARK,
+
+	/* SCIF */
+	SCK0_MARK, RXD0_MARK, TXD0_MARK,
+	SCK1_MARK, RXD1_MARK, TXD1_MARK, RTS1_MARK, CTS1_MARK,
+	SCK2_MARK, RXD2_MARK, TXD2_MARK,
+	SCK3_MARK, RXD3_MARK, TXD3_MARK,
+	SCK4_MARK, RXD4_MARK, TXD4_MARK,
+	SCK5_MARK, RXD5_MARK, TXD5_MARK, RTS5_MARK, CTS5_MARK,
+	SCK6_MARK, RXD6_MARK, TXD6_MARK,
+	SCK7_MARK, RXD7_MARK, TXD7_MARK, RTS7_MARK, CTS7_MARK,
+
+	/* RSPI */
+	MISO0_PB20_MARK, MOSI0_PB19_MARK, SSL00_PB18_MARK, RSPCK0_PB17_MARK,
+	MISO0_PJ19_MARK, MOSI0_PJ18_MARK, SSL00_PJ17_MARK, RSPCK0_PJ16_MARK,
+	MISO1_MARK, MOSI1_MARK, SSL10_MARK, RSPCK1_MARK,
+
+	/* IIC3 */
+	SCL0_MARK, SDA0_MARK,
+	SCL1_MARK, SDA1_MARK,
+	SCL2_MARK, SDA2_MARK,
+	SCL3_MARK, SDA3_MARK,
+
+	/* SSI */
+	SSISCK0_MARK, SSIWS0_MARK, SSITXD0_MARK, SSIRXD0_MARK,
+	SSISCK1_MARK, SSIWS1_MARK, SSIDATA1_MARK,
+	SSISCK2_MARK, SSIWS2_MARK, SSIDATA2_MARK,
+	SSISCK3_MARK, SSIWS3_MARK, SSIDATA3_MARK,
+	SSISCK4_MARK, SSIWS4_MARK, SSIDATA4_MARK,
+	SSISCK5_MARK, SSIWS5_MARK, SSIDATA5_MARK,
+	AUDIO_CLK_MARK,
+	AUDIO_XOUT_MARK,
+
+	/* SIOF */ /* NOTE Shares AUDIO_CLK with SSI */
+	SIOFTXD_MARK, SIOFRXD_MARK, SIOFSYNC_MARK, SIOFSCK_MARK,
+
+	/* SPDIF */ /* NOTE Shares AUDIO_CLK with SSI */
+	SPDIF_IN_MARK, SPDIF_OUT_MARK,
+	SPDIF_IN_PJ24_MARK, SPDIF_OUT_PJ25_MARK,
+
+	/* NANDFMC */ /* NOTE Controller is not available in boot mode 0 */
+	FCE_MARK,
+	FRB_MARK,
+
+	/* CAN */
+	CRX0_MARK, CTX0_MARK,
+	CRX1_MARK, CTX1_MARK,
+	CRX2_MARK, CTX2_MARK,
+	CRX0CRX1_MARK,
+	CRX0CRX1CRX2_MARK,
+	CTX0CTX1CTX2_MARK,
+	CRX1_PJ22_MARK, CTX1_PJ23_MARK,
+	CRX2_PJ20_MARK, CTX2_PJ21_MARK,
+	CRX0CRX1_PJ22_MARK,
+	CRX0CRX1CRX2_PJ20_MARK,
+
+	/* VDC */
+	DV_CLK_MARK,
+	DV_VSYNC_MARK, DV_HSYNC_MARK,
+	DV_DATA23_MARK, DV_DATA22_MARK, DV_DATA21_MARK, DV_DATA20_MARK,
+	DV_DATA19_MARK, DV_DATA18_MARK, DV_DATA17_MARK, DV_DATA16_MARK,
+	DV_DATA15_MARK, DV_DATA14_MARK, DV_DATA13_MARK, DV_DATA12_MARK,
+	DV_DATA11_MARK, DV_DATA10_MARK, DV_DATA9_MARK, DV_DATA8_MARK,
+	DV_DATA7_MARK, DV_DATA6_MARK, DV_DATA5_MARK, DV_DATA4_MARK,
+	DV_DATA3_MARK, DV_DATA2_MARK, DV_DATA1_MARK, DV_DATA0_MARK,
+	LCD_CLK_MARK, LCD_EXTCLK_MARK,
+	LCD_VSYNC_MARK, LCD_HSYNC_MARK, LCD_DE_MARK,
+	LCD_DATA23_MARK, LCD_DATA22_MARK, LCD_DATA21_MARK, LCD_DATA20_MARK,
+	LCD_DATA19_MARK, LCD_DATA18_MARK, LCD_DATA17_MARK, LCD_DATA16_MARK,
+	LCD_DATA15_MARK, LCD_DATA14_MARK, LCD_DATA13_MARK, LCD_DATA12_MARK,
+	LCD_DATA11_MARK, LCD_DATA10_MARK, LCD_DATA9_MARK, LCD_DATA8_MARK,
+	LCD_DATA7_MARK, LCD_DATA6_MARK, LCD_DATA5_MARK, LCD_DATA4_MARK,
+	LCD_DATA3_MARK, LCD_DATA2_MARK, LCD_DATA1_MARK, LCD_DATA0_MARK,
+	LCD_TCON6_MARK, LCD_TCON5_MARK, LCD_TCON4_MARK,
+	LCD_TCON3_MARK, LCD_TCON2_MARK, LCD_TCON1_MARK, LCD_TCON0_MARK,
+	LCD_M_DISP_MARK,
+	PINMUX_MARK_END,
+};
+
+static pinmux_enum_t pinmux_data[] = {
+
+	/* Port A */
+	PINMUX_DATA(PA1_DATA, PA1_IN),
+	PINMUX_DATA(PA0_DATA, PA0_IN),
+
+	/* Port B */
+	PINMUX_DATA(PB22_DATA, PB22MD_000, PB22_IN, PB22_OUT),
+	PINMUX_DATA(A22_MARK, PB22MD_001),
+	PINMUX_DATA(CTX2_MARK, PB22MD_010),
+	PINMUX_DATA(IETXD_MARK, PB22MD_011),
+	PINMUX_DATA(CS4_MARK, PB22MD_100),
+
+	PINMUX_DATA(PB21_DATA, PB21MD_00, PB21_IN, PB21_OUT),
+	PINMUX_DATA(A21_MARK, PB21MD_01),
+	PINMUX_DATA(CRX2_MARK, PB21MD_10),
+	PINMUX_DATA(IERXD_MARK, PB21MD_11),
+
+	PINMUX_DATA(A20_MARK, PB20MD_001),
+	PINMUX_DATA(A19_MARK, PB19MD_001),
+	PINMUX_DATA(A18_MARK, PB18MD_001),
+	PINMUX_DATA(A17_MARK, PB17MD_001),
+	PINMUX_DATA(A16_MARK, PB16MD_001),
+	PINMUX_DATA(A15_MARK, PB15MD_001),
+	PINMUX_DATA(A14_MARK, PB14MD_001),
+	PINMUX_DATA(A13_MARK, PB13MD_001),
+	PINMUX_DATA(A12_MARK, PB12MD_01),
+	PINMUX_DATA(A11_MARK, PB11MD_01),
+	PINMUX_DATA(A10_MARK, PB10MD_01),
+	PINMUX_DATA(A9_MARK, PB9MD_01),
+	PINMUX_DATA(A8_MARK, PB8MD_01),
+	PINMUX_DATA(A7_MARK, PB7MD_01),
+	PINMUX_DATA(A6_MARK, PB6MD_01),
+	PINMUX_DATA(A5_MARK, PB5MD_01),
+	PINMUX_DATA(A4_MARK, PB4MD_01),
+	PINMUX_DATA(A3_MARK, PB3MD_01),
+	PINMUX_DATA(A2_MARK, PB2MD_01),
+	PINMUX_DATA(A1_MARK, PB1MD_01),
+
+	/* Port C */
+	PINMUX_DATA(PC8_DATA, PC8MD_000),
+	PINMUX_DATA(CS3_MARK, PC8MD_001),
+	PINMUX_DATA(TXD7_MARK, PC8MD_010),
+	PINMUX_DATA(CTX1_MARK, PC8MD_011),
+
+	PINMUX_DATA(PC7_DATA, PC7MD_000),
+	PINMUX_DATA(CKE_MARK, PC7MD_001),
+	PINMUX_DATA(RXD7_MARK, PC7MD_010),
+	PINMUX_DATA(CRX1_MARK, PC7MD_011),
+	PINMUX_DATA(CRX0CRX1_MARK, PC7MD_100),
+	PINMUX_DATA(IRQ1_PC_MARK, PC7MD_101),
+
+	PINMUX_DATA(PC6_DATA, PC6MD_000),
+	PINMUX_DATA(CAS_MARK, PC6MD_001),
+	PINMUX_DATA(SCK7_MARK, PC6MD_010),
+	PINMUX_DATA(CTX0_MARK, PC6MD_011),
+
+	PINMUX_DATA(PC5_DATA, PC5MD_000),
+	PINMUX_DATA(RAS_MARK, PC5MD_001),
+	PINMUX_DATA(CRX0_MARK, PC5MD_011),
+	PINMUX_DATA(CTX0CTX1CTX2_MARK, PC5MD_100),
+	PINMUX_DATA(IRQ0_PC_MARK, PC5MD_101),
+
+	PINMUX_DATA(PC4_DATA, PC4MD_00),
+	PINMUX_DATA(WE1DQMUWE_MARK, PC4MD_01),
+	PINMUX_DATA(TXD6_MARK, PC4MD_10),
+
+	PINMUX_DATA(PC3_DATA, PC3MD_00),
+	PINMUX_DATA(WE0DQML_MARK, PC3MD_01),
+	PINMUX_DATA(RXD6_MARK, PC3MD_10),
+
+	PINMUX_DATA(PC2_DATA, PC2MD_00),
+	PINMUX_DATA(RDWR_MARK, PC2MD_01),
+	PINMUX_DATA(SCK5_MARK, PC2MD_10),
+
+	PINMUX_DATA(PC1_DATA, PC1MD_0),
+	PINMUX_DATA(RD_MARK, PC1MD_1),
+
+	PINMUX_DATA(PC0_DATA, PC0MD_0),
+	PINMUX_DATA(CS0_MARK, PC0MD_1),
+
+	/* Port D */
+	PINMUX_DATA(D15_MARK, PD15MD_01),
+	PINMUX_DATA(D14_MARK, PD14MD_01),
+
+	PINMUX_DATA(PD13_DATA, PD13MD_00),
+	PINMUX_DATA(D13_MARK, PD13MD_01),
+	PINMUX_DATA(PWM2F_MARK, PD13MD_10),
+
+	PINMUX_DATA(PD12_DATA, PD12MD_00),
+	PINMUX_DATA(D12_MARK, PD12MD_01),
+	PINMUX_DATA(PWM2E_MARK, PD12MD_10),
+
+	PINMUX_DATA(D11_MARK, PD11MD_01),
+	PINMUX_DATA(D10_MARK, PD10MD_01),
+	PINMUX_DATA(D9_MARK, PD9MD_01),
+	PINMUX_DATA(D8_MARK, PD8MD_01),
+	PINMUX_DATA(D7_MARK, PD7MD_01),
+	PINMUX_DATA(D6_MARK, PD6MD_01),
+	PINMUX_DATA(D5_MARK, PD5MD_01),
+	PINMUX_DATA(D4_MARK, PD4MD_01),
+	PINMUX_DATA(D3_MARK, PD3MD_01),
+	PINMUX_DATA(D2_MARK, PD2MD_01),
+	PINMUX_DATA(D1_MARK, PD1MD_01),
+	PINMUX_DATA(D0_MARK, PD0MD_01),
+
+	/* Port E */
+	PINMUX_DATA(PE7_DATA, PE7MD_00),
+	PINMUX_DATA(SDA3_MARK, PE7MD_01),
+	PINMUX_DATA(RXD7_MARK, PE7MD_10),
+
+	PINMUX_DATA(PE6_DATA, PE6MD_00),
+	PINMUX_DATA(SCL3_MARK, PE6MD_01),
+	PINMUX_DATA(RXD6_MARK, PE6MD_10),
+
+	PINMUX_DATA(PE5_DATA, PE5MD_00),
+	PINMUX_DATA(SDA2_MARK, PE5MD_01),
+	PINMUX_DATA(RXD5_MARK, PE5MD_10),
+	PINMUX_DATA(DV_HSYNC_MARK, PE5MD_11),
+
+	PINMUX_DATA(PE4_DATA, PE4MD_00),
+	PINMUX_DATA(SCL2_MARK, PE4MD_01),
+	PINMUX_DATA(DV_VSYNC_MARK, PE4MD_11),
+
+	PINMUX_DATA(PE3_DATA, PE3MD_000),
+	PINMUX_DATA(SDA1_MARK, PE3MD_001),
+	PINMUX_DATA(TCLKD_MARK, PE3MD_010),
+	PINMUX_DATA(ADTRG_MARK, PE3MD_011),
+	PINMUX_DATA(DV_HSYNC_MARK, PE3MD_100),
+
+	PINMUX_DATA(PE2_DATA, PE2MD_000),
+	PINMUX_DATA(SCL1_MARK, PE2MD_001),
+	PINMUX_DATA(TCLKD_MARK, PE2MD_010),
+	PINMUX_DATA(IOIS16_MARK, PE2MD_011),
+	PINMUX_DATA(DV_VSYNC_MARK, PE2MD_100),
+
+	PINMUX_DATA(PE1_DATA, PE1MD_000),
+	PINMUX_DATA(SDA0_MARK, PE1MD_001),
+	PINMUX_DATA(TCLKB_MARK, PE1MD_010),
+	PINMUX_DATA(AUDIO_CLK_MARK, PE1MD_010),
+	PINMUX_DATA(DV_CLK_MARK, PE1MD_100),
+
+	PINMUX_DATA(PE0_DATA, PE0MD_00),
+	PINMUX_DATA(SCL0_MARK, PE0MD_01),
+	PINMUX_DATA(TCLKA_MARK, PE0MD_10),
+	PINMUX_DATA(LCD_EXTCLK_MARK, PE0MD_11),
+
+	/* Port F */
+	PINMUX_DATA(PF23_DATA, PF23MD_000),
+	PINMUX_DATA(SD_D2_MARK, PF23MD_001),
+	PINMUX_DATA(TXD3_MARK, PF23MD_100),
+	PINMUX_DATA(MMC_D2_MARK, PF23MD_101),
+
+	PINMUX_DATA(PF22_DATA, PF22MD_000),
+	PINMUX_DATA(SD_D3_MARK, PF22MD_001),
+	PINMUX_DATA(RXD3_MARK, PF22MD_100),
+	PINMUX_DATA(MMC_D3_MARK, PF22MD_101),
+
+	PINMUX_DATA(PF21_DATA, PF21MD_000),
+	PINMUX_DATA(SD_CMD_MARK, PF21MD_001),
+	PINMUX_DATA(SCK3_MARK, PF21MD_100),
+	PINMUX_DATA(MMC_CMD_MARK, PF21MD_101),
+
+	PINMUX_DATA(PF20_DATA, PF20MD_000),
+	PINMUX_DATA(SD_CLK_MARK, PF20MD_001),
+	PINMUX_DATA(SSIDATA3_MARK, PF20MD_010),
+	PINMUX_DATA(MMC_CLK_MARK, PF20MD_101),
+
+	PINMUX_DATA(PF19_DATA, PF19MD_000),
+	PINMUX_DATA(SD_D0_MARK, PF19MD_001),
+	PINMUX_DATA(SSIWS3_MARK, PF19MD_010),
+	PINMUX_DATA(IRQ7_PF_MARK, PF19MD_100),
+	PINMUX_DATA(MMC_D0_MARK, PF19MD_101),
+
+	PINMUX_DATA(PF18_DATA, PF18MD_000),
+	PINMUX_DATA(SD_D1_MARK, PF18MD_001),
+	PINMUX_DATA(SSISCK3_MARK, PF18MD_010),
+	PINMUX_DATA(IRQ6_PF_MARK, PF18MD_100),
+	PINMUX_DATA(MMC_D1_MARK, PF18MD_101),
+
+	PINMUX_DATA(PF17_DATA, PF17MD_000),
+	PINMUX_DATA(SD_WP_MARK, PF17MD_001),
+	PINMUX_DATA(FRB_MARK, PF17MD_011),
+	PINMUX_DATA(IRQ5_PF_MARK, PF17MD_100),
+
+	PINMUX_DATA(PF16_DATA, PF16MD_000),
+	PINMUX_DATA(SD_CD_MARK, PF16MD_001),
+	PINMUX_DATA(FCE_MARK, PF16MD_011),
+	PINMUX_DATA(IRQ4_PF_MARK, PF16MD_100),
+	PINMUX_DATA(MMC_CD_MARK, PF16MD_101),
+
+	PINMUX_DATA(PF15_DATA, PF15MD_000),
+	PINMUX_DATA(A0_MARK, PF15MD_001),
+	PINMUX_DATA(SSIDATA2_MARK, PF15MD_010),
+	PINMUX_DATA(WDTOVF_MARK, PF15MD_011),
+	PINMUX_DATA(TXD2_MARK, PF15MD_100),
+
+	PINMUX_DATA(PF14_DATA, PF14MD_000),
+	PINMUX_DATA(A25_MARK, PF14MD_001),
+	PINMUX_DATA(SSIWS2_MARK, PF14MD_010),
+	PINMUX_DATA(RXD2_MARK, PF14MD_100),
+
+	PINMUX_DATA(PF13_DATA, PF13MD_000),
+	PINMUX_DATA(A24_MARK, PF13MD_001),
+	PINMUX_DATA(SSISCK2_MARK, PF13MD_010),
+	PINMUX_DATA(SCK2_MARK, PF13MD_100),
+
+	PINMUX_DATA(PF12_DATA, PF12MD_000),
+	PINMUX_DATA(SSIDATA1_MARK, PF12MD_010),
+	PINMUX_DATA(DV_DATA12_MARK, PF12MD_011),
+	PINMUX_DATA(TXD1_MARK, PF12MD_100),
+	PINMUX_DATA(MMC_D7_MARK, PF12MD_101),
+
+	PINMUX_DATA(PF11_DATA, PF11MD_000),
+	PINMUX_DATA(SSIWS1_MARK, PF11MD_010),
+	PINMUX_DATA(DV_DATA2_MARK, PF11MD_011),
+	PINMUX_DATA(RXD1_MARK, PF11MD_100),
+	PINMUX_DATA(MMC_D6_MARK, PF11MD_101),
+
+	PINMUX_DATA(PF10_DATA, PF10MD_000),
+	PINMUX_DATA(CS1_MARK, PF10MD_001),
+	PINMUX_DATA(SSISCK1_MARK, PF10MD_010),
+	PINMUX_DATA(DV_DATA1_MARK, PF10MD_011),
+	PINMUX_DATA(SCK1_MARK, PF10MD_100),
+	PINMUX_DATA(MMC_D5_MARK, PF10MD_101),
+
+	PINMUX_DATA(PF9_DATA, PF9MD_000),
+	PINMUX_DATA(BS_MARK, PF9MD_001),
+	PINMUX_DATA(DV_DATA0_MARK, PF9MD_011),
+	PINMUX_DATA(SCK0_MARK, PF9MD_100),
+	PINMUX_DATA(MMC_D4_MARK, PF9MD_101),
+	PINMUX_DATA(RTS1_MARK, PF9MD_110),
+
+	PINMUX_DATA(PF8_DATA, PF8MD_000),
+	PINMUX_DATA(A23_MARK, PF8MD_001),
+	PINMUX_DATA(TXD0_MARK, PF8MD_100),
+
+	PINMUX_DATA(PF7_DATA, PF7MD_000),
+	PINMUX_DATA(SSIRXD0_MARK, PF7MD_010),
+	PINMUX_DATA(RXD0_MARK, PF7MD_100),
+	PINMUX_DATA(CTS1_MARK, PF7MD_110),
+
+	PINMUX_DATA(PF6_DATA, PF6MD_000),
+	PINMUX_DATA(CE2A_MARK, PF6MD_001),
+	PINMUX_DATA(SSITXD0_MARK, PF6MD_010),
+
+	PINMUX_DATA(PF5_DATA, PF5MD_000),
+	PINMUX_DATA(SSIWS0_MARK, PF5MD_010),
+
+	PINMUX_DATA(PF4_DATA, PF4MD_000),
+	PINMUX_DATA(CS5CE1A_MARK, PF4MD_001),
+	PINMUX_DATA(SSISCK0_MARK, PF4MD_010),
+
+	PINMUX_DATA(PF3_DATA, PF3MD_000),
+	PINMUX_DATA(CS2_MARK, PF3MD_001),
+	PINMUX_DATA(MISO1_MARK, PF3MD_011),
+	PINMUX_DATA(TIOC4D_MARK, PF3MD_100),
+
+	PINMUX_DATA(PF2_DATA, PF2MD_000),
+	PINMUX_DATA(WAIT_MARK, PF2MD_001),
+	PINMUX_DATA(MOSI1_MARK, PF2MD_011),
+	PINMUX_DATA(TIOC4C_MARK, PF2MD_100),
+	PINMUX_DATA(TEND0_MARK, PF2MD_101),
+
+	PINMUX_DATA(PF1_DATA, PF1MD_000),
+	PINMUX_DATA(BACK_MARK, PF1MD_001),
+	PINMUX_DATA(TIOC4B_MARK, PF1MD_100),
+	PINMUX_DATA(DACK0_MARK, PF1MD_101),
+
+	PINMUX_DATA(PF0_DATA, PF0MD_000),
+	PINMUX_DATA(BREQ_MARK, PF0MD_001),
+	PINMUX_DATA(RSPCK1_MARK, PF0MD_011),
+	PINMUX_DATA(TIOC4A_MARK, PF0MD_100),
+	PINMUX_DATA(DREQ0_MARK, PF0MD_101),
+
+	/* Port G */
+	PINMUX_DATA(PG27_DATA, PG27MD_00),
+	PINMUX_DATA(LCD_TCON2_MARK, PG27MD_10),
+	PINMUX_DATA(LCD_EXTCLK_MARK, PG27MD_11),
+
+	PINMUX_DATA(PG26_DATA, PG26MD_00),
+	PINMUX_DATA(LCD_TCON1_MARK, PG26MD_10),
+
+	PINMUX_DATA(PG25_DATA, PG25MD_00),
+	PINMUX_DATA(LCD_TCON0_MARK, PG25MD_10),
+
+	PINMUX_DATA(PG24_DATA, PG24MD_00),
+	PINMUX_DATA(LCD_CLK_MARK, PG24MD_10),
+
+	PINMUX_DATA(PG23_DATA, PG23MD_000),
+	PINMUX_DATA(LCD_DATA23_MARK, PG23MD_010),
+	PINMUX_DATA(LCD_TCON6_MARK, PG23MD_011),
+	PINMUX_DATA(TXD5_MARK, PG23MD_100),
+
+	PINMUX_DATA(PG22_DATA, PG22MD_000),
+	PINMUX_DATA(LCD_DATA22_MARK, PG22MD_010),
+	PINMUX_DATA(LCD_TCON5_MARK, PG22MD_011),
+	PINMUX_DATA(RXD5_MARK, PG22MD_100),
+
+	PINMUX_DATA(PG21_DATA, PG21MD_000),
+	PINMUX_DATA(DV_DATA7_MARK, PG21MD_001),
+	PINMUX_DATA(LCD_DATA21_MARK, PG21MD_010),
+	PINMUX_DATA(LCD_TCON4_MARK, PG21MD_011),
+	PINMUX_DATA(TXD4_MARK, PG21MD_100),
+
+	PINMUX_DATA(PG20_DATA, PG20MD_000),
+	PINMUX_DATA(DV_DATA6_MARK, PG20MD_001),
+	PINMUX_DATA(LCD_DATA20_MARK, PG21MD_010),
+	PINMUX_DATA(LCD_TCON3_MARK, PG20MD_011),
+	PINMUX_DATA(RXD4_MARK, PG20MD_100),
+
+	PINMUX_DATA(PG19_DATA, PG19MD_000),
+	PINMUX_DATA(DV_DATA5_MARK, PG19MD_001),
+	PINMUX_DATA(LCD_DATA19_MARK, PG19MD_010),
+	PINMUX_DATA(SPDIF_OUT_MARK, PG19MD_011),
+	PINMUX_DATA(SCK5_MARK, PG19MD_100),
+
+	PINMUX_DATA(PG18_DATA, PG18MD_000),
+	PINMUX_DATA(DV_DATA4_MARK, PG18MD_001),
+	PINMUX_DATA(LCD_DATA18_MARK, PG18MD_010),
+	PINMUX_DATA(SPDIF_IN_MARK, PG18MD_011),
+	PINMUX_DATA(SCK4_MARK, PG18MD_100),
+
+// TODO hardware manual has PG17 3 bits wide in reg picture and 2 bits in description
+// we're going with 2 bits
+	PINMUX_DATA(PG17_DATA, PG17MD_00),
+	PINMUX_DATA(WE3ICIOWRAHDQMUU_MARK, PG17MD_01),
+	PINMUX_DATA(LCD_DATA17_MARK, PG17MD_10),
+
+// TODO hardware manual has PG16 3 bits wide in reg picture and 2 bits in description
+// we're going with 2 bits
+	PINMUX_DATA(PG16_DATA, PG16MD_00),
+	PINMUX_DATA(WE2ICIORDDQMUL_MARK, PG16MD_01),
+	PINMUX_DATA(LCD_DATA16_MARK, PG16MD_10),
+
+	PINMUX_DATA(PG15_DATA, PG15MD_00),
+	PINMUX_DATA(D31_MARK, PG15MD_01),
+	PINMUX_DATA(LCD_DATA15_MARK, PG15MD_10),
+	PINMUX_DATA(PINT7_PG_MARK, PG15MD_11),
+
+	PINMUX_DATA(PG14_DATA, PG14MD_00),
+	PINMUX_DATA(D30_MARK, PG14MD_01),
+	PINMUX_DATA(LCD_DATA14_MARK, PG14MD_10),
+	PINMUX_DATA(PINT6_PG_MARK, PG14MD_11),
+
+	PINMUX_DATA(PG13_DATA, PG13MD_00),
+	PINMUX_DATA(D29_MARK, PG13MD_01),
+	PINMUX_DATA(LCD_DATA13_MARK, PG13MD_10),
+	PINMUX_DATA(PINT5_PG_MARK, PG13MD_11),
+
+	PINMUX_DATA(PG12_DATA, PG12MD_00),
+	PINMUX_DATA(D28_MARK, PG12MD_01),
+	PINMUX_DATA(LCD_DATA12_MARK, PG12MD_10),
+	PINMUX_DATA(PINT4_PG_MARK, PG12MD_11),
+
+	PINMUX_DATA(PG11_DATA, PG11MD_000),
+	PINMUX_DATA(D27_MARK, PG11MD_001),
+	PINMUX_DATA(LCD_DATA11_MARK, PG11MD_010),
+	PINMUX_DATA(PINT3_PG_MARK, PG11MD_011),
+	PINMUX_DATA(TIOC3D_MARK, PG11MD_100),
+
+	PINMUX_DATA(PG10_DATA, PG10MD_000),
+	PINMUX_DATA(D26_MARK, PG10MD_001),
+	PINMUX_DATA(LCD_DATA10_MARK, PG10MD_010),
+	PINMUX_DATA(PINT2_PG_MARK, PG10MD_011),
+	PINMUX_DATA(TIOC3C_MARK, PG10MD_100),
+
+	PINMUX_DATA(PG9_DATA, PG9MD_000),
+	PINMUX_DATA(D25_MARK, PG9MD_001),
+	PINMUX_DATA(LCD_DATA9_MARK, PG9MD_010),
+	PINMUX_DATA(PINT1_PG_MARK, PG9MD_011),
+	PINMUX_DATA(TIOC3B_MARK, PG9MD_100),
+
+	PINMUX_DATA(PG8_DATA, PG8MD_000),
+	PINMUX_DATA(D24_MARK, PG8MD_001),
+	PINMUX_DATA(LCD_DATA8_MARK, PG8MD_010),
+	PINMUX_DATA(PINT0_PG_MARK, PG8MD_011),
+	PINMUX_DATA(TIOC3A_MARK, PG8MD_100),
+
+	PINMUX_DATA(PG7_DATA, PG7MD_000),
+	PINMUX_DATA(D23_MARK, PG7MD_001),
+	PINMUX_DATA(LCD_DATA7_MARK, PG7MD_010),
+	PINMUX_DATA(IRQ7_PG_MARK, PG7MD_011),
+	PINMUX_DATA(TIOC2B_MARK, PG7MD_100),
+
+	PINMUX_DATA(PG6_DATA, PG6MD_000),
+	PINMUX_DATA(D22_MARK, PG6MD_001),
+	PINMUX_DATA(LCD_DATA6_MARK, PG6MD_010),
+	PINMUX_DATA(IRQ6_PG_MARK, PG6MD_011),
+	PINMUX_DATA(TIOC2A_MARK, PG6MD_100),
+
+	PINMUX_DATA(PG5_DATA, PG5MD_000),
+	PINMUX_DATA(D21_MARK, PG5MD_001),
+	PINMUX_DATA(LCD_DATA5_MARK, PG5MD_010),
+	PINMUX_DATA(IRQ5_PG_MARK, PG5MD_011),
+	PINMUX_DATA(TIOC1B_MARK, PG5MD_100),
+
+	PINMUX_DATA(PG4_DATA, PG4MD_000),
+	PINMUX_DATA(D20_MARK, PG4MD_001),
+	PINMUX_DATA(LCD_DATA4_MARK, PG4MD_010),
+	PINMUX_DATA(IRQ4_PG_MARK, PG4MD_011),
+	PINMUX_DATA(TIOC1A_MARK, PG4MD_100),
+
+	PINMUX_DATA(PG3_DATA, PG3MD_000),
+	PINMUX_DATA(D19_MARK, PG3MD_001),
+	PINMUX_DATA(LCD_DATA3_MARK, PG3MD_010),
+	PINMUX_DATA(IRQ3_PG_MARK, PG3MD_011),
+	PINMUX_DATA(TIOC0D_MARK, PG3MD_100),
+
+	PINMUX_DATA(PG2_DATA, PG2MD_000),
+	PINMUX_DATA(D18_MARK, PG2MD_001),
+	PINMUX_DATA(LCD_DATA2_MARK, PG2MD_010),
+	PINMUX_DATA(IRQ2_PG_MARK, PG2MD_011),
+	PINMUX_DATA(TIOC0C_MARK, PG2MD_100),
+
+	PINMUX_DATA(PG1_DATA, PG1MD_000),
+	PINMUX_DATA(D17_MARK, PG1MD_001),
+	PINMUX_DATA(LCD_DATA1_MARK, PG1MD_010),
+	PINMUX_DATA(IRQ1_PG_MARK, PG1MD_011),
+	PINMUX_DATA(TIOC0B_MARK, PG1MD_100),
+
+	PINMUX_DATA(PG0_DATA, PG0MD_000),
+	PINMUX_DATA(D16_MARK, PG0MD_001),
+	PINMUX_DATA(LCD_DATA0_MARK, PG0MD_010),
+	PINMUX_DATA(IRQ0_PG_MARK, PG0MD_011),
+	PINMUX_DATA(TIOC0A_MARK, PG0MD_100),
+
+	/* Port H */
+	PINMUX_DATA(PH7_DATA, PH7MD_00),
+	PINMUX_DATA(PHAN7_MARK, PH7MD_01),
+	PINMUX_DATA(PINT7_PH_MARK, PH7MD_10),
+
+	PINMUX_DATA(PH6_DATA, PH6MD_00),
+	PINMUX_DATA(PHAN6_MARK, PH6MD_01),
+	PINMUX_DATA(PINT6_PH_MARK, PH6MD_10),
+
+	PINMUX_DATA(PH5_DATA, PH5MD_00),
+	PINMUX_DATA(PHAN5_MARK, PH5MD_01),
+	PINMUX_DATA(PINT5_PH_MARK, PH5MD_10),
+	PINMUX_DATA(LCD_EXTCLK_MARK, PH5MD_11),
+
+	PINMUX_DATA(PH4_DATA, PH4MD_00),
+	PINMUX_DATA(PHAN4_MARK, PH4MD_01),
+	PINMUX_DATA(PINT4_PH_MARK, PH4MD_10),
+
+	PINMUX_DATA(PH3_DATA, PH3MD_00),
+	PINMUX_DATA(PHAN3_MARK, PH3MD_01),
+	PINMUX_DATA(PINT3_PH_MARK, PH3MD_10),
+
+	PINMUX_DATA(PH2_DATA, PH2MD_00),
+	PINMUX_DATA(PHAN2_MARK, PH2MD_01),
+	PINMUX_DATA(PINT2_PH_MARK, PH2MD_10),
+
+	PINMUX_DATA(PH1_DATA, PH1MD_00),
+	PINMUX_DATA(PHAN1_MARK, PH1MD_01),
+	PINMUX_DATA(PINT1_PH_MARK, PH1MD_10),
+
+	PINMUX_DATA(PH0_DATA, PH0MD_00),
+	PINMUX_DATA(PHAN0_MARK, PH0MD_01),
+	PINMUX_DATA(PINT0_PH_MARK, PH0MD_10),
+
+	/* Port I - not on device */
+
+	/* Port J */
+	PINMUX_DATA(PJ31_DATA, PJ31MD_0),
+	PINMUX_DATA(DV_CLK_MARK, PJ31MD_1),
+
+	PINMUX_DATA(PJ30_DATA, PJ30MD_000),
+	PINMUX_DATA(SSIDATA5_MARK, PJ30MD_010),
+	PINMUX_DATA(TIOC2B_MARK, PJ30MD_100),
+	PINMUX_DATA(IETXD_MARK, PJ30MD_101),
+
+	PINMUX_DATA(PJ29_DATA, PJ29MD_000),
+	PINMUX_DATA(SSIWS5_MARK, PJ29MD_010),
+	PINMUX_DATA(TIOC2A_MARK, PJ29MD_100),
+	PINMUX_DATA(IERXD_MARK, PJ29MD_101),
+
+	PINMUX_DATA(PJ28_DATA, PJ28MD_000),
+	PINMUX_DATA(SSISCK5_MARK, PJ28MD_010),
+	PINMUX_DATA(TIOC1B_MARK, PJ28MD_100),
+	PINMUX_DATA(RTS7_MARK, PJ28MD_101),
+
+	PINMUX_DATA(PJ27_DATA, PJ27MD_000),
+	PINMUX_DATA(TIOC1A_MARK, PJ27MD_100),
+	PINMUX_DATA(CTS7_MARK, PJ27MD_101),
+
+	PINMUX_DATA(PJ26_DATA, PJ26MD_000),
+	PINMUX_DATA(SSIDATA4_MARK, PJ26MD_010),
+	PINMUX_DATA(LCD_TCON5_MARK, PJ26MD_011),
+	PINMUX_DATA(TXD7_MARK, PJ26MD_101),
+
+	PINMUX_DATA(PJ25_DATA, PJ25MD_000),
+	PINMUX_DATA(SSIWS4_MARK, PJ25MD_010),
+	PINMUX_DATA(LCD_TCON4_MARK, PJ25MD_011),
+	PINMUX_DATA(SPDIF_OUT_MARK, PJ25MD_100),
+	PINMUX_DATA(RXD7_MARK, PJ25MD_101),
+
+	PINMUX_DATA(PJ24_DATA, PJ24MD_000),
+	PINMUX_DATA(SSISCK4_MARK, PJ24MD_010),
+	PINMUX_DATA(LCD_TCON3_MARK, PJ24MD_011),
+	PINMUX_DATA(SPDIF_IN_MARK, PJ24MD_100),
+	PINMUX_DATA(SCK7_MARK, PJ24MD_101),
+
+	PINMUX_DATA(PJ23_DATA, PJ23MD_000),
+	PINMUX_DATA(DV_DATA23_MARK, PJ23MD_001),
+	PINMUX_DATA(LCD_DATA23_MARK, PJ23MD_010),
+	PINMUX_DATA(LCD_TCON6_MARK, PJ23MD_011),
+	PINMUX_DATA(IRQ3_PJ_MARK, PJ23MD_100),
+	PINMUX_DATA(CTX1_MARK, PJ23MD_101),
+
+	PINMUX_DATA(PJ22_DATA, PJ22MD_000),
+	PINMUX_DATA(DV_DATA22_MARK, PJ22MD_001),
+	PINMUX_DATA(LCD_DATA22_MARK, PJ22MD_010),
+	PINMUX_DATA(LCD_TCON5_MARK, PJ22MD_011),
+	PINMUX_DATA(IRQ2_PJ_MARK, PJ22MD_100),
+	PINMUX_DATA(CRX1_MARK, PJ22MD_101),
+	PINMUX_DATA(CRX0CRX1_MARK, PJ22MD_110),
+
+	PINMUX_DATA(PJ21_DATA, PJ21MD_000),
+	PINMUX_DATA(DV_DATA21_MARK, PJ21MD_001),
+	PINMUX_DATA(LCD_DATA21_MARK, PJ21MD_010),
+	PINMUX_DATA(LCD_TCON4_MARK, PJ21MD_011),
+	PINMUX_DATA(IRQ1_PJ_MARK, PJ21MD_100),
+	PINMUX_DATA(CTX2_MARK, PJ21MD_101),
+
+	PINMUX_DATA(PJ20_DATA, PJ20MD_000),
+	PINMUX_DATA(DV_DATA20_MARK, PJ20MD_001),
+	PINMUX_DATA(LCD_DATA20_MARK, PJ20MD_010),
+	PINMUX_DATA(LCD_TCON3_MARK, PJ20MD_011),
+	PINMUX_DATA(IRQ0_PJ_MARK, PJ20MD_100),
+	PINMUX_DATA(CRX2_MARK, PJ20MD_101),
+	PINMUX_DATA(CRX0CRX1CRX2_PJ20_MARK, PJ20MD_110),
+
+	PINMUX_DATA(PJ19_DATA, PJ19MD_000),
+	PINMUX_DATA(DV_DATA19_MARK, PJ19MD_001),
+	PINMUX_DATA(LCD_DATA19_MARK, PJ19MD_010),
+	PINMUX_DATA(MISO0_PJ19_MARK, PJ19MD_011),
+	PINMUX_DATA(TIOC0D_MARK, PJ19MD_100),
+	PINMUX_DATA(SIOFRXD_MARK, PJ19MD_101),
+	PINMUX_DATA(AUDIO_XOUT_MARK, PJ19MD_110),
+
+	PINMUX_DATA(PJ18_DATA, PJ18MD_000),
+	PINMUX_DATA(DV_DATA18_MARK, PJ18MD_001),
+	PINMUX_DATA(LCD_DATA18_MARK, PJ18MD_010),
+	PINMUX_DATA(MOSI0_PJ18_MARK, PJ18MD_011),
+	PINMUX_DATA(TIOC0C_MARK, PJ18MD_100),
+	PINMUX_DATA(SIOFTXD_MARK, PJ18MD_101),
+
+	PINMUX_DATA(PJ17_DATA, PJ17MD_000),
+	PINMUX_DATA(DV_DATA17_MARK, PJ17MD_001),
+	PINMUX_DATA(LCD_DATA17_MARK, PJ17MD_010),
+	PINMUX_DATA(SSL00_PJ17_MARK, PJ17MD_011),
+	PINMUX_DATA(TIOC0B_MARK, PJ17MD_100),
+	PINMUX_DATA(SIOFSYNC_MARK, PJ17MD_101),
+
+	PINMUX_DATA(PJ16_DATA, PJ16MD_000),
+	PINMUX_DATA(DV_DATA16_MARK, PJ16MD_001),
+	PINMUX_DATA(LCD_DATA16_MARK, PJ16MD_010),
+	PINMUX_DATA(RSPCK0_PJ16_MARK, PJ16MD_011),
+	PINMUX_DATA(TIOC0A_MARK, PJ16MD_100),
+	PINMUX_DATA(SIOFSCK_MARK, PJ16MD_101),
+
+	PINMUX_DATA(PJ15_DATA, PJ15MD_000),
+	PINMUX_DATA(DV_DATA15_MARK, PJ15MD_001),
+	PINMUX_DATA(LCD_DATA15_MARK, PJ15MD_010),
+	PINMUX_DATA(PINT7_PJ_MARK, PJ15MD_011),
+	PINMUX_DATA(PWM2H_MARK, PJ15MD_100),
+	PINMUX_DATA(TXD7_MARK, PJ15MD_101),
+
+	PINMUX_DATA(PJ14_DATA, PJ14MD_000),
+	PINMUX_DATA(DV_DATA14_MARK, PJ14MD_001),
+	PINMUX_DATA(LCD_DATA14_MARK, PJ14MD_010),
+	PINMUX_DATA(PINT6_PJ_MARK, PJ14MD_011),
+	PINMUX_DATA(PWM2G_MARK, PJ14MD_100),
+	PINMUX_DATA(TXD6_MARK, PJ14MD_101),
+
+	PINMUX_DATA(PJ13_DATA, PJ13MD_000),
+	PINMUX_DATA(DV_DATA13_MARK, PJ13MD_001),
+	PINMUX_DATA(LCD_DATA13_MARK, PJ13MD_010),
+	PINMUX_DATA(PINT5_PJ_MARK, PJ13MD_011),
+	PINMUX_DATA(PWM2F_MARK, PJ13MD_100),
+	PINMUX_DATA(TXD5_MARK, PJ13MD_101),
+
+	PINMUX_DATA(PJ12_DATA, PJ12MD_000),
+	PINMUX_DATA(DV_DATA12_MARK, PJ12MD_001),
+	PINMUX_DATA(LCD_DATA12_MARK, PJ12MD_010),
+	PINMUX_DATA(PINT4_PJ_MARK, PJ12MD_011),
+	PINMUX_DATA(PWM2E_MARK, PJ12MD_100),
+	PINMUX_DATA(SCK7_MARK, PJ12MD_101),
+
+	PINMUX_DATA(PJ11_DATA, PJ11MD_000),
+	PINMUX_DATA(DV_DATA11_MARK, PJ11MD_001),
+	PINMUX_DATA(LCD_DATA11_MARK, PJ11MD_010),
+	PINMUX_DATA(PINT3_PJ_MARK, PJ11MD_011),
+	PINMUX_DATA(PWM2D_MARK, PJ11MD_100),
+	PINMUX_DATA(SCK6_MARK, PJ11MD_101),
+
+	PINMUX_DATA(PJ10_DATA, PJ10MD_000),
+	PINMUX_DATA(DV_DATA10_MARK, PJ10MD_001),
+	PINMUX_DATA(LCD_DATA10_MARK, PJ10MD_010),
+	PINMUX_DATA(PINT2_PJ_MARK, PJ10MD_011),
+	PINMUX_DATA(PWM2C_MARK, PJ10MD_100),
+	PINMUX_DATA(SCK5_MARK, PJ10MD_101),
+
+	PINMUX_DATA(PJ9_DATA, PJ9MD_000),
+	PINMUX_DATA(DV_DATA9_MARK, PJ9MD_001),
+	PINMUX_DATA(LCD_DATA9_MARK, PJ9MD_010),
+	PINMUX_DATA(PINT1_PJ_MARK, PJ9MD_011),
+	PINMUX_DATA(PWM2B_MARK, PJ9MD_100),
+	PINMUX_DATA(RTS5_MARK, PJ9MD_101),
+
+	PINMUX_DATA(PJ8_DATA, PJ8MD_000),
+	PINMUX_DATA(DV_DATA8_MARK, PJ8MD_001),
+	PINMUX_DATA(LCD_DATA8_MARK, PJ8MD_010),
+	PINMUX_DATA(PINT0_PJ_MARK, PJ8MD_011),
+	PINMUX_DATA(PWM2A_MARK, PJ8MD_100),
+	PINMUX_DATA(CTS5_MARK, PJ8MD_101),
+
+	PINMUX_DATA(PJ7_DATA, PJ7MD_000),
+	PINMUX_DATA(DV_DATA7_MARK, PJ7MD_001),
+	PINMUX_DATA(LCD_DATA7_MARK, PJ7MD_010),
+	PINMUX_DATA(SD_D2_MARK, PJ7MD_011),
+	PINMUX_DATA(PWM1H_MARK, PJ7MD_100),
+
+	PINMUX_DATA(PJ6_DATA, PJ6MD_000),
+	PINMUX_DATA(DV_DATA6_MARK, PJ6MD_001),
+	PINMUX_DATA(LCD_DATA6_MARK, PJ6MD_010),
+	PINMUX_DATA(SD_D3_MARK, PJ6MD_011),
+	PINMUX_DATA(PWM1G_MARK, PJ6MD_100),
+
+	PINMUX_DATA(PJ5_DATA, PJ5MD_000),
+	PINMUX_DATA(DV_DATA5_MARK, PJ5MD_001),
+	PINMUX_DATA(LCD_DATA5_MARK, PJ5MD_010),
+	PINMUX_DATA(SD_CMD_MARK, PJ5MD_011),
+	PINMUX_DATA(PWM1F_MARK, PJ5MD_100),
+
+	PINMUX_DATA(PJ4_DATA, PJ4MD_000),
+	PINMUX_DATA(DV_DATA4_MARK, PJ4MD_001),
+	PINMUX_DATA(LCD_DATA4_MARK, PJ4MD_010),
+	PINMUX_DATA(SD_CLK_MARK, PJ4MD_011),
+	PINMUX_DATA(PWM1E_MARK, PJ4MD_100),
+
+	PINMUX_DATA(PJ3_DATA, PJ3MD_000),
+	PINMUX_DATA(DV_DATA3_MARK, PJ3MD_001),
+	PINMUX_DATA(LCD_DATA3_MARK, PJ3MD_010),
+	PINMUX_DATA(SD_D0_MARK, PJ3MD_011),
+	PINMUX_DATA(PWM1D_MARK, PJ3MD_100),
+
+	PINMUX_DATA(PJ2_DATA, PJ2MD_000),
+	PINMUX_DATA(DV_DATA2_MARK, PJ2MD_001),
+	PINMUX_DATA(LCD_DATA2_MARK, PJ2MD_010),
+	PINMUX_DATA(SD_D1_MARK, PJ2MD_011),
+	PINMUX_DATA(PWM1C_MARK, PJ2MD_100),
+
+	PINMUX_DATA(PJ1_DATA, PJ1MD_000),
+	PINMUX_DATA(DV_DATA1_MARK, PJ1MD_001),
+	PINMUX_DATA(LCD_DATA1_MARK, PJ1MD_010),
+	PINMUX_DATA(SD_WP_MARK, PJ1MD_011),
+	PINMUX_DATA(PWM1B_MARK, PJ1MD_100),
+
+	PINMUX_DATA(PJ0_DATA, PJ0MD_000),
+	PINMUX_DATA(DV_DATA0_MARK, PJ0MD_001),
+	PINMUX_DATA(LCD_DATA0_MARK, PJ0MD_010),
+	PINMUX_DATA(SD_CD_MARK, PJ0MD_011),
+	PINMUX_DATA(PWM1A_MARK, PJ0MD_100),
+};
+
+static struct pinmux_gpio pinmux_gpios[] = {
+	/* Port A */
+	PINMUX_GPIO(GPIO_PA1, PA1_DATA),
+	PINMUX_GPIO(GPIO_PA0, PA0_DATA),
+
+	/* Port B */
+	PINMUX_GPIO(GPIO_PB22, PB22_DATA),
+	PINMUX_GPIO(GPIO_PB21, PB21_DATA),
+	PINMUX_GPIO(GPIO_PB20, PB20_DATA),
+	PINMUX_GPIO(GPIO_PB19, PB19_DATA),
+	PINMUX_GPIO(GPIO_PB18, PB18_DATA),
+	PINMUX_GPIO(GPIO_PB17, PB17_DATA),
+	PINMUX_GPIO(GPIO_PB16, PB16_DATA),
+	PINMUX_GPIO(GPIO_PB15, PB15_DATA),
+	PINMUX_GPIO(GPIO_PB14, PB14_DATA),
+	PINMUX_GPIO(GPIO_PB13, PB13_DATA),
+	PINMUX_GPIO(GPIO_PB12, PB12_DATA),
+	PINMUX_GPIO(GPIO_PB11, PB11_DATA),
+	PINMUX_GPIO(GPIO_PB10, PB10_DATA),
+	PINMUX_GPIO(GPIO_PB9, PB9_DATA),
+	PINMUX_GPIO(GPIO_PB8, PB8_DATA),
+	PINMUX_GPIO(GPIO_PB7, PB7_DATA),
+	PINMUX_GPIO(GPIO_PB6, PB6_DATA),
+	PINMUX_GPIO(GPIO_PB5, PB5_DATA),
+	PINMUX_GPIO(GPIO_PB4, PB4_DATA),
+	PINMUX_GPIO(GPIO_PB3, PB3_DATA),
+	PINMUX_GPIO(GPIO_PB2, PB2_DATA),
+	PINMUX_GPIO(GPIO_PB1, PB1_DATA),
+
+	/* Port C */
+	PINMUX_GPIO(GPIO_PC8, PC8_DATA),
+	PINMUX_GPIO(GPIO_PC7, PC7_DATA),
+	PINMUX_GPIO(GPIO_PC6, PC6_DATA),
+	PINMUX_GPIO(GPIO_PC5, PC5_DATA),
+	PINMUX_GPIO(GPIO_PC4, PC4_DATA),
+	PINMUX_GPIO(GPIO_PC3, PC3_DATA),
+	PINMUX_GPIO(GPIO_PC2, PC2_DATA),
+	PINMUX_GPIO(GPIO_PC1, PC1_DATA),
+	PINMUX_GPIO(GPIO_PC0, PC0_DATA),
+
+	/* Port D */
+	PINMUX_GPIO(GPIO_PD15, PD15_DATA),
+	PINMUX_GPIO(GPIO_PD14, PD14_DATA),
+	PINMUX_GPIO(GPIO_PD13, PD13_DATA),
+	PINMUX_GPIO(GPIO_PD12, PD12_DATA),
+	PINMUX_GPIO(GPIO_PD11, PD11_DATA),
+	PINMUX_GPIO(GPIO_PD10, PD10_DATA),
+	PINMUX_GPIO(GPIO_PD9, PD9_DATA),
+	PINMUX_GPIO(GPIO_PD8, PD8_DATA),
+	PINMUX_GPIO(GPIO_PD7, PD7_DATA),
+	PINMUX_GPIO(GPIO_PD6, PD6_DATA),
+	PINMUX_GPIO(GPIO_PD5, PD5_DATA),
+	PINMUX_GPIO(GPIO_PD4, PD4_DATA),
+	PINMUX_GPIO(GPIO_PD3, PD3_DATA),
+	PINMUX_GPIO(GPIO_PD2, PD2_DATA),
+	PINMUX_GPIO(GPIO_PD1, PD1_DATA),
+	PINMUX_GPIO(GPIO_PD0, PD0_DATA),
+
+	/* Port E */
+	PINMUX_GPIO(GPIO_PE7, PE7_DATA),
+	PINMUX_GPIO(GPIO_PE6, PE6_DATA),
+	PINMUX_GPIO(GPIO_PE5, PE5_DATA),
+	PINMUX_GPIO(GPIO_PE4, PE4_DATA),
+	PINMUX_GPIO(GPIO_PE3, PE3_DATA),
+	PINMUX_GPIO(GPIO_PE2, PE2_DATA),
+	PINMUX_GPIO(GPIO_PE1, PE1_DATA),
+	PINMUX_GPIO(GPIO_PE0, PE0_DATA),
+
+	/* Port F */
+	PINMUX_GPIO(GPIO_PF23, PF23_DATA),
+	PINMUX_GPIO(GPIO_PF22, PF22_DATA),
+	PINMUX_GPIO(GPIO_PF21, PF21_DATA),
+	PINMUX_GPIO(GPIO_PF20, PF20_DATA),
+	PINMUX_GPIO(GPIO_PF19, PF19_DATA),
+	PINMUX_GPIO(GPIO_PF18, PF18_DATA),
+	PINMUX_GPIO(GPIO_PF17, PF17_DATA),
+	PINMUX_GPIO(GPIO_PF16, PF16_DATA),
+	PINMUX_GPIO(GPIO_PF15, PF15_DATA),
+	PINMUX_GPIO(GPIO_PF14, PF14_DATA),
+	PINMUX_GPIO(GPIO_PF13, PF13_DATA),
+	PINMUX_GPIO(GPIO_PF12, PF12_DATA),
+	PINMUX_GPIO(GPIO_PF11, PF11_DATA),
+	PINMUX_GPIO(GPIO_PF10, PF10_DATA),
+	PINMUX_GPIO(GPIO_PF9, PF9_DATA),
+	PINMUX_GPIO(GPIO_PF8, PF8_DATA),
+	PINMUX_GPIO(GPIO_PF7, PF7_DATA),
+	PINMUX_GPIO(GPIO_PF6, PF6_DATA),
+	PINMUX_GPIO(GPIO_PF5, PF5_DATA),
+	PINMUX_GPIO(GPIO_PF4, PF4_DATA),
+	PINMUX_GPIO(GPIO_PF3, PF3_DATA),
+	PINMUX_GPIO(GPIO_PF2, PF2_DATA),
+	PINMUX_GPIO(GPIO_PF1, PF1_DATA),
+	PINMUX_GPIO(GPIO_PF0, PF0_DATA),
+
+	/* Port G */
+	PINMUX_GPIO(GPIO_PG27, PG27_DATA),
+	PINMUX_GPIO(GPIO_PG26, PG26_DATA),
+	PINMUX_GPIO(GPIO_PG25, PG25_DATA),
+	PINMUX_GPIO(GPIO_PG24, PG24_DATA),
+	PINMUX_GPIO(GPIO_PG23, PG23_DATA),
+	PINMUX_GPIO(GPIO_PG22, PG22_DATA),
+	PINMUX_GPIO(GPIO_PG21, PG21_DATA),
+	PINMUX_GPIO(GPIO_PG20, PG20_DATA),
+	PINMUX_GPIO(GPIO_PG19, PG19_DATA),
+	PINMUX_GPIO(GPIO_PG18, PG18_DATA),
+	PINMUX_GPIO(GPIO_PG17, PG17_DATA),
+	PINMUX_GPIO(GPIO_PG16, PG16_DATA),
+	PINMUX_GPIO(GPIO_PG15, PG15_DATA),
+	PINMUX_GPIO(GPIO_PG14, PG14_DATA),
+	PINMUX_GPIO(GPIO_PG13, PG13_DATA),
+	PINMUX_GPIO(GPIO_PG12, PG12_DATA),
+	PINMUX_GPIO(GPIO_PG11, PG11_DATA),
+	PINMUX_GPIO(GPIO_PG10, PG10_DATA),
+	PINMUX_GPIO(GPIO_PG9, PG9_DATA),
+	PINMUX_GPIO(GPIO_PG8, PG8_DATA),
+	PINMUX_GPIO(GPIO_PG7, PG7_DATA),
+	PINMUX_GPIO(GPIO_PG6, PG6_DATA),
+	PINMUX_GPIO(GPIO_PG5, PG5_DATA),
+	PINMUX_GPIO(GPIO_PG4, PG4_DATA),
+	PINMUX_GPIO(GPIO_PG3, PG3_DATA),
+	PINMUX_GPIO(GPIO_PG2, PG2_DATA),
+	PINMUX_GPIO(GPIO_PG1, PG1_DATA),
+	PINMUX_GPIO(GPIO_PG0, PG0_DATA),
+
+	/* Port H - Port H does not have a Data Register */
+
+	/* Port I - not on device */
+
+	/* Port J */
+	PINMUX_GPIO(GPIO_PJ31, PJ31_DATA),
+	PINMUX_GPIO(GPIO_PJ30, PJ30_DATA),
+	PINMUX_GPIO(GPIO_PJ29, PJ29_DATA),
+	PINMUX_GPIO(GPIO_PJ28, PJ28_DATA),
+	PINMUX_GPIO(GPIO_PJ27, PJ27_DATA),
+	PINMUX_GPIO(GPIO_PJ26, PJ26_DATA),
+	PINMUX_GPIO(GPIO_PJ25, PJ25_DATA),
+	PINMUX_GPIO(GPIO_PJ24, PJ24_DATA),
+	PINMUX_GPIO(GPIO_PJ23, PJ23_DATA),
+	PINMUX_GPIO(GPIO_PJ22, PJ22_DATA),
+	PINMUX_GPIO(GPIO_PJ21, PJ21_DATA),
+	PINMUX_GPIO(GPIO_PJ20, PJ20_DATA),
+	PINMUX_GPIO(GPIO_PJ19, PJ19_DATA),
+	PINMUX_GPIO(GPIO_PJ18, PJ18_DATA),
+	PINMUX_GPIO(GPIO_PJ17, PJ17_DATA),
+	PINMUX_GPIO(GPIO_PJ16, PJ16_DATA),
+	PINMUX_GPIO(GPIO_PJ15, PJ15_DATA),
+	PINMUX_GPIO(GPIO_PJ14, PJ14_DATA),
+	PINMUX_GPIO(GPIO_PJ13, PJ13_DATA),
+	PINMUX_GPIO(GPIO_PJ12, PJ12_DATA),
+	PINMUX_GPIO(GPIO_PJ11, PJ11_DATA),
+	PINMUX_GPIO(GPIO_PJ10, PJ10_DATA),
+	PINMUX_GPIO(GPIO_PJ9, PJ9_DATA),
+	PINMUX_GPIO(GPIO_PJ8, PJ8_DATA),
+	PINMUX_GPIO(GPIO_PJ7, PJ7_DATA),
+	PINMUX_GPIO(GPIO_PJ6, PJ6_DATA),
+	PINMUX_GPIO(GPIO_PJ5, PJ5_DATA),
+	PINMUX_GPIO(GPIO_PJ4, PJ4_DATA),
+	PINMUX_GPIO(GPIO_PJ3, PJ3_DATA),
+	PINMUX_GPIO(GPIO_PJ2, PJ2_DATA),
+	PINMUX_GPIO(GPIO_PJ1, PJ1_DATA),
+	PINMUX_GPIO(GPIO_PJ0, PJ0_DATA),
+
+	/* INTC */
+	PINMUX_GPIO(GPIO_FN_IRQ7_PG, IRQ7_PG_MARK),
+	PINMUX_GPIO(GPIO_FN_IRQ6_PG, IRQ6_PG_MARK),
+	PINMUX_GPIO(GPIO_FN_IRQ5_PG, IRQ5_PG_MARK),
+	PINMUX_GPIO(GPIO_FN_IRQ4_PG, IRQ4_PG_MARK),
+	PINMUX_GPIO(GPIO_FN_IRQ3_PG, IRQ3_PG_MARK),
+	PINMUX_GPIO(GPIO_FN_IRQ2_PG, IRQ2_PG_MARK),
+	PINMUX_GPIO(GPIO_FN_IRQ1_PG, IRQ1_PG_MARK),
+	PINMUX_GPIO(GPIO_FN_IRQ0_PG, IRQ0_PG_MARK),
+	PINMUX_GPIO(GPIO_FN_IRQ7_PF, IRQ7_PF_MARK),
+	PINMUX_GPIO(GPIO_FN_IRQ6_PF, IRQ6_PF_MARK),
+	PINMUX_GPIO(GPIO_FN_IRQ5_PF, IRQ5_PF_MARK),
+	PINMUX_GPIO(GPIO_FN_IRQ4_PF, IRQ4_PF_MARK),
+	PINMUX_GPIO(GPIO_FN_IRQ3_PJ, IRQ3_PJ_MARK),
+	PINMUX_GPIO(GPIO_FN_IRQ2_PJ, IRQ2_PJ_MARK),
+	PINMUX_GPIO(GPIO_FN_IRQ1_PJ, IRQ1_PJ_MARK),
+	PINMUX_GPIO(GPIO_FN_IRQ0_PJ, IRQ0_PJ_MARK),
+	PINMUX_GPIO(GPIO_FN_IRQ1_PC, IRQ1_PC_MARK),
+	PINMUX_GPIO(GPIO_FN_IRQ0_PC, IRQ0_PC_MARK),
+
+	PINMUX_GPIO(GPIO_FN_PINT7_PG, PINT7_PG_MARK),
+	PINMUX_GPIO(GPIO_FN_PINT6_PG, PINT6_PG_MARK),
+	PINMUX_GPIO(GPIO_FN_PINT5_PG, PINT5_PG_MARK),
+	PINMUX_GPIO(GPIO_FN_PINT4_PG, PINT4_PG_MARK),
+	PINMUX_GPIO(GPIO_FN_PINT3_PG, PINT3_PG_MARK),
+	PINMUX_GPIO(GPIO_FN_PINT2_PG, PINT2_PG_MARK),
+	PINMUX_GPIO(GPIO_FN_PINT1_PG, PINT1_PG_MARK),
+	PINMUX_GPIO(GPIO_FN_PINT0_PG, PINT0_PG_MARK),
+	PINMUX_GPIO(GPIO_FN_PINT7_PH, PINT7_PH_MARK),
+	PINMUX_GPIO(GPIO_FN_PINT6_PH, PINT6_PH_MARK),
+	PINMUX_GPIO(GPIO_FN_PINT5_PH, PINT5_PH_MARK),
+	PINMUX_GPIO(GPIO_FN_PINT4_PH, PINT4_PH_MARK),
+	PINMUX_GPIO(GPIO_FN_PINT3_PH, PINT3_PH_MARK),
+	PINMUX_GPIO(GPIO_FN_PINT2_PH, PINT2_PH_MARK),
+	PINMUX_GPIO(GPIO_FN_PINT1_PH, PINT1_PH_MARK),
+	PINMUX_GPIO(GPIO_FN_PINT0_PH, PINT0_PH_MARK),
+	PINMUX_GPIO(GPIO_FN_PINT7_PJ, PINT7_PJ_MARK),
+	PINMUX_GPIO(GPIO_FN_PINT6_PJ, PINT6_PJ_MARK),
+	PINMUX_GPIO(GPIO_FN_PINT5_PJ, PINT5_PJ_MARK),
+	PINMUX_GPIO(GPIO_FN_PINT4_PJ, PINT4_PJ_MARK),
+	PINMUX_GPIO(GPIO_FN_PINT3_PJ, PINT3_PJ_MARK),
+	PINMUX_GPIO(GPIO_FN_PINT2_PJ, PINT2_PJ_MARK),
+	PINMUX_GPIO(GPIO_FN_PINT1_PJ, PINT1_PJ_MARK),
+	PINMUX_GPIO(GPIO_FN_PINT0_PJ, PINT0_PJ_MARK),
+
+	/* WDT */
+	PINMUX_GPIO(GPIO_FN_WDTOVF, WDTOVF_MARK),
+
+	/* CAN */
+	PINMUX_GPIO(GPIO_FN_CTX1, CTX1_MARK),
+	PINMUX_GPIO(GPIO_FN_CRX1, CRX1_MARK),
+	PINMUX_GPIO(GPIO_FN_CTX0, CTX0_MARK),
+	PINMUX_GPIO(GPIO_FN_CRX0, CRX0_MARK),
+	PINMUX_GPIO(GPIO_FN_CRX0_CRX1, CRX0CRX1_MARK),
+	PINMUX_GPIO(GPIO_FN_CRX0_CRX1_CRX2, CRX0CRX1CRX2_MARK),
+
+	/* DMAC */
+	PINMUX_GPIO(GPIO_FN_TEND0, TEND0_MARK),
+	PINMUX_GPIO(GPIO_FN_DACK0, DACK0_MARK),
+	PINMUX_GPIO(GPIO_FN_DREQ0, DREQ0_MARK),
+	PINMUX_GPIO(GPIO_FN_TEND1, TEND1_MARK),
+	PINMUX_GPIO(GPIO_FN_DACK1, DACK1_MARK),
+	PINMUX_GPIO(GPIO_FN_DREQ1, DREQ1_MARK),
+
+	/* ADC */
+	PINMUX_GPIO(GPIO_FN_ADTRG, ADTRG_MARK),
+
+	/* BSCh */
+	PINMUX_GPIO(GPIO_FN_A25, A25_MARK),
+	PINMUX_GPIO(GPIO_FN_A24, A24_MARK),
+	PINMUX_GPIO(GPIO_FN_A23, A23_MARK),
+	PINMUX_GPIO(GPIO_FN_A22, A22_MARK),
+	PINMUX_GPIO(GPIO_FN_A21, A21_MARK),
+	PINMUX_GPIO(GPIO_FN_A20, A20_MARK),
+	PINMUX_GPIO(GPIO_FN_A19, A19_MARK),
+	PINMUX_GPIO(GPIO_FN_A18, A18_MARK),
+	PINMUX_GPIO(GPIO_FN_A17, A17_MARK),
+	PINMUX_GPIO(GPIO_FN_A16, A16_MARK),
+	PINMUX_GPIO(GPIO_FN_A15, A15_MARK),
+	PINMUX_GPIO(GPIO_FN_A14, A14_MARK),
+	PINMUX_GPIO(GPIO_FN_A13, A13_MARK),
+	PINMUX_GPIO(GPIO_FN_A12, A12_MARK),
+	PINMUX_GPIO(GPIO_FN_A11, A11_MARK),
+	PINMUX_GPIO(GPIO_FN_A10, A10_MARK),
+	PINMUX_GPIO(GPIO_FN_A9, A9_MARK),
+	PINMUX_GPIO(GPIO_FN_A8, A8_MARK),
+	PINMUX_GPIO(GPIO_FN_A7, A7_MARK),
+	PINMUX_GPIO(GPIO_FN_A6, A6_MARK),
+	PINMUX_GPIO(GPIO_FN_A5, A5_MARK),
+	PINMUX_GPIO(GPIO_FN_A4, A4_MARK),
+	PINMUX_GPIO(GPIO_FN_A3, A3_MARK),
+	PINMUX_GPIO(GPIO_FN_A2, A2_MARK),
+	PINMUX_GPIO(GPIO_FN_A1, A1_MARK),
+	PINMUX_GPIO(GPIO_FN_A0, A0_MARK),
+
+	PINMUX_GPIO(GPIO_FN_D15, D15_MARK),
+	PINMUX_GPIO(GPIO_FN_D14, D14_MARK),
+	PINMUX_GPIO(GPIO_FN_D13, D13_MARK),
+	PINMUX_GPIO(GPIO_FN_D12, D12_MARK),
+	PINMUX_GPIO(GPIO_FN_D11, D11_MARK),
+	PINMUX_GPIO(GPIO_FN_D10, D10_MARK),
+	PINMUX_GPIO(GPIO_FN_D9, D9_MARK),
+	PINMUX_GPIO(GPIO_FN_D8, D8_MARK),
+	PINMUX_GPIO(GPIO_FN_D7, D7_MARK),
+	PINMUX_GPIO(GPIO_FN_D6, D6_MARK),
+	PINMUX_GPIO(GPIO_FN_D5, D5_MARK),
+	PINMUX_GPIO(GPIO_FN_D4, D4_MARK),
+	PINMUX_GPIO(GPIO_FN_D3, D3_MARK),
+	PINMUX_GPIO(GPIO_FN_D2, D2_MARK),
+	PINMUX_GPIO(GPIO_FN_D1, D1_MARK),
+	PINMUX_GPIO(GPIO_FN_D0, D0_MARK),
+
+	PINMUX_GPIO(GPIO_FN_BS, BS_MARK),
+	PINMUX_GPIO(GPIO_FN_CS4, CS4_MARK),
+	PINMUX_GPIO(GPIO_FN_CS3, CS3_MARK),
+	PINMUX_GPIO(GPIO_FN_CS2, CS2_MARK),
+	PINMUX_GPIO(GPIO_FN_CS1, CS1_MARK),
+	PINMUX_GPIO(GPIO_FN_CS0, CS0_MARK),
+	PINMUX_GPIO(GPIO_FN_CS5CE1A, CS5CE1A_MARK),
+	PINMUX_GPIO(GPIO_FN_CE2A, CE2A_MARK),
+	PINMUX_GPIO(GPIO_FN_CE2B, CE2B_MARK),
+	PINMUX_GPIO(GPIO_FN_RD, RD_MARK),
+	PINMUX_GPIO(GPIO_FN_RDWR, RDWR_MARK),
+	PINMUX_GPIO(GPIO_FN_WE3ICIOWRAHDQMUU, WE3ICIOWRAHDQMUU_MARK),
+	PINMUX_GPIO(GPIO_FN_WE2ICIORDDQMUL, WE2ICIORDDQMUL_MARK),
+	PINMUX_GPIO(GPIO_FN_WE1DQMUWE, WE1DQMUWE_MARK),
+	PINMUX_GPIO(GPIO_FN_WE0DQML, WE0DQML_MARK),
+	PINMUX_GPIO(GPIO_FN_RAS, RAS_MARK),
+	PINMUX_GPIO(GPIO_FN_CAS, CAS_MARK),
+	PINMUX_GPIO(GPIO_FN_CKE, CKE_MARK),
+	PINMUX_GPIO(GPIO_FN_WAIT, WAIT_MARK),
+	PINMUX_GPIO(GPIO_FN_BREQ, BREQ_MARK),
+	PINMUX_GPIO(GPIO_FN_BACK, BACK_MARK),
+	PINMUX_GPIO(GPIO_FN_IOIS16, IOIS16_MARK),
+
+	/* TMU */
+	PINMUX_GPIO(GPIO_FN_TIOC4D, TIOC4D_MARK),
+	PINMUX_GPIO(GPIO_FN_TIOC4C, TIOC4C_MARK),
+	PINMUX_GPIO(GPIO_FN_TIOC4B, TIOC4B_MARK),
+	PINMUX_GPIO(GPIO_FN_TIOC4A, TIOC4A_MARK),
+	PINMUX_GPIO(GPIO_FN_TIOC3D, TIOC3D_MARK),
+	PINMUX_GPIO(GPIO_FN_TIOC3C, TIOC3C_MARK),
+	PINMUX_GPIO(GPIO_FN_TIOC3B, TIOC3B_MARK),
+	PINMUX_GPIO(GPIO_FN_TIOC3A, TIOC3A_MARK),
+	PINMUX_GPIO(GPIO_FN_TIOC2B, TIOC2B_MARK),
+	PINMUX_GPIO(GPIO_FN_TIOC1B, TIOC1B_MARK),
+	PINMUX_GPIO(GPIO_FN_TIOC2A, TIOC2A_MARK),
+	PINMUX_GPIO(GPIO_FN_TIOC1A, TIOC1A_MARK),
+	PINMUX_GPIO(GPIO_FN_TIOC0D, TIOC0D_MARK),
+	PINMUX_GPIO(GPIO_FN_TIOC0C, TIOC0C_MARK),
+	PINMUX_GPIO(GPIO_FN_TIOC0B, TIOC0B_MARK),
+	PINMUX_GPIO(GPIO_FN_TIOC0A, TIOC0A_MARK),
+	PINMUX_GPIO(GPIO_FN_TCLKD, TCLKD_MARK),
+	PINMUX_GPIO(GPIO_FN_TCLKC, TCLKC_MARK),
+	PINMUX_GPIO(GPIO_FN_TCLKB, TCLKB_MARK),
+	PINMUX_GPIO(GPIO_FN_TCLKA, TCLKA_MARK),
+
+	/* SCIF */
+	PINMUX_GPIO(GPIO_FN_SCK0, SCK0_MARK),
+	PINMUX_GPIO(GPIO_FN_TXD0, TXD0_MARK),
+	PINMUX_GPIO(GPIO_FN_RXD0, RXD0_MARK),
+	PINMUX_GPIO(GPIO_FN_SCK1, SCK1_MARK),
+	PINMUX_GPIO(GPIO_FN_TXD1, TXD1_MARK),
+	PINMUX_GPIO(GPIO_FN_RXD1, RXD1_MARK),
+	PINMUX_GPIO(GPIO_FN_RTS1, RTS1_MARK),
+	PINMUX_GPIO(GPIO_FN_CTS1, CTS1_MARK),
+	PINMUX_GPIO(GPIO_FN_SCK2, SCK2_MARK),
+	PINMUX_GPIO(GPIO_FN_TXD2, TXD2_MARK),
+	PINMUX_GPIO(GPIO_FN_RXD2, RXD2_MARK),
+	PINMUX_GPIO(GPIO_FN_SCK3, SCK3_MARK),
+	PINMUX_GPIO(GPIO_FN_TXD3, TXD3_MARK),
+	PINMUX_GPIO(GPIO_FN_RXD3, RXD3_MARK),
+	PINMUX_GPIO(GPIO_FN_SCK4, SCK4_MARK),
+	PINMUX_GPIO(GPIO_FN_TXD4, TXD4_MARK),
+	PINMUX_GPIO(GPIO_FN_RXD4, RXD4_MARK),
+	PINMUX_GPIO(GPIO_FN_SCK5, SCK5_MARK),
+	PINMUX_GPIO(GPIO_FN_TXD5, TXD5_MARK),
+	PINMUX_GPIO(GPIO_FN_RXD5, RXD5_MARK),
+	PINMUX_GPIO(GPIO_FN_RTS5, RTS5_MARK),
+	PINMUX_GPIO(GPIO_FN_CTS5, CTS5_MARK),
+	PINMUX_GPIO(GPIO_FN_SCK6, SCK6_MARK),
+	PINMUX_GPIO(GPIO_FN_TXD6, TXD6_MARK),
+	PINMUX_GPIO(GPIO_FN_RXD6, RXD6_MARK),
+	PINMUX_GPIO(GPIO_FN_SCK7, SCK7_MARK),
+	PINMUX_GPIO(GPIO_FN_TXD7, TXD7_MARK),
+	PINMUX_GPIO(GPIO_FN_RXD7, RXD7_MARK),
+	PINMUX_GPIO(GPIO_FN_RTS7, RTS7_MARK),
+	PINMUX_GPIO(GPIO_FN_CTS7, CTS7_MARK),
+
+	/* RSPI */
+	PINMUX_GPIO(GPIO_FN_RSPCK0_PJ16, RSPCK0_PJ16_MARK),
+	PINMUX_GPIO(GPIO_FN_SSL00_PJ17, SSL00_PJ17_MARK),
+	PINMUX_GPIO(GPIO_FN_MOSI0_PJ18, MOSI0_PJ18_MARK),
+	PINMUX_GPIO(GPIO_FN_MISO0_PJ19, MISO0_PJ19_MARK),
+	PINMUX_GPIO(GPIO_FN_RSPCK0_PB17, RSPCK0_PB17_MARK),
+	PINMUX_GPIO(GPIO_FN_SSL00_PB18, SSL00_PB18_MARK),
+	PINMUX_GPIO(GPIO_FN_MOSI0_PB19, MOSI0_PB19_MARK),
+	PINMUX_GPIO(GPIO_FN_MISO0_PB20, MISO0_PB20_MARK),
+	PINMUX_GPIO(GPIO_FN_RSPCK1, RSPCK1_MARK),
+	PINMUX_GPIO(GPIO_FN_MOSI1, MOSI1_MARK),
+	PINMUX_GPIO(GPIO_FN_MISO1, MISO1_MARK),
+	PINMUX_GPIO(GPIO_FN_SSL10, SSL10_MARK),
+
+	/* IIC3 */
+	PINMUX_GPIO(GPIO_FN_SCL0, SCL0_MARK),
+	PINMUX_GPIO(GPIO_FN_SCL1, SCL1_MARK),
+	PINMUX_GPIO(GPIO_FN_SCL2, SCL2_MARK),
+	PINMUX_GPIO(GPIO_FN_SDA0, SDA0_MARK),
+	PINMUX_GPIO(GPIO_FN_SDA1, SDA1_MARK),
+	PINMUX_GPIO(GPIO_FN_SDA2, SDA2_MARK),
+
+	/* SSI */
+	PINMUX_GPIO(GPIO_FN_SSISCK0, SSISCK0_MARK),
+	PINMUX_GPIO(GPIO_FN_SSIWS0, SSIWS0_MARK),
+	PINMUX_GPIO(GPIO_FN_SSITXD0, SSITXD0_MARK),
+	PINMUX_GPIO(GPIO_FN_SSIRXD0, SSIRXD0_MARK),
+	PINMUX_GPIO(GPIO_FN_SSIWS1, SSIWS1_MARK),
+	PINMUX_GPIO(GPIO_FN_SSIWS2, SSIWS2_MARK),
+	PINMUX_GPIO(GPIO_FN_SSIWS3, SSIWS3_MARK),
+	PINMUX_GPIO(GPIO_FN_SSISCK1, SSISCK1_MARK),
+	PINMUX_GPIO(GPIO_FN_SSISCK2, SSISCK2_MARK),
+	PINMUX_GPIO(GPIO_FN_SSISCK3, SSISCK3_MARK),
+	PINMUX_GPIO(GPIO_FN_SSIDATA1, SSIDATA1_MARK),
+	PINMUX_GPIO(GPIO_FN_SSIDATA2, SSIDATA2_MARK),
+	PINMUX_GPIO(GPIO_FN_SSIDATA3, SSIDATA3_MARK),
+	PINMUX_GPIO(GPIO_FN_AUDIO_CLK, AUDIO_CLK_MARK),
+	PINMUX_GPIO(GPIO_FN_AUDIO_XOUT, AUDIO_XOUT_MARK),
+
+	/* SIOF */ /* NOTE Shares AUDIO_CLK with SSI */
+	PINMUX_GPIO(GPIO_FN_SIOFTXD, SIOFTXD_MARK),
+	PINMUX_GPIO(GPIO_FN_SIOFRXD, SIOFRXD_MARK),
+	PINMUX_GPIO(GPIO_FN_SIOFSYNC, SIOFSYNC_MARK),
+	PINMUX_GPIO(GPIO_FN_SIOFSCK, SIOFSCK_MARK),
+
+	/* SPDIF */ /* NOTE Shares AUDIO_CLK with SSI */
+	PINMUX_GPIO(GPIO_FN_SPDIF_IN, SPDIF_IN_MARK),
+	PINMUX_GPIO(GPIO_FN_SPDIF_OUT, SPDIF_OUT_MARK),
+
+	/* NANDFMC */ /* NOTE Controller is not available in boot mode 0 */
+	PINMUX_GPIO(GPIO_FN_FCE, FCE_MARK),
+	PINMUX_GPIO(GPIO_FN_FRB, FRB_MARK),
+
+	/* VDC3 */
+	PINMUX_GPIO(GPIO_FN_DV_CLK, DV_CLK_MARK),
+	PINMUX_GPIO(GPIO_FN_DV_VSYNC, DV_VSYNC_MARK),
+	PINMUX_GPIO(GPIO_FN_DV_HSYNC, DV_HSYNC_MARK),
+
+	PINMUX_GPIO(GPIO_FN_DV_DATA23, DV_DATA23_MARK),
+	PINMUX_GPIO(GPIO_FN_DV_DATA22, DV_DATA22_MARK),
+	PINMUX_GPIO(GPIO_FN_DV_DATA21, DV_DATA21_MARK),
+	PINMUX_GPIO(GPIO_FN_DV_DATA20, DV_DATA20_MARK),
+	PINMUX_GPIO(GPIO_FN_DV_DATA19, DV_DATA19_MARK),
+	PINMUX_GPIO(GPIO_FN_DV_DATA18, DV_DATA18_MARK),
+	PINMUX_GPIO(GPIO_FN_DV_DATA17, DV_DATA17_MARK),
+	PINMUX_GPIO(GPIO_FN_DV_DATA16, DV_DATA16_MARK),
+	PINMUX_GPIO(GPIO_FN_DV_DATA15, DV_DATA15_MARK),
+	PINMUX_GPIO(GPIO_FN_DV_DATA14, DV_DATA14_MARK),
+	PINMUX_GPIO(GPIO_FN_DV_DATA13, DV_DATA13_MARK),
+	PINMUX_GPIO(GPIO_FN_DV_DATA12, DV_DATA12_MARK),
+	PINMUX_GPIO(GPIO_FN_DV_DATA11, DV_DATA11_MARK),
+	PINMUX_GPIO(GPIO_FN_DV_DATA10, DV_DATA10_MARK),
+	PINMUX_GPIO(GPIO_FN_DV_DATA9, DV_DATA9_MARK),
+	PINMUX_GPIO(GPIO_FN_DV_DATA8, DV_DATA8_MARK),
+	PINMUX_GPIO(GPIO_FN_DV_DATA7, DV_DATA7_MARK),
+	PINMUX_GPIO(GPIO_FN_DV_DATA6, DV_DATA6_MARK),
+	PINMUX_GPIO(GPIO_FN_DV_DATA5, DV_DATA5_MARK),
+	PINMUX_GPIO(GPIO_FN_DV_DATA4, DV_DATA4_MARK),
+	PINMUX_GPIO(GPIO_FN_DV_DATA3, DV_DATA3_MARK),
+	PINMUX_GPIO(GPIO_FN_DV_DATA2, DV_DATA2_MARK),
+	PINMUX_GPIO(GPIO_FN_DV_DATA1, DV_DATA1_MARK),
+	PINMUX_GPIO(GPIO_FN_DV_DATA0, DV_DATA0_MARK),
+
+	PINMUX_GPIO(GPIO_FN_LCD_CLK, LCD_CLK_MARK),
+	PINMUX_GPIO(GPIO_FN_LCD_EXTCLK, LCD_EXTCLK_MARK),
+	PINMUX_GPIO(GPIO_FN_LCD_VSYNC, LCD_VSYNC_MARK),
+	PINMUX_GPIO(GPIO_FN_LCD_HSYNC, LCD_HSYNC_MARK),
+	PINMUX_GPIO(GPIO_FN_LCD_DE, LCD_DE_MARK),
+
+	PINMUX_GPIO(GPIO_FN_LCD_DATA23, LCD_DATA23_MARK),
+	PINMUX_GPIO(GPIO_FN_LCD_DATA22, LCD_DATA22_MARK),
+	PINMUX_GPIO(GPIO_FN_LCD_DATA21, LCD_DATA21_MARK),
+	PINMUX_GPIO(GPIO_FN_LCD_DATA20, LCD_DATA20_MARK),
+	PINMUX_GPIO(GPIO_FN_LCD_DATA19, LCD_DATA19_MARK),
+	PINMUX_GPIO(GPIO_FN_LCD_DATA18, LCD_DATA18_MARK),
+	PINMUX_GPIO(GPIO_FN_LCD_DATA17, LCD_DATA17_MARK),
+	PINMUX_GPIO(GPIO_FN_LCD_DATA16, LCD_DATA16_MARK),
+	PINMUX_GPIO(GPIO_FN_LCD_DATA15, LCD_DATA15_MARK),
+	PINMUX_GPIO(GPIO_FN_LCD_DATA14, LCD_DATA14_MARK),
+	PINMUX_GPIO(GPIO_FN_LCD_DATA13, LCD_DATA13_MARK),
+	PINMUX_GPIO(GPIO_FN_LCD_DATA12, LCD_DATA12_MARK),
+	PINMUX_GPIO(GPIO_FN_LCD_DATA11, LCD_DATA11_MARK),
+	PINMUX_GPIO(GPIO_FN_LCD_DATA10, LCD_DATA10_MARK),
+	PINMUX_GPIO(GPIO_FN_LCD_DATA9, LCD_DATA9_MARK),
+	PINMUX_GPIO(GPIO_FN_LCD_DATA8, LCD_DATA8_MARK),
+	PINMUX_GPIO(GPIO_FN_LCD_DATA7, LCD_DATA7_MARK),
+	PINMUX_GPIO(GPIO_FN_LCD_DATA6, LCD_DATA6_MARK),
+	PINMUX_GPIO(GPIO_FN_LCD_DATA5, LCD_DATA5_MARK),
+	PINMUX_GPIO(GPIO_FN_LCD_DATA4, LCD_DATA4_MARK),
+	PINMUX_GPIO(GPIO_FN_LCD_DATA3, LCD_DATA3_MARK),
+	PINMUX_GPIO(GPIO_FN_LCD_DATA2, LCD_DATA2_MARK),
+	PINMUX_GPIO(GPIO_FN_LCD_DATA1, LCD_DATA1_MARK),
+	PINMUX_GPIO(GPIO_FN_LCD_DATA0, LCD_DATA0_MARK),
+
+	PINMUX_GPIO(GPIO_FN_LCD_M_DISP, LCD_M_DISP_MARK),
+};
+
+static struct pinmux_cfg_reg pinmux_config_regs[] = {
+	/* "name" addr register_size Field_Width */
+
+	/* where Field_Width is 1 for single mode registers or 4 for upto 16
+	   mode registers and modes are described in assending order [0..16] */
+
+	{ PINMUX_CFG_REG("PAIOR0", 0xfffe3812, 16, 1) {
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, PA1_IN, PA1_OUT,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, PA0_IN, PA0_OUT }
+	},
+	{ PINMUX_CFG_REG("PBCR5", 0xfffe3824, 16, 4) {
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+
+		PB22MD_000, PB22MD_001, PB22MD_010, PB22MD_011,
+		PB22MD_100, PB22MD_101, PB22MD_110, PB22MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PB21MD_00, PB21MD_01, PB21MD_10, PB21MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PB20MD_000, PB20MD_001, PB20MD_010, PB20MD_011,
+		PB20MD_100, PB20MD_101, PB20MD_110, PB20MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG("PBCR4", 0xfffe3826, 16, 4) {
+		PB19MD_000, PB19MD_001, PB19MD_010, PB19MD_011,
+		PB19MD_100, PB19MD_101, PB19MD_110, PB19MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PB18MD_000, PB18MD_001, PB18MD_010, PB18MD_011,
+		PB18MD_100, PB18MD_101, PB18MD_110, PB18MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PB17MD_000, PB17MD_001, PB17MD_010, PB17MD_011,
+		PB17MD_100, PB17MD_101, PB17MD_110, PB17MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PB16MD_000, PB16MD_001, PB16MD_010, PB16MD_011,
+		PB16MD_100, PB16MD_101, PB16MD_110, PB16MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG("PBCR3", 0xfffe3828, 16, 4) {
+		PB15MD_000, PB15MD_001, PB15MD_010, PB15MD_011,
+		PB15MD_100, PB15MD_101, PB15MD_110, PB15MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PB14MD_000, PB14MD_001, PB14MD_010, PB14MD_011,
+		PB14MD_100, PB14MD_101, PB14MD_110, PB14MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PB13MD_000, PB13MD_001, PB13MD_010, PB13MD_011,
+		PB13MD_100, PB13MD_101, PB13MD_110, PB13MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PB12MD_00, PB12MD_01, PB12MD_10, PB12MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG("PBCR2", 0xfffe382a, 16, 4) {
+		PB11MD_00, PB11MD_01, PB11MD_10, PB11MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PB10MD_00, PB10MD_01, PB10MD_10, PB10MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PB9MD_00, PB9MD_01, PB9MD_10, PB9MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PB8MD_00, PB8MD_01, PB8MD_10, PB8MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG("PBCR1", 0xfffe382c, 16, 4) {
+		PB7MD_00, PB7MD_01, PB7MD_10, PB7MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PB6MD_00, PB6MD_01, PB6MD_10, PB6MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PB5MD_00, PB5MD_01, PB5MD_10, PB5MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PB4MD_00, PB4MD_01, PB4MD_10, PB4MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG("PBCR0", 0xfffe382e, 16, 4) {
+		PB3MD_00, PB3MD_01, PB3MD_10, PB3MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PB2MD_00, PB2MD_01, PB2MD_10, PB2MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PB1MD_00, PB1MD_01, PB1MD_10, PB1MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+
+	{ PINMUX_CFG_REG("PBIOR1", 0xfffe3830, 16, 1) {
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0,
+		PB22_IN, PB22_OUT,
+		PB21_IN, PB21_OUT,
+		PB20_IN, PB20_OUT,
+		PB19_IN, PB19_OUT,
+		PB18_IN, PB18_OUT,
+		PB17_IN, PB17_OUT,
+		PB16_IN, PB16_OUT }
+	},
+	{ PINMUX_CFG_REG("PBIOR0", 0xfffe3832, 16, 1) {
+		PB15_IN, PB15_OUT,
+		PB14_IN, PB14_OUT,
+		PB13_IN, PB13_OUT,
+		PB12_IN, PB12_OUT,
+		PB11_IN, PB11_OUT,
+		PB10_IN, PB10_OUT,
+		PB9_IN, PB9_OUT,
+		PB8_IN, PB8_OUT,
+		PB7_IN, PB7_OUT,
+		PB6_IN, PB6_OUT,
+		PB5_IN, PB5_OUT,
+		PB4_IN, PB4_OUT,
+		PB3_IN, PB3_OUT,
+		PB2_IN, PB2_OUT,
+		PB1_IN, PB1_OUT,
+		0, 0 }
+	},
+
+	{ PINMUX_CFG_REG("PCCR2", 0xfffe384a, 16, 4) {
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+
+		PC8MD_000, PC8MD_001, PC8MD_010, PC8MD_011,
+		PC8MD_100, PC8MD_101, PC8MD_110, PC8MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG("PCCR1", 0xfffe384c, 16, 4) {
+		PC7MD_000, PC7MD_001, PC7MD_010, PC7MD_011,
+		PC7MD_100, PC7MD_101, PC7MD_110, PC7MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PC6MD_000, PC6MD_001, PC6MD_010, PC6MD_011,
+		PC6MD_100, PC6MD_101, PC6MD_110, PC6MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PC5MD_000, PC5MD_001, PC5MD_010, PC5MD_011,
+		PC5MD_100, PC5MD_101, PC5MD_110, PC5MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PC4MD_00, PC4MD_01, PC4MD_10, PC4MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG("PCCR0", 0xfffe384e, 16, 4) {
+		PC3MD_00, PC3MD_01, PC3MD_10, PC3MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PC2MD_00, PC2MD_01, PC2MD_10, PC2MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PC1MD_0, PC1MD_1, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PC0MD_0, PC0MD_1, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+
+	{ PINMUX_CFG_REG("PCIOR0", 0xfffe3852, 16, 1) {
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+		PC8_IN, PC8_OUT,
+		PC7_IN, PC7_OUT,
+		PC6_IN, PC6_OUT,
+		PC5_IN, PC5_OUT,
+		PC4_IN, PC4_OUT,
+		PC3_IN, PC3_OUT,
+		PC2_IN, PC2_OUT,
+		PC1_IN, PC1_OUT,
+		PC0_IN, PC0_OUT }
+	},
+
+	{ PINMUX_CFG_REG("PDCR3", 0xfffe3868, 16, 4) {
+		PD15MD_00, PD15MD_01, PD15MD_10, PD15MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PD14MD_00, PD14MD_01, PD14MD_10, PD14MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PD13MD_00, PD13MD_01, PD13MD_10, PD13MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PD12MD_00, PD12MD_01, PD12MD_10, PD12MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG("PDCR2", 0xfffe386a, 16, 4) {
+		PD11MD_00, PD11MD_01, PD11MD_10, PD11MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PD10MD_00, PD10MD_01, PD10MD_10, PD10MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PD9MD_00, PD9MD_01, PD9MD_10, PD9MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PD8MD_00, PD8MD_01, PD8MD_10, PD8MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG("PDCR1", 0xfffe386c, 16, 4) {
+		PD7MD_00, PD7MD_01, PD7MD_10, PD7MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PD6MD_00, PD6MD_01, PD6MD_10, PD6MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PD5MD_00, PD5MD_01, PD5MD_10, PD5MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PD4MD_00, PD4MD_01, PD4MD_10, PD4MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG("PDCR0", 0xfffe386e, 16, 4) {
+		PD3MD_00, PD3MD_01, PD3MD_10, PD3MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PD2MD_00, PD2MD_01, PD2MD_10, PD2MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PD1MD_00, PD1MD_01, PD1MD_10, PD1MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PD0MD_00, PD0MD_01, PD0MD_10, PD0MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+
+	{ PINMUX_CFG_REG("PDIOR0", 0xfffe3872, 16, 1) {
+		PD15_IN, PD15_OUT,
+		PD14_IN, PD14_OUT,
+		PD13_IN, PD13_OUT,
+		PD12_IN, PD12_OUT,
+		PD11_IN, PD11_OUT,
+		PD10_IN, PD10_OUT,
+		PD9_IN, PD9_OUT,
+		PD8_IN, PD8_OUT,
+		PD7_IN, PD7_OUT,
+		PD6_IN, PD6_OUT,
+		PD5_IN, PD5_OUT,
+		PD4_IN, PD4_OUT,
+		PD3_IN, PD3_OUT,
+		PD2_IN, PD2_OUT,
+		PD1_IN, PD1_OUT,
+		PD0_IN, PD0_OUT }
+	},
+
+	{ PINMUX_CFG_REG("PECR1", 0xfffe388c, 16, 4) {
+		PE7MD_00, PE7MD_01, PE7MD_10, PE7MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PE6MD_00, PE6MD_01, PE6MD_10, PE6MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PE5MD_00, PE5MD_01, PE5MD_10, PE5MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PE4MD_00, PE4MD_01, PE4MD_10, PE4MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG("PECR0", 0xfffe388e, 16, 4) {
+		PE3MD_000, PE3MD_001, PE3MD_010, PE3MD_011,
+		PE3MD_100, PE3MD_101, PE3MD_110, PE3MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PE2MD_000, PE2MD_001, PE2MD_010, PE2MD_011,
+		PE2MD_100, PE2MD_101, PE2MD_110, PE2MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PE1MD_000, PE1MD_001, PE1MD_010, PE1MD_011,
+		PE1MD_100, PE1MD_101, PE1MD_110, PE1MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PE0MD_00, PE0MD_01, PE0MD_10, PE0MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG("PEIOR0", 0xfffe3892, 16, 1) {
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PE7_IN, PE7_OUT,
+		PE6_IN, PE6_OUT,
+		PE5_IN, PE5_OUT,
+		PE4_IN, PE4_OUT,
+		PE3_IN, PE3_OUT,
+		PE2_IN, PE2_OUT,
+		PE1_IN, PE1_OUT,
+		PE0_IN, PE0_OUT }
+	},
+
+	{ PINMUX_CFG_REG("PFCR6", 0xfffe38a2, 16, 4) {
+		PF23MD_000, PF23MD_001, PF23MD_010, PF23MD_011,
+		PF23MD_100, PF23MD_101, PF23MD_110, PF23MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PF22MD_000, PF22MD_001, PF22MD_010, PF22MD_011,
+		PF22MD_100, PF22MD_101, PF22MD_110, PF22MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PF21MD_000, PF21MD_001, PF21MD_010, PF21MD_011,
+		PF21MD_100, PF21MD_101, PF21MD_110, PF21MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PF20MD_000, PF20MD_001, PF20MD_010, PF20MD_011,
+		PF20MD_100, PF20MD_101, PF20MD_110, PF20MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG("PFCR5", 0xfffe38a4, 16, 4) {
+		PF19MD_000, PF19MD_001, PF19MD_010, PF19MD_011,
+		PF19MD_100, PF19MD_101, PF19MD_110, PF19MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PF18MD_000, PF18MD_001, PF18MD_010, PF18MD_011,
+		PF18MD_100, PF18MD_101, PF18MD_110, PF18MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PF17MD_000, PF17MD_001, PF17MD_010, PF17MD_011,
+		PF17MD_100, PF17MD_101, PF17MD_110, PF17MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PF16MD_000, PF16MD_001, PF16MD_010, PF16MD_011,
+		PF16MD_100, PF16MD_101, PF16MD_110, PF16MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG("PFCR4", 0xfffe38a6, 16, 4) {
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+
+		PF15MD_000, PF15MD_001, PF15MD_010, PF15MD_011,
+		PF15MD_100, PF15MD_101, PF15MD_110, PF15MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG("PFCR3", 0xfffe38a8, 16, 4) {
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+
+		PF14MD_000, PF14MD_001, PF14MD_010, PF14MD_011,
+		PF14MD_100, PF14MD_101, PF14MD_110, PF14MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PF13MD_000, PF13MD_001, PF13MD_010, PF13MD_011,
+		PF13MD_100, PF13MD_101, PF13MD_110, PF13MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PF12MD_000, PF12MD_001, PF12MD_010, PF12MD_011,
+		PF12MD_100, PF12MD_101, PF12MD_110, PF12MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG("PFCR2", 0xfffe38aa, 16, 4) {
+		PF11MD_000, PF11MD_001, PF11MD_010, PF11MD_011,
+		PF11MD_100, PF11MD_101, PF11MD_110, PF11MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PF10MD_000, PF10MD_001, PF10MD_010, PF10MD_011,
+		PF10MD_100, PF10MD_101, PF10MD_110, PF10MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PF9MD_000, PF9MD_001, PF9MD_010, PF9MD_011,
+		PF9MD_100, PF9MD_101, PF9MD_110, PF9MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PF8MD_000, PF8MD_001, PF8MD_010, PF8MD_011,
+		PF8MD_100, PF8MD_101, PF8MD_110, PF8MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG("PFCR1", 0xfffe38ac, 16, 4) {
+		PF7MD_000, PF7MD_001, PF7MD_010, PF7MD_011,
+		PF7MD_100, PF7MD_101, PF7MD_110, PF7MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PF6MD_000, PF6MD_001, PF6MD_010, PF6MD_011,
+		PF6MD_100, PF6MD_101, PF6MD_110, PF6MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PF5MD_000, PF5MD_001, PF5MD_010, PF5MD_011,
+		PF5MD_100, PF5MD_101, PF5MD_110, PF5MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PF4MD_000, PF4MD_001, PF4MD_010, PF4MD_011,
+		PF4MD_100, PF4MD_101, PF4MD_110, PF4MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG("PFCR0", 0xfffe38ae, 16, 4) {
+		PF3MD_000, PF3MD_001, PF3MD_010, PF3MD_011,
+		PF3MD_100, PF3MD_101, PF3MD_110, PF3MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PF2MD_000, PF2MD_001, PF2MD_010, PF2MD_011,
+		PF2MD_100, PF2MD_101, PF2MD_110, PF2MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PF1MD_000, PF1MD_001, PF1MD_010, PF1MD_011,
+		PF1MD_100, PF1MD_101, PF1MD_110, PF1MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PF0MD_000, PF0MD_001, PF0MD_010, PF0MD_011,
+		PF0MD_100, PF0MD_101, PF0MD_110, PF0MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+
+	{ PINMUX_CFG_REG("PFIOR1", 0xfffe38b0, 16, 1) {
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PF23_IN, PF23_OUT,
+		PF22_IN, PF22_OUT,
+		PF21_IN, PF21_OUT,
+		PF20_IN, PF20_OUT,
+		PF19_IN, PF19_OUT,
+		PF18_IN, PF18_OUT,
+		PF17_IN, PF17_OUT,
+		PF16_IN, PF16_OUT }
+	},
+	{ PINMUX_CFG_REG("PFIOR0", 0xfffe38b2, 16, 1) {
+		PF15_IN, PF15_OUT,
+		PF14_IN, PF14_OUT,
+		PF13_IN, PF13_OUT,
+		PF12_IN, PF12_OUT,
+		PF11_IN, PF11_OUT,
+		PF10_IN, PF10_OUT,
+		PF9_IN, PF9_OUT,
+		PF8_IN, PF8_OUT,
+		PF7_IN, PF7_OUT,
+		PF6_IN, PF6_OUT,
+		PF5_IN, PF5_OUT,
+		PF4_IN, PF4_OUT,
+		PF3_IN, PF3_OUT,
+		PF2_IN, PF2_OUT,
+		PF1_IN, PF1_OUT,
+		PF0_IN, PF0_OUT }
+	},
+
+	{ PINMUX_CFG_REG("PGCR6", 0xfffe38c2, 16, 4) {
+		PG27MD_00, PG27MD_01, PG27MD_10, PG27MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PG26MD_00, PG26MD_01, PG26MD_10, PG26MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PG25MD_00, PG25MD_01, PG25MD_10, PG25MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PG24MD_00, PG24MD_01, PG24MD_10, PG24MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG("PGCR5", 0xfffe38c4, 16, 4) {
+		PG23MD_000, PG23MD_001, PG23MD_010, PG23MD_011,
+		PG23MD_100, PG23MD_101, PG23MD_110, PG23MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PG22MD_000, PG22MD_001, PG22MD_010, PG22MD_011,
+		PG22MD_100, PG22MD_101, PG22MD_110, PG22MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PG21MD_000, PG21MD_001, PG21MD_010, PG21MD_011,
+		PG21MD_100, PG21MD_101, PG21MD_110, PG21MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PG20MD_000, PG20MD_001, PG20MD_010, PG20MD_011,
+		PG20MD_100, PG20MD_101, PG20MD_110, PG20MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG("PGCR4", 0xfffe38c6, 16, 4) {
+		PG19MD_000, PG19MD_001, PG19MD_010, PG19MD_011,
+		PG19MD_100, PG19MD_101, PG19MD_110, PG19MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PG18MD_000, PG18MD_001, PG18MD_010, PG18MD_011,
+		PG18MD_100, PG18MD_101, PG18MD_110, PG18MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PG17MD_00, PG17MD_01, PG17MD_10, PG17MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PG16MD_00, PG16MD_01, PG16MD_10, PG16MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG("PGCR3", 0xfffe38c8, 16, 4) {
+		PG15MD_00, PG15MD_01, PG15MD_10, PG15MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PG14MD_00, PG14MD_01, PG14MD_10, PG14MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PG13MD_00, PG13MD_01, PG13MD_10, PG13MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PG12MD_00, PG12MD_01, PG12MD_10, PG12MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG("PGCR2", 0xfffe38ca, 16, 4) {
+		PG11MD_000, PG11MD_001, PG11MD_010, PG11MD_011,
+		PG11MD_100, PG11MD_101, PG11MD_110, PG11MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PG10MD_000, PG10MD_001, PG10MD_010, PG10MD_011,
+		PG10MD_100, PG10MD_101, PG10MD_110, PG10MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PG9MD_000, PG9MD_001, PG9MD_010, PG9MD_011,
+		PG9MD_100, PG9MD_101, PG9MD_110, PG9MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PG8MD_000, PG8MD_001, PG8MD_010, PG8MD_011,
+		PG8MD_100, PG8MD_101, PG8MD_110, PG8MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+
+	{ PINMUX_CFG_REG("PGCR1", 0xfffe38cc, 16, 4) {
+		PG7MD_000, PG7MD_001, PG7MD_010, PG7MD_011,
+		PG7MD_100, PG7MD_101, PG7MD_110, PG7MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PG6MD_000, PG6MD_001, PG6MD_010, PG6MD_011,
+		PG6MD_100, PG6MD_101, PG6MD_110, PG6MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PG5MD_000, PG5MD_001, PG5MD_010, PG5MD_011,
+		PG5MD_100, PG5MD_101, PG5MD_110, PG5MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PG4MD_000, PG4MD_001, PG4MD_010, PG4MD_011,
+		PG4MD_100, PG4MD_101, PG4MD_110, PG4MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG("PGCR0", 0xfffe38ce, 16, 4) {
+		PG3MD_000, PG3MD_001, PG3MD_010, PG3MD_011,
+		PG3MD_100, PG3MD_101, PG3MD_110, PG3MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PG2MD_000, PG2MD_001, PG2MD_010, PG2MD_011,
+		PG2MD_100, PG2MD_101, PG2MD_110, PG2MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PG1MD_000, PG1MD_001, PG1MD_010, PG1MD_011,
+		PG1MD_100, PG1MD_101, PG1MD_110, PG1MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PG0MD_000, PG0MD_001, PG0MD_010, PG0MD_011,
+		PG0MD_100, PG0MD_101, PG0MD_110, PG0MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+
+	{ PINMUX_CFG_REG("PGIOR1", 0xfffe38d0, 16, 1) {
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PG27_IN, PG27_OUT,
+		PG26_IN, PG26_OUT,
+		PG25_IN, PG25_OUT,
+		PG24_IN, PG24_OUT,
+		PG23_IN, PG23_OUT,
+		PG22_IN, PG22_OUT,
+		PG21_IN, PG21_OUT,
+		PG20_IN, PG20_OUT,
+		PG19_IN, PG19_OUT,
+		PG18_IN, PG18_OUT,
+		PG17_IN, PG17_OUT,
+		PG16_IN, PG16_OUT }
+	},
+	{ PINMUX_CFG_REG("PGIOR0", 0xfffe38d2, 16, 1) {
+		PG15_IN, PG15_OUT,
+		PG14_IN, PG14_OUT,
+		PG13_IN, PG13_OUT,
+		PG12_IN, PG12_OUT,
+		PG11_IN, PG11_OUT,
+		PG10_IN, PG10_OUT,
+		PG9_IN, PG9_OUT,
+		PG8_IN, PG8_OUT,
+		PG7_IN, PG7_OUT,
+		PG6_IN, PG6_OUT,
+		PG5_IN, PG5_OUT,
+		PG4_IN, PG4_OUT,
+		PG3_IN, PG3_OUT,
+		PG2_IN, PG2_OUT,
+		PG1_IN, PG1_OUT,
+		PG0_IN, PG0_OUT }
+	},
+
+	{ PINMUX_CFG_REG("PHCR1", 0xfffe38ec, 16, 4) {
+		PH7MD_00, PH7MD_01, PH7MD_10, PH7MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PH6MD_00, PH6MD_01, PH6MD_10, PH6MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PH5MD_00, PH5MD_01, PH5MD_10, PH5MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PH4MD_00, PH4MD_01, PH4MD_10, PH4MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+
+	{ PINMUX_CFG_REG("PHCR0", 0xfffe38ee, 16, 4) {
+		PH3MD_00, PH3MD_01, PH3MD_10, PH3MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PH2MD_00, PH2MD_01, PH2MD_10, PH2MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PH1MD_00, PH1MD_01, PH1MD_10, PH1MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PH0MD_00, PH0MD_01, PH0MD_10, PH0MD_11, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+
+	{ PINMUX_CFG_REG("PJCR7", 0xfffe3900, 16, 4) {
+		PJ31MD_0, PJ31MD_1, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PJ30MD_000, PJ30MD_001, PJ30MD_010, PJ30MD_011,
+		PJ30MD_100, PJ30MD_101, PJ30MD_110, PJ30MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PJ29MD_000, PJ29MD_001, PJ29MD_010, PJ29MD_011,
+		PJ29MD_100, PJ29MD_101, PJ29MD_110, PJ29MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PJ28MD_000, PJ28MD_001, PJ28MD_010, PJ28MD_011,
+		PJ28MD_100, PJ28MD_101, PJ28MD_110, PJ28MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG("PJCR6", 0xfffe3902, 16, 4) {
+		PJ27MD_000, PJ27MD_001, PJ27MD_010, PJ27MD_011,
+		PJ27MD_100, PJ27MD_101, PJ27MD_110, PJ27MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PJ26MD_000, PJ26MD_001, PJ26MD_010, PJ26MD_011,
+		PJ26MD_100, PJ26MD_101, PJ26MD_110, PJ26MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PJ25MD_000, PJ25MD_001, PJ25MD_010, PJ25MD_011,
+		PJ25MD_100, PJ25MD_101, PJ25MD_110, PJ25MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PJ24MD_000, PJ24MD_001, PJ24MD_010, PJ24MD_011,
+		PJ24MD_100, PJ24MD_101, PJ24MD_110, PJ24MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG("PJCR5", 0xfffe3904, 16, 4) {
+		PJ23MD_000, PJ23MD_001, PJ23MD_010, PJ23MD_011,
+		PJ23MD_100, PJ23MD_101, PJ23MD_110, PJ23MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PJ22MD_000, PJ22MD_001, PJ22MD_010, PJ22MD_011,
+		PJ22MD_100, PJ22MD_101, PJ22MD_110, PJ22MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PJ21MD_000, PJ21MD_001, PJ21MD_010, PJ21MD_011,
+		PJ21MD_100, PJ21MD_101, PJ21MD_110, PJ21MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PJ20MD_000, PJ20MD_001, PJ20MD_010, PJ20MD_011,
+		PJ20MD_100, PJ20MD_101, PJ20MD_110, PJ20MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG("PJCR4", 0xfffe3906, 16, 4) {
+		PJ19MD_000, PJ19MD_001, PJ19MD_010, PJ19MD_011,
+		PJ19MD_100, PJ19MD_101, PJ19MD_110, PJ19MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PJ18MD_000, PJ18MD_001, PJ18MD_010, PJ18MD_011,
+		PJ18MD_100, PJ18MD_101, PJ18MD_110, PJ18MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PJ17MD_000, PJ17MD_001, PJ17MD_010, PJ17MD_011,
+		PJ17MD_100, PJ17MD_101, PJ17MD_110, PJ17MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PJ16MD_000, PJ16MD_001, PJ16MD_010, PJ16MD_011,
+		PJ16MD_100, PJ16MD_101, PJ16MD_110, PJ16MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG("PJCR3", 0xfffe3908, 16, 4) {
+		PJ15MD_000, PJ15MD_001, PJ15MD_010, PJ15MD_011,
+		PJ15MD_100, PJ15MD_101, PJ15MD_110, PJ15MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PJ14MD_000, PJ14MD_001, PJ14MD_010, PJ14MD_011,
+		PJ14MD_100, PJ14MD_101, PJ14MD_110, PJ14MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PJ13MD_000, PJ13MD_001, PJ13MD_010, PJ13MD_011,
+		PJ13MD_100, PJ13MD_101, PJ13MD_110, PJ13MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PJ12MD_000, PJ12MD_001, PJ12MD_010, PJ12MD_011,
+		PJ12MD_100, PJ12MD_101, PJ12MD_110, PJ12MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG("PJCR2", 0xfffe390a, 16, 4) {
+		PJ11MD_000, PJ11MD_001, PJ11MD_010, PJ11MD_011,
+		PJ11MD_100, PJ11MD_101, PJ11MD_110, PJ11MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PJ10MD_000, PJ10MD_001, PJ10MD_010, PJ10MD_011,
+		PJ10MD_100, PJ10MD_101, PJ10MD_110, PJ10MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PJ9MD_000, PJ9MD_001, PJ9MD_010, PJ9MD_011,
+		PJ9MD_100, PJ9MD_101, PJ9MD_110, PJ9MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PJ8MD_000, PJ8MD_001, PJ8MD_010, PJ8MD_011,
+		PJ8MD_100, PJ8MD_101, PJ8MD_110, PJ8MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG("PJCR1", 0xfffe390c, 16, 4) {
+		PJ7MD_000, PJ7MD_001, PJ7MD_010, PJ7MD_011,
+		PJ7MD_100, PJ7MD_101, PJ7MD_110, PJ7MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PJ6MD_000, PJ6MD_001, PJ6MD_010, PJ6MD_011,
+		PJ6MD_100, PJ6MD_101, PJ6MD_110, PJ6MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PJ5MD_000, PJ5MD_001, PJ5MD_010, PJ5MD_011,
+		PJ5MD_100, PJ5MD_101, PJ5MD_110, PJ5MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PJ4MD_000, PJ4MD_001, PJ4MD_010, PJ4MD_011,
+		PJ4MD_100, PJ4MD_101, PJ4MD_110, PJ4MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG("PJCR0", 0xfffe390e, 16, 4) {
+		PJ3MD_000, PJ3MD_001, PJ3MD_010, PJ3MD_011,
+		PJ3MD_100, PJ3MD_101, PJ3MD_110, PJ3MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PJ2MD_000, PJ2MD_001, PJ2MD_010, PJ2MD_011,
+		PJ2MD_100, PJ2MD_101, PJ2MD_110, PJ2MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PJ1MD_000, PJ1MD_001, PJ1MD_010, PJ1MD_011,
+		PJ1MD_100, PJ1MD_101, PJ1MD_110, PJ1MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0,
+
+		PJ0MD_000, PJ0MD_001, PJ0MD_010, PJ0MD_011,
+		PJ0MD_100, PJ0MD_101, PJ0MD_110, PJ0MD_111,
+		0, 0, 0, 0, 0, 0, 0, 0 }
+	},
+
+	{ PINMUX_CFG_REG("PJIOR1", 0xfffe3910, 16, 1) {
+		PJ31_IN, PJ31_OUT,
+		PJ30_IN, PJ30_OUT,
+		PJ29_IN, PJ29_OUT,
+		PJ28_IN, PJ28_OUT,
+		PJ27_IN, PJ27_OUT,
+		PJ26_IN, PJ26_OUT,
+		PJ25_IN, PJ25_OUT,
+		PJ24_IN, PJ24_OUT,
+		PJ23_IN, PJ23_OUT,
+		PJ22_IN, PJ22_OUT,
+		PJ21_IN, PJ21_OUT,
+		PJ20_IN, PJ20_OUT,
+		PJ19_IN, PJ19_OUT,
+		PJ18_IN, PJ18_OUT,
+		PJ17_IN, PJ17_OUT,
+		PJ16_IN, PJ16_OUT }
+	},
+	{ PINMUX_CFG_REG("PJIOR0", 0xfffe3912, 16, 1) {
+		PJ15_IN, PJ15_OUT,
+		PJ14_IN, PJ14_OUT,
+		PJ13_IN, PJ13_OUT,
+		PJ12_IN, PJ12_OUT,
+		PJ11_IN, PJ11_OUT,
+		PJ10_IN, PJ10_OUT,
+		PJ9_IN, PJ9_OUT,
+		PJ8_IN, PJ8_OUT,
+		PJ7_IN, PJ7_OUT,
+		PJ6_IN, PJ6_OUT,
+		PJ5_IN, PJ5_OUT,
+		PJ4_IN, PJ4_OUT,
+		PJ3_IN, PJ3_OUT,
+		PJ2_IN, PJ2_OUT,
+		PJ1_IN, PJ1_OUT,
+		PJ0_IN, PJ0_OUT }
+	},
+
+	{}
+};
+
+static struct pinmux_data_reg pinmux_data_regs[] = {
+	{ PINMUX_DATA_REG("PADR0", 0xfffe3816, 16) {
+		0, 0, 0, 0, 0, 0, 0, PA1_DATA,
+		0, 0, 0, 0, 0, 0, 0, PA0_DATA }
+	},
+
+	{ PINMUX_DATA_REG("PBDR1", 0xfffe3834, 16) {
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, PB22_DATA, PB21_DATA, PB20_DATA,
+		PB19_DATA, PB18_DATA, PB17_DATA, PB16_DATA }
+	},
+	{ PINMUX_DATA_REG("PBDR0", 0xfffe3836, 16) {
+		PB15_DATA, PB14_DATA, PB13_DATA, PB12_DATA,
+		PB11_DATA, PB10_DATA, PB9_DATA, PB8_DATA,
+		PB7_DATA, PB6_DATA, PB5_DATA, PB4_DATA,
+		PB3_DATA, PB2_DATA, PB1_DATA, 0 }
+	},
+
+	{ PINMUX_DATA_REG("PCDR0", 0xfffe3856, 16) {
+		0, 0, 0, 0,
+		0, 0, 0, PC8_DATA,
+		PC7_DATA, PC6_DATA, PC5_DATA, PC4_DATA,
+		PC3_DATA, PC2_DATA, PC1_DATA, PC0_DATA }
+	},
+
+	{ PINMUX_DATA_REG("PDDR0", 0xfffe3876, 16) {
+		PD15_DATA, PD14_DATA, PD13_DATA, PD12_DATA,
+		PD11_DATA, PD10_DATA, PD9_DATA, PD8_DATA,
+		PD7_DATA, PD6_DATA, PD5_DATA, PD4_DATA,
+		PD3_DATA, PD2_DATA, PD1_DATA, PD0_DATA }
+	},
+
+	{ PINMUX_DATA_REG("PEDR0", 0xfffe3896, 16) {
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PE7_DATA, PE6_DATA, PE5_DATA, PE4_DATA,
+		PE3_DATA, PE2_DATA, PE1_DATA, PE0_DATA }
+	},
+
+	{ PINMUX_DATA_REG("PFDR1", 0xfffe38b4, 16) {
+		0, 0, 0, 0, 0, 0, 0, 0,
+		PF23_DATA, PF22_DATA, PF21_DATA, PF20_DATA,
+		PF19_DATA, PF18_DATA, PF17_DATA, PF16_DATA }
+	},
+	{ PINMUX_DATA_REG("PFDR0", 0xfffe38b6, 16) {
+		PF15_DATA, PF14_DATA, PF13_DATA, PF12_DATA,
+		PF11_DATA, PF10_DATA, PF9_DATA, PF8_DATA,
+		PF7_DATA, PF6_DATA, PF5_DATA, PF4_DATA,
+		PF3_DATA, PF2_DATA, PF1_DATA, PF0_DATA }
+	},
+
+	{ PINMUX_DATA_REG("PGDR1", 0xfffe38d4, 16) {
+		0, 0, 0, 0,
+		PG27_DATA, PG26_DATA, PG25_DATA, PG24_DATA,
+		PG23_DATA, PG22_DATA, PG21_DATA, PG20_DATA,
+		PG19_DATA, PG18_DATA, PG17_DATA, PG16_DATA }
+	},
+	{ PINMUX_DATA_REG("PGDR0", 0xfffe38d6, 16) {
+		PG15_DATA, PG14_DATA, PG13_DATA, PG12_DATA,
+		PG11_DATA, PG10_DATA, PG9_DATA, PG8_DATA,
+		PG7_DATA, PG6_DATA, PG5_DATA, PG4_DATA,
+		PG3_DATA, PG2_DATA, PG1_DATA, PG0_DATA }
+	},
+
+	{ PINMUX_DATA_REG("PJDR1", 0xfffe3914, 16) {
+		PJ31_DATA, PJ30_DATA, PJ29_DATA, PJ28_DATA,
+		PJ27_DATA, PJ26_DATA, PJ25_DATA, PJ24_DATA,
+		PJ23_DATA, PJ22_DATA, PJ21_DATA, PJ20_DATA,
+		PJ19_DATA, PJ18_DATA, PJ17_DATA, PJ16_DATA }
+	},
+	{ PINMUX_DATA_REG("PJDR0", 0xfffe3916, 16) {
+		PJ15_DATA, PJ14_DATA, PJ13_DATA, PJ12_DATA,
+		PJ11_DATA, PJ10_DATA, PJ9_DATA, PJ8_DATA,
+		PJ7_DATA, PJ6_DATA, PJ5_DATA, PJ4_DATA,
+		PJ3_DATA, PJ2_DATA, PJ1_DATA, PJ0_DATA }
+	},
+
+	{ }
+};
+
+static struct pinmux_info sh7269_pinmux_info = {
+	.name = "sh7269_pfc",
+	.reserved_id = PINMUX_RESERVED,
+	.data = { PINMUX_DATA_BEGIN, PINMUX_DATA_END },
+	.input = { PINMUX_INPUT_BEGIN, PINMUX_INPUT_END, FORCE_IN },
+	.output = { PINMUX_OUTPUT_BEGIN, PINMUX_OUTPUT_END, FORCE_OUT },
+	.mark = { PINMUX_MARK_BEGIN, PINMUX_MARK_END },
+	.function = { PINMUX_FUNCTION_BEGIN, PINMUX_FUNCTION_END },
+
+	.first_gpio = GPIO_PA1,
+	.last_gpio = GPIO_FN_LCD_M_DISP,
+
+	.gpios = pinmux_gpios,
+	.cfg_regs = pinmux_config_regs,
+	.data_regs = pinmux_data_regs,
+
+	.gpio_data = pinmux_data,
+	.gpio_data_size = ARRAY_SIZE(pinmux_data),
+};
+
+static int __init plat_pinmux_setup(void)
+{
+	return register_pinmux(&sh7269_pinmux_info);
+}
+arch_initcall(plat_pinmux_setup);
diff --git a/arch/sh/kernel/cpu/sh2a/probe.c b/arch/sh/kernel/cpu/sh2a/probe.c
index 48e97a2..5170b6a 100644
--- a/arch/sh/kernel/cpu/sh2a/probe.c
+++ b/arch/sh/kernel/cpu/sh2a/probe.c
@@ -29,6 +29,12 @@
 #elif defined(CONFIG_CPU_SUBTYPE_SH7263)
 	boot_cpu_data.type			= CPU_SH7263;
 	boot_cpu_data.flags			|= CPU_HAS_FPU;
+#elif defined(CONFIG_CPU_SUBTYPE_SH7264)
+	boot_cpu_data.type			= CPU_SH7264;
+	boot_cpu_data.flags			|= CPU_HAS_FPU;
+#elif defined(CONFIG_CPU_SUBTYPE_SH7269)
+	boot_cpu_data.type			= CPU_SH7269;
+	boot_cpu_data.flags			|= CPU_HAS_FPU;
 #elif defined(CONFIG_CPU_SUBTYPE_SH7206)
 	boot_cpu_data.type			= CPU_SH7206;
 	boot_cpu_data.flags			|= CPU_HAS_DSP;
diff --git a/arch/sh/kernel/cpu/sh2a/setup-mxg.c b/arch/sh/kernel/cpu/sh2a/setup-mxg.c
index 949bf2b..f7f1cf2 100644
--- a/arch/sh/kernel/cpu/sh2a/setup-mxg.c
+++ b/arch/sh/kernel/cpu/sh2a/setup-mxg.c
@@ -204,7 +204,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCIF,
-	.irqs		= { 220, 220, 220, 220 },
+	.irqs		= SCIx_IRQ_MUXED(220),
 };
 
 static struct platform_device scif0_device = {
diff --git a/arch/sh/kernel/cpu/sh2a/setup-sh7201.c b/arch/sh/kernel/cpu/sh2a/setup-sh7201.c
index 9df558d..7b84785 100644
--- a/arch/sh/kernel/cpu/sh2a/setup-sh7201.c
+++ b/arch/sh/kernel/cpu/sh2a/setup-sh7201.c
@@ -183,7 +183,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCIF,
-	.irqs		= { 180, 180, 180, 180 }
+	.irqs		= SCIx_IRQ_MUXED(180),
 };
 
 static struct platform_device scif0_device = {
@@ -200,7 +200,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCIF,
-	.irqs		= { 184, 184, 184, 184 }
+	.irqs		= SCIx_IRQ_MUXED(184),
 };
 
 static struct platform_device scif1_device = {
@@ -217,7 +217,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCIF,
-	.irqs		= { 188, 188, 188, 188 }
+	.irqs		= SCIx_IRQ_MUXED(188),
 };
 
 static struct platform_device scif2_device = {
@@ -234,7 +234,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCIF,
-	.irqs		= { 192, 192, 192, 192 }
+	.irqs		= SCIx_IRQ_MUXED(192),
 };
 
 static struct platform_device scif3_device = {
@@ -251,7 +251,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCIF,
-	.irqs		= { 196, 196, 196, 196 }
+	.irqs		= SCIx_IRQ_MUXED(196),
 };
 
 static struct platform_device scif4_device = {
@@ -268,7 +268,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCIF,
-	.irqs		= { 200, 200, 200, 200 }
+	.irqs		= SCIx_IRQ_MUXED(200),
 };
 
 static struct platform_device scif5_device = {
@@ -285,7 +285,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCIF,
-	.irqs		= { 204, 204, 204, 204 }
+	.irqs		= SCIx_IRQ_MUXED(204),
 };
 
 static struct platform_device scif6_device = {
@@ -302,7 +302,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCIF,
-	.irqs		= { 208, 208, 208, 208 }
+	.irqs		= SCIx_IRQ_MUXED(208),
 };
 
 static struct platform_device scif7_device = {
diff --git a/arch/sh/kernel/cpu/sh2a/setup-sh7203.c b/arch/sh/kernel/cpu/sh2a/setup-sh7203.c
index 0bd744f..bfc33f6 100644
--- a/arch/sh/kernel/cpu/sh2a/setup-sh7203.c
+++ b/arch/sh/kernel/cpu/sh2a/setup-sh7203.c
@@ -180,7 +180,7 @@
 			  SCSCR_REIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCIF,
-	.irqs		=  { 192, 192, 192, 192 },
+	.irqs		= SCIx_IRQ_MUXED(192),
 	.regtype	= SCIx_SH2_SCIF_FIFODATA_REGTYPE,
 };
 
@@ -199,7 +199,7 @@
 			  SCSCR_REIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCIF,
-	.irqs		=  { 196, 196, 196, 196 },
+	.irqs		= SCIx_IRQ_MUXED(196),
 	.regtype	= SCIx_SH2_SCIF_FIFODATA_REGTYPE,
 };
 
@@ -218,7 +218,7 @@
 			  SCSCR_REIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCIF,
-	.irqs		=  { 200, 200, 200, 200 },
+	.irqs		= SCIx_IRQ_MUXED(200),
 	.regtype	= SCIx_SH2_SCIF_FIFODATA_REGTYPE,
 };
 
@@ -237,7 +237,7 @@
 			  SCSCR_REIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCIF,
-	.irqs		=  { 204, 204, 204, 204 },
+	.irqs		= SCIx_IRQ_MUXED(204),
 	.regtype	= SCIx_SH2_SCIF_FIFODATA_REGTYPE,
 };
 
diff --git a/arch/sh/kernel/cpu/sh2a/setup-sh7206.c b/arch/sh/kernel/cpu/sh2a/setup-sh7206.c
index 5d14f84..a501074 100644
--- a/arch/sh/kernel/cpu/sh2a/setup-sh7206.c
+++ b/arch/sh/kernel/cpu/sh2a/setup-sh7206.c
@@ -139,7 +139,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCIF,
-	.irqs		= { 240, 240, 240, 240 },
+	.irqs		= SCIx_IRQ_MUXED(240),
 };
 
 static struct platform_device scif0_device = {
@@ -156,7 +156,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCIF,
-	.irqs		= { 244, 244, 244, 244 },
+	.irqs		= SCIx_IRQ_MUXED(244),
 };
 
 static struct platform_device scif1_device = {
@@ -173,7 +173,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCIF,
-	.irqs		= { 248, 248, 248, 248 },
+	.irqs		= SCIx_IRQ_MUXED(248),
 };
 
 static struct platform_device scif2_device = {
@@ -190,7 +190,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCIF,
-	.irqs		= { 252, 252, 252, 252 },
+	.irqs		= SCIx_IRQ_MUXED(252),
 };
 
 static struct platform_device scif3_device = {
diff --git a/arch/sh/kernel/cpu/sh2a/setup-sh7264.c b/arch/sh/kernel/cpu/sh2a/setup-sh7264.c
new file mode 100644
index 0000000..ce5c1b5
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh2a/setup-sh7264.c
@@ -0,0 +1,606 @@
+/*
+ * SH7264 Setup
+ *
+ * Copyright (C) 2012  Renesas Electronics Europe Ltd
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/serial.h>
+#include <linux/serial_sci.h>
+#include <linux/usb/r8a66597.h>
+#include <linux/sh_timer.h>
+#include <linux/io.h>
+
+enum {
+	UNUSED = 0,
+
+	/* interrupt sources */
+	IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7,
+	PINT0, PINT1, PINT2, PINT3, PINT4, PINT5, PINT6, PINT7,
+
+	DMAC0, DMAC1, DMAC2, DMAC3, DMAC4, DMAC5, DMAC6, DMAC7,
+	DMAC8, DMAC9, DMAC10, DMAC11, DMAC12, DMAC13, DMAC14, DMAC15,
+	USB, VDC3, CMT0, CMT1, BSC, WDT,
+	MTU0_ABCD, MTU0_VEF, MTU1_AB, MTU1_VU, MTU2_AB, MTU2_VU,
+	MTU3_ABCD, MTU3_TCI3V, MTU4_ABCD, MTU4_TCI4V,
+	PWMT1, PWMT2, ADC_ADI,
+	SSIF0, SSII1, SSII2, SSII3,
+	RSPDIF,
+	IIC30, IIC31, IIC32, IIC33,
+	SCIF0_BRI, SCIF0_ERI, SCIF0_RXI, SCIF0_TXI,
+	SCIF1_BRI, SCIF1_ERI, SCIF1_RXI, SCIF1_TXI,
+	SCIF2_BRI, SCIF2_ERI, SCIF2_RXI, SCIF2_TXI,
+	SCIF3_BRI, SCIF3_ERI, SCIF3_RXI, SCIF3_TXI,
+	SCIF4_BRI, SCIF4_ERI, SCIF4_RXI, SCIF4_TXI,
+	SCIF5_BRI, SCIF5_ERI, SCIF5_RXI, SCIF5_TXI,
+	SCIF6_BRI, SCIF6_ERI, SCIF6_RXI, SCIF6_TXI,
+	SCIF7_BRI, SCIF7_ERI, SCIF7_RXI, SCIF7_TXI,
+	SIO_FIFO, RSPIC0, RSPIC1,
+	RCAN0, RCAN1, IEBC, CD_ROMD,
+	NFMC, SDHI, RTC,
+	SRCC0, SRCC1, DCOMU, OFFI, IFEI,
+
+	/* interrupt groups */
+	PINT, SCIF0, SCIF1, SCIF2, SCIF3, SCIF4, SCIF5, SCIF6, SCIF7,
+};
+
+static struct intc_vect vectors[] __initdata = {
+	INTC_IRQ(IRQ0, 64), INTC_IRQ(IRQ1, 65),
+	INTC_IRQ(IRQ2, 66), INTC_IRQ(IRQ3, 67),
+	INTC_IRQ(IRQ4, 68), INTC_IRQ(IRQ5, 69),
+	INTC_IRQ(IRQ6, 70), INTC_IRQ(IRQ7, 71),
+
+	INTC_IRQ(PINT0, 80), INTC_IRQ(PINT1, 81),
+	INTC_IRQ(PINT2, 82), INTC_IRQ(PINT3, 83),
+	INTC_IRQ(PINT4, 84), INTC_IRQ(PINT5, 85),
+	INTC_IRQ(PINT6, 86), INTC_IRQ(PINT7, 87),
+
+	INTC_IRQ(DMAC0, 108), INTC_IRQ(DMAC0, 109),
+	INTC_IRQ(DMAC1, 112), INTC_IRQ(DMAC1, 113),
+	INTC_IRQ(DMAC2, 116), INTC_IRQ(DMAC2, 117),
+	INTC_IRQ(DMAC3, 120), INTC_IRQ(DMAC3, 121),
+	INTC_IRQ(DMAC4, 124), INTC_IRQ(DMAC4, 125),
+	INTC_IRQ(DMAC5, 128), INTC_IRQ(DMAC5, 129),
+	INTC_IRQ(DMAC6, 132), INTC_IRQ(DMAC6, 133),
+	INTC_IRQ(DMAC7, 136), INTC_IRQ(DMAC7, 137),
+	INTC_IRQ(DMAC8, 140), INTC_IRQ(DMAC8, 141),
+	INTC_IRQ(DMAC9, 144), INTC_IRQ(DMAC9, 145),
+	INTC_IRQ(DMAC10, 148), INTC_IRQ(DMAC10, 149),
+	INTC_IRQ(DMAC11, 152), INTC_IRQ(DMAC11, 153),
+	INTC_IRQ(DMAC12, 156), INTC_IRQ(DMAC12, 157),
+	INTC_IRQ(DMAC13, 160), INTC_IRQ(DMAC13, 161),
+	INTC_IRQ(DMAC14, 164), INTC_IRQ(DMAC14, 165),
+	INTC_IRQ(DMAC15, 168), INTC_IRQ(DMAC15, 169),
+
+	INTC_IRQ(USB, 170),
+	INTC_IRQ(VDC3, 171), INTC_IRQ(VDC3, 172),
+	INTC_IRQ(VDC3, 173), INTC_IRQ(VDC3, 174),
+	INTC_IRQ(CMT0, 175), INTC_IRQ(CMT1, 176),
+	INTC_IRQ(BSC, 177), INTC_IRQ(WDT, 178),
+
+	INTC_IRQ(MTU0_ABCD, 179), INTC_IRQ(MTU0_ABCD, 180),
+	INTC_IRQ(MTU0_ABCD, 181), INTC_IRQ(MTU0_ABCD, 182),
+	INTC_IRQ(MTU0_VEF, 183),
+	INTC_IRQ(MTU0_VEF, 184), INTC_IRQ(MTU0_VEF, 185),
+	INTC_IRQ(MTU1_AB, 186), INTC_IRQ(MTU1_AB, 187),
+	INTC_IRQ(MTU1_VU, 188), INTC_IRQ(MTU1_VU, 189),
+	INTC_IRQ(MTU2_AB, 190), INTC_IRQ(MTU2_AB, 191),
+	INTC_IRQ(MTU2_VU, 192), INTC_IRQ(MTU2_VU, 193),
+	INTC_IRQ(MTU3_ABCD, 194), INTC_IRQ(MTU3_ABCD, 195),
+	INTC_IRQ(MTU3_ABCD, 196), INTC_IRQ(MTU3_ABCD, 197),
+	INTC_IRQ(MTU3_TCI3V, 198),
+	INTC_IRQ(MTU4_ABCD, 199), INTC_IRQ(MTU4_ABCD, 200),
+	INTC_IRQ(MTU4_ABCD, 201), INTC_IRQ(MTU4_ABCD, 202),
+	INTC_IRQ(MTU4_TCI4V, 203),
+
+	INTC_IRQ(PWMT1, 204), INTC_IRQ(PWMT2, 205),
+
+	INTC_IRQ(ADC_ADI, 206),
+
+	INTC_IRQ(SSIF0, 207), INTC_IRQ(SSIF0, 208),
+	INTC_IRQ(SSIF0, 209),
+	INTC_IRQ(SSII1, 210), INTC_IRQ(SSII1, 211),
+	INTC_IRQ(SSII2, 212), INTC_IRQ(SSII2, 213),
+	INTC_IRQ(SSII3, 214), INTC_IRQ(SSII3, 215),
+
+	INTC_IRQ(RSPDIF, 216),
+
+	INTC_IRQ(IIC30, 217), INTC_IRQ(IIC30, 218),
+	INTC_IRQ(IIC30, 219), INTC_IRQ(IIC30, 220),
+	INTC_IRQ(IIC30, 221),
+	INTC_IRQ(IIC31, 222), INTC_IRQ(IIC31, 223),
+	INTC_IRQ(IIC31, 224), INTC_IRQ(IIC31, 225),
+	INTC_IRQ(IIC31, 226),
+	INTC_IRQ(IIC32, 227), INTC_IRQ(IIC32, 228),
+	INTC_IRQ(IIC32, 229), INTC_IRQ(IIC32, 230),
+	INTC_IRQ(IIC32, 231),
+
+	INTC_IRQ(SCIF0_BRI, 232), INTC_IRQ(SCIF0_ERI, 233),
+	INTC_IRQ(SCIF0_RXI, 234), INTC_IRQ(SCIF0_TXI, 235),
+	INTC_IRQ(SCIF1_BRI, 236), INTC_IRQ(SCIF1_ERI, 237),
+	INTC_IRQ(SCIF1_RXI, 238), INTC_IRQ(SCIF1_TXI, 239),
+	INTC_IRQ(SCIF2_BRI, 240), INTC_IRQ(SCIF2_ERI, 241),
+	INTC_IRQ(SCIF2_RXI, 242), INTC_IRQ(SCIF2_TXI, 243),
+	INTC_IRQ(SCIF3_BRI, 244), INTC_IRQ(SCIF3_ERI, 245),
+	INTC_IRQ(SCIF3_RXI, 246), INTC_IRQ(SCIF3_TXI, 247),
+	INTC_IRQ(SCIF4_BRI, 248), INTC_IRQ(SCIF4_ERI, 249),
+	INTC_IRQ(SCIF4_RXI, 250), INTC_IRQ(SCIF4_TXI, 251),
+	INTC_IRQ(SCIF5_BRI, 252), INTC_IRQ(SCIF5_ERI, 253),
+	INTC_IRQ(SCIF5_RXI, 254), INTC_IRQ(SCIF5_TXI, 255),
+	INTC_IRQ(SCIF6_BRI, 256), INTC_IRQ(SCIF6_ERI, 257),
+	INTC_IRQ(SCIF6_RXI, 258), INTC_IRQ(SCIF6_TXI, 259),
+	INTC_IRQ(SCIF7_BRI, 260), INTC_IRQ(SCIF7_ERI, 261),
+	INTC_IRQ(SCIF7_RXI, 262), INTC_IRQ(SCIF7_TXI, 263),
+
+	INTC_IRQ(SIO_FIFO, 264),
+
+	INTC_IRQ(RSPIC0, 265), INTC_IRQ(RSPIC0, 266),
+	INTC_IRQ(RSPIC0, 267),
+	INTC_IRQ(RSPIC1, 268), INTC_IRQ(RSPIC1, 269),
+	INTC_IRQ(RSPIC1, 270),
+
+	INTC_IRQ(RCAN0, 271), INTC_IRQ(RCAN0, 272),
+	INTC_IRQ(RCAN0, 273), INTC_IRQ(RCAN0, 274),
+	INTC_IRQ(RCAN0, 275),
+	INTC_IRQ(RCAN1, 276), INTC_IRQ(RCAN1, 277),
+	INTC_IRQ(RCAN1, 278), INTC_IRQ(RCAN1, 279),
+	INTC_IRQ(RCAN1, 280),
+
+	INTC_IRQ(IEBC, 281),
+
+	INTC_IRQ(CD_ROMD, 282), INTC_IRQ(CD_ROMD, 283),
+	INTC_IRQ(CD_ROMD, 284), INTC_IRQ(CD_ROMD, 285),
+	INTC_IRQ(CD_ROMD, 286), INTC_IRQ(CD_ROMD, 287),
+
+	INTC_IRQ(NFMC, 288), INTC_IRQ(NFMC, 289),
+	INTC_IRQ(NFMC, 290), INTC_IRQ(NFMC, 291),
+
+	INTC_IRQ(SDHI, 292), INTC_IRQ(SDHI, 293),
+	INTC_IRQ(SDHI, 294),
+
+	INTC_IRQ(RTC, 296), INTC_IRQ(RTC, 297),
+	INTC_IRQ(RTC, 298),
+
+	INTC_IRQ(SRCC0, 299), INTC_IRQ(SRCC0, 300),
+	INTC_IRQ(SRCC0, 301), INTC_IRQ(SRCC0, 302),
+	INTC_IRQ(SRCC0, 303),
+	INTC_IRQ(SRCC1, 304), INTC_IRQ(SRCC1, 305),
+	INTC_IRQ(SRCC1, 306), INTC_IRQ(SRCC1, 307),
+	INTC_IRQ(SRCC1, 308),
+
+	INTC_IRQ(DCOMU, 310), INTC_IRQ(DCOMU, 311),
+	INTC_IRQ(DCOMU, 312),
+};
+
+static struct intc_group groups[] __initdata = {
+	INTC_GROUP(PINT, PINT0, PINT1, PINT2, PINT3,
+		   PINT4, PINT5, PINT6, PINT7),
+	INTC_GROUP(SCIF0, SCIF0_BRI, SCIF0_ERI, SCIF0_RXI, SCIF0_TXI),
+	INTC_GROUP(SCIF1, SCIF1_BRI, SCIF1_ERI, SCIF1_RXI, SCIF1_TXI),
+	INTC_GROUP(SCIF2, SCIF2_BRI, SCIF2_ERI, SCIF2_RXI, SCIF2_TXI),
+	INTC_GROUP(SCIF3, SCIF3_BRI, SCIF3_ERI, SCIF3_RXI, SCIF3_TXI),
+	INTC_GROUP(SCIF4, SCIF4_BRI, SCIF4_ERI, SCIF4_RXI, SCIF4_TXI),
+	INTC_GROUP(SCIF5, SCIF5_BRI, SCIF5_ERI, SCIF5_RXI, SCIF5_TXI),
+	INTC_GROUP(SCIF6, SCIF6_BRI, SCIF6_ERI, SCIF6_RXI, SCIF6_TXI),
+	INTC_GROUP(SCIF7, SCIF7_BRI, SCIF7_ERI, SCIF7_RXI, SCIF7_TXI),
+};
+
+static struct intc_prio_reg prio_registers[] __initdata = {
+	{ 0xfffe0818, 0, 16, 4, /* IPR01 */ { IRQ0, IRQ1, IRQ2, IRQ3 } },
+	{ 0xfffe081a, 0, 16, 4, /* IPR02 */ { IRQ4, IRQ5, IRQ6, IRQ7 } },
+	{ 0xfffe0820, 0, 16, 4, /* IPR05 */ { PINT, 0, 0, 0 } },
+	{ 0xfffe0c00, 0, 16, 4, /* IPR06 */ { DMAC0,  DMAC1,  DMAC2,  DMAC3 } },
+	{ 0xfffe0c02, 0, 16, 4, /* IPR07 */ { DMAC4,  DMAC5,  DMAC6,  DMAC7 } },
+	{ 0xfffe0c04, 0, 16, 4, /* IPR08 */ { DMAC8,  DMAC9,
+					      DMAC10, DMAC11 } },
+	{ 0xfffe0c06, 0, 16, 4, /* IPR09 */ { DMAC12, DMAC13,
+					      DMAC14, DMAC15 } },
+	{ 0xfffe0c08, 0, 16, 4, /* IPR10 */ { USB, VDC3, CMT0, CMT1 } },
+	{ 0xfffe0c0a, 0, 16, 4, /* IPR11 */ { BSC, WDT, MTU0_ABCD, MTU0_VEF } },
+	{ 0xfffe0c0c, 0, 16, 4, /* IPR12 */ { MTU1_AB, MTU1_VU,
+					      MTU2_AB, MTU2_VU } },
+	{ 0xfffe0c0e, 0, 16, 4, /* IPR13 */ { MTU3_ABCD, MTU3_TCI3V,
+					      MTU4_ABCD, MTU4_TCI4V } },
+	{ 0xfffe0c10, 0, 16, 4, /* IPR14 */ { PWMT1, PWMT2, ADC_ADI, 0 } },
+	{ 0xfffe0c12, 0, 16, 4, /* IPR15 */ { SSIF0, SSII1, SSII2, SSII3 } },
+	{ 0xfffe0c14, 0, 16, 4, /* IPR16 */ { RSPDIF, IIC30, IIC31, IIC32 } },
+	{ 0xfffe0c16, 0, 16, 4, /* IPR17 */ { SCIF0, SCIF1, SCIF2, SCIF3 } },
+	{ 0xfffe0c18, 0, 16, 4, /* IPR18 */ { SCIF4, SCIF5, SCIF6, SCIF7 } },
+	{ 0xfffe0c1a, 0, 16, 4, /* IPR19 */ { SIO_FIFO, 0, RSPIC0, RSPIC1, } },
+	{ 0xfffe0c1c, 0, 16, 4, /* IPR20 */ { RCAN0, RCAN1, IEBC, CD_ROMD } },
+	{ 0xfffe0c1e, 0, 16, 4, /* IPR21 */ { NFMC, SDHI, RTC, 0 } },
+	{ 0xfffe0c20, 0, 16, 4, /* IPR22 */ { SRCC0, SRCC1, 0, DCOMU } },
+};
+
+static struct intc_mask_reg mask_registers[] __initdata = {
+	{ 0xfffe0808, 0, 16, /* PINTER */
+	  { 0, 0, 0, 0, 0, 0, 0, 0,
+	    PINT7, PINT6, PINT5, PINT4, PINT3, PINT2, PINT1, PINT0 } },
+};
+
+static DECLARE_INTC_DESC(intc_desc, "sh7264", vectors, groups,
+			 mask_registers, prio_registers, NULL);
+
+static struct plat_sci_port scif0_platform_data = {
+	.mapbase	= 0xfffe8000,
+	.flags		= UPF_BOOT_AUTOCONF,
+	.scscr		= SCSCR_RIE | SCSCR_TIE | SCSCR_RE | SCSCR_TE |
+			  SCSCR_REIE | SCSCR_TOIE,
+	.scbrr_algo_id	= SCBRR_ALGO_2,
+	.type		= PORT_SCIF,
+	.irqs		=  { 233, 234, 235, 232 },
+	.regtype	= SCIx_SH2_SCIF_FIFODATA_REGTYPE,
+};
+
+static struct platform_device scif0_device = {
+	.name		= "sh-sci",
+	.id		= 0,
+	.dev		= {
+		.platform_data	= &scif0_platform_data,
+	},
+};
+
+static struct plat_sci_port scif1_platform_data = {
+	.mapbase	= 0xfffe8800,
+	.flags		= UPF_BOOT_AUTOCONF,
+	.scscr		= SCSCR_RIE | SCSCR_TIE | SCSCR_RE | SCSCR_TE |
+			  SCSCR_REIE | SCSCR_TOIE,
+	.scbrr_algo_id	= SCBRR_ALGO_2,
+	.type		= PORT_SCIF,
+	.irqs		=  { 237, 238, 239, 236 },
+	.regtype	= SCIx_SH2_SCIF_FIFODATA_REGTYPE,
+};
+
+static struct platform_device scif1_device = {
+	.name		= "sh-sci",
+	.id		= 1,
+	.dev		= {
+		.platform_data	= &scif1_platform_data,
+	},
+};
+
+static struct plat_sci_port scif2_platform_data = {
+	.mapbase	= 0xfffe9000,
+	.flags		= UPF_BOOT_AUTOCONF,
+	.scscr		= SCSCR_RIE | SCSCR_TIE | SCSCR_RE | SCSCR_TE |
+			  SCSCR_REIE | SCSCR_TOIE,
+	.scbrr_algo_id	= SCBRR_ALGO_2,
+	.type		= PORT_SCIF,
+	.irqs		=  { 241, 242, 243, 240 },
+	.regtype	= SCIx_SH2_SCIF_FIFODATA_REGTYPE,
+};
+
+static struct platform_device scif2_device = {
+	.name		= "sh-sci",
+	.id		= 2,
+	.dev		= {
+		.platform_data	= &scif2_platform_data,
+	},
+};
+
+static struct plat_sci_port scif3_platform_data = {
+	.mapbase	= 0xfffe9800,
+	.flags		= UPF_BOOT_AUTOCONF,
+	.scscr		= SCSCR_RIE | SCSCR_TIE | SCSCR_RE | SCSCR_TE |
+			  SCSCR_REIE | SCSCR_TOIE,
+	.scbrr_algo_id	= SCBRR_ALGO_2,
+	.type		= PORT_SCIF,
+	.irqs		=  { 245, 246, 247, 244 },
+	.regtype	= SCIx_SH2_SCIF_FIFODATA_REGTYPE,
+};
+
+static struct platform_device scif3_device = {
+	.name		= "sh-sci",
+	.id		= 3,
+	.dev		= {
+		.platform_data	= &scif3_platform_data,
+	},
+};
+
+static struct plat_sci_port scif4_platform_data = {
+	.mapbase	= 0xfffea000,
+	.flags		= UPF_BOOT_AUTOCONF,
+	.scscr		= SCSCR_RIE | SCSCR_TIE | SCSCR_RE | SCSCR_TE |
+			  SCSCR_REIE | SCSCR_TOIE,
+	.scbrr_algo_id	= SCBRR_ALGO_2,
+	.type		= PORT_SCIF,
+	.irqs		=  { 249, 250, 251, 248 },
+	.regtype	= SCIx_SH2_SCIF_FIFODATA_REGTYPE,
+};
+
+static struct platform_device scif4_device = {
+	.name		= "sh-sci",
+	.id		= 4,
+	.dev		= {
+		.platform_data	= &scif4_platform_data,
+	},
+};
+
+static struct plat_sci_port scif5_platform_data = {
+	.mapbase	= 0xfffea800,
+	.flags		= UPF_BOOT_AUTOCONF,
+	.scscr		= SCSCR_RIE | SCSCR_TIE | SCSCR_RE | SCSCR_TE |
+			  SCSCR_REIE | SCSCR_TOIE,
+	.scbrr_algo_id	= SCBRR_ALGO_2,
+	.type		= PORT_SCIF,
+	.irqs		=  { 253, 254, 255, 252 },
+	.regtype	= SCIx_SH2_SCIF_FIFODATA_REGTYPE,
+};
+
+static struct platform_device scif5_device = {
+	.name		= "sh-sci",
+	.id		= 5,
+	.dev		= {
+		.platform_data	= &scif5_platform_data,
+	},
+};
+
+static struct plat_sci_port scif6_platform_data = {
+	.mapbase	= 0xfffeb000,
+	.flags		= UPF_BOOT_AUTOCONF,
+	.scscr		= SCSCR_RIE | SCSCR_TIE | SCSCR_RE | SCSCR_TE |
+			  SCSCR_REIE | SCSCR_TOIE,
+	.scbrr_algo_id	= SCBRR_ALGO_2,
+	.type		= PORT_SCIF,
+	.irqs		=  { 257, 258, 259, 256 },
+	.regtype	= SCIx_SH2_SCIF_FIFODATA_REGTYPE,
+};
+
+static struct platform_device scif6_device = {
+	.name		= "sh-sci",
+	.id		= 6,
+	.dev		= {
+		.platform_data	= &scif6_platform_data,
+	},
+};
+
+static struct plat_sci_port scif7_platform_data = {
+	.mapbase	= 0xfffeb800,
+	.flags		= UPF_BOOT_AUTOCONF,
+	.scscr		= SCSCR_RIE | SCSCR_TIE | SCSCR_RE | SCSCR_TE |
+			  SCSCR_REIE | SCSCR_TOIE,
+	.scbrr_algo_id	= SCBRR_ALGO_2,
+	.type		= PORT_SCIF,
+	.irqs		=  { 261, 262, 263, 260 },
+	.regtype	= SCIx_SH2_SCIF_FIFODATA_REGTYPE,
+};
+
+static struct platform_device scif7_device = {
+	.name		= "sh-sci",
+	.id		= 7,
+	.dev		= {
+		.platform_data	= &scif7_platform_data,
+	},
+};
+
+static struct sh_timer_config cmt0_platform_data = {
+	.channel_offset = 0x02,
+	.timer_bit = 0,
+	.clockevent_rating = 125,
+	.clocksource_rating = 0, /* disabled due to code generation issues */
+};
+
+static struct resource cmt0_resources[] = {
+	[0] = {
+		.name	= "CMT0",
+		.start	= 0xfffec002,
+		.end	= 0xfffec007,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= 175,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device cmt0_device = {
+	.name		= "sh_cmt",
+	.id		= 0,
+	.dev = {
+		.platform_data	= &cmt0_platform_data,
+	},
+	.resource	= cmt0_resources,
+	.num_resources	= ARRAY_SIZE(cmt0_resources),
+};
+
+static struct sh_timer_config cmt1_platform_data = {
+	.name = "CMT1",
+	.channel_offset = 0x08,
+	.timer_bit = 1,
+	.clockevent_rating = 125,
+	.clocksource_rating = 0, /* disabled due to code generation issues */
+};
+
+static struct resource cmt1_resources[] = {
+	[0] = {
+		.name	= "CMT1",
+		.start	= 0xfffec008,
+		.end	= 0xfffec00d,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= 176,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device cmt1_device = {
+	.name		= "sh_cmt",
+	.id		= 1,
+	.dev = {
+		.platform_data	= &cmt1_platform_data,
+	},
+	.resource	= cmt1_resources,
+	.num_resources	= ARRAY_SIZE(cmt1_resources),
+};
+
+static struct sh_timer_config mtu2_0_platform_data = {
+	.name = "MTU2_0",
+	.channel_offset = -0x80,
+	.timer_bit = 0,
+	.clockevent_rating = 200,
+};
+
+static struct resource mtu2_0_resources[] = {
+	[0] = {
+		.name	= "MTU2_0",
+		.start	= 0xfffe4300,
+		.end	= 0xfffe4326,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= 179,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device mtu2_0_device = {
+	.name		= "sh_mtu2",
+	.id		= 0,
+	.dev = {
+		.platform_data	= &mtu2_0_platform_data,
+	},
+	.resource	= mtu2_0_resources,
+	.num_resources	= ARRAY_SIZE(mtu2_0_resources),
+};
+
+static struct sh_timer_config mtu2_1_platform_data = {
+	.name = "MTU2_1",
+	.channel_offset = -0x100,
+	.timer_bit = 1,
+	.clockevent_rating = 200,
+};
+
+static struct resource mtu2_1_resources[] = {
+	[0] = {
+		.name	= "MTU2_1",
+		.start	= 0xfffe4380,
+		.end	= 0xfffe4390,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= 186,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device mtu2_1_device = {
+	.name		= "sh_mtu2",
+	.id		= 1,
+	.dev = {
+		.platform_data	= &mtu2_1_platform_data,
+	},
+	.resource	= mtu2_1_resources,
+	.num_resources	= ARRAY_SIZE(mtu2_1_resources),
+};
+
+static struct resource rtc_resources[] = {
+	[0] = {
+		.start	= 0xfffe6000,
+		.end	= 0xfffe6000 + 0x30 - 1,
+		.flags	= IORESOURCE_IO,
+	},
+	[1] = {
+		/* Shared Period/Carry/Alarm IRQ */
+		.start	= 296,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device rtc_device = {
+	.name		= "sh-rtc",
+	.id		= -1,
+	.num_resources	= ARRAY_SIZE(rtc_resources),
+	.resource	= rtc_resources,
+};
+
+/* USB Host */
+static void usb_port_power(int port, int power)
+{
+	__raw_writew(0x200 , 0xffffc0c2) ; /* Initialise UACS25 */
+}
+
+static struct r8a66597_platdata r8a66597_data = {
+	.on_chip = 1,
+	.endian = 1,
+	.port_power = usb_port_power,
+};
+
+static struct resource r8a66597_usb_host_resources[] = {
+	[0] = {
+		.start	= 0xffffc000,
+		.end	= 0xffffc0e4,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= 170,
+		.end	= 170,
+		.flags	= IORESOURCE_IRQ | IRQF_TRIGGER_LOW,
+	},
+};
+
+static struct platform_device r8a66597_usb_host_device = {
+	.name		= "r8a66597_hcd",
+	.id		= 0,
+	.dev = {
+		.dma_mask		= NULL,         /*  not use dma */
+		.coherent_dma_mask	= 0xffffffff,
+		.platform_data		= &r8a66597_data,
+	},
+	.num_resources	= ARRAY_SIZE(r8a66597_usb_host_resources),
+	.resource	= r8a66597_usb_host_resources,
+};
+
+static struct platform_device *sh7264_devices[] __initdata = {
+	&scif0_device,
+	&scif1_device,
+	&scif2_device,
+	&scif3_device,
+	&scif4_device,
+	&scif5_device,
+	&scif6_device,
+	&scif7_device,
+	&cmt0_device,
+	&cmt1_device,
+	&mtu2_0_device,
+	&mtu2_1_device,
+	&rtc_device,
+	&r8a66597_usb_host_device,
+};
+
+static int __init sh7264_devices_setup(void)
+{
+	return platform_add_devices(sh7264_devices,
+				    ARRAY_SIZE(sh7264_devices));
+}
+arch_initcall(sh7264_devices_setup);
+
+void __init plat_irq_setup(void)
+{
+	register_intc_controller(&intc_desc);
+}
+
+static struct platform_device *sh7264_early_devices[] __initdata = {
+	&scif0_device,
+	&scif1_device,
+	&scif2_device,
+	&scif3_device,
+	&scif4_device,
+	&scif5_device,
+	&scif6_device,
+	&scif7_device,
+	&cmt0_device,
+	&cmt1_device,
+	&mtu2_0_device,
+	&mtu2_1_device,
+};
+
+void __init plat_early_device_setup(void)
+{
+	early_platform_add_devices(sh7264_early_devices,
+				   ARRAY_SIZE(sh7264_early_devices));
+}
diff --git a/arch/sh/kernel/cpu/sh2a/setup-sh7269.c b/arch/sh/kernel/cpu/sh2a/setup-sh7269.c
new file mode 100644
index 0000000..e82ae9d
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh2a/setup-sh7269.c
@@ -0,0 +1,615 @@
+/*
+ * SH7269 Setup
+ *
+ * Copyright (C) 2012  Renesas Electronics Europe Ltd
+ * Copyright (C) 2012  Phil Edworthy
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/serial.h>
+#include <linux/serial_sci.h>
+#include <linux/usb/r8a66597.h>
+#include <linux/sh_timer.h>
+#include <linux/io.h>
+
+enum {
+	UNUSED = 0,
+
+	/* interrupt sources */
+	IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7,
+	PINT0, PINT1, PINT2, PINT3, PINT4, PINT5, PINT6, PINT7,
+
+	DMAC0, DMAC1, DMAC2, DMAC3, DMAC4, DMAC5, DMAC6, DMAC7,
+	DMAC8, DMAC9, DMAC10, DMAC11, DMAC12, DMAC13, DMAC14, DMAC15,
+	USB, VDC4, CMT0, CMT1, BSC, WDT,
+	MTU0_ABCD, MTU0_VEF, MTU1_AB, MTU1_VU, MTU2_AB, MTU2_VU,
+	MTU3_ABCD, MTU3_TCI3V, MTU4_ABCD, MTU4_TCI4V,
+	PWMT1, PWMT2, ADC_ADI,
+	SSIF0, SSII1, SSII2, SSII3, SSII4, SSII5,
+	RSPDIF,
+	IIC30, IIC31, IIC32, IIC33,
+	SCIF0_BRI, SCIF0_ERI, SCIF0_RXI, SCIF0_TXI,
+	SCIF1_BRI, SCIF1_ERI, SCIF1_RXI, SCIF1_TXI,
+	SCIF2_BRI, SCIF2_ERI, SCIF2_RXI, SCIF2_TXI,
+	SCIF3_BRI, SCIF3_ERI, SCIF3_RXI, SCIF3_TXI,
+	SCIF4_BRI, SCIF4_ERI, SCIF4_RXI, SCIF4_TXI,
+	SCIF5_BRI, SCIF5_ERI, SCIF5_RXI, SCIF5_TXI,
+	SCIF6_BRI, SCIF6_ERI, SCIF6_RXI, SCIF6_TXI,
+	SCIF7_BRI, SCIF7_ERI, SCIF7_RXI, SCIF7_TXI,
+	RCAN0, RCAN1, RCAN2,
+	RSPIC0, RSPIC1,
+	IEBC, CD_ROMD,
+	NFMC,
+	SDHI0, SDHI1,
+	RTC,
+	SRCC0, SRCC1, SRCC2,
+
+	/* interrupt groups */
+	PINT, SCIF0, SCIF1, SCIF2, SCIF3, SCIF4, SCIF5, SCIF6, SCIF7,
+};
+
+static struct intc_vect vectors[] __initdata = {
+	INTC_IRQ(IRQ0, 64), INTC_IRQ(IRQ1, 65),
+	INTC_IRQ(IRQ2, 66), INTC_IRQ(IRQ3, 67),
+	INTC_IRQ(IRQ4, 68), INTC_IRQ(IRQ5, 69),
+	INTC_IRQ(IRQ6, 70), INTC_IRQ(IRQ7, 71),
+
+	INTC_IRQ(PINT0, 80), INTC_IRQ(PINT1, 81),
+	INTC_IRQ(PINT2, 82), INTC_IRQ(PINT3, 83),
+	INTC_IRQ(PINT4, 84), INTC_IRQ(PINT5, 85),
+	INTC_IRQ(PINT6, 86), INTC_IRQ(PINT7, 87),
+
+	INTC_IRQ(DMAC0, 108), INTC_IRQ(DMAC0, 109),
+	INTC_IRQ(DMAC1, 112), INTC_IRQ(DMAC1, 113),
+	INTC_IRQ(DMAC2, 116), INTC_IRQ(DMAC2, 117),
+	INTC_IRQ(DMAC3, 120), INTC_IRQ(DMAC3, 121),
+	INTC_IRQ(DMAC4, 124), INTC_IRQ(DMAC4, 125),
+	INTC_IRQ(DMAC5, 128), INTC_IRQ(DMAC5, 129),
+	INTC_IRQ(DMAC6, 132), INTC_IRQ(DMAC6, 133),
+	INTC_IRQ(DMAC7, 136), INTC_IRQ(DMAC7, 137),
+	INTC_IRQ(DMAC8, 140), INTC_IRQ(DMAC8, 141),
+	INTC_IRQ(DMAC9, 144), INTC_IRQ(DMAC9, 145),
+	INTC_IRQ(DMAC10, 148), INTC_IRQ(DMAC10, 149),
+	INTC_IRQ(DMAC11, 152), INTC_IRQ(DMAC11, 153),
+	INTC_IRQ(DMAC12, 156), INTC_IRQ(DMAC12, 157),
+	INTC_IRQ(DMAC13, 160), INTC_IRQ(DMAC13, 161),
+	INTC_IRQ(DMAC14, 164), INTC_IRQ(DMAC14, 165),
+	INTC_IRQ(DMAC15, 168), INTC_IRQ(DMAC15, 169),
+
+	INTC_IRQ(USB, 170),
+
+	INTC_IRQ(VDC4, 171), INTC_IRQ(VDC4, 172),
+	INTC_IRQ(VDC4, 173), INTC_IRQ(VDC4, 174),
+	INTC_IRQ(VDC4, 175), INTC_IRQ(VDC4, 176),
+	INTC_IRQ(VDC4, 177), INTC_IRQ(VDC4, 177),
+
+	INTC_IRQ(CMT0, 188), INTC_IRQ(CMT1, 189),
+
+	INTC_IRQ(BSC, 190), INTC_IRQ(WDT, 191),
+
+	INTC_IRQ(MTU0_ABCD, 192), INTC_IRQ(MTU0_ABCD, 193),
+	INTC_IRQ(MTU0_ABCD, 194), INTC_IRQ(MTU0_ABCD, 195),
+	INTC_IRQ(MTU0_VEF, 196), INTC_IRQ(MTU0_VEF, 197),
+	INTC_IRQ(MTU0_VEF, 198),
+	INTC_IRQ(MTU1_AB, 199), INTC_IRQ(MTU1_AB, 200),
+	INTC_IRQ(MTU1_VU, 201), INTC_IRQ(MTU1_VU, 202),
+	INTC_IRQ(MTU2_AB, 203), INTC_IRQ(MTU2_AB, 204),
+	INTC_IRQ(MTU2_VU, 205), INTC_IRQ(MTU2_VU, 206),
+	INTC_IRQ(MTU3_ABCD, 207), INTC_IRQ(MTU3_ABCD, 208),
+	INTC_IRQ(MTU3_ABCD, 209), INTC_IRQ(MTU3_ABCD, 210),
+	INTC_IRQ(MTU3_TCI3V, 211),
+	INTC_IRQ(MTU4_ABCD, 212), INTC_IRQ(MTU4_ABCD, 213),
+	INTC_IRQ(MTU4_ABCD, 214), INTC_IRQ(MTU4_ABCD, 215),
+	INTC_IRQ(MTU4_TCI4V, 216),
+
+	INTC_IRQ(PWMT1, 217), INTC_IRQ(PWMT2, 218),
+
+	INTC_IRQ(ADC_ADI, 223),
+
+	INTC_IRQ(SSIF0, 224), INTC_IRQ(SSIF0, 225),
+	INTC_IRQ(SSIF0, 226),
+	INTC_IRQ(SSII1, 227), INTC_IRQ(SSII1, 228),
+	INTC_IRQ(SSII2, 229), INTC_IRQ(SSII2, 230),
+	INTC_IRQ(SSII3, 231), INTC_IRQ(SSII3, 232),
+	INTC_IRQ(SSII4, 233), INTC_IRQ(SSII4, 234),
+	INTC_IRQ(SSII5, 235), INTC_IRQ(SSII5, 236),
+
+	INTC_IRQ(RSPDIF, 237),
+
+	INTC_IRQ(IIC30, 238), INTC_IRQ(IIC30, 239),
+	INTC_IRQ(IIC30, 240), INTC_IRQ(IIC30, 241),
+	INTC_IRQ(IIC30, 242),
+	INTC_IRQ(IIC31, 243), INTC_IRQ(IIC31, 244),
+	INTC_IRQ(IIC31, 245), INTC_IRQ(IIC31, 246),
+	INTC_IRQ(IIC31, 247),
+	INTC_IRQ(IIC32, 248), INTC_IRQ(IIC32, 249),
+	INTC_IRQ(IIC32, 250), INTC_IRQ(IIC32, 251),
+	INTC_IRQ(IIC32, 252),
+	INTC_IRQ(IIC33, 253), INTC_IRQ(IIC33, 254),
+	INTC_IRQ(IIC33, 255), INTC_IRQ(IIC33, 256),
+	INTC_IRQ(IIC33, 257),
+
+	INTC_IRQ(SCIF0_BRI, 258), INTC_IRQ(SCIF0_ERI, 259),
+	INTC_IRQ(SCIF0_RXI, 260), INTC_IRQ(SCIF0_TXI, 261),
+	INTC_IRQ(SCIF1_BRI, 262), INTC_IRQ(SCIF1_ERI, 263),
+	INTC_IRQ(SCIF1_RXI, 264), INTC_IRQ(SCIF1_TXI, 265),
+	INTC_IRQ(SCIF2_BRI, 266), INTC_IRQ(SCIF2_ERI, 267),
+	INTC_IRQ(SCIF2_RXI, 268), INTC_IRQ(SCIF2_TXI, 269),
+	INTC_IRQ(SCIF3_BRI, 270), INTC_IRQ(SCIF3_ERI, 271),
+	INTC_IRQ(SCIF3_RXI, 272), INTC_IRQ(SCIF3_TXI, 273),
+	INTC_IRQ(SCIF4_BRI, 274), INTC_IRQ(SCIF4_ERI, 275),
+	INTC_IRQ(SCIF4_RXI, 276), INTC_IRQ(SCIF4_TXI, 277),
+	INTC_IRQ(SCIF5_BRI, 278), INTC_IRQ(SCIF5_ERI, 279),
+	INTC_IRQ(SCIF5_RXI, 280), INTC_IRQ(SCIF5_TXI, 281),
+	INTC_IRQ(SCIF6_BRI, 282), INTC_IRQ(SCIF6_ERI, 283),
+	INTC_IRQ(SCIF6_RXI, 284), INTC_IRQ(SCIF6_TXI, 285),
+	INTC_IRQ(SCIF7_BRI, 286), INTC_IRQ(SCIF7_ERI, 287),
+	INTC_IRQ(SCIF7_RXI, 288), INTC_IRQ(SCIF7_TXI, 289),
+
+	INTC_IRQ(RCAN0, 291), INTC_IRQ(RCAN0, 292),
+	INTC_IRQ(RCAN0, 293), INTC_IRQ(RCAN0, 294),
+	INTC_IRQ(RCAN0, 295),
+	INTC_IRQ(RCAN1, 296), INTC_IRQ(RCAN1, 297),
+	INTC_IRQ(RCAN1, 298), INTC_IRQ(RCAN1, 299),
+	INTC_IRQ(RCAN1, 300),
+	INTC_IRQ(RCAN2, 301), INTC_IRQ(RCAN2, 302),
+	INTC_IRQ(RCAN2, 303), INTC_IRQ(RCAN2, 304),
+	INTC_IRQ(RCAN2, 305),
+
+	INTC_IRQ(RSPIC0, 306), INTC_IRQ(RSPIC0, 307),
+	INTC_IRQ(RSPIC0, 308),
+	INTC_IRQ(RSPIC1, 309), INTC_IRQ(RSPIC1, 310),
+	INTC_IRQ(RSPIC1, 311),
+
+	INTC_IRQ(IEBC, 318),
+
+	INTC_IRQ(CD_ROMD, 319), INTC_IRQ(CD_ROMD, 320),
+	INTC_IRQ(CD_ROMD, 321), INTC_IRQ(CD_ROMD, 322),
+	INTC_IRQ(CD_ROMD, 323), INTC_IRQ(CD_ROMD, 324),
+
+	INTC_IRQ(NFMC, 325), INTC_IRQ(NFMC, 326),
+	INTC_IRQ(NFMC, 327), INTC_IRQ(NFMC, 328),
+
+	INTC_IRQ(SDHI0, 332), INTC_IRQ(SDHI0, 333),
+	INTC_IRQ(SDHI0, 334),
+	INTC_IRQ(SDHI1, 335), INTC_IRQ(SDHI1, 336),
+	INTC_IRQ(SDHI1, 337),
+
+	INTC_IRQ(RTC, 338), INTC_IRQ(RTC, 339),
+	INTC_IRQ(RTC, 340),
+
+	INTC_IRQ(SRCC0, 341), INTC_IRQ(SRCC0, 342),
+	INTC_IRQ(SRCC0, 343), INTC_IRQ(SRCC0, 344),
+	INTC_IRQ(SRCC0, 345),
+	INTC_IRQ(SRCC1, 346), INTC_IRQ(SRCC1, 347),
+	INTC_IRQ(SRCC1, 348), INTC_IRQ(SRCC1, 349),
+	INTC_IRQ(SRCC1, 350),
+	INTC_IRQ(SRCC2, 351), INTC_IRQ(SRCC2, 352),
+	INTC_IRQ(SRCC2, 353), INTC_IRQ(SRCC2, 354),
+	INTC_IRQ(SRCC2, 355),
+};
+
+static struct intc_group groups[] __initdata = {
+	INTC_GROUP(PINT, PINT0, PINT1, PINT2, PINT3,
+		   PINT4, PINT5, PINT6, PINT7),
+	INTC_GROUP(SCIF0, SCIF0_BRI, SCIF0_ERI, SCIF0_RXI, SCIF0_TXI),
+	INTC_GROUP(SCIF1, SCIF1_BRI, SCIF1_ERI, SCIF1_RXI, SCIF1_TXI),
+	INTC_GROUP(SCIF2, SCIF2_BRI, SCIF2_ERI, SCIF2_RXI, SCIF2_TXI),
+	INTC_GROUP(SCIF3, SCIF3_BRI, SCIF3_ERI, SCIF3_RXI, SCIF3_TXI),
+	INTC_GROUP(SCIF4, SCIF4_BRI, SCIF4_ERI, SCIF4_RXI, SCIF4_TXI),
+	INTC_GROUP(SCIF5, SCIF5_BRI, SCIF5_ERI, SCIF5_RXI, SCIF5_TXI),
+	INTC_GROUP(SCIF6, SCIF6_BRI, SCIF6_ERI, SCIF6_RXI, SCIF6_TXI),
+	INTC_GROUP(SCIF7, SCIF7_BRI, SCIF7_ERI, SCIF7_RXI, SCIF7_TXI),
+};
+
+static struct intc_prio_reg prio_registers[] __initdata = {
+	{ 0xfffe0818, 0, 16, 4, /* IPR01 */ { IRQ0, IRQ1, IRQ2, IRQ3 } },
+	{ 0xfffe081a, 0, 16, 4, /* IPR02 */ { IRQ4, IRQ5, IRQ6, IRQ7 } },
+	{ 0xfffe0820, 0, 16, 4, /* IPR05 */ { PINT, 0, 0, 0 } },
+	{ 0xfffe0c00, 0, 16, 4, /* IPR06 */ { DMAC0,  DMAC1, DMAC2,  DMAC3 } },
+	{ 0xfffe0c02, 0, 16, 4, /* IPR07 */ { DMAC4,  DMAC5, DMAC6,  DMAC7 } },
+	{ 0xfffe0c04, 0, 16, 4, /* IPR08 */ { DMAC8,  DMAC9,
+					      DMAC10, DMAC11 } },
+	{ 0xfffe0c06, 0, 16, 4, /* IPR09 */ { DMAC12, DMAC13,
+					      DMAC14, DMAC15 } },
+	{ 0xfffe0c08, 0, 16, 4, /* IPR10 */ { USB, VDC4, VDC4, VDC4 } },
+	{ 0xfffe0c0a, 0, 16, 4, /* IPR11 */ { 0, 0, 0, 0 } },
+	{ 0xfffe0c0c, 0, 16, 4, /* IPR12 */ { CMT0, CMT1, BSC, WDT } },
+	{ 0xfffe0c0e, 0, 16, 4, /* IPR13 */ { MTU0_ABCD, MTU0_VEF,
+					      MTU1_AB, MTU1_VU } },
+	{ 0xfffe0c10, 0, 16, 4, /* IPR14 */ { MTU2_AB, MTU2_VU,
+					      MTU3_ABCD, MTU3_TCI3V } },
+	{ 0xfffe0c12, 0, 16, 4, /* IPR15 */ { MTU4_ABCD, MTU4_TCI4V,
+					      PWMT1, PWMT2 } },
+	{ 0xfffe0c14, 0, 16, 4, /* IPR16 */ { 0, 0, 0, 0 } },
+	{ 0xfffe0c16, 0, 16, 4, /* IPR17 */ { ADC_ADI, SSIF0, SSII1, SSII2 } },
+	{ 0xfffe0c18, 0, 16, 4, /* IPR18 */ { SSII3, SSII4, SSII5,  RSPDIF} },
+	{ 0xfffe0c1a, 0, 16, 4, /* IPR19 */ { IIC30, IIC31, IIC32, IIC33 } },
+	{ 0xfffe0c1c, 0, 16, 4, /* IPR20 */ { SCIF0, SCIF1, SCIF2, SCIF3 } },
+	{ 0xfffe0c1e, 0, 16, 4, /* IPR21 */ { SCIF4, SCIF5, SCIF6, SCIF7 } },
+	{ 0xfffe0c20, 0, 16, 4, /* IPR22 */ { 0, RCAN0, RCAN1, RCAN2 } },
+	{ 0xfffe0c22, 0, 16, 4, /* IPR23 */ { RSPIC0, RSPIC1, 0, 0 } },
+	{ 0xfffe0c24, 0, 16, 4, /* IPR24 */ { IEBC, CD_ROMD, NFMC, 0 } },
+	{ 0xfffe0c26, 0, 16, 4, /* IPR25 */ { SDHI0, SDHI1, RTC, 0 } },
+	{ 0xfffe0c28, 0, 16, 4, /* IPR26 */ { SRCC0, SRCC1, SRCC2, 0 } },
+};
+
+static struct intc_mask_reg mask_registers[] __initdata = {
+	{ 0xfffe0808, 0, 16, /* PINTER */
+	  { 0, 0, 0, 0, 0, 0, 0, 0,
+	    PINT7, PINT6, PINT5, PINT4, PINT3, PINT2, PINT1, PINT0 } },
+};
+
+static DECLARE_INTC_DESC(intc_desc, "sh7269", vectors, groups,
+			 mask_registers, prio_registers, NULL);
+
+static struct plat_sci_port scif0_platform_data = {
+	.mapbase	= 0xe8007000,
+	.flags		= UPF_BOOT_AUTOCONF,
+	.scscr		= SCSCR_RIE | SCSCR_TIE | SCSCR_RE | SCSCR_TE |
+			  SCSCR_REIE | SCSCR_TOIE,
+	.scbrr_algo_id	= SCBRR_ALGO_2,
+	.type		= PORT_SCIF,
+	.irqs		=  { 259, 260, 261, 258 },
+	.regtype	= SCIx_SH2_SCIF_FIFODATA_REGTYPE,
+};
+
+static struct platform_device scif0_device = {
+	.name		= "sh-sci",
+	.id		= 0,
+	.dev		= {
+		.platform_data	= &scif0_platform_data,
+	},
+};
+
+static struct plat_sci_port scif1_platform_data = {
+	.mapbase	= 0xe8007800,
+	.flags		= UPF_BOOT_AUTOCONF,
+	.scscr		= SCSCR_RIE | SCSCR_TIE | SCSCR_RE | SCSCR_TE |
+			  SCSCR_REIE | SCSCR_TOIE,
+	.scbrr_algo_id	= SCBRR_ALGO_2,
+	.type		= PORT_SCIF,
+	.irqs		=  { 263, 264, 265, 262 },
+	.regtype	= SCIx_SH2_SCIF_FIFODATA_REGTYPE,
+};
+
+static struct platform_device scif1_device = {
+	.name		= "sh-sci",
+	.id		= 1,
+	.dev		= {
+		.platform_data	= &scif1_platform_data,
+	},
+};
+
+static struct plat_sci_port scif2_platform_data = {
+	.mapbase	= 0xe8008000,
+	.flags		= UPF_BOOT_AUTOCONF,
+	.scscr		= SCSCR_RIE | SCSCR_TIE | SCSCR_RE | SCSCR_TE |
+			  SCSCR_REIE | SCSCR_TOIE,
+	.scbrr_algo_id	= SCBRR_ALGO_2,
+	.type		= PORT_SCIF,
+	.irqs		=  { 267, 268, 269, 266 },
+	.regtype	= SCIx_SH2_SCIF_FIFODATA_REGTYPE,
+};
+
+static struct platform_device scif2_device = {
+	.name		= "sh-sci",
+	.id		= 2,
+	.dev		= {
+		.platform_data	= &scif2_platform_data,
+	},
+};
+
+static struct plat_sci_port scif3_platform_data = {
+	.mapbase	= 0xe8008800,
+	.flags		= UPF_BOOT_AUTOCONF,
+	.scscr		= SCSCR_RIE | SCSCR_TIE | SCSCR_RE | SCSCR_TE |
+			  SCSCR_REIE | SCSCR_TOIE,
+	.scbrr_algo_id	= SCBRR_ALGO_2,
+	.type		= PORT_SCIF,
+	.irqs		=  { 271, 272, 273, 270 },
+	.regtype	= SCIx_SH2_SCIF_FIFODATA_REGTYPE,
+};
+
+static struct platform_device scif3_device = {
+	.name		= "sh-sci",
+	.id		= 3,
+	.dev		= {
+		.platform_data	= &scif3_platform_data,
+	},
+};
+
+static struct plat_sci_port scif4_platform_data = {
+	.mapbase	= 0xe8009000,
+	.flags		= UPF_BOOT_AUTOCONF,
+	.scscr		= SCSCR_RIE | SCSCR_TIE | SCSCR_RE | SCSCR_TE |
+			  SCSCR_REIE | SCSCR_TOIE,
+	.scbrr_algo_id	= SCBRR_ALGO_2,
+	.type		= PORT_SCIF,
+	.irqs		=  { 275, 276, 277, 274 },
+	.regtype	= SCIx_SH2_SCIF_FIFODATA_REGTYPE,
+};
+
+static struct platform_device scif4_device = {
+	.name		= "sh-sci",
+	.id		= 4,
+	.dev		= {
+		.platform_data	= &scif4_platform_data,
+	},
+};
+
+static struct plat_sci_port scif5_platform_data = {
+	.mapbase	= 0xe8009800,
+	.flags		= UPF_BOOT_AUTOCONF,
+	.scscr		= SCSCR_RIE | SCSCR_TIE | SCSCR_RE | SCSCR_TE |
+			  SCSCR_REIE | SCSCR_TOIE,
+	.scbrr_algo_id	= SCBRR_ALGO_2,
+	.type		= PORT_SCIF,
+	.irqs		=  { 279, 280, 281, 278 },
+	.regtype	= SCIx_SH2_SCIF_FIFODATA_REGTYPE,
+};
+
+static struct platform_device scif5_device = {
+	.name		= "sh-sci",
+	.id		= 5,
+	.dev		= {
+		.platform_data	= &scif5_platform_data,
+	},
+};
+
+static struct plat_sci_port scif6_platform_data = {
+	.mapbase	= 0xe800a000,
+	.flags		= UPF_BOOT_AUTOCONF,
+	.scscr		= SCSCR_RIE | SCSCR_TIE | SCSCR_RE | SCSCR_TE |
+			  SCSCR_REIE | SCSCR_TOIE,
+	.scbrr_algo_id	= SCBRR_ALGO_2,
+	.type		= PORT_SCIF,
+	.irqs		=  { 283, 284, 285, 282 },
+	.regtype	= SCIx_SH2_SCIF_FIFODATA_REGTYPE,
+};
+
+static struct platform_device scif6_device = {
+	.name		= "sh-sci",
+	.id		= 6,
+	.dev		= {
+		.platform_data	= &scif6_platform_data,
+	},
+};
+
+static struct plat_sci_port scif7_platform_data = {
+	.mapbase	= 0xe800a800,
+	.flags		= UPF_BOOT_AUTOCONF,
+	.scscr		= SCSCR_RIE | SCSCR_TIE | SCSCR_RE | SCSCR_TE |
+			  SCSCR_REIE | SCSCR_TOIE,
+	.scbrr_algo_id	= SCBRR_ALGO_2,
+	.type		= PORT_SCIF,
+	.irqs		=  { 287, 288, 289, 286 },
+	.regtype	= SCIx_SH2_SCIF_FIFODATA_REGTYPE,
+};
+
+static struct platform_device scif7_device = {
+	.name		= "sh-sci",
+	.id		= 7,
+	.dev		= {
+		.platform_data	= &scif7_platform_data,
+	},
+};
+
+static struct sh_timer_config cmt0_platform_data = {
+	.channel_offset = 0x02,
+	.timer_bit = 0,
+	.clockevent_rating = 125,
+	.clocksource_rating = 0, /* disabled due to code generation issues */
+};
+
+static struct resource cmt0_resources[] = {
+	[0] = {
+		.start	= 0xfffec002,
+		.end	= 0xfffec007,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= 188,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device cmt0_device = {
+	.name		= "sh_cmt",
+	.id		= 0,
+	.dev = {
+		.platform_data	= &cmt0_platform_data,
+	},
+	.resource	= cmt0_resources,
+	.num_resources	= ARRAY_SIZE(cmt0_resources),
+};
+
+static struct sh_timer_config cmt1_platform_data = {
+	.channel_offset = 0x08,
+	.timer_bit = 1,
+	.clockevent_rating = 125,
+	.clocksource_rating = 0, /* disabled due to code generation issues */
+};
+
+static struct resource cmt1_resources[] = {
+	[0] = {
+		.start	= 0xfffec008,
+		.end	= 0xfffec00d,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= 189,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device cmt1_device = {
+	.name		= "sh_cmt",
+	.id		= 1,
+	.dev = {
+		.platform_data	= &cmt1_platform_data,
+	},
+	.resource	= cmt1_resources,
+	.num_resources	= ARRAY_SIZE(cmt1_resources),
+};
+
+static struct sh_timer_config mtu2_0_platform_data = {
+	.channel_offset = -0x80,
+	.timer_bit = 0,
+	.clockevent_rating = 200,
+};
+
+static struct resource mtu2_0_resources[] = {
+	[0] = {
+		.start	= 0xfffe4300,
+		.end	= 0xfffe4326,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= 192,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device mtu2_0_device = {
+	.name		= "sh_mtu2",
+	.id		= 0,
+	.dev = {
+		.platform_data	= &mtu2_0_platform_data,
+	},
+	.resource	= mtu2_0_resources,
+	.num_resources	= ARRAY_SIZE(mtu2_0_resources),
+};
+
+static struct sh_timer_config mtu2_1_platform_data = {
+	.channel_offset = -0x100,
+	.timer_bit = 1,
+	.clockevent_rating = 200,
+};
+
+static struct resource mtu2_1_resources[] = {
+	[0] = {
+		.start	= 0xfffe4380,
+		.end	= 0xfffe4390,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= 203,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device mtu2_1_device = {
+	.name		= "sh_mtu2",
+	.id		= 1,
+	.dev = {
+		.platform_data	= &mtu2_1_platform_data,
+	},
+	.resource	= mtu2_1_resources,
+	.num_resources	= ARRAY_SIZE(mtu2_1_resources),
+};
+
+static struct resource rtc_resources[] = {
+	[0] = {
+		.start	= 0xfffe6000,
+		.end	= 0xfffe6000 + 0x30 - 1,
+		.flags	= IORESOURCE_IO,
+	},
+	[1] = {
+		/* Shared Period/Carry/Alarm IRQ */
+		.start	= 338,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device rtc_device = {
+	.name		= "sh-rtc",
+	.id		= -1,
+	.num_resources	= ARRAY_SIZE(rtc_resources),
+	.resource	= rtc_resources,
+};
+
+/* USB Host */
+static struct r8a66597_platdata r8a66597_data = {
+	.on_chip = 1,
+	.endian = 1,
+};
+
+static struct resource r8a66597_usb_host_resources[] = {
+	[0] = {
+		.start	= 0xe8010000,
+		.end	= 0xe80100e4,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= 170,
+		.end	= 170,
+		.flags	= IORESOURCE_IRQ | IRQF_TRIGGER_LOW,
+	},
+};
+
+static struct platform_device r8a66597_usb_host_device = {
+	.name		= "r8a66597_hcd",
+	.id		= 0,
+	.dev = {
+		.dma_mask		= NULL,         /*  not use dma */
+		.coherent_dma_mask	= 0xffffffff,
+		.platform_data		= &r8a66597_data,
+	},
+	.num_resources	= ARRAY_SIZE(r8a66597_usb_host_resources),
+	.resource	= r8a66597_usb_host_resources,
+};
+
+static struct platform_device *sh7269_devices[] __initdata = {
+	&scif0_device,
+	&scif1_device,
+	&scif2_device,
+	&scif3_device,
+	&scif4_device,
+	&scif5_device,
+	&scif6_device,
+	&scif7_device,
+	&cmt0_device,
+	&cmt1_device,
+	&mtu2_0_device,
+	&mtu2_1_device,
+	&rtc_device,
+	&r8a66597_usb_host_device,
+};
+
+static int __init sh7269_devices_setup(void)
+{
+	return platform_add_devices(sh7269_devices,
+				    ARRAY_SIZE(sh7269_devices));
+}
+arch_initcall(sh7269_devices_setup);
+
+void __init plat_irq_setup(void)
+{
+	register_intc_controller(&intc_desc);
+}
+
+static struct platform_device *sh7269_early_devices[] __initdata = {
+	&scif0_device,
+	&scif1_device,
+	&scif2_device,
+	&scif3_device,
+	&scif4_device,
+	&scif5_device,
+	&scif6_device,
+	&scif7_device,
+	&cmt0_device,
+	&cmt1_device,
+	&mtu2_0_device,
+	&mtu2_1_device,
+};
+
+void __init plat_early_device_setup(void)
+{
+	early_platform_add_devices(sh7269_early_devices,
+				   ARRAY_SIZE(sh7269_early_devices));
+}
diff --git a/arch/sh/kernel/cpu/sh3/entry.S b/arch/sh/kernel/cpu/sh3/entry.S
index f6a389c..262db6e 100644
--- a/arch/sh/kernel/cpu/sh3/entry.S
+++ b/arch/sh/kernel/cpu/sh3/entry.S
@@ -2,7 +2,7 @@
  * arch/sh/kernel/cpu/sh3/entry.S
  *
  *  Copyright (C) 1999, 2000, 2002  Niibe Yutaka
- *  Copyright (C) 2003 - 2006  Paul Mundt
+ *  Copyright (C) 2003 - 2012  Paul Mundt
  *
  * This file is subject to the terms and conditions of the GNU General Public
  * License.  See the file "COPYING" in the main directory of this archive
@@ -17,6 +17,7 @@
 #include <cpu/mmu_context.h>
 #include <asm/page.h>
 #include <asm/cache.h>
+#include <asm/thread_info.h>
 
 ! NOTE:
 ! GNU as (as of 2.9.1) changes bf/s into bt/s and bra, when the address
@@ -114,22 +115,22 @@
 	.align	2
 ENTRY(tlb_miss_store)
 	bra	call_handle_tlbmiss
-	 mov	#1, r5
+	 mov	#FAULT_CODE_WRITE, r5
 
 	.align	2
 ENTRY(initial_page_write)
 	bra	call_handle_tlbmiss
-	 mov	#2, r5
+	 mov	#FAULT_CODE_INITIAL, r5
 
 	.align	2
 ENTRY(tlb_protection_violation_load)
 	bra	call_do_page_fault
-	 mov	#0, r5
+	 mov	#FAULT_CODE_PROT, r5
 
 	.align	2
 ENTRY(tlb_protection_violation_store)
 	bra	call_do_page_fault
-	 mov	#1, r5
+	 mov	#(FAULT_CODE_PROT | FAULT_CODE_WRITE), r5
 
 call_handle_tlbmiss:
 	mov.l	1f, r0
diff --git a/arch/sh/kernel/cpu/sh3/setup-sh7705.c b/arch/sh/kernel/cpu/sh3/setup-sh7705.c
index 2309618..03e4c96 100644
--- a/arch/sh/kernel/cpu/sh3/setup-sh7705.c
+++ b/arch/sh/kernel/cpu/sh3/setup-sh7705.c
@@ -14,6 +14,7 @@
 #include <linux/serial.h>
 #include <linux/serial_sci.h>
 #include <linux/sh_timer.h>
+#include <linux/sh_intc.h>
 #include <asm/rtc.h>
 #include <cpu/serial.h>
 
@@ -75,7 +76,7 @@
 			  SCSCR_RE  | SCSCR_CKE1 | SCSCR_CKE0,
 	.scbrr_algo_id	= SCBRR_ALGO_4,
 	.type		= PORT_SCIF,
-	.irqs		= { 56, 56, 56 },
+	.irqs		= SCIx_IRQ_MUXED(evt2irq(0x900)),
 	.ops		= &sh770x_sci_port_ops,
 	.regtype	= SCIx_SH7705_SCIF_REGTYPE,
 };
@@ -94,7 +95,7 @@
 	.scscr		= SCSCR_TIE | SCSCR_RIE | SCSCR_TE | SCSCR_RE,
 	.scbrr_algo_id	= SCBRR_ALGO_4,
 	.type		= PORT_SCIF,
-	.irqs		= { 52, 52, 52 },
+	.irqs		= SCIx_IRQ_MUXED(evt2irq(0x880)),
 	.ops		= &sh770x_sci_port_ops,
 	.regtype	= SCIx_SH7705_SCIF_REGTYPE,
 };
@@ -114,7 +115,7 @@
 		.flags  = IORESOURCE_IO,
 	},
 	[1] =	{
-		.start  = 20,
+		.start  = evt2irq(0x480),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -146,7 +147,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 16,
+		.start	= evt2irq(0x400),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -174,7 +175,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 17,
+		.start	= evt2irq(0x420),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -201,7 +202,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 18,
+		.start	= evt2irq(0x440),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
diff --git a/arch/sh/kernel/cpu/sh3/setup-sh770x.c b/arch/sh/kernel/cpu/sh3/setup-sh770x.c
index 3f3d5fe..ba26cd9 100644
--- a/arch/sh/kernel/cpu/sh3/setup-sh770x.c
+++ b/arch/sh/kernel/cpu/sh3/setup-sh770x.c
@@ -19,6 +19,7 @@
 #include <linux/serial.h>
 #include <linux/serial_sci.h>
 #include <linux/sh_timer.h>
+#include <linux/sh_intc.h>
 #include <cpu/serial.h>
 
 enum {
@@ -95,7 +96,7 @@
 		.flags  = IORESOURCE_IO,
 	},
 	[1] =	{
-		.start	= 20,
+		.start	= evt2irq(0x480),
 		.flags  = IORESOURCE_IRQ,
 	},
 };
@@ -114,7 +115,7 @@
 	.scscr		= SCSCR_TE | SCSCR_RE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCI,
-	.irqs		= { 23, 23, 23, 0 },
+	.irqs		= SCIx_IRQ_MUXED(evt2irq(0x4e0)),
 	.ops		= &sh770x_sci_port_ops,
 	.regshift	= 1,
 };
@@ -135,7 +136,7 @@
 	.scscr		= SCSCR_TE | SCSCR_RE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCIF,
-	.irqs		= { 56, 56, 56, 56 },
+	.irqs		= SCIx_IRQ_MUXED(evt2irq(0x900)),
 	.ops		= &sh770x_sci_port_ops,
 	.regtype	= SCIx_SH3_SCIF_REGTYPE,
 };
@@ -157,7 +158,7 @@
 	.scscr		= SCSCR_TE | SCSCR_RE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_IRDA,
-	.irqs		= { 52, 52, 52, 52 },
+	.irqs		= SCIx_IRQ_MUXED(evt2irq(0x880)),
 	.ops		= &sh770x_sci_port_ops,
 	.regshift	= 1,
 };
@@ -184,7 +185,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 16,
+		.start	= evt2irq(0x400),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -212,7 +213,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 17,
+		.start	= evt2irq(0x420),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -239,7 +240,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 18,
+		.start	= evt2irq(0x440),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
diff --git a/arch/sh/kernel/cpu/sh3/setup-sh7710.c b/arch/sh/kernel/cpu/sh3/setup-sh7710.c
index 78f6b01..93c9c5e 100644
--- a/arch/sh/kernel/cpu/sh3/setup-sh7710.c
+++ b/arch/sh/kernel/cpu/sh3/setup-sh7710.c
@@ -14,6 +14,7 @@
 #include <linux/serial.h>
 #include <linux/serial_sci.h>
 #include <linux/sh_timer.h>
+#include <linux/sh_intc.h>
 #include <asm/rtc.h>
 
 enum {
@@ -77,7 +78,7 @@
 		.flags  = IORESOURCE_IO,
 	},
 	[1] =	{
-		.start  = 20,
+		.start  = evt2irq(0x480),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -103,7 +104,7 @@
 			  SCSCR_CKE1 | SCSCR_CKE0,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCIF,
-	.irqs		= { 52, 52, 52, 52 },
+	.irqs		= SCIx_IRQ_MUXED(evt2irq(0x880)),
 };
 
 static struct platform_device scif0_device = {
@@ -121,7 +122,7 @@
 			  SCSCR_CKE1 | SCSCR_CKE0,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCIF,
-	.irqs           = { 56, 56, 56, 56 },
+	.irqs           = SCIx_IRQ_MUXED(evt2irq(0x900)),
 };
 
 static struct platform_device scif1_device = {
@@ -145,7 +146,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 16,
+		.start	= evt2irq(0x400),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -173,7 +174,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 17,
+		.start	= evt2irq(0x420),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -200,7 +201,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 18,
+		.start	= evt2irq(0x440),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
diff --git a/arch/sh/kernel/cpu/sh3/setup-sh7720.c b/arch/sh/kernel/cpu/sh3/setup-sh7720.c
index 9492034..0c2f1b2 100644
--- a/arch/sh/kernel/cpu/sh3/setup-sh7720.c
+++ b/arch/sh/kernel/cpu/sh3/setup-sh7720.c
@@ -19,6 +19,7 @@
 #include <linux/io.h>
 #include <linux/serial_sci.h>
 #include <linux/sh_timer.h>
+#include <linux/sh_intc.h>
 #include <asm/rtc.h>
 #include <cpu/serial.h>
 
@@ -30,7 +31,7 @@
 	},
 	[1] = {
 		/* Shared Period/Carry/Alarm IRQ */
-		.start	= 20,
+		.start	= evt2irq(0x480),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -55,7 +56,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE,
 	.scbrr_algo_id	= SCBRR_ALGO_4,
 	.type		= PORT_SCIF,
-	.irqs		= { 80, 80, 80, 80 },
+	.irqs		= SCIx_IRQ_MUXED(evt2irq(0xc00)),
 	.ops		= &sh7720_sci_port_ops,
 	.regtype	= SCIx_SH7705_SCIF_REGTYPE,
 };
@@ -74,7 +75,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE,
 	.scbrr_algo_id	= SCBRR_ALGO_4,
 	.type		= PORT_SCIF,
-	.irqs           = { 81, 81, 81, 81 },
+	.irqs           = SCIx_IRQ_MUXED(evt2irq(0xc20)),
 	.ops		= &sh7720_sci_port_ops,
 	.regtype	= SCIx_SH7705_SCIF_REGTYPE,
 };
@@ -94,13 +95,14 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 67,
-		.end	= 67,
+		.start	= evt2irq(0xa60),
+		.end	= evt2irq(0xa60),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
 
 static u64 usb_ohci_dma_mask = 0xffffffffUL;
+
 static struct platform_device usb_ohci_device = {
 	.name		= "sh_ohci",
 	.id		= -1,
@@ -121,8 +123,8 @@
 	},
 	[1] = {
 		.name	= "sh_udc",
-		.start	= 65,
-		.end	= 65,
+		.start	= evt2irq(0xa20),
+		.end	= evt2irq(0xa20),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -152,7 +154,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 104,
+		.start	= evt2irq(0xf00),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -179,7 +181,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 104,
+		.start	= evt2irq(0xf00),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -206,7 +208,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 104,
+		.start	= evt2irq(0xf00),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -233,7 +235,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 104,
+		.start	= evt2irq(0xf00),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -260,7 +262,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 104,
+		.start	= evt2irq(0xf00),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -288,7 +290,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 16,
+		.start	= evt2irq(0x400),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -316,7 +318,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 17,
+		.start	= evt2irq(0x420),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -343,7 +345,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 18,
+		.start	= evt2irq(0x440),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
diff --git a/arch/sh/kernel/cpu/sh4/probe.c b/arch/sh/kernel/cpu/sh4/probe.c
index 971cf0f..0fbbd50 100644
--- a/arch/sh/kernel/cpu/sh4/probe.c
+++ b/arch/sh/kernel/cpu/sh4/probe.c
@@ -158,6 +158,9 @@
 		case 0x40: /* yon-ten-go */
 			boot_cpu_data.type = CPU_SH7372;
 			break;
+		case 0xE0: /* 0x4E0 */
+			boot_cpu_data.type = CPU_SH7734; /* SH7733/SH7734 */
+			break;
 
 		}
 		break;
diff --git a/arch/sh/kernel/cpu/sh4/setup-sh4-202.c b/arch/sh/kernel/cpu/sh4/setup-sh4-202.c
index 5b28331..2a5320a 100644
--- a/arch/sh/kernel/cpu/sh4/setup-sh4-202.c
+++ b/arch/sh/kernel/cpu/sh4/setup-sh4-202.c
@@ -13,6 +13,7 @@
 #include <linux/serial.h>
 #include <linux/serial_sci.h>
 #include <linux/sh_timer.h>
+#include <linux/sh_intc.h>
 #include <linux/io.h>
 
 static struct plat_sci_port scif0_platform_data = {
@@ -21,7 +22,10 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCIF,
-	.irqs		= { 40, 41, 43, 42 },
+	.irqs		= { evt2irq(0x700),
+			    evt2irq(0x720),
+			    evt2irq(0x760),
+			    evt2irq(0x740) },
 };
 
 static struct platform_device scif0_device = {
@@ -45,7 +49,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 16,
+		.start	= evt2irq(0x400),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -73,7 +77,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 17,
+		.start	= evt2irq(0x420),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -100,7 +104,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 18,
+		.start	= evt2irq(0x440),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
diff --git a/arch/sh/kernel/cpu/sh4/setup-sh7750.c b/arch/sh/kernel/cpu/sh4/setup-sh7750.c
index 98cc0c7..04a4551 100644
--- a/arch/sh/kernel/cpu/sh4/setup-sh7750.c
+++ b/arch/sh/kernel/cpu/sh4/setup-sh7750.c
@@ -13,6 +13,7 @@
 #include <linux/serial.h>
 #include <linux/io.h>
 #include <linux/sh_timer.h>
+#include <linux/sh_intc.h>
 #include <linux/serial_sci.h>
 #include <generated/machtypes.h>
 
@@ -24,7 +25,7 @@
 	},
 	[1] = {
 		/* Shared Period/Carry/Alarm IRQ */
-		.start	= 20,
+		.start	= evt2irq(0x480),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -43,7 +44,7 @@
 	.scscr		= SCSCR_TE | SCSCR_RE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCI,
-	.irqs		= { 23, 23, 23, 0 },
+	.irqs		= SCIx_IRQ_MUXED(evt2irq(0x4e0)),
 	.regshift	= 2,
 };
 
@@ -61,7 +62,7 @@
 	.scscr		= SCSCR_TE | SCSCR_RE | SCSCR_REIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCIF,
-	.irqs		= { 40, 40, 40, 40 },
+	.irqs		= SCIx_IRQ_MUXED(evt2irq(0x700)),
 };
 
 static struct platform_device scif_device = {
@@ -85,7 +86,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 16,
+		.start	= evt2irq(0x400),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -113,7 +114,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 17,
+		.start	= evt2irq(0x420),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -140,7 +141,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 18,
+		.start	= evt2irq(0x440),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -172,7 +173,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 72,
+		.start	= evt2irq(0xb00),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -199,7 +200,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 76,
+		.start	= evt2irq(0xb80),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
diff --git a/arch/sh/kernel/cpu/sh4/setup-sh7760.c b/arch/sh/kernel/cpu/sh4/setup-sh7760.c
index c0b4c77..98e075a 100644
--- a/arch/sh/kernel/cpu/sh4/setup-sh7760.c
+++ b/arch/sh/kernel/cpu/sh4/setup-sh7760.c
@@ -11,6 +11,7 @@
 #include <linux/init.h>
 #include <linux/serial.h>
 #include <linux/sh_timer.h>
+#include <linux/sh_intc.h>
 #include <linux/serial_sci.h>
 #include <linux/io.h>
 
@@ -132,7 +133,10 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCIF,
-	.irqs		= { 52, 53, 55, 54 },
+	.irqs		= { evt2irq(0x880),
+			    evt2irq(0x8a0),
+			    evt2irq(0x8e0),
+			    evt2irq(0x8c0) },
 	.regtype	= SCIx_SH4_SCIF_FIFODATA_REGTYPE,
 };
 
@@ -150,7 +154,10 @@
 	.type		= PORT_SCIF,
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
-	.irqs		= { 72, 73, 75, 74 },
+	.irqs		= { evt2irq(0xb00),
+			    evt2irq(0xb20),
+			    evt2irq(0xb60),
+			    evt2irq(0xb40) },
 	.regtype	= SCIx_SH4_SCIF_FIFODATA_REGTYPE,
 };
 
@@ -168,7 +175,10 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCIF,
-	.irqs		= { 76, 77, 79, 78 },
+	.irqs		= { evt2irq(0xb80),
+			    evt2irq(0xba0),
+			    evt2irq(0xbe0),
+			    evt2irq(0xbc0) },
 	.regtype	= SCIx_SH4_SCIF_FIFODATA_REGTYPE,
 };
 
@@ -186,7 +196,9 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCI,
-	.irqs		= { 80, 81, 82, 0 },
+	.irqs		= { evt2irq(0xc00),
+			    evt2irq(0xc20),
+			    evt2irq(0xc40), },
 	.regshift	= 2,
 };
 
@@ -211,7 +223,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 16,
+		.start	= evt2irq(0x400),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -239,7 +251,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 17,
+		.start	= evt2irq(0x420),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -266,7 +278,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 18,
+		.start	= evt2irq(0x440),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
diff --git a/arch/sh/kernel/cpu/sh4a/Makefile b/arch/sh/kernel/cpu/sh4a/Makefile
index 0b22d10..8fc6ec2 100644
--- a/arch/sh/kernel/cpu/sh4a/Makefile
+++ b/arch/sh/kernel/cpu/sh4a/Makefile
@@ -13,6 +13,7 @@
 obj-$(CONFIG_CPU_SUBTYPE_SH7722)	+= setup-sh7722.o serial-sh7722.o
 obj-$(CONFIG_CPU_SUBTYPE_SH7723)	+= setup-sh7723.o
 obj-$(CONFIG_CPU_SUBTYPE_SH7724)	+= setup-sh7724.o
+obj-$(CONFIG_CPU_SUBTYPE_SH7734)	+= setup-sh7734.o
 obj-$(CONFIG_CPU_SUBTYPE_SH7366)	+= setup-sh7366.o
 obj-$(CONFIG_CPU_SUBTYPE_SHX3)		+= setup-shx3.o intc-shx3.o
 
@@ -30,6 +31,7 @@
 clock-$(CONFIG_CPU_SUBTYPE_SH7722)	:= clock-sh7722.o
 clock-$(CONFIG_CPU_SUBTYPE_SH7723)	:= clock-sh7723.o
 clock-$(CONFIG_CPU_SUBTYPE_SH7724)	:= clock-sh7724.o
+clock-$(CONFIG_CPU_SUBTYPE_SH7734)	:= clock-sh7734.o
 clock-$(CONFIG_CPU_SUBTYPE_SH7366)	:= clock-sh7366.o
 clock-$(CONFIG_CPU_SUBTYPE_SHX3)	:= clock-shx3.o
 
@@ -37,6 +39,7 @@
 pinmux-$(CONFIG_CPU_SUBTYPE_SH7722)	:= pinmux-sh7722.o
 pinmux-$(CONFIG_CPU_SUBTYPE_SH7723)	:= pinmux-sh7723.o
 pinmux-$(CONFIG_CPU_SUBTYPE_SH7724)	:= pinmux-sh7724.o
+pinmux-$(CONFIG_CPU_SUBTYPE_SH7734)	:= pinmux-sh7734.o
 pinmux-$(CONFIG_CPU_SUBTYPE_SH7757)	:= pinmux-sh7757.o
 pinmux-$(CONFIG_CPU_SUBTYPE_SH7785)	:= pinmux-sh7785.o
 pinmux-$(CONFIG_CPU_SUBTYPE_SH7786)	:= pinmux-sh7786.o
diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7734.c b/arch/sh/kernel/cpu/sh4a/clock-sh7734.c
new file mode 100644
index 0000000..1697642
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh4a/clock-sh7734.c
@@ -0,0 +1,266 @@
+/*
+ * arch/sh/kernel/cpu/sh4a/clock-sh7734.c
+ *
+ * Clock framework for SH7734
+ *
+ * Copyright (C) 2011, 2012 Nobuhiro Iwamatsu <nobuhiro.iwamatsu.yj@renesas.com>
+ * Copyright (C) 2011, 2012 Renesas Solutions Corp.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/clkdev.h>
+#include <linux/delay.h>
+#include <asm/clock.h>
+#include <asm/freq.h>
+
+static struct clk extal_clk = {
+	.rate       = 33333333,
+};
+
+#define MODEMR          (0xFFCC0020)
+#define MODEMR_MASK     (0x6)
+#define MODEMR_533MHZ   (0x2)
+
+static unsigned long pll_recalc(struct clk *clk)
+{
+	int mode = 12;
+	u32 r = __raw_readl(MODEMR);
+
+	if ((r & MODEMR_MASK) & MODEMR_533MHZ)
+		mode = 16;
+
+	return clk->parent->rate * mode;
+}
+
+static struct sh_clk_ops pll_clk_ops = {
+	.recalc		= pll_recalc,
+};
+
+static struct clk pll_clk = {
+	.ops        = &pll_clk_ops,
+	.parent     = &extal_clk,
+	.flags      = CLK_ENABLE_ON_INIT,
+};
+
+static struct clk *main_clks[] = {
+	&extal_clk,
+	&pll_clk,
+};
+
+static int multipliers[] = { 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
+static int divisors[] = { 1, 3, 2, 3, 4, 6, 8, 9, 12, 16, 18, 24 };
+
+static struct clk_div_mult_table div4_div_mult_table = {
+	.divisors = divisors,
+	.nr_divisors = ARRAY_SIZE(divisors),
+	.multipliers = multipliers,
+	.nr_multipliers = ARRAY_SIZE(multipliers),
+};
+
+static struct clk_div4_table div4_table = {
+	.div_mult_table = &div4_div_mult_table,
+};
+
+enum { DIV4_I, DIV4_S, DIV4_B, DIV4_M, DIV4_S1, DIV4_P, DIV4_NR };
+
+#define DIV4(_reg, _bit, _mask, _flags) \
+	SH_CLK_DIV4(&pll_clk, _reg, _bit, _mask, _flags)
+
+struct clk div4_clks[DIV4_NR] = {
+	[DIV4_I] = DIV4(FRQMR1, 28, 0x0003, CLK_ENABLE_ON_INIT),
+	[DIV4_S] = DIV4(FRQMR1, 20, 0x000C, CLK_ENABLE_ON_INIT),
+	[DIV4_B] = DIV4(FRQMR1, 16, 0x0140, CLK_ENABLE_ON_INIT),
+	[DIV4_M] = DIV4(FRQMR1, 12, 0x0004, CLK_ENABLE_ON_INIT),
+	[DIV4_S1] = DIV4(FRQMR1, 4, 0x0030, CLK_ENABLE_ON_INIT),
+	[DIV4_P] = DIV4(FRQMR1, 0, 0x0140, CLK_ENABLE_ON_INIT),
+};
+
+#define MSTPCR0	0xFFC80030
+#define MSTPCR1	0xFFC80034
+#define MSTPCR3	0xFFC8003C
+
+enum {
+	MSTP030, MSTP029, /* IIC */
+	MSTP026, MSTP025, MSTP024, /* SCIF */
+	MSTP023,
+	MSTP022, MSTP021,
+	MSTP019, /* HSCIF */
+	MSTP016, MSTP015, MSTP014, /* TMU / TIMER */
+	MSTP012, MSTP011, MSTP010, MSTP009, MSTP008, /* SSI */
+	MSTP007, /* HSPI */
+	MSTP115, /* ADMAC */
+	MSTP114, /* GETHER */
+	MSTP111, /* DMAC */
+	MSTP109, /* VIDEOIN1 */
+	MSTP108, /* VIDEOIN0 */
+	MSTP107, /* RGPVBG */
+	MSTP106, /* 2DG */
+	MSTP103, /* VIEW */
+	MSTP100, /* USB */
+	MSTP331, /* MMC */
+	MSTP330, /* MIMLB */
+	MSTP323, /* SDHI0 */
+	MSTP322, /* SDHI1 */
+	MSTP321, /* SDHI2 */
+	MSTP320, /* RQSPI */
+	MSTP319, /* SRC0 */
+	MSTP318, /* SRC1 */
+	MSTP317, /* RSPI */
+	MSTP316, /* RCAN0 */
+	MSTP315, /* RCAN1 */
+	MSTP314, /* FLTCL */
+	MSTP313, /* ADC */
+	MSTP312, /* MTU */
+	MSTP304, /* IE-BUS */
+	MSTP303, /* RTC */
+	MSTP302, /* HIF */
+	MSTP301, /* STIF0 */
+	MSTP300, /* STIF1 */
+	MSTP_NR };
+
+static struct clk mstp_clks[MSTP_NR] = {
+	/* MSTPCR0 */
+	[MSTP030] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 30, 0),
+	[MSTP029] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 29, 0),
+	[MSTP026] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 26, 0),
+	[MSTP025] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 25, 0),
+	[MSTP024] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 24, 0),
+	[MSTP023] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 23, 0),
+	[MSTP022] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 22, 0),
+	[MSTP021] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 21, 0),
+	[MSTP019] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 19, 0),
+	[MSTP016] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 16, 0),
+	[MSTP015] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 15, 0),
+	[MSTP014] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 14, 0),
+	[MSTP012] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 12, 0),
+	[MSTP011] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 11, 0),
+	[MSTP010] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 10, 0),
+	[MSTP009] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 9, 0),
+	[MSTP008] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 8, 0),
+	[MSTP007] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 7, 0),
+
+	/* MSTPCR1 */
+	[MSTP115] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR1, 15, 0),
+	[MSTP114] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR1, 14, 0),
+	[MSTP111] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR1, 11, 0),
+	[MSTP109] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR1, 9, 0),
+	[MSTP108] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR1, 8, 0),
+	[MSTP107] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR1, 7, 0),
+	[MSTP106] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR1, 6, 0),
+	[MSTP103] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR1, 3, 0),
+	[MSTP100] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR1, 0, 0),
+
+	/* MSTPCR3 */
+	[MSTP331] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3, 31, 0),
+	[MSTP330] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3, 30, 0),
+	[MSTP323] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3, 23, 0),
+	[MSTP322] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3, 22, 0),
+	[MSTP321] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3, 21, 0),
+	[MSTP320] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3, 20, 0),
+	[MSTP319] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3, 19, 0),
+	[MSTP318] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3, 18, 0),
+	[MSTP317] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3, 17, 0),
+	[MSTP316] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3, 16, 0),
+	[MSTP315] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3, 15, 0),
+	[MSTP314] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3, 14, 0),
+	[MSTP313] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3, 13, 0),
+	[MSTP312] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3, 12, 0),
+	[MSTP304] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3,  4, 0),
+	[MSTP303] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3,  3, 0),
+	[MSTP302] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3,  2, 0),
+	[MSTP301] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3,  1, 0),
+	[MSTP300] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3,  0, 0),
+};
+
+static struct clk_lookup lookups[] = {
+	/* main clocks */
+	CLKDEV_CON_ID("extal", &extal_clk),
+	CLKDEV_CON_ID("pll_clk", &pll_clk),
+
+	/* clocks */
+	CLKDEV_CON_ID("cpu_clk", &div4_clks[DIV4_I]),
+	CLKDEV_CON_ID("shyway_clk", &div4_clks[DIV4_S]),
+	CLKDEV_CON_ID("ddr_clk", &div4_clks[DIV4_M]),
+	CLKDEV_CON_ID("bus_clk", &div4_clks[DIV4_B]),
+	CLKDEV_CON_ID("shyway_clk1", &div4_clks[DIV4_S1]),
+	CLKDEV_CON_ID("peripheral_clk", &div4_clks[DIV4_P]),
+
+	/* MSTP32 clocks */
+	CLKDEV_DEV_ID("i2c-sh7734.0", &mstp_clks[MSTP030]),
+	CLKDEV_DEV_ID("i2c-sh7734.1", &mstp_clks[MSTP029]),
+	CLKDEV_ICK_ID("sci_fck", "sh-sci.0", &mstp_clks[MSTP026]),
+	CLKDEV_ICK_ID("sci_fck", "sh-sci.1", &mstp_clks[MSTP025]),
+	CLKDEV_ICK_ID("sci_fck", "sh-sci.2", &mstp_clks[MSTP024]),
+	CLKDEV_ICK_ID("sci_fck", "sh-sci.3", &mstp_clks[MSTP023]),
+	CLKDEV_ICK_ID("sci_fck", "sh-sci.4", &mstp_clks[MSTP022]),
+	CLKDEV_ICK_ID("sci_fck", "sh-sci.5", &mstp_clks[MSTP021]),
+	CLKDEV_CON_ID("hscif", &mstp_clks[MSTP019]),
+	CLKDEV_ICK_ID("tmu_fck", "sh_tmu.0", &mstp_clks[MSTP016]),
+	CLKDEV_ICK_ID("tmu_fck", "sh_tmu.1", &mstp_clks[MSTP016]),
+	CLKDEV_ICK_ID("tmu_fck", "sh_tmu.2", &mstp_clks[MSTP016]),
+	CLKDEV_ICK_ID("tmu_fck", "sh_tmu.3", &mstp_clks[MSTP015]),
+	CLKDEV_ICK_ID("tmu_fck", "sh_tmu.4", &mstp_clks[MSTP015]),
+	CLKDEV_ICK_ID("tmu_fck", "sh_tmu.5", &mstp_clks[MSTP015]),
+	CLKDEV_ICK_ID("tmu_fck", "sh_tmu.6", &mstp_clks[MSTP014]),
+	CLKDEV_ICK_ID("tmu_fck", "sh_tmu.7", &mstp_clks[MSTP014]),
+	CLKDEV_ICK_ID("tmu_fck", "sh_tmu.8", &mstp_clks[MSTP014]),
+	CLKDEV_CON_ID("ssi0", &mstp_clks[MSTP012]),
+	CLKDEV_CON_ID("ssi1", &mstp_clks[MSTP011]),
+	CLKDEV_CON_ID("ssi2", &mstp_clks[MSTP010]),
+	CLKDEV_CON_ID("ssi3", &mstp_clks[MSTP009]),
+	CLKDEV_CON_ID("sss", &mstp_clks[MSTP008]),
+	CLKDEV_CON_ID("hspi", &mstp_clks[MSTP007]),
+	CLKDEV_CON_ID("usb_fck", &mstp_clks[MSTP100]),
+	CLKDEV_CON_ID("videoin0", &mstp_clks[MSTP109]),
+	CLKDEV_CON_ID("videoin1", &mstp_clks[MSTP108]),
+	CLKDEV_CON_ID("rgpvg", &mstp_clks[MSTP107]),
+	CLKDEV_CON_ID("2dg", &mstp_clks[MSTP106]),
+	CLKDEV_CON_ID("view", &mstp_clks[MSTP103]),
+
+	CLKDEV_CON_ID("mmc0", &mstp_clks[MSTP331]),
+	CLKDEV_CON_ID("mimlb0", &mstp_clks[MSTP330]),
+	CLKDEV_CON_ID("sdhi0", &mstp_clks[MSTP323]),
+	CLKDEV_CON_ID("sdhi1", &mstp_clks[MSTP322]),
+	CLKDEV_CON_ID("sdhi2", &mstp_clks[MSTP321]),
+	CLKDEV_CON_ID("rqspi0", &mstp_clks[MSTP320]),
+	CLKDEV_CON_ID("src0", &mstp_clks[MSTP319]),
+	CLKDEV_CON_ID("src1", &mstp_clks[MSTP318]),
+	CLKDEV_CON_ID("rsp0", &mstp_clks[MSTP317]),
+	CLKDEV_CON_ID("rcan0", &mstp_clks[MSTP316]),
+	CLKDEV_CON_ID("rcan1", &mstp_clks[MSTP315]),
+	CLKDEV_CON_ID("fltcl0", &mstp_clks[MSTP314]),
+	CLKDEV_CON_ID("adc0", &mstp_clks[MSTP313]),
+	CLKDEV_CON_ID("mtu0", &mstp_clks[MSTP312]),
+	CLKDEV_CON_ID("iebus0", &mstp_clks[MSTP304]),
+	CLKDEV_DEV_ID("sh-eth.0", &mstp_clks[MSTP114]),
+	CLKDEV_CON_ID("rtc0", &mstp_clks[MSTP303]),
+	CLKDEV_CON_ID("hif0", &mstp_clks[MSTP302]),
+	CLKDEV_CON_ID("stif0", &mstp_clks[MSTP301]),
+	CLKDEV_CON_ID("stif1", &mstp_clks[MSTP300]),
+};
+
+int __init arch_clk_init(void)
+{
+	int i, ret = 0;
+
+	for (i = 0; i < ARRAY_SIZE(main_clks); i++)
+		ret |= clk_register(main_clks[i]);
+
+	for (i = 0; i < ARRAY_SIZE(lookups); i++)
+		clkdev_add(&lookups[i]);
+
+	if (!ret)
+		ret = sh_clk_div4_register(div4_clks, ARRAY_SIZE(div4_clks),
+			&div4_table);
+
+	if (!ret)
+		ret = sh_clk_mstp32_register(mstp_clks, MSTP_NR);
+
+	return ret;
+}
diff --git a/arch/sh/kernel/cpu/sh4a/pinmux-sh7734.c b/arch/sh/kernel/cpu/sh4a/pinmux-sh7734.c
new file mode 100644
index 0000000..eed3b9d
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh4a/pinmux-sh7734.c
@@ -0,0 +1,2497 @@
+/*
+ * SH7734 processor support - PFC hardware block
+ *
+ * Copyright (C) 2012  Renesas Solutions Corp.
+ * Copyright (C) 2012  Nobuhiro Iwamatsu <nobuhiro.iwamatsu.yj@renesas.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/ioport.h>
+#include <cpu/sh7734.h>
+
+#define CPU_32_PORT(fn, pfx, sfx)				\
+	PORT_10(fn, pfx, sfx), PORT_10(fn, pfx##1, sfx),	\
+	PORT_10(fn, pfx##2, sfx), PORT_1(fn, pfx##30, sfx),	\
+	PORT_1(fn, pfx##31, sfx)
+
+#define CPU_32_PORT5(fn, pfx, sfx)				\
+	PORT_1(fn, pfx##0, sfx), PORT_1(fn, pfx##1, sfx),	\
+	PORT_1(fn, pfx##2, sfx), PORT_1(fn, pfx##3, sfx),	\
+	PORT_1(fn, pfx##4, sfx), PORT_1(fn, pfx##5, sfx),	\
+	PORT_1(fn, pfx##6, sfx), PORT_1(fn, pfx##7, sfx),	\
+	PORT_1(fn, pfx##8, sfx), PORT_1(fn, pfx##9, sfx),	\
+	PORT_1(fn, pfx##10, sfx), PORT_1(fn, pfx##11, sfx)
+
+/* GPSR0 - GPSR5 */
+#define CPU_ALL_PORT(fn, pfx, sfx)				\
+	CPU_32_PORT(fn, pfx##_0_, sfx),			\
+	CPU_32_PORT(fn, pfx##_1_, sfx),				\
+	CPU_32_PORT(fn, pfx##_2_, sfx),				\
+	CPU_32_PORT(fn, pfx##_3_, sfx),				\
+	CPU_32_PORT(fn, pfx##_4_, sfx),				\
+	CPU_32_PORT5(fn, pfx##_5_, sfx)
+
+#define _GP_GPIO(pfx, sfx) PINMUX_GPIO(GPIO_GP##pfx, GP##pfx##_DATA)
+#define _GP_DATA(pfx, sfx) PINMUX_DATA(GP##pfx##_DATA, GP##pfx##_FN,	\
+				       GP##pfx##_IN, GP##pfx##_OUT)
+
+#define _GP_INOUTSEL(pfx, sfx) GP##pfx##_IN, GP##pfx##_OUT
+#define _GP_INDT(pfx, sfx) GP##pfx##_DATA
+
+#define GP_ALL(str)	CPU_ALL_PORT(_PORT_ALL, GP, str)
+#define PINMUX_GPIO_GP_ALL()	CPU_ALL_PORT(_GP_GPIO, , unused)
+#define PINMUX_DATA_GP_ALL()	CPU_ALL_PORT(_GP_DATA, , unused)
+
+#define PORT_10_REV(fn, pfx, sfx)	\
+	PORT_1(fn, pfx##9, sfx), PORT_1(fn, pfx##8, sfx),	\
+	PORT_1(fn, pfx##7, sfx), PORT_1(fn, pfx##6, sfx),	\
+	PORT_1(fn, pfx##5, sfx), PORT_1(fn, pfx##4, sfx),	\
+	PORT_1(fn, pfx##3, sfx), PORT_1(fn, pfx##2, sfx),	\
+	PORT_1(fn, pfx##1, sfx), PORT_1(fn, pfx##0, sfx)
+
+#define CPU_32_PORT_REV(fn, pfx, sfx)	\
+	PORT_1(fn, pfx##31, sfx), PORT_1(fn, pfx##30, sfx),	\
+	PORT_10_REV(fn, pfx##2, sfx), PORT_10_REV(fn, pfx##1, sfx),	\
+	PORT_10_REV(fn, pfx, sfx)
+
+#define GP_INOUTSEL(bank) CPU_32_PORT_REV(_GP_INOUTSEL, _##bank##_, unused)
+#define GP_INDT(bank) CPU_32_PORT_REV(_GP_INDT, _##bank##_, unused)
+
+#define PINMUX_IPSR_DATA(ipsr, fn) PINMUX_DATA(fn##_MARK, FN_##ipsr, FN_##fn)
+#define PINMUX_IPSR_MODSEL_DATA(ipsr, fn, ms) PINMUX_DATA(fn##_MARK, FN_##ms, \
+							  FN_##ipsr, FN_##fn)
+
+enum {
+	PINMUX_RESERVED = 0,
+
+	PINMUX_DATA_BEGIN,
+	GP_ALL(DATA), /* GP_0_0_DATA -> GP_5_11_DATA */
+	PINMUX_DATA_END,
+
+	PINMUX_INPUT_BEGIN,
+	GP_ALL(IN), /* GP_0_0_IN -> GP_5_11_IN */
+	PINMUX_INPUT_END,
+
+	PINMUX_OUTPUT_BEGIN,
+	GP_ALL(OUT), /* GP_0_0_OUT -> GP_5_11_OUT */
+	PINMUX_OUTPUT_END,
+
+	PINMUX_FUNCTION_BEGIN,
+	GP_ALL(FN), /* GP_0_0_FN -> GP_5_11_FN */
+
+	/* GPSR0 */
+	FN_IP1_9_8, FN_IP1_11_10, FN_IP1_13_12, FN_IP1_15_14,
+	FN_IP0_7_6, FN_IP0_9_8, FN_IP0_11_10, FN_IP0_13_12,
+	FN_IP0_15_14, FN_IP0_17_16, FN_IP0_19_18, FN_IP0_21_20,
+	FN_IP0_23_22, FN_IP0_25_24, FN_IP0_27_26, FN_IP0_29_28,
+	FN_IP0_31_30, FN_IP1_1_0, FN_IP1_3_2, FN_IP1_5_4,
+	FN_IP1_7_6, FN_IP11_28, FN_IP0_1_0, FN_IP0_3_2,
+	FN_IP0_5_4, FN_IP1_17_16, FN_IP1_19_18, FN_IP1_22_20,
+	FN_IP1_25_23, FN_IP1_28_26, FN_IP1_31_29, FN_IP2_2_0,
+
+	/* GPSR1 */
+	FN_IP3_20, FN_IP3_29_27, FN_IP11_20_19, FN_IP11_22_21,
+	FN_IP2_16_14, FN_IP2_19_17, FN_IP2_22_20, FN_IP2_24_23,
+	FN_IP2_27_25, FN_IP2_30_28, FN_IP3_1_0, FN_CLKOUT,
+	FN_BS, FN_CS0, FN_IP3_2, FN_EX_CS0,
+	FN_IP3_5_3, FN_IP3_8_6, FN_IP3_11_9, FN_IP3_14_12,
+	FN_IP3_17_15, FN_RD, FN_IP3_19_18, FN_WE0,
+	FN_WE1, FN_IP2_4_3, FN_IP3_23_21, FN_IP3_26_24,
+	FN_IP2_7_5, FN_IP2_10_8, FN_IP2_13_11, FN_IP11_25_23,
+
+	/* GPSR2 */
+	FN_IP11_6_4, FN_IP11_9_7, FN_IP11_11_10, FN_IP4_2_0,
+	FN_IP8_29_28, FN_IP11_27_26, FN_IP8_22_20, FN_IP8_25_23,
+	FN_IP11_12, FN_IP8_27_26, FN_IP4_5_3, FN_IP4_8_6,
+	FN_IP4_11_9, FN_IP4_14_12, FN_IP4_17_15, FN_IP4_19_18,
+	FN_IP4_21_20, FN_IP4_23_22, FN_IP4_25_24, FN_IP4_27_26,
+	FN_IP4_29_28, FN_IP4_31_30, FN_IP5_2_0, FN_IP5_5_3,
+	FN_IP5_8_6, FN_IP5_11_9, FN_IP5_14_12, FN_IP5_17_15,
+	FN_IP5_20_18, FN_IP5_22_21, FN_IP5_24_23, FN_IP5_26_25,
+
+	/* GPSR3 */
+	FN_IP6_2_0, FN_IP6_5_3, FN_IP6_7_6, FN_IP6_9_8,
+	FN_IP6_11_10, FN_IP6_13_12, FN_IP6_15_14, FN_IP6_17_16,
+	FN_IP6_20_18, FN_IP6_23_21, FN_IP7_2_0, FN_IP7_5_3,
+	FN_IP7_8_6, FN_IP7_11_9, FN_IP7_14_12, FN_IP7_17_15,
+	FN_IP7_20_18, FN_IP7_23_21, FN_IP7_26_24, FN_IP7_28_27,
+	FN_IP7_30_29, FN_IP8_1_0, FN_IP8_3_2, FN_IP8_5_4,
+	FN_IP8_7_6, FN_IP8_9_8, FN_IP8_11_10, FN_IP8_13_12,
+	FN_IP8_15_14, FN_IP8_17_16, FN_IP8_19_18, FN_IP9_1_0,
+
+	/* GPSR4 */
+	FN_IP9_19_18, FN_IP9_21_20, FN_IP9_23_22, FN_IP9_25_24,
+	FN_IP9_11_10, FN_IP9_13_12, FN_IP9_15_14, FN_IP9_17_16,
+	FN_IP9_3_2, FN_IP9_5_4, FN_IP9_7_6, FN_IP9_9_8,
+	FN_IP9_27_26, FN_IP9_29_28, FN_IP10_2_0, FN_IP10_5_3,
+	FN_IP10_8_6, FN_IP10_11_9, FN_IP10_14_12, FN_IP10_15,
+	FN_IP10_18_16, FN_IP10_21_19, FN_IP11_0, FN_IP11_1,
+	FN_SCL0, FN_IP11_2, FN_PENC0, FN_IP11_15_13, /* Need check*/
+	FN_USB_OVC0, FN_IP11_18_16,
+	FN_IP10_22, FN_IP10_24_23,
+
+	/* GPSR5 */
+	FN_IP10_25, FN_IP11_3, FN_IRQ2_B, FN_IRQ3_B,
+	FN_IP10_27_26, /* 10 */
+	FN_IP10_29_28, /* 11 */
+
+	/* IPSR0 */
+	FN_A15, FN_ST0_VCO_CLKIN, FN_LCD_DATA15_A, FN_TIOC3D_C,
+	FN_A14, FN_LCD_DATA14_A, FN_TIOC3C_C,
+	FN_A13, FN_LCD_DATA13_A, FN_TIOC3B_C,
+	FN_A12, FN_LCD_DATA12_A, FN_TIOC3A_C,
+	FN_A11, FN_ST0_D7, FN_LCD_DATA11_A, FN_TIOC2B_C,
+	FN_A10, FN_ST0_D6, FN_LCD_DATA10_A, FN_TIOC2A_C,
+	FN_A9, FN_ST0_D5, FN_LCD_DATA9_A, FN_TIOC1B_C,
+	FN_A8, FN_ST0_D4, FN_LCD_DATA8_A, FN_TIOC1A_C,
+	FN_A7, FN_ST0_D3, FN_LCD_DATA7_A, FN_TIOC0D_C,
+	FN_A6, FN_ST0_D2, FN_LCD_DATA6_A, FN_TIOC0C_C,
+	FN_A5, FN_ST0_D1, FN_LCD_DATA5_A, FN_TIOC0B_C,
+	FN_A4, FN_ST0_D0, FN_LCD_DATA4_A, FN_TIOC0A_C,
+	FN_A3, FN_ST0_VLD, FN_LCD_DATA3_A, FN_TCLKD_C,
+	FN_A2, FN_ST0_SYC, FN_LCD_DATA2_A, FN_TCLKC_C,
+	FN_A1, FN_ST0_REQ, FN_LCD_DATA1_A, FN_TCLKB_C,
+	FN_A0, FN_ST0_CLKIN, FN_LCD_DATA0_A, FN_TCLKA_C,
+
+	/* IPSR1 */
+	FN_D3, FN_SD0_DAT3_A, FN_MMC_D3_A, FN_ST1_D6, FN_FD3_A,
+	FN_D2, FN_SD0_DAT2_A, FN_MMC_D2_A, FN_ST1_D5, FN_FD2_A,
+	FN_D1, FN_SD0_DAT1_A, FN_MMC_D1_A, FN_ST1_D4, FN_FD1_A,
+	FN_D0, FN_SD0_DAT0_A, FN_MMC_D0_A, FN_ST1_D3, FN_FD0_A,
+	FN_A25, FN_TX2_D, FN_ST1_D2,
+	FN_A24, FN_RX2_D, FN_ST1_D1,
+	FN_A23, FN_ST1_D0, FN_LCD_M_DISP_A,
+	FN_A22, FN_ST1_VLD, FN_LCD_VEPWC_A,
+	FN_A21, FN_ST1_SYC, FN_LCD_VCPWC_A,
+	FN_A20, FN_ST1_REQ, FN_LCD_FLM_A,
+	FN_A19, FN_ST1_CLKIN, FN_LCD_CLK_A,	FN_TIOC4D_C,
+	FN_A18, FN_ST1_PWM, FN_LCD_CL2_A, FN_TIOC4C_C,
+	FN_A17, FN_ST1_VCO_CLKIN, FN_LCD_CL1_A,	FN_TIOC4B_C,
+	FN_A16, FN_ST0_PWM, FN_LCD_DON_A, FN_TIOC4A_C,
+
+	/* IPSR2 */
+	FN_D14, FN_TX2_B, FN_FSE_A, FN_ET0_TX_CLK_B,
+	FN_D13, FN_RX2_B, FN_FRB_A,	FN_ET0_ETXD6_B,
+	FN_D12, FN_FWE_A, FN_ET0_ETXD5_B,
+	FN_D11, FN_RSPI_MISO_A, FN_QMI_QIO1_A, FN_FRE_A,
+		FN_ET0_ETXD3_B,
+	FN_D10, FN_RSPI_MOSI_A, FN_QMO_QIO0_A, FN_FALE_A,
+		FN_ET0_ETXD2_B,
+	FN_D9, FN_SD0_CMD_A, FN_MMC_CMD_A, FN_QIO3_A, FN_FCLE_A,
+		FN_ET0_ETXD1_B,
+	FN_D8, FN_SD0_CLK_A, FN_MMC_CLK_A, FN_QIO2_A, FN_FCE_A,
+		FN_ET0_GTX_CLK_B,
+	FN_D7, FN_RSPI_SSL_A, FN_MMC_D7_A, FN_QSSL_A, FN_FD7_A,
+	FN_D6, FN_RSPI_RSPCK_A, FN_MMC_D6_A, FN_QSPCLK_A, FN_FD6_A,
+	FN_D5, FN_SD0_WP_A, FN_MMC_D5_A, FN_FD5_A,
+	FN_D4, FN_SD0_CD_A, FN_MMC_D4_A, FN_ST1_D7, FN_FD4_A,
+
+	/* IPSR3 */
+	FN_DRACK0, FN_SD1_DAT2_A, FN_ATAG, FN_TCLK1_A, FN_ET0_ETXD7,
+	FN_EX_WAIT2, FN_SD1_DAT1_A, FN_DACK2, FN_CAN1_RX_C,
+		FN_ET0_MAGIC_C, FN_ET0_ETXD6_A,
+	FN_EX_WAIT1, FN_SD1_DAT0_A, FN_DREQ2, FN_CAN1_TX_C,
+		FN_ET0_LINK_C, FN_ET0_ETXD5_A,
+	FN_EX_WAIT0, FN_TCLK1_B,
+	FN_RD_WR, FN_TCLK0, FN_CAN_CLK_B, FN_ET0_ETXD4,
+	FN_EX_CS5, FN_SD1_CMD_A, FN_ATADIR, FN_QSSL_B, FN_ET0_ETXD3_A,
+	FN_EX_CS4, FN_SD1_WP_A, FN_ATAWR, FN_QMI_QIO1_B, FN_ET0_ETXD2_A,
+	FN_EX_CS3, FN_SD1_CD_A, FN_ATARD, FN_QMO_QIO0_B, FN_ET0_ETXD1_A,
+	FN_EX_CS2, FN_TX3_B, FN_ATACS1, FN_QSPCLK_B, FN_ET0_GTX_CLK_A,
+	FN_EX_CS1, FN_RX3_B, FN_ATACS0, FN_QIO2_B, FN_ET0_ETXD0,
+	FN_CS1_A26, FN_QIO3_B,
+	FN_D15, FN_SCK2_B,
+
+	/* IPSR4 */
+	FN_SCK2_A, FN_VI0_G3,
+	FN_RTS1_B, FN_VI0_G2,
+	FN_CTS1_B, FN_VI0_DATA7_VI0_G1,
+	FN_TX1_B, FN_VI0_DATA6_VI0_G0, FN_ET0_PHY_INT_A,
+	FN_RX1_B, FN_VI0_DATA5_VI0_B5, FN_ET0_MAGIC_A,
+	FN_SCK1_B, FN_VI0_DATA4_VI0_B4, FN_ET0_LINK_A,
+	FN_RTS0_B, FN_VI0_DATA3_VI0_B3, FN_ET0_MDIO_A,
+	FN_CTS0_B, FN_VI0_DATA2_VI0_B2, FN_RMII0_MDIO_A, FN_ET0_MDC,
+	FN_HTX0_A, FN_TX1_A, FN_VI0_DATA1_VI0_B1, FN_RMII0_MDC_A, FN_ET0_COL,
+	FN_HRX0_A, FN_RX1_A, FN_VI0_DATA0_VI0_B0, FN_RMII0_CRS_DV_A, FN_ET0_CRS,
+	FN_HSCK0_A, FN_SCK1_A, FN_VI0_VSYNC, FN_RMII0_RX_ER_A, FN_ET0_RX_ER,
+	FN_HRTS0_A, FN_RTS1_A, FN_VI0_HSYNC, FN_RMII0_TXD_EN_A, FN_ET0_RX_DV,
+	FN_HCTS0_A, FN_CTS1_A, FN_VI0_FIELD, FN_RMII0_RXD1_A, FN_ET0_ERXD7,
+
+	/* IPSR5 */
+	FN_SD2_CLK_A, FN_RX2_A, FN_VI0_G4, FN_ET0_RX_CLK_B,
+	FN_SD2_CMD_A, FN_TX2_A, FN_VI0_G5, FN_ET0_ERXD2_B,
+	FN_SD2_DAT0_A, FN_RX3_A, FN_VI0_R0, FN_ET0_ERXD3_B,
+	FN_SD2_DAT1_A, FN_TX3_A, FN_VI0_R1, FN_ET0_MDIO_B,
+	FN_SD2_DAT2_A, FN_RX4_A, FN_VI0_R2, FN_ET0_LINK_B,
+	FN_SD2_DAT3_A, FN_TX4_A, FN_VI0_R3, FN_ET0_MAGIC_B,
+	FN_SD2_CD_A, FN_RX5_A, FN_VI0_R4, FN_ET0_PHY_INT_B,
+	FN_SD2_WP_A, FN_TX5_A, FN_VI0_R5,
+	FN_REF125CK, FN_ADTRG, FN_RX5_C,
+	FN_REF50CK, FN_CTS1_E, FN_HCTS0_D,
+
+	/* IPSR6 */
+	FN_DU0_DR0, FN_SCIF_CLK_B, FN_HRX0_D, FN_IETX_A, FN_TCLKA_A, FN_HIFD00,
+	FN_DU0_DR1, FN_SCK0_B, FN_HTX0_D, FN_IERX_A, FN_TCLKB_A, FN_HIFD01,
+	FN_DU0_DR2, FN_RX0_B, FN_TCLKC_A, FN_HIFD02,
+	FN_DU0_DR3, FN_TX0_B, FN_TCLKD_A, FN_HIFD03,
+	FN_DU0_DR4, FN_CTS0_C, FN_TIOC0A_A, FN_HIFD04,
+	FN_DU0_DR5, FN_RTS0_C, FN_TIOC0B_A, FN_HIFD05,
+	FN_DU0_DR6, FN_SCK1_C, FN_TIOC0C_A, FN_HIFD06,
+	FN_DU0_DR7, FN_RX1_C, FN_TIOC0D_A, FN_HIFD07,
+	FN_DU0_DG0, FN_TX1_C, FN_HSCK0_D, FN_IECLK_A, FN_TIOC1A_A, FN_HIFD08,
+	FN_DU0_DG1, FN_CTS1_C, FN_HRTS0_D, FN_TIOC1B_A, FN_HIFD09,
+
+	/* IPSR7 */
+	FN_DU0_DG2, FN_RTS1_C, FN_RMII0_MDC_B, FN_TIOC2A_A, FN_HIFD10,
+	FN_DU0_DG3, FN_SCK2_C, FN_RMII0_MDIO_B, FN_TIOC2B_A, FN_HIFD11,
+	FN_DU0_DG4, FN_RX2_C, FN_RMII0_CRS_DV_B, FN_TIOC3A_A, FN_HIFD12,
+	FN_DU0_DG5, FN_TX2_C, FN_RMII0_RX_ER_B, FN_TIOC3B_A, FN_HIFD13,
+	FN_DU0_DG6, FN_RX3_C, FN_RMII0_RXD0_B, FN_TIOC3C_A, FN_HIFD14,
+	FN_DU0_DG7, FN_TX3_C, FN_RMII0_RXD1_B, FN_TIOC3D_A, FN_HIFD15,
+	FN_DU0_DB0, FN_RX4_C, FN_RMII0_TXD_EN_B, FN_TIOC4A_A, FN_HIFCS,
+	FN_DU0_DB1, FN_TX4_C, FN_RMII0_TXD0_B, FN_TIOC4B_A, FN_HIFRS,
+	FN_DU0_DB2, FN_RX5_B, FN_RMII0_TXD1_B, FN_TIOC4C_A, FN_HIFWR,
+	FN_DU0_DB3, FN_TX5_B, FN_TIOC4D_A, FN_HIFRD,
+	FN_DU0_DB4, FN_HIFINT,
+
+	/* IPSR8 */
+	FN_DU0_DB5, FN_HIFDREQ,
+	FN_DU0_DB6, FN_HIFRDY,
+	FN_DU0_DB7, FN_SSI_SCK0_B, FN_HIFEBL_B,
+	FN_DU0_DOTCLKIN, FN_HSPI_CS0_C, FN_SSI_WS0_B,
+	FN_DU0_DOTCLKOUT, FN_HSPI_CLK0_C, FN_SSI_SDATA0_B,
+	FN_DU0_EXHSYNC_DU0_HSYNC, FN_HSPI_TX0_C, FN_SSI_SCK1_B,
+	FN_DU0_EXVSYNC_DU0_VSYNC, FN_HSPI_RX0_C, FN_SSI_WS1_B,
+	FN_DU0_EXODDF_DU0_ODDF, FN_CAN0_RX_B, FN_HSCK0_B, FN_SSI_SDATA1_B,
+	FN_DU0_DISP, FN_CAN0_TX_B, FN_HRX0_B, FN_AUDIO_CLKA_B,
+	FN_DU0_CDE, FN_HTX0_B, FN_AUDIO_CLKB_B, FN_LCD_VCPWC_B,
+	FN_IRQ0_A, FN_HSPI_TX_B, FN_RX3_E, FN_ET0_ERXD0,
+	FN_IRQ1_A, FN_HSPI_RX_B, FN_TX3_E, FN_ET0_ERXD1,
+	FN_IRQ2_A, FN_CTS0_A, FN_HCTS0_B, FN_ET0_ERXD2_A,
+	FN_IRQ3_A, FN_RTS0_A, FN_HRTS0_B, FN_ET0_ERXD3_A,
+
+	/* IPSR9 */
+	FN_VI1_CLK_A, FN_FD0_B, FN_LCD_DATA0_B,
+	FN_VI1_0_A, FN_FD1_B, FN_LCD_DATA1_B,
+	FN_VI1_1_A, FN_FD2_B, FN_LCD_DATA2_B,
+	FN_VI1_2_A, FN_FD3_B, FN_LCD_DATA3_B,
+	FN_VI1_3_A, FN_FD4_B, FN_LCD_DATA4_B,
+	FN_VI1_4_A, FN_FD5_B, FN_LCD_DATA5_B,
+	FN_VI1_5_A, FN_FD6_B, FN_LCD_DATA6_B,
+	FN_VI1_6_A, FN_FD7_B, FN_LCD_DATA7_B,
+	FN_VI1_7_A, FN_FCE_B, FN_LCD_DATA8_B,
+	FN_SSI_SCK0_A, FN_TIOC1A_B, FN_LCD_DATA9_B,
+	FN_SSI_WS0_A, FN_TIOC1B_B, FN_LCD_DATA10_B,
+	FN_SSI_SDATA0_A, FN_VI1_0_B, FN_TIOC2A_B, FN_LCD_DATA11_B,
+	FN_SSI_SCK1_A, FN_VI1_1_B, FN_TIOC2B_B, FN_LCD_DATA12_B,
+	FN_SSI_WS1_A, FN_VI1_2_B, FN_LCD_DATA13_B,
+	FN_SSI_SDATA1_A, FN_VI1_3_B, FN_LCD_DATA14_B,
+
+	/* IPSR10 */
+	FN_SSI_SCK23, FN_VI1_4_B, FN_RX1_D, FN_FCLE_B, FN_LCD_DATA15_B,
+	FN_SSI_WS23, FN_VI1_5_B, FN_TX1_D, FN_HSCK0_C, FN_FALE_B, FN_LCD_DON_B,
+	FN_SSI_SDATA2, FN_VI1_6_B, FN_HRX0_C, FN_FRE_B, FN_LCD_CL1_B,
+	FN_SSI_SDATA3, FN_VI1_7_B, FN_HTX0_C, FN_FWE_B, FN_LCD_CL2_B,
+	FN_AUDIO_CLKA_A, FN_VI1_CLK_B, FN_SCK1_D, FN_IECLK_B, FN_LCD_FLM_B,
+	FN_AUDIO_CLKB_A, FN_LCD_CLK_B,
+	FN_AUDIO_CLKC, FN_SCK1_E, FN_HCTS0_C, FN_FRB_B, FN_LCD_VEPWC_B,
+	FN_AUDIO_CLKOUT, FN_TX1_E, FN_HRTS0_C, FN_FSE_B, FN_LCD_M_DISP_B,
+	FN_CAN_CLK_A, FN_RX4_D,
+	FN_CAN0_TX_A, FN_TX4_D, FN_MLB_CLK,
+	FN_CAN1_RX_A, FN_IRQ1_B,
+	FN_CAN0_RX_A, FN_IRQ0_B, FN_MLB_SIG,
+	FN_CAN1_TX_A, FN_TX5_C, FN_MLB_DAT,
+
+	/* IPSR11 */
+	FN_SCL1, FN_SCIF_CLK_C,
+	FN_SDA1, FN_RX1_E,
+	FN_SDA0, FN_HIFEBL_A,
+	FN_SDSELF, FN_RTS1_E,
+	FN_SCIF_CLK_A, FN_HSPI_CLK_A, FN_VI0_CLK, FN_RMII0_TXD0_A, FN_ET0_ERXD4,
+	FN_SCK0_A, FN_HSPI_CS_A, FN_VI0_CLKENB, FN_RMII0_TXD1_A, FN_ET0_ERXD5,
+	FN_RX0_A, FN_HSPI_RX_A, FN_RMII0_RXD0_A, FN_ET0_ERXD6,
+	FN_TX0_A, FN_HSPI_TX_A,
+	FN_PENC1, FN_TX3_D, FN_CAN1_TX_B, FN_TX5_D, FN_IETX_B,
+	FN_USB_OVC1, FN_RX3_D, FN_CAN1_RX_B, FN_RX5_D, FN_IERX_B,
+	FN_DREQ0, FN_SD1_CLK_A, FN_ET0_TX_EN,
+	FN_DACK0, FN_SD1_DAT3_A, FN_ET0_TX_ER,
+	FN_DREQ1, FN_HSPI_CLK_B, FN_RX4_B, FN_ET0_PHY_INT_C, FN_ET0_TX_CLK_A,
+	FN_DACK1, FN_HSPI_CS_B, FN_TX4_B, FN_ET0_RX_CLK_A,
+	FN_PRESETOUT, FN_ST_CLKOUT,
+
+	/* MOD_SEL1 */
+	FN_SEL_IEBUS_0, FN_SEL_IEBUS_1,
+	FN_SEL_RQSPI_0, FN_SEL_RQSPI_1,
+	FN_SEL_VIN1_0, FN_SEL_VIN1_1,
+	FN_SEL_HIF_0, FN_SEL_HIF_1,
+	FN_SEL_RSPI_0, FN_SEL_RSPI_1,
+	FN_SEL_LCDC_0, FN_SEL_LCDC_1,
+	FN_SEL_ET0_CTL_0, FN_SEL_ET0_CTL_1, FN_SEL_ET0_CTL_2,
+	FN_SEL_ET0_0, FN_SEL_ET0_1,
+	FN_SEL_RMII_0, FN_SEL_RMII_1,
+	FN_SEL_TMU_0, FN_SEL_TMU_1,
+	FN_SEL_HSPI_0, FN_SEL_HSPI_1, FN_SEL_HSPI_2,
+	FN_SEL_HSCIF_0, FN_SEL_HSCIF_1, FN_SEL_HSCIF_2, FN_SEL_HSCIF_3,
+	FN_SEL_RCAN_CLK_0, FN_SEL_RCAN_CLK_1,
+	FN_SEL_RCAN1_0, FN_SEL_RCAN1_1, FN_SEL_RCAN1_2,
+	FN_SEL_RCAN0_0, FN_SEL_RCAN0_1,
+	FN_SEL_SDHI2_0, FN_SEL_SDHI2_1,
+	FN_SEL_SDHI1_0, FN_SEL_SDHI1_1,
+	FN_SEL_SDHI0_0, FN_SEL_SDHI0_1,
+	FN_SEL_SSI1_0, FN_SEL_SSI1_1,
+	FN_SEL_SSI0_0, FN_SEL_SSI0_1,
+	FN_SEL_AUDIO_CLKB_0, FN_SEL_AUDIO_CLKB_1,
+	FN_SEL_AUDIO_CLKA_0, FN_SEL_AUDIO_CLKA_1,
+	FN_SEL_FLCTL_0, FN_SEL_FLCTL_1,
+	FN_SEL_MMC_0, FN_SEL_MMC_1,
+	FN_SEL_INTC_0, FN_SEL_INTC_1,
+
+	/* MOD_SEL2 */
+	FN_SEL_MTU2_CLK_0, FN_SEL_MTU2_CLK_1,
+	FN_SEL_MTU2_CH4_0, FN_SEL_MTU2_CH4_1,
+	FN_SEL_MTU2_CH3_0, FN_SEL_MTU2_CH3_1,
+	FN_SEL_MTU2_CH2_0, FN_SEL_MTU2_CH2_1, FN_SEL_MTU2_CH2_2,
+	FN_SEL_MTU2_CH1_0, FN_SEL_MTU2_CH1_1, FN_SEL_MTU2_CH1_2,
+	FN_SEL_MTU2_CH0_0, FN_SEL_MTU2_CH0_1,
+	FN_SEL_SCIF5_0, FN_SEL_SCIF5_1,
+	FN_SEL_SCIF5_2, FN_SEL_SCIF5_3,
+	FN_SEL_SCIF4_0, FN_SEL_SCIF4_1,
+	FN_SEL_SCIF4_2, FN_SEL_SCIF4_3,
+	FN_SEL_SCIF3_0, FN_SEL_SCIF3_1, FN_SEL_SCIF3_2,
+		FN_SEL_SCIF3_3, FN_SEL_SCIF3_4,
+	FN_SEL_SCIF2_0, FN_SEL_SCIF2_1, FN_SEL_SCIF2_2,
+		FN_SEL_SCIF2_3,
+	FN_SEL_SCIF1_0, FN_SEL_SCIF1_1, FN_SEL_SCIF1_2,
+		FN_SEL_SCIF1_3, FN_SEL_SCIF1_4,
+	FN_SEL_SCIF0_0, FN_SEL_SCIF0_1, FN_SEL_SCIF0_2,
+	FN_SEL_SCIF_CLK_0, FN_SEL_SCIF_CLK_1, FN_SEL_SCIF_CLK_2,
+
+	PINMUX_FUNCTION_END,
+
+	PINMUX_MARK_BEGIN,
+
+	CLKOUT_MARK, BS_MARK, CS0_MARK, EX_CS0_MARK, RD_MARK,
+	WE0_MARK, WE1_MARK,
+
+	SCL0_MARK, PENC0_MARK, USB_OVC0_MARK,
+
+	IRQ2_B_MARK, IRQ3_B_MARK,
+
+	/* IPSR0 */
+	A15_MARK, ST0_VCO_CLKIN_MARK, LCD_DATA15_A_MARK, TIOC3D_C_MARK,
+	A14_MARK, LCD_DATA14_A_MARK, TIOC3C_C_MARK,
+	A13_MARK, LCD_DATA13_A_MARK, TIOC3B_C_MARK,
+	A12_MARK, LCD_DATA12_A_MARK, TIOC3A_C_MARK,
+	A11_MARK, ST0_D7_MARK, LCD_DATA11_A_MARK, TIOC2B_C_MARK,
+	A10_MARK, ST0_D6_MARK, LCD_DATA10_A_MARK, TIOC2A_C_MARK,
+	A9_MARK, ST0_D5_MARK, LCD_DATA9_A_MARK, TIOC1B_C_MARK,
+	A8_MARK, ST0_D4_MARK, LCD_DATA8_A_MARK, TIOC1A_C_MARK,
+	A7_MARK, ST0_D3_MARK, LCD_DATA7_A_MARK, TIOC0D_C_MARK,
+	A6_MARK, ST0_D2_MARK, LCD_DATA6_A_MARK, TIOC0C_C_MARK,
+	A5_MARK, ST0_D1_MARK, LCD_DATA5_A_MARK, TIOC0B_C_MARK,
+	A4_MARK, ST0_D0_MARK, LCD_DATA4_A_MARK, TIOC0A_C_MARK,
+	A3_MARK, ST0_VLD_MARK, LCD_DATA3_A_MARK, TCLKD_C_MARK,
+	A2_MARK, ST0_SYC_MARK, LCD_DATA2_A_MARK, TCLKC_C_MARK,
+	A1_MARK, ST0_REQ_MARK, LCD_DATA1_A_MARK, TCLKB_C_MARK,
+	A0_MARK, ST0_CLKIN_MARK, LCD_DATA0_A_MARK, TCLKA_C_MARK,
+
+	/* IPSR1 */
+	D3_MARK, SD0_DAT3_A_MARK, MMC_D3_A_MARK, ST1_D6_MARK, FD3_A_MARK,
+	D2_MARK, SD0_DAT2_A_MARK, MMC_D2_A_MARK, ST1_D5_MARK, FD2_A_MARK,
+	D1_MARK, SD0_DAT1_A_MARK, MMC_D1_A_MARK, ST1_D4_MARK, FD1_A_MARK,
+	D0_MARK, SD0_DAT0_A_MARK, MMC_D0_A_MARK, ST1_D3_MARK, FD0_A_MARK,
+	A25_MARK, TX2_D_MARK, ST1_D2_MARK,
+	A24_MARK, RX2_D_MARK, ST1_D1_MARK,
+	A23_MARK, ST1_D0_MARK, LCD_M_DISP_A_MARK,
+	A22_MARK, ST1_VLD_MARK, LCD_VEPWC_A_MARK,
+	A21_MARK, ST1_SYC_MARK, LCD_VCPWC_A_MARK,
+	A20_MARK, ST1_REQ_MARK, LCD_FLM_A_MARK,
+	A19_MARK, ST1_CLKIN_MARK, LCD_CLK_A_MARK,	TIOC4D_C_MARK,
+	A18_MARK, ST1_PWM_MARK, LCD_CL2_A_MARK, TIOC4C_C_MARK,
+	A17_MARK, ST1_VCO_CLKIN_MARK, LCD_CL1_A_MARK, TIOC4B_C_MARK,
+	A16_MARK, ST0_PWM_MARK, LCD_DON_A_MARK, TIOC4A_C_MARK,
+
+	/* IPSR2 */
+	D14_MARK, TX2_B_MARK, FSE_A_MARK, ET0_TX_CLK_B_MARK,
+	D13_MARK, RX2_B_MARK, FRB_A_MARK, ET0_ETXD6_B_MARK,
+	D12_MARK, FWE_A_MARK, ET0_ETXD5_B_MARK,
+	D11_MARK, RSPI_MISO_A_MARK, QMI_QIO1_A_MARK, FRE_A_MARK,
+		ET0_ETXD3_B_MARK,
+	D10_MARK, RSPI_MOSI_A_MARK, QMO_QIO0_A_MARK, FALE_A_MARK,
+		ET0_ETXD2_B_MARK,
+	D9_MARK, SD0_CMD_A_MARK, MMC_CMD_A_MARK, QIO3_A_MARK,
+		FCLE_A_MARK, ET0_ETXD1_B_MARK,
+	D8_MARK, SD0_CLK_A_MARK, MMC_CLK_A_MARK, QIO2_A_MARK,
+		FCE_A_MARK, ET0_GTX_CLK_B_MARK,
+	D7_MARK, RSPI_SSL_A_MARK, MMC_D7_A_MARK, QSSL_A_MARK,
+		FD7_A_MARK,
+	D6_MARK, RSPI_RSPCK_A_MARK, MMC_D6_A_MARK, QSPCLK_A_MARK,
+		FD6_A_MARK,
+	D5_MARK, SD0_WP_A_MARK, MMC_D5_A_MARK, FD5_A_MARK,
+	D4_MARK, SD0_CD_A_MARK, MMC_D4_A_MARK, ST1_D7_MARK,
+		FD4_A_MARK,
+
+	/* IPSR3 */
+	DRACK0_MARK, SD1_DAT2_A_MARK, ATAG_MARK, TCLK1_A_MARK, ET0_ETXD7_MARK,
+	EX_WAIT2_MARK, SD1_DAT1_A_MARK, DACK2_MARK, CAN1_RX_C_MARK,
+		ET0_MAGIC_C_MARK, ET0_ETXD6_A_MARK,
+	EX_WAIT1_MARK, SD1_DAT0_A_MARK, DREQ2_MARK, CAN1_TX_C_MARK,
+		ET0_LINK_C_MARK, ET0_ETXD5_A_MARK,
+	EX_WAIT0_MARK, TCLK1_B_MARK,
+	RD_WR_MARK, TCLK0_MARK, CAN_CLK_B_MARK, ET0_ETXD4_MARK,
+	EX_CS5_MARK, SD1_CMD_A_MARK, ATADIR_MARK, QSSL_B_MARK,
+		ET0_ETXD3_A_MARK,
+	EX_CS4_MARK, SD1_WP_A_MARK, ATAWR_MARK, QMI_QIO1_B_MARK,
+		ET0_ETXD2_A_MARK,
+	EX_CS3_MARK, SD1_CD_A_MARK, ATARD_MARK, QMO_QIO0_B_MARK,
+		ET0_ETXD1_A_MARK,
+	EX_CS2_MARK, TX3_B_MARK, ATACS1_MARK, QSPCLK_B_MARK,
+		ET0_GTX_CLK_A_MARK,
+	EX_CS1_MARK, RX3_B_MARK, ATACS0_MARK, QIO2_B_MARK,
+		ET0_ETXD0_MARK,
+	CS1_A26_MARK, QIO3_B_MARK,
+	D15_MARK, SCK2_B_MARK,
+
+	/* IPSR4 */
+	SCK2_A_MARK, VI0_G3_MARK,
+	RTS1_B_MARK, VI0_G2_MARK,
+	CTS1_B_MARK, VI0_DATA7_VI0_G1_MARK,
+	TX1_B_MARK, VI0_DATA6_VI0_G0_MARK, ET0_PHY_INT_A_MARK,
+	RX1_B_MARK, VI0_DATA5_VI0_B5_MARK, ET0_MAGIC_A_MARK,
+	SCK1_B_MARK, VI0_DATA4_VI0_B4_MARK, ET0_LINK_A_MARK,
+	RTS0_B_MARK, VI0_DATA3_VI0_B3_MARK, ET0_MDIO_A_MARK,
+	CTS0_B_MARK, VI0_DATA2_VI0_B2_MARK, RMII0_MDIO_A_MARK,
+		ET0_MDC_MARK,
+	HTX0_A_MARK, TX1_A_MARK, VI0_DATA1_VI0_B1_MARK,
+		RMII0_MDC_A_MARK, ET0_COL_MARK,
+	HRX0_A_MARK, RX1_A_MARK, VI0_DATA0_VI0_B0_MARK,
+		RMII0_CRS_DV_A_MARK, ET0_CRS_MARK,
+	HSCK0_A_MARK, SCK1_A_MARK, VI0_VSYNC_MARK,
+		RMII0_RX_ER_A_MARK, ET0_RX_ER_MARK,
+	HRTS0_A_MARK, RTS1_A_MARK, VI0_HSYNC_MARK,
+		RMII0_TXD_EN_A_MARK, ET0_RX_DV_MARK,
+	HCTS0_A_MARK, CTS1_A_MARK, VI0_FIELD_MARK,
+		RMII0_RXD1_A_MARK, ET0_ERXD7_MARK,
+
+	/* IPSR5 */
+	SD2_CLK_A_MARK, RX2_A_MARK, VI0_G4_MARK, ET0_RX_CLK_B_MARK,
+	SD2_CMD_A_MARK, TX2_A_MARK, VI0_G5_MARK, ET0_ERXD2_B_MARK,
+	SD2_DAT0_A_MARK, RX3_A_MARK, VI0_R0_MARK, ET0_ERXD3_B_MARK,
+	SD2_DAT1_A_MARK, TX3_A_MARK, VI0_R1_MARK, ET0_MDIO_B_MARK,
+	SD2_DAT2_A_MARK, RX4_A_MARK, VI0_R2_MARK, ET0_LINK_B_MARK,
+	SD2_DAT3_A_MARK, TX4_A_MARK, VI0_R3_MARK, ET0_MAGIC_B_MARK,
+	SD2_CD_A_MARK, RX5_A_MARK, VI0_R4_MARK, ET0_PHY_INT_B_MARK,
+	SD2_WP_A_MARK, TX5_A_MARK, VI0_R5_MARK,
+	REF125CK_MARK, ADTRG_MARK, RX5_C_MARK,
+	REF50CK_MARK, CTS1_E_MARK, HCTS0_D_MARK,
+
+	/* IPSR6 */
+	DU0_DR0_MARK, SCIF_CLK_B_MARK, HRX0_D_MARK, IETX_A_MARK,
+		TCLKA_A_MARK, HIFD00_MARK,
+	DU0_DR1_MARK, SCK0_B_MARK, HTX0_D_MARK, IERX_A_MARK,
+		TCLKB_A_MARK, HIFD01_MARK,
+	DU0_DR2_MARK, RX0_B_MARK, TCLKC_A_MARK, HIFD02_MARK,
+	DU0_DR3_MARK, TX0_B_MARK, TCLKD_A_MARK, HIFD03_MARK,
+	DU0_DR4_MARK, CTS0_C_MARK, TIOC0A_A_MARK, HIFD04_MARK,
+	DU0_DR5_MARK, RTS0_C_MARK, TIOC0B_A_MARK, HIFD05_MARK,
+	DU0_DR6_MARK, SCK1_C_MARK, TIOC0C_A_MARK, HIFD06_MARK,
+	DU0_DR7_MARK, RX1_C_MARK, TIOC0D_A_MARK, HIFD07_MARK,
+	DU0_DG0_MARK, TX1_C_MARK, HSCK0_D_MARK, IECLK_A_MARK,
+		TIOC1A_A_MARK, HIFD08_MARK,
+	DU0_DG1_MARK, CTS1_C_MARK, HRTS0_D_MARK, TIOC1B_A_MARK,
+		HIFD09_MARK,
+
+	/* IPSR7 */
+	DU0_DG2_MARK, RTS1_C_MARK, RMII0_MDC_B_MARK, TIOC2A_A_MARK,
+		HIFD10_MARK,
+	DU0_DG3_MARK, SCK2_C_MARK, RMII0_MDIO_B_MARK, TIOC2B_A_MARK,
+		HIFD11_MARK,
+	DU0_DG4_MARK, RX2_C_MARK, RMII0_CRS_DV_B_MARK, TIOC3A_A_MARK,
+		HIFD12_MARK,
+	DU0_DG5_MARK, TX2_C_MARK, RMII0_RX_ER_B_MARK, TIOC3B_A_MARK,
+		HIFD13_MARK,
+	DU0_DG6_MARK, RX3_C_MARK, RMII0_RXD0_B_MARK, TIOC3C_A_MARK,
+		HIFD14_MARK,
+	DU0_DG7_MARK, TX3_C_MARK, RMII0_RXD1_B_MARK, TIOC3D_A_MARK,
+		HIFD15_MARK,
+	DU0_DB0_MARK, RX4_C_MARK, RMII0_TXD_EN_B_MARK, TIOC4A_A_MARK,
+		HIFCS_MARK,
+	DU0_DB1_MARK, TX4_C_MARK, RMII0_TXD0_B_MARK, TIOC4B_A_MARK,
+		HIFRS_MARK,
+	DU0_DB2_MARK, RX5_B_MARK, RMII0_TXD1_B_MARK, TIOC4C_A_MARK,
+		HIFWR_MARK,
+	DU0_DB3_MARK, TX5_B_MARK, TIOC4D_A_MARK, HIFRD_MARK,
+	DU0_DB4_MARK, HIFINT_MARK,
+
+	/* IPSR8 */
+	DU0_DB5_MARK, HIFDREQ_MARK,
+	DU0_DB6_MARK, HIFRDY_MARK,
+	DU0_DB7_MARK, SSI_SCK0_B_MARK, HIFEBL_B_MARK,
+	DU0_DOTCLKIN_MARK, HSPI_CS0_C_MARK, SSI_WS0_B_MARK,
+	DU0_DOTCLKOUT_MARK, HSPI_CLK0_C_MARK, SSI_SDATA0_B_MARK,
+	DU0_EXHSYNC_DU0_HSYNC_MARK, HSPI_TX0_C_MARK, SSI_SCK1_B_MARK,
+	DU0_EXVSYNC_DU0_VSYNC_MARK, HSPI_RX0_C_MARK, SSI_WS1_B_MARK,
+	DU0_EXODDF_DU0_ODDF_MARK, CAN0_RX_B_MARK, HSCK0_B_MARK,
+		SSI_SDATA1_B_MARK,
+	DU0_DISP_MARK, CAN0_TX_B_MARK, HRX0_B_MARK, AUDIO_CLKA_B_MARK,
+	DU0_CDE_MARK, HTX0_B_MARK, AUDIO_CLKB_B_MARK, LCD_VCPWC_B_MARK,
+	IRQ0_A_MARK, HSPI_TX_B_MARK, RX3_E_MARK, ET0_ERXD0_MARK,
+	IRQ1_A_MARK, HSPI_RX_B_MARK, TX3_E_MARK, ET0_ERXD1_MARK,
+	IRQ2_A_MARK, CTS0_A_MARK, HCTS0_B_MARK, ET0_ERXD2_A_MARK,
+	IRQ3_A_MARK, RTS0_A_MARK, HRTS0_B_MARK, ET0_ERXD3_A_MARK,
+
+	/* IPSR9 */
+	VI1_CLK_A_MARK, FD0_B_MARK, LCD_DATA0_B_MARK,
+	VI1_0_A_MARK, FD1_B_MARK, LCD_DATA1_B_MARK,
+	VI1_1_A_MARK, FD2_B_MARK, LCD_DATA2_B_MARK,
+	VI1_2_A_MARK, FD3_B_MARK, LCD_DATA3_B_MARK,
+	VI1_3_A_MARK, FD4_B_MARK, LCD_DATA4_B_MARK,
+	VI1_4_A_MARK, FD5_B_MARK, LCD_DATA5_B_MARK,
+	VI1_5_A_MARK, FD6_B_MARK, LCD_DATA6_B_MARK,
+	VI1_6_A_MARK, FD7_B_MARK, LCD_DATA7_B_MARK,
+	VI1_7_A_MARK, FCE_B_MARK, LCD_DATA8_B_MARK,
+	SSI_SCK0_A_MARK, TIOC1A_B_MARK, LCD_DATA9_B_MARK,
+	SSI_WS0_A_MARK, TIOC1B_B_MARK, LCD_DATA10_B_MARK,
+	SSI_SDATA0_A_MARK, VI1_0_B_MARK, TIOC2A_B_MARK, LCD_DATA11_B_MARK,
+	SSI_SCK1_A_MARK, VI1_1_B_MARK, TIOC2B_B_MARK, LCD_DATA12_B_MARK,
+	SSI_WS1_A_MARK, VI1_2_B_MARK, LCD_DATA13_B_MARK,
+	SSI_SDATA1_A_MARK, VI1_3_B_MARK, LCD_DATA14_B_MARK,
+
+	/* IPSR10 */
+	SSI_SCK23_MARK, VI1_4_B_MARK, RX1_D_MARK, FCLE_B_MARK,
+		LCD_DATA15_B_MARK,
+	SSI_WS23_MARK, VI1_5_B_MARK, TX1_D_MARK, HSCK0_C_MARK,
+		FALE_B_MARK, LCD_DON_B_MARK,
+	SSI_SDATA2_MARK, VI1_6_B_MARK, HRX0_C_MARK, FRE_B_MARK,
+		LCD_CL1_B_MARK,
+	SSI_SDATA3_MARK, VI1_7_B_MARK, HTX0_C_MARK, FWE_B_MARK,
+		LCD_CL2_B_MARK,
+	AUDIO_CLKA_A_MARK, VI1_CLK_B_MARK, SCK1_D_MARK, IECLK_B_MARK,
+		LCD_FLM_B_MARK,
+	AUDIO_CLKB_A_MARK, LCD_CLK_B_MARK,
+	AUDIO_CLKC_MARK, SCK1_E_MARK, HCTS0_C_MARK, FRB_B_MARK,
+		LCD_VEPWC_B_MARK,
+	AUDIO_CLKOUT_MARK, TX1_E_MARK, HRTS0_C_MARK, FSE_B_MARK,
+		LCD_M_DISP_B_MARK,
+	CAN_CLK_A_MARK, RX4_D_MARK,
+	CAN0_TX_A_MARK, TX4_D_MARK, MLB_CLK_MARK,
+	CAN1_RX_A_MARK, IRQ1_B_MARK,
+	CAN0_RX_A_MARK, IRQ0_B_MARK, MLB_SIG_MARK,
+	CAN1_TX_A_MARK, TX5_C_MARK, MLB_DAT_MARK,
+
+	/* IPSR11 */
+	SCL1_MARK, SCIF_CLK_C_MARK,
+	SDA1_MARK, RX1_E_MARK,
+	SDA0_MARK, HIFEBL_A_MARK,
+	SDSELF_MARK, RTS1_E_MARK,
+	SCIF_CLK_A_MARK, HSPI_CLK_A_MARK, VI0_CLK_MARK, RMII0_TXD0_A_MARK,
+		ET0_ERXD4_MARK,
+	SCK0_A_MARK, HSPI_CS_A_MARK, VI0_CLKENB_MARK, RMII0_TXD1_A_MARK,
+		ET0_ERXD5_MARK,
+	RX0_A_MARK, HSPI_RX_A_MARK, RMII0_RXD0_A_MARK, ET0_ERXD6_MARK,
+	TX0_A_MARK, HSPI_TX_A_MARK,
+	PENC1_MARK, TX3_D_MARK, CAN1_TX_B_MARK, TX5_D_MARK,
+		IETX_B_MARK,
+	USB_OVC1_MARK, RX3_D_MARK, CAN1_RX_B_MARK, RX5_D_MARK,
+		IERX_B_MARK,
+	DREQ0_MARK, SD1_CLK_A_MARK, ET0_TX_EN_MARK,
+	DACK0_MARK, SD1_DAT3_A_MARK, ET0_TX_ER_MARK,
+	DREQ1_MARK, HSPI_CLK_B_MARK, RX4_B_MARK, ET0_PHY_INT_C_MARK,
+		ET0_TX_CLK_A_MARK,
+	DACK1_MARK, HSPI_CS_B_MARK, TX4_B_MARK, ET0_RX_CLK_A_MARK,
+	PRESETOUT_MARK, ST_CLKOUT_MARK,
+
+	PINMUX_MARK_END,
+};
+
+static pinmux_enum_t pinmux_data[] = {
+	PINMUX_DATA_GP_ALL(), /* PINMUX_DATA(GP_M_N_DATA, GP_M_N_FN...), */
+
+	PINMUX_DATA(CLKOUT_MARK, FN_CLKOUT),
+	PINMUX_DATA(BS_MARK, FN_BS), PINMUX_DATA(CS0_MARK, FN_CS0),
+	PINMUX_DATA(EX_CS0_MARK, FN_EX_CS0),
+	PINMUX_DATA(RD_MARK, FN_RD), PINMUX_DATA(WE0_MARK, FN_WE0),
+	PINMUX_DATA(WE1_MARK, FN_WE1),
+	PINMUX_DATA(SCL0_MARK, FN_SCL0), PINMUX_DATA(PENC0_MARK, FN_PENC0),
+	PINMUX_DATA(USB_OVC0_MARK, FN_USB_OVC0),
+	PINMUX_DATA(IRQ2_B_MARK, FN_IRQ2_B),
+		PINMUX_DATA(IRQ3_B_MARK, FN_IRQ3_B),
+
+	/* IPSR0 */
+	PINMUX_IPSR_DATA(IP0_1_0, A0),
+	PINMUX_IPSR_DATA(IP0_1_0, ST0_CLKIN),
+	PINMUX_IPSR_MODSEL_DATA(IP0_1_0, LCD_DATA0_A, SEL_LCDC_0),
+	PINMUX_IPSR_MODSEL_DATA(IP0_1_0, TCLKA_C, SEL_MTU2_CLK_1),
+
+	PINMUX_IPSR_DATA(IP0_3_2, A1),
+	PINMUX_IPSR_DATA(IP0_3_2, ST0_REQ),
+	PINMUX_IPSR_MODSEL_DATA(IP0_3_2, LCD_DATA1_A, SEL_LCDC_0),
+	PINMUX_IPSR_MODSEL_DATA(IP0_3_2, TCLKB_C, SEL_MTU2_CLK_1),
+
+	PINMUX_IPSR_DATA(IP0_5_4, A2),
+	PINMUX_IPSR_DATA(IP0_5_4, ST0_SYC),
+	PINMUX_IPSR_MODSEL_DATA(IP0_5_4, LCD_DATA2_A, SEL_LCDC_0),
+	PINMUX_IPSR_MODSEL_DATA(IP0_5_4, TCLKC_C, SEL_MTU2_CLK_1),
+
+	PINMUX_IPSR_DATA(IP0_7_6, A3),
+	PINMUX_IPSR_DATA(IP0_7_6, ST0_VLD),
+	PINMUX_IPSR_MODSEL_DATA(IP0_7_6, LCD_DATA3_A, SEL_LCDC_0),
+	PINMUX_IPSR_MODSEL_DATA(IP0_7_6, TCLKD_C, SEL_MTU2_CLK_1),
+
+	PINMUX_IPSR_DATA(IP0_9_8, A4),
+	PINMUX_IPSR_DATA(IP0_9_8, ST0_D0),
+	PINMUX_IPSR_MODSEL_DATA(IP0_9_8, LCD_DATA4_A, SEL_LCDC_0),
+	PINMUX_IPSR_MODSEL_DATA(IP0_9_8, TIOC0A_C, SEL_MTU2_CH0_1),
+
+	PINMUX_IPSR_DATA(IP0_11_10, A5),
+	PINMUX_IPSR_DATA(IP0_11_10, ST0_D1),
+	PINMUX_IPSR_MODSEL_DATA(IP0_11_10, LCD_DATA5_A, SEL_LCDC_0),
+	PINMUX_IPSR_MODSEL_DATA(IP0_11_10, TIOC0B_C, SEL_MTU2_CH0_1),
+
+	PINMUX_IPSR_DATA(IP0_13_12, A6),
+	PINMUX_IPSR_DATA(IP0_13_12, ST0_D2),
+	PINMUX_IPSR_MODSEL_DATA(IP0_13_12, LCD_DATA6_A, SEL_LCDC_0),
+	PINMUX_IPSR_MODSEL_DATA(IP0_13_12, TIOC0C_C, SEL_MTU2_CH0_1),
+
+	PINMUX_IPSR_DATA(IP0_15_14, A7),
+	PINMUX_IPSR_DATA(IP0_15_14, ST0_D3),
+	PINMUX_IPSR_MODSEL_DATA(IP0_15_14, LCD_DATA7_A, SEL_LCDC_0),
+	PINMUX_IPSR_MODSEL_DATA(IP0_15_14, TIOC0D_C, SEL_MTU2_CH0_1),
+
+	PINMUX_IPSR_DATA(IP0_17_16, A8),
+	PINMUX_IPSR_DATA(IP0_17_16, ST0_D4),
+	PINMUX_IPSR_MODSEL_DATA(IP0_17_16, LCD_DATA8_A, SEL_LCDC_0),
+	PINMUX_IPSR_MODSEL_DATA(IP0_17_16, TIOC1A_C, SEL_MTU2_CH1_2),
+
+	PINMUX_IPSR_DATA(IP0_19_18, A9),
+	PINMUX_IPSR_DATA(IP0_19_18, ST0_D5),
+	PINMUX_IPSR_MODSEL_DATA(IP0_19_18, LCD_DATA9_A, SEL_LCDC_0),
+	PINMUX_IPSR_MODSEL_DATA(IP0_19_18, TIOC1B_C, SEL_MTU2_CH1_2),
+
+	PINMUX_IPSR_DATA(IP0_21_20, A10),
+	PINMUX_IPSR_DATA(IP0_21_20, ST0_D6),
+	PINMUX_IPSR_MODSEL_DATA(IP0_21_20, LCD_DATA10_A, SEL_LCDC_0),
+	PINMUX_IPSR_MODSEL_DATA(IP0_21_20, TIOC2A_C, SEL_MTU2_CH2_2),
+
+	PINMUX_IPSR_DATA(IP0_23_22, A11),
+	PINMUX_IPSR_DATA(IP0_23_22, ST0_D7),
+	PINMUX_IPSR_MODSEL_DATA(IP0_23_22, LCD_DATA11_A, SEL_LCDC_0),
+	PINMUX_IPSR_MODSEL_DATA(IP0_23_22, TIOC2B_C, SEL_MTU2_CH2_2),
+
+	PINMUX_IPSR_DATA(IP0_25_24, A12),
+	PINMUX_IPSR_MODSEL_DATA(IP0_25_24, LCD_DATA12_A, SEL_LCDC_0),
+	PINMUX_IPSR_MODSEL_DATA(IP0_25_24, TIOC3A_C, SEL_MTU2_CH3_1),
+
+	PINMUX_IPSR_DATA(IP0_27_26, A13),
+	PINMUX_IPSR_MODSEL_DATA(IP0_27_26, LCD_DATA13_A, SEL_LCDC_0),
+	PINMUX_IPSR_MODSEL_DATA(IP0_27_26, TIOC3B_C, SEL_MTU2_CH3_1),
+
+	PINMUX_IPSR_DATA(IP0_29_28, A14),
+	PINMUX_IPSR_MODSEL_DATA(IP0_29_28, LCD_DATA14_A, SEL_LCDC_0),
+	PINMUX_IPSR_MODSEL_DATA(IP0_29_28, TIOC3C_C, SEL_MTU2_CH3_1),
+
+	PINMUX_IPSR_DATA(IP0_31_30, A15),
+	PINMUX_IPSR_DATA(IP0_31_30, ST0_VCO_CLKIN),
+	PINMUX_IPSR_MODSEL_DATA(IP0_31_30, LCD_DATA15_A, SEL_LCDC_0),
+	PINMUX_IPSR_MODSEL_DATA(IP0_31_30, TIOC3D_C, SEL_MTU2_CH3_1),
+
+
+	/* IPSR1 */
+	PINMUX_IPSR_DATA(IP1_1_0, A16),
+	PINMUX_IPSR_DATA(IP1_1_0, ST0_PWM),
+	PINMUX_IPSR_MODSEL_DATA(IP1_1_0, LCD_DON_A, SEL_LCDC_0),
+	PINMUX_IPSR_MODSEL_DATA(IP1_1_0, TIOC4A_C, SEL_MTU2_CH4_1),
+
+	PINMUX_IPSR_DATA(IP1_3_2, A17),
+	PINMUX_IPSR_DATA(IP1_3_2, ST1_VCO_CLKIN),
+	PINMUX_IPSR_MODSEL_DATA(IP1_3_2, LCD_CL1_A, SEL_LCDC_0),
+	PINMUX_IPSR_MODSEL_DATA(IP1_3_2, TIOC4B_C, SEL_MTU2_CH4_1),
+
+	PINMUX_IPSR_DATA(IP1_5_4, A18),
+	PINMUX_IPSR_DATA(IP1_5_4, ST1_PWM),
+	PINMUX_IPSR_MODSEL_DATA(IP1_5_4, LCD_CL2_A, SEL_LCDC_0),
+	PINMUX_IPSR_MODSEL_DATA(IP1_5_4, TIOC4C_C, SEL_MTU2_CH4_1),
+
+	PINMUX_IPSR_DATA(IP1_7_6, A19),
+	PINMUX_IPSR_DATA(IP1_7_6, ST1_CLKIN),
+	PINMUX_IPSR_MODSEL_DATA(IP1_7_6, LCD_CLK_A, SEL_LCDC_0),
+	PINMUX_IPSR_MODSEL_DATA(IP1_7_6, TIOC4D_C, SEL_MTU2_CH4_1),
+
+	PINMUX_IPSR_DATA(IP1_9_8, A20),
+	PINMUX_IPSR_DATA(IP1_9_8, ST1_REQ),
+	PINMUX_IPSR_MODSEL_DATA(IP1_9_8, LCD_FLM_A, SEL_LCDC_0),
+
+	PINMUX_IPSR_DATA(IP1_11_10, A21),
+	PINMUX_IPSR_DATA(IP1_11_10, ST1_SYC),
+	PINMUX_IPSR_MODSEL_DATA(IP1_11_10, LCD_VCPWC_A, SEL_LCDC_0),
+
+	PINMUX_IPSR_DATA(IP1_13_12, A22),
+	PINMUX_IPSR_DATA(IP1_13_12, ST1_VLD),
+	PINMUX_IPSR_MODSEL_DATA(IP1_13_12, LCD_VEPWC_A, SEL_LCDC_0),
+
+	PINMUX_IPSR_DATA(IP1_15_14, A23),
+	PINMUX_IPSR_DATA(IP1_15_14, ST1_D0),
+	PINMUX_IPSR_MODSEL_DATA(IP1_15_14, LCD_M_DISP_A, SEL_LCDC_0),
+
+	PINMUX_IPSR_DATA(IP1_17_16, A24),
+	PINMUX_IPSR_MODSEL_DATA(IP1_17_16, RX2_D, SEL_SCIF2_3),
+	PINMUX_IPSR_DATA(IP1_17_16, ST1_D1),
+
+	PINMUX_IPSR_DATA(IP1_19_18, A25),
+	PINMUX_IPSR_MODSEL_DATA(IP1_17_16, RX2_D, SEL_SCIF2_3),
+	PINMUX_IPSR_DATA(IP1_17_16, ST1_D2),
+
+	PINMUX_IPSR_DATA(IP1_22_20, D0),
+	PINMUX_IPSR_MODSEL_DATA(IP1_22_20, SD0_DAT0_A, SEL_SDHI0_0),
+	PINMUX_IPSR_MODSEL_DATA(IP1_22_20, MMC_D0_A, SEL_MMC_0),
+	PINMUX_IPSR_DATA(IP1_22_20, ST1_D3),
+	PINMUX_IPSR_MODSEL_DATA(IP1_22_20, FD0_A, SEL_FLCTL_0),
+
+	PINMUX_IPSR_DATA(IP1_25_23, D1),
+	PINMUX_IPSR_MODSEL_DATA(IP1_25_23, SD0_DAT0_A, SEL_SDHI0_0),
+	PINMUX_IPSR_MODSEL_DATA(IP1_25_23, MMC_D1_A, SEL_MMC_0),
+	PINMUX_IPSR_DATA(IP1_25_23, ST1_D4),
+	PINMUX_IPSR_MODSEL_DATA(IP1_25_23, FD1_A, SEL_FLCTL_0),
+
+	PINMUX_IPSR_DATA(IP1_28_26, D2),
+	PINMUX_IPSR_MODSEL_DATA(IP1_28_26, SD0_DAT0_A, SEL_SDHI0_0),
+	PINMUX_IPSR_MODSEL_DATA(IP1_28_26, MMC_D2_A, SEL_MMC_0),
+	PINMUX_IPSR_DATA(IP1_28_26, ST1_D5),
+	PINMUX_IPSR_MODSEL_DATA(IP1_28_26, FD2_A, SEL_FLCTL_0),
+
+	PINMUX_IPSR_DATA(IP1_31_29, D3),
+	PINMUX_IPSR_MODSEL_DATA(IP1_31_29, SD0_DAT0_A, SEL_SDHI0_0),
+	PINMUX_IPSR_MODSEL_DATA(IP1_31_29, MMC_D3_A, SEL_MMC_0),
+	PINMUX_IPSR_DATA(IP1_31_29, ST1_D6),
+	PINMUX_IPSR_MODSEL_DATA(IP1_31_29, FD3_A, SEL_FLCTL_0),
+
+	/* IPSR2 */
+	PINMUX_IPSR_DATA(IP2_2_0, D4),
+	PINMUX_IPSR_MODSEL_DATA(IP2_2_0, SD0_CD_A, SEL_SDHI0_0),
+	PINMUX_IPSR_MODSEL_DATA(IP2_2_0, MMC_D4_A, SEL_MMC_0),
+	PINMUX_IPSR_DATA(IP2_2_0, ST1_D7),
+	PINMUX_IPSR_MODSEL_DATA(IP2_2_0, FD4_A, SEL_FLCTL_0),
+
+	PINMUX_IPSR_DATA(IP2_4_3, D5),
+	PINMUX_IPSR_MODSEL_DATA(IP2_4_3, SD0_WP_A, SEL_SDHI0_0),
+	PINMUX_IPSR_MODSEL_DATA(IP2_4_3, MMC_D5_A, SEL_MMC_0),
+	PINMUX_IPSR_MODSEL_DATA(IP2_4_3, FD5_A, SEL_FLCTL_0),
+
+	PINMUX_IPSR_DATA(IP2_7_5, D6),
+	PINMUX_IPSR_MODSEL_DATA(IP2_7_5, RSPI_RSPCK_A, SEL_RSPI_0),
+	PINMUX_IPSR_MODSEL_DATA(IP2_7_5, MMC_D6_A, SEL_MMC_0),
+	PINMUX_IPSR_MODSEL_DATA(IP2_7_5, QSPCLK_A, SEL_RQSPI_0),
+	PINMUX_IPSR_MODSEL_DATA(IP2_7_5, FD6_A, SEL_FLCTL_0),
+
+	PINMUX_IPSR_DATA(IP2_10_8, D7),
+	PINMUX_IPSR_MODSEL_DATA(IP2_10_8, RSPI_SSL_A, SEL_RSPI_0),
+	PINMUX_IPSR_MODSEL_DATA(IP2_10_8, MMC_D7_A, SEL_MMC_0),
+	PINMUX_IPSR_MODSEL_DATA(IP2_10_8, QSSL_A, SEL_RQSPI_0),
+	PINMUX_IPSR_MODSEL_DATA(IP2_10_8, FD7_A, SEL_FLCTL_0),
+
+	PINMUX_IPSR_DATA(IP2_13_11, D8),
+	PINMUX_IPSR_MODSEL_DATA(IP2_13_11, SD0_CLK_A, SEL_SDHI0_0),
+	PINMUX_IPSR_MODSEL_DATA(IP2_13_11, MMC_CLK_A, SEL_MMC_0),
+	PINMUX_IPSR_MODSEL_DATA(IP2_13_11, QIO2_A, SEL_RQSPI_0),
+	PINMUX_IPSR_MODSEL_DATA(IP2_13_11, FCE_A, SEL_FLCTL_0),
+	PINMUX_IPSR_MODSEL_DATA(IP2_13_11, ET0_GTX_CLK_B, SEL_ET0_1),
+
+	PINMUX_IPSR_DATA(IP2_16_14, D9),
+	PINMUX_IPSR_MODSEL_DATA(IP2_16_14, SD0_CMD_A, SEL_SDHI0_0),
+	PINMUX_IPSR_MODSEL_DATA(IP2_16_14, MMC_CMD_A, SEL_MMC_0),
+	PINMUX_IPSR_MODSEL_DATA(IP2_16_14, QIO3_A, SEL_RQSPI_0),
+	PINMUX_IPSR_MODSEL_DATA(IP2_16_14, FCLE_A, SEL_FLCTL_0),
+	PINMUX_IPSR_MODSEL_DATA(IP2_16_14, ET0_ETXD1_B, SEL_ET0_1),
+
+	PINMUX_IPSR_DATA(IP2_19_17, D10),
+	PINMUX_IPSR_MODSEL_DATA(IP2_19_17, RSPI_MOSI_A, SEL_RSPI_0),
+	PINMUX_IPSR_MODSEL_DATA(IP2_19_17, QMO_QIO0_A, SEL_RQSPI_0),
+	PINMUX_IPSR_MODSEL_DATA(IP2_19_17, FALE_A, SEL_FLCTL_0),
+	PINMUX_IPSR_MODSEL_DATA(IP2_19_17, ET0_ETXD2_B, SEL_ET0_1),
+
+	PINMUX_IPSR_DATA(IP2_22_20, D11),
+	PINMUX_IPSR_MODSEL_DATA(IP2_22_20, RSPI_MISO_A, SEL_RSPI_0),
+	PINMUX_IPSR_MODSEL_DATA(IP2_22_20, QMI_QIO1_A, SEL_RQSPI_0),
+	PINMUX_IPSR_MODSEL_DATA(IP2_22_20, FRE_A, SEL_FLCTL_0),
+
+	PINMUX_IPSR_DATA(IP2_24_23, D12),
+	PINMUX_IPSR_MODSEL_DATA(IP2_24_23, FWE_A, SEL_FLCTL_0),
+	PINMUX_IPSR_MODSEL_DATA(IP2_24_23, ET0_ETXD5_B, SEL_ET0_1),
+
+	PINMUX_IPSR_DATA(IP2_27_25, D13),
+	PINMUX_IPSR_MODSEL_DATA(IP2_27_25, RX2_B, SEL_SCIF2_1),
+	PINMUX_IPSR_MODSEL_DATA(IP2_27_25, FRB_A, SEL_FLCTL_0),
+	PINMUX_IPSR_MODSEL_DATA(IP2_27_25, ET0_ETXD6_B, SEL_ET0_1),
+
+	PINMUX_IPSR_DATA(IP2_30_28, D14),
+	PINMUX_IPSR_MODSEL_DATA(IP2_30_28, TX2_B, SEL_SCIF2_1),
+	PINMUX_IPSR_MODSEL_DATA(IP2_30_28, FSE_A, SEL_FLCTL_0),
+	PINMUX_IPSR_MODSEL_DATA(IP2_30_28, ET0_TX_CLK_B, SEL_ET0_1),
+
+	/* IPSR3 */
+	PINMUX_IPSR_DATA(IP3_1_0, D15),
+	PINMUX_IPSR_MODSEL_DATA(IP3_1_0, SCK2_B, SEL_SCIF2_1),
+
+	PINMUX_IPSR_DATA(IP3_2, CS1_A26),
+	PINMUX_IPSR_MODSEL_DATA(IP3_2, QIO3_B, SEL_RQSPI_1),
+
+	PINMUX_IPSR_DATA(IP3_5_3, EX_CS1),
+	PINMUX_IPSR_MODSEL_DATA(IP3_5_3, RX3_B, SEL_SCIF2_1),
+	PINMUX_IPSR_DATA(IP3_5_3, ATACS0),
+	PINMUX_IPSR_MODSEL_DATA(IP3_5_3, QIO2_B, SEL_RQSPI_1),
+	PINMUX_IPSR_DATA(IP3_5_3, ET0_ETXD0),
+
+	PINMUX_IPSR_DATA(IP3_8_6, EX_CS2),
+	PINMUX_IPSR_MODSEL_DATA(IP3_8_6, TX3_B, SEL_SCIF3_1),
+	PINMUX_IPSR_DATA(IP3_8_6, ATACS1),
+	PINMUX_IPSR_MODSEL_DATA(IP3_8_6, QSPCLK_B, SEL_RQSPI_1),
+	PINMUX_IPSR_MODSEL_DATA(IP3_8_6, ET0_GTX_CLK_A, SEL_ET0_0),
+
+	PINMUX_IPSR_DATA(IP3_11_9, EX_CS3),
+	PINMUX_IPSR_MODSEL_DATA(IP3_11_9, SD1_CD_A, SEL_SDHI1_0),
+	PINMUX_IPSR_DATA(IP3_11_9, ATARD),
+	PINMUX_IPSR_MODSEL_DATA(IP3_11_9, QMO_QIO0_B, SEL_RQSPI_1),
+	PINMUX_IPSR_MODSEL_DATA(IP3_11_9, ET0_ETXD1_A, SEL_ET0_0),
+
+	PINMUX_IPSR_DATA(IP3_14_12, EX_CS4),
+	PINMUX_IPSR_MODSEL_DATA(IP3_14_12, SD1_WP_A, SEL_SDHI1_0),
+	PINMUX_IPSR_DATA(IP3_14_12, ATAWR),
+	PINMUX_IPSR_MODSEL_DATA(IP3_14_12, QMI_QIO1_B, SEL_RQSPI_1),
+	PINMUX_IPSR_MODSEL_DATA(IP3_14_12, ET0_ETXD2_A, SEL_ET0_0),
+
+	PINMUX_IPSR_DATA(IP3_17_15, EX_CS5),
+	PINMUX_IPSR_MODSEL_DATA(IP3_17_15, SD1_CMD_A, SEL_SDHI1_0),
+	PINMUX_IPSR_DATA(IP3_17_15, ATADIR),
+	PINMUX_IPSR_MODSEL_DATA(IP3_17_15, QSSL_B, SEL_RQSPI_1),
+	PINMUX_IPSR_MODSEL_DATA(IP3_17_15, ET0_ETXD3_A, SEL_ET0_0),
+
+	PINMUX_IPSR_DATA(IP3_19_18, RD_WR),
+	PINMUX_IPSR_DATA(IP3_19_18, TCLK0),
+	PINMUX_IPSR_MODSEL_DATA(IP3_19_18, CAN_CLK_B, SEL_RCAN_CLK_1),
+	PINMUX_IPSR_DATA(IP3_19_18, ET0_ETXD4),
+
+	PINMUX_IPSR_DATA(IP3_20, EX_WAIT0),
+	PINMUX_IPSR_MODSEL_DATA(IP3_20, TCLK1_B, SEL_TMU_1),
+
+	PINMUX_IPSR_DATA(IP3_23_21, EX_WAIT1),
+	PINMUX_IPSR_MODSEL_DATA(IP3_23_21, SD1_DAT0_A, SEL_SDHI1_0),
+	PINMUX_IPSR_DATA(IP3_23_21, DREQ2),
+	PINMUX_IPSR_MODSEL_DATA(IP3_23_21, CAN1_TX_C, SEL_RCAN1_2),
+	PINMUX_IPSR_MODSEL_DATA(IP3_23_21, ET0_LINK_C, SEL_ET0_CTL_2),
+	PINMUX_IPSR_MODSEL_DATA(IP3_23_21, ET0_ETXD5_A, SEL_ET0_0),
+
+	PINMUX_IPSR_DATA(IP3_26_24, EX_WAIT2),
+	PINMUX_IPSR_MODSEL_DATA(IP3_26_24, SD1_DAT1_A, SEL_SDHI1_0),
+	PINMUX_IPSR_DATA(IP3_26_24, DACK2),
+	PINMUX_IPSR_MODSEL_DATA(IP3_26_24, CAN1_RX_C, SEL_RCAN1_2),
+	PINMUX_IPSR_MODSEL_DATA(IP3_26_24, ET0_MAGIC_C, SEL_ET0_CTL_2),
+	PINMUX_IPSR_MODSEL_DATA(IP3_26_24, ET0_ETXD6_A, SEL_ET0_0),
+
+	PINMUX_IPSR_DATA(IP3_29_27, DRACK0),
+	PINMUX_IPSR_MODSEL_DATA(IP3_29_27, SD1_DAT2_A, SEL_SDHI1_0),
+	PINMUX_IPSR_DATA(IP3_29_27, ATAG),
+	PINMUX_IPSR_MODSEL_DATA(IP3_29_27, TCLK1_A, SEL_TMU_0),
+	PINMUX_IPSR_DATA(IP3_29_27, ET0_ETXD7),
+
+	/* IPSR4 */
+	PINMUX_IPSR_MODSEL_DATA(IP4_2_0, HCTS0_A, SEL_HSCIF_0),
+	PINMUX_IPSR_MODSEL_DATA(IP4_2_0, CTS1_A, SEL_SCIF1_0),
+	PINMUX_IPSR_DATA(IP4_2_0, VI0_FIELD),
+	PINMUX_IPSR_MODSEL_DATA(IP4_2_0, RMII0_RXD1_A, SEL_RMII_0),
+	PINMUX_IPSR_DATA(IP4_2_0, ET0_ERXD7),
+
+	PINMUX_IPSR_MODSEL_DATA(IP4_5_3, HRTS0_A, SEL_HSCIF_0),
+	PINMUX_IPSR_MODSEL_DATA(IP4_5_3, RTS1_A, SEL_SCIF1_0),
+	PINMUX_IPSR_DATA(IP4_5_3, VI0_HSYNC),
+	PINMUX_IPSR_MODSEL_DATA(IP4_5_3, RMII0_TXD_EN_A, SEL_RMII_0),
+	PINMUX_IPSR_DATA(IP4_5_3, ET0_RX_DV),
+
+	PINMUX_IPSR_MODSEL_DATA(IP4_8_6, HSCK0_A, SEL_HSCIF_0),
+	PINMUX_IPSR_MODSEL_DATA(IP4_8_6, SCK1_A, SEL_SCIF1_0),
+	PINMUX_IPSR_DATA(IP4_8_6, VI0_VSYNC),
+	PINMUX_IPSR_MODSEL_DATA(IP4_8_6, RMII0_RX_ER_A, SEL_RMII_0),
+	PINMUX_IPSR_DATA(IP4_8_6, ET0_RX_ER),
+
+	PINMUX_IPSR_MODSEL_DATA(IP4_11_9, HRX0_A, SEL_HSCIF_0),
+	PINMUX_IPSR_MODSEL_DATA(IP4_11_9, RX1_A, SEL_SCIF1_0),
+	PINMUX_IPSR_DATA(IP4_11_9, VI0_DATA0_VI0_B0),
+	PINMUX_IPSR_MODSEL_DATA(IP4_11_9, RMII0_CRS_DV_A, SEL_RMII_0),
+	PINMUX_IPSR_DATA(IP4_11_9, ET0_CRS),
+
+	PINMUX_IPSR_MODSEL_DATA(IP4_14_12, HTX0_A, SEL_HSCIF_0),
+	PINMUX_IPSR_MODSEL_DATA(IP4_14_12, TX1_A, SEL_SCIF1_0),
+	PINMUX_IPSR_DATA(IP4_14_12, VI0_DATA1_VI0_B1),
+	PINMUX_IPSR_MODSEL_DATA(IP4_14_12, RMII0_MDC_A, SEL_RMII_0),
+	PINMUX_IPSR_DATA(IP4_14_12, ET0_COL),
+
+	PINMUX_IPSR_MODSEL_DATA(IP4_17_15, CTS0_B, SEL_SCIF0_1),
+	PINMUX_IPSR_DATA(IP4_17_15, VI0_DATA2_VI0_B2),
+	PINMUX_IPSR_MODSEL_DATA(IP4_17_15, RMII0_MDIO_A, SEL_RMII_0),
+	PINMUX_IPSR_DATA(IP4_17_15, ET0_MDC),
+
+	PINMUX_IPSR_MODSEL_DATA(IP4_19_18, RTS0_B, SEL_SCIF0_1),
+	PINMUX_IPSR_DATA(IP4_19_18, VI0_DATA3_VI0_B3),
+	PINMUX_IPSR_MODSEL_DATA(IP4_19_18, ET0_MDIO_A, SEL_ET0_0),
+
+	PINMUX_IPSR_MODSEL_DATA(IP4_21_20, SCK1_B, SEL_SCIF1_1),
+	PINMUX_IPSR_DATA(IP4_21_20, VI0_DATA4_VI0_B4),
+	PINMUX_IPSR_MODSEL_DATA(IP4_21_20, ET0_LINK_A, SEL_ET0_CTL_0),
+
+	PINMUX_IPSR_MODSEL_DATA(IP4_23_22, RX1_B, SEL_SCIF1_1),
+	PINMUX_IPSR_DATA(IP4_23_22, VI0_DATA5_VI0_B5),
+	PINMUX_IPSR_MODSEL_DATA(IP4_23_22, ET0_MAGIC_A, SEL_ET0_CTL_0),
+
+	PINMUX_IPSR_MODSEL_DATA(IP4_25_24, TX1_B, SEL_SCIF1_1),
+	PINMUX_IPSR_DATA(IP4_25_24, VI0_DATA6_VI0_G0),
+	PINMUX_IPSR_MODSEL_DATA(IP4_25_24, ET0_PHY_INT_A, SEL_ET0_CTL_0),
+
+	PINMUX_IPSR_MODSEL_DATA(IP4_27_26, CTS1_B, SEL_SCIF1_1),
+	PINMUX_IPSR_DATA(IP4_27_26, VI0_DATA7_VI0_G1),
+
+	PINMUX_IPSR_MODSEL_DATA(IP4_29_28, RTS1_B, SEL_SCIF1_1),
+	PINMUX_IPSR_DATA(IP4_29_28, VI0_G2),
+
+	PINMUX_IPSR_MODSEL_DATA(IP4_31_30, SCK2_A, SEL_SCIF2_0),
+	PINMUX_IPSR_DATA(IP4_31_30, VI0_G3),
+
+	/* IPSR5 */
+	PINMUX_IPSR_MODSEL_DATA(IP5_2_0, SD2_CLK_A, SEL_SDHI2_0),
+	PINMUX_IPSR_MODSEL_DATA(IP5_2_0, RX2_A, SEL_SCIF2_0),
+	PINMUX_IPSR_DATA(IP5_2_0, VI0_G4),
+	PINMUX_IPSR_MODSEL_DATA(IP5_2_0, ET0_RX_CLK_B, SEL_ET0_1),
+
+	PINMUX_IPSR_MODSEL_DATA(IP5_5_3, SD2_CMD_A, SEL_SDHI2_0),
+	PINMUX_IPSR_MODSEL_DATA(IP5_5_3, TX2_A, SEL_SCIF2_0),
+	PINMUX_IPSR_DATA(IP5_5_3, VI0_G5),
+	PINMUX_IPSR_MODSEL_DATA(IP5_5_3, ET0_ERXD2_B, SEL_ET0_1),
+
+	PINMUX_IPSR_MODSEL_DATA(IP5_8_6, SD2_DAT0_A, SEL_SDHI2_0),
+	PINMUX_IPSR_MODSEL_DATA(IP5_8_6, RX3_A, SEL_SCIF3_0),
+	PINMUX_IPSR_DATA(IP4_8_6, VI0_R0),
+	PINMUX_IPSR_MODSEL_DATA(IP4_8_6, ET0_ERXD2_B, SEL_ET0_1),
+
+	PINMUX_IPSR_MODSEL_DATA(IP5_11_9, SD2_DAT1_A, SEL_SDHI2_0),
+	PINMUX_IPSR_MODSEL_DATA(IP5_11_9, TX3_A, SEL_SCIF3_0),
+	PINMUX_IPSR_DATA(IP5_11_9, VI0_R1),
+	PINMUX_IPSR_MODSEL_DATA(IP5_11_9, ET0_MDIO_B, SEL_ET0_1),
+
+	PINMUX_IPSR_MODSEL_DATA(IP5_14_12, SD2_DAT2_A, SEL_SDHI2_0),
+	PINMUX_IPSR_MODSEL_DATA(IP5_14_12, RX4_A, SEL_SCIF4_0),
+	PINMUX_IPSR_DATA(IP5_14_12, VI0_R2),
+	PINMUX_IPSR_MODSEL_DATA(IP5_14_12, ET0_LINK_B, SEL_ET0_CTL_1),
+
+	PINMUX_IPSR_MODSEL_DATA(IP5_17_15, SD2_DAT3_A, SEL_SDHI2_0),
+	PINMUX_IPSR_MODSEL_DATA(IP5_17_15, TX4_A, SEL_SCIF4_0),
+	PINMUX_IPSR_DATA(IP5_17_15, VI0_R3),
+	PINMUX_IPSR_MODSEL_DATA(IP5_17_15, ET0_MAGIC_B, SEL_ET0_CTL_1),
+
+	PINMUX_IPSR_MODSEL_DATA(IP5_20_18, SD2_CD_A, SEL_SDHI2_0),
+	PINMUX_IPSR_MODSEL_DATA(IP5_20_18, RX5_A, SEL_SCIF5_0),
+	PINMUX_IPSR_DATA(IP5_20_18, VI0_R4),
+	PINMUX_IPSR_MODSEL_DATA(IP5_20_18, ET0_PHY_INT_B, SEL_ET0_CTL_1),
+
+	PINMUX_IPSR_MODSEL_DATA(IP5_22_21, SD2_WP_A, SEL_SDHI2_0),
+	PINMUX_IPSR_MODSEL_DATA(IP5_22_21, TX5_A, SEL_SCIF5_0),
+	PINMUX_IPSR_DATA(IP5_22_21, VI0_R5),
+
+	PINMUX_IPSR_DATA(IP5_24_23, REF125CK),
+	PINMUX_IPSR_DATA(IP5_24_23, ADTRG),
+	PINMUX_IPSR_MODSEL_DATA(IP5_24_23, RX5_C, SEL_SCIF5_2),
+	PINMUX_IPSR_DATA(IP5_26_25, REF50CK),
+	PINMUX_IPSR_MODSEL_DATA(IP5_26_25, CTS1_E, SEL_SCIF1_3),
+	PINMUX_IPSR_MODSEL_DATA(IP5_26_25, HCTS0_D, SEL_HSCIF_3),
+
+	/* IPSR6 */
+	PINMUX_IPSR_DATA(IP6_2_0, DU0_DR0),
+	PINMUX_IPSR_MODSEL_DATA(IP6_2_0, SCIF_CLK_B, SEL_SCIF_CLK_1),
+	PINMUX_IPSR_MODSEL_DATA(IP6_2_0, HRX0_D, SEL_HSCIF_3),
+	PINMUX_IPSR_MODSEL_DATA(IP6_2_0, IETX_A, SEL_IEBUS_0),
+	PINMUX_IPSR_MODSEL_DATA(IP6_2_0, TCLKA_A, SEL_MTU2_CLK_0),
+	PINMUX_IPSR_DATA(IP6_2_0, HIFD00),
+
+	PINMUX_IPSR_DATA(IP6_5_3, DU0_DR1),
+	PINMUX_IPSR_MODSEL_DATA(IP6_5_3, SCK0_B, SEL_SCIF0_1),
+	PINMUX_IPSR_MODSEL_DATA(IP6_5_3, HTX0_D, SEL_HSCIF_3),
+	PINMUX_IPSR_MODSEL_DATA(IP6_5_3, IERX_A, SEL_IEBUS_0),
+	PINMUX_IPSR_MODSEL_DATA(IP6_5_3, TCLKB_A, SEL_MTU2_CLK_0),
+	PINMUX_IPSR_DATA(IP6_5_3, HIFD01),
+
+	PINMUX_IPSR_DATA(IP6_7_6, DU0_DR2),
+	PINMUX_IPSR_MODSEL_DATA(IP6_7_6, RX0_B, SEL_SCIF0_1),
+	PINMUX_IPSR_MODSEL_DATA(IP6_7_6, TCLKC_A, SEL_MTU2_CLK_0),
+	PINMUX_IPSR_DATA(IP6_7_6, HIFD02),
+
+	PINMUX_IPSR_DATA(IP6_9_8, DU0_DR3),
+	PINMUX_IPSR_MODSEL_DATA(IP6_9_8, TX0_B, SEL_SCIF0_1),
+	PINMUX_IPSR_MODSEL_DATA(IP6_9_8, TCLKD_A, SEL_MTU2_CLK_0),
+	PINMUX_IPSR_DATA(IP6_9_8, HIFD03),
+
+	PINMUX_IPSR_DATA(IP6_11_10, DU0_DR4),
+	PINMUX_IPSR_MODSEL_DATA(IP6_11_10, CTS0_C, SEL_SCIF0_2),
+	PINMUX_IPSR_MODSEL_DATA(IP6_11_10, TIOC0A_A, SEL_MTU2_CH0_0),
+	PINMUX_IPSR_DATA(IP6_11_10, HIFD04),
+
+	PINMUX_IPSR_DATA(IP6_13_12, DU0_DR5),
+	PINMUX_IPSR_MODSEL_DATA(IP6_13_12, RTS0_C, SEL_SCIF0_1),
+	PINMUX_IPSR_MODSEL_DATA(IP6_13_12, TIOC0B_A, SEL_MTU2_CH0_0),
+	PINMUX_IPSR_DATA(IP6_13_12, HIFD05),
+
+	PINMUX_IPSR_DATA(IP6_15_14, DU0_DR6),
+	PINMUX_IPSR_MODSEL_DATA(IP6_15_14, SCK1_C, SEL_SCIF1_2),
+	PINMUX_IPSR_MODSEL_DATA(IP6_15_14, TIOC0C_A, SEL_MTU2_CH0_0),
+	PINMUX_IPSR_DATA(IP6_15_14, HIFD06),
+
+	PINMUX_IPSR_DATA(IP6_17_16, DU0_DR7),
+	PINMUX_IPSR_MODSEL_DATA(IP6_17_16, RX1_C, SEL_SCIF1_2),
+	PINMUX_IPSR_MODSEL_DATA(IP6_17_16, TIOC0D_A, SEL_MTU2_CH0_0),
+	PINMUX_IPSR_DATA(IP6_17_16, HIFD07),
+
+	PINMUX_IPSR_DATA(IP6_20_18, DU0_DG0),
+	PINMUX_IPSR_MODSEL_DATA(IP6_20_18, TX1_C, SEL_SCIF1_2),
+	PINMUX_IPSR_MODSEL_DATA(IP6_20_18, HSCK0_D, SEL_HSCIF_3),
+	PINMUX_IPSR_MODSEL_DATA(IP6_20_18, IECLK_A, SEL_IEBUS_0),
+	PINMUX_IPSR_MODSEL_DATA(IP6_20_18, TIOC1A_A, SEL_MTU2_CH1_0),
+	PINMUX_IPSR_DATA(IP6_20_18, HIFD08),
+
+	PINMUX_IPSR_DATA(IP6_23_21, DU0_DG1),
+	PINMUX_IPSR_MODSEL_DATA(IP6_23_21, CTS1_C, SEL_SCIF1_2),
+	PINMUX_IPSR_MODSEL_DATA(IP6_23_21, HRTS0_D, SEL_HSCIF_3),
+	PINMUX_IPSR_MODSEL_DATA(IP6_23_21, TIOC1B_A, SEL_MTU2_CH1_0),
+	PINMUX_IPSR_DATA(IP6_23_21, HIFD09),
+
+	/* IPSR7 */
+	PINMUX_IPSR_DATA(IP7_2_0, DU0_DG2),
+	PINMUX_IPSR_MODSEL_DATA(IP7_2_0, RTS1_C, SEL_SCIF1_2),
+	PINMUX_IPSR_MODSEL_DATA(IP7_2_0, RMII0_MDC_B, SEL_RMII_1),
+	PINMUX_IPSR_MODSEL_DATA(IP7_2_0, TIOC2A_A, SEL_MTU2_CH2_0),
+	PINMUX_IPSR_DATA(IP7_2_0, HIFD10),
+
+	PINMUX_IPSR_DATA(IP7_5_3, DU0_DG3),
+	PINMUX_IPSR_MODSEL_DATA(IP7_5_3, SCK2_C, SEL_SCIF2_2),
+	PINMUX_IPSR_MODSEL_DATA(IP7_5_3, RMII0_MDIO_B, SEL_RMII_1),
+	PINMUX_IPSR_MODSEL_DATA(IP7_5_3, TIOC2B_A, SEL_MTU2_CH2_0),
+	PINMUX_IPSR_DATA(IP7_5_3, HIFD11),
+
+	PINMUX_IPSR_DATA(IP7_8_6, DU0_DG4),
+	PINMUX_IPSR_MODSEL_DATA(IP7_8_6, RX2_C, SEL_SCIF2_2),
+	PINMUX_IPSR_MODSEL_DATA(IP7_8_6, RMII0_CRS_DV_B, SEL_RMII_1),
+	PINMUX_IPSR_MODSEL_DATA(IP7_8_6, TIOC3A_A, SEL_MTU2_CH3_0),
+	PINMUX_IPSR_DATA(IP7_8_6, HIFD12),
+
+	PINMUX_IPSR_DATA(IP7_11_9, DU0_DG5),
+	PINMUX_IPSR_MODSEL_DATA(IP7_11_9, TX2_C, SEL_SCIF2_2),
+	PINMUX_IPSR_MODSEL_DATA(IP7_11_9, RMII0_RX_ER_B, SEL_RMII_1),
+	PINMUX_IPSR_MODSEL_DATA(IP7_11_9, TIOC3B_A, SEL_MTU2_CH3_0),
+	PINMUX_IPSR_DATA(IP7_11_9, HIFD13),
+
+	PINMUX_IPSR_DATA(IP7_14_12, DU0_DG6),
+	PINMUX_IPSR_MODSEL_DATA(IP7_14_12, RX3_C, SEL_SCIF3_2),
+	PINMUX_IPSR_MODSEL_DATA(IP7_14_12, RMII0_RXD0_B, SEL_RMII_1),
+	PINMUX_IPSR_MODSEL_DATA(IP7_14_12, TIOC3C_A, SEL_MTU2_CH3_0),
+	PINMUX_IPSR_DATA(IP7_14_12, HIFD14),
+
+	PINMUX_IPSR_DATA(IP7_17_15, DU0_DG7),
+	PINMUX_IPSR_MODSEL_DATA(IP7_17_15, TX3_C, SEL_SCIF3_2),
+	PINMUX_IPSR_MODSEL_DATA(IP7_17_15, RMII0_RXD1_B, SEL_RMII_1),
+	PINMUX_IPSR_MODSEL_DATA(IP7_17_15, TIOC3D_A, SEL_MTU2_CH3_0),
+	PINMUX_IPSR_DATA(IP7_17_15, HIFD15),
+
+	PINMUX_IPSR_DATA(IP7_20_18, DU0_DB0),
+	PINMUX_IPSR_MODSEL_DATA(IP7_20_18, RX4_C, SEL_SCIF4_2),
+	PINMUX_IPSR_MODSEL_DATA(IP7_20_18, RMII0_TXD_EN_B, SEL_RMII_1),
+	PINMUX_IPSR_MODSEL_DATA(IP7_20_18, TIOC4A_A, SEL_MTU2_CH4_0),
+	PINMUX_IPSR_DATA(IP7_20_18, HIFCS),
+
+	PINMUX_IPSR_DATA(IP7_23_21, DU0_DB1),
+	PINMUX_IPSR_MODSEL_DATA(IP7_23_21, TX4_C, SEL_SCIF4_2),
+	PINMUX_IPSR_MODSEL_DATA(IP7_23_21, RMII0_TXD0_B, SEL_RMII_1),
+	PINMUX_IPSR_MODSEL_DATA(IP7_23_21, TIOC4B_A, SEL_MTU2_CH4_0),
+	PINMUX_IPSR_DATA(IP7_23_21, HIFWR),
+
+	PINMUX_IPSR_DATA(IP7_26_24, DU0_DB2),
+	PINMUX_IPSR_MODSEL_DATA(IP7_26_24, RX5_B, SEL_SCIF5_1),
+	PINMUX_IPSR_MODSEL_DATA(IP7_26_24, RMII0_TXD1_B, SEL_RMII_1),
+	PINMUX_IPSR_MODSEL_DATA(IP7_26_24, TIOC4C_A, SEL_MTU2_CH4_0),
+
+	PINMUX_IPSR_DATA(IP7_28_27, DU0_DB3),
+	PINMUX_IPSR_MODSEL_DATA(IP7_28_27, TX5_B, SEL_SCIF5_1),
+	PINMUX_IPSR_MODSEL_DATA(IP7_28_27, TIOC4D_A, SEL_MTU2_CH4_0),
+	PINMUX_IPSR_DATA(IP7_28_27, HIFRD),
+
+	PINMUX_IPSR_DATA(IP7_30_29, DU0_DB4),
+	PINMUX_IPSR_DATA(IP7_30_29, HIFINT),
+
+	/* IPSR8 */
+	PINMUX_IPSR_DATA(IP8_1_0, DU0_DB5),
+	PINMUX_IPSR_DATA(IP8_1_0, HIFDREQ),
+
+	PINMUX_IPSR_DATA(IP8_3_2, DU0_DB6),
+	PINMUX_IPSR_DATA(IP8_3_2, HIFRDY),
+
+	PINMUX_IPSR_DATA(IP8_5_4, DU0_DB7),
+	PINMUX_IPSR_MODSEL_DATA(IP8_5_4, SSI_SCK0_B, SEL_SSI0_1),
+	PINMUX_IPSR_MODSEL_DATA(IP8_5_4, HIFEBL_B, SEL_HIF_1),
+
+	PINMUX_IPSR_DATA(IP8_7_6, DU0_DOTCLKIN),
+	PINMUX_IPSR_MODSEL_DATA(IP8_7_6, HSPI_CS0_C, SEL_HSPI_2),
+	PINMUX_IPSR_MODSEL_DATA(IP8_7_6, SSI_WS0_B, SEL_SSI0_1),
+
+	PINMUX_IPSR_DATA(IP8_9_8, DU0_DOTCLKOUT),
+	PINMUX_IPSR_MODSEL_DATA(IP8_9_8, HSPI_CLK0_C, SEL_HSPI_2),
+	PINMUX_IPSR_MODSEL_DATA(IP8_9_8, SSI_SDATA0_B, SEL_SSI0_1),
+
+	PINMUX_IPSR_DATA(IP8_11_10, DU0_EXHSYNC_DU0_HSYNC),
+	PINMUX_IPSR_MODSEL_DATA(IP8_11_10, HSPI_TX0_C, SEL_HSPI_2),
+	PINMUX_IPSR_MODSEL_DATA(IP8_11_10, SSI_SCK1_B, SEL_SSI1_1),
+
+	PINMUX_IPSR_DATA(IP8_13_12, DU0_EXVSYNC_DU0_VSYNC),
+	PINMUX_IPSR_MODSEL_DATA(IP8_13_12, HSPI_RX0_C, SEL_HSPI_2),
+	PINMUX_IPSR_MODSEL_DATA(IP8_13_12, SSI_WS1_B, SEL_SSI1_1),
+
+	PINMUX_IPSR_DATA(IP8_15_14, DU0_EXODDF_DU0_ODDF),
+	PINMUX_IPSR_MODSEL_DATA(IP8_15_14, CAN0_RX_B, SEL_RCAN0_1),
+	PINMUX_IPSR_MODSEL_DATA(IP8_15_14, HSCK0_B, SEL_HSCIF_1),
+	PINMUX_IPSR_MODSEL_DATA(IP8_15_14, SSI_SDATA1_B, SEL_SSI1_1),
+
+	PINMUX_IPSR_DATA(IP8_17_16, DU0_DISP),
+	PINMUX_IPSR_MODSEL_DATA(IP8_17_16, CAN0_TX_B, SEL_RCAN0_1),
+	PINMUX_IPSR_MODSEL_DATA(IP8_17_16, HRX0_B, SEL_HSCIF_1),
+	PINMUX_IPSR_MODSEL_DATA(IP8_17_16, AUDIO_CLKA_B, SEL_AUDIO_CLKA_1),
+
+	PINMUX_IPSR_DATA(IP8_19_18, DU0_CDE),
+	PINMUX_IPSR_MODSEL_DATA(IP8_19_18, HTX0_B, SEL_HSCIF_1),
+	PINMUX_IPSR_MODSEL_DATA(IP8_19_18, AUDIO_CLKB_B, SEL_AUDIO_CLKB_1),
+	PINMUX_IPSR_MODSEL_DATA(IP8_19_18, LCD_VCPWC_B, SEL_LCDC_1),
+
+	PINMUX_IPSR_MODSEL_DATA(IP8_22_20, IRQ0_A, SEL_INTC_0),
+	PINMUX_IPSR_MODSEL_DATA(IP8_22_20, HSPI_TX_B, SEL_HSPI_1),
+	PINMUX_IPSR_MODSEL_DATA(IP8_22_20, RX3_E, SEL_SCIF3_4),
+	PINMUX_IPSR_DATA(IP8_22_20, ET0_ERXD0),
+
+	PINMUX_IPSR_MODSEL_DATA(IP8_25_23, IRQ1_A, SEL_INTC_0),
+	PINMUX_IPSR_MODSEL_DATA(IP8_25_23, HSPI_RX_B, SEL_HSPI_1),
+	PINMUX_IPSR_MODSEL_DATA(IP8_25_23, TX3_E, SEL_SCIF3_4),
+	PINMUX_IPSR_DATA(IP8_25_23, ET0_ERXD1),
+
+	PINMUX_IPSR_MODSEL_DATA(IP8_27_26, IRQ2_A, SEL_INTC_0),
+	PINMUX_IPSR_MODSEL_DATA(IP8_27_26, CTS0_A, SEL_SCIF0_0),
+	PINMUX_IPSR_MODSEL_DATA(IP8_27_26, HCTS0_B, SEL_HSCIF_1),
+	PINMUX_IPSR_MODSEL_DATA(IP8_27_26, ET0_ERXD2_A, SEL_ET0_0),
+
+	PINMUX_IPSR_MODSEL_DATA(IP8_29_28, IRQ3_A, SEL_INTC_0),
+	PINMUX_IPSR_MODSEL_DATA(IP8_29_28, RTS0_A, SEL_SCIF0_0),
+	PINMUX_IPSR_MODSEL_DATA(IP8_29_28, HRTS0_B, SEL_HSCIF_1),
+	PINMUX_IPSR_MODSEL_DATA(IP8_29_28, ET0_ERXD3_A, SEL_ET0_0),
+
+	/* IPSR9 */
+	PINMUX_IPSR_MODSEL_DATA(IP9_1_0, VI1_CLK_A, SEL_VIN1_0),
+	PINMUX_IPSR_MODSEL_DATA(IP9_1_0, FD0_B, SEL_FLCTL_1),
+	PINMUX_IPSR_MODSEL_DATA(IP9_1_0, LCD_DATA0_B, SEL_LCDC_1),
+
+	PINMUX_IPSR_MODSEL_DATA(IP9_3_2, VI1_0_A, SEL_VIN1_0),
+	PINMUX_IPSR_MODSEL_DATA(IP9_3_2, FD1_B, SEL_FLCTL_1),
+	PINMUX_IPSR_MODSEL_DATA(IP9_3_2, LCD_DATA1_B, SEL_LCDC_1),
+
+	PINMUX_IPSR_MODSEL_DATA(IP9_5_4, VI1_1_A, SEL_VIN1_0),
+	PINMUX_IPSR_MODSEL_DATA(IP9_5_4, FD2_B, SEL_FLCTL_1),
+	PINMUX_IPSR_MODSEL_DATA(IP9_5_4, LCD_DATA2_B, SEL_LCDC_1),
+
+	PINMUX_IPSR_MODSEL_DATA(IP9_7_6, VI1_2_A, SEL_VIN1_0),
+	PINMUX_IPSR_MODSEL_DATA(IP9_7_6, FD3_B, SEL_FLCTL_1),
+	PINMUX_IPSR_MODSEL_DATA(IP9_7_6, LCD_DATA3_B, SEL_LCDC_1),
+
+	PINMUX_IPSR_MODSEL_DATA(IP9_9_8, VI1_3_A, SEL_VIN1_0),
+	PINMUX_IPSR_MODSEL_DATA(IP9_9_8, FD4_B, SEL_FLCTL_1),
+	PINMUX_IPSR_MODSEL_DATA(IP9_9_8, LCD_DATA4_B, SEL_LCDC_1),
+
+	PINMUX_IPSR_MODSEL_DATA(IP9_11_10, VI1_4_A, SEL_VIN1_0),
+	PINMUX_IPSR_MODSEL_DATA(IP9_11_10, FD5_B, SEL_FLCTL_1),
+	PINMUX_IPSR_MODSEL_DATA(IP9_11_10, LCD_DATA5_B, SEL_LCDC_1),
+
+	PINMUX_IPSR_MODSEL_DATA(IP9_13_12, VI1_5_A, SEL_VIN1_0),
+	PINMUX_IPSR_MODSEL_DATA(IP9_13_12, FD6_B, SEL_FLCTL_1),
+	PINMUX_IPSR_MODSEL_DATA(IP9_13_12, LCD_DATA6_B, SEL_LCDC_1),
+
+	PINMUX_IPSR_MODSEL_DATA(IP9_15_14, VI1_6_A, SEL_VIN1_0),
+	PINMUX_IPSR_MODSEL_DATA(IP9_15_14, FD7_B, SEL_FLCTL_1),
+	PINMUX_IPSR_MODSEL_DATA(IP9_15_14, LCD_DATA7_B, SEL_LCDC_1),
+
+	PINMUX_IPSR_MODSEL_DATA(IP9_17_16, VI1_7_A, SEL_VIN1_0),
+	PINMUX_IPSR_MODSEL_DATA(IP9_17_16, FCE_B, SEL_FLCTL_1),
+	PINMUX_IPSR_MODSEL_DATA(IP9_17_16, LCD_DATA8_B, SEL_LCDC_1),
+
+	PINMUX_IPSR_MODSEL_DATA(IP9_19_18, SSI_SCK0_A, SEL_SSI0_0),
+	PINMUX_IPSR_MODSEL_DATA(IP9_19_18, TIOC1A_B, SEL_MTU2_CH1_1),
+	PINMUX_IPSR_MODSEL_DATA(IP9_19_18, LCD_DATA9_B, SEL_LCDC_1),
+
+	PINMUX_IPSR_MODSEL_DATA(IP9_21_20, SSI_WS0_A, SEL_SSI0_0),
+	PINMUX_IPSR_MODSEL_DATA(IP9_21_20, TIOC1B_B, SEL_MTU2_CH1_1),
+	PINMUX_IPSR_MODSEL_DATA(IP9_21_20, LCD_DATA10_B, SEL_LCDC_1),
+
+	PINMUX_IPSR_MODSEL_DATA(IP9_23_22, SSI_SDATA0_A, SEL_SSI0_0),
+	PINMUX_IPSR_MODSEL_DATA(IP9_23_22, VI1_0_B, SEL_VIN1_1),
+	PINMUX_IPSR_MODSEL_DATA(IP9_23_22, TIOC2A_B, SEL_MTU2_CH2_1),
+	PINMUX_IPSR_MODSEL_DATA(IP9_23_22, LCD_DATA11_B, SEL_LCDC_1),
+
+	PINMUX_IPSR_MODSEL_DATA(IP9_25_24, SSI_SCK1_A, SEL_SSI1_0),
+	PINMUX_IPSR_MODSEL_DATA(IP9_25_24, VI1_1_B, SEL_VIN1_1),
+	PINMUX_IPSR_MODSEL_DATA(IP9_25_24, TIOC2B_B, SEL_MTU2_CH2_1),
+	PINMUX_IPSR_MODSEL_DATA(IP9_25_24, LCD_DATA12_B, SEL_LCDC_1),
+
+	PINMUX_IPSR_MODSEL_DATA(IP9_27_26, SSI_WS1_A, SEL_SSI1_0),
+	PINMUX_IPSR_MODSEL_DATA(IP9_27_26, VI1_2_B, SEL_VIN1_1),
+	PINMUX_IPSR_MODSEL_DATA(IP9_27_26, LCD_DATA13_B, SEL_LCDC_1),
+
+	PINMUX_IPSR_MODSEL_DATA(IP9_29_28, SSI_SDATA1_A, SEL_SSI1_0),
+	PINMUX_IPSR_MODSEL_DATA(IP9_29_28, VI1_3_B, SEL_VIN1_1),
+	PINMUX_IPSR_MODSEL_DATA(IP9_29_28, LCD_DATA14_B, SEL_LCDC_1),
+
+	/* IPSE10 */
+	PINMUX_IPSR_DATA(IP10_2_0, SSI_SCK23),
+	PINMUX_IPSR_MODSEL_DATA(IP10_2_0, VI1_4_B, SEL_VIN1_1),
+	PINMUX_IPSR_MODSEL_DATA(IP10_2_0, RX1_D, SEL_SCIF1_3),
+	PINMUX_IPSR_MODSEL_DATA(IP10_2_0, FCLE_B, SEL_FLCTL_1),
+	PINMUX_IPSR_MODSEL_DATA(IP10_2_0, LCD_DATA15_B, SEL_LCDC_1),
+
+	PINMUX_IPSR_DATA(IP10_5_3, SSI_WS23),
+	PINMUX_IPSR_MODSEL_DATA(IP10_5_3, VI1_5_B, SEL_VIN1_1),
+	PINMUX_IPSR_MODSEL_DATA(IP10_5_3, TX1_D, SEL_SCIF1_3),
+	PINMUX_IPSR_MODSEL_DATA(IP10_5_3, HSCK0_C, SEL_HSCIF_2),
+	PINMUX_IPSR_MODSEL_DATA(IP10_5_3, FALE_B, SEL_FLCTL_1),
+	PINMUX_IPSR_MODSEL_DATA(IP10_5_3, LCD_DON_B, SEL_LCDC_1),
+
+	PINMUX_IPSR_DATA(IP10_8_6, SSI_SDATA2),
+	PINMUX_IPSR_MODSEL_DATA(IP10_8_6, VI1_6_B, SEL_VIN1_1),
+	PINMUX_IPSR_MODSEL_DATA(IP10_8_6, HRX0_C, SEL_HSCIF_2),
+	PINMUX_IPSR_MODSEL_DATA(IP10_8_6, FRE_B, SEL_FLCTL_1),
+	PINMUX_IPSR_MODSEL_DATA(IP10_8_6, LCD_CL1_B, SEL_LCDC_1),
+
+	PINMUX_IPSR_DATA(IP10_11_9, SSI_SDATA3),
+	PINMUX_IPSR_MODSEL_DATA(IP10_11_9, VI1_7_B, SEL_VIN1_1),
+	PINMUX_IPSR_MODSEL_DATA(IP10_11_9, HTX0_C, SEL_HSCIF_2),
+	PINMUX_IPSR_MODSEL_DATA(IP10_11_9, FWE_B, SEL_FLCTL_1),
+	PINMUX_IPSR_MODSEL_DATA(IP10_11_9, LCD_CL2_B, SEL_LCDC_1),
+
+	PINMUX_IPSR_MODSEL_DATA(IP10_14_12, AUDIO_CLKA_A, SEL_AUDIO_CLKA_0),
+	PINMUX_IPSR_MODSEL_DATA(IP10_14_12, VI1_CLK_B, SEL_VIN1_1),
+	PINMUX_IPSR_MODSEL_DATA(IP10_14_12, SCK1_D, SEL_SCIF1_3),
+	PINMUX_IPSR_MODSEL_DATA(IP10_14_12, IECLK_B, SEL_IEBUS_1),
+	PINMUX_IPSR_MODSEL_DATA(IP10_14_12, LCD_FLM_B, SEL_LCDC_1),
+
+	PINMUX_IPSR_MODSEL_DATA(IP10_15, AUDIO_CLKB_A, SEL_AUDIO_CLKB_0),
+	PINMUX_IPSR_MODSEL_DATA(IP10_15, LCD_CLK_B, SEL_LCDC_1),
+
+	PINMUX_IPSR_DATA(IP10_18_16, AUDIO_CLKC),
+	PINMUX_IPSR_MODSEL_DATA(IP10_18_16, SCK1_E, SEL_SCIF1_4),
+	PINMUX_IPSR_MODSEL_DATA(IP10_18_16, HCTS0_C, SEL_HSCIF_2),
+	PINMUX_IPSR_MODSEL_DATA(IP10_18_16, FRB_B, SEL_FLCTL_1),
+	PINMUX_IPSR_MODSEL_DATA(IP10_18_16, LCD_VEPWC_B, SEL_LCDC_1),
+
+	PINMUX_IPSR_DATA(IP10_21_19, AUDIO_CLKOUT),
+	PINMUX_IPSR_MODSEL_DATA(IP10_21_19, TX1_E, SEL_SCIF1_4),
+	PINMUX_IPSR_MODSEL_DATA(IP10_21_19, HRTS0_C, SEL_HSCIF_2),
+	PINMUX_IPSR_MODSEL_DATA(IP10_21_19, FSE_B, SEL_FLCTL_1),
+	PINMUX_IPSR_MODSEL_DATA(IP10_21_19, LCD_M_DISP_B, SEL_LCDC_1),
+
+	PINMUX_IPSR_MODSEL_DATA(IP10_22, CAN_CLK_A, SEL_RCAN_CLK_0),
+	PINMUX_IPSR_MODSEL_DATA(IP10_22, RX4_D, SEL_SCIF4_3),
+
+	PINMUX_IPSR_MODSEL_DATA(IP10_24_23, CAN0_TX_A, SEL_RCAN0_0),
+	PINMUX_IPSR_MODSEL_DATA(IP10_24_23, TX4_D, SEL_SCIF4_3),
+	PINMUX_IPSR_DATA(IP10_24_23, MLB_CLK),
+
+	PINMUX_IPSR_MODSEL_DATA(IP10_25, CAN1_RX_A, SEL_RCAN1_0),
+	PINMUX_IPSR_MODSEL_DATA(IP10_25, IRQ1_B, SEL_INTC_1),
+
+	PINMUX_IPSR_MODSEL_DATA(IP10_27_26, CAN0_RX_A, SEL_RCAN0_0),
+	PINMUX_IPSR_MODSEL_DATA(IP10_27_26, IRQ0_B, SEL_INTC_1),
+	PINMUX_IPSR_DATA(IP10_27_26, MLB_SIG),
+
+	PINMUX_IPSR_MODSEL_DATA(IP10_29_28, CAN1_TX_A, SEL_RCAN1_0),
+	PINMUX_IPSR_MODSEL_DATA(IP10_29_28, TX5_C, SEL_SCIF1_2),
+	PINMUX_IPSR_DATA(IP10_29_28, MLB_DAT),
+
+	/* IPSR11 */
+	PINMUX_IPSR_DATA(IP11_0, SCL1),
+	PINMUX_IPSR_MODSEL_DATA(IP11_0, SCIF_CLK_C, SEL_SCIF_CLK_2),
+
+	PINMUX_IPSR_DATA(IP11_1, SDA1),
+	PINMUX_IPSR_MODSEL_DATA(IP11_0, RX1_E, SEL_SCIF1_4),
+
+	PINMUX_IPSR_DATA(IP11_2, SDA0),
+	PINMUX_IPSR_MODSEL_DATA(IP11_2, HIFEBL_A, SEL_HIF_0),
+
+	PINMUX_IPSR_DATA(IP11_3, SDSELF),
+	PINMUX_IPSR_MODSEL_DATA(IP11_3, RTS1_E, SEL_SCIF1_3),
+
+	PINMUX_IPSR_MODSEL_DATA(IP11_6_4, SCIF_CLK_A, SEL_SCIF_CLK_0),
+	PINMUX_IPSR_MODSEL_DATA(IP11_6_4, HSPI_CLK_A, SEL_HSPI_0),
+	PINMUX_IPSR_DATA(IP11_6_4, VI0_CLK),
+	PINMUX_IPSR_MODSEL_DATA(IP11_6_4, RMII0_TXD0_A, SEL_RMII_0),
+	PINMUX_IPSR_DATA(IP11_6_4, ET0_ERXD4),
+
+	PINMUX_IPSR_MODSEL_DATA(IP11_9_7, SCK0_A, SEL_SCIF0_0),
+	PINMUX_IPSR_MODSEL_DATA(IP11_9_7, HSPI_CS_A, SEL_HSPI_0),
+	PINMUX_IPSR_DATA(IP11_9_7, VI0_CLKENB),
+	PINMUX_IPSR_MODSEL_DATA(IP11_9_7, RMII0_TXD1_A, SEL_RMII_0),
+	PINMUX_IPSR_DATA(IP11_9_7, ET0_ERXD5),
+
+	PINMUX_IPSR_MODSEL_DATA(IP11_11_10, RX0_A, SEL_SCIF0_0),
+	PINMUX_IPSR_MODSEL_DATA(IP11_11_10, HSPI_RX_A, SEL_HSPI_0),
+	PINMUX_IPSR_MODSEL_DATA(IP11_11_10, RMII0_RXD0_A, SEL_RMII_0),
+	PINMUX_IPSR_DATA(IP11_11_10, ET0_ERXD6),
+
+	PINMUX_IPSR_MODSEL_DATA(IP11_12, TX0_A, SEL_SCIF0_0),
+	PINMUX_IPSR_MODSEL_DATA(IP11_12, HSPI_TX_A, SEL_HSPI_0),
+
+	PINMUX_IPSR_DATA(IP11_15_13, PENC1),
+	PINMUX_IPSR_MODSEL_DATA(IP11_15_13, TX3_D, SEL_SCIF3_3),
+	PINMUX_IPSR_MODSEL_DATA(IP11_15_13, CAN1_TX_B,  SEL_RCAN1_1),
+	PINMUX_IPSR_MODSEL_DATA(IP11_15_13, TX5_D, SEL_SCIF5_3),
+	PINMUX_IPSR_MODSEL_DATA(IP11_15_13, IETX_B, SEL_IEBUS_1),
+
+	PINMUX_IPSR_DATA(IP11_18_16, USB_OVC1),
+	PINMUX_IPSR_MODSEL_DATA(IP11_18_16, RX3_D, SEL_SCIF3_3),
+	PINMUX_IPSR_MODSEL_DATA(IP11_18_16, CAN1_RX_B, SEL_RCAN1_1),
+	PINMUX_IPSR_MODSEL_DATA(IP11_18_16, RX5_D, SEL_SCIF5_3),
+	PINMUX_IPSR_MODSEL_DATA(IP11_18_16, IERX_B, SEL_IEBUS_1),
+
+	PINMUX_IPSR_DATA(IP11_20_19, DREQ0),
+	PINMUX_IPSR_MODSEL_DATA(IP11_20_19, SD1_CLK_A, SEL_SDHI1_0),
+	PINMUX_IPSR_DATA(IP11_20_19, ET0_TX_EN),
+
+	PINMUX_IPSR_DATA(IP11_22_21, DACK0),
+	PINMUX_IPSR_MODSEL_DATA(IP11_22_21, SD1_DAT3_A, SEL_SDHI1_0),
+	PINMUX_IPSR_DATA(IP11_22_21, ET0_TX_ER),
+
+	PINMUX_IPSR_DATA(IP11_25_23, DREQ1),
+	PINMUX_IPSR_MODSEL_DATA(IP11_25_23, HSPI_CLK_B, SEL_HSPI_1),
+	PINMUX_IPSR_MODSEL_DATA(IP11_25_23, RX4_B, SEL_SCIF4_1),
+	PINMUX_IPSR_MODSEL_DATA(IP11_25_23, ET0_PHY_INT_C, SEL_ET0_CTL_0),
+	PINMUX_IPSR_MODSEL_DATA(IP11_25_23, ET0_TX_CLK_A, SEL_ET0_0),
+
+	PINMUX_IPSR_DATA(IP11_27_26, DACK1),
+	PINMUX_IPSR_MODSEL_DATA(IP11_27_26, HSPI_CS_B, SEL_HSPI_1),
+	PINMUX_IPSR_MODSEL_DATA(IP11_27_26, TX4_B, SEL_SCIF3_1),
+	PINMUX_IPSR_MODSEL_DATA(IP11_27_26, ET0_RX_CLK_A, SEL_ET0_0),
+
+	PINMUX_IPSR_DATA(IP11_28, PRESETOUT),
+	PINMUX_IPSR_DATA(IP11_28, ST_CLKOUT),
+};
+
+static struct pinmux_gpio pinmux_gpios[] = {
+	PINMUX_GPIO_GP_ALL(),
+
+	GPIO_FN(CLKOUT), GPIO_FN(BS), GPIO_FN(CS0), GPIO_FN(EX_CS0),
+	GPIO_FN(RD), GPIO_FN(WE0), GPIO_FN(WE1),
+	GPIO_FN(SCL0), GPIO_FN(PENC0), GPIO_FN(USB_OVC0),
+	GPIO_FN(IRQ2_B), GPIO_FN(IRQ3_B),
+
+	/* IPSR0 */
+	GPIO_FN(A0), GPIO_FN(ST0_CLKIN), GPIO_FN(LCD_DATA0_A),
+	GPIO_FN(TCLKA_C),
+	GPIO_FN(A1), GPIO_FN(ST0_REQ), GPIO_FN(LCD_DATA1_A),
+	GPIO_FN(TCLKB_C),
+	GPIO_FN(A2), GPIO_FN(ST0_SYC), GPIO_FN(LCD_DATA2_A),
+	GPIO_FN(TCLKC_C),
+	GPIO_FN(A3), GPIO_FN(ST0_VLD), GPIO_FN(LCD_DATA3_A),
+	GPIO_FN(TCLKD_C),
+	GPIO_FN(A4), GPIO_FN(ST0_D0), GPIO_FN(LCD_DATA4_A),
+	GPIO_FN(TIOC0A_C),
+	GPIO_FN(A5), GPIO_FN(ST0_D1), GPIO_FN(LCD_DATA5_A),
+	GPIO_FN(TIOC0B_C),
+	GPIO_FN(A6), GPIO_FN(ST0_D2), GPIO_FN(LCD_DATA6_A),
+	GPIO_FN(TIOC0C_C),
+	GPIO_FN(A7), GPIO_FN(ST0_D3), GPIO_FN(LCD_DATA7_A),
+	GPIO_FN(TIOC0D_C),
+	GPIO_FN(A8), GPIO_FN(ST0_D4), GPIO_FN(LCD_DATA8_A),
+	GPIO_FN(TIOC1A_C),
+	GPIO_FN(A9), GPIO_FN(ST0_D5), GPIO_FN(LCD_DATA9_A),
+	GPIO_FN(TIOC1B_C),
+	GPIO_FN(A10), GPIO_FN(ST0_D6), GPIO_FN(LCD_DATA10_A),
+	GPIO_FN(TIOC2A_C),
+	GPIO_FN(A11), GPIO_FN(ST0_D7), GPIO_FN(LCD_DATA11_A),
+	GPIO_FN(TIOC2B_C),
+	GPIO_FN(A12), GPIO_FN(LCD_DATA12_A), GPIO_FN(TIOC3A_C),
+	GPIO_FN(A13), GPIO_FN(LCD_DATA13_A), GPIO_FN(TIOC3B_C),
+	GPIO_FN(A14), GPIO_FN(LCD_DATA14_A), GPIO_FN(TIOC3C_C),
+	GPIO_FN(A15), GPIO_FN(ST0_VCO_CLKIN), GPIO_FN(LCD_DATA15_A),
+	GPIO_FN(TIOC3D_C),
+
+	/* IPSR1 */
+	GPIO_FN(A16), GPIO_FN(ST0_PWM), GPIO_FN(LCD_DON_A),
+	GPIO_FN(TIOC4A_C),
+	GPIO_FN(A17), GPIO_FN(ST1_VCO_CLKIN), GPIO_FN(LCD_CL1_A),
+	GPIO_FN(TIOC4B_C),
+	GPIO_FN(A18), GPIO_FN(ST1_PWM), GPIO_FN(LCD_CL2_A),
+	GPIO_FN(TIOC4C_C),
+	GPIO_FN(A19), GPIO_FN(ST1_CLKIN), GPIO_FN(LCD_CLK_A),
+	GPIO_FN(TIOC4D_C),
+	GPIO_FN(A20), GPIO_FN(ST1_REQ), GPIO_FN(LCD_FLM_A),
+	GPIO_FN(A21), GPIO_FN(ST1_SYC), GPIO_FN(LCD_VCPWC_A),
+	GPIO_FN(A22), GPIO_FN(ST1_VLD), GPIO_FN(LCD_VEPWC_A),
+	GPIO_FN(A23), GPIO_FN(ST1_D0), GPIO_FN(LCD_M_DISP_A),
+	GPIO_FN(A24), GPIO_FN(RX2_D), GPIO_FN(ST1_D1),
+	GPIO_FN(A25), GPIO_FN(TX2_D), GPIO_FN(ST1_D2),
+	GPIO_FN(D0), GPIO_FN(SD0_DAT0_A), GPIO_FN(MMC_D0_A),
+	GPIO_FN(ST1_D3), GPIO_FN(FD0_A),
+	GPIO_FN(D1), GPIO_FN(SD0_DAT1_A), GPIO_FN(MMC_D1_A),
+	GPIO_FN(ST1_D4), GPIO_FN(FD1_A),
+	GPIO_FN(D2), GPIO_FN(SD0_DAT2_A), GPIO_FN(MMC_D2_A),
+	GPIO_FN(ST1_D5), GPIO_FN(FD2_A),
+	GPIO_FN(D3), GPIO_FN(SD0_DAT3_A), GPIO_FN(MMC_D3_A),
+	GPIO_FN(ST1_D6), GPIO_FN(FD3_A),
+
+	/* IPSR2 */
+	GPIO_FN(D4), GPIO_FN(SD0_CD_A), GPIO_FN(MMC_D4_A), GPIO_FN(ST1_D7),
+	GPIO_FN(FD4_A),
+	GPIO_FN(D5), GPIO_FN(SD0_WP_A), GPIO_FN(MMC_D5_A), GPIO_FN(FD5_A),
+	GPIO_FN(D6), GPIO_FN(RSPI_RSPCK_A), GPIO_FN(MMC_D6_A),
+		GPIO_FN(QSPCLK_A),
+	GPIO_FN(FD6_A),
+	GPIO_FN(D7), GPIO_FN(RSPI_SSL_A), GPIO_FN(MMC_D7_A), GPIO_FN(QSSL_A),
+	GPIO_FN(FD7_A),
+	GPIO_FN(D8), GPIO_FN(SD0_CLK_A), GPIO_FN(MMC_CLK_A), GPIO_FN(QIO2_A),
+	GPIO_FN(FCE_A), GPIO_FN(ET0_GTX_CLK_B),
+	GPIO_FN(D9), GPIO_FN(SD0_CMD_A), GPIO_FN(MMC_CMD_A), GPIO_FN(QIO3_A),
+	GPIO_FN(FCLE_A), GPIO_FN(ET0_ETXD1_B),
+	GPIO_FN(D10), GPIO_FN(RSPI_MOSI_A), GPIO_FN(QMO_QIO0_A),
+		GPIO_FN(FALE_A), GPIO_FN(ET0_ETXD2_B),
+	GPIO_FN(D11), GPIO_FN(RSPI_MISO_A), GPIO_FN(QMI_QIO1_A), GPIO_FN(FRE_A),
+		GPIO_FN(ET0_ETXD3_B),
+	GPIO_FN(D12), GPIO_FN(FWE_A), GPIO_FN(ET0_ETXD5_B),
+	GPIO_FN(D13), GPIO_FN(RX2_B), GPIO_FN(FRB_A), GPIO_FN(ET0_ETXD6_B),
+	GPIO_FN(D14), GPIO_FN(TX2_B), GPIO_FN(FSE_A), GPIO_FN(ET0_TX_CLK_B),
+
+	/* IPSR3 */
+	GPIO_FN(D15), GPIO_FN(SCK2_B),
+	GPIO_FN(CS1_A26), GPIO_FN(QIO3_B),
+	GPIO_FN(EX_CS1), GPIO_FN(RX3_B), GPIO_FN(ATACS0), GPIO_FN(QIO2_B),
+	GPIO_FN(ET0_ETXD0),
+	GPIO_FN(EX_CS2), GPIO_FN(TX3_B), GPIO_FN(ATACS1), GPIO_FN(QSPCLK_B),
+	GPIO_FN(ET0_GTX_CLK_A),
+	GPIO_FN(EX_CS3), GPIO_FN(SD1_CD_A), GPIO_FN(ATARD), GPIO_FN(QMO_QIO0_B),
+	GPIO_FN(ET0_ETXD1_A),
+	GPIO_FN(EX_CS4), GPIO_FN(SD1_WP_A), GPIO_FN(ATAWR), GPIO_FN(QMI_QIO1_B),
+	GPIO_FN(ET0_ETXD2_A),
+	GPIO_FN(EX_CS5), GPIO_FN(SD1_CMD_A), GPIO_FN(ATADIR), GPIO_FN(QSSL_B),
+	GPIO_FN(ET0_ETXD3_A),
+	GPIO_FN(RD_WR), GPIO_FN(TCLK1_B),
+	GPIO_FN(EX_WAIT0), GPIO_FN(TCLK1_B),
+	GPIO_FN(EX_WAIT1), GPIO_FN(SD1_DAT0_A), GPIO_FN(DREQ2),
+		GPIO_FN(CAN1_TX_C), GPIO_FN(ET0_LINK_C), GPIO_FN(ET0_ETXD5_A),
+	GPIO_FN(EX_WAIT2), GPIO_FN(SD1_DAT1_A), GPIO_FN(DACK2),
+		GPIO_FN(CAN1_RX_C), GPIO_FN(ET0_MAGIC_C), GPIO_FN(ET0_ETXD6_A),
+	GPIO_FN(DRACK0), GPIO_FN(SD1_DAT2_A), GPIO_FN(ATAG), GPIO_FN(TCLK1_A),
+	GPIO_FN(ET0_ETXD7),
+
+	/* IPSR4 */
+	GPIO_FN(HCTS0_A), GPIO_FN(CTS1_A), GPIO_FN(VI0_FIELD),
+		GPIO_FN(RMII0_RXD1_A), GPIO_FN(ET0_ERXD7),
+	GPIO_FN(HRTS0_A), GPIO_FN(RTS1_A), GPIO_FN(VI0_HSYNC),
+		GPIO_FN(RMII0_TXD_EN_A), GPIO_FN(ET0_RX_DV),
+	GPIO_FN(HSCK0_A), GPIO_FN(SCK1_A), GPIO_FN(VI0_VSYNC),
+		GPIO_FN(RMII0_RX_ER_A), GPIO_FN(ET0_RX_ER),
+	GPIO_FN(HRX0_A), GPIO_FN(RX1_A), GPIO_FN(VI0_DATA0_VI0_B0),
+		GPIO_FN(RMII0_CRS_DV_A), GPIO_FN(ET0_CRS),
+	GPIO_FN(HTX0_A), GPIO_FN(TX1_A), GPIO_FN(VI0_DATA1_VI0_B1),
+		GPIO_FN(RMII0_MDC_A), GPIO_FN(ET0_COL),
+	GPIO_FN(CTS0_B), GPIO_FN(VI0_DATA2_VI0_B2), GPIO_FN(RMII0_MDIO_A),
+		GPIO_FN(ET0_MDC),
+	GPIO_FN(RTS0_B), GPIO_FN(VI0_DATA3_VI0_B3), GPIO_FN(ET0_MDIO_A),
+	GPIO_FN(SCK1_B), GPIO_FN(VI0_DATA4_VI0_B4), GPIO_FN(ET0_LINK_A),
+	GPIO_FN(RX1_B), GPIO_FN(VI0_DATA5_VI0_B5), GPIO_FN(ET0_MAGIC_A),
+	GPIO_FN(TX1_B), GPIO_FN(VI0_DATA6_VI0_G0), GPIO_FN(ET0_PHY_INT_A),
+	GPIO_FN(CTS1_B), GPIO_FN(VI0_DATA7_VI0_G1),
+	GPIO_FN(RTS1_B), GPIO_FN(VI0_G2),
+	GPIO_FN(SCK2_A), GPIO_FN(VI0_G3),
+
+	/* IPSR5 */
+	GPIO_FN(REF50CK), GPIO_FN(CTS1_E), GPIO_FN(HCTS0_D),
+	GPIO_FN(REF125CK), GPIO_FN(ADTRG), GPIO_FN(RX5_C),
+	GPIO_FN(SD2_WP_A), GPIO_FN(TX5_A), GPIO_FN(VI0_R5),
+	GPIO_FN(SD2_CD_A), GPIO_FN(RX5_A), GPIO_FN(VI0_R4),
+		GPIO_FN(ET0_PHY_INT_B),
+	GPIO_FN(SD2_DAT3_A), GPIO_FN(TX4_A), GPIO_FN(VI0_R3),
+		GPIO_FN(ET0_MAGIC_B),
+	GPIO_FN(SD2_DAT2_A), GPIO_FN(RX4_A), GPIO_FN(VI0_R2),
+		GPIO_FN(ET0_LINK_B),
+	GPIO_FN(SD2_DAT1_A), GPIO_FN(TX3_A), GPIO_FN(VI0_R1),
+		GPIO_FN(ET0_MDIO_B),
+	GPIO_FN(SD2_DAT0_A), GPIO_FN(RX3_A), GPIO_FN(VI0_R0),
+		GPIO_FN(ET0_ERXD3_B),
+	GPIO_FN(SD2_CMD_A), GPIO_FN(TX2_A), GPIO_FN(VI0_G5),
+		GPIO_FN(ET0_ERXD2_B),
+	GPIO_FN(SD2_CLK_A), GPIO_FN(RX2_A), GPIO_FN(VI0_G4),
+		GPIO_FN(ET0_RX_CLK_B),
+
+	/* IPSR6 */
+	GPIO_FN(DU0_DG1), GPIO_FN(CTS1_C), GPIO_FN(HRTS0_D),
+		GPIO_FN(TIOC1B_A), GPIO_FN(HIFD09),
+	GPIO_FN(DU0_DG0), GPIO_FN(TX1_C), GPIO_FN(HSCK0_D),
+		GPIO_FN(IECLK_A), GPIO_FN(TIOC1A_A), GPIO_FN(HIFD08),
+	GPIO_FN(DU0_DR7), GPIO_FN(RX1_C), GPIO_FN(TIOC0D_A),
+		GPIO_FN(HIFD07),
+	GPIO_FN(DU0_DR6), GPIO_FN(SCK1_C), GPIO_FN(TIOC0C_A),
+		GPIO_FN(HIFD06),
+	GPIO_FN(DU0_DR5), GPIO_FN(RTS0_C), GPIO_FN(TIOC0B_A),
+		GPIO_FN(HIFD05),
+	GPIO_FN(DU0_DR4), GPIO_FN(CTS0_C), GPIO_FN(TIOC0A_A),
+		GPIO_FN(HIFD04),
+	GPIO_FN(DU0_DR3), GPIO_FN(TX0_B), GPIO_FN(TCLKD_A), GPIO_FN(HIFD03),
+	GPIO_FN(DU0_DR2), GPIO_FN(RX0_B), GPIO_FN(TCLKC_A), GPIO_FN(HIFD02),
+	GPIO_FN(DU0_DR1), GPIO_FN(SCK0_B), GPIO_FN(HTX0_D),
+		GPIO_FN(IERX_A), GPIO_FN(TCLKB_A), GPIO_FN(HIFD01),
+	GPIO_FN(DU0_DR0), GPIO_FN(SCIF_CLK_B), GPIO_FN(HRX0_D),
+		GPIO_FN(IETX_A), GPIO_FN(TCLKA_A), GPIO_FN(HIFD00),
+
+	/* IPSR7 */
+	GPIO_FN(DU0_DB4), GPIO_FN(HIFINT),
+	GPIO_FN(DU0_DB3), GPIO_FN(TX5_B), GPIO_FN(TIOC4D_A), GPIO_FN(HIFRD),
+	GPIO_FN(DU0_DB2), GPIO_FN(RX5_B), GPIO_FN(RMII0_TXD1_B),
+		GPIO_FN(TIOC4C_A), GPIO_FN(HIFWR),
+	GPIO_FN(DU0_DB1), GPIO_FN(TX4_C), GPIO_FN(RMII0_TXD0_B),
+		GPIO_FN(TIOC4B_A), GPIO_FN(HIFRS),
+	GPIO_FN(DU0_DB0), GPIO_FN(RX4_C), GPIO_FN(RMII0_TXD_EN_B),
+		GPIO_FN(TIOC4A_A), GPIO_FN(HIFCS),
+	GPIO_FN(DU0_DG7), GPIO_FN(TX3_C), GPIO_FN(RMII0_RXD1_B),
+		GPIO_FN(TIOC3D_A), GPIO_FN(HIFD15),
+	GPIO_FN(DU0_DG6), GPIO_FN(RX3_C), GPIO_FN(RMII0_RXD0_B),
+		GPIO_FN(TIOC3C_A), GPIO_FN(HIFD14),
+	GPIO_FN(DU0_DG5), GPIO_FN(TX2_C), GPIO_FN(RMII0_RX_ER_B),
+		GPIO_FN(TIOC3B_A), GPIO_FN(HIFD13),
+	GPIO_FN(DU0_DG4), GPIO_FN(RX2_C), GPIO_FN(RMII0_CRS_DV_B),
+		GPIO_FN(TIOC3A_A), GPIO_FN(HIFD12),
+	GPIO_FN(DU0_DG3), GPIO_FN(SCK2_C), GPIO_FN(RMII0_MDIO_B),
+		GPIO_FN(TIOC2B_A), GPIO_FN(HIFD11),
+	GPIO_FN(DU0_DG2), GPIO_FN(RTS1_C), GPIO_FN(RMII0_MDC_B),
+		GPIO_FN(TIOC2A_A), GPIO_FN(HIFD10),
+
+	/* IPSR8 */
+	GPIO_FN(IRQ3_A), GPIO_FN(RTS0_A), GPIO_FN(HRTS0_B),
+		GPIO_FN(ET0_ERXD3_A),
+	GPIO_FN(IRQ2_A), GPIO_FN(CTS0_A), GPIO_FN(HCTS0_B),
+		GPIO_FN(ET0_ERXD2_A),
+	GPIO_FN(IRQ1_A), GPIO_FN(HSPI_RX_B), GPIO_FN(TX3_E),
+		GPIO_FN(ET0_ERXD1),
+	GPIO_FN(IRQ0_A), GPIO_FN(HSPI_TX_B), GPIO_FN(RX3_E),
+		GPIO_FN(ET0_ERXD0),
+	GPIO_FN(DU0_CDE), GPIO_FN(HTX0_B), GPIO_FN(AUDIO_CLKB_B),
+		GPIO_FN(LCD_VCPWC_B),
+	GPIO_FN(DU0_DISP), GPIO_FN(CAN0_TX_B), GPIO_FN(HRX0_B),
+		GPIO_FN(AUDIO_CLKA_B),
+	GPIO_FN(DU0_EXODDF_DU0_ODDF), GPIO_FN(CAN0_RX_B), GPIO_FN(HSCK0_B),
+		GPIO_FN(SSI_SDATA1_B),
+	GPIO_FN(DU0_EXVSYNC_DU0_VSYNC), GPIO_FN(HSPI_RX0_C),
+		GPIO_FN(SSI_WS1_B),
+	GPIO_FN(DU0_EXHSYNC_DU0_HSYNC), GPIO_FN(HSPI_TX0_C),
+		GPIO_FN(SSI_SCK1_B),
+	GPIO_FN(DU0_DOTCLKOUT), GPIO_FN(HSPI_CLK0_C),
+		GPIO_FN(SSI_SDATA0_B),
+	GPIO_FN(DU0_DOTCLKIN), GPIO_FN(HSPI_CS0_C),
+		GPIO_FN(SSI_WS0_B),
+	GPIO_FN(DU0_DB7), GPIO_FN(SSI_SCK0_B), GPIO_FN(HIFEBL_B),
+	GPIO_FN(DU0_DB6), GPIO_FN(HIFRDY),
+	GPIO_FN(DU0_DB5), GPIO_FN(HIFDREQ),
+
+	/* IPSR9 */
+	GPIO_FN(SSI_SDATA1_A), GPIO_FN(VI1_3_B), GPIO_FN(LCD_DATA14_B),
+	GPIO_FN(SSI_WS1_A), GPIO_FN(VI1_2_B), GPIO_FN(LCD_DATA13_B),
+	GPIO_FN(SSI_SCK1_A), GPIO_FN(VI1_1_B), GPIO_FN(TIOC2B_B),
+		GPIO_FN(LCD_DATA12_B),
+	GPIO_FN(SSI_SDATA0_A), GPIO_FN(VI1_0_B), GPIO_FN(TIOC2A_B),
+		GPIO_FN(LCD_DATA11_B),
+	GPIO_FN(SSI_WS0_A), GPIO_FN(TIOC1B_B), GPIO_FN(LCD_DATA10_B),
+	GPIO_FN(SSI_SCK0_A), GPIO_FN(TIOC1A_B), GPIO_FN(LCD_DATA9_B),
+	GPIO_FN(VI1_7_A), GPIO_FN(FCE_B), GPIO_FN(LCD_DATA8_B),
+	GPIO_FN(VI1_6_A), GPIO_FN(FD7_B), GPIO_FN(LCD_DATA7_B),
+	GPIO_FN(VI1_5_A), GPIO_FN(FD6_B), GPIO_FN(LCD_DATA6_B),
+	GPIO_FN(VI1_4_A), GPIO_FN(FD5_B), GPIO_FN(LCD_DATA5_B),
+	GPIO_FN(VI1_3_A), GPIO_FN(FD4_B), GPIO_FN(LCD_DATA4_B),
+	GPIO_FN(VI1_2_A), GPIO_FN(FD3_B), GPIO_FN(LCD_DATA3_B),
+	GPIO_FN(VI1_1_A), GPIO_FN(FD2_B), GPIO_FN(LCD_DATA2_B),
+	GPIO_FN(VI1_0_A), GPIO_FN(FD1_B), GPIO_FN(LCD_DATA1_B),
+	GPIO_FN(VI1_CLK_A), GPIO_FN(FD0_B), GPIO_FN(LCD_DATA0_B),
+
+	/* IPSR10 */
+	GPIO_FN(CAN1_TX_A), GPIO_FN(TX5_C), GPIO_FN(MLB_DAT),
+	GPIO_FN(CAN0_RX_A), GPIO_FN(IRQ0_B), GPIO_FN(MLB_SIG),
+	GPIO_FN(CAN1_RX_A), GPIO_FN(IRQ1_B),
+	GPIO_FN(CAN0_TX_A), GPIO_FN(TX4_D), GPIO_FN(MLB_CLK),
+	GPIO_FN(CAN_CLK_A), GPIO_FN(RX4_D),
+	GPIO_FN(AUDIO_CLKOUT), GPIO_FN(TX1_E), GPIO_FN(HRTS0_C),
+		GPIO_FN(FSE_B), GPIO_FN(LCD_M_DISP_B),
+	GPIO_FN(AUDIO_CLKC), GPIO_FN(SCK1_E), GPIO_FN(HCTS0_C),
+		GPIO_FN(FRB_B), GPIO_FN(LCD_VEPWC_B),
+	GPIO_FN(AUDIO_CLKB_A), GPIO_FN(LCD_CLK_B),
+	GPIO_FN(AUDIO_CLKA_A), GPIO_FN(VI1_CLK_B), GPIO_FN(SCK1_D),
+		GPIO_FN(IECLK_B), GPIO_FN(LCD_FLM_B),
+	GPIO_FN(SSI_SDATA3), GPIO_FN(VI1_7_B), GPIO_FN(HTX0_C),
+		GPIO_FN(FWE_B), GPIO_FN(LCD_CL2_B),
+	GPIO_FN(SSI_SDATA2), GPIO_FN(VI1_6_B), GPIO_FN(HRX0_C),
+		GPIO_FN(FRE_B), GPIO_FN(LCD_CL1_B),
+	GPIO_FN(SSI_WS23), GPIO_FN(VI1_5_B), GPIO_FN(TX1_D),
+		GPIO_FN(HSCK0_C), GPIO_FN(FALE_B), GPIO_FN(LCD_DON_B),
+	GPIO_FN(SSI_SCK23), GPIO_FN(VI1_4_B), GPIO_FN(RX1_D),
+		GPIO_FN(FCLE_B), GPIO_FN(LCD_DATA15_B),
+
+	/* IPSR11 */
+	GPIO_FN(PRESETOUT), GPIO_FN(ST_CLKOUT),
+	GPIO_FN(DACK1), GPIO_FN(HSPI_CS_B), GPIO_FN(TX4_B),
+		GPIO_FN(ET0_RX_CLK_A),
+	GPIO_FN(DREQ1), GPIO_FN(HSPI_CLK_B), GPIO_FN(RX4_B),
+		GPIO_FN(ET0_PHY_INT_C), GPIO_FN(ET0_TX_CLK_A),
+	GPIO_FN(DACK0), GPIO_FN(SD1_DAT3_A), GPIO_FN(ET0_TX_ER),
+	GPIO_FN(DREQ0), GPIO_FN(SD1_CLK_A), GPIO_FN(ET0_TX_EN),
+	GPIO_FN(USB_OVC1), GPIO_FN(RX3_D), GPIO_FN(CAN1_RX_B),
+		GPIO_FN(RX5_D), GPIO_FN(IERX_B),
+	GPIO_FN(PENC1), GPIO_FN(TX3_D), GPIO_FN(CAN1_TX_B),
+		GPIO_FN(TX5_D), GPIO_FN(IETX_B),
+	GPIO_FN(TX0_A), GPIO_FN(HSPI_TX_A),
+	GPIO_FN(RX0_A), GPIO_FN(HSPI_RX_A), GPIO_FN(RMII0_RXD0_A),
+		GPIO_FN(ET0_ERXD6),
+	GPIO_FN(SCK0_A), GPIO_FN(HSPI_CS_A), GPIO_FN(VI0_CLKENB),
+		GPIO_FN(RMII0_TXD1_A), GPIO_FN(ET0_ERXD5),
+	GPIO_FN(SCIF_CLK_A), GPIO_FN(HSPI_CLK_A), GPIO_FN(VI0_CLK),
+		GPIO_FN(RMII0_TXD0_A), GPIO_FN(ET0_ERXD4),
+	GPIO_FN(SDSELF), GPIO_FN(RTS1_E),
+	GPIO_FN(SDA0), GPIO_FN(HIFEBL_A),
+	GPIO_FN(SDA1), GPIO_FN(RX1_E),
+	GPIO_FN(SCL1), GPIO_FN(SCIF_CLK_C),
+};
+
+static struct pinmux_cfg_reg pinmux_config_regs[] = {
+	{ PINMUX_CFG_REG("GPSR0", 0xFFFC0004, 32, 1) {
+		GP_0_31_FN, FN_IP2_2_0,
+		GP_0_30_FN, FN_IP1_31_29,
+		GP_0_29_FN, FN_IP1_28_26,
+		GP_0_28_FN, FN_IP1_25_23,
+		GP_0_27_FN, FN_IP1_22_20,
+		GP_0_26_FN, FN_IP1_19_18,
+		GP_0_25_FN, FN_IP1_17_16,
+		GP_0_24_FN, FN_IP0_5_4,
+		GP_0_23_FN, FN_IP0_3_2,
+		GP_0_22_FN, FN_IP0_1_0,
+		GP_0_21_FN, FN_IP11_28,
+		GP_0_20_FN, FN_IP1_7_6,
+		GP_0_19_FN, FN_IP1_5_4,
+		GP_0_18_FN, FN_IP1_3_2,
+		GP_0_17_FN, FN_IP1_1_0,
+		GP_0_16_FN, FN_IP0_31_30,
+		GP_0_15_FN, FN_IP0_29_28,
+		GP_0_14_FN, FN_IP0_27_26,
+		GP_0_13_FN, FN_IP0_25_24,
+		GP_0_12_FN, FN_IP0_23_22,
+		GP_0_11_FN, FN_IP0_21_20,
+		GP_0_10_FN, FN_IP0_19_18,
+		GP_0_9_FN, FN_IP0_17_16,
+		GP_0_8_FN, FN_IP0_15_14,
+		GP_0_7_FN, FN_IP0_13_12,
+		GP_0_6_FN, FN_IP0_11_10,
+		GP_0_5_FN, FN_IP0_9_8,
+		GP_0_4_FN, FN_IP0_7_6,
+		GP_0_3_FN, FN_IP1_15_14,
+		GP_0_2_FN, FN_IP1_13_12,
+		GP_0_1_FN, FN_IP1_11_10,
+		GP_0_0_FN, FN_IP1_9_8 }
+	},
+	{ PINMUX_CFG_REG("GPSR1", 0xFFFC0008, 32, 1) {
+		GP_1_31_FN, FN_IP11_25_23,
+		GP_1_30_FN, FN_IP2_13_11,
+		GP_1_29_FN, FN_IP2_10_8,
+		GP_1_28_FN, FN_IP2_7_5,
+		GP_1_27_FN, FN_IP3_26_24,
+		GP_1_26_FN, FN_IP3_23_21,
+		GP_1_25_FN, FN_IP2_4_3,
+		GP_1_24_FN, FN_WE1,
+		GP_1_23_FN, FN_WE0,
+		GP_1_22_FN, FN_IP3_19_18,
+		GP_1_21_FN, FN_RD,
+		GP_1_20_FN, FN_IP3_17_15,
+		GP_1_19_FN, FN_IP3_14_12,
+		GP_1_18_FN, FN_IP3_11_9,
+		GP_1_17_FN, FN_IP3_8_6,
+		GP_1_16_FN, FN_IP3_5_3,
+		GP_1_15_FN, FN_EX_CS0,
+		GP_1_14_FN, FN_IP3_2,
+		GP_1_13_FN, FN_CS0,
+		GP_1_12_FN, FN_BS,
+		GP_1_11_FN, FN_CLKOUT,
+		GP_1_10_FN, FN_IP3_1_0,
+		GP_1_9_FN, FN_IP2_30_28,
+		GP_1_8_FN, FN_IP2_27_25,
+		GP_1_7_FN, FN_IP2_24_23,
+		GP_1_6_FN, FN_IP2_22_20,
+		GP_1_5_FN, FN_IP2_19_17,
+		GP_1_4_FN, FN_IP2_16_14,
+		GP_1_3_FN, FN_IP11_22_21,
+		GP_1_2_FN, FN_IP11_20_19,
+		GP_1_1_FN, FN_IP3_29_27,
+		GP_1_0_FN, FN_IP3_20 }
+	},
+	{ PINMUX_CFG_REG("GPSR2", 0xFFFC000C, 32, 1) {
+		GP_2_31_FN, FN_IP4_31_30,
+		GP_2_30_FN, FN_IP5_2_0,
+		GP_2_29_FN, FN_IP5_5_3,
+		GP_2_28_FN, FN_IP5_8_6,
+		GP_2_27_FN, FN_IP5_11_9,
+		GP_2_26_FN, FN_IP5_14_12,
+		GP_2_25_FN, FN_IP5_17_15,
+		GP_2_24_FN, FN_IP5_20_18,
+		GP_2_23_FN, FN_IP5_22_21,
+		GP_2_22_FN, FN_IP5_24_23,
+		GP_2_21_FN, FN_IP5_26_25,
+		GP_2_20_FN, FN_IP4_29_28,
+		GP_2_19_FN, FN_IP4_27_26,
+		GP_2_18_FN, FN_IP4_25_24,
+		GP_2_17_FN, FN_IP4_23_22,
+		GP_2_16_FN, FN_IP4_21_20,
+		GP_2_15_FN, FN_IP4_19_18,
+		GP_2_14_FN, FN_IP4_17_15,
+		GP_2_13_FN, FN_IP4_14_12,
+		GP_2_12_FN, FN_IP4_11_9,
+		GP_2_11_FN, FN_IP4_8_6,
+		GP_2_10_FN, FN_IP4_5_3,
+		GP_2_9_FN, FN_IP8_27_26,
+		GP_2_8_FN, FN_IP11_12,
+		GP_2_7_FN, FN_IP8_25_23,
+		GP_2_6_FN, FN_IP8_22_20,
+		GP_2_5_FN, FN_IP11_27_26,
+		GP_2_4_FN, FN_IP8_29_28,
+		GP_2_3_FN, FN_IP4_2_0,
+		GP_2_2_FN, FN_IP11_11_10,
+		GP_2_1_FN, FN_IP11_9_7,
+		GP_2_0_FN, FN_IP11_6_4 }
+	},
+	{ PINMUX_CFG_REG("GPSR3", 0xFFFC0010, 32, 1) {
+		GP_3_31_FN, FN_IP9_1_0,
+		GP_3_30_FN, FN_IP8_19_18,
+		GP_3_29_FN, FN_IP8_17_16,
+		GP_3_28_FN, FN_IP8_15_14,
+		GP_3_27_FN, FN_IP8_13_12,
+		GP_3_26_FN, FN_IP8_11_10,
+		GP_3_25_FN, FN_IP8_9_8,
+		GP_3_24_FN, FN_IP8_7_6,
+		GP_3_23_FN, FN_IP8_5_4,
+		GP_3_22_FN, FN_IP8_3_2,
+		GP_3_21_FN, FN_IP8_1_0,
+		GP_3_20_FN, FN_IP7_30_29,
+		GP_3_19_FN, FN_IP7_28_27,
+		GP_3_18_FN, FN_IP7_26_24,
+		GP_3_17_FN, FN_IP7_23_21,
+		GP_3_16_FN, FN_IP7_20_18,
+		GP_3_15_FN, FN_IP7_17_15,
+		GP_3_14_FN, FN_IP7_14_12,
+		GP_3_13_FN, FN_IP7_11_9,
+		GP_3_12_FN, FN_IP7_8_6,
+		GP_3_11_FN, FN_IP7_5_3,
+		GP_3_10_FN, FN_IP7_2_0,
+		GP_3_9_FN, FN_IP6_23_21,
+		GP_3_8_FN, FN_IP6_20_18,
+		GP_3_7_FN, FN_IP6_17_16,
+		GP_3_6_FN, FN_IP6_15_14,
+		GP_3_5_FN, FN_IP6_13_12,
+		GP_3_4_FN, FN_IP6_11_10,
+		GP_3_3_FN, FN_IP6_9_8,
+		GP_3_2_FN, FN_IP6_7_6,
+		GP_3_1_FN, FN_IP6_5_3,
+		GP_3_0_FN, FN_IP6_2_0 }
+	},
+
+	{ PINMUX_CFG_REG("GPSR4", 0xFFFC0014, 32, 1) {
+		GP_4_31_FN, FN_IP10_24_23,
+		GP_4_30_FN, FN_IP10_22,
+		GP_4_29_FN, FN_IP11_18_16,
+		GP_4_28_FN, FN_USB_OVC0,
+		GP_4_27_FN, FN_IP11_15_13,
+		GP_4_26_FN, FN_PENC0,
+		GP_4_25_FN, FN_IP11_2,
+		GP_4_24_FN, FN_SCL0,
+		GP_4_23_FN, FN_IP11_1,
+		GP_4_22_FN, FN_IP11_0,
+		GP_4_21_FN, FN_IP10_21_19,
+		GP_4_20_FN, FN_IP10_18_16,
+		GP_4_19_FN, FN_IP10_15,
+		GP_4_18_FN, FN_IP10_14_12,
+		GP_4_17_FN, FN_IP10_11_9,
+		GP_4_16_FN, FN_IP10_8_6,
+		GP_4_15_FN, FN_IP10_5_3,
+		GP_4_14_FN, FN_IP10_2_0,
+		GP_4_13_FN, FN_IP9_29_28,
+		GP_4_12_FN, FN_IP9_27_26,
+		GP_4_11_FN, FN_IP9_9_8,
+		GP_4_10_FN, FN_IP9_7_6,
+		GP_4_9_FN, FN_IP9_5_4,
+		GP_4_8_FN, FN_IP9_3_2,
+		GP_4_7_FN, FN_IP9_17_16,
+		GP_4_6_FN, FN_IP9_15_14,
+		GP_4_5_FN, FN_IP9_13_12,
+		GP_4_4_FN, FN_IP9_11_10,
+		GP_4_3_FN, FN_IP9_25_24,
+		GP_4_2_FN, FN_IP9_23_22,
+		GP_4_1_FN, FN_IP9_21_20,
+		GP_4_0_FN, FN_IP9_19_18 }
+	},
+	{ PINMUX_CFG_REG("GPSR5", 0xFFFC0018, 32, 1) {
+		0, 0, 0, 0, 0, 0, 0, 0, /* 31 - 28 */
+		0, 0, 0, 0, 0, 0, 0, 0, /* 27 - 24 */
+		0, 0, 0, 0, 0, 0, 0, 0, /* 23 - 20 */
+		0, 0, 0, 0, 0, 0, 0, 0, /* 19 - 16 */
+		0, 0, 0, 0, 0, 0, 0, 0, /* 15 - 12 */
+		GP_5_11_FN, FN_IP10_29_28,
+		GP_5_10_FN, FN_IP10_27_26,
+		0, 0, 0, 0, 0, 0, 0, 0, /* 9 - 6 */
+		0, 0, 0, 0, /* 5, 4 */
+		GP_5_3_FN, FN_IRQ3_B,
+		GP_5_2_FN, FN_IRQ2_B,
+		GP_5_1_FN, FN_IP11_3,
+		GP_5_0_FN, FN_IP10_25 }
+	},
+
+	{ PINMUX_CFG_REG_VAR("IPSR0", 0xFFFC001C, 32,
+			2, 2, 2, 2, 2, 2, 2, 2,
+			2, 2, 2, 2, 2, 2, 2, 2) {
+		/* IP0_31_30 [2] */
+		FN_A15, FN_ST0_VCO_CLKIN, FN_LCD_DATA15_A,
+			FN_TIOC3D_C,
+		/* IP0_29_28 [2] */
+		FN_A14, FN_LCD_DATA14_A, FN_TIOC3C_C, 0,
+		/* IP0_27_26 [2] */
+		FN_A13, FN_LCD_DATA13_A, FN_TIOC3B_C, 0,
+		/* IP0_25_24 [2] */
+		FN_A12, FN_LCD_DATA12_A, FN_TIOC3A_C, 0,
+		/* IP0_23_22 [2] */
+		FN_A11, FN_ST0_D7, FN_LCD_DATA11_A, FN_TIOC2B_C,
+		/* IP0_21_20 [2] */
+		FN_A10, FN_ST0_D6, FN_LCD_DATA10_A, FN_TIOC2A_C,
+		/* IP0_19_18 [2] */
+		FN_A9, FN_ST0_D5, FN_LCD_DATA9_A, FN_TIOC1B_C,
+		/* IP0_17_16 [2] */
+		FN_A8, FN_ST0_D4, FN_LCD_DATA8_A, FN_TIOC1A_C,
+		/* IP0_15_14 [2] */
+		FN_A7, FN_ST0_D3, FN_LCD_DATA7_A, FN_TIOC0D_C,
+		/* IP0_13_12 [2] */
+		FN_A6, FN_ST0_D2, FN_LCD_DATA6_A, FN_TIOC0C_C,
+		/* IP0_11_10 [2] */
+		FN_A5, FN_ST0_D1, FN_LCD_DATA5_A, FN_TIOC0B_C,
+		/* IP0_9_8 [2] */
+		FN_A4, FN_ST0_D0, FN_LCD_DATA4_A, FN_TIOC0A_C,
+		/* IP0_7_6 [2] */
+		FN_A3, FN_ST0_VLD, FN_LCD_DATA3_A, FN_TCLKD_C,
+		/* IP0_5_4 [2] */
+		FN_A2, FN_ST0_SYC, FN_LCD_DATA2_A, FN_TCLKC_C,
+		/* IP0_3_2 [2] */
+		FN_A1, FN_ST0_REQ, FN_LCD_DATA1_A, FN_TCLKB_C,
+		/* IP0_1_0 [2] */
+		FN_A0, FN_ST0_CLKIN, FN_LCD_DATA0_A, FN_TCLKA_C }
+	},
+	{ PINMUX_CFG_REG_VAR("IPSR1", 0xFFFC0020, 32,
+			3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2) {
+		/* IP1_31_29 [3] */
+		FN_D3, FN_SD0_DAT3_A, FN_MMC_D3_A, FN_ST1_D6,
+			FN_FD3_A, 0, 0, 0,
+		/* IP1_28_26 [3] */
+		FN_D2, FN_SD0_DAT2_A, FN_MMC_D2_A, FN_ST1_D5,
+			FN_FD2_A, 0, 0, 0,
+		/* IP1_25_23 [3] */
+		FN_D1, FN_SD0_DAT1_A, FN_MMC_D1_A, FN_ST1_D4,
+			FN_FD1_A, 0, 0, 0,
+		/* IP1_22_20 [3] */
+		FN_D0, FN_SD0_DAT0_A, FN_MMC_D0_A, FN_ST1_D3,
+			FN_FD0_A, 0, 0, 0,
+		/* IP1_19_18 [2] */
+		FN_A25, FN_TX2_D, FN_ST1_D2, 0,
+		/* IP1_17_16 [2] */
+		FN_A24, FN_RX2_D, FN_ST1_D1, 0,
+		/* IP1_15_14 [2] */
+		FN_A23, FN_ST1_D0, FN_LCD_M_DISP_A, 0,
+		/* IP1_13_12 [2] */
+		FN_A22, FN_ST1_VLD, FN_LCD_VEPWC_A, 0,
+		/* IP1_11_10 [2] */
+		FN_A21, FN_ST1_SYC, FN_LCD_VCPWC_A, 0,
+		/* IP1_9_8 [2] */
+		FN_A20, FN_ST1_REQ, FN_LCD_FLM_A, 0,
+		/* IP1_7_6 [2] */
+		FN_A19, FN_ST1_CLKIN, FN_LCD_CLK_A,	FN_TIOC4D_C,
+		/* IP1_5_4 [2] */
+		FN_A18, FN_ST1_PWM, FN_LCD_CL2_A, FN_TIOC4C_C,
+		/* IP1_3_2 [2] */
+		FN_A17, FN_ST1_VCO_CLKIN, FN_LCD_CL1_A,	FN_TIOC4B_C,
+		/* IP1_1_0 [2] */
+		FN_A16, FN_ST0_PWM, FN_LCD_DON_A, FN_TIOC4A_C }
+	},
+	{ PINMUX_CFG_REG_VAR("IPSR2", 0xFFFC0024, 32,
+			     1, 3, 3, 2, 3, 3, 3, 3, 3, 3, 2, 3) {
+		/* IP2_31 [1] */
+		0, 0,
+		/* IP2_30_28 [3] */
+		FN_D14, FN_TX2_B, 0, FN_FSE_A,
+			FN_ET0_TX_CLK_B, 0, 0, 0,
+		/* IP2_27_25 [3] */
+		FN_D13, FN_RX2_B, 0, FN_FRB_A,
+			FN_ET0_ETXD6_B, 0, 0, 0,
+		/* IP2_24_23 [2] */
+		FN_D12, 0, FN_FWE_A, FN_ET0_ETXD5_B,
+		/* IP2_22_20 [3] */
+		FN_D11, FN_RSPI_MISO_A, 0, FN_QMI_QIO1_A,
+			FN_FRE_A, FN_ET0_ETXD3_B, 0, 0,
+		/* IP2_19_17 [3] */
+		FN_D10, FN_RSPI_MOSI_A, 0, FN_QMO_QIO0_A,
+			FN_FALE_A, FN_ET0_ETXD2_B, 0, 0,
+		/* IP2_16_14 [3] */
+		FN_D9, FN_SD0_CMD_A, FN_MMC_CMD_A, FN_QIO3_A,
+			FN_FCLE_A, FN_ET0_ETXD1_B, 0, 0,
+		/* IP2_13_11 [3] */
+		FN_D8, FN_SD0_CLK_A, FN_MMC_CLK_A, FN_QIO2_A,
+			FN_FCE_A, FN_ET0_GTX_CLK_B, 0, 0,
+		/* IP2_10_8 [3] */
+		FN_D7, FN_RSPI_SSL_A, FN_MMC_D7_A, FN_QSSL_A,
+			FN_FD7_A, 0, 0, 0,
+		/* IP2_7_5 [3] */
+		FN_D6, FN_RSPI_RSPCK_A, FN_MMC_D6_A, FN_QSPCLK_A,
+			FN_FD6_A, 0, 0, 0,
+		/* IP2_4_3 [2] */
+		FN_D5, FN_SD0_WP_A, FN_MMC_D5_A, FN_FD5_A,
+		/* IP2_2_0 [3] */
+		FN_D4, FN_SD0_CD_A, FN_MMC_D4_A, FN_ST1_D7,
+			FN_FD4_A, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG_VAR("IPSR3", 0xFFFC0028, 32,
+				2, 3, 3, 3, 1, 2, 3, 3, 3, 3, 3, 1, 2) {
+	    /* IP3_31_30 [2] */
+		0, 0, 0, 0,
+	    /* IP3_29_27 [3] */
+		FN_DRACK0, FN_SD1_DAT2_A, FN_ATAG, FN_TCLK1_A,
+		FN_ET0_ETXD7, 0, 0, 0,
+	    /* IP3_26_24 [3] */
+		FN_EX_WAIT2, FN_SD1_DAT1_A, FN_DACK2, FN_CAN1_RX_C,
+		FN_ET0_MAGIC_C, FN_ET0_ETXD6_A, 0, 0,
+	    /* IP3_23_21 [3] */
+		FN_EX_WAIT1, FN_SD1_DAT0_A, FN_DREQ2, FN_CAN1_TX_C,
+		FN_ET0_LINK_C, FN_ET0_ETXD5_A, 0, 0,
+	    /* IP3_20 [1] */
+		FN_EX_WAIT0, FN_TCLK1_B,
+	    /* IP3_19_18 [2] */
+		FN_RD_WR, FN_TCLK1_B, 0, 0,
+	    /* IP3_17_15 [3] */
+		FN_EX_CS5, FN_SD1_CMD_A, FN_ATADIR, FN_QSSL_B,
+		FN_ET0_ETXD3_A, 0, 0, 0,
+	    /* IP3_14_12 [3] */
+		FN_EX_CS4, FN_SD1_WP_A, FN_ATAWR, FN_QMI_QIO1_B,
+		FN_ET0_ETXD2_A, 0, 0, 0,
+	    /* IP3_11_9 [3] */
+		FN_EX_CS3, FN_SD1_CD_A, FN_ATARD, FN_QMO_QIO0_B,
+		FN_ET0_ETXD1_A, 0, 0, 0,
+	    /* IP3_8_6 [3] */
+		FN_EX_CS2, FN_TX3_B, FN_ATACS1, FN_QSPCLK_B,
+		FN_ET0_GTX_CLK_A, 0, 0, 0,
+	    /* IP3_5_3 [3] */
+		FN_EX_CS1, FN_RX3_B, FN_ATACS0, FN_QIO2_B,
+		FN_ET0_ETXD0, 0, 0, 0,
+	    /* IP3_2 [1] */
+		FN_CS1_A26, FN_QIO3_B,
+	    /* IP3_1_0 [2] */
+		FN_D15, FN_SCK2_B, 0, 0 }
+	},
+	{ PINMUX_CFG_REG_VAR("IPSR4", 0xFFFC002C, 32,
+				2, 2, 2, 2, 2, 2 , 2, 3, 3, 3, 3, 3, 3) {
+	    /* IP4_31_30 [2] */
+		0, FN_SCK2_A, FN_VI0_G3, 0,
+	    /* IP4_29_28 [2] */
+		0, FN_RTS1_B, FN_VI0_G2, 0,
+	    /* IP4_27_26 [2] */
+		0, FN_CTS1_B, FN_VI0_DATA7_VI0_G1, 0,
+	    /* IP4_25_24 [2] */
+		0, FN_TX1_B, FN_VI0_DATA6_VI0_G0, FN_ET0_PHY_INT_A,
+	    /* IP4_23_22 [2] */
+		0, FN_RX1_B, FN_VI0_DATA5_VI0_B5, FN_ET0_MAGIC_A,
+	    /* IP4_21_20 [2] */
+		0, FN_SCK1_B, FN_VI0_DATA4_VI0_B4, FN_ET0_LINK_A,
+	    /* IP4_19_18 [2] */
+		0, FN_RTS0_B, FN_VI0_DATA3_VI0_B3, FN_ET0_MDIO_A,
+	    /* IP4_17_15 [3] */
+		0, FN_CTS0_B, FN_VI0_DATA2_VI0_B2, FN_RMII0_MDIO_A,
+			FN_ET0_MDC, 0, 0, 0,
+	    /* IP4_14_12 [3] */
+		FN_HTX0_A, FN_TX1_A, FN_VI0_DATA1_VI0_B1, FN_RMII0_MDC_A,
+			FN_ET0_COL, 0, 0, 0,
+	    /* IP4_11_9 [3] */
+		FN_HRX0_A, FN_RX1_A, FN_VI0_DATA0_VI0_B0, FN_RMII0_CRS_DV_A,
+			FN_ET0_CRS, 0, 0, 0,
+	    /* IP4_8_6 [3] */
+		FN_HSCK0_A, FN_SCK1_A, FN_VI0_VSYNC, FN_RMII0_RX_ER_A,
+			FN_ET0_RX_ER, 0, 0, 0,
+	    /* IP4_5_3 [3] */
+		FN_HRTS0_A, FN_RTS1_A, FN_VI0_HSYNC, FN_RMII0_TXD_EN_A,
+			FN_ET0_RX_DV, 0, 0, 0,
+	    /* IP4_2_0 [3] */
+		FN_HCTS0_A, FN_CTS1_A, FN_VI0_FIELD, FN_RMII0_RXD1_A,
+			FN_ET0_ERXD7, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG_VAR("IPSR5", 0xFFFC0030, 32,
+				1, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3) {
+	    /* IP5_31 [1] */
+	    0, 0,
+	    /* IP5_30 [1] */
+	    0, 0,
+	    /* IP5_29 [1] */
+	    0, 0,
+	    /* IP5_28 [1] */
+	    0, 0,
+	    /* IP5_27 [1] */
+	    0, 0,
+	    /* IP5_26_25 [2] */
+		FN_REF50CK, FN_CTS1_E, FN_HCTS0_D, 0,
+	    /* IP5_24_23 [2] */
+		FN_REF125CK, FN_ADTRG, FN_RX5_C, 0,
+	    /* IP5_22_21 [2] */
+		FN_SD2_WP_A, FN_TX5_A, FN_VI0_R5, 0,
+	    /* IP5_20_18 [3] */
+		FN_SD2_CD_A, FN_RX5_A, FN_VI0_R4, 0,
+		0, 0, 0, FN_ET0_PHY_INT_B,
+	    /* IP5_17_15 [3] */
+		FN_SD2_DAT3_A, FN_TX4_A, FN_VI0_R3, 0,
+		0, 0, 0, FN_ET0_MAGIC_B,
+	    /* IP5_14_12 [3] */
+		FN_SD2_DAT2_A, FN_RX4_A, FN_VI0_R2, 0,
+		0, 0, 0, FN_ET0_LINK_B,
+	    /* IP5_11_9 [3] */
+		FN_SD2_DAT1_A, FN_TX3_A, FN_VI0_R1, 0,
+		0, 0, 0, FN_ET0_MDIO_B,
+	    /* IP5_8_6 [3] */
+		FN_SD2_DAT0_A, FN_RX3_A, FN_VI0_R0, 0,
+		0, 0, 0, FN_ET0_ERXD3_B,
+	    /* IP5_5_3 [3] */
+		FN_SD2_CMD_A, FN_TX2_A, FN_VI0_G5, 0,
+		0, 0, 0, FN_ET0_ERXD2_B,
+	    /* IP5_2_0 [3] */
+		FN_SD2_CLK_A, FN_RX2_A, FN_VI0_G4, 0,
+		FN_ET0_RX_CLK_B, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG_VAR("IPSR6", 0xFFFC0034, 32,
+				1, 1, 1, 1, 1, 1, 1, 1,
+				3, 3, 2, 2, 2, 2, 2, 2, 3, 3) {
+	    /* IP5_31 [1] */
+	    0, 0,
+	    /* IP6_30 [1] */
+	    0, 0,
+	    /* IP6_29 [1] */
+	    0, 0,
+	    /* IP6_28 [1] */
+	    0, 0,
+	    /* IP6_27 [1] */
+	    0, 0,
+	    /* IP6_26 [1] */
+	    0, 0,
+	    /* IP6_25 [1] */
+	    0, 0,
+	    /* IP6_24 [1] */
+	    0, 0,
+	    /* IP6_23_21 [3] */
+		FN_DU0_DG1, FN_CTS1_C, FN_HRTS0_D, FN_TIOC1B_A,
+		FN_HIFD09, 0, 0, 0,
+	    /* IP6_20_18 [3] */
+		FN_DU0_DG0, FN_TX1_C, FN_HSCK0_D, FN_IECLK_A,
+		FN_TIOC1A_A, FN_HIFD08, 0, 0,
+	    /* IP6_17_16 [2] */
+		FN_DU0_DR7, FN_RX1_C, FN_TIOC0D_A, FN_HIFD07,
+	    /* IP6_15_14 [2] */
+		FN_DU0_DR6, FN_SCK1_C, FN_TIOC0C_A, FN_HIFD06,
+	    /* IP6_13_12 [2] */
+		FN_DU0_DR5, FN_RTS0_C, FN_TIOC0B_A, FN_HIFD05,
+	    /* IP6_11_10 [2] */
+		FN_DU0_DR4, FN_CTS0_C, FN_TIOC0A_A, FN_HIFD04,
+	    /* IP6_9_8 [2] */
+		FN_DU0_DR3, FN_TX0_B, FN_TCLKD_A, FN_HIFD03,
+	    /* IP6_7_6 [2] */
+		FN_DU0_DR2, FN_RX0_B, FN_TCLKC_A, FN_HIFD02,
+	    /* IP6_5_3 [3] */
+		FN_DU0_DR1, FN_SCK0_B, FN_HTX0_D, FN_IERX_A,
+		FN_TCLKB_A, FN_HIFD01, 0, 0,
+	    /* IP6_2_0 [3] */
+		FN_DU0_DR0, FN_SCIF_CLK_B, FN_HRX0_D, FN_IETX_A,
+		FN_TCLKA_A, FN_HIFD00, 0, 0 }
+	},
+	{ PINMUX_CFG_REG_VAR("IPSR7", 0xFFFC0038, 32,
+			     1, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3) {
+	    /* IP7_31 [1] */
+	    0, 0,
+	    /* IP7_30_29 [2] */
+		FN_DU0_DB4, 0, FN_HIFINT, 0,
+	    /* IP7_28_27 [2] */
+		FN_DU0_DB3, FN_TX5_B, FN_TIOC4D_A, FN_HIFRD,
+	    /* IP7_26_24 [3] */
+		FN_DU0_DB2, FN_RX5_B, FN_RMII0_TXD1_B, FN_TIOC4C_A,
+		FN_HIFWR, 0, 0, 0,
+	    /* IP7_23_21 [3] */
+		FN_DU0_DB1, FN_TX4_C, FN_RMII0_TXD0_B, FN_TIOC4B_A,
+		FN_HIFRS, 0, 0, 0,
+	    /* IP7_20_18 [3] */
+		FN_DU0_DB0, FN_RX4_C, FN_RMII0_TXD_EN_B, FN_TIOC4A_A,
+		FN_HIFCS, 0, 0, 0,
+	    /* IP7_17_15 [3] */
+		FN_DU0_DG7, FN_TX3_C, FN_RMII0_RXD1_B, FN_TIOC3D_A,
+		FN_HIFD15, 0, 0, 0,
+	    /* IP7_14_12 [3] */
+		FN_DU0_DG6, FN_RX3_C, FN_RMII0_RXD0_B, FN_TIOC3C_A,
+		FN_HIFD14, 0, 0, 0,
+	    /* IP7_11_9 [3] */
+		FN_DU0_DG5, FN_TX2_C, FN_RMII0_RX_ER_B, FN_TIOC3B_A,
+		FN_HIFD13, 0, 0, 0,
+	    /* IP7_8_6 [3] */
+		FN_DU0_DG4, FN_RX2_C, FN_RMII0_CRS_DV_B, FN_TIOC3A_A,
+		FN_HIFD12, 0, 0, 0,
+	    /* IP7_5_3 [3] */
+		FN_DU0_DG3, FN_SCK2_C, FN_RMII0_MDIO_B, FN_TIOC2B_A,
+		FN_HIFD11, 0, 0, 0,
+	    /* IP7_2_0 [3] */
+		FN_DU0_DG2, FN_RTS1_C, FN_RMII0_MDC_B, FN_TIOC2A_A,
+		FN_HIFD10, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG_VAR("IPSR8", 0xFFFC003C, 32,
+			     2, 2, 2, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2) {
+	    /* IP9_31_30 [2] */
+	    0, 0, 0, 0,
+	    /* IP8_29_28 [2] */
+		FN_IRQ3_A, FN_RTS0_A, FN_HRTS0_B, FN_ET0_ERXD3_A,
+	    /* IP8_27_26 [2] */
+		FN_IRQ2_A, FN_CTS0_A, FN_HCTS0_B, FN_ET0_ERXD2_A,
+	    /* IP8_25_23 [3] */
+		FN_IRQ1_A, 0, FN_HSPI_RX_B, FN_TX3_E,
+			FN_ET0_ERXD1, 0, 0, 0,
+	    /* IP8_22_20 [3] */
+		FN_IRQ0_A, 0, FN_HSPI_TX_B, FN_RX3_E,
+			FN_ET0_ERXD0, 0, 0, 0,
+	    /* IP8_19_18 [2] */
+		FN_DU0_CDE, FN_HTX0_B, FN_AUDIO_CLKB_B, FN_LCD_VCPWC_B,
+	    /* IP8_17_16 [2] */
+		FN_DU0_DISP, FN_CAN0_TX_B, FN_HRX0_B, FN_AUDIO_CLKA_B,
+	    /* IP8_15_14 [2] */
+		FN_DU0_EXODDF_DU0_ODDF, FN_CAN0_RX_B, FN_HSCK0_B,
+			FN_SSI_SDATA1_B,
+	    /* IP8_13_12 [2] */
+		FN_DU0_EXVSYNC_DU0_VSYNC, 0, FN_HSPI_RX0_C, FN_SSI_WS1_B,
+	    /* IP8_11_10 [2] */
+		FN_DU0_EXHSYNC_DU0_HSYNC, 0, FN_HSPI_TX0_C, FN_SSI_SCK1_B,
+	    /* IP8_9_8 [2] */
+		FN_DU0_DOTCLKOUT, 0, FN_HSPI_CLK0_C, FN_SSI_SDATA0_B,
+	    /* IP8_7_6 [2] */
+		FN_DU0_DOTCLKIN, 0, FN_HSPI_CS0_C, FN_SSI_WS0_B,
+	    /* IP8_5_4 [2] */
+		FN_DU0_DB7, 0, FN_SSI_SCK0_B, FN_HIFEBL_B,
+	    /* IP8_3_2 [2] */
+		FN_DU0_DB6, 0, FN_HIFRDY, 0,
+	    /* IP8_1_0 [2] */
+		FN_DU0_DB5, 0, FN_HIFDREQ, 0 }
+	},
+	{ PINMUX_CFG_REG_VAR("IPSR9", 0xFFFC0040, 32,
+			     2, 2, 2, 2, 2, 2, 2, 2,
+			     2, 2, 2, 2, 2, 2, 2, 2) {
+	    /* IP9_31_30 [2] */
+	    0, 0, 0, 0,
+	    /* IP9_29_28 [2] */
+		FN_SSI_SDATA1_A, FN_VI1_3_B, FN_LCD_DATA14_B, 0,
+	    /* IP9_27_26 [2] */
+		FN_SSI_WS1_A, FN_VI1_2_B, FN_LCD_DATA13_B, 0,
+	    /* IP9_25_24 [2] */
+		FN_SSI_SCK1_A, FN_VI1_1_B, FN_TIOC2B_B, FN_LCD_DATA12_B,
+	    /* IP9_23_22 [2] */
+		FN_SSI_SDATA0_A, FN_VI1_0_B, FN_TIOC2A_B, FN_LCD_DATA11_B,
+	    /* IP9_21_20 [2] */
+		FN_SSI_WS0_A, FN_TIOC1B_B, FN_LCD_DATA10_B, 0,
+	    /* IP9_19_18 [2] */
+		FN_SSI_SCK0_A, FN_TIOC1A_B, FN_LCD_DATA9_B, 0,
+	    /* IP9_17_16 [2] */
+		FN_VI1_7_A, FN_FCE_B, FN_LCD_DATA8_B, 0,
+	    /* IP9_15_14 [2] */
+		FN_VI1_6_A, 0, FN_FD7_B, FN_LCD_DATA7_B,
+	    /* IP9_13_12 [2] */
+		FN_VI1_5_A, 0, FN_FD6_B, FN_LCD_DATA6_B,
+	    /* IP9_11_10 [2] */
+		FN_VI1_4_A, 0, FN_FD5_B, FN_LCD_DATA5_B,
+	    /* IP9_9_8 [2] */
+		FN_VI1_3_A, 0, FN_FD4_B, FN_LCD_DATA4_B,
+	    /* IP9_7_6 [2] */
+		FN_VI1_2_A, 0, FN_FD3_B, FN_LCD_DATA3_B,
+	    /* IP9_5_4 [2] */
+		FN_VI1_1_A, 0, FN_FD2_B, FN_LCD_DATA2_B,
+	    /* IP9_3_2 [2] */
+		FN_VI1_0_A, 0, FN_FD1_B, FN_LCD_DATA1_B,
+	    /* IP9_1_0 [2] */
+		FN_VI1_CLK_A, 0, FN_FD0_B, FN_LCD_DATA0_B }
+	},
+	{ PINMUX_CFG_REG_VAR("IPSR10", 0xFFFC0044, 32,
+					2, 2, 2, 1, 2, 1, 3,
+					3, 1, 3, 3, 3, 3, 3) {
+	    /* IP9_31_30 [2] */
+	    0, 0, 0, 0,
+	    /* IP10_29_28 [2] */
+		FN_CAN1_TX_A, FN_TX5_C, FN_MLB_DAT, 0,
+	    /* IP10_27_26 [2] */
+		FN_CAN0_RX_A, FN_IRQ0_B, FN_MLB_SIG, 0,
+	    /* IP10_25 [1] */
+		FN_CAN1_RX_A, FN_IRQ1_B,
+	    /* IP10_24_23 [2] */
+		FN_CAN0_TX_A, FN_TX4_D, FN_MLB_CLK, 0,
+	    /* IP10_22 [1] */
+		FN_CAN_CLK_A, FN_RX4_D,
+	    /* IP10_21_19 [3] */
+		FN_AUDIO_CLKOUT, FN_TX1_E, FN_HRTS0_C, FN_FSE_B,
+		FN_LCD_M_DISP_B, 0, 0, 0,
+	    /* IP10_18_16 [3] */
+		FN_AUDIO_CLKC, FN_SCK1_E, FN_HCTS0_C, FN_FRB_B,
+		FN_LCD_VEPWC_B, 0, 0, 0,
+	    /* IP10_15 [1] */
+		FN_AUDIO_CLKB_A, FN_LCD_CLK_B,
+	    /* IP10_14_12 [3] */
+		FN_AUDIO_CLKA_A, FN_VI1_CLK_B, FN_SCK1_D, FN_IECLK_B,
+		FN_LCD_FLM_B, 0, 0, 0,
+	    /* IP10_11_9 [3] */
+		FN_SSI_SDATA3, FN_VI1_7_B, FN_HTX0_C, FN_FWE_B,
+		FN_LCD_CL2_B, 0, 0, 0,
+	    /* IP10_8_6 [3] */
+		FN_SSI_SDATA2, FN_VI1_6_B, FN_HRX0_C, FN_FRE_B,
+		FN_LCD_CL1_B, 0, 0, 0,
+	    /* IP10_5_3 [3] */
+		FN_SSI_WS23, FN_VI1_5_B, FN_TX1_D, FN_HSCK0_C, FN_FALE_B,
+		FN_LCD_DON_B, 0, 0, 0,
+	    /* IP10_2_0 [3] */
+		FN_SSI_SCK23, FN_VI1_4_B, FN_RX1_D, FN_FCLE_B,
+		FN_LCD_DATA15_B, 0, 0, 0 }
+	},
+	{ PINMUX_CFG_REG_VAR("IPSR11", 0xFFFC0048, 32,
+			3, 1, 2, 2, 2, 3, 3, 1, 2, 3, 3, 1, 1, 1, 1) {
+	    /* IP11_31_29 [3] */
+	    0, 0, 0, 0, 0, 0, 0, 0,
+	    /* IP11_28 [1] */
+		FN_PRESETOUT, FN_ST_CLKOUT,
+	    /* IP11_27_26 [2] */
+		FN_DACK1, FN_HSPI_CS_B, FN_TX4_B, FN_ET0_RX_CLK_A,
+	    /* IP11_25_23 [3] */
+		FN_DREQ1, FN_HSPI_CLK_B, FN_RX4_B, FN_ET0_PHY_INT_C,
+		FN_ET0_TX_CLK_A, 0, 0, 0,
+	    /* IP11_22_21 [2] */
+		FN_DACK0, FN_SD1_DAT3_A, FN_ET0_TX_ER, 0,
+	    /* IP11_20_19 [2] */
+		FN_DREQ0, FN_SD1_CLK_A, FN_ET0_TX_EN, 0,
+	    /* IP11_18_16 [3] */
+		FN_USB_OVC1, FN_RX3_D, FN_CAN1_RX_B, FN_RX5_D,
+		FN_IERX_B, 0, 0, 0,
+	    /* IP11_15_13 [3] */
+		FN_PENC1, FN_TX3_D, FN_CAN1_TX_B, FN_TX5_D,
+		FN_IETX_B, 0, 0, 0,
+	    /* IP11_12 [1] */
+		FN_TX0_A, FN_HSPI_TX_A,
+	    /* IP11_11_10 [2] */
+		FN_RX0_A, FN_HSPI_RX_A, FN_RMII0_RXD0_A, FN_ET0_ERXD6,
+	    /* IP11_9_7 [3] */
+		FN_SCK0_A, FN_HSPI_CS_A, FN_VI0_CLKENB, FN_RMII0_TXD1_A,
+		FN_ET0_ERXD5, 0, 0, 0,
+	    /* IP11_6_4 [3] */
+		FN_SCIF_CLK_A, FN_HSPI_CLK_A, FN_VI0_CLK, FN_RMII0_TXD0_A,
+		FN_ET0_ERXD4, 0, 0, 0,
+	    /* IP11_3 [1] */
+		FN_SDSELF, FN_RTS1_E,
+	    /* IP11_2 [1] */
+		FN_SDA0, FN_HIFEBL_A,
+	    /* IP11_1 [1] */
+		FN_SDA1, FN_RX1_E,
+	    /* IP11_0 [1] */
+		FN_SCL1, FN_SCIF_CLK_C }
+	},
+	{ PINMUX_CFG_REG_VAR("MOD_SEL1", 0xFFFC004C, 32,
+				3, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 2,
+				1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) {
+		/* SEL1_31_29 [3] */
+		0, 0, 0, 0, 0, 0, 0, 0,
+		/* SEL1_28 [1] */
+		FN_SEL_IEBUS_0, FN_SEL_IEBUS_1,
+		/* SEL1_27 [1] */
+		FN_SEL_RQSPI_0, FN_SEL_RQSPI_1,
+		/* SEL1_26 [1] */
+		FN_SEL_VIN1_0, FN_SEL_VIN1_1,
+		/* SEL1_25 [1] */
+		FN_SEL_HIF_0, FN_SEL_HIF_1,
+		/* SEL1_24 [1] */
+		FN_SEL_RSPI_0, FN_SEL_RSPI_1,
+		/* SEL1_23 [1] */
+		FN_SEL_LCDC_0, FN_SEL_LCDC_1,
+		/* SEL1_22_21 [2] */
+		FN_SEL_ET0_CTL_0, FN_SEL_ET0_CTL_1, FN_SEL_ET0_CTL_2, 0,
+		/* SEL1_20 [1] */
+		FN_SEL_ET0_0, FN_SEL_ET0_1,
+		/* SEL1_19 [1] */
+		FN_SEL_RMII_0, FN_SEL_RMII_1,
+		/* SEL1_18 [1] */
+		FN_SEL_TMU_0, FN_SEL_TMU_1,
+		/* SEL1_17_16 [2] */
+		FN_SEL_HSPI_0, FN_SEL_HSPI_1, FN_SEL_HSPI_2, 0,
+		/* SEL1_15_14 [2] */
+		FN_SEL_HSCIF_0, FN_SEL_HSCIF_1, FN_SEL_HSCIF_2, FN_SEL_HSCIF_3,
+		/* SEL1_13 [1] */
+		FN_SEL_RCAN_CLK_0, FN_SEL_RCAN_CLK_1,
+		/* SEL1_12_11 [2] */
+		FN_SEL_RCAN1_0, FN_SEL_RCAN1_1, FN_SEL_RCAN1_2, 0,
+		/* SEL1_10 [1] */
+		FN_SEL_RCAN0_0, FN_SEL_RCAN0_1,
+		/* SEL1_9 [1] */
+		FN_SEL_SDHI2_0, FN_SEL_SDHI2_1,
+		/* SEL1_8 [1] */
+		FN_SEL_SDHI1_0, FN_SEL_SDHI1_1,
+		/* SEL1_7 [1] */
+		FN_SEL_SDHI0_0, FN_SEL_SDHI0_1,
+		/* SEL1_6 [1] */
+		FN_SEL_SSI1_0, FN_SEL_SSI1_1,
+		/* SEL1_5 [1] */
+		FN_SEL_SSI0_0, FN_SEL_SSI0_1,
+		/* SEL1_4 [1] */
+		FN_SEL_AUDIO_CLKB_0, FN_SEL_AUDIO_CLKB_1,
+		/* SEL1_3 [1] */
+		FN_SEL_AUDIO_CLKA_0, FN_SEL_AUDIO_CLKA_1,
+		/* SEL1_2 [1] */
+		FN_SEL_FLCTL_0, FN_SEL_FLCTL_1,
+		/* SEL1_1 [1] */
+		FN_SEL_MMC_0, FN_SEL_MMC_1,
+		/* SEL1_0 [1] */
+		FN_SEL_INTC_0, FN_SEL_INTC_1 }
+	},
+	{ PINMUX_CFG_REG_VAR("MOD_SEL2", 0xFFFC0050, 32,
+				1, 1, 1, 1, 1, 1, 1, 1,
+				1, 1, 1, 2, 2, 1, 2, 2, 3, 2, 3, 2, 2) {
+		/* SEL2_31 [1] */
+		0, 0,
+		/* SEL2_30 [1] */
+		0, 0,
+		/* SEL2_29 [1] */
+		0, 0,
+		/* SEL2_28 [1] */
+		0, 0,
+		/* SEL2_27 [1] */
+		0, 0,
+		/* SEL2_26 [1] */
+		0, 0,
+		/* SEL2_25 [1] */
+		0, 0,
+		/* SEL2_24 [1] */
+		0, 0,
+		/* SEL2_23 [1] */
+		FN_SEL_MTU2_CLK_0, FN_SEL_MTU2_CLK_1,
+		/* SEL2_22 [1] */
+		FN_SEL_MTU2_CH4_0, FN_SEL_MTU2_CH4_1,
+		/* SEL2_21 [1] */
+		FN_SEL_MTU2_CH3_0, FN_SEL_MTU2_CH3_1,
+		/* SEL2_20_19 [2] */
+		FN_SEL_MTU2_CH2_0, FN_SEL_MTU2_CH2_1, FN_SEL_MTU2_CH2_2, 0,
+		/* SEL2_18_17 [2] */
+		FN_SEL_MTU2_CH1_0, FN_SEL_MTU2_CH1_1, FN_SEL_MTU2_CH1_2, 0,
+		/* SEL2_16 [1] */
+		FN_SEL_MTU2_CH0_0, FN_SEL_MTU2_CH0_1,
+		/* SEL2_15_14 [2] */
+		FN_SEL_SCIF5_0, FN_SEL_SCIF5_1, FN_SEL_SCIF5_2, FN_SEL_SCIF5_3,
+		/* SEL2_13_12 [2] */
+		FN_SEL_SCIF4_0, FN_SEL_SCIF4_1, FN_SEL_SCIF4_2, FN_SEL_SCIF4_3,
+		/* SEL2_11_9 [3] */
+		FN_SEL_SCIF3_0, FN_SEL_SCIF3_1, FN_SEL_SCIF3_2, FN_SEL_SCIF3_3,
+		FN_SEL_SCIF3_4, 0, 0, 0,
+		/* SEL2_8_7 [2] */
+		FN_SEL_SCIF2_0, FN_SEL_SCIF2_1, FN_SEL_SCIF2_2, FN_SEL_SCIF2_3,
+		/* SEL2_6_4 [3] */
+		FN_SEL_SCIF1_0, FN_SEL_SCIF1_1, FN_SEL_SCIF1_2, FN_SEL_SCIF1_3,
+			FN_SEL_SCIF1_4, 0, 0, 0,
+		/* SEL2_3_2 [2] */
+		FN_SEL_SCIF0_0, FN_SEL_SCIF0_1, FN_SEL_SCIF0_2, 0,
+		/* SEL2_1_0 [2] */
+		FN_SEL_SCIF_CLK_0, FN_SEL_SCIF_CLK_1, FN_SEL_SCIF_CLK_2, 0  }
+	},
+	/* GPIO 0 - 5*/
+	{ PINMUX_CFG_REG("INOUTSEL0", 0xFFC40004, 32, 1) { GP_INOUTSEL(0) } },
+	{ PINMUX_CFG_REG("INOUTSEL1", 0xFFC41004, 32, 1) { GP_INOUTSEL(1) } },
+	{ PINMUX_CFG_REG("INOUTSEL2", 0xFFC42004, 32, 1) { GP_INOUTSEL(2) } },
+	{ PINMUX_CFG_REG("INOUTSEL3", 0xFFC43004, 32, 1) { GP_INOUTSEL(3) } },
+	{ PINMUX_CFG_REG("INOUTSEL4", 0xFFC44004, 32, 1) { GP_INOUTSEL(4) } },
+	{ PINMUX_CFG_REG("INOUTSEL5", 0xffc45004, 32, 1) {
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 31 - 24 */
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 23 - 16 */
+		0, 0, 0, 0, 0, 0, 0, 0, /* 15 - 12 */
+		GP_5_11_IN, GP_5_11_OUT,
+		GP_5_10_IN, GP_5_10_OUT,
+		GP_5_9_IN, GP_5_9_OUT,
+		GP_5_8_IN, GP_5_8_OUT,
+		GP_5_7_IN, GP_5_7_OUT,
+		GP_5_6_IN, GP_5_6_OUT,
+		GP_5_5_IN, GP_5_5_OUT,
+		GP_5_4_IN, GP_5_4_OUT,
+		GP_5_3_IN, GP_5_3_OUT,
+		GP_5_2_IN, GP_5_2_OUT,
+		GP_5_1_IN, GP_5_1_OUT,
+		GP_5_0_IN, GP_5_0_OUT }
+	},
+	{ },
+};
+
+static struct pinmux_data_reg pinmux_data_regs[] = {
+	/* GPIO 0 - 5*/
+	{ PINMUX_DATA_REG("INDT0", 0xFFC4000C, 32) { GP_INDT(0) } },
+	{ PINMUX_DATA_REG("INDT1", 0xFFC4100C, 32) { GP_INDT(1) } },
+	{ PINMUX_DATA_REG("INDT2", 0xFFC4200C, 32) { GP_INDT(2) } },
+	{ PINMUX_DATA_REG("INDT3", 0xFFC4300C, 32) { GP_INDT(3) } },
+	{ PINMUX_DATA_REG("INDT4", 0xFFC4400C, 32) { GP_INDT(4) } },
+	{ PINMUX_DATA_REG("INDT5", 0xFFC4500C, 32) {
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0,
+		GP_5_11_DATA, GP_5_10_DATA, GP_5_9_DATA, GP_5_8_DATA,
+		GP_5_7_DATA, GP_5_6_DATA, GP_5_5_DATA, GP_5_4_DATA,
+		GP_5_3_DATA, GP_5_2_DATA, GP_5_1_DATA, GP_5_0_DATA }
+	},
+	{ },
+};
+
+static struct resource sh7734_pfc_resources[] = {
+	[0] = { /* PFC */
+		.start	= 0xFFFC0000,
+		.end	= 0xFFFC011C,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = { /* GPIO */
+		.start	= 0xFFC40000,
+		.end	= 0xFFC4502B,
+		.flags	= IORESOURCE_MEM,
+	}
+};
+
+static struct pinmux_info sh7734_pinmux_info = {
+	.name = "sh7734_pfc",
+
+	.resource = sh7734_pfc_resources,
+	.num_resources = ARRAY_SIZE(sh7734_pfc_resources),
+
+	.unlock_reg = 0xFFFC0000,
+
+	.reserved_id = PINMUX_RESERVED,
+	.data = { PINMUX_DATA_BEGIN, PINMUX_DATA_END },
+	.input = { PINMUX_INPUT_BEGIN, PINMUX_INPUT_END },
+	.output = { PINMUX_OUTPUT_BEGIN, PINMUX_OUTPUT_END },
+	.mark = { PINMUX_MARK_BEGIN, PINMUX_MARK_END },
+	.function = { PINMUX_FUNCTION_BEGIN, PINMUX_FUNCTION_END },
+
+	.first_gpio = GPIO_GP_0_0,
+	.last_gpio = GPIO_FN_ST_CLKOUT,
+
+	.gpios = pinmux_gpios,
+	.cfg_regs = pinmux_config_regs,
+	.data_regs = pinmux_data_regs,
+
+	.gpio_data = pinmux_data,
+	.gpio_data_size = ARRAY_SIZE(pinmux_data),
+};
+
+static int __init plat_pinmux_setup(void)
+{
+	return register_pinmux(&sh7734_pinmux_info);
+}
+arch_initcall(plat_pinmux_setup);
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7343.c b/arch/sh/kernel/cpu/sh4a/setup-sh7343.c
index 1b88483..b91ea83 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7343.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7343.c
@@ -13,6 +13,7 @@
 #include <linux/serial_sci.h>
 #include <linux/uio_driver.h>
 #include <linux/sh_timer.h>
+#include <linux/sh_intc.h>
 #include <asm/clock.h>
 
 /* Serial */
@@ -22,7 +23,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_CKE1,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type           = PORT_SCIF,
-	.irqs           = { 80, 80, 80, 80 },
+	.irqs           = SCIx_IRQ_MUXED(evt2irq(0xc00)),
 };
 
 static struct platform_device scif0_device = {
@@ -39,7 +40,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_CKE1,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type           = PORT_SCIF,
-	.irqs           = { 81, 81, 81, 81 },
+	.irqs           = SCIx_IRQ_MUXED(evt2irq(0xc20)),
 };
 
 static struct platform_device scif1_device = {
@@ -56,7 +57,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_CKE1,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type           = PORT_SCIF,
-	.irqs           = { 82, 82, 82, 82 },
+	.irqs           = SCIx_IRQ_MUXED(evt2irq(0xc40)),
 };
 
 static struct platform_device scif2_device = {
@@ -73,7 +74,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_CKE1,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type           = PORT_SCIF,
-	.irqs           = { 83, 83, 83, 83 },
+	.irqs           = SCIx_IRQ_MUXED(evt2irq(0xc60)),
 };
 
 static struct platform_device scif3_device = {
@@ -92,8 +93,8 @@
 		.flags  = IORESOURCE_MEM,
 	},
 	[1] = {
-		.start  = 96,
-		.end    = 99,
+		.start  = evt2irq(0xe00),
+		.end    = evt2irq(0xe60),
 		.flags  = IORESOURCE_IRQ,
        },
 };
@@ -113,8 +114,8 @@
 		.flags  = IORESOURCE_MEM,
 	},
 	[1] = {
-		.start  = 44,
-		.end    = 47,
+		.start  = evt2irq(0x780),
+		.end    = evt2irq(0x7e0),
 		.flags  = IORESOURCE_IRQ,
        },
 };
@@ -129,7 +130,7 @@
 static struct uio_info vpu_platform_data = {
 	.name = "VPU4",
 	.version = "0",
-	.irq = 60,
+	.irq = evt2irq(0x980),
 };
 
 static struct resource vpu_resources[] = {
@@ -157,7 +158,7 @@
 static struct uio_info veu_platform_data = {
 	.name = "VEU",
 	.version = "0",
-	.irq = 54,
+	.irq = evt2irq(0x8c0),
 };
 
 static struct resource veu_resources[] = {
@@ -185,7 +186,7 @@
 static struct uio_info jpu_platform_data = {
 	.name = "JPU",
 	.version = "0",
-	.irq = 27,
+	.irq = evt2irq(0x560),
 };
 
 static struct resource jpu_resources[] = {
@@ -224,7 +225,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 104,
+		.start	= evt2irq(0xf00),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -252,7 +253,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 16,
+		.start	= evt2irq(0x400),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -280,7 +281,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 17,
+		.start	= evt2irq(0x420),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -307,7 +308,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 18,
+		.start	= evt2irq(0x440),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7366.c b/arch/sh/kernel/cpu/sh4a/setup-sh7366.c
index 8777386..0bd09d5 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7366.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7366.c
@@ -15,6 +15,7 @@
 #include <linux/serial_sci.h>
 #include <linux/uio_driver.h>
 #include <linux/sh_timer.h>
+#include <linux/sh_intc.h>
 #include <linux/usb/r8a66597.h>
 #include <asm/clock.h>
 
@@ -25,7 +26,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCIF,
-	.irqs		= { 80, 80, 80, 80 },
+	.irqs		= SCIx_IRQ_MUXED(evt2irq(0xc00)),
 };
 
 static struct platform_device scif0_device = {
@@ -44,8 +45,8 @@
 		.flags  = IORESOURCE_MEM,
 	},
 	[1] = {
-		.start  = 96,
-		.end    = 99,
+		.start  = evt2irq(0xe00),
+		.end    = evt2irq(0xe60),
 		.flags  = IORESOURCE_IRQ,
        },
 };
@@ -68,8 +69,8 @@
 		.flags  = IORESOURCE_MEM,
 	},
 	[1] = {
-		.start  = 65,
-		.end    = 65,
+		.start  = evt2irq(0xa20),
+		.end    = evt2irq(0xa20),
 		.flags	= IORESOURCE_IRQ | IRQF_TRIGGER_LOW,
 	},
 };
@@ -89,7 +90,7 @@
 static struct uio_info vpu_platform_data = {
 	.name = "VPU5",
 	.version = "0",
-	.irq = 60,
+	.irq = evt2irq(0x980),
 };
 
 static struct resource vpu_resources[] = {
@@ -117,7 +118,7 @@
 static struct uio_info veu0_platform_data = {
 	.name = "VEU",
 	.version = "0",
-	.irq = 54,
+	.irq = evt2irq(0x8c0),
 };
 
 static struct resource veu0_resources[] = {
@@ -145,7 +146,7 @@
 static struct uio_info veu1_platform_data = {
 	.name = "VEU",
 	.version = "0",
-	.irq = 27,
+	.irq = evt2irq(0x560),
 };
 
 static struct resource veu1_resources[] = {
@@ -184,7 +185,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 104,
+		.start	= evt2irq(0xf00),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -240,7 +241,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 17,
+		.start	= evt2irq(0x420),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -267,7 +268,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 18,
+		.start	= evt2irq(0x440),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7722.c b/arch/sh/kernel/cpu/sh4a/setup-sh7722.c
index 8420d4b..0f5a219 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7722.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7722.c
@@ -13,6 +13,7 @@
 #include <linux/serial.h>
 #include <linux/serial_sci.h>
 #include <linux/sh_timer.h>
+#include <linux/sh_intc.h>
 #include <linux/uio_driver.h>
 #include <linux/usb/m66592.h>
 
@@ -147,20 +148,20 @@
 	},
 	{
 		.name	= "error_irq",
-		.start	= 78,
-		.end	= 78,
+		.start	= evt2irq(0xbc0),
+		.end	= evt2irq(0xbc0),
 		.flags	= IORESOURCE_IRQ,
 	},
 	{
 		/* IRQ for channels 0-3 */
-		.start	= 48,
-		.end	= 51,
+		.start	= evt2irq(0x800),
+		.end	= evt2irq(0x860),
 		.flags	= IORESOURCE_IRQ,
 	},
 	{
 		/* IRQ for channels 4-5 */
-		.start	= 76,
-		.end	= 77,
+		.start	= evt2irq(0xb80),
+		.end	= evt2irq(0xba0),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -182,7 +183,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type           = PORT_SCIF,
-	.irqs           = { 80, 80, 80, 80 },
+	.irqs           = SCIx_IRQ_MUXED(evt2irq(0xc00)),
 	.ops		= &sh7722_sci_port_ops,
 	.regtype	= SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE,
 };
@@ -201,7 +202,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type           = PORT_SCIF,
-	.irqs           = { 81, 81, 81, 81 },
+	.irqs           = SCIx_IRQ_MUXED(evt2irq(0xc20)),
 	.ops		= &sh7722_sci_port_ops,
 	.regtype	= SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE,
 };
@@ -220,7 +221,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type           = PORT_SCIF,
-	.irqs           = { 82, 82, 82, 82 },
+	.irqs           = SCIx_IRQ_MUXED(evt2irq(0xc40)),
 	.ops		= &sh7722_sci_port_ops,
 	.regtype	= SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE,
 };
@@ -241,17 +242,17 @@
 	},
 	[1] = {
 		/* Period IRQ */
-		.start	= 45,
+		.start	= evt2irq(0x7a0),
 		.flags	= IORESOURCE_IRQ,
 	},
 	[2] = {
 		/* Carry IRQ */
-		.start	= 46,
+		.start	= evt2irq(0x7c0),
 		.flags	= IORESOURCE_IRQ,
 	},
 	[3] = {
 		/* Alarm IRQ */
-		.start	= 44,
+		.start	= evt2irq(0x780),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -275,8 +276,8 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 65,
-		.end	= 65,
+		.start	= evt2irq(0xa20),
+		.end	= evt2irq(0xa20),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -301,8 +302,8 @@
 		.flags  = IORESOURCE_MEM,
 	},
 	[1] = {
-		.start  = 96,
-		.end    = 99,
+		.start  = evt2irq(0xe00),
+		.end    = evt2irq(0xe60),
 		.flags  = IORESOURCE_IRQ,
        },
 };
@@ -317,7 +318,7 @@
 static struct uio_info vpu_platform_data = {
 	.name = "VPU4",
 	.version = "0",
-	.irq = 60,
+	.irq = evt2irq(0x980),
 };
 
 static struct resource vpu_resources[] = {
@@ -345,7 +346,7 @@
 static struct uio_info veu_platform_data = {
 	.name = "VEU",
 	.version = "0",
-	.irq = 54,
+	.irq = evt2irq(0x8c0),
 };
 
 static struct resource veu_resources[] = {
@@ -373,7 +374,7 @@
 static struct uio_info jpu_platform_data = {
 	.name = "JPU",
 	.version = "0",
-	.irq = 27,
+	.irq = evt2irq(0x560),
 };
 
 static struct resource jpu_resources[] = {
@@ -412,7 +413,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 104,
+		.start	= evt2irq(0xf00),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -440,7 +441,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 16,
+		.start	= evt2irq(0x400),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -468,7 +469,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 17,
+		.start	= evt2irq(0x420),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -525,7 +526,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 108,
+		.start	= evt2irq(0xf80),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7723.c b/arch/sh/kernel/cpu/sh4a/setup-sh7723.c
index a188c9e..28d6fd8 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7723.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7723.c
@@ -15,6 +15,7 @@
 #include <linux/uio_driver.h>
 #include <linux/usb/r8a66597.h>
 #include <linux/sh_timer.h>
+#include <linux/sh_intc.h>
 #include <linux/io.h>
 #include <asm/clock.h>
 #include <asm/mmzone.h>
@@ -28,7 +29,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type           = PORT_SCIF,
-	.irqs           = { 80, 80, 80, 80 },
+	.irqs           = SCIx_IRQ_MUXED(evt2irq(0xc00)),
 	.regtype	= SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE,
 };
 
@@ -47,7 +48,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type           = PORT_SCIF,
-	.irqs           = { 81, 81, 81, 81 },
+	.irqs           = SCIx_IRQ_MUXED(evt2irq(0xc20)),
 	.regtype	= SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE,
 };
 
@@ -66,7 +67,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type           = PORT_SCIF,
-	.irqs           = { 82, 82, 82, 82 },
+	.irqs           = SCIx_IRQ_MUXED(evt2irq(0xc40)),
 	.regtype	= SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE,
 };
 
@@ -85,7 +86,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE,
 	.scbrr_algo_id	= SCBRR_ALGO_3,
 	.type           = PORT_SCIFA,
-	.irqs           = { 56, 56, 56, 56 },
+	.irqs           = SCIx_IRQ_MUXED(evt2irq(0x900)),
 };
 
 static struct platform_device scif3_device = {
@@ -103,7 +104,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE,
 	.scbrr_algo_id	= SCBRR_ALGO_3,
 	.type           = PORT_SCIFA,
-	.irqs           = { 88, 88, 88, 88 },
+	.irqs           = SCIx_IRQ_MUXED(evt2irq(0xd00)),
 };
 
 static struct platform_device scif4_device = {
@@ -121,7 +122,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE,
 	.scbrr_algo_id	= SCBRR_ALGO_3,
 	.type           = PORT_SCIFA,
-	.irqs           = { 109, 109, 109, 109 },
+	.irqs           = SCIx_IRQ_MUXED(evt2irq(0xfa0)),
 };
 
 static struct platform_device scif5_device = {
@@ -135,7 +136,7 @@
 static struct uio_info vpu_platform_data = {
 	.name = "VPU5",
 	.version = "0",
-	.irq = 60,
+	.irq = evt2irq(0x980),
 };
 
 static struct resource vpu_resources[] = {
@@ -163,7 +164,7 @@
 static struct uio_info veu0_platform_data = {
 	.name = "VEU2H",
 	.version = "0",
-	.irq = 54,
+	.irq = evt2irq(0x8c0),
 };
 
 static struct resource veu0_resources[] = {
@@ -191,7 +192,7 @@
 static struct uio_info veu1_platform_data = {
 	.name = "VEU2H",
 	.version = "0",
-	.irq = 27,
+	.irq = evt2irq(0x560),
 };
 
 static struct resource veu1_resources[] = {
@@ -230,7 +231,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 104,
+		.start	= evt2irq(0xf00),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -258,7 +259,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 16,
+		.start	= evt2irq(0x400),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -286,7 +287,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 17,
+		.start	= evt2irq(0x420),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -313,7 +314,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 18,
+		.start	= evt2irq(0x440),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -340,7 +341,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 57,
+		.start	= evt2irq(0x920),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -367,7 +368,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 58,
+		.start	= evt2irq(0x940),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -394,7 +395,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 57,
+		.start	= evt2irq(0x920),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -417,17 +418,17 @@
 	},
 	[1] = {
 		/* Period IRQ */
-		.start	= 69,
+		.start	= evt2irq(0xaa0),
 		.flags	= IORESOURCE_IRQ,
 	},
 	[2] = {
 		/* Carry IRQ */
-		.start	= 70,
+		.start	= evt2irq(0xac0),
 		.flags	= IORESOURCE_IRQ,
 	},
 	[3] = {
 		/* Alarm IRQ */
-		.start	= 68,
+		.start	= evt2irq(0xa80),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -450,8 +451,8 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 65,
-		.end	= 65,
+		.start	= evt2irq(0xa20),
+		.end	= evt2irq(0xa20),
 		.flags	= IORESOURCE_IRQ | IRQF_TRIGGER_LOW,
 	},
 };
@@ -476,8 +477,8 @@
 		.flags  = IORESOURCE_MEM,
 	},
 	[1] = {
-		.start  = 96,
-		.end    = 99,
+		.start  = evt2irq(0xe00),
+		.end    = evt2irq(0xe60),
 		.flags  = IORESOURCE_IRQ,
        },
 };
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7724.c b/arch/sh/kernel/cpu/sh4a/setup-sh7724.c
index 4c671cf..26b74c2 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7724.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7724.c
@@ -20,6 +20,7 @@
 #include <linux/uio_driver.h>
 #include <linux/sh_dma.h>
 #include <linux/sh_timer.h>
+#include <linux/sh_intc.h>
 #include <linux/io.h>
 #include <linux/notifier.h>
 
@@ -215,20 +216,20 @@
 	},
 	{
 		.name	= "error_irq",
-		.start	= 78,
-		.end	= 78,
+		.start	= evt2irq(0xbc0),
+		.end	= evt2irq(0xbc0),
 		.flags	= IORESOURCE_IRQ,
 	},
 	{
 		/* IRQ for channels 0-3 */
-		.start	= 48,
-		.end	= 51,
+		.start	= evt2irq(0x800),
+		.end	= evt2irq(0x860),
 		.flags	= IORESOURCE_IRQ,
 	},
 	{
 		/* IRQ for channels 4-5 */
-		.start	= 76,
-		.end	= 77,
+		.start	= evt2irq(0xb80),
+		.end	= evt2irq(0xba0),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -249,20 +250,20 @@
 	},
 	{
 		.name	= "error_irq",
-		.start	= 74,
-		.end	= 74,
+		.start	= evt2irq(0xb40),
+		.end	= evt2irq(0xb40),
 		.flags	= IORESOURCE_IRQ,
 	},
 	{
 		/* IRQ for channels 0-3 */
-		.start	= 40,
-		.end	= 43,
+		.start	= evt2irq(0x700),
+		.end	= evt2irq(0x760),
 		.flags	= IORESOURCE_IRQ,
 	},
 	{
 		/* IRQ for channels 4-5 */
-		.start	= 72,
-		.end	= 73,
+		.start	= evt2irq(0xb00),
+		.end	= evt2irq(0xb20),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -295,7 +296,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type           = PORT_SCIF,
-	.irqs           = { 80, 80, 80, 80 },
+	.irqs           = SCIx_IRQ_MUXED(evt2irq(0xc00)),
 	.regtype	= SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE,
 };
 
@@ -314,7 +315,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type           = PORT_SCIF,
-	.irqs           = { 81, 81, 81, 81 },
+	.irqs           = SCIx_IRQ_MUXED(evt2irq(0xc20)),
 	.regtype	= SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE,
 };
 
@@ -333,7 +334,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type           = PORT_SCIF,
-	.irqs           = { 82, 82, 82, 82 },
+	.irqs           = SCIx_IRQ_MUXED(evt2irq(0xc40)),
 	.regtype	= SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE,
 };
 
@@ -352,7 +353,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE,
 	.scbrr_algo_id	= SCBRR_ALGO_3,
 	.type           = PORT_SCIFA,
-	.irqs           = { 56, 56, 56, 56 },
+	.irqs           = SCIx_IRQ_MUXED(evt2irq(0x900)),
 };
 
 static struct platform_device scif3_device = {
@@ -370,7 +371,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE,
 	.scbrr_algo_id	= SCBRR_ALGO_3,
 	.type           = PORT_SCIFA,
-	.irqs           = { 88, 88, 88, 88 },
+	.irqs           = SCIx_IRQ_MUXED(evt2irq(0xd00)),
 };
 
 static struct platform_device scif4_device = {
@@ -388,7 +389,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE,
 	.scbrr_algo_id	= SCBRR_ALGO_3,
 	.type           = PORT_SCIFA,
-	.irqs           = { 109, 109, 109, 109 },
+	.irqs           = SCIx_IRQ_MUXED(evt2irq(0xfa0)),
 };
 
 static struct platform_device scif5_device = {
@@ -408,17 +409,17 @@
 	},
 	[1] = {
 		/* Period IRQ */
-		.start	= 69,
+		.start	= evt2irq(0xaa0),
 		.flags	= IORESOURCE_IRQ,
 	},
 	[2] = {
 		/* Carry IRQ */
-		.start	= 70,
+		.start	= evt2irq(0xac0),
 		.flags	= IORESOURCE_IRQ,
 	},
 	[3] = {
 		/* Alarm IRQ */
-		.start	= 68,
+		.start	= evt2irq(0xa80),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -439,8 +440,8 @@
 		.flags  = IORESOURCE_MEM,
 	},
 	[1] = {
-		.start  = 96,
-		.end    = 99,
+		.start  = evt2irq(0xe00),
+		.end    = evt2irq(0xe60),
 		.flags  = IORESOURCE_IRQ,
 	},
 };
@@ -461,8 +462,8 @@
 		.flags  = IORESOURCE_MEM,
 	},
 	[1] = {
-		.start  = 92,
-		.end    = 95,
+		.start  = evt2irq(0xd80),
+		.end    = evt2irq(0xde0),
 		.flags  = IORESOURCE_IRQ,
 	},
 };
@@ -478,7 +479,7 @@
 static struct uio_info vpu_platform_data = {
 	.name = "VPU5F",
 	.version = "0",
-	.irq = 60,
+	.irq = evt2irq(0x980),
 };
 
 static struct resource vpu_resources[] = {
@@ -507,7 +508,7 @@
 static struct uio_info veu0_platform_data = {
 	.name = "VEU3F0",
 	.version = "0",
-	.irq = 83,
+	.irq = evt2irq(0xc60),
 };
 
 static struct resource veu0_resources[] = {
@@ -536,7 +537,7 @@
 static struct uio_info veu1_platform_data = {
 	.name = "VEU3F1",
 	.version = "0",
-	.irq = 54,
+	.irq = evt2irq(0x8c0),
 };
 
 static struct resource veu1_resources[] = {
@@ -633,7 +634,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 104,
+		.start	= evt2irq(0xf00),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -661,7 +662,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 16,
+		.start	= evt2irq(0x400),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -689,7 +690,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 17,
+		.start	= evt2irq(0x420),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -716,7 +717,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 18,
+		.start	= evt2irq(0x440),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -744,7 +745,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 57,
+		.start	= evt2irq(0x920),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -771,7 +772,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 58,
+		.start	= evt2irq(0x940),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -798,7 +799,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 57,
+		.start	= evt2irq(0x920),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -817,7 +818,7 @@
 static struct uio_info jpu_platform_data = {
 	.name = "JPU",
 	.version = "0",
-	.irq = 27,
+	.irq = evt2irq(0x560),
 };
 
 static struct resource jpu_resources[] = {
@@ -846,7 +847,7 @@
 static struct uio_info spu0_platform_data = {
 	.name = "SPU2DSP0",
 	.version = "0",
-	.irq = 86,
+	.irq = evt2irq(0xcc0),
 };
 
 static struct resource spu0_resources[] = {
@@ -875,7 +876,7 @@
 static struct uio_info spu1_platform_data = {
 	.name = "SPU2DSP1",
 	.version = "0",
-	.irq = 87,
+	.irq = evt2irq(0xce0),
 };
 
 static struct resource spu1_resources[] = {
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7734.c b/arch/sh/kernel/cpu/sh4a/setup-sh7734.c
new file mode 100644
index 0000000..f799971d
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7734.c
@@ -0,0 +1,800 @@
+/*
+ * arch/sh/kernel/cpu/sh4a/setup-sh7734.c
+
+ * SH7734 Setup
+ *
+ * Copyright (C) 2011,2012 Nobuhiro Iwamatsu <nobuhiro.iwamatsu.yj@renesas.com>
+ * Copyright (C) 2011,2012 Renesas Solutions Corp.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/serial.h>
+#include <linux/mm.h>
+#include <linux/dma-mapping.h>
+#include <linux/serial_sci.h>
+#include <linux/sh_timer.h>
+#include <linux/io.h>
+#include <asm/clock.h>
+#include <asm/irq.h>
+#include <cpu/sh7734.h>
+
+/* SCIF */
+static struct plat_sci_port scif0_platform_data = {
+	.mapbase        = 0xFFE40000,
+	.flags          = UPF_BOOT_AUTOCONF,
+	.scscr          = SCSCR_RE | SCSCR_TE | SCSCR_REIE,
+	.scbrr_algo_id  = SCBRR_ALGO_2,
+	.type           = PORT_SCIF,
+	.irqs           = SCIx_IRQ_MUXED(evt2irq(0x8C0)),
+	.regtype        = SCIx_SH4_SCIF_REGTYPE,
+};
+
+static struct platform_device scif0_device = {
+	.name		= "sh-sci",
+	.id			= 0,
+	.dev		= {
+		.platform_data	= &scif0_platform_data,
+	},
+};
+
+static struct plat_sci_port scif1_platform_data = {
+	.mapbase        = 0xFFE41000,
+	.flags          = UPF_BOOT_AUTOCONF,
+	.scscr          = SCSCR_RE | SCSCR_TE | SCSCR_REIE,
+	.scbrr_algo_id	= SCBRR_ALGO_2,
+	.type           = PORT_SCIF,
+	.irqs           = SCIx_IRQ_MUXED(evt2irq(0x8E0)),
+	.regtype        = SCIx_SH4_SCIF_REGTYPE,
+};
+
+static struct platform_device scif1_device = {
+	.name		= "sh-sci",
+	.id         = 1,
+	.dev		= {
+		.platform_data = &scif1_platform_data,
+	},
+};
+
+static struct plat_sci_port scif2_platform_data = {
+	.mapbase        = 0xFFE42000,
+	.flags          = UPF_BOOT_AUTOCONF,
+	.scscr          = SCSCR_RE | SCSCR_TE | SCSCR_REIE,
+	.scbrr_algo_id  = SCBRR_ALGO_2,
+	.type           = PORT_SCIF,
+	.irqs           = SCIx_IRQ_MUXED(evt2irq(0x900)),
+	.regtype        = SCIx_SH4_SCIF_REGTYPE,
+};
+
+static struct platform_device scif2_device = {
+	.name		= "sh-sci",
+	.id         = 2,
+	.dev		= {
+		.platform_data = &scif2_platform_data,
+	},
+};
+
+static struct plat_sci_port scif3_platform_data = {
+	.mapbase        = 0xFFE43000,
+	.flags          = UPF_BOOT_AUTOCONF,
+	.scscr          = SCSCR_RE | SCSCR_TE | SCSCR_REIE | SCSCR_TOIE,
+	.scbrr_algo_id  = SCBRR_ALGO_2,
+	.type           = PORT_SCIF,
+	.irqs           = SCIx_IRQ_MUXED(evt2irq(0x920)),
+	.regtype        = SCIx_SH4_SCIF_REGTYPE,
+};
+
+static struct platform_device scif3_device = {
+	.name		= "sh-sci",
+	.id	        = 3,
+	.dev		= {
+		.platform_data	= &scif3_platform_data,
+	},
+};
+
+static struct plat_sci_port scif4_platform_data = {
+	.mapbase        = 0xFFE44000,
+	.flags          = UPF_BOOT_AUTOCONF,
+	.scscr          = SCSCR_RE | SCSCR_TE | SCSCR_REIE,
+	.scbrr_algo_id	= SCBRR_ALGO_2,
+	.type           = PORT_SCIF,
+	.irqs           = SCIx_IRQ_MUXED(evt2irq(0x940)),
+	.regtype        = SCIx_SH4_SCIF_REGTYPE,
+};
+
+static struct platform_device scif4_device = {
+	.name		= "sh-sci",
+	.id	        = 4,
+	.dev		= {
+		.platform_data	= &scif4_platform_data,
+	},
+};
+
+static struct plat_sci_port scif5_platform_data = {
+	.mapbase        = 0xFFE43000,
+	.flags          = UPF_BOOT_AUTOCONF,
+	.scscr          = SCSCR_RE | SCSCR_TE | SCSCR_REIE,
+	.scbrr_algo_id	= SCBRR_ALGO_2,
+	.type           = PORT_SCIF,
+	.irqs           = SCIx_IRQ_MUXED(evt2irq(0x960)),
+	.regtype		= SCIx_SH4_SCIF_REGTYPE,
+};
+
+static struct platform_device scif5_device = {
+	.name		= "sh-sci",
+	.id	        = 5,
+	.dev		= {
+		.platform_data	= &scif5_platform_data,
+	},
+};
+
+/* RTC */
+static struct resource rtc_resources[] = {
+	[0] = {
+		.name	= "rtc",
+		.start	= 0xFFFC5000,
+		.end	= 0xFFFC5000 + 0x26 - 1,
+		.flags	= IORESOURCE_IO,
+	},
+	[1] = {
+		.start	= evt2irq(0xC00),
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device rtc_device = {
+	.name		= "sh-rtc",
+	.id		= -1,
+	.num_resources	= ARRAY_SIZE(rtc_resources),
+	.resource	= rtc_resources,
+};
+
+/* I2C 0 */
+static struct resource i2c0_resources[] = {
+	[0] = {
+		.name	= "IIC0",
+		.start  = 0xFFC70000,
+		.end    = 0xFFC7000A - 1,
+		.flags  = IORESOURCE_MEM,
+	},
+	[1] = {
+		.start  = evt2irq(0x860),
+		.flags  = IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device i2c0_device = {
+	.name           = "i2c-sh7734",
+	.id             = 0,
+	.num_resources  = ARRAY_SIZE(i2c0_resources),
+	.resource       = i2c0_resources,
+};
+
+/* TMU */
+static struct sh_timer_config tmu0_platform_data = {
+	.channel_offset = 0x04,
+	.timer_bit = 0,
+	.clockevent_rating = 200,
+};
+
+static struct resource tmu0_resources[] = {
+	[0] = {
+		.start	= 0xFFD80008,
+		.end	= 0xFFD80014 - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= evt2irq(0x400),
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device tmu0_device = {
+	.name	= "sh_tmu",
+	.id		= 0,
+	.dev = {
+		.platform_data	= &tmu0_platform_data,
+	},
+	.resource	= tmu0_resources,
+	.num_resources	= ARRAY_SIZE(tmu0_resources),
+};
+
+static struct sh_timer_config tmu1_platform_data = {
+	.channel_offset = 0x10,
+	.timer_bit = 1,
+	.clocksource_rating = 200,
+};
+
+static struct resource tmu1_resources[] = {
+	[0] = {
+		.start	= 0xFFD80014,
+		.end	= 0xFFD80020 - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= evt2irq(0x420),
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device tmu1_device = {
+	.name		= "sh_tmu",
+	.id			= 1,
+	.dev = {
+		.platform_data	= &tmu1_platform_data,
+	},
+	.resource	= tmu1_resources,
+	.num_resources	= ARRAY_SIZE(tmu1_resources),
+};
+
+static struct sh_timer_config tmu2_platform_data = {
+	.channel_offset = 0x1c,
+	.timer_bit = 2,
+};
+
+static struct resource tmu2_resources[] = {
+	[0] = {
+		.start	= 0xFFD80020,
+		.end	= 0xFFD80030 - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= evt2irq(0x440),
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device tmu2_device = {
+	.name		= "sh_tmu",
+	.id			= 2,
+	.dev = {
+		.platform_data	= &tmu2_platform_data,
+	},
+	.resource	= tmu2_resources,
+	.num_resources	= ARRAY_SIZE(tmu2_resources),
+};
+
+
+static struct sh_timer_config tmu3_platform_data = {
+	.channel_offset = 0x04,
+	.timer_bit = 0,
+};
+
+static struct resource tmu3_resources[] = {
+	[0] = {
+		.start	= 0xFFD81008,
+		.end	= 0xFFD81014 - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= evt2irq(0x480),
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device tmu3_device = {
+	.name		= "sh_tmu",
+	.id			= 3,
+	.dev = {
+		.platform_data	= &tmu3_platform_data,
+	},
+	.resource	= tmu3_resources,
+	.num_resources	= ARRAY_SIZE(tmu3_resources),
+};
+
+static struct sh_timer_config tmu4_platform_data = {
+	.channel_offset = 0x10,
+	.timer_bit = 1,
+};
+
+static struct resource tmu4_resources[] = {
+	[0] = {
+		.start	= 0xFFD81014,
+		.end	= 0xFFD81020 - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= evt2irq(0x4A0),
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device tmu4_device = {
+	.name		= "sh_tmu",
+	.id			= 4,
+	.dev = {
+		.platform_data	= &tmu4_platform_data,
+	},
+	.resource	= tmu4_resources,
+	.num_resources	= ARRAY_SIZE(tmu4_resources),
+};
+
+static struct sh_timer_config tmu5_platform_data = {
+	.channel_offset = 0x1c,
+	.timer_bit = 2,
+};
+
+static struct resource tmu5_resources[] = {
+	[0] = {
+		.start	= 0xFFD81020,
+		.end	= 0xFFD81030 - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= evt2irq(0x4C0),
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device tmu5_device = {
+	.name		= "sh_tmu",
+	.id			= 5,
+	.dev = {
+		.platform_data	= &tmu5_platform_data,
+	},
+	.resource	= tmu5_resources,
+	.num_resources	= ARRAY_SIZE(tmu5_resources),
+};
+
+static struct sh_timer_config tmu6_platform_data = {
+	.channel_offset = 0x4,
+	.timer_bit = 0,
+};
+
+static struct resource tmu6_resources[] = {
+	[0] = {
+		.start	= 0xFFD82008,
+		.end	= 0xFFD82014 - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= evt2irq(0x500),
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device tmu6_device = {
+	.name		= "sh_tmu",
+	.id			= 6,
+	.dev = {
+		.platform_data	= &tmu6_platform_data,
+	},
+	.resource	= tmu6_resources,
+	.num_resources	= ARRAY_SIZE(tmu6_resources),
+};
+
+static struct sh_timer_config tmu7_platform_data = {
+	.channel_offset = 0x10,
+	.timer_bit = 1,
+};
+
+static struct resource tmu7_resources[] = {
+	[0] = {
+		.start	= 0xFFD82014,
+		.end	= 0xFFD82020 - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= evt2irq(0x520),
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device tmu7_device = {
+	.name		= "sh_tmu",
+	.id			= 7,
+	.dev = {
+		.platform_data	= &tmu7_platform_data,
+	},
+	.resource	= tmu7_resources,
+	.num_resources	= ARRAY_SIZE(tmu7_resources),
+};
+
+static struct sh_timer_config tmu8_platform_data = {
+	.channel_offset = 0x1c,
+	.timer_bit = 2,
+};
+
+static struct resource tmu8_resources[] = {
+	[0] = {
+		.start	= 0xFFD82020,
+		.end	= 0xFFD82030 - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= evt2irq(0x540),
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device tmu8_device = {
+	.name		= "sh_tmu",
+	.id			= 8,
+	.dev = {
+		.platform_data	= &tmu8_platform_data,
+	},
+	.resource	= tmu8_resources,
+	.num_resources	= ARRAY_SIZE(tmu8_resources),
+};
+
+static struct platform_device *sh7734_devices[] __initdata = {
+	&scif0_device,
+	&scif1_device,
+	&scif2_device,
+	&scif3_device,
+	&scif4_device,
+	&scif5_device,
+	&tmu0_device,
+	&tmu1_device,
+	&tmu2_device,
+	&tmu3_device,
+	&tmu4_device,
+	&tmu5_device,
+	&tmu6_device,
+	&tmu7_device,
+	&tmu8_device,
+	&rtc_device,
+};
+
+static struct platform_device *sh7734_early_devices[] __initdata = {
+	&scif0_device,
+	&scif1_device,
+	&scif2_device,
+	&scif3_device,
+	&scif4_device,
+	&scif5_device,
+	&tmu0_device,
+	&tmu1_device,
+	&tmu2_device,
+	&tmu3_device,
+	&tmu4_device,
+	&tmu5_device,
+	&tmu6_device,
+	&tmu7_device,
+	&tmu8_device,
+};
+
+void __init plat_early_device_setup(void)
+{
+	early_platform_add_devices(sh7734_early_devices,
+		ARRAY_SIZE(sh7734_early_devices));
+}
+
+#define GROUP 0
+enum {
+	UNUSED = 0,
+
+	/* interrupt sources */
+
+	IRL0_LLLL, IRL0_LLLH, IRL0_LLHL, IRL0_LLHH,
+	IRL0_LHLL, IRL0_LHLH, IRL0_LHHL, IRL0_LHHH,
+	IRL0_HLLL, IRL0_HLLH, IRL0_HLHL, IRL0_HLHH,
+	IRL0_HHLL, IRL0_HHLH, IRL0_HHHL,
+
+	IRQ0, IRQ1, IRQ2, IRQ3,
+	DU,
+	TMU00, TMU10, TMU20, TMU21,
+	TMU30, TMU40, TMU50, TMU51,
+	TMU60, TMU70, TMU80,
+	RESET_WDT,
+	USB,
+	HUDI,
+	SHDMAC,
+	SSI0, SSI1,	SSI2, SSI3,
+	VIN0,
+	RGPVG,
+	_2DG,
+	MMC,
+	HSPI,
+	LBSCATA,
+	I2C0,
+	RCAN0,
+	MIMLB,
+	SCIF0, SCIF1, SCIF2, SCIF3, SCIF4, SCIF5,
+	LBSCDMAC0, LBSCDMAC1, LBSCDMAC2,
+	RCAN1,
+	SDHI0, SDHI1,
+	IEBUS,
+	HPBDMAC0_3, HPBDMAC4_10, HPBDMAC11_18, HPBDMAC19_22, HPBDMAC23_25_27_28,
+	RTC,
+	VIN1,
+	LCDC,
+	SRC0, SRC1,
+	GETHER,
+	SDHI2,
+	GPIO0_3, GPIO4_5,
+	STIF0, STIF1,
+	ADMAC,
+	HIF,
+	FLCTL,
+	ADC,
+	MTU2,
+	RSPI,
+	QSPI,
+	HSCIF,
+	VEU3F_VE3,
+
+	/* Group */
+	/* Mask */
+	STIF_M,
+	GPIO_M,
+	HPBDMAC_M,
+	LBSCDMAC_M,
+	RCAN_M,
+	SRC_M,
+	SCIF_M,
+	LCDC_M,
+	_2DG_M,
+	VIN_M,
+	TMU_3_M,
+	TMU_0_M,
+
+	/* Priority */
+	RCAN_P,
+	LBSCDMAC_P,
+
+	/* Common */
+	SDHI,
+	SSI,
+	SPI,
+};
+
+static struct intc_vect vectors[] __initdata = {
+	INTC_VECT(DU, 0x3E0),
+	INTC_VECT(TMU00, 0x400),
+	INTC_VECT(TMU10, 0x420),
+	INTC_VECT(TMU20, 0x440),
+	INTC_VECT(TMU30, 0x480),
+	INTC_VECT(TMU40, 0x4A0),
+	INTC_VECT(TMU50, 0x4C0),
+	INTC_VECT(TMU51, 0x4E0),
+	INTC_VECT(TMU60, 0x500),
+	INTC_VECT(TMU70, 0x520),
+	INTC_VECT(TMU80, 0x540),
+	INTC_VECT(RESET_WDT, 0x560),
+	INTC_VECT(USB, 0x580),
+	INTC_VECT(HUDI, 0x600),
+	INTC_VECT(SHDMAC, 0x620),
+	INTC_VECT(SSI0, 0x6C0),
+	INTC_VECT(SSI1, 0x6E0),
+	INTC_VECT(SSI2, 0x700),
+	INTC_VECT(SSI3, 0x720),
+	INTC_VECT(VIN0, 0x740),
+	INTC_VECT(RGPVG, 0x760),
+	INTC_VECT(_2DG, 0x780),
+	INTC_VECT(MMC, 0x7A0),
+	INTC_VECT(HSPI, 0x7E0),
+	INTC_VECT(LBSCATA, 0x840),
+	INTC_VECT(I2C0, 0x860),
+	INTC_VECT(RCAN0, 0x880),
+	INTC_VECT(SCIF0, 0x8A0),
+	INTC_VECT(SCIF1, 0x8C0),
+	INTC_VECT(SCIF2, 0x900),
+	INTC_VECT(SCIF3, 0x920),
+	INTC_VECT(SCIF4, 0x940),
+	INTC_VECT(SCIF5, 0x960),
+	INTC_VECT(LBSCDMAC0, 0x9E0),
+	INTC_VECT(LBSCDMAC1, 0xA00),
+	INTC_VECT(LBSCDMAC2, 0xA20),
+	INTC_VECT(RCAN1, 0xA60),
+	INTC_VECT(SDHI0, 0xAE0),
+	INTC_VECT(SDHI1, 0xB00),
+	INTC_VECT(IEBUS, 0xB20),
+	INTC_VECT(HPBDMAC0_3, 0xB60),
+	INTC_VECT(HPBDMAC4_10, 0xB80),
+	INTC_VECT(HPBDMAC11_18, 0xBA0),
+	INTC_VECT(HPBDMAC19_22, 0xBC0),
+	INTC_VECT(HPBDMAC23_25_27_28, 0xBE0),
+	INTC_VECT(RTC, 0xC00),
+	INTC_VECT(VIN1, 0xC20),
+	INTC_VECT(LCDC, 0xC40),
+	INTC_VECT(SRC0, 0xC60),
+	INTC_VECT(SRC1, 0xC80),
+	INTC_VECT(GETHER, 0xCA0),
+	INTC_VECT(SDHI2, 0xCC0),
+	INTC_VECT(GPIO0_3, 0xCE0),
+	INTC_VECT(GPIO4_5, 0xD00),
+	INTC_VECT(STIF0, 0xD20),
+	INTC_VECT(STIF1, 0xD40),
+	INTC_VECT(ADMAC, 0xDA0),
+	INTC_VECT(HIF, 0xDC0),
+	INTC_VECT(FLCTL, 0xDE0),
+	INTC_VECT(ADC, 0xE00),
+	INTC_VECT(MTU2, 0xE20),
+	INTC_VECT(RSPI, 0xE40),
+	INTC_VECT(QSPI, 0xE60),
+	INTC_VECT(HSCIF, 0xFC0),
+	INTC_VECT(VEU3F_VE3, 0xF40),
+};
+
+static struct intc_group groups[] __initdata = {
+	/* Common */
+	INTC_GROUP(SDHI, SDHI0, SDHI1, SDHI2),
+	INTC_GROUP(SPI, HSPI, RSPI, QSPI),
+	INTC_GROUP(SSI, SSI0, SSI1, SSI2, SSI3),
+
+	/* Mask group */
+	INTC_GROUP(STIF_M, STIF0, STIF1), /* 22 */
+	INTC_GROUP(GPIO_M, GPIO0_3, GPIO4_5), /* 21 */
+	INTC_GROUP(HPBDMAC_M, HPBDMAC0_3, HPBDMAC4_10, HPBDMAC11_18,
+			HPBDMAC19_22, HPBDMAC23_25_27_28), /* 19 */
+	INTC_GROUP(LBSCDMAC_M, LBSCDMAC0, LBSCDMAC1, LBSCDMAC2), /* 18 */
+	INTC_GROUP(RCAN_M, RCAN0, RCAN1, IEBUS), /* 17 */
+	INTC_GROUP(SRC_M, SRC0, SRC1), /* 16 */
+	INTC_GROUP(SCIF_M, SCIF0, SCIF1, SCIF2, SCIF3, SCIF4, SCIF5,
+			HSCIF), /* 14 */
+	INTC_GROUP(LCDC_M, LCDC, MIMLB), /* 13 */
+	INTC_GROUP(_2DG_M, _2DG, RGPVG), /* 12 */
+	INTC_GROUP(VIN_M, VIN0, VIN1), /* 10 */
+	INTC_GROUP(TMU_3_M, TMU30, TMU40, TMU50, TMU51,
+			TMU60, TMU60, TMU70, TMU80), /* 2 */
+	INTC_GROUP(TMU_0_M, TMU00, TMU10, TMU20, TMU21), /* 1 */
+
+	/* Priority group*/
+	INTC_GROUP(RCAN_P, RCAN0, RCAN1), /* INT2PRI5 */
+	INTC_GROUP(LBSCDMAC_P, LBSCDMAC0, LBSCDMAC1), /* INT2PRI5 */
+};
+
+static struct intc_mask_reg mask_registers[] __initdata = {
+	{ 0xFF804040, 0xFF804044, 32, /* INT2MSKRG / INT2MSKCR */
+	  { 0,
+		VEU3F_VE3,
+		SDHI, /* SDHI 0-2 */
+		ADMAC,
+		FLCTL,
+		RESET_WDT,
+		HIF,
+		ADC,
+		MTU2,
+		STIF_M, /* STIF 0,1 */
+		GPIO_M, /* GPIO 0-5*/
+		GETHER,
+		HPBDMAC_M, /* HPBDMAC 0_3 - 23_25_27_28 */
+		LBSCDMAC_M, /* LBSCDMAC 0 - 2 */
+		RCAN_M, /* RCAN, IEBUS */
+		SRC_M,	/* SRC 0,1 */
+		LBSCATA,
+		SCIF_M, /* SCIF 0-5, HSCIF */
+		LCDC_M, /* LCDC, MIMLB */
+		_2DG_M,	/* 2DG, RGPVG */
+		SPI, /* HSPI, RSPI, QSPI */
+		VIN_M,	/* VIN0, 1 */
+		SSI,	/* SSI 0-3 */
+		USB,
+		SHDMAC,
+		HUDI,
+		MMC,
+		RTC,
+		I2C0, /* I2C */ /* I2C 0, 1*/
+		TMU_3_M, /* TMU30 - TMU80 */
+		TMU_0_M, /* TMU00 - TMU21 */
+		DU } },
+};
+
+static struct intc_prio_reg prio_registers[] __initdata = {
+	{ 0xFF804000, 0, 32, 8, /* INT2PRI0 */
+		{ DU, TMU00, TMU10, TMU20 } },
+	{ 0xFF804004, 0, 32, 8, /* INT2PRI1 */
+		{ TMU30, TMU60, RTC, SDHI } },
+	{ 0xFF804008, 0, 32, 8, /* INT2PRI2 */
+		{ HUDI, SHDMAC, USB, SSI } },
+	{ 0xFF80400C, 0, 32, 8, /* INT2PRI3 */
+		{ VIN0, SPI, _2DG, LBSCATA } },
+	{ 0xFF804010, 0, 32, 8, /* INT2PRI4 */
+		{ SCIF0, SCIF3, HSCIF, LCDC } },
+	{ 0xFF804014, 0, 32, 8, /* INT2PRI5 */
+		{ RCAN_P, LBSCDMAC_P, LBSCDMAC2, MMC } },
+	{ 0xFF804018, 0, 32, 8, /* INT2PRI6 */
+		{ HPBDMAC0_3, HPBDMAC4_10, HPBDMAC11_18, HPBDMAC19_22 } },
+	{ 0xFF80401C, 0, 32, 8, /* INT2PRI7 */
+		{ HPBDMAC23_25_27_28, I2C0, SRC0, SRC1 } },
+	{ 0xFF804020, 0, 32, 8, /* INT2PRI8 */
+		{ 0 /* ADIF */, VIN1, RESET_WDT, HIF } },
+	{ 0xFF804024, 0, 32, 8, /* INT2PRI9 */
+		{ ADMAC, FLCTL, GPIO0_3, GPIO4_5 } },
+	{ 0xFF804028, 0, 32, 8, /* INT2PRI10 */
+		{ STIF0, STIF1, VEU3F_VE3, GETHER } },
+	{ 0xFF80402C, 0, 32, 8, /* INT2PRI11 */
+		{ MTU2, RGPVG, MIMLB, IEBUS } },
+};
+
+static DECLARE_INTC_DESC(intc_desc, "sh7734", vectors, groups,
+	mask_registers, prio_registers, NULL);
+
+/* Support for external interrupt pins in IRQ mode */
+
+static struct intc_vect irq3210_vectors[] __initdata = {
+	INTC_VECT(IRQ0, 0x240), INTC_VECT(IRQ1, 0x280),
+	INTC_VECT(IRQ2, 0x2C0), INTC_VECT(IRQ3, 0x300),
+};
+
+static struct intc_sense_reg irq3210_sense_registers[] __initdata = {
+	{ 0xFF80201C, 32, 2, /* ICR1 */
+	{ IRQ0, IRQ1, IRQ2, IRQ3, } },
+};
+
+static struct intc_mask_reg irq3210_ack_registers[] __initdata = {
+	{ 0xFF802024, 0, 32, /* INTREQ */
+	{ IRQ0, IRQ1, IRQ2, IRQ3, } },
+};
+
+static struct intc_mask_reg irq3210_mask_registers[] __initdata = {
+	{ 0xFF802044, 0xFF802064, 32, /* INTMSK0 / INTMSKCLR0 */
+	{ IRQ0, IRQ1, IRQ2, IRQ3, } },
+};
+
+static struct intc_prio_reg irq3210_prio_registers[] __initdata = {
+	{ 0xFF802010, 0, 32, 4, /* INTPRI */
+	{ IRQ0, IRQ1, IRQ2, IRQ3, } },
+};
+
+static DECLARE_INTC_DESC_ACK(intc_desc_irq3210, "sh7734-irq3210",
+	irq3210_vectors, NULL,
+	irq3210_mask_registers, irq3210_prio_registers,
+	irq3210_sense_registers, irq3210_ack_registers);
+
+/* External interrupt pins in IRL mode */
+
+static struct intc_vect vectors_irl3210[] __initdata = {
+	INTC_VECT(IRL0_LLLL, 0x200), INTC_VECT(IRL0_LLLH, 0x220),
+	INTC_VECT(IRL0_LLHL, 0x240), INTC_VECT(IRL0_LLHH, 0x260),
+	INTC_VECT(IRL0_LHLL, 0x280), INTC_VECT(IRL0_LHLH, 0x2a0),
+	INTC_VECT(IRL0_LHHL, 0x2c0), INTC_VECT(IRL0_LHHH, 0x2e0),
+	INTC_VECT(IRL0_HLLL, 0x300), INTC_VECT(IRL0_HLLH, 0x320),
+	INTC_VECT(IRL0_HLHL, 0x340), INTC_VECT(IRL0_HLHH, 0x360),
+	INTC_VECT(IRL0_HHLL, 0x380), INTC_VECT(IRL0_HHLH, 0x3a0),
+	INTC_VECT(IRL0_HHHL, 0x3c0),
+};
+
+static DECLARE_INTC_DESC(intc_desc_irl3210, "sh7734-irl3210",
+	vectors_irl3210, NULL, mask_registers, NULL, NULL);
+
+#define INTC_ICR0		0xFF802000
+#define INTC_INTMSK0    0xFF802044
+#define INTC_INTMSK1    0xFF802048
+#define INTC_INTMSKCLR0 0xFF802064
+#define INTC_INTMSKCLR1 0xFF802068
+
+void __init plat_irq_setup(void)
+{
+	/* disable IRQ3-0 */
+	__raw_writel(0xF0000000, INTC_INTMSK0);
+
+	/* disable IRL3-0 */
+	__raw_writel(0x80000000, INTC_INTMSK1);
+
+	/* select IRL mode for IRL3-0 */
+	__raw_writel(__raw_readl(INTC_ICR0) & ~0x00800000, INTC_ICR0);
+
+	/* disable holding function, ie enable "SH-4 Mode (LVLMODE)" */
+	__raw_writel(__raw_readl(INTC_ICR0) | 0x00200000, INTC_ICR0);
+
+	register_intc_controller(&intc_desc);
+}
+
+void __init plat_irq_setup_pins(int mode)
+{
+	switch (mode) {
+	case IRQ_MODE_IRQ3210:
+		/* select IRQ mode for IRL3-0 */
+		__raw_writel(__raw_readl(INTC_ICR0) | 0x00800000, INTC_ICR0);
+		register_intc_controller(&intc_desc_irq3210);
+		break;
+	case IRQ_MODE_IRL3210:
+		/* enable IRL0-3 but don't provide any masking */
+		__raw_writel(0x80000000, INTC_INTMSKCLR1);
+		__raw_writel(0xf0000000, INTC_INTMSKCLR0);
+		break;
+	case IRQ_MODE_IRL3210_MASK:
+		/* enable IRL0-3 and mask using cpu intc controller */
+		__raw_writel(0x80000000, INTC_INTMSKCLR0);
+		register_intc_controller(&intc_desc_irl3210);
+		break;
+	default:
+		BUG();
+	}
+}
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7757.c b/arch/sh/kernel/cpu/sh4a/setup-sh7757.c
index c8836cf..a770842 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7757.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7757.c
@@ -18,7 +18,7 @@
 #include <linux/dma-mapping.h>
 #include <linux/sh_timer.h>
 #include <linux/sh_dma.h>
-
+#include <linux/sh_intc.h>
 #include <cpu/dma-register.h>
 #include <cpu/sh7757.h>
 
@@ -28,7 +28,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCIF,
-	.irqs		= { 40, 40, 40, 40 },
+	.irqs		= SCIx_IRQ_MUXED(evt2irq(0x700)),
 };
 
 static struct platform_device scif2_device = {
@@ -45,7 +45,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCIF,
-	.irqs		= { 76, 76, 76, 76 },
+	.irqs		= SCIx_IRQ_MUXED(evt2irq(0xb80)),
 };
 
 static struct platform_device scif3_device = {
@@ -62,7 +62,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCIF,
-	.irqs		= { 104, 104, 104, 104 },
+	.irqs		= SCIx_IRQ_MUXED(evt2irq(0xF00)),
 };
 
 static struct platform_device scif4_device = {
@@ -86,7 +86,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 28,
+		.start	= evt2irq(0x580),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -114,7 +114,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 29,
+		.start	= evt2irq(0x5a0),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -136,7 +136,7 @@
 		.flags	= IORESOURCE_MEM | IORESOURCE_MEM_32BIT,
 	},
 	[1] = {
-		.start	= 86,
+		.start	= evt2irq(0xcc0),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -466,8 +466,8 @@
 	},
 	{
 		.name	= "error_irq",
-		.start	= 34,
-		.end	= 34,
+		.start	= evt2irq(0x640),
+		.end	= evt2irq(0x640),
 		.flags	= IORESOURCE_IRQ | IORESOURCE_IRQ_SHAREABLE,
 	},
 };
@@ -488,56 +488,56 @@
 	},
 	{
 		.name	= "error_irq",
-		.start	= 34,
-		.end	= 34,
+		.start	= evt2irq(0x640),
+		.end	= evt2irq(0x640),
 		.flags	= IORESOURCE_IRQ | IORESOURCE_IRQ_SHAREABLE,
 	},
 	{
 		/* IRQ for channels 4 */
-		.start	= 46,
-		.end	= 46,
+		.start	= evt2irq(0x7c0),
+		.end	= evt2irq(0x7c0),
 		.flags	= IORESOURCE_IRQ | IORESOURCE_IRQ_SHAREABLE,
 	},
 	{
 		/* IRQ for channels 5 */
-		.start	= 46,
-		.end	= 46,
+		.start	= evt2irq(0x7c0),
+		.end	= evt2irq(0x7c0),
 		.flags	= IORESOURCE_IRQ | IORESOURCE_IRQ_SHAREABLE,
 	},
 	{
 		/* IRQ for channels 6 */
-		.start	= 88,
-		.end	= 88,
+		.start	= evt2irq(0xd00),
+		.end	= evt2irq(0xd00),
 		.flags	= IORESOURCE_IRQ | IORESOURCE_IRQ_SHAREABLE,
 	},
 	{
 		/* IRQ for channels 7 */
-		.start	= 88,
-		.end	= 88,
+		.start	= evt2irq(0xd00),
+		.end	= evt2irq(0xd00),
 		.flags	= IORESOURCE_IRQ | IORESOURCE_IRQ_SHAREABLE,
 	},
 	{
 		/* IRQ for channels 8 */
-		.start	= 88,
-		.end	= 88,
+		.start	= evt2irq(0xd00),
+		.end	= evt2irq(0xd00),
 		.flags	= IORESOURCE_IRQ | IORESOURCE_IRQ_SHAREABLE,
 	},
 	{
 		/* IRQ for channels 9 */
-		.start	= 88,
-		.end	= 88,
+		.start	= evt2irq(0xd00),
+		.end	= evt2irq(0xd00),
 		.flags	= IORESOURCE_IRQ | IORESOURCE_IRQ_SHAREABLE,
 	},
 	{
 		/* IRQ for channels 10 */
-		.start	= 88,
-		.end	= 88,
+		.start	= evt2irq(0xd00),
+		.end	= evt2irq(0xd00),
 		.flags	= IORESOURCE_IRQ | IORESOURCE_IRQ_SHAREABLE,
 	},
 	{
 		/* IRQ for channels 11 */
-		.start	= 88,
-		.end	= 88,
+		.start	= evt2irq(0xd00),
+		.end	= evt2irq(0xd00),
 		.flags	= IORESOURCE_IRQ | IORESOURCE_IRQ_SHAREABLE,
 	},
 };
@@ -558,20 +558,20 @@
 	},
 	{
 		.name	= "error_irq",
-		.start	= 323,
-		.end	= 323,
+		.start	= evt2irq(0x2a60),
+		.end	= evt2irq(0x2a60),
 		.flags	= IORESOURCE_IRQ,
 	},
 	{
 		/* IRQ for channels 12 to 16 */
-		.start	= 272,
-		.end	= 276,
+		.start	= evt2irq(0x2400),
+		.end	= evt2irq(0x2480),
 		.flags	= IORESOURCE_IRQ,
 	},
 	{
 		/* IRQ for channel 17 */
-		.start	= 279,
-		.end	= 279,
+		.start	= evt2irq(0x24e0),
+		.end	= evt2irq(0x24e0),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -592,20 +592,20 @@
 	},
 	{
 		.name	= "error_irq",
-		.start	= 324,
-		.end	= 324,
+		.start	= evt2irq(0x2a80),
+		.end	= evt2irq(0x2a80),
 		.flags	= IORESOURCE_IRQ,
 	},
 	{
 		/* IRQ for channels 18 to 22 */
-		.start	= 280,
-		.end	= 284,
+		.start	= evt2irq(0x2500),
+		.end	= evt2irq(0x2580),
 		.flags	= IORESOURCE_IRQ,
 	},
 	{
 		/* IRQ for channel 23 */
-		.start	= 288,
-		.end	= 288,
+		.start	= evt2irq(0x2600),
+		.end	= evt2irq(0x2600),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -668,7 +668,7 @@
 		.flags	= IORESOURCE_MEM | IORESOURCE_MEM_8BIT,
 	},
 	{
-		.start	= 54,
+		.start	= evt2irq(0x8c0),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -687,7 +687,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	{
-		.start	= 220,
+		.start	= evt2irq(0x1d80),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -706,8 +706,8 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 57,
-		.end	= 57,
+		.start	= evt2irq(0x920),
+		.end	= evt2irq(0x920),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -730,8 +730,8 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 57,
-		.end	= 57,
+		.start	= evt2irq(0x920),
+		.end	= evt2irq(0x920),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7763.c b/arch/sh/kernel/cpu/sh4a/setup-sh7763.c
index 0011351..bd0a8fb 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7763.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7763.c
@@ -13,6 +13,7 @@
 #include <linux/init.h>
 #include <linux/serial.h>
 #include <linux/sh_timer.h>
+#include <linux/sh_intc.h>
 #include <linux/io.h>
 #include <linux/serial_sci.h>
 
@@ -22,7 +23,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCIF,
-	.irqs		= { 40, 40, 40, 40 },
+	.irqs		= SCIx_IRQ_MUXED(evt2irq(0x700)),
 	.regtype	= SCIx_SH4_SCIF_FIFODATA_REGTYPE,
 };
 
@@ -40,7 +41,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCIF,
-	.irqs		= { 76, 76, 76, 76 },
+	.irqs		= SCIx_IRQ_MUXED(evt2irq(0xb80)),
 	.regtype	= SCIx_SH4_SCIF_FIFODATA_REGTYPE,
 };
 
@@ -58,7 +59,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCIF,
-	.irqs		= { 104, 104, 104, 104 },
+	.irqs		= SCIx_IRQ_MUXED(evt2irq(0xf00)),
 	.regtype	= SCIx_SH4_SCIF_FIFODATA_REGTYPE,
 };
 
@@ -78,7 +79,7 @@
 	},
 	[1] = {
 		/* Shared Period/Carry/Alarm IRQ */
-		.start  = 20,
+		.start  = evt2irq(0x480),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -97,13 +98,14 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 83,
-		.end	= 83,
+		.start	= evt2irq(0xc60),
+		.end	= evt2irq(0xc60),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
 
 static u64 usb_ohci_dma_mask = 0xffffffffUL;
+
 static struct platform_device usb_ohci_device = {
 	.name		= "sh_ohci",
 	.id		= -1,
@@ -122,8 +124,8 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 84,
-		.end	= 84,
+		.start	= evt2irq(0xc80),
+		.end	= evt2irq(0xc80),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -152,7 +154,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 28,
+		.start	= evt2irq(0x580),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -180,7 +182,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 29,
+		.start	= evt2irq(0x5a0),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -207,7 +209,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 30,
+		.start	= evt2irq(0x5c0),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -234,7 +236,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 96,
+		.start	= evt2irq(0xe00),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -261,7 +263,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 97,
+		.start	= evt2irq(0xe20),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -288,7 +290,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 98,
+		.start	= evt2irq(0xe40),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7770.c b/arch/sh/kernel/cpu/sh4a/setup-sh7770.c
index 2c6aa22..256ea7a 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7770.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7770.c
@@ -12,6 +12,7 @@
 #include <linux/serial.h>
 #include <linux/serial_sci.h>
 #include <linux/sh_timer.h>
+#include <linux/sh_intc.h>
 #include <linux/io.h>
 
 static struct plat_sci_port scif0_platform_data = {
@@ -20,7 +21,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE | SCSCR_TOIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCIF,
-	.irqs		= { 61, 61, 61, 61 },
+	.irqs		= SCIx_IRQ_MUXED(evt2irq(0x9a0)),
 };
 
 static struct platform_device scif0_device = {
@@ -37,7 +38,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE | SCSCR_TOIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCIF,
-	.irqs		= { 62, 62, 62, 62 },
+	.irqs		= SCIx_IRQ_MUXED(evt2irq(0x9c0)),
 };
 
 static struct platform_device scif1_device = {
@@ -54,7 +55,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE | SCSCR_TOIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCIF,
-	.irqs		= { 63, 63, 63, 63 },
+	.irqs		= SCIx_IRQ_MUXED(evt2irq(0x9e0)),
 };
 
 static struct platform_device scif2_device = {
@@ -71,7 +72,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE | SCSCR_TOIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCIF,
-	.irqs		= { 64, 64, 64, 64 },
+	.irqs		= SCIx_IRQ_MUXED(evt2irq(0xa00)),
 };
 
 static struct platform_device scif3_device = {
@@ -88,7 +89,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE | SCSCR_TOIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCIF,
-	.irqs		= { 65, 65, 65, 65 },
+	.irqs		= SCIx_IRQ_MUXED(evt2irq(0xa20)),
 };
 
 static struct platform_device scif4_device = {
@@ -105,7 +106,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE | SCSCR_TOIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCIF,
-	.irqs		= { 66, 66, 66, 66 },
+	.irqs		= SCIx_IRQ_MUXED(evt2irq(0xa40)),
 };
 
 static struct platform_device scif5_device = {
@@ -122,7 +123,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE | SCSCR_TOIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCIF,
-	.irqs		= { 67, 67, 67, 67 },
+	.irqs		= SCIx_IRQ_MUXED(evt2irq(0xa60)),
 };
 
 static struct platform_device scif6_device = {
@@ -139,7 +140,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE | SCSCR_TOIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCIF,
-	.irqs		= { 68, 68, 68, 68 },
+	.irqs		= SCIx_IRQ_MUXED(evt2irq(0xa80)),
 };
 
 static struct platform_device scif7_device = {
@@ -156,7 +157,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE | SCSCR_TOIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCIF,
-	.irqs		= { 69, 69, 69, 69 },
+	.irqs		= SCIx_IRQ_MUXED(evt2irq(0xaa0)),
 };
 
 static struct platform_device scif8_device = {
@@ -173,7 +174,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE | SCSCR_TOIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCIF,
-	.irqs		= { 70, 70, 70, 70 },
+	.irqs		= SCIx_IRQ_MUXED(evt2irq(0xac0)),
 };
 
 static struct platform_device scif9_device = {
@@ -197,7 +198,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 16,
+		.start	= evt2irq(0x400),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -225,7 +226,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 17,
+		.start	= evt2irq(0x420),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -252,7 +253,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 18,
+		.start	= evt2irq(0x440),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -279,7 +280,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 19,
+		.start	= evt2irq(0x460),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -306,7 +307,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 20,
+		.start	= evt2irq(0x480),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -333,7 +334,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 21,
+		.start	= evt2irq(0x4a0),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -360,7 +361,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 22,
+		.start	= evt2irq(0x4c0),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -387,7 +388,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 23,
+		.start	= evt2irq(0x4e0),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -414,7 +415,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 24,
+		.start	= evt2irq(0x500),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7780.c b/arch/sh/kernel/cpu/sh4a/setup-sh7780.c
index d431b00..de45b704 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7780.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7780.c
@@ -14,6 +14,7 @@
 #include <linux/serial_sci.h>
 #include <linux/sh_dma.h>
 #include <linux/sh_timer.h>
+#include <linux/sh_intc.h>
 #include <cpu/dma-register.h>
 
 static struct plat_sci_port scif0_platform_data = {
@@ -22,7 +23,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE | SCSCR_CKE1,
 	.scbrr_algo_id	= SCBRR_ALGO_1,
 	.type		= PORT_SCIF,
-	.irqs		= { 40, 40, 40, 40 },
+	.irqs		= SCIx_IRQ_MUXED(evt2irq(0x700)),
 	.regtype	= SCIx_SH4_SCIF_FIFODATA_REGTYPE,
 };
 
@@ -40,7 +41,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE | SCSCR_CKE1,
 	.scbrr_algo_id	= SCBRR_ALGO_1,
 	.type		= PORT_SCIF,
-	.irqs		= { 76, 76, 76, 76 },
+	.irqs		= SCIx_IRQ_MUXED(evt2irq(0xb80)),
 	.regtype	= SCIx_SH4_SCIF_FIFODATA_REGTYPE,
 };
 
@@ -65,7 +66,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 28,
+		.start	= evt2irq(0x580),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -93,7 +94,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 29,
+		.start	= evt2irq(0x5a0),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -120,7 +121,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 30,
+		.start	= evt2irq(0x5c0),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -147,7 +148,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 96,
+		.start	= evt2irq(0xe00),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -174,7 +175,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 97,
+		.start	= evt2irq(0xe20),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -201,7 +202,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 98,
+		.start	= evt2irq(0xe40),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -224,7 +225,7 @@
 	},
 	[1] = {
 		/* Shared Period/Carry/Alarm IRQ */
-		.start	= 20,
+		.start	= evt2irq(0x480),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -321,10 +322,13 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	{
-		/* Real DMA error IRQ is 38, and channel IRQs are 34-37, 44-45 */
+		/*
+		 * Real DMA error vector is 0x6c0, and channel
+		 * vectors are 0x640-0x6a0, 0x780-0x7a0
+		 */
 		.name	= "error_irq",
-		.start	= 34,
-		.end	= 34,
+		.start	= evt2irq(0x640),
+		.end	= evt2irq(0x640),
 		.flags	= IORESOURCE_IRQ | IORESOURCE_IRQ_SHAREABLE,
 	},
 };
@@ -338,10 +342,13 @@
 	},
 	/* DMAC1 has no DMARS */
 	{
-		/* Real DMA error IRQ is 38, and channel IRQs are 46-47, 92-95 */
+		/*
+		 * Real DMA error vector is 0x6c0, and channel
+		 * vectors are 0x7c0-0x7e0, 0xd80-0xde0
+		 */
 		.name	= "error_irq",
-		.start	= 46,
-		.end	= 46,
+		.start	= evt2irq(0x7c0),
+		.end	= evt2irq(0x7c0),
 		.flags	= IORESOURCE_IRQ | IORESOURCE_IRQ_SHAREABLE,
 	},
 };
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7785.c b/arch/sh/kernel/cpu/sh4a/setup-sh7785.c
index 81588ef..0968ecb 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7785.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7785.c
@@ -15,6 +15,7 @@
 #include <linux/mm.h>
 #include <linux/sh_dma.h>
 #include <linux/sh_timer.h>
+#include <linux/sh_intc.h>
 #include <asm/mmzone.h>
 #include <cpu/dma-register.h>
 
@@ -24,7 +25,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE | SCSCR_CKE1,
 	.scbrr_algo_id	= SCBRR_ALGO_1,
 	.type		= PORT_SCIF,
-	.irqs		= { 40, 40, 40, 40 },
+	.irqs		= SCIx_IRQ_MUXED(evt2irq(0x700)),
 	.regtype	= SCIx_SH4_SCIF_FIFODATA_REGTYPE,
 };
 
@@ -42,7 +43,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE | SCSCR_CKE1,
 	.scbrr_algo_id	= SCBRR_ALGO_1,
 	.type		= PORT_SCIF,
-	.irqs		= { 44, 44, 44, 44 },
+	.irqs		= SCIx_IRQ_MUXED(evt2irq(0x780)),
 	.regtype	= SCIx_SH4_SCIF_FIFODATA_REGTYPE,
 };
 
@@ -60,7 +61,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE | SCSCR_CKE1,
 	.scbrr_algo_id	= SCBRR_ALGO_1,
 	.type		= PORT_SCIF,
-	.irqs		= { 60, 60, 60, 60 },
+	.irqs		= SCIx_IRQ_MUXED(evt2irq(0x980)),
 	.regtype	= SCIx_SH4_SCIF_FIFODATA_REGTYPE,
 };
 
@@ -78,7 +79,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE | SCSCR_CKE1,
 	.scbrr_algo_id	= SCBRR_ALGO_1,
 	.type		= PORT_SCIF,
-	.irqs		= { 61, 61, 61, 61 },
+	.irqs		= SCIx_IRQ_MUXED(evt2irq(0x9a0)),
 	.regtype	= SCIx_SH4_SCIF_FIFODATA_REGTYPE,
 };
 
@@ -96,7 +97,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE | SCSCR_CKE1,
 	.scbrr_algo_id	= SCBRR_ALGO_1,
 	.type		= PORT_SCIF,
-	.irqs		= { 62, 62, 62, 62 },
+	.irqs		= SCIx_IRQ_MUXED(evt2irq(0x9c0)),
 	.regtype	= SCIx_SH4_SCIF_FIFODATA_REGTYPE,
 };
 
@@ -114,7 +115,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE | SCSCR_CKE1,
 	.scbrr_algo_id	= SCBRR_ALGO_1,
 	.type		= PORT_SCIF,
-	.irqs		= { 63, 63, 63, 63 },
+	.irqs		= SCIx_IRQ_MUXED(evt2irq(0x9e0)),
 	.regtype	= SCIx_SH4_SCIF_FIFODATA_REGTYPE,
 };
 
@@ -139,7 +140,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 28,
+		.start	= evt2irq(0x580),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -167,7 +168,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 29,
+		.start	= evt2irq(0x5a0),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -194,7 +195,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 30,
+		.start	= evt2irq(0x5c0),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -221,7 +222,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 96,
+		.start	= evt2irq(0xe00),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -248,7 +249,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 97,
+		.start	= evt2irq(0xe20),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -275,7 +276,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 98,
+		.start	= evt2irq(0xe40),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -375,10 +376,13 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	{
-		/* Real DMA error IRQ is 39, and channel IRQs are 33-38 */
+		/*
+		 * Real DMA error vector is 0x6e0, and channel
+		 * vectors are 0x620-0x6c0
+		 */
 		.name	= "error_irq",
-		.start	= 33,
-		.end	= 33,
+		.start	= evt2irq(0x620),
+		.end	= evt2irq(0x620),
 		.flags	= IORESOURCE_IRQ | IORESOURCE_IRQ_SHAREABLE,
 	},
 };
@@ -392,10 +396,13 @@
 	},
 	/* DMAC1 has no DMARS */
 	{
-		/* Real DMA error IRQ is 58, and channel IRQs are 52-57 */
+		/*
+		 * Real DMA error vector is 0x940, and channel
+		 * vectors are 0x880-0x920
+		 */
 		.name	= "error_irq",
-		.start	= 52,
-		.end	= 52,
+		.start	= evt2irq(0x880),
+		.end	= evt2irq(0x880),
 		.flags	= IORESOURCE_IRQ | IORESOURCE_IRQ_SHAREABLE,
 	},
 };
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7786.c b/arch/sh/kernel/cpu/sh4a/setup-sh7786.c
index 599022d..2e6952f 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7786.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7786.c
@@ -32,7 +32,10 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE | SCSCR_CKE1,
 	.scbrr_algo_id	= SCBRR_ALGO_1,
 	.type		= PORT_SCIF,
-	.irqs		= { 40, 41, 43, 42 },
+	.irqs		= { evt2irq(0x700),
+			    evt2irq(0x720),
+			    evt2irq(0x760),
+			    evt2irq(0x740) },
 	.regtype	= SCIx_SH4_SCIF_FIFODATA_REGTYPE,
 };
 
@@ -53,7 +56,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE | SCSCR_CKE1,
 	.scbrr_algo_id	= SCBRR_ALGO_1,
 	.type		= PORT_SCIF,
-	.irqs		= { 44, 44, 44, 44 },
+	.irqs		= SCIx_IRQ_MUXED(evt2irq(0x780)),
 	.regtype	= SCIx_SH4_SCIF_FIFODATA_REGTYPE,
 };
 
@@ -71,7 +74,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE | SCSCR_CKE1,
 	.scbrr_algo_id	= SCBRR_ALGO_1,
 	.type		= PORT_SCIF,
-	.irqs		= { 50, 50, 50, 50 },
+	.irqs		= SCIx_IRQ_MUXED(evt2irq(0x840)),
 	.regtype	= SCIx_SH4_SCIF_FIFODATA_REGTYPE,
 };
 
@@ -89,7 +92,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE | SCSCR_CKE1,
 	.scbrr_algo_id	= SCBRR_ALGO_1,
 	.type		= PORT_SCIF,
-	.irqs		= { 51, 51, 51, 51 },
+	.irqs		= SCIx_IRQ_MUXED(evt2irq(0x860)),
 	.regtype	= SCIx_SH4_SCIF_FIFODATA_REGTYPE,
 };
 
@@ -107,7 +110,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE | SCSCR_CKE1,
 	.scbrr_algo_id	= SCBRR_ALGO_1,
 	.type		= PORT_SCIF,
-	.irqs		= { 52, 52, 52, 52 },
+	.irqs		= SCIx_IRQ_MUXED(evt2irq(0x880)),
 	.regtype	= SCIx_SH4_SCIF_FIFODATA_REGTYPE,
 };
 
@@ -125,7 +128,7 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE | SCSCR_CKE1,
 	.scbrr_algo_id	= SCBRR_ALGO_1,
 	.type		= PORT_SCIF,
-	.irqs		= { 53, 53, 53, 53 },
+	.irqs		= SCIx_IRQ_MUXED(evt2irq(0x8a0)),
 	.regtype	= SCIx_SH4_SCIF_FIFODATA_REGTYPE,
 };
 
@@ -150,7 +153,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 16,
+		.start	= evt2irq(0x400),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -178,7 +181,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 17,
+		.start	= evt2irq(0x420),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -205,7 +208,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 18,
+		.start	= evt2irq(0x440),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -232,7 +235,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 20,
+		.start	= evt2irq(0x480),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -259,7 +262,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 21,
+		.start	= evt2irq(0x4a0),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -286,7 +289,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 22,
+		.start	= evt2irq(0x4c0),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -313,7 +316,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 45,
+		.start	= evt2irq(0x7a0),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -340,7 +343,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 45,
+		.start	= evt2irq(0x7a0),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -367,7 +370,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 45,
+		.start	= evt2irq(0x7a0),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -394,7 +397,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 46,
+		.start	= evt2irq(0x7c0),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -421,7 +424,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 46,
+		.start	= evt2irq(0x7c0),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -448,7 +451,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 46,
+		.start	= evt2irq(0x7c0),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -550,8 +553,8 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 77,
-		.end	= 77,
+		.start	= evt2irq(0xba0),
+		.end	= evt2irq(0xba0),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -574,8 +577,8 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 77,
-		.end	= 77,
+		.start	= evt2irq(0xba0),
+		.end	= evt2irq(0xba0),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
diff --git a/arch/sh/kernel/cpu/sh4a/setup-shx3.c b/arch/sh/kernel/cpu/sh4a/setup-shx3.c
index bb20880..688f7ed 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-shx3.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-shx3.c
@@ -14,6 +14,7 @@
 #include <linux/io.h>
 #include <linux/gpio.h>
 #include <linux/sh_timer.h>
+#include <linux/sh_intc.h>
 #include <cpu/shx3.h>
 #include <asm/mmzone.h>
 
@@ -32,7 +33,10 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCIF,
-	.irqs		= { 40, 41, 43, 42 },
+	.irqs		= { evt2irq(0x700),
+			    evt2irq(0x720),
+			    evt2irq(0x760),
+			    evt2irq(0x740) },
 };
 
 static struct platform_device scif0_device = {
@@ -49,7 +53,10 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCIF,
-	.irqs		= { 44, 45, 47, 46 },
+	.irqs		= { evt2irq(0x780),
+			    evt2irq(0x7a0),
+			    evt2irq(0x7e0),
+			    evt2irq(0x7c0) },
 };
 
 static struct platform_device scif1_device = {
@@ -66,7 +73,10 @@
 	.scscr		= SCSCR_RE | SCSCR_TE | SCSCR_REIE,
 	.scbrr_algo_id	= SCBRR_ALGO_2,
 	.type		= PORT_SCIF,
-	.irqs		= { 52, 53, 55, 54 },
+	.irqs		= { evt2irq(0x880),
+			    evt2irq(0x8a0),
+			    evt2irq(0x8e0),
+			    evt2irq(0x8c0) },
 };
 
 static struct platform_device scif2_device = {
@@ -90,7 +100,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 16,
+		.start	= evt2irq(0x400),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -118,7 +128,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 17,
+		.start	= evt2irq(0x420),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -145,7 +155,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 18,
+		.start	= evt2irq(0x440),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -172,7 +182,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 19,
+		.start	= evt2irq(0x460),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -199,7 +209,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 20,
+		.start	= evt2irq(0x480),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -226,7 +236,7 @@
 		.flags	= IORESOURCE_MEM,
 	},
 	[1] = {
-		.start	= 21,
+		.start	= evt2irq(0x4a0),
 		.flags	= IORESOURCE_IRQ,
 	},
 };
@@ -484,9 +494,6 @@
 
 void __init plat_irq_setup(void)
 {
-	reserve_intc_vectors(vectors_irq, ARRAY_SIZE(vectors_irq));
-	reserve_intc_vectors(vectors_irl, ARRAY_SIZE(vectors_irl));
-
 	register_intc_controller(&intc_desc);
 }
 
diff --git a/arch/sh/kernel/cpu/sh5/entry.S b/arch/sh/kernel/cpu/sh5/entry.S
index 6b80295..ff1f0e6 100644
--- a/arch/sh/kernel/cpu/sh5/entry.S
+++ b/arch/sh/kernel/cpu/sh5/entry.S
@@ -335,7 +335,7 @@
 	/* If the fast path handler fixed the fault, just drop through quickly
 	   to the restore code right away to return to the excepting context.
 	   */
-	beqi/u	r2, 0, tr1
+	bnei/u	r2, 0, tr1
 
 fast_tlb_miss_restore:
 	ld.q	SP, SAVED_TR0, r2
@@ -1079,9 +1079,8 @@
  *
  * Kernel TLB fault handlers will get a slightly different interface.
  * (r2)   struct pt_regs *, original register's frame pointer
- * (r3)   writeaccess, whether it's a store fault as opposed to load fault
- * (r4)   execaccess, whether it's a ITLB fault as opposed to DTLB fault
- * (r5)   Effective Address of fault
+ * (r3)   page fault error code (see asm/thread_info.h)
+ * (r4)   Effective Address of fault
  * (LINK) return address
  * (SP)   = r2
  *
@@ -1092,26 +1091,25 @@
 tlb_miss_load:
 	or	SP, ZERO, r2
 	or	ZERO, ZERO, r3		/* Read */
-	or	ZERO, ZERO, r4		/* Data */
-	getcon	TEA, r5
+	getcon	TEA, r4
 	pta	call_do_page_fault, tr0
 	beq	ZERO, ZERO, tr0
 
 tlb_miss_store:
 	or	SP, ZERO, r2
-	movi	1, r3			/* Write */
-	or	ZERO, ZERO, r4		/* Data */
-	getcon	TEA, r5
+	movi	FAULT_CODE_WRITE, r3		/* Write */
+	getcon	TEA, r4
 	pta	call_do_page_fault, tr0
 	beq	ZERO, ZERO, tr0
 
 itlb_miss_or_IRQ:
 	pta	its_IRQ, tr0
 	beqi/u	r4, EVENT_INTERRUPT, tr0
+
+	/* ITLB miss */
 	or	SP, ZERO, r2
-	or	ZERO, ZERO, r3		/* Read */
-	movi	1, r4			/* Text */
-	getcon	TEA, r5
+	movi	FAULT_CODE_ITLB, r3
+	getcon	TEA, r4
 	/* Fall through */
 
 call_do_page_fault:
diff --git a/arch/sh/kernel/cpu/sh5/fpu.c b/arch/sh/kernel/cpu/sh5/fpu.c
index 4b3bb35..9f8713a 100644
--- a/arch/sh/kernel/cpu/sh5/fpu.c
+++ b/arch/sh/kernel/cpu/sh5/fpu.c
@@ -107,8 +107,5 @@
 
 	regs->pc += 4;
 
-	tsk->thread.trap_no = 11;
-	tsk->thread.error_code = 0;
-
 	force_sig(SIGFPE, tsk);
 }
diff --git a/arch/sh/kernel/irq.c b/arch/sh/kernel/irq.c
index a3ee919..dadce73 100644
--- a/arch/sh/kernel/irq.c
+++ b/arch/sh/kernel/irq.c
@@ -234,8 +234,10 @@
 #ifdef CONFIG_SPARSE_IRQ
 int __init arch_probe_nr_irqs(void)
 {
-	nr_irqs = sh_mv.mv_nr_irqs;
-	return NR_IRQS_LEGACY;
+	/*
+	 * No pre-allocated IRQs.
+	 */
+	return 0;
 }
 #endif
 
diff --git a/arch/sh/kernel/kgdb.c b/arch/sh/kernel/kgdb.c
index b117781..38b3139 100644
--- a/arch/sh/kernel/kgdb.c
+++ b/arch/sh/kernel/kgdb.c
@@ -1,7 +1,7 @@
 /*
  * SuperH KGDB support
  *
- * Copyright (C) 2008 - 2009  Paul Mundt
+ * Copyright (C) 2008 - 2012  Paul Mundt
  *
  * Single stepping taken from the old stub by Henry Bell and Jeremy Siegel.
  *
@@ -164,42 +164,89 @@
 	stepped_opcode = 0;
 }
 
-void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] = {
+	{ "r0",		GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[0]) },
+	{ "r1",		GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[1]) },
+	{ "r2",		GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[2]) },
+	{ "r3",		GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[3]) },
+	{ "r4",		GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[4]) },
+	{ "r5",		GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[5]) },
+	{ "r6",		GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[6]) },
+	{ "r7",		GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[7]) },
+	{ "r8",		GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[8]) },
+	{ "r9",		GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[9]) },
+	{ "r10",	GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[10]) },
+	{ "r11",	GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[11]) },
+	{ "r12",	GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[12]) },
+	{ "r13",	GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[13]) },
+	{ "r14",	GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[14]) },
+	{ "r15",	GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[15]) },
+	{ "pc",		GDB_SIZEOF_REG, offsetof(struct pt_regs, pc) },
+	{ "pr",		GDB_SIZEOF_REG, offsetof(struct pt_regs, pr) },
+	{ "sr",		GDB_SIZEOF_REG, offsetof(struct pt_regs, sr) },
+	{ "gbr",	GDB_SIZEOF_REG, offsetof(struct pt_regs, gbr) },
+	{ "mach",	GDB_SIZEOF_REG, offsetof(struct pt_regs, mach) },
+	{ "macl",	GDB_SIZEOF_REG, offsetof(struct pt_regs, macl) },
+	{ "vbr",	GDB_SIZEOF_REG, -1 },
+};
+
+int dbg_set_reg(int regno, void *mem, struct pt_regs *regs)
 {
-	int i;
+	if (regno < 0 || regno >= DBG_MAX_REG_NUM)
+		return -EINVAL;
 
-	for (i = 0; i < 16; i++)
-		gdb_regs[GDB_R0 + i] = regs->regs[i];
+	if (dbg_reg_def[regno].offset != -1)
+		memcpy((void *)regs + dbg_reg_def[regno].offset, mem,
+		       dbg_reg_def[regno].size);
 
-	gdb_regs[GDB_PC] = regs->pc;
-	gdb_regs[GDB_PR] = regs->pr;
-	gdb_regs[GDB_SR] = regs->sr;
-	gdb_regs[GDB_GBR] = regs->gbr;
-	gdb_regs[GDB_MACH] = regs->mach;
-	gdb_regs[GDB_MACL] = regs->macl;
-
-	__asm__ __volatile__ ("stc vbr, %0" : "=r" (gdb_regs[GDB_VBR]));
+	return 0;
 }
 
-void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+char *dbg_get_reg(int regno, void *mem, struct pt_regs *regs)
 {
-	int i;
+	if (regno >= DBG_MAX_REG_NUM || regno < 0)
+		return NULL;
 
-	for (i = 0; i < 16; i++)
-		regs->regs[GDB_R0 + i] = gdb_regs[GDB_R0 + i];
+	if (dbg_reg_def[regno].size != -1)
+		memcpy(mem, (void *)regs + dbg_reg_def[regno].offset,
+		       dbg_reg_def[regno].size);
 
-	regs->pc = gdb_regs[GDB_PC];
-	regs->pr = gdb_regs[GDB_PR];
-	regs->sr = gdb_regs[GDB_SR];
-	regs->gbr = gdb_regs[GDB_GBR];
-	regs->mach = gdb_regs[GDB_MACH];
-	regs->macl = gdb_regs[GDB_MACL];
+	switch (regno) {
+	case GDB_VBR:
+		__asm__ __volatile__ ("stc vbr, %0" : "=r" (mem));
+		break;
+	}
+
+	return dbg_reg_def[regno].name;
 }
 
 void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p)
 {
+	struct pt_regs *thread_regs = task_pt_regs(p);
+	int reg;
+
+	/* Initialize to zero */
+	for (reg = 0; reg < DBG_MAX_REG_NUM; reg++)
+		gdb_regs[reg] = 0;
+
+	/*
+	 * Copy out GP regs 8 to 14.
+	 *
+	 * switch_to() relies on SR.RB toggling, so regs 0->7 are banked
+	 * and need privileged instructions to get to. The r15 value we
+	 * fetch from the thread info directly.
+	 */
+	for (reg = GDB_R8; reg < GDB_R15; reg++)
+		gdb_regs[reg] = thread_regs->regs[reg];
+
 	gdb_regs[GDB_R15] = p->thread.sp;
 	gdb_regs[GDB_PC] = p->thread.pc;
+
+	/*
+	 * Additional registers we have context for
+	 */
+	gdb_regs[GDB_PR] = thread_regs->pr;
+	gdb_regs[GDB_GBR] = thread_regs->gbr;
 }
 
 int kgdb_arch_handle_exception(int e_vector, int signo, int err_code,
@@ -264,6 +311,18 @@
 	local_irq_restore(flags);
 }
 
+static void kgdb_call_nmi_hook(void *ignored)
+{
+	kgdb_nmicallback(raw_smp_processor_id(), get_irq_regs());
+}
+
+void kgdb_roundup_cpus(unsigned long flags)
+{
+	local_irq_enable();
+	smp_call_function(kgdb_call_nmi_hook, NULL, 0);
+	local_irq_disable();
+}
+
 static int __kgdb_notify(struct die_args *args, unsigned long cmd)
 {
 	int ret;
diff --git a/arch/sh/kernel/machvec.c b/arch/sh/kernel/machvec.c
index 3d722e4..ec05f49 100644
--- a/arch/sh/kernel/machvec.c
+++ b/arch/sh/kernel/machvec.c
@@ -121,7 +121,4 @@
 	mv_set(irq_demux);
 	mv_set(mode_pins);
 	mv_set(mem_init);
-
-	if (!sh_mv.mv_nr_irqs)
-		sh_mv.mv_nr_irqs = NR_IRQS;
 }
diff --git a/arch/sh/kernel/process.c b/arch/sh/kernel/process.c
index f2621ab..9b7a459 100644
--- a/arch/sh/kernel/process.c
+++ b/arch/sh/kernel/process.c
@@ -2,12 +2,26 @@
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/sched.h>
+#include <linux/export.h>
+#include <linux/stackprotector.h>
 
 struct kmem_cache *task_xstate_cachep = NULL;
 unsigned int xstate_size;
 
+#ifdef CONFIG_CC_STACKPROTECTOR
+unsigned long __stack_chk_guard __read_mostly;
+EXPORT_SYMBOL(__stack_chk_guard);
+#endif
+
+/*
+ * this gets called so that we can store lazy state into memory and copy the
+ * current task into the new thread.
+ */
 int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)
 {
+#ifdef CONFIG_SUPERH32
+	unlazy_fpu(src, task_pt_regs(src));
+#endif
 	*dst = *src;
 
 	if (src->thread.xstate) {
diff --git a/arch/sh/kernel/process_32.c b/arch/sh/kernel/process_32.c
index 94273aa..59521e8 100644
--- a/arch/sh/kernel/process_32.c
+++ b/arch/sh/kernel/process_32.c
@@ -22,6 +22,7 @@
 #include <linux/ftrace.h>
 #include <linux/hw_breakpoint.h>
 #include <linux/prefetch.h>
+#include <linux/stackprotector.h>
 #include <asm/uaccess.h>
 #include <asm/mmu_context.h>
 #include <asm/fpu.h>
@@ -155,15 +156,6 @@
 }
 EXPORT_SYMBOL(dump_fpu);
 
-/*
- * This gets called before we allocate a new thread and copy
- * the current task into it.
- */
-void prepare_to_copy(struct task_struct *tsk)
-{
-	unlazy_fpu(tsk, task_pt_regs(tsk));
-}
-
 asmlinkage void ret_from_fork(void);
 
 int copy_thread(unsigned long clone_flags, unsigned long usp,
@@ -220,6 +212,10 @@
 {
 	struct thread_struct *next_t = &next->thread;
 
+#if defined(CONFIG_CC_STACKPROTECTOR) && !defined(CONFIG_SMP)
+	__stack_chk_guard = next->stack_canary;
+#endif
+
 	unlazy_fpu(prev, task_pt_regs(prev));
 
 	/* we're going to use this soon, after a few expensive things */
diff --git a/arch/sh/kernel/traps_64.c b/arch/sh/kernel/traps_64.c
index 6c04860..8dae93e 100644
--- a/arch/sh/kernel/traps_64.c
+++ b/arch/sh/kernel/traps_64.c
@@ -283,8 +283,6 @@
 		unsigned long error_code, struct pt_regs *regs, struct task_struct *tsk)
 {
 	show_excp_regs(fn_name, trapnr, signr, regs);
-	tsk->thread.error_code = error_code;
-	tsk->thread.trap_no = trapnr;
 
 	if (user_mode(regs))
 		force_sig(signr, tsk);
diff --git a/arch/sh/mm/Makefile b/arch/sh/mm/Makefile
index 2228c8c..cee6b99 100644
--- a/arch/sh/mm/Makefile
+++ b/arch/sh/mm/Makefile
@@ -15,8 +15,8 @@
 obj-y			+= $(cacheops-y)
 
 mmu-y			:= nommu.o extable_32.o
-mmu-$(CONFIG_MMU)	:= extable_$(BITS).o fault_$(BITS).o gup.o \
-			   ioremap.o kmap.o pgtable.o tlbflush_$(BITS).o
+mmu-$(CONFIG_MMU)	:= extable_$(BITS).o fault.o gup.o ioremap.o kmap.o \
+			   pgtable.o tlbex_$(BITS).o tlbflush_$(BITS).o
 
 obj-y			+= $(mmu-y)
 
@@ -44,7 +44,7 @@
 
 GCOV_PROFILE_pmb.o := n
 
-# Special flags for fault_64.o.  This puts restrictions on the number of
+# Special flags for tlbex_64.o.  This puts restrictions on the number of
 # caller-save registers that the compiler can target when building this file.
 # This is required because the code is called from a context in entry.S where
 # very few registers have been saved in the exception handler (for speed
@@ -59,7 +59,7 @@
 # The resources not listed below are callee save, i.e. the compiler is free to
 # use any of them and will spill them to the stack itself.
 
-CFLAGS_fault_64.o += -ffixed-r7 \
+CFLAGS_tlbex_64.o += -ffixed-r7 \
 	-ffixed-r8 -ffixed-r9 -ffixed-r10 -ffixed-r11 -ffixed-r12 \
 	-ffixed-r13 -ffixed-r14 -ffixed-r16 -ffixed-r17 -ffixed-r19 \
 	-ffixed-r20 -ffixed-r21 -ffixed-r22 -ffixed-r23 \
diff --git a/arch/sh/mm/fault.c b/arch/sh/mm/fault.c
new file mode 100644
index 0000000..1fc25d8
--- /dev/null
+++ b/arch/sh/mm/fault.c
@@ -0,0 +1,514 @@
+/*
+ * Page fault handler for SH with an MMU.
+ *
+ *  Copyright (C) 1999  Niibe Yutaka
+ *  Copyright (C) 2003 - 2012  Paul Mundt
+ *
+ *  Based on linux/arch/i386/mm/fault.c:
+ *   Copyright (C) 1995  Linus Torvalds
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/hardirq.h>
+#include <linux/kprobes.h>
+#include <linux/perf_event.h>
+#include <linux/kdebug.h>
+#include <asm/io_trapped.h>
+#include <asm/mmu_context.h>
+#include <asm/tlbflush.h>
+#include <asm/traps.h>
+
+static inline int notify_page_fault(struct pt_regs *regs, int trap)
+{
+	int ret = 0;
+
+	if (kprobes_built_in() && !user_mode(regs)) {
+		preempt_disable();
+		if (kprobe_running() && kprobe_fault_handler(regs, trap))
+			ret = 1;
+		preempt_enable();
+	}
+
+	return ret;
+}
+
+static void
+force_sig_info_fault(int si_signo, int si_code, unsigned long address,
+		     struct task_struct *tsk)
+{
+	siginfo_t info;
+
+	info.si_signo	= si_signo;
+	info.si_errno	= 0;
+	info.si_code	= si_code;
+	info.si_addr	= (void __user *)address;
+
+	force_sig_info(si_signo, &info, tsk);
+}
+
+/*
+ * This is useful to dump out the page tables associated with
+ * 'addr' in mm 'mm'.
+ */
+static void show_pte(struct mm_struct *mm, unsigned long addr)
+{
+	pgd_t *pgd;
+
+	if (mm)
+		pgd = mm->pgd;
+	else
+		pgd = get_TTB();
+
+	printk(KERN_ALERT "pgd = %p\n", pgd);
+	pgd += pgd_index(addr);
+	printk(KERN_ALERT "[%08lx] *pgd=%0*Lx", addr,
+	       (u32)(sizeof(*pgd) * 2), (u64)pgd_val(*pgd));
+
+	do {
+		pud_t *pud;
+		pmd_t *pmd;
+		pte_t *pte;
+
+		if (pgd_none(*pgd))
+			break;
+
+		if (pgd_bad(*pgd)) {
+			printk("(bad)");
+			break;
+		}
+
+		pud = pud_offset(pgd, addr);
+		if (PTRS_PER_PUD != 1)
+			printk(", *pud=%0*Lx", (u32)(sizeof(*pud) * 2),
+			       (u64)pud_val(*pud));
+
+		if (pud_none(*pud))
+			break;
+
+		if (pud_bad(*pud)) {
+			printk("(bad)");
+			break;
+		}
+
+		pmd = pmd_offset(pud, addr);
+		if (PTRS_PER_PMD != 1)
+			printk(", *pmd=%0*Lx", (u32)(sizeof(*pmd) * 2),
+			       (u64)pmd_val(*pmd));
+
+		if (pmd_none(*pmd))
+			break;
+
+		if (pmd_bad(*pmd)) {
+			printk("(bad)");
+			break;
+		}
+
+		/* We must not map this if we have highmem enabled */
+		if (PageHighMem(pfn_to_page(pmd_val(*pmd) >> PAGE_SHIFT)))
+			break;
+
+		pte = pte_offset_kernel(pmd, addr);
+		printk(", *pte=%0*Lx", (u32)(sizeof(*pte) * 2),
+		       (u64)pte_val(*pte));
+	} while (0);
+
+	printk("\n");
+}
+
+static inline pmd_t *vmalloc_sync_one(pgd_t *pgd, unsigned long address)
+{
+	unsigned index = pgd_index(address);
+	pgd_t *pgd_k;
+	pud_t *pud, *pud_k;
+	pmd_t *pmd, *pmd_k;
+
+	pgd += index;
+	pgd_k = init_mm.pgd + index;
+
+	if (!pgd_present(*pgd_k))
+		return NULL;
+
+	pud = pud_offset(pgd, address);
+	pud_k = pud_offset(pgd_k, address);
+	if (!pud_present(*pud_k))
+		return NULL;
+
+	if (!pud_present(*pud))
+	    set_pud(pud, *pud_k);
+
+	pmd = pmd_offset(pud, address);
+	pmd_k = pmd_offset(pud_k, address);
+	if (!pmd_present(*pmd_k))
+		return NULL;
+
+	if (!pmd_present(*pmd))
+		set_pmd(pmd, *pmd_k);
+	else {
+		/*
+		 * The page tables are fully synchronised so there must
+		 * be another reason for the fault. Return NULL here to
+		 * signal that we have not taken care of the fault.
+		 */
+		BUG_ON(pmd_page(*pmd) != pmd_page(*pmd_k));
+		return NULL;
+	}
+
+	return pmd_k;
+}
+
+#ifdef CONFIG_SH_STORE_QUEUES
+#define __FAULT_ADDR_LIMIT	P3_ADDR_MAX
+#else
+#define __FAULT_ADDR_LIMIT	VMALLOC_END
+#endif
+
+/*
+ * Handle a fault on the vmalloc or module mapping area
+ */
+static noinline int vmalloc_fault(unsigned long address)
+{
+	pgd_t *pgd_k;
+	pmd_t *pmd_k;
+	pte_t *pte_k;
+
+	/* Make sure we are in vmalloc/module/P3 area: */
+	if (!(address >= VMALLOC_START && address < __FAULT_ADDR_LIMIT))
+		return -1;
+
+	/*
+	 * Synchronize this task's top level page-table
+	 * with the 'reference' page table.
+	 *
+	 * Do _not_ use "current" here. We might be inside
+	 * an interrupt in the middle of a task switch..
+	 */
+	pgd_k = get_TTB();
+	pmd_k = vmalloc_sync_one(pgd_k, address);
+	if (!pmd_k)
+		return -1;
+
+	pte_k = pte_offset_kernel(pmd_k, address);
+	if (!pte_present(*pte_k))
+		return -1;
+
+	return 0;
+}
+
+static void
+show_fault_oops(struct pt_regs *regs, unsigned long address)
+{
+	if (!oops_may_print())
+		return;
+
+	printk(KERN_ALERT "BUG: unable to handle kernel ");
+	if (address < PAGE_SIZE)
+		printk(KERN_CONT "NULL pointer dereference");
+	else
+		printk(KERN_CONT "paging request");
+
+	printk(KERN_CONT " at %08lx\n", address);
+	printk(KERN_ALERT "PC:");
+	printk_address(regs->pc, 1);
+
+	show_pte(NULL, address);
+}
+
+static noinline void
+no_context(struct pt_regs *regs, unsigned long error_code,
+	   unsigned long address)
+{
+	/* Are we prepared to handle this kernel fault?  */
+	if (fixup_exception(regs))
+		return;
+
+	if (handle_trapped_io(regs, address))
+		return;
+
+	/*
+	 * Oops. The kernel tried to access some bad page. We'll have to
+	 * terminate things with extreme prejudice.
+	 */
+	bust_spinlocks(1);
+
+	show_fault_oops(regs, address);
+
+	die("Oops", regs, error_code);
+	bust_spinlocks(0);
+	do_exit(SIGKILL);
+}
+
+static void
+__bad_area_nosemaphore(struct pt_regs *regs, unsigned long error_code,
+		       unsigned long address, int si_code)
+{
+	struct task_struct *tsk = current;
+
+	/* User mode accesses just cause a SIGSEGV */
+	if (user_mode(regs)) {
+		/*
+		 * It's possible to have interrupts off here:
+		 */
+		local_irq_enable();
+
+		force_sig_info_fault(SIGSEGV, si_code, address, tsk);
+
+		return;
+	}
+
+	no_context(regs, error_code, address);
+}
+
+static noinline void
+bad_area_nosemaphore(struct pt_regs *regs, unsigned long error_code,
+		     unsigned long address)
+{
+	__bad_area_nosemaphore(regs, error_code, address, SEGV_MAPERR);
+}
+
+static void
+__bad_area(struct pt_regs *regs, unsigned long error_code,
+	   unsigned long address, int si_code)
+{
+	struct mm_struct *mm = current->mm;
+
+	/*
+	 * Something tried to access memory that isn't in our memory map..
+	 * Fix it, but check if it's kernel or user first..
+	 */
+	up_read(&mm->mmap_sem);
+
+	__bad_area_nosemaphore(regs, error_code, address, si_code);
+}
+
+static noinline void
+bad_area(struct pt_regs *regs, unsigned long error_code, unsigned long address)
+{
+	__bad_area(regs, error_code, address, SEGV_MAPERR);
+}
+
+static noinline void
+bad_area_access_error(struct pt_regs *regs, unsigned long error_code,
+		      unsigned long address)
+{
+	__bad_area(regs, error_code, address, SEGV_ACCERR);
+}
+
+static void out_of_memory(void)
+{
+	/*
+	 * We ran out of memory, call the OOM killer, and return the userspace
+	 * (which will retry the fault, or kill us if we got oom-killed):
+	 */
+	up_read(&current->mm->mmap_sem);
+
+	pagefault_out_of_memory();
+}
+
+static void
+do_sigbus(struct pt_regs *regs, unsigned long error_code, unsigned long address)
+{
+	struct task_struct *tsk = current;
+	struct mm_struct *mm = tsk->mm;
+
+	up_read(&mm->mmap_sem);
+
+	/* Kernel mode? Handle exceptions or die: */
+	if (!user_mode(regs))
+		no_context(regs, error_code, address);
+
+	force_sig_info_fault(SIGBUS, BUS_ADRERR, address, tsk);
+}
+
+static noinline int
+mm_fault_error(struct pt_regs *regs, unsigned long error_code,
+	       unsigned long address, unsigned int fault)
+{
+	/*
+	 * Pagefault was interrupted by SIGKILL. We have no reason to
+	 * continue pagefault.
+	 */
+	if (fatal_signal_pending(current)) {
+		if (!(fault & VM_FAULT_RETRY))
+			up_read(&current->mm->mmap_sem);
+		if (!user_mode(regs))
+			no_context(regs, error_code, address);
+		return 1;
+	}
+
+	if (!(fault & VM_FAULT_ERROR))
+		return 0;
+
+	if (fault & VM_FAULT_OOM) {
+		/* Kernel mode? Handle exceptions or die: */
+		if (!user_mode(regs)) {
+			up_read(&current->mm->mmap_sem);
+			no_context(regs, error_code, address);
+			return 1;
+		}
+
+		out_of_memory();
+	} else {
+		if (fault & VM_FAULT_SIGBUS)
+			do_sigbus(regs, error_code, address);
+		else
+			BUG();
+	}
+
+	return 1;
+}
+
+static inline int access_error(int error_code, struct vm_area_struct *vma)
+{
+	if (error_code & FAULT_CODE_WRITE) {
+		/* write, present and write, not present: */
+		if (unlikely(!(vma->vm_flags & VM_WRITE)))
+			return 1;
+		return 0;
+	}
+
+	/* ITLB miss on NX page */
+	if (unlikely((error_code & FAULT_CODE_ITLB) &&
+		     !(vma->vm_flags & VM_EXEC)))
+		return 1;
+
+	/* read, not present: */
+	if (unlikely(!(vma->vm_flags & (VM_READ | VM_EXEC | VM_WRITE))))
+		return 1;
+
+	return 0;
+}
+
+static int fault_in_kernel_space(unsigned long address)
+{
+	return address >= TASK_SIZE;
+}
+
+/*
+ * This routine handles page faults.  It determines the address,
+ * and the problem, and then passes it off to one of the appropriate
+ * routines.
+ */
+asmlinkage void __kprobes do_page_fault(struct pt_regs *regs,
+					unsigned long error_code,
+					unsigned long address)
+{
+	unsigned long vec;
+	struct task_struct *tsk;
+	struct mm_struct *mm;
+	struct vm_area_struct * vma;
+	int fault;
+	int write = error_code & FAULT_CODE_WRITE;
+	unsigned int flags = (FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE |
+			      (write ? FAULT_FLAG_WRITE : 0));
+
+	tsk = current;
+	mm = tsk->mm;
+	vec = lookup_exception_vector();
+
+	/*
+	 * We fault-in kernel-space virtual memory on-demand. The
+	 * 'reference' page table is init_mm.pgd.
+	 *
+	 * NOTE! We MUST NOT take any locks for this case. We may
+	 * be in an interrupt or a critical region, and should
+	 * only copy the information from the master page table,
+	 * nothing more.
+	 */
+	if (unlikely(fault_in_kernel_space(address))) {
+		if (vmalloc_fault(address) >= 0)
+			return;
+		if (notify_page_fault(regs, vec))
+			return;
+
+		bad_area_nosemaphore(regs, error_code, address);
+		return;
+	}
+
+	if (unlikely(notify_page_fault(regs, vec)))
+		return;
+
+	/* Only enable interrupts if they were on before the fault */
+	if ((regs->sr & SR_IMASK) != SR_IMASK)
+		local_irq_enable();
+
+	perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address);
+
+	/*
+	 * If we're in an interrupt, have no user context or are running
+	 * in an atomic region then we must not take the fault:
+	 */
+	if (unlikely(in_atomic() || !mm)) {
+		bad_area_nosemaphore(regs, error_code, address);
+		return;
+	}
+
+retry:
+	down_read(&mm->mmap_sem);
+
+	vma = find_vma(mm, address);
+	if (unlikely(!vma)) {
+		bad_area(regs, error_code, address);
+		return;
+	}
+	if (likely(vma->vm_start <= address))
+		goto good_area;
+	if (unlikely(!(vma->vm_flags & VM_GROWSDOWN))) {
+		bad_area(regs, error_code, address);
+		return;
+	}
+	if (unlikely(expand_stack(vma, address))) {
+		bad_area(regs, error_code, address);
+		return;
+	}
+
+	/*
+	 * Ok, we have a good vm_area for this memory access, so
+	 * we can handle it..
+	 */
+good_area:
+	if (unlikely(access_error(error_code, vma))) {
+		bad_area_access_error(regs, error_code, address);
+		return;
+	}
+
+	set_thread_fault_code(error_code);
+
+	/*
+	 * If for any reason at all we couldn't handle the fault,
+	 * make sure we exit gracefully rather than endlessly redo
+	 * the fault.
+	 */
+	fault = handle_mm_fault(mm, vma, address, flags);
+
+	if (unlikely(fault & (VM_FAULT_RETRY | VM_FAULT_ERROR)))
+		if (mm_fault_error(regs, error_code, address, fault))
+			return;
+
+	if (flags & FAULT_FLAG_ALLOW_RETRY) {
+		if (fault & VM_FAULT_MAJOR) {
+			tsk->maj_flt++;
+			perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1,
+				      regs, address);
+		} else {
+			tsk->min_flt++;
+			perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1,
+				      regs, address);
+		}
+		if (fault & VM_FAULT_RETRY) {
+			flags &= ~FAULT_FLAG_ALLOW_RETRY;
+
+			/*
+			 * No need to up_read(&mm->mmap_sem) as we would
+			 * have already released it in __lock_page_or_retry
+			 * in mm/filemap.c.
+			 */
+			goto retry;
+		}
+	}
+
+	up_read(&mm->mmap_sem);
+}
diff --git a/arch/sh/mm/fault_32.c b/arch/sh/mm/fault_32.c
deleted file mode 100644
index e99b104..0000000
--- a/arch/sh/mm/fault_32.c
+++ /dev/null
@@ -1,374 +0,0 @@
-/*
- * Page fault handler for SH with an MMU.
- *
- *  Copyright (C) 1999  Niibe Yutaka
- *  Copyright (C) 2003 - 2009  Paul Mundt
- *
- *  Based on linux/arch/i386/mm/fault.c:
- *   Copyright (C) 1995  Linus Torvalds
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file "COPYING" in the main directory of this archive
- * for more details.
- */
-#include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/hardirq.h>
-#include <linux/kprobes.h>
-#include <linux/perf_event.h>
-#include <asm/io_trapped.h>
-#include <asm/mmu_context.h>
-#include <asm/tlbflush.h>
-#include <asm/traps.h>
-
-static inline int notify_page_fault(struct pt_regs *regs, int trap)
-{
-	int ret = 0;
-
-	if (kprobes_built_in() && !user_mode(regs)) {
-		preempt_disable();
-		if (kprobe_running() && kprobe_fault_handler(regs, trap))
-			ret = 1;
-		preempt_enable();
-	}
-
-	return ret;
-}
-
-static inline pmd_t *vmalloc_sync_one(pgd_t *pgd, unsigned long address)
-{
-	unsigned index = pgd_index(address);
-	pgd_t *pgd_k;
-	pud_t *pud, *pud_k;
-	pmd_t *pmd, *pmd_k;
-
-	pgd += index;
-	pgd_k = init_mm.pgd + index;
-
-	if (!pgd_present(*pgd_k))
-		return NULL;
-
-	pud = pud_offset(pgd, address);
-	pud_k = pud_offset(pgd_k, address);
-	if (!pud_present(*pud_k))
-		return NULL;
-
-	if (!pud_present(*pud))
-	    set_pud(pud, *pud_k);
-
-	pmd = pmd_offset(pud, address);
-	pmd_k = pmd_offset(pud_k, address);
-	if (!pmd_present(*pmd_k))
-		return NULL;
-
-	if (!pmd_present(*pmd))
-		set_pmd(pmd, *pmd_k);
-	else {
-		/*
-		 * The page tables are fully synchronised so there must
-		 * be another reason for the fault. Return NULL here to
-		 * signal that we have not taken care of the fault.
-		 */
-		BUG_ON(pmd_page(*pmd) != pmd_page(*pmd_k));
-		return NULL;
-	}
-
-	return pmd_k;
-}
-
-/*
- * Handle a fault on the vmalloc or module mapping area
- */
-static noinline int vmalloc_fault(unsigned long address)
-{
-	pgd_t *pgd_k;
-	pmd_t *pmd_k;
-	pte_t *pte_k;
-
-	/* Make sure we are in vmalloc/module/P3 area: */
-	if (!(address >= P3SEG && address < P3_ADDR_MAX))
-		return -1;
-
-	/*
-	 * Synchronize this task's top level page-table
-	 * with the 'reference' page table.
-	 *
-	 * Do _not_ use "current" here. We might be inside
-	 * an interrupt in the middle of a task switch..
-	 */
-	pgd_k = get_TTB();
-	pmd_k = vmalloc_sync_one(pgd_k, address);
-	if (!pmd_k)
-		return -1;
-
-	pte_k = pte_offset_kernel(pmd_k, address);
-	if (!pte_present(*pte_k))
-		return -1;
-
-	return 0;
-}
-
-static int fault_in_kernel_space(unsigned long address)
-{
-	return address >= TASK_SIZE;
-}
-
-/*
- * This routine handles page faults.  It determines the address,
- * and the problem, and then passes it off to one of the appropriate
- * routines.
- */
-asmlinkage void __kprobes do_page_fault(struct pt_regs *regs,
-					unsigned long writeaccess,
-					unsigned long address)
-{
-	unsigned long vec;
-	struct task_struct *tsk;
-	struct mm_struct *mm;
-	struct vm_area_struct * vma;
-	int si_code;
-	int fault;
-	siginfo_t info;
-
-	tsk = current;
-	mm = tsk->mm;
-	si_code = SEGV_MAPERR;
-	vec = lookup_exception_vector();
-
-	/*
-	 * We fault-in kernel-space virtual memory on-demand. The
-	 * 'reference' page table is init_mm.pgd.
-	 *
-	 * NOTE! We MUST NOT take any locks for this case. We may
-	 * be in an interrupt or a critical region, and should
-	 * only copy the information from the master page table,
-	 * nothing more.
-	 */
-	if (unlikely(fault_in_kernel_space(address))) {
-		if (vmalloc_fault(address) >= 0)
-			return;
-		if (notify_page_fault(regs, vec))
-			return;
-
-		goto bad_area_nosemaphore;
-	}
-
-	if (unlikely(notify_page_fault(regs, vec)))
-		return;
-
-	/* Only enable interrupts if they were on before the fault */
-	if ((regs->sr & SR_IMASK) != SR_IMASK)
-		local_irq_enable();
-
-	perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address);
-
-	/*
-	 * If we're in an interrupt, have no user context or are running
-	 * in an atomic region then we must not take the fault:
-	 */
-	if (in_atomic() || !mm)
-		goto no_context;
-
-	down_read(&mm->mmap_sem);
-
-	vma = find_vma(mm, address);
-	if (!vma)
-		goto bad_area;
-	if (vma->vm_start <= address)
-		goto good_area;
-	if (!(vma->vm_flags & VM_GROWSDOWN))
-		goto bad_area;
-	if (expand_stack(vma, address))
-		goto bad_area;
-
-	/*
-	 * Ok, we have a good vm_area for this memory access, so
-	 * we can handle it..
-	 */
-good_area:
-	si_code = SEGV_ACCERR;
-	if (writeaccess) {
-		if (!(vma->vm_flags & VM_WRITE))
-			goto bad_area;
-	} else {
-		if (!(vma->vm_flags & (VM_READ | VM_EXEC | VM_WRITE)))
-			goto bad_area;
-	}
-
-	/*
-	 * If for any reason at all we couldn't handle the fault,
-	 * make sure we exit gracefully rather than endlessly redo
-	 * the fault.
-	 */
-	fault = handle_mm_fault(mm, vma, address, writeaccess ? FAULT_FLAG_WRITE : 0);
-	if (unlikely(fault & VM_FAULT_ERROR)) {
-		if (fault & VM_FAULT_OOM)
-			goto out_of_memory;
-		else if (fault & VM_FAULT_SIGBUS)
-			goto do_sigbus;
-		BUG();
-	}
-	if (fault & VM_FAULT_MAJOR) {
-		tsk->maj_flt++;
-		perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1,
-				     regs, address);
-	} else {
-		tsk->min_flt++;
-		perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1,
-				     regs, address);
-	}
-
-	up_read(&mm->mmap_sem);
-	return;
-
-	/*
-	 * Something tried to access memory that isn't in our memory map..
-	 * Fix it, but check if it's kernel or user first..
-	 */
-bad_area:
-	up_read(&mm->mmap_sem);
-
-bad_area_nosemaphore:
-	if (user_mode(regs)) {
-		info.si_signo = SIGSEGV;
-		info.si_errno = 0;
-		info.si_code = si_code;
-		info.si_addr = (void *) address;
-		force_sig_info(SIGSEGV, &info, tsk);
-		return;
-	}
-
-no_context:
-	/* Are we prepared to handle this kernel fault?  */
-	if (fixup_exception(regs))
-		return;
-
-	if (handle_trapped_io(regs, address))
-		return;
-/*
- * Oops. The kernel tried to access some bad page. We'll have to
- * terminate things with extreme prejudice.
- *
- */
-
-	bust_spinlocks(1);
-
-	if (oops_may_print()) {
-		unsigned long page;
-
-		if (address < PAGE_SIZE)
-			printk(KERN_ALERT "Unable to handle kernel NULL "
-					  "pointer dereference");
-		else
-			printk(KERN_ALERT "Unable to handle kernel paging "
-					  "request");
-		printk(" at virtual address %08lx\n", address);
-		printk(KERN_ALERT "pc = %08lx\n", regs->pc);
-		page = (unsigned long)get_TTB();
-		if (page) {
-			page = ((__typeof__(page) *)page)[address >> PGDIR_SHIFT];
-			printk(KERN_ALERT "*pde = %08lx\n", page);
-			if (page & _PAGE_PRESENT) {
-				page &= PAGE_MASK;
-				address &= 0x003ff000;
-				page = ((__typeof__(page) *)
-						__va(page))[address >>
-							    PAGE_SHIFT];
-				printk(KERN_ALERT "*pte = %08lx\n", page);
-			}
-		}
-	}
-
-	die("Oops", regs, writeaccess);
-	bust_spinlocks(0);
-	do_exit(SIGKILL);
-
-/*
- * We ran out of memory, or some other thing happened to us that made
- * us unable to handle the page fault gracefully.
- */
-out_of_memory:
-	up_read(&mm->mmap_sem);
-	if (!user_mode(regs))
-		goto no_context;
-	pagefault_out_of_memory();
-	return;
-
-do_sigbus:
-	up_read(&mm->mmap_sem);
-
-	/*
-	 * Send a sigbus, regardless of whether we were in kernel
-	 * or user mode.
-	 */
-	info.si_signo = SIGBUS;
-	info.si_errno = 0;
-	info.si_code = BUS_ADRERR;
-	info.si_addr = (void *)address;
-	force_sig_info(SIGBUS, &info, tsk);
-
-	/* Kernel mode? Handle exceptions or die */
-	if (!user_mode(regs))
-		goto no_context;
-}
-
-/*
- * Called with interrupts disabled.
- */
-asmlinkage int __kprobes
-handle_tlbmiss(struct pt_regs *regs, unsigned long writeaccess,
-	       unsigned long address)
-{
-	pgd_t *pgd;
-	pud_t *pud;
-	pmd_t *pmd;
-	pte_t *pte;
-	pte_t entry;
-
-	/*
-	 * We don't take page faults for P1, P2, and parts of P4, these
-	 * are always mapped, whether it be due to legacy behaviour in
-	 * 29-bit mode, or due to PMB configuration in 32-bit mode.
-	 */
-	if (address >= P3SEG && address < P3_ADDR_MAX) {
-		pgd = pgd_offset_k(address);
-	} else {
-		if (unlikely(address >= TASK_SIZE || !current->mm))
-			return 1;
-
-		pgd = pgd_offset(current->mm, address);
-	}
-
-	pud = pud_offset(pgd, address);
-	if (pud_none_or_clear_bad(pud))
-		return 1;
-	pmd = pmd_offset(pud, address);
-	if (pmd_none_or_clear_bad(pmd))
-		return 1;
-	pte = pte_offset_kernel(pmd, address);
-	entry = *pte;
-	if (unlikely(pte_none(entry) || pte_not_present(entry)))
-		return 1;
-	if (unlikely(writeaccess && !pte_write(entry)))
-		return 1;
-
-	if (writeaccess)
-		entry = pte_mkdirty(entry);
-	entry = pte_mkyoung(entry);
-
-	set_pte(pte, entry);
-
-#if defined(CONFIG_CPU_SH4) && !defined(CONFIG_SMP)
-	/*
-	 * SH-4 does not set MMUCR.RC to the corresponding TLB entry in
-	 * the case of an initial page write exception, so we need to
-	 * flush it in order to avoid potential TLB entry duplication.
-	 */
-	if (writeaccess == 2)
-		local_flush_tlb_one(get_asid(), address & PAGE_MASK);
-#endif
-
-	update_mmu_cache(NULL, address, pte);
-
-	return 0;
-}
diff --git a/arch/sh/mm/fault_64.c b/arch/sh/mm/fault_64.c
deleted file mode 100644
index 44a3410..0000000
--- a/arch/sh/mm/fault_64.c
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
- * The SH64 TLB miss.
- *
- * Original code from fault.c
- * Copyright (C) 2000, 2001  Paolo Alberelli
- *
- * Fast PTE->TLB refill path
- * Copyright (C) 2003 Richard.Curnow@superh.com
- *
- * IMPORTANT NOTES :
- * The do_fast_page_fault function is called from a context in entry.S
- * where very few registers have been saved.  In particular, the code in
- * this file must be compiled not to use ANY caller-save registers that
- * are not part of the restricted save set.  Also, it means that code in
- * this file must not make calls to functions elsewhere in the kernel, or
- * else the excepting context will see corruption in its caller-save
- * registers.  Plus, the entry.S save area is non-reentrant, so this code
- * has to run with SR.BL==1, i.e. no interrupts taken inside it and panic
- * on any exception.
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file "COPYING" in the main directory of this archive
- * for more details.
- */
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/string.h>
-#include <linux/types.h>
-#include <linux/ptrace.h>
-#include <linux/mman.h>
-#include <linux/mm.h>
-#include <linux/smp.h>
-#include <linux/interrupt.h>
-#include <asm/tlb.h>
-#include <asm/io.h>
-#include <asm/uaccess.h>
-#include <asm/pgalloc.h>
-#include <asm/mmu_context.h>
-#include <cpu/registers.h>
-
-/* Callable from fault.c, so not static */
-inline void __do_tlb_refill(unsigned long address,
-                            unsigned long long is_text_not_data, pte_t *pte)
-{
-	unsigned long long ptel;
-	unsigned long long pteh=0;
-	struct tlb_info *tlbp;
-	unsigned long long next;
-
-	/* Get PTEL first */
-	ptel = pte_val(*pte);
-
-	/*
-	 * Set PTEH register
-	 */
-	pteh = neff_sign_extend(address & MMU_VPN_MASK);
-
-	/* Set the ASID. */
-	pteh |= get_asid() << PTEH_ASID_SHIFT;
-	pteh |= PTEH_VALID;
-
-	/* Set PTEL register, set_pte has performed the sign extension */
-	ptel &= _PAGE_FLAGS_HARDWARE_MASK; /* drop software flags */
-
-	tlbp = is_text_not_data ? &(cpu_data->itlb) : &(cpu_data->dtlb);
-	next = tlbp->next;
-	__flush_tlb_slot(next);
-	asm volatile ("putcfg %0,1,%2\n\n\t"
-		      "putcfg %0,0,%1\n"
-		      :  : "r" (next), "r" (pteh), "r" (ptel) );
-
-	next += TLB_STEP;
-	if (next > tlbp->last) next = tlbp->first;
-	tlbp->next = next;
-
-}
-
-static int handle_vmalloc_fault(struct mm_struct *mm,
-				unsigned long protection_flags,
-                                unsigned long long textaccess,
-				unsigned long address)
-{
-	pgd_t *dir;
-	pud_t *pud;
-	pmd_t *pmd;
-	static pte_t *pte;
-	pte_t entry;
-
-	dir = pgd_offset_k(address);
-
-	pud = pud_offset(dir, address);
-	if (pud_none_or_clear_bad(pud))
-		return 0;
-
-	pmd = pmd_offset(pud, address);
-	if (pmd_none_or_clear_bad(pmd))
-		return 0;
-
-	pte = pte_offset_kernel(pmd, address);
-	entry = *pte;
-
-	if (pte_none(entry) || !pte_present(entry))
-		return 0;
-	if ((pte_val(entry) & protection_flags) != protection_flags)
-		return 0;
-
-        __do_tlb_refill(address, textaccess, pte);
-
-	return 1;
-}
-
-static int handle_tlbmiss(struct mm_struct *mm,
-			  unsigned long long protection_flags,
-			  unsigned long long textaccess,
-			  unsigned long address)
-{
-	pgd_t *dir;
-	pud_t *pud;
-	pmd_t *pmd;
-	pte_t *pte;
-	pte_t entry;
-
-	/* NB. The PGD currently only contains a single entry - there is no
-	   page table tree stored for the top half of the address space since
-	   virtual pages in that region should never be mapped in user mode.
-	   (In kernel mode, the only things in that region are the 512Mb super
-	   page (locked in), and vmalloc (modules) +  I/O device pages (handled
-	   by handle_vmalloc_fault), so no PGD for the upper half is required
-	   by kernel mode either).
-
-	   See how mm->pgd is allocated and initialised in pgd_alloc to see why
-	   the next test is necessary.  - RPC */
-	if (address >= (unsigned long) TASK_SIZE)
-		/* upper half - never has page table entries. */
-		return 0;
-
-	dir = pgd_offset(mm, address);
-	if (pgd_none(*dir) || !pgd_present(*dir))
-		return 0;
-	if (!pgd_present(*dir))
-		return 0;
-
-	pud = pud_offset(dir, address);
-	if (pud_none(*pud) || !pud_present(*pud))
-		return 0;
-
-	pmd = pmd_offset(pud, address);
-	if (pmd_none(*pmd) || !pmd_present(*pmd))
-		return 0;
-
-	pte = pte_offset_kernel(pmd, address);
-	entry = *pte;
-
-	if (pte_none(entry) || !pte_present(entry))
-		return 0;
-
-	/*
-	 * If the page doesn't have sufficient protection bits set to
-	 * service the kind of fault being handled, there's not much
-	 * point doing the TLB refill.  Punt the fault to the general
-	 * handler.
-	 */
-	if ((pte_val(entry) & protection_flags) != protection_flags)
-		return 0;
-
-        __do_tlb_refill(address, textaccess, pte);
-
-	return 1;
-}
-
-/*
- * Put all this information into one structure so that everything is just
- * arithmetic relative to a single base address.  This reduces the number
- * of movi/shori pairs needed just to load addresses of static data.
- */
-struct expevt_lookup {
-	unsigned short protection_flags[8];
-	unsigned char  is_text_access[8];
-	unsigned char  is_write_access[8];
-};
-
-#define PRU (1<<9)
-#define PRW (1<<8)
-#define PRX (1<<7)
-#define PRR (1<<6)
-
-#define DIRTY (_PAGE_DIRTY | _PAGE_ACCESSED)
-#define YOUNG (_PAGE_ACCESSED)
-
-/* Sized as 8 rather than 4 to allow checking the PTE's PRU bit against whether
-   the fault happened in user mode or privileged mode. */
-static struct expevt_lookup expevt_lookup_table = {
-	.protection_flags = {PRX, PRX, 0, 0, PRR, PRR, PRW, PRW},
-	.is_text_access   = {1,   1,   0, 0, 0,   0,   0,   0}
-};
-
-/*
-   This routine handles page faults that can be serviced just by refilling a
-   TLB entry from an existing page table entry.  (This case represents a very
-   large majority of page faults.) Return 1 if the fault was successfully
-   handled.  Return 0 if the fault could not be handled.  (This leads into the
-   general fault handling in fault.c which deals with mapping file-backed
-   pages, stack growth, segmentation faults, swapping etc etc)
- */
-asmlinkage int do_fast_page_fault(unsigned long long ssr_md,
-				  unsigned long long expevt,
-			          unsigned long address)
-{
-	struct task_struct *tsk;
-	struct mm_struct *mm;
-	unsigned long long textaccess;
-	unsigned long long protection_flags;
-	unsigned long long index;
-	unsigned long long expevt4;
-
-	/* The next few lines implement a way of hashing EXPEVT into a
-	 * small array index which can be used to lookup parameters
-	 * specific to the type of TLBMISS being handled.
-	 *
-	 * Note:
-	 *	ITLBMISS has EXPEVT==0xa40
-	 *	RTLBMISS has EXPEVT==0x040
-	 *	WTLBMISS has EXPEVT==0x060
-	 */
-	expevt4 = (expevt >> 4);
-	/* TODO : xor ssr_md into this expression too. Then we can check
-	 * that PRU is set when it needs to be. */
-	index = expevt4 ^ (expevt4 >> 5);
-	index &= 7;
-	protection_flags = expevt_lookup_table.protection_flags[index];
-	textaccess       = expevt_lookup_table.is_text_access[index];
-
-	/* SIM
-	 * Note this is now called with interrupts still disabled
-	 * This is to cope with being called for a missing IO port
-	 * address with interrupts disabled. This should be fixed as
-	 * soon as we have a better 'fast path' miss handler.
-	 *
-	 * Plus take care how you try and debug this stuff.
-	 * For example, writing debug data to a port which you
-	 * have just faulted on is not going to work.
-	 */
-
-	tsk = current;
-	mm = tsk->mm;
-
-	if ((address >= VMALLOC_START && address < VMALLOC_END) ||
-	    (address >= IOBASE_VADDR  && address < IOBASE_END)) {
-		if (ssr_md)
-			/*
-			 * Process-contexts can never have this address
-			 * range mapped
-			 */
-			if (handle_vmalloc_fault(mm, protection_flags,
-						 textaccess, address))
-				return 1;
-	} else if (!in_interrupt() && mm) {
-		if (handle_tlbmiss(mm, protection_flags, textaccess, address))
-			return 1;
-	}
-
-	return 0;
-}
diff --git a/arch/sh/mm/tlb-sh5.c b/arch/sh/mm/tlb-sh5.c
index f27dbe1..3aea25d 100644
--- a/arch/sh/mm/tlb-sh5.c
+++ b/arch/sh/mm/tlb-sh5.c
@@ -182,3 +182,43 @@
 
 	local_irq_restore(flags);
 }
+
+void __update_tlb(struct vm_area_struct *vma, unsigned long address, pte_t pte)
+{
+	unsigned long long ptel;
+	unsigned long long pteh=0;
+	struct tlb_info *tlbp;
+	unsigned long long next;
+	unsigned int fault_code = get_thread_fault_code();
+
+	/* Get PTEL first */
+	ptel = pte.pte_low;
+
+	/*
+	 * Set PTEH register
+	 */
+	pteh = neff_sign_extend(address & MMU_VPN_MASK);
+
+	/* Set the ASID. */
+	pteh |= get_asid() << PTEH_ASID_SHIFT;
+	pteh |= PTEH_VALID;
+
+	/* Set PTEL register, set_pte has performed the sign extension */
+	ptel &= _PAGE_FLAGS_HARDWARE_MASK; /* drop software flags */
+
+	if (fault_code & FAULT_CODE_ITLB)
+		tlbp = &cpu_data->itlb;
+	else
+		tlbp = &cpu_data->dtlb;
+
+	next = tlbp->next;
+	__flush_tlb_slot(next);
+	asm volatile ("putcfg %0,1,%2\n\n\t"
+		      "putcfg %0,0,%1\n"
+		      :  : "r" (next), "r" (pteh), "r" (ptel) );
+
+	next += TLB_STEP;
+	if (next > tlbp->last)
+		next = tlbp->first;
+	tlbp->next = next;
+}
diff --git a/arch/sh/mm/tlbex_32.c b/arch/sh/mm/tlbex_32.c
new file mode 100644
index 0000000..382262d
--- /dev/null
+++ b/arch/sh/mm/tlbex_32.c
@@ -0,0 +1,78 @@
+/*
+ * TLB miss handler for SH with an MMU.
+ *
+ *  Copyright (C) 1999  Niibe Yutaka
+ *  Copyright (C) 2003 - 2012  Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/kprobes.h>
+#include <linux/kdebug.h>
+#include <asm/mmu_context.h>
+#include <asm/thread_info.h>
+
+/*
+ * Called with interrupts disabled.
+ */
+asmlinkage int __kprobes
+handle_tlbmiss(struct pt_regs *regs, unsigned long error_code,
+	       unsigned long address)
+{
+	pgd_t *pgd;
+	pud_t *pud;
+	pmd_t *pmd;
+	pte_t *pte;
+	pte_t entry;
+
+	/*
+	 * We don't take page faults for P1, P2, and parts of P4, these
+	 * are always mapped, whether it be due to legacy behaviour in
+	 * 29-bit mode, or due to PMB configuration in 32-bit mode.
+	 */
+	if (address >= P3SEG && address < P3_ADDR_MAX) {
+		pgd = pgd_offset_k(address);
+	} else {
+		if (unlikely(address >= TASK_SIZE || !current->mm))
+			return 1;
+
+		pgd = pgd_offset(current->mm, address);
+	}
+
+	pud = pud_offset(pgd, address);
+	if (pud_none_or_clear_bad(pud))
+		return 1;
+	pmd = pmd_offset(pud, address);
+	if (pmd_none_or_clear_bad(pmd))
+		return 1;
+	pte = pte_offset_kernel(pmd, address);
+	entry = *pte;
+	if (unlikely(pte_none(entry) || pte_not_present(entry)))
+		return 1;
+	if (unlikely(error_code && !pte_write(entry)))
+		return 1;
+
+	if (error_code)
+		entry = pte_mkdirty(entry);
+	entry = pte_mkyoung(entry);
+
+	set_pte(pte, entry);
+
+#if defined(CONFIG_CPU_SH4) && !defined(CONFIG_SMP)
+	/*
+	 * SH-4 does not set MMUCR.RC to the corresponding TLB entry in
+	 * the case of an initial page write exception, so we need to
+	 * flush it in order to avoid potential TLB entry duplication.
+	 */
+	if (error_code == FAULT_CODE_INITIAL)
+		local_flush_tlb_one(get_asid(), address & PAGE_MASK);
+#endif
+
+	set_thread_fault_code(error_code);
+	update_mmu_cache(NULL, address, pte);
+
+	return 0;
+}
diff --git a/arch/sh/mm/tlbex_64.c b/arch/sh/mm/tlbex_64.c
new file mode 100644
index 0000000..8557548
--- /dev/null
+++ b/arch/sh/mm/tlbex_64.c
@@ -0,0 +1,166 @@
+/*
+ * The SH64 TLB miss.
+ *
+ * Original code from fault.c
+ * Copyright (C) 2000, 2001  Paolo Alberelli
+ *
+ * Fast PTE->TLB refill path
+ * Copyright (C) 2003 Richard.Curnow@superh.com
+ *
+ * IMPORTANT NOTES :
+ * The do_fast_page_fault function is called from a context in entry.S
+ * where very few registers have been saved.  In particular, the code in
+ * this file must be compiled not to use ANY caller-save registers that
+ * are not part of the restricted save set.  Also, it means that code in
+ * this file must not make calls to functions elsewhere in the kernel, or
+ * else the excepting context will see corruption in its caller-save
+ * registers.  Plus, the entry.S save area is non-reentrant, so this code
+ * has to run with SR.BL==1, i.e. no interrupts taken inside it and panic
+ * on any exception.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/ptrace.h>
+#include <linux/mman.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/interrupt.h>
+#include <linux/kprobes.h>
+#include <asm/tlb.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/pgalloc.h>
+#include <asm/mmu_context.h>
+
+static int handle_tlbmiss(unsigned long long protection_flags,
+			  unsigned long address)
+{
+	pgd_t *pgd;
+	pud_t *pud;
+	pmd_t *pmd;
+	pte_t *pte;
+	pte_t entry;
+
+	if (is_vmalloc_addr((void *)address)) {
+		pgd = pgd_offset_k(address);
+	} else {
+		if (unlikely(address >= TASK_SIZE || !current->mm))
+			return 1;
+
+		pgd = pgd_offset(current->mm, address);
+	}
+
+	pud = pud_offset(pgd, address);
+	if (pud_none(*pud) || !pud_present(*pud))
+		return 1;
+
+	pmd = pmd_offset(pud, address);
+	if (pmd_none(*pmd) || !pmd_present(*pmd))
+		return 1;
+
+	pte = pte_offset_kernel(pmd, address);
+	entry = *pte;
+	if (pte_none(entry) || !pte_present(entry))
+		return 1;
+
+	/*
+	 * If the page doesn't have sufficient protection bits set to
+	 * service the kind of fault being handled, there's not much
+	 * point doing the TLB refill.  Punt the fault to the general
+	 * handler.
+	 */
+	if ((pte_val(entry) & protection_flags) != protection_flags)
+		return 1;
+
+	update_mmu_cache(NULL, address, pte);
+
+	return 0;
+}
+
+/*
+ * Put all this information into one structure so that everything is just
+ * arithmetic relative to a single base address.  This reduces the number
+ * of movi/shori pairs needed just to load addresses of static data.
+ */
+struct expevt_lookup {
+	unsigned short protection_flags[8];
+	unsigned char  is_text_access[8];
+	unsigned char  is_write_access[8];
+};
+
+#define PRU (1<<9)
+#define PRW (1<<8)
+#define PRX (1<<7)
+#define PRR (1<<6)
+
+/* Sized as 8 rather than 4 to allow checking the PTE's PRU bit against whether
+   the fault happened in user mode or privileged mode. */
+static struct expevt_lookup expevt_lookup_table = {
+	.protection_flags = {PRX, PRX, 0, 0, PRR, PRR, PRW, PRW},
+	.is_text_access   = {1,   1,   0, 0, 0,   0,   0,   0}
+};
+
+static inline unsigned int
+expevt_to_fault_code(unsigned long expevt)
+{
+	if (expevt == 0xa40)
+		return FAULT_CODE_ITLB;
+	else if (expevt == 0x060)
+		return FAULT_CODE_WRITE;
+
+	return 0;
+}
+
+/*
+   This routine handles page faults that can be serviced just by refilling a
+   TLB entry from an existing page table entry.  (This case represents a very
+   large majority of page faults.) Return 1 if the fault was successfully
+   handled.  Return 0 if the fault could not be handled.  (This leads into the
+   general fault handling in fault.c which deals with mapping file-backed
+   pages, stack growth, segmentation faults, swapping etc etc)
+ */
+asmlinkage int __kprobes
+do_fast_page_fault(unsigned long long ssr_md, unsigned long long expevt,
+		   unsigned long address)
+{
+	unsigned long long protection_flags;
+	unsigned long long index;
+	unsigned long long expevt4;
+	unsigned int fault_code;
+
+	/* The next few lines implement a way of hashing EXPEVT into a
+	 * small array index which can be used to lookup parameters
+	 * specific to the type of TLBMISS being handled.
+	 *
+	 * Note:
+	 *	ITLBMISS has EXPEVT==0xa40
+	 *	RTLBMISS has EXPEVT==0x040
+	 *	WTLBMISS has EXPEVT==0x060
+	 */
+	expevt4 = (expevt >> 4);
+	/* TODO : xor ssr_md into this expression too. Then we can check
+	 * that PRU is set when it needs to be. */
+	index = expevt4 ^ (expevt4 >> 5);
+	index &= 7;
+
+	fault_code = expevt_to_fault_code(expevt);
+
+	protection_flags = expevt_lookup_table.protection_flags[index];
+
+	if (expevt_lookup_table.is_text_access[index])
+		fault_code |= FAULT_CODE_ITLB;
+	if (!ssr_md)
+		fault_code |= FAULT_CODE_USER;
+
+	set_thread_fault_code(fault_code);
+
+	return handle_tlbmiss(protection_flags, address);
+}
diff --git a/arch/sh/mm/tlbflush_64.c b/arch/sh/mm/tlbflush_64.c
index 11c5a18..f33fdd2 100644
--- a/arch/sh/mm/tlbflush_64.c
+++ b/arch/sh/mm/tlbflush_64.c
@@ -3,7 +3,7 @@
  *
  * Copyright (C) 2000, 2001  Paolo Alberelli
  * Copyright (C) 2003  Richard Curnow (/proc/tlb, bug fixes)
- * Copyright (C) 2003 - 2009 Paul Mundt
+ * Copyright (C) 2003 - 2012 Paul Mundt
  *
  * This file is subject to the terms and conditions of the GNU General Public
  * License.  See the file "COPYING" in the main directory of this archive
@@ -28,294 +28,6 @@
 #include <asm/pgalloc.h>
 #include <asm/mmu_context.h>
 
-extern void die(const char *,struct pt_regs *,long);
-
-#define PFLAG(val,flag)   (( (val) & (flag) ) ? #flag : "" )
-#define PPROT(flag) PFLAG(pgprot_val(prot),flag)
-
-static inline void print_prots(pgprot_t prot)
-{
-	printk("prot is 0x%016llx\n",pgprot_val(prot));
-
-	printk("%s %s %s %s %s\n",PPROT(_PAGE_SHARED),PPROT(_PAGE_READ),
-	       PPROT(_PAGE_EXECUTE),PPROT(_PAGE_WRITE),PPROT(_PAGE_USER));
-}
-
-static inline void print_vma(struct vm_area_struct *vma)
-{
-	printk("vma start 0x%08lx\n", vma->vm_start);
-	printk("vma end   0x%08lx\n", vma->vm_end);
-
-	print_prots(vma->vm_page_prot);
-	printk("vm_flags 0x%08lx\n", vma->vm_flags);
-}
-
-static inline void print_task(struct task_struct *tsk)
-{
-	printk("Task pid %d\n", task_pid_nr(tsk));
-}
-
-static pte_t *lookup_pte(struct mm_struct *mm, unsigned long address)
-{
-	pgd_t *dir;
-	pud_t *pud;
-	pmd_t *pmd;
-	pte_t *pte;
-	pte_t entry;
-
-	dir = pgd_offset(mm, address);
-	if (pgd_none(*dir))
-		return NULL;
-
-	pud = pud_offset(dir, address);
-	if (pud_none(*pud))
-		return NULL;
-
-	pmd = pmd_offset(pud, address);
-	if (pmd_none(*pmd))
-		return NULL;
-
-	pte = pte_offset_kernel(pmd, address);
-	entry = *pte;
-	if (pte_none(entry) || !pte_present(entry))
-		return NULL;
-
-	return pte;
-}
-
-/*
- * This routine handles page faults.  It determines the address,
- * and the problem, and then passes it off to one of the appropriate
- * routines.
- */
-asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long writeaccess,
-			      unsigned long textaccess, unsigned long address)
-{
-	struct task_struct *tsk;
-	struct mm_struct *mm;
-	struct vm_area_struct * vma;
-	const struct exception_table_entry *fixup;
-	pte_t *pte;
-	int fault;
-
-	/* SIM
-	 * Note this is now called with interrupts still disabled
-	 * This is to cope with being called for a missing IO port
-	 * address with interrupts disabled. This should be fixed as
-	 * soon as we have a better 'fast path' miss handler.
-	 *
-	 * Plus take care how you try and debug this stuff.
-	 * For example, writing debug data to a port which you
-	 * have just faulted on is not going to work.
-	 */
-
-	tsk = current;
-	mm = tsk->mm;
-
-	/* Not an IO address, so reenable interrupts */
-	local_irq_enable();
-
-	perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address);
-
-	/*
-	 * If we're in an interrupt or have no user
-	 * context, we must not take the fault..
-	 */
-	if (in_atomic() || !mm)
-		goto no_context;
-
-	/* TLB misses upon some cache flushes get done under cli() */
-	down_read(&mm->mmap_sem);
-
-	vma = find_vma(mm, address);
-
-	if (!vma) {
-#ifdef DEBUG_FAULT
-		print_task(tsk);
-		printk("%s:%d fault, address is 0x%08x PC %016Lx textaccess %d writeaccess %d\n",
-		       __func__, __LINE__,
-		       address,regs->pc,textaccess,writeaccess);
-		show_regs(regs);
-#endif
-		goto bad_area;
-	}
-	if (vma->vm_start <= address) {
-		goto good_area;
-	}
-
-	if (!(vma->vm_flags & VM_GROWSDOWN)) {
-#ifdef DEBUG_FAULT
-		print_task(tsk);
-		printk("%s:%d fault, address is 0x%08x PC %016Lx textaccess %d writeaccess %d\n",
-		       __func__, __LINE__,
-		       address,regs->pc,textaccess,writeaccess);
-		show_regs(regs);
-
-		print_vma(vma);
-#endif
-		goto bad_area;
-	}
-	if (expand_stack(vma, address)) {
-#ifdef DEBUG_FAULT
-		print_task(tsk);
-		printk("%s:%d fault, address is 0x%08x PC %016Lx textaccess %d writeaccess %d\n",
-		       __func__, __LINE__,
-		       address,regs->pc,textaccess,writeaccess);
-		show_regs(regs);
-#endif
-		goto bad_area;
-	}
-/*
- * Ok, we have a good vm_area for this memory access, so
- * we can handle it..
- */
-good_area:
-	if (textaccess) {
-		if (!(vma->vm_flags & VM_EXEC))
-			goto bad_area;
-	} else {
-		if (writeaccess) {
-			if (!(vma->vm_flags & VM_WRITE))
-				goto bad_area;
-		} else {
-			if (!(vma->vm_flags & VM_READ))
-				goto bad_area;
-		}
-	}
-
-	/*
-	 * If for any reason at all we couldn't handle the fault,
-	 * make sure we exit gracefully rather than endlessly redo
-	 * the fault.
-	 */
-	fault = handle_mm_fault(mm, vma, address, writeaccess ? FAULT_FLAG_WRITE : 0);
-	if (unlikely(fault & VM_FAULT_ERROR)) {
-		if (fault & VM_FAULT_OOM)
-			goto out_of_memory;
-		else if (fault & VM_FAULT_SIGBUS)
-			goto do_sigbus;
-		BUG();
-	}
-
-	if (fault & VM_FAULT_MAJOR) {
-		tsk->maj_flt++;
-		perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1,
-				     regs, address);
-	} else {
-		tsk->min_flt++;
-		perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1,
-				     regs, address);
-	}
-
-	/* If we get here, the page fault has been handled.  Do the TLB refill
-	   now from the newly-setup PTE, to avoid having to fault again right
-	   away on the same instruction. */
-	pte = lookup_pte (mm, address);
-	if (!pte) {
-		/* From empirical evidence, we can get here, due to
-		   !pte_present(pte).  (e.g. if a swap-in occurs, and the page
-		   is swapped back out again before the process that wanted it
-		   gets rescheduled?) */
-		goto no_pte;
-	}
-
-	__do_tlb_refill(address, textaccess, pte);
-
-no_pte:
-
-	up_read(&mm->mmap_sem);
-	return;
-
-/*
- * Something tried to access memory that isn't in our memory map..
- * Fix it, but check if it's kernel or user first..
- */
-bad_area:
-#ifdef DEBUG_FAULT
-	printk("fault:bad area\n");
-#endif
-	up_read(&mm->mmap_sem);
-
-	if (user_mode(regs)) {
-		static int count=0;
-		siginfo_t info;
-		if (count < 4) {
-			/* This is really to help debug faults when starting
-			 * usermode, so only need a few */
-			count++;
-			printk("user mode bad_area address=%08lx pid=%d (%s) pc=%08lx\n",
-				address, task_pid_nr(current), current->comm,
-				(unsigned long) regs->pc);
-#if 0
-			show_regs(regs);
-#endif
-		}
-		if (is_global_init(tsk)) {
-			panic("INIT had user mode bad_area\n");
-		}
-		tsk->thread.address = address;
-		tsk->thread.error_code = writeaccess;
-		info.si_signo = SIGSEGV;
-		info.si_errno = 0;
-		info.si_addr = (void *) address;
-		force_sig_info(SIGSEGV, &info, tsk);
-		return;
-	}
-
-no_context:
-#ifdef DEBUG_FAULT
-	printk("fault:No context\n");
-#endif
-	/* Are we prepared to handle this kernel fault?  */
-	fixup = search_exception_tables(regs->pc);
-	if (fixup) {
-		regs->pc = fixup->fixup;
-		return;
-	}
-
-/*
- * Oops. The kernel tried to access some bad page. We'll have to
- * terminate things with extreme prejudice.
- *
- */
-	if (address < PAGE_SIZE)
-		printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference");
-	else
-		printk(KERN_ALERT "Unable to handle kernel paging request");
-	printk(" at virtual address %08lx\n", address);
-	printk(KERN_ALERT "pc = %08Lx%08Lx\n", regs->pc >> 32, regs->pc & 0xffffffff);
-	die("Oops", regs, writeaccess);
-	do_exit(SIGKILL);
-
-/*
- * We ran out of memory, or some other thing happened to us that made
- * us unable to handle the page fault gracefully.
- */
-out_of_memory:
-	up_read(&mm->mmap_sem);
-	if (!user_mode(regs))
-		goto no_context;
-	pagefault_out_of_memory();
-	return;
-
-do_sigbus:
-	printk("fault:Do sigbus\n");
-	up_read(&mm->mmap_sem);
-
-	/*
-	 * Send a sigbus, regardless of whether we were in kernel
-	 * or user mode.
-	 */
-	tsk->thread.address = address;
-	tsk->thread.error_code = writeaccess;
-	tsk->thread.trap_no = 14;
-	force_sig(SIGBUS, tsk);
-
-	/* Kernel mode? Handle exceptions or die */
-	if (!user_mode(regs))
-		goto no_context;
-}
-
 void local_flush_tlb_one(unsigned long asid, unsigned long page)
 {
 	unsigned long long match, pteh=0, lpage;
@@ -458,7 +170,3 @@
 {
 	flush_tlb_all();
 }
-
-void __update_tlb(struct vm_area_struct *vma, unsigned long address, pte_t pte)
-{
-}
diff --git a/arch/sh/tools/mach-types b/arch/sh/tools/mach-types
index 6dd56c4..569977e 100644
--- a/arch/sh/tools/mach-types
+++ b/arch/sh/tools/mach-types
@@ -51,6 +51,8 @@
 MIGOR			SH_MIGOR
 RSK7201			SH_RSK7201
 RSK7203			SH_RSK7203
+RSK7264			SH_RSK7264
+RSK7269			SH_RSK7269
 AP325RXA		SH_AP325RXA
 SH2007			SH_SH2007
 SH7757LCR		SH_SH7757LCR
diff --git a/arch/sparc/include/asm/processor_32.h b/arch/sparc/include/asm/processor_32.h
index 9cbd854..f74ac9e 100644
--- a/arch/sparc/include/asm/processor_32.h
+++ b/arch/sparc/include/asm/processor_32.h
@@ -108,9 +108,6 @@
 #define release_thread(tsk)		do { } while(0)
 extern pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
 
-/* Prepare to copy thread state - unlazy all lazy status */
-#define prepare_to_copy(tsk)	do { } while (0)
-
 extern unsigned long get_wchan(struct task_struct *);
 
 #define task_pt_regs(tsk) ((tsk)->thread.kregs)
diff --git a/arch/sparc/include/asm/processor_64.h b/arch/sparc/include/asm/processor_64.h
index e713db2..67df5cc 100644
--- a/arch/sparc/include/asm/processor_64.h
+++ b/arch/sparc/include/asm/processor_64.h
@@ -186,9 +186,6 @@
 /* Free all resources held by a thread. */
 #define release_thread(tsk)		do { } while (0)
 
-/* Prepare to copy thread state - unlazy all lazy status */
-#define prepare_to_copy(tsk)	do { } while (0)
-
 extern pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
 
 extern unsigned long get_wchan(struct task_struct *task);
diff --git a/arch/tile/include/asm/processor.h b/arch/tile/include/asm/processor.h
index 34c1e01..15cd8a4 100644
--- a/arch/tile/include/asm/processor.h
+++ b/arch/tile/include/asm/processor.h
@@ -210,9 +210,6 @@
 	/* Nothing for now */
 }
 
-/* Prepare to copy thread state - unlazy all lazy status. */
-#define prepare_to_copy(tsk)	do { } while (0)
-
 extern int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags);
 
 extern int do_work_pending(struct pt_regs *regs, u32 flags);
diff --git a/arch/um/Kconfig.common b/arch/um/Kconfig.common
index 20a49ba..43ef890 100644
--- a/arch/um/Kconfig.common
+++ b/arch/um/Kconfig.common
@@ -56,11 +56,6 @@
 	bool
 	default y
 
-# Used in kernel/irq/manage.c and include/linux/irq.h
-config IRQ_RELEASE_METHOD
-	bool
-	default y
-
 config HZ
 	int
 	default 100
diff --git a/arch/um/defconfig b/arch/um/defconfig
index fdc97e2..7823ab1 100644
--- a/arch/um/defconfig
+++ b/arch/um/defconfig
@@ -12,7 +12,6 @@
 CONFIG_GENERIC_CALIBRATE_DELAY=y
 CONFIG_GENERIC_BUG=y
 CONFIG_GENERIC_CLOCKEVENTS=y
-CONFIG_IRQ_RELEASE_METHOD=y
 CONFIG_HZ=100
 
 #
diff --git a/arch/um/drivers/chan_kern.c b/arch/um/drivers/chan_kern.c
index ca4c7eb..45e248c 100644
--- a/arch/um/drivers/chan_kern.c
+++ b/arch/um/drivers/chan_kern.c
@@ -8,6 +8,7 @@
 #include <linux/tty_flip.h>
 #include "chan.h"
 #include "os.h"
+#include "irq_kern.h"
 
 #ifdef CONFIG_NOCONFIG_CHAN
 static void *not_configged_init(char *str, int device,
@@ -213,9 +214,9 @@
 		chan = list_entry(ele, struct chan, free_list);
 
 		if (chan->input && chan->enabled)
-			free_irq(chan->line->driver->read_irq, chan);
+			um_free_irq(chan->line->driver->read_irq, chan);
 		if (chan->output && chan->enabled)
-			free_irq(chan->line->driver->write_irq, chan);
+			um_free_irq(chan->line->driver->write_irq, chan);
 		chan->enabled = 0;
 	}
 }
@@ -234,9 +235,9 @@
 	}
 	else {
 		if (chan->input && chan->enabled)
-			free_irq(chan->line->driver->read_irq, chan);
+			um_free_irq(chan->line->driver->read_irq, chan);
 		if (chan->output && chan->enabled)
-			free_irq(chan->line->driver->write_irq, chan);
+			um_free_irq(chan->line->driver->write_irq, chan);
 		chan->enabled = 0;
 	}
 	if (chan->ops->close != NULL)
diff --git a/arch/um/drivers/line.c b/arch/um/drivers/line.c
index 4ab0d9c..acfd0e0 100644
--- a/arch/um/drivers/line.c
+++ b/arch/um/drivers/line.c
@@ -699,7 +699,7 @@
 static void __free_winch(struct work_struct *work)
 {
 	struct winch *winch = container_of(work, struct winch, work);
-	free_irq(WINCH_IRQ, winch);
+	um_free_irq(WINCH_IRQ, winch);
 
 	if (winch->pid != -1)
 		os_kill_process(winch->pid, 1);
diff --git a/arch/um/drivers/net_kern.c b/arch/um/drivers/net_kern.c
index 95f4416..0d60c56 100644
--- a/arch/um/drivers/net_kern.c
+++ b/arch/um/drivers/net_kern.c
@@ -195,7 +195,7 @@
 
 	netif_stop_queue(dev);
 
-	free_irq(dev->irq, dev);
+	um_free_irq(dev->irq, dev);
 	if (lp->close != NULL)
 		(*lp->close)(lp->fd, &lp->user);
 	lp->fd = -1;
@@ -835,7 +835,7 @@
 	spin_lock(&opened_lock);
 	list_for_each(ele, &opened) {
 		lp = list_entry(ele, struct uml_net_private, list);
-		free_irq(lp->dev->irq, lp->dev);
+		um_free_irq(lp->dev->irq, lp->dev);
 		if ((lp->close != NULL) && (lp->fd >= 0))
 			(*lp->close)(lp->fd, &lp->user);
 		if (lp->remove != NULL)
diff --git a/arch/um/drivers/port_kern.c b/arch/um/drivers/port_kern.c
index e31680e..11866ff 100644
--- a/arch/um/drivers/port_kern.c
+++ b/arch/um/drivers/port_kern.c
@@ -254,7 +254,7 @@
 		 * connection.  Then we loop here throwing out failed
 		 * connections until a good one is found.
 		 */
-		free_irq(TELNETD_IRQ, conn);
+		um_free_irq(TELNETD_IRQ, conn);
 
 		if (conn->fd >= 0)
 			break;
diff --git a/arch/um/drivers/xterm_kern.c b/arch/um/drivers/xterm_kern.c
index 8bd130f..b68bbe2 100644
--- a/arch/um/drivers/xterm_kern.c
+++ b/arch/um/drivers/xterm_kern.c
@@ -65,7 +65,7 @@
 	 * isn't set) this will hang... */
 	wait_for_completion(&data->ready);
 
-	free_irq(XTERM_IRQ, data);
+	um_free_irq(XTERM_IRQ, data);
 
 	ret = data->new_fd;
 	*pid_out = data->pid;
diff --git a/arch/um/include/asm/pgtable.h b/arch/um/include/asm/pgtable.h
index 6a3f984..5888f1b 100644
--- a/arch/um/include/asm/pgtable.h
+++ b/arch/um/include/asm/pgtable.h
@@ -273,6 +273,12 @@
 }
 #define set_pte_at(mm,addr,ptep,pteval) set_pte(ptep,pteval)
 
+#define __HAVE_ARCH_PTE_SAME
+static inline int pte_same(pte_t pte_a, pte_t pte_b)
+{
+	return !((pte_val(pte_a) ^ pte_val(pte_b)) & ~_PAGE_NEWPAGE);
+}
+
 /*
  * Conversion functions: convert a page and protection to a page entry,
  * and a page entry and page directory to the page they refer to.
@@ -348,11 +354,11 @@
 #define update_mmu_cache(vma,address,ptep) do ; while (0)
 
 /* Encode and de-code a swap entry */
-#define __swp_type(x)			(((x).val >> 4) & 0x3f)
+#define __swp_type(x)			(((x).val >> 5) & 0x1f)
 #define __swp_offset(x)			((x).val >> 11)
 
 #define __swp_entry(type, offset) \
-	((swp_entry_t) { ((type) << 4) | ((offset) << 11) })
+	((swp_entry_t) { ((type) << 5) | ((offset) << 11) })
 #define __pte_to_swp_entry(pte) \
 	((swp_entry_t) { pte_val(pte_mkuptodate(pte)) })
 #define __swp_entry_to_pte(x)		((pte_t) { (x).val })
diff --git a/arch/um/include/asm/processor-generic.h b/arch/um/include/asm/processor-generic.h
index 7827394..69f1c57 100644
--- a/arch/um/include/asm/processor-generic.h
+++ b/arch/um/include/asm/processor-generic.h
@@ -74,11 +74,6 @@
 
 extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
 
-static inline void prepare_to_copy(struct task_struct *tsk)
-{
-}
-
-
 extern unsigned long thread_saved_pc(struct task_struct *t);
 
 static inline void mm_copy_segments(struct mm_struct *from_mm,
diff --git a/arch/um/include/asm/thread_info.h b/arch/um/include/asm/thread_info.h
index 200c4ab..c04e5ab 100644
--- a/arch/um/include/asm/thread_info.h
+++ b/arch/um/include/asm/thread_info.h
@@ -71,6 +71,7 @@
 #define TIF_MEMDIE		5	/* is terminating due to OOM killer */
 #define TIF_SYSCALL_AUDIT	6
 #define TIF_RESTORE_SIGMASK	7
+#define TIF_NOTIFY_RESUME	8
 
 #define _TIF_SYSCALL_TRACE	(1 << TIF_SYSCALL_TRACE)
 #define _TIF_SIGPENDING		(1 << TIF_SIGPENDING)
@@ -78,6 +79,5 @@
 #define _TIF_POLLING_NRFLAG     (1 << TIF_POLLING_NRFLAG)
 #define _TIF_MEMDIE		(1 << TIF_MEMDIE)
 #define _TIF_SYSCALL_AUDIT	(1 << TIF_SYSCALL_AUDIT)
-#define _TIF_RESTORE_SIGMASK	(1 << TIF_RESTORE_SIGMASK)
 
 #endif
diff --git a/arch/um/include/shared/irq_kern.h b/arch/um/include/shared/irq_kern.h
index b05d22f..7a5bfa6 100644
--- a/arch/um/include/shared/irq_kern.h
+++ b/arch/um/include/shared/irq_kern.h
@@ -13,6 +13,6 @@
 			  irq_handler_t handler,
 			  unsigned long irqflags,  const char * devname,
 			  void *dev_id);
-
+void um_free_irq(unsigned int irq, void *dev);
 #endif
 
diff --git a/arch/um/kernel/irq.c b/arch/um/kernel/irq.c
index 71b8c94..00506c3 100644
--- a/arch/um/kernel/irq.c
+++ b/arch/um/kernel/irq.c
@@ -297,6 +297,13 @@
 	return 1;
 }
 
+void um_free_irq(unsigned int irq, void *dev)
+{
+	free_irq_by_irq_and_dev(irq, dev);
+	free_irq(irq, dev);
+}
+EXPORT_SYMBOL(um_free_irq);
+
 int um_request_irq(unsigned int irq, int fd, int type,
 		   irq_handler_t handler,
 		   unsigned long irqflags, const char * devname,
@@ -327,7 +334,6 @@
 /* This is used for everything else than the timer. */
 static struct irq_chip normal_irq_type = {
 	.name = "SIGIO",
-	.release = free_irq_by_irq_and_dev,
 	.irq_disable = dummy,
 	.irq_enable = dummy,
 	.irq_ack = dummy,
@@ -335,7 +341,6 @@
 
 static struct irq_chip SIGVTALRM_irq_type = {
 	.name = "SIGVTALRM",
-	.release = free_irq_by_irq_and_dev,
 	.irq_disable = dummy,
 	.irq_enable = dummy,
 	.irq_ack = dummy,
diff --git a/arch/um/kernel/process.c b/arch/um/kernel/process.c
index 2b73ded..3a2235e 100644
--- a/arch/um/kernel/process.c
+++ b/arch/um/kernel/process.c
@@ -18,6 +18,7 @@
 #include <linux/seq_file.h>
 #include <linux/tick.h>
 #include <linux/threads.h>
+#include <linux/tracehook.h>
 #include <asm/current.h>
 #include <asm/pgtable.h>
 #include <asm/mmu_context.h>
@@ -114,8 +115,13 @@
 {
 	if (need_resched())
 		schedule();
-	if (test_tsk_thread_flag(current, TIF_SIGPENDING))
+	if (test_thread_flag(TIF_SIGPENDING))
 		do_signal();
+	if (test_and_clear_thread_flag(TIF_NOTIFY_RESUME)) {
+		tracehook_notify_resume(&current->thread.regs);
+		if (current->replacement_session_keyring)
+			key_replace_session_keyring();
+	}
 }
 
 void exit_thread(void)
@@ -190,7 +196,7 @@
 	if (current->thread.forking) {
 	  	memcpy(&p->thread.regs.regs, &regs->regs,
 		       sizeof(p->thread.regs.regs));
-		REGS_SET_SYSCALL_RETURN(p->thread.regs.regs.gp, 0);
+		UPT_SET_SYSCALL_RETURN(&p->thread.regs.regs, 0);
 		if (sp != 0)
 			REGS_SP(p->thread.regs.regs.gp) = sp;
 
diff --git a/arch/um/kernel/signal.c b/arch/um/kernel/signal.c
index fb12f4c..187118f 100644
--- a/arch/um/kernel/signal.c
+++ b/arch/um/kernel/signal.c
@@ -29,9 +29,6 @@
 	unsigned long sp;
 	int err;
 
-	/* Always make any pending restarted system calls return -EINTR */
-	current_thread_info()->restart_block.fn = do_no_restart_syscall;
-
 	/* Did we come from a system call? */
 	if (PT_REGS_SYSCALL_NR(regs) >= 0) {
 		/* If so, check system call restarting.. */
@@ -77,15 +74,14 @@
 {
 	struct k_sigaction ka_copy;
 	siginfo_t info;
-	sigset_t *oldset;
 	int sig, handled_sig = 0;
 
-	if (test_thread_flag(TIF_RESTORE_SIGMASK))
-		oldset = &current->saved_sigmask;
-	else
-		oldset = &current->blocked;
-
 	while ((sig = get_signal_to_deliver(&info, &ka_copy, regs, NULL)) > 0) {
+		sigset_t *oldset;
+		if (test_thread_flag(TIF_RESTORE_SIGMASK))
+			oldset = &current->saved_sigmask;
+		else
+			oldset = &current->blocked;
 		handled_sig = 1;
 		/* Whee!  Actually deliver the signal.  */
 		if (!handle_signal(regs, sig, &ka_copy, &info, oldset)) {
diff --git a/arch/um/kernel/skas/syscall.c b/arch/um/kernel/skas/syscall.c
index f5173e1..05fbeb4 100644
--- a/arch/um/kernel/skas/syscall.c
+++ b/arch/um/kernel/skas/syscall.c
@@ -34,7 +34,7 @@
 		result = -ENOSYS;
 	else result = EXECUTE_SYSCALL(syscall, regs);
 
-	REGS_SET_SYSCALL_RETURN(r->gp, result);
+	UPT_SET_SYSCALL_RETURN(r, result);
 
 	syscall_trace(r, 1);
 }
diff --git a/arch/um/kernel/tlb.c b/arch/um/kernel/tlb.c
index 7f3d4d86..f819af9 100644
--- a/arch/um/kernel/tlb.c
+++ b/arch/um/kernel/tlb.c
@@ -75,6 +75,7 @@
 		default:
 			printk(KERN_ERR "Unknown op type %d in do_ops\n",
 			       op->type);
+			BUG();
 			break;
 		}
 	}
diff --git a/arch/um/os-Linux/skas/mem.c b/arch/um/os-Linux/skas/mem.c
index c0afff7..90b310d 100644
--- a/arch/um/os-Linux/skas/mem.c
+++ b/arch/um/os-Linux/skas/mem.c
@@ -48,10 +48,6 @@
 
 extern int proc_mm;
 
-int single_count = 0;
-int multi_count = 0;
-int multi_op_count = 0;
-
 static inline long do_syscall_stub(struct mm_id * mm_idp, void **addr)
 {
 	int n, i;
@@ -64,8 +60,6 @@
 		/* FIXME: Need to look up userspace_pid by cpu */
 		pid = userspace_pid[0];
 
-	multi_count++;
-
 	n = ptrace_setregs(pid, syscall_regs);
 	if (n < 0) {
 		printk(UM_KERN_ERR "Registers - \n");
@@ -126,9 +120,6 @@
 {
 	unsigned long *stack = check_init_stack(mm_idp, *addr);
 
-	if (done && *addr == NULL)
-		single_count++;
-
 	*stack += sizeof(long);
 	stack += *stack / sizeof(long);
 
@@ -141,7 +132,6 @@
 	*stack++ = args[5];
 	*stack++ = expected;
 	*stack = 0;
-	multi_op_count++;
 
 	if (!done && ((((unsigned long) stack) & ~UM_KERN_PAGE_MASK) <
 		     UM_KERN_PAGE_SIZE - 10 * sizeof(long))) {
diff --git a/arch/unicore32/include/asm/processor.h b/arch/unicore32/include/asm/processor.h
index f0d780a..14382cb 100644
--- a/arch/unicore32/include/asm/processor.h
+++ b/arch/unicore32/include/asm/processor.h
@@ -68,9 +68,6 @@
 /* Free all resources held by a thread. */
 extern void release_thread(struct task_struct *);
 
-/* Prepare to copy thread state - unlazy all lazy status */
-#define prepare_to_copy(tsk)	do { } while (0)
-
 unsigned long get_wchan(struct task_struct *p);
 
 #define cpu_relax()			barrier()
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 7b383d8..d616899 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -12,6 +12,7 @@
 
 config X86_64
 	def_bool 64BIT
+	select X86_DEV_DMA_OPS
 
 ### Arch settings
 config X86
@@ -83,6 +84,7 @@
 	select DCACHE_WORD_ACCESS
 	select GENERIC_SMP_IDLE_THREAD
 	select HAVE_ARCH_SECCOMP_FILTER
+	select BUILDTIME_EXTABLE_SORT
 
 config INSTRUCTION_DECODER
 	def_bool (KPROBES || PERF_EVENTS)
@@ -326,6 +328,7 @@
 		NUMAQ (IBM/Sequent)
 		RDC R-321x SoC
 		SGI 320/540 (Visual Workstation)
+		STA2X11-based (e.g. Northville)
 		Summit/EXA (IBM x440)
 		Unisys ES7000 IA32 series
 		Moorestown MID devices
@@ -372,6 +375,7 @@
 	select PARAVIRT
 	depends on X86_64 && PCI
 	depends on X86_EXTENDED_PLATFORM
+	depends on SMP
 	---help---
 	  Support for ScaleMP vSMP systems.  Say 'Y' here if this kernel is
 	  supposed to run on these EM64T-based machines.  Only choose this option
@@ -458,10 +462,10 @@
 	depends on X86_32 && SMP
 	depends on X86_EXTENDED_PLATFORM
 	---help---
-	  This option compiles in the NUMAQ, Summit, bigsmp, ES7000, default
-	  subarchitectures.  It is intended for a generic binary kernel.
-	  if you select them all, kernel will probe it one by one. and will
-	  fallback to default.
+	  This option compiles in the NUMAQ, Summit, bigsmp, ES7000,
+	  STA2X11, default subarchitectures.  It is intended for a generic
+	  binary kernel. If you select them all, kernel will probe it
+	  one by one and will fallback to default.
 
 # Alphabetically sorted list of Non standard 32 bit platforms
 
@@ -501,6 +505,22 @@
 	  A kernel compiled for the Visual Workstation will run on general
 	  PCs as well. See <file:Documentation/sgi-visws.txt> for details.
 
+config STA2X11
+	bool "STA2X11 Companion Chip Support"
+	depends on X86_32_NON_STANDARD && PCI
+	select X86_DEV_DMA_OPS
+	select X86_DMA_REMAP
+	select SWIOTLB
+	select MFD_STA2X11
+	select ARCH_REQUIRE_GPIOLIB
+	default n
+	---help---
+	  This adds support for boards based on the STA2X11 IO-Hub,
+	  a.k.a. "ConneXt". The chip is used in place of the standard
+	  PC chipset, so all "standard" peripherals are missing. If this
+	  option is selected the kernel will still be able to boot on
+	  standard PC machines.
+
 config X86_SUMMIT
 	bool "Summit/EXA (IBM x440)"
 	depends on X86_32_NON_STANDARD
@@ -1237,10 +1257,6 @@
 	  Specify the maximum number of NUMA Nodes available on the target
 	  system.  Increases memory reserved to accommodate various tables.
 
-config HAVE_ARCH_BOOTMEM
-	def_bool y
-	depends on X86_32 && NUMA
-
 config HAVE_ARCH_ALLOC_REMAP
 	def_bool y
 	depends on X86_32 && NUMA
@@ -2021,16 +2037,6 @@
 
 source "drivers/eisa/Kconfig"
 
-config MCA
-	bool "MCA support"
-	---help---
-	  MicroChannel Architecture is found in some IBM PS/2 machines and
-	  laptops.  It is a bus system similar to PCI or ISA. See
-	  <file:Documentation/mca.txt> (and especially the web page given
-	  there) before attempting to build an MCA bus kernel.
-
-source "drivers/mca/Kconfig"
-
 config SCx200
 	tristate "NatSemi SCx200 support"
 	---help---
@@ -2213,6 +2219,14 @@
 	bool
 	select STOP_MACHINE if SMP
 
+config X86_DEV_DMA_OPS
+	bool
+	depends on X86_64 || STA2X11
+
+config X86_DMA_REMAP
+	bool
+	depends on STA2X11
+
 source "net/Kconfig"
 
 source "drivers/Kconfig"
diff --git a/arch/x86/Makefile b/arch/x86/Makefile
index 277418f..1f25214 100644
--- a/arch/x86/Makefile
+++ b/arch/x86/Makefile
@@ -115,9 +115,10 @@
 
 # does binutils support specific instructions?
 asinstr := $(call as-instr,fxsaveq (%rax),-DCONFIG_AS_FXSAVEQ=1)
+avx_instr := $(call as-instr,vxorps %ymm0$(comma)%ymm1$(comma)%ymm2,-DCONFIG_AS_AVX=1)
 
-KBUILD_AFLAGS += $(cfi) $(cfi-sigframe) $(cfi-sections) $(asinstr)
-KBUILD_CFLAGS += $(cfi) $(cfi-sigframe) $(cfi-sections) $(asinstr)
+KBUILD_AFLAGS += $(cfi) $(cfi-sigframe) $(cfi-sections) $(asinstr) $(avx_instr)
+KBUILD_CFLAGS += $(cfi) $(cfi-sigframe) $(cfi-sections) $(asinstr) $(avx_instr)
 
 LDFLAGS := -m elf_$(UTS_MACHINE)
 
@@ -205,6 +206,7 @@
 	$(Q)rm -rf $(objtree)/arch/i386
 	$(Q)rm -rf $(objtree)/arch/x86_64
 	$(Q)$(MAKE) $(clean)=$(boot)
+	$(Q)$(MAKE) $(clean)=arch/x86/tools
 
 define archhelp
   echo  '* bzImage      - Compressed kernel image (arch/x86/boot/bzImage)'
diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index 0cdfc0d..2c14e76 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -904,11 +904,19 @@
 
 	memset(boot_params, 0x0, 0x4000);
 
-	/* Copy first two sectors to boot_params */
-	memcpy(boot_params, image->image_base, 1024);
-
 	hdr = &boot_params->hdr;
 
+	/* Copy the second sector to boot_params */
+	memcpy(&hdr->jump, image->image_base + 512, 512);
+
+	/*
+	 * Fill out some of the header fields ourselves because the
+	 * EFI firmware loader doesn't load the first sector.
+	 */
+	hdr->root_flags = 1;
+	hdr->vid_mode = 0xffff;
+	hdr->boot_flag = 0xAA55;
+
 	/*
 	 * The EFI firmware loader could have placed the kernel image
 	 * anywhere in memory, but the kernel has various restrictions
diff --git a/arch/x86/boot/header.S b/arch/x86/boot/header.S
index f1bbeeb..8bbea6a 100644
--- a/arch/x86/boot/header.S
+++ b/arch/x86/boot/header.S
@@ -147,7 +147,7 @@
 	# Filled in by build.c
 	.long	0x0000				# AddressOfEntryPoint
 
-	.long	0x0000				# BaseOfCode
+	.long	0x0200				# BaseOfCode
 #ifdef CONFIG_X86_32
 	.long	0				# data
 #endif
@@ -189,7 +189,7 @@
 	.quad	0				# SizeOfHeapCommit
 #endif
 	.long	0				# LoaderFlags
-	.long	0x1				# NumberOfRvaAndSizes
+	.long	0x6				# NumberOfRvaAndSizes
 
 	.quad	0				# ExportTable
 	.quad	0				# ImportTable
@@ -217,18 +217,17 @@
 
 	#
 	# The EFI application loader requires a relocation section
-	# because EFI applications are relocatable and not having
-	# this section seems to confuse it. But since we don't need
-	# the loader to fixup any relocs for us just fill it with a
-	# single dummy reloc.
+	# because EFI applications must be relocatable. But since
+	# we don't need the loader to fixup any relocs for us, we
+	# just create an empty (zero-length) .reloc section header.
 	#
 	.ascii	".reloc"
 	.byte	0
 	.byte	0
-	.long	reloc_end - reloc_start
-	.long	reloc_start
-	.long	reloc_end - reloc_start		# SizeOfRawData
-	.long	reloc_start			# PointerToRawData
+	.long	0
+	.long	0
+	.long	0				# SizeOfRawData
+	.long	0				# PointerToRawData
 	.long	0				# PointerToRelocations
 	.long	0				# PointerToLineNumbers
 	.word	0				# NumberOfRelocations
@@ -469,10 +468,3 @@
 
 	.data
 dummy:	.long	0
-
-	.section .reloc
-reloc_start:
-	.long	dummy - reloc_start
-	.long	10
-	.word	0
-reloc_end:
diff --git a/arch/x86/boot/main.c b/arch/x86/boot/main.c
index 40358c8..cf6083d 100644
--- a/arch/x86/boot/main.c
+++ b/arch/x86/boot/main.c
@@ -57,14 +57,20 @@
 }
 
 /*
- * Set the keyboard repeat rate to maximum.  Unclear why this
+ * Query the keyboard lock status as given by the BIOS, and
+ * set the keyboard repeat rate to maximum.  Unclear why the latter
  * is done here; this might be possible to kill off as stale code.
  */
-static void keyboard_set_repeat(void)
+static void keyboard_init(void)
 {
-	struct biosregs ireg;
+	struct biosregs ireg, oreg;
 	initregs(&ireg);
-	ireg.ax = 0x0305;
+
+	ireg.ah = 0x02;		/* Get keyboard status */
+	intcall(0x16, &ireg, &oreg);
+	boot_params.kbd_status = oreg.al;
+
+	ireg.ax = 0x0305;	/* Set keyboard repeat rate */
 	intcall(0x16, &ireg, NULL);
 }
 
@@ -151,8 +157,8 @@
 	/* Detect memory layout */
 	detect_memory();
 
-	/* Set keyboard repeat rate (why?) */
-	keyboard_set_repeat();
+	/* Set keyboard repeat rate (why?) and query the lock flags */
+	keyboard_init();
 
 	/* Query MCA information */
 	query_mca();
diff --git a/arch/x86/boot/tools/build.c b/arch/x86/boot/tools/build.c
index 24443a3..3f61f6e 100644
--- a/arch/x86/boot/tools/build.c
+++ b/arch/x86/boot/tools/build.c
@@ -198,12 +198,19 @@
 
 	pe_header = get_unaligned_le32(&buf[0x3c]);
 
-	/* Size of code */
-	put_unaligned_le32(file_sz, &buf[pe_header + 0x1c]);
-
 	/* Size of image */
 	put_unaligned_le32(file_sz, &buf[pe_header + 0x50]);
 
+	/*
+	 * Subtract the size of the first section (512 bytes) which
+	 * includes the header and .reloc section. The remaining size
+	 * is that of the .text section.
+	 */
+	file_sz -= 512;
+
+	/* Size of code */
+	put_unaligned_le32(file_sz, &buf[pe_header + 0x1c]);
+
 #ifdef CONFIG_X86_32
 	/*
 	 * Address of entry point.
@@ -216,8 +223,14 @@
 	/* .text size */
 	put_unaligned_le32(file_sz, &buf[pe_header + 0xb0]);
 
+	/* .text vma */
+	put_unaligned_le32(0x200, &buf[pe_header + 0xb4]);
+
 	/* .text size of initialised data */
 	put_unaligned_le32(file_sz, &buf[pe_header + 0xb8]);
+
+	/* .text file offset */
+	put_unaligned_le32(0x200, &buf[pe_header + 0xbc]);
 #else
 	/*
 	 * Address of entry point. startup_32 is at the beginning and
@@ -231,9 +244,14 @@
 	/* .text size */
 	put_unaligned_le32(file_sz, &buf[pe_header + 0xc0]);
 
+	/* .text vma */
+	put_unaligned_le32(0x200, &buf[pe_header + 0xc4]);
+
 	/* .text size of initialised data */
 	put_unaligned_le32(file_sz, &buf[pe_header + 0xc8]);
 
+	/* .text file offset */
+	put_unaligned_le32(0x200, &buf[pe_header + 0xcc]);
 #endif /* CONFIG_X86_32 */
 #endif /* CONFIG_EFI_STUB */
 
diff --git a/arch/x86/crypto/aesni-intel_glue.c b/arch/x86/crypto/aesni-intel_glue.c
index c799352..ac7f5cd 100644
--- a/arch/x86/crypto/aesni-intel_glue.c
+++ b/arch/x86/crypto/aesni-intel_glue.c
@@ -222,27 +222,6 @@
 	}
 }
 
-static struct crypto_alg aesni_alg = {
-	.cra_name		= "aes",
-	.cra_driver_name	= "aes-aesni",
-	.cra_priority		= 300,
-	.cra_flags		= CRYPTO_ALG_TYPE_CIPHER,
-	.cra_blocksize		= AES_BLOCK_SIZE,
-	.cra_ctxsize		= sizeof(struct crypto_aes_ctx)+AESNI_ALIGN-1,
-	.cra_alignmask		= 0,
-	.cra_module		= THIS_MODULE,
-	.cra_list		= LIST_HEAD_INIT(aesni_alg.cra_list),
-	.cra_u	= {
-		.cipher	= {
-			.cia_min_keysize	= AES_MIN_KEY_SIZE,
-			.cia_max_keysize	= AES_MAX_KEY_SIZE,
-			.cia_setkey		= aes_set_key,
-			.cia_encrypt		= aes_encrypt,
-			.cia_decrypt		= aes_decrypt
-		}
-	}
-};
-
 static void __aes_encrypt(struct crypto_tfm *tfm, u8 *dst, const u8 *src)
 {
 	struct crypto_aes_ctx *ctx = aes_ctx(crypto_tfm_ctx(tfm));
@@ -257,27 +236,6 @@
 	aesni_dec(ctx, dst, src);
 }
 
-static struct crypto_alg __aesni_alg = {
-	.cra_name		= "__aes-aesni",
-	.cra_driver_name	= "__driver-aes-aesni",
-	.cra_priority		= 0,
-	.cra_flags		= CRYPTO_ALG_TYPE_CIPHER,
-	.cra_blocksize		= AES_BLOCK_SIZE,
-	.cra_ctxsize		= sizeof(struct crypto_aes_ctx)+AESNI_ALIGN-1,
-	.cra_alignmask		= 0,
-	.cra_module		= THIS_MODULE,
-	.cra_list		= LIST_HEAD_INIT(__aesni_alg.cra_list),
-	.cra_u	= {
-		.cipher	= {
-			.cia_min_keysize	= AES_MIN_KEY_SIZE,
-			.cia_max_keysize	= AES_MAX_KEY_SIZE,
-			.cia_setkey		= aes_set_key,
-			.cia_encrypt		= __aes_encrypt,
-			.cia_decrypt		= __aes_decrypt
-		}
-	}
-};
-
 static int ecb_encrypt(struct blkcipher_desc *desc,
 		       struct scatterlist *dst, struct scatterlist *src,
 		       unsigned int nbytes)
@@ -326,28 +284,6 @@
 	return err;
 }
 
-static struct crypto_alg blk_ecb_alg = {
-	.cra_name		= "__ecb-aes-aesni",
-	.cra_driver_name	= "__driver-ecb-aes-aesni",
-	.cra_priority		= 0,
-	.cra_flags		= CRYPTO_ALG_TYPE_BLKCIPHER,
-	.cra_blocksize		= AES_BLOCK_SIZE,
-	.cra_ctxsize		= sizeof(struct crypto_aes_ctx)+AESNI_ALIGN-1,
-	.cra_alignmask		= 0,
-	.cra_type		= &crypto_blkcipher_type,
-	.cra_module		= THIS_MODULE,
-	.cra_list		= LIST_HEAD_INIT(blk_ecb_alg.cra_list),
-	.cra_u = {
-		.blkcipher = {
-			.min_keysize	= AES_MIN_KEY_SIZE,
-			.max_keysize	= AES_MAX_KEY_SIZE,
-			.setkey		= aes_set_key,
-			.encrypt	= ecb_encrypt,
-			.decrypt	= ecb_decrypt,
-		},
-	},
-};
-
 static int cbc_encrypt(struct blkcipher_desc *desc,
 		       struct scatterlist *dst, struct scatterlist *src,
 		       unsigned int nbytes)
@@ -396,28 +332,6 @@
 	return err;
 }
 
-static struct crypto_alg blk_cbc_alg = {
-	.cra_name		= "__cbc-aes-aesni",
-	.cra_driver_name	= "__driver-cbc-aes-aesni",
-	.cra_priority		= 0,
-	.cra_flags		= CRYPTO_ALG_TYPE_BLKCIPHER,
-	.cra_blocksize		= AES_BLOCK_SIZE,
-	.cra_ctxsize		= sizeof(struct crypto_aes_ctx)+AESNI_ALIGN-1,
-	.cra_alignmask		= 0,
-	.cra_type		= &crypto_blkcipher_type,
-	.cra_module		= THIS_MODULE,
-	.cra_list		= LIST_HEAD_INIT(blk_cbc_alg.cra_list),
-	.cra_u = {
-		.blkcipher = {
-			.min_keysize	= AES_MIN_KEY_SIZE,
-			.max_keysize	= AES_MAX_KEY_SIZE,
-			.setkey		= aes_set_key,
-			.encrypt	= cbc_encrypt,
-			.decrypt	= cbc_decrypt,
-		},
-	},
-};
-
 #ifdef CONFIG_X86_64
 static void ctr_crypt_final(struct crypto_aes_ctx *ctx,
 			    struct blkcipher_walk *walk)
@@ -461,29 +375,6 @@
 
 	return err;
 }
-
-static struct crypto_alg blk_ctr_alg = {
-	.cra_name		= "__ctr-aes-aesni",
-	.cra_driver_name	= "__driver-ctr-aes-aesni",
-	.cra_priority		= 0,
-	.cra_flags		= CRYPTO_ALG_TYPE_BLKCIPHER,
-	.cra_blocksize		= 1,
-	.cra_ctxsize		= sizeof(struct crypto_aes_ctx)+AESNI_ALIGN-1,
-	.cra_alignmask		= 0,
-	.cra_type		= &crypto_blkcipher_type,
-	.cra_module		= THIS_MODULE,
-	.cra_list		= LIST_HEAD_INIT(blk_ctr_alg.cra_list),
-	.cra_u = {
-		.blkcipher = {
-			.min_keysize	= AES_MIN_KEY_SIZE,
-			.max_keysize	= AES_MAX_KEY_SIZE,
-			.ivsize		= AES_BLOCK_SIZE,
-			.setkey		= aes_set_key,
-			.encrypt	= ctr_crypt,
-			.decrypt	= ctr_crypt,
-		},
-	},
-};
 #endif
 
 static int ablk_set_key(struct crypto_ablkcipher *tfm, const u8 *key,
@@ -551,281 +442,65 @@
 	cryptd_free_ablkcipher(ctx->cryptd_tfm);
 }
 
-static void ablk_init_common(struct crypto_tfm *tfm,
-			     struct cryptd_ablkcipher *cryptd_tfm)
+static int ablk_init_common(struct crypto_tfm *tfm, const char *drv_name)
 {
 	struct async_aes_ctx *ctx = crypto_tfm_ctx(tfm);
+	struct cryptd_ablkcipher *cryptd_tfm;
+
+	cryptd_tfm = cryptd_alloc_ablkcipher(drv_name, 0, 0);
+	if (IS_ERR(cryptd_tfm))
+		return PTR_ERR(cryptd_tfm);
 
 	ctx->cryptd_tfm = cryptd_tfm;
 	tfm->crt_ablkcipher.reqsize = sizeof(struct ablkcipher_request) +
 		crypto_ablkcipher_reqsize(&cryptd_tfm->base);
+
+	return 0;
 }
 
 static int ablk_ecb_init(struct crypto_tfm *tfm)
 {
-	struct cryptd_ablkcipher *cryptd_tfm;
-
-	cryptd_tfm = cryptd_alloc_ablkcipher("__driver-ecb-aes-aesni", 0, 0);
-	if (IS_ERR(cryptd_tfm))
-		return PTR_ERR(cryptd_tfm);
-	ablk_init_common(tfm, cryptd_tfm);
-	return 0;
+	return ablk_init_common(tfm, "__driver-ecb-aes-aesni");
 }
 
-static struct crypto_alg ablk_ecb_alg = {
-	.cra_name		= "ecb(aes)",
-	.cra_driver_name	= "ecb-aes-aesni",
-	.cra_priority		= 400,
-	.cra_flags		= CRYPTO_ALG_TYPE_ABLKCIPHER|CRYPTO_ALG_ASYNC,
-	.cra_blocksize		= AES_BLOCK_SIZE,
-	.cra_ctxsize		= sizeof(struct async_aes_ctx),
-	.cra_alignmask		= 0,
-	.cra_type		= &crypto_ablkcipher_type,
-	.cra_module		= THIS_MODULE,
-	.cra_list		= LIST_HEAD_INIT(ablk_ecb_alg.cra_list),
-	.cra_init		= ablk_ecb_init,
-	.cra_exit		= ablk_exit,
-	.cra_u = {
-		.ablkcipher = {
-			.min_keysize	= AES_MIN_KEY_SIZE,
-			.max_keysize	= AES_MAX_KEY_SIZE,
-			.setkey		= ablk_set_key,
-			.encrypt	= ablk_encrypt,
-			.decrypt	= ablk_decrypt,
-		},
-	},
-};
-
 static int ablk_cbc_init(struct crypto_tfm *tfm)
 {
-	struct cryptd_ablkcipher *cryptd_tfm;
-
-	cryptd_tfm = cryptd_alloc_ablkcipher("__driver-cbc-aes-aesni", 0, 0);
-	if (IS_ERR(cryptd_tfm))
-		return PTR_ERR(cryptd_tfm);
-	ablk_init_common(tfm, cryptd_tfm);
-	return 0;
+	return ablk_init_common(tfm, "__driver-cbc-aes-aesni");
 }
 
-static struct crypto_alg ablk_cbc_alg = {
-	.cra_name		= "cbc(aes)",
-	.cra_driver_name	= "cbc-aes-aesni",
-	.cra_priority		= 400,
-	.cra_flags		= CRYPTO_ALG_TYPE_ABLKCIPHER|CRYPTO_ALG_ASYNC,
-	.cra_blocksize		= AES_BLOCK_SIZE,
-	.cra_ctxsize		= sizeof(struct async_aes_ctx),
-	.cra_alignmask		= 0,
-	.cra_type		= &crypto_ablkcipher_type,
-	.cra_module		= THIS_MODULE,
-	.cra_list		= LIST_HEAD_INIT(ablk_cbc_alg.cra_list),
-	.cra_init		= ablk_cbc_init,
-	.cra_exit		= ablk_exit,
-	.cra_u = {
-		.ablkcipher = {
-			.min_keysize	= AES_MIN_KEY_SIZE,
-			.max_keysize	= AES_MAX_KEY_SIZE,
-			.ivsize		= AES_BLOCK_SIZE,
-			.setkey		= ablk_set_key,
-			.encrypt	= ablk_encrypt,
-			.decrypt	= ablk_decrypt,
-		},
-	},
-};
-
 #ifdef CONFIG_X86_64
 static int ablk_ctr_init(struct crypto_tfm *tfm)
 {
-	struct cryptd_ablkcipher *cryptd_tfm;
-
-	cryptd_tfm = cryptd_alloc_ablkcipher("__driver-ctr-aes-aesni", 0, 0);
-	if (IS_ERR(cryptd_tfm))
-		return PTR_ERR(cryptd_tfm);
-	ablk_init_common(tfm, cryptd_tfm);
-	return 0;
+	return ablk_init_common(tfm, "__driver-ctr-aes-aesni");
 }
 
-static struct crypto_alg ablk_ctr_alg = {
-	.cra_name		= "ctr(aes)",
-	.cra_driver_name	= "ctr-aes-aesni",
-	.cra_priority		= 400,
-	.cra_flags		= CRYPTO_ALG_TYPE_ABLKCIPHER|CRYPTO_ALG_ASYNC,
-	.cra_blocksize		= 1,
-	.cra_ctxsize		= sizeof(struct async_aes_ctx),
-	.cra_alignmask		= 0,
-	.cra_type		= &crypto_ablkcipher_type,
-	.cra_module		= THIS_MODULE,
-	.cra_list		= LIST_HEAD_INIT(ablk_ctr_alg.cra_list),
-	.cra_init		= ablk_ctr_init,
-	.cra_exit		= ablk_exit,
-	.cra_u = {
-		.ablkcipher = {
-			.min_keysize	= AES_MIN_KEY_SIZE,
-			.max_keysize	= AES_MAX_KEY_SIZE,
-			.ivsize		= AES_BLOCK_SIZE,
-			.setkey		= ablk_set_key,
-			.encrypt	= ablk_encrypt,
-			.decrypt	= ablk_encrypt,
-			.geniv		= "chainiv",
-		},
-	},
-};
-
 #ifdef HAS_CTR
 static int ablk_rfc3686_ctr_init(struct crypto_tfm *tfm)
 {
-	struct cryptd_ablkcipher *cryptd_tfm;
-
-	cryptd_tfm = cryptd_alloc_ablkcipher(
-		"rfc3686(__driver-ctr-aes-aesni)", 0, 0);
-	if (IS_ERR(cryptd_tfm))
-		return PTR_ERR(cryptd_tfm);
-	ablk_init_common(tfm, cryptd_tfm);
-	return 0;
+	return ablk_init_common(tfm, "rfc3686(__driver-ctr-aes-aesni)");
 }
-
-static struct crypto_alg ablk_rfc3686_ctr_alg = {
-	.cra_name		= "rfc3686(ctr(aes))",
-	.cra_driver_name	= "rfc3686-ctr-aes-aesni",
-	.cra_priority		= 400,
-	.cra_flags		= CRYPTO_ALG_TYPE_ABLKCIPHER|CRYPTO_ALG_ASYNC,
-	.cra_blocksize		= 1,
-	.cra_ctxsize		= sizeof(struct async_aes_ctx),
-	.cra_alignmask		= 0,
-	.cra_type		= &crypto_ablkcipher_type,
-	.cra_module		= THIS_MODULE,
-	.cra_list		= LIST_HEAD_INIT(ablk_rfc3686_ctr_alg.cra_list),
-	.cra_init		= ablk_rfc3686_ctr_init,
-	.cra_exit		= ablk_exit,
-	.cra_u = {
-		.ablkcipher = {
-			.min_keysize = AES_MIN_KEY_SIZE+CTR_RFC3686_NONCE_SIZE,
-			.max_keysize = AES_MAX_KEY_SIZE+CTR_RFC3686_NONCE_SIZE,
-			.ivsize	     = CTR_RFC3686_IV_SIZE,
-			.setkey	     = ablk_set_key,
-			.encrypt     = ablk_encrypt,
-			.decrypt     = ablk_decrypt,
-			.geniv	     = "seqiv",
-		},
-	},
-};
 #endif
 #endif
 
 #ifdef HAS_LRW
 static int ablk_lrw_init(struct crypto_tfm *tfm)
 {
-	struct cryptd_ablkcipher *cryptd_tfm;
-
-	cryptd_tfm = cryptd_alloc_ablkcipher("fpu(lrw(__driver-aes-aesni))",
-					     0, 0);
-	if (IS_ERR(cryptd_tfm))
-		return PTR_ERR(cryptd_tfm);
-	ablk_init_common(tfm, cryptd_tfm);
-	return 0;
+	return ablk_init_common(tfm, "fpu(lrw(__driver-aes-aesni))");
 }
-
-static struct crypto_alg ablk_lrw_alg = {
-	.cra_name		= "lrw(aes)",
-	.cra_driver_name	= "lrw-aes-aesni",
-	.cra_priority		= 400,
-	.cra_flags		= CRYPTO_ALG_TYPE_ABLKCIPHER|CRYPTO_ALG_ASYNC,
-	.cra_blocksize		= AES_BLOCK_SIZE,
-	.cra_ctxsize		= sizeof(struct async_aes_ctx),
-	.cra_alignmask		= 0,
-	.cra_type		= &crypto_ablkcipher_type,
-	.cra_module		= THIS_MODULE,
-	.cra_list		= LIST_HEAD_INIT(ablk_lrw_alg.cra_list),
-	.cra_init		= ablk_lrw_init,
-	.cra_exit		= ablk_exit,
-	.cra_u = {
-		.ablkcipher = {
-			.min_keysize	= AES_MIN_KEY_SIZE + AES_BLOCK_SIZE,
-			.max_keysize	= AES_MAX_KEY_SIZE + AES_BLOCK_SIZE,
-			.ivsize		= AES_BLOCK_SIZE,
-			.setkey		= ablk_set_key,
-			.encrypt	= ablk_encrypt,
-			.decrypt	= ablk_decrypt,
-		},
-	},
-};
 #endif
 
 #ifdef HAS_PCBC
 static int ablk_pcbc_init(struct crypto_tfm *tfm)
 {
-	struct cryptd_ablkcipher *cryptd_tfm;
-
-	cryptd_tfm = cryptd_alloc_ablkcipher("fpu(pcbc(__driver-aes-aesni))",
-					     0, 0);
-	if (IS_ERR(cryptd_tfm))
-		return PTR_ERR(cryptd_tfm);
-	ablk_init_common(tfm, cryptd_tfm);
-	return 0;
+	return ablk_init_common(tfm, "fpu(pcbc(__driver-aes-aesni))");
 }
-
-static struct crypto_alg ablk_pcbc_alg = {
-	.cra_name		= "pcbc(aes)",
-	.cra_driver_name	= "pcbc-aes-aesni",
-	.cra_priority		= 400,
-	.cra_flags		= CRYPTO_ALG_TYPE_ABLKCIPHER|CRYPTO_ALG_ASYNC,
-	.cra_blocksize		= AES_BLOCK_SIZE,
-	.cra_ctxsize		= sizeof(struct async_aes_ctx),
-	.cra_alignmask		= 0,
-	.cra_type		= &crypto_ablkcipher_type,
-	.cra_module		= THIS_MODULE,
-	.cra_list		= LIST_HEAD_INIT(ablk_pcbc_alg.cra_list),
-	.cra_init		= ablk_pcbc_init,
-	.cra_exit		= ablk_exit,
-	.cra_u = {
-		.ablkcipher = {
-			.min_keysize	= AES_MIN_KEY_SIZE,
-			.max_keysize	= AES_MAX_KEY_SIZE,
-			.ivsize		= AES_BLOCK_SIZE,
-			.setkey		= ablk_set_key,
-			.encrypt	= ablk_encrypt,
-			.decrypt	= ablk_decrypt,
-		},
-	},
-};
 #endif
 
 #ifdef HAS_XTS
 static int ablk_xts_init(struct crypto_tfm *tfm)
 {
-	struct cryptd_ablkcipher *cryptd_tfm;
-
-	cryptd_tfm = cryptd_alloc_ablkcipher("fpu(xts(__driver-aes-aesni))",
-					     0, 0);
-	if (IS_ERR(cryptd_tfm))
-		return PTR_ERR(cryptd_tfm);
-	ablk_init_common(tfm, cryptd_tfm);
-	return 0;
+	return ablk_init_common(tfm, "fpu(xts(__driver-aes-aesni))");
 }
-
-static struct crypto_alg ablk_xts_alg = {
-	.cra_name		= "xts(aes)",
-	.cra_driver_name	= "xts-aes-aesni",
-	.cra_priority		= 400,
-	.cra_flags		= CRYPTO_ALG_TYPE_ABLKCIPHER|CRYPTO_ALG_ASYNC,
-	.cra_blocksize		= AES_BLOCK_SIZE,
-	.cra_ctxsize		= sizeof(struct async_aes_ctx),
-	.cra_alignmask		= 0,
-	.cra_type		= &crypto_ablkcipher_type,
-	.cra_module		= THIS_MODULE,
-	.cra_list		= LIST_HEAD_INIT(ablk_xts_alg.cra_list),
-	.cra_init		= ablk_xts_init,
-	.cra_exit		= ablk_exit,
-	.cra_u = {
-		.ablkcipher = {
-			.min_keysize	= 2 * AES_MIN_KEY_SIZE,
-			.max_keysize	= 2 * AES_MAX_KEY_SIZE,
-			.ivsize		= AES_BLOCK_SIZE,
-			.setkey		= ablk_set_key,
-			.encrypt	= ablk_encrypt,
-			.decrypt	= ablk_decrypt,
-		},
-	},
-};
 #endif
 
 #ifdef CONFIG_X86_64
@@ -1050,32 +725,6 @@
 	}
 }
 
-static struct crypto_alg rfc4106_alg = {
-	.cra_name = "rfc4106(gcm(aes))",
-	.cra_driver_name = "rfc4106-gcm-aesni",
-	.cra_priority = 400,
-	.cra_flags = CRYPTO_ALG_TYPE_AEAD | CRYPTO_ALG_ASYNC,
-	.cra_blocksize = 1,
-	.cra_ctxsize = sizeof(struct aesni_rfc4106_gcm_ctx) + AESNI_ALIGN,
-	.cra_alignmask = 0,
-	.cra_type = &crypto_nivaead_type,
-	.cra_module = THIS_MODULE,
-	.cra_list = LIST_HEAD_INIT(rfc4106_alg.cra_list),
-	.cra_init = rfc4106_init,
-	.cra_exit = rfc4106_exit,
-	.cra_u = {
-		.aead = {
-			.setkey = rfc4106_set_key,
-			.setauthsize = rfc4106_set_authsize,
-			.encrypt = rfc4106_encrypt,
-			.decrypt = rfc4106_decrypt,
-			.geniv = "seqiv",
-			.ivsize = 8,
-			.maxauthsize = 16,
-		},
-	},
-};
-
 static int __driver_rfc4106_encrypt(struct aead_request *req)
 {
 	u8 one_entry_in_sg = 0;
@@ -1233,26 +882,316 @@
 	}
 	return retval;
 }
+#endif
 
-static struct crypto_alg __rfc4106_alg = {
+static struct crypto_alg aesni_algs[] = { {
+	.cra_name		= "aes",
+	.cra_driver_name	= "aes-aesni",
+	.cra_priority		= 300,
+	.cra_flags		= CRYPTO_ALG_TYPE_CIPHER,
+	.cra_blocksize		= AES_BLOCK_SIZE,
+	.cra_ctxsize		= sizeof(struct crypto_aes_ctx) +
+				  AESNI_ALIGN - 1,
+	.cra_alignmask		= 0,
+	.cra_module		= THIS_MODULE,
+	.cra_u	= {
+		.cipher	= {
+			.cia_min_keysize	= AES_MIN_KEY_SIZE,
+			.cia_max_keysize	= AES_MAX_KEY_SIZE,
+			.cia_setkey		= aes_set_key,
+			.cia_encrypt		= aes_encrypt,
+			.cia_decrypt		= aes_decrypt
+		}
+	}
+}, {
+	.cra_name		= "__aes-aesni",
+	.cra_driver_name	= "__driver-aes-aesni",
+	.cra_priority		= 0,
+	.cra_flags		= CRYPTO_ALG_TYPE_CIPHER,
+	.cra_blocksize		= AES_BLOCK_SIZE,
+	.cra_ctxsize		= sizeof(struct crypto_aes_ctx) +
+				  AESNI_ALIGN - 1,
+	.cra_alignmask		= 0,
+	.cra_module		= THIS_MODULE,
+	.cra_u	= {
+		.cipher	= {
+			.cia_min_keysize	= AES_MIN_KEY_SIZE,
+			.cia_max_keysize	= AES_MAX_KEY_SIZE,
+			.cia_setkey		= aes_set_key,
+			.cia_encrypt		= __aes_encrypt,
+			.cia_decrypt		= __aes_decrypt
+		}
+	}
+}, {
+	.cra_name		= "__ecb-aes-aesni",
+	.cra_driver_name	= "__driver-ecb-aes-aesni",
+	.cra_priority		= 0,
+	.cra_flags		= CRYPTO_ALG_TYPE_BLKCIPHER,
+	.cra_blocksize		= AES_BLOCK_SIZE,
+	.cra_ctxsize		= sizeof(struct crypto_aes_ctx) +
+				  AESNI_ALIGN - 1,
+	.cra_alignmask		= 0,
+	.cra_type		= &crypto_blkcipher_type,
+	.cra_module		= THIS_MODULE,
+	.cra_u = {
+		.blkcipher = {
+			.min_keysize	= AES_MIN_KEY_SIZE,
+			.max_keysize	= AES_MAX_KEY_SIZE,
+			.setkey		= aes_set_key,
+			.encrypt	= ecb_encrypt,
+			.decrypt	= ecb_decrypt,
+		},
+	},
+}, {
+	.cra_name		= "__cbc-aes-aesni",
+	.cra_driver_name	= "__driver-cbc-aes-aesni",
+	.cra_priority		= 0,
+	.cra_flags		= CRYPTO_ALG_TYPE_BLKCIPHER,
+	.cra_blocksize		= AES_BLOCK_SIZE,
+	.cra_ctxsize		= sizeof(struct crypto_aes_ctx) +
+				  AESNI_ALIGN - 1,
+	.cra_alignmask		= 0,
+	.cra_type		= &crypto_blkcipher_type,
+	.cra_module		= THIS_MODULE,
+	.cra_u = {
+		.blkcipher = {
+			.min_keysize	= AES_MIN_KEY_SIZE,
+			.max_keysize	= AES_MAX_KEY_SIZE,
+			.setkey		= aes_set_key,
+			.encrypt	= cbc_encrypt,
+			.decrypt	= cbc_decrypt,
+		},
+	},
+}, {
+	.cra_name		= "ecb(aes)",
+	.cra_driver_name	= "ecb-aes-aesni",
+	.cra_priority		= 400,
+	.cra_flags		= CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
+	.cra_blocksize		= AES_BLOCK_SIZE,
+	.cra_ctxsize		= sizeof(struct async_aes_ctx),
+	.cra_alignmask		= 0,
+	.cra_type		= &crypto_ablkcipher_type,
+	.cra_module		= THIS_MODULE,
+	.cra_init		= ablk_ecb_init,
+	.cra_exit		= ablk_exit,
+	.cra_u = {
+		.ablkcipher = {
+			.min_keysize	= AES_MIN_KEY_SIZE,
+			.max_keysize	= AES_MAX_KEY_SIZE,
+			.setkey		= ablk_set_key,
+			.encrypt	= ablk_encrypt,
+			.decrypt	= ablk_decrypt,
+		},
+	},
+}, {
+	.cra_name		= "cbc(aes)",
+	.cra_driver_name	= "cbc-aes-aesni",
+	.cra_priority		= 400,
+	.cra_flags		= CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
+	.cra_blocksize		= AES_BLOCK_SIZE,
+	.cra_ctxsize		= sizeof(struct async_aes_ctx),
+	.cra_alignmask		= 0,
+	.cra_type		= &crypto_ablkcipher_type,
+	.cra_module		= THIS_MODULE,
+	.cra_init		= ablk_cbc_init,
+	.cra_exit		= ablk_exit,
+	.cra_u = {
+		.ablkcipher = {
+			.min_keysize	= AES_MIN_KEY_SIZE,
+			.max_keysize	= AES_MAX_KEY_SIZE,
+			.ivsize		= AES_BLOCK_SIZE,
+			.setkey		= ablk_set_key,
+			.encrypt	= ablk_encrypt,
+			.decrypt	= ablk_decrypt,
+		},
+	},
+#ifdef CONFIG_X86_64
+}, {
+	.cra_name		= "__ctr-aes-aesni",
+	.cra_driver_name	= "__driver-ctr-aes-aesni",
+	.cra_priority		= 0,
+	.cra_flags		= CRYPTO_ALG_TYPE_BLKCIPHER,
+	.cra_blocksize		= 1,
+	.cra_ctxsize		= sizeof(struct crypto_aes_ctx) +
+				  AESNI_ALIGN - 1,
+	.cra_alignmask		= 0,
+	.cra_type		= &crypto_blkcipher_type,
+	.cra_module		= THIS_MODULE,
+	.cra_u = {
+		.blkcipher = {
+			.min_keysize	= AES_MIN_KEY_SIZE,
+			.max_keysize	= AES_MAX_KEY_SIZE,
+			.ivsize		= AES_BLOCK_SIZE,
+			.setkey		= aes_set_key,
+			.encrypt	= ctr_crypt,
+			.decrypt	= ctr_crypt,
+		},
+	},
+}, {
+	.cra_name		= "ctr(aes)",
+	.cra_driver_name	= "ctr-aes-aesni",
+	.cra_priority		= 400,
+	.cra_flags		= CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
+	.cra_blocksize		= 1,
+	.cra_ctxsize		= sizeof(struct async_aes_ctx),
+	.cra_alignmask		= 0,
+	.cra_type		= &crypto_ablkcipher_type,
+	.cra_module		= THIS_MODULE,
+	.cra_init		= ablk_ctr_init,
+	.cra_exit		= ablk_exit,
+	.cra_u = {
+		.ablkcipher = {
+			.min_keysize	= AES_MIN_KEY_SIZE,
+			.max_keysize	= AES_MAX_KEY_SIZE,
+			.ivsize		= AES_BLOCK_SIZE,
+			.setkey		= ablk_set_key,
+			.encrypt	= ablk_encrypt,
+			.decrypt	= ablk_encrypt,
+			.geniv		= "chainiv",
+		},
+	},
+}, {
 	.cra_name		= "__gcm-aes-aesni",
 	.cra_driver_name	= "__driver-gcm-aes-aesni",
 	.cra_priority		= 0,
 	.cra_flags		= CRYPTO_ALG_TYPE_AEAD,
 	.cra_blocksize		= 1,
-	.cra_ctxsize	= sizeof(struct aesni_rfc4106_gcm_ctx) + AESNI_ALIGN,
+	.cra_ctxsize		= sizeof(struct aesni_rfc4106_gcm_ctx) +
+				  AESNI_ALIGN,
 	.cra_alignmask		= 0,
 	.cra_type		= &crypto_aead_type,
 	.cra_module		= THIS_MODULE,
-	.cra_list		= LIST_HEAD_INIT(__rfc4106_alg.cra_list),
 	.cra_u = {
 		.aead = {
 			.encrypt	= __driver_rfc4106_encrypt,
 			.decrypt	= __driver_rfc4106_decrypt,
 		},
 	},
-};
+}, {
+	.cra_name		= "rfc4106(gcm(aes))",
+	.cra_driver_name	= "rfc4106-gcm-aesni",
+	.cra_priority		= 400,
+	.cra_flags		= CRYPTO_ALG_TYPE_AEAD | CRYPTO_ALG_ASYNC,
+	.cra_blocksize		= 1,
+	.cra_ctxsize		= sizeof(struct aesni_rfc4106_gcm_ctx) +
+				  AESNI_ALIGN,
+	.cra_alignmask		= 0,
+	.cra_type		= &crypto_nivaead_type,
+	.cra_module		= THIS_MODULE,
+	.cra_init		= rfc4106_init,
+	.cra_exit		= rfc4106_exit,
+	.cra_u = {
+		.aead = {
+			.setkey		= rfc4106_set_key,
+			.setauthsize	= rfc4106_set_authsize,
+			.encrypt	= rfc4106_encrypt,
+			.decrypt	= rfc4106_decrypt,
+			.geniv		= "seqiv",
+			.ivsize		= 8,
+			.maxauthsize	= 16,
+		},
+	},
+#ifdef HAS_CTR
+}, {
+	.cra_name		= "rfc3686(ctr(aes))",
+	.cra_driver_name	= "rfc3686-ctr-aes-aesni",
+	.cra_priority		= 400,
+	.cra_flags		= CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
+	.cra_blocksize		= 1,
+	.cra_ctxsize		= sizeof(struct async_aes_ctx),
+	.cra_alignmask		= 0,
+	.cra_type		= &crypto_ablkcipher_type,
+	.cra_module		= THIS_MODULE,
+	.cra_init		= ablk_rfc3686_ctr_init,
+	.cra_exit		= ablk_exit,
+	.cra_u = {
+		.ablkcipher = {
+			.min_keysize = AES_MIN_KEY_SIZE +
+				       CTR_RFC3686_NONCE_SIZE,
+			.max_keysize = AES_MAX_KEY_SIZE +
+				       CTR_RFC3686_NONCE_SIZE,
+			.ivsize	     = CTR_RFC3686_IV_SIZE,
+			.setkey	     = ablk_set_key,
+			.encrypt     = ablk_encrypt,
+			.decrypt     = ablk_decrypt,
+			.geniv	     = "seqiv",
+		},
+	},
 #endif
+#endif
+#ifdef HAS_LRW
+}, {
+	.cra_name		= "lrw(aes)",
+	.cra_driver_name	= "lrw-aes-aesni",
+	.cra_priority		= 400,
+	.cra_flags		= CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
+	.cra_blocksize		= AES_BLOCK_SIZE,
+	.cra_ctxsize		= sizeof(struct async_aes_ctx),
+	.cra_alignmask		= 0,
+	.cra_type		= &crypto_ablkcipher_type,
+	.cra_module		= THIS_MODULE,
+	.cra_init		= ablk_lrw_init,
+	.cra_exit		= ablk_exit,
+	.cra_u = {
+		.ablkcipher = {
+			.min_keysize	= AES_MIN_KEY_SIZE + AES_BLOCK_SIZE,
+			.max_keysize	= AES_MAX_KEY_SIZE + AES_BLOCK_SIZE,
+			.ivsize		= AES_BLOCK_SIZE,
+			.setkey		= ablk_set_key,
+			.encrypt	= ablk_encrypt,
+			.decrypt	= ablk_decrypt,
+		},
+	},
+#endif
+#ifdef HAS_PCBC
+}, {
+	.cra_name		= "pcbc(aes)",
+	.cra_driver_name	= "pcbc-aes-aesni",
+	.cra_priority		= 400,
+	.cra_flags		= CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
+	.cra_blocksize		= AES_BLOCK_SIZE,
+	.cra_ctxsize		= sizeof(struct async_aes_ctx),
+	.cra_alignmask		= 0,
+	.cra_type		= &crypto_ablkcipher_type,
+	.cra_module		= THIS_MODULE,
+	.cra_init		= ablk_pcbc_init,
+	.cra_exit		= ablk_exit,
+	.cra_u = {
+		.ablkcipher = {
+			.min_keysize	= AES_MIN_KEY_SIZE,
+			.max_keysize	= AES_MAX_KEY_SIZE,
+			.ivsize		= AES_BLOCK_SIZE,
+			.setkey		= ablk_set_key,
+			.encrypt	= ablk_encrypt,
+			.decrypt	= ablk_decrypt,
+		},
+	},
+#endif
+#ifdef HAS_XTS
+}, {
+	.cra_name		= "xts(aes)",
+	.cra_driver_name	= "xts-aes-aesni",
+	.cra_priority		= 400,
+	.cra_flags		= CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
+	.cra_blocksize		= AES_BLOCK_SIZE,
+	.cra_ctxsize		= sizeof(struct async_aes_ctx),
+	.cra_alignmask		= 0,
+	.cra_type		= &crypto_ablkcipher_type,
+	.cra_module		= THIS_MODULE,
+	.cra_init		= ablk_xts_init,
+	.cra_exit		= ablk_exit,
+	.cra_u = {
+		.ablkcipher = {
+			.min_keysize	= 2 * AES_MIN_KEY_SIZE,
+			.max_keysize	= 2 * AES_MAX_KEY_SIZE,
+			.ivsize		= AES_BLOCK_SIZE,
+			.setkey		= ablk_set_key,
+			.encrypt	= ablk_encrypt,
+			.decrypt	= ablk_decrypt,
+		},
+	},
+#endif
+} };
 
 
 static const struct x86_cpu_id aesni_cpu_id[] = {
@@ -1263,120 +1202,24 @@
 
 static int __init aesni_init(void)
 {
-	int err;
+	int err, i;
 
 	if (!x86_match_cpu(aesni_cpu_id))
 		return -ENODEV;
 
-	if ((err = crypto_fpu_init()))
-		goto fpu_err;
-	if ((err = crypto_register_alg(&aesni_alg)))
-		goto aes_err;
-	if ((err = crypto_register_alg(&__aesni_alg)))
-		goto __aes_err;
-	if ((err = crypto_register_alg(&blk_ecb_alg)))
-		goto blk_ecb_err;
-	if ((err = crypto_register_alg(&blk_cbc_alg)))
-		goto blk_cbc_err;
-	if ((err = crypto_register_alg(&ablk_ecb_alg)))
-		goto ablk_ecb_err;
-	if ((err = crypto_register_alg(&ablk_cbc_alg)))
-		goto ablk_cbc_err;
-#ifdef CONFIG_X86_64
-	if ((err = crypto_register_alg(&blk_ctr_alg)))
-		goto blk_ctr_err;
-	if ((err = crypto_register_alg(&ablk_ctr_alg)))
-		goto ablk_ctr_err;
-	if ((err = crypto_register_alg(&__rfc4106_alg)))
-		goto __aead_gcm_err;
-	if ((err = crypto_register_alg(&rfc4106_alg)))
-		goto aead_gcm_err;
-#ifdef HAS_CTR
-	if ((err = crypto_register_alg(&ablk_rfc3686_ctr_alg)))
-		goto ablk_rfc3686_ctr_err;
-#endif
-#endif
-#ifdef HAS_LRW
-	if ((err = crypto_register_alg(&ablk_lrw_alg)))
-		goto ablk_lrw_err;
-#endif
-#ifdef HAS_PCBC
-	if ((err = crypto_register_alg(&ablk_pcbc_alg)))
-		goto ablk_pcbc_err;
-#endif
-#ifdef HAS_XTS
-	if ((err = crypto_register_alg(&ablk_xts_alg)))
-		goto ablk_xts_err;
-#endif
-	return err;
+	err = crypto_fpu_init();
+	if (err)
+		return err;
 
-#ifdef HAS_XTS
-ablk_xts_err:
-#endif
-#ifdef HAS_PCBC
-	crypto_unregister_alg(&ablk_pcbc_alg);
-ablk_pcbc_err:
-#endif
-#ifdef HAS_LRW
-	crypto_unregister_alg(&ablk_lrw_alg);
-ablk_lrw_err:
-#endif
-#ifdef CONFIG_X86_64
-#ifdef HAS_CTR
-	crypto_unregister_alg(&ablk_rfc3686_ctr_alg);
-ablk_rfc3686_ctr_err:
-#endif
-	crypto_unregister_alg(&rfc4106_alg);
-aead_gcm_err:
-	crypto_unregister_alg(&__rfc4106_alg);
-__aead_gcm_err:
-	crypto_unregister_alg(&ablk_ctr_alg);
-ablk_ctr_err:
-	crypto_unregister_alg(&blk_ctr_alg);
-blk_ctr_err:
-#endif
-	crypto_unregister_alg(&ablk_cbc_alg);
-ablk_cbc_err:
-	crypto_unregister_alg(&ablk_ecb_alg);
-ablk_ecb_err:
-	crypto_unregister_alg(&blk_cbc_alg);
-blk_cbc_err:
-	crypto_unregister_alg(&blk_ecb_alg);
-blk_ecb_err:
-	crypto_unregister_alg(&__aesni_alg);
-__aes_err:
-	crypto_unregister_alg(&aesni_alg);
-aes_err:
-fpu_err:
-	return err;
+	for (i = 0; i < ARRAY_SIZE(aesni_algs); i++)
+		INIT_LIST_HEAD(&aesni_algs[i].cra_list);
+
+	return crypto_register_algs(aesni_algs, ARRAY_SIZE(aesni_algs));
 }
 
 static void __exit aesni_exit(void)
 {
-#ifdef HAS_XTS
-	crypto_unregister_alg(&ablk_xts_alg);
-#endif
-#ifdef HAS_PCBC
-	crypto_unregister_alg(&ablk_pcbc_alg);
-#endif
-#ifdef HAS_LRW
-	crypto_unregister_alg(&ablk_lrw_alg);
-#endif
-#ifdef CONFIG_X86_64
-#ifdef HAS_CTR
-	crypto_unregister_alg(&ablk_rfc3686_ctr_alg);
-#endif
-	crypto_unregister_alg(&rfc4106_alg);
-	crypto_unregister_alg(&__rfc4106_alg);
-	crypto_unregister_alg(&ablk_ctr_alg);
-	crypto_unregister_alg(&blk_ctr_alg);
-#endif
-	crypto_unregister_alg(&ablk_cbc_alg);
-	crypto_unregister_alg(&ablk_ecb_alg);
-	crypto_unregister_alg(&blk_cbc_alg);
-	crypto_unregister_alg(&blk_ecb_alg);
-	crypto_unregister_alg(&__aesni_alg);
-	crypto_unregister_alg(&aesni_alg);
+	crypto_unregister_algs(aesni_algs, ARRAY_SIZE(aesni_algs));
 
 	crypto_fpu_exit();
 }
diff --git a/arch/x86/ia32/ia32entry.S b/arch/x86/ia32/ia32entry.S
index e3e7340..20e5f7b 100644
--- a/arch/x86/ia32/ia32entry.S
+++ b/arch/x86/ia32/ia32entry.S
@@ -13,6 +13,7 @@
 #include <asm/thread_info.h>	
 #include <asm/segment.h>
 #include <asm/irqflags.h>
+#include <asm/asm.h>
 #include <linux/linkage.h>
 #include <linux/err.h>
 
@@ -146,9 +147,7 @@
  	/* no need to do an access_ok check here because rbp has been
  	   32bit zero extended */ 
 1:	movl	(%rbp),%ebp
- 	.section __ex_table,"a"
- 	.quad 1b,ia32_badarg
- 	.previous	
+	_ASM_EXTABLE(1b,ia32_badarg)
 	orl     $TS_COMPAT,TI_status+THREAD_INFO(%rsp,RIP-ARGOFFSET)
 	testl   $_TIF_WORK_SYSCALL_ENTRY,TI_flags+THREAD_INFO(%rsp,RIP-ARGOFFSET)
 	CFI_REMEMBER_STATE
@@ -303,9 +302,7 @@
 	   32bit zero extended */ 
 	/* hardware stack frame is complete now */	
 1:	movl	(%r8),%r9d
-	.section __ex_table,"a"
-	.quad 1b,ia32_badarg
-	.previous	
+	_ASM_EXTABLE(1b,ia32_badarg)
 	orl     $TS_COMPAT,TI_status+THREAD_INFO(%rsp,RIP-ARGOFFSET)
 	testl   $_TIF_WORK_SYSCALL_ENTRY,TI_flags+THREAD_INFO(%rsp,RIP-ARGOFFSET)
 	CFI_REMEMBER_STATE
diff --git a/arch/x86/ia32/sys_ia32.c b/arch/x86/ia32/sys_ia32.c
index aec2202..edca9c0 100644
--- a/arch/x86/ia32/sys_ia32.c
+++ b/arch/x86/ia32/sys_ia32.c
@@ -287,11 +287,6 @@
 	return ret;
 }
 
-asmlinkage long sys32_alarm(unsigned int seconds)
-{
-	return alarm_setitimer(seconds);
-}
-
 asmlinkage long sys32_waitpid(compat_pid_t pid, unsigned int *stat_addr,
 			      int options)
 {
@@ -300,11 +295,6 @@
 
 /* 32-bit timeval and related flotsam.  */
 
-asmlinkage long sys32_sysfs(int option, u32 arg1, u32 arg2)
-{
-	return sys_sysfs(option, arg1, arg2);
-}
-
 asmlinkage long sys32_sched_rr_get_interval(compat_pid_t pid,
 				    struct compat_timespec __user *interval)
 {
@@ -375,19 +365,6 @@
 }
 
 
-asmlinkage long sys32_personality(unsigned long personality)
-{
-	int ret;
-
-	if (personality(current->personality) == PER_LINUX32 &&
-		personality == PER_LINUX)
-		personality = PER_LINUX32;
-	ret = sys_personality(personality);
-	if (ret == PER_LINUX32)
-		ret = PER_LINUX;
-	return ret;
-}
-
 asmlinkage long sys32_sendfile(int out_fd, int in_fd,
 			       compat_off_t __user *offset, s32 count)
 {
diff --git a/arch/x86/include/asm/asm.h b/arch/x86/include/asm/asm.h
index 9412d65..1c2d247 100644
--- a/arch/x86/include/asm/asm.h
+++ b/arch/x86/include/asm/asm.h
@@ -4,11 +4,9 @@
 #ifdef __ASSEMBLY__
 # define __ASM_FORM(x)	x
 # define __ASM_FORM_COMMA(x) x,
-# define __ASM_EX_SEC	.section __ex_table, "a"
 #else
 # define __ASM_FORM(x)	" " #x " "
 # define __ASM_FORM_COMMA(x) " " #x ","
-# define __ASM_EX_SEC	" .section __ex_table,\"a\"\n"
 #endif
 
 #ifdef CONFIG_X86_32
@@ -42,17 +40,33 @@
 
 /* Exception table entry */
 #ifdef __ASSEMBLY__
-# define _ASM_EXTABLE(from,to)	    \
-	__ASM_EX_SEC ;		    \
-	_ASM_ALIGN ;		    \
-	_ASM_PTR from , to ;	    \
-	.previous
+# define _ASM_EXTABLE(from,to)					\
+	.pushsection "__ex_table","a" ;				\
+	.balign 8 ;						\
+	.long (from) - . ;					\
+	.long (to) - . ;					\
+	.popsection
+
+# define _ASM_EXTABLE_EX(from,to)				\
+	.pushsection "__ex_table","a" ;				\
+	.balign 8 ;						\
+	.long (from) - . ;					\
+	.long (to) - . + 0x7ffffff0 ;				\
+	.popsection
 #else
-# define _ASM_EXTABLE(from,to) \
-	__ASM_EX_SEC	\
-	_ASM_ALIGN "\n" \
-	_ASM_PTR #from "," #to "\n" \
-	" .previous\n"
+# define _ASM_EXTABLE(from,to)					\
+	" .pushsection \"__ex_table\",\"a\"\n"			\
+	" .balign 8\n"						\
+	" .long (" #from ") - .\n"				\
+	" .long (" #to ") - .\n"				\
+	" .popsection\n"
+
+# define _ASM_EXTABLE_EX(from,to)				\
+	" .pushsection \"__ex_table\",\"a\"\n"			\
+	" .balign 8\n"						\
+	" .long (" #from ") - .\n"				\
+	" .long (" #to ") - . + 0x7ffffff0\n"			\
+	" .popsection\n"
 #endif
 
 #endif /* _ASM_X86_ASM_H */
diff --git a/arch/x86/include/asm/bootparam.h b/arch/x86/include/asm/bootparam.h
index 2f90c51..eb45aa6 100644
--- a/arch/x86/include/asm/bootparam.h
+++ b/arch/x86/include/asm/bootparam.h
@@ -112,7 +112,8 @@
 	__u8  e820_entries;				/* 0x1e8 */
 	__u8  eddbuf_entries;				/* 0x1e9 */
 	__u8  edd_mbr_sig_buf_entries;			/* 0x1ea */
-	__u8  _pad6[6];					/* 0x1eb */
+	__u8  kbd_status;				/* 0x1eb */
+	__u8  _pad6[5];					/* 0x1ec */
 	struct setup_header hdr;    /* setup header */	/* 0x1f1 */
 	__u8  _pad7[0x290-0x1f1-sizeof(struct setup_header)];
 	__u32 edd_mbr_sig_buffer[EDD_MBR_SIG_MAX];	/* 0x290 */
diff --git a/arch/x86/include/asm/device.h b/arch/x86/include/asm/device.h
index 63a2a03..93e1c55 100644
--- a/arch/x86/include/asm/device.h
+++ b/arch/x86/include/asm/device.h
@@ -5,8 +5,8 @@
 #ifdef CONFIG_ACPI
 	void	*acpi_handle;
 #endif
-#ifdef CONFIG_X86_64
-struct dma_map_ops *dma_ops;
+#ifdef CONFIG_X86_DEV_DMA_OPS
+	struct dma_map_ops *dma_ops;
 #endif
 #if defined(CONFIG_INTEL_IOMMU) || defined(CONFIG_AMD_IOMMU)
 	void *iommu; /* hook for IOMMU specific extension */
diff --git a/arch/x86/include/asm/dma-mapping.h b/arch/x86/include/asm/dma-mapping.h
index 4b4331d..61c0bd2 100644
--- a/arch/x86/include/asm/dma-mapping.h
+++ b/arch/x86/include/asm/dma-mapping.h
@@ -30,7 +30,7 @@
 
 static inline struct dma_map_ops *get_dma_ops(struct device *dev)
 {
-#ifdef CONFIG_X86_32
+#ifndef CONFIG_X86_DEV_DMA_OPS
 	return dma_ops;
 #else
 	if (unlikely(!dev) || !dev->archdata.dma_ops)
@@ -62,6 +62,12 @@
 					dma_addr_t *dma_addr, gfp_t flag,
 					struct dma_attrs *attrs);
 
+#ifdef CONFIG_X86_DMA_REMAP /* Platform code defines bridge-specific code */
+extern bool dma_capable(struct device *dev, dma_addr_t addr, size_t size);
+extern dma_addr_t phys_to_dma(struct device *dev, phys_addr_t paddr);
+extern phys_addr_t dma_to_phys(struct device *dev, dma_addr_t daddr);
+#else
+
 static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size)
 {
 	if (!dev->dma_mask)
@@ -79,6 +85,7 @@
 {
 	return daddr;
 }
+#endif /* CONFIG_X86_DMA_REMAP */
 
 static inline void
 dma_cache_sync(struct device *dev, void *vaddr, size_t size,
diff --git a/arch/x86/include/asm/kbdleds.h b/arch/x86/include/asm/kbdleds.h
new file mode 100644
index 0000000..f27ac5f
--- /dev/null
+++ b/arch/x86/include/asm/kbdleds.h
@@ -0,0 +1,17 @@
+#ifndef _ASM_X86_KBDLEDS_H
+#define _ASM_X86_KBDLEDS_H
+
+/*
+ * Some laptops take the 789uiojklm,. keys as number pad when NumLock is on.
+ * This seems a good reason to start with NumLock off. That's why on X86 we
+ * ask the bios for the correct state.
+ */
+
+#include <asm/setup.h>
+
+static inline int kbd_defleds(void)
+{
+	return boot_params.kbd_status & 0x20 ? (1 << VC_NUMLOCK) : 0;
+}
+
+#endif /* _ASM_X86_KBDLEDS_H */
diff --git a/arch/x86/include/asm/kdebug.h b/arch/x86/include/asm/kdebug.h
index d73f157..2c37aad 100644
--- a/arch/x86/include/asm/kdebug.h
+++ b/arch/x86/include/asm/kdebug.h
@@ -24,7 +24,6 @@
 extern void printk_address(unsigned long address, int reliable);
 extern void die(const char *, struct pt_regs *,long);
 extern int __must_check __die(const char *, struct pt_regs *, long);
-extern void show_registers(struct pt_regs *regs);
 extern void show_trace(struct task_struct *t, struct pt_regs *regs,
 		       unsigned long *sp, unsigned long bp);
 extern void __show_regs(struct pt_regs *regs, int all);
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index e216ba0..e5b97be 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -27,6 +27,7 @@
 #include <asm/desc.h>
 #include <asm/mtrr.h>
 #include <asm/msr-index.h>
+#include <asm/asm.h>
 
 #define KVM_MAX_VCPUS 254
 #define KVM_SOFT_MAX_VCPUS 160
@@ -921,9 +922,7 @@
 	__ASM_SIZE(push) " $666b \n\t"	      \
 	"call kvm_spurious_fault \n\t"	      \
 	".popsection \n\t" \
-	".pushsection __ex_table, \"a\" \n\t" \
-	_ASM_PTR " 666b, 667b \n\t" \
-	".popsection"
+	_ASM_EXTABLE(666b, 667b)
 
 #define __kvm_handle_fault_on_reboot(insn)		\
 	____kvm_handle_fault_on_reboot(insn, "")
diff --git a/arch/x86/include/asm/mca.h b/arch/x86/include/asm/mca.h
deleted file mode 100644
index eedbb6c..0000000
--- a/arch/x86/include/asm/mca.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/* -*- mode: c; c-basic-offset: 8 -*- */
-
-/* Platform specific MCA defines */
-#ifndef _ASM_X86_MCA_H
-#define _ASM_X86_MCA_H
-
-/* Maximal number of MCA slots - actually, some machines have less, but
- * they all have sufficient number of POS registers to cover 8.
- */
-#define MCA_MAX_SLOT_NR  8
-
-/* Most machines have only one MCA bus.  The only multiple bus machines
- * I know have at most two */
-#define MAX_MCA_BUSSES 2
-
-#define MCA_PRIMARY_BUS		0
-#define MCA_SECONDARY_BUS	1
-
-/* Dummy slot numbers on primary MCA for integrated functions */
-#define MCA_INTEGSCSI	(MCA_MAX_SLOT_NR)
-#define MCA_INTEGVIDEO	(MCA_MAX_SLOT_NR+1)
-#define MCA_MOTHERBOARD (MCA_MAX_SLOT_NR+2)
-
-/* Dummy POS values for integrated functions */
-#define MCA_DUMMY_POS_START	0x10000
-#define MCA_INTEGSCSI_POS	(MCA_DUMMY_POS_START+1)
-#define MCA_INTEGVIDEO_POS	(MCA_DUMMY_POS_START+2)
-#define MCA_MOTHERBOARD_POS	(MCA_DUMMY_POS_START+3)
-
-/* MCA registers */
-
-#define MCA_MOTHERBOARD_SETUP_REG	0x94
-#define MCA_ADAPTER_SETUP_REG		0x96
-#define MCA_POS_REG(n)			(0x100+(n))
-
-#define MCA_ENABLED	0x01	/* POS 2, set if adapter enabled */
-
-/* Max number of adapters, including both slots and various integrated
- * things.
- */
-#define MCA_NUMADAPTERS (MCA_MAX_SLOT_NR+3)
-
-#endif /* _ASM_X86_MCA_H */
diff --git a/arch/x86/include/asm/mca_dma.h b/arch/x86/include/asm/mca_dma.h
deleted file mode 100644
index 45271ae..0000000
--- a/arch/x86/include/asm/mca_dma.h
+++ /dev/null
@@ -1,201 +0,0 @@
-#ifndef _ASM_X86_MCA_DMA_H
-#define _ASM_X86_MCA_DMA_H
-
-#include <asm/io.h>
-#include <linux/ioport.h>
-
-/*
- * Microchannel specific DMA stuff.  DMA on an MCA machine is fairly similar to
- *   standard PC dma, but it certainly has its quirks.  DMA register addresses
- *   are in a different place and there are some added functions.  Most of this
- *   should be pretty obvious on inspection.  Note that the user must divide
- *   count by 2 when using 16-bit dma; that is not handled by these functions.
- *
- * Ramen Noodles are yummy.
- *
- *  1998 Tymm Twillman <tymm@computer.org>
- */
-
-/*
- * Registers that are used by the DMA controller; FN is the function register
- *   (tell the controller what to do) and EXE is the execution register (how
- *   to do it)
- */
-
-#define MCA_DMA_REG_FN  0x18
-#define MCA_DMA_REG_EXE 0x1A
-
-/*
- * Functions that the DMA controller can do
- */
-
-#define MCA_DMA_FN_SET_IO       0x00
-#define MCA_DMA_FN_SET_ADDR     0x20
-#define MCA_DMA_FN_GET_ADDR     0x30
-#define MCA_DMA_FN_SET_COUNT    0x40
-#define MCA_DMA_FN_GET_COUNT    0x50
-#define MCA_DMA_FN_GET_STATUS   0x60
-#define MCA_DMA_FN_SET_MODE     0x70
-#define MCA_DMA_FN_SET_ARBUS    0x80
-#define MCA_DMA_FN_MASK         0x90
-#define MCA_DMA_FN_RESET_MASK   0xA0
-#define MCA_DMA_FN_MASTER_CLEAR 0xD0
-
-/*
- * Modes (used by setting MCA_DMA_FN_MODE in the function register)
- *
- * Note that the MODE_READ is read from memory (write to device), and
- *   MODE_WRITE is vice-versa.
- */
-
-#define MCA_DMA_MODE_XFER  0x04  /* read by default */
-#define MCA_DMA_MODE_READ  0x04  /* same as XFER */
-#define MCA_DMA_MODE_WRITE 0x08  /* OR with MODE_XFER to use */
-#define MCA_DMA_MODE_IO    0x01  /* DMA from IO register */
-#define MCA_DMA_MODE_16    0x40  /* 16 bit xfers */
-
-
-/**
- *	mca_enable_dma	-	channel to enable DMA on
- *	@dmanr: DMA channel
- *
- *	Enable the MCA bus DMA on a channel. This can be called from
- *	IRQ context.
- */
-
-static inline void mca_enable_dma(unsigned int dmanr)
-{
-	outb(MCA_DMA_FN_RESET_MASK | dmanr, MCA_DMA_REG_FN);
-}
-
-/**
- *	mca_disble_dma	-	channel to disable DMA on
- *	@dmanr: DMA channel
- *
- *	Enable the MCA bus DMA on a channel. This can be called from
- *	IRQ context.
- */
-
-static inline void mca_disable_dma(unsigned int dmanr)
-{
-	outb(MCA_DMA_FN_MASK | dmanr, MCA_DMA_REG_FN);
-}
-
-/**
- *	mca_set_dma_addr -	load a 24bit DMA address
- *	@dmanr: DMA channel
- *	@a: 24bit bus address
- *
- *	Load the address register in the DMA controller. This has a 24bit
- *	limitation (16Mb).
- */
-
-static inline void mca_set_dma_addr(unsigned int dmanr, unsigned int a)
-{
-	outb(MCA_DMA_FN_SET_ADDR | dmanr, MCA_DMA_REG_FN);
-	outb(a & 0xff, MCA_DMA_REG_EXE);
-	outb((a >> 8) & 0xff, MCA_DMA_REG_EXE);
-	outb((a >> 16) & 0xff, MCA_DMA_REG_EXE);
-}
-
-/**
- *	mca_get_dma_addr -	load a 24bit DMA address
- *	@dmanr: DMA channel
- *
- *	Read the address register in the DMA controller. This has a 24bit
- *	limitation (16Mb). The return is a bus address.
- */
-
-static inline unsigned int mca_get_dma_addr(unsigned int dmanr)
-{
-	unsigned int addr;
-
-	outb(MCA_DMA_FN_GET_ADDR | dmanr, MCA_DMA_REG_FN);
-	addr = inb(MCA_DMA_REG_EXE);
-	addr |= inb(MCA_DMA_REG_EXE) << 8;
-	addr |= inb(MCA_DMA_REG_EXE) << 16;
-
-	return addr;
-}
-
-/**
- *	mca_set_dma_count -	load a 16bit transfer count
- *	@dmanr: DMA channel
- *	@count: count
- *
- *	Set the DMA count for this channel. This can be up to 64Kbytes.
- *	Setting a count of zero will not do what you expect.
- */
-
-static inline void mca_set_dma_count(unsigned int dmanr, unsigned int count)
-{
-	count--;  /* transfers one more than count -- correct for this */
-
-	outb(MCA_DMA_FN_SET_COUNT | dmanr, MCA_DMA_REG_FN);
-	outb(count & 0xff, MCA_DMA_REG_EXE);
-	outb((count >> 8) & 0xff, MCA_DMA_REG_EXE);
-}
-
-/**
- *	mca_get_dma_residue -	get the remaining bytes to transfer
- *	@dmanr: DMA channel
- *
- *	This function returns the number of bytes left to transfer
- *	on this DMA channel.
- */
-
-static inline unsigned int mca_get_dma_residue(unsigned int dmanr)
-{
-	unsigned short count;
-
-	outb(MCA_DMA_FN_GET_COUNT | dmanr, MCA_DMA_REG_FN);
-	count = 1 + inb(MCA_DMA_REG_EXE);
-	count += inb(MCA_DMA_REG_EXE) << 8;
-
-	return count;
-}
-
-/**
- *	mca_set_dma_io -	set the port for an I/O transfer
- *	@dmanr: DMA channel
- *	@io_addr: an I/O port number
- *
- *	Unlike the ISA bus DMA controllers the DMA on MCA bus can transfer
- *	with an I/O port target.
- */
-
-static inline void mca_set_dma_io(unsigned int dmanr, unsigned int io_addr)
-{
-	/*
-	 * DMA from a port address -- set the io address
-	 */
-
-	outb(MCA_DMA_FN_SET_IO | dmanr, MCA_DMA_REG_FN);
-	outb(io_addr & 0xff, MCA_DMA_REG_EXE);
-	outb((io_addr >>  8) & 0xff, MCA_DMA_REG_EXE);
-}
-
-/**
- *	mca_set_dma_mode -	set the DMA mode
- *	@dmanr: DMA channel
- *	@mode: mode to set
- *
- *	The DMA controller supports several modes. The mode values you can
- *	set are-
- *
- *	%MCA_DMA_MODE_READ when reading from the DMA device.
- *
- *	%MCA_DMA_MODE_WRITE to writing to the DMA device.
- *
- *	%MCA_DMA_MODE_IO to do DMA to or from an I/O port.
- *
- *	%MCA_DMA_MODE_16 to do 16bit transfers.
- */
-
-static inline void mca_set_dma_mode(unsigned int dmanr, unsigned int mode)
-{
-	outb(MCA_DMA_FN_SET_MODE | dmanr, MCA_DMA_REG_FN);
-	outb(mode, MCA_DMA_REG_EXE);
-}
-
-#endif /* _ASM_X86_MCA_DMA_H */
diff --git a/arch/x86/include/asm/mmzone_32.h b/arch/x86/include/asm/mmzone_32.h
index 55728e1..eb05fb3 100644
--- a/arch/x86/include/asm/mmzone_32.h
+++ b/arch/x86/include/asm/mmzone_32.h
@@ -61,10 +61,4 @@
 
 #endif /* CONFIG_DISCONTIGMEM */
 
-#ifdef CONFIG_NEED_MULTIPLE_NODES
-/* always use node 0 for bootmem on this numa platform */
-#define bootmem_arch_preferred_node(__bdata, size, align, goal, limit)	\
-	(NODE_DATA(0)->bdata)
-#endif /* CONFIG_NEED_MULTIPLE_NODES */
-
 #endif /* _ASM_X86_MMZONE_32_H */
diff --git a/arch/x86/include/asm/mpspec.h b/arch/x86/include/asm/mpspec.h
index 9c7d95f..3e2f42a 100644
--- a/arch/x86/include/asm/mpspec.h
+++ b/arch/x86/include/asm/mpspec.h
@@ -40,7 +40,7 @@
 
 #endif /* CONFIG_X86_64 */
 
-#if defined(CONFIG_MCA) || defined(CONFIG_EISA)
+#ifdef CONFIG_EISA
 extern int mp_bus_id_to_type[MAX_MP_BUSSES];
 #endif
 
diff --git a/arch/x86/include/asm/mpspec_def.h b/arch/x86/include/asm/mpspec_def.h
index c0a955a..b31f8c0 100644
--- a/arch/x86/include/asm/mpspec_def.h
+++ b/arch/x86/include/asm/mpspec_def.h
@@ -84,7 +84,7 @@
 #define BUSTYPE_EISA	"EISA"
 #define BUSTYPE_ISA	"ISA"
 #define BUSTYPE_INTERN	"INTERN"	/* Internal BUS */
-#define BUSTYPE_MCA	"MCA"
+#define BUSTYPE_MCA	"MCA"		/* Obsolete */
 #define BUSTYPE_VL	"VL"		/* Local bus */
 #define BUSTYPE_PCI	"PCI"
 #define BUSTYPE_PCMCIA	"PCMCIA"
@@ -169,6 +169,5 @@
 	MP_BUS_ISA = 1,
 	MP_BUS_EISA,
 	MP_BUS_PCI,
-	MP_BUS_MCA,
 };
 #endif /* _ASM_X86_MPSPEC_DEF_H */
diff --git a/arch/x86/include/asm/msr.h b/arch/x86/include/asm/msr.h
index 95203d4..084ef95 100644
--- a/arch/x86/include/asm/msr.h
+++ b/arch/x86/include/asm/msr.h
@@ -169,14 +169,7 @@
 	return native_write_msr_safe(msr, low, high);
 }
 
-/*
- * rdmsr with exception handling.
- *
- * Please note that the exception handling works only after we've
- * switched to the "smart" #GP handler in trap_init() which knows about
- * exception tables - using this macro earlier than that causes machine
- * hangs on boxes which do not implement the @msr in the first argument.
- */
+/* rdmsr with exception handling */
 #define rdmsr_safe(msr, p1, p2)					\
 ({								\
 	int __err;						\
diff --git a/arch/x86/include/asm/nops.h b/arch/x86/include/asm/nops.h
index 405b4032..aff2b33 100644
--- a/arch/x86/include/asm/nops.h
+++ b/arch/x86/include/asm/nops.h
@@ -87,7 +87,11 @@
 #define P6_NOP8	0x0f,0x1f,0x84,0x00,0,0,0,0
 #define P6_NOP5_ATOMIC P6_NOP5
 
+#ifdef __ASSEMBLY__
+#define _ASM_MK_NOP(x) .byte x
+#else
 #define _ASM_MK_NOP(x) ".byte " __stringify(x) "\n"
+#endif
 
 #if defined(CONFIG_MK7)
 #define ASM_NOP1 _ASM_MK_NOP(K7_NOP1)
diff --git a/arch/x86/include/asm/paravirt.h b/arch/x86/include/asm/paravirt.h
index aa0f913..6cbbabf 100644
--- a/arch/x86/include/asm/paravirt.h
+++ b/arch/x86/include/asm/paravirt.h
@@ -1023,10 +1023,8 @@
 		  call PARA_INDIRECT(pv_cpu_ops+PV_CPU_swapgs)		\
 		 )
 
-#define GET_CR2_INTO_RCX				\
-	call PARA_INDIRECT(pv_mmu_ops+PV_MMU_read_cr2);	\
-	movq %rax, %rcx;				\
-	xorq %rax, %rax;
+#define GET_CR2_INTO_RAX				\
+	call PARA_INDIRECT(pv_mmu_ops+PV_MMU_read_cr2)
 
 #define PARAVIRT_ADJUST_EXCEPTION_FRAME					\
 	PARA_SITE(PARA_PATCH(pv_irq_ops, PV_IRQ_adjust_exception_frame), \
diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h
index ccbb1ea..7745b257 100644
--- a/arch/x86/include/asm/processor.h
+++ b/arch/x86/include/asm/processor.h
@@ -579,9 +579,6 @@
 /* Free all resources held by a thread. */
 extern void release_thread(struct task_struct *);
 
-/* Prepare to copy thread state - unlazy all lazy state */
-extern void prepare_to_copy(struct task_struct *tsk);
-
 unsigned long get_wchan(struct task_struct *p);
 
 /*
diff --git a/arch/x86/include/asm/segment.h b/arch/x86/include/asm/segment.h
index 1654662..c48a950 100644
--- a/arch/x86/include/asm/segment.h
+++ b/arch/x86/include/asm/segment.h
@@ -205,13 +205,15 @@
 
 #define IDT_ENTRIES 256
 #define NUM_EXCEPTION_VECTORS 32
+/* Bitmask of exception vectors which push an error code on the stack */
+#define EXCEPTION_ERRCODE_MASK  0x00027d00
 #define GDT_SIZE (GDT_ENTRIES * 8)
 #define GDT_ENTRY_TLS_ENTRIES 3
 #define TLS_SIZE (GDT_ENTRY_TLS_ENTRIES * 8)
 
 #ifdef __KERNEL__
 #ifndef __ASSEMBLY__
-extern const char early_idt_handlers[NUM_EXCEPTION_VECTORS][10];
+extern const char early_idt_handlers[NUM_EXCEPTION_VECTORS][2+2+5];
 
 /*
  * Load a segment. Fall back on loading the zero
diff --git a/arch/x86/include/asm/spinlock.h b/arch/x86/include/asm/spinlock.h
index 76bfa2c..b315a33 100644
--- a/arch/x86/include/asm/spinlock.h
+++ b/arch/x86/include/asm/spinlock.h
@@ -20,10 +20,8 @@
 
 #ifdef CONFIG_X86_32
 # define LOCK_PTR_REG "a"
-# define REG_PTR_MODE "k"
 #else
 # define LOCK_PTR_REG "D"
-# define REG_PTR_MODE "q"
 #endif
 
 #if defined(CONFIG_X86_32) && \
diff --git a/arch/x86/include/asm/tlbflush.h b/arch/x86/include/asm/tlbflush.h
index 1620d23..36a1a2a 100644
--- a/arch/x86/include/asm/tlbflush.h
+++ b/arch/x86/include/asm/tlbflush.h
@@ -62,11 +62,7 @@
 		__flush_tlb();
 }
 
-#ifdef CONFIG_X86_32
-# define TLB_FLUSH_ALL	0xffffffff
-#else
-# define TLB_FLUSH_ALL	-1ULL
-#endif
+#define TLB_FLUSH_ALL	-1UL
 
 /*
  * TLB flushing:
diff --git a/arch/x86/include/asm/uaccess.h b/arch/x86/include/asm/uaccess.h
index e054459..851fe0d 100644
--- a/arch/x86/include/asm/uaccess.h
+++ b/arch/x86/include/asm/uaccess.h
@@ -79,11 +79,12 @@
 #define access_ok(type, addr, size) (likely(__range_not_ok(addr, size) == 0))
 
 /*
- * The exception table consists of pairs of addresses: the first is the
- * address of an instruction that is allowed to fault, and the second is
- * the address at which the program should continue.  No registers are
- * modified, so it is entirely up to the continuation code to figure out
- * what to do.
+ * The exception table consists of pairs of addresses relative to the
+ * exception table enty itself: the first is the address of an
+ * instruction that is allowed to fault, and the second is the address
+ * at which the program should continue.  No registers are modified,
+ * so it is entirely up to the continuation code to figure out what to
+ * do.
  *
  * All the routines below use bits of fixup code that are out of line
  * with the main instruction path.  This means when everything is well,
@@ -92,10 +93,14 @@
  */
 
 struct exception_table_entry {
-	unsigned long insn, fixup;
+	int insn, fixup;
 };
+/* This is not the generic standard exception_table_entry format */
+#define ARCH_HAS_SORT_EXTABLE
+#define ARCH_HAS_SEARCH_EXTABLE
 
 extern int fixup_exception(struct pt_regs *regs);
+extern int early_fixup_exception(unsigned long *ip);
 
 /*
  * These are the main single-value transfer routines.  They automatically
@@ -202,8 +207,8 @@
 	asm volatile("1:	movl %%eax,0(%1)\n"			\
 		     "2:	movl %%edx,4(%1)\n"			\
 		     "3:\n"						\
-		     _ASM_EXTABLE(1b, 2b - 1b)				\
-		     _ASM_EXTABLE(2b, 3b - 2b)				\
+		     _ASM_EXTABLE_EX(1b, 2b)				\
+		     _ASM_EXTABLE_EX(2b, 3b)				\
 		     : : "A" (x), "r" (addr))
 
 #define __put_user_x8(x, ptr, __ret_pu)				\
@@ -408,7 +413,7 @@
 #define __get_user_asm_ex(x, addr, itype, rtype, ltype)			\
 	asm volatile("1:	mov"itype" %1,%"rtype"0\n"		\
 		     "2:\n"						\
-		     _ASM_EXTABLE(1b, 2b - 1b)				\
+		     _ASM_EXTABLE_EX(1b, 2b)				\
 		     : ltype(x) : "m" (__m(addr)))
 
 #define __put_user_nocheck(x, ptr, size)			\
@@ -450,7 +455,7 @@
 #define __put_user_asm_ex(x, addr, itype, rtype, ltype)			\
 	asm volatile("1:	mov"itype" %"rtype"0,%1\n"		\
 		     "2:\n"						\
-		     _ASM_EXTABLE(1b, 2b - 1b)				\
+		     _ASM_EXTABLE_EX(1b, 2b)				\
 		     : : ltype(x), "m" (__m(addr)))
 
 /*
diff --git a/arch/x86/include/asm/xor_32.h b/arch/x86/include/asm/xor_32.h
index 133b40a..4545708 100644
--- a/arch/x86/include/asm/xor_32.h
+++ b/arch/x86/include/asm/xor_32.h
@@ -861,6 +861,9 @@
 	.do_5 = xor_sse_5,
 };
 
+/* Also try the AVX routines */
+#include "xor_avx.h"
+
 /* Also try the generic routines.  */
 #include <asm-generic/xor.h>
 
@@ -871,6 +874,7 @@
 	xor_speed(&xor_block_8regs_p);			\
 	xor_speed(&xor_block_32regs);			\
 	xor_speed(&xor_block_32regs_p);			\
+	AVX_XOR_SPEED;					\
 	if (cpu_has_xmm)				\
 		xor_speed(&xor_block_pIII_sse);		\
 	if (cpu_has_mmx) {				\
@@ -883,6 +887,6 @@
    We may also be able to load into the L1 only depending on how the cpu
    deals with a load to a line that is being prefetched.  */
 #define XOR_SELECT_TEMPLATE(FASTEST)			\
-	(cpu_has_xmm ? &xor_block_pIII_sse : FASTEST)
+	AVX_SELECT(cpu_has_xmm ? &xor_block_pIII_sse : FASTEST)
 
 #endif /* _ASM_X86_XOR_32_H */
diff --git a/arch/x86/include/asm/xor_64.h b/arch/x86/include/asm/xor_64.h
index 1549b5e..b9b2323 100644
--- a/arch/x86/include/asm/xor_64.h
+++ b/arch/x86/include/asm/xor_64.h
@@ -347,15 +347,21 @@
 	.do_5 = xor_sse_5,
 };
 
+
+/* Also try the AVX routines */
+#include "xor_avx.h"
+
 #undef XOR_TRY_TEMPLATES
 #define XOR_TRY_TEMPLATES			\
 do {						\
+	AVX_XOR_SPEED;				\
 	xor_speed(&xor_block_sse);		\
 } while (0)
 
 /* We force the use of the SSE xor block because it can write around L2.
    We may also be able to load into the L1 only depending on how the cpu
    deals with a load to a line that is being prefetched.  */
-#define XOR_SELECT_TEMPLATE(FASTEST) (&xor_block_sse)
+#define XOR_SELECT_TEMPLATE(FASTEST) \
+	AVX_SELECT(&xor_block_sse)
 
 #endif /* _ASM_X86_XOR_64_H */
diff --git a/arch/x86/include/asm/xor_avx.h b/arch/x86/include/asm/xor_avx.h
new file mode 100644
index 0000000..2510d35
--- /dev/null
+++ b/arch/x86/include/asm/xor_avx.h
@@ -0,0 +1,214 @@
+#ifndef _ASM_X86_XOR_AVX_H
+#define _ASM_X86_XOR_AVX_H
+
+/*
+ * Optimized RAID-5 checksumming functions for AVX
+ *
+ * Copyright (C) 2012 Intel Corporation
+ * Author: Jim Kukunas <james.t.kukunas@linux.intel.com>
+ *
+ * Based on Ingo Molnar and Zach Brown's respective MMX and SSE routines
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ */
+
+#ifdef CONFIG_AS_AVX
+
+#include <linux/compiler.h>
+#include <asm/i387.h>
+
+#define ALIGN32 __aligned(32)
+
+#define YMM_SAVED_REGS 4
+
+#define YMMS_SAVE \
+do { \
+	preempt_disable(); \
+	cr0 = read_cr0(); \
+	clts(); \
+	asm volatile("vmovaps %%ymm0, %0" : "=m" (ymm_save[0]) : : "memory"); \
+	asm volatile("vmovaps %%ymm1, %0" : "=m" (ymm_save[32]) : : "memory"); \
+	asm volatile("vmovaps %%ymm2, %0" : "=m" (ymm_save[64]) : : "memory"); \
+	asm volatile("vmovaps %%ymm3, %0" : "=m" (ymm_save[96]) : : "memory"); \
+} while (0);
+
+#define YMMS_RESTORE \
+do { \
+	asm volatile("sfence" : : : "memory"); \
+	asm volatile("vmovaps %0, %%ymm3" : : "m" (ymm_save[96])); \
+	asm volatile("vmovaps %0, %%ymm2" : : "m" (ymm_save[64])); \
+	asm volatile("vmovaps %0, %%ymm1" : : "m" (ymm_save[32])); \
+	asm volatile("vmovaps %0, %%ymm0" : : "m" (ymm_save[0])); \
+	write_cr0(cr0); \
+	preempt_enable(); \
+} while (0);
+
+#define BLOCK4(i) \
+		BLOCK(32 * i, 0) \
+		BLOCK(32 * (i + 1), 1) \
+		BLOCK(32 * (i + 2), 2) \
+		BLOCK(32 * (i + 3), 3)
+
+#define BLOCK16() \
+		BLOCK4(0) \
+		BLOCK4(4) \
+		BLOCK4(8) \
+		BLOCK4(12)
+
+static void xor_avx_2(unsigned long bytes, unsigned long *p0, unsigned long *p1)
+{
+	unsigned long cr0, lines = bytes >> 9;
+	char ymm_save[32 * YMM_SAVED_REGS] ALIGN32;
+
+	YMMS_SAVE
+
+	while (lines--) {
+#undef BLOCK
+#define BLOCK(i, reg) \
+do { \
+	asm volatile("vmovdqa %0, %%ymm" #reg : : "m" (p1[i / sizeof(*p1)])); \
+	asm volatile("vxorps %0, %%ymm" #reg ", %%ymm"  #reg : : \
+		"m" (p0[i / sizeof(*p0)])); \
+	asm volatile("vmovdqa %%ymm" #reg ", %0" : \
+		"=m" (p0[i / sizeof(*p0)])); \
+} while (0);
+
+		BLOCK16()
+
+		p0 = (unsigned long *)((uintptr_t)p0 + 512);
+		p1 = (unsigned long *)((uintptr_t)p1 + 512);
+	}
+
+	YMMS_RESTORE
+}
+
+static void xor_avx_3(unsigned long bytes, unsigned long *p0, unsigned long *p1,
+	unsigned long *p2)
+{
+	unsigned long cr0, lines = bytes >> 9;
+	char ymm_save[32 * YMM_SAVED_REGS] ALIGN32;
+
+	YMMS_SAVE
+
+	while (lines--) {
+#undef BLOCK
+#define BLOCK(i, reg) \
+do { \
+	asm volatile("vmovdqa %0, %%ymm" #reg : : "m" (p2[i / sizeof(*p2)])); \
+	asm volatile("vxorps %0, %%ymm" #reg ", %%ymm" #reg : : \
+		"m" (p1[i / sizeof(*p1)])); \
+	asm volatile("vxorps %0, %%ymm" #reg ", %%ymm" #reg : : \
+		"m" (p0[i / sizeof(*p0)])); \
+	asm volatile("vmovdqa %%ymm" #reg ", %0" : \
+		"=m" (p0[i / sizeof(*p0)])); \
+} while (0);
+
+		BLOCK16()
+
+		p0 = (unsigned long *)((uintptr_t)p0 + 512);
+		p1 = (unsigned long *)((uintptr_t)p1 + 512);
+		p2 = (unsigned long *)((uintptr_t)p2 + 512);
+	}
+
+	YMMS_RESTORE
+}
+
+static void xor_avx_4(unsigned long bytes, unsigned long *p0, unsigned long *p1,
+	unsigned long *p2, unsigned long *p3)
+{
+	unsigned long cr0, lines = bytes >> 9;
+	char ymm_save[32 * YMM_SAVED_REGS] ALIGN32;
+
+	YMMS_SAVE
+
+	while (lines--) {
+#undef BLOCK
+#define BLOCK(i, reg) \
+do { \
+	asm volatile("vmovdqa %0, %%ymm" #reg : : "m" (p3[i / sizeof(*p3)])); \
+	asm volatile("vxorps %0, %%ymm" #reg ", %%ymm" #reg : : \
+		"m" (p2[i / sizeof(*p2)])); \
+	asm volatile("vxorps %0, %%ymm" #reg ", %%ymm" #reg : : \
+		"m" (p1[i / sizeof(*p1)])); \
+	asm volatile("vxorps %0, %%ymm" #reg ", %%ymm" #reg : : \
+		"m" (p0[i / sizeof(*p0)])); \
+	asm volatile("vmovdqa %%ymm" #reg ", %0" : \
+		"=m" (p0[i / sizeof(*p0)])); \
+} while (0);
+
+		BLOCK16();
+
+		p0 = (unsigned long *)((uintptr_t)p0 + 512);
+		p1 = (unsigned long *)((uintptr_t)p1 + 512);
+		p2 = (unsigned long *)((uintptr_t)p2 + 512);
+		p3 = (unsigned long *)((uintptr_t)p3 + 512);
+	}
+
+	YMMS_RESTORE
+}
+
+static void xor_avx_5(unsigned long bytes, unsigned long *p0, unsigned long *p1,
+	unsigned long *p2, unsigned long *p3, unsigned long *p4)
+{
+	unsigned long cr0, lines = bytes >> 9;
+	char ymm_save[32 * YMM_SAVED_REGS] ALIGN32;
+
+	YMMS_SAVE
+
+	while (lines--) {
+#undef BLOCK
+#define BLOCK(i, reg) \
+do { \
+	asm volatile("vmovdqa %0, %%ymm" #reg : : "m" (p4[i / sizeof(*p4)])); \
+	asm volatile("vxorps %0, %%ymm" #reg ", %%ymm" #reg : : \
+		"m" (p3[i / sizeof(*p3)])); \
+	asm volatile("vxorps %0, %%ymm" #reg ", %%ymm" #reg : : \
+		"m" (p2[i / sizeof(*p2)])); \
+	asm volatile("vxorps %0, %%ymm" #reg ", %%ymm" #reg : : \
+		"m" (p1[i / sizeof(*p1)])); \
+	asm volatile("vxorps %0, %%ymm" #reg ", %%ymm" #reg : : \
+		"m" (p0[i / sizeof(*p0)])); \
+	asm volatile("vmovdqa %%ymm" #reg ", %0" : \
+		"=m" (p0[i / sizeof(*p0)])); \
+} while (0);
+
+		BLOCK16()
+
+		p0 = (unsigned long *)((uintptr_t)p0 + 512);
+		p1 = (unsigned long *)((uintptr_t)p1 + 512);
+		p2 = (unsigned long *)((uintptr_t)p2 + 512);
+		p3 = (unsigned long *)((uintptr_t)p3 + 512);
+		p4 = (unsigned long *)((uintptr_t)p4 + 512);
+	}
+
+	YMMS_RESTORE
+}
+
+static struct xor_block_template xor_block_avx = {
+	.name = "avx",
+	.do_2 = xor_avx_2,
+	.do_3 = xor_avx_3,
+	.do_4 = xor_avx_4,
+	.do_5 = xor_avx_5,
+};
+
+#define AVX_XOR_SPEED \
+do { \
+	if (cpu_has_avx) \
+		xor_speed(&xor_block_avx); \
+} while (0)
+
+#define AVX_SELECT(FASTEST) \
+	(cpu_has_avx ? &xor_block_avx : FASTEST)
+
+#else
+
+#define AVX_XOR_SPEED {}
+
+#define AVX_SELECT(FASTEST) (FASTEST)
+
+#endif
+#endif
diff --git a/arch/x86/include/asm/xsave.h b/arch/x86/include/asm/xsave.h
index c6ce245..8a1b6f9 100644
--- a/arch/x86/include/asm/xsave.h
+++ b/arch/x86/include/asm/xsave.h
@@ -80,10 +80,7 @@
 			     "3:  movl $-1,%[err]\n"
 			     "    jmp  2b\n"
 			     ".previous\n"
-			     ".section __ex_table,\"a\"\n"
-			     _ASM_ALIGN "\n"
-			     _ASM_PTR "1b,3b\n"
-			     ".previous"
+			     _ASM_EXTABLE(1b,3b)
 			     : [err] "=r" (err)
 			     : "D" (buf), "a" (-1), "d" (-1), "0" (0)
 			     : "memory");
@@ -106,10 +103,7 @@
 			     "3:  movl $-1,%[err]\n"
 			     "    jmp  2b\n"
 			     ".previous\n"
-			     ".section __ex_table,\"a\"\n"
-			     _ASM_ALIGN "\n"
-			     _ASM_PTR "1b,3b\n"
-			     ".previous"
+			     _ASM_EXTABLE(1b,3b)
 			     : [err] "=r" (err)
 			     : "D" (xstate), "a" (lmask), "d" (hmask), "0" (0)
 			     : "memory");	/* memory required? */
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index 56ebd1f..bb85292 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -49,7 +49,6 @@
 obj-y				+= acpi/
 obj-y				+= reboot.o
 obj-$(CONFIG_X86_32)		+= reboot_32.o
-obj-$(CONFIG_MCA)		+= mca_32.o
 obj-$(CONFIG_X86_MSR)		+= msr.o
 obj-$(CONFIG_X86_CPUID)		+= cpuid.o
 obj-$(CONFIG_PCI)		+= early-quirks.o
diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c
index 7c439fe..8afb693 100644
--- a/arch/x86/kernel/acpi/boot.c
+++ b/arch/x86/kernel/acpi/boot.c
@@ -990,7 +990,7 @@
 	int i;
 	struct mpc_intsrc mp_irq;
 
-#if defined (CONFIG_MCA) || defined (CONFIG_EISA)
+#ifdef CONFIG_EISA
 	/*
 	 * Fabricate the legacy ISA bus (bus #31).
 	 */
diff --git a/arch/x86/kernel/apic/io_apic.c b/arch/x86/kernel/apic/io_apic.c
index ffdc152..ac96561 100644
--- a/arch/x86/kernel/apic/io_apic.c
+++ b/arch/x86/kernel/apic/io_apic.c
@@ -140,7 +140,7 @@
 /* GSI interrupts */
 static int nr_irqs_gsi = NR_IRQS_LEGACY;
 
-#if defined (CONFIG_MCA) || defined (CONFIG_EISA)
+#ifdef CONFIG_EISA
 int mp_bus_id_to_type[MAX_MP_BUSSES];
 #endif
 
@@ -835,7 +835,7 @@
 	return -1;
 }
 
-#if defined(CONFIG_EISA) || defined(CONFIG_MCA)
+#ifdef CONFIG_EISA
 /*
  * EISA Edge/Level control register, ELCR
  */
@@ -872,12 +872,6 @@
 #define default_PCI_trigger(idx)	(1)
 #define default_PCI_polarity(idx)	(1)
 
-/* MCA interrupts are always polarity zero level triggered,
- * when listed as conforming in the MP table. */
-
-#define default_MCA_trigger(idx)	(1)
-#define default_MCA_polarity(idx)	default_ISA_polarity(idx)
-
 static int irq_polarity(int idx)
 {
 	int bus = mp_irqs[idx].srcbus;
@@ -935,7 +929,7 @@
 				trigger = default_ISA_trigger(idx);
 			else
 				trigger = default_PCI_trigger(idx);
-#if defined(CONFIG_EISA) || defined(CONFIG_MCA)
+#ifdef CONFIG_EISA
 			switch (mp_bus_id_to_type[bus]) {
 				case MP_BUS_ISA: /* ISA pin */
 				{
@@ -952,11 +946,6 @@
 					/* set before the switch */
 					break;
 				}
-				case MP_BUS_MCA: /* MCA pin */
-				{
-					trigger = default_MCA_trigger(idx);
-					break;
-				}
 				default:
 				{
 					printk(KERN_WARNING "broken BIOS!!\n");
diff --git a/arch/x86/kernel/check.c b/arch/x86/kernel/check.c
index 5da1269..e2dbcb7 100644
--- a/arch/x86/kernel/check.c
+++ b/arch/x86/kernel/check.c
@@ -27,21 +27,29 @@
 
 static __init int set_corruption_check(char *arg)
 {
-	char *end;
+	ssize_t ret;
+	unsigned long val;
 
-	memory_corruption_check = simple_strtol(arg, &end, 10);
+	ret = kstrtoul(arg, 10, &val);
+	if (ret)
+		return ret;
 
-	return (*end == 0) ? 0 : -EINVAL;
+	memory_corruption_check = val;
+	return 0;
 }
 early_param("memory_corruption_check", set_corruption_check);
 
 static __init int set_corruption_check_period(char *arg)
 {
-	char *end;
+	ssize_t ret;
+	unsigned long val;
 
-	corruption_check_period = simple_strtoul(arg, &end, 10);
+	ret = kstrtoul(arg, 10, &val);
+	if (ret)
+		return ret;
 
-	return (*end == 0) ? 0 : -EINVAL;
+	corruption_check_period = val;
+	return 0;
 }
 early_param("memory_corruption_check_period", set_corruption_check_period);
 
diff --git a/arch/x86/kernel/cpu/intel_cacheinfo.c b/arch/x86/kernel/cpu/intel_cacheinfo.c
index b8f3653..9a7c90d 100644
--- a/arch/x86/kernel/cpu/intel_cacheinfo.c
+++ b/arch/x86/kernel/cpu/intel_cacheinfo.c
@@ -615,14 +615,14 @@
 					new_l2 = this_leaf.size/1024;
 					num_threads_sharing = 1 + this_leaf.eax.split.num_threads_sharing;
 					index_msb = get_count_order(num_threads_sharing);
-					l2_id = c->apicid >> index_msb;
+					l2_id = c->apicid & ~((1 << index_msb) - 1);
 					break;
 				case 3:
 					new_l3 = this_leaf.size/1024;
 					num_threads_sharing = 1 + this_leaf.eax.split.num_threads_sharing;
 					index_msb = get_count_order(
 							num_threads_sharing);
-					l3_id = c->apicid >> index_msb;
+					l3_id = c->apicid & ~((1 << index_msb) - 1);
 					break;
 				default:
 					break;
diff --git a/arch/x86/kernel/cpu/mcheck/mce.c b/arch/x86/kernel/cpu/mcheck/mce.c
index 297edb1..2afcbd2 100644
--- a/arch/x86/kernel/cpu/mcheck/mce.c
+++ b/arch/x86/kernel/cpu/mcheck/mce.c
@@ -1431,6 +1431,43 @@
 		 */
 		 if (c->x86 == 6 && banks > 0)
 			mce_banks[0].ctl = 0;
+
+		 /*
+		  * Turn off MC4_MISC thresholding banks on those models since
+		  * they're not supported there.
+		  */
+		 if (c->x86 == 0x15 &&
+		     (c->x86_model >= 0x10 && c->x86_model <= 0x1f)) {
+			 int i;
+			 u64 val, hwcr;
+			 bool need_toggle;
+			 u32 msrs[] = {
+				0x00000413, /* MC4_MISC0 */
+				0xc0000408, /* MC4_MISC1 */
+			 };
+
+			 rdmsrl(MSR_K7_HWCR, hwcr);
+
+			 /* McStatusWrEn has to be set */
+			 need_toggle = !(hwcr & BIT(18));
+
+			 if (need_toggle)
+				 wrmsrl(MSR_K7_HWCR, hwcr | BIT(18));
+
+			 for (i = 0; i < ARRAY_SIZE(msrs); i++) {
+				 rdmsrl(msrs[i], val);
+
+				 /* CntP bit set? */
+				 if (val & BIT(62)) {
+					 val &= ~BIT(62);
+					 wrmsrl(msrs[i], val);
+				 }
+			 }
+
+			 /* restore old settings */
+			 if (need_toggle)
+				 wrmsrl(MSR_K7_HWCR, hwcr);
+		 }
 	}
 
 	if (c->x86_vendor == X86_VENDOR_INTEL) {
diff --git a/arch/x86/kernel/cpu/mcheck/mce_amd.c b/arch/x86/kernel/cpu/mcheck/mce_amd.c
index 99b5717..f4873a6 100644
--- a/arch/x86/kernel/cpu/mcheck/mce_amd.c
+++ b/arch/x86/kernel/cpu/mcheck/mce_amd.c
@@ -51,6 +51,7 @@
 	unsigned int		cpu;
 	u32			address;
 	u16			interrupt_enable;
+	bool			interrupt_capable;
 	u16			threshold_limit;
 	struct kobject		kobj;
 	struct list_head	miscj;
@@ -83,6 +84,21 @@
 	u16			old_limit;
 };
 
+static bool lvt_interrupt_supported(unsigned int bank, u32 msr_high_bits)
+{
+	/*
+	 * bank 4 supports APIC LVT interrupts implicitly since forever.
+	 */
+	if (bank == 4)
+		return true;
+
+	/*
+	 * IntP: interrupt present; if this bit is set, the thresholding
+	 * bank can generate APIC LVT interrupts
+	 */
+	return msr_high_bits & BIT(28);
+}
+
 static int lvt_off_valid(struct threshold_block *b, int apic, u32 lo, u32 hi)
 {
 	int msr = (hi & MASK_LVTOFF_HI) >> 20;
@@ -104,8 +120,10 @@
 	return 1;
 };
 
-/* must be called with correct cpu affinity */
-/* Called via smp_call_function_single() */
+/*
+ * Called via smp_call_function_single(), must be called with correct
+ * cpu affinity.
+ */
 static void threshold_restart_bank(void *_tr)
 {
 	struct thresh_restart *tr = _tr;
@@ -128,6 +146,12 @@
 		    (new_count & THRESHOLD_MAX);
 	}
 
+	/* clear IntType */
+	hi &= ~MASK_INT_TYPE_HI;
+
+	if (!tr->b->interrupt_capable)
+		goto done;
+
 	if (tr->set_lvt_off) {
 		if (lvt_off_valid(tr->b, tr->lvt_off, lo, hi)) {
 			/* set new lvt offset */
@@ -136,9 +160,10 @@
 		}
 	}
 
-	tr->b->interrupt_enable ?
-	    (hi = (hi & ~MASK_INT_TYPE_HI) | INT_TYPE_APIC) :
-	    (hi &= ~MASK_INT_TYPE_HI);
+	if (tr->b->interrupt_enable)
+		hi |= INT_TYPE_APIC;
+
+ done:
 
 	hi |= MASK_COUNT_EN_HI;
 	wrmsr(tr->b->address, lo, hi);
@@ -202,14 +227,17 @@
 			if (shared_bank[bank] && c->cpu_core_id)
 				break;
 
-			offset = setup_APIC_mce(offset,
-						(high & MASK_LVTOFF_HI) >> 20);
-
 			memset(&b, 0, sizeof(b));
-			b.cpu		= cpu;
-			b.bank		= bank;
-			b.block		= block;
-			b.address	= address;
+			b.cpu			= cpu;
+			b.bank			= bank;
+			b.block			= block;
+			b.address		= address;
+			b.interrupt_capable	= lvt_interrupt_supported(bank, high);
+
+			if (b.interrupt_capable) {
+				int new = (high & MASK_LVTOFF_HI) >> 20;
+				offset  = setup_APIC_mce(offset, new);
+			}
 
 			mce_threshold_block_init(&b, offset);
 			mce_threshold_vector = amd_threshold_interrupt;
@@ -309,6 +337,9 @@
 	struct thresh_restart tr;
 	unsigned long new;
 
+	if (!b->interrupt_capable)
+		return -EINVAL;
+
 	if (strict_strtoul(buf, 0, &new) < 0)
 		return -EINVAL;
 
@@ -390,10 +421,10 @@
 RW_ATTR(error_count);
 
 static struct attribute *default_attrs[] = {
-	&interrupt_enable.attr,
 	&threshold_limit.attr,
 	&error_count.attr,
-	NULL
+	NULL,	/* possibly interrupt_enable if supported, see below */
+	NULL,
 };
 
 #define to_block(k)	container_of(k, struct threshold_block, kobj)
@@ -467,8 +498,14 @@
 	b->cpu			= cpu;
 	b->address		= address;
 	b->interrupt_enable	= 0;
+	b->interrupt_capable	= lvt_interrupt_supported(bank, high);
 	b->threshold_limit	= THRESHOLD_MAX;
 
+	if (b->interrupt_capable)
+		threshold_ktype.default_attrs[2] = &interrupt_enable.attr;
+	else
+		threshold_ktype.default_attrs[2] = NULL;
+
 	INIT_LIST_HEAD(&b->miscj);
 
 	if (per_cpu(threshold_banks, cpu)[bank]->blocks) {
diff --git a/arch/x86/kernel/cpu/perf_event_amd.c b/arch/x86/kernel/cpu/perf_event_amd.c
index 6565226..11a4eb9 100644
--- a/arch/x86/kernel/cpu/perf_event_amd.c
+++ b/arch/x86/kernel/cpu/perf_event_amd.c
@@ -496,6 +496,7 @@
  * 0x023	DE	PERF_CTL[2:0]
  * 0x02D	LS	PERF_CTL[3]
  * 0x02E	LS	PERF_CTL[3,0]
+ * 0x031	LS	PERF_CTL[2:0] (**)
  * 0x043	CU	PERF_CTL[2:0]
  * 0x045	CU	PERF_CTL[2:0]
  * 0x046	CU	PERF_CTL[2:0]
@@ -509,10 +510,12 @@
  * 0x0DD	LS	PERF_CTL[5:0]
  * 0x0DE	LS	PERF_CTL[5:0]
  * 0x0DF	LS	PERF_CTL[5:0]
+ * 0x1C0	EX	PERF_CTL[5:3]
  * 0x1D6	EX	PERF_CTL[5:0]
  * 0x1D8	EX	PERF_CTL[5:0]
  *
- * (*) depending on the umask all FPU counters may be used
+ * (*)  depending on the umask all FPU counters may be used
+ * (**) only one unitmask enabled at a time
  */
 
 static struct event_constraint amd_f15_PMC0  = EVENT_CONSTRAINT(0, 0x01, 0);
@@ -562,6 +565,12 @@
 			return &amd_f15_PMC3;
 		case 0x02E:
 			return &amd_f15_PMC30;
+		case 0x031:
+			if (hweight_long(hwc->config & ARCH_PERFMON_EVENTSEL_UMASK) <= 1)
+				return &amd_f15_PMC20;
+			return &emptyconstraint;
+		case 0x1C0:
+			return &amd_f15_PMC53;
 		default:
 			return &amd_f15_PMC50;
 		}
diff --git a/arch/x86/kernel/dumpstack.c b/arch/x86/kernel/dumpstack.c
index 1b81839..571246d 100644
--- a/arch/x86/kernel/dumpstack.c
+++ b/arch/x86/kernel/dumpstack.c
@@ -271,7 +271,7 @@
 			current->thread.trap_nr, SIGSEGV) == NOTIFY_STOP)
 		return 1;
 
-	show_registers(regs);
+	show_regs(regs);
 #ifdef CONFIG_X86_32
 	if (user_mode_vm(regs)) {
 		sp = regs->sp;
@@ -311,16 +311,33 @@
 
 static int __init kstack_setup(char *s)
 {
+	ssize_t ret;
+	unsigned long val;
+
 	if (!s)
 		return -EINVAL;
-	kstack_depth_to_print = simple_strtoul(s, NULL, 0);
+
+	ret = kstrtoul(s, 0, &val);
+	if (ret)
+		return ret;
+	kstack_depth_to_print = val;
 	return 0;
 }
 early_param("kstack", kstack_setup);
 
 static int __init code_bytes_setup(char *s)
 {
-	code_bytes = simple_strtoul(s, NULL, 0);
+	ssize_t ret;
+	unsigned long val;
+
+	if (!s)
+		return -EINVAL;
+
+	ret = kstrtoul(s, 0, &val);
+	if (ret)
+		return ret;
+
+	code_bytes = val;
 	if (code_bytes > 8192)
 		code_bytes = 8192;
 
diff --git a/arch/x86/kernel/dumpstack_32.c b/arch/x86/kernel/dumpstack_32.c
index 88ec912..e0b1d78 100644
--- a/arch/x86/kernel/dumpstack_32.c
+++ b/arch/x86/kernel/dumpstack_32.c
@@ -82,7 +82,7 @@
 }
 
 
-void show_registers(struct pt_regs *regs)
+void show_regs(struct pt_regs *regs)
 {
 	int i;
 
diff --git a/arch/x86/kernel/dumpstack_64.c b/arch/x86/kernel/dumpstack_64.c
index 17107bd..791b761 100644
--- a/arch/x86/kernel/dumpstack_64.c
+++ b/arch/x86/kernel/dumpstack_64.c
@@ -245,7 +245,7 @@
 	show_trace_log_lvl(task, regs, sp, bp, log_lvl);
 }
 
-void show_registers(struct pt_regs *regs)
+void show_regs(struct pt_regs *regs)
 {
 	int i;
 	unsigned long sp;
diff --git a/arch/x86/kernel/entry_32.S b/arch/x86/kernel/entry_32.S
index 7b784f4..01ccf9b 100644
--- a/arch/x86/kernel/entry_32.S
+++ b/arch/x86/kernel/entry_32.S
@@ -56,6 +56,7 @@
 #include <asm/irq_vectors.h>
 #include <asm/cpufeature.h>
 #include <asm/alternative-asm.h>
+#include <asm/asm.h>
 
 /* Avoid __ASSEMBLER__'ifying <linux/audit.h> just for this.  */
 #include <linux/elf-em.h>
@@ -151,10 +152,8 @@
 .pushsection .fixup, "ax"
 99:	movl $0, (%esp)
 	jmp 98b
-.section __ex_table, "a"
-	.align 4
-	.long 98b, 99b
 .popsection
+	_ASM_EXTABLE(98b,99b)
 .endm
 
 .macro PTGS_TO_GS
@@ -164,10 +163,8 @@
 .pushsection .fixup, "ax"
 99:	movl $0, PT_GS(%esp)
 	jmp 98b
-.section __ex_table, "a"
-	.align 4
-	.long 98b, 99b
 .popsection
+	_ASM_EXTABLE(98b,99b)
 .endm
 
 .macro GS_TO_REG reg
@@ -249,12 +246,10 @@
 	jmp 2b
 6:	movl $0, (%esp)
 	jmp 3b
-.section __ex_table, "a"
-	.align 4
-	.long 1b, 4b
-	.long 2b, 5b
-	.long 3b, 6b
 .popsection
+	_ASM_EXTABLE(1b,4b)
+	_ASM_EXTABLE(2b,5b)
+	_ASM_EXTABLE(3b,6b)
 	POP_GS_EX
 .endm
 
@@ -415,10 +410,7 @@
 	jae syscall_fault
 1:	movl (%ebp),%ebp
 	movl %ebp,PT_EBP(%esp)
-.section __ex_table,"a"
-	.align 4
-	.long 1b,syscall_fault
-.previous
+	_ASM_EXTABLE(1b,syscall_fault)
 
 	GET_THREAD_INFO(%ebp)
 
@@ -485,10 +477,8 @@
 .pushsection .fixup,"ax"
 2:	movl $0,PT_FS(%esp)
 	jmp 1b
-.section __ex_table,"a"
-	.align 4
-	.long 1b,2b
 .popsection
+	_ASM_EXTABLE(1b,2b)
 	PTGS_TO_GS_EX
 ENDPROC(ia32_sysenter_target)
 
@@ -543,10 +533,7 @@
 	pushl $do_iret_error
 	jmp error_code
 .previous
-.section __ex_table,"a"
-	.align 4
-	.long irq_return,iret_exc
-.previous
+	_ASM_EXTABLE(irq_return,iret_exc)
 
 	CFI_RESTORE_STATE
 ldt_ss:
@@ -901,10 +888,7 @@
 #ifdef CONFIG_PARAVIRT
 ENTRY(native_iret)
 	iret
-.section __ex_table,"a"
-	.align 4
-	.long native_iret, iret_exc
-.previous
+	_ASM_EXTABLE(native_iret, iret_exc)
 END(native_iret)
 
 ENTRY(native_irq_enable_sysexit)
@@ -1093,13 +1077,10 @@
 	movl %eax,16(%esp)
 	jmp 4b
 .previous
-.section __ex_table,"a"
-	.align 4
-	.long 1b,6b
-	.long 2b,7b
-	.long 3b,8b
-	.long 4b,9b
-.previous
+	_ASM_EXTABLE(1b,6b)
+	_ASM_EXTABLE(2b,7b)
+	_ASM_EXTABLE(3b,8b)
+	_ASM_EXTABLE(4b,9b)
 ENDPROC(xen_failsafe_callback)
 
 BUILD_INTERRUPT3(xen_hvm_callback_vector, XEN_HVM_EVTCHN_CALLBACK,
diff --git a/arch/x86/kernel/entry_64.S b/arch/x86/kernel/entry_64.S
index cdc79b5..320852d 100644
--- a/arch/x86/kernel/entry_64.S
+++ b/arch/x86/kernel/entry_64.S
@@ -55,6 +55,7 @@
 #include <asm/paravirt.h>
 #include <asm/ftrace.h>
 #include <asm/percpu.h>
+#include <asm/asm.h>
 #include <linux/err.h>
 
 /* Avoid __ASSEMBLER__'ifying <linux/audit.h> just for this.  */
@@ -900,18 +901,12 @@
 
 irq_return:
 	INTERRUPT_RETURN
-
-	.section __ex_table, "a"
-	.quad irq_return, bad_iret
-	.previous
+	_ASM_EXTABLE(irq_return, bad_iret)
 
 #ifdef CONFIG_PARAVIRT
 ENTRY(native_iret)
 	iretq
-
-	.section __ex_table,"a"
-	.quad native_iret, bad_iret
-	.previous
+	_ASM_EXTABLE(native_iret, bad_iret)
 #endif
 
 	.section .fixup,"ax"
@@ -1181,10 +1176,7 @@
 	CFI_ENDPROC
 END(native_load_gs_index)
 
-	.section __ex_table,"a"
-	.align 8
-	.quad gs_change,bad_gs
-	.previous
+	_ASM_EXTABLE(gs_change,bad_gs)
 	.section .fixup,"ax"
 	/* running with kernelgs */
 bad_gs:
diff --git a/arch/x86/kernel/head_32.S b/arch/x86/kernel/head_32.S
index ce0be7c..463c979 100644
--- a/arch/x86/kernel/head_32.S
+++ b/arch/x86/kernel/head_32.S
@@ -21,6 +21,7 @@
 #include <asm/msr-index.h>
 #include <asm/cpufeature.h>
 #include <asm/percpu.h>
+#include <asm/nops.h>
 
 /* Physical address */
 #define pa(X) ((X) - __PAGE_OFFSET)
@@ -363,28 +364,23 @@
 	pushl $0
 	popfl
 
-#ifdef CONFIG_SMP
-	cmpb $0, ready
-	jnz checkCPUtype
-#endif /* CONFIG_SMP */
-
 /*
  * start system 32-bit setup. We need to re-do some of the things done
  * in 16-bit mode for the "real" operations.
  */
-	call setup_idt
-
-checkCPUtype:
-
-	movl $-1,X86_CPUID		#  -1 for no CPUID initially
-
+	movl setup_once_ref,%eax
+	andl %eax,%eax
+	jz 1f				# Did we do this already?
+	call *%eax
+1:
+	
 /* check if it is 486 or 386. */
 /*
  * XXX - this does a lot of unnecessary setup.  Alignment checks don't
  * apply at our cpl of 0 and the stack ought to be aligned already, and
  * we don't need to preserve eflags.
  */
-
+	movl $-1,X86_CPUID	# -1 for no CPUID initially
 	movb $3,X86		# at least 386
 	pushfl			# push EFLAGS
 	popl %eax		# get EFLAGS
@@ -450,21 +446,6 @@
 	movl $(__KERNEL_PERCPU), %eax
 	movl %eax,%fs			# set this cpu's percpu
 
-#ifdef CONFIG_CC_STACKPROTECTOR
-	/*
-	 * The linker can't handle this by relocation.  Manually set
-	 * base address in stack canary segment descriptor.
-	 */
-	cmpb $0,ready
-	jne 1f
-	movl $gdt_page,%eax
-	movl $stack_canary,%ecx
-	movw %cx, 8 * GDT_ENTRY_STACK_CANARY + 2(%eax)
-	shrl $16, %ecx
-	movb %cl, 8 * GDT_ENTRY_STACK_CANARY + 4(%eax)
-	movb %ch, 8 * GDT_ENTRY_STACK_CANARY + 7(%eax)
-1:
-#endif
 	movl $(__KERNEL_STACK_CANARY),%eax
 	movl %eax,%gs
 
@@ -473,7 +454,6 @@
 
 	cld			# gcc2 wants the direction flag cleared at all times
 	pushl $0		# fake return address for unwinder
-	movb $1, ready
 	jmp *(initial_code)
 
 /*
@@ -495,81 +475,122 @@
 	.byte 0xDB,0xE4		/* fsetpm for 287, ignored by 387 */
 	ret
 
+	
+#include "verify_cpu.S"
+
 /*
- *  setup_idt
+ *  setup_once
  *
- *  sets up a idt with 256 entries pointing to
- *  ignore_int, interrupt gates. It doesn't actually load
- *  idt - that can be done only after paging has been enabled
- *  and the kernel moved to PAGE_OFFSET. Interrupts
- *  are enabled elsewhere, when we can be relatively
- *  sure everything is ok.
+ *  The setup work we only want to run on the BSP.
  *
  *  Warning: %esi is live across this function.
  */
-setup_idt:
-	lea ignore_int,%edx
+__INIT
+setup_once:
+	/*
+	 * Set up a idt with 256 entries pointing to ignore_int,
+	 * interrupt gates. It doesn't actually load idt - that needs
+	 * to be done on each CPU. Interrupts are enabled elsewhere,
+	 * when we can be relatively sure everything is ok.
+	 */
+
+	movl $idt_table,%edi
+	movl $early_idt_handlers,%eax
+	movl $NUM_EXCEPTION_VECTORS,%ecx
+1:
+	movl %eax,(%edi)
+	movl %eax,4(%edi)
+	/* interrupt gate, dpl=0, present */
+	movl $(0x8E000000 + __KERNEL_CS),2(%edi)
+	addl $9,%eax
+	addl $8,%edi
+	loop 1b
+
+	movl $256 - NUM_EXCEPTION_VECTORS,%ecx
+	movl $ignore_int,%edx
 	movl $(__KERNEL_CS << 16),%eax
 	movw %dx,%ax		/* selector = 0x0010 = cs */
 	movw $0x8E00,%dx	/* interrupt gate - dpl=0, present */
-
-	lea idt_table,%edi
-	mov $256,%ecx
-rp_sidt:
+2:
 	movl %eax,(%edi)
 	movl %edx,4(%edi)
 	addl $8,%edi
-	dec %ecx
-	jne rp_sidt
+	loop 2b
 
-.macro	set_early_handler handler,trapno
-	lea \handler,%edx
-	movl $(__KERNEL_CS << 16),%eax
-	movw %dx,%ax
-	movw $0x8E00,%dx	/* interrupt gate - dpl=0, present */
-	lea idt_table,%edi
-	movl %eax,8*\trapno(%edi)
-	movl %edx,8*\trapno+4(%edi)
-.endm
+#ifdef CONFIG_CC_STACKPROTECTOR
+	/*
+	 * Configure the stack canary. The linker can't handle this by
+	 * relocation.  Manually set base address in stack canary
+	 * segment descriptor.
+	 */
+	movl $gdt_page,%eax
+	movl $stack_canary,%ecx
+	movw %cx, 8 * GDT_ENTRY_STACK_CANARY + 2(%eax)
+	shrl $16, %ecx
+	movb %cl, 8 * GDT_ENTRY_STACK_CANARY + 4(%eax)
+	movb %ch, 8 * GDT_ENTRY_STACK_CANARY + 7(%eax)
+#endif
 
-	set_early_handler handler=early_divide_err,trapno=0
-	set_early_handler handler=early_illegal_opcode,trapno=6
-	set_early_handler handler=early_protection_fault,trapno=13
-	set_early_handler handler=early_page_fault,trapno=14
-
+	andl $0,setup_once_ref	/* Once is enough, thanks */
 	ret
 
-early_divide_err:
-	xor %edx,%edx
-	pushl $0	/* fake errcode */
-	jmp early_fault
-
-early_illegal_opcode:
-	movl $6,%edx
-	pushl $0	/* fake errcode */
-	jmp early_fault
-
-early_protection_fault:
-	movl $13,%edx
-	jmp early_fault
-
-early_page_fault:
-	movl $14,%edx
-	jmp early_fault
-
-early_fault:
+ENTRY(early_idt_handlers)
+	# 36(%esp) %eflags
+	# 32(%esp) %cs
+	# 28(%esp) %eip
+	# 24(%rsp) error code
+	i = 0
+	.rept NUM_EXCEPTION_VECTORS
+	.if (EXCEPTION_ERRCODE_MASK >> i) & 1
+	ASM_NOP2
+	.else
+	pushl $0		# Dummy error code, to make stack frame uniform
+	.endif
+	pushl $i		# 20(%esp) Vector number
+	jmp early_idt_handler
+	i = i + 1
+	.endr
+ENDPROC(early_idt_handlers)
+	
+	/* This is global to keep gas from relaxing the jumps */
+ENTRY(early_idt_handler)
 	cld
-#ifdef CONFIG_PRINTK
-	pusha
+	cmpl $2,%ss:early_recursion_flag
+	je hlt_loop
+	incl %ss:early_recursion_flag
+
+	push %eax		# 16(%esp)
+	push %ecx		# 12(%esp)
+	push %edx		#  8(%esp)
+	push %ds		#  4(%esp)
+	push %es		#  0(%esp)
 	movl $(__KERNEL_DS),%eax
 	movl %eax,%ds
 	movl %eax,%es
-	cmpl $2,early_recursion_flag
-	je hlt_loop
-	incl early_recursion_flag
+
+	cmpl $(__KERNEL_CS),32(%esp)
+	jne 10f
+
+	leal 28(%esp),%eax	# Pointer to %eip
+	call early_fixup_exception
+	andl %eax,%eax
+	jnz ex_entry		/* found an exception entry */
+
+10:
+#ifdef CONFIG_PRINTK
+	xorl %eax,%eax
+	movw %ax,2(%esp)	/* clean up the segment values on some cpus */
+	movw %ax,6(%esp)
+	movw %ax,34(%esp)
+	leal  40(%esp),%eax
+	pushl %eax		/* %esp before the exception */
+	pushl %ebx
+	pushl %ebp
+	pushl %esi
+	pushl %edi
 	movl %cr2,%eax
 	pushl %eax
-	pushl %edx		/* trapno */
+	pushl (20+6*4)(%esp)	/* trapno */
 	pushl $fault_msg
 	call printk
 #endif
@@ -578,6 +599,17 @@
 	hlt
 	jmp hlt_loop
 
+ex_entry:
+	pop %es
+	pop %ds
+	pop %edx
+	pop %ecx
+	pop %eax
+	addl $8,%esp		/* drop vector number and error code */
+	decl %ss:early_recursion_flag
+	iret
+ENDPROC(early_idt_handler)
+
 /* This is the default interrupt "handler" :-) */
 	ALIGN
 ignore_int:
@@ -611,13 +643,18 @@
 	popl %eax
 #endif
 	iret
+ENDPROC(ignore_int)
+__INITDATA
+	.align 4
+early_recursion_flag:
+	.long 0
 
-#include "verify_cpu.S"
-
-	__REFDATA
-.align 4
+__REFDATA
+	.align 4
 ENTRY(initial_code)
 	.long i386_start_kernel
+ENTRY(setup_once_ref)
+	.long setup_once
 
 /*
  * BSS section
@@ -670,22 +707,19 @@
 ENTRY(stack_start)
 	.long init_thread_union+THREAD_SIZE
 
-early_recursion_flag:
-	.long 0
-
-ready:	.byte 0
-
+__INITRODATA
 int_msg:
 	.asciz "Unknown interrupt or fault at: %p %p %p\n"
 
 fault_msg:
 /* fault info: */
 	.ascii "BUG: Int %d: CR2 %p\n"
-/* pusha regs: */
-	.ascii "     EDI %p  ESI %p  EBP %p  ESP %p\n"
-	.ascii "     EBX %p  EDX %p  ECX %p  EAX %p\n"
+/* regs pushed in early_idt_handler: */
+	.ascii "     EDI %p  ESI %p  EBP %p  EBX %p\n"
+	.ascii "     ESP %p   ES %p   DS %p\n"
+	.ascii "     EDX %p  ECX %p  EAX %p\n"
 /* fault frame: */
-	.ascii "     err %p  EIP %p   CS %p  flg %p\n"
+	.ascii "     vec %p  err %p  EIP %p   CS %p  flg %p\n"
 	.ascii "Stack: %p %p %p %p %p %p %p %p\n"
 	.ascii "       %p %p %p %p %p %p %p %p\n"
 	.asciz "       %p %p %p %p %p %p %p %p\n"
@@ -699,6 +733,7 @@
  * segment size, and 32-bit linear address value:
  */
 
+	.data
 .globl boot_gdt_descr
 .globl idt_descr
 
diff --git a/arch/x86/kernel/head_64.S b/arch/x86/kernel/head_64.S
index 40f4eb3..7a40f24 100644
--- a/arch/x86/kernel/head_64.S
+++ b/arch/x86/kernel/head_64.S
@@ -19,12 +19,15 @@
 #include <asm/cache.h>
 #include <asm/processor-flags.h>
 #include <asm/percpu.h>
+#include <asm/nops.h>
 
 #ifdef CONFIG_PARAVIRT
 #include <asm/asm-offsets.h>
 #include <asm/paravirt.h>
+#define GET_CR2_INTO(reg) GET_CR2_INTO_RAX ; movq %rax, reg
 #else
-#define GET_CR2_INTO_RCX movq %cr2, %rcx
+#define GET_CR2_INTO(reg) movq %cr2, reg
+#define INTERRUPT_RETURN iretq
 #endif
 
 /* we are not able to switch in one step to the final KERNEL ADDRESS SPACE
@@ -270,36 +273,56 @@
 	jmp bad_address
 
 	.section ".init.text","ax"
-#ifdef CONFIG_EARLY_PRINTK
 	.globl early_idt_handlers
 early_idt_handlers:
+	# 104(%rsp) %rflags
+	#  96(%rsp) %cs
+	#  88(%rsp) %rip
+	#  80(%rsp) error code
 	i = 0
 	.rept NUM_EXCEPTION_VECTORS
-	movl $i, %esi
+	.if (EXCEPTION_ERRCODE_MASK >> i) & 1
+	ASM_NOP2
+	.else
+	pushq $0		# Dummy error code, to make stack frame uniform
+	.endif
+	pushq $i		# 72(%rsp) Vector number
 	jmp early_idt_handler
 	i = i + 1
 	.endr
-#endif
 
 ENTRY(early_idt_handler)
-#ifdef CONFIG_EARLY_PRINTK
+	cld
+
 	cmpl $2,early_recursion_flag(%rip)
 	jz  1f
 	incl early_recursion_flag(%rip)
-	GET_CR2_INTO_RCX
-	movq %rcx,%r9
-	xorl %r8d,%r8d		# zero for error code
-	movl %esi,%ecx		# get vector number
-	# Test %ecx against mask of vectors that push error code.
-	cmpl $31,%ecx
-	ja 0f
-	movl $1,%eax
-	salq %cl,%rax
-	testl $0x27d00,%eax
-	je 0f
-	popq %r8		# get error code
-0:	movq 0(%rsp),%rcx	# get ip
-	movq 8(%rsp),%rdx	# get cs
+
+	pushq %rax		# 64(%rsp)
+	pushq %rcx		# 56(%rsp)
+	pushq %rdx		# 48(%rsp)
+	pushq %rsi		# 40(%rsp)
+	pushq %rdi		# 32(%rsp)
+	pushq %r8		# 24(%rsp)
+	pushq %r9		# 16(%rsp)
+	pushq %r10		#  8(%rsp)
+	pushq %r11		#  0(%rsp)
+
+	cmpl $__KERNEL_CS,96(%rsp)
+	jne 10f
+
+	leaq 88(%rsp),%rdi	# Pointer to %rip
+	call early_fixup_exception
+	andl %eax,%eax
+	jnz 20f			# Found an exception entry
+
+10:
+#ifdef CONFIG_EARLY_PRINTK
+	GET_CR2_INTO(%r9)	# can clobber any volatile register if pv
+	movl 80(%rsp),%r8d	# error code
+	movl 72(%rsp),%esi	# vector number
+	movl 96(%rsp),%edx	# %cs
+	movq 88(%rsp),%rcx	# %rip
 	xorl %eax,%eax
 	leaq early_idt_msg(%rip),%rdi
 	call early_printk
@@ -308,17 +331,32 @@
 	call dump_stack
 #ifdef CONFIG_KALLSYMS	
 	leaq early_idt_ripmsg(%rip),%rdi
-	movq 0(%rsp),%rsi	# get rip again
+	movq 40(%rsp),%rsi	# %rip again
 	call __print_symbol
 #endif
 #endif /* EARLY_PRINTK */
 1:	hlt
 	jmp 1b
 
-#ifdef CONFIG_EARLY_PRINTK
+20:	# Exception table entry found
+	popq %r11
+	popq %r10
+	popq %r9
+	popq %r8
+	popq %rdi
+	popq %rsi
+	popq %rdx
+	popq %rcx
+	popq %rax
+	addq $16,%rsp		# drop vector number and error code
+	decl early_recursion_flag(%rip)
+	INTERRUPT_RETURN
+
+	.balign 4
 early_recursion_flag:
 	.long 0
 
+#ifdef CONFIG_EARLY_PRINTK
 early_idt_msg:
 	.asciz "PANIC: early exception %02lx rip %lx:%lx error %lx cr2 %lx\n"
 early_idt_ripmsg:
diff --git a/arch/x86/kernel/kprobes.c b/arch/x86/kernel/kprobes.c
index e213fc8..e2f751e 100644
--- a/arch/x86/kernel/kprobes.c
+++ b/arch/x86/kernel/kprobes.c
@@ -1037,9 +1037,9 @@
 			       "current sp %p does not match saved sp %p\n",
 			       stack_addr(regs), kcb->jprobe_saved_sp);
 			printk(KERN_ERR "Saved registers for jprobe %p\n", jp);
-			show_registers(saved_regs);
+			show_regs(saved_regs);
 			printk(KERN_ERR "Current registers\n");
-			show_registers(regs);
+			show_regs(regs);
 			BUG();
 		}
 		*regs = kcb->jprobe_saved_regs;
diff --git a/arch/x86/kernel/mca_32.c b/arch/x86/kernel/mca_32.c
deleted file mode 100644
index 7eb1e2b..0000000
--- a/arch/x86/kernel/mca_32.c
+++ /dev/null
@@ -1,476 +0,0 @@
-/*
- *  Written by Martin Kolinek, February 1996
- *
- * Changes:
- *
- *	Chris Beauregard July 28th, 1996
- *	- Fixed up integrated SCSI detection
- *
- *	Chris Beauregard August 3rd, 1996
- *	- Made mca_info local
- *	- Made integrated registers accessible through standard function calls
- *	- Added name field
- *	- More sanity checking
- *
- *	Chris Beauregard August 9th, 1996
- *	- Rewrote /proc/mca
- *
- *	Chris Beauregard January 7th, 1997
- *	- Added basic NMI-processing
- *	- Added more information to mca_info structure
- *
- *	David Weinehall October 12th, 1998
- *	- Made a lot of cleaning up in the source
- *	- Added use of save_flags / restore_flags
- *	- Added the 'driver_loaded' flag in MCA_adapter
- *	- Added an alternative implemention of ZP Gu's mca_find_unused_adapter
- *
- *	David Weinehall March 24th, 1999
- *	- Fixed the output of 'Driver Installed' in /proc/mca/pos
- *	- Made the Integrated Video & SCSI show up even if they have id 0000
- *
- *	Alexander Viro November 9th, 1999
- *	- Switched to regular procfs methods
- *
- *	Alfred Arnold & David Weinehall August 23rd, 2000
- *	- Added support for Planar POS-registers
- */
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/mca.h>
-#include <linux/kprobes.h>
-#include <linux/slab.h>
-#include <asm/io.h>
-#include <linux/proc_fs.h>
-#include <linux/mman.h>
-#include <linux/mm.h>
-#include <linux/pagemap.h>
-#include <linux/ioport.h>
-#include <asm/uaccess.h>
-#include <linux/init.h>
-
-static unsigned char which_scsi;
-
-int MCA_bus;
-EXPORT_SYMBOL(MCA_bus);
-
-/*
- * Motherboard register spinlock. Untested on SMP at the moment, but
- * are there any MCA SMP boxes?
- *
- * Yes - Alan
- */
-static DEFINE_SPINLOCK(mca_lock);
-
-/* Build the status info for the adapter */
-
-static void mca_configure_adapter_status(struct mca_device *mca_dev)
-{
-	mca_dev->status = MCA_ADAPTER_NONE;
-
-	mca_dev->pos_id = mca_dev->pos[0]
-		+ (mca_dev->pos[1] << 8);
-
-	if (!mca_dev->pos_id && mca_dev->slot < MCA_MAX_SLOT_NR) {
-
-		/*
-		 * id = 0x0000 usually indicates hardware failure,
-		 * however, ZP Gu (zpg@castle.net> reports that his 9556
-		 * has 0x0000 as id and everything still works. There
-		 * also seem to be an adapter with id = 0x0000; the
-		 * NCR Parallel Bus Memory Card. Until this is confirmed,
-		 * however, this code will stay.
-		 */
-
-		mca_dev->status = MCA_ADAPTER_ERROR;
-
-		return;
-	} else if (mca_dev->pos_id != 0xffff) {
-
-		/*
-		 * 0xffff usually indicates that there's no adapter,
-		 * however, some integrated adapters may have 0xffff as
-		 * their id and still be valid. Examples are on-board
-		 * VGA of the 55sx, the integrated SCSI of the 56 & 57,
-		 * and possibly also the 95 ULTIMEDIA.
-		 */
-
-		mca_dev->status = MCA_ADAPTER_NORMAL;
-	}
-
-	if ((mca_dev->pos_id == 0xffff ||
-	    mca_dev->pos_id == 0x0000) && mca_dev->slot >= MCA_MAX_SLOT_NR) {
-		int j;
-
-		for (j = 2; j < 8; j++) {
-			if (mca_dev->pos[j] != 0xff) {
-				mca_dev->status = MCA_ADAPTER_NORMAL;
-				break;
-			}
-		}
-	}
-
-	if (!(mca_dev->pos[2] & MCA_ENABLED)) {
-
-		/* enabled bit is in POS 2 */
-
-		mca_dev->status = MCA_ADAPTER_DISABLED;
-	}
-} /* mca_configure_adapter_status */
-
-/*--------------------------------------------------------------------*/
-
-static struct resource mca_standard_resources[] = {
-	{ .start = 0x60, .end = 0x60, .name = "system control port B (MCA)" },
-	{ .start = 0x90, .end = 0x90, .name = "arbitration (MCA)" },
-	{ .start = 0x91, .end = 0x91, .name = "card Select Feedback (MCA)" },
-	{ .start = 0x92, .end = 0x92, .name = "system Control port A (MCA)" },
-	{ .start = 0x94, .end = 0x94, .name = "system board setup (MCA)" },
-	{ .start = 0x96, .end = 0x97, .name = "POS (MCA)" },
-	{ .start = 0x100, .end = 0x107, .name = "POS (MCA)" }
-};
-
-#define MCA_STANDARD_RESOURCES	ARRAY_SIZE(mca_standard_resources)
-
-/*
- *	mca_read_and_store_pos - read the POS registers into a memory buffer
- *      @pos: a char pointer to 8 bytes, contains the POS register value on
- *            successful return
- *
- *	Returns 1 if a card actually exists (i.e. the pos isn't
- *	all 0xff) or 0 otherwise
- */
-static int mca_read_and_store_pos(unsigned char *pos)
-{
-	int j;
-	int found = 0;
-
-	for (j = 0; j < 8; j++) {
-		pos[j] = inb_p(MCA_POS_REG(j));
-		if (pos[j] != 0xff) {
-			/* 0xff all across means no device. 0x00 means
-			 * something's broken, but a device is
-			 * probably there.  However, if you get 0x00
-			 * from a motherboard register it won't matter
-			 * what we find.  For the record, on the
-			 * 57SLC, the integrated SCSI adapter has
-			 * 0xffff for the adapter ID, but nonzero for
-			 * other registers.  */
-
-			found = 1;
-		}
-	}
-	return found;
-}
-
-static unsigned char mca_pc_read_pos(struct mca_device *mca_dev, int reg)
-{
-	unsigned char byte;
-	unsigned long flags;
-
-	if (reg < 0 || reg >= 8)
-		return 0;
-
-	spin_lock_irqsave(&mca_lock, flags);
-	if (mca_dev->pos_register) {
-		/* Disable adapter setup, enable motherboard setup */
-
-		outb_p(0, MCA_ADAPTER_SETUP_REG);
-		outb_p(mca_dev->pos_register, MCA_MOTHERBOARD_SETUP_REG);
-
-		byte = inb_p(MCA_POS_REG(reg));
-		outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG);
-	} else {
-
-		/* Make sure motherboard setup is off */
-
-		outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG);
-
-		/* Read the appropriate register */
-
-		outb_p(0x8|(mca_dev->slot & 0xf), MCA_ADAPTER_SETUP_REG);
-		byte = inb_p(MCA_POS_REG(reg));
-		outb_p(0, MCA_ADAPTER_SETUP_REG);
-	}
-	spin_unlock_irqrestore(&mca_lock, flags);
-
-	mca_dev->pos[reg] = byte;
-
-	return byte;
-}
-
-static void mca_pc_write_pos(struct mca_device *mca_dev, int reg,
-			     unsigned char byte)
-{
-	unsigned long flags;
-
-	if (reg < 0 || reg >= 8)
-		return;
-
-	spin_lock_irqsave(&mca_lock, flags);
-
-	/* Make sure motherboard setup is off */
-
-	outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG);
-
-	/* Read in the appropriate register */
-
-	outb_p(0x8|(mca_dev->slot&0xf), MCA_ADAPTER_SETUP_REG);
-	outb_p(byte, MCA_POS_REG(reg));
-	outb_p(0, MCA_ADAPTER_SETUP_REG);
-
-	spin_unlock_irqrestore(&mca_lock, flags);
-
-	/* Update the global register list, while we have the byte */
-
-	mca_dev->pos[reg] = byte;
-
-}
-
-/* for the primary MCA bus, we have identity transforms */
-static int mca_dummy_transform_irq(struct mca_device *mca_dev, int irq)
-{
-	return irq;
-}
-
-static int mca_dummy_transform_ioport(struct mca_device *mca_dev, int port)
-{
-	return port;
-}
-
-static void *mca_dummy_transform_memory(struct mca_device *mca_dev, void *mem)
-{
-	return mem;
-}
-
-
-static int __init mca_init(void)
-{
-	unsigned int i, j;
-	struct mca_device *mca_dev;
-	unsigned char pos[8];
-	short mca_builtin_scsi_ports[] = {0xf7, 0xfd, 0x00};
-	struct mca_bus *bus;
-
-	/*
-	 * WARNING: Be careful when making changes here. Putting an adapter
-	 * and the motherboard simultaneously into setup mode may result in
-	 * damage to chips (according to The Indispensable PC Hardware Book
-	 * by Hans-Peter Messmer). Also, we disable system interrupts (so
-	 * that we are not disturbed in the middle of this).
-	 */
-
-	/* Make sure the MCA bus is present */
-
-	if (mca_system_init()) {
-		printk(KERN_ERR "MCA bus system initialisation failed\n");
-		return -ENODEV;
-	}
-
-	if (!MCA_bus)
-		return -ENODEV;
-
-	printk(KERN_INFO "Micro Channel bus detected.\n");
-
-	/* All MCA systems have at least a primary bus */
-	bus = mca_attach_bus(MCA_PRIMARY_BUS);
-	if (!bus)
-		goto out_nomem;
-	bus->default_dma_mask = 0xffffffffLL;
-	bus->f.mca_write_pos = mca_pc_write_pos;
-	bus->f.mca_read_pos = mca_pc_read_pos;
-	bus->f.mca_transform_irq = mca_dummy_transform_irq;
-	bus->f.mca_transform_ioport = mca_dummy_transform_ioport;
-	bus->f.mca_transform_memory = mca_dummy_transform_memory;
-
-	/* get the motherboard device */
-	mca_dev = kzalloc(sizeof(struct mca_device), GFP_KERNEL);
-	if (unlikely(!mca_dev))
-		goto out_nomem;
-
-	/*
-	 * We do not expect many MCA interrupts during initialization,
-	 * but let us be safe:
-	 */
-	spin_lock_irq(&mca_lock);
-
-	/* Make sure adapter setup is off */
-
-	outb_p(0, MCA_ADAPTER_SETUP_REG);
-
-	/* Read motherboard POS registers */
-
-	mca_dev->pos_register = 0x7f;
-	outb_p(mca_dev->pos_register, MCA_MOTHERBOARD_SETUP_REG);
-	mca_dev->name[0] = 0;
-	mca_read_and_store_pos(mca_dev->pos);
-	mca_configure_adapter_status(mca_dev);
-	/* fake POS and slot for a motherboard */
-	mca_dev->pos_id = MCA_MOTHERBOARD_POS;
-	mca_dev->slot = MCA_MOTHERBOARD;
-	mca_register_device(MCA_PRIMARY_BUS, mca_dev);
-
-	mca_dev = kzalloc(sizeof(struct mca_device), GFP_ATOMIC);
-	if (unlikely(!mca_dev))
-		goto out_unlock_nomem;
-
-	/* Put motherboard into video setup mode, read integrated video
-	 * POS registers, and turn motherboard setup off.
-	 */
-
-	mca_dev->pos_register = 0xdf;
-	outb_p(mca_dev->pos_register, MCA_MOTHERBOARD_SETUP_REG);
-	mca_dev->name[0] = 0;
-	mca_read_and_store_pos(mca_dev->pos);
-	mca_configure_adapter_status(mca_dev);
-	/* fake POS and slot for the integrated video */
-	mca_dev->pos_id = MCA_INTEGVIDEO_POS;
-	mca_dev->slot = MCA_INTEGVIDEO;
-	mca_register_device(MCA_PRIMARY_BUS, mca_dev);
-
-	/*
-	 * Put motherboard into scsi setup mode, read integrated scsi
-	 * POS registers, and turn motherboard setup off.
-	 *
-	 * It seems there are two possible SCSI registers. Martin says that
-	 * for the 56,57, 0xf7 is the one, but fails on the 76.
-	 * Alfredo (apena@vnet.ibm.com) says
-	 * 0xfd works on his machine. We'll try both of them. I figure it's
-	 * a good bet that only one could be valid at a time. This could
-	 * screw up though if one is used for something else on the other
-	 * machine.
-	 */
-
-	for (i = 0; (which_scsi = mca_builtin_scsi_ports[i]) != 0; i++) {
-		outb_p(which_scsi, MCA_MOTHERBOARD_SETUP_REG);
-		if (mca_read_and_store_pos(pos))
-			break;
-	}
-	if (which_scsi) {
-		/* found a scsi card */
-		mca_dev = kzalloc(sizeof(struct mca_device), GFP_ATOMIC);
-		if (unlikely(!mca_dev))
-			goto out_unlock_nomem;
-
-		for (j = 0; j < 8; j++)
-			mca_dev->pos[j] = pos[j];
-
-		mca_configure_adapter_status(mca_dev);
-		/* fake POS and slot for integrated SCSI controller */
-		mca_dev->pos_id = MCA_INTEGSCSI_POS;
-		mca_dev->slot = MCA_INTEGSCSI;
-		mca_dev->pos_register = which_scsi;
-		mca_register_device(MCA_PRIMARY_BUS, mca_dev);
-	}
-
-	/* Turn off motherboard setup */
-
-	outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG);
-
-	/*
-	 * Now loop over MCA slots: put each adapter into setup mode, and
-	 * read its POS registers. Then put adapter setup off.
-	 */
-
-	for (i = 0; i < MCA_MAX_SLOT_NR; i++) {
-		outb_p(0x8|(i&0xf), MCA_ADAPTER_SETUP_REG);
-		if (!mca_read_and_store_pos(pos))
-			continue;
-
-		mca_dev = kzalloc(sizeof(struct mca_device), GFP_ATOMIC);
-		if (unlikely(!mca_dev))
-			goto out_unlock_nomem;
-
-		for (j = 0; j < 8; j++)
-			mca_dev->pos[j] = pos[j];
-
-		mca_dev->driver_loaded = 0;
-		mca_dev->slot = i;
-		mca_dev->pos_register = 0;
-		mca_configure_adapter_status(mca_dev);
-		mca_register_device(MCA_PRIMARY_BUS, mca_dev);
-	}
-	outb_p(0, MCA_ADAPTER_SETUP_REG);
-
-	/* Enable interrupts and return memory start */
-	spin_unlock_irq(&mca_lock);
-
-	for (i = 0; i < MCA_STANDARD_RESOURCES; i++)
-		request_resource(&ioport_resource, mca_standard_resources + i);
-
-	mca_do_proc_init();
-
-	return 0;
-
- out_unlock_nomem:
-	spin_unlock_irq(&mca_lock);
- out_nomem:
-	printk(KERN_EMERG "Failed memory allocation in MCA setup!\n");
-	return -ENOMEM;
-}
-
-subsys_initcall(mca_init);
-
-/*--------------------------------------------------------------------*/
-
-static __kprobes void
-mca_handle_nmi_device(struct mca_device *mca_dev, int check_flag)
-{
-	int slot = mca_dev->slot;
-
-	if (slot == MCA_INTEGSCSI) {
-		printk(KERN_CRIT "NMI: caused by MCA integrated SCSI adapter (%s)\n",
-			mca_dev->name);
-	} else if (slot == MCA_INTEGVIDEO) {
-		printk(KERN_CRIT "NMI: caused by MCA integrated video adapter (%s)\n",
-			mca_dev->name);
-	} else if (slot == MCA_MOTHERBOARD) {
-		printk(KERN_CRIT "NMI: caused by motherboard (%s)\n",
-			mca_dev->name);
-	}
-
-	/* More info available in POS 6 and 7? */
-
-	if (check_flag) {
-		unsigned char pos6, pos7;
-
-		pos6 = mca_device_read_pos(mca_dev, 6);
-		pos7 = mca_device_read_pos(mca_dev, 7);
-
-		printk(KERN_CRIT "NMI: POS 6 = 0x%x, POS 7 = 0x%x\n", pos6, pos7);
-	}
-
-} /* mca_handle_nmi_slot */
-
-/*--------------------------------------------------------------------*/
-
-static int __kprobes mca_handle_nmi_callback(struct device *dev, void *data)
-{
-	struct mca_device *mca_dev = to_mca_device(dev);
-	unsigned char pos5;
-
-	pos5 = mca_device_read_pos(mca_dev, 5);
-
-	if (!(pos5 & 0x80)) {
-		/*
-		 *  Bit 7 of POS 5 is reset when this adapter has a hardware
-		 * error. Bit 7 it reset if there's error information
-		 * available in POS 6 and 7.
-		 */
-		mca_handle_nmi_device(mca_dev, !(pos5 & 0x40));
-		return 1;
-	}
-	return 0;
-}
-
-void __kprobes mca_handle_nmi(void)
-{
-	/*
-	 *  First try - scan the various adapters and see if a specific
-	 * adapter was responsible for the error.
-	 */
-	bus_for_each_dev(&mca_bus_type, NULL, NULL, mca_handle_nmi_callback);
-}
diff --git a/arch/x86/kernel/microcode_core.c b/arch/x86/kernel/microcode_core.c
index c9bda6d..fbdfc69 100644
--- a/arch/x86/kernel/microcode_core.c
+++ b/arch/x86/kernel/microcode_core.c
@@ -299,12 +299,11 @@
 {
 	unsigned long val;
 	int cpu = dev->id;
-	int ret = 0;
-	char *end;
+	ssize_t ret = 0;
 
-	val = simple_strtoul(buf, &end, 0);
-	if (end == buf)
-		return -EINVAL;
+	ret = kstrtoul(buf, 0, &val);
+	if (ret)
+		return ret;
 
 	if (val == 1) {
 		get_online_cpus();
diff --git a/arch/x86/kernel/mpparse.c b/arch/x86/kernel/mpparse.c
index ca470e4..b02d4dd 100644
--- a/arch/x86/kernel/mpparse.c
+++ b/arch/x86/kernel/mpparse.c
@@ -97,7 +97,7 @@
 
 	set_bit(m->busid, mp_bus_not_pci);
 	if (strncmp(str, BUSTYPE_ISA, sizeof(BUSTYPE_ISA) - 1) == 0) {
-#if defined(CONFIG_EISA) || defined(CONFIG_MCA)
+#ifdef CONFIG_EISA
 		mp_bus_id_to_type[m->busid] = MP_BUS_ISA;
 #endif
 	} else if (strncmp(str, BUSTYPE_PCI, sizeof(BUSTYPE_PCI) - 1) == 0) {
@@ -105,12 +105,10 @@
 			x86_init.mpparse.mpc_oem_pci_bus(m);
 
 		clear_bit(m->busid, mp_bus_not_pci);
-#if defined(CONFIG_EISA) || defined(CONFIG_MCA)
+#ifdef CONFIG_EISA
 		mp_bus_id_to_type[m->busid] = MP_BUS_PCI;
 	} else if (strncmp(str, BUSTYPE_EISA, sizeof(BUSTYPE_EISA) - 1) == 0) {
 		mp_bus_id_to_type[m->busid] = MP_BUS_EISA;
-	} else if (strncmp(str, BUSTYPE_MCA, sizeof(BUSTYPE_MCA) - 1) == 0) {
-		mp_bus_id_to_type[m->busid] = MP_BUS_MCA;
 #endif
 	} else
 		printk(KERN_WARNING "Unknown bustype %s - ignoring\n", str);
@@ -368,9 +366,6 @@
 	case 3:
 		memcpy(bus.bustype, "EISA  ", 6);
 		break;
-	case 4:
-	case 7:
-		memcpy(bus.bustype, "MCA   ", 6);
 	}
 	MP_bus_info(&bus);
 	if (mpc_default_type > 4) {
@@ -623,7 +618,7 @@
 		return;
 	/*
 	 * If it is an SMP machine we should know now, unless the
-	 * configuration is in an EISA/MCA bus machine with an
+	 * configuration is in an EISA bus machine with an
 	 * extended bios data area.
 	 *
 	 * there is a real-mode segmented pointer pointing to the
diff --git a/arch/x86/kernel/nmi.c b/arch/x86/kernel/nmi.c
index a1faed5..9087527 100644
--- a/arch/x86/kernel/nmi.c
+++ b/arch/x86/kernel/nmi.c
@@ -19,8 +19,6 @@
 #include <linux/slab.h>
 #include <linux/export.h>
 
-#include <linux/mca.h>
-
 #if defined(CONFIG_EDAC)
 #include <linux/edac.h>
 #endif
@@ -209,7 +207,7 @@
 	pr_emerg(
 	"NMI: IOCK error (debug interrupt?) for reason %02x on CPU %d.\n",
 		 reason, smp_processor_id());
-	show_registers(regs);
+	show_regs(regs);
 
 	if (panic_on_io_nmi)
 		panic("NMI IOCK error: Not continuing");
@@ -247,16 +245,6 @@
 
 	__this_cpu_add(nmi_stats.unknown, 1);
 
-#ifdef CONFIG_MCA
-	/*
-	 * Might actually be able to figure out what the guilty party
-	 * is:
-	 */
-	if (MCA_bus) {
-		mca_handle_nmi();
-		return;
-	}
-#endif
 	pr_emerg("Uhhuh. NMI received for unknown reason %02x on CPU %d.\n",
 		 reason, smp_processor_id());
 
diff --git a/arch/x86/kernel/nmi_selftest.c b/arch/x86/kernel/nmi_selftest.c
index ff36986..e31bf8d 100644
--- a/arch/x86/kernel/nmi_selftest.c
+++ b/arch/x86/kernel/nmi_selftest.c
@@ -118,15 +118,15 @@
 		unexpected_testcase_failures++;
 
 		if (nmi_fail == FAILURE)
-			printk("FAILED |");
+			printk(KERN_CONT "FAILED |");
 		else if (nmi_fail == TIMEOUT)
-			printk("TIMEOUT|");
+			printk(KERN_CONT "TIMEOUT|");
 		else
-			printk("ERROR  |");
+			printk(KERN_CONT "ERROR  |");
 		dump_stack();
 	} else {
 		testcase_successes++;
-		printk("  ok  |");
+		printk(KERN_CONT "  ok  |");
 	}
 	testcase_total++;
 
@@ -151,10 +151,10 @@
 
 	print_testname("remote IPI");
 	dotest(remote_ipi, SUCCESS);
-	printk("\n");
+	printk(KERN_CONT "\n");
 	print_testname("local IPI");
 	dotest(local_ipi, SUCCESS);
-	printk("\n");
+	printk(KERN_CONT "\n");
 
 	cleanup_nmi_testsuite();
 
diff --git a/arch/x86/kernel/pci-calgary_64.c b/arch/x86/kernel/pci-calgary_64.c
index d0b2fb9..b72838b 100644
--- a/arch/x86/kernel/pci-calgary_64.c
+++ b/arch/x86/kernel/pci-calgary_64.c
@@ -1480,8 +1480,9 @@
 static int __init calgary_parse_options(char *p)
 {
 	unsigned int bridge;
+	unsigned long val;
 	size_t len;
-	char* endp;
+	ssize_t ret;
 
 	while (*p) {
 		if (!strncmp(p, "64k", 3))
@@ -1512,10 +1513,11 @@
 				++p;
 			if (*p == '\0')
 				break;
-			bridge = simple_strtoul(p, &endp, 0);
-			if (p == endp)
+			ret = kstrtoul(p, 0, &val);
+			if (ret)
 				break;
 
+			bridge = val;
 			if (bridge < MAX_PHB_BUS_NUM) {
 				printk(KERN_INFO "Calgary: disabling "
 				       "translation for PHB %#x\n", bridge);
diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c
index 8040b75..735279e 100644
--- a/arch/x86/kernel/process.c
+++ b/arch/x86/kernel/process.c
@@ -56,10 +56,16 @@
 struct kmem_cache *task_xstate_cachep;
 EXPORT_SYMBOL_GPL(task_xstate_cachep);
 
+/*
+ * this gets called so that we can store lazy state into memory and copy the
+ * current task into the new thread.
+ */
 int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)
 {
 	int ret;
 
+	unlazy_fpu(src);
+
 	*dst = *src;
 	if (fpu_allocated(&src->thread.fpu)) {
 		memset(&dst->thread.fpu, 0, sizeof(dst->thread.fpu));
@@ -89,6 +95,16 @@
 				  SLAB_PANIC | SLAB_NOTRACK, NULL);
 }
 
+static inline void drop_fpu(struct task_struct *tsk)
+{
+	/*
+	 * Forget coprocessor state..
+	 */
+	tsk->fpu_counter = 0;
+	clear_fpu(tsk);
+	clear_used_math();
+}
+
 /*
  * Free current thread data structures etc..
  */
@@ -111,12 +127,8 @@
 		put_cpu();
 		kfree(bp);
 	}
-}
 
-void show_regs(struct pt_regs *regs)
-{
-	show_registers(regs);
-	show_trace(NULL, regs, (unsigned long *)kernel_stack_pointer(regs), 0);
+	drop_fpu(me);
 }
 
 void show_regs_common(void)
@@ -151,12 +163,7 @@
 
 	flush_ptrace_hw_breakpoint(tsk);
 	memset(tsk->thread.tls_array, 0, sizeof(tsk->thread.tls_array));
-	/*
-	 * Forget coprocessor state..
-	 */
-	tsk->fpu_counter = 0;
-	clear_fpu(tsk);
-	clear_used_math();
+	drop_fpu(tsk);
 }
 
 static void hard_disable_TSC(void)
diff --git a/arch/x86/kernel/process_32.c b/arch/x86/kernel/process_32.c
index 01d8d40..516fa18 100644
--- a/arch/x86/kernel/process_32.c
+++ b/arch/x86/kernel/process_32.c
@@ -126,15 +126,6 @@
 	release_vm86_irqs(dead_task);
 }
 
-/*
- * This gets called before we allocate a new thread and copy
- * the current task into it.
- */
-void prepare_to_copy(struct task_struct *tsk)
-{
-	unlazy_fpu(tsk);
-}
-
 int copy_thread(unsigned long clone_flags, unsigned long sp,
 	unsigned long unused,
 	struct task_struct *p, struct pt_regs *regs)
diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c
index 28e8102..61cdf7f 100644
--- a/arch/x86/kernel/process_64.c
+++ b/arch/x86/kernel/process_64.c
@@ -145,15 +145,6 @@
 	return get_desc_base(&t->thread.tls_array[tls]);
 }
 
-/*
- * This gets called before we allocate a new thread and copy
- * the current task into it.
- */
-void prepare_to_copy(struct task_struct *tsk)
-{
-	unlazy_fpu(tsk);
-}
-
 int copy_thread(unsigned long clone_flags, unsigned long sp,
 		unsigned long unused,
 	struct task_struct *p, struct pt_regs *regs)
diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c
index d840e69..77215c2 100644
--- a/arch/x86/kernel/reboot.c
+++ b/arch/x86/kernel/reboot.c
@@ -39,7 +39,8 @@
 enum reboot_type reboot_type = BOOT_ACPI;
 int reboot_force;
 
-/* This variable is used privately to keep track of whether or not
+/*
+ * This variable is used privately to keep track of whether or not
  * reboot_type is still set to its default value (i.e., reboot= hasn't
  * been set on the command line).  This is needed so that we can
  * suppress DMI scanning for reboot quirks.  Without it, it's
@@ -51,7 +52,8 @@
 static int reboot_cpu = -1;
 #endif
 
-/* This is set if we need to go through the 'emergency' path.
+/*
+ * This is set if we need to go through the 'emergency' path.
  * When machine_emergency_restart() is called, we may be on
  * an inconsistent state and won't be able to do a clean cleanup
  */
@@ -60,22 +62,24 @@
 /* This is set by the PCI code if either type 1 or type 2 PCI is detected */
 bool port_cf9_safe = false;
 
-/* reboot=b[ios] | s[mp] | t[riple] | k[bd] | e[fi] [, [w]arm | [c]old] | p[ci]
-   warm   Don't set the cold reboot flag
-   cold   Set the cold reboot flag
-   bios   Reboot by jumping through the BIOS (only for X86_32)
-   smp    Reboot by executing reset on BSP or other CPU (only for X86_32)
-   triple Force a triple fault (init)
-   kbd    Use the keyboard controller. cold reset (default)
-   acpi   Use the RESET_REG in the FADT
-   efi    Use efi reset_system runtime service
-   pci    Use the so-called "PCI reset register", CF9
-   force  Avoid anything that could hang.
+/*
+ * reboot=b[ios] | s[mp] | t[riple] | k[bd] | e[fi] [, [w]arm | [c]old] | p[ci]
+ * warm   Don't set the cold reboot flag
+ * cold   Set the cold reboot flag
+ * bios   Reboot by jumping through the BIOS (only for X86_32)
+ * smp    Reboot by executing reset on BSP or other CPU (only for X86_32)
+ * triple Force a triple fault (init)
+ * kbd    Use the keyboard controller. cold reset (default)
+ * acpi   Use the RESET_REG in the FADT
+ * efi    Use efi reset_system runtime service
+ * pci    Use the so-called "PCI reset register", CF9
+ * force  Avoid anything that could hang.
  */
 static int __init reboot_setup(char *str)
 {
 	for (;;) {
-		/* Having anything passed on the command line via
+		/*
+		 * Having anything passed on the command line via
 		 * reboot= will cause us to disable DMI checking
 		 * below.
 		 */
@@ -98,9 +102,11 @@
 				if (isdigit(*(str+2)))
 					reboot_cpu = reboot_cpu*10 + (int)(*(str+2) - '0');
 			}
-				/* we will leave sorting out the final value
-				   when we are ready to reboot, since we might not
-				   have detected BSP APIC ID or smp_num_cpu */
+			/*
+			 * We will leave sorting out the final value
+			 * when we are ready to reboot, since we might not
+			 * have detected BSP APIC ID or smp_num_cpu
+			 */
 			break;
 #endif /* CONFIG_SMP */
 
@@ -150,6 +156,82 @@
 	return 0;
 }
 
+extern const unsigned char machine_real_restart_asm[];
+extern const u64 machine_real_restart_gdt[3];
+
+void machine_real_restart(unsigned int type)
+{
+	void *restart_va;
+	unsigned long restart_pa;
+	void (*restart_lowmem)(unsigned int);
+	u64 *lowmem_gdt;
+
+	local_irq_disable();
+
+	/*
+	 * Write zero to CMOS register number 0x0f, which the BIOS POST
+	 * routine will recognize as telling it to do a proper reboot.  (Well
+	 * that's what this book in front of me says -- it may only apply to
+	 * the Phoenix BIOS though, it's not clear).  At the same time,
+	 * disable NMIs by setting the top bit in the CMOS address register,
+	 * as we're about to do peculiar things to the CPU.  I'm not sure if
+	 * `outb_p' is needed instead of just `outb'.  Use it to be on the
+	 * safe side.  (Yes, CMOS_WRITE does outb_p's. -  Paul G.)
+	 */
+	spin_lock(&rtc_lock);
+	CMOS_WRITE(0x00, 0x8f);
+	spin_unlock(&rtc_lock);
+
+	/*
+	 * Switch back to the initial page table.
+	 */
+	load_cr3(initial_page_table);
+
+	/*
+	 * Write 0x1234 to absolute memory location 0x472.  The BIOS reads
+	 * this on booting to tell it to "Bypass memory test (also warm
+	 * boot)".  This seems like a fairly standard thing that gets set by
+	 * REBOOT.COM programs, and the previous reset routine did this
+	 * too. */
+	*((unsigned short *)0x472) = reboot_mode;
+
+	/* Patch the GDT in the low memory trampoline */
+	lowmem_gdt = TRAMPOLINE_SYM(machine_real_restart_gdt);
+
+	restart_va = TRAMPOLINE_SYM(machine_real_restart_asm);
+	restart_pa = virt_to_phys(restart_va);
+	restart_lowmem = (void (*)(unsigned int))restart_pa;
+
+	/* GDT[0]: GDT self-pointer */
+	lowmem_gdt[0] =
+		(u64)(sizeof(machine_real_restart_gdt) - 1) +
+		((u64)virt_to_phys(lowmem_gdt) << 16);
+	/* GDT[1]: 64K real mode code segment */
+	lowmem_gdt[1] =
+		GDT_ENTRY(0x009b, restart_pa, 0xffff);
+
+	/* Jump to the identity-mapped low memory code */
+	restart_lowmem(type);
+}
+#ifdef CONFIG_APM_MODULE
+EXPORT_SYMBOL(machine_real_restart);
+#endif
+
+#endif /* CONFIG_X86_32 */
+
+/*
+ * Some Apple MacBook and MacBookPro's needs reboot=p to be able to reboot
+ */
+static int __init set_pci_reboot(const struct dmi_system_id *d)
+{
+	if (reboot_type != BOOT_CF9) {
+		reboot_type = BOOT_CF9;
+		printk(KERN_INFO "%s series board detected. "
+		       "Selecting PCI-method for reboots.\n", d->ident);
+	}
+	return 0;
+}
+
 static int __init set_kbd_reboot(const struct dmi_system_id *d)
 {
 	if (reboot_type != BOOT_KBD) {
@@ -159,7 +241,12 @@
 	return 0;
 }
 
+/*
+ * This is a single dmi_table handling all reboot quirks.  Note that
+ * REBOOT_BIOS is only available for 32bit
+ */
 static struct dmi_system_id __initdata reboot_dmi_table[] = {
+#ifdef CONFIG_X86_32
 	{	/* Handle problems with rebooting on Dell E520's */
 		.callback = set_bios_reboot,
 		.ident = "Dell E520",
@@ -184,7 +271,7 @@
 			DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge 300/"),
 		},
 	},
-	{       /* Handle problems with rebooting on Dell Optiplex 745's SFF*/
+	{	/* Handle problems with rebooting on Dell Optiplex 745's SFF */
 		.callback = set_bios_reboot,
 		.ident = "Dell OptiPlex 745",
 		.matches = {
@@ -192,7 +279,7 @@
 			DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 745"),
 		},
 	},
-	{       /* Handle problems with rebooting on Dell Optiplex 745's DFF*/
+	{	/* Handle problems with rebooting on Dell Optiplex 745's DFF */
 		.callback = set_bios_reboot,
 		.ident = "Dell OptiPlex 745",
 		.matches = {
@@ -201,7 +288,7 @@
 			DMI_MATCH(DMI_BOARD_NAME, "0MM599"),
 		},
 	},
-	{       /* Handle problems with rebooting on Dell Optiplex 745 with 0KW626 */
+	{	/* Handle problems with rebooting on Dell Optiplex 745 with 0KW626 */
 		.callback = set_bios_reboot,
 		.ident = "Dell OptiPlex 745",
 		.matches = {
@@ -210,7 +297,7 @@
 			DMI_MATCH(DMI_BOARD_NAME, "0KW626"),
 		},
 	},
-	{   /* Handle problems with rebooting on Dell Optiplex 330 with 0KP561 */
+	{	/* Handle problems with rebooting on Dell Optiplex 330 with 0KP561 */
 		.callback = set_bios_reboot,
 		.ident = "Dell OptiPlex 330",
 		.matches = {
@@ -219,7 +306,7 @@
 			DMI_MATCH(DMI_BOARD_NAME, "0KP561"),
 		},
 	},
-	{   /* Handle problems with rebooting on Dell Optiplex 360 with 0T656F */
+	{	/* Handle problems with rebooting on Dell Optiplex 360 with 0T656F */
 		.callback = set_bios_reboot,
 		.ident = "Dell OptiPlex 360",
 		.matches = {
@@ -228,7 +315,7 @@
 			DMI_MATCH(DMI_BOARD_NAME, "0T656F"),
 		},
 	},
-	{	/* Handle problems with rebooting on Dell OptiPlex 760 with 0G919G*/
+	{	/* Handle problems with rebooting on Dell OptiPlex 760 with 0G919G */
 		.callback = set_bios_reboot,
 		.ident = "Dell OptiPlex 760",
 		.matches = {
@@ -301,7 +388,7 @@
 			DMI_MATCH(DMI_PRODUCT_NAME, "SBC-FITPC2"),
 		},
 	},
-	{       /* Handle problems with rebooting on ASUS P4S800 */
+	{	/* Handle problems with rebooting on ASUS P4S800 */
 		.callback = set_bios_reboot,
 		.ident = "ASUS P4S800",
 		.matches = {
@@ -309,7 +396,9 @@
 			DMI_MATCH(DMI_BOARD_NAME, "P4S800"),
 		},
 	},
-	{ /* Handle reboot issue on Acer Aspire one */
+#endif /* CONFIG_X86_32 */
+
+	{	/* Handle reboot issue on Acer Aspire one */
 		.callback = set_kbd_reboot,
 		.ident = "Acer Aspire One A110",
 		.matches = {
@@ -317,96 +406,6 @@
 			DMI_MATCH(DMI_PRODUCT_NAME, "AOA110"),
 		},
 	},
-	{ }
-};
-
-static int __init reboot_init(void)
-{
-	/* Only do the DMI check if reboot_type hasn't been overridden
-	 * on the command line
-	 */
-	if (reboot_default) {
-		dmi_check_system(reboot_dmi_table);
-	}
-	return 0;
-}
-core_initcall(reboot_init);
-
-extern const unsigned char machine_real_restart_asm[];
-extern const u64 machine_real_restart_gdt[3];
-
-void machine_real_restart(unsigned int type)
-{
-	void *restart_va;
-	unsigned long restart_pa;
-	void (*restart_lowmem)(unsigned int);
-	u64 *lowmem_gdt;
-
-	local_irq_disable();
-
-	/* Write zero to CMOS register number 0x0f, which the BIOS POST
-	   routine will recognize as telling it to do a proper reboot.  (Well
-	   that's what this book in front of me says -- it may only apply to
-	   the Phoenix BIOS though, it's not clear).  At the same time,
-	   disable NMIs by setting the top bit in the CMOS address register,
-	   as we're about to do peculiar things to the CPU.  I'm not sure if
-	   `outb_p' is needed instead of just `outb'.  Use it to be on the
-	   safe side.  (Yes, CMOS_WRITE does outb_p's. -  Paul G.)
-	 */
-	spin_lock(&rtc_lock);
-	CMOS_WRITE(0x00, 0x8f);
-	spin_unlock(&rtc_lock);
-
-	/*
-	 * Switch back to the initial page table.
-	 */
-	load_cr3(initial_page_table);
-
-	/* Write 0x1234 to absolute memory location 0x472.  The BIOS reads
-	   this on booting to tell it to "Bypass memory test (also warm
-	   boot)".  This seems like a fairly standard thing that gets set by
-	   REBOOT.COM programs, and the previous reset routine did this
-	   too. */
-	*((unsigned short *)0x472) = reboot_mode;
-
-	/* Patch the GDT in the low memory trampoline */
-	lowmem_gdt = TRAMPOLINE_SYM(machine_real_restart_gdt);
-
-	restart_va = TRAMPOLINE_SYM(machine_real_restart_asm);
-	restart_pa = virt_to_phys(restart_va);
-	restart_lowmem = (void (*)(unsigned int))restart_pa;
-
-	/* GDT[0]: GDT self-pointer */
-	lowmem_gdt[0] =
-		(u64)(sizeof(machine_real_restart_gdt) - 1) +
-		((u64)virt_to_phys(lowmem_gdt) << 16);
-	/* GDT[1]: 64K real mode code segment */
-	lowmem_gdt[1] =
-		GDT_ENTRY(0x009b, restart_pa, 0xffff);
-
-	/* Jump to the identity-mapped low memory code */
-	restart_lowmem(type);
-}
-#ifdef CONFIG_APM_MODULE
-EXPORT_SYMBOL(machine_real_restart);
-#endif
-
-#endif /* CONFIG_X86_32 */
-
-/*
- * Some Apple MacBook and MacBookPro's needs reboot=p to be able to reboot
- */
-static int __init set_pci_reboot(const struct dmi_system_id *d)
-{
-	if (reboot_type != BOOT_CF9) {
-		reboot_type = BOOT_CF9;
-		printk(KERN_INFO "%s series board detected. "
-		       "Selecting PCI-method for reboots.\n", d->ident);
-	}
-	return 0;
-}
-
-static struct dmi_system_id __initdata pci_reboot_dmi_table[] = {
 	{	/* Handle problems with rebooting on Apple MacBook5 */
 		.callback = set_pci_reboot,
 		.ident = "Apple MacBook5",
@@ -474,17 +473,17 @@
 	{ }
 };
 
-static int __init pci_reboot_init(void)
+static int __init reboot_init(void)
 {
-	/* Only do the DMI check if reboot_type hasn't been overridden
+	/*
+	 * Only do the DMI check if reboot_type hasn't been overridden
 	 * on the command line
 	 */
-	if (reboot_default) {
-		dmi_check_system(pci_reboot_dmi_table);
-	}
+	if (reboot_default)
+		dmi_check_system(reboot_dmi_table);
 	return 0;
 }
-core_initcall(pci_reboot_init);
+core_initcall(reboot_init);
 
 static inline void kb_wait(void)
 {
@@ -502,14 +501,14 @@
 	cpu_emergency_vmxoff();
 }
 
-/* Use NMIs as IPIs to tell all CPUs to disable virtualization
- */
+/* Use NMIs as IPIs to tell all CPUs to disable virtualization */
 static void emergency_vmx_disable_all(void)
 {
 	/* Just make sure we won't change CPUs while doing this */
 	local_irq_disable();
 
-	/* We need to disable VMX on all CPUs before rebooting, otherwise
+	/*
+	 * We need to disable VMX on all CPUs before rebooting, otherwise
 	 * we risk hanging up the machine, because the CPU ignore INIT
 	 * signals when VMX is enabled.
 	 *
@@ -528,8 +527,7 @@
 	 * is still enabling VMX.
 	 */
 	if (cpu_has_vmx() && cpu_vmx_enabled()) {
-		/* Disable VMX on this CPU.
-		 */
+		/* Disable VMX on this CPU. */
 		cpu_vmxoff();
 
 		/* Halt and disable VMX on the other CPUs */
@@ -574,12 +572,12 @@
 		/* Could also try the reset bit in the Hammer NB */
 		switch (reboot_type) {
 		case BOOT_KBD:
-			mach_reboot_fixups(); /* for board specific fixups */
+			mach_reboot_fixups(); /* For board specific fixups */
 
 			for (i = 0; i < 10; i++) {
 				kb_wait();
 				udelay(50);
-				outb(0xfe, 0x64); /* pulse reset low */
+				outb(0xfe, 0x64); /* Pulse reset low */
 				udelay(50);
 			}
 			if (attempt == 0 && orig_reboot_type == BOOT_ACPI) {
@@ -621,7 +619,7 @@
 
 		case BOOT_CF9:
 			port_cf9_safe = true;
-			/* fall through */
+			/* Fall through */
 
 		case BOOT_CF9_COND:
 			if (port_cf9_safe) {
@@ -659,7 +657,8 @@
 	/* Make certain I only run on the appropriate processor */
 	set_cpus_allowed_ptr(current, cpumask_of(reboot_cpu_id));
 
-	/* O.K Now that I'm on the appropriate processor,
+	/*
+	 * O.K Now that I'm on the appropriate processor,
 	 * stop all of the others.
 	 */
 	stop_other_cpus();
@@ -697,12 +696,11 @@
 
 static void native_machine_halt(void)
 {
-	/* stop other cpus and apics */
+	/* Stop other cpus and apics */
 	machine_shutdown();
 
 	tboot_shutdown(TB_SHUTDOWN_HALT);
 
-	/* stop this cpu */
 	stop_this_cpu(NULL);
 }
 
@@ -713,7 +711,7 @@
 			machine_shutdown();
 		pm_power_off();
 	}
-	/* a fallback in case there is no PM info available */
+	/* A fallback in case there is no PM info available */
 	tboot_shutdown(TB_SHUTDOWN_HALT);
 }
 
@@ -775,7 +773,8 @@
 
 	cpu = raw_smp_processor_id();
 
-	/* Don't do anything if this handler is invoked on crashing cpu.
+	/*
+	 * Don't do anything if this handler is invoked on crashing cpu.
 	 * Otherwise, system will completely hang. Crashing cpu can get
 	 * an NMI if system was initially booted with nmi_watchdog parameter.
 	 */
@@ -799,7 +798,8 @@
 	apic->send_IPI_allbutself(NMI_VECTOR);
 }
 
-/* Halt all other CPUs, calling the specified function on each of them
+/*
+ * Halt all other CPUs, calling the specified function on each of them
  *
  * This function can be used to halt all other CPUs on crash
  * or emergency reboot time. The function passed as parameter
@@ -810,7 +810,7 @@
 	unsigned long msecs;
 	local_irq_disable();
 
-	/* Make a note of crashing cpu. Will be used in NMI callback.*/
+	/* Make a note of crashing cpu. Will be used in NMI callback. */
 	crashing_cpu = safe_smp_processor_id();
 
 	shootdown_callback = callback;
@@ -819,8 +819,9 @@
 	/* Would it be better to replace the trap vector here? */
 	if (register_nmi_handler(NMI_LOCAL, crash_nmi_callback,
 				 NMI_FLAG_FIRST, "crash"))
-		return;		/* return what? */
-	/* Ensure the new callback function is set before sending
+		return;		/* Return what? */
+	/*
+	 * Ensure the new callback function is set before sending
 	 * out the NMI
 	 */
 	wmb();
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
index 7e67c5a..366c688 100644
--- a/arch/x86/kernel/setup.c
+++ b/arch/x86/kernel/setup.c
@@ -34,7 +34,6 @@
 #include <linux/memblock.h>
 #include <linux/seq_file.h>
 #include <linux/console.h>
-#include <linux/mca.h>
 #include <linux/root_dev.h>
 #include <linux/highmem.h>
 #include <linux/module.h>
@@ -179,12 +178,6 @@
 /* common cpu data for all cpus */
 struct cpuinfo_x86 boot_cpu_data __read_mostly = {0, 0, 0, 0, -1, 1, 0, 0, -1};
 EXPORT_SYMBOL(boot_cpu_data);
-static void set_mca_bus(int x)
-{
-#ifdef CONFIG_MCA
-	MCA_bus = x;
-#endif
-}
 
 unsigned int def_to_bigsmp;
 
@@ -393,10 +386,9 @@
 	initrd_start = 0;
 
 	if (ramdisk_size >= (end_of_lowmem>>1)) {
-		memblock_free(ramdisk_image, ramdisk_end - ramdisk_image);
-		printk(KERN_ERR "initrd too large to handle, "
-		       "disabling initrd\n");
-		return;
+		panic("initrd too large to handle, "
+		       "disabling initrd (%lld needed, %lld available)\n",
+		       ramdisk_size, end_of_lowmem>>1);
 	}
 
 	printk(KERN_INFO "RAMDISK: %08llx - %08llx\n", ramdisk_image,
@@ -717,7 +709,6 @@
 	apm_info.bios = boot_params.apm_bios_info;
 	ist_info = boot_params.ist_info;
 	if (boot_params.sys_desc_table.length != 0) {
-		set_mca_bus(boot_params.sys_desc_table.table[3] & 0x2);
 		machine_id = boot_params.sys_desc_table.table[0];
 		machine_submodel_id = boot_params.sys_desc_table.table[1];
 		BIOS_revision = boot_params.sys_desc_table.table[2];
diff --git a/arch/x86/kernel/smp.c b/arch/x86/kernel/smp.c
index 66c74f4..48d2b7d 100644
--- a/arch/x86/kernel/smp.c
+++ b/arch/x86/kernel/smp.c
@@ -109,6 +109,9 @@
  *	about nothing of note with C stepping upwards.
  */
 
+static atomic_t stopping_cpu = ATOMIC_INIT(-1);
+static bool smp_no_nmi_ipi = false;
+
 /*
  * this function sends a 'reschedule' IPI to another CPU.
  * it goes straight through and wastes no time serializing
@@ -149,8 +152,6 @@
 	free_cpumask_var(allbutself);
 }
 
-static atomic_t stopping_cpu = ATOMIC_INIT(-1);
-
 static int smp_stop_nmi_callback(unsigned int val, struct pt_regs *regs)
 {
 	/* We are registered on stopping cpu too, avoid spurious NMI */
@@ -162,47 +163,6 @@
 	return NMI_HANDLED;
 }
 
-static void native_nmi_stop_other_cpus(int wait)
-{
-	unsigned long flags;
-	unsigned long timeout;
-
-	if (reboot_force)
-		return;
-
-	/*
-	 * Use an own vector here because smp_call_function
-	 * does lots of things not suitable in a panic situation.
-	 */
-	if (num_online_cpus() > 1) {
-		/* did someone beat us here? */
-		if (atomic_cmpxchg(&stopping_cpu, -1, safe_smp_processor_id()) != -1)
-			return;
-
-		if (register_nmi_handler(NMI_LOCAL, smp_stop_nmi_callback,
-					 NMI_FLAG_FIRST, "smp_stop"))
-			/* Note: we ignore failures here */
-			return;
-
-		/* sync above data before sending NMI */
-		wmb();
-
-		apic->send_IPI_allbutself(NMI_VECTOR);
-
-		/*
-		 * Don't wait longer than a second if the caller
-		 * didn't ask us to wait.
-		 */
-		timeout = USEC_PER_SEC;
-		while (num_online_cpus() > 1 && (wait || timeout--))
-			udelay(1);
-	}
-
-	local_irq_save(flags);
-	disable_local_APIC();
-	local_irq_restore(flags);
-}
-
 /*
  * this function calls the 'stop' function on all other CPUs in the system.
  */
@@ -215,7 +175,7 @@
 	irq_exit();
 }
 
-static void native_irq_stop_other_cpus(int wait)
+static void native_stop_other_cpus(int wait)
 {
 	unsigned long flags;
 	unsigned long timeout;
@@ -226,13 +186,25 @@
 	/*
 	 * Use an own vector here because smp_call_function
 	 * does lots of things not suitable in a panic situation.
-	 * On most systems we could also use an NMI here,
-	 * but there are a few systems around where NMI
-	 * is problematic so stay with an non NMI for now
-	 * (this implies we cannot stop CPUs spinning with irq off
-	 * currently)
+	 */
+
+	/*
+	 * We start by using the REBOOT_VECTOR irq.
+	 * The irq is treated as a sync point to allow critical
+	 * regions of code on other cpus to release their spin locks
+	 * and re-enable irqs.  Jumping straight to an NMI might
+	 * accidentally cause deadlocks with further shutdown/panic
+	 * code.  By syncing, we give the cpus up to one second to
+	 * finish their work before we force them off with the NMI.
 	 */
 	if (num_online_cpus() > 1) {
+		/* did someone beat us here? */
+		if (atomic_cmpxchg(&stopping_cpu, -1, safe_smp_processor_id()) != -1)
+			return;
+
+		/* sync above data before sending IRQ */
+		wmb();
+
 		apic->send_IPI_allbutself(REBOOT_VECTOR);
 
 		/*
@@ -243,17 +215,37 @@
 		while (num_online_cpus() > 1 && (wait || timeout--))
 			udelay(1);
 	}
+	
+	/* if the REBOOT_VECTOR didn't work, try with the NMI */
+	if ((num_online_cpus() > 1) && (!smp_no_nmi_ipi))  {
+		if (register_nmi_handler(NMI_LOCAL, smp_stop_nmi_callback,
+					 NMI_FLAG_FIRST, "smp_stop"))
+			/* Note: we ignore failures here */
+			/* Hope the REBOOT_IRQ is good enough */
+			goto finish;
 
+		/* sync above data before sending IRQ */
+		wmb();
+
+		pr_emerg("Shutting down cpus with NMI\n");
+
+		apic->send_IPI_allbutself(NMI_VECTOR);
+
+		/*
+		 * Don't wait longer than a 10 ms if the caller
+		 * didn't ask us to wait.
+		 */
+		timeout = USEC_PER_MSEC * 10;
+		while (num_online_cpus() > 1 && (wait || timeout--))
+			udelay(1);
+	}
+
+finish:
 	local_irq_save(flags);
 	disable_local_APIC();
 	local_irq_restore(flags);
 }
 
-static void native_smp_disable_nmi_ipi(void)
-{
-	smp_ops.stop_other_cpus = native_irq_stop_other_cpus;
-}
-
 /*
  * Reschedule call back.
  */
@@ -287,8 +279,8 @@
 
 static int __init nonmi_ipi_setup(char *str)
 {
-        native_smp_disable_nmi_ipi();
-        return 1;
+	smp_no_nmi_ipi = true;
+	return 1;
 }
 
 __setup("nonmi_ipi", nonmi_ipi_setup);
@@ -298,7 +290,7 @@
 	.smp_prepare_cpus	= native_smp_prepare_cpus,
 	.smp_cpus_done		= native_smp_cpus_done,
 
-	.stop_other_cpus	= native_nmi_stop_other_cpus,
+	.stop_other_cpus	= native_stop_other_cpus,
 	.smp_send_reschedule	= native_smp_send_reschedule,
 
 	.cpu_up			= native_cpu_up,
diff --git a/arch/x86/kernel/test_rodata.c b/arch/x86/kernel/test_rodata.c
index c29e235..b79133a 100644
--- a/arch/x86/kernel/test_rodata.c
+++ b/arch/x86/kernel/test_rodata.c
@@ -12,6 +12,7 @@
 #include <linux/module.h>
 #include <asm/cacheflush.h>
 #include <asm/sections.h>
+#include <asm/asm.h>
 
 int rodata_test(void)
 {
@@ -42,14 +43,7 @@
 		".section .fixup,\"ax\"\n"
 		"2:	jmp 1b\n"
 		".previous\n"
-		".section __ex_table,\"a\"\n"
-		"       .align 16\n"
-#ifdef CONFIG_X86_32
-		"	.long 0b,2b\n"
-#else
-		"	.quad 0b,2b\n"
-#endif
-		".previous"
+		_ASM_EXTABLE(0b,2b)
 		: [rslt] "=r" (result)
 		: [rodata_test] "r" (&rodata_test_data), [zero] "r" (0UL)
 	);
diff --git a/arch/x86/kernel/time.c b/arch/x86/kernel/time.c
index c6eba2b..24d3c91 100644
--- a/arch/x86/kernel/time.c
+++ b/arch/x86/kernel/time.c
@@ -14,7 +14,6 @@
 #include <linux/i8253.h>
 #include <linux/time.h>
 #include <linux/export.h>
-#include <linux/mca.h>
 
 #include <asm/vsyscall.h>
 #include <asm/x86_init.h>
@@ -58,11 +57,6 @@
 static irqreturn_t timer_interrupt(int irq, void *dev_id)
 {
 	global_clock_event->event_handler(global_clock_event);
-
-	/* MCA bus quirk: Acknowledge irq0 by setting bit 7 in port 0x61 */
-	if (MCA_bus)
-		outb_p(inb_p(0x61)| 0x80, 0x61);
-
 	return IRQ_HANDLED;
 }
 
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
index 92d5756..ff08457 100644
--- a/arch/x86/kernel/traps.c
+++ b/arch/x86/kernel/traps.c
@@ -37,10 +37,6 @@
 #include <linux/eisa.h>
 #endif
 
-#ifdef CONFIG_MCA
-#include <linux/mca.h>
-#endif
-
 #if defined(CONFIG_EDAC)
 #include <linux/edac.h>
 #endif
diff --git a/arch/x86/kernel/vsmp_64.c b/arch/x86/kernel/vsmp_64.c
index a1d804b..8eeb55a 100644
--- a/arch/x86/kernel/vsmp_64.c
+++ b/arch/x86/kernel/vsmp_64.c
@@ -15,6 +15,7 @@
 #include <linux/init.h>
 #include <linux/pci_ids.h>
 #include <linux/pci_regs.h>
+#include <linux/smp.h>
 
 #include <asm/apic.h>
 #include <asm/pci-direct.h>
@@ -22,6 +23,8 @@
 #include <asm/paravirt.h>
 #include <asm/setup.h>
 
+#define TOPOLOGY_REGISTER_OFFSET 0x10
+
 #if defined CONFIG_PCI && defined CONFIG_PARAVIRT
 /*
  * Interrupt control on vSMPowered systems:
@@ -149,12 +152,49 @@
 	return 0;
 }
 #endif
+
+static void __init vsmp_cap_cpus(void)
+{
+#if !defined(CONFIG_X86_VSMP) && defined(CONFIG_SMP)
+	void __iomem *address;
+	unsigned int cfg, topology, node_shift, maxcpus;
+
+	/*
+	 * CONFIG_X86_VSMP is not configured, so limit the number CPUs to the
+	 * ones present in the first board, unless explicitly overridden by
+	 * setup_max_cpus
+	 */
+	if (setup_max_cpus != NR_CPUS)
+		return;
+
+	/* Read the vSMP Foundation topology register */
+	cfg = read_pci_config(0, 0x1f, 0, PCI_BASE_ADDRESS_0);
+	address = early_ioremap(cfg + TOPOLOGY_REGISTER_OFFSET, 4);
+	if (WARN_ON(!address))
+		return;
+
+	topology = readl(address);
+	node_shift = (topology >> 16) & 0x7;
+	if (!node_shift)
+		/* The value 0 should be decoded as 8 */
+		node_shift = 8;
+	maxcpus = (topology & ((1 << node_shift) - 1)) + 1;
+
+	pr_info("vSMP CTL: Capping CPUs to %d (CONFIG_X86_VSMP is unset)\n",
+		maxcpus);
+	setup_max_cpus = maxcpus;
+	early_iounmap(address, 4);
+#endif
+}
+
 void __init vsmp_init(void)
 {
 	detect_vsmp_box();
 	if (!is_vsmp_box())
 		return;
 
+	vsmp_cap_cpus();
+
 	set_vsmp_pv_ops();
 	return;
 }
diff --git a/arch/x86/kernel/xsave.c b/arch/x86/kernel/xsave.c
index e62728e..bd18149 100644
--- a/arch/x86/kernel/xsave.c
+++ b/arch/x86/kernel/xsave.c
@@ -48,8 +48,6 @@
 	if (!fx)
 		return;
 
-	BUG_ON(__thread_has_fpu(tsk));
-
 	xstate_bv = tsk->thread.fpu.state->xsave.xsave_hdr.xstate_bv;
 
 	/*
diff --git a/arch/x86/lib/checksum_32.S b/arch/x86/lib/checksum_32.S
index 78d16a5..2af5df3 100644
--- a/arch/x86/lib/checksum_32.S
+++ b/arch/x86/lib/checksum_32.S
@@ -28,6 +28,7 @@
 #include <linux/linkage.h>
 #include <asm/dwarf2.h>
 #include <asm/errno.h>
+#include <asm/asm.h>
 				
 /*
  * computes a partial checksum, e.g. for TCP/UDP fragments
@@ -282,15 +283,11 @@
 
 #define SRC(y...)			\
 	9999: y;			\
-	.section __ex_table, "a";	\
-	.long 9999b, 6001f	;	\
-	.previous
+	_ASM_EXTABLE(9999b, 6001f)
 
 #define DST(y...)			\
 	9999: y;			\
-	.section __ex_table, "a";	\
-	.long 9999b, 6002f	;	\
-	.previous
+	_ASM_EXTABLE(9999b, 6002f)
 
 #ifndef CONFIG_X86_USE_PPRO_CHECKSUM
 
diff --git a/arch/x86/lib/copy_user_64.S b/arch/x86/lib/copy_user_64.S
index 0248402..5b2995f 100644
--- a/arch/x86/lib/copy_user_64.S
+++ b/arch/x86/lib/copy_user_64.S
@@ -16,6 +16,7 @@
 #include <asm/thread_info.h>
 #include <asm/cpufeature.h>
 #include <asm/alternative-asm.h>
+#include <asm/asm.h>
 
 /*
  * By placing feature2 after feature1 in altinstructions section, we logically
@@ -63,11 +64,8 @@
 	jmp copy_user_handle_tail
 	.previous
 
-	.section __ex_table,"a"
-	.align 8
-	.quad 100b,103b
-	.quad 101b,103b
-	.previous
+	_ASM_EXTABLE(100b,103b)
+	_ASM_EXTABLE(101b,103b)
 #endif
 	.endm
 
@@ -191,29 +189,26 @@
 60:	jmp copy_user_handle_tail /* ecx is zerorest also */
 	.previous
 
-	.section __ex_table,"a"
-	.align 8
-	.quad 1b,30b
-	.quad 2b,30b
-	.quad 3b,30b
-	.quad 4b,30b
-	.quad 5b,30b
-	.quad 6b,30b
-	.quad 7b,30b
-	.quad 8b,30b
-	.quad 9b,30b
-	.quad 10b,30b
-	.quad 11b,30b
-	.quad 12b,30b
-	.quad 13b,30b
-	.quad 14b,30b
-	.quad 15b,30b
-	.quad 16b,30b
-	.quad 18b,40b
-	.quad 19b,40b
-	.quad 21b,50b
-	.quad 22b,50b
-	.previous
+	_ASM_EXTABLE(1b,30b)
+	_ASM_EXTABLE(2b,30b)
+	_ASM_EXTABLE(3b,30b)
+	_ASM_EXTABLE(4b,30b)
+	_ASM_EXTABLE(5b,30b)
+	_ASM_EXTABLE(6b,30b)
+	_ASM_EXTABLE(7b,30b)
+	_ASM_EXTABLE(8b,30b)
+	_ASM_EXTABLE(9b,30b)
+	_ASM_EXTABLE(10b,30b)
+	_ASM_EXTABLE(11b,30b)
+	_ASM_EXTABLE(12b,30b)
+	_ASM_EXTABLE(13b,30b)
+	_ASM_EXTABLE(14b,30b)
+	_ASM_EXTABLE(15b,30b)
+	_ASM_EXTABLE(16b,30b)
+	_ASM_EXTABLE(18b,40b)
+	_ASM_EXTABLE(19b,40b)
+	_ASM_EXTABLE(21b,50b)
+	_ASM_EXTABLE(22b,50b)
 	CFI_ENDPROC
 ENDPROC(copy_user_generic_unrolled)
 
@@ -259,11 +254,8 @@
 	jmp copy_user_handle_tail
 	.previous
 
-	.section __ex_table,"a"
-	.align 8
-	.quad 1b,11b
-	.quad 3b,12b
-	.previous
+	_ASM_EXTABLE(1b,11b)
+	_ASM_EXTABLE(3b,12b)
 	CFI_ENDPROC
 ENDPROC(copy_user_generic_string)
 
@@ -294,9 +286,6 @@
 	jmp copy_user_handle_tail
 	.previous
 
-	.section __ex_table,"a"
-	.align 8
-	.quad 1b,12b
-	.previous
+	_ASM_EXTABLE(1b,12b)
 	CFI_ENDPROC
 ENDPROC(copy_user_enhanced_fast_string)
diff --git a/arch/x86/lib/copy_user_nocache_64.S b/arch/x86/lib/copy_user_nocache_64.S
index cb0c112..cacddc7 100644
--- a/arch/x86/lib/copy_user_nocache_64.S
+++ b/arch/x86/lib/copy_user_nocache_64.S
@@ -14,6 +14,7 @@
 #include <asm/current.h>
 #include <asm/asm-offsets.h>
 #include <asm/thread_info.h>
+#include <asm/asm.h>
 
 	.macro ALIGN_DESTINATION
 #ifdef FIX_ALIGNMENT
@@ -36,11 +37,8 @@
 	jmp copy_user_handle_tail
 	.previous
 
-	.section __ex_table,"a"
-	.align 8
-	.quad 100b,103b
-	.quad 101b,103b
-	.previous
+	_ASM_EXTABLE(100b,103b)
+	_ASM_EXTABLE(101b,103b)
 #endif
 	.endm
 
@@ -111,27 +109,25 @@
 	jmp copy_user_handle_tail
 	.previous
 
-	.section __ex_table,"a"
-	.quad 1b,30b
-	.quad 2b,30b
-	.quad 3b,30b
-	.quad 4b,30b
-	.quad 5b,30b
-	.quad 6b,30b
-	.quad 7b,30b
-	.quad 8b,30b
-	.quad 9b,30b
-	.quad 10b,30b
-	.quad 11b,30b
-	.quad 12b,30b
-	.quad 13b,30b
-	.quad 14b,30b
-	.quad 15b,30b
-	.quad 16b,30b
-	.quad 18b,40b
-	.quad 19b,40b
-	.quad 21b,50b
-	.quad 22b,50b
-	.previous
+	_ASM_EXTABLE(1b,30b)
+	_ASM_EXTABLE(2b,30b)
+	_ASM_EXTABLE(3b,30b)
+	_ASM_EXTABLE(4b,30b)
+	_ASM_EXTABLE(5b,30b)
+	_ASM_EXTABLE(6b,30b)
+	_ASM_EXTABLE(7b,30b)
+	_ASM_EXTABLE(8b,30b)
+	_ASM_EXTABLE(9b,30b)
+	_ASM_EXTABLE(10b,30b)
+	_ASM_EXTABLE(11b,30b)
+	_ASM_EXTABLE(12b,30b)
+	_ASM_EXTABLE(13b,30b)
+	_ASM_EXTABLE(14b,30b)
+	_ASM_EXTABLE(15b,30b)
+	_ASM_EXTABLE(16b,30b)
+	_ASM_EXTABLE(18b,40b)
+	_ASM_EXTABLE(19b,40b)
+	_ASM_EXTABLE(21b,50b)
+	_ASM_EXTABLE(22b,50b)
 	CFI_ENDPROC
 ENDPROC(__copy_user_nocache)
diff --git a/arch/x86/lib/csum-copy_64.S b/arch/x86/lib/csum-copy_64.S
index fb903b7..2419d5f 100644
--- a/arch/x86/lib/csum-copy_64.S
+++ b/arch/x86/lib/csum-copy_64.S
@@ -8,6 +8,7 @@
 #include <linux/linkage.h>
 #include <asm/dwarf2.h>
 #include <asm/errno.h>
+#include <asm/asm.h>
 
 /*
  * Checksum copy with exception handling.
@@ -31,26 +32,17 @@
 
 	.macro source
 10:
-	.section __ex_table, "a"
-	.align 8
-	.quad 10b, .Lbad_source
-	.previous
+	_ASM_EXTABLE(10b, .Lbad_source)
 	.endm
 
 	.macro dest
 20:
-	.section __ex_table, "a"
-	.align 8
-	.quad 20b, .Lbad_dest
-	.previous
+	_ASM_EXTABLE(20b, .Lbad_dest)
 	.endm
 
 	.macro ignore L=.Lignore
 30:
-	.section __ex_table, "a"
-	.align 8
-	.quad 30b, \L
-	.previous
+	_ASM_EXTABLE(30b, \L)
 	.endm
 
 
diff --git a/arch/x86/lib/getuser.S b/arch/x86/lib/getuser.S
index 51f1504..b33b1fb 100644
--- a/arch/x86/lib/getuser.S
+++ b/arch/x86/lib/getuser.S
@@ -95,10 +95,9 @@
 	CFI_ENDPROC
 END(bad_get_user)
 
-.section __ex_table,"a"
-	_ASM_PTR 1b,bad_get_user
-	_ASM_PTR 2b,bad_get_user
-	_ASM_PTR 3b,bad_get_user
+	_ASM_EXTABLE(1b,bad_get_user)
+	_ASM_EXTABLE(2b,bad_get_user)
+	_ASM_EXTABLE(3b,bad_get_user)
 #ifdef CONFIG_X86_64
-	_ASM_PTR 4b,bad_get_user
+	_ASM_EXTABLE(4b,bad_get_user)
 #endif
diff --git a/arch/x86/lib/putuser.S b/arch/x86/lib/putuser.S
index 36b0d15..7f951c8 100644
--- a/arch/x86/lib/putuser.S
+++ b/arch/x86/lib/putuser.S
@@ -86,12 +86,10 @@
 	EXIT
 END(bad_put_user)
 
-.section __ex_table,"a"
-	_ASM_PTR 1b,bad_put_user
-	_ASM_PTR 2b,bad_put_user
-	_ASM_PTR 3b,bad_put_user
-	_ASM_PTR 4b,bad_put_user
+	_ASM_EXTABLE(1b,bad_put_user)
+	_ASM_EXTABLE(2b,bad_put_user)
+	_ASM_EXTABLE(3b,bad_put_user)
+	_ASM_EXTABLE(4b,bad_put_user)
 #ifdef CONFIG_X86_32
-	_ASM_PTR 5b,bad_put_user
+	_ASM_EXTABLE(5b,bad_put_user)
 #endif
-.previous
diff --git a/arch/x86/lib/usercopy_32.c b/arch/x86/lib/usercopy_32.c
index ef2a6a5..883b216 100644
--- a/arch/x86/lib/usercopy_32.c
+++ b/arch/x86/lib/usercopy_32.c
@@ -13,6 +13,7 @@
 #include <linux/interrupt.h>
 #include <asm/uaccess.h>
 #include <asm/mmx.h>
+#include <asm/asm.h>
 
 #ifdef CONFIG_X86_INTEL_USERCOPY
 /*
@@ -127,10 +128,7 @@
 		"3:	movb $1,%%al\n"
 		"	jmp 1b\n"
 		".previous\n"
-		".section __ex_table,\"a\"\n"
-		"	.align 4\n"
-		"	.long 0b,2b\n"
-		".previous"
+		_ASM_EXTABLE(0b,2b)
 		:"=&r" (n), "=&D" (s), "=&a" (res), "=&c" (tmp)
 		:"0" (n), "1" (s), "2" (0), "3" (mask)
 		:"cc");
@@ -199,47 +197,44 @@
 		       "101:   lea 0(%%eax,%0,4),%0\n"
 		       "       jmp 100b\n"
 		       ".previous\n"
-		       ".section __ex_table,\"a\"\n"
-		       "       .align 4\n"
-		       "       .long 1b,100b\n"
-		       "       .long 2b,100b\n"
-		       "       .long 3b,100b\n"
-		       "       .long 4b,100b\n"
-		       "       .long 5b,100b\n"
-		       "       .long 6b,100b\n"
-		       "       .long 7b,100b\n"
-		       "       .long 8b,100b\n"
-		       "       .long 9b,100b\n"
-		       "       .long 10b,100b\n"
-		       "       .long 11b,100b\n"
-		       "       .long 12b,100b\n"
-		       "       .long 13b,100b\n"
-		       "       .long 14b,100b\n"
-		       "       .long 15b,100b\n"
-		       "       .long 16b,100b\n"
-		       "       .long 17b,100b\n"
-		       "       .long 18b,100b\n"
-		       "       .long 19b,100b\n"
-		       "       .long 20b,100b\n"
-		       "       .long 21b,100b\n"
-		       "       .long 22b,100b\n"
-		       "       .long 23b,100b\n"
-		       "       .long 24b,100b\n"
-		       "       .long 25b,100b\n"
-		       "       .long 26b,100b\n"
-		       "       .long 27b,100b\n"
-		       "       .long 28b,100b\n"
-		       "       .long 29b,100b\n"
-		       "       .long 30b,100b\n"
-		       "       .long 31b,100b\n"
-		       "       .long 32b,100b\n"
-		       "       .long 33b,100b\n"
-		       "       .long 34b,100b\n"
-		       "       .long 35b,100b\n"
-		       "       .long 36b,100b\n"
-		       "       .long 37b,100b\n"
-		       "       .long 99b,101b\n"
-		       ".previous"
+		       _ASM_EXTABLE(1b,100b)
+		       _ASM_EXTABLE(2b,100b)
+		       _ASM_EXTABLE(3b,100b)
+		       _ASM_EXTABLE(4b,100b)
+		       _ASM_EXTABLE(5b,100b)
+		       _ASM_EXTABLE(6b,100b)
+		       _ASM_EXTABLE(7b,100b)
+		       _ASM_EXTABLE(8b,100b)
+		       _ASM_EXTABLE(9b,100b)
+		       _ASM_EXTABLE(10b,100b)
+		       _ASM_EXTABLE(11b,100b)
+		       _ASM_EXTABLE(12b,100b)
+		       _ASM_EXTABLE(13b,100b)
+		       _ASM_EXTABLE(14b,100b)
+		       _ASM_EXTABLE(15b,100b)
+		       _ASM_EXTABLE(16b,100b)
+		       _ASM_EXTABLE(17b,100b)
+		       _ASM_EXTABLE(18b,100b)
+		       _ASM_EXTABLE(19b,100b)
+		       _ASM_EXTABLE(20b,100b)
+		       _ASM_EXTABLE(21b,100b)
+		       _ASM_EXTABLE(22b,100b)
+		       _ASM_EXTABLE(23b,100b)
+		       _ASM_EXTABLE(24b,100b)
+		       _ASM_EXTABLE(25b,100b)
+		       _ASM_EXTABLE(26b,100b)
+		       _ASM_EXTABLE(27b,100b)
+		       _ASM_EXTABLE(28b,100b)
+		       _ASM_EXTABLE(29b,100b)
+		       _ASM_EXTABLE(30b,100b)
+		       _ASM_EXTABLE(31b,100b)
+		       _ASM_EXTABLE(32b,100b)
+		       _ASM_EXTABLE(33b,100b)
+		       _ASM_EXTABLE(34b,100b)
+		       _ASM_EXTABLE(35b,100b)
+		       _ASM_EXTABLE(36b,100b)
+		       _ASM_EXTABLE(37b,100b)
+		       _ASM_EXTABLE(99b,101b)
 		       : "=&c"(size), "=&D" (d0), "=&S" (d1)
 		       :  "1"(to), "2"(from), "0"(size)
 		       : "eax", "edx", "memory");
@@ -312,29 +307,26 @@
 		       "        popl %0\n"
 		       "        jmp 8b\n"
 		       ".previous\n"
-		       ".section __ex_table,\"a\"\n"
-		       "	.align 4\n"
-		       "	.long 0b,16b\n"
-		       "	.long 1b,16b\n"
-		       "	.long 2b,16b\n"
-		       "	.long 21b,16b\n"
-		       "	.long 3b,16b\n"
-		       "	.long 31b,16b\n"
-		       "	.long 4b,16b\n"
-		       "	.long 41b,16b\n"
-		       "	.long 10b,16b\n"
-		       "	.long 51b,16b\n"
-		       "	.long 11b,16b\n"
-		       "	.long 61b,16b\n"
-		       "	.long 12b,16b\n"
-		       "	.long 71b,16b\n"
-		       "	.long 13b,16b\n"
-		       "	.long 81b,16b\n"
-		       "	.long 14b,16b\n"
-		       "	.long 91b,16b\n"
-		       "	.long 6b,9b\n"
-		       "        .long 7b,16b\n"
-		       ".previous"
+		       _ASM_EXTABLE(0b,16b)
+		       _ASM_EXTABLE(1b,16b)
+		       _ASM_EXTABLE(2b,16b)
+		       _ASM_EXTABLE(21b,16b)
+		       _ASM_EXTABLE(3b,16b)
+		       _ASM_EXTABLE(31b,16b)
+		       _ASM_EXTABLE(4b,16b)
+		       _ASM_EXTABLE(41b,16b)
+		       _ASM_EXTABLE(10b,16b)
+		       _ASM_EXTABLE(51b,16b)
+		       _ASM_EXTABLE(11b,16b)
+		       _ASM_EXTABLE(61b,16b)
+		       _ASM_EXTABLE(12b,16b)
+		       _ASM_EXTABLE(71b,16b)
+		       _ASM_EXTABLE(13b,16b)
+		       _ASM_EXTABLE(81b,16b)
+		       _ASM_EXTABLE(14b,16b)
+		       _ASM_EXTABLE(91b,16b)
+		       _ASM_EXTABLE(6b,9b)
+		       _ASM_EXTABLE(7b,16b)
 		       : "=&c"(size), "=&D" (d0), "=&S" (d1)
 		       :  "1"(to), "2"(from), "0"(size)
 		       : "eax", "edx", "memory");
@@ -414,29 +406,26 @@
 	       "        popl %0\n"
 	       "        jmp 8b\n"
 	       ".previous\n"
-	       ".section __ex_table,\"a\"\n"
-	       "	.align 4\n"
-	       "	.long 0b,16b\n"
-	       "	.long 1b,16b\n"
-	       "	.long 2b,16b\n"
-	       "	.long 21b,16b\n"
-	       "	.long 3b,16b\n"
-	       "	.long 31b,16b\n"
-	       "	.long 4b,16b\n"
-	       "	.long 41b,16b\n"
-	       "	.long 10b,16b\n"
-	       "	.long 51b,16b\n"
-	       "	.long 11b,16b\n"
-	       "	.long 61b,16b\n"
-	       "	.long 12b,16b\n"
-	       "	.long 71b,16b\n"
-	       "	.long 13b,16b\n"
-	       "	.long 81b,16b\n"
-	       "	.long 14b,16b\n"
-	       "	.long 91b,16b\n"
-	       "	.long 6b,9b\n"
-	       "        .long 7b,16b\n"
-	       ".previous"
+	       _ASM_EXTABLE(0b,16b)
+	       _ASM_EXTABLE(1b,16b)
+	       _ASM_EXTABLE(2b,16b)
+	       _ASM_EXTABLE(21b,16b)
+	       _ASM_EXTABLE(3b,16b)
+	       _ASM_EXTABLE(31b,16b)
+	       _ASM_EXTABLE(4b,16b)
+	       _ASM_EXTABLE(41b,16b)
+	       _ASM_EXTABLE(10b,16b)
+	       _ASM_EXTABLE(51b,16b)
+	       _ASM_EXTABLE(11b,16b)
+	       _ASM_EXTABLE(61b,16b)
+	       _ASM_EXTABLE(12b,16b)
+	       _ASM_EXTABLE(71b,16b)
+	       _ASM_EXTABLE(13b,16b)
+	       _ASM_EXTABLE(81b,16b)
+	       _ASM_EXTABLE(14b,16b)
+	       _ASM_EXTABLE(91b,16b)
+	       _ASM_EXTABLE(6b,9b)
+	       _ASM_EXTABLE(7b,16b)
 	       : "=&c"(size), "=&D" (d0), "=&S" (d1)
 	       :  "1"(to), "2"(from), "0"(size)
 	       : "eax", "edx", "memory");
@@ -505,29 +494,26 @@
 	       "9:      lea 0(%%eax,%0,4),%0\n"
 	       "16:     jmp 8b\n"
 	       ".previous\n"
-	       ".section __ex_table,\"a\"\n"
-	       "	.align 4\n"
-	       "	.long 0b,16b\n"
-	       "	.long 1b,16b\n"
-	       "	.long 2b,16b\n"
-	       "	.long 21b,16b\n"
-	       "	.long 3b,16b\n"
-	       "	.long 31b,16b\n"
-	       "	.long 4b,16b\n"
-	       "	.long 41b,16b\n"
-	       "	.long 10b,16b\n"
-	       "	.long 51b,16b\n"
-	       "	.long 11b,16b\n"
-	       "	.long 61b,16b\n"
-	       "	.long 12b,16b\n"
-	       "	.long 71b,16b\n"
-	       "	.long 13b,16b\n"
-	       "	.long 81b,16b\n"
-	       "	.long 14b,16b\n"
-	       "	.long 91b,16b\n"
-	       "	.long 6b,9b\n"
-	       "        .long 7b,16b\n"
-	       ".previous"
+	       _ASM_EXTABLE(0b,16b)
+	       _ASM_EXTABLE(1b,16b)
+	       _ASM_EXTABLE(2b,16b)
+	       _ASM_EXTABLE(21b,16b)
+	       _ASM_EXTABLE(3b,16b)
+	       _ASM_EXTABLE(31b,16b)
+	       _ASM_EXTABLE(4b,16b)
+	       _ASM_EXTABLE(41b,16b)
+	       _ASM_EXTABLE(10b,16b)
+	       _ASM_EXTABLE(51b,16b)
+	       _ASM_EXTABLE(11b,16b)
+	       _ASM_EXTABLE(61b,16b)
+	       _ASM_EXTABLE(12b,16b)
+	       _ASM_EXTABLE(71b,16b)
+	       _ASM_EXTABLE(13b,16b)
+	       _ASM_EXTABLE(81b,16b)
+	       _ASM_EXTABLE(14b,16b)
+	       _ASM_EXTABLE(91b,16b)
+	       _ASM_EXTABLE(6b,9b)
+	       _ASM_EXTABLE(7b,16b)
 	       : "=&c"(size), "=&D" (d0), "=&S" (d1)
 	       :  "1"(to), "2"(from), "0"(size)
 	       : "eax", "edx", "memory");
@@ -574,12 +560,9 @@
 		"3:	lea 0(%3,%0,4),%0\n"				\
 		"	jmp 2b\n"					\
 		".previous\n"						\
-		".section __ex_table,\"a\"\n"				\
-		"	.align 4\n"					\
-		"	.long 4b,5b\n"					\
-		"	.long 0b,3b\n"					\
-		"	.long 1b,2b\n"					\
-		".previous"						\
+		_ASM_EXTABLE(4b,5b)					\
+		_ASM_EXTABLE(0b,3b)					\
+		_ASM_EXTABLE(1b,2b)					\
 		: "=&c"(size), "=&D" (__d0), "=&S" (__d1), "=r"(__d2)	\
 		: "3"(size), "0"(size), "1"(to), "2"(from)		\
 		: "memory");						\
@@ -616,12 +599,9 @@
 		"	popl %0\n"					\
 		"	jmp 2b\n"					\
 		".previous\n"						\
-		".section __ex_table,\"a\"\n"				\
-		"	.align 4\n"					\
-		"	.long 4b,5b\n"					\
-		"	.long 0b,3b\n"					\
-		"	.long 1b,6b\n"					\
-		".previous"						\
+		_ASM_EXTABLE(4b,5b)					\
+		_ASM_EXTABLE(0b,3b)					\
+		_ASM_EXTABLE(1b,6b)					\
 		: "=&c"(size), "=&D" (__d0), "=&S" (__d1), "=r"(__d2)	\
 		: "3"(size), "0"(size), "1"(to), "2"(from)		\
 		: "memory");						\
diff --git a/arch/x86/mm/extable.c b/arch/x86/mm/extable.c
index 1fb85db..903ec1e 100644
--- a/arch/x86/mm/extable.c
+++ b/arch/x86/mm/extable.c
@@ -1,11 +1,23 @@
 #include <linux/module.h>
 #include <linux/spinlock.h>
+#include <linux/sort.h>
 #include <asm/uaccess.h>
 
+static inline unsigned long
+ex_insn_addr(const struct exception_table_entry *x)
+{
+	return (unsigned long)&x->insn + x->insn;
+}
+static inline unsigned long
+ex_fixup_addr(const struct exception_table_entry *x)
+{
+	return (unsigned long)&x->fixup + x->fixup;
+}
 
 int fixup_exception(struct pt_regs *regs)
 {
 	const struct exception_table_entry *fixup;
+	unsigned long new_ip;
 
 #ifdef CONFIG_PNPBIOS
 	if (unlikely(SEGMENT_IS_PNP_CODE(regs->cs))) {
@@ -23,15 +35,135 @@
 
 	fixup = search_exception_tables(regs->ip);
 	if (fixup) {
-		/* If fixup is less than 16, it means uaccess error */
-		if (fixup->fixup < 16) {
+		new_ip = ex_fixup_addr(fixup);
+
+		if (fixup->fixup - fixup->insn >= 0x7ffffff0 - 4) {
+			/* Special hack for uaccess_err */
 			current_thread_info()->uaccess_err = 1;
-			regs->ip += fixup->fixup;
-			return 1;
+			new_ip -= 0x7ffffff0;
 		}
-		regs->ip = fixup->fixup;
+		regs->ip = new_ip;
 		return 1;
 	}
 
 	return 0;
 }
+
+/* Restricted version used during very early boot */
+int __init early_fixup_exception(unsigned long *ip)
+{
+	const struct exception_table_entry *fixup;
+	unsigned long new_ip;
+
+	fixup = search_exception_tables(*ip);
+	if (fixup) {
+		new_ip = ex_fixup_addr(fixup);
+
+		if (fixup->fixup - fixup->insn >= 0x7ffffff0 - 4) {
+			/* uaccess handling not supported during early boot */
+			return 0;
+		}
+
+		*ip = new_ip;
+		return 1;
+	}
+
+	return 0;
+}
+
+/*
+ * Search one exception table for an entry corresponding to the
+ * given instruction address, and return the address of the entry,
+ * or NULL if none is found.
+ * We use a binary search, and thus we assume that the table is
+ * already sorted.
+ */
+const struct exception_table_entry *
+search_extable(const struct exception_table_entry *first,
+	       const struct exception_table_entry *last,
+	       unsigned long value)
+{
+	while (first <= last) {
+		const struct exception_table_entry *mid;
+		unsigned long addr;
+
+		mid = ((last - first) >> 1) + first;
+		addr = ex_insn_addr(mid);
+		if (addr < value)
+			first = mid + 1;
+		else if (addr > value)
+			last = mid - 1;
+		else
+			return mid;
+        }
+        return NULL;
+}
+
+/*
+ * The exception table needs to be sorted so that the binary
+ * search that we use to find entries in it works properly.
+ * This is used both for the kernel exception table and for
+ * the exception tables of modules that get loaded.
+ *
+ */
+static int cmp_ex(const void *a, const void *b)
+{
+	const struct exception_table_entry *x = a, *y = b;
+
+	/*
+	 * This value will always end up fittin in an int, because on
+	 * both i386 and x86-64 the kernel symbol-reachable address
+	 * space is < 2 GiB.
+	 *
+	 * This compare is only valid after normalization.
+	 */
+	return x->insn - y->insn;
+}
+
+void sort_extable(struct exception_table_entry *start,
+		  struct exception_table_entry *finish)
+{
+	struct exception_table_entry *p;
+	int i;
+
+	/* Convert all entries to being relative to the start of the section */
+	i = 0;
+	for (p = start; p < finish; p++) {
+		p->insn += i;
+		i += 4;
+		p->fixup += i;
+		i += 4;
+	}
+
+	sort(start, finish - start, sizeof(struct exception_table_entry),
+	     cmp_ex, NULL);
+
+	/* Denormalize all entries */
+	i = 0;
+	for (p = start; p < finish; p++) {
+		p->insn -= i;
+		i += 4;
+		p->fixup -= i;
+		i += 4;
+	}
+}
+
+#ifdef CONFIG_MODULES
+/*
+ * If the exception table is sorted, any referring to the module init
+ * will be at the beginning or the end.
+ */
+void trim_init_extable(struct module *m)
+{
+	/*trim the beginning*/
+	while (m->num_exentries &&
+	       within_module_init(ex_insn_addr(&m->extable[0]), m)) {
+		m->extable++;
+		m->num_exentries--;
+	}
+	/*trim the end*/
+	while (m->num_exentries &&
+	       within_module_init(ex_insn_addr(&m->extable[m->num_exentries-1]), m))
+		m->num_exentries--;
+}
+#endif /* CONFIG_MODULES */
diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c
index 4f0cec7..319b6f2 100644
--- a/arch/x86/mm/init.c
+++ b/arch/x86/mm/init.c
@@ -29,8 +29,14 @@
 #endif
 ;
 
-static void __init find_early_table_space(unsigned long end, int use_pse,
-					  int use_gbpages)
+struct map_range {
+	unsigned long start;
+	unsigned long end;
+	unsigned page_size_mask;
+};
+
+static void __init find_early_table_space(struct map_range *mr, unsigned long end,
+					  int use_pse, int use_gbpages)
 {
 	unsigned long puds, pmds, ptes, tables, start = 0, good_end = end;
 	phys_addr_t base;
@@ -55,6 +61,9 @@
 #ifdef CONFIG_X86_32
 		extra += PMD_SIZE;
 #endif
+		/* The first 2/4M doesn't use large pages. */
+		extra += mr->end - mr->start;
+
 		ptes = (extra + PAGE_SIZE - 1) >> PAGE_SHIFT;
 	} else
 		ptes = (end + PAGE_SIZE - 1) >> PAGE_SHIFT;
@@ -84,12 +93,6 @@
 	memblock_reserve(start, end - start);
 }
 
-struct map_range {
-	unsigned long start;
-	unsigned long end;
-	unsigned page_size_mask;
-};
-
 #ifdef CONFIG_X86_32
 #define NR_RANGE_MR 3
 #else /* CONFIG_X86_64 */
@@ -261,7 +264,7 @@
 	 * nodes are discovered.
 	 */
 	if (!after_bootmem)
-		find_early_table_space(end, use_pse, use_gbpages);
+		find_early_table_space(&mr[0], end, use_pse, use_gbpages);
 
 	for (i = 0; i < nr_range; i++)
 		ret = kernel_physical_mapping_init(mr[i].start, mr[i].end,
diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c
index fc18be0..2b6b4a3 100644
--- a/arch/x86/mm/init_64.c
+++ b/arch/x86/mm/init_64.c
@@ -407,12 +407,12 @@
 phys_pmd_init(pmd_t *pmd_page, unsigned long address, unsigned long end,
 	      unsigned long page_size_mask, pgprot_t prot)
 {
-	unsigned long pages = 0;
+	unsigned long pages = 0, next;
 	unsigned long last_map_addr = end;
 
 	int i = pmd_index(address);
 
-	for (; i < PTRS_PER_PMD; i++, address += PMD_SIZE) {
+	for (; i < PTRS_PER_PMD; i++, address = next) {
 		unsigned long pte_phys;
 		pmd_t *pmd = pmd_page + pmd_index(address);
 		pte_t *pte;
@@ -426,6 +426,8 @@
 			break;
 		}
 
+		next = (address & PMD_MASK) + PMD_SIZE;
+
 		if (pmd_val(*pmd)) {
 			if (!pmd_large(*pmd)) {
 				spin_lock(&init_mm.page_table_lock);
@@ -449,7 +451,7 @@
 			 * attributes.
 			 */
 			if (page_size_mask & (1 << PG_LEVEL_2M)) {
-				pages++;
+				last_map_addr = next;
 				continue;
 			}
 			new_prot = pte_pgprot(pte_clrhuge(*(pte_t *)pmd));
@@ -462,7 +464,7 @@
 				pfn_pte(address >> PAGE_SHIFT,
 					__pgprot(pgprot_val(prot) | _PAGE_PSE)));
 			spin_unlock(&init_mm.page_table_lock);
-			last_map_addr = (address & PMD_MASK) + PMD_SIZE;
+			last_map_addr = next;
 			continue;
 		}
 
@@ -482,11 +484,11 @@
 phys_pud_init(pud_t *pud_page, unsigned long addr, unsigned long end,
 			 unsigned long page_size_mask)
 {
-	unsigned long pages = 0;
+	unsigned long pages = 0, next;
 	unsigned long last_map_addr = end;
 	int i = pud_index(addr);
 
-	for (; i < PTRS_PER_PUD; i++, addr = (addr & PUD_MASK) + PUD_SIZE) {
+	for (; i < PTRS_PER_PUD; i++, addr = next) {
 		unsigned long pmd_phys;
 		pud_t *pud = pud_page + pud_index(addr);
 		pmd_t *pmd;
@@ -495,8 +497,9 @@
 		if (addr >= end)
 			break;
 
-		if (!after_bootmem &&
-				!e820_any_mapped(addr, addr+PUD_SIZE, 0)) {
+		next = (addr & PUD_MASK) + PUD_SIZE;
+
+		if (!after_bootmem && !e820_any_mapped(addr, next, 0)) {
 			set_pud(pud, __pud(0));
 			continue;
 		}
@@ -523,7 +526,7 @@
 			 * attributes.
 			 */
 			if (page_size_mask & (1 << PG_LEVEL_1G)) {
-				pages++;
+				last_map_addr = next;
 				continue;
 			}
 			prot = pte_pgprot(pte_clrhuge(*(pte_t *)pud));
@@ -535,7 +538,7 @@
 			set_pte((pte_t *)pud,
 				pfn_pte(addr >> PAGE_SHIFT, PAGE_KERNEL_LARGE));
 			spin_unlock(&init_mm.page_table_lock);
-			last_map_addr = (addr & PUD_MASK) + PUD_SIZE;
+			last_map_addr = next;
 			continue;
 		}
 
diff --git a/arch/x86/mm/tlb.c b/arch/x86/mm/tlb.c
index 3804471..5e57e11 100644
--- a/arch/x86/mm/tlb.c
+++ b/arch/x86/mm/tlb.c
@@ -61,11 +61,13 @@
  */
 void leave_mm(int cpu)
 {
+	struct mm_struct *active_mm = this_cpu_read(cpu_tlbstate.active_mm);
 	if (this_cpu_read(cpu_tlbstate.state) == TLBSTATE_OK)
 		BUG();
-	cpumask_clear_cpu(cpu,
-			  mm_cpumask(this_cpu_read(cpu_tlbstate.active_mm)));
-	load_cr3(swapper_pg_dir);
+	if (cpumask_test_cpu(cpu, mm_cpumask(active_mm))) {
+		cpumask_clear_cpu(cpu, mm_cpumask(active_mm));
+		load_cr3(swapper_pg_dir);
+	}
 }
 EXPORT_SYMBOL_GPL(leave_mm);
 
diff --git a/arch/x86/pci/Makefile b/arch/x86/pci/Makefile
index e76e18c..3af5a1e 100644
--- a/arch/x86/pci/Makefile
+++ b/arch/x86/pci/Makefile
@@ -11,6 +11,8 @@
 obj-$(CONFIG_ACPI)		+= acpi.o
 obj-y				+= legacy.o irq.o
 
+obj-$(CONFIG_STA2X11)           += sta2x11-fixup.o
+
 obj-$(CONFIG_X86_VISWS)		+= visws.o
 
 obj-$(CONFIG_X86_NUMAQ)		+= numaq_32.o
diff --git a/arch/x86/pci/fixup.c b/arch/x86/pci/fixup.c
index d0e6e40..5dd467b 100644
--- a/arch/x86/pci/fixup.c
+++ b/arch/x86/pci/fixup.c
@@ -519,3 +519,20 @@
 	}
 }
 DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_ATI, 0x4385, sb600_disable_hpet_bar);
+
+/*
+ * Twinhead H12Y needs us to block out a region otherwise we map devices
+ * there and any access kills the box.
+ *
+ *   See: https://bugzilla.kernel.org/show_bug.cgi?id=10231
+ *
+ * Match off the LPC and svid/sdid (older kernels lose the bridge subvendor)
+ */
+static void __devinit twinhead_reserve_killing_zone(struct pci_dev *dev)
+{
+        if (dev->subsystem_vendor == 0x14FF && dev->subsystem_device == 0xA003) {
+                pr_info("Reserving memory on Twinhead H12Y\n");
+                request_mem_region(0xFFB00000, 0x100000, "twinhead");
+        }
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x27B9, twinhead_reserve_killing_zone);
diff --git a/arch/x86/pci/sta2x11-fixup.c b/arch/x86/pci/sta2x11-fixup.c
new file mode 100644
index 0000000..9d8a509
--- /dev/null
+++ b/arch/x86/pci/sta2x11-fixup.c
@@ -0,0 +1,366 @@
+/*
+ * arch/x86/pci/sta2x11-fixup.c
+ * glue code for lib/swiotlb.c and DMA translation between STA2x11
+ * AMBA memory mapping and the X86 memory mapping
+ *
+ * ST Microelectronics ConneXt (STA2X11/STA2X10)
+ *
+ * Copyright (c) 2010-2011 Wind River Systems, 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/export.h>
+#include <linux/list.h>
+
+#define STA2X11_SWIOTLB_SIZE (4*1024*1024)
+extern int swiotlb_late_init_with_default_size(size_t default_size);
+
+/*
+ * We build a list of bus numbers that are under the ConneXt. The
+ * main bridge hosts 4 busses, which are the 4 endpoints, in order.
+ */
+#define STA2X11_NR_EP		4	/* 0..3 included */
+#define STA2X11_NR_FUNCS	8	/* 0..7 included */
+#define STA2X11_AMBA_SIZE	(512 << 20)
+
+struct sta2x11_ahb_regs { /* saved during suspend */
+	u32 base, pexlbase, pexhbase, crw;
+};
+
+struct sta2x11_mapping {
+	u32 amba_base;
+	int is_suspended;
+	struct sta2x11_ahb_regs regs[STA2X11_NR_FUNCS];
+};
+
+struct sta2x11_instance {
+	struct list_head list;
+	int bus0;
+	struct sta2x11_mapping map[STA2X11_NR_EP];
+};
+
+static LIST_HEAD(sta2x11_instance_list);
+
+/* At probe time, record new instances of this bridge (likely one only) */
+static void sta2x11_new_instance(struct pci_dev *pdev)
+{
+	struct sta2x11_instance *instance;
+
+	instance = kzalloc(sizeof(*instance), GFP_ATOMIC);
+	if (!instance)
+		return;
+	/* This has a subordinate bridge, with 4 more-subordinate ones */
+	instance->bus0 = pdev->subordinate->number + 1;
+
+	if (list_empty(&sta2x11_instance_list)) {
+		int size = STA2X11_SWIOTLB_SIZE;
+		/* First instance: register your own swiotlb area */
+		dev_info(&pdev->dev, "Using SWIOTLB (size %i)\n", size);
+		if (swiotlb_late_init_with_default_size(size))
+			dev_emerg(&pdev->dev, "init swiotlb failed\n");
+	}
+	list_add(&instance->list, &sta2x11_instance_list);
+}
+DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_STMICRO, 0xcc17, sta2x11_new_instance);
+
+/*
+ * Utility functions used in this file from below
+ */
+static struct sta2x11_instance *sta2x11_pdev_to_instance(struct pci_dev *pdev)
+{
+	struct sta2x11_instance *instance;
+	int ep;
+
+	list_for_each_entry(instance, &sta2x11_instance_list, list) {
+		ep = pdev->bus->number - instance->bus0;
+		if (ep >= 0 && ep < STA2X11_NR_EP)
+			return instance;
+	}
+	return NULL;
+}
+
+static int sta2x11_pdev_to_ep(struct pci_dev *pdev)
+{
+	struct sta2x11_instance *instance;
+
+	instance = sta2x11_pdev_to_instance(pdev);
+	if (!instance)
+		return -1;
+
+	return pdev->bus->number - instance->bus0;
+}
+
+static struct sta2x11_mapping *sta2x11_pdev_to_mapping(struct pci_dev *pdev)
+{
+	struct sta2x11_instance *instance;
+	int ep;
+
+	instance = sta2x11_pdev_to_instance(pdev);
+	if (!instance)
+		return NULL;
+	ep = sta2x11_pdev_to_ep(pdev);
+	return instance->map + ep;
+}
+
+/* This is exported, as some devices need to access the MFD registers */
+struct sta2x11_instance *sta2x11_get_instance(struct pci_dev *pdev)
+{
+	return sta2x11_pdev_to_instance(pdev);
+}
+EXPORT_SYMBOL(sta2x11_get_instance);
+
+
+/**
+ * p2a - Translate physical address to STA2x11 AMBA address,
+ *       used for DMA transfers to STA2x11
+ * @p: Physical address
+ * @pdev: PCI device (must be hosted within the connext)
+ */
+static dma_addr_t p2a(dma_addr_t p, struct pci_dev *pdev)
+{
+	struct sta2x11_mapping *map;
+	dma_addr_t a;
+
+	map = sta2x11_pdev_to_mapping(pdev);
+	a = p + map->amba_base;
+	return a;
+}
+
+/**
+ * a2p - Translate STA2x11 AMBA address to physical address
+ *       used for DMA transfers from STA2x11
+ * @a: STA2x11 AMBA address
+ * @pdev: PCI device (must be hosted within the connext)
+ */
+static dma_addr_t a2p(dma_addr_t a, struct pci_dev *pdev)
+{
+	struct sta2x11_mapping *map;
+	dma_addr_t p;
+
+	map = sta2x11_pdev_to_mapping(pdev);
+	p = a - map->amba_base;
+	return p;
+}
+
+/**
+ * sta2x11_swiotlb_alloc_coherent - Allocate swiotlb bounce buffers
+ *     returns virtual address. This is the only "special" function here.
+ * @dev: PCI device
+ * @size: Size of the buffer
+ * @dma_handle: DMA address
+ * @flags: memory flags
+ */
+static void *sta2x11_swiotlb_alloc_coherent(struct device *dev,
+					    size_t size,
+					    dma_addr_t *dma_handle,
+					    gfp_t flags,
+					    struct dma_attrs *attrs)
+{
+	void *vaddr;
+
+	vaddr = dma_generic_alloc_coherent(dev, size, dma_handle, flags, attrs);
+	if (!vaddr)
+		vaddr = swiotlb_alloc_coherent(dev, size, dma_handle, flags);
+	*dma_handle = p2a(*dma_handle, to_pci_dev(dev));
+	return vaddr;
+}
+
+/* We have our own dma_ops: the same as swiotlb but from alloc (above) */
+static struct dma_map_ops sta2x11_dma_ops = {
+	.alloc = sta2x11_swiotlb_alloc_coherent,
+	.free = swiotlb_free_coherent,
+	.map_page = swiotlb_map_page,
+	.unmap_page = swiotlb_unmap_page,
+	.map_sg = swiotlb_map_sg_attrs,
+	.unmap_sg = swiotlb_unmap_sg_attrs,
+	.sync_single_for_cpu = swiotlb_sync_single_for_cpu,
+	.sync_single_for_device = swiotlb_sync_single_for_device,
+	.sync_sg_for_cpu = swiotlb_sync_sg_for_cpu,
+	.sync_sg_for_device = swiotlb_sync_sg_for_device,
+	.mapping_error = swiotlb_dma_mapping_error,
+	.dma_supported = NULL, /* FIXME: we should use this instead! */
+};
+
+/* At setup time, we use our own ops if the device is a ConneXt one */
+static void sta2x11_setup_pdev(struct pci_dev *pdev)
+{
+	struct sta2x11_instance *instance = sta2x11_pdev_to_instance(pdev);
+
+	if (!instance) /* either a sta2x11 bridge or another ST device */
+		return;
+	pci_set_consistent_dma_mask(pdev, STA2X11_AMBA_SIZE - 1);
+	pci_set_dma_mask(pdev, STA2X11_AMBA_SIZE - 1);
+	pdev->dev.archdata.dma_ops = &sta2x11_dma_ops;
+
+	/* We must enable all devices as master, for audio DMA to work */
+	pci_set_master(pdev);
+}
+DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_STMICRO, PCI_ANY_ID, sta2x11_setup_pdev);
+
+/*
+ * The following three functions are exported (used in swiotlb: FIXME)
+ */
+/**
+ * dma_capable - Check if device can manage DMA transfers (FIXME: kill it)
+ * @dev: device for a PCI device
+ * @addr: DMA address
+ * @size: DMA size
+ */
+bool dma_capable(struct device *dev, dma_addr_t addr, size_t size)
+{
+	struct sta2x11_mapping *map;
+
+	if (dev->archdata.dma_ops != &sta2x11_dma_ops) {
+		if (!dev->dma_mask)
+			return false;
+		return addr + size - 1 <= *dev->dma_mask;
+	}
+
+	map = sta2x11_pdev_to_mapping(to_pci_dev(dev));
+
+	if (!map || (addr < map->amba_base))
+		return false;
+	if (addr + size >= map->amba_base + STA2X11_AMBA_SIZE) {
+		return false;
+	}
+
+	return true;
+}
+
+/**
+ * phys_to_dma - Return the DMA AMBA address used for this STA2x11 device
+ * @dev: device for a PCI device
+ * @paddr: Physical address
+ */
+dma_addr_t phys_to_dma(struct device *dev, phys_addr_t paddr)
+{
+	if (dev->archdata.dma_ops != &sta2x11_dma_ops)
+		return paddr;
+	return p2a(paddr, to_pci_dev(dev));
+}
+
+/**
+ * dma_to_phys - Return the physical address used for this STA2x11 DMA address
+ * @dev: device for a PCI device
+ * @daddr: STA2x11 AMBA DMA address
+ */
+phys_addr_t dma_to_phys(struct device *dev, dma_addr_t daddr)
+{
+	if (dev->archdata.dma_ops != &sta2x11_dma_ops)
+		return daddr;
+	return a2p(daddr, to_pci_dev(dev));
+}
+
+
+/*
+ * At boot we must set up the mappings for the pcie-to-amba bridge.
+ * It involves device access, and the same happens at suspend/resume time
+ */
+
+#define AHB_MAPB		0xCA4
+#define AHB_CRW(i)		(AHB_MAPB + 0  + (i) * 0x10)
+#define AHB_CRW_SZMASK			0xfffffc00UL
+#define AHB_CRW_ENABLE			(1 << 0)
+#define AHB_CRW_WTYPE_MEM		(2 << 1)
+#define AHB_CRW_ROE			(1UL << 3)	/* Relax Order Ena */
+#define AHB_CRW_NSE			(1UL << 4)	/* No Snoop Enable */
+#define AHB_BASE(i)		(AHB_MAPB + 4  + (i) * 0x10)
+#define AHB_PEXLBASE(i)		(AHB_MAPB + 8  + (i) * 0x10)
+#define AHB_PEXHBASE(i)		(AHB_MAPB + 12 + (i) * 0x10)
+
+/* At probe time, enable mapping for each endpoint, using the pdev */
+static void sta2x11_map_ep(struct pci_dev *pdev)
+{
+	struct sta2x11_mapping *map = sta2x11_pdev_to_mapping(pdev);
+	int i;
+
+	if (!map)
+		return;
+	pci_read_config_dword(pdev, AHB_BASE(0), &map->amba_base);
+
+	/* Configure AHB mapping */
+	pci_write_config_dword(pdev, AHB_PEXLBASE(0), 0);
+	pci_write_config_dword(pdev, AHB_PEXHBASE(0), 0);
+	pci_write_config_dword(pdev, AHB_CRW(0), STA2X11_AMBA_SIZE |
+			       AHB_CRW_WTYPE_MEM | AHB_CRW_ENABLE);
+
+	/* Disable all the other windows */
+	for (i = 1; i < STA2X11_NR_FUNCS; i++)
+		pci_write_config_dword(pdev, AHB_CRW(i), 0);
+
+	dev_info(&pdev->dev,
+		 "sta2x11: Map EP %i: AMBA address %#8x-%#8x\n",
+		 sta2x11_pdev_to_ep(pdev),  map->amba_base,
+		 map->amba_base + STA2X11_AMBA_SIZE - 1);
+}
+DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_STMICRO, PCI_ANY_ID, sta2x11_map_ep);
+
+#ifdef CONFIG_PM /* Some register values must be saved and restored */
+
+static void suspend_mapping(struct pci_dev *pdev)
+{
+	struct sta2x11_mapping *map = sta2x11_pdev_to_mapping(pdev);
+	int i;
+
+	if (!map)
+		return;
+
+	if (map->is_suspended)
+		return;
+	map->is_suspended = 1;
+
+	/* Save all window configs */
+	for (i = 0; i < STA2X11_NR_FUNCS; i++) {
+		struct sta2x11_ahb_regs *regs = map->regs + i;
+
+		pci_read_config_dword(pdev, AHB_BASE(i), &regs->base);
+		pci_read_config_dword(pdev, AHB_PEXLBASE(i), &regs->pexlbase);
+		pci_read_config_dword(pdev, AHB_PEXHBASE(i), &regs->pexhbase);
+		pci_read_config_dword(pdev, AHB_CRW(i), &regs->crw);
+	}
+}
+DECLARE_PCI_FIXUP_SUSPEND(PCI_VENDOR_ID_STMICRO, PCI_ANY_ID, suspend_mapping);
+
+static void resume_mapping(struct pci_dev *pdev)
+{
+	struct sta2x11_mapping *map = sta2x11_pdev_to_mapping(pdev);
+	int i;
+
+	if (!map)
+		return;
+
+
+	if (!map->is_suspended)
+		goto out;
+	map->is_suspended = 0;
+
+	/* Restore all window configs */
+	for (i = 0; i < STA2X11_NR_FUNCS; i++) {
+		struct sta2x11_ahb_regs *regs = map->regs + i;
+
+		pci_write_config_dword(pdev, AHB_BASE(i), regs->base);
+		pci_write_config_dword(pdev, AHB_PEXLBASE(i), regs->pexlbase);
+		pci_write_config_dword(pdev, AHB_PEXHBASE(i), regs->pexhbase);
+		pci_write_config_dword(pdev, AHB_CRW(i), regs->crw);
+	}
+out:
+	pci_set_master(pdev); /* Like at boot, enable master on all devices */
+}
+DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_STMICRO, PCI_ANY_ID, resume_mapping);
+
+#endif /* CONFIG_PM */
diff --git a/arch/x86/platform/olpc/olpc-xo1-sci.c b/arch/x86/platform/olpc/olpc-xo1-sci.c
index 1d4c783..04b8c73 100644
--- a/arch/x86/platform/olpc/olpc-xo1-sci.c
+++ b/arch/x86/platform/olpc/olpc-xo1-sci.c
@@ -18,6 +18,7 @@
 #include <linux/interrupt.h>
 #include <linux/platform_device.h>
 #include <linux/pm.h>
+#include <linux/pm_wakeup.h>
 #include <linux/mfd/core.h>
 #include <linux/power_supply.h>
 #include <linux/suspend.h>
@@ -83,8 +84,12 @@
 		return;
 	}
 
+	if (!!test_bit(SW_TABLET_MODE, ebook_switch_idev->sw) == state)
+		return; /* Nothing new to report. */
+
 	input_report_switch(ebook_switch_idev, SW_TABLET_MODE, state);
 	input_sync(ebook_switch_idev);
+	pm_wakeup_event(&ebook_switch_idev->dev, 0);
 }
 
 static void flip_lid_inverter(void)
@@ -123,8 +128,12 @@
 /* Report current lid switch state through input layer */
 static void send_lid_state(void)
 {
+	if (!!test_bit(SW_LID, lid_switch_idev->sw) == !lid_open)
+		return; /* Nothing new to report. */
+
 	input_report_switch(lid_switch_idev, SW_LID, !lid_open);
 	input_sync(lid_switch_idev);
+	pm_wakeup_event(&lid_switch_idev->dev, 0);
 }
 
 static ssize_t lid_wake_mode_show(struct device *dev,
@@ -213,11 +222,30 @@
 
 	dev_dbg(&pdev->dev, "sts %x gpe %x\n", sts, gpe);
 
-	if (sts & CS5536_PWRBTN_FLAG && !(sts & CS5536_WAK_FLAG)) {
-		input_report_key(power_button_idev, KEY_POWER, 1);
-		input_sync(power_button_idev);
-		input_report_key(power_button_idev, KEY_POWER, 0);
-		input_sync(power_button_idev);
+	if (sts & CS5536_PWRBTN_FLAG) {
+		if (!(sts & CS5536_WAK_FLAG)) {
+			/* Only report power button input when it was pressed
+			 * during regular operation (as opposed to when it
+			 * was used to wake the system). */
+			input_report_key(power_button_idev, KEY_POWER, 1);
+			input_sync(power_button_idev);
+			input_report_key(power_button_idev, KEY_POWER, 0);
+			input_sync(power_button_idev);
+		}
+		/* Report the wakeup event in all cases. */
+		pm_wakeup_event(&power_button_idev->dev, 0);
+	}
+
+	if ((sts & (CS5536_RTC_FLAG | CS5536_WAK_FLAG)) ==
+			(CS5536_RTC_FLAG | CS5536_WAK_FLAG)) {
+		/* When the system is woken by the RTC alarm, report the
+		 * event on the rtc device. */
+		struct device *rtc = bus_find_device_by_name(
+			&platform_bus_type, NULL, "rtc_cmos");
+		if (rtc) {
+			pm_wakeup_event(rtc, 0);
+			put_device(rtc);
+		}
 	}
 
 	if (gpe & CS5536_GPIOM7_PME_FLAG) { /* EC GPIO */
@@ -310,9 +338,10 @@
 		outb(lo, CS5536_PIC_INT_SEL2);
 	}
 
-	/* Enable SCI from power button, and clear pending interrupts */
+	/* Enable interesting SCI events, and clear pending interrupts */
 	sts = inl(acpi_base + CS5536_PM1_STS);
-	outl((CS5536_PM_PWRBTN << 16) | 0xffff, acpi_base + CS5536_PM1_STS);
+	outl(((CS5536_PM_PWRBTN | CS5536_PM_RTC) << 16) | 0xffff,
+	     acpi_base + CS5536_PM1_STS);
 
 	r = request_irq(sci_irq, xo1_sci_intr, 0, DRV_NAME, pdev);
 	if (r)
diff --git a/arch/x86/um/asm/elf.h b/arch/x86/um/asm/elf.h
index f3b0633..0e07adc 100644
--- a/arch/x86/um/asm/elf.h
+++ b/arch/x86/um/asm/elf.h
@@ -34,25 +34,25 @@
 #define ELF_ARCH        EM_386
 
 #define ELF_PLAT_INIT(regs, load_addr) do { \
-	PT_REGS_EBX(regs) = 0; \
-	PT_REGS_ECX(regs) = 0; \
-	PT_REGS_EDX(regs) = 0; \
-	PT_REGS_ESI(regs) = 0; \
-	PT_REGS_EDI(regs) = 0; \
-	PT_REGS_EBP(regs) = 0; \
-	PT_REGS_EAX(regs) = 0; \
+	PT_REGS_BX(regs) = 0; \
+	PT_REGS_CX(regs) = 0; \
+	PT_REGS_DX(regs) = 0; \
+	PT_REGS_SI(regs) = 0; \
+	PT_REGS_DI(regs) = 0; \
+	PT_REGS_BP(regs) = 0; \
+	PT_REGS_AX(regs) = 0; \
 } while (0)
 
 /* Shamelessly stolen from include/asm-i386/elf.h */
 
 #define ELF_CORE_COPY_REGS(pr_reg, regs) do {	\
-	pr_reg[0] = PT_REGS_EBX(regs);		\
-	pr_reg[1] = PT_REGS_ECX(regs);		\
-	pr_reg[2] = PT_REGS_EDX(regs);		\
-	pr_reg[3] = PT_REGS_ESI(regs);		\
-	pr_reg[4] = PT_REGS_EDI(regs);		\
-	pr_reg[5] = PT_REGS_EBP(regs);		\
-	pr_reg[6] = PT_REGS_EAX(regs);		\
+	pr_reg[0] = PT_REGS_BX(regs);		\
+	pr_reg[1] = PT_REGS_CX(regs);		\
+	pr_reg[2] = PT_REGS_DX(regs);		\
+	pr_reg[3] = PT_REGS_SI(regs);		\
+	pr_reg[4] = PT_REGS_DI(regs);		\
+	pr_reg[5] = PT_REGS_BP(regs);		\
+	pr_reg[6] = PT_REGS_AX(regs);		\
 	pr_reg[7] = PT_REGS_DS(regs);		\
 	pr_reg[8] = PT_REGS_ES(regs);		\
 	/* fake once used fs and gs selectors? */	\
@@ -130,13 +130,13 @@
 #define ELF_ARCH        EM_X86_64
 
 #define ELF_PLAT_INIT(regs, load_addr)    do { \
-	PT_REGS_RBX(regs) = 0; \
-	PT_REGS_RCX(regs) = 0; \
-	PT_REGS_RDX(regs) = 0; \
-	PT_REGS_RSI(regs) = 0; \
-	PT_REGS_RDI(regs) = 0; \
-	PT_REGS_RBP(regs) = 0; \
-	PT_REGS_RAX(regs) = 0; \
+	PT_REGS_BX(regs) = 0; \
+	PT_REGS_CX(regs) = 0; \
+	PT_REGS_DX(regs) = 0; \
+	PT_REGS_SI(regs) = 0; \
+	PT_REGS_DI(regs) = 0; \
+	PT_REGS_BP(regs) = 0; \
+	PT_REGS_AX(regs) = 0; \
 	PT_REGS_R8(regs) = 0; \
 	PT_REGS_R9(regs) = 0; \
 	PT_REGS_R10(regs) = 0; \
diff --git a/arch/x86/um/asm/ptrace.h b/arch/x86/um/asm/ptrace.h
index c8aca8c..950dfb7 100644
--- a/arch/x86/um/asm/ptrace.h
+++ b/arch/x86/um/asm/ptrace.h
@@ -1,5 +1,39 @@
+#ifndef __UM_X86_PTRACE_H
+#define __UM_X86_PTRACE_H
+
 #ifdef CONFIG_X86_32
 # include "ptrace_32.h"
 #else
 # include "ptrace_64.h"
 #endif
+
+#define PT_REGS_AX(r) UPT_AX(&(r)->regs)
+#define PT_REGS_BX(r) UPT_BX(&(r)->regs)
+#define PT_REGS_CX(r) UPT_CX(&(r)->regs)
+#define PT_REGS_DX(r) UPT_DX(&(r)->regs)
+
+#define PT_REGS_SI(r) UPT_SI(&(r)->regs)
+#define PT_REGS_DI(r) UPT_DI(&(r)->regs)
+#define PT_REGS_BP(r) UPT_BP(&(r)->regs)
+#define PT_REGS_EFLAGS(r) UPT_EFLAGS(&(r)->regs)
+
+#define PT_REGS_CS(r) UPT_CS(&(r)->regs)
+#define PT_REGS_SS(r) UPT_SS(&(r)->regs)
+#define PT_REGS_DS(r) UPT_DS(&(r)->regs)
+#define PT_REGS_ES(r) UPT_ES(&(r)->regs)
+
+#define PT_REGS_ORIG_SYSCALL(r) PT_REGS_AX(r)
+#define PT_REGS_SYSCALL_RET(r) PT_REGS_AX(r)
+
+#define PT_FIX_EXEC_STACK(sp) do ; while(0)
+
+#define profile_pc(regs) PT_REGS_IP(regs)
+
+#define UPT_RESTART_SYSCALL(r) (UPT_IP(r) -= 2)
+#define UPT_SET_SYSCALL_RETURN(r, res) (UPT_AX(r) = (res))
+
+static inline long regs_return_value(struct uml_pt_regs *regs)
+{
+	return UPT_AX(regs);
+}
+#endif /* __UM_X86_PTRACE_H */
diff --git a/arch/x86/um/asm/ptrace_32.h b/arch/x86/um/asm/ptrace_32.h
index 5d2a591..2cf2253 100644
--- a/arch/x86/um/asm/ptrace_32.h
+++ b/arch/x86/um/asm/ptrace_32.h
@@ -11,29 +11,6 @@
 #include "linux/compiler.h"
 #include "asm/ptrace-generic.h"
 
-#define PT_REGS_EAX(r) UPT_EAX(&(r)->regs)
-#define PT_REGS_EBX(r) UPT_EBX(&(r)->regs)
-#define PT_REGS_ECX(r) UPT_ECX(&(r)->regs)
-#define PT_REGS_EDX(r) UPT_EDX(&(r)->regs)
-#define PT_REGS_ESI(r) UPT_ESI(&(r)->regs)
-#define PT_REGS_EDI(r) UPT_EDI(&(r)->regs)
-#define PT_REGS_EBP(r) UPT_EBP(&(r)->regs)
-
-#define PT_REGS_CS(r) UPT_CS(&(r)->regs)
-#define PT_REGS_SS(r) UPT_SS(&(r)->regs)
-#define PT_REGS_DS(r) UPT_DS(&(r)->regs)
-#define PT_REGS_ES(r) UPT_ES(&(r)->regs)
-#define PT_REGS_FS(r) UPT_FS(&(r)->regs)
-#define PT_REGS_GS(r) UPT_GS(&(r)->regs)
-
-#define PT_REGS_EFLAGS(r) UPT_EFLAGS(&(r)->regs)
-
-#define PT_REGS_ORIG_SYSCALL(r) PT_REGS_EAX(r)
-#define PT_REGS_SYSCALL_RET(r) PT_REGS_EAX(r)
-#define PT_FIX_EXEC_STACK(sp) do ; while(0)
-
-#define profile_pc(regs) PT_REGS_IP(regs)
-
 #define user_mode(r) UPT_IS_USER(&(r)->regs)
 
 /*
diff --git a/arch/x86/um/asm/ptrace_64.h b/arch/x86/um/asm/ptrace_64.h
index 706a0d8..ea7bff3 100644
--- a/arch/x86/um/asm/ptrace_64.h
+++ b/arch/x86/um/asm/ptrace_64.h
@@ -15,13 +15,6 @@
 
 #define HOST_AUDIT_ARCH AUDIT_ARCH_X86_64
 
-#define PT_REGS_RBX(r) UPT_RBX(&(r)->regs)
-#define PT_REGS_RCX(r) UPT_RCX(&(r)->regs)
-#define PT_REGS_RDX(r) UPT_RDX(&(r)->regs)
-#define PT_REGS_RSI(r) UPT_RSI(&(r)->regs)
-#define PT_REGS_RDI(r) UPT_RDI(&(r)->regs)
-#define PT_REGS_RBP(r) UPT_RBP(&(r)->regs)
-#define PT_REGS_RAX(r) UPT_RAX(&(r)->regs)
 #define PT_REGS_R8(r) UPT_R8(&(r)->regs)
 #define PT_REGS_R9(r) UPT_R9(&(r)->regs)
 #define PT_REGS_R10(r) UPT_R10(&(r)->regs)
@@ -31,27 +24,8 @@
 #define PT_REGS_R14(r) UPT_R14(&(r)->regs)
 #define PT_REGS_R15(r) UPT_R15(&(r)->regs)
 
-#define PT_REGS_FS(r) UPT_FS(&(r)->regs)
-#define PT_REGS_GS(r) UPT_GS(&(r)->regs)
-#define PT_REGS_DS(r) UPT_DS(&(r)->regs)
-#define PT_REGS_ES(r) UPT_ES(&(r)->regs)
-#define PT_REGS_SS(r) UPT_SS(&(r)->regs)
-#define PT_REGS_CS(r) UPT_CS(&(r)->regs)
-
-#define PT_REGS_ORIG_RAX(r) UPT_ORIG_RAX(&(r)->regs)
-#define PT_REGS_RIP(r) UPT_IP(&(r)->regs)
-#define PT_REGS_SP(r) UPT_SP(&(r)->regs)
-
-#define PT_REGS_EFLAGS(r) UPT_EFLAGS(&(r)->regs)
-
 /* XXX */
 #define user_mode(r) UPT_IS_USER(&(r)->regs)
-#define PT_REGS_ORIG_SYSCALL(r) PT_REGS_RAX(r)
-#define PT_REGS_SYSCALL_RET(r) PT_REGS_RAX(r)
-
-#define PT_FIX_EXEC_STACK(sp) do ; while(0)
-
-#define profile_pc(regs) PT_REGS_IP(regs)
 
 struct user_desc;
 
diff --git a/arch/x86/um/checksum_32.S b/arch/x86/um/checksum_32.S
index f058d2f..8d0c420 100644
--- a/arch/x86/um/checksum_32.S
+++ b/arch/x86/um/checksum_32.S
@@ -26,6 +26,7 @@
  */
 
 #include <asm/errno.h>
+#include <asm/asm.h>
 				
 /*
  * computes a partial checksum, e.g. for TCP/UDP fragments
@@ -232,15 +233,11 @@
 
 #define SRC(y...)			\
 	9999: y;			\
-	.section __ex_table, "a";	\
-	.long 9999b, 6001f	;	\
-	.previous
+	_ASM_EXTABLE(9999b, 6001f)
 
 #define DST(y...)			\
 	9999: y;			\
-	.section __ex_table, "a";	\
-	.long 9999b, 6002f	;	\
-	.previous
+	_ASM_EXTABLE(9999b, 6002f)
 
 .align 4
 
diff --git a/arch/x86/um/shared/sysdep/ptrace.h b/arch/x86/um/shared/sysdep/ptrace.h
index 2bbe1ec2..6ce2d76 100644
--- a/arch/x86/um/shared/sysdep/ptrace.h
+++ b/arch/x86/um/shared/sysdep/ptrace.h
@@ -1,15 +1,74 @@
 #ifndef __SYSDEP_X86_PTRACE_H
 #define __SYSDEP_X86_PTRACE_H
 
+#include <generated/user_constants.h>
+#include "sysdep/faultinfo.h"
+
+#define MAX_REG_OFFSET (UM_FRAME_SIZE)
+#define MAX_REG_NR ((MAX_REG_OFFSET) / sizeof(unsigned long))
+
+#define REGS_IP(r) ((r)[HOST_IP])
+#define REGS_SP(r) ((r)[HOST_SP])
+#define REGS_EFLAGS(r) ((r)[HOST_EFLAGS])
+#define REGS_AX(r) ((r)[HOST_AX])
+#define REGS_BX(r) ((r)[HOST_BX])
+#define REGS_CX(r) ((r)[HOST_CX])
+#define REGS_DX(r) ((r)[HOST_DX])
+#define REGS_SI(r) ((r)[HOST_SI])
+#define REGS_DI(r) ((r)[HOST_DI])
+#define REGS_BP(r) ((r)[HOST_BP])
+#define REGS_CS(r) ((r)[HOST_CS])
+#define REGS_SS(r) ((r)[HOST_SS])
+#define REGS_DS(r) ((r)[HOST_DS])
+#define REGS_ES(r) ((r)[HOST_ES])
+
+#define UPT_IP(r) REGS_IP((r)->gp)
+#define UPT_SP(r) REGS_SP((r)->gp)
+#define UPT_EFLAGS(r) REGS_EFLAGS((r)->gp)
+#define UPT_AX(r) REGS_AX((r)->gp)
+#define UPT_BX(r) REGS_BX((r)->gp)
+#define UPT_CX(r) REGS_CX((r)->gp)
+#define UPT_DX(r) REGS_DX((r)->gp)
+#define UPT_SI(r) REGS_SI((r)->gp)
+#define UPT_DI(r) REGS_DI((r)->gp)
+#define UPT_BP(r) REGS_BP((r)->gp)
+#define UPT_CS(r) REGS_CS((r)->gp)
+#define UPT_SS(r) REGS_SS((r)->gp)
+#define UPT_DS(r) REGS_DS((r)->gp)
+#define UPT_ES(r) REGS_ES((r)->gp)
+
 #ifdef __i386__
 #include "ptrace_32.h"
 #else
 #include "ptrace_64.h"
 #endif
 
-static inline long regs_return_value(struct uml_pt_regs *regs)
-{
-	return UPT_SYSCALL_RET(regs);
-}
+struct syscall_args {
+	unsigned long args[6];
+};
+
+#define SYSCALL_ARGS(r) ((struct syscall_args) \
+			 { .args = { UPT_SYSCALL_ARG1(r),	 \
+				     UPT_SYSCALL_ARG2(r),	 \
+				     UPT_SYSCALL_ARG3(r),	 \
+				     UPT_SYSCALL_ARG4(r),	 \
+				     UPT_SYSCALL_ARG5(r),	 \
+				     UPT_SYSCALL_ARG6(r) } } )
+
+struct uml_pt_regs {
+	unsigned long gp[MAX_REG_NR];
+	unsigned long fp[MAX_FP_NR];
+	struct faultinfo faultinfo;
+	long syscall;
+	int is_user;
+};
+
+#define EMPTY_UML_PT_REGS { }
+
+#define UPT_SYSCALL_NR(r) ((r)->syscall)
+#define UPT_FAULTINFO(r) (&(r)->faultinfo)
+#define UPT_IS_USER(r) ((r)->is_user)
+
+extern int user_context(unsigned long sp);
 
 #endif /* __SYSDEP_X86_PTRACE_H */
diff --git a/arch/x86/um/shared/sysdep/ptrace_32.h b/arch/x86/um/shared/sysdep/ptrace_32.h
index befd1df..b94a108 100644
--- a/arch/x86/um/shared/sysdep/ptrace_32.h
+++ b/arch/x86/um/shared/sysdep/ptrace_32.h
@@ -6,11 +6,7 @@
 #ifndef __SYSDEP_I386_PTRACE_H
 #define __SYSDEP_I386_PTRACE_H
 
-#include <generated/user_constants.h>
-#include "sysdep/faultinfo.h"
-
-#define MAX_REG_NR (UM_FRAME_SIZE / sizeof(unsigned long))
-#define MAX_REG_OFFSET (UM_FRAME_SIZE)
+#define MAX_FP_NR HOST_FPX_SIZE
 
 static inline void update_debugregs(int seq) {}
 
@@ -24,90 +20,16 @@
 int get_using_sysemu(void);
 extern int sysemu_supported;
 
-#define REGS_IP(r) ((r)[HOST_IP])
-#define REGS_SP(r) ((r)[HOST_SP])
-#define REGS_EFLAGS(r) ((r)[HOST_EFLAGS])
-#define REGS_EAX(r) ((r)[HOST_AX])
-#define REGS_EBX(r) ((r)[HOST_BX])
-#define REGS_ECX(r) ((r)[HOST_CX])
-#define REGS_EDX(r) ((r)[HOST_DX])
-#define REGS_ESI(r) ((r)[HOST_SI])
-#define REGS_EDI(r) ((r)[HOST_DI])
-#define REGS_EBP(r) ((r)[HOST_BP])
-#define REGS_CS(r) ((r)[HOST_CS])
-#define REGS_SS(r) ((r)[HOST_SS])
-#define REGS_DS(r) ((r)[HOST_DS])
-#define REGS_ES(r) ((r)[HOST_ES])
-#define REGS_FS(r) ((r)[HOST_FS])
-#define REGS_GS(r) ((r)[HOST_GS])
-
-#define REGS_SET_SYSCALL_RETURN(r, res) REGS_EAX(r) = (res)
-
-#define IP_RESTART_SYSCALL(ip) ((ip) -= 2)
-#define REGS_RESTART_SYSCALL(r) IP_RESTART_SYSCALL(REGS_IP(r))
-
 #ifndef PTRACE_SYSEMU_SINGLESTEP
 #define PTRACE_SYSEMU_SINGLESTEP 32
 #endif
 
-struct uml_pt_regs {
-	unsigned long gp[MAX_REG_NR];
-	unsigned long fp[HOST_FPX_SIZE];
-	struct faultinfo faultinfo;
-	long syscall;
-	int is_user;
-};
-
-#define EMPTY_UML_PT_REGS { }
-
-#define UPT_IP(r) REGS_IP((r)->gp)
-#define UPT_SP(r) REGS_SP((r)->gp)
-#define UPT_EFLAGS(r) REGS_EFLAGS((r)->gp)
-#define UPT_EAX(r) REGS_EAX((r)->gp)
-#define UPT_EBX(r) REGS_EBX((r)->gp)
-#define UPT_ECX(r) REGS_ECX((r)->gp)
-#define UPT_EDX(r) REGS_EDX((r)->gp)
-#define UPT_ESI(r) REGS_ESI((r)->gp)
-#define UPT_EDI(r) REGS_EDI((r)->gp)
-#define UPT_EBP(r) REGS_EBP((r)->gp)
-#define UPT_ORIG_EAX(r) ((r)->syscall)
-#define UPT_CS(r) REGS_CS((r)->gp)
-#define UPT_SS(r) REGS_SS((r)->gp)
-#define UPT_DS(r) REGS_DS((r)->gp)
-#define UPT_ES(r) REGS_ES((r)->gp)
-#define UPT_FS(r) REGS_FS((r)->gp)
-#define UPT_GS(r) REGS_GS((r)->gp)
-
-#define UPT_SYSCALL_ARG1(r) UPT_EBX(r)
-#define UPT_SYSCALL_ARG2(r) UPT_ECX(r)
-#define UPT_SYSCALL_ARG3(r) UPT_EDX(r)
-#define UPT_SYSCALL_ARG4(r) UPT_ESI(r)
-#define UPT_SYSCALL_ARG5(r) UPT_EDI(r)
-#define UPT_SYSCALL_ARG6(r) UPT_EBP(r)
-
-extern int user_context(unsigned long sp);
-
-#define UPT_IS_USER(r) ((r)->is_user)
-
-struct syscall_args {
-	unsigned long args[6];
-};
-
-#define SYSCALL_ARGS(r) ((struct syscall_args) \
-			 { .args = { UPT_SYSCALL_ARG1(r),	\
-				     UPT_SYSCALL_ARG2(r),	\
-				     UPT_SYSCALL_ARG3(r),	\
-				     UPT_SYSCALL_ARG4(r),	\
-				     UPT_SYSCALL_ARG5(r),	\
-				     UPT_SYSCALL_ARG6(r) } } )
-
-#define UPT_RESTART_SYSCALL(r) REGS_RESTART_SYSCALL((r)->gp)
-
-#define UPT_ORIG_SYSCALL(r) UPT_EAX(r)
-#define UPT_SYSCALL_NR(r) UPT_ORIG_EAX(r)
-#define UPT_SYSCALL_RET(r) UPT_EAX(r)
-
-#define UPT_FAULTINFO(r) (&(r)->faultinfo)
+#define UPT_SYSCALL_ARG1(r) UPT_BX(r)
+#define UPT_SYSCALL_ARG2(r) UPT_CX(r)
+#define UPT_SYSCALL_ARG3(r) UPT_DX(r)
+#define UPT_SYSCALL_ARG4(r) UPT_SI(r)
+#define UPT_SYSCALL_ARG5(r) UPT_DI(r)
+#define UPT_SYSCALL_ARG6(r) UPT_BP(r)
 
 extern void arch_init_registers(int pid);
 
diff --git a/arch/x86/um/shared/sysdep/ptrace_64.h b/arch/x86/um/shared/sysdep/ptrace_64.h
index 031edc5..919789f 100644
--- a/arch/x86/um/shared/sysdep/ptrace_64.h
+++ b/arch/x86/um/shared/sysdep/ptrace_64.h
@@ -8,22 +8,8 @@
 #ifndef __SYSDEP_X86_64_PTRACE_H
 #define __SYSDEP_X86_64_PTRACE_H
 
-#include <generated/user_constants.h>
-#include "sysdep/faultinfo.h"
+#define MAX_FP_NR HOST_FP_SIZE
 
-#define MAX_REG_OFFSET (UM_FRAME_SIZE)
-#define MAX_REG_NR ((MAX_REG_OFFSET) / sizeof(unsigned long))
-
-#define REGS_IP(r) ((r)[HOST_IP])
-#define REGS_SP(r) ((r)[HOST_SP])
-
-#define REGS_RBX(r) ((r)[HOST_BX])
-#define REGS_RCX(r) ((r)[HOST_CX])
-#define REGS_RDX(r) ((r)[HOST_DX])
-#define REGS_RSI(r) ((r)[HOST_SI])
-#define REGS_RDI(r) ((r)[HOST_DI])
-#define REGS_RBP(r) ((r)[HOST_BP])
-#define REGS_RAX(r) ((r)[HOST_AX])
 #define REGS_R8(r) ((r)[HOST_R8])
 #define REGS_R9(r) ((r)[HOST_R9])
 #define REGS_R10(r) ((r)[HOST_R10])
@@ -32,9 +18,6 @@
 #define REGS_R13(r) ((r)[HOST_R13])
 #define REGS_R14(r) ((r)[HOST_R14])
 #define REGS_R15(r) ((r)[HOST_R15])
-#define REGS_CS(r) ((r)[HOST_CS])
-#define REGS_EFLAGS(r) ((r)[HOST_EFLAGS])
-#define REGS_SS(r) ((r)[HOST_SS])
 
 #define HOST_FS_BASE 21
 #define HOST_GS_BASE 22
@@ -58,45 +41,6 @@
 #define GS (HOST_GS * sizeof(long))
 #endif
 
-#define REGS_FS_BASE(r) ((r)[HOST_FS_BASE])
-#define REGS_GS_BASE(r) ((r)[HOST_GS_BASE])
-#define REGS_DS(r) ((r)[HOST_DS])
-#define REGS_ES(r) ((r)[HOST_ES])
-#define REGS_FS(r) ((r)[HOST_FS])
-#define REGS_GS(r) ((r)[HOST_GS])
-
-#define REGS_ORIG_RAX(r) ((r)[HOST_ORIG_AX])
-
-#define REGS_SET_SYSCALL_RETURN(r, res) REGS_RAX(r) = (res)
-
-#define IP_RESTART_SYSCALL(ip) ((ip) -= 2)
-#define REGS_RESTART_SYSCALL(r) IP_RESTART_SYSCALL(REGS_IP(r))
-
-#define REGS_FAULT_ADDR(r) ((r)->fault_addr)
-
-#define REGS_FAULT_WRITE(r) FAULT_WRITE((r)->fault_type)
-
-#define REGS_TRAP(r) ((r)->trap_type)
-
-#define REGS_ERR(r) ((r)->fault_type)
-
-struct uml_pt_regs {
-	unsigned long gp[MAX_REG_NR];
-	unsigned long fp[HOST_FP_SIZE];
-	struct faultinfo faultinfo;
-	long syscall;
-	int is_user;
-};
-
-#define EMPTY_UML_PT_REGS { }
-
-#define UPT_RBX(r) REGS_RBX((r)->gp)
-#define UPT_RCX(r) REGS_RCX((r)->gp)
-#define UPT_RDX(r) REGS_RDX((r)->gp)
-#define UPT_RSI(r) REGS_RSI((r)->gp)
-#define UPT_RDI(r) REGS_RDI((r)->gp)
-#define UPT_RBP(r) REGS_RBP((r)->gp)
-#define UPT_RAX(r) REGS_RAX((r)->gp)
 #define UPT_R8(r) REGS_R8((r)->gp)
 #define UPT_R9(r) REGS_R9((r)->gp)
 #define UPT_R10(r) REGS_R10((r)->gp)
@@ -105,51 +49,14 @@
 #define UPT_R13(r) REGS_R13((r)->gp)
 #define UPT_R14(r) REGS_R14((r)->gp)
 #define UPT_R15(r) REGS_R15((r)->gp)
-#define UPT_CS(r) REGS_CS((r)->gp)
-#define UPT_FS_BASE(r) REGS_FS_BASE((r)->gp)
-#define UPT_FS(r) REGS_FS((r)->gp)
-#define UPT_GS_BASE(r) REGS_GS_BASE((r)->gp)
-#define UPT_GS(r) REGS_GS((r)->gp)
-#define UPT_DS(r) REGS_DS((r)->gp)
-#define UPT_ES(r) REGS_ES((r)->gp)
-#define UPT_CS(r) REGS_CS((r)->gp)
-#define UPT_SS(r) REGS_SS((r)->gp)
-#define UPT_ORIG_RAX(r) REGS_ORIG_RAX((r)->gp)
 
-#define UPT_IP(r) REGS_IP((r)->gp)
-#define UPT_SP(r) REGS_SP((r)->gp)
-
-#define UPT_EFLAGS(r) REGS_EFLAGS((r)->gp)
-#define UPT_SYSCALL_NR(r) ((r)->syscall)
-#define UPT_SYSCALL_RET(r) UPT_RAX(r)
-
-extern int user_context(unsigned long sp);
-
-#define UPT_IS_USER(r) ((r)->is_user)
-
-#define UPT_SYSCALL_ARG1(r) UPT_RDI(r)
-#define UPT_SYSCALL_ARG2(r) UPT_RSI(r)
-#define UPT_SYSCALL_ARG3(r) UPT_RDX(r)
+#define UPT_SYSCALL_ARG1(r) UPT_DI(r)
+#define UPT_SYSCALL_ARG2(r) UPT_SI(r)
+#define UPT_SYSCALL_ARG3(r) UPT_DX(r)
 #define UPT_SYSCALL_ARG4(r) UPT_R10(r)
 #define UPT_SYSCALL_ARG5(r) UPT_R8(r)
 #define UPT_SYSCALL_ARG6(r) UPT_R9(r)
 
-struct syscall_args {
-	unsigned long args[6];
-};
-
-#define SYSCALL_ARGS(r) ((struct syscall_args) \
-			 { .args = { UPT_SYSCALL_ARG1(r),	 \
-				     UPT_SYSCALL_ARG2(r),	 \
-				     UPT_SYSCALL_ARG3(r),	 \
-				     UPT_SYSCALL_ARG4(r),	 \
-				     UPT_SYSCALL_ARG5(r),	 \
-				     UPT_SYSCALL_ARG6(r) } } )
-
-#define UPT_RESTART_SYSCALL(r) REGS_RESTART_SYSCALL((r)->gp)
-
-#define UPT_FAULTINFO(r) (&(r)->faultinfo)
-
 static inline void arch_init_registers(int pid)
 {
 }
diff --git a/arch/x86/um/signal.c b/arch/x86/um/signal.c
index 4883b95..bb0fb03 100644
--- a/arch/x86/um/signal.c
+++ b/arch/x86/um/signal.c
@@ -156,6 +156,9 @@
 	struct sigcontext sc;
 	int err, pid;
 
+	/* Always make any pending restarted system calls return -EINTR */
+	current_thread_info()->restart_block.fn = do_no_restart_syscall;
+
 	err = copy_from_user(&sc, from, sizeof(sc));
 	if (err)
 		return err;
@@ -410,9 +413,9 @@
 
 	PT_REGS_SP(regs) = (unsigned long) frame;
 	PT_REGS_IP(regs) = (unsigned long) ka->sa.sa_handler;
-	PT_REGS_EAX(regs) = (unsigned long) sig;
-	PT_REGS_EDX(regs) = (unsigned long) 0;
-	PT_REGS_ECX(regs) = (unsigned long) 0;
+	PT_REGS_AX(regs) = (unsigned long) sig;
+	PT_REGS_DX(regs) = (unsigned long) 0;
+	PT_REGS_CX(regs) = (unsigned long) 0;
 
 	if ((current->ptrace & PT_DTRACE) && (current->ptrace & PT_PTRACED))
 		ptrace_notify(SIGTRAP);
@@ -460,9 +463,9 @@
 
 	PT_REGS_SP(regs) = (unsigned long) frame;
 	PT_REGS_IP(regs) = (unsigned long) ka->sa.sa_handler;
-	PT_REGS_EAX(regs) = (unsigned long) sig;
-	PT_REGS_EDX(regs) = (unsigned long) &frame->info;
-	PT_REGS_ECX(regs) = (unsigned long) &frame->uc;
+	PT_REGS_AX(regs) = (unsigned long) sig;
+	PT_REGS_DX(regs) = (unsigned long) &frame->info;
+	PT_REGS_CX(regs) = (unsigned long) &frame->uc;
 
 	if ((current->ptrace & PT_DTRACE) && (current->ptrace & PT_PTRACED))
 		ptrace_notify(SIGTRAP);
@@ -541,8 +544,8 @@
 			       set->sig[0]);
 	err |= __put_user(&frame->fpstate, &frame->uc.uc_mcontext.fpstate);
 	if (sizeof(*set) == 16) {
-		__put_user(set->sig[0], &frame->uc.uc_sigmask.sig[0]);
-		__put_user(set->sig[1], &frame->uc.uc_sigmask.sig[1]);
+		err |= __put_user(set->sig[0], &frame->uc.uc_sigmask.sig[0]);
+		err |= __put_user(set->sig[1], &frame->uc.uc_sigmask.sig[1]);
 	}
 	else
 		err |= __copy_to_user(&frame->uc.uc_sigmask, set,
@@ -570,17 +573,17 @@
 	}
 
 	PT_REGS_SP(regs) = (unsigned long) frame;
-	PT_REGS_RDI(regs) = sig;
+	PT_REGS_DI(regs) = sig;
 	/* In case the signal handler was declared without prototypes */
-	PT_REGS_RAX(regs) = 0;
+	PT_REGS_AX(regs) = 0;
 
 	/*
 	 * This also works for non SA_SIGINFO handlers because they expect the
 	 * next argument after the signal number on the stack.
 	 */
-	PT_REGS_RSI(regs) = (unsigned long) &frame->info;
-	PT_REGS_RDX(regs) = (unsigned long) &frame->uc;
-	PT_REGS_RIP(regs) = (unsigned long) ka->sa.sa_handler;
+	PT_REGS_SI(regs) = (unsigned long) &frame->info;
+	PT_REGS_DX(regs) = (unsigned long) &frame->uc;
+	PT_REGS_IP(regs) = (unsigned long) ka->sa.sa_handler;
  out:
 	return err;
 }
diff --git a/arch/x86/um/sys_call_table_64.c b/arch/x86/um/sys_call_table_64.c
index 9924776..170bd92 100644
--- a/arch/x86/um/sys_call_table_64.c
+++ b/arch/x86/um/sys_call_table_64.c
@@ -31,7 +31,6 @@
 #define stub_fork sys_fork
 #define stub_vfork sys_vfork
 #define stub_execve sys_execve
-#define stub_rt_sigsuspend sys_rt_sigsuspend
 #define stub_sigaltstack sys_sigaltstack
 #define stub_rt_sigreturn sys_rt_sigreturn
 
diff --git a/arch/x86/um/syscalls_32.c b/arch/x86/um/syscalls_32.c
index 70ca357..b853e86 100644
--- a/arch/x86/um/syscalls_32.c
+++ b/arch/x86/um/syscalls_32.c
@@ -44,10 +44,10 @@
 		old_sigset_t mask;
 		if (!access_ok(VERIFY_READ, act, sizeof(*act)) ||
 		    __get_user(new_ka.sa.sa_handler, &act->sa_handler) ||
-		    __get_user(new_ka.sa.sa_restorer, &act->sa_restorer))
+		    __get_user(new_ka.sa.sa_restorer, &act->sa_restorer) ||
+		    __get_user(new_ka.sa.sa_flags, &act->sa_flags) ||
+		    __get_user(mask, &act->sa_mask))
 			return -EFAULT;
-		__get_user(new_ka.sa.sa_flags, &act->sa_flags);
-		__get_user(mask, &act->sa_mask);
 		siginitset(&new_ka.sa.sa_mask, mask);
 	}
 
@@ -56,10 +56,10 @@
 	if (!ret && oact) {
 		if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)) ||
 		    __put_user(old_ka.sa.sa_handler, &oact->sa_handler) ||
-		    __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer))
+		    __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer) ||
+		    __put_user(old_ka.sa.sa_flags, &oact->sa_flags) ||
+		    __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask))
 			return -EFAULT;
-		__put_user(old_ka.sa.sa_flags, &oact->sa_flags);
-		__put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask);
 	}
 
 	return ret;
diff --git a/arch/x86/um/sysrq_32.c b/arch/x86/um/sysrq_32.c
index 171b3e9..2d5cc51 100644
--- a/arch/x86/um/sysrq_32.c
+++ b/arch/x86/um/sysrq_32.c
@@ -23,12 +23,10 @@
         printk(" EFLAGS: %08lx\n    %s\n", PT_REGS_EFLAGS(regs),
 	       print_tainted());
         printk("EAX: %08lx EBX: %08lx ECX: %08lx EDX: %08lx\n",
-                PT_REGS_EAX(regs), PT_REGS_EBX(regs), 
-	       PT_REGS_ECX(regs), 
-	       PT_REGS_EDX(regs));
+               PT_REGS_AX(regs), PT_REGS_BX(regs), 
+	       PT_REGS_CX(regs), PT_REGS_DX(regs));
         printk("ESI: %08lx EDI: %08lx EBP: %08lx",
-	       PT_REGS_ESI(regs), PT_REGS_EDI(regs), 
-	       PT_REGS_EBP(regs));
+	       PT_REGS_SI(regs), PT_REGS_DI(regs), PT_REGS_BP(regs));
         printk(" DS: %04lx ES: %04lx\n",
 	       0xffff & PT_REGS_DS(regs), 
 	       0xffff & PT_REGS_ES(regs));
diff --git a/arch/x86/um/sysrq_64.c b/arch/x86/um/sysrq_64.c
index e891343..08258f1 100644
--- a/arch/x86/um/sysrq_64.c
+++ b/arch/x86/um/sysrq_64.c
@@ -19,15 +19,15 @@
 	printk(KERN_INFO "Pid: %d, comm: %.20s %s %s\n", task_pid_nr(current),
 		current->comm, print_tainted(), init_utsname()->release);
 	printk(KERN_INFO "RIP: %04lx:[<%016lx>]\n", PT_REGS_CS(regs) & 0xffff,
-	       PT_REGS_RIP(regs));
+	       PT_REGS_IP(regs));
 	printk(KERN_INFO "RSP: %016lx  EFLAGS: %08lx\n", PT_REGS_SP(regs),
 	       PT_REGS_EFLAGS(regs));
 	printk(KERN_INFO "RAX: %016lx RBX: %016lx RCX: %016lx\n",
-	       PT_REGS_RAX(regs), PT_REGS_RBX(regs), PT_REGS_RCX(regs));
+	       PT_REGS_AX(regs), PT_REGS_BX(regs), PT_REGS_CX(regs));
 	printk(KERN_INFO "RDX: %016lx RSI: %016lx RDI: %016lx\n",
-	       PT_REGS_RDX(regs), PT_REGS_RSI(regs), PT_REGS_RDI(regs));
+	       PT_REGS_DX(regs), PT_REGS_SI(regs), PT_REGS_DI(regs));
 	printk(KERN_INFO "RBP: %016lx R08: %016lx R09: %016lx\n",
-	       PT_REGS_RBP(regs), PT_REGS_R8(regs), PT_REGS_R9(regs));
+	       PT_REGS_BP(regs), PT_REGS_R8(regs), PT_REGS_R9(regs));
 	printk(KERN_INFO "R10: %016lx R11: %016lx R12: %016lx\n",
 	       PT_REGS_R10(regs), PT_REGS_R11(regs), PT_REGS_R12(regs));
 	printk(KERN_INFO "R13: %016lx R14: %016lx R15: %016lx\n",
diff --git a/arch/x86/um/tls_32.c b/arch/x86/um/tls_32.c
index c6c7131..baba84f 100644
--- a/arch/x86/um/tls_32.c
+++ b/arch/x86/um/tls_32.c
@@ -219,7 +219,7 @@
 	int idx, ret = -EFAULT;
 
 	if (copy_from_user(&info,
-			   (void __user *) UPT_ESI(&new->thread.regs.regs),
+			   (void __user *) UPT_SI(&new->thread.regs.regs),
 			   sizeof(info)))
 		goto out;
 
diff --git a/arch/x86/xen/xen-asm_32.S b/arch/x86/xen/xen-asm_32.S
index b040b0e..f9643fc 100644
--- a/arch/x86/xen/xen-asm_32.S
+++ b/arch/x86/xen/xen-asm_32.S
@@ -14,6 +14,7 @@
 #include <asm/thread_info.h>
 #include <asm/processor-flags.h>
 #include <asm/segment.h>
+#include <asm/asm.h>
 
 #include <xen/interface/xen.h>
 
@@ -137,10 +138,7 @@
 
 1:	iret
 xen_iret_end_crit:
-.section __ex_table, "a"
-	.align 4
-	.long 1b, iret_exc
-.previous
+	_ASM_EXTABLE(1b, iret_exc)
 
 hyper_iret:
 	/* put this out of line since its very rarely used */
diff --git a/arch/xtensa/include/asm/processor.h b/arch/xtensa/include/asm/processor.h
index 3acb26e..5c371d8 100644
--- a/arch/xtensa/include/asm/processor.h
+++ b/arch/xtensa/include/asm/processor.h
@@ -168,9 +168,6 @@
 /* Free all resources held by a thread. */
 #define release_thread(thread) do { } while(0)
 
-/* Prepare to copy thread state - unlazy all lazy status */
-extern void prepare_to_copy(struct task_struct*);
-
 /* Create a kernel thread without removing it from tasklists */
 extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
 
diff --git a/arch/xtensa/kernel/process.c b/arch/xtensa/kernel/process.c
index 6a2d6ed..9b306e5 100644
--- a/arch/xtensa/kernel/process.c
+++ b/arch/xtensa/kernel/process.c
@@ -140,13 +140,16 @@
 }
 
 /*
- * This is called before the thread is copied. 
+ * this gets called so that we can store coprocessor state into memory and
+ * copy the current task into the new thread.
  */
-void prepare_to_copy(struct task_struct *tsk)
+int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)
 {
 #if XTENSA_HAVE_COPROCESSORS
-	coprocessor_flush_all(task_thread_info(tsk));
+	coprocessor_flush_all(task_thread_info(src));
 #endif
+	*dst = *src;
+	return 0;
 }
 
 /*
diff --git a/crypto/xor.c b/crypto/xor.c
index b75182d..65c7b41 100644
--- a/crypto/xor.c
+++ b/crypto/xor.c
@@ -21,6 +21,7 @@
 #include <linux/gfp.h>
 #include <linux/raid/xor.h>
 #include <linux/jiffies.h>
+#include <linux/preempt.h>
 #include <asm/xor.h>
 
 /* The xor routines to use.  */
@@ -63,12 +64,14 @@
 do_xor_speed(struct xor_block_template *tmpl, void *b1, void *b2)
 {
 	int speed;
-	unsigned long now;
+	unsigned long now, j;
 	int i, count, max;
 
 	tmpl->next = template_list;
 	template_list = tmpl;
 
+	preempt_disable();
+
 	/*
 	 * Count the number of XORs done during a whole jiffy, and use
 	 * this to calculate the speed of checksumming.  We use a 2-page
@@ -76,9 +79,11 @@
 	 */
 	max = 0;
 	for (i = 0; i < 5; i++) {
-		now = jiffies;
+		j = jiffies;
 		count = 0;
-		while (jiffies == now) {
+		while ((now = jiffies) == j)
+			cpu_relax();
+		while (time_before(jiffies, now + 1)) {
 			mb(); /* prevent loop optimzation */
 			tmpl->do_2(BENCH_SIZE, b1, b2);
 			mb();
@@ -89,6 +94,8 @@
 			max = count;
 	}
 
+	preempt_enable();
+
 	speed = max * (HZ * BENCH_SIZE / 1024);
 	tmpl->speed = speed;
 
@@ -129,9 +136,9 @@
 
 	if (fastest) {
 		printk(KERN_INFO "xor: automatically using best "
-			"checksumming function: %s\n",
-			fastest->name);
+				 "checksumming function:\n");
 		xor_speed(fastest);
+		goto out;
 	} else {
 		printk(KERN_INFO "xor: measuring software checksum speed\n");
 		XOR_TRY_TEMPLATES;
@@ -146,6 +153,7 @@
 
 #undef xor_speed
 
+ out:
 	free_pages((unsigned long)b1, 2);
 
 	active_template = fastest;
diff --git a/drivers/Makefile b/drivers/Makefile
index 35d5181..0ee98d5 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -92,7 +92,6 @@
 obj-$(CONFIG_ACCESSIBILITY)	+= accessibility/
 obj-$(CONFIG_ISDN)		+= isdn/
 obj-$(CONFIG_EDAC)		+= edac/
-obj-$(CONFIG_MCA)		+= mca/
 obj-$(CONFIG_EISA)		+= eisa/
 obj-y				+= lguest/
 obj-$(CONFIG_CPU_FREQ)		+= cpufreq/
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index 6bdedd7..2be8ef1 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -416,6 +416,15 @@
 
 	  If unsure, say N.
 
+config PATA_EP93XX
+	tristate "Cirrus Logic EP93xx PATA support"
+	depends on ARCH_EP93XX
+	help
+	  This option enables support for the PATA controller in
+	  the Cirrus Logic EP9312 and EP9315 ARM CPU.
+
+	  If unsure, say N.
+
 config PATA_HPT366
 	tristate "HPT 366/368 PATA support"
 	depends on PCI
diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
index 6ece5b7..a454a13 100644
--- a/drivers/ata/Makefile
+++ b/drivers/ata/Makefile
@@ -43,6 +43,7 @@
 obj-$(CONFIG_PATA_CS5536)	+= pata_cs5536.o
 obj-$(CONFIG_PATA_CYPRESS)	+= pata_cypress.o
 obj-$(CONFIG_PATA_EFAR)		+= pata_efar.o
+obj-$(CONFIG_PATA_EP93XX)	+= pata_ep93xx.o
 obj-$(CONFIG_PATA_HPT366)	+= pata_hpt366.o
 obj-$(CONFIG_PATA_HPT37X)	+= pata_hpt37x.o
 obj-$(CONFIG_PATA_HPT3X2N)	+= pata_hpt3x2n.o
diff --git a/drivers/ata/ata_generic.c b/drivers/ata/ata_generic.c
index 7df56ec..aae1156 100644
--- a/drivers/ata/ata_generic.c
+++ b/drivers/ata/ata_generic.c
@@ -177,7 +177,7 @@
 	if ((id->driver_data & ATA_GEN_CLASS_MATCH) && all_generic_ide == 0)
 		return -ENODEV;
 
-	if (id->driver_data & ATA_GEN_INTEL_IDER)
+	if ((id->driver_data & ATA_GEN_INTEL_IDER) && !all_generic_ide)
 		if (!is_intel_ider(dev))
 			return -ENODEV;
 
diff --git a/drivers/ata/ata_piix.c b/drivers/ata/ata_piix.c
index 7857e8f..3c809bf 100644
--- a/drivers/ata/ata_piix.c
+++ b/drivers/ata/ata_piix.c
@@ -1554,6 +1554,39 @@
 	return false;
 }
 
+static int prefer_ms_hyperv = 1;
+module_param(prefer_ms_hyperv, int, 0);
+
+static void piix_ignore_devices_quirk(struct ata_host *host)
+{
+#if IS_ENABLED(CONFIG_HYPERV_STORAGE)
+	static const struct dmi_system_id ignore_hyperv[] = {
+		{
+			/* On Hyper-V hypervisors the disks are exposed on
+			 * both the emulated SATA controller and on the
+			 * paravirtualised drivers.  The CD/DVD devices
+			 * are only exposed on the emulated controller.
+			 * Request we ignore ATA devices on this host.
+			 */
+			.ident = "Hyper-V Virtual Machine",
+			.matches = {
+				DMI_MATCH(DMI_SYS_VENDOR,
+						"Microsoft Corporation"),
+				DMI_MATCH(DMI_PRODUCT_NAME, "Virtual Machine"),
+			},
+		},
+		{ }	/* terminate list */
+	};
+	const struct dmi_system_id *dmi = dmi_first_match(ignore_hyperv);
+
+	if (dmi && prefer_ms_hyperv) {
+		host->flags |= ATA_HOST_IGNORE_ATA;
+		dev_info(host->dev, "%s detected, ATA device ignore set\n",
+			dmi->ident);
+	}
+#endif
+}
+
 /**
  *	piix_init_one - Register PIIX ATA PCI device with kernel services
  *	@pdev: PCI device to register
@@ -1669,6 +1702,9 @@
 	}
 	host->flags |= ATA_HOST_PARALLEL_SCAN;
 
+	/* Allow hosts to specify device types to ignore when scanning. */
+	piix_ignore_devices_quirk(host);
+
 	pci_set_master(pdev);
 	return ata_pci_sff_activate_host(host, ata_bmdma_interrupt, sht);
 }
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 23763a1..cece3a4 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -1973,6 +1973,12 @@
 	if (class == ATA_DEV_ATA) {
 		if (!ata_id_is_ata(id) && !ata_id_is_cfa(id))
 			goto err_out;
+		if (ap->host->flags & ATA_HOST_IGNORE_ATA &&
+							ata_id_is_ata(id)) {
+			ata_dev_dbg(dev,
+				"host indicates ignore ATA devices, ignored\n");
+			return -ENOENT;
+		}
 	} else {
 		if (ata_id_is_ata(id))
 			goto err_out;
@@ -4051,6 +4057,7 @@
 	{ "_NEC DV5800A", 	NULL,		ATA_HORKAGE_NODMA },
 	{ "SAMSUNG CD-ROM SN-124", "N001",	ATA_HORKAGE_NODMA },
 	{ "Seagate STT20000A", NULL,		ATA_HORKAGE_NODMA },
+	{ "2GB ATA Flash Disk", "ADMA428M",	ATA_HORKAGE_NODMA },
 	/* Odd clown on sil3726/4726 PMPs */
 	{ "Config  Disk",	NULL,		ATA_HORKAGE_DISABLE },
 
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
index d1fbd59..6d53cf9 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -2047,6 +2047,26 @@
 }
 
 /**
+ *	ata_eh_worth_retry - analyze error and decide whether to retry
+ *	@qc: qc to possibly retry
+ *
+ *	Look at the cause of the error and decide if a retry
+ * 	might be useful or not.  We don't want to retry media errors
+ *	because the drive itself has probably already taken 10-30 seconds
+ *	doing its own internal retries before reporting the failure.
+ */
+static inline int ata_eh_worth_retry(struct ata_queued_cmd *qc)
+{
+	if (qc->flags & AC_ERR_MEDIA)
+		return 0;	/* don't retry media errors */
+	if (qc->flags & ATA_QCFLAG_IO)
+		return 1;	/* otherwise retry anything from fs stack */
+	if (qc->err_mask & AC_ERR_INVALID)
+		return 0;	/* don't retry these */
+	return qc->err_mask != AC_ERR_DEV;  /* retry if not dev error */
+}
+
+/**
  *	ata_eh_link_autopsy - analyze error and determine recovery action
  *	@link: host link to perform autopsy on
  *
@@ -2120,9 +2140,7 @@
 			qc->err_mask &= ~(AC_ERR_DEV | AC_ERR_OTHER);
 
 		/* determine whether the command is worth retrying */
-		if (qc->flags & ATA_QCFLAG_IO ||
-		    (!(qc->err_mask & AC_ERR_INVALID) &&
-		     qc->err_mask != AC_ERR_DEV))
+		if (ata_eh_worth_retry(qc))
 			qc->flags |= ATA_QCFLAG_RETRY;
 
 		/* accumulate error info */
diff --git a/drivers/ata/pata_ep93xx.c b/drivers/ata/pata_ep93xx.c
new file mode 100644
index 0000000..6ef2e37
--- /dev/null
+++ b/drivers/ata/pata_ep93xx.c
@@ -0,0 +1,1044 @@
+/*
+ * EP93XX PATA controller driver.
+ *
+ * Copyright (c) 2012, Metasoft s.c.
+ *	Rafal Prylowski <prylowski@metasoft.pl>
+ *
+ * Based on pata_scc.c, pata_icside.c and on earlier version of EP93XX
+ * PATA driver by Lennert Buytenhek and Alessandro Zummo.
+ * Read/Write timings, resource management and other improvements
+ * from driver by Joao Ramos and Bartlomiej Zolnierkiewicz.
+ * DMA engine support based on spi-ep93xx.c by Mika Westerberg.
+ *
+ * Original copyrights:
+ *
+ * Support for Cirrus Logic's EP93xx (EP9312, EP9315) CPUs
+ * PATA host controller driver.
+ *
+ * Copyright (c) 2009, Bartlomiej Zolnierkiewicz
+ *
+ * Heavily based on the ep93xx-ide.c driver:
+ *
+ * Copyright (c) 2009, Joao Ramos <joao.ramos@inov.pt>
+ *		      INESC Inovacao (INOV)
+ *
+ * EP93XX PATA controller driver.
+ * Copyright (C) 2007 Lennert Buytenhek <buytenh@wantstofly.org>
+ *
+ * An ATA driver for the Cirrus Logic EP93xx PATA controller.
+ *
+ * Based on an earlier version by Alessandro Zummo, which is:
+ *   Copyright (C) 2006 Tower Technologies
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <scsi/scsi_host.h>
+#include <linux/ata.h>
+#include <linux/libata.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/dmaengine.h>
+#include <linux/ktime.h>
+
+#include <mach/dma.h>
+#include <mach/platform.h>
+
+#define DRV_NAME	"ep93xx-ide"
+#define DRV_VERSION	"1.0"
+
+enum {
+	/* IDE Control Register */
+	IDECTRL				= 0x00,
+	IDECTRL_CS0N			= (1 << 0),
+	IDECTRL_CS1N			= (1 << 1),
+	IDECTRL_DIORN			= (1 << 5),
+	IDECTRL_DIOWN			= (1 << 6),
+	IDECTRL_INTRQ			= (1 << 9),
+	IDECTRL_IORDY			= (1 << 10),
+	/*
+	 * the device IDE register to be accessed is selected through
+	 * IDECTRL register's specific bitfields 'DA', 'CS1N' and 'CS0N':
+	 *   b4   b3   b2    b1     b0
+	 *   A2   A1   A0   CS1N   CS0N
+	 * the values filled in this structure allows the value to be directly
+	 * ORed to the IDECTRL register, hence giving directly the A[2:0] and
+	 * CS1N/CS0N values for each IDE register.
+	 * The values correspond to the transformation:
+	 *   ((real IDE address) << 2) | CS1N value << 1 | CS0N value
+	 */
+	IDECTRL_ADDR_CMD		= 0 + 2, /* CS1 */
+	IDECTRL_ADDR_DATA		= (ATA_REG_DATA << 2) + 2,
+	IDECTRL_ADDR_ERROR		= (ATA_REG_ERR << 2) + 2,
+	IDECTRL_ADDR_FEATURE		= (ATA_REG_FEATURE << 2) + 2,
+	IDECTRL_ADDR_NSECT		= (ATA_REG_NSECT << 2) + 2,
+	IDECTRL_ADDR_LBAL		= (ATA_REG_LBAL << 2) + 2,
+	IDECTRL_ADDR_LBAM		= (ATA_REG_LBAM << 2) + 2,
+	IDECTRL_ADDR_LBAH		= (ATA_REG_LBAH << 2) + 2,
+	IDECTRL_ADDR_DEVICE		= (ATA_REG_DEVICE << 2) + 2,
+	IDECTRL_ADDR_STATUS		= (ATA_REG_STATUS << 2) + 2,
+	IDECTRL_ADDR_COMMAND		= (ATA_REG_CMD << 2) + 2,
+	IDECTRL_ADDR_ALTSTATUS		= (0x06 << 2) + 1, /* CS0 */
+	IDECTRL_ADDR_CTL		= (0x06 << 2) + 1, /* CS0 */
+
+	/* IDE Configuration Register */
+	IDECFG				= 0x04,
+	IDECFG_IDEEN			= (1 << 0),
+	IDECFG_PIO			= (1 << 1),
+	IDECFG_MDMA			= (1 << 2),
+	IDECFG_UDMA			= (1 << 3),
+	IDECFG_MODE_SHIFT		= 4,
+	IDECFG_MODE_MASK		= (0xf << 4),
+	IDECFG_WST_SHIFT		= 8,
+	IDECFG_WST_MASK			= (0x3 << 8),
+
+	/* MDMA Operation Register */
+	IDEMDMAOP			= 0x08,
+
+	/* UDMA Operation Register */
+	IDEUDMAOP			= 0x0c,
+	IDEUDMAOP_UEN			= (1 << 0),
+	IDEUDMAOP_RWOP			= (1 << 1),
+
+	/* PIO/MDMA/UDMA Data Registers */
+	IDEDATAOUT			= 0x10,
+	IDEDATAIN			= 0x14,
+	IDEMDMADATAOUT			= 0x18,
+	IDEMDMADATAIN			= 0x1c,
+	IDEUDMADATAOUT			= 0x20,
+	IDEUDMADATAIN			= 0x24,
+
+	/* UDMA Status Register */
+	IDEUDMASTS			= 0x28,
+	IDEUDMASTS_DMAIDE		= (1 << 16),
+	IDEUDMASTS_INTIDE		= (1 << 17),
+	IDEUDMASTS_SBUSY		= (1 << 18),
+	IDEUDMASTS_NDO			= (1 << 24),
+	IDEUDMASTS_NDI			= (1 << 25),
+	IDEUDMASTS_N4X			= (1 << 26),
+
+	/* UDMA Debug Status Register */
+	IDEUDMADEBUG			= 0x2c,
+};
+
+struct ep93xx_pata_data {
+	const struct platform_device *pdev;
+	void __iomem *ide_base;
+	struct ata_timing t;
+	bool iordy;
+
+	unsigned long udma_in_phys;
+	unsigned long udma_out_phys;
+
+	struct dma_chan *dma_rx_channel;
+	struct ep93xx_dma_data dma_rx_data;
+	struct dma_chan *dma_tx_channel;
+	struct ep93xx_dma_data dma_tx_data;
+};
+
+static void ep93xx_pata_clear_regs(void __iomem *base)
+{
+	writel(IDECTRL_CS0N | IDECTRL_CS1N | IDECTRL_DIORN |
+		IDECTRL_DIOWN, base + IDECTRL);
+
+	writel(0, base + IDECFG);
+	writel(0, base + IDEMDMAOP);
+	writel(0, base + IDEUDMAOP);
+	writel(0, base + IDEDATAOUT);
+	writel(0, base + IDEDATAIN);
+	writel(0, base + IDEMDMADATAOUT);
+	writel(0, base + IDEMDMADATAIN);
+	writel(0, base + IDEUDMADATAOUT);
+	writel(0, base + IDEUDMADATAIN);
+	writel(0, base + IDEUDMADEBUG);
+}
+
+static bool ep93xx_pata_check_iordy(void __iomem *base)
+{
+	return !!(readl(base + IDECTRL) & IDECTRL_IORDY);
+}
+
+/*
+ * According to EP93xx User's Guide, WST field of IDECFG specifies number
+ * of HCLK cycles to hold the data bus after a PIO write operation.
+ * It should be programmed to guarantee following delays:
+ *
+ * PIO Mode   [ns]
+ * 0          30
+ * 1          20
+ * 2          15
+ * 3          10
+ * 4          5
+ *
+ * Maximum possible value for HCLK is 100MHz.
+ */
+static int ep93xx_pata_get_wst(int pio_mode)
+{
+	int val;
+
+	if (pio_mode == 0)
+		val = 3;
+	else if (pio_mode < 3)
+		val = 2;
+	else
+		val = 1;
+
+	return val << IDECFG_WST_SHIFT;
+}
+
+static void ep93xx_pata_enable_pio(void __iomem *base, int pio_mode)
+{
+	writel(IDECFG_IDEEN | IDECFG_PIO |
+		ep93xx_pata_get_wst(pio_mode) |
+		(pio_mode << IDECFG_MODE_SHIFT), base + IDECFG);
+}
+
+/*
+ * Based on delay loop found in mach-pxa/mp900.c.
+ *
+ * Single iteration should take 5 cpu cycles. This is 25ns assuming the
+ * fastest ep93xx cpu speed (200MHz) and is better optimized for PIO4 timings
+ * than eg. 20ns.
+ */
+static void ep93xx_pata_delay(unsigned long count)
+{
+	__asm__ volatile (
+		"0:\n"
+		"mov r0, r0\n"
+		"subs %0, %1, #1\n"
+		"bge 0b\n"
+		: "=r" (count)
+		: "0" (count)
+	);
+}
+
+static unsigned long ep93xx_pata_wait_for_iordy(void __iomem *base,
+						unsigned long t2)
+{
+	/*
+	 * According to ATA specification, IORDY pin can be first sampled
+	 * tA = 35ns after activation of DIOR-/DIOW-. Maximum IORDY pulse
+	 * width is tB = 1250ns.
+	 *
+	 * We are already t2 delay loop iterations after activation of
+	 * DIOR-/DIOW-, so we set timeout to (1250 + 35) / 25 - t2 additional
+	 * delay loop iterations.
+	 */
+	unsigned long start = (1250 + 35) / 25 - t2;
+	unsigned long counter = start;
+
+	while (!ep93xx_pata_check_iordy(base) && counter--)
+		ep93xx_pata_delay(1);
+	return start - counter;
+}
+
+/* common part at start of ep93xx_pata_read/write() */
+static void ep93xx_pata_rw_begin(void __iomem *base, unsigned long addr,
+				 unsigned long t1)
+{
+	writel(IDECTRL_DIOWN | IDECTRL_DIORN | addr, base + IDECTRL);
+	ep93xx_pata_delay(t1);
+}
+
+/* common part at end of ep93xx_pata_read/write() */
+static void ep93xx_pata_rw_end(void __iomem *base, unsigned long addr,
+			       bool iordy, unsigned long t0, unsigned long t2,
+			       unsigned long t2i)
+{
+	ep93xx_pata_delay(t2);
+	/* lengthen t2 if needed */
+	if (iordy)
+		t2 += ep93xx_pata_wait_for_iordy(base, t2);
+	writel(IDECTRL_DIOWN | IDECTRL_DIORN | addr, base + IDECTRL);
+	if (t0 > t2 && t0 - t2 > t2i)
+		ep93xx_pata_delay(t0 - t2);
+	else
+		ep93xx_pata_delay(t2i);
+}
+
+static u16 ep93xx_pata_read(struct ep93xx_pata_data *drv_data,
+			    unsigned long addr,
+			    bool reg)
+{
+	void __iomem *base = drv_data->ide_base;
+	const struct ata_timing *t = &drv_data->t;
+	unsigned long t0 = reg ? t->cyc8b : t->cycle;
+	unsigned long t2 = reg ? t->act8b : t->active;
+	unsigned long t2i = reg ? t->rec8b : t->recover;
+
+	ep93xx_pata_rw_begin(base, addr, t->setup);
+	writel(IDECTRL_DIOWN | addr, base + IDECTRL);
+	/*
+	 * The IDEDATAIN register is loaded from the DD pins at the positive
+	 * edge of the DIORN signal. (EP93xx UG p27-14)
+	 */
+	ep93xx_pata_rw_end(base, addr, drv_data->iordy, t0, t2, t2i);
+	return readl(base + IDEDATAIN);
+}
+
+/* IDE register read */
+static u16 ep93xx_pata_read_reg(struct ep93xx_pata_data *drv_data,
+				unsigned long addr)
+{
+	return ep93xx_pata_read(drv_data, addr, true);
+}
+
+/* PIO data read */
+static u16 ep93xx_pata_read_data(struct ep93xx_pata_data *drv_data,
+				 unsigned long addr)
+{
+	return ep93xx_pata_read(drv_data, addr, false);
+}
+
+static void ep93xx_pata_write(struct ep93xx_pata_data *drv_data,
+			      u16 value, unsigned long addr,
+			      bool reg)
+{
+	void __iomem *base = drv_data->ide_base;
+	const struct ata_timing *t = &drv_data->t;
+	unsigned long t0 = reg ? t->cyc8b : t->cycle;
+	unsigned long t2 = reg ? t->act8b : t->active;
+	unsigned long t2i = reg ? t->rec8b : t->recover;
+
+	ep93xx_pata_rw_begin(base, addr, t->setup);
+	/*
+	 * Value from IDEDATAOUT register is driven onto the DD pins when
+	 * DIOWN is low. (EP93xx UG p27-13)
+	 */
+	writel(value, base + IDEDATAOUT);
+	writel(IDECTRL_DIORN | addr, base + IDECTRL);
+	ep93xx_pata_rw_end(base, addr, drv_data->iordy, t0, t2, t2i);
+}
+
+/* IDE register write */
+static void ep93xx_pata_write_reg(struct ep93xx_pata_data *drv_data,
+				  u16 value, unsigned long addr)
+{
+	ep93xx_pata_write(drv_data, value, addr, true);
+}
+
+/* PIO data write */
+static void ep93xx_pata_write_data(struct ep93xx_pata_data *drv_data,
+				   u16 value, unsigned long addr)
+{
+	ep93xx_pata_write(drv_data, value, addr, false);
+}
+
+static void ep93xx_pata_set_piomode(struct ata_port *ap,
+				    struct ata_device *adev)
+{
+	struct ep93xx_pata_data *drv_data = ap->host->private_data;
+	struct ata_device *pair = ata_dev_pair(adev);
+	/*
+	 * Calculate timings for the delay loop, assuming ep93xx cpu speed
+	 * is 200MHz (maximum possible for ep93xx). If actual cpu speed is
+	 * slower, we will wait a bit longer in each delay.
+	 * Additional division of cpu speed by 5, because single iteration
+	 * of our delay loop takes 5 cpu cycles (25ns).
+	 */
+	unsigned long T = 1000000 / (200 / 5);
+
+	ata_timing_compute(adev, adev->pio_mode, &drv_data->t, T, 0);
+	if (pair && pair->pio_mode) {
+		struct ata_timing t;
+		ata_timing_compute(pair, pair->pio_mode, &t, T, 0);
+		ata_timing_merge(&t, &drv_data->t, &drv_data->t,
+			ATA_TIMING_SETUP | ATA_TIMING_8BIT);
+	}
+	drv_data->iordy = ata_pio_need_iordy(adev);
+
+	ep93xx_pata_enable_pio(drv_data->ide_base,
+			       adev->pio_mode - XFER_PIO_0);
+}
+
+/* Note: original code is ata_sff_check_status */
+static u8 ep93xx_pata_check_status(struct ata_port *ap)
+{
+	struct ep93xx_pata_data *drv_data = ap->host->private_data;
+
+	return ep93xx_pata_read_reg(drv_data, IDECTRL_ADDR_STATUS);
+}
+
+static u8 ep93xx_pata_check_altstatus(struct ata_port *ap)
+{
+	struct ep93xx_pata_data *drv_data = ap->host->private_data;
+
+	return ep93xx_pata_read_reg(drv_data, IDECTRL_ADDR_ALTSTATUS);
+}
+
+/* Note: original code is ata_sff_tf_load */
+static void ep93xx_pata_tf_load(struct ata_port *ap,
+				const struct ata_taskfile *tf)
+{
+	struct ep93xx_pata_data *drv_data = ap->host->private_data;
+	unsigned int is_addr = tf->flags & ATA_TFLAG_ISADDR;
+
+	if (tf->ctl != ap->last_ctl) {
+		ep93xx_pata_write_reg(drv_data, tf->ctl, IDECTRL_ADDR_CTL);
+		ap->last_ctl = tf->ctl;
+		ata_wait_idle(ap);
+	}
+
+	if (is_addr && (tf->flags & ATA_TFLAG_LBA48)) {
+		ep93xx_pata_write_reg(drv_data, tf->hob_feature,
+			IDECTRL_ADDR_FEATURE);
+		ep93xx_pata_write_reg(drv_data, tf->hob_nsect,
+			IDECTRL_ADDR_NSECT);
+		ep93xx_pata_write_reg(drv_data, tf->hob_lbal,
+			IDECTRL_ADDR_LBAL);
+		ep93xx_pata_write_reg(drv_data, tf->hob_lbam,
+			IDECTRL_ADDR_LBAM);
+		ep93xx_pata_write_reg(drv_data, tf->hob_lbah,
+			IDECTRL_ADDR_LBAH);
+	}
+
+	if (is_addr) {
+		ep93xx_pata_write_reg(drv_data, tf->feature,
+			IDECTRL_ADDR_FEATURE);
+		ep93xx_pata_write_reg(drv_data, tf->nsect, IDECTRL_ADDR_NSECT);
+		ep93xx_pata_write_reg(drv_data, tf->lbal, IDECTRL_ADDR_LBAL);
+		ep93xx_pata_write_reg(drv_data, tf->lbam, IDECTRL_ADDR_LBAM);
+		ep93xx_pata_write_reg(drv_data, tf->lbah, IDECTRL_ADDR_LBAH);
+	}
+
+	if (tf->flags & ATA_TFLAG_DEVICE)
+		ep93xx_pata_write_reg(drv_data, tf->device,
+			IDECTRL_ADDR_DEVICE);
+
+	ata_wait_idle(ap);
+}
+
+/* Note: original code is ata_sff_tf_read */
+static void ep93xx_pata_tf_read(struct ata_port *ap, struct ata_taskfile *tf)
+{
+	struct ep93xx_pata_data *drv_data = ap->host->private_data;
+
+	tf->command = ep93xx_pata_check_status(ap);
+	tf->feature = ep93xx_pata_read_reg(drv_data, IDECTRL_ADDR_FEATURE);
+	tf->nsect = ep93xx_pata_read_reg(drv_data, IDECTRL_ADDR_NSECT);
+	tf->lbal = ep93xx_pata_read_reg(drv_data, IDECTRL_ADDR_LBAL);
+	tf->lbam = ep93xx_pata_read_reg(drv_data, IDECTRL_ADDR_LBAM);
+	tf->lbah = ep93xx_pata_read_reg(drv_data, IDECTRL_ADDR_LBAH);
+	tf->device = ep93xx_pata_read_reg(drv_data, IDECTRL_ADDR_DEVICE);
+
+	if (tf->flags & ATA_TFLAG_LBA48) {
+		ep93xx_pata_write_reg(drv_data, tf->ctl | ATA_HOB,
+			IDECTRL_ADDR_CTL);
+		tf->hob_feature = ep93xx_pata_read_reg(drv_data,
+			IDECTRL_ADDR_FEATURE);
+		tf->hob_nsect = ep93xx_pata_read_reg(drv_data,
+			IDECTRL_ADDR_NSECT);
+		tf->hob_lbal = ep93xx_pata_read_reg(drv_data,
+			IDECTRL_ADDR_LBAL);
+		tf->hob_lbam = ep93xx_pata_read_reg(drv_data,
+			IDECTRL_ADDR_LBAM);
+		tf->hob_lbah = ep93xx_pata_read_reg(drv_data,
+			IDECTRL_ADDR_LBAH);
+		ep93xx_pata_write_reg(drv_data, tf->ctl, IDECTRL_ADDR_CTL);
+		ap->last_ctl = tf->ctl;
+	}
+}
+
+/* Note: original code is ata_sff_exec_command */
+static void ep93xx_pata_exec_command(struct ata_port *ap,
+				     const struct ata_taskfile *tf)
+{
+	struct ep93xx_pata_data *drv_data = ap->host->private_data;
+
+	ep93xx_pata_write_reg(drv_data, tf->command,
+			  IDECTRL_ADDR_COMMAND);
+	ata_sff_pause(ap);
+}
+
+/* Note: original code is ata_sff_dev_select */
+static void ep93xx_pata_dev_select(struct ata_port *ap, unsigned int device)
+{
+	struct ep93xx_pata_data *drv_data = ap->host->private_data;
+	u8 tmp = ATA_DEVICE_OBS;
+
+	if (device != 0)
+		tmp |= ATA_DEV1;
+
+	ep93xx_pata_write_reg(drv_data, tmp, IDECTRL_ADDR_DEVICE);
+	ata_sff_pause(ap);	/* needed; also flushes, for mmio */
+}
+
+/* Note: original code is ata_sff_set_devctl */
+static void ep93xx_pata_set_devctl(struct ata_port *ap, u8 ctl)
+{
+	struct ep93xx_pata_data *drv_data = ap->host->private_data;
+
+	ep93xx_pata_write_reg(drv_data, ctl, IDECTRL_ADDR_CTL);
+}
+
+/* Note: original code is ata_sff_data_xfer */
+static unsigned int ep93xx_pata_data_xfer(struct ata_device *adev,
+					  unsigned char *buf,
+					  unsigned int buflen, int rw)
+{
+	struct ata_port *ap = adev->link->ap;
+	struct ep93xx_pata_data *drv_data = ap->host->private_data;
+	u16 *data = (u16 *)buf;
+	unsigned int words = buflen >> 1;
+
+	/* Transfer multiple of 2 bytes */
+	while (words--)
+		if (rw == READ)
+			*data++ = cpu_to_le16(
+				ep93xx_pata_read_data(
+					drv_data, IDECTRL_ADDR_DATA));
+		else
+			ep93xx_pata_write_data(drv_data, le16_to_cpu(*data++),
+				IDECTRL_ADDR_DATA);
+
+	/* Transfer trailing 1 byte, if any. */
+	if (unlikely(buflen & 0x01)) {
+		unsigned char pad[2] = { };
+
+		buf += buflen - 1;
+
+		if (rw == READ) {
+			*pad = cpu_to_le16(
+				ep93xx_pata_read_data(
+					drv_data, IDECTRL_ADDR_DATA));
+			*buf = pad[0];
+		} else {
+			pad[0] = *buf;
+			ep93xx_pata_write_data(drv_data, le16_to_cpu(*pad),
+					  IDECTRL_ADDR_DATA);
+		}
+		words++;
+	}
+
+	return words << 1;
+}
+
+/* Note: original code is ata_devchk */
+static bool ep93xx_pata_device_is_present(struct ata_port *ap,
+					  unsigned int device)
+{
+	struct ep93xx_pata_data *drv_data = ap->host->private_data;
+	u8 nsect, lbal;
+
+	ap->ops->sff_dev_select(ap, device);
+
+	ep93xx_pata_write_reg(drv_data, 0x55, IDECTRL_ADDR_NSECT);
+	ep93xx_pata_write_reg(drv_data, 0xaa, IDECTRL_ADDR_LBAL);
+
+	ep93xx_pata_write_reg(drv_data, 0xaa, IDECTRL_ADDR_NSECT);
+	ep93xx_pata_write_reg(drv_data, 0x55, IDECTRL_ADDR_LBAL);
+
+	ep93xx_pata_write_reg(drv_data, 0x55, IDECTRL_ADDR_NSECT);
+	ep93xx_pata_write_reg(drv_data, 0xaa, IDECTRL_ADDR_LBAL);
+
+	nsect = ep93xx_pata_read_reg(drv_data, IDECTRL_ADDR_NSECT);
+	lbal = ep93xx_pata_read_reg(drv_data, IDECTRL_ADDR_LBAL);
+
+	if ((nsect == 0x55) && (lbal == 0xaa))
+		return true;
+
+	return false;
+}
+
+/* Note: original code is ata_sff_wait_after_reset */
+static int ep93xx_pata_wait_after_reset(struct ata_link *link,
+					unsigned int devmask,
+					unsigned long deadline)
+{
+	struct ata_port *ap = link->ap;
+	struct ep93xx_pata_data *drv_data = ap->host->private_data;
+	unsigned int dev0 = devmask & (1 << 0);
+	unsigned int dev1 = devmask & (1 << 1);
+	int rc, ret = 0;
+
+	ata_msleep(ap, ATA_WAIT_AFTER_RESET);
+
+	/* always check readiness of the master device */
+	rc = ata_sff_wait_ready(link, deadline);
+	/*
+	 * -ENODEV means the odd clown forgot the D7 pulldown resistor
+	 * and TF status is 0xff, bail out on it too.
+	 */
+	if (rc)
+		return rc;
+
+	/*
+	 * if device 1 was found in ata_devchk, wait for register
+	 * access briefly, then wait for BSY to clear.
+	 */
+	if (dev1) {
+		int i;
+
+		ap->ops->sff_dev_select(ap, 1);
+
+		/*
+		 * Wait for register access.  Some ATAPI devices fail
+		 * to set nsect/lbal after reset, so don't waste too
+		 * much time on it.  We're gonna wait for !BSY anyway.
+		 */
+		for (i = 0; i < 2; i++) {
+			u8 nsect, lbal;
+
+			nsect = ep93xx_pata_read_reg(drv_data,
+				IDECTRL_ADDR_NSECT);
+			lbal = ep93xx_pata_read_reg(drv_data,
+				IDECTRL_ADDR_LBAL);
+			if (nsect == 1 && lbal == 1)
+				break;
+			msleep(50);	/* give drive a breather */
+		}
+
+		rc = ata_sff_wait_ready(link, deadline);
+		if (rc) {
+			if (rc != -ENODEV)
+				return rc;
+			ret = rc;
+		}
+	}
+	/* is all this really necessary? */
+	ap->ops->sff_dev_select(ap, 0);
+	if (dev1)
+		ap->ops->sff_dev_select(ap, 1);
+	if (dev0)
+		ap->ops->sff_dev_select(ap, 0);
+
+	return ret;
+}
+
+/* Note: original code is ata_bus_softreset */
+static int ep93xx_pata_bus_softreset(struct ata_port *ap, unsigned int devmask,
+				     unsigned long deadline)
+{
+	struct ep93xx_pata_data *drv_data = ap->host->private_data;
+
+	ep93xx_pata_write_reg(drv_data, ap->ctl, IDECTRL_ADDR_CTL);
+	udelay(20);		/* FIXME: flush */
+	ep93xx_pata_write_reg(drv_data, ap->ctl | ATA_SRST, IDECTRL_ADDR_CTL);
+	udelay(20);		/* FIXME: flush */
+	ep93xx_pata_write_reg(drv_data, ap->ctl, IDECTRL_ADDR_CTL);
+	ap->last_ctl = ap->ctl;
+
+	return ep93xx_pata_wait_after_reset(&ap->link, devmask, deadline);
+}
+
+static void ep93xx_pata_release_dma(struct ep93xx_pata_data *drv_data)
+{
+	if (drv_data->dma_rx_channel) {
+		dma_release_channel(drv_data->dma_rx_channel);
+		drv_data->dma_rx_channel = NULL;
+	}
+	if (drv_data->dma_tx_channel) {
+		dma_release_channel(drv_data->dma_tx_channel);
+		drv_data->dma_tx_channel = NULL;
+	}
+}
+
+static bool ep93xx_pata_dma_filter(struct dma_chan *chan, void *filter_param)
+{
+	if (ep93xx_dma_chan_is_m2p(chan))
+		return false;
+
+	chan->private = filter_param;
+	return true;
+}
+
+static void ep93xx_pata_dma_init(struct ep93xx_pata_data *drv_data)
+{
+	const struct platform_device *pdev = drv_data->pdev;
+	dma_cap_mask_t mask;
+	struct dma_slave_config conf;
+
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_SLAVE, mask);
+
+	/*
+	 * Request two channels for IDE. Another possibility would be
+	 * to request only one channel, and reprogram it's direction at
+	 * start of new transfer.
+	 */
+	drv_data->dma_rx_data.port = EP93XX_DMA_IDE;
+	drv_data->dma_rx_data.direction = DMA_FROM_DEVICE;
+	drv_data->dma_rx_data.name = "ep93xx-pata-rx";
+	drv_data->dma_rx_channel = dma_request_channel(mask,
+		ep93xx_pata_dma_filter, &drv_data->dma_rx_data);
+	if (!drv_data->dma_rx_channel)
+		return;
+
+	drv_data->dma_tx_data.port = EP93XX_DMA_IDE;
+	drv_data->dma_tx_data.direction = DMA_TO_DEVICE;
+	drv_data->dma_tx_data.name = "ep93xx-pata-tx";
+	drv_data->dma_tx_channel = dma_request_channel(mask,
+		ep93xx_pata_dma_filter, &drv_data->dma_tx_data);
+	if (!drv_data->dma_tx_channel) {
+		dma_release_channel(drv_data->dma_rx_channel);
+		return;
+	}
+
+	/* Configure receive channel direction and source address */
+	memset(&conf, 0, sizeof(conf));
+	conf.direction = DMA_FROM_DEVICE;
+	conf.src_addr = drv_data->udma_in_phys;
+	conf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+	if (dmaengine_slave_config(drv_data->dma_rx_channel, &conf)) {
+		dev_err(&pdev->dev, "failed to configure rx dma channel\n");
+		ep93xx_pata_release_dma(drv_data);
+		return;
+	}
+
+	/* Configure transmit channel direction and destination address */
+	memset(&conf, 0, sizeof(conf));
+	conf.direction = DMA_TO_DEVICE;
+	conf.dst_addr = drv_data->udma_out_phys;
+	conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+	if (dmaengine_slave_config(drv_data->dma_tx_channel, &conf)) {
+		dev_err(&pdev->dev, "failed to configure tx dma channel\n");
+		ep93xx_pata_release_dma(drv_data);
+	}
+}
+
+static void ep93xx_pata_dma_start(struct ata_queued_cmd *qc)
+{
+	struct dma_async_tx_descriptor *txd;
+	struct ep93xx_pata_data *drv_data = qc->ap->host->private_data;
+	void __iomem *base = drv_data->ide_base;
+	struct ata_device *adev = qc->dev;
+	u32 v = qc->dma_dir == DMA_TO_DEVICE ? IDEUDMAOP_RWOP : 0;
+	struct dma_chan *channel = qc->dma_dir == DMA_TO_DEVICE
+		? drv_data->dma_tx_channel : drv_data->dma_rx_channel;
+
+	txd = channel->device->device_prep_slave_sg(channel, qc->sg,
+		 qc->n_elem, qc->dma_dir, DMA_CTRL_ACK, NULL);
+	if (!txd) {
+		dev_err(qc->ap->dev, "failed to prepare slave for sg dma\n");
+		return;
+	}
+	txd->callback = NULL;
+	txd->callback_param = NULL;
+
+	if (dmaengine_submit(txd) < 0) {
+		dev_err(qc->ap->dev, "failed to submit dma transfer\n");
+		return;
+	}
+	dma_async_issue_pending(channel);
+
+	/*
+	 * When enabling UDMA operation, IDEUDMAOP register needs to be
+	 * programmed in three step sequence:
+	 * 1) set or clear the RWOP bit,
+	 * 2) perform dummy read of the register,
+	 * 3) set the UEN bit.
+	 */
+	writel(v, base + IDEUDMAOP);
+	readl(base + IDEUDMAOP);
+	writel(v | IDEUDMAOP_UEN, base + IDEUDMAOP);
+
+	writel(IDECFG_IDEEN | IDECFG_UDMA |
+		((adev->xfer_mode - XFER_UDMA_0) << IDECFG_MODE_SHIFT),
+		base + IDECFG);
+}
+
+static void ep93xx_pata_dma_stop(struct ata_queued_cmd *qc)
+{
+	struct ep93xx_pata_data *drv_data = qc->ap->host->private_data;
+	void __iomem *base = drv_data->ide_base;
+
+	/* terminate all dma transfers, if not yet finished */
+	dmaengine_terminate_all(drv_data->dma_rx_channel);
+	dmaengine_terminate_all(drv_data->dma_tx_channel);
+
+	/*
+	 * To properly stop IDE-DMA, IDEUDMAOP register must to be cleared
+	 * and IDECTRL register must be set to default value.
+	 */
+	writel(0, base + IDEUDMAOP);
+	writel(readl(base + IDECTRL) | IDECTRL_DIOWN | IDECTRL_DIORN |
+		IDECTRL_CS0N | IDECTRL_CS1N, base + IDECTRL);
+
+	ep93xx_pata_enable_pio(drv_data->ide_base,
+		qc->dev->pio_mode - XFER_PIO_0);
+
+	ata_sff_dma_pause(qc->ap);
+}
+
+static void ep93xx_pata_dma_setup(struct ata_queued_cmd *qc)
+{
+	qc->ap->ops->sff_exec_command(qc->ap, &qc->tf);
+}
+
+static u8 ep93xx_pata_dma_status(struct ata_port *ap)
+{
+	struct ep93xx_pata_data *drv_data = ap->host->private_data;
+	u32 val = readl(drv_data->ide_base + IDEUDMASTS);
+
+	/*
+	 * UDMA Status Register bits:
+	 *
+	 * DMAIDE - DMA request signal from UDMA state machine,
+	 * INTIDE - INT line generated by UDMA because of errors in the
+	 *          state machine,
+	 * SBUSY - UDMA state machine busy, not in idle state,
+	 * NDO   - error for data-out not completed,
+	 * NDI   - error for data-in not completed,
+	 * N4X   - error for data transferred not multiplies of four
+	 *         32-bit words.
+	 * (EP93xx UG p27-17)
+	 */
+	if (val & IDEUDMASTS_NDO || val & IDEUDMASTS_NDI ||
+	    val & IDEUDMASTS_N4X || val & IDEUDMASTS_INTIDE)
+		return ATA_DMA_ERR;
+
+	/* read INTRQ (INT[3]) pin input state */
+	if (readl(drv_data->ide_base + IDECTRL) & IDECTRL_INTRQ)
+		return ATA_DMA_INTR;
+
+	if (val & IDEUDMASTS_SBUSY || val & IDEUDMASTS_DMAIDE)
+		return ATA_DMA_ACTIVE;
+
+	return 0;
+}
+
+/* Note: original code is ata_sff_softreset */
+static int ep93xx_pata_softreset(struct ata_link *al, unsigned int *classes,
+				 unsigned long deadline)
+{
+	struct ata_port *ap = al->ap;
+	unsigned int slave_possible = ap->flags & ATA_FLAG_SLAVE_POSS;
+	unsigned int devmask = 0;
+	int rc;
+	u8 err;
+
+	/* determine if device 0/1 are present */
+	if (ep93xx_pata_device_is_present(ap, 0))
+		devmask |= (1 << 0);
+	if (slave_possible && ep93xx_pata_device_is_present(ap, 1))
+		devmask |= (1 << 1);
+
+	/* select device 0 again */
+	ap->ops->sff_dev_select(al->ap, 0);
+
+	/* issue bus reset */
+	rc = ep93xx_pata_bus_softreset(ap, devmask, deadline);
+	/* if link is ocuppied, -ENODEV too is an error */
+	if (rc && (rc != -ENODEV || sata_scr_valid(al))) {
+		ata_link_printk(al, KERN_ERR, "SRST failed (errno=%d)\n",
+				rc);
+		return rc;
+	}
+
+	/* determine by signature whether we have ATA or ATAPI devices */
+	classes[0] = ata_sff_dev_classify(&al->device[0], devmask & (1 << 0),
+					  &err);
+	if (slave_possible && err != 0x81)
+		classes[1] = ata_sff_dev_classify(&al->device[1],
+						  devmask & (1 << 1), &err);
+
+	return 0;
+}
+
+/* Note: original code is ata_sff_drain_fifo */
+static void ep93xx_pata_drain_fifo(struct ata_queued_cmd *qc)
+{
+	int count;
+	struct ata_port *ap;
+	struct ep93xx_pata_data *drv_data;
+
+	/* We only need to flush incoming data when a command was running */
+	if (qc == NULL || qc->dma_dir == DMA_TO_DEVICE)
+		return;
+
+	ap = qc->ap;
+	drv_data = ap->host->private_data;
+	/* Drain up to 64K of data before we give up this recovery method */
+	for (count = 0; (ap->ops->sff_check_status(ap) & ATA_DRQ)
+		     && count < 65536; count += 2)
+		ep93xx_pata_read_reg(drv_data, IDECTRL_ADDR_DATA);
+
+	/* Can become DEBUG later */
+	if (count)
+		ata_port_printk(ap, KERN_DEBUG,
+				"drained %d bytes to clear DRQ.\n", count);
+
+}
+
+static int ep93xx_pata_port_start(struct ata_port *ap)
+{
+	struct ep93xx_pata_data *drv_data = ap->host->private_data;
+
+	/*
+	 * Set timings to safe values at startup (= number of ns from ATA
+	 * specification), we'll switch to properly calculated values later.
+	 */
+	drv_data->t = *ata_timing_find_mode(XFER_PIO_0);
+	return 0;
+}
+
+static struct scsi_host_template ep93xx_pata_sht = {
+	ATA_BASE_SHT(DRV_NAME),
+	/* ep93xx dma implementation limit */
+	.sg_tablesize		= 32,
+	/* ep93xx dma can't transfer 65536 bytes at once */
+	.dma_boundary		= 0x7fff,
+};
+
+static struct ata_port_operations ep93xx_pata_port_ops = {
+	.inherits		= &ata_bmdma_port_ops,
+
+	.qc_prep		= ata_noop_qc_prep,
+
+	.softreset		= ep93xx_pata_softreset,
+	.hardreset		= ATA_OP_NULL,
+
+	.sff_dev_select		= ep93xx_pata_dev_select,
+	.sff_set_devctl		= ep93xx_pata_set_devctl,
+	.sff_check_status	= ep93xx_pata_check_status,
+	.sff_check_altstatus	= ep93xx_pata_check_altstatus,
+	.sff_tf_load		= ep93xx_pata_tf_load,
+	.sff_tf_read		= ep93xx_pata_tf_read,
+	.sff_exec_command	= ep93xx_pata_exec_command,
+	.sff_data_xfer		= ep93xx_pata_data_xfer,
+	.sff_drain_fifo		= ep93xx_pata_drain_fifo,
+	.sff_irq_clear		= ATA_OP_NULL,
+
+	.set_piomode		= ep93xx_pata_set_piomode,
+
+	.bmdma_setup		= ep93xx_pata_dma_setup,
+	.bmdma_start		= ep93xx_pata_dma_start,
+	.bmdma_stop		= ep93xx_pata_dma_stop,
+	.bmdma_status		= ep93xx_pata_dma_status,
+
+	.cable_detect		= ata_cable_unknown,
+	.port_start		= ep93xx_pata_port_start,
+};
+
+static int __devinit ep93xx_pata_probe(struct platform_device *pdev)
+{
+	struct ep93xx_pata_data *drv_data;
+	struct ata_host *host;
+	struct ata_port *ap;
+	unsigned int irq;
+	struct resource *mem_res;
+	void __iomem *ide_base;
+	int err;
+
+	err = ep93xx_ide_acquire_gpio(pdev);
+	if (err)
+		return err;
+
+	/* INT[3] (IRQ_EP93XX_EXT3) line connected as pull down */
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		err = -ENXIO;
+		goto err_rel_gpio;
+	}
+
+	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!mem_res) {
+		err = -ENXIO;
+		goto err_rel_gpio;
+	}
+
+	ide_base = devm_request_and_ioremap(&pdev->dev, mem_res);
+	if (!ide_base) {
+		err = -ENXIO;
+		goto err_rel_gpio;
+	}
+
+	drv_data = devm_kzalloc(&pdev->dev, sizeof(*drv_data), GFP_KERNEL);
+	if (!drv_data) {
+		err = -ENXIO;
+		goto err_rel_gpio;
+	}
+
+	platform_set_drvdata(pdev, drv_data);
+	drv_data->pdev = pdev;
+	drv_data->ide_base = ide_base;
+	drv_data->udma_in_phys = mem_res->start + IDEUDMADATAIN;
+	drv_data->udma_out_phys = mem_res->start + IDEUDMADATAOUT;
+	ep93xx_pata_dma_init(drv_data);
+
+	/* allocate host */
+	host = ata_host_alloc(&pdev->dev, 1);
+	if (!host) {
+		err = -ENXIO;
+		goto err_rel_dma;
+	}
+
+	ep93xx_pata_clear_regs(ide_base);
+
+	host->private_data = drv_data;
+
+	ap = host->ports[0];
+	ap->dev = &pdev->dev;
+	ap->ops = &ep93xx_pata_port_ops;
+	ap->flags |= ATA_FLAG_SLAVE_POSS;
+	ap->pio_mask = ATA_PIO4;
+
+	/*
+	 * Maximum UDMA modes:
+	 * EP931x rev.E0 - UDMA2
+	 * EP931x rev.E1 - UDMA3
+	 * EP931x rev.E2 - UDMA4
+	 *
+	 * MWDMA support was removed from EP931x rev.E2,
+	 * so this driver supports only UDMA modes.
+	 */
+	if (drv_data->dma_rx_channel && drv_data->dma_tx_channel) {
+		int chip_rev = ep93xx_chip_revision();
+
+		if (chip_rev == EP93XX_CHIP_REV_E1)
+			ap->udma_mask = ATA_UDMA3;
+		else if (chip_rev == EP93XX_CHIP_REV_E2)
+			ap->udma_mask = ATA_UDMA4;
+		else
+			ap->udma_mask = ATA_UDMA2;
+	}
+
+	/* defaults, pio 0 */
+	ep93xx_pata_enable_pio(ide_base, 0);
+
+	dev_info(&pdev->dev, "version " DRV_VERSION "\n");
+
+	/* activate host */
+	err = ata_host_activate(host, irq, ata_bmdma_interrupt, 0,
+		&ep93xx_pata_sht);
+	if (err == 0)
+		return 0;
+
+err_rel_dma:
+	ep93xx_pata_release_dma(drv_data);
+err_rel_gpio:
+	ep93xx_ide_release_gpio(pdev);
+	return err;
+}
+
+static int __devexit ep93xx_pata_remove(struct platform_device *pdev)
+{
+	struct ata_host *host = platform_get_drvdata(pdev);
+	struct ep93xx_pata_data *drv_data = host->private_data;
+
+	ata_host_detach(host);
+	ep93xx_pata_release_dma(drv_data);
+	ep93xx_pata_clear_regs(drv_data->ide_base);
+	ep93xx_ide_release_gpio(pdev);
+	return 0;
+}
+
+static struct platform_driver ep93xx_pata_platform_driver = {
+	.driver = {
+		.name = DRV_NAME,
+		.owner = THIS_MODULE,
+	},
+	.probe = ep93xx_pata_probe,
+	.remove = __devexit_p(ep93xx_pata_remove),
+};
+
+module_platform_driver(ep93xx_pata_platform_driver);
+
+MODULE_AUTHOR("Alessandro Zummo, Lennert Buytenhek, Joao Ramos, "
+		"Bartlomiej Zolnierkiewicz, Rafal Prylowski");
+MODULE_DESCRIPTION("low-level driver for cirrus ep93xx IDE controller");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+MODULE_ALIAS("platform:pata_ep93xx");
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index 73ce9fb..83aa694 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -11,6 +11,7 @@
 #include <linux/io.h>
 #include <linux/pm_runtime.h>
 #include <linux/pm_domain.h>
+#include <linux/pm_qos.h>
 #include <linux/slab.h>
 #include <linux/err.h>
 #include <linux/sched.h>
@@ -38,11 +39,13 @@
 	ktime_t __start = ktime_get();						\
 	type __retval = GENPD_DEV_CALLBACK(genpd, type, callback, dev);		\
 	s64 __elapsed = ktime_to_ns(ktime_sub(ktime_get(), __start));		\
-	struct generic_pm_domain_data *__gpd_data = dev_gpd_data(dev);		\
-	if (__elapsed > __gpd_data->td.field) {					\
-		__gpd_data->td.field = __elapsed;				\
+	struct gpd_timing_data *__td = &dev_gpd_data(dev)->td;			\
+	if (!__retval && __elapsed > __td->field) {				\
+		__td->field = __elapsed;					\
 		dev_warn(dev, name " latency exceeded, new value %lld ns\n",	\
 			__elapsed);						\
+		genpd->max_off_time_changed = true;				\
+		__td->constraint_changed = true;				\
 	}									\
 	__retval;								\
 })
@@ -211,6 +214,7 @@
 		elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
 		if (elapsed_ns > genpd->power_on_latency_ns) {
 			genpd->power_on_latency_ns = elapsed_ns;
+			genpd->max_off_time_changed = true;
 			if (genpd->name)
 				pr_warning("%s: Power-on latency exceeded, "
 					"new value %lld ns\n", genpd->name,
@@ -247,6 +251,53 @@
 
 #ifdef CONFIG_PM_RUNTIME
 
+static int genpd_dev_pm_qos_notifier(struct notifier_block *nb,
+				     unsigned long val, void *ptr)
+{
+	struct generic_pm_domain_data *gpd_data;
+	struct device *dev;
+
+	gpd_data = container_of(nb, struct generic_pm_domain_data, nb);
+
+	mutex_lock(&gpd_data->lock);
+	dev = gpd_data->base.dev;
+	if (!dev) {
+		mutex_unlock(&gpd_data->lock);
+		return NOTIFY_DONE;
+	}
+	mutex_unlock(&gpd_data->lock);
+
+	for (;;) {
+		struct generic_pm_domain *genpd;
+		struct pm_domain_data *pdd;
+
+		spin_lock_irq(&dev->power.lock);
+
+		pdd = dev->power.subsys_data ?
+				dev->power.subsys_data->domain_data : NULL;
+		if (pdd) {
+			to_gpd_data(pdd)->td.constraint_changed = true;
+			genpd = dev_to_genpd(dev);
+		} else {
+			genpd = ERR_PTR(-ENODATA);
+		}
+
+		spin_unlock_irq(&dev->power.lock);
+
+		if (!IS_ERR(genpd)) {
+			mutex_lock(&genpd->lock);
+			genpd->max_off_time_changed = true;
+			mutex_unlock(&genpd->lock);
+		}
+
+		dev = dev->parent;
+		if (!dev || dev->power.ignore_children)
+			break;
+	}
+
+	return NOTIFY_DONE;
+}
+
 /**
  * __pm_genpd_save_device - Save the pre-suspend state of a device.
  * @pdd: Domain data of the device to save the state of.
@@ -435,6 +486,7 @@
 		elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
 		if (elapsed_ns > genpd->power_off_latency_ns) {
 			genpd->power_off_latency_ns = elapsed_ns;
+			genpd->max_off_time_changed = true;
 			if (genpd->name)
 				pr_warning("%s: Power-off latency exceeded, "
 					"new value %lld ns\n", genpd->name,
@@ -443,17 +495,6 @@
 	}
 
 	genpd->status = GPD_STATE_POWER_OFF;
-	genpd->power_off_time = ktime_get();
-
-	/* Update PM QoS information for devices in the domain. */
-	list_for_each_entry_reverse(pdd, &genpd->dev_list, list_node) {
-		struct gpd_timing_data *td = &to_gpd_data(pdd)->td;
-
-		pm_runtime_update_max_time_suspended(pdd->dev,
-					td->start_latency_ns +
-					td->restore_state_latency_ns +
-					genpd->power_on_latency_ns);
-	}
 
 	list_for_each_entry(link, &genpd->slave_links, slave_node) {
 		genpd_sd_counter_dec(link->master);
@@ -514,9 +555,6 @@
 	if (ret)
 		return ret;
 
-	pm_runtime_update_max_time_suspended(dev,
-				dev_gpd_data(dev)->td.start_latency_ns);
-
 	/*
 	 * If power.irq_safe is set, this routine will be run with interrupts
 	 * off, so it can't use mutexes.
@@ -613,6 +651,12 @@
 
 #else
 
+static inline int genpd_dev_pm_qos_notifier(struct notifier_block *nb,
+					    unsigned long val, void *ptr)
+{
+	return NOTIFY_DONE;
+}
+
 static inline void genpd_power_off_work_fn(struct work_struct *work) {}
 
 #define pm_genpd_runtime_suspend	NULL
@@ -1209,12 +1253,15 @@
 	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
 		return -EINVAL;
 
-	genpd_acquire_lock(genpd);
+	gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL);
+	if (!gpd_data)
+		return -ENOMEM;
 
-	if (genpd->status == GPD_STATE_POWER_OFF) {
-		ret = -EINVAL;
-		goto out;
-	}
+	mutex_init(&gpd_data->lock);
+	gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier;
+	dev_pm_qos_add_notifier(dev, &gpd_data->nb);
+
+	genpd_acquire_lock(genpd);
 
 	if (genpd->prepared_count > 0) {
 		ret = -EAGAIN;
@@ -1227,26 +1274,35 @@
 			goto out;
 		}
 
-	gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL);
-	if (!gpd_data) {
-		ret = -ENOMEM;
-		goto out;
-	}
-
 	genpd->device_count++;
+	genpd->max_off_time_changed = true;
 
-	dev->pm_domain = &genpd->domain;
 	dev_pm_get_subsys_data(dev);
+
+	mutex_lock(&gpd_data->lock);
+	spin_lock_irq(&dev->power.lock);
+	dev->pm_domain = &genpd->domain;
 	dev->power.subsys_data->domain_data = &gpd_data->base;
 	gpd_data->base.dev = dev;
-	gpd_data->need_restore = false;
 	list_add_tail(&gpd_data->base.list_node, &genpd->dev_list);
+	gpd_data->need_restore = genpd->status == GPD_STATE_POWER_OFF;
 	if (td)
 		gpd_data->td = *td;
 
+	gpd_data->td.constraint_changed = true;
+	gpd_data->td.effective_constraint_ns = -1;
+	spin_unlock_irq(&dev->power.lock);
+	mutex_unlock(&gpd_data->lock);
+
+	genpd_release_lock(genpd);
+
+	return 0;
+
  out:
 	genpd_release_lock(genpd);
 
+	dev_pm_qos_remove_notifier(dev, &gpd_data->nb);
+	kfree(gpd_data);
 	return ret;
 }
 
@@ -1290,12 +1346,15 @@
 int pm_genpd_remove_device(struct generic_pm_domain *genpd,
 			   struct device *dev)
 {
+	struct generic_pm_domain_data *gpd_data;
 	struct pm_domain_data *pdd;
-	int ret = -EINVAL;
+	int ret = 0;
 
 	dev_dbg(dev, "%s()\n", __func__);
 
-	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)
+	    ||  IS_ERR_OR_NULL(dev->pm_domain)
+	    ||  pd_to_genpd(dev->pm_domain) != genpd)
 		return -EINVAL;
 
 	genpd_acquire_lock(genpd);
@@ -1305,21 +1364,27 @@
 		goto out;
 	}
 
-	list_for_each_entry(pdd, &genpd->dev_list, list_node) {
-		if (pdd->dev != dev)
-			continue;
+	genpd->device_count--;
+	genpd->max_off_time_changed = true;
 
-		list_del_init(&pdd->list_node);
-		pdd->dev = NULL;
-		dev_pm_put_subsys_data(dev);
-		dev->pm_domain = NULL;
-		kfree(to_gpd_data(pdd));
+	spin_lock_irq(&dev->power.lock);
+	dev->pm_domain = NULL;
+	pdd = dev->power.subsys_data->domain_data;
+	list_del_init(&pdd->list_node);
+	dev->power.subsys_data->domain_data = NULL;
+	spin_unlock_irq(&dev->power.lock);
 
-		genpd->device_count--;
+	gpd_data = to_gpd_data(pdd);
+	mutex_lock(&gpd_data->lock);
+	pdd->dev = NULL;
+	mutex_unlock(&gpd_data->lock);
 
-		ret = 0;
-		break;
-	}
+	genpd_release_lock(genpd);
+
+	dev_pm_qos_remove_notifier(dev, &gpd_data->nb);
+	kfree(gpd_data);
+	dev_pm_put_subsys_data(dev);
+	return 0;
 
  out:
 	genpd_release_lock(genpd);
@@ -1348,6 +1413,26 @@
 EXPORT_SYMBOL_GPL(pm_genpd_dev_always_on);
 
 /**
+ * pm_genpd_dev_need_restore - Set/unset the device's "need restore" flag.
+ * @dev: Device to set/unset the flag for.
+ * @val: The new value of the device's "need restore" flag.
+ */
+void pm_genpd_dev_need_restore(struct device *dev, bool val)
+{
+	struct pm_subsys_data *psd;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->power.lock, flags);
+
+	psd = dev_to_psd(dev);
+	if (psd && psd->domain_data)
+		to_gpd_data(psd->domain_data)->need_restore = val;
+
+	spin_unlock_irqrestore(&dev->power.lock, flags);
+}
+EXPORT_SYMBOL_GPL(pm_genpd_dev_need_restore);
+
+/**
  * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
  * @genpd: Master PM domain to add the subdomain to.
  * @subdomain: Subdomain to be added.
@@ -1378,7 +1463,7 @@
 		goto out;
 	}
 
-	list_for_each_entry(link, &genpd->slave_links, slave_node) {
+	list_for_each_entry(link, &genpd->master_links, master_node) {
 		if (link->slave == subdomain && link->master == genpd) {
 			ret = -EINVAL;
 			goto out;
@@ -1690,6 +1775,7 @@
 	genpd->resume_count = 0;
 	genpd->device_count = 0;
 	genpd->max_off_time_ns = -1;
+	genpd->max_off_time_changed = true;
 	genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
 	genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
 	genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
diff --git a/drivers/base/power/domain_governor.c b/drivers/base/power/domain_governor.c
index 66a265b..28dee30 100644
--- a/drivers/base/power/domain_governor.c
+++ b/drivers/base/power/domain_governor.c
@@ -14,6 +14,31 @@
 
 #ifdef CONFIG_PM_RUNTIME
 
+static int dev_update_qos_constraint(struct device *dev, void *data)
+{
+	s64 *constraint_ns_p = data;
+	s32 constraint_ns = -1;
+
+	if (dev->power.subsys_data && dev->power.subsys_data->domain_data)
+		constraint_ns = dev_gpd_data(dev)->td.effective_constraint_ns;
+
+	if (constraint_ns < 0) {
+		constraint_ns = dev_pm_qos_read_value(dev);
+		constraint_ns *= NSEC_PER_USEC;
+	}
+	if (constraint_ns == 0)
+		return 0;
+
+	/*
+	 * constraint_ns cannot be negative here, because the device has been
+	 * suspended.
+	 */
+	if (constraint_ns < *constraint_ns_p || *constraint_ns_p == 0)
+		*constraint_ns_p = constraint_ns;
+
+	return 0;
+}
+
 /**
  * default_stop_ok - Default PM domain governor routine for stopping devices.
  * @dev: Device to check.
@@ -21,14 +46,52 @@
 bool default_stop_ok(struct device *dev)
 {
 	struct gpd_timing_data *td = &dev_gpd_data(dev)->td;
+	unsigned long flags;
+	s64 constraint_ns;
 
 	dev_dbg(dev, "%s()\n", __func__);
 
-	if (dev->power.max_time_suspended_ns < 0 || td->break_even_ns == 0)
-		return true;
+	spin_lock_irqsave(&dev->power.lock, flags);
 
-	return td->stop_latency_ns + td->start_latency_ns < td->break_even_ns
-		&& td->break_even_ns < dev->power.max_time_suspended_ns;
+	if (!td->constraint_changed) {
+		bool ret = td->cached_stop_ok;
+
+		spin_unlock_irqrestore(&dev->power.lock, flags);
+		return ret;
+	}
+	td->constraint_changed = false;
+	td->cached_stop_ok = false;
+	td->effective_constraint_ns = -1;
+	constraint_ns = __dev_pm_qos_read_value(dev);
+
+	spin_unlock_irqrestore(&dev->power.lock, flags);
+
+	if (constraint_ns < 0)
+		return false;
+
+	constraint_ns *= NSEC_PER_USEC;
+	/*
+	 * We can walk the children without any additional locking, because
+	 * they all have been suspended at this point and their
+	 * effective_constraint_ns fields won't be modified in parallel with us.
+	 */
+	if (!dev->power.ignore_children)
+		device_for_each_child(dev, &constraint_ns,
+				      dev_update_qos_constraint);
+
+	if (constraint_ns > 0) {
+		constraint_ns -= td->start_latency_ns;
+		if (constraint_ns == 0)
+			return false;
+	}
+	td->effective_constraint_ns = constraint_ns;
+	td->cached_stop_ok = constraint_ns > td->stop_latency_ns ||
+				constraint_ns == 0;
+	/*
+	 * The children have been suspended already, so we don't need to take
+	 * their stop latencies into account here.
+	 */
+	return td->cached_stop_ok;
 }
 
 /**
@@ -42,9 +105,27 @@
 	struct generic_pm_domain *genpd = pd_to_genpd(pd);
 	struct gpd_link *link;
 	struct pm_domain_data *pdd;
-	s64 min_dev_off_time_ns;
+	s64 min_off_time_ns;
 	s64 off_on_time_ns;
-	ktime_t time_now = ktime_get();
+
+	if (genpd->max_off_time_changed) {
+		struct gpd_link *link;
+
+		/*
+		 * We have to invalidate the cached results for the masters, so
+		 * use the observation that default_power_down_ok() is not
+		 * going to be called for any master until this instance
+		 * returns.
+		 */
+		list_for_each_entry(link, &genpd->slave_links, slave_node)
+			link->master->max_off_time_changed = true;
+
+		genpd->max_off_time_changed = false;
+		genpd->cached_power_down_ok = false;
+		genpd->max_off_time_ns = -1;
+	} else {
+		return genpd->cached_power_down_ok;
+	}
 
 	off_on_time_ns = genpd->power_off_latency_ns +
 				genpd->power_on_latency_ns;
@@ -61,6 +142,7 @@
 				to_gpd_data(pdd)->td.save_state_latency_ns;
 	}
 
+	min_off_time_ns = -1;
 	/*
 	 * Check if subdomains can be off for enough time.
 	 *
@@ -73,8 +155,6 @@
 		if (sd_max_off_ns < 0)
 			continue;
 
-		sd_max_off_ns -= ktime_to_ns(ktime_sub(time_now,
-						       sd->power_off_time));
 		/*
 		 * Check if the subdomain is allowed to be off long enough for
 		 * the current domain to turn off and on (that's how much time
@@ -82,60 +162,64 @@
 		 */
 		if (sd_max_off_ns <= off_on_time_ns)
 			return false;
+
+		if (min_off_time_ns > sd_max_off_ns || min_off_time_ns < 0)
+			min_off_time_ns = sd_max_off_ns;
 	}
 
 	/*
 	 * Check if the devices in the domain can be off enough time.
 	 */
-	min_dev_off_time_ns = -1;
 	list_for_each_entry(pdd, &genpd->dev_list, list_node) {
 		struct gpd_timing_data *td;
-		struct device *dev = pdd->dev;
-		s64 dev_off_time_ns;
+		s64 constraint_ns;
 
-		if (!dev->driver || dev->power.max_time_suspended_ns < 0)
+		if (!pdd->dev->driver)
 			continue;
 
+		/*
+		 * Check if the device is allowed to be off long enough for the
+		 * domain to turn off and on (that's how much time it will
+		 * have to wait worst case).
+		 */
 		td = &to_gpd_data(pdd)->td;
-		dev_off_time_ns = dev->power.max_time_suspended_ns -
-			(td->start_latency_ns + td->restore_state_latency_ns +
-				ktime_to_ns(ktime_sub(time_now,
-						dev->power.suspend_time)));
-		if (dev_off_time_ns <= off_on_time_ns)
+		constraint_ns = td->effective_constraint_ns;
+		/* default_stop_ok() need not be called before us. */
+		if (constraint_ns < 0) {
+			constraint_ns = dev_pm_qos_read_value(pdd->dev);
+			constraint_ns *= NSEC_PER_USEC;
+		}
+		if (constraint_ns == 0)
+			continue;
+
+		/*
+		 * constraint_ns cannot be negative here, because the device has
+		 * been suspended.
+		 */
+		constraint_ns -= td->restore_state_latency_ns;
+		if (constraint_ns <= off_on_time_ns)
 			return false;
 
-		if (min_dev_off_time_ns > dev_off_time_ns
-		    || min_dev_off_time_ns < 0)
-			min_dev_off_time_ns = dev_off_time_ns;
+		if (min_off_time_ns > constraint_ns || min_off_time_ns < 0)
+			min_off_time_ns = constraint_ns;
 	}
 
-	if (min_dev_off_time_ns < 0) {
-		/*
-		 * There are no latency constraints, so the domain can spend
-		 * arbitrary time in the "off" state.
-		 */
-		genpd->max_off_time_ns = -1;
+	genpd->cached_power_down_ok = true;
+
+	/*
+	 * If the computed minimum device off time is negative, there are no
+	 * latency constraints, so the domain can spend arbitrary time in the
+	 * "off" state.
+	 */
+	if (min_off_time_ns < 0)
 		return true;
-	}
 
 	/*
-	 * The difference between the computed minimum delta and the time needed
-	 * to turn the domain on is the maximum theoretical time this domain can
-	 * spend in the "off" state.
+	 * The difference between the computed minimum subdomain or device off
+	 * time and the time needed to turn the domain on is the maximum
+	 * theoretical time this domain can spend in the "off" state.
 	 */
-	min_dev_off_time_ns -= genpd->power_on_latency_ns;
-
-	/*
-	 * If the difference between the computed minimum delta and the time
-	 * needed to turn the domain off and back on on is smaller than the
-	 * domain's power break even time, removing power from the domain is not
-	 * worth it.
-	 */
-	if (genpd->break_even_ns >
-	    min_dev_off_time_ns - genpd->power_off_latency_ns)
-		return false;
-
-	genpd->max_off_time_ns = min_dev_off_time_ns;
+	genpd->max_off_time_ns = min_off_time_ns - genpd->power_on_latency_ns;
 	return true;
 }
 
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index b462c0e..e0fb5b0 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -889,6 +889,11 @@
 		if (!list_empty(&dev->power.entry))
 			list_move(&dev->power.entry, &dpm_noirq_list);
 		put_device(dev);
+
+		if (pm_wakeup_pending()) {
+			error = -EBUSY;
+			break;
+		}
 	}
 	mutex_unlock(&dpm_list_mtx);
 	if (error)
@@ -962,6 +967,11 @@
 		if (!list_empty(&dev->power.entry))
 			list_move(&dev->power.entry, &dpm_late_early_list);
 		put_device(dev);
+
+		if (pm_wakeup_pending()) {
+			error = -EBUSY;
+			break;
+		}
 	}
 	mutex_unlock(&dpm_list_mtx);
 	if (error)
diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c
index 7185557..fd849a2 100644
--- a/drivers/base/power/qos.c
+++ b/drivers/base/power/qos.c
@@ -352,21 +352,26 @@
  *
  * Will register the notifier into a notification chain that gets called
  * upon changes to the target value for the device.
+ *
+ * If the device's constraints object doesn't exist when this routine is called,
+ * it will be created (or error code will be returned if that fails).
  */
 int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier)
 {
-	int retval = 0;
+	int ret = 0;
 
 	mutex_lock(&dev_pm_qos_mtx);
 
-	/* Silently return if the constraints object is not present. */
-	if (dev->power.constraints)
-		retval = blocking_notifier_chain_register(
-				dev->power.constraints->notifiers,
-				notifier);
+	if (!dev->power.constraints)
+		ret = dev->power.power_state.event != PM_EVENT_INVALID ?
+			dev_pm_qos_constraints_allocate(dev) : -ENODEV;
+
+	if (!ret)
+		ret = blocking_notifier_chain_register(
+				dev->power.constraints->notifiers, notifier);
 
 	mutex_unlock(&dev_pm_qos_mtx);
-	return retval;
+	return ret;
 }
 EXPORT_SYMBOL_GPL(dev_pm_qos_add_notifier);
 
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
index bd0f394..5989487 100644
--- a/drivers/base/power/runtime.c
+++ b/drivers/base/power/runtime.c
@@ -282,47 +282,6 @@
 	return retval != -EACCES ? retval : -EIO;
 }
 
-struct rpm_qos_data {
-	ktime_t time_now;
-	s64 constraint_ns;
-};
-
-/**
- * rpm_update_qos_constraint - Update a given PM QoS constraint data.
- * @dev: Device whose timing data to use.
- * @data: PM QoS constraint data to update.
- *
- * Use the suspend timing data of @dev to update PM QoS constraint data pointed
- * to by @data.
- */
-static int rpm_update_qos_constraint(struct device *dev, void *data)
-{
-	struct rpm_qos_data *qos = data;
-	unsigned long flags;
-	s64 delta_ns;
-	int ret = 0;
-
-	spin_lock_irqsave(&dev->power.lock, flags);
-
-	if (dev->power.max_time_suspended_ns < 0)
-		goto out;
-
-	delta_ns = dev->power.max_time_suspended_ns -
-		ktime_to_ns(ktime_sub(qos->time_now, dev->power.suspend_time));
-	if (delta_ns <= 0) {
-		ret = -EBUSY;
-		goto out;
-	}
-
-	if (qos->constraint_ns > delta_ns || qos->constraint_ns == 0)
-		qos->constraint_ns = delta_ns;
-
- out:
-	spin_unlock_irqrestore(&dev->power.lock, flags);
-
-	return ret;
-}
-
 /**
  * rpm_suspend - Carry out runtime suspend of given device.
  * @dev: Device to suspend.
@@ -349,7 +308,6 @@
 {
 	int (*callback)(struct device *);
 	struct device *parent = NULL;
-	struct rpm_qos_data qos;
 	int retval;
 
 	trace_rpm_suspend(dev, rpmflags);
@@ -445,38 +403,14 @@
 		goto out;
 	}
 
-	qos.constraint_ns = __dev_pm_qos_read_value(dev);
-	if (qos.constraint_ns < 0) {
-		/* Negative constraint means "never suspend". */
+	if (__dev_pm_qos_read_value(dev) < 0) {
+		/* Negative PM QoS constraint means "never suspend". */
 		retval = -EPERM;
 		goto out;
 	}
-	qos.constraint_ns *= NSEC_PER_USEC;
-	qos.time_now = ktime_get();
 
 	__update_runtime_status(dev, RPM_SUSPENDING);
 
-	if (!dev->power.ignore_children) {
-		if (dev->power.irq_safe)
-			spin_unlock(&dev->power.lock);
-		else
-			spin_unlock_irq(&dev->power.lock);
-
-		retval = device_for_each_child(dev, &qos,
-					       rpm_update_qos_constraint);
-
-		if (dev->power.irq_safe)
-			spin_lock(&dev->power.lock);
-		else
-			spin_lock_irq(&dev->power.lock);
-
-		if (retval)
-			goto fail;
-	}
-
-	dev->power.suspend_time = qos.time_now;
-	dev->power.max_time_suspended_ns = qos.constraint_ns ? : -1;
-
 	if (dev->pm_domain)
 		callback = dev->pm_domain->ops.runtime_suspend;
 	else if (dev->type && dev->type->pm)
@@ -529,8 +463,6 @@
 
  fail:
 	__update_runtime_status(dev, RPM_ACTIVE);
-	dev->power.suspend_time = ktime_set(0, 0);
-	dev->power.max_time_suspended_ns = -1;
 	dev->power.deferred_resume = false;
 	wake_up_all(&dev->power.wait_queue);
 
@@ -704,9 +636,6 @@
 	if (dev->power.no_callbacks)
 		goto no_callback;	/* Assume success. */
 
-	dev->power.suspend_time = ktime_set(0, 0);
-	dev->power.max_time_suspended_ns = -1;
-
 	__update_runtime_status(dev, RPM_RESUMING);
 
 	if (dev->pm_domain)
@@ -1369,9 +1298,6 @@
 	setup_timer(&dev->power.suspend_timer, pm_suspend_timer_fn,
 			(unsigned long)dev);
 
-	dev->power.suspend_time = ktime_set(0, 0);
-	dev->power.max_time_suspended_ns = -1;
-
 	init_waitqueue_head(&dev->power.wait_queue);
 }
 
@@ -1389,28 +1315,3 @@
 	if (dev->power.irq_safe && dev->parent)
 		pm_runtime_put_sync(dev->parent);
 }
-
-/**
- * pm_runtime_update_max_time_suspended - Update device's suspend time data.
- * @dev: Device to handle.
- * @delta_ns: Value to subtract from the device's max_time_suspended_ns field.
- *
- * Update the device's power.max_time_suspended_ns field by subtracting
- * @delta_ns from it.  The resulting value of power.max_time_suspended_ns is
- * never negative.
- */
-void pm_runtime_update_max_time_suspended(struct device *dev, s64 delta_ns)
-{
-	unsigned long flags;
-
-	spin_lock_irqsave(&dev->power.lock, flags);
-
-	if (delta_ns > 0 && dev->power.max_time_suspended_ns > 0) {
-		if (dev->power.max_time_suspended_ns > delta_ns)
-			dev->power.max_time_suspended_ns -= delta_ns;
-		else
-			dev->power.max_time_suspended_ns = 0;
-	}
-
-	spin_unlock_irqrestore(&dev->power.lock, flags);
-}
diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c
index 95c12f6..48be2ad 100644
--- a/drivers/base/power/sysfs.c
+++ b/drivers/base/power/sysfs.c
@@ -314,22 +314,41 @@
 
 static DEVICE_ATTR(wakeup_active_count, 0444, wakeup_active_count_show, NULL);
 
-static ssize_t wakeup_hit_count_show(struct device *dev,
-				struct device_attribute *attr, char *buf)
+static ssize_t wakeup_abort_count_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
 {
 	unsigned long count = 0;
 	bool enabled = false;
 
 	spin_lock_irq(&dev->power.lock);
 	if (dev->power.wakeup) {
-		count = dev->power.wakeup->hit_count;
+		count = dev->power.wakeup->wakeup_count;
 		enabled = true;
 	}
 	spin_unlock_irq(&dev->power.lock);
 	return enabled ? sprintf(buf, "%lu\n", count) : sprintf(buf, "\n");
 }
 
-static DEVICE_ATTR(wakeup_hit_count, 0444, wakeup_hit_count_show, NULL);
+static DEVICE_ATTR(wakeup_abort_count, 0444, wakeup_abort_count_show, NULL);
+
+static ssize_t wakeup_expire_count_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	unsigned long count = 0;
+	bool enabled = false;
+
+	spin_lock_irq(&dev->power.lock);
+	if (dev->power.wakeup) {
+		count = dev->power.wakeup->expire_count;
+		enabled = true;
+	}
+	spin_unlock_irq(&dev->power.lock);
+	return enabled ? sprintf(buf, "%lu\n", count) : sprintf(buf, "\n");
+}
+
+static DEVICE_ATTR(wakeup_expire_count, 0444, wakeup_expire_count_show, NULL);
 
 static ssize_t wakeup_active_show(struct device *dev,
 				struct device_attribute *attr, char *buf)
@@ -398,6 +417,27 @@
 }
 
 static DEVICE_ATTR(wakeup_last_time_ms, 0444, wakeup_last_time_show, NULL);
+
+#ifdef CONFIG_PM_AUTOSLEEP
+static ssize_t wakeup_prevent_sleep_time_show(struct device *dev,
+					      struct device_attribute *attr,
+					      char *buf)
+{
+	s64 msec = 0;
+	bool enabled = false;
+
+	spin_lock_irq(&dev->power.lock);
+	if (dev->power.wakeup) {
+		msec = ktime_to_ms(dev->power.wakeup->prevent_sleep_time);
+		enabled = true;
+	}
+	spin_unlock_irq(&dev->power.lock);
+	return enabled ? sprintf(buf, "%lld\n", msec) : sprintf(buf, "\n");
+}
+
+static DEVICE_ATTR(wakeup_prevent_sleep_time_ms, 0444,
+		   wakeup_prevent_sleep_time_show, NULL);
+#endif /* CONFIG_PM_AUTOSLEEP */
 #endif /* CONFIG_PM_SLEEP */
 
 #ifdef CONFIG_PM_ADVANCED_DEBUG
@@ -486,11 +526,15 @@
 	&dev_attr_wakeup.attr,
 	&dev_attr_wakeup_count.attr,
 	&dev_attr_wakeup_active_count.attr,
-	&dev_attr_wakeup_hit_count.attr,
+	&dev_attr_wakeup_abort_count.attr,
+	&dev_attr_wakeup_expire_count.attr,
 	&dev_attr_wakeup_active.attr,
 	&dev_attr_wakeup_total_time_ms.attr,
 	&dev_attr_wakeup_max_time_ms.attr,
 	&dev_attr_wakeup_last_time_ms.attr,
+#ifdef CONFIG_PM_AUTOSLEEP
+	&dev_attr_wakeup_prevent_sleep_time_ms.attr,
+#endif
 #endif
 	NULL,
 };
diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c
index 2a3e581..cbb463b 100644
--- a/drivers/base/power/wakeup.c
+++ b/drivers/base/power/wakeup.c
@@ -14,16 +14,15 @@
 #include <linux/suspend.h>
 #include <linux/seq_file.h>
 #include <linux/debugfs.h>
+#include <trace/events/power.h>
 
 #include "power.h"
 
-#define TIMEOUT		100
-
 /*
  * If set, the suspend/hibernate code will abort transitions to a sleep state
  * if wakeup events are registered during or immediately before the transition.
  */
-bool events_check_enabled;
+bool events_check_enabled __read_mostly;
 
 /*
  * Combined counters of registered wakeup events and wakeup events in progress.
@@ -52,6 +51,8 @@
 
 static LIST_HEAD(wakeup_sources);
 
+static DECLARE_WAIT_QUEUE_HEAD(wakeup_count_wait_queue);
+
 /**
  * wakeup_source_prepare - Prepare a new wakeup source for initialization.
  * @ws: Wakeup source to prepare.
@@ -132,6 +133,7 @@
 	spin_lock_init(&ws->lock);
 	setup_timer(&ws->timer, pm_wakeup_timer_fn, (unsigned long)ws);
 	ws->active = false;
+	ws->last_time = ktime_get();
 
 	spin_lock_irq(&events_lock);
 	list_add_rcu(&ws->entry, &wakeup_sources);
@@ -374,12 +376,33 @@
  */
 static void wakeup_source_activate(struct wakeup_source *ws)
 {
+	unsigned int cec;
+
 	ws->active = true;
 	ws->active_count++;
 	ws->last_time = ktime_get();
+	if (ws->autosleep_enabled)
+		ws->start_prevent_time = ws->last_time;
 
 	/* Increment the counter of events in progress. */
-	atomic_inc(&combined_event_count);
+	cec = atomic_inc_return(&combined_event_count);
+
+	trace_wakeup_source_activate(ws->name, cec);
+}
+
+/**
+ * wakeup_source_report_event - Report wakeup event using the given source.
+ * @ws: Wakeup source to report the event for.
+ */
+static void wakeup_source_report_event(struct wakeup_source *ws)
+{
+	ws->event_count++;
+	/* This is racy, but the counter is approximate anyway. */
+	if (events_check_enabled)
+		ws->wakeup_count++;
+
+	if (!ws->active)
+		wakeup_source_activate(ws);
 }
 
 /**
@@ -397,10 +420,7 @@
 
 	spin_lock_irqsave(&ws->lock, flags);
 
-	ws->event_count++;
-	if (!ws->active)
-		wakeup_source_activate(ws);
-
+	wakeup_source_report_event(ws);
 	del_timer(&ws->timer);
 	ws->timer_expires = 0;
 
@@ -432,6 +452,17 @@
 }
 EXPORT_SYMBOL_GPL(pm_stay_awake);
 
+#ifdef CONFIG_PM_AUTOSLEEP
+static void update_prevent_sleep_time(struct wakeup_source *ws, ktime_t now)
+{
+	ktime_t delta = ktime_sub(now, ws->start_prevent_time);
+	ws->prevent_sleep_time = ktime_add(ws->prevent_sleep_time, delta);
+}
+#else
+static inline void update_prevent_sleep_time(struct wakeup_source *ws,
+					     ktime_t now) {}
+#endif
+
 /**
  * wakup_source_deactivate - Mark given wakeup source as inactive.
  * @ws: Wakeup source to handle.
@@ -442,6 +473,7 @@
  */
 static void wakeup_source_deactivate(struct wakeup_source *ws)
 {
+	unsigned int cnt, inpr, cec;
 	ktime_t duration;
 	ktime_t now;
 
@@ -468,14 +500,23 @@
 	if (ktime_to_ns(duration) > ktime_to_ns(ws->max_time))
 		ws->max_time = duration;
 
+	ws->last_time = now;
 	del_timer(&ws->timer);
 	ws->timer_expires = 0;
 
+	if (ws->autosleep_enabled)
+		update_prevent_sleep_time(ws, now);
+
 	/*
 	 * Increment the counter of registered wakeup events and decrement the
 	 * couter of wakeup events in progress simultaneously.
 	 */
-	atomic_add(MAX_IN_PROGRESS, &combined_event_count);
+	cec = atomic_add_return(MAX_IN_PROGRESS, &combined_event_count);
+	trace_wakeup_source_deactivate(ws->name, cec);
+
+	split_counters(&cnt, &inpr);
+	if (!inpr && waitqueue_active(&wakeup_count_wait_queue))
+		wake_up(&wakeup_count_wait_queue);
 }
 
 /**
@@ -536,8 +577,10 @@
 	spin_lock_irqsave(&ws->lock, flags);
 
 	if (ws->active && ws->timer_expires
-	    && time_after_eq(jiffies, ws->timer_expires))
+	    && time_after_eq(jiffies, ws->timer_expires)) {
 		wakeup_source_deactivate(ws);
+		ws->expire_count++;
+	}
 
 	spin_unlock_irqrestore(&ws->lock, flags);
 }
@@ -564,9 +607,7 @@
 
 	spin_lock_irqsave(&ws->lock, flags);
 
-	ws->event_count++;
-	if (!ws->active)
-		wakeup_source_activate(ws);
+	wakeup_source_report_event(ws);
 
 	if (!msec) {
 		wakeup_source_deactivate(ws);
@@ -609,24 +650,6 @@
 EXPORT_SYMBOL_GPL(pm_wakeup_event);
 
 /**
- * pm_wakeup_update_hit_counts - Update hit counts of all active wakeup sources.
- */
-static void pm_wakeup_update_hit_counts(void)
-{
-	unsigned long flags;
-	struct wakeup_source *ws;
-
-	rcu_read_lock();
-	list_for_each_entry_rcu(ws, &wakeup_sources, entry) {
-		spin_lock_irqsave(&ws->lock, flags);
-		if (ws->active)
-			ws->hit_count++;
-		spin_unlock_irqrestore(&ws->lock, flags);
-	}
-	rcu_read_unlock();
-}
-
-/**
  * pm_wakeup_pending - Check if power transition in progress should be aborted.
  *
  * Compare the current number of registered wakeup events with its preserved
@@ -648,32 +671,38 @@
 		events_check_enabled = !ret;
 	}
 	spin_unlock_irqrestore(&events_lock, flags);
-	if (ret)
-		pm_wakeup_update_hit_counts();
 	return ret;
 }
 
 /**
  * pm_get_wakeup_count - Read the number of registered wakeup events.
  * @count: Address to store the value at.
+ * @block: Whether or not to block.
  *
- * Store the number of registered wakeup events at the address in @count.  Block
- * if the current number of wakeup events being processed is nonzero.
+ * Store the number of registered wakeup events at the address in @count.  If
+ * @block is set, block until the current number of wakeup events being
+ * processed is zero.
  *
- * Return 'false' if the wait for the number of wakeup events being processed to
- * drop down to zero has been interrupted by a signal (and the current number
- * of wakeup events being processed is still nonzero).  Otherwise return 'true'.
+ * Return 'false' if the current number of wakeup events being processed is
+ * nonzero.  Otherwise return 'true'.
  */
-bool pm_get_wakeup_count(unsigned int *count)
+bool pm_get_wakeup_count(unsigned int *count, bool block)
 {
 	unsigned int cnt, inpr;
 
-	for (;;) {
-		split_counters(&cnt, &inpr);
-		if (inpr == 0 || signal_pending(current))
-			break;
-		pm_wakeup_update_hit_counts();
-		schedule_timeout_interruptible(msecs_to_jiffies(TIMEOUT));
+	if (block) {
+		DEFINE_WAIT(wait);
+
+		for (;;) {
+			prepare_to_wait(&wakeup_count_wait_queue, &wait,
+					TASK_INTERRUPTIBLE);
+			split_counters(&cnt, &inpr);
+			if (inpr == 0 || signal_pending(current))
+				break;
+
+			schedule();
+		}
+		finish_wait(&wakeup_count_wait_queue, &wait);
 	}
 
 	split_counters(&cnt, &inpr);
@@ -703,11 +732,37 @@
 		events_check_enabled = true;
 	}
 	spin_unlock_irq(&events_lock);
-	if (!events_check_enabled)
-		pm_wakeup_update_hit_counts();
 	return events_check_enabled;
 }
 
+#ifdef CONFIG_PM_AUTOSLEEP
+/**
+ * pm_wakep_autosleep_enabled - Modify autosleep_enabled for all wakeup sources.
+ * @enabled: Whether to set or to clear the autosleep_enabled flags.
+ */
+void pm_wakep_autosleep_enabled(bool set)
+{
+	struct wakeup_source *ws;
+	ktime_t now = ktime_get();
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(ws, &wakeup_sources, entry) {
+		spin_lock_irq(&ws->lock);
+		if (ws->autosleep_enabled != set) {
+			ws->autosleep_enabled = set;
+			if (ws->active) {
+				if (set)
+					ws->start_prevent_time = now;
+				else
+					update_prevent_sleep_time(ws, now);
+			}
+		}
+		spin_unlock_irq(&ws->lock);
+	}
+	rcu_read_unlock();
+}
+#endif /* CONFIG_PM_AUTOSLEEP */
+
 static struct dentry *wakeup_sources_stats_dentry;
 
 /**
@@ -723,27 +778,37 @@
 	ktime_t max_time;
 	unsigned long active_count;
 	ktime_t active_time;
+	ktime_t prevent_sleep_time;
 	int ret;
 
 	spin_lock_irqsave(&ws->lock, flags);
 
 	total_time = ws->total_time;
 	max_time = ws->max_time;
+	prevent_sleep_time = ws->prevent_sleep_time;
 	active_count = ws->active_count;
 	if (ws->active) {
-		active_time = ktime_sub(ktime_get(), ws->last_time);
+		ktime_t now = ktime_get();
+
+		active_time = ktime_sub(now, ws->last_time);
 		total_time = ktime_add(total_time, active_time);
 		if (active_time.tv64 > max_time.tv64)
 			max_time = active_time;
+
+		if (ws->autosleep_enabled)
+			prevent_sleep_time = ktime_add(prevent_sleep_time,
+				ktime_sub(now, ws->start_prevent_time));
 	} else {
 		active_time = ktime_set(0, 0);
 	}
 
-	ret = seq_printf(m, "%-12s\t%lu\t\t%lu\t\t%lu\t\t"
-			"%lld\t\t%lld\t\t%lld\t\t%lld\n",
-			ws->name, active_count, ws->event_count, ws->hit_count,
+	ret = seq_printf(m, "%-12s\t%lu\t\t%lu\t\t%lu\t\t%lu\t\t"
+			"%lld\t\t%lld\t\t%lld\t\t%lld\t\t%lld\n",
+			ws->name, active_count, ws->event_count,
+			ws->wakeup_count, ws->expire_count,
 			ktime_to_ms(active_time), ktime_to_ms(total_time),
-			ktime_to_ms(max_time), ktime_to_ms(ws->last_time));
+			ktime_to_ms(max_time), ktime_to_ms(ws->last_time),
+			ktime_to_ms(prevent_sleep_time));
 
 	spin_unlock_irqrestore(&ws->lock, flags);
 
@@ -758,8 +823,9 @@
 {
 	struct wakeup_source *ws;
 
-	seq_puts(m, "name\t\tactive_count\tevent_count\thit_count\t"
-		"active_since\ttotal_time\tmax_time\tlast_change\n");
+	seq_puts(m, "name\t\tactive_count\tevent_count\twakeup_count\t"
+		"expire_count\tactive_since\ttotal_time\tmax_time\t"
+		"last_change\tprevent_suspend_time\n");
 
 	rcu_read_lock();
 	list_for_each_entry_rcu(ws, &wakeup_sources, entry)
diff --git a/drivers/base/regmap/Kconfig b/drivers/base/regmap/Kconfig
index 9ef0a53..6be390b 100644
--- a/drivers/base/regmap/Kconfig
+++ b/drivers/base/regmap/Kconfig
@@ -6,6 +6,7 @@
 	default y if (REGMAP_I2C || REGMAP_SPI)
 	select LZO_COMPRESS
 	select LZO_DECOMPRESS
+	select IRQ_DOMAIN if REGMAP_IRQ
 	bool
 
 config REGMAP_I2C
diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig
index b2402eb..f45dad3 100644
--- a/drivers/char/hw_random/Kconfig
+++ b/drivers/char/hw_random/Kconfig
@@ -63,7 +63,7 @@
 config HW_RANDOM_ATMEL
 	tristate "Atmel Random Number Generator support"
 	depends on HW_RANDOM && HAVE_CLK
-	default HW_RANDOM
+	default (HW_RANDOM && ARCH_AT91)
 	---help---
 	  This driver provides kernel-side support for the Random Number
 	  Generator hardware found on Atmel AT91 devices.
@@ -250,3 +250,16 @@
 	  (check your distro, or download from
 	  http://sourceforge.net/projects/gkernel/).  rngd periodically reads
 	  /dev/hwrng and injects the entropy into /dev/random.
+
+config HW_RANDOM_PSERIES
+	tristate "pSeries HW Random Number Generator support"
+	depends on HW_RANDOM && PPC64 && IBMVIO
+	default HW_RANDOM
+	---help---
+	  This driver provides kernel-side support for the Random Number
+	  Generator hardware found on POWER7+ machines and above
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called pseries-rng.
+
+	  If unsure, say Y.
diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile
index b2ff526..d901dfa 100644
--- a/drivers/char/hw_random/Makefile
+++ b/drivers/char/hw_random/Makefile
@@ -22,3 +22,4 @@
 obj-$(CONFIG_HW_RANDOM_NOMADIK) += nomadik-rng.o
 obj-$(CONFIG_HW_RANDOM_PICOXCELL) += picoxcell-rng.o
 obj-$(CONFIG_HW_RANDOM_PPC4XX) += ppc4xx-rng.o
+obj-$(CONFIG_HW_RANDOM_PSERIES) += pseries-rng.o
diff --git a/drivers/char/hw_random/omap-rng.c b/drivers/char/hw_random/omap-rng.c
index a07a5ca..1412565 100644
--- a/drivers/char/hw_random/omap-rng.c
+++ b/drivers/char/hw_random/omap-rng.c
@@ -115,22 +115,12 @@
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 
-	if (!res) {
-		ret = -ENOENT;
-		goto err_region;
-	}
-
-	if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
-		ret = -EBUSY;
-		goto err_region;
-	}
-
-	dev_set_drvdata(&pdev->dev, res);
-	rng_base = ioremap(res->start, resource_size(res));
+	rng_base = devm_request_and_ioremap(&pdev->dev, res);
 	if (!rng_base) {
 		ret = -ENOMEM;
 		goto err_ioremap;
 	}
+	dev_set_drvdata(&pdev->dev, res);
 
 	ret = hwrng_register(&omap_rng_ops);
 	if (ret)
@@ -145,11 +135,8 @@
 	return 0;
 
 err_register:
-	iounmap(rng_base);
 	rng_base = NULL;
 err_ioremap:
-	release_mem_region(res->start, resource_size(res));
-err_region:
 	if (cpu_is_omap24xx()) {
 		clk_disable(rng_ick);
 		clk_put(rng_ick);
@@ -159,20 +146,15 @@
 
 static int __exit omap_rng_remove(struct platform_device *pdev)
 {
-	struct resource *res = dev_get_drvdata(&pdev->dev);
-
 	hwrng_unregister(&omap_rng_ops);
 
 	omap_rng_write_reg(RNG_MASK_REG, 0x0);
 
-	iounmap(rng_base);
-
 	if (cpu_is_omap24xx()) {
 		clk_disable(rng_ick);
 		clk_put(rng_ick);
 	}
 
-	release_mem_region(res->start, resource_size(res));
 	rng_base = NULL;
 
 	return 0;
diff --git a/drivers/char/hw_random/pseries-rng.c b/drivers/char/hw_random/pseries-rng.c
new file mode 100644
index 0000000..5f11979
--- /dev/null
+++ b/drivers/char/hw_random/pseries-rng.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2010 Michael Neuling IBM Corporation
+ *
+ * Driver for the pseries hardware RNG for POWER7+ and above
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/hw_random.h>
+#include <asm/vio.h>
+
+#define MODULE_NAME "pseries-rng"
+
+static int pseries_rng_data_read(struct hwrng *rng, u32 *data)
+{
+	if (plpar_hcall(H_RANDOM, (unsigned long *)data) != H_SUCCESS) {
+		printk(KERN_ERR "pseries rng hcall error\n");
+		return 0;
+	}
+	return 8;
+}
+
+/**
+ * pseries_rng_get_desired_dma - Return desired DMA allocate for CMO operations
+ *
+ * This is a required function for a driver to operate in a CMO environment
+ * but this device does not make use of DMA allocations, return 0.
+ *
+ * Return value:
+ *	Number of bytes of IO data the driver will need to perform well -> 0
+ */
+static unsigned long pseries_rng_get_desired_dma(struct vio_dev *vdev)
+{
+	return 0;
+};
+
+static struct hwrng pseries_rng = {
+	.name		= MODULE_NAME,
+	.data_read	= pseries_rng_data_read,
+};
+
+static int __init pseries_rng_probe(struct vio_dev *dev,
+		const struct vio_device_id *id)
+{
+	return hwrng_register(&pseries_rng);
+}
+
+static int __exit pseries_rng_remove(struct vio_dev *dev)
+{
+	hwrng_unregister(&pseries_rng);
+	return 0;
+}
+
+static struct vio_device_id pseries_rng_driver_ids[] = {
+	{ "ibm,random-v1", "ibm,random"},
+	{ "", "" }
+};
+MODULE_DEVICE_TABLE(vio, pseries_rng_driver_ids);
+
+static struct vio_driver pseries_rng_driver = {
+	.name = MODULE_NAME,
+	.probe = pseries_rng_probe,
+	.remove = pseries_rng_remove,
+	.get_desired_dma = pseries_rng_get_desired_dma,
+	.id_table = pseries_rng_driver_ids
+};
+
+static int __init rng_init(void)
+{
+	printk(KERN_INFO "Registering IBM pSeries RNG driver\n");
+	return vio_register_driver(&pseries_rng_driver);
+}
+
+module_init(rng_init);
+
+static void __exit rng_exit(void)
+{
+	vio_unregister_driver(&pseries_rng_driver);
+}
+module_exit(rng_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Michael Neuling <mikey@neuling.org>");
+MODULE_DESCRIPTION("H/W RNG driver for IBM pSeries processors");
diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
index 371f13c..1092a77 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -297,4 +297,32 @@
 	  To compile this driver as a module, choose M here: the module
 	  will be called tegra-aes.
 
+config CRYPTO_DEV_NX
+	tristate "Support for Power7+ in-Nest cryptographic accleration"
+	depends on PPC64 && IBMVIO
+	select CRYPTO_AES
+	select CRYPTO_CBC
+	select CRYPTO_ECB
+	select CRYPTO_CCM
+	select CRYPTO_GCM
+	select CRYPTO_AUTHENC
+	select CRYPTO_XCBC
+	select CRYPTO_SHA256
+	select CRYPTO_SHA512
+	help
+	  Support for Power7+ in-Nest cryptographic acceleration. This
+	  module supports acceleration for AES and SHA2 algorithms. If you
+	  choose 'M' here, this module will be called nx_crypto.
+
+config CRYPTO_DEV_UX500
+	tristate "Driver for ST-Ericsson UX500 crypto hardware acceleration"
+	depends on ARCH_U8500
+	select CRYPTO_ALGAPI
+	help
+	  Driver for ST-Ericsson UX500 crypto engine.
+
+if CRYPTO_DEV_UX500
+	source "drivers/crypto/ux500/Kconfig"
+endif # if CRYPTO_DEV_UX500
+
 endif # CRYPTO_HW
diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile
index f3e64ea..0139032 100644
--- a/drivers/crypto/Makefile
+++ b/drivers/crypto/Makefile
@@ -14,3 +14,4 @@
 obj-$(CONFIG_CRYPTO_DEV_PICOXCELL) += picoxcell_crypto.o
 obj-$(CONFIG_CRYPTO_DEV_S5P) += s5p-sss.o
 obj-$(CONFIG_CRYPTO_DEV_TEGRA_AES) += tegra-aes.o
+obj-$(CONFIG_CRYPTO_DEV_UX500) += ux500/
\ No newline at end of file
diff --git a/drivers/crypto/amcc/crypto4xx_core.c b/drivers/crypto/amcc/crypto4xx_core.c
index 13f8e1a..802e851 100644
--- a/drivers/crypto/amcc/crypto4xx_core.c
+++ b/drivers/crypto/amcc/crypto4xx_core.c
@@ -1244,9 +1244,9 @@
 	iounmap(core_dev->dev->ce_base);
 err_iomap:
 	free_irq(core_dev->irq, dev);
+err_request_irq:
 	irq_dispose_mapping(core_dev->irq);
 	tasklet_kill(&core_dev->tasklet);
-err_request_irq:
 	crypto4xx_destroy_sdr(core_dev->dev);
 err_build_sdr:
 	crypto4xx_destroy_gdr(core_dev->dev);
diff --git a/drivers/crypto/caam/caamalg.c b/drivers/crypto/caam/caamalg.c
index 534a364..4eec389 100644
--- a/drivers/crypto/caam/caamalg.c
+++ b/drivers/crypto/caam/caamalg.c
@@ -2267,8 +2267,11 @@
 	int i, err;
 
 	dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec-v4.0");
-	if (!dev_node)
-		return;
+	if (!dev_node) {
+		dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec4.0");
+		if (!dev_node)
+			return;
+	}
 
 	pdev = of_find_device_by_node(dev_node);
 	if (!pdev)
@@ -2350,8 +2353,11 @@
 	int i = 0, err = 0;
 
 	dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec-v4.0");
-	if (!dev_node)
-		return -ENODEV;
+	if (!dev_node) {
+		dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec4.0");
+		if (!dev_node)
+			return -ENODEV;
+	}
 
 	pdev = of_find_device_by_node(dev_node);
 	if (!pdev)
diff --git a/drivers/crypto/caam/ctrl.c b/drivers/crypto/caam/ctrl.c
index c5f61c5..77557eb 100644
--- a/drivers/crypto/caam/ctrl.c
+++ b/drivers/crypto/caam/ctrl.c
@@ -98,6 +98,12 @@
 	rspec = 0;
 	for_each_compatible_node(np, NULL, "fsl,sec-v4.0-job-ring")
 		rspec++;
+	if (!rspec) {
+		/* for backward compatible with device trees */
+		for_each_compatible_node(np, NULL, "fsl,sec4.0-job-ring")
+			rspec++;
+	}
+
 	ctrlpriv->jrdev = kzalloc(sizeof(struct device *) * rspec, GFP_KERNEL);
 	if (ctrlpriv->jrdev == NULL) {
 		iounmap(&topregs->ctrl);
@@ -111,6 +117,13 @@
 		ctrlpriv->total_jobrs++;
 		ring++;
 	}
+	if (!ring) {
+		for_each_compatible_node(np, NULL, "fsl,sec4.0-job-ring") {
+			caam_jr_probe(pdev, np, ring);
+			ctrlpriv->total_jobrs++;
+			ring++;
+		}
+	}
 
 	/* Check to see if QI present. If so, enable */
 	ctrlpriv->qi_present = !!(rd_reg64(&topregs->ctrl.perfmon.comp_parms) &
@@ -226,6 +239,9 @@
 	{
 		.compatible = "fsl,sec-v4.0",
 	},
+	{
+		.compatible = "fsl,sec4.0",
+	},
 	{},
 };
 MODULE_DEVICE_TABLE(of, caam_match);
diff --git a/drivers/crypto/nx/Makefile b/drivers/crypto/nx/Makefile
new file mode 100644
index 0000000..411ce59
--- /dev/null
+++ b/drivers/crypto/nx/Makefile
@@ -0,0 +1,11 @@
+obj-$(CONFIG_CRYPTO_DEV_NX) += nx-crypto.o
+nx-crypto-objs := nx.o \
+		  nx_debugfs.o \
+		  nx-aes-cbc.o \
+		  nx-aes-ecb.o \
+		  nx-aes-gcm.o \
+		  nx-aes-ccm.o \
+		  nx-aes-ctr.o \
+		  nx-aes-xcbc.o \
+		  nx-sha256.o \
+		  nx-sha512.o
diff --git a/drivers/crypto/nx/nx-aes-cbc.c b/drivers/crypto/nx/nx-aes-cbc.c
new file mode 100644
index 0000000..69ed796
--- /dev/null
+++ b/drivers/crypto/nx/nx-aes-cbc.c
@@ -0,0 +1,141 @@
+/**
+ * AES CBC routines supporting the Power 7+ Nest Accelerators driver
+ *
+ * Copyright (C) 2011-2012 International Business Machines Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 only.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Kent Yoder <yoder1@us.ibm.com>
+ */
+
+#include <crypto/aes.h>
+#include <crypto/algapi.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/crypto.h>
+#include <asm/vio.h>
+
+#include "nx_csbcpb.h"
+#include "nx.h"
+
+
+static int cbc_aes_nx_set_key(struct crypto_tfm *tfm,
+			      const u8          *in_key,
+			      unsigned int       key_len)
+{
+	struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(tfm);
+	struct nx_csbcpb *csbcpb = nx_ctx->csbcpb;
+
+	nx_ctx_init(nx_ctx, HCOP_FC_AES);
+
+	switch (key_len) {
+	case AES_KEYSIZE_128:
+		NX_CPB_SET_KEY_SIZE(csbcpb, NX_KS_AES_128);
+		nx_ctx->ap = &nx_ctx->props[NX_PROPS_AES_128];
+		break;
+	case AES_KEYSIZE_192:
+		NX_CPB_SET_KEY_SIZE(csbcpb, NX_KS_AES_192);
+		nx_ctx->ap = &nx_ctx->props[NX_PROPS_AES_192];
+		break;
+	case AES_KEYSIZE_256:
+		NX_CPB_SET_KEY_SIZE(csbcpb, NX_KS_AES_256);
+		nx_ctx->ap = &nx_ctx->props[NX_PROPS_AES_256];
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	csbcpb->cpb.hdr.mode = NX_MODE_AES_CBC;
+	memcpy(csbcpb->cpb.aes_cbc.key, in_key, key_len);
+
+	return 0;
+}
+
+static int cbc_aes_nx_crypt(struct blkcipher_desc *desc,
+			    struct scatterlist    *dst,
+			    struct scatterlist    *src,
+			    unsigned int           nbytes,
+			    int                    enc)
+{
+	struct nx_crypto_ctx *nx_ctx = crypto_blkcipher_ctx(desc->tfm);
+	struct nx_csbcpb *csbcpb = nx_ctx->csbcpb;
+	int rc;
+
+	if (nbytes > nx_ctx->ap->databytelen)
+		return -EINVAL;
+
+	if (enc)
+		NX_CPB_FDM(csbcpb) |= NX_FDM_ENDE_ENCRYPT;
+	else
+		NX_CPB_FDM(csbcpb) &= ~NX_FDM_ENDE_ENCRYPT;
+
+	rc = nx_build_sg_lists(nx_ctx, desc, dst, src, nbytes,
+			       csbcpb->cpb.aes_cbc.iv);
+	if (rc)
+		goto out;
+
+	if (!nx_ctx->op.inlen || !nx_ctx->op.outlen) {
+		rc = -EINVAL;
+		goto out;
+	}
+
+	rc = nx_hcall_sync(nx_ctx, &nx_ctx->op,
+			   desc->flags & CRYPTO_TFM_REQ_MAY_SLEEP);
+	if (rc)
+		goto out;
+
+	atomic_inc(&(nx_ctx->stats->aes_ops));
+	atomic64_add(csbcpb->csb.processed_byte_count,
+		     &(nx_ctx->stats->aes_bytes));
+out:
+	return rc;
+}
+
+static int cbc_aes_nx_encrypt(struct blkcipher_desc *desc,
+			      struct scatterlist    *dst,
+			      struct scatterlist    *src,
+			      unsigned int           nbytes)
+{
+	return cbc_aes_nx_crypt(desc, dst, src, nbytes, 1);
+}
+
+static int cbc_aes_nx_decrypt(struct blkcipher_desc *desc,
+			      struct scatterlist    *dst,
+			      struct scatterlist    *src,
+			      unsigned int           nbytes)
+{
+	return cbc_aes_nx_crypt(desc, dst, src, nbytes, 0);
+}
+
+struct crypto_alg nx_cbc_aes_alg = {
+	.cra_name        = "cbc(aes)",
+	.cra_driver_name = "cbc-aes-nx",
+	.cra_priority    = 300,
+	.cra_flags       = CRYPTO_ALG_TYPE_BLKCIPHER,
+	.cra_blocksize   = AES_BLOCK_SIZE,
+	.cra_ctxsize     = sizeof(struct nx_crypto_ctx),
+	.cra_type        = &crypto_blkcipher_type,
+	.cra_module      = THIS_MODULE,
+	.cra_list        = LIST_HEAD_INIT(nx_cbc_aes_alg.cra_list),
+	.cra_init        = nx_crypto_ctx_aes_cbc_init,
+	.cra_exit        = nx_crypto_ctx_exit,
+	.cra_blkcipher = {
+		.min_keysize = AES_MIN_KEY_SIZE,
+		.max_keysize = AES_MAX_KEY_SIZE,
+		.ivsize      = AES_BLOCK_SIZE,
+		.setkey      = cbc_aes_nx_set_key,
+		.encrypt     = cbc_aes_nx_encrypt,
+		.decrypt     = cbc_aes_nx_decrypt,
+	}
+};
diff --git a/drivers/crypto/nx/nx-aes-ccm.c b/drivers/crypto/nx/nx-aes-ccm.c
new file mode 100644
index 0000000..7aeac67
--- /dev/null
+++ b/drivers/crypto/nx/nx-aes-ccm.c
@@ -0,0 +1,468 @@
+/**
+ * AES CCM routines supporting the Power 7+ Nest Accelerators driver
+ *
+ * Copyright (C) 2012 International Business Machines Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 only.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Kent Yoder <yoder1@us.ibm.com>
+ */
+
+#include <crypto/internal/aead.h>
+#include <crypto/aes.h>
+#include <crypto/algapi.h>
+#include <crypto/scatterwalk.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/crypto.h>
+#include <asm/vio.h>
+
+#include "nx_csbcpb.h"
+#include "nx.h"
+
+
+static int ccm_aes_nx_set_key(struct crypto_aead *tfm,
+			      const u8           *in_key,
+			      unsigned int        key_len)
+{
+	struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&tfm->base);
+	struct nx_csbcpb *csbcpb = nx_ctx->csbcpb;
+	struct nx_csbcpb *csbcpb_aead = nx_ctx->csbcpb_aead;
+
+	nx_ctx_init(nx_ctx, HCOP_FC_AES);
+
+	switch (key_len) {
+	case AES_KEYSIZE_128:
+		NX_CPB_SET_KEY_SIZE(csbcpb, NX_KS_AES_128);
+		NX_CPB_SET_KEY_SIZE(csbcpb_aead, NX_KS_AES_128);
+		nx_ctx->ap = &nx_ctx->props[NX_PROPS_AES_128];
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	csbcpb->cpb.hdr.mode = NX_MODE_AES_CCM;
+	memcpy(csbcpb->cpb.aes_ccm.key, in_key, key_len);
+
+	csbcpb_aead->cpb.hdr.mode = NX_MODE_AES_CCA;
+	memcpy(csbcpb_aead->cpb.aes_cca.key, in_key, key_len);
+
+	return 0;
+
+}
+
+static int ccm4309_aes_nx_set_key(struct crypto_aead *tfm,
+				  const u8           *in_key,
+				  unsigned int        key_len)
+{
+	struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&tfm->base);
+
+	if (key_len < 3)
+		return -EINVAL;
+
+	key_len -= 3;
+
+	memcpy(nx_ctx->priv.ccm.nonce, in_key + key_len, 3);
+
+	return ccm_aes_nx_set_key(tfm, in_key, key_len);
+}
+
+static int ccm_aes_nx_setauthsize(struct crypto_aead *tfm,
+				  unsigned int authsize)
+{
+	switch (authsize) {
+	case 4:
+	case 6:
+	case 8:
+	case 10:
+	case 12:
+	case 14:
+	case 16:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	crypto_aead_crt(tfm)->authsize = authsize;
+
+	return 0;
+}
+
+static int ccm4309_aes_nx_setauthsize(struct crypto_aead *tfm,
+				      unsigned int authsize)
+{
+	switch (authsize) {
+	case 8:
+	case 12:
+	case 16:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	crypto_aead_crt(tfm)->authsize = authsize;
+
+	return 0;
+}
+
+/* taken from crypto/ccm.c */
+static int set_msg_len(u8 *block, unsigned int msglen, int csize)
+{
+	__be32 data;
+
+	memset(block, 0, csize);
+	block += csize;
+
+	if (csize >= 4)
+		csize = 4;
+	else if (msglen > (unsigned int)(1 << (8 * csize)))
+		return -EOVERFLOW;
+
+	data = cpu_to_be32(msglen);
+	memcpy(block - csize, (u8 *)&data + 4 - csize, csize);
+
+	return 0;
+}
+
+/* taken from crypto/ccm.c */
+static inline int crypto_ccm_check_iv(const u8 *iv)
+{
+	/* 2 <= L <= 8, so 1 <= L' <= 7. */
+	if (1 > iv[0] || iv[0] > 7)
+		return -EINVAL;
+
+	return 0;
+}
+
+/* based on code from crypto/ccm.c */
+static int generate_b0(u8 *iv, unsigned int assoclen, unsigned int authsize,
+		       unsigned int cryptlen, u8 *b0)
+{
+	unsigned int l, lp, m = authsize;
+	int rc;
+
+	memcpy(b0, iv, 16);
+
+	lp = b0[0];
+	l = lp + 1;
+
+	/* set m, bits 3-5 */
+	*b0 |= (8 * ((m - 2) / 2));
+
+	/* set adata, bit 6, if associated data is used */
+	if (assoclen)
+		*b0 |= 64;
+
+	rc = set_msg_len(b0 + 16 - l, cryptlen, l);
+
+	return rc;
+}
+
+static int generate_pat(u8                   *iv,
+			struct aead_request  *req,
+			struct nx_crypto_ctx *nx_ctx,
+			unsigned int          authsize,
+			unsigned int          nbytes,
+			u8                   *out)
+{
+	struct nx_sg *nx_insg = nx_ctx->in_sg;
+	struct nx_sg *nx_outsg = nx_ctx->out_sg;
+	unsigned int iauth_len = 0;
+	struct vio_pfo_op *op = NULL;
+	u8 tmp[16], *b1 = NULL, *b0 = NULL, *result = NULL;
+	int rc;
+
+	/* zero the ctr value */
+	memset(iv + 15 - iv[0], 0, iv[0] + 1);
+
+	if (!req->assoclen) {
+		b0 = nx_ctx->csbcpb->cpb.aes_ccm.in_pat_or_b0;
+	} else if (req->assoclen <= 14) {
+		/* if associated data is 14 bytes or less, we do 1 GCM
+		 * operation on 2 AES blocks, B0 (stored in the csbcpb) and B1,
+		 * which is fed in through the source buffers here */
+		b0 = nx_ctx->csbcpb->cpb.aes_ccm.in_pat_or_b0;
+		b1 = nx_ctx->priv.ccm.iauth_tag;
+		iauth_len = req->assoclen;
+
+		nx_insg = nx_build_sg_list(nx_insg, b1, 16, nx_ctx->ap->sglen);
+		nx_outsg = nx_build_sg_list(nx_outsg, tmp, 16,
+					    nx_ctx->ap->sglen);
+
+		/* inlen should be negative, indicating to phyp that its a
+		 * pointer to an sg list */
+		nx_ctx->op.inlen = (nx_ctx->in_sg - nx_insg) *
+					sizeof(struct nx_sg);
+		nx_ctx->op.outlen = (nx_ctx->out_sg - nx_outsg) *
+					sizeof(struct nx_sg);
+
+		NX_CPB_FDM(nx_ctx->csbcpb) |= NX_FDM_ENDE_ENCRYPT;
+		NX_CPB_FDM(nx_ctx->csbcpb) |= NX_FDM_INTERMEDIATE;
+
+		op = &nx_ctx->op;
+		result = nx_ctx->csbcpb->cpb.aes_ccm.out_pat_or_mac;
+	} else if (req->assoclen <= 65280) {
+		/* if associated data is less than (2^16 - 2^8), we construct
+		 * B1 differently and feed in the associated data to a CCA
+		 * operation */
+		b0 = nx_ctx->csbcpb_aead->cpb.aes_cca.b0;
+		b1 = nx_ctx->csbcpb_aead->cpb.aes_cca.b1;
+		iauth_len = 14;
+
+		/* remaining assoc data must have scatterlist built for it */
+		nx_insg = nx_walk_and_build(nx_insg, nx_ctx->ap->sglen,
+					    req->assoc, iauth_len,
+					    req->assoclen - iauth_len);
+		nx_ctx->op_aead.inlen = (nx_ctx->in_sg - nx_insg) *
+						sizeof(struct nx_sg);
+
+		op = &nx_ctx->op_aead;
+		result = nx_ctx->csbcpb_aead->cpb.aes_cca.out_pat_or_b0;
+	} else {
+		/* if associated data is less than (2^32), we construct B1
+		 * differently yet again and feed in the associated data to a
+		 * CCA operation */
+		pr_err("associated data len is %u bytes (returning -EINVAL)\n",
+		       req->assoclen);
+		rc = -EINVAL;
+	}
+
+	rc = generate_b0(iv, req->assoclen, authsize, nbytes, b0);
+	if (rc)
+		goto done;
+
+	if (b1) {
+		memset(b1, 0, 16);
+		*(u16 *)b1 = (u16)req->assoclen;
+
+		scatterwalk_map_and_copy(b1 + 2, req->assoc, 0,
+					 iauth_len, SCATTERWALK_FROM_SG);
+
+		rc = nx_hcall_sync(nx_ctx, op,
+				   req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP);
+		if (rc)
+			goto done;
+
+		atomic_inc(&(nx_ctx->stats->aes_ops));
+		atomic64_add(req->assoclen, &(nx_ctx->stats->aes_bytes));
+
+		memcpy(out, result, AES_BLOCK_SIZE);
+	}
+done:
+	return rc;
+}
+
+static int ccm_nx_decrypt(struct aead_request   *req,
+			  struct blkcipher_desc *desc)
+{
+	struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(req->base.tfm);
+	struct nx_csbcpb *csbcpb = nx_ctx->csbcpb;
+	unsigned int nbytes = req->cryptlen;
+	unsigned int authsize = crypto_aead_authsize(crypto_aead_reqtfm(req));
+	struct nx_ccm_priv *priv = &nx_ctx->priv.ccm;
+	int rc = -1;
+
+	if (nbytes > nx_ctx->ap->databytelen)
+		return -EINVAL;
+
+	nbytes -= authsize;
+
+	/* copy out the auth tag to compare with later */
+	scatterwalk_map_and_copy(priv->oauth_tag,
+				 req->src, nbytes, authsize,
+				 SCATTERWALK_FROM_SG);
+
+	rc = generate_pat(desc->info, req, nx_ctx, authsize, nbytes,
+			  csbcpb->cpb.aes_ccm.in_pat_or_b0);
+	if (rc)
+		goto out;
+
+	rc = nx_build_sg_lists(nx_ctx, desc, req->dst, req->src, nbytes,
+			       csbcpb->cpb.aes_ccm.iv_or_ctr);
+	if (rc)
+		goto out;
+
+	NX_CPB_FDM(nx_ctx->csbcpb) &= ~NX_FDM_ENDE_ENCRYPT;
+	NX_CPB_FDM(nx_ctx->csbcpb) &= ~NX_FDM_INTERMEDIATE;
+
+	rc = nx_hcall_sync(nx_ctx, &nx_ctx->op,
+			   req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP);
+	if (rc)
+		goto out;
+
+	atomic_inc(&(nx_ctx->stats->aes_ops));
+	atomic64_add(csbcpb->csb.processed_byte_count,
+		     &(nx_ctx->stats->aes_bytes));
+
+	rc = memcmp(csbcpb->cpb.aes_ccm.out_pat_or_mac, priv->oauth_tag,
+		    authsize) ? -EBADMSG : 0;
+out:
+	return rc;
+}
+
+static int ccm_nx_encrypt(struct aead_request   *req,
+			  struct blkcipher_desc *desc)
+{
+	struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(req->base.tfm);
+	struct nx_csbcpb *csbcpb = nx_ctx->csbcpb;
+	unsigned int nbytes = req->cryptlen;
+	unsigned int authsize = crypto_aead_authsize(crypto_aead_reqtfm(req));
+	int rc = -1;
+
+	if (nbytes > nx_ctx->ap->databytelen)
+		return -EINVAL;
+
+	rc = generate_pat(desc->info, req, nx_ctx, authsize, nbytes,
+			  csbcpb->cpb.aes_ccm.in_pat_or_b0);
+	if (rc)
+		goto out;
+
+	rc = nx_build_sg_lists(nx_ctx, desc, req->dst, req->src, nbytes,
+			       csbcpb->cpb.aes_ccm.iv_or_ctr);
+	if (rc)
+		goto out;
+
+	NX_CPB_FDM(csbcpb) |= NX_FDM_ENDE_ENCRYPT;
+	NX_CPB_FDM(csbcpb) &= ~NX_FDM_INTERMEDIATE;
+
+	rc = nx_hcall_sync(nx_ctx, &nx_ctx->op,
+			   req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP);
+	if (rc)
+		goto out;
+
+	atomic_inc(&(nx_ctx->stats->aes_ops));
+	atomic64_add(csbcpb->csb.processed_byte_count,
+		     &(nx_ctx->stats->aes_bytes));
+
+	/* copy out the auth tag */
+	scatterwalk_map_and_copy(csbcpb->cpb.aes_ccm.out_pat_or_mac,
+				 req->dst, nbytes, authsize,
+				 SCATTERWALK_TO_SG);
+out:
+	return rc;
+}
+
+static int ccm4309_aes_nx_encrypt(struct aead_request *req)
+{
+	struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(req->base.tfm);
+	struct blkcipher_desc desc;
+	u8 *iv = nx_ctx->priv.ccm.iv;
+
+	iv[0] = 3;
+	memcpy(iv + 1, nx_ctx->priv.ccm.nonce, 3);
+	memcpy(iv + 4, req->iv, 8);
+
+	desc.info = iv;
+	desc.tfm = (struct crypto_blkcipher *)req->base.tfm;
+
+	return ccm_nx_encrypt(req, &desc);
+}
+
+static int ccm_aes_nx_encrypt(struct aead_request *req)
+{
+	struct blkcipher_desc desc;
+	int rc;
+
+	desc.info = req->iv;
+	desc.tfm = (struct crypto_blkcipher *)req->base.tfm;
+
+	rc = crypto_ccm_check_iv(desc.info);
+	if (rc)
+		return rc;
+
+	return ccm_nx_encrypt(req, &desc);
+}
+
+static int ccm4309_aes_nx_decrypt(struct aead_request *req)
+{
+	struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(req->base.tfm);
+	struct blkcipher_desc desc;
+	u8 *iv = nx_ctx->priv.ccm.iv;
+
+	iv[0] = 3;
+	memcpy(iv + 1, nx_ctx->priv.ccm.nonce, 3);
+	memcpy(iv + 4, req->iv, 8);
+
+	desc.info = iv;
+	desc.tfm = (struct crypto_blkcipher *)req->base.tfm;
+
+	return ccm_nx_decrypt(req, &desc);
+}
+
+static int ccm_aes_nx_decrypt(struct aead_request *req)
+{
+	struct blkcipher_desc desc;
+	int rc;
+
+	desc.info = req->iv;
+	desc.tfm = (struct crypto_blkcipher *)req->base.tfm;
+
+	rc = crypto_ccm_check_iv(desc.info);
+	if (rc)
+		return rc;
+
+	return ccm_nx_decrypt(req, &desc);
+}
+
+/* tell the block cipher walk routines that this is a stream cipher by
+ * setting cra_blocksize to 1. Even using blkcipher_walk_virt_block
+ * during encrypt/decrypt doesn't solve this problem, because it calls
+ * blkcipher_walk_done under the covers, which doesn't use walk->blocksize,
+ * but instead uses this tfm->blocksize. */
+struct crypto_alg nx_ccm_aes_alg = {
+	.cra_name        = "ccm(aes)",
+	.cra_driver_name = "ccm-aes-nx",
+	.cra_priority    = 300,
+	.cra_flags       = CRYPTO_ALG_TYPE_AEAD |
+			   CRYPTO_ALG_NEED_FALLBACK,
+	.cra_blocksize   = 1,
+	.cra_ctxsize     = sizeof(struct nx_crypto_ctx),
+	.cra_type        = &crypto_aead_type,
+	.cra_module      = THIS_MODULE,
+	.cra_list        = LIST_HEAD_INIT(nx_ccm_aes_alg.cra_list),
+	.cra_init        = nx_crypto_ctx_aes_ccm_init,
+	.cra_exit        = nx_crypto_ctx_exit,
+	.cra_aead = {
+		.ivsize      = AES_BLOCK_SIZE,
+		.maxauthsize = AES_BLOCK_SIZE,
+		.setkey      = ccm_aes_nx_set_key,
+		.setauthsize = ccm_aes_nx_setauthsize,
+		.encrypt     = ccm_aes_nx_encrypt,
+		.decrypt     = ccm_aes_nx_decrypt,
+	}
+};
+
+struct crypto_alg nx_ccm4309_aes_alg = {
+	.cra_name        = "rfc4309(ccm(aes))",
+	.cra_driver_name = "rfc4309-ccm-aes-nx",
+	.cra_priority    = 300,
+	.cra_flags       = CRYPTO_ALG_TYPE_AEAD |
+			   CRYPTO_ALG_NEED_FALLBACK,
+	.cra_blocksize   = 1,
+	.cra_ctxsize     = sizeof(struct nx_crypto_ctx),
+	.cra_type        = &crypto_nivaead_type,
+	.cra_module      = THIS_MODULE,
+	.cra_list        = LIST_HEAD_INIT(nx_ccm4309_aes_alg.cra_list),
+	.cra_init        = nx_crypto_ctx_aes_ccm_init,
+	.cra_exit        = nx_crypto_ctx_exit,
+	.cra_aead = {
+		.ivsize      = 8,
+		.maxauthsize = AES_BLOCK_SIZE,
+		.setkey      = ccm4309_aes_nx_set_key,
+		.setauthsize = ccm4309_aes_nx_setauthsize,
+		.encrypt     = ccm4309_aes_nx_encrypt,
+		.decrypt     = ccm4309_aes_nx_decrypt,
+		.geniv       = "seqiv",
+	}
+};
diff --git a/drivers/crypto/nx/nx-aes-ctr.c b/drivers/crypto/nx/nx-aes-ctr.c
new file mode 100644
index 0000000..52d4eb0
--- /dev/null
+++ b/drivers/crypto/nx/nx-aes-ctr.c
@@ -0,0 +1,178 @@
+/**
+ * AES CTR routines supporting the Power 7+ Nest Accelerators driver
+ *
+ * Copyright (C) 2011-2012 International Business Machines Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 only.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Kent Yoder <yoder1@us.ibm.com>
+ */
+
+#include <crypto/aes.h>
+#include <crypto/ctr.h>
+#include <crypto/algapi.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/crypto.h>
+#include <asm/vio.h>
+
+#include "nx_csbcpb.h"
+#include "nx.h"
+
+
+static int ctr_aes_nx_set_key(struct crypto_tfm *tfm,
+			      const u8          *in_key,
+			      unsigned int       key_len)
+{
+	struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(tfm);
+	struct nx_csbcpb *csbcpb = nx_ctx->csbcpb;
+
+	nx_ctx_init(nx_ctx, HCOP_FC_AES);
+
+	switch (key_len) {
+	case AES_KEYSIZE_128:
+		NX_CPB_SET_KEY_SIZE(csbcpb, NX_KS_AES_128);
+		nx_ctx->ap = &nx_ctx->props[NX_PROPS_AES_128];
+		break;
+	case AES_KEYSIZE_192:
+		NX_CPB_SET_KEY_SIZE(csbcpb, NX_KS_AES_192);
+		nx_ctx->ap = &nx_ctx->props[NX_PROPS_AES_192];
+		break;
+	case AES_KEYSIZE_256:
+		NX_CPB_SET_KEY_SIZE(csbcpb, NX_KS_AES_256);
+		nx_ctx->ap = &nx_ctx->props[NX_PROPS_AES_256];
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	csbcpb->cpb.hdr.mode = NX_MODE_AES_CTR;
+	memcpy(csbcpb->cpb.aes_ctr.key, in_key, key_len);
+
+	return 0;
+}
+
+static int ctr3686_aes_nx_set_key(struct crypto_tfm *tfm,
+				  const u8          *in_key,
+				  unsigned int       key_len)
+{
+	struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(tfm);
+
+	if (key_len < CTR_RFC3686_NONCE_SIZE)
+		return -EINVAL;
+
+	memcpy(nx_ctx->priv.ctr.iv,
+	       in_key + key_len - CTR_RFC3686_NONCE_SIZE,
+	       CTR_RFC3686_NONCE_SIZE);
+
+	key_len -= CTR_RFC3686_NONCE_SIZE;
+
+	return ctr_aes_nx_set_key(tfm, in_key, key_len);
+}
+
+static int ctr_aes_nx_crypt(struct blkcipher_desc *desc,
+			    struct scatterlist    *dst,
+			    struct scatterlist    *src,
+			    unsigned int           nbytes)
+{
+	struct nx_crypto_ctx *nx_ctx = crypto_blkcipher_ctx(desc->tfm);
+	struct nx_csbcpb *csbcpb = nx_ctx->csbcpb;
+	int rc;
+
+	if (nbytes > nx_ctx->ap->databytelen)
+		return -EINVAL;
+
+	rc = nx_build_sg_lists(nx_ctx, desc, dst, src, nbytes,
+			       csbcpb->cpb.aes_ctr.iv);
+	if (rc)
+		goto out;
+
+	if (!nx_ctx->op.inlen || !nx_ctx->op.outlen) {
+		rc = -EINVAL;
+		goto out;
+	}
+
+	rc = nx_hcall_sync(nx_ctx, &nx_ctx->op,
+			   desc->flags & CRYPTO_TFM_REQ_MAY_SLEEP);
+	if (rc)
+		goto out;
+
+	atomic_inc(&(nx_ctx->stats->aes_ops));
+	atomic64_add(csbcpb->csb.processed_byte_count,
+		     &(nx_ctx->stats->aes_bytes));
+out:
+	return rc;
+}
+
+static int ctr3686_aes_nx_crypt(struct blkcipher_desc *desc,
+				struct scatterlist    *dst,
+				struct scatterlist    *src,
+				unsigned int           nbytes)
+{
+	struct nx_crypto_ctx *nx_ctx = crypto_blkcipher_ctx(desc->tfm);
+	u8 *iv = nx_ctx->priv.ctr.iv;
+
+	memcpy(iv + CTR_RFC3686_NONCE_SIZE,
+	       desc->info, CTR_RFC3686_IV_SIZE);
+	iv[15] = 1;
+
+	desc->info = nx_ctx->priv.ctr.iv;
+
+	return ctr_aes_nx_crypt(desc, dst, src, nbytes);
+}
+
+struct crypto_alg nx_ctr_aes_alg = {
+	.cra_name        = "ctr(aes)",
+	.cra_driver_name = "ctr-aes-nx",
+	.cra_priority    = 300,
+	.cra_flags       = CRYPTO_ALG_TYPE_BLKCIPHER,
+	.cra_blocksize   = 1,
+	.cra_ctxsize     = sizeof(struct nx_crypto_ctx),
+	.cra_type        = &crypto_blkcipher_type,
+	.cra_module      = THIS_MODULE,
+	.cra_list        = LIST_HEAD_INIT(nx_ctr_aes_alg.cra_list),
+	.cra_init        = nx_crypto_ctx_aes_ctr_init,
+	.cra_exit        = nx_crypto_ctx_exit,
+	.cra_blkcipher = {
+		.min_keysize = AES_MIN_KEY_SIZE,
+		.max_keysize = AES_MAX_KEY_SIZE,
+		.ivsize      = AES_BLOCK_SIZE,
+		.setkey      = ctr_aes_nx_set_key,
+		.encrypt     = ctr_aes_nx_crypt,
+		.decrypt     = ctr_aes_nx_crypt,
+	}
+};
+
+struct crypto_alg nx_ctr3686_aes_alg = {
+	.cra_name        = "rfc3686(ctr(aes))",
+	.cra_driver_name = "rfc3686-ctr-aes-nx",
+	.cra_priority    = 300,
+	.cra_flags       = CRYPTO_ALG_TYPE_BLKCIPHER,
+	.cra_blocksize   = 1,
+	.cra_ctxsize     = sizeof(struct nx_crypto_ctx),
+	.cra_type        = &crypto_blkcipher_type,
+	.cra_module      = THIS_MODULE,
+	.cra_list        = LIST_HEAD_INIT(nx_ctr3686_aes_alg.cra_list),
+	.cra_init        = nx_crypto_ctx_aes_ctr_init,
+	.cra_exit        = nx_crypto_ctx_exit,
+	.cra_blkcipher = {
+		.min_keysize = AES_MIN_KEY_SIZE + CTR_RFC3686_NONCE_SIZE,
+		.max_keysize = AES_MAX_KEY_SIZE + CTR_RFC3686_NONCE_SIZE,
+		.ivsize      = CTR_RFC3686_IV_SIZE,
+		.geniv       = "seqiv",
+		.setkey      = ctr3686_aes_nx_set_key,
+		.encrypt     = ctr3686_aes_nx_crypt,
+		.decrypt     = ctr3686_aes_nx_crypt,
+	}
+};
diff --git a/drivers/crypto/nx/nx-aes-ecb.c b/drivers/crypto/nx/nx-aes-ecb.c
new file mode 100644
index 0000000..7b77bc2
--- /dev/null
+++ b/drivers/crypto/nx/nx-aes-ecb.c
@@ -0,0 +1,139 @@
+/**
+ * AES ECB routines supporting the Power 7+ Nest Accelerators driver
+ *
+ * Copyright (C) 2011-2012 International Business Machines Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 only.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Kent Yoder <yoder1@us.ibm.com>
+ */
+
+#include <crypto/aes.h>
+#include <crypto/algapi.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/crypto.h>
+#include <asm/vio.h>
+
+#include "nx_csbcpb.h"
+#include "nx.h"
+
+
+static int ecb_aes_nx_set_key(struct crypto_tfm *tfm,
+			      const u8          *in_key,
+			      unsigned int       key_len)
+{
+	struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(tfm);
+	struct nx_csbcpb *csbcpb = (struct nx_csbcpb *)nx_ctx->csbcpb;
+
+	nx_ctx_init(nx_ctx, HCOP_FC_AES);
+
+	switch (key_len) {
+	case AES_KEYSIZE_128:
+		NX_CPB_SET_KEY_SIZE(csbcpb, NX_KS_AES_128);
+		nx_ctx->ap = &nx_ctx->props[NX_PROPS_AES_128];
+		break;
+	case AES_KEYSIZE_192:
+		NX_CPB_SET_KEY_SIZE(csbcpb, NX_KS_AES_192);
+		nx_ctx->ap = &nx_ctx->props[NX_PROPS_AES_192];
+		break;
+	case AES_KEYSIZE_256:
+		NX_CPB_SET_KEY_SIZE(csbcpb, NX_KS_AES_256);
+		nx_ctx->ap = &nx_ctx->props[NX_PROPS_AES_256];
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	csbcpb->cpb.hdr.mode = NX_MODE_AES_ECB;
+	memcpy(csbcpb->cpb.aes_ecb.key, in_key, key_len);
+
+	return 0;
+}
+
+static int ecb_aes_nx_crypt(struct blkcipher_desc *desc,
+			    struct scatterlist    *dst,
+			    struct scatterlist    *src,
+			    unsigned int           nbytes,
+			    int                    enc)
+{
+	struct nx_crypto_ctx *nx_ctx = crypto_blkcipher_ctx(desc->tfm);
+	struct nx_csbcpb *csbcpb = nx_ctx->csbcpb;
+	int rc;
+
+	if (nbytes > nx_ctx->ap->databytelen)
+		return -EINVAL;
+
+	if (enc)
+		NX_CPB_FDM(csbcpb) |= NX_FDM_ENDE_ENCRYPT;
+	else
+		NX_CPB_FDM(csbcpb) &= ~NX_FDM_ENDE_ENCRYPT;
+
+	rc = nx_build_sg_lists(nx_ctx, desc, dst, src, nbytes, NULL);
+	if (rc)
+		goto out;
+
+	if (!nx_ctx->op.inlen || !nx_ctx->op.outlen) {
+		rc = -EINVAL;
+		goto out;
+	}
+
+	rc = nx_hcall_sync(nx_ctx, &nx_ctx->op,
+			   desc->flags & CRYPTO_TFM_REQ_MAY_SLEEP);
+	if (rc)
+		goto out;
+
+	atomic_inc(&(nx_ctx->stats->aes_ops));
+	atomic64_add(csbcpb->csb.processed_byte_count,
+		     &(nx_ctx->stats->aes_bytes));
+out:
+	return rc;
+}
+
+static int ecb_aes_nx_encrypt(struct blkcipher_desc *desc,
+			      struct scatterlist    *dst,
+			      struct scatterlist    *src,
+			      unsigned int           nbytes)
+{
+	return ecb_aes_nx_crypt(desc, dst, src, nbytes, 1);
+}
+
+static int ecb_aes_nx_decrypt(struct blkcipher_desc *desc,
+			      struct scatterlist    *dst,
+			      struct scatterlist    *src,
+			      unsigned int           nbytes)
+{
+	return ecb_aes_nx_crypt(desc, dst, src, nbytes, 0);
+}
+
+struct crypto_alg nx_ecb_aes_alg = {
+	.cra_name        = "ecb(aes)",
+	.cra_driver_name = "ecb-aes-nx",
+	.cra_priority    = 300,
+	.cra_flags       = CRYPTO_ALG_TYPE_BLKCIPHER,
+	.cra_blocksize   = AES_BLOCK_SIZE,
+	.cra_ctxsize     = sizeof(struct nx_crypto_ctx),
+	.cra_type        = &crypto_blkcipher_type,
+	.cra_module      = THIS_MODULE,
+	.cra_list        = LIST_HEAD_INIT(nx_ecb_aes_alg.cra_list),
+	.cra_init        = nx_crypto_ctx_aes_ecb_init,
+	.cra_exit        = nx_crypto_ctx_exit,
+	.cra_blkcipher = {
+		.min_keysize = AES_MIN_KEY_SIZE,
+		.max_keysize = AES_MAX_KEY_SIZE,
+		.setkey      = ecb_aes_nx_set_key,
+		.encrypt     = ecb_aes_nx_encrypt,
+		.decrypt     = ecb_aes_nx_decrypt,
+	}
+};
diff --git a/drivers/crypto/nx/nx-aes-gcm.c b/drivers/crypto/nx/nx-aes-gcm.c
new file mode 100644
index 0000000..9ab1c73
--- /dev/null
+++ b/drivers/crypto/nx/nx-aes-gcm.c
@@ -0,0 +1,353 @@
+/**
+ * AES GCM routines supporting the Power 7+ Nest Accelerators driver
+ *
+ * Copyright (C) 2012 International Business Machines Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 only.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Kent Yoder <yoder1@us.ibm.com>
+ */
+
+#include <crypto/internal/aead.h>
+#include <crypto/aes.h>
+#include <crypto/algapi.h>
+#include <crypto/scatterwalk.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/crypto.h>
+#include <asm/vio.h>
+
+#include "nx_csbcpb.h"
+#include "nx.h"
+
+
+static int gcm_aes_nx_set_key(struct crypto_aead *tfm,
+			      const u8           *in_key,
+			      unsigned int        key_len)
+{
+	struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&tfm->base);
+	struct nx_csbcpb *csbcpb = nx_ctx->csbcpb;
+	struct nx_csbcpb *csbcpb_aead = nx_ctx->csbcpb_aead;
+
+	nx_ctx_init(nx_ctx, HCOP_FC_AES);
+
+	switch (key_len) {
+	case AES_KEYSIZE_128:
+		NX_CPB_SET_KEY_SIZE(csbcpb, NX_KS_AES_128);
+		NX_CPB_SET_KEY_SIZE(csbcpb_aead, NX_KS_AES_128);
+		nx_ctx->ap = &nx_ctx->props[NX_PROPS_AES_128];
+		break;
+	case AES_KEYSIZE_192:
+		NX_CPB_SET_KEY_SIZE(csbcpb, NX_KS_AES_192);
+		NX_CPB_SET_KEY_SIZE(csbcpb_aead, NX_KS_AES_192);
+		nx_ctx->ap = &nx_ctx->props[NX_PROPS_AES_192];
+		break;
+	case AES_KEYSIZE_256:
+		NX_CPB_SET_KEY_SIZE(csbcpb, NX_KS_AES_256);
+		NX_CPB_SET_KEY_SIZE(csbcpb_aead, NX_KS_AES_256);
+		nx_ctx->ap = &nx_ctx->props[NX_PROPS_AES_256];
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	csbcpb->cpb.hdr.mode = NX_MODE_AES_GCM;
+	memcpy(csbcpb->cpb.aes_gcm.key, in_key, key_len);
+
+	csbcpb_aead->cpb.hdr.mode = NX_MODE_AES_GCA;
+	memcpy(csbcpb_aead->cpb.aes_gca.key, in_key, key_len);
+
+	return 0;
+}
+
+static int gcm4106_aes_nx_set_key(struct crypto_aead *tfm,
+				  const u8           *in_key,
+				  unsigned int        key_len)
+{
+	struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&tfm->base);
+	char *nonce = nx_ctx->priv.gcm.nonce;
+	int rc;
+
+	if (key_len < 4)
+		return -EINVAL;
+
+	key_len -= 4;
+
+	rc = gcm_aes_nx_set_key(tfm, in_key, key_len);
+	if (rc)
+		goto out;
+
+	memcpy(nonce, in_key + key_len, 4);
+out:
+	return rc;
+}
+
+static int gcm_aes_nx_setauthsize(struct crypto_aead *tfm,
+				  unsigned int authsize)
+{
+	if (authsize > crypto_aead_alg(tfm)->maxauthsize)
+		return -EINVAL;
+
+	crypto_aead_crt(tfm)->authsize = authsize;
+
+	return 0;
+}
+
+static int gcm4106_aes_nx_setauthsize(struct crypto_aead *tfm,
+				      unsigned int authsize)
+{
+	switch (authsize) {
+	case 8:
+	case 12:
+	case 16:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	crypto_aead_crt(tfm)->authsize = authsize;
+
+	return 0;
+}
+
+static int nx_gca(struct nx_crypto_ctx  *nx_ctx,
+		  struct aead_request   *req,
+		  u8                    *out)
+{
+	struct nx_csbcpb *csbcpb_aead = nx_ctx->csbcpb_aead;
+	int rc = -EINVAL;
+	struct scatter_walk walk;
+	struct nx_sg *nx_sg = nx_ctx->in_sg;
+
+	if (req->assoclen > nx_ctx->ap->databytelen)
+		goto out;
+
+	if (req->assoclen <= AES_BLOCK_SIZE) {
+		scatterwalk_start(&walk, req->assoc);
+		scatterwalk_copychunks(out, &walk, req->assoclen,
+				       SCATTERWALK_FROM_SG);
+		scatterwalk_done(&walk, SCATTERWALK_FROM_SG, 0);
+
+		rc = 0;
+		goto out;
+	}
+
+	nx_sg = nx_walk_and_build(nx_sg, nx_ctx->ap->sglen, req->assoc, 0,
+				  req->assoclen);
+	nx_ctx->op_aead.inlen = (nx_ctx->in_sg - nx_sg) * sizeof(struct nx_sg);
+
+	rc = nx_hcall_sync(nx_ctx, &nx_ctx->op_aead,
+			   req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP);
+	if (rc)
+		goto out;
+
+	atomic_inc(&(nx_ctx->stats->aes_ops));
+	atomic64_add(req->assoclen, &(nx_ctx->stats->aes_bytes));
+
+	memcpy(out, csbcpb_aead->cpb.aes_gca.out_pat, AES_BLOCK_SIZE);
+out:
+	return rc;
+}
+
+static int gcm_aes_nx_crypt(struct aead_request *req, int enc)
+{
+	struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(req->base.tfm);
+	struct nx_csbcpb *csbcpb = nx_ctx->csbcpb;
+	struct blkcipher_desc desc;
+	unsigned int nbytes = req->cryptlen;
+	int rc = -EINVAL;
+
+	if (nbytes > nx_ctx->ap->databytelen)
+		goto out;
+
+	desc.info = nx_ctx->priv.gcm.iv;
+	/* initialize the counter */
+	*(u32 *)(desc.info + NX_GCM_CTR_OFFSET) = 1;
+
+	/* For scenarios where the input message is zero length, AES CTR mode
+	 * may be used. Set the source data to be a single block (16B) of all
+	 * zeros, and set the input IV value to be the same as the GMAC IV
+	 * value. - nx_wb 4.8.1.3 */
+	if (nbytes == 0) {
+		char src[AES_BLOCK_SIZE] = {};
+		struct scatterlist sg;
+
+		desc.tfm = crypto_alloc_blkcipher("ctr(aes)", 0, 0);
+		if (IS_ERR(desc.tfm)) {
+			rc = -ENOMEM;
+			goto out;
+		}
+
+		crypto_blkcipher_setkey(desc.tfm, csbcpb->cpb.aes_gcm.key,
+			NX_CPB_KEY_SIZE(csbcpb) == NX_KS_AES_128 ? 16 :
+			NX_CPB_KEY_SIZE(csbcpb) == NX_KS_AES_192 ? 24 : 32);
+
+		sg_init_one(&sg, src, AES_BLOCK_SIZE);
+		if (enc)
+			crypto_blkcipher_encrypt_iv(&desc, req->dst, &sg,
+						    AES_BLOCK_SIZE);
+		else
+			crypto_blkcipher_decrypt_iv(&desc, req->dst, &sg,
+						    AES_BLOCK_SIZE);
+		crypto_free_blkcipher(desc.tfm);
+
+		rc = 0;
+		goto out;
+	}
+
+	desc.tfm = (struct crypto_blkcipher *)req->base.tfm;
+
+	csbcpb->cpb.aes_gcm.bit_length_aad = req->assoclen * 8;
+
+	if (req->assoclen) {
+		rc = nx_gca(nx_ctx, req, csbcpb->cpb.aes_gcm.in_pat_or_aad);
+		if (rc)
+			goto out;
+	}
+
+	if (enc)
+		NX_CPB_FDM(csbcpb) |= NX_FDM_ENDE_ENCRYPT;
+	else
+		nbytes -= AES_BLOCK_SIZE;
+
+	csbcpb->cpb.aes_gcm.bit_length_data = nbytes * 8;
+
+	rc = nx_build_sg_lists(nx_ctx, &desc, req->dst, req->src, nbytes,
+			       csbcpb->cpb.aes_gcm.iv_or_cnt);
+	if (rc)
+		goto out;
+
+	rc = nx_hcall_sync(nx_ctx, &nx_ctx->op,
+			   req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP);
+	if (rc)
+		goto out;
+
+	atomic_inc(&(nx_ctx->stats->aes_ops));
+	atomic64_add(csbcpb->csb.processed_byte_count,
+		     &(nx_ctx->stats->aes_bytes));
+
+	if (enc) {
+		/* copy out the auth tag */
+		scatterwalk_map_and_copy(csbcpb->cpb.aes_gcm.out_pat_or_mac,
+				 req->dst, nbytes,
+				 crypto_aead_authsize(crypto_aead_reqtfm(req)),
+				 SCATTERWALK_TO_SG);
+	} else if (req->assoclen) {
+		u8 *itag = nx_ctx->priv.gcm.iauth_tag;
+		u8 *otag = csbcpb->cpb.aes_gcm.out_pat_or_mac;
+
+		scatterwalk_map_and_copy(itag, req->dst, nbytes,
+				 crypto_aead_authsize(crypto_aead_reqtfm(req)),
+				 SCATTERWALK_FROM_SG);
+		rc = memcmp(itag, otag,
+			    crypto_aead_authsize(crypto_aead_reqtfm(req))) ?
+		     -EBADMSG : 0;
+	}
+out:
+	return rc;
+}
+
+static int gcm_aes_nx_encrypt(struct aead_request *req)
+{
+	struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(req->base.tfm);
+	char *iv = nx_ctx->priv.gcm.iv;
+
+	memcpy(iv, req->iv, 12);
+
+	return gcm_aes_nx_crypt(req, 1);
+}
+
+static int gcm_aes_nx_decrypt(struct aead_request *req)
+{
+	struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(req->base.tfm);
+	char *iv = nx_ctx->priv.gcm.iv;
+
+	memcpy(iv, req->iv, 12);
+
+	return gcm_aes_nx_crypt(req, 0);
+}
+
+static int gcm4106_aes_nx_encrypt(struct aead_request *req)
+{
+	struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(req->base.tfm);
+	char *iv = nx_ctx->priv.gcm.iv;
+	char *nonce = nx_ctx->priv.gcm.nonce;
+
+	memcpy(iv, nonce, NX_GCM4106_NONCE_LEN);
+	memcpy(iv + NX_GCM4106_NONCE_LEN, req->iv, 8);
+
+	return gcm_aes_nx_crypt(req, 1);
+}
+
+static int gcm4106_aes_nx_decrypt(struct aead_request *req)
+{
+	struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(req->base.tfm);
+	char *iv = nx_ctx->priv.gcm.iv;
+	char *nonce = nx_ctx->priv.gcm.nonce;
+
+	memcpy(iv, nonce, NX_GCM4106_NONCE_LEN);
+	memcpy(iv + NX_GCM4106_NONCE_LEN, req->iv, 8);
+
+	return gcm_aes_nx_crypt(req, 0);
+}
+
+/* tell the block cipher walk routines that this is a stream cipher by
+ * setting cra_blocksize to 1. Even using blkcipher_walk_virt_block
+ * during encrypt/decrypt doesn't solve this problem, because it calls
+ * blkcipher_walk_done under the covers, which doesn't use walk->blocksize,
+ * but instead uses this tfm->blocksize. */
+struct crypto_alg nx_gcm_aes_alg = {
+	.cra_name        = "gcm(aes)",
+	.cra_driver_name = "gcm-aes-nx",
+	.cra_priority    = 300,
+	.cra_flags       = CRYPTO_ALG_TYPE_AEAD,
+	.cra_blocksize   = 1,
+	.cra_ctxsize     = sizeof(struct nx_crypto_ctx),
+	.cra_type        = &crypto_aead_type,
+	.cra_module      = THIS_MODULE,
+	.cra_list        = LIST_HEAD_INIT(nx_gcm_aes_alg.cra_list),
+	.cra_init        = nx_crypto_ctx_aes_gcm_init,
+	.cra_exit        = nx_crypto_ctx_exit,
+	.cra_aead = {
+		.ivsize      = AES_BLOCK_SIZE,
+		.maxauthsize = AES_BLOCK_SIZE,
+		.setkey      = gcm_aes_nx_set_key,
+		.setauthsize = gcm_aes_nx_setauthsize,
+		.encrypt     = gcm_aes_nx_encrypt,
+		.decrypt     = gcm_aes_nx_decrypt,
+	}
+};
+
+struct crypto_alg nx_gcm4106_aes_alg = {
+	.cra_name        = "rfc4106(gcm(aes))",
+	.cra_driver_name = "rfc4106-gcm-aes-nx",
+	.cra_priority    = 300,
+	.cra_flags       = CRYPTO_ALG_TYPE_AEAD,
+	.cra_blocksize   = 1,
+	.cra_ctxsize     = sizeof(struct nx_crypto_ctx),
+	.cra_type        = &crypto_nivaead_type,
+	.cra_module      = THIS_MODULE,
+	.cra_list        = LIST_HEAD_INIT(nx_gcm4106_aes_alg.cra_list),
+	.cra_init        = nx_crypto_ctx_aes_gcm_init,
+	.cra_exit        = nx_crypto_ctx_exit,
+	.cra_aead = {
+		.ivsize      = 8,
+		.maxauthsize = AES_BLOCK_SIZE,
+		.geniv       = "seqiv",
+		.setkey      = gcm4106_aes_nx_set_key,
+		.setauthsize = gcm4106_aes_nx_setauthsize,
+		.encrypt     = gcm4106_aes_nx_encrypt,
+		.decrypt     = gcm4106_aes_nx_decrypt,
+	}
+};
diff --git a/drivers/crypto/nx/nx-aes-xcbc.c b/drivers/crypto/nx/nx-aes-xcbc.c
new file mode 100644
index 0000000..93923e4
--- /dev/null
+++ b/drivers/crypto/nx/nx-aes-xcbc.c
@@ -0,0 +1,236 @@
+/**
+ * AES XCBC routines supporting the Power 7+ Nest Accelerators driver
+ *
+ * Copyright (C) 2011-2012 International Business Machines Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 only.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Kent Yoder <yoder1@us.ibm.com>
+ */
+
+#include <crypto/internal/hash.h>
+#include <crypto/aes.h>
+#include <crypto/algapi.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/crypto.h>
+#include <asm/vio.h>
+
+#include "nx_csbcpb.h"
+#include "nx.h"
+
+
+struct xcbc_state {
+	u8 state[AES_BLOCK_SIZE];
+	unsigned int count;
+	u8 buffer[AES_BLOCK_SIZE];
+};
+
+static int nx_xcbc_set_key(struct crypto_shash *desc,
+			   const u8            *in_key,
+			   unsigned int         key_len)
+{
+	struct nx_crypto_ctx *nx_ctx = crypto_shash_ctx(desc);
+
+	switch (key_len) {
+	case AES_KEYSIZE_128:
+		nx_ctx->ap = &nx_ctx->props[NX_PROPS_AES_128];
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	memcpy(nx_ctx->priv.xcbc.key, in_key, key_len);
+
+	return 0;
+}
+
+static int nx_xcbc_init(struct shash_desc *desc)
+{
+	struct xcbc_state *sctx = shash_desc_ctx(desc);
+	struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&desc->tfm->base);
+	struct nx_csbcpb *csbcpb = nx_ctx->csbcpb;
+	struct nx_sg *out_sg;
+
+	nx_ctx_init(nx_ctx, HCOP_FC_AES);
+
+	memset(sctx, 0, sizeof *sctx);
+
+	NX_CPB_SET_KEY_SIZE(csbcpb, NX_KS_AES_128);
+	csbcpb->cpb.hdr.mode = NX_MODE_AES_XCBC_MAC;
+
+	memcpy(csbcpb->cpb.aes_xcbc.key, nx_ctx->priv.xcbc.key, AES_BLOCK_SIZE);
+	memset(nx_ctx->priv.xcbc.key, 0, sizeof *nx_ctx->priv.xcbc.key);
+
+	out_sg = nx_build_sg_list(nx_ctx->out_sg, (u8 *)sctx->state,
+				  AES_BLOCK_SIZE, nx_ctx->ap->sglen);
+	nx_ctx->op.outlen = (nx_ctx->out_sg - out_sg) * sizeof(struct nx_sg);
+
+	return 0;
+}
+
+static int nx_xcbc_update(struct shash_desc *desc,
+			  const u8          *data,
+			  unsigned int       len)
+{
+	struct xcbc_state *sctx = shash_desc_ctx(desc);
+	struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&desc->tfm->base);
+	struct nx_csbcpb *csbcpb = nx_ctx->csbcpb;
+	struct nx_sg *in_sg;
+	u32 to_process, leftover;
+	int rc = 0;
+
+	if (NX_CPB_FDM(csbcpb) & NX_FDM_CONTINUATION) {
+		/* we've hit the nx chip previously and we're updating again,
+		 * so copy over the partial digest */
+		memcpy(csbcpb->cpb.aes_xcbc.cv,
+		       csbcpb->cpb.aes_xcbc.out_cv_mac, AES_BLOCK_SIZE);
+	}
+
+	/* 2 cases for total data len:
+	 *  1: <= AES_BLOCK_SIZE: copy into state, return 0
+	 *  2: > AES_BLOCK_SIZE: process X blocks, copy in leftover
+	 */
+	if (len + sctx->count <= AES_BLOCK_SIZE) {
+		memcpy(sctx->buffer + sctx->count, data, len);
+		sctx->count += len;
+		goto out;
+	}
+
+	/* to_process: the AES_BLOCK_SIZE data chunk to process in this
+	 * update */
+	to_process = (sctx->count + len) & ~(AES_BLOCK_SIZE - 1);
+	leftover = (sctx->count + len) & (AES_BLOCK_SIZE - 1);
+
+	/* the hardware will not accept a 0 byte operation for this algorithm
+	 * and the operation MUST be finalized to be correct. So if we happen
+	 * to get an update that falls on a block sized boundary, we must
+	 * save off the last block to finalize with later. */
+	if (!leftover) {
+		to_process -= AES_BLOCK_SIZE;
+		leftover = AES_BLOCK_SIZE;
+	}
+
+	if (sctx->count) {
+		in_sg = nx_build_sg_list(nx_ctx->in_sg, sctx->buffer,
+					 sctx->count, nx_ctx->ap->sglen);
+		in_sg = nx_build_sg_list(in_sg, (u8 *)data,
+					 to_process - sctx->count,
+					 nx_ctx->ap->sglen);
+		nx_ctx->op.inlen = (nx_ctx->in_sg - in_sg) *
+					sizeof(struct nx_sg);
+	} else {
+		in_sg = nx_build_sg_list(nx_ctx->in_sg, (u8 *)data, to_process,
+					 nx_ctx->ap->sglen);
+		nx_ctx->op.inlen = (nx_ctx->in_sg - in_sg) *
+					sizeof(struct nx_sg);
+	}
+
+	NX_CPB_FDM(csbcpb) |= NX_FDM_INTERMEDIATE;
+
+	if (!nx_ctx->op.inlen || !nx_ctx->op.outlen) {
+		rc = -EINVAL;
+		goto out;
+	}
+
+	rc = nx_hcall_sync(nx_ctx, &nx_ctx->op,
+			   desc->flags & CRYPTO_TFM_REQ_MAY_SLEEP);
+	if (rc)
+		goto out;
+
+	atomic_inc(&(nx_ctx->stats->aes_ops));
+
+	/* copy the leftover back into the state struct */
+	memcpy(sctx->buffer, data + len - leftover, leftover);
+	sctx->count = leftover;
+
+	/* everything after the first update is continuation */
+	NX_CPB_FDM(csbcpb) |= NX_FDM_CONTINUATION;
+out:
+	return rc;
+}
+
+static int nx_xcbc_final(struct shash_desc *desc, u8 *out)
+{
+	struct xcbc_state *sctx = shash_desc_ctx(desc);
+	struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&desc->tfm->base);
+	struct nx_csbcpb *csbcpb = nx_ctx->csbcpb;
+	struct nx_sg *in_sg, *out_sg;
+	int rc = 0;
+
+	if (NX_CPB_FDM(csbcpb) & NX_FDM_CONTINUATION) {
+		/* we've hit the nx chip previously, now we're finalizing,
+		 * so copy over the partial digest */
+		memcpy(csbcpb->cpb.aes_xcbc.cv,
+		       csbcpb->cpb.aes_xcbc.out_cv_mac, AES_BLOCK_SIZE);
+	} else if (sctx->count == 0) {
+		/* we've never seen an update, so this is a 0 byte op. The
+		 * hardware cannot handle a 0 byte op, so just copy out the
+		 * known 0 byte result. This is cheaper than allocating a
+		 * software context to do a 0 byte op */
+		u8 data[] = { 0x75, 0xf0, 0x25, 0x1d, 0x52, 0x8a, 0xc0, 0x1c,
+			      0x45, 0x73, 0xdf, 0xd5, 0x84, 0xd7, 0x9f, 0x29 };
+		memcpy(out, data, sizeof(data));
+		goto out;
+	}
+
+	/* final is represented by continuing the operation and indicating that
+	 * this is not an intermediate operation */
+	NX_CPB_FDM(csbcpb) &= ~NX_FDM_INTERMEDIATE;
+
+	in_sg = nx_build_sg_list(nx_ctx->in_sg, (u8 *)sctx->buffer,
+				 sctx->count, nx_ctx->ap->sglen);
+	out_sg = nx_build_sg_list(nx_ctx->out_sg, out, AES_BLOCK_SIZE,
+				  nx_ctx->ap->sglen);
+
+	nx_ctx->op.inlen = (nx_ctx->in_sg - in_sg) * sizeof(struct nx_sg);
+	nx_ctx->op.outlen = (nx_ctx->out_sg - out_sg) * sizeof(struct nx_sg);
+
+	if (!nx_ctx->op.outlen) {
+		rc = -EINVAL;
+		goto out;
+	}
+
+	rc = nx_hcall_sync(nx_ctx, &nx_ctx->op,
+			   desc->flags & CRYPTO_TFM_REQ_MAY_SLEEP);
+	if (rc)
+		goto out;
+
+	atomic_inc(&(nx_ctx->stats->aes_ops));
+
+	memcpy(out, csbcpb->cpb.aes_xcbc.out_cv_mac, AES_BLOCK_SIZE);
+out:
+	return rc;
+}
+
+struct shash_alg nx_shash_aes_xcbc_alg = {
+	.digestsize = AES_BLOCK_SIZE,
+	.init       = nx_xcbc_init,
+	.update     = nx_xcbc_update,
+	.final      = nx_xcbc_final,
+	.setkey     = nx_xcbc_set_key,
+	.descsize   = sizeof(struct xcbc_state),
+	.statesize  = sizeof(struct xcbc_state),
+	.base       = {
+		.cra_name        = "xcbc(aes)",
+		.cra_driver_name = "xcbc-aes-nx",
+		.cra_priority    = 300,
+		.cra_flags       = CRYPTO_ALG_TYPE_SHASH,
+		.cra_blocksize   = AES_BLOCK_SIZE,
+		.cra_module      = THIS_MODULE,
+		.cra_ctxsize     = sizeof(struct nx_crypto_ctx),
+		.cra_init        = nx_crypto_ctx_aes_xcbc_init,
+		.cra_exit        = nx_crypto_ctx_exit,
+	}
+};
diff --git a/drivers/crypto/nx/nx-sha256.c b/drivers/crypto/nx/nx-sha256.c
new file mode 100644
index 0000000..9767315
--- /dev/null
+++ b/drivers/crypto/nx/nx-sha256.c
@@ -0,0 +1,246 @@
+/**
+ * SHA-256 routines supporting the Power 7+ Nest Accelerators driver
+ *
+ * Copyright (C) 2011-2012 International Business Machines Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 only.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Kent Yoder <yoder1@us.ibm.com>
+ */
+
+#include <crypto/internal/hash.h>
+#include <crypto/sha.h>
+#include <linux/module.h>
+#include <asm/vio.h>
+
+#include "nx_csbcpb.h"
+#include "nx.h"
+
+
+static int nx_sha256_init(struct shash_desc *desc)
+{
+	struct sha256_state *sctx = shash_desc_ctx(desc);
+	struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&desc->tfm->base);
+	struct nx_sg *out_sg;
+
+	nx_ctx_init(nx_ctx, HCOP_FC_SHA);
+
+	memset(sctx, 0, sizeof *sctx);
+
+	nx_ctx->ap = &nx_ctx->props[NX_PROPS_SHA256];
+
+	NX_CPB_SET_DIGEST_SIZE(nx_ctx->csbcpb, NX_DS_SHA256);
+	out_sg = nx_build_sg_list(nx_ctx->out_sg, (u8 *)sctx->state,
+				  SHA256_DIGEST_SIZE, nx_ctx->ap->sglen);
+	nx_ctx->op.outlen = (nx_ctx->out_sg - out_sg) * sizeof(struct nx_sg);
+
+	return 0;
+}
+
+static int nx_sha256_update(struct shash_desc *desc, const u8 *data,
+			    unsigned int len)
+{
+	struct sha256_state *sctx = shash_desc_ctx(desc);
+	struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&desc->tfm->base);
+	struct nx_csbcpb *csbcpb = (struct nx_csbcpb *)nx_ctx->csbcpb;
+	struct nx_sg *in_sg;
+	u64 to_process, leftover;
+	int rc = 0;
+
+	if (NX_CPB_FDM(csbcpb) & NX_FDM_CONTINUATION) {
+		/* we've hit the nx chip previously and we're updating again,
+		 * so copy over the partial digest */
+		memcpy(csbcpb->cpb.sha256.input_partial_digest,
+		       csbcpb->cpb.sha256.message_digest, SHA256_DIGEST_SIZE);
+	}
+
+	/* 2 cases for total data len:
+	 *  1: <= SHA256_BLOCK_SIZE: copy into state, return 0
+	 *  2: > SHA256_BLOCK_SIZE: process X blocks, copy in leftover
+	 */
+	if (len + sctx->count <= SHA256_BLOCK_SIZE) {
+		memcpy(sctx->buf + sctx->count, data, len);
+		sctx->count += len;
+		goto out;
+	}
+
+	/* to_process: the SHA256_BLOCK_SIZE data chunk to process in this
+	 * update */
+	to_process = (sctx->count + len) & ~(SHA256_BLOCK_SIZE - 1);
+	leftover = (sctx->count + len) & (SHA256_BLOCK_SIZE - 1);
+
+	if (sctx->count) {
+		in_sg = nx_build_sg_list(nx_ctx->in_sg, (u8 *)sctx->buf,
+					 sctx->count, nx_ctx->ap->sglen);
+		in_sg = nx_build_sg_list(in_sg, (u8 *)data,
+					 to_process - sctx->count,
+					 nx_ctx->ap->sglen);
+		nx_ctx->op.inlen = (nx_ctx->in_sg - in_sg) *
+					sizeof(struct nx_sg);
+	} else {
+		in_sg = nx_build_sg_list(nx_ctx->in_sg, (u8 *)data,
+					 to_process, nx_ctx->ap->sglen);
+		nx_ctx->op.inlen = (nx_ctx->in_sg - in_sg) *
+					sizeof(struct nx_sg);
+	}
+
+	NX_CPB_FDM(csbcpb) |= NX_FDM_INTERMEDIATE;
+
+	if (!nx_ctx->op.inlen || !nx_ctx->op.outlen) {
+		rc = -EINVAL;
+		goto out;
+	}
+
+	rc = nx_hcall_sync(nx_ctx, &nx_ctx->op,
+			   desc->flags & CRYPTO_TFM_REQ_MAY_SLEEP);
+	if (rc)
+		goto out;
+
+	atomic_inc(&(nx_ctx->stats->sha256_ops));
+
+	/* copy the leftover back into the state struct */
+	memcpy(sctx->buf, data + len - leftover, leftover);
+	sctx->count = leftover;
+
+	csbcpb->cpb.sha256.message_bit_length += (u64)
+		(csbcpb->cpb.sha256.spbc * 8);
+
+	/* everything after the first update is continuation */
+	NX_CPB_FDM(csbcpb) |= NX_FDM_CONTINUATION;
+out:
+	return rc;
+}
+
+static int nx_sha256_final(struct shash_desc *desc, u8 *out)
+{
+	struct sha256_state *sctx = shash_desc_ctx(desc);
+	struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&desc->tfm->base);
+	struct nx_csbcpb *csbcpb = (struct nx_csbcpb *)nx_ctx->csbcpb;
+	struct nx_sg *in_sg, *out_sg;
+	int rc;
+
+	if (NX_CPB_FDM(csbcpb) & NX_FDM_CONTINUATION) {
+		/* we've hit the nx chip previously, now we're finalizing,
+		 * so copy over the partial digest */
+		memcpy(csbcpb->cpb.sha256.input_partial_digest,
+		       csbcpb->cpb.sha256.message_digest, SHA256_DIGEST_SIZE);
+	}
+
+	/* final is represented by continuing the operation and indicating that
+	 * this is not an intermediate operation */
+	NX_CPB_FDM(csbcpb) &= ~NX_FDM_INTERMEDIATE;
+
+	csbcpb->cpb.sha256.message_bit_length += (u64)(sctx->count * 8);
+
+	in_sg = nx_build_sg_list(nx_ctx->in_sg, (u8 *)sctx->buf,
+				 sctx->count, nx_ctx->ap->sglen);
+	out_sg = nx_build_sg_list(nx_ctx->out_sg, out, SHA256_DIGEST_SIZE,
+				  nx_ctx->ap->sglen);
+	nx_ctx->op.inlen = (nx_ctx->in_sg - in_sg) * sizeof(struct nx_sg);
+	nx_ctx->op.outlen = (nx_ctx->out_sg - out_sg) * sizeof(struct nx_sg);
+
+	if (!nx_ctx->op.outlen) {
+		rc = -EINVAL;
+		goto out;
+	}
+
+	rc = nx_hcall_sync(nx_ctx, &nx_ctx->op,
+			   desc->flags & CRYPTO_TFM_REQ_MAY_SLEEP);
+	if (rc)
+		goto out;
+
+	atomic_inc(&(nx_ctx->stats->sha256_ops));
+
+	atomic64_add(csbcpb->cpb.sha256.message_bit_length,
+		     &(nx_ctx->stats->sha256_bytes));
+	memcpy(out, csbcpb->cpb.sha256.message_digest, SHA256_DIGEST_SIZE);
+out:
+	return rc;
+}
+
+static int nx_sha256_export(struct shash_desc *desc, void *out)
+{
+	struct sha256_state *sctx = shash_desc_ctx(desc);
+	struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&desc->tfm->base);
+	struct nx_csbcpb *csbcpb = (struct nx_csbcpb *)nx_ctx->csbcpb;
+	struct sha256_state *octx = out;
+
+	octx->count = sctx->count +
+		      (csbcpb->cpb.sha256.message_bit_length / 8);
+	memcpy(octx->buf, sctx->buf, sizeof(octx->buf));
+
+	/* if no data has been processed yet, we need to export SHA256's
+	 * initial data, in case this context gets imported into a software
+	 * context */
+	if (csbcpb->cpb.sha256.message_bit_length)
+		memcpy(octx->state, csbcpb->cpb.sha256.message_digest,
+		       SHA256_DIGEST_SIZE);
+	else {
+		octx->state[0] = SHA256_H0;
+		octx->state[1] = SHA256_H1;
+		octx->state[2] = SHA256_H2;
+		octx->state[3] = SHA256_H3;
+		octx->state[4] = SHA256_H4;
+		octx->state[5] = SHA256_H5;
+		octx->state[6] = SHA256_H6;
+		octx->state[7] = SHA256_H7;
+	}
+
+	return 0;
+}
+
+static int nx_sha256_import(struct shash_desc *desc, const void *in)
+{
+	struct sha256_state *sctx = shash_desc_ctx(desc);
+	struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&desc->tfm->base);
+	struct nx_csbcpb *csbcpb = (struct nx_csbcpb *)nx_ctx->csbcpb;
+	const struct sha256_state *ictx = in;
+
+	memcpy(sctx->buf, ictx->buf, sizeof(ictx->buf));
+
+	sctx->count = ictx->count & 0x3f;
+	csbcpb->cpb.sha256.message_bit_length = (ictx->count & ~0x3f) * 8;
+
+	if (csbcpb->cpb.sha256.message_bit_length) {
+		memcpy(csbcpb->cpb.sha256.message_digest, ictx->state,
+		       SHA256_DIGEST_SIZE);
+
+		NX_CPB_FDM(csbcpb) |= NX_FDM_CONTINUATION;
+		NX_CPB_FDM(csbcpb) |= NX_FDM_INTERMEDIATE;
+	}
+
+	return 0;
+}
+
+struct shash_alg nx_shash_sha256_alg = {
+	.digestsize = SHA256_DIGEST_SIZE,
+	.init       = nx_sha256_init,
+	.update     = nx_sha256_update,
+	.final      = nx_sha256_final,
+	.export     = nx_sha256_export,
+	.import     = nx_sha256_import,
+	.descsize   = sizeof(struct sha256_state),
+	.statesize  = sizeof(struct sha256_state),
+	.base       = {
+		.cra_name        = "sha256",
+		.cra_driver_name = "sha256-nx",
+		.cra_priority    = 300,
+		.cra_flags       = CRYPTO_ALG_TYPE_SHASH,
+		.cra_blocksize   = SHA256_BLOCK_SIZE,
+		.cra_module      = THIS_MODULE,
+		.cra_ctxsize     = sizeof(struct nx_crypto_ctx),
+		.cra_init        = nx_crypto_ctx_sha_init,
+		.cra_exit        = nx_crypto_ctx_exit,
+	}
+};
diff --git a/drivers/crypto/nx/nx-sha512.c b/drivers/crypto/nx/nx-sha512.c
new file mode 100644
index 0000000..3177b8c
--- /dev/null
+++ b/drivers/crypto/nx/nx-sha512.c
@@ -0,0 +1,265 @@
+/**
+ * SHA-512 routines supporting the Power 7+ Nest Accelerators driver
+ *
+ * Copyright (C) 2011-2012 International Business Machines Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 only.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Kent Yoder <yoder1@us.ibm.com>
+ */
+
+#include <crypto/internal/hash.h>
+#include <crypto/sha.h>
+#include <linux/module.h>
+#include <asm/vio.h>
+
+#include "nx_csbcpb.h"
+#include "nx.h"
+
+
+static int nx_sha512_init(struct shash_desc *desc)
+{
+	struct sha512_state *sctx = shash_desc_ctx(desc);
+	struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&desc->tfm->base);
+	struct nx_sg *out_sg;
+
+	nx_ctx_init(nx_ctx, HCOP_FC_SHA);
+
+	memset(sctx, 0, sizeof *sctx);
+
+	nx_ctx->ap = &nx_ctx->props[NX_PROPS_SHA512];
+
+	NX_CPB_SET_DIGEST_SIZE(nx_ctx->csbcpb, NX_DS_SHA512);
+	out_sg = nx_build_sg_list(nx_ctx->out_sg, (u8 *)sctx->state,
+				  SHA512_DIGEST_SIZE, nx_ctx->ap->sglen);
+	nx_ctx->op.outlen = (nx_ctx->out_sg - out_sg) * sizeof(struct nx_sg);
+
+	return 0;
+}
+
+static int nx_sha512_update(struct shash_desc *desc, const u8 *data,
+			    unsigned int len)
+{
+	struct sha512_state *sctx = shash_desc_ctx(desc);
+	struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&desc->tfm->base);
+	struct nx_csbcpb *csbcpb = (struct nx_csbcpb *)nx_ctx->csbcpb;
+	struct nx_sg *in_sg;
+	u64 to_process, leftover, spbc_bits;
+	int rc = 0;
+
+	if (NX_CPB_FDM(csbcpb) & NX_FDM_CONTINUATION) {
+		/* we've hit the nx chip previously and we're updating again,
+		 * so copy over the partial digest */
+		memcpy(csbcpb->cpb.sha512.input_partial_digest,
+		       csbcpb->cpb.sha512.message_digest, SHA512_DIGEST_SIZE);
+	}
+
+	/* 2 cases for total data len:
+	 *  1: <= SHA512_BLOCK_SIZE: copy into state, return 0
+	 *  2: > SHA512_BLOCK_SIZE: process X blocks, copy in leftover
+	 */
+	if ((u64)len + sctx->count[0] <= SHA512_BLOCK_SIZE) {
+		memcpy(sctx->buf + sctx->count[0], data, len);
+		sctx->count[0] += len;
+		goto out;
+	}
+
+	/* to_process: the SHA512_BLOCK_SIZE data chunk to process in this
+	 * update */
+	to_process = (sctx->count[0] + len) & ~(SHA512_BLOCK_SIZE - 1);
+	leftover = (sctx->count[0] + len) & (SHA512_BLOCK_SIZE - 1);
+
+	if (sctx->count[0]) {
+		in_sg = nx_build_sg_list(nx_ctx->in_sg, (u8 *)sctx->buf,
+					 sctx->count[0], nx_ctx->ap->sglen);
+		in_sg = nx_build_sg_list(in_sg, (u8 *)data,
+					 to_process - sctx->count[0],
+					 nx_ctx->ap->sglen);
+		nx_ctx->op.inlen = (nx_ctx->in_sg - in_sg) *
+					sizeof(struct nx_sg);
+	} else {
+		in_sg = nx_build_sg_list(nx_ctx->in_sg, (u8 *)data,
+					 to_process, nx_ctx->ap->sglen);
+		nx_ctx->op.inlen = (nx_ctx->in_sg - in_sg) *
+					sizeof(struct nx_sg);
+	}
+
+	NX_CPB_FDM(csbcpb) |= NX_FDM_INTERMEDIATE;
+
+	if (!nx_ctx->op.inlen || !nx_ctx->op.outlen) {
+		rc = -EINVAL;
+		goto out;
+	}
+
+	rc = nx_hcall_sync(nx_ctx, &nx_ctx->op,
+			   desc->flags & CRYPTO_TFM_REQ_MAY_SLEEP);
+	if (rc)
+		goto out;
+
+	atomic_inc(&(nx_ctx->stats->sha512_ops));
+
+	/* copy the leftover back into the state struct */
+	memcpy(sctx->buf, data + len - leftover, leftover);
+	sctx->count[0] = leftover;
+
+	spbc_bits = csbcpb->cpb.sha512.spbc * 8;
+	csbcpb->cpb.sha512.message_bit_length_lo += spbc_bits;
+	if (csbcpb->cpb.sha512.message_bit_length_lo < spbc_bits)
+		csbcpb->cpb.sha512.message_bit_length_hi++;
+
+	/* everything after the first update is continuation */
+	NX_CPB_FDM(csbcpb) |= NX_FDM_CONTINUATION;
+out:
+	return rc;
+}
+
+static int nx_sha512_final(struct shash_desc *desc, u8 *out)
+{
+	struct sha512_state *sctx = shash_desc_ctx(desc);
+	struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&desc->tfm->base);
+	struct nx_csbcpb *csbcpb = (struct nx_csbcpb *)nx_ctx->csbcpb;
+	struct nx_sg *in_sg, *out_sg;
+	u64 count0;
+	int rc;
+
+	if (NX_CPB_FDM(csbcpb) & NX_FDM_CONTINUATION) {
+		/* we've hit the nx chip previously, now we're finalizing,
+		 * so copy over the partial digest */
+		memcpy(csbcpb->cpb.sha512.input_partial_digest,
+		       csbcpb->cpb.sha512.message_digest, SHA512_DIGEST_SIZE);
+	}
+
+	/* final is represented by continuing the operation and indicating that
+	 * this is not an intermediate operation */
+	NX_CPB_FDM(csbcpb) &= ~NX_FDM_INTERMEDIATE;
+
+	count0 = sctx->count[0] * 8;
+
+	csbcpb->cpb.sha512.message_bit_length_lo += count0;
+	if (csbcpb->cpb.sha512.message_bit_length_lo < count0)
+		csbcpb->cpb.sha512.message_bit_length_hi++;
+
+	in_sg = nx_build_sg_list(nx_ctx->in_sg, sctx->buf, sctx->count[0],
+				 nx_ctx->ap->sglen);
+	out_sg = nx_build_sg_list(nx_ctx->out_sg, out, SHA512_DIGEST_SIZE,
+				  nx_ctx->ap->sglen);
+	nx_ctx->op.inlen = (nx_ctx->in_sg - in_sg) * sizeof(struct nx_sg);
+	nx_ctx->op.outlen = (nx_ctx->out_sg - out_sg) * sizeof(struct nx_sg);
+
+	if (!nx_ctx->op.outlen) {
+		rc = -EINVAL;
+		goto out;
+	}
+
+	rc = nx_hcall_sync(nx_ctx, &nx_ctx->op,
+			   desc->flags & CRYPTO_TFM_REQ_MAY_SLEEP);
+	if (rc)
+		goto out;
+
+	atomic_inc(&(nx_ctx->stats->sha512_ops));
+	atomic64_add(csbcpb->cpb.sha512.message_bit_length_lo,
+		     &(nx_ctx->stats->sha512_bytes));
+
+	memcpy(out, csbcpb->cpb.sha512.message_digest, SHA512_DIGEST_SIZE);
+out:
+	return rc;
+}
+
+static int nx_sha512_export(struct shash_desc *desc, void *out)
+{
+	struct sha512_state *sctx = shash_desc_ctx(desc);
+	struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&desc->tfm->base);
+	struct nx_csbcpb *csbcpb = (struct nx_csbcpb *)nx_ctx->csbcpb;
+	struct sha512_state *octx = out;
+
+	/* move message_bit_length (128 bits) into count and convert its value
+	 * to bytes */
+	octx->count[0] = csbcpb->cpb.sha512.message_bit_length_lo >> 3 |
+			 ((csbcpb->cpb.sha512.message_bit_length_hi & 7) << 61);
+	octx->count[1] = csbcpb->cpb.sha512.message_bit_length_hi >> 3;
+
+	octx->count[0] += sctx->count[0];
+	if (octx->count[0] < sctx->count[0])
+		octx->count[1]++;
+
+	memcpy(octx->buf, sctx->buf, sizeof(octx->buf));
+
+	/* if no data has been processed yet, we need to export SHA512's
+	 * initial data, in case this context gets imported into a software
+	 * context */
+	if (csbcpb->cpb.sha512.message_bit_length_hi ||
+	    csbcpb->cpb.sha512.message_bit_length_lo)
+		memcpy(octx->state, csbcpb->cpb.sha512.message_digest,
+		       SHA512_DIGEST_SIZE);
+	else {
+		octx->state[0] = SHA512_H0;
+		octx->state[1] = SHA512_H1;
+		octx->state[2] = SHA512_H2;
+		octx->state[3] = SHA512_H3;
+		octx->state[4] = SHA512_H4;
+		octx->state[5] = SHA512_H5;
+		octx->state[6] = SHA512_H6;
+		octx->state[7] = SHA512_H7;
+	}
+
+	return 0;
+}
+
+static int nx_sha512_import(struct shash_desc *desc, const void *in)
+{
+	struct sha512_state *sctx = shash_desc_ctx(desc);
+	struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&desc->tfm->base);
+	struct nx_csbcpb *csbcpb = (struct nx_csbcpb *)nx_ctx->csbcpb;
+	const struct sha512_state *ictx = in;
+
+	memcpy(sctx->buf, ictx->buf, sizeof(ictx->buf));
+	sctx->count[0] = ictx->count[0] & 0x3f;
+	csbcpb->cpb.sha512.message_bit_length_lo = (ictx->count[0] & ~0x3f)
+							<< 3;
+	csbcpb->cpb.sha512.message_bit_length_hi = ictx->count[1] << 3 |
+						   ictx->count[0] >> 61;
+
+	if (csbcpb->cpb.sha512.message_bit_length_hi ||
+	    csbcpb->cpb.sha512.message_bit_length_lo) {
+		memcpy(csbcpb->cpb.sha512.message_digest, ictx->state,
+		       SHA512_DIGEST_SIZE);
+
+		NX_CPB_FDM(csbcpb) |= NX_FDM_CONTINUATION;
+		NX_CPB_FDM(csbcpb) |= NX_FDM_INTERMEDIATE;
+	}
+
+	return 0;
+}
+
+struct shash_alg nx_shash_sha512_alg = {
+	.digestsize = SHA512_DIGEST_SIZE,
+	.init       = nx_sha512_init,
+	.update     = nx_sha512_update,
+	.final      = nx_sha512_final,
+	.export     = nx_sha512_export,
+	.import     = nx_sha512_import,
+	.descsize   = sizeof(struct sha512_state),
+	.statesize  = sizeof(struct sha512_state),
+	.base       = {
+		.cra_name        = "sha512",
+		.cra_driver_name = "sha512-nx",
+		.cra_priority    = 300,
+		.cra_flags       = CRYPTO_ALG_TYPE_SHASH,
+		.cra_blocksize   = SHA512_BLOCK_SIZE,
+		.cra_module      = THIS_MODULE,
+		.cra_ctxsize     = sizeof(struct nx_crypto_ctx),
+		.cra_init        = nx_crypto_ctx_sha_init,
+		.cra_exit        = nx_crypto_ctx_exit,
+	}
+};
diff --git a/drivers/crypto/nx/nx.c b/drivers/crypto/nx/nx.c
new file mode 100644
index 0000000..d7f179c
--- /dev/null
+++ b/drivers/crypto/nx/nx.c
@@ -0,0 +1,716 @@
+/**
+ * Routines supporting the Power 7+ Nest Accelerators driver
+ *
+ * Copyright (C) 2011-2012 International Business Machines Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 only.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Kent Yoder <yoder1@us.ibm.com>
+ */
+
+#include <crypto/internal/hash.h>
+#include <crypto/hash.h>
+#include <crypto/aes.h>
+#include <crypto/sha.h>
+#include <crypto/algapi.h>
+#include <crypto/scatterwalk.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/crypto.h>
+#include <linux/scatterlist.h>
+#include <linux/device.h>
+#include <linux/of.h>
+#include <asm/pSeries_reconfig.h>
+#include <asm/abs_addr.h>
+#include <asm/hvcall.h>
+#include <asm/vio.h>
+
+#include "nx_csbcpb.h"
+#include "nx.h"
+
+
+/**
+ * nx_hcall_sync - make an H_COP_OP hcall for the passed in op structure
+ *
+ * @nx_ctx: the crypto context handle
+ * @op: PFO operation struct to pass in
+ * @may_sleep: flag indicating the request can sleep
+ *
+ * Make the hcall, retrying while the hardware is busy. If we cannot yield
+ * the thread, limit the number of retries to 10 here.
+ */
+int nx_hcall_sync(struct nx_crypto_ctx *nx_ctx,
+		  struct vio_pfo_op    *op,
+		  u32                   may_sleep)
+{
+	int rc, retries = 10;
+	struct vio_dev *viodev = nx_driver.viodev;
+
+	atomic_inc(&(nx_ctx->stats->sync_ops));
+
+	do {
+		rc = vio_h_cop_sync(viodev, op);
+	} while ((rc == -EBUSY && !may_sleep && retries--) ||
+	         (rc == -EBUSY && may_sleep && cond_resched()));
+
+	if (rc) {
+		dev_dbg(&viodev->dev, "vio_h_cop_sync failed: rc: %d "
+			"hcall rc: %ld\n", rc, op->hcall_err);
+		atomic_inc(&(nx_ctx->stats->errors));
+		atomic_set(&(nx_ctx->stats->last_error), op->hcall_err);
+		atomic_set(&(nx_ctx->stats->last_error_pid), current->pid);
+	}
+
+	return rc;
+}
+
+/**
+ * nx_build_sg_list - build an NX scatter list describing a single  buffer
+ *
+ * @sg_head: pointer to the first scatter list element to build
+ * @start_addr: pointer to the linear buffer
+ * @len: length of the data at @start_addr
+ * @sgmax: the largest number of scatter list elements we're allowed to create
+ *
+ * This function will start writing nx_sg elements at @sg_head and keep
+ * writing them until all of the data from @start_addr is described or
+ * until sgmax elements have been written. Scatter list elements will be
+ * created such that none of the elements describes a buffer that crosses a 4K
+ * boundary.
+ */
+struct nx_sg *nx_build_sg_list(struct nx_sg *sg_head,
+			       u8           *start_addr,
+			       unsigned int  len,
+			       u32           sgmax)
+{
+	unsigned int sg_len = 0;
+	struct nx_sg *sg;
+	u64 sg_addr = (u64)start_addr;
+	u64 end_addr;
+
+	/* determine the start and end for this address range - slightly
+	 * different if this is in VMALLOC_REGION */
+	if (is_vmalloc_addr(start_addr))
+		sg_addr = phys_to_abs(page_to_phys(vmalloc_to_page(start_addr)))
+			  + offset_in_page(sg_addr);
+	else
+		sg_addr = virt_to_abs(sg_addr);
+
+	end_addr = sg_addr + len;
+
+	/* each iteration will write one struct nx_sg element and add the
+	 * length of data described by that element to sg_len. Once @len bytes
+	 * have been described (or @sgmax elements have been written), the
+	 * loop ends. min_t is used to ensure @end_addr falls on the same page
+	 * as sg_addr, if not, we need to create another nx_sg element for the
+	 * data on the next page */
+	for (sg = sg_head; sg_len < len; sg++) {
+		sg->addr = sg_addr;
+		sg_addr = min_t(u64, NX_PAGE_NUM(sg_addr + NX_PAGE_SIZE), end_addr);
+		sg->len = sg_addr - sg->addr;
+		sg_len += sg->len;
+
+		if ((sg - sg_head) == sgmax) {
+			pr_err("nx: scatter/gather list overflow, pid: %d\n",
+			       current->pid);
+			return NULL;
+		}
+	}
+
+	/* return the moved sg_head pointer */
+	return sg;
+}
+
+/**
+ * nx_walk_and_build - walk a linux scatterlist and build an nx scatterlist
+ *
+ * @nx_dst: pointer to the first nx_sg element to write
+ * @sglen: max number of nx_sg entries we're allowed to write
+ * @sg_src: pointer to the source linux scatterlist to walk
+ * @start: number of bytes to fast-forward past at the beginning of @sg_src
+ * @src_len: number of bytes to walk in @sg_src
+ */
+struct nx_sg *nx_walk_and_build(struct nx_sg       *nx_dst,
+				unsigned int        sglen,
+				struct scatterlist *sg_src,
+				unsigned int        start,
+				unsigned int        src_len)
+{
+	struct scatter_walk walk;
+	struct nx_sg *nx_sg = nx_dst;
+	unsigned int n, offset = 0, len = src_len;
+	char *dst;
+
+	/* we need to fast forward through @start bytes first */
+	for (;;) {
+		scatterwalk_start(&walk, sg_src);
+
+		if (start < offset + sg_src->length)
+			break;
+
+		offset += sg_src->length;
+		sg_src = scatterwalk_sg_next(sg_src);
+	}
+
+	/* start - offset is the number of bytes to advance in the scatterlist
+	 * element we're currently looking at */
+	scatterwalk_advance(&walk, start - offset);
+
+	while (len && nx_sg) {
+		n = scatterwalk_clamp(&walk, len);
+		if (!n) {
+			scatterwalk_start(&walk, sg_next(walk.sg));
+			n = scatterwalk_clamp(&walk, len);
+		}
+		dst = scatterwalk_map(&walk);
+
+		nx_sg = nx_build_sg_list(nx_sg, dst, n, sglen);
+		len -= n;
+
+		scatterwalk_unmap(dst);
+		scatterwalk_advance(&walk, n);
+		scatterwalk_done(&walk, SCATTERWALK_FROM_SG, len);
+	}
+
+	/* return the moved destination pointer */
+	return nx_sg;
+}
+
+/**
+ * nx_build_sg_lists - walk the input scatterlists and build arrays of NX
+ *                     scatterlists based on them.
+ *
+ * @nx_ctx: NX crypto context for the lists we're building
+ * @desc: the block cipher descriptor for the operation
+ * @dst: destination scatterlist
+ * @src: source scatterlist
+ * @nbytes: length of data described in the scatterlists
+ * @iv: destination for the iv data, if the algorithm requires it
+ *
+ * This is common code shared by all the AES algorithms. It uses the block
+ * cipher walk routines to traverse input and output scatterlists, building
+ * corresponding NX scatterlists
+ */
+int nx_build_sg_lists(struct nx_crypto_ctx  *nx_ctx,
+		      struct blkcipher_desc *desc,
+		      struct scatterlist    *dst,
+		      struct scatterlist    *src,
+		      unsigned int           nbytes,
+		      u8                    *iv)
+{
+	struct nx_sg *nx_insg = nx_ctx->in_sg;
+	struct nx_sg *nx_outsg = nx_ctx->out_sg;
+	struct blkcipher_walk walk;
+	int rc;
+
+	blkcipher_walk_init(&walk, dst, src, nbytes);
+	rc = blkcipher_walk_virt_block(desc, &walk, AES_BLOCK_SIZE);
+	if (rc)
+		goto out;
+
+	if (iv)
+		memcpy(iv, walk.iv, AES_BLOCK_SIZE);
+
+	while (walk.nbytes) {
+		nx_insg = nx_build_sg_list(nx_insg, walk.src.virt.addr,
+					   walk.nbytes, nx_ctx->ap->sglen);
+		nx_outsg = nx_build_sg_list(nx_outsg, walk.dst.virt.addr,
+					    walk.nbytes, nx_ctx->ap->sglen);
+
+		rc = blkcipher_walk_done(desc, &walk, 0);
+		if (rc)
+			break;
+	}
+
+	if (walk.nbytes) {
+		nx_insg = nx_build_sg_list(nx_insg, walk.src.virt.addr,
+					   walk.nbytes, nx_ctx->ap->sglen);
+		nx_outsg = nx_build_sg_list(nx_outsg, walk.dst.virt.addr,
+					    walk.nbytes, nx_ctx->ap->sglen);
+
+		rc = 0;
+	}
+
+	/* these lengths should be negative, which will indicate to phyp that
+	 * the input and output parameters are scatterlists, not linear
+	 * buffers */
+	nx_ctx->op.inlen = (nx_ctx->in_sg - nx_insg) * sizeof(struct nx_sg);
+	nx_ctx->op.outlen = (nx_ctx->out_sg - nx_outsg) * sizeof(struct nx_sg);
+out:
+	return rc;
+}
+
+/**
+ * nx_ctx_init - initialize an nx_ctx's vio_pfo_op struct
+ *
+ * @nx_ctx: the nx context to initialize
+ * @function: the function code for the op
+ */
+void nx_ctx_init(struct nx_crypto_ctx *nx_ctx, unsigned int function)
+{
+	memset(nx_ctx->kmem, 0, nx_ctx->kmem_len);
+	nx_ctx->csbcpb->csb.valid |= NX_CSB_VALID_BIT;
+
+	nx_ctx->op.flags = function;
+	nx_ctx->op.csbcpb = virt_to_abs(nx_ctx->csbcpb);
+	nx_ctx->op.in = virt_to_abs(nx_ctx->in_sg);
+	nx_ctx->op.out = virt_to_abs(nx_ctx->out_sg);
+
+	if (nx_ctx->csbcpb_aead) {
+		nx_ctx->csbcpb_aead->csb.valid |= NX_CSB_VALID_BIT;
+
+		nx_ctx->op_aead.flags = function;
+		nx_ctx->op_aead.csbcpb = virt_to_abs(nx_ctx->csbcpb_aead);
+		nx_ctx->op_aead.in = virt_to_abs(nx_ctx->in_sg);
+		nx_ctx->op_aead.out = virt_to_abs(nx_ctx->out_sg);
+	}
+}
+
+static void nx_of_update_status(struct device   *dev,
+			       struct property *p,
+			       struct nx_of    *props)
+{
+	if (!strncmp(p->value, "okay", p->length)) {
+		props->status = NX_WAITING;
+		props->flags |= NX_OF_FLAG_STATUS_SET;
+	} else {
+		dev_info(dev, "%s: status '%s' is not 'okay'\n", __func__,
+			 (char *)p->value);
+	}
+}
+
+static void nx_of_update_sglen(struct device   *dev,
+			       struct property *p,
+			       struct nx_of    *props)
+{
+	if (p->length != sizeof(props->max_sg_len)) {
+		dev_err(dev, "%s: unexpected format for "
+			"ibm,max-sg-len property\n", __func__);
+		dev_dbg(dev, "%s: ibm,max-sg-len is %d bytes "
+			"long, expected %zd bytes\n", __func__,
+			p->length, sizeof(props->max_sg_len));
+		return;
+	}
+
+	props->max_sg_len = *(u32 *)p->value;
+	props->flags |= NX_OF_FLAG_MAXSGLEN_SET;
+}
+
+static void nx_of_update_msc(struct device   *dev,
+			     struct property *p,
+			     struct nx_of    *props)
+{
+	struct msc_triplet *trip;
+	struct max_sync_cop *msc;
+	unsigned int bytes_so_far, i, lenp;
+
+	msc = (struct max_sync_cop *)p->value;
+	lenp = p->length;
+
+	/* You can't tell if the data read in for this property is sane by its
+	 * size alone. This is because there are sizes embedded in the data
+	 * structure. The best we can do is check lengths as we parse and bail
+	 * as soon as a length error is detected. */
+	bytes_so_far = 0;
+
+	while ((bytes_so_far + sizeof(struct max_sync_cop)) <= lenp) {
+		bytes_so_far += sizeof(struct max_sync_cop);
+
+		trip = msc->trip;
+
+		for (i = 0;
+		     ((bytes_so_far + sizeof(struct msc_triplet)) <= lenp) &&
+		     i < msc->triplets;
+		     i++) {
+			if (msc->fc > NX_MAX_FC || msc->mode > NX_MAX_MODE) {
+				dev_err(dev, "unknown function code/mode "
+					"combo: %d/%d (ignored)\n", msc->fc,
+					msc->mode);
+				goto next_loop;
+			}
+
+			switch (trip->keybitlen) {
+			case 128:
+			case 160:
+				props->ap[msc->fc][msc->mode][0].databytelen =
+					trip->databytelen;
+				props->ap[msc->fc][msc->mode][0].sglen =
+					trip->sglen;
+				break;
+			case 192:
+				props->ap[msc->fc][msc->mode][1].databytelen =
+					trip->databytelen;
+				props->ap[msc->fc][msc->mode][1].sglen =
+					trip->sglen;
+				break;
+			case 256:
+				if (msc->fc == NX_FC_AES) {
+					props->ap[msc->fc][msc->mode][2].
+						databytelen = trip->databytelen;
+					props->ap[msc->fc][msc->mode][2].sglen =
+						trip->sglen;
+				} else if (msc->fc == NX_FC_AES_HMAC ||
+					   msc->fc == NX_FC_SHA) {
+					props->ap[msc->fc][msc->mode][1].
+						databytelen = trip->databytelen;
+					props->ap[msc->fc][msc->mode][1].sglen =
+						trip->sglen;
+				} else {
+					dev_warn(dev, "unknown function "
+						"code/key bit len combo"
+						": (%u/256)\n", msc->fc);
+				}
+				break;
+			case 512:
+				props->ap[msc->fc][msc->mode][2].databytelen =
+					trip->databytelen;
+				props->ap[msc->fc][msc->mode][2].sglen =
+					trip->sglen;
+				break;
+			default:
+				dev_warn(dev, "unknown function code/key bit "
+					 "len combo: (%u/%u)\n", msc->fc,
+					 trip->keybitlen);
+				break;
+			}
+next_loop:
+			bytes_so_far += sizeof(struct msc_triplet);
+			trip++;
+		}
+
+		msc = (struct max_sync_cop *)trip;
+	}
+
+	props->flags |= NX_OF_FLAG_MAXSYNCCOP_SET;
+}
+
+/**
+ * nx_of_init - read openFirmware values from the device tree
+ *
+ * @dev: device handle
+ * @props: pointer to struct to hold the properties values
+ *
+ * Called once at driver probe time, this function will read out the
+ * openFirmware properties we use at runtime. If all the OF properties are
+ * acceptable, when we exit this function props->flags will indicate that
+ * we're ready to register our crypto algorithms.
+ */
+static void nx_of_init(struct device *dev, struct nx_of *props)
+{
+	struct device_node *base_node = dev->of_node;
+	struct property *p;
+
+	p = of_find_property(base_node, "status", NULL);
+	if (!p)
+		dev_info(dev, "%s: property 'status' not found\n", __func__);
+	else
+		nx_of_update_status(dev, p, props);
+
+	p = of_find_property(base_node, "ibm,max-sg-len", NULL);
+	if (!p)
+		dev_info(dev, "%s: property 'ibm,max-sg-len' not found\n",
+			 __func__);
+	else
+		nx_of_update_sglen(dev, p, props);
+
+	p = of_find_property(base_node, "ibm,max-sync-cop", NULL);
+	if (!p)
+		dev_info(dev, "%s: property 'ibm,max-sync-cop' not found\n",
+			 __func__);
+	else
+		nx_of_update_msc(dev, p, props);
+}
+
+/**
+ * nx_register_algs - register algorithms with the crypto API
+ *
+ * Called from nx_probe()
+ *
+ * If all OF properties are in an acceptable state, the driver flags will
+ * indicate that we're ready and we'll create our debugfs files and register
+ * out crypto algorithms.
+ */
+static int nx_register_algs(void)
+{
+	int rc = -1;
+
+	if (nx_driver.of.flags != NX_OF_FLAG_MASK_READY)
+		goto out;
+
+	memset(&nx_driver.stats, 0, sizeof(struct nx_stats));
+
+	rc = NX_DEBUGFS_INIT(&nx_driver);
+	if (rc)
+		goto out;
+
+	rc = crypto_register_alg(&nx_ecb_aes_alg);
+	if (rc)
+		goto out;
+
+	rc = crypto_register_alg(&nx_cbc_aes_alg);
+	if (rc)
+		goto out_unreg_ecb;
+
+	rc = crypto_register_alg(&nx_ctr_aes_alg);
+	if (rc)
+		goto out_unreg_cbc;
+
+	rc = crypto_register_alg(&nx_ctr3686_aes_alg);
+	if (rc)
+		goto out_unreg_ctr;
+
+	rc = crypto_register_alg(&nx_gcm_aes_alg);
+	if (rc)
+		goto out_unreg_ctr3686;
+
+	rc = crypto_register_alg(&nx_gcm4106_aes_alg);
+	if (rc)
+		goto out_unreg_gcm;
+
+	rc = crypto_register_alg(&nx_ccm_aes_alg);
+	if (rc)
+		goto out_unreg_gcm4106;
+
+	rc = crypto_register_alg(&nx_ccm4309_aes_alg);
+	if (rc)
+		goto out_unreg_ccm;
+
+	rc = crypto_register_shash(&nx_shash_sha256_alg);
+	if (rc)
+		goto out_unreg_ccm4309;
+
+	rc = crypto_register_shash(&nx_shash_sha512_alg);
+	if (rc)
+		goto out_unreg_s256;
+
+	rc = crypto_register_shash(&nx_shash_aes_xcbc_alg);
+	if (rc)
+		goto out_unreg_s512;
+
+	nx_driver.of.status = NX_OKAY;
+
+	goto out;
+
+out_unreg_s512:
+	crypto_unregister_shash(&nx_shash_sha512_alg);
+out_unreg_s256:
+	crypto_unregister_shash(&nx_shash_sha256_alg);
+out_unreg_ccm4309:
+	crypto_unregister_alg(&nx_ccm4309_aes_alg);
+out_unreg_ccm:
+	crypto_unregister_alg(&nx_ccm_aes_alg);
+out_unreg_gcm4106:
+	crypto_unregister_alg(&nx_gcm4106_aes_alg);
+out_unreg_gcm:
+	crypto_unregister_alg(&nx_gcm_aes_alg);
+out_unreg_ctr3686:
+	crypto_unregister_alg(&nx_ctr3686_aes_alg);
+out_unreg_ctr:
+	crypto_unregister_alg(&nx_ctr_aes_alg);
+out_unreg_cbc:
+	crypto_unregister_alg(&nx_cbc_aes_alg);
+out_unreg_ecb:
+	crypto_unregister_alg(&nx_ecb_aes_alg);
+out:
+	return rc;
+}
+
+/**
+ * nx_crypto_ctx_init - create and initialize a crypto api context
+ *
+ * @nx_ctx: the crypto api context
+ * @fc: function code for the context
+ * @mode: the function code specific mode for this context
+ */
+static int nx_crypto_ctx_init(struct nx_crypto_ctx *nx_ctx, u32 fc, u32 mode)
+{
+	if (nx_driver.of.status != NX_OKAY) {
+		pr_err("Attempt to initialize NX crypto context while device "
+		       "is not available!\n");
+		return -ENODEV;
+	}
+
+	/* we need an extra page for csbcpb_aead for these modes */
+	if (mode == NX_MODE_AES_GCM || mode == NX_MODE_AES_CCM)
+		nx_ctx->kmem_len = (4 * NX_PAGE_SIZE) +
+				   sizeof(struct nx_csbcpb);
+	else
+		nx_ctx->kmem_len = (3 * NX_PAGE_SIZE) +
+				   sizeof(struct nx_csbcpb);
+
+	nx_ctx->kmem = kmalloc(nx_ctx->kmem_len, GFP_KERNEL);
+	if (!nx_ctx->kmem)
+		return -ENOMEM;
+
+	/* the csbcpb and scatterlists must be 4K aligned pages */
+	nx_ctx->csbcpb = (struct nx_csbcpb *)(round_up((u64)nx_ctx->kmem,
+						       (u64)NX_PAGE_SIZE));
+	nx_ctx->in_sg = (struct nx_sg *)((u8 *)nx_ctx->csbcpb + NX_PAGE_SIZE);
+	nx_ctx->out_sg = (struct nx_sg *)((u8 *)nx_ctx->in_sg + NX_PAGE_SIZE);
+
+	if (mode == NX_MODE_AES_GCM || mode == NX_MODE_AES_CCM)
+		nx_ctx->csbcpb_aead =
+			(struct nx_csbcpb *)((u8 *)nx_ctx->out_sg +
+					     NX_PAGE_SIZE);
+
+	/* give each context a pointer to global stats and their OF
+	 * properties */
+	nx_ctx->stats = &nx_driver.stats;
+	memcpy(nx_ctx->props, nx_driver.of.ap[fc][mode],
+	       sizeof(struct alg_props) * 3);
+
+	return 0;
+}
+
+/* entry points from the crypto tfm initializers */
+int nx_crypto_ctx_aes_ccm_init(struct crypto_tfm *tfm)
+{
+	return nx_crypto_ctx_init(crypto_tfm_ctx(tfm), NX_FC_AES,
+				  NX_MODE_AES_CCM);
+}
+
+int nx_crypto_ctx_aes_gcm_init(struct crypto_tfm *tfm)
+{
+	return nx_crypto_ctx_init(crypto_tfm_ctx(tfm), NX_FC_AES,
+				  NX_MODE_AES_GCM);
+}
+
+int nx_crypto_ctx_aes_ctr_init(struct crypto_tfm *tfm)
+{
+	return nx_crypto_ctx_init(crypto_tfm_ctx(tfm), NX_FC_AES,
+				  NX_MODE_AES_CTR);
+}
+
+int nx_crypto_ctx_aes_cbc_init(struct crypto_tfm *tfm)
+{
+	return nx_crypto_ctx_init(crypto_tfm_ctx(tfm), NX_FC_AES,
+				  NX_MODE_AES_CBC);
+}
+
+int nx_crypto_ctx_aes_ecb_init(struct crypto_tfm *tfm)
+{
+	return nx_crypto_ctx_init(crypto_tfm_ctx(tfm), NX_FC_AES,
+				  NX_MODE_AES_ECB);
+}
+
+int nx_crypto_ctx_sha_init(struct crypto_tfm *tfm)
+{
+	return nx_crypto_ctx_init(crypto_tfm_ctx(tfm), NX_FC_SHA, NX_MODE_SHA);
+}
+
+int nx_crypto_ctx_aes_xcbc_init(struct crypto_tfm *tfm)
+{
+	return nx_crypto_ctx_init(crypto_tfm_ctx(tfm), NX_FC_AES,
+				  NX_MODE_AES_XCBC_MAC);
+}
+
+/**
+ * nx_crypto_ctx_exit - destroy a crypto api context
+ *
+ * @tfm: the crypto transform pointer for the context
+ *
+ * As crypto API contexts are destroyed, this exit hook is called to free the
+ * memory associated with it.
+ */
+void nx_crypto_ctx_exit(struct crypto_tfm *tfm)
+{
+	struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(tfm);
+
+	kzfree(nx_ctx->kmem);
+	nx_ctx->csbcpb = NULL;
+	nx_ctx->csbcpb_aead = NULL;
+	nx_ctx->in_sg = NULL;
+	nx_ctx->out_sg = NULL;
+}
+
+static int __devinit nx_probe(struct vio_dev *viodev,
+			      const struct vio_device_id *id)
+{
+	dev_dbg(&viodev->dev, "driver probed: %s resource id: 0x%x\n",
+		viodev->name, viodev->resource_id);
+
+	if (nx_driver.viodev) {
+		dev_err(&viodev->dev, "%s: Attempt to register more than one "
+			"instance of the hardware\n", __func__);
+		return -EINVAL;
+	}
+
+	nx_driver.viodev = viodev;
+
+	nx_of_init(&viodev->dev, &nx_driver.of);
+
+	return nx_register_algs();
+}
+
+static int __devexit nx_remove(struct vio_dev *viodev)
+{
+	dev_dbg(&viodev->dev, "entering nx_remove for UA 0x%x\n",
+		viodev->unit_address);
+
+	if (nx_driver.of.status == NX_OKAY) {
+		NX_DEBUGFS_FINI(&nx_driver);
+
+		crypto_unregister_alg(&nx_ccm_aes_alg);
+		crypto_unregister_alg(&nx_ccm4309_aes_alg);
+		crypto_unregister_alg(&nx_gcm_aes_alg);
+		crypto_unregister_alg(&nx_gcm4106_aes_alg);
+		crypto_unregister_alg(&nx_ctr_aes_alg);
+		crypto_unregister_alg(&nx_ctr3686_aes_alg);
+		crypto_unregister_alg(&nx_cbc_aes_alg);
+		crypto_unregister_alg(&nx_ecb_aes_alg);
+		crypto_unregister_shash(&nx_shash_sha256_alg);
+		crypto_unregister_shash(&nx_shash_sha512_alg);
+		crypto_unregister_shash(&nx_shash_aes_xcbc_alg);
+	}
+
+	return 0;
+}
+
+
+/* module wide initialization/cleanup */
+static int __init nx_init(void)
+{
+	return vio_register_driver(&nx_driver.viodriver);
+}
+
+static void __exit nx_fini(void)
+{
+	vio_unregister_driver(&nx_driver.viodriver);
+}
+
+static struct vio_device_id nx_crypto_driver_ids[] __devinitdata = {
+	{ "ibm,sym-encryption-v1", "ibm,sym-encryption" },
+	{ "", "" }
+};
+MODULE_DEVICE_TABLE(vio, nx_crypto_driver_ids);
+
+/* driver state structure */
+struct nx_crypto_driver nx_driver = {
+	.viodriver = {
+		.id_table = nx_crypto_driver_ids,
+		.probe = nx_probe,
+		.remove = nx_remove,
+		.name  = NX_NAME,
+	},
+};
+
+module_init(nx_init);
+module_exit(nx_fini);
+
+MODULE_AUTHOR("Kent Yoder <yoder1@us.ibm.com>");
+MODULE_DESCRIPTION(NX_STRING);
+MODULE_LICENSE("GPL");
+MODULE_VERSION(NX_VERSION);
diff --git a/drivers/crypto/nx/nx.h b/drivers/crypto/nx/nx.h
new file mode 100644
index 0000000..3232b18
--- /dev/null
+++ b/drivers/crypto/nx/nx.h
@@ -0,0 +1,193 @@
+
+#ifndef __NX_H__
+#define __NX_H__
+
+#define NX_NAME		"nx-crypto"
+#define NX_STRING	"IBM Power7+ Nest Accelerator Crypto Driver"
+#define NX_VERSION	"1.0"
+
+static const char nx_driver_string[] = NX_STRING;
+static const char nx_driver_version[] = NX_VERSION;
+
+/* a scatterlist in the format PHYP is expecting */
+struct nx_sg {
+	u64 addr;
+	u32 rsvd;
+	u32 len;
+} __attribute((packed));
+
+#define NX_PAGE_SIZE		(4096)
+#define NX_MAX_SG_ENTRIES	(NX_PAGE_SIZE/(sizeof(struct nx_sg)))
+
+enum nx_status {
+	NX_DISABLED,
+	NX_WAITING,
+	NX_OKAY
+};
+
+/* msc_triplet and max_sync_cop are used only to assist in parsing the
+ * openFirmware property */
+struct msc_triplet {
+	u32 keybitlen;
+	u32 databytelen;
+	u32 sglen;
+} __packed;
+
+struct max_sync_cop {
+	u32 fc;
+	u32 mode;
+	u32 triplets;
+	struct msc_triplet trip[0];
+} __packed;
+
+struct alg_props {
+	u32 databytelen;
+	u32 sglen;
+};
+
+#define NX_OF_FLAG_MAXSGLEN_SET		(1)
+#define NX_OF_FLAG_STATUS_SET		(2)
+#define NX_OF_FLAG_MAXSYNCCOP_SET	(4)
+#define NX_OF_FLAG_MASK_READY		(NX_OF_FLAG_MAXSGLEN_SET | \
+					 NX_OF_FLAG_STATUS_SET |   \
+					 NX_OF_FLAG_MAXSYNCCOP_SET)
+struct nx_of {
+	u32 flags;
+	u32 max_sg_len;
+	enum nx_status status;
+	struct alg_props ap[NX_MAX_FC][NX_MAX_MODE][3];
+};
+
+struct nx_stats {
+	atomic_t aes_ops;
+	atomic64_t aes_bytes;
+	atomic_t sha256_ops;
+	atomic64_t sha256_bytes;
+	atomic_t sha512_ops;
+	atomic64_t sha512_bytes;
+
+	atomic_t sync_ops;
+
+	atomic_t errors;
+	atomic_t last_error;
+	atomic_t last_error_pid;
+};
+
+struct nx_debugfs {
+	struct dentry *dfs_root;
+	struct dentry *dfs_aes_ops, *dfs_aes_bytes;
+	struct dentry *dfs_sha256_ops, *dfs_sha256_bytes;
+	struct dentry *dfs_sha512_ops, *dfs_sha512_bytes;
+	struct dentry *dfs_errors, *dfs_last_error, *dfs_last_error_pid;
+};
+
+struct nx_crypto_driver {
+	struct nx_stats    stats;
+	struct nx_of       of;
+	struct vio_dev    *viodev;
+	struct vio_driver  viodriver;
+	struct nx_debugfs  dfs;
+};
+
+#define NX_GCM4106_NONCE_LEN		(4)
+#define NX_GCM_CTR_OFFSET		(12)
+struct nx_gcm_priv {
+	u8 iv[16];
+	u8 iauth_tag[16];
+	u8 nonce[NX_GCM4106_NONCE_LEN];
+};
+
+#define NX_CCM_AES_KEY_LEN		(16)
+#define NX_CCM4309_AES_KEY_LEN		(19)
+#define NX_CCM4309_NONCE_LEN		(3)
+struct nx_ccm_priv {
+	u8 iv[16];
+	u8 b0[16];
+	u8 iauth_tag[16];
+	u8 oauth_tag[16];
+	u8 nonce[NX_CCM4309_NONCE_LEN];
+};
+
+struct nx_xcbc_priv {
+	u8 key[16];
+};
+
+struct nx_ctr_priv {
+	u8 iv[16];
+};
+
+struct nx_crypto_ctx {
+	void *kmem;		  /* unaligned, kmalloc'd buffer */
+	size_t kmem_len;	  /* length of kmem */
+	struct nx_csbcpb *csbcpb; /* aligned page given to phyp @ hcall time */
+	struct vio_pfo_op op;     /* operation struct with hcall parameters */
+	struct nx_csbcpb *csbcpb_aead; /* secondary csbcpb used by AEAD algs */
+	struct vio_pfo_op op_aead;/* operation struct for csbcpb_aead */
+
+	struct nx_sg *in_sg;      /* aligned pointer into kmem to an sg list */
+	struct nx_sg *out_sg;     /* aligned pointer into kmem to an sg list */
+
+	struct alg_props *ap;	  /* pointer into props based on our key size */
+	struct alg_props props[3];/* openFirmware properties for requests */
+	struct nx_stats *stats;   /* pointer into an nx_crypto_driver for stats
+				     reporting */
+
+	union {
+		struct nx_gcm_priv gcm;
+		struct nx_ccm_priv ccm;
+		struct nx_xcbc_priv xcbc;
+		struct nx_ctr_priv ctr;
+	} priv;
+};
+
+/* prototypes */
+int nx_crypto_ctx_aes_ccm_init(struct crypto_tfm *tfm);
+int nx_crypto_ctx_aes_gcm_init(struct crypto_tfm *tfm);
+int nx_crypto_ctx_aes_xcbc_init(struct crypto_tfm *tfm);
+int nx_crypto_ctx_aes_ctr_init(struct crypto_tfm *tfm);
+int nx_crypto_ctx_aes_cbc_init(struct crypto_tfm *tfm);
+int nx_crypto_ctx_aes_ecb_init(struct crypto_tfm *tfm);
+int nx_crypto_ctx_sha_init(struct crypto_tfm *tfm);
+void nx_crypto_ctx_exit(struct crypto_tfm *tfm);
+void nx_ctx_init(struct nx_crypto_ctx *nx_ctx, unsigned int function);
+int nx_hcall_sync(struct nx_crypto_ctx *ctx, struct vio_pfo_op *op,
+		  u32 may_sleep);
+struct nx_sg *nx_build_sg_list(struct nx_sg *, u8 *, unsigned int, u32);
+int nx_build_sg_lists(struct nx_crypto_ctx *, struct blkcipher_desc *,
+		      struct scatterlist *, struct scatterlist *, unsigned int,
+		      u8 *);
+struct nx_sg *nx_walk_and_build(struct nx_sg *, unsigned int,
+				struct scatterlist *, unsigned int,
+				unsigned int);
+
+#ifdef CONFIG_DEBUG_FS
+#define NX_DEBUGFS_INIT(drv)	nx_debugfs_init(drv)
+#define NX_DEBUGFS_FINI(drv)	nx_debugfs_fini(drv)
+
+int nx_debugfs_init(struct nx_crypto_driver *);
+void nx_debugfs_fini(struct nx_crypto_driver *);
+#else
+#define NX_DEBUGFS_INIT(drv)	(0)
+#define NX_DEBUGFS_FINI(drv)	(0)
+#endif
+
+#define NX_PAGE_NUM(x)		((u64)(x) & 0xfffffffffffff000ULL)
+
+extern struct crypto_alg nx_cbc_aes_alg;
+extern struct crypto_alg nx_ecb_aes_alg;
+extern struct crypto_alg nx_gcm_aes_alg;
+extern struct crypto_alg nx_gcm4106_aes_alg;
+extern struct crypto_alg nx_ctr_aes_alg;
+extern struct crypto_alg nx_ctr3686_aes_alg;
+extern struct crypto_alg nx_ccm_aes_alg;
+extern struct crypto_alg nx_ccm4309_aes_alg;
+extern struct shash_alg nx_shash_aes_xcbc_alg;
+extern struct shash_alg nx_shash_sha512_alg;
+extern struct shash_alg nx_shash_sha256_alg;
+
+extern struct nx_crypto_driver nx_driver;
+
+#define SCATTERWALK_TO_SG	1
+#define SCATTERWALK_FROM_SG	0
+
+#endif
diff --git a/drivers/crypto/nx/nx_csbcpb.h b/drivers/crypto/nx/nx_csbcpb.h
new file mode 100644
index 0000000..a304f956
--- /dev/null
+++ b/drivers/crypto/nx/nx_csbcpb.h
@@ -0,0 +1,205 @@
+
+#ifndef __NX_CSBCPB_H__
+#define __NX_CSBCPB_H__
+
+struct cop_symcpb_aes_ecb {
+	u8 key[32];
+	u8 __rsvd[80];
+} __packed;
+
+struct cop_symcpb_aes_cbc {
+	u8 iv[16];
+	u8 key[32];
+	u8 cv[16];
+	u32 spbc;
+	u8 __rsvd[44];
+} __packed;
+
+struct cop_symcpb_aes_gca {
+	u8 in_pat[16];
+	u8 key[32];
+	u8 out_pat[16];
+	u32 spbc;
+	u8 __rsvd[44];
+} __packed;
+
+struct cop_symcpb_aes_gcm {
+	u8 in_pat_or_aad[16];
+	u8 iv_or_cnt[16];
+	u64 bit_length_aad;
+	u64 bit_length_data;
+	u8 in_s0[16];
+	u8 key[32];
+	u8 __rsvd1[16];
+	u8 out_pat_or_mac[16];
+	u8 out_s0[16];
+	u8 out_cnt[16];
+	u32 spbc;
+	u8 __rsvd2[12];
+} __packed;
+
+struct cop_symcpb_aes_ctr {
+	u8 iv[16];
+	u8 key[32];
+	u8 cv[16];
+	u32 spbc;
+	u8 __rsvd2[44];
+} __packed;
+
+struct cop_symcpb_aes_cca {
+	u8 b0[16];
+	u8 b1[16];
+	u8 key[16];
+	u8 out_pat_or_b0[16];
+	u32 spbc;
+	u8 __rsvd[44];
+} __packed;
+
+struct cop_symcpb_aes_ccm {
+	u8 in_pat_or_b0[16];
+	u8 iv_or_ctr[16];
+	u8 in_s0[16];
+	u8 key[16];
+	u8 __rsvd1[48];
+	u8 out_pat_or_mac[16];
+	u8 out_s0[16];
+	u8 out_ctr[16];
+	u32 spbc;
+	u8 __rsvd2[12];
+} __packed;
+
+struct cop_symcpb_aes_xcbc {
+	u8 cv[16];
+	u8 key[16];
+	u8 __rsvd1[16];
+	u8 out_cv_mac[16];
+	u32 spbc;
+	u8 __rsvd2[44];
+} __packed;
+
+struct cop_symcpb_sha256 {
+	u64 message_bit_length;
+	u64 __rsvd1;
+	u8 input_partial_digest[32];
+	u8 message_digest[32];
+	u32 spbc;
+	u8 __rsvd2[44];
+} __packed;
+
+struct cop_symcpb_sha512 {
+	u64 message_bit_length_hi;
+	u64 message_bit_length_lo;
+	u8 input_partial_digest[64];
+	u8 __rsvd1[32];
+	u8 message_digest[64];
+	u32 spbc;
+	u8 __rsvd2[76];
+} __packed;
+
+#define NX_FDM_INTERMEDIATE		0x01
+#define NX_FDM_CONTINUATION		0x02
+#define NX_FDM_ENDE_ENCRYPT		0x80
+
+#define NX_CPB_FDM(c)			((c)->cpb.hdr.fdm)
+#define NX_CPB_KS_DS(c)			((c)->cpb.hdr.ks_ds)
+
+#define NX_CPB_KEY_SIZE(c)		(NX_CPB_KS_DS(c) >> 4)
+#define NX_CPB_SET_KEY_SIZE(c, x)	NX_CPB_KS_DS(c) |= ((x) << 4)
+#define NX_CPB_SET_DIGEST_SIZE(c, x)	NX_CPB_KS_DS(c) |= (x)
+
+struct cop_symcpb_header {
+	u8 mode;
+	u8 fdm;
+	u8 ks_ds;
+	u8 pad_byte;
+	u8 __rsvd[12];
+} __packed;
+
+struct cop_parameter_block {
+	struct cop_symcpb_header hdr;
+	union {
+		struct cop_symcpb_aes_ecb  aes_ecb;
+		struct cop_symcpb_aes_cbc  aes_cbc;
+		struct cop_symcpb_aes_gca  aes_gca;
+		struct cop_symcpb_aes_gcm  aes_gcm;
+		struct cop_symcpb_aes_cca  aes_cca;
+		struct cop_symcpb_aes_ccm  aes_ccm;
+		struct cop_symcpb_aes_ctr  aes_ctr;
+		struct cop_symcpb_aes_xcbc aes_xcbc;
+		struct cop_symcpb_sha256   sha256;
+		struct cop_symcpb_sha512   sha512;
+	};
+} __packed;
+
+#define NX_CSB_VALID_BIT	0x80
+
+/* co-processor status block */
+struct cop_status_block {
+	u8 valid;
+	u8 crb_seq_number;
+	u8 completion_code;
+	u8 completion_extension;
+	u32 processed_byte_count;
+	u64 address;
+} __packed;
+
+/* Nest accelerator workbook section 4.4 */
+struct nx_csbcpb {
+	unsigned char __rsvd[112];
+	struct cop_status_block csb;
+	struct cop_parameter_block cpb;
+} __packed;
+
+/* nx_csbcpb related definitions */
+#define NX_MODE_AES_ECB			0
+#define NX_MODE_AES_CBC			1
+#define NX_MODE_AES_GMAC		2
+#define NX_MODE_AES_GCA			3
+#define NX_MODE_AES_GCM			4
+#define NX_MODE_AES_CCA			5
+#define NX_MODE_AES_CCM			6
+#define NX_MODE_AES_CTR			7
+#define NX_MODE_AES_XCBC_MAC		20
+#define NX_MODE_SHA			0
+#define NX_MODE_SHA_HMAC		1
+#define NX_MODE_AES_CBC_HMAC_ETA	8
+#define NX_MODE_AES_CBC_HMAC_ATE	9
+#define NX_MODE_AES_CBC_HMAC_EAA	10
+#define NX_MODE_AES_CTR_HMAC_ETA	12
+#define NX_MODE_AES_CTR_HMAC_ATE	13
+#define NX_MODE_AES_CTR_HMAC_EAA	14
+
+#define NX_FDM_CI_FULL		0
+#define NX_FDM_CI_FIRST		1
+#define NX_FDM_CI_LAST		2
+#define NX_FDM_CI_MIDDLE	3
+
+#define NX_FDM_PR_NONE		0
+#define NX_FDM_PR_PAD		1
+
+#define NX_KS_AES_128		1
+#define NX_KS_AES_192		2
+#define NX_KS_AES_256		3
+
+#define NX_DS_SHA256		2
+#define NX_DS_SHA512		3
+
+#define NX_FC_AES		0
+#define NX_FC_SHA		2
+#define NX_FC_AES_HMAC		6
+
+#define NX_MAX_FC		(NX_FC_AES_HMAC + 1)
+#define NX_MAX_MODE		(NX_MODE_AES_XCBC_MAC + 1)
+
+#define HCOP_FC_AES          NX_FC_AES
+#define HCOP_FC_SHA          NX_FC_SHA
+#define HCOP_FC_AES_HMAC     NX_FC_AES_HMAC
+
+/* indices into the array of algorithm properties */
+#define NX_PROPS_AES_128		0
+#define NX_PROPS_AES_192		1
+#define NX_PROPS_AES_256		2
+#define NX_PROPS_SHA256			1
+#define NX_PROPS_SHA512			2
+
+#endif
diff --git a/drivers/crypto/nx/nx_debugfs.c b/drivers/crypto/nx/nx_debugfs.c
new file mode 100644
index 0000000..7ab2e8d
--- /dev/null
+++ b/drivers/crypto/nx/nx_debugfs.c
@@ -0,0 +1,103 @@
+/**
+ * debugfs routines supporting the Power 7+ Nest Accelerators driver
+ *
+ * Copyright (C) 2011-2012 International Business Machines Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 only.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Kent Yoder <yoder1@us.ibm.com>
+ */
+
+#include <linux/device.h>
+#include <linux/kobject.h>
+#include <linux/string.h>
+#include <linux/debugfs.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/crypto.h>
+#include <crypto/hash.h>
+#include <asm/vio.h>
+
+#include "nx_csbcpb.h"
+#include "nx.h"
+
+#ifdef CONFIG_DEBUG_FS
+
+/*
+ * debugfs
+ *
+ * For documentation on these attributes, please see:
+ *
+ * Documentation/ABI/testing/debugfs-pfo-nx-crypto
+ */
+
+int nx_debugfs_init(struct nx_crypto_driver *drv)
+{
+	struct nx_debugfs *dfs = &drv->dfs;
+
+	dfs->dfs_root = debugfs_create_dir(NX_NAME, NULL);
+
+	dfs->dfs_aes_ops =
+		debugfs_create_u32("aes_ops",
+				   S_IRUSR | S_IRGRP | S_IROTH,
+				   dfs->dfs_root, (u32 *)&drv->stats.aes_ops);
+	dfs->dfs_sha256_ops =
+		debugfs_create_u32("sha256_ops",
+				   S_IRUSR | S_IRGRP | S_IROTH,
+				   dfs->dfs_root,
+				   (u32 *)&drv->stats.sha256_ops);
+	dfs->dfs_sha512_ops =
+		debugfs_create_u32("sha512_ops",
+				   S_IRUSR | S_IRGRP | S_IROTH,
+				   dfs->dfs_root,
+				   (u32 *)&drv->stats.sha512_ops);
+	dfs->dfs_aes_bytes =
+		debugfs_create_u64("aes_bytes",
+				   S_IRUSR | S_IRGRP | S_IROTH,
+				   dfs->dfs_root,
+				   (u64 *)&drv->stats.aes_bytes);
+	dfs->dfs_sha256_bytes =
+		debugfs_create_u64("sha256_bytes",
+				   S_IRUSR | S_IRGRP | S_IROTH,
+				   dfs->dfs_root,
+				   (u64 *)&drv->stats.sha256_bytes);
+	dfs->dfs_sha512_bytes =
+		debugfs_create_u64("sha512_bytes",
+				   S_IRUSR | S_IRGRP | S_IROTH,
+				   dfs->dfs_root,
+				   (u64 *)&drv->stats.sha512_bytes);
+	dfs->dfs_errors =
+		debugfs_create_u32("errors",
+				   S_IRUSR | S_IRGRP | S_IROTH,
+				   dfs->dfs_root, (u32 *)&drv->stats.errors);
+	dfs->dfs_last_error =
+		debugfs_create_u32("last_error",
+				   S_IRUSR | S_IRGRP | S_IROTH,
+				   dfs->dfs_root,
+				   (u32 *)&drv->stats.last_error);
+	dfs->dfs_last_error_pid =
+		debugfs_create_u32("last_error_pid",
+				   S_IRUSR | S_IRGRP | S_IROTH,
+				   dfs->dfs_root,
+				   (u32 *)&drv->stats.last_error_pid);
+	return 0;
+}
+
+void
+nx_debugfs_fini(struct nx_crypto_driver *drv)
+{
+	debugfs_remove_recursive(drv->dfs.dfs_root);
+}
+
+#endif
diff --git a/drivers/crypto/ux500/Kconfig b/drivers/crypto/ux500/Kconfig
new file mode 100644
index 0000000..b35e5c4
--- /dev/null
+++ b/drivers/crypto/ux500/Kconfig
@@ -0,0 +1,30 @@
+#
+# Copyright (C) ST-Ericsson SA 2010
+# Author: Shujuan Chen (shujuan.chen@stericsson.com)
+# License terms: GNU General Public License (GPL) version 2
+#
+
+config CRYPTO_DEV_UX500_CRYP
+	tristate "UX500 crypto driver for CRYP block"
+	depends on CRYPTO_DEV_UX500
+	select CRYPTO_DES
+	help
+        This selects the crypto driver for the UX500_CRYP hardware. It supports
+        AES-ECB, CBC and CTR with keys sizes of 128, 192 and 256 bit sizes.
+
+config CRYPTO_DEV_UX500_HASH
+        tristate "UX500 crypto driver for HASH block"
+        depends on CRYPTO_DEV_UX500
+        select CRYPTO_HASH
+        select CRYPTO_HMAC
+        help
+          This selects the hash driver for the UX500_HASH hardware.
+          Depends on UX500/STM DMA if running in DMA mode.
+
+config CRYPTO_DEV_UX500_DEBUG
+	bool "Activate ux500 platform debug-mode for crypto and hash block"
+	depends on CRYPTO_DEV_UX500_CRYP || CRYPTO_DEV_UX500_HASH
+	default n
+	help
+	  Say Y if you want to add debug prints to ux500_hash and
+	  ux500_cryp devices.
diff --git a/drivers/crypto/ux500/Makefile b/drivers/crypto/ux500/Makefile
new file mode 100644
index 0000000..b9a365b
--- /dev/null
+++ b/drivers/crypto/ux500/Makefile
@@ -0,0 +1,8 @@
+#
+# Copyright (C) ST-Ericsson SA 2010
+# Author: Shujuan Chen (shujuan.chen@stericsson.com)
+# License terms: GNU General Public License (GPL) version 2
+#
+
+obj-$(CONFIG_CRYPTO_DEV_UX500_HASH) += hash/
+obj-$(CONFIG_CRYPTO_DEV_UX500_CRYP) += cryp/
diff --git a/drivers/crypto/ux500/cryp/Makefile b/drivers/crypto/ux500/cryp/Makefile
new file mode 100644
index 0000000..e5d362a
--- /dev/null
+++ b/drivers/crypto/ux500/cryp/Makefile
@@ -0,0 +1,13 @@
+#/*
+# * Copyright (C) ST-Ericsson SA 2010
+# * Author: shujuan.chen@stericsson.com for ST-Ericsson.
+# * License terms: GNU General Public License (GPL) version 2  */
+
+ifdef CONFIG_CRYPTO_DEV_UX500_DEBUG
+CFLAGS_cryp_core.o := -DDEBUG -O0
+CFLAGS_cryp.o := -DDEBUG -O0
+CFLAGS_cryp_irq.o := -DDEBUG -O0
+endif
+
+obj-$(CONFIG_CRYPTO_DEV_UX500_CRYP) += ux500_cryp.o
+ux500_cryp-objs :=  cryp.o cryp_irq.o cryp_core.o
diff --git a/drivers/crypto/ux500/cryp/cryp.c b/drivers/crypto/ux500/cryp/cryp.c
new file mode 100644
index 0000000..e208cea
--- /dev/null
+++ b/drivers/crypto/ux500/cryp/cryp.c
@@ -0,0 +1,389 @@
+/**
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Shujuan Chen <shujuan.chen@stericsson.com> for ST-Ericsson.
+ * Author: Jonas Linde <jonas.linde@stericsson.com> for ST-Ericsson.
+ * Author: Niklas Hernaeus <niklas.hernaeus@stericsson.com> for ST-Ericsson.
+ * Author: Joakim Bech <joakim.xx.bech@stericsson.com> for ST-Ericsson.
+ * Author: Berne Hebark <berne.herbark@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+
+#include <mach/hardware.h>
+
+#include "cryp_p.h"
+#include "cryp.h"
+
+/**
+ * cryp_wait_until_done - wait until the device logic is not busy
+ */
+void cryp_wait_until_done(struct cryp_device_data *device_data)
+{
+	while (cryp_is_logic_busy(device_data))
+		cpu_relax();
+}
+
+/**
+ * cryp_check - This routine checks Peripheral and PCell Id
+ * @device_data: Pointer to the device data struct for base address.
+ */
+int cryp_check(struct cryp_device_data *device_data)
+{
+	int peripheralid2 = 0;
+
+	if (NULL == device_data)
+		return -EINVAL;
+
+	peripheralid2 = readl_relaxed(&device_data->base->periphId2);
+
+	if (peripheralid2 != CRYP_PERIPHERAL_ID2_DB8500)
+		return -EPERM;
+
+	/* Check Peripheral and Pcell Id Register for CRYP */
+	if ((CRYP_PERIPHERAL_ID0 ==
+		readl_relaxed(&device_data->base->periphId0))
+	    && (CRYP_PERIPHERAL_ID1 ==
+		    readl_relaxed(&device_data->base->periphId1))
+	    && (CRYP_PERIPHERAL_ID3 ==
+		    readl_relaxed(&device_data->base->periphId3))
+	    && (CRYP_PCELL_ID0 ==
+		    readl_relaxed(&device_data->base->pcellId0))
+	    && (CRYP_PCELL_ID1 ==
+		    readl_relaxed(&device_data->base->pcellId1))
+	    && (CRYP_PCELL_ID2 ==
+		    readl_relaxed(&device_data->base->pcellId2))
+	    && (CRYP_PCELL_ID3 ==
+		    readl_relaxed(&device_data->base->pcellId3))) {
+		return 0;
+	}
+
+	return -EPERM;
+}
+
+/**
+ * cryp_activity - This routine enables/disable the cryptography function.
+ * @device_data: Pointer to the device data struct for base address.
+ * @cryp_crypen: Enable/Disable functionality
+ */
+void cryp_activity(struct cryp_device_data *device_data,
+		   enum cryp_crypen cryp_crypen)
+{
+	CRYP_PUT_BITS(&device_data->base->cr,
+		      cryp_crypen,
+		      CRYP_CR_CRYPEN_POS,
+		      CRYP_CR_CRYPEN_MASK);
+}
+
+/**
+ * cryp_flush_inoutfifo - Resets both the input and the output FIFOs
+ * @device_data: Pointer to the device data struct for base address.
+ */
+void cryp_flush_inoutfifo(struct cryp_device_data *device_data)
+{
+	/*
+	 * We always need to disble the hardware before trying to flush the
+	 * FIFO. This is something that isn't written in the design
+	 * specification, but we have been informed by the hardware designers
+	 * that this must be done.
+	 */
+	cryp_activity(device_data, CRYP_CRYPEN_DISABLE);
+	cryp_wait_until_done(device_data);
+
+	CRYP_SET_BITS(&device_data->base->cr, CRYP_CR_FFLUSH_MASK);
+	/*
+	 * CRYP_SR_INFIFO_READY_MASK is the expected value on the status
+	 * register when starting a new calculation, which means Input FIFO is
+	 * not full and input FIFO is empty.
+	 */
+	while (readl_relaxed(&device_data->base->sr) !=
+	       CRYP_SR_INFIFO_READY_MASK)
+		cpu_relax();
+}
+
+/**
+ * cryp_set_configuration - This routine set the cr CRYP IP
+ * @device_data: Pointer to the device data struct for base address.
+ * @cryp_config: Pointer to the configuration parameter
+ * @control_register: The control register to be written later on.
+ */
+int cryp_set_configuration(struct cryp_device_data *device_data,
+			   struct cryp_config *cryp_config,
+			   u32 *control_register)
+{
+	u32 cr_for_kse;
+
+	if (NULL == device_data || NULL == cryp_config)
+		return -EINVAL;
+
+	*control_register |= (cryp_config->keysize << CRYP_CR_KEYSIZE_POS);
+
+	/* Prepare key for decryption in AES_ECB and AES_CBC mode. */
+	if ((CRYP_ALGORITHM_DECRYPT == cryp_config->algodir) &&
+	    ((CRYP_ALGO_AES_ECB == cryp_config->algomode) ||
+	     (CRYP_ALGO_AES_CBC == cryp_config->algomode))) {
+		cr_for_kse = *control_register;
+		/*
+		 * This seems a bit odd, but it is indeed needed to set this to
+		 * encrypt even though it is a decryption that we are doing. It
+		 * also mentioned in the design spec that you need to do this.
+		 * After the keyprepartion for decrypting is done you should set
+		 * algodir back to decryption, which is done outside this if
+		 * statement.
+		 *
+		 * According to design specification we should set mode ECB
+		 * during key preparation even though we might be running CBC
+		 * when enter this function.
+		 *
+		 * Writing to KSE_ENABLED will drop CRYPEN when key preparation
+		 * is done. Therefore we need to set CRYPEN again outside this
+		 * if statement when running decryption.
+		 */
+		cr_for_kse |= ((CRYP_ALGORITHM_ENCRYPT << CRYP_CR_ALGODIR_POS) |
+			       (CRYP_ALGO_AES_ECB << CRYP_CR_ALGOMODE_POS) |
+			       (CRYP_CRYPEN_ENABLE << CRYP_CR_CRYPEN_POS) |
+			       (KSE_ENABLED << CRYP_CR_KSE_POS));
+
+		writel_relaxed(cr_for_kse, &device_data->base->cr);
+		cryp_wait_until_done(device_data);
+	}
+
+	*control_register |=
+		((cryp_config->algomode << CRYP_CR_ALGOMODE_POS) |
+		 (cryp_config->algodir << CRYP_CR_ALGODIR_POS));
+
+	return 0;
+}
+
+/**
+ * cryp_configure_protection - set the protection bits in the CRYP logic.
+ * @device_data: Pointer to the device data struct for base address.
+ * @p_protect_config:	Pointer to the protection mode and
+ *			secure mode configuration
+ */
+int cryp_configure_protection(struct cryp_device_data *device_data,
+			      struct cryp_protection_config *p_protect_config)
+{
+	if (NULL == p_protect_config)
+		return -EINVAL;
+
+	CRYP_WRITE_BIT(&device_data->base->cr,
+		       (u32) p_protect_config->secure_access,
+		       CRYP_CR_SECURE_MASK);
+	CRYP_PUT_BITS(&device_data->base->cr,
+		      p_protect_config->privilege_access,
+		      CRYP_CR_PRLG_POS,
+		      CRYP_CR_PRLG_MASK);
+
+	return 0;
+}
+
+/**
+ * cryp_is_logic_busy - returns the busy status of the CRYP logic
+ * @device_data: Pointer to the device data struct for base address.
+ */
+int cryp_is_logic_busy(struct cryp_device_data *device_data)
+{
+	return CRYP_TEST_BITS(&device_data->base->sr,
+			      CRYP_SR_BUSY_MASK);
+}
+
+/**
+ * cryp_configure_for_dma - configures the CRYP IP for DMA operation
+ * @device_data: Pointer to the device data struct for base address.
+ * @dma_req: Specifies the DMA request type value.
+ */
+void cryp_configure_for_dma(struct cryp_device_data *device_data,
+			    enum cryp_dma_req_type dma_req)
+{
+	CRYP_SET_BITS(&device_data->base->dmacr,
+		      (u32) dma_req);
+}
+
+/**
+ * cryp_configure_key_values - configures the key values for CRYP operations
+ * @device_data: Pointer to the device data struct for base address.
+ * @key_reg_index: Key value index register
+ * @key_value: The key value struct
+ */
+int cryp_configure_key_values(struct cryp_device_data *device_data,
+			      enum cryp_key_reg_index key_reg_index,
+			      struct cryp_key_value key_value)
+{
+	while (cryp_is_logic_busy(device_data))
+		cpu_relax();
+
+	switch (key_reg_index) {
+	case CRYP_KEY_REG_1:
+		writel_relaxed(key_value.key_value_left,
+				&device_data->base->key_1_l);
+		writel_relaxed(key_value.key_value_right,
+				&device_data->base->key_1_r);
+		break;
+	case CRYP_KEY_REG_2:
+		writel_relaxed(key_value.key_value_left,
+				&device_data->base->key_2_l);
+		writel_relaxed(key_value.key_value_right,
+				&device_data->base->key_2_r);
+		break;
+	case CRYP_KEY_REG_3:
+		writel_relaxed(key_value.key_value_left,
+				&device_data->base->key_3_l);
+		writel_relaxed(key_value.key_value_right,
+				&device_data->base->key_3_r);
+		break;
+	case CRYP_KEY_REG_4:
+		writel_relaxed(key_value.key_value_left,
+				&device_data->base->key_4_l);
+		writel_relaxed(key_value.key_value_right,
+				&device_data->base->key_4_r);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * cryp_configure_init_vector - configures the initialization vector register
+ * @device_data: Pointer to the device data struct for base address.
+ * @init_vector_index: Specifies the index of the init vector.
+ * @init_vector_value: Specifies the value for the init vector.
+ */
+int cryp_configure_init_vector(struct cryp_device_data *device_data,
+			       enum cryp_init_vector_index
+			       init_vector_index,
+			       struct cryp_init_vector_value
+			       init_vector_value)
+{
+	while (cryp_is_logic_busy(device_data))
+		cpu_relax();
+
+	switch (init_vector_index) {
+	case CRYP_INIT_VECTOR_INDEX_0:
+		writel_relaxed(init_vector_value.init_value_left,
+		       &device_data->base->init_vect_0_l);
+		writel_relaxed(init_vector_value.init_value_right,
+		       &device_data->base->init_vect_0_r);
+		break;
+	case CRYP_INIT_VECTOR_INDEX_1:
+		writel_relaxed(init_vector_value.init_value_left,
+		       &device_data->base->init_vect_1_l);
+		writel_relaxed(init_vector_value.init_value_right,
+		       &device_data->base->init_vect_1_r);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * cryp_save_device_context -	Store hardware registers and
+ *				other device context parameter
+ * @device_data: Pointer to the device data struct for base address.
+ * @ctx: Crypto device context
+ */
+void cryp_save_device_context(struct cryp_device_data *device_data,
+			      struct cryp_device_context *ctx,
+			      int cryp_mode)
+{
+	enum cryp_algo_mode algomode;
+	struct cryp_register *src_reg = device_data->base;
+	struct cryp_config *config =
+		(struct cryp_config *)device_data->current_ctx;
+
+	/*
+	 * Always start by disable the hardware and wait for it to finish the
+	 * ongoing calculations before trying to reprogram it.
+	 */
+	cryp_activity(device_data, CRYP_CRYPEN_DISABLE);
+	cryp_wait_until_done(device_data);
+
+	if (cryp_mode == CRYP_MODE_DMA)
+		cryp_configure_for_dma(device_data, CRYP_DMA_DISABLE_BOTH);
+
+	if (CRYP_TEST_BITS(&src_reg->sr, CRYP_SR_IFEM_MASK) == 0)
+		ctx->din = readl_relaxed(&src_reg->din);
+
+	ctx->cr = readl_relaxed(&src_reg->cr) & CRYP_CR_CONTEXT_SAVE_MASK;
+
+	switch (config->keysize) {
+	case CRYP_KEY_SIZE_256:
+		ctx->key_4_l = readl_relaxed(&src_reg->key_4_l);
+		ctx->key_4_r = readl_relaxed(&src_reg->key_4_r);
+
+	case CRYP_KEY_SIZE_192:
+		ctx->key_3_l = readl_relaxed(&src_reg->key_3_l);
+		ctx->key_3_r = readl_relaxed(&src_reg->key_3_r);
+
+	case CRYP_KEY_SIZE_128:
+		ctx->key_2_l = readl_relaxed(&src_reg->key_2_l);
+		ctx->key_2_r = readl_relaxed(&src_reg->key_2_r);
+
+	default:
+		ctx->key_1_l = readl_relaxed(&src_reg->key_1_l);
+		ctx->key_1_r = readl_relaxed(&src_reg->key_1_r);
+	}
+
+	/* Save IV for CBC mode for both AES and DES. */
+	algomode = ((ctx->cr & CRYP_CR_ALGOMODE_MASK) >> CRYP_CR_ALGOMODE_POS);
+	if (algomode == CRYP_ALGO_TDES_CBC ||
+	    algomode == CRYP_ALGO_DES_CBC ||
+	    algomode == CRYP_ALGO_AES_CBC) {
+		ctx->init_vect_0_l = readl_relaxed(&src_reg->init_vect_0_l);
+		ctx->init_vect_0_r = readl_relaxed(&src_reg->init_vect_0_r);
+		ctx->init_vect_1_l = readl_relaxed(&src_reg->init_vect_1_l);
+		ctx->init_vect_1_r = readl_relaxed(&src_reg->init_vect_1_r);
+	}
+}
+
+/**
+ * cryp_restore_device_context -	Restore hardware registers and
+ *					other device context parameter
+ * @device_data: Pointer to the device data struct for base address.
+ * @ctx: Crypto device context
+ */
+void cryp_restore_device_context(struct cryp_device_data *device_data,
+				 struct cryp_device_context *ctx)
+{
+	struct cryp_register *reg = device_data->base;
+	struct cryp_config *config =
+		(struct cryp_config *)device_data->current_ctx;
+
+	/*
+	 * Fall through for all items in switch statement. DES is captured in
+	 * the default.
+	 */
+	switch (config->keysize) {
+	case CRYP_KEY_SIZE_256:
+		writel_relaxed(ctx->key_4_l, &reg->key_4_l);
+		writel_relaxed(ctx->key_4_r, &reg->key_4_r);
+
+	case CRYP_KEY_SIZE_192:
+		writel_relaxed(ctx->key_3_l, &reg->key_3_l);
+		writel_relaxed(ctx->key_3_r, &reg->key_3_r);
+
+	case CRYP_KEY_SIZE_128:
+		writel_relaxed(ctx->key_2_l, &reg->key_2_l);
+		writel_relaxed(ctx->key_2_r, &reg->key_2_r);
+
+	default:
+		writel_relaxed(ctx->key_1_l, &reg->key_1_l);
+		writel_relaxed(ctx->key_1_r, &reg->key_1_r);
+	}
+
+	/* Restore IV for CBC mode for AES and DES. */
+	if (config->algomode == CRYP_ALGO_TDES_CBC ||
+	    config->algomode == CRYP_ALGO_DES_CBC ||
+	    config->algomode == CRYP_ALGO_AES_CBC) {
+		writel_relaxed(ctx->init_vect_0_l, &reg->init_vect_0_l);
+		writel_relaxed(ctx->init_vect_0_r, &reg->init_vect_0_r);
+		writel_relaxed(ctx->init_vect_1_l, &reg->init_vect_1_l);
+		writel_relaxed(ctx->init_vect_1_r, &reg->init_vect_1_r);
+	}
+}
diff --git a/drivers/crypto/ux500/cryp/cryp.h b/drivers/crypto/ux500/cryp/cryp.h
new file mode 100644
index 0000000..14cfd05
--- /dev/null
+++ b/drivers/crypto/ux500/cryp/cryp.h
@@ -0,0 +1,308 @@
+/**
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Shujuan Chen <shujuan.chen@stericsson.com> for ST-Ericsson.
+ * Author: Jonas Linde <jonas.linde@stericsson.com> for ST-Ericsson.
+ * Author: Joakim Bech <joakim.xx.bech@stericsson.com> for ST-Ericsson.
+ * Author: Berne Hebark <berne.herbark@stericsson.com> for ST-Ericsson.
+ * Author: Niklas Hernaeus <niklas.hernaeus@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef _CRYP_H_
+#define _CRYP_H_
+
+#include <linux/completion.h>
+#include <linux/dmaengine.h>
+#include <linux/klist.h>
+#include <linux/mutex.h>
+
+#define DEV_DBG_NAME "crypX crypX:"
+
+/* CRYP enable/disable */
+enum cryp_crypen {
+	CRYP_CRYPEN_DISABLE = 0,
+	CRYP_CRYPEN_ENABLE = 1
+};
+
+/* CRYP Start Computation enable/disable */
+enum cryp_start {
+	CRYP_START_DISABLE = 0,
+	CRYP_START_ENABLE = 1
+};
+
+/* CRYP Init Signal enable/disable */
+enum cryp_init {
+	CRYP_INIT_DISABLE = 0,
+	CRYP_INIT_ENABLE = 1
+};
+
+/* Cryp State enable/disable */
+enum cryp_state {
+	CRYP_STATE_DISABLE = 0,
+	CRYP_STATE_ENABLE = 1
+};
+
+/* Key preparation bit enable */
+enum cryp_key_prep {
+	KSE_DISABLED = 0,
+	KSE_ENABLED = 1
+};
+
+/* Key size for AES */
+#define	CRYP_KEY_SIZE_128 (0)
+#define	CRYP_KEY_SIZE_192 (1)
+#define	CRYP_KEY_SIZE_256 (2)
+
+/* AES modes */
+enum cryp_algo_mode {
+	CRYP_ALGO_TDES_ECB,
+	CRYP_ALGO_TDES_CBC,
+	CRYP_ALGO_DES_ECB,
+	CRYP_ALGO_DES_CBC,
+	CRYP_ALGO_AES_ECB,
+	CRYP_ALGO_AES_CBC,
+	CRYP_ALGO_AES_CTR,
+	CRYP_ALGO_AES_XTS
+};
+
+/* Cryp Encryption or Decryption */
+enum cryp_algorithm_dir {
+	CRYP_ALGORITHM_ENCRYPT,
+	CRYP_ALGORITHM_DECRYPT
+};
+
+/* Hardware access method */
+enum cryp_mode {
+	CRYP_MODE_POLLING,
+	CRYP_MODE_INTERRUPT,
+	CRYP_MODE_DMA
+};
+
+/**
+ * struct cryp_config -
+ * @keysize: Key size for AES
+ * @algomode: AES modes
+ * @algodir: Cryp Encryption or Decryption
+ *
+ * CRYP configuration structure to be passed to set configuration
+ */
+struct cryp_config {
+	int keysize;
+	enum cryp_algo_mode algomode;
+	enum cryp_algorithm_dir algodir;
+};
+
+/**
+ * struct cryp_protection_config -
+ * @privilege_access: Privileged cryp state enable/disable
+ * @secure_access: Secure cryp state enable/disable
+ *
+ * Protection configuration structure for setting privilage access
+ */
+struct cryp_protection_config {
+	enum cryp_state privilege_access;
+	enum cryp_state secure_access;
+};
+
+/* Cryp status */
+enum cryp_status_id {
+	CRYP_STATUS_BUSY = 0x10,
+	CRYP_STATUS_OUTPUT_FIFO_FULL = 0x08,
+	CRYP_STATUS_OUTPUT_FIFO_NOT_EMPTY = 0x04,
+	CRYP_STATUS_INPUT_FIFO_NOT_FULL = 0x02,
+	CRYP_STATUS_INPUT_FIFO_EMPTY = 0x01
+};
+
+/* Cryp DMA interface */
+enum cryp_dma_req_type {
+	CRYP_DMA_DISABLE_BOTH,
+	CRYP_DMA_ENABLE_IN_DATA,
+	CRYP_DMA_ENABLE_OUT_DATA,
+	CRYP_DMA_ENABLE_BOTH_DIRECTIONS
+};
+
+enum cryp_dma_channel {
+	CRYP_DMA_RX = 0,
+	CRYP_DMA_TX
+};
+
+/* Key registers */
+enum cryp_key_reg_index {
+	CRYP_KEY_REG_1,
+	CRYP_KEY_REG_2,
+	CRYP_KEY_REG_3,
+	CRYP_KEY_REG_4
+};
+
+/* Key register left and right */
+struct cryp_key_value {
+	u32 key_value_left;
+	u32 key_value_right;
+};
+
+/* Cryp Initialization structure */
+enum cryp_init_vector_index {
+	CRYP_INIT_VECTOR_INDEX_0,
+	CRYP_INIT_VECTOR_INDEX_1
+};
+
+/* struct cryp_init_vector_value -
+ * @init_value_left
+ * @init_value_right
+ * */
+struct cryp_init_vector_value {
+	u32 init_value_left;
+	u32 init_value_right;
+};
+
+/**
+ * struct cryp_device_context - structure for a cryp context.
+ * @cr: control register
+ * @dmacr: DMA control register
+ * @imsc: Interrupt mask set/clear register
+ * @key_1_l: Key 1l register
+ * @key_1_r: Key 1r register
+ * @key_2_l: Key 2l register
+ * @key_2_r: Key 2r register
+ * @key_3_l: Key 3l register
+ * @key_3_r: Key 3r register
+ * @key_4_l: Key 4l register
+ * @key_4_r: Key 4r register
+ * @init_vect_0_l: Initialization vector 0l register
+ * @init_vect_0_r: Initialization vector 0r register
+ * @init_vect_1_l: Initialization vector 1l register
+ * @init_vect_1_r: Initialization vector 0r register
+ * @din: Data in register
+ * @dout: Data out register
+ *
+ * CRYP power management specifc structure.
+ */
+struct cryp_device_context {
+	u32 cr;
+	u32 dmacr;
+	u32 imsc;
+
+	u32 key_1_l;
+	u32 key_1_r;
+	u32 key_2_l;
+	u32 key_2_r;
+	u32 key_3_l;
+	u32 key_3_r;
+	u32 key_4_l;
+	u32 key_4_r;
+
+	u32 init_vect_0_l;
+	u32 init_vect_0_r;
+	u32 init_vect_1_l;
+	u32 init_vect_1_r;
+
+	u32 din;
+	u32 dout;
+};
+
+struct cryp_dma {
+	dma_cap_mask_t mask;
+	struct completion cryp_dma_complete;
+	struct dma_chan *chan_cryp2mem;
+	struct dma_chan *chan_mem2cryp;
+	struct stedma40_chan_cfg *cfg_cryp2mem;
+	struct stedma40_chan_cfg *cfg_mem2cryp;
+	int sg_src_len;
+	int sg_dst_len;
+	struct scatterlist *sg_src;
+	struct scatterlist *sg_dst;
+	int nents_src;
+	int nents_dst;
+};
+
+/**
+ * struct cryp_device_data - structure for a cryp device.
+ * @base: Pointer to the hardware base address.
+ * @dev: Pointer to the devices dev structure.
+ * @clk: Pointer to the device's clock control.
+ * @pwr_regulator: Pointer to the device's power control.
+ * @power_status: Current status of the power.
+ * @ctx_lock: Lock for current_ctx.
+ * @current_ctx: Pointer to the currently allocated context.
+ * @list_node: For inclusion into a klist.
+ * @dma: The dma structure holding channel configuration.
+ * @power_state: TRUE = power state on, FALSE = power state off.
+ * @power_state_spinlock: Spinlock for power_state.
+ * @restore_dev_ctx: TRUE = saved ctx, FALSE = no saved ctx.
+ */
+struct cryp_device_data {
+	struct cryp_register __iomem *base;
+	struct device *dev;
+	struct clk *clk;
+	struct regulator *pwr_regulator;
+	int power_status;
+	struct spinlock ctx_lock;
+	struct cryp_ctx *current_ctx;
+	struct klist_node list_node;
+	struct cryp_dma dma;
+	bool power_state;
+	struct spinlock power_state_spinlock;
+	bool restore_dev_ctx;
+};
+
+void cryp_wait_until_done(struct cryp_device_data *device_data);
+
+/* Initialization functions */
+
+int cryp_check(struct cryp_device_data *device_data);
+
+void cryp_activity(struct cryp_device_data *device_data,
+		   enum cryp_crypen cryp_crypen);
+
+void cryp_flush_inoutfifo(struct cryp_device_data *device_data);
+
+int cryp_set_configuration(struct cryp_device_data *device_data,
+			   struct cryp_config *cryp_config,
+			   u32 *control_register);
+
+void cryp_configure_for_dma(struct cryp_device_data *device_data,
+			    enum cryp_dma_req_type dma_req);
+
+int cryp_configure_key_values(struct cryp_device_data *device_data,
+			      enum cryp_key_reg_index key_reg_index,
+			      struct cryp_key_value key_value);
+
+int cryp_configure_init_vector(struct cryp_device_data *device_data,
+			       enum cryp_init_vector_index
+			       init_vector_index,
+			       struct cryp_init_vector_value
+			       init_vector_value);
+
+int cryp_configure_protection(struct cryp_device_data *device_data,
+			      struct cryp_protection_config *p_protect_config);
+
+/* Power management funtions */
+void cryp_save_device_context(struct cryp_device_data *device_data,
+			      struct cryp_device_context *ctx,
+			      int cryp_mode);
+
+void cryp_restore_device_context(struct cryp_device_data *device_data,
+				 struct cryp_device_context *ctx);
+
+/* Data transfer and status bits. */
+int cryp_is_logic_busy(struct cryp_device_data *device_data);
+
+int cryp_get_status(struct cryp_device_data *device_data);
+
+/**
+ * cryp_write_indata - This routine writes 32 bit data into the data input
+ *		       register of the cryptography IP.
+ * @device_data: Pointer to the device data struct for base address.
+ * @write_data: Data to write.
+ */
+int cryp_write_indata(struct cryp_device_data *device_data, u32 write_data);
+
+/**
+ * cryp_read_outdata - This routine reads the data from the data output
+ *		       register of the CRYP logic
+ * @device_data: Pointer to the device data struct for base address.
+ * @read_data: Read the data from the output FIFO.
+ */
+int cryp_read_outdata(struct cryp_device_data *device_data, u32 *read_data);
+
+#endif /* _CRYP_H_ */
diff --git a/drivers/crypto/ux500/cryp/cryp_core.c b/drivers/crypto/ux500/cryp/cryp_core.c
new file mode 100644
index 0000000..7cac127
--- /dev/null
+++ b/drivers/crypto/ux500/cryp/cryp_core.c
@@ -0,0 +1,1784 @@
+/**
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Shujuan Chen <shujuan.chen@stericsson.com> for ST-Ericsson.
+ * Author: Joakim Bech <joakim.xx.bech@stericsson.com> for ST-Ericsson.
+ * Author: Berne Hebark <berne.herbark@stericsson.com> for ST-Ericsson.
+ * Author: Niklas Hernaeus <niklas.hernaeus@stericsson.com> for ST-Ericsson.
+ * Author: Jonas Linde <jonas.linde@stericsson.com> for ST-Ericsson.
+ * Author: Andreas Westin <andreas.westin@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/crypto.h>
+#include <linux/dmaengine.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irqreturn.h>
+#include <linux/klist.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/semaphore.h>
+
+#include <crypto/aes.h>
+#include <crypto/algapi.h>
+#include <crypto/ctr.h>
+#include <crypto/des.h>
+#include <crypto/scatterwalk.h>
+
+#include <plat/ste_dma40.h>
+
+#include <mach/crypto-ux500.h>
+#include <mach/hardware.h>
+
+#include "cryp_p.h"
+#include "cryp.h"
+
+#define CRYP_MAX_KEY_SIZE	32
+#define BYTES_PER_WORD		4
+
+static int cryp_mode;
+static atomic_t session_id;
+
+static struct stedma40_chan_cfg *mem_to_engine;
+static struct stedma40_chan_cfg *engine_to_mem;
+
+/**
+ * struct cryp_driver_data - data specific to the driver.
+ *
+ * @device_list: A list of registered devices to choose from.
+ * @device_allocation: A semaphore initialized with number of devices.
+ */
+struct cryp_driver_data {
+	struct klist device_list;
+	struct semaphore device_allocation;
+};
+
+/**
+ * struct cryp_ctx - Crypto context
+ * @config: Crypto mode.
+ * @key[CRYP_MAX_KEY_SIZE]: Key.
+ * @keylen: Length of key.
+ * @iv: Pointer to initialization vector.
+ * @indata: Pointer to indata.
+ * @outdata: Pointer to outdata.
+ * @datalen: Length of indata.
+ * @outlen: Length of outdata.
+ * @blocksize: Size of blocks.
+ * @updated: Updated flag.
+ * @dev_ctx: Device dependent context.
+ * @device: Pointer to the device.
+ */
+struct cryp_ctx {
+	struct cryp_config config;
+	u8 key[CRYP_MAX_KEY_SIZE];
+	u32 keylen;
+	u8 *iv;
+	const u8 *indata;
+	u8 *outdata;
+	u32 datalen;
+	u32 outlen;
+	u32 blocksize;
+	u8 updated;
+	struct cryp_device_context dev_ctx;
+	struct cryp_device_data *device;
+	u32 session_id;
+};
+
+static struct cryp_driver_data driver_data;
+
+/**
+ * uint8p_to_uint32_be - 4*uint8 to uint32 big endian
+ * @in: Data to convert.
+ */
+static inline u32 uint8p_to_uint32_be(u8 *in)
+{
+	u32 *data = (u32 *)in;
+
+	return cpu_to_be32p(data);
+}
+
+/**
+ * swap_bits_in_byte - mirror the bits in a byte
+ * @b: the byte to be mirrored
+ *
+ * The bits are swapped the following way:
+ *  Byte b include bits 0-7, nibble 1 (n1) include bits 0-3 and
+ *  nibble 2 (n2) bits 4-7.
+ *
+ *  Nibble 1 (n1):
+ *  (The "old" (moved) bit is replaced with a zero)
+ *  1. Move bit 6 and 7, 4 positions to the left.
+ *  2. Move bit 3 and 5, 2 positions to the left.
+ *  3. Move bit 1-4, 1 position to the left.
+ *
+ *  Nibble 2 (n2):
+ *  1. Move bit 0 and 1, 4 positions to the right.
+ *  2. Move bit 2 and 4, 2 positions to the right.
+ *  3. Move bit 3-6, 1 position to the right.
+ *
+ *  Combine the two nibbles to a complete and swapped byte.
+ */
+
+static inline u8 swap_bits_in_byte(u8 b)
+{
+#define R_SHIFT_4_MASK  0xc0 /* Bits 6 and 7, right shift 4 */
+#define R_SHIFT_2_MASK  0x28 /* (After right shift 4) Bits 3 and 5,
+				  right shift 2 */
+#define R_SHIFT_1_MASK  0x1e /* (After right shift 2) Bits 1-4,
+				  right shift 1 */
+#define L_SHIFT_4_MASK  0x03 /* Bits 0 and 1, left shift 4 */
+#define L_SHIFT_2_MASK  0x14 /* (After left shift 4) Bits 2 and 4,
+				  left shift 2 */
+#define L_SHIFT_1_MASK  0x78 /* (After left shift 1) Bits 3-6,
+				  left shift 1 */
+
+	u8 n1;
+	u8 n2;
+
+	/* Swap most significant nibble */
+	/* Right shift 4, bits 6 and 7 */
+	n1 = ((b  & R_SHIFT_4_MASK) >> 4) | (b  & ~(R_SHIFT_4_MASK >> 4));
+	/* Right shift 2, bits 3 and 5 */
+	n1 = ((n1 & R_SHIFT_2_MASK) >> 2) | (n1 & ~(R_SHIFT_2_MASK >> 2));
+	/* Right shift 1, bits 1-4 */
+	n1 = (n1  & R_SHIFT_1_MASK) >> 1;
+
+	/* Swap least significant nibble */
+	/* Left shift 4, bits 0 and 1 */
+	n2 = ((b  & L_SHIFT_4_MASK) << 4) | (b  & ~(L_SHIFT_4_MASK << 4));
+	/* Left shift 2, bits 2 and 4 */
+	n2 = ((n2 & L_SHIFT_2_MASK) << 2) | (n2 & ~(L_SHIFT_2_MASK << 2));
+	/* Left shift 1, bits 3-6 */
+	n2 = (n2  & L_SHIFT_1_MASK) << 1;
+
+	return n1 | n2;
+}
+
+static inline void swap_words_in_key_and_bits_in_byte(const u8 *in,
+						      u8 *out, u32 len)
+{
+	unsigned int i = 0;
+	int j;
+	int index = 0;
+
+	j = len - BYTES_PER_WORD;
+	while (j >= 0) {
+		for (i = 0; i < BYTES_PER_WORD; i++) {
+			index = len - j - BYTES_PER_WORD + i;
+			out[j + i] =
+				swap_bits_in_byte(in[index]);
+		}
+		j -= BYTES_PER_WORD;
+	}
+}
+
+static void add_session_id(struct cryp_ctx *ctx)
+{
+	/*
+	 * We never want 0 to be a valid value, since this is the default value
+	 * for the software context.
+	 */
+	if (unlikely(atomic_inc_and_test(&session_id)))
+		atomic_inc(&session_id);
+
+	ctx->session_id = atomic_read(&session_id);
+}
+
+static irqreturn_t cryp_interrupt_handler(int irq, void *param)
+{
+	struct cryp_ctx *ctx;
+	int i;
+	struct cryp_device_data *device_data;
+
+	if (param == NULL) {
+		BUG_ON(!param);
+		return IRQ_HANDLED;
+	}
+
+	/* The device is coming from the one found in hw_crypt_noxts. */
+	device_data = (struct cryp_device_data *)param;
+
+	ctx = device_data->current_ctx;
+
+	if (ctx == NULL) {
+		BUG_ON(!ctx);
+		return IRQ_HANDLED;
+	}
+
+	dev_dbg(ctx->device->dev, "[%s] (len: %d) %s, ", __func__, ctx->outlen,
+		cryp_pending_irq_src(device_data, CRYP_IRQ_SRC_OUTPUT_FIFO) ?
+		"out" : "in");
+
+	if (cryp_pending_irq_src(device_data,
+				 CRYP_IRQ_SRC_OUTPUT_FIFO)) {
+		if (ctx->outlen / ctx->blocksize > 0) {
+			for (i = 0; i < ctx->blocksize / 4; i++) {
+				*(ctx->outdata) = readl_relaxed(
+						&device_data->base->dout);
+				ctx->outdata += 4;
+				ctx->outlen -= 4;
+			}
+
+			if (ctx->outlen == 0) {
+				cryp_disable_irq_src(device_data,
+						     CRYP_IRQ_SRC_OUTPUT_FIFO);
+			}
+		}
+	} else if (cryp_pending_irq_src(device_data,
+					CRYP_IRQ_SRC_INPUT_FIFO)) {
+		if (ctx->datalen / ctx->blocksize > 0) {
+			for (i = 0 ; i < ctx->blocksize / 4; i++) {
+				writel_relaxed(ctx->indata,
+						&device_data->base->din);
+				ctx->indata += 4;
+				ctx->datalen -= 4;
+			}
+
+			if (ctx->datalen == 0)
+				cryp_disable_irq_src(device_data,
+						   CRYP_IRQ_SRC_INPUT_FIFO);
+
+			if (ctx->config.algomode == CRYP_ALGO_AES_XTS) {
+				CRYP_PUT_BITS(&device_data->base->cr,
+					      CRYP_START_ENABLE,
+					      CRYP_CR_START_POS,
+					      CRYP_CR_START_MASK);
+
+				cryp_wait_until_done(device_data);
+			}
+		}
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int mode_is_aes(enum cryp_algo_mode mode)
+{
+	return	CRYP_ALGO_AES_ECB == mode ||
+		CRYP_ALGO_AES_CBC == mode ||
+		CRYP_ALGO_AES_CTR == mode ||
+		CRYP_ALGO_AES_XTS == mode;
+}
+
+static int cfg_iv(struct cryp_device_data *device_data, u32 left, u32 right,
+		  enum cryp_init_vector_index index)
+{
+	struct cryp_init_vector_value vector_value;
+
+	dev_dbg(device_data->dev, "[%s]", __func__);
+
+	vector_value.init_value_left = left;
+	vector_value.init_value_right = right;
+
+	return cryp_configure_init_vector(device_data,
+					  index,
+					  vector_value);
+}
+
+static int cfg_ivs(struct cryp_device_data *device_data, struct cryp_ctx *ctx)
+{
+	int i;
+	int status = 0;
+	int num_of_regs = ctx->blocksize / 8;
+	u32 iv[AES_BLOCK_SIZE / 4];
+
+	dev_dbg(device_data->dev, "[%s]", __func__);
+
+	/*
+	 * Since we loop on num_of_regs we need to have a check in case
+	 * someone provides an incorrect blocksize which would force calling
+	 * cfg_iv with i greater than 2 which is an error.
+	 */
+	if (num_of_regs > 2) {
+		dev_err(device_data->dev, "[%s] Incorrect blocksize %d",
+			__func__, ctx->blocksize);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < ctx->blocksize / 4; i++)
+		iv[i] = uint8p_to_uint32_be(ctx->iv + i*4);
+
+	for (i = 0; i < num_of_regs; i++) {
+		status = cfg_iv(device_data, iv[i*2], iv[i*2+1],
+				(enum cryp_init_vector_index) i);
+		if (status != 0)
+			return status;
+	}
+	return status;
+}
+
+static int set_key(struct cryp_device_data *device_data,
+		   u32 left_key,
+		   u32 right_key,
+		   enum cryp_key_reg_index index)
+{
+	struct cryp_key_value key_value;
+	int cryp_error;
+
+	dev_dbg(device_data->dev, "[%s]", __func__);
+
+	key_value.key_value_left = left_key;
+	key_value.key_value_right = right_key;
+
+	cryp_error = cryp_configure_key_values(device_data,
+					       index,
+					       key_value);
+	if (cryp_error != 0)
+		dev_err(device_data->dev, "[%s]: "
+			"cryp_configure_key_values() failed!", __func__);
+
+	return cryp_error;
+}
+
+static int cfg_keys(struct cryp_ctx *ctx)
+{
+	int i;
+	int num_of_regs = ctx->keylen / 8;
+	u32 swapped_key[CRYP_MAX_KEY_SIZE / 4];
+	int cryp_error = 0;
+
+	dev_dbg(ctx->device->dev, "[%s]", __func__);
+
+	if (mode_is_aes(ctx->config.algomode)) {
+		swap_words_in_key_and_bits_in_byte((u8 *)ctx->key,
+						   (u8 *)swapped_key,
+						   ctx->keylen);
+	} else {
+		for (i = 0; i < ctx->keylen / 4; i++)
+			swapped_key[i] = uint8p_to_uint32_be(ctx->key + i*4);
+	}
+
+	for (i = 0; i < num_of_regs; i++) {
+		cryp_error = set_key(ctx->device,
+				     *(((u32 *)swapped_key)+i*2),
+				     *(((u32 *)swapped_key)+i*2+1),
+				     (enum cryp_key_reg_index) i);
+
+		if (cryp_error != 0) {
+			dev_err(ctx->device->dev, "[%s]: set_key() failed!",
+					__func__);
+			return cryp_error;
+		}
+	}
+	return cryp_error;
+}
+
+static int cryp_setup_context(struct cryp_ctx *ctx,
+			      struct cryp_device_data *device_data)
+{
+	u32 control_register = CRYP_CR_DEFAULT;
+
+	switch (cryp_mode) {
+	case CRYP_MODE_INTERRUPT:
+		writel_relaxed(CRYP_IMSC_DEFAULT, &device_data->base->imsc);
+		break;
+
+	case CRYP_MODE_DMA:
+		writel_relaxed(CRYP_DMACR_DEFAULT, &device_data->base->dmacr);
+		break;
+
+	default:
+		break;
+	}
+
+	if (ctx->updated == 0) {
+		cryp_flush_inoutfifo(device_data);
+		if (cfg_keys(ctx) != 0) {
+			dev_err(ctx->device->dev, "[%s]: cfg_keys failed!",
+				__func__);
+			return -EINVAL;
+		}
+
+		if (ctx->iv &&
+		    CRYP_ALGO_AES_ECB != ctx->config.algomode &&
+		    CRYP_ALGO_DES_ECB != ctx->config.algomode &&
+		    CRYP_ALGO_TDES_ECB != ctx->config.algomode) {
+			if (cfg_ivs(device_data, ctx) != 0)
+				return -EPERM;
+		}
+
+		cryp_set_configuration(device_data, &ctx->config,
+				       &control_register);
+		add_session_id(ctx);
+	} else if (ctx->updated == 1 &&
+		   ctx->session_id != atomic_read(&session_id)) {
+		cryp_flush_inoutfifo(device_data);
+		cryp_restore_device_context(device_data, &ctx->dev_ctx);
+
+		add_session_id(ctx);
+		control_register = ctx->dev_ctx.cr;
+	} else
+		control_register = ctx->dev_ctx.cr;
+
+	writel(control_register |
+	       (CRYP_CRYPEN_ENABLE << CRYP_CR_CRYPEN_POS),
+	       &device_data->base->cr);
+
+	return 0;
+}
+
+static int cryp_get_device_data(struct cryp_ctx *ctx,
+				struct cryp_device_data **device_data)
+{
+	int ret;
+	struct klist_iter device_iterator;
+	struct klist_node *device_node;
+	struct cryp_device_data *local_device_data = NULL;
+	pr_debug(DEV_DBG_NAME " [%s]", __func__);
+
+	/* Wait until a device is available */
+	ret = down_interruptible(&driver_data.device_allocation);
+	if (ret)
+		return ret;  /* Interrupted */
+
+	/* Select a device */
+	klist_iter_init(&driver_data.device_list, &device_iterator);
+
+	device_node = klist_next(&device_iterator);
+	while (device_node) {
+		local_device_data = container_of(device_node,
+					   struct cryp_device_data, list_node);
+		spin_lock(&local_device_data->ctx_lock);
+		/* current_ctx allocates a device, NULL = unallocated */
+		if (local_device_data->current_ctx) {
+			device_node = klist_next(&device_iterator);
+		} else {
+			local_device_data->current_ctx = ctx;
+			ctx->device = local_device_data;
+			spin_unlock(&local_device_data->ctx_lock);
+			break;
+		}
+		spin_unlock(&local_device_data->ctx_lock);
+	}
+	klist_iter_exit(&device_iterator);
+
+	if (!device_node) {
+		/**
+		 * No free device found.
+		 * Since we allocated a device with down_interruptible, this
+		 * should not be able to happen.
+		 * Number of available devices, which are contained in
+		 * device_allocation, is therefore decremented by not doing
+		 * an up(device_allocation).
+		 */
+		return -EBUSY;
+	}
+
+	*device_data = local_device_data;
+
+	return 0;
+}
+
+static void cryp_dma_setup_channel(struct cryp_device_data *device_data,
+				   struct device *dev)
+{
+	dma_cap_zero(device_data->dma.mask);
+	dma_cap_set(DMA_SLAVE, device_data->dma.mask);
+
+	device_data->dma.cfg_mem2cryp = mem_to_engine;
+	device_data->dma.chan_mem2cryp =
+		dma_request_channel(device_data->dma.mask,
+				    stedma40_filter,
+				    device_data->dma.cfg_mem2cryp);
+
+	device_data->dma.cfg_cryp2mem = engine_to_mem;
+	device_data->dma.chan_cryp2mem =
+		dma_request_channel(device_data->dma.mask,
+				    stedma40_filter,
+				    device_data->dma.cfg_cryp2mem);
+
+	init_completion(&device_data->dma.cryp_dma_complete);
+}
+
+static void cryp_dma_out_callback(void *data)
+{
+	struct cryp_ctx *ctx = (struct cryp_ctx *) data;
+	dev_dbg(ctx->device->dev, "[%s]: ", __func__);
+
+	complete(&ctx->device->dma.cryp_dma_complete);
+}
+
+static int cryp_set_dma_transfer(struct cryp_ctx *ctx,
+				 struct scatterlist *sg,
+				 int len,
+				 enum dma_data_direction direction)
+{
+	struct dma_async_tx_descriptor *desc;
+	struct dma_chan *channel = NULL;
+	dma_cookie_t cookie;
+
+	dev_dbg(ctx->device->dev, "[%s]: ", __func__);
+
+	if (unlikely(!IS_ALIGNED((u32)sg, 4))) {
+		dev_err(ctx->device->dev, "[%s]: Data in sg list isn't "
+			"aligned! Addr: 0x%08x", __func__, (u32)sg);
+		return -EFAULT;
+	}
+
+	switch (direction) {
+	case DMA_TO_DEVICE:
+		channel = ctx->device->dma.chan_mem2cryp;
+		ctx->device->dma.sg_src = sg;
+		ctx->device->dma.sg_src_len = dma_map_sg(channel->device->dev,
+						 ctx->device->dma.sg_src,
+						 ctx->device->dma.nents_src,
+						 direction);
+
+		if (!ctx->device->dma.sg_src_len) {
+			dev_dbg(ctx->device->dev,
+				"[%s]: Could not map the sg list (TO_DEVICE)",
+				__func__);
+			return -EFAULT;
+		}
+
+		dev_dbg(ctx->device->dev, "[%s]: Setting up DMA for buffer "
+			"(TO_DEVICE)", __func__);
+
+		desc = channel->device->device_prep_slave_sg(channel,
+					     ctx->device->dma.sg_src,
+					     ctx->device->dma.sg_src_len,
+					     direction, DMA_CTRL_ACK, NULL);
+		break;
+
+	case DMA_FROM_DEVICE:
+		channel = ctx->device->dma.chan_cryp2mem;
+		ctx->device->dma.sg_dst = sg;
+		ctx->device->dma.sg_dst_len = dma_map_sg(channel->device->dev,
+						 ctx->device->dma.sg_dst,
+						 ctx->device->dma.nents_dst,
+						 direction);
+
+		if (!ctx->device->dma.sg_dst_len) {
+			dev_dbg(ctx->device->dev,
+				"[%s]: Could not map the sg list (FROM_DEVICE)",
+				__func__);
+			return -EFAULT;
+		}
+
+		dev_dbg(ctx->device->dev, "[%s]: Setting up DMA for buffer "
+			"(FROM_DEVICE)", __func__);
+
+		desc = channel->device->device_prep_slave_sg(channel,
+					     ctx->device->dma.sg_dst,
+					     ctx->device->dma.sg_dst_len,
+					     direction,
+					     DMA_CTRL_ACK |
+					     DMA_PREP_INTERRUPT, NULL);
+
+		desc->callback = cryp_dma_out_callback;
+		desc->callback_param = ctx;
+		break;
+
+	default:
+		dev_dbg(ctx->device->dev, "[%s]: Invalid DMA direction",
+			__func__);
+		return -EFAULT;
+	}
+
+	cookie = desc->tx_submit(desc);
+	dma_async_issue_pending(channel);
+
+	return 0;
+}
+
+static void cryp_dma_done(struct cryp_ctx *ctx)
+{
+	struct dma_chan *chan;
+
+	dev_dbg(ctx->device->dev, "[%s]: ", __func__);
+
+	chan = ctx->device->dma.chan_mem2cryp;
+	chan->device->device_control(chan, DMA_TERMINATE_ALL, 0);
+	dma_unmap_sg(chan->device->dev, ctx->device->dma.sg_src,
+		     ctx->device->dma.sg_src_len, DMA_TO_DEVICE);
+
+	chan = ctx->device->dma.chan_cryp2mem;
+	chan->device->device_control(chan, DMA_TERMINATE_ALL, 0);
+	dma_unmap_sg(chan->device->dev, ctx->device->dma.sg_dst,
+		     ctx->device->dma.sg_dst_len, DMA_FROM_DEVICE);
+}
+
+static int cryp_dma_write(struct cryp_ctx *ctx, struct scatterlist *sg,
+			  int len)
+{
+	int error = cryp_set_dma_transfer(ctx, sg, len, DMA_TO_DEVICE);
+	dev_dbg(ctx->device->dev, "[%s]: ", __func__);
+
+	if (error) {
+		dev_dbg(ctx->device->dev, "[%s]: cryp_set_dma_transfer() "
+			"failed", __func__);
+		return error;
+	}
+
+	return len;
+}
+
+static int cryp_dma_read(struct cryp_ctx *ctx, struct scatterlist *sg, int len)
+{
+	int error = cryp_set_dma_transfer(ctx, sg, len, DMA_FROM_DEVICE);
+	if (error) {
+		dev_dbg(ctx->device->dev, "[%s]: cryp_set_dma_transfer() "
+			"failed", __func__);
+		return error;
+	}
+
+	return len;
+}
+
+static void cryp_polling_mode(struct cryp_ctx *ctx,
+			      struct cryp_device_data *device_data)
+{
+	int len = ctx->blocksize / BYTES_PER_WORD;
+	int remaining_length = ctx->datalen;
+	u32 *indata = (u32 *)ctx->indata;
+	u32 *outdata = (u32 *)ctx->outdata;
+
+	while (remaining_length > 0) {
+		writesl(&device_data->base->din, indata, len);
+		indata += len;
+		remaining_length -= (len * BYTES_PER_WORD);
+		cryp_wait_until_done(device_data);
+
+		readsl(&device_data->base->dout, outdata, len);
+		outdata += len;
+		cryp_wait_until_done(device_data);
+	}
+}
+
+static int cryp_disable_power(struct device *dev,
+			      struct cryp_device_data *device_data,
+			      bool save_device_context)
+{
+	int ret = 0;
+
+	dev_dbg(dev, "[%s]", __func__);
+
+	spin_lock(&device_data->power_state_spinlock);
+	if (!device_data->power_state)
+		goto out;
+
+	spin_lock(&device_data->ctx_lock);
+	if (save_device_context && device_data->current_ctx) {
+		cryp_save_device_context(device_data,
+				&device_data->current_ctx->dev_ctx,
+				cryp_mode);
+		device_data->restore_dev_ctx = true;
+	}
+	spin_unlock(&device_data->ctx_lock);
+
+	clk_disable(device_data->clk);
+	ret = regulator_disable(device_data->pwr_regulator);
+	if (ret)
+		dev_err(dev, "[%s]: "
+				"regulator_disable() failed!",
+				__func__);
+
+	device_data->power_state = false;
+
+out:
+	spin_unlock(&device_data->power_state_spinlock);
+
+	return ret;
+}
+
+static int cryp_enable_power(
+		struct device *dev,
+		struct cryp_device_data *device_data,
+		bool restore_device_context)
+{
+	int ret = 0;
+
+	dev_dbg(dev, "[%s]", __func__);
+
+	spin_lock(&device_data->power_state_spinlock);
+	if (!device_data->power_state) {
+		ret = regulator_enable(device_data->pwr_regulator);
+		if (ret) {
+			dev_err(dev, "[%s]: regulator_enable() failed!",
+					__func__);
+			goto out;
+		}
+
+		ret = clk_enable(device_data->clk);
+		if (ret) {
+			dev_err(dev, "[%s]: clk_enable() failed!",
+					__func__);
+			regulator_disable(device_data->pwr_regulator);
+			goto out;
+		}
+		device_data->power_state = true;
+	}
+
+	if (device_data->restore_dev_ctx) {
+		spin_lock(&device_data->ctx_lock);
+		if (restore_device_context && device_data->current_ctx) {
+			device_data->restore_dev_ctx = false;
+			cryp_restore_device_context(device_data,
+					&device_data->current_ctx->dev_ctx);
+		}
+		spin_unlock(&device_data->ctx_lock);
+	}
+out:
+	spin_unlock(&device_data->power_state_spinlock);
+
+	return ret;
+}
+
+static int hw_crypt_noxts(struct cryp_ctx *ctx,
+			  struct cryp_device_data *device_data)
+{
+	int ret = 0;
+
+	const u8 *indata = ctx->indata;
+	u8 *outdata = ctx->outdata;
+	u32 datalen = ctx->datalen;
+	u32 outlen = datalen;
+
+	pr_debug(DEV_DBG_NAME " [%s]", __func__);
+
+	ctx->outlen = ctx->datalen;
+
+	if (unlikely(!IS_ALIGNED((u32)indata, 4))) {
+		pr_debug(DEV_DBG_NAME " [%s]: Data isn't aligned! Addr: "
+			 "0x%08x", __func__, (u32)indata);
+		return -EINVAL;
+	}
+
+	ret = cryp_setup_context(ctx, device_data);
+
+	if (ret)
+		goto out;
+
+	if (cryp_mode == CRYP_MODE_INTERRUPT) {
+		cryp_enable_irq_src(device_data, CRYP_IRQ_SRC_INPUT_FIFO |
+				    CRYP_IRQ_SRC_OUTPUT_FIFO);
+
+		/*
+		 * ctx->outlen is decremented in the cryp_interrupt_handler
+		 * function. We had to add cpu_relax() (barrier) to make sure
+		 * that gcc didn't optimze away this variable.
+		 */
+		while (ctx->outlen > 0)
+			cpu_relax();
+	} else if (cryp_mode == CRYP_MODE_POLLING ||
+		   cryp_mode == CRYP_MODE_DMA) {
+		/*
+		 * The reason for having DMA in this if case is that if we are
+		 * running cryp_mode = 2, then we separate DMA routines for
+		 * handling cipher/plaintext > blocksize, except when
+		 * running the normal CRYPTO_ALG_TYPE_CIPHER, then we still use
+		 * the polling mode. Overhead of doing DMA setup eats up the
+		 * benefits using it.
+		 */
+		cryp_polling_mode(ctx, device_data);
+	} else {
+		dev_err(ctx->device->dev, "[%s]: Invalid operation mode!",
+			__func__);
+		ret = -EPERM;
+		goto out;
+	}
+
+	cryp_save_device_context(device_data, &ctx->dev_ctx, cryp_mode);
+	ctx->updated = 1;
+
+out:
+	ctx->indata = indata;
+	ctx->outdata = outdata;
+	ctx->datalen = datalen;
+	ctx->outlen = outlen;
+
+	return ret;
+}
+
+static int get_nents(struct scatterlist *sg, int nbytes)
+{
+	int nents = 0;
+
+	while (nbytes > 0) {
+		nbytes -= sg->length;
+		sg = scatterwalk_sg_next(sg);
+		nents++;
+	}
+
+	return nents;
+}
+
+static int ablk_dma_crypt(struct ablkcipher_request *areq)
+{
+	struct crypto_ablkcipher *cipher = crypto_ablkcipher_reqtfm(areq);
+	struct cryp_ctx *ctx = crypto_ablkcipher_ctx(cipher);
+	struct cryp_device_data *device_data;
+
+	int bytes_written = 0;
+	int bytes_read = 0;
+	int ret;
+
+	pr_debug(DEV_DBG_NAME " [%s]", __func__);
+
+	ctx->datalen = areq->nbytes;
+	ctx->outlen = areq->nbytes;
+
+	ret = cryp_get_device_data(ctx, &device_data);
+	if (ret)
+		return ret;
+
+	ret = cryp_setup_context(ctx, device_data);
+	if (ret)
+		goto out;
+
+	/* We have the device now, so store the nents in the dma struct. */
+	ctx->device->dma.nents_src = get_nents(areq->src, ctx->datalen);
+	ctx->device->dma.nents_dst = get_nents(areq->dst, ctx->outlen);
+
+	/* Enable DMA in- and output. */
+	cryp_configure_for_dma(device_data, CRYP_DMA_ENABLE_BOTH_DIRECTIONS);
+
+	bytes_written = cryp_dma_write(ctx, areq->src, ctx->datalen);
+	bytes_read = cryp_dma_read(ctx, areq->dst, bytes_written);
+
+	wait_for_completion(&ctx->device->dma.cryp_dma_complete);
+	cryp_dma_done(ctx);
+
+	cryp_save_device_context(device_data, &ctx->dev_ctx, cryp_mode);
+	ctx->updated = 1;
+
+out:
+	spin_lock(&device_data->ctx_lock);
+	device_data->current_ctx = NULL;
+	ctx->device = NULL;
+	spin_unlock(&device_data->ctx_lock);
+
+	/*
+	 * The down_interruptible part for this semaphore is called in
+	 * cryp_get_device_data.
+	 */
+	up(&driver_data.device_allocation);
+
+	if (unlikely(bytes_written != bytes_read))
+		return -EPERM;
+
+	return 0;
+}
+
+static int ablk_crypt(struct ablkcipher_request *areq)
+{
+	struct ablkcipher_walk walk;
+	struct crypto_ablkcipher *cipher = crypto_ablkcipher_reqtfm(areq);
+	struct cryp_ctx *ctx = crypto_ablkcipher_ctx(cipher);
+	struct cryp_device_data *device_data;
+	unsigned long src_paddr;
+	unsigned long dst_paddr;
+	int ret;
+	int nbytes;
+
+	pr_debug(DEV_DBG_NAME " [%s]", __func__);
+
+	ret = cryp_get_device_data(ctx, &device_data);
+	if (ret)
+		goto out;
+
+	ablkcipher_walk_init(&walk, areq->dst, areq->src, areq->nbytes);
+	ret = ablkcipher_walk_phys(areq, &walk);
+
+	if (ret) {
+		pr_err(DEV_DBG_NAME "[%s]: ablkcipher_walk_phys() failed!",
+			__func__);
+		goto out;
+	}
+
+	while ((nbytes = walk.nbytes) > 0) {
+		ctx->iv = walk.iv;
+		src_paddr = (page_to_phys(walk.src.page) + walk.src.offset);
+		ctx->indata = phys_to_virt(src_paddr);
+
+		dst_paddr = (page_to_phys(walk.dst.page) + walk.dst.offset);
+		ctx->outdata = phys_to_virt(dst_paddr);
+
+		ctx->datalen = nbytes - (nbytes % ctx->blocksize);
+
+		ret = hw_crypt_noxts(ctx, device_data);
+		if (ret)
+			goto out;
+
+		nbytes -= ctx->datalen;
+		ret = ablkcipher_walk_done(areq, &walk, nbytes);
+		if (ret)
+			goto out;
+	}
+	ablkcipher_walk_complete(&walk);
+
+out:
+	/* Release the device */
+	spin_lock(&device_data->ctx_lock);
+	device_data->current_ctx = NULL;
+	ctx->device = NULL;
+	spin_unlock(&device_data->ctx_lock);
+
+	/*
+	 * The down_interruptible part for this semaphore is called in
+	 * cryp_get_device_data.
+	 */
+	up(&driver_data.device_allocation);
+
+	return ret;
+}
+
+static int aes_ablkcipher_setkey(struct crypto_ablkcipher *cipher,
+				 const u8 *key, unsigned int keylen)
+{
+	struct cryp_ctx *ctx = crypto_ablkcipher_ctx(cipher);
+	u32 *flags = &cipher->base.crt_flags;
+
+	pr_debug(DEV_DBG_NAME " [%s]", __func__);
+
+	switch (keylen) {
+	case AES_KEYSIZE_128:
+		ctx->config.keysize = CRYP_KEY_SIZE_128;
+		break;
+
+	case AES_KEYSIZE_192:
+		ctx->config.keysize = CRYP_KEY_SIZE_192;
+		break;
+
+	case AES_KEYSIZE_256:
+		ctx->config.keysize = CRYP_KEY_SIZE_256;
+		break;
+
+	default:
+		pr_err(DEV_DBG_NAME "[%s]: Unknown keylen!", __func__);
+		*flags |= CRYPTO_TFM_RES_BAD_KEY_LEN;
+		return -EINVAL;
+	}
+
+	memcpy(ctx->key, key, keylen);
+	ctx->keylen = keylen;
+
+	ctx->updated = 0;
+
+	return 0;
+}
+
+static int des_ablkcipher_setkey(struct crypto_ablkcipher *cipher,
+				 const u8 *key, unsigned int keylen)
+{
+	struct cryp_ctx *ctx = crypto_ablkcipher_ctx(cipher);
+	u32 *flags = &cipher->base.crt_flags;
+	u32 tmp[DES_EXPKEY_WORDS];
+	int ret;
+
+	pr_debug(DEV_DBG_NAME " [%s]", __func__);
+	if (keylen != DES_KEY_SIZE) {
+		*flags |= CRYPTO_TFM_RES_BAD_KEY_LEN;
+		pr_debug(DEV_DBG_NAME " [%s]: CRYPTO_TFM_RES_BAD_KEY_LEN",
+				__func__);
+		return -EINVAL;
+	}
+
+	ret = des_ekey(tmp, key);
+	if (unlikely(ret == 0) && (*flags & CRYPTO_TFM_REQ_WEAK_KEY)) {
+		*flags |= CRYPTO_TFM_RES_WEAK_KEY;
+		pr_debug(DEV_DBG_NAME " [%s]: CRYPTO_TFM_REQ_WEAK_KEY",
+				__func__);
+		return -EINVAL;
+	}
+
+	memcpy(ctx->key, key, keylen);
+	ctx->keylen = keylen;
+
+	ctx->updated = 0;
+	return 0;
+}
+
+static int des3_ablkcipher_setkey(struct crypto_ablkcipher *cipher,
+				  const u8 *key, unsigned int keylen)
+{
+	struct cryp_ctx *ctx = crypto_ablkcipher_ctx(cipher);
+	u32 *flags = &cipher->base.crt_flags;
+	const u32 *K = (const u32 *)key;
+	u32 tmp[DES3_EDE_EXPKEY_WORDS];
+	int i, ret;
+
+	pr_debug(DEV_DBG_NAME " [%s]", __func__);
+	if (keylen != DES3_EDE_KEY_SIZE) {
+		*flags |= CRYPTO_TFM_RES_BAD_KEY_LEN;
+		pr_debug(DEV_DBG_NAME " [%s]: CRYPTO_TFM_RES_BAD_KEY_LEN",
+				__func__);
+		return -EINVAL;
+	}
+
+	/* Checking key interdependency for weak key detection. */
+	if (unlikely(!((K[0] ^ K[2]) | (K[1] ^ K[3])) ||
+				!((K[2] ^ K[4]) | (K[3] ^ K[5]))) &&
+			(*flags & CRYPTO_TFM_REQ_WEAK_KEY)) {
+		*flags |= CRYPTO_TFM_RES_WEAK_KEY;
+		pr_debug(DEV_DBG_NAME " [%s]: CRYPTO_TFM_REQ_WEAK_KEY",
+				__func__);
+		return -EINVAL;
+	}
+	for (i = 0; i < 3; i++) {
+		ret = des_ekey(tmp, key + i*DES_KEY_SIZE);
+		if (unlikely(ret == 0) && (*flags & CRYPTO_TFM_REQ_WEAK_KEY)) {
+			*flags |= CRYPTO_TFM_RES_WEAK_KEY;
+			pr_debug(DEV_DBG_NAME " [%s]: "
+					"CRYPTO_TFM_REQ_WEAK_KEY", __func__);
+			return -EINVAL;
+		}
+	}
+
+	memcpy(ctx->key, key, keylen);
+	ctx->keylen = keylen;
+
+	ctx->updated = 0;
+	return 0;
+}
+
+static int cryp_blk_encrypt(struct ablkcipher_request *areq)
+{
+	struct crypto_ablkcipher *cipher = crypto_ablkcipher_reqtfm(areq);
+	struct cryp_ctx *ctx = crypto_ablkcipher_ctx(cipher);
+
+	pr_debug(DEV_DBG_NAME " [%s]", __func__);
+
+	ctx->config.algodir = CRYP_ALGORITHM_ENCRYPT;
+
+	/*
+	 * DMA does not work for DES due to a hw bug */
+	if (cryp_mode == CRYP_MODE_DMA && mode_is_aes(ctx->config.algomode))
+		return ablk_dma_crypt(areq);
+
+	/* For everything except DMA, we run the non DMA version. */
+	return ablk_crypt(areq);
+}
+
+static int cryp_blk_decrypt(struct ablkcipher_request *areq)
+{
+	struct crypto_ablkcipher *cipher = crypto_ablkcipher_reqtfm(areq);
+	struct cryp_ctx *ctx = crypto_ablkcipher_ctx(cipher);
+
+	pr_debug(DEV_DBG_NAME " [%s]", __func__);
+
+	ctx->config.algodir = CRYP_ALGORITHM_DECRYPT;
+
+	/* DMA does not work for DES due to a hw bug */
+	if (cryp_mode == CRYP_MODE_DMA && mode_is_aes(ctx->config.algomode))
+		return ablk_dma_crypt(areq);
+
+	/* For everything except DMA, we run the non DMA version. */
+	return ablk_crypt(areq);
+}
+
+struct cryp_algo_template {
+	enum cryp_algo_mode algomode;
+	struct crypto_alg crypto;
+};
+
+static int cryp_cra_init(struct crypto_tfm *tfm)
+{
+	struct cryp_ctx *ctx = crypto_tfm_ctx(tfm);
+	struct crypto_alg *alg = tfm->__crt_alg;
+	struct cryp_algo_template *cryp_alg = container_of(alg,
+			struct cryp_algo_template,
+			crypto);
+
+	ctx->config.algomode = cryp_alg->algomode;
+	ctx->blocksize = crypto_tfm_alg_blocksize(tfm);
+
+	return 0;
+}
+
+static struct cryp_algo_template cryp_algs[] = {
+	{
+		.algomode = CRYP_ALGO_AES_ECB,
+		.crypto = {
+			.cra_name = "aes",
+			.cra_driver_name = "aes-ux500",
+			.cra_priority =	300,
+			.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+					CRYPTO_ALG_ASYNC,
+			.cra_blocksize = AES_BLOCK_SIZE,
+			.cra_ctxsize = sizeof(struct cryp_ctx),
+			.cra_alignmask = 3,
+			.cra_type = &crypto_ablkcipher_type,
+			.cra_init = cryp_cra_init,
+			.cra_module = THIS_MODULE,
+			.cra_u = {
+				.ablkcipher = {
+					.min_keysize = AES_MIN_KEY_SIZE,
+					.max_keysize = AES_MAX_KEY_SIZE,
+					.setkey = aes_ablkcipher_setkey,
+					.encrypt = cryp_blk_encrypt,
+					.decrypt = cryp_blk_decrypt
+				}
+			}
+		}
+	},
+	{
+		.algomode = CRYP_ALGO_AES_ECB,
+		.crypto = {
+			.cra_name = "ecb(aes)",
+			.cra_driver_name = "ecb-aes-ux500",
+			.cra_priority = 300,
+			.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+					CRYPTO_ALG_ASYNC,
+			.cra_blocksize = AES_BLOCK_SIZE,
+			.cra_ctxsize = sizeof(struct cryp_ctx),
+			.cra_alignmask = 3,
+			.cra_type = &crypto_ablkcipher_type,
+			.cra_init = cryp_cra_init,
+			.cra_module = THIS_MODULE,
+			.cra_u = {
+				.ablkcipher = {
+					.min_keysize = AES_MIN_KEY_SIZE,
+					.max_keysize = AES_MAX_KEY_SIZE,
+					.setkey = aes_ablkcipher_setkey,
+					.encrypt = cryp_blk_encrypt,
+					.decrypt = cryp_blk_decrypt,
+				}
+			}
+		}
+	},
+	{
+		.algomode = CRYP_ALGO_AES_CBC,
+		.crypto = {
+			.cra_name = "cbc(aes)",
+			.cra_driver_name = "cbc-aes-ux500",
+			.cra_priority = 300,
+			.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+					CRYPTO_ALG_ASYNC,
+			.cra_blocksize = AES_BLOCK_SIZE,
+			.cra_ctxsize = sizeof(struct cryp_ctx),
+			.cra_alignmask = 3,
+			.cra_type = &crypto_ablkcipher_type,
+			.cra_init = cryp_cra_init,
+			.cra_module = THIS_MODULE,
+			.cra_u = {
+				.ablkcipher = {
+					.min_keysize = AES_MIN_KEY_SIZE,
+					.max_keysize = AES_MAX_KEY_SIZE,
+					.setkey = aes_ablkcipher_setkey,
+					.encrypt = cryp_blk_encrypt,
+					.decrypt = cryp_blk_decrypt,
+					.ivsize = AES_BLOCK_SIZE,
+				}
+			}
+		}
+	},
+	{
+		.algomode = CRYP_ALGO_AES_CTR,
+		.crypto = {
+			.cra_name = "ctr(aes)",
+			.cra_driver_name = "ctr-aes-ux500",
+			.cra_priority = 300,
+			.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+						CRYPTO_ALG_ASYNC,
+			.cra_blocksize = AES_BLOCK_SIZE,
+			.cra_ctxsize = sizeof(struct cryp_ctx),
+			.cra_alignmask = 3,
+			.cra_type = &crypto_ablkcipher_type,
+			.cra_init = cryp_cra_init,
+			.cra_module = THIS_MODULE,
+			.cra_u = {
+				.ablkcipher = {
+					.min_keysize = AES_MIN_KEY_SIZE,
+					.max_keysize = AES_MAX_KEY_SIZE,
+					.setkey = aes_ablkcipher_setkey,
+					.encrypt = cryp_blk_encrypt,
+					.decrypt = cryp_blk_decrypt,
+					.ivsize = AES_BLOCK_SIZE,
+				}
+			}
+		}
+	},
+	{
+		.algomode = CRYP_ALGO_DES_ECB,
+		.crypto = {
+			.cra_name = "des",
+			.cra_driver_name = "des-ux500",
+			.cra_priority = 300,
+			.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+						CRYPTO_ALG_ASYNC,
+			.cra_blocksize = DES_BLOCK_SIZE,
+			.cra_ctxsize = sizeof(struct cryp_ctx),
+			.cra_alignmask = 3,
+			.cra_type = &crypto_ablkcipher_type,
+			.cra_init = cryp_cra_init,
+			.cra_module = THIS_MODULE,
+			.cra_u = {
+				.ablkcipher = {
+					.min_keysize = DES_KEY_SIZE,
+					.max_keysize = DES_KEY_SIZE,
+					.setkey = des_ablkcipher_setkey,
+					.encrypt = cryp_blk_encrypt,
+					.decrypt = cryp_blk_decrypt
+				}
+			}
+		}
+
+	},
+	{
+		.algomode = CRYP_ALGO_TDES_ECB,
+		.crypto = {
+			.cra_name = "des3_ede",
+			.cra_driver_name = "des3_ede-ux500",
+			.cra_priority = 300,
+			.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+						CRYPTO_ALG_ASYNC,
+			.cra_blocksize = DES3_EDE_BLOCK_SIZE,
+			.cra_ctxsize = sizeof(struct cryp_ctx),
+			.cra_alignmask = 3,
+			.cra_type = &crypto_ablkcipher_type,
+			.cra_init = cryp_cra_init,
+			.cra_module = THIS_MODULE,
+			.cra_u = {
+				.ablkcipher = {
+					.min_keysize = DES3_EDE_KEY_SIZE,
+					.max_keysize = DES3_EDE_KEY_SIZE,
+					.setkey = des_ablkcipher_setkey,
+					.encrypt = cryp_blk_encrypt,
+					.decrypt = cryp_blk_decrypt
+				}
+			}
+		}
+	},
+	{
+		.algomode = CRYP_ALGO_DES_ECB,
+		.crypto = {
+			.cra_name = "ecb(des)",
+			.cra_driver_name = "ecb-des-ux500",
+			.cra_priority = 300,
+			.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+					CRYPTO_ALG_ASYNC,
+			.cra_blocksize = DES_BLOCK_SIZE,
+			.cra_ctxsize = sizeof(struct cryp_ctx),
+			.cra_alignmask = 3,
+			.cra_type = &crypto_ablkcipher_type,
+			.cra_init = cryp_cra_init,
+			.cra_module = THIS_MODULE,
+			.cra_u = {
+				.ablkcipher = {
+					.min_keysize = DES_KEY_SIZE,
+					.max_keysize = DES_KEY_SIZE,
+					.setkey = des_ablkcipher_setkey,
+					.encrypt = cryp_blk_encrypt,
+					.decrypt = cryp_blk_decrypt,
+				}
+			}
+		}
+	},
+	{
+		.algomode = CRYP_ALGO_TDES_ECB,
+		.crypto = {
+			.cra_name = "ecb(des3_ede)",
+			.cra_driver_name = "ecb-des3_ede-ux500",
+			.cra_priority = 300,
+			.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+					CRYPTO_ALG_ASYNC,
+			.cra_blocksize = DES3_EDE_BLOCK_SIZE,
+			.cra_ctxsize = sizeof(struct cryp_ctx),
+			.cra_alignmask = 3,
+			.cra_type = &crypto_ablkcipher_type,
+			.cra_init = cryp_cra_init,
+			.cra_module = THIS_MODULE,
+			.cra_u = {
+				.ablkcipher = {
+					.min_keysize = DES3_EDE_KEY_SIZE,
+					.max_keysize = DES3_EDE_KEY_SIZE,
+					.setkey = des3_ablkcipher_setkey,
+					.encrypt = cryp_blk_encrypt,
+					.decrypt = cryp_blk_decrypt,
+				}
+			}
+		}
+	},
+	{
+		.algomode = CRYP_ALGO_DES_CBC,
+		.crypto = {
+			.cra_name = "cbc(des)",
+			.cra_driver_name = "cbc-des-ux500",
+			.cra_priority = 300,
+			.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+					CRYPTO_ALG_ASYNC,
+			.cra_blocksize = DES_BLOCK_SIZE,
+			.cra_ctxsize = sizeof(struct cryp_ctx),
+			.cra_alignmask = 3,
+			.cra_type = &crypto_ablkcipher_type,
+			.cra_init = cryp_cra_init,
+			.cra_module = THIS_MODULE,
+			.cra_u = {
+				.ablkcipher = {
+					.min_keysize = DES_KEY_SIZE,
+					.max_keysize = DES_KEY_SIZE,
+					.setkey = des_ablkcipher_setkey,
+					.encrypt = cryp_blk_encrypt,
+					.decrypt = cryp_blk_decrypt,
+				}
+			}
+		}
+	},
+	{
+		.algomode = CRYP_ALGO_TDES_CBC,
+		.crypto = {
+			.cra_name = "cbc(des3_ede)",
+			.cra_driver_name = "cbc-des3_ede-ux500",
+			.cra_priority = 300,
+			.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+					CRYPTO_ALG_ASYNC,
+			.cra_blocksize = DES3_EDE_BLOCK_SIZE,
+			.cra_ctxsize = sizeof(struct cryp_ctx),
+			.cra_alignmask = 3,
+			.cra_type = &crypto_ablkcipher_type,
+			.cra_init = cryp_cra_init,
+			.cra_module = THIS_MODULE,
+			.cra_u = {
+				.ablkcipher = {
+					.min_keysize = DES3_EDE_KEY_SIZE,
+					.max_keysize = DES3_EDE_KEY_SIZE,
+					.setkey = des3_ablkcipher_setkey,
+					.encrypt = cryp_blk_encrypt,
+					.decrypt = cryp_blk_decrypt,
+					.ivsize = DES3_EDE_BLOCK_SIZE,
+				}
+			}
+		}
+	}
+};
+
+/**
+ * cryp_algs_register_all -
+ */
+static int cryp_algs_register_all(void)
+{
+	int ret;
+	int i;
+	int count;
+
+	pr_debug("[%s]", __func__);
+
+	for (i = 0; i < ARRAY_SIZE(cryp_algs); i++) {
+		ret = crypto_register_alg(&cryp_algs[i].crypto);
+		if (ret) {
+			count = i;
+			pr_err("[%s] alg registration failed",
+					cryp_algs[i].crypto.cra_driver_name);
+			goto unreg;
+		}
+	}
+	return 0;
+unreg:
+	for (i = 0; i < count; i++)
+		crypto_unregister_alg(&cryp_algs[i].crypto);
+	return ret;
+}
+
+/**
+ * cryp_algs_unregister_all -
+ */
+static void cryp_algs_unregister_all(void)
+{
+	int i;
+
+	pr_debug(DEV_DBG_NAME " [%s]", __func__);
+
+	for (i = 0; i < ARRAY_SIZE(cryp_algs); i++)
+		crypto_unregister_alg(&cryp_algs[i].crypto);
+}
+
+static int ux500_cryp_probe(struct platform_device *pdev)
+{
+	int ret;
+	int cryp_error = 0;
+	struct resource *res = NULL;
+	struct resource *res_irq = NULL;
+	struct cryp_device_data *device_data;
+	struct cryp_protection_config prot = {
+		.privilege_access = CRYP_STATE_ENABLE
+	};
+	struct device *dev = &pdev->dev;
+
+	dev_dbg(dev, "[%s]", __func__);
+	device_data = kzalloc(sizeof(struct cryp_device_data), GFP_ATOMIC);
+	if (!device_data) {
+		dev_err(dev, "[%s]: kzalloc() failed!", __func__);
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	device_data->dev = dev;
+	device_data->current_ctx = NULL;
+
+	/* Grab the DMA configuration from platform data. */
+	mem_to_engine = &((struct cryp_platform_data *)
+			 dev->platform_data)->mem_to_engine;
+	engine_to_mem = &((struct cryp_platform_data *)
+			 dev->platform_data)->engine_to_mem;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(dev, "[%s]: platform_get_resource() failed",
+				__func__);
+		ret = -ENODEV;
+		goto out_kfree;
+	}
+
+	res = request_mem_region(res->start, resource_size(res), pdev->name);
+	if (res == NULL) {
+		dev_err(dev, "[%s]: request_mem_region() failed",
+				__func__);
+		ret = -EBUSY;
+		goto out_kfree;
+	}
+
+	device_data->base = ioremap(res->start, resource_size(res));
+	if (!device_data->base) {
+		dev_err(dev, "[%s]: ioremap failed!", __func__);
+		ret = -ENOMEM;
+		goto out_free_mem;
+	}
+
+	spin_lock_init(&device_data->ctx_lock);
+	spin_lock_init(&device_data->power_state_spinlock);
+
+	/* Enable power for CRYP hardware block */
+	device_data->pwr_regulator = regulator_get(&pdev->dev, "v-ape");
+	if (IS_ERR(device_data->pwr_regulator)) {
+		dev_err(dev, "[%s]: could not get cryp regulator", __func__);
+		ret = PTR_ERR(device_data->pwr_regulator);
+		device_data->pwr_regulator = NULL;
+		goto out_unmap;
+	}
+
+	/* Enable the clk for CRYP hardware block */
+	device_data->clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(device_data->clk)) {
+		dev_err(dev, "[%s]: clk_get() failed!", __func__);
+		ret = PTR_ERR(device_data->clk);
+		goto out_regulator;
+	}
+
+	/* Enable device power (and clock) */
+	ret = cryp_enable_power(device_data->dev, device_data, false);
+	if (ret) {
+		dev_err(dev, "[%s]: cryp_enable_power() failed!", __func__);
+		goto out_clk;
+	}
+
+	cryp_error = cryp_check(device_data);
+	if (cryp_error != 0) {
+		dev_err(dev, "[%s]: cryp_init() failed!", __func__);
+		ret = -EINVAL;
+		goto out_power;
+	}
+
+	cryp_error = cryp_configure_protection(device_data, &prot);
+	if (cryp_error != 0) {
+		dev_err(dev, "[%s]: cryp_configure_protection() failed!",
+			__func__);
+		ret = -EINVAL;
+		goto out_power;
+	}
+
+	res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!res_irq) {
+		dev_err(dev, "[%s]: IORESOURCE_IRQ unavailable",
+			__func__);
+		goto out_power;
+	}
+
+	ret = request_irq(res_irq->start,
+			  cryp_interrupt_handler,
+			  0,
+			  "cryp1",
+			  device_data);
+	if (ret) {
+		dev_err(dev, "[%s]: Unable to request IRQ", __func__);
+		goto out_power;
+	}
+
+	if (cryp_mode == CRYP_MODE_DMA)
+		cryp_dma_setup_channel(device_data, dev);
+
+	platform_set_drvdata(pdev, device_data);
+
+	/* Put the new device into the device list... */
+	klist_add_tail(&device_data->list_node, &driver_data.device_list);
+
+	/* ... and signal that a new device is available. */
+	up(&driver_data.device_allocation);
+
+	atomic_set(&session_id, 1);
+
+	ret = cryp_algs_register_all();
+	if (ret) {
+		dev_err(dev, "[%s]: cryp_algs_register_all() failed!",
+			__func__);
+		goto out_power;
+	}
+
+	return 0;
+
+out_power:
+	cryp_disable_power(device_data->dev, device_data, false);
+
+out_clk:
+	clk_put(device_data->clk);
+
+out_regulator:
+	regulator_put(device_data->pwr_regulator);
+
+out_unmap:
+	iounmap(device_data->base);
+
+out_free_mem:
+	release_mem_region(res->start, resource_size(res));
+
+out_kfree:
+	kfree(device_data);
+out:
+	return ret;
+}
+
+static int ux500_cryp_remove(struct platform_device *pdev)
+{
+	struct resource *res = NULL;
+	struct resource *res_irq = NULL;
+	struct cryp_device_data *device_data;
+
+	dev_dbg(&pdev->dev, "[%s]", __func__);
+	device_data = platform_get_drvdata(pdev);
+	if (!device_data) {
+		dev_err(&pdev->dev, "[%s]: platform_get_drvdata() failed!",
+			__func__);
+		return -ENOMEM;
+	}
+
+	/* Try to decrease the number of available devices. */
+	if (down_trylock(&driver_data.device_allocation))
+		return -EBUSY;
+
+	/* Check that the device is free */
+	spin_lock(&device_data->ctx_lock);
+	/* current_ctx allocates a device, NULL = unallocated */
+	if (device_data->current_ctx) {
+		/* The device is busy */
+		spin_unlock(&device_data->ctx_lock);
+		/* Return the device to the pool. */
+		up(&driver_data.device_allocation);
+		return -EBUSY;
+	}
+
+	spin_unlock(&device_data->ctx_lock);
+
+	/* Remove the device from the list */
+	if (klist_node_attached(&device_data->list_node))
+		klist_remove(&device_data->list_node);
+
+	/* If this was the last device, remove the services */
+	if (list_empty(&driver_data.device_list.k_list))
+		cryp_algs_unregister_all();
+
+	res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!res_irq)
+		dev_err(&pdev->dev, "[%s]: IORESOURCE_IRQ, unavailable",
+			__func__);
+	else {
+		disable_irq(res_irq->start);
+		free_irq(res_irq->start, device_data);
+	}
+
+	if (cryp_disable_power(&pdev->dev, device_data, false))
+		dev_err(&pdev->dev, "[%s]: cryp_disable_power() failed",
+			__func__);
+
+	clk_put(device_data->clk);
+	regulator_put(device_data->pwr_regulator);
+
+	iounmap(device_data->base);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res)
+		release_mem_region(res->start, res->end - res->start + 1);
+
+	kfree(device_data);
+
+	return 0;
+}
+
+static void ux500_cryp_shutdown(struct platform_device *pdev)
+{
+	struct resource *res_irq = NULL;
+	struct cryp_device_data *device_data;
+
+	dev_dbg(&pdev->dev, "[%s]", __func__);
+
+	device_data = platform_get_drvdata(pdev);
+	if (!device_data) {
+		dev_err(&pdev->dev, "[%s]: platform_get_drvdata() failed!",
+			__func__);
+		return;
+	}
+
+	/* Check that the device is free */
+	spin_lock(&device_data->ctx_lock);
+	/* current_ctx allocates a device, NULL = unallocated */
+	if (!device_data->current_ctx) {
+		if (down_trylock(&driver_data.device_allocation))
+			dev_dbg(&pdev->dev, "[%s]: Cryp still in use!"
+				"Shutting down anyway...", __func__);
+		/**
+		 * (Allocate the device)
+		 * Need to set this to non-null (dummy) value,
+		 * to avoid usage if context switching.
+		 */
+		device_data->current_ctx++;
+	}
+	spin_unlock(&device_data->ctx_lock);
+
+	/* Remove the device from the list */
+	if (klist_node_attached(&device_data->list_node))
+		klist_remove(&device_data->list_node);
+
+	/* If this was the last device, remove the services */
+	if (list_empty(&driver_data.device_list.k_list))
+		cryp_algs_unregister_all();
+
+	res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!res_irq)
+		dev_err(&pdev->dev, "[%s]: IORESOURCE_IRQ, unavailable",
+			__func__);
+	else {
+		disable_irq(res_irq->start);
+		free_irq(res_irq->start, device_data);
+	}
+
+	if (cryp_disable_power(&pdev->dev, device_data, false))
+		dev_err(&pdev->dev, "[%s]: cryp_disable_power() failed",
+			__func__);
+
+}
+
+static int ux500_cryp_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	int ret;
+	struct cryp_device_data *device_data;
+	struct resource *res_irq;
+	struct cryp_ctx *temp_ctx = NULL;
+
+	dev_dbg(&pdev->dev, "[%s]", __func__);
+
+	/* Handle state? */
+	device_data = platform_get_drvdata(pdev);
+	if (!device_data) {
+		dev_err(&pdev->dev, "[%s]: platform_get_drvdata() failed!",
+			__func__);
+		return -ENOMEM;
+	}
+
+	res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!res_irq)
+		dev_err(&pdev->dev, "[%s]: IORESOURCE_IRQ, unavailable",
+			__func__);
+	else
+		disable_irq(res_irq->start);
+
+	spin_lock(&device_data->ctx_lock);
+	if (!device_data->current_ctx)
+		device_data->current_ctx++;
+	spin_unlock(&device_data->ctx_lock);
+
+	if (device_data->current_ctx == ++temp_ctx) {
+		if (down_interruptible(&driver_data.device_allocation))
+			dev_dbg(&pdev->dev, "[%s]: down_interruptible() "
+					"failed", __func__);
+		ret = cryp_disable_power(&pdev->dev, device_data, false);
+
+	} else
+		ret = cryp_disable_power(&pdev->dev, device_data, true);
+
+	if (ret)
+		dev_err(&pdev->dev, "[%s]: cryp_disable_power()", __func__);
+
+	return ret;
+}
+
+static int ux500_cryp_resume(struct platform_device *pdev)
+{
+	int ret = 0;
+	struct cryp_device_data *device_data;
+	struct resource *res_irq;
+	struct cryp_ctx *temp_ctx = NULL;
+
+	dev_dbg(&pdev->dev, "[%s]", __func__);
+
+	device_data = platform_get_drvdata(pdev);
+	if (!device_data) {
+		dev_err(&pdev->dev, "[%s]: platform_get_drvdata() failed!",
+			__func__);
+		return -ENOMEM;
+	}
+
+	spin_lock(&device_data->ctx_lock);
+	if (device_data->current_ctx == ++temp_ctx)
+		device_data->current_ctx = NULL;
+	spin_unlock(&device_data->ctx_lock);
+
+
+	if (!device_data->current_ctx)
+		up(&driver_data.device_allocation);
+	else
+		ret = cryp_enable_power(&pdev->dev, device_data, true);
+
+	if (ret)
+		dev_err(&pdev->dev, "[%s]: cryp_enable_power() failed!",
+			__func__);
+	else {
+		res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+		if (res_irq)
+			enable_irq(res_irq->start);
+	}
+
+	return ret;
+}
+
+static struct platform_driver cryp_driver = {
+	.probe  = ux500_cryp_probe,
+	.remove = ux500_cryp_remove,
+	.shutdown = ux500_cryp_shutdown,
+	.suspend  = ux500_cryp_suspend,
+	.resume   = ux500_cryp_resume,
+	.driver = {
+		.owner = THIS_MODULE,
+		.name  = "cryp1"
+	}
+};
+
+static int __init ux500_cryp_mod_init(void)
+{
+	pr_debug("[%s] is called!", __func__);
+	klist_init(&driver_data.device_list, NULL, NULL);
+	/* Initialize the semaphore to 0 devices (locked state) */
+	sema_init(&driver_data.device_allocation, 0);
+	return platform_driver_register(&cryp_driver);
+}
+
+static void __exit ux500_cryp_mod_fini(void)
+{
+	pr_debug("[%s] is called!", __func__);
+	platform_driver_unregister(&cryp_driver);
+	return;
+}
+
+module_init(ux500_cryp_mod_init);
+module_exit(ux500_cryp_mod_fini);
+
+module_param(cryp_mode, int, 0);
+
+MODULE_DESCRIPTION("Driver for ST-Ericsson UX500 CRYP crypto engine.");
+MODULE_ALIAS("aes-all");
+MODULE_ALIAS("des-all");
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/crypto/ux500/cryp/cryp_irq.c b/drivers/crypto/ux500/cryp/cryp_irq.c
new file mode 100644
index 0000000..08d291c
--- /dev/null
+++ b/drivers/crypto/ux500/cryp/cryp_irq.c
@@ -0,0 +1,45 @@
+/**
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Shujuan Chen <shujuan.chen@stericsson.com> for ST-Ericsson.
+ * Author: Jonas Linde <jonas.linde@stericsson.com> for ST-Ericsson.
+ * Author: Joakim Bech <joakim.xx.bech@stericsson.com> for ST-Ericsson.
+ * Author: Berne Hebark <berne.herbark@stericsson.com> for ST-Ericsson.
+ * Author: Niklas Hernaeus <niklas.hernaeus@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/bitmap.h>
+#include <linux/device.h>
+
+#include "cryp.h"
+#include "cryp_p.h"
+#include "cryp_irq.h"
+#include "cryp_irqp.h"
+
+void cryp_enable_irq_src(struct cryp_device_data *device_data, u32 irq_src)
+{
+	u32 i;
+
+	dev_dbg(device_data->dev, "[%s]", __func__);
+
+	i = readl_relaxed(&device_data->base->imsc);
+	i = i | irq_src;
+	writel_relaxed(i, &device_data->base->imsc);
+}
+
+void cryp_disable_irq_src(struct cryp_device_data *device_data, u32 irq_src)
+{
+	u32 i;
+
+	dev_dbg(device_data->dev, "[%s]", __func__);
+
+	i = readl_relaxed(&device_data->base->imsc);
+	i = i & ~irq_src;
+	writel_relaxed(i, &device_data->base->imsc);
+}
+
+bool cryp_pending_irq_src(struct cryp_device_data *device_data, u32 irq_src)
+{
+	return (readl_relaxed(&device_data->base->mis) & irq_src) > 0;
+}
diff --git a/drivers/crypto/ux500/cryp/cryp_irq.h b/drivers/crypto/ux500/cryp/cryp_irq.h
new file mode 100644
index 0000000..5a7837f
--- /dev/null
+++ b/drivers/crypto/ux500/cryp/cryp_irq.h
@@ -0,0 +1,31 @@
+/**
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Shujuan Chen <shujuan.chen@stericsson.com> for ST-Ericsson.
+ * Author: Jonas Linde <jonas.linde@stericsson.com> for ST-Ericsson.
+ * Author: Joakim Bech <joakim.xx.bech@stericsson.com> for ST-Ericsson.
+ * Author: Berne Hebark <berne.herbark@stericsson.com> for ST-Ericsson.
+ * Author: Niklas Hernaeus <niklas.hernaeus@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef _CRYP_IRQ_H_
+#define _CRYP_IRQ_H_
+
+#include "cryp.h"
+
+enum cryp_irq_src_id {
+	CRYP_IRQ_SRC_INPUT_FIFO = 0x1,
+	CRYP_IRQ_SRC_OUTPUT_FIFO = 0x2,
+	CRYP_IRQ_SRC_ALL = 0x3
+};
+
+/**
+ * M0 Funtions
+ */
+void cryp_enable_irq_src(struct cryp_device_data *device_data, u32 irq_src);
+
+void cryp_disable_irq_src(struct cryp_device_data *device_data, u32 irq_src);
+
+bool cryp_pending_irq_src(struct cryp_device_data *device_data, u32 irq_src);
+
+#endif				/* _CRYP_IRQ_H_ */
diff --git a/drivers/crypto/ux500/cryp/cryp_irqp.h b/drivers/crypto/ux500/cryp/cryp_irqp.h
new file mode 100644
index 0000000..8b339cc
--- /dev/null
+++ b/drivers/crypto/ux500/cryp/cryp_irqp.h
@@ -0,0 +1,125 @@
+/**
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Shujuan Chen <shujuan.chen@stericsson.com> for ST-Ericsson.
+ * Author: Jonas Linde <jonas.linde@stericsson.com> for ST-Ericsson.
+ * Author: Joakim Bech <joakim.xx.bech@stericsson.com> for ST-Ericsson.
+ * Author: Berne Hebark <berne.herbark@stericsson.com> for ST-Ericsson.
+ * Author: Niklas Hernaeus <niklas.hernaeus@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef __CRYP_IRQP_H_
+#define __CRYP_IRQP_H_
+
+#include "cryp_irq.h"
+
+/**
+ *
+ * CRYP Registers - Offset mapping
+ *     +-----------------+
+ * 00h | CRYP_CR         |  Configuration register
+ *     +-----------------+
+ * 04h | CRYP_SR         |  Status register
+ *     +-----------------+
+ * 08h | CRYP_DIN        |  Data In register
+ *     +-----------------+
+ * 0ch | CRYP_DOUT       |  Data out register
+ *     +-----------------+
+ * 10h | CRYP_DMACR      |  DMA control register
+ *     +-----------------+
+ * 14h | CRYP_IMSC       |  IMSC
+ *     +-----------------+
+ * 18h | CRYP_RIS        |  Raw interrupt status
+ *     +-----------------+
+ * 1ch | CRYP_MIS        |  Masked interrupt status.
+ *     +-----------------+
+ *       Key registers
+ *       IVR registers
+ *       Peripheral
+ *       Cell IDs
+ *
+ *       Refer data structure for other register map
+ */
+
+/**
+ * struct cryp_register
+ * @cr			- Configuration register
+ * @status		- Status register
+ * @din			- Data input register
+ * @din_size		- Data input size register
+ * @dout		- Data output register
+ * @dout_size		- Data output size register
+ * @dmacr		- Dma control register
+ * @imsc		- Interrupt mask set/clear register
+ * @ris			- Raw interrupt status
+ * @mis			- Masked interrupt statu register
+ * @key_1_l		- Key register 1 L
+ * @key_1_r		- Key register 1 R
+ * @key_2_l		- Key register 2 L
+ * @key_2_r		- Key register 2 R
+ * @key_3_l		- Key register 3 L
+ * @key_3_r		- Key register 3 R
+ * @key_4_l		- Key register 4 L
+ * @key_4_r		- Key register 4 R
+ * @init_vect_0_l	- init vector 0 L
+ * @init_vect_0_r	- init vector 0 R
+ * @init_vect_1_l	- init vector 1 L
+ * @init_vect_1_r	- init vector 1 R
+ * @cryp_unused1	- unused registers
+ * @itcr		- Integration test control register
+ * @itip		- Integration test input register
+ * @itop		- Integration test output register
+ * @cryp_unused2	- unused registers
+ * @periphId0		- FE0 CRYP Peripheral Identication Register
+ * @periphId1		- FE4
+ * @periphId2		- FE8
+ * @periphId3		- FEC
+ * @pcellId0		- FF0  CRYP PCell Identication Register
+ * @pcellId1		- FF4
+ * @pcellId2		- FF8
+ * @pcellId3		- FFC
+ */
+struct cryp_register {
+	u32 cr;			/* Configuration register   */
+	u32 sr;			/* Status register          */
+	u32 din;		/* Data input register      */
+	u32 din_size;		/* Data input size register */
+	u32 dout;		/* Data output register     */
+	u32 dout_size;		/* Data output size register */
+	u32 dmacr;		/* Dma control register     */
+	u32 imsc;		/* Interrupt mask set/clear register */
+	u32 ris;		/* Raw interrupt status             */
+	u32 mis;		/* Masked interrupt statu register  */
+
+	u32 key_1_l;		/*Key register 1 L */
+	u32 key_1_r;		/*Key register 1 R */
+	u32 key_2_l;		/*Key register 2 L */
+	u32 key_2_r;		/*Key register 2 R */
+	u32 key_3_l;		/*Key register 3 L */
+	u32 key_3_r;		/*Key register 3 R */
+	u32 key_4_l;		/*Key register 4 L */
+	u32 key_4_r;		/*Key register 4 R */
+
+	u32 init_vect_0_l;	/*init vector 0 L */
+	u32 init_vect_0_r;	/*init vector 0 R */
+	u32 init_vect_1_l;	/*init vector 1 L */
+	u32 init_vect_1_r;	/*init vector 1 R */
+
+	u32 cryp_unused1[(0x80 - 0x58) / sizeof(u32)];	/* unused registers */
+	u32 itcr;		/*Integration test control register */
+	u32 itip;		/*Integration test input register */
+	u32 itop;		/*Integration test output register */
+	u32 cryp_unused2[(0xFE0 - 0x8C) / sizeof(u32)];	/* unused registers */
+
+	u32 periphId0;		/* FE0  CRYP Peripheral Identication Register */
+	u32 periphId1;		/* FE4 */
+	u32 periphId2;		/* FE8 */
+	u32 periphId3;		/* FEC */
+
+	u32 pcellId0;		/* FF0  CRYP PCell Identication Register */
+	u32 pcellId1;		/* FF4 */
+	u32 pcellId2;		/* FF8 */
+	u32 pcellId3;		/* FFC */
+};
+
+#endif
diff --git a/drivers/crypto/ux500/cryp/cryp_p.h b/drivers/crypto/ux500/cryp/cryp_p.h
new file mode 100644
index 0000000..6dcffe1
--- /dev/null
+++ b/drivers/crypto/ux500/cryp/cryp_p.h
@@ -0,0 +1,123 @@
+/**
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Shujuan Chen <shujuan.chen@stericsson.com> for ST-Ericsson.
+ * Author: Jonas Linde <jonas.linde@stericsson.com> for ST-Ericsson.
+ * Author: Joakim Bech <joakim.xx.bech@stericsson.com> for ST-Ericsson.
+ * Author: Berne Hebark <berne.herbark@stericsson.com> for ST-Ericsson.
+ * Author: Niklas Hernaeus <niklas.hernaeus@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef _CRYP_P_H_
+#define _CRYP_P_H_
+
+#include <linux/io.h>
+#include <linux/bitops.h>
+
+#include "cryp.h"
+#include "cryp_irqp.h"
+
+/**
+ * Generic Macros
+ */
+#define CRYP_SET_BITS(reg_name, mask) \
+	writel_relaxed((readl_relaxed(reg_name) | mask), reg_name)
+
+#define CRYP_WRITE_BIT(reg_name, val, mask) \
+	writel_relaxed(((readl_relaxed(reg_name) & ~(mask)) |\
+			((val) & (mask))), reg_name)
+
+#define CRYP_TEST_BITS(reg_name, val) \
+	(readl_relaxed(reg_name) & (val))
+
+#define CRYP_PUT_BITS(reg, val, shift, mask) \
+	writel_relaxed(((readl_relaxed(reg) & ~(mask)) | \
+		(((u32)val << shift) & (mask))), reg)
+
+/**
+ * CRYP specific Macros
+ */
+#define CRYP_PERIPHERAL_ID0		0xE3
+#define CRYP_PERIPHERAL_ID1		0x05
+
+#define CRYP_PERIPHERAL_ID2_DB8500	0x28
+#define CRYP_PERIPHERAL_ID3		0x00
+
+#define CRYP_PCELL_ID0			0x0D
+#define CRYP_PCELL_ID1			0xF0
+#define CRYP_PCELL_ID2			0x05
+#define CRYP_PCELL_ID3			0xB1
+
+/**
+ * CRYP register default values
+ */
+#define MAX_DEVICE_SUPPORT		2
+
+/* Priv set, keyrden set and datatype 8bits swapped set as default. */
+#define CRYP_CR_DEFAULT			0x0482
+#define CRYP_DMACR_DEFAULT		0x0
+#define CRYP_IMSC_DEFAULT		0x0
+#define CRYP_DIN_DEFAULT		0x0
+#define CRYP_DOUT_DEFAULT		0x0
+#define CRYP_KEY_DEFAULT		0x0
+#define CRYP_INIT_VECT_DEFAULT		0x0
+
+/**
+ * CRYP Control register specific mask
+ */
+#define CRYP_CR_SECURE_MASK		BIT(0)
+#define CRYP_CR_PRLG_MASK		BIT(1)
+#define CRYP_CR_ALGODIR_MASK		BIT(2)
+#define CRYP_CR_ALGOMODE_MASK		(BIT(5) | BIT(4) | BIT(3))
+#define CRYP_CR_DATATYPE_MASK		(BIT(7) | BIT(6))
+#define CRYP_CR_KEYSIZE_MASK		(BIT(9) | BIT(8))
+#define CRYP_CR_KEYRDEN_MASK		BIT(10)
+#define CRYP_CR_KSE_MASK		BIT(11)
+#define CRYP_CR_START_MASK		BIT(12)
+#define CRYP_CR_INIT_MASK		BIT(13)
+#define CRYP_CR_FFLUSH_MASK		BIT(14)
+#define CRYP_CR_CRYPEN_MASK		BIT(15)
+#define CRYP_CR_CONTEXT_SAVE_MASK	(CRYP_CR_SECURE_MASK |\
+					 CRYP_CR_PRLG_MASK |\
+					 CRYP_CR_ALGODIR_MASK |\
+					 CRYP_CR_ALGOMODE_MASK |\
+					 CRYP_CR_DATATYPE_MASK |\
+					 CRYP_CR_KEYSIZE_MASK |\
+					 CRYP_CR_KEYRDEN_MASK |\
+					 CRYP_CR_DATATYPE_MASK)
+
+
+#define CRYP_SR_INFIFO_READY_MASK	(BIT(0) | BIT(1))
+#define CRYP_SR_IFEM_MASK		BIT(0)
+#define CRYP_SR_BUSY_MASK		BIT(4)
+
+/**
+ * Bit position used while setting bits in register
+ */
+#define CRYP_CR_PRLG_POS		1
+#define CRYP_CR_ALGODIR_POS		2
+#define CRYP_CR_ALGOMODE_POS		3
+#define CRYP_CR_DATATYPE_POS		6
+#define CRYP_CR_KEYSIZE_POS		8
+#define CRYP_CR_KEYRDEN_POS		10
+#define CRYP_CR_KSE_POS			11
+#define CRYP_CR_START_POS		12
+#define CRYP_CR_INIT_POS		13
+#define CRYP_CR_CRYPEN_POS		15
+
+#define CRYP_SR_BUSY_POS		4
+
+/**
+ * CRYP PCRs------PC_NAND control register
+ * BIT_MASK
+ */
+#define CRYP_DMA_REQ_MASK		(BIT(1) | BIT(0))
+#define CRYP_DMA_REQ_MASK_POS		0
+
+
+struct cryp_system_context {
+	/* CRYP Register structure */
+	struct cryp_register *p_cryp_reg[MAX_DEVICE_SUPPORT];
+};
+
+#endif
diff --git a/drivers/crypto/ux500/hash/Makefile b/drivers/crypto/ux500/hash/Makefile
new file mode 100644
index 0000000..b2f90d9
--- /dev/null
+++ b/drivers/crypto/ux500/hash/Makefile
@@ -0,0 +1,11 @@
+#
+# Copyright (C) ST-Ericsson SA 2010
+# Author: Shujuan Chen (shujuan.chen@stericsson.com)
+# License terms: GNU General Public License (GPL) version 2
+#
+ifdef CONFIG_CRYPTO_DEV_UX500_DEBUG
+CFLAGS_hash_core.o := -DDEBUG -O0
+endif
+
+obj-$(CONFIG_CRYPTO_DEV_UX500_HASH) += ux500_hash.o
+ux500_hash-objs :=  hash_core.o
diff --git a/drivers/crypto/ux500/hash/hash_alg.h b/drivers/crypto/ux500/hash/hash_alg.h
new file mode 100644
index 0000000..cd9351c
--- /dev/null
+++ b/drivers/crypto/ux500/hash/hash_alg.h
@@ -0,0 +1,395 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Shujuan Chen (shujuan.chen@stericsson.com)
+ * Author: Joakim Bech (joakim.xx.bech@stericsson.com)
+ * Author: Berne Hebark (berne.hebark@stericsson.com))
+ * License terms: GNU General Public License (GPL) version 2
+ */
+#ifndef _HASH_ALG_H
+#define _HASH_ALG_H
+
+#include <linux/bitops.h>
+
+#define HASH_BLOCK_SIZE			64
+#define HASH_DMA_ALIGN_SIZE		4
+#define HASH_DMA_PERFORMANCE_MIN_SIZE	1024
+#define HASH_BYTES_PER_WORD		4
+
+/* Maximum value of the length's high word */
+#define HASH_HIGH_WORD_MAX_VAL		0xFFFFFFFFUL
+
+/* Power on Reset values HASH registers */
+#define HASH_RESET_CR_VALUE		0x0
+#define HASH_RESET_STR_VALUE		0x0
+
+/* Number of context swap registers */
+#define HASH_CSR_COUNT			52
+
+#define HASH_RESET_CSRX_REG_VALUE	0x0
+#define HASH_RESET_CSFULL_REG_VALUE	0x0
+#define HASH_RESET_CSDATAIN_REG_VALUE	0x0
+
+#define HASH_RESET_INDEX_VAL		0x0
+#define HASH_RESET_BIT_INDEX_VAL	0x0
+#define HASH_RESET_BUFFER_VAL		0x0
+#define HASH_RESET_LEN_HIGH_VAL		0x0
+#define HASH_RESET_LEN_LOW_VAL		0x0
+
+/* Control register bitfields */
+#define HASH_CR_RESUME_MASK	0x11FCF
+
+#define HASH_CR_SWITCHON_POS	31
+#define HASH_CR_SWITCHON_MASK	BIT(31)
+
+#define HASH_CR_EMPTYMSG_POS	20
+#define HASH_CR_EMPTYMSG_MASK	BIT(20)
+
+#define HASH_CR_DINF_POS	12
+#define HASH_CR_DINF_MASK	BIT(12)
+
+#define HASH_CR_NBW_POS		8
+#define HASH_CR_NBW_MASK	0x00000F00UL
+
+#define HASH_CR_LKEY_POS	16
+#define HASH_CR_LKEY_MASK	BIT(16)
+
+#define HASH_CR_ALGO_POS	7
+#define HASH_CR_ALGO_MASK	BIT(7)
+
+#define HASH_CR_MODE_POS	6
+#define HASH_CR_MODE_MASK	BIT(6)
+
+#define HASH_CR_DATAFORM_POS	4
+#define HASH_CR_DATAFORM_MASK	(BIT(4) | BIT(5))
+
+#define HASH_CR_DMAE_POS	3
+#define HASH_CR_DMAE_MASK	BIT(3)
+
+#define HASH_CR_INIT_POS	2
+#define HASH_CR_INIT_MASK	BIT(2)
+
+#define HASH_CR_PRIVN_POS	1
+#define HASH_CR_PRIVN_MASK	BIT(1)
+
+#define HASH_CR_SECN_POS	0
+#define HASH_CR_SECN_MASK	BIT(0)
+
+/* Start register bitfields */
+#define HASH_STR_DCAL_POS	8
+#define HASH_STR_DCAL_MASK	BIT(8)
+#define HASH_STR_DEFAULT	0x0
+
+#define HASH_STR_NBLW_POS	0
+#define HASH_STR_NBLW_MASK	0x0000001FUL
+
+#define HASH_NBLW_MAX_VAL	0x1F
+
+/* PrimeCell IDs */
+#define HASH_P_ID0		0xE0
+#define HASH_P_ID1		0x05
+#define HASH_P_ID2		0x38
+#define HASH_P_ID3		0x00
+#define HASH_CELL_ID0		0x0D
+#define HASH_CELL_ID1		0xF0
+#define HASH_CELL_ID2		0x05
+#define HASH_CELL_ID3		0xB1
+
+#define HASH_SET_BITS(reg_name, mask)	\
+	writel_relaxed((readl_relaxed(reg_name) | mask), reg_name)
+
+#define HASH_CLEAR_BITS(reg_name, mask)	\
+	writel_relaxed((readl_relaxed(reg_name) & ~mask), reg_name)
+
+#define HASH_PUT_BITS(reg, val, shift, mask)	\
+	writel_relaxed(((readl(reg) & ~(mask)) |	\
+		(((u32)val << shift) & (mask))), reg)
+
+#define HASH_SET_DIN(val, len)	writesl(&device_data->base->din, (val), (len))
+
+#define HASH_INITIALIZE			\
+	HASH_PUT_BITS(			\
+		&device_data->base->cr,	\
+		0x01, HASH_CR_INIT_POS,	\
+		HASH_CR_INIT_MASK)
+
+#define HASH_SET_DATA_FORMAT(data_format)				\
+		HASH_PUT_BITS(						\
+			&device_data->base->cr,				\
+			(u32) (data_format), HASH_CR_DATAFORM_POS,	\
+			HASH_CR_DATAFORM_MASK)
+#define HASH_SET_NBLW(val)					\
+		HASH_PUT_BITS(					\
+			&device_data->base->str,		\
+			(u32) (val), HASH_STR_NBLW_POS,		\
+			HASH_STR_NBLW_MASK)
+#define HASH_SET_DCAL					\
+		HASH_PUT_BITS(				\
+			&device_data->base->str,	\
+			0x01, HASH_STR_DCAL_POS,	\
+			HASH_STR_DCAL_MASK)
+
+/* Hardware access method */
+enum hash_mode {
+	HASH_MODE_CPU,
+	HASH_MODE_DMA
+};
+
+/**
+ * struct uint64 - Structure to handle 64 bits integers.
+ * @high_word:	Most significant bits.
+ * @low_word:	Least significant bits.
+ *
+ * Used to handle 64 bits integers.
+ */
+struct uint64 {
+	u32 high_word;
+	u32 low_word;
+};
+
+/**
+ * struct hash_register - Contains all registers in ux500 hash hardware.
+ * @cr:		HASH control register (0x000).
+ * @din:	HASH data input register (0x004).
+ * @str:	HASH start register (0x008).
+ * @hx:		HASH digest register 0..7 (0x00c-0x01C).
+ * @padding0:	Reserved (0x02C).
+ * @itcr:	Integration test control register (0x080).
+ * @itip:	Integration test input register (0x084).
+ * @itop:	Integration test output register (0x088).
+ * @padding1:	Reserved (0x08C).
+ * @csfull:	HASH context full register (0x0F8).
+ * @csdatain:	HASH context swap data input register (0x0FC).
+ * @csrx:	HASH context swap register 0..51 (0x100-0x1CC).
+ * @padding2:	Reserved (0x1D0).
+ * @periphid0:	HASH peripheral identification register 0 (0xFE0).
+ * @periphid1:	HASH peripheral identification register 1 (0xFE4).
+ * @periphid2:	HASH peripheral identification register 2 (0xFE8).
+ * @periphid3:	HASH peripheral identification register 3 (0xFEC).
+ * @cellid0:	HASH PCell identification register 0 (0xFF0).
+ * @cellid1:	HASH PCell identification register 1 (0xFF4).
+ * @cellid2:	HASH PCell identification register 2 (0xFF8).
+ * @cellid3:	HASH PCell identification register 3 (0xFFC).
+ *
+ * The device communicates to the HASH via 32-bit-wide control registers
+ * accessible via the 32-bit width AMBA rev. 2.0 AHB Bus. Below is a structure
+ * with the registers used.
+ */
+struct hash_register {
+	u32 cr;
+	u32 din;
+	u32 str;
+	u32 hx[8];
+
+	u32 padding0[(0x080 - 0x02C) / sizeof(u32)];
+
+	u32 itcr;
+	u32 itip;
+	u32 itop;
+
+	u32 padding1[(0x0F8 - 0x08C) / sizeof(u32)];
+
+	u32 csfull;
+	u32 csdatain;
+	u32 csrx[HASH_CSR_COUNT];
+
+	u32 padding2[(0xFE0 - 0x1D0) / sizeof(u32)];
+
+	u32 periphid0;
+	u32 periphid1;
+	u32 periphid2;
+	u32 periphid3;
+
+	u32 cellid0;
+	u32 cellid1;
+	u32 cellid2;
+	u32 cellid3;
+};
+
+/**
+ * struct hash_state - Hash context state.
+ * @temp_cr:	Temporary HASH Control Register.
+ * @str_reg:	HASH Start Register.
+ * @din_reg:	HASH Data Input Register.
+ * @csr[52]:	HASH Context Swap Registers 0-39.
+ * @csfull:	HASH Context Swap Registers 40 ie Status flags.
+ * @csdatain:	HASH Context Swap Registers 41 ie Input data.
+ * @buffer:	Working buffer for messages going to the hardware.
+ * @length:	Length of the part of message hashed so far (floor(N/64) * 64).
+ * @index:	Valid number of bytes in buffer (N % 64).
+ * @bit_index:	Valid number of bits in buffer (N % 8).
+ *
+ * This structure is used between context switches, i.e. when ongoing jobs are
+ * interupted with new jobs. When this happens we need to store intermediate
+ * results in software.
+ *
+ * WARNING: "index" is the  member of the structure, to be sure  that "buffer"
+ * is aligned on a 4-bytes boundary. This is highly implementation dependent
+ * and MUST be checked whenever this code is ported on new platforms.
+ */
+struct hash_state {
+	u32		temp_cr;
+	u32		str_reg;
+	u32		din_reg;
+	u32		csr[52];
+	u32		csfull;
+	u32		csdatain;
+	u32		buffer[HASH_BLOCK_SIZE / sizeof(u32)];
+	struct uint64	length;
+	u8		index;
+	u8		bit_index;
+};
+
+/**
+ * enum hash_device_id - HASH device ID.
+ * @HASH_DEVICE_ID_0: Hash hardware with ID 0
+ * @HASH_DEVICE_ID_1: Hash hardware with ID 1
+ */
+enum hash_device_id {
+	HASH_DEVICE_ID_0 = 0,
+	HASH_DEVICE_ID_1 = 1
+};
+
+/**
+ * enum hash_data_format - HASH data format.
+ * @HASH_DATA_32_BITS:	32 bits data format
+ * @HASH_DATA_16_BITS:	16 bits data format
+ * @HASH_DATA_8_BITS:	8 bits data format.
+ * @HASH_DATA_1_BITS:	1 bit data format.
+ */
+enum hash_data_format {
+	HASH_DATA_32_BITS	= 0x0,
+	HASH_DATA_16_BITS	= 0x1,
+	HASH_DATA_8_BITS	= 0x2,
+	HASH_DATA_1_BIT		= 0x3
+};
+
+/**
+ * enum hash_algo - Enumeration for selecting between SHA1 or SHA2 algorithm.
+ * @HASH_ALGO_SHA1: Indicates that SHA1 is used.
+ * @HASH_ALGO_SHA2: Indicates that SHA2 (SHA256) is used.
+ */
+enum hash_algo {
+	HASH_ALGO_SHA1		= 0x0,
+	HASH_ALGO_SHA256	= 0x1
+};
+
+/**
+ * enum hash_op - Enumeration for selecting between HASH or HMAC mode.
+ * @HASH_OPER_MODE_HASH: Indicates usage of normal HASH mode.
+ * @HASH_OPER_MODE_HMAC: Indicates usage of HMAC.
+ */
+enum hash_op {
+	HASH_OPER_MODE_HASH = 0x0,
+	HASH_OPER_MODE_HMAC = 0x1
+};
+
+/**
+ * struct hash_config - Configuration data for the hardware.
+ * @data_format:	Format of data entered into the hash data in register.
+ * @algorithm:		Algorithm selection bit.
+ * @oper_mode:		Operating mode selection bit.
+ */
+struct hash_config {
+	int data_format;
+	int algorithm;
+	int oper_mode;
+};
+
+/**
+ * struct hash_dma - Structure used for dma.
+ * @mask:		DMA capabilities bitmap mask.
+ * @complete:		Used to maintain state for a "completion".
+ * @chan_mem2hash:	DMA channel.
+ * @cfg_mem2hash:	DMA channel configuration.
+ * @sg_len:		Scatterlist length.
+ * @sg:			Scatterlist.
+ * @nents:		Number of sg entries.
+ */
+struct hash_dma {
+	dma_cap_mask_t		mask;
+	struct completion	complete;
+	struct dma_chan		*chan_mem2hash;
+	void			*cfg_mem2hash;
+	int			sg_len;
+	struct scatterlist	*sg;
+	int			nents;
+};
+
+/**
+ * struct hash_ctx - The context used for hash calculations.
+ * @key:	The key used in the operation.
+ * @keylen:	The length of the key.
+ * @state:	The state of the current calculations.
+ * @config:	The current configuration.
+ * @digestsize:	The size of current digest.
+ * @device:	Pointer to the device structure.
+ */
+struct hash_ctx {
+	u8			*key;
+	u32			keylen;
+	struct hash_config	config;
+	int			digestsize;
+	struct hash_device_data	*device;
+};
+
+/**
+ * struct hash_ctx - The request context used for hash calculations.
+ * @state:	The state of the current calculations.
+ * @dma_mode:	Used in special cases (workaround), e.g. need to change to
+ *		cpu mode, if not supported/working in dma mode.
+ * @updated:	Indicates if hardware is initialized for new operations.
+ */
+struct hash_req_ctx {
+	struct hash_state	state;
+	bool			dma_mode;
+	u8			updated;
+};
+
+/**
+ * struct hash_device_data - structure for a hash device.
+ * @base:		Pointer to the hardware base address.
+ * @list_node:		For inclusion in klist.
+ * @dev:		Pointer to the device dev structure.
+ * @ctx_lock:		Spinlock for current_ctx.
+ * @current_ctx:	Pointer to the currently allocated context.
+ * @power_state:	TRUE = power state on, FALSE = power state off.
+ * @power_state_lock:	Spinlock for power_state.
+ * @regulator:		Pointer to the device's power control.
+ * @clk:		Pointer to the device's clock control.
+ * @restore_dev_state:	TRUE = saved state, FALSE = no saved state.
+ * @dma:		Structure used for dma.
+ */
+struct hash_device_data {
+	struct hash_register __iomem	*base;
+	struct klist_node	list_node;
+	struct device		*dev;
+	struct spinlock		ctx_lock;
+	struct hash_ctx		*current_ctx;
+	bool			power_state;
+	struct spinlock		power_state_lock;
+	struct regulator	*regulator;
+	struct clk		*clk;
+	bool			restore_dev_state;
+	struct hash_state	state; /* Used for saving and resuming state */
+	struct hash_dma		dma;
+};
+
+int hash_check_hw(struct hash_device_data *device_data);
+
+int hash_setconfiguration(struct hash_device_data *device_data,
+		struct hash_config *config);
+
+void hash_begin(struct hash_device_data *device_data, struct hash_ctx *ctx);
+
+void hash_get_digest(struct hash_device_data *device_data,
+		u8 *digest, int algorithm);
+
+int hash_hw_update(struct ahash_request *req);
+
+int hash_save_state(struct hash_device_data *device_data,
+		struct hash_state *state);
+
+int hash_resume_state(struct hash_device_data *device_data,
+		const struct hash_state *state);
+
+#endif
diff --git a/drivers/crypto/ux500/hash/hash_core.c b/drivers/crypto/ux500/hash/hash_core.c
new file mode 100644
index 0000000..6dbb9ec
--- /dev/null
+++ b/drivers/crypto/ux500/hash/hash_core.c
@@ -0,0 +1,2009 @@
+/*
+ * Cryptographic API.
+ * Support for Nomadik hardware crypto engine.
+
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Shujuan Chen <shujuan.chen@stericsson.com> for ST-Ericsson
+ * Author: Joakim Bech <joakim.xx.bech@stericsson.com> for ST-Ericsson
+ * Author: Berne Hebark <berne.herbark@stericsson.com> for ST-Ericsson.
+ * Author: Niklas Hernaeus <niklas.hernaeus@stericsson.com> for ST-Ericsson.
+ * Author: Andreas Westin <andreas.westin@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/klist.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/crypto.h>
+
+#include <linux/regulator/consumer.h>
+#include <linux/dmaengine.h>
+#include <linux/bitops.h>
+
+#include <crypto/internal/hash.h>
+#include <crypto/sha.h>
+#include <crypto/scatterwalk.h>
+#include <crypto/algapi.h>
+
+#include <mach/crypto-ux500.h>
+#include <mach/hardware.h>
+
+#include "hash_alg.h"
+
+#define DEV_DBG_NAME "hashX hashX:"
+
+static int hash_mode;
+module_param(hash_mode, int, 0);
+MODULE_PARM_DESC(hash_mode, "CPU or DMA mode. CPU = 0 (default), DMA = 1");
+
+/**
+ * Pre-calculated empty message digests.
+ */
+static u8 zero_message_hash_sha1[SHA1_DIGEST_SIZE] = {
+	0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d,
+	0x32, 0x55, 0xbf, 0xef, 0x95, 0x60, 0x18, 0x90,
+	0xaf, 0xd8, 0x07, 0x09
+};
+
+static u8 zero_message_hash_sha256[SHA256_DIGEST_SIZE] = {
+	0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14,
+	0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24,
+	0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c,
+	0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55
+};
+
+/* HMAC-SHA1, no key */
+static u8 zero_message_hmac_sha1[SHA1_DIGEST_SIZE] = {
+	0xfb, 0xdb, 0x1d, 0x1b, 0x18, 0xaa, 0x6c, 0x08,
+	0x32, 0x4b, 0x7d, 0x64, 0xb7, 0x1f, 0xb7, 0x63,
+	0x70, 0x69, 0x0e, 0x1d
+};
+
+/* HMAC-SHA256, no key */
+static u8 zero_message_hmac_sha256[SHA256_DIGEST_SIZE] = {
+	0xb6, 0x13, 0x67, 0x9a, 0x08, 0x14, 0xd9, 0xec,
+	0x77, 0x2f, 0x95, 0xd7, 0x78, 0xc3, 0x5f, 0xc5,
+	0xff, 0x16, 0x97, 0xc4, 0x93, 0x71, 0x56, 0x53,
+	0xc6, 0xc7, 0x12, 0x14, 0x42, 0x92, 0xc5, 0xad
+};
+
+/**
+ * struct hash_driver_data - data specific to the driver.
+ *
+ * @device_list:	A list of registered devices to choose from.
+ * @device_allocation:	A semaphore initialized with number of devices.
+ */
+struct hash_driver_data {
+	struct klist		device_list;
+	struct semaphore	device_allocation;
+};
+
+static struct hash_driver_data	driver_data;
+
+/* Declaration of functions */
+/**
+ * hash_messagepad - Pads a message and write the nblw bits.
+ * @device_data:	Structure for the hash device.
+ * @message:		Last word of a message
+ * @index_bytes:	The number of bytes in the last message
+ *
+ * This function manages the final part of the digest calculation, when less
+ * than 512 bits (64 bytes) remain in message. This means index_bytes < 64.
+ *
+ */
+static void hash_messagepad(struct hash_device_data *device_data,
+		const u32 *message, u8 index_bytes);
+
+/**
+ * release_hash_device - Releases a previously allocated hash device.
+ * @device_data:	Structure for the hash device.
+ *
+ */
+static void release_hash_device(struct hash_device_data *device_data)
+{
+	spin_lock(&device_data->ctx_lock);
+	device_data->current_ctx->device = NULL;
+	device_data->current_ctx = NULL;
+	spin_unlock(&device_data->ctx_lock);
+
+	/*
+	 * The down_interruptible part for this semaphore is called in
+	 * cryp_get_device_data.
+	 */
+	up(&driver_data.device_allocation);
+}
+
+static void hash_dma_setup_channel(struct hash_device_data *device_data,
+				struct device *dev)
+{
+	struct hash_platform_data *platform_data = dev->platform_data;
+	dma_cap_zero(device_data->dma.mask);
+	dma_cap_set(DMA_SLAVE, device_data->dma.mask);
+
+	device_data->dma.cfg_mem2hash = platform_data->mem_to_engine;
+	device_data->dma.chan_mem2hash =
+		dma_request_channel(device_data->dma.mask,
+				platform_data->dma_filter,
+				device_data->dma.cfg_mem2hash);
+
+	init_completion(&device_data->dma.complete);
+}
+
+static void hash_dma_callback(void *data)
+{
+	struct hash_ctx *ctx = (struct hash_ctx *) data;
+
+	complete(&ctx->device->dma.complete);
+}
+
+static int hash_set_dma_transfer(struct hash_ctx *ctx, struct scatterlist *sg,
+		int len, enum dma_data_direction direction)
+{
+	struct dma_async_tx_descriptor *desc = NULL;
+	struct dma_chan *channel = NULL;
+	dma_cookie_t cookie;
+
+	if (direction != DMA_TO_DEVICE) {
+		dev_err(ctx->device->dev, "[%s] Invalid DMA direction",
+				__func__);
+		return -EFAULT;
+	}
+
+	sg->length = ALIGN(sg->length, HASH_DMA_ALIGN_SIZE);
+
+	channel = ctx->device->dma.chan_mem2hash;
+	ctx->device->dma.sg = sg;
+	ctx->device->dma.sg_len = dma_map_sg(channel->device->dev,
+			ctx->device->dma.sg, ctx->device->dma.nents,
+			direction);
+
+	if (!ctx->device->dma.sg_len) {
+		dev_err(ctx->device->dev,
+				"[%s]: Could not map the sg list (TO_DEVICE)",
+				__func__);
+		return -EFAULT;
+	}
+
+	dev_dbg(ctx->device->dev, "[%s]: Setting up DMA for buffer "
+			"(TO_DEVICE)", __func__);
+	desc = channel->device->device_prep_slave_sg(channel,
+			ctx->device->dma.sg, ctx->device->dma.sg_len,
+			direction, DMA_CTRL_ACK | DMA_PREP_INTERRUPT, NULL);
+	if (!desc) {
+		dev_err(ctx->device->dev,
+			"[%s]: device_prep_slave_sg() failed!", __func__);
+		return -EFAULT;
+	}
+
+	desc->callback = hash_dma_callback;
+	desc->callback_param = ctx;
+
+	cookie = desc->tx_submit(desc);
+	dma_async_issue_pending(channel);
+
+	return 0;
+}
+
+static void hash_dma_done(struct hash_ctx *ctx)
+{
+	struct dma_chan *chan;
+
+	chan = ctx->device->dma.chan_mem2hash;
+	chan->device->device_control(chan, DMA_TERMINATE_ALL, 0);
+	dma_unmap_sg(chan->device->dev, ctx->device->dma.sg,
+			ctx->device->dma.sg_len, DMA_TO_DEVICE);
+
+}
+
+static int hash_dma_write(struct hash_ctx *ctx,
+		struct scatterlist *sg, int len)
+{
+	int error = hash_set_dma_transfer(ctx, sg, len, DMA_TO_DEVICE);
+	if (error) {
+		dev_dbg(ctx->device->dev, "[%s]: hash_set_dma_transfer() "
+			"failed", __func__);
+		return error;
+	}
+
+	return len;
+}
+
+/**
+ * get_empty_message_digest - Returns a pre-calculated digest for
+ * the empty message.
+ * @device_data:	Structure for the hash device.
+ * @zero_hash:		Buffer to return the empty message digest.
+ * @zero_hash_size:	Hash size of the empty message digest.
+ * @zero_digest:	True if zero_digest returned.
+ */
+static int get_empty_message_digest(
+		struct hash_device_data *device_data,
+		u8 *zero_hash, u32 *zero_hash_size, bool *zero_digest)
+{
+	int ret = 0;
+	struct hash_ctx *ctx = device_data->current_ctx;
+	*zero_digest = false;
+
+	/**
+	 * Caller responsible for ctx != NULL.
+	 */
+
+	if (HASH_OPER_MODE_HASH == ctx->config.oper_mode) {
+		if (HASH_ALGO_SHA1 == ctx->config.algorithm) {
+			memcpy(zero_hash, &zero_message_hash_sha1[0],
+					SHA1_DIGEST_SIZE);
+			*zero_hash_size = SHA1_DIGEST_SIZE;
+			*zero_digest = true;
+		} else if (HASH_ALGO_SHA256 ==
+				ctx->config.algorithm) {
+			memcpy(zero_hash, &zero_message_hash_sha256[0],
+					SHA256_DIGEST_SIZE);
+			*zero_hash_size = SHA256_DIGEST_SIZE;
+			*zero_digest = true;
+		} else {
+			dev_err(device_data->dev, "[%s] "
+					"Incorrect algorithm!"
+					, __func__);
+			ret = -EINVAL;
+			goto out;
+		}
+	} else if (HASH_OPER_MODE_HMAC == ctx->config.oper_mode) {
+		if (!ctx->keylen) {
+			if (HASH_ALGO_SHA1 == ctx->config.algorithm) {
+				memcpy(zero_hash, &zero_message_hmac_sha1[0],
+						SHA1_DIGEST_SIZE);
+				*zero_hash_size = SHA1_DIGEST_SIZE;
+				*zero_digest = true;
+			} else if (HASH_ALGO_SHA256 == ctx->config.algorithm) {
+				memcpy(zero_hash, &zero_message_hmac_sha256[0],
+						SHA256_DIGEST_SIZE);
+				*zero_hash_size = SHA256_DIGEST_SIZE;
+				*zero_digest = true;
+			} else {
+				dev_err(device_data->dev, "[%s] "
+						"Incorrect algorithm!"
+						, __func__);
+				ret = -EINVAL;
+				goto out;
+			}
+		} else {
+			dev_dbg(device_data->dev, "[%s] Continue hash "
+					"calculation, since hmac key avalable",
+					__func__);
+		}
+	}
+out:
+
+	return ret;
+}
+
+/**
+ * hash_disable_power - Request to disable power and clock.
+ * @device_data:	Structure for the hash device.
+ * @save_device_state:	If true, saves the current hw state.
+ *
+ * This function request for disabling power (regulator) and clock,
+ * and could also save current hw state.
+ */
+static int hash_disable_power(
+		struct hash_device_data *device_data,
+		bool			save_device_state)
+{
+	int ret = 0;
+	struct device *dev = device_data->dev;
+
+	spin_lock(&device_data->power_state_lock);
+	if (!device_data->power_state)
+		goto out;
+
+	if (save_device_state) {
+		hash_save_state(device_data,
+				&device_data->state);
+		device_data->restore_dev_state = true;
+	}
+
+	clk_disable(device_data->clk);
+	ret = regulator_disable(device_data->regulator);
+	if (ret)
+		dev_err(dev, "[%s] regulator_disable() failed!", __func__);
+
+	device_data->power_state = false;
+
+out:
+	spin_unlock(&device_data->power_state_lock);
+
+	return ret;
+}
+
+/**
+ * hash_enable_power - Request to enable power and clock.
+ * @device_data:		Structure for the hash device.
+ * @restore_device_state:	If true, restores a previous saved hw state.
+ *
+ * This function request for enabling power (regulator) and clock,
+ * and could also restore a previously saved hw state.
+ */
+static int hash_enable_power(
+		struct hash_device_data *device_data,
+		bool			restore_device_state)
+{
+	int ret = 0;
+	struct device *dev = device_data->dev;
+
+	spin_lock(&device_data->power_state_lock);
+	if (!device_data->power_state) {
+		ret = regulator_enable(device_data->regulator);
+		if (ret) {
+			dev_err(dev, "[%s]: regulator_enable() failed!",
+					__func__);
+			goto out;
+		}
+		ret = clk_enable(device_data->clk);
+		if (ret) {
+			dev_err(dev, "[%s]: clk_enable() failed!",
+					__func__);
+			ret = regulator_disable(
+					device_data->regulator);
+			goto out;
+		}
+		device_data->power_state = true;
+	}
+
+	if (device_data->restore_dev_state) {
+		if (restore_device_state) {
+			device_data->restore_dev_state = false;
+			hash_resume_state(device_data,
+				&device_data->state);
+		}
+	}
+out:
+	spin_unlock(&device_data->power_state_lock);
+
+	return ret;
+}
+
+/**
+ * hash_get_device_data - Checks for an available hash device and return it.
+ * @hash_ctx:		Structure for the hash context.
+ * @device_data:	Structure for the hash device.
+ *
+ * This function check for an available hash device and return it to
+ * the caller.
+ * Note! Caller need to release the device, calling up().
+ */
+static int hash_get_device_data(struct hash_ctx *ctx,
+				struct hash_device_data **device_data)
+{
+	int			ret;
+	struct klist_iter	device_iterator;
+	struct klist_node	*device_node;
+	struct hash_device_data *local_device_data = NULL;
+
+	/* Wait until a device is available */
+	ret = down_interruptible(&driver_data.device_allocation);
+	if (ret)
+		return ret;  /* Interrupted */
+
+	/* Select a device */
+	klist_iter_init(&driver_data.device_list, &device_iterator);
+	device_node = klist_next(&device_iterator);
+	while (device_node) {
+		local_device_data = container_of(device_node,
+					   struct hash_device_data, list_node);
+		spin_lock(&local_device_data->ctx_lock);
+		/* current_ctx allocates a device, NULL = unallocated */
+		if (local_device_data->current_ctx) {
+			device_node = klist_next(&device_iterator);
+		} else {
+			local_device_data->current_ctx = ctx;
+			ctx->device = local_device_data;
+			spin_unlock(&local_device_data->ctx_lock);
+			break;
+		}
+		spin_unlock(&local_device_data->ctx_lock);
+	}
+	klist_iter_exit(&device_iterator);
+
+	if (!device_node) {
+		/**
+		 * No free device found.
+		 * Since we allocated a device with down_interruptible, this
+		 * should not be able to happen.
+		 * Number of available devices, which are contained in
+		 * device_allocation, is therefore decremented by not doing
+		 * an up(device_allocation).
+		 */
+		return -EBUSY;
+	}
+
+	*device_data = local_device_data;
+
+	return 0;
+}
+
+/**
+ * hash_hw_write_key - Writes the key to the hardware registries.
+ *
+ * @device_data:	Structure for the hash device.
+ * @key:		Key to be written.
+ * @keylen:		The lengt of the key.
+ *
+ * Note! This function DOES NOT write to the NBLW registry, even though
+ * specified in the the hw design spec. Either due to incorrect info in the
+ * spec or due to a bug in the hw.
+ */
+static void hash_hw_write_key(struct hash_device_data *device_data,
+		const u8 *key, unsigned int keylen)
+{
+	u32 word = 0;
+	int nwords = 1;
+
+	HASH_CLEAR_BITS(&device_data->base->str, HASH_STR_NBLW_MASK);
+
+	while (keylen >= 4) {
+		u32 *key_word = (u32 *)key;
+
+		HASH_SET_DIN(key_word, nwords);
+		keylen -= 4;
+		key += 4;
+	}
+
+	/* Take care of the remaining bytes in the last word */
+	if (keylen) {
+		word = 0;
+		while (keylen) {
+			word |= (key[keylen - 1] << (8 * (keylen - 1)));
+			keylen--;
+		}
+
+		HASH_SET_DIN(&word, nwords);
+	}
+
+	while (device_data->base->str & HASH_STR_DCAL_MASK)
+		cpu_relax();
+
+	HASH_SET_DCAL;
+
+	while (device_data->base->str & HASH_STR_DCAL_MASK)
+		cpu_relax();
+}
+
+/**
+ * init_hash_hw - Initialise the hash hardware for a new calculation.
+ * @device_data:	Structure for the hash device.
+ * @ctx:		The hash context.
+ *
+ * This function will enable the bits needed to clear and start a new
+ * calculation.
+ */
+static int init_hash_hw(struct hash_device_data *device_data,
+		struct hash_ctx *ctx)
+{
+	int ret = 0;
+
+	ret = hash_setconfiguration(device_data, &ctx->config);
+	if (ret) {
+		dev_err(device_data->dev, "[%s] hash_setconfiguration() "
+				"failed!", __func__);
+		return ret;
+	}
+
+	hash_begin(device_data, ctx);
+
+	if (ctx->config.oper_mode == HASH_OPER_MODE_HMAC)
+		hash_hw_write_key(device_data, ctx->key, ctx->keylen);
+
+	return ret;
+}
+
+/**
+ * hash_get_nents - Return number of entries (nents) in scatterlist (sg).
+ *
+ * @sg:		Scatterlist.
+ * @size:	Size in bytes.
+ * @aligned:	True if sg data aligned to work in DMA mode.
+ *
+ */
+static int hash_get_nents(struct scatterlist *sg, int size, bool *aligned)
+{
+	int nents = 0;
+	bool aligned_data = true;
+
+	while (size > 0 && sg) {
+		nents++;
+		size -= sg->length;
+
+		/* hash_set_dma_transfer will align last nent */
+		if ((aligned && !IS_ALIGNED(sg->offset, HASH_DMA_ALIGN_SIZE))
+			|| (!IS_ALIGNED(sg->length, HASH_DMA_ALIGN_SIZE) &&
+				size > 0))
+			aligned_data = false;
+
+		sg = sg_next(sg);
+	}
+
+	if (aligned)
+		*aligned = aligned_data;
+
+	if (size != 0)
+		return -EFAULT;
+
+	return nents;
+}
+
+/**
+ * hash_dma_valid_data - checks for dma valid sg data.
+ * @sg:		Scatterlist.
+ * @datasize:	Datasize in bytes.
+ *
+ * NOTE! This function checks for dma valid sg data, since dma
+ * only accept datasizes of even wordsize.
+ */
+static bool hash_dma_valid_data(struct scatterlist *sg, int datasize)
+{
+	bool aligned;
+
+	/* Need to include at least one nent, else error */
+	if (hash_get_nents(sg, datasize, &aligned) < 1)
+		return false;
+
+	return aligned;
+}
+
+/**
+ * hash_init - Common hash init function for SHA1/SHA2 (SHA256).
+ * @req: The hash request for the job.
+ *
+ * Initialize structures.
+ */
+static int hash_init(struct ahash_request *req)
+{
+	struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+	struct hash_ctx *ctx = crypto_ahash_ctx(tfm);
+	struct hash_req_ctx *req_ctx = ahash_request_ctx(req);
+
+	if (!ctx->key)
+		ctx->keylen = 0;
+
+	memset(&req_ctx->state, 0, sizeof(struct hash_state));
+	req_ctx->updated = 0;
+	if (hash_mode == HASH_MODE_DMA) {
+		if (req->nbytes < HASH_DMA_ALIGN_SIZE) {
+			req_ctx->dma_mode = false; /* Don't use DMA */
+
+			pr_debug(DEV_DBG_NAME " [%s] DMA mode, but direct "
+					"to CPU mode for data size < %d",
+					__func__, HASH_DMA_ALIGN_SIZE);
+		} else {
+			if (req->nbytes >= HASH_DMA_PERFORMANCE_MIN_SIZE &&
+					hash_dma_valid_data(req->src,
+						req->nbytes)) {
+				req_ctx->dma_mode = true;
+			} else {
+				req_ctx->dma_mode = false;
+				pr_debug(DEV_DBG_NAME " [%s] DMA mode, but use"
+						" CPU mode for datalength < %d"
+						" or non-aligned data, except "
+						"in last nent", __func__,
+						HASH_DMA_PERFORMANCE_MIN_SIZE);
+			}
+		}
+	}
+	return 0;
+}
+
+/**
+ * hash_processblock - This function processes a single block of 512 bits (64
+ *                     bytes), word aligned, starting at message.
+ * @device_data:	Structure for the hash device.
+ * @message:		Block (512 bits) of message to be written to
+ *			the HASH hardware.
+ *
+ */
+static void hash_processblock(
+		struct hash_device_data *device_data,
+		const u32 *message, int length)
+{
+	int len = length / HASH_BYTES_PER_WORD;
+	/*
+	 * NBLW bits. Reset the number of bits in last word (NBLW).
+	 */
+	HASH_CLEAR_BITS(&device_data->base->str, HASH_STR_NBLW_MASK);
+
+	/*
+	 * Write message data to the HASH_DIN register.
+	 */
+	HASH_SET_DIN(message, len);
+}
+
+/**
+ * hash_messagepad - Pads a message and write the nblw bits.
+ * @device_data:	Structure for the hash device.
+ * @message:		Last word of a message.
+ * @index_bytes:	The number of bytes in the last message.
+ *
+ * This function manages the final part of the digest calculation, when less
+ * than 512 bits (64 bytes) remain in message. This means index_bytes < 64.
+ *
+ */
+static void hash_messagepad(struct hash_device_data *device_data,
+		const u32 *message, u8 index_bytes)
+{
+	int nwords = 1;
+
+	/*
+	 * Clear hash str register, only clear NBLW
+	 * since DCAL will be reset by hardware.
+	 */
+	HASH_CLEAR_BITS(&device_data->base->str, HASH_STR_NBLW_MASK);
+
+	/* Main loop */
+	while (index_bytes >= 4) {
+		HASH_SET_DIN(message, nwords);
+		index_bytes -= 4;
+		message++;
+	}
+
+	if (index_bytes)
+		HASH_SET_DIN(message, nwords);
+
+	while (device_data->base->str & HASH_STR_DCAL_MASK)
+		cpu_relax();
+
+	/* num_of_bytes == 0 => NBLW <- 0 (32 bits valid in DATAIN) */
+	HASH_SET_NBLW(index_bytes * 8);
+	dev_dbg(device_data->dev, "[%s] DIN=0x%08x NBLW=%d", __func__,
+			readl_relaxed(&device_data->base->din),
+			(int)(readl_relaxed(&device_data->base->str) &
+				HASH_STR_NBLW_MASK));
+	HASH_SET_DCAL;
+	dev_dbg(device_data->dev, "[%s] after dcal -> DIN=0x%08x NBLW=%d",
+			__func__, readl_relaxed(&device_data->base->din),
+			(int)(readl_relaxed(&device_data->base->str) &
+				HASH_STR_NBLW_MASK));
+
+	while (device_data->base->str & HASH_STR_DCAL_MASK)
+		cpu_relax();
+}
+
+/**
+ * hash_incrementlength - Increments the length of the current message.
+ * @ctx: Hash context
+ * @incr: Length of message processed already
+ *
+ * Overflow cannot occur, because conditions for overflow are checked in
+ * hash_hw_update.
+ */
+static void hash_incrementlength(struct hash_req_ctx *ctx, u32 incr)
+{
+	ctx->state.length.low_word += incr;
+
+	/* Check for wrap-around */
+	if (ctx->state.length.low_word < incr)
+		ctx->state.length.high_word++;
+}
+
+/**
+ * hash_setconfiguration - Sets the required configuration for the hash
+ *                         hardware.
+ * @device_data:	Structure for the hash device.
+ * @config:		Pointer to a configuration structure.
+ */
+int hash_setconfiguration(struct hash_device_data *device_data,
+		struct hash_config *config)
+{
+	int ret = 0;
+
+	if (config->algorithm != HASH_ALGO_SHA1 &&
+	    config->algorithm != HASH_ALGO_SHA256)
+		return -EPERM;
+
+	/*
+	 * DATAFORM bits. Set the DATAFORM bits to 0b11, which means the data
+	 * to be written to HASH_DIN is considered as 32 bits.
+	 */
+	HASH_SET_DATA_FORMAT(config->data_format);
+
+	/*
+	 * ALGO bit. Set to 0b1 for SHA-1 and 0b0 for SHA-256
+	 */
+	switch (config->algorithm) {
+	case HASH_ALGO_SHA1:
+		HASH_SET_BITS(&device_data->base->cr, HASH_CR_ALGO_MASK);
+		break;
+
+	case HASH_ALGO_SHA256:
+		HASH_CLEAR_BITS(&device_data->base->cr, HASH_CR_ALGO_MASK);
+		break;
+
+	default:
+		dev_err(device_data->dev, "[%s] Incorrect algorithm.",
+				__func__);
+		return -EPERM;
+	}
+
+	/*
+	 * MODE bit. This bit selects between HASH or HMAC mode for the
+	 * selected algorithm. 0b0 = HASH and 0b1 = HMAC.
+	 */
+	if (HASH_OPER_MODE_HASH == config->oper_mode)
+		HASH_CLEAR_BITS(&device_data->base->cr,
+				HASH_CR_MODE_MASK);
+	else if (HASH_OPER_MODE_HMAC == config->oper_mode) {
+		HASH_SET_BITS(&device_data->base->cr,
+				HASH_CR_MODE_MASK);
+		if (device_data->current_ctx->keylen > HASH_BLOCK_SIZE) {
+			/* Truncate key to blocksize */
+			dev_dbg(device_data->dev, "[%s] LKEY set", __func__);
+			HASH_SET_BITS(&device_data->base->cr,
+					HASH_CR_LKEY_MASK);
+		} else {
+			dev_dbg(device_data->dev, "[%s] LKEY cleared",
+					__func__);
+			HASH_CLEAR_BITS(&device_data->base->cr,
+					HASH_CR_LKEY_MASK);
+		}
+	} else {	/* Wrong hash mode */
+		ret = -EPERM;
+		dev_err(device_data->dev, "[%s] HASH_INVALID_PARAMETER!",
+				__func__);
+	}
+	return ret;
+}
+
+/**
+ * hash_begin - This routine resets some globals and initializes the hash
+ *              hardware.
+ * @device_data:	Structure for the hash device.
+ * @ctx:		Hash context.
+ */
+void hash_begin(struct hash_device_data *device_data, struct hash_ctx *ctx)
+{
+	/* HW and SW initializations */
+	/* Note: there is no need to initialize buffer and digest members */
+
+	while (device_data->base->str & HASH_STR_DCAL_MASK)
+		cpu_relax();
+
+	/*
+	 * INIT bit. Set this bit to 0b1 to reset the HASH processor core and
+	 * prepare the initialize the HASH accelerator to compute the message
+	 * digest of a new message.
+	 */
+	HASH_INITIALIZE;
+
+	/*
+	 * NBLW bits. Reset the number of bits in last word (NBLW).
+	 */
+	HASH_CLEAR_BITS(&device_data->base->str, HASH_STR_NBLW_MASK);
+}
+
+int hash_process_data(
+		struct hash_device_data *device_data,
+		struct hash_ctx *ctx, struct hash_req_ctx *req_ctx,
+		int msg_length, u8 *data_buffer, u8 *buffer, u8 *index)
+{
+	int ret = 0;
+	u32 count;
+
+	do {
+		if ((*index + msg_length) < HASH_BLOCK_SIZE) {
+			for (count = 0; count < msg_length; count++) {
+				buffer[*index + count] =
+					*(data_buffer + count);
+			}
+			*index += msg_length;
+			msg_length = 0;
+		} else {
+			if (req_ctx->updated) {
+
+				ret = hash_resume_state(device_data,
+						&device_data->state);
+				memmove(req_ctx->state.buffer,
+						device_data->state.buffer,
+						HASH_BLOCK_SIZE / sizeof(u32));
+				if (ret) {
+					dev_err(device_data->dev, "[%s] "
+							"hash_resume_state()"
+							" failed!", __func__);
+					goto out;
+				}
+			} else {
+				ret = init_hash_hw(device_data, ctx);
+				if (ret) {
+					dev_err(device_data->dev, "[%s] "
+							"init_hash_hw()"
+							" failed!", __func__);
+					goto out;
+				}
+				req_ctx->updated = 1;
+			}
+			/*
+			 * If 'data_buffer' is four byte aligned and
+			 * local buffer does not have any data, we can
+			 * write data directly from 'data_buffer' to
+			 * HW peripheral, otherwise we first copy data
+			 * to a local buffer
+			 */
+			if ((0 == (((u32)data_buffer) % 4))
+					&& (0 == *index))
+				hash_processblock(device_data,
+						(const u32 *)
+						data_buffer, HASH_BLOCK_SIZE);
+			else {
+				for (count = 0; count <
+						(u32)(HASH_BLOCK_SIZE -
+							*index);
+						count++) {
+					buffer[*index + count] =
+						*(data_buffer + count);
+				}
+				hash_processblock(device_data,
+						(const u32 *)buffer,
+						HASH_BLOCK_SIZE);
+			}
+			hash_incrementlength(req_ctx, HASH_BLOCK_SIZE);
+			data_buffer += (HASH_BLOCK_SIZE - *index);
+
+			msg_length -= (HASH_BLOCK_SIZE - *index);
+			*index = 0;
+
+			ret = hash_save_state(device_data,
+					&device_data->state);
+
+			memmove(device_data->state.buffer,
+					req_ctx->state.buffer,
+					HASH_BLOCK_SIZE / sizeof(u32));
+			if (ret) {
+				dev_err(device_data->dev, "[%s] "
+						"hash_save_state()"
+						" failed!", __func__);
+				goto out;
+			}
+		}
+	} while (msg_length != 0);
+out:
+
+	return ret;
+}
+
+/**
+ * hash_dma_final - The hash dma final function for SHA1/SHA256.
+ * @req:	The hash request for the job.
+ */
+static int hash_dma_final(struct ahash_request *req)
+{
+	int ret = 0;
+	struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+	struct hash_ctx *ctx = crypto_ahash_ctx(tfm);
+	struct hash_req_ctx *req_ctx = ahash_request_ctx(req);
+	struct hash_device_data *device_data;
+	u8 digest[SHA256_DIGEST_SIZE];
+	int bytes_written = 0;
+
+	ret = hash_get_device_data(ctx, &device_data);
+	if (ret)
+		return ret;
+
+	dev_dbg(device_data->dev, "[%s] (ctx=0x%x)!", __func__, (u32) ctx);
+
+	if (req_ctx->updated) {
+		ret = hash_resume_state(device_data, &device_data->state);
+
+		if (ret) {
+			dev_err(device_data->dev, "[%s] hash_resume_state() "
+					"failed!", __func__);
+			goto out;
+		}
+
+	}
+
+	if (!req_ctx->updated) {
+		ret = hash_setconfiguration(device_data, &ctx->config);
+		if (ret) {
+			dev_err(device_data->dev, "[%s] "
+					"hash_setconfiguration() failed!",
+					__func__);
+			goto out;
+		}
+
+		/* Enable DMA input */
+		if (hash_mode != HASH_MODE_DMA || !req_ctx->dma_mode) {
+			HASH_CLEAR_BITS(&device_data->base->cr,
+					HASH_CR_DMAE_MASK);
+		} else {
+			HASH_SET_BITS(&device_data->base->cr,
+					HASH_CR_DMAE_MASK);
+			HASH_SET_BITS(&device_data->base->cr,
+					HASH_CR_PRIVN_MASK);
+		}
+
+		HASH_INITIALIZE;
+
+		if (ctx->config.oper_mode == HASH_OPER_MODE_HMAC)
+			hash_hw_write_key(device_data, ctx->key, ctx->keylen);
+
+		/* Number of bits in last word = (nbytes * 8) % 32 */
+		HASH_SET_NBLW((req->nbytes * 8) % 32);
+		req_ctx->updated = 1;
+	}
+
+	/* Store the nents in the dma struct. */
+	ctx->device->dma.nents = hash_get_nents(req->src, req->nbytes, NULL);
+	if (!ctx->device->dma.nents) {
+		dev_err(device_data->dev, "[%s] "
+				"ctx->device->dma.nents = 0", __func__);
+		goto out;
+	}
+
+	bytes_written = hash_dma_write(ctx, req->src, req->nbytes);
+	if (bytes_written != req->nbytes) {
+		dev_err(device_data->dev, "[%s] "
+				"hash_dma_write() failed!", __func__);
+		goto out;
+	}
+
+	wait_for_completion(&ctx->device->dma.complete);
+	hash_dma_done(ctx);
+
+	while (device_data->base->str & HASH_STR_DCAL_MASK)
+		cpu_relax();
+
+	if (ctx->config.oper_mode == HASH_OPER_MODE_HMAC && ctx->key) {
+		unsigned int keylen = ctx->keylen;
+		u8 *key = ctx->key;
+
+		dev_dbg(device_data->dev, "[%s] keylen: %d", __func__,
+				ctx->keylen);
+		hash_hw_write_key(device_data, key, keylen);
+	}
+
+	hash_get_digest(device_data, digest, ctx->config.algorithm);
+	memcpy(req->result, digest, ctx->digestsize);
+
+out:
+	release_hash_device(device_data);
+
+	/**
+	 * Allocated in setkey, and only used in HMAC.
+	 */
+	kfree(ctx->key);
+
+	return ret;
+}
+
+/**
+ * hash_hw_final - The final hash calculation function
+ * @req:	The hash request for the job.
+ */
+int hash_hw_final(struct ahash_request *req)
+{
+	int ret = 0;
+	struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+	struct hash_ctx *ctx = crypto_ahash_ctx(tfm);
+	struct hash_req_ctx *req_ctx = ahash_request_ctx(req);
+	struct hash_device_data *device_data;
+	u8 digest[SHA256_DIGEST_SIZE];
+
+	ret = hash_get_device_data(ctx, &device_data);
+	if (ret)
+		return ret;
+
+	dev_dbg(device_data->dev, "[%s] (ctx=0x%x)!", __func__, (u32) ctx);
+
+	if (req_ctx->updated) {
+		ret = hash_resume_state(device_data, &device_data->state);
+
+		if (ret) {
+			dev_err(device_data->dev, "[%s] hash_resume_state() "
+					"failed!", __func__);
+			goto out;
+		}
+	} else if (req->nbytes == 0 && ctx->keylen == 0) {
+		u8 zero_hash[SHA256_DIGEST_SIZE];
+		u32 zero_hash_size = 0;
+		bool zero_digest = false;
+		/**
+		 * Use a pre-calculated empty message digest
+		 * (workaround since hw return zeroes, hw bug!?)
+		 */
+		ret = get_empty_message_digest(device_data, &zero_hash[0],
+				&zero_hash_size, &zero_digest);
+		if (!ret && likely(zero_hash_size == ctx->digestsize) &&
+				zero_digest) {
+			memcpy(req->result, &zero_hash[0], ctx->digestsize);
+			goto out;
+		} else if (!ret && !zero_digest) {
+			dev_dbg(device_data->dev, "[%s] HMAC zero msg with "
+					"key, continue...", __func__);
+		} else {
+			dev_err(device_data->dev, "[%s] ret=%d, or wrong "
+					"digest size? %s", __func__, ret,
+					(zero_hash_size == ctx->digestsize) ?
+					"true" : "false");
+			/* Return error */
+			goto out;
+		}
+	} else if (req->nbytes == 0 && ctx->keylen > 0) {
+		dev_err(device_data->dev, "[%s] Empty message with "
+				"keylength > 0, NOT supported.", __func__);
+		goto out;
+	}
+
+	if (!req_ctx->updated) {
+		ret = init_hash_hw(device_data, ctx);
+		if (ret) {
+			dev_err(device_data->dev, "[%s] init_hash_hw() "
+					"failed!", __func__);
+			goto out;
+		}
+	}
+
+	if (req_ctx->state.index) {
+		hash_messagepad(device_data, req_ctx->state.buffer,
+				req_ctx->state.index);
+	} else {
+		HASH_SET_DCAL;
+		while (device_data->base->str & HASH_STR_DCAL_MASK)
+			cpu_relax();
+	}
+
+	if (ctx->config.oper_mode == HASH_OPER_MODE_HMAC && ctx->key) {
+		unsigned int keylen = ctx->keylen;
+		u8 *key = ctx->key;
+
+		dev_dbg(device_data->dev, "[%s] keylen: %d", __func__,
+				ctx->keylen);
+		hash_hw_write_key(device_data, key, keylen);
+	}
+
+	hash_get_digest(device_data, digest, ctx->config.algorithm);
+	memcpy(req->result, digest, ctx->digestsize);
+
+out:
+	release_hash_device(device_data);
+
+	/**
+	 * Allocated in setkey, and only used in HMAC.
+	 */
+	kfree(ctx->key);
+
+	return ret;
+}
+
+/**
+ * hash_hw_update - Updates current HASH computation hashing another part of
+ *                  the message.
+ * @req:	Byte array containing the message to be hashed (caller
+ *		allocated).
+ */
+int hash_hw_update(struct ahash_request *req)
+{
+	int ret = 0;
+	u8 index = 0;
+	u8 *buffer;
+	struct hash_device_data *device_data;
+	u8 *data_buffer;
+	struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+	struct hash_ctx *ctx = crypto_ahash_ctx(tfm);
+	struct hash_req_ctx *req_ctx = ahash_request_ctx(req);
+	struct crypto_hash_walk walk;
+	int msg_length = crypto_hash_walk_first(req, &walk);
+
+	/* Empty message ("") is correct indata */
+	if (msg_length == 0)
+		return ret;
+
+	index = req_ctx->state.index;
+	buffer = (u8 *)req_ctx->state.buffer;
+
+	/* Check if ctx->state.length + msg_length
+	   overflows */
+	if (msg_length > (req_ctx->state.length.low_word + msg_length) &&
+			HASH_HIGH_WORD_MAX_VAL ==
+			req_ctx->state.length.high_word) {
+		pr_err(DEV_DBG_NAME " [%s] HASH_MSG_LENGTH_OVERFLOW!",
+				__func__);
+		return -EPERM;
+	}
+
+	ret = hash_get_device_data(ctx, &device_data);
+	if (ret)
+		return ret;
+
+	/* Main loop */
+	while (0 != msg_length) {
+		data_buffer = walk.data;
+		ret = hash_process_data(device_data, ctx, req_ctx, msg_length,
+				data_buffer, buffer, &index);
+
+		if (ret) {
+			dev_err(device_data->dev, "[%s] hash_internal_hw_"
+					"update() failed!", __func__);
+			goto out;
+		}
+
+		msg_length = crypto_hash_walk_done(&walk, 0);
+	}
+
+	req_ctx->state.index = index;
+	dev_dbg(device_data->dev, "[%s] indata length=%d, bin=%d))",
+			__func__, req_ctx->state.index,
+			req_ctx->state.bit_index);
+
+out:
+	release_hash_device(device_data);
+
+	return ret;
+}
+
+/**
+ * hash_resume_state - Function that resumes the state of an calculation.
+ * @device_data:	Pointer to the device structure.
+ * @device_state:	The state to be restored in the hash hardware
+ */
+int hash_resume_state(struct hash_device_data *device_data,
+		const struct hash_state *device_state)
+{
+	u32 temp_cr;
+	s32 count;
+	int hash_mode = HASH_OPER_MODE_HASH;
+
+	if (NULL == device_state) {
+		dev_err(device_data->dev, "[%s] HASH_INVALID_PARAMETER!",
+				__func__);
+		return -EPERM;
+	}
+
+	/* Check correctness of index and length members */
+	if (device_state->index > HASH_BLOCK_SIZE
+	    || (device_state->length.low_word % HASH_BLOCK_SIZE) != 0) {
+		dev_err(device_data->dev, "[%s] HASH_INVALID_PARAMETER!",
+				__func__);
+		return -EPERM;
+	}
+
+	/*
+	 * INIT bit. Set this bit to 0b1 to reset the HASH processor core and
+	 * prepare the initialize the HASH accelerator to compute the message
+	 * digest of a new message.
+	 */
+	HASH_INITIALIZE;
+
+	temp_cr = device_state->temp_cr;
+	writel_relaxed(temp_cr & HASH_CR_RESUME_MASK, &device_data->base->cr);
+
+	if (device_data->base->cr & HASH_CR_MODE_MASK)
+		hash_mode = HASH_OPER_MODE_HMAC;
+	else
+		hash_mode = HASH_OPER_MODE_HASH;
+
+	for (count = 0; count < HASH_CSR_COUNT; count++) {
+		if ((count >= 36) && (hash_mode == HASH_OPER_MODE_HASH))
+			break;
+
+		writel_relaxed(device_state->csr[count],
+				&device_data->base->csrx[count]);
+	}
+
+	writel_relaxed(device_state->csfull, &device_data->base->csfull);
+	writel_relaxed(device_state->csdatain, &device_data->base->csdatain);
+
+	writel_relaxed(device_state->str_reg, &device_data->base->str);
+	writel_relaxed(temp_cr, &device_data->base->cr);
+
+	return 0;
+}
+
+/**
+ * hash_save_state - Function that saves the state of hardware.
+ * @device_data:	Pointer to the device structure.
+ * @device_state:	The strucure where the hardware state should be saved.
+ */
+int hash_save_state(struct hash_device_data *device_data,
+		struct hash_state *device_state)
+{
+	u32 temp_cr;
+	u32 count;
+	int hash_mode = HASH_OPER_MODE_HASH;
+
+	if (NULL == device_state) {
+		dev_err(device_data->dev, "[%s] HASH_INVALID_PARAMETER!",
+				__func__);
+		return -ENOTSUPP;
+	}
+
+	/* Write dummy value to force digest intermediate calculation. This
+	 * actually makes sure that there isn't any ongoing calculation in the
+	 * hardware.
+	 */
+	while (device_data->base->str & HASH_STR_DCAL_MASK)
+		cpu_relax();
+
+	temp_cr = readl_relaxed(&device_data->base->cr);
+
+	device_state->str_reg = readl_relaxed(&device_data->base->str);
+
+	device_state->din_reg = readl_relaxed(&device_data->base->din);
+
+	if (device_data->base->cr & HASH_CR_MODE_MASK)
+		hash_mode = HASH_OPER_MODE_HMAC;
+	else
+		hash_mode = HASH_OPER_MODE_HASH;
+
+	for (count = 0; count < HASH_CSR_COUNT; count++) {
+		if ((count >= 36) && (hash_mode == HASH_OPER_MODE_HASH))
+			break;
+
+		device_state->csr[count] =
+			readl_relaxed(&device_data->base->csrx[count]);
+	}
+
+	device_state->csfull = readl_relaxed(&device_data->base->csfull);
+	device_state->csdatain = readl_relaxed(&device_data->base->csdatain);
+
+	device_state->temp_cr = temp_cr;
+
+	return 0;
+}
+
+/**
+ * hash_check_hw - This routine checks for peripheral Ids and PCell Ids.
+ * @device_data:
+ *
+ */
+int hash_check_hw(struct hash_device_data *device_data)
+{
+	/* Checking Peripheral Ids  */
+	if (HASH_P_ID0 == readl_relaxed(&device_data->base->periphid0)
+		&& HASH_P_ID1 == readl_relaxed(&device_data->base->periphid1)
+		&& HASH_P_ID2 == readl_relaxed(&device_data->base->periphid2)
+		&& HASH_P_ID3 == readl_relaxed(&device_data->base->periphid3)
+		&& HASH_CELL_ID0 == readl_relaxed(&device_data->base->cellid0)
+		&& HASH_CELL_ID1 == readl_relaxed(&device_data->base->cellid1)
+		&& HASH_CELL_ID2 == readl_relaxed(&device_data->base->cellid2)
+		&& HASH_CELL_ID3 == readl_relaxed(&device_data->base->cellid3)
+	   ) {
+		return 0;
+	}
+
+	dev_err(device_data->dev, "[%s] HASH_UNSUPPORTED_HW!",
+			__func__);
+	return -ENOTSUPP;
+}
+
+/**
+ * hash_get_digest - Gets the digest.
+ * @device_data:	Pointer to the device structure.
+ * @digest:		User allocated byte array for the calculated digest.
+ * @algorithm:		The algorithm in use.
+ */
+void hash_get_digest(struct hash_device_data *device_data,
+		u8 *digest, int algorithm)
+{
+	u32 temp_hx_val, count;
+	int loop_ctr;
+
+	if (algorithm != HASH_ALGO_SHA1 && algorithm != HASH_ALGO_SHA256) {
+		dev_err(device_data->dev, "[%s] Incorrect algorithm %d",
+				__func__, algorithm);
+		return;
+	}
+
+	if (algorithm == HASH_ALGO_SHA1)
+		loop_ctr = SHA1_DIGEST_SIZE / sizeof(u32);
+	else
+		loop_ctr = SHA256_DIGEST_SIZE / sizeof(u32);
+
+	dev_dbg(device_data->dev, "[%s] digest array:(0x%x)",
+			__func__, (u32) digest);
+
+	/* Copy result into digest array */
+	for (count = 0; count < loop_ctr; count++) {
+		temp_hx_val = readl_relaxed(&device_data->base->hx[count]);
+		digest[count * 4] = (u8) ((temp_hx_val >> 24) & 0xFF);
+		digest[count * 4 + 1] = (u8) ((temp_hx_val >> 16) & 0xFF);
+		digest[count * 4 + 2] = (u8) ((temp_hx_val >> 8) & 0xFF);
+		digest[count * 4 + 3] = (u8) ((temp_hx_val >> 0) & 0xFF);
+	}
+}
+
+/**
+ * hash_update - The hash update function for SHA1/SHA2 (SHA256).
+ * @req: The hash request for the job.
+ */
+static int ahash_update(struct ahash_request *req)
+{
+	int ret = 0;
+	struct hash_req_ctx *req_ctx = ahash_request_ctx(req);
+
+	if (hash_mode != HASH_MODE_DMA || !req_ctx->dma_mode)
+		ret = hash_hw_update(req);
+	/* Skip update for DMA, all data will be passed to DMA in final */
+
+	if (ret) {
+		pr_err(DEV_DBG_NAME " [%s] hash_hw_update() failed!",
+				__func__);
+	}
+
+	return ret;
+}
+
+/**
+ * hash_final - The hash final function for SHA1/SHA2 (SHA256).
+ * @req:	The hash request for the job.
+ */
+static int ahash_final(struct ahash_request *req)
+{
+	int ret = 0;
+	struct hash_req_ctx *req_ctx = ahash_request_ctx(req);
+
+	pr_debug(DEV_DBG_NAME " [%s] data size: %d", __func__, req->nbytes);
+
+	if ((hash_mode == HASH_MODE_DMA) && req_ctx->dma_mode)
+		ret = hash_dma_final(req);
+	else
+		ret = hash_hw_final(req);
+
+	if (ret) {
+		pr_err(DEV_DBG_NAME " [%s] hash_hw/dma_final() failed",
+				__func__);
+	}
+
+	return ret;
+}
+
+static int hash_setkey(struct crypto_ahash *tfm,
+		const u8 *key, unsigned int keylen, int alg)
+{
+	int ret = 0;
+	struct hash_ctx *ctx = crypto_ahash_ctx(tfm);
+
+	/**
+	 * Freed in final.
+	 */
+	ctx->key = kmalloc(keylen, GFP_KERNEL);
+	if (!ctx->key) {
+		pr_err(DEV_DBG_NAME " [%s] Failed to allocate ctx->key "
+		       "for %d\n", __func__, alg);
+		return -ENOMEM;
+	}
+
+	memcpy(ctx->key, key, keylen);
+	ctx->keylen = keylen;
+
+	return ret;
+}
+
+static int ahash_sha1_init(struct ahash_request *req)
+{
+	struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+	struct hash_ctx *ctx = crypto_ahash_ctx(tfm);
+
+	ctx->config.data_format = HASH_DATA_8_BITS;
+	ctx->config.algorithm = HASH_ALGO_SHA1;
+	ctx->config.oper_mode = HASH_OPER_MODE_HASH;
+	ctx->digestsize = SHA1_DIGEST_SIZE;
+
+	return hash_init(req);
+}
+
+static int ahash_sha256_init(struct ahash_request *req)
+{
+	struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+	struct hash_ctx *ctx = crypto_ahash_ctx(tfm);
+
+	ctx->config.data_format = HASH_DATA_8_BITS;
+	ctx->config.algorithm = HASH_ALGO_SHA256;
+	ctx->config.oper_mode = HASH_OPER_MODE_HASH;
+	ctx->digestsize = SHA256_DIGEST_SIZE;
+
+	return hash_init(req);
+}
+
+static int ahash_sha1_digest(struct ahash_request *req)
+{
+	int ret2, ret1;
+
+	ret1 = ahash_sha1_init(req);
+	if (ret1)
+		goto out;
+
+	ret1 = ahash_update(req);
+	ret2 = ahash_final(req);
+
+out:
+	return ret1 ? ret1 : ret2;
+}
+
+static int ahash_sha256_digest(struct ahash_request *req)
+{
+	int ret2, ret1;
+
+	ret1 = ahash_sha256_init(req);
+	if (ret1)
+		goto out;
+
+	ret1 = ahash_update(req);
+	ret2 = ahash_final(req);
+
+out:
+	return ret1 ? ret1 : ret2;
+}
+
+static int hmac_sha1_init(struct ahash_request *req)
+{
+	struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+	struct hash_ctx *ctx = crypto_ahash_ctx(tfm);
+
+	ctx->config.data_format	= HASH_DATA_8_BITS;
+	ctx->config.algorithm	= HASH_ALGO_SHA1;
+	ctx->config.oper_mode	= HASH_OPER_MODE_HMAC;
+	ctx->digestsize		= SHA1_DIGEST_SIZE;
+
+	return hash_init(req);
+}
+
+static int hmac_sha256_init(struct ahash_request *req)
+{
+	struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+	struct hash_ctx *ctx = crypto_ahash_ctx(tfm);
+
+	ctx->config.data_format	= HASH_DATA_8_BITS;
+	ctx->config.algorithm	= HASH_ALGO_SHA256;
+	ctx->config.oper_mode	= HASH_OPER_MODE_HMAC;
+	ctx->digestsize		= SHA256_DIGEST_SIZE;
+
+	return hash_init(req);
+}
+
+static int hmac_sha1_digest(struct ahash_request *req)
+{
+	int ret2, ret1;
+
+	ret1 = hmac_sha1_init(req);
+	if (ret1)
+		goto out;
+
+	ret1 = ahash_update(req);
+	ret2 = ahash_final(req);
+
+out:
+	return ret1 ? ret1 : ret2;
+}
+
+static int hmac_sha256_digest(struct ahash_request *req)
+{
+	int ret2, ret1;
+
+	ret1 = hmac_sha256_init(req);
+	if (ret1)
+		goto out;
+
+	ret1 = ahash_update(req);
+	ret2 = ahash_final(req);
+
+out:
+	return ret1 ? ret1 : ret2;
+}
+
+static int hmac_sha1_setkey(struct crypto_ahash *tfm,
+		const u8 *key, unsigned int keylen)
+{
+	return hash_setkey(tfm, key, keylen, HASH_ALGO_SHA1);
+}
+
+static int hmac_sha256_setkey(struct crypto_ahash *tfm,
+		const u8 *key, unsigned int keylen)
+{
+	return hash_setkey(tfm, key, keylen, HASH_ALGO_SHA256);
+}
+
+struct hash_algo_template {
+	struct hash_config conf;
+	struct ahash_alg hash;
+};
+
+static int hash_cra_init(struct crypto_tfm *tfm)
+{
+	struct hash_ctx *ctx = crypto_tfm_ctx(tfm);
+	struct crypto_alg *alg = tfm->__crt_alg;
+	struct hash_algo_template *hash_alg;
+
+	hash_alg = container_of(__crypto_ahash_alg(alg),
+			struct hash_algo_template,
+			hash);
+
+	crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm),
+			sizeof(struct hash_req_ctx));
+
+	ctx->config.data_format = HASH_DATA_8_BITS;
+	ctx->config.algorithm = hash_alg->conf.algorithm;
+	ctx->config.oper_mode = hash_alg->conf.oper_mode;
+
+	ctx->digestsize = hash_alg->hash.halg.digestsize;
+
+	return 0;
+}
+
+static struct hash_algo_template hash_algs[] = {
+	{
+			.conf.algorithm	= HASH_ALGO_SHA1,
+			.conf.oper_mode	= HASH_OPER_MODE_HASH,
+			.hash = {
+				.init = hash_init,
+				.update = ahash_update,
+				.final = ahash_final,
+				.digest = ahash_sha1_digest,
+				.halg.digestsize = SHA1_DIGEST_SIZE,
+				.halg.statesize = sizeof(struct hash_ctx),
+				.halg.base = {
+					.cra_name = "sha1",
+					.cra_driver_name = "sha1-ux500",
+					.cra_flags = CRYPTO_ALG_TYPE_AHASH |
+							CRYPTO_ALG_ASYNC,
+					.cra_blocksize = SHA1_BLOCK_SIZE,
+					.cra_ctxsize = sizeof(struct hash_ctx),
+					.cra_init = hash_cra_init,
+					.cra_module = THIS_MODULE,
+			}
+		}
+	},
+	{
+			.conf.algorithm		= HASH_ALGO_SHA256,
+			.conf.oper_mode		= HASH_OPER_MODE_HASH,
+			.hash = {
+				.init = hash_init,
+				.update	= ahash_update,
+				.final = ahash_final,
+				.digest = ahash_sha256_digest,
+				.halg.digestsize = SHA256_DIGEST_SIZE,
+				.halg.statesize = sizeof(struct hash_ctx),
+				.halg.base = {
+					.cra_name = "sha256",
+					.cra_driver_name = "sha256-ux500",
+					.cra_flags = CRYPTO_ALG_TYPE_AHASH |
+							CRYPTO_ALG_ASYNC,
+					.cra_blocksize = SHA256_BLOCK_SIZE,
+					.cra_ctxsize = sizeof(struct hash_ctx),
+					.cra_type = &crypto_ahash_type,
+					.cra_init = hash_cra_init,
+					.cra_module = THIS_MODULE,
+				}
+			}
+
+	},
+	{
+			.conf.algorithm		= HASH_ALGO_SHA1,
+			.conf.oper_mode		= HASH_OPER_MODE_HMAC,
+			.hash = {
+				.init = hash_init,
+				.update = ahash_update,
+				.final = ahash_final,
+				.digest = hmac_sha1_digest,
+				.setkey = hmac_sha1_setkey,
+				.halg.digestsize = SHA1_DIGEST_SIZE,
+				.halg.statesize = sizeof(struct hash_ctx),
+				.halg.base = {
+					.cra_name = "hmac(sha1)",
+					.cra_driver_name = "hmac-sha1-ux500",
+					.cra_flags = CRYPTO_ALG_TYPE_AHASH |
+							CRYPTO_ALG_ASYNC,
+					.cra_blocksize = SHA1_BLOCK_SIZE,
+					.cra_ctxsize = sizeof(struct hash_ctx),
+					.cra_type = &crypto_ahash_type,
+					.cra_init = hash_cra_init,
+					.cra_module = THIS_MODULE,
+				}
+			}
+	},
+	{
+			.conf.algorithm		= HASH_ALGO_SHA256,
+			.conf.oper_mode		= HASH_OPER_MODE_HMAC,
+			.hash = {
+				.init = hash_init,
+				.update = ahash_update,
+				.final = ahash_final,
+				.digest = hmac_sha256_digest,
+				.setkey = hmac_sha256_setkey,
+				.halg.digestsize = SHA256_DIGEST_SIZE,
+				.halg.statesize = sizeof(struct hash_ctx),
+				.halg.base = {
+					.cra_name = "hmac(sha256)",
+					.cra_driver_name = "hmac-sha256-ux500",
+					.cra_flags = CRYPTO_ALG_TYPE_AHASH |
+							CRYPTO_ALG_ASYNC,
+					.cra_blocksize = SHA256_BLOCK_SIZE,
+					.cra_ctxsize = sizeof(struct hash_ctx),
+					.cra_type = &crypto_ahash_type,
+					.cra_init = hash_cra_init,
+					.cra_module = THIS_MODULE,
+				}
+			}
+	}
+};
+
+/**
+ * hash_algs_register_all -
+ */
+static int ahash_algs_register_all(struct hash_device_data *device_data)
+{
+	int ret;
+	int i;
+	int count;
+
+	for (i = 0; i < ARRAY_SIZE(hash_algs); i++) {
+		ret = crypto_register_ahash(&hash_algs[i].hash);
+		if (ret) {
+			count = i;
+			dev_err(device_data->dev, "[%s] alg registration failed",
+				hash_algs[i].hash.halg.base.cra_driver_name);
+			goto unreg;
+		}
+	}
+	return 0;
+unreg:
+	for (i = 0; i < count; i++)
+		crypto_unregister_ahash(&hash_algs[i].hash);
+	return ret;
+}
+
+/**
+ * hash_algs_unregister_all -
+ */
+static void ahash_algs_unregister_all(struct hash_device_data *device_data)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(hash_algs); i++)
+		crypto_unregister_ahash(&hash_algs[i].hash);
+}
+
+/**
+ * ux500_hash_probe - Function that probes the hash hardware.
+ * @pdev: The platform device.
+ */
+static int ux500_hash_probe(struct platform_device *pdev)
+{
+	int			ret = 0;
+	struct resource		*res = NULL;
+	struct hash_device_data *device_data;
+	struct device		*dev = &pdev->dev;
+
+	device_data = kzalloc(sizeof(struct hash_device_data), GFP_ATOMIC);
+	if (!device_data) {
+		dev_dbg(dev, "[%s] kzalloc() failed!", __func__);
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	device_data->dev = dev;
+	device_data->current_ctx = NULL;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_dbg(dev, "[%s] platform_get_resource() failed!", __func__);
+		ret = -ENODEV;
+		goto out_kfree;
+	}
+
+	res = request_mem_region(res->start, resource_size(res), pdev->name);
+	if (res == NULL) {
+		dev_dbg(dev, "[%s] request_mem_region() failed!", __func__);
+		ret = -EBUSY;
+		goto out_kfree;
+	}
+
+	device_data->base = ioremap(res->start, resource_size(res));
+	if (!device_data->base) {
+		dev_err(dev, "[%s] ioremap() failed!",
+				__func__);
+		ret = -ENOMEM;
+		goto out_free_mem;
+	}
+	spin_lock_init(&device_data->ctx_lock);
+	spin_lock_init(&device_data->power_state_lock);
+
+	/* Enable power for HASH1 hardware block */
+	device_data->regulator = regulator_get(dev, "v-ape");
+	if (IS_ERR(device_data->regulator)) {
+		dev_err(dev, "[%s] regulator_get() failed!", __func__);
+		ret = PTR_ERR(device_data->regulator);
+		device_data->regulator = NULL;
+		goto out_unmap;
+	}
+
+	/* Enable the clock for HASH1 hardware block */
+	device_data->clk = clk_get(dev, NULL);
+	if (IS_ERR(device_data->clk)) {
+		dev_err(dev, "[%s] clk_get() failed!", __func__);
+		ret = PTR_ERR(device_data->clk);
+		goto out_regulator;
+	}
+
+	/* Enable device power (and clock) */
+	ret = hash_enable_power(device_data, false);
+	if (ret) {
+		dev_err(dev, "[%s]: hash_enable_power() failed!", __func__);
+		goto out_clk;
+	}
+
+	ret = hash_check_hw(device_data);
+	if (ret) {
+		dev_err(dev, "[%s] hash_check_hw() failed!", __func__);
+		goto out_power;
+	}
+
+	if (hash_mode == HASH_MODE_DMA)
+		hash_dma_setup_channel(device_data, dev);
+
+	platform_set_drvdata(pdev, device_data);
+
+	/* Put the new device into the device list... */
+	klist_add_tail(&device_data->list_node, &driver_data.device_list);
+	/* ... and signal that a new device is available. */
+	up(&driver_data.device_allocation);
+
+	ret = ahash_algs_register_all(device_data);
+	if (ret) {
+		dev_err(dev, "[%s] ahash_algs_register_all() "
+				"failed!", __func__);
+		goto out_power;
+	}
+
+	dev_info(dev, "[%s] successfully probed\n", __func__);
+	return 0;
+
+out_power:
+	hash_disable_power(device_data, false);
+
+out_clk:
+	clk_put(device_data->clk);
+
+out_regulator:
+	regulator_put(device_data->regulator);
+
+out_unmap:
+	iounmap(device_data->base);
+
+out_free_mem:
+	release_mem_region(res->start, resource_size(res));
+
+out_kfree:
+	kfree(device_data);
+out:
+	return ret;
+}
+
+/**
+ * ux500_hash_remove - Function that removes the hash device from the platform.
+ * @pdev: The platform device.
+ */
+static int ux500_hash_remove(struct platform_device *pdev)
+{
+	struct resource		*res;
+	struct hash_device_data *device_data;
+	struct device		*dev = &pdev->dev;
+
+	device_data = platform_get_drvdata(pdev);
+	if (!device_data) {
+		dev_err(dev, "[%s]: platform_get_drvdata() failed!",
+			__func__);
+		return -ENOMEM;
+	}
+
+	/* Try to decrease the number of available devices. */
+	if (down_trylock(&driver_data.device_allocation))
+		return -EBUSY;
+
+	/* Check that the device is free */
+	spin_lock(&device_data->ctx_lock);
+	/* current_ctx allocates a device, NULL = unallocated */
+	if (device_data->current_ctx) {
+		/* The device is busy */
+		spin_unlock(&device_data->ctx_lock);
+		/* Return the device to the pool. */
+		up(&driver_data.device_allocation);
+		return -EBUSY;
+	}
+
+	spin_unlock(&device_data->ctx_lock);
+
+	/* Remove the device from the list */
+	if (klist_node_attached(&device_data->list_node))
+		klist_remove(&device_data->list_node);
+
+	/* If this was the last device, remove the services */
+	if (list_empty(&driver_data.device_list.k_list))
+		ahash_algs_unregister_all(device_data);
+
+	if (hash_disable_power(device_data, false))
+		dev_err(dev, "[%s]: hash_disable_power() failed",
+			__func__);
+
+	clk_put(device_data->clk);
+	regulator_put(device_data->regulator);
+
+	iounmap(device_data->base);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res)
+		release_mem_region(res->start, resource_size(res));
+
+	kfree(device_data);
+
+	return 0;
+}
+
+/**
+ * ux500_hash_shutdown - Function that shutdown the hash device.
+ * @pdev: The platform device
+ */
+static void ux500_hash_shutdown(struct platform_device *pdev)
+{
+	struct resource *res = NULL;
+	struct hash_device_data *device_data;
+
+	device_data = platform_get_drvdata(pdev);
+	if (!device_data) {
+		dev_err(&pdev->dev, "[%s] platform_get_drvdata() failed!",
+				__func__);
+		return;
+	}
+
+	/* Check that the device is free */
+	spin_lock(&device_data->ctx_lock);
+	/* current_ctx allocates a device, NULL = unallocated */
+	if (!device_data->current_ctx) {
+		if (down_trylock(&driver_data.device_allocation))
+			dev_dbg(&pdev->dev, "[%s]: Cryp still in use!"
+				"Shutting down anyway...", __func__);
+		/**
+		 * (Allocate the device)
+		 * Need to set this to non-null (dummy) value,
+		 * to avoid usage if context switching.
+		 */
+		device_data->current_ctx++;
+	}
+	spin_unlock(&device_data->ctx_lock);
+
+	/* Remove the device from the list */
+	if (klist_node_attached(&device_data->list_node))
+		klist_remove(&device_data->list_node);
+
+	/* If this was the last device, remove the services */
+	if (list_empty(&driver_data.device_list.k_list))
+		ahash_algs_unregister_all(device_data);
+
+	iounmap(device_data->base);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res)
+		release_mem_region(res->start, resource_size(res));
+
+	if (hash_disable_power(device_data, false))
+		dev_err(&pdev->dev, "[%s] hash_disable_power() failed",
+				__func__);
+}
+
+/**
+ * ux500_hash_suspend - Function that suspends the hash device.
+ * @pdev:	The platform device.
+ * @state:	-
+ */
+static int ux500_hash_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	int ret;
+	struct hash_device_data *device_data;
+	struct hash_ctx *temp_ctx = NULL;
+
+	device_data = platform_get_drvdata(pdev);
+	if (!device_data) {
+		dev_err(&pdev->dev, "[%s] platform_get_drvdata() failed!",
+				__func__);
+		return -ENOMEM;
+	}
+
+	spin_lock(&device_data->ctx_lock);
+	if (!device_data->current_ctx)
+		device_data->current_ctx++;
+	spin_unlock(&device_data->ctx_lock);
+
+	if (device_data->current_ctx == ++temp_ctx) {
+		if (down_interruptible(&driver_data.device_allocation))
+			dev_dbg(&pdev->dev, "[%s]: down_interruptible() "
+					"failed", __func__);
+		ret = hash_disable_power(device_data, false);
+
+	} else
+		ret = hash_disable_power(device_data, true);
+
+	if (ret)
+		dev_err(&pdev->dev, "[%s]: hash_disable_power()", __func__);
+
+	return ret;
+}
+
+/**
+ * ux500_hash_resume - Function that resume the hash device.
+ * @pdev:	The platform device.
+ */
+static int ux500_hash_resume(struct platform_device *pdev)
+{
+	int ret = 0;
+	struct hash_device_data *device_data;
+	struct hash_ctx *temp_ctx = NULL;
+
+	device_data = platform_get_drvdata(pdev);
+	if (!device_data) {
+		dev_err(&pdev->dev, "[%s] platform_get_drvdata() failed!",
+				__func__);
+		return -ENOMEM;
+	}
+
+	spin_lock(&device_data->ctx_lock);
+	if (device_data->current_ctx == ++temp_ctx)
+		device_data->current_ctx = NULL;
+	spin_unlock(&device_data->ctx_lock);
+
+	if (!device_data->current_ctx)
+		up(&driver_data.device_allocation);
+	else
+		ret = hash_enable_power(device_data, true);
+
+	if (ret)
+		dev_err(&pdev->dev, "[%s]: hash_enable_power() failed!",
+			__func__);
+
+	return ret;
+}
+
+static struct platform_driver hash_driver = {
+	.probe  = ux500_hash_probe,
+	.remove = ux500_hash_remove,
+	.shutdown = ux500_hash_shutdown,
+	.suspend  = ux500_hash_suspend,
+	.resume   = ux500_hash_resume,
+	.driver = {
+		.owner = THIS_MODULE,
+		.name  = "hash1",
+	}
+};
+
+/**
+ * ux500_hash_mod_init - The kernel module init function.
+ */
+static int __init ux500_hash_mod_init(void)
+{
+	klist_init(&driver_data.device_list, NULL, NULL);
+	/* Initialize the semaphore to 0 devices (locked state) */
+	sema_init(&driver_data.device_allocation, 0);
+
+	return platform_driver_register(&hash_driver);
+}
+
+/**
+ * ux500_hash_mod_fini - The kernel module exit function.
+ */
+static void __exit ux500_hash_mod_fini(void)
+{
+	platform_driver_unregister(&hash_driver);
+	return;
+}
+
+module_init(ux500_hash_mod_init);
+module_exit(ux500_hash_mod_fini);
+
+MODULE_DESCRIPTION("Driver for ST-Ericsson UX500 HASH engine.");
+MODULE_LICENSE("GPL");
+
+MODULE_ALIAS("sha1-all");
+MODULE_ALIAS("sha256-all");
+MODULE_ALIAS("hmac-sha1-all");
+MODULE_ALIAS("hmac-sha256-all");
diff --git a/drivers/devfreq/governor_performance.c b/drivers/devfreq/governor_performance.c
index 574a06b..af75ddd 100644
--- a/drivers/devfreq/governor_performance.c
+++ b/drivers/devfreq/governor_performance.c
@@ -10,6 +10,7 @@
  */
 
 #include <linux/devfreq.h>
+#include "governor.h"
 
 static int devfreq_performance_func(struct devfreq *df,
 				    unsigned long *freq)
@@ -25,8 +26,14 @@
 	return 0;
 }
 
+static int performance_init(struct devfreq *devfreq)
+{
+	return update_devfreq(devfreq);
+}
+
 const struct devfreq_governor devfreq_performance = {
 	.name = "performance",
+	.init = performance_init,
 	.get_target_freq = devfreq_performance_func,
 	.no_central_polling = true,
 };
diff --git a/drivers/devfreq/governor_powersave.c b/drivers/devfreq/governor_powersave.c
index d742d4a..fec0cdb 100644
--- a/drivers/devfreq/governor_powersave.c
+++ b/drivers/devfreq/governor_powersave.c
@@ -10,6 +10,7 @@
  */
 
 #include <linux/devfreq.h>
+#include "governor.h"
 
 static int devfreq_powersave_func(struct devfreq *df,
 				  unsigned long *freq)
@@ -22,8 +23,14 @@
 	return 0;
 }
 
+static int powersave_init(struct devfreq *devfreq)
+{
+	return update_devfreq(devfreq);
+}
+
 const struct devfreq_governor devfreq_powersave = {
 	.name = "powersave",
+	.init = powersave_init,
 	.get_target_freq = devfreq_powersave_func,
 	.no_central_polling = true,
 };
diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c
index cc595eb..f5552b3 100644
--- a/drivers/firewire/core-card.c
+++ b/drivers/firewire/core-card.c
@@ -676,6 +676,7 @@
 
 	complete(&card->done);
 }
+EXPORT_SYMBOL_GPL(fw_card_release);
 
 void fw_core_remove_card(struct fw_card *card)
 {
diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c
index dea2dcc..db8a965 100644
--- a/drivers/firewire/core-transaction.c
+++ b/drivers/firewire/core-transaction.c
@@ -820,6 +820,15 @@
 }
 EXPORT_SYMBOL(fw_send_response);
 
+/**
+ * fw_get_request_speed() - returns speed at which the @request was received
+ */
+int fw_get_request_speed(struct fw_request *request)
+{
+	return request->response.speed;
+}
+EXPORT_SYMBOL(fw_get_request_speed);
+
 static void handle_exclusive_region_request(struct fw_card *card,
 					    struct fw_packet *p,
 					    struct fw_request *request,
diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h
index 9047f55..b5a2f61 100644
--- a/drivers/firewire/core.h
+++ b/drivers/firewire/core.h
@@ -120,21 +120,6 @@
 void fw_schedule_bus_reset(struct fw_card *card, bool delayed, bool short_reset);
 void fw_schedule_bm_work(struct fw_card *card, unsigned long delay);
 
-static inline struct fw_card *fw_card_get(struct fw_card *card)
-{
-	kref_get(&card->kref);
-
-	return card;
-}
-
-void fw_card_release(struct kref *kref);
-
-static inline void fw_card_put(struct fw_card *card)
-{
-	kref_put(&card->kref, fw_card_release);
-}
-
-
 /* -cdev */
 
 extern const struct file_operations fw_device_ops;
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index e466ecb..7cd9bf4 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1102,6 +1102,19 @@
 	  This driver can also be build as a module.  If so, the module
 	  will be called amc6821.
 
+config SENSORS_INA2XX
+	tristate "Texas Instruments INA219, INA226"
+	depends on I2C && EXPERIMENTAL
+	help
+	  If you say yes here you get support for INA219 and INA226 power
+	  monitor chips.
+
+	  The INA2xx driver is configured for the default configuration of
+	  the part as described in the datasheet.
+	  Default value for Rshunt is 10 mOhms.
+	  This driver can also be built as a module.  If so, the module
+	  will be called ina2xx.
+
 config SENSORS_THMC50
 	tristate "Texas Instruments THMC50 / Analog Devices ADM1022"
 	depends on I2C
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 6d3f11f..e1eeac1 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -62,6 +62,7 @@
 obj-$(CONFIG_SENSORS_I5K_AMB)	+= i5k_amb.o
 obj-$(CONFIG_SENSORS_IBMAEM)	+= ibmaem.o
 obj-$(CONFIG_SENSORS_IBMPEX)	+= ibmpex.o
+obj-$(CONFIG_SENSORS_INA2XX)	+= ina2xx.o
 obj-$(CONFIG_SENSORS_IT87)	+= it87.o
 obj-$(CONFIG_SENSORS_JC42)	+= jc42.o
 obj-$(CONFIG_SENSORS_JZ4740)	+= jz4740-hwmon.o
diff --git a/drivers/hwmon/acpi_power_meter.c b/drivers/hwmon/acpi_power_meter.c
index 9140236..34ad5a2 100644
--- a/drivers/hwmon/acpi_power_meter.c
+++ b/drivers/hwmon/acpi_power_meter.c
@@ -107,15 +107,7 @@
 	struct kobject		*holders_dir;
 };
 
-struct ro_sensor_template {
-	char *label;
-	ssize_t (*show)(struct device *dev,
-			struct device_attribute *devattr,
-			char *buf);
-	int index;
-};
-
-struct rw_sensor_template {
+struct sensor_template {
 	char *label;
 	ssize_t (*show)(struct device *dev,
 			struct device_attribute *devattr,
@@ -469,52 +461,67 @@
 	return sprintf(buf, "%s\n", ACPI_POWER_METER_NAME);
 }
 
+#define RO_SENSOR_TEMPLATE(_label, _show, _index)	\
+	{						\
+		.label = _label,			\
+		.show  = _show,				\
+		.index = _index,			\
+	}
+
+#define RW_SENSOR_TEMPLATE(_label, _show, _set, _index)	\
+	{						\
+		.label = _label,			\
+		.show  = _show,				\
+		.set   = _set,				\
+		.index = _index,			\
+	}
+
 /* Sensor descriptions.  If you add a sensor, update NUM_SENSORS above! */
-static struct ro_sensor_template meter_ro_attrs[] = {
-{POWER_AVERAGE_NAME, show_power, 0},
-{"power1_accuracy", show_accuracy, 0},
-{"power1_average_interval_min", show_val, 0},
-{"power1_average_interval_max", show_val, 1},
-{"power1_is_battery", show_val, 5},
-{NULL, NULL, 0},
+static struct sensor_template meter_attrs[] = {
+	RO_SENSOR_TEMPLATE(POWER_AVERAGE_NAME, show_power, 0),
+	RO_SENSOR_TEMPLATE("power1_accuracy", show_accuracy, 0),
+	RO_SENSOR_TEMPLATE("power1_average_interval_min", show_val, 0),
+	RO_SENSOR_TEMPLATE("power1_average_interval_max", show_val, 1),
+	RO_SENSOR_TEMPLATE("power1_is_battery", show_val, 5),
+	RW_SENSOR_TEMPLATE(POWER_AVG_INTERVAL_NAME, show_avg_interval,
+		set_avg_interval, 0),
+	{},
 };
 
-static struct rw_sensor_template meter_rw_attrs[] = {
-{POWER_AVG_INTERVAL_NAME, show_avg_interval, set_avg_interval, 0},
-{NULL, NULL, NULL, 0},
+static struct sensor_template misc_cap_attrs[] = {
+	RO_SENSOR_TEMPLATE("power1_cap_min", show_val, 2),
+	RO_SENSOR_TEMPLATE("power1_cap_max", show_val, 3),
+	RO_SENSOR_TEMPLATE("power1_cap_hyst", show_val, 4),
+	RO_SENSOR_TEMPLATE(POWER_ALARM_NAME, show_val, 6),
+	{},
 };
 
-static struct ro_sensor_template misc_cap_attrs[] = {
-{"power1_cap_min", show_val, 2},
-{"power1_cap_max", show_val, 3},
-{"power1_cap_hyst", show_val, 4},
-{POWER_ALARM_NAME, show_val, 6},
-{NULL, NULL, 0},
+static struct sensor_template ro_cap_attrs[] = {
+	RO_SENSOR_TEMPLATE(POWER_CAP_NAME, show_cap, 0),
+	{},
 };
 
-static struct ro_sensor_template ro_cap_attrs[] = {
-{POWER_CAP_NAME, show_cap, 0},
-{NULL, NULL, 0},
+static struct sensor_template rw_cap_attrs[] = {
+	RW_SENSOR_TEMPLATE(POWER_CAP_NAME, show_cap, set_cap, 0),
+	{},
 };
 
-static struct rw_sensor_template rw_cap_attrs[] = {
-{POWER_CAP_NAME, show_cap, set_cap, 0},
-{NULL, NULL, NULL, 0},
+static struct sensor_template trip_attrs[] = {
+	RW_SENSOR_TEMPLATE("power1_average_min", show_val, set_trip, 7),
+	RW_SENSOR_TEMPLATE("power1_average_max", show_val, set_trip, 8),
+	{},
 };
 
-static struct rw_sensor_template trip_attrs[] = {
-{"power1_average_min", show_val, set_trip, 7},
-{"power1_average_max", show_val, set_trip, 8},
-{NULL, NULL, NULL, 0},
+static struct sensor_template misc_attrs[] = {
+	RO_SENSOR_TEMPLATE("name", show_name, 0),
+	RO_SENSOR_TEMPLATE("power1_model_number", show_str, 0),
+	RO_SENSOR_TEMPLATE("power1_oem_info", show_str, 2),
+	RO_SENSOR_TEMPLATE("power1_serial_number", show_str, 1),
+	{},
 };
 
-static struct ro_sensor_template misc_attrs[] = {
-{"name", show_name, 0},
-{"power1_model_number", show_str, 0},
-{"power1_oem_info", show_str, 2},
-{"power1_serial_number", show_str, 1},
-{NULL, NULL, 0},
-};
+#undef RO_SENSOR_TEMPLATE
+#undef RW_SENSOR_TEMPLATE
 
 /* Read power domain data */
 static void remove_domain_devices(struct acpi_power_meter_resource *resource)
@@ -619,19 +626,24 @@
 }
 
 /* Registration and deregistration */
-static int register_ro_attrs(struct acpi_power_meter_resource *resource,
-			     struct ro_sensor_template *ro)
+static int register_attrs(struct acpi_power_meter_resource *resource,
+			  struct sensor_template *attrs)
 {
 	struct device *dev = &resource->acpi_dev->dev;
 	struct sensor_device_attribute *sensors =
 		&resource->sensors[resource->num_sensors];
 	int res = 0;
 
-	while (ro->label) {
-		sensors->dev_attr.attr.name = ro->label;
+	while (attrs->label) {
+		sensors->dev_attr.attr.name = attrs->label;
 		sensors->dev_attr.attr.mode = S_IRUGO;
-		sensors->dev_attr.show = ro->show;
-		sensors->index = ro->index;
+		sensors->dev_attr.show = attrs->show;
+		sensors->index = attrs->index;
+
+		if (attrs->set) {
+			sensors->dev_attr.attr.mode |= S_IWUSR;
+			sensors->dev_attr.store = attrs->set;
+		}
 
 		sysfs_attr_init(&sensors->dev_attr.attr);
 		res = device_create_file(dev, &sensors->dev_attr);
@@ -641,37 +653,7 @@
 		}
 		sensors++;
 		resource->num_sensors++;
-		ro++;
-	}
-
-error:
-	return res;
-}
-
-static int register_rw_attrs(struct acpi_power_meter_resource *resource,
-			     struct rw_sensor_template *rw)
-{
-	struct device *dev = &resource->acpi_dev->dev;
-	struct sensor_device_attribute *sensors =
-		&resource->sensors[resource->num_sensors];
-	int res = 0;
-
-	while (rw->label) {
-		sensors->dev_attr.attr.name = rw->label;
-		sensors->dev_attr.attr.mode = S_IRUGO | S_IWUSR;
-		sensors->dev_attr.show = rw->show;
-		sensors->dev_attr.store = rw->set;
-		sensors->index = rw->index;
-
-		sysfs_attr_init(&sensors->dev_attr.attr);
-		res = device_create_file(dev, &sensors->dev_attr);
-		if (res) {
-			sensors->dev_attr.attr.name = NULL;
-			goto error;
-		}
-		sensors++;
-		resource->num_sensors++;
-		rw++;
+		attrs++;
 	}
 
 error:
@@ -703,10 +685,7 @@
 		return res;
 
 	if (resource->caps.flags & POWER_METER_CAN_MEASURE) {
-		res = register_ro_attrs(resource, meter_ro_attrs);
-		if (res)
-			goto error;
-		res = register_rw_attrs(resource, meter_rw_attrs);
+		res = register_attrs(resource, meter_attrs);
 		if (res)
 			goto error;
 	}
@@ -718,28 +697,27 @@
 			goto skip_unsafe_cap;
 		}
 
-		if (resource->caps.configurable_cap) {
-			res = register_rw_attrs(resource, rw_cap_attrs);
-			if (res)
-				goto error;
-		} else {
-			res = register_ro_attrs(resource, ro_cap_attrs);
-			if (res)
-				goto error;
-		}
-		res = register_ro_attrs(resource, misc_cap_attrs);
+		if (resource->caps.configurable_cap)
+			res = register_attrs(resource, rw_cap_attrs);
+		else
+			res = register_attrs(resource, ro_cap_attrs);
+
+		if (res)
+			goto error;
+
+		res = register_attrs(resource, misc_cap_attrs);
 		if (res)
 			goto error;
 	}
+
 skip_unsafe_cap:
-
 	if (resource->caps.flags & POWER_METER_CAN_TRIP) {
-		res = register_rw_attrs(resource, trip_attrs);
+		res = register_attrs(resource, trip_attrs);
 		if (res)
 			goto error;
 	}
 
-	res = register_ro_attrs(resource, misc_attrs);
+	res = register_attrs(resource, misc_attrs);
 	if (res)
 		goto error;
 
diff --git a/drivers/hwmon/ad7314.c b/drivers/hwmon/ad7314.c
index f85ce70..cfec802 100644
--- a/drivers/hwmon/ad7314.c
+++ b/drivers/hwmon/ad7314.c
@@ -18,21 +18,14 @@
 #include <linux/hwmon-sysfs.h>
 
 /*
- * AD7314 power mode
- */
-#define AD7314_PD		0x2000
-
-/*
  * AD7314 temperature masks
  */
-#define AD7314_TEMP_SIGN		0x200
 #define AD7314_TEMP_MASK		0x7FE0
-#define AD7314_TEMP_OFFSET		5
+#define AD7314_TEMP_SHIFT		5
 
 /*
  * ADT7301 and ADT7302 temperature masks
  */
-#define ADT7301_TEMP_SIGN		0x2000
 #define ADT7301_TEMP_MASK		0x3FFF
 
 enum ad7314_variant {
@@ -73,7 +66,7 @@
 		return ret;
 	switch (spi_get_device_id(chip->spi_dev)->driver_data) {
 	case ad7314:
-		data = (ret & AD7314_TEMP_MASK) >> AD7314_TEMP_OFFSET;
+		data = (ret & AD7314_TEMP_MASK) >> AD7314_TEMP_SHIFT;
 		data = (data << 6) >> 6;
 
 		return sprintf(buf, "%d\n", 250 * data);
diff --git a/drivers/hwmon/fam15h_power.c b/drivers/hwmon/fam15h_power.c
index e8e18ca..6b13f1a 100644
--- a/drivers/hwmon/fam15h_power.c
+++ b/drivers/hwmon/fam15h_power.c
@@ -257,15 +257,4 @@
 	.remove = __devexit_p(fam15h_power_remove),
 };
 
-static int __init fam15h_power_init(void)
-{
-	return pci_register_driver(&fam15h_power_driver);
-}
-
-static void __exit fam15h_power_exit(void)
-{
-	pci_unregister_driver(&fam15h_power_driver);
-}
-
-module_init(fam15h_power_init)
-module_exit(fam15h_power_exit)
+module_pci_driver(fam15h_power_driver);
diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c
new file mode 100644
index 0000000..7f3f4a3
--- /dev/null
+++ b/drivers/hwmon/ina2xx.c
@@ -0,0 +1,368 @@
+/*
+ * Driver for Texas Instruments INA219, INA226 power monitor chips
+ *
+ * INA219:
+ * Zero Drift Bi-Directional Current/Power Monitor with I2C Interface
+ * Datasheet: http://www.ti.com/product/ina219
+ *
+ * INA226:
+ * Bi-Directional Current/Power Monitor with I2C Interface
+ * Datasheet: http://www.ti.com/product/ina226
+ *
+ * Copyright (C) 2012 Lothar Felten <l-felten@ti.com>
+ * Thanks to Jan Volkering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+
+#include <linux/platform_data/ina2xx.h>
+
+/* common register definitions */
+#define INA2XX_CONFIG			0x00
+#define INA2XX_SHUNT_VOLTAGE		0x01 /* readonly */
+#define INA2XX_BUS_VOLTAGE		0x02 /* readonly */
+#define INA2XX_POWER			0x03 /* readonly */
+#define INA2XX_CURRENT			0x04 /* readonly */
+#define INA2XX_CALIBRATION		0x05
+
+/* INA226 register definitions */
+#define INA226_MASK_ENABLE		0x06
+#define INA226_ALERT_LIMIT		0x07
+#define INA226_DIE_ID			0xFF
+
+
+/* register count */
+#define INA219_REGISTERS		6
+#define INA226_REGISTERS		8
+
+#define INA2XX_MAX_REGISTERS		8
+
+/* settings - depend on use case */
+#define INA219_CONFIG_DEFAULT		0x399F	/* PGA=8 */
+#define INA226_CONFIG_DEFAULT		0x4527	/* averages=16 */
+
+/* worst case is 68.10 ms (~14.6Hz, ina219) */
+#define INA2XX_CONVERSION_RATE		15
+
+enum ina2xx_ids { ina219, ina226 };
+
+struct ina2xx_data {
+	struct device *hwmon_dev;
+
+	struct mutex update_lock;
+	bool valid;
+	unsigned long last_updated;
+
+	int kind;
+	int registers;
+	u16 regs[INA2XX_MAX_REGISTERS];
+};
+
+int ina2xx_read_word(struct i2c_client *client, int reg)
+{
+	int val = i2c_smbus_read_word_data(client, reg);
+	if (unlikely(val < 0)) {
+		dev_dbg(&client->dev,
+			"Failed to read register: %d\n", reg);
+		return val;
+	}
+	return be16_to_cpu(val);
+}
+
+void ina2xx_write_word(struct i2c_client *client, int reg, int data)
+{
+	i2c_smbus_write_word_data(client, reg, cpu_to_be16(data));
+}
+
+static struct ina2xx_data *ina2xx_update_device(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct ina2xx_data *data = i2c_get_clientdata(client);
+	struct ina2xx_data *ret = data;
+
+	mutex_lock(&data->update_lock);
+
+	if (time_after(jiffies, data->last_updated +
+		       HZ / INA2XX_CONVERSION_RATE) || !data->valid) {
+
+		int i;
+
+		dev_dbg(&client->dev, "Starting ina2xx update\n");
+
+		/* Read all registers */
+		for (i = 0; i < data->registers; i++) {
+			int rv = ina2xx_read_word(client, i);
+			if (rv < 0) {
+				ret = ERR_PTR(rv);
+				goto abort;
+			}
+			data->regs[i] = rv;
+		}
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+abort:
+	mutex_unlock(&data->update_lock);
+	return ret;
+}
+
+static int ina219_get_value(struct ina2xx_data *data, u8 reg)
+{
+	/*
+	 * calculate exact value for the given register
+	 * we assume default power-on reset settings:
+	 * bus voltage range 32V
+	 * gain = /8
+	 * adc 1 & 2 -> conversion time 532uS
+	 * mode is continuous shunt and bus
+	 * calibration value is INA219_CALIBRATION_VALUE
+	 */
+	int val = data->regs[reg];
+
+	switch (reg) {
+	case INA2XX_SHUNT_VOLTAGE:
+		/* LSB=10uV. Convert to mV. */
+		val = DIV_ROUND_CLOSEST(val, 100);
+		break;
+	case INA2XX_BUS_VOLTAGE:
+		/* LSB=4mV. Register is not right aligned, convert to mV. */
+		val = (val >> 3) * 4;
+		break;
+	case INA2XX_POWER:
+		/* LSB=20mW. Convert to uW */
+		val = val * 20 * 1000;
+		break;
+	case INA2XX_CURRENT:
+		/* LSB=1mA (selected). Is in mA */
+		break;
+	default:
+		/* programmer goofed */
+		WARN_ON_ONCE(1);
+		val = 0;
+		break;
+	}
+
+	return val;
+}
+
+static int ina226_get_value(struct ina2xx_data *data, u8 reg)
+{
+	/*
+	 * calculate exact value for the given register
+	 * we assume default power-on reset settings:
+	 * bus voltage range 32V
+	 * gain = /8
+	 * adc 1 & 2 -> conversion time 532uS
+	 * mode is continuous shunt and bus
+	 * calibration value is INA226_CALIBRATION_VALUE
+	 */
+	int val = data->regs[reg];
+
+	switch (reg) {
+	case INA2XX_SHUNT_VOLTAGE:
+		/* LSB=2.5uV. Convert to mV. */
+		val = DIV_ROUND_CLOSEST(val, 400);
+		break;
+	case INA2XX_BUS_VOLTAGE:
+		/* LSB=1.25mV. Convert to mV. */
+		val = val + DIV_ROUND_CLOSEST(val, 4);
+		break;
+	case INA2XX_POWER:
+		/* LSB=25mW. Convert to uW */
+		val = val * 25 * 1000;
+		break;
+	case INA2XX_CURRENT:
+		/* LSB=1mA (selected). Is in mA */
+		break;
+	default:
+		/* programmer goofed */
+		WARN_ON_ONCE(1);
+		val = 0;
+		break;
+	}
+
+	return val;
+}
+
+static ssize_t ina2xx_show_value(struct device *dev,
+				 struct device_attribute *da, char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+	struct ina2xx_data *data = ina2xx_update_device(dev);
+	int value = 0;
+
+	if (IS_ERR(data))
+		return PTR_ERR(data);
+
+	switch (data->kind) {
+	case ina219:
+		value = ina219_get_value(data, attr->index);
+		break;
+	case ina226:
+		value = ina226_get_value(data, attr->index);
+		break;
+	default:
+		WARN_ON_ONCE(1);
+		break;
+	}
+	return snprintf(buf, PAGE_SIZE, "%d\n", value);
+}
+
+/* shunt voltage */
+static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, \
+	ina2xx_show_value, NULL, INA2XX_SHUNT_VOLTAGE);
+
+/* bus voltage */
+static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, \
+	ina2xx_show_value, NULL, INA2XX_BUS_VOLTAGE);
+
+/* calculated current */
+static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, \
+	ina2xx_show_value, NULL, INA2XX_CURRENT);
+
+/* calculated power */
+static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, \
+	ina2xx_show_value, NULL, INA2XX_POWER);
+
+/* pointers to created device attributes */
+static struct attribute *ina2xx_attributes[] = {
+	&sensor_dev_attr_in0_input.dev_attr.attr,
+	&sensor_dev_attr_in1_input.dev_attr.attr,
+	&sensor_dev_attr_curr1_input.dev_attr.attr,
+	&sensor_dev_attr_power1_input.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group ina2xx_group = {
+	.attrs = ina2xx_attributes,
+};
+
+static int ina2xx_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct i2c_adapter *adapter = client->adapter;
+	struct ina2xx_data *data;
+	struct ina2xx_platform_data *pdata;
+	int ret = 0;
+	long shunt = 10000; /* default shunt value 10mOhms */
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA))
+		return -ENODEV;
+
+	data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	if (client->dev.platform_data) {
+		pdata =
+		  (struct ina2xx_platform_data *)client->dev.platform_data;
+		shunt = pdata->shunt_uohms;
+	}
+
+	if (shunt <= 0)
+		return -ENODEV;
+
+	/* set the device type */
+	data->kind = id->driver_data;
+
+	switch (data->kind) {
+	case ina219:
+		/* device configuration */
+		ina2xx_write_word(client, INA2XX_CONFIG, INA219_CONFIG_DEFAULT);
+
+		/* set current LSB to 1mA, shunt is in uOhms */
+		/* (equation 13 in datasheet) */
+		ina2xx_write_word(client, INA2XX_CALIBRATION, 40960000 / shunt);
+		dev_info(&client->dev,
+			 "power monitor INA219 (Rshunt = %li uOhm)\n", shunt);
+		data->registers = INA219_REGISTERS;
+		break;
+	case ina226:
+		/* device configuration */
+		ina2xx_write_word(client, INA2XX_CONFIG, INA226_CONFIG_DEFAULT);
+
+		/* set current LSB to 1mA, shunt is in uOhms */
+		/* (equation 1 in datasheet)*/
+		ina2xx_write_word(client, INA2XX_CALIBRATION, 5120000 / shunt);
+		dev_info(&client->dev,
+			 "power monitor INA226 (Rshunt = %li uOhm)\n", shunt);
+		data->registers = INA226_REGISTERS;
+		break;
+	default:
+		/* unknown device id */
+		return -ENODEV;
+	}
+
+	i2c_set_clientdata(client, data);
+	mutex_init(&data->update_lock);
+
+	ret = sysfs_create_group(&client->dev.kobj, &ina2xx_group);
+	if (ret)
+		return ret;
+
+	data->hwmon_dev = hwmon_device_register(&client->dev);
+	if (IS_ERR(data->hwmon_dev)) {
+		ret = PTR_ERR(data->hwmon_dev);
+		goto out_err_hwmon;
+	}
+
+	return 0;
+
+out_err_hwmon:
+	sysfs_remove_group(&client->dev.kobj, &ina2xx_group);
+	return ret;
+}
+
+static int ina2xx_remove(struct i2c_client *client)
+{
+	struct ina2xx_data *data = i2c_get_clientdata(client);
+
+	hwmon_device_unregister(data->hwmon_dev);
+	sysfs_remove_group(&client->dev.kobj, &ina2xx_group);
+
+	return 0;
+}
+
+static const struct i2c_device_id ina2xx_id[] = {
+	{ "ina219", ina219 },
+	{ "ina226", ina226 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ina2xx_id);
+
+static struct i2c_driver ina2xx_driver = {
+	.driver = {
+		.name	= "ina2xx",
+	},
+	.probe		= ina2xx_probe,
+	.remove		= ina2xx_remove,
+	.id_table	= ina2xx_id,
+};
+
+static int __init ina2xx_init(void)
+{
+	return i2c_add_driver(&ina2xx_driver);
+}
+
+static void __exit ina2xx_exit(void)
+{
+	i2c_del_driver(&ina2xx_driver);
+}
+
+MODULE_AUTHOR("Lothar Felten <l-felten@ti.com>");
+MODULE_DESCRIPTION("ina2xx driver");
+MODULE_LICENSE("GPL");
+
+module_init(ina2xx_init);
+module_exit(ina2xx_exit);
diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c
index 0b204e4..e7701d9 100644
--- a/drivers/hwmon/it87.c
+++ b/drivers/hwmon/it87.c
@@ -19,6 +19,8 @@
  *            IT8726F  Super I/O chip w/LPC interface
  *            IT8728F  Super I/O chip w/LPC interface
  *            IT8758E  Super I/O chip w/LPC interface
+ *            IT8782F  Super I/O chip w/LPC interface
+ *            IT8783E/F Super I/O chip w/LPC interface
  *            Sis950   A clone of the IT8705F
  *
  *  Copyright (C) 2001 Chris Gauthron
@@ -59,7 +61,8 @@
 
 #define DRVNAME "it87"
 
-enum chips { it87, it8712, it8716, it8718, it8720, it8721, it8728 };
+enum chips { it87, it8712, it8716, it8718, it8720, it8721, it8728, it8782,
+	     it8783 };
 
 static unsigned short force_id;
 module_param(force_id, ushort, 0);
@@ -137,13 +140,18 @@
 #define IT8721F_DEVID 0x8721
 #define IT8726F_DEVID 0x8726
 #define IT8728F_DEVID 0x8728
+#define IT8782F_DEVID 0x8782
+#define IT8783E_DEVID 0x8783
 #define IT87_ACT_REG  0x30
 #define IT87_BASE_REG 0x60
 
 /* Logical device 7 registers (IT8712F and later) */
+#define IT87_SIO_GPIO1_REG	0x25
 #define IT87_SIO_GPIO3_REG	0x27
 #define IT87_SIO_GPIO5_REG	0x29
+#define IT87_SIO_PINX1_REG	0x2a	/* Pin selection */
 #define IT87_SIO_PINX2_REG	0x2c	/* Pin selection */
+#define IT87_SIO_SPI_REG	0xef	/* SPI function pin select */
 #define IT87_SIO_VID_REG	0xfc	/* VID value */
 #define IT87_SIO_BEEP_PIN_REG	0xf6	/* Beep pin mapping */
 
@@ -210,6 +218,7 @@
 
 #define IT87_REG_VIN_ENABLE    0x50
 #define IT87_REG_TEMP_ENABLE   0x51
+#define IT87_REG_TEMP_EXTRA    0x55
 #define IT87_REG_BEEP_ENABLE   0x5c
 
 #define IT87_REG_CHIPID        0x58
@@ -226,9 +235,11 @@
 	u8 beep_pin;
 	u8 internal;	/* Internal sensors can be labeled */
 	/* Features skipped based on config or DMI */
+	u16 skip_in;
 	u8 skip_vid;
 	u8 skip_fan;
 	u8 skip_pwm;
+	u8 skip_temp;
 };
 
 /*
@@ -253,6 +264,7 @@
 	u8 has_fan;		/* Bitfield, fans enabled */
 	u16 fan[5];		/* Register values, possibly combined */
 	u16 fan_min[5];		/* Register values, possibly combined */
+	u8 has_temp;		/* Bitfield, temp sensors enabled */
 	s8 temp[3];		/* Register value */
 	s8 temp_high[3];	/* Register value */
 	s8 temp_low[3];		/* Register value */
@@ -304,31 +316,23 @@
 	    || data->type == it8728;
 }
 
+static int adc_lsb(const struct it87_data *data, int nr)
+{
+	int lsb = has_12mv_adc(data) ? 12 : 16;
+	if (data->in_scaled & (1 << nr))
+		lsb <<= 1;
+	return lsb;
+}
+
 static u8 in_to_reg(const struct it87_data *data, int nr, long val)
 {
-	long lsb;
-
-	if (has_12mv_adc(data)) {
-		if (data->in_scaled & (1 << nr))
-			lsb = 24;
-		else
-			lsb = 12;
-	} else
-		lsb = 16;
-
-	val = DIV_ROUND_CLOSEST(val, lsb);
+	val = DIV_ROUND_CLOSEST(val, adc_lsb(data, nr));
 	return SENSORS_LIMIT(val, 0, 255);
 }
 
 static int in_from_reg(const struct it87_data *data, int nr, int val)
 {
-	if (has_12mv_adc(data)) {
-		if (data->in_scaled & (1 << nr))
-			return val * 24;
-		else
-			return val * 12;
-	} else
-		return val * 16;
+	return val * adc_lsb(data, nr);
 }
 
 static inline u8 FAN_TO_REG(long rpm, int div)
@@ -407,7 +411,9 @@
 	    || data->type == it8718
 	    || data->type == it8720
 	    || data->type == it8721
-	    || data->type == it8728;
+	    || data->type == it8728
+	    || data->type == it8782
+	    || data->type == it8783;
 }
 
 static inline int has_old_autopwm(const struct it87_data *data)
@@ -1369,57 +1375,103 @@
 }
 static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
 
-static struct attribute *it87_attributes[] = {
+static struct attribute *it87_attributes_in[9][5] = {
+{
 	&sensor_dev_attr_in0_input.dev_attr.attr,
-	&sensor_dev_attr_in1_input.dev_attr.attr,
-	&sensor_dev_attr_in2_input.dev_attr.attr,
-	&sensor_dev_attr_in3_input.dev_attr.attr,
-	&sensor_dev_attr_in4_input.dev_attr.attr,
-	&sensor_dev_attr_in5_input.dev_attr.attr,
-	&sensor_dev_attr_in6_input.dev_attr.attr,
-	&sensor_dev_attr_in7_input.dev_attr.attr,
-	&sensor_dev_attr_in8_input.dev_attr.attr,
 	&sensor_dev_attr_in0_min.dev_attr.attr,
-	&sensor_dev_attr_in1_min.dev_attr.attr,
-	&sensor_dev_attr_in2_min.dev_attr.attr,
-	&sensor_dev_attr_in3_min.dev_attr.attr,
-	&sensor_dev_attr_in4_min.dev_attr.attr,
-	&sensor_dev_attr_in5_min.dev_attr.attr,
-	&sensor_dev_attr_in6_min.dev_attr.attr,
-	&sensor_dev_attr_in7_min.dev_attr.attr,
 	&sensor_dev_attr_in0_max.dev_attr.attr,
-	&sensor_dev_attr_in1_max.dev_attr.attr,
-	&sensor_dev_attr_in2_max.dev_attr.attr,
-	&sensor_dev_attr_in3_max.dev_attr.attr,
-	&sensor_dev_attr_in4_max.dev_attr.attr,
-	&sensor_dev_attr_in5_max.dev_attr.attr,
-	&sensor_dev_attr_in6_max.dev_attr.attr,
-	&sensor_dev_attr_in7_max.dev_attr.attr,
 	&sensor_dev_attr_in0_alarm.dev_attr.attr,
+	NULL
+}, {
+	&sensor_dev_attr_in1_input.dev_attr.attr,
+	&sensor_dev_attr_in1_min.dev_attr.attr,
+	&sensor_dev_attr_in1_max.dev_attr.attr,
 	&sensor_dev_attr_in1_alarm.dev_attr.attr,
+	NULL
+}, {
+	&sensor_dev_attr_in2_input.dev_attr.attr,
+	&sensor_dev_attr_in2_min.dev_attr.attr,
+	&sensor_dev_attr_in2_max.dev_attr.attr,
 	&sensor_dev_attr_in2_alarm.dev_attr.attr,
+	NULL
+}, {
+	&sensor_dev_attr_in3_input.dev_attr.attr,
+	&sensor_dev_attr_in3_min.dev_attr.attr,
+	&sensor_dev_attr_in3_max.dev_attr.attr,
 	&sensor_dev_attr_in3_alarm.dev_attr.attr,
+	NULL
+}, {
+	&sensor_dev_attr_in4_input.dev_attr.attr,
+	&sensor_dev_attr_in4_min.dev_attr.attr,
+	&sensor_dev_attr_in4_max.dev_attr.attr,
 	&sensor_dev_attr_in4_alarm.dev_attr.attr,
+	NULL
+}, {
+	&sensor_dev_attr_in5_input.dev_attr.attr,
+	&sensor_dev_attr_in5_min.dev_attr.attr,
+	&sensor_dev_attr_in5_max.dev_attr.attr,
 	&sensor_dev_attr_in5_alarm.dev_attr.attr,
+	NULL
+}, {
+	&sensor_dev_attr_in6_input.dev_attr.attr,
+	&sensor_dev_attr_in6_min.dev_attr.attr,
+	&sensor_dev_attr_in6_max.dev_attr.attr,
 	&sensor_dev_attr_in6_alarm.dev_attr.attr,
+	NULL
+}, {
+	&sensor_dev_attr_in7_input.dev_attr.attr,
+	&sensor_dev_attr_in7_min.dev_attr.attr,
+	&sensor_dev_attr_in7_max.dev_attr.attr,
 	&sensor_dev_attr_in7_alarm.dev_attr.attr,
+	NULL
+}, {
+	&sensor_dev_attr_in8_input.dev_attr.attr,
+	NULL
+} };
 
+static const struct attribute_group it87_group_in[9] = {
+	{ .attrs = it87_attributes_in[0] },
+	{ .attrs = it87_attributes_in[1] },
+	{ .attrs = it87_attributes_in[2] },
+	{ .attrs = it87_attributes_in[3] },
+	{ .attrs = it87_attributes_in[4] },
+	{ .attrs = it87_attributes_in[5] },
+	{ .attrs = it87_attributes_in[6] },
+	{ .attrs = it87_attributes_in[7] },
+	{ .attrs = it87_attributes_in[8] },
+};
+
+static struct attribute *it87_attributes_temp[3][6] = {
+{
 	&sensor_dev_attr_temp1_input.dev_attr.attr,
-	&sensor_dev_attr_temp2_input.dev_attr.attr,
-	&sensor_dev_attr_temp3_input.dev_attr.attr,
 	&sensor_dev_attr_temp1_max.dev_attr.attr,
-	&sensor_dev_attr_temp2_max.dev_attr.attr,
-	&sensor_dev_attr_temp3_max.dev_attr.attr,
 	&sensor_dev_attr_temp1_min.dev_attr.attr,
-	&sensor_dev_attr_temp2_min.dev_attr.attr,
-	&sensor_dev_attr_temp3_min.dev_attr.attr,
 	&sensor_dev_attr_temp1_type.dev_attr.attr,
-	&sensor_dev_attr_temp2_type.dev_attr.attr,
-	&sensor_dev_attr_temp3_type.dev_attr.attr,
 	&sensor_dev_attr_temp1_alarm.dev_attr.attr,
+	NULL
+} , {
+	&sensor_dev_attr_temp2_input.dev_attr.attr,
+	&sensor_dev_attr_temp2_max.dev_attr.attr,
+	&sensor_dev_attr_temp2_min.dev_attr.attr,
+	&sensor_dev_attr_temp2_type.dev_attr.attr,
 	&sensor_dev_attr_temp2_alarm.dev_attr.attr,
+	NULL
+} , {
+	&sensor_dev_attr_temp3_input.dev_attr.attr,
+	&sensor_dev_attr_temp3_max.dev_attr.attr,
+	&sensor_dev_attr_temp3_min.dev_attr.attr,
+	&sensor_dev_attr_temp3_type.dev_attr.attr,
 	&sensor_dev_attr_temp3_alarm.dev_attr.attr,
+	NULL
+} };
 
+static const struct attribute_group it87_group_temp[3] = {
+	{ .attrs = it87_attributes_temp[0] },
+	{ .attrs = it87_attributes_temp[1] },
+	{ .attrs = it87_attributes_temp[2] },
+};
+
+static struct attribute *it87_attributes[] = {
 	&dev_attr_alarms.attr,
 	&sensor_dev_attr_intrusion0_alarm.dev_attr.attr,
 	&dev_attr_name.attr,
@@ -1430,7 +1482,7 @@
 	.attrs = it87_attributes,
 };
 
-static struct attribute *it87_attributes_beep[] = {
+static struct attribute *it87_attributes_in_beep[] = {
 	&sensor_dev_attr_in0_beep.dev_attr.attr,
 	&sensor_dev_attr_in1_beep.dev_attr.attr,
 	&sensor_dev_attr_in2_beep.dev_attr.attr,
@@ -1439,15 +1491,13 @@
 	&sensor_dev_attr_in5_beep.dev_attr.attr,
 	&sensor_dev_attr_in6_beep.dev_attr.attr,
 	&sensor_dev_attr_in7_beep.dev_attr.attr,
-
-	&sensor_dev_attr_temp1_beep.dev_attr.attr,
-	&sensor_dev_attr_temp2_beep.dev_attr.attr,
-	&sensor_dev_attr_temp3_beep.dev_attr.attr,
 	NULL
 };
 
-static const struct attribute_group it87_group_beep = {
-	.attrs = it87_attributes_beep,
+static struct attribute *it87_attributes_temp_beep[] = {
+	&sensor_dev_attr_temp1_beep.dev_attr.attr,
+	&sensor_dev_attr_temp2_beep.dev_attr.attr,
+	&sensor_dev_attr_temp3_beep.dev_attr.attr,
 };
 
 static struct attribute *it87_attributes_fan16[5][3+1] = { {
@@ -1651,6 +1701,12 @@
 	case IT8728F_DEVID:
 		sio_data->type = it8728;
 		break;
+	case IT8782F_DEVID:
+		sio_data->type = it8782;
+		break;
+	case IT8783E_DEVID:
+		sio_data->type = it8783;
+		break;
 	case 0xffff:	/* No device at all */
 		goto exit;
 	default:
@@ -1686,16 +1742,86 @@
 		/* The IT8705F has a different LD number for GPIO */
 		superio_select(5);
 		sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f;
+	} else if (sio_data->type == it8783) {
+		int reg25, reg27, reg2A, reg2C, regEF;
+
+		sio_data->skip_vid = 1;	/* No VID */
+
+		superio_select(GPIO);
+
+		reg25 = superio_inb(IT87_SIO_GPIO1_REG);
+		reg27 = superio_inb(IT87_SIO_GPIO3_REG);
+		reg2A = superio_inb(IT87_SIO_PINX1_REG);
+		reg2C = superio_inb(IT87_SIO_PINX2_REG);
+		regEF = superio_inb(IT87_SIO_SPI_REG);
+
+		/* Check if fan3 is there or not */
+		if ((reg27 & (1 << 0)) || !(reg2C & (1 << 2)))
+			sio_data->skip_fan |= (1 << 2);
+		if ((reg25 & (1 << 4))
+		    || (!(reg2A & (1 << 1)) && (regEF & (1 << 0))))
+			sio_data->skip_pwm |= (1 << 2);
+
+		/* Check if fan2 is there or not */
+		if (reg27 & (1 << 7))
+			sio_data->skip_fan |= (1 << 1);
+		if (reg27 & (1 << 3))
+			sio_data->skip_pwm |= (1 << 1);
+
+		/* VIN5 */
+		if ((reg27 & (1 << 0)) || (reg2C & (1 << 2)))
+			sio_data->skip_in |= (1 << 5); /* No VIN5 */
+
+		/* VIN6 */
+		if (reg27 & (1 << 1))
+			sio_data->skip_in |= (1 << 6); /* No VIN6 */
+
+		/*
+		 * VIN7
+		 * Does not depend on bit 2 of Reg2C, contrary to datasheet.
+		 */
+		if (reg27 & (1 << 2)) {
+			/*
+			 * The data sheet is a bit unclear regarding the
+			 * internal voltage divider for VCCH5V. It says
+			 * "This bit enables and switches VIN7 (pin 91) to the
+			 * internal voltage divider for VCCH5V".
+			 * This is different to other chips, where the internal
+			 * voltage divider would connect VIN7 to an internal
+			 * voltage source. Maybe that is the case here as well.
+			 *
+			 * Since we don't know for sure, re-route it if that is
+			 * not the case, and ask the user to report if the
+			 * resulting voltage is sane.
+			 */
+			if (!(reg2C & (1 << 1))) {
+				reg2C |= (1 << 1);
+				superio_outb(IT87_SIO_PINX2_REG, reg2C);
+				pr_notice("Routing internal VCCH5V to in7.\n");
+			}
+			pr_notice("in7 routed to internal voltage divider, with external pin disabled.\n");
+			pr_notice("Please report if it displays a reasonable voltage.\n");
+		}
+
+		if (reg2C & (1 << 0))
+			sio_data->internal |= (1 << 0);
+		if (reg2C & (1 << 1))
+			sio_data->internal |= (1 << 1);
+
+		sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f;
+
 	} else {
 		int reg;
+		bool uart6;
 
 		superio_select(GPIO);
 
 		reg = superio_inb(IT87_SIO_GPIO3_REG);
-		if (sio_data->type == it8721 || sio_data->type == it8728) {
+		if (sio_data->type == it8721 || sio_data->type == it8728 ||
+		    sio_data->type == it8782) {
 			/*
-			 * The IT8721F/IT8758E doesn't have VID pins at all,
-			 * not sure about the IT8728F.
+			 * IT8721F/IT8758E, and IT8782F don't have VID pins
+			 * at all, not sure about the IT8728F.
 			 */
 			sio_data->skip_vid = 1;
 		} else {
@@ -1724,6 +1850,9 @@
 			sio_data->vid_value = superio_inb(IT87_SIO_VID_REG);
 
 		reg = superio_inb(IT87_SIO_PINX2_REG);
+
+		uart6 = sio_data->type == it8782 && (reg & (1 << 2));
+
 		/*
 		 * The IT8720F has no VIN7 pin, so VCCH should always be
 		 * routed internally to VIN7 with an internal divider.
@@ -1733,8 +1862,12 @@
 		 * configured, even though the IT8720F datasheet claims
 		 * that the internal routing of VCCH to VIN7 is the default
 		 * setting. So we force the internal routing in this case.
+		 *
+		 * On IT8782F, VIN7 is multiplexed with one of the UART6 pins.
+		 * If UART6 is enabled, re-route VIN7 to the internal divider
+		 * if that is not already the case.
 		 */
-		if (sio_data->type == it8720 && !(reg & (1 << 1))) {
+		if ((sio_data->type == it8720 || uart6) && !(reg & (1 << 1))) {
 			reg |= (1 << 1);
 			superio_outb(IT87_SIO_PINX2_REG, reg);
 			pr_notice("Routing internal VCCH to in7\n");
@@ -1745,6 +1878,20 @@
 		    sio_data->type == it8728)
 			sio_data->internal |= (1 << 1);
 
+		/*
+		 * On IT8782F, UART6 pins overlap with VIN5, VIN6, and VIN7.
+		 * While VIN7 can be routed to the internal voltage divider,
+		 * VIN5 and VIN6 are not available if UART6 is enabled.
+		 *
+		 * Also, temp3 is not available if UART6 is enabled and TEMPIN3
+		 * is the temperature source. Since we can not read the
+		 * temperature source here, skip_temp is preliminary.
+		 */
+		if (uart6) {
+			sio_data->skip_in |= (1 << 5) | (1 << 6);
+			sio_data->skip_temp |= (1 << 2);
+		}
+
 		sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f;
 	}
 	if (sio_data->beep_pin)
@@ -1782,8 +1929,22 @@
 	int i;
 
 	sysfs_remove_group(&dev->kobj, &it87_group);
-	if (sio_data->beep_pin)
-		sysfs_remove_group(&dev->kobj, &it87_group_beep);
+	for (i = 0; i < 9; i++) {
+		if (sio_data->skip_in & (1 << i))
+			continue;
+		sysfs_remove_group(&dev->kobj, &it87_group_in[i]);
+		if (it87_attributes_in_beep[i])
+			sysfs_remove_file(&dev->kobj,
+					  it87_attributes_in_beep[i]);
+	}
+	for (i = 0; i < 3; i++) {
+		if (!(data->has_temp & (1 << i)))
+			continue;
+		sysfs_remove_group(&dev->kobj, &it87_group_temp[i]);
+		if (sio_data->beep_pin)
+			sysfs_remove_file(&dev->kobj,
+					  it87_attributes_temp_beep[i]);
+	}
 	for (i = 0; i < 5; i++) {
 		if (!(data->has_fan & (1 << i)))
 			continue;
@@ -1823,22 +1984,22 @@
 		"it8720",
 		"it8721",
 		"it8728",
+		"it8782",
+		"it8783",
 	};
 
 	res = platform_get_resource(pdev, IORESOURCE_IO, 0);
-	if (!request_region(res->start, IT87_EC_EXTENT, DRVNAME)) {
+	if (!devm_request_region(&pdev->dev, res->start, IT87_EC_EXTENT,
+				 DRVNAME)) {
 		dev_err(dev, "Failed to request region 0x%lx-0x%lx\n",
 			(unsigned long)res->start,
 			(unsigned long)(res->start + IT87_EC_EXTENT - 1));
-		err = -EBUSY;
-		goto ERROR0;
+		return -EBUSY;
 	}
 
-	data = kzalloc(sizeof(struct it87_data), GFP_KERNEL);
-	if (!data) {
-		err = -ENOMEM;
-		goto ERROR1;
-	}
+	data = devm_kzalloc(&pdev->dev, sizeof(struct it87_data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
 
 	data->addr = res->start;
 	data->type = sio_data->type;
@@ -1847,10 +2008,8 @@
 
 	/* Now, we do the remaining detection. */
 	if ((it87_read_value(data, IT87_REG_CONFIG) & 0x80)
-	 || it87_read_value(data, IT87_REG_CHIPID) != 0x90) {
-		err = -ENODEV;
-		goto ERROR2;
-	}
+	 || it87_read_value(data, IT87_REG_CHIPID) != 0x90)
+		return -ENODEV;
 
 	platform_set_drvdata(pdev, data);
 
@@ -1867,6 +2026,18 @@
 			data->in_scaled |= (1 << 7);	/* in7 is VSB */
 		if (sio_data->internal & (1 << 2))
 			data->in_scaled |= (1 << 8);	/* in8 is Vbat */
+	} else if (sio_data->type == it8782 || sio_data->type == it8783) {
+		if (sio_data->internal & (1 << 0))
+			data->in_scaled |= (1 << 3);	/* in3 is VCC5V */
+		if (sio_data->internal & (1 << 1))
+			data->in_scaled |= (1 << 7);	/* in7 is VCCH5V */
+	}
+
+	data->has_temp = 0x07;
+	if (sio_data->skip_temp & (1 << 2)) {
+		if (sio_data->type == it8782
+		    && !(it87_read_value(data, IT87_REG_TEMP_EXTRA) & 0x80))
+			data->has_temp &= ~(1 << 2);
 	}
 
 	/* Initialize the IT87 chip */
@@ -1875,12 +2046,34 @@
 	/* Register sysfs hooks */
 	err = sysfs_create_group(&dev->kobj, &it87_group);
 	if (err)
-		goto ERROR2;
+		return err;
 
-	if (sio_data->beep_pin) {
-		err = sysfs_create_group(&dev->kobj, &it87_group_beep);
+	for (i = 0; i < 9; i++) {
+		if (sio_data->skip_in & (1 << i))
+			continue;
+		err = sysfs_create_group(&dev->kobj, &it87_group_in[i]);
 		if (err)
-			goto ERROR4;
+			goto error;
+		if (sio_data->beep_pin && it87_attributes_in_beep[i]) {
+			err = sysfs_create_file(&dev->kobj,
+						it87_attributes_in_beep[i]);
+			if (err)
+				goto error;
+		}
+	}
+
+	for (i = 0; i < 3; i++) {
+		if (!(data->has_temp & (1 << i)))
+			continue;
+		err = sysfs_create_group(&dev->kobj, &it87_group_temp[i]);
+		if (err)
+			goto error;
+		if (sio_data->beep_pin) {
+			err = sysfs_create_file(&dev->kobj,
+						it87_attributes_temp_beep[i]);
+			if (err)
+				goto error;
+		}
 	}
 
 	/* Do not create fan files for disabled fans */
@@ -1891,13 +2084,13 @@
 			continue;
 		err = sysfs_create_group(&dev->kobj, &fan_group[i]);
 		if (err)
-			goto ERROR4;
+			goto error;
 
 		if (sio_data->beep_pin) {
 			err = sysfs_create_file(&dev->kobj,
 						it87_attributes_fan_beep[i]);
 			if (err)
-				goto ERROR4;
+				goto error;
 			if (!fan_beep_need_rw)
 				continue;
 
@@ -1922,14 +2115,14 @@
 			err = sysfs_create_group(&dev->kobj,
 						 &it87_group_pwm[i]);
 			if (err)
-				goto ERROR4;
+				goto error;
 
 			if (!has_old_autopwm(data))
 				continue;
 			err = sysfs_create_group(&dev->kobj,
 						 &it87_group_autopwm[i]);
 			if (err)
-				goto ERROR4;
+				goto error;
 		}
 	}
 
@@ -1939,7 +2132,7 @@
 		data->vid = sio_data->vid_value;
 		err = sysfs_create_group(&dev->kobj, &it87_group_vid);
 		if (err)
-			goto ERROR4;
+			goto error;
 	}
 
 	/* Export labels for internal sensors */
@@ -1949,25 +2142,19 @@
 		err = sysfs_create_file(&dev->kobj,
 					it87_attributes_label[i]);
 		if (err)
-			goto ERROR4;
+			goto error;
 	}
 
 	data->hwmon_dev = hwmon_device_register(dev);
 	if (IS_ERR(data->hwmon_dev)) {
 		err = PTR_ERR(data->hwmon_dev);
-		goto ERROR4;
+		goto error;
 	}
 
 	return 0;
 
-ERROR4:
+error:
 	it87_remove_files(dev);
-ERROR2:
-	platform_set_drvdata(pdev, NULL);
-	kfree(data);
-ERROR1:
-	release_region(res->start, IT87_EC_EXTENT);
-ERROR0:
 	return err;
 }
 
@@ -1978,10 +2165,6 @@
 	hwmon_device_unregister(data->hwmon_dev);
 	it87_remove_files(&pdev->dev);
 
-	release_region(data->addr, IT87_EC_EXTENT);
-	platform_set_drvdata(pdev, NULL);
-	kfree(data);
-
 	return 0;
 }
 
@@ -2143,8 +2326,9 @@
 			it87_write_value(data, IT87_REG_FAN_16BIT,
 					 tmp | 0x07);
 		}
-		/* IT8705F only supports three fans. */
-		if (data->type != it87) {
+		/* IT8705F, IT8782F, and IT8783E/F only support three fans. */
+		if (data->type != it87 && data->type != it8782 &&
+		    data->type != it8783) {
 			if (tmp & (1 << 4))
 				data->has_fan |= (1 << 3); /* fan4 enabled */
 			if (tmp & (1 << 5))
@@ -2233,6 +2417,8 @@
 			}
 		}
 		for (i = 0; i < 3; i++) {
+			if (!(data->has_temp & (1 << i)))
+				continue;
 			data->temp[i] =
 				it87_read_value(data, IT87_REG_TEMP(i));
 			data->temp_high[i] =
diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c
index 307bb32..7356b5e 100644
--- a/drivers/hwmon/k10temp.c
+++ b/drivers/hwmon/k10temp.c
@@ -225,15 +225,4 @@
 	.remove = __devexit_p(k10temp_remove),
 };
 
-static int __init k10temp_init(void)
-{
-	return pci_register_driver(&k10temp_driver);
-}
-
-static void __exit k10temp_exit(void)
-{
-	pci_unregister_driver(&k10temp_driver);
-}
-
-module_init(k10temp_init)
-module_exit(k10temp_exit)
+module_pci_driver(k10temp_driver);
diff --git a/drivers/hwmon/k8temp.c b/drivers/hwmon/k8temp.c
index 5751019..35aac82 100644
--- a/drivers/hwmon/k8temp.c
+++ b/drivers/hwmon/k8temp.c
@@ -339,19 +339,8 @@
 	.remove = __devexit_p(k8temp_remove),
 };
 
-static int __init k8temp_init(void)
-{
-	return pci_register_driver(&k8temp_driver);
-}
-
-static void __exit k8temp_exit(void)
-{
-	pci_unregister_driver(&k8temp_driver);
-}
+module_pci_driver(k8temp_driver);
 
 MODULE_AUTHOR("Rudolf Marek <r.marek@assembler.cz>");
 MODULE_DESCRIPTION("AMD K8 core temperature monitor");
 MODULE_LICENSE("GPL");
-
-module_init(k8temp_init)
-module_exit(k8temp_exit)
diff --git a/drivers/hwmon/ntc_thermistor.c b/drivers/hwmon/ntc_thermistor.c
index 9b382ec..6da9696 100644
--- a/drivers/hwmon/ntc_thermistor.c
+++ b/drivers/hwmon/ntc_thermistor.c
@@ -134,8 +134,7 @@
 	return div64_u64(dividend, divisor);
 }
 
-static unsigned int get_ohm_of_thermistor(struct ntc_data *data,
-		unsigned int uV)
+static int get_ohm_of_thermistor(struct ntc_data *data, unsigned int uV)
 {
 	struct ntc_thermistor_platform_data *pdata = data->pdata;
 	u64 mV = uV / 1000;
@@ -146,12 +145,12 @@
 
 	if (mV == 0) {
 		if (pdata->connect == NTC_CONNECTED_POSITIVE)
-			return UINT_MAX;
+			return INT_MAX;
 		return 0;
 	}
 	if (mV >= pmV)
 		return (pdata->connect == NTC_CONNECTED_POSITIVE) ?
-			0 : UINT_MAX;
+			0 : INT_MAX;
 
 	if (pdata->connect == NTC_CONNECTED_POSITIVE && puO == 0)
 		N = div64_u64_safe(pdO * (pmV - mV), mV);
@@ -163,113 +162,109 @@
 	else
 		N = div64_u64_safe(pdO * puO * mV, pdO * (pmV - mV) - puO * mV);
 
-	return (unsigned int) N;
+	if (N > INT_MAX)
+		N = INT_MAX;
+	return N;
 }
 
-static int lookup_comp(struct ntc_data *data,
-		unsigned int ohm, int *i_low, int *i_high)
+static void lookup_comp(struct ntc_data *data, unsigned int ohm,
+			int *i_low, int *i_high)
 {
-	int start, end, mid = -1;
+	int start, end, mid;
+
+	/*
+	 * Handle special cases: Resistance is higher than or equal to
+	 * resistance in first table entry, or resistance is lower or equal
+	 * to resistance in last table entry.
+	 * In these cases, return i_low == i_high, either pointing to the
+	 * beginning or to the end of the table depending on the condition.
+	 */
+	if (ohm >= data->comp[0].ohm) {
+		*i_low = 0;
+		*i_high = 0;
+		return;
+	}
+	if (ohm <= data->comp[data->n_comp - 1].ohm) {
+		*i_low = data->n_comp - 1;
+		*i_high = data->n_comp - 1;
+		return;
+	}
 
 	/* Do a binary search on compensation table */
 	start = 0;
 	end = data->n_comp;
-
-	while (end > start) {
+	while (start < end) {
 		mid = start + (end - start) / 2;
-		if (data->comp[mid].ohm < ohm)
+		/*
+		 * start <= mid < end
+		 * data->comp[start].ohm > ohm >= data->comp[end].ohm
+		 *
+		 * We could check for "ohm == data->comp[mid].ohm" here, but
+		 * that is a quite unlikely condition, and we would have to
+		 * check again after updating start. Check it at the end instead
+		 * for simplicity.
+		 */
+		if (ohm >= data->comp[mid].ohm) {
 			end = mid;
-		else if (data->comp[mid].ohm > ohm)
+		} else {
 			start = mid + 1;
-		else
-			break;
-	}
-
-	if (mid == 0) {
-		if (data->comp[mid].ohm > ohm) {
-			*i_high = mid;
-			*i_low = mid + 1;
-			return 0;
-		} else {
-			*i_low = mid;
-			*i_high = -1;
-			return -EINVAL;
+			/*
+			 * ohm >= data->comp[start].ohm might be true here,
+			 * since we set start to mid + 1. In that case, we are
+			 * done. We could keep going, but the condition is quite
+			 * likely to occur, so it is worth checking for it.
+			 */
+			if (ohm >= data->comp[start].ohm)
+				end = start;
 		}
+		/*
+		 * start <= end
+		 * data->comp[start].ohm >= ohm >= data->comp[end].ohm
+		 */
 	}
-	if (mid == (data->n_comp - 1)) {
-		if (data->comp[mid].ohm <= ohm) {
-			*i_low = mid;
-			*i_high = mid - 1;
-			return 0;
-		} else {
-			*i_low = -1;
-			*i_high = mid;
-			return -EINVAL;
-		}
-	}
-
-	if (data->comp[mid].ohm <= ohm) {
-		*i_low = mid;
-		*i_high = mid - 1;
-	} else {
-		*i_low = mid + 1;
-		*i_high = mid;
-	}
-
-	return 0;
+	/*
+	 * start == end
+	 * ohm >= data->comp[end].ohm
+	 */
+	*i_low = end;
+	if (ohm == data->comp[end].ohm)
+		*i_high = end;
+	else
+		*i_high = end - 1;
 }
 
-static int get_temp_mC(struct ntc_data *data, unsigned int ohm, int *temp)
+static int get_temp_mC(struct ntc_data *data, unsigned int ohm)
 {
 	int low, high;
-	int ret;
+	int temp;
 
-	ret = lookup_comp(data, ohm, &low, &high);
-	if (ret) {
+	lookup_comp(data, ohm, &low, &high);
+	if (low == high) {
 		/* Unable to use linear approximation */
-		if (low != -1)
-			*temp = data->comp[low].temp_C * 1000;
-		else if (high != -1)
-			*temp = data->comp[high].temp_C * 1000;
-		else
-			return ret;
+		temp = data->comp[low].temp_C * 1000;
 	} else {
-		*temp = data->comp[low].temp_C * 1000 +
+		temp = data->comp[low].temp_C * 1000 +
 			((data->comp[high].temp_C - data->comp[low].temp_C) *
 			 1000 * ((int)ohm - (int)data->comp[low].ohm)) /
 			((int)data->comp[high].ohm - (int)data->comp[low].ohm);
 	}
-
-	return 0;
+	return temp;
 }
 
-static int ntc_thermistor_read(struct ntc_data *data, int *temp)
+static int ntc_thermistor_get_ohm(struct ntc_data *data)
 {
-	int ret;
-	int read_ohm, read_uV;
-	unsigned int ohm = 0;
+	int read_uV;
 
-	if (data->pdata->read_ohm) {
-		read_ohm = data->pdata->read_ohm();
-		if (read_ohm < 0)
-			return read_ohm;
-		ohm = (unsigned int)read_ohm;
-	}
+	if (data->pdata->read_ohm)
+		return data->pdata->read_ohm();
 
 	if (data->pdata->read_uV) {
 		read_uV = data->pdata->read_uV();
 		if (read_uV < 0)
 			return read_uV;
-		ohm = get_ohm_of_thermistor(data, (unsigned int)read_uV);
+		return get_ohm_of_thermistor(data, read_uV);
 	}
-
-	ret = get_temp_mC(data, ohm, temp);
-	if (ret) {
-		dev_dbg(data->dev, "Sensor reading function not available.\n");
-		return ret;
-	}
-
-	return 0;
+	return -EINVAL;
 }
 
 static ssize_t ntc_show_name(struct device *dev,
@@ -290,12 +285,13 @@
 		struct device_attribute *attr, char *buf)
 {
 	struct ntc_data *data = dev_get_drvdata(dev);
-	int temp, ret;
+	int ohm;
 
-	ret = ntc_thermistor_read(data, &temp);
-	if (ret)
-		return ret;
-	return sprintf(buf, "%d\n", temp);
+	ohm = ntc_thermistor_get_ohm(data);
+	if (ohm < 0)
+		return ohm;
+
+	return sprintf(buf, "%d\n", get_temp_mC(data, ohm));
 }
 
 static SENSOR_DEVICE_ATTR(temp1_type, S_IRUGO, ntc_show_type, NULL, 0);
@@ -326,14 +322,14 @@
 
 	/* Either one of the two is required. */
 	if (!pdata->read_uV && !pdata->read_ohm) {
-		dev_err(&pdev->dev, "Both read_uV and read_ohm missing."
-				"Need either one of the two.\n");
+		dev_err(&pdev->dev,
+			"Both read_uV and read_ohm missing. Need either one of the two.\n");
 		return -EINVAL;
 	}
 
 	if (pdata->read_uV && pdata->read_ohm) {
-		dev_warn(&pdev->dev, "Only one of read_uV and read_ohm "
-				"is needed; ignoring read_uV.\n");
+		dev_warn(&pdev->dev,
+			 "Only one of read_uV and read_ohm is needed; ignoring read_uV.\n");
 		pdata->read_uV = NULL;
 	}
 
@@ -344,12 +340,12 @@
 				 NTC_CONNECTED_POSITIVE) ||
 				(pdata->connect != NTC_CONNECTED_POSITIVE &&
 				 pdata->connect != NTC_CONNECTED_GROUND))) {
-		dev_err(&pdev->dev, "Required data to use read_uV not "
-				"supplied.\n");
+		dev_err(&pdev->dev,
+			"Required data to use read_uV not supplied.\n");
 		return -EINVAL;
 	}
 
-	data = kzalloc(sizeof(struct ntc_data), GFP_KERNEL);
+	data = devm_kzalloc(&pdev->dev, sizeof(struct ntc_data), GFP_KERNEL);
 	if (!data)
 		return -ENOMEM;
 
@@ -370,8 +366,7 @@
 		dev_err(&pdev->dev, "Unknown device type: %lu(%s)\n",
 				pdev->id_entry->driver_data,
 				pdev->id_entry->name);
-		ret = -EINVAL;
-		goto err;
+		return -EINVAL;
 	}
 
 	platform_set_drvdata(pdev, data);
@@ -379,13 +374,13 @@
 	ret = sysfs_create_group(&data->dev->kobj, &ntc_attr_group);
 	if (ret) {
 		dev_err(data->dev, "unable to create sysfs files\n");
-		goto err;
+		return ret;
 	}
 
 	data->hwmon_dev = hwmon_device_register(data->dev);
-	if (IS_ERR_OR_NULL(data->hwmon_dev)) {
+	if (IS_ERR(data->hwmon_dev)) {
 		dev_err(data->dev, "unable to register as hwmon device.\n");
-		ret = -EINVAL;
+		ret = PTR_ERR(data->hwmon_dev);
 		goto err_after_sysfs;
 	}
 
@@ -395,8 +390,6 @@
 	return 0;
 err_after_sysfs:
 	sysfs_remove_group(&data->dev->kobj, &ntc_attr_group);
-err:
-	kfree(data);
 	return ret;
 }
 
@@ -408,8 +401,6 @@
 	sysfs_remove_group(&data->dev->kobj, &ntc_attr_group);
 	platform_set_drvdata(pdev, NULL);
 
-	kfree(data);
-
 	return 0;
 }
 
diff --git a/drivers/i2c/busses/i2c-powermac.c b/drivers/i2c/busses/i2c-powermac.c
index 7b397c6..31c47e1 100644
--- a/drivers/i2c/busses/i2c-powermac.c
+++ b/drivers/i2c/busses/i2c-powermac.c
@@ -227,6 +227,72 @@
 	return 0;
 }
 
+static void __devinit i2c_powermac_register_devices(struct i2c_adapter *adap,
+						    struct pmac_i2c_bus *bus)
+{
+	struct i2c_client *newdev;
+	struct device_node *node;
+
+	for_each_child_of_node(adap->dev.of_node, node) {
+		struct i2c_board_info info = {};
+		struct dev_archdata dev_ad = {};
+		const __be32 *reg;
+		char tmp[16];
+		u32 addr;
+		int len;
+
+		/* Get address & channel */
+		reg = of_get_property(node, "reg", &len);
+		if (!reg || (len < sizeof(int))) {
+			dev_err(&adap->dev, "i2c-powermac: invalid reg on %s\n",
+				node->full_name);
+			continue;
+		}
+		addr = be32_to_cpup(reg);
+
+		/* Multibus setup, check channel */
+		if (!pmac_i2c_match_adapter(node, adap))
+			continue;
+
+		dev_dbg(&adap->dev, "i2c-powermac: register %s\n",
+			node->full_name);
+
+		/* Make up a modalias. Note: we to _NOT_ want the standard
+		 * i2c drivers to match with any of our powermac stuff
+		 * unless they have been specifically modified to handle
+		 * it on a case by case basis. For example, for thermal
+		 * control, things like lm75 etc... shall match with their
+		 * corresponding windfarm drivers, _NOT_ the generic ones,
+		 * so we force a prefix of AAPL, onto the modalias to
+		 * make that happen
+		 */
+		if (of_modalias_node(node, tmp, sizeof(tmp)) < 0) {
+			dev_err(&adap->dev, "i2c-powermac: modalias failure"
+				" on %s\n", node->full_name);
+			continue;
+		}
+		snprintf(info.type, sizeof(info.type), "MAC,%s", tmp);
+
+		/* Fill out the rest of the info structure */
+		info.addr = (addr & 0xff) >> 1;
+		info.irq = irq_of_parse_and_map(node, 0);
+		info.of_node = of_node_get(node);
+		info.archdata = &dev_ad;
+
+		newdev = i2c_new_device(adap, &info);
+		if (!newdev) {
+			dev_err(&adap->dev, "i2c-powermac: Failure to register"
+				" %s\n", node->full_name);
+			of_node_put(node);
+			/* We do not dispose of the interrupt mapping on
+			 * purpose. It's not necessary (interrupt cannot be
+			 * re-used) and somebody else might have grabbed it
+			 * via direct DT lookup so let's not bother
+			 */
+			continue;
+		}
+	}
+}
 
 static int __devinit i2c_powermac_probe(struct platform_device *dev)
 {
@@ -272,6 +338,7 @@
 	adapter->algo = &i2c_powermac_algorithm;
 	i2c_set_adapdata(adapter, bus);
 	adapter->dev.parent = &dev->dev;
+	adapter->dev.of_node = dev->dev.of_node;
 	rc = i2c_add_adapter(adapter);
 	if (rc) {
 		printk(KERN_ERR "i2c-powermac: Adapter %s registration "
@@ -281,33 +348,10 @@
 
 	printk(KERN_INFO "PowerMac i2c bus %s registered\n", adapter->name);
 
-	if (!strncmp(basename, "uni-n", 5)) {
-		struct device_node *np;
-		const u32 *prop;
-		struct i2c_board_info info;
-
-		/* Instantiate I2C motion sensor if present */
-		np = of_find_node_by_name(NULL, "accelerometer");
-		if (np && of_device_is_compatible(np, "AAPL,accelerometer_1") &&
-		    (prop = of_get_property(np, "reg", NULL))) {
-			int i2c_bus;
-			const char *tmp_bus;
-
-			/* look for bus either using "reg" or by path */
-			tmp_bus = strstr(np->full_name, "/i2c-bus@");
-			if (tmp_bus)
-				i2c_bus = *(tmp_bus + 9) - '0';
-			else
-				i2c_bus = ((*prop) >> 8) & 0x0f;
-
-			if (pmac_i2c_get_channel(bus) == i2c_bus) {
-				memset(&info, 0, sizeof(struct i2c_board_info));
-				info.addr = ((*prop) & 0xff) >> 1;
-				strlcpy(info.type, "ams", I2C_NAME_SIZE);
-				i2c_new_device(adapter, &info);
-			}
-		}
-	}
+	/* Cannot use of_i2c_register_devices() due to Apple device-tree
+	 * funkyness
+	 */
+	i2c_powermac_register_devices(adapter, bus);
 
 	return rc;
 }
diff --git a/drivers/macintosh/Kconfig b/drivers/macintosh/Kconfig
index fa51af1..a555da6 100644
--- a/drivers/macintosh/Kconfig
+++ b/drivers/macintosh/Kconfig
@@ -204,11 +204,14 @@
 	  better fan behaviour by default, and some manual control.
 
 config THERM_PM72
-	tristate "Support for thermal management on PowerMac G5"
+	tristate "Support for thermal management on PowerMac G5 (AGP)"
 	depends on I2C && I2C_POWERMAC && PPC_PMAC64
+	default n
 	help
 	  This driver provides thermostat and fan control for the desktop
-	  G5 machines. 
+	  G5 machines.
+
+	  This is deprecated, use windfarm instead.
 
 config WINDFARM
 	tristate "New PowerMac thermal control infrastructure"
@@ -221,6 +224,22 @@
 	help
 	  This driver provides thermal control for the iMacG5
 
+config WINDFARM_PM72
+	tristate "Support for thermal management on PowerMac G5 (AGP)"
+	depends on WINDFARM && I2C && CPU_FREQ_PMAC64 && ADB_PMU
+	select I2C_POWERMAC
+	help
+	  This driver provides thermal control for the PowerMac G5
+	  "AGP" variants (PowerMac 7,2 and 7,3)
+
+config WINDFARM_RM31
+	tristate "Support for thermal management on Xserve G5"
+	depends on WINDFARM && I2C && CPU_FREQ_PMAC64 && ADB_PMU
+	select I2C_POWERMAC
+	help
+	  This driver provides thermal control for the Xserve G5
+	  (RackMac3,1)
+
 config WINDFARM_PM91
 	tristate "Support for thermal management on PowerMac9,1"
 	depends on WINDFARM && I2C && CPU_FREQ_PMAC64 && PMAC_SMU
diff --git a/drivers/macintosh/Makefile b/drivers/macintosh/Makefile
index 6652a6e..6753b65 100644
--- a/drivers/macintosh/Makefile
+++ b/drivers/macintosh/Makefile
@@ -29,6 +29,20 @@
 obj-$(CONFIG_THERM_WINDTUNNEL)	+= therm_windtunnel.o
 obj-$(CONFIG_THERM_ADT746X)	+= therm_adt746x.o
 obj-$(CONFIG_WINDFARM)	        += windfarm_core.o
+obj-$(CONFIG_WINDFARM_PM72)     += windfarm_fcu_controls.o \
+				   windfarm_ad7417_sensor.o \
+				   windfarm_lm75_sensor.o \
+				   windfarm_max6690_sensor.o \
+				   windfarm_pid.o \
+				   windfarm_cpufreq_clamp.o \
+				   windfarm_pm72.o
+obj-$(CONFIG_WINDFARM_RM31)     += windfarm_fcu_controls.o \
+				   windfarm_ad7417_sensor.o \
+				   windfarm_lm75_sensor.o \
+				   windfarm_lm87_sensor.o \
+				   windfarm_pid.o \
+				   windfarm_cpufreq_clamp.o \
+				   windfarm_rm31.o
 obj-$(CONFIG_WINDFARM_PM81)     += windfarm_smu_controls.o \
 				   windfarm_smu_sensors.o \
 				   windfarm_lm75_sensor.o windfarm_pid.o \
diff --git a/drivers/macintosh/ams/ams-i2c.c b/drivers/macintosh/ams/ams-i2c.c
index abeecd2..978eda8 100644
--- a/drivers/macintosh/ams/ams-i2c.c
+++ b/drivers/macintosh/ams/ams-i2c.c
@@ -65,7 +65,7 @@
 static int ams_i2c_remove(struct i2c_client *client);
 
 static const struct i2c_device_id ams_id[] = {
-	{ "ams", 0 },
+	{ "MAC,accelerometer_1", 0 },
 	{ }
 };
 MODULE_DEVICE_TABLE(i2c, ams_id);
diff --git a/drivers/macintosh/therm_adt746x.c b/drivers/macintosh/therm_adt746x.c
index fc71723c..f433521 100644
--- a/drivers/macintosh/therm_adt746x.c
+++ b/drivers/macintosh/therm_adt746x.c
@@ -47,7 +47,7 @@
 
 static u8 default_limits_local[3] = {70, 50, 70};    /* local, sensor1, sensor2 */
 static u8 default_limits_chip[3] = {80, 65, 80};    /* local, sensor1, sensor2 */
-static const char *sensor_location[3];
+static const char *sensor_location[3] = { "?", "?", "?" };
 
 static int limit_adjust;
 static int fan_speed = -1;
@@ -79,18 +79,16 @@
 	int			last_speed[2];
 	int			last_var[2];
 	int			pwm_inv[2];
+	struct task_struct	*thread;
+	struct platform_device	*pdev;
+	enum {
+		ADT7460,
+		ADT7467
+	}			type;
 };
 
-static enum {ADT7460, ADT7467} therm_type;
-static int therm_bus, therm_address;
-static struct platform_device * of_dev;
-static struct thermostat* thermostat;
-static struct task_struct *thread_therm = NULL;
-
 static void write_both_fan_speed(struct thermostat *th, int speed);
 static void write_fan_speed(struct thermostat *th, int speed, int fan);
-static void thermostat_create_files(void);
-static void thermostat_remove_files(void);
 
 static int
 write_reg(struct thermostat* th, int reg, u8 data)
@@ -126,66 +124,6 @@
 	return data;
 }
 
-static struct i2c_driver thermostat_driver;
-
-static int
-attach_thermostat(struct i2c_adapter *adapter)
-{
-	unsigned long bus_no;
-	struct i2c_board_info info;
-	struct i2c_client *client;
-
-	if (strncmp(adapter->name, "uni-n", 5))
-		return -ENODEV;
-	bus_no = simple_strtoul(adapter->name + 6, NULL, 10);
-	if (bus_no != therm_bus)
-		return -ENODEV;
-
-	memset(&info, 0, sizeof(struct i2c_board_info));
-	strlcpy(info.type, "therm_adt746x", I2C_NAME_SIZE);
-	info.addr = therm_address;
-	client = i2c_new_device(adapter, &info);
-	if (!client)
-		return -ENODEV;
-
-	/*
-	 * Let i2c-core delete that device on driver removal.
-	 * This is safe because i2c-core holds the core_lock mutex for us.
-	 */
-	list_add_tail(&client->detected, &thermostat_driver.clients);
-	return 0;
-}
-
-static int
-remove_thermostat(struct i2c_client *client)
-{
-	struct thermostat *th = i2c_get_clientdata(client);
-	int i;
-	
-	thermostat_remove_files();
-
-	if (thread_therm != NULL) {
-		kthread_stop(thread_therm);
-	}
-
-	printk(KERN_INFO "adt746x: Putting max temperatures back from "
-			 "%d, %d, %d to %d, %d, %d\n",
-		th->limits[0], th->limits[1], th->limits[2],
-		th->initial_limits[0], th->initial_limits[1],
-		th->initial_limits[2]);
-
-	for (i = 0; i < 3; i++)
-		write_reg(th, LIMIT_REG[i], th->initial_limits[i]);
-
-	write_both_fan_speed(th, -1);
-
-	thermostat = NULL;
-
-	kfree(th);
-
-	return 0;
-}
-
 static int read_fan_speed(struct thermostat *th, u8 addr)
 {
 	u8 tmp[2];
@@ -203,7 +141,7 @@
 static void write_both_fan_speed(struct thermostat *th, int speed)
 {
 	write_fan_speed(th, speed, 0);
-	if (therm_type == ADT7460)
+	if (th->type == ADT7460)
 		write_fan_speed(th, speed, 1);
 }
 
@@ -216,7 +154,7 @@
 	else if (speed < -1) 
 		speed = 0;
 	
-	if (therm_type == ADT7467 && fan == 1)
+	if (th->type == ADT7467 && fan == 1)
 		return;
 	
 	if (th->last_speed[fan] != speed) {
@@ -239,7 +177,7 @@
 		write_reg(th, FAN_SPD_SET[fan], speed);
 	} else {
 		/* back to automatic */
-		if(therm_type == ADT7460) {
+		if(th->type == ADT7460) {
 			manual = read_reg(th,
 				MANUAL_MODE[fan]) & (~MANUAL_MASK);
 			manual &= ~INVERT_MASK;
@@ -293,7 +231,7 @@
 	/* we don't care about local sensor, so we start at sensor 1 */
 	for (i = 1; i < 3; i++) {
 		int started = 0;
-		int fan_number = (therm_type == ADT7460 && i == 2);
+		int fan_number = (th->type == ADT7460 && i == 2);
 		int var = th->temps[i] - th->limits[i];
 
 		if (var > -1) {
@@ -370,118 +308,24 @@
 
 static void set_limit(struct thermostat *th, int i)
 {
-		/* Set sensor1 limit higher to avoid powerdowns */
-		th->limits[i] = default_limits_chip[i] + limit_adjust;
-		write_reg(th, LIMIT_REG[i], th->limits[i]);
+	/* Set sensor1 limit higher to avoid powerdowns */
+	th->limits[i] = default_limits_chip[i] + limit_adjust;
+	write_reg(th, LIMIT_REG[i], th->limits[i]);
 		
-		/* set our limits to normal */
-		th->limits[i] = default_limits_local[i] + limit_adjust;
+	/* set our limits to normal */
+	th->limits[i] = default_limits_local[i] + limit_adjust;
 }
 
-static int probe_thermostat(struct i2c_client *client,
-			    const struct i2c_device_id *id)
-{
-	struct thermostat* th;
-	int rc;
-	int i;
-
-	if (thermostat)
-		return 0;
-
-	th = kzalloc(sizeof(struct thermostat), GFP_KERNEL);
-	if (!th)
-		return -ENOMEM;
-
-	i2c_set_clientdata(client, th);
-	th->clt = client;
-
-	rc = read_reg(th, CONFIG_REG);
-	if (rc < 0) {
-		dev_err(&client->dev, "Thermostat failed to read config!\n");
-		kfree(th);
-		return -ENODEV;
-	}
-
-	/* force manual control to start the fan quieter */
-	if (fan_speed == -1)
-		fan_speed = 64;
-	
-	if(therm_type == ADT7460) {
-		printk(KERN_INFO "adt746x: ADT7460 initializing\n");
-		/* The 7460 needs to be started explicitly */
-		write_reg(th, CONFIG_REG, 1);
-	} else
-		printk(KERN_INFO "adt746x: ADT7467 initializing\n");
-
-	for (i = 0; i < 3; i++) {
-		th->initial_limits[i] = read_reg(th, LIMIT_REG[i]);
-		set_limit(th, i);
-	}
-
-	printk(KERN_INFO "adt746x: Lowering max temperatures from %d, %d, %d"
-			 " to %d, %d, %d\n",
-			 th->initial_limits[0], th->initial_limits[1],
-			 th->initial_limits[2], th->limits[0], th->limits[1],
-			 th->limits[2]);
-
-	thermostat = th;
-
-	/* record invert bit status because fw can corrupt it after suspend */
-	th->pwm_inv[0] = read_reg(th, MANUAL_MODE[0]) & INVERT_MASK;
-	th->pwm_inv[1] = read_reg(th, MANUAL_MODE[1]) & INVERT_MASK;
-
-	/* be sure to really write fan speed the first time */
-	th->last_speed[0] = -2;
-	th->last_speed[1] = -2;
-	th->last_var[0] = -80;
-	th->last_var[1] = -80;
-
-	if (fan_speed != -1) {
-		/* manual mode, stop fans */
-		write_both_fan_speed(th, 0);
-	} else {
-		/* automatic mode */
-		write_both_fan_speed(th, -1);
-	}
-	
-	thread_therm = kthread_run(monitor_task, th, "kfand");
-
-	if (thread_therm == ERR_PTR(-ENOMEM)) {
-		printk(KERN_INFO "adt746x: Kthread creation failed\n");
-		thread_therm = NULL;
-		return -ENOMEM;
-	}
-
-	thermostat_create_files();
-
-	return 0;
-}
-
-static const struct i2c_device_id therm_adt746x_id[] = {
-	{ "therm_adt746x", 0 },
-	{ }
-};
-
-static struct i2c_driver thermostat_driver = {
-	.driver = {
-		.name	= "therm_adt746x",
-	},
-	.attach_adapter	= attach_thermostat,
-	.probe = probe_thermostat,
-	.remove = remove_thermostat,
-	.id_table = therm_adt746x_id,
-};
-
-/* 
- * Now, unfortunately, sysfs doesn't give us a nice void * we could
- * pass around to the attribute functions, so we don't really have
- * choice but implement a bunch of them...
- *
- * FIXME, it does now...
- */
 #define BUILD_SHOW_FUNC_INT(name, data)				\
 static ssize_t show_##name(struct device *dev, struct device_attribute *attr, char *buf)	\
 {								\
+	struct thermostat *th = dev_get_drvdata(dev);		\
+	return sprintf(buf, "%d\n", data);			\
+}
+
+#define BUILD_SHOW_FUNC_INT_LITE(name, data)				\
+static ssize_t show_##name(struct device *dev, struct device_attribute *attr, char *buf)	\
+{								\
 	return sprintf(buf, "%d\n", data);			\
 }
 
@@ -494,22 +338,24 @@
 #define BUILD_SHOW_FUNC_FAN(name, data)				\
 static ssize_t show_##name(struct device *dev, struct device_attribute *attr, char *buf)       \
 {								\
+	struct thermostat *th = dev_get_drvdata(dev);		\
 	return sprintf(buf, "%d (%d rpm)\n", 			\
-		thermostat->last_speed[data],			\
-		read_fan_speed(thermostat, FAN_SPEED[data])	\
+		th->last_speed[data],				\
+		read_fan_speed(th, FAN_SPEED[data])		\
 		);						\
 }
 
 #define BUILD_STORE_FUNC_DEG(name, data)			\
 static ssize_t store_##name(struct device *dev, struct device_attribute *attr, const char *buf, size_t n) \
 {								\
+	struct thermostat *th = dev_get_drvdata(dev);		\
 	int val;						\
 	int i;							\
 	val = simple_strtol(buf, NULL, 10);			\
 	printk(KERN_INFO "Adjusting limits by %d degrees\n", val);	\
 	limit_adjust = val;					\
 	for (i=0; i < 3; i++)					\
-		set_limit(thermostat, i);			\
+		set_limit(th, i);				\
 	return n;						\
 }
 
@@ -525,20 +371,21 @@
 	return n;						\
 }
 
-BUILD_SHOW_FUNC_INT(sensor1_temperature,	 (read_reg(thermostat, TEMP_REG[1])))
-BUILD_SHOW_FUNC_INT(sensor2_temperature,	 (read_reg(thermostat, TEMP_REG[2])))
-BUILD_SHOW_FUNC_INT(sensor1_limit,		 thermostat->limits[1])
-BUILD_SHOW_FUNC_INT(sensor2_limit,		 thermostat->limits[2])
+BUILD_SHOW_FUNC_INT(sensor1_temperature,	 (read_reg(th, TEMP_REG[1])))
+BUILD_SHOW_FUNC_INT(sensor2_temperature,	 (read_reg(th, TEMP_REG[2])))
+BUILD_SHOW_FUNC_INT(sensor1_limit,		 th->limits[1])
+BUILD_SHOW_FUNC_INT(sensor2_limit,		 th->limits[2])
 BUILD_SHOW_FUNC_STR(sensor1_location,		 sensor_location[1])
 BUILD_SHOW_FUNC_STR(sensor2_location,		 sensor_location[2])
 
-BUILD_SHOW_FUNC_INT(specified_fan_speed, fan_speed)
+BUILD_SHOW_FUNC_INT_LITE(specified_fan_speed, fan_speed)
+BUILD_STORE_FUNC_INT(specified_fan_speed,fan_speed)
+
 BUILD_SHOW_FUNC_FAN(sensor1_fan_speed,	 0)
 BUILD_SHOW_FUNC_FAN(sensor2_fan_speed,	 1)
 
-BUILD_STORE_FUNC_INT(specified_fan_speed,fan_speed)
-BUILD_SHOW_FUNC_INT(limit_adjust,	 limit_adjust)
-BUILD_STORE_FUNC_DEG(limit_adjust,	 thermostat)
+BUILD_SHOW_FUNC_INT_LITE(limit_adjust,	 limit_adjust)
+BUILD_STORE_FUNC_DEG(limit_adjust,	 th)
 		
 static DEVICE_ATTR(sensor1_temperature,	S_IRUGO,
 		   show_sensor1_temperature,NULL);
@@ -564,53 +411,77 @@
 static DEVICE_ATTR(limit_adjust,	S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH,
 		   show_limit_adjust,	store_limit_adjust);
 
-
-static int __init
-thermostat_init(void)
+static void thermostat_create_files(struct thermostat *th)
 {
-	struct device_node* np;
-	const u32 *prop;
-	int i = 0, offset = 0;
+	struct device_node *np = th->clt->dev.of_node;
+	struct device *dev;
+	int err;
 
-	np = of_find_node_by_name(NULL, "fan");
+	/* To maintain ABI compatibility with userspace, create
+	 * the old style platform driver and attach the attributes
+	 * to it here
+	 */
+	th->pdev = of_platform_device_create(np, "temperatures", NULL);
+	if (!th->pdev)
+		return;
+	dev = &th->pdev->dev;
+	dev_set_drvdata(dev, th);
+	err = device_create_file(dev, &dev_attr_sensor1_temperature);
+	err |= device_create_file(dev, &dev_attr_sensor2_temperature);
+	err |= device_create_file(dev, &dev_attr_sensor1_limit);
+	err |= device_create_file(dev, &dev_attr_sensor2_limit);
+	err |= device_create_file(dev, &dev_attr_sensor1_location);
+	err |= device_create_file(dev, &dev_attr_sensor2_location);
+	err |= device_create_file(dev, &dev_attr_limit_adjust);
+	err |= device_create_file(dev, &dev_attr_specified_fan_speed);
+	err |= device_create_file(dev, &dev_attr_sensor1_fan_speed);
+	if(th->type == ADT7460)
+		err |= device_create_file(dev, &dev_attr_sensor2_fan_speed);
+	if (err)
+		printk(KERN_WARNING
+			"Failed to create temperature attribute file(s).\n");
+}
+
+static void thermostat_remove_files(struct thermostat *th)
+{
+	struct device *dev;
+
+	if (!th->pdev)
+		return;
+	dev = &th->pdev->dev;
+	device_remove_file(dev, &dev_attr_sensor1_temperature);
+	device_remove_file(dev, &dev_attr_sensor2_temperature);
+	device_remove_file(dev, &dev_attr_sensor1_limit);
+	device_remove_file(dev, &dev_attr_sensor2_limit);
+	device_remove_file(dev, &dev_attr_sensor1_location);
+	device_remove_file(dev, &dev_attr_sensor2_location);
+	device_remove_file(dev, &dev_attr_limit_adjust);
+	device_remove_file(dev, &dev_attr_specified_fan_speed);
+	device_remove_file(dev, &dev_attr_sensor1_fan_speed);	
+	if (th->type == ADT7460)
+		device_remove_file(dev, &dev_attr_sensor2_fan_speed);
+	of_device_unregister(th->pdev);
+
+}
+
+static int probe_thermostat(struct i2c_client *client,
+			    const struct i2c_device_id *id)
+{
+	struct device_node *np = client->dev.of_node;
+	struct thermostat* th;
+	const __be32 *prop;
+	int i, rc, vers, offset = 0;
+
 	if (!np)
-		return -ENODEV;
-	if (of_device_is_compatible(np, "adt7460"))
-		therm_type = ADT7460;
-	else if (of_device_is_compatible(np, "adt7467"))
-		therm_type = ADT7467;
-	else {
-		of_node_put(np);
-		return -ENODEV;
-	}
-
+		return -ENXIO;
 	prop = of_get_property(np, "hwsensor-params-version", NULL);
-	printk(KERN_INFO "adt746x: version %d (%ssupported)\n", *prop,
-			 (*prop == 1)?"":"un");
-	if (*prop != 1) {
-		of_node_put(np);
-		return -ENODEV;
-	}
-
-	prop = of_get_property(np, "reg", NULL);
-	if (!prop) {
-		of_node_put(np);
-		return -ENODEV;
-	}
-
-	/* look for bus either by path or using "reg" */
-	if (strstr(np->full_name, "/i2c-bus@") != NULL) {
-		const char *tmp_bus = (strstr(np->full_name, "/i2c-bus@") + 9);
-		therm_bus = tmp_bus[0]-'0';
-	} else {
-		therm_bus = ((*prop) >> 8) & 0x0f;
-	}
-
-	therm_address = ((*prop) & 0xff) >> 1;
-
-	printk(KERN_INFO "adt746x: Thermostat bus: %d, address: 0x%02x, "
-			 "limit_adjust: %d, fan_speed: %d\n",
-			 therm_bus, therm_address, limit_adjust, fan_speed);
+	if (!prop)
+		return -ENXIO;
+	vers = be32_to_cpup(prop);
+	printk(KERN_INFO "adt746x: version %d (%ssupported)\n",
+	       vers, vers == 1 ? "" : "un");
+	if (vers != 1)
+		return -ENXIO;
 
 	if (of_get_property(np, "hwsensor-location", NULL)) {
 		for (i = 0; i < 3; i++) {
@@ -623,20 +494,119 @@
 			printk(KERN_INFO "sensor %d: %s\n", i, sensor_location[i]);
 			offset += strlen(sensor_location[i]) + 1;
 		}
-	} else {
-		sensor_location[0] = "?";
-		sensor_location[1] = "?";
-		sensor_location[2] = "?";
 	}
 
-	of_dev = of_platform_device_create(np, "temperatures", NULL);
-	of_node_put(np);
+	th = kzalloc(sizeof(struct thermostat), GFP_KERNEL);
+	if (!th)
+		return -ENOMEM;
 
-	if (of_dev == NULL) {
-		printk(KERN_ERR "Can't register temperatures device !\n");
+	i2c_set_clientdata(client, th);
+	th->clt = client;
+	th->type = id->driver_data;
+
+	rc = read_reg(th, CONFIG_REG);
+	if (rc < 0) {
+		dev_err(&client->dev, "Thermostat failed to read config!\n");
+		kfree(th);
 		return -ENODEV;
 	}
 
+	/* force manual control to start the fan quieter */
+	if (fan_speed == -1)
+		fan_speed = 64;
+	
+	if (th->type == ADT7460) {
+		printk(KERN_INFO "adt746x: ADT7460 initializing\n");
+		/* The 7460 needs to be started explicitly */
+		write_reg(th, CONFIG_REG, 1);
+	} else
+		printk(KERN_INFO "adt746x: ADT7467 initializing\n");
+
+	for (i = 0; i < 3; i++) {
+		th->initial_limits[i] = read_reg(th, LIMIT_REG[i]);
+		set_limit(th, i);
+	}
+
+	printk(KERN_INFO "adt746x: Lowering max temperatures from %d, %d, %d"
+			 " to %d, %d, %d\n",
+			 th->initial_limits[0], th->initial_limits[1],
+			 th->initial_limits[2], th->limits[0], th->limits[1],
+			 th->limits[2]);
+
+	/* record invert bit status because fw can corrupt it after suspend */
+	th->pwm_inv[0] = read_reg(th, MANUAL_MODE[0]) & INVERT_MASK;
+	th->pwm_inv[1] = read_reg(th, MANUAL_MODE[1]) & INVERT_MASK;
+
+	/* be sure to really write fan speed the first time */
+	th->last_speed[0] = -2;
+	th->last_speed[1] = -2;
+	th->last_var[0] = -80;
+	th->last_var[1] = -80;
+
+	if (fan_speed != -1) {
+		/* manual mode, stop fans */
+		write_both_fan_speed(th, 0);
+	} else {
+		/* automatic mode */
+		write_both_fan_speed(th, -1);
+	}
+	
+	th->thread = kthread_run(monitor_task, th, "kfand");
+	if (th->thread == ERR_PTR(-ENOMEM)) {
+		printk(KERN_INFO "adt746x: Kthread creation failed\n");
+		th->thread = NULL;
+		return -ENOMEM;
+	}
+
+	thermostat_create_files(th);
+
+	return 0;
+}
+
+static int remove_thermostat(struct i2c_client *client)
+{
+	struct thermostat *th = i2c_get_clientdata(client);
+	int i;
+	
+	thermostat_remove_files(th);
+
+	if (th->thread != NULL)
+		kthread_stop(th->thread);
+
+	printk(KERN_INFO "adt746x: Putting max temperatures back from "
+			 "%d, %d, %d to %d, %d, %d\n",
+		th->limits[0], th->limits[1], th->limits[2],
+		th->initial_limits[0], th->initial_limits[1],
+		th->initial_limits[2]);
+
+	for (i = 0; i < 3; i++)
+		write_reg(th, LIMIT_REG[i], th->initial_limits[i]);
+
+	write_both_fan_speed(th, -1);
+
+	kfree(th);
+
+	return 0;
+}
+
+static const struct i2c_device_id therm_adt746x_id[] = {
+	{ "MAC,adt7460", ADT7460 },
+	{ "MAC,adt7467", ADT7467 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, therm_adt746x_id);
+
+static struct i2c_driver thermostat_driver = {
+	.driver = {
+		.name	= "therm_adt746x",
+	},
+	.probe = probe_thermostat,
+	.remove = remove_thermostat,
+	.id_table = therm_adt746x_id,
+};
+
+static int __init thermostat_init(void)
+{
 #ifndef CONFIG_I2C_POWERMAC
 	request_module("i2c-powermac");
 #endif
@@ -644,51 +614,9 @@
 	return i2c_add_driver(&thermostat_driver);
 }
 
-static void thermostat_create_files(void)
-{
-	int err;
-
-	err = device_create_file(&of_dev->dev, &dev_attr_sensor1_temperature);
-	err |= device_create_file(&of_dev->dev, &dev_attr_sensor2_temperature);
-	err |= device_create_file(&of_dev->dev, &dev_attr_sensor1_limit);
-	err |= device_create_file(&of_dev->dev, &dev_attr_sensor2_limit);
-	err |= device_create_file(&of_dev->dev, &dev_attr_sensor1_location);
-	err |= device_create_file(&of_dev->dev, &dev_attr_sensor2_location);
-	err |= device_create_file(&of_dev->dev, &dev_attr_limit_adjust);
-	err |= device_create_file(&of_dev->dev, &dev_attr_specified_fan_speed);
-	err |= device_create_file(&of_dev->dev, &dev_attr_sensor1_fan_speed);
-	if(therm_type == ADT7460)
-		err |= device_create_file(&of_dev->dev, &dev_attr_sensor2_fan_speed);
-	if (err)
-		printk(KERN_WARNING
-			"Failed to create temperature attribute file(s).\n");
-}
-
-static void thermostat_remove_files(void)
-{
-	if (of_dev) {
-		device_remove_file(&of_dev->dev, &dev_attr_sensor1_temperature);
-		device_remove_file(&of_dev->dev, &dev_attr_sensor2_temperature);
-		device_remove_file(&of_dev->dev, &dev_attr_sensor1_limit);
-		device_remove_file(&of_dev->dev, &dev_attr_sensor2_limit);
-		device_remove_file(&of_dev->dev, &dev_attr_sensor1_location);
-		device_remove_file(&of_dev->dev, &dev_attr_sensor2_location);
-		device_remove_file(&of_dev->dev, &dev_attr_limit_adjust);
-		device_remove_file(&of_dev->dev, &dev_attr_specified_fan_speed);
-		device_remove_file(&of_dev->dev, &dev_attr_sensor1_fan_speed);
-
-		if(therm_type == ADT7460)
-			device_remove_file(&of_dev->dev,
-					   &dev_attr_sensor2_fan_speed);
-
-	}
-}
-
-static void __exit
-thermostat_exit(void)
+static void __exit thermostat_exit(void)
 {
 	i2c_del_driver(&thermostat_driver);
-	of_device_unregister(of_dev);
 }
 
 module_init(thermostat_init);
diff --git a/drivers/macintosh/windfarm.h b/drivers/macintosh/windfarm.h
index 7a2482c..028cdac 100644
--- a/drivers/macintosh/windfarm.h
+++ b/drivers/macintosh/windfarm.h
@@ -17,7 +17,7 @@
 #include <linux/device.h>
 
 /* Display a 16.16 fixed point value */
-#define FIX32TOPRINT(f)	((f) >> 16),((((f) & 0xffff) * 1000) >> 16)
+#define FIX32TOPRINT(f)	(((s32)(f)) >> 16),(((((s32)(f)) & 0xffff) * 1000) >> 16)
 
 /*
  * Control objects
@@ -35,12 +35,13 @@
 };
 
 struct wf_control {
-	struct list_head	link;
-	struct wf_control_ops	*ops;
-	char			*name;
-	int			type;
-	struct kref		ref;
-	struct device_attribute	attr;
+	struct list_head		link;
+	const struct wf_control_ops	*ops;
+	const char			*name;
+	int				type;
+	struct kref			ref;
+	struct device_attribute		attr;
+	void				*priv;
 };
 
 #define WF_CONTROL_TYPE_GENERIC		0
@@ -72,6 +73,26 @@
 	return ct->ops->set_value(ct, vmin);
 }
 
+static inline int wf_control_set(struct wf_control *ct, s32 val)
+{
+	return ct->ops->set_value(ct, val);
+}
+
+static inline int wf_control_get(struct wf_control *ct, s32 *val)
+{
+	return ct->ops->get_value(ct, val);
+}
+
+static inline s32 wf_control_get_min(struct wf_control *ct)
+{
+	return ct->ops->get_min(ct);
+}
+
+static inline s32 wf_control_get_max(struct wf_control *ct)
+{
+	return ct->ops->get_max(ct);
+}
+
 /*
  * Sensor objects
  */
@@ -85,11 +106,12 @@
 };
 
 struct wf_sensor {
-	struct list_head	link;
-	struct wf_sensor_ops	*ops;
-	char			*name;
-	struct kref		ref;
-	struct device_attribute	attr;
+	struct list_head		link;
+	const struct wf_sensor_ops	*ops;
+	const char			*name;
+	struct kref			ref;
+	struct device_attribute		attr;
+	void				*priv;
 };
 
 /* Same lifetime rules as controls */
@@ -99,6 +121,11 @@
 extern int wf_get_sensor(struct wf_sensor *sr);
 extern void wf_put_sensor(struct wf_sensor *sr);
 
+static inline int wf_sensor_get(struct wf_sensor *sr, s32 *val)
+{
+	return sr->ops->get_value(sr, val);
+}
+
 /* For use by clients. Note that we are a bit racy here since
  * notifier_block doesn't have a module owner field. I may fix
  * it one day ...
diff --git a/drivers/macintosh/windfarm_ad7417_sensor.c b/drivers/macintosh/windfarm_ad7417_sensor.c
new file mode 100644
index 0000000..ac3f243
--- /dev/null
+++ b/drivers/macintosh/windfarm_ad7417_sensor.c
@@ -0,0 +1,347 @@
+/*
+ * Windfarm PowerMac thermal control. AD7417 sensors
+ *
+ * Copyright 2012 Benjamin Herrenschmidt, IBM Corp.
+ *
+ * Released under the term of the GNU GPL v2.
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/wait.h>
+#include <linux/i2c.h>
+#include <asm/prom.h>
+#include <asm/machdep.h>
+#include <asm/io.h>
+#include <asm/sections.h>
+
+#include "windfarm.h"
+#include "windfarm_mpu.h"
+
+#define VERSION "1.0"
+
+struct wf_ad7417_priv {
+	struct kref		ref;
+	struct i2c_client	*i2c;
+	u8			config;
+	u8			cpu;
+	const struct mpu_data	*mpu;
+	struct wf_sensor	sensors[5];
+	struct mutex		lock;
+};
+
+static int wf_ad7417_temp_get(struct wf_sensor *sr, s32 *value)
+{
+	struct wf_ad7417_priv *pv = sr->priv;
+	u8 buf[2];
+	s16 raw;
+	int rc;
+
+	*value = 0;
+	mutex_lock(&pv->lock);
+
+	/* Read temp register */
+	buf[0] = 0;
+	rc = i2c_master_send(pv->i2c, buf, 1);
+	if (rc < 0)
+		goto error;
+	rc = i2c_master_recv(pv->i2c, buf, 2);
+	if (rc < 0)
+		goto error;
+
+	/* Read a a 16-bit signed value */
+	raw = be16_to_cpup((__le16 *)buf);
+
+	/* Convert 8.8-bit to 16.16 fixed point */
+	*value = ((s32)raw) << 8;
+
+	mutex_unlock(&pv->lock);
+	return 0;
+
+error:
+	mutex_unlock(&pv->lock);
+	return -1;
+}
+
+/*
+ * Scaling factors for the AD7417 ADC converters (except
+ * for the CPU diode which is obtained from the EEPROM).
+ * Those values are obtained from the property list of
+ * the darwin driver
+ */
+#define ADC_12V_CURRENT_SCALE	0x0320	/* _AD2 */
+#define ADC_CPU_VOLTAGE_SCALE	0x00a0	/* _AD3 */
+#define ADC_CPU_CURRENT_SCALE	0x1f40	/* _AD4 */
+
+static void wf_ad7417_adc_convert(struct wf_ad7417_priv *pv,
+				  int chan, s32 raw, s32 *value)
+{
+	switch(chan) {
+	case 1: /* Diode */
+		*value = (raw * (s32)pv->mpu->mdiode +
+			((s32)pv->mpu->bdiode << 12)) >> 2;
+		break;
+	case 2: /* 12v current */
+		*value = raw * ADC_12V_CURRENT_SCALE;
+		break;
+	case 3: /* core voltage */
+		*value = raw * ADC_CPU_VOLTAGE_SCALE;
+		break;
+	case 4: /* core current */
+		*value = raw * ADC_CPU_CURRENT_SCALE;
+		break;
+	}
+}
+
+static int wf_ad7417_adc_get(struct wf_sensor *sr, s32 *value)
+{
+	struct wf_ad7417_priv *pv = sr->priv;
+	int chan = sr - pv->sensors;
+	int i, rc;
+	u8 buf[2];
+	u16 raw;
+
+	*value = 0;
+	mutex_lock(&pv->lock);
+	for (i = 0; i < 10; i++) {
+		/* Set channel */
+		buf[0] = 1;
+		buf[1] = (pv->config & 0x1f) | (chan << 5);
+		rc = i2c_master_send(pv->i2c, buf, 2);
+		if (rc < 0)
+			goto error;
+
+		/* Wait for conversion */
+		msleep(1);
+
+		/* Switch to data register */
+		buf[0] = 4;
+		rc = i2c_master_send(pv->i2c, buf, 1);
+		if (rc < 0)
+			goto error;
+
+		/* Read result */
+		rc = i2c_master_recv(pv->i2c, buf, 2);
+		if (rc < 0)
+			goto error;
+
+		/* Read a a 16-bit signed value */
+		raw = be16_to_cpup((__le16 *)buf) >> 6;
+		wf_ad7417_adc_convert(pv, chan, raw, value);
+
+		dev_vdbg(&pv->i2c->dev, "ADC chan %d [%s]"
+			 " raw value: 0x%x, conv to: 0x%08x\n",
+			 chan, sr->name, raw, *value);
+
+		mutex_unlock(&pv->lock);
+		return 0;
+
+	error:
+		dev_dbg(&pv->i2c->dev,
+			  "Error reading ADC, try %d...\n", i);
+		if (i < 9)
+			msleep(10);
+	}
+	mutex_unlock(&pv->lock);
+	return -1;
+}
+
+static void wf_ad7417_release(struct kref *ref)
+{
+	struct wf_ad7417_priv *pv = container_of(ref,
+						 struct wf_ad7417_priv, ref);
+	kfree(pv);
+}
+
+static void wf_ad7417_sensor_release(struct wf_sensor *sr)
+{
+	struct wf_ad7417_priv *pv = sr->priv;
+
+	kfree(sr->name);
+	kref_put(&pv->ref, wf_ad7417_release);
+}
+
+static const struct wf_sensor_ops wf_ad7417_temp_ops = {
+	.get_value	= wf_ad7417_temp_get,
+	.release	= wf_ad7417_sensor_release,
+	.owner		= THIS_MODULE,
+};
+
+static const struct wf_sensor_ops wf_ad7417_adc_ops = {
+	.get_value	= wf_ad7417_adc_get,
+	.release	= wf_ad7417_sensor_release,
+	.owner		= THIS_MODULE,
+};
+
+static void __devinit wf_ad7417_add_sensor(struct wf_ad7417_priv *pv,
+					   int index, const char *name,
+					   const struct wf_sensor_ops *ops)
+{
+	pv->sensors[index].name = kasprintf(GFP_KERNEL, "%s-%d", name, pv->cpu);
+	pv->sensors[index].priv = pv;
+	pv->sensors[index].ops = ops;
+	if (!wf_register_sensor(&pv->sensors[index]))
+		kref_get(&pv->ref);
+}
+
+static void __devinit wf_ad7417_init_chip(struct wf_ad7417_priv *pv)
+{
+	int rc;
+	u8 buf[2];
+	u8 config = 0;
+
+	/*
+	 * Read ADC the configuration register and cache it. We
+	 * also make sure Config2 contains proper values, I've seen
+	 * cases where we got stale grabage in there, thus preventing
+	 * proper reading of conv. values
+	 */
+
+	/* Clear Config2 */
+	buf[0] = 5;
+	buf[1] = 0;
+	i2c_master_send(pv->i2c, buf, 2);
+
+	/* Read & cache Config1 */
+	buf[0] = 1;
+	rc = i2c_master_send(pv->i2c, buf, 1);
+	if (rc > 0) {
+		rc = i2c_master_recv(pv->i2c, buf, 1);
+		if (rc > 0) {
+			config = buf[0];
+
+			dev_dbg(&pv->i2c->dev, "ADC config reg: %02x\n",
+				config);
+
+			/* Disable shutdown mode */
+			config &= 0xfe;
+			buf[0] = 1;
+			buf[1] = config;
+			rc = i2c_master_send(pv->i2c, buf, 2);
+		}
+	}
+	if (rc <= 0)
+		dev_err(&pv->i2c->dev, "Error reading ADC config\n");
+
+	pv->config = config;
+}
+
+static int __devinit wf_ad7417_probe(struct i2c_client *client,
+				     const struct i2c_device_id *id)
+{
+	struct wf_ad7417_priv *pv;
+	const struct mpu_data *mpu;
+	const char *loc;
+	int cpu_nr;
+
+	loc = of_get_property(client->dev.of_node, "hwsensor-location", NULL);
+	if (!loc) {
+		dev_warn(&client->dev, "Missing hwsensor-location property!\n");
+		return -ENXIO;
+	}
+
+	/*
+	 * Identify which CPU we belong to by looking at the first entry
+	 * in the hwsensor-location list
+	 */
+	if (!strncmp(loc, "CPU A", 5))
+		cpu_nr = 0;
+	else if (!strncmp(loc, "CPU B", 5))
+		cpu_nr = 1;
+	else {
+		pr_err("wf_ad7417: Can't identify location %s\n", loc);
+		return -ENXIO;
+	}
+	mpu = wf_get_mpu(cpu_nr);
+	if (!mpu) {
+		dev_err(&client->dev, "Failed to retrieve MPU data\n");
+		return -ENXIO;
+	}
+
+	pv = kzalloc(sizeof(struct wf_ad7417_priv), GFP_KERNEL);
+	if (pv == NULL)
+		return -ENODEV;
+
+	kref_init(&pv->ref);
+	mutex_init(&pv->lock);
+	pv->i2c = client;
+	pv->cpu = cpu_nr;
+	pv->mpu = mpu;
+	dev_set_drvdata(&client->dev, pv);
+
+	/* Initialize the chip */
+	wf_ad7417_init_chip(pv);
+
+	/*
+	 * We cannot rely on Apple device-tree giving us child
+	 * node with the names of the individual sensors so we
+	 * just hard code what we know about them
+	 */
+	wf_ad7417_add_sensor(pv, 0, "cpu-amb-temp", &wf_ad7417_temp_ops);
+	wf_ad7417_add_sensor(pv, 1, "cpu-diode-temp", &wf_ad7417_adc_ops);
+	wf_ad7417_add_sensor(pv, 2, "cpu-12v-current", &wf_ad7417_adc_ops);
+	wf_ad7417_add_sensor(pv, 3, "cpu-voltage", &wf_ad7417_adc_ops);
+	wf_ad7417_add_sensor(pv, 4, "cpu-current", &wf_ad7417_adc_ops);
+
+	return 0;
+}
+
+static int __devexit wf_ad7417_remove(struct i2c_client *client)
+{
+	struct wf_ad7417_priv *pv = dev_get_drvdata(&client->dev);
+	int i;
+
+	/* Mark client detached */
+	pv->i2c = NULL;
+
+	/* Release sensor */
+	for (i = 0; i < 5; i++)
+		wf_unregister_sensor(&pv->sensors[i]);
+
+	kref_put(&pv->ref, wf_ad7417_release);
+
+	return 0;
+}
+
+static const struct i2c_device_id wf_ad7417_id[] = {
+	{ "MAC,ad7417", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, wf_ad7417_id);
+
+static struct i2c_driver wf_ad7417_driver = {
+	.driver = {
+		.name	= "wf_ad7417",
+	},
+	.probe		= wf_ad7417_probe,
+	.remove		= wf_ad7417_remove,
+	.id_table	= wf_ad7417_id,
+};
+
+static int __devinit wf_ad7417_init(void)
+{
+	/* This is only supported on these machines */
+	if (!of_machine_is_compatible("PowerMac7,2") &&
+	    !of_machine_is_compatible("PowerMac7,3") &&
+	    !of_machine_is_compatible("RackMac3,1"))
+		return -ENODEV;
+
+	return i2c_add_driver(&wf_ad7417_driver);
+}
+
+static void __devexit wf_ad7417_exit(void)
+{
+	i2c_del_driver(&wf_ad7417_driver);
+}
+
+module_init(wf_ad7417_init);
+module_exit(wf_ad7417_exit);
+
+MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
+MODULE_DESCRIPTION("ad7417 sensor driver for PowerMacs");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/macintosh/windfarm_core.c b/drivers/macintosh/windfarm_core.c
index ce88979..3ee198b 100644
--- a/drivers/macintosh/windfarm_core.c
+++ b/drivers/macintosh/windfarm_core.c
@@ -164,13 +164,27 @@
 			       struct device_attribute *attr, char *buf)
 {
 	struct wf_control *ctrl = container_of(attr, struct wf_control, attr);
+	const char *typestr;
 	s32 val = 0;
 	int err;
 
 	err = ctrl->ops->get_value(ctrl, &val);
-	if (err < 0)
+	if (err < 0) {
+		if (err == -EFAULT)
+			return sprintf(buf, "<HW FAULT>\n");
 		return err;
-	return sprintf(buf, "%d\n", val);
+	}
+	switch(ctrl->type) {
+	case WF_CONTROL_RPM_FAN:
+		typestr = " RPM";
+		break;
+	case WF_CONTROL_PWM_FAN:
+		typestr = " %";
+		break;
+	default:
+		typestr = "";
+	}
+	return sprintf(buf, "%d%s\n", val, typestr);
 }
 
 /* This is really only for debugging... */
@@ -470,11 +484,6 @@
 {
 	DBG("wf: core loaded\n");
 
-	/* Don't register on old machines that use therm_pm72 for now */
-	if (of_machine_is_compatible("PowerMac7,2") ||
-	    of_machine_is_compatible("PowerMac7,3") ||
-	    of_machine_is_compatible("RackMac3,1"))
-		return -ENODEV;
 	platform_device_register(&wf_platform_device);
 	return 0;
 }
diff --git a/drivers/macintosh/windfarm_cpufreq_clamp.c b/drivers/macintosh/windfarm_cpufreq_clamp.c
index 1a77a7c..72d1fdf 100644
--- a/drivers/macintosh/windfarm_cpufreq_clamp.c
+++ b/drivers/macintosh/windfarm_cpufreq_clamp.c
@@ -75,12 +75,6 @@
 {
 	struct wf_control *clamp;
 
-	/* Don't register on old machines that use therm_pm72 for now */
-	if (of_machine_is_compatible("PowerMac7,2") ||
-	    of_machine_is_compatible("PowerMac7,3") ||
-	    of_machine_is_compatible("RackMac3,1"))
-		return -ENODEV;
-
 	clamp = kmalloc(sizeof(struct wf_control), GFP_KERNEL);
 	if (clamp == NULL)
 		return -ENOMEM;
diff --git a/drivers/macintosh/windfarm_fcu_controls.c b/drivers/macintosh/windfarm_fcu_controls.c
new file mode 100644
index 0000000..b3411ed
--- /dev/null
+++ b/drivers/macintosh/windfarm_fcu_controls.c
@@ -0,0 +1,613 @@
+/*
+ * Windfarm PowerMac thermal control. FCU fan control
+ *
+ * Copyright 2012 Benjamin Herrenschmidt, IBM Corp.
+ *
+ * Released under the term of the GNU GPL v2.
+ */
+#undef DEBUG
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/wait.h>
+#include <linux/i2c.h>
+#include <asm/prom.h>
+#include <asm/machdep.h>
+#include <asm/io.h>
+#include <asm/sections.h>
+
+#include "windfarm.h"
+#include "windfarm_mpu.h"
+
+#define VERSION "1.0"
+
+#ifdef DEBUG
+#define DBG(args...)	printk(args)
+#else
+#define DBG(args...)	do { } while(0)
+#endif
+
+/*
+ * This option is "weird" :) Basically, if you define this to 1
+ * the control loop for the RPMs fans (not PWMs) will apply the
+ * correction factor obtained from the PID to the actual RPM
+ * speed read from the FCU.
+ *
+ * If you define the below constant to 0, then it will be
+ * applied to the setpoint RPM speed, that is basically the
+ * speed we proviously "asked" for.
+ *
+ * I'm using 0 for now which is what therm_pm72 used to do and
+ * what Darwin -apparently- does based on observed behaviour.
+ */
+#define RPM_PID_USE_ACTUAL_SPEED	0
+
+/* Default min/max for pumps */
+#define CPU_PUMP_OUTPUT_MAX		3200
+#define CPU_PUMP_OUTPUT_MIN		1250
+
+#define FCU_FAN_RPM		0
+#define FCU_FAN_PWM		1
+
+struct wf_fcu_priv {
+	struct kref		ref;
+	struct i2c_client	*i2c;
+	struct mutex		lock;
+	struct list_head	fan_list;
+	int			rpm_shift;
+};
+
+struct wf_fcu_fan {
+	struct list_head	link;
+	int			id;
+	s32			min, max, target;
+	struct wf_fcu_priv	*fcu_priv;
+	struct wf_control	ctrl;
+};
+
+static void wf_fcu_release(struct kref *ref)
+{
+	struct wf_fcu_priv *pv = container_of(ref, struct wf_fcu_priv, ref);
+
+	kfree(pv);
+}
+
+static void wf_fcu_fan_release(struct wf_control *ct)
+{
+	struct wf_fcu_fan *fan = ct->priv;
+
+	kref_put(&fan->fcu_priv->ref, wf_fcu_release);
+	kfree(fan);
+}
+
+static int wf_fcu_read_reg(struct wf_fcu_priv *pv, int reg,
+			   unsigned char *buf, int nb)
+{
+	int tries, nr, nw;
+
+	mutex_lock(&pv->lock);
+
+	buf[0] = reg;
+	tries = 0;
+	for (;;) {
+		nw = i2c_master_send(pv->i2c, buf, 1);
+		if (nw > 0 || (nw < 0 && nw != -EIO) || tries >= 100)
+			break;
+		msleep(10);
+		++tries;
+	}
+	if (nw <= 0) {
+		pr_err("Failure writing address to FCU: %d", nw);
+		nr = nw;
+		goto bail;
+	}
+	tries = 0;
+	for (;;) {
+		nr = i2c_master_recv(pv->i2c, buf, nb);
+		if (nr > 0 || (nr < 0 && nr != -ENODEV) || tries >= 100)
+			break;
+		msleep(10);
+		++tries;
+	}
+	if (nr <= 0)
+		pr_err("wf_fcu: Failure reading data from FCU: %d", nw);
+ bail:
+	mutex_unlock(&pv->lock);
+	return nr;
+}
+
+static int wf_fcu_write_reg(struct wf_fcu_priv *pv, int reg,
+			    const unsigned char *ptr, int nb)
+{
+	int tries, nw;
+	unsigned char buf[16];
+
+	buf[0] = reg;
+	memcpy(buf+1, ptr, nb);
+	++nb;
+	tries = 0;
+	for (;;) {
+		nw = i2c_master_send(pv->i2c, buf, nb);
+		if (nw > 0 || (nw < 0 && nw != -EIO) || tries >= 100)
+			break;
+		msleep(10);
+		++tries;
+	}
+	if (nw < 0)
+		pr_err("wf_fcu: Failure writing to FCU: %d", nw);
+	return nw;
+}
+
+static int wf_fcu_fan_set_rpm(struct wf_control *ct, s32 value)
+{
+	struct wf_fcu_fan *fan = ct->priv;
+	struct wf_fcu_priv *pv = fan->fcu_priv;
+	int rc, shift = pv->rpm_shift;
+	unsigned char buf[2];
+
+	if (value < fan->min)
+		value = fan->min;
+	if (value > fan->max)
+		value = fan->max;
+
+	fan->target = value;
+
+	buf[0] = value >> (8 - shift);
+	buf[1] = value << shift;
+	rc = wf_fcu_write_reg(pv, 0x10 + (fan->id * 2), buf, 2);
+	if (rc < 0)
+		return -EIO;
+	return 0;
+}
+
+static int wf_fcu_fan_get_rpm(struct wf_control *ct, s32 *value)
+{
+	struct wf_fcu_fan *fan = ct->priv;
+	struct wf_fcu_priv *pv = fan->fcu_priv;
+	int rc, reg_base, shift = pv->rpm_shift;
+	unsigned char failure;
+	unsigned char active;
+	unsigned char buf[2];
+
+	rc = wf_fcu_read_reg(pv, 0xb, &failure, 1);
+	if (rc != 1)
+		return -EIO;
+	if ((failure & (1 << fan->id)) != 0)
+		return -EFAULT;
+	rc = wf_fcu_read_reg(pv, 0xd, &active, 1);
+	if (rc != 1)
+		return -EIO;
+	if ((active & (1 << fan->id)) == 0)
+		return -ENXIO;
+
+	/* Programmed value or real current speed */
+#if RPM_PID_USE_ACTUAL_SPEED
+	reg_base = 0x11;
+#else
+	reg_base = 0x10;
+#endif
+	rc = wf_fcu_read_reg(pv, reg_base + (fan->id * 2), buf, 2);
+	if (rc != 2)
+		return -EIO;
+
+	*value = (buf[0] << (8 - shift)) | buf[1] >> shift;
+
+	return 0;
+}
+
+static int wf_fcu_fan_set_pwm(struct wf_control *ct, s32 value)
+{
+	struct wf_fcu_fan *fan = ct->priv;
+	struct wf_fcu_priv *pv = fan->fcu_priv;
+	unsigned char buf[2];
+	int rc;
+
+	if (value < fan->min)
+		value = fan->min;
+	if (value > fan->max)
+		value = fan->max;
+
+	fan->target = value;
+
+	value = (value * 2559) / 1000;
+	buf[0] = value;
+	rc = wf_fcu_write_reg(pv, 0x30 + (fan->id * 2), buf, 1);
+	if (rc < 0)
+		return -EIO;
+	return 0;
+}
+
+static int wf_fcu_fan_get_pwm(struct wf_control *ct, s32 *value)
+{
+	struct wf_fcu_fan *fan = ct->priv;
+	struct wf_fcu_priv *pv = fan->fcu_priv;
+	unsigned char failure;
+	unsigned char active;
+	unsigned char buf[2];
+	int rc;
+
+	rc = wf_fcu_read_reg(pv, 0x2b, &failure, 1);
+	if (rc != 1)
+		return -EIO;
+	if ((failure & (1 << fan->id)) != 0)
+		return -EFAULT;
+	rc = wf_fcu_read_reg(pv, 0x2d, &active, 1);
+	if (rc != 1)
+		return -EIO;
+	if ((active & (1 << fan->id)) == 0)
+		return -ENXIO;
+
+	rc = wf_fcu_read_reg(pv, 0x30 + (fan->id * 2), buf, 1);
+	if (rc != 1)
+		return -EIO;
+
+	*value = (((s32)buf[0]) * 1000) / 2559;
+
+	return 0;
+}
+
+static s32 wf_fcu_fan_min(struct wf_control *ct)
+{
+	struct wf_fcu_fan *fan = ct->priv;
+
+	return fan->min;
+}
+
+static s32 wf_fcu_fan_max(struct wf_control *ct)
+{
+	struct wf_fcu_fan *fan = ct->priv;
+
+	return fan->max;
+}
+
+static const struct wf_control_ops wf_fcu_fan_rpm_ops = {
+	.set_value	= wf_fcu_fan_set_rpm,
+	.get_value	= wf_fcu_fan_get_rpm,
+	.get_min	= wf_fcu_fan_min,
+	.get_max	= wf_fcu_fan_max,
+	.release	= wf_fcu_fan_release,
+	.owner		= THIS_MODULE,
+};
+
+static const struct wf_control_ops wf_fcu_fan_pwm_ops = {
+	.set_value	= wf_fcu_fan_set_pwm,
+	.get_value	= wf_fcu_fan_get_pwm,
+	.get_min	= wf_fcu_fan_min,
+	.get_max	= wf_fcu_fan_max,
+	.release	= wf_fcu_fan_release,
+	.owner		= THIS_MODULE,
+};
+
+static void __devinit wf_fcu_get_pump_minmax(struct wf_fcu_fan *fan)
+{
+	const struct mpu_data *mpu = wf_get_mpu(0);
+	u16 pump_min = 0, pump_max = 0xffff;
+	u16 tmp[4];
+
+	/* Try to fetch pumps min/max infos from eeprom */
+	if (mpu) {
+		memcpy(&tmp, mpu->processor_part_num, 8);
+		if (tmp[0] != 0xffff && tmp[1] != 0xffff) {
+			pump_min = max(pump_min, tmp[0]);
+			pump_max = min(pump_max, tmp[1]);
+		}
+		if (tmp[2] != 0xffff && tmp[3] != 0xffff) {
+			pump_min = max(pump_min, tmp[2]);
+			pump_max = min(pump_max, tmp[3]);
+		}
+	}
+
+	/* Double check the values, this _IS_ needed as the EEPROM on
+	 * some dual 2.5Ghz G5s seem, at least, to have both min & max
+	 * same to the same value ... (grrrr)
+	 */
+	if (pump_min == pump_max || pump_min == 0 || pump_max == 0xffff) {
+		pump_min = CPU_PUMP_OUTPUT_MIN;
+		pump_max = CPU_PUMP_OUTPUT_MAX;
+	}
+
+	fan->min = pump_min;
+	fan->max = pump_max;
+
+	DBG("wf_fcu: pump min/max for %s set to: [%d..%d] RPM\n",
+	    fan->ctrl.name, pump_min, pump_max);
+}
+
+static void __devinit wf_fcu_get_rpmfan_minmax(struct wf_fcu_fan *fan)
+{
+	struct wf_fcu_priv *pv = fan->fcu_priv;
+	const struct mpu_data *mpu0 = wf_get_mpu(0);
+	const struct mpu_data *mpu1 = wf_get_mpu(1);
+
+	/* Default */
+	fan->min = 2400 >> pv->rpm_shift;
+	fan->max = 56000 >> pv->rpm_shift;
+
+	/* CPU fans have min/max in MPU */
+	if (mpu0 && !strcmp(fan->ctrl.name, "cpu-front-fan-0")) {
+		fan->min = max(fan->min, (s32)mpu0->rminn_intake_fan);
+		fan->max = min(fan->max, (s32)mpu0->rmaxn_intake_fan);
+		goto bail;
+	}
+	if (mpu1 && !strcmp(fan->ctrl.name, "cpu-front-fan-1")) {
+		fan->min = max(fan->min, (s32)mpu1->rminn_intake_fan);
+		fan->max = min(fan->max, (s32)mpu1->rmaxn_intake_fan);
+		goto bail;
+	}
+	if (mpu0 && !strcmp(fan->ctrl.name, "cpu-rear-fan-0")) {
+		fan->min = max(fan->min, (s32)mpu0->rminn_exhaust_fan);
+		fan->max = min(fan->max, (s32)mpu0->rmaxn_exhaust_fan);
+		goto bail;
+	}
+	if (mpu1 && !strcmp(fan->ctrl.name, "cpu-rear-fan-1")) {
+		fan->min = max(fan->min, (s32)mpu1->rminn_exhaust_fan);
+		fan->max = min(fan->max, (s32)mpu1->rmaxn_exhaust_fan);
+		goto bail;
+	}
+	/* Rackmac variants, we just use mpu0 intake */
+	if (!strncmp(fan->ctrl.name, "cpu-fan", 7)) {
+		fan->min = max(fan->min, (s32)mpu0->rminn_intake_fan);
+		fan->max = min(fan->max, (s32)mpu0->rmaxn_intake_fan);
+		goto bail;
+	}
+ bail:
+	DBG("wf_fcu: fan min/max for %s set to: [%d..%d] RPM\n",
+	    fan->ctrl.name, fan->min, fan->max);
+}
+
+static void __devinit wf_fcu_add_fan(struct wf_fcu_priv *pv,
+				     const char *name,
+				     int type, int id)
+{
+	struct wf_fcu_fan *fan;
+
+	fan = kzalloc(sizeof(*fan), GFP_KERNEL);
+	if (!fan)
+		return;
+	fan->fcu_priv = pv;
+	fan->id = id;
+	fan->ctrl.name = name;
+	fan->ctrl.priv = fan;
+
+	/* min/max is oddball but the code comes from
+	 * therm_pm72 which seems to work so ...
+	 */
+	if (type == FCU_FAN_RPM) {
+		if (!strncmp(name, "cpu-pump", strlen("cpu-pump")))
+			wf_fcu_get_pump_minmax(fan);
+		else
+			wf_fcu_get_rpmfan_minmax(fan);
+		fan->ctrl.type = WF_CONTROL_RPM_FAN;
+		fan->ctrl.ops = &wf_fcu_fan_rpm_ops;
+	} else {
+		fan->min = 10;
+		fan->max = 100;
+		fan->ctrl.type = WF_CONTROL_PWM_FAN;
+		fan->ctrl.ops = &wf_fcu_fan_pwm_ops;
+	}
+
+	if (wf_register_control(&fan->ctrl)) {
+		pr_err("wf_fcu: Failed to register fan %s\n", name);
+		kfree(fan);
+		return;
+	}
+	list_add(&fan->link, &pv->fan_list);
+	kref_get(&pv->ref);
+}
+
+static void __devinit wf_fcu_lookup_fans(struct wf_fcu_priv *pv)
+{
+	/* Translation of device-tree location properties to
+	 * windfarm fan names
+	 */
+	static const struct {
+		const char *dt_name;	/* Device-tree name */
+		const char *ct_name;	/* Control name */
+	} loc_trans[] = {
+		{ "BACKSIDE",		"backside-fan",		},
+		{ "SYS CTRLR FAN",	"backside-fan",		},
+		{ "DRIVE BAY",		"drive-bay-fan",	},
+		{ "SLOT",		"slots-fan",		},
+		{ "PCI FAN",		"slots-fan",		},
+		{ "CPU A INTAKE",	"cpu-front-fan-0",	},
+		{ "CPU A EXHAUST",	"cpu-rear-fan-0",	},
+		{ "CPU B INTAKE",	"cpu-front-fan-1",	},
+		{ "CPU B EXHAUST",	"cpu-rear-fan-1",	},
+		{ "CPU A PUMP",		"cpu-pump-0",		},
+		{ "CPU B PUMP",		"cpu-pump-1",		},
+		{ "CPU A 1",		"cpu-fan-a-0",		},
+		{ "CPU A 2",		"cpu-fan-b-0",		},
+		{ "CPU A 3",		"cpu-fan-c-0",		},
+		{ "CPU B 1",		"cpu-fan-a-1",		},
+		{ "CPU B 2",		"cpu-fan-b-1",		},
+		{ "CPU B 3",		"cpu-fan-c-1",		},
+	};
+	struct device_node *np = NULL, *fcu = pv->i2c->dev.of_node;
+	int i;
+
+	DBG("Looking up FCU controls in device-tree...\n");
+
+	while ((np = of_get_next_child(fcu, np)) != NULL) {
+		int id, type = -1;
+		const char *loc;
+		const char *name;
+		const u32 *reg;
+
+		DBG(" control: %s, type: %s\n", np->name, np->type);
+
+		/* Detect control type */
+		if (!strcmp(np->type, "fan-rpm-control") ||
+		    !strcmp(np->type, "fan-rpm"))
+			type = FCU_FAN_RPM;
+		if (!strcmp(np->type, "fan-pwm-control") ||
+		    !strcmp(np->type, "fan-pwm"))
+			type = FCU_FAN_PWM;
+		/* Only care about fans for now */
+		if (type == -1)
+			continue;
+
+		/* Lookup for a matching location */
+		loc = of_get_property(np, "location", NULL);
+		reg = of_get_property(np, "reg", NULL);
+		if (loc == NULL || reg == NULL)
+			continue;
+		DBG(" matching location: %s, reg: 0x%08x\n", loc, *reg);
+
+		for (i = 0; i < ARRAY_SIZE(loc_trans); i++) {
+			if (strncmp(loc, loc_trans[i].dt_name,
+				    strlen(loc_trans[i].dt_name)))
+				continue;
+			name = loc_trans[i].ct_name;
+
+			DBG(" location match, name: %s\n", name);
+
+			if (type == FCU_FAN_RPM)
+				id = ((*reg) - 0x10) / 2;
+			else
+				id = ((*reg) - 0x30) / 2;
+			if (id > 7) {
+				pr_warning("wf_fcu: Can't parse "
+				       "fan ID in device-tree for %s\n",
+					   np->full_name);
+				break;
+			}
+			wf_fcu_add_fan(pv, name, type, id);
+			break;
+		}
+	}
+}
+
+static void __devinit wf_fcu_default_fans(struct wf_fcu_priv *pv)
+{
+	/* We only support the default fans for PowerMac7,2 */
+	if (!of_machine_is_compatible("PowerMac7,2"))
+		return;
+
+	wf_fcu_add_fan(pv, "backside-fan",	FCU_FAN_PWM, 1);
+	wf_fcu_add_fan(pv, "drive-bay-fan",	FCU_FAN_RPM, 2);
+	wf_fcu_add_fan(pv, "slots-fan",		FCU_FAN_PWM, 2);
+	wf_fcu_add_fan(pv, "cpu-front-fan-0",	FCU_FAN_RPM, 3);
+	wf_fcu_add_fan(pv, "cpu-rear-fan-0",	FCU_FAN_RPM, 4);
+	wf_fcu_add_fan(pv, "cpu-front-fan-1",	FCU_FAN_RPM, 5);
+	wf_fcu_add_fan(pv, "cpu-rear-fan-1",	FCU_FAN_RPM, 6);
+}
+
+static int __devinit wf_fcu_init_chip(struct wf_fcu_priv *pv)
+{
+	unsigned char buf = 0xff;
+	int rc;
+
+	rc = wf_fcu_write_reg(pv, 0xe, &buf, 1);
+	if (rc < 0)
+		return -EIO;
+	rc = wf_fcu_write_reg(pv, 0x2e, &buf, 1);
+	if (rc < 0)
+		return -EIO;
+	rc = wf_fcu_read_reg(pv, 0, &buf, 1);
+	if (rc < 0)
+		return -EIO;
+	pv->rpm_shift = (buf == 1) ? 2 : 3;
+
+	pr_debug("wf_fcu: FCU Initialized, RPM fan shift is %d\n",
+		 pv->rpm_shift);
+
+	return 0;
+}
+
+static int __devinit wf_fcu_probe(struct i2c_client *client,
+				  const struct i2c_device_id *id)
+{
+	struct wf_fcu_priv *pv;
+
+	pv = kzalloc(sizeof(*pv), GFP_KERNEL);
+	if (!pv)
+		return -ENOMEM;
+
+	kref_init(&pv->ref);
+	mutex_init(&pv->lock);
+	INIT_LIST_HEAD(&pv->fan_list);
+	pv->i2c = client;
+
+	/*
+	 * First we must start the FCU which will query the
+	 * shift value to apply to RPMs
+	 */
+	if (wf_fcu_init_chip(pv)) {
+		pr_err("wf_fcu: Initialization failed !\n");
+		kfree(pv);
+		return -ENXIO;
+	}
+
+	/* First lookup fans in the device-tree */
+	wf_fcu_lookup_fans(pv);
+
+	/*
+	 * Older machines don't have the device-tree entries
+	 * we are looking for, just hard code the list
+	 */
+	if (list_empty(&pv->fan_list))
+		wf_fcu_default_fans(pv);
+
+	/* Still no fans ? FAIL */
+	if (list_empty(&pv->fan_list)) {
+		pr_err("wf_fcu: Failed to find fans for your machine\n");
+		kfree(pv);
+		return -ENODEV;
+	}
+
+	dev_set_drvdata(&client->dev, pv);
+
+	return 0;
+}
+
+static int __devexit wf_fcu_remove(struct i2c_client *client)
+{
+	struct wf_fcu_priv *pv = dev_get_drvdata(&client->dev);
+	struct wf_fcu_fan *fan;
+
+	while (!list_empty(&pv->fan_list)) {
+		fan = list_first_entry(&pv->fan_list, struct wf_fcu_fan, link);
+		list_del(&fan->link);
+		wf_unregister_control(&fan->ctrl);
+	}
+	kref_put(&pv->ref, wf_fcu_release);
+	return 0;
+}
+
+static const struct i2c_device_id wf_fcu_id[] = {
+	{ "MAC,fcu", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, wf_fcu_id);
+
+static struct i2c_driver wf_fcu_driver = {
+	.driver = {
+		.name	= "wf_fcu",
+	},
+	.probe		= wf_fcu_probe,
+	.remove		= wf_fcu_remove,
+	.id_table	= wf_fcu_id,
+};
+
+static int __init wf_fcu_init(void)
+{
+	return i2c_add_driver(&wf_fcu_driver);
+}
+
+static void __exit wf_fcu_exit(void)
+{
+	i2c_del_driver(&wf_fcu_driver);
+}
+
+
+module_init(wf_fcu_init);
+module_exit(wf_fcu_exit);
+
+MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
+MODULE_DESCRIPTION("FCU control objects for PowerMacs thermal control");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/macintosh/windfarm_lm75_sensor.c b/drivers/macintosh/windfarm_lm75_sensor.c
index 4d6a90a..b0c2d36 100644
--- a/drivers/macintosh/windfarm_lm75_sensor.c
+++ b/drivers/macintosh/windfarm_lm75_sensor.c
@@ -23,7 +23,7 @@
 
 #include "windfarm.h"
 
-#define VERSION "0.2"
+#define VERSION "1.0"
 
 #undef DEBUG
 
@@ -36,8 +36,8 @@
 struct wf_lm75_sensor {
 	int			ds1775 : 1;
 	int			inited : 1;
-	struct 	i2c_client	*i2c;
-	struct 	wf_sensor	sens;
+	struct i2c_client	*i2c;
+	struct wf_sensor	sens;
 };
 #define wf_to_lm75(c) container_of(c, struct wf_lm75_sensor, sens)
 
@@ -90,40 +90,19 @@
 
 static int wf_lm75_probe(struct i2c_client *client,
 			 const struct i2c_device_id *id)
-{
+{	
 	struct wf_lm75_sensor *lm;
-	int rc;
-
-	lm = kzalloc(sizeof(struct wf_lm75_sensor), GFP_KERNEL);
-	if (lm == NULL)
-		return -ENODEV;
-
-	lm->inited = 0;
-	lm->ds1775 = id->driver_data;
-	lm->i2c = client;
-	lm->sens.name = client->dev.platform_data;
-	lm->sens.ops = &wf_lm75_ops;
-	i2c_set_clientdata(client, lm);
-
-	rc = wf_register_sensor(&lm->sens);
-	if (rc)
-		kfree(lm);
-
-	return rc;
-}
-
-static struct i2c_driver wf_lm75_driver;
-
-static struct i2c_client *wf_lm75_create(struct i2c_adapter *adapter,
-					     u8 addr, int ds1775,
-					     const char *loc)
-{
-	struct i2c_board_info info;
-	struct i2c_client *client;
-	char *name;
+	int rc, ds1775 = id->driver_data;
+	const char *name, *loc;
 
 	DBG("wf_lm75: creating  %s device at address 0x%02x\n",
-	    ds1775 ? "ds1775" : "lm75", addr);
+	    ds1775 ? "ds1775" : "lm75", client->addr);
+
+	loc = of_get_property(client->dev.of_node, "hwsensor-location", NULL);
+	if (!loc) {
+		dev_warn(&client->dev, "Missing hwsensor-location property!\n");
+		return -ENXIO;
+	}
 
 	/* Usual rant about sensor names not beeing very consistent in
 	 * the device-tree, oh well ...
@@ -137,68 +116,31 @@
 		name = "optical-drive-temp";
 	else if (!strcmp(loc, "HD Temp"))
 		name = "hard-drive-temp";
+	else if (!strcmp(loc, "PCI SLOTS"))
+		name = "slots-temp";
+	else if (!strcmp(loc, "CPU A INLET"))
+		name = "cpu-inlet-temp-0";
+	else if (!strcmp(loc, "CPU B INLET"))
+		name = "cpu-inlet-temp-1";
 	else
-		goto fail;
+		return -ENXIO;
+ 	
 
-	memset(&info, 0, sizeof(struct i2c_board_info));
-	info.addr = (addr >> 1) & 0x7f;
-	info.platform_data = name;
-	strlcpy(info.type, ds1775 ? "wf_ds1775" : "wf_lm75", I2C_NAME_SIZE);
-
-	client = i2c_new_device(adapter, &info);
-	if (client == NULL) {
-		printk(KERN_ERR "windfarm: failed to attach %s %s to i2c\n",
-		       ds1775 ? "ds1775" : "lm75", name);
-		goto fail;
-	}
-
-	/*
-	 * Let i2c-core delete that device on driver removal.
-	 * This is safe because i2c-core holds the core_lock mutex for us.
-	 */
-	list_add_tail(&client->detected, &wf_lm75_driver.clients);
-	return client;
- fail:
-	return NULL;
-}
-
-static int wf_lm75_attach(struct i2c_adapter *adapter)
-{
-	struct device_node *busnode, *dev;
-	struct pmac_i2c_bus *bus;
-
-	DBG("wf_lm75: adapter %s detected\n", adapter->name);
-
-	bus = pmac_i2c_adapter_to_bus(adapter);
-	if (bus == NULL)
+	lm = kzalloc(sizeof(struct wf_lm75_sensor), GFP_KERNEL);
+	if (lm == NULL)
 		return -ENODEV;
-	busnode = pmac_i2c_get_bus_node(bus);
 
-	DBG("wf_lm75: bus found, looking for device...\n");
+	lm->inited = 0;
+	lm->ds1775 = ds1775;
+	lm->i2c = client;
+	lm->sens.name = (char *)name; /* XXX fix constness in structure */
+	lm->sens.ops = &wf_lm75_ops;
+	i2c_set_clientdata(client, lm);
 
-	/* Now look for lm75(s) in there */
-	for (dev = NULL;
-	     (dev = of_get_next_child(busnode, dev)) != NULL;) {
-		const char *loc =
-			of_get_property(dev, "hwsensor-location", NULL);
-		u8 addr;
-
-		/* We must re-match the adapter in order to properly check
-		 * the channel on multibus setups
-		 */
-		if (!pmac_i2c_match_adapter(dev, adapter))
-			continue;
-		addr = pmac_i2c_get_dev_addr(dev);
-		if (loc == NULL || addr == 0)
-			continue;
-		/* real lm75 */
-		if (of_device_is_compatible(dev, "lm75"))
-			wf_lm75_create(adapter, addr, 0, loc);
-		/* ds1775 (compatible, better resolution */
-		else if (of_device_is_compatible(dev, "ds1775"))
-			wf_lm75_create(adapter, addr, 1, loc);
-	}
-	return 0;
+	rc = wf_register_sensor(&lm->sens);
+	if (rc)
+		kfree(lm);
+	return rc;
 }
 
 static int wf_lm75_remove(struct i2c_client *client)
@@ -217,16 +159,16 @@
 }
 
 static const struct i2c_device_id wf_lm75_id[] = {
-	{ "wf_lm75", 0 },
-	{ "wf_ds1775", 1 },
+	{ "MAC,lm75", 0 },
+	{ "MAC,ds1775", 1 },
 	{ }
 };
+MODULE_DEVICE_TABLE(i2c, wf_lm75_id);
 
 static struct i2c_driver wf_lm75_driver = {
 	.driver = {
 		.name	= "wf_lm75",
 	},
-	.attach_adapter	= wf_lm75_attach,
 	.probe		= wf_lm75_probe,
 	.remove		= wf_lm75_remove,
 	.id_table	= wf_lm75_id,
@@ -234,11 +176,6 @@
 
 static int __init wf_lm75_sensor_init(void)
 {
-	/* Don't register on old machines that use therm_pm72 for now */
-	if (of_machine_is_compatible("PowerMac7,2") ||
-	    of_machine_is_compatible("PowerMac7,3") ||
-	    of_machine_is_compatible("RackMac3,1"))
-		return -ENODEV;
 	return i2c_add_driver(&wf_lm75_driver);
 }
 
diff --git a/drivers/macintosh/windfarm_lm87_sensor.c b/drivers/macintosh/windfarm_lm87_sensor.c
new file mode 100644
index 0000000..c071aab
--- /dev/null
+++ b/drivers/macintosh/windfarm_lm87_sensor.c
@@ -0,0 +1,201 @@
+/*
+ * Windfarm PowerMac thermal control. LM87 sensor
+ *
+ * Copyright 2012 Benjamin Herrenschmidt, IBM Corp.
+ *
+ * Released under the term of the GNU GPL v2.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/wait.h>
+#include <linux/i2c.h>
+#include <asm/prom.h>
+#include <asm/machdep.h>
+#include <asm/io.h>
+#include <asm/sections.h>
+#include <asm/pmac_low_i2c.h>
+
+#include "windfarm.h"
+
+#define VERSION "1.0"
+
+#undef DEBUG
+
+#ifdef DEBUG
+#define DBG(args...)	printk(args)
+#else
+#define DBG(args...)	do { } while(0)
+#endif
+
+struct wf_lm87_sensor {
+	struct i2c_client	*i2c;
+	struct wf_sensor	sens;
+};
+#define wf_to_lm87(c) container_of(c, struct wf_lm87_sensor, sens)
+
+
+static int wf_lm87_read_reg(struct i2c_client *chip, int reg)
+{
+	int rc, tries = 0;
+	u8 buf;
+
+	for (;;) {
+		/* Set address */
+		buf = (u8)reg;
+		rc = i2c_master_send(chip, &buf, 1);
+		if (rc <= 0)
+			goto error;
+		rc = i2c_master_recv(chip, &buf, 1);
+		if (rc <= 0)
+			goto error;
+		return (int)buf;
+	error:
+		DBG("wf_lm87: Error reading LM87, retrying...\n");
+		if (++tries > 10) {
+			printk(KERN_ERR "wf_lm87: Error reading LM87 !\n");
+			return -EIO;
+		}
+		msleep(10);
+	}
+}
+
+static int wf_lm87_get(struct wf_sensor *sr, s32 *value)
+{
+	struct wf_lm87_sensor *lm = sr->priv;
+	s32 temp;
+
+	if (lm->i2c == NULL)
+		return -ENODEV;
+
+#define LM87_INT_TEMP		0x27
+
+	/* Read temperature register */
+	temp = wf_lm87_read_reg(lm->i2c, LM87_INT_TEMP);
+	if (temp < 0)
+		return temp;
+	*value = temp << 16;
+
+	return 0;
+}
+
+static void wf_lm87_release(struct wf_sensor *sr)
+{
+	struct wf_lm87_sensor *lm = wf_to_lm87(sr);
+
+	kfree(lm);
+}
+
+static struct wf_sensor_ops wf_lm87_ops = {
+	.get_value	= wf_lm87_get,
+	.release	= wf_lm87_release,
+	.owner		= THIS_MODULE,
+};
+
+static int wf_lm87_probe(struct i2c_client *client,
+			 const struct i2c_device_id *id)
+{	
+	struct wf_lm87_sensor *lm;
+	const char *name = NULL, *loc;
+	struct device_node *np = NULL;
+	int rc;
+
+	/*
+	 * The lm87 contains a whole pile of sensors, additionally,
+	 * the Xserve G5 has several lm87's. However, for now we only
+	 * care about the internal temperature sensor
+	 */
+	while ((np = of_get_next_child(client->dev.of_node, np)) != NULL) {
+		if (strcmp(np->name, "int-temp"))
+			continue;
+		loc = of_get_property(np, "location", NULL);
+		if (!loc)
+			continue;
+		if (strstr(loc, "DIMM"))
+			name = "dimms-temp";
+		else if (strstr(loc, "Processors"))
+			name = "between-cpus-temp";
+		if (name) {
+			of_node_put(np);
+			break;
+		}
+	}
+	if (!name) {
+		pr_warning("wf_lm87: Unsupported sensor %s\n",
+			   client->dev.of_node->full_name);
+		return -ENODEV;
+	}
+
+	lm = kzalloc(sizeof(struct wf_lm87_sensor), GFP_KERNEL);
+	if (lm == NULL)
+		return -ENODEV;
+
+	lm->i2c = client;
+	lm->sens.name = name;
+	lm->sens.ops = &wf_lm87_ops;
+	lm->sens.priv = lm;
+	i2c_set_clientdata(client, lm);
+
+	rc = wf_register_sensor(&lm->sens);
+	if (rc)
+		kfree(lm);
+	return rc;
+}
+
+static int wf_lm87_remove(struct i2c_client *client)
+{
+	struct wf_lm87_sensor *lm = i2c_get_clientdata(client);
+
+	DBG("wf_lm87: i2c detatch called for %s\n", lm->sens.name);
+
+	/* Mark client detached */
+	lm->i2c = NULL;
+
+	/* release sensor */
+	wf_unregister_sensor(&lm->sens);
+
+	return 0;
+}
+
+static const struct i2c_device_id wf_lm87_id[] = {
+	{ "MAC,lm87cimt", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, wf_lm87_id);
+
+static struct i2c_driver wf_lm87_driver = {
+	.driver = {
+		.name	= "wf_lm87",
+	},
+	.probe		= wf_lm87_probe,
+	.remove		= wf_lm87_remove,
+	.id_table	= wf_lm87_id,
+};
+
+static int __init wf_lm87_sensor_init(void)
+{
+	/* We only support this on the Xserve */
+	if (!of_machine_is_compatible("RackMac3,1"))
+		return -ENODEV;
+
+	return i2c_add_driver(&wf_lm87_driver);
+}
+
+static void __exit wf_lm87_sensor_exit(void)
+{
+	i2c_del_driver(&wf_lm87_driver);
+}
+
+
+module_init(wf_lm87_sensor_init);
+module_exit(wf_lm87_sensor_exit);
+
+MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
+MODULE_DESCRIPTION("LM87 sensor objects for PowerMacs thermal control");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/macintosh/windfarm_max6690_sensor.c b/drivers/macintosh/windfarm_max6690_sensor.c
index 8204113..371b058 100644
--- a/drivers/macintosh/windfarm_max6690_sensor.c
+++ b/drivers/macintosh/windfarm_max6690_sensor.c
@@ -16,7 +16,7 @@
 
 #include "windfarm.h"
 
-#define VERSION "0.2"
+#define VERSION "1.0"
 
 /* This currently only exports the external temperature sensor,
    since that's all the control loops need. */
@@ -64,9 +64,29 @@
 static int wf_max6690_probe(struct i2c_client *client,
 			    const struct i2c_device_id *id)
 {
+	const char *name, *loc;
 	struct wf_6690_sensor *max;
 	int rc;
 
+	loc = of_get_property(client->dev.of_node, "hwsensor-location", NULL);
+	if (!loc) {
+		dev_warn(&client->dev, "Missing hwsensor-location property!\n");
+		return -ENXIO;
+	}
+
+	/*
+	 * We only expose the external temperature register for
+	 * now as this is all we need for our control loops
+	 */
+	if (!strcmp(loc, "BACKSIDE") || !strcmp(loc, "SYS CTRLR AMBIENT"))
+		name = "backside-temp";
+	else if (!strcmp(loc, "NB Ambient"))
+		name = "north-bridge-temp";
+	else if (!strcmp(loc, "GPU Ambient"))
+		name = "gpu-temp";
+	else
+		return -ENXIO;
+
 	max = kzalloc(sizeof(struct wf_6690_sensor), GFP_KERNEL);
 	if (max == NULL) {
 		printk(KERN_ERR "windfarm: Couldn't create MAX6690 sensor: "
@@ -75,90 +95,16 @@
 	}
 
 	max->i2c = client;
-	max->sens.name = client->dev.platform_data;
+	max->sens.name = (char *)name; /* XXX fix constness in structure */
 	max->sens.ops = &wf_max6690_ops;
 	i2c_set_clientdata(client, max);
 
 	rc = wf_register_sensor(&max->sens);
-	if (rc) {
+	if (rc)
 		kfree(max);
-	}
-
 	return rc;
 }
 
-static struct i2c_driver wf_max6690_driver;
-
-static struct i2c_client *wf_max6690_create(struct i2c_adapter *adapter,
-					    u8 addr, const char *loc)
-{
-	struct i2c_board_info info;
-	struct i2c_client *client;
-	char *name;
-
-	if (!strcmp(loc, "BACKSIDE"))
-		name = "backside-temp";
-	else if (!strcmp(loc, "NB Ambient"))
-		name = "north-bridge-temp";
-	else if (!strcmp(loc, "GPU Ambient"))
-		name = "gpu-temp";
-	else
-		goto fail;
-
-	memset(&info, 0, sizeof(struct i2c_board_info));
-	info.addr = addr >> 1;
-	info.platform_data = name;
-	strlcpy(info.type, "wf_max6690", I2C_NAME_SIZE);
-
-	client = i2c_new_device(adapter, &info);
-	if (client == NULL) {
-		printk(KERN_ERR "windfarm: failed to attach MAX6690 sensor\n");
-		goto fail;
-	}
-
-	/*
-	 * Let i2c-core delete that device on driver removal.
-	 * This is safe because i2c-core holds the core_lock mutex for us.
-	 */
-	list_add_tail(&client->detected, &wf_max6690_driver.clients);
-	return client;
-
- fail:
-	return NULL;
-}
-
-static int wf_max6690_attach(struct i2c_adapter *adapter)
-{
-	struct device_node *busnode, *dev = NULL;
-	struct pmac_i2c_bus *bus;
-	const char *loc;
-
-	bus = pmac_i2c_adapter_to_bus(adapter);
-	if (bus == NULL)
-		return -ENODEV;
-	busnode = pmac_i2c_get_bus_node(bus);
-
-	while ((dev = of_get_next_child(busnode, dev)) != NULL) {
-		u8 addr;
-
-		/* We must re-match the adapter in order to properly check
-		 * the channel on multibus setups
-		 */
-		if (!pmac_i2c_match_adapter(dev, adapter))
-			continue;
-		if (!of_device_is_compatible(dev, "max6690"))
-			continue;
-		addr = pmac_i2c_get_dev_addr(dev);
-		loc = of_get_property(dev, "hwsensor-location", NULL);
-		if (loc == NULL || addr == 0)
-			continue;
-		printk("found max6690, loc=%s addr=0x%02x\n", loc, addr);
-		wf_max6690_create(adapter, addr, loc);
-	}
-
-	return 0;
-}
-
 static int wf_max6690_remove(struct i2c_client *client)
 {
 	struct wf_6690_sensor *max = i2c_get_clientdata(client);
@@ -170,15 +116,15 @@
 }
 
 static const struct i2c_device_id wf_max6690_id[] = {
-	{ "wf_max6690", 0 },
+	{ "MAC,max6690", 0 },
 	{ }
 };
+MODULE_DEVICE_TABLE(i2c, wf_max6690_id);
 
 static struct i2c_driver wf_max6690_driver = {
 	.driver = {
 		.name		= "wf_max6690",
 	},
-	.attach_adapter	= wf_max6690_attach,
 	.probe		= wf_max6690_probe,
 	.remove		= wf_max6690_remove,
 	.id_table	= wf_max6690_id,
@@ -186,11 +132,6 @@
 
 static int __init wf_max6690_sensor_init(void)
 {
-	/* Don't register on old machines that use therm_pm72 for now */
-	if (of_machine_is_compatible("PowerMac7,2") ||
-	    of_machine_is_compatible("PowerMac7,3") ||
-	    of_machine_is_compatible("RackMac3,1"))
-		return -ENODEV;
 	return i2c_add_driver(&wf_max6690_driver);
 }
 
diff --git a/drivers/macintosh/windfarm_mpu.h b/drivers/macintosh/windfarm_mpu.h
new file mode 100644
index 0000000..046edc8
--- /dev/null
+++ b/drivers/macintosh/windfarm_mpu.h
@@ -0,0 +1,105 @@
+/*
+ * Windfarm PowerMac thermal control
+ *
+ * Copyright 2012 Benjamin Herrenschmidt, IBM Corp.
+ *
+ * Released under the term of the GNU GPL v2.
+ */
+
+#ifndef __WINDFARM_MPU_H
+#define __WINDFARM_MPU_H
+
+typedef unsigned short fu16;
+typedef int fs32;
+typedef short fs16;
+
+/* Definition of the MPU data structure which contains per CPU
+ * calibration information (among others) for the G5 machines
+ */
+struct mpu_data
+{
+	u8	signature;		/* 0x00 - EEPROM sig. */
+	u8	bytes_used;		/* 0x01 - Bytes used in eeprom (160 ?) */
+	u8	size;			/* 0x02 - EEPROM size (256 ?) */
+	u8	version;		/* 0x03 - EEPROM version */
+	u32	data_revision;		/* 0x04 - Dataset revision */
+	u8	processor_bin_code[3];	/* 0x08 - Processor BIN code */
+	u8	bin_code_expansion;	/* 0x0b - ??? (padding ?) */
+	u8	processor_num;		/* 0x0c - Number of CPUs on this MPU */
+	u8	input_mul_bus_div;	/* 0x0d - Clock input multiplier/bus divider */
+	u8	reserved1[2];		/* 0x0e - */
+	u32	input_clk_freq_high;	/* 0x10 - Input clock frequency high */
+	u8	cpu_nb_target_cycles;	/* 0x14 - ??? */
+	u8	cpu_statlat;		/* 0x15 - ??? */
+	u8	cpu_snooplat;		/* 0x16 - ??? */
+	u8	cpu_snoopacc;		/* 0x17 - ??? */
+	u8	nb_paamwin;		/* 0x18 - ??? */
+	u8	nb_statlat;		/* 0x19 - ??? */
+	u8	nb_snooplat;		/* 0x1a - ??? */
+	u8	nb_snoopwin;		/* 0x1b - ??? */
+	u8	api_bus_mode;		/* 0x1c - ??? */
+	u8	reserved2[3];		/* 0x1d - */
+	u32	input_clk_freq_low;	/* 0x20 - Input clock frequency low */
+	u8	processor_card_slot;	/* 0x24 - Processor card slot number */
+	u8	reserved3[2];		/* 0x25 - */
+	u8	padjmax;       		/* 0x27 - Max power adjustment (Not in OF!) */
+	u8	ttarget;		/* 0x28 - Target temperature */
+	u8	tmax;			/* 0x29 - Max temperature */
+	u8	pmaxh;			/* 0x2a - Max power */
+	u8	tguardband;		/* 0x2b - Guardband temp ??? Hist. len in OSX */
+	fs32	pid_gp;			/* 0x2c - PID proportional gain */
+	fs32	pid_gr;			/* 0x30 - PID reset gain */
+	fs32	pid_gd;			/* 0x34 - PID derivative gain */
+	fu16	voph;			/* 0x38 - Vop High */
+	fu16	vopl;			/* 0x3a - Vop Low */
+	fs16	nactual_die;		/* 0x3c - nActual Die */
+	fs16	nactual_heatsink;	/* 0x3e - nActual Heatsink */
+	fs16	nactual_system;		/* 0x40 - nActual System */
+	u16	calibration_flags;	/* 0x42 - Calibration flags */
+	fu16	mdiode;			/* 0x44 - Diode M value (scaling factor) */
+	fs16	bdiode;			/* 0x46 - Diode B value (offset) */
+	fs32	theta_heat_sink;	/* 0x48 - Theta heat sink */
+	u16	rminn_intake_fan;	/* 0x4c - Intake fan min RPM */
+	u16	rmaxn_intake_fan;	/* 0x4e - Intake fan max RPM */
+	u16	rminn_exhaust_fan;	/* 0x50 - Exhaust fan min RPM */
+	u16	rmaxn_exhaust_fan;	/* 0x52 - Exhaust fan max RPM */
+	u8	processor_part_num[8];	/* 0x54 - Processor part number XX pumps min/max */
+	u32	processor_lot_num;	/* 0x5c - Processor lot number */
+	u8	orig_card_sernum[0x10];	/* 0x60 - Card original serial number */
+	u8	curr_card_sernum[0x10];	/* 0x70 - Card current serial number */
+	u8	mlb_sernum[0x18];	/* 0x80 - MLB serial number */
+	u32	checksum1;		/* 0x98 - */
+	u32	checksum2;		/* 0x9c - */	
+}; /* Total size = 0xa0 */
+
+static inline const struct mpu_data *wf_get_mpu(int cpu)
+{
+	struct device_node *np;
+	char nodename[64];
+	const void *data;
+	int len;
+
+	/*
+	 * prom.c routine for finding a node by path is a bit brain dead
+	 * and requires exact @xxx unit numbers. This is a bit ugly but
+	 * will work for these machines
+	 */
+	sprintf(nodename, "/u3@0,f8000000/i2c@f8001000/cpuid@a%d", cpu ? 2 : 0);
+	np = of_find_node_by_path(nodename);
+	if (!np)
+		return NULL;
+	data = of_get_property(np, "cpuid", &len);	
+	of_node_put(np);
+	if (!data)
+		return NULL;
+
+	/*
+	 * We are naughty, we have dropped the reference to the device
+	 * node and still return a pointer to the content. We know we
+	 * can do that though as this is only ever called on PowerMac
+	 * which cannot remove those nodes
+	 */
+	return data;
+}
+
+#endif /*  __WINDFARM_MPU_H */
diff --git a/drivers/macintosh/windfarm_pm72.c b/drivers/macintosh/windfarm_pm72.c
new file mode 100644
index 0000000..84ac913
--- /dev/null
+++ b/drivers/macintosh/windfarm_pm72.c
@@ -0,0 +1,847 @@
+/*
+ * Windfarm PowerMac thermal control.
+ * Control loops for PowerMac7,2 and 7,3
+ *
+ * Copyright (C) 2012 Benjamin Herrenschmidt, IBM Corp.
+ *
+ * Use and redistribute under the terms of the GNU GPL v2.
+ */
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <asm/prom.h>
+#include <asm/smu.h>
+
+#include "windfarm.h"
+#include "windfarm_pid.h"
+#include "windfarm_mpu.h"
+
+#define VERSION "1.0"
+
+#undef DEBUG
+#undef LOTSA_DEBUG
+
+#ifdef DEBUG
+#define DBG(args...)	printk(args)
+#else
+#define DBG(args...)	do { } while(0)
+#endif
+
+#ifdef LOTSA_DEBUG
+#define DBG_LOTS(args...)	printk(args)
+#else
+#define DBG_LOTS(args...)	do { } while(0)
+#endif
+
+/* define this to force CPU overtemp to 60 degree, useful for testing
+ * the overtemp code
+ */
+#undef HACKED_OVERTEMP
+
+/* We currently only handle 2 chips */
+#define NR_CHIPS	2
+#define NR_CPU_FANS	3 * NR_CHIPS
+
+/* Controls and sensors */
+static struct wf_sensor *sens_cpu_temp[NR_CHIPS];
+static struct wf_sensor *sens_cpu_volts[NR_CHIPS];
+static struct wf_sensor *sens_cpu_amps[NR_CHIPS];
+static struct wf_sensor *backside_temp;
+static struct wf_sensor *drives_temp;
+
+static struct wf_control *cpu_front_fans[NR_CHIPS];
+static struct wf_control *cpu_rear_fans[NR_CHIPS];
+static struct wf_control *cpu_pumps[NR_CHIPS];
+static struct wf_control *backside_fan;
+static struct wf_control *drives_fan;
+static struct wf_control *slots_fan;
+static struct wf_control *cpufreq_clamp;
+
+/* We keep a temperature history for average calculation of 180s */
+#define CPU_TEMP_HIST_SIZE	180
+
+/* Fixed speed for slot fan */
+#define	SLOTS_FAN_DEFAULT_PWM	40
+
+/* Scale value for CPU intake fans */
+#define CPU_INTAKE_SCALE	0x0000f852
+
+/* PID loop state */
+static const struct mpu_data *cpu_mpu_data[NR_CHIPS];
+static struct wf_cpu_pid_state cpu_pid[NR_CHIPS];
+static bool cpu_pid_combined;
+static u32 cpu_thist[CPU_TEMP_HIST_SIZE];
+static int cpu_thist_pt;
+static s64 cpu_thist_total;
+static s32 cpu_all_tmax = 100 << 16;
+static struct wf_pid_state backside_pid;
+static int backside_tick;
+static struct wf_pid_state drives_pid;
+static int drives_tick;
+
+static int nr_chips;
+static bool have_all_controls;
+static bool have_all_sensors;
+static bool started;
+
+static int failure_state;
+#define FAILURE_SENSOR		1
+#define FAILURE_FAN		2
+#define FAILURE_PERM		4
+#define FAILURE_LOW_OVERTEMP	8
+#define FAILURE_HIGH_OVERTEMP	16
+
+/* Overtemp values */
+#define LOW_OVER_AVERAGE	0
+#define LOW_OVER_IMMEDIATE	(10 << 16)
+#define LOW_OVER_CLEAR		((-10) << 16)
+#define HIGH_OVER_IMMEDIATE	(14 << 16)
+#define HIGH_OVER_AVERAGE	(10 << 16)
+#define HIGH_OVER_IMMEDIATE	(14 << 16)
+
+
+static void cpu_max_all_fans(void)
+{
+	int i;
+
+	/* We max all CPU fans in case of a sensor error. We also do the
+	 * cpufreq clamping now, even if it's supposedly done later by the
+	 * generic code anyway, we do it earlier here to react faster
+	 */
+	if (cpufreq_clamp)
+		wf_control_set_max(cpufreq_clamp);
+	for (i = 0; i < nr_chips; i++) {
+		if (cpu_front_fans[i])
+			wf_control_set_max(cpu_front_fans[i]);
+		if (cpu_rear_fans[i])
+			wf_control_set_max(cpu_rear_fans[i]);
+		if (cpu_pumps[i])
+			wf_control_set_max(cpu_pumps[i]);
+	}
+}
+
+static int cpu_check_overtemp(s32 temp)
+{
+	int new_state = 0;
+	s32 t_avg, t_old;
+	static bool first = true;
+
+	/* First check for immediate overtemps */
+	if (temp >= (cpu_all_tmax + LOW_OVER_IMMEDIATE)) {
+		new_state |= FAILURE_LOW_OVERTEMP;
+		if ((failure_state & FAILURE_LOW_OVERTEMP) == 0)
+			printk(KERN_ERR "windfarm: Overtemp due to immediate CPU"
+			       " temperature !\n");
+	}
+	if (temp >= (cpu_all_tmax + HIGH_OVER_IMMEDIATE)) {
+		new_state |= FAILURE_HIGH_OVERTEMP;
+		if ((failure_state & FAILURE_HIGH_OVERTEMP) == 0)
+			printk(KERN_ERR "windfarm: Critical overtemp due to"
+			       " immediate CPU temperature !\n");
+	}
+
+	/*
+	 * The first time around, initialize the array with the first
+	 * temperature reading
+	 */
+	if (first) {
+		int i;
+
+		cpu_thist_total = 0;
+		for (i = 0; i < CPU_TEMP_HIST_SIZE; i++) {
+			cpu_thist[i] = temp;
+			cpu_thist_total += temp;
+		}
+		first = false;
+	}
+
+	/*
+	 * We calculate a history of max temperatures and use that for the
+	 * overtemp management
+	 */
+	t_old = cpu_thist[cpu_thist_pt];
+	cpu_thist[cpu_thist_pt] = temp;
+	cpu_thist_pt = (cpu_thist_pt + 1) % CPU_TEMP_HIST_SIZE;
+	cpu_thist_total -= t_old;
+	cpu_thist_total += temp;
+	t_avg = cpu_thist_total / CPU_TEMP_HIST_SIZE;
+
+	DBG_LOTS("  t_avg = %d.%03d (out: %d.%03d, in: %d.%03d)\n",
+		 FIX32TOPRINT(t_avg), FIX32TOPRINT(t_old), FIX32TOPRINT(temp));
+
+	/* Now check for average overtemps */
+	if (t_avg >= (cpu_all_tmax + LOW_OVER_AVERAGE)) {
+		new_state |= FAILURE_LOW_OVERTEMP;
+		if ((failure_state & FAILURE_LOW_OVERTEMP) == 0)
+			printk(KERN_ERR "windfarm: Overtemp due to average CPU"
+			       " temperature !\n");
+	}
+	if (t_avg >= (cpu_all_tmax + HIGH_OVER_AVERAGE)) {
+		new_state |= FAILURE_HIGH_OVERTEMP;
+		if ((failure_state & FAILURE_HIGH_OVERTEMP) == 0)
+			printk(KERN_ERR "windfarm: Critical overtemp due to"
+			       " average CPU temperature !\n");
+	}
+
+	/* Now handle overtemp conditions. We don't currently use the windfarm
+	 * overtemp handling core as it's not fully suited to the needs of those
+	 * new machine. This will be fixed later.
+	 */
+	if (new_state) {
+		/* High overtemp -> immediate shutdown */
+		if (new_state & FAILURE_HIGH_OVERTEMP)
+			machine_power_off();
+		if ((failure_state & new_state) != new_state)
+			cpu_max_all_fans();
+		failure_state |= new_state;
+	} else if ((failure_state & FAILURE_LOW_OVERTEMP) &&
+		   (temp < (cpu_all_tmax + LOW_OVER_CLEAR))) {
+		printk(KERN_ERR "windfarm: Overtemp condition cleared !\n");
+		failure_state &= ~FAILURE_LOW_OVERTEMP;
+	}
+
+	return failure_state & (FAILURE_LOW_OVERTEMP | FAILURE_HIGH_OVERTEMP);
+}
+
+static int read_one_cpu_vals(int cpu, s32 *temp, s32 *power)
+{
+	s32 dtemp, volts, amps;
+	int rc;
+
+	/* Get diode temperature */
+	rc = wf_sensor_get(sens_cpu_temp[cpu], &dtemp);
+	if (rc) {
+		DBG("  CPU%d: temp reading error !\n", cpu);
+		return -EIO;
+	}
+	DBG_LOTS("  CPU%d: temp   = %d.%03d\n", cpu, FIX32TOPRINT((dtemp)));
+	*temp = dtemp;
+
+	/* Get voltage */
+	rc = wf_sensor_get(sens_cpu_volts[cpu], &volts);
+	if (rc) {
+		DBG("  CPU%d, volts reading error !\n", cpu);
+		return -EIO;
+	}
+	DBG_LOTS("  CPU%d: volts  = %d.%03d\n", cpu, FIX32TOPRINT((volts)));
+
+	/* Get current */
+	rc = wf_sensor_get(sens_cpu_amps[cpu], &amps);
+	if (rc) {
+		DBG("  CPU%d, current reading error !\n", cpu);
+		return -EIO;
+	}
+	DBG_LOTS("  CPU%d: amps   = %d.%03d\n", cpu, FIX32TOPRINT((amps)));
+
+	/* Calculate power */
+
+	/* Scale voltage and current raw sensor values according to fixed scales
+	 * obtained in Darwin and calculate power from I and V
+	 */
+	*power = (((u64)volts) * ((u64)amps)) >> 16;
+
+	DBG_LOTS("  CPU%d: power  = %d.%03d\n", cpu, FIX32TOPRINT((*power)));
+
+	return 0;
+
+}
+
+static void cpu_fans_tick_split(void)
+{
+	int err, cpu;
+	s32 intake, temp, power, t_max = 0;
+
+	DBG_LOTS("* cpu fans_tick_split()\n");
+
+	for (cpu = 0; cpu < nr_chips; ++cpu) {
+		struct wf_cpu_pid_state *sp = &cpu_pid[cpu];
+
+		/* Read current speed */
+		wf_control_get(cpu_rear_fans[cpu], &sp->target);
+
+		DBG_LOTS("  CPU%d: cur_target = %d RPM\n", cpu, sp->target);
+
+		err = read_one_cpu_vals(cpu, &temp, &power);
+		if (err) {
+			failure_state |= FAILURE_SENSOR;
+			cpu_max_all_fans();
+			return;
+		}
+
+		/* Keep track of highest temp */
+		t_max = max(t_max, temp);
+
+		/* Handle possible overtemps */
+		if (cpu_check_overtemp(t_max))
+			return;
+
+		/* Run PID */
+		wf_cpu_pid_run(sp, power, temp);
+
+		DBG_LOTS("  CPU%d: target = %d RPM\n", cpu, sp->target);
+
+		/* Apply result directly to exhaust fan */
+		err = wf_control_set(cpu_rear_fans[cpu], sp->target);
+		if (err) {
+			pr_warning("wf_pm72: Fan %s reports error %d\n",
+			       cpu_rear_fans[cpu]->name, err);
+			failure_state |= FAILURE_FAN;
+			break;
+		}
+
+		/* Scale result for intake fan */
+		intake = (sp->target * CPU_INTAKE_SCALE) >> 16;
+		DBG_LOTS("  CPU%d: intake = %d RPM\n", cpu, intake);
+		err = wf_control_set(cpu_front_fans[cpu], intake);
+		if (err) {
+			pr_warning("wf_pm72: Fan %s reports error %d\n",
+			       cpu_front_fans[cpu]->name, err);
+			failure_state |= FAILURE_FAN;
+			break;
+		}
+	}
+}
+
+static void cpu_fans_tick_combined(void)
+{
+	s32 temp0, power0, temp1, power1, t_max = 0;
+	s32 temp, power, intake, pump;
+	struct wf_control *pump0, *pump1;
+	struct wf_cpu_pid_state *sp = &cpu_pid[0];
+	int err, cpu;
+
+	DBG_LOTS("* cpu fans_tick_combined()\n");
+
+	/* Read current speed from cpu 0 */
+	wf_control_get(cpu_rear_fans[0], &sp->target);
+
+	DBG_LOTS("  CPUs: cur_target = %d RPM\n", sp->target);
+
+	/* Read values for both CPUs */
+	err = read_one_cpu_vals(0, &temp0, &power0);
+	if (err) {
+		failure_state |= FAILURE_SENSOR;
+		cpu_max_all_fans();
+		return;
+	}
+	err = read_one_cpu_vals(1, &temp1, &power1);
+	if (err) {
+		failure_state |= FAILURE_SENSOR;
+		cpu_max_all_fans();
+		return;
+	}
+
+	/* Keep track of highest temp */
+	t_max = max(t_max, max(temp0, temp1));
+
+	/* Handle possible overtemps */
+	if (cpu_check_overtemp(t_max))
+		return;
+
+	/* Use the max temp & power of both */
+	temp = max(temp0, temp1);
+	power = max(power0, power1);
+
+	/* Run PID */
+	wf_cpu_pid_run(sp, power, temp);
+
+	/* Scale result for intake fan */
+	intake = (sp->target * CPU_INTAKE_SCALE) >> 16;
+
+	/* Same deal with pump speed */
+	pump0 = cpu_pumps[0];
+	pump1 = cpu_pumps[1];
+	if (!pump0) {
+		pump0 = pump1;
+		pump1 = NULL;
+	}
+	pump = (sp->target * wf_control_get_max(pump0)) /
+		cpu_mpu_data[0]->rmaxn_exhaust_fan;
+
+	DBG_LOTS("  CPUs: target = %d RPM\n", sp->target);
+	DBG_LOTS("  CPUs: intake = %d RPM\n", intake);
+	DBG_LOTS("  CPUs: pump   = %d RPM\n", pump);
+
+	for (cpu = 0; cpu < nr_chips; cpu++) {
+		err = wf_control_set(cpu_rear_fans[cpu], sp->target);
+		if (err) {
+			pr_warning("wf_pm72: Fan %s reports error %d\n",
+				   cpu_rear_fans[cpu]->name, err);
+			failure_state |= FAILURE_FAN;
+		}
+		err = wf_control_set(cpu_front_fans[cpu], intake);
+		if (err) {
+			pr_warning("wf_pm72: Fan %s reports error %d\n",
+				   cpu_front_fans[cpu]->name, err);
+			failure_state |= FAILURE_FAN;
+		}
+		err = 0;
+		if (cpu_pumps[cpu])
+			err = wf_control_set(cpu_pumps[cpu], pump);
+		if (err) {
+			pr_warning("wf_pm72: Pump %s reports error %d\n",
+				   cpu_pumps[cpu]->name, err);
+			failure_state |= FAILURE_FAN;
+		}
+	}
+}
+
+/* Implementation... */
+static int cpu_setup_pid(int cpu)
+{
+	struct wf_cpu_pid_param pid;
+	const struct mpu_data *mpu = cpu_mpu_data[cpu];
+	s32 tmax, ttarget, ptarget;
+	int fmin, fmax, hsize;
+
+	/* Get PID params from the appropriate MPU EEPROM */
+	tmax = mpu->tmax << 16;
+	ttarget = mpu->ttarget << 16;
+	ptarget = ((s32)(mpu->pmaxh - mpu->padjmax)) << 16;
+
+	DBG("wf_72: CPU%d ttarget = %d.%03d, tmax = %d.%03d\n",
+	    cpu, FIX32TOPRINT(ttarget), FIX32TOPRINT(tmax));
+
+	/* We keep a global tmax for overtemp calculations */
+	if (tmax < cpu_all_tmax)
+		cpu_all_tmax = tmax;
+
+	/* Set PID min/max by using the rear fan min/max */
+	fmin = wf_control_get_min(cpu_rear_fans[cpu]);
+	fmax = wf_control_get_max(cpu_rear_fans[cpu]);
+	DBG("wf_72: CPU%d max RPM range = [%d..%d]\n", cpu, fmin, fmax);
+
+	/* History size */
+	hsize = min_t(int, mpu->tguardband, WF_PID_MAX_HISTORY);
+	DBG("wf_72: CPU%d history size = %d\n", cpu, hsize);
+
+	/* Initialize PID loop */
+	pid.interval	= 1;	/* seconds */
+	pid.history_len = hsize;
+	pid.gd		= mpu->pid_gd;
+	pid.gp		= mpu->pid_gp;
+	pid.gr		= mpu->pid_gr;
+	pid.tmax	= tmax;
+	pid.ttarget	= ttarget;
+	pid.pmaxadj	= ptarget;
+	pid.min		= fmin;
+	pid.max		= fmax;
+
+	wf_cpu_pid_init(&cpu_pid[cpu], &pid);
+	cpu_pid[cpu].target = 1000;
+
+	return 0;
+}
+
+/* Backside/U3 fan */
+static struct wf_pid_param backside_u3_param = {
+	.interval	= 5,
+	.history_len	= 2,
+	.gd		= 40 << 20,
+	.gp		= 5 << 20,
+	.gr		= 0,
+	.itarget	= 65 << 16,
+	.additive	= 1,
+	.min		= 20,
+	.max		= 100,
+};
+
+static struct wf_pid_param backside_u3h_param = {
+	.interval	= 5,
+	.history_len	= 2,
+	.gd		= 20 << 20,
+	.gp		= 5 << 20,
+	.gr		= 0,
+	.itarget	= 75 << 16,
+	.additive	= 1,
+	.min		= 20,
+	.max		= 100,
+};
+
+static void backside_fan_tick(void)
+{
+	s32 temp;
+	int speed;
+	int err;
+
+	if (!backside_fan || !backside_temp || !backside_tick)
+		return;
+	if (--backside_tick > 0)
+		return;
+	backside_tick = backside_pid.param.interval;
+
+	DBG_LOTS("* backside fans tick\n");
+
+	/* Update fan speed from actual fans */
+	err = wf_control_get(backside_fan, &speed);
+	if (!err)
+		backside_pid.target = speed;
+
+	err = wf_sensor_get(backside_temp, &temp);
+	if (err) {
+		printk(KERN_WARNING "windfarm: U4 temp sensor error %d\n",
+		       err);
+		failure_state |= FAILURE_SENSOR;
+		wf_control_set_max(backside_fan);
+		return;
+	}
+	speed = wf_pid_run(&backside_pid, temp);
+
+	DBG_LOTS("backside PID temp=%d.%.3d speed=%d\n",
+		 FIX32TOPRINT(temp), speed);
+
+	err = wf_control_set(backside_fan, speed);
+	if (err) {
+		printk(KERN_WARNING "windfarm: backside fan error %d\n", err);
+		failure_state |= FAILURE_FAN;
+	}
+}
+
+static void backside_setup_pid(void)
+{
+	/* first time initialize things */
+	s32 fmin = wf_control_get_min(backside_fan);
+	s32 fmax = wf_control_get_max(backside_fan);
+	struct wf_pid_param param;
+	struct device_node *u3;
+	int u3h = 1; /* conservative by default */
+
+	u3 = of_find_node_by_path("/u3@0,f8000000");
+	if (u3 != NULL) {
+		const u32 *vers = of_get_property(u3, "device-rev", NULL);
+		if (vers)
+			if (((*vers) & 0x3f) < 0x34)
+				u3h = 0;
+		of_node_put(u3);
+	}
+
+	param = u3h ? backside_u3h_param : backside_u3_param;
+
+	param.min = max(param.min, fmin);
+	param.max = min(param.max, fmax);
+	wf_pid_init(&backside_pid, &param);
+	backside_tick = 1;
+
+	pr_info("wf_pm72: Backside control loop started.\n");
+}
+
+/* Drive bay fan */
+static const struct wf_pid_param drives_param = {
+	.interval	= 5,
+	.history_len	= 2,
+	.gd		= 30 << 20,
+	.gp		= 5 << 20,
+	.gr		= 0,
+	.itarget	= 40 << 16,
+	.additive	= 1,
+	.min		= 300,
+	.max		= 4000,
+};
+
+static void drives_fan_tick(void)
+{
+	s32 temp;
+	int speed;
+	int err;
+
+	if (!drives_fan || !drives_temp || !drives_tick)
+		return;
+	if (--drives_tick > 0)
+		return;
+	drives_tick = drives_pid.param.interval;
+
+	DBG_LOTS("* drives fans tick\n");
+
+	/* Update fan speed from actual fans */
+	err = wf_control_get(drives_fan, &speed);
+	if (!err)
+		drives_pid.target = speed;
+
+	err = wf_sensor_get(drives_temp, &temp);
+	if (err) {
+		pr_warning("wf_pm72: drive bay temp sensor error %d\n", err);
+		failure_state |= FAILURE_SENSOR;
+		wf_control_set_max(drives_fan);
+		return;
+	}
+	speed = wf_pid_run(&drives_pid, temp);
+
+	DBG_LOTS("drives PID temp=%d.%.3d speed=%d\n",
+		 FIX32TOPRINT(temp), speed);
+
+	err = wf_control_set(drives_fan, speed);
+	if (err) {
+		printk(KERN_WARNING "windfarm: drive bay fan error %d\n", err);
+		failure_state |= FAILURE_FAN;
+	}
+}
+
+static void drives_setup_pid(void)
+{
+	/* first time initialize things */
+	s32 fmin = wf_control_get_min(drives_fan);
+	s32 fmax = wf_control_get_max(drives_fan);
+	struct wf_pid_param param = drives_param;
+
+	param.min = max(param.min, fmin);
+	param.max = min(param.max, fmax);
+	wf_pid_init(&drives_pid, &param);
+	drives_tick = 1;
+
+	pr_info("wf_pm72: Drive bay control loop started.\n");
+}
+
+static void set_fail_state(void)
+{
+	cpu_max_all_fans();
+
+	if (backside_fan)
+		wf_control_set_max(backside_fan);
+	if (slots_fan)
+		wf_control_set_max(slots_fan);
+	if (drives_fan)
+		wf_control_set_max(drives_fan);
+}
+
+static void pm72_tick(void)
+{
+	int i, last_failure;
+
+	if (!started) {
+		started = 1;
+		printk(KERN_INFO "windfarm: CPUs control loops started.\n");
+		for (i = 0; i < nr_chips; ++i) {
+			if (cpu_setup_pid(i) < 0) {
+				failure_state = FAILURE_PERM;
+				set_fail_state();
+				break;
+			}
+		}
+		DBG_LOTS("cpu_all_tmax=%d.%03d\n", FIX32TOPRINT(cpu_all_tmax));
+
+		backside_setup_pid();
+		drives_setup_pid();
+
+		/*
+		 * We don't have the right stuff to drive the PCI fan
+		 * so we fix it to a default value
+		 */
+		wf_control_set(slots_fan, SLOTS_FAN_DEFAULT_PWM);
+
+#ifdef HACKED_OVERTEMP
+		cpu_all_tmax = 60 << 16;
+#endif
+	}
+
+	/* Permanent failure, bail out */
+	if (failure_state & FAILURE_PERM)
+		return;
+
+	/*
+	 * Clear all failure bits except low overtemp which will be eventually
+	 * cleared by the control loop itself
+	 */
+	last_failure = failure_state;
+	failure_state &= FAILURE_LOW_OVERTEMP;
+	if (cpu_pid_combined)
+		cpu_fans_tick_combined();
+	else
+		cpu_fans_tick_split();
+	backside_fan_tick();
+	drives_fan_tick();
+
+	DBG_LOTS("  last_failure: 0x%x, failure_state: %x\n",
+		 last_failure, failure_state);
+
+	/* Check for failures. Any failure causes cpufreq clamping */
+	if (failure_state && last_failure == 0 && cpufreq_clamp)
+		wf_control_set_max(cpufreq_clamp);
+	if (failure_state == 0 && last_failure && cpufreq_clamp)
+		wf_control_set_min(cpufreq_clamp);
+
+	/* That's it for now, we might want to deal with other failures
+	 * differently in the future though
+	 */
+}
+
+static void pm72_new_control(struct wf_control *ct)
+{
+	bool all_controls;
+	bool had_pump = cpu_pumps[0] || cpu_pumps[1];
+
+	if (!strcmp(ct->name, "cpu-front-fan-0"))
+		cpu_front_fans[0] = ct;
+	else if (!strcmp(ct->name, "cpu-front-fan-1"))
+		cpu_front_fans[1] = ct;
+	else if (!strcmp(ct->name, "cpu-rear-fan-0"))
+		cpu_rear_fans[0] = ct;
+	else if (!strcmp(ct->name, "cpu-rear-fan-1"))
+		cpu_rear_fans[1] = ct;
+	else if (!strcmp(ct->name, "cpu-pump-0"))
+		cpu_pumps[0] = ct;
+	else if (!strcmp(ct->name, "cpu-pump-1"))
+		cpu_pumps[1] = ct;
+	else if (!strcmp(ct->name, "backside-fan"))
+		backside_fan = ct;
+	else if (!strcmp(ct->name, "slots-fan"))
+		slots_fan = ct;
+	else if (!strcmp(ct->name, "drive-bay-fan"))
+		drives_fan = ct;
+	else if (!strcmp(ct->name, "cpufreq-clamp"))
+		cpufreq_clamp = ct;
+
+	all_controls =
+		cpu_front_fans[0] &&
+		cpu_rear_fans[0] &&
+		backside_fan &&
+		slots_fan &&
+		drives_fan;
+	if (nr_chips > 1)
+		all_controls &=
+			cpu_front_fans[1] &&
+			cpu_rear_fans[1];
+	have_all_controls = all_controls;
+
+	if ((cpu_pumps[0] || cpu_pumps[1]) && !had_pump) {
+		pr_info("wf_pm72: Liquid cooling pump(s) detected,"
+			" using new algorithm !\n");
+		cpu_pid_combined = true;
+	}
+}
+
+
+static void pm72_new_sensor(struct wf_sensor *sr)
+{
+	bool all_sensors;
+
+	if (!strcmp(sr->name, "cpu-diode-temp-0"))
+		sens_cpu_temp[0] = sr;
+	else if (!strcmp(sr->name, "cpu-diode-temp-1"))
+		sens_cpu_temp[1] = sr;
+	else if (!strcmp(sr->name, "cpu-voltage-0"))
+		sens_cpu_volts[0] = sr;
+	else if (!strcmp(sr->name, "cpu-voltage-1"))
+		sens_cpu_volts[1] = sr;
+	else if (!strcmp(sr->name, "cpu-current-0"))
+		sens_cpu_amps[0] = sr;
+	else if (!strcmp(sr->name, "cpu-current-1"))
+		sens_cpu_amps[1] = sr;
+	else if (!strcmp(sr->name, "backside-temp"))
+		backside_temp = sr;
+	else if (!strcmp(sr->name, "hd-temp"))
+		drives_temp = sr;
+
+	all_sensors =
+		sens_cpu_temp[0] &&
+		sens_cpu_volts[0] &&
+		sens_cpu_amps[0] &&
+		backside_temp &&
+		drives_temp;
+	if (nr_chips > 1)
+		all_sensors &=
+			sens_cpu_temp[1] &&
+			sens_cpu_volts[1] &&
+			sens_cpu_amps[1];
+
+	have_all_sensors = all_sensors;
+}
+
+static int pm72_wf_notify(struct notifier_block *self,
+			  unsigned long event, void *data)
+{
+	switch (event) {
+	case WF_EVENT_NEW_SENSOR:
+		pm72_new_sensor(data);
+		break;
+	case WF_EVENT_NEW_CONTROL:
+		pm72_new_control(data);
+		break;
+	case WF_EVENT_TICK:
+		if (have_all_controls && have_all_sensors)
+			pm72_tick();
+	}
+	return 0;
+}
+
+static struct notifier_block pm72_events = {
+	.notifier_call = pm72_wf_notify,
+};
+
+static int wf_pm72_probe(struct platform_device *dev)
+{
+	wf_register_client(&pm72_events);
+	return 0;
+}
+
+static int __devexit wf_pm72_remove(struct platform_device *dev)
+{
+	wf_unregister_client(&pm72_events);
+
+	/* should release all sensors and controls */
+	return 0;
+}
+
+static struct platform_driver wf_pm72_driver = {
+	.probe	= wf_pm72_probe,
+	.remove	= wf_pm72_remove,
+	.driver	= {
+		.name = "windfarm",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init wf_pm72_init(void)
+{
+	struct device_node *cpu;
+	int i;
+
+	if (!of_machine_is_compatible("PowerMac7,2") &&
+	    !of_machine_is_compatible("PowerMac7,3"))
+		return -ENODEV;
+
+	/* Count the number of CPU cores */
+	nr_chips = 0;
+	for (cpu = NULL; (cpu = of_find_node_by_type(cpu, "cpu")) != NULL; )
+		++nr_chips;
+	if (nr_chips > NR_CHIPS)
+		nr_chips = NR_CHIPS;
+
+	pr_info("windfarm: Initializing for desktop G5 with %d chips\n",
+		nr_chips);
+
+	/* Get MPU data for each CPU */
+	for (i = 0; i < nr_chips; i++) {
+		cpu_mpu_data[i] = wf_get_mpu(i);
+		if (!cpu_mpu_data[i]) {
+			pr_err("wf_pm72: Failed to find MPU data for CPU %d\n", i);
+			return -ENXIO;
+		}
+	}
+
+#ifdef MODULE
+	request_module("windfarm_fcu_controls");
+	request_module("windfarm_lm75_sensor");
+	request_module("windfarm_ad7417_sensor");
+	request_module("windfarm_max6690_sensor");
+	request_module("windfarm_cpufreq_clamp");
+#endif /* MODULE */
+
+	platform_driver_register(&wf_pm72_driver);
+	return 0;
+}
+
+static void __exit wf_pm72_exit(void)
+{
+	platform_driver_unregister(&wf_pm72_driver);
+}
+
+module_init(wf_pm72_init);
+module_exit(wf_pm72_exit);
+
+MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
+MODULE_DESCRIPTION("Thermal control for AGP PowerMac G5s");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:windfarm");
diff --git a/drivers/macintosh/windfarm_pm81.c b/drivers/macintosh/windfarm_pm81.c
index fc13d0f..990c876 100644
--- a/drivers/macintosh/windfarm_pm81.c
+++ b/drivers/macintosh/windfarm_pm81.c
@@ -302,13 +302,13 @@
 	pid_param.interval = WF_SMU_SYS_FANS_INTERVAL;
 	pid_param.history_len = WF_SMU_SYS_FANS_HISTORY_SIZE;
 	pid_param.itarget = param->itarget;
-	pid_param.min = fan_system->ops->get_min(fan_system);
-	pid_param.max = fan_system->ops->get_max(fan_system);
+	pid_param.min = wf_control_get_min(fan_system);
+	pid_param.max = wf_control_get_max(fan_system);
 	if (fan_hd) {
 		pid_param.min =
-			max(pid_param.min,fan_hd->ops->get_min(fan_hd));
+			max(pid_param.min, wf_control_get_min(fan_hd));
 		pid_param.max =
-			min(pid_param.max,fan_hd->ops->get_max(fan_hd));
+			min(pid_param.max, wf_control_get_max(fan_hd));
 	}
 	wf_pid_init(&wf_smu_sys_fans->pid, &pid_param);
 
@@ -337,7 +337,7 @@
 	}
 	st->ticks = WF_SMU_SYS_FANS_INTERVAL;
 
-	rc = sensor_hd_temp->ops->get_value(sensor_hd_temp, &temp);
+	rc = wf_sensor_get(sensor_hd_temp, &temp);
 	if (rc) {
 		printk(KERN_WARNING "windfarm: HD temp sensor error %d\n",
 		       rc);
@@ -373,7 +373,7 @@
 	st->hd_setpoint = new_setpoint;
  readjust:
 	if (fan_system && wf_smu_failure_state == 0) {
-		rc = fan_system->ops->set_value(fan_system, st->sys_setpoint);
+		rc = wf_control_set(fan_system, st->sys_setpoint);
 		if (rc) {
 			printk(KERN_WARNING "windfarm: Sys fan error %d\n",
 			       rc);
@@ -381,7 +381,7 @@
 		}
 	}
 	if (fan_hd && wf_smu_failure_state == 0) {
-		rc = fan_hd->ops->set_value(fan_hd, st->hd_setpoint);
+		rc = wf_control_set(fan_hd, st->hd_setpoint);
 		if (rc) {
 			printk(KERN_WARNING "windfarm: HD fan error %d\n",
 			       rc);
@@ -447,8 +447,8 @@
 	pid_param.ttarget = tmax - tdelta;
 	pid_param.pmaxadj = maxpow - powadj;
 
-	pid_param.min = fan_cpu_main->ops->get_min(fan_cpu_main);
-	pid_param.max = fan_cpu_main->ops->get_max(fan_cpu_main);
+	pid_param.min = wf_control_get_min(fan_cpu_main);
+	pid_param.max = wf_control_get_max(fan_cpu_main);
 
 	wf_cpu_pid_init(&wf_smu_cpu_fans->pid, &pid_param);
 
@@ -481,7 +481,7 @@
 	}
 	st->ticks = WF_SMU_CPU_FANS_INTERVAL;
 
-	rc = sensor_cpu_temp->ops->get_value(sensor_cpu_temp, &temp);
+	rc = wf_sensor_get(sensor_cpu_temp, &temp);
 	if (rc) {
 		printk(KERN_WARNING "windfarm: CPU temp sensor error %d\n",
 		       rc);
@@ -489,7 +489,7 @@
 		return;
 	}
 
-	rc = sensor_cpu_power->ops->get_value(sensor_cpu_power, &power);
+	rc = wf_sensor_get(sensor_cpu_power, &power);
 	if (rc) {
 		printk(KERN_WARNING "windfarm: CPU power sensor error %d\n",
 		       rc);
@@ -525,8 +525,7 @@
 	st->cpu_setpoint = new_setpoint;
  readjust:
 	if (fan_cpu_main && wf_smu_failure_state == 0) {
-		rc = fan_cpu_main->ops->set_value(fan_cpu_main,
-						  st->cpu_setpoint);
+		rc = wf_control_set(fan_cpu_main, st->cpu_setpoint);
 		if (rc) {
 			printk(KERN_WARNING "windfarm: CPU main fan"
 			       " error %d\n", rc);
diff --git a/drivers/macintosh/windfarm_pm91.c b/drivers/macintosh/windfarm_pm91.c
index a9430ed..7653603 100644
--- a/drivers/macintosh/windfarm_pm91.c
+++ b/drivers/macintosh/windfarm_pm91.c
@@ -192,8 +192,8 @@
 	pid_param.ttarget = tmax - tdelta;
 	pid_param.pmaxadj = maxpow - powadj;
 
-	pid_param.min = fan_cpu_main->ops->get_min(fan_cpu_main);
-	pid_param.max = fan_cpu_main->ops->get_max(fan_cpu_main);
+	pid_param.min = wf_control_get_min(fan_cpu_main);
+	pid_param.max = wf_control_get_max(fan_cpu_main);
 
 	wf_cpu_pid_init(&wf_smu_cpu_fans->pid, &pid_param);
 
@@ -226,7 +226,7 @@
 	}
 	st->ticks = WF_SMU_CPU_FANS_INTERVAL;
 
-	rc = sensor_cpu_temp->ops->get_value(sensor_cpu_temp, &temp);
+	rc = wf_sensor_get(sensor_cpu_temp, &temp);
 	if (rc) {
 		printk(KERN_WARNING "windfarm: CPU temp sensor error %d\n",
 		       rc);
@@ -234,7 +234,7 @@
 		return;
 	}
 
-	rc = sensor_cpu_power->ops->get_value(sensor_cpu_power, &power);
+	rc = wf_sensor_get(sensor_cpu_power, &power);
 	if (rc) {
 		printk(KERN_WARNING "windfarm: CPU power sensor error %d\n",
 		       rc);
@@ -261,8 +261,7 @@
 	st->cpu_setpoint = new_setpoint;
  readjust:
 	if (fan_cpu_main && wf_smu_failure_state == 0) {
-		rc = fan_cpu_main->ops->set_value(fan_cpu_main,
-						  st->cpu_setpoint);
+		rc = wf_control_set(fan_cpu_main, st->cpu_setpoint);
 		if (rc) {
 			printk(KERN_WARNING "windfarm: CPU main fan"
 			       " error %d\n", rc);
@@ -270,8 +269,7 @@
 		}
 	}
 	if (fan_cpu_second && wf_smu_failure_state == 0) {
-		rc = fan_cpu_second->ops->set_value(fan_cpu_second,
-						    st->cpu_setpoint);
+		rc = wf_control_set(fan_cpu_second, st->cpu_setpoint);
 		if (rc) {
 			printk(KERN_WARNING "windfarm: CPU second fan"
 			       " error %d\n", rc);
@@ -279,8 +277,7 @@
 		}
 	}
 	if (fan_cpu_third && wf_smu_failure_state == 0) {
-		rc = fan_cpu_main->ops->set_value(fan_cpu_third,
-						  st->cpu_setpoint);
+		rc = wf_control_set(fan_cpu_third, st->cpu_setpoint);
 		if (rc) {
 			printk(KERN_WARNING "windfarm: CPU third fan"
 			       " error %d\n", rc);
@@ -312,8 +309,8 @@
 
 	/* Fill PID params */
 	param.additive = (fan_hd->type == WF_CONTROL_RPM_FAN);
-	param.min = fan_hd->ops->get_min(fan_hd);
-	param.max = fan_hd->ops->get_max(fan_hd);
+	param.min = wf_control_get_min(fan_hd);
+	param.max = wf_control_get_max(fan_hd);
 	wf_pid_init(&wf_smu_drive_fans->pid, &param);
 
 	DBG("wf: Drive Fan control initialized.\n");
@@ -338,7 +335,7 @@
 	}
 	st->ticks = st->pid.param.interval;
 
-	rc = sensor_hd_temp->ops->get_value(sensor_hd_temp, &temp);
+	rc = wf_sensor_get(sensor_hd_temp, &temp);
 	if (rc) {
 		printk(KERN_WARNING "windfarm: HD temp sensor error %d\n",
 		       rc);
@@ -361,7 +358,7 @@
 	st->setpoint = new_setpoint;
  readjust:
 	if (fan_hd && wf_smu_failure_state == 0) {
-		rc = fan_hd->ops->set_value(fan_hd, st->setpoint);
+		rc = wf_control_set(fan_hd, st->setpoint);
 		if (rc) {
 			printk(KERN_WARNING "windfarm: HD fan error %d\n",
 			       rc);
@@ -393,8 +390,8 @@
 
 	/* Fill PID params */
 	param.additive = (fan_slots->type == WF_CONTROL_RPM_FAN);
-	param.min = fan_slots->ops->get_min(fan_slots);
-	param.max = fan_slots->ops->get_max(fan_slots);
+	param.min = wf_control_get_min(fan_slots);
+	param.max = wf_control_get_max(fan_slots);
 	wf_pid_init(&wf_smu_slots_fans->pid, &param);
 
 	DBG("wf: Slots Fan control initialized.\n");
@@ -419,7 +416,7 @@
 	}
 	st->ticks = st->pid.param.interval;
 
-	rc = sensor_slots_power->ops->get_value(sensor_slots_power, &power);
+	rc = wf_sensor_get(sensor_slots_power, &power);
 	if (rc) {
 		printk(KERN_WARNING "windfarm: Slots power sensor error %d\n",
 		       rc);
@@ -444,7 +441,7 @@
 	st->setpoint = new_setpoint;
  readjust:
 	if (fan_slots && wf_smu_failure_state == 0) {
-		rc = fan_slots->ops->set_value(fan_slots, st->setpoint);
+		rc = wf_control_set(fan_slots, st->setpoint);
 		if (rc) {
 			printk(KERN_WARNING "windfarm: Slots fan error %d\n",
 			       rc);
diff --git a/drivers/macintosh/windfarm_rm31.c b/drivers/macintosh/windfarm_rm31.c
new file mode 100644
index 0000000..3eca6d4
--- /dev/null
+++ b/drivers/macintosh/windfarm_rm31.c
@@ -0,0 +1,740 @@
+/*
+ * Windfarm PowerMac thermal control.
+ * Control loops for RackMack3,1 (Xserve G5)
+ *
+ * Copyright (C) 2012 Benjamin Herrenschmidt, IBM Corp.
+ *
+ * Use and redistribute under the terms of the GNU GPL v2.
+ */
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <asm/prom.h>
+#include <asm/smu.h>
+
+#include "windfarm.h"
+#include "windfarm_pid.h"
+#include "windfarm_mpu.h"
+
+#define VERSION "1.0"
+
+#undef DEBUG
+#undef LOTSA_DEBUG
+
+#ifdef DEBUG
+#define DBG(args...)	printk(args)
+#else
+#define DBG(args...)	do { } while(0)
+#endif
+
+#ifdef LOTSA_DEBUG
+#define DBG_LOTS(args...)	printk(args)
+#else
+#define DBG_LOTS(args...)	do { } while(0)
+#endif
+
+/* define this to force CPU overtemp to 60 degree, useful for testing
+ * the overtemp code
+ */
+#undef HACKED_OVERTEMP
+
+/* We currently only handle 2 chips */
+#define NR_CHIPS	2
+#define NR_CPU_FANS	3 * NR_CHIPS
+
+/* Controls and sensors */
+static struct wf_sensor *sens_cpu_temp[NR_CHIPS];
+static struct wf_sensor *sens_cpu_volts[NR_CHIPS];
+static struct wf_sensor *sens_cpu_amps[NR_CHIPS];
+static struct wf_sensor *backside_temp;
+static struct wf_sensor *slots_temp;
+static struct wf_sensor *dimms_temp;
+
+static struct wf_control *cpu_fans[NR_CHIPS][3];
+static struct wf_control *backside_fan;
+static struct wf_control *slots_fan;
+static struct wf_control *cpufreq_clamp;
+
+/* We keep a temperature history for average calculation of 180s */
+#define CPU_TEMP_HIST_SIZE	180
+
+/* PID loop state */
+static const struct mpu_data *cpu_mpu_data[NR_CHIPS];
+static struct wf_cpu_pid_state cpu_pid[NR_CHIPS];
+static u32 cpu_thist[CPU_TEMP_HIST_SIZE];
+static int cpu_thist_pt;
+static s64 cpu_thist_total;
+static s32 cpu_all_tmax = 100 << 16;
+static struct wf_pid_state backside_pid;
+static int backside_tick;
+static struct wf_pid_state slots_pid;
+static int slots_tick;
+static int slots_speed;
+static struct wf_pid_state dimms_pid;
+static int dimms_output_clamp;
+
+static int nr_chips;
+static bool have_all_controls;
+static bool have_all_sensors;
+static bool started;
+
+static int failure_state;
+#define FAILURE_SENSOR		1
+#define FAILURE_FAN		2
+#define FAILURE_PERM		4
+#define FAILURE_LOW_OVERTEMP	8
+#define FAILURE_HIGH_OVERTEMP	16
+
+/* Overtemp values */
+#define LOW_OVER_AVERAGE	0
+#define LOW_OVER_IMMEDIATE	(10 << 16)
+#define LOW_OVER_CLEAR		((-10) << 16)
+#define HIGH_OVER_IMMEDIATE	(14 << 16)
+#define HIGH_OVER_AVERAGE	(10 << 16)
+#define HIGH_OVER_IMMEDIATE	(14 << 16)
+
+
+static void cpu_max_all_fans(void)
+{
+	int i;
+
+	/* We max all CPU fans in case of a sensor error. We also do the
+	 * cpufreq clamping now, even if it's supposedly done later by the
+	 * generic code anyway, we do it earlier here to react faster
+	 */
+	if (cpufreq_clamp)
+		wf_control_set_max(cpufreq_clamp);
+	for (i = 0; i < nr_chips; i++) {
+		if (cpu_fans[i][0])
+			wf_control_set_max(cpu_fans[i][0]);
+		if (cpu_fans[i][1])
+			wf_control_set_max(cpu_fans[i][1]);
+		if (cpu_fans[i][2])
+			wf_control_set_max(cpu_fans[i][2]);
+	}
+}
+
+static int cpu_check_overtemp(s32 temp)
+{
+	int new_state = 0;
+	s32 t_avg, t_old;
+	static bool first = true;
+
+	/* First check for immediate overtemps */
+	if (temp >= (cpu_all_tmax + LOW_OVER_IMMEDIATE)) {
+		new_state |= FAILURE_LOW_OVERTEMP;
+		if ((failure_state & FAILURE_LOW_OVERTEMP) == 0)
+			printk(KERN_ERR "windfarm: Overtemp due to immediate CPU"
+			       " temperature !\n");
+	}
+	if (temp >= (cpu_all_tmax + HIGH_OVER_IMMEDIATE)) {
+		new_state |= FAILURE_HIGH_OVERTEMP;
+		if ((failure_state & FAILURE_HIGH_OVERTEMP) == 0)
+			printk(KERN_ERR "windfarm: Critical overtemp due to"
+			       " immediate CPU temperature !\n");
+	}
+
+	/*
+	 * The first time around, initialize the array with the first
+	 * temperature reading
+	 */
+	if (first) {
+		int i;
+
+		cpu_thist_total = 0;
+		for (i = 0; i < CPU_TEMP_HIST_SIZE; i++) {
+			cpu_thist[i] = temp;
+			cpu_thist_total += temp;
+		}
+		first = false;
+	}
+
+	/*
+	 * We calculate a history of max temperatures and use that for the
+	 * overtemp management
+	 */
+	t_old = cpu_thist[cpu_thist_pt];
+	cpu_thist[cpu_thist_pt] = temp;
+	cpu_thist_pt = (cpu_thist_pt + 1) % CPU_TEMP_HIST_SIZE;
+	cpu_thist_total -= t_old;
+	cpu_thist_total += temp;
+	t_avg = cpu_thist_total / CPU_TEMP_HIST_SIZE;
+
+	DBG_LOTS("  t_avg = %d.%03d (out: %d.%03d, in: %d.%03d)\n",
+		 FIX32TOPRINT(t_avg), FIX32TOPRINT(t_old), FIX32TOPRINT(temp));
+
+	/* Now check for average overtemps */
+	if (t_avg >= (cpu_all_tmax + LOW_OVER_AVERAGE)) {
+		new_state |= FAILURE_LOW_OVERTEMP;
+		if ((failure_state & FAILURE_LOW_OVERTEMP) == 0)
+			printk(KERN_ERR "windfarm: Overtemp due to average CPU"
+			       " temperature !\n");
+	}
+	if (t_avg >= (cpu_all_tmax + HIGH_OVER_AVERAGE)) {
+		new_state |= FAILURE_HIGH_OVERTEMP;
+		if ((failure_state & FAILURE_HIGH_OVERTEMP) == 0)
+			printk(KERN_ERR "windfarm: Critical overtemp due to"
+			       " average CPU temperature !\n");
+	}
+
+	/* Now handle overtemp conditions. We don't currently use the windfarm
+	 * overtemp handling core as it's not fully suited to the needs of those
+	 * new machine. This will be fixed later.
+	 */
+	if (new_state) {
+		/* High overtemp -> immediate shutdown */
+		if (new_state & FAILURE_HIGH_OVERTEMP)
+			machine_power_off();
+		if ((failure_state & new_state) != new_state)
+			cpu_max_all_fans();
+		failure_state |= new_state;
+	} else if ((failure_state & FAILURE_LOW_OVERTEMP) &&
+		   (temp < (cpu_all_tmax + LOW_OVER_CLEAR))) {
+		printk(KERN_ERR "windfarm: Overtemp condition cleared !\n");
+		failure_state &= ~FAILURE_LOW_OVERTEMP;
+	}
+
+	return failure_state & (FAILURE_LOW_OVERTEMP | FAILURE_HIGH_OVERTEMP);
+}
+
+static int read_one_cpu_vals(int cpu, s32 *temp, s32 *power)
+{
+	s32 dtemp, volts, amps;
+	int rc;
+
+	/* Get diode temperature */
+	rc = wf_sensor_get(sens_cpu_temp[cpu], &dtemp);
+	if (rc) {
+		DBG("  CPU%d: temp reading error !\n", cpu);
+		return -EIO;
+	}
+	DBG_LOTS("  CPU%d: temp   = %d.%03d\n", cpu, FIX32TOPRINT((dtemp)));
+	*temp = dtemp;
+
+	/* Get voltage */
+	rc = wf_sensor_get(sens_cpu_volts[cpu], &volts);
+	if (rc) {
+		DBG("  CPU%d, volts reading error !\n", cpu);
+		return -EIO;
+	}
+	DBG_LOTS("  CPU%d: volts  = %d.%03d\n", cpu, FIX32TOPRINT((volts)));
+
+	/* Get current */
+	rc = wf_sensor_get(sens_cpu_amps[cpu], &amps);
+	if (rc) {
+		DBG("  CPU%d, current reading error !\n", cpu);
+		return -EIO;
+	}
+	DBG_LOTS("  CPU%d: amps   = %d.%03d\n", cpu, FIX32TOPRINT((amps)));
+
+	/* Calculate power */
+
+	/* Scale voltage and current raw sensor values according to fixed scales
+	 * obtained in Darwin and calculate power from I and V
+	 */
+	*power = (((u64)volts) * ((u64)amps)) >> 16;
+
+	DBG_LOTS("  CPU%d: power  = %d.%03d\n", cpu, FIX32TOPRINT((*power)));
+
+	return 0;
+
+}
+
+static void cpu_fans_tick(void)
+{
+	int err, cpu, i;
+	s32 speed, temp, power, t_max = 0;
+
+	DBG_LOTS("* cpu fans_tick_split()\n");
+
+	for (cpu = 0; cpu < nr_chips; ++cpu) {
+		struct wf_cpu_pid_state *sp = &cpu_pid[cpu];
+
+		/* Read current speed */
+		wf_control_get(cpu_fans[cpu][0], &sp->target);
+
+		err = read_one_cpu_vals(cpu, &temp, &power);
+		if (err) {
+			failure_state |= FAILURE_SENSOR;
+			cpu_max_all_fans();
+			return;
+		}
+
+		/* Keep track of highest temp */
+		t_max = max(t_max, temp);
+
+		/* Handle possible overtemps */
+		if (cpu_check_overtemp(t_max))
+			return;
+
+		/* Run PID */
+		wf_cpu_pid_run(sp, power, temp);
+
+		DBG_LOTS("  CPU%d: target = %d RPM\n", cpu, sp->target);
+
+		/* Apply DIMMs clamp */
+		speed = max(sp->target, dimms_output_clamp);
+
+		/* Apply result to all cpu fans */
+		for (i = 0; i < 3; i++) {
+			err = wf_control_set(cpu_fans[cpu][i], speed);
+			if (err) {
+				pr_warning("wf_rm31: Fan %s reports error %d\n",
+					   cpu_fans[cpu][i]->name, err);
+				failure_state |= FAILURE_FAN;
+			}
+		}
+	}
+}
+
+/* Implementation... */
+static int cpu_setup_pid(int cpu)
+{
+	struct wf_cpu_pid_param pid;
+	const struct mpu_data *mpu = cpu_mpu_data[cpu];
+	s32 tmax, ttarget, ptarget;
+	int fmin, fmax, hsize;
+
+	/* Get PID params from the appropriate MPU EEPROM */
+	tmax = mpu->tmax << 16;
+	ttarget = mpu->ttarget << 16;
+	ptarget = ((s32)(mpu->pmaxh - mpu->padjmax)) << 16;
+
+	DBG("wf_72: CPU%d ttarget = %d.%03d, tmax = %d.%03d\n",
+	    cpu, FIX32TOPRINT(ttarget), FIX32TOPRINT(tmax));
+
+	/* We keep a global tmax for overtemp calculations */
+	if (tmax < cpu_all_tmax)
+		cpu_all_tmax = tmax;
+
+	/* Set PID min/max by using the rear fan min/max */
+	fmin = wf_control_get_min(cpu_fans[cpu][0]);
+	fmax = wf_control_get_max(cpu_fans[cpu][0]);
+	DBG("wf_72: CPU%d max RPM range = [%d..%d]\n", cpu, fmin, fmax);
+
+	/* History size */
+	hsize = min_t(int, mpu->tguardband, WF_PID_MAX_HISTORY);
+	DBG("wf_72: CPU%d history size = %d\n", cpu, hsize);
+
+	/* Initialize PID loop */
+	pid.interval	= 1;	/* seconds */
+	pid.history_len = hsize;
+	pid.gd		= mpu->pid_gd;
+	pid.gp		= mpu->pid_gp;
+	pid.gr		= mpu->pid_gr;
+	pid.tmax	= tmax;
+	pid.ttarget	= ttarget;
+	pid.pmaxadj	= ptarget;
+	pid.min		= fmin;
+	pid.max		= fmax;
+
+	wf_cpu_pid_init(&cpu_pid[cpu], &pid);
+	cpu_pid[cpu].target = 4000;
+	
+	return 0;
+}
+
+/* Backside/U3 fan */
+static struct wf_pid_param backside_param = {
+	.interval	= 1,
+	.history_len	= 2,
+	.gd		= 0x00500000,
+	.gp		= 0x0004cccc,
+	.gr		= 0,
+	.itarget	= 70 << 16,
+	.additive	= 0,
+	.min		= 20,
+	.max		= 100,
+};
+
+/* DIMMs temperature (clamp the backside fan) */
+static struct wf_pid_param dimms_param = {
+	.interval	= 1,
+	.history_len	= 20,
+	.gd		= 0,
+	.gp		= 0,
+	.gr		= 0x06553600,
+	.itarget	= 50 << 16,
+	.additive	= 0,
+	.min		= 4000,
+	.max		= 14000,
+};
+
+static void backside_fan_tick(void)
+{
+	s32 temp, dtemp;
+	int speed, dspeed, fan_min;
+	int err;
+
+	if (!backside_fan || !backside_temp || !dimms_temp || !backside_tick)
+		return;
+	if (--backside_tick > 0)
+		return;
+	backside_tick = backside_pid.param.interval;
+
+	DBG_LOTS("* backside fans tick\n");
+
+	/* Update fan speed from actual fans */
+	err = wf_control_get(backside_fan, &speed);
+	if (!err)
+		backside_pid.target = speed;
+
+	err = wf_sensor_get(backside_temp, &temp);
+	if (err) {
+		printk(KERN_WARNING "windfarm: U3 temp sensor error %d\n",
+		       err);
+		failure_state |= FAILURE_SENSOR;
+		wf_control_set_max(backside_fan);
+		return;
+	}
+	speed = wf_pid_run(&backside_pid, temp);
+
+	DBG_LOTS("backside PID temp=%d.%.3d speed=%d\n",
+		 FIX32TOPRINT(temp), speed);
+
+	err = wf_sensor_get(dimms_temp, &dtemp);
+	if (err) {
+		printk(KERN_WARNING "windfarm: DIMMs temp sensor error %d\n",
+		       err);
+		failure_state |= FAILURE_SENSOR;
+		wf_control_set_max(backside_fan);
+		return;
+	}
+	dspeed = wf_pid_run(&dimms_pid, dtemp);
+	dimms_output_clamp = dspeed;
+
+	fan_min = (dspeed * 100) / 14000;
+	fan_min = max(fan_min, backside_param.min);
+	speed = max(speed, fan_min);
+
+	err = wf_control_set(backside_fan, speed);
+	if (err) {
+		printk(KERN_WARNING "windfarm: backside fan error %d\n", err);
+		failure_state |= FAILURE_FAN;
+	}
+}
+
+static void backside_setup_pid(void)
+{
+	/* first time initialize things */
+	s32 fmin = wf_control_get_min(backside_fan);
+	s32 fmax = wf_control_get_max(backside_fan);
+	struct wf_pid_param param;
+
+	param = backside_param;
+	param.min = max(param.min, fmin);
+	param.max = min(param.max, fmax);
+	wf_pid_init(&backside_pid, &param);
+
+	param = dimms_param;
+	wf_pid_init(&dimms_pid, &param);
+
+	backside_tick = 1;
+
+	pr_info("wf_rm31: Backside control loop started.\n");
+}
+
+/* Slots fan */
+static const struct wf_pid_param slots_param = {
+	.interval	= 5,
+	.history_len	= 2,
+	.gd		= 30 << 20,
+	.gp		= 5 << 20,
+	.gr		= 0,
+	.itarget	= 40 << 16,
+	.additive	= 1,
+	.min		= 300,
+	.max		= 4000,
+};
+
+static void slots_fan_tick(void)
+{
+	s32 temp;
+	int speed;
+	int err;
+
+	if (!slots_fan || !slots_temp || !slots_tick)
+		return;
+	if (--slots_tick > 0)
+		return;
+	slots_tick = slots_pid.param.interval;
+
+	DBG_LOTS("* slots fans tick\n");
+
+	err = wf_sensor_get(slots_temp, &temp);
+	if (err) {
+		pr_warning("wf_rm31: slots temp sensor error %d\n", err);
+		failure_state |= FAILURE_SENSOR;
+		wf_control_set_max(slots_fan);
+		return;
+	}
+	speed = wf_pid_run(&slots_pid, temp);
+
+	DBG_LOTS("slots PID temp=%d.%.3d speed=%d\n",
+		 FIX32TOPRINT(temp), speed);
+
+	slots_speed = speed;
+	err = wf_control_set(slots_fan, speed);
+	if (err) {
+		printk(KERN_WARNING "windfarm: slots bay fan error %d\n", err);
+		failure_state |= FAILURE_FAN;
+	}
+}
+
+static void slots_setup_pid(void)
+{
+	/* first time initialize things */
+	s32 fmin = wf_control_get_min(slots_fan);
+	s32 fmax = wf_control_get_max(slots_fan);
+	struct wf_pid_param param = slots_param;
+
+	param.min = max(param.min, fmin);
+	param.max = min(param.max, fmax);
+	wf_pid_init(&slots_pid, &param);
+	slots_tick = 1;
+
+	pr_info("wf_rm31: Slots control loop started.\n");
+}
+
+static void set_fail_state(void)
+{
+	cpu_max_all_fans();
+
+	if (backside_fan)
+		wf_control_set_max(backside_fan);
+	if (slots_fan)
+		wf_control_set_max(slots_fan);
+}
+
+static void rm31_tick(void)
+{
+	int i, last_failure;
+
+	if (!started) {
+		started = 1;
+		printk(KERN_INFO "windfarm: CPUs control loops started.\n");
+		for (i = 0; i < nr_chips; ++i) {
+			if (cpu_setup_pid(i) < 0) {
+				failure_state = FAILURE_PERM;
+				set_fail_state();
+				break;
+			}
+		}
+		DBG_LOTS("cpu_all_tmax=%d.%03d\n", FIX32TOPRINT(cpu_all_tmax));
+
+		backside_setup_pid();
+		slots_setup_pid();
+
+#ifdef HACKED_OVERTEMP
+		cpu_all_tmax = 60 << 16;
+#endif
+	}
+
+	/* Permanent failure, bail out */
+	if (failure_state & FAILURE_PERM)
+		return;
+
+	/*
+	 * Clear all failure bits except low overtemp which will be eventually
+	 * cleared by the control loop itself
+	 */
+	last_failure = failure_state;
+	failure_state &= FAILURE_LOW_OVERTEMP;
+	backside_fan_tick();
+	slots_fan_tick();
+
+	/* We do CPUs last because they can be clamped high by
+	 * DIMM temperature
+	 */
+	cpu_fans_tick();
+
+	DBG_LOTS("  last_failure: 0x%x, failure_state: %x\n",
+		 last_failure, failure_state);
+
+	/* Check for failures. Any failure causes cpufreq clamping */
+	if (failure_state && last_failure == 0 && cpufreq_clamp)
+		wf_control_set_max(cpufreq_clamp);
+	if (failure_state == 0 && last_failure && cpufreq_clamp)
+		wf_control_set_min(cpufreq_clamp);
+
+	/* That's it for now, we might want to deal with other failures
+	 * differently in the future though
+	 */
+}
+
+static void rm31_new_control(struct wf_control *ct)
+{
+	bool all_controls;
+
+	if (!strcmp(ct->name, "cpu-fan-a-0"))
+		cpu_fans[0][0] = ct;
+	else if (!strcmp(ct->name, "cpu-fan-b-0"))
+		cpu_fans[0][1] = ct;
+	else if (!strcmp(ct->name, "cpu-fan-c-0"))
+		cpu_fans[0][2] = ct;
+	else if (!strcmp(ct->name, "cpu-fan-a-1"))
+		cpu_fans[1][0] = ct;
+	else if (!strcmp(ct->name, "cpu-fan-b-1"))
+		cpu_fans[1][1] = ct;
+	else if (!strcmp(ct->name, "cpu-fan-c-1"))
+		cpu_fans[1][2] = ct;
+	else if (!strcmp(ct->name, "backside-fan"))
+		backside_fan = ct;
+	else if (!strcmp(ct->name, "slots-fan"))
+		slots_fan = ct;
+	else if (!strcmp(ct->name, "cpufreq-clamp"))
+		cpufreq_clamp = ct;
+
+	all_controls =
+		cpu_fans[0][0] &&
+		cpu_fans[0][1] &&
+		cpu_fans[0][2] &&
+		backside_fan &&
+		slots_fan;
+	if (nr_chips > 1)
+		all_controls &=
+			cpu_fans[1][0] &&
+			cpu_fans[1][1] &&
+			cpu_fans[1][2];
+	have_all_controls = all_controls;
+}
+
+
+static void rm31_new_sensor(struct wf_sensor *sr)
+{
+	bool all_sensors;
+
+	if (!strcmp(sr->name, "cpu-diode-temp-0"))
+		sens_cpu_temp[0] = sr;
+	else if (!strcmp(sr->name, "cpu-diode-temp-1"))
+		sens_cpu_temp[1] = sr;
+	else if (!strcmp(sr->name, "cpu-voltage-0"))
+		sens_cpu_volts[0] = sr;
+	else if (!strcmp(sr->name, "cpu-voltage-1"))
+		sens_cpu_volts[1] = sr;
+	else if (!strcmp(sr->name, "cpu-current-0"))
+		sens_cpu_amps[0] = sr;
+	else if (!strcmp(sr->name, "cpu-current-1"))
+		sens_cpu_amps[1] = sr;
+	else if (!strcmp(sr->name, "backside-temp"))
+		backside_temp = sr;
+	else if (!strcmp(sr->name, "slots-temp"))
+		slots_temp = sr;
+	else if (!strcmp(sr->name, "dimms-temp"))
+		dimms_temp = sr;
+
+	all_sensors =
+		sens_cpu_temp[0] &&
+		sens_cpu_volts[0] &&
+		sens_cpu_amps[0] &&
+		backside_temp &&
+		slots_temp &&
+		dimms_temp;
+	if (nr_chips > 1)
+		all_sensors &=
+			sens_cpu_temp[1] &&
+			sens_cpu_volts[1] &&
+			sens_cpu_amps[1];
+
+	have_all_sensors = all_sensors;
+}
+
+static int rm31_wf_notify(struct notifier_block *self,
+			  unsigned long event, void *data)
+{
+	switch (event) {
+	case WF_EVENT_NEW_SENSOR:
+		rm31_new_sensor(data);
+		break;
+	case WF_EVENT_NEW_CONTROL:
+		rm31_new_control(data);
+		break;
+	case WF_EVENT_TICK:
+		if (have_all_controls && have_all_sensors)
+			rm31_tick();
+	}
+	return 0;
+}
+
+static struct notifier_block rm31_events = {
+	.notifier_call = rm31_wf_notify,
+};
+
+static int wf_rm31_probe(struct platform_device *dev)
+{
+	wf_register_client(&rm31_events);
+	return 0;
+}
+
+static int __devexit wf_rm31_remove(struct platform_device *dev)
+{
+	wf_unregister_client(&rm31_events);
+
+	/* should release all sensors and controls */
+	return 0;
+}
+
+static struct platform_driver wf_rm31_driver = {
+	.probe	= wf_rm31_probe,
+	.remove	= wf_rm31_remove,
+	.driver	= {
+		.name = "windfarm",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init wf_rm31_init(void)
+{
+	struct device_node *cpu;
+	int i;
+
+	if (!of_machine_is_compatible("RackMac3,1"))
+		return -ENODEV;
+
+	/* Count the number of CPU cores */
+	nr_chips = 0;
+	for (cpu = NULL; (cpu = of_find_node_by_type(cpu, "cpu")) != NULL; )
+		++nr_chips;
+	if (nr_chips > NR_CHIPS)
+		nr_chips = NR_CHIPS;
+
+	pr_info("windfarm: Initializing for desktop G5 with %d chips\n",
+		nr_chips);
+
+	/* Get MPU data for each CPU */
+	for (i = 0; i < nr_chips; i++) {
+		cpu_mpu_data[i] = wf_get_mpu(i);
+		if (!cpu_mpu_data[i]) {
+			pr_err("wf_rm31: Failed to find MPU data for CPU %d\n", i);
+			return -ENXIO;
+		}
+	}
+
+#ifdef MODULE
+	request_module("windfarm_fcu_controls");
+	request_module("windfarm_lm75_sensor");
+	request_module("windfarm_lm87_sensor");
+	request_module("windfarm_ad7417_sensor");
+	request_module("windfarm_max6690_sensor");
+	request_module("windfarm_cpufreq_clamp");
+#endif /* MODULE */
+
+	platform_driver_register(&wf_rm31_driver);
+	return 0;
+}
+
+static void __exit wf_rm31_exit(void)
+{
+	platform_driver_unregister(&wf_rm31_driver);
+}
+
+module_init(wf_rm31_init);
+module_exit(wf_rm31_exit);
+
+MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
+MODULE_DESCRIPTION("Thermal control for Xserve G5");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:windfarm");
diff --git a/drivers/macintosh/windfarm_smu_controls.c b/drivers/macintosh/windfarm_smu_controls.c
index 3c2be51..c155a54 100644
--- a/drivers/macintosh/windfarm_smu_controls.c
+++ b/drivers/macintosh/windfarm_smu_controls.c
@@ -172,7 +172,6 @@
 
 	fct->fan_type = pwm_fan;
 	fct->ctrl.type = pwm_fan ? WF_CONTROL_PWM_FAN : WF_CONTROL_RPM_FAN;
-	sysfs_attr_init(&fct->ctrl.attr.attr);
 
 	/* We use the name & location here the same way we do for SMU sensors,
 	 * see the comment in windfarm_smu_sensors.c. The locations are a bit
diff --git a/drivers/macintosh/windfarm_smu_sat.c b/drivers/macintosh/windfarm_smu_sat.c
index 65a8ff3..426e810 100644
--- a/drivers/macintosh/windfarm_smu_sat.c
+++ b/drivers/macintosh/windfarm_smu_sat.c
@@ -20,7 +20,7 @@
 
 #include "windfarm.h"
 
-#define VERSION "0.2"
+#define VERSION "1.0"
 
 #define DEBUG
 
@@ -34,11 +34,12 @@
 #define MAX_AGE		msecs_to_jiffies(800)
 
 struct wf_sat {
+	struct kref		ref;
 	int			nr;
-	atomic_t		refcnt;
 	struct mutex		mutex;
 	unsigned long		last_read; /* jiffies when cache last updated */
 	u8			cache[16];
+	struct list_head	sensors;
 	struct i2c_client	*i2c;
 	struct device_node	*node;
 };
@@ -46,11 +47,12 @@
 static struct wf_sat *sats[2];
 
 struct wf_sat_sensor {
-	int		index;
-	int		index2;		/* used for power sensors */
-	int		shift;
-	struct wf_sat	*sat;
-	struct wf_sensor sens;
+	struct list_head	link;
+	int			index;
+	int			index2;		/* used for power sensors */
+	int			shift;
+	struct wf_sat		*sat;
+	struct wf_sensor 	sens;
 };
 
 #define wf_to_sat(c)	container_of(c, struct wf_sat_sensor, sens)
@@ -142,7 +144,7 @@
 	return 0;
 }
 
-static int wf_sat_get(struct wf_sensor *sr, s32 *value)
+static int wf_sat_sensor_get(struct wf_sensor *sr, s32 *value)
 {
 	struct wf_sat_sensor *sens = wf_to_sat(sr);
 	struct wf_sat *sat = sens->sat;
@@ -175,62 +177,34 @@
 	return err;
 }
 
-static void wf_sat_release(struct wf_sensor *sr)
+static void wf_sat_release(struct kref *ref)
+{
+	struct wf_sat *sat = container_of(ref, struct wf_sat, ref);
+
+	if (sat->nr >= 0)
+		sats[sat->nr] = NULL;
+	kfree(sat);
+}
+
+static void wf_sat_sensor_release(struct wf_sensor *sr)
 {
 	struct wf_sat_sensor *sens = wf_to_sat(sr);
 	struct wf_sat *sat = sens->sat;
 
-	if (atomic_dec_and_test(&sat->refcnt)) {
-		if (sat->nr >= 0)
-			sats[sat->nr] = NULL;
-		kfree(sat);
-	}
 	kfree(sens);
+	kref_put(&sat->ref, wf_sat_release);
 }
 
 static struct wf_sensor_ops wf_sat_ops = {
-	.get_value	= wf_sat_get,
-	.release	= wf_sat_release,
+	.get_value	= wf_sat_sensor_get,
+	.release	= wf_sat_sensor_release,
 	.owner		= THIS_MODULE,
 };
 
-static struct i2c_driver wf_sat_driver;
-
-static void wf_sat_create(struct i2c_adapter *adapter, struct device_node *dev)
-{
-	struct i2c_board_info info;
-	struct i2c_client *client;
-	const u32 *reg;
-	u8 addr;
-
-	reg = of_get_property(dev, "reg", NULL);
-	if (reg == NULL)
-		return;
-	addr = *reg;
-	DBG(KERN_DEBUG "wf_sat: creating sat at address %x\n", addr);
-
-	memset(&info, 0, sizeof(struct i2c_board_info));
-	info.addr = (addr >> 1) & 0x7f;
-	info.platform_data = dev;
-	strlcpy(info.type, "wf_sat", I2C_NAME_SIZE);
-
-	client = i2c_new_device(adapter, &info);
-	if (client == NULL) {
-		printk(KERN_ERR "windfarm: failed to attach smu-sat to i2c\n");
-		return;
-	}
-
-	/*
-	 * Let i2c-core delete that device on driver removal.
-	 * This is safe because i2c-core holds the core_lock mutex for us.
-	 */
-	list_add_tail(&client->detected, &wf_sat_driver.clients);
-}
-
 static int wf_sat_probe(struct i2c_client *client,
 			const struct i2c_device_id *id)
 {
-	struct device_node *dev = client->dev.platform_data;
+	struct device_node *dev = client->dev.of_node;
 	struct wf_sat *sat;
 	struct wf_sat_sensor *sens;
 	const u32 *reg;
@@ -246,9 +220,10 @@
 		return -ENOMEM;
 	sat->nr = -1;
 	sat->node = of_node_get(dev);
-	atomic_set(&sat->refcnt, 0);
+	kref_init(&sat->ref);
 	mutex_init(&sat->mutex);
 	sat->i2c = client;
+	INIT_LIST_HEAD(&sat->sensors);
 	i2c_set_clientdata(client, sat);
 
 	vsens[0] = vsens[1] = -1;
@@ -310,14 +285,15 @@
 		sens->index2 = -1;
 		sens->shift = shift;
 		sens->sat = sat;
-		atomic_inc(&sat->refcnt);
 		sens->sens.ops = &wf_sat_ops;
 		sens->sens.name = (char *) (sens + 1);
-		snprintf(sens->sens.name, 16, "%s-%d", name, cpu);
+		snprintf((char *)sens->sens.name, 16, "%s-%d", name, cpu);
 
-		if (wf_register_sensor(&sens->sens)) {
-			atomic_dec(&sat->refcnt);
+		if (wf_register_sensor(&sens->sens))
 			kfree(sens);
+		else {
+			list_add(&sens->link, &sat->sensors);
+			kref_get(&sat->ref);
 		}
 	}
 
@@ -336,14 +312,15 @@
 		sens->index2 = isens[core];
 		sens->shift = 0;
 		sens->sat = sat;
-		atomic_inc(&sat->refcnt);
 		sens->sens.ops = &wf_sat_ops;
 		sens->sens.name = (char *) (sens + 1);
-		snprintf(sens->sens.name, 16, "cpu-power-%d", cpu);
+		snprintf((char *)sens->sens.name, 16, "cpu-power-%d", cpu);
 
-		if (wf_register_sensor(&sens->sens)) {
-			atomic_dec(&sat->refcnt);
+		if (wf_register_sensor(&sens->sens))
 			kfree(sens);
+		else {
+			list_add(&sens->link, &sat->sensors);
+			kref_get(&sat->ref);
 		}
 	}
 
@@ -353,42 +330,35 @@
 	return 0;
 }
 
-static int wf_sat_attach(struct i2c_adapter *adapter)
-{
-	struct device_node *busnode, *dev = NULL;
-	struct pmac_i2c_bus *bus;
-
-	bus = pmac_i2c_adapter_to_bus(adapter);
-	if (bus == NULL)
-		return -ENODEV;
-	busnode = pmac_i2c_get_bus_node(bus);
-
-	while ((dev = of_get_next_child(busnode, dev)) != NULL)
-		if (of_device_is_compatible(dev, "smu-sat"))
-			wf_sat_create(adapter, dev);
-	return 0;
-}
-
 static int wf_sat_remove(struct i2c_client *client)
 {
 	struct wf_sat *sat = i2c_get_clientdata(client);
+	struct wf_sat_sensor *sens;
 
-	/* XXX TODO */
-
+	/* release sensors */
+	while(!list_empty(&sat->sensors)) {
+		sens = list_first_entry(&sat->sensors,
+					struct wf_sat_sensor, link);
+		list_del(&sens->link);
+		wf_unregister_sensor(&sens->sens);
+	}
 	sat->i2c = NULL;
+	i2c_set_clientdata(client, NULL);
+	kref_put(&sat->ref, wf_sat_release);
+
 	return 0;
 }
 
 static const struct i2c_device_id wf_sat_id[] = {
-	{ "wf_sat", 0 },
+	{ "MAC,smu-sat", 0 },
 	{ }
 };
+MODULE_DEVICE_TABLE(i2c, wf_sat_id);
 
 static struct i2c_driver wf_sat_driver = {
 	.driver = {
 		.name		= "wf_smu_sat",
 	},
-	.attach_adapter	= wf_sat_attach,
 	.probe		= wf_sat_probe,
 	.remove		= wf_sat_remove,
 	.id_table	= wf_sat_id,
@@ -399,15 +369,13 @@
 	return i2c_add_driver(&wf_sat_driver);
 }
 
-#if 0	/* uncomment when module_exit() below is uncommented */
 static void __exit sat_sensors_exit(void)
 {
 	i2c_del_driver(&wf_sat_driver);
 }
-#endif
 
 module_init(sat_sensors_init);
-/*module_exit(sat_sensors_exit); Uncomment when cleanup is implemented */
+module_exit(sat_sensors_exit);
 
 MODULE_AUTHOR("Paul Mackerras <paulus@samba.org>");
 MODULE_DESCRIPTION("SMU satellite sensors for PowerMac thermal control");
diff --git a/drivers/mca/Kconfig b/drivers/mca/Kconfig
deleted file mode 100644
index a7a0220..0000000
--- a/drivers/mca/Kconfig
+++ /dev/null
@@ -1,14 +0,0 @@
-config MCA_LEGACY
-	bool "Legacy MCA API Support"
-	depends on MCA
-	help
-	  This compiles in support for the old slot based MCA API.  If you
-	  have an unconverted MCA driver, you will need to say Y here.  It
-	  is safe to say Y anyway.
-
-config MCA_PROC_FS
-	bool "Support for the mca entry in /proc"
-	depends on MCA_LEGACY && PROC_FS
-	help
-	  If you want the old style /proc/mca directory in addition to the
-	  new style sysfs say Y here.
diff --git a/drivers/mca/Makefile b/drivers/mca/Makefile
deleted file mode 100644
index 0794b12..0000000
--- a/drivers/mca/Makefile
+++ /dev/null
@@ -1,7 +0,0 @@
-# Makefile for the Linux MCA bus support
-
-obj-y	:= mca-bus.o mca-device.o mca-driver.o
-
-obj-$(CONFIG_MCA_PROC_FS)	+= mca-proc.o
-obj-$(CONFIG_MCA_LEGACY)	+= mca-legacy.o
-
diff --git a/drivers/mca/mca-bus.c b/drivers/mca/mca-bus.c
deleted file mode 100644
index ada5ebb..0000000
--- a/drivers/mca/mca-bus.c
+++ /dev/null
@@ -1,169 +0,0 @@
-/* -*- mode: c; c-basic-offset: 8 -*- */
-
-/*
- * MCA bus support functions for sysfs.
- *
- * (C) 2002 James Bottomley <James.Bottomley@HansenPartnership.com>
- *
-**-----------------------------------------------------------------------------
-**  
-**  This program is free software; you can redistribute it and/or modify
-**  it under the terms of the GNU General Public License as published by
-**  the Free Software Foundation; either version 2 of the License, or
-**  (at your option) any later version.
-**
-**  This program is distributed in the hope that it will be useful,
-**  but WITHOUT ANY WARRANTY; without even the implied warranty of
-**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-**  GNU General Public License for more details.
-**
-**  You should have received a copy of the GNU General Public License
-**  along with this program; if not, write to the Free Software
-**  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-**
-**-----------------------------------------------------------------------------
- */
-
-#include <linux/kernel.h>
-#include <linux/device.h>
-#include <linux/mca.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-
-/* Very few machines have more than one MCA bus.  However, there are
- * those that do (Voyager 35xx/5xxx), so we do it this way for future
- * expansion.  None that I know have more than 2 */
-static struct mca_bus *mca_root_busses[MAX_MCA_BUSSES];
-
-#define MCA_DEVINFO(i,s) { .pos = i, .name = s }
-
-struct mca_device_info {
-	short pos_id;		/* the 2 byte pos id for this card */
-	char name[50];
-};
-
-static int mca_bus_match (struct device *dev, struct device_driver *drv)
-{
-	struct mca_device *mca_dev = to_mca_device (dev);
-	struct mca_driver *mca_drv = to_mca_driver (drv);
-	const unsigned short *mca_ids = mca_drv->id_table;
-	int i = 0;
-
-	if (mca_ids) {
-		for(i = 0; mca_ids[i]; i++) {
-			if (mca_ids[i] == mca_dev->pos_id) {
-				mca_dev->index = i;
-				return 1;
-			}
-		}
-	}
-	/* If the integrated id is present, treat it as though it were an
-	 * additional id in the id_table (it can't be because by definition,
-	 * integrated id's overflow a short */
-	if (mca_drv->integrated_id && mca_dev->pos_id ==
-	    mca_drv->integrated_id) {
-		mca_dev->index = i;
-		return 1;
-	}
-	return 0;
-}
-
-struct bus_type mca_bus_type = {
-	.name  = "MCA",
-	.match = mca_bus_match,
-};
-EXPORT_SYMBOL (mca_bus_type);
-
-static ssize_t mca_show_pos_id(struct device *dev, struct device_attribute *attr, char *buf)
-{
-	/* four digits, \n and trailing \0 */
-	struct mca_device *mca_dev = to_mca_device(dev);
-	int len;
-
-	if(mca_dev->pos_id < MCA_DUMMY_POS_START)
-		len = sprintf(buf, "%04x\n", mca_dev->pos_id);
-	else
-		len = sprintf(buf, "none\n");
-	return len;
-}
-static ssize_t mca_show_pos(struct device *dev, struct device_attribute *attr, char *buf)
-{
-	/* enough for 8 two byte hex chars plus space and new line */
-	int j, len=0;
-	struct mca_device *mca_dev = to_mca_device(dev);
-
-	for(j=0; j<8; j++)
-		len += sprintf(buf+len, "%02x ", mca_dev->pos[j]);
-	/* change last trailing space to new line */
-	buf[len-1] = '\n';
-	return len;
-}
-
-static DEVICE_ATTR(id, S_IRUGO, mca_show_pos_id, NULL);
-static DEVICE_ATTR(pos, S_IRUGO, mca_show_pos, NULL);
-
-int __init mca_register_device(int bus, struct mca_device *mca_dev)
-{
-	struct mca_bus *mca_bus = mca_root_busses[bus];
-	int rc;
-
-	mca_dev->dev.parent = &mca_bus->dev;
-	mca_dev->dev.bus = &mca_bus_type;
-	dev_set_name(&mca_dev->dev, "%02d:%02X", bus, mca_dev->slot);
-	mca_dev->dma_mask = mca_bus->default_dma_mask;
-	mca_dev->dev.dma_mask = &mca_dev->dma_mask;
-	mca_dev->dev.coherent_dma_mask = mca_dev->dma_mask;
-
-	rc = device_register(&mca_dev->dev);
-	if (rc)
-		goto err_out;
-
-	rc = device_create_file(&mca_dev->dev, &dev_attr_id);
-	if (rc) goto err_out_devreg;
-	rc = device_create_file(&mca_dev->dev, &dev_attr_pos);
-	if (rc) goto err_out_id;
-
-	return 1;
-
-err_out_id:
-	device_remove_file(&mca_dev->dev, &dev_attr_id);
-err_out_devreg:
-	device_unregister(&mca_dev->dev);
-err_out:
-	return 0;
-}
-
-/* */
-struct mca_bus * __devinit mca_attach_bus(int bus)
-{
-	struct mca_bus *mca_bus;
-
-	if (unlikely(mca_root_busses[bus] != NULL)) {
-		/* This should never happen, but just in case */
-		printk(KERN_EMERG "MCA tried to add already existing bus %d\n",
-		       bus);
-		dump_stack();
-		return NULL;
-	}
-
-	mca_bus = kzalloc(sizeof(struct mca_bus), GFP_KERNEL);
-	if (!mca_bus)
-		return NULL;
-
-	dev_set_name(&mca_bus->dev, "mca%d", bus);
-	sprintf(mca_bus->name,"Host %s MCA Bridge", bus ? "Secondary" : "Primary");
-	if (device_register(&mca_bus->dev)) {
-		kfree(mca_bus);
-		return NULL;
-	}
-
-	mca_root_busses[bus] = mca_bus;
-
-	return mca_bus;
-}
-
-int __init mca_system_init (void)
-{
-	return bus_register(&mca_bus_type);
-}
diff --git a/drivers/mca/mca-device.c b/drivers/mca/mca-device.c
deleted file mode 100644
index e7adf89..0000000
--- a/drivers/mca/mca-device.c
+++ /dev/null
@@ -1,218 +0,0 @@
-/* -*- mode: c; c-basic-offset: 8 -*- */
-
-/*
- * MCA device support functions
- *
- * These functions support the ongoing device access API.
- *
- * (C) 2002 James Bottomley <James.Bottomley@HansenPartnership.com>
- *
-**-----------------------------------------------------------------------------
-**  
-**  This program is free software; you can redistribute it and/or modify
-**  it under the terms of the GNU General Public License as published by
-**  the Free Software Foundation; either version 2 of the License, or
-**  (at your option) any later version.
-**
-**  This program is distributed in the hope that it will be useful,
-**  but WITHOUT ANY WARRANTY; without even the implied warranty of
-**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-**  GNU General Public License for more details.
-**
-**  You should have received a copy of the GNU General Public License
-**  along with this program; if not, write to the Free Software
-**  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-**
-**-----------------------------------------------------------------------------
- */
-
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/mca.h>
-#include <linux/string.h>
-
-/**
- *	mca_device_read_stored_pos - read POS register from stored data
- *	@mca_dev: device to read from
- *	@reg:  register to read from
- *
- *	Fetch a POS value that was stored at boot time by the kernel
- *	when it scanned the MCA space. The register value is returned.
- *	Missing or invalid registers report 0.
- */
-unsigned char mca_device_read_stored_pos(struct mca_device *mca_dev, int reg)
-{
-	if(reg < 0 || reg >= 8)
-		return 0;
-
-	return mca_dev->pos[reg];
-}
-EXPORT_SYMBOL(mca_device_read_stored_pos);
-
-/**
- *	mca_device_read_pos - read POS register from card
- *	@mca_dev: device to read from
- *	@reg:  register to read from
- *
- *	Fetch a POS value directly from the hardware to obtain the
- *	current value. This is much slower than
- *	mca_device_read_stored_pos and may not be invoked from
- *	interrupt context. It handles the deep magic required for
- *	onboard devices transparently.
- */
-unsigned char mca_device_read_pos(struct mca_device *mca_dev, int reg)
-{
-	struct mca_bus *mca_bus = to_mca_bus(mca_dev->dev.parent);
-
-	return mca_bus->f.mca_read_pos(mca_dev, reg);
-
-	return 	mca_dev->pos[reg];
-}
-EXPORT_SYMBOL(mca_device_read_pos);
-
-
-/**
- *	mca_device_write_pos - read POS register from card
- *	@mca_dev: device to write pos register to
- *	@reg:  register to write to
- *	@byte: byte to write to the POS registers
- *
- *	Store a POS value directly to the hardware. You should not
- *	normally need to use this function and should have a very good
- *	knowledge of MCA bus before you do so. Doing this wrongly can
- *	damage the hardware.
- *
- *	This function may not be used from interrupt context.
- *
- */
-void mca_device_write_pos(struct mca_device *mca_dev, int reg,
-			  unsigned char byte)
-{
-	struct mca_bus *mca_bus = to_mca_bus(mca_dev->dev.parent);
-
-	mca_bus->f.mca_write_pos(mca_dev, reg, byte);
-}
-EXPORT_SYMBOL(mca_device_write_pos);
-
-/**
- *	mca_device_transform_irq - transform the ADF obtained IRQ
- *	@mca_device: device whose irq needs transforming
- *	@irq: input irq from ADF
- *
- *	MCA Adapter Definition Files (ADF) contain irq, ioport, memory
- *	etc. definitions.  In systems with more than one bus, these need
- *	to be transformed through bus mapping functions to get the real
- *	system global quantities.
- *
- *	This function transforms the interrupt number and returns the
- *	transformed system global interrupt
- */
-int mca_device_transform_irq(struct mca_device *mca_dev, int irq)
-{
-	struct mca_bus *mca_bus = to_mca_bus(mca_dev->dev.parent);
-
-	return mca_bus->f.mca_transform_irq(mca_dev, irq);
-}
-EXPORT_SYMBOL(mca_device_transform_irq);
-
-/**
- *	mca_device_transform_ioport - transform the ADF obtained I/O port
- *	@mca_device: device whose port needs transforming
- *	@ioport: input I/O port from ADF
- *
- *	MCA Adapter Definition Files (ADF) contain irq, ioport, memory
- *	etc. definitions.  In systems with more than one bus, these need
- *	to be transformed through bus mapping functions to get the real
- *	system global quantities.
- *
- *	This function transforms the I/O port number and returns the
- *	transformed system global port number.
- *
- *	This transformation can be assumed to be linear for port ranges.
- */
-int mca_device_transform_ioport(struct mca_device *mca_dev, int port)
-{
-	struct mca_bus *mca_bus = to_mca_bus(mca_dev->dev.parent);
-
-	return mca_bus->f.mca_transform_ioport(mca_dev, port);
-}
-EXPORT_SYMBOL(mca_device_transform_ioport);
-
-/**
- *	mca_device_transform_memory - transform the ADF obtained memory
- *	@mca_device: device whose memory region needs transforming
- *	@mem: memory region start from ADF
- *
- *	MCA Adapter Definition Files (ADF) contain irq, ioport, memory
- *	etc. definitions.  In systems with more than one bus, these need
- *	to be transformed through bus mapping functions to get the real
- *	system global quantities.
- *
- *	This function transforms the memory region start and returns the
- *	transformed system global memory region (physical).
- *
- *	This transformation can be assumed to be linear for region ranges.
- */
-void *mca_device_transform_memory(struct mca_device *mca_dev, void *mem)
-{
-	struct mca_bus *mca_bus = to_mca_bus(mca_dev->dev.parent);
-
-	return mca_bus->f.mca_transform_memory(mca_dev, mem);
-}
-EXPORT_SYMBOL(mca_device_transform_memory);
-
-
-/**
- *	mca_device_claimed - check if claimed by driver
- *	@mca_dev:	device to check
- *
- *	Returns 1 if the slot has been claimed by a driver
- */
-
-int mca_device_claimed(struct mca_device *mca_dev)
-{
-	return mca_dev->driver_loaded;
-}
-EXPORT_SYMBOL(mca_device_claimed);
-
-/**
- *	mca_device_set_claim - set the claim value of the driver
- *	@mca_dev:	device to set value for
- *	@val:		claim value to set (1 claimed, 0 unclaimed)
- */
-void mca_device_set_claim(struct mca_device *mca_dev, int val)
-{
-	mca_dev->driver_loaded = val;
-}
-EXPORT_SYMBOL(mca_device_set_claim);
-
-/**
- *	mca_device_status - get the status of the device
- *	@mca_device:	device to get
- *
- *	returns an enumeration of the device status:
- *
- *	MCA_ADAPTER_NORMAL	adapter is OK.
- *	MCA_ADAPTER_NONE	no adapter at device (should never happen).
- *	MCA_ADAPTER_DISABLED	adapter is disabled.
- *	MCA_ADAPTER_ERROR	adapter cannot be initialised.
- */
-enum MCA_AdapterStatus mca_device_status(struct mca_device *mca_dev)
-{
-	return mca_dev->status;
-}
-EXPORT_SYMBOL(mca_device_status);
-
-/**
- *	mca_device_set_name - set the name of the device
- *	@mca_device:	device to set the name of
- *	@name:		name to set
- */
-void mca_device_set_name(struct mca_device *mca_dev, const char *name)
-{
-	if(!mca_dev)
-		return;
-
-	strlcpy(mca_dev->name, name, sizeof(mca_dev->name));
-}
-EXPORT_SYMBOL(mca_device_set_name);
diff --git a/drivers/mca/mca-driver.c b/drivers/mca/mca-driver.c
deleted file mode 100644
index 32cd39b..0000000
--- a/drivers/mca/mca-driver.c
+++ /dev/null
@@ -1,63 +0,0 @@
-/* -*- mode: c; c-basic-offset: 8 -*- */
-
-/*
- * MCA driver support functions for sysfs.
- *
- * (C) 2002 James Bottomley <James.Bottomley@HansenPartnership.com>
- *
-**-----------------------------------------------------------------------------
-**  
-**  This program is free software; you can redistribute it and/or modify
-**  it under the terms of the GNU General Public License as published by
-**  the Free Software Foundation; either version 2 of the License, or
-**  (at your option) any later version.
-**
-**  This program is distributed in the hope that it will be useful,
-**  but WITHOUT ANY WARRANTY; without even the implied warranty of
-**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-**  GNU General Public License for more details.
-**
-**  You should have received a copy of the GNU General Public License
-**  along with this program; if not, write to the Free Software
-**  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-**
-**-----------------------------------------------------------------------------
- */
-
-#include <linux/device.h>
-#include <linux/mca.h>
-#include <linux/module.h>
-
-int mca_register_driver(struct mca_driver *mca_drv)
-{
-	int r;
-
-	if (MCA_bus) {
-		mca_drv->driver.bus = &mca_bus_type;
-		if ((r = driver_register(&mca_drv->driver)) < 0)
-			return r;
-		mca_drv->integrated_id = 0;
-	}
-
-	return 0;
-}
-EXPORT_SYMBOL(mca_register_driver);
-
-int mca_register_driver_integrated(struct mca_driver *mca_driver,
-				   int integrated_id)
-{
-	int r = mca_register_driver(mca_driver);
-
-	if (!r)
-		mca_driver->integrated_id = integrated_id;
-
-	return r;
-}
-EXPORT_SYMBOL(mca_register_driver_integrated);
-
-void mca_unregister_driver(struct mca_driver *mca_drv)
-{
-	if (MCA_bus)
-		driver_unregister(&mca_drv->driver);
-}
-EXPORT_SYMBOL(mca_unregister_driver);
diff --git a/drivers/mca/mca-legacy.c b/drivers/mca/mca-legacy.c
deleted file mode 100644
index 494f0c2..0000000
--- a/drivers/mca/mca-legacy.c
+++ /dev/null
@@ -1,329 +0,0 @@
-/* -*- mode: c; c-basic-offset: 8 -*- */
-
-/*
- * MCA bus support functions for legacy (2.4) API.
- *
- * Legacy API means the API that operates in terms of MCA slot number
- *
- * (C) 2002 James Bottomley <James.Bottomley@HansenPartnership.com>
- *
-**-----------------------------------------------------------------------------
-**  
-**  This program is free software; you can redistribute it and/or modify
-**  it under the terms of the GNU General Public License as published by
-**  the Free Software Foundation; either version 2 of the License, or
-**  (at your option) any later version.
-**
-**  This program is distributed in the hope that it will be useful,
-**  but WITHOUT ANY WARRANTY; without even the implied warranty of
-**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-**  GNU General Public License for more details.
-**
-**  You should have received a copy of the GNU General Public License
-**  along with this program; if not, write to the Free Software
-**  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-**
-**-----------------------------------------------------------------------------
- */
-
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/mca-legacy.h>
-#include <asm/io.h>
-
-/* NOTE: This structure is stack allocated */
-struct mca_find_adapter_info {
-	int			id;
-	int			slot;
-	struct mca_device	*mca_dev;
-};
-
-/* The purpose of this iterator is to loop over all the devices and
- * find the one with the smallest slot number that's just greater than
- * or equal to the required slot with a matching id */
-static int mca_find_adapter_callback(struct device *dev, void *data)
-{
-	struct mca_find_adapter_info *info = data;
-	struct mca_device *mca_dev = to_mca_device(dev);
-
-	if(mca_dev->pos_id != info->id)
-		return 0;
-
-	if(mca_dev->slot < info->slot)
-		return 0;
-
-	if(!info->mca_dev || info->mca_dev->slot >= mca_dev->slot)
-		info->mca_dev = mca_dev;
-
-	return 0;
-}
-
-/**
- *	mca_find_adapter - scan for adapters
- *	@id:	MCA identification to search for
- *	@start:	starting slot
- *
- *	Search the MCA configuration for adapters matching the 16bit
- *	ID given. The first time it should be called with start as zero
- *	and then further calls made passing the return value of the
- *	previous call until %MCA_NOTFOUND is returned.
- *
- *	Disabled adapters are not reported.
- */
-
-int mca_find_adapter(int id, int start)
-{
-	struct mca_find_adapter_info info;
-
-	if(id == 0xffff)
-		return MCA_NOTFOUND;
-
-	info.slot = start;
-	info.id = id;
-	info.mca_dev = NULL;
-
-	for(;;) {
-		bus_for_each_dev(&mca_bus_type, NULL, &info, mca_find_adapter_callback);
-
-		if(info.mca_dev == NULL)
-			return MCA_NOTFOUND;
-
-		if(info.mca_dev->status != MCA_ADAPTER_DISABLED)
-			break;
-
-		/* OK, found adapter but it was disabled.  Go around
-		 * again, excluding the slot we just found */
-
-		info.slot = info.mca_dev->slot + 1;
-		info.mca_dev = NULL;
-	}
-		
-	return info.mca_dev->slot;
-}
-EXPORT_SYMBOL(mca_find_adapter);
-
-/*--------------------------------------------------------------------*/
-
-/**
- *	mca_find_unused_adapter - scan for unused adapters
- *	@id:	MCA identification to search for
- *	@start:	starting slot
- *
- *	Search the MCA configuration for adapters matching the 16bit
- *	ID given. The first time it should be called with start as zero
- *	and then further calls made passing the return value of the
- *	previous call until %MCA_NOTFOUND is returned.
- *
- *	Adapters that have been claimed by drivers and those that
- *	are disabled are not reported. This function thus allows a driver
- *	to scan for further cards when some may already be driven.
- */
-
-int mca_find_unused_adapter(int id, int start)
-{
-	struct mca_find_adapter_info info = { 0 };
-
-	if (!MCA_bus || id == 0xffff)
-		return MCA_NOTFOUND;
-
-	info.slot = start;
-	info.id = id;
-	info.mca_dev = NULL;
-
-	for(;;) {
-		bus_for_each_dev(&mca_bus_type, NULL, &info, mca_find_adapter_callback);
-
-		if(info.mca_dev == NULL)
-			return MCA_NOTFOUND;
-
-		if(info.mca_dev->status != MCA_ADAPTER_DISABLED
-		   && !info.mca_dev->driver_loaded)
-			break;
-
-		/* OK, found adapter but it was disabled or already in
-		 * use.  Go around again, excluding the slot we just
-		 * found */
-
-		info.slot = info.mca_dev->slot + 1;
-		info.mca_dev = NULL;
-	}
-		
-	return info.mca_dev->slot;
-}
-EXPORT_SYMBOL(mca_find_unused_adapter);
-
-/* NOTE: stack allocated structure */
-struct mca_find_device_by_slot_info {
-	int			slot;
-	struct mca_device 	*mca_dev;
-};
-
-static int mca_find_device_by_slot_callback(struct device *dev, void *data)
-{
-	struct mca_find_device_by_slot_info *info = data;
-	struct mca_device *mca_dev = to_mca_device(dev);
-
-	if(mca_dev->slot == info->slot)
-		info->mca_dev = mca_dev;
-
-	return 0;
-}
-
-struct mca_device *mca_find_device_by_slot(int slot)
-{
-	struct mca_find_device_by_slot_info info;
-
-	info.slot = slot;
-	info.mca_dev = NULL;
-
-	bus_for_each_dev(&mca_bus_type, NULL, &info, mca_find_device_by_slot_callback);
-
-	return info.mca_dev;
-}
-
-/**
- *	mca_read_stored_pos - read POS register from boot data
- *	@slot: slot number to read from
- *	@reg:  register to read from
- *
- *	Fetch a POS value that was stored at boot time by the kernel
- *	when it scanned the MCA space. The register value is returned.
- *	Missing or invalid registers report 0.
- */
-unsigned char mca_read_stored_pos(int slot, int reg)
-{
-	struct mca_device *mca_dev = mca_find_device_by_slot(slot);
-
-	if(!mca_dev)
-		return 0;
-
-	return mca_device_read_stored_pos(mca_dev, reg);
-}
-EXPORT_SYMBOL(mca_read_stored_pos);
-
-
-/**
- *	mca_read_pos - read POS register from card
- *	@slot: slot number to read from
- *	@reg:  register to read from
- *
- *	Fetch a POS value directly from the hardware to obtain the
- *	current value. This is much slower than mca_read_stored_pos and
- *	may not be invoked from interrupt context. It handles the
- *	deep magic required for onboard devices transparently.
- */
-
-unsigned char mca_read_pos(int slot, int reg)
-{
-	struct mca_device *mca_dev = mca_find_device_by_slot(slot);
-
-	if(!mca_dev)
-		return 0;
-
-	return mca_device_read_pos(mca_dev, reg);
-}
-EXPORT_SYMBOL(mca_read_pos);
-
-		
-/**
- *	mca_write_pos - read POS register from card
- *	@slot: slot number to read from
- *	@reg:  register to read from
- *	@byte: byte to write to the POS registers
- *
- *	Store a POS value directly from the hardware. You should not
- *	normally need to use this function and should have a very good
- *	knowledge of MCA bus before you do so. Doing this wrongly can
- *	damage the hardware.
- *
- *	This function may not be used from interrupt context.
- *
- *	Note that this a technically a Bad Thing, as IBM tech stuff says
- *	you should only set POS values through their utilities.
- *	However, some devices such as the 3c523 recommend that you write
- *	back some data to make sure the configuration is consistent.
- *	I'd say that IBM is right, but I like my drivers to work.
- *
- *	This function can't do checks to see if multiple devices end up
- *	with the same resources, so you might see magic smoke if someone
- *	screws up.
- */
-
-void mca_write_pos(int slot, int reg, unsigned char byte)
-{
-	struct mca_device *mca_dev = mca_find_device_by_slot(slot);
-
-	if(!mca_dev)
-		return;
-
-	mca_device_write_pos(mca_dev, reg, byte);
-}
-EXPORT_SYMBOL(mca_write_pos);
-
-/**
- *	mca_set_adapter_name - Set the description of the card
- *	@slot: slot to name
- *	@name: text string for the namen
- *
- *	This function sets the name reported via /proc for this
- *	adapter slot. This is for user information only. Setting a
- *	name deletes any previous name.
- */
-
-void mca_set_adapter_name(int slot, char* name)
-{
-	struct mca_device *mca_dev = mca_find_device_by_slot(slot);
-
-	if(!mca_dev)
-		return;
-
-	mca_device_set_name(mca_dev, name);
-}
-EXPORT_SYMBOL(mca_set_adapter_name);
-
-/**
- *	mca_mark_as_used - claim an MCA device
- *	@slot:	slot to claim
- *	FIXME:  should we make this threadsafe
- *
- *	Claim an MCA slot for a device driver. If the
- *	slot is already taken the function returns 1,
- *	if it is not taken it is claimed and 0 is
- *	returned.
- */
-
-int mca_mark_as_used(int slot)
-{
-	struct mca_device *mca_dev = mca_find_device_by_slot(slot);
-
-	if(!mca_dev)
-		/* FIXME: this is actually a severe error */
-		return 1;
-
-	if(mca_device_claimed(mca_dev))
-		return 1;
-
-	mca_device_set_claim(mca_dev, 1);
-
-	return 0;
-}
-EXPORT_SYMBOL(mca_mark_as_used);
-
-/**
- *	mca_mark_as_unused - release an MCA device
- *	@slot:	slot to claim
- *
- *	Release the slot for other drives to use.
- */
-
-void mca_mark_as_unused(int slot)
-{
-	struct mca_device *mca_dev = mca_find_device_by_slot(slot);
-
-	if(!mca_dev)
-		return;
-
-	mca_device_set_claim(mca_dev, 0);
-}
-EXPORT_SYMBOL(mca_mark_as_unused);
-
diff --git a/drivers/mca/mca-proc.c b/drivers/mca/mca-proc.c
deleted file mode 100644
index 81ea0d3..0000000
--- a/drivers/mca/mca-proc.c
+++ /dev/null
@@ -1,249 +0,0 @@
-/* -*- mode: c; c-basic-offset: 8 -*- */
-
-/*
- * MCA bus support functions for the proc fs.
- *
- * NOTE: this code *requires* the legacy MCA api.
- *
- * Legacy API means the API that operates in terms of MCA slot number
- *
- * (C) 2002 James Bottomley <James.Bottomley@HansenPartnership.com>
- *
-**-----------------------------------------------------------------------------
-**  
-**  This program is free software; you can redistribute it and/or modify
-**  it under the terms of the GNU General Public License as published by
-**  the Free Software Foundation; either version 2 of the License, or
-**  (at your option) any later version.
-**
-**  This program is distributed in the hope that it will be useful,
-**  but WITHOUT ANY WARRANTY; without even the implied warranty of
-**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-**  GNU General Public License for more details.
-**
-**  You should have received a copy of the GNU General Public License
-**  along with this program; if not, write to the Free Software
-**  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-**
-**-----------------------------------------------------------------------------
- */
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/proc_fs.h>
-#include <linux/mca.h>
-
-static int get_mca_info_helper(struct mca_device *mca_dev, char *page, int len)
-{
-	int j;
-
-	for(j=0; j<8; j++)
-		len += sprintf(page+len, "%02x ",
-			       mca_dev ? mca_dev->pos[j] : 0xff);
-	len += sprintf(page+len, " %s\n", mca_dev ? mca_dev->name : "");
-	return len;
-}
-
-static int get_mca_info(char *page, char **start, off_t off,
-			int count, int *eof, void *data)
-{
-	int i, len = 0;
-
-	if(MCA_bus) {
-		struct mca_device *mca_dev;
-		/* Format POS registers of eight MCA slots */
-
-		for(i=0; i<MCA_MAX_SLOT_NR; i++) {
-			mca_dev = mca_find_device_by_slot(i);
-
-			len += sprintf(page+len, "Slot %d: ", i+1);
-			len = get_mca_info_helper(mca_dev, page, len);
-		}
-
-		/* Format POS registers of integrated video subsystem */
-
-		mca_dev = mca_find_device_by_slot(MCA_INTEGVIDEO);
-		len += sprintf(page+len, "Video : ");
-		len = get_mca_info_helper(mca_dev, page, len);
-
-		/* Format POS registers of integrated SCSI subsystem */
-
-		mca_dev = mca_find_device_by_slot(MCA_INTEGSCSI);
-		len += sprintf(page+len, "SCSI  : ");
-		len = get_mca_info_helper(mca_dev, page, len);
-
-		/* Format POS registers of motherboard */
-
-		mca_dev = mca_find_device_by_slot(MCA_MOTHERBOARD);
-		len += sprintf(page+len, "Planar: ");
-		len = get_mca_info_helper(mca_dev, page, len);
-	} else {
-		/* Leave it empty if MCA not detected - this should *never*
-		 * happen!
-		 */
-	}
-
-	if (len <= off+count) *eof = 1;
-	*start = page + off;
-	len -= off;
-	if (len>count) len = count;
-	if (len<0) len = 0;
-	return len;
-}
-
-/*--------------------------------------------------------------------*/
-
-static int mca_default_procfn(char* buf, struct mca_device *mca_dev)
-{
-	int len = 0, i;
-	int slot = mca_dev->slot;
-
-	/* Print out the basic information */
-
-	if(slot < MCA_MAX_SLOT_NR) {
-		len += sprintf(buf+len, "Slot: %d\n", slot+1);
-	} else if(slot == MCA_INTEGSCSI) {
-		len += sprintf(buf+len, "Integrated SCSI Adapter\n");
-	} else if(slot == MCA_INTEGVIDEO) {
-		len += sprintf(buf+len, "Integrated Video Adapter\n");
-	} else if(slot == MCA_MOTHERBOARD) {
-		len += sprintf(buf+len, "Motherboard\n");
-	}
-	if (mca_dev->name[0]) {
-
-		/* Drivers might register a name without /proc handler... */
-
-		len += sprintf(buf+len, "Adapter Name: %s\n",
-			       mca_dev->name);
-	} else {
-		len += sprintf(buf+len, "Adapter Name: Unknown\n");
-	}
-	len += sprintf(buf+len, "Id: %02x%02x\n",
-		mca_dev->pos[1], mca_dev->pos[0]);
-	len += sprintf(buf+len, "Enabled: %s\nPOS: ",
-		mca_device_status(mca_dev) == MCA_ADAPTER_NORMAL ?
-			"Yes" : "No");
-	for(i=0; i<8; i++) {
-		len += sprintf(buf+len, "%02x ", mca_dev->pos[i]);
-	}
-	len += sprintf(buf+len, "\nDriver Installed: %s",
-		mca_device_claimed(mca_dev) ? "Yes" : "No");
-	buf[len++] = '\n';
-	buf[len] = 0;
-
-	return len;
-} /* mca_default_procfn() */
-
-static int get_mca_machine_info(char* page, char **start, off_t off,
-				 int count, int *eof, void *data)
-{
-	int len = 0;
-
-	len += sprintf(page+len, "Model Id: 0x%x\n", machine_id);
-	len += sprintf(page+len, "Submodel Id: 0x%x\n", machine_submodel_id);
-	len += sprintf(page+len, "BIOS Revision: 0x%x\n", BIOS_revision);
-
-	if (len <= off+count) *eof = 1;
-	*start = page + off;
-	len -= off;
-	if (len>count) len = count;
-	if (len<0) len = 0;
-	return len;
-}
-
-static int mca_read_proc(char *page, char **start, off_t off,
-				 int count, int *eof, void *data)
-{
-	struct mca_device *mca_dev = (struct mca_device *)data;
-	int len = 0;
-
-	/* Get the standard info */
-
-	len = mca_default_procfn(page, mca_dev);
-
-	/* Do any device-specific processing, if there is any */
-
-	if(mca_dev->procfn) {
-		len += mca_dev->procfn(page+len, mca_dev->slot,
-				       mca_dev->proc_dev);
-	}
-	if (len <= off+count) *eof = 1;
-	*start = page + off;
-	len -= off;
-	if (len>count) len = count;
-	if (len<0) len = 0;
-	return len;
-} /* mca_read_proc() */
-
-/*--------------------------------------------------------------------*/
-
-void __init mca_do_proc_init(void)
-{
-	int i;
-	struct proc_dir_entry *proc_mca;
-	struct proc_dir_entry* node = NULL;
-	struct mca_device *mca_dev;
-
-	proc_mca = proc_mkdir("mca", NULL);
-	create_proc_read_entry("pos",0,proc_mca,get_mca_info,NULL);
-	create_proc_read_entry("machine",0,proc_mca,get_mca_machine_info,NULL);
-
-	/* Initialize /proc/mca entries for existing adapters */
-
-	for(i = 0; i < MCA_NUMADAPTERS; i++) {
-		enum MCA_AdapterStatus status;
-		mca_dev = mca_find_device_by_slot(i);
-		if(!mca_dev)
-			continue;
-
-		mca_dev->procfn = NULL;
-
-		if(i < MCA_MAX_SLOT_NR) sprintf(mca_dev->procname,"slot%d", i+1);
-		else if(i == MCA_INTEGVIDEO) sprintf(mca_dev->procname,"video");
-		else if(i == MCA_INTEGSCSI) sprintf(mca_dev->procname,"scsi");
-		else if(i == MCA_MOTHERBOARD) sprintf(mca_dev->procname,"planar");
-
-		status = mca_device_status(mca_dev);
-		if (status != MCA_ADAPTER_NORMAL &&
-		    status != MCA_ADAPTER_DISABLED)
-			continue;
-
-		node = create_proc_read_entry(mca_dev->procname, 0, proc_mca,
-					      mca_read_proc, (void *)mca_dev);
-
-		if(node == NULL) {
-			printk("Failed to allocate memory for MCA proc-entries!");
-			return;
-		}
-	}
-
-} /* mca_do_proc_init() */
-
-/**
- *	mca_set_adapter_procfn - Set the /proc callback
- *	@slot: slot to configure
- *	@procfn: callback function to call for /proc
- *	@dev: device information passed to the callback
- *
- *	This sets up an information callback for /proc/mca/slot?.  The
- *	function is called with the buffer, slot, and device pointer (or
- *	some equally informative context information, or nothing, if you
- *	prefer), and is expected to put useful information into the
- *	buffer.  The adapter name, ID, and POS registers get printed
- *	before this is called though, so don't do it again.
- *
- *	This should be called with a %NULL @procfn when a module
- *	unregisters, thus preventing kernel crashes and other such
- *	nastiness.
- */
-
-void mca_set_adapter_procfn(int slot, MCA_ProcFn procfn, void* proc_dev)
-{
-	struct mca_device *mca_dev = mca_find_device_by_slot(slot);
-
-	if(!mca_dev)
-		return;
-
-	mca_dev->procfn = procfn;
-	mca_dev->proc_dev = proc_dev;
-}
-EXPORT_SYMBOL(mca_set_adapter_procfn);
diff --git a/drivers/md/bitmap.c b/drivers/md/bitmap.c
index 17e2b47..15dbe03 100644
--- a/drivers/md/bitmap.c
+++ b/drivers/md/bitmap.c
@@ -45,7 +45,7 @@
  * if we find our page, we increment the page's refcount so that it stays
  * allocated while we're using it
  */
-static int bitmap_checkpage(struct bitmap *bitmap,
+static int bitmap_checkpage(struct bitmap_counts *bitmap,
 			    unsigned long page, int create)
 __releases(bitmap->lock)
 __acquires(bitmap->lock)
@@ -76,8 +76,7 @@
 	spin_lock_irq(&bitmap->lock);
 
 	if (mappage == NULL) {
-		pr_debug("%s: bitmap map page allocation failed, hijacking\n",
-			 bmname(bitmap));
+		pr_debug("md/bitmap: map page allocation failed, hijacking\n");
 		/* failed - set the hijacked flag so that we can use the
 		 * pointer as a counter */
 		if (!bitmap->bp[page].map)
@@ -100,7 +99,7 @@
 /* if page is completely empty, put it back on the free list, or dealloc it */
 /* if page was hijacked, unmark the flag so it might get alloced next time */
 /* Note: lock should be held when calling this */
-static void bitmap_checkfree(struct bitmap *bitmap, unsigned long page)
+static void bitmap_checkfree(struct bitmap_counts *bitmap, unsigned long page)
 {
 	char *ptr;
 
@@ -130,22 +129,14 @@
  */
 
 /* IO operations when bitmap is stored near all superblocks */
-static struct page *read_sb_page(struct mddev *mddev, loff_t offset,
-				 struct page *page,
-				 unsigned long index, int size)
+static int read_sb_page(struct mddev *mddev, loff_t offset,
+			struct page *page,
+			unsigned long index, int size)
 {
 	/* choose a good rdev and read the page from there */
 
 	struct md_rdev *rdev;
 	sector_t target;
-	int did_alloc = 0;
-
-	if (!page) {
-		page = alloc_page(GFP_KERNEL);
-		if (!page)
-			return ERR_PTR(-ENOMEM);
-		did_alloc = 1;
-	}
 
 	rdev_for_each(rdev, mddev) {
 		if (! test_bit(In_sync, &rdev->flags)
@@ -158,15 +149,10 @@
 				 roundup(size, bdev_logical_block_size(rdev->bdev)),
 				 page, READ, true)) {
 			page->index = index;
-			attach_page_buffers(page, NULL); /* so that free_buffer will
-							  * quietly no-op */
-			return page;
+			return 0;
 		}
 	}
-	if (did_alloc)
-		put_page(page);
-	return ERR_PTR(-EIO);
-
+	return -EIO;
 }
 
 static struct md_rdev *next_active_rdev(struct md_rdev *rdev, struct mddev *mddev)
@@ -208,6 +194,7 @@
 	struct md_rdev *rdev = NULL;
 	struct block_device *bdev;
 	struct mddev *mddev = bitmap->mddev;
+	struct bitmap_storage *store = &bitmap->storage;
 
 	while ((rdev = next_active_rdev(rdev, mddev)) != NULL) {
 		int size = PAGE_SIZE;
@@ -215,9 +202,13 @@
 
 		bdev = (rdev->meta_bdev) ? rdev->meta_bdev : rdev->bdev;
 
-		if (page->index == bitmap->file_pages-1)
-			size = roundup(bitmap->last_page_size,
+		if (page->index == store->file_pages-1) {
+			int last_page_size = store->bytes & (PAGE_SIZE-1);
+			if (last_page_size == 0)
+				last_page_size = PAGE_SIZE;
+			size = roundup(last_page_size,
 				       bdev_logical_block_size(bdev));
+		}
 		/* Just make sure we aren't corrupting data or
 		 * metadata
 		 */
@@ -276,10 +267,10 @@
 {
 	struct buffer_head *bh;
 
-	if (bitmap->file == NULL) {
+	if (bitmap->storage.file == NULL) {
 		switch (write_sb_page(bitmap, page, wait)) {
 		case -EINVAL:
-			bitmap->flags |= BITMAP_WRITE_ERROR;
+			set_bit(BITMAP_WRITE_ERROR, &bitmap->flags);
 		}
 	} else {
 
@@ -297,20 +288,16 @@
 			wait_event(bitmap->write_wait,
 				   atomic_read(&bitmap->pending_writes)==0);
 	}
-	if (bitmap->flags & BITMAP_WRITE_ERROR)
+	if (test_bit(BITMAP_WRITE_ERROR, &bitmap->flags))
 		bitmap_file_kick(bitmap);
 }
 
 static void end_bitmap_write(struct buffer_head *bh, int uptodate)
 {
 	struct bitmap *bitmap = bh->b_private;
-	unsigned long flags;
 
-	if (!uptodate) {
-		spin_lock_irqsave(&bitmap->lock, flags);
-		bitmap->flags |= BITMAP_WRITE_ERROR;
-		spin_unlock_irqrestore(&bitmap->lock, flags);
-	}
+	if (!uptodate)
+		set_bit(BITMAP_WRITE_ERROR, &bitmap->flags);
 	if (atomic_dec_and_test(&bitmap->pending_writes))
 		wake_up(&bitmap->write_wait);
 }
@@ -325,8 +312,12 @@
 }
 static void free_buffers(struct page *page)
 {
-	struct buffer_head *bh = page_buffers(page);
+	struct buffer_head *bh;
 
+	if (!PagePrivate(page))
+		return;
+
+	bh = page_buffers(page);
 	while (bh) {
 		struct buffer_head *next = bh->b_this_page;
 		free_buffer_head(bh);
@@ -343,11 +334,12 @@
  * This usage is similar to how swap files are handled, and allows us
  * to write to a file with no concerns of memory allocation failing.
  */
-static struct page *read_page(struct file *file, unsigned long index,
-			      struct bitmap *bitmap,
-			      unsigned long count)
+static int read_page(struct file *file, unsigned long index,
+		     struct bitmap *bitmap,
+		     unsigned long count,
+		     struct page *page)
 {
-	struct page *page = NULL;
+	int ret = 0;
 	struct inode *inode = file->f_path.dentry->d_inode;
 	struct buffer_head *bh;
 	sector_t block;
@@ -355,16 +347,9 @@
 	pr_debug("read bitmap file (%dB @ %llu)\n", (int)PAGE_SIZE,
 		 (unsigned long long)index << PAGE_SHIFT);
 
-	page = alloc_page(GFP_KERNEL);
-	if (!page)
-		page = ERR_PTR(-ENOMEM);
-	if (IS_ERR(page))
-		goto out;
-
 	bh = alloc_page_buffers(page, 1<<inode->i_blkbits, 0);
 	if (!bh) {
-		put_page(page);
-		page = ERR_PTR(-ENOMEM);
+		ret = -ENOMEM;
 		goto out;
 	}
 	attach_page_buffers(page, bh);
@@ -376,8 +361,7 @@
 			bh->b_blocknr = bmap(inode, block);
 			if (bh->b_blocknr == 0) {
 				/* Cannot use this file! */
-				free_buffers(page);
-				page = ERR_PTR(-EINVAL);
+				ret = -EINVAL;
 				goto out;
 			}
 			bh->b_bdev = inode->i_sb->s_bdev;
@@ -400,17 +384,15 @@
 
 	wait_event(bitmap->write_wait,
 		   atomic_read(&bitmap->pending_writes)==0);
-	if (bitmap->flags & BITMAP_WRITE_ERROR) {
-		free_buffers(page);
-		page = ERR_PTR(-EIO);
-	}
+	if (test_bit(BITMAP_WRITE_ERROR, &bitmap->flags))
+		ret = -EIO;
 out:
-	if (IS_ERR(page))
-		printk(KERN_ALERT "md: bitmap read error: (%dB @ %llu): %ld\n",
+	if (ret)
+		printk(KERN_ALERT "md: bitmap read error: (%dB @ %llu): %d\n",
 			(int)PAGE_SIZE,
 			(unsigned long long)index << PAGE_SHIFT,
-			PTR_ERR(page));
-	return page;
+			ret);
+	return ret;
 }
 
 /*
@@ -426,9 +408,9 @@
 		return;
 	if (bitmap->mddev->bitmap_info.external)
 		return;
-	if (!bitmap->sb_page) /* no superblock */
+	if (!bitmap->storage.sb_page) /* no superblock */
 		return;
-	sb = kmap_atomic(bitmap->sb_page);
+	sb = kmap_atomic(bitmap->storage.sb_page);
 	sb->events = cpu_to_le64(bitmap->mddev->events);
 	if (bitmap->mddev->events < bitmap->events_cleared)
 		/* rocking back to read-only */
@@ -438,8 +420,13 @@
 	/* Just in case these have been changed via sysfs: */
 	sb->daemon_sleep = cpu_to_le32(bitmap->mddev->bitmap_info.daemon_sleep/HZ);
 	sb->write_behind = cpu_to_le32(bitmap->mddev->bitmap_info.max_write_behind);
+	/* This might have been changed by a reshape */
+	sb->sync_size = cpu_to_le64(bitmap->mddev->resync_max_sectors);
+	sb->chunksize = cpu_to_le32(bitmap->mddev->bitmap_info.chunksize);
+	sb->sectors_reserved = cpu_to_le32(bitmap->mddev->
+					   bitmap_info.space);
 	kunmap_atomic(sb);
-	write_page(bitmap, bitmap->sb_page, 1);
+	write_page(bitmap, bitmap->storage.sb_page, 1);
 }
 
 /* print out the bitmap file superblock */
@@ -447,9 +434,9 @@
 {
 	bitmap_super_t *sb;
 
-	if (!bitmap || !bitmap->sb_page)
+	if (!bitmap || !bitmap->storage.sb_page)
 		return;
-	sb = kmap_atomic(bitmap->sb_page);
+	sb = kmap_atomic(bitmap->storage.sb_page);
 	printk(KERN_DEBUG "%s: bitmap file superblock:\n", bmname(bitmap));
 	printk(KERN_DEBUG "         magic: %08x\n", le32_to_cpu(sb->magic));
 	printk(KERN_DEBUG "       version: %d\n", le32_to_cpu(sb->version));
@@ -488,15 +475,15 @@
 	unsigned long chunksize, daemon_sleep, write_behind;
 	int err = -EINVAL;
 
-	bitmap->sb_page = alloc_page(GFP_KERNEL);
-	if (IS_ERR(bitmap->sb_page)) {
-		err = PTR_ERR(bitmap->sb_page);
-		bitmap->sb_page = NULL;
+	bitmap->storage.sb_page = alloc_page(GFP_KERNEL);
+	if (IS_ERR(bitmap->storage.sb_page)) {
+		err = PTR_ERR(bitmap->storage.sb_page);
+		bitmap->storage.sb_page = NULL;
 		return err;
 	}
-	bitmap->sb_page->index = 0;
+	bitmap->storage.sb_page->index = 0;
 
-	sb = kmap_atomic(bitmap->sb_page);
+	sb = kmap_atomic(bitmap->storage.sb_page);
 
 	sb->magic = cpu_to_le32(BITMAP_MAGIC);
 	sb->version = cpu_to_le32(BITMAP_MAJOR_HI);
@@ -534,8 +521,8 @@
 
 	memcpy(sb->uuid, bitmap->mddev->uuid, 16);
 
-	bitmap->flags |= BITMAP_STALE;
-	sb->state |= cpu_to_le32(BITMAP_STALE);
+	set_bit(BITMAP_STALE, &bitmap->flags);
+	sb->state = cpu_to_le32(bitmap->flags);
 	bitmap->events_cleared = bitmap->mddev->events;
 	sb->events_cleared = cpu_to_le64(bitmap->mddev->events);
 
@@ -551,31 +538,45 @@
 	bitmap_super_t *sb;
 	unsigned long chunksize, daemon_sleep, write_behind;
 	unsigned long long events;
+	unsigned long sectors_reserved = 0;
 	int err = -EINVAL;
+	struct page *sb_page;
 
+	if (!bitmap->storage.file && !bitmap->mddev->bitmap_info.offset) {
+		chunksize = 128 * 1024 * 1024;
+		daemon_sleep = 5 * HZ;
+		write_behind = 0;
+		set_bit(BITMAP_STALE, &bitmap->flags);
+		err = 0;
+		goto out_no_sb;
+	}
 	/* page 0 is the superblock, read it... */
-	if (bitmap->file) {
-		loff_t isize = i_size_read(bitmap->file->f_mapping->host);
+	sb_page = alloc_page(GFP_KERNEL);
+	if (!sb_page)
+		return -ENOMEM;
+	bitmap->storage.sb_page = sb_page;
+
+	if (bitmap->storage.file) {
+		loff_t isize = i_size_read(bitmap->storage.file->f_mapping->host);
 		int bytes = isize > PAGE_SIZE ? PAGE_SIZE : isize;
 
-		bitmap->sb_page = read_page(bitmap->file, 0, bitmap, bytes);
+		err = read_page(bitmap->storage.file, 0,
+				bitmap, bytes, sb_page);
 	} else {
-		bitmap->sb_page = read_sb_page(bitmap->mddev,
-					       bitmap->mddev->bitmap_info.offset,
-					       NULL,
-					       0, sizeof(bitmap_super_t));
+		err = read_sb_page(bitmap->mddev,
+				   bitmap->mddev->bitmap_info.offset,
+				   sb_page,
+				   0, sizeof(bitmap_super_t));
 	}
-	if (IS_ERR(bitmap->sb_page)) {
-		err = PTR_ERR(bitmap->sb_page);
-		bitmap->sb_page = NULL;
+	if (err)
 		return err;
-	}
 
-	sb = kmap_atomic(bitmap->sb_page);
+	sb = kmap_atomic(sb_page);
 
 	chunksize = le32_to_cpu(sb->chunksize);
 	daemon_sleep = le32_to_cpu(sb->daemon_sleep) * HZ;
 	write_behind = le32_to_cpu(sb->write_behind);
+	sectors_reserved = le32_to_cpu(sb->sectors_reserved);
 
 	/* verify that the bitmap-specific fields are valid */
 	if (sb->magic != cpu_to_le32(BITMAP_MAGIC))
@@ -618,60 +619,32 @@
 			       "-- forcing full recovery\n",
 			       bmname(bitmap), events,
 			       (unsigned long long) bitmap->mddev->events);
-			sb->state |= cpu_to_le32(BITMAP_STALE);
+			set_bit(BITMAP_STALE, &bitmap->flags);
 		}
 	}
 
 	/* assign fields using values from superblock */
-	bitmap->mddev->bitmap_info.chunksize = chunksize;
-	bitmap->mddev->bitmap_info.daemon_sleep = daemon_sleep;
-	bitmap->mddev->bitmap_info.max_write_behind = write_behind;
 	bitmap->flags |= le32_to_cpu(sb->state);
 	if (le32_to_cpu(sb->version) == BITMAP_MAJOR_HOSTENDIAN)
-		bitmap->flags |= BITMAP_HOSTENDIAN;
+		set_bit(BITMAP_HOSTENDIAN, &bitmap->flags);
 	bitmap->events_cleared = le64_to_cpu(sb->events_cleared);
-	if (bitmap->flags & BITMAP_STALE)
-		bitmap->events_cleared = bitmap->mddev->events;
 	err = 0;
 out:
 	kunmap_atomic(sb);
+out_no_sb:
+	if (test_bit(BITMAP_STALE, &bitmap->flags))
+		bitmap->events_cleared = bitmap->mddev->events;
+	bitmap->mddev->bitmap_info.chunksize = chunksize;
+	bitmap->mddev->bitmap_info.daemon_sleep = daemon_sleep;
+	bitmap->mddev->bitmap_info.max_write_behind = write_behind;
+	if (bitmap->mddev->bitmap_info.space == 0 ||
+	    bitmap->mddev->bitmap_info.space > sectors_reserved)
+		bitmap->mddev->bitmap_info.space = sectors_reserved;
 	if (err)
 		bitmap_print_sb(bitmap);
 	return err;
 }
 
-enum bitmap_mask_op {
-	MASK_SET,
-	MASK_UNSET
-};
-
-/* record the state of the bitmap in the superblock.  Return the old value */
-static int bitmap_mask_state(struct bitmap *bitmap, enum bitmap_state bits,
-			     enum bitmap_mask_op op)
-{
-	bitmap_super_t *sb;
-	int old;
-
-	if (!bitmap->sb_page) /* can't set the state */
-		return 0;
-	sb = kmap_atomic(bitmap->sb_page);
-	old = le32_to_cpu(sb->state) & bits;
-	switch (op) {
-	case MASK_SET:
-		sb->state |= cpu_to_le32(bits);
-		bitmap->flags |= bits;
-		break;
-	case MASK_UNSET:
-		sb->state &= cpu_to_le32(~bits);
-		bitmap->flags &= ~bits;
-		break;
-	default:
-		BUG();
-	}
-	kunmap_atomic(sb);
-	return old;
-}
-
 /*
  * general bitmap file operations
  */
@@ -683,17 +656,19 @@
  * file a page at a time. There's a superblock at the start of the file.
  */
 /* calculate the index of the page that contains this bit */
-static inline unsigned long file_page_index(struct bitmap *bitmap, unsigned long chunk)
+static inline unsigned long file_page_index(struct bitmap_storage *store,
+					    unsigned long chunk)
 {
-	if (!bitmap->mddev->bitmap_info.external)
+	if (store->sb_page)
 		chunk += sizeof(bitmap_super_t) << 3;
 	return chunk >> PAGE_BIT_SHIFT;
 }
 
 /* calculate the (bit) offset of this bit within a page */
-static inline unsigned long file_page_offset(struct bitmap *bitmap, unsigned long chunk)
+static inline unsigned long file_page_offset(struct bitmap_storage *store,
+					     unsigned long chunk)
 {
-	if (!bitmap->mddev->bitmap_info.external)
+	if (store->sb_page)
 		chunk += sizeof(bitmap_super_t) << 3;
 	return chunk & (PAGE_BITS - 1);
 }
@@ -705,57 +680,86 @@
  * 1 page (e.g., x86) or less than 1 page -- so the bitmap might start on page
  * 0 or page 1
  */
-static inline struct page *filemap_get_page(struct bitmap *bitmap,
+static inline struct page *filemap_get_page(struct bitmap_storage *store,
 					    unsigned long chunk)
 {
-	if (file_page_index(bitmap, chunk) >= bitmap->file_pages)
+	if (file_page_index(store, chunk) >= store->file_pages)
 		return NULL;
-	return bitmap->filemap[file_page_index(bitmap, chunk)
-			       - file_page_index(bitmap, 0)];
+	return store->filemap[file_page_index(store, chunk)
+			      - file_page_index(store, 0)];
 }
 
-static void bitmap_file_unmap(struct bitmap *bitmap)
+static int bitmap_storage_alloc(struct bitmap_storage *store,
+				unsigned long chunks, int with_super)
+{
+	int pnum;
+	unsigned long num_pages;
+	unsigned long bytes;
+
+	bytes = DIV_ROUND_UP(chunks, 8);
+	if (with_super)
+		bytes += sizeof(bitmap_super_t);
+
+	num_pages = DIV_ROUND_UP(bytes, PAGE_SIZE);
+
+	store->filemap = kmalloc(sizeof(struct page *)
+				 * num_pages, GFP_KERNEL);
+	if (!store->filemap)
+		return -ENOMEM;
+
+	if (with_super && !store->sb_page) {
+		store->sb_page = alloc_page(GFP_KERNEL|__GFP_ZERO);
+		if (store->sb_page == NULL)
+			return -ENOMEM;
+		store->sb_page->index = 0;
+	}
+	pnum = 0;
+	if (store->sb_page) {
+		store->filemap[0] = store->sb_page;
+		pnum = 1;
+	}
+	for ( ; pnum < num_pages; pnum++) {
+		store->filemap[pnum] = alloc_page(GFP_KERNEL|__GFP_ZERO);
+		if (!store->filemap[pnum]) {
+			store->file_pages = pnum;
+			return -ENOMEM;
+		}
+		store->filemap[pnum]->index = pnum;
+	}
+	store->file_pages = pnum;
+
+	/* We need 4 bits per page, rounded up to a multiple
+	 * of sizeof(unsigned long) */
+	store->filemap_attr = kzalloc(
+		roundup(DIV_ROUND_UP(num_pages*4, 8), sizeof(unsigned long)),
+		GFP_KERNEL);
+	if (!store->filemap_attr)
+		return -ENOMEM;
+
+	store->bytes = bytes;
+
+	return 0;
+}
+
+static void bitmap_file_unmap(struct bitmap_storage *store)
 {
 	struct page **map, *sb_page;
-	unsigned long *attr;
 	int pages;
-	unsigned long flags;
+	struct file *file;
 
-	spin_lock_irqsave(&bitmap->lock, flags);
-	map = bitmap->filemap;
-	bitmap->filemap = NULL;
-	attr = bitmap->filemap_attr;
-	bitmap->filemap_attr = NULL;
-	pages = bitmap->file_pages;
-	bitmap->file_pages = 0;
-	sb_page = bitmap->sb_page;
-	bitmap->sb_page = NULL;
-	spin_unlock_irqrestore(&bitmap->lock, flags);
+	file = store->file;
+	map = store->filemap;
+	pages = store->file_pages;
+	sb_page = store->sb_page;
 
 	while (pages--)
 		if (map[pages] != sb_page) /* 0 is sb_page, release it below */
 			free_buffers(map[pages]);
 	kfree(map);
-	kfree(attr);
+	kfree(store->filemap_attr);
 
 	if (sb_page)
 		free_buffers(sb_page);
-}
-
-static void bitmap_file_put(struct bitmap *bitmap)
-{
-	struct file *file;
-	unsigned long flags;
-
-	spin_lock_irqsave(&bitmap->lock, flags);
-	file = bitmap->file;
-	bitmap->file = NULL;
-	spin_unlock_irqrestore(&bitmap->lock, flags);
-
-	if (file)
-		wait_event(bitmap->write_wait,
-			   atomic_read(&bitmap->pending_writes)==0);
-	bitmap_file_unmap(bitmap);
 
 	if (file) {
 		struct inode *inode = file->f_path.dentry->d_inode;
@@ -773,14 +777,14 @@
 {
 	char *path, *ptr = NULL;
 
-	if (bitmap_mask_state(bitmap, BITMAP_STALE, MASK_SET) == 0) {
+	if (!test_and_set_bit(BITMAP_STALE, &bitmap->flags)) {
 		bitmap_update_sb(bitmap);
 
-		if (bitmap->file) {
+		if (bitmap->storage.file) {
 			path = kmalloc(PAGE_SIZE, GFP_KERNEL);
 			if (path)
-				ptr = d_path(&bitmap->file->f_path, path,
-					     PAGE_SIZE);
+				ptr = d_path(&bitmap->storage.file->f_path,
+					     path, PAGE_SIZE);
 
 			printk(KERN_ALERT
 			      "%s: kicking failed bitmap file %s from array!\n",
@@ -792,10 +796,6 @@
 			       "%s: disabling internal bitmap due to errors\n",
 			       bmname(bitmap));
 	}
-
-	bitmap_file_put(bitmap);
-
-	return;
 }
 
 enum bitmap_page_attr {
@@ -805,24 +805,30 @@
 	BITMAP_PAGE_NEEDWRITE = 2, /* there are cleared bits that need to be synced */
 };
 
-static inline void set_page_attr(struct bitmap *bitmap, struct page *page,
-				enum bitmap_page_attr attr)
+static inline void set_page_attr(struct bitmap *bitmap, int pnum,
+				 enum bitmap_page_attr attr)
 {
-	__set_bit((page->index<<2) + attr, bitmap->filemap_attr);
+	set_bit((pnum<<2) + attr, bitmap->storage.filemap_attr);
 }
 
-static inline void clear_page_attr(struct bitmap *bitmap, struct page *page,
-				enum bitmap_page_attr attr)
+static inline void clear_page_attr(struct bitmap *bitmap, int pnum,
+				   enum bitmap_page_attr attr)
 {
-	__clear_bit((page->index<<2) + attr, bitmap->filemap_attr);
+	clear_bit((pnum<<2) + attr, bitmap->storage.filemap_attr);
 }
 
-static inline unsigned long test_page_attr(struct bitmap *bitmap, struct page *page,
+static inline int test_page_attr(struct bitmap *bitmap, int pnum,
+				 enum bitmap_page_attr attr)
+{
+	return test_bit((pnum<<2) + attr, bitmap->storage.filemap_attr);
+}
+
+static inline int test_and_clear_page_attr(struct bitmap *bitmap, int pnum,
 					   enum bitmap_page_attr attr)
 {
-	return test_bit((page->index<<2) + attr, bitmap->filemap_attr);
+	return test_and_clear_bit((pnum<<2) + attr,
+				  bitmap->storage.filemap_attr);
 }
-
 /*
  * bitmap_file_set_bit -- called before performing a write to the md device
  * to set (and eventually sync) a particular bit in the bitmap file
@@ -835,26 +841,46 @@
 	unsigned long bit;
 	struct page *page;
 	void *kaddr;
-	unsigned long chunk = block >> bitmap->chunkshift;
+	unsigned long chunk = block >> bitmap->counts.chunkshift;
 
-	if (!bitmap->filemap)
-		return;
-
-	page = filemap_get_page(bitmap, chunk);
+	page = filemap_get_page(&bitmap->storage, chunk);
 	if (!page)
 		return;
-	bit = file_page_offset(bitmap, chunk);
+	bit = file_page_offset(&bitmap->storage, chunk);
 
 	/* set the bit */
 	kaddr = kmap_atomic(page);
-	if (bitmap->flags & BITMAP_HOSTENDIAN)
+	if (test_bit(BITMAP_HOSTENDIAN, &bitmap->flags))
 		set_bit(bit, kaddr);
 	else
-		__set_bit_le(bit, kaddr);
+		test_and_set_bit_le(bit, kaddr);
 	kunmap_atomic(kaddr);
 	pr_debug("set file bit %lu page %lu\n", bit, page->index);
 	/* record page number so it gets flushed to disk when unplug occurs */
-	set_page_attr(bitmap, page, BITMAP_PAGE_DIRTY);
+	set_page_attr(bitmap, page->index, BITMAP_PAGE_DIRTY);
+}
+
+static void bitmap_file_clear_bit(struct bitmap *bitmap, sector_t block)
+{
+	unsigned long bit;
+	struct page *page;
+	void *paddr;
+	unsigned long chunk = block >> bitmap->counts.chunkshift;
+
+	page = filemap_get_page(&bitmap->storage, chunk);
+	if (!page)
+		return;
+	bit = file_page_offset(&bitmap->storage, chunk);
+	paddr = kmap_atomic(page);
+	if (test_bit(BITMAP_HOSTENDIAN, &bitmap->flags))
+		clear_bit(bit, paddr);
+	else
+		test_and_clear_bit_le(bit, paddr);
+	kunmap_atomic(paddr);
+	if (!test_page_attr(bitmap, page->index, BITMAP_PAGE_NEEDWRITE)) {
+		set_page_attr(bitmap, page->index, BITMAP_PAGE_PENDING);
+		bitmap->allclean = 0;
+	}
 }
 
 /* this gets called when the md device is ready to unplug its underlying
@@ -862,42 +888,37 @@
  * sync the dirty pages of the bitmap file to disk */
 void bitmap_unplug(struct bitmap *bitmap)
 {
-	unsigned long i, flags;
+	unsigned long i;
 	int dirty, need_write;
-	struct page *page;
 	int wait = 0;
 
-	if (!bitmap)
+	if (!bitmap || !bitmap->storage.filemap ||
+	    test_bit(BITMAP_STALE, &bitmap->flags))
 		return;
 
 	/* look at each page to see if there are any set bits that need to be
 	 * flushed out to disk */
-	for (i = 0; i < bitmap->file_pages; i++) {
-		spin_lock_irqsave(&bitmap->lock, flags);
-		if (!bitmap->filemap) {
-			spin_unlock_irqrestore(&bitmap->lock, flags);
+	for (i = 0; i < bitmap->storage.file_pages; i++) {
+		if (!bitmap->storage.filemap)
 			return;
+		dirty = test_and_clear_page_attr(bitmap, i, BITMAP_PAGE_DIRTY);
+		need_write = test_and_clear_page_attr(bitmap, i,
+						      BITMAP_PAGE_NEEDWRITE);
+		if (dirty || need_write) {
+			clear_page_attr(bitmap, i, BITMAP_PAGE_PENDING);
+			write_page(bitmap, bitmap->storage.filemap[i], 0);
 		}
-		page = bitmap->filemap[i];
-		dirty = test_page_attr(bitmap, page, BITMAP_PAGE_DIRTY);
-		need_write = test_page_attr(bitmap, page, BITMAP_PAGE_NEEDWRITE);
-		clear_page_attr(bitmap, page, BITMAP_PAGE_DIRTY);
-		clear_page_attr(bitmap, page, BITMAP_PAGE_NEEDWRITE);
 		if (dirty)
 			wait = 1;
-		spin_unlock_irqrestore(&bitmap->lock, flags);
-
-		if (dirty || need_write)
-			write_page(bitmap, page, 0);
 	}
 	if (wait) { /* if any writes were performed, we need to wait on them */
-		if (bitmap->file)
+		if (bitmap->storage.file)
 			wait_event(bitmap->write_wait,
 				   atomic_read(&bitmap->pending_writes)==0);
 		else
 			md_super_wait(bitmap->mddev);
 	}
-	if (bitmap->flags & BITMAP_WRITE_ERROR)
+	if (test_bit(BITMAP_WRITE_ERROR, &bitmap->flags))
 		bitmap_file_kick(bitmap);
 }
 EXPORT_SYMBOL(bitmap_unplug);
@@ -917,98 +938,77 @@
 static int bitmap_init_from_disk(struct bitmap *bitmap, sector_t start)
 {
 	unsigned long i, chunks, index, oldindex, bit;
-	struct page *page = NULL, *oldpage = NULL;
-	unsigned long num_pages, bit_cnt = 0;
+	struct page *page = NULL;
+	unsigned long bit_cnt = 0;
 	struct file *file;
-	unsigned long bytes, offset;
+	unsigned long offset;
 	int outofdate;
 	int ret = -ENOSPC;
 	void *paddr;
+	struct bitmap_storage *store = &bitmap->storage;
 
-	chunks = bitmap->chunks;
-	file = bitmap->file;
+	chunks = bitmap->counts.chunks;
+	file = store->file;
 
-	BUG_ON(!file && !bitmap->mddev->bitmap_info.offset);
+	if (!file && !bitmap->mddev->bitmap_info.offset) {
+		/* No permanent bitmap - fill with '1s'. */
+		store->filemap = NULL;
+		store->file_pages = 0;
+		for (i = 0; i < chunks ; i++) {
+			/* if the disk bit is set, set the memory bit */
+			int needed = ((sector_t)(i+1) << (bitmap->counts.chunkshift)
+				      >= start);
+			bitmap_set_memory_bits(bitmap,
+					       (sector_t)i << bitmap->counts.chunkshift,
+					       needed);
+		}
+		return 0;
+	}
 
-	outofdate = bitmap->flags & BITMAP_STALE;
+	outofdate = test_bit(BITMAP_STALE, &bitmap->flags);
 	if (outofdate)
 		printk(KERN_INFO "%s: bitmap file is out of date, doing full "
 			"recovery\n", bmname(bitmap));
 
-	bytes = DIV_ROUND_UP(bitmap->chunks, 8);
-	if (!bitmap->mddev->bitmap_info.external)
-		bytes += sizeof(bitmap_super_t);
-
-	num_pages = DIV_ROUND_UP(bytes, PAGE_SIZE);
-
-	if (file && i_size_read(file->f_mapping->host) < bytes) {
+	if (file && i_size_read(file->f_mapping->host) < store->bytes) {
 		printk(KERN_INFO "%s: bitmap file too short %lu < %lu\n",
-			bmname(bitmap),
-			(unsigned long) i_size_read(file->f_mapping->host),
-			bytes);
+		       bmname(bitmap),
+		       (unsigned long) i_size_read(file->f_mapping->host),
+		       store->bytes);
 		goto err;
 	}
 
-	ret = -ENOMEM;
-
-	bitmap->filemap = kmalloc(sizeof(struct page *) * num_pages, GFP_KERNEL);
-	if (!bitmap->filemap)
-		goto err;
-
-	/* We need 4 bits per page, rounded up to a multiple of sizeof(unsigned long) */
-	bitmap->filemap_attr = kzalloc(
-		roundup(DIV_ROUND_UP(num_pages*4, 8), sizeof(unsigned long)),
-		GFP_KERNEL);
-	if (!bitmap->filemap_attr)
-		goto err;
-
 	oldindex = ~0L;
+	offset = 0;
+	if (!bitmap->mddev->bitmap_info.external)
+		offset = sizeof(bitmap_super_t);
 
 	for (i = 0; i < chunks; i++) {
 		int b;
-		index = file_page_index(bitmap, i);
-		bit = file_page_offset(bitmap, i);
+		index = file_page_index(&bitmap->storage, i);
+		bit = file_page_offset(&bitmap->storage, i);
 		if (index != oldindex) { /* this is a new page, read it in */
 			int count;
 			/* unmap the old page, we're done with it */
-			if (index == num_pages-1)
-				count = bytes - index * PAGE_SIZE;
+			if (index == store->file_pages-1)
+				count = store->bytes - index * PAGE_SIZE;
 			else
 				count = PAGE_SIZE;
-			if (index == 0 && bitmap->sb_page) {
-				/*
-				 * if we're here then the superblock page
-				 * contains some bits (PAGE_SIZE != sizeof sb)
-				 * we've already read it in, so just use it
-				 */
-				page = bitmap->sb_page;
-				offset = sizeof(bitmap_super_t);
-				if (!file)
-					page = read_sb_page(
-						bitmap->mddev,
-						bitmap->mddev->bitmap_info.offset,
-						page,
-						index, count);
-			} else if (file) {
-				page = read_page(file, index, bitmap, count);
-				offset = 0;
-			} else {
-				page = read_sb_page(bitmap->mddev,
-						    bitmap->mddev->bitmap_info.offset,
-						    NULL,
-						    index, count);
-				offset = 0;
-			}
-			if (IS_ERR(page)) { /* read error */
-				ret = PTR_ERR(page);
+			page = store->filemap[index];
+			if (file)
+				ret = read_page(file, index, bitmap,
+						count, page);
+			else
+				ret = read_sb_page(
+					bitmap->mddev,
+					bitmap->mddev->bitmap_info.offset,
+					page,
+					index, count);
+
+			if (ret)
 				goto err;
-			}
 
 			oldindex = index;
-			oldpage = page;
-
-			bitmap->filemap[bitmap->file_pages++] = page;
-			bitmap->last_page_size = count;
 
 			if (outofdate) {
 				/*
@@ -1022,39 +1022,33 @@
 				write_page(bitmap, page, 1);
 
 				ret = -EIO;
-				if (bitmap->flags & BITMAP_WRITE_ERROR)
+				if (test_bit(BITMAP_WRITE_ERROR,
+					     &bitmap->flags))
 					goto err;
 			}
 		}
 		paddr = kmap_atomic(page);
-		if (bitmap->flags & BITMAP_HOSTENDIAN)
+		if (test_bit(BITMAP_HOSTENDIAN, &bitmap->flags))
 			b = test_bit(bit, paddr);
 		else
 			b = test_bit_le(bit, paddr);
 		kunmap_atomic(paddr);
 		if (b) {
 			/* if the disk bit is set, set the memory bit */
-			int needed = ((sector_t)(i+1) << bitmap->chunkshift
+			int needed = ((sector_t)(i+1) << bitmap->counts.chunkshift
 				      >= start);
 			bitmap_set_memory_bits(bitmap,
-					       (sector_t)i << bitmap->chunkshift,
+					       (sector_t)i << bitmap->counts.chunkshift,
 					       needed);
 			bit_cnt++;
 		}
-	}
-
-	/* everything went OK */
-	ret = 0;
-	bitmap_mask_state(bitmap, BITMAP_STALE, MASK_UNSET);
-
-	if (bit_cnt) { /* Kick recovery if any bits were set */
-		set_bit(MD_RECOVERY_NEEDED, &bitmap->mddev->recovery);
-		md_wakeup_thread(bitmap->mddev->thread);
+		offset = 0;
 	}
 
 	printk(KERN_INFO "%s: bitmap initialized from disk: "
-	       "read %lu/%lu pages, set %lu of %lu bits\n",
-	       bmname(bitmap), bitmap->file_pages, num_pages, bit_cnt, chunks);
+	       "read %lu pages, set %lu of %lu bits\n",
+	       bmname(bitmap), store->file_pages,
+	       bit_cnt, chunks);
 
 	return 0;
 
@@ -1071,22 +1065,38 @@
 	 */
 	int i;
 
-	spin_lock_irq(&bitmap->lock);
-	for (i = 0; i < bitmap->file_pages; i++)
-		set_page_attr(bitmap, bitmap->filemap[i],
+	if (!bitmap || !bitmap->storage.filemap)
+		return;
+	if (bitmap->storage.file)
+		/* Only one copy, so nothing needed */
+		return;
+
+	for (i = 0; i < bitmap->storage.file_pages; i++)
+		set_page_attr(bitmap, i,
 			      BITMAP_PAGE_NEEDWRITE);
 	bitmap->allclean = 0;
-	spin_unlock_irq(&bitmap->lock);
 }
 
-static void bitmap_count_page(struct bitmap *bitmap, sector_t offset, int inc)
+static void bitmap_count_page(struct bitmap_counts *bitmap,
+			      sector_t offset, int inc)
 {
 	sector_t chunk = offset >> bitmap->chunkshift;
 	unsigned long page = chunk >> PAGE_COUNTER_SHIFT;
 	bitmap->bp[page].count += inc;
 	bitmap_checkfree(bitmap, page);
 }
-static bitmap_counter_t *bitmap_get_counter(struct bitmap *bitmap,
+
+static void bitmap_set_pending(struct bitmap_counts *bitmap, sector_t offset)
+{
+	sector_t chunk = offset >> bitmap->chunkshift;
+	unsigned long page = chunk >> PAGE_COUNTER_SHIFT;
+	struct bitmap_page *bp = &bitmap->bp[page];
+
+	if (!bp->pending)
+		bp->pending = 1;
+}
+
+static bitmap_counter_t *bitmap_get_counter(struct bitmap_counts *bitmap,
 					    sector_t offset, sector_t *blocks,
 					    int create);
 
@@ -1099,10 +1109,9 @@
 {
 	struct bitmap *bitmap;
 	unsigned long j;
-	unsigned long flags;
-	struct page *page = NULL, *lastpage = NULL;
+	unsigned long nextpage;
 	sector_t blocks;
-	void *paddr;
+	struct bitmap_counts *counts;
 
 	/* Use a mutex to guard daemon_work against
 	 * bitmap_destroy.
@@ -1124,112 +1133,90 @@
 	}
 	bitmap->allclean = 1;
 
-	spin_lock_irqsave(&bitmap->lock, flags);
-	for (j = 0; j < bitmap->chunks; j++) {
-		bitmap_counter_t *bmc;
-		if (!bitmap->filemap)
-			/* error or shutdown */
-			break;
+	/* Any file-page which is PENDING now needs to be written.
+	 * So set NEEDWRITE now, then after we make any last-minute changes
+	 * we will write it.
+	 */
+	for (j = 0; j < bitmap->storage.file_pages; j++)
+		if (test_and_clear_page_attr(bitmap, j,
+					     BITMAP_PAGE_PENDING))
+			set_page_attr(bitmap, j,
+				      BITMAP_PAGE_NEEDWRITE);
 
-		page = filemap_get_page(bitmap, j);
-
-		if (page != lastpage) {
-			/* skip this page unless it's marked as needing cleaning */
-			if (!test_page_attr(bitmap, page, BITMAP_PAGE_PENDING)) {
-				int need_write = test_page_attr(bitmap, page,
-								BITMAP_PAGE_NEEDWRITE);
-				if (need_write)
-					clear_page_attr(bitmap, page, BITMAP_PAGE_NEEDWRITE);
-
-				spin_unlock_irqrestore(&bitmap->lock, flags);
-				if (need_write)
-					write_page(bitmap, page, 0);
-				spin_lock_irqsave(&bitmap->lock, flags);
-				j |= (PAGE_BITS - 1);
-				continue;
-			}
-
-			/* grab the new page, sync and release the old */
-			if (lastpage != NULL) {
-				if (test_page_attr(bitmap, lastpage,
-						   BITMAP_PAGE_NEEDWRITE)) {
-					clear_page_attr(bitmap, lastpage,
-							BITMAP_PAGE_NEEDWRITE);
-					spin_unlock_irqrestore(&bitmap->lock, flags);
-					write_page(bitmap, lastpage, 0);
-				} else {
-					set_page_attr(bitmap, lastpage,
-						      BITMAP_PAGE_NEEDWRITE);
-					bitmap->allclean = 0;
-					spin_unlock_irqrestore(&bitmap->lock, flags);
-				}
-			} else
-				spin_unlock_irqrestore(&bitmap->lock, flags);
-			lastpage = page;
-
-			/* We are possibly going to clear some bits, so make
-			 * sure that events_cleared is up-to-date.
-			 */
-			if (bitmap->need_sync &&
-			    mddev->bitmap_info.external == 0) {
-				bitmap_super_t *sb;
-				bitmap->need_sync = 0;
-				sb = kmap_atomic(bitmap->sb_page);
-				sb->events_cleared =
-					cpu_to_le64(bitmap->events_cleared);
-				kunmap_atomic(sb);
-				write_page(bitmap, bitmap->sb_page, 1);
-			}
-			spin_lock_irqsave(&bitmap->lock, flags);
-			if (!bitmap->need_sync)
-				clear_page_attr(bitmap, page, BITMAP_PAGE_PENDING);
-			else
-				bitmap->allclean = 0;
-		}
-		bmc = bitmap_get_counter(bitmap,
-					 (sector_t)j << bitmap->chunkshift,
-					 &blocks, 0);
-		if (!bmc)
-			j |= PAGE_COUNTER_MASK;
-		else if (*bmc) {
-			if (*bmc == 1 && !bitmap->need_sync) {
-				/* we can clear the bit */
-				*bmc = 0;
-				bitmap_count_page(bitmap,
-						  (sector_t)j << bitmap->chunkshift,
-						  -1);
-
-				/* clear the bit */
-				paddr = kmap_atomic(page);
-				if (bitmap->flags & BITMAP_HOSTENDIAN)
-					clear_bit(file_page_offset(bitmap, j),
-						  paddr);
-				else
-					__clear_bit_le(
-						file_page_offset(bitmap,
-								 j),
-						paddr);
-				kunmap_atomic(paddr);
-			} else if (*bmc <= 2) {
-				*bmc = 1; /* maybe clear the bit next time */
-				set_page_attr(bitmap, page, BITMAP_PAGE_PENDING);
-				bitmap->allclean = 0;
-			}
+	if (bitmap->need_sync &&
+	    mddev->bitmap_info.external == 0) {
+		/* Arrange for superblock update as well as
+		 * other changes */
+		bitmap_super_t *sb;
+		bitmap->need_sync = 0;
+		if (bitmap->storage.filemap) {
+			sb = kmap_atomic(bitmap->storage.sb_page);
+			sb->events_cleared =
+				cpu_to_le64(bitmap->events_cleared);
+			kunmap_atomic(sb);
+			set_page_attr(bitmap, 0,
+				      BITMAP_PAGE_NEEDWRITE);
 		}
 	}
-	spin_unlock_irqrestore(&bitmap->lock, flags);
+	/* Now look at the bitmap counters and if any are '2' or '1',
+	 * decrement and handle accordingly.
+	 */
+	counts = &bitmap->counts;
+	spin_lock_irq(&counts->lock);
+	nextpage = 0;
+	for (j = 0; j < counts->chunks; j++) {
+		bitmap_counter_t *bmc;
+		sector_t  block = (sector_t)j << counts->chunkshift;
 
-	/* now sync the final page */
-	if (lastpage != NULL) {
-		spin_lock_irqsave(&bitmap->lock, flags);
-		if (test_page_attr(bitmap, lastpage, BITMAP_PAGE_NEEDWRITE)) {
-			clear_page_attr(bitmap, lastpage, BITMAP_PAGE_NEEDWRITE);
-			spin_unlock_irqrestore(&bitmap->lock, flags);
-			write_page(bitmap, lastpage, 0);
-		} else {
-			set_page_attr(bitmap, lastpage, BITMAP_PAGE_NEEDWRITE);
+		if (j == nextpage) {
+			nextpage += PAGE_COUNTER_RATIO;
+			if (!counts->bp[j >> PAGE_COUNTER_SHIFT].pending) {
+				j |= PAGE_COUNTER_MASK;
+				continue;
+			}
+			counts->bp[j >> PAGE_COUNTER_SHIFT].pending = 0;
+		}
+		bmc = bitmap_get_counter(counts,
+					 block,
+					 &blocks, 0);
+
+		if (!bmc) {
+			j |= PAGE_COUNTER_MASK;
+			continue;
+		}
+		if (*bmc == 1 && !bitmap->need_sync) {
+			/* We can clear the bit */
+			*bmc = 0;
+			bitmap_count_page(counts, block, -1);
+			bitmap_file_clear_bit(bitmap, block);
+		} else if (*bmc && *bmc <= 2) {
+			*bmc = 1;
+			bitmap_set_pending(counts, block);
 			bitmap->allclean = 0;
-			spin_unlock_irqrestore(&bitmap->lock, flags);
+		}
+	}
+	spin_unlock_irq(&counts->lock);
+
+	/* Now start writeout on any page in NEEDWRITE that isn't DIRTY.
+	 * DIRTY pages need to be written by bitmap_unplug so it can wait
+	 * for them.
+	 * If we find any DIRTY page we stop there and let bitmap_unplug
+	 * handle all the rest.  This is important in the case where
+	 * the first blocking holds the superblock and it has been updated.
+	 * We mustn't write any other blocks before the superblock.
+	 */
+	for (j = 0;
+	     j < bitmap->storage.file_pages
+		     && !test_bit(BITMAP_STALE, &bitmap->flags);
+	     j++) {
+
+		if (test_page_attr(bitmap, j,
+				   BITMAP_PAGE_DIRTY))
+			/* bitmap_unplug will handle the rest */
+			break;
+		if (test_and_clear_page_attr(bitmap, j,
+					     BITMAP_PAGE_NEEDWRITE)) {
+			write_page(bitmap, bitmap->storage.filemap[j], 0);
 		}
 	}
 
@@ -1240,7 +1227,7 @@
 	mutex_unlock(&mddev->bitmap_info.mutex);
 }
 
-static bitmap_counter_t *bitmap_get_counter(struct bitmap *bitmap,
+static bitmap_counter_t *bitmap_get_counter(struct bitmap_counts *bitmap,
 					    sector_t offset, sector_t *blocks,
 					    int create)
 __releases(bitmap->lock)
@@ -1302,10 +1289,10 @@
 		sector_t blocks;
 		bitmap_counter_t *bmc;
 
-		spin_lock_irq(&bitmap->lock);
-		bmc = bitmap_get_counter(bitmap, offset, &blocks, 1);
+		spin_lock_irq(&bitmap->counts.lock);
+		bmc = bitmap_get_counter(&bitmap->counts, offset, &blocks, 1);
 		if (!bmc) {
-			spin_unlock_irq(&bitmap->lock);
+			spin_unlock_irq(&bitmap->counts.lock);
 			return 0;
 		}
 
@@ -1317,7 +1304,7 @@
 			 */
 			prepare_to_wait(&bitmap->overflow_wait, &__wait,
 					TASK_UNINTERRUPTIBLE);
-			spin_unlock_irq(&bitmap->lock);
+			spin_unlock_irq(&bitmap->counts.lock);
 			io_schedule();
 			finish_wait(&bitmap->overflow_wait, &__wait);
 			continue;
@@ -1326,7 +1313,7 @@
 		switch (*bmc) {
 		case 0:
 			bitmap_file_set_bit(bitmap, offset);
-			bitmap_count_page(bitmap, offset, 1);
+			bitmap_count_page(&bitmap->counts, offset, 1);
 			/* fall through */
 		case 1:
 			*bmc = 2;
@@ -1334,7 +1321,7 @@
 
 		(*bmc)++;
 
-		spin_unlock_irq(&bitmap->lock);
+		spin_unlock_irq(&bitmap->counts.lock);
 
 		offset += blocks;
 		if (sectors > blocks)
@@ -1364,10 +1351,10 @@
 		unsigned long flags;
 		bitmap_counter_t *bmc;
 
-		spin_lock_irqsave(&bitmap->lock, flags);
-		bmc = bitmap_get_counter(bitmap, offset, &blocks, 0);
+		spin_lock_irqsave(&bitmap->counts.lock, flags);
+		bmc = bitmap_get_counter(&bitmap->counts, offset, &blocks, 0);
 		if (!bmc) {
-			spin_unlock_irqrestore(&bitmap->lock, flags);
+			spin_unlock_irqrestore(&bitmap->counts.lock, flags);
 			return;
 		}
 
@@ -1386,14 +1373,10 @@
 
 		(*bmc)--;
 		if (*bmc <= 2) {
-			set_page_attr(bitmap,
-				      filemap_get_page(
-					      bitmap,
-					      offset >> bitmap->chunkshift),
-				      BITMAP_PAGE_PENDING);
+			bitmap_set_pending(&bitmap->counts, offset);
 			bitmap->allclean = 0;
 		}
-		spin_unlock_irqrestore(&bitmap->lock, flags);
+		spin_unlock_irqrestore(&bitmap->counts.lock, flags);
 		offset += blocks;
 		if (sectors > blocks)
 			sectors -= blocks;
@@ -1412,8 +1395,8 @@
 		*blocks = 1024;
 		return 1; /* always resync if no bitmap */
 	}
-	spin_lock_irq(&bitmap->lock);
-	bmc = bitmap_get_counter(bitmap, offset, blocks, 0);
+	spin_lock_irq(&bitmap->counts.lock);
+	bmc = bitmap_get_counter(&bitmap->counts, offset, blocks, 0);
 	rv = 0;
 	if (bmc) {
 		/* locked */
@@ -1427,7 +1410,7 @@
 			}
 		}
 	}
-	spin_unlock_irq(&bitmap->lock);
+	spin_unlock_irq(&bitmap->counts.lock);
 	return rv;
 }
 
@@ -1464,8 +1447,8 @@
 		*blocks = 1024;
 		return;
 	}
-	spin_lock_irqsave(&bitmap->lock, flags);
-	bmc = bitmap_get_counter(bitmap, offset, blocks, 0);
+	spin_lock_irqsave(&bitmap->counts.lock, flags);
+	bmc = bitmap_get_counter(&bitmap->counts, offset, blocks, 0);
 	if (bmc == NULL)
 		goto unlock;
 	/* locked */
@@ -1476,15 +1459,13 @@
 			*bmc |= NEEDED_MASK;
 		else {
 			if (*bmc <= 2) {
-				set_page_attr(bitmap,
-					      filemap_get_page(bitmap, offset >> bitmap->chunkshift),
-					      BITMAP_PAGE_PENDING);
+				bitmap_set_pending(&bitmap->counts, offset);
 				bitmap->allclean = 0;
 			}
 		}
 	}
  unlock:
-	spin_unlock_irqrestore(&bitmap->lock, flags);
+	spin_unlock_irqrestore(&bitmap->counts.lock, flags);
 }
 EXPORT_SYMBOL(bitmap_end_sync);
 
@@ -1524,7 +1505,7 @@
 
 	bitmap->mddev->curr_resync_completed = sector;
 	set_bit(MD_CHANGE_CLEAN, &bitmap->mddev->flags);
-	sector &= ~((1ULL << bitmap->chunkshift) - 1);
+	sector &= ~((1ULL << bitmap->counts.chunkshift) - 1);
 	s = 0;
 	while (s < sector && s < bitmap->mddev->resync_max_sectors) {
 		bitmap_end_sync(bitmap, s, &blocks, 0);
@@ -1538,27 +1519,25 @@
 static void bitmap_set_memory_bits(struct bitmap *bitmap, sector_t offset, int needed)
 {
 	/* For each chunk covered by any of these sectors, set the
-	 * counter to 1 and set resync_needed.  They should all
+	 * counter to 2 and possibly set resync_needed.  They should all
 	 * be 0 at this point
 	 */
 
 	sector_t secs;
 	bitmap_counter_t *bmc;
-	spin_lock_irq(&bitmap->lock);
-	bmc = bitmap_get_counter(bitmap, offset, &secs, 1);
+	spin_lock_irq(&bitmap->counts.lock);
+	bmc = bitmap_get_counter(&bitmap->counts, offset, &secs, 1);
 	if (!bmc) {
-		spin_unlock_irq(&bitmap->lock);
+		spin_unlock_irq(&bitmap->counts.lock);
 		return;
 	}
 	if (!*bmc) {
-		struct page *page;
 		*bmc = 2 | (needed ? NEEDED_MASK : 0);
-		bitmap_count_page(bitmap, offset, 1);
-		page = filemap_get_page(bitmap, offset >> bitmap->chunkshift);
-		set_page_attr(bitmap, page, BITMAP_PAGE_PENDING);
+		bitmap_count_page(&bitmap->counts, offset, 1);
+		bitmap_set_pending(&bitmap->counts, offset);
 		bitmap->allclean = 0;
 	}
-	spin_unlock_irq(&bitmap->lock);
+	spin_unlock_irq(&bitmap->counts.lock);
 }
 
 /* dirty the memory and file bits for bitmap chunks "s" to "e" */
@@ -1567,11 +1546,9 @@
 	unsigned long chunk;
 
 	for (chunk = s; chunk <= e; chunk++) {
-		sector_t sec = (sector_t)chunk << bitmap->chunkshift;
+		sector_t sec = (sector_t)chunk << bitmap->counts.chunkshift;
 		bitmap_set_memory_bits(bitmap, sec, 1);
-		spin_lock_irq(&bitmap->lock);
 		bitmap_file_set_bit(bitmap, sec);
-		spin_unlock_irq(&bitmap->lock);
 		if (sec < bitmap->mddev->recovery_cp)
 			/* We are asserting that the array is dirty,
 			 * so move the recovery_cp address back so
@@ -1616,11 +1593,15 @@
 	if (!bitmap) /* there was no bitmap */
 		return;
 
-	/* release the bitmap file and kill the daemon */
-	bitmap_file_put(bitmap);
+	/* Shouldn't be needed - but just in case.... */
+	wait_event(bitmap->write_wait,
+		   atomic_read(&bitmap->pending_writes) == 0);
 
-	bp = bitmap->bp;
-	pages = bitmap->pages;
+	/* release the bitmap file  */
+	bitmap_file_unmap(&bitmap->storage);
+
+	bp = bitmap->counts.bp;
+	pages = bitmap->counts.pages;
 
 	/* free all allocated memory */
 
@@ -1659,25 +1640,19 @@
 {
 	struct bitmap *bitmap;
 	sector_t blocks = mddev->resync_max_sectors;
-	unsigned long chunks;
-	unsigned long pages;
 	struct file *file = mddev->bitmap_info.file;
 	int err;
 	struct sysfs_dirent *bm = NULL;
 
 	BUILD_BUG_ON(sizeof(bitmap_super_t) != 256);
 
-	if (!file
-	    && !mddev->bitmap_info.offset) /* bitmap disabled, nothing to do */
-		return 0;
-
 	BUG_ON(file && mddev->bitmap_info.offset);
 
 	bitmap = kzalloc(sizeof(*bitmap), GFP_KERNEL);
 	if (!bitmap)
 		return -ENOMEM;
 
-	spin_lock_init(&bitmap->lock);
+	spin_lock_init(&bitmap->counts.lock);
 	atomic_set(&bitmap->pending_writes, 0);
 	init_waitqueue_head(&bitmap->write_wait);
 	init_waitqueue_head(&bitmap->overflow_wait);
@@ -1693,7 +1668,7 @@
 	} else
 		bitmap->sysfs_can_clear = NULL;
 
-	bitmap->file = file;
+	bitmap->storage.file = file;
 	if (file) {
 		get_file(file);
 		/* As future accesses to this file will use bmap,
@@ -1724,32 +1699,15 @@
 		goto error;
 
 	bitmap->daemon_lastrun = jiffies;
-	bitmap->chunkshift = (ffz(~mddev->bitmap_info.chunksize)
-			      - BITMAP_BLOCK_SHIFT);
-
-	chunks = (blocks + (1 << bitmap->chunkshift) - 1) >>
-			bitmap->chunkshift;
-	pages = (chunks + PAGE_COUNTER_RATIO - 1) / PAGE_COUNTER_RATIO;
-
-	BUG_ON(!pages);
-
-	bitmap->chunks = chunks;
-	bitmap->pages = pages;
-	bitmap->missing_pages = pages;
-
-	bitmap->bp = kzalloc(pages * sizeof(*bitmap->bp), GFP_KERNEL);
-
-	err = -ENOMEM;
-	if (!bitmap->bp)
+	err = bitmap_resize(bitmap, blocks, mddev->bitmap_info.chunksize, 1);
+	if (err)
 		goto error;
 
 	printk(KERN_INFO "created bitmap (%lu pages) for device %s\n",
-		pages, bmname(bitmap));
+	       bitmap->counts.pages, bmname(bitmap));
 
 	mddev->bitmap = bitmap;
-
-
-	return (bitmap->flags & BITMAP_WRITE_ERROR) ? -EIO : 0;
+	return test_bit(BITMAP_WRITE_ERROR, &bitmap->flags) ? -EIO : 0;
 
  error:
 	bitmap_free(bitmap);
@@ -1790,13 +1748,17 @@
 
 	if (err)
 		goto out;
+	clear_bit(BITMAP_STALE, &bitmap->flags);
+
+	/* Kick recovery in case any bits were set */
+	set_bit(MD_RECOVERY_NEEDED, &bitmap->mddev->recovery);
 
 	mddev->thread->timeout = mddev->bitmap_info.daemon_sleep;
 	md_wakeup_thread(mddev->thread);
 
 	bitmap_update_sb(bitmap);
 
-	if (bitmap->flags & BITMAP_WRITE_ERROR)
+	if (test_bit(BITMAP_WRITE_ERROR, &bitmap->flags))
 		err = -EIO;
 out:
 	return err;
@@ -1806,30 +1768,194 @@
 void bitmap_status(struct seq_file *seq, struct bitmap *bitmap)
 {
 	unsigned long chunk_kb;
-	unsigned long flags;
+	struct bitmap_counts *counts;
 
 	if (!bitmap)
 		return;
 
-	spin_lock_irqsave(&bitmap->lock, flags);
+	counts = &bitmap->counts;
+
 	chunk_kb = bitmap->mddev->bitmap_info.chunksize >> 10;
 	seq_printf(seq, "bitmap: %lu/%lu pages [%luKB], "
 		   "%lu%s chunk",
-		   bitmap->pages - bitmap->missing_pages,
-		   bitmap->pages,
-		   (bitmap->pages - bitmap->missing_pages)
+		   counts->pages - counts->missing_pages,
+		   counts->pages,
+		   (counts->pages - counts->missing_pages)
 		   << (PAGE_SHIFT - 10),
 		   chunk_kb ? chunk_kb : bitmap->mddev->bitmap_info.chunksize,
 		   chunk_kb ? "KB" : "B");
-	if (bitmap->file) {
+	if (bitmap->storage.file) {
 		seq_printf(seq, ", file: ");
-		seq_path(seq, &bitmap->file->f_path, " \t\n");
+		seq_path(seq, &bitmap->storage.file->f_path, " \t\n");
 	}
 
 	seq_printf(seq, "\n");
-	spin_unlock_irqrestore(&bitmap->lock, flags);
 }
 
+int bitmap_resize(struct bitmap *bitmap, sector_t blocks,
+		  int chunksize, int init)
+{
+	/* If chunk_size is 0, choose an appropriate chunk size.
+	 * Then possibly allocate new storage space.
+	 * Then quiesce, copy bits, replace bitmap, and re-start
+	 *
+	 * This function is called both to set up the initial bitmap
+	 * and to resize the bitmap while the array is active.
+	 * If this happens as a result of the array being resized,
+	 * chunksize will be zero, and we need to choose a suitable
+	 * chunksize, otherwise we use what we are given.
+	 */
+	struct bitmap_storage store;
+	struct bitmap_counts old_counts;
+	unsigned long chunks;
+	sector_t block;
+	sector_t old_blocks, new_blocks;
+	int chunkshift;
+	int ret = 0;
+	long pages;
+	struct bitmap_page *new_bp;
+
+	if (chunksize == 0) {
+		/* If there is enough space, leave the chunk size unchanged,
+		 * else increase by factor of two until there is enough space.
+		 */
+		long bytes;
+		long space = bitmap->mddev->bitmap_info.space;
+
+		if (space == 0) {
+			/* We don't know how much space there is, so limit
+			 * to current size - in sectors.
+			 */
+			bytes = DIV_ROUND_UP(bitmap->counts.chunks, 8);
+			if (!bitmap->mddev->bitmap_info.external)
+				bytes += sizeof(bitmap_super_t);
+			space = DIV_ROUND_UP(bytes, 512);
+			bitmap->mddev->bitmap_info.space = space;
+		}
+		chunkshift = bitmap->counts.chunkshift;
+		chunkshift--;
+		do {
+			/* 'chunkshift' is shift from block size to chunk size */
+			chunkshift++;
+			chunks = DIV_ROUND_UP_SECTOR_T(blocks, 1 << chunkshift);
+			bytes = DIV_ROUND_UP(chunks, 8);
+			if (!bitmap->mddev->bitmap_info.external)
+				bytes += sizeof(bitmap_super_t);
+		} while (bytes > (space << 9));
+	} else
+		chunkshift = ffz(~chunksize) - BITMAP_BLOCK_SHIFT;
+
+	chunks = DIV_ROUND_UP_SECTOR_T(blocks, 1 << chunkshift);
+	memset(&store, 0, sizeof(store));
+	if (bitmap->mddev->bitmap_info.offset || bitmap->mddev->bitmap_info.file)
+		ret = bitmap_storage_alloc(&store, chunks,
+					   !bitmap->mddev->bitmap_info.external);
+	if (ret)
+		goto err;
+
+	pages = DIV_ROUND_UP(chunks, PAGE_COUNTER_RATIO);
+
+	new_bp = kzalloc(pages * sizeof(*new_bp), GFP_KERNEL);
+	ret = -ENOMEM;
+	if (!new_bp) {
+		bitmap_file_unmap(&store);
+		goto err;
+	}
+
+	if (!init)
+		bitmap->mddev->pers->quiesce(bitmap->mddev, 1);
+
+	store.file = bitmap->storage.file;
+	bitmap->storage.file = NULL;
+
+	if (store.sb_page && bitmap->storage.sb_page)
+		memcpy(page_address(store.sb_page),
+		       page_address(bitmap->storage.sb_page),
+		       sizeof(bitmap_super_t));
+	bitmap_file_unmap(&bitmap->storage);
+	bitmap->storage = store;
+
+	old_counts = bitmap->counts;
+	bitmap->counts.bp = new_bp;
+	bitmap->counts.pages = pages;
+	bitmap->counts.missing_pages = pages;
+	bitmap->counts.chunkshift = chunkshift;
+	bitmap->counts.chunks = chunks;
+	bitmap->mddev->bitmap_info.chunksize = 1 << (chunkshift +
+						     BITMAP_BLOCK_SHIFT);
+
+	blocks = min(old_counts.chunks << old_counts.chunkshift,
+		     chunks << chunkshift);
+
+	spin_lock_irq(&bitmap->counts.lock);
+	for (block = 0; block < blocks; ) {
+		bitmap_counter_t *bmc_old, *bmc_new;
+		int set;
+
+		bmc_old = bitmap_get_counter(&old_counts, block,
+					     &old_blocks, 0);
+		set = bmc_old && NEEDED(*bmc_old);
+
+		if (set) {
+			bmc_new = bitmap_get_counter(&bitmap->counts, block,
+						     &new_blocks, 1);
+			if (*bmc_new == 0) {
+				/* need to set on-disk bits too. */
+				sector_t end = block + new_blocks;
+				sector_t start = block >> chunkshift;
+				start <<= chunkshift;
+				while (start < end) {
+					bitmap_file_set_bit(bitmap, block);
+					start += 1 << chunkshift;
+				}
+				*bmc_new = 2;
+				bitmap_count_page(&bitmap->counts,
+						  block, 1);
+				bitmap_set_pending(&bitmap->counts,
+						   block);
+			}
+			*bmc_new |= NEEDED_MASK;
+			if (new_blocks < old_blocks)
+				old_blocks = new_blocks;
+		}
+		block += old_blocks;
+	}
+
+	if (!init) {
+		int i;
+		while (block < (chunks << chunkshift)) {
+			bitmap_counter_t *bmc;
+			bmc = bitmap_get_counter(&bitmap->counts, block,
+						 &new_blocks, 1);
+			if (bmc) {
+				/* new space.  It needs to be resynced, so
+				 * we set NEEDED_MASK.
+				 */
+				if (*bmc == 0) {
+					*bmc = NEEDED_MASK | 2;
+					bitmap_count_page(&bitmap->counts,
+							  block, 1);
+					bitmap_set_pending(&bitmap->counts,
+							   block);
+				}
+			}
+			block += new_blocks;
+		}
+		for (i = 0; i < bitmap->storage.file_pages; i++)
+			set_page_attr(bitmap, i, BITMAP_PAGE_DIRTY);
+	}
+	spin_unlock_irq(&bitmap->counts.lock);
+
+	if (!init) {
+		bitmap_unplug(bitmap);
+		bitmap->mddev->pers->quiesce(bitmap->mddev, 0);
+	}
+	ret = 0;
+err:
+	return ret;
+}
+EXPORT_SYMBOL_GPL(bitmap_resize);
+
 static ssize_t
 location_show(struct mddev *mddev, char *page)
 {
@@ -1923,6 +2049,43 @@
 static struct md_sysfs_entry bitmap_location =
 __ATTR(location, S_IRUGO|S_IWUSR, location_show, location_store);
 
+/* 'bitmap/space' is the space available at 'location' for the
+ * bitmap.  This allows the kernel to know when it is safe to
+ * resize the bitmap to match a resized array.
+ */
+static ssize_t
+space_show(struct mddev *mddev, char *page)
+{
+	return sprintf(page, "%lu\n", mddev->bitmap_info.space);
+}
+
+static ssize_t
+space_store(struct mddev *mddev, const char *buf, size_t len)
+{
+	unsigned long sectors;
+	int rv;
+
+	rv = kstrtoul(buf, 10, &sectors);
+	if (rv)
+		return rv;
+
+	if (sectors == 0)
+		return -EINVAL;
+
+	if (mddev->bitmap &&
+	    sectors < (mddev->bitmap->storage.bytes + 511) >> 9)
+		return -EFBIG; /* Bitmap is too big for this small space */
+
+	/* could make sure it isn't too big, but that isn't really
+	 * needed - user-space should be careful.
+	 */
+	mddev->bitmap_info.space = sectors;
+	return len;
+}
+
+static struct md_sysfs_entry bitmap_space =
+__ATTR(space, S_IRUGO|S_IWUSR, space_show, space_store);
+
 static ssize_t
 timeout_show(struct mddev *mddev, char *page)
 {
@@ -2098,6 +2261,7 @@
 
 static struct attribute *md_bitmap_attrs[] = {
 	&bitmap_location.attr,
+	&bitmap_space.attr,
 	&bitmap_timeout.attr,
 	&bitmap_backlog.attr,
 	&bitmap_chunksize.attr,
diff --git a/drivers/md/bitmap.h b/drivers/md/bitmap.h
index b44b0aba..df4aeb6 100644
--- a/drivers/md/bitmap.h
+++ b/drivers/md/bitmap.h
@@ -111,9 +111,9 @@
 
 /* use these for bitmap->flags and bitmap->sb->state bit-fields */
 enum bitmap_state {
-	BITMAP_STALE  = 0x002,  /* the bitmap file is out of date or had -EIO */
-	BITMAP_WRITE_ERROR = 0x004, /* A write error has occurred */
-	BITMAP_HOSTENDIAN = 0x8000,
+	BITMAP_STALE	   = 1,  /* the bitmap file is out of date or had -EIO */
+	BITMAP_WRITE_ERROR = 2, /* A write error has occurred */
+	BITMAP_HOSTENDIAN  =15,
 };
 
 /* the superblock at the front of the bitmap file -- little endian */
@@ -128,8 +128,10 @@
 	__le32 chunksize;    /* 52  the bitmap chunk size in bytes */
 	__le32 daemon_sleep; /* 56  seconds between disk flushes */
 	__le32 write_behind; /* 60  number of outstanding write-behind writes */
+	__le32 sectors_reserved; /* 64 number of 512-byte sectors that are
+				  * reserved for the bitmap. */
 
-	__u8  pad[256 - 64]; /* set to zero */
+	__u8  pad[256 - 68]; /* set to zero */
 } bitmap_super_t;
 
 /* notes:
@@ -160,35 +162,48 @@
 	 */
 	unsigned int hijacked:1;
 	/*
+	 * If any counter in this page is '1' or '2' - and so could be
+	 * cleared then that page is marked as 'pending'
+	 */
+	unsigned int pending:1;
+	/*
 	 * count of dirty bits on the page
 	 */
-	unsigned int  count:31;
+	unsigned int  count:30;
 };
 
 /* the main bitmap structure - one per mddev */
 struct bitmap {
-	struct bitmap_page *bp;
-	unsigned long pages; /* total number of pages in the bitmap */
-	unsigned long missing_pages; /* number of pages not yet allocated */
+
+	struct bitmap_counts {
+		spinlock_t lock;
+		struct bitmap_page *bp;
+		unsigned long pages;		/* total number of pages
+						 * in the bitmap */
+		unsigned long missing_pages;	/* number of pages
+						 * not yet allocated */
+		unsigned long chunkshift;	/* chunksize = 2^chunkshift
+						 * (for bitops) */
+		unsigned long chunks;		/* Total number of data
+						 * chunks for the array */
+	} counts;
 
 	struct mddev *mddev; /* the md device that the bitmap is for */
 
-	/* bitmap chunksize -- how much data does each bit represent? */
-	unsigned long chunkshift; /* chunksize = 2^(chunkshift+9) (for bitops) */
-	unsigned long chunks; /* total number of data chunks for the array */
-
 	__u64	events_cleared;
 	int need_sync;
 
-	/* bitmap spinlock */
-	spinlock_t lock;
-
-	struct file *file; /* backing disk file */
-	struct page *sb_page; /* cached copy of the bitmap file superblock */
-	struct page **filemap; /* list of cache pages for the file */
-	unsigned long *filemap_attr; /* attributes associated w/ filemap pages */
-	unsigned long file_pages; /* number of pages in the file */
-	int last_page_size; /* bytes in the last page */
+	struct bitmap_storage {
+		struct file *file;		/* backing disk file */
+		struct page *sb_page;		/* cached copy of the bitmap
+						 * file superblock */
+		struct page **filemap;		/* list of cache pages for
+						 * the file */
+		unsigned long *filemap_attr;	/* attributes associated
+						 * w/ filemap pages */
+		unsigned long file_pages;	/* number of pages in the file*/
+		unsigned long bytes;		/* total bytes in the bitmap */
+	} storage;
 
 	unsigned long flags;
 
@@ -242,6 +257,9 @@
 
 void bitmap_unplug(struct bitmap *bitmap);
 void bitmap_daemon_work(struct mddev *mddev);
+
+int bitmap_resize(struct bitmap *bitmap, sector_t blocks,
+		  int chunksize, int init);
 #endif
 
 #endif
diff --git a/drivers/md/dm-raid.c b/drivers/md/dm-raid.c
index 68965e6..017c34d 100644
--- a/drivers/md/dm-raid.c
+++ b/drivers/md/dm-raid.c
@@ -155,10 +155,7 @@
 	for (i = 0; i < rs->md.raid_disks; i++) {
 		if (rs->dev[i].meta_dev)
 			dm_put_device(rs->ti, rs->dev[i].meta_dev);
-		if (rs->dev[i].rdev.sb_page)
-			put_page(rs->dev[i].rdev.sb_page);
-		rs->dev[i].rdev.sb_page = NULL;
-		rs->dev[i].rdev.sb_loaded = 0;
+		md_rdev_clear(&rs->dev[i].rdev);
 		if (rs->dev[i].data_dev)
 			dm_put_device(rs->ti, rs->dev[i].data_dev);
 	}
@@ -606,7 +603,7 @@
 	if (!sync_page_io(rdev, 0, size, rdev->sb_page, READ, 1)) {
 		DMERR("Failed to read superblock of device at position %d",
 		      rdev->raid_disk);
-		set_bit(Faulty, &rdev->flags);
+		md_error(rdev->mddev, rdev);
 		return -EINVAL;
 	}
 
@@ -617,16 +614,18 @@
 
 static void super_sync(struct mddev *mddev, struct md_rdev *rdev)
 {
-	struct md_rdev *r;
+	int i;
 	uint64_t failed_devices;
 	struct dm_raid_superblock *sb;
+	struct raid_set *rs = container_of(mddev, struct raid_set, md);
 
 	sb = page_address(rdev->sb_page);
 	failed_devices = le64_to_cpu(sb->failed_devices);
 
-	rdev_for_each(r, mddev)
-		if ((r->raid_disk >= 0) && test_bit(Faulty, &r->flags))
-			failed_devices |= (1ULL << r->raid_disk);
+	for (i = 0; i < mddev->raid_disks; i++)
+		if (!rs->dev[i].data_dev ||
+		    test_bit(Faulty, &(rs->dev[i].rdev.flags)))
+			failed_devices |= (1ULL << i);
 
 	memset(sb, 0, sizeof(*sb));
 
@@ -1252,12 +1251,13 @@
 {
 	struct raid_set *rs = ti->private;
 
+	set_bit(MD_CHANGE_DEVS, &rs->md.flags);
 	if (!rs->bitmap_loaded) {
 		bitmap_load(&rs->md);
 		rs->bitmap_loaded = 1;
-	} else
-		md_wakeup_thread(rs->md.thread);
+	}
 
+	clear_bit(MD_RECOVERY_FROZEN, &rs->md.recovery);
 	mddev_resume(&rs->md);
 }
 
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 01233d8..1c2f904 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -402,6 +402,7 @@
 	wake_up(&mddev->sb_wait);
 	mddev->pers->quiesce(mddev, 0);
 
+	set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
 	md_wakeup_thread(mddev->thread);
 	md_wakeup_thread(mddev->sync_thread); /* possibly kick off a reshape */
 }
@@ -452,7 +453,7 @@
 			atomic_inc(&rdev->nr_pending);
 			atomic_inc(&rdev->nr_pending);
 			rcu_read_unlock();
-			bi = bio_alloc_mddev(GFP_KERNEL, 0, mddev);
+			bi = bio_alloc_mddev(GFP_NOIO, 0, mddev);
 			bi->bi_end_io = md_end_flush;
 			bi->bi_private = rdev;
 			bi->bi_bdev = rdev->bdev;
@@ -607,6 +608,7 @@
 	init_waitqueue_head(&mddev->sb_wait);
 	init_waitqueue_head(&mddev->recovery_wait);
 	mddev->reshape_position = MaxSector;
+	mddev->reshape_backwards = 0;
 	mddev->resync_min = 0;
 	mddev->resync_max = MaxSector;
 	mddev->level = LEVEL_NONE;
@@ -802,7 +804,7 @@
 	return 0;
 }
 
-static void free_disk_sb(struct md_rdev * rdev)
+void md_rdev_clear(struct md_rdev *rdev)
 {
 	if (rdev->sb_page) {
 		put_page(rdev->sb_page);
@@ -815,8 +817,10 @@
 		put_page(rdev->bb_page);
 		rdev->bb_page = NULL;
 	}
+	kfree(rdev->badblocks.page);
+	rdev->badblocks.page = NULL;
 }
-
+EXPORT_SYMBOL_GPL(md_rdev_clear);
 
 static void super_written(struct bio *bio, int error)
 {
@@ -887,6 +891,10 @@
 		rdev->meta_bdev : rdev->bdev;
 	if (metadata_op)
 		bio->bi_sector = sector + rdev->sb_start;
+	else if (rdev->mddev->reshape_position != MaxSector &&
+		 (rdev->mddev->reshape_backwards ==
+		  (sector >= rdev->mddev->reshape_position)))
+		bio->bi_sector = sector + rdev->new_data_offset;
 	else
 		bio->bi_sector = sector + rdev->data_offset;
 	bio_add_page(bio, page, size, 0);
@@ -1034,12 +1042,17 @@
 struct super_type  {
 	char		    *name;
 	struct module	    *owner;
-	int		    (*load_super)(struct md_rdev *rdev, struct md_rdev *refdev,
+	int		    (*load_super)(struct md_rdev *rdev,
+					  struct md_rdev *refdev,
 					  int minor_version);
-	int		    (*validate_super)(struct mddev *mddev, struct md_rdev *rdev);
-	void		    (*sync_super)(struct mddev *mddev, struct md_rdev *rdev);
+	int		    (*validate_super)(struct mddev *mddev,
+					      struct md_rdev *rdev);
+	void		    (*sync_super)(struct mddev *mddev,
+					  struct md_rdev *rdev);
 	unsigned long long  (*rdev_size_change)(struct md_rdev *rdev,
 						sector_t num_sectors);
+	int		    (*allow_new_offset)(struct md_rdev *rdev,
+						unsigned long long new_offset);
 };
 
 /*
@@ -1111,6 +1124,7 @@
 
 	rdev->preferred_minor = sb->md_minor;
 	rdev->data_offset = 0;
+	rdev->new_data_offset = 0;
 	rdev->sb_size = MD_SB_BYTES;
 	rdev->badblocks.shift = -1;
 
@@ -1184,7 +1198,11 @@
 		mddev->dev_sectors = ((sector_t)sb->size) * 2;
 		mddev->events = ev1;
 		mddev->bitmap_info.offset = 0;
+		mddev->bitmap_info.space = 0;
+		/* bitmap can use 60 K after the 4K superblocks */
 		mddev->bitmap_info.default_offset = MD_SB_BYTES >> 9;
+		mddev->bitmap_info.default_space = 64*2 - (MD_SB_BYTES >> 9);
+		mddev->reshape_backwards = 0;
 
 		if (mddev->minor_version >= 91) {
 			mddev->reshape_position = sb->reshape_position;
@@ -1192,6 +1210,8 @@
 			mddev->new_level = sb->new_level;
 			mddev->new_layout = sb->new_layout;
 			mddev->new_chunk_sectors = sb->new_chunk >> 9;
+			if (mddev->delta_disks < 0)
+				mddev->reshape_backwards = 1;
 		} else {
 			mddev->reshape_position = MaxSector;
 			mddev->delta_disks = 0;
@@ -1218,9 +1238,12 @@
 		mddev->max_disks = MD_SB_DISKS;
 
 		if (sb->state & (1<<MD_SB_BITMAP_PRESENT) &&
-		    mddev->bitmap_info.file == NULL)
+		    mddev->bitmap_info.file == NULL) {
 			mddev->bitmap_info.offset =
 				mddev->bitmap_info.default_offset;
+			mddev->bitmap_info.space =
+				mddev->bitmap_info.space;
+		}
 
 	} else if (mddev->pers == NULL) {
 		/* Insist on good event counter while assembling, except
@@ -1434,6 +1457,12 @@
 	return num_sectors;
 }
 
+static int
+super_90_allow_new_offset(struct md_rdev *rdev, unsigned long long new_offset)
+{
+	/* non-zero offset changes not possible with v0.90 */
+	return new_offset == 0;
+}
 
 /*
  * version 1 superblock
@@ -1469,6 +1498,7 @@
 	struct mdp_superblock_1 *sb;
 	int ret;
 	sector_t sb_start;
+	sector_t sectors;
 	char b[BDEVNAME_SIZE], b2[BDEVNAME_SIZE];
 	int bmask;
 
@@ -1523,9 +1553,18 @@
 		       bdevname(rdev->bdev,b));
 		return -EINVAL;
 	}
+	if (sb->pad0 ||
+	    sb->pad3[0] ||
+	    memcmp(sb->pad3, sb->pad3+1, sizeof(sb->pad3) - sizeof(sb->pad3[1])))
+		/* Some padding is non-zero, might be a new feature */
+		return -EINVAL;
 
 	rdev->preferred_minor = 0xffff;
 	rdev->data_offset = le64_to_cpu(sb->data_offset);
+	rdev->new_data_offset = rdev->data_offset;
+	if ((le32_to_cpu(sb->feature_map) & MD_FEATURE_RESHAPE_ACTIVE) &&
+	    (le32_to_cpu(sb->feature_map) & MD_FEATURE_NEW_OFFSET))
+		rdev->new_data_offset += (s32)le32_to_cpu(sb->new_offset);
 	atomic_set(&rdev->corrected_errors, le32_to_cpu(sb->cnt_corrected_read));
 
 	rdev->sb_size = le32_to_cpu(sb->max_dev) * 2 + 256;
@@ -1536,6 +1575,9 @@
 	if (minor_version
 	    && rdev->data_offset < sb_start + (rdev->sb_size/512))
 		return -EINVAL;
+	if (minor_version
+	    && rdev->new_data_offset < sb_start + (rdev->sb_size/512))
+		return -EINVAL;
 
 	if (sb->level == cpu_to_le32(LEVEL_MULTIPATH))
 		rdev->desc_nr = -1;
@@ -1607,16 +1649,14 @@
 		else
 			ret = 0;
 	}
-	if (minor_version)
-		rdev->sectors = (i_size_read(rdev->bdev->bd_inode) >> 9) -
-			le64_to_cpu(sb->data_offset);
-	else
-		rdev->sectors = rdev->sb_start;
-	if (rdev->sectors < le64_to_cpu(sb->data_size))
+	if (minor_version) {
+		sectors = (i_size_read(rdev->bdev->bd_inode) >> 9);
+		sectors -= rdev->data_offset;
+	} else
+		sectors = rdev->sb_start;
+	if (sectors < le64_to_cpu(sb->data_size))
 		return -EINVAL;
 	rdev->sectors = le64_to_cpu(sb->data_size);
-	if (le64_to_cpu(sb->size) > rdev->sectors)
-		return -EINVAL;
 	return ret;
 }
 
@@ -1644,17 +1684,37 @@
 		mddev->dev_sectors = le64_to_cpu(sb->size);
 		mddev->events = ev1;
 		mddev->bitmap_info.offset = 0;
+		mddev->bitmap_info.space = 0;
+		/* Default location for bitmap is 1K after superblock
+		 * using 3K - total of 4K
+		 */
 		mddev->bitmap_info.default_offset = 1024 >> 9;
-		
+		mddev->bitmap_info.default_space = (4096-1024) >> 9;
+		mddev->reshape_backwards = 0;
+
 		mddev->recovery_cp = le64_to_cpu(sb->resync_offset);
 		memcpy(mddev->uuid, sb->set_uuid, 16);
 
 		mddev->max_disks =  (4096-256)/2;
 
 		if ((le32_to_cpu(sb->feature_map) & MD_FEATURE_BITMAP_OFFSET) &&
-		    mddev->bitmap_info.file == NULL )
+		    mddev->bitmap_info.file == NULL) {
 			mddev->bitmap_info.offset =
 				(__s32)le32_to_cpu(sb->bitmap_offset);
+			/* Metadata doesn't record how much space is available.
+			 * For 1.0, we assume we can use up to the superblock
+			 * if before, else to 4K beyond superblock.
+			 * For others, assume no change is possible.
+			 */
+			if (mddev->minor_version > 0)
+				mddev->bitmap_info.space = 0;
+			else if (mddev->bitmap_info.offset > 0)
+				mddev->bitmap_info.space =
+					8 - mddev->bitmap_info.offset;
+			else
+				mddev->bitmap_info.space =
+					-mddev->bitmap_info.offset;
+		}
 
 		if ((le32_to_cpu(sb->feature_map) & MD_FEATURE_RESHAPE_ACTIVE)) {
 			mddev->reshape_position = le64_to_cpu(sb->reshape_position);
@@ -1662,6 +1722,11 @@
 			mddev->new_level = le32_to_cpu(sb->new_level);
 			mddev->new_layout = le32_to_cpu(sb->new_layout);
 			mddev->new_chunk_sectors = le32_to_cpu(sb->new_chunk);
+			if (mddev->delta_disks < 0 ||
+			    (mddev->delta_disks == 0 &&
+			     (le32_to_cpu(sb->feature_map)
+			      & MD_FEATURE_RESHAPE_BACKWARDS)))
+				mddev->reshape_backwards = 1;
 		} else {
 			mddev->reshape_position = MaxSector;
 			mddev->delta_disks = 0;
@@ -1735,7 +1800,6 @@
 	sb->feature_map = 0;
 	sb->pad0 = 0;
 	sb->recovery_offset = cpu_to_le64(0);
-	memset(sb->pad1, 0, sizeof(sb->pad1));
 	memset(sb->pad3, 0, sizeof(sb->pad3));
 
 	sb->utime = cpu_to_le64((__u64)mddev->utime);
@@ -1757,6 +1821,8 @@
 		sb->devflags |= WriteMostly1;
 	else
 		sb->devflags &= ~WriteMostly1;
+	sb->data_offset = cpu_to_le64(rdev->data_offset);
+	sb->data_size = cpu_to_le64(rdev->sectors);
 
 	if (mddev->bitmap && mddev->bitmap_info.file == NULL) {
 		sb->bitmap_offset = cpu_to_le32((__u32)mddev->bitmap_info.offset);
@@ -1781,6 +1847,16 @@
 		sb->delta_disks = cpu_to_le32(mddev->delta_disks);
 		sb->new_level = cpu_to_le32(mddev->new_level);
 		sb->new_chunk = cpu_to_le32(mddev->new_chunk_sectors);
+		if (mddev->delta_disks == 0 &&
+		    mddev->reshape_backwards)
+			sb->feature_map
+				|= cpu_to_le32(MD_FEATURE_RESHAPE_BACKWARDS);
+		if (rdev->new_data_offset != rdev->data_offset) {
+			sb->feature_map
+				|= cpu_to_le32(MD_FEATURE_NEW_OFFSET);
+			sb->new_offset = cpu_to_le32((__u32)(rdev->new_data_offset
+							     - rdev->data_offset));
+		}
 	}
 
 	if (rdev->badblocks.count == 0)
@@ -1857,6 +1933,8 @@
 	sector_t max_sectors;
 	if (num_sectors && num_sectors < rdev->mddev->dev_sectors)
 		return 0; /* component must fit device */
+	if (rdev->data_offset != rdev->new_data_offset)
+		return 0; /* too confusing */
 	if (rdev->sb_start < rdev->data_offset) {
 		/* minor versions 1 and 2; superblock before data */
 		max_sectors = i_size_read(rdev->bdev->bd_inode) >> 9;
@@ -1884,6 +1962,40 @@
 		       rdev->sb_page);
 	md_super_wait(rdev->mddev);
 	return num_sectors;
+
+}
+
+static int
+super_1_allow_new_offset(struct md_rdev *rdev,
+			 unsigned long long new_offset)
+{
+	/* All necessary checks on new >= old have been done */
+	struct bitmap *bitmap;
+	if (new_offset >= rdev->data_offset)
+		return 1;
+
+	/* with 1.0 metadata, there is no metadata to tread on
+	 * so we can always move back */
+	if (rdev->mddev->minor_version == 0)
+		return 1;
+
+	/* otherwise we must be sure not to step on
+	 * any metadata, so stay:
+	 * 36K beyond start of superblock
+	 * beyond end of badblocks
+	 * beyond write-intent bitmap
+	 */
+	if (rdev->sb_start + (32+4)*2 > new_offset)
+		return 0;
+	bitmap = rdev->mddev->bitmap;
+	if (bitmap && !rdev->mddev->bitmap_info.file &&
+	    rdev->sb_start + rdev->mddev->bitmap_info.offset +
+	    bitmap->storage.file_pages * (PAGE_SIZE>>9) > new_offset)
+		return 0;
+	if (rdev->badblocks.sector + rdev->badblocks.size > new_offset)
+		return 0;
+
+	return 1;
 }
 
 static struct super_type super_types[] = {
@@ -1894,6 +2006,7 @@
 		.validate_super	    = super_90_validate,
 		.sync_super	    = super_90_sync,
 		.rdev_size_change   = super_90_rdev_size_change,
+		.allow_new_offset   = super_90_allow_new_offset,
 	},
 	[1] = {
 		.name	= "md-1",
@@ -1902,6 +2015,7 @@
 		.validate_super	    = super_1_validate,
 		.sync_super	    = super_1_sync,
 		.rdev_size_change   = super_1_rdev_size_change,
+		.allow_new_offset   = super_1_allow_new_offset,
 	},
 };
 
@@ -2105,9 +2219,7 @@
 	sysfs_remove_link(&rdev->kobj, "block");
 	sysfs_put(rdev->sysfs_state);
 	rdev->sysfs_state = NULL;
-	kfree(rdev->badblocks.page);
 	rdev->badblocks.count = 0;
-	rdev->badblocks.page = NULL;
 	/* We need to delay this, otherwise we can deadlock when
 	 * writing to 'remove' to "dev/state".  We also need
 	 * to delay it due to rcu usage.
@@ -2158,7 +2270,7 @@
 		bdevname(rdev->bdev,b));
 	if (rdev->mddev)
 		MD_BUG();
-	free_disk_sb(rdev);
+	md_rdev_clear(rdev);
 #ifndef MODULE
 	if (test_bit(AutoDetected, &rdev->flags))
 		md_autodetect_dev(rdev->bdev->bd_dev);
@@ -2809,9 +2921,8 @@
 static ssize_t
 offset_store(struct md_rdev *rdev, const char *buf, size_t len)
 {
-	char *e;
-	unsigned long long offset = simple_strtoull(buf, &e, 10);
-	if (e==buf || (*e && *e != '\n'))
+	unsigned long long offset;
+	if (strict_strtoull(buf, 10, &offset) < 0)
 		return -EINVAL;
 	if (rdev->mddev->pers && rdev->raid_disk >= 0)
 		return -EBUSY;
@@ -2826,6 +2937,63 @@
 static struct rdev_sysfs_entry rdev_offset =
 __ATTR(offset, S_IRUGO|S_IWUSR, offset_show, offset_store);
 
+static ssize_t new_offset_show(struct md_rdev *rdev, char *page)
+{
+	return sprintf(page, "%llu\n",
+		       (unsigned long long)rdev->new_data_offset);
+}
+
+static ssize_t new_offset_store(struct md_rdev *rdev,
+				const char *buf, size_t len)
+{
+	unsigned long long new_offset;
+	struct mddev *mddev = rdev->mddev;
+
+	if (strict_strtoull(buf, 10, &new_offset) < 0)
+		return -EINVAL;
+
+	if (mddev->sync_thread)
+		return -EBUSY;
+	if (new_offset == rdev->data_offset)
+		/* reset is always permitted */
+		;
+	else if (new_offset > rdev->data_offset) {
+		/* must not push array size beyond rdev_sectors */
+		if (new_offset - rdev->data_offset
+		    + mddev->dev_sectors > rdev->sectors)
+				return -E2BIG;
+	}
+	/* Metadata worries about other space details. */
+
+	/* decreasing the offset is inconsistent with a backwards
+	 * reshape.
+	 */
+	if (new_offset < rdev->data_offset &&
+	    mddev->reshape_backwards)
+		return -EINVAL;
+	/* Increasing offset is inconsistent with forwards
+	 * reshape.  reshape_direction should be set to
+	 * 'backwards' first.
+	 */
+	if (new_offset > rdev->data_offset &&
+	    !mddev->reshape_backwards)
+		return -EINVAL;
+
+	if (mddev->pers && mddev->persistent &&
+	    !super_types[mddev->major_version]
+	    .allow_new_offset(rdev, new_offset))
+		return -E2BIG;
+	rdev->new_data_offset = new_offset;
+	if (new_offset > rdev->data_offset)
+		mddev->reshape_backwards = 1;
+	else if (new_offset < rdev->data_offset)
+		mddev->reshape_backwards = 0;
+
+	return len;
+}
+static struct rdev_sysfs_entry rdev_new_offset =
+__ATTR(new_offset, S_IRUGO|S_IWUSR, new_offset_show, new_offset_store);
+
 static ssize_t
 rdev_size_show(struct md_rdev *rdev, char *page)
 {
@@ -2870,6 +3038,8 @@
 
 	if (strict_blocks_to_sectors(buf, &sectors) < 0)
 		return -EINVAL;
+	if (rdev->data_offset != rdev->new_data_offset)
+		return -EINVAL; /* too confusing */
 	if (my_mddev->pers && rdev->raid_disk >= 0) {
 		if (my_mddev->persistent) {
 			sectors = super_types[my_mddev->major_version].
@@ -3006,6 +3176,7 @@
 	&rdev_errors.attr,
 	&rdev_slot.attr,
 	&rdev_offset.attr,
+	&rdev_new_offset.attr,
 	&rdev_size.attr,
 	&rdev_recovery_start.attr,
 	&rdev_bad_blocks.attr,
@@ -3080,6 +3251,7 @@
 	rdev->raid_disk = -1;
 	rdev->flags = 0;
 	rdev->data_offset = 0;
+	rdev->new_data_offset = 0;
 	rdev->sb_events = 0;
 	rdev->last_read_error.tv_sec  = 0;
 	rdev->last_read_error.tv_nsec = 0;
@@ -3178,8 +3350,7 @@
 abort_free:
 	if (rdev->bdev)
 		unlock_rdev(rdev);
-	free_disk_sb(rdev);
-	kfree(rdev->badblocks.page);
+	md_rdev_clear(rdev);
 	kfree(rdev);
 	return ERR_PTR(err);
 }
@@ -3419,6 +3590,7 @@
 		mddev->new_chunk_sectors = mddev->chunk_sectors;
 		mddev->raid_disks -= mddev->delta_disks;
 		mddev->delta_disks = 0;
+		mddev->reshape_backwards = 0;
 		module_put(pers->owner);
 		printk(KERN_WARNING "md: %s: %s would not accept array\n",
 		       mdname(mddev), clevel);
@@ -3492,6 +3664,7 @@
 	mddev->layout = mddev->new_layout;
 	mddev->chunk_sectors = mddev->new_chunk_sectors;
 	mddev->delta_disks = 0;
+	mddev->reshape_backwards = 0;
 	mddev->degraded = 0;
 	if (mddev->pers->sync_request == NULL) {
 		/* this is now an array without redundancy, so
@@ -3501,10 +3674,8 @@
 		del_timer_sync(&mddev->safemode_timer);
 	}
 	pers->run(mddev);
-	mddev_resume(mddev);
 	set_bit(MD_CHANGE_DEVS, &mddev->flags);
-	set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
-	md_wakeup_thread(mddev->thread);
+	mddev_resume(mddev);
 	sysfs_notify(&mddev->kobj, NULL, "level");
 	md_new_event(mddev);
 	return rv;
@@ -3582,9 +3753,20 @@
 	if (mddev->pers)
 		rv = update_raid_disks(mddev, n);
 	else if (mddev->reshape_position != MaxSector) {
+		struct md_rdev *rdev;
 		int olddisks = mddev->raid_disks - mddev->delta_disks;
+
+		rdev_for_each(rdev, mddev) {
+			if (olddisks < n &&
+			    rdev->data_offset < rdev->new_data_offset)
+				return -EINVAL;
+			if (olddisks > n &&
+			    rdev->data_offset > rdev->new_data_offset)
+				return -EINVAL;
+		}
 		mddev->delta_disks = n - olddisks;
 		mddev->raid_disks = n;
+		mddev->reshape_backwards = (mddev->delta_disks < 0);
 	} else
 		mddev->raid_disks = n;
 	return rv ? rv : len;
@@ -4266,7 +4448,8 @@
 	if (!test_bit(MD_RECOVERY_RUNNING, &mddev->recovery))
 		return sprintf(page, "none\n");
 
-	if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery))
+	if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery) ||
+	    test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery))
 		max_sectors = mddev->resync_max_sectors;
 	else
 		max_sectors = mddev->dev_sectors;
@@ -4428,6 +4611,7 @@
 static ssize_t
 reshape_position_store(struct mddev *mddev, const char *buf, size_t len)
 {
+	struct md_rdev *rdev;
 	char *e;
 	unsigned long long new = simple_strtoull(buf, &e, 10);
 	if (mddev->pers)
@@ -4436,9 +4620,12 @@
 		return -EINVAL;
 	mddev->reshape_position = new;
 	mddev->delta_disks = 0;
+	mddev->reshape_backwards = 0;
 	mddev->new_level = mddev->level;
 	mddev->new_layout = mddev->layout;
 	mddev->new_chunk_sectors = mddev->chunk_sectors;
+	rdev_for_each(rdev, mddev)
+		rdev->new_data_offset = rdev->data_offset;
 	return len;
 }
 
@@ -4447,6 +4634,42 @@
        reshape_position_store);
 
 static ssize_t
+reshape_direction_show(struct mddev *mddev, char *page)
+{
+	return sprintf(page, "%s\n",
+		       mddev->reshape_backwards ? "backwards" : "forwards");
+}
+
+static ssize_t
+reshape_direction_store(struct mddev *mddev, const char *buf, size_t len)
+{
+	int backwards = 0;
+	if (cmd_match(buf, "forwards"))
+		backwards = 0;
+	else if (cmd_match(buf, "backwards"))
+		backwards = 1;
+	else
+		return -EINVAL;
+	if (mddev->reshape_backwards == backwards)
+		return len;
+
+	/* check if we are allowed to change */
+	if (mddev->delta_disks)
+		return -EBUSY;
+
+	if (mddev->persistent &&
+	    mddev->major_version == 0)
+		return -EINVAL;
+
+	mddev->reshape_backwards = backwards;
+	return len;
+}
+
+static struct md_sysfs_entry md_reshape_direction =
+__ATTR(reshape_direction, S_IRUGO|S_IWUSR, reshape_direction_show,
+       reshape_direction_store);
+
+static ssize_t
 array_size_show(struct mddev *mddev, char *page)
 {
 	if (mddev->external_size)
@@ -4501,6 +4724,7 @@
 	&md_safe_delay.attr,
 	&md_array_state.attr,
 	&md_reshape_position.attr,
+	&md_reshape_direction.attr,
 	&md_array_size.attr,
 	&max_corr_read_errors.attr,
 	NULL,
@@ -4914,7 +5138,8 @@
 		err = -EINVAL;
 		mddev->pers->stop(mddev);
 	}
-	if (err == 0 && mddev->pers->sync_request) {
+	if (err == 0 && mddev->pers->sync_request &&
+	    (mddev->bitmap_info.file || mddev->bitmap_info.offset)) {
 		err = bitmap_create(mddev);
 		if (err) {
 			printk(KERN_ERR "%s: failed to create bitmap (%d)\n",
@@ -5064,6 +5289,7 @@
 	mddev->events = 0;
 	mddev->can_decrease_events = 0;
 	mddev->delta_disks = 0;
+	mddev->reshape_backwards = 0;
 	mddev->new_level = LEVEL_NONE;
 	mddev->new_layout = 0;
 	mddev->new_chunk_sectors = 0;
@@ -5079,6 +5305,7 @@
 	mddev->merge_check_needed = 0;
 	mddev->bitmap_info.offset = 0;
 	mddev->bitmap_info.default_offset = 0;
+	mddev->bitmap_info.default_space = 0;
 	mddev->bitmap_info.chunksize = 0;
 	mddev->bitmap_info.daemon_sleep = 0;
 	mddev->bitmap_info.max_write_behind = 0;
@@ -5421,7 +5648,7 @@
 		goto out;
 
 	/* bitmap disabled, zero the first byte and copy out */
-	if (!mddev->bitmap || !mddev->bitmap->file) {
+	if (!mddev->bitmap || !mddev->bitmap->storage.file) {
 		file->pathname[0] = '\0';
 		goto copy_out;
 	}
@@ -5430,7 +5657,8 @@
 	if (!buf)
 		goto out;
 
-	ptr = d_path(&mddev->bitmap->file->f_path, buf, sizeof(file->pathname));
+	ptr = d_path(&mddev->bitmap->storage.file->f_path,
+		     buf, sizeof(file->pathname));
 	if (IS_ERR(ptr))
 		goto out;
 
@@ -5875,6 +6103,7 @@
 	set_bit(MD_CHANGE_DEVS, &mddev->flags);
 
 	mddev->bitmap_info.default_offset = MD_SB_BYTES >> 9;
+	mddev->bitmap_info.default_space = 64*2 - (MD_SB_BYTES >> 9);
 	mddev->bitmap_info.offset = 0;
 
 	mddev->reshape_position = MaxSector;
@@ -5888,6 +6117,7 @@
 	mddev->new_chunk_sectors = mddev->chunk_sectors;
 	mddev->new_layout = mddev->layout;
 	mddev->delta_disks = 0;
+	mddev->reshape_backwards = 0;
 
 	return 0;
 }
@@ -5922,11 +6152,7 @@
 	 */
 	if (mddev->sync_thread)
 		return -EBUSY;
-	if (mddev->bitmap)
-		/* Sorry, cannot grow a bitmap yet, just remove it,
-		 * grow, and re-add.
-		 */
-		return -EBUSY;
+
 	rdev_for_each(rdev, mddev) {
 		sector_t avail = rdev->sectors;
 
@@ -5944,6 +6170,7 @@
 static int update_raid_disks(struct mddev *mddev, int raid_disks)
 {
 	int rv;
+	struct md_rdev *rdev;
 	/* change the number of raid disks */
 	if (mddev->pers->check_reshape == NULL)
 		return -EINVAL;
@@ -5952,11 +6179,27 @@
 		return -EINVAL;
 	if (mddev->sync_thread || mddev->reshape_position != MaxSector)
 		return -EBUSY;
+
+	rdev_for_each(rdev, mddev) {
+		if (mddev->raid_disks < raid_disks &&
+		    rdev->data_offset < rdev->new_data_offset)
+			return -EINVAL;
+		if (mddev->raid_disks > raid_disks &&
+		    rdev->data_offset > rdev->new_data_offset)
+			return -EINVAL;
+	}
+
 	mddev->delta_disks = raid_disks - mddev->raid_disks;
+	if (mddev->delta_disks < 0)
+		mddev->reshape_backwards = 1;
+	else if (mddev->delta_disks > 0)
+		mddev->reshape_backwards = 0;
 
 	rv = mddev->pers->check_reshape(mddev);
-	if (rv < 0)
+	if (rv < 0) {
 		mddev->delta_disks = 0;
+		mddev->reshape_backwards = 0;
+	}
 	return rv;
 }
 
@@ -6039,6 +6282,8 @@
 				return -EINVAL;
 			mddev->bitmap_info.offset =
 				mddev->bitmap_info.default_offset;
+			mddev->bitmap_info.space =
+				mddev->bitmap_info.default_space;
 			mddev->pers->quiesce(mddev, 1);
 			rv = bitmap_create(mddev);
 			if (!rv)
@@ -6050,7 +6295,7 @@
 			/* remove the bitmap */
 			if (!mddev->bitmap)
 				return -ENOENT;
-			if (mddev->bitmap->file)
+			if (mddev->bitmap->storage.file)
 				return -EINVAL;
 			mddev->pers->quiesce(mddev, 1);
 			bitmap_destroy(mddev);
@@ -6373,6 +6618,9 @@
 	struct mddev *mddev = mddev_find(bdev->bd_dev);
 	int err;
 
+	if (!mddev)
+		return -ENODEV;
+
 	if (mddev->gendisk != bdev->bd_disk) {
 		/* we are racing with mddev_put which is discarding this
 		 * bd_disk.
@@ -6584,7 +6832,8 @@
 
 	resync = mddev->curr_resync - atomic_read(&mddev->recovery_active);
 
-	if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery))
+	if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery) ||
+	    test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery))
 		max_sectors = mddev->resync_max_sectors;
 	else
 		max_sectors = mddev->dev_sectors;
@@ -7147,7 +7396,7 @@
 			j = mddev->recovery_cp;
 
 	} else if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery))
-		max_sectors = mddev->dev_sectors;
+		max_sectors = mddev->resync_max_sectors;
 	else {
 		/* recovery follows the physical size of devices */
 		max_sectors = mddev->dev_sectors;
@@ -7598,7 +7847,7 @@
 			goto unlock;
 
 		if (mddev->pers->sync_request) {
-			if (spares && mddev->bitmap && ! mddev->bitmap->file) {
+			if (spares) {
 				/* We are adding a device or devices to an array
 				 * which has the bitmap stored on all devices.
 				 * So make sure all bitmap pages get written
@@ -7646,6 +7895,20 @@
 }
 EXPORT_SYMBOL(md_wait_for_blocked_rdev);
 
+void md_finish_reshape(struct mddev *mddev)
+{
+	/* called be personality module when reshape completes. */
+	struct md_rdev *rdev;
+
+	rdev_for_each(rdev, mddev) {
+		if (rdev->data_offset > rdev->new_data_offset)
+			rdev->sectors += rdev->data_offset - rdev->new_data_offset;
+		else
+			rdev->sectors -= rdev->new_data_offset - rdev->data_offset;
+		rdev->data_offset = rdev->new_data_offset;
+	}
+}
+EXPORT_SYMBOL(md_finish_reshape);
 
 /* Bad block management.
  * We can record which blocks on each device are 'bad' and so just
@@ -7894,10 +8157,15 @@
 }
 
 int rdev_set_badblocks(struct md_rdev *rdev, sector_t s, int sectors,
-		       int acknowledged)
+		       int is_new)
 {
-	int rv = md_set_badblocks(&rdev->badblocks,
-				  s + rdev->data_offset, sectors, acknowledged);
+	int rv;
+	if (is_new)
+		s += rdev->new_data_offset;
+	else
+		s += rdev->data_offset;
+	rv = md_set_badblocks(&rdev->badblocks,
+			      s, sectors, 0);
 	if (rv) {
 		/* Make sure they get written out promptly */
 		sysfs_notify_dirent_safe(rdev->sysfs_state);
@@ -8003,11 +8271,15 @@
 	return rv;
 }
 
-int rdev_clear_badblocks(struct md_rdev *rdev, sector_t s, int sectors)
+int rdev_clear_badblocks(struct md_rdev *rdev, sector_t s, int sectors,
+			 int is_new)
 {
+	if (is_new)
+		s += rdev->new_data_offset;
+	else
+		s += rdev->data_offset;
 	return md_clear_badblocks(&rdev->badblocks,
-				  s + rdev->data_offset,
-				  sectors);
+				  s, sectors);
 }
 EXPORT_SYMBOL_GPL(rdev_clear_badblocks);
 
diff --git a/drivers/md/md.h b/drivers/md/md.h
index 1c2063c..7b4a3c3 100644
--- a/drivers/md/md.h
+++ b/drivers/md/md.h
@@ -55,6 +55,7 @@
 	int		sb_loaded;
 	__u64		sb_events;
 	sector_t	data_offset;	/* start of data in array */
+	sector_t	new_data_offset;/* only relevant while reshaping */
 	sector_t 	sb_start;	/* offset of the super block (in 512byte sectors) */
 	int		sb_size;	/* bytes in the superblock */
 	int		preferred_minor;	/* autorun support */
@@ -193,8 +194,9 @@
 	return 0;
 }
 extern int rdev_set_badblocks(struct md_rdev *rdev, sector_t s, int sectors,
-			      int acknowledged);
-extern int rdev_clear_badblocks(struct md_rdev *rdev, sector_t s, int sectors);
+			      int is_new);
+extern int rdev_clear_badblocks(struct md_rdev *rdev, sector_t s, int sectors,
+				int is_new);
 extern void md_ack_all_badblocks(struct badblocks *bb);
 
 struct mddev {
@@ -262,6 +264,7 @@
 	sector_t			reshape_position;
 	int				delta_disks, new_level, new_layout;
 	int				new_chunk_sectors;
+	int				reshape_backwards;
 
 	atomic_t			plug_cnt;	/* If device is expecting
 							 * more bios soon.
@@ -390,10 +393,13 @@
 						 * For external metadata, offset
 						 * from start of device. 
 						 */
+		unsigned long		space; /* space available at this offset */
 		loff_t			default_offset; /* this is the offset to use when
 							 * hot-adding a bitmap.  It should
 							 * eventually be settable by sysfs.
 							 */
+		unsigned long		default_space; /* space available at
+							* default offset */
 		struct mutex		mutex;
 		unsigned long		chunksize;
 		unsigned long		daemon_sleep; /* how many jiffies between updates? */
@@ -591,6 +597,7 @@
 extern void md_write_end(struct mddev *mddev);
 extern void md_done_sync(struct mddev *mddev, int blocks, int ok);
 extern void md_error(struct mddev *mddev, struct md_rdev *rdev);
+extern void md_finish_reshape(struct mddev *mddev);
 
 extern int mddev_congested(struct mddev *mddev, int bits);
 extern void md_flush_request(struct mddev *mddev, struct bio *bio);
@@ -615,6 +622,7 @@
 extern void md_stop(struct mddev *mddev);
 extern void md_stop_writes(struct mddev *mddev);
 extern int md_rdev_init(struct md_rdev *rdev);
+extern void md_rdev_clear(struct md_rdev *rdev);
 
 extern void mddev_suspend(struct mddev *mddev);
 extern void mddev_resume(struct mddev *mddev);
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index 15dd59b..835de71 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -1859,7 +1859,9 @@
 
 			rdev = conf->mirrors[d].rdev;
 			if (rdev &&
-			    test_bit(In_sync, &rdev->flags) &&
+			    (test_bit(In_sync, &rdev->flags) ||
+			     (!test_bit(Faulty, &rdev->flags) &&
+			      rdev->recovery_offset >= sect + s)) &&
 			    is_badblock(rdev, sect, s,
 					&first_bad, &bad_sectors) == 0 &&
 			    sync_page_io(rdev, sect, s<<9,
@@ -2024,7 +2026,7 @@
 			continue;
 		if (test_bit(BIO_UPTODATE, &bio->bi_flags) &&
 		    test_bit(R1BIO_MadeGood, &r1_bio->state)) {
-			rdev_clear_badblocks(rdev, r1_bio->sector, s);
+			rdev_clear_badblocks(rdev, r1_bio->sector, s, 0);
 		}
 		if (!test_bit(BIO_UPTODATE, &bio->bi_flags) &&
 		    test_bit(R1BIO_WriteError, &r1_bio->state)) {
@@ -2044,7 +2046,7 @@
 			struct md_rdev *rdev = conf->mirrors[m].rdev;
 			rdev_clear_badblocks(rdev,
 					     r1_bio->sector,
-					     r1_bio->sectors);
+					     r1_bio->sectors, 0);
 			rdev_dec_pending(rdev, conf->mddev);
 		} else if (r1_bio->bios[m] != NULL) {
 			/* This drive got a write error.  We need to
@@ -2598,7 +2600,8 @@
 		if (!disk->rdev ||
 		    !test_bit(In_sync, &disk->rdev->flags)) {
 			disk->head_position = 0;
-			if (disk->rdev)
+			if (disk->rdev &&
+			    (disk->rdev->saved_raid_disk < 0))
 				conf->fullsync = 1;
 		} else if (conf->last_used < 0)
 			/*
@@ -2750,9 +2753,16 @@
 	 * any io in the removed space completes, but it hardly seems
 	 * worth it.
 	 */
-	md_set_array_sectors(mddev, raid1_size(mddev, sectors, 0));
-	if (mddev->array_sectors > raid1_size(mddev, sectors, 0))
+	sector_t newsize = raid1_size(mddev, sectors, 0);
+	if (mddev->external_size &&
+	    mddev->array_sectors > newsize)
 		return -EINVAL;
+	if (mddev->bitmap) {
+		int ret = bitmap_resize(mddev->bitmap, newsize, 0, 0);
+		if (ret)
+			return ret;
+	}
+	md_set_array_sectors(mddev, newsize);
 	set_capacity(mddev->gendisk, mddev->array_sectors);
 	revalidate_disk(mddev->gendisk);
 	if (sectors > mddev->dev_sectors &&
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 3f91c2e..987db37 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -24,6 +24,7 @@
 #include <linux/module.h>
 #include <linux/seq_file.h>
 #include <linux/ratelimit.h>
+#include <linux/kthread.h>
 #include "md.h"
 #include "raid10.h"
 #include "raid0.h"
@@ -68,6 +69,11 @@
 static void allow_barrier(struct r10conf *conf);
 static void lower_barrier(struct r10conf *conf);
 static int enough(struct r10conf *conf, int ignore);
+static sector_t reshape_request(struct mddev *mddev, sector_t sector_nr,
+				int *skipped);
+static void reshape_request_write(struct mddev *mddev, struct r10bio *r10_bio);
+static void end_reshape_write(struct bio *bio, int error);
+static void end_reshape(struct r10conf *conf);
 
 static void * r10bio_pool_alloc(gfp_t gfp_flags, void *data)
 {
@@ -112,7 +118,8 @@
 	if (!r10_bio)
 		return NULL;
 
-	if (test_bit(MD_RECOVERY_SYNC, &conf->mddev->recovery))
+	if (test_bit(MD_RECOVERY_SYNC, &conf->mddev->recovery) ||
+	    test_bit(MD_RECOVERY_RESHAPE, &conf->mddev->recovery))
 		nalloc = conf->copies; /* resync */
 	else
 		nalloc = 2; /* recovery */
@@ -140,9 +147,10 @@
 		struct bio *rbio = r10_bio->devs[j].repl_bio;
 		bio = r10_bio->devs[j].bio;
 		for (i = 0; i < RESYNC_PAGES; i++) {
-			if (j == 1 && !test_bit(MD_RECOVERY_SYNC,
-						&conf->mddev->recovery)) {
-				/* we can share bv_page's during recovery */
+			if (j > 0 && !test_bit(MD_RECOVERY_SYNC,
+					       &conf->mddev->recovery)) {
+				/* we can share bv_page's during recovery
+				 * and reshape */
 				struct bio *rbio = r10_bio->devs[0].bio;
 				page = rbio->bi_io_vec[i].bv_page;
 				get_page(page);
@@ -165,10 +173,11 @@
 	while (j--)
 		for (i = 0; i < RESYNC_PAGES ; i++)
 			safe_put_page(r10_bio->devs[j].bio->bi_io_vec[i].bv_page);
-	j = -1;
+	j = 0;
 out_free_bio:
-	while (++j < nalloc) {
-		bio_put(r10_bio->devs[j].bio);
+	for ( ; j < nalloc; j++) {
+		if (r10_bio->devs[j].bio)
+			bio_put(r10_bio->devs[j].bio);
 		if (r10_bio->devs[j].repl_bio)
 			bio_put(r10_bio->devs[j].repl_bio);
 	}
@@ -504,79 +513,96 @@
  * sector offset to a virtual address
  */
 
-static void raid10_find_phys(struct r10conf *conf, struct r10bio *r10bio)
+static void __raid10_find_phys(struct geom *geo, struct r10bio *r10bio)
 {
 	int n,f;
 	sector_t sector;
 	sector_t chunk;
 	sector_t stripe;
 	int dev;
-
 	int slot = 0;
 
 	/* now calculate first sector/dev */
-	chunk = r10bio->sector >> conf->chunk_shift;
-	sector = r10bio->sector & conf->chunk_mask;
+	chunk = r10bio->sector >> geo->chunk_shift;
+	sector = r10bio->sector & geo->chunk_mask;
 
-	chunk *= conf->near_copies;
+	chunk *= geo->near_copies;
 	stripe = chunk;
-	dev = sector_div(stripe, conf->raid_disks);
-	if (conf->far_offset)
-		stripe *= conf->far_copies;
+	dev = sector_div(stripe, geo->raid_disks);
+	if (geo->far_offset)
+		stripe *= geo->far_copies;
 
-	sector += stripe << conf->chunk_shift;
+	sector += stripe << geo->chunk_shift;
 
 	/* and calculate all the others */
-	for (n=0; n < conf->near_copies; n++) {
+	for (n = 0; n < geo->near_copies; n++) {
 		int d = dev;
 		sector_t s = sector;
 		r10bio->devs[slot].addr = sector;
 		r10bio->devs[slot].devnum = d;
 		slot++;
 
-		for (f = 1; f < conf->far_copies; f++) {
-			d += conf->near_copies;
-			if (d >= conf->raid_disks)
-				d -= conf->raid_disks;
-			s += conf->stride;
+		for (f = 1; f < geo->far_copies; f++) {
+			d += geo->near_copies;
+			if (d >= geo->raid_disks)
+				d -= geo->raid_disks;
+			s += geo->stride;
 			r10bio->devs[slot].devnum = d;
 			r10bio->devs[slot].addr = s;
 			slot++;
 		}
 		dev++;
-		if (dev >= conf->raid_disks) {
+		if (dev >= geo->raid_disks) {
 			dev = 0;
-			sector += (conf->chunk_mask + 1);
+			sector += (geo->chunk_mask + 1);
 		}
 	}
-	BUG_ON(slot != conf->copies);
+}
+
+static void raid10_find_phys(struct r10conf *conf, struct r10bio *r10bio)
+{
+	struct geom *geo = &conf->geo;
+
+	if (conf->reshape_progress != MaxSector &&
+	    ((r10bio->sector >= conf->reshape_progress) !=
+	     conf->mddev->reshape_backwards)) {
+		set_bit(R10BIO_Previous, &r10bio->state);
+		geo = &conf->prev;
+	} else
+		clear_bit(R10BIO_Previous, &r10bio->state);
+
+	__raid10_find_phys(geo, r10bio);
 }
 
 static sector_t raid10_find_virt(struct r10conf *conf, sector_t sector, int dev)
 {
 	sector_t offset, chunk, vchunk;
+	/* Never use conf->prev as this is only called during resync
+	 * or recovery, so reshape isn't happening
+	 */
+	struct geom *geo = &conf->geo;
 
-	offset = sector & conf->chunk_mask;
-	if (conf->far_offset) {
+	offset = sector & geo->chunk_mask;
+	if (geo->far_offset) {
 		int fc;
-		chunk = sector >> conf->chunk_shift;
-		fc = sector_div(chunk, conf->far_copies);
-		dev -= fc * conf->near_copies;
+		chunk = sector >> geo->chunk_shift;
+		fc = sector_div(chunk, geo->far_copies);
+		dev -= fc * geo->near_copies;
 		if (dev < 0)
-			dev += conf->raid_disks;
+			dev += geo->raid_disks;
 	} else {
-		while (sector >= conf->stride) {
-			sector -= conf->stride;
-			if (dev < conf->near_copies)
-				dev += conf->raid_disks - conf->near_copies;
+		while (sector >= geo->stride) {
+			sector -= geo->stride;
+			if (dev < geo->near_copies)
+				dev += geo->raid_disks - geo->near_copies;
 			else
-				dev -= conf->near_copies;
+				dev -= geo->near_copies;
 		}
-		chunk = sector >> conf->chunk_shift;
+		chunk = sector >> geo->chunk_shift;
 	}
-	vchunk = chunk * conf->raid_disks + dev;
-	sector_div(vchunk, conf->near_copies);
-	return (vchunk << conf->chunk_shift) + offset;
+	vchunk = chunk * geo->raid_disks + dev;
+	sector_div(vchunk, geo->near_copies);
+	return (vchunk << geo->chunk_shift) + offset;
 }
 
 /**
@@ -597,10 +623,17 @@
 	struct r10conf *conf = mddev->private;
 	sector_t sector = bvm->bi_sector + get_start_sect(bvm->bi_bdev);
 	int max;
-	unsigned int chunk_sectors = mddev->chunk_sectors;
+	unsigned int chunk_sectors;
 	unsigned int bio_sectors = bvm->bi_size >> 9;
+	struct geom *geo = &conf->geo;
 
-	if (conf->near_copies < conf->raid_disks) {
+	chunk_sectors = (conf->geo.chunk_mask & conf->prev.chunk_mask) + 1;
+	if (conf->reshape_progress != MaxSector &&
+	    ((sector >= conf->reshape_progress) !=
+	     conf->mddev->reshape_backwards))
+		geo = &conf->prev;
+
+	if (geo->near_copies < geo->raid_disks) {
 		max = (chunk_sectors - ((sector & (chunk_sectors - 1))
 					+ bio_sectors)) << 9;
 		if (max < 0)
@@ -614,6 +647,12 @@
 	if (mddev->merge_check_needed) {
 		struct r10bio r10_bio;
 		int s;
+		if (conf->reshape_progress != MaxSector) {
+			/* Cannot give any guidance during reshape */
+			if (max <= biovec->bv_len && bio_sectors == 0)
+				return biovec->bv_len;
+			return 0;
+		}
 		r10_bio.sector = sector;
 		raid10_find_phys(conf, &r10_bio);
 		rcu_read_lock();
@@ -681,6 +720,7 @@
 	struct md_rdev *rdev, *best_rdev;
 	int do_balance;
 	int best_slot;
+	struct geom *geo = &conf->geo;
 
 	raid10_find_phys(conf, r10_bio);
 	rcu_read_lock();
@@ -761,11 +801,11 @@
 		 * sequential read speed for 'far copies' arrays.  So only
 		 * keep it for 'near' arrays, and review those later.
 		 */
-		if (conf->near_copies > 1 && !atomic_read(&rdev->nr_pending))
+		if (geo->near_copies > 1 && !atomic_read(&rdev->nr_pending))
 			break;
 
 		/* for far > 1 always use the lowest address */
-		if (conf->far_copies > 1)
+		if (geo->far_copies > 1)
 			new_distance = r10_bio->devs[slot].addr;
 		else
 			new_distance = abs(r10_bio->devs[slot].addr -
@@ -812,7 +852,10 @@
 	if (mddev_congested(mddev, bits))
 		return 1;
 	rcu_read_lock();
-	for (i = 0; i < conf->raid_disks && ret == 0; i++) {
+	for (i = 0;
+	     (i < conf->geo.raid_disks || i < conf->prev.raid_disks)
+		     && ret == 0;
+	     i++) {
 		struct md_rdev *rdev = rcu_dereference(conf->mirrors[i].rdev);
 		if (rdev && !test_bit(Faulty, &rdev->flags)) {
 			struct request_queue *q = bdev_get_queue(rdev->bdev);
@@ -973,13 +1016,24 @@
 	spin_unlock_irq(&conf->resync_lock);
 }
 
+static sector_t choose_data_offset(struct r10bio *r10_bio,
+				   struct md_rdev *rdev)
+{
+	if (!test_bit(MD_RECOVERY_RESHAPE, &rdev->mddev->recovery) ||
+	    test_bit(R10BIO_Previous, &r10_bio->state))
+		return rdev->data_offset;
+	else
+		return rdev->new_data_offset;
+}
+
 static void make_request(struct mddev *mddev, struct bio * bio)
 {
 	struct r10conf *conf = mddev->private;
 	struct r10bio *r10_bio;
 	struct bio *read_bio;
 	int i;
-	int chunk_sects = conf->chunk_mask + 1;
+	sector_t chunk_mask = (conf->geo.chunk_mask & conf->prev.chunk_mask);
+	int chunk_sects = chunk_mask + 1;
 	const int rw = bio_data_dir(bio);
 	const unsigned long do_sync = (bio->bi_rw & REQ_SYNC);
 	const unsigned long do_fua = (bio->bi_rw & REQ_FUA);
@@ -988,6 +1042,7 @@
 	int plugged;
 	int sectors_handled;
 	int max_sectors;
+	int sectors;
 
 	if (unlikely(bio->bi_rw & REQ_FLUSH)) {
 		md_flush_request(mddev, bio);
@@ -997,9 +1052,10 @@
 	/* If this request crosses a chunk boundary, we need to
 	 * split it.  This will only happen for 1 PAGE (or less) requests.
 	 */
-	if (unlikely( (bio->bi_sector & conf->chunk_mask) + (bio->bi_size >> 9)
-		      > chunk_sects &&
-		    conf->near_copies < conf->raid_disks)) {
+	if (unlikely((bio->bi_sector & chunk_mask) + (bio->bi_size >> 9)
+		     > chunk_sects
+		     && (conf->geo.near_copies < conf->geo.raid_disks
+			 || conf->prev.near_copies < conf->prev.raid_disks))) {
 		struct bio_pair *bp;
 		/* Sanity check -- queue functions should prevent this happening */
 		if (bio->bi_vcnt != 1 ||
@@ -1051,10 +1107,41 @@
 	 */
 	wait_barrier(conf);
 
+	sectors = bio->bi_size >> 9;
+	while (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery) &&
+	    bio->bi_sector < conf->reshape_progress &&
+	    bio->bi_sector + sectors > conf->reshape_progress) {
+		/* IO spans the reshape position.  Need to wait for
+		 * reshape to pass
+		 */
+		allow_barrier(conf);
+		wait_event(conf->wait_barrier,
+			   conf->reshape_progress <= bio->bi_sector ||
+			   conf->reshape_progress >= bio->bi_sector + sectors);
+		wait_barrier(conf);
+	}
+	if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery) &&
+	    bio_data_dir(bio) == WRITE &&
+	    (mddev->reshape_backwards
+	     ? (bio->bi_sector < conf->reshape_safe &&
+		bio->bi_sector + sectors > conf->reshape_progress)
+	     : (bio->bi_sector + sectors > conf->reshape_safe &&
+		bio->bi_sector < conf->reshape_progress))) {
+		/* Need to update reshape_position in metadata */
+		mddev->reshape_position = conf->reshape_progress;
+		set_bit(MD_CHANGE_DEVS, &mddev->flags);
+		set_bit(MD_CHANGE_PENDING, &mddev->flags);
+		md_wakeup_thread(mddev->thread);
+		wait_event(mddev->sb_wait,
+			   !test_bit(MD_CHANGE_PENDING, &mddev->flags));
+
+		conf->reshape_safe = mddev->reshape_position;
+	}
+
 	r10_bio = mempool_alloc(conf->r10bio_pool, GFP_NOIO);
 
 	r10_bio->master_bio = bio;
-	r10_bio->sectors = bio->bi_size >> 9;
+	r10_bio->sectors = sectors;
 
 	r10_bio->mddev = mddev;
 	r10_bio->sector = bio->bi_sector;
@@ -1093,7 +1180,7 @@
 		r10_bio->devs[slot].rdev = rdev;
 
 		read_bio->bi_sector = r10_bio->devs[slot].addr +
-			rdev->data_offset;
+			choose_data_offset(r10_bio, rdev);
 		read_bio->bi_bdev = rdev->bdev;
 		read_bio->bi_end_io = raid10_end_read_request;
 		read_bio->bi_rw = READ | do_sync;
@@ -1297,7 +1384,8 @@
 		r10_bio->devs[i].bio = mbio;
 
 		mbio->bi_sector	= (r10_bio->devs[i].addr+
-				   conf->mirrors[d].rdev->data_offset);
+				   choose_data_offset(r10_bio,
+						      conf->mirrors[d].rdev));
 		mbio->bi_bdev = conf->mirrors[d].rdev->bdev;
 		mbio->bi_end_io	= raid10_end_write_request;
 		mbio->bi_rw = WRITE | do_sync | do_fua;
@@ -1321,8 +1409,10 @@
 		 * so it cannot disappear, so the replacement cannot
 		 * become NULL here
 		 */
-		mbio->bi_sector	= (r10_bio->devs[i].addr+
-				   conf->mirrors[d].replacement->data_offset);
+		mbio->bi_sector	= (r10_bio->devs[i].addr +
+				   choose_data_offset(
+					   r10_bio,
+					   conf->mirrors[d].replacement));
 		mbio->bi_bdev = conf->mirrors[d].replacement->bdev;
 		mbio->bi_end_io	= raid10_end_write_request;
 		mbio->bi_rw = WRITE | do_sync | do_fua;
@@ -1368,19 +1458,19 @@
 	struct r10conf *conf = mddev->private;
 	int i;
 
-	if (conf->near_copies < conf->raid_disks)
+	if (conf->geo.near_copies < conf->geo.raid_disks)
 		seq_printf(seq, " %dK chunks", mddev->chunk_sectors / 2);
-	if (conf->near_copies > 1)
-		seq_printf(seq, " %d near-copies", conf->near_copies);
-	if (conf->far_copies > 1) {
-		if (conf->far_offset)
-			seq_printf(seq, " %d offset-copies", conf->far_copies);
+	if (conf->geo.near_copies > 1)
+		seq_printf(seq, " %d near-copies", conf->geo.near_copies);
+	if (conf->geo.far_copies > 1) {
+		if (conf->geo.far_offset)
+			seq_printf(seq, " %d offset-copies", conf->geo.far_copies);
 		else
-			seq_printf(seq, " %d far-copies", conf->far_copies);
+			seq_printf(seq, " %d far-copies", conf->geo.far_copies);
 	}
-	seq_printf(seq, " [%d/%d] [", conf->raid_disks,
-					conf->raid_disks - mddev->degraded);
-	for (i = 0; i < conf->raid_disks; i++)
+	seq_printf(seq, " [%d/%d] [", conf->geo.raid_disks,
+					conf->geo.raid_disks - mddev->degraded);
+	for (i = 0; i < conf->geo.raid_disks; i++)
 		seq_printf(seq, "%s",
 			      conf->mirrors[i].rdev &&
 			      test_bit(In_sync, &conf->mirrors[i].rdev->flags) ? "U" : "_");
@@ -1392,7 +1482,7 @@
  * Don't consider the device numbered 'ignore'
  * as we might be about to remove it.
  */
-static int enough(struct r10conf *conf, int ignore)
+static int _enough(struct r10conf *conf, struct geom *geo, int ignore)
 {
 	int first = 0;
 
@@ -1403,7 +1493,7 @@
 			if (conf->mirrors[first].rdev &&
 			    first != ignore)
 				cnt++;
-			first = (first+1) % conf->raid_disks;
+			first = (first+1) % geo->raid_disks;
 		}
 		if (cnt == 0)
 			return 0;
@@ -1411,6 +1501,12 @@
 	return 1;
 }
 
+static int enough(struct r10conf *conf, int ignore)
+{
+	return _enough(conf, &conf->geo, ignore) &&
+		_enough(conf, &conf->prev, ignore);
+}
+
 static void error(struct mddev *mddev, struct md_rdev *rdev)
 {
 	char b[BDEVNAME_SIZE];
@@ -1445,7 +1541,7 @@
 	       "md/raid10:%s: Disk failure on %s, disabling device.\n"
 	       "md/raid10:%s: Operation continuing on %d devices.\n",
 	       mdname(mddev), bdevname(rdev->bdev, b),
-	       mdname(mddev), conf->raid_disks - mddev->degraded);
+	       mdname(mddev), conf->geo.raid_disks - mddev->degraded);
 }
 
 static void print_conf(struct r10conf *conf)
@@ -1458,10 +1554,10 @@
 		printk(KERN_DEBUG "(!conf)\n");
 		return;
 	}
-	printk(KERN_DEBUG " --- wd:%d rd:%d\n", conf->raid_disks - conf->mddev->degraded,
-		conf->raid_disks);
+	printk(KERN_DEBUG " --- wd:%d rd:%d\n", conf->geo.raid_disks - conf->mddev->degraded,
+		conf->geo.raid_disks);
 
-	for (i = 0; i < conf->raid_disks; i++) {
+	for (i = 0; i < conf->geo.raid_disks; i++) {
 		char b[BDEVNAME_SIZE];
 		tmp = conf->mirrors + i;
 		if (tmp->rdev)
@@ -1493,7 +1589,7 @@
 	 * Find all non-in_sync disks within the RAID10 configuration
 	 * and mark them in_sync
 	 */
-	for (i = 0; i < conf->raid_disks; i++) {
+	for (i = 0; i < conf->geo.raid_disks; i++) {
 		tmp = conf->mirrors + i;
 		if (tmp->replacement
 		    && tmp->replacement->recovery_offset == MaxSector
@@ -1535,7 +1631,7 @@
 	int err = -EEXIST;
 	int mirror;
 	int first = 0;
-	int last = conf->raid_disks - 1;
+	int last = conf->geo.raid_disks - 1;
 	struct request_queue *q = bdev_get_queue(rdev->bdev);
 
 	if (mddev->recovery_cp < MaxSector)
@@ -1543,7 +1639,7 @@
 		 * very different from resync
 		 */
 		return -EBUSY;
-	if (rdev->saved_raid_disk < 0 && !enough(conf, -1))
+	if (rdev->saved_raid_disk < 0 && !_enough(conf, &conf->prev, -1))
 		return -EINVAL;
 
 	if (rdev->raid_disk >= 0)
@@ -1635,6 +1731,7 @@
 	if (!test_bit(Faulty, &rdev->flags) &&
 	    mddev->recovery_disabled != p->recovery_disabled &&
 	    (!p->replacement || p->replacement == rdev) &&
+	    number < conf->geo.raid_disks &&
 	    enough(conf, -1)) {
 		err = -EBUSY;
 		goto abort;
@@ -1676,7 +1773,11 @@
 	struct r10conf *conf = r10_bio->mddev->private;
 	int d;
 
-	d = find_bio_disk(conf, r10_bio, bio, NULL, NULL);
+	if (bio == r10_bio->master_bio) {
+		/* this is a reshape read */
+		d = r10_bio->read_slot; /* really the read dev */
+	} else
+		d = find_bio_disk(conf, r10_bio, bio, NULL, NULL);
 
 	if (test_bit(BIO_UPTODATE, &bio->bi_flags))
 		set_bit(R10BIO_Uptodate, &r10_bio->state);
@@ -2218,7 +2319,9 @@
 				       " (%d sectors at %llu on %s)\n",
 				       mdname(mddev), s,
 				       (unsigned long long)(
-					       sect + rdev->data_offset),
+					       sect +
+					       choose_data_offset(r10_bio,
+								  rdev)),
 				       bdevname(rdev->bdev, b));
 				printk(KERN_NOTICE "md/raid10:%s: %s: failing "
 				       "drive\n",
@@ -2256,7 +2359,8 @@
 				       " (%d sectors at %llu on %s)\n",
 				       mdname(mddev), s,
 				       (unsigned long long)(
-					       sect + rdev->data_offset),
+					       sect +
+					       choose_data_offset(r10_bio, rdev)),
 				       bdevname(rdev->bdev, b));
 				printk(KERN_NOTICE "md/raid10:%s: %s: failing "
 				       "drive\n",
@@ -2269,7 +2373,8 @@
 				       " (%d sectors at %llu on %s)\n",
 				       mdname(mddev), s,
 				       (unsigned long long)(
-					       sect + rdev->data_offset),
+					       sect +
+					       choose_data_offset(r10_bio, rdev)),
 				       bdevname(rdev->bdev, b));
 				atomic_add(s, &rdev->corrected_errors);
 			}
@@ -2343,7 +2448,7 @@
 		wbio = bio_clone_mddev(bio, GFP_NOIO, mddev);
 		md_trim_bio(wbio, sector - bio->bi_sector, sectors);
 		wbio->bi_sector = (r10_bio->devs[i].addr+
-				   rdev->data_offset+
+				   choose_data_offset(r10_bio, rdev) +
 				   (sector - r10_bio->sector));
 		wbio->bi_bdev = rdev->bdev;
 		if (submit_bio_wait(WRITE, wbio) == 0)
@@ -2420,7 +2525,7 @@
 	r10_bio->devs[slot].bio = bio;
 	r10_bio->devs[slot].rdev = rdev;
 	bio->bi_sector = r10_bio->devs[slot].addr
-		+ rdev->data_offset;
+		+ choose_data_offset(r10_bio, rdev);
 	bio->bi_bdev = rdev->bdev;
 	bio->bi_rw = READ | do_sync;
 	bio->bi_private = r10_bio;
@@ -2480,7 +2585,7 @@
 				rdev_clear_badblocks(
 					rdev,
 					r10_bio->devs[m].addr,
-					r10_bio->sectors);
+					r10_bio->sectors, 0);
 			} else {
 				if (!rdev_set_badblocks(
 					    rdev,
@@ -2496,7 +2601,7 @@
 				rdev_clear_badblocks(
 					rdev,
 					r10_bio->devs[m].addr,
-					r10_bio->sectors);
+					r10_bio->sectors, 0);
 			} else {
 				if (!rdev_set_badblocks(
 					    rdev,
@@ -2515,7 +2620,7 @@
 				rdev_clear_badblocks(
 					rdev,
 					r10_bio->devs[m].addr,
-					r10_bio->sectors);
+					r10_bio->sectors, 0);
 				rdev_dec_pending(rdev, conf->mddev);
 			} else if (bio != NULL &&
 				   !test_bit(BIO_UPTODATE, &bio->bi_flags)) {
@@ -2532,7 +2637,7 @@
 				rdev_clear_badblocks(
 					rdev,
 					r10_bio->devs[m].addr,
-					r10_bio->sectors);
+					r10_bio->sectors, 0);
 				rdev_dec_pending(rdev, conf->mddev);
 			}
 		}
@@ -2573,6 +2678,8 @@
 		if (test_bit(R10BIO_MadeGood, &r10_bio->state) ||
 		    test_bit(R10BIO_WriteError, &r10_bio->state))
 			handle_write_completed(conf, r10_bio);
+		else if (test_bit(R10BIO_IsReshape, &r10_bio->state))
+			reshape_request_write(mddev, r10_bio);
 		else if (test_bit(R10BIO_IsSync, &r10_bio->state))
 			sync_request_write(mddev, r10_bio);
 		else if (test_bit(R10BIO_IsRecover, &r10_bio->state))
@@ -2603,7 +2710,7 @@
 	buffs = RESYNC_WINDOW / RESYNC_BLOCK_SIZE;
 	BUG_ON(conf->r10buf_pool);
 	conf->have_replacement = 0;
-	for (i = 0; i < conf->raid_disks; i++)
+	for (i = 0; i < conf->geo.raid_disks; i++)
 		if (conf->mirrors[i].replacement)
 			conf->have_replacement = 1;
 	conf->r10buf_pool = mempool_create(buffs, r10buf_pool_alloc, r10buf_pool_free, conf);
@@ -2657,6 +2764,7 @@
 	sector_t sync_blocks;
 	sector_t sectors_skipped = 0;
 	int chunks_skipped = 0;
+	sector_t chunk_mask = conf->geo.chunk_mask;
 
 	if (!conf->r10buf_pool)
 		if (init_resync(conf))
@@ -2664,7 +2772,8 @@
 
  skipped:
 	max_sector = mddev->dev_sectors;
-	if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery))
+	if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery) ||
+	    test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery))
 		max_sector = mddev->resync_max_sectors;
 	if (sector_nr >= max_sector) {
 		/* If we aborted, we need to abort the
@@ -2676,11 +2785,16 @@
 		 * we need to convert that to several
 		 * virtual addresses.
 		 */
+		if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery)) {
+			end_reshape(conf);
+			return 0;
+		}
+
 		if (mddev->curr_resync < max_sector) { /* aborted */
 			if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery))
 				bitmap_end_sync(mddev->bitmap, mddev->curr_resync,
 						&sync_blocks, 1);
-			else for (i=0; i<conf->raid_disks; i++) {
+			else for (i = 0; i < conf->geo.raid_disks; i++) {
 				sector_t sect =
 					raid10_find_virt(conf, mddev->curr_resync, i);
 				bitmap_end_sync(mddev->bitmap, sect,
@@ -2694,7 +2808,7 @@
 				/* Completed a full sync so the replacements
 				 * are now fully recovered.
 				 */
-				for (i = 0; i < conf->raid_disks; i++)
+				for (i = 0; i < conf->geo.raid_disks; i++)
 					if (conf->mirrors[i].replacement)
 						conf->mirrors[i].replacement
 							->recovery_offset
@@ -2707,7 +2821,11 @@
 		*skipped = 1;
 		return sectors_skipped;
 	}
-	if (chunks_skipped >= conf->raid_disks) {
+
+	if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery))
+		return reshape_request(mddev, sector_nr, skipped);
+
+	if (chunks_skipped >= conf->geo.raid_disks) {
 		/* if there has been nothing to do on any drive,
 		 * then there is nothing to do at all..
 		 */
@@ -2721,9 +2839,9 @@
 	/* make sure whole request will fit in a chunk - if chunks
 	 * are meaningful
 	 */
-	if (conf->near_copies < conf->raid_disks &&
-	    max_sector > (sector_nr | conf->chunk_mask))
-		max_sector = (sector_nr | conf->chunk_mask) + 1;
+	if (conf->geo.near_copies < conf->geo.raid_disks &&
+	    max_sector > (sector_nr | chunk_mask))
+		max_sector = (sector_nr | chunk_mask) + 1;
 	/*
 	 * If there is non-resync activity waiting for us then
 	 * put in a delay to throttle resync.
@@ -2752,7 +2870,7 @@
 		int j;
 		r10_bio = NULL;
 
-		for (i=0 ; i<conf->raid_disks; i++) {
+		for (i = 0 ; i < conf->geo.raid_disks; i++) {
 			int still_degraded;
 			struct r10bio *rb2;
 			sector_t sect;
@@ -2806,7 +2924,7 @@
 			/* Need to check if the array will still be
 			 * degraded
 			 */
-			for (j=0; j<conf->raid_disks; j++)
+			for (j = 0; j < conf->geo.raid_disks; j++)
 				if (conf->mirrors[j].rdev == NULL ||
 				    test_bit(Faulty, &conf->mirrors[j].rdev->flags)) {
 					still_degraded = 1;
@@ -2984,9 +3102,9 @@
 		r10_bio->sector = sector_nr;
 		set_bit(R10BIO_IsSync, &r10_bio->state);
 		raid10_find_phys(conf, r10_bio);
-		r10_bio->sectors = (sector_nr | conf->chunk_mask) - sector_nr +1;
+		r10_bio->sectors = (sector_nr | chunk_mask) - sector_nr + 1;
 
-		for (i=0; i<conf->copies; i++) {
+		for (i = 0; i < conf->copies; i++) {
 			int d = r10_bio->devs[i].devnum;
 			sector_t first_bad, sector;
 			int bad_sectors;
@@ -3152,16 +3270,17 @@
 	struct r10conf *conf = mddev->private;
 
 	if (!raid_disks)
-		raid_disks = conf->raid_disks;
+		raid_disks = min(conf->geo.raid_disks,
+				 conf->prev.raid_disks);
 	if (!sectors)
 		sectors = conf->dev_sectors;
 
-	size = sectors >> conf->chunk_shift;
-	sector_div(size, conf->far_copies);
+	size = sectors >> conf->geo.chunk_shift;
+	sector_div(size, conf->geo.far_copies);
 	size = size * raid_disks;
-	sector_div(size, conf->near_copies);
+	sector_div(size, conf->geo.near_copies);
 
-	return size << conf->chunk_shift;
+	return size << conf->geo.chunk_shift;
 }
 
 static void calc_sectors(struct r10conf *conf, sector_t size)
@@ -3171,10 +3290,10 @@
 	 * conf->stride
 	 */
 
-	size = size >> conf->chunk_shift;
-	sector_div(size, conf->far_copies);
-	size = size * conf->raid_disks;
-	sector_div(size, conf->near_copies);
+	size = size >> conf->geo.chunk_shift;
+	sector_div(size, conf->geo.far_copies);
+	size = size * conf->geo.raid_disks;
+	sector_div(size, conf->geo.near_copies);
 	/* 'size' is now the number of chunks in the array */
 	/* calculate "used chunks per device" */
 	size = size * conf->copies;
@@ -3182,38 +3301,76 @@
 	/* We need to round up when dividing by raid_disks to
 	 * get the stride size.
 	 */
-	size = DIV_ROUND_UP_SECTOR_T(size, conf->raid_disks);
+	size = DIV_ROUND_UP_SECTOR_T(size, conf->geo.raid_disks);
 
-	conf->dev_sectors = size << conf->chunk_shift;
+	conf->dev_sectors = size << conf->geo.chunk_shift;
 
-	if (conf->far_offset)
-		conf->stride = 1 << conf->chunk_shift;
+	if (conf->geo.far_offset)
+		conf->geo.stride = 1 << conf->geo.chunk_shift;
 	else {
-		sector_div(size, conf->far_copies);
-		conf->stride = size << conf->chunk_shift;
+		sector_div(size, conf->geo.far_copies);
+		conf->geo.stride = size << conf->geo.chunk_shift;
 	}
 }
 
+enum geo_type {geo_new, geo_old, geo_start};
+static int setup_geo(struct geom *geo, struct mddev *mddev, enum geo_type new)
+{
+	int nc, fc, fo;
+	int layout, chunk, disks;
+	switch (new) {
+	case geo_old:
+		layout = mddev->layout;
+		chunk = mddev->chunk_sectors;
+		disks = mddev->raid_disks - mddev->delta_disks;
+		break;
+	case geo_new:
+		layout = mddev->new_layout;
+		chunk = mddev->new_chunk_sectors;
+		disks = mddev->raid_disks;
+		break;
+	default: /* avoid 'may be unused' warnings */
+	case geo_start: /* new when starting reshape - raid_disks not
+			 * updated yet. */
+		layout = mddev->new_layout;
+		chunk = mddev->new_chunk_sectors;
+		disks = mddev->raid_disks + mddev->delta_disks;
+		break;
+	}
+	if (layout >> 17)
+		return -1;
+	if (chunk < (PAGE_SIZE >> 9) ||
+	    !is_power_of_2(chunk))
+		return -2;
+	nc = layout & 255;
+	fc = (layout >> 8) & 255;
+	fo = layout & (1<<16);
+	geo->raid_disks = disks;
+	geo->near_copies = nc;
+	geo->far_copies = fc;
+	geo->far_offset = fo;
+	geo->chunk_mask = chunk - 1;
+	geo->chunk_shift = ffz(~chunk);
+	return nc*fc;
+}
+
 static struct r10conf *setup_conf(struct mddev *mddev)
 {
 	struct r10conf *conf = NULL;
-	int nc, fc, fo;
 	int err = -EINVAL;
+	struct geom geo;
+	int copies;
 
-	if (mddev->new_chunk_sectors < (PAGE_SIZE >> 9) ||
-	    !is_power_of_2(mddev->new_chunk_sectors)) {
+	copies = setup_geo(&geo, mddev, geo_new);
+
+	if (copies == -2) {
 		printk(KERN_ERR "md/raid10:%s: chunk size must be "
 		       "at least PAGE_SIZE(%ld) and be a power of 2.\n",
 		       mdname(mddev), PAGE_SIZE);
 		goto out;
 	}
 
-	nc = mddev->new_layout & 255;
-	fc = (mddev->new_layout >> 8) & 255;
-	fo = mddev->new_layout & (1<<16);
-
-	if ((nc*fc) <2 || (nc*fc) > mddev->raid_disks ||
-	    (mddev->new_layout >> 17)) {
+	if (copies < 2 || copies > mddev->raid_disks) {
 		printk(KERN_ERR "md/raid10:%s: unsupported raid10 layout: 0x%8x\n",
 		       mdname(mddev), mddev->new_layout);
 		goto out;
@@ -3224,7 +3381,9 @@
 	if (!conf)
 		goto out;
 
-	conf->mirrors = kzalloc(sizeof(struct mirror_info)*mddev->raid_disks,
+	/* FIXME calc properly */
+	conf->mirrors = kzalloc(sizeof(struct mirror_info)*(mddev->raid_disks +
+							    max(0,mddev->delta_disks)),
 				GFP_KERNEL);
 	if (!conf->mirrors)
 		goto out;
@@ -3233,22 +3392,29 @@
 	if (!conf->tmppage)
 		goto out;
 
-
-	conf->raid_disks = mddev->raid_disks;
-	conf->near_copies = nc;
-	conf->far_copies = fc;
-	conf->copies = nc*fc;
-	conf->far_offset = fo;
-	conf->chunk_mask = mddev->new_chunk_sectors - 1;
-	conf->chunk_shift = ffz(~mddev->new_chunk_sectors);
-
+	conf->geo = geo;
+	conf->copies = copies;
 	conf->r10bio_pool = mempool_create(NR_RAID10_BIOS, r10bio_pool_alloc,
 					   r10bio_pool_free, conf);
 	if (!conf->r10bio_pool)
 		goto out;
 
 	calc_sectors(conf, mddev->dev_sectors);
-
+	if (mddev->reshape_position == MaxSector) {
+		conf->prev = conf->geo;
+		conf->reshape_progress = MaxSector;
+	} else {
+		if (setup_geo(&conf->prev, mddev, geo_old) != conf->copies) {
+			err = -EINVAL;
+			goto out;
+		}
+		conf->reshape_progress = mddev->reshape_position;
+		if (conf->prev.far_offset)
+			conf->prev.stride = 1 << conf->prev.chunk_shift;
+		else
+			/* far_copies must be 1 */
+			conf->prev.stride = conf->dev_sectors;
+	}
 	spin_lock_init(&conf->device_lock);
 	INIT_LIST_HEAD(&conf->retry_list);
 
@@ -3263,8 +3429,9 @@
 	return conf;
 
  out:
-	printk(KERN_ERR "md/raid10:%s: couldn't allocate memory.\n",
-	       mdname(mddev));
+	if (err == -ENOMEM)
+		printk(KERN_ERR "md/raid10:%s: couldn't allocate memory.\n",
+		       mdname(mddev));
 	if (conf) {
 		if (conf->r10bio_pool)
 			mempool_destroy(conf->r10bio_pool);
@@ -3282,12 +3449,8 @@
 	struct mirror_info *disk;
 	struct md_rdev *rdev;
 	sector_t size;
-
-	/*
-	 * copy the already verified devices into our private RAID10
-	 * bookkeeping area. [whatever we allocate in run(),
-	 * should be freed in stop()]
-	 */
+	sector_t min_offset_diff = 0;
+	int first = 1;
 
 	if (mddev->private == NULL) {
 		conf = setup_conf(mddev);
@@ -3304,17 +3467,20 @@
 
 	chunk_size = mddev->chunk_sectors << 9;
 	blk_queue_io_min(mddev->queue, chunk_size);
-	if (conf->raid_disks % conf->near_copies)
-		blk_queue_io_opt(mddev->queue, chunk_size * conf->raid_disks);
+	if (conf->geo.raid_disks % conf->geo.near_copies)
+		blk_queue_io_opt(mddev->queue, chunk_size * conf->geo.raid_disks);
 	else
 		blk_queue_io_opt(mddev->queue, chunk_size *
-				 (conf->raid_disks / conf->near_copies));
+				 (conf->geo.raid_disks / conf->geo.near_copies));
 
 	rdev_for_each(rdev, mddev) {
+		long long diff;
 
 		disk_idx = rdev->raid_disk;
-		if (disk_idx >= conf->raid_disks
-		    || disk_idx < 0)
+		if (disk_idx < 0)
+			continue;
+		if (disk_idx >= conf->geo.raid_disks &&
+		    disk_idx >= conf->prev.raid_disks)
 			continue;
 		disk = conf->mirrors + disk_idx;
 
@@ -3327,12 +3493,20 @@
 				goto out_free_conf;
 			disk->rdev = rdev;
 		}
+		diff = (rdev->new_data_offset - rdev->data_offset);
+		if (!mddev->reshape_backwards)
+			diff = -diff;
+		if (diff < 0)
+			diff = 0;
+		if (first || diff < min_offset_diff)
+			min_offset_diff = diff;
 
 		disk_stack_limits(mddev->gendisk, rdev->bdev,
 				  rdev->data_offset << 9);
 
 		disk->head_position = 0;
 	}
+
 	/* need to check that every block has at least one working mirror */
 	if (!enough(conf, -1)) {
 		printk(KERN_ERR "md/raid10:%s: not enough operational mirrors.\n",
@@ -3340,8 +3514,21 @@
 		goto out_free_conf;
 	}
 
+	if (conf->reshape_progress != MaxSector) {
+		/* must ensure that shape change is supported */
+		if (conf->geo.far_copies != 1 &&
+		    conf->geo.far_offset == 0)
+			goto out_free_conf;
+		if (conf->prev.far_copies != 1 &&
+		    conf->geo.far_offset == 0)
+			goto out_free_conf;
+	}
+
 	mddev->degraded = 0;
-	for (i = 0; i < conf->raid_disks; i++) {
+	for (i = 0;
+	     i < conf->geo.raid_disks
+		     || i < conf->prev.raid_disks;
+	     i++) {
 
 		disk = conf->mirrors + i;
 
@@ -3368,8 +3555,8 @@
 		       mdname(mddev));
 	printk(KERN_INFO
 		"md/raid10:%s: active with %d out of %d devices\n",
-		mdname(mddev), conf->raid_disks - mddev->degraded,
-		conf->raid_disks);
+		mdname(mddev), conf->geo.raid_disks - mddev->degraded,
+		conf->geo.raid_disks);
 	/*
 	 * Ok, everything is just fine now
 	 */
@@ -3386,11 +3573,11 @@
 	 * maybe...
 	 */
 	{
-		int stripe = conf->raid_disks *
+		int stripe = conf->geo.raid_disks *
 			((mddev->chunk_sectors << 9) / PAGE_SIZE);
-		stripe /= conf->near_copies;
-		if (mddev->queue->backing_dev_info.ra_pages < 2* stripe)
-			mddev->queue->backing_dev_info.ra_pages = 2* stripe;
+		stripe /= conf->geo.near_copies;
+		if (mddev->queue->backing_dev_info.ra_pages < 2 * stripe)
+			mddev->queue->backing_dev_info.ra_pages = 2 * stripe;
 	}
 
 	blk_queue_merge_bvec(mddev->queue, raid10_mergeable_bvec);
@@ -3398,6 +3585,30 @@
 	if (md_integrity_register(mddev))
 		goto out_free_conf;
 
+	if (conf->reshape_progress != MaxSector) {
+		unsigned long before_length, after_length;
+
+		before_length = ((1 << conf->prev.chunk_shift) *
+				 conf->prev.far_copies);
+		after_length = ((1 << conf->geo.chunk_shift) *
+				conf->geo.far_copies);
+
+		if (max(before_length, after_length) > min_offset_diff) {
+			/* This cannot work */
+			printk("md/raid10: offset difference not enough to continue reshape\n");
+			goto out_free_conf;
+		}
+		conf->offset_diff = min_offset_diff;
+
+		conf->reshape_safe = conf->reshape_progress;
+		clear_bit(MD_RECOVERY_SYNC, &mddev->recovery);
+		clear_bit(MD_RECOVERY_CHECK, &mddev->recovery);
+		set_bit(MD_RECOVERY_RESHAPE, &mddev->recovery);
+		set_bit(MD_RECOVERY_RUNNING, &mddev->recovery);
+		mddev->sync_thread = md_register_thread(md_do_sync, mddev,
+							"reshape");
+	}
+
 	return 0;
 
 out_free_conf:
@@ -3460,14 +3671,23 @@
 	struct r10conf *conf = mddev->private;
 	sector_t oldsize, size;
 
-	if (conf->far_copies > 1 && !conf->far_offset)
+	if (mddev->reshape_position != MaxSector)
+		return -EBUSY;
+
+	if (conf->geo.far_copies > 1 && !conf->geo.far_offset)
 		return -EINVAL;
 
 	oldsize = raid10_size(mddev, 0, 0);
 	size = raid10_size(mddev, sectors, 0);
-	md_set_array_sectors(mddev, size);
-	if (mddev->array_sectors > size)
+	if (mddev->external_size &&
+	    mddev->array_sectors > size)
 		return -EINVAL;
+	if (mddev->bitmap) {
+		int ret = bitmap_resize(mddev->bitmap, size, 0, 0);
+		if (ret)
+			return ret;
+	}
+	md_set_array_sectors(mddev, size);
 	set_capacity(mddev->gendisk, mddev->array_sectors);
 	revalidate_disk(mddev->gendisk);
 	if (sectors > mddev->dev_sectors &&
@@ -3534,6 +3754,758 @@
 	return ERR_PTR(-EINVAL);
 }
 
+static int raid10_check_reshape(struct mddev *mddev)
+{
+	/* Called when there is a request to change
+	 * - layout (to ->new_layout)
+	 * - chunk size (to ->new_chunk_sectors)
+	 * - raid_disks (by delta_disks)
+	 * or when trying to restart a reshape that was ongoing.
+	 *
+	 * We need to validate the request and possibly allocate
+	 * space if that might be an issue later.
+	 *
+	 * Currently we reject any reshape of a 'far' mode array,
+	 * allow chunk size to change if new is generally acceptable,
+	 * allow raid_disks to increase, and allow
+	 * a switch between 'near' mode and 'offset' mode.
+	 */
+	struct r10conf *conf = mddev->private;
+	struct geom geo;
+
+	if (conf->geo.far_copies != 1 && !conf->geo.far_offset)
+		return -EINVAL;
+
+	if (setup_geo(&geo, mddev, geo_start) != conf->copies)
+		/* mustn't change number of copies */
+		return -EINVAL;
+	if (geo.far_copies > 1 && !geo.far_offset)
+		/* Cannot switch to 'far' mode */
+		return -EINVAL;
+
+	if (mddev->array_sectors & geo.chunk_mask)
+			/* not factor of array size */
+			return -EINVAL;
+
+	if (!enough(conf, -1))
+		return -EINVAL;
+
+	kfree(conf->mirrors_new);
+	conf->mirrors_new = NULL;
+	if (mddev->delta_disks > 0) {
+		/* allocate new 'mirrors' list */
+		conf->mirrors_new = kzalloc(
+			sizeof(struct mirror_info)
+			*(mddev->raid_disks +
+			  mddev->delta_disks),
+			GFP_KERNEL);
+		if (!conf->mirrors_new)
+			return -ENOMEM;
+	}
+	return 0;
+}
+
+/*
+ * Need to check if array has failed when deciding whether to:
+ *  - start an array
+ *  - remove non-faulty devices
+ *  - add a spare
+ *  - allow a reshape
+ * This determination is simple when no reshape is happening.
+ * However if there is a reshape, we need to carefully check
+ * both the before and after sections.
+ * This is because some failed devices may only affect one
+ * of the two sections, and some non-in_sync devices may
+ * be insync in the section most affected by failed devices.
+ */
+static int calc_degraded(struct r10conf *conf)
+{
+	int degraded, degraded2;
+	int i;
+
+	rcu_read_lock();
+	degraded = 0;
+	/* 'prev' section first */
+	for (i = 0; i < conf->prev.raid_disks; i++) {
+		struct md_rdev *rdev = rcu_dereference(conf->mirrors[i].rdev);
+		if (!rdev || test_bit(Faulty, &rdev->flags))
+			degraded++;
+		else if (!test_bit(In_sync, &rdev->flags))
+			/* When we can reduce the number of devices in
+			 * an array, this might not contribute to
+			 * 'degraded'.  It does now.
+			 */
+			degraded++;
+	}
+	rcu_read_unlock();
+	if (conf->geo.raid_disks == conf->prev.raid_disks)
+		return degraded;
+	rcu_read_lock();
+	degraded2 = 0;
+	for (i = 0; i < conf->geo.raid_disks; i++) {
+		struct md_rdev *rdev = rcu_dereference(conf->mirrors[i].rdev);
+		if (!rdev || test_bit(Faulty, &rdev->flags))
+			degraded2++;
+		else if (!test_bit(In_sync, &rdev->flags)) {
+			/* If reshape is increasing the number of devices,
+			 * this section has already been recovered, so
+			 * it doesn't contribute to degraded.
+			 * else it does.
+			 */
+			if (conf->geo.raid_disks <= conf->prev.raid_disks)
+				degraded2++;
+		}
+	}
+	rcu_read_unlock();
+	if (degraded2 > degraded)
+		return degraded2;
+	return degraded;
+}
+
+static int raid10_start_reshape(struct mddev *mddev)
+{
+	/* A 'reshape' has been requested. This commits
+	 * the various 'new' fields and sets MD_RECOVER_RESHAPE
+	 * This also checks if there are enough spares and adds them
+	 * to the array.
+	 * We currently require enough spares to make the final
+	 * array non-degraded.  We also require that the difference
+	 * between old and new data_offset - on each device - is
+	 * enough that we never risk over-writing.
+	 */
+
+	unsigned long before_length, after_length;
+	sector_t min_offset_diff = 0;
+	int first = 1;
+	struct geom new;
+	struct r10conf *conf = mddev->private;
+	struct md_rdev *rdev;
+	int spares = 0;
+	int ret;
+
+	if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery))
+		return -EBUSY;
+
+	if (setup_geo(&new, mddev, geo_start) != conf->copies)
+		return -EINVAL;
+
+	before_length = ((1 << conf->prev.chunk_shift) *
+			 conf->prev.far_copies);
+	after_length = ((1 << conf->geo.chunk_shift) *
+			conf->geo.far_copies);
+
+	rdev_for_each(rdev, mddev) {
+		if (!test_bit(In_sync, &rdev->flags)
+		    && !test_bit(Faulty, &rdev->flags))
+			spares++;
+		if (rdev->raid_disk >= 0) {
+			long long diff = (rdev->new_data_offset
+					  - rdev->data_offset);
+			if (!mddev->reshape_backwards)
+				diff = -diff;
+			if (diff < 0)
+				diff = 0;
+			if (first || diff < min_offset_diff)
+				min_offset_diff = diff;
+		}
+	}
+
+	if (max(before_length, after_length) > min_offset_diff)
+		return -EINVAL;
+
+	if (spares < mddev->delta_disks)
+		return -EINVAL;
+
+	conf->offset_diff = min_offset_diff;
+	spin_lock_irq(&conf->device_lock);
+	if (conf->mirrors_new) {
+		memcpy(conf->mirrors_new, conf->mirrors,
+		       sizeof(struct mirror_info)*conf->prev.raid_disks);
+		smp_mb();
+		kfree(conf->mirrors_old); /* FIXME and elsewhere */
+		conf->mirrors_old = conf->mirrors;
+		conf->mirrors = conf->mirrors_new;
+		conf->mirrors_new = NULL;
+	}
+	setup_geo(&conf->geo, mddev, geo_start);
+	smp_mb();
+	if (mddev->reshape_backwards) {
+		sector_t size = raid10_size(mddev, 0, 0);
+		if (size < mddev->array_sectors) {
+			spin_unlock_irq(&conf->device_lock);
+			printk(KERN_ERR "md/raid10:%s: array size must be reduce before number of disks\n",
+			       mdname(mddev));
+			return -EINVAL;
+		}
+		mddev->resync_max_sectors = size;
+		conf->reshape_progress = size;
+	} else
+		conf->reshape_progress = 0;
+	spin_unlock_irq(&conf->device_lock);
+
+	if (mddev->delta_disks && mddev->bitmap) {
+		ret = bitmap_resize(mddev->bitmap,
+				    raid10_size(mddev, 0,
+						conf->geo.raid_disks),
+				    0, 0);
+		if (ret)
+			goto abort;
+	}
+	if (mddev->delta_disks > 0) {
+		rdev_for_each(rdev, mddev)
+			if (rdev->raid_disk < 0 &&
+			    !test_bit(Faulty, &rdev->flags)) {
+				if (raid10_add_disk(mddev, rdev) == 0) {
+					if (rdev->raid_disk >=
+					    conf->prev.raid_disks)
+						set_bit(In_sync, &rdev->flags);
+					else
+						rdev->recovery_offset = 0;
+
+					if (sysfs_link_rdev(mddev, rdev))
+						/* Failure here  is OK */;
+				}
+			} else if (rdev->raid_disk >= conf->prev.raid_disks
+				   && !test_bit(Faulty, &rdev->flags)) {
+				/* This is a spare that was manually added */
+				set_bit(In_sync, &rdev->flags);
+			}
+	}
+	/* When a reshape changes the number of devices,
+	 * ->degraded is measured against the larger of the
+	 * pre and  post numbers.
+	 */
+	spin_lock_irq(&conf->device_lock);
+	mddev->degraded = calc_degraded(conf);
+	spin_unlock_irq(&conf->device_lock);
+	mddev->raid_disks = conf->geo.raid_disks;
+	mddev->reshape_position = conf->reshape_progress;
+	set_bit(MD_CHANGE_DEVS, &mddev->flags);
+
+	clear_bit(MD_RECOVERY_SYNC, &mddev->recovery);
+	clear_bit(MD_RECOVERY_CHECK, &mddev->recovery);
+	set_bit(MD_RECOVERY_RESHAPE, &mddev->recovery);
+	set_bit(MD_RECOVERY_RUNNING, &mddev->recovery);
+
+	mddev->sync_thread = md_register_thread(md_do_sync, mddev,
+						"reshape");
+	if (!mddev->sync_thread) {
+		ret = -EAGAIN;
+		goto abort;
+	}
+	conf->reshape_checkpoint = jiffies;
+	md_wakeup_thread(mddev->sync_thread);
+	md_new_event(mddev);
+	return 0;
+
+abort:
+	mddev->recovery = 0;
+	spin_lock_irq(&conf->device_lock);
+	conf->geo = conf->prev;
+	mddev->raid_disks = conf->geo.raid_disks;
+	rdev_for_each(rdev, mddev)
+		rdev->new_data_offset = rdev->data_offset;
+	smp_wmb();
+	conf->reshape_progress = MaxSector;
+	mddev->reshape_position = MaxSector;
+	spin_unlock_irq(&conf->device_lock);
+	return ret;
+}
+
+/* Calculate the last device-address that could contain
+ * any block from the chunk that includes the array-address 's'
+ * and report the next address.
+ * i.e. the address returned will be chunk-aligned and after
+ * any data that is in the chunk containing 's'.
+ */
+static sector_t last_dev_address(sector_t s, struct geom *geo)
+{
+	s = (s | geo->chunk_mask) + 1;
+	s >>= geo->chunk_shift;
+	s *= geo->near_copies;
+	s = DIV_ROUND_UP_SECTOR_T(s, geo->raid_disks);
+	s *= geo->far_copies;
+	s <<= geo->chunk_shift;
+	return s;
+}
+
+/* Calculate the first device-address that could contain
+ * any block from the chunk that includes the array-address 's'.
+ * This too will be the start of a chunk
+ */
+static sector_t first_dev_address(sector_t s, struct geom *geo)
+{
+	s >>= geo->chunk_shift;
+	s *= geo->near_copies;
+	sector_div(s, geo->raid_disks);
+	s *= geo->far_copies;
+	s <<= geo->chunk_shift;
+	return s;
+}
+
+static sector_t reshape_request(struct mddev *mddev, sector_t sector_nr,
+				int *skipped)
+{
+	/* We simply copy at most one chunk (smallest of old and new)
+	 * at a time, possibly less if that exceeds RESYNC_PAGES,
+	 * or we hit a bad block or something.
+	 * This might mean we pause for normal IO in the middle of
+	 * a chunk, but that is not a problem was mddev->reshape_position
+	 * can record any location.
+	 *
+	 * If we will want to write to a location that isn't
+	 * yet recorded as 'safe' (i.e. in metadata on disk) then
+	 * we need to flush all reshape requests and update the metadata.
+	 *
+	 * When reshaping forwards (e.g. to more devices), we interpret
+	 * 'safe' as the earliest block which might not have been copied
+	 * down yet.  We divide this by previous stripe size and multiply
+	 * by previous stripe length to get lowest device offset that we
+	 * cannot write to yet.
+	 * We interpret 'sector_nr' as an address that we want to write to.
+	 * From this we use last_device_address() to find where we might
+	 * write to, and first_device_address on the  'safe' position.
+	 * If this 'next' write position is after the 'safe' position,
+	 * we must update the metadata to increase the 'safe' position.
+	 *
+	 * When reshaping backwards, we round in the opposite direction
+	 * and perform the reverse test:  next write position must not be
+	 * less than current safe position.
+	 *
+	 * In all this the minimum difference in data offsets
+	 * (conf->offset_diff - always positive) allows a bit of slack,
+	 * so next can be after 'safe', but not by more than offset_disk
+	 *
+	 * We need to prepare all the bios here before we start any IO
+	 * to ensure the size we choose is acceptable to all devices.
+	 * The means one for each copy for write-out and an extra one for
+	 * read-in.
+	 * We store the read-in bio in ->master_bio and the others in
+	 * ->devs[x].bio and ->devs[x].repl_bio.
+	 */
+	struct r10conf *conf = mddev->private;
+	struct r10bio *r10_bio;
+	sector_t next, safe, last;
+	int max_sectors;
+	int nr_sectors;
+	int s;
+	struct md_rdev *rdev;
+	int need_flush = 0;
+	struct bio *blist;
+	struct bio *bio, *read_bio;
+	int sectors_done = 0;
+
+	if (sector_nr == 0) {
+		/* If restarting in the middle, skip the initial sectors */
+		if (mddev->reshape_backwards &&
+		    conf->reshape_progress < raid10_size(mddev, 0, 0)) {
+			sector_nr = (raid10_size(mddev, 0, 0)
+				     - conf->reshape_progress);
+		} else if (!mddev->reshape_backwards &&
+			   conf->reshape_progress > 0)
+			sector_nr = conf->reshape_progress;
+		if (sector_nr) {
+			mddev->curr_resync_completed = sector_nr;
+			sysfs_notify(&mddev->kobj, NULL, "sync_completed");
+			*skipped = 1;
+			return sector_nr;
+		}
+	}
+
+	/* We don't use sector_nr to track where we are up to
+	 * as that doesn't work well for ->reshape_backwards.
+	 * So just use ->reshape_progress.
+	 */
+	if (mddev->reshape_backwards) {
+		/* 'next' is the earliest device address that we might
+		 * write to for this chunk in the new layout
+		 */
+		next = first_dev_address(conf->reshape_progress - 1,
+					 &conf->geo);
+
+		/* 'safe' is the last device address that we might read from
+		 * in the old layout after a restart
+		 */
+		safe = last_dev_address(conf->reshape_safe - 1,
+					&conf->prev);
+
+		if (next + conf->offset_diff < safe)
+			need_flush = 1;
+
+		last = conf->reshape_progress - 1;
+		sector_nr = last & ~(sector_t)(conf->geo.chunk_mask
+					       & conf->prev.chunk_mask);
+		if (sector_nr + RESYNC_BLOCK_SIZE/512 < last)
+			sector_nr = last + 1 - RESYNC_BLOCK_SIZE/512;
+	} else {
+		/* 'next' is after the last device address that we
+		 * might write to for this chunk in the new layout
+		 */
+		next = last_dev_address(conf->reshape_progress, &conf->geo);
+
+		/* 'safe' is the earliest device address that we might
+		 * read from in the old layout after a restart
+		 */
+		safe = first_dev_address(conf->reshape_safe, &conf->prev);
+
+		/* Need to update metadata if 'next' might be beyond 'safe'
+		 * as that would possibly corrupt data
+		 */
+		if (next > safe + conf->offset_diff)
+			need_flush = 1;
+
+		sector_nr = conf->reshape_progress;
+		last  = sector_nr | (conf->geo.chunk_mask
+				     & conf->prev.chunk_mask);
+
+		if (sector_nr + RESYNC_BLOCK_SIZE/512 <= last)
+			last = sector_nr + RESYNC_BLOCK_SIZE/512 - 1;
+	}
+
+	if (need_flush ||
+	    time_after(jiffies, conf->reshape_checkpoint + 10*HZ)) {
+		/* Need to update reshape_position in metadata */
+		wait_barrier(conf);
+		mddev->reshape_position = conf->reshape_progress;
+		if (mddev->reshape_backwards)
+			mddev->curr_resync_completed = raid10_size(mddev, 0, 0)
+				- conf->reshape_progress;
+		else
+			mddev->curr_resync_completed = conf->reshape_progress;
+		conf->reshape_checkpoint = jiffies;
+		set_bit(MD_CHANGE_DEVS, &mddev->flags);
+		md_wakeup_thread(mddev->thread);
+		wait_event(mddev->sb_wait, mddev->flags == 0 ||
+			   kthread_should_stop());
+		conf->reshape_safe = mddev->reshape_position;
+		allow_barrier(conf);
+	}
+
+read_more:
+	/* Now schedule reads for blocks from sector_nr to last */
+	r10_bio = mempool_alloc(conf->r10buf_pool, GFP_NOIO);
+	raise_barrier(conf, sectors_done != 0);
+	atomic_set(&r10_bio->remaining, 0);
+	r10_bio->mddev = mddev;
+	r10_bio->sector = sector_nr;
+	set_bit(R10BIO_IsReshape, &r10_bio->state);
+	r10_bio->sectors = last - sector_nr + 1;
+	rdev = read_balance(conf, r10_bio, &max_sectors);
+	BUG_ON(!test_bit(R10BIO_Previous, &r10_bio->state));
+
+	if (!rdev) {
+		/* Cannot read from here, so need to record bad blocks
+		 * on all the target devices.
+		 */
+		// FIXME
+		set_bit(MD_RECOVERY_INTR, &mddev->recovery);
+		return sectors_done;
+	}
+
+	read_bio = bio_alloc_mddev(GFP_KERNEL, RESYNC_PAGES, mddev);
+
+	read_bio->bi_bdev = rdev->bdev;
+	read_bio->bi_sector = (r10_bio->devs[r10_bio->read_slot].addr
+			       + rdev->data_offset);
+	read_bio->bi_private = r10_bio;
+	read_bio->bi_end_io = end_sync_read;
+	read_bio->bi_rw = READ;
+	read_bio->bi_flags &= ~(BIO_POOL_MASK - 1);
+	read_bio->bi_flags |= 1 << BIO_UPTODATE;
+	read_bio->bi_vcnt = 0;
+	read_bio->bi_idx = 0;
+	read_bio->bi_size = 0;
+	r10_bio->master_bio = read_bio;
+	r10_bio->read_slot = r10_bio->devs[r10_bio->read_slot].devnum;
+
+	/* Now find the locations in the new layout */
+	__raid10_find_phys(&conf->geo, r10_bio);
+
+	blist = read_bio;
+	read_bio->bi_next = NULL;
+
+	for (s = 0; s < conf->copies*2; s++) {
+		struct bio *b;
+		int d = r10_bio->devs[s/2].devnum;
+		struct md_rdev *rdev2;
+		if (s&1) {
+			rdev2 = conf->mirrors[d].replacement;
+			b = r10_bio->devs[s/2].repl_bio;
+		} else {
+			rdev2 = conf->mirrors[d].rdev;
+			b = r10_bio->devs[s/2].bio;
+		}
+		if (!rdev2 || test_bit(Faulty, &rdev2->flags))
+			continue;
+		b->bi_bdev = rdev2->bdev;
+		b->bi_sector = r10_bio->devs[s/2].addr + rdev2->new_data_offset;
+		b->bi_private = r10_bio;
+		b->bi_end_io = end_reshape_write;
+		b->bi_rw = WRITE;
+		b->bi_flags &= ~(BIO_POOL_MASK - 1);
+		b->bi_flags |= 1 << BIO_UPTODATE;
+		b->bi_next = blist;
+		b->bi_vcnt = 0;
+		b->bi_idx = 0;
+		b->bi_size = 0;
+		blist = b;
+	}
+
+	/* Now add as many pages as possible to all of these bios. */
+
+	nr_sectors = 0;
+	for (s = 0 ; s < max_sectors; s += PAGE_SIZE >> 9) {
+		struct page *page = r10_bio->devs[0].bio->bi_io_vec[s/(PAGE_SIZE>>9)].bv_page;
+		int len = (max_sectors - s) << 9;
+		if (len > PAGE_SIZE)
+			len = PAGE_SIZE;
+		for (bio = blist; bio ; bio = bio->bi_next) {
+			struct bio *bio2;
+			if (bio_add_page(bio, page, len, 0))
+				continue;
+
+			/* Didn't fit, must stop */
+			for (bio2 = blist;
+			     bio2 && bio2 != bio;
+			     bio2 = bio2->bi_next) {
+				/* Remove last page from this bio */
+				bio2->bi_vcnt--;
+				bio2->bi_size -= len;
+				bio2->bi_flags &= ~(1<<BIO_SEG_VALID);
+			}
+			goto bio_full;
+		}
+		sector_nr += len >> 9;
+		nr_sectors += len >> 9;
+	}
+bio_full:
+	r10_bio->sectors = nr_sectors;
+
+	/* Now submit the read */
+	md_sync_acct(read_bio->bi_bdev, r10_bio->sectors);
+	atomic_inc(&r10_bio->remaining);
+	read_bio->bi_next = NULL;
+	generic_make_request(read_bio);
+	sector_nr += nr_sectors;
+	sectors_done += nr_sectors;
+	if (sector_nr <= last)
+		goto read_more;
+
+	/* Now that we have done the whole section we can
+	 * update reshape_progress
+	 */
+	if (mddev->reshape_backwards)
+		conf->reshape_progress -= sectors_done;
+	else
+		conf->reshape_progress += sectors_done;
+
+	return sectors_done;
+}
+
+static void end_reshape_request(struct r10bio *r10_bio);
+static int handle_reshape_read_error(struct mddev *mddev,
+				     struct r10bio *r10_bio);
+static void reshape_request_write(struct mddev *mddev, struct r10bio *r10_bio)
+{
+	/* Reshape read completed.  Hopefully we have a block
+	 * to write out.
+	 * If we got a read error then we do sync 1-page reads from
+	 * elsewhere until we find the data - or give up.
+	 */
+	struct r10conf *conf = mddev->private;
+	int s;
+
+	if (!test_bit(R10BIO_Uptodate, &r10_bio->state))
+		if (handle_reshape_read_error(mddev, r10_bio) < 0) {
+			/* Reshape has been aborted */
+			md_done_sync(mddev, r10_bio->sectors, 0);
+			return;
+		}
+
+	/* We definitely have the data in the pages, schedule the
+	 * writes.
+	 */
+	atomic_set(&r10_bio->remaining, 1);
+	for (s = 0; s < conf->copies*2; s++) {
+		struct bio *b;
+		int d = r10_bio->devs[s/2].devnum;
+		struct md_rdev *rdev;
+		if (s&1) {
+			rdev = conf->mirrors[d].replacement;
+			b = r10_bio->devs[s/2].repl_bio;
+		} else {
+			rdev = conf->mirrors[d].rdev;
+			b = r10_bio->devs[s/2].bio;
+		}
+		if (!rdev || test_bit(Faulty, &rdev->flags))
+			continue;
+		atomic_inc(&rdev->nr_pending);
+		md_sync_acct(b->bi_bdev, r10_bio->sectors);
+		atomic_inc(&r10_bio->remaining);
+		b->bi_next = NULL;
+		generic_make_request(b);
+	}
+	end_reshape_request(r10_bio);
+}
+
+static void end_reshape(struct r10conf *conf)
+{
+	if (test_bit(MD_RECOVERY_INTR, &conf->mddev->recovery))
+		return;
+
+	spin_lock_irq(&conf->device_lock);
+	conf->prev = conf->geo;
+	md_finish_reshape(conf->mddev);
+	smp_wmb();
+	conf->reshape_progress = MaxSector;
+	spin_unlock_irq(&conf->device_lock);
+
+	/* read-ahead size must cover two whole stripes, which is
+	 * 2 * (datadisks) * chunksize where 'n' is the number of raid devices
+	 */
+	if (conf->mddev->queue) {
+		int stripe = conf->geo.raid_disks *
+			((conf->mddev->chunk_sectors << 9) / PAGE_SIZE);
+		stripe /= conf->geo.near_copies;
+		if (conf->mddev->queue->backing_dev_info.ra_pages < 2 * stripe)
+			conf->mddev->queue->backing_dev_info.ra_pages = 2 * stripe;
+	}
+	conf->fullsync = 0;
+}
+
+
+static int handle_reshape_read_error(struct mddev *mddev,
+				     struct r10bio *r10_bio)
+{
+	/* Use sync reads to get the blocks from somewhere else */
+	int sectors = r10_bio->sectors;
+	struct r10bio r10b;
+	struct r10conf *conf = mddev->private;
+	int slot = 0;
+	int idx = 0;
+	struct bio_vec *bvec = r10_bio->master_bio->bi_io_vec;
+
+	r10b.sector = r10_bio->sector;
+	__raid10_find_phys(&conf->prev, &r10b);
+
+	while (sectors) {
+		int s = sectors;
+		int success = 0;
+		int first_slot = slot;
+
+		if (s > (PAGE_SIZE >> 9))
+			s = PAGE_SIZE >> 9;
+
+		while (!success) {
+			int d = r10b.devs[slot].devnum;
+			struct md_rdev *rdev = conf->mirrors[d].rdev;
+			sector_t addr;
+			if (rdev == NULL ||
+			    test_bit(Faulty, &rdev->flags) ||
+			    !test_bit(In_sync, &rdev->flags))
+				goto failed;
+
+			addr = r10b.devs[slot].addr + idx * PAGE_SIZE;
+			success = sync_page_io(rdev,
+					       addr,
+					       s << 9,
+					       bvec[idx].bv_page,
+					       READ, false);
+			if (success)
+				break;
+		failed:
+			slot++;
+			if (slot >= conf->copies)
+				slot = 0;
+			if (slot == first_slot)
+				break;
+		}
+		if (!success) {
+			/* couldn't read this block, must give up */
+			set_bit(MD_RECOVERY_INTR,
+				&mddev->recovery);
+			return -EIO;
+		}
+		sectors -= s;
+		idx++;
+	}
+	return 0;
+}
+
+static void end_reshape_write(struct bio *bio, int error)
+{
+	int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags);
+	struct r10bio *r10_bio = bio->bi_private;
+	struct mddev *mddev = r10_bio->mddev;
+	struct r10conf *conf = mddev->private;
+	int d;
+	int slot;
+	int repl;
+	struct md_rdev *rdev = NULL;
+
+	d = find_bio_disk(conf, r10_bio, bio, &slot, &repl);
+	if (repl)
+		rdev = conf->mirrors[d].replacement;
+	if (!rdev) {
+		smp_mb();
+		rdev = conf->mirrors[d].rdev;
+	}
+
+	if (!uptodate) {
+		/* FIXME should record badblock */
+		md_error(mddev, rdev);
+	}
+
+	rdev_dec_pending(rdev, mddev);
+	end_reshape_request(r10_bio);
+}
+
+static void end_reshape_request(struct r10bio *r10_bio)
+{
+	if (!atomic_dec_and_test(&r10_bio->remaining))
+		return;
+	md_done_sync(r10_bio->mddev, r10_bio->sectors, 1);
+	bio_put(r10_bio->master_bio);
+	put_buf(r10_bio);
+}
+
+static void raid10_finish_reshape(struct mddev *mddev)
+{
+	struct r10conf *conf = mddev->private;
+
+	if (test_bit(MD_RECOVERY_INTR, &mddev->recovery))
+		return;
+
+	if (mddev->delta_disks > 0) {
+		sector_t size = raid10_size(mddev, 0, 0);
+		md_set_array_sectors(mddev, size);
+		if (mddev->recovery_cp > mddev->resync_max_sectors) {
+			mddev->recovery_cp = mddev->resync_max_sectors;
+			set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
+		}
+		mddev->resync_max_sectors = size;
+		set_capacity(mddev->gendisk, mddev->array_sectors);
+		revalidate_disk(mddev->gendisk);
+	} else {
+		int d;
+		for (d = conf->geo.raid_disks ;
+		     d < conf->geo.raid_disks - mddev->delta_disks;
+		     d++) {
+			struct md_rdev *rdev = conf->mirrors[d].rdev;
+			if (rdev)
+				clear_bit(In_sync, &rdev->flags);
+			rdev = conf->mirrors[d].replacement;
+			if (rdev)
+				clear_bit(In_sync, &rdev->flags);
+		}
+	}
+	mddev->layout = mddev->new_layout;
+	mddev->chunk_sectors = 1 << conf->geo.chunk_shift;
+	mddev->reshape_position = MaxSector;
+	mddev->delta_disks = 0;
+	mddev->reshape_backwards = 0;
+}
+
 static struct md_personality raid10_personality =
 {
 	.name		= "raid10",
@@ -3552,6 +4524,9 @@
 	.size		= raid10_size,
 	.resize		= raid10_resize,
 	.takeover	= raid10_takeover,
+	.check_reshape	= raid10_check_reshape,
+	.start_reshape	= raid10_start_reshape,
+	.finish_reshape	= raid10_finish_reshape,
 };
 
 static int __init raid_init(void)
diff --git a/drivers/md/raid10.h b/drivers/md/raid10.h
index 7c615613..135b1b0 100644
--- a/drivers/md/raid10.h
+++ b/drivers/md/raid10.h
@@ -14,32 +14,38 @@
 struct r10conf {
 	struct mddev		*mddev;
 	struct mirror_info	*mirrors;
-	int			raid_disks;
+	struct mirror_info	*mirrors_new, *mirrors_old;
 	spinlock_t		device_lock;
 
 	/* geometry */
-	int			near_copies;  /* number of copies laid out
+	struct geom {
+		int		raid_disks;
+		int		near_copies;  /* number of copies laid out
 					       * raid0 style */
-	int 			far_copies;   /* number of copies laid out
+		int		far_copies;   /* number of copies laid out
 					       * at large strides across drives
 					       */
-	int			far_offset;   /* far_copies are offset by 1
+		int		far_offset;   /* far_copies are offset by 1
 					       * stripe instead of many
 					       */
-	int			copies;	      /* near_copies * far_copies.
-					       * must be <= raid_disks
-					       */
-	sector_t		stride;	      /* distance between far copies.
+		sector_t	stride;	      /* distance between far copies.
 					       * This is size / far_copies unless
 					       * far_offset, in which case it is
 					       * 1 stripe.
 					       */
+		int		chunk_shift; /* shift from chunks to sectors */
+		sector_t	chunk_mask;
+	} prev, geo;
+	int			copies;	      /* near_copies * far_copies.
+					       * must be <= raid_disks
+					       */
 
 	sector_t		dev_sectors;  /* temp copy of
 					       * mddev->dev_sectors */
-
-	int			chunk_shift; /* shift from chunks to sectors */
-	sector_t		chunk_mask;
+	sector_t		reshape_progress;
+	sector_t		reshape_safe;
+	unsigned long		reshape_checkpoint;
+	sector_t		offset_diff;
 
 	struct list_head	retry_list;
 	/* queue pending writes and submit them on unplug */
@@ -136,6 +142,7 @@
 	R10BIO_Uptodate,
 	R10BIO_IsSync,
 	R10BIO_IsRecover,
+	R10BIO_IsReshape,
 	R10BIO_Degraded,
 /* Set ReadError on bios that experience a read error
  * so that raid10d knows what to do with them.
@@ -146,5 +153,10 @@
  */
 	R10BIO_MadeGood,
 	R10BIO_WriteError,
+/* During a reshape we might be performing IO on the
+ * 'previous' part of the array, in which case this
+ * flag is set
+ */
+	R10BIO_Previous,
 };
 #endif
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index f351422..d267672 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -488,6 +488,27 @@
 	return sh;
 }
 
+/* Determine if 'data_offset' or 'new_data_offset' should be used
+ * in this stripe_head.
+ */
+static int use_new_offset(struct r5conf *conf, struct stripe_head *sh)
+{
+	sector_t progress = conf->reshape_progress;
+	/* Need a memory barrier to make sure we see the value
+	 * of conf->generation, or ->data_offset that was set before
+	 * reshape_progress was updated.
+	 */
+	smp_rmb();
+	if (progress == MaxSector)
+		return 0;
+	if (sh->generation == conf->generation - 1)
+		return 0;
+	/* We are in a reshape, and this is a new-generation stripe,
+	 * so use new_data_offset.
+	 */
+	return 1;
+}
+
 static void
 raid5_end_read_request(struct bio *bi, int error);
 static void
@@ -518,6 +539,8 @@
 			replace_only = 1;
 		} else
 			continue;
+		if (test_and_clear_bit(R5_SyncIO, &sh->dev[i].flags))
+			rw |= REQ_SYNC;
 
 		bi = &sh->dev[i].req;
 		rbi = &sh->dev[i].rreq; /* For writing to replacement */
@@ -603,7 +626,12 @@
 				__func__, (unsigned long long)sh->sector,
 				bi->bi_rw, i);
 			atomic_inc(&sh->count);
-			bi->bi_sector = sh->sector + rdev->data_offset;
+			if (use_new_offset(conf, sh))
+				bi->bi_sector = (sh->sector
+						 + rdev->new_data_offset);
+			else
+				bi->bi_sector = (sh->sector
+						 + rdev->data_offset);
 			bi->bi_flags = 1 << BIO_UPTODATE;
 			bi->bi_idx = 0;
 			bi->bi_io_vec[0].bv_len = STRIPE_SIZE;
@@ -627,7 +655,12 @@
 				__func__, (unsigned long long)sh->sector,
 				rbi->bi_rw, i);
 			atomic_inc(&sh->count);
-			rbi->bi_sector = sh->sector + rrdev->data_offset;
+			if (use_new_offset(conf, sh))
+				rbi->bi_sector = (sh->sector
+						  + rrdev->new_data_offset);
+			else
+				rbi->bi_sector = (sh->sector
+						  + rrdev->data_offset);
 			rbi->bi_flags = 1 << BIO_UPTODATE;
 			rbi->bi_idx = 0;
 			rbi->bi_io_vec[0].bv_len = STRIPE_SIZE;
@@ -1114,6 +1147,8 @@
 				dev->sector + STRIPE_SECTORS) {
 				if (wbi->bi_rw & REQ_FUA)
 					set_bit(R5_WantFUA, &dev->flags);
+				if (wbi->bi_rw & REQ_SYNC)
+					set_bit(R5_SyncIO, &dev->flags);
 				tx = async_copy_data(1, wbi, dev->page,
 					dev->sector, tx);
 				wbi = r5_next_bio(wbi, dev->sector);
@@ -1131,13 +1166,15 @@
 	int pd_idx = sh->pd_idx;
 	int qd_idx = sh->qd_idx;
 	int i;
-	bool fua = false;
+	bool fua = false, sync = false;
 
 	pr_debug("%s: stripe %llu\n", __func__,
 		(unsigned long long)sh->sector);
 
-	for (i = disks; i--; )
+	for (i = disks; i--; ) {
 		fua |= test_bit(R5_WantFUA, &sh->dev[i].flags);
+		sync |= test_bit(R5_SyncIO, &sh->dev[i].flags);
+	}
 
 	for (i = disks; i--; ) {
 		struct r5dev *dev = &sh->dev[i];
@@ -1146,6 +1183,8 @@
 			set_bit(R5_UPTODATE, &dev->flags);
 			if (fua)
 				set_bit(R5_WantFUA, &dev->flags);
+			if (sync)
+				set_bit(R5_SyncIO, &dev->flags);
 		}
 	}
 
@@ -1648,7 +1687,7 @@
 	int uptodate = test_bit(BIO_UPTODATE, &bi->bi_flags);
 	char b[BDEVNAME_SIZE];
 	struct md_rdev *rdev = NULL;
-
+	sector_t s;
 
 	for (i=0 ; i<disks; i++)
 		if (bi == &sh->dev[i].req)
@@ -1671,6 +1710,10 @@
 	if (!rdev)
 		rdev = conf->disks[i].rdev;
 
+	if (use_new_offset(conf, sh))
+		s = sh->sector + rdev->new_data_offset;
+	else
+		s = sh->sector + rdev->data_offset;
 	if (uptodate) {
 		set_bit(R5_UPTODATE, &sh->dev[i].flags);
 		if (test_bit(R5_ReadError, &sh->dev[i].flags)) {
@@ -1683,8 +1726,7 @@
 				"md/raid:%s: read error corrected"
 				" (%lu sectors at %llu on %s)\n",
 				mdname(conf->mddev), STRIPE_SECTORS,
-				(unsigned long long)(sh->sector
-						     + rdev->data_offset),
+				(unsigned long long)s,
 				bdevname(rdev->bdev, b));
 			atomic_add(STRIPE_SECTORS, &rdev->corrected_errors);
 			clear_bit(R5_ReadError, &sh->dev[i].flags);
@@ -1704,8 +1746,7 @@
 				"md/raid:%s: read error on replacement device "
 				"(sector %llu on %s).\n",
 				mdname(conf->mddev),
-				(unsigned long long)(sh->sector
-						     + rdev->data_offset),
+				(unsigned long long)s,
 				bdn);
 		else if (conf->mddev->degraded >= conf->max_degraded)
 			printk_ratelimited(
@@ -1713,8 +1754,7 @@
 				"md/raid:%s: read error not correctable "
 				"(sector %llu on %s).\n",
 				mdname(conf->mddev),
-				(unsigned long long)(sh->sector
-						     + rdev->data_offset),
+				(unsigned long long)s,
 				bdn);
 		else if (test_bit(R5_ReWrite, &sh->dev[i].flags))
 			/* Oh, no!!! */
@@ -1723,8 +1763,7 @@
 				"md/raid:%s: read error NOT corrected!! "
 				"(sector %llu on %s).\n",
 				mdname(conf->mddev),
-				(unsigned long long)(sh->sector
-						     + rdev->data_offset),
+				(unsigned long long)s,
 				bdn);
 		else if (atomic_read(&rdev->read_errors)
 			 > conf->max_nr_stripes)
@@ -3561,7 +3600,7 @@
 			if (test_and_clear_bit(R5_MadeGood, &dev->flags)) {
 				rdev = conf->disks[i].rdev;
 				rdev_clear_badblocks(rdev, sh->sector,
-						     STRIPE_SECTORS);
+						     STRIPE_SECTORS, 0);
 				rdev_dec_pending(rdev, conf->mddev);
 			}
 			if (test_and_clear_bit(R5_MadeGoodRepl, &dev->flags)) {
@@ -3570,7 +3609,7 @@
 					/* rdev have been moved down */
 					rdev = conf->disks[i].rdev;
 				rdev_clear_badblocks(rdev, sh->sector,
-						     STRIPE_SECTORS);
+						     STRIPE_SECTORS, 0);
 				rdev_dec_pending(rdev, conf->mddev);
 			}
 		}
@@ -3842,6 +3881,7 @@
 		raid_bio->bi_next = (void*)rdev;
 		align_bi->bi_bdev =  rdev->bdev;
 		align_bi->bi_flags &= ~(1 << BIO_SEG_VALID);
+		/* No reshape active, so we can trust rdev->data_offset */
 		align_bi->bi_sector += rdev->data_offset;
 
 		if (!bio_fits_rdev(align_bi) ||
@@ -3953,12 +3993,10 @@
 	plugged = mddev_check_plugged(mddev);
 	for (;logical_sector < last_sector; logical_sector += STRIPE_SECTORS) {
 		DEFINE_WAIT(w);
-		int disks, data_disks;
 		int previous;
 
 	retry:
 		previous = 0;
-		disks = conf->raid_disks;
 		prepare_to_wait(&conf->wait_for_overlap, &w, TASK_UNINTERRUPTIBLE);
 		if (unlikely(conf->reshape_progress != MaxSector)) {
 			/* spinlock is needed as reshape_progress may be
@@ -3970,13 +4008,12 @@
 			 * to check again.
 			 */
 			spin_lock_irq(&conf->device_lock);
-			if (mddev->delta_disks < 0
+			if (mddev->reshape_backwards
 			    ? logical_sector < conf->reshape_progress
 			    : logical_sector >= conf->reshape_progress) {
-				disks = conf->previous_raid_disks;
 				previous = 1;
 			} else {
-				if (mddev->delta_disks < 0
+				if (mddev->reshape_backwards
 				    ? logical_sector < conf->reshape_safe
 				    : logical_sector >= conf->reshape_safe) {
 					spin_unlock_irq(&conf->device_lock);
@@ -3986,7 +4023,6 @@
 			}
 			spin_unlock_irq(&conf->device_lock);
 		}
-		data_disks = disks - conf->max_degraded;
 
 		new_sector = raid5_compute_sector(conf, logical_sector,
 						  previous,
@@ -4009,7 +4045,7 @@
 				 */
 				int must_retry = 0;
 				spin_lock_irq(&conf->device_lock);
-				if (mddev->delta_disks < 0
+				if (mddev->reshape_backwards
 				    ? logical_sector >= conf->reshape_progress
 				    : logical_sector < conf->reshape_progress)
 					/* mismatch, need to try again */
@@ -4108,11 +4144,11 @@
 
 	if (sector_nr == 0) {
 		/* If restarting in the middle, skip the initial sectors */
-		if (mddev->delta_disks < 0 &&
+		if (mddev->reshape_backwards &&
 		    conf->reshape_progress < raid5_size(mddev, 0, 0)) {
 			sector_nr = raid5_size(mddev, 0, 0)
 				- conf->reshape_progress;
-		} else if (mddev->delta_disks >= 0 &&
+		} else if (!mddev->reshape_backwards &&
 			   conf->reshape_progress > 0)
 			sector_nr = conf->reshape_progress;
 		sector_div(sector_nr, new_data_disks);
@@ -4133,13 +4169,11 @@
 	else
 		reshape_sectors = mddev->chunk_sectors;
 
-	/* we update the metadata when there is more than 3Meg
-	 * in the block range (that is rather arbitrary, should
-	 * probably be time based) or when the data about to be
-	 * copied would over-write the source of the data at
-	 * the front of the range.
-	 * i.e. one new_stripe along from reshape_progress new_maps
-	 * to after where reshape_safe old_maps to
+	/* We update the metadata at least every 10 seconds, or when
+	 * the data about to be copied would over-write the source of
+	 * the data at the front of the range.  i.e. one new_stripe
+	 * along from reshape_progress new_maps to after where
+	 * reshape_safe old_maps to
 	 */
 	writepos = conf->reshape_progress;
 	sector_div(writepos, new_data_disks);
@@ -4147,7 +4181,7 @@
 	sector_div(readpos, data_disks);
 	safepos = conf->reshape_safe;
 	sector_div(safepos, data_disks);
-	if (mddev->delta_disks < 0) {
+	if (mddev->reshape_backwards) {
 		writepos -= min_t(sector_t, reshape_sectors, writepos);
 		readpos += reshape_sectors;
 		safepos += reshape_sectors;
@@ -4157,11 +4191,29 @@
 		safepos -= min_t(sector_t, reshape_sectors, safepos);
 	}
 
+	/* Having calculated the 'writepos' possibly use it
+	 * to set 'stripe_addr' which is where we will write to.
+	 */
+	if (mddev->reshape_backwards) {
+		BUG_ON(conf->reshape_progress == 0);
+		stripe_addr = writepos;
+		BUG_ON((mddev->dev_sectors &
+			~((sector_t)reshape_sectors - 1))
+		       - reshape_sectors - stripe_addr
+		       != sector_nr);
+	} else {
+		BUG_ON(writepos != sector_nr + reshape_sectors);
+		stripe_addr = sector_nr;
+	}
+
 	/* 'writepos' is the most advanced device address we might write.
 	 * 'readpos' is the least advanced device address we might read.
 	 * 'safepos' is the least address recorded in the metadata as having
 	 *     been reshaped.
-	 * If 'readpos' is behind 'writepos', then there is no way that we can
+	 * If there is a min_offset_diff, these are adjusted either by
+	 * increasing the safepos/readpos if diff is negative, or
+	 * increasing writepos if diff is positive.
+	 * If 'readpos' is then behind 'writepos', there is no way that we can
 	 * ensure safety in the face of a crash - that must be done by userspace
 	 * making a backup of the data.  So in that case there is no particular
 	 * rush to update metadata.
@@ -4174,7 +4226,13 @@
 	 * Maybe that number should be configurable, but I'm not sure it is
 	 * worth it.... maybe it could be a multiple of safemode_delay???
 	 */
-	if ((mddev->delta_disks < 0
+	if (conf->min_offset_diff < 0) {
+		safepos += -conf->min_offset_diff;
+		readpos += -conf->min_offset_diff;
+	} else
+		writepos += conf->min_offset_diff;
+
+	if ((mddev->reshape_backwards
 	     ? (safepos > writepos && readpos < writepos)
 	     : (safepos < writepos && readpos > writepos)) ||
 	    time_after(jiffies, conf->reshape_checkpoint + 10*HZ)) {
@@ -4195,17 +4253,6 @@
 		sysfs_notify(&mddev->kobj, NULL, "sync_completed");
 	}
 
-	if (mddev->delta_disks < 0) {
-		BUG_ON(conf->reshape_progress == 0);
-		stripe_addr = writepos;
-		BUG_ON((mddev->dev_sectors &
-			~((sector_t)reshape_sectors - 1))
-		       - reshape_sectors - stripe_addr
-		       != sector_nr);
-	} else {
-		BUG_ON(writepos != sector_nr + reshape_sectors);
-		stripe_addr = sector_nr;
-	}
 	INIT_LIST_HEAD(&stripes);
 	for (i = 0; i < reshape_sectors; i += STRIPE_SECTORS) {
 		int j;
@@ -4239,7 +4286,7 @@
 		list_add(&sh->lru, &stripes);
 	}
 	spin_lock_irq(&conf->device_lock);
-	if (mddev->delta_disks < 0)
+	if (mddev->reshape_backwards)
 		conf->reshape_progress -= reshape_sectors * new_data_disks;
 	else
 		conf->reshape_progress += reshape_sectors * new_data_disks;
@@ -4952,16 +4999,42 @@
 	struct md_rdev *rdev;
 	sector_t reshape_offset = 0;
 	int i;
+	long long min_offset_diff = 0;
+	int first = 1;
 
 	if (mddev->recovery_cp != MaxSector)
 		printk(KERN_NOTICE "md/raid:%s: not clean"
 		       " -- starting background reconstruction\n",
 		       mdname(mddev));
+
+	rdev_for_each(rdev, mddev) {
+		long long diff;
+		if (rdev->raid_disk < 0)
+			continue;
+		diff = (rdev->new_data_offset - rdev->data_offset);
+		if (first) {
+			min_offset_diff = diff;
+			first = 0;
+		} else if (mddev->reshape_backwards &&
+			 diff < min_offset_diff)
+			min_offset_diff = diff;
+		else if (!mddev->reshape_backwards &&
+			 diff > min_offset_diff)
+			min_offset_diff = diff;
+	}
+
 	if (mddev->reshape_position != MaxSector) {
 		/* Check that we can continue the reshape.
-		 * Currently only disks can change, it must
-		 * increase, and we must be past the point where
-		 * a stripe over-writes itself
+		 * Difficulties arise if the stripe we would write to
+		 * next is at or after the stripe we would read from next.
+		 * For a reshape that changes the number of devices, this
+		 * is only possible for a very short time, and mdadm makes
+		 * sure that time appears to have past before assembling
+		 * the array.  So we fail if that time hasn't passed.
+		 * For a reshape that keeps the number of devices the same
+		 * mdadm must be monitoring the reshape can keeping the
+		 * critical areas read-only and backed up.  It will start
+		 * the array in read-only mode, so we check for that.
 		 */
 		sector_t here_new, here_old;
 		int old_disks;
@@ -4993,26 +5066,34 @@
 		/* here_old is the first stripe that we might need to read
 		 * from */
 		if (mddev->delta_disks == 0) {
+			if ((here_new * mddev->new_chunk_sectors !=
+			     here_old * mddev->chunk_sectors)) {
+				printk(KERN_ERR "md/raid:%s: reshape position is"
+				       " confused - aborting\n", mdname(mddev));
+				return -EINVAL;
+			}
 			/* We cannot be sure it is safe to start an in-place
-			 * reshape.  It is only safe if user-space if monitoring
+			 * reshape.  It is only safe if user-space is monitoring
 			 * and taking constant backups.
 			 * mdadm always starts a situation like this in
 			 * readonly mode so it can take control before
 			 * allowing any writes.  So just check for that.
 			 */
-			if ((here_new * mddev->new_chunk_sectors != 
-			     here_old * mddev->chunk_sectors) ||
-			    mddev->ro == 0) {
-				printk(KERN_ERR "md/raid:%s: in-place reshape must be started"
-				       " in read-only mode - aborting\n",
+			if (abs(min_offset_diff) >= mddev->chunk_sectors &&
+			    abs(min_offset_diff) >= mddev->new_chunk_sectors)
+				/* not really in-place - so OK */;
+			else if (mddev->ro == 0) {
+				printk(KERN_ERR "md/raid:%s: in-place reshape "
+				       "must be started in read-only mode "
+				       "- aborting\n",
 				       mdname(mddev));
 				return -EINVAL;
 			}
-		} else if (mddev->delta_disks < 0
-		    ? (here_new * mddev->new_chunk_sectors <=
+		} else if (mddev->reshape_backwards
+		    ? (here_new * mddev->new_chunk_sectors + min_offset_diff <=
 		       here_old * mddev->chunk_sectors)
 		    : (here_new * mddev->new_chunk_sectors >=
-		       here_old * mddev->chunk_sectors)) {
+		       here_old * mddev->chunk_sectors + (-min_offset_diff))) {
 			/* Reading from the same stripe as writing to - bad */
 			printk(KERN_ERR "md/raid:%s: reshape_position too early for "
 			       "auto-recovery - aborting.\n",
@@ -5037,6 +5118,7 @@
 	if (IS_ERR(conf))
 		return PTR_ERR(conf);
 
+	conf->min_offset_diff = min_offset_diff;
 	mddev->thread = conf->thread;
 	conf->thread = NULL;
 	mddev->private = conf;
@@ -5182,9 +5264,12 @@
 		blk_queue_io_opt(mddev->queue, chunk_size *
 				 (conf->raid_disks - conf->max_degraded));
 
-		rdev_for_each(rdev, mddev)
+		rdev_for_each(rdev, mddev) {
 			disk_stack_limits(mddev->gendisk, rdev->bdev,
 					  rdev->data_offset << 9);
+			disk_stack_limits(mddev->gendisk, rdev->bdev,
+					  rdev->new_data_offset << 9);
+		}
 	}
 
 	return 0;
@@ -5418,12 +5503,18 @@
 	 * any io in the removed space completes, but it hardly seems
 	 * worth it.
 	 */
+	sector_t newsize;
 	sectors &= ~((sector_t)mddev->chunk_sectors - 1);
-	md_set_array_sectors(mddev, raid5_size(mddev, sectors,
-					       mddev->raid_disks));
-	if (mddev->array_sectors >
-	    raid5_size(mddev, sectors, mddev->raid_disks))
+	newsize = raid5_size(mddev, sectors, mddev->raid_disks);
+	if (mddev->external_size &&
+	    mddev->array_sectors > newsize)
 		return -EINVAL;
+	if (mddev->bitmap) {
+		int ret = bitmap_resize(mddev->bitmap, sectors, 0, 0);
+		if (ret)
+			return ret;
+	}
+	md_set_array_sectors(mddev, newsize);
 	set_capacity(mddev->gendisk, mddev->array_sectors);
 	revalidate_disk(mddev->gendisk);
 	if (sectors > mddev->dev_sectors &&
@@ -5468,9 +5559,6 @@
 	    mddev->new_layout == mddev->layout &&
 	    mddev->new_chunk_sectors == mddev->chunk_sectors)
 		return 0; /* nothing to do */
-	if (mddev->bitmap)
-		/* Cannot grow a bitmap yet */
-		return -EBUSY;
 	if (has_failed(conf))
 		return -EINVAL;
 	if (mddev->delta_disks < 0) {
@@ -5505,10 +5593,14 @@
 	if (!check_stripe_cache(mddev))
 		return -ENOSPC;
 
-	rdev_for_each(rdev, mddev)
+	if (has_failed(conf))
+		return -EINVAL;
+
+	rdev_for_each(rdev, mddev) {
 		if (!test_bit(In_sync, &rdev->flags)
 		    && !test_bit(Faulty, &rdev->flags))
 			spares++;
+	}
 
 	if (spares - mddev->degraded < mddev->delta_disks - conf->max_degraded)
 		/* Not enough devices even to make a degraded array
@@ -5535,12 +5627,16 @@
 	conf->chunk_sectors = mddev->new_chunk_sectors;
 	conf->prev_algo = conf->algorithm;
 	conf->algorithm = mddev->new_layout;
-	if (mddev->delta_disks < 0)
+	conf->generation++;
+	/* Code that selects data_offset needs to see the generation update
+	 * if reshape_progress has been set - so a memory barrier needed.
+	 */
+	smp_mb();
+	if (mddev->reshape_backwards)
 		conf->reshape_progress = raid5_size(mddev, 0, 0);
 	else
 		conf->reshape_progress = 0;
 	conf->reshape_safe = conf->reshape_progress;
-	conf->generation++;
 	spin_unlock_irq(&conf->device_lock);
 
 	/* Add some new drives, as many as will fit.
@@ -5592,6 +5688,9 @@
 		mddev->recovery = 0;
 		spin_lock_irq(&conf->device_lock);
 		mddev->raid_disks = conf->raid_disks = conf->previous_raid_disks;
+		rdev_for_each(rdev, mddev)
+			rdev->new_data_offset = rdev->data_offset;
+		smp_wmb();
 		conf->reshape_progress = MaxSector;
 		mddev->reshape_position = MaxSector;
 		spin_unlock_irq(&conf->device_lock);
@@ -5610,9 +5709,13 @@
 {
 
 	if (!test_bit(MD_RECOVERY_INTR, &conf->mddev->recovery)) {
+		struct md_rdev *rdev;
 
 		spin_lock_irq(&conf->device_lock);
 		conf->previous_raid_disks = conf->raid_disks;
+		rdev_for_each(rdev, conf->mddev)
+			rdev->data_offset = rdev->new_data_offset;
+		smp_wmb();
 		conf->reshape_progress = MaxSector;
 		spin_unlock_irq(&conf->device_lock);
 		wake_up(&conf->wait_for_overlap);
@@ -5652,17 +5755,18 @@
 			     d < conf->raid_disks - mddev->delta_disks;
 			     d++) {
 				struct md_rdev *rdev = conf->disks[d].rdev;
-				if (rdev &&
-				    raid5_remove_disk(mddev, rdev) == 0) {
-					sysfs_unlink_rdev(mddev, rdev);
-					rdev->raid_disk = -1;
-				}
+				if (rdev)
+					clear_bit(In_sync, &rdev->flags);
+				rdev = conf->disks[d].replacement;
+				if (rdev)
+					clear_bit(In_sync, &rdev->flags);
 			}
 		}
 		mddev->layout = conf->algorithm;
 		mddev->chunk_sectors = conf->chunk_sectors;
 		mddev->reshape_position = MaxSector;
 		mddev->delta_disks = 0;
+		mddev->reshape_backwards = 0;
 	}
 }
 
diff --git a/drivers/md/raid5.h b/drivers/md/raid5.h
index 8d8e139..2164021 100644
--- a/drivers/md/raid5.h
+++ b/drivers/md/raid5.h
@@ -285,6 +285,7 @@
 			 */
 	R5_Wantdrain,	/* dev->towrite needs to be drained */
 	R5_WantFUA,	/* Write should be FUA */
+	R5_SyncIO,	/* The IO is sync */
 	R5_WriteError,	/* got a write error - need to record it */
 	R5_MadeGood,	/* A bad block has been fixed by writing to it */
 	R5_ReadRepl,	/* Will/did read from replacement rather than orig */
@@ -385,6 +386,12 @@
 	short			generation; /* increments with every reshape */
 	unsigned long		reshape_checkpoint; /* Time we last updated
 						     * metadata */
+	long long		min_offset_diff; /* minimum difference between
+						  * data_offset and
+						  * new_data_offset across all
+						  * devices.  May be negative,
+						  * but is closest to zero.
+						  */
 
 	struct list_head	handle_list; /* stripes needing handling */
 	struct list_head	hold_list; /* preread ready stripes */
diff --git a/drivers/message/i2o/i2o_proc.c b/drivers/message/i2o/i2o_proc.c
index 6d115c7..506c36f 100644
--- a/drivers/message/i2o/i2o_proc.c
+++ b/drivers/message/i2o/i2o_proc.c
@@ -283,7 +283,6 @@
 	"Local Bus",
 	"ISA",
 	"EISA",
-	"MCA",
 	"PCI",
 	"PCMCIA",
 	"NUBUS",
@@ -351,18 +350,6 @@
 					   EisaSlotNumber);
 				break;
 
-			case I2O_BUS_MCA:
-				seq_printf(seq, "     IOBase: %0#6x,",
-					   hrt->hrt_entry[i].bus.mca_bus.
-					   McaBaseIOPort);
-				seq_printf(seq, " MemoryBase: %0#10x,",
-					   hrt->hrt_entry[i].bus.mca_bus.
-					   McaBaseMemoryAddress);
-				seq_printf(seq, " Slot: %0#4x,",
-					   hrt->hrt_entry[i].bus.mca_bus.
-					   McaSlotNumber);
-				break;
-
 			case I2O_BUS_PCI:
 				seq_printf(seq, "     Bus: %0#4x",
 					   hrt->hrt_entry[i].bus.pci_bus.
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 2206861..f4b4dad 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -377,7 +377,6 @@
 
 config MFD_DA9052_SPI
 	bool "Support Dialog Semiconductor DA9052/53 PMIC variants with SPI"
-	select IRQ_DOMAIN
 	select REGMAP_SPI
 	select REGMAP_IRQ
 	select PMIC_DA9052
@@ -390,7 +389,6 @@
 
 config MFD_DA9052_I2C
 	bool "Support Dialog Semiconductor DA9052/53 PMIC variants with I2C"
-	select IRQ_DOMAIN
 	select REGMAP_I2C
 	select REGMAP_IRQ
 	select PMIC_DA9052
@@ -561,7 +559,6 @@
 	bool "Support Wolfson Microelectronics WM8994"
 	select MFD_CORE
 	select REGMAP_I2C
-	select IRQ_DOMAIN
 	select REGMAP_IRQ
 	depends on I2C=y && GENERIC_HARDIRQS
 	help
diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c
index 9fd4f63..738722c 100644
--- a/drivers/mfd/mc13xxx-core.c
+++ b/drivers/mfd/mc13xxx-core.c
@@ -813,7 +813,8 @@
 		mc13xxx_add_subdevice(mc13xxx, "%s-adc");
 
 	if (mc13xxx->flags & MC13XXX_USE_CODEC)
-		mc13xxx_add_subdevice(mc13xxx, "%s-codec");
+		mc13xxx_add_subdevice_pdata(mc13xxx, "%s-codec",
+					pdata->codec, sizeof(*pdata->codec));
 
 	if (mc13xxx->flags & MC13XXX_USE_RTC)
 		mc13xxx_add_subdevice(mc13xxx, "%s-rtc");
diff --git a/drivers/ps3/ps3av.c b/drivers/ps3/ps3av.c
index a409fa0..93d0a8b 100644
--- a/drivers/ps3/ps3av.c
+++ b/drivers/ps3/ps3av.c
@@ -338,7 +338,7 @@
 	mutex_unlock(&ps3av->mutex);
 	return 0;
 
-      err:
+err:
 	mutex_unlock(&ps3av->mutex);
 	printk(KERN_ERR "%s: failed cid:%x res:%d\n", __func__, cid, res);
 	return res;
@@ -477,7 +477,6 @@
 
 	return 0;
 }
-
 EXPORT_SYMBOL_GPL(ps3av_set_audio_mode);
 
 static int ps3av_set_videomode(void)
@@ -501,7 +500,7 @@
 
 	video_mode = &video_mode_table[id & PS3AV_MODE_MASK];
 
-	avb_param.num_of_video_pkt = PS3AV_AVB_NUM_VIDEO;	/* num of head */
+	avb_param.num_of_video_pkt = PS3AV_AVB_NUM_VIDEO; /* num of head */
 	avb_param.num_of_audio_pkt = 0;
 	avb_param.num_of_av_video_pkt = ps3av->av_hw_conf.num_of_hdmi +
 					ps3av->av_hw_conf.num_of_avmulti;
@@ -521,7 +520,7 @@
 #ifndef PS3AV_HDMI_YUV
 		if (ps3av->av_port[i] == PS3AV_CMD_AVPORT_HDMI_0 ||
 		    ps3av->av_port[i] == PS3AV_CMD_AVPORT_HDMI_1)
-			av_video_cs = RGB8;	/* use RGB for HDMI */
+			av_video_cs = RGB8; /* use RGB for HDMI */
 #endif
 		len += ps3av_cmd_set_av_video_cs(&avb_param.buf[len],
 						 ps3av->av_port[i],
@@ -590,8 +589,8 @@
 #define SHIFT_VESA	8
 
 static const struct {
-	unsigned mask : 19;
-	unsigned id :  4;
+	unsigned mask:19;
+	unsigned id:4;
 } ps3av_preferred_modes[] = {
 	{ PS3AV_RESBIT_WUXGA      << SHIFT_VESA, PS3AV_MODE_WUXGA   },
 	{ PS3AV_RESBIT_1920x1080P << SHIFT_60,   PS3AV_MODE_1080P60 },
@@ -667,7 +666,8 @@
 	return id;
 }
 
-static void ps3av_monitor_info_dump(const struct ps3av_pkt_av_get_monitor_info *monitor_info)
+static void ps3av_monitor_info_dump(
+	const struct ps3av_pkt_av_get_monitor_info *monitor_info)
 {
 	const struct ps3av_info_monitor *info = &monitor_info->info;
 	const struct ps3av_info_audio *audio = info->audio;
@@ -717,8 +717,8 @@
 
 	/* audio block */
 	for (i = 0; i < info->num_of_audio_block; i++) {
-		pr_debug("audio[%d] type: %02x max_ch: %02x fs: %02x sbit: "
-			 "%02x\n",
+		pr_debug(
+			"audio[%d] type: %02x max_ch: %02x fs: %02x sbit: %02x\n",
 			 i, audio->type, audio->max_num_of_ch, audio->fs,
 			 audio->sbit);
 		audio++;
@@ -870,21 +870,18 @@
 
 	return 0;
 }
-
 EXPORT_SYMBOL_GPL(ps3av_set_video_mode);
 
 int ps3av_get_auto_mode(void)
 {
 	return ps3av_auto_videomode(&ps3av->av_hw_conf);
 }
-
 EXPORT_SYMBOL_GPL(ps3av_get_auto_mode);
 
 int ps3av_get_mode(void)
 {
 	return ps3av ? ps3av->ps3av_mode : 0;
 }
-
 EXPORT_SYMBOL_GPL(ps3av_get_mode);
 
 /* get resolution by video_mode */
@@ -902,7 +899,6 @@
 	*yres = video_mode_table[id].y;
 	return 0;
 }
-
 EXPORT_SYMBOL_GPL(ps3av_video_mode2res);
 
 /* mute */
@@ -911,7 +907,6 @@
 	return ps3av_set_av_video_mute(mute ? PS3AV_CMD_MUTE_ON
 					    : PS3AV_CMD_MUTE_OFF);
 }
-
 EXPORT_SYMBOL_GPL(ps3av_video_mute);
 
 /* mute analog output only */
@@ -935,7 +930,6 @@
 	return ps3av_set_audio_mute(mute ? PS3AV_CMD_MUTE_ON
 					 : PS3AV_CMD_MUTE_OFF);
 }
-
 EXPORT_SYMBOL_GPL(ps3av_audio_mute);
 
 static int __devinit ps3av_probe(struct ps3_system_bus_device *dev)
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index bea04e5..e955978 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -808,19 +808,6 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called fdomain.
 
-config SCSI_FD_MCS
-	tristate "Future Domain MCS-600/700 SCSI support"
-	depends on MCA_LEGACY && SCSI
-	---help---
-	  This is support for Future Domain MCS 600/700 MCA SCSI adapters.
-	  Some PS/2 computers are equipped with IBM Fast SCSI Adapter/A which
-	  is identical to the MCS 700 and hence also supported by this driver.
-	  This driver also supports the Reply SB16/SCSI card (the SCSI part).
-	  It supports multiple adapters in the same system.
-
-	  To compile this driver as a module, choose M here: the
-	  module will be called fd_mcs.
-
 config SCSI_GDTH
 	tristate "Intel/ICP (former GDT SCSI Disk Array) RAID Controller support"
 	depends on (ISA || EISA || PCI) && SCSI && ISA_DMA_API
@@ -890,76 +877,6 @@
 	  not detect your card.  See the file
 	  <file:Documentation/scsi/g_NCR5380.txt> for details.
 
-config SCSI_IBMMCA
-	tristate "IBMMCA SCSI support"
-	depends on MCA && SCSI
-	---help---
-	  This is support for the IBM SCSI adapter found in many of the PS/2
-	  series computers.  These machines have an MCA bus, so you need to
-	  answer Y to "MCA support" as well and read
-	  <file:Documentation/mca.txt>.
-
-	  If the adapter isn't found during boot (a common problem for models
-	  56, 57, 76, and 77) you'll need to use the 'ibmmcascsi=<pun>' kernel
-	  option, where <pun> is the id of the SCSI subsystem (usually 7, but
-	  if that doesn't work check your reference diskette).  Owners of
-	  model 95 with a LED-matrix-display can in addition activate some
-	  activity info like under OS/2, but more informative, by setting
-	  'ibmmcascsi=display' as an additional kernel parameter.  Try "man
-	  bootparam" or see the documentation of your boot loader about how to
-	  pass options to the kernel.
-
-	  To compile this driver as a module, choose M here: the
-	  module will be called ibmmca.
-
-config IBMMCA_SCSI_ORDER_STANDARD
-	bool "Standard SCSI-order"
-	depends on SCSI_IBMMCA
-	---help---
-	  In the PC-world and in most modern SCSI-BIOS-setups, SCSI-hard disks
-	  are assigned to the drive letters, starting with the lowest SCSI-id
-	  (physical number -- pun) to be drive C:, as seen from DOS and
-	  similar operating systems. When looking into papers describing the
-	  ANSI-SCSI-standard, this assignment of drives appears to be wrong.
-	  The SCSI-standard follows a hardware-hierarchy which says that id 7
-	  has the highest priority and id 0 the lowest. Therefore, the host
-	  adapters are still today everywhere placed as SCSI-id 7 by default.
-	  In the SCSI-standard, the drive letters express the priority of the
-	  disk. C: should be the hard disk, or a partition on it, with the
-	  highest priority. This must therefore be the disk with the highest
-	  SCSI-id (e.g. 6) and not the one with the lowest! IBM-BIOS kept the
-	  original definition of the SCSI-standard as also industrial- and
-	  process-control-machines, like VME-CPUs running under realtime-OSes
-	  (e.g. LynxOS, OS9) do.
-
-	  If you like to run Linux on your MCA-machine with the same
-	  assignment of hard disks as seen from e.g. DOS or OS/2 on your
-	  machine, which is in addition conformant to the SCSI-standard, you
-	  must say Y here. This is also necessary for MCA-Linux users who want
-	  to keep downward compatibility to older releases of the
-	  IBM-MCA-SCSI-driver (older than driver-release 2.00 and older than
-	  June 1997).
-
-	  If you like to have the lowest SCSI-id assigned as drive C:, as
-	  modern SCSI-BIOSes do, which does not conform to the standard, but
-	  is widespread and common in the PC-world of today, you must say N
-	  here. If unsure, say Y.
-
-config IBMMCA_SCSI_DEV_RESET
-	bool "Reset SCSI-devices at boottime"
-	depends on SCSI_IBMMCA
-	---help---
-	  By default, SCSI-devices are reset when the machine is powered on.
-	  However, some devices exist, like special-control-devices,
-	  SCSI-CNC-machines, SCSI-printer or scanners of older type, that do
-	  not reset when switched on. If you say Y here, each device connected
-	  to your SCSI-bus will be issued a reset-command after it has been
-	  probed, while the kernel is booting. This may cause problems with
-	  more modern devices, like hard disks, which do not appreciate these
-	  reset commands, and can cause your system to hang. So say Y only if
-	  you know that one of your older devices needs it; N is the safe
-	  answer.
-
 config SCSI_IPS
 	tristate "IBM ServeRAID support"
 	depends on PCI && SCSI
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index 8deedea..1a3368b 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -75,7 +75,6 @@
 obj-$(CONFIG_SCSI_PM8001)	+= pm8001/
 obj-$(CONFIG_SCSI_ISCI)		+= isci/
 obj-$(CONFIG_SCSI_IPS)		+= ips.o
-obj-$(CONFIG_SCSI_FD_MCS)	+= fd_mcs.o
 obj-$(CONFIG_SCSI_FUTURE_DOMAIN)+= fdomain.o
 obj-$(CONFIG_SCSI_IN2000)	+= in2000.o
 obj-$(CONFIG_SCSI_GENERIC_NCR5380) += g_NCR5380.o
@@ -100,7 +99,6 @@
 obj-$(CONFIG_SCSI_ZALON)	+= zalon7xx.o
 obj-$(CONFIG_SCSI_EATA_PIO)	+= eata_pio.o
 obj-$(CONFIG_SCSI_7000FASST)	+= wd7000.o
-obj-$(CONFIG_SCSI_IBMMCA)	+= ibmmca.o
 obj-$(CONFIG_SCSI_EATA)		+= eata.o
 obj-$(CONFIG_SCSI_DC395x)	+= dc395x.o
 obj-$(CONFIG_SCSI_DC390T)	+= tmscsim.o
diff --git a/drivers/scsi/aha1542.c b/drivers/scsi/aha1542.c
index ede91f3..f79c8f9 100644
--- a/drivers/scsi/aha1542.c
+++ b/drivers/scsi/aha1542.c
@@ -22,7 +22,7 @@
  *        Added module command-line options
  *        19-Jul-99
  *  Modified by Adam Fritzler
- *        Added proper detection of the AHA-1640 (MCA version of AHA-1540)
+ *        Added proper detection of the AHA-1640 (MCA, now deleted)
  */
 
 #include <linux/module.h>
@@ -37,8 +37,6 @@
 #include <linux/spinlock.h>
 #include <linux/isapnp.h>
 #include <linux/blkdev.h>
-#include <linux/mca.h>
-#include <linux/mca-legacy.h>
 #include <linux/slab.h>
 
 #include <asm/dma.h>
@@ -71,7 +69,7 @@
 #define MAXBOARDS 4		/* Increase this and the sizes of the
 				   arrays below, if you need more.. */
 
-/* Boards 3,4 slots are reserved for ISAPnP/MCA scans */
+/* Boards 3,4 slots are reserved for ISAPnP scans */
 
 static unsigned int bases[MAXBOARDS] __initdata = {0x330, 0x334, 0, 0};
 
@@ -1009,66 +1007,6 @@
 #endif
 
 	/*
-	 *	Find MicroChannel cards (AHA1640)
-	 */
-#ifdef CONFIG_MCA_LEGACY
-	if(MCA_bus) {
-		int slot = 0;
-		int pos = 0;
-
-		for (indx = 0; (slot != MCA_NOTFOUND) && (indx < ARRAY_SIZE(bases)); indx++) {
-
-			if (bases[indx])
-				continue;
-
-			/* Detect only AHA-1640 cards -- MCA ID 0F1F */
-			slot = mca_find_unused_adapter(0x0f1f, slot);
-			if (slot == MCA_NOTFOUND)
-				break;
-
-			/* Found one */
-			pos = mca_read_stored_pos(slot, 3);
-
-			/* Decode address */
-			if (pos & 0x80) {
-				if (pos & 0x02) {
-					if (pos & 0x01)
-						bases[indx] = 0x334;
-					else
-						bases[indx] = 0x234;
-				} else {
-					if (pos & 0x01)
-						bases[indx] = 0x134;
-				}
-			} else {
-				if (pos & 0x02) {
-					if (pos & 0x01)
-						bases[indx] = 0x330;
-					else
-						bases[indx] = 0x230;
-				} else {
-					if (pos & 0x01)
-						bases[indx] = 0x130;
-				}
-			}
-
-			/* No need to decode IRQ and Arb level -- those are
-			 * read off the card later.
-			 */
-			printk(KERN_INFO "Found an AHA-1640 in MCA slot %d, I/O 0x%04x\n", slot, bases[indx]);
-
-			mca_set_adapter_name(slot, "Adapter AHA-1640");
-			mca_set_adapter_procfn(slot, NULL, NULL);
-			mca_mark_as_used(slot);
-
-			/* Go on */
-			slot++;
-		}
-
-	}
-#endif
-
-	/*
 	 *	Hunt for ISA Plug'n'Pray Adaptecs (AHA1535)
 	 */
 
diff --git a/drivers/scsi/fd_mcs.c b/drivers/scsi/fd_mcs.c
deleted file mode 100644
index 53bfcaa..0000000
--- a/drivers/scsi/fd_mcs.c
+++ /dev/null
@@ -1,1354 +0,0 @@
-/* fd_mcs.c -- Future Domain MCS 600/700 (or IBM OEM) driver
- *
- * FutureDomain MCS-600/700 v0.2 03/11/1998 by ZP Gu (zpg@castle.net)
- *
- * This driver is cloned from fdomain.* to specifically support
- * the Future Domain MCS 600/700 MCA SCSI adapters. Some PS/2s
- * also equipped with IBM Fast SCSI Adapter/A which is an OEM
- * of MCS 700.
- *
- * This driver also supports Reply SB16/SCSI card (the SCSI part).
- *
- * What makes this driver different is that this driver is MCA only
- * and it supports multiple adapters in the same system, IRQ 
- * sharing, some driver statistics, and maps highest SCSI id to sda.
- * All cards are auto-detected.
- *
- * Assumptions: TMC-1800/18C50/18C30, BIOS >= 3.4
- *
- * LILO command-line options:
- *   fd_mcs=<FIFO_COUNT>[,<FIFO_SIZE>]
- *
- * ********************************************************
- * Please see Copyrights/Comments in fdomain.* for credits.
- * Following is from fdomain.c for acknowledgement:
- *
- * Created: Sun May  3 18:53:19 1992 by faith@cs.unc.edu
- * Revised: Wed Oct  2 11:10:55 1996 by r.faith@ieee.org
- * Author: Rickard E. Faith, faith@cs.unc.edu
- * Copyright 1992, 1993, 1994, 1995, 1996 Rickard E. Faith
- *
- * $Id: fdomain.c,v 5.45 1996/10/02 15:13:06 root Exp $
-
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2, or (at your option) any
- * later version.
-
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
-
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 675 Mass Ave, Cambridge, MA 02139, USA.
-
- **************************************************************************
-
- NOTES ON USER DEFINABLE OPTIONS:
-
- DEBUG: This turns on the printing of various debug information.
-
- ENABLE_PARITY: This turns on SCSI parity checking.  With the current
- driver, all attached devices must support SCSI parity.  If none of your
- devices support parity, then you can probably get the driver to work by
- turning this option off.  I have no way of testing this, however, and it
- would appear that no one ever uses this option.
-
- FIFO_COUNT: The host adapter has an 8K cache (host adapters based on the
- 18C30 chip have a 2k cache).  When this many 512 byte blocks are filled by
- the SCSI device, an interrupt will be raised.  Therefore, this could be as
- low as 0, or as high as 16.  Note, however, that values which are too high
- or too low seem to prevent any interrupts from occurring, and thereby lock
- up the machine.  I have found that 2 is a good number, but throughput may
- be increased by changing this value to values which are close to 2.
- Please let me know if you try any different values.
- [*****Now a runtime option*****]
-
- RESELECTION: This is no longer an option, since I gave up trying to
- implement it in version 4.x of this driver.  It did not improve
- performance at all and made the driver unstable (because I never found one
- of the two race conditions which were introduced by the multiple
- outstanding command code).  The instability seems a very high price to pay
- just so that you don't have to wait for the tape to rewind.  If you want
- this feature implemented, send me patches.  I'll be happy to send a copy
- of my (broken) driver to anyone who would like to see a copy.
-
- **************************************************************************/
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/blkdev.h>
-#include <linux/errno.h>
-#include <linux/string.h>
-#include <linux/ioport.h>
-#include <linux/proc_fs.h>
-#include <linux/delay.h>
-#include <linux/mca.h>
-#include <linux/spinlock.h>
-#include <linux/slab.h>
-#include <scsi/scsicam.h>
-#include <linux/mca-legacy.h>
-
-#include <asm/io.h>
-
-#include "scsi.h"
-#include <scsi/scsi_host.h>
-
-#define DRIVER_VERSION "v0.2 by ZP Gu<zpg@castle.net>"
-
-/* START OF USER DEFINABLE OPTIONS */
-
-#define DEBUG            0	/* Enable debugging output */
-#define ENABLE_PARITY    1	/* Enable SCSI Parity */
-
-/* END OF USER DEFINABLE OPTIONS */
-
-#if DEBUG
-#define EVERY_ACCESS     0	/* Write a line on every scsi access */
-#define ERRORS_ONLY      1	/* Only write a line if there is an error */
-#define DEBUG_MESSAGES   1	/* Debug MESSAGE IN phase */
-#define DEBUG_ABORT      1	/* Debug abort() routine */
-#define DEBUG_RESET      1	/* Debug reset() routine */
-#define DEBUG_RACE       1	/* Debug interrupt-driven race condition */
-#else
-#define EVERY_ACCESS     0	/* LEAVE THESE ALONE--CHANGE THE ONES ABOVE */
-#define ERRORS_ONLY      0
-#define DEBUG_MESSAGES   0
-#define DEBUG_ABORT      0
-#define DEBUG_RESET      0
-#define DEBUG_RACE       0
-#endif
-
-/* Errors are reported on the line, so we don't need to report them again */
-#if EVERY_ACCESS
-#undef ERRORS_ONLY
-#define ERRORS_ONLY      0
-#endif
-
-#if ENABLE_PARITY
-#define PARITY_MASK      0x08
-#else
-#define PARITY_MASK      0x00
-#endif
-
-enum chip_type {
-	unknown = 0x00,
-	tmc1800 = 0x01,
-	tmc18c50 = 0x02,
-	tmc18c30 = 0x03,
-};
-
-enum {
-	in_arbitration = 0x02,
-	in_selection = 0x04,
-	in_other = 0x08,
-	disconnect = 0x10,
-	aborted = 0x20,
-	sent_ident = 0x40,
-};
-
-enum in_port_type {
-	Read_SCSI_Data = 0,
-	SCSI_Status = 1,
-	TMC_Status = 2,
-	FIFO_Status = 3,	/* tmc18c50/tmc18c30 only */
-	Interrupt_Cond = 4,	/* tmc18c50/tmc18c30 only */
-	LSB_ID_Code = 5,
-	MSB_ID_Code = 6,
-	Read_Loopback = 7,
-	SCSI_Data_NoACK = 8,
-	Interrupt_Status = 9,
-	Configuration1 = 10,
-	Configuration2 = 11,	/* tmc18c50/tmc18c30 only */
-	Read_FIFO = 12,
-	FIFO_Data_Count = 14
-};
-
-enum out_port_type {
-	Write_SCSI_Data = 0,
-	SCSI_Cntl = 1,
-	Interrupt_Cntl = 2,
-	SCSI_Mode_Cntl = 3,
-	TMC_Cntl = 4,
-	Memory_Cntl = 5,	/* tmc18c50/tmc18c30 only */
-	Write_Loopback = 7,
-	IO_Control = 11,	/* tmc18c30 only */
-	Write_FIFO = 12
-};
-
-struct fd_hostdata {
-	unsigned long _bios_base;
-	int _bios_major;
-	int _bios_minor;
-	volatile int _in_command;
-	Scsi_Cmnd *_current_SC;
-	enum chip_type _chip;
-	int _adapter_mask;
-	int _fifo_count;	/* Number of 512 byte blocks before INTR */
-
-	char _adapter_name[64];
-#if DEBUG_RACE
-	volatile int _in_interrupt_flag;
-#endif
-
-	int _SCSI_Mode_Cntl_port;
-	int _FIFO_Data_Count_port;
-	int _Interrupt_Cntl_port;
-	int _Interrupt_Status_port;
-	int _Interrupt_Cond_port;
-	int _Read_FIFO_port;
-	int _Read_SCSI_Data_port;
-	int _SCSI_Cntl_port;
-	int _SCSI_Data_NoACK_port;
-	int _SCSI_Status_port;
-	int _TMC_Cntl_port;
-	int _TMC_Status_port;
-	int _Write_FIFO_port;
-	int _Write_SCSI_Data_port;
-
-	int _FIFO_Size;		/* = 0x2000;  8k FIFO for
-				   pre-tmc18c30 chips */
-	/* simple stats */
-	int _Bytes_Read;
-	int _Bytes_Written;
-	int _INTR_Processed;
-};
-
-#define FD_MAX_HOSTS 3		/* enough? */
-
-#define HOSTDATA(shpnt) ((struct fd_hostdata *) shpnt->hostdata)
-#define bios_base             (HOSTDATA(shpnt)->_bios_base)
-#define bios_major            (HOSTDATA(shpnt)->_bios_major)
-#define bios_minor            (HOSTDATA(shpnt)->_bios_minor)
-#define in_command            (HOSTDATA(shpnt)->_in_command)
-#define current_SC            (HOSTDATA(shpnt)->_current_SC)
-#define chip                  (HOSTDATA(shpnt)->_chip)
-#define adapter_mask          (HOSTDATA(shpnt)->_adapter_mask)
-#define FIFO_COUNT            (HOSTDATA(shpnt)->_fifo_count)
-#define adapter_name          (HOSTDATA(shpnt)->_adapter_name)
-#if DEBUG_RACE
-#define in_interrupt_flag     (HOSTDATA(shpnt)->_in_interrupt_flag)
-#endif
-#define SCSI_Mode_Cntl_port   (HOSTDATA(shpnt)->_SCSI_Mode_Cntl_port)
-#define FIFO_Data_Count_port  (HOSTDATA(shpnt)->_FIFO_Data_Count_port)
-#define Interrupt_Cntl_port   (HOSTDATA(shpnt)->_Interrupt_Cntl_port)
-#define Interrupt_Status_port (HOSTDATA(shpnt)->_Interrupt_Status_port)
-#define Interrupt_Cond_port   (HOSTDATA(shpnt)->_Interrupt_Cond_port)
-#define Read_FIFO_port        (HOSTDATA(shpnt)->_Read_FIFO_port)
-#define Read_SCSI_Data_port   (HOSTDATA(shpnt)->_Read_SCSI_Data_port)
-#define SCSI_Cntl_port        (HOSTDATA(shpnt)->_SCSI_Cntl_port)
-#define SCSI_Data_NoACK_port  (HOSTDATA(shpnt)->_SCSI_Data_NoACK_port)
-#define SCSI_Status_port      (HOSTDATA(shpnt)->_SCSI_Status_port)
-#define TMC_Cntl_port         (HOSTDATA(shpnt)->_TMC_Cntl_port)
-#define TMC_Status_port       (HOSTDATA(shpnt)->_TMC_Status_port)
-#define Write_FIFO_port       (HOSTDATA(shpnt)->_Write_FIFO_port)
-#define Write_SCSI_Data_port  (HOSTDATA(shpnt)->_Write_SCSI_Data_port)
-#define FIFO_Size             (HOSTDATA(shpnt)->_FIFO_Size)
-#define Bytes_Read            (HOSTDATA(shpnt)->_Bytes_Read)
-#define Bytes_Written         (HOSTDATA(shpnt)->_Bytes_Written)
-#define INTR_Processed        (HOSTDATA(shpnt)->_INTR_Processed)
-
-struct fd_mcs_adapters_struct {
-	char *name;
-	int id;
-	enum chip_type fd_chip;
-	int fifo_size;
-	int fifo_count;
-};
-
-#define REPLY_ID 0x5137
-
-static struct fd_mcs_adapters_struct fd_mcs_adapters[] = {
-	{"Future Domain SCSI Adapter MCS-700(18C50)",
-	 0x60e9,
-	 tmc18c50,
-	 0x2000,
-	 4},
-	{"Future Domain SCSI Adapter MCS-600/700(TMC-1800)",
-	 0x6127,
-	 tmc1800,
-	 0x2000,
-	 4},
-	{"Reply Sound Blaster/SCSI Adapter",
-	 REPLY_ID,
-	 tmc18c30,
-	 0x800,
-	 2},
-};
-
-#define FD_BRDS ARRAY_SIZE(fd_mcs_adapters)
-
-static irqreturn_t fd_mcs_intr(int irq, void *dev_id);
-
-static unsigned long addresses[] = { 0xc8000, 0xca000, 0xce000, 0xde000 };
-static unsigned short ports[] = { 0x140, 0x150, 0x160, 0x170 };
-static unsigned short interrupts[] = { 3, 5, 10, 11, 12, 14, 15, 0 };
-
-/* host information */
-static int found = 0;
-static struct Scsi_Host *hosts[FD_MAX_HOSTS + 1] = { NULL };
-
-static int user_fifo_count = 0;
-static int user_fifo_size = 0;
-
-#ifndef MODULE
-static int __init fd_mcs_setup(char *str)
-{
-	static int done_setup = 0;
-	int ints[3];
-
-	get_options(str, 3, ints);
-	if (done_setup++ || ints[0] < 1 || ints[0] > 2 || ints[1] < 1 || ints[1] > 16) {
-		printk("fd_mcs: usage: fd_mcs=FIFO_COUNT, FIFO_SIZE\n");
-		return 0;
-	}
-
-	user_fifo_count = ints[0] >= 1 ? ints[1] : 0;
-	user_fifo_size = ints[0] >= 2 ? ints[2] : 0;
-	return 1;
-}
-
-__setup("fd_mcs=", fd_mcs_setup);
-#endif /* !MODULE */
-
-static void print_banner(struct Scsi_Host *shpnt)
-{
-	printk("scsi%d <fd_mcs>: ", shpnt->host_no);
-
-	if (bios_base) {
-		printk("BIOS at 0x%lX", bios_base);
-	} else {
-		printk("No BIOS");
-	}
-
-	printk(", HostID %d, %s Chip, IRQ %d, IO 0x%lX\n", shpnt->this_id, chip == tmc18c50 ? "TMC-18C50" : (chip == tmc18c30 ? "TMC-18C30" : (chip == tmc1800 ? "TMC-1800" : "Unknown")), shpnt->irq, shpnt->io_port);
-}
-
-
-static void do_pause(unsigned amount)
-{				/* Pause for amount*10 milliseconds */
-	do {
-		mdelay(10);
-	} while (--amount);
-}
-
-static void fd_mcs_make_bus_idle(struct Scsi_Host *shpnt)
-{
-	outb(0, SCSI_Cntl_port);
-	outb(0, SCSI_Mode_Cntl_port);
-	if (chip == tmc18c50 || chip == tmc18c30)
-		outb(0x21 | PARITY_MASK, TMC_Cntl_port);	/* Clear forced intr. */
-	else
-		outb(0x01 | PARITY_MASK, TMC_Cntl_port);
-}
-
-static int fd_mcs_detect(struct scsi_host_template * tpnt)
-{
-	int loop;
-	struct Scsi_Host *shpnt;
-
-	/* get id, port, bios, irq */
-	int slot;
-	u_char pos2, pos3, pos4;
-	int id, port, irq;
-	unsigned long bios;
-
-	/* if not MCA machine, return */
-	if (!MCA_bus)
-		return 0;
-
-	/* changeable? */
-	id = 7;
-
-	for (loop = 0; loop < FD_BRDS; loop++) {
-		slot = 0;
-		while (MCA_NOTFOUND != (slot = mca_find_adapter(fd_mcs_adapters[loop].id, slot))) {
-
-			/* if we get this far, an adapter has been detected and is
-			   enabled */
-
-			printk(KERN_INFO "scsi  <fd_mcs>: %s at slot %d\n", fd_mcs_adapters[loop].name, slot + 1);
-
-			pos2 = mca_read_stored_pos(slot, 2);
-			pos3 = mca_read_stored_pos(slot, 3);
-			pos4 = mca_read_stored_pos(slot, 4);
-
-			/* ready for next probe */
-			slot++;
-
-			if (fd_mcs_adapters[loop].id == REPLY_ID) {	/* reply card */
-				static int reply_irq[] = { 10, 11, 14, 15 };
-
-				bios = 0;	/* no bios */
-
-				if (pos2 & 0x2)
-					port = ports[pos4 & 0x3];
-				else
-					continue;
-
-				/* can't really disable it, same as irq=10 */
-				irq = reply_irq[((pos4 >> 2) & 0x1) + 2 * ((pos4 >> 4) & 0x1)];
-			} else {
-				bios = addresses[pos2 >> 6];
-				port = ports[(pos2 >> 4) & 0x03];
-				irq = interrupts[(pos2 >> 1) & 0x07];
-			}
-
-			if (irq) {
-				/* claim the slot */
-				mca_set_adapter_name(slot - 1, fd_mcs_adapters[loop].name);
-
-				/* check irq/region */
-				if (request_irq(irq, fd_mcs_intr, IRQF_SHARED, "fd_mcs", hosts)) {
-					printk(KERN_ERR "fd_mcs: interrupt is not available, skipping...\n");
-					continue;
-				}
-
-				/* request I/O region */
-				if (request_region(port, 0x10, "fd_mcs")) {
-					printk(KERN_ERR "fd_mcs: I/O region is already in use, skipping...\n");
-					continue;
-				}
-				/* register */
-				if (!(shpnt = scsi_register(tpnt, sizeof(struct fd_hostdata)))) {
-					printk(KERN_ERR "fd_mcs: scsi_register() failed\n");
-					release_region(port, 0x10);
-					free_irq(irq, hosts);
-					continue;
-				}
-
-
-				/* save name */
-				strcpy(adapter_name, fd_mcs_adapters[loop].name);
-
-				/* chip/fifo */
-				chip = fd_mcs_adapters[loop].fd_chip;
-				/* use boot time value if available */
-				FIFO_COUNT = user_fifo_count ? user_fifo_count : fd_mcs_adapters[loop].fifo_count;
-				FIFO_Size = user_fifo_size ? user_fifo_size : fd_mcs_adapters[loop].fifo_size;
-
-/* FIXME: Do we need to keep this bit of code inside NOT_USED around at all? */
-#ifdef NOT_USED
-				/* *************************************************** */
-				/* Try to toggle 32-bit mode.  This only
-				   works on an 18c30 chip.  (User reports
-				   say this works, so we should switch to
-				   it in the near future.) */
-				outb(0x80, port + IO_Control);
-				if ((inb(port + Configuration2) & 0x80) == 0x80) {
-					outb(0x00, port + IO_Control);
-					if ((inb(port + Configuration2) & 0x80) == 0x00) {
-						chip = tmc18c30;
-						FIFO_Size = 0x800;	/* 2k FIFO */
-
-						printk("FIRST: chip=%s, fifo_size=0x%x\n", (chip == tmc18c30) ? "tmc18c30" : "tmc18c50", FIFO_Size);
-					}
-				}
-
-				/* That should have worked, but appears to
-				   have problems.  Let's assume it is an
-				   18c30 if the RAM is disabled. */
-
-				if (inb(port + Configuration2) & 0x02) {
-					chip = tmc18c30;
-					FIFO_Size = 0x800;	/* 2k FIFO */
-
-					printk("SECOND: chip=%s, fifo_size=0x%x\n", (chip == tmc18c30) ? "tmc18c30" : "tmc18c50", FIFO_Size);
-				}
-				/* *************************************************** */
-#endif
-
-				/* IBM/ANSI scsi scan ordering */
-				/* Stick this back in when the scsi.c changes are there */
-				shpnt->reverse_ordering = 1;
-
-
-				/* saving info */
-				hosts[found++] = shpnt;
-
-				shpnt->this_id = id;
-				shpnt->irq = irq;
-				shpnt->io_port = port;
-				shpnt->n_io_port = 0x10;
-
-				/* save */
-				bios_base = bios;
-				adapter_mask = (1 << id);
-
-				/* save more */
-				SCSI_Mode_Cntl_port = port + SCSI_Mode_Cntl;
-				FIFO_Data_Count_port = port + FIFO_Data_Count;
-				Interrupt_Cntl_port = port + Interrupt_Cntl;
-				Interrupt_Status_port = port + Interrupt_Status;
-				Interrupt_Cond_port = port + Interrupt_Cond;
-				Read_FIFO_port = port + Read_FIFO;
-				Read_SCSI_Data_port = port + Read_SCSI_Data;
-				SCSI_Cntl_port = port + SCSI_Cntl;
-				SCSI_Data_NoACK_port = port + SCSI_Data_NoACK;
-				SCSI_Status_port = port + SCSI_Status;
-				TMC_Cntl_port = port + TMC_Cntl;
-				TMC_Status_port = port + TMC_Status;
-				Write_FIFO_port = port + Write_FIFO;
-				Write_SCSI_Data_port = port + Write_SCSI_Data;
-
-				Bytes_Read = 0;
-				Bytes_Written = 0;
-				INTR_Processed = 0;
-
-				/* say something */
-				print_banner(shpnt);
-
-				/* reset */
-				outb(1, SCSI_Cntl_port);
-				do_pause(2);
-				outb(0, SCSI_Cntl_port);
-				do_pause(115);
-				outb(0, SCSI_Mode_Cntl_port);
-				outb(PARITY_MASK, TMC_Cntl_port);
-				/* done reset */
-			}
-		}
-
-		if (found == FD_MAX_HOSTS) {
-			printk("fd_mcs: detecting reached max=%d host adapters.\n", FD_MAX_HOSTS);
-			break;
-		}
-	}
-
-	return found;
-}
-
-static const char *fd_mcs_info(struct Scsi_Host *shpnt)
-{
-	return adapter_name;
-}
-
-static int TOTAL_INTR = 0;
-
-/*
- * inout : decides on the direction of the dataflow and the meaning of the 
- *         variables
- * buffer: If inout==FALSE data is being written to it else read from it
- * *start: If inout==FALSE start of the valid data in the buffer
- * offset: If inout==FALSE offset from the beginning of the imaginary file 
- *         from which we start writing into the buffer
- * length: If inout==FALSE max number of bytes to be written into the buffer 
- *         else number of bytes in the buffer
- */
-static int fd_mcs_proc_info(struct Scsi_Host *shpnt, char *buffer, char **start, off_t offset, int length, int inout)
-{
-	int len = 0;
-
-	if (inout)
-		return (-ENOSYS);
-
-	*start = buffer + offset;
-
-	len += sprintf(buffer + len, "Future Domain MCS-600/700 Driver %s\n", DRIVER_VERSION);
-	len += sprintf(buffer + len, "HOST #%d: %s\n", shpnt->host_no, adapter_name);
-	len += sprintf(buffer + len, "FIFO Size=0x%x, FIFO Count=%d\n", FIFO_Size, FIFO_COUNT);
-	len += sprintf(buffer + len, "DriverCalls=%d, Interrupts=%d, BytesRead=%d, BytesWrite=%d\n\n", TOTAL_INTR, INTR_Processed, Bytes_Read, Bytes_Written);
-
-	if ((len -= offset) <= 0)
-		return 0;
-	if (len > length)
-		len = length;
-	return len;
-}
-
-static int fd_mcs_select(struct Scsi_Host *shpnt, int target)
-{
-	int status;
-	unsigned long timeout;
-
-	outb(0x82, SCSI_Cntl_port);	/* Bus Enable + Select */
-	outb(adapter_mask | (1 << target), SCSI_Data_NoACK_port);
-
-	/* Stop arbitration and enable parity */
-	outb(PARITY_MASK, TMC_Cntl_port);
-
-	timeout = 350;		/* 350mS -- because of timeouts
-				   (was 250mS) */
-
-	do {
-		status = inb(SCSI_Status_port);	/* Read adapter status */
-		if (status & 1) {	/* Busy asserted */
-			/* Enable SCSI Bus (on error, should make bus idle with 0) */
-			outb(0x80, SCSI_Cntl_port);
-			return 0;
-		}
-		udelay(1000);	/* wait one msec */
-	} while (--timeout);
-
-	/* Make bus idle */
-	fd_mcs_make_bus_idle(shpnt);
-#if EVERY_ACCESS
-	if (!target)
-		printk("Selection failed\n");
-#endif
-#if ERRORS_ONLY
-	if (!target) {
-		static int flag = 0;
-
-		if (!flag)	/* Skip first failure for all chips. */
-			++flag;
-		else
-			printk("fd_mcs: Selection failed\n");
-	}
-#endif
-	return 1;
-}
-
-static void my_done(struct Scsi_Host *shpnt, int error)
-{
-	if (in_command) {
-		in_command = 0;
-		outb(0x00, Interrupt_Cntl_port);
-		fd_mcs_make_bus_idle(shpnt);
-		current_SC->result = error;
-		current_SC->scsi_done(current_SC);
-	} else {
-		panic("fd_mcs: my_done() called outside of command\n");
-	}
-#if DEBUG_RACE
-	in_interrupt_flag = 0;
-#endif
-}
-
-/* only my_done needs to be protected  */
-static irqreturn_t fd_mcs_intr(int irq, void *dev_id)
-{
-	unsigned long flags;
-	int status;
-	int done = 0;
-	unsigned data_count, tmp_count;
-
-	int i = 0;
-	struct Scsi_Host *shpnt;
-
-	TOTAL_INTR++;
-
-	/* search for one adapter-response on shared interrupt */
-	while ((shpnt = hosts[i++])) {
-		if ((inb(TMC_Status_port)) & 1)
-			break;
-	}
-
-	/* return if some other device on this IRQ caused the interrupt */
-	if (!shpnt) {
-		return IRQ_NONE;
-	}
-
-	INTR_Processed++;
-
-	outb(0x00, Interrupt_Cntl_port);
-
-	/* Abort calls my_done, so we do nothing here. */
-	if (current_SC->SCp.phase & aborted) {
-#if DEBUG_ABORT
-		printk("Interrupt after abort, ignoring\n");
-#endif
-		/* return IRQ_HANDLED; */
-	}
-#if DEBUG_RACE
-	++in_interrupt_flag;
-#endif
-
-	if (current_SC->SCp.phase & in_arbitration) {
-		status = inb(TMC_Status_port);	/* Read adapter status */
-		if (!(status & 0x02)) {
-#if EVERY_ACCESS
-			printk(" AFAIL ");
-#endif
-			spin_lock_irqsave(shpnt->host_lock, flags);
-			my_done(shpnt, DID_BUS_BUSY << 16);
-			spin_unlock_irqrestore(shpnt->host_lock, flags);
-			return IRQ_HANDLED;
-		}
-		current_SC->SCp.phase = in_selection;
-
-		outb(0x40 | FIFO_COUNT, Interrupt_Cntl_port);
-
-		outb(0x82, SCSI_Cntl_port);	/* Bus Enable + Select */
-		outb(adapter_mask | (1 << scmd_id(current_SC)), SCSI_Data_NoACK_port);
-
-		/* Stop arbitration and enable parity */
-		outb(0x10 | PARITY_MASK, TMC_Cntl_port);
-#if DEBUG_RACE
-		in_interrupt_flag = 0;
-#endif
-		return IRQ_HANDLED;
-	} else if (current_SC->SCp.phase & in_selection) {
-		status = inb(SCSI_Status_port);
-		if (!(status & 0x01)) {
-			/* Try again, for slow devices */
-			if (fd_mcs_select(shpnt, scmd_id(current_SC))) {
-#if EVERY_ACCESS
-				printk(" SFAIL ");
-#endif
-				spin_lock_irqsave(shpnt->host_lock, flags);
-				my_done(shpnt, DID_NO_CONNECT << 16);
-				spin_unlock_irqrestore(shpnt->host_lock, flags);
-				return IRQ_HANDLED;
-			} else {
-#if EVERY_ACCESS
-				printk(" AltSel ");
-#endif
-				/* Stop arbitration and enable parity */
-				outb(0x10 | PARITY_MASK, TMC_Cntl_port);
-			}
-		}
-		current_SC->SCp.phase = in_other;
-		outb(0x90 | FIFO_COUNT, Interrupt_Cntl_port);
-		outb(0x80, SCSI_Cntl_port);
-#if DEBUG_RACE
-		in_interrupt_flag = 0;
-#endif
-		return IRQ_HANDLED;
-	}
-
-	/* current_SC->SCp.phase == in_other: this is the body of the routine */
-
-	status = inb(SCSI_Status_port);
-
-	if (status & 0x10) {	/* REQ */
-
-		switch (status & 0x0e) {
-
-		case 0x08:	/* COMMAND OUT */
-			outb(current_SC->cmnd[current_SC->SCp.sent_command++], Write_SCSI_Data_port);
-#if EVERY_ACCESS
-			printk("CMD = %x,", current_SC->cmnd[current_SC->SCp.sent_command - 1]);
-#endif
-			break;
-		case 0x00:	/* DATA OUT -- tmc18c50/tmc18c30 only */
-			if (chip != tmc1800 && !current_SC->SCp.have_data_in) {
-				current_SC->SCp.have_data_in = -1;
-				outb(0xd0 | PARITY_MASK, TMC_Cntl_port);
-			}
-			break;
-		case 0x04:	/* DATA IN -- tmc18c50/tmc18c30 only */
-			if (chip != tmc1800 && !current_SC->SCp.have_data_in) {
-				current_SC->SCp.have_data_in = 1;
-				outb(0x90 | PARITY_MASK, TMC_Cntl_port);
-			}
-			break;
-		case 0x0c:	/* STATUS IN */
-			current_SC->SCp.Status = inb(Read_SCSI_Data_port);
-#if EVERY_ACCESS
-			printk("Status = %x, ", current_SC->SCp.Status);
-#endif
-#if ERRORS_ONLY
-			if (current_SC->SCp.Status && current_SC->SCp.Status != 2 && current_SC->SCp.Status != 8) {
-				printk("ERROR fd_mcs: target = %d, command = %x, status = %x\n", current_SC->device->id, current_SC->cmnd[0], current_SC->SCp.Status);
-			}
-#endif
-			break;
-		case 0x0a:	/* MESSAGE OUT */
-			outb(MESSAGE_REJECT, Write_SCSI_Data_port);	/* Reject */
-			break;
-		case 0x0e:	/* MESSAGE IN */
-			current_SC->SCp.Message = inb(Read_SCSI_Data_port);
-#if EVERY_ACCESS
-			printk("Message = %x, ", current_SC->SCp.Message);
-#endif
-			if (!current_SC->SCp.Message)
-				++done;
-#if DEBUG_MESSAGES || EVERY_ACCESS
-			if (current_SC->SCp.Message) {
-				printk("fd_mcs: message = %x\n", current_SC->SCp.Message);
-			}
-#endif
-			break;
-		}
-	}
-
-	if (chip == tmc1800 && !current_SC->SCp.have_data_in && (current_SC->SCp.sent_command >= current_SC->cmd_len)) {
-		/* We have to get the FIFO direction
-		   correct, so I've made a table based
-		   on the SCSI Standard of which commands
-		   appear to require a DATA OUT phase.
-		 */
-		/*
-		   p. 94: Command for all device types
-		   CHANGE DEFINITION            40 DATA OUT
-		   COMPARE                      39 DATA OUT
-		   COPY                         18 DATA OUT
-		   COPY AND VERIFY              3a DATA OUT
-		   INQUIRY                      12 
-		   LOG SELECT                   4c DATA OUT
-		   LOG SENSE                    4d
-		   MODE SELECT (6)              15 DATA OUT
-		   MODE SELECT (10)             55 DATA OUT
-		   MODE SENSE (6)               1a
-		   MODE SENSE (10)              5a
-		   READ BUFFER                  3c
-		   RECEIVE DIAGNOSTIC RESULTS   1c
-		   REQUEST SENSE                03
-		   SEND DIAGNOSTIC              1d DATA OUT
-		   TEST UNIT READY              00
-		   WRITE BUFFER                 3b DATA OUT
-
-		   p.178: Commands for direct-access devices (not listed on p. 94)
-		   FORMAT UNIT                  04 DATA OUT
-		   LOCK-UNLOCK CACHE            36
-		   PRE-FETCH                    34
-		   PREVENT-ALLOW MEDIUM REMOVAL 1e
-		   READ (6)/RECEIVE             08
-		   READ (10)                    3c
-		   READ CAPACITY                25
-		   READ DEFECT DATA (10)        37
-		   READ LONG                    3e
-		   REASSIGN BLOCKS              07 DATA OUT
-		   RELEASE                      17
-		   RESERVE                      16 DATA OUT
-		   REZERO UNIT/REWIND           01
-		   SEARCH DATA EQUAL (10)       31 DATA OUT
-		   SEARCH DATA HIGH (10)        30 DATA OUT
-		   SEARCH DATA LOW (10)         32 DATA OUT
-		   SEEK (6)                     0b
-		   SEEK (10)                    2b
-		   SET LIMITS (10)              33
-		   START STOP UNIT              1b
-		   SYNCHRONIZE CACHE            35
-		   VERIFY (10)                  2f
-		   WRITE (6)/PRINT/SEND         0a DATA OUT
-		   WRITE (10)/SEND              2a DATA OUT
-		   WRITE AND VERIFY (10)        2e DATA OUT
-		   WRITE LONG                   3f DATA OUT
-		   WRITE SAME                   41 DATA OUT ?
-
-		   p. 261: Commands for sequential-access devices (not previously listed)
-		   ERASE                        19
-		   LOAD UNLOAD                  1b
-		   LOCATE                       2b
-		   READ BLOCK LIMITS            05
-		   READ POSITION                34
-		   READ REVERSE                 0f
-		   RECOVER BUFFERED DATA        14
-		   SPACE                        11
-		   WRITE FILEMARKS              10 ?
-
-		   p. 298: Commands for printer devices (not previously listed)
-		   ****** NOT SUPPORTED BY THIS DRIVER, since 0b is SEEK (6) *****
-		   SLEW AND PRINT               0b DATA OUT  -- same as seek
-		   STOP PRINT                   1b
-		   SYNCHRONIZE BUFFER           10
-
-		   p. 315: Commands for processor devices (not previously listed)
-
-		   p. 321: Commands for write-once devices (not previously listed)
-		   MEDIUM SCAN                  38
-		   READ (12)                    a8
-		   SEARCH DATA EQUAL (12)       b1 DATA OUT
-		   SEARCH DATA HIGH (12)        b0 DATA OUT
-		   SEARCH DATA LOW (12)         b2 DATA OUT
-		   SET LIMITS (12)              b3
-		   VERIFY (12)                  af
-		   WRITE (12)                   aa DATA OUT
-		   WRITE AND VERIFY (12)        ae DATA OUT
-
-		   p. 332: Commands for CD-ROM devices (not previously listed)
-		   PAUSE/RESUME                 4b
-		   PLAY AUDIO (10)              45
-		   PLAY AUDIO (12)              a5
-		   PLAY AUDIO MSF               47
-		   PLAY TRACK RELATIVE (10)     49
-		   PLAY TRACK RELATIVE (12)     a9
-		   READ HEADER                  44
-		   READ SUB-CHANNEL             42
-		   READ TOC                     43
-
-		   p. 370: Commands for scanner devices (not previously listed)
-		   GET DATA BUFFER STATUS       34
-		   GET WINDOW                   25
-		   OBJECT POSITION              31
-		   SCAN                         1b
-		   SET WINDOW                   24 DATA OUT
-
-		   p. 391: Commands for optical memory devices (not listed)
-		   ERASE (10)                   2c
-		   ERASE (12)                   ac
-		   MEDIUM SCAN                  38 DATA OUT
-		   READ DEFECT DATA (12)        b7
-		   READ GENERATION              29
-		   READ UPDATED BLOCK           2d
-		   UPDATE BLOCK                 3d DATA OUT
-
-		   p. 419: Commands for medium changer devices (not listed)
-		   EXCHANGE MEDIUM              46
-		   INITIALIZE ELEMENT STATUS    07
-		   MOVE MEDIUM                  a5
-		   POSITION TO ELEMENT          2b
-		   READ ELEMENT STATUS          b8
-		   REQUEST VOL. ELEMENT ADDRESS b5
-		   SEND VOLUME TAG              b6 DATA OUT
-
-		   p. 454: Commands for communications devices (not listed previously)
-		   GET MESSAGE (6)              08
-		   GET MESSAGE (10)             28
-		   GET MESSAGE (12)             a8
-		 */
-
-		switch (current_SC->cmnd[0]) {
-		case CHANGE_DEFINITION:
-		case COMPARE:
-		case COPY:
-		case COPY_VERIFY:
-		case LOG_SELECT:
-		case MODE_SELECT:
-		case MODE_SELECT_10:
-		case SEND_DIAGNOSTIC:
-		case WRITE_BUFFER:
-
-		case FORMAT_UNIT:
-		case REASSIGN_BLOCKS:
-		case RESERVE:
-		case SEARCH_EQUAL:
-		case SEARCH_HIGH:
-		case SEARCH_LOW:
-		case WRITE_6:
-		case WRITE_10:
-		case WRITE_VERIFY:
-		case 0x3f:
-		case 0x41:
-
-		case 0xb1:
-		case 0xb0:
-		case 0xb2:
-		case 0xaa:
-		case 0xae:
-
-		case 0x24:
-
-		case 0x38:
-		case 0x3d:
-
-		case 0xb6:
-
-		case 0xea:	/* alternate number for WRITE LONG */
-
-			current_SC->SCp.have_data_in = -1;
-			outb(0xd0 | PARITY_MASK, TMC_Cntl_port);
-			break;
-
-		case 0x00:
-		default:
-
-			current_SC->SCp.have_data_in = 1;
-			outb(0x90 | PARITY_MASK, TMC_Cntl_port);
-			break;
-		}
-	}
-
-	if (current_SC->SCp.have_data_in == -1) {	/* DATA OUT */
-		while ((data_count = FIFO_Size - inw(FIFO_Data_Count_port)) > 512) {
-#if EVERY_ACCESS
-			printk("DC=%d, ", data_count);
-#endif
-			if (data_count > current_SC->SCp.this_residual)
-				data_count = current_SC->SCp.this_residual;
-			if (data_count > 0) {
-#if EVERY_ACCESS
-				printk("%d OUT, ", data_count);
-#endif
-				if (data_count == 1) {
-					Bytes_Written++;
-
-					outb(*current_SC->SCp.ptr++, Write_FIFO_port);
-					--current_SC->SCp.this_residual;
-				} else {
-					data_count >>= 1;
-					tmp_count = data_count << 1;
-					outsw(Write_FIFO_port, current_SC->SCp.ptr, data_count);
-					current_SC->SCp.ptr += tmp_count;
-					Bytes_Written += tmp_count;
-					current_SC->SCp.this_residual -= tmp_count;
-				}
-			}
-			if (!current_SC->SCp.this_residual) {
-				if (current_SC->SCp.buffers_residual) {
-					--current_SC->SCp.buffers_residual;
-					++current_SC->SCp.buffer;
-					current_SC->SCp.ptr = sg_virt(current_SC->SCp.buffer);
-					current_SC->SCp.this_residual = current_SC->SCp.buffer->length;
-				} else
-					break;
-			}
-		}
-	} else if (current_SC->SCp.have_data_in == 1) {	/* DATA IN */
-		while ((data_count = inw(FIFO_Data_Count_port)) > 0) {
-#if EVERY_ACCESS
-			printk("DC=%d, ", data_count);
-#endif
-			if (data_count > current_SC->SCp.this_residual)
-				data_count = current_SC->SCp.this_residual;
-			if (data_count) {
-#if EVERY_ACCESS
-				printk("%d IN, ", data_count);
-#endif
-				if (data_count == 1) {
-					Bytes_Read++;
-					*current_SC->SCp.ptr++ = inb(Read_FIFO_port);
-					--current_SC->SCp.this_residual;
-				} else {
-					data_count >>= 1;	/* Number of words */
-					tmp_count = data_count << 1;
-					insw(Read_FIFO_port, current_SC->SCp.ptr, data_count);
-					current_SC->SCp.ptr += tmp_count;
-					Bytes_Read += tmp_count;
-					current_SC->SCp.this_residual -= tmp_count;
-				}
-			}
-			if (!current_SC->SCp.this_residual && current_SC->SCp.buffers_residual) {
-				--current_SC->SCp.buffers_residual;
-				++current_SC->SCp.buffer;
-				current_SC->SCp.ptr = sg_virt(current_SC->SCp.buffer);
-				current_SC->SCp.this_residual = current_SC->SCp.buffer->length;
-			}
-		}
-	}
-
-	if (done) {
-#if EVERY_ACCESS
-		printk(" ** IN DONE %d ** ", current_SC->SCp.have_data_in);
-#endif
-
-#if EVERY_ACCESS
-		printk("BEFORE MY_DONE. . .");
-#endif
-		spin_lock_irqsave(shpnt->host_lock, flags);
-		my_done(shpnt, (current_SC->SCp.Status & 0xff)
-			| ((current_SC->SCp.Message & 0xff) << 8) | (DID_OK << 16));
-		spin_unlock_irqrestore(shpnt->host_lock, flags);
-#if EVERY_ACCESS
-		printk("RETURNING.\n");
-#endif
-
-	} else {
-		if (current_SC->SCp.phase & disconnect) {
-			outb(0xd0 | FIFO_COUNT, Interrupt_Cntl_port);
-			outb(0x00, SCSI_Cntl_port);
-		} else {
-			outb(0x90 | FIFO_COUNT, Interrupt_Cntl_port);
-		}
-	}
-#if DEBUG_RACE
-	in_interrupt_flag = 0;
-#endif
-	return IRQ_HANDLED;
-}
-
-static int fd_mcs_release(struct Scsi_Host *shpnt)
-{
-	int i, this_host, irq_usage;
-
-	release_region(shpnt->io_port, shpnt->n_io_port);
-
-	this_host = -1;
-	irq_usage = 0;
-	for (i = 0; i < found; i++) {
-		if (shpnt == hosts[i])
-			this_host = i;
-		if (shpnt->irq == hosts[i]->irq)
-			irq_usage++;
-	}
-
-	/* only for the last one */
-	if (1 == irq_usage)
-		free_irq(shpnt->irq, hosts);
-
-	found--;
-
-	for (i = this_host; i < found; i++)
-		hosts[i] = hosts[i + 1];
-
-	hosts[found] = NULL;
-
-	return 0;
-}
-
-static int fd_mcs_queue_lck(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *))
-{
-	struct Scsi_Host *shpnt = SCpnt->device->host;
-
-	if (in_command) {
-		panic("fd_mcs: fd_mcs_queue() NOT REENTRANT!\n");
-	}
-#if EVERY_ACCESS
-	printk("queue: target = %d cmnd = 0x%02x pieces = %d size = %u\n",
-		SCpnt->target, *(unsigned char *) SCpnt->cmnd,
-		scsi_sg_count(SCpnt), scsi_bufflen(SCpnt));
-#endif
-
-	fd_mcs_make_bus_idle(shpnt);
-
-	SCpnt->scsi_done = done;	/* Save this for the done function */
-	current_SC = SCpnt;
-
-	/* Initialize static data */
-
-	if (scsi_bufflen(current_SC)) {
-		current_SC->SCp.buffer = scsi_sglist(current_SC);
-		current_SC->SCp.ptr = sg_virt(current_SC->SCp.buffer);
-		current_SC->SCp.this_residual = current_SC->SCp.buffer->length;
-		current_SC->SCp.buffers_residual = scsi_sg_count(current_SC) - 1;
-	} else {
-		current_SC->SCp.ptr = NULL;
-		current_SC->SCp.this_residual = 0;
-		current_SC->SCp.buffer = NULL;
-		current_SC->SCp.buffers_residual = 0;
-	}
-
-
-	current_SC->SCp.Status = 0;
-	current_SC->SCp.Message = 0;
-	current_SC->SCp.have_data_in = 0;
-	current_SC->SCp.sent_command = 0;
-	current_SC->SCp.phase = in_arbitration;
-
-	/* Start arbitration */
-	outb(0x00, Interrupt_Cntl_port);
-	outb(0x00, SCSI_Cntl_port);	/* Disable data drivers */
-	outb(adapter_mask, SCSI_Data_NoACK_port);	/* Set our id bit */
-	in_command = 1;
-	outb(0x20, Interrupt_Cntl_port);
-	outb(0x14 | PARITY_MASK, TMC_Cntl_port);	/* Start arbitration */
-
-	return 0;
-}
-
-static DEF_SCSI_QCMD(fd_mcs_queue)
-
-#if DEBUG_ABORT || DEBUG_RESET
-static void fd_mcs_print_info(Scsi_Cmnd * SCpnt)
-{
-	unsigned int imr;
-	unsigned int irr;
-	unsigned int isr;
-	struct Scsi_Host *shpnt = SCpnt->host;
-
-	if (!SCpnt || !SCpnt->host) {
-		printk("fd_mcs: cannot provide detailed information\n");
-	}
-
-	printk("%s\n", fd_mcs_info(SCpnt->host));
-	print_banner(SCpnt->host);
-	switch (SCpnt->SCp.phase) {
-	case in_arbitration:
-		printk("arbitration ");
-		break;
-	case in_selection:
-		printk("selection ");
-		break;
-	case in_other:
-		printk("other ");
-		break;
-	default:
-		printk("unknown ");
-		break;
-	}
-
-	printk("(%d), target = %d cmnd = 0x%02x pieces = %d size = %u\n",
-		SCpnt->SCp.phase, SCpnt->device->id, *(unsigned char *) SCpnt->cmnd,
-		scsi_sg_count(SCpnt), scsi_bufflen(SCpnt));
-	printk("sent_command = %d, have_data_in = %d, timeout = %d\n", SCpnt->SCp.sent_command, SCpnt->SCp.have_data_in, SCpnt->timeout);
-#if DEBUG_RACE
-	printk("in_interrupt_flag = %d\n", in_interrupt_flag);
-#endif
-
-	imr = (inb(0x0a1) << 8) + inb(0x21);
-	outb(0x0a, 0xa0);
-	irr = inb(0xa0) << 8;
-	outb(0x0a, 0x20);
-	irr += inb(0x20);
-	outb(0x0b, 0xa0);
-	isr = inb(0xa0) << 8;
-	outb(0x0b, 0x20);
-	isr += inb(0x20);
-
-	/* Print out interesting information */
-	printk("IMR = 0x%04x", imr);
-	if (imr & (1 << shpnt->irq))
-		printk(" (masked)");
-	printk(", IRR = 0x%04x, ISR = 0x%04x\n", irr, isr);
-
-	printk("SCSI Status      = 0x%02x\n", inb(SCSI_Status_port));
-	printk("TMC Status       = 0x%02x", inb(TMC_Status_port));
-	if (inb(TMC_Status_port) & 1)
-		printk(" (interrupt)");
-	printk("\n");
-	printk("Interrupt Status = 0x%02x", inb(Interrupt_Status_port));
-	if (inb(Interrupt_Status_port) & 0x08)
-		printk(" (enabled)");
-	printk("\n");
-	if (chip == tmc18c50 || chip == tmc18c30) {
-		printk("FIFO Status      = 0x%02x\n", inb(shpnt->io_port + FIFO_Status));
-		printk("Int. Condition   = 0x%02x\n", inb(shpnt->io_port + Interrupt_Cond));
-	}
-	printk("Configuration 1  = 0x%02x\n", inb(shpnt->io_port + Configuration1));
-	if (chip == tmc18c50 || chip == tmc18c30)
-		printk("Configuration 2  = 0x%02x\n", inb(shpnt->io_port + Configuration2));
-}
-#endif
-
-static int fd_mcs_abort(Scsi_Cmnd * SCpnt)
-{
-	struct Scsi_Host *shpnt = SCpnt->device->host;
-
-	unsigned long flags;
-#if EVERY_ACCESS || ERRORS_ONLY || DEBUG_ABORT
-	printk("fd_mcs: abort ");
-#endif
-
-	spin_lock_irqsave(shpnt->host_lock, flags);
-	if (!in_command) {
-#if EVERY_ACCESS || ERRORS_ONLY
-		printk(" (not in command)\n");
-#endif
-		spin_unlock_irqrestore(shpnt->host_lock, flags);
-		return FAILED;
-	} else
-		printk("\n");
-
-#if DEBUG_ABORT
-	fd_mcs_print_info(SCpnt);
-#endif
-
-	fd_mcs_make_bus_idle(shpnt);
-
-	current_SC->SCp.phase |= aborted;
-
-	current_SC->result = DID_ABORT << 16;
-
-	/* Aborts are not done well. . . */
-	my_done(shpnt, DID_ABORT << 16);
-
-	spin_unlock_irqrestore(shpnt->host_lock, flags);
-	return SUCCESS;
-}
-
-static int fd_mcs_bus_reset(Scsi_Cmnd * SCpnt) {
-	struct Scsi_Host *shpnt = SCpnt->device->host;
-	unsigned long flags;
-
-#if DEBUG_RESET
-	static int called_once = 0;
-#endif
-
-#if ERRORS_ONLY
-	if (SCpnt)
-		printk("fd_mcs: SCSI Bus Reset\n");
-#endif
-
-#if DEBUG_RESET
-	if (called_once)
-		fd_mcs_print_info(current_SC);
-	called_once = 1;
-#endif
-
-	spin_lock_irqsave(shpnt->host_lock, flags);
-
-	outb(1, SCSI_Cntl_port);
-	do_pause(2);
-	outb(0, SCSI_Cntl_port);
-	do_pause(115);
-	outb(0, SCSI_Mode_Cntl_port);
-	outb(PARITY_MASK, TMC_Cntl_port);
-
-	spin_unlock_irqrestore(shpnt->host_lock, flags);
-
-	/* Unless this is the very first call (i.e., SCPnt == NULL), everything
-	   is probably hosed at this point.  We will, however, try to keep
-	   things going by informing the high-level code that we need help. */
-		return SUCCESS;
-}
-
-#include <scsi/scsi_ioctl.h>
-
-static int fd_mcs_biosparam(struct scsi_device * disk, struct block_device *bdev,
-			    sector_t capacity, int *info_array) 
-{
-	unsigned char *p = scsi_bios_ptable(bdev);
-	int size = capacity;
-
-	/* BIOS >= 3.4 for MCA cards */
-	/* This algorithm was provided by Future Domain (much thanks!). */
-
-	if (p && p[65] == 0xaa && p[64] == 0x55	/* Partition table valid */
-	    && p[4]) {	/* Partition type */
-		/* The partition table layout is as follows:
-
-		   Start: 0x1b3h
-		   Offset: 0 = partition status
-		   1 = starting head
-		   2 = starting sector and cylinder (word, encoded)
-		   4 = partition type
-		   5 = ending head
-		   6 = ending sector and cylinder (word, encoded)
-		   8 = starting absolute sector (double word)
-		   c = number of sectors (double word)
-		   Signature: 0x1fe = 0x55aa
-
-		   So, this algorithm assumes:
-		   1) the first partition table is in use,
-		   2) the data in the first entry is correct, and
-		   3) partitions never divide cylinders
-
-		   Note that (1) may be FALSE for NetBSD (and other BSD flavors),
-		   as well as for Linux.  Note also, that Linux doesn't pay any
-		   attention to the fields that are used by this algorithm -- it
-		   only uses the absolute sector data.  Recent versions of Linux's
-		   fdisk(1) will fill this data in correctly, and forthcoming
-		   versions will check for consistency.
-
-		   Checking for a non-zero partition type is not part of the
-		   Future Domain algorithm, but it seemed to be a reasonable thing
-		   to do, especially in the Linux and BSD worlds. */
-
-		info_array[0] = p[5] + 1;	/* heads */
-		info_array[1] = p[6] & 0x3f;	/* sectors */
-	} else {
-		/* Note that this new method guarantees that there will always be
-		   less than 1024 cylinders on a platter.  This is good for drives
-		   up to approximately 7.85GB (where 1GB = 1024 * 1024 kB). */
-		if ((unsigned int) size >= 0x7e0000U) 
-		{
-			info_array[0] = 0xff;	/* heads   = 255 */
-			info_array[1] = 0x3f;	/* sectors =  63 */
-		} else if ((unsigned int) size >= 0x200000U) {
-			info_array[0] = 0x80;	/* heads   = 128 */
-			info_array[1] = 0x3f;	/* sectors =  63 */
-		} else {
-			info_array[0] = 0x40;	/* heads   =  64 */
-			info_array[1] = 0x20;	/* sectors =  32 */
-		}
-	}
-	/* For both methods, compute the cylinders */
-	info_array[2] = (unsigned int) size / (info_array[0] * info_array[1]);
-	kfree(p);
-	return 0;
-}
-
-static struct scsi_host_template driver_template = {
-	.proc_name			= "fd_mcs",
-	.proc_info			= fd_mcs_proc_info,
-	.detect				= fd_mcs_detect,
-	.release			= fd_mcs_release,
-	.info				= fd_mcs_info,
-	.queuecommand   		= fd_mcs_queue, 
-	.eh_abort_handler		= fd_mcs_abort,
-	.eh_bus_reset_handler		= fd_mcs_bus_reset,
-	.bios_param     		= fd_mcs_biosparam,
-	.can_queue      		= 1,
-	.this_id        		= 7,
-	.sg_tablesize   		= 64,
-	.cmd_per_lun    		= 1,
-	.use_clustering 		= DISABLE_CLUSTERING,
-};
-#include "scsi_module.c"
-
-MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/ibmmca.c b/drivers/scsi/ibmmca.c
deleted file mode 100644
index cd09132..0000000
--- a/drivers/scsi/ibmmca.c
+++ /dev/null
@@ -1,2379 +0,0 @@
-/*
- Low Level Linux Driver for the IBM Microchannel SCSI Subsystem for
- Linux Kernel >= 2.4.0.
- Copyright (c) 1995 Strom Systems, Inc. under the terms of the GNU
- General Public License. Written by Martin Kolinek, December 1995.
- Further development by: Chris Beauregard, Klaus Kudielka, Michael Lang
- See the file Documentation/scsi/ibmmca.txt for a detailed description
- of this driver, the commandline arguments and the history of its
- development.
- See the WWW-page: http://www.uni-mainz.de/~langm000/linux.html for latest
- updates, info and ADF-files for adapters supported by this driver.
-
- Alan Cox <alan@lxorguk.ukuu.org.uk>
- Updated for Linux 2.5.45 to use the new error handler, cleaned up the
- lock macros and did a few unavoidable locking tweaks, plus one locking
- fix in the irq and completion path.
- 
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/ctype.h>
-#include <linux/string.h>
-#include <linux/interrupt.h>
-#include <linux/ioport.h>
-#include <linux/delay.h>
-#include <linux/blkdev.h>
-#include <linux/proc_fs.h>
-#include <linux/stat.h>
-#include <linux/mca.h>
-#include <linux/spinlock.h>
-#include <linux/init.h>
-
-#include <asm/io.h>
-
-#include "scsi.h"
-#include <scsi/scsi_host.h>
-
-/* Common forward declarations for all Linux-versions: */
-static int ibmmca_queuecommand (struct Scsi_Host *, struct scsi_cmnd *);
-static int ibmmca_abort (Scsi_Cmnd *);
-static int ibmmca_host_reset (Scsi_Cmnd *);
-static int ibmmca_biosparam (struct scsi_device *, struct block_device *, sector_t, int *);
-static int ibmmca_proc_info(struct Scsi_Host *shpnt, char *buffer, char **start, off_t offset, int length, int inout);
-
-
-
-/* current version of this driver-source: */
-#define IBMMCA_SCSI_DRIVER_VERSION "4.0b-ac"
-
-/* driver configuration */
-#define IM_MAX_HOSTS     8	/* maximum number of host adapters */
-#define IM_RESET_DELAY	60	/* seconds allowed for a reset */
-
-/* driver debugging - #undef all for normal operation */
-/* if defined: count interrupts and ignore this special one: */
-#undef	IM_DEBUG_TIMEOUT	//50
-#define TIMEOUT_PUN	0
-#define TIMEOUT_LUN	0
-/* verbose interrupt: */
-#undef IM_DEBUG_INT
-/* verbose queuecommand: */
-#undef IM_DEBUG_CMD
-/* verbose queucommand for specific SCSI-device type: */
-#undef IM_DEBUG_CMD_SPEC_DEV
-/* verbose device probing */
-#undef IM_DEBUG_PROBE
-
-/* device type that shall be displayed on syslog (only during debugging): */
-#define IM_DEBUG_CMD_DEVICE	TYPE_TAPE
-
-/* relative addresses of hardware registers on a subsystem */
-#define IM_CMD_REG(h)	((h)->io_port)	/*Command Interface, (4 bytes long) */
-#define IM_ATTN_REG(h)	((h)->io_port+4)	/*Attention (1 byte) */
-#define IM_CTR_REG(h)	((h)->io_port+5)	/*Basic Control (1 byte) */
-#define IM_INTR_REG(h)	((h)->io_port+6)	/*Interrupt Status (1 byte, r/o) */
-#define IM_STAT_REG(h)	((h)->io_port+7)	/*Basic Status (1 byte, read only) */
-
-/* basic I/O-port of first adapter */
-#define IM_IO_PORT	0x3540
-/* maximum number of hosts that can be found */
-#define IM_N_IO_PORT	8
-
-/*requests going into the upper nibble of the Attention register */
-/*note: the lower nibble specifies the device(0-14), or subsystem(15) */
-#define IM_IMM_CMD	0x10	/*immediate command */
-#define IM_SCB		0x30	/*Subsystem Control Block command */
-#define IM_LONG_SCB	0x40	/*long Subsystem Control Block command */
-#define IM_EOI		0xe0	/*end-of-interrupt request */
-
-/*values for bits 7,1,0 of Basic Control reg. (bits 6-2 reserved) */
-#define IM_HW_RESET	0x80	/*hardware reset */
-#define IM_ENABLE_DMA	0x02	/*enable subsystem's busmaster DMA */
-#define IM_ENABLE_INTR	0x01	/*enable interrupts to the system */
-
-/*to interpret the upper nibble of Interrupt Status register */
-/*note: the lower nibble specifies the device(0-14), or subsystem(15) */
-#define IM_SCB_CMD_COMPLETED			0x10
-#define IM_SCB_CMD_COMPLETED_WITH_RETRIES	0x50
-#define IM_LOOP_SCATTER_BUFFER_FULL		0x60
-#define IM_ADAPTER_HW_FAILURE			0x70
-#define IM_IMMEDIATE_CMD_COMPLETED		0xa0
-#define IM_CMD_COMPLETED_WITH_FAILURE		0xc0
-#define IM_CMD_ERROR				0xe0
-#define IM_SOFTWARE_SEQUENCING_ERROR		0xf0
-
-/*to interpret bits 3-0 of Basic Status register (bits 7-4 reserved) */
-#define IM_CMD_REG_FULL		0x08
-#define IM_CMD_REG_EMPTY	0x04
-#define IM_INTR_REQUEST		0x02
-#define IM_BUSY			0x01
-
-/*immediate commands (word written into low 2 bytes of command reg) */
-#define IM_RESET_IMM_CMD	0x0400
-#define IM_FEATURE_CTR_IMM_CMD	0x040c
-#define IM_DMA_PACING_IMM_CMD	0x040d
-#define IM_ASSIGN_IMM_CMD	0x040e
-#define IM_ABORT_IMM_CMD	0x040f
-#define IM_FORMAT_PREP_IMM_CMD	0x0417
-
-/*SCB (Subsystem Control Block) structure */
-struct im_scb {
-	unsigned short command;	/*command word (read, etc.) */
-	unsigned short enable;	/*enable word, modifies cmd */
-	union {
-		unsigned long log_blk_adr;	/*block address on SCSI device */
-		unsigned char scsi_cmd_length;	/*6,10,12, for other scsi cmd */
-	} u1;
-	unsigned long sys_buf_adr;	/*physical system memory adr */
-	unsigned long sys_buf_length;	/*size of sys mem buffer */
-	unsigned long tsb_adr;	/*Termination Status Block adr */
-	unsigned long scb_chain_adr;	/*optional SCB chain address */
-	union {
-		struct {
-			unsigned short count;	/*block count, on SCSI device */
-			unsigned short length;	/*block length, on SCSI device */
-		} blk;
-		unsigned char scsi_command[12];	/*other scsi command */
-	} u2;
-};
-
-/*structure scatter-gather element (for list of system memory areas) */
-struct im_sge {
-	void *address;
-	unsigned long byte_length;
-};
-
-/*structure returned by a get_pos_info command: */
-struct im_pos_info {
-	unsigned short pos_id;	/* adapter id */
-	unsigned char pos_3a;	/* pos 3 (if pos 6 = 0) */
-	unsigned char pos_2;	/* pos 2 */
-	unsigned char int_level;	/* interrupt level IRQ 11 or 14 */
-	unsigned char pos_4a;	/* pos 4 (if pos 6 = 0) */
-	unsigned short connector_size;	/* MCA connector size: 16 or 32 Bit */
-	unsigned char num_luns;	/* number of supported luns per device */
-	unsigned char num_puns;	/* number of supported puns */
-	unsigned char pacing_factor;	/* pacing factor */
-	unsigned char num_ldns;	/* number of ldns available */
-	unsigned char eoi_off;	/* time EOI and interrupt inactive */
-	unsigned char max_busy;	/* time between reset and busy on */
-	unsigned short cache_stat;	/* ldn cachestat. Bit=1 = not cached */
-	unsigned short retry_stat;	/* retry status of ldns. Bit=1=disabled */
-	unsigned char pos_4b;	/* pos 4 (if pos 6 = 1) */
-	unsigned char pos_3b;	/* pos 3 (if pos 6 = 1) */
-	unsigned char pos_6;	/* pos 6 */
-	unsigned char pos_5;	/* pos 5 */
-	unsigned short max_overlap;	/* maximum overlapping requests */
-	unsigned short num_bus;	/* number of SCSI-busses */
-};
-
-/*values for SCB command word */
-#define IM_NO_SYNCHRONOUS      0x0040	/*flag for any command */
-#define IM_NO_DISCONNECT       0x0080	/*flag for any command */
-#define IM_READ_DATA_CMD       0x1c01
-#define IM_WRITE_DATA_CMD      0x1c02
-#define IM_READ_VERIFY_CMD     0x1c03
-#define IM_WRITE_VERIFY_CMD    0x1c04
-#define IM_REQUEST_SENSE_CMD   0x1c08
-#define IM_READ_CAPACITY_CMD   0x1c09
-#define IM_DEVICE_INQUIRY_CMD  0x1c0b
-#define IM_READ_LOGICAL_CMD    0x1c2a
-#define IM_OTHER_SCSI_CMD_CMD  0x241f
-
-/* unused, but supported, SCB commands */
-#define IM_GET_COMMAND_COMPLETE_STATUS_CMD   0x1c07	/* command status */
-#define IM_GET_POS_INFO_CMD                  0x1c0a	/* returns neat stuff */
-#define IM_READ_PREFETCH_CMD                 0x1c31	/* caching controller only */
-#define IM_FOMAT_UNIT_CMD                    0x1c16	/* format unit */
-#define IM_REASSIGN_BLOCK_CMD                0x1c18	/* in case of error */
-
-/*values to set bits in the enable word of SCB */
-#define IM_READ_CONTROL              0x8000
-#define IM_REPORT_TSB_ONLY_ON_ERROR  0x4000
-#define IM_RETRY_ENABLE              0x2000
-#define IM_POINTER_TO_LIST           0x1000
-#define IM_SUPRESS_EXCEPTION_SHORT   0x0400
-#define IM_BYPASS_BUFFER             0x0200
-#define IM_CHAIN_ON_NO_ERROR         0x0001
-
-/*TSB (Termination Status Block) structure */
-struct im_tsb {
-	unsigned short end_status;
-	unsigned short reserved1;
-	unsigned long residual_byte_count;
-	unsigned long sg_list_element_adr;
-	unsigned short status_length;
-	unsigned char dev_status;
-	unsigned char cmd_status;
-	unsigned char dev_error;
-	unsigned char cmd_error;
-	unsigned short reserved2;
-	unsigned short reserved3;
-	unsigned short low_of_last_scb_adr;
-	unsigned short high_of_last_scb_adr;
-};
-
-/*subsystem uses interrupt request level 14 */
-#define IM_IRQ     14
-/*SCSI-2 F/W may evade to interrupt 11 */
-#define IM_IRQ_FW  11
-
-/* Model 95 has an additional alphanumeric display, which can be used
-   to display SCSI-activities. 8595 models do not have any disk led, which
-   makes this feature quite useful.
-   The regular PS/2 disk led is turned on/off by bits 6,7 of system
-   control port. */
-
-/* LED display-port (actually, last LED on display) */
-#define MOD95_LED_PORT	   0x108
-/* system-control-register of PS/2s with diskindicator */
-#define PS2_SYS_CTR        0x92
-/* activity displaying methods */
-#define LED_DISP           1
-#define LED_ADISP          2
-#define LED_ACTIVITY       4
-/* failed intr */
-#define CMD_FAIL           255
-
-/* The SCSI-ID(!) of the accessed SCSI-device is shown on PS/2-95 machines' LED
-   displays. ldn is no longer displayed here, because the ldn mapping is now 
-   done dynamically and the ldn <-> pun,lun maps can be looked-up at boottime 
-   or during uptime in /proc/scsi/ibmmca/<host_no> in case of trouble, 
-   interest, debugging or just for having fun. The left number gives the
-   host-adapter number and the right shows the accessed SCSI-ID. */
-
-/* display_mode is set by the ibmmcascsi= command line arg */
-static int display_mode = 0;
-/* set default adapter timeout */
-static unsigned int adapter_timeout = 45;
-/* for probing on feature-command: */
-static unsigned int global_command_error_excuse = 0;
-/* global setting by command line for adapter_speed */
-static int global_adapter_speed = 0;	/* full speed by default */
-
-/* Panel / LED on, do it right for F/W addressin, too. adisplay will
- * just ignore ids>7, as the panel has only 7 digits available */
-#define PS2_DISK_LED_ON(ad,id) { if (display_mode & LED_DISP) { if (id>9) \
-    outw((ad+48)|((id+55)<<8), MOD95_LED_PORT ); else \
-    outw((ad+48)|((id+48)<<8), MOD95_LED_PORT ); } else \
-    if (display_mode & LED_ADISP) { if (id<7) outb((char)(id+48),MOD95_LED_PORT+1+id); \
-    outb((char)(ad+48), MOD95_LED_PORT); } \
-    if ((display_mode & LED_ACTIVITY)||(!display_mode)) \
-    outb(inb(PS2_SYS_CTR) | 0xc0, PS2_SYS_CTR); }
-
-/* Panel / LED off */
-/* bug fixed, Dec 15, 1997, where | was replaced by & here */
-#define PS2_DISK_LED_OFF() { if (display_mode & LED_DISP) \
-    outw(0x2020, MOD95_LED_PORT ); else if (display_mode & LED_ADISP) { \
-    outl(0x20202020,MOD95_LED_PORT); outl(0x20202020,MOD95_LED_PORT+4); } \
-    if ((display_mode & LED_ACTIVITY)||(!display_mode)) \
-    outb(inb(PS2_SYS_CTR) & 0x3f, PS2_SYS_CTR); }
-
-/* types of different supported hardware that goes to hostdata special */
-#define IBM_SCSI2_FW     0
-#define IBM_7568_WCACHE  1
-#define IBM_EXP_UNIT     2
-#define IBM_SCSI_WCACHE  3
-#define IBM_SCSI         4
-#define IBM_INTEGSCSI	 5
-
-/* other special flags for hostdata structure */
-#define FORCED_DETECTION         100
-#define INTEGRATED_SCSI          101
-
-/* List of possible IBM-SCSI-adapters */
-static short ibmmca_id_table[] = {
-	0x8efc,
-	0x8efd,
-	0x8ef8,
-	0x8eff,
-	0x8efe,
-	/* No entry for integrated SCSI, that's part of the register */
-	0
-};
-
-static const char *ibmmca_description[] = {
-	"IBM SCSI-2 F/W Adapter",	/* special = 0 */
-	"IBM 7568 Industrial Computer SCSI Adapter w/Cache",	/* special = 1 */
-	"IBM Expansion Unit SCSI Controller",	/* special = 2 */
-	"IBM SCSI Adapter w/Cache",	/* special = 3 */
-	"IBM SCSI Adapter",	/* special = 4 */
-	"IBM Integrated SCSI Controller", /* special = 5 */
-};
-
-/* Max number of logical devices (can be up from 0 to 14).  15 is the address
-of the adapter itself. */
-#define MAX_LOG_DEV  15
-
-/*local data for a logical device */
-struct logical_device {
-	struct im_scb scb;	/* SCSI-subsystem-control-block structure */
-	struct im_tsb tsb;	/* SCSI command complete status block structure */
-	struct im_sge sge[16];	/* scatter gather list structure */
-	unsigned char buf[256];	/* SCSI command return data buffer */
-	Scsi_Cmnd *cmd;		/* SCSI-command that is currently in progress */
-	int device_type;	/* type of the SCSI-device. See include/scsi/scsi.h
-				   for interpretation of the possible values */
-	int block_length;	/* blocksize of a particular logical SCSI-device */
-	int cache_flag;		/* 1 if this is uncached, 0 if cache is present for ldn */
-	int retry_flag;		/* 1 if adapter retry is disabled, 0 if enabled */
-};
-
-/* statistics of the driver during operations (for proc_info) */
-struct Driver_Statistics {
-	/* SCSI statistics on the adapter */
-	int ldn_access[MAX_LOG_DEV + 1];	/* total accesses on a ldn */
-	int ldn_read_access[MAX_LOG_DEV + 1];	/* total read-access on a ldn */
-	int ldn_write_access[MAX_LOG_DEV + 1];	/* total write-access on a ldn */
-	int ldn_inquiry_access[MAX_LOG_DEV + 1];	/* total inquiries on a ldn */
-	int ldn_modeselect_access[MAX_LOG_DEV + 1];	/* total mode selects on ldn */
-	int scbs;		/* short SCBs queued */
-	int long_scbs;		/* long SCBs queued */
-	int total_accesses;	/* total accesses on all ldns */
-	int total_interrupts;	/* total interrupts (should be
-				   same as total_accesses) */
-	int total_errors;	/* command completed with error */
-	/* dynamical assignment statistics */
-	int total_scsi_devices;	/* number of physical pun,lun */
-	int dyn_flag;		/* flag showing dynamical mode */
-	int dynamical_assignments;	/* number of remappings of ldns */
-	int ldn_assignments[MAX_LOG_DEV + 1];	/* number of remappings of each
-						   ldn */
-};
-
-/* data structure for each host adapter */
-struct ibmmca_hostdata {
-	/* array of logical devices: */
-	struct logical_device _ld[MAX_LOG_DEV + 1];
-	/* array to convert (pun, lun) into logical device number: */
-	unsigned char _get_ldn[16][8];
-	/*array that contains the information about the physical SCSI-devices
-	   attached to this host adapter: */
-	unsigned char _get_scsi[16][8];
-	/* used only when checking logical devices: */
-	int _local_checking_phase_flag;
-	/* report received interrupt: */
-	int _got_interrupt;
-	/* report termination-status of SCSI-command: */
-	int _stat_result;
-	/* reset status (used only when doing reset): */
-	int _reset_status;
-	/* code of the last SCSI command (needed for panic info): */
-	int _last_scsi_command[MAX_LOG_DEV + 1];
-	/* identifier of the last SCSI-command type */
-	int _last_scsi_type[MAX_LOG_DEV + 1];
-	/* last blockcount */
-	int _last_scsi_blockcount[MAX_LOG_DEV + 1];
-	/* last locgical block address */
-	unsigned long _last_scsi_logical_block[MAX_LOG_DEV + 1];
-	/* Counter that points on the next reassignable ldn for dynamical
-	   remapping. The default value is 7, that is the first reassignable
-	   number in the list at boottime: */
-	int _next_ldn;
-	/* Statistics-structure for this IBM-SCSI-host: */
-	struct Driver_Statistics _IBM_DS;
-	/* This hostadapters pos-registers pos2 until pos6 */
-	unsigned int _pos[8];
-	/* assign a special variable, that contains dedicated info about the
-	   adaptertype */
-	int _special;
-	/* connector size on the MCA bus */
-	int _connector_size;
-	/* synchronous SCSI transfer rate bitpattern */
-	int _adapter_speed;
-};
-
-/* macros to access host data structure */
-#define subsystem_pun(h) ((h)->this_id)
-#define subsystem_maxid(h) ((h)->max_id)
-#define ld(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_ld)
-#define get_ldn(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_get_ldn)
-#define get_scsi(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_get_scsi)
-#define local_checking_phase_flag(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_local_checking_phase_flag)
-#define got_interrupt(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_got_interrupt)
-#define stat_result(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_stat_result)
-#define reset_status(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_reset_status)
-#define last_scsi_command(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_last_scsi_command)
-#define last_scsi_type(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_last_scsi_type)
-#define last_scsi_blockcount(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_last_scsi_blockcount)
-#define last_scsi_logical_block(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_last_scsi_logical_block)
-#define last_scsi_type(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_last_scsi_type)
-#define next_ldn(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_next_ldn)
-#define IBM_DS(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_IBM_DS)
-#define special(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_special)
-#define subsystem_connector_size(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_connector_size)
-#define adapter_speed(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_adapter_speed)
-#define pos2(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_pos[2])
-#define pos3(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_pos[3])
-#define pos4(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_pos[4])
-#define pos5(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_pos[5])
-#define pos6(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_pos[6])
-
-/* Define a arbitrary number as subsystem-marker-type. This number is, as
-   described in the ANSI-SCSI-standard, not occupied by other device-types. */
-#define TYPE_IBM_SCSI_ADAPTER   0x2F
-
-/* Define 0xFF for no device type, because this type is not defined within
-   the ANSI-SCSI-standard, therefore, it can be used and should not cause any
-   harm. */
-#define TYPE_NO_DEVICE          0xFF
-
-/* define medium-changer. If this is not defined previously, e.g. Linux
-   2.0.x, define this type here. */
-#ifndef TYPE_MEDIUM_CHANGER
-#define TYPE_MEDIUM_CHANGER     0x08
-#endif
-
-/* define possible operations for the immediate_assign command */
-#define SET_LDN        0
-#define REMOVE_LDN     1
-
-/* ldn which is used to probe the SCSI devices */
-#define PROBE_LDN      0
-
-/* reset status flag contents */
-#define IM_RESET_NOT_IN_PROGRESS         0
-#define IM_RESET_IN_PROGRESS             1
-#define IM_RESET_FINISHED_OK             2
-#define IM_RESET_FINISHED_FAIL           3
-#define IM_RESET_NOT_IN_PROGRESS_NO_INT  4
-#define IM_RESET_FINISHED_OK_NO_INT      5
-
-/* define undefined SCSI-command */
-#define NO_SCSI                  0xffff
-
-/*-----------------------------------------------------------------------*/
-
-/* if this is nonzero, ibmmcascsi option has been passed to the kernel */
-static int io_port[IM_MAX_HOSTS] = { 0, 0, 0, 0, 0, 0, 0, 0 };
-static int scsi_id[IM_MAX_HOSTS] = { 7, 7, 7, 7, 7, 7, 7, 7 };
-
-/* fill module-parameters only, when this define is present.
-   (that is kernel version 2.1.x) */
-#if defined(MODULE)
-static char *boot_options = NULL;
-module_param(boot_options, charp, 0);
-module_param_array(io_port, int, NULL, 0);
-module_param_array(scsi_id, int, NULL, 0);
-
-MODULE_LICENSE("GPL");
-#endif
-/*counter of concurrent disk read/writes, to turn on/off disk led */
-static int disk_rw_in_progress = 0;
-
-static unsigned int pos[8];	/* whole pos register-line for diagnosis */
-/* Taking into account the additions, made by ZP Gu.
- * This selects now the preset value from the configfile and
- * offers the 'normal' commandline option to be accepted */
-#ifdef CONFIG_IBMMCA_SCSI_ORDER_STANDARD
-static char ibm_ansi_order = 1;
-#else
-static char ibm_ansi_order = 0;
-#endif
-
-static void issue_cmd(struct Scsi_Host *, unsigned long, unsigned char);
-static void internal_done(Scsi_Cmnd * cmd);
-static void check_devices(struct Scsi_Host *, int);
-static int immediate_assign(struct Scsi_Host *, unsigned int, unsigned int, unsigned int, unsigned int);
-static int immediate_feature(struct Scsi_Host *, unsigned int, unsigned int);
-#ifdef CONFIG_IBMMCA_SCSI_DEV_RESET
-static int immediate_reset(struct Scsi_Host *, unsigned int);
-#endif
-static int device_inquiry(struct Scsi_Host *, int);
-static int read_capacity(struct Scsi_Host *, int);
-static int get_pos_info(struct Scsi_Host *);
-static char *ti_p(int);
-static char *ti_l(int);
-static char *ibmrate(unsigned int, int);
-static int probe_display(int);
-static int probe_bus_mode(struct Scsi_Host *);
-static int device_exists(struct Scsi_Host *, int, int *, int *);
-static int option_setup(char *);
-/* local functions needed for proc_info */
-static int ldn_access_load(struct Scsi_Host *, int);
-static int ldn_access_total_read_write(struct Scsi_Host *);
-
-static irqreturn_t interrupt_handler(int irq, void *dev_id)
-{
-	unsigned int intr_reg;
-	unsigned int cmd_result;
-	unsigned int ldn;
-	unsigned long flags;
-	Scsi_Cmnd *cmd;
-	int lastSCSI;
-	struct device *dev = dev_id;
-	struct Scsi_Host *shpnt = dev_get_drvdata(dev);
-
-	spin_lock_irqsave(shpnt->host_lock, flags);
-
-	if(!(inb(IM_STAT_REG(shpnt)) & IM_INTR_REQUEST)) {
-		spin_unlock_irqrestore(shpnt->host_lock, flags);
-		return IRQ_NONE;
-	}
-
-	/* the reset-function already did all the job, even ints got
-	   renabled on the subsystem, so just return */
-	if ((reset_status(shpnt) == IM_RESET_NOT_IN_PROGRESS_NO_INT) || (reset_status(shpnt) == IM_RESET_FINISHED_OK_NO_INT)) {
-		reset_status(shpnt) = IM_RESET_NOT_IN_PROGRESS;
-		spin_unlock_irqrestore(shpnt->host_lock, flags);
-		return IRQ_HANDLED;
-	}
-
-	/*must wait for attention reg not busy, then send EOI to subsystem */
-	while (1) {
-		if (!(inb(IM_STAT_REG(shpnt)) & IM_BUSY))
-			break;
-		cpu_relax();
-	}
-
-	/*get command result and logical device */
-	intr_reg = (unsigned char) (inb(IM_INTR_REG(shpnt)));
-	cmd_result = intr_reg & 0xf0;
-	ldn = intr_reg & 0x0f;
-	/* get the last_scsi_command here */
-	lastSCSI = last_scsi_command(shpnt)[ldn];
-	outb(IM_EOI | ldn, IM_ATTN_REG(shpnt));
-	
-	/*these should never happen (hw fails, or a local programming bug) */
-	if (!global_command_error_excuse) {
-		switch (cmd_result) {
-			/* Prevent from Ooopsing on error to show the real reason */
-		case IM_ADAPTER_HW_FAILURE:
-		case IM_SOFTWARE_SEQUENCING_ERROR:
-		case IM_CMD_ERROR:
-			printk(KERN_ERR "IBM MCA SCSI: Fatal Subsystem ERROR!\n");
-			printk(KERN_ERR "              Last cmd=0x%x, ena=%x, len=", lastSCSI, ld(shpnt)[ldn].scb.enable);
-			if (ld(shpnt)[ldn].cmd)
-				printk("%ld/%ld,", (long) (scsi_bufflen(ld(shpnt)[ldn].cmd)), (long) (ld(shpnt)[ldn].scb.sys_buf_length));
-			else
-				printk("none,");
-			if (ld(shpnt)[ldn].cmd)
-				printk("Blocksize=%d", ld(shpnt)[ldn].scb.u2.blk.length);
-			else
-				printk("Blocksize=none");
-			printk(", host=%p, ldn=0x%x\n", shpnt, ldn);
-			if (ld(shpnt)[ldn].cmd) {
-				printk(KERN_ERR "Blockcount=%d/%d\n", last_scsi_blockcount(shpnt)[ldn], ld(shpnt)[ldn].scb.u2.blk.count);
-				printk(KERN_ERR "Logical block=%lx/%lx\n", last_scsi_logical_block(shpnt)[ldn], ld(shpnt)[ldn].scb.u1.log_blk_adr);
-			}
-			printk(KERN_ERR "Reason given: %s\n", (cmd_result == IM_ADAPTER_HW_FAILURE) ? "HARDWARE FAILURE" : (cmd_result == IM_SOFTWARE_SEQUENCING_ERROR) ? "SOFTWARE SEQUENCING ERROR" : (cmd_result == IM_CMD_ERROR) ? "COMMAND ERROR" : "UNKNOWN");
-			/* if errors appear, enter this section to give detailed info */
-			printk(KERN_ERR "IBM MCA SCSI: Subsystem Error-Status follows:\n");
-			printk(KERN_ERR "              Command Type................: %x\n", last_scsi_type(shpnt)[ldn]);
-			printk(KERN_ERR "              Attention Register..........: %x\n", inb(IM_ATTN_REG(shpnt)));
-			printk(KERN_ERR "              Basic Control Register......: %x\n", inb(IM_CTR_REG(shpnt)));
-			printk(KERN_ERR "              Interrupt Status Register...: %x\n", intr_reg);
-			printk(KERN_ERR "              Basic Status Register.......: %x\n", inb(IM_STAT_REG(shpnt)));
-			if ((last_scsi_type(shpnt)[ldn] == IM_SCB) || (last_scsi_type(shpnt)[ldn] == IM_LONG_SCB)) {
-				printk(KERN_ERR "              SCB-Command.................: %x\n", ld(shpnt)[ldn].scb.command);
-				printk(KERN_ERR "              SCB-Enable..................: %x\n", ld(shpnt)[ldn].scb.enable);
-				printk(KERN_ERR "              SCB-logical block address...: %lx\n", ld(shpnt)[ldn].scb.u1.log_blk_adr);
-				printk(KERN_ERR "              SCB-system buffer address...: %lx\n", ld(shpnt)[ldn].scb.sys_buf_adr);
-				printk(KERN_ERR "              SCB-system buffer length....: %lx\n", ld(shpnt)[ldn].scb.sys_buf_length);
-				printk(KERN_ERR "              SCB-tsb address.............: %lx\n", ld(shpnt)[ldn].scb.tsb_adr);
-				printk(KERN_ERR "              SCB-Chain address...........: %lx\n", ld(shpnt)[ldn].scb.scb_chain_adr);
-				printk(KERN_ERR "              SCB-block count.............: %x\n", ld(shpnt)[ldn].scb.u2.blk.count);
-				printk(KERN_ERR "              SCB-block length............: %x\n", ld(shpnt)[ldn].scb.u2.blk.length);
-			}
-			printk(KERN_ERR "              Send this report to the maintainer.\n");
-			panic("IBM MCA SCSI: Fatal error message from the subsystem (0x%X,0x%X)!\n", lastSCSI, cmd_result);
-			break;
-		}
-	} else {
-		/* The command error handling is made silent, but we tell the
-		 * calling function, that there is a reported error from the
-		 * adapter. */
-		switch (cmd_result) {
-		case IM_ADAPTER_HW_FAILURE:
-		case IM_SOFTWARE_SEQUENCING_ERROR:
-		case IM_CMD_ERROR:
-			global_command_error_excuse = CMD_FAIL;
-			break;
-		default:
-			global_command_error_excuse = 0;
-			break;
-		}
-	}
-	/* if no panic appeared, increase the interrupt-counter */
-	IBM_DS(shpnt).total_interrupts++;
-	/*only for local checking phase */
-	if (local_checking_phase_flag(shpnt)) {
-		stat_result(shpnt) = cmd_result;
-		got_interrupt(shpnt) = 1;
-		reset_status(shpnt) = IM_RESET_FINISHED_OK;
-		last_scsi_command(shpnt)[ldn] = NO_SCSI;
-		spin_unlock_irqrestore(shpnt->host_lock, flags);
-		return IRQ_HANDLED;
-	}
-	/* handling of commands coming from upper level of scsi driver */
-	if (last_scsi_type(shpnt)[ldn] == IM_IMM_CMD) {
-		/* verify ldn, and may handle rare reset immediate command */
-		if ((reset_status(shpnt) == IM_RESET_IN_PROGRESS) && (last_scsi_command(shpnt)[ldn] == IM_RESET_IMM_CMD)) {
-			if (cmd_result == IM_CMD_COMPLETED_WITH_FAILURE) {
-				disk_rw_in_progress = 0;
-				PS2_DISK_LED_OFF();
-				reset_status(shpnt) = IM_RESET_FINISHED_FAIL;
-			} else {
-				/*reset disk led counter, turn off disk led */
-				disk_rw_in_progress = 0;
-				PS2_DISK_LED_OFF();
-				reset_status(shpnt) = IM_RESET_FINISHED_OK;
-			}
-			stat_result(shpnt) = cmd_result;
-			last_scsi_command(shpnt)[ldn] = NO_SCSI;
-			last_scsi_type(shpnt)[ldn] = 0;
-			spin_unlock_irqrestore(shpnt->host_lock, flags);
-			return IRQ_HANDLED;
-		} else if (last_scsi_command(shpnt)[ldn] == IM_ABORT_IMM_CMD) {
-			/* react on SCSI abort command */
-#ifdef IM_DEBUG_PROBE
-			printk("IBM MCA SCSI: Interrupt from SCSI-abort.\n");
-#endif
-			disk_rw_in_progress = 0;
-			PS2_DISK_LED_OFF();
-			cmd = ld(shpnt)[ldn].cmd;
-			ld(shpnt)[ldn].cmd = NULL;
-			if (cmd_result == IM_CMD_COMPLETED_WITH_FAILURE)
-				cmd->result = DID_NO_CONNECT << 16;
-			else
-				cmd->result = DID_ABORT << 16;
-			stat_result(shpnt) = cmd_result;
-			last_scsi_command(shpnt)[ldn] = NO_SCSI;
-			last_scsi_type(shpnt)[ldn] = 0;
-			if (cmd->scsi_done)
-				(cmd->scsi_done) (cmd);	/* should be the internal_done */
-			spin_unlock_irqrestore(shpnt->host_lock, flags);
-			return IRQ_HANDLED;
-		} else {
-			disk_rw_in_progress = 0;
-			PS2_DISK_LED_OFF();
-			reset_status(shpnt) = IM_RESET_FINISHED_OK;
-			stat_result(shpnt) = cmd_result;
-			last_scsi_command(shpnt)[ldn] = NO_SCSI;
-			spin_unlock_irqrestore(shpnt->host_lock, flags);
-			return IRQ_HANDLED;
-		}
-	}
-	last_scsi_command(shpnt)[ldn] = NO_SCSI;
-	last_scsi_type(shpnt)[ldn] = 0;
-	cmd = ld(shpnt)[ldn].cmd;
-	ld(shpnt)[ldn].cmd = NULL;
-#ifdef IM_DEBUG_TIMEOUT
-	if (cmd) {
-		if ((cmd->target == TIMEOUT_PUN) && (cmd->device->lun == TIMEOUT_LUN)) {
-			spin_unlock_irqsave(shpnt->host_lock, flags);
-			printk("IBM MCA SCSI: Ignoring interrupt from pun=%x, lun=%x.\n", cmd->target, cmd->device->lun);
-			return IRQ_HANDLED;
-		}
-	}
-#endif
-	/*if no command structure, just return, else clear cmd */
-	if (!cmd)
-	{
-		spin_unlock_irqrestore(shpnt->host_lock, flags);
-		return IRQ_HANDLED;
-	}
-
-#ifdef IM_DEBUG_INT
-	printk("cmd=%02x ireg=%02x ds=%02x cs=%02x de=%02x ce=%02x\n", cmd->cmnd[0], intr_reg, ld(shpnt)[ldn].tsb.dev_status, ld(shpnt)[ldn].tsb.cmd_status, ld(shpnt)[ldn].tsb.dev_error, ld(shpnt)[ldn].tsb.cmd_error);
-#endif
-	/*if this is end of media read/write, may turn off PS/2 disk led */
-	if ((ld(shpnt)[ldn].device_type != TYPE_NO_LUN) && (ld(shpnt)[ldn].device_type != TYPE_NO_DEVICE)) {
-		/* only access this, if there was a valid device addressed */
-		if (--disk_rw_in_progress == 0)
-			PS2_DISK_LED_OFF();
-	}
-
-	/* IBM describes the status-mask to be 0x1e, but this is not conform
-	 * with SCSI-definition, I suppose, the reason for it is that IBM
-	 * adapters do not support CMD_TERMINATED, TASK_SET_FULL and
-	 * ACA_ACTIVE as returning statusbyte information. (ML) */
-	if (cmd_result == IM_CMD_COMPLETED_WITH_FAILURE) {
-		cmd->result = (unsigned char) (ld(shpnt)[ldn].tsb.dev_status & 0x1e);
-		IBM_DS(shpnt).total_errors++;
-	} else
-		cmd->result = 0;
-	/* write device status into cmd->result, and call done function */
-	if (lastSCSI == NO_SCSI) {	/* unexpected interrupt :-( */
-		cmd->result |= DID_BAD_INTR << 16;
-		printk("IBM MCA SCSI: WARNING - Interrupt from non-pending SCSI-command!\n");
-	} else			/* things went right :-) */
-		cmd->result |= DID_OK << 16;
-	if (cmd->scsi_done)
-		(cmd->scsi_done) (cmd);
-	spin_unlock_irqrestore(shpnt->host_lock, flags);
-	return IRQ_HANDLED;
-}
-
-static void issue_cmd(struct Scsi_Host *shpnt, unsigned long cmd_reg,
-		      unsigned char attn_reg)
-{
-	unsigned long flags;
-	/* must wait for attention reg not busy */
-	while (1) {
-		spin_lock_irqsave(shpnt->host_lock, flags);
-		if (!(inb(IM_STAT_REG(shpnt)) & IM_BUSY))
-			break;
-		spin_unlock_irqrestore(shpnt->host_lock, flags);
-	}
-	/* write registers and enable system interrupts */
-	outl(cmd_reg, IM_CMD_REG(shpnt));
-	outb(attn_reg, IM_ATTN_REG(shpnt));
-	spin_unlock_irqrestore(shpnt->host_lock, flags);
-}
-
-static void internal_done(Scsi_Cmnd * cmd)
-{
-	cmd->SCp.Status++;
-	return;
-}
-
-/* SCSI-SCB-command for device_inquiry */
-static int device_inquiry(struct Scsi_Host *shpnt, int ldn)
-{
-	int retr;
-	struct im_scb *scb;
-	struct im_tsb *tsb;
-	unsigned char *buf;
-
-	scb = &(ld(shpnt)[ldn].scb);
-	tsb = &(ld(shpnt)[ldn].tsb);
-	buf = (unsigned char *) (&(ld(shpnt)[ldn].buf));
-	ld(shpnt)[ldn].tsb.dev_status = 0;	/* prepare statusblock */
-	for (retr = 0; retr < 3; retr++) {
-		/* fill scb with inquiry command */
-		scb->command = IM_DEVICE_INQUIRY_CMD | IM_NO_DISCONNECT;
-		scb->enable = IM_REPORT_TSB_ONLY_ON_ERROR | IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT | IM_RETRY_ENABLE | IM_BYPASS_BUFFER;
-		last_scsi_command(shpnt)[ldn] = IM_DEVICE_INQUIRY_CMD;
-		last_scsi_type(shpnt)[ldn] = IM_SCB;
-		scb->sys_buf_adr = isa_virt_to_bus(buf);
-		scb->sys_buf_length = 255;	/* maximum bufferlength gives max info */
-		scb->tsb_adr = isa_virt_to_bus(tsb);
-		/* issue scb to passed ldn, and busy wait for interrupt */
-		got_interrupt(shpnt) = 0;
-		issue_cmd(shpnt, isa_virt_to_bus(scb), IM_SCB | ldn);
-		while (!got_interrupt(shpnt))
-			barrier();
-
-		/*if command successful, break */
-		if ((stat_result(shpnt) == IM_SCB_CMD_COMPLETED) || (stat_result(shpnt) == IM_SCB_CMD_COMPLETED_WITH_RETRIES))
-			return 1;
-	}
-	/*if all three retries failed, return "no device at this ldn" */
-	if (retr >= 3)
-		return 0;
-	else
-		return 1;
-}
-
-static int read_capacity(struct Scsi_Host *shpnt, int ldn)
-{
-	int retr;
-	struct im_scb *scb;
-	struct im_tsb *tsb;
-	unsigned char *buf;
-
-	scb = &(ld(shpnt)[ldn].scb);
-	tsb = &(ld(shpnt)[ldn].tsb);
-	buf = (unsigned char *) (&(ld(shpnt)[ldn].buf));
-	ld(shpnt)[ldn].tsb.dev_status = 0;
-	for (retr = 0; retr < 3; retr++) {
-		/*fill scb with read capacity command */
-		scb->command = IM_READ_CAPACITY_CMD;
-		scb->enable = IM_REPORT_TSB_ONLY_ON_ERROR | IM_READ_CONTROL | IM_RETRY_ENABLE | IM_BYPASS_BUFFER;
-		last_scsi_command(shpnt)[ldn] = IM_READ_CAPACITY_CMD;
-		last_scsi_type(shpnt)[ldn] = IM_SCB;
-		scb->sys_buf_adr = isa_virt_to_bus(buf);
-		scb->sys_buf_length = 8;
-		scb->tsb_adr = isa_virt_to_bus(tsb);
-		/*issue scb to passed ldn, and busy wait for interrupt */
-		got_interrupt(shpnt) = 0;
-		issue_cmd(shpnt, isa_virt_to_bus(scb), IM_SCB | ldn);
-		while (!got_interrupt(shpnt))
-			barrier();
-
-		/*if got capacity, get block length and return one device found */
-		if ((stat_result(shpnt) == IM_SCB_CMD_COMPLETED) || (stat_result(shpnt) == IM_SCB_CMD_COMPLETED_WITH_RETRIES))
-			return 1;
-	}
-	/*if all three retries failed, return "no device at this ldn" */
-	if (retr >= 3)
-		return 0;
-	else
-		return 1;
-}
-
-static int get_pos_info(struct Scsi_Host *shpnt)
-{
-	int retr;
-	struct im_scb *scb;
-	struct im_tsb *tsb;
-	unsigned char *buf;
-
-	scb = &(ld(shpnt)[MAX_LOG_DEV].scb);
-	tsb = &(ld(shpnt)[MAX_LOG_DEV].tsb);
-	buf = (unsigned char *) (&(ld(shpnt)[MAX_LOG_DEV].buf));
-	ld(shpnt)[MAX_LOG_DEV].tsb.dev_status = 0;
-	for (retr = 0; retr < 3; retr++) {
-		/*fill scb with get_pos_info command */
-		scb->command = IM_GET_POS_INFO_CMD;
-		scb->enable = IM_READ_CONTROL | IM_REPORT_TSB_ONLY_ON_ERROR | IM_RETRY_ENABLE | IM_BYPASS_BUFFER;
-		last_scsi_command(shpnt)[MAX_LOG_DEV] = IM_GET_POS_INFO_CMD;
-		last_scsi_type(shpnt)[MAX_LOG_DEV] = IM_SCB;
-		scb->sys_buf_adr = isa_virt_to_bus(buf);
-		if (special(shpnt) == IBM_SCSI2_FW)
-			scb->sys_buf_length = 256;	/* get all info from F/W adapter */
-		else
-			scb->sys_buf_length = 18;	/* get exactly 18 bytes for other SCSI */
-		scb->tsb_adr = isa_virt_to_bus(tsb);
-		/*issue scb to ldn=15, and busy wait for interrupt */
-		got_interrupt(shpnt) = 0;
-		issue_cmd(shpnt, isa_virt_to_bus(scb), IM_SCB | MAX_LOG_DEV);
-		
-		/* FIXME: timeout */
-		while (!got_interrupt(shpnt))
-			barrier();
-
-		/*if got POS-stuff, get block length and return one device found */
-		if ((stat_result(shpnt) == IM_SCB_CMD_COMPLETED) || (stat_result(shpnt) == IM_SCB_CMD_COMPLETED_WITH_RETRIES))
-			return 1;
-	}
-	/* if all three retries failed, return "no device at this ldn" */
-	if (retr >= 3)
-		return 0;
-	else
-		return 1;
-}
-
-/* SCSI-immediate-command for assign. This functions maps/unmaps specific
- ldn-numbers on SCSI (PUN,LUN). It is needed for presetting of the
- subsystem and for dynamical remapping od ldns. */
-static int immediate_assign(struct Scsi_Host *shpnt, unsigned int pun,
-			    unsigned int lun, unsigned int ldn,
-			    unsigned int operation)
-{
-	int retr;
-	unsigned long imm_cmd;
-
-	for (retr = 0; retr < 3; retr++) {
-		/* select mutation level of the SCSI-adapter */
-		switch (special(shpnt)) {
-		case IBM_SCSI2_FW:
-			imm_cmd = (unsigned long) (IM_ASSIGN_IMM_CMD);
-			imm_cmd |= (unsigned long) ((lun & 7) << 24);
-			imm_cmd |= (unsigned long) ((operation & 1) << 23);
-			imm_cmd |= (unsigned long) ((pun & 7) << 20) | ((pun & 8) << 24);
-			imm_cmd |= (unsigned long) ((ldn & 15) << 16);
-			break;
-		default:
-			imm_cmd = inl(IM_CMD_REG(shpnt));
-			imm_cmd &= (unsigned long) (0xF8000000);	/* keep reserved bits */
-			imm_cmd |= (unsigned long) (IM_ASSIGN_IMM_CMD);
-			imm_cmd |= (unsigned long) ((lun & 7) << 24);
-			imm_cmd |= (unsigned long) ((operation & 1) << 23);
-			imm_cmd |= (unsigned long) ((pun & 7) << 20);
-			imm_cmd |= (unsigned long) ((ldn & 15) << 16);
-			break;
-		}
-		last_scsi_command(shpnt)[MAX_LOG_DEV] = IM_ASSIGN_IMM_CMD;
-		last_scsi_type(shpnt)[MAX_LOG_DEV] = IM_IMM_CMD;
-		got_interrupt(shpnt) = 0;
-		issue_cmd(shpnt, (unsigned long) (imm_cmd), IM_IMM_CMD | MAX_LOG_DEV);
-		while (!got_interrupt(shpnt))
-			barrier();
-
-		/*if command successful, break */
-		if (stat_result(shpnt) == IM_IMMEDIATE_CMD_COMPLETED)
-			return 1;
-	}
-	if (retr >= 3)
-		return 0;
-	else
-		return 1;
-}
-
-static int immediate_feature(struct Scsi_Host *shpnt, unsigned int speed, unsigned int timeout)
-{
-	int retr;
-	unsigned long imm_cmd;
-
-	for (retr = 0; retr < 3; retr++) {
-		/* select mutation level of the SCSI-adapter */
-		imm_cmd = IM_FEATURE_CTR_IMM_CMD;
-		imm_cmd |= (unsigned long) ((speed & 0x7) << 29);
-		imm_cmd |= (unsigned long) ((timeout & 0x1fff) << 16);
-		last_scsi_command(shpnt)[MAX_LOG_DEV] = IM_FEATURE_CTR_IMM_CMD;
-		last_scsi_type(shpnt)[MAX_LOG_DEV] = IM_IMM_CMD;
-		got_interrupt(shpnt) = 0;
-		/* we need to run into command errors in order to probe for the
-		 * right speed! */
-		global_command_error_excuse = 1;
-		issue_cmd(shpnt, (unsigned long) (imm_cmd), IM_IMM_CMD | MAX_LOG_DEV);
-		
-		/* FIXME: timeout */
-		while (!got_interrupt(shpnt))
-			barrier();
-		if (global_command_error_excuse == CMD_FAIL) {
-			global_command_error_excuse = 0;
-			return 2;
-		} else
-			global_command_error_excuse = 0;
-		/*if command successful, break */
-		if (stat_result(shpnt) == IM_IMMEDIATE_CMD_COMPLETED)
-			return 1;
-	}
-	if (retr >= 3)
-		return 0;
-	else
-		return 1;
-}
-
-#ifdef CONFIG_IBMMCA_SCSI_DEV_RESET
-static int immediate_reset(struct Scsi_Host *shpnt, unsigned int ldn)
-{
-	int retries;
-	int ticks;
-	unsigned long imm_command;
-
-	for (retries = 0; retries < 3; retries++) {
-		imm_command = inl(IM_CMD_REG(shpnt));
-		imm_command &= (unsigned long) (0xFFFF0000);	/* keep reserved bits */
-		imm_command |= (unsigned long) (IM_RESET_IMM_CMD);
-		last_scsi_command(shpnt)[ldn] = IM_RESET_IMM_CMD;
-		last_scsi_type(shpnt)[ldn] = IM_IMM_CMD;
-		got_interrupt(shpnt) = 0;
-		reset_status(shpnt) = IM_RESET_IN_PROGRESS;
-		issue_cmd(shpnt, (unsigned long) (imm_command), IM_IMM_CMD | ldn);
-		ticks = IM_RESET_DELAY * HZ;
-		while (reset_status(shpnt) == IM_RESET_IN_PROGRESS && --ticks) {
-			udelay((1 + 999 / HZ) * 1000);
-			barrier();
-		}
-		/* if reset did not complete, just complain */
-		if (!ticks) {
-			printk(KERN_ERR "IBM MCA SCSI: reset did not complete within %d seconds.\n", IM_RESET_DELAY);
-			reset_status(shpnt) = IM_RESET_FINISHED_OK;
-			/* did not work, finish */
-			return 1;
-		}
-		/*if command successful, break */
-		if (stat_result(shpnt) == IM_IMMEDIATE_CMD_COMPLETED)
-			return 1;
-	}
-	if (retries >= 3)
-		return 0;
-	else
-		return 1;
-}
-#endif
-
-/* type-interpreter for physical device numbers */
-static char *ti_p(int dev)
-{
-	switch (dev) {
-	case TYPE_IBM_SCSI_ADAPTER:
-		return ("A");
-	case TYPE_DISK:
-		return ("D");
-	case TYPE_TAPE:
-		return ("T");
-	case TYPE_PROCESSOR:
-		return ("P");
-	case TYPE_WORM:
-		return ("W");
-	case TYPE_ROM:
-		return ("R");
-	case TYPE_SCANNER:
-		return ("S");
-	case TYPE_MOD:
-		return ("M");
-	case TYPE_MEDIUM_CHANGER:
-		return ("C");
-	case TYPE_NO_LUN:
-		return ("+");	/* show NO_LUN */
-	}
-	return ("-");		/* TYPE_NO_DEVICE and others */
-}
-
-/* interpreter for logical device numbers (ldn) */
-static char *ti_l(int val)
-{
-	const char hex[16] = "0123456789abcdef";
-	static char answer[2];
-
-	answer[1] = (char) (0x0);
-	if (val <= MAX_LOG_DEV)
-		answer[0] = hex[val];
-	else
-		answer[0] = '-';
-	return (char *) &answer;
-}
-
-/* transfers bitpattern of the feature command to values in MHz */
-static char *ibmrate(unsigned int speed, int i)
-{
-	switch (speed) {
-	case 0:
-		return i ? "5.00" : "10.00";
-	case 1:
-		return i ? "4.00" : "8.00";
-	case 2:
-		return i ? "3.33" : "6.66";
-	case 3:
-		return i ? "2.86" : "5.00";
-	case 4:
-		return i ? "2.50" : "4.00";
-	case 5:
-		return i ? "2.22" : "3.10";
-	case 6:
-		return i ? "2.00" : "2.50";
-	case 7:
-		return i ? "1.82" : "2.00";
-	}
-	return "---";
-}
-
-static int probe_display(int what)
-{
-	static int rotator = 0;
-	const char rotor[] = "|/-\\";
-
-	if (!(display_mode & LED_DISP))
-		return 0;
-	if (!what) {
-		outl(0x20202020, MOD95_LED_PORT);
-		outl(0x20202020, MOD95_LED_PORT + 4);
-	} else {
-		outb('S', MOD95_LED_PORT + 7);
-		outb('C', MOD95_LED_PORT + 6);
-		outb('S', MOD95_LED_PORT + 5);
-		outb('I', MOD95_LED_PORT + 4);
-		outb('i', MOD95_LED_PORT + 3);
-		outb('n', MOD95_LED_PORT + 2);
-		outb('i', MOD95_LED_PORT + 1);
-		outb((char) (rotor[rotator]), MOD95_LED_PORT);
-		rotator++;
-		if (rotator > 3)
-			rotator = 0;
-	}
-	return 0;
-}
-
-static int probe_bus_mode(struct Scsi_Host *shpnt)
-{
-	struct im_pos_info *info;
-	int num_bus = 0;
-	int ldn;
-
-	info = (struct im_pos_info *) (&(ld(shpnt)[MAX_LOG_DEV].buf));
-	if (get_pos_info(shpnt)) {
-		if (info->connector_size & 0xf000)
-			subsystem_connector_size(shpnt) = 16;
-		else
-			subsystem_connector_size(shpnt) = 32;
-		num_bus |= (info->pos_4b & 8) >> 3;
-		for (ldn = 0; ldn <= MAX_LOG_DEV; ldn++) {
-			if ((special(shpnt) == IBM_SCSI_WCACHE) || (special(shpnt) == IBM_7568_WCACHE)) {
-				if (!((info->cache_stat >> ldn) & 1))
-					ld(shpnt)[ldn].cache_flag = 0;
-			}
-			if (!((info->retry_stat >> ldn) & 1))
-				ld(shpnt)[ldn].retry_flag = 0;
-		}
-#ifdef IM_DEBUG_PROBE
-		printk("IBM MCA SCSI: SCSI-Cache bits: ");
-		for (ldn = 0; ldn <= MAX_LOG_DEV; ldn++) {
-			printk("%d", ld(shpnt)[ldn].cache_flag);
-		}
-		printk("\nIBM MCA SCSI: SCSI-Retry bits: ");
-		for (ldn = 0; ldn <= MAX_LOG_DEV; ldn++) {
-			printk("%d", ld(shpnt)[ldn].retry_flag);
-		}
-		printk("\n");
-#endif
-	}
-	return num_bus;
-}
-
-/* probing scsi devices */
-static void check_devices(struct Scsi_Host *shpnt, int adaptertype)
-{
-	int id, lun, ldn, ticks;
-	int count_devices;	/* local counter for connected device */
-	int max_pun;
-	int num_bus;
-	int speedrun;		/* local adapter_speed check variable */
-
-	/* assign default values to certain variables */
-	ticks = 0;
-	count_devices = 0;
-	IBM_DS(shpnt).dyn_flag = 0;	/* normally no need for dynamical ldn management */
-	IBM_DS(shpnt).total_errors = 0;	/* set errorcounter to 0 */
-	next_ldn(shpnt) = 7;	/* next ldn to be assigned is 7, because 0-6 is 'hardwired' */
-
-	/* initialize the very important driver-informational arrays/structs */
-	memset(ld(shpnt), 0, sizeof(ld(shpnt)));
-	for (ldn = 0; ldn <= MAX_LOG_DEV; ldn++) {
-		last_scsi_command(shpnt)[ldn] = NO_SCSI;	/* emptify last SCSI-command storage */
-		last_scsi_type(shpnt)[ldn] = 0;
-		ld(shpnt)[ldn].cache_flag = 1;
-		ld(shpnt)[ldn].retry_flag = 1;
-	}
-	memset(get_ldn(shpnt), TYPE_NO_DEVICE, sizeof(get_ldn(shpnt)));	/* this is essential ! */
-	memset(get_scsi(shpnt), TYPE_NO_DEVICE, sizeof(get_scsi(shpnt)));	/* this is essential ! */
-	for (lun = 0; lun < 8; lun++) {
-		/* mark the adapter at its pun on all luns */
-		get_scsi(shpnt)[subsystem_pun(shpnt)][lun] = TYPE_IBM_SCSI_ADAPTER;
-		get_ldn(shpnt)[subsystem_pun(shpnt)][lun] = MAX_LOG_DEV;	/* make sure, the subsystem
-											   ldn is active for all
-											   luns. */
-	}
-	probe_display(0);	/* Supercool display usage during SCSI-probing. */
-	/* This makes sense, when booting without any */
-	/* monitor connected on model XX95. */
-
-	/* STEP 1: */
-	adapter_speed(shpnt) = global_adapter_speed;
-	speedrun = adapter_speed(shpnt);
-	while (immediate_feature(shpnt, speedrun, adapter_timeout) == 2) {
-		probe_display(1);
-		if (speedrun == 7)
-			panic("IBM MCA SCSI: Cannot set Synchronous-Transfer-Rate!\n");
-		speedrun++;
-		if (speedrun > 7)
-			speedrun = 7;
-	}
-	adapter_speed(shpnt) = speedrun;
-	/* Get detailed information about the current adapter, necessary for
-	 * device operations: */
-	num_bus = probe_bus_mode(shpnt);
-
-	/* num_bus contains only valid data for the F/W adapter! */
-	if (adaptertype == IBM_SCSI2_FW) {	/* F/W SCSI adapter: */
-		/* F/W adapter PUN-space extension evaluation: */
-		if (num_bus) {
-			printk(KERN_INFO "IBM MCA SCSI: Separate bus mode (wide-addressing enabled)\n");
-			subsystem_maxid(shpnt) = 16;
-		} else {
-			printk(KERN_INFO "IBM MCA SCSI: Combined bus mode (wide-addressing disabled)\n");
-			subsystem_maxid(shpnt) = 8;
-		}
-		printk(KERN_INFO "IBM MCA SCSI: Sync.-Rate (F/W: 20, Int.: 10, Ext.: %s) MBytes/s\n", ibmrate(speedrun, adaptertype));
-	} else			/* all other IBM SCSI adapters: */
-		printk(KERN_INFO "IBM MCA SCSI: Synchronous-SCSI-Transfer-Rate: %s MBytes/s\n", ibmrate(speedrun, adaptertype));
-
-	/* assign correct PUN device space */
-	max_pun = subsystem_maxid(shpnt);
-
-#ifdef IM_DEBUG_PROBE
-	printk("IBM MCA SCSI: Current SCSI-host index: %d\n", shpnt);
-	printk("IBM MCA SCSI: Removing default logical SCSI-device mapping.");
-#else
-	printk(KERN_INFO "IBM MCA SCSI: Dev. Order: %s, Mapping (takes <2min): ", (ibm_ansi_order) ? "ANSI" : "New");
-#endif
-	for (ldn = 0; ldn < MAX_LOG_DEV; ldn++) {
-		probe_display(1);
-#ifdef IM_DEBUG_PROBE
-		printk(".");
-#endif
-		immediate_assign(shpnt, 0, 0, ldn, REMOVE_LDN);	/* remove ldn (wherever) */
-	}
-	lun = 0;		/* default lun is 0 */
-#ifndef IM_DEBUG_PROBE
-	printk("cleared,");
-#endif
-	/* STEP 2: */
-#ifdef IM_DEBUG_PROBE
-	printk("\nIBM MCA SCSI: Scanning SCSI-devices.");
-#endif
-	for (id = 0; id < max_pun; id++)
-#ifdef CONFIG_SCSI_MULTI_LUN
-		for (lun = 0; lun < 8; lun++)
-#endif
-		{
-			probe_display(1);
-#ifdef IM_DEBUG_PROBE
-			printk(".");
-#endif
-			if (id != subsystem_pun(shpnt)) {
-				/* if pun is not the adapter: */
-				/* set ldn=0 to pun,lun */
-				immediate_assign(shpnt, id, lun, PROBE_LDN, SET_LDN);
-				if (device_inquiry(shpnt, PROBE_LDN)) {	/* probe device */
-					get_scsi(shpnt)[id][lun] = (unsigned char) (ld(shpnt)[PROBE_LDN].buf[0]);
-					/* entry, even for NO_LUN */
-					if (ld(shpnt)[PROBE_LDN].buf[0] != TYPE_NO_LUN)
-						count_devices++;	/* a existing device is found */
-				}
-				/* remove ldn */
-				immediate_assign(shpnt, id, lun, PROBE_LDN, REMOVE_LDN);
-			}
-		}
-#ifndef IM_DEBUG_PROBE
-	printk("scanned,");
-#endif
-	/* STEP 3: */
-#ifdef IM_DEBUG_PROBE
-	printk("\nIBM MCA SCSI: Mapping SCSI-devices.");
-#endif
-	ldn = 0;
-	lun = 0;
-#ifdef CONFIG_SCSI_MULTI_LUN
-	for (lun = 0; lun < 8 && ldn < MAX_LOG_DEV; lun++)
-#endif
-		for (id = 0; id < max_pun && ldn < MAX_LOG_DEV; id++) {
-			probe_display(1);
-#ifdef IM_DEBUG_PROBE
-			printk(".");
-#endif
-			if (id != subsystem_pun(shpnt)) {
-				if (get_scsi(shpnt)[id][lun] != TYPE_NO_LUN && get_scsi(shpnt)[id][lun] != TYPE_NO_DEVICE) {
-					/* Only map if accepted type. Always enter for
-					   lun == 0 to get no gaps into ldn-mapping for ldn<7. */
-					immediate_assign(shpnt, id, lun, ldn, SET_LDN);
-					get_ldn(shpnt)[id][lun] = ldn;	/* map ldn */
-					if (device_exists(shpnt, ldn, &ld(shpnt)[ldn].block_length, &ld(shpnt)[ldn].device_type)) {
-#ifdef CONFIG_IBMMCA_SCSI_DEV_RESET
-						printk("resetting device at ldn=%x ... ", ldn);
-						immediate_reset(shpnt, ldn);
-#endif
-						ldn++;
-					} else {
-						/* device vanished, probably because we don't know how to
-						 * handle it or because it has problems */
-						if (lun > 0) {
-							/* remove mapping */
-							get_ldn(shpnt)[id][lun] = TYPE_NO_DEVICE;
-							immediate_assign(shpnt, 0, 0, ldn, REMOVE_LDN);
-						} else
-							ldn++;
-					}
-				} else if (lun == 0) {
-					/* map lun == 0, even if no device exists */
-					immediate_assign(shpnt, id, lun, ldn, SET_LDN);
-					get_ldn(shpnt)[id][lun] = ldn;	/* map ldn */
-					ldn++;
-				}
-			}
-		}
-	/* STEP 4: */
-
-	/* map remaining ldns to non-existing devices */
-	for (lun = 1; lun < 8 && ldn < MAX_LOG_DEV; lun++)
-		for (id = 0; id < max_pun && ldn < MAX_LOG_DEV; id++) {
-			if (get_scsi(shpnt)[id][lun] == TYPE_NO_LUN || get_scsi(shpnt)[id][lun] == TYPE_NO_DEVICE) {
-				probe_display(1);
-				/* Map remaining ldns only to NON-existing pun,lun
-				   combinations to make sure an inquiry will fail.
-				   For MULTI_LUN, it is needed to avoid adapter autonome
-				   SCSI-remapping. */
-				immediate_assign(shpnt, id, lun, ldn, SET_LDN);
-				get_ldn(shpnt)[id][lun] = ldn;
-				ldn++;
-			}
-		}
-#ifndef IM_DEBUG_PROBE
-	printk("mapped.");
-#endif
-	printk("\n");
-#ifdef IM_DEBUG_PROBE
-	if (ibm_ansi_order)
-		printk("IBM MCA SCSI: Device order: IBM/ANSI (pun=7 is first).\n");
-	else
-		printk("IBM MCA SCSI: Device order: New Industry Standard (pun=0 is first).\n");
-#endif
-
-#ifdef IM_DEBUG_PROBE
-	/* Show the physical and logical mapping during boot. */
-	printk("IBM MCA SCSI: Determined SCSI-device-mapping:\n");
-	printk("    Physical SCSI-Device Map               Logical SCSI-Device Map\n");
-	printk("ID\\LUN  0  1  2  3  4  5  6  7       ID\\LUN  0  1  2  3  4  5  6  7\n");
-	for (id = 0; id < max_pun; id++) {
-		printk("%2d     ", id);
-		for (lun = 0; lun < 8; lun++)
-			printk("%2s ", ti_p(get_scsi(shpnt)[id][lun]));
-		printk("      %2d     ", id);
-		for (lun = 0; lun < 8; lun++)
-			printk("%2s ", ti_l(get_ldn(shpnt)[id][lun]));
-		printk("\n");
-	}
-#endif
-
-	/* assign total number of found SCSI-devices to the statistics struct */
-	IBM_DS(shpnt).total_scsi_devices = count_devices;
-
-	/* decide for output in /proc-filesystem, if the configuration of
-	   SCSI-devices makes dynamical reassignment of devices necessary */
-	if (count_devices >= MAX_LOG_DEV)
-		IBM_DS(shpnt).dyn_flag = 1;	/* dynamical assignment is necessary */
-	else
-		IBM_DS(shpnt).dyn_flag = 0;	/* dynamical assignment is not necessary */
-
-	/* If no SCSI-devices are assigned, return 1 in order to cause message. */
-	if (ldn == 0)
-		printk("IBM MCA SCSI: Warning: No SCSI-devices found/assigned!\n");
-
-	/* reset the counters for statistics on the current adapter */
-	IBM_DS(shpnt).scbs = 0;
-	IBM_DS(shpnt).long_scbs = 0;
-	IBM_DS(shpnt).total_accesses = 0;
-	IBM_DS(shpnt).total_interrupts = 0;
-	IBM_DS(shpnt).dynamical_assignments = 0;
-	memset(IBM_DS(shpnt).ldn_access, 0x0, sizeof(IBM_DS(shpnt).ldn_access));
-	memset(IBM_DS(shpnt).ldn_read_access, 0x0, sizeof(IBM_DS(shpnt).ldn_read_access));
-	memset(IBM_DS(shpnt).ldn_write_access, 0x0, sizeof(IBM_DS(shpnt).ldn_write_access));
-	memset(IBM_DS(shpnt).ldn_inquiry_access, 0x0, sizeof(IBM_DS(shpnt).ldn_inquiry_access));
-	memset(IBM_DS(shpnt).ldn_modeselect_access, 0x0, sizeof(IBM_DS(shpnt).ldn_modeselect_access));
-	memset(IBM_DS(shpnt).ldn_assignments, 0x0, sizeof(IBM_DS(shpnt).ldn_assignments));
-	probe_display(0);
-	return;
-}
-
-static int device_exists(struct Scsi_Host *shpnt, int ldn, int *block_length, int *device_type)
-{
-	unsigned char *buf;
-	/* if no valid device found, return immediately with 0 */
-	if (!(device_inquiry(shpnt, ldn)))
-		return 0;
-	buf = (unsigned char *) (&(ld(shpnt)[ldn].buf));
-	if (*buf == TYPE_ROM) {
-		*device_type = TYPE_ROM;
-		*block_length = 2048;	/* (standard blocksize for yellow-/red-book) */
-		return 1;
-	}
-	if (*buf == TYPE_WORM) {
-		*device_type = TYPE_WORM;
-		*block_length = 2048;
-		return 1;
-	}
-	if (*buf == TYPE_DISK) {
-		*device_type = TYPE_DISK;
-		if (read_capacity(shpnt, ldn)) {
-			*block_length = *(buf + 7) + (*(buf + 6) << 8) + (*(buf + 5) << 16) + (*(buf + 4) << 24);
-			return 1;
-		} else
-			return 0;
-	}
-	if (*buf == TYPE_MOD) {
-		*device_type = TYPE_MOD;
-		if (read_capacity(shpnt, ldn)) {
-			*block_length = *(buf + 7) + (*(buf + 6) << 8) + (*(buf + 5) << 16) + (*(buf + 4) << 24);
-			return 1;
-		} else
-			return 0;
-	}
-	if (*buf == TYPE_TAPE) {
-		*device_type = TYPE_TAPE;
-		*block_length = 0;	/* not in use (setting by mt and mtst in op.) */
-		return 1;
-	}
-	if (*buf == TYPE_PROCESSOR) {
-		*device_type = TYPE_PROCESSOR;
-		*block_length = 0;	/* they set their stuff on drivers */
-		return 1;
-	}
-	if (*buf == TYPE_SCANNER) {
-		*device_type = TYPE_SCANNER;
-		*block_length = 0;	/* they set their stuff on drivers */
-		return 1;
-	}
-	if (*buf == TYPE_MEDIUM_CHANGER) {
-		*device_type = TYPE_MEDIUM_CHANGER;
-		*block_length = 0;	/* One never knows, what to expect on a medium
-					   changer device. */
-		return 1;
-	}
-	return 0;
-}
-
-static void internal_ibmmca_scsi_setup(char *str, int *ints)
-{
-	int i, j, io_base, id_base;
-	char *token;
-
-	io_base = 0;
-	id_base = 0;
-	if (str) {
-		j = 0;
-		while ((token = strsep(&str, ",")) != NULL) {
-			if (!strcmp(token, "activity"))
-				display_mode |= LED_ACTIVITY;
-			if (!strcmp(token, "display"))
-				display_mode |= LED_DISP;
-			if (!strcmp(token, "adisplay"))
-				display_mode |= LED_ADISP;
-			if (!strcmp(token, "normal"))
-				ibm_ansi_order = 0;
-			if (!strcmp(token, "ansi"))
-				ibm_ansi_order = 1;
-			if (!strcmp(token, "fast"))
-				global_adapter_speed = 0;
-			if (!strcmp(token, "medium"))
-				global_adapter_speed = 4;
-			if (!strcmp(token, "slow"))
-				global_adapter_speed = 7;
-			if ((*token == '-') || (isdigit(*token))) {
-				if (!(j % 2) && (io_base < IM_MAX_HOSTS))
-					io_port[io_base++] = simple_strtoul(token, NULL, 0);
-				if ((j % 2) && (id_base < IM_MAX_HOSTS))
-					scsi_id[id_base++] = simple_strtoul(token, NULL, 0);
-				j++;
-			}
-		}
-	} else if (ints) {
-		for (i = 0; i < IM_MAX_HOSTS && 2 * i + 2 < ints[0]; i++) {
-			io_port[i] = ints[2 * i + 2];
-			scsi_id[i] = ints[2 * i + 2];
-		}
-	}
-	return;
-}
-
-#if 0
- FIXME NEED TO MOVE TO SYSFS
-
-static int ibmmca_getinfo(char *buf, int slot, void *dev_id)
-{
-	struct Scsi_Host *shpnt;
-	int len, speciale, connectore, k;
-	unsigned int pos[8];
-	unsigned long flags;
-	struct Scsi_Host *dev = dev_id;
-
-	spin_lock_irqsave(dev->host_lock, flags);
-
-	shpnt = dev;		/* assign host-structure to local pointer */
-	len = 0;		/* set filled text-buffer index to 0 */
-	/* get the _special contents of the hostdata structure */
-	speciale = ((struct ibmmca_hostdata *) shpnt->hostdata)->_special;
-	connectore = ((struct ibmmca_hostdata *) shpnt->hostdata)->_connector_size;
-	for (k = 2; k < 4; k++)
-		pos[k] = ((struct ibmmca_hostdata *) shpnt->hostdata)->_pos[k];
-	if (speciale == FORCED_DETECTION) {	/* forced detection */
-		len += sprintf(buf + len,
-			       "Adapter category: forced detected\n" "***************************************\n" "***  Forced detected SCSI Adapter   ***\n" "***  No chip-information available  ***\n" "***************************************\n");
-	} else if (speciale == INTEGRATED_SCSI) {
-		/* if the integrated subsystem has been found automatically: */
-		len += sprintf(buf + len,
-			       "Adapter category: integrated\n" "Chip revision level: %d\n" "Chip status: %s\n" "8 kByte NVRAM status: %s\n", ((pos[2] & 0xf0) >> 4), (pos[2] & 1) ? "enabled" : "disabled", (pos[2] & 2) ? "locked" : "accessible");
-	} else if ((speciale >= 0) && (speciale < ARRAY_SIZE(subsys_list))) {
-		/* if the subsystem is a slot adapter */
-		len += sprintf(buf + len, "Adapter category: slot-card\n" "ROM Segment Address: ");
-		if ((pos[2] & 0xf0) == 0xf0)
-			len += sprintf(buf + len, "off\n");
-		else
-			len += sprintf(buf + len, "0x%x\n", ((pos[2] & 0xf0) << 13) + 0xc0000);
-		len += sprintf(buf + len, "Chip status: %s\n", (pos[2] & 1) ? "enabled" : "disabled");
-		len += sprintf(buf + len, "Adapter I/O Offset: 0x%x\n", ((pos[2] & 0x0e) << 2));
-	} else {
-		len += sprintf(buf + len, "Adapter category: unknown\n");
-	}
-	/* common subsystem information to write to the slotn file */
-	len += sprintf(buf + len, "Subsystem PUN: %d\n", shpnt->this_id);
-	len += sprintf(buf + len, "I/O base address range: 0x%x-0x%x\n", (unsigned int) (shpnt->io_port), (unsigned int) (shpnt->io_port + 7));
-	len += sprintf(buf + len, "MCA-slot size: %d bits", connectore);
-	/* Now make sure, the bufferlength is devidable by 4 to avoid
-	 * paging problems of the buffer. */
-	while (len % sizeof(int) != (sizeof(int) - 1))
-		len += sprintf(buf + len, " ");
-	len += sprintf(buf + len, "\n");
-
-	spin_unlock_irqrestore(shpnt->host_lock, flags);
-
-	return len;
-}
-#endif
-
-static struct scsi_host_template ibmmca_driver_template = {
-          .proc_name      = "ibmmca",
-	  .proc_info	  = ibmmca_proc_info,
-          .name           = "IBM SCSI-Subsystem",
-          .queuecommand   = ibmmca_queuecommand,
-	  .eh_abort_handler = ibmmca_abort,
-	  .eh_host_reset_handler = ibmmca_host_reset,
-          .bios_param     = ibmmca_biosparam,
-          .can_queue      = 16,
-          .this_id        = 7,
-          .sg_tablesize   = 16,
-          .cmd_per_lun    = 1,
-          .use_clustering = ENABLE_CLUSTERING,
-};
-
-static int ibmmca_probe(struct device *dev)
-{
-	struct Scsi_Host *shpnt;
-	int port, id, i, j, k, irq, enabled, ret = -EINVAL;
-	struct mca_device *mca_dev = to_mca_device(dev);
-	const char *description = ibmmca_description[mca_dev->index];
-
-	/* First of all, print the version number of the driver. This is
-	 * important to allow better user bugreports in case of already
-	 * having problems with the MCA_bus probing. */
-	printk(KERN_INFO "IBM MCA SCSI: Version %s\n", IBMMCA_SCSI_DRIVER_VERSION);
-	/* The POS2-register of all PS/2 model SCSI-subsystems has the following
-	 * interpretation of bits:
-	 *                             Bit 7 - 4 : Chip Revision ID (Release)
-	 *                             Bit 3 - 2 : Reserved
-	 *                             Bit 1     : 8k NVRAM Disabled
-	 *                             Bit 0     : Chip Enable (EN-Signal)
-	 * The POS3-register is interpreted as follows:
-	 *                             Bit 7 - 5 : SCSI ID
-	 *                             Bit 4     : Reserved = 0
-	 *                             Bit 3 - 0 : Reserved = 0
-	 * (taken from "IBM, PS/2 Hardware Interface Technical Reference, Common
-	 * Interfaces (1991)").
-	 * In short words, this means, that IBM PS/2 machines only support
-	 * 1 single subsystem by default. The slot-adapters must have another
-	 * configuration on pos2. Here, one has to assume the following
-	 * things for POS2-register:
-	 *                             Bit 7 - 4 : Chip Revision ID (Release)
-	 *                             Bit 3 - 1 : port offset factor
-	 *                             Bit 0     : Chip Enable (EN-Signal)
-	 * As I found a patch here, setting the IO-registers to 0x3540 forced,
-	 * as there was a 0x05 in POS2 on a model 56, I assume, that the
-	 * port 0x3540 must be fix for integrated SCSI-controllers.
-	 * Ok, this discovery leads to the following implementation: (M.Lang) */
-
-	/* first look for the IBM SCSI integrated subsystem on the motherboard */
-	for (j = 0; j < 8; j++)	/* read the pos-information */
-		pos[j] = mca_device_read_pos(mca_dev, j);
-	id = (pos[3] & 0xe0) >> 5; /* this is correct and represents the PUN */
-	enabled = (pos[2] &0x01);
-	if (!enabled) {
-		printk(KERN_WARNING "IBM MCA SCSI: WARNING - Your SCSI-subsystem is disabled!\n");
-		printk(KERN_WARNING "              SCSI-operations may not work.\n");
-	}
-
-	/* pos2 = pos3 = 0xff if there is no integrated SCSI-subsystem present, but
-	 * if we ignore the settings of all surrounding pos registers, it is not
-	 * completely sufficient to only check pos2 and pos3. */
-	/* Therefore, now the following if statement is used to
-	 * make sure, we see a real integrated onboard SCSI-interface and no
-	 * internal system information, which gets mapped to some pos registers
-	 * on models 95xx. */
-	if (mca_dev->slot == MCA_INTEGSCSI &&
-	    ((!pos[0] && !pos[1] && pos[2] > 0 &&
-	      pos[3] > 0 && !pos[4] && !pos[5] &&
-	      !pos[6] && !pos[7]) ||
-	     (pos[0] == 0xff && pos[1] == 0xff &&
-	      pos[2] < 0xff && pos[3] < 0xff &&
-	      pos[4] == 0xff && pos[5] == 0xff &&
-	      pos[6] == 0xff && pos[7] == 0xff))) {
-		irq = IM_IRQ;
-		port = IM_IO_PORT;
-	} else {
-		irq = IM_IRQ;
-		port = IM_IO_PORT + ((pos[2] &0x0e) << 2);
-		if ((mca_dev->index == IBM_SCSI2_FW) && (pos[6] != 0)) {
-			printk(KERN_ERR "IBM MCA SCSI: ERROR - Wrong POS(6)-register setting!\n");
-			printk(KERN_ERR "              Impossible to determine adapter PUN!\n");
-			printk(KERN_ERR "              Guessing adapter PUN = 7.\n");
-			id = 7;
-		} else {
-			id = (pos[3] & 0xe0) >> 5;	/* get subsystem PUN */
-			if (mca_dev->index == IBM_SCSI2_FW) {
-				id |= (pos[3] & 0x10) >> 1;	/* get subsystem PUN high-bit
-								 * for F/W adapters */
-			}
-		}
-		if ((mca_dev->index == IBM_SCSI2_FW) &&
-		    (pos[4] & 0x01) && (pos[6] == 0)) {
-			/* IRQ11 is used by SCSI-2 F/W Adapter/A */
-			printk(KERN_DEBUG "IBM MCA SCSI: SCSI-2 F/W adapter needs IRQ 11.\n");
-			irq = IM_IRQ_FW;
-		}
-	}
-
-
-
-	/* give detailed information on the subsystem. This helps me
-	 * additionally during debugging and analyzing bug-reports. */
-	printk(KERN_INFO "IBM MCA SCSI: %s found, io=0x%x, scsi id=%d,\n",
-	       description, port, id);
-	if (mca_dev->slot == MCA_INTEGSCSI)
-		printk(KERN_INFO "              chip rev.=%d, 8K NVRAM=%s, subsystem=%s\n", ((pos[2] & 0xf0) >> 4), (pos[2] & 2) ? "locked" : "accessible", (pos[2] & 1) ? "enabled." : "disabled.");
-	else {
-		if ((pos[2] & 0xf0) == 0xf0)
-			printk(KERN_DEBUG "              ROM Addr.=off,");
-		else
-			printk(KERN_DEBUG "              ROM Addr.=0x%x,", ((pos[2] & 0xf0) << 13) + 0xc0000);
-
-		printk(KERN_DEBUG " port-offset=0x%x, subsystem=%s\n", ((pos[2] & 0x0e) << 2), (pos[2] & 1) ? "enabled." : "disabled.");
-	}
-
-	/* check I/O region */
-	if (!request_region(port, IM_N_IO_PORT, description)) {
-		printk(KERN_ERR "IBM MCA SCSI: Unable to get I/O region 0x%x-0x%x (%d ports).\n", port, port + IM_N_IO_PORT - 1, IM_N_IO_PORT);
-		goto out_fail;
-	}
-
-	/* register host */
-	shpnt = scsi_host_alloc(&ibmmca_driver_template,
-				sizeof(struct ibmmca_hostdata));
-	if (!shpnt) {
-		printk(KERN_ERR "IBM MCA SCSI: Unable to register host.\n");
-		goto out_release;
-	}
-
-	dev_set_drvdata(dev, shpnt);
-	if(request_irq(irq, interrupt_handler, IRQF_SHARED, description, dev)) {
-		printk(KERN_ERR "IBM MCA SCSI: failed to request interrupt %d\n", irq);
-		goto out_free_host;
-	}
-
-	/* request I/O region */
-	special(shpnt) = mca_dev->index;	/* important assignment or else crash! */
-	subsystem_connector_size(shpnt) = 0;	/* preset slot-size */
-	shpnt->irq = irq;	/* assign necessary stuff for the adapter */
-	shpnt->io_port = port;
-	shpnt->n_io_port = IM_N_IO_PORT;
-	shpnt->this_id = id;
-	shpnt->max_id = 8;	/* 8 PUNs are default */
-	/* now, the SCSI-subsystem is connected to Linux */
-
-#ifdef IM_DEBUG_PROBE
-	ctrl = (unsigned int) (inb(IM_CTR_REG(found)));	/* get control-register status */
-	printk("IBM MCA SCSI: Control Register contents: %x, status: %x\n", ctrl, inb(IM_STAT_REG(found)));
-	printk("IBM MCA SCSI: This adapters' POS-registers: ");
-	for (i = 0; i < 8; i++)
-		printk("%x ", pos[i]);
-	printk("\n");
-#endif
-	reset_status(shpnt) = IM_RESET_NOT_IN_PROGRESS;
-
-	for (i = 0; i < 16; i++)	/* reset the tables */
-		for (j = 0; j < 8; j++)
-			get_ldn(shpnt)[i][j] = MAX_LOG_DEV;
-
-	/* check which logical devices exist */
-	/* after this line, local interrupting is possible: */
-	local_checking_phase_flag(shpnt) = 1;
-	check_devices(shpnt, mca_dev->index);	/* call by value, using the global variable hosts */
-	local_checking_phase_flag(shpnt) = 0;
-
-	/* an ibm mca subsystem has been detected */
-
-	for (k = 2; k < 7; k++)
-		((struct ibmmca_hostdata *) shpnt->hostdata)->_pos[k] = pos[k];
-	((struct ibmmca_hostdata *) shpnt->hostdata)->_special = INTEGRATED_SCSI;
-	mca_device_set_name(mca_dev, description);
-	/* FIXME: NEED TO REPLUMB TO SYSFS
-	   mca_set_adapter_procfn(MCA_INTEGSCSI, (MCA_ProcFn) ibmmca_getinfo, shpnt);
-	*/
-	mca_device_set_claim(mca_dev, 1);
-	if (scsi_add_host(shpnt, dev)) {
-		dev_printk(KERN_ERR, dev, "IBM MCA SCSI: scsi_add_host failed\n");
-		goto out_free_host;
-	}
-	scsi_scan_host(shpnt);
-
-	return 0;
- out_free_host:
-	scsi_host_put(shpnt);
- out_release:
-	release_region(port, IM_N_IO_PORT);
- out_fail:
-	return ret;
-}
-
-static int __devexit ibmmca_remove(struct device *dev)
-{
-	struct Scsi_Host *shpnt = dev_get_drvdata(dev);
-	scsi_remove_host(shpnt);
-	release_region(shpnt->io_port, shpnt->n_io_port);
-	free_irq(shpnt->irq, dev);
-	scsi_host_put(shpnt);
-	return 0;
-}
-
-/* The following routine is the SCSI command queue for the midlevel driver */
-static int ibmmca_queuecommand_lck(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
-{
-	unsigned int ldn;
-	unsigned int scsi_cmd;
-	struct im_scb *scb;
-	struct Scsi_Host *shpnt;
-	int current_ldn;
-	int id, lun;
-	int target;
-	int max_pun;
-	int i;
-	struct scatterlist *sg;
-
-	shpnt = cmd->device->host;
-
-	max_pun = subsystem_maxid(shpnt);
-	if (ibm_ansi_order) {
-		target = max_pun - 1 - cmd->device->id;
-		if ((target <= subsystem_pun(shpnt)) && (cmd->device->id <= subsystem_pun(shpnt)))
-			target--;
-		else if ((target >= subsystem_pun(shpnt)) && (cmd->device->id >= subsystem_pun(shpnt)))
-			target++;
-	} else
-		target = cmd->device->id;
-
-	/* if (target,lun) is NO LUN or not existing at all, return error */
-	if ((get_scsi(shpnt)[target][cmd->device->lun] == TYPE_NO_LUN) || (get_scsi(shpnt)[target][cmd->device->lun] == TYPE_NO_DEVICE)) {
-		cmd->result = DID_NO_CONNECT << 16;
-		if (done)
-			done(cmd);
-		return 0;
-	}
-
-	/*if (target,lun) unassigned, do further checks... */
-	ldn = get_ldn(shpnt)[target][cmd->device->lun];
-	if (ldn >= MAX_LOG_DEV) {	/* on invalid ldn do special stuff */
-		if (ldn > MAX_LOG_DEV) {	/* dynamical remapping if ldn unassigned */
-			current_ldn = next_ldn(shpnt);	/* stop-value for one circle */
-			while (ld(shpnt)[next_ldn(shpnt)].cmd) {	/* search for a occupied, but not in */
-				/* command-processing ldn. */
-				next_ldn(shpnt)++;
-				if (next_ldn(shpnt) >= MAX_LOG_DEV)
-					next_ldn(shpnt) = 7;
-				if (current_ldn == next_ldn(shpnt)) {	/* One circle done ? */
-					/* no non-processing ldn found */
-					scmd_printk(KERN_WARNING, cmd,
-	"IBM MCA SCSI: Cannot assign SCSI-device dynamically!\n"
-	"              On ldn 7-14 SCSI-commands everywhere in progress.\n"
-	"              Reporting DID_NO_CONNECT for device.\n");
-					cmd->result = DID_NO_CONNECT << 16;	/* return no connect */
-					if (done)
-						done(cmd);
-					return 0;
-				}
-			}
-
-			/* unmap non-processing ldn */
-			for (id = 0; id < max_pun; id++)
-				for (lun = 0; lun < 8; lun++) {
-					if (get_ldn(shpnt)[id][lun] == next_ldn(shpnt)) {
-						get_ldn(shpnt)[id][lun] = TYPE_NO_DEVICE;
-						get_scsi(shpnt)[id][lun] = TYPE_NO_DEVICE;
-						/* unmap entry */
-					}
-				}
-			/* set reduced interrupt_handler-mode for checking */
-			local_checking_phase_flag(shpnt) = 1;
-			/* map found ldn to pun,lun */
-			get_ldn(shpnt)[target][cmd->device->lun] = next_ldn(shpnt);
-			/* change ldn to the right value, that is now next_ldn */
-			ldn = next_ldn(shpnt);
-			/* unassign all ldns (pun,lun,ldn does not matter for remove) */
-			immediate_assign(shpnt, 0, 0, 0, REMOVE_LDN);
-			/* set only LDN for remapped device */
-			immediate_assign(shpnt, target, cmd->device->lun, ldn, SET_LDN);
-			/* get device information for ld[ldn] */
-			if (device_exists(shpnt, ldn, &ld(shpnt)[ldn].block_length, &ld(shpnt)[ldn].device_type)) {
-				ld(shpnt)[ldn].cmd = NULL;	/* To prevent panic set 0, because
-								   devices that were not assigned,
-								   should have nothing in progress. */
-				get_scsi(shpnt)[target][cmd->device->lun] = ld(shpnt)[ldn].device_type;
-				/* increase assignment counters for statistics in /proc */
-				IBM_DS(shpnt).dynamical_assignments++;
-				IBM_DS(shpnt).ldn_assignments[ldn]++;
-			} else
-				/* panic here, because a device, found at boottime has
-				   vanished */
-				panic("IBM MCA SCSI: ldn=0x%x, SCSI-device on (%d,%d) vanished!\n", ldn, target, cmd->device->lun);
-			/* unassign again all ldns (pun,lun,ldn does not matter for remove) */
-			immediate_assign(shpnt, 0, 0, 0, REMOVE_LDN);
-			/* remap all ldns, as written in the pun/lun table */
-			lun = 0;
-#ifdef CONFIG_SCSI_MULTI_LUN
-			for (lun = 0; lun < 8; lun++)
-#endif
-				for (id = 0; id < max_pun; id++) {
-					if (get_ldn(shpnt)[id][lun] <= MAX_LOG_DEV)
-						immediate_assign(shpnt, id, lun, get_ldn(shpnt)[id][lun], SET_LDN);
-				}
-			/* set back to normal interrupt_handling */
-			local_checking_phase_flag(shpnt) = 0;
-#ifdef IM_DEBUG_PROBE
-			/* Information on syslog terminal */
-			printk("IBM MCA SCSI: ldn=0x%x dynamically reassigned to (%d,%d).\n", ldn, target, cmd->device->lun);
-#endif
-			/* increase next_ldn for next dynamical assignment */
-			next_ldn(shpnt)++;
-			if (next_ldn(shpnt) >= MAX_LOG_DEV)
-				next_ldn(shpnt) = 7;
-		} else {	/* wall against Linux accesses to the subsystem adapter */
-			cmd->result = DID_BAD_TARGET << 16;
-			if (done)
-				done(cmd);
-			return 0;
-		}
-	}
-
-	/*verify there is no command already in progress for this log dev */
-	if (ld(shpnt)[ldn].cmd)
-		panic("IBM MCA SCSI: cmd already in progress for this ldn.\n");
-
-	/*save done in cmd, and save cmd for the interrupt handler */
-	cmd->scsi_done = done;
-	ld(shpnt)[ldn].cmd = cmd;
-
-	/*fill scb information independent of the scsi command */
-	scb = &(ld(shpnt)[ldn].scb);
-	ld(shpnt)[ldn].tsb.dev_status = 0;
-	scb->enable = IM_REPORT_TSB_ONLY_ON_ERROR | IM_RETRY_ENABLE;
-	scb->tsb_adr = isa_virt_to_bus(&(ld(shpnt)[ldn].tsb));
-	scsi_cmd = cmd->cmnd[0];
-
-	if (scsi_sg_count(cmd)) {
-		BUG_ON(scsi_sg_count(cmd) > 16);
-
-		scsi_for_each_sg(cmd, sg, scsi_sg_count(cmd), i) {
-			ld(shpnt)[ldn].sge[i].address = (void *) (isa_page_to_bus(sg_page(sg)) + sg->offset);
-			ld(shpnt)[ldn].sge[i].byte_length = sg->length;
-		}
-		scb->enable |= IM_POINTER_TO_LIST;
-		scb->sys_buf_adr = isa_virt_to_bus(&(ld(shpnt)[ldn].sge[0]));
-		scb->sys_buf_length = scsi_sg_count(cmd) * sizeof(struct im_sge);
-	} else {
- 		scb->sys_buf_adr = isa_virt_to_bus(scsi_sglist(cmd));
-		/* recent Linux midlevel SCSI places 1024 byte for inquiry
-		 * command. Far too much for old PS/2 hardware. */
-		switch (scsi_cmd) {
-			/* avoid command errors by setting bufferlengths to
-			 * ANSI-standard. Beware of forcing it to 255,
-			 * this could SEGV the kernel!!! */
-		case INQUIRY:
-		case REQUEST_SENSE:
-		case MODE_SENSE:
-		case MODE_SELECT:
-			if (scsi_bufflen(cmd) > 255)
-				scb->sys_buf_length = 255;
-			else
-				scb->sys_buf_length = scsi_bufflen(cmd);
-			break;
-		case TEST_UNIT_READY:
-			scb->sys_buf_length = 0;
-			break;
-		default:
-			scb->sys_buf_length = scsi_bufflen(cmd);
-			break;
-		}
-	}
-	/*fill scb information dependent on scsi command */
-
-#ifdef IM_DEBUG_CMD
-	printk("issue scsi cmd=%02x to ldn=%d\n", scsi_cmd, ldn);
-#endif
-
-	/* for specific device-type debugging: */
-#ifdef IM_DEBUG_CMD_SPEC_DEV
-	if (ld(shpnt)[ldn].device_type == IM_DEBUG_CMD_DEVICE)
-		printk("(SCSI-device-type=0x%x) issue scsi cmd=%02x to ldn=%d\n", ld(shpnt)[ldn].device_type, scsi_cmd, ldn);
-#endif
-
-	/* for possible panics store current command */
-	last_scsi_command(shpnt)[ldn] = scsi_cmd;
-	last_scsi_type(shpnt)[ldn] = IM_SCB;
-	/* update statistical info */
-	IBM_DS(shpnt).total_accesses++;
-	IBM_DS(shpnt).ldn_access[ldn]++;
-
-	switch (scsi_cmd) {
-	case READ_6:
-	case WRITE_6:
-	case READ_10:
-	case WRITE_10:
-	case READ_12:
-	case WRITE_12:
-		/* Distinguish between disk and other devices. Only disks (that are the
-		   most frequently accessed devices) should be supported by the
-		   IBM-SCSI-Subsystem commands. */
-		switch (ld(shpnt)[ldn].device_type) {
-		case TYPE_DISK:	/* for harddisks enter here ... */
-		case TYPE_MOD:	/* ... try it also for MO-drives (send flames as */
-			/*     you like, if this won't work.) */
-			if (scsi_cmd == READ_6 || scsi_cmd == READ_10 || scsi_cmd == READ_12) {
-				/* read command preparations */
-				scb->enable |= IM_READ_CONTROL;
-				IBM_DS(shpnt).ldn_read_access[ldn]++;	/* increase READ-access on ldn stat. */
-				scb->command = IM_READ_DATA_CMD | IM_NO_DISCONNECT;
-			} else {	/* write command preparations */
-				IBM_DS(shpnt).ldn_write_access[ldn]++;	/* increase write-count on ldn stat. */
-				scb->command = IM_WRITE_DATA_CMD | IM_NO_DISCONNECT;
-			}
-			if (scsi_cmd == READ_6 || scsi_cmd == WRITE_6) {
-				scb->u1.log_blk_adr = (((unsigned) cmd->cmnd[3]) << 0) | (((unsigned) cmd->cmnd[2]) << 8) | ((((unsigned) cmd->cmnd[1]) & 0x1f) << 16);
-				scb->u2.blk.count = (unsigned) cmd->cmnd[4];
-			} else {
-				scb->u1.log_blk_adr = (((unsigned) cmd->cmnd[5]) << 0) | (((unsigned) cmd->cmnd[4]) << 8) | (((unsigned) cmd->cmnd[3]) << 16) | (((unsigned) cmd->cmnd[2]) << 24);
-				scb->u2.blk.count = (((unsigned) cmd->cmnd[8]) << 0) | (((unsigned) cmd->cmnd[7]) << 8);
-			}
-			last_scsi_logical_block(shpnt)[ldn] = scb->u1.log_blk_adr;
-			last_scsi_blockcount(shpnt)[ldn] = scb->u2.blk.count;
-			scb->u2.blk.length = ld(shpnt)[ldn].block_length;
-			break;
-			/* for other devices, enter here. Other types are not known by
-			   Linux! TYPE_NO_LUN is forbidden as valid device. */
-		case TYPE_ROM:
-		case TYPE_TAPE:
-		case TYPE_PROCESSOR:
-		case TYPE_WORM:
-		case TYPE_SCANNER:
-		case TYPE_MEDIUM_CHANGER:
-			/* If there is a sequential-device, IBM recommends to use
-			   IM_OTHER_SCSI_CMD_CMD instead of subsystem READ/WRITE.
-			   This includes CD-ROM devices, too, due to the partial sequential
-			   read capabilities. */
-			scb->command = IM_OTHER_SCSI_CMD_CMD;
-			if (scsi_cmd == READ_6 || scsi_cmd == READ_10 || scsi_cmd == READ_12)
-				/* enable READ */
-				scb->enable |= IM_READ_CONTROL;
-			scb->enable |= IM_BYPASS_BUFFER;
-			scb->u1.scsi_cmd_length = cmd->cmd_len;
-			memcpy(scb->u2.scsi_command, cmd->cmnd, cmd->cmd_len);
-			last_scsi_type(shpnt)[ldn] = IM_LONG_SCB;
-			/* Read/write on this non-disk devices is also displayworthy,
-			   so flash-up the LED/display. */
-			break;
-		}
-		break;
-	case INQUIRY:
-		IBM_DS(shpnt).ldn_inquiry_access[ldn]++;
-		scb->command = IM_DEVICE_INQUIRY_CMD;
-		scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT | IM_BYPASS_BUFFER;
-		scb->u1.log_blk_adr = 0;
-		break;
-	case TEST_UNIT_READY:
-		scb->command = IM_OTHER_SCSI_CMD_CMD;
-		scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT | IM_BYPASS_BUFFER;
-		scb->u1.log_blk_adr = 0;
-		scb->u1.scsi_cmd_length = 6;
-		memcpy(scb->u2.scsi_command, cmd->cmnd, 6);
-		last_scsi_type(shpnt)[ldn] = IM_LONG_SCB;
-		break;
-	case READ_CAPACITY:
-		/* the length of system memory buffer must be exactly 8 bytes */
-		scb->command = IM_READ_CAPACITY_CMD;
-		scb->enable |= IM_READ_CONTROL | IM_BYPASS_BUFFER;
-		if (scb->sys_buf_length > 8)
-			scb->sys_buf_length = 8;
-		break;
-		/* Commands that need read-only-mode (system <- device): */
-	case REQUEST_SENSE:
-		scb->command = IM_REQUEST_SENSE_CMD;
-		scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT | IM_BYPASS_BUFFER;
-		break;
-		/* Commands that need write-only-mode (system -> device): */
-	case MODE_SELECT:
-	case MODE_SELECT_10:
-		IBM_DS(shpnt).ldn_modeselect_access[ldn]++;
-		scb->command = IM_OTHER_SCSI_CMD_CMD;
-		scb->enable |= IM_SUPRESS_EXCEPTION_SHORT | IM_BYPASS_BUFFER;	/*Select needs WRITE-enabled */
-		scb->u1.scsi_cmd_length = cmd->cmd_len;
-		memcpy(scb->u2.scsi_command, cmd->cmnd, cmd->cmd_len);
-		last_scsi_type(shpnt)[ldn] = IM_LONG_SCB;
-		break;
-		/* For other commands, read-only is useful. Most other commands are
-		   running without an input-data-block. */
-	default:
-		scb->command = IM_OTHER_SCSI_CMD_CMD;
-		scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT | IM_BYPASS_BUFFER;
-		scb->u1.scsi_cmd_length = cmd->cmd_len;
-		memcpy(scb->u2.scsi_command, cmd->cmnd, cmd->cmd_len);
-		last_scsi_type(shpnt)[ldn] = IM_LONG_SCB;
-		break;
-	}
-	/*issue scb command, and return */
-	if (++disk_rw_in_progress == 1)
-		PS2_DISK_LED_ON(shpnt->host_no, target);
-
-	if (last_scsi_type(shpnt)[ldn] == IM_LONG_SCB) {
-		issue_cmd(shpnt, isa_virt_to_bus(scb), IM_LONG_SCB | ldn);
-		IBM_DS(shpnt).long_scbs++;
-	} else {
-		issue_cmd(shpnt, isa_virt_to_bus(scb), IM_SCB | ldn);
-		IBM_DS(shpnt).scbs++;
-	}
-	return 0;
-}
-
-static DEF_SCSI_QCMD(ibmmca_queuecommand)
-
-static int __ibmmca_abort(Scsi_Cmnd * cmd)
-{
-	/* Abort does not work, as the adapter never generates an interrupt on
-	 * whatever situation is simulated, even when really pending commands
-	 * are running on the adapters' hardware ! */
-
-	struct Scsi_Host *shpnt;
-	unsigned int ldn;
-	void (*saved_done) (Scsi_Cmnd *);
-	int target;
-	int max_pun;
-	unsigned long imm_command;
-
-#ifdef IM_DEBUG_PROBE
-	printk("IBM MCA SCSI: Abort subroutine called...\n");
-#endif
-
-	shpnt = cmd->device->host;
-
-	max_pun = subsystem_maxid(shpnt);
-	if (ibm_ansi_order) {
-		target = max_pun - 1 - cmd->device->id;
-		if ((target <= subsystem_pun(shpnt)) && (cmd->device->id <= subsystem_pun(shpnt)))
-			target--;
-		else if ((target >= subsystem_pun(shpnt)) && (cmd->device->id >= subsystem_pun(shpnt)))
-			target++;
-	} else
-		target = cmd->device->id;
-
-	/* get logical device number, and disable system interrupts */
-	printk(KERN_WARNING "IBM MCA SCSI: Sending abort to device pun=%d, lun=%d.\n", target, cmd->device->lun);
-	ldn = get_ldn(shpnt)[target][cmd->device->lun];
-
-	/*if cmd for this ldn has already finished, no need to abort */
-	if (!ld(shpnt)[ldn].cmd) {
-		    return SUCCESS;
-	}
-
-	/* Clear ld.cmd, save done function, install internal done,
-	 * send abort immediate command (this enables sys. interrupts),
-	 * and wait until the interrupt arrives.
-	 */
-	saved_done = cmd->scsi_done;
-	cmd->scsi_done = internal_done;
-	cmd->SCp.Status = 0;
-	last_scsi_command(shpnt)[ldn] = IM_ABORT_IMM_CMD;
-	last_scsi_type(shpnt)[ldn] = IM_IMM_CMD;
-	imm_command = inl(IM_CMD_REG(shpnt));
-	imm_command &= (unsigned long) (0xffff0000);	/* mask reserved stuff */
-	imm_command |= (unsigned long) (IM_ABORT_IMM_CMD);
-	/* must wait for attention reg not busy */
-	/* FIXME - timeout, politeness */
-	while (1) {
-		if (!(inb(IM_STAT_REG(shpnt)) & IM_BUSY))
-			break;
-	}
-	/* write registers and enable system interrupts */
-	outl(imm_command, IM_CMD_REG(shpnt));
-	outb(IM_IMM_CMD | ldn, IM_ATTN_REG(shpnt));
-#ifdef IM_DEBUG_PROBE
-	printk("IBM MCA SCSI: Abort queued to adapter...\n");
-#endif
-	spin_unlock_irq(shpnt->host_lock);
-	while (!cmd->SCp.Status)
-		yield();
-	spin_lock_irq(shpnt->host_lock);
-	cmd->scsi_done = saved_done;
-#ifdef IM_DEBUG_PROBE
-	printk("IBM MCA SCSI: Abort returned with adapter response...\n");
-#endif
-
-	/*if abort went well, call saved done, then return success or error */
-	if (cmd->result == (DID_ABORT << 16)) 
-	{
-		cmd->result |= DID_ABORT << 16;
-		if (cmd->scsi_done)
-			(cmd->scsi_done) (cmd);
-		ld(shpnt)[ldn].cmd = NULL;
-#ifdef IM_DEBUG_PROBE
-		printk("IBM MCA SCSI: Abort finished with success.\n");
-#endif
-		return SUCCESS;
-	} else {
-		cmd->result |= DID_NO_CONNECT << 16;
-		if (cmd->scsi_done)
-			(cmd->scsi_done) (cmd);
-		ld(shpnt)[ldn].cmd = NULL;
-#ifdef IM_DEBUG_PROBE
-		printk("IBM MCA SCSI: Abort failed.\n");
-#endif
-		return FAILED;
-	}
-}
-
-static int ibmmca_abort(Scsi_Cmnd * cmd)
-{
-	struct Scsi_Host *shpnt = cmd->device->host;
-	int rc;
-
-	spin_lock_irq(shpnt->host_lock);
-	rc = __ibmmca_abort(cmd);
-	spin_unlock_irq(shpnt->host_lock);
-
-	return rc;
-}
-
-static int __ibmmca_host_reset(Scsi_Cmnd * cmd)
-{
-	struct Scsi_Host *shpnt;
-	Scsi_Cmnd *cmd_aid;
-	int ticks, i;
-	unsigned long imm_command;
-
-	BUG_ON(cmd == NULL);
-
-	ticks = IM_RESET_DELAY * HZ;
-	shpnt = cmd->device->host;
-
-	if (local_checking_phase_flag(shpnt)) {
-		printk(KERN_WARNING "IBM MCA SCSI: unable to reset while checking devices.\n");
-		return FAILED;
-	}
-
-	/* issue reset immediate command to subsystem, and wait for interrupt */
-	printk("IBM MCA SCSI: resetting all devices.\n");
-	reset_status(shpnt) = IM_RESET_IN_PROGRESS;
-	last_scsi_command(shpnt)[0xf] = IM_RESET_IMM_CMD;
-	last_scsi_type(shpnt)[0xf] = IM_IMM_CMD;
-	imm_command = inl(IM_CMD_REG(shpnt));
-	imm_command &= (unsigned long) (0xffff0000);	/* mask reserved stuff */
-	imm_command |= (unsigned long) (IM_RESET_IMM_CMD);
-	/* must wait for attention reg not busy */
-	while (1) {
-		if (!(inb(IM_STAT_REG(shpnt)) & IM_BUSY))
-			break;
-		spin_unlock_irq(shpnt->host_lock);
-		yield();
-		spin_lock_irq(shpnt->host_lock);
-	}
-	/*write registers and enable system interrupts */
-	outl(imm_command, IM_CMD_REG(shpnt));
-	outb(IM_IMM_CMD | 0xf, IM_ATTN_REG(shpnt));
-	/* wait for interrupt finished or intr_stat register to be set, as the
-	 * interrupt will not be executed, while we are in here! */
-	 
-	/* FIXME: This is really really icky we so want a sleeping version of this ! */
-	while (reset_status(shpnt) == IM_RESET_IN_PROGRESS && --ticks && ((inb(IM_INTR_REG(shpnt)) & 0x8f) != 0x8f)) {
-		udelay((1 + 999 / HZ) * 1000);
-		barrier();
-	}
-	/* if reset did not complete, just return an error */
-	if (!ticks) {
-		printk(KERN_ERR "IBM MCA SCSI: reset did not complete within %d seconds.\n", IM_RESET_DELAY);
-		reset_status(shpnt) = IM_RESET_FINISHED_FAIL;
-		return FAILED;
-	}
-
-	if ((inb(IM_INTR_REG(shpnt)) & 0x8f) == 0x8f) {
-		/* analysis done by this routine and not by the intr-routine */
-		if (inb(IM_INTR_REG(shpnt)) == 0xaf)
-			reset_status(shpnt) = IM_RESET_FINISHED_OK_NO_INT;
-		else if (inb(IM_INTR_REG(shpnt)) == 0xcf)
-			reset_status(shpnt) = IM_RESET_FINISHED_FAIL;
-		else		/* failed, 4get it */
-			reset_status(shpnt) = IM_RESET_NOT_IN_PROGRESS_NO_INT;
-		outb(IM_EOI | 0xf, IM_ATTN_REG(shpnt));
-	}
-
-	/* if reset failed, just return an error */
-	if (reset_status(shpnt) == IM_RESET_FINISHED_FAIL) {
-		printk(KERN_ERR "IBM MCA SCSI: reset failed.\n");
-		return FAILED;
-	}
-
-	/* so reset finished ok - call outstanding done's, and return success */
-	printk(KERN_INFO "IBM MCA SCSI: Reset successfully completed.\n");
-	for (i = 0; i < MAX_LOG_DEV; i++) {
-		cmd_aid = ld(shpnt)[i].cmd;
-		if (cmd_aid && cmd_aid->scsi_done) {
-			ld(shpnt)[i].cmd = NULL;
-			cmd_aid->result = DID_RESET << 16;
-		}
-	}
-	return SUCCESS;
-}
-
-static int ibmmca_host_reset(Scsi_Cmnd * cmd)
-{
-	struct Scsi_Host *shpnt = cmd->device->host;
-	int rc;
-
-	spin_lock_irq(shpnt->host_lock);
-	rc = __ibmmca_host_reset(cmd);
-	spin_unlock_irq(shpnt->host_lock);
-
-	return rc;
-}
-
-static int ibmmca_biosparam(struct scsi_device *sdev, struct block_device *bdev, sector_t capacity, int *info)
-{
-	int size = capacity;
-	info[0] = 64;
-	info[1] = 32;
-	info[2] = size / (info[0] * info[1]);
-	if (info[2] >= 1024) {
-		info[0] = 128;
-		info[1] = 63;
-		info[2] = size / (info[0] * info[1]);
-		if (info[2] >= 1024) {
-			info[0] = 255;
-			info[1] = 63;
-			info[2] = size / (info[0] * info[1]);
-			if (info[2] >= 1024)
-				info[2] = 1023;
-		}
-	}
-	return 0;
-}
-
-/* calculate percentage of total accesses on a ldn */
-static int ldn_access_load(struct Scsi_Host *shpnt, int ldn)
-{
-	if (IBM_DS(shpnt).total_accesses == 0)
-		return (0);
-	if (IBM_DS(shpnt).ldn_access[ldn] == 0)
-		return (0);
-	return (IBM_DS(shpnt).ldn_access[ldn] * 100) / IBM_DS(shpnt).total_accesses;
-}
-
-/* calculate total amount of r/w-accesses */
-static int ldn_access_total_read_write(struct Scsi_Host *shpnt)
-{
-	int a;
-	int i;
-
-	a = 0;
-	for (i = 0; i <= MAX_LOG_DEV; i++)
-		a += IBM_DS(shpnt).ldn_read_access[i] + IBM_DS(shpnt).ldn_write_access[i];
-	return (a);
-}
-
-static int ldn_access_total_inquiry(struct Scsi_Host *shpnt)
-{
-	int a;
-	int i;
-
-	a = 0;
-	for (i = 0; i <= MAX_LOG_DEV; i++)
-		a += IBM_DS(shpnt).ldn_inquiry_access[i];
-	return (a);
-}
-
-static int ldn_access_total_modeselect(struct Scsi_Host *shpnt)
-{
-	int a;
-	int i;
-
-	a = 0;
-	for (i = 0; i <= MAX_LOG_DEV; i++)
-		a += IBM_DS(shpnt).ldn_modeselect_access[i];
-	return (a);
-}
-
-/* routine to display info in the proc-fs-structure (a deluxe feature) */
-static int ibmmca_proc_info(struct Scsi_Host *shpnt, char *buffer, char **start, off_t offset, int length, int inout)
-{
-	int len = 0;
-	int i, id, lun;
-	unsigned long flags;
-	int max_pun;
-
-	
-	spin_lock_irqsave(shpnt->host_lock, flags);	/* Check it */
-
-	max_pun = subsystem_maxid(shpnt);
-
-	len += sprintf(buffer + len, "\n             IBM-SCSI-Subsystem-Linux-Driver, Version %s\n\n\n", IBMMCA_SCSI_DRIVER_VERSION);
-	len += sprintf(buffer + len, " SCSI Access-Statistics:\n");
-	len += sprintf(buffer + len, "               Device Scanning Order....: %s\n", (ibm_ansi_order) ? "IBM/ANSI" : "New Industry Standard");
-#ifdef CONFIG_SCSI_MULTI_LUN
-	len += sprintf(buffer + len, "               Multiple LUN probing.....: Yes\n");
-#else
-	len += sprintf(buffer + len, "               Multiple LUN probing.....: No\n");
-#endif
-	len += sprintf(buffer + len, "               This Hostnumber..........: %d\n", shpnt->host_no);
-	len += sprintf(buffer + len, "               Base I/O-Port............: 0x%x\n", (unsigned int) (IM_CMD_REG(shpnt)));
-	len += sprintf(buffer + len, "               (Shared) IRQ.............: %d\n", IM_IRQ);
-	len += sprintf(buffer + len, "               Total Interrupts.........: %d\n", IBM_DS(shpnt).total_interrupts);
-	len += sprintf(buffer + len, "               Total SCSI Accesses......: %d\n", IBM_DS(shpnt).total_accesses);
-	len += sprintf(buffer + len, "               Total short SCBs.........: %d\n", IBM_DS(shpnt).scbs);
-	len += sprintf(buffer + len, "               Total long SCBs..........: %d\n", IBM_DS(shpnt).long_scbs);
-	len += sprintf(buffer + len, "                 Total SCSI READ/WRITE..: %d\n", ldn_access_total_read_write(shpnt));
-	len += sprintf(buffer + len, "                 Total SCSI Inquiries...: %d\n", ldn_access_total_inquiry(shpnt));
-	len += sprintf(buffer + len, "                 Total SCSI Modeselects.: %d\n", ldn_access_total_modeselect(shpnt));
-	len += sprintf(buffer + len, "                 Total SCSI other cmds..: %d\n", IBM_DS(shpnt).total_accesses - ldn_access_total_read_write(shpnt)
-		       - ldn_access_total_modeselect(shpnt)
-		       - ldn_access_total_inquiry(shpnt));
-	len += sprintf(buffer + len, "               Total SCSI command fails.: %d\n\n", IBM_DS(shpnt).total_errors);
-	len += sprintf(buffer + len, " Logical-Device-Number (LDN) Access-Statistics:\n");
-	len += sprintf(buffer + len, "         LDN | Accesses [%%] |   READ    |   WRITE   | ASSIGNMENTS\n");
-	len += sprintf(buffer + len, "        -----|--------------|-----------|-----------|--------------\n");
-	for (i = 0; i <= MAX_LOG_DEV; i++)
-		len += sprintf(buffer + len, "         %2X  |    %3d       |  %8d |  %8d | %8d\n", i, ldn_access_load(shpnt, i), IBM_DS(shpnt).ldn_read_access[i], IBM_DS(shpnt).ldn_write_access[i], IBM_DS(shpnt).ldn_assignments[i]);
-	len += sprintf(buffer + len, "        -----------------------------------------------------------\n\n");
-	len += sprintf(buffer + len, " Dynamical-LDN-Assignment-Statistics:\n");
-	len += sprintf(buffer + len, "               Number of physical SCSI-devices..: %d (+ Adapter)\n", IBM_DS(shpnt).total_scsi_devices);
-	len += sprintf(buffer + len, "               Dynamical Assignment necessary...: %s\n", IBM_DS(shpnt).dyn_flag ? "Yes" : "No ");
-	len += sprintf(buffer + len, "               Next LDN to be assigned..........: 0x%x\n", next_ldn(shpnt));
-	len += sprintf(buffer + len, "               Dynamical assignments done yet...: %d\n", IBM_DS(shpnt).dynamical_assignments);
-	len += sprintf(buffer + len, "\n Current SCSI-Device-Mapping:\n");
-	len += sprintf(buffer + len, "        Physical SCSI-Device Map               Logical SCSI-Device Map\n");
-	len += sprintf(buffer + len, "    ID\\LUN  0  1  2  3  4  5  6  7       ID\\LUN  0  1  2  3  4  5  6  7\n");
-	for (id = 0; id < max_pun; id++) {
-		len += sprintf(buffer + len, "    %2d     ", id);
-		for (lun = 0; lun < 8; lun++)
-			len += sprintf(buffer + len, "%2s ", ti_p(get_scsi(shpnt)[id][lun]));
-		len += sprintf(buffer + len, "      %2d     ", id);
-		for (lun = 0; lun < 8; lun++)
-			len += sprintf(buffer + len, "%2s ", ti_l(get_ldn(shpnt)[id][lun]));
-		len += sprintf(buffer + len, "\n");
-	}
-
-	len += sprintf(buffer + len, "(A = IBM-Subsystem, D = Harddisk, T = Tapedrive, P = Processor, W = WORM,\n");
-	len += sprintf(buffer + len, " R = CD-ROM, S = Scanner, M = MO-Drive, C = Medium-Changer, + = unprovided LUN,\n");
-	len += sprintf(buffer + len, " - = nothing found, nothing assigned or unprobed LUN)\n\n");
-
-	*start = buffer + offset;
-	len -= offset;
-	if (len > length)
-		len = length;
-	spin_unlock_irqrestore(shpnt->host_lock, flags);
-	return len;
-}
-
-static int option_setup(char *str)
-{
-	int ints[IM_MAX_HOSTS];
-	char *cur = str;
-	int i = 1;
-
-	while (cur && isdigit(*cur) && i < IM_MAX_HOSTS) {
-		ints[i++] = simple_strtoul(cur, NULL, 0);
-		if ((cur = strchr(cur, ',')) != NULL)
-			cur++;
-	}
-	ints[0] = i - 1;
-	internal_ibmmca_scsi_setup(cur, ints);
-	return 1;
-}
-
-__setup("ibmmcascsi=", option_setup);
-
-static struct mca_driver ibmmca_driver = {
-	.id_table = ibmmca_id_table,
-	.driver = {
-		.name	= "ibmmca",
-		.bus	= &mca_bus_type,
-		.probe	= ibmmca_probe,
-		.remove	= __devexit_p(ibmmca_remove),
-	},
-};
-
-static int __init ibmmca_init(void)
-{
-#ifdef MODULE
-	/* If the driver is run as module, read from conf.modules or cmd-line */
-	if (boot_options)
-		option_setup(boot_options);
-#endif
-
-	return mca_register_driver_integrated(&ibmmca_driver, MCA_INTEGSCSI);
-}
-
-static void __exit ibmmca_exit(void)
-{
-	mca_unregister_driver(&ibmmca_driver);
-}
-
-module_init(ibmmca_init);
-module_exit(ibmmca_exit);
diff --git a/drivers/scsi/sim710.c b/drivers/scsi/sim710.c
index 8ac6ce7..a318264 100644
--- a/drivers/scsi/sim710.c
+++ b/drivers/scsi/sim710.c
@@ -17,7 +17,7 @@
  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *----------------------------------------------------------------------------
  *
- * MCA card detection code by Trent McNair.
+ * MCA card detection code by Trent McNair. (now deleted)
  * Fixes to not explicitly nul bss data from Xavier Bestel.
  * Some multiboard fixes from Rolf Eike Beer.
  * Auto probing of EISA config space from Trevor Hemsley.
@@ -32,7 +32,6 @@
 #include <linux/blkdev.h>
 #include <linux/device.h>
 #include <linux/init.h>
-#include <linux/mca.h>
 #include <linux/eisa.h>
 #include <linux/interrupt.h>
 #include <scsi/scsi_host.h>
@@ -43,7 +42,7 @@
 #include "53c700.h"
 
 
-/* Must be enough for both EISA and MCA */
+/* Must be enough for EISA */
 #define MAX_SLOTS 8
 static __u8 __initdata id_array[MAX_SLOTS] = { [0 ... MAX_SLOTS-1] = 7 };
 
@@ -89,7 +88,7 @@
 __setup("sim710=", param_setup);
 
 static struct scsi_host_template sim710_driver_template = {
-	.name			= "LSI (Symbios) 710 MCA/EISA",
+	.name			= "LSI (Symbios) 710 EISA",
 	.proc_name		= "sim710",
 	.this_id		= 7,
 	.module			= THIS_MODULE,
@@ -169,114 +168,6 @@
 	return 0;
 }
 
-#ifdef CONFIG_MCA
-
-/* CARD ID 01BB and 01BA use the same pos values */
-#define MCA_01BB_IO_PORTS { 0x0000, 0x0000, 0x0800, 0x0C00, 0x1000, 0x1400, \
-			    0x1800, 0x1C00, 0x2000, 0x2400, 0x2800, \
-			    0x2C00, 0x3000, 0x3400, 0x3800, 0x3C00, \
-			    0x4000, 0x4400, 0x4800, 0x4C00, 0x5000  }
-
-#define MCA_01BB_IRQS { 3, 5, 11, 14 }
-
-/* CARD ID 004f */
-#define MCA_004F_IO_PORTS { 0x0000, 0x0200, 0x0300, 0x0400, 0x0500,  0x0600 }
-#define MCA_004F_IRQS { 5, 9, 14 }
-
-static short sim710_mca_id_table[] = { 0x01bb, 0x01ba, 0x004f, 0};
-
-static __init int
-sim710_mca_probe(struct device *dev)
-{
-	struct mca_device *mca_dev = to_mca_device(dev);
-	int slot = mca_dev->slot;
-	int pos[3];
-	unsigned int base;
-	int irq_vector;
-	short id = sim710_mca_id_table[mca_dev->index];
-	static int io_004f_by_pos[] = MCA_004F_IO_PORTS;
-	static int irq_004f_by_pos[] = MCA_004F_IRQS;
-	static int io_01bb_by_pos[] = MCA_01BB_IO_PORTS;
-	static int irq_01bb_by_pos[] = MCA_01BB_IRQS;
-	char *name;
-	int clock;
-
-	pos[0] = mca_device_read_stored_pos(mca_dev, 2);
-	pos[1] = mca_device_read_stored_pos(mca_dev, 3);
-	pos[2] = mca_device_read_stored_pos(mca_dev, 4);
-
-	/*
-	 * 01BB & 01BA port base by bits 7,6,5,4,3,2 in pos[2]
-	 *
-	 *    000000  <disabled>   001010  0x2800
-	 *    000001  <invalid>    001011  0x2C00
-	 *    000010  0x0800       001100  0x3000
-	 *    000011  0x0C00       001101  0x3400
-	 *    000100  0x1000       001110  0x3800
-	 *    000101  0x1400       001111  0x3C00
-	 *    000110  0x1800       010000  0x4000
-	 *    000111  0x1C00       010001  0x4400
-	 *    001000  0x2000       010010  0x4800
-	 *    001001  0x2400       010011  0x4C00
-	 *                         010100  0x5000
-	 *
-	 * 00F4 port base by bits 3,2,1 in pos[0]
-	 *
-	 *    000  <disabled>      001    0x200
-	 *    010  0x300           011    0x400
-	 *    100  0x500           101    0x600
-	 *
-	 * 01BB & 01BA IRQ is specified in pos[0] bits 7 and 6:
-	 *
-	 *    00   3               10   11
-	 *    01   5               11   14
-	 *
-	 * 00F4 IRQ specified by bits 6,5,4 in pos[0]
-	 *
-	 *    100   5              101    9
-	 *    110   14
-	 */
-
-	if (id == 0x01bb || id == 0x01ba) {
-		base = io_01bb_by_pos[(pos[2] & 0xFC) >> 2];
-		irq_vector =
-			irq_01bb_by_pos[((pos[0] & 0xC0) >> 6)];
-
-		clock = 50;
-		if (id == 0x01bb)
-			name = "NCR 3360/3430 SCSI SubSystem";
-		else
-			name = "NCR Dual SIOP SCSI Host Adapter Board";
-	} else if ( id == 0x004f ) {
-		base = io_004f_by_pos[((pos[0] & 0x0E) >> 1)];
-		irq_vector =
-			irq_004f_by_pos[((pos[0] & 0x70) >> 4) - 4];
-		clock = 50;
-		name = "NCR 53c710 SCSI Host Adapter Board";
-	} else {
-		return -ENODEV;
-	}
-	mca_device_set_name(mca_dev, name);
-	mca_device_set_claim(mca_dev, 1);
-	base = mca_device_transform_ioport(mca_dev, base);
-	irq_vector = mca_device_transform_irq(mca_dev, irq_vector);
-
-	return sim710_probe_common(dev, base, irq_vector, clock,
-				   0, id_array[slot]);
-}
-
-static struct mca_driver sim710_mca_driver = {
-	.id_table		= sim710_mca_id_table,
-	.driver = {
-		.name		= "sim710",
-		.bus		= &mca_bus_type,
-		.probe		= sim710_mca_probe,
-		.remove		= __devexit_p(sim710_device_remove),
-	},
-};
-
-#endif /* CONFIG_MCA */
-
 #ifdef CONFIG_EISA
 static struct eisa_device_id sim710_eisa_ids[] = {
 	{ "CPQ4410" },
@@ -344,10 +235,6 @@
 		param_setup(sim710);
 #endif
 
-#ifdef CONFIG_MCA
-	err = mca_register_driver(&sim710_mca_driver);
-#endif
-
 #ifdef CONFIG_EISA
 	err = eisa_driver_register(&sim710_eisa_driver);
 #endif
@@ -361,11 +248,6 @@
 
 static void __exit sim710_exit(void)
 {
-#ifdef CONFIG_MCA
-	if (MCA_bus)
-		mca_unregister_driver(&sim710_mca_driver);
-#endif
-
 #ifdef CONFIG_EISA
 	eisa_driver_unregister(&sim710_eisa_driver);
 #endif
diff --git a/drivers/sh/clk/cpg.c b/drivers/sh/clk/cpg.c
index 91b6d52..f0d015d 100644
--- a/drivers/sh/clk/cpg.c
+++ b/drivers/sh/clk/cpg.c
@@ -2,6 +2,7 @@
  * Helper routines for SuperH Clock Pulse Generator blocks (CPG).
  *
  *  Copyright (C) 2010  Magnus Damm
+ *  Copyright (C) 2010 - 2012  Paul Mundt
  *
  * This file is subject to the terms and conditions of the GNU General Public
  * License.  See the file "COPYING" in the main directory of this archive
@@ -13,26 +14,44 @@
 #include <linux/io.h>
 #include <linux/sh_clk.h>
 
-static int sh_clk_mstp32_enable(struct clk *clk)
+static unsigned int sh_clk_read(struct clk *clk)
 {
-	iowrite32(ioread32(clk->mapped_reg) & ~(1 << clk->enable_bit),
-		  clk->mapped_reg);
+	if (clk->flags & CLK_ENABLE_REG_8BIT)
+		return ioread8(clk->mapped_reg);
+	else if (clk->flags & CLK_ENABLE_REG_16BIT)
+		return ioread16(clk->mapped_reg);
+
+	return ioread32(clk->mapped_reg);
+}
+
+static void sh_clk_write(int value, struct clk *clk)
+{
+	if (clk->flags & CLK_ENABLE_REG_8BIT)
+		iowrite8(value, clk->mapped_reg);
+	else if (clk->flags & CLK_ENABLE_REG_16BIT)
+		iowrite16(value, clk->mapped_reg);
+	else
+		iowrite32(value, clk->mapped_reg);
+}
+
+static int sh_clk_mstp_enable(struct clk *clk)
+{
+	sh_clk_write(sh_clk_read(clk) & ~(1 << clk->enable_bit), clk);
 	return 0;
 }
 
-static void sh_clk_mstp32_disable(struct clk *clk)
+static void sh_clk_mstp_disable(struct clk *clk)
 {
-	iowrite32(ioread32(clk->mapped_reg) | (1 << clk->enable_bit),
-		  clk->mapped_reg);
+	sh_clk_write(sh_clk_read(clk) | (1 << clk->enable_bit), clk);
 }
 
-static struct sh_clk_ops sh_clk_mstp32_clk_ops = {
-	.enable		= sh_clk_mstp32_enable,
-	.disable	= sh_clk_mstp32_disable,
+static struct sh_clk_ops sh_clk_mstp_clk_ops = {
+	.enable		= sh_clk_mstp_enable,
+	.disable	= sh_clk_mstp_disable,
 	.recalc		= followparent_recalc,
 };
 
-int __init sh_clk_mstp32_register(struct clk *clks, int nr)
+int __init sh_clk_mstp_register(struct clk *clks, int nr)
 {
 	struct clk *clkp;
 	int ret = 0;
@@ -40,7 +59,7 @@
 
 	for (k = 0; !ret && (k < nr); k++) {
 		clkp = clks + k;
-		clkp->ops = &sh_clk_mstp32_clk_ops;
+		clkp->ops = &sh_clk_mstp_clk_ops;
 		ret |= clk_register(clkp);
 	}
 
@@ -72,7 +91,7 @@
 	clk_rate_table_build(clk, clk->freq_table, table->nr_divisors,
 			     table, NULL);
 
-	idx = ioread32(clk->mapped_reg) & 0x003f;
+	idx = sh_clk_read(clk) & 0x003f;
 
 	return clk->freq_table[idx].frequency;
 }
@@ -98,10 +117,10 @@
 	if (ret < 0)
 		return ret;
 
-	value = ioread32(clk->mapped_reg) &
+	value = sh_clk_read(clk) &
 		~(((1 << clk->src_width) - 1) << clk->src_shift);
 
-	iowrite32(value | (i << clk->src_shift), clk->mapped_reg);
+	sh_clk_write(value | (i << clk->src_shift), clk);
 
 	/* Rebuild the frequency table */
 	clk_rate_table_build(clk, clk->freq_table, table->nr_divisors,
@@ -119,10 +138,10 @@
 	if (idx < 0)
 		return idx;
 
-	value = ioread32(clk->mapped_reg);
+	value = sh_clk_read(clk);
 	value &= ~0x3f;
 	value |= idx;
-	iowrite32(value, clk->mapped_reg);
+	sh_clk_write(value, clk);
 	return 0;
 }
 
@@ -133,9 +152,9 @@
 
 	ret = sh_clk_div6_set_rate(clk, clk->rate);
 	if (ret == 0) {
-		value = ioread32(clk->mapped_reg);
+		value = sh_clk_read(clk);
 		value &= ~0x100; /* clear stop bit to enable clock */
-		iowrite32(value, clk->mapped_reg);
+		sh_clk_write(value, clk);
 	}
 	return ret;
 }
@@ -144,10 +163,10 @@
 {
 	unsigned long value;
 
-	value = ioread32(clk->mapped_reg);
+	value = sh_clk_read(clk);
 	value |= 0x100; /* stop clock */
 	value |= 0x3f; /* VDIV bits must be non-zero, overwrite divider */
-	iowrite32(value, clk->mapped_reg);
+	sh_clk_write(value, clk);
 }
 
 static struct sh_clk_ops sh_clk_div6_clk_ops = {
@@ -182,7 +201,7 @@
 		return -EINVAL;
 	}
 
-	val  = (ioread32(clk->mapped_reg) >> clk->src_shift);
+	val  = (sh_clk_read(clk) >> clk->src_shift);
 	val &= (1 << clk->src_width) - 1;
 
 	if (val >= clk->parent_num) {
@@ -252,7 +271,7 @@
 	clk_rate_table_build(clk, clk->freq_table, table->nr_divisors,
 			     table, &clk->arch_flags);
 
-	idx = (ioread32(clk->mapped_reg) >> clk->enable_bit) & 0x000f;
+	idx = (sh_clk_read(clk) >> clk->enable_bit) & 0x000f;
 
 	return clk->freq_table[idx].frequency;
 }
@@ -270,15 +289,15 @@
 	 */
 
 	if (parent->flags & CLK_ENABLE_ON_INIT)
-		value = ioread32(clk->mapped_reg) & ~(1 << 7);
+		value = sh_clk_read(clk) & ~(1 << 7);
 	else
-		value = ioread32(clk->mapped_reg) | (1 << 7);
+		value = sh_clk_read(clk) | (1 << 7);
 
 	ret = clk_reparent(clk, parent);
 	if (ret < 0)
 		return ret;
 
-	iowrite32(value, clk->mapped_reg);
+	sh_clk_write(value, clk);
 
 	/* Rebiuld the frequency table */
 	clk_rate_table_build(clk, clk->freq_table, table->nr_divisors,
@@ -295,10 +314,10 @@
 	if (idx < 0)
 		return idx;
 
-	value = ioread32(clk->mapped_reg);
+	value = sh_clk_read(clk);
 	value &= ~(0xf << clk->enable_bit);
 	value |= (idx << clk->enable_bit);
-	iowrite32(value, clk->mapped_reg);
+	sh_clk_write(value, clk);
 
 	if (d4t->kick)
 		d4t->kick(clk);
@@ -308,13 +327,13 @@
 
 static int sh_clk_div4_enable(struct clk *clk)
 {
-	iowrite32(ioread32(clk->mapped_reg) & ~(1 << 8), clk->mapped_reg);
+	sh_clk_write(sh_clk_read(clk) & ~(1 << 8), clk);
 	return 0;
 }
 
 static void sh_clk_div4_disable(struct clk *clk)
 {
-	iowrite32(ioread32(clk->mapped_reg) | (1 << 8), clk->mapped_reg);
+	sh_clk_write(sh_clk_read(clk) | (1 << 8), clk);
 }
 
 static struct sh_clk_ops sh_clk_div4_clk_ops = {
diff --git a/drivers/sh/intc/dynamic.c b/drivers/sh/intc/dynamic.c
index 5fea1ee..14eb01e 100644
--- a/drivers/sh/intc/dynamic.c
+++ b/drivers/sh/intc/dynamic.c
@@ -55,11 +55,3 @@
 {
 	irq_free_desc(irq);
 }
-
-void reserve_intc_vectors(struct intc_vect *vectors, unsigned int nr_vecs)
-{
-	int i;
-
-	for (i = 0; i < nr_vecs; i++)
-		irq_reserve_irq(evt2irq(vectors[i].vect));
-}
diff --git a/drivers/target/Kconfig b/drivers/target/Kconfig
index b28794b..1830368 100644
--- a/drivers/target/Kconfig
+++ b/drivers/target/Kconfig
@@ -32,5 +32,6 @@
 source "drivers/target/loopback/Kconfig"
 source "drivers/target/tcm_fc/Kconfig"
 source "drivers/target/iscsi/Kconfig"
+source "drivers/target/sbp/Kconfig"
 
 endif
diff --git a/drivers/target/Makefile b/drivers/target/Makefile
index 62e5405..61648d8 100644
--- a/drivers/target/Makefile
+++ b/drivers/target/Makefile
@@ -25,3 +25,4 @@
 obj-$(CONFIG_LOOPBACK_TARGET)	+= loopback/
 obj-$(CONFIG_TCM_FC)		+= tcm_fc/
 obj-$(CONFIG_ISCSI_TARGET)	+= iscsi/
+obj-$(CONFIG_SBP_TARGET)	+= sbp/
diff --git a/drivers/target/sbp/Kconfig b/drivers/target/sbp/Kconfig
new file mode 100644
index 0000000..132da54
--- /dev/null
+++ b/drivers/target/sbp/Kconfig
@@ -0,0 +1,11 @@
+config SBP_TARGET
+	tristate "FireWire SBP-2 fabric module"
+	depends on FIREWIRE && EXPERIMENTAL
+	help
+	  Say Y or M here to enable SCSI target functionality over FireWire.
+	  This enables you to expose SCSI devices to other nodes on the FireWire
+	  bus, for example hard disks. Similar to FireWire Target Disk mode on
+	  many Apple computers.
+
+	  To compile this driver as a module, say M here: The module will be
+	  called sbp-target.
diff --git a/drivers/target/sbp/Makefile b/drivers/target/sbp/Makefile
new file mode 100644
index 0000000..27747ad
--- /dev/null
+++ b/drivers/target/sbp/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_SBP_TARGET) += sbp_target.o
diff --git a/drivers/target/sbp/sbp_target.c b/drivers/target/sbp/sbp_target.c
new file mode 100644
index 0000000..37c6098
--- /dev/null
+++ b/drivers/target/sbp/sbp_target.c
@@ -0,0 +1,2621 @@
+/*
+ * SBP2 target driver (SCSI over IEEE1394 in target mode)
+ *
+ * Copyright (C) 2011  Chris Boot <bootc@bootc.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define KMSG_COMPONENT "sbp_target"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/configfs.h>
+#include <linux/ctype.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_tcq.h>
+#include <target/target_core_base.h>
+#include <target/target_core_backend.h>
+#include <target/target_core_fabric.h>
+#include <target/target_core_fabric_configfs.h>
+#include <target/target_core_configfs.h>
+#include <target/configfs_macros.h>
+#include <asm/unaligned.h>
+
+#include "sbp_target.h"
+
+/* Local pointer to allocated TCM configfs fabric module */
+static struct target_fabric_configfs *sbp_fabric_configfs;
+
+/* FireWire address region for management and command block address handlers */
+static const struct fw_address_region sbp_register_region = {
+	.start	= CSR_REGISTER_BASE + 0x10000,
+	.end	= 0x1000000000000ULL,
+};
+
+static const u32 sbp_unit_directory_template[] = {
+	0x1200609e, /* unit_specifier_id: NCITS/T10 */
+	0x13010483, /* unit_sw_version: 1155D Rev 4 */
+	0x3800609e, /* command_set_specifier_id: NCITS/T10 */
+	0x390104d8, /* command_set: SPC-2 */
+	0x3b000000, /* command_set_revision: 0 */
+	0x3c000001, /* firmware_revision: 1 */
+};
+
+#define SESSION_MAINTENANCE_INTERVAL HZ
+
+static atomic_t login_id = ATOMIC_INIT(0);
+
+static void session_maintenance_work(struct work_struct *);
+static int sbp_run_transaction(struct fw_card *, int, int, int, int,
+		unsigned long long, void *, size_t);
+
+static int read_peer_guid(u64 *guid, const struct sbp_management_request *req)
+{
+	int ret;
+	__be32 high, low;
+
+	ret = sbp_run_transaction(req->card, TCODE_READ_QUADLET_REQUEST,
+			req->node_addr, req->generation, req->speed,
+			(CSR_REGISTER_BASE | CSR_CONFIG_ROM) + 3 * 4,
+			&high, sizeof(high));
+	if (ret != RCODE_COMPLETE)
+		return ret;
+
+	ret = sbp_run_transaction(req->card, TCODE_READ_QUADLET_REQUEST,
+			req->node_addr, req->generation, req->speed,
+			(CSR_REGISTER_BASE | CSR_CONFIG_ROM) + 4 * 4,
+			&low, sizeof(low));
+	if (ret != RCODE_COMPLETE)
+		return ret;
+
+	*guid = (u64)be32_to_cpu(high) << 32 | be32_to_cpu(low);
+
+	return RCODE_COMPLETE;
+}
+
+static struct sbp_session *sbp_session_find_by_guid(
+	struct sbp_tpg *tpg, u64 guid)
+{
+	struct se_session *se_sess;
+	struct sbp_session *sess, *found = NULL;
+
+	spin_lock_bh(&tpg->se_tpg.session_lock);
+	list_for_each_entry(se_sess, &tpg->se_tpg.tpg_sess_list, sess_list) {
+		sess = se_sess->fabric_sess_ptr;
+		if (sess->guid == guid)
+			found = sess;
+	}
+	spin_unlock_bh(&tpg->se_tpg.session_lock);
+
+	return found;
+}
+
+static struct sbp_login_descriptor *sbp_login_find_by_lun(
+		struct sbp_session *session, struct se_lun *lun)
+{
+	struct sbp_login_descriptor *login, *found = NULL;
+
+	spin_lock_bh(&session->lock);
+	list_for_each_entry(login, &session->login_list, link) {
+		if (login->lun == lun)
+			found = login;
+	}
+	spin_unlock_bh(&session->lock);
+
+	return found;
+}
+
+static int sbp_login_count_all_by_lun(
+		struct sbp_tpg *tpg,
+		struct se_lun *lun,
+		int exclusive)
+{
+	struct se_session *se_sess;
+	struct sbp_session *sess;
+	struct sbp_login_descriptor *login;
+	int count = 0;
+
+	spin_lock_bh(&tpg->se_tpg.session_lock);
+	list_for_each_entry(se_sess, &tpg->se_tpg.tpg_sess_list, sess_list) {
+		sess = se_sess->fabric_sess_ptr;
+
+		spin_lock_bh(&sess->lock);
+		list_for_each_entry(login, &sess->login_list, link) {
+			if (login->lun != lun)
+				continue;
+
+			if (!exclusive || login->exclusive)
+				count++;
+		}
+		spin_unlock_bh(&sess->lock);
+	}
+	spin_unlock_bh(&tpg->se_tpg.session_lock);
+
+	return count;
+}
+
+static struct sbp_login_descriptor *sbp_login_find_by_id(
+	struct sbp_tpg *tpg, int login_id)
+{
+	struct se_session *se_sess;
+	struct sbp_session *sess;
+	struct sbp_login_descriptor *login, *found = NULL;
+
+	spin_lock_bh(&tpg->se_tpg.session_lock);
+	list_for_each_entry(se_sess, &tpg->se_tpg.tpg_sess_list, sess_list) {
+		sess = se_sess->fabric_sess_ptr;
+
+		spin_lock_bh(&sess->lock);
+		list_for_each_entry(login, &sess->login_list, link) {
+			if (login->login_id == login_id)
+				found = login;
+		}
+		spin_unlock_bh(&sess->lock);
+	}
+	spin_unlock_bh(&tpg->se_tpg.session_lock);
+
+	return found;
+}
+
+static struct se_lun *sbp_get_lun_from_tpg(struct sbp_tpg *tpg, int lun)
+{
+	struct se_portal_group *se_tpg = &tpg->se_tpg;
+	struct se_lun *se_lun;
+
+	if (lun >= TRANSPORT_MAX_LUNS_PER_TPG)
+		return ERR_PTR(-EINVAL);
+
+	spin_lock(&se_tpg->tpg_lun_lock);
+	se_lun = se_tpg->tpg_lun_list[lun];
+
+	if (se_lun->lun_status != TRANSPORT_LUN_STATUS_ACTIVE)
+		se_lun = ERR_PTR(-ENODEV);
+
+	spin_unlock(&se_tpg->tpg_lun_lock);
+
+	return se_lun;
+}
+
+static struct sbp_session *sbp_session_create(
+		struct sbp_tpg *tpg,
+		u64 guid)
+{
+	struct sbp_session *sess;
+	int ret;
+	char guid_str[17];
+	struct se_node_acl *se_nacl;
+
+	sess = kmalloc(sizeof(*sess), GFP_KERNEL);
+	if (!sess) {
+		pr_err("failed to allocate session descriptor\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	sess->se_sess = transport_init_session();
+	if (IS_ERR(sess->se_sess)) {
+		pr_err("failed to init se_session\n");
+
+		ret = PTR_ERR(sess->se_sess);
+		kfree(sess);
+		return ERR_PTR(ret);
+	}
+
+	snprintf(guid_str, sizeof(guid_str), "%016llx", guid);
+
+	se_nacl = core_tpg_check_initiator_node_acl(&tpg->se_tpg, guid_str);
+	if (!se_nacl) {
+		pr_warn("Node ACL not found for %s\n", guid_str);
+
+		transport_free_session(sess->se_sess);
+		kfree(sess);
+
+		return ERR_PTR(-EPERM);
+	}
+
+	sess->se_sess->se_node_acl = se_nacl;
+
+	spin_lock_init(&sess->lock);
+	INIT_LIST_HEAD(&sess->login_list);
+	INIT_DELAYED_WORK(&sess->maint_work, session_maintenance_work);
+
+	sess->guid = guid;
+
+	transport_register_session(&tpg->se_tpg, se_nacl, sess->se_sess, sess);
+
+	return sess;
+}
+
+static void sbp_session_release(struct sbp_session *sess, bool cancel_work)
+{
+	spin_lock_bh(&sess->lock);
+	if (!list_empty(&sess->login_list)) {
+		spin_unlock_bh(&sess->lock);
+		return;
+	}
+	spin_unlock_bh(&sess->lock);
+
+	if (cancel_work)
+		cancel_delayed_work_sync(&sess->maint_work);
+
+	transport_deregister_session_configfs(sess->se_sess);
+	transport_deregister_session(sess->se_sess);
+
+	if (sess->card)
+		fw_card_put(sess->card);
+
+	kfree(sess);
+}
+
+static void sbp_target_agent_unregister(struct sbp_target_agent *);
+
+static void sbp_login_release(struct sbp_login_descriptor *login,
+	bool cancel_work)
+{
+	struct sbp_session *sess = login->sess;
+
+	/* FIXME: abort/wait on tasks */
+
+	sbp_target_agent_unregister(login->tgt_agt);
+
+	if (sess) {
+		spin_lock_bh(&sess->lock);
+		list_del(&login->link);
+		spin_unlock_bh(&sess->lock);
+
+		sbp_session_release(sess, cancel_work);
+	}
+
+	kfree(login);
+}
+
+static struct sbp_target_agent *sbp_target_agent_register(
+	struct sbp_login_descriptor *);
+
+static void sbp_management_request_login(
+	struct sbp_management_agent *agent, struct sbp_management_request *req,
+	int *status_data_size)
+{
+	struct sbp_tport *tport = agent->tport;
+	struct sbp_tpg *tpg = tport->tpg;
+	struct se_lun *se_lun;
+	int ret;
+	u64 guid;
+	struct sbp_session *sess;
+	struct sbp_login_descriptor *login;
+	struct sbp_login_response_block *response;
+	int login_response_len;
+
+	se_lun = sbp_get_lun_from_tpg(tpg,
+			LOGIN_ORB_LUN(be32_to_cpu(req->orb.misc)));
+	if (IS_ERR(se_lun)) {
+		pr_notice("login to unknown LUN: %d\n",
+			LOGIN_ORB_LUN(be32_to_cpu(req->orb.misc)));
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_LUN_NOTSUPP));
+		return;
+	}
+
+	ret = read_peer_guid(&guid, req);
+	if (ret != RCODE_COMPLETE) {
+		pr_warn("failed to read peer GUID: %d\n", ret);
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+		return;
+	}
+
+	pr_notice("mgt_agent LOGIN to LUN %d from %016llx\n",
+		se_lun->unpacked_lun, guid);
+
+	sess = sbp_session_find_by_guid(tpg, guid);
+	if (sess) {
+		login = sbp_login_find_by_lun(sess, se_lun);
+		if (login) {
+			pr_notice("initiator already logged-in\n");
+
+			/*
+			 * SBP-2 R4 says we should return access denied, but
+			 * that can confuse initiators. Instead we need to
+			 * treat this like a reconnect, but send the login
+			 * response block like a fresh login.
+			 *
+			 * This is required particularly in the case of Apple
+			 * devices booting off the FireWire target, where
+			 * the firmware has an active login to the target. When
+			 * the OS takes control of the session it issues its own
+			 * LOGIN rather than a RECONNECT. To avoid the machine
+			 * waiting until the reconnect_hold expires, we can skip
+			 * the ACCESS_DENIED errors to speed things up.
+			 */
+
+			goto already_logged_in;
+		}
+	}
+
+	/*
+	 * check exclusive bit in login request
+	 * reject with access_denied if any logins present
+	 */
+	if (LOGIN_ORB_EXCLUSIVE(be32_to_cpu(req->orb.misc)) &&
+			sbp_login_count_all_by_lun(tpg, se_lun, 0)) {
+		pr_warn("refusing exclusive login with other active logins\n");
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED));
+		return;
+	}
+
+	/*
+	 * check exclusive bit in any existing login descriptor
+	 * reject with access_denied if any exclusive logins present
+	 */
+	if (sbp_login_count_all_by_lun(tpg, se_lun, 1)) {
+		pr_warn("refusing login while another exclusive login present\n");
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED));
+		return;
+	}
+
+	/*
+	 * check we haven't exceeded the number of allowed logins
+	 * reject with resources_unavailable if we have
+	 */
+	if (sbp_login_count_all_by_lun(tpg, se_lun, 0) >=
+			tport->max_logins_per_lun) {
+		pr_warn("max number of logins reached\n");
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_RESOURCES_UNAVAIL));
+		return;
+	}
+
+	if (!sess) {
+		sess = sbp_session_create(tpg, guid);
+		if (IS_ERR(sess)) {
+			switch (PTR_ERR(sess)) {
+			case -EPERM:
+				ret = SBP_STATUS_ACCESS_DENIED;
+				break;
+			default:
+				ret = SBP_STATUS_RESOURCES_UNAVAIL;
+				break;
+			}
+
+			req->status.status = cpu_to_be32(
+				STATUS_BLOCK_RESP(
+					STATUS_RESP_REQUEST_COMPLETE) |
+				STATUS_BLOCK_SBP_STATUS(ret));
+			return;
+		}
+
+		sess->node_id = req->node_addr;
+		sess->card = fw_card_get(req->card);
+		sess->generation = req->generation;
+		sess->speed = req->speed;
+
+		schedule_delayed_work(&sess->maint_work,
+				SESSION_MAINTENANCE_INTERVAL);
+	}
+
+	/* only take the latest reconnect_hold into account */
+	sess->reconnect_hold = min(
+		1 << LOGIN_ORB_RECONNECT(be32_to_cpu(req->orb.misc)),
+		tport->max_reconnect_timeout) - 1;
+
+	login = kmalloc(sizeof(*login), GFP_KERNEL);
+	if (!login) {
+		pr_err("failed to allocate login descriptor\n");
+
+		sbp_session_release(sess, true);
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_RESOURCES_UNAVAIL));
+		return;
+	}
+
+	login->sess = sess;
+	login->lun = se_lun;
+	login->status_fifo_addr = sbp2_pointer_to_addr(&req->orb.status_fifo);
+	login->exclusive = LOGIN_ORB_EXCLUSIVE(be32_to_cpu(req->orb.misc));
+	login->login_id = atomic_inc_return(&login_id);
+
+	login->tgt_agt = sbp_target_agent_register(login);
+	if (IS_ERR(login->tgt_agt)) {
+		ret = PTR_ERR(login->tgt_agt);
+		pr_err("failed to map command block handler: %d\n", ret);
+
+		sbp_session_release(sess, true);
+		kfree(login);
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_RESOURCES_UNAVAIL));
+		return;
+	}
+
+	spin_lock_bh(&sess->lock);
+	list_add_tail(&login->link, &sess->login_list);
+	spin_unlock_bh(&sess->lock);
+
+already_logged_in:
+	response = kzalloc(sizeof(*response), GFP_KERNEL);
+	if (!response) {
+		pr_err("failed to allocate login response block\n");
+
+		sbp_login_release(login, true);
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_RESOURCES_UNAVAIL));
+		return;
+	}
+
+	login_response_len = clamp_val(
+			LOGIN_ORB_RESPONSE_LENGTH(be32_to_cpu(req->orb.length)),
+			12, sizeof(*response));
+	response->misc = cpu_to_be32(
+		((login_response_len & 0xffff) << 16) |
+		(login->login_id & 0xffff));
+	response->reconnect_hold = cpu_to_be32(sess->reconnect_hold & 0xffff);
+	addr_to_sbp2_pointer(login->tgt_agt->handler.offset,
+		&response->command_block_agent);
+
+	ret = sbp_run_transaction(sess->card, TCODE_WRITE_BLOCK_REQUEST,
+		sess->node_id, sess->generation, sess->speed,
+		sbp2_pointer_to_addr(&req->orb.ptr2), response,
+		login_response_len);
+	if (ret != RCODE_COMPLETE) {
+		pr_debug("failed to write login response block: %x\n", ret);
+
+		kfree(response);
+		sbp_login_release(login, true);
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+		return;
+	}
+
+	kfree(response);
+
+	req->status.status = cpu_to_be32(
+		STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+		STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK));
+}
+
+static void sbp_management_request_query_logins(
+	struct sbp_management_agent *agent, struct sbp_management_request *req,
+	int *status_data_size)
+{
+	pr_notice("QUERY LOGINS not implemented\n");
+	/* FIXME: implement */
+
+	req->status.status = cpu_to_be32(
+		STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+		STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+}
+
+static void sbp_management_request_reconnect(
+	struct sbp_management_agent *agent, struct sbp_management_request *req,
+	int *status_data_size)
+{
+	struct sbp_tport *tport = agent->tport;
+	struct sbp_tpg *tpg = tport->tpg;
+	int ret;
+	u64 guid;
+	struct sbp_login_descriptor *login;
+
+	ret = read_peer_guid(&guid, req);
+	if (ret != RCODE_COMPLETE) {
+		pr_warn("failed to read peer GUID: %d\n", ret);
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+		return;
+	}
+
+	pr_notice("mgt_agent RECONNECT from %016llx\n", guid);
+
+	login = sbp_login_find_by_id(tpg,
+		RECONNECT_ORB_LOGIN_ID(be32_to_cpu(req->orb.misc)));
+
+	if (!login) {
+		pr_err("mgt_agent RECONNECT unknown login ID\n");
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED));
+		return;
+	}
+
+	if (login->sess->guid != guid) {
+		pr_err("mgt_agent RECONNECT login GUID doesn't match\n");
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED));
+		return;
+	}
+
+	spin_lock_bh(&login->sess->lock);
+	if (login->sess->card)
+		fw_card_put(login->sess->card);
+
+	/* update the node details */
+	login->sess->generation = req->generation;
+	login->sess->node_id = req->node_addr;
+	login->sess->card = fw_card_get(req->card);
+	login->sess->speed = req->speed;
+	spin_unlock_bh(&login->sess->lock);
+
+	req->status.status = cpu_to_be32(
+		STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+		STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK));
+}
+
+static void sbp_management_request_logout(
+	struct sbp_management_agent *agent, struct sbp_management_request *req,
+	int *status_data_size)
+{
+	struct sbp_tport *tport = agent->tport;
+	struct sbp_tpg *tpg = tport->tpg;
+	int login_id;
+	struct sbp_login_descriptor *login;
+
+	login_id = LOGOUT_ORB_LOGIN_ID(be32_to_cpu(req->orb.misc));
+
+	login = sbp_login_find_by_id(tpg, login_id);
+	if (!login) {
+		pr_warn("cannot find login: %d\n", login_id);
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_LOGIN_ID_UNKNOWN));
+		return;
+	}
+
+	pr_info("mgt_agent LOGOUT from LUN %d session %d\n",
+		login->lun->unpacked_lun, login->login_id);
+
+	if (req->node_addr != login->sess->node_id) {
+		pr_warn("logout from different node ID\n");
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED));
+		return;
+	}
+
+	sbp_login_release(login, true);
+
+	req->status.status = cpu_to_be32(
+		STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+		STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK));
+}
+
+static void session_check_for_reset(struct sbp_session *sess)
+{
+	bool card_valid = false;
+
+	spin_lock_bh(&sess->lock);
+
+	if (sess->card) {
+		spin_lock_irq(&sess->card->lock);
+		card_valid = (sess->card->local_node != NULL);
+		spin_unlock_irq(&sess->card->lock);
+
+		if (!card_valid) {
+			fw_card_put(sess->card);
+			sess->card = NULL;
+		}
+	}
+
+	if (!card_valid || (sess->generation != sess->card->generation)) {
+		pr_info("Waiting for reconnect from node: %016llx\n",
+				sess->guid);
+
+		sess->node_id = -1;
+		sess->reconnect_expires = get_jiffies_64() +
+			((sess->reconnect_hold + 1) * HZ);
+	}
+
+	spin_unlock_bh(&sess->lock);
+}
+
+static void session_reconnect_expired(struct sbp_session *sess)
+{
+	struct sbp_login_descriptor *login, *temp;
+	LIST_HEAD(login_list);
+
+	pr_info("Reconnect timer expired for node: %016llx\n", sess->guid);
+
+	spin_lock_bh(&sess->lock);
+	list_for_each_entry_safe(login, temp, &sess->login_list, link) {
+		login->sess = NULL;
+		list_del(&login->link);
+		list_add_tail(&login->link, &login_list);
+	}
+	spin_unlock_bh(&sess->lock);
+
+	list_for_each_entry_safe(login, temp, &login_list, link) {
+		list_del(&login->link);
+		sbp_login_release(login, false);
+	}
+
+	sbp_session_release(sess, false);
+}
+
+static void session_maintenance_work(struct work_struct *work)
+{
+	struct sbp_session *sess = container_of(work, struct sbp_session,
+			maint_work.work);
+
+	/* could be called while tearing down the session */
+	spin_lock_bh(&sess->lock);
+	if (list_empty(&sess->login_list)) {
+		spin_unlock_bh(&sess->lock);
+		return;
+	}
+	spin_unlock_bh(&sess->lock);
+
+	if (sess->node_id != -1) {
+		/* check for bus reset and make node_id invalid */
+		session_check_for_reset(sess);
+
+		schedule_delayed_work(&sess->maint_work,
+				SESSION_MAINTENANCE_INTERVAL);
+	} else if (!time_after64(get_jiffies_64(), sess->reconnect_expires)) {
+		/* still waiting for reconnect */
+		schedule_delayed_work(&sess->maint_work,
+				SESSION_MAINTENANCE_INTERVAL);
+	} else {
+		/* reconnect timeout has expired */
+		session_reconnect_expired(sess);
+	}
+}
+
+static int tgt_agent_rw_agent_state(struct fw_card *card, int tcode, void *data,
+		struct sbp_target_agent *agent)
+{
+	__be32 state;
+
+	switch (tcode) {
+	case TCODE_READ_QUADLET_REQUEST:
+		pr_debug("tgt_agent AGENT_STATE READ\n");
+
+		spin_lock_bh(&agent->lock);
+		state = cpu_to_be32(agent->state);
+		spin_unlock_bh(&agent->lock);
+		memcpy(data, &state, sizeof(state));
+
+		return RCODE_COMPLETE;
+
+	case TCODE_WRITE_QUADLET_REQUEST:
+		/* ignored */
+		return RCODE_COMPLETE;
+
+	default:
+		return RCODE_TYPE_ERROR;
+	}
+}
+
+static int tgt_agent_rw_agent_reset(struct fw_card *card, int tcode, void *data,
+		struct sbp_target_agent *agent)
+{
+	switch (tcode) {
+	case TCODE_WRITE_QUADLET_REQUEST:
+		pr_debug("tgt_agent AGENT_RESET\n");
+		spin_lock_bh(&agent->lock);
+		agent->state = AGENT_STATE_RESET;
+		spin_unlock_bh(&agent->lock);
+		return RCODE_COMPLETE;
+
+	default:
+		return RCODE_TYPE_ERROR;
+	}
+}
+
+static int tgt_agent_rw_orb_pointer(struct fw_card *card, int tcode, void *data,
+		struct sbp_target_agent *agent)
+{
+	struct sbp2_pointer *ptr = data;
+
+	switch (tcode) {
+	case TCODE_WRITE_BLOCK_REQUEST:
+		spin_lock_bh(&agent->lock);
+		if (agent->state != AGENT_STATE_SUSPENDED &&
+				agent->state != AGENT_STATE_RESET) {
+			spin_unlock_bh(&agent->lock);
+			pr_notice("Ignoring ORB_POINTER write while active.\n");
+			return RCODE_CONFLICT_ERROR;
+		}
+		agent->state = AGENT_STATE_ACTIVE;
+		spin_unlock_bh(&agent->lock);
+
+		agent->orb_pointer = sbp2_pointer_to_addr(ptr);
+		agent->doorbell = false;
+
+		pr_debug("tgt_agent ORB_POINTER write: 0x%llx\n",
+				agent->orb_pointer);
+
+		queue_work(system_unbound_wq, &agent->work);
+
+		return RCODE_COMPLETE;
+
+	case TCODE_READ_BLOCK_REQUEST:
+		pr_debug("tgt_agent ORB_POINTER READ\n");
+		spin_lock_bh(&agent->lock);
+		addr_to_sbp2_pointer(agent->orb_pointer, ptr);
+		spin_unlock_bh(&agent->lock);
+		return RCODE_COMPLETE;
+
+	default:
+		return RCODE_TYPE_ERROR;
+	}
+}
+
+static int tgt_agent_rw_doorbell(struct fw_card *card, int tcode, void *data,
+		struct sbp_target_agent *agent)
+{
+	switch (tcode) {
+	case TCODE_WRITE_QUADLET_REQUEST:
+		spin_lock_bh(&agent->lock);
+		if (agent->state != AGENT_STATE_SUSPENDED) {
+			spin_unlock_bh(&agent->lock);
+			pr_debug("Ignoring DOORBELL while active.\n");
+			return RCODE_CONFLICT_ERROR;
+		}
+		agent->state = AGENT_STATE_ACTIVE;
+		spin_unlock_bh(&agent->lock);
+
+		agent->doorbell = true;
+
+		pr_debug("tgt_agent DOORBELL\n");
+
+		queue_work(system_unbound_wq, &agent->work);
+
+		return RCODE_COMPLETE;
+
+	case TCODE_READ_QUADLET_REQUEST:
+		return RCODE_COMPLETE;
+
+	default:
+		return RCODE_TYPE_ERROR;
+	}
+}
+
+static int tgt_agent_rw_unsolicited_status_enable(struct fw_card *card,
+		int tcode, void *data, struct sbp_target_agent *agent)
+{
+	switch (tcode) {
+	case TCODE_WRITE_QUADLET_REQUEST:
+		pr_debug("tgt_agent UNSOLICITED_STATUS_ENABLE\n");
+		/* ignored as we don't send unsolicited status */
+		return RCODE_COMPLETE;
+
+	case TCODE_READ_QUADLET_REQUEST:
+		return RCODE_COMPLETE;
+
+	default:
+		return RCODE_TYPE_ERROR;
+	}
+}
+
+static void tgt_agent_rw(struct fw_card *card, struct fw_request *request,
+		int tcode, int destination, int source, int generation,
+		unsigned long long offset, void *data, size_t length,
+		void *callback_data)
+{
+	struct sbp_target_agent *agent = callback_data;
+	struct sbp_session *sess = agent->login->sess;
+	int sess_gen, sess_node, rcode;
+
+	spin_lock_bh(&sess->lock);
+	sess_gen = sess->generation;
+	sess_node = sess->node_id;
+	spin_unlock_bh(&sess->lock);
+
+	if (generation != sess_gen) {
+		pr_notice("ignoring request with wrong generation\n");
+		rcode = RCODE_TYPE_ERROR;
+		goto out;
+	}
+
+	if (source != sess_node) {
+		pr_notice("ignoring request from foreign node (%x != %x)\n",
+				source, sess_node);
+		rcode = RCODE_TYPE_ERROR;
+		goto out;
+	}
+
+	/* turn offset into the offset from the start of the block */
+	offset -= agent->handler.offset;
+
+	if (offset == 0x00 && length == 4) {
+		/* AGENT_STATE */
+		rcode = tgt_agent_rw_agent_state(card, tcode, data, agent);
+	} else if (offset == 0x04 && length == 4) {
+		/* AGENT_RESET */
+		rcode = tgt_agent_rw_agent_reset(card, tcode, data, agent);
+	} else if (offset == 0x08 && length == 8) {
+		/* ORB_POINTER */
+		rcode = tgt_agent_rw_orb_pointer(card, tcode, data, agent);
+	} else if (offset == 0x10 && length == 4) {
+		/* DOORBELL */
+		rcode = tgt_agent_rw_doorbell(card, tcode, data, agent);
+	} else if (offset == 0x14 && length == 4) {
+		/* UNSOLICITED_STATUS_ENABLE */
+		rcode = tgt_agent_rw_unsolicited_status_enable(card, tcode,
+				data, agent);
+	} else {
+		rcode = RCODE_ADDRESS_ERROR;
+	}
+
+out:
+	fw_send_response(card, request, rcode);
+}
+
+static void sbp_handle_command(struct sbp_target_request *);
+static int sbp_send_status(struct sbp_target_request *);
+static void sbp_free_request(struct sbp_target_request *);
+
+static void tgt_agent_process_work(struct work_struct *work)
+{
+	struct sbp_target_request *req =
+		container_of(work, struct sbp_target_request, work);
+
+	pr_debug("tgt_orb ptr:0x%llx next_ORB:0x%llx data_descriptor:0x%llx misc:0x%x\n",
+			req->orb_pointer,
+			sbp2_pointer_to_addr(&req->orb.next_orb),
+			sbp2_pointer_to_addr(&req->orb.data_descriptor),
+			be32_to_cpu(req->orb.misc));
+
+	if (req->orb_pointer >> 32)
+		pr_debug("ORB with high bits set\n");
+
+	switch (ORB_REQUEST_FORMAT(be32_to_cpu(req->orb.misc))) {
+		case 0:/* Format specified by this standard */
+			sbp_handle_command(req);
+			return;
+		case 1: /* Reserved for future standardization */
+		case 2: /* Vendor-dependent */
+			req->status.status |= cpu_to_be32(
+					STATUS_BLOCK_RESP(
+						STATUS_RESP_REQUEST_COMPLETE) |
+					STATUS_BLOCK_DEAD(0) |
+					STATUS_BLOCK_LEN(1) |
+					STATUS_BLOCK_SBP_STATUS(
+						SBP_STATUS_REQ_TYPE_NOTSUPP));
+			sbp_send_status(req);
+			sbp_free_request(req);
+			return;
+		case 3: /* Dummy ORB */
+			req->status.status |= cpu_to_be32(
+					STATUS_BLOCK_RESP(
+						STATUS_RESP_REQUEST_COMPLETE) |
+					STATUS_BLOCK_DEAD(0) |
+					STATUS_BLOCK_LEN(1) |
+					STATUS_BLOCK_SBP_STATUS(
+						SBP_STATUS_DUMMY_ORB_COMPLETE));
+			sbp_send_status(req);
+			sbp_free_request(req);
+			return;
+		default:
+			BUG();
+	}
+}
+
+/* used to double-check we haven't been issued an AGENT_RESET */
+static inline bool tgt_agent_check_active(struct sbp_target_agent *agent)
+{
+	bool active;
+
+	spin_lock_bh(&agent->lock);
+	active = (agent->state == AGENT_STATE_ACTIVE);
+	spin_unlock_bh(&agent->lock);
+
+	return active;
+}
+
+static void tgt_agent_fetch_work(struct work_struct *work)
+{
+	struct sbp_target_agent *agent =
+		container_of(work, struct sbp_target_agent, work);
+	struct sbp_session *sess = agent->login->sess;
+	struct sbp_target_request *req;
+	int ret;
+	bool doorbell = agent->doorbell;
+	u64 next_orb = agent->orb_pointer;
+
+	while (next_orb && tgt_agent_check_active(agent)) {
+		req = kzalloc(sizeof(*req), GFP_KERNEL);
+		if (!req) {
+			spin_lock_bh(&agent->lock);
+			agent->state = AGENT_STATE_DEAD;
+			spin_unlock_bh(&agent->lock);
+			return;
+		}
+
+		req->login = agent->login;
+		req->orb_pointer = next_orb;
+
+		req->status.status = cpu_to_be32(STATUS_BLOCK_ORB_OFFSET_HIGH(
+					req->orb_pointer >> 32));
+		req->status.orb_low = cpu_to_be32(
+				req->orb_pointer & 0xfffffffc);
+
+		/* read in the ORB */
+		ret = sbp_run_transaction(sess->card, TCODE_READ_BLOCK_REQUEST,
+				sess->node_id, sess->generation, sess->speed,
+				req->orb_pointer, &req->orb, sizeof(req->orb));
+		if (ret != RCODE_COMPLETE) {
+			pr_debug("tgt_orb fetch failed: %x\n", ret);
+			req->status.status |= cpu_to_be32(
+					STATUS_BLOCK_SRC(
+						STATUS_SRC_ORB_FINISHED) |
+					STATUS_BLOCK_RESP(
+						STATUS_RESP_TRANSPORT_FAILURE) |
+					STATUS_BLOCK_DEAD(1) |
+					STATUS_BLOCK_LEN(1) |
+					STATUS_BLOCK_SBP_STATUS(
+						SBP_STATUS_UNSPECIFIED_ERROR));
+			spin_lock_bh(&agent->lock);
+			agent->state = AGENT_STATE_DEAD;
+			spin_unlock_bh(&agent->lock);
+
+			sbp_send_status(req);
+			sbp_free_request(req);
+			return;
+		}
+
+		/* check the next_ORB field */
+		if (be32_to_cpu(req->orb.next_orb.high) & 0x80000000) {
+			next_orb = 0;
+			req->status.status |= cpu_to_be32(STATUS_BLOCK_SRC(
+						STATUS_SRC_ORB_FINISHED));
+		} else {
+			next_orb = sbp2_pointer_to_addr(&req->orb.next_orb);
+			req->status.status |= cpu_to_be32(STATUS_BLOCK_SRC(
+						STATUS_SRC_ORB_CONTINUING));
+		}
+
+		if (tgt_agent_check_active(agent) && !doorbell) {
+			INIT_WORK(&req->work, tgt_agent_process_work);
+			queue_work(system_unbound_wq, &req->work);
+		} else {
+			/* don't process this request, just check next_ORB */
+			sbp_free_request(req);
+		}
+
+		spin_lock_bh(&agent->lock);
+		doorbell = agent->doorbell = false;
+
+		/* check if we should carry on processing */
+		if (next_orb)
+			agent->orb_pointer = next_orb;
+		else
+			agent->state = AGENT_STATE_SUSPENDED;
+
+		spin_unlock_bh(&agent->lock);
+	};
+}
+
+static struct sbp_target_agent *sbp_target_agent_register(
+		struct sbp_login_descriptor *login)
+{
+	struct sbp_target_agent *agent;
+	int ret;
+
+	agent = kmalloc(sizeof(*agent), GFP_KERNEL);
+	if (!agent)
+		return ERR_PTR(-ENOMEM);
+
+	spin_lock_init(&agent->lock);
+
+	agent->handler.length = 0x20;
+	agent->handler.address_callback = tgt_agent_rw;
+	agent->handler.callback_data = agent;
+
+	agent->login = login;
+	agent->state = AGENT_STATE_RESET;
+	INIT_WORK(&agent->work, tgt_agent_fetch_work);
+	agent->orb_pointer = 0;
+	agent->doorbell = false;
+
+	ret = fw_core_add_address_handler(&agent->handler,
+			&sbp_register_region);
+	if (ret < 0) {
+		kfree(agent);
+		return ERR_PTR(ret);
+	}
+
+	return agent;
+}
+
+static void sbp_target_agent_unregister(struct sbp_target_agent *agent)
+{
+	fw_core_remove_address_handler(&agent->handler);
+	cancel_work_sync(&agent->work);
+	kfree(agent);
+}
+
+/*
+ * Simple wrapper around fw_run_transaction that retries the transaction several
+ * times in case of failure, with an exponential backoff.
+ */
+static int sbp_run_transaction(struct fw_card *card, int tcode, int destination_id,
+		int generation, int speed, unsigned long long offset,
+		void *payload, size_t length)
+{
+	int attempt, ret, delay;
+
+	for (attempt = 1; attempt <= 5; attempt++) {
+		ret = fw_run_transaction(card, tcode, destination_id,
+				generation, speed, offset, payload, length);
+
+		switch (ret) {
+		case RCODE_COMPLETE:
+		case RCODE_TYPE_ERROR:
+		case RCODE_ADDRESS_ERROR:
+		case RCODE_GENERATION:
+			return ret;
+
+		default:
+			delay = 5 * attempt * attempt;
+			usleep_range(delay, delay * 2);
+		}
+	}
+
+	return ret;
+}
+
+/*
+ * Wrapper around sbp_run_transaction that gets the card, destination,
+ * generation and speed out of the request's session.
+ */
+static int sbp_run_request_transaction(struct sbp_target_request *req,
+		int tcode, unsigned long long offset, void *payload,
+		size_t length)
+{
+	struct sbp_login_descriptor *login = req->login;
+	struct sbp_session *sess = login->sess;
+	struct fw_card *card;
+	int node_id, generation, speed, ret;
+
+	spin_lock_bh(&sess->lock);
+	card = fw_card_get(sess->card);
+	node_id = sess->node_id;
+	generation = sess->generation;
+	speed = sess->speed;
+	spin_unlock_bh(&sess->lock);
+
+	ret = sbp_run_transaction(card, tcode, node_id, generation, speed,
+			offset, payload, length);
+
+	fw_card_put(card);
+
+	return ret;
+}
+
+static int sbp_fetch_command(struct sbp_target_request *req)
+{
+	int ret, cmd_len, copy_len;
+
+	cmd_len = scsi_command_size(req->orb.command_block);
+
+	req->cmd_buf = kmalloc(cmd_len, GFP_KERNEL);
+	if (!req->cmd_buf)
+		return -ENOMEM;
+
+	memcpy(req->cmd_buf, req->orb.command_block,
+		min_t(int, cmd_len, sizeof(req->orb.command_block)));
+
+	if (cmd_len > sizeof(req->orb.command_block)) {
+		pr_debug("sbp_fetch_command: filling in long command\n");
+		copy_len = cmd_len - sizeof(req->orb.command_block);
+
+		ret = sbp_run_request_transaction(req,
+				TCODE_READ_BLOCK_REQUEST,
+				req->orb_pointer + sizeof(req->orb),
+				req->cmd_buf + sizeof(req->orb.command_block),
+				copy_len);
+		if (ret != RCODE_COMPLETE)
+			return -EIO;
+	}
+
+	return 0;
+}
+
+static int sbp_fetch_page_table(struct sbp_target_request *req)
+{
+	int pg_tbl_sz, ret;
+	struct sbp_page_table_entry *pg_tbl;
+
+	if (!CMDBLK_ORB_PG_TBL_PRESENT(be32_to_cpu(req->orb.misc)))
+		return 0;
+
+	pg_tbl_sz = CMDBLK_ORB_DATA_SIZE(be32_to_cpu(req->orb.misc)) *
+		sizeof(struct sbp_page_table_entry);
+
+	pg_tbl = kmalloc(pg_tbl_sz, GFP_KERNEL);
+	if (!pg_tbl)
+		return -ENOMEM;
+
+	ret = sbp_run_request_transaction(req, TCODE_READ_BLOCK_REQUEST,
+			sbp2_pointer_to_addr(&req->orb.data_descriptor),
+			pg_tbl, pg_tbl_sz);
+	if (ret != RCODE_COMPLETE) {
+		kfree(pg_tbl);
+		return -EIO;
+	}
+
+	req->pg_tbl = pg_tbl;
+	return 0;
+}
+
+static void sbp_calc_data_length_direction(struct sbp_target_request *req,
+	u32 *data_len, enum dma_data_direction *data_dir)
+{
+	int data_size, direction, idx;
+
+	data_size = CMDBLK_ORB_DATA_SIZE(be32_to_cpu(req->orb.misc));
+	direction = CMDBLK_ORB_DIRECTION(be32_to_cpu(req->orb.misc));
+
+	if (!data_size) {
+		*data_len = 0;
+		*data_dir = DMA_NONE;
+		return;
+	}
+
+	*data_dir = direction ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+
+	if (req->pg_tbl) {
+		*data_len = 0;
+		for (idx = 0; idx < data_size; idx++) {
+			*data_len += be16_to_cpu(
+					req->pg_tbl[idx].segment_length);
+		}
+	} else {
+		*data_len = data_size;
+	}
+}
+
+static void sbp_handle_command(struct sbp_target_request *req)
+{
+	struct sbp_login_descriptor *login = req->login;
+	struct sbp_session *sess = login->sess;
+	int ret, unpacked_lun;
+	u32 data_length;
+	enum dma_data_direction data_dir;
+
+	ret = sbp_fetch_command(req);
+	if (ret) {
+		pr_debug("sbp_handle_command: fetch command failed: %d\n", ret);
+		req->status.status |= cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+			STATUS_BLOCK_DEAD(0) |
+			STATUS_BLOCK_LEN(1) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+		sbp_send_status(req);
+		sbp_free_request(req);
+		return;
+	}
+
+	ret = sbp_fetch_page_table(req);
+	if (ret) {
+		pr_debug("sbp_handle_command: fetch page table failed: %d\n",
+			ret);
+		req->status.status |= cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+			STATUS_BLOCK_DEAD(0) |
+			STATUS_BLOCK_LEN(1) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+		sbp_send_status(req);
+		sbp_free_request(req);
+		return;
+	}
+
+	unpacked_lun = req->login->lun->unpacked_lun;
+	sbp_calc_data_length_direction(req, &data_length, &data_dir);
+
+	pr_debug("sbp_handle_command ORB:0x%llx unpacked_lun:%d data_len:%d data_dir:%d\n",
+			req->orb_pointer, unpacked_lun, data_length, data_dir);
+
+	target_submit_cmd(&req->se_cmd, sess->se_sess, req->cmd_buf,
+			req->sense_buf, unpacked_lun, data_length,
+			MSG_SIMPLE_TAG, data_dir, 0);
+}
+
+/*
+ * DMA_TO_DEVICE = read from initiator (SCSI WRITE)
+ * DMA_FROM_DEVICE = write to initiator (SCSI READ)
+ */
+static int sbp_rw_data(struct sbp_target_request *req)
+{
+	struct sbp_session *sess = req->login->sess;
+	int tcode, sg_miter_flags, max_payload, pg_size, speed, node_id,
+		generation, num_pte, length, tfr_length,
+		rcode = RCODE_COMPLETE;
+	struct sbp_page_table_entry *pte;
+	unsigned long long offset;
+	struct fw_card *card;
+	struct sg_mapping_iter iter;
+
+	if (req->se_cmd.data_direction == DMA_FROM_DEVICE) {
+		tcode = TCODE_WRITE_BLOCK_REQUEST;
+		sg_miter_flags = SG_MITER_FROM_SG;
+	} else {
+		tcode = TCODE_READ_BLOCK_REQUEST;
+		sg_miter_flags = SG_MITER_TO_SG;
+	}
+
+	max_payload = 4 << CMDBLK_ORB_MAX_PAYLOAD(be32_to_cpu(req->orb.misc));
+	speed = CMDBLK_ORB_SPEED(be32_to_cpu(req->orb.misc));
+
+	pg_size = CMDBLK_ORB_PG_SIZE(be32_to_cpu(req->orb.misc));
+	if (pg_size) {
+		pr_err("sbp_run_transaction: page size ignored\n");
+		pg_size = 0x100 << pg_size;
+	}
+
+	spin_lock_bh(&sess->lock);
+	card = fw_card_get(sess->card);
+	node_id = sess->node_id;
+	generation = sess->generation;
+	spin_unlock_bh(&sess->lock);
+
+	if (req->pg_tbl) {
+		pte = req->pg_tbl;
+		num_pte = CMDBLK_ORB_DATA_SIZE(be32_to_cpu(req->orb.misc));
+
+		offset = 0;
+		length = 0;
+	} else {
+		pte = NULL;
+		num_pte = 0;
+
+		offset = sbp2_pointer_to_addr(&req->orb.data_descriptor);
+		length = req->se_cmd.data_length;
+	}
+
+	sg_miter_start(&iter, req->se_cmd.t_data_sg, req->se_cmd.t_data_nents,
+		sg_miter_flags);
+
+	while (length || num_pte) {
+		if (!length) {
+			offset = (u64)be16_to_cpu(pte->segment_base_hi) << 32 |
+				be32_to_cpu(pte->segment_base_lo);
+			length = be16_to_cpu(pte->segment_length);
+
+			pte++;
+			num_pte--;
+		}
+
+		sg_miter_next(&iter);
+
+		tfr_length = min3(length, max_payload, (int)iter.length);
+
+		/* FIXME: take page_size into account */
+
+		rcode = sbp_run_transaction(card, tcode, node_id,
+				generation, speed,
+				offset, iter.addr, tfr_length);
+
+		if (rcode != RCODE_COMPLETE)
+			break;
+
+		length -= tfr_length;
+		offset += tfr_length;
+		iter.consumed = tfr_length;
+	}
+
+	sg_miter_stop(&iter);
+	fw_card_put(card);
+
+	if (rcode == RCODE_COMPLETE) {
+		WARN_ON(length != 0);
+		return 0;
+	} else {
+		return -EIO;
+	}
+}
+
+static int sbp_send_status(struct sbp_target_request *req)
+{
+	int ret, length;
+	struct sbp_login_descriptor *login = req->login;
+
+	length = (((be32_to_cpu(req->status.status) >> 24) & 0x07) + 1) * 4;
+
+	ret = sbp_run_request_transaction(req, TCODE_WRITE_BLOCK_REQUEST,
+			login->status_fifo_addr, &req->status, length);
+	if (ret != RCODE_COMPLETE) {
+		pr_debug("sbp_send_status: write failed: 0x%x\n", ret);
+		return -EIO;
+	}
+
+	pr_debug("sbp_send_status: status write complete for ORB: 0x%llx\n",
+			req->orb_pointer);
+
+	return 0;
+}
+
+static void sbp_sense_mangle(struct sbp_target_request *req)
+{
+	struct se_cmd *se_cmd = &req->se_cmd;
+	u8 *sense = req->sense_buf;
+	u8 *status = req->status.data;
+
+	WARN_ON(se_cmd->scsi_sense_length < 18);
+
+	switch (sense[0] & 0x7f) { 		/* sfmt */
+	case 0x70: /* current, fixed */
+		status[0] = 0 << 6;
+		break;
+	case 0x71: /* deferred, fixed */
+		status[0] = 1 << 6;
+		break;
+	case 0x72: /* current, descriptor */
+	case 0x73: /* deferred, descriptor */
+	default:
+		/*
+		 * TODO: SBP-3 specifies what we should do with descriptor
+		 * format sense data
+		 */
+		pr_err("sbp_send_sense: unknown sense format: 0x%x\n",
+			sense[0]);
+		req->status.status |= cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_DEAD(0) |
+			STATUS_BLOCK_LEN(1) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQUEST_ABORTED));
+		return;
+	}
+
+	status[0] |= se_cmd->scsi_status & 0x3f;/* status */
+	status[1] =
+		(sense[0] & 0x80) |		/* valid */
+		((sense[2] & 0xe0) >> 1) |	/* mark, eom, ili */
+		(sense[2] & 0x0f);		/* sense_key */
+	status[2] = se_cmd->scsi_asc;		/* sense_code */
+	status[3] = se_cmd->scsi_ascq;		/* sense_qualifier */
+
+	/* information */
+	status[4] = sense[3];
+	status[5] = sense[4];
+	status[6] = sense[5];
+	status[7] = sense[6];
+
+	/* CDB-dependent */
+	status[8] = sense[8];
+	status[9] = sense[9];
+	status[10] = sense[10];
+	status[11] = sense[11];
+
+	/* fru */
+	status[12] = sense[14];
+
+	/* sense_key-dependent */
+	status[13] = sense[15];
+	status[14] = sense[16];
+	status[15] = sense[17];
+
+	req->status.status |= cpu_to_be32(
+		STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+		STATUS_BLOCK_DEAD(0) |
+		STATUS_BLOCK_LEN(5) |
+		STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK));
+}
+
+static int sbp_send_sense(struct sbp_target_request *req)
+{
+	struct se_cmd *se_cmd = &req->se_cmd;
+
+	if (se_cmd->scsi_sense_length) {
+		sbp_sense_mangle(req);
+	} else {
+		req->status.status |= cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_DEAD(0) |
+			STATUS_BLOCK_LEN(1) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK));
+	}
+
+	return sbp_send_status(req);
+}
+
+static void sbp_free_request(struct sbp_target_request *req)
+{
+	kfree(req->pg_tbl);
+	kfree(req->cmd_buf);
+	kfree(req);
+}
+
+static void sbp_mgt_agent_process(struct work_struct *work)
+{
+	struct sbp_management_agent *agent =
+		container_of(work, struct sbp_management_agent, work);
+	struct sbp_management_request *req = agent->request;
+	int ret;
+	int status_data_len = 0;
+
+	/* fetch the ORB from the initiator */
+	ret = sbp_run_transaction(req->card, TCODE_READ_BLOCK_REQUEST,
+		req->node_addr, req->generation, req->speed,
+		agent->orb_offset, &req->orb, sizeof(req->orb));
+	if (ret != RCODE_COMPLETE) {
+		pr_debug("mgt_orb fetch failed: %x\n", ret);
+		goto out;
+	}
+
+	pr_debug("mgt_orb ptr1:0x%llx ptr2:0x%llx misc:0x%x len:0x%x status_fifo:0x%llx\n",
+		sbp2_pointer_to_addr(&req->orb.ptr1),
+		sbp2_pointer_to_addr(&req->orb.ptr2),
+		be32_to_cpu(req->orb.misc), be32_to_cpu(req->orb.length),
+		sbp2_pointer_to_addr(&req->orb.status_fifo));
+
+	if (!ORB_NOTIFY(be32_to_cpu(req->orb.misc)) ||
+		ORB_REQUEST_FORMAT(be32_to_cpu(req->orb.misc)) != 0) {
+		pr_err("mgt_orb bad request\n");
+		goto out;
+	}
+
+	switch (MANAGEMENT_ORB_FUNCTION(be32_to_cpu(req->orb.misc))) {
+	case MANAGEMENT_ORB_FUNCTION_LOGIN:
+		sbp_management_request_login(agent, req, &status_data_len);
+		break;
+
+	case MANAGEMENT_ORB_FUNCTION_QUERY_LOGINS:
+		sbp_management_request_query_logins(agent, req,
+				&status_data_len);
+		break;
+
+	case MANAGEMENT_ORB_FUNCTION_RECONNECT:
+		sbp_management_request_reconnect(agent, req, &status_data_len);
+		break;
+
+	case MANAGEMENT_ORB_FUNCTION_SET_PASSWORD:
+		pr_notice("SET PASSWORD not implemented\n");
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+
+		break;
+
+	case MANAGEMENT_ORB_FUNCTION_LOGOUT:
+		sbp_management_request_logout(agent, req, &status_data_len);
+		break;
+
+	case MANAGEMENT_ORB_FUNCTION_ABORT_TASK:
+		pr_notice("ABORT TASK not implemented\n");
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+
+		break;
+
+	case MANAGEMENT_ORB_FUNCTION_ABORT_TASK_SET:
+		pr_notice("ABORT TASK SET not implemented\n");
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+
+		break;
+
+	case MANAGEMENT_ORB_FUNCTION_LOGICAL_UNIT_RESET:
+		pr_notice("LOGICAL UNIT RESET not implemented\n");
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+
+		break;
+
+	case MANAGEMENT_ORB_FUNCTION_TARGET_RESET:
+		pr_notice("TARGET RESET not implemented\n");
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+
+		break;
+
+	default:
+		pr_notice("unknown management function 0x%x\n",
+			MANAGEMENT_ORB_FUNCTION(be32_to_cpu(req->orb.misc)));
+
+		req->status.status = cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+
+		break;
+	}
+
+	req->status.status |= cpu_to_be32(
+		STATUS_BLOCK_SRC(1) | /* Response to ORB, next_ORB absent */
+		STATUS_BLOCK_LEN(DIV_ROUND_UP(status_data_len, 4) + 1) |
+		STATUS_BLOCK_ORB_OFFSET_HIGH(agent->orb_offset >> 32));
+	req->status.orb_low = cpu_to_be32(agent->orb_offset);
+
+	/* write the status block back to the initiator */
+	ret = sbp_run_transaction(req->card, TCODE_WRITE_BLOCK_REQUEST,
+		req->node_addr, req->generation, req->speed,
+		sbp2_pointer_to_addr(&req->orb.status_fifo),
+		&req->status, 8 + status_data_len);
+	if (ret != RCODE_COMPLETE) {
+		pr_debug("mgt_orb status write failed: %x\n", ret);
+		goto out;
+	}
+
+out:
+	fw_card_put(req->card);
+	kfree(req);
+
+	spin_lock_bh(&agent->lock);
+	agent->state = MANAGEMENT_AGENT_STATE_IDLE;
+	spin_unlock_bh(&agent->lock);
+}
+
+static void sbp_mgt_agent_rw(struct fw_card *card,
+	struct fw_request *request, int tcode, int destination, int source,
+	int generation, unsigned long long offset, void *data, size_t length,
+	void *callback_data)
+{
+	struct sbp_management_agent *agent = callback_data;
+	struct sbp2_pointer *ptr = data;
+	int rcode = RCODE_ADDRESS_ERROR;
+
+	if (!agent->tport->enable)
+		goto out;
+
+	if ((offset != agent->handler.offset) || (length != 8))
+		goto out;
+
+	if (tcode == TCODE_WRITE_BLOCK_REQUEST) {
+		struct sbp_management_request *req;
+		int prev_state;
+
+		spin_lock_bh(&agent->lock);
+		prev_state = agent->state;
+		agent->state = MANAGEMENT_AGENT_STATE_BUSY;
+		spin_unlock_bh(&agent->lock);
+
+		if (prev_state == MANAGEMENT_AGENT_STATE_BUSY) {
+			pr_notice("ignoring management request while busy\n");
+			rcode = RCODE_CONFLICT_ERROR;
+			goto out;
+		}
+
+		req = kzalloc(sizeof(*req), GFP_ATOMIC);
+		if (!req) {
+			rcode = RCODE_CONFLICT_ERROR;
+			goto out;
+		}
+
+		req->card = fw_card_get(card);
+		req->generation = generation;
+		req->node_addr = source;
+		req->speed = fw_get_request_speed(request);
+
+		agent->orb_offset = sbp2_pointer_to_addr(ptr);
+		agent->request = req;
+
+		queue_work(system_unbound_wq, &agent->work);
+		rcode = RCODE_COMPLETE;
+	} else if (tcode == TCODE_READ_BLOCK_REQUEST) {
+		addr_to_sbp2_pointer(agent->orb_offset, ptr);
+		rcode = RCODE_COMPLETE;
+	} else {
+		rcode = RCODE_TYPE_ERROR;
+	}
+
+out:
+	fw_send_response(card, request, rcode);
+}
+
+static struct sbp_management_agent *sbp_management_agent_register(
+		struct sbp_tport *tport)
+{
+	int ret;
+	struct sbp_management_agent *agent;
+
+	agent = kmalloc(sizeof(*agent), GFP_KERNEL);
+	if (!agent)
+		return ERR_PTR(-ENOMEM);
+
+	spin_lock_init(&agent->lock);
+	agent->tport = tport;
+	agent->handler.length = 0x08;
+	agent->handler.address_callback = sbp_mgt_agent_rw;
+	agent->handler.callback_data = agent;
+	agent->state = MANAGEMENT_AGENT_STATE_IDLE;
+	INIT_WORK(&agent->work, sbp_mgt_agent_process);
+	agent->orb_offset = 0;
+	agent->request = NULL;
+
+	ret = fw_core_add_address_handler(&agent->handler,
+			&sbp_register_region);
+	if (ret < 0) {
+		kfree(agent);
+		return ERR_PTR(ret);
+	}
+
+	return agent;
+}
+
+static void sbp_management_agent_unregister(struct sbp_management_agent *agent)
+{
+	fw_core_remove_address_handler(&agent->handler);
+	cancel_work_sync(&agent->work);
+	kfree(agent);
+}
+
+static int sbp_check_true(struct se_portal_group *se_tpg)
+{
+	return 1;
+}
+
+static int sbp_check_false(struct se_portal_group *se_tpg)
+{
+	return 0;
+}
+
+static char *sbp_get_fabric_name(void)
+{
+	return "sbp";
+}
+
+static char *sbp_get_fabric_wwn(struct se_portal_group *se_tpg)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+
+	return &tport->tport_name[0];
+}
+
+static u16 sbp_get_tag(struct se_portal_group *se_tpg)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	return tpg->tport_tpgt;
+}
+
+static u32 sbp_get_default_depth(struct se_portal_group *se_tpg)
+{
+	return 1;
+}
+
+static struct se_node_acl *sbp_alloc_fabric_acl(struct se_portal_group *se_tpg)
+{
+	struct sbp_nacl *nacl;
+
+	nacl = kzalloc(sizeof(struct sbp_nacl), GFP_KERNEL);
+	if (!nacl) {
+		pr_err("Unable to alocate struct sbp_nacl\n");
+		return NULL;
+	}
+
+	return &nacl->se_node_acl;
+}
+
+static void sbp_release_fabric_acl(
+	struct se_portal_group *se_tpg,
+	struct se_node_acl *se_nacl)
+{
+	struct sbp_nacl *nacl =
+		container_of(se_nacl, struct sbp_nacl, se_node_acl);
+	kfree(nacl);
+}
+
+static u32 sbp_tpg_get_inst_index(struct se_portal_group *se_tpg)
+{
+	return 1;
+}
+
+static void sbp_release_cmd(struct se_cmd *se_cmd)
+{
+	struct sbp_target_request *req = container_of(se_cmd,
+			struct sbp_target_request, se_cmd);
+
+	sbp_free_request(req);
+}
+
+static int sbp_shutdown_session(struct se_session *se_sess)
+{
+	return 0;
+}
+
+static void sbp_close_session(struct se_session *se_sess)
+{
+	return;
+}
+
+static u32 sbp_sess_get_index(struct se_session *se_sess)
+{
+	return 0;
+}
+
+static int sbp_write_pending(struct se_cmd *se_cmd)
+{
+	struct sbp_target_request *req = container_of(se_cmd,
+			struct sbp_target_request, se_cmd);
+	int ret;
+
+	ret = sbp_rw_data(req);
+	if (ret) {
+		req->status.status |= cpu_to_be32(
+			STATUS_BLOCK_RESP(
+				STATUS_RESP_TRANSPORT_FAILURE) |
+			STATUS_BLOCK_DEAD(0) |
+			STATUS_BLOCK_LEN(1) |
+			STATUS_BLOCK_SBP_STATUS(
+				SBP_STATUS_UNSPECIFIED_ERROR));
+		sbp_send_status(req);
+		return ret;
+	}
+
+	transport_generic_process_write(se_cmd);
+
+	return 0;
+}
+
+static int sbp_write_pending_status(struct se_cmd *se_cmd)
+{
+	return 0;
+}
+
+static void sbp_set_default_node_attrs(struct se_node_acl *nacl)
+{
+	return;
+}
+
+static u32 sbp_get_task_tag(struct se_cmd *se_cmd)
+{
+	struct sbp_target_request *req = container_of(se_cmd,
+			struct sbp_target_request, se_cmd);
+
+	/* only used for printk until we do TMRs */
+	return (u32)req->orb_pointer;
+}
+
+static int sbp_get_cmd_state(struct se_cmd *se_cmd)
+{
+	return 0;
+}
+
+static int sbp_queue_data_in(struct se_cmd *se_cmd)
+{
+	struct sbp_target_request *req = container_of(se_cmd,
+			struct sbp_target_request, se_cmd);
+	int ret;
+
+	ret = sbp_rw_data(req);
+	if (ret) {
+		req->status.status |= cpu_to_be32(
+			STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+			STATUS_BLOCK_DEAD(0) |
+			STATUS_BLOCK_LEN(1) |
+			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+		sbp_send_status(req);
+		return ret;
+	}
+
+	return sbp_send_sense(req);
+}
+
+/*
+ * Called after command (no data transfer) or after the write (to device)
+ * operation is completed
+ */
+static int sbp_queue_status(struct se_cmd *se_cmd)
+{
+	struct sbp_target_request *req = container_of(se_cmd,
+			struct sbp_target_request, se_cmd);
+
+	return sbp_send_sense(req);
+}
+
+static int sbp_queue_tm_rsp(struct se_cmd *se_cmd)
+{
+	return 0;
+}
+
+static u16 sbp_set_fabric_sense_len(struct se_cmd *se_cmd, u32 sense_length)
+{
+	return 0;
+}
+
+static u16 sbp_get_fabric_sense_len(void)
+{
+	return 0;
+}
+
+static int sbp_check_stop_free(struct se_cmd *se_cmd)
+{
+	struct sbp_target_request *req = container_of(se_cmd,
+			struct sbp_target_request, se_cmd);
+
+	transport_generic_free_cmd(&req->se_cmd, 0);
+	return 1;
+}
+
+/*
+ * Handlers for Serial Bus Protocol 2/3 (SBP-2 / SBP-3)
+ */
+static u8 sbp_get_fabric_proto_ident(struct se_portal_group *se_tpg)
+{
+	/*
+	 * Return a IEEE 1394 SCSI Protocol identifier for loopback operations
+	 * This is defined in section 7.5.1 Table 362 in spc4r17
+	 */
+	return SCSI_PROTOCOL_SBP;
+}
+
+static u32 sbp_get_pr_transport_id(
+	struct se_portal_group *se_tpg,
+	struct se_node_acl *se_nacl,
+	struct t10_pr_registration *pr_reg,
+	int *format_code,
+	unsigned char *buf)
+{
+	int ret;
+
+	/*
+	 * Set PROTOCOL IDENTIFIER to 3h for SBP
+	 */
+	buf[0] = SCSI_PROTOCOL_SBP;
+	/*
+	 * From spc4r17, 7.5.4.4 TransportID for initiator ports using SCSI
+	 * over IEEE 1394
+	 */
+	ret = hex2bin(&buf[8], se_nacl->initiatorname, 8);
+	if (ret < 0)
+		pr_debug("sbp transport_id: invalid hex string\n");
+
+	/*
+	 * The IEEE 1394 Transport ID is a hardcoded 24-byte length
+	 */
+	return 24;
+}
+
+static u32 sbp_get_pr_transport_id_len(
+	struct se_portal_group *se_tpg,
+	struct se_node_acl *se_nacl,
+	struct t10_pr_registration *pr_reg,
+	int *format_code)
+{
+	*format_code = 0;
+	/*
+	 * From spc4r17, 7.5.4.4 TransportID for initiator ports using SCSI
+	 * over IEEE 1394
+	 *
+	 * The SBP Transport ID is a hardcoded 24-byte length
+	 */
+	return 24;
+}
+
+/*
+ * Used for handling SCSI fabric dependent TransportIDs in SPC-3 and above
+ * Persistent Reservation SPEC_I_PT=1 and PROUT REGISTER_AND_MOVE operations.
+ */
+static char *sbp_parse_pr_out_transport_id(
+	struct se_portal_group *se_tpg,
+	const char *buf,
+	u32 *out_tid_len,
+	char **port_nexus_ptr)
+{
+	/*
+	 * Assume the FORMAT CODE 00b from spc4r17, 7.5.4.4 TransportID
+	 * for initiator ports using SCSI over SBP Serial SCSI Protocol
+	 *
+	 * The TransportID for a IEEE 1394 Initiator Port is of fixed size of
+	 * 24 bytes, and IEEE 1394 does not contain a I_T nexus identifier,
+	 * so we return the **port_nexus_ptr set to NULL.
+	 */
+	*port_nexus_ptr = NULL;
+	*out_tid_len = 24;
+
+	return (char *)&buf[8];
+}
+
+static int sbp_count_se_tpg_luns(struct se_portal_group *tpg)
+{
+	int i, count = 0;
+
+	spin_lock(&tpg->tpg_lun_lock);
+	for (i = 0; i < TRANSPORT_MAX_LUNS_PER_TPG; i++) {
+		struct se_lun *se_lun = tpg->tpg_lun_list[i];
+
+		if (se_lun->lun_status == TRANSPORT_LUN_STATUS_FREE)
+			continue;
+
+		count++;
+	}
+	spin_unlock(&tpg->tpg_lun_lock);
+
+	return count;
+}
+
+static int sbp_update_unit_directory(struct sbp_tport *tport)
+{
+	int num_luns, num_entries, idx = 0, mgt_agt_addr, ret, i;
+	u32 *data;
+
+	if (tport->unit_directory.data) {
+		fw_core_remove_descriptor(&tport->unit_directory);
+		kfree(tport->unit_directory.data);
+		tport->unit_directory.data = NULL;
+	}
+
+	if (!tport->enable || !tport->tpg)
+		return 0;
+
+	num_luns = sbp_count_se_tpg_luns(&tport->tpg->se_tpg);
+
+	/*
+	 * Number of entries in the final unit directory:
+	 *  - all of those in the template
+	 *  - management_agent
+	 *  - unit_characteristics
+	 *  - reconnect_timeout
+	 *  - unit unique ID
+	 *  - one for each LUN
+	 *
+	 *  MUST NOT include leaf or sub-directory entries
+	 */
+	num_entries = ARRAY_SIZE(sbp_unit_directory_template) + 4 + num_luns;
+
+	if (tport->directory_id != -1)
+		num_entries++;
+
+	/* allocate num_entries + 4 for the header and unique ID leaf */
+	data = kcalloc((num_entries + 4), sizeof(u32), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	/* directory_length */
+	data[idx++] = num_entries << 16;
+
+	/* directory_id */
+	if (tport->directory_id != -1)
+		data[idx++] = (CSR_DIRECTORY_ID << 24) | tport->directory_id;
+
+	/* unit directory template */
+	memcpy(&data[idx], sbp_unit_directory_template,
+			sizeof(sbp_unit_directory_template));
+	idx += ARRAY_SIZE(sbp_unit_directory_template);
+
+	/* management_agent */
+	mgt_agt_addr = (tport->mgt_agt->handler.offset - CSR_REGISTER_BASE) / 4;
+	data[idx++] = 0x54000000 | (mgt_agt_addr & 0x00ffffff);
+
+	/* unit_characteristics */
+	data[idx++] = 0x3a000000 |
+		(((tport->mgt_orb_timeout * 2) << 8) & 0xff00) |
+		SBP_ORB_FETCH_SIZE;
+
+	/* reconnect_timeout */
+	data[idx++] = 0x3d000000 | (tport->max_reconnect_timeout & 0xffff);
+
+	/* unit unique ID (leaf is just after LUNs) */
+	data[idx++] = 0x8d000000 | (num_luns + 1);
+
+	spin_lock(&tport->tpg->se_tpg.tpg_lun_lock);
+	for (i = 0; i < TRANSPORT_MAX_LUNS_PER_TPG; i++) {
+		struct se_lun *se_lun = tport->tpg->se_tpg.tpg_lun_list[i];
+		struct se_device *dev;
+		int type;
+
+		if (se_lun->lun_status == TRANSPORT_LUN_STATUS_FREE)
+			continue;
+
+		spin_unlock(&tport->tpg->se_tpg.tpg_lun_lock);
+
+		dev = se_lun->lun_se_dev;
+		type = dev->transport->get_device_type(dev);
+
+		/* logical_unit_number */
+		data[idx++] = 0x14000000 |
+			((type << 16) & 0x1f0000) |
+			(se_lun->unpacked_lun & 0xffff);
+
+		spin_lock(&tport->tpg->se_tpg.tpg_lun_lock);
+	}
+	spin_unlock(&tport->tpg->se_tpg.tpg_lun_lock);
+
+	/* unit unique ID leaf */
+	data[idx++] = 2 << 16;
+	data[idx++] = tport->guid >> 32;
+	data[idx++] = tport->guid;
+
+	tport->unit_directory.length = idx;
+	tport->unit_directory.key = (CSR_DIRECTORY | CSR_UNIT) << 24;
+	tport->unit_directory.data = data;
+
+	ret = fw_core_add_descriptor(&tport->unit_directory);
+	if (ret < 0) {
+		kfree(tport->unit_directory.data);
+		tport->unit_directory.data = NULL;
+	}
+
+	return ret;
+}
+
+static ssize_t sbp_parse_wwn(const char *name, u64 *wwn, int strict)
+{
+	const char *cp;
+	char c, nibble;
+	int pos = 0, err;
+
+	*wwn = 0;
+	for (cp = name; cp < &name[SBP_NAMELEN - 1]; cp++) {
+		c = *cp;
+		if (c == '\n' && cp[1] == '\0')
+			continue;
+		if (c == '\0') {
+			err = 2;
+			if (pos != 16)
+				goto fail;
+			return cp - name;
+		}
+		err = 3;
+		if (isdigit(c))
+			nibble = c - '0';
+		else if (isxdigit(c) && (islower(c) || !strict))
+			nibble = tolower(c) - 'a' + 10;
+		else
+			goto fail;
+		*wwn = (*wwn << 4) | nibble;
+		pos++;
+	}
+	err = 4;
+fail:
+	printk(KERN_INFO "err %u len %zu pos %u\n",
+			err, cp - name, pos);
+	return -1;
+}
+
+static ssize_t sbp_format_wwn(char *buf, size_t len, u64 wwn)
+{
+	return snprintf(buf, len, "%016llx", wwn);
+}
+
+static struct se_node_acl *sbp_make_nodeacl(
+		struct se_portal_group *se_tpg,
+		struct config_group *group,
+		const char *name)
+{
+	struct se_node_acl *se_nacl, *se_nacl_new;
+	struct sbp_nacl *nacl;
+	u64 guid = 0;
+	u32 nexus_depth = 1;
+
+	if (sbp_parse_wwn(name, &guid, 1) < 0)
+		return ERR_PTR(-EINVAL);
+
+	se_nacl_new = sbp_alloc_fabric_acl(se_tpg);
+	if (!se_nacl_new)
+		return ERR_PTR(-ENOMEM);
+
+	/*
+	 * se_nacl_new may be released by core_tpg_add_initiator_node_acl()
+	 * when converting a NodeACL from demo mode -> explict
+	 */
+	se_nacl = core_tpg_add_initiator_node_acl(se_tpg, se_nacl_new,
+			name, nexus_depth);
+	if (IS_ERR(se_nacl)) {
+		sbp_release_fabric_acl(se_tpg, se_nacl_new);
+		return se_nacl;
+	}
+
+	nacl = container_of(se_nacl, struct sbp_nacl, se_node_acl);
+	nacl->guid = guid;
+	sbp_format_wwn(nacl->iport_name, SBP_NAMELEN, guid);
+
+	return se_nacl;
+}
+
+static void sbp_drop_nodeacl(struct se_node_acl *se_acl)
+{
+	struct sbp_nacl *nacl =
+		container_of(se_acl, struct sbp_nacl, se_node_acl);
+
+	core_tpg_del_initiator_node_acl(se_acl->se_tpg, se_acl, 1);
+	kfree(nacl);
+}
+
+static int sbp_post_link_lun(
+		struct se_portal_group *se_tpg,
+		struct se_lun *se_lun)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+
+	return sbp_update_unit_directory(tpg->tport);
+}
+
+static void sbp_pre_unlink_lun(
+		struct se_portal_group *se_tpg,
+		struct se_lun *se_lun)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+	int ret;
+
+	if (sbp_count_se_tpg_luns(&tpg->se_tpg) == 0)
+		tport->enable = 0;
+
+	ret = sbp_update_unit_directory(tport);
+	if (ret < 0)
+		pr_err("unlink LUN: failed to update unit directory\n");
+}
+
+static struct se_portal_group *sbp_make_tpg(
+		struct se_wwn *wwn,
+		struct config_group *group,
+		const char *name)
+{
+	struct sbp_tport *tport =
+		container_of(wwn, struct sbp_tport, tport_wwn);
+
+	struct sbp_tpg *tpg;
+	unsigned long tpgt;
+	int ret;
+
+	if (strstr(name, "tpgt_") != name)
+		return ERR_PTR(-EINVAL);
+	if (kstrtoul(name + 5, 10, &tpgt) || tpgt > UINT_MAX)
+		return ERR_PTR(-EINVAL);
+
+	if (tport->tpg) {
+		pr_err("Only one TPG per Unit is possible.\n");
+		return ERR_PTR(-EBUSY);
+	}
+
+	tpg = kzalloc(sizeof(*tpg), GFP_KERNEL);
+	if (!tpg) {
+		pr_err("Unable to allocate struct sbp_tpg\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	tpg->tport = tport;
+	tpg->tport_tpgt = tpgt;
+	tport->tpg = tpg;
+
+	/* default attribute values */
+	tport->enable = 0;
+	tport->directory_id = -1;
+	tport->mgt_orb_timeout = 15;
+	tport->max_reconnect_timeout = 5;
+	tport->max_logins_per_lun = 1;
+
+	tport->mgt_agt = sbp_management_agent_register(tport);
+	if (IS_ERR(tport->mgt_agt)) {
+		ret = PTR_ERR(tport->mgt_agt);
+		kfree(tpg);
+		return ERR_PTR(ret);
+	}
+
+	ret = core_tpg_register(&sbp_fabric_configfs->tf_ops, wwn,
+			&tpg->se_tpg, (void *)tpg,
+			TRANSPORT_TPG_TYPE_NORMAL);
+	if (ret < 0) {
+		sbp_management_agent_unregister(tport->mgt_agt);
+		kfree(tpg);
+		return ERR_PTR(ret);
+	}
+
+	return &tpg->se_tpg;
+}
+
+static void sbp_drop_tpg(struct se_portal_group *se_tpg)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+
+	core_tpg_deregister(se_tpg);
+	sbp_management_agent_unregister(tport->mgt_agt);
+	tport->tpg = NULL;
+	kfree(tpg);
+}
+
+static struct se_wwn *sbp_make_tport(
+		struct target_fabric_configfs *tf,
+		struct config_group *group,
+		const char *name)
+{
+	struct sbp_tport *tport;
+	u64 guid = 0;
+
+	if (sbp_parse_wwn(name, &guid, 1) < 0)
+		return ERR_PTR(-EINVAL);
+
+	tport = kzalloc(sizeof(*tport), GFP_KERNEL);
+	if (!tport) {
+		pr_err("Unable to allocate struct sbp_tport\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	tport->guid = guid;
+	sbp_format_wwn(tport->tport_name, SBP_NAMELEN, guid);
+
+	return &tport->tport_wwn;
+}
+
+static void sbp_drop_tport(struct se_wwn *wwn)
+{
+	struct sbp_tport *tport =
+		container_of(wwn, struct sbp_tport, tport_wwn);
+
+	kfree(tport);
+}
+
+static ssize_t sbp_wwn_show_attr_version(
+		struct target_fabric_configfs *tf,
+		char *page)
+{
+	return sprintf(page, "FireWire SBP fabric module %s\n", SBP_VERSION);
+}
+
+TF_WWN_ATTR_RO(sbp, version);
+
+static struct configfs_attribute *sbp_wwn_attrs[] = {
+	&sbp_wwn_version.attr,
+	NULL,
+};
+
+static ssize_t sbp_tpg_show_directory_id(
+		struct se_portal_group *se_tpg,
+		char *page)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+
+	if (tport->directory_id == -1)
+		return sprintf(page, "implicit\n");
+	else
+		return sprintf(page, "%06x\n", tport->directory_id);
+}
+
+static ssize_t sbp_tpg_store_directory_id(
+		struct se_portal_group *se_tpg,
+		const char *page,
+		size_t count)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+	unsigned long val;
+
+	if (tport->enable) {
+		pr_err("Cannot change the directory_id on an active target.\n");
+		return -EBUSY;
+	}
+
+	if (strstr(page, "implicit") == page) {
+		tport->directory_id = -1;
+	} else {
+		if (kstrtoul(page, 16, &val) < 0)
+			return -EINVAL;
+		if (val > 0xffffff)
+			return -EINVAL;
+
+		tport->directory_id = val;
+	}
+
+	return count;
+}
+
+static ssize_t sbp_tpg_show_enable(
+		struct se_portal_group *se_tpg,
+		char *page)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+	return sprintf(page, "%d\n", tport->enable);
+}
+
+static ssize_t sbp_tpg_store_enable(
+		struct se_portal_group *se_tpg,
+		const char *page,
+		size_t count)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+	unsigned long val;
+	int ret;
+
+	if (kstrtoul(page, 0, &val) < 0)
+		return -EINVAL;
+	if ((val != 0) && (val != 1))
+		return -EINVAL;
+
+	if (tport->enable == val)
+		return count;
+
+	if (val) {
+		if (sbp_count_se_tpg_luns(&tpg->se_tpg) == 0) {
+			pr_err("Cannot enable a target with no LUNs!\n");
+			return -EINVAL;
+		}
+	} else {
+		/* XXX: force-shutdown sessions instead? */
+		spin_lock_bh(&se_tpg->session_lock);
+		if (!list_empty(&se_tpg->tpg_sess_list)) {
+			spin_unlock_bh(&se_tpg->session_lock);
+			return -EBUSY;
+		}
+		spin_unlock_bh(&se_tpg->session_lock);
+	}
+
+	tport->enable = val;
+
+	ret = sbp_update_unit_directory(tport);
+	if (ret < 0) {
+		pr_err("Could not update Config ROM\n");
+		return ret;
+	}
+
+	return count;
+}
+
+TF_TPG_BASE_ATTR(sbp, directory_id, S_IRUGO | S_IWUSR);
+TF_TPG_BASE_ATTR(sbp, enable, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *sbp_tpg_base_attrs[] = {
+	&sbp_tpg_directory_id.attr,
+	&sbp_tpg_enable.attr,
+	NULL,
+};
+
+static ssize_t sbp_tpg_attrib_show_mgt_orb_timeout(
+		struct se_portal_group *se_tpg,
+		char *page)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+	return sprintf(page, "%d\n", tport->mgt_orb_timeout);
+}
+
+static ssize_t sbp_tpg_attrib_store_mgt_orb_timeout(
+		struct se_portal_group *se_tpg,
+		const char *page,
+		size_t count)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+	unsigned long val;
+	int ret;
+
+	if (kstrtoul(page, 0, &val) < 0)
+		return -EINVAL;
+	if ((val < 1) || (val > 127))
+		return -EINVAL;
+
+	if (tport->mgt_orb_timeout == val)
+		return count;
+
+	tport->mgt_orb_timeout = val;
+
+	ret = sbp_update_unit_directory(tport);
+	if (ret < 0)
+		return ret;
+
+	return count;
+}
+
+static ssize_t sbp_tpg_attrib_show_max_reconnect_timeout(
+		struct se_portal_group *se_tpg,
+		char *page)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+	return sprintf(page, "%d\n", tport->max_reconnect_timeout);
+}
+
+static ssize_t sbp_tpg_attrib_store_max_reconnect_timeout(
+		struct se_portal_group *se_tpg,
+		const char *page,
+		size_t count)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+	unsigned long val;
+	int ret;
+
+	if (kstrtoul(page, 0, &val) < 0)
+		return -EINVAL;
+	if ((val < 1) || (val > 32767))
+		return -EINVAL;
+
+	if (tport->max_reconnect_timeout == val)
+		return count;
+
+	tport->max_reconnect_timeout = val;
+
+	ret = sbp_update_unit_directory(tport);
+	if (ret < 0)
+		return ret;
+
+	return count;
+}
+
+static ssize_t sbp_tpg_attrib_show_max_logins_per_lun(
+		struct se_portal_group *se_tpg,
+		char *page)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+	return sprintf(page, "%d\n", tport->max_logins_per_lun);
+}
+
+static ssize_t sbp_tpg_attrib_store_max_logins_per_lun(
+		struct se_portal_group *se_tpg,
+		const char *page,
+		size_t count)
+{
+	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+	struct sbp_tport *tport = tpg->tport;
+	unsigned long val;
+
+	if (kstrtoul(page, 0, &val) < 0)
+		return -EINVAL;
+	if ((val < 1) || (val > 127))
+		return -EINVAL;
+
+	/* XXX: also check against current count? */
+
+	tport->max_logins_per_lun = val;
+
+	return count;
+}
+
+TF_TPG_ATTRIB_ATTR(sbp, mgt_orb_timeout, S_IRUGO | S_IWUSR);
+TF_TPG_ATTRIB_ATTR(sbp, max_reconnect_timeout, S_IRUGO | S_IWUSR);
+TF_TPG_ATTRIB_ATTR(sbp, max_logins_per_lun, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *sbp_tpg_attrib_attrs[] = {
+	&sbp_tpg_attrib_mgt_orb_timeout.attr,
+	&sbp_tpg_attrib_max_reconnect_timeout.attr,
+	&sbp_tpg_attrib_max_logins_per_lun.attr,
+	NULL,
+};
+
+static struct target_core_fabric_ops sbp_ops = {
+	.get_fabric_name		= sbp_get_fabric_name,
+	.get_fabric_proto_ident		= sbp_get_fabric_proto_ident,
+	.tpg_get_wwn			= sbp_get_fabric_wwn,
+	.tpg_get_tag			= sbp_get_tag,
+	.tpg_get_default_depth		= sbp_get_default_depth,
+	.tpg_get_pr_transport_id	= sbp_get_pr_transport_id,
+	.tpg_get_pr_transport_id_len	= sbp_get_pr_transport_id_len,
+	.tpg_parse_pr_out_transport_id	= sbp_parse_pr_out_transport_id,
+	.tpg_check_demo_mode		= sbp_check_true,
+	.tpg_check_demo_mode_cache	= sbp_check_true,
+	.tpg_check_demo_mode_write_protect = sbp_check_false,
+	.tpg_check_prod_mode_write_protect = sbp_check_false,
+	.tpg_alloc_fabric_acl		= sbp_alloc_fabric_acl,
+	.tpg_release_fabric_acl		= sbp_release_fabric_acl,
+	.tpg_get_inst_index		= sbp_tpg_get_inst_index,
+	.release_cmd			= sbp_release_cmd,
+	.shutdown_session		= sbp_shutdown_session,
+	.close_session			= sbp_close_session,
+	.sess_get_index			= sbp_sess_get_index,
+	.write_pending			= sbp_write_pending,
+	.write_pending_status		= sbp_write_pending_status,
+	.set_default_node_attributes	= sbp_set_default_node_attrs,
+	.get_task_tag			= sbp_get_task_tag,
+	.get_cmd_state			= sbp_get_cmd_state,
+	.queue_data_in			= sbp_queue_data_in,
+	.queue_status			= sbp_queue_status,
+	.queue_tm_rsp			= sbp_queue_tm_rsp,
+	.get_fabric_sense_len		= sbp_get_fabric_sense_len,
+	.set_fabric_sense_len		= sbp_set_fabric_sense_len,
+	.check_stop_free		= sbp_check_stop_free,
+
+	.fabric_make_wwn		= sbp_make_tport,
+	.fabric_drop_wwn		= sbp_drop_tport,
+	.fabric_make_tpg		= sbp_make_tpg,
+	.fabric_drop_tpg		= sbp_drop_tpg,
+	.fabric_post_link		= sbp_post_link_lun,
+	.fabric_pre_unlink		= sbp_pre_unlink_lun,
+	.fabric_make_np			= NULL,
+	.fabric_drop_np			= NULL,
+	.fabric_make_nodeacl		= sbp_make_nodeacl,
+	.fabric_drop_nodeacl		= sbp_drop_nodeacl,
+};
+
+static int sbp_register_configfs(void)
+{
+	struct target_fabric_configfs *fabric;
+	int ret;
+
+	fabric = target_fabric_configfs_init(THIS_MODULE, "sbp");
+	if (!fabric) {
+		pr_err("target_fabric_configfs_init() failed\n");
+		return -ENOMEM;
+	}
+
+	fabric->tf_ops = sbp_ops;
+
+	/*
+	 * Setup default attribute lists for various fabric->tf_cit_tmpl
+	 */
+	TF_CIT_TMPL(fabric)->tfc_wwn_cit.ct_attrs = sbp_wwn_attrs;
+	TF_CIT_TMPL(fabric)->tfc_tpg_base_cit.ct_attrs = sbp_tpg_base_attrs;
+	TF_CIT_TMPL(fabric)->tfc_tpg_attrib_cit.ct_attrs = sbp_tpg_attrib_attrs;
+	TF_CIT_TMPL(fabric)->tfc_tpg_param_cit.ct_attrs = NULL;
+	TF_CIT_TMPL(fabric)->tfc_tpg_np_base_cit.ct_attrs = NULL;
+	TF_CIT_TMPL(fabric)->tfc_tpg_nacl_base_cit.ct_attrs = NULL;
+	TF_CIT_TMPL(fabric)->tfc_tpg_nacl_attrib_cit.ct_attrs = NULL;
+	TF_CIT_TMPL(fabric)->tfc_tpg_nacl_auth_cit.ct_attrs = NULL;
+	TF_CIT_TMPL(fabric)->tfc_tpg_nacl_param_cit.ct_attrs = NULL;
+
+	ret = target_fabric_configfs_register(fabric);
+	if (ret < 0) {
+		pr_err("target_fabric_configfs_register() failed for SBP\n");
+		return ret;
+	}
+
+	sbp_fabric_configfs = fabric;
+
+	return 0;
+};
+
+static void sbp_deregister_configfs(void)
+{
+	if (!sbp_fabric_configfs)
+		return;
+
+	target_fabric_configfs_deregister(sbp_fabric_configfs);
+	sbp_fabric_configfs = NULL;
+};
+
+static int __init sbp_init(void)
+{
+	int ret;
+
+	ret = sbp_register_configfs();
+	if (ret < 0)
+		return ret;
+
+	return 0;
+};
+
+static void sbp_exit(void)
+{
+	sbp_deregister_configfs();
+};
+
+MODULE_DESCRIPTION("FireWire SBP fabric driver");
+MODULE_LICENSE("GPL");
+module_init(sbp_init);
+module_exit(sbp_exit);
diff --git a/drivers/target/sbp/sbp_target.h b/drivers/target/sbp/sbp_target.h
new file mode 100644
index 0000000..6d0d74a
--- /dev/null
+++ b/drivers/target/sbp/sbp_target.h
@@ -0,0 +1,251 @@
+#ifndef _SBP_BASE_H
+#define _SBP_BASE_H
+
+#include <linux/firewire.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+#include <target/target_core_base.h>
+
+#define SBP_VERSION  "v0.1"
+#define SBP_NAMELEN 32
+
+#define SBP_ORB_FETCH_SIZE	8
+
+#define MANAGEMENT_AGENT_STATE_IDLE	0
+#define MANAGEMENT_AGENT_STATE_BUSY	1
+
+#define ORB_NOTIFY(v)			(((v) >> 31) & 0x01)
+#define ORB_REQUEST_FORMAT(v)		(((v) >> 29) & 0x03)
+
+#define MANAGEMENT_ORB_FUNCTION(v)	(((v) >> 16) & 0x0f)
+
+#define MANAGEMENT_ORB_FUNCTION_LOGIN			0x0
+#define MANAGEMENT_ORB_FUNCTION_QUERY_LOGINS		0x1
+#define MANAGEMENT_ORB_FUNCTION_RECONNECT		0x3
+#define MANAGEMENT_ORB_FUNCTION_SET_PASSWORD		0x4
+#define MANAGEMENT_ORB_FUNCTION_LOGOUT			0x7
+#define MANAGEMENT_ORB_FUNCTION_ABORT_TASK		0xb
+#define MANAGEMENT_ORB_FUNCTION_ABORT_TASK_SET		0xc
+#define MANAGEMENT_ORB_FUNCTION_LOGICAL_UNIT_RESET	0xe
+#define MANAGEMENT_ORB_FUNCTION_TARGET_RESET		0xf
+
+#define LOGIN_ORB_EXCLUSIVE(v)		(((v) >> 28) &   0x01)
+#define LOGIN_ORB_RESERVED(v)		(((v) >> 24) &   0x0f)
+#define LOGIN_ORB_RECONNECT(v)		(((v) >> 20) &   0x0f)
+#define LOGIN_ORB_LUN(v)		(((v) >>  0) & 0xffff)
+#define LOGIN_ORB_PASSWORD_LENGTH(v)	(((v) >> 16) & 0xffff)
+#define LOGIN_ORB_RESPONSE_LENGTH(v)	(((v) >>  0) & 0xffff)
+
+#define RECONNECT_ORB_LOGIN_ID(v)	(((v) >>  0) & 0xffff)
+#define LOGOUT_ORB_LOGIN_ID(v)		(((v) >>  0) & 0xffff)
+
+#define CMDBLK_ORB_DIRECTION(v)		(((v) >> 27) &   0x01)
+#define CMDBLK_ORB_SPEED(v)		(((v) >> 24) &   0x07)
+#define CMDBLK_ORB_MAX_PAYLOAD(v)	(((v) >> 20) &   0x0f)
+#define CMDBLK_ORB_PG_TBL_PRESENT(v)	(((v) >> 19) &   0x01)
+#define CMDBLK_ORB_PG_SIZE(v)		(((v) >> 16) &   0x07)
+#define CMDBLK_ORB_DATA_SIZE(v)		(((v) >>  0) & 0xffff)
+
+#define STATUS_BLOCK_SRC(v)		(((v) &   0x03) << 30)
+#define STATUS_BLOCK_RESP(v)		(((v) &   0x03) << 28)
+#define STATUS_BLOCK_DEAD(v)		(((v) ? 1 : 0)  << 27)
+#define STATUS_BLOCK_LEN(v)		(((v) &   0x07) << 24)
+#define STATUS_BLOCK_SBP_STATUS(v)	(((v) &   0xff) << 16)
+#define STATUS_BLOCK_ORB_OFFSET_HIGH(v)	(((v) & 0xffff) <<  0)
+
+#define STATUS_SRC_ORB_CONTINUING	0
+#define STATUS_SRC_ORB_FINISHED		1
+#define STATUS_SRC_UNSOLICITED		2
+
+#define STATUS_RESP_REQUEST_COMPLETE	0
+#define STATUS_RESP_TRANSPORT_FAILURE	1
+#define STATUS_RESP_ILLEGAL_REQUEST	2
+#define STATUS_RESP_VENDOR_DEPENDENT	3
+
+#define SBP_STATUS_OK			0
+#define SBP_STATUS_REQ_TYPE_NOTSUPP	1
+#define SBP_STATUS_SPEED_NOTSUPP	2
+#define SBP_STATUS_PAGE_SIZE_NOTSUPP	3
+#define SBP_STATUS_ACCESS_DENIED	4
+#define SBP_STATUS_LUN_NOTSUPP		5
+#define SBP_STATUS_PAYLOAD_TOO_SMALL	6
+/* 7 is reserved */
+#define SBP_STATUS_RESOURCES_UNAVAIL	8
+#define SBP_STATUS_FUNCTION_REJECTED	9
+#define SBP_STATUS_LOGIN_ID_UNKNOWN	10
+#define SBP_STATUS_DUMMY_ORB_COMPLETE	11
+#define SBP_STATUS_REQUEST_ABORTED	12
+#define SBP_STATUS_UNSPECIFIED_ERROR	0xff
+
+#define AGENT_STATE_RESET	0
+#define AGENT_STATE_ACTIVE	1
+#define AGENT_STATE_SUSPENDED	2
+#define AGENT_STATE_DEAD	3
+
+struct sbp2_pointer {
+	__be32 high;
+	__be32 low;
+};
+
+struct sbp_command_block_orb {
+	struct sbp2_pointer next_orb;
+	struct sbp2_pointer data_descriptor;
+	__be32 misc;
+	u8 command_block[12];
+};
+
+struct sbp_page_table_entry {
+	__be16 segment_length;
+	__be16 segment_base_hi;
+	__be32 segment_base_lo;
+};
+
+struct sbp_management_orb {
+	struct sbp2_pointer ptr1;
+	struct sbp2_pointer ptr2;
+	__be32 misc;
+	__be32 length;
+	struct sbp2_pointer status_fifo;
+};
+
+struct sbp_status_block {
+	__be32 status;
+	__be32 orb_low;
+	u8 data[24];
+};
+
+struct sbp_login_response_block {
+	__be32 misc;
+	struct sbp2_pointer command_block_agent;
+	__be32 reconnect_hold;
+};
+
+struct sbp_login_descriptor {
+	struct sbp_session *sess;
+	struct list_head link;
+
+	struct se_lun *lun;
+
+	u64 status_fifo_addr;
+	int exclusive;
+	u16 login_id;
+
+	struct sbp_target_agent *tgt_agt;
+};
+
+struct sbp_session {
+	spinlock_t lock;
+	struct se_session *se_sess;
+	struct list_head login_list;
+	struct delayed_work maint_work;
+
+	u64 guid; /* login_owner_EUI_64 */
+	int node_id; /* login_owner_ID */
+
+	struct fw_card *card;
+	int generation;
+	int speed;
+
+	int reconnect_hold;
+	u64 reconnect_expires;
+};
+
+struct sbp_nacl {
+	/* Initiator EUI-64 */
+	u64 guid;
+	/* ASCII formatted GUID for SBP Initiator port */
+	char iport_name[SBP_NAMELEN];
+	/* Returned by sbp_make_nodeacl() */
+	struct se_node_acl se_node_acl;
+};
+
+struct sbp_tpg {
+	/* Target portal group tag for TCM */
+	u16 tport_tpgt;
+	/* Pointer back to sbp_tport */
+	struct sbp_tport *tport;
+	/* Returned by sbp_make_tpg() */
+	struct se_portal_group se_tpg;
+};
+
+struct sbp_tport {
+	/* Target Unit Identifier (EUI-64) */
+	u64 guid;
+	/* Target port name */
+	char tport_name[SBP_NAMELEN];
+	/* Returned by sbp_make_tport() */
+	struct se_wwn tport_wwn;
+
+	struct sbp_tpg *tpg;
+
+	/* FireWire unit directory */
+	struct fw_descriptor unit_directory;
+
+	/* SBP Management Agent */
+	struct sbp_management_agent *mgt_agt;
+
+	/* Parameters */
+	int enable;
+	s32 directory_id;
+	int mgt_orb_timeout;
+	int max_reconnect_timeout;
+	int max_logins_per_lun;
+};
+
+static inline u64 sbp2_pointer_to_addr(const struct sbp2_pointer *ptr)
+{
+	return (u64)(be32_to_cpu(ptr->high) & 0x0000ffff) << 32 |
+		(be32_to_cpu(ptr->low) & 0xfffffffc);
+}
+
+static inline void addr_to_sbp2_pointer(u64 addr, struct sbp2_pointer *ptr)
+{
+	ptr->high = cpu_to_be32(addr >> 32);
+	ptr->low = cpu_to_be32(addr);
+}
+
+struct sbp_target_agent {
+	spinlock_t lock;
+	struct fw_address_handler handler;
+	struct sbp_login_descriptor *login;
+	int state;
+	struct work_struct work;
+	u64 orb_pointer;
+	bool doorbell;
+};
+
+struct sbp_target_request {
+	struct sbp_login_descriptor *login;
+	u64 orb_pointer;
+	struct sbp_command_block_orb orb;
+	struct sbp_status_block status;
+	struct work_struct work;
+
+	struct se_cmd se_cmd;
+	struct sbp_page_table_entry *pg_tbl;
+	void *cmd_buf;
+
+	unsigned char sense_buf[TRANSPORT_SENSE_BUFFER];
+};
+
+struct sbp_management_agent {
+	spinlock_t lock;
+	struct sbp_tport *tport;
+	struct fw_address_handler handler;
+	int state;
+	struct work_struct work;
+	u64 orb_offset;
+	struct sbp_management_request *request;
+};
+
+struct sbp_management_request {
+	struct sbp_management_orb orb;
+	struct sbp_status_block status;
+	struct fw_card *card;
+	int generation;
+	int node_addr;
+	int speed;
+};
+
+#endif
diff --git a/drivers/tty/serial/8250/8250_mca.c b/drivers/tty/serial/8250/8250_mca.c
deleted file mode 100644
index d20abf0..0000000
--- a/drivers/tty/serial/8250/8250_mca.c
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- *  Copyright (C) 2005 Russell King.
- *  Data taken from include/asm-i386/serial.h
- *
- * 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.
- */
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/mca.h>
-#include <linux/serial_8250.h>
-
-/*
- * FIXME: Should we be doing AUTO_IRQ here?
- */
-#ifdef CONFIG_SERIAL_8250_DETECT_IRQ
-#define MCA_FLAGS	UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_AUTO_IRQ
-#else
-#define MCA_FLAGS	UPF_BOOT_AUTOCONF | UPF_SKIP_TEST
-#endif
-
-#define PORT(_base,_irq)			\
-	{					\
-		.iobase		= _base,	\
-		.irq		= _irq,		\
-		.uartclk	= 1843200,	\
-		.iotype		= UPIO_PORT,	\
-		.flags		= MCA_FLAGS,	\
-	}
-
-static struct plat_serial8250_port mca_data[] = {
-	PORT(0x3220, 3),
-	PORT(0x3228, 3),
-	PORT(0x4220, 3),
-	PORT(0x4228, 3),
-	PORT(0x5220, 3),
-	PORT(0x5228, 3),
-	{ },
-};
-
-static struct platform_device mca_device = {
-	.name			= "serial8250",
-	.id			= PLAT8250_DEV_MCA,
-	.dev			= {
-		.platform_data	= mca_data,
-	},
-};
-
-static int __init mca_init(void)
-{
-	if (!MCA_bus)
-		return -ENODEV;
-	return platform_device_register(&mca_device);
-}
-
-module_init(mca_init);
-
-MODULE_AUTHOR("Russell King");
-MODULE_DESCRIPTION("8250 serial probe module for MCA ports");
-MODULE_LICENSE("GPL");
diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig
index 8bc7ecb..a27dd05 100644
--- a/drivers/tty/serial/8250/Kconfig
+++ b/drivers/tty/serial/8250/Kconfig
@@ -241,15 +241,6 @@
 	help
 	  ::: To be written :::
 
-config SERIAL_8250_MCA
-	tristate "Support 8250-type ports on MCA buses"
-	depends on SERIAL_8250 != n && MCA
-	help
-	  Say Y here if you have a MCA serial ports.
-
-	  To compile this driver as a module, choose M here: the module
-	  will be called 8250_mca.
-
 config SERIAL_8250_ACORN
 	tristate "Acorn expansion card serial port support"
 	depends on ARCH_ACORN && SERIAL_8250
diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile
index 3f35eac..d7533c7 100644
--- a/drivers/tty/serial/8250/Makefile
+++ b/drivers/tty/serial/8250/Makefile
@@ -15,7 +15,6 @@
 obj-$(CONFIG_SERIAL_8250_BOCA)		+= 8250_boca.o
 obj-$(CONFIG_SERIAL_8250_EXAR_ST16C554)	+= 8250_exar_st16c554.o
 obj-$(CONFIG_SERIAL_8250_HUB6)		+= 8250_hub6.o
-obj-$(CONFIG_SERIAL_8250_MCA)		+= 8250_mca.o
 obj-$(CONFIG_SERIAL_8250_FSL)		+= 8250_fsl.o
 obj-$(CONFIG_SERIAL_8250_DW)		+= 8250_dw.o
 obj-$(CONFIG_SERIAL_8250_EM)		+= 8250_em.o
diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
index 3158e17..4604153 100644
--- a/drivers/tty/serial/sh-sci.c
+++ b/drivers/tty/serial/sh-sci.c
@@ -1052,9 +1052,17 @@
 		if (SCIx_IRQ_IS_MUXED(port)) {
 			i = SCIx_MUX_IRQ;
 			irq = up->irq;
-		} else
+		} else {
 			irq = port->cfg->irqs[i];
 
+			/*
+			 * Certain port types won't support all of the
+			 * available interrupt sources.
+			 */
+			if (unlikely(!irq))
+				continue;
+		}
+
 		desc = sci_irq_desc + i;
 		port->irqstr[j] = kasprintf(GFP_KERNEL, "%s:%s",
 					    dev_name(up->dev), desc->desc);
@@ -1094,6 +1102,15 @@
 	 * IRQ first.
 	 */
 	for (i = 0; i < SCIx_NR_IRQS; i++) {
+		unsigned int irq = port->cfg->irqs[i];
+
+		/*
+		 * Certain port types won't support all of the available
+		 * interrupt sources.
+		 */
+		if (unlikely(!irq))
+			continue;
+
 		free_irq(port->cfg->irqs[i], port);
 		kfree(port->irqstr[i]);
 
@@ -1564,10 +1581,32 @@
 
 static void sci_break_ctl(struct uart_port *port, int break_state)
 {
-	/*
-	 * Not supported by hardware. Most parts couple break and rx
-	 * interrupts together, with break detection always enabled.
-	 */
+	struct sci_port *s = to_sci_port(port);
+	struct plat_sci_reg *reg = sci_regmap[s->cfg->regtype] + SCSPTR;
+	unsigned short scscr, scsptr;
+
+	/* check wheter the port has SCSPTR */
+	if (!reg->size) {
+		/*
+		 * Not supported by hardware. Most parts couple break and rx
+		 * interrupts together, with break detection always enabled.
+		 */
+		return;
+	}
+
+	scsptr = serial_port_in(port, SCSPTR);
+	scscr = serial_port_in(port, SCSCR);
+
+	if (break_state == -1) {
+		scsptr = (scsptr | SCSPTR_SPB2IO) & ~SCSPTR_SPB2DT;
+		scscr &= ~SCSCR_TE;
+	} else {
+		scsptr = (scsptr | SCSPTR_SPB2DT) & ~SCSPTR_SPB2IO;
+		scscr |= SCSCR_TE;
+	}
+
+	serial_port_out(port, SCSPTR, scsptr);
+	serial_port_out(port, SCSCR, scscr);
 }
 
 #ifdef CONFIG_SERIAL_SH_SCI_DMA
diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c
index 3b0c4e3..48cc6f2 100644
--- a/drivers/tty/vt/keyboard.c
+++ b/drivers/tty/vt/keyboard.c
@@ -53,17 +53,13 @@
 
 #define KBD_DEFMODE ((1 << VC_REPEAT) | (1 << VC_META))
 
-/*
- * Some laptops take the 789uiojklm,. keys as number pad when NumLock is on.
- * This seems a good reason to start with NumLock off. On HIL keyboards
- * of PARISC machines however there is no NumLock key and everyone expects the
- * keypad to be used for numbers.
- */
-
-#if defined(CONFIG_PARISC) && (defined(CONFIG_KEYBOARD_HIL) || defined(CONFIG_KEYBOARD_HIL_OLD))
-#define KBD_DEFLEDS (1 << VC_NUMLOCK)
+#if defined(CONFIG_X86) || defined(CONFIG_PARISC)
+#include <asm/kbdleds.h>
 #else
-#define KBD_DEFLEDS 0
+static inline int kbd_defleds(void)
+{
+	return 0;
+}
 #endif
 
 #define KBD_DEFLOCK 0
@@ -1524,8 +1520,8 @@
 	int error;
 
 	for (i = 0; i < MAX_NR_CONSOLES; i++) {
-		kbd_table[i].ledflagstate = KBD_DEFLEDS;
-		kbd_table[i].default_ledflagstate = KBD_DEFLEDS;
+		kbd_table[i].ledflagstate = kbd_defleds();
+		kbd_table[i].default_ledflagstate = kbd_defleds();
 		kbd_table[i].ledmode = LED_SHOW_FLAGS;
 		kbd_table[i].lockstate = KBD_DEFLOCK;
 		kbd_table[i].slockstate = 0;
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index eeea76f..a18bf63 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -129,17 +129,6 @@
 
 	  Not sure? It's safe to say N.
 
-config IXP2000_WATCHDOG
-	tristate "IXP2000 Watchdog"
-	depends on ARCH_IXP2000
-	help
-	  Say Y here if to include support for the watchdog timer
-	  in the Intel IXP2000(2400, 2800, 2850) network processors.
-	  This driver can be built as a module by choosing M. The module
-	  will be called ixp2000_wdt.
-
-	  Say N if you are unsure.
-
 config IXP4XX_WATCHDOG
 	tristate "IXP4xx Watchdog"
 	depends on ARCH_IXP4XX
@@ -543,7 +532,7 @@
 
 config I6300ESB_WDT
 	tristate "Intel 6300ESB Timer/Watchdog"
-	depends on X86 && PCI
+	depends on PCI
 	---help---
 	  Hardware driver for the watchdog timer built into the Intel
 	  6300ESB controller hub.
@@ -551,6 +540,19 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called i6300esb.
 
+config IE6XX_WDT
+	tristate "Intel Atom E6xx Watchdog"
+	depends on X86 && PCI
+	select WATCHDOG_CORE
+	select MFD_CORE
+	select LPC_SCH
+	---help---
+	  Hardware driver for the watchdog timer built into the Intel
+	  Atom E6XX (TunnelCreek) processor.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ie6xx_wdt.
+
 config INTEL_SCU_WATCHDOG
 	bool "Intel SCU Watchdog for Mobile Platforms"
 	depends on X86_MRST
@@ -607,7 +609,12 @@
 	depends on X86 && EXPERIMENTAL
 	---help---
 	  This is the driver for the hardware watchdog on the ITE IT8702,
-	  IT8712, IT8716, IT8718, IT8720, IT8721, IT8726 Super I/O chips.
+	  IT8712, IT8716, IT8718, IT8720, IT8721, IT8726 and IT8728
+	  Super I/O chips.
+
+	  If the driver does not work, then make sure that the game port in
+	  the BIOS is enabled.
+
 	  This watchdog simply watches your kernel to make sure it doesn't
 	  freeze, and if it does, it reboots your computer after a certain
 	  amount of time.
@@ -780,7 +787,7 @@
 
 config VIA_WDT
 	tristate "VIA Watchdog Timer"
-	depends on X86
+	depends on X86 && PCI
 	select WATCHDOG_CORE
 	---help---
 	This is the driver for the hardware watchdog timer on VIA
@@ -1138,6 +1145,7 @@
 config SH_WDT
 	tristate "SuperH Watchdog"
 	depends on SUPERH && (CPU_SH3 || CPU_SH4)
+	select WATCHDOG_CORE
 	help
 	  This driver adds watchdog support for the integrated watchdog in the
 	  SuperH processors. If you have one of these processors and wish
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index e8f479a..442bfbe 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -36,7 +36,6 @@
 obj-$(CONFIG_TWL4030_WATCHDOG) += twl4030_wdt.o
 obj-$(CONFIG_21285_WATCHDOG) += wdt285.o
 obj-$(CONFIG_977_WATCHDOG) += wdt977.o
-obj-$(CONFIG_IXP2000_WATCHDOG) += ixp2000_wdt.o
 obj-$(CONFIG_IXP4XX_WATCHDOG) += ixp4xx_wdt.o
 obj-$(CONFIG_KS8695_WATCHDOG) += ks8695_wdt.o
 obj-$(CONFIG_S3C2410_WATCHDOG) += s3c2410_wdt.o
@@ -81,6 +80,7 @@
 obj-$(CONFIG_IBMASR) += ibmasr.o
 obj-$(CONFIG_WAFER_WDT) += wafer5823wdt.o
 obj-$(CONFIG_I6300ESB_WDT) += i6300esb.o
+obj-$(CONFIG_IE6XX_WDT) += ie6xx_wdt.o
 obj-$(CONFIG_ITCO_WDT) += iTCO_wdt.o
 ifeq ($(CONFIG_ITCO_VENDOR_SUPPORT),y)
 obj-$(CONFIG_ITCO_WDT) += iTCO_vendor_support.o
diff --git a/drivers/watchdog/ar7_wdt.c b/drivers/watchdog/ar7_wdt.c
index 639ae9a..dc30dbd 100644
--- a/drivers/watchdog/ar7_wdt.c
+++ b/drivers/watchdog/ar7_wdt.c
@@ -282,29 +282,19 @@
 		platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
 	if (!ar7_regs_wdt) {
 		pr_err("could not get registers resource\n");
-		rc = -ENODEV;
-		goto out;
+		return -ENODEV;
 	}
 
-	if (!request_mem_region(ar7_regs_wdt->start,
-				resource_size(ar7_regs_wdt), LONGNAME)) {
-		pr_warn("watchdog I/O region busy\n");
-		rc = -EBUSY;
-		goto out;
-	}
-
-	ar7_wdt = ioremap(ar7_regs_wdt->start, resource_size(ar7_regs_wdt));
+	ar7_wdt = devm_request_and_ioremap(&pdev->dev, ar7_regs_wdt);
 	if (!ar7_wdt) {
 		pr_err("could not ioremap registers\n");
-		rc = -ENXIO;
-		goto out_mem_region;
+		return -ENXIO;
 	}
 
 	vbus_clk = clk_get(NULL, "vbus");
 	if (IS_ERR(vbus_clk)) {
 		pr_err("could not get vbus clock\n");
-		rc = PTR_ERR(vbus_clk);
-		goto out_mem_region;
+		return PTR_ERR(vbus_clk);
 	}
 
 	ar7_wdt_disable_wdt();
@@ -314,24 +304,21 @@
 	rc = misc_register(&ar7_wdt_miscdev);
 	if (rc) {
 		pr_err("unable to register misc device\n");
-		goto out_alloc;
+		goto out;
 	}
-	goto out;
+	return 0;
 
-out_alloc:
-	iounmap(ar7_wdt);
-out_mem_region:
-	release_mem_region(ar7_regs_wdt->start, resource_size(ar7_regs_wdt));
 out:
+	clk_put(vbus_clk);
+	vbus_clk = NULL;
 	return rc;
 }
 
 static int __devexit ar7_wdt_remove(struct platform_device *pdev)
 {
 	misc_deregister(&ar7_wdt_miscdev);
-	iounmap(ar7_wdt);
-	release_mem_region(ar7_regs_wdt->start, resource_size(ar7_regs_wdt));
-
+	clk_put(vbus_clk);
+	vbus_clk = NULL;
 	return 0;
 }
 
diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c
index 23885f2d..2b76381 100644
--- a/drivers/watchdog/hpwdt.c
+++ b/drivers/watchdog/hpwdt.c
@@ -861,16 +861,6 @@
 	.remove = __devexit_p(hpwdt_exit),
 };
 
-static void __exit hpwdt_cleanup(void)
-{
-	pci_unregister_driver(&hpwdt_driver);
-}
-
-static int __init hpwdt_init(void)
-{
-	return pci_register_driver(&hpwdt_driver);
-}
-
 MODULE_AUTHOR("Tom Mingarelli");
 MODULE_DESCRIPTION("hp watchdog driver");
 MODULE_LICENSE("GPL");
@@ -889,5 +879,4 @@
 MODULE_PARM_DESC(allow_kdump, "Start a kernel dump after NMI occurs");
 #endif /* !CONFIG_HPWDT_NMI_DECODING */
 
-module_init(hpwdt_init);
-module_exit(hpwdt_cleanup);
+module_pci_driver(hpwdt_driver);
diff --git a/drivers/watchdog/i6300esb.c b/drivers/watchdog/i6300esb.c
index 738032a..276877d 100644
--- a/drivers/watchdog/i6300esb.c
+++ b/drivers/watchdog/i6300esb.c
@@ -492,19 +492,7 @@
 	.shutdown       = esb_shutdown,
 };
 
-static int __init watchdog_init(void)
-{
-	return pci_register_driver(&esb_driver);
-}
-
-static void __exit watchdog_cleanup(void)
-{
-	pci_unregister_driver(&esb_driver);
-	pr_info("Watchdog Module Unloaded\n");
-}
-
-module_init(watchdog_init);
-module_exit(watchdog_cleanup);
+module_pci_driver(esb_driver);
 
 MODULE_AUTHOR("Ross Biro and David Härdeman");
 MODULE_DESCRIPTION("Watchdog driver for Intel 6300ESB chipsets");
diff --git a/drivers/watchdog/ie6xx_wdt.c b/drivers/watchdog/ie6xx_wdt.c
new file mode 100644
index 0000000..5f0d776
--- /dev/null
+++ b/drivers/watchdog/ie6xx_wdt.c
@@ -0,0 +1,348 @@
+/*
+ *      Intel Atom E6xx Watchdog driver
+ *
+ *      Copyright (C) 2011 Alexander Stein
+ *                <alexander.stein@systec-electronic.com>
+ *
+ *      This program is free software; you can redistribute it and/or
+ *      modify it under the terms of version 2 of the GNU General
+ *      Public License 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.
+ *      You should have received a copy of the GNU General Public
+ *      License along with this program; if not, write to the Free
+ *      Software Foundation, Inc., 59 Temple Place - Suite 330,
+ *      Boston, MA  02111-1307, USA.
+ *      The full GNU General Public License is included in this
+ *      distribution in the file called COPYING.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/watchdog.h>
+#include <linux/miscdevice.h>
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <linux/spinlock.h>
+
+#define DRIVER_NAME "ie6xx_wdt"
+
+#define PV1	0x00
+#define PV2	0x04
+
+#define RR0	0x0c
+#define RR1	0x0d
+#define WDT_RELOAD	0x01
+#define WDT_TOUT	0x02
+
+#define WDTCR	0x10
+#define WDT_PRE_SEL	0x04
+#define WDT_RESET_SEL	0x08
+#define WDT_RESET_EN	0x10
+#define WDT_TOUT_EN	0x20
+
+#define DCR	0x14
+
+#define WDTLR	0x18
+#define WDT_LOCK	0x01
+#define WDT_ENABLE	0x02
+#define WDT_TOUT_CNF	0x03
+
+#define MIN_TIME	1
+#define MAX_TIME	(10 * 60) /* 10 minutes */
+#define DEFAULT_TIME	60
+
+static unsigned int timeout = DEFAULT_TIME;
+module_param(timeout, uint, 0);
+MODULE_PARM_DESC(timeout,
+		"Default Watchdog timer setting ("
+		__MODULE_STRING(DEFAULT_TIME) "s)."
+		"The range is from 1 to 600");
+
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
+MODULE_PARM_DESC(nowayout,
+	"Watchdog cannot be stopped once started (default="
+		__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static u8 resetmode = 0x10;
+module_param(resetmode, byte, 0);
+MODULE_PARM_DESC(resetmode,
+	"Resetmode bits: 0x08 warm reset (cold reset otherwise), "
+	"0x10 reset enable, 0x20 disable toggle GPIO[4] (default=0x10)");
+
+static struct {
+	unsigned short sch_wdtba;
+	struct spinlock unlock_sequence;
+#ifdef CONFIG_DEBUG_FS
+	struct dentry *debugfs;
+#endif
+} ie6xx_wdt_data;
+
+/*
+ * This is needed to write to preload and reload registers
+ * struct ie6xx_wdt_data.unlock_sequence must be used
+ * to prevent sequence interrupts
+ */
+static void ie6xx_wdt_unlock_registers(void)
+{
+	outb(0x80, ie6xx_wdt_data.sch_wdtba + RR0);
+	outb(0x86, ie6xx_wdt_data.sch_wdtba + RR0);
+}
+
+static int ie6xx_wdt_ping(struct watchdog_device *wdd)
+{
+	spin_lock(&ie6xx_wdt_data.unlock_sequence);
+	ie6xx_wdt_unlock_registers();
+	outb(WDT_RELOAD, ie6xx_wdt_data.sch_wdtba + RR1);
+	spin_unlock(&ie6xx_wdt_data.unlock_sequence);
+	return 0;
+}
+
+static int ie6xx_wdt_set_timeout(struct watchdog_device *wdd, unsigned int t)
+{
+	u32 preload;
+	u64 clock;
+	u8 wdtcr;
+
+	/* Watchdog clock is PCI Clock (33MHz) */
+	clock = 33000000;
+	/* and the preload value is loaded into [34:15] of the down counter */
+	preload = (t * clock) >> 15;
+	/*
+	 * Manual states preload must be one less.
+	 * Does not wrap as t is at least 1
+	 */
+	preload -= 1;
+
+	spin_lock(&ie6xx_wdt_data.unlock_sequence);
+
+	/* Set ResetMode & Enable prescaler for range 10ms to 10 min */
+	wdtcr = resetmode & 0x38;
+	outb(wdtcr, ie6xx_wdt_data.sch_wdtba + WDTCR);
+
+	ie6xx_wdt_unlock_registers();
+	outl(0, ie6xx_wdt_data.sch_wdtba + PV1);
+
+	ie6xx_wdt_unlock_registers();
+	outl(preload, ie6xx_wdt_data.sch_wdtba + PV2);
+
+	ie6xx_wdt_unlock_registers();
+	outb(WDT_RELOAD | WDT_TOUT, ie6xx_wdt_data.sch_wdtba + RR1);
+
+	spin_unlock(&ie6xx_wdt_data.unlock_sequence);
+
+	wdd->timeout = t;
+	return 0;
+}
+
+static int ie6xx_wdt_start(struct watchdog_device *wdd)
+{
+	ie6xx_wdt_set_timeout(wdd, wdd->timeout);
+
+	/* Enable the watchdog timer */
+	spin_lock(&ie6xx_wdt_data.unlock_sequence);
+	outb(WDT_ENABLE, ie6xx_wdt_data.sch_wdtba + WDTLR);
+	spin_unlock(&ie6xx_wdt_data.unlock_sequence);
+
+	return 0;
+}
+
+static int ie6xx_wdt_stop(struct watchdog_device *wdd)
+{
+	if (inb(ie6xx_wdt_data.sch_wdtba + WDTLR) & WDT_LOCK)
+		return -1;
+
+	/* Disable the watchdog timer */
+	spin_lock(&ie6xx_wdt_data.unlock_sequence);
+	outb(0, ie6xx_wdt_data.sch_wdtba + WDTLR);
+	spin_unlock(&ie6xx_wdt_data.unlock_sequence);
+
+	return 0;
+}
+
+static const struct watchdog_info ie6xx_wdt_info = {
+	.identity =	"Intel Atom E6xx Watchdog",
+	.options =	WDIOF_SETTIMEOUT |
+			WDIOF_MAGICCLOSE |
+			WDIOF_KEEPALIVEPING,
+};
+
+static const struct watchdog_ops ie6xx_wdt_ops = {
+	.owner =	THIS_MODULE,
+	.start =	ie6xx_wdt_start,
+	.stop =		ie6xx_wdt_stop,
+	.ping =		ie6xx_wdt_ping,
+	.set_timeout =	ie6xx_wdt_set_timeout,
+};
+
+static struct watchdog_device ie6xx_wdt_dev = {
+	.info =		&ie6xx_wdt_info,
+	.ops =		&ie6xx_wdt_ops,
+	.min_timeout =	MIN_TIME,
+	.max_timeout =	MAX_TIME,
+};
+
+#ifdef CONFIG_DEBUG_FS
+
+static int ie6xx_wdt_dbg_show(struct seq_file *s, void *unused)
+{
+	seq_printf(s, "PV1   = 0x%08x\n",
+		inl(ie6xx_wdt_data.sch_wdtba + PV1));
+	seq_printf(s, "PV2   = 0x%08x\n",
+		inl(ie6xx_wdt_data.sch_wdtba + PV2));
+	seq_printf(s, "RR    = 0x%08x\n",
+		inw(ie6xx_wdt_data.sch_wdtba + RR0));
+	seq_printf(s, "WDTCR = 0x%08x\n",
+		inw(ie6xx_wdt_data.sch_wdtba + WDTCR));
+	seq_printf(s, "DCR   = 0x%08x\n",
+		inl(ie6xx_wdt_data.sch_wdtba + DCR));
+	seq_printf(s, "WDTLR = 0x%08x\n",
+		inw(ie6xx_wdt_data.sch_wdtba + WDTLR));
+
+	seq_printf(s, "\n");
+	return 0;
+}
+
+static int ie6xx_wdt_dbg_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, ie6xx_wdt_dbg_show, NULL);
+}
+
+static const struct file_operations ie6xx_wdt_dbg_operations = {
+	.open		= ie6xx_wdt_dbg_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static void __devinit ie6xx_wdt_debugfs_init(void)
+{
+	/* /sys/kernel/debug/ie6xx_wdt */
+	ie6xx_wdt_data.debugfs = debugfs_create_file("ie6xx_wdt",
+		S_IFREG | S_IRUGO, NULL, NULL, &ie6xx_wdt_dbg_operations);
+}
+
+static void __devexit ie6xx_wdt_debugfs_exit(void)
+{
+	debugfs_remove(ie6xx_wdt_data.debugfs);
+}
+
+#else
+static void __devinit ie6xx_wdt_debugfs_init(void)
+{
+}
+
+static void __devexit ie6xx_wdt_debugfs_exit(void)
+{
+}
+#endif
+
+static int __devinit ie6xx_wdt_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	u8 wdtlr;
+	int ret;
+
+	res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+	if (!res)
+		return -ENODEV;
+
+	if (!request_region(res->start, resource_size(res), pdev->name)) {
+		dev_err(&pdev->dev, "Watchdog region 0x%llx already in use!\n",
+			(u64)res->start);
+		return -EBUSY;
+	}
+
+	ie6xx_wdt_data.sch_wdtba = res->start;
+	dev_dbg(&pdev->dev, "WDT = 0x%X\n", ie6xx_wdt_data.sch_wdtba);
+
+	ie6xx_wdt_dev.timeout = timeout;
+	watchdog_set_nowayout(&ie6xx_wdt_dev, nowayout);
+
+	spin_lock_init(&ie6xx_wdt_data.unlock_sequence);
+
+	wdtlr = inb(ie6xx_wdt_data.sch_wdtba + WDTLR);
+	if (wdtlr & WDT_LOCK)
+		dev_warn(&pdev->dev,
+			"Watchdog Timer is Locked (Reg=0x%x)\n", wdtlr);
+
+	ie6xx_wdt_debugfs_init();
+
+	ret = watchdog_register_device(&ie6xx_wdt_dev);
+	if (ret) {
+		dev_err(&pdev->dev,
+			"Watchdog timer: cannot register device (err =%d)\n",
+									ret);
+		goto misc_register_error;
+	}
+
+	return 0;
+
+misc_register_error:
+	ie6xx_wdt_debugfs_exit();
+	release_region(res->start, resource_size(res));
+	ie6xx_wdt_data.sch_wdtba = 0;
+	return ret;
+}
+
+static int __devexit ie6xx_wdt_remove(struct platform_device *pdev)
+{
+	struct resource *res;
+
+	res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+	ie6xx_wdt_stop(NULL);
+	watchdog_unregister_device(&ie6xx_wdt_dev);
+	ie6xx_wdt_debugfs_exit();
+	release_region(res->start, resource_size(res));
+	ie6xx_wdt_data.sch_wdtba = 0;
+
+	return 0;
+}
+
+static struct platform_driver ie6xx_wdt_driver = {
+	.probe		= ie6xx_wdt_probe,
+	.remove		= __devexit_p(ie6xx_wdt_remove),
+	.driver		= {
+		.name	= DRIVER_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init ie6xx_wdt_init(void)
+{
+	/* Check boot parameters to verify that their initial values */
+	/* are in range. */
+	if ((timeout < MIN_TIME) ||
+	    (timeout > MAX_TIME)) {
+		pr_err("Watchdog timer: value of timeout %d (dec) "
+		  "is out of range from %d to %d (dec)\n",
+		  timeout, MIN_TIME, MAX_TIME);
+		return -EINVAL;
+	}
+
+	return platform_driver_register(&ie6xx_wdt_driver);
+}
+
+static void __exit ie6xx_wdt_exit(void)
+{
+	platform_driver_unregister(&ie6xx_wdt_driver);
+}
+
+late_initcall(ie6xx_wdt_init);
+module_exit(ie6xx_wdt_exit);
+
+MODULE_AUTHOR("Alexander Stein <alexander.stein@systec-electronic.com>");
+MODULE_DESCRIPTION("Intel Atom E6xx Watchdog Device Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/watchdog/it87_wdt.c b/drivers/watchdog/it87_wdt.c
index 8a741bc..d3dcc69 100644
--- a/drivers/watchdog/it87_wdt.c
+++ b/drivers/watchdog/it87_wdt.c
@@ -12,7 +12,8 @@
  *		    http://www.ite.com.tw/
  *
  *	Support of the watchdog timers, which are available on
- *	IT8702, IT8712, IT8716, IT8718, IT8720, IT8721 and IT8726.
+ *	IT8702, IT8712, IT8716, IT8718, IT8720, IT8721, IT8726
+ *	and IT8728.
  *
  *	This program is free software; you can redistribute it and/or
  *	modify it under the terms of the GNU General Public License
@@ -84,6 +85,7 @@
 #define IT8720_ID	0x8720
 #define IT8721_ID	0x8721
 #define IT8726_ID	0x8726	/* the data sheet suggest wrongly 0x8716 */
+#define IT8728_ID	0x8728
 
 /* GPIO Configuration Registers LDN=0x07 */
 #define WDTCTRL		0x71
@@ -95,7 +97,7 @@
 #define WDT_CIRINT	0x80
 #define WDT_MOUSEINT	0x40
 #define WDT_KYBINT	0x20
-#define WDT_GAMEPORT	0x10 /* not in it8718, it8720, it8721 */
+#define WDT_GAMEPORT	0x10 /* not in it8718, it8720, it8721, it8728 */
 #define WDT_FORCE	0x02
 #define WDT_ZERO	0x01
 
@@ -616,6 +618,7 @@
 	case IT8718_ID:
 	case IT8720_ID:
 	case IT8721_ID:
+	case IT8728_ID:
 		max_units = 65535;
 		try_gameport = 0;
 		break;
diff --git a/drivers/watchdog/ixp2000_wdt.c b/drivers/watchdog/ixp2000_wdt.c
deleted file mode 100644
index 3f047a5..0000000
--- a/drivers/watchdog/ixp2000_wdt.c
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- * drivers/char/watchdog/ixp2000_wdt.c
- *
- * Watchdog driver for Intel IXP2000 network processors
- *
- * Adapted from the IXP4xx watchdog driver by Lennert Buytenhek.
- * The original version carries these notices:
- *
- * Author: Deepak Saxena <dsaxena@plexity.net>
- *
- * Copyright 2004 (c) MontaVista, Software, Inc.
- * Based on sa1100 driver, Copyright (C) 2000 Oleg Drokin <green@crimea.edu>
- *
- * This file is licensed under  the terms of the GNU General Public
- * License version 2. This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/types.h>
-#include <linux/timer.h>
-#include <linux/kernel.h>
-#include <linux/fs.h>
-#include <linux/miscdevice.h>
-#include <linux/watchdog.h>
-#include <linux/init.h>
-#include <linux/bitops.h>
-#include <linux/uaccess.h>
-#include <mach/hardware.h>
-
-static bool nowayout = WATCHDOG_NOWAYOUT;
-static unsigned int heartbeat = 60;	/* (secs) Default is 1 minute */
-static unsigned long wdt_status;
-static DEFINE_SPINLOCK(wdt_lock);
-
-#define	WDT_IN_USE		0
-#define	WDT_OK_TO_CLOSE		1
-
-static unsigned long wdt_tick_rate;
-
-static void wdt_enable(void)
-{
-	spin_lock(&wdt_lock);
-	ixp2000_reg_write(IXP2000_RESET0, *(IXP2000_RESET0) | WDT_RESET_ENABLE);
-	ixp2000_reg_write(IXP2000_TWDE, WDT_ENABLE);
-	ixp2000_reg_write(IXP2000_T4_CLD, heartbeat * wdt_tick_rate);
-	ixp2000_reg_write(IXP2000_T4_CTL, TIMER_DIVIDER_256 | TIMER_ENABLE);
-	spin_unlock(&wdt_lock);
-}
-
-static void wdt_disable(void)
-{
-	spin_lock(&wdt_lock);
-	ixp2000_reg_write(IXP2000_T4_CTL, 0);
-	spin_unlock(&wdt_lock);
-}
-
-static void wdt_keepalive(void)
-{
-	spin_lock(&wdt_lock);
-	ixp2000_reg_write(IXP2000_T4_CLD, heartbeat * wdt_tick_rate);
-	spin_unlock(&wdt_lock);
-}
-
-static int ixp2000_wdt_open(struct inode *inode, struct file *file)
-{
-	if (test_and_set_bit(WDT_IN_USE, &wdt_status))
-		return -EBUSY;
-
-	clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
-
-	wdt_enable();
-
-	return nonseekable_open(inode, file);
-}
-
-static ssize_t ixp2000_wdt_write(struct file *file, const char *data,
-						size_t len, loff_t *ppos)
-{
-	if (len) {
-		if (!nowayout) {
-			size_t i;
-
-			clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
-
-			for (i = 0; i != len; i++) {
-				char c;
-
-				if (get_user(c, data + i))
-					return -EFAULT;
-				if (c == 'V')
-					set_bit(WDT_OK_TO_CLOSE, &wdt_status);
-			}
-		}
-		wdt_keepalive();
-	}
-
-	return len;
-}
-
-
-static const struct watchdog_info ident = {
-	.options	= WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT |
-				WDIOF_KEEPALIVEPING,
-	.identity	= "IXP2000 Watchdog",
-};
-
-static long ixp2000_wdt_ioctl(struct file *file, unsigned int cmd,
-							unsigned long arg)
-{
-	int ret = -ENOTTY;
-	int time;
-
-	switch (cmd) {
-	case WDIOC_GETSUPPORT:
-		ret = copy_to_user((struct watchdog_info *)arg, &ident,
-				   sizeof(ident)) ? -EFAULT : 0;
-		break;
-
-	case WDIOC_GETSTATUS:
-		ret = put_user(0, (int *)arg);
-		break;
-
-	case WDIOC_GETBOOTSTATUS:
-		ret = put_user(0, (int *)arg);
-		break;
-
-	case WDIOC_KEEPALIVE:
-		wdt_enable();
-		ret = 0;
-		break;
-
-	case WDIOC_SETTIMEOUT:
-		ret = get_user(time, (int *)arg);
-		if (ret)
-			break;
-
-		if (time <= 0 || time > 60) {
-			ret = -EINVAL;
-			break;
-		}
-
-		heartbeat = time;
-		wdt_keepalive();
-		/* Fall through */
-
-	case WDIOC_GETTIMEOUT:
-		ret = put_user(heartbeat, (int *)arg);
-		break;
-	}
-
-	return ret;
-}
-
-static int ixp2000_wdt_release(struct inode *inode, struct file *file)
-{
-	if (test_bit(WDT_OK_TO_CLOSE, &wdt_status))
-		wdt_disable();
-	else
-		pr_crit("Device closed unexpectedly - timer will not stop\n");
-	clear_bit(WDT_IN_USE, &wdt_status);
-	clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
-
-	return 0;
-}
-
-
-static const struct file_operations ixp2000_wdt_fops = {
-	.owner		= THIS_MODULE,
-	.llseek		= no_llseek,
-	.write		= ixp2000_wdt_write,
-	.unlocked_ioctl	= ixp2000_wdt_ioctl,
-	.open		= ixp2000_wdt_open,
-	.release	= ixp2000_wdt_release,
-};
-
-static struct miscdevice ixp2000_wdt_miscdev = {
-	.minor		= WATCHDOG_MINOR,
-	.name		= "watchdog",
-	.fops		= &ixp2000_wdt_fops,
-};
-
-static int __init ixp2000_wdt_init(void)
-{
-	if ((*IXP2000_PRODUCT_ID & 0x001ffef0) == 0x00000000) {
-		pr_info("Unable to use IXP2000 watchdog due to IXP2800 erratum #25\n");
-		return -EIO;
-	}
-	wdt_tick_rate = (*IXP2000_T1_CLD * HZ) / 256;
-	return misc_register(&ixp2000_wdt_miscdev);
-}
-
-static void __exit ixp2000_wdt_exit(void)
-{
-	misc_deregister(&ixp2000_wdt_miscdev);
-}
-
-module_init(ixp2000_wdt_init);
-module_exit(ixp2000_wdt_exit);
-
-MODULE_AUTHOR("Deepak Saxena <dsaxena@plexity.net>");
-MODULE_DESCRIPTION("IXP2000 Network Processor Watchdog");
-
-module_param(heartbeat, int, 0);
-MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds (default 60s)");
-
-module_param(nowayout, bool, 0);
-MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
-
-MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
-
diff --git a/drivers/watchdog/pcwd_pci.c b/drivers/watchdog/pcwd_pci.c
index c891399..ee6900d 100644
--- a/drivers/watchdog/pcwd_pci.c
+++ b/drivers/watchdog/pcwd_pci.c
@@ -707,6 +707,7 @@
 		goto err_out_disable_device;
 	}
 
+	spin_lock_init(&pcipcwd_private.io_lock);
 	pcipcwd_private.pdev = pdev;
 	pcipcwd_private.io_addr = pci_resource_start(pdev, 0);
 
@@ -814,22 +815,7 @@
 	.remove		= __devexit_p(pcipcwd_card_exit),
 };
 
-static int __init pcipcwd_init_module(void)
-{
-	spin_lock_init(&pcipcwd_private.io_lock);
-
-	return pci_register_driver(&pcipcwd_driver);
-}
-
-static void __exit pcipcwd_cleanup_module(void)
-{
-	pci_unregister_driver(&pcipcwd_driver);
-
-	pr_info("Watchdog Module Unloaded\n");
-}
-
-module_init(pcipcwd_init_module);
-module_exit(pcipcwd_cleanup_module);
+module_pci_driver(pcipcwd_driver);
 
 MODULE_AUTHOR("Wim Van Sebroeck <wim@iguana.be>");
 MODULE_DESCRIPTION("Berkshire PCI-PC Watchdog driver");
diff --git a/drivers/watchdog/pnx4008_wdt.c b/drivers/watchdog/pnx4008_wdt.c
index 6b8432f..87722e1 100644
--- a/drivers/watchdog/pnx4008_wdt.c
+++ b/drivers/watchdog/pnx4008_wdt.c
@@ -32,6 +32,7 @@
 #include <linux/io.h>
 #include <linux/slab.h>
 #include <linux/err.h>
+#include <linux/of.h>
 #include <mach/hardware.h>
 
 /* WatchDog Timer - Chapter 23 Page 207 */
@@ -201,10 +202,19 @@
 	return 0;
 }
 
+#ifdef CONFIG_OF
+static const struct of_device_id pnx4008_wdt_match[] = {
+	{ .compatible = "nxp,pnx4008-wdt" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, pnx4008_wdt_match);
+#endif
+
 static struct platform_driver platform_wdt_driver = {
 	.driver = {
 		.name = "pnx4008-watchdog",
 		.owner	= THIS_MODULE,
+		.of_match_table = of_match_ptr(pnx4008_wdt_match),
 	},
 	.probe = pnx4008_wdt_probe,
 	.remove = __devexit_p(pnx4008_wdt_remove),
diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c
index 04e5a6d..200ece5 100644
--- a/drivers/watchdog/s3c2410_wdt.c
+++ b/drivers/watchdog/s3c2410_wdt.c
@@ -40,6 +40,7 @@
 #include <linux/cpufreq.h>
 #include <linux/slab.h>
 #include <linux/err.h>
+#include <linux/of.h>
 
 #include <mach/map.h>
 
@@ -201,7 +202,7 @@
 	writel(count, wdt_base + S3C2410_WTDAT);
 	writel(wtcon, wdt_base + S3C2410_WTCON);
 
-	wdd->timeout = timeout;
+	wdd->timeout = (count * divisor) / freq;
 
 	return 0;
 }
@@ -503,8 +504,6 @@
 	{},
 };
 MODULE_DEVICE_TABLE(of, s3c2410_wdt_match);
-#else
-#define s3c2410_wdt_match NULL
 #endif
 
 static struct platform_driver s3c2410wdt_driver = {
@@ -516,7 +515,7 @@
 	.driver		= {
 		.owner	= THIS_MODULE,
 		.name	= "s3c2410-wdt",
-		.of_match_table	= s3c2410_wdt_match,
+		.of_match_table	= of_match_ptr(s3c2410_wdt_match),
 	},
 };
 
diff --git a/drivers/watchdog/sch311x_wdt.c b/drivers/watchdog/sch311x_wdt.c
index bd86f32..f847700 100644
--- a/drivers/watchdog/sch311x_wdt.c
+++ b/drivers/watchdog/sch311x_wdt.c
@@ -41,7 +41,6 @@
 #define DRV_NAME	"sch311x_wdt"
 
 /* Runtime registers */
-#define RESGEN			0x1d
 #define GP60			0x47
 #define WDT_TIME_OUT		0x65
 #define WDT_VAL			0x66
@@ -69,10 +68,6 @@
 module_param(force_id, ushort, 0);
 MODULE_PARM_DESC(force_id, "Override the detected device ID");
 
-static unsigned short therm_trip;
-module_param(therm_trip, ushort, 0);
-MODULE_PARM_DESC(therm_trip, "Should a ThermTrip trigger the reset generator");
-
 #define WATCHDOG_TIMEOUT 60		/* 60 sec default timeout */
 static int timeout = WATCHDOG_TIMEOUT;	/* in seconds */
 module_param(timeout, int, 0);
@@ -358,26 +353,16 @@
 static int __devinit sch311x_wdt_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
-	unsigned char val;
 	int err;
 
 	spin_lock_init(&sch311x_wdt_data.io_lock);
 
-	if (!request_region(sch311x_wdt_data.runtime_reg + RESGEN, 1,
-								DRV_NAME)) {
-		dev_err(dev, "Failed to request region 0x%04x-0x%04x.\n",
-			sch311x_wdt_data.runtime_reg + RESGEN,
-			sch311x_wdt_data.runtime_reg + RESGEN);
-		err = -EBUSY;
-		goto exit;
-	}
-
 	if (!request_region(sch311x_wdt_data.runtime_reg + GP60, 1, DRV_NAME)) {
 		dev_err(dev, "Failed to request region 0x%04x-0x%04x.\n",
 			sch311x_wdt_data.runtime_reg + GP60,
 			sch311x_wdt_data.runtime_reg + GP60);
 		err = -EBUSY;
-		goto exit_release_region;
+		goto exit;
 	}
 
 	if (!request_region(sch311x_wdt_data.runtime_reg + WDT_TIME_OUT, 4,
@@ -386,7 +371,7 @@
 			sch311x_wdt_data.runtime_reg + WDT_TIME_OUT,
 			sch311x_wdt_data.runtime_reg + WDT_CTRL);
 		err = -EBUSY;
-		goto exit_release_region2;
+		goto exit_release_region;
 	}
 
 	/* Make sure that the watchdog is not running */
@@ -414,24 +399,13 @@
 	/* Get status at boot */
 	sch311x_wdt_get_status(&sch311x_wdt_data.boot_status);
 
-	/* enable watchdog */
-	/* -- Reset Generator --
-	 * Bit 0   Enable Watchdog Timer Generation: 0* = Enabled, 1 = Disabled
-	 * Bit 1   Thermtrip Source Select: O* = No Source, 1 = Source
-	 * Bit 2   WDT2_CTL: WDT input bit
-	 * Bit 3-7 Reserved
-	 */
-	outb(0, sch311x_wdt_data.runtime_reg + RESGEN);
-	val = therm_trip ? 0x06 : 0x04;
-	outb(val, sch311x_wdt_data.runtime_reg + RESGEN);
-
 	sch311x_wdt_miscdev.parent = dev;
 
 	err = misc_register(&sch311x_wdt_miscdev);
 	if (err != 0) {
 		dev_err(dev, "cannot register miscdev on minor=%d (err=%d)\n",
 							WATCHDOG_MINOR, err);
-		goto exit_release_region3;
+		goto exit_release_region2;
 	}
 
 	dev_info(dev,
@@ -440,12 +414,10 @@
 
 	return 0;
 
-exit_release_region3:
-	release_region(sch311x_wdt_data.runtime_reg + WDT_TIME_OUT, 4);
 exit_release_region2:
-	release_region(sch311x_wdt_data.runtime_reg + GP60, 1);
+	release_region(sch311x_wdt_data.runtime_reg + WDT_TIME_OUT, 4);
 exit_release_region:
-	release_region(sch311x_wdt_data.runtime_reg + RESGEN, 1);
+	release_region(sch311x_wdt_data.runtime_reg + GP60, 1);
 	sch311x_wdt_data.runtime_reg = 0;
 exit:
 	return err;
@@ -461,7 +433,6 @@
 	misc_deregister(&sch311x_wdt_miscdev);
 	release_region(sch311x_wdt_data.runtime_reg + WDT_TIME_OUT, 4);
 	release_region(sch311x_wdt_data.runtime_reg + GP60, 1);
-	release_region(sch311x_wdt_data.runtime_reg + RESGEN, 1);
 	sch311x_wdt_data.runtime_reg = 0;
 	return 0;
 }
diff --git a/drivers/watchdog/shwdt.c b/drivers/watchdog/shwdt.c
index 93958a7..e5b59be 100644
--- a/drivers/watchdog/shwdt.c
+++ b/drivers/watchdog/shwdt.c
@@ -3,7 +3,7 @@
  *
  * Watchdog driver for integrated watchdog in the SuperH processors.
  *
- * Copyright (C) 2001 - 2010  Paul Mundt <lethal@linux-sh.org>
+ * Copyright (C) 2001 - 2012  Paul Mundt <lethal@linux-sh.org>
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -25,16 +25,15 @@
 #include <linux/platform_device.h>
 #include <linux/init.h>
 #include <linux/types.h>
+#include <linux/spinlock.h>
 #include <linux/miscdevice.h>
 #include <linux/watchdog.h>
-#include <linux/reboot.h>
-#include <linux/notifier.h>
-#include <linux/ioport.h>
+#include <linux/pm_runtime.h>
 #include <linux/fs.h>
 #include <linux/mm.h>
 #include <linux/slab.h>
 #include <linux/io.h>
-#include <linux/uaccess.h>
+#include <linux/clk.h>
 #include <asm/watchdog.h>
 
 #define DRV_NAME "sh-wdt"
@@ -69,10 +68,6 @@
 static int clock_division_ratio = WTCSR_CKS_4096;
 #define next_ping_period(cks)	(jiffies + msecs_to_jiffies(cks - 4))
 
-static const struct watchdog_info sh_wdt_info;
-static struct platform_device *sh_wdt_dev;
-static DEFINE_SPINLOCK(shwdt_lock);
-
 #define WATCHDOG_HEARTBEAT 30			/* 30 sec default heartbeat */
 static int heartbeat = WATCHDOG_HEARTBEAT;	/* in seconds */
 static bool nowayout = WATCHDOG_NOWAYOUT;
@@ -81,19 +76,22 @@
 struct sh_wdt {
 	void __iomem		*base;
 	struct device		*dev;
+	struct clk		*clk;
+	spinlock_t		lock;
 
 	struct timer_list	timer;
-
-	unsigned long		enabled;
-	char			expect_close;
 };
 
-static void sh_wdt_start(struct sh_wdt *wdt)
+static int sh_wdt_start(struct watchdog_device *wdt_dev)
 {
+	struct sh_wdt *wdt = watchdog_get_drvdata(wdt_dev);
 	unsigned long flags;
 	u8 csr;
 
-	spin_lock_irqsave(&shwdt_lock, flags);
+	pm_runtime_get_sync(wdt->dev);
+	clk_enable(wdt->clk);
+
+	spin_lock_irqsave(&wdt->lock, flags);
 
 	next_heartbeat = jiffies + (heartbeat * HZ);
 	mod_timer(&wdt->timer, next_ping_period(clock_division_ratio));
@@ -122,15 +120,18 @@
 	csr &= ~RSTCSR_RSTS;
 	sh_wdt_write_rstcsr(csr);
 #endif
-	spin_unlock_irqrestore(&shwdt_lock, flags);
+	spin_unlock_irqrestore(&wdt->lock, flags);
+
+	return 0;
 }
 
-static void sh_wdt_stop(struct sh_wdt *wdt)
+static int sh_wdt_stop(struct watchdog_device *wdt_dev)
 {
+	struct sh_wdt *wdt = watchdog_get_drvdata(wdt_dev);
 	unsigned long flags;
 	u8 csr;
 
-	spin_lock_irqsave(&shwdt_lock, flags);
+	spin_lock_irqsave(&wdt->lock, flags);
 
 	del_timer(&wdt->timer);
 
@@ -138,28 +139,39 @@
 	csr &= ~WTCSR_TME;
 	sh_wdt_write_csr(csr);
 
-	spin_unlock_irqrestore(&shwdt_lock, flags);
+	spin_unlock_irqrestore(&wdt->lock, flags);
+
+	clk_disable(wdt->clk);
+	pm_runtime_put_sync(wdt->dev);
+
+	return 0;
 }
 
-static inline void sh_wdt_keepalive(struct sh_wdt *wdt)
+static int sh_wdt_keepalive(struct watchdog_device *wdt_dev)
 {
+	struct sh_wdt *wdt = watchdog_get_drvdata(wdt_dev);
 	unsigned long flags;
 
-	spin_lock_irqsave(&shwdt_lock, flags);
+	spin_lock_irqsave(&wdt->lock, flags);
 	next_heartbeat = jiffies + (heartbeat * HZ);
-	spin_unlock_irqrestore(&shwdt_lock, flags);
+	spin_unlock_irqrestore(&wdt->lock, flags);
+
+	return 0;
 }
 
-static int sh_wdt_set_heartbeat(int t)
+static int sh_wdt_set_heartbeat(struct watchdog_device *wdt_dev, unsigned t)
 {
+	struct sh_wdt *wdt = watchdog_get_drvdata(wdt_dev);
 	unsigned long flags;
 
 	if (unlikely(t < 1 || t > 3600)) /* arbitrary upper limit */
 		return -EINVAL;
 
-	spin_lock_irqsave(&shwdt_lock, flags);
+	spin_lock_irqsave(&wdt->lock, flags);
 	heartbeat = t;
-	spin_unlock_irqrestore(&shwdt_lock, flags);
+	wdt_dev->timeout = t;
+	spin_unlock_irqrestore(&wdt->lock, flags);
+
 	return 0;
 }
 
@@ -168,7 +180,7 @@
 	struct sh_wdt *wdt = (struct sh_wdt *)data;
 	unsigned long flags;
 
-	spin_lock_irqsave(&shwdt_lock, flags);
+	spin_lock_irqsave(&wdt->lock, flags);
 	if (time_before(jiffies, next_heartbeat)) {
 		u8 csr;
 
@@ -182,137 +194,9 @@
 	} else
 		dev_warn(wdt->dev, "Heartbeat lost! Will not ping "
 		         "the watchdog\n");
-	spin_unlock_irqrestore(&shwdt_lock, flags);
+	spin_unlock_irqrestore(&wdt->lock, flags);
 }
 
-static int sh_wdt_open(struct inode *inode, struct file *file)
-{
-	struct sh_wdt *wdt = platform_get_drvdata(sh_wdt_dev);
-
-	if (test_and_set_bit(0, &wdt->enabled))
-		return -EBUSY;
-	if (nowayout)
-		__module_get(THIS_MODULE);
-
-	file->private_data = wdt;
-
-	sh_wdt_start(wdt);
-
-	return nonseekable_open(inode, file);
-}
-
-static int sh_wdt_close(struct inode *inode, struct file *file)
-{
-	struct sh_wdt *wdt = file->private_data;
-
-	if (wdt->expect_close == 42) {
-		sh_wdt_stop(wdt);
-	} else {
-		dev_crit(wdt->dev, "Unexpected close, not "
-		         "stopping watchdog!\n");
-		sh_wdt_keepalive(wdt);
-	}
-
-	clear_bit(0, &wdt->enabled);
-	wdt->expect_close = 0;
-
-	return 0;
-}
-
-static ssize_t sh_wdt_write(struct file *file, const char *buf,
-			    size_t count, loff_t *ppos)
-{
-	struct sh_wdt *wdt = file->private_data;
-
-	if (count) {
-		if (!nowayout) {
-			size_t i;
-
-			wdt->expect_close = 0;
-
-			for (i = 0; i != count; i++) {
-				char c;
-				if (get_user(c, buf + i))
-					return -EFAULT;
-				if (c == 'V')
-					wdt->expect_close = 42;
-			}
-		}
-		sh_wdt_keepalive(wdt);
-	}
-
-	return count;
-}
-
-static long sh_wdt_ioctl(struct file *file, unsigned int cmd,
-							unsigned long arg)
-{
-	struct sh_wdt *wdt = file->private_data;
-	int new_heartbeat;
-	int options, retval = -EINVAL;
-
-	switch (cmd) {
-	case WDIOC_GETSUPPORT:
-		return copy_to_user((struct watchdog_info *)arg,
-			  &sh_wdt_info, sizeof(sh_wdt_info)) ? -EFAULT : 0;
-	case WDIOC_GETSTATUS:
-	case WDIOC_GETBOOTSTATUS:
-		return put_user(0, (int *)arg);
-	case WDIOC_SETOPTIONS:
-		if (get_user(options, (int *)arg))
-			return -EFAULT;
-
-		if (options & WDIOS_DISABLECARD) {
-			sh_wdt_stop(wdt);
-			retval = 0;
-		}
-
-		if (options & WDIOS_ENABLECARD) {
-			sh_wdt_start(wdt);
-			retval = 0;
-		}
-
-		return retval;
-	case WDIOC_KEEPALIVE:
-		sh_wdt_keepalive(wdt);
-		return 0;
-	case WDIOC_SETTIMEOUT:
-		if (get_user(new_heartbeat, (int *)arg))
-			return -EFAULT;
-
-		if (sh_wdt_set_heartbeat(new_heartbeat))
-			return -EINVAL;
-
-		sh_wdt_keepalive(wdt);
-		/* Fall */
-	case WDIOC_GETTIMEOUT:
-		return put_user(heartbeat, (int *)arg);
-	default:
-		return -ENOTTY;
-	}
-	return 0;
-}
-
-static int sh_wdt_notify_sys(struct notifier_block *this,
-			     unsigned long code, void *unused)
-{
-	struct sh_wdt *wdt = platform_get_drvdata(sh_wdt_dev);
-
-	if (code == SYS_DOWN || code == SYS_HALT)
-		sh_wdt_stop(wdt);
-
-	return NOTIFY_DONE;
-}
-
-static const struct file_operations sh_wdt_fops = {
-	.owner		= THIS_MODULE,
-	.llseek		= no_llseek,
-	.write		= sh_wdt_write,
-	.unlocked_ioctl	= sh_wdt_ioctl,
-	.open		= sh_wdt_open,
-	.release	= sh_wdt_close,
-};
-
 static const struct watchdog_info sh_wdt_info = {
 	.options		= WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT |
 				  WDIOF_MAGICCLOSE,
@@ -320,14 +204,17 @@
 	.identity		= "SH WDT",
 };
 
-static struct notifier_block sh_wdt_notifier = {
-	.notifier_call		= sh_wdt_notify_sys,
+static const struct watchdog_ops sh_wdt_ops = {
+	.owner		= THIS_MODULE,
+	.start		= sh_wdt_start,
+	.stop		= sh_wdt_stop,
+	.ping		= sh_wdt_keepalive,
+	.set_timeout	= sh_wdt_set_heartbeat,
 };
 
-static struct miscdevice sh_wdt_miscdev = {
-	.minor		= WATCHDOG_MINOR,
-	.name		= "watchdog",
-	.fops		= &sh_wdt_fops,
+static struct watchdog_device sh_wdt_dev = {
+	.info	= &sh_wdt_info,
+	.ops	= &sh_wdt_ops,
 };
 
 static int __devinit sh_wdt_probe(struct platform_device *pdev)
@@ -347,39 +234,49 @@
 	if (unlikely(!res))
 		return -EINVAL;
 
-	if (!devm_request_mem_region(&pdev->dev, res->start,
-				     resource_size(res), DRV_NAME))
-		return -EBUSY;
-
 	wdt = devm_kzalloc(&pdev->dev, sizeof(struct sh_wdt), GFP_KERNEL);
-	if (unlikely(!wdt)) {
-		rc = -ENOMEM;
-		goto out_release;
-	}
+	if (unlikely(!wdt))
+		return -ENOMEM;
 
 	wdt->dev = &pdev->dev;
 
-	wdt->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+	wdt->clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(wdt->clk)) {
+		/*
+		 * Clock framework support is optional, continue on
+		 * anyways if we don't find a matching clock.
+		 */
+		wdt->clk = NULL;
+	}
+
+	wdt->base = devm_request_and_ioremap(wdt->dev, res);
 	if (unlikely(!wdt->base)) {
-		rc = -ENXIO;
-		goto out_err;
+		rc = -EADDRNOTAVAIL;
+		goto err;
 	}
 
-	rc = register_reboot_notifier(&sh_wdt_notifier);
+	watchdog_set_nowayout(&sh_wdt_dev, nowayout);
+	watchdog_set_drvdata(&sh_wdt_dev, wdt);
+
+	spin_lock_init(&wdt->lock);
+
+	rc = sh_wdt_set_heartbeat(&sh_wdt_dev, heartbeat);
 	if (unlikely(rc)) {
-		dev_err(&pdev->dev,
-			"Can't register reboot notifier (err=%d)\n", rc);
-		goto out_unmap;
+		/* Default timeout if invalid */
+		sh_wdt_set_heartbeat(&sh_wdt_dev, WATCHDOG_HEARTBEAT);
+
+		dev_warn(&pdev->dev,
+			 "heartbeat value must be 1<=x<=3600, using %d\n",
+			 sh_wdt_dev.timeout);
 	}
 
-	sh_wdt_miscdev.parent = wdt->dev;
+	dev_info(&pdev->dev, "configured with heartbeat=%d sec (nowayout=%d)\n",
+		 sh_wdt_dev.timeout, nowayout);
 
-	rc = misc_register(&sh_wdt_miscdev);
+	rc = watchdog_register_device(&sh_wdt_dev);
 	if (unlikely(rc)) {
-		dev_err(&pdev->dev,
-			"Can't register miscdev on minor=%d (err=%d)\n",
-						sh_wdt_miscdev.minor, rc);
-		goto out_unreg;
+		dev_err(&pdev->dev, "Can't register watchdog (err=%d)\n", rc);
+		goto err;
 	}
 
 	init_timer(&wdt->timer);
@@ -388,20 +285,15 @@
 	wdt->timer.expires	= next_ping_period(clock_division_ratio);
 
 	platform_set_drvdata(pdev, wdt);
-	sh_wdt_dev = pdev;
 
 	dev_info(&pdev->dev, "initialized.\n");
 
+	pm_runtime_enable(&pdev->dev);
+
 	return 0;
 
-out_unreg:
-	unregister_reboot_notifier(&sh_wdt_notifier);
-out_unmap:
-	devm_iounmap(&pdev->dev, wdt->base);
-out_err:
-	devm_kfree(&pdev->dev, wdt);
-out_release:
-	devm_release_mem_region(&pdev->dev, res->start, resource_size(res));
+err:
+	clk_put(wdt->clk);
 
 	return rc;
 }
@@ -409,36 +301,35 @@
 static int __devexit sh_wdt_remove(struct platform_device *pdev)
 {
 	struct sh_wdt *wdt = platform_get_drvdata(pdev);
-	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 
 	platform_set_drvdata(pdev, NULL);
 
-	misc_deregister(&sh_wdt_miscdev);
+	watchdog_unregister_device(&sh_wdt_dev);
 
-	sh_wdt_dev = NULL;
-
-	unregister_reboot_notifier(&sh_wdt_notifier);
-	devm_release_mem_region(&pdev->dev, res->start, resource_size(res));
-	devm_iounmap(&pdev->dev, wdt->base);
-	devm_kfree(&pdev->dev, wdt);
+	pm_runtime_disable(&pdev->dev);
+	clk_put(wdt->clk);
 
 	return 0;
 }
 
+static void sh_wdt_shutdown(struct platform_device *pdev)
+{
+	sh_wdt_stop(&sh_wdt_dev);
+}
+
 static struct platform_driver sh_wdt_driver = {
 	.driver		= {
 		.name	= DRV_NAME,
 		.owner	= THIS_MODULE,
 	},
 
-	.probe	= sh_wdt_probe,
-	.remove	= __devexit_p(sh_wdt_remove),
+	.probe		= sh_wdt_probe,
+	.remove		= __devexit_p(sh_wdt_remove),
+	.shutdown	= sh_wdt_shutdown,
 };
 
 static int __init sh_wdt_init(void)
 {
-	int rc;
-
 	if (unlikely(clock_division_ratio < 0x5 ||
 		     clock_division_ratio > 0x7)) {
 		clock_division_ratio = WTCSR_CKS_4096;
@@ -447,17 +338,6 @@
 			clock_division_ratio);
 	}
 
-	rc = sh_wdt_set_heartbeat(heartbeat);
-	if (unlikely(rc)) {
-		heartbeat = WATCHDOG_HEARTBEAT;
-
-		pr_info("heartbeat value must be 1<=x<=3600, using %d\n",
-			heartbeat);
-	}
-
-	pr_info("configured with heartbeat=%d sec (nowayout=%d)\n",
-		heartbeat, nowayout);
-
 	return platform_driver_register(&sh_wdt_driver);
 }
 
diff --git a/drivers/watchdog/sp5100_tco.c b/drivers/watchdog/sp5100_tco.c
index 59108e4..ae5e82c 100644
--- a/drivers/watchdog/sp5100_tco.c
+++ b/drivers/watchdog/sp5100_tco.c
@@ -313,7 +313,7 @@
 	tcobase_phys = val;
 
 	tcobase = ioremap(val, SP5100_WDT_MEM_MAP_SIZE);
-	if (tcobase == 0) {
+	if (!tcobase) {
 		pr_err("failed to get tcobase address\n");
 		goto unreg_mem_region;
 	}
diff --git a/drivers/watchdog/via_wdt.c b/drivers/watchdog/via_wdt.c
index 465e082..5603e31 100644
--- a/drivers/watchdog/via_wdt.c
+++ b/drivers/watchdog/via_wdt.c
@@ -202,6 +202,9 @@
 		goto err_out_release;
 	}
 
+	if (timeout < 1 || timeout > WDT_TIMEOUT_MAX)
+		timeout = WDT_TIMEOUT;
+
 	wdt_dev.timeout = timeout;
 	watchdog_set_nowayout(&wdt_dev, nowayout);
 	if (readl(wdt_mem) & VIA_WDT_FIRED)
@@ -250,20 +253,7 @@
 	.remove		= __devexit_p(wdt_remove),
 };
 
-static int __init wdt_init(void)
-{
-	if (timeout < 1 || timeout > WDT_TIMEOUT_MAX)
-		timeout = WDT_TIMEOUT;
-	return pci_register_driver(&wdt_driver);
-}
-
-static void __exit wdt_exit(void)
-{
-	pci_unregister_driver(&wdt_driver);
-}
-
-module_init(wdt_init);
-module_exit(wdt_exit);
+module_pci_driver(wdt_driver);
 
 MODULE_AUTHOR("Marc Vertes");
 MODULE_DESCRIPTION("Driver for watchdog timer on VIA chipset");
diff --git a/drivers/watchdog/wdt_pci.c b/drivers/watchdog/wdt_pci.c
index 1c888c7..e32654e 100644
--- a/drivers/watchdog/wdt_pci.c
+++ b/drivers/watchdog/wdt_pci.c
@@ -739,39 +739,7 @@
 	.remove		= __devexit_p(wdtpci_remove_one),
 };
 
-
-/**
- *	wdtpci_cleanup:
- *
- *	Unload the watchdog. You cannot do this with any file handles open.
- *	If your watchdog is set to continue ticking on close and you unload
- *	it, well it keeps ticking. We won't get the interrupt but the board
- *	will not touch PC memory so all is fine. You just have to load a new
- *	module in xx seconds or reboot.
- */
-
-static void __exit wdtpci_cleanup(void)
-{
-	pci_unregister_driver(&wdtpci_driver);
-}
-
-
-/**
- *	wdtpci_init:
- *
- *	Set up the WDT watchdog board. All we have to do is grab the
- *	resources we require and bitch if anyone beat us to them.
- *	The open() function will actually kick the board off.
- */
-
-static int __init wdtpci_init(void)
-{
-	return pci_register_driver(&wdtpci_driver);
-}
-
-
-module_init(wdtpci_init);
-module_exit(wdtpci_cleanup);
+module_pci_driver(wdtpci_driver);
 
 MODULE_AUTHOR("JP Nollmann, Alan Cox");
 MODULE_DESCRIPTION("Driver for the ICS PCI-WDT500/501 watchdog cards");
diff --git a/drivers/watchdog/wm831x_wdt.c b/drivers/watchdog/wm831x_wdt.c
index b1815c5..87d66d2 100644
--- a/drivers/watchdog/wm831x_wdt.c
+++ b/drivers/watchdog/wm831x_wdt.c
@@ -247,8 +247,9 @@
 		reg |= pdata->software << WM831X_WDOG_RST_SRC_SHIFT;
 
 		if (pdata->update_gpio) {
-			ret = gpio_request(pdata->update_gpio,
-					   "Watchdog update");
+			ret = gpio_request_one(pdata->update_gpio,
+					       GPIOF_DIR_OUT | GPIOF_INIT_LOW,
+					       "Watchdog update");
 			if (ret < 0) {
 				dev_err(wm831x->dev,
 					"Failed to request update GPIO: %d\n",
@@ -256,14 +257,6 @@
 				goto err;
 			}
 
-			ret = gpio_direction_output(pdata->update_gpio, 0);
-			if (ret != 0) {
-				dev_err(wm831x->dev,
-					"gpio_direction_output returned: %d\n",
-					ret);
-				goto err_gpio;
-			}
-
 			driver_data->update_gpio = pdata->update_gpio;
 
 			/* Make sure the watchdog takes hardware updates */
diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index c0b3c70..079d1be 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -33,6 +33,7 @@
 #include <linux/bitops.h>
 #include <linux/mutex.h>
 #include <linux/anon_inodes.h>
+#include <linux/device.h>
 #include <asm/uaccess.h>
 #include <asm/io.h>
 #include <asm/mman.h>
@@ -87,7 +88,7 @@
  */
 
 /* Epoll private bits inside the event mask */
-#define EP_PRIVATE_BITS (EPOLLONESHOT | EPOLLET)
+#define EP_PRIVATE_BITS (EPOLLWAKEUP | EPOLLONESHOT | EPOLLET)
 
 /* Maximum number of nesting allowed inside epoll sets */
 #define EP_MAX_NESTS 4
@@ -154,6 +155,9 @@
 	/* List header used to link this item to the "struct file" items list */
 	struct list_head fllink;
 
+	/* wakeup_source used when EPOLLWAKEUP is set */
+	struct wakeup_source *ws;
+
 	/* The structure that describe the interested events and the source fd */
 	struct epoll_event event;
 };
@@ -194,6 +198,9 @@
 	 */
 	struct epitem *ovflist;
 
+	/* wakeup_source used when ep_scan_ready_list is running */
+	struct wakeup_source *ws;
+
 	/* The user that created the eventpoll descriptor */
 	struct user_struct *user;
 
@@ -588,8 +595,10 @@
 		 * queued into ->ovflist but the "txlist" might already
 		 * contain them, and the list_splice() below takes care of them.
 		 */
-		if (!ep_is_linked(&epi->rdllink))
+		if (!ep_is_linked(&epi->rdllink)) {
 			list_add_tail(&epi->rdllink, &ep->rdllist);
+			__pm_stay_awake(epi->ws);
+		}
 	}
 	/*
 	 * We need to set back ep->ovflist to EP_UNACTIVE_PTR, so that after
@@ -602,6 +611,7 @@
 	 * Quickly re-inject items left on "txlist".
 	 */
 	list_splice(&txlist, &ep->rdllist);
+	__pm_relax(ep->ws);
 
 	if (!list_empty(&ep->rdllist)) {
 		/*
@@ -656,6 +666,8 @@
 		list_del_init(&epi->rdllink);
 	spin_unlock_irqrestore(&ep->lock, flags);
 
+	wakeup_source_unregister(epi->ws);
+
 	/* At this point it is safe to free the eventpoll item */
 	kmem_cache_free(epi_cache, epi);
 
@@ -706,6 +718,7 @@
 	mutex_unlock(&epmutex);
 	mutex_destroy(&ep->mtx);
 	free_uid(ep->user);
+	wakeup_source_unregister(ep->ws);
 	kfree(ep);
 }
 
@@ -737,6 +750,7 @@
 			 * callback, but it's not actually ready, as far as
 			 * caller requested events goes. We can remove it here.
 			 */
+			__pm_relax(epi->ws);
 			list_del_init(&epi->rdllink);
 		}
 	}
@@ -927,13 +941,23 @@
 		if (epi->next == EP_UNACTIVE_PTR) {
 			epi->next = ep->ovflist;
 			ep->ovflist = epi;
+			if (epi->ws) {
+				/*
+				 * Activate ep->ws since epi->ws may get
+				 * deactivated at any time.
+				 */
+				__pm_stay_awake(ep->ws);
+			}
+
 		}
 		goto out_unlock;
 	}
 
 	/* If this file is already in the ready list we exit soon */
-	if (!ep_is_linked(&epi->rdllink))
+	if (!ep_is_linked(&epi->rdllink)) {
 		list_add_tail(&epi->rdllink, &ep->rdllist);
+		__pm_stay_awake(epi->ws);
+	}
 
 	/*
 	 * Wake up ( if active ) both the eventpoll wait list and the ->poll()
@@ -1091,6 +1115,30 @@
 	return error;
 }
 
+static int ep_create_wakeup_source(struct epitem *epi)
+{
+	const char *name;
+
+	if (!epi->ep->ws) {
+		epi->ep->ws = wakeup_source_register("eventpoll");
+		if (!epi->ep->ws)
+			return -ENOMEM;
+	}
+
+	name = epi->ffd.file->f_path.dentry->d_name.name;
+	epi->ws = wakeup_source_register(name);
+	if (!epi->ws)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void ep_destroy_wakeup_source(struct epitem *epi)
+{
+	wakeup_source_unregister(epi->ws);
+	epi->ws = NULL;
+}
+
 /*
  * Must be called with "mtx" held.
  */
@@ -1118,6 +1166,13 @@
 	epi->event = *event;
 	epi->nwait = 0;
 	epi->next = EP_UNACTIVE_PTR;
+	if (epi->event.events & EPOLLWAKEUP) {
+		error = ep_create_wakeup_source(epi);
+		if (error)
+			goto error_create_wakeup_source;
+	} else {
+		epi->ws = NULL;
+	}
 
 	/* Initialize the poll table using the queue callback */
 	epq.epi = epi;
@@ -1164,6 +1219,7 @@
 	/* If the file is already "ready" we drop it inside the ready list */
 	if ((revents & event->events) && !ep_is_linked(&epi->rdllink)) {
 		list_add_tail(&epi->rdllink, &ep->rdllist);
+		__pm_stay_awake(epi->ws);
 
 		/* Notify waiting tasks that events are available */
 		if (waitqueue_active(&ep->wq))
@@ -1204,6 +1260,9 @@
 		list_del_init(&epi->rdllink);
 	spin_unlock_irqrestore(&ep->lock, flags);
 
+	wakeup_source_unregister(epi->ws);
+
+error_create_wakeup_source:
 	kmem_cache_free(epi_cache, epi);
 
 	return error;
@@ -1229,6 +1288,12 @@
 	epi->event.events = event->events;
 	pt._key = event->events;
 	epi->event.data = event->data; /* protected by mtx */
+	if (epi->event.events & EPOLLWAKEUP) {
+		if (!epi->ws)
+			ep_create_wakeup_source(epi);
+	} else if (epi->ws) {
+		ep_destroy_wakeup_source(epi);
+	}
 
 	/*
 	 * Get current event bits. We can safely use the file* here because
@@ -1244,6 +1309,7 @@
 		spin_lock_irq(&ep->lock);
 		if (!ep_is_linked(&epi->rdllink)) {
 			list_add_tail(&epi->rdllink, &ep->rdllist);
+			__pm_stay_awake(epi->ws);
 
 			/* Notify waiting tasks that events are available */
 			if (waitqueue_active(&ep->wq))
@@ -1282,6 +1348,18 @@
 	     !list_empty(head) && eventcnt < esed->maxevents;) {
 		epi = list_first_entry(head, struct epitem, rdllink);
 
+		/*
+		 * Activate ep->ws before deactivating epi->ws to prevent
+		 * triggering auto-suspend here (in case we reactive epi->ws
+		 * below).
+		 *
+		 * This could be rearranged to delay the deactivation of epi->ws
+		 * instead, but then epi->ws would temporarily be out of sync
+		 * with ep_is_linked().
+		 */
+		if (epi->ws && epi->ws->active)
+			__pm_stay_awake(ep->ws);
+		__pm_relax(epi->ws);
 		list_del_init(&epi->rdllink);
 
 		pt._key = epi->event.events;
@@ -1298,6 +1376,7 @@
 			if (__put_user(revents, &uevent->events) ||
 			    __put_user(epi->event.data, &uevent->data)) {
 				list_add(&epi->rdllink, head);
+				__pm_stay_awake(epi->ws);
 				return eventcnt ? eventcnt : -EFAULT;
 			}
 			eventcnt++;
@@ -1317,6 +1396,7 @@
 				 * poll callback will queue them in ep->ovflist.
 				 */
 				list_add_tail(&epi->rdllink, &ep->rdllist);
+				__pm_stay_awake(epi->ws);
 			}
 		}
 	}
@@ -1629,6 +1709,10 @@
 	if (!tfile->f_op || !tfile->f_op->poll)
 		goto error_tgt_fput;
 
+	/* Check if EPOLLWAKEUP is allowed */
+	if ((epds.events & EPOLLWAKEUP) && !capable(CAP_EPOLLWAKEUP))
+		epds.events &= ~EPOLLWAKEUP;
+
 	/*
 	 * We have to check that the file structure underneath the file descriptor
 	 * the user passed to us _is_ an eventpoll file. And also we do not permit
diff --git a/fs/exec.c b/fs/exec.c
index d038968..1e8efdc 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1938,8 +1938,21 @@
 		core_waiters = zap_threads(tsk, mm, core_state, exit_code);
 	up_write(&mm->mmap_sem);
 
-	if (core_waiters > 0)
+	if (core_waiters > 0) {
+		struct core_thread *ptr;
+
 		wait_for_completion(&core_state->startup);
+		/*
+		 * Wait for all the threads to become inactive, so that
+		 * all the thread context (extended register state, like
+		 * fpu etc) gets copied to the memory.
+		 */
+		ptr = core_state->dumper.next;
+		while (ptr != NULL) {
+			wait_task_inactive(ptr->task, 0);
+			ptr = ptr->next;
+		}
+	}
 
 	return core_waiters;
 }
diff --git a/include/linux/capability.h b/include/linux/capability.h
index 12d52de..c398cff 100644
--- a/include/linux/capability.h
+++ b/include/linux/capability.h
@@ -360,8 +360,11 @@
 
 #define CAP_WAKE_ALARM            35
 
+/* Allow preventing system suspends while epoll events are pending */
 
-#define CAP_LAST_CAP         CAP_WAKE_ALARM
+#define CAP_EPOLLWAKEUP      36
+
+#define CAP_LAST_CAP         CAP_EPOLLWAKEUP
 
 #define cap_valid(x) ((x) >= 0 && (x) <= CAP_LAST_CAP)
 
diff --git a/include/linux/cs5535.h b/include/linux/cs5535.h
index c077aec..cfe8323 100644
--- a/include/linux/cs5535.h
+++ b/include/linux/cs5535.h
@@ -95,6 +95,7 @@
 
 /* CS5536_PM1_STS bits */
 #define CS5536_WAK_FLAG		(1 << 15)
+#define CS5536_RTC_FLAG		(1 << 10)
 #define CS5536_PWRBTN_FLAG	(1 << 8)
 
 /* CS5536_PM1_EN bits */
diff --git a/include/linux/eventpoll.h b/include/linux/eventpoll.h
index 657ab55..6f8be32 100644
--- a/include/linux/eventpoll.h
+++ b/include/linux/eventpoll.h
@@ -26,6 +26,18 @@
 #define EPOLL_CTL_DEL 2
 #define EPOLL_CTL_MOD 3
 
+/*
+ * Request the handling of system wakeup events so as to prevent system suspends
+ * from happening while those events are being processed.
+ *
+ * Assuming neither EPOLLET nor EPOLLONESHOT is set, system suspends will not be
+ * re-allowed until epoll_wait is called again after consuming the wakeup
+ * event(s).
+ *
+ * Requires CAP_EPOLLWAKEUP
+ */
+#define EPOLLWAKEUP (1 << 29)
+
 /* Set the One Shot behaviour for the target file descriptor */
 #define EPOLLONESHOT (1 << 30)
 
diff --git a/include/linux/firewire.h b/include/linux/firewire.h
index cdc9b71..e83c24a 100644
--- a/include/linux/firewire.h
+++ b/include/linux/firewire.h
@@ -135,6 +135,20 @@
 	__be32 maint_utility_register;
 };
 
+static inline struct fw_card *fw_card_get(struct fw_card *card)
+{
+	kref_get(&card->kref);
+
+	return card;
+}
+
+void fw_card_release(struct kref *kref);
+
+static inline void fw_card_put(struct fw_card *card)
+{
+	kref_put(&card->kref, fw_card_release);
+}
+
 struct fw_attribute_group {
 	struct attribute_group *groups[2];
 	struct attribute_group group;
@@ -325,6 +339,7 @@
 void fw_core_remove_address_handler(struct fw_address_handler *handler);
 void fw_send_response(struct fw_card *card,
 		      struct fw_request *request, int rcode);
+int fw_get_request_speed(struct fw_request *request);
 void fw_send_request(struct fw_card *card, struct fw_transaction *t,
 		     int tcode, int destination_id, int generation, int speed,
 		     unsigned long long offset, void *payload, size_t length,
diff --git a/include/linux/i2o-dev.h b/include/linux/i2o-dev.h
index a0b23dd..a8093bf 100644
--- a/include/linux/i2o-dev.h
+++ b/include/linux/i2o-dev.h
@@ -124,7 +124,7 @@
 #define I2O_BUS_LOCAL	0
 #define I2O_BUS_ISA	1
 #define I2O_BUS_EISA	2
-#define I2O_BUS_MCA	3
+/* was  I2O_BUS_MCA	3 */
 #define I2O_BUS_PCI	4
 #define I2O_BUS_PCMCIA	5
 #define I2O_BUS_NUBUS	6
diff --git a/include/linux/irq.h b/include/linux/irq.h
index b27cfcf..61f5cec 100644
--- a/include/linux/irq.h
+++ b/include/linux/irq.h
@@ -335,11 +335,6 @@
 	void		(*irq_print_chip)(struct irq_data *data, struct seq_file *p);
 
 	unsigned long	flags;
-
-	/* Currently used only by UML, might disappear one day.*/
-#ifdef CONFIG_IRQ_RELEASE_METHOD
-	void		(*release)(unsigned int irq, void *dev_id);
-#endif
 };
 
 /*
diff --git a/include/linux/libata.h b/include/linux/libata.h
index e926df7..6e887c7 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -247,6 +247,7 @@
 	ATA_HOST_SIMPLEX	= (1 << 0),	/* Host is simplex, one DMA channel per host only */
 	ATA_HOST_STARTED	= (1 << 1),	/* Host started */
 	ATA_HOST_PARALLEL_SCAN	= (1 << 2),	/* Ports on this host can be scanned in parallel */
+	ATA_HOST_IGNORE_ATA	= (1 << 3),	/* Ignore ATA devices on this host. */
 
 	/* bits 24:31 of host->flags are reserved for LLD specific flags */
 
diff --git a/include/linux/mca-legacy.h b/include/linux/mca-legacy.h
deleted file mode 100644
index 7a3aea8..0000000
--- a/include/linux/mca-legacy.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/* -*- mode: c; c-basic-offset: 8 -*- */
-
-/* This is the function prototypes for the old legacy MCA interface
- *
- * Please move your driver to the new sysfs based one instead */
-
-#ifndef _LINUX_MCA_LEGACY_H
-#define _LINUX_MCA_LEGACY_H
-
-#include <linux/mca.h>
-
-#warning "MCA legacy - please move your driver to the new sysfs api"
-
-/* MCA_NOTFOUND is an error condition.  The other two indicate
- * motherboard POS registers contain the adapter.  They might be
- * returned by the mca_find_adapter() function, and can be used as
- * arguments to mca_read_stored_pos().  I'm not going to allow direct
- * access to the motherboard registers until we run across an adapter
- * that requires it.  We don't know enough about them to know if it's
- * safe.
- *
- * See Documentation/mca.txt or one of the existing drivers for
- * more information.
- */
-#define MCA_NOTFOUND	(-1)
-
-
-
-/* Returns the slot of the first enabled adapter matching id.  User can
- * specify a starting slot beyond zero, to deal with detecting multiple
- * devices.  Returns MCA_NOTFOUND if id not found.  Also checks the
- * integrated adapters.
- */
-extern int mca_find_adapter(int id, int start);
-extern int mca_find_unused_adapter(int id, int start);
-
-extern int mca_mark_as_used(int slot);
-extern void mca_mark_as_unused(int slot);
-
-/* gets a byte out of POS register (stored in memory) */
-extern unsigned char mca_read_stored_pos(int slot, int reg);
-
-/* This can be expanded later.  Right now, it gives us a way of
- * getting meaningful information into the MCA_info structure,
- * so we can have a more interesting /proc/mca.
- */
-extern void mca_set_adapter_name(int slot, char* name);
-
-/* These routines actually mess with the hardware POS registers.  They
- * temporarily disable the device (and interrupts), so make sure you know
- * what you're doing if you use them.  Furthermore, writing to a POS may
- * result in two devices trying to share a resource, which in turn can
- * result in multiple devices sharing memory spaces, IRQs, or even trashing
- * hardware.  YOU HAVE BEEN WARNED.
- *
- * You can only access slots with this.  Motherboard registers are off
- * limits.
- */
-
-/* read a byte from the specified POS register. */
-extern unsigned char mca_read_pos(int slot, int reg);
-
-/* write a byte to the specified POS register. */
-extern void mca_write_pos(int slot, int reg, unsigned char byte);
-
-#endif
diff --git a/include/linux/mca.h b/include/linux/mca.h
deleted file mode 100644
index 3797270..0000000
--- a/include/linux/mca.h
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Header for Microchannel Architecture Bus
- * Written by Martin Kolinek, February 1996
- */
-
-#ifndef _LINUX_MCA_H
-#define _LINUX_MCA_H
-
-#include <linux/device.h>
-
-#ifdef CONFIG_MCA
-#include <asm/mca.h>
-
-extern int MCA_bus;
-#else
-#define MCA_bus 0
-#endif
-
-/* This sets up an information callback for /proc/mca/slot?.  The
- * function is called with the buffer, slot, and device pointer (or
- * some equally informative context information, or nothing, if you
- * prefer), and is expected to put useful information into the
- * buffer.  The adapter name, id, and POS registers get printed
- * before this is called though, so don't do it again.
- *
- * This should be called with a NULL procfn when a module
- * unregisters, thus preventing kernel crashes and other such
- * nastiness.
- */
-typedef int (*MCA_ProcFn)(char* buf, int slot, void* dev);
-
-/* Should only be called by the NMI interrupt handler, this will do some
- * fancy stuff to figure out what might have generated a NMI.
- */
-extern void mca_handle_nmi(void);
-
-enum MCA_AdapterStatus {
-	MCA_ADAPTER_NORMAL = 0,
-	MCA_ADAPTER_NONE = 1,
-	MCA_ADAPTER_DISABLED = 2,
-	MCA_ADAPTER_ERROR = 3
-};
-
-struct mca_device {
-	u64			dma_mask;
-	int			pos_id;
-	int			slot;
-
-	/* index into id_table, set by the bus match routine */
-	int			index;
-
-	/* is there a driver installed? 0 - No, 1 - Yes */
-	int			driver_loaded;
-	/* POS registers */
-	unsigned char		pos[8];
-	/* if a pseudo adapter of the motherboard, this is the motherboard
-	 * register value to use for setup cycles */
-	short			pos_register;
-	
-	enum MCA_AdapterStatus	status;
-#ifdef CONFIG_MCA_PROC_FS
-	/* name of the proc/mca file */
-	char			procname[8];
-	/* /proc info callback */
-	MCA_ProcFn		procfn;
-	/* device/context info for proc callback */
-	void			*proc_dev;
-#endif
-	struct device		dev;
-	char			name[32];
-};
-#define to_mca_device(mdev) container_of(mdev, struct mca_device, dev)
-
-struct mca_bus_accessor_functions {
-	unsigned char	(*mca_read_pos)(struct mca_device *, int reg);
-	void		(*mca_write_pos)(struct mca_device *, int reg,
-					 unsigned char byte);
-	int		(*mca_transform_irq)(struct mca_device *, int irq);
-	int		(*mca_transform_ioport)(struct mca_device *,
-						  int region);
-	void *		(*mca_transform_memory)(struct mca_device *,
-						void *memory);
-};
-
-struct mca_bus {
-	u64			default_dma_mask;
-	int			number;
-	struct mca_bus_accessor_functions f;
-	struct device		dev;
-	char			name[32];
-};
-#define to_mca_bus(mdev) container_of(mdev, struct mca_bus, dev)
-
-struct mca_driver {
-	const short		*id_table;
-	void			*driver_data;
-	int			integrated_id;
-	struct device_driver	driver;
-};
-#define to_mca_driver(mdriver) container_of(mdriver, struct mca_driver, driver)
-
-/* Ongoing supported API functions */
-extern struct mca_device *mca_find_device_by_slot(int slot);
-extern int mca_system_init(void);
-extern struct mca_bus *mca_attach_bus(int);
-
-extern unsigned char mca_device_read_stored_pos(struct mca_device *mca_dev,
-						int reg);
-extern unsigned char mca_device_read_pos(struct mca_device *mca_dev, int reg);
-extern void mca_device_write_pos(struct mca_device *mca_dev, int reg,
-				 unsigned char byte);
-extern int mca_device_transform_irq(struct mca_device *mca_dev, int irq);
-extern int mca_device_transform_ioport(struct mca_device *mca_dev, int port);
-extern void *mca_device_transform_memory(struct mca_device *mca_dev,
-					 void *mem);
-extern int mca_device_claimed(struct mca_device *mca_dev);
-extern void mca_device_set_claim(struct mca_device *mca_dev, int val);
-extern void mca_device_set_name(struct mca_device *mca_dev, const char *name);
-static inline char *mca_device_get_name(struct mca_device *mca_dev)
-{
-	return mca_dev ? mca_dev->name : NULL;
-}
-
-extern enum MCA_AdapterStatus mca_device_status(struct mca_device *mca_dev);
-
-extern struct bus_type mca_bus_type;
-
-extern int mca_register_driver(struct mca_driver *drv);
-extern int mca_register_driver_integrated(struct mca_driver *, int);
-extern void mca_unregister_driver(struct mca_driver *drv);
-
-/* WARNING: only called by the boot time device setup */
-extern int mca_register_device(int bus, struct mca_device *mca_dev);
-
-#ifdef CONFIG_MCA_PROC_FS
-extern void mca_do_proc_init(void);
-extern void mca_set_adapter_procfn(int slot, MCA_ProcFn, void* dev);
-#else
-static inline void mca_do_proc_init(void)
-{
-}
-
-static inline void mca_set_adapter_procfn(int slot, MCA_ProcFn fn, void* dev)
-{
-}
-#endif
-
-#endif /* _LINUX_MCA_H */
diff --git a/include/linux/mfd/mc13xxx.h b/include/linux/mfd/mc13xxx.h
index 10e038b..bf07075 100644
--- a/include/linux/mfd/mc13xxx.h
+++ b/include/linux/mfd/mc13xxx.h
@@ -170,6 +170,16 @@
 	bool atox;
 };
 
+enum mc13783_ssi_port {
+	MC13783_SSI1_PORT,
+	MC13783_SSI2_PORT,
+};
+
+struct mc13xxx_codec_platform_data {
+	enum mc13783_ssi_port adc_ssi_port;
+	enum mc13783_ssi_port dac_ssi_port;
+};
+
 struct mc13xxx_platform_data {
 #define MC13XXX_USE_TOUCHSCREEN (1 << 0)
 #define MC13XXX_USE_CODEC	(1 << 1)
@@ -181,6 +191,7 @@
 	struct mc13xxx_leds_platform_data *leds;
 	struct mc13xxx_buttons_platform_data *buttons;
 	struct mc13xxx_ts_platform_data touch;
+	struct mc13xxx_codec_platform_data *codec;
 };
 
 #define MC13XXX_ADC_MODE_TS		1
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
index 8adf70e..f325786 100644
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -1084,8 +1084,10 @@
 
 extern int perf_num_counters(void);
 extern const char *perf_pmu_name(void);
-extern void __perf_event_task_sched(struct task_struct *prev,
-				    struct task_struct *next);
+extern void __perf_event_task_sched_in(struct task_struct *prev,
+				       struct task_struct *task);
+extern void __perf_event_task_sched_out(struct task_struct *prev,
+					struct task_struct *next);
 extern int perf_event_init_task(struct task_struct *child);
 extern void perf_event_exit_task(struct task_struct *child);
 extern void perf_event_free_task(struct task_struct *task);
@@ -1205,13 +1207,20 @@
 
 extern struct static_key_deferred perf_sched_events;
 
-static inline void perf_event_task_sched(struct task_struct *prev,
+static inline void perf_event_task_sched_in(struct task_struct *prev,
 					    struct task_struct *task)
 {
+	if (static_key_false(&perf_sched_events.key))
+		__perf_event_task_sched_in(prev, task);
+}
+
+static inline void perf_event_task_sched_out(struct task_struct *prev,
+					     struct task_struct *next)
+{
 	perf_sw_event(PERF_COUNT_SW_CONTEXT_SWITCHES, 1, NULL, 0);
 
 	if (static_key_false(&perf_sched_events.key))
-		__perf_event_task_sched(prev, task);
+		__perf_event_task_sched_out(prev, next);
 }
 
 extern void perf_event_mmap(struct vm_area_struct *vma);
@@ -1286,8 +1295,11 @@
 extern void perf_event_task_tick(void);
 #else
 static inline void
-perf_event_task_sched(struct task_struct *prev,
-		      struct task_struct *task)				{ }
+perf_event_task_sched_in(struct task_struct *prev,
+			 struct task_struct *task)			{ }
+static inline void
+perf_event_task_sched_out(struct task_struct *prev,
+			  struct task_struct *next)			{ }
 static inline int perf_event_init_task(struct task_struct *child)	{ return 0; }
 static inline void perf_event_exit_task(struct task_struct *child)	{ }
 static inline void perf_event_free_task(struct task_struct *task)	{ }
diff --git a/include/linux/platform_data/ina2xx.h b/include/linux/platform_data/ina2xx.h
new file mode 100644
index 0000000..9abc0ca
--- /dev/null
+++ b/include/linux/platform_data/ina2xx.h
@@ -0,0 +1,19 @@
+/*
+ * Driver for Texas Instruments INA219, INA226 power monitor chips
+ *
+ * Copyright (C) 2012 Lothar Felten <l-felten@ti.com>
+ *
+ * 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.
+ *
+ * For further information, see the Documentation/hwmon/ina2xx file.
+ */
+
+/**
+ * struct ina2xx_platform_data - ina2xx info
+ * @shunt_uohms		shunt resistance in microohms
+ */
+struct ina2xx_platform_data {
+	long shunt_uohms;
+};
diff --git a/include/linux/pm.h b/include/linux/pm.h
index 715305e..f067e60 100644
--- a/include/linux/pm.h
+++ b/include/linux/pm.h
@@ -544,8 +544,6 @@
 	unsigned long		active_jiffies;
 	unsigned long		suspended_jiffies;
 	unsigned long		accounting_timestamp;
-	ktime_t			suspend_time;
-	s64			max_time_suspended_ns;
 	struct dev_pm_qos_request *pq_req;
 #endif
 	struct pm_subsys_data	*subsys_data;  /* Owned by the subsystem. */
diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
index 91f8286..30f794e 100644
--- a/include/linux/pm_domain.h
+++ b/include/linux/pm_domain.h
@@ -14,6 +14,7 @@
 #include <linux/pm.h>
 #include <linux/err.h>
 #include <linux/of.h>
+#include <linux/notifier.h>
 
 enum gpd_status {
 	GPD_STATE_ACTIVE = 0,	/* PM domain is active */
@@ -70,9 +71,9 @@
 	int (*power_on)(struct generic_pm_domain *domain);
 	s64 power_on_latency_ns;
 	struct gpd_dev_ops dev_ops;
-	s64 break_even_ns;	/* Power break even for the entire domain. */
 	s64 max_off_time_ns;	/* Maximum allowed "suspended" time. */
-	ktime_t power_off_time;
+	bool max_off_time_changed;
+	bool cached_power_down_ok;
 	struct device_node *of_node; /* Node in device tree */
 };
 
@@ -93,13 +94,17 @@
 	s64 start_latency_ns;
 	s64 save_state_latency_ns;
 	s64 restore_state_latency_ns;
-	s64 break_even_ns;
+	s64 effective_constraint_ns;
+	bool constraint_changed;
+	bool cached_stop_ok;
 };
 
 struct generic_pm_domain_data {
 	struct pm_domain_data base;
 	struct gpd_dev_ops ops;
 	struct gpd_timing_data td;
+	struct notifier_block nb;
+	struct mutex lock;
 	bool need_restore;
 	bool always_on;
 };
@@ -141,6 +146,7 @@
 extern int pm_genpd_remove_device(struct generic_pm_domain *genpd,
 				  struct device *dev);
 extern void pm_genpd_dev_always_on(struct device *dev, bool val);
+extern void pm_genpd_dev_need_restore(struct device *dev, bool val);
 extern int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
 				  struct generic_pm_domain *new_subdomain);
 extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
@@ -184,6 +190,7 @@
 	return -ENOSYS;
 }
 static inline void pm_genpd_dev_always_on(struct device *dev, bool val) {}
+static inline void pm_genpd_dev_need_restore(struct device *dev, bool val) {}
 static inline int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
 					 struct generic_pm_domain *new_sd)
 {
diff --git a/include/linux/pm_runtime.h b/include/linux/pm_runtime.h
index 609daae..f271860 100644
--- a/include/linux/pm_runtime.h
+++ b/include/linux/pm_runtime.h
@@ -150,9 +150,6 @@
 static inline unsigned long pm_runtime_autosuspend_expiration(
 				struct device *dev) { return 0; }
 
-static inline void pm_runtime_update_max_time_suspended(struct device *dev,
-							s64 delta_ns) {}
-
 #endif /* !CONFIG_PM_RUNTIME */
 
 static inline int pm_runtime_idle(struct device *dev)
diff --git a/include/linux/pm_wakeup.h b/include/linux/pm_wakeup.h
index d9f0511..569781f 100644
--- a/include/linux/pm_wakeup.h
+++ b/include/linux/pm_wakeup.h
@@ -33,12 +33,15 @@
  *
  * @total_time: Total time this wakeup source has been active.
  * @max_time: Maximum time this wakeup source has been continuously active.
- * @last_time: Monotonic clock when the wakeup source's was activated last time.
+ * @last_time: Monotonic clock when the wakeup source's was touched last time.
+ * @prevent_sleep_time: Total time this source has been preventing autosleep.
  * @event_count: Number of signaled wakeup events.
  * @active_count: Number of times the wakeup sorce was activated.
  * @relax_count: Number of times the wakeup sorce was deactivated.
- * @hit_count: Number of times the wakeup sorce might abort system suspend.
+ * @expire_count: Number of times the wakeup source's timeout has expired.
+ * @wakeup_count: Number of times the wakeup source might abort suspend.
  * @active: Status of the wakeup source.
+ * @has_timeout: The wakeup source has been activated with a timeout.
  */
 struct wakeup_source {
 	const char 		*name;
@@ -49,11 +52,15 @@
 	ktime_t total_time;
 	ktime_t max_time;
 	ktime_t last_time;
+	ktime_t start_prevent_time;
+	ktime_t prevent_sleep_time;
 	unsigned long		event_count;
 	unsigned long		active_count;
 	unsigned long		relax_count;
-	unsigned long		hit_count;
-	unsigned int		active:1;
+	unsigned long		expire_count;
+	unsigned long		wakeup_count;
+	bool			active:1;
+	bool			autosleep_enabled:1;
 };
 
 #ifdef CONFIG_PM_SLEEP
diff --git a/include/linux/raid/md_p.h b/include/linux/raid/md_p.h
index 8c0a3ad..ee75353 100644
--- a/include/linux/raid/md_p.h
+++ b/include/linux/raid/md_p.h
@@ -233,7 +233,10 @@
 	__le32	delta_disks;	/* change in number of raid_disks		*/
 	__le32	new_layout;	/* new layout					*/
 	__le32	new_chunk;	/* new chunk size (512byte sectors)		*/
-	__u8	pad1[128-124];	/* set to 0 when written */
+	__le32  new_offset;	/* signed number to add to data_offset in new
+				 * layout.  0 == no-change.  This can be
+				 * different on each device in the array.
+				 */
 
 	/* constant this-device information - 64 bytes */
 	__le64	data_offset;	/* sector start of data, often 0 */
@@ -281,10 +284,18 @@
 					    * active device with same 'role'.
 					    * 'recovery_offset' is also set.
 					    */
+#define	MD_FEATURE_RESHAPE_BACKWARDS	32 /* Reshape doesn't change number
+					    * of devices, but is going
+					    * backwards anyway.
+					    */
+#define	MD_FEATURE_NEW_OFFSET		64 /* new_offset must be honoured */
 #define	MD_FEATURE_ALL			(MD_FEATURE_BITMAP_OFFSET	\
 					|MD_FEATURE_RECOVERY_OFFSET	\
 					|MD_FEATURE_RESHAPE_ACTIVE	\
 					|MD_FEATURE_BAD_BLOCKS		\
-					|MD_FEATURE_REPLACEMENT)
+					|MD_FEATURE_REPLACEMENT		\
+					|MD_FEATURE_RESHAPE_BACKWARDS	\
+					|MD_FEATURE_NEW_OFFSET		\
+					)
 
 #endif 
diff --git a/include/linux/raid/pq.h b/include/linux/raid/pq.h
index 53272e9..640c69c 100644
--- a/include/linux/raid/pq.h
+++ b/include/linux/raid/pq.h
@@ -99,8 +99,20 @@
 extern const struct raid6_calls raid6_altivec4;
 extern const struct raid6_calls raid6_altivec8;
 
+struct raid6_recov_calls {
+	void (*data2)(int, size_t, int, int, void **);
+	void (*datap)(int, size_t, int, void **);
+	int  (*valid)(void);
+	const char *name;
+	int priority;
+};
+
+extern const struct raid6_recov_calls raid6_recov_intx1;
+extern const struct raid6_recov_calls raid6_recov_ssse3;
+
 /* Algorithm list */
 extern const struct raid6_calls * const raid6_algos[];
+extern const struct raid6_recov_calls *const raid6_recov_algos[];
 int raid6_select_algo(void);
 
 /* Return values from chk_syndrome */
@@ -111,14 +123,16 @@
 
 /* Galois field tables */
 extern const u8 raid6_gfmul[256][256] __attribute__((aligned(256)));
+extern const u8 raid6_vgfmul[256][32] __attribute__((aligned(256)));
 extern const u8 raid6_gfexp[256]      __attribute__((aligned(256)));
 extern const u8 raid6_gfinv[256]      __attribute__((aligned(256)));
 extern const u8 raid6_gfexi[256]      __attribute__((aligned(256)));
 
 /* Recovery routines */
-void raid6_2data_recov(int disks, size_t bytes, int faila, int failb,
+extern void (*raid6_2data_recov)(int disks, size_t bytes, int faila, int failb,
 		       void **ptrs);
-void raid6_datap_recov(int disks, size_t bytes, int faila, void **ptrs);
+extern void (*raid6_datap_recov)(int disks, size_t bytes, int faila,
+			void **ptrs);
 void raid6_dual_recov(int disks, size_t bytes, int faila, int failb,
 		      void **ptrs);
 
diff --git a/include/linux/serial.h b/include/linux/serial.h
index 441980e..90e9f98 100644
--- a/include/linux/serial.h
+++ b/include/linux/serial.h
@@ -130,7 +130,6 @@
 #define ASYNCB_CHECK_CD		25 /* i.e., CLOCAL */
 #define ASYNCB_SHARE_IRQ	24 /* for multifunction cards, no longer used */
 #define ASYNCB_CONS_FLOW	23 /* flow control for console  */
-#define ASYNCB_BOOT_ONLYMCA	22 /* Probe only if MCA bus */
 #define ASYNCB_FIRST_KERNEL	22
 
 #define ASYNC_HUP_NOTIFY	(1U << ASYNCB_HUP_NOTIFY)
@@ -166,7 +165,6 @@
 #define ASYNC_CHECK_CD		(1U << ASYNCB_CHECK_CD)
 #define ASYNC_SHARE_IRQ		(1U << ASYNCB_SHARE_IRQ)
 #define ASYNC_CONS_FLOW		(1U << ASYNCB_CONS_FLOW)
-#define ASYNC_BOOT_ONLYMCA	(1U << ASYNCB_BOOT_ONLYMCA)
 #define ASYNC_INTERNAL_FLAGS	(~((1U << ASYNCB_FIRST_KERNEL) - 1))
 
 /*
diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h
index 10dbce5..a416e92 100644
--- a/include/linux/serial_8250.h
+++ b/include/linux/serial_8250.h
@@ -55,7 +55,6 @@
 	PLAT8250_DEV_BOCA,
 	PLAT8250_DEV_EXAR_ST16C554,
 	PLAT8250_DEV_HUB6,
-	PLAT8250_DEV_MCA,
 	PLAT8250_DEV_AU1X00,
 	PLAT8250_DEV_SM501,
 };
diff --git a/include/linux/serial_sci.h b/include/linux/serial_sci.h
index 7877907..eb763ad 100644
--- a/include/linux/serial_sci.h
+++ b/include/linux/serial_sci.h
@@ -52,6 +52,8 @@
 /* SCSPTR, optional */
 #define SCSPTR_RTSIO	(1 << 7)
 #define SCSPTR_CTSIO	(1 << 5)
+#define SCSPTR_SPB2IO	(1 << 1)
+#define SCSPTR_SPB2DT	(1 << 0)
 
 /* Offsets into the sci_port->irqs array */
 enum {
diff --git a/include/linux/sh_clk.h b/include/linux/sh_clk.h
index 0a9d8f2..c513b73 100644
--- a/include/linux/sh_clk.h
+++ b/include/linux/sh_clk.h
@@ -59,7 +59,15 @@
 	unsigned int		nr_freqs;
 };
 
-#define CLK_ENABLE_ON_INIT	(1 << 0)
+#define CLK_ENABLE_ON_INIT	BIT(0)
+
+#define CLK_ENABLE_REG_32BIT	BIT(1)	/* default access size */
+#define CLK_ENABLE_REG_16BIT	BIT(2)
+#define CLK_ENABLE_REG_8BIT	BIT(3)
+
+#define CLK_ENABLE_REG_MASK	(CLK_ENABLE_REG_32BIT | \
+				 CLK_ENABLE_REG_16BIT | \
+				 CLK_ENABLE_REG_8BIT)
 
 /* drivers/sh/clk.c */
 unsigned long followparent_recalc(struct clk *);
@@ -102,7 +110,7 @@
 		      unsigned long *best_freq, unsigned long *parent_freq,
 		      unsigned int div_min, unsigned int div_max);
 
-#define SH_CLK_MSTP32(_parent, _enable_reg, _enable_bit, _flags)	\
+#define SH_CLK_MSTP(_parent, _enable_reg, _enable_bit, _flags)		\
 {									\
 	.parent		= _parent,					\
 	.enable_reg	= (void __iomem *)_enable_reg,			\
@@ -110,7 +118,27 @@
 	.flags		= _flags,					\
 }
 
-int sh_clk_mstp32_register(struct clk *clks, int nr);
+#define SH_CLK_MSTP32(_p, _r, _b, _f)					\
+	SH_CLK_MSTP(_p, _r, _b, _f | CLK_ENABLE_REG_32BIT)
+
+#define SH_CLK_MSTP16(_p, _r, _b, _f)					\
+	SH_CLK_MSTP(_p, _r, _b, _f | CLK_ENABLE_REG_16BIT)
+
+#define SH_CLK_MSTP8(_p, _r, _b, _f)					\
+	SH_CLK_MSTP(_p, _r, _b, _f | CLK_ENABLE_REG_8BIT)
+
+int sh_clk_mstp_register(struct clk *clks, int nr);
+
+/*
+ * MSTP registration never really cared about access size, despite the
+ * original enable/disable pairs assuming a 32-bit access. Clocks are
+ * responsible for defining their access sizes either directly or via the
+ * clock definition wrappers.
+ */
+static inline int __deprecated sh_clk_mstp32_register(struct clk *clks, int nr)
+{
+	return sh_clk_mstp_register(clks, nr);
+}
 
 #define SH_CLK_DIV4(_parent, _reg, _shift, _div_bitmap, _flags)	\
 {								\
diff --git a/include/linux/sh_intc.h b/include/linux/sh_intc.h
index 6aed080..3238328 100644
--- a/include/linux/sh_intc.h
+++ b/include/linux/sh_intc.h
@@ -133,7 +133,6 @@
 }
 
 int register_intc_controller(struct intc_desc *desc);
-void reserve_intc_vectors(struct intc_vect *vectors, unsigned int nr_vecs);
 int intc_set_priority(unsigned int irq, unsigned int prio);
 int intc_irq_lookup(const char *chipname, intc_enum enum_id);
 void intc_finalize(void);
diff --git a/include/linux/suspend.h b/include/linux/suspend.h
index ac1c114..cd83059 100644
--- a/include/linux/suspend.h
+++ b/include/linux/suspend.h
@@ -356,8 +356,9 @@
 extern bool events_check_enabled;
 
 extern bool pm_wakeup_pending(void);
-extern bool pm_get_wakeup_count(unsigned int *count);
+extern bool pm_get_wakeup_count(unsigned int *count, bool block);
 extern bool pm_save_wakeup_count(unsigned int count);
+extern void pm_wakep_autosleep_enabled(bool set);
 
 static inline void lock_system_sleep(void)
 {
@@ -407,6 +408,17 @@
 
 #endif /* !CONFIG_PM_SLEEP */
 
+#ifdef CONFIG_PM_AUTOSLEEP
+
+/* kernel/power/autosleep.c */
+void queue_up_suspend_work(void);
+
+#else /* !CONFIG_PM_AUTOSLEEP */
+
+static inline void queue_up_suspend_work(void) {}
+
+#endif /* !CONFIG_PM_AUTOSLEEP */
+
 #ifdef CONFIG_ARCH_SAVE_PAGE_KEYS
 /*
  * The ARCH_SAVE_PAGE_KEYS functions can be used by an architecture
diff --git a/include/sound/asound.h b/include/sound/asound.h
index a2e4ff5..0876a1e 100644
--- a/include/sound/asound.h
+++ b/include/sound/asound.h
@@ -70,6 +70,20 @@
 
 /****************************************************************************
  *                                                                          *
+ *        CEA-861 Audio InfoFrame. Used in HDMI and DisplayPort		    *
+ *                                                                          *
+ ****************************************************************************/
+
+struct snd_cea_861_aud_if {
+	unsigned char db1_ct_cc; /* coding type and channel count */
+	unsigned char db2_sf_ss; /* sample frequency and size */
+	unsigned char db3; /* not used, all zeros */
+	unsigned char db4_ca; /* channel allocation code */
+	unsigned char db5_dminh_lsv; /* downmix inhibit & level-shit values */
+};
+
+/****************************************************************************
+ *                                                                          *
  *      Section for driver hardware dependent interface - /dev/snd/hw?      *
  *                                                                          *
  ****************************************************************************/
diff --git a/include/sound/asoundef.h b/include/sound/asoundef.h
index 20ebf32..bb05c02 100644
--- a/include/sound/asoundef.h
+++ b/include/sound/asoundef.h
@@ -170,6 +170,47 @@
 #define IEC958_AES5_CON_CGMSA_COPYNOMORE (2<<0)	/* condition not be used */
 #define IEC958_AES5_CON_CGMSA_COPYNEVER	(3<<0)	/* no copying is permitted */
 
+/****************************************************************************
+ *                                                                          *
+ *        CEA-861 Audio InfoFrame. Used in HDMI and DisplayPort		    *
+ *                                                                          *
+ ****************************************************************************/
+#define CEA861_AUDIO_INFOFRAME_DB1CC		(7<<0) /* mask - channel count */
+#define CEA861_AUDIO_INFOFRAME_DB1CT		(0xf<<4) /* mask - coding type */
+#define CEA861_AUDIO_INFOFRAME_DB1CT_FROM_STREAM (0<<4) /* refer to stream */
+#define CEA861_AUDIO_INFOFRAME_DB1CT_IEC60958	(1<<4) /* IEC-60958 L-PCM */
+#define CEA861_AUDIO_INFOFRAME_DB1CT_AC3	(2<<4) /* AC-3 */
+#define CEA861_AUDIO_INFOFRAME_DB1CT_MPEG1	(3<<4) /* MPEG1 Layers 1 & 2 */
+#define CEA861_AUDIO_INFOFRAME_DB1CT_MP3	(4<<4) /* MPEG1 Layer 3 */
+#define CEA861_AUDIO_INFOFRAME_DB1CT_MPEG2_MULTICH (5<<4) /* MPEG2 Multichannel */
+#define CEA861_AUDIO_INFOFRAME_DB1CT_AAC	(6<<4) /* AAC */
+#define CEA861_AUDIO_INFOFRAME_DB1CT_DTS	(7<<4) /* DTS */
+#define CEA861_AUDIO_INFOFRAME_DB1CT_ATRAC	(8<<4) /* ATRAC */
+#define CEA861_AUDIO_INFOFRAME_DB1CT_ONEBIT	(9<<4) /* One Bit Audio */
+#define CEA861_AUDIO_INFOFRAME_DB1CT_DOLBY_DIG_PLUS (10<<4) /* Dolby Digital + */
+#define CEA861_AUDIO_INFOFRAME_DB1CT_DTS_HD	(11<<4) /* DTS-HD */
+#define CEA861_AUDIO_INFOFRAME_DB1CT_MAT	(12<<4) /* MAT (MLP) */
+#define CEA861_AUDIO_INFOFRAME_DB1CT_DST	(13<<4) /* DST */
+#define CEA861_AUDIO_INFOFRAME_DB1CT_WMA_PRO	(14<<4) /* WMA Pro */
+#define CEA861_AUDIO_INFOFRAME_DB2SF		(7<<2) /* mask - sample frequency */
+#define CEA861_AUDIO_INFOFRAME_DB2SF_FROM_STREAM (0<<2) /* refer to stream */
+#define CEA861_AUDIO_INFOFRAME_DB2SF_32000	(1<<2) /* 32kHz */
+#define CEA861_AUDIO_INFOFRAME_DB2SF_44100	(2<<2) /* 44.1kHz */
+#define CEA861_AUDIO_INFOFRAME_DB2SF_48000	(3<<2) /* 48kHz */
+#define CEA861_AUDIO_INFOFRAME_DB2SF_88200	(4<<2) /* 88.2kHz */
+#define CEA861_AUDIO_INFOFRAME_DB2SF_96000	(5<<2) /* 96kHz */
+#define CEA861_AUDIO_INFOFRAME_DB2SF_176400	(6<<2) /* 176.4kHz */
+#define CEA861_AUDIO_INFOFRAME_DB2SF_192000	(7<<2) /* 192kHz */
+#define CEA861_AUDIO_INFOFRAME_DB2SS		(3<<0) /* mask - sample size */
+#define CEA861_AUDIO_INFOFRAME_DB2SS_FROM_STREAM (0<<0) /* refer to stream */
+#define CEA861_AUDIO_INFOFRAME_DB2SS_16BIT	(1<<0) /* 16 bits */
+#define CEA861_AUDIO_INFOFRAME_DB2SS_20BIT	(2<<0) /* 20 bits */
+#define CEA861_AUDIO_INFOFRAME_DB2SS_24BIT	(3<<0) /* 24 bits */
+#define CEA861_AUDIO_INFOFRAME_DB5_DM_INH	(1<<7) /* mask - inhibit downmixing */
+#define CEA861_AUDIO_INFOFRAME_DB5_DM_INH_PERMITTED (0<<7) /* stereo downmix permitted */
+#define CEA861_AUDIO_INFOFRAME_DB5_DM_INH_PROHIBITED (1<<7) /* stereo downmis prohibited */
+#define CEA861_AUDIO_INFOFRAME_DB5_LSV		(0xf<<3) /* mask - level-shift values */
+
 /*****************************************************************************
  *                                                                           *
  *                            MIDI v1.0 interface                            *
diff --git a/include/sound/cs42l52.h b/include/sound/cs42l52.h
new file mode 100644
index 0000000..4c68955
--- /dev/null
+++ b/include/sound/cs42l52.h
@@ -0,0 +1,36 @@
+/*
+ * linux/sound/cs42l52.h -- Platform data for CS42L52
+ *
+ * Copyright (c) 2012 Cirrus Logic 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.
+ */
+
+#ifndef __CS42L52_H
+#define __CS42L52_H
+
+struct cs42l52_platform_data {
+
+	/* MICBIAS Level. Check datasheet Pg48 */
+	unsigned int micbias_lvl;
+
+	/* MICA mode selection 0=Single 1=Differential */
+	unsigned int mica_cfg;
+
+	/* MICB mode selection 0=Single 1=Differential */
+	unsigned int micb_cfg;
+
+	/* MICA Select 0=MIC1A 1=MIC2A */
+	unsigned int mica_sel;
+
+	/* MICB Select 0=MIC2A 1=MIC2B */
+	unsigned int micb_sel;
+
+	/* Charge Pump Freq. Check datasheet Pg73 */
+	unsigned int chgfreq;
+
+};
+
+#endif /* __CS42L52_H */
diff --git a/include/sound/max98095.h b/include/sound/max98095.h
index 7513a42..e87ae67 100644
--- a/include/sound/max98095.h
+++ b/include/sound/max98095.h
@@ -49,6 +49,18 @@
 	 */
 	unsigned int digmic_left_mode:1;
 	unsigned int digmic_right_mode:1;
+
+	/* Pin5 is the mechanical method of sensing jack insertion
+	 * but it is something that might not be supported.
+	 * 0 = PIN5 not supported
+	 * 1 = PIN5 supported
+	 */
+	unsigned int jack_detect_pin5en:1;
+
+	/* Slew amount for jack detection. Calculated as 4 * (delay + 1).
+	 * Default delay is 24 to get a time of 100ms.
+	 */
+	unsigned int jack_detect_delay;
 };
 
 #endif
diff --git a/include/sound/sh_fsi.h b/include/sound/sh_fsi.h
index b457e87..9060103 100644
--- a/include/sound/sh_fsi.h
+++ b/include/sound/sh_fsi.h
@@ -21,10 +21,11 @@
 /*
  * flags format
  *
- * 0x000000BA
+ * 0x00000CBA
  *
  * A:  inversion
  * B:  format mode
+ * C:  chip specific
  */
 
 /* A: clock inversion */
@@ -39,6 +40,9 @@
 #define SH_FSI_FMT_DAI		(0 << 4)
 #define SH_FSI_FMT_SPDIF	(1 << 4)
 
+/* C: chip specific */
+#define SH_FSI_OPTION_MASK	0x00000F00
+#define SH_FSI_ENABLE_STREAM_MODE	(1 << 8) /* for 16bit data */
 
 /*
  * set_rate return value
@@ -84,16 +88,4 @@
 	struct sh_fsi_port_info port_b;
 };
 
-/*
- * for fsi-ak4642
- */
-struct fsi_ak4642_info {
-	const char *name;
-	const char *card;
-	const char *cpu_dai;
-	const char *codec;
-	const char *platform;
-	int id;
-};
-
 #endif /* __SOUND_FSI_H */
diff --git a/include/sound/simple_card.h b/include/sound/simple_card.h
new file mode 100644
index 0000000..4b62b8d
--- /dev/null
+++ b/include/sound/simple_card.h
@@ -0,0 +1,38 @@
+/*
+ * ASoC simple sound card support
+ *
+ * Copyright (C) 2012 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * 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.
+ */
+
+#ifndef __SIMPLE_CARD_H
+#define __SIMPLE_CARD_H
+
+#include <sound/soc.h>
+
+struct asoc_simple_dai_init_info {
+	unsigned int fmt;
+	unsigned int cpu_daifmt;
+	unsigned int codec_daifmt;
+	unsigned int sysclk;
+};
+
+struct asoc_simple_card_info {
+	const char *name;
+	const char *card;
+	const char *cpu_dai;
+	const char *codec;
+	const char *platform;
+	const char *codec_dai;
+	struct asoc_simple_dai_init_info *init; /* for snd_link.init */
+
+	/* used in simple-card.c */
+	struct snd_soc_dai_link snd_link;
+	struct snd_soc_card snd_card;
+};
+
+#endif /* __SIMPLE_CARD_H */
diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h
index c429f24..1f69e0a 100644
--- a/include/sound/soc-dai.h
+++ b/include/sound/soc-dai.h
@@ -173,6 +173,8 @@
 		struct snd_soc_dai *);
 	int (*trigger)(struct snd_pcm_substream *, int,
 		struct snd_soc_dai *);
+	int (*bespoke_trigger)(struct snd_pcm_substream *, int,
+		struct snd_soc_dai *);
 	/*
 	 * For hardware based FIFO caused delay reporting.
 	 * Optional.
@@ -196,6 +198,7 @@
 	const char *name;
 	unsigned int id;
 	int ac97_control;
+	unsigned int base;
 
 	/* DAI driver callbacks */
 	int (*probe)(struct snd_soc_dai *dai);
@@ -241,6 +244,7 @@
 
 	struct snd_soc_dapm_widget *playback_widget;
 	struct snd_soc_dapm_widget *capture_widget;
+	struct snd_soc_dapm_context dapm;
 
 	/* DAI DMA data */
 	void *playback_dma_data;
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h
index 8da3c24..e3833d9 100644
--- a/include/sound/soc-dapm.h
+++ b/include/sound/soc-dapm.h
@@ -141,10 +141,6 @@
 {       .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
 	.invert = winvert, .kcontrol_news = wcontrols, \
 	.num_kcontrols = wncontrols, .event = wevent, .event_flags = wflags}
-#define SND_SOC_DAPM_MICBIAS_E(wname, wreg, wshift, winvert, wevent, wflags) \
-{	.id = snd_soc_dapm_micbias, .name = wname, .reg = wreg, .shift = wshift, \
-	.invert = winvert, .kcontrol_news = NULL, .num_kcontrols = 0, \
-	.event = wevent, .event_flags = wflags}
 #define SND_SOC_DAPM_SWITCH_E(wname, wreg, wshift, winvert, wcontrols, \
 	wevent, wflags) \
 {	.id = snd_soc_dapm_switch, .name = wname, .reg = wreg, .shift = wshift, \
@@ -324,6 +320,8 @@
 struct snd_soc_dapm_pin;
 struct snd_soc_dapm_route;
 struct snd_soc_dapm_context;
+struct regulator;
+struct snd_soc_dapm_widget_list;
 
 int dapm_reg_event(struct snd_soc_dapm_widget *w,
 		   struct snd_kcontrol *kcontrol, int event);
@@ -359,6 +357,10 @@
 int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm,
 				 struct snd_soc_dai *dai);
 int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card);
+int snd_soc_dapm_new_pcm(struct snd_soc_card *card,
+			 const struct snd_soc_pcm_stream *params,
+			 struct snd_soc_dapm_widget *source,
+			 struct snd_soc_dapm_widget *sink);
 
 /* dapm path setup */
 int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm);
@@ -369,8 +371,8 @@
 			     const struct snd_soc_dapm_route *route, int num);
 
 /* dapm events */
-int snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream,
-			      struct snd_soc_dai *dai, int event);
+void snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream,
+	int event);
 void snd_soc_dapm_shutdown(struct snd_soc_card *card);
 
 /* external DAPM widget events */
@@ -402,6 +404,10 @@
 /* Mostly internal - should not normally be used */
 void dapm_mark_dirty(struct snd_soc_dapm_widget *w, const char *reason);
 
+/* dapm path query */
+int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
+	struct snd_soc_dapm_widget_list **list);
+
 /* dapm widget types */
 enum snd_soc_dapm_type {
 	snd_soc_dapm_input = 0,		/* input pin */
@@ -430,6 +436,12 @@
 	snd_soc_dapm_aif_out,		/* audio interface output */
 	snd_soc_dapm_siggen,		/* signal generator */
 	snd_soc_dapm_dai,		/* link to DAI structure */
+	snd_soc_dapm_dai_link,		/* link between two DAI structures */
+};
+
+enum snd_soc_dapm_subclass {
+	SND_SOC_DAPM_CLASS_INIT		= 0,
+	SND_SOC_DAPM_CLASS_RUNTIME	= 1,
 };
 
 /*
@@ -482,9 +494,11 @@
 	struct snd_soc_dapm_context *dapm;
 
 	void *priv;				/* widget specific data */
+	struct regulator *regulator;		/* attached regulator */
+	const struct snd_soc_pcm_stream *params; /* params for dai links */
 
 	/* dapm control */
-	short reg;						/* negative reg = no direct dapm */
+	int reg;				/* negative reg = no direct dapm */
 	unsigned char shift;			/* bits to shift */
 	unsigned int saved_value;		/* widget saved value */
 	unsigned int value;				/* widget current value */
diff --git a/include/sound/soc-dpcm.h b/include/sound/soc-dpcm.h
new file mode 100644
index 0000000..04598f1
--- /dev/null
+++ b/include/sound/soc-dpcm.h
@@ -0,0 +1,138 @@
+/*
+ * linux/sound/soc-dpcm.h -- ALSA SoC Dynamic PCM Support
+ *
+ * Author:		Liam Girdwood <lrg@ti.com>
+ *
+ * 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.
+ */
+
+#ifndef __LINUX_SND_SOC_DPCM_H
+#define __LINUX_SND_SOC_DPCM_H
+
+#include <linux/list.h>
+#include <sound/pcm.h>
+
+struct snd_soc_pcm_runtime;
+
+/*
+ * Types of runtime_update to perform. e.g. originated from FE PCM ops
+ * or audio route changes triggered by muxes/mixers.
+ */
+enum snd_soc_dpcm_update {
+	SND_SOC_DPCM_UPDATE_NO	= 0,
+	SND_SOC_DPCM_UPDATE_BE,
+	SND_SOC_DPCM_UPDATE_FE,
+};
+
+/*
+ * Dynamic PCM Frontend -> Backend link management states.
+ */
+enum snd_soc_dpcm_link_state {
+	SND_SOC_DPCM_LINK_STATE_NEW	= 0,	/* newly created link */
+	SND_SOC_DPCM_LINK_STATE_FREE,		/* link to be dismantled */
+};
+
+/*
+ * Dynamic PCM Frontend -> Backend link PCM states.
+ */
+enum snd_soc_dpcm_state {
+	SND_SOC_DPCM_STATE_NEW	= 0,
+	SND_SOC_DPCM_STATE_OPEN,
+	SND_SOC_DPCM_STATE_HW_PARAMS,
+	SND_SOC_DPCM_STATE_PREPARE,
+	SND_SOC_DPCM_STATE_START,
+	SND_SOC_DPCM_STATE_STOP,
+	SND_SOC_DPCM_STATE_PAUSED,
+	SND_SOC_DPCM_STATE_SUSPEND,
+	SND_SOC_DPCM_STATE_HW_FREE,
+	SND_SOC_DPCM_STATE_CLOSE,
+};
+
+/*
+ * Dynamic PCM trigger ordering. Triggering flexibility is required as some
+ * DSPs require triggering before/after their CPU platform and DAIs.
+ *
+ * i.e. some clients may want to manually order this call in their PCM
+ * trigger() whilst others will just use the regular core ordering.
+ */
+enum snd_soc_dpcm_trigger {
+	SND_SOC_DPCM_TRIGGER_PRE		= 0,
+	SND_SOC_DPCM_TRIGGER_POST,
+	SND_SOC_DPCM_TRIGGER_BESPOKE,
+};
+
+/*
+ * Dynamic PCM link
+ * This links together a FE and BE DAI at runtime and stores the link
+ * state information and the hw_params configuration.
+ */
+struct snd_soc_dpcm {
+	/* FE and BE DAIs*/
+	struct snd_soc_pcm_runtime *be;
+	struct snd_soc_pcm_runtime *fe;
+
+	/* link state */
+	enum snd_soc_dpcm_link_state state;
+
+	/* list of BE and FE for this DPCM link */
+	struct list_head list_be;
+	struct list_head list_fe;
+
+	/* hw params for this link - may be different for each link */
+	struct snd_pcm_hw_params hw_params;
+#ifdef CONFIG_DEBUG_FS
+	struct dentry *debugfs_state;
+#endif
+};
+
+/*
+ * Dynamic PCM runtime data.
+ */
+struct snd_soc_dpcm_runtime {
+	struct list_head be_clients;
+	struct list_head fe_clients;
+
+	int users;
+	struct snd_pcm_runtime *runtime;
+	struct snd_pcm_hw_params hw_params;
+
+	/* state and update */
+	enum snd_soc_dpcm_update runtime_update;
+	enum snd_soc_dpcm_state state;
+};
+
+/* can this BE stop and free */
+int snd_soc_dpcm_can_be_free_stop(struct snd_soc_pcm_runtime *fe,
+		struct snd_soc_pcm_runtime *be, int stream);
+
+/* can this BE perform a hw_params() */
+int snd_soc_dpcm_can_be_params(struct snd_soc_pcm_runtime *fe,
+		struct snd_soc_pcm_runtime *be, int stream);
+
+/* is the current PCM operation for this FE ? */
+int snd_soc_dpcm_fe_can_update(struct snd_soc_pcm_runtime *fe, int stream);
+
+/* is the current PCM operation for this BE ? */
+int snd_soc_dpcm_be_can_update(struct snd_soc_pcm_runtime *fe,
+		struct snd_soc_pcm_runtime *be, int stream);
+
+/* get the substream for this BE */
+struct snd_pcm_substream *
+	snd_soc_dpcm_get_substream(struct snd_soc_pcm_runtime *be, int stream);
+
+/* get the BE runtime state */
+enum snd_soc_dpcm_state
+	snd_soc_dpcm_be_get_state(struct snd_soc_pcm_runtime *be, int stream);
+
+/* set the BE runtime state */
+void snd_soc_dpcm_be_set_state(struct snd_soc_pcm_runtime *be, int stream,
+	enum snd_soc_dpcm_state state);
+
+/* internal use only */
+int soc_dpcm_be_digital_mute(struct snd_soc_pcm_runtime *fe, int mute);
+int soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd);
+int soc_dpcm_runtime_update(struct snd_soc_dapm_widget *);
+
+#endif
diff --git a/include/sound/soc.h b/include/sound/soc.h
index 2ebf787..c703871 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -55,6 +55,18 @@
 	.info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\
 	.put = snd_soc_put_volsw, \
 	.private_value =  SOC_SINGLE_VALUE(reg, shift, max, invert) }
+#define SOC_SINGLE_SX_TLV(xname, xreg, xshift, xmin, xmax, tlv_array) \
+{       .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+	SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+	.tlv.p  = (tlv_array),\
+	.info = snd_soc_info_volsw, \
+	.get = snd_soc_get_volsw_sx,\
+	.put = snd_soc_put_volsw_sx, \
+	.private_value = (unsigned long)&(struct soc_mixer_control) \
+		{.reg = xreg, .rreg = xreg, \
+		.shift = xshift, .rshift = xshift, \
+		.max = xmax, .min = xmin} }
 #define SOC_DOUBLE(xname, reg, shift_left, shift_right, max, invert) \
 {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
 	.info = snd_soc_info_volsw, .get = snd_soc_get_volsw, \
@@ -85,6 +97,18 @@
 	.get = snd_soc_get_volsw, .put = snd_soc_put_volsw, \
 	.private_value = SOC_DOUBLE_R_VALUE(reg_left, reg_right, xshift, \
 					    xmax, xinvert) }
+#define SOC_DOUBLE_R_SX_TLV(xname, xreg, xrreg, xshift, xmin, xmax, tlv_array) \
+{       .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+	SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+	.tlv.p  = (tlv_array), \
+	.info = snd_soc_info_volsw, \
+	.get = snd_soc_get_volsw_sx, \
+	.put = snd_soc_put_volsw_sx, \
+	.private_value = (unsigned long)&(struct soc_mixer_control) \
+		{.reg = xreg, .rreg = xrreg, \
+		.shift = xshift, .rshift = xshift, \
+		.max = xmax, .min = xmin} }
 #define SOC_DOUBLE_S8_TLV(xname, xreg, xmin, xmax, tlv_array) \
 {	.iface  = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
 	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
@@ -171,20 +195,6 @@
 	.get = xhandler_get, .put = xhandler_put, \
 	.private_value = (unsigned long)&xenum }
 
-#define SOC_DOUBLE_R_SX_TLV(xname, xreg_left, xreg_right, xshift,\
-		xmin, xmax, tlv_array) \
-{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
-	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
-		  SNDRV_CTL_ELEM_ACCESS_READWRITE, \
-	.tlv.p = (tlv_array), \
-	.info = snd_soc_info_volsw_2r_sx, \
-	.get = snd_soc_get_volsw_2r_sx, \
-	.put = snd_soc_put_volsw_2r_sx, \
-	.private_value = (unsigned long)&(struct soc_mixer_control) \
-		{.reg = xreg_left, \
-		 .rreg = xreg_right, .shift = xshift, \
-		 .min = xmin, .max = xmax} }
-
 #define SND_SOC_BYTES(xname, xbase, xregs)		      \
 {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,   \
 	.info = snd_soc_bytes_info, .get = snd_soc_bytes_get, \
@@ -200,6 +210,19 @@
 		{.base = xbase, .num_regs = xregs,	      \
 		 .mask = xmask }) }
 
+#define SOC_SINGLE_XR_SX(xname, xregbase, xregcount, xnbits, \
+		xmin, xmax, xinvert) \
+{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+	.info = snd_soc_info_xr_sx, .get = snd_soc_get_xr_sx, \
+	.put = snd_soc_put_xr_sx, \
+	.private_value = (unsigned long)&(struct soc_mreg_control) \
+		{.regbase = xregbase, .regcount = xregcount, .nbits = xnbits, \
+		.invert = xinvert, .min = xmin, .max = xmax} }
+
+#define SOC_SINGLE_STROBE(xname, xreg, xshift, xinvert) \
+	SOC_SINGLE_EXT(xname, xreg, xshift, 1, xinvert, \
+		snd_soc_get_strobe, snd_soc_put_strobe)
+
 /*
  * Simplified versions of above macros, declaring a struct and calculating
  * ARRAY_SIZE internally
@@ -264,6 +287,7 @@
 struct snd_soc_jack_pin;
 struct snd_soc_cache_ops;
 #include <sound/soc-dapm.h>
+#include <sound/soc-dpcm.h>
 
 #ifdef CONFIG_GPIOLIB
 struct snd_soc_jack_gpio;
@@ -288,6 +312,11 @@
 	SND_SOC_PCM_CLASS_BE	= 1,
 };
 
+enum snd_soc_card_subclass {
+	SND_SOC_CARD_CLASS_INIT		= 0,
+	SND_SOC_CARD_CLASS_RUNTIME	= 1,
+};
+
 int snd_soc_codec_set_sysclk(struct snd_soc_codec *codec, int clk_id,
 			     int source, unsigned int freq, int dir);
 int snd_soc_codec_set_pll(struct snd_soc_codec *codec, int pll_id, int source,
@@ -333,6 +362,11 @@
 					unsigned int reg, unsigned int val);
 int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num);
 
+struct snd_pcm_substream *snd_soc_get_dai_substream(struct snd_soc_card *card,
+		const char *dai_link, int stream);
+struct snd_soc_pcm_runtime *snd_soc_get_pcm_runtime(struct snd_soc_card *card,
+		const char *dai_link);
+
 /* Utility functions to get clock rates from various things */
 int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots);
 int snd_soc_params_to_frame_size(struct snd_pcm_hw_params *params);
@@ -343,6 +377,9 @@
 int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream,
 	const struct snd_pcm_hardware *hw);
 
+int snd_soc_platform_trigger(struct snd_pcm_substream *substream,
+		int cmd, struct snd_soc_platform *platform);
+
 /* Jack reporting */
 int snd_soc_jack_new(struct snd_soc_codec *codec, const char *id, int type,
 		     struct snd_soc_jack *jack);
@@ -413,6 +450,10 @@
 	struct snd_ctl_elem_value *ucontrol);
 #define snd_soc_get_volsw_2r snd_soc_get_volsw
 #define snd_soc_put_volsw_2r snd_soc_put_volsw
+int snd_soc_get_volsw_sx(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol);
+int snd_soc_put_volsw_sx(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol);
 int snd_soc_info_volsw_s8(struct snd_kcontrol *kcontrol,
 	struct snd_ctl_elem_info *uinfo);
 int snd_soc_get_volsw_s8(struct snd_kcontrol *kcontrol,
@@ -421,19 +462,22 @@
 	struct snd_ctl_elem_value *ucontrol);
 int snd_soc_limit_volume(struct snd_soc_codec *codec,
 	const char *name, int max);
-int snd_soc_info_volsw_2r_sx(struct snd_kcontrol *kcontrol,
-	struct snd_ctl_elem_info *uinfo);
-int snd_soc_get_volsw_2r_sx(struct snd_kcontrol *kcontrol,
-	struct snd_ctl_elem_value *ucontrol);
-int snd_soc_put_volsw_2r_sx(struct snd_kcontrol *kcontrol,
-	struct snd_ctl_elem_value *ucontrol);
 int snd_soc_bytes_info(struct snd_kcontrol *kcontrol,
 		       struct snd_ctl_elem_info *uinfo);
 int snd_soc_bytes_get(struct snd_kcontrol *kcontrol,
 		      struct snd_ctl_elem_value *ucontrol);
 int snd_soc_bytes_put(struct snd_kcontrol *kcontrol,
 		      struct snd_ctl_elem_value *ucontrol);
-
+int snd_soc_info_xr_sx(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_info *uinfo);
+int snd_soc_get_xr_sx(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol);
+int snd_soc_put_xr_sx(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol);
+int snd_soc_get_strobe(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol);
+int snd_soc_put_strobe(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol);
 
 /**
  * struct snd_soc_reg_access - Describes whether a given register is
@@ -513,6 +557,7 @@
 #endif
 
 struct snd_soc_jack {
+	struct mutex mutex;
 	struct snd_jack *jack;
 	struct snd_soc_codec *codec;
 	struct list_head pins;
@@ -711,6 +756,7 @@
 	/* platform IO - used for platform DAPM */
 	unsigned int (*read)(struct snd_soc_platform *, unsigned int);
 	int (*write)(struct snd_soc_platform *, unsigned int, unsigned int);
+	int (*bespoke_trigger)(struct snd_pcm_substream *, int);
 };
 
 struct snd_soc_platform {
@@ -746,21 +792,36 @@
 	const char *cpu_dai_name;
 	const struct device_node *cpu_dai_of_node;
 	const char *codec_dai_name;
+	int be_id;	/* optional ID for machine driver BE identification */
+
+	const struct snd_soc_pcm_stream *params;
 
 	unsigned int dai_fmt;           /* format to set on init */
 
+	enum snd_soc_dpcm_trigger trigger[2]; /* trigger type for DPCM */
+
 	/* Keep DAI active over suspend */
 	unsigned int ignore_suspend:1;
 
 	/* Symmetry requirements */
 	unsigned int symmetric_rates:1;
 
+	/* Do not create a PCM for this DAI link (Backend link) */
+	unsigned int no_pcm:1;
+
+	/* This DAI link can route to other DAI links at runtime (Frontend)*/
+	unsigned int dynamic:1;
+
 	/* pmdown_time is ignored at stop */
 	unsigned int ignore_pmdown_time:1;
 
 	/* codec/machine specific init - e.g. add machine controls */
 	int (*init)(struct snd_soc_pcm_runtime *rtd);
 
+	/* optional hw_params re-writing for BE and FE sync */
+	int (*be_hw_params_fixup)(struct snd_soc_pcm_runtime *rtd,
+			struct snd_pcm_hw_params *params);
+
 	/* machine stream operations */
 	struct snd_soc_ops *ops;
 };
@@ -800,6 +861,7 @@
 
 	struct list_head list;
 	struct mutex mutex;
+	struct mutex dapm_mutex;
 
 	bool instantiated;
 
@@ -889,9 +951,11 @@
 	enum snd_soc_pcm_subclass pcm_subclass;
 	struct snd_pcm_ops ops;
 
-	unsigned int complete:1;
 	unsigned int dev_registered:1;
 
+	/* Dynamic PCM BE runtime data */
+	struct snd_soc_dpcm_runtime dpcm[2];
+
 	long pmdown_time;
 
 	/* runtime devices */
@@ -902,6 +966,10 @@
 	struct snd_soc_dai *cpu_dai;
 
 	struct delayed_work delayed_work;
+#ifdef CONFIG_DEBUG_FS
+	struct dentry *debugfs_dpcm_root;
+	struct dentry *debugfs_dpcm_state;
+#endif
 };
 
 /* mixer control */
@@ -916,6 +984,12 @@
 	u32 mask;
 };
 
+/* multi register control */
+struct soc_mreg_control {
+	long min, max;
+	unsigned int regbase, regcount, nbits, invert;
+};
+
 /* enumerated kcontrol */
 struct soc_enum {
 	unsigned short reg;
diff --git a/include/trace/events/asoc.h b/include/trace/events/asoc.h
index ab26f8a..5fc2dcd 100644
--- a/include/trace/events/asoc.h
+++ b/include/trace/events/asoc.h
@@ -7,6 +7,8 @@
 #include <linux/ktime.h>
 #include <linux/tracepoint.h>
 
+#define DAPM_DIRECT "(direct)"
+
 struct snd_soc_jack;
 struct snd_soc_codec;
 struct snd_soc_platform;
@@ -241,6 +243,84 @@
 		  (int)__entry->path_checks, (int)__entry->neighbour_checks)
 );
 
+TRACE_EVENT(snd_soc_dapm_output_path,
+
+	TP_PROTO(struct snd_soc_dapm_widget *widget,
+		struct snd_soc_dapm_path *path),
+
+	TP_ARGS(widget, path),
+
+	TP_STRUCT__entry(
+		__string(	wname,	widget->name		)
+		__string(	pname,	path->name ? path->name : DAPM_DIRECT)
+		__string(	psname,	path->sink->name	)
+		__field(	int,	path_sink		)
+		__field(	int,	path_connect		)
+	),
+
+	TP_fast_assign(
+		__assign_str(wname, widget->name);
+		__assign_str(pname, path->name ? path->name : DAPM_DIRECT);
+		__assign_str(psname, path->sink->name);
+		__entry->path_connect = path->connect;
+		__entry->path_sink = (long)path->sink;
+	),
+
+	TP_printk("%c%s -> %s -> %s\n",
+		(int) __entry->path_sink &&
+		(int) __entry->path_connect ? '*' : ' ',
+		__get_str(wname), __get_str(pname), __get_str(psname))
+);
+
+TRACE_EVENT(snd_soc_dapm_input_path,
+
+	TP_PROTO(struct snd_soc_dapm_widget *widget,
+		struct snd_soc_dapm_path *path),
+
+	TP_ARGS(widget, path),
+
+	TP_STRUCT__entry(
+		__string(	wname,	widget->name		)
+		__string(	pname,	path->name ? path->name : DAPM_DIRECT)
+		__string(	psname,	path->source->name	)
+		__field(	int,	path_source		)
+		__field(	int,	path_connect		)
+	),
+
+	TP_fast_assign(
+		__assign_str(wname, widget->name);
+		__assign_str(pname, path->name ? path->name : DAPM_DIRECT);
+		__assign_str(psname, path->source->name);
+		__entry->path_connect = path->connect;
+		__entry->path_source = (long)path->source;
+	),
+
+	TP_printk("%c%s <- %s <- %s\n",
+		(int) __entry->path_source &&
+		(int) __entry->path_connect ? '*' : ' ',
+		__get_str(wname), __get_str(pname), __get_str(psname))
+);
+
+TRACE_EVENT(snd_soc_dapm_connected,
+
+	TP_PROTO(int paths, int stream),
+
+	TP_ARGS(paths, stream),
+
+	TP_STRUCT__entry(
+		__field(	int,	paths		)
+		__field(	int,	stream		)
+	),
+
+	TP_fast_assign(
+		__entry->paths = paths;
+		__entry->stream = stream;
+	),
+
+	TP_printk("%s: found %d paths\n",
+		__entry->stream ? "capture" : "playback", __entry->paths)
+);
+
 TRACE_EVENT(snd_soc_jack_irq,
 
 	TP_PROTO(const char *name),
diff --git a/include/trace/events/power.h b/include/trace/events/power.h
index cae9a94..0c978384 100644
--- a/include/trace/events/power.h
+++ b/include/trace/events/power.h
@@ -65,6 +65,40 @@
 	TP_printk("state=%lu", (unsigned long)__entry->state)
 );
 
+DECLARE_EVENT_CLASS(wakeup_source,
+
+	TP_PROTO(const char *name, unsigned int state),
+
+	TP_ARGS(name, state),
+
+	TP_STRUCT__entry(
+		__string(       name,           name            )
+		__field(        u64,            state           )
+	),
+
+	TP_fast_assign(
+		__assign_str(name, name);
+		__entry->state = state;
+	),
+
+	TP_printk("%s state=0x%lx", __get_str(name),
+		(unsigned long)__entry->state)
+);
+
+DEFINE_EVENT(wakeup_source, wakeup_source_activate,
+
+	TP_PROTO(const char *name, unsigned int state),
+
+	TP_ARGS(name, state)
+);
+
+DEFINE_EVENT(wakeup_source, wakeup_source_deactivate,
+
+	TP_PROTO(const char *name, unsigned int state),
+
+	TP_ARGS(name, state)
+);
+
 #ifdef CONFIG_EVENT_POWER_TRACING_DEPRECATED
 
 /*
diff --git a/init/Kconfig b/init/Kconfig
index da2d2d2..a30fe08 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -27,6 +27,9 @@
 	bool
 	depends on HAVE_IRQ_WORK
 
+config BUILDTIME_EXTABLE_SORT
+	bool
+
 menu "General setup"
 
 config EXPERIMENTAL
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 91a4459..5b06cbb 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -2039,8 +2039,8 @@
  * accessing the event control register. If a NMI hits, then it will
  * not restart the event.
  */
-static void __perf_event_task_sched_out(struct task_struct *task,
-					struct task_struct *next)
+void __perf_event_task_sched_out(struct task_struct *task,
+				 struct task_struct *next)
 {
 	int ctxn;
 
@@ -2279,8 +2279,8 @@
  * accessing the event control register. If a NMI hits, then it will
  * keep the event running.
  */
-static void __perf_event_task_sched_in(struct task_struct *prev,
-				       struct task_struct *task)
+void __perf_event_task_sched_in(struct task_struct *prev,
+				struct task_struct *task)
 {
 	struct perf_event_context *ctx;
 	int ctxn;
@@ -2305,12 +2305,6 @@
 		perf_branch_stack_sched_in(prev, task);
 }
 
-void __perf_event_task_sched(struct task_struct *prev, struct task_struct *next)
-{
-	__perf_event_task_sched_out(prev, next);
-	__perf_event_task_sched_in(prev, next);
-}
-
 static u64 perf_calculate_period(struct perf_event *event, u64 nsec, u64 count)
 {
 	u64 frequency = event->attr.sample_freq;
diff --git a/kernel/extable.c b/kernel/extable.c
index 5339705..fe35a63 100644
--- a/kernel/extable.c
+++ b/kernel/extable.c
@@ -35,10 +35,16 @@
 extern struct exception_table_entry __start___ex_table[];
 extern struct exception_table_entry __stop___ex_table[];
 
+/* Cleared by build time tools if the table is already sorted. */
+u32 __initdata main_extable_sort_needed = 1;
+
 /* Sort the kernel's built-in exception table */
 void __init sort_main_extable(void)
 {
-	sort_extable(__start___ex_table, __stop___ex_table);
+	if (main_extable_sort_needed)
+		sort_extable(__start___ex_table, __stop___ex_table);
+	else
+		pr_notice("__ex_table already sorted, skipping sort\n");
 }
 
 /* Given an address, look for it in the exception tables. */
diff --git a/kernel/fork.c b/kernel/fork.c
index ad54c83..05c813d 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -292,8 +292,6 @@
 	int node = tsk_fork_get_node(orig);
 	int err;
 
-	prepare_to_copy(orig);
-
 	tsk = alloc_task_struct_node(node);
 	if (!tsk)
 		return NULL;
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
index 585f638..bb32326 100644
--- a/kernel/irq/manage.c
+++ b/kernel/irq/manage.c
@@ -1220,12 +1220,6 @@
 	/* Found it - now remove it from the list of entries: */
 	*action_ptr = action->next;
 
-	/* Currently used only by UML, might disappear one day: */
-#ifdef CONFIG_IRQ_RELEASE_METHOD
-	if (desc->irq_data.chip->release)
-		desc->irq_data.chip->release(irq, dev_id);
-#endif
-
 	/* If this was the last handler, shut down the IRQ line: */
 	if (!desc->action)
 		irq_shutdown(desc);
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index deb5461..8f9b4eb 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -103,6 +103,33 @@
 	select HOTPLUG
 	select HOTPLUG_CPU
 
+config PM_AUTOSLEEP
+	bool "Opportunistic sleep"
+	depends on PM_SLEEP
+	default n
+	---help---
+	Allow the kernel to trigger a system transition into a global sleep
+	state automatically whenever there are no active wakeup sources.
+
+config PM_WAKELOCKS
+	bool "User space wakeup sources interface"
+	depends on PM_SLEEP
+	default n
+	---help---
+	Allow user space to create, activate and deactivate wakeup source
+	objects with the help of a sysfs-based interface.
+
+config PM_WAKELOCKS_LIMIT
+	int "Maximum number of user space wakeup sources (0 = no limit)"
+	range 0 100000
+	default 100
+	depends on PM_WAKELOCKS
+
+config PM_WAKELOCKS_GC
+	bool "Garbage collector for user space wakeup sources"
+	depends on PM_WAKELOCKS
+	default y
+
 config PM_RUNTIME
 	bool "Run-time PM core functionality"
 	depends on !IA64_HP_SIM
diff --git a/kernel/power/Makefile b/kernel/power/Makefile
index 66d808e..29472bf 100644
--- a/kernel/power/Makefile
+++ b/kernel/power/Makefile
@@ -9,5 +9,7 @@
 obj-$(CONFIG_PM_TEST_SUSPEND)	+= suspend_test.o
 obj-$(CONFIG_HIBERNATION)	+= hibernate.o snapshot.o swap.o user.o \
 				   block_io.o
+obj-$(CONFIG_PM_AUTOSLEEP)	+= autosleep.o
+obj-$(CONFIG_PM_WAKELOCKS)	+= wakelock.o
 
 obj-$(CONFIG_MAGIC_SYSRQ)	+= poweroff.o
diff --git a/kernel/power/autosleep.c b/kernel/power/autosleep.c
new file mode 100644
index 0000000..ca304046
--- /dev/null
+++ b/kernel/power/autosleep.c
@@ -0,0 +1,127 @@
+/*
+ * kernel/power/autosleep.c
+ *
+ * Opportunistic sleep support.
+ *
+ * Copyright (C) 2012 Rafael J. Wysocki <rjw@sisk.pl>
+ */
+
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/pm_wakeup.h>
+
+#include "power.h"
+
+static suspend_state_t autosleep_state;
+static struct workqueue_struct *autosleep_wq;
+/*
+ * Note: it is only safe to mutex_lock(&autosleep_lock) if a wakeup_source
+ * is active, otherwise a deadlock with try_to_suspend() is possible.
+ * Alternatively mutex_lock_interruptible() can be used.  This will then fail
+ * if an auto_sleep cycle tries to freeze processes.
+ */
+static DEFINE_MUTEX(autosleep_lock);
+static struct wakeup_source *autosleep_ws;
+
+static void try_to_suspend(struct work_struct *work)
+{
+	unsigned int initial_count, final_count;
+
+	if (!pm_get_wakeup_count(&initial_count, true))
+		goto out;
+
+	mutex_lock(&autosleep_lock);
+
+	if (!pm_save_wakeup_count(initial_count)) {
+		mutex_unlock(&autosleep_lock);
+		goto out;
+	}
+
+	if (autosleep_state == PM_SUSPEND_ON) {
+		mutex_unlock(&autosleep_lock);
+		return;
+	}
+	if (autosleep_state >= PM_SUSPEND_MAX)
+		hibernate();
+	else
+		pm_suspend(autosleep_state);
+
+	mutex_unlock(&autosleep_lock);
+
+	if (!pm_get_wakeup_count(&final_count, false))
+		goto out;
+
+	/*
+	 * If the wakeup occured for an unknown reason, wait to prevent the
+	 * system from trying to suspend and waking up in a tight loop.
+	 */
+	if (final_count == initial_count)
+		schedule_timeout_uninterruptible(HZ / 2);
+
+ out:
+	queue_up_suspend_work();
+}
+
+static DECLARE_WORK(suspend_work, try_to_suspend);
+
+void queue_up_suspend_work(void)
+{
+	if (!work_pending(&suspend_work) && autosleep_state > PM_SUSPEND_ON)
+		queue_work(autosleep_wq, &suspend_work);
+}
+
+suspend_state_t pm_autosleep_state(void)
+{
+	return autosleep_state;
+}
+
+int pm_autosleep_lock(void)
+{
+	return mutex_lock_interruptible(&autosleep_lock);
+}
+
+void pm_autosleep_unlock(void)
+{
+	mutex_unlock(&autosleep_lock);
+}
+
+int pm_autosleep_set_state(suspend_state_t state)
+{
+
+#ifndef CONFIG_HIBERNATION
+	if (state >= PM_SUSPEND_MAX)
+		return -EINVAL;
+#endif
+
+	__pm_stay_awake(autosleep_ws);
+
+	mutex_lock(&autosleep_lock);
+
+	autosleep_state = state;
+
+	__pm_relax(autosleep_ws);
+
+	if (state > PM_SUSPEND_ON) {
+		pm_wakep_autosleep_enabled(true);
+		queue_up_suspend_work();
+	} else {
+		pm_wakep_autosleep_enabled(false);
+	}
+
+	mutex_unlock(&autosleep_lock);
+	return 0;
+}
+
+int __init pm_autosleep_init(void)
+{
+	autosleep_ws = wakeup_source_register("autosleep");
+	if (!autosleep_ws)
+		return -ENOMEM;
+
+	autosleep_wq = alloc_ordered_workqueue("autosleep", 0);
+	if (autosleep_wq)
+		return 0;
+
+	wakeup_source_unregister(autosleep_ws);
+	return -ENOMEM;
+}
diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c
index e09dfbf..8b53db3 100644
--- a/kernel/power/hibernate.c
+++ b/kernel/power/hibernate.c
@@ -25,6 +25,8 @@
 #include <linux/freezer.h>
 #include <linux/gfp.h>
 #include <linux/syscore_ops.h>
+#include <linux/ctype.h>
+#include <linux/genhd.h>
 #include <scsi/scsi_scan.h>
 
 #include "power.h"
@@ -722,6 +724,17 @@
 
 	/* Check if the device is there */
 	swsusp_resume_device = name_to_dev_t(resume_file);
+
+	/*
+	 * name_to_dev_t is ineffective to verify parition if resume_file is in
+	 * integer format. (e.g. major:minor)
+	 */
+	if (isdigit(resume_file[0]) && resume_wait) {
+		int partno;
+		while (!get_gendisk(swsusp_resume_device, &partno))
+			msleep(10);
+	}
+
 	if (!swsusp_resume_device) {
 		/*
 		 * Some device discovery might still be in progress; we need
diff --git a/kernel/power/main.c b/kernel/power/main.c
index 1c12581..428f8a0 100644
--- a/kernel/power/main.c
+++ b/kernel/power/main.c
@@ -269,8 +269,7 @@
 	return (s - buf);
 }
 
-static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
-			   const char *buf, size_t n)
+static suspend_state_t decode_state(const char *buf, size_t n)
 {
 #ifdef CONFIG_SUSPEND
 	suspend_state_t state = PM_SUSPEND_STANDBY;
@@ -278,27 +277,48 @@
 #endif
 	char *p;
 	int len;
-	int error = -EINVAL;
 
 	p = memchr(buf, '\n', n);
 	len = p ? p - buf : n;
 
-	/* First, check if we are requested to hibernate */
-	if (len == 4 && !strncmp(buf, "disk", len)) {
-		error = hibernate();
-		goto Exit;
-	}
+	/* Check hibernation first. */
+	if (len == 4 && !strncmp(buf, "disk", len))
+		return PM_SUSPEND_MAX;
 
 #ifdef CONFIG_SUSPEND
-	for (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++, state++) {
-		if (*s && len == strlen(*s) && !strncmp(buf, *s, len)) {
-			error = pm_suspend(state);
-			break;
-		}
-	}
+	for (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++, state++)
+		if (*s && len == strlen(*s) && !strncmp(buf, *s, len))
+			return state;
 #endif
 
- Exit:
+	return PM_SUSPEND_ON;
+}
+
+static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
+			   const char *buf, size_t n)
+{
+	suspend_state_t state;
+	int error;
+
+	error = pm_autosleep_lock();
+	if (error)
+		return error;
+
+	if (pm_autosleep_state() > PM_SUSPEND_ON) {
+		error = -EBUSY;
+		goto out;
+	}
+
+	state = decode_state(buf, n);
+	if (state < PM_SUSPEND_MAX)
+		error = pm_suspend(state);
+	else if (state == PM_SUSPEND_MAX)
+		error = hibernate();
+	else
+		error = -EINVAL;
+
+ out:
+	pm_autosleep_unlock();
 	return error ? error : n;
 }
 
@@ -339,7 +359,8 @@
 {
 	unsigned int val;
 
-	return pm_get_wakeup_count(&val) ? sprintf(buf, "%u\n", val) : -EINTR;
+	return pm_get_wakeup_count(&val, true) ?
+		sprintf(buf, "%u\n", val) : -EINTR;
 }
 
 static ssize_t wakeup_count_store(struct kobject *kobj,
@@ -347,15 +368,106 @@
 				const char *buf, size_t n)
 {
 	unsigned int val;
+	int error;
 
+	error = pm_autosleep_lock();
+	if (error)
+		return error;
+
+	if (pm_autosleep_state() > PM_SUSPEND_ON) {
+		error = -EBUSY;
+		goto out;
+	}
+
+	error = -EINVAL;
 	if (sscanf(buf, "%u", &val) == 1) {
 		if (pm_save_wakeup_count(val))
-			return n;
+			error = n;
 	}
-	return -EINVAL;
+
+ out:
+	pm_autosleep_unlock();
+	return error;
 }
 
 power_attr(wakeup_count);
+
+#ifdef CONFIG_PM_AUTOSLEEP
+static ssize_t autosleep_show(struct kobject *kobj,
+			      struct kobj_attribute *attr,
+			      char *buf)
+{
+	suspend_state_t state = pm_autosleep_state();
+
+	if (state == PM_SUSPEND_ON)
+		return sprintf(buf, "off\n");
+
+#ifdef CONFIG_SUSPEND
+	if (state < PM_SUSPEND_MAX)
+		return sprintf(buf, "%s\n", valid_state(state) ?
+						pm_states[state] : "error");
+#endif
+#ifdef CONFIG_HIBERNATION
+	return sprintf(buf, "disk\n");
+#else
+	return sprintf(buf, "error");
+#endif
+}
+
+static ssize_t autosleep_store(struct kobject *kobj,
+			       struct kobj_attribute *attr,
+			       const char *buf, size_t n)
+{
+	suspend_state_t state = decode_state(buf, n);
+	int error;
+
+	if (state == PM_SUSPEND_ON
+	    && strcmp(buf, "off") && strcmp(buf, "off\n"))
+		return -EINVAL;
+
+	error = pm_autosleep_set_state(state);
+	return error ? error : n;
+}
+
+power_attr(autosleep);
+#endif /* CONFIG_PM_AUTOSLEEP */
+
+#ifdef CONFIG_PM_WAKELOCKS
+static ssize_t wake_lock_show(struct kobject *kobj,
+			      struct kobj_attribute *attr,
+			      char *buf)
+{
+	return pm_show_wakelocks(buf, true);
+}
+
+static ssize_t wake_lock_store(struct kobject *kobj,
+			       struct kobj_attribute *attr,
+			       const char *buf, size_t n)
+{
+	int error = pm_wake_lock(buf);
+	return error ? error : n;
+}
+
+power_attr(wake_lock);
+
+static ssize_t wake_unlock_show(struct kobject *kobj,
+				struct kobj_attribute *attr,
+				char *buf)
+{
+	return pm_show_wakelocks(buf, false);
+}
+
+static ssize_t wake_unlock_store(struct kobject *kobj,
+				 struct kobj_attribute *attr,
+				 const char *buf, size_t n)
+{
+	int error = pm_wake_unlock(buf);
+	return error ? error : n;
+}
+
+power_attr(wake_unlock);
+
+#endif /* CONFIG_PM_WAKELOCKS */
 #endif /* CONFIG_PM_SLEEP */
 
 #ifdef CONFIG_PM_TRACE
@@ -409,6 +521,13 @@
 #ifdef CONFIG_PM_SLEEP
 	&pm_async_attr.attr,
 	&wakeup_count_attr.attr,
+#ifdef CONFIG_PM_AUTOSLEEP
+	&autosleep_attr.attr,
+#endif
+#ifdef CONFIG_PM_WAKELOCKS
+	&wake_lock_attr.attr,
+	&wake_unlock_attr.attr,
+#endif
 #ifdef CONFIG_PM_DEBUG
 	&pm_test_attr.attr,
 #endif
@@ -444,7 +563,10 @@
 	power_kobj = kobject_create_and_add("power", NULL);
 	if (!power_kobj)
 		return -ENOMEM;
-	return sysfs_create_group(power_kobj, &attr_group);
+	error = sysfs_create_group(power_kobj, &attr_group);
+	if (error)
+		return error;
+	return pm_autosleep_init();
 }
 
 core_initcall(pm_init);
diff --git a/kernel/power/power.h b/kernel/power/power.h
index 98f3622..b0bd4be 100644
--- a/kernel/power/power.h
+++ b/kernel/power/power.h
@@ -264,3 +264,30 @@
 {
 }
 #endif
+
+#ifdef CONFIG_PM_AUTOSLEEP
+
+/* kernel/power/autosleep.c */
+extern int pm_autosleep_init(void);
+extern int pm_autosleep_lock(void);
+extern void pm_autosleep_unlock(void);
+extern suspend_state_t pm_autosleep_state(void);
+extern int pm_autosleep_set_state(suspend_state_t state);
+
+#else /* !CONFIG_PM_AUTOSLEEP */
+
+static inline int pm_autosleep_init(void) { return 0; }
+static inline int pm_autosleep_lock(void) { return 0; }
+static inline void pm_autosleep_unlock(void) {}
+static inline suspend_state_t pm_autosleep_state(void) { return PM_SUSPEND_ON; }
+
+#endif /* !CONFIG_PM_AUTOSLEEP */
+
+#ifdef CONFIG_PM_WAKELOCKS
+
+/* kernel/power/wakelock.c */
+extern ssize_t pm_show_wakelocks(char *buf, bool show_active);
+extern int pm_wake_lock(const char *buf);
+extern int pm_wake_unlock(const char *buf);
+
+#endif /* !CONFIG_PM_WAKELOCKS */
diff --git a/kernel/power/swap.c b/kernel/power/swap.c
index eef311a..11e22c0 100644
--- a/kernel/power/swap.c
+++ b/kernel/power/swap.c
@@ -6,7 +6,7 @@
  *
  * Copyright (C) 1998,2001-2005 Pavel Machek <pavel@ucw.cz>
  * Copyright (C) 2006 Rafael J. Wysocki <rjw@sisk.pl>
- * Copyright (C) 2010 Bojan Smojver <bojan@rexursive.com>
+ * Copyright (C) 2010-2012 Bojan Smojver <bojan@rexursive.com>
  *
  * This file is released under the GPLv2.
  *
@@ -282,14 +282,17 @@
 		return -ENOSPC;
 
 	if (bio_chain) {
-		src = (void *)__get_free_page(__GFP_WAIT | __GFP_HIGH);
+		src = (void *)__get_free_page(__GFP_WAIT | __GFP_NOWARN |
+		                              __GFP_NORETRY);
 		if (src) {
 			copy_page(src, buf);
 		} else {
 			ret = hib_wait_on_bio_chain(bio_chain); /* Free pages */
 			if (ret)
 				return ret;
-			src = (void *)__get_free_page(__GFP_WAIT | __GFP_HIGH);
+			src = (void *)__get_free_page(__GFP_WAIT |
+			                              __GFP_NOWARN |
+			                              __GFP_NORETRY);
 			if (src) {
 				copy_page(src, buf);
 			} else {
@@ -367,12 +370,17 @@
 		clear_page(handle->cur);
 		handle->cur_swap = offset;
 		handle->k = 0;
-	}
-	if (bio_chain && low_free_pages() <= handle->reqd_free_pages) {
-		error = hib_wait_on_bio_chain(bio_chain);
-		if (error)
-			goto out;
-		handle->reqd_free_pages = reqd_free_pages();
+
+		if (bio_chain && low_free_pages() <= handle->reqd_free_pages) {
+			error = hib_wait_on_bio_chain(bio_chain);
+			if (error)
+				goto out;
+			/*
+			 * Recalculate the number of required free pages, to
+			 * make sure we never take more than half.
+			 */
+			handle->reqd_free_pages = reqd_free_pages();
+		}
 	}
  out:
 	return error;
@@ -419,8 +427,9 @@
 /* Maximum number of threads for compression/decompression. */
 #define LZO_THREADS	3
 
-/* Maximum number of pages for read buffering. */
-#define LZO_READ_PAGES	(MAP_PAGE_ENTRIES * 8)
+/* Minimum/maximum number of pages for read buffering. */
+#define LZO_MIN_RD_PAGES	1024
+#define LZO_MAX_RD_PAGES	8192
 
 
 /**
@@ -631,12 +640,6 @@
 	}
 
 	/*
-	 * Adjust number of free pages after all allocations have been done.
-	 * We don't want to run out of pages when writing.
-	 */
-	handle->reqd_free_pages = reqd_free_pages();
-
-	/*
 	 * Start the CRC32 thread.
 	 */
 	init_waitqueue_head(&crc->go);
@@ -657,6 +660,12 @@
 		goto out_clean;
 	}
 
+	/*
+	 * Adjust the number of required free pages after all allocations have
+	 * been done. We don't want to run out of pages when writing.
+	 */
+	handle->reqd_free_pages = reqd_free_pages();
+
 	printk(KERN_INFO
 		"PM: Using %u thread(s) for compression.\n"
 		"PM: Compressing and saving image data (%u pages) ...     ",
@@ -1067,7 +1076,7 @@
 	unsigned i, thr, run_threads, nr_threads;
 	unsigned ring = 0, pg = 0, ring_size = 0,
 	         have = 0, want, need, asked = 0;
-	unsigned long read_pages;
+	unsigned long read_pages = 0;
 	unsigned char **page = NULL;
 	struct dec_data *data = NULL;
 	struct crc_data *crc = NULL;
@@ -1079,7 +1088,7 @@
 	nr_threads = num_online_cpus() - 1;
 	nr_threads = clamp_val(nr_threads, 1, LZO_THREADS);
 
-	page = vmalloc(sizeof(*page) * LZO_READ_PAGES);
+	page = vmalloc(sizeof(*page) * LZO_MAX_RD_PAGES);
 	if (!page) {
 		printk(KERN_ERR "PM: Failed to allocate LZO page\n");
 		ret = -ENOMEM;
@@ -1144,15 +1153,22 @@
 	}
 
 	/*
-	 * Adjust number of pages for read buffering, in case we are short.
+	 * Set the number of pages for read buffering.
+	 * This is complete guesswork, because we'll only know the real
+	 * picture once prepare_image() is called, which is much later on
+	 * during the image load phase. We'll assume the worst case and
+	 * say that none of the image pages are from high memory.
 	 */
-	read_pages = (nr_free_pages() - snapshot_get_image_size()) >> 1;
-	read_pages = clamp_val(read_pages, LZO_CMP_PAGES, LZO_READ_PAGES);
+	if (low_free_pages() > snapshot_get_image_size())
+		read_pages = (low_free_pages() - snapshot_get_image_size()) / 2;
+	read_pages = clamp_val(read_pages, LZO_MIN_RD_PAGES, LZO_MAX_RD_PAGES);
 
 	for (i = 0; i < read_pages; i++) {
 		page[i] = (void *)__get_free_page(i < LZO_CMP_PAGES ?
 		                                  __GFP_WAIT | __GFP_HIGH :
-		                                  __GFP_WAIT);
+		                                  __GFP_WAIT | __GFP_NOWARN |
+		                                  __GFP_NORETRY);
+
 		if (!page[i]) {
 			if (i < LZO_CMP_PAGES) {
 				ring_size = i;
diff --git a/kernel/power/wakelock.c b/kernel/power/wakelock.c
new file mode 100644
index 0000000..c8fba33
--- /dev/null
+++ b/kernel/power/wakelock.c
@@ -0,0 +1,259 @@
+/*
+ * kernel/power/wakelock.c
+ *
+ * User space wakeup sources support.
+ *
+ * Copyright (C) 2012 Rafael J. Wysocki <rjw@sisk.pl>
+ *
+ * This code is based on the analogous interface allowing user space to
+ * manipulate wakelocks on Android.
+ */
+
+#include <linux/ctype.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/hrtimer.h>
+#include <linux/list.h>
+#include <linux/rbtree.h>
+#include <linux/slab.h>
+
+static DEFINE_MUTEX(wakelocks_lock);
+
+struct wakelock {
+	char			*name;
+	struct rb_node		node;
+	struct wakeup_source	ws;
+#ifdef CONFIG_PM_WAKELOCKS_GC
+	struct list_head	lru;
+#endif
+};
+
+static struct rb_root wakelocks_tree = RB_ROOT;
+
+ssize_t pm_show_wakelocks(char *buf, bool show_active)
+{
+	struct rb_node *node;
+	struct wakelock *wl;
+	char *str = buf;
+	char *end = buf + PAGE_SIZE;
+
+	mutex_lock(&wakelocks_lock);
+
+	for (node = rb_first(&wakelocks_tree); node; node = rb_next(node)) {
+		wl = rb_entry(node, struct wakelock, node);
+		if (wl->ws.active == show_active)
+			str += scnprintf(str, end - str, "%s ", wl->name);
+	}
+	if (str > buf)
+		str--;
+
+	str += scnprintf(str, end - str, "\n");
+
+	mutex_unlock(&wakelocks_lock);
+	return (str - buf);
+}
+
+#if CONFIG_PM_WAKELOCKS_LIMIT > 0
+static unsigned int number_of_wakelocks;
+
+static inline bool wakelocks_limit_exceeded(void)
+{
+	return number_of_wakelocks > CONFIG_PM_WAKELOCKS_LIMIT;
+}
+
+static inline void increment_wakelocks_number(void)
+{
+	number_of_wakelocks++;
+}
+
+static inline void decrement_wakelocks_number(void)
+{
+	number_of_wakelocks--;
+}
+#else /* CONFIG_PM_WAKELOCKS_LIMIT = 0 */
+static inline bool wakelocks_limit_exceeded(void) { return false; }
+static inline void increment_wakelocks_number(void) {}
+static inline void decrement_wakelocks_number(void) {}
+#endif /* CONFIG_PM_WAKELOCKS_LIMIT */
+
+#ifdef CONFIG_PM_WAKELOCKS_GC
+#define WL_GC_COUNT_MAX	100
+#define WL_GC_TIME_SEC	300
+
+static LIST_HEAD(wakelocks_lru_list);
+static unsigned int wakelocks_gc_count;
+
+static inline void wakelocks_lru_add(struct wakelock *wl)
+{
+	list_add(&wl->lru, &wakelocks_lru_list);
+}
+
+static inline void wakelocks_lru_most_recent(struct wakelock *wl)
+{
+	list_move(&wl->lru, &wakelocks_lru_list);
+}
+
+static void wakelocks_gc(void)
+{
+	struct wakelock *wl, *aux;
+	ktime_t now;
+
+	if (++wakelocks_gc_count <= WL_GC_COUNT_MAX)
+		return;
+
+	now = ktime_get();
+	list_for_each_entry_safe_reverse(wl, aux, &wakelocks_lru_list, lru) {
+		u64 idle_time_ns;
+		bool active;
+
+		spin_lock_irq(&wl->ws.lock);
+		idle_time_ns = ktime_to_ns(ktime_sub(now, wl->ws.last_time));
+		active = wl->ws.active;
+		spin_unlock_irq(&wl->ws.lock);
+
+		if (idle_time_ns < ((u64)WL_GC_TIME_SEC * NSEC_PER_SEC))
+			break;
+
+		if (!active) {
+			wakeup_source_remove(&wl->ws);
+			rb_erase(&wl->node, &wakelocks_tree);
+			list_del(&wl->lru);
+			kfree(wl->name);
+			kfree(wl);
+			decrement_wakelocks_number();
+		}
+	}
+	wakelocks_gc_count = 0;
+}
+#else /* !CONFIG_PM_WAKELOCKS_GC */
+static inline void wakelocks_lru_add(struct wakelock *wl) {}
+static inline void wakelocks_lru_most_recent(struct wakelock *wl) {}
+static inline void wakelocks_gc(void) {}
+#endif /* !CONFIG_PM_WAKELOCKS_GC */
+
+static struct wakelock *wakelock_lookup_add(const char *name, size_t len,
+					    bool add_if_not_found)
+{
+	struct rb_node **node = &wakelocks_tree.rb_node;
+	struct rb_node *parent = *node;
+	struct wakelock *wl;
+
+	while (*node) {
+		int diff;
+
+		parent = *node;
+		wl = rb_entry(*node, struct wakelock, node);
+		diff = strncmp(name, wl->name, len);
+		if (diff == 0) {
+			if (wl->name[len])
+				diff = -1;
+			else
+				return wl;
+		}
+		if (diff < 0)
+			node = &(*node)->rb_left;
+		else
+			node = &(*node)->rb_right;
+	}
+	if (!add_if_not_found)
+		return ERR_PTR(-EINVAL);
+
+	if (wakelocks_limit_exceeded())
+		return ERR_PTR(-ENOSPC);
+
+	/* Not found, we have to add a new one. */
+	wl = kzalloc(sizeof(*wl), GFP_KERNEL);
+	if (!wl)
+		return ERR_PTR(-ENOMEM);
+
+	wl->name = kstrndup(name, len, GFP_KERNEL);
+	if (!wl->name) {
+		kfree(wl);
+		return ERR_PTR(-ENOMEM);
+	}
+	wl->ws.name = wl->name;
+	wakeup_source_add(&wl->ws);
+	rb_link_node(&wl->node, parent, node);
+	rb_insert_color(&wl->node, &wakelocks_tree);
+	wakelocks_lru_add(wl);
+	increment_wakelocks_number();
+	return wl;
+}
+
+int pm_wake_lock(const char *buf)
+{
+	const char *str = buf;
+	struct wakelock *wl;
+	u64 timeout_ns = 0;
+	size_t len;
+	int ret = 0;
+
+	while (*str && !isspace(*str))
+		str++;
+
+	len = str - buf;
+	if (!len)
+		return -EINVAL;
+
+	if (*str && *str != '\n') {
+		/* Find out if there's a valid timeout string appended. */
+		ret = kstrtou64(skip_spaces(str), 10, &timeout_ns);
+		if (ret)
+			return -EINVAL;
+	}
+
+	mutex_lock(&wakelocks_lock);
+
+	wl = wakelock_lookup_add(buf, len, true);
+	if (IS_ERR(wl)) {
+		ret = PTR_ERR(wl);
+		goto out;
+	}
+	if (timeout_ns) {
+		u64 timeout_ms = timeout_ns + NSEC_PER_MSEC - 1;
+
+		do_div(timeout_ms, NSEC_PER_MSEC);
+		__pm_wakeup_event(&wl->ws, timeout_ms);
+	} else {
+		__pm_stay_awake(&wl->ws);
+	}
+
+	wakelocks_lru_most_recent(wl);
+
+ out:
+	mutex_unlock(&wakelocks_lock);
+	return ret;
+}
+
+int pm_wake_unlock(const char *buf)
+{
+	struct wakelock *wl;
+	size_t len;
+	int ret = 0;
+
+	len = strlen(buf);
+	if (!len)
+		return -EINVAL;
+
+	if (buf[len-1] == '\n')
+		len--;
+
+	if (!len)
+		return -EINVAL;
+
+	mutex_lock(&wakelocks_lock);
+
+	wl = wakelock_lookup_add(buf, len, false);
+	if (IS_ERR(wl)) {
+		ret = PTR_ERR(wl);
+		goto out;
+	}
+	__pm_relax(&wl->ws);
+
+	wakelocks_lru_most_recent(wl);
+	wakelocks_gc();
+
+ out:
+	mutex_unlock(&wakelocks_lock);
+	return ret;
+}
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index d833cc9..a5a9d39 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -1912,7 +1912,7 @@
 		    struct task_struct *next)
 {
 	sched_info_switch(prev, next);
-	perf_event_task_sched(prev, next);
+	perf_event_task_sched_out(prev, next);
 	fire_sched_out_preempt_notifiers(prev, next);
 	prepare_lock_switch(rq, next);
 	prepare_arch_switch(next);
@@ -1955,6 +1955,13 @@
 	 */
 	prev_state = prev->state;
 	finish_arch_switch(prev);
+#ifdef __ARCH_WANT_INTERRUPTS_ON_CTXSW
+	local_irq_disable();
+#endif /* __ARCH_WANT_INTERRUPTS_ON_CTXSW */
+	perf_event_task_sched_in(prev, current);
+#ifdef __ARCH_WANT_INTERRUPTS_ON_CTXSW
+	local_irq_enable();
+#endif /* __ARCH_WANT_INTERRUPTS_ON_CTXSW */
 	finish_lock_switch(rq, prev);
 	finish_arch_post_lock_switch();
 
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index e119341..a42d3ae 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -74,6 +74,15 @@
 	  that look like '.Lxxx') so they don't pollute the output of
 	  get_wchan() and suchlike.
 
+config READABLE_ASM
+        bool "Generate readable assembler code"
+        depends on DEBUG_KERNEL
+        help
+          Disable some compiler optimizations that tend to generate human unreadable
+          assembler output. This may make the kernel slightly slower, but it helps
+          to keep kernel developers who have to stare a lot at assembler listings
+          sane.
+
 config UNUSED_SYMBOLS
 	bool "Enable unused/obsolete exported symbols"
 	default y if X86
diff --git a/lib/raid6/Makefile b/lib/raid6/Makefile
index 8a38102..de06dfe 100644
--- a/lib/raid6/Makefile
+++ b/lib/raid6/Makefile
@@ -1,6 +1,6 @@
 obj-$(CONFIG_RAID6_PQ)	+= raid6_pq.o
 
-raid6_pq-y	+= algos.o recov.o tables.o int1.o int2.o int4.o \
+raid6_pq-y	+= algos.o recov.o recov_ssse3.o tables.o int1.o int2.o int4.o \
 		   int8.o int16.o int32.o altivec1.o altivec2.o altivec4.o \
 		   altivec8.o mmx.o sse1.o sse2.o
 hostprogs-y	+= mktables
diff --git a/lib/raid6/algos.c b/lib/raid6/algos.c
index 8b02f60..589f5f5 100644
--- a/lib/raid6/algos.c
+++ b/lib/raid6/algos.c
@@ -17,11 +17,11 @@
  */
 
 #include <linux/raid/pq.h>
-#include <linux/module.h>
 #ifndef __KERNEL__
 #include <sys/mman.h>
 #include <stdio.h>
 #else
+#include <linux/module.h>
 #include <linux/gfp.h>
 #if !RAID6_USE_EMPTY_ZERO_PAGE
 /* In .bss so it's zeroed */
@@ -34,10 +34,6 @@
 EXPORT_SYMBOL_GPL(raid6_call);
 
 const struct raid6_calls * const raid6_algos[] = {
-	&raid6_intx1,
-	&raid6_intx2,
-	&raid6_intx4,
-	&raid6_intx8,
 #if defined(__ia64__)
 	&raid6_intx16,
 	&raid6_intx32,
@@ -61,6 +57,24 @@
 	&raid6_altivec4,
 	&raid6_altivec8,
 #endif
+	&raid6_intx1,
+	&raid6_intx2,
+	&raid6_intx4,
+	&raid6_intx8,
+	NULL
+};
+
+void (*raid6_2data_recov)(int, size_t, int, int, void **);
+EXPORT_SYMBOL_GPL(raid6_2data_recov);
+
+void (*raid6_datap_recov)(int, size_t, int, void **);
+EXPORT_SYMBOL_GPL(raid6_datap_recov);
+
+const struct raid6_recov_calls *const raid6_recov_algos[] = {
+#if (defined(__i386__) || defined(__x86_64__)) && !defined(__arch_um__)
+	&raid6_recov_ssse3,
+#endif
+	&raid6_recov_intx1,
 	NULL
 };
 
@@ -72,59 +86,55 @@
 #define time_before(x, y) ((x) < (y))
 #endif
 
-/* Try to pick the best algorithm */
-/* This code uses the gfmul table as convenient data set to abuse */
-
-int __init raid6_select_algo(void)
+static inline const struct raid6_recov_calls *raid6_choose_recov(void)
 {
-	const struct raid6_calls * const * algo;
-	const struct raid6_calls * best;
-	char *syndromes;
-	void *dptrs[(65536/PAGE_SIZE)+2];
-	int i, disks;
-	unsigned long perf, bestperf;
-	int bestprefer;
-	unsigned long j0, j1;
+	const struct raid6_recov_calls *const *algo;
+	const struct raid6_recov_calls *best;
 
-	disks = (65536/PAGE_SIZE)+2;
-	for ( i = 0 ; i < disks-2 ; i++ ) {
-		dptrs[i] = ((char *)raid6_gfmul) + PAGE_SIZE*i;
-	}
+	for (best = NULL, algo = raid6_recov_algos; *algo; algo++)
+		if (!best || (*algo)->priority > best->priority)
+			if (!(*algo)->valid || (*algo)->valid())
+				best = *algo;
 
-	/* Normal code - use a 2-page allocation to avoid D$ conflict */
-	syndromes = (void *) __get_free_pages(GFP_KERNEL, 1);
+	if (best) {
+		raid6_2data_recov = best->data2;
+		raid6_datap_recov = best->datap;
 
-	if ( !syndromes ) {
-		printk("raid6: Yikes!  No memory available.\n");
-		return -ENOMEM;
-	}
+		printk("raid6: using %s recovery algorithm\n", best->name);
+	} else
+		printk("raid6: Yikes! No recovery algorithm found!\n");
 
-	dptrs[disks-2] = syndromes;
-	dptrs[disks-1] = syndromes + PAGE_SIZE;
+	return best;
+}
 
-	bestperf = 0;  bestprefer = 0;  best = NULL;
+static inline const struct raid6_calls *raid6_choose_gen(
+	void *(*const dptrs)[(65536/PAGE_SIZE)+2], const int disks)
+{
+	unsigned long perf, bestperf, j0, j1;
+	const struct raid6_calls *const *algo;
+	const struct raid6_calls *best;
 
-	for ( algo = raid6_algos ; *algo ; algo++ ) {
-		if ( !(*algo)->valid || (*algo)->valid() ) {
+	for (bestperf = 0, best = NULL, algo = raid6_algos; *algo; algo++) {
+		if (!best || (*algo)->prefer >= best->prefer) {
+			if ((*algo)->valid && !(*algo)->valid())
+				continue;
+
 			perf = 0;
 
 			preempt_disable();
 			j0 = jiffies;
-			while ( (j1 = jiffies) == j0 )
+			while ((j1 = jiffies) == j0)
 				cpu_relax();
 			while (time_before(jiffies,
 					    j1 + (1<<RAID6_TIME_JIFFIES_LG2))) {
-				(*algo)->gen_syndrome(disks, PAGE_SIZE, dptrs);
+				(*algo)->gen_syndrome(disks, PAGE_SIZE, *dptrs);
 				perf++;
 			}
 			preempt_enable();
 
-			if ( (*algo)->prefer > bestprefer ||
-			     ((*algo)->prefer == bestprefer &&
-			      perf > bestperf) ) {
-				best = *algo;
-				bestprefer = best->prefer;
+			if (perf > bestperf) {
 				bestperf = perf;
+				best = *algo;
 			}
 			printk("raid6: %-8s %5ld MB/s\n", (*algo)->name,
 			       (perf*HZ) >> (20-16+RAID6_TIME_JIFFIES_LG2));
@@ -139,9 +149,46 @@
 	} else
 		printk("raid6: Yikes!  No algorithm found!\n");
 
+	return best;
+}
+
+
+/* Try to pick the best algorithm */
+/* This code uses the gfmul table as convenient data set to abuse */
+
+int __init raid6_select_algo(void)
+{
+	const int disks = (65536/PAGE_SIZE)+2;
+
+	const struct raid6_calls *gen_best;
+	const struct raid6_recov_calls *rec_best;
+	char *syndromes;
+	void *dptrs[(65536/PAGE_SIZE)+2];
+	int i;
+
+	for (i = 0; i < disks-2; i++)
+		dptrs[i] = ((char *)raid6_gfmul) + PAGE_SIZE*i;
+
+	/* Normal code - use a 2-page allocation to avoid D$ conflict */
+	syndromes = (void *) __get_free_pages(GFP_KERNEL, 1);
+
+	if (!syndromes) {
+		printk("raid6: Yikes!  No memory available.\n");
+		return -ENOMEM;
+	}
+
+	dptrs[disks-2] = syndromes;
+	dptrs[disks-1] = syndromes + PAGE_SIZE;
+
+	/* select raid gen_syndrome function */
+	gen_best = raid6_choose_gen(&dptrs, disks);
+
+	/* select raid recover functions */
+	rec_best = raid6_choose_recov();
+
 	free_pages((unsigned long)syndromes, 1);
 
-	return best ? 0 : -EINVAL;
+	return gen_best && rec_best ? 0 : -EINVAL;
 }
 
 static void raid6_exit(void)
diff --git a/lib/raid6/mktables.c b/lib/raid6/mktables.c
index 8a37809..39787db 100644
--- a/lib/raid6/mktables.c
+++ b/lib/raid6/mktables.c
@@ -81,6 +81,31 @@
 	printf("EXPORT_SYMBOL(raid6_gfmul);\n");
 	printf("#endif\n");
 
+	/* Compute vector multiplication table */
+	printf("\nconst u8  __attribute__((aligned(256)))\n"
+		"raid6_vgfmul[256][32] =\n"
+		"{\n");
+	for (i = 0; i < 256; i++) {
+		printf("\t{\n");
+		for (j = 0; j < 16; j += 8) {
+			printf("\t\t");
+			for (k = 0; k < 8; k++)
+				printf("0x%02x,%c", gfmul(i, j + k),
+				       (k == 7) ? '\n' : ' ');
+		}
+		for (j = 0; j < 16; j += 8) {
+			printf("\t\t");
+			for (k = 0; k < 8; k++)
+				printf("0x%02x,%c", gfmul(i, (j + k) << 4),
+				       (k == 7) ? '\n' : ' ');
+		}
+		printf("\t},\n");
+	}
+	printf("};\n");
+	printf("#ifdef __KERNEL__\n");
+	printf("EXPORT_SYMBOL(raid6_vgfmul);\n");
+	printf("#endif\n");
+
 	/* Compute power-of-2 table (exponent) */
 	v = 1;
 	printf("\nconst u8 __attribute__((aligned(256)))\n"
diff --git a/lib/raid6/recov.c b/lib/raid6/recov.c
index fe275d7..1805a5c 100644
--- a/lib/raid6/recov.c
+++ b/lib/raid6/recov.c
@@ -22,7 +22,7 @@
 #include <linux/raid/pq.h>
 
 /* Recover two failed data blocks. */
-void raid6_2data_recov(int disks, size_t bytes, int faila, int failb,
+void raid6_2data_recov_intx1(int disks, size_t bytes, int faila, int failb,
 		       void **ptrs)
 {
 	u8 *p, *q, *dp, *dq;
@@ -64,10 +64,9 @@
 		p++; q++;
 	}
 }
-EXPORT_SYMBOL_GPL(raid6_2data_recov);
 
 /* Recover failure of one data block plus the P block */
-void raid6_datap_recov(int disks, size_t bytes, int faila, void **ptrs)
+void raid6_datap_recov_intx1(int disks, size_t bytes, int faila, void **ptrs)
 {
 	u8 *p, *q, *dq;
 	const u8 *qmul;		/* Q multiplier table */
@@ -96,7 +95,15 @@
 		q++; dq++;
 	}
 }
-EXPORT_SYMBOL_GPL(raid6_datap_recov);
+
+
+const struct raid6_recov_calls raid6_recov_intx1 = {
+	.data2 = raid6_2data_recov_intx1,
+	.datap = raid6_datap_recov_intx1,
+	.valid = NULL,
+	.name = "intx1",
+	.priority = 0,
+};
 
 #ifndef __KERNEL__
 /* Testing only */
diff --git a/lib/raid6/recov_ssse3.c b/lib/raid6/recov_ssse3.c
new file mode 100644
index 0000000..37ae619
--- /dev/null
+++ b/lib/raid6/recov_ssse3.c
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2012 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ */
+
+#if (defined(__i386__) || defined(__x86_64__)) && !defined(__arch_um__)
+
+#include <linux/raid/pq.h>
+#include "x86.h"
+
+static int raid6_has_ssse3(void)
+{
+	return boot_cpu_has(X86_FEATURE_XMM) &&
+		boot_cpu_has(X86_FEATURE_XMM2) &&
+		boot_cpu_has(X86_FEATURE_SSSE3);
+}
+
+void raid6_2data_recov_ssse3(int disks, size_t bytes, int faila, int failb,
+		       void **ptrs)
+{
+	u8 *p, *q, *dp, *dq;
+	const u8 *pbmul;	/* P multiplier table for B data */
+	const u8 *qmul;		/* Q multiplier table (for both) */
+	static const u8 __aligned(16) x0f[16] = {
+		 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+		 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f};
+
+	p = (u8 *)ptrs[disks-2];
+	q = (u8 *)ptrs[disks-1];
+
+	/* Compute syndrome with zero for the missing data pages
+	   Use the dead data pages as temporary storage for
+	   delta p and delta q */
+	dp = (u8 *)ptrs[faila];
+	ptrs[faila] = (void *)raid6_empty_zero_page;
+	ptrs[disks-2] = dp;
+	dq = (u8 *)ptrs[failb];
+	ptrs[failb] = (void *)raid6_empty_zero_page;
+	ptrs[disks-1] = dq;
+
+	raid6_call.gen_syndrome(disks, bytes, ptrs);
+
+	/* Restore pointer table */
+	ptrs[faila]   = dp;
+	ptrs[failb]   = dq;
+	ptrs[disks-2] = p;
+	ptrs[disks-1] = q;
+
+	/* Now, pick the proper data tables */
+	pbmul = raid6_vgfmul[raid6_gfexi[failb-faila]];
+	qmul  = raid6_vgfmul[raid6_gfinv[raid6_gfexp[faila] ^
+		raid6_gfexp[failb]]];
+
+	kernel_fpu_begin();
+
+	asm volatile("movdqa %0,%%xmm7" : : "m" (x0f[0]));
+
+#ifdef CONFIG_X86_64
+	asm volatile("movdqa %0,%%xmm6" : : "m" (qmul[0]));
+	asm volatile("movdqa %0,%%xmm14" : : "m" (pbmul[0]));
+	asm volatile("movdqa %0,%%xmm15" : : "m" (pbmul[16]));
+#endif
+
+	/* Now do it... */
+	while (bytes) {
+#ifdef CONFIG_X86_64
+		/* xmm6, xmm14, xmm15 */
+
+		asm volatile("movdqa %0,%%xmm1" : : "m" (q[0]));
+		asm volatile("movdqa %0,%%xmm9" : : "m" (q[16]));
+		asm volatile("movdqa %0,%%xmm0" : : "m" (p[0]));
+		asm volatile("movdqa %0,%%xmm8" : : "m" (p[16]));
+		asm volatile("pxor   %0,%%xmm1" : : "m" (dq[0]));
+		asm volatile("pxor   %0,%%xmm9" : : "m" (dq[16]));
+		asm volatile("pxor   %0,%%xmm0" : : "m" (dp[0]));
+		asm volatile("pxor   %0,%%xmm8" : : "m" (dp[16]));
+
+		/* xmm0/8 = px */
+
+		asm volatile("movdqa %xmm6,%xmm4");
+		asm volatile("movdqa %0,%%xmm5" : : "m" (qmul[16]));
+		asm volatile("movdqa %xmm6,%xmm12");
+		asm volatile("movdqa %xmm5,%xmm13");
+		asm volatile("movdqa %xmm1,%xmm3");
+		asm volatile("movdqa %xmm9,%xmm11");
+		asm volatile("movdqa %xmm0,%xmm2"); /* xmm2/10 = px */
+		asm volatile("movdqa %xmm8,%xmm10");
+		asm volatile("psraw  $4,%xmm1");
+		asm volatile("psraw  $4,%xmm9");
+		asm volatile("pand   %xmm7,%xmm3");
+		asm volatile("pand   %xmm7,%xmm11");
+		asm volatile("pand   %xmm7,%xmm1");
+		asm volatile("pand   %xmm7,%xmm9");
+		asm volatile("pshufb %xmm3,%xmm4");
+		asm volatile("pshufb %xmm11,%xmm12");
+		asm volatile("pshufb %xmm1,%xmm5");
+		asm volatile("pshufb %xmm9,%xmm13");
+		asm volatile("pxor   %xmm4,%xmm5");
+		asm volatile("pxor   %xmm12,%xmm13");
+
+		/* xmm5/13 = qx */
+
+		asm volatile("movdqa %xmm14,%xmm4");
+		asm volatile("movdqa %xmm15,%xmm1");
+		asm volatile("movdqa %xmm14,%xmm12");
+		asm volatile("movdqa %xmm15,%xmm9");
+		asm volatile("movdqa %xmm2,%xmm3");
+		asm volatile("movdqa %xmm10,%xmm11");
+		asm volatile("psraw  $4,%xmm2");
+		asm volatile("psraw  $4,%xmm10");
+		asm volatile("pand   %xmm7,%xmm3");
+		asm volatile("pand   %xmm7,%xmm11");
+		asm volatile("pand   %xmm7,%xmm2");
+		asm volatile("pand   %xmm7,%xmm10");
+		asm volatile("pshufb %xmm3,%xmm4");
+		asm volatile("pshufb %xmm11,%xmm12");
+		asm volatile("pshufb %xmm2,%xmm1");
+		asm volatile("pshufb %xmm10,%xmm9");
+		asm volatile("pxor   %xmm4,%xmm1");
+		asm volatile("pxor   %xmm12,%xmm9");
+
+		/* xmm1/9 = pbmul[px] */
+		asm volatile("pxor   %xmm5,%xmm1");
+		asm volatile("pxor   %xmm13,%xmm9");
+		/* xmm1/9 = db = DQ */
+		asm volatile("movdqa %%xmm1,%0" : "=m" (dq[0]));
+		asm volatile("movdqa %%xmm9,%0" : "=m" (dq[16]));
+
+		asm volatile("pxor   %xmm1,%xmm0");
+		asm volatile("pxor   %xmm9,%xmm8");
+		asm volatile("movdqa %%xmm0,%0" : "=m" (dp[0]));
+		asm volatile("movdqa %%xmm8,%0" : "=m" (dp[16]));
+
+		bytes -= 32;
+		p += 32;
+		q += 32;
+		dp += 32;
+		dq += 32;
+#else
+		asm volatile("movdqa %0,%%xmm1" : : "m" (*q));
+		asm volatile("movdqa %0,%%xmm0" : : "m" (*p));
+		asm volatile("pxor   %0,%%xmm1" : : "m" (*dq));
+		asm volatile("pxor   %0,%%xmm0" : : "m" (*dp));
+
+		/* 1 = dq ^ q
+		 * 0 = dp ^ p
+		 */
+		asm volatile("movdqa %0,%%xmm4" : : "m" (qmul[0]));
+		asm volatile("movdqa %0,%%xmm5" : : "m" (qmul[16]));
+
+		asm volatile("movdqa %xmm1,%xmm3");
+		asm volatile("psraw  $4,%xmm1");
+		asm volatile("pand   %xmm7,%xmm3");
+		asm volatile("pand   %xmm7,%xmm1");
+		asm volatile("pshufb %xmm3,%xmm4");
+		asm volatile("pshufb %xmm1,%xmm5");
+		asm volatile("pxor   %xmm4,%xmm5");
+
+		asm volatile("movdqa %xmm0,%xmm2"); /* xmm2 = px */
+
+		/* xmm5 = qx */
+
+		asm volatile("movdqa %0,%%xmm4" : : "m" (pbmul[0]));
+		asm volatile("movdqa %0,%%xmm1" : : "m" (pbmul[16]));
+		asm volatile("movdqa %xmm2,%xmm3");
+		asm volatile("psraw  $4,%xmm2");
+		asm volatile("pand   %xmm7,%xmm3");
+		asm volatile("pand   %xmm7,%xmm2");
+		asm volatile("pshufb %xmm3,%xmm4");
+		asm volatile("pshufb %xmm2,%xmm1");
+		asm volatile("pxor   %xmm4,%xmm1");
+
+		/* xmm1 = pbmul[px] */
+		asm volatile("pxor   %xmm5,%xmm1");
+		/* xmm1 = db = DQ */
+		asm volatile("movdqa %%xmm1,%0" : "=m" (*dq));
+
+		asm volatile("pxor   %xmm1,%xmm0");
+		asm volatile("movdqa %%xmm0,%0" : "=m" (*dp));
+
+		bytes -= 16;
+		p += 16;
+		q += 16;
+		dp += 16;
+		dq += 16;
+#endif
+	}
+
+	kernel_fpu_end();
+}
+
+
+void raid6_datap_recov_ssse3(int disks, size_t bytes, int faila, void **ptrs)
+{
+	u8 *p, *q, *dq;
+	const u8 *qmul;		/* Q multiplier table */
+	static const u8 __aligned(16) x0f[16] = {
+		 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+		 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f};
+
+	p = (u8 *)ptrs[disks-2];
+	q = (u8 *)ptrs[disks-1];
+
+	/* Compute syndrome with zero for the missing data page
+	   Use the dead data page as temporary storage for delta q */
+	dq = (u8 *)ptrs[faila];
+	ptrs[faila] = (void *)raid6_empty_zero_page;
+	ptrs[disks-1] = dq;
+
+	raid6_call.gen_syndrome(disks, bytes, ptrs);
+
+	/* Restore pointer table */
+	ptrs[faila]   = dq;
+	ptrs[disks-1] = q;
+
+	/* Now, pick the proper data tables */
+	qmul  = raid6_vgfmul[raid6_gfinv[raid6_gfexp[faila]]];
+
+	kernel_fpu_begin();
+
+	asm volatile("movdqa %0, %%xmm7" : : "m" (x0f[0]));
+
+	while (bytes) {
+#ifdef CONFIG_X86_64
+		asm volatile("movdqa %0, %%xmm3" : : "m" (dq[0]));
+		asm volatile("movdqa %0, %%xmm4" : : "m" (dq[16]));
+		asm volatile("pxor %0, %%xmm3" : : "m" (q[0]));
+		asm volatile("movdqa %0, %%xmm0" : : "m" (qmul[0]));
+
+		/* xmm3 = q[0] ^ dq[0] */
+
+		asm volatile("pxor %0, %%xmm4" : : "m" (q[16]));
+		asm volatile("movdqa %0, %%xmm1" : : "m" (qmul[16]));
+
+		/* xmm4 = q[16] ^ dq[16] */
+
+		asm volatile("movdqa %xmm3, %xmm6");
+		asm volatile("movdqa %xmm4, %xmm8");
+
+		/* xmm4 = xmm8 = q[16] ^ dq[16] */
+
+		asm volatile("psraw $4, %xmm3");
+		asm volatile("pand %xmm7, %xmm6");
+		asm volatile("pand %xmm7, %xmm3");
+		asm volatile("pshufb %xmm6, %xmm0");
+		asm volatile("pshufb %xmm3, %xmm1");
+		asm volatile("movdqa %0, %%xmm10" : : "m" (qmul[0]));
+		asm volatile("pxor %xmm0, %xmm1");
+		asm volatile("movdqa %0, %%xmm11" : : "m" (qmul[16]));
+
+		/* xmm1 = qmul[q[0] ^ dq[0]] */
+
+		asm volatile("psraw $4, %xmm4");
+		asm volatile("pand %xmm7, %xmm8");
+		asm volatile("pand %xmm7, %xmm4");
+		asm volatile("pshufb %xmm8, %xmm10");
+		asm volatile("pshufb %xmm4, %xmm11");
+		asm volatile("movdqa %0, %%xmm2" : : "m" (p[0]));
+		asm volatile("pxor %xmm10, %xmm11");
+		asm volatile("movdqa %0, %%xmm12" : : "m" (p[16]));
+
+		/* xmm11 = qmul[q[16] ^ dq[16]] */
+
+		asm volatile("pxor %xmm1, %xmm2");
+
+		/* xmm2 = p[0] ^ qmul[q[0] ^ dq[0]] */
+
+		asm volatile("pxor %xmm11, %xmm12");
+
+		/* xmm12 = p[16] ^ qmul[q[16] ^ dq[16]] */
+
+		asm volatile("movdqa %%xmm1, %0" : "=m" (dq[0]));
+		asm volatile("movdqa %%xmm11, %0" : "=m" (dq[16]));
+
+		asm volatile("movdqa %%xmm2, %0" : "=m" (p[0]));
+		asm volatile("movdqa %%xmm12, %0" : "=m" (p[16]));
+
+		bytes -= 32;
+		p += 32;
+		q += 32;
+		dq += 32;
+
+#else
+		asm volatile("movdqa %0, %%xmm3" : : "m" (dq[0]));
+		asm volatile("movdqa %0, %%xmm0" : : "m" (qmul[0]));
+		asm volatile("pxor %0, %%xmm3" : : "m" (q[0]));
+		asm volatile("movdqa %0, %%xmm1" : : "m" (qmul[16]));
+
+		/* xmm3 = *q ^ *dq */
+
+		asm volatile("movdqa %xmm3, %xmm6");
+		asm volatile("movdqa %0, %%xmm2" : : "m" (p[0]));
+		asm volatile("psraw $4, %xmm3");
+		asm volatile("pand %xmm7, %xmm6");
+		asm volatile("pand %xmm7, %xmm3");
+		asm volatile("pshufb %xmm6, %xmm0");
+		asm volatile("pshufb %xmm3, %xmm1");
+		asm volatile("pxor %xmm0, %xmm1");
+
+		/* xmm1 = qmul[*q ^ *dq */
+
+		asm volatile("pxor %xmm1, %xmm2");
+
+		/* xmm2 = *p ^ qmul[*q ^ *dq] */
+
+		asm volatile("movdqa %%xmm1, %0" : "=m" (dq[0]));
+		asm volatile("movdqa %%xmm2, %0" : "=m" (p[0]));
+
+		bytes -= 16;
+		p += 16;
+		q += 16;
+		dq += 16;
+#endif
+	}
+
+	kernel_fpu_end();
+}
+
+const struct raid6_recov_calls raid6_recov_ssse3 = {
+	.data2 = raid6_2data_recov_ssse3,
+	.datap = raid6_datap_recov_ssse3,
+	.valid = raid6_has_ssse3,
+#ifdef CONFIG_X86_64
+	.name = "ssse3x2",
+#else
+	.name = "ssse3x1",
+#endif
+	.priority = 1,
+};
+
+#endif
diff --git a/lib/raid6/test/Makefile b/lib/raid6/test/Makefile
index aa65169..c76151d 100644
--- a/lib/raid6/test/Makefile
+++ b/lib/raid6/test/Makefile
@@ -23,7 +23,7 @@
 all:	raid6.a raid6test
 
 raid6.a: int1.o int2.o int4.o int8.o int16.o int32.o mmx.o sse1.o sse2.o \
-	 altivec1.o altivec2.o altivec4.o altivec8.o recov.o algos.o \
+	 altivec1.o altivec2.o altivec4.o altivec8.o recov.o recov_ssse3.o algos.o \
 	 tables.o
 	 rm -f $@
 	 $(AR) cq $@ $^
diff --git a/lib/raid6/test/test.c b/lib/raid6/test/test.c
index 7a93031..5a485b7 100644
--- a/lib/raid6/test/test.c
+++ b/lib/raid6/test/test.c
@@ -90,25 +90,35 @@
 int main(int argc, char *argv[])
 {
 	const struct raid6_calls *const *algo;
+	const struct raid6_recov_calls *const *ra;
 	int i, j;
 	int err = 0;
 
 	makedata();
 
-	for (algo = raid6_algos; *algo; algo++) {
-		if (!(*algo)->valid || (*algo)->valid()) {
-			raid6_call = **algo;
+	for (ra = raid6_recov_algos; *ra; ra++) {
+		if ((*ra)->valid  && !(*ra)->valid())
+			continue;
+		raid6_2data_recov = (*ra)->data2;
+		raid6_datap_recov = (*ra)->datap;
 
-			/* Nuke syndromes */
-			memset(data[NDISKS-2], 0xee, 2*PAGE_SIZE);
+		printf("using recovery %s\n", (*ra)->name);
 
-			/* Generate assumed good syndrome */
-			raid6_call.gen_syndrome(NDISKS, PAGE_SIZE,
-						(void **)&dataptrs);
+		for (algo = raid6_algos; *algo; algo++) {
+			if (!(*algo)->valid || (*algo)->valid()) {
+				raid6_call = **algo;
 
-			for (i = 0; i < NDISKS-1; i++)
-				for (j = i+1; j < NDISKS; j++)
-					err += test_disks(i, j);
+				/* Nuke syndromes */
+				memset(data[NDISKS-2], 0xee, 2*PAGE_SIZE);
+
+				/* Generate assumed good syndrome */
+				raid6_call.gen_syndrome(NDISKS, PAGE_SIZE,
+							(void **)&dataptrs);
+
+				for (i = 0; i < NDISKS-1; i++)
+					for (j = i+1; j < NDISKS; j++)
+						err += test_disks(i, j);
+			}
 		}
 		printf("\n");
 	}
diff --git a/lib/raid6/x86.h b/lib/raid6/x86.h
index cb2a8c9..d55d632 100644
--- a/lib/raid6/x86.h
+++ b/lib/raid6/x86.h
@@ -35,24 +35,29 @@
 {
 }
 
+#define __aligned(x) __attribute__((aligned(x)))
+
 #define X86_FEATURE_MMX		(0*32+23) /* Multimedia Extensions */
 #define X86_FEATURE_FXSR	(0*32+24) /* FXSAVE and FXRSTOR instructions
 					   * (fast save and restore) */
 #define X86_FEATURE_XMM		(0*32+25) /* Streaming SIMD Extensions */
 #define X86_FEATURE_XMM2	(0*32+26) /* Streaming SIMD Extensions-2 */
+#define X86_FEATURE_XMM3	(4*32+ 0) /* "pni" SSE-3 */
+#define X86_FEATURE_SSSE3	(4*32+ 9) /* Supplemental SSE-3 */
+#define X86_FEATURE_AVX	(4*32+28) /* Advanced Vector Extensions */
 #define X86_FEATURE_MMXEXT	(1*32+22) /* AMD MMX extensions */
 
 /* Should work well enough on modern CPUs for testing */
 static inline int boot_cpu_has(int flag)
 {
-	u32 eax = (flag >> 5) ? 0x80000001 : 1;
-	u32 edx;
+	u32 eax = (flag & 0x20) ? 0x80000001 : 1;
+	u32 ecx, edx;
 
 	asm volatile("cpuid"
-		     : "+a" (eax), "=d" (edx)
-		     : : "ecx", "ebx");
+		     : "+a" (eax), "=d" (edx), "=c" (ecx)
+		     : : "ebx");
 
-	return (edx >> (flag & 31)) & 1;
+	return ((flag & 0x80 ? ecx : edx) >> (flag & 31)) & 1;
 }
 
 #endif /* ndef __KERNEL__ */
diff --git a/scripts/.gitignore b/scripts/.gitignore
index 105b21f..65f362d 100644
--- a/scripts/.gitignore
+++ b/scripts/.gitignore
@@ -9,3 +9,4 @@
 ihex2fw
 recordmcount
 docproc
+sortextable
diff --git a/scripts/Makefile b/scripts/Makefile
index 3626666..a55b006 100644
--- a/scripts/Makefile
+++ b/scripts/Makefile
@@ -15,6 +15,9 @@
 hostprogs-$(CONFIG_VT)           += conmakehash
 hostprogs-$(CONFIG_IKCONFIG)     += bin2c
 hostprogs-$(BUILD_C_RECORDMCOUNT) += recordmcount
+hostprogs-$(CONFIG_BUILDTIME_EXTABLE_SORT) += sortextable
+
+HOSTCFLAGS_sortextable.o = -I$(srctree)/tools/include
 
 always		:= $(hostprogs-y) $(hostprogs-m)
 
diff --git a/scripts/kconfig/mconf.c b/scripts/kconfig/mconf.c
index 2c6286c..f606738 100644
--- a/scripts/kconfig/mconf.c
+++ b/scripts/kconfig/mconf.c
@@ -240,7 +240,7 @@
 	"Defined at drivers/pci/Kconfig:47\n"
 	"Depends on: X86_LOCAL_APIC && X86_IO_APIC || IA64\n"
 	"Location:\n"
-	"  -> Bus options (PCI, PCMCIA, EISA, MCA, ISA)\n"
+	"  -> Bus options (PCI, PCMCIA, EISA, ISA)\n"
 	"    -> PCI support (PCI [=y])\n"
 	"      -> PCI access mode (<choice> [=y])\n"
 	"Selects: LIBCRC32\n"
diff --git a/scripts/kconfig/nconf.c b/scripts/kconfig/nconf.c
index 73070cb..8c0eb659 100644
--- a/scripts/kconfig/nconf.c
+++ b/scripts/kconfig/nconf.c
@@ -223,7 +223,7 @@
 "Defined at drivers/pci/Kconfig:47\n"
 "Depends on: X86_LOCAL_APIC && X86_IO_APIC || IA64\n"
 "Location:\n"
-"  -> Bus options (PCI, PCMCIA, EISA, MCA, ISA)\n"
+"  -> Bus options (PCI, PCMCIA, EISA, ISA)\n"
 "    -> PCI support (PCI [ = y])\n"
 "      -> PCI access mode (<choice> [ = y])\n"
 "Selects: LIBCRC32\n"
diff --git a/scripts/sortextable.c b/scripts/sortextable.c
new file mode 100644
index 0000000..1ca9ceb
--- /dev/null
+++ b/scripts/sortextable.c
@@ -0,0 +1,322 @@
+/*
+ * sortextable.c: Sort the kernel's exception table
+ *
+ * Copyright 2011 - 2012 Cavium, Inc.
+ *
+ * Based on code taken from recortmcount.c which is:
+ *
+ * Copyright 2009 John F. Reiser <jreiser@BitWagon.com>.  All rights reserved.
+ * Licensed under the GNU General Public License, version 2 (GPLv2).
+ *
+ * Restructured to fit Linux format, as well as other updates:
+ *  Copyright 2010 Steven Rostedt <srostedt@redhat.com>, Red Hat Inc.
+ */
+
+/*
+ * Strategy: alter the vmlinux file in-place.
+ */
+
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <getopt.h>
+#include <elf.h>
+#include <fcntl.h>
+#include <setjmp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <tools/be_byteshift.h>
+#include <tools/le_byteshift.h>
+
+static int fd_map;	/* File descriptor for file being modified. */
+static int mmap_failed; /* Boolean flag. */
+static void *ehdr_curr; /* current ElfXX_Ehdr *  for resource cleanup */
+static struct stat sb;	/* Remember .st_size, etc. */
+static jmp_buf jmpenv;	/* setjmp/longjmp per-file error escape */
+
+/* setjmp() return values */
+enum {
+	SJ_SETJMP = 0,  /* hardwired first return */
+	SJ_FAIL,
+	SJ_SUCCEED
+};
+
+/* Per-file resource cleanup when multiple files. */
+static void
+cleanup(void)
+{
+	if (!mmap_failed)
+		munmap(ehdr_curr, sb.st_size);
+	close(fd_map);
+}
+
+static void __attribute__((noreturn))
+fail_file(void)
+{
+	cleanup();
+	longjmp(jmpenv, SJ_FAIL);
+}
+
+static void __attribute__((noreturn))
+succeed_file(void)
+{
+	cleanup();
+	longjmp(jmpenv, SJ_SUCCEED);
+}
+
+
+/*
+ * Get the whole file as a programming convenience in order to avoid
+ * malloc+lseek+read+free of many pieces.  If successful, then mmap
+ * avoids copying unused pieces; else just read the whole file.
+ * Open for both read and write.
+ */
+static void *mmap_file(char const *fname)
+{
+	void *addr;
+
+	fd_map = open(fname, O_RDWR);
+	if (fd_map < 0 || fstat(fd_map, &sb) < 0) {
+		perror(fname);
+		fail_file();
+	}
+	if (!S_ISREG(sb.st_mode)) {
+		fprintf(stderr, "not a regular file: %s\n", fname);
+		fail_file();
+	}
+	addr = mmap(0, sb.st_size, PROT_READ|PROT_WRITE, MAP_SHARED,
+		    fd_map, 0);
+	if (addr == MAP_FAILED) {
+		mmap_failed = 1;
+		fprintf(stderr, "Could not mmap file: %s\n", fname);
+		fail_file();
+	}
+	return addr;
+}
+
+static uint64_t r8be(const uint64_t *x)
+{
+	return get_unaligned_be64(x);
+}
+static uint32_t rbe(const uint32_t *x)
+{
+	return get_unaligned_be32(x);
+}
+static uint16_t r2be(const uint16_t *x)
+{
+	return get_unaligned_be16(x);
+}
+static uint64_t r8le(const uint64_t *x)
+{
+	return get_unaligned_le64(x);
+}
+static uint32_t rle(const uint32_t *x)
+{
+	return get_unaligned_le32(x);
+}
+static uint16_t r2le(const uint16_t *x)
+{
+	return get_unaligned_le16(x);
+}
+
+static void w8be(uint64_t val, uint64_t *x)
+{
+	put_unaligned_be64(val, x);
+}
+static void wbe(uint32_t val, uint32_t *x)
+{
+	put_unaligned_be32(val, x);
+}
+static void w2be(uint16_t val, uint16_t *x)
+{
+	put_unaligned_be16(val, x);
+}
+static void w8le(uint64_t val, uint64_t *x)
+{
+	put_unaligned_le64(val, x);
+}
+static void wle(uint32_t val, uint32_t *x)
+{
+	put_unaligned_le32(val, x);
+}
+static void w2le(uint16_t val, uint16_t *x)
+{
+	put_unaligned_le16(val, x);
+}
+
+static uint64_t (*r8)(const uint64_t *);
+static uint32_t (*r)(const uint32_t *);
+static uint16_t (*r2)(const uint16_t *);
+static void (*w8)(uint64_t, uint64_t *);
+static void (*w)(uint32_t, uint32_t *);
+static void (*w2)(uint16_t, uint16_t *);
+
+typedef void (*table_sort_t)(char *, int);
+
+/* 32 bit and 64 bit are very similar */
+#include "sortextable.h"
+#define SORTEXTABLE_64
+#include "sortextable.h"
+
+static int compare_x86_table(const void *a, const void *b)
+{
+	int32_t av = (int32_t)r(a);
+	int32_t bv = (int32_t)r(b);
+
+	if (av < bv)
+		return -1;
+	if (av > bv)
+		return 1;
+	return 0;
+}
+
+static void sort_x86_table(char *extab_image, int image_size)
+{
+	int i;
+
+	/*
+	 * Do the same thing the runtime sort does, first normalize to
+	 * being relative to the start of the section.
+	 */
+	i = 0;
+	while (i < image_size) {
+		uint32_t *loc = (uint32_t *)(extab_image + i);
+		w(r(loc) + i, loc);
+		i += 4;
+	}
+
+	qsort(extab_image, image_size / 8, 8, compare_x86_table);
+
+	/* Now denormalize. */
+	i = 0;
+	while (i < image_size) {
+		uint32_t *loc = (uint32_t *)(extab_image + i);
+		w(r(loc) - i, loc);
+		i += 4;
+	}
+}
+
+static void
+do_file(char const *const fname)
+{
+	table_sort_t custom_sort;
+	Elf32_Ehdr *ehdr = mmap_file(fname);
+
+	ehdr_curr = ehdr;
+	switch (ehdr->e_ident[EI_DATA]) {
+	default:
+		fprintf(stderr, "unrecognized ELF data encoding %d: %s\n",
+			ehdr->e_ident[EI_DATA], fname);
+		fail_file();
+		break;
+	case ELFDATA2LSB:
+		r = rle;
+		r2 = r2le;
+		r8 = r8le;
+		w = wle;
+		w2 = w2le;
+		w8 = w8le;
+		break;
+	case ELFDATA2MSB:
+		r = rbe;
+		r2 = r2be;
+		r8 = r8be;
+		w = wbe;
+		w2 = w2be;
+		w8 = w8be;
+		break;
+	}  /* end switch */
+	if (memcmp(ELFMAG, ehdr->e_ident, SELFMAG) != 0
+	||  r2(&ehdr->e_type) != ET_EXEC
+	||  ehdr->e_ident[EI_VERSION] != EV_CURRENT) {
+		fprintf(stderr, "unrecognized ET_EXEC file %s\n", fname);
+		fail_file();
+	}
+
+	custom_sort = NULL;
+	switch (r2(&ehdr->e_machine)) {
+	default:
+		fprintf(stderr, "unrecognized e_machine %d %s\n",
+			r2(&ehdr->e_machine), fname);
+		fail_file();
+		break;
+	case EM_386:
+	case EM_X86_64:
+		custom_sort = sort_x86_table;
+		break;
+	case EM_MIPS:
+		break;
+	}  /* end switch */
+
+	switch (ehdr->e_ident[EI_CLASS]) {
+	default:
+		fprintf(stderr, "unrecognized ELF class %d %s\n",
+			ehdr->e_ident[EI_CLASS], fname);
+		fail_file();
+		break;
+	case ELFCLASS32:
+		if (r2(&ehdr->e_ehsize) != sizeof(Elf32_Ehdr)
+		||  r2(&ehdr->e_shentsize) != sizeof(Elf32_Shdr)) {
+			fprintf(stderr,
+				"unrecognized ET_EXEC file: %s\n", fname);
+			fail_file();
+		}
+		do32(ehdr, fname, custom_sort);
+		break;
+	case ELFCLASS64: {
+		Elf64_Ehdr *const ghdr = (Elf64_Ehdr *)ehdr;
+		if (r2(&ghdr->e_ehsize) != sizeof(Elf64_Ehdr)
+		||  r2(&ghdr->e_shentsize) != sizeof(Elf64_Shdr)) {
+			fprintf(stderr,
+				"unrecognized ET_EXEC file: %s\n", fname);
+			fail_file();
+		}
+		do64(ghdr, fname, custom_sort);
+		break;
+	}
+	}  /* end switch */
+
+	cleanup();
+}
+
+int
+main(int argc, char *argv[])
+{
+	int n_error = 0;  /* gcc-4.3.0 false positive complaint */
+	int i;
+
+	if (argc < 2) {
+		fprintf(stderr, "usage: sortextable vmlinux...\n");
+		return 0;
+	}
+
+	/* Process each file in turn, allowing deep failure. */
+	for (i = 1; i < argc; i++) {
+		char *file = argv[i];
+		int const sjval = setjmp(jmpenv);
+
+		switch (sjval) {
+		default:
+			fprintf(stderr, "internal error: %s\n", file);
+			exit(1);
+			break;
+		case SJ_SETJMP:    /* normal sequence */
+			/* Avoid problems if early cleanup() */
+			fd_map = -1;
+			ehdr_curr = NULL;
+			mmap_failed = 1;
+			do_file(file);
+			break;
+		case SJ_FAIL:    /* error in do_file or below */
+			++n_error;
+			break;
+		case SJ_SUCCEED:    /* premature success */
+			/* do nothing */
+			break;
+		}  /* end switch */
+	}
+	return !!n_error;
+}
diff --git a/scripts/sortextable.h b/scripts/sortextable.h
new file mode 100644
index 0000000..e4fd45b
--- /dev/null
+++ b/scripts/sortextable.h
@@ -0,0 +1,191 @@
+/*
+ * sortextable.h
+ *
+ * Copyright 2011 - 2012 Cavium, Inc.
+ *
+ * Some of this code was taken out of recordmcount.h written by:
+ *
+ * Copyright 2009 John F. Reiser <jreiser@BitWagon.com>.  All rights reserved.
+ * Copyright 2010 Steven Rostedt <srostedt@redhat.com>, Red Hat Inc.
+ *
+ *
+ * Licensed under the GNU General Public License, version 2 (GPLv2).
+ */
+
+#undef extable_ent_size
+#undef compare_extable
+#undef do_func
+#undef Elf_Addr
+#undef Elf_Ehdr
+#undef Elf_Shdr
+#undef Elf_Rel
+#undef Elf_Rela
+#undef Elf_Sym
+#undef ELF_R_SYM
+#undef Elf_r_sym
+#undef ELF_R_INFO
+#undef Elf_r_info
+#undef ELF_ST_BIND
+#undef ELF_ST_TYPE
+#undef fn_ELF_R_SYM
+#undef fn_ELF_R_INFO
+#undef uint_t
+#undef _r
+#undef _w
+
+#ifdef SORTEXTABLE_64
+# define extable_ent_size	16
+# define compare_extable	compare_extable_64
+# define do_func		do64
+# define Elf_Addr		Elf64_Addr
+# define Elf_Ehdr		Elf64_Ehdr
+# define Elf_Shdr		Elf64_Shdr
+# define Elf_Rel		Elf64_Rel
+# define Elf_Rela		Elf64_Rela
+# define Elf_Sym		Elf64_Sym
+# define ELF_R_SYM		ELF64_R_SYM
+# define Elf_r_sym		Elf64_r_sym
+# define ELF_R_INFO		ELF64_R_INFO
+# define Elf_r_info		Elf64_r_info
+# define ELF_ST_BIND		ELF64_ST_BIND
+# define ELF_ST_TYPE		ELF64_ST_TYPE
+# define fn_ELF_R_SYM		fn_ELF64_R_SYM
+# define fn_ELF_R_INFO		fn_ELF64_R_INFO
+# define uint_t			uint64_t
+# define _r			r8
+# define _w			w8
+#else
+# define extable_ent_size	8
+# define compare_extable	compare_extable_32
+# define do_func		do32
+# define Elf_Addr		Elf32_Addr
+# define Elf_Ehdr		Elf32_Ehdr
+# define Elf_Shdr		Elf32_Shdr
+# define Elf_Rel		Elf32_Rel
+# define Elf_Rela		Elf32_Rela
+# define Elf_Sym		Elf32_Sym
+# define ELF_R_SYM		ELF32_R_SYM
+# define Elf_r_sym		Elf32_r_sym
+# define ELF_R_INFO		ELF32_R_INFO
+# define Elf_r_info		Elf32_r_info
+# define ELF_ST_BIND		ELF32_ST_BIND
+# define ELF_ST_TYPE		ELF32_ST_TYPE
+# define fn_ELF_R_SYM		fn_ELF32_R_SYM
+# define fn_ELF_R_INFO		fn_ELF32_R_INFO
+# define uint_t			uint32_t
+# define _r			r
+# define _w			w
+#endif
+
+static int compare_extable(const void *a, const void *b)
+{
+	Elf_Addr av = _r(a);
+	Elf_Addr bv = _r(b);
+
+	if (av < bv)
+		return -1;
+	if (av > bv)
+		return 1;
+	return 0;
+}
+
+static void
+do_func(Elf_Ehdr *ehdr, char const *const fname, table_sort_t custom_sort)
+{
+	Elf_Shdr *shdr;
+	Elf_Shdr *shstrtab_sec;
+	Elf_Shdr *strtab_sec = NULL;
+	Elf_Shdr *symtab_sec = NULL;
+	Elf_Shdr *extab_sec = NULL;
+	Elf_Sym *sym;
+	Elf_Sym *sort_needed_sym;
+	Elf_Shdr *sort_needed_sec;
+	Elf_Rel *relocs = NULL;
+	int relocs_size;
+	uint32_t *sort_done_location;
+	const char *secstrtab;
+	const char *strtab;
+	char *extab_image;
+	int extab_index = 0;
+	int i;
+	int idx;
+
+	shdr = (Elf_Shdr *)((char *)ehdr + _r(&ehdr->e_shoff));
+	shstrtab_sec = shdr + r2(&ehdr->e_shstrndx);
+	secstrtab = (const char *)ehdr + _r(&shstrtab_sec->sh_offset);
+	for (i = 0; i < r2(&ehdr->e_shnum); i++) {
+		idx = r(&shdr[i].sh_name);
+		if (strcmp(secstrtab + idx, "__ex_table") == 0) {
+			extab_sec = shdr + i;
+			extab_index = i;
+		}
+		if ((r(&shdr[i].sh_type) == SHT_REL ||
+		     r(&shdr[i].sh_type) == SHT_RELA) &&
+		    r(&shdr[i].sh_info) == extab_index) {
+			relocs = (void *)ehdr + _r(&shdr[i].sh_offset);
+			relocs_size = _r(&shdr[i].sh_size);
+		}
+		if (strcmp(secstrtab + idx, ".symtab") == 0)
+			symtab_sec = shdr + i;
+		if (strcmp(secstrtab + idx, ".strtab") == 0)
+			strtab_sec = shdr + i;
+	}
+	if (strtab_sec == NULL) {
+		fprintf(stderr,	"no .strtab in  file: %s\n", fname);
+		fail_file();
+	}
+	if (symtab_sec == NULL) {
+		fprintf(stderr,	"no .symtab in  file: %s\n", fname);
+		fail_file();
+	}
+	if (extab_sec == NULL) {
+		fprintf(stderr,	"no __ex_table in  file: %s\n", fname);
+		fail_file();
+	}
+	strtab = (const char *)ehdr + _r(&strtab_sec->sh_offset);
+
+	extab_image = (void *)ehdr + _r(&extab_sec->sh_offset);
+
+	if (custom_sort) {
+		custom_sort(extab_image, _r(&extab_sec->sh_size));
+	} else {
+		int num_entries = _r(&extab_sec->sh_size) / extable_ent_size;
+		qsort(extab_image, num_entries,
+		      extable_ent_size, compare_extable);
+	}
+	/* If there were relocations, we no longer need them. */
+	if (relocs)
+		memset(relocs, 0, relocs_size);
+
+	/* find main_extable_sort_needed */
+	sort_needed_sym = NULL;
+	for (i = 0; i < _r(&symtab_sec->sh_size) / sizeof(Elf_Sym); i++) {
+		sym = (void *)ehdr + _r(&symtab_sec->sh_offset);
+		sym += i;
+		if (ELF_ST_TYPE(sym->st_info) != STT_OBJECT)
+			continue;
+		idx = r(&sym->st_name);
+		if (strcmp(strtab + idx, "main_extable_sort_needed") == 0) {
+			sort_needed_sym = sym;
+			break;
+		}
+	}
+	if (sort_needed_sym == NULL) {
+		fprintf(stderr,
+			"no main_extable_sort_needed symbol in  file: %s\n",
+			fname);
+		fail_file();
+	}
+	sort_needed_sec = &shdr[r2(&sort_needed_sym->st_shndx)];
+	sort_done_location = (void *)ehdr +
+		_r(&sort_needed_sec->sh_offset) +
+		_r(&sort_needed_sym->st_value) -
+		_r(&sort_needed_sec->sh_addr);
+
+#if 1
+	printf("sort done marker at %lx\n",
+	       (unsigned long)((char *)sort_done_location - (char *)ehdr));
+#endif
+	/* We sorted it, clear the flag. */
+	w(0, sort_done_location);
+}
diff --git a/sound/atmel/ac97c.c b/sound/atmel/ac97c.c
index 115313e..f5ded64 100644
--- a/sound/atmel/ac97c.c
+++ b/sound/atmel/ac97c.c
@@ -991,6 +991,8 @@
 			gpio_direction_output(pdata->reset_pin, 1);
 			chip->reset_pin = pdata->reset_pin;
 		}
+	} else {
+		chip->reset_pin = -EINVAL;
 	}
 
 	snd_card_set_dev(card, &pdev->dev);
diff --git a/sound/core/jack.c b/sound/core/jack.c
index 471e1e3..a06b165 100644
--- a/sound/core/jack.c
+++ b/sound/core/jack.c
@@ -155,7 +155,7 @@
  * @jack:   The jack to configure
  * @parent: The device to set as parent for the jack.
  *
- * Set the parent for the jack input device in the device tree.  This
+ * Set the parent for the jack devices in the device tree.  This
  * function is only valid prior to registration of the jack.  If no
  * parent is configured then the parent device will be the sound card.
  */
@@ -179,6 +179,9 @@
  * mapping is provided but keys are enabled in the jack type then
  * BTN_n numeric buttons will be reported.
  *
+ * If jacks are not reporting via the input API this call will have no
+ * effect.
+ *
  * Note that this is intended to be use by simple devices with small
  * numbers of keys that can be reported.  It is also possible to
  * access the input device directly - devices with complex input
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index 4d18941..faedb14 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -1894,6 +1894,7 @@
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	snd_pcm_uframes_t xfer = 0;
 	snd_pcm_uframes_t offset = 0;
+	snd_pcm_uframes_t avail;
 	int err = 0;
 
 	if (size == 0)
@@ -1917,13 +1918,12 @@
 	}
 
 	runtime->twake = runtime->control->avail_min ? : 1;
+	if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)
+		snd_pcm_update_hw_ptr(substream);
+	avail = snd_pcm_playback_avail(runtime);
 	while (size > 0) {
 		snd_pcm_uframes_t frames, appl_ptr, appl_ofs;
-		snd_pcm_uframes_t avail;
 		snd_pcm_uframes_t cont;
-		if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)
-			snd_pcm_update_hw_ptr(substream);
-		avail = snd_pcm_playback_avail(runtime);
 		if (!avail) {
 			if (nonblock) {
 				err = -EAGAIN;
@@ -1971,6 +1971,7 @@
 		offset += frames;
 		size -= frames;
 		xfer += frames;
+		avail -= frames;
 		if (runtime->status->state == SNDRV_PCM_STATE_PREPARED &&
 		    snd_pcm_playback_hw_avail(runtime) >= (snd_pcm_sframes_t)runtime->start_threshold) {
 			err = snd_pcm_start(substream);
@@ -2111,6 +2112,7 @@
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	snd_pcm_uframes_t xfer = 0;
 	snd_pcm_uframes_t offset = 0;
+	snd_pcm_uframes_t avail;
 	int err = 0;
 
 	if (size == 0)
@@ -2141,13 +2143,12 @@
 	}
 
 	runtime->twake = runtime->control->avail_min ? : 1;
+	if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)
+		snd_pcm_update_hw_ptr(substream);
+	avail = snd_pcm_capture_avail(runtime);
 	while (size > 0) {
 		snd_pcm_uframes_t frames, appl_ptr, appl_ofs;
-		snd_pcm_uframes_t avail;
 		snd_pcm_uframes_t cont;
-		if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)
-			snd_pcm_update_hw_ptr(substream);
-		avail = snd_pcm_capture_avail(runtime);
 		if (!avail) {
 			if (runtime->status->state ==
 			    SNDRV_PCM_STATE_DRAINING) {
@@ -2202,6 +2203,7 @@
 		offset += frames;
 		size -= frames;
 		xfer += frames;
+		avail -= frames;
 	}
  _end_unlock:
 	runtime->twake = 0;
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 3fe99e6..53b5ada 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -1360,7 +1360,14 @@
 
 static int snd_pcm_pre_drain_init(struct snd_pcm_substream *substream, int state)
 {
-	substream->runtime->trigger_master = substream;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	switch (runtime->status->state) {
+	case SNDRV_PCM_STATE_OPEN:
+	case SNDRV_PCM_STATE_DISCONNECTED:
+	case SNDRV_PCM_STATE_SUSPENDED:
+		return -EBADFD;
+	}
+	runtime->trigger_master = substream;
 	return 0;
 }
 
@@ -1379,6 +1386,9 @@
 		case SNDRV_PCM_STATE_RUNNING:
 			runtime->status->state = SNDRV_PCM_STATE_DRAINING;
 			break;
+		case SNDRV_PCM_STATE_XRUN:
+			runtime->status->state = SNDRV_PCM_STATE_SETUP;
+			break;
 		default:
 			break;
 		}
diff --git a/sound/core/sound_oss.c b/sound/core/sound_oss.c
index c700920..e952833 100644
--- a/sound/core/sound_oss.c
+++ b/sound/core/sound_oss.c
@@ -35,7 +35,7 @@
 #include <linux/sound.h>
 #include <linux/mutex.h>
 
-#define SNDRV_OSS_MINORS 128
+#define SNDRV_OSS_MINORS 256
 
 static struct snd_minor *snd_oss_minors[SNDRV_OSS_MINORS];
 static DEFINE_MUTEX(sound_oss_mutex);
@@ -111,7 +111,7 @@
 	int register1 = -1, register2 = -1;
 	struct device *carddev = snd_card_get_device_link(card);
 
-	if (card && card->number >= 8)
+	if (card && card->number >= SNDRV_MINOR_OSS_DEVICES)
 		return 0; /* ignore silently */
 	if (minor < 0)
 		return minor;
@@ -170,7 +170,7 @@
 	int track2 = -1;
 	struct snd_minor *mptr;
 
-	if (card && card->number >= 8)
+	if (card && card->number >= SNDRV_MINOR_OSS_DEVICES)
 		return 0;
 	if (minor < 0)
 		return minor;
diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c
index ad079b6..8b5c36f 100644
--- a/sound/drivers/aloop.c
+++ b/sound/drivers/aloop.c
@@ -117,6 +117,7 @@
 	/* timer stuff */
 	unsigned int irq_pos;		/* fractional IRQ position */
 	unsigned int period_size_frac;
+	unsigned int last_drift;
 	unsigned long last_jiffies;
 	struct timer_list timer;
 };
@@ -264,6 +265,7 @@
 			return err;
 		dpcm->last_jiffies = jiffies;
 		dpcm->pcm_rate_shift = 0;
+		dpcm->last_drift = 0;
 		spin_lock(&cable->lock);	
 		cable->running |= stream;
 		cable->pause &= ~stream;
@@ -444,34 +446,30 @@
 	}
 }
 
-#define BYTEPOS_UPDATE_POSONLY	0
-#define BYTEPOS_UPDATE_CLEAR	1
-#define BYTEPOS_UPDATE_COPY	2
-
-static void loopback_bytepos_update(struct loopback_pcm *dpcm,
-				    unsigned int delta,
-				    unsigned int cmd)
+static inline unsigned int bytepos_delta(struct loopback_pcm *dpcm,
+					 unsigned int jiffies_delta)
 {
-	unsigned int count;
 	unsigned long last_pos;
+	unsigned int delta;
 
 	last_pos = byte_pos(dpcm, dpcm->irq_pos);
-	dpcm->irq_pos += delta * dpcm->pcm_bps;
-	count = byte_pos(dpcm, dpcm->irq_pos) - last_pos;
-	if (!count)
-		return;
-	if (cmd == BYTEPOS_UPDATE_CLEAR)
-		clear_capture_buf(dpcm, count);
-	else if (cmd == BYTEPOS_UPDATE_COPY)
-		copy_play_buf(dpcm->cable->streams[SNDRV_PCM_STREAM_PLAYBACK],
-			      dpcm->cable->streams[SNDRV_PCM_STREAM_CAPTURE],
-			      count);
-	dpcm->buf_pos += count;
-	dpcm->buf_pos %= dpcm->pcm_buffer_size;
+	dpcm->irq_pos += jiffies_delta * dpcm->pcm_bps;
+	delta = byte_pos(dpcm, dpcm->irq_pos) - last_pos;
+	if (delta >= dpcm->last_drift)
+		delta -= dpcm->last_drift;
+	dpcm->last_drift = 0;
 	if (dpcm->irq_pos >= dpcm->period_size_frac) {
 		dpcm->irq_pos %= dpcm->period_size_frac;
 		dpcm->period_update_pending = 1;
 	}
+	return delta;
+}
+
+static inline void bytepos_finish(struct loopback_pcm *dpcm,
+				  unsigned int delta)
+{
+	dpcm->buf_pos += delta;
+	dpcm->buf_pos %= dpcm->pcm_buffer_size;
 }
 
 static unsigned int loopback_pos_update(struct loopback_cable *cable)
@@ -481,7 +479,7 @@
 	struct loopback_pcm *dpcm_capt =
 			cable->streams[SNDRV_PCM_STREAM_CAPTURE];
 	unsigned long delta_play = 0, delta_capt = 0;
-	unsigned int running;
+	unsigned int running, count1, count2;
 	unsigned long flags;
 
 	spin_lock_irqsave(&cable->lock, flags);
@@ -500,12 +498,13 @@
 		goto unlock;
 		
 	if (delta_play > delta_capt) {
-		loopback_bytepos_update(dpcm_play, delta_play - delta_capt,
-					BYTEPOS_UPDATE_POSONLY);
+		count1 = bytepos_delta(dpcm_play, delta_play - delta_capt);
+		bytepos_finish(dpcm_play, count1);
 		delta_play = delta_capt;
 	} else if (delta_play < delta_capt) {
-		loopback_bytepos_update(dpcm_capt, delta_capt - delta_play,
-					BYTEPOS_UPDATE_CLEAR);
+		count1 = bytepos_delta(dpcm_capt, delta_capt - delta_play);
+		clear_capture_buf(dpcm_capt, count1);
+		bytepos_finish(dpcm_capt, count1);
 		delta_capt = delta_play;
 	}
 
@@ -513,8 +512,17 @@
 		goto unlock;
 
 	/* note delta_capt == delta_play at this moment */
-	loopback_bytepos_update(dpcm_capt, delta_capt, BYTEPOS_UPDATE_COPY);
-	loopback_bytepos_update(dpcm_play, delta_play, BYTEPOS_UPDATE_POSONLY);
+	count1 = bytepos_delta(dpcm_play, delta_play);
+	count2 = bytepos_delta(dpcm_capt, delta_capt);
+	if (count1 < count2) {
+		dpcm_capt->last_drift = count2 - count1;
+		count1 = count2;
+	} else if (count1 > count2) {
+		dpcm_play->last_drift = count1 - count2;
+	}
+	copy_play_buf(dpcm_play, dpcm_capt, count1);
+	bytepos_finish(dpcm_play, count1);
+	bytepos_finish(dpcm_capt, count1);
  unlock:
 	spin_unlock_irqrestore(&cable->lock, flags);
 	return running;
diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c
index 87657dd..ea995af 100644
--- a/sound/firewire/amdtp.c
+++ b/sound/firewire/amdtp.c
@@ -31,6 +31,8 @@
 #define INTERRUPT_INTERVAL	16
 #define QUEUE_LENGTH		48
 
+static void pcm_period_tasklet(unsigned long data);
+
 /**
  * amdtp_out_stream_init - initialize an AMDTP output stream structure
  * @s: the AMDTP output stream to initialize
@@ -47,6 +49,7 @@
 	s->flags = flags;
 	s->context = ERR_PTR(-1);
 	mutex_init(&s->mutex);
+	tasklet_init(&s->period_tasklet, pcm_period_tasklet, (unsigned long)s);
 	s->packet_index = 0;
 
 	return 0;
@@ -164,6 +167,21 @@
 }
 EXPORT_SYMBOL(amdtp_out_stream_set_pcm_format);
 
+/**
+ * amdtp_out_stream_pcm_prepare - prepare PCM device for running
+ * @s: the AMDTP output stream
+ *
+ * This function should be called from the PCM device's .prepare callback.
+ */
+void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s)
+{
+	tasklet_kill(&s->period_tasklet);
+	s->pcm_buffer_pointer = 0;
+	s->pcm_period_pointer = 0;
+	s->pointer_flush = true;
+}
+EXPORT_SYMBOL(amdtp_out_stream_pcm_prepare);
+
 static unsigned int calculate_data_blocks(struct amdtp_out_stream *s)
 {
 	unsigned int phase, data_blocks;
@@ -376,11 +394,21 @@
 		s->pcm_period_pointer += data_blocks;
 		if (s->pcm_period_pointer >= pcm->runtime->period_size) {
 			s->pcm_period_pointer -= pcm->runtime->period_size;
-			snd_pcm_period_elapsed(pcm);
+			s->pointer_flush = false;
+			tasklet_hi_schedule(&s->period_tasklet);
 		}
 	}
 }
 
+static void pcm_period_tasklet(unsigned long data)
+{
+	struct amdtp_out_stream *s = (void *)data;
+	struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm);
+
+	if (pcm)
+		snd_pcm_period_elapsed(pcm);
+}
+
 static void out_packet_callback(struct fw_iso_context *context, u32 cycle,
 				size_t header_length, void *header, void *data)
 {
@@ -506,6 +534,24 @@
 EXPORT_SYMBOL(amdtp_out_stream_start);
 
 /**
+ * amdtp_out_stream_pcm_pointer - get the PCM buffer position
+ * @s: the AMDTP output stream that transports the PCM data
+ *
+ * Returns the current buffer position, in frames.
+ */
+unsigned long amdtp_out_stream_pcm_pointer(struct amdtp_out_stream *s)
+{
+	/* this optimization is allowed to be racy */
+	if (s->pointer_flush)
+		fw_iso_context_flush_completions(s->context);
+	else
+		s->pointer_flush = true;
+
+	return ACCESS_ONCE(s->pcm_buffer_pointer);
+}
+EXPORT_SYMBOL(amdtp_out_stream_pcm_pointer);
+
+/**
  * amdtp_out_stream_update - update the stream after a bus reset
  * @s: the AMDTP output stream
  */
@@ -532,6 +578,7 @@
 		return;
 	}
 
+	tasklet_kill(&s->period_tasklet);
 	fw_iso_context_stop(s->context);
 	fw_iso_context_destroy(s->context);
 	s->context = ERR_PTR(-1);
diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h
index 537a9cb..b680c5e 100644
--- a/sound/firewire/amdtp.h
+++ b/sound/firewire/amdtp.h
@@ -1,6 +1,7 @@
 #ifndef SOUND_FIREWIRE_AMDTP_H_INCLUDED
 #define SOUND_FIREWIRE_AMDTP_H_INCLUDED
 
+#include <linux/interrupt.h>
 #include <linux/mutex.h>
 #include <linux/spinlock.h>
 #include "packets-buffer.h"
@@ -55,6 +56,7 @@
 	struct iso_packets_buffer buffer;
 
 	struct snd_pcm_substream *pcm;
+	struct tasklet_struct period_tasklet;
 
 	int packet_index;
 	unsigned int data_block_counter;
@@ -66,6 +68,7 @@
 
 	unsigned int pcm_buffer_pointer;
 	unsigned int pcm_period_pointer;
+	bool pointer_flush;
 };
 
 int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit,
@@ -81,6 +84,8 @@
 
 void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s,
 				     snd_pcm_format_t format);
+void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s);
+unsigned long amdtp_out_stream_pcm_pointer(struct amdtp_out_stream *s);
 void amdtp_out_stream_pcm_abort(struct amdtp_out_stream *s);
 
 /**
@@ -123,18 +128,6 @@
 }
 
 /**
- * amdtp_out_stream_pcm_prepare - prepare PCM device for running
- * @s: the AMDTP output stream
- *
- * This function should be called from the PCM device's .prepare callback.
- */
-static inline void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s)
-{
-	s->pcm_buffer_pointer = 0;
-	s->pcm_period_pointer = 0;
-}
-
-/**
  * amdtp_out_stream_pcm_trigger - start/stop playback from a PCM device
  * @s: the AMDTP output stream
  * @pcm: the PCM device to be started, or %NULL to stop the current device
@@ -149,18 +142,6 @@
 	ACCESS_ONCE(s->pcm) = pcm;
 }
 
-/**
- * amdtp_out_stream_pcm_pointer - get the PCM buffer position
- * @s: the AMDTP output stream that transports the PCM data
- *
- * Returns the current buffer position, in frames.
- */
-static inline unsigned long
-amdtp_out_stream_pcm_pointer(struct amdtp_out_stream *s)
-{
-	return ACCESS_ONCE(s->pcm_buffer_pointer);
-}
-
 static inline bool cip_sfc_is_base_44100(enum cip_sfc sfc)
 {
 	return sfc & 1;
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig
index 5ca0939..ff3af6e 100644
--- a/sound/pci/Kconfig
+++ b/sound/pci/Kconfig
@@ -228,7 +228,7 @@
 	  Say Y here to include support for sound cards based on the
 	  C-Media CMI8788 (Oxygen HD Audio) chip:
 	   * Asound A-8788
-	   * Asus Xonar DG
+	   * Asus Xonar DG/DGX
 	   * AuzenTech X-Meridian
 	   * AuzenTech X-Meridian 2G
 	   * Bgears b-Enspirer
diff --git a/sound/pci/ad1889.c b/sound/pci/ad1889.c
index 9d91d61..e672ff4 100644
--- a/sound/pci/ad1889.c
+++ b/sound/pci/ad1889.c
@@ -1062,17 +1062,4 @@
 	.remove = __devexit_p(snd_ad1889_remove),
 };
 
-static int __init
-alsa_ad1889_init(void)
-{
-	return pci_register_driver(&ad1889_pci_driver);
-}
-
-static void __exit
-alsa_ad1889_fini(void)
-{
-	pci_unregister_driver(&ad1889_pci_driver);
-}
-
-module_init(alsa_ad1889_init);
-module_exit(alsa_ad1889_fini);
+module_pci_driver(ad1889_pci_driver);
diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c
index bdd6164..9dfc27b 100644
--- a/sound/pci/ali5451/ali5451.c
+++ b/sound/pci/ali5451/ali5451.c
@@ -2294,7 +2294,7 @@
 	pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver ali5451_driver = {
 	.name = KBUILD_MODNAME,
 	.id_table = snd_ali_ids,
 	.probe = snd_ali_probe,
@@ -2305,15 +2305,4 @@
 #endif
 };                                
 
-static int __init alsa_card_ali_init(void)
-{
-	return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_ali_exit(void)
-{
-	pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_ali_init)
-module_exit(alsa_card_ali_exit)
+module_pci_driver(ali5451_driver);
diff --git a/sound/pci/als300.c b/sound/pci/als300.c
index 8196e22..59d6538 100644
--- a/sound/pci/als300.c
+++ b/sound/pci/als300.c
@@ -852,7 +852,7 @@
 	return 0;
 }
 
-static struct pci_driver driver = {
+static struct pci_driver als300_driver = {
 	.name = KBUILD_MODNAME,
 	.id_table = snd_als300_ids,
 	.probe = snd_als300_probe,
@@ -863,15 +863,4 @@
 #endif
 };
 
-static int __init alsa_card_als300_init(void)
-{
-	return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_als300_exit(void)
-{
-	pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_als300_init)
-module_exit(alsa_card_als300_exit)
+module_pci_driver(als300_driver);
diff --git a/sound/pci/als4000.c b/sound/pci/als4000.c
index 3269b80..7d7f259 100644
--- a/sound/pci/als4000.c
+++ b/sound/pci/als4000.c
@@ -1036,7 +1036,7 @@
 #endif /* CONFIG_PM */
 
 
-static struct pci_driver driver = {
+static struct pci_driver als4000_driver = {
 	.name = KBUILD_MODNAME,
 	.id_table = snd_als4000_ids,
 	.probe = snd_card_als4000_probe,
@@ -1047,15 +1047,4 @@
 #endif
 };
 
-static int __init alsa_card_als4000_init(void)
-{
-	return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_als4000_exit(void)
-{
-	pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_als4000_init)
-module_exit(alsa_card_als4000_exit)
+module_pci_driver(als4000_driver);
diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c
index 590682f..156a94f 100644
--- a/sound/pci/atiixp.c
+++ b/sound/pci/atiixp.c
@@ -1700,7 +1700,7 @@
 	pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver atiixp_driver = {
 	.name = KBUILD_MODNAME,
 	.id_table = snd_atiixp_ids,
 	.probe = snd_atiixp_probe,
@@ -1711,16 +1711,4 @@
 #endif
 };
 
-
-static int __init alsa_card_atiixp_init(void)
-{
-	return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_atiixp_exit(void)
-{
-	pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_atiixp_init)
-module_exit(alsa_card_atiixp_exit)
+module_pci_driver(atiixp_driver);
diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c
index 524d35f..30a4fd9 100644
--- a/sound/pci/atiixp_modem.c
+++ b/sound/pci/atiixp_modem.c
@@ -1331,7 +1331,7 @@
 	pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver atiixp_modem_driver = {
 	.name = KBUILD_MODNAME,
 	.id_table = snd_atiixp_ids,
 	.probe = snd_atiixp_probe,
@@ -1342,16 +1342,4 @@
 #endif
 };
 
-
-static int __init alsa_card_atiixp_init(void)
-{
-	return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_atiixp_exit(void)
-{
-	pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_atiixp_init)
-module_exit(alsa_card_atiixp_exit)
+module_pci_driver(atiixp_modem_driver);
diff --git a/sound/pci/au88x0/au88x0.c b/sound/pci/au88x0/au88x0.c
index f13ad53..ffc376f 100644
--- a/sound/pci/au88x0/au88x0.c
+++ b/sound/pci/au88x0/au88x0.c
@@ -375,24 +375,11 @@
 }
 
 // pci_driver definition
-static struct pci_driver driver = {
+static struct pci_driver vortex_driver = {
 	.name = KBUILD_MODNAME,
 	.id_table = snd_vortex_ids,
 	.probe = snd_vortex_probe,
 	.remove = __devexit_p(snd_vortex_remove),
 };
 
-// initialization of the module
-static int __init alsa_card_vortex_init(void)
-{
-	return pci_register_driver(&driver);
-}
-
-// clean up the module
-static void __exit alsa_card_vortex_exit(void)
-{
-	pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_vortex_init)
-module_exit(alsa_card_vortex_exit)
+module_pci_driver(vortex_driver);
diff --git a/sound/pci/aw2/aw2-alsa.c b/sound/pci/aw2/aw2-alsa.c
index 1c523193..0f80474 100644
--- a/sound/pci/aw2/aw2-alsa.c
+++ b/sound/pci/aw2/aw2-alsa.c
@@ -112,8 +112,6 @@
 /*********************************
  * FUNCTION DECLARATIONS
  ********************************/
-static int __init alsa_card_aw2_init(void);
-static void __exit alsa_card_aw2_exit(void);
 static int snd_aw2_dev_free(struct snd_device *device);
 static int __devinit snd_aw2_create(struct snd_card *card,
 				    struct pci_dev *pci, struct aw2 **rchip);
@@ -171,13 +169,15 @@
 MODULE_DEVICE_TABLE(pci, snd_aw2_ids);
 
 /* pci_driver definition */
-static struct pci_driver driver = {
+static struct pci_driver aw2_driver = {
 	.name = KBUILD_MODNAME,
 	.id_table = snd_aw2_ids,
 	.probe = snd_aw2_probe,
 	.remove = __devexit_p(snd_aw2_remove),
 };
 
+module_pci_driver(aw2_driver);
+
 /* operators for playback PCM alsa interface */
 static struct snd_pcm_ops snd_aw2_playback_ops = {
 	.open = snd_aw2_pcm_playback_open,
@@ -217,23 +217,6 @@
  * FUNCTION IMPLEMENTATIONS
  ********************************/
 
-/* initialization of the module */
-static int __init alsa_card_aw2_init(void)
-{
-	snd_printdd(KERN_DEBUG "aw2: Load aw2 module\n");
-	return pci_register_driver(&driver);
-}
-
-/* clean up the module */
-static void __exit alsa_card_aw2_exit(void)
-{
-	snd_printdd(KERN_DEBUG "aw2: Unload aw2 module\n");
-	pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_aw2_init);
-module_exit(alsa_card_aw2_exit);
-
 /* component-destructor */
 static int snd_aw2_dev_free(struct snd_device *device)
 {
diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c
index 496f14c..f0b4d74 100644
--- a/sound/pci/azt3328.c
+++ b/sound/pci/azt3328.c
@@ -2862,7 +2862,7 @@
 #endif /* CONFIG_PM */
 
 
-static struct pci_driver driver = {
+static struct pci_driver azf3328_driver = {
 	.name = KBUILD_MODNAME,
 	.id_table = snd_azf3328_ids,
 	.probe = snd_azf3328_probe,
@@ -2873,23 +2873,4 @@
 #endif
 };
 
-static int __init
-alsa_card_azf3328_init(void)
-{
-	int err;
-	snd_azf3328_dbgcallenter();
-	err = pci_register_driver(&driver);
-	snd_azf3328_dbgcallleave();
-	return err;
-}
-
-static void __exit
-alsa_card_azf3328_exit(void)
-{
-	snd_azf3328_dbgcallenter();
-	pci_unregister_driver(&driver);
-	snd_azf3328_dbgcallleave();
-}
-
-module_init(alsa_card_azf3328_init)
-module_exit(alsa_card_azf3328_exit)
+module_pci_driver(azf3328_driver);
diff --git a/sound/pci/bt87x.c b/sound/pci/bt87x.c
index 62d6163..b6a95ee 100644
--- a/sound/pci/bt87x.c
+++ b/sound/pci/bt87x.c
@@ -836,8 +836,6 @@
 	{0x7063, 0x2000}, /* pcHDTV HD-2000 TV */
 };
 
-static struct pci_driver driver;
-
 /* return the id of the card, or a negative value if it's blacklisted */
 static int __devinit snd_bt87x_detect_card(struct pci_dev *pci)
 {
@@ -964,24 +962,11 @@
 	{ }
 };
 
-static struct pci_driver driver = {
+static struct pci_driver bt87x_driver = {
 	.name = KBUILD_MODNAME,
 	.id_table = snd_bt87x_ids,
 	.probe = snd_bt87x_probe,
 	.remove = __devexit_p(snd_bt87x_remove),
 };
 
-static int __init alsa_card_bt87x_init(void)
-{
-	if (load_all)
-		driver.id_table = snd_bt87x_default_ids;
-	return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_bt87x_exit(void)
-{
-	pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_bt87x_init)
-module_exit(alsa_card_bt87x_exit)
+module_pci_driver(bt87x_driver);
diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c
index 08d6ebf..e76d68a 100644
--- a/sound/pci/ca0106/ca0106_main.c
+++ b/sound/pci/ca0106/ca0106_main.c
@@ -1932,7 +1932,7 @@
 MODULE_DEVICE_TABLE(pci, snd_ca0106_ids);
 
 // pci_driver definition
-static struct pci_driver driver = {
+static struct pci_driver ca0106_driver = {
 	.name = KBUILD_MODNAME,
 	.id_table = snd_ca0106_ids,
 	.probe = snd_ca0106_probe,
@@ -1943,17 +1943,4 @@
 #endif
 };
 
-// initialization of the module
-static int __init alsa_card_ca0106_init(void)
-{
-	return pci_register_driver(&driver);
-}
-
-// clean up the module
-static void __exit alsa_card_ca0106_exit(void)
-{
-	pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_ca0106_init)
-module_exit(alsa_card_ca0106_exit)
+module_pci_driver(ca0106_driver);
diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c
index 19b0626..3815bd4 100644
--- a/sound/pci/cmipci.c
+++ b/sound/pci/cmipci.c
@@ -3398,7 +3398,7 @@
 }
 #endif /* CONFIG_PM */
 
-static struct pci_driver driver = {
+static struct pci_driver cmipci_driver = {
 	.name = KBUILD_MODNAME,
 	.id_table = snd_cmipci_ids,
 	.probe = snd_cmipci_probe,
@@ -3409,15 +3409,4 @@
 #endif
 };
 	
-static int __init alsa_card_cmipci_init(void)
-{
-	return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_cmipci_exit(void)
-{
-	pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_cmipci_init)
-module_exit(alsa_card_cmipci_exit)
+module_pci_driver(cmipci_driver);
diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c
index a9f368f..33506ee 100644
--- a/sound/pci/cs4281.c
+++ b/sound/pci/cs4281.c
@@ -2084,7 +2084,7 @@
 }
 #endif /* CONFIG_PM */
 
-static struct pci_driver driver = {
+static struct pci_driver cs4281_driver = {
 	.name = KBUILD_MODNAME,
 	.id_table = snd_cs4281_ids,
 	.probe = snd_cs4281_probe,
@@ -2095,15 +2095,4 @@
 #endif
 };
 	
-static int __init alsa_card_cs4281_init(void)
-{
-	return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_cs4281_exit(void)
-{
-	pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_cs4281_init)
-module_exit(alsa_card_cs4281_exit)
+module_pci_driver(cs4281_driver);
diff --git a/sound/pci/cs46xx/cs46xx.c b/sound/pci/cs46xx/cs46xx.c
index 819d79d..6cc7404 100644
--- a/sound/pci/cs46xx/cs46xx.c
+++ b/sound/pci/cs46xx/cs46xx.c
@@ -161,7 +161,7 @@
 	pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver cs46xx_driver = {
 	.name = KBUILD_MODNAME,
 	.id_table = snd_cs46xx_ids,
 	.probe = snd_card_cs46xx_probe,
@@ -172,15 +172,4 @@
 #endif
 };
 
-static int __init alsa_card_cs46xx_init(void)
-{
-	return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_cs46xx_exit(void)
-{
-	pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_cs46xx_init)
-module_exit(alsa_card_cs46xx_exit)
+module_pci_driver(cs46xx_driver);
diff --git a/sound/pci/cs5530.c b/sound/pci/cs5530.c
index c47cabf..f1e4229 100644
--- a/sound/pci/cs5530.c
+++ b/sound/pci/cs5530.c
@@ -291,23 +291,11 @@
 	return 0;
 }
 
-static struct pci_driver driver = {
+static struct pci_driver cs5530_driver = {
 	.name = KBUILD_MODNAME,
 	.id_table = snd_cs5530_ids,
 	.probe = snd_cs5530_probe,
 	.remove = __devexit_p(snd_cs5530_remove),
 };
 
-static int __init alsa_card_cs5530_init(void)
-{
-	return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_cs5530_exit(void)
-{
-	pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_cs5530_init)
-module_exit(alsa_card_cs5530_exit)
-
+module_pci_driver(cs5530_driver);
diff --git a/sound/pci/cs5535audio/cs5535audio.c b/sound/pci/cs5535audio/cs5535audio.c
index a2fb217..2c9697c 100644
--- a/sound/pci/cs5535audio/cs5535audio.c
+++ b/sound/pci/cs5535audio/cs5535audio.c
@@ -394,7 +394,7 @@
 	pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver cs5535audio_driver = {
 	.name = KBUILD_MODNAME,
 	.id_table = snd_cs5535audio_ids,
 	.probe = snd_cs5535audio_probe,
@@ -405,18 +405,7 @@
 #endif
 };
 
-static int __init alsa_card_cs5535audio_init(void)
-{
-	return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_cs5535audio_exit(void)
-{
-	pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_cs5535audio_init)
-module_exit(alsa_card_cs5535audio_exit)
+module_pci_driver(cs5535audio_driver);
 
 MODULE_AUTHOR("Jaya Kumar");
 MODULE_LICENSE("GPL");
diff --git a/sound/pci/ctxfi/xfi.c b/sound/pci/ctxfi/xfi.c
index 15d95d2..75aa2c3 100644
--- a/sound/pci/ctxfi/xfi.c
+++ b/sound/pci/ctxfi/xfi.c
@@ -154,15 +154,4 @@
 #endif
 };
 
-static int __init ct_card_init(void)
-{
-	return pci_register_driver(&ct_driver);
-}
-
-static void __exit ct_card_exit(void)
-{
-	pci_unregister_driver(&ct_driver);
-}
-
-module_init(ct_card_init)
-module_exit(ct_card_exit)
+module_pci_driver(ct_driver);
diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c
index 595c11f..0f8eda1 100644
--- a/sound/pci/echoaudio/echoaudio.c
+++ b/sound/pci/echoaudio/echoaudio.c
@@ -2328,7 +2328,7 @@
 ******************************************************************************/
 
 /* pci_driver definition */
-static struct pci_driver driver = {
+static struct pci_driver echo_driver = {
 	.name = KBUILD_MODNAME,
 	.id_table = snd_echo_ids,
 	.probe = snd_echo_probe,
@@ -2339,22 +2339,4 @@
 #endif /* CONFIG_PM */
 };
 
-
-
-/* initialization of the module */
-static int __init alsa_card_echo_init(void)
-{
-	return pci_register_driver(&driver);
-}
-
-
-
-/* clean up the module */
-static void __exit alsa_card_echo_exit(void)
-{
-	pci_unregister_driver(&driver);
-}
-
-
-module_init(alsa_card_echo_init)
-module_exit(alsa_card_echo_exit)
+module_pci_driver(echo_driver);
diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c
index 790c65d..7fdbbe4 100644
--- a/sound/pci/emu10k1/emu10k1.c
+++ b/sound/pci/emu10k1/emu10k1.c
@@ -263,7 +263,7 @@
 }
 #endif
 
-static struct pci_driver driver = {
+static struct pci_driver emu10k1_driver = {
 	.name = KBUILD_MODNAME,
 	.id_table = snd_emu10k1_ids,
 	.probe = snd_card_emu10k1_probe,
@@ -274,15 +274,4 @@
 #endif
 };
 
-static int __init alsa_card_emu10k1_init(void)
-{
-	return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_emu10k1_exit(void)
-{
-	pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_emu10k1_init)
-module_exit(alsa_card_emu10k1_exit)
+module_pci_driver(emu10k1_driver);
diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c
index 47a651c..5c8978b 100644
--- a/sound/pci/emu10k1/emu10k1x.c
+++ b/sound/pci/emu10k1/emu10k1x.c
@@ -1612,24 +1612,11 @@
 MODULE_DEVICE_TABLE(pci, snd_emu10k1x_ids);
 
 // pci_driver definition
-static struct pci_driver driver = {
+static struct pci_driver emu10k1x_driver = {
 	.name = KBUILD_MODNAME,
 	.id_table = snd_emu10k1x_ids,
 	.probe = snd_emu10k1x_probe,
 	.remove = __devexit_p(snd_emu10k1x_remove),
 };
 
-// initialization of the module
-static int __init alsa_card_emu10k1x_init(void)
-{
-	return pci_register_driver(&driver);
-}
-
-// clean up the module
-static void __exit alsa_card_emu10k1x_exit(void)
-{
-	pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_emu10k1x_init)
-module_exit(alsa_card_emu10k1x_exit)
+module_pci_driver(emu10k1x_driver);
diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c
index 47a245e..3821c81 100644
--- a/sound/pci/ens1370.c
+++ b/sound/pci/ens1370.c
@@ -2488,7 +2488,7 @@
 	pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver ens137x_driver = {
 	.name = KBUILD_MODNAME,
 	.id_table = snd_audiopci_ids,
 	.probe = snd_audiopci_probe,
@@ -2499,15 +2499,4 @@
 #endif
 };
 	
-static int __init alsa_card_ens137x_init(void)
-{
-	return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_ens137x_exit(void)
-{
-	pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_ens137x_init)
-module_exit(alsa_card_ens137x_exit)
+module_pci_driver(ens137x_driver);
diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c
index 53eb76b..82c8d8c 100644
--- a/sound/pci/es1938.c
+++ b/sound/pci/es1938.c
@@ -1882,7 +1882,7 @@
 	pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver es1938_driver = {
 	.name = KBUILD_MODNAME,
 	.id_table = snd_es1938_ids,
 	.probe = snd_es1938_probe,
@@ -1893,15 +1893,4 @@
 #endif
 };
 
-static int __init alsa_card_es1938_init(void)
-{
-	return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_es1938_exit(void)
-{
-	pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_es1938_init)
-module_exit(alsa_card_es1938_exit)
+module_pci_driver(es1938_driver);
diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c
index a8faae1..67f47d8 100644
--- a/sound/pci/es1968.c
+++ b/sound/pci/es1968.c
@@ -2898,7 +2898,7 @@
 	pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver es1968_driver = {
 	.name = KBUILD_MODNAME,
 	.id_table = snd_es1968_ids,
 	.probe = snd_es1968_probe,
@@ -2909,15 +2909,4 @@
 #endif
 };
 
-static int __init alsa_card_es1968_init(void)
-{
-	return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_es1968_exit(void)
-{
-	pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_es1968_init)
-module_exit(alsa_card_es1968_exit)
+module_pci_driver(es1968_driver);
diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c
index a416ea8..f696623 100644
--- a/sound/pci/fm801.c
+++ b/sound/pci/fm801.c
@@ -1416,7 +1416,7 @@
 }
 #endif
 
-static struct pci_driver driver = {
+static struct pci_driver fm801_driver = {
 	.name = KBUILD_MODNAME,
 	.id_table = snd_fm801_ids,
 	.probe = snd_card_fm801_probe,
@@ -1427,15 +1427,4 @@
 #endif
 };
 
-static int __init alsa_card_fm801_init(void)
-{
-	return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_fm801_exit(void)
-{
-	pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_fm801_init)
-module_exit(alsa_card_fm801_exit)
+module_pci_driver(fm801_driver);
diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile
index ace157c..bd4149f 100644
--- a/sound/pci/hda/Makefile
+++ b/sound/pci/hda/Makefile
@@ -1,6 +1,6 @@
 snd-hda-intel-objs := hda_intel.o
 
-snd-hda-codec-y := hda_codec.o hda_jack.o
+snd-hda-codec-y := hda_codec.o hda_jack.o hda_auto_parser.o
 snd-hda-codec-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o
 snd-hda-codec-$(CONFIG_PROC_FS) += hda_proc.o
 snd-hda-codec-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o
diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c
new file mode 100644
index 0000000..6e9ef3e
--- /dev/null
+++ b/sound/pci/hda/hda_auto_parser.c
@@ -0,0 +1,760 @@
+/*
+ * BIOS auto-parser helper functions for HD-audio
+ *
+ * Copyright (c) 2012 Takashi Iwai <tiwai@suse.de>
+ *
+ * This driver is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/slab.h>
+#include <linux/export.h>
+#include <sound/core.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+#include "hda_auto_parser.h"
+
+#define SFX	"hda_codec: "
+
+/*
+ * Helper for automatic pin configuration
+ */
+
+static int is_in_nid_list(hda_nid_t nid, const hda_nid_t *list)
+{
+	for (; *list; list++)
+		if (*list == nid)
+			return 1;
+	return 0;
+}
+
+
+/*
+ * Sort an associated group of pins according to their sequence numbers.
+ */
+static void sort_pins_by_sequence(hda_nid_t *pins, short *sequences,
+				  int num_pins)
+{
+	int i, j;
+	short seq;
+	hda_nid_t nid;
+
+	for (i = 0; i < num_pins; i++) {
+		for (j = i + 1; j < num_pins; j++) {
+			if (sequences[i] > sequences[j]) {
+				seq = sequences[i];
+				sequences[i] = sequences[j];
+				sequences[j] = seq;
+				nid = pins[i];
+				pins[i] = pins[j];
+				pins[j] = nid;
+			}
+		}
+	}
+}
+
+
+/* add the found input-pin to the cfg->inputs[] table */
+static void add_auto_cfg_input_pin(struct auto_pin_cfg *cfg, hda_nid_t nid,
+				   int type)
+{
+	if (cfg->num_inputs < AUTO_CFG_MAX_INS) {
+		cfg->inputs[cfg->num_inputs].pin = nid;
+		cfg->inputs[cfg->num_inputs].type = type;
+		cfg->num_inputs++;
+	}
+}
+
+/* sort inputs in the order of AUTO_PIN_* type */
+static void sort_autocfg_input_pins(struct auto_pin_cfg *cfg)
+{
+	int i, j;
+
+	for (i = 0; i < cfg->num_inputs; i++) {
+		for (j = i + 1; j < cfg->num_inputs; j++) {
+			if (cfg->inputs[i].type > cfg->inputs[j].type) {
+				struct auto_pin_cfg_item tmp;
+				tmp = cfg->inputs[i];
+				cfg->inputs[i] = cfg->inputs[j];
+				cfg->inputs[j] = tmp;
+			}
+		}
+	}
+}
+
+/* Reorder the surround channels
+ * ALSA sequence is front/surr/clfe/side
+ * HDA sequence is:
+ *    4-ch: front/surr  =>  OK as it is
+ *    6-ch: front/clfe/surr
+ *    8-ch: front/clfe/rear/side|fc
+ */
+static void reorder_outputs(unsigned int nums, hda_nid_t *pins)
+{
+	hda_nid_t nid;
+
+	switch (nums) {
+	case 3:
+	case 4:
+		nid = pins[1];
+		pins[1] = pins[2];
+		pins[2] = nid;
+		break;
+	}
+}
+
+/*
+ * Parse all pin widgets and store the useful pin nids to cfg
+ *
+ * The number of line-outs or any primary output is stored in line_outs,
+ * and the corresponding output pins are assigned to line_out_pins[],
+ * in the order of front, rear, CLFE, side, ...
+ *
+ * If more extra outputs (speaker and headphone) are found, the pins are
+ * assisnged to hp_pins[] and speaker_pins[], respectively.  If no line-out jack
+ * is detected, one of speaker of HP pins is assigned as the primary
+ * output, i.e. to line_out_pins[0].  So, line_outs is always positive
+ * if any analog output exists.
+ *
+ * The analog input pins are assigned to inputs array.
+ * The digital input/output pins are assigned to dig_in_pin and dig_out_pin,
+ * respectively.
+ */
+int snd_hda_parse_pin_defcfg(struct hda_codec *codec,
+			     struct auto_pin_cfg *cfg,
+			     const hda_nid_t *ignore_nids,
+			     unsigned int cond_flags)
+{
+	hda_nid_t nid, end_nid;
+	short seq, assoc_line_out;
+	short sequences_line_out[ARRAY_SIZE(cfg->line_out_pins)];
+	short sequences_speaker[ARRAY_SIZE(cfg->speaker_pins)];
+	short sequences_hp[ARRAY_SIZE(cfg->hp_pins)];
+	int i;
+
+	memset(cfg, 0, sizeof(*cfg));
+
+	memset(sequences_line_out, 0, sizeof(sequences_line_out));
+	memset(sequences_speaker, 0, sizeof(sequences_speaker));
+	memset(sequences_hp, 0, sizeof(sequences_hp));
+	assoc_line_out = 0;
+
+	codec->ignore_misc_bit = true;
+	end_nid = codec->start_nid + codec->num_nodes;
+	for (nid = codec->start_nid; nid < end_nid; nid++) {
+		unsigned int wid_caps = get_wcaps(codec, nid);
+		unsigned int wid_type = get_wcaps_type(wid_caps);
+		unsigned int def_conf;
+		short assoc, loc, conn, dev;
+
+		/* read all default configuration for pin complex */
+		if (wid_type != AC_WID_PIN)
+			continue;
+		/* ignore the given nids (e.g. pc-beep returns error) */
+		if (ignore_nids && is_in_nid_list(nid, ignore_nids))
+			continue;
+
+		def_conf = snd_hda_codec_get_pincfg(codec, nid);
+		if (!(get_defcfg_misc(snd_hda_codec_get_pincfg(codec, nid)) &
+		      AC_DEFCFG_MISC_NO_PRESENCE))
+			codec->ignore_misc_bit = false;
+		conn = get_defcfg_connect(def_conf);
+		if (conn == AC_JACK_PORT_NONE)
+			continue;
+		loc = get_defcfg_location(def_conf);
+		dev = get_defcfg_device(def_conf);
+
+		/* workaround for buggy BIOS setups */
+		if (dev == AC_JACK_LINE_OUT) {
+			if (conn == AC_JACK_PORT_FIXED)
+				dev = AC_JACK_SPEAKER;
+		}
+
+		switch (dev) {
+		case AC_JACK_LINE_OUT:
+			seq = get_defcfg_sequence(def_conf);
+			assoc = get_defcfg_association(def_conf);
+
+			if (!(wid_caps & AC_WCAP_STEREO))
+				if (!cfg->mono_out_pin)
+					cfg->mono_out_pin = nid;
+			if (!assoc)
+				continue;
+			if (!assoc_line_out)
+				assoc_line_out = assoc;
+			else if (assoc_line_out != assoc)
+				continue;
+			if (cfg->line_outs >= ARRAY_SIZE(cfg->line_out_pins))
+				continue;
+			cfg->line_out_pins[cfg->line_outs] = nid;
+			sequences_line_out[cfg->line_outs] = seq;
+			cfg->line_outs++;
+			break;
+		case AC_JACK_SPEAKER:
+			seq = get_defcfg_sequence(def_conf);
+			assoc = get_defcfg_association(def_conf);
+			if (cfg->speaker_outs >= ARRAY_SIZE(cfg->speaker_pins))
+				continue;
+			cfg->speaker_pins[cfg->speaker_outs] = nid;
+			sequences_speaker[cfg->speaker_outs] = (assoc << 4) | seq;
+			cfg->speaker_outs++;
+			break;
+		case AC_JACK_HP_OUT:
+			seq = get_defcfg_sequence(def_conf);
+			assoc = get_defcfg_association(def_conf);
+			if (cfg->hp_outs >= ARRAY_SIZE(cfg->hp_pins))
+				continue;
+			cfg->hp_pins[cfg->hp_outs] = nid;
+			sequences_hp[cfg->hp_outs] = (assoc << 4) | seq;
+			cfg->hp_outs++;
+			break;
+		case AC_JACK_MIC_IN:
+			add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_MIC);
+			break;
+		case AC_JACK_LINE_IN:
+			add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_LINE_IN);
+			break;
+		case AC_JACK_CD:
+			add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_CD);
+			break;
+		case AC_JACK_AUX:
+			add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_AUX);
+			break;
+		case AC_JACK_SPDIF_OUT:
+		case AC_JACK_DIG_OTHER_OUT:
+			if (cfg->dig_outs >= ARRAY_SIZE(cfg->dig_out_pins))
+				continue;
+			cfg->dig_out_pins[cfg->dig_outs] = nid;
+			cfg->dig_out_type[cfg->dig_outs] =
+				(loc == AC_JACK_LOC_HDMI) ?
+				HDA_PCM_TYPE_HDMI : HDA_PCM_TYPE_SPDIF;
+			cfg->dig_outs++;
+			break;
+		case AC_JACK_SPDIF_IN:
+		case AC_JACK_DIG_OTHER_IN:
+			cfg->dig_in_pin = nid;
+			if (loc == AC_JACK_LOC_HDMI)
+				cfg->dig_in_type = HDA_PCM_TYPE_HDMI;
+			else
+				cfg->dig_in_type = HDA_PCM_TYPE_SPDIF;
+			break;
+		}
+	}
+
+	/* FIX-UP:
+	 * If no line-out is defined but multiple HPs are found,
+	 * some of them might be the real line-outs.
+	 */
+	if (!cfg->line_outs && cfg->hp_outs > 1 &&
+	    !(cond_flags & HDA_PINCFG_NO_HP_FIXUP)) {
+		int i = 0;
+		while (i < cfg->hp_outs) {
+			/* The real HPs should have the sequence 0x0f */
+			if ((sequences_hp[i] & 0x0f) == 0x0f) {
+				i++;
+				continue;
+			}
+			/* Move it to the line-out table */
+			cfg->line_out_pins[cfg->line_outs] = cfg->hp_pins[i];
+			sequences_line_out[cfg->line_outs] = sequences_hp[i];
+			cfg->line_outs++;
+			cfg->hp_outs--;
+			memmove(cfg->hp_pins + i, cfg->hp_pins + i + 1,
+				sizeof(cfg->hp_pins[0]) * (cfg->hp_outs - i));
+			memmove(sequences_hp + i, sequences_hp + i + 1,
+				sizeof(sequences_hp[0]) * (cfg->hp_outs - i));
+		}
+		memset(cfg->hp_pins + cfg->hp_outs, 0,
+		       sizeof(hda_nid_t) * (AUTO_CFG_MAX_OUTS - cfg->hp_outs));
+		if (!cfg->hp_outs)
+			cfg->line_out_type = AUTO_PIN_HP_OUT;
+
+	}
+
+	/* sort by sequence */
+	sort_pins_by_sequence(cfg->line_out_pins, sequences_line_out,
+			      cfg->line_outs);
+	sort_pins_by_sequence(cfg->speaker_pins, sequences_speaker,
+			      cfg->speaker_outs);
+	sort_pins_by_sequence(cfg->hp_pins, sequences_hp,
+			      cfg->hp_outs);
+
+	/*
+	 * FIX-UP: if no line-outs are detected, try to use speaker or HP pin
+	 * as a primary output
+	 */
+	if (!cfg->line_outs &&
+	    !(cond_flags & HDA_PINCFG_NO_LO_FIXUP)) {
+		if (cfg->speaker_outs) {
+			cfg->line_outs = cfg->speaker_outs;
+			memcpy(cfg->line_out_pins, cfg->speaker_pins,
+			       sizeof(cfg->speaker_pins));
+			cfg->speaker_outs = 0;
+			memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins));
+			cfg->line_out_type = AUTO_PIN_SPEAKER_OUT;
+		} else if (cfg->hp_outs) {
+			cfg->line_outs = cfg->hp_outs;
+			memcpy(cfg->line_out_pins, cfg->hp_pins,
+			       sizeof(cfg->hp_pins));
+			cfg->hp_outs = 0;
+			memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins));
+			cfg->line_out_type = AUTO_PIN_HP_OUT;
+		}
+	}
+
+	reorder_outputs(cfg->line_outs, cfg->line_out_pins);
+	reorder_outputs(cfg->hp_outs, cfg->hp_pins);
+	reorder_outputs(cfg->speaker_outs, cfg->speaker_pins);
+
+	sort_autocfg_input_pins(cfg);
+
+	/*
+	 * debug prints of the parsed results
+	 */
+	snd_printd("autoconfig: line_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x) type:%s\n",
+		   cfg->line_outs, cfg->line_out_pins[0], cfg->line_out_pins[1],
+		   cfg->line_out_pins[2], cfg->line_out_pins[3],
+		   cfg->line_out_pins[4],
+		   cfg->line_out_type == AUTO_PIN_HP_OUT ? "hp" :
+		   (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT ?
+		    "speaker" : "line"));
+	snd_printd("   speaker_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
+		   cfg->speaker_outs, cfg->speaker_pins[0],
+		   cfg->speaker_pins[1], cfg->speaker_pins[2],
+		   cfg->speaker_pins[3], cfg->speaker_pins[4]);
+	snd_printd("   hp_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
+		   cfg->hp_outs, cfg->hp_pins[0],
+		   cfg->hp_pins[1], cfg->hp_pins[2],
+		   cfg->hp_pins[3], cfg->hp_pins[4]);
+	snd_printd("   mono: mono_out=0x%x\n", cfg->mono_out_pin);
+	if (cfg->dig_outs)
+		snd_printd("   dig-out=0x%x/0x%x\n",
+			   cfg->dig_out_pins[0], cfg->dig_out_pins[1]);
+	snd_printd("   inputs:");
+	for (i = 0; i < cfg->num_inputs; i++) {
+		snd_printd(" %s=0x%x",
+			    hda_get_autocfg_input_label(codec, cfg, i),
+			    cfg->inputs[i].pin);
+	}
+	snd_printd("\n");
+	if (cfg->dig_in_pin)
+		snd_printd("   dig-in=0x%x\n", cfg->dig_in_pin);
+
+	return 0;
+}
+EXPORT_SYMBOL_HDA(snd_hda_parse_pin_defcfg);
+
+int snd_hda_get_input_pin_attr(unsigned int def_conf)
+{
+	unsigned int loc = get_defcfg_location(def_conf);
+	unsigned int conn = get_defcfg_connect(def_conf);
+	if (conn == AC_JACK_PORT_NONE)
+		return INPUT_PIN_ATTR_UNUSED;
+	/* Windows may claim the internal mic to be BOTH, too */
+	if (conn == AC_JACK_PORT_FIXED || conn == AC_JACK_PORT_BOTH)
+		return INPUT_PIN_ATTR_INT;
+	if ((loc & 0x30) == AC_JACK_LOC_INTERNAL)
+		return INPUT_PIN_ATTR_INT;
+	if ((loc & 0x30) == AC_JACK_LOC_SEPARATE)
+		return INPUT_PIN_ATTR_DOCK;
+	if (loc == AC_JACK_LOC_REAR)
+		return INPUT_PIN_ATTR_REAR;
+	if (loc == AC_JACK_LOC_FRONT)
+		return INPUT_PIN_ATTR_FRONT;
+	return INPUT_PIN_ATTR_NORMAL;
+}
+EXPORT_SYMBOL_HDA(snd_hda_get_input_pin_attr);
+
+/**
+ * hda_get_input_pin_label - Give a label for the given input pin
+ *
+ * When check_location is true, the function checks the pin location
+ * for mic and line-in pins, and set an appropriate prefix like "Front",
+ * "Rear", "Internal".
+ */
+
+static const char *hda_get_input_pin_label(struct hda_codec *codec,
+					   hda_nid_t pin, bool check_location)
+{
+	unsigned int def_conf;
+	static const char * const mic_names[] = {
+		"Internal Mic", "Dock Mic", "Mic", "Front Mic", "Rear Mic",
+	};
+	int attr;
+
+	def_conf = snd_hda_codec_get_pincfg(codec, pin);
+
+	switch (get_defcfg_device(def_conf)) {
+	case AC_JACK_MIC_IN:
+		if (!check_location)
+			return "Mic";
+		attr = snd_hda_get_input_pin_attr(def_conf);
+		if (!attr)
+			return "None";
+		return mic_names[attr - 1];
+	case AC_JACK_LINE_IN:
+		if (!check_location)
+			return "Line";
+		attr = snd_hda_get_input_pin_attr(def_conf);
+		if (!attr)
+			return "None";
+		if (attr == INPUT_PIN_ATTR_DOCK)
+			return "Dock Line";
+		return "Line";
+	case AC_JACK_AUX:
+		return "Aux";
+	case AC_JACK_CD:
+		return "CD";
+	case AC_JACK_SPDIF_IN:
+		return "SPDIF In";
+	case AC_JACK_DIG_OTHER_IN:
+		return "Digital In";
+	default:
+		return "Misc";
+	}
+}
+
+/* Check whether the location prefix needs to be added to the label.
+ * If all mic-jacks are in the same location (e.g. rear panel), we don't
+ * have to put "Front" prefix to each label.  In such a case, returns false.
+ */
+static int check_mic_location_need(struct hda_codec *codec,
+				   const struct auto_pin_cfg *cfg,
+				   int input)
+{
+	unsigned int defc;
+	int i, attr, attr2;
+
+	defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[input].pin);
+	attr = snd_hda_get_input_pin_attr(defc);
+	/* for internal or docking mics, we need locations */
+	if (attr <= INPUT_PIN_ATTR_NORMAL)
+		return 1;
+
+	attr = 0;
+	for (i = 0; i < cfg->num_inputs; i++) {
+		defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[i].pin);
+		attr2 = snd_hda_get_input_pin_attr(defc);
+		if (attr2 >= INPUT_PIN_ATTR_NORMAL) {
+			if (attr && attr != attr2)
+				return 1; /* different locations found */
+			attr = attr2;
+		}
+	}
+	return 0;
+}
+
+/**
+ * hda_get_autocfg_input_label - Get a label for the given input
+ *
+ * Get a label for the given input pin defined by the autocfg item.
+ * Unlike hda_get_input_pin_label(), this function checks all inputs
+ * defined in autocfg and avoids the redundant mic/line prefix as much as
+ * possible.
+ */
+const char *hda_get_autocfg_input_label(struct hda_codec *codec,
+					const struct auto_pin_cfg *cfg,
+					int input)
+{
+	int type = cfg->inputs[input].type;
+	int has_multiple_pins = 0;
+
+	if ((input > 0 && cfg->inputs[input - 1].type == type) ||
+	    (input < cfg->num_inputs - 1 && cfg->inputs[input + 1].type == type))
+		has_multiple_pins = 1;
+	if (has_multiple_pins && type == AUTO_PIN_MIC)
+		has_multiple_pins &= check_mic_location_need(codec, cfg, input);
+	return hda_get_input_pin_label(codec, cfg->inputs[input].pin,
+				       has_multiple_pins);
+}
+EXPORT_SYMBOL_HDA(hda_get_autocfg_input_label);
+
+/* return the position of NID in the list, or -1 if not found */
+static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums)
+{
+	int i;
+	for (i = 0; i < nums; i++)
+		if (list[i] == nid)
+			return i;
+	return -1;
+}
+
+/* get a unique suffix or an index number */
+static const char *check_output_sfx(hda_nid_t nid, const hda_nid_t *pins,
+				    int num_pins, int *indexp)
+{
+	static const char * const channel_sfx[] = {
+		" Front", " Surround", " CLFE", " Side"
+	};
+	int i;
+
+	i = find_idx_in_nid_list(nid, pins, num_pins);
+	if (i < 0)
+		return NULL;
+	if (num_pins == 1)
+		return "";
+	if (num_pins > ARRAY_SIZE(channel_sfx)) {
+		if (indexp)
+			*indexp = i;
+		return "";
+	}
+	return channel_sfx[i];
+}
+
+static int fill_audio_out_name(struct hda_codec *codec, hda_nid_t nid,
+			       const struct auto_pin_cfg *cfg,
+			       const char *name, char *label, int maxlen,
+			       int *indexp)
+{
+	unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
+	int attr = snd_hda_get_input_pin_attr(def_conf);
+	const char *pfx = "", *sfx = "";
+
+	/* handle as a speaker if it's a fixed line-out */
+	if (!strcmp(name, "Line Out") && attr == INPUT_PIN_ATTR_INT)
+		name = "Speaker";
+	/* check the location */
+	switch (attr) {
+	case INPUT_PIN_ATTR_DOCK:
+		pfx = "Dock ";
+		break;
+	case INPUT_PIN_ATTR_FRONT:
+		pfx = "Front ";
+		break;
+	}
+	if (cfg) {
+		/* try to give a unique suffix if needed */
+		sfx = check_output_sfx(nid, cfg->line_out_pins, cfg->line_outs,
+				       indexp);
+		if (!sfx)
+			sfx = check_output_sfx(nid, cfg->speaker_pins, cfg->speaker_outs,
+					       indexp);
+		if (!sfx) {
+			/* don't add channel suffix for Headphone controls */
+			int idx = find_idx_in_nid_list(nid, cfg->hp_pins,
+						       cfg->hp_outs);
+			if (idx >= 0)
+				*indexp = idx;
+			sfx = "";
+		}
+	}
+	snprintf(label, maxlen, "%s%s%s", pfx, name, sfx);
+	return 1;
+}
+
+/**
+ * snd_hda_get_pin_label - Get a label for the given I/O pin
+ *
+ * Get a label for the given pin.  This function works for both input and
+ * output pins.  When @cfg is given as non-NULL, the function tries to get
+ * an optimized label using hda_get_autocfg_input_label().
+ *
+ * This function tries to give a unique label string for the pin as much as
+ * possible.  For example, when the multiple line-outs are present, it adds
+ * the channel suffix like "Front", "Surround", etc (only when @cfg is given).
+ * If no unique name with a suffix is available and @indexp is non-NULL, the
+ * index number is stored in the pointer.
+ */
+int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid,
+			  const struct auto_pin_cfg *cfg,
+			  char *label, int maxlen, int *indexp)
+{
+	unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
+	const char *name = NULL;
+	int i;
+
+	if (indexp)
+		*indexp = 0;
+	if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
+		return 0;
+
+	switch (get_defcfg_device(def_conf)) {
+	case AC_JACK_LINE_OUT:
+		return fill_audio_out_name(codec, nid, cfg, "Line Out",
+					   label, maxlen, indexp);
+	case AC_JACK_SPEAKER:
+		return fill_audio_out_name(codec, nid, cfg, "Speaker",
+					   label, maxlen, indexp);
+	case AC_JACK_HP_OUT:
+		return fill_audio_out_name(codec, nid, cfg, "Headphone",
+					   label, maxlen, indexp);
+	case AC_JACK_SPDIF_OUT:
+	case AC_JACK_DIG_OTHER_OUT:
+		if (get_defcfg_location(def_conf) == AC_JACK_LOC_HDMI)
+			name = "HDMI";
+		else
+			name = "SPDIF";
+		if (cfg && indexp) {
+			i = find_idx_in_nid_list(nid, cfg->dig_out_pins,
+						 cfg->dig_outs);
+			if (i >= 0)
+				*indexp = i;
+		}
+		break;
+	default:
+		if (cfg) {
+			for (i = 0; i < cfg->num_inputs; i++) {
+				if (cfg->inputs[i].pin != nid)
+					continue;
+				name = hda_get_autocfg_input_label(codec, cfg, i);
+				if (name)
+					break;
+			}
+		}
+		if (!name)
+			name = hda_get_input_pin_label(codec, nid, true);
+		break;
+	}
+	if (!name)
+		return 0;
+	strlcpy(label, name, maxlen);
+	return 1;
+}
+EXPORT_SYMBOL_HDA(snd_hda_get_pin_label);
+
+int snd_hda_gen_add_verbs(struct hda_gen_spec *spec,
+			  const struct hda_verb *list)
+{
+	const struct hda_verb **v;
+	snd_array_init(&spec->verbs, sizeof(struct hda_verb *), 8);
+	v = snd_array_new(&spec->verbs);
+	if (!v)
+		return -ENOMEM;
+	*v = list;
+	return 0;
+}
+EXPORT_SYMBOL_HDA(snd_hda_gen_add_verbs);
+
+void snd_hda_gen_apply_verbs(struct hda_codec *codec)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	int i;
+	for (i = 0; i < spec->verbs.used; i++) {
+		struct hda_verb **v = snd_array_elem(&spec->verbs, i);
+		snd_hda_sequence_write(codec, *v);
+	}
+}
+EXPORT_SYMBOL_HDA(snd_hda_gen_apply_verbs);
+
+void snd_hda_apply_pincfgs(struct hda_codec *codec,
+			   const struct hda_pintbl *cfg)
+{
+	for (; cfg->nid; cfg++)
+		snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val);
+}
+EXPORT_SYMBOL_HDA(snd_hda_apply_pincfgs);
+
+void snd_hda_apply_fixup(struct hda_codec *codec, int action)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	int id = spec->fixup_id;
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+	const char *modelname = spec->fixup_name;
+#endif
+	int depth = 0;
+
+	if (!spec->fixup_list)
+		return;
+
+	while (id >= 0) {
+		const struct hda_fixup *fix = spec->fixup_list + id;
+
+		switch (fix->type) {
+		case HDA_FIXUP_PINS:
+			if (action != HDA_FIXUP_ACT_PRE_PROBE || !fix->v.pins)
+				break;
+			snd_printdd(KERN_INFO SFX
+				    "%s: Apply pincfg for %s\n",
+				    codec->chip_name, modelname);
+			snd_hda_apply_pincfgs(codec, fix->v.pins);
+			break;
+		case HDA_FIXUP_VERBS:
+			if (action != HDA_FIXUP_ACT_PROBE || !fix->v.verbs)
+				break;
+			snd_printdd(KERN_INFO SFX
+				    "%s: Apply fix-verbs for %s\n",
+				    codec->chip_name, modelname);
+			snd_hda_gen_add_verbs(codec->spec, fix->v.verbs);
+			break;
+		case HDA_FIXUP_FUNC:
+			if (!fix->v.func)
+				break;
+			snd_printdd(KERN_INFO SFX
+				    "%s: Apply fix-func for %s\n",
+				    codec->chip_name, modelname);
+			fix->v.func(codec, fix, action);
+			break;
+		default:
+			snd_printk(KERN_ERR SFX
+				   "%s: Invalid fixup type %d\n",
+				   codec->chip_name, fix->type);
+			break;
+		}
+		if (!fix->chained)
+			break;
+		if (++depth > 10)
+			break;
+		id = fix->chain_id;
+	}
+}
+EXPORT_SYMBOL_HDA(snd_hda_apply_fixup);
+
+void snd_hda_pick_fixup(struct hda_codec *codec,
+			const struct hda_model_fixup *models,
+			const struct snd_pci_quirk *quirk,
+			const struct hda_fixup *fixlist)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	const struct snd_pci_quirk *q;
+	int id = -1;
+	const char *name = NULL;
+
+	/* when model=nofixup is given, don't pick up any fixups */
+	if (codec->modelname && !strcmp(codec->modelname, "nofixup")) {
+		spec->fixup_list = NULL;
+		spec->fixup_id = -1;
+		return;
+	}
+
+	if (codec->modelname && models) {
+		while (models->name) {
+			if (!strcmp(codec->modelname, models->name)) {
+				id = models->id;
+				name = models->name;
+				break;
+			}
+			models++;
+		}
+	}
+	if (id < 0) {
+		q = snd_pci_quirk_lookup(codec->bus->pci, quirk);
+		if (q) {
+			id = q->value;
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+			name = q->name;
+#endif
+		}
+	}
+	if (id < 0) {
+		for (q = quirk; q->subvendor; q++) {
+			unsigned int vendorid =
+				q->subdevice | (q->subvendor << 16);
+			if (vendorid == codec->subsystem_id) {
+				id = q->value;
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+				name = q->name;
+#endif
+				break;
+			}
+		}
+	}
+
+	spec->fixup_id = id;
+	if (id >= 0) {
+		spec->fixup_list = fixlist;
+		spec->fixup_name = name;
+	}
+}
+EXPORT_SYMBOL_HDA(snd_hda_pick_fixup);
diff --git a/sound/pci/hda/hda_auto_parser.h b/sound/pci/hda/hda_auto_parser.h
new file mode 100644
index 0000000..2a7889d
--- /dev/null
+++ b/sound/pci/hda/hda_auto_parser.h
@@ -0,0 +1,160 @@
+/*
+ * BIOS auto-parser helper functions for HD-audio
+ *
+ * Copyright (c) 2012 Takashi Iwai <tiwai@suse.de>
+ *
+ * This driver is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __SOUND_HDA_AUTO_PARSER_H
+#define __SOUND_HDA_AUTO_PARSER_H
+
+/*
+ * Helper for automatic pin configuration
+ */
+
+enum {
+	AUTO_PIN_MIC,
+	AUTO_PIN_LINE_IN,
+	AUTO_PIN_CD,
+	AUTO_PIN_AUX,
+	AUTO_PIN_LAST
+};
+
+enum {
+	AUTO_PIN_LINE_OUT,
+	AUTO_PIN_SPEAKER_OUT,
+	AUTO_PIN_HP_OUT
+};
+
+#define AUTO_CFG_MAX_OUTS	HDA_MAX_OUTS
+#define AUTO_CFG_MAX_INS	8
+
+struct auto_pin_cfg_item {
+	hda_nid_t pin;
+	int type;
+};
+
+struct auto_pin_cfg;
+const char *hda_get_autocfg_input_label(struct hda_codec *codec,
+					const struct auto_pin_cfg *cfg,
+					int input);
+int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid,
+			  const struct auto_pin_cfg *cfg,
+			  char *label, int maxlen, int *indexp);
+
+enum {
+	INPUT_PIN_ATTR_UNUSED,	/* pin not connected */
+	INPUT_PIN_ATTR_INT,	/* internal mic/line-in */
+	INPUT_PIN_ATTR_DOCK,	/* docking mic/line-in */
+	INPUT_PIN_ATTR_NORMAL,	/* mic/line-in jack */
+	INPUT_PIN_ATTR_FRONT,	/* mic/line-in jack in front */
+	INPUT_PIN_ATTR_REAR,	/* mic/line-in jack in rear */
+};
+
+int snd_hda_get_input_pin_attr(unsigned int def_conf);
+
+struct auto_pin_cfg {
+	int line_outs;
+	/* sorted in the order of Front/Surr/CLFE/Side */
+	hda_nid_t line_out_pins[AUTO_CFG_MAX_OUTS];
+	int speaker_outs;
+	hda_nid_t speaker_pins[AUTO_CFG_MAX_OUTS];
+	int hp_outs;
+	int line_out_type;	/* AUTO_PIN_XXX_OUT */
+	hda_nid_t hp_pins[AUTO_CFG_MAX_OUTS];
+	int num_inputs;
+	struct auto_pin_cfg_item inputs[AUTO_CFG_MAX_INS];
+	int dig_outs;
+	hda_nid_t dig_out_pins[2];
+	hda_nid_t dig_in_pin;
+	hda_nid_t mono_out_pin;
+	int dig_out_type[2]; /* HDA_PCM_TYPE_XXX */
+	int dig_in_type; /* HDA_PCM_TYPE_XXX */
+};
+
+/* bit-flags for snd_hda_parse_pin_def_config() behavior */
+#define HDA_PINCFG_NO_HP_FIXUP	(1 << 0) /* no HP-split */
+#define HDA_PINCFG_NO_LO_FIXUP	(1 << 1) /* don't take other outs as LO */
+
+int snd_hda_parse_pin_defcfg(struct hda_codec *codec,
+			     struct auto_pin_cfg *cfg,
+			     const hda_nid_t *ignore_nids,
+			     unsigned int cond_flags);
+
+/* older function */
+#define snd_hda_parse_pin_def_config(codec, cfg, ignore) \
+	snd_hda_parse_pin_defcfg(codec, cfg, ignore, 0)
+
+/*
+ */
+
+struct hda_gen_spec {
+	/* fix-up list */
+	int fixup_id;
+	const struct hda_fixup *fixup_list;
+	const char *fixup_name;
+
+	/* additional init verbs */
+	struct snd_array verbs;
+};
+
+
+/*
+ * Fix-up pin default configurations and add default verbs
+ */
+
+struct hda_pintbl {
+	hda_nid_t nid;
+	u32 val;
+};
+
+struct hda_model_fixup {
+	const int id;
+	const char *name;
+};
+
+struct hda_fixup {
+	int type;
+	bool chained;
+	int chain_id;
+	union {
+		const struct hda_pintbl *pins;
+		const struct hda_verb *verbs;
+		void (*func)(struct hda_codec *codec,
+			     const struct hda_fixup *fix,
+			     int action);
+	} v;
+};
+
+/* fixup types */
+enum {
+	HDA_FIXUP_INVALID,
+	HDA_FIXUP_PINS,
+	HDA_FIXUP_VERBS,
+	HDA_FIXUP_FUNC,
+};
+
+/* fixup action definitions */
+enum {
+	HDA_FIXUP_ACT_PRE_PROBE,
+	HDA_FIXUP_ACT_PROBE,
+	HDA_FIXUP_ACT_INIT,
+	HDA_FIXUP_ACT_BUILD,
+};
+
+int snd_hda_gen_add_verbs(struct hda_gen_spec *spec,
+			  const struct hda_verb *list);
+void snd_hda_gen_apply_verbs(struct hda_codec *codec);
+void snd_hda_apply_pincfgs(struct hda_codec *codec,
+			   const struct hda_pintbl *cfg);
+void snd_hda_apply_fixup(struct hda_codec *codec, int action);
+void snd_hda_pick_fixup(struct hda_codec *codec,
+			const struct hda_model_fixup *models,
+			const struct snd_pci_quirk *quirk,
+			const struct hda_fixup *fixlist);
+
+#endif /* __SOUND_HDA_AUTO_PARSER_H */
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 841475c..eb09a33 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -334,54 +334,23 @@
 	return NULL;
 }
 
-/**
- * snd_hda_get_conn_list - get connection list
- * @codec: the HDA codec
- * @nid: NID to parse
- * @listp: the pointer to store NID list
- *
- * Parses the connection list of the given widget and stores the list
- * of NIDs.
- *
- * Returns the number of connections, or a negative error code.
- */
-int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid,
-			  const hda_nid_t **listp)
+/* read the connection and add to the cache */
+static int read_and_add_raw_conns(struct hda_codec *codec, hda_nid_t nid)
 {
-	struct snd_array *array = &codec->conn_lists;
-	int len, err;
 	hda_nid_t list[HDA_MAX_CONNECTIONS];
-	hda_nid_t *p;
-	bool added = false;
+	int len;
 
- again:
-	/* if the connection-list is already cached, read it */
-	p = lookup_conn_list(array, nid);
-	if (p) {
-		if (listp)
-			*listp = p + 2;
-		return p[1];
-	}
-	if (snd_BUG_ON(added))
-		return -EINVAL;
-
-	/* read the connection and add to the cache */
-	len = snd_hda_get_raw_connections(codec, nid, list, HDA_MAX_CONNECTIONS);
+	len = snd_hda_get_raw_connections(codec, nid, list, ARRAY_SIZE(list));
 	if (len < 0)
 		return len;
-	err = snd_hda_override_conn_list(codec, nid, len, list);
-	if (err < 0)
-		return err;
-	added = true;
-	goto again;
+	return snd_hda_override_conn_list(codec, nid, len, list);
 }
-EXPORT_SYMBOL_HDA(snd_hda_get_conn_list);
 
 /**
  * snd_hda_get_connections - copy connection list
  * @codec: the HDA codec
  * @nid: NID to parse
- * @conn_list: connection list array
+ * @conn_list: connection list array; when NULL, checks only the size
  * @max_conns: max. number of connections to store
  *
  * Parses the connection list of the given widget and stores the list
@@ -390,21 +359,41 @@
  * Returns the number of connections, or a negative error code.
  */
 int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
-			     hda_nid_t *conn_list, int max_conns)
+			    hda_nid_t *conn_list, int max_conns)
 {
-	const hda_nid_t *list;
-	int len = snd_hda_get_conn_list(codec, nid, &list);
+	struct snd_array *array = &codec->conn_lists;
+	int len;
+	hda_nid_t *p;
+	bool added = false;
 
-	if (len <= 0)
-		return len;
-	if (len > max_conns) {
-		snd_printk(KERN_ERR "hda_codec: "
-			   "Too many connections %d for NID 0x%x\n",
-			   len, nid);
-		return -EINVAL;
+ again:
+	mutex_lock(&codec->hash_mutex);
+	len = -1;
+	/* if the connection-list is already cached, read it */
+	p = lookup_conn_list(array, nid);
+	if (p) {
+		len = p[1];
+		if (conn_list && len > max_conns) {
+			snd_printk(KERN_ERR "hda_codec: "
+				   "Too many connections %d for NID 0x%x\n",
+				   len, nid);
+			mutex_unlock(&codec->hash_mutex);
+			return -EINVAL;
+		}
+		if (conn_list && len)
+			memcpy(conn_list, p + 2, len * sizeof(hda_nid_t));
 	}
-	memcpy(conn_list, list, len * sizeof(hda_nid_t));
-	return len;
+	mutex_unlock(&codec->hash_mutex);
+	if (len >= 0)
+		return len;
+	if (snd_BUG_ON(added))
+		return -EINVAL;
+
+	len = read_and_add_raw_conns(codec, nid);
+	if (len < 0)
+		return len;
+	added = true;
+	goto again;
 }
 EXPORT_SYMBOL_HDA(snd_hda_get_connections);
 
@@ -543,6 +532,7 @@
 	hda_nid_t *p;
 	int i, old_used;
 
+	mutex_lock(&codec->hash_mutex);
 	p = lookup_conn_list(array, nid);
 	if (p)
 		*p = -1; /* invalidate the old entry */
@@ -553,10 +543,12 @@
 	for (i = 0; i < len; i++)
 		if (!add_conn_list(array, list[i]))
 			goto error_add;
+	mutex_unlock(&codec->hash_mutex);
 	return 0;
 
  error_add:
 	array->used = old_used;
+	mutex_unlock(&codec->hash_mutex);
 	return -ENOMEM;
 }
 EXPORT_SYMBOL_HDA(snd_hda_override_conn_list);
@@ -1255,6 +1247,7 @@
 	codec->addr = codec_addr;
 	mutex_init(&codec->spdif_mutex);
 	mutex_init(&codec->control_mutex);
+	mutex_init(&codec->hash_mutex);
 	init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
 	init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
 	snd_array_init(&codec->mixers, sizeof(struct hda_nid_item), 32);
@@ -1264,15 +1257,9 @@
 	snd_array_init(&codec->cvt_setups, sizeof(struct hda_cvt_setup), 8);
 	snd_array_init(&codec->conn_lists, sizeof(hda_nid_t), 64);
 	snd_array_init(&codec->spdif_out, sizeof(struct hda_spdif_out), 16);
-	if (codec->bus->modelname) {
-		codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL);
-		if (!codec->modelname) {
-			snd_hda_codec_free(codec);
-			return -ENODEV;
-		}
-	}
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
+	spin_lock_init(&codec->power_lock);
 	INIT_DELAYED_WORK(&codec->power_work, hda_power_work);
 	/* snd_hda_codec_new() marks the codec as power-up, and leave it as is.
 	 * the caller has to power down appropriatley after initialization
@@ -1281,6 +1268,14 @@
 	hda_keep_power_on(codec);
 #endif
 
+	if (codec->bus->modelname) {
+		codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL);
+		if (!codec->modelname) {
+			snd_hda_codec_free(codec);
+			return -ENODEV;
+		}
+	}
+
 	list_add_tail(&codec->list, &bus->codec_list);
 	bus->caddr_tbl[codec_addr] = codec;
 
@@ -1603,6 +1598,60 @@
 	return (struct hda_amp_info *)get_alloc_hash(&codec->amp_cache, key);
 }
 
+/* overwrite the value with the key in the caps hash */
+static int write_caps_hash(struct hda_codec *codec, u32 key, unsigned int val)
+{
+	struct hda_amp_info *info;
+
+	mutex_lock(&codec->hash_mutex);
+	info = get_alloc_amp_hash(codec, key);
+	if (!info) {
+		mutex_unlock(&codec->hash_mutex);
+		return -EINVAL;
+	}
+	info->amp_caps = val;
+	info->head.val |= INFO_AMP_CAPS;
+	mutex_unlock(&codec->hash_mutex);
+	return 0;
+}
+
+/* query the value from the caps hash; if not found, fetch the current
+ * value from the given function and store in the hash
+ */
+static unsigned int
+query_caps_hash(struct hda_codec *codec, hda_nid_t nid, int dir, u32 key,
+		unsigned int (*func)(struct hda_codec *, hda_nid_t, int))
+{
+	struct hda_amp_info *info;
+	unsigned int val;
+
+	mutex_lock(&codec->hash_mutex);
+	info = get_alloc_amp_hash(codec, key);
+	if (!info) {
+		mutex_unlock(&codec->hash_mutex);
+		return 0;
+	}
+	if (!(info->head.val & INFO_AMP_CAPS)) {
+		mutex_unlock(&codec->hash_mutex); /* for reentrance */
+		val = func(codec, nid, dir);
+		write_caps_hash(codec, key, val);
+	} else {
+		val = info->amp_caps;
+		mutex_unlock(&codec->hash_mutex);
+	}
+	return val;
+}
+
+static unsigned int read_amp_cap(struct hda_codec *codec, hda_nid_t nid,
+				 int direction)
+{
+	if (!(get_wcaps(codec, nid) & AC_WCAP_AMP_OVRD))
+		nid = codec->afg;
+	return snd_hda_param_read(codec, nid,
+				  direction == HDA_OUTPUT ?
+				  AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP);
+}
+
 /**
  * query_amp_caps - query AMP capabilities
  * @codec: the HD-auio codec
@@ -1617,22 +1666,9 @@
  */
 u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction)
 {
-	struct hda_amp_info *info;
-
-	info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, 0));
-	if (!info)
-		return 0;
-	if (!(info->head.val & INFO_AMP_CAPS)) {
-		if (!(get_wcaps(codec, nid) & AC_WCAP_AMP_OVRD))
-			nid = codec->afg;
-		info->amp_caps = snd_hda_param_read(codec, nid,
-						    direction == HDA_OUTPUT ?
-						    AC_PAR_AMP_OUT_CAP :
-						    AC_PAR_AMP_IN_CAP);
-		if (info->amp_caps)
-			info->head.val |= INFO_AMP_CAPS;
-	}
-	return info->amp_caps;
+	return query_caps_hash(codec, nid, direction,
+			       HDA_HASH_KEY(nid, direction, 0),
+			       read_amp_cap);
 }
 EXPORT_SYMBOL_HDA(query_amp_caps);
 
@@ -1652,34 +1688,12 @@
 int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
 			      unsigned int caps)
 {
-	struct hda_amp_info *info;
-
-	info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, dir, 0));
-	if (!info)
-		return -EINVAL;
-	info->amp_caps = caps;
-	info->head.val |= INFO_AMP_CAPS;
-	return 0;
+	return write_caps_hash(codec, HDA_HASH_KEY(nid, dir, 0), caps);
 }
 EXPORT_SYMBOL_HDA(snd_hda_override_amp_caps);
 
-static unsigned int
-query_caps_hash(struct hda_codec *codec, hda_nid_t nid, u32 key,
-		unsigned int (*func)(struct hda_codec *, hda_nid_t))
-{
-	struct hda_amp_info *info;
-
-	info = get_alloc_amp_hash(codec, key);
-	if (!info)
-		return 0;
-	if (!info->head.val) {
-		info->head.val |= INFO_AMP_CAPS;
-		info->amp_caps = func(codec, nid);
-	}
-	return info->amp_caps;
-}
-
-static unsigned int read_pin_cap(struct hda_codec *codec, hda_nid_t nid)
+static unsigned int read_pin_cap(struct hda_codec *codec, hda_nid_t nid,
+				 int dir)
 {
 	return snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
 }
@@ -1697,7 +1711,7 @@
  */
 u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid)
 {
-	return query_caps_hash(codec, nid, HDA_HASH_PINCAP_KEY(nid),
+	return query_caps_hash(codec, nid, 0, HDA_HASH_PINCAP_KEY(nid),
 			       read_pin_cap);
 }
 EXPORT_SYMBOL_HDA(snd_hda_query_pin_caps);
@@ -1715,41 +1729,47 @@
 int snd_hda_override_pin_caps(struct hda_codec *codec, hda_nid_t nid,
 			      unsigned int caps)
 {
-	struct hda_amp_info *info;
-	info = get_alloc_amp_hash(codec, HDA_HASH_PINCAP_KEY(nid));
-	if (!info)
-		return -ENOMEM;
-	info->amp_caps = caps;
-	info->head.val |= INFO_AMP_CAPS;
-	return 0;
+	return write_caps_hash(codec, HDA_HASH_PINCAP_KEY(nid), caps);
 }
 EXPORT_SYMBOL_HDA(snd_hda_override_pin_caps);
 
-/*
- * read the current volume to info
- * if the cache exists, read the cache value.
+/* read or sync the hash value with the current value;
+ * call within hash_mutex
  */
-static unsigned int get_vol_mute(struct hda_codec *codec,
-				 struct hda_amp_info *info, hda_nid_t nid,
-				 int ch, int direction, int index)
+static struct hda_amp_info *
+update_amp_hash(struct hda_codec *codec, hda_nid_t nid, int ch,
+		int direction, int index)
 {
-	u32 val, parm;
+	struct hda_amp_info *info;
+	unsigned int parm, val = 0;
+	bool val_read = false;
 
-	if (info->head.val & INFO_AMP_VOL(ch))
-		return info->vol[ch];
-
-	parm = ch ? AC_AMP_GET_RIGHT : AC_AMP_GET_LEFT;
-	parm |= direction == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT;
-	parm |= index;
-	val = snd_hda_codec_read(codec, nid, 0,
+ retry:
+	info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, index));
+	if (!info)
+		return NULL;
+	if (!(info->head.val & INFO_AMP_VOL(ch))) {
+		if (!val_read) {
+			mutex_unlock(&codec->hash_mutex);
+			parm = ch ? AC_AMP_GET_RIGHT : AC_AMP_GET_LEFT;
+			parm |= direction == HDA_OUTPUT ?
+				AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT;
+			parm |= index;
+			val = snd_hda_codec_read(codec, nid, 0,
 				 AC_VERB_GET_AMP_GAIN_MUTE, parm);
-	info->vol[ch] = val & 0xff;
-	info->head.val |= INFO_AMP_VOL(ch);
-	return info->vol[ch];
+			val &= 0xff;
+			val_read = true;
+			mutex_lock(&codec->hash_mutex);
+			goto retry;
+		}
+		info->vol[ch] = val;
+		info->head.val |= INFO_AMP_VOL(ch);
+	}
+	return info;
 }
 
 /*
- * write the current volume in info to the h/w and update the cache
+ * write the current volume in info to the h/w
  */
 static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info,
 			 hda_nid_t nid, int ch, int direction, int index,
@@ -1766,7 +1786,6 @@
 	else
 		parm |= val;
 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, parm);
-	info->vol[ch] = val;
 }
 
 /**
@@ -1783,10 +1802,14 @@
 			   int direction, int index)
 {
 	struct hda_amp_info *info;
-	info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, index));
-	if (!info)
-		return 0;
-	return get_vol_mute(codec, info, nid, ch, direction, index);
+	unsigned int val = 0;
+
+	mutex_lock(&codec->hash_mutex);
+	info = update_amp_hash(codec, nid, ch, direction, index);
+	if (info)
+		val = info->vol[ch];
+	mutex_unlock(&codec->hash_mutex);
+	return val;
 }
 EXPORT_SYMBOL_HDA(snd_hda_codec_amp_read);
 
@@ -1808,15 +1831,23 @@
 {
 	struct hda_amp_info *info;
 
-	info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, idx));
-	if (!info)
-		return 0;
 	if (snd_BUG_ON(mask & ~0xff))
 		mask &= 0xff;
 	val &= mask;
-	val |= get_vol_mute(codec, info, nid, ch, direction, idx) & ~mask;
-	if (info->vol[ch] == val)
+
+	mutex_lock(&codec->hash_mutex);
+	info = update_amp_hash(codec, nid, ch, direction, idx);
+	if (!info) {
+		mutex_unlock(&codec->hash_mutex);
 		return 0;
+	}
+	val |= info->vol[ch] & ~mask;
+	if (info->vol[ch] == val) {
+		mutex_unlock(&codec->hash_mutex);
+		return 0;
+	}
+	info->vol[ch] = val;
+	mutex_unlock(&codec->hash_mutex);
 	put_vol_mute(codec, info, nid, ch, direction, idx, val);
 	return 1;
 }
@@ -2263,7 +2294,10 @@
 	/* OK, let it free */
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
-	cancel_delayed_work(&codec->power_work);
+	cancel_delayed_work_sync(&codec->power_work);
+	codec->power_on = 0;
+	codec->power_transition = 0;
+	codec->power_jiffies = jiffies;
 	flush_workqueue(codec->bus->workq);
 #endif
 	snd_hda_ctls_clear(codec);
@@ -2859,12 +2893,15 @@
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	int idx = kcontrol->private_value;
-	struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx);
+	struct hda_spdif_out *spdif;
 
+	mutex_lock(&codec->spdif_mutex);
+	spdif = snd_array_elem(&codec->spdif_out, idx);
 	ucontrol->value.iec958.status[0] = spdif->status & 0xff;
 	ucontrol->value.iec958.status[1] = (spdif->status >> 8) & 0xff;
 	ucontrol->value.iec958.status[2] = (spdif->status >> 16) & 0xff;
 	ucontrol->value.iec958.status[3] = (spdif->status >> 24) & 0xff;
+	mutex_unlock(&codec->spdif_mutex);
 
 	return 0;
 }
@@ -2950,12 +2987,14 @@
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	int idx = kcontrol->private_value;
-	struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx);
-	hda_nid_t nid = spdif->nid;
+	struct hda_spdif_out *spdif;
+	hda_nid_t nid;
 	unsigned short val;
 	int change;
 
 	mutex_lock(&codec->spdif_mutex);
+	spdif = snd_array_elem(&codec->spdif_out, idx);
+	nid = spdif->nid;
 	spdif->status = ucontrol->value.iec958.status[0] |
 		((unsigned int)ucontrol->value.iec958.status[1] << 8) |
 		((unsigned int)ucontrol->value.iec958.status[2] << 16) |
@@ -2977,9 +3016,12 @@
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	int idx = kcontrol->private_value;
-	struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx);
+	struct hda_spdif_out *spdif;
 
+	mutex_lock(&codec->spdif_mutex);
+	spdif = snd_array_elem(&codec->spdif_out, idx);
 	ucontrol->value.integer.value[0] = spdif->ctls & AC_DIG1_ENABLE;
+	mutex_unlock(&codec->spdif_mutex);
 	return 0;
 }
 
@@ -2999,12 +3041,14 @@
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	int idx = kcontrol->private_value;
-	struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx);
-	hda_nid_t nid = spdif->nid;
+	struct hda_spdif_out *spdif;
+	hda_nid_t nid;
 	unsigned short val;
 	int change;
 
 	mutex_lock(&codec->spdif_mutex);
+	spdif = snd_array_elem(&codec->spdif_out, idx);
+	nid = spdif->nid;
 	val = spdif->ctls & ~AC_DIG1_ENABLE;
 	if (ucontrol->value.integer.value[0])
 		val |= AC_DIG1_ENABLE;
@@ -3092,6 +3136,9 @@
 }
 EXPORT_SYMBOL_HDA(snd_hda_create_spdif_out_ctls);
 
+/* get the hda_spdif_out entry from the given NID
+ * call within spdif_mutex lock
+ */
 struct hda_spdif_out *snd_hda_spdif_out_of_nid(struct hda_codec *codec,
 					       hda_nid_t nid)
 {
@@ -3108,9 +3155,10 @@
 
 void snd_hda_spdif_ctls_unassign(struct hda_codec *codec, int idx)
 {
-	struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx);
+	struct hda_spdif_out *spdif;
 
 	mutex_lock(&codec->spdif_mutex);
+	spdif = snd_array_elem(&codec->spdif_out, idx);
 	spdif->nid = (u16)-1;
 	mutex_unlock(&codec->spdif_mutex);
 }
@@ -3118,10 +3166,11 @@
 
 void snd_hda_spdif_ctls_assign(struct hda_codec *codec, int idx, hda_nid_t nid)
 {
-	struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx);
+	struct hda_spdif_out *spdif;
 	unsigned short val;
 
 	mutex_lock(&codec->spdif_mutex);
+	spdif = snd_array_elem(&codec->spdif_out, idx);
 	if (spdif->nid != nid) {
 		spdif->nid = nid;
 		val = spdif->ctls;
@@ -3486,11 +3535,14 @@
 			    codec->afg ? codec->afg : codec->mfg,
 			    AC_PWRST_D3);
 #ifdef CONFIG_SND_HDA_POWER_SAVE
-	snd_hda_update_power_acct(codec);
 	cancel_delayed_work(&codec->power_work);
+	spin_lock(&codec->power_lock);
+	snd_hda_update_power_acct(codec);
+	trace_hda_power_down(codec);
 	codec->power_on = 0;
 	codec->power_transition = 0;
 	codec->power_jiffies = jiffies;
+	spin_unlock(&codec->power_lock);
 #endif
 }
 
@@ -3499,6 +3551,10 @@
  */
 static void hda_call_codec_resume(struct hda_codec *codec)
 {
+	/* set as if powered on for avoiding re-entering the resume
+	 * in the resume / power-save sequence
+	 */
+	hda_keep_power_on(codec);
 	hda_set_power_state(codec,
 			    codec->afg ? codec->afg : codec->mfg,
 			    AC_PWRST_D0);
@@ -3514,6 +3570,7 @@
 		snd_hda_codec_resume_amp(codec);
 		snd_hda_codec_resume_cache(codec);
 	}
+	snd_hda_power_down(codec); /* flag down before returning */
 }
 #endif /* CONFIG_PM */
 
@@ -3665,7 +3722,8 @@
 }
 EXPORT_SYMBOL_HDA(snd_hda_calc_stream_format);
 
-static unsigned int get_pcm_param(struct hda_codec *codec, hda_nid_t nid)
+static unsigned int get_pcm_param(struct hda_codec *codec, hda_nid_t nid,
+				  int dir)
 {
 	unsigned int val = 0;
 	if (nid != codec->afg &&
@@ -3680,11 +3738,12 @@
 
 static unsigned int query_pcm_param(struct hda_codec *codec, hda_nid_t nid)
 {
-	return query_caps_hash(codec, nid, HDA_HASH_PARPCM_KEY(nid),
+	return query_caps_hash(codec, nid, 0, HDA_HASH_PARPCM_KEY(nid),
 			       get_pcm_param);
 }
 
-static unsigned int get_stream_param(struct hda_codec *codec, hda_nid_t nid)
+static unsigned int get_stream_param(struct hda_codec *codec, hda_nid_t nid,
+				     int dir)
 {
 	unsigned int streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
 	if (!streams || streams == -1)
@@ -3696,7 +3755,7 @@
 
 static unsigned int query_stream_param(struct hda_codec *codec, hda_nid_t nid)
 {
-	return query_caps_hash(codec, nid, HDA_HASH_PARSTR_KEY(nid),
+	return query_caps_hash(codec, nid, 0, HDA_HASH_PARSTR_KEY(nid),
 			       get_stream_param);
 }
 
@@ -3775,11 +3834,13 @@
 					bps = 20;
 			}
 		}
+#if 0 /* FIXME: CS4206 doesn't work, which is the only codec supporting float */
 		if (streams & AC_SUPFMT_FLOAT32) {
 			formats |= SNDRV_PCM_FMTBIT_FLOAT_LE;
 			if (!bps)
 				bps = 32;
 		}
+#endif
 		if (streams == AC_SUPFMT_AC3) {
 			/* should be exclusive */
 			/* temporary hack: we have still no proper support
@@ -4283,12 +4344,18 @@
 		container_of(work, struct hda_codec, power_work.work);
 	struct hda_bus *bus = codec->bus;
 
-	if (!codec->power_on || codec->power_count) {
-		codec->power_transition = 0;
+	spin_lock(&codec->power_lock);
+	if (codec->power_transition > 0) { /* during power-up sequence? */
+		spin_unlock(&codec->power_lock);
 		return;
 	}
+	if (!codec->power_on || codec->power_count) {
+		codec->power_transition = 0;
+		spin_unlock(&codec->power_lock);
+		return;
+	}
+	spin_unlock(&codec->power_lock);
 
-	trace_hda_power_down(codec);
 	hda_call_codec_suspend(codec);
 	if (bus->ops.pm_notify)
 		bus->ops.pm_notify(bus);
@@ -4296,9 +4363,11 @@
 
 static void hda_keep_power_on(struct hda_codec *codec)
 {
+	spin_lock(&codec->power_lock);
 	codec->power_count++;
 	codec->power_on = 1;
 	codec->power_jiffies = jiffies;
+	spin_unlock(&codec->power_lock);
 }
 
 /* update the power on/off account with the current jiffies */
@@ -4323,19 +4392,31 @@
 {
 	struct hda_bus *bus = codec->bus;
 
+	spin_lock(&codec->power_lock);
 	codec->power_count++;
-	if (codec->power_on || codec->power_transition)
+	if (codec->power_on || codec->power_transition > 0) {
+		spin_unlock(&codec->power_lock);
 		return;
+	}
+	spin_unlock(&codec->power_lock);
 
+	cancel_delayed_work_sync(&codec->power_work);
+
+	spin_lock(&codec->power_lock);
 	trace_hda_power_up(codec);
 	snd_hda_update_power_acct(codec);
 	codec->power_on = 1;
 	codec->power_jiffies = jiffies;
+	codec->power_transition = 1; /* avoid reentrance */
+	spin_unlock(&codec->power_lock);
+
 	if (bus->ops.pm_notify)
 		bus->ops.pm_notify(bus);
 	hda_call_codec_resume(codec);
-	cancel_delayed_work(&codec->power_work);
+
+	spin_lock(&codec->power_lock);
 	codec->power_transition = 0;
+	spin_unlock(&codec->power_lock);
 }
 EXPORT_SYMBOL_HDA(snd_hda_power_up);
 
@@ -4351,14 +4432,18 @@
  */
 void snd_hda_power_down(struct hda_codec *codec)
 {
+	spin_lock(&codec->power_lock);
 	--codec->power_count;
-	if (!codec->power_on || codec->power_count || codec->power_transition)
+	if (!codec->power_on || codec->power_count || codec->power_transition) {
+		spin_unlock(&codec->power_lock);
 		return;
+	}
 	if (power_save(codec)) {
-		codec->power_transition = 1; /* avoid reentrance */
+		codec->power_transition = -1; /* avoid reentrance */
 		queue_delayed_work(codec->bus->workq, &codec->power_work,
 				msecs_to_jiffies(power_save(codec) * 1000));
 	}
+	spin_unlock(&codec->power_lock);
 }
 EXPORT_SYMBOL_HDA(snd_hda_power_down);
 
@@ -4710,11 +4795,11 @@
 {
 	const hda_nid_t *nids = mout->dac_nids;
 	int chs = substream->runtime->channels;
-	struct hda_spdif_out *spdif =
-			snd_hda_spdif_out_of_nid(codec, mout->dig_out_nid);
+	struct hda_spdif_out *spdif;
 	int i;
 
 	mutex_lock(&codec->spdif_mutex);
+	spdif = snd_hda_spdif_out_of_nid(codec, mout->dig_out_nid);
 	if (mout->dig_out_nid && mout->share_spdif &&
 	    mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
 		if (chs == 2 &&
@@ -4795,601 +4880,58 @@
 }
 EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_cleanup);
 
-/*
- * Helper for automatic pin configuration
- */
-
-static int is_in_nid_list(hda_nid_t nid, const hda_nid_t *list)
-{
-	for (; *list; list++)
-		if (*list == nid)
-			return 1;
-	return 0;
-}
-
-
-/*
- * Sort an associated group of pins according to their sequence numbers.
- */
-static void sort_pins_by_sequence(hda_nid_t *pins, short *sequences,
-				  int num_pins)
-{
-	int i, j;
-	short seq;
-	hda_nid_t nid;
-
-	for (i = 0; i < num_pins; i++) {
-		for (j = i + 1; j < num_pins; j++) {
-			if (sequences[i] > sequences[j]) {
-				seq = sequences[i];
-				sequences[i] = sequences[j];
-				sequences[j] = seq;
-				nid = pins[i];
-				pins[i] = pins[j];
-				pins[j] = nid;
-			}
-		}
-	}
-}
-
-
-/* add the found input-pin to the cfg->inputs[] table */
-static void add_auto_cfg_input_pin(struct auto_pin_cfg *cfg, hda_nid_t nid,
-				   int type)
-{
-	if (cfg->num_inputs < AUTO_CFG_MAX_INS) {
-		cfg->inputs[cfg->num_inputs].pin = nid;
-		cfg->inputs[cfg->num_inputs].type = type;
-		cfg->num_inputs++;
-	}
-}
-
-/* sort inputs in the order of AUTO_PIN_* type */
-static void sort_autocfg_input_pins(struct auto_pin_cfg *cfg)
-{
-	int i, j;
-
-	for (i = 0; i < cfg->num_inputs; i++) {
-		for (j = i + 1; j < cfg->num_inputs; j++) {
-			if (cfg->inputs[i].type > cfg->inputs[j].type) {
-				struct auto_pin_cfg_item tmp;
-				tmp = cfg->inputs[i];
-				cfg->inputs[i] = cfg->inputs[j];
-				cfg->inputs[j] = tmp;
-			}
-		}
-	}
-}
-
-/* Reorder the surround channels
- * ALSA sequence is front/surr/clfe/side
- * HDA sequence is:
- *    4-ch: front/surr  =>  OK as it is
- *    6-ch: front/clfe/surr
- *    8-ch: front/clfe/rear/side|fc
- */
-static void reorder_outputs(unsigned int nums, hda_nid_t *pins)
-{
-	hda_nid_t nid;
-
-	switch (nums) {
-	case 3:
-	case 4:
-		nid = pins[1];
-		pins[1] = pins[2];
-		pins[2] = nid;
-		break;
-	}
-}
-
-/*
- * Parse all pin widgets and store the useful pin nids to cfg
- *
- * The number of line-outs or any primary output is stored in line_outs,
- * and the corresponding output pins are assigned to line_out_pins[],
- * in the order of front, rear, CLFE, side, ...
- *
- * If more extra outputs (speaker and headphone) are found, the pins are
- * assisnged to hp_pins[] and speaker_pins[], respectively.  If no line-out jack
- * is detected, one of speaker of HP pins is assigned as the primary
- * output, i.e. to line_out_pins[0].  So, line_outs is always positive
- * if any analog output exists.
- *
- * The analog input pins are assigned to inputs array.
- * The digital input/output pins are assigned to dig_in_pin and dig_out_pin,
- * respectively.
- */
-int snd_hda_parse_pin_defcfg(struct hda_codec *codec,
-			     struct auto_pin_cfg *cfg,
-			     const hda_nid_t *ignore_nids,
-			     unsigned int cond_flags)
-{
-	hda_nid_t nid, end_nid;
-	short seq, assoc_line_out;
-	short sequences_line_out[ARRAY_SIZE(cfg->line_out_pins)];
-	short sequences_speaker[ARRAY_SIZE(cfg->speaker_pins)];
-	short sequences_hp[ARRAY_SIZE(cfg->hp_pins)];
-	int i;
-
-	memset(cfg, 0, sizeof(*cfg));
-
-	memset(sequences_line_out, 0, sizeof(sequences_line_out));
-	memset(sequences_speaker, 0, sizeof(sequences_speaker));
-	memset(sequences_hp, 0, sizeof(sequences_hp));
-	assoc_line_out = 0;
-
-	codec->ignore_misc_bit = true;
-	end_nid = codec->start_nid + codec->num_nodes;
-	for (nid = codec->start_nid; nid < end_nid; nid++) {
-		unsigned int wid_caps = get_wcaps(codec, nid);
-		unsigned int wid_type = get_wcaps_type(wid_caps);
-		unsigned int def_conf;
-		short assoc, loc, conn, dev;
-
-		/* read all default configuration for pin complex */
-		if (wid_type != AC_WID_PIN)
-			continue;
-		/* ignore the given nids (e.g. pc-beep returns error) */
-		if (ignore_nids && is_in_nid_list(nid, ignore_nids))
-			continue;
-
-		def_conf = snd_hda_codec_get_pincfg(codec, nid);
-		if (!(get_defcfg_misc(snd_hda_codec_get_pincfg(codec, nid)) &
-		      AC_DEFCFG_MISC_NO_PRESENCE))
-			codec->ignore_misc_bit = false;
-		conn = get_defcfg_connect(def_conf);
-		if (conn == AC_JACK_PORT_NONE)
-			continue;
-		loc = get_defcfg_location(def_conf);
-		dev = get_defcfg_device(def_conf);
-
-		/* workaround for buggy BIOS setups */
-		if (dev == AC_JACK_LINE_OUT) {
-			if (conn == AC_JACK_PORT_FIXED)
-				dev = AC_JACK_SPEAKER;
-		}
-
-		switch (dev) {
-		case AC_JACK_LINE_OUT:
-			seq = get_defcfg_sequence(def_conf);
-			assoc = get_defcfg_association(def_conf);
-
-			if (!(wid_caps & AC_WCAP_STEREO))
-				if (!cfg->mono_out_pin)
-					cfg->mono_out_pin = nid;
-			if (!assoc)
-				continue;
-			if (!assoc_line_out)
-				assoc_line_out = assoc;
-			else if (assoc_line_out != assoc)
-				continue;
-			if (cfg->line_outs >= ARRAY_SIZE(cfg->line_out_pins))
-				continue;
-			cfg->line_out_pins[cfg->line_outs] = nid;
-			sequences_line_out[cfg->line_outs] = seq;
-			cfg->line_outs++;
-			break;
-		case AC_JACK_SPEAKER:
-			seq = get_defcfg_sequence(def_conf);
-			assoc = get_defcfg_association(def_conf);
-			if (cfg->speaker_outs >= ARRAY_SIZE(cfg->speaker_pins))
-				continue;
-			cfg->speaker_pins[cfg->speaker_outs] = nid;
-			sequences_speaker[cfg->speaker_outs] = (assoc << 4) | seq;
-			cfg->speaker_outs++;
-			break;
-		case AC_JACK_HP_OUT:
-			seq = get_defcfg_sequence(def_conf);
-			assoc = get_defcfg_association(def_conf);
-			if (cfg->hp_outs >= ARRAY_SIZE(cfg->hp_pins))
-				continue;
-			cfg->hp_pins[cfg->hp_outs] = nid;
-			sequences_hp[cfg->hp_outs] = (assoc << 4) | seq;
-			cfg->hp_outs++;
-			break;
-		case AC_JACK_MIC_IN:
-			add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_MIC);
-			break;
-		case AC_JACK_LINE_IN:
-			add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_LINE_IN);
-			break;
-		case AC_JACK_CD:
-			add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_CD);
-			break;
-		case AC_JACK_AUX:
-			add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_AUX);
-			break;
-		case AC_JACK_SPDIF_OUT:
-		case AC_JACK_DIG_OTHER_OUT:
-			if (cfg->dig_outs >= ARRAY_SIZE(cfg->dig_out_pins))
-				continue;
-			cfg->dig_out_pins[cfg->dig_outs] = nid;
-			cfg->dig_out_type[cfg->dig_outs] =
-				(loc == AC_JACK_LOC_HDMI) ?
-				HDA_PCM_TYPE_HDMI : HDA_PCM_TYPE_SPDIF;
-			cfg->dig_outs++;
-			break;
-		case AC_JACK_SPDIF_IN:
-		case AC_JACK_DIG_OTHER_IN:
-			cfg->dig_in_pin = nid;
-			if (loc == AC_JACK_LOC_HDMI)
-				cfg->dig_in_type = HDA_PCM_TYPE_HDMI;
-			else
-				cfg->dig_in_type = HDA_PCM_TYPE_SPDIF;
-			break;
-		}
-	}
-
-	/* FIX-UP:
-	 * If no line-out is defined but multiple HPs are found,
-	 * some of them might be the real line-outs.
-	 */
-	if (!cfg->line_outs && cfg->hp_outs > 1 &&
-	    !(cond_flags & HDA_PINCFG_NO_HP_FIXUP)) {
-		int i = 0;
-		while (i < cfg->hp_outs) {
-			/* The real HPs should have the sequence 0x0f */
-			if ((sequences_hp[i] & 0x0f) == 0x0f) {
-				i++;
-				continue;
-			}
-			/* Move it to the line-out table */
-			cfg->line_out_pins[cfg->line_outs] = cfg->hp_pins[i];
-			sequences_line_out[cfg->line_outs] = sequences_hp[i];
-			cfg->line_outs++;
-			cfg->hp_outs--;
-			memmove(cfg->hp_pins + i, cfg->hp_pins + i + 1,
-				sizeof(cfg->hp_pins[0]) * (cfg->hp_outs - i));
-			memmove(sequences_hp + i, sequences_hp + i + 1,
-				sizeof(sequences_hp[0]) * (cfg->hp_outs - i));
-		}
-		memset(cfg->hp_pins + cfg->hp_outs, 0,
-		       sizeof(hda_nid_t) * (AUTO_CFG_MAX_OUTS - cfg->hp_outs));
-		if (!cfg->hp_outs)
-			cfg->line_out_type = AUTO_PIN_HP_OUT;
-
-	}
-
-	/* sort by sequence */
-	sort_pins_by_sequence(cfg->line_out_pins, sequences_line_out,
-			      cfg->line_outs);
-	sort_pins_by_sequence(cfg->speaker_pins, sequences_speaker,
-			      cfg->speaker_outs);
-	sort_pins_by_sequence(cfg->hp_pins, sequences_hp,
-			      cfg->hp_outs);
-
-	/*
-	 * FIX-UP: if no line-outs are detected, try to use speaker or HP pin
-	 * as a primary output
-	 */
-	if (!cfg->line_outs &&
-	    !(cond_flags & HDA_PINCFG_NO_LO_FIXUP)) {
-		if (cfg->speaker_outs) {
-			cfg->line_outs = cfg->speaker_outs;
-			memcpy(cfg->line_out_pins, cfg->speaker_pins,
-			       sizeof(cfg->speaker_pins));
-			cfg->speaker_outs = 0;
-			memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins));
-			cfg->line_out_type = AUTO_PIN_SPEAKER_OUT;
-		} else if (cfg->hp_outs) {
-			cfg->line_outs = cfg->hp_outs;
-			memcpy(cfg->line_out_pins, cfg->hp_pins,
-			       sizeof(cfg->hp_pins));
-			cfg->hp_outs = 0;
-			memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins));
-			cfg->line_out_type = AUTO_PIN_HP_OUT;
-		}
-	}
-
-	reorder_outputs(cfg->line_outs, cfg->line_out_pins);
-	reorder_outputs(cfg->hp_outs, cfg->hp_pins);
-	reorder_outputs(cfg->speaker_outs, cfg->speaker_pins);
-
-	sort_autocfg_input_pins(cfg);
-
-	/*
-	 * debug prints of the parsed results
-	 */
-	snd_printd("autoconfig: line_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x) type:%s\n",
-		   cfg->line_outs, cfg->line_out_pins[0], cfg->line_out_pins[1],
-		   cfg->line_out_pins[2], cfg->line_out_pins[3],
-		   cfg->line_out_pins[4],
-		   cfg->line_out_type == AUTO_PIN_HP_OUT ? "hp" :
-		   (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT ?
-		    "speaker" : "line"));
-	snd_printd("   speaker_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
-		   cfg->speaker_outs, cfg->speaker_pins[0],
-		   cfg->speaker_pins[1], cfg->speaker_pins[2],
-		   cfg->speaker_pins[3], cfg->speaker_pins[4]);
-	snd_printd("   hp_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
-		   cfg->hp_outs, cfg->hp_pins[0],
-		   cfg->hp_pins[1], cfg->hp_pins[2],
-		   cfg->hp_pins[3], cfg->hp_pins[4]);
-	snd_printd("   mono: mono_out=0x%x\n", cfg->mono_out_pin);
-	if (cfg->dig_outs)
-		snd_printd("   dig-out=0x%x/0x%x\n",
-			   cfg->dig_out_pins[0], cfg->dig_out_pins[1]);
-	snd_printd("   inputs:");
-	for (i = 0; i < cfg->num_inputs; i++) {
-		snd_printd(" %s=0x%x",
-			    hda_get_autocfg_input_label(codec, cfg, i),
-			    cfg->inputs[i].pin);
-	}
-	snd_printd("\n");
-	if (cfg->dig_in_pin)
-		snd_printd("   dig-in=0x%x\n", cfg->dig_in_pin);
-
-	return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_parse_pin_defcfg);
-
-int snd_hda_get_input_pin_attr(unsigned int def_conf)
-{
-	unsigned int loc = get_defcfg_location(def_conf);
-	unsigned int conn = get_defcfg_connect(def_conf);
-	if (conn == AC_JACK_PORT_NONE)
-		return INPUT_PIN_ATTR_UNUSED;
-	/* Windows may claim the internal mic to be BOTH, too */
-	if (conn == AC_JACK_PORT_FIXED || conn == AC_JACK_PORT_BOTH)
-		return INPUT_PIN_ATTR_INT;
-	if ((loc & 0x30) == AC_JACK_LOC_INTERNAL)
-		return INPUT_PIN_ATTR_INT;
-	if ((loc & 0x30) == AC_JACK_LOC_SEPARATE)
-		return INPUT_PIN_ATTR_DOCK;
-	if (loc == AC_JACK_LOC_REAR)
-		return INPUT_PIN_ATTR_REAR;
-	if (loc == AC_JACK_LOC_FRONT)
-		return INPUT_PIN_ATTR_FRONT;
-	return INPUT_PIN_ATTR_NORMAL;
-}
-EXPORT_SYMBOL_HDA(snd_hda_get_input_pin_attr);
-
 /**
- * hda_get_input_pin_label - Give a label for the given input pin
+ * snd_hda_get_default_vref - Get the default (mic) VREF pin bits
  *
- * When check_location is true, the function checks the pin location
- * for mic and line-in pins, and set an appropriate prefix like "Front",
- * "Rear", "Internal".
+ * Guess the suitable VREF pin bits to be set as the pin-control value.
+ * Note: the function doesn't set the AC_PINCTL_IN_EN bit.
  */
-
-static const char *hda_get_input_pin_label(struct hda_codec *codec,
-					   hda_nid_t pin, bool check_location)
+unsigned int snd_hda_get_default_vref(struct hda_codec *codec, hda_nid_t pin)
 {
-	unsigned int def_conf;
-	static const char * const mic_names[] = {
-		"Internal Mic", "Dock Mic", "Mic", "Front Mic", "Rear Mic",
-	};
-	int attr;
-
-	def_conf = snd_hda_codec_get_pincfg(codec, pin);
-
-	switch (get_defcfg_device(def_conf)) {
-	case AC_JACK_MIC_IN:
-		if (!check_location)
-			return "Mic";
-		attr = snd_hda_get_input_pin_attr(def_conf);
-		if (!attr)
-			return "None";
-		return mic_names[attr - 1];
-	case AC_JACK_LINE_IN:
-		if (!check_location)
-			return "Line";
-		attr = snd_hda_get_input_pin_attr(def_conf);
-		if (!attr)
-			return "None";
-		if (attr == INPUT_PIN_ATTR_DOCK)
-			return "Dock Line";
-		return "Line";
-	case AC_JACK_AUX:
-		return "Aux";
-	case AC_JACK_CD:
-		return "CD";
-	case AC_JACK_SPDIF_IN:
-		return "SPDIF In";
-	case AC_JACK_DIG_OTHER_IN:
-		return "Digital In";
-	default:
-		return "Misc";
-	}
+	unsigned int pincap;
+	unsigned int oldval;
+	oldval = snd_hda_codec_read(codec, pin, 0,
+				    AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+	pincap = snd_hda_query_pin_caps(codec, pin);
+	pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
+	/* Exception: if the default pin setup is vref50, we give it priority */
+	if ((pincap & AC_PINCAP_VREF_80) && oldval != PIN_VREF50)
+		return AC_PINCTL_VREF_80;
+	else if (pincap & AC_PINCAP_VREF_50)
+		return AC_PINCTL_VREF_50;
+	else if (pincap & AC_PINCAP_VREF_100)
+		return AC_PINCTL_VREF_100;
+	else if (pincap & AC_PINCAP_VREF_GRD)
+		return AC_PINCTL_VREF_GRD;
+	return AC_PINCTL_VREF_HIZ;
 }
+EXPORT_SYMBOL_HDA(snd_hda_get_default_vref);
 
-/* Check whether the location prefix needs to be added to the label.
- * If all mic-jacks are in the same location (e.g. rear panel), we don't
- * have to put "Front" prefix to each label.  In such a case, returns false.
- */
-static int check_mic_location_need(struct hda_codec *codec,
-				   const struct auto_pin_cfg *cfg,
-				   int input)
+int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin,
+			 unsigned int val, bool cached)
 {
-	unsigned int defc;
-	int i, attr, attr2;
-
-	defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[input].pin);
-	attr = snd_hda_get_input_pin_attr(defc);
-	/* for internal or docking mics, we need locations */
-	if (attr <= INPUT_PIN_ATTR_NORMAL)
-		return 1;
-
-	attr = 0;
-	for (i = 0; i < cfg->num_inputs; i++) {
-		defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[i].pin);
-		attr2 = snd_hda_get_input_pin_attr(defc);
-		if (attr2 >= INPUT_PIN_ATTR_NORMAL) {
-			if (attr && attr != attr2)
-				return 1; /* different locations found */
-			attr = attr2;
+	if (val) {
+		unsigned int cap = snd_hda_query_pin_caps(codec, pin);
+		if (cap && (val & AC_PINCTL_OUT_EN)) {
+			if (!(cap & AC_PINCAP_OUT))
+				val &= ~(AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN);
+			else if ((val & AC_PINCTL_HP_EN) &&
+				 !(cap & AC_PINCAP_HP_DRV))
+				val &= ~AC_PINCTL_HP_EN;
+		}
+		if (cap && (val & AC_PINCTL_IN_EN)) {
+			if (!(cap & AC_PINCAP_IN))
+				val &= ~(AC_PINCTL_IN_EN | AC_PINCTL_VREFEN);
 		}
 	}
-	return 0;
+	if (cached)
+		return snd_hda_codec_update_cache(codec, pin, 0,
+				AC_VERB_SET_PIN_WIDGET_CONTROL, val);
+	else
+		return snd_hda_codec_write(codec, pin, 0,
+					   AC_VERB_SET_PIN_WIDGET_CONTROL, val);
 }
-
-/**
- * hda_get_autocfg_input_label - Get a label for the given input
- *
- * Get a label for the given input pin defined by the autocfg item.
- * Unlike hda_get_input_pin_label(), this function checks all inputs
- * defined in autocfg and avoids the redundant mic/line prefix as much as
- * possible.
- */
-const char *hda_get_autocfg_input_label(struct hda_codec *codec,
-					const struct auto_pin_cfg *cfg,
-					int input)
-{
-	int type = cfg->inputs[input].type;
-	int has_multiple_pins = 0;
-
-	if ((input > 0 && cfg->inputs[input - 1].type == type) ||
-	    (input < cfg->num_inputs - 1 && cfg->inputs[input + 1].type == type))
-		has_multiple_pins = 1;
-	if (has_multiple_pins && type == AUTO_PIN_MIC)
-		has_multiple_pins &= check_mic_location_need(codec, cfg, input);
-	return hda_get_input_pin_label(codec, cfg->inputs[input].pin,
-				       has_multiple_pins);
-}
-EXPORT_SYMBOL_HDA(hda_get_autocfg_input_label);
-
-/* return the position of NID in the list, or -1 if not found */
-static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums)
-{
-	int i;
-	for (i = 0; i < nums; i++)
-		if (list[i] == nid)
-			return i;
-	return -1;
-}
-
-/* get a unique suffix or an index number */
-static const char *check_output_sfx(hda_nid_t nid, const hda_nid_t *pins,
-				    int num_pins, int *indexp)
-{
-	static const char * const channel_sfx[] = {
-		" Front", " Surround", " CLFE", " Side"
-	};
-	int i;
-
-	i = find_idx_in_nid_list(nid, pins, num_pins);
-	if (i < 0)
-		return NULL;
-	if (num_pins == 1)
-		return "";
-	if (num_pins > ARRAY_SIZE(channel_sfx)) {
-		if (indexp)
-			*indexp = i;
-		return "";
-	}
-	return channel_sfx[i];
-}
-
-static int fill_audio_out_name(struct hda_codec *codec, hda_nid_t nid,
-			       const struct auto_pin_cfg *cfg,
-			       const char *name, char *label, int maxlen,
-			       int *indexp)
-{
-	unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
-	int attr = snd_hda_get_input_pin_attr(def_conf);
-	const char *pfx = "", *sfx = "";
-
-	/* handle as a speaker if it's a fixed line-out */
-	if (!strcmp(name, "Line Out") && attr == INPUT_PIN_ATTR_INT)
-		name = "Speaker";
-	/* check the location */
-	switch (attr) {
-	case INPUT_PIN_ATTR_DOCK:
-		pfx = "Dock ";
-		break;
-	case INPUT_PIN_ATTR_FRONT:
-		pfx = "Front ";
-		break;
-	}
-	if (cfg) {
-		/* try to give a unique suffix if needed */
-		sfx = check_output_sfx(nid, cfg->line_out_pins, cfg->line_outs,
-				       indexp);
-		if (!sfx)
-			sfx = check_output_sfx(nid, cfg->speaker_pins, cfg->speaker_outs,
-					       indexp);
-		if (!sfx) {
-			/* don't add channel suffix for Headphone controls */
-			int idx = find_idx_in_nid_list(nid, cfg->hp_pins,
-						       cfg->hp_outs);
-			if (idx >= 0)
-				*indexp = idx;
-			sfx = "";
-		}
-	}
-	snprintf(label, maxlen, "%s%s%s", pfx, name, sfx);
-	return 1;
-}
-
-/**
- * snd_hda_get_pin_label - Get a label for the given I/O pin
- *
- * Get a label for the given pin.  This function works for both input and
- * output pins.  When @cfg is given as non-NULL, the function tries to get
- * an optimized label using hda_get_autocfg_input_label().
- *
- * This function tries to give a unique label string for the pin as much as
- * possible.  For example, when the multiple line-outs are present, it adds
- * the channel suffix like "Front", "Surround", etc (only when @cfg is given).
- * If no unique name with a suffix is available and @indexp is non-NULL, the
- * index number is stored in the pointer.
- */
-int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid,
-			  const struct auto_pin_cfg *cfg,
-			  char *label, int maxlen, int *indexp)
-{
-	unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
-	const char *name = NULL;
-	int i;
-
-	if (indexp)
-		*indexp = 0;
-	if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
-		return 0;
-
-	switch (get_defcfg_device(def_conf)) {
-	case AC_JACK_LINE_OUT:
-		return fill_audio_out_name(codec, nid, cfg, "Line Out",
-					   label, maxlen, indexp);
-	case AC_JACK_SPEAKER:
-		return fill_audio_out_name(codec, nid, cfg, "Speaker",
-					   label, maxlen, indexp);
-	case AC_JACK_HP_OUT:
-		return fill_audio_out_name(codec, nid, cfg, "Headphone",
-					   label, maxlen, indexp);
-	case AC_JACK_SPDIF_OUT:
-	case AC_JACK_DIG_OTHER_OUT:
-		if (get_defcfg_location(def_conf) == AC_JACK_LOC_HDMI)
-			name = "HDMI";
-		else
-			name = "SPDIF";
-		if (cfg && indexp) {
-			i = find_idx_in_nid_list(nid, cfg->dig_out_pins,
-						 cfg->dig_outs);
-			if (i >= 0)
-				*indexp = i;
-		}
-		break;
-	default:
-		if (cfg) {
-			for (i = 0; i < cfg->num_inputs; i++) {
-				if (cfg->inputs[i].pin != nid)
-					continue;
-				name = hda_get_autocfg_input_label(codec, cfg, i);
-				if (name)
-					break;
-			}
-		}
-		if (!name)
-			name = hda_get_input_pin_label(codec, nid, true);
-		break;
-	}
-	if (!name)
-		return 0;
-	strlcpy(label, name, maxlen);
-	return 1;
-}
-EXPORT_SYMBOL_HDA(snd_hda_get_pin_label);
+EXPORT_SYMBOL_HDA(_snd_hda_set_pin_ctl);
 
 /**
  * snd_hda_add_imux_item - Add an item to input_mux
@@ -5444,8 +4986,6 @@
 	list_for_each_entry(codec, &bus->codec_list, list) {
 		if (hda_codec_is_power_on(codec))
 			hda_call_codec_suspend(codec);
-		if (codec->patch_ops.post_suspend)
-			codec->patch_ops.post_suspend(codec);
 	}
 	return 0;
 }
@@ -5465,10 +5005,7 @@
 	struct hda_codec *codec;
 
 	list_for_each_entry(codec, &bus->codec_list, list) {
-		if (codec->patch_ops.pre_resume)
-			codec->patch_ops.pre_resume(codec);
-		if (snd_hda_codec_needs_resume(codec))
-			hda_call_codec_resume(codec);
+		hda_call_codec_resume(codec);
 	}
 	return 0;
 }
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index 56b4f74..54b5281 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -704,8 +704,6 @@
 				unsigned int power_state);
 #ifdef CONFIG_PM
 	int (*suspend)(struct hda_codec *codec, pm_message_t state);
-	int (*post_suspend)(struct hda_codec *codec);
-	int (*pre_resume)(struct hda_codec *codec);
 	int (*resume)(struct hda_codec *codec);
 #endif
 #ifdef CONFIG_SND_HDA_POWER_SAVE
@@ -829,6 +827,7 @@
 
 	struct mutex spdif_mutex;
 	struct mutex control_mutex;
+	struct mutex hash_mutex;
 	struct snd_array spdif_out;
 	unsigned int spdif_in_enable;	/* SPDIF input enable? */
 	const hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */
@@ -861,12 +860,13 @@
 	unsigned int no_jack_detect:1;	/* Machine has no jack-detection */
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 	unsigned int power_on :1;	/* current (global) power-state */
-	unsigned int power_transition :1; /* power-state in transition */
+	int power_transition;	/* power-state in transition */
 	int power_count;	/* current (global) power refcount */
 	struct delayed_work power_work; /* delayed task for powerdown */
 	unsigned long power_on_acct;
 	unsigned long power_off_acct;
 	unsigned long power_jiffies;
+	spinlock_t power_lock;
 #endif
 
 	/* codec-specific additional proc output */
@@ -911,10 +911,13 @@
 			  hda_nid_t *start_id);
 int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
 			    hda_nid_t *conn_list, int max_conns);
+static inline int
+snd_hda_get_num_conns(struct hda_codec *codec, hda_nid_t nid)
+{
+	return snd_hda_get_connections(codec, nid, NULL, 0);
+}
 int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid,
 			    hda_nid_t *conn_list, int max_conns);
-int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid,
-			  const hda_nid_t **listp);
 int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int nums,
 			  const hda_nid_t *list);
 int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux,
@@ -1051,12 +1054,10 @@
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 void snd_hda_power_up(struct hda_codec *codec);
 void snd_hda_power_down(struct hda_codec *codec);
-#define snd_hda_codec_needs_resume(codec) codec->power_count
 void snd_hda_update_power_acct(struct hda_codec *codec);
 #else
 static inline void snd_hda_power_up(struct hda_codec *codec) {}
 static inline void snd_hda_power_down(struct hda_codec *codec) {}
-#define snd_hda_codec_needs_resume(codec) 1
 #endif
 
 #ifdef CONFIG_SND_HDA_PATCH_LOADER
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 1f35052..4ab8102 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -497,6 +497,7 @@
 	AZX_DRIVER_NVIDIA,
 	AZX_DRIVER_TERA,
 	AZX_DRIVER_CTX,
+	AZX_DRIVER_CTHDA,
 	AZX_DRIVER_GENERIC,
 	AZX_NUM_DRIVERS, /* keep this as last entry */
 };
@@ -518,6 +519,7 @@
 #define AZX_DCAPS_OLD_SSYNC	(1 << 20)	/* Old SSYNC reg for ICH */
 #define AZX_DCAPS_BUFSIZE	(1 << 21)	/* no buffer size alignment */
 #define AZX_DCAPS_ALIGN_BUFSIZE	(1 << 22)	/* buffer size alignment */
+#define AZX_DCAPS_4K_BDLE_BOUNDARY (1 << 23)	/* BDLE in 4k boundary */
 
 /* quirks for ATI SB / AMD Hudson */
 #define AZX_DCAPS_PRESET_ATI_SB \
@@ -533,6 +535,9 @@
 	(AZX_DCAPS_NVIDIA_SNOOP | AZX_DCAPS_RIRB_DELAY | AZX_DCAPS_NO_MSI |\
 	 AZX_DCAPS_ALIGN_BUFSIZE)
 
+#define AZX_DCAPS_PRESET_CTHDA \
+	(AZX_DCAPS_NO_MSI | AZX_DCAPS_POSFIX_LPIB | AZX_DCAPS_4K_BDLE_BOUNDARY)
+
 static char *driver_short_names[] __devinitdata = {
 	[AZX_DRIVER_ICH] = "HDA Intel",
 	[AZX_DRIVER_PCH] = "HDA Intel PCH",
@@ -546,6 +551,7 @@
 	[AZX_DRIVER_NVIDIA] = "HDA NVidia",
 	[AZX_DRIVER_TERA] = "HDA Teradici", 
 	[AZX_DRIVER_CTX] = "HDA Creative", 
+	[AZX_DRIVER_CTHDA] = "HDA Creative",
 	[AZX_DRIVER_GENERIC] = "HD-Audio Generic",
 };
 
@@ -1285,7 +1291,8 @@
 /*
  * set up a BDL entry
  */
-static int setup_bdle(struct snd_pcm_substream *substream,
+static int setup_bdle(struct azx *chip,
+		      struct snd_pcm_substream *substream,
 		      struct azx_dev *azx_dev, u32 **bdlp,
 		      int ofs, int size, int with_ioc)
 {
@@ -1304,6 +1311,12 @@
 		bdl[1] = cpu_to_le32(upper_32_bits(addr));
 		/* program the size field of the BDL entry */
 		chunk = snd_pcm_sgbuf_get_chunk_size(substream, ofs, size);
+		/* one BDLE cannot cross 4K boundary on CTHDA chips */
+		if (chip->driver_caps & AZX_DCAPS_4K_BDLE_BOUNDARY) {
+			u32 remain = 0x1000 - (ofs & 0xfff);
+			if (chunk > remain)
+				chunk = remain;
+		}
 		bdl[2] = cpu_to_le32(chunk);
 		/* program the IOC to enable interrupt
 		 * only when the whole fragment is processed
@@ -1356,7 +1369,7 @@
 				   bdl_pos_adj[chip->dev_index]);
 			pos_adj = 0;
 		} else {
-			ofs = setup_bdle(substream, azx_dev,
+			ofs = setup_bdle(chip, substream, azx_dev,
 					 &bdl, ofs, pos_adj,
 					 !substream->runtime->no_period_wakeup);
 			if (ofs < 0)
@@ -1366,10 +1379,10 @@
 		pos_adj = 0;
 	for (i = 0; i < periods; i++) {
 		if (i == periods - 1 && pos_adj)
-			ofs = setup_bdle(substream, azx_dev, &bdl, ofs,
+			ofs = setup_bdle(chip, substream, azx_dev, &bdl, ofs,
 					 period_bytes - pos_adj, 0);
 		else
-			ofs = setup_bdle(substream, azx_dev, &bdl, ofs,
+			ofs = setup_bdle(chip, substream, azx_dev, &bdl, ofs,
 					 period_bytes,
 					 !substream->runtime->no_period_wakeup);
 		if (ofs < 0)
@@ -2353,17 +2366,6 @@
  * power management
  */
 
-static int snd_hda_codecs_inuse(struct hda_bus *bus)
-{
-	struct hda_codec *codec;
-
-	list_for_each_entry(codec, &bus->codec_list, list) {
-		if (snd_hda_codec_needs_resume(codec))
-			return 1;
-	}
-	return 0;
-}
-
 static int azx_suspend(struct pci_dev *pci, pm_message_t state)
 {
 	struct snd_card *card = pci_get_drvdata(pci);
@@ -2410,8 +2412,7 @@
 		return -EIO;
 	azx_init_pci(chip);
 
-	if (snd_hda_codecs_inuse(chip->bus))
-		azx_init_chip(chip, 1);
+	azx_init_chip(chip, 1);
 
 	snd_hda_resume(chip->bus);
 	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
@@ -2565,6 +2566,8 @@
 	/* forced codec slots */
 	SND_PCI_QUIRK(0x1043, 0x1262, "ASUS W5Fm", 0x103),
 	SND_PCI_QUIRK(0x1046, 0x1262, "ASUS W5F", 0x103),
+	/* WinFast VP200 H (Teradici) user reported broken communication */
+	SND_PCI_QUIRK(0x3a21, 0x040d, "WinFast VP200 H", 0x101),
 	{}
 };
 
@@ -3130,6 +3133,11 @@
 	  .driver_data = AZX_DRIVER_CTX | AZX_DCAPS_CTX_WORKAROUND |
 	  AZX_DCAPS_RIRB_PRE_DELAY | AZX_DCAPS_POSFIX_LPIB },
 #endif
+	/* CTHDA chips */
+	{ PCI_DEVICE(0x1102, 0x0010),
+	  .driver_data = AZX_DRIVER_CTHDA | AZX_DCAPS_PRESET_CTHDA },
+	{ PCI_DEVICE(0x1102, 0x0012),
+	  .driver_data = AZX_DRIVER_CTHDA | AZX_DCAPS_PRESET_CTHDA },
 	/* Vortex86MX */
 	{ PCI_DEVICE(0x17f3, 0x3010), .driver_data = AZX_DRIVER_GENERIC },
 	/* VMware HDAudio */
@@ -3148,7 +3156,7 @@
 MODULE_DEVICE_TABLE(pci, azx_ids);
 
 /* pci_driver definition */
-static struct pci_driver driver = {
+static struct pci_driver azx_driver = {
 	.name = KBUILD_MODNAME,
 	.id_table = azx_ids,
 	.probe = azx_probe,
@@ -3159,15 +3167,4 @@
 #endif
 };
 
-static int __init alsa_card_azx_init(void)
-{
-	return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_azx_exit(void)
-{
-	pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_azx_init)
-module_exit(alsa_card_azx_exit)
+module_pci_driver(azx_driver);
diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c
index d689484..2dd1c11 100644
--- a/sound/pci/hda/hda_jack.c
+++ b/sound/pci/hda/hda_jack.c
@@ -17,6 +17,7 @@
 #include <sound/jack.h>
 #include "hda_codec.h"
 #include "hda_local.h"
+#include "hda_auto_parser.h"
 #include "hda_jack.h"
 
 bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid)
diff --git a/sound/pci/hda/hda_jack.h b/sound/pci/hda/hda_jack.h
index c66655c..8ae5246 100644
--- a/sound/pci/hda/hda_jack.h
+++ b/sound/pci/hda/hda_jack.h
@@ -12,6 +12,8 @@
 #ifndef __SOUND_HDA_JACK_H
 #define __SOUND_HDA_JACK_H
 
+struct auto_pin_cfg;
+
 struct hda_jack_tbl {
 	hda_nid_t nid;
 	unsigned char action;		/* event action (0 = none) */
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index 0ec9248..9a096a8 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -262,6 +262,8 @@
 			  const struct hda_input_mux *imux,
 			  struct snd_ctl_elem_value *ucontrol, hda_nid_t nid,
 			  unsigned int *cur_val);
+int snd_hda_add_imux_item(struct hda_input_mux *imux, const char *label,
+			  int index, int *type_index_ret);
 
 /*
  * Channel mode helper
@@ -393,72 +395,7 @@
 	struct hda_bus *bus;
 };
 
-/*
- * Helper for automatic pin configuration
- */
-
-enum {
-	AUTO_PIN_MIC,
-	AUTO_PIN_LINE_IN,
-	AUTO_PIN_CD,
-	AUTO_PIN_AUX,
-	AUTO_PIN_LAST
-};
-
-enum {
-	AUTO_PIN_LINE_OUT,
-	AUTO_PIN_SPEAKER_OUT,
-	AUTO_PIN_HP_OUT
-};
-
-#define AUTO_CFG_MAX_OUTS	HDA_MAX_OUTS
-#define AUTO_CFG_MAX_INS	8
-
-struct auto_pin_cfg_item {
-	hda_nid_t pin;
-	int type;
-};
-
-struct auto_pin_cfg;
-const char *hda_get_autocfg_input_label(struct hda_codec *codec,
-					const struct auto_pin_cfg *cfg,
-					int input);
-int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid,
-			  const struct auto_pin_cfg *cfg,
-			  char *label, int maxlen, int *indexp);
-int snd_hda_add_imux_item(struct hda_input_mux *imux, const char *label,
-			  int index, int *type_index_ret);
-
-enum {
-	INPUT_PIN_ATTR_UNUSED,	/* pin not connected */
-	INPUT_PIN_ATTR_INT,	/* internal mic/line-in */
-	INPUT_PIN_ATTR_DOCK,	/* docking mic/line-in */
-	INPUT_PIN_ATTR_NORMAL,	/* mic/line-in jack */
-	INPUT_PIN_ATTR_FRONT,	/* mic/line-in jack in front */
-	INPUT_PIN_ATTR_REAR,	/* mic/line-in jack in rear */
-};
-
-int snd_hda_get_input_pin_attr(unsigned int def_conf);
-
-struct auto_pin_cfg {
-	int line_outs;
-	/* sorted in the order of Front/Surr/CLFE/Side */
-	hda_nid_t line_out_pins[AUTO_CFG_MAX_OUTS];
-	int speaker_outs;
-	hda_nid_t speaker_pins[AUTO_CFG_MAX_OUTS];
-	int hp_outs;
-	int line_out_type;	/* AUTO_PIN_XXX_OUT */
-	hda_nid_t hp_pins[AUTO_CFG_MAX_OUTS];
-	int num_inputs;
-	struct auto_pin_cfg_item inputs[AUTO_CFG_MAX_INS];
-	int dig_outs;
-	hda_nid_t dig_out_pins[2];
-	hda_nid_t dig_in_pin;
-	hda_nid_t mono_out_pin;
-	int dig_out_type[2]; /* HDA_PCM_TYPE_XXX */
-	int dig_in_type; /* HDA_PCM_TYPE_XXX */
-};
-
+/* helper macros to retrieve pin default-config values */
 #define get_defcfg_connect(cfg) \
 	((cfg & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT)
 #define get_defcfg_association(cfg) \
@@ -472,19 +409,6 @@
 #define get_defcfg_misc(cfg) \
 	((cfg & AC_DEFCFG_MISC) >> AC_DEFCFG_MISC_SHIFT)
 
-/* bit-flags for snd_hda_parse_pin_def_config() behavior */
-#define HDA_PINCFG_NO_HP_FIXUP	(1 << 0) /* no HP-split */
-#define HDA_PINCFG_NO_LO_FIXUP	(1 << 1) /* don't take other outs as LO */
-
-int snd_hda_parse_pin_defcfg(struct hda_codec *codec,
-			     struct auto_pin_cfg *cfg,
-			     const hda_nid_t *ignore_nids,
-			     unsigned int cond_flags);
-
-/* older function */
-#define snd_hda_parse_pin_def_config(codec, cfg, ignore) \
-	snd_hda_parse_pin_defcfg(codec, cfg, ignore, 0)
-
 /* amp values */
 #define AMP_IN_MUTE(idx)	(0x7080 | ((idx)<<8))
 #define AMP_IN_UNMUTE(idx)	(0x7000 | ((idx)<<8))
@@ -502,6 +426,46 @@
 #define PIN_HP			(AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN)
 #define PIN_HP_AMP		(AC_PINCTL_HP_EN)
 
+unsigned int snd_hda_get_default_vref(struct hda_codec *codec, hda_nid_t pin);
+int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin,
+			 unsigned int val, bool cached);
+
+/**
+ * _snd_hda_set_pin_ctl - Set a pin-control value safely
+ * @codec: the codec instance
+ * @pin: the pin NID to set the control
+ * @val: the pin-control value (AC_PINCTL_* bits)
+ *
+ * This function sets the pin-control value to the given pin, but
+ * filters out the invalid pin-control bits when the pin has no such
+ * capabilities.  For example, when PIN_HP is passed but the pin has no
+ * HP-drive capability, the HP bit is omitted.
+ *
+ * The function doesn't check the input VREF capability bits, though.
+ * Use snd_hda_get_default_vref() to guess the right value.
+ * Also, this function is only for analog pins, not for HDMI pins.
+ */
+static inline int
+snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin, unsigned int val)
+{
+	return _snd_hda_set_pin_ctl(codec, pin, val, false);
+}
+
+/**
+ * snd_hda_set_pin_ctl_cache - Set a pin-control value safely
+ * @codec: the codec instance
+ * @pin: the pin NID to set the control
+ * @val: the pin-control value (AC_PINCTL_* bits)
+ *
+ * Just like snd_hda_set_pin_ctl() but write to cache as well.
+ */
+static inline int
+snd_hda_set_pin_ctl_cache(struct hda_codec *codec, hda_nid_t pin,
+			  unsigned int val)
+{
+	return _snd_hda_set_pin_ctl(codec, pin, val, true);
+}
+
 /*
  * get widget capabilities
  */
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index 7143393..d8b2d6d 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -28,6 +28,7 @@
 #include <sound/core.h>
 #include "hda_codec.h"
 #include "hda_local.h"
+#include "hda_auto_parser.h"
 #include "hda_beep.h"
 #include "hda_jack.h"
 
@@ -1742,9 +1743,7 @@
 	if (! ad198x_eapd_put(kcontrol, ucontrol))
 		return 0;
 	/* change speaker pin appropriately */
-	snd_hda_codec_write(codec, 0x05, 0,
-			    AC_VERB_SET_PIN_WIDGET_CONTROL,
-			    spec->cur_eapd ? PIN_OUT : 0);
+	snd_hda_set_pin_ctl(codec, 0x05, spec->cur_eapd ? PIN_OUT : 0);
 	/* toggle HP mute appropriately */
 	snd_hda_codec_amp_stereo(codec, 0x06, HDA_OUTPUT, 0,
 				 HDA_AMP_MUTE,
@@ -3103,7 +3102,7 @@
 					      int dac_idx)
 {
 	/* set as output */
-	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
+	snd_hda_set_pin_ctl(codec, nid, pin_type);
 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
 	switch (nid) {
 	case 0x11: /* port-A - DAC 03 */
@@ -3157,6 +3156,7 @@
 	for (i = 0; i < cfg->num_inputs; i++) {
 		hda_nid_t nid = cfg->inputs[i].pin;
 		int type = cfg->inputs[i].type;
+		int val;
 		switch (nid) {
 		case 0x15: /* port-C */
 			snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
@@ -3165,8 +3165,10 @@
 			snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
 			break;
 		}
-		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
-				    type == AUTO_PIN_MIC ? PIN_VREF80 : PIN_IN);
+		val = PIN_IN;
+		if (type == AUTO_PIN_MIC)
+			val |= snd_hda_get_default_vref(codec, nid);
+		snd_hda_set_pin_ctl(codec, nid, val);
 		if (nid != AD1988_PIN_CD_NID)
 			snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
 					    AMP_OUT_MUTE);
diff --git a/sound/pci/hda/patch_ca0110.c b/sound/pci/hda/patch_ca0110.c
index 09ccfab..19ae14f 100644
--- a/sound/pci/hda/patch_ca0110.c
+++ b/sound/pci/hda/patch_ca0110.c
@@ -26,6 +26,7 @@
 #include <sound/core.h>
 #include "hda_codec.h"
 #include "hda_local.h"
+#include "hda_auto_parser.h"
 
 /*
  */
@@ -341,8 +342,7 @@
 static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac)
 {
 	if (pin) {
-		snd_hda_codec_write(codec, pin, 0,
-				    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
+		snd_hda_set_pin_ctl(codec, pin, PIN_HP);
 		if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)
 			snd_hda_codec_write(codec, pin, 0,
 					    AC_VERB_SET_AMP_GAIN_MUTE,
@@ -356,8 +356,8 @@
 static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc)
 {
 	if (pin) {
-		snd_hda_codec_write(codec, pin, 0,
-				    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80);
+		snd_hda_set_pin_ctl(codec, pin, PIN_IN |
+				    snd_hda_get_default_vref(codec, pin));
 		if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP)
 			snd_hda_codec_write(codec, pin, 0,
 					    AC_VERB_SET_AMP_GAIN_MUTE,
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c
index 21d91d5..d0d3540 100644
--- a/sound/pci/hda/patch_ca0132.c
+++ b/sound/pci/hda/patch_ca0132.c
@@ -30,6 +30,7 @@
 #include <sound/core.h>
 #include "hda_codec.h"
 #include "hda_local.h"
+#include "hda_auto_parser.h"
 
 #define WIDGET_CHIP_CTRL      0x15
 #define WIDGET_DSP_CTRL       0x16
@@ -239,8 +240,7 @@
 static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac)
 {
 	if (pin) {
-		snd_hda_codec_write(codec, pin, 0,
-				    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
+		snd_hda_set_pin_ctl(codec, pin, PIN_HP);
 		if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)
 			snd_hda_codec_write(codec, pin, 0,
 					    AC_VERB_SET_AMP_GAIN_MUTE,
@@ -254,9 +254,8 @@
 static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc)
 {
 	if (pin) {
-		snd_hda_codec_write(codec, pin, 0,
-				    AC_VERB_SET_PIN_WIDGET_CONTROL,
-				    PIN_VREF80);
+		snd_hda_set_pin_ctl(codec, pin, PIN_IN |
+				    snd_hda_get_default_vref(codec, pin));
 		if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP)
 			snd_hda_codec_write(codec, pin, 0,
 					    AC_VERB_SET_AMP_GAIN_MUTE,
diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c
index c83ccdb..9647ed4 100644
--- a/sound/pci/hda/patch_cirrus.c
+++ b/sound/pci/hda/patch_cirrus.c
@@ -26,6 +26,7 @@
 #include <sound/core.h>
 #include "hda_codec.h"
 #include "hda_local.h"
+#include "hda_auto_parser.h"
 #include "hda_jack.h"
 #include <sound/tlv.h>
 
@@ -933,8 +934,7 @@
 			pin_ctl = 0;
 
 		nid = cfg->speaker_pins[i];
-		snd_hda_codec_write(codec, nid, 0,
-				    AC_VERB_SET_PIN_WIDGET_CONTROL, pin_ctl);
+		snd_hda_set_pin_ctl(codec, nid, pin_ctl);
 	}
 	if (spec->gpio_eapd_hp) {
 		unsigned int gpio = hp_present ?
@@ -948,16 +948,14 @@
 		/* mute HPs if spdif jack (SENSE_B) is present */
 		for (i = 0; i < cfg->hp_outs; i++) {
 			nid = cfg->hp_pins[i];
-			snd_hda_codec_write(codec, nid, 0,
-				AC_VERB_SET_PIN_WIDGET_CONTROL,
+			snd_hda_set_pin_ctl(codec, nid,
 				(spdif_present && spec->sense_b) ? 0 : PIN_HP);
 		}
 
 		/* SPDIF TX on/off */
 		if (cfg->dig_outs) {
 			nid = cfg->dig_out_pins[0];
-			snd_hda_codec_write(codec, nid, 0,
-				AC_VERB_SET_PIN_WIDGET_CONTROL,
+			snd_hda_set_pin_ctl(codec, nid,
 				spdif_present ? PIN_OUT : 0);
 
 		}
@@ -1024,13 +1022,11 @@
 
 	/* set appropriate pin controls */
 	for (i = 0; i < cfg->line_outs; i++)
-		snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
-				    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+		snd_hda_set_pin_ctl(codec, cfg->line_out_pins[i], PIN_OUT);
 	/* HP */
 	for (i = 0; i < cfg->hp_outs; i++) {
 		hda_nid_t nid = cfg->hp_pins[i];
-		snd_hda_codec_write(codec, nid, 0,
-				    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
+		snd_hda_set_pin_ctl(codec, nid, PIN_HP);
 		if (!cfg->speaker_outs)
 			continue;
 		if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) {
@@ -1041,8 +1037,7 @@
 
 	/* Speaker */
 	for (i = 0; i < cfg->speaker_outs; i++)
-		snd_hda_codec_write(codec, cfg->speaker_pins[i], 0,
-				    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+		snd_hda_set_pin_ctl(codec, cfg->speaker_pins[i], PIN_OUT);
 
 	/* SPDIF is enabled on presence detect for CS421x */
 	if (spec->hp_detect || spec->spdif_detect)
@@ -1063,14 +1058,9 @@
 			continue;
 		/* set appropriate pin control and mute first */
 		ctl = PIN_IN;
-		if (cfg->inputs[i].type == AUTO_PIN_MIC) {
-			unsigned int caps = snd_hda_query_pin_caps(codec, pin);
-			caps >>= AC_PINCAP_VREF_SHIFT;
-			if (caps & AC_PINCAP_VREF_80)
-				ctl = PIN_VREF80;
-		}
-		snd_hda_codec_write(codec, pin, 0,
-				    AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
+		if (cfg->inputs[i].type == AUTO_PIN_MIC)
+			ctl |= snd_hda_get_default_vref(codec, pin);
+		snd_hda_set_pin_ctl(codec, pin, ctl);
 		snd_hda_codec_write(codec, spec->adc_nid[i], 0,
 				    AC_VERB_SET_AMP_GAIN_MUTE,
 				    AMP_IN_MUTE(spec->adc_idx[i]));
diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c
index b6767b4..c8fdaae 100644
--- a/sound/pci/hda/patch_cmedia.c
+++ b/sound/pci/hda/patch_cmedia.c
@@ -29,6 +29,7 @@
 #include <sound/core.h>
 #include "hda_codec.h"
 #include "hda_local.h"
+#include "hda_auto_parser.h"
 #define NUM_PINS	11
 
 
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index d906c5b..3acb582 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -30,6 +30,7 @@
 
 #include "hda_codec.h"
 #include "hda_local.h"
+#include "hda_auto_parser.h"
 #include "hda_beep.h"
 #include "hda_jack.h"
 
@@ -66,6 +67,7 @@
 };
 
 struct conexant_spec {
+	struct hda_gen_spec gen;
 
 	const struct snd_kcontrol_new *mixers[5];
 	int num_mixers;
@@ -141,6 +143,7 @@
 	unsigned int hp_laptop:1;
 	unsigned int asus:1;
 	unsigned int pin_eapd_ctrls:1;
+	unsigned int fixup_stereo_dmic:1;
 
 	unsigned int adc_switching:1;
 
@@ -1601,17 +1604,13 @@
 	unsigned int pinctl;
 	/* headphone pin */
 	pinctl = (spec->hp_present && spec->cur_eapd) ? PIN_HP : 0;
-	snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
-			    pinctl);
+	snd_hda_set_pin_ctl(codec, 0x16, pinctl);
 	/* speaker pin */
 	pinctl = (!spec->hp_present && spec->cur_eapd) ? PIN_OUT : 0;
-	snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
-			    pinctl);
+	snd_hda_set_pin_ctl(codec, 0x1a, pinctl);
 	/* on ideapad there is an additional speaker (subwoofer) to mute */
 	if (spec->ideapad)
-		snd_hda_codec_write(codec, 0x1b, 0,
-				    AC_VERB_SET_PIN_WIDGET_CONTROL,
-				    pinctl);
+		snd_hda_set_pin_ctl(codec, 0x1b, pinctl);
 }
 
 /* turn on/off EAPD (+ mute HP) as a master switch */
@@ -1996,8 +1995,7 @@
 
 	/* Port A (HP) */
 	pinctl = (hp_port_a_present(spec) && spec->cur_eapd) ? PIN_HP : 0;
-	snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
-			pinctl);
+	snd_hda_set_pin_ctl(codec, 0x19, pinctl);
 
 	/* Port D (HP/LO) */
 	pinctl = spec->cur_eapd ? spec->port_d_mode : 0;
@@ -2010,13 +2008,11 @@
 		if (!hp_port_d_present(spec))
 			pinctl = 0;
 	}
-	snd_hda_codec_write(codec, 0x1c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
-			pinctl);
+	snd_hda_set_pin_ctl(codec, 0x1c, pinctl);
 
 	/* CLASS_D AMP */
 	pinctl = (!spec->hp_present && spec->cur_eapd) ? PIN_OUT : 0;
-	snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
-			pinctl);
+	snd_hda_set_pin_ctl(codec, 0x1f, pinctl);
 }
 
 /* turn on/off EAPD (+ mute HP) as a master switch */
@@ -2047,8 +2043,7 @@
 	/* Even though port F is the DC input, the bias is controlled on port B.
 	 * we also leave that port as an active input (but unselected) in DC mode
 	 * just in case that is necessary to make the bias setting take effect. */
-	return snd_hda_codec_write_cache(codec, 0x1a, 0,
-		AC_VERB_SET_PIN_WIDGET_CONTROL,
+	return snd_hda_set_pin_ctl_cache(codec, 0x1a,
 		cxt5066_olpc_dc_bias.items[spec->dc_input_bias].index);
 }
 
@@ -2081,14 +2076,14 @@
 	}
 
 	/* disable DC (port F) */
-	snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
+	snd_hda_set_pin_ctl(codec, 0x1e, 0);
 
 	/* external mic, port B */
-	snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+	snd_hda_set_pin_ctl(codec, 0x1a,
 		spec->ext_mic_present ? CXT5066_OLPC_EXT_MIC_BIAS : 0);
 
 	/* internal mic, port C */
-	snd_hda_codec_write(codec, 0x1b, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+	snd_hda_set_pin_ctl(codec, 0x1b,
 		spec->ext_mic_present ? 0 : PIN_VREF80);
 }
 
@@ -3357,9 +3352,7 @@
 	struct conexant_spec *spec = codec->spec;
 	int i;
 	for (i = 0; i < num_pins; i++)
-		snd_hda_codec_write(codec, pins[i], 0,
-				    AC_VERB_SET_PIN_WIDGET_CONTROL,
-				    on ? PIN_OUT : 0);
+		snd_hda_set_pin_ctl(codec, pins[i], on ? PIN_OUT : 0);
 	if (spec->pin_eapd_ctrls)
 		cx_auto_turn_eapd(codec, num_pins, pins, on);
 }
@@ -3976,8 +3969,7 @@
 		if (snd_hda_query_pin_caps(codec, cfg->hp_pins[i]) &
 		    AC_PINCAP_HP_DRV)
 			val |= AC_PINCTL_HP_EN;
-		snd_hda_codec_write(codec, cfg->hp_pins[i], 0,
-				    AC_VERB_SET_PIN_WIDGET_CONTROL, val);
+		snd_hda_set_pin_ctl(codec, cfg->hp_pins[i], val);
 	}
 	mute_outputs(codec, cfg->hp_outs, cfg->hp_pins);
 	mute_outputs(codec, cfg->line_outs, cfg->line_out_pins);
@@ -4030,13 +4022,11 @@
 	}
 
 	for (i = 0; i < cfg->num_inputs; i++) {
-		unsigned int type;
+		hda_nid_t pin = cfg->inputs[i].pin;
+		unsigned int type = PIN_IN;
 		if (cfg->inputs[i].type == AUTO_PIN_MIC)
-			type = PIN_VREF80;
-		else
-			type = PIN_IN;
-		snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
-				    AC_VERB_SET_PIN_WIDGET_CONTROL, type);
+			type |= snd_hda_get_default_vref(codec, pin);
+		snd_hda_set_pin_ctl(codec, pin, type);
 	}
 
 	if (spec->auto_mic) {
@@ -4063,11 +4053,9 @@
 	struct auto_pin_cfg *cfg = &spec->autocfg;
 
 	if (spec->multiout.dig_out_nid)
-		snd_hda_codec_write(codec, cfg->dig_out_pins[0], 0,
-				    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+		snd_hda_set_pin_ctl(codec, cfg->dig_out_pins[0], PIN_OUT);
 	if (spec->dig_in_nid)
-		snd_hda_codec_write(codec, cfg->dig_in_pin, 0,
-				    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
+		snd_hda_set_pin_ctl(codec, cfg->dig_in_pin, PIN_IN);
 }
 
 static int cx_auto_init(struct hda_codec *codec)
@@ -4084,9 +4072,9 @@
 
 static int cx_auto_add_volume_idx(struct hda_codec *codec, const char *basename,
 			      const char *dir, int cidx,
-			      hda_nid_t nid, int hda_dir, int amp_idx)
+			      hda_nid_t nid, int hda_dir, int amp_idx, int chs)
 {
-	static char name[32];
+	static char name[44];
 	static struct snd_kcontrol_new knew[] = {
 		HDA_CODEC_VOLUME(name, 0, 0, 0),
 		HDA_CODEC_MUTE(name, 0, 0, 0),
@@ -4096,7 +4084,7 @@
 
 	for (i = 0; i < 2; i++) {
 		struct snd_kcontrol *kctl;
-		knew[i].private_value = HDA_COMPOSE_AMP_VAL(nid, 3, amp_idx,
+		knew[i].private_value = HDA_COMPOSE_AMP_VAL(nid, chs, amp_idx,
 							    hda_dir);
 		knew[i].subdevice = HDA_SUBDEV_AMP_FLAG;
 		knew[i].index = cidx;
@@ -4115,7 +4103,7 @@
 }
 
 #define cx_auto_add_volume(codec, str, dir, cidx, nid, hda_dir)		\
-	cx_auto_add_volume_idx(codec, str, dir, cidx, nid, hda_dir, 0)
+	cx_auto_add_volume_idx(codec, str, dir, cidx, nid, hda_dir, 0, 3)
 
 #define cx_auto_add_pb_volume(codec, nid, str, idx)			\
 	cx_auto_add_volume(codec, str, " Playback", idx, nid, HDA_OUTPUT)
@@ -4185,6 +4173,36 @@
 	return 0;
 }
 
+/* Returns zero if this is a normal stereo channel, and non-zero if it should
+   be split in two independent channels.
+   dest_label must be at least 44 characters. */
+static int cx_auto_get_rightch_label(struct hda_codec *codec, const char *label,
+				     char *dest_label, int nid)
+{
+	struct conexant_spec *spec = codec->spec;
+	int i;
+
+	if (!spec->fixup_stereo_dmic)
+		return 0;
+
+	for (i = 0; i < AUTO_CFG_MAX_INS; i++) {
+		int def_conf;
+		if (spec->autocfg.inputs[i].pin != nid)
+			continue;
+
+		if (spec->autocfg.inputs[i].type != AUTO_PIN_MIC)
+			return 0;
+		def_conf = snd_hda_codec_get_pincfg(codec, nid);
+		if (snd_hda_get_input_pin_attr(def_conf) != INPUT_PIN_ATTR_INT)
+			return 0;
+
+		/* Finally found the inverted internal mic! */
+		snprintf(dest_label, 44, "Inverted %s", label);
+		return 1;
+	}
+	return 0;
+}
+
 static int cx_auto_add_capture_volume(struct hda_codec *codec, hda_nid_t nid,
 				      const char *label, const char *pfx,
 				      int cidx)
@@ -4193,14 +4211,25 @@
 	int i;
 
 	for (i = 0; i < spec->num_adc_nids; i++) {
+		char rightch_label[44];
 		hda_nid_t adc_nid = spec->adc_nids[i];
 		int idx = get_input_connection(codec, adc_nid, nid);
 		if (idx < 0)
 			continue;
 		if (codec->single_adc_amp)
 			idx = 0;
+
+		if (cx_auto_get_rightch_label(codec, label, rightch_label, nid)) {
+			/* Make two independent kcontrols for left and right */
+			int err = cx_auto_add_volume_idx(codec, label, pfx,
+					      cidx, adc_nid, HDA_INPUT, idx, 1);
+			if (err < 0)
+				return err;
+			return cx_auto_add_volume_idx(codec, rightch_label, pfx,
+						      cidx, adc_nid, HDA_INPUT, idx, 2);
+		}
 		return cx_auto_add_volume_idx(codec, label, pfx,
-					      cidx, adc_nid, HDA_INPUT, idx);
+					      cidx, adc_nid, HDA_INPUT, idx, 3);
 	}
 	return 0;
 }
@@ -4213,9 +4242,19 @@
 	int i, con;
 
 	nid = spec->imux_info[idx].pin;
-	if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)
+	if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) {
+		char rightch_label[44];
+		if (cx_auto_get_rightch_label(codec, label, rightch_label, nid)) {
+			int err = cx_auto_add_volume_idx(codec, label, " Boost",
+							 cidx, nid, HDA_INPUT, 0, 1);
+			if (err < 0)
+				return err;
+			return cx_auto_add_volume_idx(codec, rightch_label, " Boost",
+						      cidx, nid, HDA_INPUT, 0, 2);
+		}
 		return cx_auto_add_volume(codec, label, " Boost", cidx,
 					  nid, HDA_INPUT);
+	}
 	con = __select_input_connection(codec, spec->imux_info[idx].adc, nid,
 					&mux, false, 0);
 	if (con < 0)
@@ -4370,37 +4409,21 @@
 /*
  * pin fix-up
  */
-struct cxt_pincfg {
-	hda_nid_t nid;
-	u32 val;
-};
-
-static void apply_pincfg(struct hda_codec *codec, const struct cxt_pincfg *cfg)
-{
-	for (; cfg->nid; cfg++)
-		snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val);
-
-}
-
-static void apply_pin_fixup(struct hda_codec *codec,
-			    const struct snd_pci_quirk *quirk,
-			    const struct cxt_pincfg **table)
-{
-	quirk = snd_pci_quirk_lookup(codec->bus->pci, quirk);
-	if (quirk) {
-		snd_printdd(KERN_INFO "hda_codec: applying pincfg for %s\n",
-			    quirk->name);
-		apply_pincfg(codec, table[quirk->value]);
-	}
-}
-
 enum {
 	CXT_PINCFG_LENOVO_X200,
 	CXT_PINCFG_LENOVO_TP410,
+	CXT_FIXUP_STEREO_DMIC,
 };
 
+static void cxt_fixup_stereo_dmic(struct hda_codec *codec,
+				  const struct hda_fixup *fix, int action)
+{
+	struct conexant_spec *spec = codec->spec;
+	spec->fixup_stereo_dmic = 1;
+}
+
 /* ThinkPad X200 & co with cxt5051 */
-static const struct cxt_pincfg cxt_pincfg_lenovo_x200[] = {
+static const struct hda_pintbl cxt_pincfg_lenovo_x200[] = {
 	{ 0x16, 0x042140ff }, /* HP (seq# overridden) */
 	{ 0x17, 0x21a11000 }, /* dock-mic */
 	{ 0x19, 0x2121103f }, /* dock-HP */
@@ -4409,16 +4432,26 @@
 };
 
 /* ThinkPad 410/420/510/520, X201 & co with cxt5066 */
-static const struct cxt_pincfg cxt_pincfg_lenovo_tp410[] = {
+static const struct hda_pintbl cxt_pincfg_lenovo_tp410[] = {
 	{ 0x19, 0x042110ff }, /* HP (seq# overridden) */
 	{ 0x1a, 0x21a190f0 }, /* dock-mic */
 	{ 0x1c, 0x212140ff }, /* dock-HP */
 	{}
 };
 
-static const struct cxt_pincfg *cxt_pincfg_tbl[] = {
-	[CXT_PINCFG_LENOVO_X200] = cxt_pincfg_lenovo_x200,
-	[CXT_PINCFG_LENOVO_TP410] = cxt_pincfg_lenovo_tp410,
+static const struct hda_fixup cxt_fixups[] = {
+	[CXT_PINCFG_LENOVO_X200] = {
+		.type = HDA_FIXUP_PINS,
+		.v.pins = cxt_pincfg_lenovo_x200,
+	},
+	[CXT_PINCFG_LENOVO_TP410] = {
+		.type = HDA_FIXUP_PINS,
+		.v.pins = cxt_pincfg_lenovo_tp410,
+	},
+	[CXT_FIXUP_STEREO_DMIC] = {
+		.type = HDA_FIXUP_FUNC,
+		.v.func = cxt_fixup_stereo_dmic,
+	},
 };
 
 static const struct snd_pci_quirk cxt5051_fixups[] = {
@@ -4432,6 +4465,7 @@
 	SND_PCI_QUIRK(0x17aa, 0x215f, "Lenovo T510", CXT_PINCFG_LENOVO_TP410),
 	SND_PCI_QUIRK(0x17aa, 0x21ce, "Lenovo T420", CXT_PINCFG_LENOVO_TP410),
 	SND_PCI_QUIRK(0x17aa, 0x21cf, "Lenovo T520", CXT_PINCFG_LENOVO_TP410),
+	SND_PCI_QUIRK(0x17aa, 0x3975, "Lenovo U300s", CXT_FIXUP_STEREO_DMIC),
 	{}
 };
 
@@ -4471,13 +4505,16 @@
 	case 0x14f15051:
 		add_cx5051_fake_mutes(codec);
 		codec->pin_amp_workaround = 1;
-		apply_pin_fixup(codec, cxt5051_fixups, cxt_pincfg_tbl);
+		snd_hda_pick_fixup(codec, NULL, cxt5051_fixups, cxt_fixups);
 		break;
 	default:
 		codec->pin_amp_workaround = 1;
-		apply_pin_fixup(codec, cxt5066_fixups, cxt_pincfg_tbl);
+		snd_hda_pick_fixup(codec, NULL, cxt5066_fixups, cxt_fixups);
+		break;
 	}
 
+	snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
 	/* Show mute-led control only on HP laptops
 	 * This is a sort of white-list: on HP laptops, EAPD corresponds
 	 * only to the mute-LED without actualy amp function.  Meanwhile,
@@ -4556,6 +4593,12 @@
 	  .patch = patch_conexant_auto },
 	{ .id = 0x14f150b9, .name = "CX20665",
 	  .patch = patch_conexant_auto },
+	{ .id = 0x14f1510f, .name = "CX20751/2",
+	  .patch = patch_conexant_auto },
+	{ .id = 0x14f15110, .name = "CX20751/2",
+	  .patch = patch_conexant_auto },
+	{ .id = 0x14f15111, .name = "CX20753/4",
+	  .patch = patch_conexant_auto },
 	{} /* terminator */
 };
 
@@ -4576,6 +4619,9 @@
 MODULE_ALIAS("snd-hda-codec-id:14f150ac");
 MODULE_ALIAS("snd-hda-codec-id:14f150b8");
 MODULE_ALIAS("snd-hda-codec-id:14f150b9");
+MODULE_ALIAS("snd-hda-codec-id:14f1510f");
+MODULE_ALIAS("snd-hda-codec-id:14f15110");
+MODULE_ALIAS("snd-hda-codec-id:14f15111");
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Conexant HD-audio codec");
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index 83f345f..ad319d4d 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -1592,10 +1592,10 @@
 	unsigned int dataDCC2, channel_id;
 	int i;
 	struct hdmi_spec *spec = codec->spec;
-	struct hda_spdif_out *spdif =
-		snd_hda_spdif_out_of_nid(codec, spec->cvts[0].cvt_nid);
+	struct hda_spdif_out *spdif;
 
 	mutex_lock(&codec->spdif_mutex);
+	spdif = snd_hda_spdif_out_of_nid(codec, spec->cvts[0].cvt_nid);
 
 	chs = substream->runtime->channels;
 
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 708d47c..ff71dce 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -32,6 +32,7 @@
 #include <sound/jack.h>
 #include "hda_codec.h"
 #include "hda_local.h"
+#include "hda_auto_parser.h"
 #include "hda_beep.h"
 #include "hda_jack.h"
 
@@ -66,8 +67,6 @@
 	unsigned int  fixup:1; /* Means that this sku is set by driver, not read from hw */
 };
 
-struct alc_fixup;
-
 struct alc_multi_io {
 	hda_nid_t pin;		/* multi-io widget pin NID */
 	hda_nid_t dac;		/* DAC to be connected */
@@ -82,19 +81,33 @@
 
 #define MAX_VOL_NIDS	0x40
 
+/* make compatible with old code */
+#define alc_apply_pincfgs	snd_hda_apply_pincfgs
+#define alc_apply_fixup		snd_hda_apply_fixup
+#define alc_pick_fixup		snd_hda_pick_fixup
+#define alc_fixup		hda_fixup
+#define alc_pincfg		hda_pintbl
+#define alc_model_fixup		hda_model_fixup
+
+#define ALC_FIXUP_PINS	HDA_FIXUP_PINS
+#define ALC_FIXUP_VERBS	HDA_FIXUP_VERBS
+#define ALC_FIXUP_FUNC	HDA_FIXUP_FUNC
+
+#define ALC_FIXUP_ACT_PRE_PROBE	HDA_FIXUP_ACT_PRE_PROBE
+#define ALC_FIXUP_ACT_PROBE	HDA_FIXUP_ACT_PROBE
+#define ALC_FIXUP_ACT_INIT	HDA_FIXUP_ACT_INIT
+#define ALC_FIXUP_ACT_BUILD	HDA_FIXUP_ACT_BUILD
+
+
 struct alc_spec {
+	struct hda_gen_spec gen;
+
 	/* codec parameterization */
 	const struct snd_kcontrol_new *mixers[5];	/* mixer arrays */
 	unsigned int num_mixers;
 	const struct snd_kcontrol_new *cap_mixer;	/* capture mixer */
 	unsigned int beep_amp;	/* beep amp value, set via set_beep_amp() */
 
-	const struct hda_verb *init_verbs[10];	/* initialization verbs
-						 * don't forget NULL
-						 * termination!
-						 */
-	unsigned int num_init_verbs;
-
 	char stream_name_analog[32];	/* analog PCM stream */
 	const struct hda_pcm_stream *stream_analog_playback;
 	const struct hda_pcm_stream *stream_analog_capture;
@@ -210,11 +223,6 @@
 	unsigned int pll_coef_idx, pll_coef_bit;
 	unsigned int coef0;
 
-	/* fix-up list */
-	int fixup_id;
-	const struct alc_fixup *fixup_list;
-	const char *fixup_name;
-
 	/* multi-io */
 	int multi_ios;
 	struct alc_multi_io multi_io[4];
@@ -319,13 +327,16 @@
 
 	/* for shared I/O, change the pin-control accordingly */
 	if (spec->shared_mic_hp) {
+		unsigned int val;
+		hda_nid_t pin = spec->autocfg.inputs[1].pin;
 		/* NOTE: this assumes that there are only two inputs, the
 		 * first is the real internal mic and the second is HP jack.
 		 */
-		snd_hda_codec_write(codec, spec->autocfg.inputs[1].pin, 0,
-				    AC_VERB_SET_PIN_WIDGET_CONTROL,
-				    spec->cur_mux[adc_idx] ?
-				    PIN_VREF80 : PIN_HP);
+		if (spec->cur_mux[adc_idx])
+			val = snd_hda_get_default_vref(codec, pin) | PIN_IN;
+		else
+			val = PIN_HP;
+		snd_hda_set_pin_ctl(codec, pin, val);
 		spec->automute_speaker = !spec->cur_mux[adc_idx];
 		call_update_outputs(codec);
 	}
@@ -338,7 +349,7 @@
 	nid = get_capsrc(spec, adc_idx);
 
 	/* no selection? */
-	num_conns = snd_hda_get_conn_list(codec, nid, NULL);
+	num_conns = snd_hda_get_num_conns(codec, nid);
 	if (num_conns <= 1)
 		return 1;
 
@@ -376,25 +387,9 @@
 			      int auto_pin_type)
 {
 	unsigned int val = PIN_IN;
-
-	if (auto_pin_type == AUTO_PIN_MIC) {
-		unsigned int pincap;
-		unsigned int oldval;
-		oldval = snd_hda_codec_read(codec, nid, 0,
-					    AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
-		pincap = snd_hda_query_pin_caps(codec, nid);
-		pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
-		/* if the default pin setup is vref50, we give it priority */
-		if ((pincap & AC_PINCAP_VREF_80) && oldval != PIN_VREF50)
-			val = PIN_VREF80;
-		else if (pincap & AC_PINCAP_VREF_50)
-			val = PIN_VREF50;
-		else if (pincap & AC_PINCAP_VREF_100)
-			val = PIN_VREF100;
-		else if (pincap & AC_PINCAP_VREF_GRD)
-			val = PIN_VREFGRD;
-	}
-	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, val);
+	if (auto_pin_type == AUTO_PIN_MIC)
+		val |= snd_hda_get_default_vref(codec, nid);
+	snd_hda_set_pin_ctl(codec, nid, val);
 }
 
 /*
@@ -409,13 +404,6 @@
 	spec->mixers[spec->num_mixers++] = mix;
 }
 
-static void add_verb(struct alc_spec *spec, const struct hda_verb *verb)
-{
-	if (snd_BUG_ON(spec->num_init_verbs >= ARRAY_SIZE(spec->init_verbs)))
-		return;
-	spec->init_verbs[spec->num_init_verbs++] = verb;
-}
-
 /*
  * GPIO setup tables, used in initialization
  */
@@ -517,9 +505,7 @@
 			} else
 				val = 0;
 			val |= pin_bits;
-			snd_hda_codec_write(codec, nid, 0,
-					    AC_VERB_SET_PIN_WIDGET_CONTROL,
-					    val);
+			snd_hda_set_pin_ctl(codec, nid, val);
 			break;
 		case ALC_AUTOMUTE_AMP:
 			snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
@@ -1200,6 +1186,16 @@
  */
 #define ALC_FIXUP_SKU_IGNORE (2)
 
+static void alc_fixup_sku_ignore(struct hda_codec *codec,
+				 const struct hda_fixup *fix, int action)
+{
+	struct alc_spec *spec = codec->spec;
+	if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+		spec->cdefine.fixup = 1;
+		spec->cdefine.sku_cfg = ALC_FIXUP_SKU_IGNORE;
+	}
+}
+
 static int alc_auto_parse_customize_define(struct hda_codec *codec)
 {
 	unsigned int ass, tmp, i;
@@ -1403,178 +1399,6 @@
 }
 
 /*
- * Fix-up pin default configurations and add default verbs
- */
-
-struct alc_pincfg {
-	hda_nid_t nid;
-	u32 val;
-};
-
-struct alc_model_fixup {
-	const int id;
-	const char *name;
-};
-
-struct alc_fixup {
-	int type;
-	bool chained;
-	int chain_id;
-	union {
-		unsigned int sku;
-		const struct alc_pincfg *pins;
-		const struct hda_verb *verbs;
-		void (*func)(struct hda_codec *codec,
-			     const struct alc_fixup *fix,
-			     int action);
-	} v;
-};
-
-enum {
-	ALC_FIXUP_INVALID,
-	ALC_FIXUP_SKU,
-	ALC_FIXUP_PINS,
-	ALC_FIXUP_VERBS,
-	ALC_FIXUP_FUNC,
-};
-
-enum {
-	ALC_FIXUP_ACT_PRE_PROBE,
-	ALC_FIXUP_ACT_PROBE,
-	ALC_FIXUP_ACT_INIT,
-	ALC_FIXUP_ACT_BUILD,
-};
-
-static void alc_apply_pincfgs(struct hda_codec *codec,
-			      const struct alc_pincfg *cfg)
-{
-	for (; cfg->nid; cfg++)
-		snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val);
-}
-
-static void alc_apply_fixup(struct hda_codec *codec, int action)
-{
-	struct alc_spec *spec = codec->spec;
-	int id = spec->fixup_id;
-#ifdef CONFIG_SND_DEBUG_VERBOSE
-	const char *modelname = spec->fixup_name;
-#endif
-	int depth = 0;
-
-	if (!spec->fixup_list)
-		return;
-
-	while (id >= 0) {
-		const struct alc_fixup *fix = spec->fixup_list + id;
-		const struct alc_pincfg *cfg;
-
-		switch (fix->type) {
-		case ALC_FIXUP_SKU:
-			if (action != ALC_FIXUP_ACT_PRE_PROBE || !fix->v.sku)
-				break;
-			snd_printdd(KERN_INFO "hda_codec: %s: "
-				    "Apply sku override for %s\n",
-				    codec->chip_name, modelname);
-			spec->cdefine.sku_cfg = fix->v.sku;
-			spec->cdefine.fixup = 1;
-			break;
-		case ALC_FIXUP_PINS:
-			cfg = fix->v.pins;
-			if (action != ALC_FIXUP_ACT_PRE_PROBE || !cfg)
-				break;
-			snd_printdd(KERN_INFO "hda_codec: %s: "
-				    "Apply pincfg for %s\n",
-				    codec->chip_name, modelname);
-			alc_apply_pincfgs(codec, cfg);
-			break;
-		case ALC_FIXUP_VERBS:
-			if (action != ALC_FIXUP_ACT_PROBE || !fix->v.verbs)
-				break;
-			snd_printdd(KERN_INFO "hda_codec: %s: "
-				    "Apply fix-verbs for %s\n",
-				    codec->chip_name, modelname);
-			add_verb(codec->spec, fix->v.verbs);
-			break;
-		case ALC_FIXUP_FUNC:
-			if (!fix->v.func)
-				break;
-			snd_printdd(KERN_INFO "hda_codec: %s: "
-				    "Apply fix-func for %s\n",
-				    codec->chip_name, modelname);
-			fix->v.func(codec, fix, action);
-			break;
-		default:
-			snd_printk(KERN_ERR "hda_codec: %s: "
-				   "Invalid fixup type %d\n",
-				   codec->chip_name, fix->type);
-			break;
-		}
-		if (!fix->chained)
-			break;
-		if (++depth > 10)
-			break;
-		id = fix->chain_id;
-	}
-}
-
-static void alc_pick_fixup(struct hda_codec *codec,
-			   const struct alc_model_fixup *models,
-			   const struct snd_pci_quirk *quirk,
-			   const struct alc_fixup *fixlist)
-{
-	struct alc_spec *spec = codec->spec;
-	const struct snd_pci_quirk *q;
-	int id = -1;
-	const char *name = NULL;
-
-	/* when model=nofixup is given, don't pick up any fixups */
-	if (codec->modelname && !strcmp(codec->modelname, "nofixup")) {
-		spec->fixup_list = NULL;
-		spec->fixup_id = -1;
-		return;
-	}
-
-	if (codec->modelname && models) {
-		while (models->name) {
-			if (!strcmp(codec->modelname, models->name)) {
-				id = models->id;
-				name = models->name;
-				break;
-			}
-			models++;
-		}
-	}
-	if (id < 0) {
-		q = snd_pci_quirk_lookup(codec->bus->pci, quirk);
-		if (q) {
-			id = q->value;
-#ifdef CONFIG_SND_DEBUG_VERBOSE
-			name = q->name;
-#endif
-		}
-	}
-	if (id < 0) {
-		for (q = quirk; q->subvendor; q++) {
-			unsigned int vendorid =
-				q->subdevice | (q->subvendor << 16);
-			if (vendorid == codec->subsystem_id) {
-				id = q->value;
-#ifdef CONFIG_SND_DEBUG_VERBOSE
-				name = q->name;
-#endif
-				break;
-			}
-		}
-	}
-
-	spec->fixup_id = id;
-	if (id >= 0) {
-		spec->fixup_list = fixlist;
-		spec->fixup_name = name;
-	}
-}
-
-/*
  * COEF access helper functions
  */
 static int alc_read_coef_idx(struct hda_codec *codec,
@@ -1621,8 +1445,7 @@
 		pin = spec->autocfg.dig_out_pins[i];
 		if (!pin)
 			continue;
-		snd_hda_codec_write(codec, pin, 0,
-				    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+		snd_hda_set_pin_ctl(codec, pin, PIN_OUT);
 		if (!i)
 			dac = spec->multiout.dig_out_nid;
 		else
@@ -1635,9 +1458,7 @@
 	}
 	pin = spec->autocfg.dig_in_pin;
 	if (pin)
-		snd_hda_codec_write(codec, pin, 0,
-				    AC_VERB_SET_PIN_WIDGET_CONTROL,
-				    PIN_IN);
+		snd_hda_set_pin_ctl(codec, pin, PIN_IN);
 }
 
 /* parse digital I/Os and set up NIDs in BIOS auto-parse mode */
@@ -2068,7 +1889,6 @@
 static int alc_init(struct hda_codec *codec)
 {
 	struct alc_spec *spec = codec->spec;
-	unsigned int i;
 
 	if (spec->init_hook)
 		spec->init_hook(codec);
@@ -2076,8 +1896,6 @@
 	alc_fix_pll(codec);
 	alc_auto_init_amp(codec, spec->init_amp);
 
-	for (i = 0; i < spec->num_init_verbs; i++)
-		snd_hda_sequence_write(codec, spec->init_verbs[i]);
 	alc_init_special_input_src(codec);
 	alc_auto_init_std(codec);
 
@@ -2725,7 +2543,6 @@
 	nid = codec->start_nid;
 	for (i = 0; i < codec->num_nodes; i++, nid++) {
 		hda_nid_t src;
-		const hda_nid_t *list;
 		unsigned int caps = get_wcaps(codec, nid);
 		int type = get_wcaps_type(caps);
 
@@ -2743,13 +2560,14 @@
 				cap_nids[nums] = src;
 				break;
 			}
-			n = snd_hda_get_conn_list(codec, src, &list);
+			n = snd_hda_get_num_conns(codec, src);
 			if (n > 1) {
 				cap_nids[nums] = src;
 				break;
 			} else if (n != 1)
 				break;
-			src = *list;
+			if (snd_hda_get_connections(codec, src, &src, 1) != 1)
+				break;
 		}
 		if (++nums >= max_nums)
 			break;
@@ -2856,8 +2674,7 @@
 static void alc_set_pin_output(struct hda_codec *codec, hda_nid_t nid,
 			       unsigned int pin_type)
 {
-	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
-			    pin_type);
+	snd_hda_set_pin_ctl(codec, nid, pin_type);
 	/* unmute pin */
 	if (nid_has_mute(codec, nid, HDA_OUTPUT))
 		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
@@ -2891,7 +2708,7 @@
 
 	/* mute all loopback inputs */
 	if (spec->mixer_nid) {
-		int nums = snd_hda_get_conn_list(codec, spec->mixer_nid, NULL);
+		int nums = snd_hda_get_num_conns(codec, spec->mixer_nid);
 		for (i = 0; i < nums; i++)
 			snd_hda_codec_write(codec, spec->mixer_nid, 0,
 					    AC_VERB_SET_AMP_GAIN_MUTE,
@@ -3521,7 +3338,7 @@
 	if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT) {
 		type = ALC_CTL_WIDGET_MUTE;
 		val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT);
-	} else if (snd_hda_get_conn_list(codec, nid, NULL) == 1) {
+	} else if (snd_hda_get_num_conns(codec, nid) == 1) {
 		type = ALC_CTL_WIDGET_MUTE;
 		val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_INPUT);
 	} else {
@@ -3998,9 +3815,7 @@
 			snd_hda_codec_read(codec, nid, 0,
 					   AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
 	if (output) {
-		snd_hda_codec_update_cache(codec, nid, 0,
-					   AC_VERB_SET_PIN_WIDGET_CONTROL,
-					   PIN_OUT);
+		snd_hda_set_pin_ctl_cache(codec, nid, PIN_OUT);
 		if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)
 			snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
 						 HDA_AMP_MUTE, 0);
@@ -4009,9 +3824,8 @@
 		if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)
 			snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
 						 HDA_AMP_MUTE, HDA_AMP_MUTE);
-		snd_hda_codec_update_cache(codec, nid, 0,
-					   AC_VERB_SET_PIN_WIDGET_CONTROL,
-					   spec->multi_io[idx].ctl_in);
+		snd_hda_set_pin_ctl_cache(codec, nid,
+					  spec->multi_io[idx].ctl_in);
 	}
 	return 0;
 }
@@ -4084,7 +3898,7 @@
 	nums = 0;
 	for (n = 0; n < spec->num_adc_nids; n++) {
 		hda_nid_t cap = spec->private_capsrc_nids[n];
-		int num_conns = snd_hda_get_conn_list(codec, cap, NULL);
+		int num_conns = snd_hda_get_num_conns(codec, cap);
 		for (i = 0; i < imux->num_items; i++) {
 			hda_nid_t pin = spec->imux_pins[i];
 			if (pin) {
@@ -4213,7 +4027,7 @@
 	if (get_wcaps_type(get_wcaps(codec, cap)) == AC_WID_AUD_MIX) {
 		snd_hda_codec_amp_stereo(codec, cap, HDA_INPUT, idx,
 					 HDA_AMP_MUTE, 0);
-	} else if (snd_hda_get_conn_list(codec, cap, NULL) > 1) {
+	} else if (snd_hda_get_num_conns(codec, cap) > 1) {
 		snd_hda_codec_write_cache(codec, cap, 0,
 					  AC_VERB_SET_CONNECT_SEL, idx);
 	}
@@ -4427,6 +4241,25 @@
 	return 1;
 }
 
+/* common preparation job for alc_spec */
+static int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid)
+{
+	struct alc_spec *spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+	int err;
+
+	if (!spec)
+		return -ENOMEM;
+	codec->spec = spec;
+	spec->mixer_nid = mixer_nid;
+
+	err = alc_codec_rename_from_preset(codec);
+	if (err < 0) {
+		kfree(spec);
+		return err;
+	}
+	return 0;
+}
+
 static int alc880_parse_auto_config(struct hda_codec *codec)
 {
 	static const hda_nid_t alc880_ignore[] = { 0x1d, 0 };
@@ -4808,13 +4641,11 @@
 	struct alc_spec *spec;
 	int err;
 
-	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
-	if (spec == NULL)
-		return -ENOMEM;
+	err = alc_alloc_spec(codec, 0x0b);
+	if (err < 0)
+		return err;
 
-	codec->spec = spec;
-
-	spec->mixer_nid = 0x0b;
+	spec = codec->spec;
 	spec->need_dac_fix = 1;
 
 	alc_pick_fixup(codec, alc880_fixup_models, alc880_fixup_tbl,
@@ -4890,7 +4721,7 @@
 		spec->autocfg.hp_pins[0] = 0x0f; /* copy it for automute */
 		snd_hda_jack_detect_enable(codec, 0x0f, ALC_HP_EVENT);
 		spec->unsol_event = alc_sku_unsol_event;
-		add_verb(codec->spec, alc_gpio1_init_verbs);
+		snd_hda_gen_add_verbs(&spec->gen, alc_gpio1_init_verbs);
 	}
 }
 
@@ -5001,13 +4832,11 @@
 	struct alc_spec *spec;
 	int err;
 
-	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
-	if (spec == NULL)
-		return -ENOMEM;
+	err = alc_alloc_spec(codec, 0x07);
+	if (err < 0)
+		return err;
 
-	codec->spec = spec;
-
-	spec->mixer_nid = 0x07;
+	spec = codec->spec;
 
 	alc_pick_fixup(codec, NULL, alc260_fixup_tbl, alc260_fixups);
 	alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
@@ -5171,8 +5000,7 @@
 		val = snd_hda_codec_read(codec, nids[i], 0,
 					 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
 		val |= AC_PINCTL_VREF_80;
-		snd_hda_codec_write(codec, nids[i], 0,
-				    AC_VERB_SET_PIN_WIDGET_CONTROL, val);
+		snd_hda_set_pin_ctl(codec, nids[i], val);
 		spec->keep_vref_in_automute = 1;
 		break;
 	}
@@ -5193,8 +5021,7 @@
 		val = snd_hda_codec_read(codec, nids[i], 0,
 					 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
 		val |= AC_PINCTL_VREF_50;
-		snd_hda_codec_write(codec, nids[i], 0,
-				    AC_VERB_SET_PIN_WIDGET_CONTROL, val);
+		snd_hda_set_pin_ctl(codec, nids[i], val);
 	}
 	spec->keep_vref_in_automute = 1;
 }
@@ -5225,8 +5052,8 @@
 		}
 	},
 	[ALC882_FIXUP_ACER_ASPIRE_7736] = {
-		.type = ALC_FIXUP_SKU,
-		.v.sku = ALC_FIXUP_SKU_IGNORE,
+		.type = ALC_FIXUP_FUNC,
+		.v.func = alc_fixup_sku_ignore,
 	},
 	[ALC882_FIXUP_ASUS_W90V] = {
 		.type = ALC_FIXUP_PINS,
@@ -5476,13 +5303,11 @@
 	struct alc_spec *spec;
 	int err;
 
-	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
-	if (spec == NULL)
-		return -ENOMEM;
+	err = alc_alloc_spec(codec, 0x0b);
+	if (err < 0)
+		return err;
 
-	codec->spec = spec;
-
-	spec->mixer_nid = 0x0b;
+	spec = codec->spec;
 
 	switch (codec->vendor_id) {
 	case 0x10ec0882:
@@ -5494,10 +5319,6 @@
 		break;
 	}
 
-	err = alc_codec_rename_from_preset(codec);
-	if (err < 0)
-		goto error;
-
 	alc_pick_fixup(codec, alc882_fixup_models, alc882_fixup_tbl,
 		       alc882_fixups);
 	alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
@@ -5621,13 +5442,11 @@
 	struct alc_spec *spec;
 	int err;
 
-	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
-	if (spec == NULL)
-		return -ENOMEM;
+	err = alc_alloc_spec(codec, 0x0b);
+	if (err < 0)
+		return err;
 
-	codec->spec = spec;
-
-	spec->mixer_nid = 0x0b;
+	spec = codec->spec;
 
 #if 0
 	/* pshou 07/11/05  set a zero PCM sample to DAC when FIFO is
@@ -5710,7 +5529,7 @@
 	if (err > 0) {
 		if (!spec->no_analog && spec->autocfg.speaker_pins[0] != 0x1d) {
 			add_mixer(spec, alc268_beep_mixer);
-			add_verb(spec, alc268_beep_init_verbs);
+			snd_hda_gen_add_verbs(&spec->gen, alc268_beep_init_verbs);
 		}
 	}
 	return err;
@@ -5723,13 +5542,12 @@
 	struct alc_spec *spec;
 	int i, has_beep, err;
 
-	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
-	if (spec == NULL)
-		return -ENOMEM;
-
-	codec->spec = spec;
-
 	/* ALC268 has no aa-loopback mixer */
+	err = alc_alloc_spec(codec, 0);
+	if (err < 0)
+		return err;
+
+	spec = codec->spec;
 
 	/* automatic parse from the BIOS config */
 	err = alc268_parse_auto_config(codec);
@@ -5946,9 +5764,7 @@
 {
 	struct hda_codec *codec = private_data;
 	unsigned int pinval = enabled ? 0x20 : 0x24;
-	snd_hda_codec_update_cache(codec, 0x19, 0,
-				   AC_VERB_SET_PIN_WIDGET_CONTROL,
-				   pinval);
+	snd_hda_set_pin_ctl_cache(codec, 0x19, pinval);
 }
 
 static void alc269_fixup_mic2_mute(struct hda_codec *codec,
@@ -6015,8 +5831,8 @@
 		}
 	},
 	[ALC269_FIXUP_SKU_IGNORE] = {
-		.type = ALC_FIXUP_SKU,
-		.v.sku = ALC_FIXUP_SKU_IGNORE,
+		.type = ALC_FIXUP_FUNC,
+		.v.func = alc_fixup_sku_ignore,
 	},
 	[ALC269_FIXUP_ASUS_G73JW] = {
 		.type = ALC_FIXUP_PINS,
@@ -6242,19 +6058,13 @@
 static int patch_alc269(struct hda_codec *codec)
 {
 	struct alc_spec *spec;
-	int err = 0;
+	int err;
 
-	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
-	if (spec == NULL)
-		return -ENOMEM;
-
-	codec->spec = spec;
-
-	spec->mixer_nid = 0x0b;
-
-	err = alc_codec_rename_from_preset(codec);
+	err = alc_alloc_spec(codec, 0x0b);
 	if (err < 0)
-		goto error;
+		return err;
+
+	spec = codec->spec;
 
 	if (codec->vendor_id == 0x10ec0269) {
 		spec->codec_variant = ALC269_TYPE_ALC269VA;
@@ -6346,8 +6156,7 @@
 	if (!(val & (AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN)))
 		val |= AC_PINCTL_IN_EN;
 	val |= AC_PINCTL_VREF_50;
-	snd_hda_codec_write(codec, 0x0f, 0,
-			    AC_VERB_SET_PIN_WIDGET_CONTROL, val);
+	snd_hda_set_pin_ctl(codec, 0x0f, val);
 	spec->keep_vref_in_automute = 1;
 }
 
@@ -6401,13 +6210,11 @@
 	struct alc_spec *spec;
 	int err;
 
-	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
-	if (spec == NULL)
-		return -ENOMEM;
+	err = alc_alloc_spec(codec, 0x15);
+	if (err < 0)
+		return err;
 
-	codec->spec = spec;
-
-	spec->mixer_nid = 0x15;
+	spec = codec->spec;
 
 	alc_pick_fixup(codec, NULL, alc861_fixup_tbl, alc861_fixups);
 	alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
@@ -6504,13 +6311,11 @@
 	struct alc_spec *spec;
 	int err;
 
-	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
-	if (spec == NULL)
-		return -ENOMEM;
+	err = alc_alloc_spec(codec, 0x0b);
+	if (err < 0)
+		return err;
 
-	codec->spec = spec;
-
-	spec->mixer_nid = 0x0b;
+	spec = codec->spec;
 
 	alc_pick_fixup(codec, NULL, alc861vd_fixup_tbl, alc861vd_fixups);
 	alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
@@ -6522,7 +6327,7 @@
 
 	if (codec->vendor_id == 0x10ec0660) {
 		/* always turn on EAPD */
-		add_verb(spec, alc660vd_eapd_verbs);
+		snd_hda_gen_add_verbs(&spec->gen, alc660vd_eapd_verbs);
 	}
 
 	if (!spec->no_analog) {
@@ -6635,8 +6440,8 @@
 		}
 	},
 	[ALC662_FIXUP_SKU_IGNORE] = {
-		.type = ALC_FIXUP_SKU,
-		.v.sku = ALC_FIXUP_SKU_IGNORE,
+		.type = ALC_FIXUP_FUNC,
+		.v.func = alc_fixup_sku_ignore,
 	},
 	[ALC662_FIXUP_HP_RP5800] = {
 		.type = ALC_FIXUP_PINS,
@@ -6849,25 +6654,19 @@
 static int patch_alc662(struct hda_codec *codec)
 {
 	struct alc_spec *spec;
-	int err = 0;
+	int err;
 
-	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
-	if (!spec)
-		return -ENOMEM;
+	err = alc_alloc_spec(codec, 0x0b);
+	if (err < 0)
+		return err;
 
-	codec->spec = spec;
-
-	spec->mixer_nid = 0x0b;
+	spec = codec->spec;
 
 	/* handle multiple HPs as is */
 	spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP;
 
 	alc_fix_pll_init(codec, 0x20, 0x04, 15);
 
-	err = alc_codec_rename_from_preset(codec);
-	if (err < 0)
-		goto error;
-
 	if ((alc_get_coef0(codec) & (1 << 14)) &&
 	    codec->bus->pci->subsystem_vendor == 0x1025 &&
 	    spec->cdefine.platform_type == 1) {
@@ -6930,16 +6729,12 @@
  */
 static int patch_alc680(struct hda_codec *codec)
 {
-	struct alc_spec *spec;
 	int err;
 
-	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
-	if (spec == NULL)
-		return -ENOMEM;
-
-	codec->spec = spec;
-
 	/* ALC680 has no aa-loopback mixer */
+	err = alc_alloc_spec(codec, 0);
+	if (err < 0)
+		return err;
 
 	/* automatic parse from the BIOS config */
 	err = alc680_parse_auto_config(codec);
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index 2cb1e08..7db8228 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -36,6 +36,7 @@
 #include <sound/tlv.h>
 #include "hda_codec.h"
 #include "hda_local.h"
+#include "hda_auto_parser.h"
 #include "hda_beep.h"
 #include "hda_jack.h"
 
@@ -221,6 +222,7 @@
 	unsigned char aloopback_shift;
 
 	/* power management */
+	unsigned int power_map_bits;
 	unsigned int num_pwrs;
 	const hda_nid_t *pwr_nids;
 	const hda_nid_t *dac_list;
@@ -314,6 +316,9 @@
 	struct hda_vmaster_mute_hook vmaster_mute;
 };
 
+#define AC_VERB_IDT_SET_POWER_MAP	0x7ec
+#define AC_VERB_IDT_GET_POWER_MAP	0xfec
+
 static const hda_nid_t stac9200_adc_nids[1] = {
         0x03,
 };
@@ -681,8 +686,7 @@
 	pinctl &= ~AC_PINCTL_VREFEN;
 	pinctl |= (new_vref & AC_PINCTL_VREFEN);
 
-	error = snd_hda_codec_write_cache(codec, nid, 0,
-					AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl);
+	error = snd_hda_set_pin_ctl_cache(codec, nid, pinctl);
 	if (error < 0)
 		return error;
 
@@ -706,8 +710,7 @@
 	else
 		pincfg |= AC_PINCTL_IN_EN;
 
-	error = snd_hda_codec_write_cache(codec, nid, 0,
-					AC_VERB_SET_PIN_WIDGET_CONTROL, pincfg);
+	error = snd_hda_set_pin_ctl_cache(codec, nid, pincfg);
 	if (error < 0)
 		return error;
 	else
@@ -2505,27 +2508,10 @@
 	return 0;
 }
 
-static unsigned int stac92xx_get_default_vref(struct hda_codec *codec,
-					hda_nid_t nid)
-{
-	unsigned int pincap = snd_hda_query_pin_caps(codec, nid);
-	pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
-	if (pincap & AC_PINCAP_VREF_100)
-		return AC_PINCTL_VREF_100;
-	if (pincap & AC_PINCAP_VREF_80)
-		return AC_PINCTL_VREF_80;
-	if (pincap & AC_PINCAP_VREF_50)
-		return AC_PINCTL_VREF_50;
-	if (pincap & AC_PINCAP_VREF_GRD)
-		return AC_PINCTL_VREF_GRD;
-	return 0;
-}
-
 static void stac92xx_auto_set_pinctl(struct hda_codec *codec, hda_nid_t nid, int pin_type)
 
 {
-	snd_hda_codec_write_cache(codec, nid, 0,
-				  AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
+	snd_hda_set_pin_ctl_cache(codec, nid, pin_type);
 }
 
 #define stac92xx_hp_switch_info		snd_ctl_boolean_mono_info
@@ -2594,7 +2580,7 @@
 	hda_nid_t nid = kcontrol->private_value;
 	unsigned int vref = stac92xx_vref_get(codec, nid);
 
-	if (vref == stac92xx_get_default_vref(codec, nid))
+	if (vref == snd_hda_get_default_vref(codec, nid))
 		ucontrol->value.enumerated.item[0] = 0;
 	else if (vref == AC_PINCTL_VREF_GRD)
 		ucontrol->value.enumerated.item[0] = 1;
@@ -2613,7 +2599,7 @@
 	hda_nid_t nid = kcontrol->private_value;
 
 	if (ucontrol->value.enumerated.item[0] == 0)
-		new_vref = stac92xx_get_default_vref(codec, nid);
+		new_vref = snd_hda_get_default_vref(codec, nid);
 	else if (ucontrol->value.enumerated.item[0] == 1)
 		new_vref = AC_PINCTL_VREF_GRD;
 	else if (ucontrol->value.enumerated.item[0] == 2)
@@ -2679,7 +2665,7 @@
 	else {
 		unsigned int pinctl = AC_PINCTL_IN_EN;
 		if (io_idx) /* set VREF for mic */
-			pinctl |= stac92xx_get_default_vref(codec, nid);
+			pinctl |= snd_hda_get_default_vref(codec, nid);
 		stac92xx_auto_set_pinctl(codec, nid, pinctl);
 	}
 
@@ -2847,7 +2833,7 @@
 	char name[22];
 
 	if (snd_hda_get_input_pin_attr(def_conf) != INPUT_PIN_ATTR_INT) {
-		if (stac92xx_get_default_vref(codec, nid) == AC_PINCTL_VREF_GRD
+		if (snd_hda_get_default_vref(codec, nid) == AC_PINCTL_VREF_GRD
 			&& nid == spec->line_switch)
 			control = STAC_CTL_WIDGET_IO_SWITCH;
 		else if (snd_hda_query_pin_caps(codec, nid)
@@ -4250,13 +4236,6 @@
 	val = snd_hda_get_bool_hint(codec, "eapd_switch");
 	if (val >= 0)
 		spec->eapd_switch = val;
-	get_int_hint(codec, "gpio_led_polarity", &spec->gpio_led_polarity);
-	if (get_int_hint(codec, "gpio_led", &spec->gpio_led)) {
-		spec->gpio_mask |= spec->gpio_led;
-		spec->gpio_dir |= spec->gpio_led;
-		if (spec->gpio_led_polarity)
-			spec->gpio_data |= spec->gpio_led;
-	}
 }
 
 static void stac_issue_unsol_events(struct hda_codec *codec, int num_pins,
@@ -4354,7 +4333,7 @@
 		unsigned int pinctl, conf;
 		if (type == AUTO_PIN_MIC) {
 			/* for mic pins, force to initialize */
-			pinctl = stac92xx_get_default_vref(codec, nid);
+			pinctl = snd_hda_get_default_vref(codec, nid);
 			pinctl |= AC_PINCTL_IN_EN;
 			stac92xx_auto_set_pinctl(codec, nid, pinctl);
 		} else {
@@ -4390,10 +4369,18 @@
 		hda_nid_t nid = spec->pwr_nids[i];
 		int pinctl, def_conf;
 
+		def_conf = snd_hda_codec_get_pincfg(codec, nid);
+		def_conf = get_defcfg_connect(def_conf);
+		if (def_conf == AC_JACK_PORT_NONE) {
+			/* power off unused ports */
+			stac_toggle_power_map(codec, nid, 0);
+			continue;
+		}
 		/* power on when no jack detection is available */
 		/* or when the VREF is used for controlling LED */
 		if (!spec->hp_detect ||
-		    spec->vref_mute_led_nid == nid) {
+		    spec->vref_mute_led_nid == nid ||
+		    !is_jack_detectable(codec, nid)) {
 			stac_toggle_power_map(codec, nid, 1);
 			continue;
 		}
@@ -4411,15 +4398,6 @@
 			stac_toggle_power_map(codec, nid, 1);
 			continue;
 		}
-		def_conf = snd_hda_codec_get_pincfg(codec, nid);
-		def_conf = get_defcfg_connect(def_conf);
-		/* skip any ports that don't have jacks since presence
- 		 * detection is useless */
-		if (def_conf != AC_JACK_PORT_NONE &&
-		    !is_jack_detectable(codec, nid)) {
-			stac_toggle_power_map(codec, nid, 1);
-			continue;
-		}
 		if (enable_pin_detect(codec, nid, STAC_PWR_EVENT)) {
 			stac_issue_unsol_event(codec, nid);
 			continue;
@@ -4432,6 +4410,12 @@
 
 	/* sync mute LED */
 	snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
+
+	/* sync the power-map */
+	if (spec->num_pwrs)
+		snd_hda_codec_write(codec, codec->afg, 0,
+				    AC_VERB_IDT_SET_POWER_MAP,
+				    spec->power_map_bits);
 	if (spec->dac_list)
 		stac92xx_power_down(codec);
 	return 0;
@@ -4460,8 +4444,7 @@
 		struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i);
 		def_conf = snd_hda_codec_get_pincfg(codec, pin->nid);
 		if (get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)
-			snd_hda_codec_write(codec, pin->nid, 0,
-				    AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
+			snd_hda_set_pin_ctl(codec, pin->nid, 0);
 	}
 }
 
@@ -4517,9 +4500,7 @@
 	
 	pin_ctl |= flag;
 	if (old_ctl != pin_ctl)
-		snd_hda_codec_write_cache(codec, nid, 0,
-					  AC_VERB_SET_PIN_WIDGET_CONTROL,
-					  pin_ctl);
+		snd_hda_set_pin_ctl_cache(codec, nid, pin_ctl);
 }
 
 static void stac92xx_reset_pinctl(struct hda_codec *codec, hda_nid_t nid,
@@ -4528,9 +4509,7 @@
 	unsigned int pin_ctl = snd_hda_codec_read(codec, nid,
 			0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00);
 	if (pin_ctl & flag)
-		snd_hda_codec_write_cache(codec, nid, 0,
-					  AC_VERB_SET_PIN_WIDGET_CONTROL,
-					  pin_ctl & ~flag);
+		snd_hda_set_pin_ctl_cache(codec, nid, pin_ctl & ~flag);
 }
 
 static inline int get_pin_presence(struct hda_codec *codec, hda_nid_t nid)
@@ -4682,14 +4661,18 @@
 
 	idx = 1 << idx;
 
-	val = snd_hda_codec_read(codec, codec->afg, 0, 0x0fec, 0x0) & 0xff;
+	val = spec->power_map_bits;
 	if (enable)
 		val &= ~idx;
 	else
 		val |= idx;
 
 	/* power down unused output ports */
-	snd_hda_codec_write(codec, codec->afg, 0, 0x7ec, val);
+	if (val != spec->power_map_bits) {
+		spec->power_map_bits = val;
+		snd_hda_codec_write(codec, codec->afg, 0,
+				    AC_VERB_IDT_SET_POWER_MAP, val);
+	}
 }
 
 static void stac92xx_pin_sense(struct hda_codec *codec, hda_nid_t nid)
@@ -4866,6 +4849,11 @@
 	struct sigmatel_spec *spec = codec->spec;
 	const struct dmi_device *dev = NULL;
 
+	if (get_int_hint(codec, "gpio_led", &spec->gpio_led)) {
+		get_int_hint(codec, "gpio_led_polarity",
+			     &spec->gpio_led_polarity);
+		return 1;
+	}
 	if ((codec->subsystem_id >> 16) == PCI_VENDOR_ID_HP) {
 		while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING,
 								NULL, dev))) {
@@ -4952,7 +4940,8 @@
 {
 	if (nid == codec->afg)
 		snd_iprintf(buffer, "Power-Map: 0x%02x\n", 
-			    snd_hda_codec_read(codec, nid, 0, 0x0fec, 0x0));
+			    snd_hda_codec_read(codec, nid, 0,
+					       AC_VERB_IDT_GET_POWER_MAP, 0));
 }
 
 static void analog_loop_proc_hook(struct snd_info_buffer *buffer,
@@ -5009,20 +4998,6 @@
 	return 0;
 }
 
-static int stac92xx_pre_resume(struct hda_codec *codec)
-{
-	struct sigmatel_spec *spec = codec->spec;
-
-	/* sync mute LED */
-	if (spec->vref_mute_led_nid)
-		stac_vrefout_set(codec, spec->vref_mute_led_nid,
-				 spec->vref_led);
-	else if (spec->gpio_led)
-		stac_gpio_set(codec, spec->gpio_mask,
-			      spec->gpio_dir, spec->gpio_data);
-	return 0;
-}
-
 static void stac92xx_set_power_state(struct hda_codec *codec, hda_nid_t fg,
 				unsigned int power_state)
 {
@@ -5046,7 +5021,6 @@
 #else
 #define stac92xx_suspend	NULL
 #define stac92xx_resume		NULL
-#define stac92xx_pre_resume	NULL
 #define stac92xx_set_power_state NULL
 #endif /* CONFIG_PM */
 
@@ -5592,9 +5566,6 @@
 			codec->patch_ops.set_power_state =
 					stac92xx_set_power_state;
 		}
-#ifdef CONFIG_PM
-		codec->patch_ops.pre_resume = stac92xx_pre_resume;
-#endif
 	}
 
 	err = stac92xx_parse_auto_config(codec);
@@ -5901,9 +5872,6 @@
 			codec->patch_ops.set_power_state =
 					stac92xx_set_power_state;
 		}
-#ifdef CONFIG_PM
-		codec->patch_ops.pre_resume = stac92xx_pre_resume;
-#endif
 	}
 
 	spec->multiout.dac_nids = spec->dac_nids;
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index 06214fd..82b3680 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -54,6 +54,7 @@
 #include <sound/asoundef.h>
 #include "hda_codec.h"
 #include "hda_local.h"
+#include "hda_auto_parser.h"
 #include "hda_jack.h"
 
 /* Pin Widget NID */
@@ -484,7 +485,7 @@
 
 	if (!path)
 		return;
-	num = snd_hda_get_conn_list(codec, mix_nid, NULL);
+	num = snd_hda_get_num_conns(codec, mix_nid);
 	for (i = 0; i < num; i++) {
 		if (i == idx)
 			val = AMP_IN_UNMUTE(i);
@@ -532,8 +533,7 @@
 {
 	if (!pin)
 		return;
-	snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
-			    pin_type);
+	snd_hda_set_pin_ctl(codec, pin, pin_type);
 	if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)
 		snd_hda_codec_write(codec, pin, 0,
 				    AC_VERB_SET_EAPD_BTLENABLE, 0x02);
@@ -662,12 +662,12 @@
 		hda_nid_t nid = cfg->inputs[i].pin;
 		if (spec->smart51_enabled && is_smart51_pins(codec, nid))
 			ctl = PIN_OUT;
-		else if (cfg->inputs[i].type == AUTO_PIN_MIC)
-			ctl = PIN_VREF50;
-		else
+		else {
 			ctl = PIN_IN;
-		snd_hda_codec_write(codec, nid, 0,
-				    AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
+			if (cfg->inputs[i].type == AUTO_PIN_MIC)
+				ctl |= snd_hda_get_default_vref(codec, nid);
+		}
+		snd_hda_set_pin_ctl(codec, nid, ctl);
 	}
 
 	/* init input-src */
@@ -1006,9 +1006,7 @@
 					  AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
 		parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
 		parm |= out_in;
-		snd_hda_codec_write(codec, nid, 0,
-				    AC_VERB_SET_PIN_WIDGET_CONTROL,
-				    parm);
+		snd_hda_set_pin_ctl(codec, nid, parm);
 		if (out_in == AC_PINCTL_OUT_EN) {
 			mute_aa_path(codec, 1);
 			notify_aa_path_ctls(codec);
@@ -1647,8 +1645,7 @@
 			parm &= ~AC_PINCTL_OUT_EN;
 		else
 			parm |= AC_PINCTL_OUT_EN;
-		snd_hda_codec_write(codec, pins[i], 0,
-				    AC_VERB_SET_PIN_WIDGET_CONTROL, parm);
+		snd_hda_set_pin_ctl(codec, pins[i], parm);
 	}
 }
 
@@ -1709,8 +1706,7 @@
 
 	if (gpio_data == 0x02) {
 		/* unmute line out */
-		snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
-				    AC_VERB_SET_PIN_WIDGET_CONTROL,
+		snd_hda_set_pin_ctl(codec, spec->autocfg.line_out_pins[0],
 				    PIN_OUT);
 		if (vol_counter & 0x20) {
 			/* decrease volume */
@@ -1728,9 +1724,7 @@
 		}
 	} else if (!(gpio_data & 0x02)) {
 		/* mute line out */
-		snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
-				    AC_VERB_SET_PIN_WIDGET_CONTROL,
-				    0);
+		snd_hda_set_pin_ctl(codec, spec->autocfg.line_out_pins[0], 0);
 	}
 }
 
@@ -2757,8 +2751,7 @@
 	struct via_spec *spec = codec->spec;
 	if (!spec->dig_in_nid)
 		return;
-	snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
-			    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
+	snd_hda_set_pin_ctl(codec, spec->autocfg.dig_in_pin, PIN_IN);
 }
 
 /* initialize the unsolicited events */
diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c
index 132a86e..5be2e12 100644
--- a/sound/pci/ice1712/ice1712.c
+++ b/sound/pci/ice1712/ice1712.c
@@ -2803,22 +2803,11 @@
 	pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver ice1712_driver = {
 	.name = KBUILD_MODNAME,
 	.id_table = snd_ice1712_ids,
 	.probe = snd_ice1712_probe,
 	.remove = __devexit_p(snd_ice1712_remove),
 };
 
-static int __init alsa_card_ice1712_init(void)
-{
-	return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_ice1712_exit(void)
-{
-	pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_ice1712_init)
-module_exit(alsa_card_ice1712_exit)
+module_pci_driver(ice1712_driver);
diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c
index 812d10e..a01a00d 100644
--- a/sound/pci/ice1712/ice1724.c
+++ b/sound/pci/ice1712/ice1724.c
@@ -2873,7 +2873,7 @@
 }
 #endif
 
-static struct pci_driver driver = {
+static struct pci_driver vt1724_driver = {
 	.name = KBUILD_MODNAME,
 	.id_table = snd_vt1724_ids,
 	.probe = snd_vt1724_probe,
@@ -2884,15 +2884,4 @@
 #endif
 };
 
-static int __init alsa_card_ice1724_init(void)
-{
-	return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_ice1724_exit(void)
-{
-	pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_ice1724_init)
-module_exit(alsa_card_ice1724_exit)
+module_pci_driver(vt1724_driver);
diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c
index e0a4263..f4e2dd4 100644
--- a/sound/pci/intel8x0.c
+++ b/sound/pci/intel8x0.c
@@ -3338,7 +3338,7 @@
 	pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver intel8x0_driver = {
 	.name = KBUILD_MODNAME,
 	.id_table = snd_intel8x0_ids,
 	.probe = snd_intel8x0_probe,
@@ -3349,16 +3349,4 @@
 #endif
 };
 
-
-static int __init alsa_card_intel8x0_init(void)
-{
-	return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_intel8x0_exit(void)
-{
-	pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_intel8x0_init)
-module_exit(alsa_card_intel8x0_exit)
+module_pci_driver(intel8x0_driver);
diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c
index d689913..fc27a6a 100644
--- a/sound/pci/intel8x0m.c
+++ b/sound/pci/intel8x0m.c
@@ -1324,7 +1324,7 @@
 	pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver intel8x0m_driver = {
 	.name = KBUILD_MODNAME,
 	.id_table = snd_intel8x0m_ids,
 	.probe = snd_intel8x0m_probe,
@@ -1335,16 +1335,4 @@
 #endif
 };
 
-
-static int __init alsa_card_intel8x0m_init(void)
-{
-	return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_intel8x0m_exit(void)
-{
-	pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_intel8x0m_init)
-module_exit(alsa_card_intel8x0m_exit)
+module_pci_driver(intel8x0m_driver);
diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c
index 8fea45a..e69ce5f 100644
--- a/sound/pci/korg1212/korg1212.c
+++ b/sound/pci/korg1212/korg1212.c
@@ -2476,22 +2476,11 @@
 	pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver korg1212_driver = {
 	.name = KBUILD_MODNAME,
 	.id_table = snd_korg1212_ids,
 	.probe = snd_korg1212_probe,
 	.remove = __devexit_p(snd_korg1212_remove),
 };
 
-static int __init alsa_card_korg1212_init(void)
-{
-	return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_korg1212_exit(void)
-{
-	pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_korg1212_init)
-module_exit(alsa_card_korg1212_exit)
+module_pci_driver(korg1212_driver);
diff --git a/sound/pci/lola/lola.c b/sound/pci/lola/lola.c
index 3759827..ac15166 100644
--- a/sound/pci/lola/lola.c
+++ b/sound/pci/lola/lola.c
@@ -770,22 +770,11 @@
 MODULE_DEVICE_TABLE(pci, lola_ids);
 
 /* pci_driver definition */
-static struct pci_driver driver = {
+static struct pci_driver lola_driver = {
 	.name = KBUILD_MODNAME,
 	.id_table = lola_ids,
 	.probe = lola_probe,
 	.remove = __devexit_p(lola_remove),
 };
 
-static int __init alsa_card_lola_init(void)
-{
-	return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_lola_exit(void)
-{
-	pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_lola_init)
-module_exit(alsa_card_lola_exit)
+module_pci_driver(lola_driver);
diff --git a/sound/pci/lx6464es/lx6464es.c b/sound/pci/lx6464es/lx6464es.c
index d94c0c2..d1ab437 100644
--- a/sound/pci/lx6464es/lx6464es.c
+++ b/sound/pci/lx6464es/lx6464es.c
@@ -1141,24 +1141,11 @@
 }
 
 
-static struct pci_driver driver = {
+static struct pci_driver lx6464es_driver = {
 	.name =     KBUILD_MODNAME,
 	.id_table = snd_lx6464es_ids,
 	.probe =    snd_lx6464es_probe,
 	.remove = __devexit_p(snd_lx6464es_remove),
 };
 
-
-/* module initialization */
-static int __init mod_init(void)
-{
-	return pci_register_driver(&driver);
-}
-
-static void __exit mod_exit(void)
-{
-	pci_unregister_driver(&driver);
-}
-
-module_init(mod_init);
-module_exit(mod_exit);
+module_pci_driver(lx6464es_driver);
diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c
index 78229b0..deef213 100644
--- a/sound/pci/maestro3.c
+++ b/sound/pci/maestro3.c
@@ -2837,7 +2837,7 @@
 	pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver m3_driver = {
 	.name = KBUILD_MODNAME,
 	.id_table = snd_m3_ids,
 	.probe = snd_m3_probe,
@@ -2848,15 +2848,4 @@
 #endif
 };
 	
-static int __init alsa_card_m3_init(void)
-{
-	return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_m3_exit(void)
-{
-	pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_m3_init)
-module_exit(alsa_card_m3_exit)
+module_pci_driver(m3_driver);
diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c
index 487837c..0762610 100644
--- a/sound/pci/mixart/mixart.c
+++ b/sound/pci/mixart/mixart.c
@@ -1380,22 +1380,11 @@
 	pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver mixart_driver = {
 	.name = KBUILD_MODNAME,
 	.id_table = snd_mixart_ids,
 	.probe = snd_mixart_probe,
 	.remove = __devexit_p(snd_mixart_remove),
 };
 
-static int __init alsa_card_mixart_init(void)
-{
-	return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_mixart_exit(void)
-{
-	pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_mixart_init)
-module_exit(alsa_card_mixart_exit)
+module_pci_driver(mixart_driver);
diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c
index ade2c64..8159b05 100644
--- a/sound/pci/nm256/nm256.c
+++ b/sound/pci/nm256/nm256.c
@@ -1742,7 +1742,7 @@
 }
 
 
-static struct pci_driver driver = {
+static struct pci_driver nm256_driver = {
 	.name = KBUILD_MODNAME,
 	.id_table = snd_nm256_ids,
 	.probe = snd_nm256_probe,
@@ -1753,16 +1753,4 @@
 #endif
 };
 
-
-static int __init alsa_card_nm256_init(void)
-{
-	return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_nm256_exit(void)
-{
-	pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_nm256_init)
-module_exit(alsa_card_nm256_exit)
+module_pci_driver(nm256_driver);
diff --git a/sound/pci/oxygen/oxygen.c b/sound/pci/oxygen/oxygen.c
index eab663e..610275b 100644
--- a/sound/pci/oxygen/oxygen.c
+++ b/sound/pci/oxygen/oxygen.c
@@ -94,6 +94,7 @@
 	MODEL_2CH_OUTPUT,
 	MODEL_HG2PCI,
 	MODEL_XONAR_DG,
+	MODEL_XONAR_DGX,
 };
 
 static DEFINE_PCI_DEVICE_TABLE(oxygen_ids) = {
@@ -109,6 +110,8 @@
 	{ OXYGEN_PCI_SUBID(0x1a58, 0x0910), .driver_data = MODEL_CMEDIA_REF },
 	/* Asus Xonar DG */
 	{ OXYGEN_PCI_SUBID(0x1043, 0x8467), .driver_data = MODEL_XONAR_DG },
+	/* Asus Xonar DGX */
+	{ OXYGEN_PCI_SUBID(0x1043, 0x8521), .driver_data = MODEL_XONAR_DGX },
 	/* PCI 2.0 HD Audio */
 	{ OXYGEN_PCI_SUBID(0x13f6, 0x8782), .driver_data = MODEL_2CH_OUTPUT },
 	/* Kuroutoshikou CMI8787-HG2PCI */
@@ -827,6 +830,11 @@
 		break;
 	case MODEL_XONAR_DG:
 		chip->model = model_xonar_dg;
+		chip->model.shortname = "Xonar DG";
+		break;
+	case MODEL_XONAR_DGX:
+		chip->model = model_xonar_dg;
+		chip->model.shortname = "Xonar DGX";
 		break;
 	}
 	if (id->driver_data == MODEL_MERIDIAN ||
@@ -870,15 +878,4 @@
 #endif
 };
 
-static int __init alsa_card_oxygen_init(void)
-{
-	return pci_register_driver(&oxygen_driver);
-}
-
-static void __exit alsa_card_oxygen_exit(void)
-{
-	pci_unregister_driver(&oxygen_driver);
-}
-
-module_init(alsa_card_oxygen_init)
-module_exit(alsa_card_oxygen_exit)
+module_pci_driver(oxygen_driver);
diff --git a/sound/pci/oxygen/virtuoso.c b/sound/pci/oxygen/virtuoso.c
index 3fdee49..19962c6 100644
--- a/sound/pci/oxygen/virtuoso.c
+++ b/sound/pci/oxygen/virtuoso.c
@@ -100,15 +100,4 @@
 	.shutdown = oxygen_pci_shutdown,
 };
 
-static int __init alsa_card_xonar_init(void)
-{
-	return pci_register_driver(&xonar_driver);
-}
-
-static void __exit alsa_card_xonar_exit(void)
-{
-	pci_unregister_driver(&xonar_driver);
-}
-
-module_init(alsa_card_xonar_init)
-module_exit(alsa_card_xonar_exit)
+module_pci_driver(xonar_driver);
diff --git a/sound/pci/oxygen/xonar_dg.c b/sound/pci/oxygen/xonar_dg.c
index 793bdf0..77acd79 100644
--- a/sound/pci/oxygen/xonar_dg.c
+++ b/sound/pci/oxygen/xonar_dg.c
@@ -1,5 +1,5 @@
 /*
- * card driver for the Xonar DG
+ * card driver for the Xonar DG/DGX
  *
  * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
  *
@@ -17,8 +17,8 @@
  */
 
 /*
- * Xonar DG
- * --------
+ * Xonar DG/DGX
+ * ------------
  *
  * CMI8788:
  *
@@ -581,7 +581,6 @@
 }
 
 struct oxygen_model model_xonar_dg = {
-	.shortname = "Xonar DG",
 	.longname = "C-Media Oxygen HD Audio",
 	.chip = "CMI8786",
 	.init = dg_init,
diff --git a/sound/pci/pcxhr/pcxhr.c b/sound/pci/pcxhr/pcxhr.c
index fd1809a..0435f45 100644
--- a/sound/pci/pcxhr/pcxhr.c
+++ b/sound/pci/pcxhr/pcxhr.c
@@ -1607,22 +1607,11 @@
 	pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver pcxhr_driver = {
 	.name = KBUILD_MODNAME,
 	.id_table = pcxhr_ids,
 	.probe = pcxhr_probe,
 	.remove = __devexit_p(pcxhr_remove),
 };
 
-static int __init pcxhr_module_init(void)
-{
-	return pci_register_driver(&driver);
-}
-
-static void __exit pcxhr_module_exit(void)
-{
-	pci_unregister_driver(&driver);
-}
-
-module_init(pcxhr_module_init)
-module_exit(pcxhr_module_exit)
+module_pci_driver(pcxhr_driver);
diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c
index 0481d94..cbeb3f7 100644
--- a/sound/pci/riptide/riptide.c
+++ b/sound/pci/riptide/riptide.c
@@ -1837,8 +1837,7 @@
 	}
 	if (chip->irq >= 0)
 		free_irq(chip->irq, chip);
-	if (chip->fw_entry)
-		release_firmware(chip->fw_entry);
+	release_firmware(chip->fw_entry);
 	release_and_free_resource(chip->res_port);
 	kfree(chip);
 	return 0;
diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c
index b4819d5..46b3629 100644
--- a/sound/pci/rme32.c
+++ b/sound/pci/rme32.c
@@ -1984,22 +1984,11 @@
 	pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver rme32_driver = {
 	.name =		KBUILD_MODNAME,
 	.id_table =	snd_rme32_ids,
 	.probe =	snd_rme32_probe,
 	.remove =	__devexit_p(snd_rme32_remove),
 };
 
-static int __init alsa_card_rme32_init(void)
-{
-	return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_rme32_exit(void)
-{
-	pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_rme32_init)
-module_exit(alsa_card_rme32_exit)
+module_pci_driver(rme32_driver);
diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c
index ba89415..9b98dc4 100644
--- a/sound/pci/rme96.c
+++ b/sound/pci/rme96.c
@@ -2395,22 +2395,11 @@
 	pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver rme96_driver = {
 	.name = KBUILD_MODNAME,
 	.id_table = snd_rme96_ids,
 	.probe = snd_rme96_probe,
 	.remove = __devexit_p(snd_rme96_remove),
 };
 
-static int __init alsa_card_rme96_init(void)
-{
-	return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_rme96_exit(void)
-{
-	pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_rme96_init)
-module_exit(alsa_card_rme96_exit)
+module_pci_driver(rme96_driver);
diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c
index 0b2aea2..0d6930c 100644
--- a/sound/pci/rme9652/hdsp.c
+++ b/sound/pci/rme9652/hdsp.c
@@ -5636,22 +5636,11 @@
 	pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver hdsp_driver = {
 	.name =     KBUILD_MODNAME,
 	.id_table = snd_hdsp_ids,
 	.probe =    snd_hdsp_probe,
 	.remove = __devexit_p(snd_hdsp_remove),
 };
 
-static int __init alsa_card_hdsp_init(void)
-{
-	return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_hdsp_exit(void)
-{
-	pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_hdsp_init)
-module_exit(alsa_card_hdsp_exit)
+module_pci_driver(hdsp_driver);
diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c
index bc030a2..0a5027b 100644
--- a/sound/pci/rme9652/hdspm.c
+++ b/sound/pci/rme9652/hdspm.c
@@ -6918,23 +6918,11 @@
 	pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver hdspm_driver = {
 	.name = KBUILD_MODNAME,
 	.id_table = snd_hdspm_ids,
 	.probe = snd_hdspm_probe,
 	.remove = __devexit_p(snd_hdspm_remove),
 };
 
-
-static int __init alsa_card_hdspm_init(void)
-{
-	return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_hdspm_exit(void)
-{
-	pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_hdspm_init)
-module_exit(alsa_card_hdspm_exit)
+module_pci_driver(hdspm_driver);
diff --git a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c
index b737d16..a15fc10 100644
--- a/sound/pci/rme9652/rme9652.c
+++ b/sound/pci/rme9652/rme9652.c
@@ -2631,22 +2631,11 @@
 	pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver rme9652_driver = {
 	.name	  = KBUILD_MODNAME,
 	.id_table = snd_rme9652_ids,
 	.probe	  = snd_rme9652_probe,
 	.remove	  = __devexit_p(snd_rme9652_remove),
 };
 
-static int __init alsa_card_hammerfall_init(void)
-{
-	return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_hammerfall_exit(void)
-{
-	pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_hammerfall_init)
-module_exit(alsa_card_hammerfall_exit)
+module_pci_driver(rme9652_driver);
diff --git a/sound/pci/sis7019.c b/sound/pci/sis7019.c
index ff500a8..1552642 100644
--- a/sound/pci/sis7019.c
+++ b/sound/pci/sis7019.c
@@ -1488,15 +1488,4 @@
 #endif
 };
 
-static int __init sis7019_init(void)
-{
-	return pci_register_driver(&sis7019_driver);
-}
-
-static void __exit sis7019_exit(void)
-{
-	pci_unregister_driver(&sis7019_driver);
-}
-
-module_init(sis7019_init);
-module_exit(sis7019_exit);
+module_pci_driver(sis7019_driver);
diff --git a/sound/pci/sonicvibes.c b/sound/pci/sonicvibes.c
index 54cc802..baa9946b 100644
--- a/sound/pci/sonicvibes.c
+++ b/sound/pci/sonicvibes.c
@@ -1530,22 +1530,11 @@
 	pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver sonicvibes_driver = {
 	.name = KBUILD_MODNAME,
 	.id_table = snd_sonic_ids,
 	.probe = snd_sonic_probe,
 	.remove = __devexit_p(snd_sonic_remove),
 };
 
-static int __init alsa_card_sonicvibes_init(void)
-{
-	return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_sonicvibes_exit(void)
-{
-	pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_sonicvibes_init)
-module_exit(alsa_card_sonicvibes_exit)
+module_pci_driver(sonicvibes_driver);
diff --git a/sound/pci/trident/trident.c b/sound/pci/trident/trident.c
index 5f1def7..611983e 100644
--- a/sound/pci/trident/trident.c
+++ b/sound/pci/trident/trident.c
@@ -172,7 +172,7 @@
 	pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver trident_driver = {
 	.name = KBUILD_MODNAME,
 	.id_table = snd_trident_ids,
 	.probe = snd_trident_probe,
@@ -183,15 +183,4 @@
 #endif
 };
 
-static int __init alsa_card_trident_init(void)
-{
-	return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_trident_exit(void)
-{
-	pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_trident_init)
-module_exit(alsa_card_trident_exit)
+module_pci_driver(trident_driver);
diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c
index 7563040..b5afab4 100644
--- a/sound/pci/via82xx.c
+++ b/sound/pci/via82xx.c
@@ -2619,7 +2619,7 @@
 	pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver via82xx_driver = {
 	.name = KBUILD_MODNAME,
 	.id_table = snd_via82xx_ids,
 	.probe = snd_via82xx_probe,
@@ -2630,15 +2630,4 @@
 #endif
 };
 
-static int __init alsa_card_via82xx_init(void)
-{
-	return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_via82xx_exit(void)
-{
-	pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_via82xx_init)
-module_exit(alsa_card_via82xx_exit)
+module_pci_driver(via82xx_driver);
diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c
index 5efcbca..59fd47e 100644
--- a/sound/pci/via82xx_modem.c
+++ b/sound/pci/via82xx_modem.c
@@ -1223,7 +1223,7 @@
 	pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver via82xx_modem_driver = {
 	.name = KBUILD_MODNAME,
 	.id_table = snd_via82xx_modem_ids,
 	.probe = snd_via82xx_probe,
@@ -1234,15 +1234,4 @@
 #endif
 };
 
-static int __init alsa_card_via82xx_init(void)
-{
-	return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_via82xx_exit(void)
-{
-	pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_via82xx_init)
-module_exit(alsa_card_via82xx_exit)
+module_pci_driver(via82xx_modem_driver);
diff --git a/sound/pci/vx222/vx222.c b/sound/pci/vx222/vx222.c
index 6a534bf..1ea1f65 100644
--- a/sound/pci/vx222/vx222.c
+++ b/sound/pci/vx222/vx222.c
@@ -289,7 +289,7 @@
 }
 #endif
 
-static struct pci_driver driver = {
+static struct pci_driver vx222_driver = {
 	.name = KBUILD_MODNAME,
 	.id_table = snd_vx222_ids,
 	.probe = snd_vx222_probe,
@@ -300,15 +300,4 @@
 #endif
 };
 
-static int __init alsa_card_vx222_init(void)
-{
-	return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_vx222_exit(void)
-{
-	pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_vx222_init)
-module_exit(alsa_card_vx222_exit)
+module_pci_driver(vx222_driver);
diff --git a/sound/pci/ymfpci/ymfpci.c b/sound/pci/ymfpci/ymfpci.c
index 94ab728..9a1d01d 100644
--- a/sound/pci/ymfpci/ymfpci.c
+++ b/sound/pci/ymfpci/ymfpci.c
@@ -350,7 +350,7 @@
 	pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver ymfpci_driver = {
 	.name = KBUILD_MODNAME,
 	.id_table = snd_ymfpci_ids,
 	.probe = snd_card_ymfpci_probe,
@@ -361,15 +361,4 @@
 #endif
 };
 
-static int __init alsa_card_ymfpci_init(void)
-{
-	return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_ymfpci_exit(void)
-{
-	pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_ymfpci_init)
-module_exit(alsa_card_ymfpci_exit)
+module_pci_driver(ymfpci_driver);
diff --git a/sound/sh/sh_dac_audio.c b/sound/sh/sh_dac_audio.c
index b11f82b..f8b01c7 100644
--- a/sound/sh/sh_dac_audio.c
+++ b/sound/sh/sh_dac_audio.c
@@ -433,7 +433,7 @@
 /*
  * "driver" definition
  */
-static struct platform_driver driver = {
+static struct platform_driver sh_dac_driver = {
 	.probe	= snd_sh_dac_probe,
 	.remove = snd_sh_dac_remove,
 	.driver = {
@@ -441,4 +441,4 @@
 	},
 };
 
-module_platform_driver(driver);
+module_platform_driver(sh_dac_driver);
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 91c9855..40b2ad1 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -35,7 +35,6 @@
 source "sound/soc/davinci/Kconfig"
 source "sound/soc/ep93xx/Kconfig"
 source "sound/soc/fsl/Kconfig"
-source "sound/soc/imx/Kconfig"
 source "sound/soc/jz4740/Kconfig"
 source "sound/soc/nuc900/Kconfig"
 source "sound/soc/omap/Kconfig"
@@ -48,9 +47,13 @@
 source "sound/soc/sh/Kconfig"
 source "sound/soc/tegra/Kconfig"
 source "sound/soc/txx9/Kconfig"
+source "sound/soc/ux500/Kconfig"
 
 # Supported codecs
 source "sound/soc/codecs/Kconfig"
 
+# generic frame-work
+source "sound/soc/generic/Kconfig"
+
 endif	# SND_SOC
 
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 2feaf37..70990f4 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -6,13 +6,13 @@
 
 obj-$(CONFIG_SND_SOC)	+= snd-soc-core.o
 obj-$(CONFIG_SND_SOC)	+= codecs/
+obj-$(CONFIG_SND_SOC)	+= generic/
 obj-$(CONFIG_SND_SOC)	+= atmel/
 obj-$(CONFIG_SND_SOC)	+= au1x/
 obj-$(CONFIG_SND_SOC)	+= blackfin/
 obj-$(CONFIG_SND_SOC)	+= davinci/
 obj-$(CONFIG_SND_SOC)	+= ep93xx/
 obj-$(CONFIG_SND_SOC)	+= fsl/
-obj-$(CONFIG_SND_SOC)   += imx/
 obj-$(CONFIG_SND_SOC)	+= jz4740/
 obj-$(CONFIG_SND_SOC)	+= mid-x86/
 obj-$(CONFIG_SND_SOC)	+= mxs/
@@ -25,3 +25,4 @@
 obj-$(CONFIG_SND_SOC)	+= sh/
 obj-$(CONFIG_SND_SOC)	+= tegra/
 obj-$(CONFIG_SND_SOC)	+= txx9/
+obj-$(CONFIG_SND_SOC)	+= ux500/
diff --git a/sound/soc/blackfin/bf5xx-ssm2602.c b/sound/soc/blackfin/bf5xx-ssm2602.c
index b39ad35..7dbeef1 100644
--- a/sound/soc/blackfin/bf5xx-ssm2602.c
+++ b/sound/soc/blackfin/bf5xx-ssm2602.c
@@ -44,16 +44,8 @@
 
 static struct snd_soc_card bf5xx_ssm2602;
 
-static int bf5xx_ssm2602_hw_params(struct snd_pcm_substream *substream,
-	struct snd_pcm_hw_params *params)
+static int bf5xx_ssm2602_dai_init(struct snd_soc_pcm_runtime *rtd)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_dai *codec_dai = rtd->codec_dai;
-	unsigned int clk = 0;
-	int ret = 0;
-
-	pr_debug("%s rate %d format %x\n", __func__, params_rate(params),
-		params_format(params));
 	/*
 	 * If you are using a crystal source which frequency is not 12MHz
 	 * then modify the below case statement with frequency of the crystal.
@@ -61,31 +53,10 @@
 	 * If you are using the SPORT to generate clocking then this is
 	 * where to do it.
 	 */
-
-	switch (params_rate(params)) {
-	case 8000:
-	case 16000:
-	case 48000:
-	case 96000:
-	case 11025:
-	case 22050:
-	case 44100:
-		clk = 12000000;
-		break;
-	}
-
-	ret = snd_soc_dai_set_sysclk(codec_dai, SSM2602_SYSCLK, clk,
+	return snd_soc_dai_set_sysclk(rtd->codec_dai, SSM2602_SYSCLK, 12000000,
 		SND_SOC_CLOCK_IN);
-	if (ret < 0)
-		return ret;
-
-	return 0;
 }
 
-static struct snd_soc_ops bf5xx_ssm2602_ops = {
-	.hw_params = bf5xx_ssm2602_hw_params,
-};
-
 /* CODEC is master for BCLK and LRC in this configuration. */
 #define BF5XX_SSM2602_DAIFMT (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | \
 				SND_SOC_DAIFMT_CBM_CFM)
@@ -98,7 +69,7 @@
 		.codec_dai_name = "ssm2602-hifi",
 		.platform_name = "bfin-i2s-pcm-audio",
 		.codec_name = "ssm2602.0-001b",
-		.ops = &bf5xx_ssm2602_ops,
+		.init = bf5xx_ssm2602_dai_init,
 		.dai_fmt = BF5XX_SSM2602_DAIFMT,
 	},
 	{
@@ -108,7 +79,7 @@
 		.codec_dai_name = "ssm2602-hifi",
 		.platform_name = "bfin-i2s-pcm-audio",
 		.codec_name = "ssm2602.0-001b",
-		.ops = &bf5xx_ssm2602_ops,
+		.init = bf5xx_ssm2602_dai_init,
 		.dai_fmt = BF5XX_SSM2602_DAIFMT,
 	},
 };
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 59d8efa..1e1613a 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -29,6 +29,7 @@
 	select SND_SOC_ALC5632 if I2C
 	select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC
 	select SND_SOC_CS42L51 if I2C
+	select SND_SOC_CS42L52 if I2C
 	select SND_SOC_CS42L73 if I2C
 	select SND_SOC_CS4270 if I2C
 	select SND_SOC_CS4271 if SND_SOC_I2C_AND_SPI
@@ -37,11 +38,15 @@
 	select SND_SOC_DFBMCS320
 	select SND_SOC_JZ4740_CODEC
 	select SND_SOC_LM4857 if I2C
+	select SND_SOC_LM49453 if I2C
 	select SND_SOC_MAX98088 if I2C
 	select SND_SOC_MAX98095 if I2C
 	select SND_SOC_MAX9850 if I2C
 	select SND_SOC_MAX9768 if I2C
 	select SND_SOC_MAX9877 if I2C
+	select SND_SOC_MC13783 if MFD_MC13XXX
+	select SND_SOC_ML26124 if I2C
+	select SND_SOC_OMAP_HDMI_CODEC if OMAP4_DSS_HDMI
 	select SND_SOC_PCM3008
 	select SND_SOC_RT5631 if I2C
 	select SND_SOC_SGTL5000 if I2C
@@ -181,6 +186,9 @@
 config SND_SOC_CS42L51
 	tristate
 
+config SND_SOC_CS42L52
+	tristate
+
 config SND_SOC_CS42L73
 	tristate
 
@@ -217,6 +225,9 @@
 config SND_SOC_DMIC
 	tristate
 
+config SND_SOC_LM49453
+	tristate
+
 config SND_SOC_MAX98088
        tristate
 
@@ -226,6 +237,9 @@
 config SND_SOC_MAX9850
 	tristate
 
+config SND_SOC_OMAP_HDMI_CODEC
+       tristate
+
 config SND_SOC_PCM3008
        tristate
 
@@ -435,5 +449,11 @@
 config SND_SOC_MAX9877
 	tristate
 
+config SND_SOC_MC13783
+	tristate
+
+config SND_SOC_ML26124
+	tristate
+
 config SND_SOC_TPA6130A2
 	tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 6662eb0..fc27fec 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -15,6 +15,7 @@
 snd-soc-ak4671-objs := ak4671.o
 snd-soc-cq93vc-objs := cq93vc.o
 snd-soc-cs42l51-objs := cs42l51.o
+snd-soc-cs42l52-objs := cs42l52.o
 snd-soc-cs42l73-objs := cs42l73.o
 snd-soc-cs4270-objs := cs4270.o
 snd-soc-cs4271-objs := cs4271.o
@@ -25,10 +26,14 @@
 snd-soc-jz4740-codec-objs := jz4740.o
 snd-soc-l3-objs := l3.o
 snd-soc-lm4857-objs := lm4857.o
+snd-soc-lm49453-objs := lm49453.o
 snd-soc-max9768-objs := max9768.o
 snd-soc-max98088-objs := max98088.o
 snd-soc-max98095-objs := max98095.o
 snd-soc-max9850-objs := max9850.o
+snd-soc-mc13783-objs := mc13783.o
+snd-soc-ml26124-objs := ml26124.o
+snd-soc-omap-hdmi-codec-objs := omap-hdmi.o
 snd-soc-pcm3008-objs := pcm3008.o
 snd-soc-rt5631-objs := rt5631.o
 snd-soc-sgtl5000-objs := sgtl5000.o
@@ -121,6 +126,7 @@
 obj-$(CONFIG_SND_SOC_ALC5632)	+= snd-soc-alc5632.o
 obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o
 obj-$(CONFIG_SND_SOC_CS42L51)	+= snd-soc-cs42l51.o
+obj-$(CONFIG_SND_SOC_CS42L52)	+= snd-soc-cs42l52.o
 obj-$(CONFIG_SND_SOC_CS42L73)	+= snd-soc-cs42l73.o
 obj-$(CONFIG_SND_SOC_CS4270)	+= snd-soc-cs4270.o
 obj-$(CONFIG_SND_SOC_CS4271)	+= snd-soc-cs4271.o
@@ -128,13 +134,17 @@
 obj-$(CONFIG_SND_SOC_DA7210)	+= snd-soc-da7210.o
 obj-$(CONFIG_SND_SOC_DFBMCS320)	+= snd-soc-dfbmcs320.o
 obj-$(CONFIG_SND_SOC_DMIC)	+= snd-soc-dmic.o
+obj-$(CONFIG_SND_SOC_JZ4740_CODEC)	+= snd-soc-jz4740-codec.o
 obj-$(CONFIG_SND_SOC_L3)	+= snd-soc-l3.o
 obj-$(CONFIG_SND_SOC_LM4857)	+= snd-soc-lm4857.o
-obj-$(CONFIG_SND_SOC_JZ4740_CODEC)	+= snd-soc-jz4740-codec.o
+obj-$(CONFIG_SND_SOC_LM49453)   += snd-soc-lm49453.o
 obj-$(CONFIG_SND_SOC_MAX9768)	+= snd-soc-max9768.o
 obj-$(CONFIG_SND_SOC_MAX98088)	+= snd-soc-max98088.o
 obj-$(CONFIG_SND_SOC_MAX98095)	+= snd-soc-max98095.o
 obj-$(CONFIG_SND_SOC_MAX9850)	+= snd-soc-max9850.o
+obj-$(CONFIG_SND_SOC_MC13783)	+= snd-soc-mc13783.o
+obj-$(CONFIG_SND_SOC_ML26124)	+= snd-soc-ml26124.o
+obj-$(CONFIG_SND_SOC_OMAP_HDMI_CODEC) += snd-soc-omap-hdmi-codec.o
 obj-$(CONFIG_SND_SOC_PCM3008)	+= snd-soc-pcm3008.o
 obj-$(CONFIG_SND_SOC_RT5631)	+= snd-soc-rt5631.o
 obj-$(CONFIG_SND_SOC_SGTL5000)  += snd-soc-sgtl5000.o
diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c
index 1bbad4c..2023c74 100644
--- a/sound/soc/codecs/ac97.c
+++ b/sound/soc/codecs/ac97.c
@@ -26,13 +26,11 @@
 static int ac97_prepare(struct snd_pcm_substream *substream,
 			struct snd_soc_dai *dai)
 {
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 
 	int reg = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
 		  AC97_PCM_FRONT_DAC_RATE : AC97_PCM_LR_ADC_RATE;
-	return snd_ac97_set_rate(codec->ac97, reg, runtime->rate);
+	return snd_ac97_set_rate(codec->ac97, reg, substream->runtime->rate);
 }
 
 #define STD_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
diff --git a/sound/soc/codecs/ad1836.c b/sound/soc/codecs/ad1836.c
index 12e3b41..c67b50d 100644
--- a/sound/soc/codecs/ad1836.c
+++ b/sound/soc/codecs/ad1836.c
@@ -162,9 +162,7 @@
 		struct snd_soc_dai *dai)
 {
 	int word_len = 0;
-
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 
 	/* bit size */
 	switch (params_format(params)) {
diff --git a/sound/soc/codecs/ad193x.c b/sound/soc/codecs/ad193x.c
index a4a6bef..13e62be 100644
--- a/sound/soc/codecs/ad193x.c
+++ b/sound/soc/codecs/ad193x.c
@@ -245,9 +245,7 @@
 		struct snd_soc_dai *dai)
 {
 	int word_len = 0, master_rate = 0;
-
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec);
 
 	/* bit size */
diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c
index 78e9ce4..3d50fc8 100644
--- a/sound/soc/codecs/adau1701.c
+++ b/sound/soc/codecs/adau1701.c
@@ -258,8 +258,7 @@
 static int adau1701_hw_params(struct snd_pcm_substream *substream,
 		struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	snd_pcm_format_t format;
 	unsigned int val;
 
diff --git a/sound/soc/codecs/ak4104.c b/sound/soc/codecs/ak4104.c
index ceb96ecf..31d4483 100644
--- a/sound/soc/codecs/ak4104.c
+++ b/sound/soc/codecs/ak4104.c
@@ -88,8 +88,7 @@
 			    struct snd_pcm_hw_params *params,
 			    struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	int val = 0;
 
 	/* set the IEC958 bits: consumer mode, no copyright bit */
diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c
index 838ae8b..618fdc3 100644
--- a/sound/soc/codecs/ak4535.c
+++ b/sound/soc/codecs/ak4535.c
@@ -262,8 +262,7 @@
 			    struct snd_pcm_hw_params *params,
 			    struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	struct ak4535_priv *ak4535 = snd_soc_codec_get_drvdata(codec);
 	u8 mode2 = snd_soc_read(codec, AK4535_MODE2) & ~(0x3 << 5);
 	int rate = params_rate(params), fs = 256;
diff --git a/sound/soc/codecs/ak4641.c b/sound/soc/codecs/ak4641.c
index c4d165a..543a12f 100644
--- a/sound/soc/codecs/ak4641.c
+++ b/sound/soc/codecs/ak4641.c
@@ -296,8 +296,7 @@
 				 struct snd_pcm_hw_params *params,
 				 struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
 	int rate = params_rate(params), fs = 256;
 	u8 mode2;
@@ -517,67 +516,24 @@
 
 static int ak4641_probe(struct snd_soc_codec *codec)
 {
-	struct ak4641_platform_data *pdata = codec->dev->platform_data;
 	int ret;
 
-
-	if (pdata) {
-		if (gpio_is_valid(pdata->gpio_power)) {
-			ret = gpio_request_one(pdata->gpio_power,
-					GPIOF_OUT_INIT_LOW, "ak4641 power");
-			if (ret)
-				goto err_out;
-		}
-		if (gpio_is_valid(pdata->gpio_npdn)) {
-			ret = gpio_request_one(pdata->gpio_npdn,
-					GPIOF_OUT_INIT_LOW, "ak4641 npdn");
-			if (ret)
-				goto err_gpio;
-
-			udelay(1); /* > 150 ns */
-			gpio_set_value(pdata->gpio_npdn, 1);
-		}
-	}
-
 	ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C);
 	if (ret != 0) {
 		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
-		goto err_register;
+		return ret;
 	}
 
 	/* power on device */
 	ak4641_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
 	return 0;
-
-err_register:
-	if (pdata) {
-		if (gpio_is_valid(pdata->gpio_power))
-			gpio_set_value(pdata->gpio_power, 0);
-		if (gpio_is_valid(pdata->gpio_npdn))
-			gpio_free(pdata->gpio_npdn);
-	}
-err_gpio:
-	if (pdata && gpio_is_valid(pdata->gpio_power))
-		gpio_free(pdata->gpio_power);
-err_out:
-	return ret;
 }
 
 static int ak4641_remove(struct snd_soc_codec *codec)
 {
-	struct ak4641_platform_data *pdata = codec->dev->platform_data;
-
 	ak4641_set_bias_level(codec, SND_SOC_BIAS_OFF);
 
-	if (pdata) {
-		if (gpio_is_valid(pdata->gpio_power)) {
-			gpio_set_value(pdata->gpio_power, 0);
-			gpio_free(pdata->gpio_power);
-		}
-		if (gpio_is_valid(pdata->gpio_npdn))
-			gpio_free(pdata->gpio_npdn);
-	}
 	return 0;
 }
 
@@ -604,6 +560,7 @@
 static int __devinit ak4641_i2c_probe(struct i2c_client *i2c,
 				      const struct i2c_device_id *id)
 {
+	struct ak4641_platform_data *pdata = i2c->dev.platform_data;
 	struct ak4641_priv *ak4641;
 	int ret;
 
@@ -612,16 +569,62 @@
 	if (!ak4641)
 		return -ENOMEM;
 
+	if (pdata) {
+		if (gpio_is_valid(pdata->gpio_power)) {
+			ret = gpio_request_one(pdata->gpio_power,
+					GPIOF_OUT_INIT_LOW, "ak4641 power");
+			if (ret)
+				goto err_out;
+		}
+		if (gpio_is_valid(pdata->gpio_npdn)) {
+			ret = gpio_request_one(pdata->gpio_npdn,
+					GPIOF_OUT_INIT_LOW, "ak4641 npdn");
+			if (ret)
+				goto err_gpio;
+
+			udelay(1); /* > 150 ns */
+			gpio_set_value(pdata->gpio_npdn, 1);
+		}
+	}
+
 	i2c_set_clientdata(i2c, ak4641);
 
 	ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_ak4641,
 				ak4641_dai, ARRAY_SIZE(ak4641_dai));
+	if (ret != 0)
+		goto err_gpio2;
+
+	return 0;
+
+err_gpio2:
+	if (pdata) {
+		if (gpio_is_valid(pdata->gpio_power))
+			gpio_set_value(pdata->gpio_power, 0);
+		if (gpio_is_valid(pdata->gpio_npdn))
+			gpio_free(pdata->gpio_npdn);
+	}
+err_gpio:
+	if (pdata && gpio_is_valid(pdata->gpio_power))
+		gpio_free(pdata->gpio_power);
+err_out:
 	return ret;
 }
 
 static int __devexit ak4641_i2c_remove(struct i2c_client *i2c)
 {
+	struct ak4641_platform_data *pdata = i2c->dev.platform_data;
+
 	snd_soc_unregister_codec(&i2c->dev);
+
+	if (pdata) {
+		if (gpio_is_valid(pdata->gpio_power)) {
+			gpio_set_value(pdata->gpio_power, 0);
+			gpio_free(pdata->gpio_power);
+		}
+		if (gpio_is_valid(pdata->gpio_npdn))
+			gpio_free(pdata->gpio_npdn);
+	}
+
 	return 0;
 }
 
@@ -641,23 +644,7 @@
 	.id_table = ak4641_i2c_id,
 };
 
-static int __init ak4641_modinit(void)
-{
-	int ret;
-
-	ret = i2c_add_driver(&ak4641_i2c_driver);
-	if (ret != 0)
-		pr_err("Failed to register AK4641 I2C driver: %d\n", ret);
-
-	return ret;
-}
-module_init(ak4641_modinit);
-
-static void __exit ak4641_exit(void)
-{
-	i2c_del_driver(&ak4641_i2c_driver);
-}
-module_exit(ak4641_exit);
+module_i2c_driver(ak4641_i2c_driver);
 
 MODULE_DESCRIPTION("SoC AK4641 driver");
 MODULE_AUTHOR("Harald Welte <laforge@gnufiish.org>");
diff --git a/sound/soc/codecs/alc5623.c b/sound/soc/codecs/alc5623.c
index d47b62d..1960478 100644
--- a/sound/soc/codecs/alc5623.c
+++ b/sound/soc/codecs/alc5623.c
@@ -705,8 +705,7 @@
 static int alc5623_pcm_hw_params(struct snd_pcm_substream *substream,
 		struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec);
 	int coeff, rate;
 	u16 iface;
@@ -1084,25 +1083,7 @@
 	.id_table = alc5623_i2c_table,
 };
 
-static int __init alc5623_modinit(void)
-{
-	int ret;
-
-	ret = i2c_add_driver(&alc5623_i2c_driver);
-	if (ret != 0) {
-		printk(KERN_ERR "%s: can't add i2c driver", __func__);
-		return ret;
-	}
-
-	return ret;
-}
-module_init(alc5623_modinit);
-
-static void __exit alc5623_modexit(void)
-{
-	i2c_del_driver(&alc5623_i2c_driver);
-}
-module_exit(alc5623_modexit);
+module_i2c_driver(alc5623_i2c_driver);
 
 MODULE_DESCRIPTION("ASoC alc5621/2/3 driver");
 MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>");
diff --git a/sound/soc/codecs/alc5632.c b/sound/soc/codecs/alc5632.c
index e2111e0..7dd0242 100644
--- a/sound/soc/codecs/alc5632.c
+++ b/sound/soc/codecs/alc5632.c
@@ -861,8 +861,7 @@
 static int alc5632_pcm_hw_params(struct snd_pcm_substream *substream,
 		struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	int coeff, rate;
 	u16 iface;
 
@@ -1131,7 +1130,7 @@
 
 	i2c_set_clientdata(client, alc5632);
 
-	alc5632->regmap = regmap_init_i2c(client, &alc5632_regmap);
+	alc5632->regmap = devm_regmap_init_i2c(client, &alc5632_regmap);
 	if (IS_ERR(alc5632->regmap)) {
 		ret = PTR_ERR(alc5632->regmap);
 		dev_err(&client->dev, "regmap_init() failed: %d\n", ret);
@@ -1143,7 +1142,6 @@
 	if (ret1 != 0 || ret2 != 0) {
 		dev_err(&client->dev,
 		"Failed to read chip ID: ret1=%d, ret2=%d\n", ret1, ret2);
-		regmap_exit(alc5632->regmap);
 		return -EIO;
 	}
 
@@ -1152,14 +1150,12 @@
 	if ((vid1 != 0x10EC) || (vid2 != id->driver_data)) {
 		dev_err(&client->dev,
 		"Device is not a ALC5632: VID1=0x%x, VID2=0x%x\n", vid1, vid2);
-		regmap_exit(alc5632->regmap);
 		return -EINVAL;
 	}
 
 	ret = alc5632_reset(alc5632->regmap);
 	if (ret < 0) {
 		dev_err(&client->dev, "Failed to issue reset\n");
-		regmap_exit(alc5632->regmap);
 		return ret;
 	}
 
@@ -1177,7 +1173,6 @@
 
 	if (ret < 0) {
 		dev_err(&client->dev, "Failed to register codec: %d\n", ret);
-		regmap_exit(alc5632->regmap);
 		return ret;
 	}
 
@@ -1186,9 +1181,7 @@
 
 static __devexit int alc5632_i2c_remove(struct i2c_client *client)
 {
-	struct alc5632_priv *alc5632 = i2c_get_clientdata(client);
 	snd_soc_unregister_codec(&client->dev);
-	regmap_exit(alc5632->regmap);
 	return 0;
 }
 
@@ -1209,25 +1202,7 @@
 	.id_table = alc5632_i2c_table,
 };
 
-static int __init alc5632_modinit(void)
-{
-	int ret;
-
-	ret = i2c_add_driver(&alc5632_i2c_driver);
-	if (ret != 0) {
-		printk(KERN_ERR "%s: can't add i2c driver", __func__);
-		return ret;
-	}
-
-	return ret;
-}
-module_init(alc5632_modinit);
-
-static void __exit alc5632_modexit(void)
-{
-	i2c_del_driver(&alc5632_i2c_driver);
-}
-module_exit(alc5632_modexit);
+module_i2c_driver(alc5632_i2c_driver);
 
 MODULE_DESCRIPTION("ASoC ALC5632 driver");
 MODULE_AUTHOR("Leon Romanovsky <leon@leon.nu>");
diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c
index 1d672f5..047917f 100644
--- a/sound/soc/codecs/cs4270.c
+++ b/sound/soc/codecs/cs4270.c
@@ -307,8 +307,7 @@
 			    struct snd_pcm_hw_params *params,
 			    struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
 	int ret;
 	unsigned int i;
@@ -600,10 +599,12 @@
 static int cs4270_soc_resume(struct snd_soc_codec *codec)
 {
 	struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
-	int reg;
+	int reg, ret;
 
-	regulator_bulk_enable(ARRAY_SIZE(cs4270->supplies),
-			      cs4270->supplies);
+	ret = regulator_bulk_enable(ARRAY_SIZE(cs4270->supplies),
+				    cs4270->supplies);
+	if (ret != 0)
+		return ret;
 
 	/* In case the device was put to hard reset during sleep, we need to
 	 * wait 500ns here before any I2C communication. */
diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c
index bf71412..9eb01d7 100644
--- a/sound/soc/codecs/cs4271.c
+++ b/sound/soc/codecs/cs4271.c
@@ -318,8 +318,7 @@
 			    struct snd_pcm_hw_params *params,
 			    struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
 	int i, ret;
 	unsigned int ratio, val;
diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c
index a8bf588..091d019 100644
--- a/sound/soc/codecs/cs42l51.c
+++ b/sound/soc/codecs/cs42l51.c
@@ -141,15 +141,15 @@
 static const struct snd_kcontrol_new cs42l51_snd_controls[] = {
 	SOC_DOUBLE_R_SX_TLV("PCM Playback Volume",
 			CS42L51_PCMA_VOL, CS42L51_PCMB_VOL,
-			7, 0xffffff99, 0x18, adc_pcm_tlv),
+			6, 0x19, 0x7F, adc_pcm_tlv),
 	SOC_DOUBLE_R("PCM Playback Switch",
 			CS42L51_PCMA_VOL, CS42L51_PCMB_VOL, 7, 1, 1),
 	SOC_DOUBLE_R_SX_TLV("Analog Playback Volume",
 			CS42L51_AOUTA_VOL, CS42L51_AOUTB_VOL,
-			8, 0xffffff19, 0x18, aout_tlv),
+			0, 0x34, 0xE4, aout_tlv),
 	SOC_DOUBLE_R_SX_TLV("ADC Mixer Volume",
 			CS42L51_ADCA_VOL, CS42L51_ADCB_VOL,
-			7, 0xffffff99, 0x18, adc_pcm_tlv),
+			6, 0x19, 0x7F, adc_pcm_tlv),
 	SOC_DOUBLE_R("ADC Mixer Switch",
 			CS42L51_ADCA_VOL, CS42L51_ADCB_VOL, 7, 1, 1),
 	SOC_SINGLE("Playback Deemphasis Switch", CS42L51_DAC_CTL, 3, 1, 0),
@@ -356,8 +356,7 @@
 		struct snd_pcm_hw_params *params,
 		struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	struct cs42l51_private *cs42l51 = snd_soc_codec_get_drvdata(codec);
 	int ret;
 	unsigned int i;
diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c
new file mode 100644
index 0000000..a710941
--- /dev/null
+++ b/sound/soc/codecs/cs42l52.c
@@ -0,0 +1,1295 @@
+/*
+ * cs42l52.c -- CS42L52 ALSA SoC audio driver
+ *
+ * Copyright 2012 CirrusLogic, Inc.
+ *
+ * Author: Georgi Vlaev <joe@nucleusys.com>
+ * Author: Brian Austin <brian.austin@cirrus.com>
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/cs42l52.h>
+#include "cs42l52.h"
+
+struct sp_config {
+	u8 spc, format, spfs;
+	u32 srate;
+};
+
+struct  cs42l52_private {
+	struct regmap *regmap;
+	struct snd_soc_codec *codec;
+	struct device *dev;
+	struct sp_config config;
+	struct cs42l52_platform_data pdata;
+	u32 sysclk;
+	u8 mclksel;
+	u32 mclk;
+	u8 flags;
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+	struct input_dev *beep;
+	struct work_struct beep_work;
+	int beep_rate;
+#endif
+};
+
+static const struct reg_default cs42l52_reg_defaults[] = {
+	{ CS42L52_PWRCTL1, 0x9F },	/* r02 PWRCTL 1 */
+	{ CS42L52_PWRCTL2, 0x07 },	/* r03 PWRCTL 2 */
+	{ CS42L52_PWRCTL3, 0xFF },	/* r04 PWRCTL 3 */
+	{ CS42L52_CLK_CTL, 0xA0 },	/* r05 Clocking Ctl */
+	{ CS42L52_IFACE_CTL1, 0x00 },	/* r06 Interface Ctl 1 */
+	{ CS42L52_ADC_PGA_A, 0x80 },	/* r08 Input A Select */
+	{ CS42L52_ADC_PGA_B, 0x80 },	/* r09 Input B Select */
+	{ CS42L52_ANALOG_HPF_CTL, 0xA5 },	/* r0A Analog HPF Ctl */
+	{ CS42L52_ADC_HPF_FREQ, 0x00 },	/* r0B ADC HPF Corner Freq */
+	{ CS42L52_ADC_MISC_CTL, 0x00 },	/* r0C Misc. ADC Ctl */
+	{ CS42L52_PB_CTL1, 0x60 },	/* r0D Playback Ctl 1 */
+	{ CS42L52_MISC_CTL, 0x02 },	/* r0E Misc. Ctl */
+	{ CS42L52_PB_CTL2, 0x00 },	/* r0F Playback Ctl 2 */
+	{ CS42L52_MICA_CTL, 0x00 },	/* r10 MICA Amp Ctl */
+	{ CS42L52_MICB_CTL, 0x00 },	/* r11 MICB Amp Ctl */
+	{ CS42L52_PGAA_CTL, 0x00 },	/* r12 PGAA Vol, Misc. */
+	{ CS42L52_PGAB_CTL, 0x00 },	/* r13 PGAB Vol, Misc. */
+	{ CS42L52_PASSTHRUA_VOL, 0x00 },	/* r14 Bypass A Vol */
+	{ CS42L52_PASSTHRUB_VOL, 0x00 },	/* r15 Bypass B Vol */
+	{ CS42L52_ADCA_VOL, 0x00 },	/* r16 ADCA Volume */
+	{ CS42L52_ADCB_VOL, 0x00 },	/* r17 ADCB Volume */
+	{ CS42L52_ADCA_MIXER_VOL, 0x80 },	/* r18 ADCA Mixer Volume */
+	{ CS42L52_ADCB_MIXER_VOL, 0x80 },	/* r19 ADCB Mixer Volume */
+	{ CS42L52_PCMA_MIXER_VOL, 0x00 },	/* r1A PCMA Mixer Volume */
+	{ CS42L52_PCMB_MIXER_VOL, 0x00 },	/* r1B PCMB Mixer Volume */
+	{ CS42L52_BEEP_FREQ, 0x00 },	/* r1C Beep Freq on Time */
+	{ CS42L52_BEEP_VOL, 0x00 },	/* r1D Beep Volume off Time */
+	{ CS42L52_BEEP_TONE_CTL, 0x00 },	/* r1E Beep Tone Cfg. */
+	{ CS42L52_TONE_CTL, 0x00 },	/* r1F Tone Ctl */
+	{ CS42L52_MASTERA_VOL, 0x88 },	/* r20 Master A Volume */
+	{ CS42L52_MASTERB_VOL, 0x00 },	/* r21 Master B Volume */
+	{ CS42L52_HPA_VOL, 0x00 },	/* r22 Headphone A Volume */
+	{ CS42L52_HPB_VOL, 0x00 },	/* r23 Headphone B Volume */
+	{ CS42L52_SPKA_VOL, 0x00 },	/* r24 Speaker A Volume */
+	{ CS42L52_SPKB_VOL, 0x00 },	/* r25 Speaker B Volume */
+	{ CS42L52_ADC_PCM_MIXER, 0x00 },	/* r26 Channel Mixer and Swap */
+	{ CS42L52_LIMITER_CTL1, 0x00 },	/* r27 Limit Ctl 1 Thresholds */
+	{ CS42L52_LIMITER_CTL2, 0x7F },	/* r28 Limit Ctl 2 Release Rate */
+	{ CS42L52_LIMITER_AT_RATE, 0xC0 },	/* r29 Limiter Attack Rate */
+	{ CS42L52_ALC_CTL, 0x00 },	/* r2A ALC Ctl 1 Attack Rate */
+	{ CS42L52_ALC_RATE, 0x3F },	/* r2B ALC Release Rate */
+	{ CS42L52_ALC_THRESHOLD, 0x3f },	/* r2C ALC Thresholds */
+	{ CS42L52_NOISE_GATE_CTL, 0x00 },	/* r2D Noise Gate Ctl */
+	{ CS42L52_CLK_STATUS, 0x00 },	/* r2E Overflow and Clock Status */
+	{ CS42L52_BATT_COMPEN, 0x00 },	/* r2F battery Compensation */
+	{ CS42L52_BATT_LEVEL, 0x00 },	/* r30 VP Battery Level */
+	{ CS42L52_SPK_STATUS, 0x00 },	/* r31 Speaker Status */
+	{ CS42L52_TEM_CTL, 0x3B },	/* r32 Temp Ctl */
+	{ CS42L52_THE_FOLDBACK, 0x00 },	/* r33 Foldback */
+};
+
+static bool cs42l52_readable_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case CS42L52_CHIP:
+	case CS42L52_PWRCTL1:
+	case CS42L52_PWRCTL2:
+	case CS42L52_PWRCTL3:
+	case CS42L52_CLK_CTL:
+	case CS42L52_IFACE_CTL1:
+	case CS42L52_IFACE_CTL2:
+	case CS42L52_ADC_PGA_A:
+	case CS42L52_ADC_PGA_B:
+	case CS42L52_ANALOG_HPF_CTL:
+	case CS42L52_ADC_HPF_FREQ:
+	case CS42L52_ADC_MISC_CTL:
+	case CS42L52_PB_CTL1:
+	case CS42L52_MISC_CTL:
+	case CS42L52_PB_CTL2:
+	case CS42L52_MICA_CTL:
+	case CS42L52_MICB_CTL:
+	case CS42L52_PGAA_CTL:
+	case CS42L52_PGAB_CTL:
+	case CS42L52_PASSTHRUA_VOL:
+	case CS42L52_PASSTHRUB_VOL:
+	case CS42L52_ADCA_VOL:
+	case CS42L52_ADCB_VOL:
+	case CS42L52_ADCA_MIXER_VOL:
+	case CS42L52_ADCB_MIXER_VOL:
+	case CS42L52_PCMA_MIXER_VOL:
+	case CS42L52_PCMB_MIXER_VOL:
+	case CS42L52_BEEP_FREQ:
+	case CS42L52_BEEP_VOL:
+	case CS42L52_BEEP_TONE_CTL:
+	case CS42L52_TONE_CTL:
+	case CS42L52_MASTERA_VOL:
+	case CS42L52_MASTERB_VOL:
+	case CS42L52_HPA_VOL:
+	case CS42L52_HPB_VOL:
+	case CS42L52_SPKA_VOL:
+	case CS42L52_SPKB_VOL:
+	case CS42L52_ADC_PCM_MIXER:
+	case CS42L52_LIMITER_CTL1:
+	case CS42L52_LIMITER_CTL2:
+	case CS42L52_LIMITER_AT_RATE:
+	case CS42L52_ALC_CTL:
+	case CS42L52_ALC_RATE:
+	case CS42L52_ALC_THRESHOLD:
+	case CS42L52_NOISE_GATE_CTL:
+	case CS42L52_CLK_STATUS:
+	case CS42L52_BATT_COMPEN:
+	case CS42L52_BATT_LEVEL:
+	case CS42L52_SPK_STATUS:
+	case CS42L52_TEM_CTL:
+	case CS42L52_THE_FOLDBACK:
+	case CS42L52_CHARGE_PUMP:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool cs42l52_volatile_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case CS42L52_IFACE_CTL2:
+	case CS42L52_CLK_STATUS:
+	case CS42L52_BATT_LEVEL:
+	case CS42L52_SPK_STATUS:
+	case CS42L52_CHARGE_PUMP:
+		return 1;
+	default:
+		return 0;
+	}
+}
+
+static DECLARE_TLV_DB_SCALE(hl_tlv, -10200, 50, 0);
+
+static DECLARE_TLV_DB_SCALE(hpd_tlv, -9600, 50, 1);
+
+static DECLARE_TLV_DB_SCALE(ipd_tlv, -9600, 100, 0);
+
+static DECLARE_TLV_DB_SCALE(mic_tlv, 1600, 100, 0);
+
+static DECLARE_TLV_DB_SCALE(pga_tlv, -600, 50, 0);
+
+static const unsigned int limiter_tlv[] = {
+	TLV_DB_RANGE_HEAD(2),
+	0, 2, TLV_DB_SCALE_ITEM(-3000, 600, 0),
+	3, 7, TLV_DB_SCALE_ITEM(-1200, 300, 0),
+};
+
+static const char * const cs42l52_adca_text[] = {
+	"Input1A", "Input2A", "Input3A", "Input4A", "PGA Input Left"};
+
+static const char * const cs42l52_adcb_text[] = {
+	"Input1B", "Input2B", "Input3B", "Input4B", "PGA Input Right"};
+
+static const struct soc_enum adca_enum =
+	SOC_ENUM_SINGLE(CS42L52_ADC_PGA_A, 5,
+		ARRAY_SIZE(cs42l52_adca_text), cs42l52_adca_text);
+
+static const struct soc_enum adcb_enum =
+	SOC_ENUM_SINGLE(CS42L52_ADC_PGA_B, 5,
+		ARRAY_SIZE(cs42l52_adcb_text), cs42l52_adcb_text);
+
+static const struct snd_kcontrol_new adca_mux =
+	SOC_DAPM_ENUM("Left ADC Input Capture Mux", adca_enum);
+
+static const struct snd_kcontrol_new adcb_mux =
+	SOC_DAPM_ENUM("Right ADC Input Capture Mux", adcb_enum);
+
+static const char * const mic_bias_level_text[] = {
+	"0.5 +VA", "0.6 +VA", "0.7 +VA",
+	"0.8 +VA", "0.83 +VA", "0.91 +VA"
+};
+
+static const struct soc_enum mic_bias_level_enum =
+	SOC_ENUM_SINGLE(CS42L52_IFACE_CTL1, 0,
+			ARRAY_SIZE(mic_bias_level_text), mic_bias_level_text);
+
+static const char * const cs42l52_mic_text[] = { "Single", "Differential" };
+
+static const struct soc_enum mica_enum =
+	SOC_ENUM_SINGLE(CS42L52_MICA_CTL, 5,
+			ARRAY_SIZE(cs42l52_mic_text), cs42l52_mic_text);
+
+static const struct soc_enum micb_enum =
+	SOC_ENUM_SINGLE(CS42L52_MICB_CTL, 5,
+			ARRAY_SIZE(cs42l52_mic_text), cs42l52_mic_text);
+
+static const struct snd_kcontrol_new mica_mux =
+	SOC_DAPM_ENUM("Left Mic Input Capture Mux", mica_enum);
+
+static const struct snd_kcontrol_new micb_mux =
+	SOC_DAPM_ENUM("Right Mic Input Capture Mux", micb_enum);
+
+static const char * const digital_output_mux_text[] = {"ADC", "DSP"};
+
+static const struct soc_enum digital_output_mux_enum =
+	SOC_ENUM_SINGLE(CS42L52_ADC_MISC_CTL, 6,
+			ARRAY_SIZE(digital_output_mux_text),
+			digital_output_mux_text);
+
+static const struct snd_kcontrol_new digital_output_mux =
+	SOC_DAPM_ENUM("Digital Output Mux", digital_output_mux_enum);
+
+static const char * const hp_gain_num_text[] = {
+	"0.3959", "0.4571", "0.5111", "0.6047",
+	"0.7099", "0.8399", "1.000", "1.1430"
+};
+
+static const struct soc_enum hp_gain_enum =
+	SOC_ENUM_SINGLE(CS42L52_PB_CTL1, 4,
+		ARRAY_SIZE(hp_gain_num_text), hp_gain_num_text);
+
+static const char * const beep_pitch_text[] = {
+	"C4", "C5", "D5", "E5", "F5", "G5", "A5", "B5",
+	"C6", "D6", "E6", "F6", "G6", "A6", "B6", "C7"
+};
+
+static const struct soc_enum beep_pitch_enum =
+	SOC_ENUM_SINGLE(CS42L52_BEEP_FREQ, 4,
+			ARRAY_SIZE(beep_pitch_text), beep_pitch_text);
+
+static const char * const beep_ontime_text[] = {
+	"86 ms", "430 ms", "780 ms", "1.20 s", "1.50 s",
+	"1.80 s", "2.20 s", "2.50 s", "2.80 s", "3.20 s",
+	"3.50 s", "3.80 s", "4.20 s", "4.50 s", "4.80 s", "5.20 s"
+};
+
+static const struct soc_enum beep_ontime_enum =
+	SOC_ENUM_SINGLE(CS42L52_BEEP_FREQ, 0,
+			ARRAY_SIZE(beep_ontime_text), beep_ontime_text);
+
+static const char * const beep_offtime_text[] = {
+	"1.23 s", "2.58 s", "3.90 s", "5.20 s",
+	"6.60 s", "8.05 s", "9.35 s", "10.80 s"
+};
+
+static const struct soc_enum beep_offtime_enum =
+	SOC_ENUM_SINGLE(CS42L52_BEEP_VOL, 5,
+			ARRAY_SIZE(beep_offtime_text), beep_offtime_text);
+
+static const char * const beep_config_text[] = {
+	"Off", "Single", "Multiple", "Continuous"
+};
+
+static const struct soc_enum beep_config_enum =
+	SOC_ENUM_SINGLE(CS42L52_BEEP_TONE_CTL, 6,
+			ARRAY_SIZE(beep_config_text), beep_config_text);
+
+static const char * const beep_bass_text[] = {
+	"50 Hz", "100 Hz", "200 Hz", "250 Hz"
+};
+
+static const struct soc_enum beep_bass_enum =
+	SOC_ENUM_SINGLE(CS42L52_BEEP_TONE_CTL, 1,
+			ARRAY_SIZE(beep_bass_text), beep_bass_text);
+
+static const char * const beep_treble_text[] = {
+	"5 kHz", "7 kHz", "10 kHz", " 15 kHz"
+};
+
+static const struct soc_enum beep_treble_enum =
+	SOC_ENUM_SINGLE(CS42L52_BEEP_TONE_CTL, 3,
+			ARRAY_SIZE(beep_treble_text), beep_treble_text);
+
+static const char * const ng_threshold_text[] = {
+	"-34dB", "-37dB", "-40dB", "-43dB",
+	"-46dB", "-52dB", "-58dB", "-64dB"
+};
+
+static const struct soc_enum ng_threshold_enum =
+	SOC_ENUM_SINGLE(CS42L52_NOISE_GATE_CTL, 2,
+		ARRAY_SIZE(ng_threshold_text), ng_threshold_text);
+
+static const char * const cs42l52_ng_delay_text[] = {
+	"50ms", "100ms", "150ms", "200ms"};
+
+static const struct soc_enum ng_delay_enum =
+	SOC_ENUM_SINGLE(CS42L52_NOISE_GATE_CTL, 0,
+		ARRAY_SIZE(cs42l52_ng_delay_text), cs42l52_ng_delay_text);
+
+static const char * const cs42l52_ng_type_text[] = {
+	"Apply Specific", "Apply All"
+};
+
+static const struct soc_enum ng_type_enum =
+	SOC_ENUM_SINGLE(CS42L52_NOISE_GATE_CTL, 6,
+		ARRAY_SIZE(cs42l52_ng_type_text), cs42l52_ng_type_text);
+
+static const char * const left_swap_text[] = {
+	"Left", "LR 2", "Right"};
+
+static const char * const right_swap_text[] = {
+	"Right", "LR 2", "Left"};
+
+static const unsigned int swap_values[] = { 0, 1, 3 };
+
+static const struct soc_enum adca_swap_enum =
+	SOC_VALUE_ENUM_SINGLE(CS42L52_ADC_PCM_MIXER, 2, 1,
+			      ARRAY_SIZE(left_swap_text),
+			      left_swap_text,
+			      swap_values);
+
+static const struct snd_kcontrol_new adca_mixer =
+	SOC_DAPM_ENUM("Route", adca_swap_enum);
+
+static const struct soc_enum pcma_swap_enum =
+	SOC_VALUE_ENUM_SINGLE(CS42L52_ADC_PCM_MIXER, 6, 1,
+			      ARRAY_SIZE(left_swap_text),
+			      left_swap_text,
+			      swap_values);
+
+static const struct snd_kcontrol_new pcma_mixer =
+	SOC_DAPM_ENUM("Route", pcma_swap_enum);
+
+static const struct soc_enum adcb_swap_enum =
+	SOC_VALUE_ENUM_SINGLE(CS42L52_ADC_PCM_MIXER, 0, 1,
+			      ARRAY_SIZE(right_swap_text),
+			      right_swap_text,
+			      swap_values);
+
+static const struct snd_kcontrol_new adcb_mixer =
+	SOC_DAPM_ENUM("Route", adcb_swap_enum);
+
+static const struct soc_enum pcmb_swap_enum =
+	SOC_VALUE_ENUM_SINGLE(CS42L52_ADC_PCM_MIXER, 4, 1,
+			      ARRAY_SIZE(right_swap_text),
+			      right_swap_text,
+			      swap_values);
+
+static const struct snd_kcontrol_new pcmb_mixer =
+	SOC_DAPM_ENUM("Route", pcmb_swap_enum);
+
+
+static const struct snd_kcontrol_new passthrul_ctl =
+	SOC_DAPM_SINGLE("Switch", CS42L52_MISC_CTL, 6, 1, 0);
+
+static const struct snd_kcontrol_new passthrur_ctl =
+	SOC_DAPM_SINGLE("Switch", CS42L52_MISC_CTL, 7, 1, 0);
+
+static const struct snd_kcontrol_new spkl_ctl =
+	SOC_DAPM_SINGLE("Switch", CS42L52_PWRCTL3, 0, 1, 1);
+
+static const struct snd_kcontrol_new spkr_ctl =
+	SOC_DAPM_SINGLE("Switch", CS42L52_PWRCTL3, 2, 1, 1);
+
+static const struct snd_kcontrol_new hpl_ctl =
+	SOC_DAPM_SINGLE("Switch", CS42L52_PWRCTL3, 4, 1, 1);
+
+static const struct snd_kcontrol_new hpr_ctl =
+	SOC_DAPM_SINGLE("Switch", CS42L52_PWRCTL3, 6, 1, 1);
+
+static const struct snd_kcontrol_new cs42l52_snd_controls[] = {
+
+	SOC_DOUBLE_R_SX_TLV("Master Volume", CS42L52_MASTERA_VOL,
+			      CS42L52_MASTERB_VOL, 0, 0x34, 0xE4, hl_tlv),
+
+	SOC_DOUBLE_R_SX_TLV("Headphone Volume", CS42L52_HPA_VOL,
+			      CS42L52_HPB_VOL, 0, 0x34, 0xCC, hpd_tlv),
+
+	SOC_ENUM("Headphone Analog Gain", hp_gain_enum),
+
+	SOC_DOUBLE_R_SX_TLV("Speaker Volume", CS42L52_SPKA_VOL,
+			      CS42L52_SPKB_VOL, 7, 0x1, 0xff, hl_tlv),
+
+	SOC_DOUBLE_R_SX_TLV("Bypass Volume", CS42L52_PASSTHRUA_VOL,
+			      CS42L52_PASSTHRUB_VOL, 6, 0x18, 0x90, pga_tlv),
+
+	SOC_DOUBLE("Bypass Mute", CS42L52_MISC_CTL, 4, 5, 1, 0),
+
+	SOC_DOUBLE_R_TLV("MIC Gain Volume", CS42L52_MICA_CTL,
+			      CS42L52_MICB_CTL, 0, 0x10, 0, mic_tlv),
+
+	SOC_ENUM("MIC Bias Level", mic_bias_level_enum),
+
+	SOC_DOUBLE_R_SX_TLV("ADC Volume", CS42L52_ADCA_VOL,
+			      CS42L52_ADCB_VOL, 7, 0x80, 0xA0, ipd_tlv),
+	SOC_DOUBLE_R_SX_TLV("ADC Mixer Volume",
+			     CS42L52_ADCA_MIXER_VOL, CS42L52_ADCB_MIXER_VOL,
+				6, 0x7f, 0x19, ipd_tlv),
+
+	SOC_DOUBLE("ADC Switch", CS42L52_ADC_MISC_CTL, 0, 1, 1, 0),
+
+	SOC_DOUBLE_R("ADC Mixer Switch", CS42L52_ADCA_MIXER_VOL,
+		     CS42L52_ADCB_MIXER_VOL, 7, 1, 1),
+
+	SOC_DOUBLE_R_SX_TLV("PGA Volume", CS42L52_PGAA_CTL,
+			    CS42L52_PGAB_CTL, 0, 0x28, 0x30, pga_tlv),
+
+	SOC_DOUBLE_R_SX_TLV("PCM Mixer Volume",
+			    CS42L52_PCMA_MIXER_VOL, CS42L52_PCMB_MIXER_VOL,
+				6, 0x7f, 0x19, hl_tlv),
+	SOC_DOUBLE_R("PCM Mixer Switch",
+		     CS42L52_PCMA_MIXER_VOL, CS42L52_PCMB_MIXER_VOL, 7, 1, 1),
+
+	SOC_ENUM("Beep Config", beep_config_enum),
+	SOC_ENUM("Beep Pitch", beep_pitch_enum),
+	SOC_ENUM("Beep on Time", beep_ontime_enum),
+	SOC_ENUM("Beep off Time", beep_offtime_enum),
+	SOC_SINGLE_TLV("Beep Volume", CS42L52_BEEP_VOL, 0, 0x1f, 0x07, hl_tlv),
+	SOC_SINGLE("Beep Mixer Switch", CS42L52_BEEP_TONE_CTL, 5, 1, 1),
+	SOC_ENUM("Beep Treble Corner Freq", beep_treble_enum),
+	SOC_ENUM("Beep Bass Corner Freq", beep_bass_enum),
+
+	SOC_SINGLE("Tone Control Switch", CS42L52_BEEP_TONE_CTL, 0, 1, 1),
+	SOC_SINGLE_TLV("Treble Gain Volume",
+			    CS42L52_TONE_CTL, 4, 15, 1, hl_tlv),
+	SOC_SINGLE_TLV("Bass Gain Volume",
+			    CS42L52_TONE_CTL, 0, 15, 1, hl_tlv),
+
+	/* Limiter */
+	SOC_SINGLE_TLV("Limiter Max Threshold Volume",
+		       CS42L52_LIMITER_CTL1, 5, 7, 0, limiter_tlv),
+	SOC_SINGLE_TLV("Limiter Cushion Threshold Volume",
+		       CS42L52_LIMITER_CTL1, 2, 7, 0, limiter_tlv),
+	SOC_SINGLE_TLV("Limiter Release Rate Volume",
+		       CS42L52_LIMITER_CTL2, 0, 63, 0, limiter_tlv),
+	SOC_SINGLE_TLV("Limiter Attack Rate Volume",
+		       CS42L52_LIMITER_AT_RATE, 0, 63, 0, limiter_tlv),
+
+	SOC_SINGLE("Limiter SR Switch", CS42L52_LIMITER_CTL1, 1, 1, 0),
+	SOC_SINGLE("Limiter ZC Switch", CS42L52_LIMITER_CTL1, 0, 1, 0),
+	SOC_SINGLE("Limiter Switch", CS42L52_LIMITER_CTL2, 7, 1, 0),
+
+	/* ALC */
+	SOC_SINGLE_TLV("ALC Attack Rate Volume", CS42L52_ALC_CTL,
+		       0, 63, 0, limiter_tlv),
+	SOC_SINGLE_TLV("ALC Release Rate Volume", CS42L52_ALC_RATE,
+		       0, 63, 0, limiter_tlv),
+	SOC_SINGLE_TLV("ALC Max Threshold Volume", CS42L52_ALC_THRESHOLD,
+		       5, 7, 0, limiter_tlv),
+	SOC_SINGLE_TLV("ALC Min Threshold Volume", CS42L52_ALC_THRESHOLD,
+		       2, 7, 0, limiter_tlv),
+
+	SOC_DOUBLE_R("ALC SR Capture Switch", CS42L52_PGAA_CTL,
+		     CS42L52_PGAB_CTL, 7, 1, 1),
+	SOC_DOUBLE_R("ALC ZC Capture Switch", CS42L52_PGAA_CTL,
+		     CS42L52_PGAB_CTL, 6, 1, 1),
+	SOC_DOUBLE("ALC Capture Switch", CS42L52_ALC_CTL, 6, 7, 1, 0),
+
+	/* Noise gate */
+	SOC_ENUM("NG Type Switch", ng_type_enum),
+	SOC_SINGLE("NG Enable Switch", CS42L52_NOISE_GATE_CTL, 6, 1, 0),
+	SOC_SINGLE("NG Boost Switch", CS42L52_NOISE_GATE_CTL, 5, 1, 1),
+	SOC_ENUM("NG Threshold", ng_threshold_enum),
+	SOC_ENUM("NG Delay", ng_delay_enum),
+
+	SOC_DOUBLE("HPF Switch", CS42L52_ANALOG_HPF_CTL, 5, 7, 1, 0),
+
+	SOC_DOUBLE("Analog SR Switch", CS42L52_ANALOG_HPF_CTL, 1, 3, 1, 1),
+	SOC_DOUBLE("Analog ZC Switch", CS42L52_ANALOG_HPF_CTL, 0, 2, 1, 1),
+	SOC_SINGLE("Digital SR Switch", CS42L52_MISC_CTL, 1, 1, 0),
+	SOC_SINGLE("Digital ZC Switch", CS42L52_MISC_CTL, 0, 1, 0),
+	SOC_SINGLE("Deemphasis Switch", CS42L52_MISC_CTL, 2, 1, 0),
+
+	SOC_SINGLE("Batt Compensation Switch", CS42L52_BATT_COMPEN, 7, 1, 0),
+	SOC_SINGLE("Batt VP Monitor Switch", CS42L52_BATT_COMPEN, 6, 1, 0),
+	SOC_SINGLE("Batt VP ref", CS42L52_BATT_COMPEN, 0, 0x0f, 0),
+
+	SOC_SINGLE("PGA AIN1L Switch", CS42L52_ADC_PGA_A, 0, 1, 0),
+	SOC_SINGLE("PGA AIN1R Switch", CS42L52_ADC_PGA_B, 0, 1, 0),
+	SOC_SINGLE("PGA AIN2L Switch", CS42L52_ADC_PGA_A, 1, 1, 0),
+	SOC_SINGLE("PGA AIN2R Switch", CS42L52_ADC_PGA_B, 1, 1, 0),
+
+	SOC_SINGLE("PGA AIN3L Switch", CS42L52_ADC_PGA_A, 2, 1, 0),
+	SOC_SINGLE("PGA AIN3R Switch", CS42L52_ADC_PGA_B, 2, 1, 0),
+
+	SOC_SINGLE("PGA AIN4L Switch", CS42L52_ADC_PGA_A, 3, 1, 0),
+	SOC_SINGLE("PGA AIN4R Switch", CS42L52_ADC_PGA_B, 3, 1, 0),
+
+	SOC_SINGLE("PGA MICA Switch", CS42L52_ADC_PGA_A, 4, 1, 0),
+	SOC_SINGLE("PGA MICB Switch", CS42L52_ADC_PGA_B, 4, 1, 0),
+
+};
+
+static const struct snd_soc_dapm_widget cs42l52_dapm_widgets[] = {
+
+	SND_SOC_DAPM_INPUT("AIN1L"),
+	SND_SOC_DAPM_INPUT("AIN1R"),
+	SND_SOC_DAPM_INPUT("AIN2L"),
+	SND_SOC_DAPM_INPUT("AIN2R"),
+	SND_SOC_DAPM_INPUT("AIN3L"),
+	SND_SOC_DAPM_INPUT("AIN3R"),
+	SND_SOC_DAPM_INPUT("AIN4L"),
+	SND_SOC_DAPM_INPUT("AIN4R"),
+	SND_SOC_DAPM_INPUT("MICA"),
+	SND_SOC_DAPM_INPUT("MICB"),
+	SND_SOC_DAPM_SIGGEN("Beep"),
+
+	SND_SOC_DAPM_AIF_OUT("AIFOUTL", NULL,  0,
+			SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("AIFOUTR", NULL,  0,
+			SND_SOC_NOPM, 0, 0),
+
+	SND_SOC_DAPM_MUX("MICA Mux", SND_SOC_NOPM, 0, 0, &mica_mux),
+	SND_SOC_DAPM_MUX("MICB Mux", SND_SOC_NOPM, 0, 0, &micb_mux),
+
+	SND_SOC_DAPM_ADC("ADC Left", NULL, CS42L52_PWRCTL1, 1, 1),
+	SND_SOC_DAPM_ADC("ADC Right", NULL, CS42L52_PWRCTL1, 2, 1),
+	SND_SOC_DAPM_PGA("PGA Left", CS42L52_PWRCTL1, 3, 1, NULL, 0),
+	SND_SOC_DAPM_PGA("PGA Right", CS42L52_PWRCTL1, 4, 1, NULL, 0),
+
+	SND_SOC_DAPM_MUX("ADC Left Mux", SND_SOC_NOPM, 0, 0, &adca_mux),
+	SND_SOC_DAPM_MUX("ADC Right Mux", SND_SOC_NOPM, 0, 0, &adcb_mux),
+
+	SND_SOC_DAPM_MUX("ADC Left Swap", SND_SOC_NOPM,
+			 0, 0, &adca_mixer),
+	SND_SOC_DAPM_MUX("ADC Right Swap", SND_SOC_NOPM,
+			 0, 0, &adcb_mixer),
+
+	SND_SOC_DAPM_MUX("Output Mux", SND_SOC_NOPM,
+			 0, 0, &digital_output_mux),
+
+	SND_SOC_DAPM_PGA("PGA MICA", CS42L52_PWRCTL2, 1, 1, NULL, 0),
+	SND_SOC_DAPM_PGA("PGA MICB", CS42L52_PWRCTL2, 2, 1, NULL, 0),
+
+	SND_SOC_DAPM_SUPPLY("Mic Bias", CS42L52_PWRCTL2, 0, 1, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("Charge Pump", CS42L52_PWRCTL1, 7, 1, NULL, 0),
+
+	SND_SOC_DAPM_AIF_IN("AIFINL", NULL,  0,
+			SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_IN("AIFINR", NULL,  0,
+			SND_SOC_NOPM, 0, 0),
+
+	SND_SOC_DAPM_DAC("DAC Left", NULL, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_DAC("DAC Right", NULL, SND_SOC_NOPM, 0, 0),
+
+	SND_SOC_DAPM_SWITCH("Bypass Left", CS42L52_MISC_CTL,
+			    6, 0, &passthrul_ctl),
+	SND_SOC_DAPM_SWITCH("Bypass Right", CS42L52_MISC_CTL,
+			    7, 0, &passthrur_ctl),
+
+	SND_SOC_DAPM_MUX("PCM Left Swap", SND_SOC_NOPM,
+			 0, 0, &pcma_mixer),
+	SND_SOC_DAPM_MUX("PCM Right Swap", SND_SOC_NOPM,
+			 0, 0, &pcmb_mixer),
+
+	SND_SOC_DAPM_SWITCH("HP Left Amp", SND_SOC_NOPM, 0, 0, &hpl_ctl),
+	SND_SOC_DAPM_SWITCH("HP Right Amp", SND_SOC_NOPM, 0, 0, &hpr_ctl),
+
+	SND_SOC_DAPM_SWITCH("SPK Left Amp", SND_SOC_NOPM, 0, 0, &spkl_ctl),
+	SND_SOC_DAPM_SWITCH("SPK Right Amp", SND_SOC_NOPM, 0, 0, &spkr_ctl),
+
+	SND_SOC_DAPM_OUTPUT("HPOUTA"),
+	SND_SOC_DAPM_OUTPUT("HPOUTB"),
+	SND_SOC_DAPM_OUTPUT("SPKOUTA"),
+	SND_SOC_DAPM_OUTPUT("SPKOUTB"),
+
+};
+
+static const struct snd_soc_dapm_route cs42l52_audio_map[] = {
+
+	{"Capture", NULL, "AIFOUTL"},
+	{"Capture", NULL, "AIFOUTL"},
+
+	{"AIFOUTL", NULL, "Output Mux"},
+	{"AIFOUTR", NULL, "Output Mux"},
+
+	{"Output Mux", "ADC", "ADC Left"},
+	{"Output Mux", "ADC", "ADC Right"},
+
+	{"ADC Left", NULL, "Charge Pump"},
+	{"ADC Right", NULL, "Charge Pump"},
+
+	{"Charge Pump", NULL, "ADC Left Mux"},
+	{"Charge Pump", NULL, "ADC Right Mux"},
+
+	{"ADC Left Mux", "Input1A", "AIN1L"},
+	{"ADC Right Mux", "Input1B", "AIN1R"},
+	{"ADC Left Mux", "Input2A", "AIN2L"},
+	{"ADC Right Mux", "Input2B", "AIN2R"},
+	{"ADC Left Mux", "Input3A", "AIN3L"},
+	{"ADC Right Mux", "Input3B", "AIN3R"},
+	{"ADC Left Mux", "Input4A", "AIN4L"},
+	{"ADC Right Mux", "Input4B", "AIN4R"},
+	{"ADC Left Mux", "PGA Input Left", "PGA Left"},
+	{"ADC Right Mux", "PGA Input Right" , "PGA Right"},
+
+	{"PGA Left", "Switch", "AIN1L"},
+	{"PGA Right", "Switch", "AIN1R"},
+	{"PGA Left", "Switch", "AIN2L"},
+	{"PGA Right", "Switch", "AIN2R"},
+	{"PGA Left", "Switch", "AIN3L"},
+	{"PGA Right", "Switch", "AIN3R"},
+	{"PGA Left", "Switch", "AIN4L"},
+	{"PGA Right", "Switch", "AIN4R"},
+
+	{"PGA Left", "Switch", "PGA MICA"},
+	{"PGA MICA", NULL, "MICA"},
+
+	{"PGA Right", "Switch", "PGA MICB"},
+	{"PGA MICB", NULL, "MICB"},
+
+	{"HPOUTA", NULL, "HP Left Amp"},
+	{"HPOUTB", NULL, "HP Right Amp"},
+	{"HP Left Amp", NULL, "Bypass Left"},
+	{"HP Right Amp", NULL, "Bypass Right"},
+	{"Bypass Left", "Switch", "PGA Left"},
+	{"Bypass Right", "Switch", "PGA Right"},
+	{"HP Left Amp", "Switch", "DAC Left"},
+	{"HP Right Amp", "Switch", "DAC Right"},
+
+	{"SPKOUTA", NULL, "SPK Left Amp"},
+	{"SPKOUTB", NULL, "SPK Right Amp"},
+
+	{"SPK Left Amp", NULL, "Beep"},
+	{"SPK Right Amp", NULL, "Beep"},
+	{"SPK Left Amp", "Switch", "Playback"},
+	{"SPK Right Amp", "Switch", "Playback"},
+
+	{"DAC Left", NULL, "Beep"},
+	{"DAC Right", NULL, "Beep"},
+	{"DAC Left", NULL, "Playback"},
+	{"DAC Right", NULL, "Playback"},
+
+	{"Output Mux", "DSP", "Playback"},
+	{"Output Mux", "DSP", "Playback"},
+
+	{"AIFINL", NULL, "Playback"},
+	{"AIFINR", NULL, "Playback"},
+
+};
+
+struct cs42l52_clk_para {
+	u32 mclk;
+	u32 rate;
+	u8 speed;
+	u8 group;
+	u8 videoclk;
+	u8 ratio;
+	u8 mclkdiv2;
+};
+
+static const struct cs42l52_clk_para clk_map_table[] = {
+	/*8k*/
+	{12288000, 8000, CLK_QS_MODE, CLK_32K, CLK_NO_27M, CLK_R_128, 0},
+	{18432000, 8000, CLK_QS_MODE, CLK_32K, CLK_NO_27M, CLK_R_128, 0},
+	{12000000, 8000, CLK_QS_MODE, CLK_32K, CLK_NO_27M, CLK_R_125, 0},
+	{24000000, 8000, CLK_QS_MODE, CLK_32K, CLK_NO_27M, CLK_R_125, 1},
+	{27000000, 8000, CLK_QS_MODE, CLK_32K, CLK_27M_MCLK, CLK_R_125, 0},
+
+	/*11.025k*/
+	{11289600, 11025, CLK_QS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0},
+	{16934400, 11025, CLK_QS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0},
+
+	/*16k*/
+	{12288000, 16000, CLK_HS_MODE, CLK_32K, CLK_NO_27M, CLK_R_128, 0},
+	{18432000, 16000, CLK_HS_MODE, CLK_32K, CLK_NO_27M, CLK_R_128, 0},
+	{12000000, 16000, CLK_HS_MODE, CLK_32K, CLK_NO_27M, CLK_R_125, 0},
+	{24000000, 16000, CLK_HS_MODE, CLK_32K, CLK_NO_27M, CLK_R_125, 1},
+	{27000000, 16000, CLK_HS_MODE, CLK_32K, CLK_27M_MCLK, CLK_R_125, 1},
+
+	/*22.05k*/
+	{11289600, 22050, CLK_HS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0},
+	{16934400, 22050, CLK_HS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0},
+
+	/* 32k */
+	{12288000, 32000, CLK_SS_MODE, CLK_32K, CLK_NO_27M, CLK_R_128, 0},
+	{18432000, 32000, CLK_SS_MODE, CLK_32K, CLK_NO_27M, CLK_R_128, 0},
+	{12000000, 32000, CLK_SS_MODE, CLK_32K, CLK_NO_27M, CLK_R_125, 0},
+	{24000000, 32000, CLK_SS_MODE, CLK_32K, CLK_NO_27M, CLK_R_125, 1},
+	{27000000, 32000, CLK_SS_MODE, CLK_32K, CLK_27M_MCLK, CLK_R_125, 0},
+
+	/* 44.1k */
+	{11289600, 44100, CLK_SS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0},
+	{16934400, 44100, CLK_SS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0},
+
+	/* 48k */
+	{12288000, 48000, CLK_SS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0},
+	{18432000, 48000, CLK_SS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0},
+	{12000000, 48000, CLK_SS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_125, 0},
+	{24000000, 48000, CLK_SS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_125, 1},
+	{27000000, 48000, CLK_SS_MODE, CLK_NO_32K, CLK_27M_MCLK, CLK_R_125, 1},
+
+	/* 88.2k */
+	{11289600, 88200, CLK_DS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0},
+	{16934400, 88200, CLK_DS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0},
+
+	/* 96k */
+	{12288000, 96000, CLK_DS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0},
+	{18432000, 96000, CLK_DS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0},
+	{12000000, 96000, CLK_DS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_125, 0},
+	{24000000, 96000, CLK_DS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_125, 1},
+};
+
+static int cs42l52_get_clk(int mclk, int rate)
+{
+	int i, ret = 0;
+	u_int mclk1, mclk2 = 0;
+
+	for (i = 0; i < ARRAY_SIZE(clk_map_table); i++) {
+		if (clk_map_table[i].rate == rate) {
+			mclk1 = clk_map_table[i].mclk;
+			if (abs(mclk - mclk1) < abs(mclk - mclk2)) {
+				mclk2 = mclk1;
+				ret = i;
+			}
+		}
+	}
+	if (ret > ARRAY_SIZE(clk_map_table))
+		return -EINVAL;
+	return ret;
+}
+
+static int cs42l52_set_sysclk(struct snd_soc_dai *codec_dai,
+			int clk_id, unsigned int freq, int dir)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+
+	if ((freq >= CS42L52_MIN_CLK) && (freq <= CS42L52_MAX_CLK)) {
+		cs42l52->sysclk = freq;
+	} else {
+		dev_err(codec->dev, "Invalid freq paramter\n");
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int cs42l52_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+	int ret = 0;
+	u8 iface = 0;
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		iface = CS42L52_IFACE_CTL1_MASTER;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		iface = CS42L52_IFACE_CTL1_SLAVE;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	 /* interface format */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		iface |= CS42L52_IFACE_CTL1_ADC_FMT_I2S |
+				CS42L52_IFACE_CTL1_DAC_FMT_I2S;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		iface |= CS42L52_IFACE_CTL1_DAC_FMT_RIGHT_J;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		iface |= CS42L52_IFACE_CTL1_ADC_FMT_LEFT_J |
+				CS42L52_IFACE_CTL1_DAC_FMT_LEFT_J;
+		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		iface |= CS42L52_IFACE_CTL1_DSP_MODE_EN;
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* clock inversion */
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		break;
+	case SND_SOC_DAIFMT_IB_IF:
+		iface |= CS42L52_IFACE_CTL1_INV_SCLK;
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		iface |= CS42L52_IFACE_CTL1_INV_SCLK;
+		break;
+	case SND_SOC_DAIFMT_NB_IF:
+		break;
+	default:
+		ret = -EINVAL;
+	}
+	cs42l52->config.format = iface;
+	snd_soc_write(codec, CS42L52_IFACE_CTL1, cs42l52->config.format);
+
+	return 0;
+}
+
+static int cs42l52_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+	struct snd_soc_codec *codec = dai->codec;
+
+	if (mute)
+		snd_soc_update_bits(codec, CS42L52_PB_CTL1,
+				    CS42L52_PB_CTL1_MUTE_MASK,
+				CS42L52_PB_CTL1_MUTE);
+	else
+		snd_soc_update_bits(codec, CS42L52_PB_CTL1,
+				    CS42L52_PB_CTL1_MUTE_MASK,
+				CS42L52_PB_CTL1_UNMUTE);
+
+	return 0;
+}
+
+static int cs42l52_pcm_hw_params(struct snd_pcm_substream *substream,
+				     struct snd_pcm_hw_params *params,
+				     struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+	u32 clk = 0;
+	int index;
+
+	index = cs42l52_get_clk(cs42l52->sysclk, params_rate(params));
+	if (index >= 0) {
+		cs42l52->sysclk = clk_map_table[index].mclk;
+
+		clk |= (clk_map_table[index].speed << CLK_SPEED_SHIFT) |
+		(clk_map_table[index].group << CLK_32K_SR_SHIFT) |
+		(clk_map_table[index].videoclk << CLK_27M_MCLK_SHIFT) |
+		(clk_map_table[index].ratio << CLK_RATIO_SHIFT) |
+		clk_map_table[index].mclkdiv2;
+
+		snd_soc_write(codec, CS42L52_CLK_CTL, clk);
+	} else {
+		dev_err(codec->dev, "can't get correct mclk\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int cs42l52_set_bias_level(struct snd_soc_codec *codec,
+					enum snd_soc_bias_level level)
+{
+	struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		break;
+	case SND_SOC_BIAS_PREPARE:
+		snd_soc_update_bits(codec, CS42L52_PWRCTL1,
+				    CS42L52_PWRCTL1_PDN_CODEC, 0);
+		break;
+	case SND_SOC_BIAS_STANDBY:
+		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+			regcache_cache_only(cs42l52->regmap, false);
+			regcache_sync(cs42l52->regmap);
+		}
+		snd_soc_write(codec, CS42L52_PWRCTL1, CS42L52_PWRCTL1_PDN_ALL);
+		break;
+	case SND_SOC_BIAS_OFF:
+		snd_soc_write(codec, CS42L52_PWRCTL1, CS42L52_PWRCTL1_PDN_ALL);
+		regcache_cache_only(cs42l52->regmap, true);
+		break;
+	}
+	codec->dapm.bias_level = level;
+
+	return 0;
+}
+
+#define CS42L52_RATES (SNDRV_PCM_RATE_8000_96000)
+
+#define CS42L52_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE | \
+			SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_U18_3LE | \
+			SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_U20_3LE | \
+			SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U24_LE)
+
+static struct snd_soc_dai_ops cs42l52_ops = {
+	.hw_params	= cs42l52_pcm_hw_params,
+	.digital_mute	= cs42l52_digital_mute,
+	.set_fmt	= cs42l52_set_fmt,
+	.set_sysclk	= cs42l52_set_sysclk,
+};
+
+static struct snd_soc_dai_driver cs42l52_dai = {
+		.name = "cs42l52",
+		.playback = {
+			.stream_name = "Playback",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = CS42L52_RATES,
+			.formats = CS42L52_FORMATS,
+		},
+		.capture = {
+			.stream_name = "Capture",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = CS42L52_RATES,
+			.formats = CS42L52_FORMATS,
+		},
+		.ops = &cs42l52_ops,
+};
+
+static int cs42l52_suspend(struct snd_soc_codec *codec)
+{
+	cs42l52_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+	return 0;
+}
+
+static int cs42l52_resume(struct snd_soc_codec *codec)
+{
+	cs42l52_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	return 0;
+}
+
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+static int beep_rates[] = {
+	261, 522, 585, 667, 706, 774, 889, 1000,
+	1043, 1200, 1333, 1412, 1600, 1714, 2000, 2182
+};
+
+static void cs42l52_beep_work(struct work_struct *work)
+{
+	struct cs42l52_private *cs42l52 =
+		container_of(work, struct cs42l52_private, beep_work);
+	struct snd_soc_codec *codec = cs42l52->codec;
+	struct snd_soc_dapm_context *dapm = &codec->dapm;
+	int i;
+	int val = 0;
+	int best = 0;
+
+	if (cs42l52->beep_rate) {
+		for (i = 0; i < ARRAY_SIZE(beep_rates); i++) {
+			if (abs(cs42l52->beep_rate - beep_rates[i]) <
+			    abs(cs42l52->beep_rate - beep_rates[best]))
+				best = i;
+		}
+
+		dev_dbg(codec->dev, "Set beep rate %dHz for requested %dHz\n",
+			beep_rates[best], cs42l52->beep_rate);
+
+		val = (best << CS42L52_BEEP_RATE_SHIFT);
+
+		snd_soc_dapm_enable_pin(dapm, "Beep");
+	} else {
+		dev_dbg(codec->dev, "Disabling beep\n");
+		snd_soc_dapm_disable_pin(dapm, "Beep");
+	}
+
+	snd_soc_update_bits(codec, CS42L52_BEEP_FREQ,
+			    CS42L52_BEEP_RATE_MASK, val);
+
+	snd_soc_dapm_sync(dapm);
+}
+
+/* For usability define a way of injecting beep events for the device -
+ * many systems will not have a keyboard.
+ */
+static int cs42l52_beep_event(struct input_dev *dev, unsigned int type,
+			     unsigned int code, int hz)
+{
+	struct snd_soc_codec *codec = input_get_drvdata(dev);
+	struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+
+	dev_dbg(codec->dev, "Beep event %x %x\n", code, hz);
+
+	switch (code) {
+	case SND_BELL:
+		if (hz)
+			hz = 261;
+	case SND_TONE:
+		break;
+	default:
+		return -1;
+	}
+
+	/* Kick the beep from a workqueue */
+	cs42l52->beep_rate = hz;
+	schedule_work(&cs42l52->beep_work);
+	return 0;
+}
+
+static ssize_t cs42l52_beep_set(struct device *dev,
+			       struct device_attribute *attr,
+			       const char *buf, size_t count)
+{
+	struct cs42l52_private *cs42l52 = dev_get_drvdata(dev);
+	long int time;
+	int ret;
+
+	ret = kstrtol(buf, 10, &time);
+	if (ret != 0)
+		return ret;
+
+	input_event(cs42l52->beep, EV_SND, SND_TONE, time);
+
+	return count;
+}
+
+static DEVICE_ATTR(beep, 0200, NULL, cs42l52_beep_set);
+
+static void cs42l52_init_beep(struct snd_soc_codec *codec)
+{
+	struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+	int ret;
+
+	cs42l52->beep = input_allocate_device();
+	if (!cs42l52->beep) {
+		dev_err(codec->dev, "Failed to allocate beep device\n");
+		return;
+	}
+
+	INIT_WORK(&cs42l52->beep_work, cs42l52_beep_work);
+	cs42l52->beep_rate = 0;
+
+	cs42l52->beep->name = "CS42L52 Beep Generator";
+	cs42l52->beep->phys = dev_name(codec->dev);
+	cs42l52->beep->id.bustype = BUS_I2C;
+
+	cs42l52->beep->evbit[0] = BIT_MASK(EV_SND);
+	cs42l52->beep->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
+	cs42l52->beep->event = cs42l52_beep_event;
+	cs42l52->beep->dev.parent = codec->dev;
+	input_set_drvdata(cs42l52->beep, codec);
+
+	ret = input_register_device(cs42l52->beep);
+	if (ret != 0) {
+		input_free_device(cs42l52->beep);
+		cs42l52->beep = NULL;
+		dev_err(codec->dev, "Failed to register beep device\n");
+	}
+
+	ret = device_create_file(codec->dev, &dev_attr_beep);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to create keyclick file: %d\n",
+			ret);
+	}
+}
+
+static void cs42l52_free_beep(struct snd_soc_codec *codec)
+{
+	struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+
+	device_remove_file(codec->dev, &dev_attr_beep);
+	input_unregister_device(cs42l52->beep);
+	cancel_work_sync(&cs42l52->beep_work);
+	cs42l52->beep = NULL;
+
+	snd_soc_update_bits(codec, CS42L52_BEEP_TONE_CTL,
+			    CS42L52_BEEP_EN_MASK, 0);
+}
+#else
+static void cs42l52_init_beep(struct snd_soc_codec *codec)
+{
+}
+
+static void cs42l52_free_beep(struct snd_soc_codec *codec)
+{
+}
+#endif
+
+static int cs42l52_probe(struct snd_soc_codec *codec)
+{
+	struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+	int ret;
+
+	codec->control_data = cs42l52->regmap;
+	ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_REGMAP);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+		return ret;
+	}
+	regcache_cache_only(cs42l52->regmap, true);
+
+	cs42l52_init_beep(codec);
+
+	cs42l52_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	cs42l52->sysclk = CS42L52_DEFAULT_CLK;
+	cs42l52->config.format = CS42L52_DEFAULT_FORMAT;
+
+	/* Set Platform MICx CFG */
+	snd_soc_update_bits(codec, CS42L52_MICA_CTL,
+			    CS42L52_MIC_CTL_TYPE_MASK,
+				cs42l52->pdata.mica_cfg <<
+				CS42L52_MIC_CTL_TYPE_SHIFT);
+
+	snd_soc_update_bits(codec, CS42L52_MICB_CTL,
+			    CS42L52_MIC_CTL_TYPE_MASK,
+				cs42l52->pdata.micb_cfg <<
+				CS42L52_MIC_CTL_TYPE_SHIFT);
+
+	/* if Single Ended, Get Mic_Select */
+	if (cs42l52->pdata.mica_cfg)
+		snd_soc_update_bits(codec, CS42L52_MICA_CTL,
+				    CS42L52_MIC_CTL_MIC_SEL_MASK,
+				cs42l52->pdata.mica_sel <<
+				CS42L52_MIC_CTL_MIC_SEL_SHIFT);
+	if (cs42l52->pdata.micb_cfg)
+		snd_soc_update_bits(codec, CS42L52_MICB_CTL,
+				    CS42L52_MIC_CTL_MIC_SEL_MASK,
+				cs42l52->pdata.micb_sel <<
+				CS42L52_MIC_CTL_MIC_SEL_SHIFT);
+
+	/* Set Platform Charge Pump Freq */
+	snd_soc_update_bits(codec, CS42L52_CHARGE_PUMP,
+			    CS42L52_CHARGE_PUMP_MASK,
+				cs42l52->pdata.chgfreq <<
+				CS42L52_CHARGE_PUMP_SHIFT);
+
+	/* Set Platform Bias Level */
+	snd_soc_update_bits(codec, CS42L52_IFACE_CTL2,
+			    CS42L52_IFACE_CTL2_BIAS_LVL,
+				cs42l52->pdata.micbias_lvl);
+
+	return ret;
+}
+
+static int cs42l52_remove(struct snd_soc_codec *codec)
+{
+	cs42l52_free_beep(codec);
+	cs42l52_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+	return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_cs42l52 = {
+	.probe = cs42l52_probe,
+	.remove = cs42l52_remove,
+	.suspend = cs42l52_suspend,
+	.resume = cs42l52_resume,
+	.set_bias_level = cs42l52_set_bias_level,
+
+	.dapm_widgets = cs42l52_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(cs42l52_dapm_widgets),
+	.dapm_routes = cs42l52_audio_map,
+	.num_dapm_routes = ARRAY_SIZE(cs42l52_audio_map),
+
+	.controls = cs42l52_snd_controls,
+	.num_controls = ARRAY_SIZE(cs42l52_snd_controls),
+};
+
+/* Current and threshold powerup sequence Pg37 */
+static const struct reg_default cs42l52_threshold_patch[] = {
+
+	{ 0x00, 0x99 },
+	{ 0x3E, 0xBA },
+	{ 0x47, 0x80 },
+	{ 0x32, 0xBB },
+	{ 0x32, 0x3B },
+	{ 0x00, 0x00 },
+
+};
+
+static struct regmap_config cs42l52_regmap = {
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.max_register = CS42L52_MAX_REGISTER,
+	.reg_defaults = cs42l52_reg_defaults,
+	.num_reg_defaults = ARRAY_SIZE(cs42l52_reg_defaults),
+	.readable_reg = cs42l52_readable_register,
+	.volatile_reg = cs42l52_volatile_register,
+	.cache_type = REGCACHE_RBTREE,
+};
+
+static int cs42l52_i2c_probe(struct i2c_client *i2c_client,
+			     const struct i2c_device_id *id)
+{
+	struct cs42l52_private *cs42l52;
+	int ret;
+	unsigned int devid = 0;
+	unsigned int reg;
+
+	cs42l52 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs42l52_private),
+			       GFP_KERNEL);
+	if (cs42l52 == NULL)
+		return -ENOMEM;
+	cs42l52->dev = &i2c_client->dev;
+
+	cs42l52->regmap = regmap_init_i2c(i2c_client, &cs42l52_regmap);
+	if (IS_ERR(cs42l52->regmap)) {
+		ret = PTR_ERR(cs42l52->regmap);
+		dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret);
+		goto err;
+	}
+
+	i2c_set_clientdata(i2c_client, cs42l52);
+
+	if (dev_get_platdata(&i2c_client->dev))
+		memcpy(&cs42l52->pdata, dev_get_platdata(&i2c_client->dev),
+		       sizeof(cs42l52->pdata));
+
+	ret = regmap_register_patch(cs42l52->regmap, cs42l52_threshold_patch,
+				    ARRAY_SIZE(cs42l52_threshold_patch));
+	if (ret != 0)
+		dev_warn(cs42l52->dev, "Failed to apply regmap patch: %d\n",
+			 ret);
+
+	ret = regmap_read(cs42l52->regmap, CS42L52_CHIP, &reg);
+	devid = reg & CS42L52_CHIP_ID_MASK;
+	if (devid != CS42L52_CHIP_ID) {
+		ret = -ENODEV;
+		dev_err(&i2c_client->dev,
+			"CS42L52 Device ID (%X). Expected %X\n",
+			devid, CS42L52_CHIP_ID);
+		goto err_regmap;
+	}
+
+	regcache_cache_only(cs42l52->regmap, true);
+
+	ret =  snd_soc_register_codec(&i2c_client->dev,
+			&soc_codec_dev_cs42l52, &cs42l52_dai, 1);
+	if (ret < 0)
+		goto err_regmap;
+	return 0;
+
+err_regmap:
+	regmap_exit(cs42l52->regmap);
+
+err:
+	return ret;
+}
+
+static int cs42l52_i2c_remove(struct i2c_client *client)
+{
+	struct cs42l52_private *cs42l52 = i2c_get_clientdata(client);
+
+	snd_soc_unregister_codec(&client->dev);
+	regmap_exit(cs42l52->regmap);
+
+	return 0;
+}
+
+static const struct i2c_device_id cs42l52_id[] = {
+	{ "cs42l52", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, cs42l52_id);
+
+static struct i2c_driver cs42l52_i2c_driver = {
+	.driver = {
+		.name = "cs42l52",
+		.owner = THIS_MODULE,
+	},
+	.id_table = cs42l52_id,
+	.probe =    cs42l52_i2c_probe,
+	.remove =   __devexit_p(cs42l52_i2c_remove),
+};
+
+module_i2c_driver(cs42l52_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS42L52 driver");
+MODULE_AUTHOR("Georgi Vlaev, Nucleus Systems Ltd, <joe@nucleusys.com>");
+MODULE_AUTHOR("Brian Austin, Cirrus Logic Inc, <brian.austin@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs42l52.h b/sound/soc/codecs/cs42l52.h
new file mode 100644
index 0000000..60985c0
--- /dev/null
+++ b/sound/soc/codecs/cs42l52.h
@@ -0,0 +1,274 @@
+/*
+ * cs42l52.h -- CS42L52 ALSA SoC audio driver
+ *
+ * Copyright 2012 CirrusLogic, Inc.
+ *
+ * Author: Georgi Vlaev <joe@nucleusys.com>
+ * Author: Brian Austin <brian.austin@cirrus.com>
+ *
+ * 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.
+ *
+ */
+
+#ifndef __CS42L52_H__
+#define __CS42L52_H__
+
+#define CS42L52_NAME				"CS42L52"
+#define CS42L52_DEFAULT_CLK			12000000
+#define CS42L52_MIN_CLK				11000000
+#define CS42L52_MAX_CLK				27000000
+#define CS42L52_DEFAULT_FORMAT			SNDRV_PCM_FMTBIT_S16_LE
+#define CS42L52_DEFAULT_MAX_CHANS		2
+#define CS42L52_SYSCLK				1
+
+#define CS42L52_CHIP_SWICTH			(1 << 17)
+#define CS42L52_ALL_IN_ONE			(1 << 16)
+#define CS42L52_CHIP_ONE			0x00
+#define CS42L52_CHIP_TWO			0x01
+#define CS42L52_CHIP_THR			0x02
+#define CS42L52_CHIP_MASK			0x0f
+
+#define CS42L52_FIX_BITS_CTL			0x00
+#define CS42L52_CHIP				0x01
+#define CS42L52_CHIP_ID				0xE0
+#define CS42L52_CHIP_ID_MASK			0xF8
+#define CS42L52_CHIP_REV_A0			0x00
+#define CS42L52_CHIP_REV_A1			0x01
+#define CS42L52_CHIP_REV_B0			0x02
+#define CS42L52_CHIP_REV_MASK			0x03
+
+#define CS42L52_PWRCTL1				0x02
+#define CS42L52_PWRCTL1_PDN_ALL			0x9F
+#define CS42L52_PWRCTL1_PDN_CHRG		0x80
+#define CS42L52_PWRCTL1_PDN_PGAB		0x10
+#define CS42L52_PWRCTL1_PDN_PGAA		0x08
+#define CS42L52_PWRCTL1_PDN_ADCB		0x04
+#define CS42L52_PWRCTL1_PDN_ADCA		0x02
+#define CS42L52_PWRCTL1_PDN_CODEC		0x01
+
+#define CS42L52_PWRCTL2				0x03
+#define CS42L52_PWRCTL2_OVRDB			(1 << 4)
+#define CS42L52_PWRCTL2_OVRDA			(1 << 3)
+#define	CS42L52_PWRCTL2_PDN_MICB		(1 << 2)
+#define CS42L52_PWRCTL2_PDN_MICB_SHIFT		2
+#define CS42L52_PWRCTL2_PDN_MICA		(1 << 1)
+#define CS42L52_PWRCTL2_PDN_MICA_SHIFT		1
+#define CS42L52_PWRCTL2_PDN_MICBIAS		(1 << 0)
+#define CS42L52_PWRCTL2_PDN_MICBIAS_SHIFT	0
+
+#define CS42L52_PWRCTL3				0x04
+#define CS42L52_PWRCTL3_HPB_PDN_SHIFT		6
+#define CS42L52_PWRCTL3_HPB_ON_LOW		0x00
+#define CS42L52_PWRCTL3_HPB_ON_HIGH		0x01
+#define CS42L52_PWRCTL3_HPB_ALWAYS_ON		0x02
+#define CS42L52_PWRCTL3_HPB_ALWAYS_OFF		0x03
+#define CS42L52_PWRCTL3_HPA_PDN_SHIFT		4
+#define CS42L52_PWRCTL3_HPA_ON_LOW		0x00
+#define CS42L52_PWRCTL3_HPA_ON_HIGH		0x01
+#define CS42L52_PWRCTL3_HPA_ALWAYS_ON		0x02
+#define CS42L52_PWRCTL3_HPA_ALWAYS_OFF		0x03
+#define CS42L52_PWRCTL3_SPKB_PDN_SHIFT		2
+#define CS42L52_PWRCTL3_SPKB_ON_LOW		0x00
+#define CS42L52_PWRCTL3_SPKB_ON_HIGH		0x01
+#define CS42L52_PWRCTL3_SPKB_ALWAYS_ON		0x02
+#define CS42L52_PWRCTL3_PDN_SPKB		(1 << 2)
+#define CS42L52_PWRCTL3_PDN_SPKA		(1 << 0)
+#define CS42L52_PWRCTL3_SPKA_PDN_SHIFT		0
+#define CS42L52_PWRCTL3_SPKA_ON_LOW		0x00
+#define CS42L52_PWRCTL3_SPKA_ON_HIGH		0x01
+#define CS42L52_PWRCTL3_SPKA_ALWAYS_ON		0x02
+
+#define CS42L52_DEFAULT_OUTPUT_STATE		0x05
+#define CS42L52_PWRCTL3_CONF_MASK		0x03
+
+#define CS42L52_CLK_CTL				0x05
+#define CLK_AUTODECT_ENABLE			(1 << 7)
+#define CLK_SPEED_SHIFT				5
+#define CLK_DS_MODE				0x00
+#define CLK_SS_MODE				0x01
+#define CLK_HS_MODE				0x02
+#define CLK_QS_MODE				0x03
+#define CLK_32K_SR_SHIFT			4
+#define CLK_32K					0x01
+#define CLK_NO_32K				0x00
+#define CLK_27M_MCLK_SHIFT			3
+#define CLK_27M_MCLK				0x01
+#define CLK_NO_27M				0x00
+#define CLK_RATIO_SHIFT				1
+#define CLK_R_128				0x00
+#define CLK_R_125				0x01
+#define CLK_R_132				0x02
+#define CLK_R_136				0x03
+
+#define CS42L52_IFACE_CTL1			0x06
+#define CS42L52_IFACE_CTL1_MASTER		(1 << 7)
+#define CS42L52_IFACE_CTL1_SLAVE		(0 << 7)
+#define CS42L52_IFACE_CTL1_INV_SCLK		(1 << 6)
+#define CS42L52_IFACE_CTL1_ADC_FMT_I2S		(1 << 5)
+#define CS42L52_IFACE_CTL1_ADC_FMT_LEFT_J	(0 << 5)
+#define CS42L52_IFACE_CTL1_DSP_MODE_EN		(1 << 4)
+#define CS42L52_IFACE_CTL1_DAC_FMT_LEFT_J	(0 << 2)
+#define CS42L52_IFACE_CTL1_DAC_FMT_I2S		(1 << 2)
+#define CS42L52_IFACE_CTL1_DAC_FMT_RIGHT_J	(2 << 2)
+#define CS42L52_IFACE_CTL1_WL_32BIT		(0x00)
+#define CS42L52_IFACE_CTL1_WL_24BIT		(0x01)
+#define CS42L52_IFACE_CTL1_WL_20BIT		(0x02)
+#define CS42L52_IFACE_CTL1_WL_16BIT		(0x03)
+#define CS42L52_IFACE_CTL1_WL_MASK		0xFFFF
+
+#define CS42L52_IFACE_CTL2			0x07
+#define CS42L52_IFACE_CTL2_SC_MC_EQ		(1 << 6)
+#define CS42L52_IFACE_CTL2_LOOPBACK		(1 << 5)
+#define CS42L52_IFACE_CTL2_S_MODE_OUTPUT_EN	(0 << 4)
+#define CS42L52_IFACE_CTL2_S_MODE_OUTPUT_HIZ	(1 << 4)
+#define CS42L52_IFACE_CTL2_HP_SW_INV		(1 << 3)
+#define CS42L52_IFACE_CTL2_BIAS_LVL		0x07
+
+#define CS42L52_ADC_PGA_A			0x08
+#define CS42L52_ADC_PGA_B			0x09
+#define CS42L52_ADC_SEL_SHIFT			5
+#define CS42L52_ADC_SEL_AIN1			0x00
+#define CS42L52_ADC_SEL_AIN2			0x01
+#define CS42L52_ADC_SEL_AIN3			0x02
+#define CS42L52_ADC_SEL_AIN4			0x03
+#define CS42L52_ADC_SEL_PGA			0x04
+
+#define CS42L52_ANALOG_HPF_CTL			0x0A
+#define CS42L52_HPF_CTL_ANLGSFTB		(1 << 3)
+#define CS42L52_HPF_CTL_ANLGSFTA                (1 << 0)
+
+#define CS42L52_ADC_HPF_FREQ			0x0B
+#define CS42L52_ADC_MISC_CTL			0x0C
+#define CS42L52_ADC_MISC_CTL_SOURCE_DSP		(1 << 6)
+
+#define CS42L52_PB_CTL1				0x0D
+#define CS42L52_PB_CTL1_HP_GAIN_SHIFT		5
+#define CS42L52_PB_CTL1_HP_GAIN_03959		0x00
+#define CS42L52_PB_CTL1_HP_GAIN_04571		0x01
+#define CS42L52_PB_CTL1_HP_GAIN_05111		0x02
+#define CS42L52_PB_CTL1_HP_GAIN_06047		0x03
+#define CS42L52_PB_CTL1_HP_GAIN_07099		0x04
+#define CS42L52_PB_CTL1_HP_GAIN_08399		0x05
+#define CS42L52_PB_CTL1_HP_GAIN_10000		0x06
+#define CS42L52_PB_CTL1_HP_GAIN_11430		0x07
+#define CS42L52_PB_CTL1_INV_PCMB		(1 << 3)
+#define CS42L52_PB_CTL1_INV_PCMA		(1 << 2)
+#define CS42L52_PB_CTL1_MSTB_MUTE		(1 << 1)
+#define CS42L52_PB_CTL1_MSTA_MUTE		(1 << 0)
+#define CS42L52_PB_CTL1_MUTE_MASK		0xFFFD
+#define CS42L52_PB_CTL1_MUTE			3
+#define CS42L52_PB_CTL1_UNMUTE			0
+
+#define CS42L52_MISC_CTL			0x0E
+#define CS42L52_MISC_CTL_DEEMPH			(1 << 2)
+#define CS42L52_MISC_CTL_DIGSFT			(1 << 1)
+#define CS42L52_MISC_CTL_DIGZC			(1 << 0)
+
+#define CS42L52_PB_CTL2				0x0F
+#define CS42L52_PB_CTL2_HPB_MUTE		(1 << 7)
+#define CS42L52_PB_CTL2_HPA_MUTE		(1 << 6)
+#define CS42L52_PB_CTL2_SPKB_MUTE		(1 << 5)
+#define CS42L52_PB_CTL2_SPKA_MUTE		(1 << 4)
+#define CS42L52_PB_CTL2_SPK_SWAP		(1 << 2)
+#define CS42L52_PB_CTL2_SPK_MONO		(1 << 1)
+#define CS42L52_PB_CTL2_SPK_MUTE50		(1 << 0)
+
+#define	CS42L52_MICA_CTL			0x10
+#define CS42L52_MICB_CTL			0x11
+#define	CS42L52_MIC_CTL_MIC_SEL_MASK		0xBF
+#define	CS42L52_MIC_CTL_MIC_SEL_SHIFT		6
+#define CS42L52_MIC_CTL_TYPE_MASK		0xDF
+#define CS42L52_MIC_CTL_TYPE_SHIFT		5
+
+
+#define CS42L52_PGAA_CTL			0x12
+#define CS42L52_PGAB_CTL			0x13
+#define CS42L52_PGAX_CTL_VOL_12DB		24
+#define CS42L52_PGAX_CTL_VOL_6DB		12 /*step size 0.5db*/
+
+#define CS42L52_PASSTHRUA_VOL			0x14
+#define CS42L52_PASSTHRUB_VOL			0x15
+
+#define CS42L52_ADCA_VOL			0x16
+#define CS42L52_ADCB_VOL			0x17
+#define CS42L52_ADCX_VOL_24DB			24 /*step size 1db*/
+#define CS42L52_ADCX_VOL_12DB			12
+#define CS42L52_ADCX_VOL_6DB			6
+
+#define CS42L52_ADCA_MIXER_VOL			0x18
+#define CS42L52_ADCB_MIXER_VOL			0x19
+#define CS42L52_ADC_MIXER_VOL_12DB		0x18
+
+#define CS42L52_PCMA_MIXER_VOL			0x1A
+#define CS42L52_PCMB_MIXER_VOL			0x1B
+
+#define CS42L52_BEEP_FREQ			0x1C
+#define CS42L52_BEEP_VOL			0x1D
+#define CS42L52_BEEP_TONE_CTL			0x1E
+#define CS42L52_BEEP_RATE_SHIFT			4
+#define CS42L52_BEEP_RATE_MASK			0x0F
+
+#define CS42L52_TONE_CTL			0x1F
+#define CS42L52_BEEP_EN_MASK			0x3F
+
+#define CS42L52_MASTERA_VOL			0x20
+#define CS42L52_MASTERB_VOL			0x21
+
+#define CS42L52_HPA_VOL				0x22
+#define CS42L52_HPB_VOL				0x23
+#define CS42L52_DEFAULT_HP_VOL			0xF0
+
+#define CS42L52_SPKA_VOL			0x24
+#define CS42L52_SPKB_VOL			0x25
+#define CS42L52_DEFAULT_SPK_VOL			0xF0
+
+#define CS42L52_ADC_PCM_MIXER			0x26
+
+#define CS42L52_LIMITER_CTL1			0x27
+#define CS42L52_LIMITER_CTL2			0x28
+#define CS42L52_LIMITER_AT_RATE			0x29
+
+#define CS42L52_ALC_CTL				0x2A
+#define CS42L52_ALC_CTL_ALCB_ENABLE_SHIFT	7
+#define CS42L52_ALC_CTL_ALCA_ENABLE_SHIFT	6
+#define CS42L52_ALC_CTL_FASTEST_ATTACK		0
+
+#define CS42L52_ALC_RATE			0x2B
+#define CS42L52_ALC_SLOWEST_RELEASE		0x3F
+
+#define CS42L52_ALC_THRESHOLD			0x2C
+#define CS42L52_ALC_MAX_RATE_SHIFT		5
+#define CS42L52_ALC_MIN_RATE_SHIFT		2
+#define CS42L52_ALC_RATE_0DB			0
+#define CS42L52_ALC_RATE_3DB			1
+#define CS42L52_ALC_RATE_6DB			2
+
+#define CS42L52_NOISE_GATE_CTL			0x2D
+#define CS42L52_NG_ENABLE_SHIFT			6
+#define CS42L52_NG_THRESHOLD_SHIFT		2
+#define CS42L52_NG_MIN_70DB			2
+#define CS42L52_NG_DELAY_SHIFT			0
+#define CS42L52_NG_DELAY_100MS			1
+
+#define CS42L52_CLK_STATUS			0x2E
+#define CS42L52_BATT_COMPEN			0x2F
+
+#define CS42L52_BATT_LEVEL			0x30
+#define CS42L52_SPK_STATUS			0x31
+#define CS42L52_SPK_STATUS_PIN_SHIFT		3
+#define CS42L52_SPK_STATUS_PIN_HIGH		1
+
+#define CS42L52_TEM_CTL				0x32
+#define CS42L52_TEM_CTL_SET			0x80
+#define CS42L52_THE_FOLDBACK			0x33
+#define CS42L52_CHARGE_PUMP			0x34
+#define CS42L52_CHARGE_PUMP_MASK		0xF0
+#define CS42L52_CHARGE_PUMP_SHIFT		4
+#define CS42L52_FIX_BITS1			0x3E
+#define CS42L52_FIX_BITS2			0x47
+
+#define CS42L52_MAX_REGISTER			0x34
+
+#endif
diff --git a/sound/soc/codecs/cs42l73.c b/sound/soc/codecs/cs42l73.c
index 3686417..e0d45fd 100644
--- a/sound/soc/codecs/cs42l73.c
+++ b/sound/soc/codecs/cs42l73.c
@@ -43,9 +43,6 @@
 };
 
 static const struct reg_default cs42l73_reg_defaults[] = {
-	{ 1, 0x42 },	/* r01	- Device ID A&B */
-	{ 2, 0xA7 },	/* r02	- Device ID C&D */
-	{ 3, 0x30 },	/* r03	- Device ID E */
 	{ 6, 0xF1 },	/* r06	- Power Ctl 1 */
 	{ 7, 0xDF },	/* r07	- Power Ctl 2 */
 	{ 8, 0x3F },	/* r08	- Power Ctl 3 */
@@ -402,37 +399,37 @@
 
 static const struct snd_kcontrol_new cs42l73_snd_controls[] = {
 	SOC_DOUBLE_R_SX_TLV("Headphone Analog Playback Volume",
-			CS42L73_HPAAVOL, CS42L73_HPBAVOL, 7,
-			0xffffffC1, 0x0C, hpaloa_tlv),
+			CS42L73_HPAAVOL, CS42L73_HPBAVOL, 0,
+			0x41, 0x4B, hpaloa_tlv),
 
 	SOC_DOUBLE_R_SX_TLV("LineOut Analog Playback Volume", CS42L73_LOAAVOL,
-			CS42L73_LOBAVOL, 7, 0xffffffC1, 0x0C, hpaloa_tlv),
+			CS42L73_LOBAVOL, 0, 0x41, 0x4B, hpaloa_tlv),
 
 	SOC_DOUBLE_R_SX_TLV("Input PGA Analog Volume", CS42L73_MICAPREPGAAVOL,
-			CS42L73_MICBPREPGABVOL, 5, 0xffffff35,
-			0x34, micpga_tlv),
+			CS42L73_MICBPREPGABVOL, 5, 0x34,
+			0x24, micpga_tlv),
 
 	SOC_DOUBLE_R("MIC Preamp Switch", CS42L73_MICAPREPGAAVOL,
 			CS42L73_MICBPREPGABVOL, 6, 1, 1),
 
 	SOC_DOUBLE_R_SX_TLV("Input Path Digital Volume", CS42L73_IPADVOL,
-			CS42L73_IPBDVOL, 7, 0xffffffA0, 0xA0, ipd_tlv),
+			CS42L73_IPBDVOL, 0, 0xA0, 0x6C, ipd_tlv),
 
 	SOC_DOUBLE_R_SX_TLV("HL Digital Playback Volume",
-			CS42L73_HLADVOL, CS42L73_HLBDVOL, 7, 0xffffffE5,
-			0xE4, hl_tlv),
+			CS42L73_HLADVOL, CS42L73_HLBDVOL,
+			0, 0x34, 0xE4, hl_tlv),
 
 	SOC_SINGLE_TLV("ADC A Boost Volume",
 			CS42L73_ADCIPC, 2, 0x01, 1, adc_boost_tlv),
 
 	SOC_SINGLE_TLV("ADC B Boost Volume",
-			CS42L73_ADCIPC, 6, 0x01, 1, adc_boost_tlv),
+		       CS42L73_ADCIPC, 6, 0x01, 1, adc_boost_tlv),
 
-	SOC_SINGLE_TLV("Speakerphone Digital Playback Volume",
-			CS42L73_SPKDVOL, 0, 0xE4, 1, hl_tlv),
+	SOC_SINGLE_SX_TLV("Speakerphone Digital Volume",
+			    CS42L73_SPKDVOL, 0, 0x34, 0xE4, hl_tlv),
 
-	SOC_SINGLE_TLV("Ear Speaker Digital Playback Volume",
-			CS42L73_ESLDVOL, 0, 0xE4, 1, hl_tlv),
+	SOC_SINGLE_SX_TLV("Ear Speaker Digital Volume",
+			    CS42L73_ESLDVOL, 0, 0x34, 0xE4, hl_tlv),
 
 	SOC_DOUBLE_R("Headphone Analog Playback Switch", CS42L73_HPAAVOL,
 			CS42L73_HPBAVOL, 7, 1, 1),
@@ -599,17 +596,17 @@
 	SND_SOC_DAPM_INPUT("MIC2"),
 	SND_SOC_DAPM_SUPPLY("MIC2 Bias", CS42L73_PWRCTL2, 7, 1, NULL, 0),
 
-	SND_SOC_DAPM_AIF_OUT("XSPOUTL", "XSP Capture",  0,
+	SND_SOC_DAPM_AIF_OUT("XSPOUTL", NULL,  0,
 			CS42L73_PWRCTL2, 1, 1),
-	SND_SOC_DAPM_AIF_OUT("XSPOUTR", "XSP Capture",  0,
+	SND_SOC_DAPM_AIF_OUT("XSPOUTR", NULL,  0,
 			CS42L73_PWRCTL2, 1, 1),
-	SND_SOC_DAPM_AIF_OUT("ASPOUTL", "ASP Capture",  0,
+	SND_SOC_DAPM_AIF_OUT("ASPOUTL", NULL,  0,
 			CS42L73_PWRCTL2, 3, 1),
-	SND_SOC_DAPM_AIF_OUT("ASPOUTR", "ASP Capture",  0,
+	SND_SOC_DAPM_AIF_OUT("ASPOUTR", NULL,  0,
 			CS42L73_PWRCTL2, 3, 1),
-	SND_SOC_DAPM_AIF_OUT("VSPOUTL", "VSP Capture",  0,
+	SND_SOC_DAPM_AIF_OUT("VSPOUTL", NULL,  0,
 			CS42L73_PWRCTL2, 4, 1),
-	SND_SOC_DAPM_AIF_OUT("VSPOUTR", "VSP Capture",  0,
+	SND_SOC_DAPM_AIF_OUT("VSPOUTR", NULL,  0,
 			CS42L73_PWRCTL2, 4, 1),
 
 	SND_SOC_DAPM_PGA("PGA Left", SND_SOC_NOPM, 0, 0, NULL, 0),
@@ -638,21 +635,21 @@
 	SND_SOC_DAPM_MIXER("VSPL Output Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
 	SND_SOC_DAPM_MIXER("VSPR Output Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
 
-	SND_SOC_DAPM_AIF_IN("XSPINL", "XSP Playback", 0,
+	SND_SOC_DAPM_AIF_IN("XSPINL", NULL, 0,
 				CS42L73_PWRCTL2, 0, 1),
-	SND_SOC_DAPM_AIF_IN("XSPINR", "XSP Playback", 0,
+	SND_SOC_DAPM_AIF_IN("XSPINR", NULL, 0,
 				CS42L73_PWRCTL2, 0, 1),
-	SND_SOC_DAPM_AIF_IN("XSPINM", "XSP Playback", 0,
+	SND_SOC_DAPM_AIF_IN("XSPINM", NULL, 0,
 				CS42L73_PWRCTL2, 0, 1),
 
-	SND_SOC_DAPM_AIF_IN("ASPINL", "ASP Playback", 0,
+	SND_SOC_DAPM_AIF_IN("ASPINL", NULL, 0,
 				CS42L73_PWRCTL2, 2, 1),
-	SND_SOC_DAPM_AIF_IN("ASPINR", "ASP Playback", 0,
+	SND_SOC_DAPM_AIF_IN("ASPINR", NULL, 0,
 				CS42L73_PWRCTL2, 2, 1),
-	SND_SOC_DAPM_AIF_IN("ASPINM", "ASP Playback", 0,
+	SND_SOC_DAPM_AIF_IN("ASPINM", NULL, 0,
 				CS42L73_PWRCTL2, 2, 1),
 
-	SND_SOC_DAPM_AIF_IN("VSPIN", "VSP Playback", 0,
+	SND_SOC_DAPM_AIF_IN("VSPIN", NULL, 0,
 				CS42L73_PWRCTL2, 4, 1),
 
 	SND_SOC_DAPM_MIXER("HL Left Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
@@ -776,6 +773,14 @@
 	{"HL Left Mixer", NULL, "VSPIN"},
 	{"HL Right Mixer", NULL, "VSPIN"},
 
+	{"ASPINL", NULL, "ASP Playback"},
+	{"ASPINM", NULL, "ASP Playback"},
+	{"ASPINR", NULL, "ASP Playback"},
+	{"XSPINL", NULL, "XSP Playback"},
+	{"XSPINM", NULL, "XSP Playback"},
+	{"XSPINR", NULL, "XSP Playback"},
+	{"VSPIN", NULL, "VSP Playback"},
+
 	/* Capture Paths */
 	{"MIC1", NULL, "MIC1 Bias"},
 	{"PGA Left Mux", "Mic 1", "MIC1"},
@@ -822,6 +827,13 @@
 
 	{"VSPOUTL", NULL, "VSPL Output Mixer"},
 	{"VSPOUTR", NULL, "VSPR Output Mixer"},
+
+	{"ASP Capture", NULL, "ASPOUTL"},
+	{"ASP Capture", NULL, "ASPOUTR"},
+	{"XSP Capture", NULL, "XSPOUTL"},
+	{"XSP Capture", NULL, "XSPOUTR"},
+	{"VSP Capture", NULL, "VSPOUTL"},
+	{"VSP Capture", NULL, "VSPOUTR"},
 };
 
 struct cs42l73_mclk_div {
@@ -1091,8 +1103,7 @@
 				 struct snd_pcm_hw_params *params,
 				 struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec);
 	int id = dai->id;
 	int mclk_coeff;
@@ -1429,25 +1440,7 @@
 
 };
 
-static int __init cs42l73_modinit(void)
-{
-	int ret;
-	ret = i2c_add_driver(&cs42l73_i2c_driver);
-	if (ret != 0) {
-		pr_err("Failed to register CS42L73 I2C driver: %d\n", ret);
-		return ret;
-	}
-	return 0;
-}
-
-module_init(cs42l73_modinit);
-
-static void __exit cs42l73_exit(void)
-{
-	i2c_del_driver(&cs42l73_i2c_driver);
-}
-
-module_exit(cs42l73_exit);
+module_i2c_driver(cs42l73_i2c_driver);
 
 MODULE_DESCRIPTION("ASoC CS42L73 driver");
 MODULE_AUTHOR("Georgi Vlaev, Nucleus Systems Ltd, <joe@nucleusys.com>");
diff --git a/sound/soc/codecs/da7210.c b/sound/soc/codecs/da7210.c
index 7843711..af5db70 100644
--- a/sound/soc/codecs/da7210.c
+++ b/sound/soc/codecs/da7210.c
@@ -17,6 +17,7 @@
 
 #include <linux/delay.h>
 #include <linux/i2c.h>
+#include <linux/spi/spi.h>
 #include <linux/regmap.h>
 #include <linux/slab.h>
 #include <linux/module.h>
@@ -27,6 +28,7 @@
 #include <sound/tlv.h>
 
 /* DA7210 register space */
+#define DA7210_PAGE_CONTROL		0x00
 #define DA7210_CONTROL			0x01
 #define DA7210_STATUS			0x02
 #define DA7210_STARTUP1			0x03
@@ -146,6 +148,7 @@
 #define DA7210_DAI_EN			(1 << 7)
 
 /*PLL_DIV3 bit fields */
+#define DA7210_PLL_DIV_L_MASK		(0xF << 0)
 #define DA7210_MCLK_RANGE_10_20_MHZ	(1 << 4)
 #define DA7210_PLL_BYP			(1 << 6)
 
@@ -162,12 +165,16 @@
 #define DA7210_PLL_FS_48000		(0xB << 0)
 #define DA7210_PLL_FS_88200		(0xE << 0)
 #define DA7210_PLL_FS_96000		(0xF << 0)
+#define DA7210_MCLK_DET_EN		(0x1 << 5)
+#define DA7210_MCLK_SRM_EN		(0x1 << 6)
 #define DA7210_PLL_EN			(0x1 << 7)
 
 /* SOFTMUTE bit fields */
 #define DA7210_RAMP_EN			(1 << 6)
 
 /* CONTROL bit fields */
+#define DA7210_REG_EN			(1 << 0)
+#define DA7210_BIAS_EN			(1 << 2)
 #define DA7210_NOISE_SUP_EN		(1 << 3)
 
 /* IN_GAIN bit fields */
@@ -206,6 +213,47 @@
 #define DA7210_OUT2_OUTMIX_L		(1 << 6)
 #define DA7210_OUT2_EN			(1 << 7)
 
+struct pll_div {
+	int fref;
+	int fout;
+	u8 div1;
+	u8 div2;
+	u8 div3;
+	u8 mode;	/* 0 = slave, 1 = master */
+};
+
+/* PLL dividers table */
+static const struct pll_div da7210_pll_div[] = {
+	/* for MASTER mode, fs = 44.1Khz */
+	{ 12000000, 2822400, 0xE8, 0x6C, 0x2, 1},	/* MCLK=12Mhz */
+	{ 13000000, 2822400, 0xDF, 0x28, 0xC, 1},	/* MCLK=13Mhz */
+	{ 13500000, 2822400, 0xDB, 0x0A, 0xD, 1},	/* MCLK=13.5Mhz */
+	{ 14400000, 2822400, 0xD4, 0x5A, 0x2, 1},	/* MCLK=14.4Mhz */
+	{ 19200000, 2822400, 0xBB, 0x43, 0x9, 1},	/* MCLK=19.2Mhz */
+	{ 19680000, 2822400, 0xB9, 0x6D, 0xA, 1},	/* MCLK=19.68Mhz */
+	{ 19800000, 2822400, 0xB8, 0xFB, 0xB, 1},	/* MCLK=19.8Mhz */
+	/* for MASTER mode, fs = 48Khz */
+	{ 12000000, 3072000, 0xF3, 0x12, 0x7, 1},	/* MCLK=12Mhz */
+	{ 13000000, 3072000, 0xE8, 0xFD, 0x5, 1},	/* MCLK=13Mhz */
+	{ 13500000, 3072000, 0xE4, 0x82, 0x3, 1},	/* MCLK=13.5Mhz */
+	{ 14400000, 3072000, 0xDD, 0x3A, 0x0, 1},	/* MCLK=14.4Mhz */
+	{ 19200000, 3072000, 0xC1, 0xEB, 0x8, 1},	/* MCLK=19.2Mhz */
+	{ 19680000, 3072000, 0xBF, 0xEC, 0x0, 1},	/* MCLK=19.68Mhz */
+	{ 19800000, 3072000, 0xBF, 0x70, 0x0, 1},	/* MCLK=19.8Mhz */
+	/* for SLAVE mode with SRM */
+	{ 12000000, 2822400, 0xED, 0xBF, 0x5, 0},	/* MCLK=12Mhz */
+	{ 13000000, 2822400, 0xE4, 0x13, 0x0, 0},	/* MCLK=13Mhz */
+	{ 13500000, 2822400, 0xDF, 0xC6, 0x8, 0},	/* MCLK=13.5Mhz */
+	{ 14400000, 2822400, 0xD8, 0xCA, 0x1, 0},	/* MCLK=14.4Mhz */
+	{ 19200000, 2822400, 0xBE, 0x97, 0x9, 0},	/* MCLK=19.2Mhz */
+	{ 19680000, 2822400, 0xBC, 0xAC, 0xD, 0},	/* MCLK=19.68Mhz */
+	{ 19800000, 2822400, 0xBC, 0x35, 0xE, 0},	/* MCLK=19.8Mhz  */
+};
+
+enum clk_src {
+	DA7210_CLKSRC_MCLK
+};
+
 #define DA7210_VERSION "0.0.1"
 
 /*
@@ -628,9 +676,12 @@
 /* Codec private data */
 struct da7210_priv {
 	struct regmap *regmap;
+	unsigned int mclk_rate;
+	int master;
 };
 
 static struct reg_default da7210_reg_defaults[] = {
+	{ 0x00, 0x00 },
 	{ 0x01, 0x11 },
 	{ 0x03, 0x00 },
 	{ 0x04, 0x00 },
@@ -713,10 +764,10 @@
 			    struct snd_pcm_hw_params *params,
 			    struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
+	struct da7210_priv *da7210 = snd_soc_codec_get_drvdata(codec);
 	u32 dai_cfg1;
-	u32 fs, bypass;
+	u32 fs, sysclk;
 
 	/* set DAI source to Left and Right ADC */
 	snd_soc_write(codec, DA7210_DAI_SRC_SEL,
@@ -749,43 +800,43 @@
 	switch (params_rate(params)) {
 	case 8000:
 		fs		= DA7210_PLL_FS_8000;
-		bypass		= DA7210_PLL_BYP;
+		sysclk		= 3072000;
 		break;
 	case 11025:
 		fs		= DA7210_PLL_FS_11025;
-		bypass		= 0;
+		sysclk		= 2822400;
 		break;
 	case 12000:
 		fs		= DA7210_PLL_FS_12000;
-		bypass		= DA7210_PLL_BYP;
+		sysclk		= 3072000;
 		break;
 	case 16000:
 		fs		= DA7210_PLL_FS_16000;
-		bypass		= DA7210_PLL_BYP;
+		sysclk		= 3072000;
 		break;
 	case 22050:
 		fs		= DA7210_PLL_FS_22050;
-		bypass		= 0;
+		sysclk		= 2822400;
 		break;
 	case 32000:
 		fs		= DA7210_PLL_FS_32000;
-		bypass		= DA7210_PLL_BYP;
+		sysclk		= 3072000;
 		break;
 	case 44100:
 		fs		= DA7210_PLL_FS_44100;
-		bypass		= 0;
+		sysclk		= 2822400;
 		break;
 	case 48000:
 		fs		= DA7210_PLL_FS_48000;
-		bypass		= DA7210_PLL_BYP;
+		sysclk		= 3072000;
 		break;
 	case 88200:
 		fs		= DA7210_PLL_FS_88200;
-		bypass		= 0;
+		sysclk		= 2822400;
 		break;
 	case 96000:
 		fs		= DA7210_PLL_FS_96000;
-		bypass		= DA7210_PLL_BYP;
+		sysclk		= 3072000;
 		break;
 	default:
 		return -EINVAL;
@@ -795,8 +846,26 @@
 	snd_soc_update_bits(codec, DA7210_STARTUP1, DA7210_SC_MST_EN, 0);
 
 	snd_soc_update_bits(codec, DA7210_PLL, DA7210_PLL_FS_MASK, fs);
-	snd_soc_update_bits(codec, DA7210_PLL_DIV3, DA7210_PLL_BYP, bypass);
 
+	if (da7210->mclk_rate && (da7210->mclk_rate != sysclk)) {
+		/* PLL mode, disable PLL bypass */
+		snd_soc_update_bits(codec, DA7210_PLL_DIV3, DA7210_PLL_BYP, 0);
+
+		if (!da7210->master) {
+			/* PLL slave mode, also enable SRM */
+			snd_soc_update_bits(codec, DA7210_PLL,
+						   (DA7210_MCLK_SRM_EN |
+						    DA7210_MCLK_DET_EN),
+						   (DA7210_MCLK_SRM_EN |
+						    DA7210_MCLK_DET_EN));
+		}
+	} else {
+		/* PLL bypass mode, enable PLL bypass and Auto Detection */
+		snd_soc_update_bits(codec, DA7210_PLL, DA7210_MCLK_DET_EN,
+						       DA7210_MCLK_DET_EN);
+		snd_soc_update_bits(codec, DA7210_PLL_DIV3, DA7210_PLL_BYP,
+							    DA7210_PLL_BYP);
+	}
 	/* Enable active mode */
 	snd_soc_update_bits(codec, DA7210_STARTUP1,
 			    DA7210_SC_MST_EN, DA7210_SC_MST_EN);
@@ -810,17 +879,24 @@
 static int da7210_set_dai_fmt(struct snd_soc_dai *codec_dai, u32 fmt)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
+	struct da7210_priv *da7210 = snd_soc_codec_get_drvdata(codec);
 	u32 dai_cfg1;
 	u32 dai_cfg3;
 
 	dai_cfg1 = 0x7f & snd_soc_read(codec, DA7210_DAI_CFG1);
 	dai_cfg3 = 0xfc & snd_soc_read(codec, DA7210_DAI_CFG3);
 
+	if ((snd_soc_read(codec, DA7210_PLL) & DA7210_PLL_EN) &&
+		(!(snd_soc_read(codec, DA7210_PLL_DIV3) & DA7210_PLL_BYP)))
+		return -EINVAL;
+
 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
 	case SND_SOC_DAIFMT_CBM_CFM:
+		da7210->master = 1;
 		dai_cfg1 |= DA7210_DAI_MODE_MASTER;
 		break;
 	case SND_SOC_DAIFMT_CBS_CFS:
+		da7210->master = 0;
 		dai_cfg1 |= DA7210_DAI_MODE_SLAVE;
 		break;
 	default:
@@ -872,10 +948,101 @@
 #define DA7210_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
 			SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
 
+static int da7210_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+				 int clk_id, unsigned int freq, int dir)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct da7210_priv *da7210 = snd_soc_codec_get_drvdata(codec);
+
+	switch (clk_id) {
+	case DA7210_CLKSRC_MCLK:
+		switch (freq) {
+		case 12000000:
+		case 13000000:
+		case 13500000:
+		case 14400000:
+		case 19200000:
+		case 19680000:
+		case 19800000:
+			da7210->mclk_rate = freq;
+			return 0;
+		default:
+			dev_err(codec_dai->dev, "Unsupported MCLK value %d\n",
+				freq);
+			return -EINVAL;
+		}
+		break;
+	default:
+		dev_err(codec_dai->dev, "Unknown clock source %d\n", clk_id);
+		return -EINVAL;
+	}
+}
+
+/**
+ * da7210_set_dai_pll	:Configure the codec PLL
+ * @param codec_dai	: pointer to codec DAI
+ * @param pll_id	: da7210 has only one pll, so pll_id is always zero
+ * @param fref		: MCLK frequency, should be < 20MHz
+ * @param fout		: FsDM value, Refer page 44 & 45 of datasheet
+ * @return int		: Zero for success, negative error code for error
+ *
+ * Note: Supported PLL input frequencies are 12MHz, 13MHz, 13.5MHz, 14.4MHz,
+ *       19.2MHz, 19.6MHz and 19.8MHz
+ */
+static int da7210_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+			      int source, unsigned int fref, unsigned int fout)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct da7210_priv *da7210 = snd_soc_codec_get_drvdata(codec);
+
+	u8 pll_div1, pll_div2, pll_div3, cnt;
+
+	/* In slave mode, there is only one set of divisors */
+	if (!da7210->master)
+		fout = 2822400;
+
+	/* Search pll div array for correct divisors */
+	for (cnt = 0; cnt < ARRAY_SIZE(da7210_pll_div); cnt++) {
+		/* check fref, mode  and fout */
+		if ((fref == da7210_pll_div[cnt].fref) &&
+		    (da7210->master ==  da7210_pll_div[cnt].mode) &&
+		    (fout == da7210_pll_div[cnt].fout)) {
+			/* all match, pick up divisors */
+			pll_div1 = da7210_pll_div[cnt].div1;
+			pll_div2 = da7210_pll_div[cnt].div2;
+			pll_div3 = da7210_pll_div[cnt].div3;
+			break;
+		}
+	}
+	if (cnt >= ARRAY_SIZE(da7210_pll_div))
+		goto err;
+
+	/* Disable active mode */
+	snd_soc_update_bits(codec, DA7210_STARTUP1, DA7210_SC_MST_EN, 0);
+	/* Write PLL dividers */
+	snd_soc_write(codec, DA7210_PLL_DIV1, pll_div1);
+	snd_soc_write(codec, DA7210_PLL_DIV2, pll_div2);
+	snd_soc_update_bits(codec, DA7210_PLL_DIV3,
+				   DA7210_PLL_DIV_L_MASK, pll_div3);
+
+	/* Enable PLL */
+	snd_soc_update_bits(codec, DA7210_PLL, DA7210_PLL_EN, DA7210_PLL_EN);
+
+	/* Enable active mode */
+	snd_soc_update_bits(codec, DA7210_STARTUP1, DA7210_SC_MST_EN,
+						    DA7210_SC_MST_EN);
+	return 0;
+err:
+	dev_err(codec_dai->dev, "Unsupported PLL input frequency %d\n", fref);
+	return -EINVAL;
+}
+
 /* DAI operations */
 static const struct snd_soc_dai_ops da7210_dai_ops = {
 	.hw_params	= da7210_hw_params,
 	.set_fmt	= da7210_set_dai_fmt,
+	.set_sysclk	= da7210_set_dai_sysclk,
+	.set_pll	= da7210_set_dai_pll,
 	.digital_mute	= da7210_mute,
 };
 
@@ -915,24 +1082,11 @@
 		return ret;
 	}
 
-	/* FIXME
-	 *
-	 * This driver use fixed value here
-	 * And below settings expects MCLK = 12.288MHz
-	 *
-	 * When you select different MCLK, please check...
-	 *      DA7210_PLL_DIV1 val
-	 *      DA7210_PLL_DIV2 val
-	 *      DA7210_PLL_DIV3 val
-	 *      DA7210_PLL_DIV3 :: DA7210_MCLK_RANGExxx
-	 */
+	da7210->mclk_rate       = 0;    /* This will be set from set_sysclk() */
+	da7210->master          = 0;    /* This will be set from set_fmt() */
 
-	/*
-	 * make sure that DA7210 use bypass mode before start up
-	 */
-	snd_soc_write(codec, DA7210_STARTUP1, 0);
-	snd_soc_write(codec, DA7210_PLL_DIV3,
-		     DA7210_MCLK_RANGE_10_20_MHZ | DA7210_PLL_BYP);
+	/* Enable internal regulator & bias current */
+	snd_soc_write(codec, DA7210_CONTROL, DA7210_REG_EN | DA7210_BIAS_EN);
 
 	/*
 	 * ADC settings
@@ -1007,34 +1161,13 @@
 	/* Enable Aux2 */
 	snd_soc_write(codec, DA7210_AUX2, DA7210_AUX2_EN);
 
+	/* Set PLL Master clock range 10-20 MHz, enable PLL bypass */
+	snd_soc_write(codec, DA7210_PLL_DIV3, DA7210_MCLK_RANGE_10_20_MHZ |
+					      DA7210_PLL_BYP);
+
 	/* Diable PLL and bypass it */
 	snd_soc_write(codec, DA7210_PLL, DA7210_PLL_FS_48000);
 
-	/*
-	 * If 48kHz sound came, it use bypass mode,
-	 * and when it is 44.1kHz, it use PLL.
-	 *
-	 * This time, this driver sets PLL always ON
-	 * and controls bypass/PLL mode by switching
-	 * DA7210_PLL_DIV3 :: DA7210_PLL_BYP bit.
-	 *   see da7210_hw_params
-	 */
-	snd_soc_write(codec, DA7210_PLL_DIV1, 0xE5); /* MCLK = 12.288MHz */
-	snd_soc_write(codec, DA7210_PLL_DIV2, 0x99);
-	snd_soc_write(codec, DA7210_PLL_DIV3, 0x0A |
-		     DA7210_MCLK_RANGE_10_20_MHZ | DA7210_PLL_BYP);
-	snd_soc_update_bits(codec, DA7210_PLL, DA7210_PLL_EN, DA7210_PLL_EN);
-
-	/* As suggested by Dialog */
-	/* unlock */
-	regmap_write(da7210->regmap, DA7210_A_HID_UNLOCK,	0x8B);
-	regmap_write(da7210->regmap, DA7210_A_TEST_UNLOCK,	0xB4);
-	regmap_write(da7210->regmap, DA7210_A_PLL1,		0x01);
-	regmap_write(da7210->regmap, DA7210_A_CP_MODE,		0x7C);
-	/* re-lock */
-	regmap_write(da7210->regmap, DA7210_A_HID_UNLOCK,	0x00);
-	regmap_write(da7210->regmap, DA7210_A_TEST_UNLOCK,	0x00);
-
 	/* Activate all enabled subsystem */
 	snd_soc_write(codec, DA7210_STARTUP1, DA7210_SC_MST_EN);
 
@@ -1055,7 +1188,26 @@
 	.num_dapm_routes	= ARRAY_SIZE(da7210_audio_map),
 };
 
-static struct regmap_config da7210_regmap = {
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+
+static struct reg_default da7210_regmap_i2c_patch[] = {
+
+	/* System controller master disable */
+	{ DA7210_STARTUP1, 0x00 },
+	/* Set PLL Master clock range 10-20 MHz */
+	{ DA7210_PLL_DIV3, DA7210_MCLK_RANGE_10_20_MHZ },
+
+	/* to unlock */
+	{ DA7210_A_HID_UNLOCK, 0x8B},
+	{ DA7210_A_TEST_UNLOCK, 0xB4},
+	{ DA7210_A_PLL1, 0x01},
+	{ DA7210_A_CP_MODE, 0x7C},
+	/* to re-lock */
+	{ DA7210_A_HID_UNLOCK, 0x00},
+	{ DA7210_A_TEST_UNLOCK, 0x00},
+};
+
+static const struct regmap_config da7210_regmap_config_i2c = {
 	.reg_bits = 8,
 	.val_bits = 8,
 
@@ -1066,7 +1218,6 @@
 	.cache_type = REGCACHE_RBTREE,
 };
 
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
 static int __devinit da7210_i2c_probe(struct i2c_client *i2c,
 			   	      const struct i2c_device_id *id)
 {
@@ -1080,13 +1231,18 @@
 
 	i2c_set_clientdata(i2c, da7210);
 
-	da7210->regmap = regmap_init_i2c(i2c, &da7210_regmap);
+	da7210->regmap = regmap_init_i2c(i2c, &da7210_regmap_config_i2c);
 	if (IS_ERR(da7210->regmap)) {
 		ret = PTR_ERR(da7210->regmap);
 		dev_err(&i2c->dev, "regmap_init() failed: %d\n", ret);
 		return ret;
 	}
 
+	ret = regmap_register_patch(da7210->regmap, da7210_regmap_i2c_patch,
+				    ARRAY_SIZE(da7210_regmap_i2c_patch));
+	if (ret != 0)
+		dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
+
 	ret =  snd_soc_register_codec(&i2c->dev,
 			&soc_codec_dev_da7210, &da7210_dai, 1);
 	if (ret < 0) {
@@ -1119,7 +1275,7 @@
 /* I2C codec control layer */
 static struct i2c_driver da7210_i2c_driver = {
 	.driver = {
-		.name = "da7210-codec",
+		.name = "da7210",
 		.owner = THIS_MODULE,
 	},
 	.probe		= da7210_i2c_probe,
@@ -1128,12 +1284,112 @@
 };
 #endif
 
+#if defined(CONFIG_SPI_MASTER)
+
+static struct reg_default da7210_regmap_spi_patch[] = {
+	/* Dummy read to give two pulses over nCS for SPI */
+	{ DA7210_AUX2, 0x00 },
+	{ DA7210_AUX2, 0x00 },
+
+	/* System controller master disable */
+	{ DA7210_STARTUP1, 0x00 },
+	/* Set PLL Master clock range 10-20 MHz */
+	{ DA7210_PLL_DIV3, DA7210_MCLK_RANGE_10_20_MHZ },
+
+	/* to set PAGE1 of SPI register space */
+	{ DA7210_PAGE_CONTROL, 0x80 },
+	/* to unlock */
+	{ DA7210_A_HID_UNLOCK, 0x8B},
+	{ DA7210_A_TEST_UNLOCK, 0xB4},
+	{ DA7210_A_PLL1, 0x01},
+	{ DA7210_A_CP_MODE, 0x7C},
+	/* to re-lock */
+	{ DA7210_A_HID_UNLOCK, 0x00},
+	{ DA7210_A_TEST_UNLOCK, 0x00},
+	/* to set back PAGE0 of SPI register space */
+	{ DA7210_PAGE_CONTROL, 0x00 },
+};
+
+static const struct regmap_config da7210_regmap_config_spi = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.read_flag_mask = 0x01,
+	.write_flag_mask = 0x00,
+
+	.reg_defaults = da7210_reg_defaults,
+	.num_reg_defaults = ARRAY_SIZE(da7210_reg_defaults),
+	.volatile_reg = da7210_volatile_register,
+	.readable_reg = da7210_readable_register,
+	.cache_type = REGCACHE_RBTREE,
+};
+
+static int __devinit da7210_spi_probe(struct spi_device *spi)
+{
+	struct da7210_priv *da7210;
+	int ret;
+
+	da7210 = devm_kzalloc(&spi->dev, sizeof(struct da7210_priv),
+			      GFP_KERNEL);
+	if (!da7210)
+		return -ENOMEM;
+
+	spi_set_drvdata(spi, da7210);
+	da7210->regmap = devm_regmap_init_spi(spi, &da7210_regmap_config_spi);
+	if (IS_ERR(da7210->regmap)) {
+		ret = PTR_ERR(da7210->regmap);
+		dev_err(&spi->dev, "Failed to register regmap: %d\n", ret);
+		return ret;
+	}
+
+	ret = regmap_register_patch(da7210->regmap, da7210_regmap_spi_patch,
+				    ARRAY_SIZE(da7210_regmap_spi_patch));
+	if (ret != 0)
+		dev_warn(&spi->dev, "Failed to apply regmap patch: %d\n", ret);
+
+	ret =  snd_soc_register_codec(&spi->dev,
+			&soc_codec_dev_da7210, &da7210_dai, 1);
+	if (ret < 0)
+		goto err_regmap;
+
+	return ret;
+
+err_regmap:
+	regmap_exit(da7210->regmap);
+
+	return ret;
+}
+
+static int __devexit da7210_spi_remove(struct spi_device *spi)
+{
+	struct da7210_priv *da7210 = spi_get_drvdata(spi);
+	snd_soc_unregister_codec(&spi->dev);
+	regmap_exit(da7210->regmap);
+	return 0;
+}
+
+static struct spi_driver da7210_spi_driver = {
+	.driver = {
+		.name = "da7210",
+		.owner = THIS_MODULE,
+	},
+	.probe = da7210_spi_probe,
+	.remove = __devexit_p(da7210_spi_remove)
+};
+#endif
+
 static int __init da7210_modinit(void)
 {
 	int ret = 0;
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
 	ret = i2c_add_driver(&da7210_i2c_driver);
 #endif
+#if defined(CONFIG_SPI_MASTER)
+	ret = spi_register_driver(&da7210_spi_driver);
+	if (ret) {
+		printk(KERN_ERR "Failed to register da7210 SPI driver: %d\n",
+		       ret);
+	}
+#endif
 	return ret;
 }
 module_init(da7210_modinit);
@@ -1143,6 +1399,9 @@
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
 	i2c_del_driver(&da7210_i2c_driver);
 #endif
+#if defined(CONFIG_SPI_MASTER)
+	spi_unregister_driver(&da7210_spi_driver);
+#endif
 }
 module_exit(da7210_exit);
 
diff --git a/sound/soc/codecs/jz4740.c b/sound/soc/codecs/jz4740.c
index 4624e75..85d9cab 100644
--- a/sound/soc/codecs/jz4740.c
+++ b/sound/soc/codecs/jz4740.c
@@ -164,8 +164,7 @@
 	struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
 {
 	uint32_t val;
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec =rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 
 	switch (params_rate(params)) {
 	case 8000:
diff --git a/sound/soc/codecs/lm49453.c b/sound/soc/codecs/lm49453.c
new file mode 100644
index 0000000..802b9f1
--- /dev/null
+++ b/sound/soc/codecs/lm49453.c
@@ -0,0 +1,1550 @@
+/*
+ * lm49453.c  -  LM49453 ALSA Soc Audio driver
+ *
+ * Copyright (c) 2012 Texas Instruments, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * Initially based on sound/soc/codecs/wm8350.c
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <sound/jack.h>
+#include <sound/initval.h>
+#include <asm/div64.h>
+#include "lm49453.h"
+
+static struct reg_default lm49453_reg_defs[] = {
+	{ 0, 0x00 },
+	{ 1, 0x00 },
+	{ 2, 0x00 },
+	{ 3, 0x00 },
+	{ 4, 0x00 },
+	{ 5, 0x00 },
+	{ 6, 0x00 },
+	{ 7, 0x00 },
+	{ 8, 0x00 },
+	{ 9, 0x00 },
+	{ 10, 0x00 },
+	{ 11, 0x00 },
+	{ 12, 0x00 },
+	{ 13, 0x00 },
+	{ 14, 0x00 },
+	{ 15, 0x00 },
+	{ 16, 0x00 },
+	{ 17, 0x00 },
+	{ 18, 0x00 },
+	{ 19, 0x00 },
+	{ 20, 0x00 },
+	{ 21, 0x00 },
+	{ 22, 0x00 },
+	{ 23, 0x00 },
+	{ 32, 0x00 },
+	{ 33, 0x00 },
+	{ 35, 0x00 },
+	{ 36, 0x00 },
+	{ 37, 0x00 },
+	{ 46, 0x00 },
+	{ 48, 0x00 },
+	{ 49, 0x00 },
+	{ 51, 0x00 },
+	{ 56, 0x00 },
+	{ 58, 0x00 },
+	{ 59, 0x00 },
+	{ 60, 0x00 },
+	{ 61, 0x00 },
+	{ 62, 0x00 },
+	{ 63, 0x00 },
+	{ 64, 0x00 },
+	{ 65, 0x00 },
+	{ 66, 0x00 },
+	{ 67, 0x00 },
+	{ 68, 0x00 },
+	{ 69, 0x00 },
+	{ 70, 0x00 },
+	{ 71, 0x00 },
+	{ 72, 0x00 },
+	{ 73, 0x00 },
+	{ 74, 0x00 },
+	{ 75, 0x00 },
+	{ 76, 0x00 },
+	{ 77, 0x00 },
+	{ 78, 0x00 },
+	{ 79, 0x00 },
+	{ 80, 0x00 },
+	{ 81, 0x00 },
+	{ 82, 0x00 },
+	{ 83, 0x00 },
+	{ 85, 0x00 },
+	{ 85, 0x00 },
+	{ 86, 0x00 },
+	{ 87, 0x00 },
+	{ 88, 0x00 },
+	{ 89, 0x00 },
+	{ 90, 0x00 },
+	{ 91, 0x00 },
+	{ 92, 0x00 },
+	{ 93, 0x00 },
+	{ 94, 0x00 },
+	{ 95, 0x00 },
+	{ 96, 0x01 },
+	{ 97, 0x00 },
+	{ 98, 0x00 },
+	{ 99, 0x00 },
+	{ 100, 0x00 },
+	{ 101, 0x00 },
+	{ 102, 0x00 },
+	{ 103, 0x01 },
+	{ 105, 0x01 },
+	{ 106, 0x00 },
+	{ 107, 0x01 },
+	{ 107, 0x00 },
+	{ 108, 0x00 },
+	{ 109, 0x00 },
+	{ 110, 0x00 },
+	{ 111, 0x02 },
+	{ 112, 0x02 },
+	{ 113, 0x00 },
+	{ 121, 0x80 },
+	{ 122, 0xBB },
+	{ 123, 0x80 },
+	{ 124, 0xBB },
+	{ 128, 0x00 },
+	{ 130, 0x00 },
+	{ 131, 0x00 },
+	{ 132, 0x00 },
+	{ 133, 0x0A },
+	{ 134, 0x0A },
+	{ 135, 0x0A },
+	{ 136, 0x0F },
+	{ 137, 0x00 },
+	{ 138, 0x73 },
+	{ 139, 0x33 },
+	{ 140, 0x73 },
+	{ 141, 0x33 },
+	{ 142, 0x73 },
+	{ 143, 0x33 },
+	{ 144, 0x73 },
+	{ 145, 0x33 },
+	{ 146, 0x73 },
+	{ 147, 0x33 },
+	{ 148, 0x73 },
+	{ 149, 0x33 },
+	{ 150, 0x73 },
+	{ 151, 0x33 },
+	{ 152, 0x00 },
+	{ 153, 0x00 },
+	{ 154, 0x00 },
+	{ 155, 0x00 },
+	{ 176, 0x00 },
+	{ 177, 0x00 },
+	{ 178, 0x00 },
+	{ 179, 0x00 },
+	{ 180, 0x00 },
+	{ 181, 0x00 },
+	{ 182, 0x00 },
+	{ 183, 0x00 },
+	{ 184, 0x00 },
+	{ 185, 0x00 },
+	{ 186, 0x00 },
+	{ 189, 0x00 },
+	{ 188, 0x00 },
+	{ 194, 0x00 },
+	{ 195, 0x00 },
+	{ 196, 0x00 },
+	{ 197, 0x00 },
+	{ 200, 0x00 },
+	{ 201, 0x00 },
+	{ 202, 0x00 },
+	{ 203, 0x00 },
+	{ 204, 0x00 },
+	{ 205, 0x00 },
+	{ 208, 0x00 },
+	{ 209, 0x00 },
+	{ 210, 0x00 },
+	{ 211, 0x00 },
+	{ 213, 0x00 },
+	{ 214, 0x00 },
+	{ 215, 0x00 },
+	{ 216, 0x00 },
+	{ 217, 0x00 },
+	{ 218, 0x00 },
+	{ 219, 0x00 },
+	{ 221, 0x00 },
+	{ 222, 0x00 },
+	{ 224, 0x00 },
+	{ 225, 0x00 },
+	{ 226, 0x00 },
+	{ 227, 0x00 },
+	{ 228, 0x00 },
+	{ 229, 0x00 },
+	{ 230, 0x13 },
+	{ 231, 0x00 },
+	{ 232, 0x80 },
+	{ 233, 0x0C },
+	{ 234, 0xDD },
+	{ 235, 0x00 },
+	{ 236, 0x04 },
+	{ 237, 0x00 },
+	{ 238, 0x00 },
+	{ 239, 0x00 },
+	{ 240, 0x00 },
+	{ 241, 0x00 },
+	{ 242, 0x00 },
+	{ 243, 0x00 },
+	{ 244, 0x00 },
+	{ 245, 0x00 },
+	{ 248, 0x00 },
+	{ 249, 0x00 },
+	{ 254, 0x00 },
+	{ 255, 0x00 },
+};
+
+/* codec private data */
+struct lm49453_priv {
+	struct regmap *regmap;
+	int fs_rate;
+};
+
+/* capture path controls */
+
+static const char *lm49453_mic2mode_text[] = {"Single Ended", "Differential"};
+
+static const SOC_ENUM_SINGLE_DECL(lm49453_mic2mode_enum, LM49453_P0_MICR_REG, 5,
+				  lm49453_mic2mode_text);
+
+static const char *lm49453_dmic_cfg_text[] = {"DMICDAT1", "DMICDAT2"};
+
+static const SOC_ENUM_SINGLE_DECL(lm49453_dmic12_cfg_enum,
+				  LM49453_P0_DIGITAL_MIC1_CONFIG_REG,
+				  7, lm49453_dmic_cfg_text);
+
+static const SOC_ENUM_SINGLE_DECL(lm49453_dmic34_cfg_enum,
+				  LM49453_P0_DIGITAL_MIC2_CONFIG_REG,
+				  7, lm49453_dmic_cfg_text);
+
+/* MUX Controls */
+static const char *lm49453_adcl_mux_text[] = { "MIC1", "Aux_L" };
+
+static const char *lm49453_adcr_mux_text[] = { "MIC2", "Aux_R" };
+
+static const struct soc_enum lm49453_adcl_enum =
+	SOC_ENUM_SINGLE(LM49453_P0_ANALOG_MIXER_ADC_REG, 0,
+			ARRAY_SIZE(lm49453_adcl_mux_text),
+			lm49453_adcl_mux_text);
+
+static const struct soc_enum lm49453_adcr_enum =
+	SOC_ENUM_SINGLE(LM49453_P0_ANALOG_MIXER_ADC_REG, 1,
+			ARRAY_SIZE(lm49453_adcr_mux_text),
+			lm49453_adcr_mux_text);
+
+static const struct snd_kcontrol_new lm49453_adcl_mux_control =
+	SOC_DAPM_ENUM("ADC Left Mux", lm49453_adcl_enum);
+
+static const struct snd_kcontrol_new lm49453_adcr_mux_control =
+	SOC_DAPM_ENUM("ADC Right Mux", lm49453_adcr_enum);
+
+static const struct snd_kcontrol_new lm49453_headset_left_mixer[] = {
+SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_DACHPL1_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_DACHPL1_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_DACHPL1_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_DACHPL1_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_DACHPL1_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_DACHPL1_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_DACHPL1_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_DACHPL1_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_DACHPL2_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_DACHPL2_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_DACHPL2_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_DACHPL2_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_DACHPL2_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_DACHPL2_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_DACHPL2_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_DACHPL2_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("Sidetone Switch", LM49453_P0_STN_SEL_REG, 0, 0, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_headset_right_mixer[] = {
+SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_DACHPR1_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_DACHPR1_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_DACHPR1_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_DACHPR1_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_DACHPR1_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_DACHPR1_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_DACHPR1_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_DACHPR1_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_DACHPR2_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_DACHPR2_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_DACHPR2_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_DACHPR2_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_DACHPR2_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_DACHPR2_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_DACHPR2_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_DACHPR2_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("Sidetone Switch", LM49453_P0_STN_SEL_REG, 1, 0, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_speaker_left_mixer[] = {
+SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_DACLSL1_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_DACLSL1_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_DACLSL1_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_DACLSL1_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_DACLSL1_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_DACLSL1_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_DACLSL1_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_DACLSL1_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_DACLSL2_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_DACLSL2_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_DACLSL2_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_DACLSL2_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_DACLSL2_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_DACLSL2_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_DACLSL2_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_DACLSL2_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("Sidetone Switch", LM49453_P0_STN_SEL_REG, 2, 0, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_speaker_right_mixer[] = {
+SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_DACLSR1_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_DACLSR1_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_DACLSR1_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_DACLSR1_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_DACLSR1_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_DACLSR1_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_DACLSR1_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_DACLSR1_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_DACLSR2_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_DACLSR2_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_DACLSR2_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_DACLSR2_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_DACLSR2_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_DACLSR2_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_DACLSR2_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_DACLSR2_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("Sidetone Switch", LM49453_P0_STN_SEL_REG, 3, 0, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_haptic_left_mixer[] = {
+SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_DACHAL1_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_DACHAL1_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_DACHAL1_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_DACHAL1_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_DACHAL1_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_DACHAL1_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_DACHAL1_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_DACHAL1_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_DACHAL2_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_DACHAL2_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_DACHAL2_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_DACHAL2_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_DACHAL2_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_DACHAL2_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_DACHAL2_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_DACHAL2_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("Sidetone Switch", LM49453_P0_STN_SEL_REG, 4, 0, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_haptic_right_mixer[] = {
+SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_DACHAR1_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_DACHAR1_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_DACHAR1_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_DACHAR1_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_DACHAR1_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_DACHAR1_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_DACHAR1_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_DACHAR1_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_DACHAR2_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_DACHAR2_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_DACHAR2_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_DACHAR2_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_DACHAR2_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_DACHAR2_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_DACHAR2_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_DACHAR2_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("Sidetone Switch", LM49453_P0_STN_SEL_REG, 5, 0, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_lineout_left_mixer[] = {
+SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_DACLOL1_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_DACLOL1_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_DACLOL1_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_DACLOL1_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_DACLOL1_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_DACLOL1_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_DACLOL1_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_DACLOL1_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_DACLOL2_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_DACLOL2_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_DACLOL2_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_DACLOL2_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_DACLOL2_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_DACLOL2_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_DACLOL2_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_DACLOL2_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("Sidetone Switch", LM49453_P0_STN_SEL_REG, 6, 0, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_lineout_right_mixer[] = {
+SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_DACLOR1_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_DACLOR1_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_DACLOR1_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_DACLOR1_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_DACLOR1_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_DACLOR1_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_DACLOR1_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_DACLOR1_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_DACLOR2_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_DACLOR2_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_DACLOR2_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_DACLOR2_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_DACLOR2_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_DACLOR2_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_DACLOR2_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_DACLOR2_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("Sidetone Switch", LM49453_P0_STN_SEL_REG, 7, 0, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_port1_tx1_mixer[] = {
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT1_TX1_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT1_TX1_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT1_TX1_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT1_TX1_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT1_TX1_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT1_TX1_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_PORT1_TX1_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_PORT1_TX1_REG, 7, 1, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_port1_tx2_mixer[] = {
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT1_TX2_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT1_TX2_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT1_TX2_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT1_TX2_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT1_TX2_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT1_TX2_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_PORT1_TX2_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_PORT1_TX2_REG, 7, 1, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_port1_tx3_mixer[] = {
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT1_TX3_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT1_TX3_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT1_TX3_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT1_TX3_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT1_TX3_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT1_TX3_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_PORT1_TX3_REG, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_port1_tx4_mixer[] = {
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT1_TX4_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT1_TX4_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT1_TX4_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT1_TX4_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT1_TX4_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT1_TX4_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_PORT1_TX4_REG, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_port1_tx5_mixer[] = {
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT1_TX5_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT1_TX5_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT1_TX5_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT1_TX5_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT1_TX5_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT1_TX5_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_PORT1_TX5_REG, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_port1_tx6_mixer[] = {
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT1_TX6_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT1_TX6_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT1_TX6_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT1_TX6_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT1_TX6_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT1_TX6_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_PORT1_TX6_REG, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_port1_tx7_mixer[] = {
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT1_TX7_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT1_TX7_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT1_TX7_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT1_TX7_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT1_TX7_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT1_TX7_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_PORT1_TX7_REG, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_port1_tx8_mixer[] = {
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT1_TX8_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT1_TX8_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT1_TX8_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT1_TX8_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT1_TX8_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT1_TX8_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_PORT1_TX8_REG, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_port2_tx1_mixer[] = {
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT2_TX1_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT2_TX1_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT2_TX1_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT2_TX1_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT2_TX1_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT2_TX1_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_PORT2_TX1_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_PORT2_TX1_REG, 7, 1, 0),
+};
+
+static const struct snd_kcontrol_new lm49453_port2_tx2_mixer[] = {
+SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT2_TX2_REG, 0, 1, 0),
+SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT2_TX2_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT2_TX2_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT2_TX2_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT2_TX2_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT2_TX2_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_PORT2_TX2_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_PORT2_TX2_REG, 7, 1, 0),
+};
+
+/* TLV Declarations */
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -7650, 150, 1);
+static const DECLARE_TLV_DB_SCALE(port_tlv, 0, 600, 0);
+
+static const struct snd_kcontrol_new lm49453_sidetone_mixer_controls[] = {
+/* Sidetone supports mono only */
+SOC_DAPM_SINGLE_TLV("Sidetone ADCL Volume", LM49453_P0_STN_VOL_ADCL_REG,
+		     0, 0x3F, 0, digital_tlv),
+SOC_DAPM_SINGLE_TLV("Sidetone ADCR Volume", LM49453_P0_STN_VOL_ADCR_REG,
+		     0, 0x3F, 0, digital_tlv),
+SOC_DAPM_SINGLE_TLV("Sidetone DMIC1L Volume", LM49453_P0_STN_VOL_DMIC1L_REG,
+		     0, 0x3F, 0, digital_tlv),
+SOC_DAPM_SINGLE_TLV("Sidetone DMIC1R Volume", LM49453_P0_STN_VOL_DMIC1R_REG,
+		     0, 0x3F, 0, digital_tlv),
+SOC_DAPM_SINGLE_TLV("Sidetone DMIC2L Volume", LM49453_P0_STN_VOL_DMIC2L_REG,
+		     0, 0x3F, 0, digital_tlv),
+SOC_DAPM_SINGLE_TLV("Sidetone DMIC2R Volume", LM49453_P0_STN_VOL_DMIC2R_REG,
+		     0, 0x3F, 0, digital_tlv),
+};
+
+static const struct snd_kcontrol_new lm49453_snd_controls[] = {
+	/* mic1 and mic2 supports mono only */
+	SOC_SINGLE_TLV("Mic1 Volume", LM49453_P0_ADC_LEVELL_REG, 0, 6,
+			0, digital_tlv),
+	SOC_SINGLE_TLV("Mic2 Volume", LM49453_P0_ADC_LEVELR_REG, 0, 6,
+			0, digital_tlv),
+
+	SOC_DOUBLE_R_TLV("DMIC1 Volume", LM49453_P0_DMIC1_LEVELL_REG,
+			  LM49453_P0_DMIC1_LEVELR_REG, 0, 6, 0, digital_tlv),
+	SOC_DOUBLE_R_TLV("DMIC2 Volume", LM49453_P0_DMIC2_LEVELL_REG,
+			  LM49453_P0_DMIC2_LEVELR_REG, 0, 6, 0, digital_tlv),
+
+	SOC_DAPM_ENUM("Mic2Mode", lm49453_mic2mode_enum),
+	SOC_DAPM_ENUM("DMIC12 SRC", lm49453_dmic12_cfg_enum),
+	SOC_DAPM_ENUM("DMIC34 SRC", lm49453_dmic34_cfg_enum),
+
+	/* Capture path filter enable */
+	SOC_SINGLE("DMIC1 HPFilter Switch", LM49453_P0_ADC_FX_ENABLES_REG,
+					    0, 1, 0),
+	SOC_SINGLE("DMIC2 HPFilter Switch", LM49453_P0_ADC_FX_ENABLES_REG,
+					    1, 1, 0),
+	SOC_SINGLE("ADC HPFilter Switch", LM49453_P0_ADC_FX_ENABLES_REG,
+					  2, 1, 0),
+
+	SOC_DOUBLE_R_TLV("DAC HP Volume", LM49453_P0_DAC_HP_LEVELL_REG,
+			  LM49453_P0_DAC_HP_LEVELR_REG, 0, 6, 0, digital_tlv),
+	SOC_DOUBLE_R_TLV("DAC LO Volume", LM49453_P0_DAC_LO_LEVELL_REG,
+			  LM49453_P0_DAC_LO_LEVELR_REG, 0, 6, 0, digital_tlv),
+	SOC_DOUBLE_R_TLV("DAC LS Volume", LM49453_P0_DAC_LS_LEVELL_REG,
+			  LM49453_P0_DAC_LS_LEVELR_REG, 0, 6, 0, digital_tlv),
+	SOC_DOUBLE_R_TLV("DAC HA Volume", LM49453_P0_DAC_HA_LEVELL_REG,
+			  LM49453_P0_DAC_HA_LEVELR_REG, 0, 6, 0, digital_tlv),
+
+	SOC_SINGLE_TLV("EP Volume", LM49453_P0_DAC_LS_LEVELL_REG,
+			0, 6, 0, digital_tlv),
+
+	SOC_SINGLE_TLV("PORT1_1_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL1_REG,
+			0, 3, 0, port_tlv),
+	SOC_SINGLE_TLV("PORT1_2_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL1_REG,
+			2, 3, 0, port_tlv),
+	SOC_SINGLE_TLV("PORT1_3_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL1_REG,
+			4, 3, 0, port_tlv),
+	SOC_SINGLE_TLV("PORT1_4_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL1_REG,
+			6, 3, 0, port_tlv),
+	SOC_SINGLE_TLV("PORT1_5_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL2_REG,
+			0, 3, 0, port_tlv),
+	SOC_SINGLE_TLV("PORT1_6_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL2_REG,
+			2, 3, 0, port_tlv),
+	SOC_SINGLE_TLV("PORT1_7_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL2_REG,
+			4, 3, 0, port_tlv),
+	SOC_SINGLE_TLV("PORT1_8_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL2_REG,
+			6, 3, 0, port_tlv),
+
+	SOC_SINGLE_TLV("PORT2_1_RX_LVL Volume", LM49453_P0_PORT2_RX_LVL_REG,
+			0, 3, 0, port_tlv),
+	SOC_SINGLE_TLV("PORT2_2_RX_LVL Volume", LM49453_P0_PORT2_RX_LVL_REG,
+			2, 3, 0, port_tlv),
+
+	SOC_SINGLE("Port1 Playback Switch", LM49453_P0_AUDIO_PORT1_BASIC_REG,
+		    1, 1, 0),
+	SOC_SINGLE("Port2 Playback Switch", LM49453_P0_AUDIO_PORT2_BASIC_REG,
+		    1, 1, 0),
+	SOC_SINGLE("Port1 Capture Switch", LM49453_P0_AUDIO_PORT1_BASIC_REG,
+		    2, 1, 0),
+	SOC_SINGLE("Port2 Capture Switch", LM49453_P0_AUDIO_PORT2_BASIC_REG,
+		    2, 1, 0)
+
+};
+
+/* DAPM widgets */
+static const struct snd_soc_dapm_widget lm49453_dapm_widgets[] = {
+
+	/* All end points HP,EP, LS, Lineout and Haptic */
+	SND_SOC_DAPM_OUTPUT("HPOUTL"),
+	SND_SOC_DAPM_OUTPUT("HPOUTR"),
+	SND_SOC_DAPM_OUTPUT("EPOUT"),
+	SND_SOC_DAPM_OUTPUT("LSOUTL"),
+	SND_SOC_DAPM_OUTPUT("LSOUTR"),
+	SND_SOC_DAPM_OUTPUT("LOOUTR"),
+	SND_SOC_DAPM_OUTPUT("LOOUTL"),
+	SND_SOC_DAPM_OUTPUT("HAOUTL"),
+	SND_SOC_DAPM_OUTPUT("HAOUTR"),
+
+	SND_SOC_DAPM_INPUT("AMIC1"),
+	SND_SOC_DAPM_INPUT("AMIC2"),
+	SND_SOC_DAPM_INPUT("DMIC1DAT"),
+	SND_SOC_DAPM_INPUT("DMIC2DAT"),
+	SND_SOC_DAPM_INPUT("AUXL"),
+	SND_SOC_DAPM_INPUT("AUXR"),
+
+	SND_SOC_DAPM_PGA("PORT1_1_RX", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("PORT1_2_RX", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("PORT1_3_RX", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("PORT1_4_RX", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("PORT1_5_RX", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("PORT1_6_RX", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("PORT1_7_RX", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("PORT1_8_RX", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("PORT2_1_RX", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("PORT2_2_RX", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+	SND_SOC_DAPM_SUPPLY("AMIC1Bias", LM49453_P0_MICL_REG, 6, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("AMIC2Bias", LM49453_P0_MICR_REG, 6, 0, NULL, 0),
+
+	/* playback path driver enables */
+	SND_SOC_DAPM_OUT_DRV("Headset Switch",
+			LM49453_P0_PMC_SETUP_REG, 0, 0, NULL, 0),
+	SND_SOC_DAPM_OUT_DRV("Earpiece Switch",
+			LM49453_P0_EP_REG, 0, 0, NULL, 0),
+	SND_SOC_DAPM_OUT_DRV("Speaker Left Switch",
+			LM49453_P0_DIS_PKVL_FB_REG, 0, 1, NULL, 0),
+	SND_SOC_DAPM_OUT_DRV("Speaker Right Switch",
+			LM49453_P0_DIS_PKVL_FB_REG, 1, 1, NULL, 0),
+	SND_SOC_DAPM_OUT_DRV("Haptic Left Switch",
+			LM49453_P0_DIS_PKVL_FB_REG, 2, 1, NULL, 0),
+	SND_SOC_DAPM_OUT_DRV("Haptic Right Switch",
+			LM49453_P0_DIS_PKVL_FB_REG, 3, 1, NULL, 0),
+
+	/* DAC */
+	SND_SOC_DAPM_DAC("HPL DAC", "Headset", SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_DAC("HPR DAC", "Headset", SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_DAC("LSL DAC", "Speaker", SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_DAC("LSR DAC", "Speaker", SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_DAC("HAL DAC", "Haptic", SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_DAC("HAR DAC", "Haptic", SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_DAC("LOL DAC", "Lineout", SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_DAC("LOR DAC", "Lineout", SND_SOC_NOPM, 0, 0),
+
+
+	SND_SOC_DAPM_PGA("AUXL Input",
+			LM49453_P0_ANALOG_MIXER_ADC_REG, 2, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("AUXR Input",
+			LM49453_P0_ANALOG_MIXER_ADC_REG, 3, 0, NULL, 0),
+
+	SND_SOC_DAPM_PGA("Sidetone", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+	/* ADC */
+	SND_SOC_DAPM_ADC("DMIC1 Left", "Capture", SND_SOC_NOPM, 1, 0),
+	SND_SOC_DAPM_ADC("DMIC1 Right", "Capture", SND_SOC_NOPM, 1, 0),
+	SND_SOC_DAPM_ADC("DMIC2 Left", "Capture", SND_SOC_NOPM, 1, 0),
+	SND_SOC_DAPM_ADC("DMIC2 Right", "Capture", SND_SOC_NOPM, 1, 0),
+
+	SND_SOC_DAPM_ADC("ADC Left", "Capture", SND_SOC_NOPM, 1, 0),
+	SND_SOC_DAPM_ADC("ADC Right", "Capture", SND_SOC_NOPM, 0, 0),
+
+	SND_SOC_DAPM_MUX("ADCL Mux", SND_SOC_NOPM, 0, 0,
+			  &lm49453_adcl_mux_control),
+	SND_SOC_DAPM_MUX("ADCR Mux", SND_SOC_NOPM, 0, 0,
+			  &lm49453_adcr_mux_control),
+
+	SND_SOC_DAPM_MUX("Mic1 Input",
+			SND_SOC_NOPM, 0, 0, &lm49453_adcl_mux_control),
+
+	SND_SOC_DAPM_MUX("Mic2 Input",
+			SND_SOC_NOPM, 0, 0, &lm49453_adcr_mux_control),
+
+	/* AIF */
+	SND_SOC_DAPM_AIF_IN("PORT1_SDI", NULL, 0,
+			    LM49453_P0_PULL_CONFIG1_REG, 2, 0),
+	SND_SOC_DAPM_AIF_IN("PORT2_SDI", NULL, 0,
+			    LM49453_P0_PULL_CONFIG1_REG, 6, 0),
+
+	SND_SOC_DAPM_AIF_OUT("PORT1_SDO", NULL, 0,
+			     LM49453_P0_PULL_CONFIG1_REG, 3, 0),
+	SND_SOC_DAPM_AIF_OUT("PORT2_SDO", NULL, 0,
+			      LM49453_P0_PULL_CONFIG1_REG, 7, 0),
+
+	/* Port1 TX controls */
+	SND_SOC_DAPM_OUT_DRV("P1_1_TX", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_OUT_DRV("P1_2_TX", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_OUT_DRV("P1_3_TX", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_OUT_DRV("P1_4_TX", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_OUT_DRV("P1_5_TX", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_OUT_DRV("P1_6_TX", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_OUT_DRV("P1_7_TX", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_OUT_DRV("P1_8_TX", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+	/* Port2 TX controls */
+	SND_SOC_DAPM_OUT_DRV("P2_1_TX", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_OUT_DRV("P2_2_TX", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+	/* Sidetone Mixer */
+	SND_SOC_DAPM_MIXER("Sidetone Mixer", SND_SOC_NOPM, 0, 0,
+			    lm49453_sidetone_mixer_controls,
+			    ARRAY_SIZE(lm49453_sidetone_mixer_controls)),
+
+	/* DAC MIXERS */
+	SND_SOC_DAPM_MIXER("HPL Mixer", SND_SOC_NOPM, 0, 0,
+			    lm49453_headset_left_mixer,
+			    ARRAY_SIZE(lm49453_headset_left_mixer)),
+	SND_SOC_DAPM_MIXER("HPR Mixer", SND_SOC_NOPM, 0, 0,
+			    lm49453_headset_right_mixer,
+			    ARRAY_SIZE(lm49453_headset_right_mixer)),
+	SND_SOC_DAPM_MIXER("LOL Mixer", SND_SOC_NOPM, 0, 0,
+			    lm49453_lineout_left_mixer,
+			    ARRAY_SIZE(lm49453_lineout_left_mixer)),
+	SND_SOC_DAPM_MIXER("LOR Mixer", SND_SOC_NOPM, 0, 0,
+			    lm49453_lineout_right_mixer,
+			    ARRAY_SIZE(lm49453_lineout_right_mixer)),
+	SND_SOC_DAPM_MIXER("LSL Mixer", SND_SOC_NOPM, 0, 0,
+			    lm49453_speaker_left_mixer,
+			    ARRAY_SIZE(lm49453_speaker_left_mixer)),
+	SND_SOC_DAPM_MIXER("LSR Mixer", SND_SOC_NOPM, 0, 0,
+			    lm49453_speaker_right_mixer,
+			    ARRAY_SIZE(lm49453_speaker_right_mixer)),
+	SND_SOC_DAPM_MIXER("HAL Mixer", SND_SOC_NOPM, 0, 0,
+			    lm49453_haptic_left_mixer,
+			    ARRAY_SIZE(lm49453_haptic_left_mixer)),
+	SND_SOC_DAPM_MIXER("HAR Mixer", SND_SOC_NOPM, 0, 0,
+			    lm49453_haptic_right_mixer,
+			    ARRAY_SIZE(lm49453_haptic_right_mixer)),
+
+	/* Capture Mixer */
+	SND_SOC_DAPM_MIXER("Port1_1 Mixer", SND_SOC_NOPM, 0, 0,
+			    lm49453_port1_tx1_mixer,
+			    ARRAY_SIZE(lm49453_port1_tx1_mixer)),
+	SND_SOC_DAPM_MIXER("Port1_2 Mixer", SND_SOC_NOPM, 0, 0,
+			    lm49453_port1_tx2_mixer,
+			    ARRAY_SIZE(lm49453_port1_tx2_mixer)),
+	SND_SOC_DAPM_MIXER("Port1_3 Mixer", SND_SOC_NOPM, 0, 0,
+			    lm49453_port1_tx3_mixer,
+			    ARRAY_SIZE(lm49453_port1_tx3_mixer)),
+	SND_SOC_DAPM_MIXER("Port1_4 Mixer", SND_SOC_NOPM, 0, 0,
+			    lm49453_port1_tx4_mixer,
+			    ARRAY_SIZE(lm49453_port1_tx4_mixer)),
+	SND_SOC_DAPM_MIXER("Port1_5 Mixer", SND_SOC_NOPM, 0, 0,
+			    lm49453_port1_tx5_mixer,
+			    ARRAY_SIZE(lm49453_port1_tx5_mixer)),
+	SND_SOC_DAPM_MIXER("Port1_6 Mixer", SND_SOC_NOPM, 0, 0,
+			    lm49453_port1_tx6_mixer,
+			    ARRAY_SIZE(lm49453_port1_tx6_mixer)),
+	SND_SOC_DAPM_MIXER("Port1_7 Mixer", SND_SOC_NOPM, 0, 0,
+			    lm49453_port1_tx7_mixer,
+			    ARRAY_SIZE(lm49453_port1_tx7_mixer)),
+	SND_SOC_DAPM_MIXER("Port1_8 Mixer", SND_SOC_NOPM, 0, 0,
+			    lm49453_port1_tx8_mixer,
+			    ARRAY_SIZE(lm49453_port1_tx8_mixer)),
+
+	SND_SOC_DAPM_MIXER("Port2_1 Mixer", SND_SOC_NOPM, 0, 0,
+			    lm49453_port2_tx1_mixer,
+			    ARRAY_SIZE(lm49453_port2_tx1_mixer)),
+	SND_SOC_DAPM_MIXER("Port2_2 Mixer", SND_SOC_NOPM, 0, 0,
+			    lm49453_port2_tx2_mixer,
+			    ARRAY_SIZE(lm49453_port2_tx2_mixer)),
+};
+
+static const struct snd_soc_dapm_route lm49453_audio_map[] = {
+	/* Port SDI mapping */
+	{ "PORT1_1_RX", "Port1 Playback Switch", "PORT1_SDI" },
+	{ "PORT1_2_RX", "Port1 Playback Switch", "PORT1_SDI" },
+	{ "PORT1_3_RX", "Port1 Playback Switch", "PORT1_SDI" },
+	{ "PORT1_4_RX", "Port1 Playback Switch", "PORT1_SDI" },
+	{ "PORT1_5_RX", "Port1 Playback Switch", "PORT1_SDI" },
+	{ "PORT1_6_RX", "Port1 Playback Switch", "PORT1_SDI" },
+	{ "PORT1_7_RX", "Port1 Playback Switch", "PORT1_SDI" },
+	{ "PORT1_8_RX", "Port1 Playback Switch", "PORT1_SDI" },
+
+	{ "PORT2_1_RX", "Port2 Playback Switch", "PORT2_SDI" },
+	{ "PORT2_2_RX", "Port2 Playback Switch", "PORT2_SDI" },
+
+	/* HP mapping */
+	{ "HPL Mixer", "Port1_1 Switch", "PORT1_1_RX" },
+	{ "HPL Mixer", "Port1_2 Switch", "PORT1_2_RX" },
+	{ "HPL Mixer", "Port1_3 Switch", "PORT1_3_RX" },
+	{ "HPL Mixer", "Port1_4 Switch", "PORT1_4_RX" },
+	{ "HPL Mixer", "Port1_5 Switch", "PORT1_5_RX" },
+	{ "HPL Mixer", "Port1_6 Switch", "PORT1_6_RX" },
+	{ "HPL Mixer", "Port1_7 Switch", "PORT1_7_RX" },
+	{ "HPL Mixer", "Port1_8 Switch", "PORT1_8_RX" },
+
+	{ "HPL Mixer", "Port2_1 Switch", "PORT2_1_RX" },
+	{ "HPL Mixer", "Port2_2 Switch", "PORT2_2_RX" },
+
+	{ "HPL Mixer", "ADCL Switch", "ADC Left" },
+	{ "HPL Mixer", "ADCR Switch", "ADC Right" },
+	{ "HPL Mixer", "DMIC1L Switch", "DMIC1 Left" },
+	{ "HPL Mixer", "DMIC1R Switch", "DMIC1 Right" },
+	{ "HPL Mixer", "DMIC2L Switch", "DMIC2 Left" },
+	{ "HPL Mixer", "DMIC2R Switch", "DMIC2 Right" },
+	{ "HPL Mixer", "Sidetone Switch", "Sidetone" },
+
+	{ "HPL DAC", NULL, "HPL Mixer" },
+
+	{ "HPR Mixer", "Port1_1 Switch", "PORT1_1_RX" },
+	{ "HPR Mixer", "Port1_2 Switch", "PORT1_2_RX" },
+	{ "HPR Mixer", "Port1_3 Switch", "PORT1_3_RX" },
+	{ "HPR Mixer", "Port1_4 Switch", "PORT1_4_RX" },
+	{ "HPR Mixer", "Port1_5 Switch", "PORT1_5_RX" },
+	{ "HPR Mixer", "Port1_6 Switch", "PORT1_6_RX" },
+	{ "HPR Mixer", "Port1_7 Switch", "PORT1_7_RX" },
+	{ "HPR Mixer", "Port1_8 Switch", "PORT1_8_RX" },
+
+	/* Port 2 */
+	{ "HPR Mixer", "Port2_1 Switch", "PORT2_1_RX" },
+	{ "HPR Mixer", "Port2_2 Switch", "PORT2_2_RX" },
+
+	{ "HPR Mixer", "ADCL Switch", "ADC Left" },
+	{ "HPR Mixer", "ADCR Switch", "ADC Right" },
+	{ "HPR Mixer", "DMIC1L Switch", "DMIC1 Left" },
+	{ "HPR Mixer", "DMIC1R Switch", "DMIC1 Right" },
+	{ "HPR Mixer", "DMIC2L Switch", "DMIC2 Left" },
+	{ "HPR Mixer", "DMIC2L Switch", "DMIC2 Right" },
+	{ "HPR Mixer", "Sidetone Switch", "Sidetone" },
+
+	{ "HPR DAC", NULL, "HPR Mixer" },
+
+	{ "HPOUTL", "Headset Switch", "HPL DAC"},
+	{ "HPOUTR", "Headset Switch", "HPR DAC"},
+
+	/* EP map */
+	{ "EPOUT", "Earpiece Switch", "HPL DAC" },
+
+	/* Speaker map */
+	{ "LSL Mixer", "Port1_1 Switch", "PORT1_1_RX" },
+	{ "LSL Mixer", "Port1_2 Switch", "PORT1_2_RX" },
+	{ "LSL Mixer", "Port1_3 Switch", "PORT1_3_RX" },
+	{ "LSL Mixer", "Port1_4 Switch", "PORT1_4_RX" },
+	{ "LSL Mixer", "Port1_5 Switch", "PORT1_5_RX" },
+	{ "LSL Mixer", "Port1_6 Switch", "PORT1_6_RX" },
+	{ "LSL Mixer", "Port1_7 Switch", "PORT1_7_RX" },
+	{ "LSL Mixer", "Port1_8 Switch", "PORT1_8_RX" },
+
+	/* Port 2 */
+	{ "LSL Mixer", "Port2_1 Switch", "PORT2_1_RX" },
+	{ "LSL Mixer", "Port2_2 Switch", "PORT2_2_RX" },
+
+	{ "LSL Mixer", "ADCL Switch", "ADC Left" },
+	{ "LSL Mixer", "ADCR Switch", "ADC Right" },
+	{ "LSL Mixer", "DMIC1L Switch", "DMIC1 Left" },
+	{ "LSL Mixer", "DMIC1R Switch", "DMIC1 Right" },
+	{ "LSL Mixer", "DMIC2L Switch", "DMIC2 Left" },
+	{ "LSL Mixer", "DMIC2R Switch", "DMIC2 Right" },
+	{ "LSL Mixer", "Sidetone Switch", "Sidetone" },
+
+	{ "LSL DAC", NULL, "LSL Mixer" },
+
+	{ "LSR Mixer", "Port1_1 Switch", "PORT1_1_RX" },
+	{ "LSR Mixer", "Port1_2 Switch", "PORT1_2_RX" },
+	{ "LSR Mixer", "Port1_3 Switch", "PORT1_3_RX" },
+	{ "LSR Mixer", "Port1_4 Switch", "PORT1_4_RX" },
+	{ "LSR Mixer", "Port1_5 Switch", "PORT1_5_RX" },
+	{ "LSR Mixer", "Port1_6 Switch", "PORT1_6_RX" },
+	{ "LSR Mixer", "Port1_7 Switch", "PORT1_7_RX" },
+	{ "LSR Mixer", "Port1_8 Switch", "PORT1_8_RX" },
+
+	/* Port 2 */
+	{ "LSR Mixer", "Port2_1 Switch", "PORT2_1_RX" },
+	{ "LSR Mixer", "Port2_2 Switch", "PORT2_2_RX" },
+
+	{ "LSR Mixer", "ADCL Switch", "ADC Left" },
+	{ "LSR Mixer", "ADCR Switch", "ADC Right" },
+	{ "LSR Mixer", "DMIC1L Switch", "DMIC1 Left" },
+	{ "LSR Mixer", "DMIC1R Switch", "DMIC1 Right" },
+	{ "LSR Mixer", "DMIC2L Switch", "DMIC2 Left" },
+	{ "LSR Mixer", "DMIC2R Switch", "DMIC2 Right" },
+	{ "LSR Mixer", "Sidetone Switch", "Sidetone" },
+
+	{ "LSR DAC", NULL, "LSR Mixer" },
+
+	{ "LSOUTL", "Speaker Left Switch", "LSL DAC"},
+	{ "LSOUTR", "Speaker Left Switch", "LSR DAC"},
+
+	/* Haptic map */
+	{ "HAL Mixer", "Port1_1 Switch", "PORT1_1_RX" },
+	{ "HAL Mixer", "Port1_2 Switch", "PORT1_2_RX" },
+	{ "HAL Mixer", "Port1_3 Switch", "PORT1_3_RX" },
+	{ "HAL Mixer", "Port1_4 Switch", "PORT1_4_RX" },
+	{ "HAL Mixer", "Port1_5 Switch", "PORT1_5_RX" },
+	{ "HAL Mixer", "Port1_6 Switch", "PORT1_6_RX" },
+	{ "HAL Mixer", "Port1_7 Switch", "PORT1_7_RX" },
+	{ "HAL Mixer", "Port1_8 Switch", "PORT1_8_RX" },
+
+	/* Port 2 */
+	{ "HAL Mixer", "Port2_1 Switch", "PORT2_1_RX" },
+	{ "HAL Mixer", "Port2_2 Switch", "PORT2_2_RX" },
+
+	{ "HAL Mixer", "ADCL Switch", "ADC Left" },
+	{ "HAL Mixer", "ADCR Switch", "ADC Right" },
+	{ "HAL Mixer", "DMIC1L Switch", "DMIC1 Left" },
+	{ "HAL Mixer", "DMIC1R Switch", "DMIC1 Right" },
+	{ "HAL Mixer", "DMIC2L Switch", "DMIC2 Left" },
+	{ "HAL Mixer", "DMIC2R Switch", "DMIC2 Right" },
+	{ "HAL Mixer", "Sidetone Switch", "Sidetone" },
+
+	{ "HAL DAC", NULL, "HAL Mixer" },
+
+	{ "HAR Mixer", "Port1_1 Switch", "PORT1_1_RX" },
+	{ "HAR Mixer", "Port1_2 Switch", "PORT1_2_RX" },
+	{ "HAR Mixer", "Port1_3 Switch", "PORT1_3_RX" },
+	{ "HAR Mixer", "Port1_4 Switch", "PORT1_4_RX" },
+	{ "HAR Mixer", "Port1_5 Switch", "PORT1_5_RX" },
+	{ "HAR Mixer", "Port1_6 Switch", "PORT1_6_RX" },
+	{ "HAR Mixer", "Port1_7 Switch", "PORT1_7_RX" },
+	{ "HAR Mixer", "Port1_8 Switch", "PORT1_8_RX" },
+
+	/* Port 2 */
+	{ "HAR Mixer", "Port2_1 Switch", "PORT2_1_RX" },
+	{ "HAR Mixer", "Port2_2 Switch", "PORT2_2_RX" },
+
+	{ "HAR Mixer", "ADCL Switch", "ADC Left" },
+	{ "HAR Mixer", "ADCR Switch", "ADC Right" },
+	{ "HAR Mixer", "DMIC1L Switch", "DMIC1 Left" },
+	{ "HAR Mixer", "DMIC1R Switch", "DMIC1 Right" },
+	{ "HAR Mixer", "DMIC2L Switch", "DMIC2 Left" },
+	{ "HAR Mixer", "DMIC2R Switch", "DMIC2 Right" },
+	{ "HAR Mixer", "Sideton Switch", "Sidetone" },
+
+	{ "HAR DAC", NULL, "HAR Mixer" },
+
+	{ "HAOUTL", "Haptic Left Switch", "HAL DAC" },
+	{ "HAOUTR", "Haptic Right Switch", "HAR DAC" },
+
+	/* Lineout map */
+	{ "LOL Mixer", "Port1_1 Switch", "PORT1_1_RX" },
+	{ "LOL Mixer", "Port1_2 Switch", "PORT1_2_RX" },
+	{ "LOL Mixer", "Port1_3 Switch", "PORT1_3_RX" },
+	{ "LOL Mixer", "Port1_4 Switch", "PORT1_4_RX" },
+	{ "LOL Mixer", "Port1_5 Switch", "PORT1_5_RX" },
+	{ "LOL Mixer", "Port1_6 Switch", "PORT1_6_RX" },
+	{ "LOL Mixer", "Port1_7 Switch", "PORT1_7_RX" },
+	{ "LOL Mixer", "Port1_8 Switch", "PORT1_8_RX" },
+
+	/* Port 2 */
+	{ "LOL Mixer", "Port2_1 Switch", "PORT2_1_RX" },
+	{ "LOL Mixer", "Port2_2 Switch", "PORT2_2_RX" },
+
+	{ "LOL Mixer", "ADCL Switch", "ADC Left" },
+	{ "LOL Mixer", "ADCR Switch", "ADC Right" },
+	{ "LOL Mixer", "DMIC1L Switch", "DMIC1 Left" },
+	{ "LOL Mixer", "DMIC1R Switch", "DMIC1 Right" },
+	{ "LOL Mixer", "DMIC2L Switch", "DMIC2 Left" },
+	{ "LOL Mixer", "DMIC2R Switch", "DMIC2 Right" },
+	{ "LOL Mixer", "Sidetone Switch", "Sidetone" },
+
+	{ "LOL DAC", NULL, "LOL Mixer" },
+
+	{ "LOR Mixer", "Port1_1 Switch", "PORT1_1_RX" },
+	{ "LOR Mixer", "Port1_2 Switch", "PORT1_2_RX" },
+	{ "LOR Mixer", "Port1_3 Switch", "PORT1_3_RX" },
+	{ "LOR Mixer", "Port1_4 Switch", "PORT1_4_RX" },
+	{ "LOR Mixer", "Port1_5 Switch", "PORT1_5_RX" },
+	{ "LOR Mixer", "Port1_6 Switch", "PORT1_6_RX" },
+	{ "LOR Mixer", "Port1_7 Switch", "PORT1_7_RX" },
+	{ "LOR Mixer", "Port1_8 Switch", "PORT1_8_RX" },
+
+	/* Port 2 */
+	{ "LOR Mixer", "Port2_1 Switch", "PORT2_1_RX" },
+	{ "LOR Mixer", "Port2_2 Switch", "PORT2_2_RX" },
+
+	{ "LOR Mixer", "ADCL Switch", "ADC Left" },
+	{ "LOR Mixer", "ADCR Switch", "ADC Right" },
+	{ "LOR Mixer", "DMIC1L Switch", "DMIC1 Left" },
+	{ "LOR Mixer", "DMIC1R Switch", "DMIC1 Right" },
+	{ "LOR Mixer", "DMIC2L Switch", "DMIC2 Left" },
+	{ "LOR Mixer", "DMIC2R Switch", "DMIC2 Right" },
+	{ "LOR Mixer", "Sidetone Switch", "Sidetone" },
+
+	{ "LOR DAC", NULL, "LOR Mixer" },
+
+	{ "LOOUTL", NULL, "LOL DAC" },
+	{ "LOOUTR", NULL, "LOR DAC" },
+
+	/* TX map */
+	/* Port1 mappings */
+	{ "Port1_1 Mixer", "ADCL Switch", "ADC Left" },
+	{ "Port1_1 Mixer", "ADCR Switch", "ADC Right" },
+	{ "Port1_1 Mixer", "DMIC1L Switch", "DMIC1 Left" },
+	{ "Port1_1 Mixer", "DMIC1R Switch", "DMIC1 Right" },
+	{ "Port1_1 Mixer", "DMIC2L Switch", "DMIC2 Left" },
+	{ "Port1_1 Mixer", "DMIC2R Switch", "DMIC2 Right" },
+
+	{ "Port1_2 Mixer", "ADCL Switch", "ADC Left" },
+	{ "Port1_2 Mixer", "ADCR Switch", "ADC Right" },
+	{ "Port1_2 Mixer", "DMIC1L Switch", "DMIC1 Left" },
+	{ "Port1_2 Mixer", "DMIC1R Switch", "DMIC1 Right" },
+	{ "Port1_2 Mixer", "DMIC2L Switch", "DMIC2 Left" },
+	{ "Port1_2 Mixer", "DMIC2R Switch", "DMIC2 Right" },
+
+	{ "Port1_3 Mixer", "ADCL Switch", "ADC Left" },
+	{ "Port1_3 Mixer", "ADCR Switch", "ADC Right" },
+	{ "Port1_3 Mixer", "DMIC1L Switch", "DMIC1 Left" },
+	{ "Port1_3 Mixer", "DMIC1R Switch", "DMIC1 Right" },
+	{ "Port1_3 Mixer", "DMIC2L Switch", "DMIC2 Left" },
+	{ "Port1_3 Mixer", "DMIC2R Switch", "DMIC2 Right" },
+
+	{ "Port1_4 Mixer", "ADCL Switch", "ADC Left" },
+	{ "Port1_4 Mixer", "ADCR Switch", "ADC Right" },
+	{ "Port1_4 Mixer", "DMIC1L Switch", "DMIC1 Left" },
+	{ "Port1_4 Mixer", "DMIC1R Switch", "DMIC1 Right" },
+	{ "Port1_4 Mixer", "DMIC2L Switch", "DMIC2 Left" },
+	{ "Port1_4 Mixer", "DMIC2R Switch", "DMIC2 Right" },
+
+	{ "Port1_5 Mixer", "ADCL Switch", "ADC Left" },
+	{ "Port1_5 Mixer", "ADCR Switch", "ADC Right" },
+	{ "Port1_5 Mixer", "DMIC1L Switch", "DMIC1 Left" },
+	{ "Port1_5 Mixer", "DMIC1R Switch", "DMIC1 Right" },
+	{ "Port1_5 Mixer", "DMIC2L Switch", "DMIC2 Left" },
+	{ "Port1_5 Mixer", "DMIC2R Switch", "DMIC2 Right" },
+
+	{ "Port1_6 Mixer", "ADCL Switch", "ADC Left" },
+	{ "Port1_6 Mixer", "ADCR Switch", "ADC Right" },
+	{ "Port1_6 Mixer", "DMIC1L Switch", "DMIC1 Left" },
+	{ "Port1_6 Mixer", "DMIC1R Switch", "DMIC1 Right" },
+	{ "Port1_6 Mixer", "DMIC2L Switch", "DMIC2 Left" },
+	{ "Port1_6 Mixer", "DMIC2R Switch", "DMIC2 Right" },
+
+	{ "Port1_7 Mixer", "ADCL Switch", "ADC Left" },
+	{ "Port1_7 Mixer", "ADCR Switch", "ADC Right" },
+	{ "Port1_7 Mixer", "DMIC1L Switch", "DMIC1 Left" },
+	{ "Port1_7 Mixer", "DMIC1R Switch", "DMIC1 Right" },
+	{ "Port1_7 Mixer", "DMIC2L Switch", "DMIC2 Left" },
+	{ "Port1_7 Mixer", "DMIC2R Switch", "DMIC2 Right" },
+
+	{ "Port1_8 Mixer", "ADCL Switch", "ADC Left" },
+	{ "Port1_8 Mixer", "ADCR Switch", "ADC Right" },
+	{ "Port1_8 Mixer", "DMIC1L Switch", "DMIC1 Left" },
+	{ "Port1_8 Mixer", "DMIC1R Switch", "DMIC1 Right" },
+	{ "Port1_8 Mixer", "DMIC2L Switch", "DMIC2 Left" },
+	{ "Port1_8 Mixer", "DMIC2R Switch", "DMIC2 Right" },
+
+	{ "Port2_1 Mixer", "ADCL Switch", "ADC Left" },
+	{ "Port2_1 Mixer", "ADCR Switch", "ADC Right" },
+	{ "Port2_1 Mixer", "DMIC1L Switch", "DMIC1 Left" },
+	{ "Port2_1 Mixer", "DMIC1R Switch", "DMIC1 Right" },
+	{ "Port2_1 Mixer", "DMIC2L Switch", "DMIC2 Left" },
+	{ "Port2_1 Mixer", "DMIC2R Switch", "DMIC2 Right" },
+
+	{ "Port2_2 Mixer", "ADCL Switch", "ADC Left" },
+	{ "Port2_2 Mixer", "ADCR Switch", "ADC Right" },
+	{ "Port2_2 Mixer", "DMIC1L Switch", "DMIC1 Left" },
+	{ "Port2_2 Mixer", "DMIC1R Switch", "DMIC1 Right" },
+	{ "Port2_2 Mixer", "DMIC2L Switch", "DMIC2 Left" },
+	{ "Port2_2 Mixer", "DMIC2R Switch", "DMIC2 Right" },
+
+	{ "P1_1_TX", NULL, "Port1_1 Mixer" },
+	{ "P1_2_TX", NULL, "Port1_2 Mixer" },
+	{ "P1_3_TX", NULL, "Port1_3 Mixer" },
+	{ "P1_4_TX", NULL, "Port1_4 Mixer" },
+	{ "P1_5_TX", NULL, "Port1_5 Mixer" },
+	{ "P1_6_TX", NULL, "Port1_6 Mixer" },
+	{ "P1_7_TX", NULL, "Port1_7 Mixer" },
+	{ "P1_8_TX", NULL, "Port1_8 Mixer" },
+
+	{ "P2_1_TX", NULL, "Port2_1 Mixer" },
+	{ "P2_2_TX", NULL, "Port2_2 Mixer" },
+
+	{ "PORT1_SDO", "Port1 Capture Switch", "P1_1_TX"},
+	{ "PORT1_SDO", "Port1 Capture Switch", "P1_2_TX"},
+	{ "PORT1_SDO", "Port1 Capture Switch", "P1_3_TX"},
+	{ "PORT1_SDO", "Port1 Capture Switch", "P1_4_TX"},
+	{ "PORT1_SDO", "Port1 Capture Switch", "P1_5_TX"},
+	{ "PORT1_SDO", "Port1 Capture Switch", "P1_6_TX"},
+	{ "PORT1_SDO", "Port1 Capture Switch", "P1_7_TX"},
+	{ "PORT1_SDO", "Port1 Capture Switch", "P1_8_TX"},
+
+	{ "PORT2_SDO", "Port2 Capture Switch", "P2_1_TX"},
+	{ "PORT2_SDO", "Port2 Capture Switch", "P2_2_TX"},
+
+	{ "Mic1 Input", NULL, "AMIC1" },
+	{ "Mic2 Input", NULL, "AMIC2" },
+
+	{ "AUXL Input", NULL, "AUXL" },
+	{ "AUXR Input", NULL, "AUXR" },
+
+	/* AUX connections */
+	{ "ADCL Mux", "Aux_L", "AUXL Input" },
+	{ "ADCL Mux", "MIC1", "Mic1 Input" },
+
+	{ "ADCR Mux", "Aux_R", "AUXR Input" },
+	{ "ADCR Mux", "MIC2", "Mic2 Input" },
+
+	/* ADC connection */
+	{ "ADC Left", NULL, "ADCL Mux"},
+	{ "ADC Right", NULL, "ADCR Mux"},
+
+	{ "DMIC1 Left", NULL, "DMIC1DAT"},
+	{ "DMIC1 Right", NULL, "DMIC1DAT"},
+	{ "DMIC2 Left", NULL, "DMIC2DAT"},
+	{ "DMIC2 Right", NULL, "DMIC2DAT"},
+
+	/* Sidetone map */
+	{ "Sidetone Mixer", NULL, "ADC Left" },
+	{ "Sidetone Mixer", NULL, "ADC Right" },
+	{ "Sidetone Mixer", NULL, "DMIC1 Left" },
+	{ "Sidetone Mixer", NULL, "DMIC1 Right" },
+	{ "Sidetone Mixer", NULL, "DMIC2 Left" },
+	{ "Sidetone Mixer", NULL, "DMIC2 Right" },
+
+	{ "Sidetone", "Sidetone Switch", "Sidetone Mixer" },
+};
+
+static int lm49453_hw_params(struct snd_pcm_substream *substream,
+			     struct snd_pcm_hw_params *params,
+			     struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct lm49453_priv *lm49453 = snd_soc_codec_get_drvdata(codec);
+	u16 clk_div = 0;
+
+	lm49453->fs_rate = params_rate(params);
+
+	/* Setting DAC clock dividers based on substream sample rate. */
+	switch (lm49453->fs_rate) {
+	case 8000:
+	case 16000:
+	case 32000:
+	case 24000:
+	case 48000:
+		clk_div = 256;
+		break;
+	case 11025:
+	case 22050:
+	case 44100:
+		clk_div = 216;
+		break;
+	case 96000:
+		clk_div = 127;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	snd_soc_write(codec, LM49453_P0_ADC_CLK_DIV_REG, clk_div);
+	snd_soc_write(codec, LM49453_P0_DAC_HP_CLK_DIV_REG, clk_div);
+
+	return 0;
+}
+
+static int lm49453_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+
+	u16 aif_val;
+	int mode = 0;
+	int clk_phase = 0;
+	int clk_shift = 0;
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		aif_val = 0;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFM:
+		aif_val = LM49453_AUDIO_PORT1_BASIC_SYNC_MS;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFS:
+		aif_val = LM49453_AUDIO_PORT1_BASIC_CLK_MS;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFM:
+		aif_val = LM49453_AUDIO_PORT1_BASIC_CLK_MS |
+			  LM49453_AUDIO_PORT1_BASIC_SYNC_MS;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		mode = 1;
+		clk_phase = (1 << 5);
+		clk_shift = 1;
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		mode = 1;
+		clk_phase = (1 << 5);
+		clk_shift = 0;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	snd_soc_update_bits(codec, LM49453_P0_AUDIO_PORT1_BASIC_REG,
+			    LM49453_AUDIO_PORT1_BASIC_FMT_MASK|BIT(1)|BIT(5),
+			    (aif_val | mode | clk_phase));
+
+	snd_soc_write(codec, LM49453_P0_AUDIO_PORT1_RX_MSB_REG, clk_shift);
+
+	return 0;
+}
+
+static int lm49453_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
+				  unsigned int freq, int dir)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	u16 pll_clk = 0;
+
+	switch (freq) {
+	case 12288000:
+	case 26000000:
+	case 19200000:
+		/* pll clk slection */
+		pll_clk = 0;
+		break;
+	case 48000:
+	case 32576:
+		/* fll clk slection */
+		pll_clk = BIT(4);
+		return 0;
+	default:
+		return -EINVAL;
+	}
+
+	snd_soc_update_bits(codec, LM49453_P0_PMC_SETUP_REG, BIT(4), pll_clk);
+
+	return 0;
+}
+
+static int lm49453_hp_mute(struct snd_soc_dai *dai, int mute)
+{
+	snd_soc_update_bits(dai->codec, LM49453_P0_DAC_DSP_REG, BIT(1)|BIT(0),
+			    (mute ? (BIT(1)|BIT(0)) : 0));
+	return 0;
+}
+
+static int lm49453_lo_mute(struct snd_soc_dai *dai, int mute)
+{
+	snd_soc_update_bits(dai->codec, LM49453_P0_DAC_DSP_REG, BIT(3)|BIT(2),
+			    (mute ? (BIT(3)|BIT(2)) : 0));
+	return 0;
+}
+
+static int lm49453_ls_mute(struct snd_soc_dai *dai, int mute)
+{
+	snd_soc_update_bits(dai->codec, LM49453_P0_DAC_DSP_REG, BIT(5)|BIT(4),
+			    (mute ? (BIT(5)|BIT(4)) : 0));
+	return 0;
+}
+
+static int lm49453_ep_mute(struct snd_soc_dai *dai, int mute)
+{
+	snd_soc_update_bits(dai->codec, LM49453_P0_DAC_DSP_REG, BIT(4),
+			    (mute ? BIT(4) : 0));
+	return 0;
+}
+
+static int lm49453_ha_mute(struct snd_soc_dai *dai, int mute)
+{
+	snd_soc_update_bits(dai->codec, LM49453_P0_DAC_DSP_REG, BIT(7)|BIT(6),
+			    (mute ? (BIT(7)|BIT(6)) : 0));
+	return 0;
+}
+
+static int lm49453_set_bias_level(struct snd_soc_codec *codec,
+				  enum snd_soc_bias_level level)
+{
+	struct lm49453_priv *lm49453 = snd_soc_codec_get_drvdata(codec);
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+	case SND_SOC_BIAS_PREPARE:
+		break;
+
+	case SND_SOC_BIAS_STANDBY:
+		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+			regcache_sync(lm49453->regmap);
+
+		snd_soc_update_bits(codec, LM49453_P0_PMC_SETUP_REG,
+				    LM49453_PMC_SETUP_CHIP_EN, LM49453_CHIP_EN);
+		break;
+
+	case SND_SOC_BIAS_OFF:
+		snd_soc_update_bits(codec, LM49453_P0_PMC_SETUP_REG,
+				    LM49453_PMC_SETUP_CHIP_EN, 0);
+		break;
+	}
+
+	codec->dapm.bias_level = level;
+
+	return 0;
+}
+
+/* Formates supported by LM49453 driver. */
+#define LM49453_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+			 SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_ops lm49453_headset_dai_ops = {
+	.hw_params	= lm49453_hw_params,
+	.set_sysclk	= lm49453_set_dai_sysclk,
+	.set_fmt	= lm49453_set_dai_fmt,
+	.digital_mute	= lm49453_hp_mute,
+};
+
+static struct snd_soc_dai_ops lm49453_speaker_dai_ops = {
+	.hw_params	= lm49453_hw_params,
+	.set_sysclk	= lm49453_set_dai_sysclk,
+	.set_fmt	= lm49453_set_dai_fmt,
+	.digital_mute	= lm49453_ls_mute,
+};
+
+static struct snd_soc_dai_ops lm49453_haptic_dai_ops = {
+	.hw_params	= lm49453_hw_params,
+	.set_sysclk	= lm49453_set_dai_sysclk,
+	.set_fmt	= lm49453_set_dai_fmt,
+	.digital_mute	= lm49453_ha_mute,
+};
+
+static struct snd_soc_dai_ops lm49453_ep_dai_ops = {
+	.hw_params	= lm49453_hw_params,
+	.set_sysclk	= lm49453_set_dai_sysclk,
+	.set_fmt	= lm49453_set_dai_fmt,
+	.digital_mute	= lm49453_ep_mute,
+};
+
+static struct snd_soc_dai_ops lm49453_lineout_dai_ops = {
+	.hw_params	= lm49453_hw_params,
+	.set_sysclk	= lm49453_set_dai_sysclk,
+	.set_fmt	= lm49453_set_dai_fmt,
+	.digital_mute	= lm49453_lo_mute,
+};
+
+/* LM49453 dai structure. */
+static const struct snd_soc_dai_driver lm49453_dai[] = {
+	{
+		.name = "LM49453 Headset",
+		.playback = {
+			.stream_name = "Headset",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = LM49453_FORMATS,
+		},
+		.capture = {
+			.stream_name = "Capture",
+			.channels_min = 1,
+			.channels_max = 5,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = LM49453_FORMATS,
+		},
+		.ops = &lm49453_headset_dai_ops,
+		.symmetric_rates = 1,
+	},
+	{
+		.name = "LM49453 Speaker",
+		.playback = {
+			.stream_name = "Speaker",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = LM49453_FORMATS,
+		},
+		.ops = &lm49453_speaker_dai_ops,
+	},
+	{
+		.name = "LM49453 Haptic",
+		.playback = {
+			.stream_name = "Haptic",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = LM49453_FORMATS,
+		},
+		.ops = &lm49453_haptic_dai_ops,
+	},
+	{
+		.name = "LM49453 Earpiece",
+		.playback = {
+			.stream_name = "Earpiece",
+			.channels_min = 1,
+			.channels_max = 1,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = LM49453_FORMATS,
+		},
+		.ops = &lm49453_ep_dai_ops,
+	},
+	{
+		.name = "LM49453 line out",
+		.playback = {
+			.stream_name = "Lineout",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = LM49453_FORMATS,
+		},
+		.ops = &lm49453_lineout_dai_ops,
+	},
+};
+
+static int lm49453_suspend(struct snd_soc_codec *codec)
+{
+	lm49453_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	return 0;
+}
+
+static int lm49453_resume(struct snd_soc_codec *codec)
+{
+	lm49453_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	return 0;
+}
+
+static int lm49453_probe(struct snd_soc_codec *codec)
+{
+	struct lm49453_priv *lm49453 = snd_soc_codec_get_drvdata(codec);
+	int ret = 0;
+
+	codec->control_data = lm49453->regmap;
+
+	ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_REGMAP);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+/* power down chip */
+static int lm49453_remove(struct snd_soc_codec *codec)
+{
+	lm49453_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_lm49453 = {
+	.probe = lm49453_probe,
+	.remove = lm49453_remove,
+	.suspend = lm49453_suspend,
+	.resume = lm49453_resume,
+	.set_bias_level = lm49453_set_bias_level,
+	.controls = lm49453_snd_controls,
+	.num_controls = ARRAY_SIZE(lm49453_snd_controls),
+	.dapm_widgets = lm49453_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(lm49453_dapm_widgets),
+	.dapm_routes = lm49453_audio_map,
+	.num_dapm_routes = ARRAY_SIZE(lm49453_audio_map),
+	.idle_bias_off = true,
+};
+
+static const struct regmap_config lm49453_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.max_register = LM49453_MAX_REGISTER,
+	.reg_defaults = lm49453_reg_defs,
+	.num_reg_defaults = ARRAY_SIZE(lm49453_reg_defs),
+	.cache_type = REGCACHE_RBTREE,
+};
+
+static __devinit int lm49453_i2c_probe(struct i2c_client *i2c,
+				       const struct i2c_device_id *id)
+{
+	struct lm49453_priv *lm49453;
+	int ret = 0;
+
+	lm49453 = devm_kzalloc(&i2c->dev, sizeof(struct lm49453_priv),
+				GFP_KERNEL);
+
+	if (lm49453 == NULL)
+		return -ENOMEM;
+
+	i2c_set_clientdata(i2c, lm49453);
+
+	lm49453->regmap = regmap_init_i2c(i2c, &lm49453_regmap_config);
+	if (IS_ERR(lm49453->regmap)) {
+		ret = PTR_ERR(lm49453->regmap);
+		dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+			ret);
+		return ret;
+	}
+
+	ret =  snd_soc_register_codec(&i2c->dev,
+				      &soc_codec_dev_lm49453,
+				      lm49453_dai, ARRAY_SIZE(lm49453_dai));
+	if (ret < 0) {
+		dev_err(&i2c->dev, "Failed to register codec: %d\n", ret);
+		regmap_exit(lm49453->regmap);
+		return ret;
+	}
+
+	return ret;
+}
+
+static int __devexit lm49453_i2c_remove(struct i2c_client *client)
+{
+	struct lm49453_priv *lm49453 = i2c_get_clientdata(client);
+
+	snd_soc_unregister_codec(&client->dev);
+	regmap_exit(lm49453->regmap);
+	return 0;
+}
+
+static const struct i2c_device_id lm49453_i2c_id[] = {
+	{ "lm49453", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, lm49453_i2c_id);
+
+static struct i2c_driver lm49453_i2c_driver = {
+	.driver = {
+		.name = "lm49453",
+		.owner = THIS_MODULE,
+	},
+	.probe = lm49453_i2c_probe,
+	.remove = __devexit_p(lm49453_i2c_remove),
+	.id_table = lm49453_i2c_id,
+};
+
+module_i2c_driver(lm49453_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC LM49453 driver");
+MODULE_AUTHOR("M R Swami Reddy <MR.Swami.Reddy@ti.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/lm49453.h b/sound/soc/codecs/lm49453.h
new file mode 100644
index 0000000..a63cfa5
--- /dev/null
+++ b/sound/soc/codecs/lm49453.h
@@ -0,0 +1,380 @@
+/*
+ * lm49453.h  -  LM49453 ALSA Soc Audio drive
+ *
+ * Copyright (c) 2012  Texas Instruments, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ */
+
+#ifndef _LM49453_H
+#define _LM49453_H
+
+#include <linux/bitops.h>
+
+/* LM49453_P0 register space for page0 */
+#define LM49453_P0_PMC_SETUP_REG			0x00
+#define LM49453_P0_PLL_CLK_SEL1_REG			0x01
+#define LM49453_P0_PLL_CLK_SEL2_REG			0x02
+#define LM49453_P0_PMC_CLK_DIV_REG			0x03
+#define LM49453_P0_HSDET_CLK_DIV_REG			0x04
+#define LM49453_P0_DMIC_CLK_DIV_REG			0x05
+#define LM49453_P0_ADC_CLK_DIV_REG			0x06
+#define LM49453_P0_DAC_OT_CLK_DIV_REG			0x07
+#define LM49453_P0_PLL_HF_M_REG				0x08
+#define LM49453_P0_PLL_LF_M_REG				0x09
+#define LM49453_P0_PLL_NL_REG				0x0A
+#define LM49453_P0_PLL_N_MODL_REG			0x0B
+#define LM49453_P0_PLL_N_MODH_REG			0x0C
+#define LM49453_P0_PLL_P1_REG				0x0D
+#define LM49453_P0_PLL_P2_REG				0x0E
+#define LM49453_P0_FLL_REF_FREQL_REG			0x0F
+#define LM49453_P0_FLL_REF_FREQH_REG			0x10
+#define LM49453_P0_VCO_TARGETLL_REG			0x11
+#define LM49453_P0_VCO_TARGETLH_REG			0x12
+#define LM49453_P0_VCO_TARGETHL_REG			0x13
+#define LM49453_P0_VCO_TARGETHH_REG			0x14
+#define LM49453_P0_PLL_CONFIG_REG			0x15
+#define LM49453_P0_DAC_CLK_SEL_REG			0x16
+#define LM49453_P0_DAC_HP_CLK_DIV_REG			0x17
+
+/* Analog Mixer Input Stages */
+#define LM49453_P0_MICL_REG				0x20
+#define LM49453_P0_MICR_REG				0x21
+#define LM49453_P0_EP_REG				0x24
+#define LM49453_P0_DIS_PKVL_FB_REG			0x25
+
+/* Analog Mixer Output Stages */
+#define LM49453_P0_ANALOG_MIXER_ADC_REG			0x2E
+
+/*ADC or DAC */
+#define LM49453_P0_ADC_DSP_REG				0x30
+#define LM49453_P0_DAC_DSP_REG				0x31
+
+/* EFFECTS ENABLES */
+#define LM49453_P0_ADC_FX_ENABLES_REG			0x33
+
+/* GPIO */
+#define LM49453_P0_GPIO1_REG				0x38
+#define LM49453_P0_GPIO2_REG				0x39
+#define LM49453_P0_GPIO3_REG				0x3A
+#define LM49453_P0_HAP_CTL_REG				0x3B
+#define LM49453_P0_HAP_FREQ_PROG_LEFTL_REG		0x3C
+#define LM49453_P0_HAP_FREQ_PROG_LEFTH_REG		0x3D
+#define LM49453_P0_HAP_FREQ_PROG_RIGHTL_REG		0x3E
+#define LM49453_P0_HAP_FREQ_PROG_RIGHTH_REG		0x3F
+
+/* DIGITAL MIXER */
+#define LM49453_P0_DMIX_CLK_SEL_REG			0x40
+#define LM49453_P0_PORT1_RX_LVL1_REG			0x41
+#define LM49453_P0_PORT1_RX_LVL2_REG			0x42
+#define LM49453_P0_PORT2_RX_LVL_REG			0x43
+#define LM49453_P0_PORT1_TX1_REG			0x44
+#define LM49453_P0_PORT1_TX2_REG			0x45
+#define LM49453_P0_PORT1_TX3_REG			0x46
+#define LM49453_P0_PORT1_TX4_REG			0x47
+#define LM49453_P0_PORT1_TX5_REG			0x48
+#define LM49453_P0_PORT1_TX6_REG			0x49
+#define LM49453_P0_PORT1_TX7_REG			0x4A
+#define LM49453_P0_PORT1_TX8_REG			0x4B
+#define LM49453_P0_PORT2_TX1_REG			0x4C
+#define LM49453_P0_PORT2_TX2_REG			0x4D
+#define LM49453_P0_STN_SEL_REG				0x4F
+#define LM49453_P0_DACHPL1_REG				0x50
+#define LM49453_P0_DACHPL2_REG				0x51
+#define LM49453_P0_DACHPR1_REG				0x52
+#define LM49453_P0_DACHPR2_REG				0x53
+#define LM49453_P0_DACLOL1_REG				0x54
+#define LM49453_P0_DACLOL2_REG				0x55
+#define LM49453_P0_DACLOR1_REG				0x56
+#define LM49453_P0_DACLOR2_REG				0x57
+#define LM49453_P0_DACLSL1_REG				0x58
+#define LM49453_P0_DACLSL2_REG				0x59
+#define LM49453_P0_DACLSR1_REG				0x5A
+#define LM49453_P0_DACLSR2_REG				0x5B
+#define LM49453_P0_DACHAL1_REG				0x5C
+#define LM49453_P0_DACHAL2_REG				0x5D
+#define LM49453_P0_DACHAR1_REG				0x5E
+#define LM49453_P0_DACHAR2_REG				0x5F
+
+/* AUDIO PORT 1 (TDM) */
+#define LM49453_P0_AUDIO_PORT1_BASIC_REG		0x60
+#define LM49453_P0_AUDIO_PORT1_CLK_GEN1_REG		0x61
+#define LM49453_P0_AUDIO_PORT1_CLK_GEN2_REG		0x62
+#define LM49453_P0_AUDIO_PORT1_CLK_GEN3_REG		0x63
+#define LM49453_P0_AUDIO_PORT1_SYNC_RATE_REG		0x64
+#define LM49453_P0_AUDIO_PORT1_SYNC_SDO_SETUP_REG	0x65
+#define LM49453_P0_AUDIO_PORT1_DATA_WIDTH_REG		0x66
+#define LM49453_P0_AUDIO_PORT1_RX_MSB_REG		0x67
+#define LM49453_P0_AUDIO_PORT1_TX_MSB_REG		0x68
+#define LM49453_P0_AUDIO_PORT1_TDM_CHANNELS_REG		0x69
+
+/* AUDIO PORT 2 */
+#define LM49453_P0_AUDIO_PORT2_BASIC_REG		0x6A
+#define LM49453_P0_AUDIO_PORT2_CLK_GEN1_REG		0x6B
+#define LM49453_P0_AUDIO_PORT2_CLK_GEN2_REG		0x6C
+#define LM49453_P0_AUDIO_PORT2_SYNC_GEN_REG		0x6D
+#define LM49453_P0_AUDIO_PORT2_DATA_WIDTH_REG		0x6E
+#define LM49453_P0_AUDIO_PORT2_RX_MODE_REG		0x6F
+#define LM49453_P0_AUDIO_PORT2_TX_MODE_REG		0x70
+
+/* SAMPLE RATE */
+#define LM49453_P0_PORT1_SR_LSB_REG			0x79
+#define LM49453_P0_PORT1_SR_MSB_REG			0x7A
+#define LM49453_P0_PORT2_SR_LSB_REG			0x7B
+#define LM49453_P0_PORT2_SR_MSB_REG			0x7C
+
+/* EFFECTS - HPFs */
+#define LM49453_P0_HPF_REG				0x80
+
+/* EFFECTS ADC ALC */
+#define LM49453_P0_ADC_ALC1_REG				0x82
+#define LM49453_P0_ADC_ALC2_REG				0x83
+#define LM49453_P0_ADC_ALC3_REG				0x84
+#define LM49453_P0_ADC_ALC4_REG				0x85
+#define LM49453_P0_ADC_ALC5_REG				0x86
+#define LM49453_P0_ADC_ALC6_REG				0x87
+#define LM49453_P0_ADC_ALC7_REG				0x88
+#define LM49453_P0_ADC_ALC8_REG				0x89
+#define LM49453_P0_DMIC1_LEVELL_REG			0x8A
+#define LM49453_P0_DMIC1_LEVELR_REG			0x8B
+#define LM49453_P0_DMIC2_LEVELL_REG			0x8C
+#define LM49453_P0_DMIC2_LEVELR_REG			0x8D
+#define LM49453_P0_ADC_LEVELL_REG			0x8E
+#define LM49453_P0_ADC_LEVELR_REG			0x8F
+#define LM49453_P0_DAC_HP_LEVELL_REG			0x90
+#define LM49453_P0_DAC_HP_LEVELR_REG			0x91
+#define LM49453_P0_DAC_LO_LEVELL_REG			0x92
+#define LM49453_P0_DAC_LO_LEVELR_REG			0x93
+#define LM49453_P0_DAC_LS_LEVELL_REG			0x94
+#define LM49453_P0_DAC_LS_LEVELR_REG			0x95
+#define LM49453_P0_DAC_HA_LEVELL_REG			0x96
+#define LM49453_P0_DAC_HA_LEVELR_REG			0x97
+#define LM49453_P0_SOFT_MUTE_REG			0x98
+#define LM49453_P0_DMIC_MUTE_CFG_REG			0x99
+#define LM49453_P0_ADC_MUTE_CFG_REG			0x9A
+#define LM49453_P0_DAC_MUTE_CFG_REG			0x9B
+
+/*DIGITAL MIC1 */
+#define LM49453_P0_DIGITAL_MIC1_CONFIG_REG		0xB0
+#define LM49453_P0_DIGITAL_MIC1_DATA_DELAYL_REG		0xB1
+#define LM49453_P0_DIGITAL_MIC1_DATA_DELAYR_REG		0xB2
+
+/*DIGITAL MIC2 */
+#define LM49453_P0_DIGITAL_MIC2_CONFIG_REG		0xB3
+#define LM49453_P0_DIGITAL_MIC2_DATA_DELAYL_REG		0xB4
+#define LM49453_P0_DIGITAL_MIC2_DATA_DELAYR_REG		0xB5
+
+/* ADC DECIMATOR */
+#define LM49453_P0_ADC_DECIMATOR_REG			0xB6
+
+/* DAC CONFIGURE */
+#define LM49453_P0_DAC_CONFIG_REG			0xB7
+
+/* SIDETONE */
+#define LM49453_P0_STN_VOL_ADCL_REG			0xB8
+#define LM49453_P0_STN_VOL_ADCR_REG			0xB9
+#define LM49453_P0_STN_VOL_DMIC1L_REG			0xBA
+#define LM49453_P0_STN_VOL_DMIC1R_REG			0xBB
+#define LM49453_P0_STN_VOL_DMIC2L_REG			0xBC
+#define LM49453_P0_STN_VOL_DMIC2R_REG			0xBD
+
+/* ADC/DAC CLIPPING MONITORS (Read Only/Write to Clear) */
+#define LM49453_P0_ADC_DEC_CLIP_REG			0xC2
+#define LM49453_P0_ADC_HPF_CLIP_REG			0xC3
+#define LM49453_P0_ADC_LVL_CLIP_REG			0xC4
+#define LM49453_P0_DAC_LVL_CLIP_REG			0xC5
+
+/* ADC ALC EFFECT MONITORS (Read Only) */
+#define LM49453_P0_ADC_LVLMONL_REG			0xC8
+#define LM49453_P0_ADC_LVLMONR_REG			0xC9
+#define LM49453_P0_ADC_ALCMONL_REG			0xCA
+#define LM49453_P0_ADC_ALCMONR_REG			0xCB
+#define LM49453_P0_ADC_MUTED_REG			0xCC
+#define LM49453_P0_DAC_MUTED_REG			0xCD
+
+/* HEADSET DETECT */
+#define LM49453_P0_HSD_PPB_LONG_CNT_LIMITL_REG		0xD0
+#define LM49453_P0_HSD_PPB_LONG_CNT_LIMITR_REG		0xD1
+#define LM49453_P0_HSD_PIN3_4_EX_LOOP_CNT_LIMITL_REG	0xD2
+#define LM49453_P0_HSD_PIN3_4_EX_LOOP_CNT_LIMITH_REG	0xD3
+#define LM49453_P0_HSD_TIMEOUT1_REG			0xD4
+#define LM49453_P0_HSD_TIMEOUT2_REG			0xD5
+#define LM49453_P0_HSD_TIMEOUT3_REG			0xD6
+#define LM49453_P0_HSD_PIN3_4_CFG_REG			0xD7
+#define LM49453_P0_HSD_IRQ1_REG				0xD8
+#define LM49453_P0_HSD_IRQ2_REG				0xD9
+#define LM49453_P0_HSD_IRQ3_REG				0xDA
+#define LM49453_P0_HSD_IRQ4_REG				0xDB
+#define LM49453_P0_HSD_IRQ_MASK1_REG			0xDC
+#define LM49453_P0_HSD_IRQ_MASK2_REG			0xDD
+#define LM49453_P0_HSD_IRQ_MASK3_REG			0xDE
+#define LM49453_P0_HSD_R_HPLL_REG			0xE0
+#define LM49453_P0_HSD_R_HPLH_REG			0xE1
+#define LM49453_P0_HSD_R_HPLU_REG			0xE2
+#define LM49453_P0_HSD_R_HPRL_REG			0xE3
+#define LM49453_P0_HSD_R_HPRH_REG			0xE4
+#define LM49453_P0_HSD_R_HPRU_REG			0xE5
+#define LM49453_P0_HSD_VEL_L_FINALL_REG			0xE6
+#define LM49453_P0_HSD_VEL_L_FINALH_REG			0xE7
+#define LM49453_P0_HSD_VEL_L_FINALU_REG			0xE8
+#define LM49453_P0_HSD_RO_FINALL_REG			0xE9
+#define LM49453_P0_HSD_RO_FINALH_REG			0xEA
+#define LM49453_P0_HSD_RO_FINALU_REG			0xEB
+#define LM49453_P0_HSD_VMIC_BIAS_FINALL_REG		0xEC
+#define LM49453_P0_HSD_VMIC_BIAS_FINALH_REG		0xED
+#define LM49453_P0_HSD_VMIC_BIAS_FINALU_REG		0xEE
+#define LM49453_P0_HSD_PIN_CONFIG_REG			0xEF
+#define LM49453_P0_HSD_PLUG_DETECT_BB_IRQ_STATUS1_REG	0xF1
+#define LM49453_P0_HSD_PLUG_DETECT_BB_IRQ_STATUS2_REG	0xF2
+#define LM49453_P0_HSD_PLUG_DETECT_BB_IRQ_STATUS3_REG	0xF3
+#define LM49453_P0_HSD_PLUG_DETECT_BB_IRQ_STATEL_REG	0xF4
+#define LM49453_P0_HSD_PLUG_DETECT_BB_IRQ_STATEH_REG	0xF5
+
+/* I/O PULLDOWN CONFIG */
+#define LM49453_P0_PULL_CONFIG1_REG			0xF8
+#define LM49453_P0_PULL_CONFIG2_REG			0xF9
+#define LM49453_P0_PULL_CONFIG3_REG			0xFA
+
+/* RESET */
+#define LM49453_P0_RESET_REG				0xFE
+
+/* PAGE */
+#define LM49453_PAGE_REG				0xFF
+
+#define LM49453_MAX_REGISTER				(0xFF+1)
+
+/* LM49453_P0_PMC_SETUP_REG (0x00h) */
+#define LM49453_PMC_SETUP_CHIP_EN			(BIT(1)|BIT(0))
+#define LM49453_PMC_SETUP_PLL_EN			BIT(2)
+#define LM49453_PMC_SETUP_PLL_P2_EN			BIT(3)
+#define LM49453_PMC_SETUP_PLL_FLL			BIT(4)
+#define LM49453_PMC_SETUP_MCLK_OVER			BIT(5)
+#define LM49453_PMC_SETUP_RTC_CLK_OVER			BIT(6)
+#define LM49453_PMC_SETUP_CHIP_ACTIVE			BIT(7)
+
+/* Chip Enable bits */
+#define LM49453_CHIP_EN_SHUTDOWN			0x00
+#define LM49453_CHIP_EN					0x01
+#define LM49453_CHIP_EN_HSD_DETECT			0x02
+#define LM49453_CHIP_EN_INVALID_HSD			0x03
+
+/* LM49453_P0_PLL_CLK_SEL1_REG (0x01h) */
+#define LM49453_CLK_SEL1_MCLK_SEL			0x11
+#define LM49453_CLK_SEL1_RTC_SEL			0x11
+#define LM49453_CLK_SEL1_PORT1_SEL			0x10
+#define LM49453_CLK_SEL1_PORT2_SEL			0x11
+
+/* LM49453_P0_PLL_CLK_SEL2_REG (0x02h) */
+#define LM49453_CLK_SEL2_ADC_CLK_SEL			0x38
+
+/* LM49453_P0_FLL_REF_FREQL_REG (0x0F) */
+#define LM49453_FLL_REF_FREQ_VAL			0x8ca0001
+
+/* LM49453_P0_VCO_TARGETLL_REG (0x11) */
+#define LM49453_VCO_TARGET_VAL				0x8ca0001
+
+/* LM49453_P0_ADC_DSP_REG (0x30h) */
+#define LM49453_ADC_DSP_ADC_MUTEL			BIT(0)
+#define LM49453_ADC_DSP_ADC_MUTER			BIT(1)
+#define LM49453_ADC_DSP_DMIC1_MUTEL			BIT(2)
+#define LM49453_ADC_DSP_DMIC1_MUTER			BIT(3)
+#define LM49453_ADC_DSP_DMIC2_MUTEL			BIT(4)
+#define LM49453_ADC_DSP_DMIC2_MUTER			BIT(5)
+#define LM49453_ADC_DSP_MUTE_ALL			0x3F
+
+/* LM49453_P0_DAC_DSP_REG (0x31h) */
+#define LM49453_DAC_DSP_MUTE_ALL			0xFF
+
+/* LM49453_P0_AUDIO_PORT1_BASIC_REG (0x60h) */
+#define LM49453_AUDIO_PORT1_BASIC_FMT_MASK		(BIT(4)|BIT(3))
+#define LM49453_AUDIO_PORT1_BASIC_CLK_MS		BIT(3)
+#define LM49453_AUDIO_PORT1_BASIC_SYNC_MS		BIT(4)
+
+/* LM49453_P0_RESET_REG (0xFEh) */
+#define LM49453_RESET_REG_RST				BIT(0)
+
+/* Page select register bits (0xFF) */
+#define LM49453_PAGE0_SELECT				0x0
+#define LM49453_PAGE1_SELECT				0x1
+
+/* LM49453_P0_HSD_PIN3_4_CFG_REG (Jack Pin config - 0xD7) */
+#define LM49453_JACK_DISABLE				0x00
+#define LM49453_JACK_CONFIG1				0x01
+#define LM49453_JACK_CONFIG2				0x02
+#define LM49453_JACK_CONFIG3				0x03
+#define LM49453_JACK_CONFIG4				0x04
+#define LM49453_JACK_CONFIG5				0x05
+
+/* Page 1 REGISTERS */
+
+/* SIDETONE */
+#define LM49453_P1_SIDETONE_SA0L_REG			0x80
+#define LM49453_P1_SIDETONE_SA0H_REG			0x81
+#define LM49453_P1_SIDETONE_SAB0U_REG			0x82
+#define LM49453_P1_SIDETONE_SB0L_REG			0x83
+#define LM49453_P1_SIDETONE_SB0H_REG			0x84
+#define LM49453_P1_SIDETONE_SH0L_REG			0x85
+#define LM49453_P1_SIDETONE_SH0H_REG			0x86
+#define LM49453_P1_SIDETONE_SH0U_REG			0x87
+#define LM49453_P1_SIDETONE_SA1L_REG			0x88
+#define LM49453_P1_SIDETONE_SA1H_REG			0x89
+#define LM49453_P1_SIDETONE_SAB1U_REG			0x8A
+#define LM49453_P1_SIDETONE_SB1L_REG			0x8B
+#define LM49453_P1_SIDETONE_SB1H_REG			0x8C
+#define LM49453_P1_SIDETONE_SH1L_REG			0x8D
+#define LM49453_P1_SIDETONE_SH1H_REG			0x8E
+#define LM49453_P1_SIDETONE_SH1U_REG			0x8F
+#define LM49453_P1_SIDETONE_SA2L_REG			0x90
+#define LM49453_P1_SIDETONE_SA2H_REG			0x91
+#define LM49453_P1_SIDETONE_SAB2U_REG			0x92
+#define LM49453_P1_SIDETONE_SB2L_REG			0x93
+#define LM49453_P1_SIDETONE_SB2H_REG			0x94
+#define LM49453_P1_SIDETONE_SH2L_REG			0x95
+#define LM49453_P1_SIDETONE_SH2H_REG			0x96
+#define LM49453_P1_SIDETONE_SH2U_REG			0x97
+#define LM49453_P1_SIDETONE_SA3L_REG			0x98
+#define LM49453_P1_SIDETONE_SA3H_REG			0x99
+#define LM49453_P1_SIDETONE_SAB3U_REG			0x9A
+#define LM49453_P1_SIDETONE_SB3L_REG			0x9B
+#define LM49453_P1_SIDETONE_SB3H_REG			0x9C
+#define LM49453_P1_SIDETONE_SH3L_REG			0x9D
+#define LM49453_P1_SIDETONE_SH3H_REG			0x9E
+#define LM49453_P1_SIDETONE_SH3U_REG			0x9F
+#define LM49453_P1_SIDETONE_SA4L_REG			0xA0
+#define LM49453_P1_SIDETONE_SA4H_REG			0xA1
+#define LM49453_P1_SIDETONE_SAB4U_REG			0xA2
+#define LM49453_P1_SIDETONE_SB4L_REG			0xA3
+#define LM49453_P1_SIDETONE_SB4H_REG			0xA4
+#define LM49453_P1_SIDETONE_SH4L_REG			0xA5
+#define LM49453_P1_SIDETONE_SH4H_REG			0xA6
+#define LM49453_P1_SIDETONE_SH4U_REG			0xA7
+#define LM49453_P1_SIDETONE_SA5L_REG			0xA8
+#define LM49453_P1_SIDETONE_SA5H_REG			0xA9
+#define LM49453_P1_SIDETONE_SAB5U_REG			0xAA
+#define LM49453_P1_SIDETONE_SB5L_REG			0xAB
+#define LM49453_P1_SIDETONE_SB5H_REG			0xAC
+#define LM49453_P1_SIDETONE_SH5L_REG			0xAD
+#define LM49453_P1_SIDETONE_SH5H_REG			0xAE
+#define LM49453_P1_SIDETONE_SH5U_REG			0xAF
+
+/* CHARGE PUMP CONFIG */
+#define LM49453_P1_CP_CONFIG1_REG			0xB0
+#define LM49453_P1_CP_CONFIG2_REG			0xB1
+#define LM49453_P1_CP_CONFIG3_REG			0xB2
+#define LM49453_P1_CP_CONFIG4_REG			0xB3
+#define LM49453_P1_CP_LA_VTH1L_REG			0xB4
+#define LM49453_P1_CP_LA_VTH1M_REG			0xB5
+#define LM49453_P1_CP_LA_VTH2L_REG			0xB6
+#define LM49453_P1_CP_LA_VTH2M_REG			0xB7
+#define LM49453_P1_CP_LA_VTH3L_REG			0xB8
+#define LM49453_P1_CP_LA_VTH3H_REG			0xB9
+#define LM49453_P1_CP_CLK_DIV_REG			0xBA
+
+/* DAC */
+#define LM49453_P1_DAC_CHOP_REG				0xC0
+
+#define	LM49453_CLK_SRC_MCLK				1
+#endif
diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c
index 0bb511a..35179e2 100644
--- a/sound/soc/codecs/max98095.c
+++ b/sound/soc/codecs/max98095.c
@@ -24,6 +24,7 @@
 #include <linux/slab.h>
 #include <asm/div64.h>
 #include <sound/max98095.h>
+#include <sound/jack.h>
 #include "max98095.h"
 
 enum max98095_type {
@@ -51,6 +52,8 @@
 	u8 lin_state;
 	unsigned int mic1pre;
 	unsigned int mic2pre;
+	struct snd_soc_jack *headphone_jack;
+	struct snd_soc_jack *mic_jack;
 };
 
 static const u8 max98095_reg_def[M98095_REG_CNT] = {
@@ -2173,9 +2176,125 @@
 		max98095_handle_bq_pdata(codec);
 }
 
+static irqreturn_t max98095_report_jack(int irq, void *data)
+{
+	struct snd_soc_codec *codec = data;
+	struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+	unsigned int value;
+	int hp_report = 0;
+	int mic_report = 0;
+
+	/* Read the Jack Status Register */
+	value = snd_soc_read(codec, M98095_007_JACK_AUTO_STS);
+
+	/* If ddone is not set, then detection isn't finished yet */
+	if ((value & M98095_DDONE) == 0)
+		return IRQ_NONE;
+
+	/* if hp, check its bit, and if set, clear it */
+	if ((value & M98095_HP_IN || value & M98095_LO_IN) &&
+		max98095->headphone_jack)
+		hp_report |= SND_JACK_HEADPHONE;
+
+	/* if mic, check its bit, and if set, clear it */
+	if ((value & M98095_MIC_IN) && max98095->mic_jack)
+		mic_report |= SND_JACK_MICROPHONE;
+
+	if (max98095->headphone_jack == max98095->mic_jack) {
+		snd_soc_jack_report(max98095->headphone_jack,
+					hp_report | mic_report,
+					SND_JACK_HEADSET);
+	} else {
+		if (max98095->headphone_jack)
+			snd_soc_jack_report(max98095->headphone_jack,
+					hp_report, SND_JACK_HEADPHONE);
+		if (max98095->mic_jack)
+			snd_soc_jack_report(max98095->mic_jack,
+					mic_report, SND_JACK_MICROPHONE);
+	}
+
+	return IRQ_HANDLED;
+}
+
+int max98095_jack_detect_enable(struct snd_soc_codec *codec)
+{
+	struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+	int ret = 0;
+	int detect_enable = M98095_JDEN;
+	unsigned int slew = M98095_DEFAULT_SLEW_DELAY;
+
+	if (max98095->pdata->jack_detect_pin5en)
+		detect_enable |= M98095_PIN5EN;
+
+	if (max98095->pdata->jack_detect_delay)
+		slew = max98095->pdata->jack_detect_delay;
+
+	ret = snd_soc_write(codec, M98095_08E_JACK_DC_SLEW, slew);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to cfg auto detect %d\n", ret);
+		return ret;
+	}
+
+	/* configure auto detection to be enabled */
+	ret = snd_soc_write(codec, M98095_089_JACK_DET_AUTO, detect_enable);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to cfg auto detect %d\n", ret);
+		return ret;
+	}
+
+	return ret;
+}
+
+int max98095_jack_detect_disable(struct snd_soc_codec *codec)
+{
+	int ret = 0;
+
+	/* configure auto detection to be disabled */
+	ret = snd_soc_write(codec, M98095_089_JACK_DET_AUTO, 0x0);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to cfg auto detect %d\n", ret);
+		return ret;
+	}
+
+	return ret;
+}
+
+int max98095_jack_detect(struct snd_soc_codec *codec,
+	struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack)
+{
+	struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+	struct i2c_client *client = to_i2c_client(codec->dev);
+	int ret = 0;
+
+	max98095->headphone_jack = hp_jack;
+	max98095->mic_jack = mic_jack;
+
+	/* only progress if we have at least 1 jack pointer */
+	if (!hp_jack && !mic_jack)
+		return -EINVAL;
+
+	max98095_jack_detect_enable(codec);
+
+	/* enable interrupts for headphone jack detection */
+	ret = snd_soc_update_bits(codec, M98095_013_JACK_INT_EN,
+		M98095_IDDONE, M98095_IDDONE);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to cfg jack irqs %d\n", ret);
+		return ret;
+	}
+
+	max98095_report_jack(client->irq, codec);
+	return 0;
+}
+
 #ifdef CONFIG_PM
 static int max98095_suspend(struct snd_soc_codec *codec)
 {
+	struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+
+	if (max98095->headphone_jack || max98095->mic_jack)
+		max98095_jack_detect_disable(codec);
+
 	max98095_set_bias_level(codec, SND_SOC_BIAS_OFF);
 
 	return 0;
@@ -2183,8 +2302,16 @@
 
 static int max98095_resume(struct snd_soc_codec *codec)
 {
+	struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+	struct i2c_client *client = to_i2c_client(codec->dev);
+
 	max98095_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
+	if (max98095->headphone_jack || max98095->mic_jack) {
+		max98095_jack_detect_enable(codec);
+		max98095_report_jack(client->irq, codec);
+	}
+
 	return 0;
 }
 #else
@@ -2227,6 +2354,7 @@
 {
 	struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
 	struct max98095_cdata *cdata;
+	struct i2c_client *client;
 	int ret = 0;
 
 	ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C);
@@ -2238,6 +2366,8 @@
 	/* reset the codec, the DSP core, and disable all interrupts */
 	max98095_reset(codec);
 
+	client = to_i2c_client(codec->dev);
+
 	/* initialize private data */
 
 	max98095->sysclk = (unsigned)-1;
@@ -2266,11 +2396,23 @@
 	max98095->mic1pre = 0;
 	max98095->mic2pre = 0;
 
+	if (client->irq) {
+		/* register an audio interrupt */
+		ret = request_threaded_irq(client->irq, NULL,
+			max98095_report_jack,
+			IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+			"max98095", codec);
+		if (ret) {
+			dev_err(codec->dev, "Failed to request IRQ: %d\n", ret);
+			goto err_access;
+		}
+	}
+
 	ret = snd_soc_read(codec, M98095_0FF_REV_ID);
 	if (ret < 0) {
 		dev_err(codec->dev, "Failure reading hardware revision: %d\n",
 			ret);
-		goto err_access;
+		goto err_irq;
 	}
 	dev_info(codec->dev, "Hardware revision: %c\n", ret - 0x40 + 'A');
 
@@ -2306,14 +2448,28 @@
 
 	max98095_add_widgets(codec);
 
+	return 0;
+
+err_irq:
+	if (client->irq)
+		free_irq(client->irq, codec);
 err_access:
 	return ret;
 }
 
 static int max98095_remove(struct snd_soc_codec *codec)
 {
+	struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+	struct i2c_client *client = to_i2c_client(codec->dev);
+
 	max98095_set_bias_level(codec, SND_SOC_BIAS_OFF);
 
+	if (max98095->headphone_jack || max98095->mic_jack)
+		max98095_jack_detect_disable(codec);
+
+	if (client->irq)
+		free_irq(client->irq, codec);
+
 	return 0;
 }
 
diff --git a/sound/soc/codecs/max98095.h b/sound/soc/codecs/max98095.h
index 891584a..2ebbe4e 100644
--- a/sound/soc/codecs/max98095.h
+++ b/sound/soc/codecs/max98095.h
@@ -175,11 +175,23 @@
 
 /* MAX98095 Registers Bit Fields */
 
+/* M98095_007_JACK_AUTO_STS */
+	#define M98095_MIC_IN			(1<<3)
+	#define M98095_LO_IN			(1<<5)
+	#define M98095_HP_IN			(1<<6)
+	#define M98095_DDONE			(1<<7)
+
 /* M98095_00F_HOST_CFG */
 	#define M98095_SEG                      (1<<0)
 	#define M98095_XTEN                     (1<<1)
 	#define M98095_MDLLEN                   (1<<2)
 
+/* M98095_013_JACK_INT_EN */
+	#define M98095_IMIC_IN			(1<<3)
+	#define M98095_ILO_IN			(1<<5)
+	#define M98095_IHP_IN			(1<<6)
+	#define M98095_IDDONE			(1<<7)
+
 /* M98095_027_DAI1_CLKMODE, M98095_031_DAI2_CLKMODE, M98095_03B_DAI3_CLKMODE */
 	#define M98095_CLKMODE_MASK             0xFF
 
@@ -255,6 +267,10 @@
 	#define M98095_EQ2EN                    (1<<1)
 	#define M98095_EQ1EN                    (1<<0)
 
+/* M98095_089_JACK_DET_AUTO */
+	#define M98095_PIN5EN			(1<<2)
+	#define M98095_JDEN			(1<<7)
+
 /* M98095_090_PWR_EN_IN */
 	#define M98095_INEN                     (1<<7)
 	#define M98095_MB2EN                    (1<<3)
@@ -296,4 +312,10 @@
 #define M98095_174_DAI1_BQ_BASE             0x74
 #define M98095_17E_DAI2_BQ_BASE             0x7E
 
+/* Default Delay used in Slew Rate Calculation for Jack detection */
+#define M98095_DEFAULT_SLEW_DELAY		0x18
+
+extern int max98095_jack_detect(struct snd_soc_codec *codec,
+	struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack);
+
 #endif
diff --git a/sound/soc/codecs/mc13783.c b/sound/soc/codecs/mc13783.c
new file mode 100644
index 0000000..6276e35
--- /dev/null
+++ b/sound/soc/codecs/mc13783.c
@@ -0,0 +1,786 @@
+/*
+ * Copyright 2008 Juergen Beisert, kernel@pengutronix.de
+ * Copyright 2009 Sascha Hauer, s.hauer@pengutronix.de
+ * Copyright 2012 Philippe Retornaz, philippe.retornaz@epfl.ch
+ *
+ * Initial development of this code was funded by
+ * Phytec Messtechnik GmbH, http://www.phytec.de
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA  02110-1301, USA.
+ */
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/mfd/mc13xxx.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/soc-dapm.h>
+
+#include "mc13783.h"
+
+#define MC13783_AUDIO_RX0	36
+#define MC13783_AUDIO_RX1	37
+#define MC13783_AUDIO_TX	38
+#define MC13783_SSI_NETWORK	39
+#define MC13783_AUDIO_CODEC	40
+#define MC13783_AUDIO_DAC	41
+
+#define AUDIO_RX0_ALSPEN		(1 << 5)
+#define AUDIO_RX0_ALSPSEL		(1 << 7)
+#define AUDIO_RX0_ADDCDC		(1 << 21)
+#define AUDIO_RX0_ADDSTDC		(1 << 22)
+#define AUDIO_RX0_ADDRXIN		(1 << 23)
+
+#define AUDIO_RX1_PGARXEN		(1 << 0);
+#define AUDIO_RX1_PGASTEN		(1 << 5)
+#define AUDIO_RX1_ARXINEN		(1 << 10)
+
+#define AUDIO_TX_AMC1REN		(1 << 5)
+#define AUDIO_TX_AMC1LEN		(1 << 7)
+#define AUDIO_TX_AMC2EN			(1 << 9)
+#define AUDIO_TX_ATXINEN		(1 << 11)
+#define AUDIO_TX_RXINREC		(1 << 13)
+
+#define SSI_NETWORK_CDCTXRXSLOT(x)	(((x) & 0x3) << 2)
+#define SSI_NETWORK_CDCTXSECSLOT(x)	(((x) & 0x3) << 4)
+#define SSI_NETWORK_CDCRXSECSLOT(x)	(((x) & 0x3) << 6)
+#define SSI_NETWORK_CDCRXSECGAIN(x)	(((x) & 0x3) << 8)
+#define SSI_NETWORK_CDCSUMGAIN(x)	(1 << 10)
+#define SSI_NETWORK_CDCFSDLY(x)		(1 << 11)
+#define SSI_NETWORK_DAC_SLOTS_8		(1 << 12)
+#define SSI_NETWORK_DAC_SLOTS_4		(2 << 12)
+#define SSI_NETWORK_DAC_SLOTS_2		(3 << 12)
+#define SSI_NETWORK_DAC_SLOT_MASK	(3 << 12)
+#define SSI_NETWORK_DAC_RXSLOT_0_1	(0 << 14)
+#define SSI_NETWORK_DAC_RXSLOT_2_3	(1 << 14)
+#define SSI_NETWORK_DAC_RXSLOT_4_5	(2 << 14)
+#define SSI_NETWORK_DAC_RXSLOT_6_7	(3 << 14)
+#define SSI_NETWORK_DAC_RXSLOT_MASK	(3 << 14)
+#define SSI_NETWORK_STDCRXSECSLOT(x)	(((x) & 0x3) << 16)
+#define SSI_NETWORK_STDCRXSECGAIN(x)	(((x) & 0x3) << 18)
+#define SSI_NETWORK_STDCSUMGAIN		(1 << 20)
+
+/*
+ * MC13783_AUDIO_CODEC and MC13783_AUDIO_DAC mostly share the same
+ * register layout
+ */
+#define AUDIO_SSI_SEL			(1 << 0)
+#define AUDIO_CLK_SEL			(1 << 1)
+#define AUDIO_CSM			(1 << 2)
+#define AUDIO_BCL_INV			(1 << 3)
+#define AUDIO_CFS_INV			(1 << 4)
+#define AUDIO_CFS(x)			(((x) & 0x3) << 5)
+#define AUDIO_CLK(x)			(((x) & 0x7) << 7)
+#define AUDIO_C_EN			(1 << 11)
+#define AUDIO_C_CLK_EN			(1 << 12)
+#define AUDIO_C_RESET			(1 << 15)
+
+#define AUDIO_CODEC_CDCFS8K16K		(1 << 10)
+#define AUDIO_DAC_CFS_DLY_B		(1 << 10)
+
+struct mc13783_priv {
+	struct snd_soc_codec codec;
+	struct mc13xxx *mc13xxx;
+
+	enum mc13783_ssi_port adc_ssi_port;
+	enum mc13783_ssi_port dac_ssi_port;
+};
+
+static unsigned int mc13783_read(struct snd_soc_codec *codec,
+	unsigned int reg)
+{
+	struct mc13783_priv *priv = snd_soc_codec_get_drvdata(codec);
+	unsigned int value = 0;
+
+	mc13xxx_lock(priv->mc13xxx);
+
+	mc13xxx_reg_read(priv->mc13xxx, reg, &value);
+
+	mc13xxx_unlock(priv->mc13xxx);
+
+	return value;
+}
+
+static int mc13783_write(struct snd_soc_codec *codec,
+	unsigned int reg, unsigned int value)
+{
+	struct mc13783_priv *priv = snd_soc_codec_get_drvdata(codec);
+	int ret;
+
+	mc13xxx_lock(priv->mc13xxx);
+
+	ret = mc13xxx_reg_write(priv->mc13xxx, reg, value);
+
+	mc13xxx_unlock(priv->mc13xxx);
+
+	return ret;
+}
+
+/* Mapping between sample rates and register value */
+static unsigned int mc13783_rates[] = {
+	8000, 11025, 12000, 16000,
+	22050, 24000, 32000, 44100,
+	48000, 64000, 96000
+};
+
+static int mc13783_pcm_hw_params_dac(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params,
+				struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_codec *codec = rtd->codec;
+	unsigned int rate = params_rate(params);
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(mc13783_rates); i++) {
+		if (rate == mc13783_rates[i]) {
+			snd_soc_update_bits(codec, MC13783_AUDIO_DAC,
+					0xf << 17, i << 17);
+			return 0;
+		}
+	}
+
+	return -EINVAL;
+}
+
+static int mc13783_pcm_hw_params_codec(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params,
+				struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_codec *codec = rtd->codec;
+	unsigned int rate = params_rate(params);
+	unsigned int val;
+
+	switch (rate) {
+	case 8000:
+		val = 0;
+		break;
+	case 16000:
+		val = AUDIO_CODEC_CDCFS8K16K;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	snd_soc_update_bits(codec, MC13783_AUDIO_CODEC, AUDIO_CODEC_CDCFS8K16K,
+			val);
+
+	return 0;
+}
+
+static int mc13783_pcm_hw_params_sync(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params,
+				struct snd_soc_dai *dai)
+{
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		return mc13783_pcm_hw_params_dac(substream, params, dai);
+	else
+		return mc13783_pcm_hw_params_codec(substream, params, dai);
+}
+
+static int mc13783_set_fmt(struct snd_soc_dai *dai, unsigned int fmt,
+			unsigned int reg)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	unsigned int val = 0;
+	unsigned int mask = AUDIO_CFS(3) | AUDIO_BCL_INV | AUDIO_CFS_INV |
+				AUDIO_CSM | AUDIO_C_CLK_EN | AUDIO_C_RESET;
+
+
+	/* DAI mode */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		val |= AUDIO_CFS(2);
+		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		val |= AUDIO_CFS(1);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* DAI clock inversion */
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		val |= AUDIO_BCL_INV;
+		break;
+	case SND_SOC_DAIFMT_NB_IF:
+		val |= AUDIO_BCL_INV | AUDIO_CFS_INV;
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		break;
+	case SND_SOC_DAIFMT_IB_IF:
+		val |= AUDIO_CFS_INV;
+		break;
+	}
+
+	/* DAI clock master masks */
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		val |= AUDIO_C_CLK_EN;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		val |= AUDIO_CSM;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFS:
+	case SND_SOC_DAIFMT_CBS_CFM:
+		return -EINVAL;
+	}
+
+	val |= AUDIO_C_RESET;
+
+	snd_soc_update_bits(codec, reg, mask, val);
+
+	return 0;
+}
+
+static int mc13783_set_fmt_async(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	if (dai->id == MC13783_ID_STEREO_DAC)
+		return mc13783_set_fmt(dai, fmt, MC13783_AUDIO_DAC);
+	else
+		return mc13783_set_fmt(dai, fmt, MC13783_AUDIO_CODEC);
+}
+
+static int mc13783_set_fmt_sync(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	int ret;
+
+	ret = mc13783_set_fmt(dai, fmt, MC13783_AUDIO_DAC);
+	if (ret)
+		return ret;
+
+	/*
+	 * In synchronous mode force the voice codec into slave mode
+	 * so that the clock / framesync from the stereo DAC is used
+	 */
+	fmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
+	fmt |= SND_SOC_DAIFMT_CBS_CFS;
+	ret = mc13783_set_fmt(dai, fmt, MC13783_AUDIO_CODEC);
+
+	return ret;
+}
+
+static int mc13783_sysclk[] = {
+	13000000,
+	15360000,
+	16800000,
+	-1,
+	26000000,
+	-1, /* 12000000, invalid for voice codec */
+	-1, /* 3686400, invalid for voice codec */
+	33600000,
+};
+
+static int mc13783_set_sysclk(struct snd_soc_dai *dai,
+				  int clk_id, unsigned int freq, int dir,
+				  unsigned int reg)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	int clk;
+	unsigned int val = 0;
+	unsigned int mask = AUDIO_CLK(0x7) | AUDIO_CLK_SEL;
+
+	for (clk = 0; clk < ARRAY_SIZE(mc13783_sysclk); clk++) {
+		if (mc13783_sysclk[clk] < 0)
+			continue;
+		if (mc13783_sysclk[clk] == freq)
+			break;
+	}
+
+	if (clk == ARRAY_SIZE(mc13783_sysclk))
+		return -EINVAL;
+
+	if (clk_id == MC13783_CLK_CLIB)
+		val |= AUDIO_CLK_SEL;
+
+	val |= AUDIO_CLK(clk);
+
+	snd_soc_update_bits(codec, reg, mask, val);
+
+	return 0;
+}
+
+static int mc13783_set_sysclk_dac(struct snd_soc_dai *dai,
+				  int clk_id, unsigned int freq, int dir)
+{
+	return mc13783_set_sysclk(dai, clk_id, freq, dir, MC13783_AUDIO_DAC);
+}
+
+static int mc13783_set_sysclk_codec(struct snd_soc_dai *dai,
+				  int clk_id, unsigned int freq, int dir)
+{
+	return mc13783_set_sysclk(dai, clk_id, freq, dir, MC13783_AUDIO_CODEC);
+}
+
+static int mc13783_set_sysclk_sync(struct snd_soc_dai *dai,
+				  int clk_id, unsigned int freq, int dir)
+{
+	int ret;
+
+	ret = mc13783_set_sysclk(dai, clk_id, freq, dir, MC13783_AUDIO_DAC);
+	if (ret)
+		return ret;
+
+	return mc13783_set_sysclk(dai, clk_id, freq, dir, MC13783_AUDIO_CODEC);
+}
+
+static int mc13783_set_tdm_slot_dac(struct snd_soc_dai *dai,
+	unsigned int tx_mask, unsigned int rx_mask, int slots,
+	int slot_width)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	unsigned int val = 0;
+	unsigned int mask = SSI_NETWORK_DAC_SLOT_MASK |
+				SSI_NETWORK_DAC_RXSLOT_MASK;
+
+	switch (slots) {
+	case 2:
+		val |= SSI_NETWORK_DAC_SLOTS_2;
+		break;
+	case 4:
+		val |= SSI_NETWORK_DAC_SLOTS_4;
+		break;
+	case 8:
+		val |= SSI_NETWORK_DAC_SLOTS_8;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (rx_mask) {
+	case 0xfffffffc:
+		val |= SSI_NETWORK_DAC_RXSLOT_0_1;
+		break;
+	case 0xfffffff3:
+		val |= SSI_NETWORK_DAC_RXSLOT_2_3;
+		break;
+	case 0xffffffcf:
+		val |= SSI_NETWORK_DAC_RXSLOT_4_5;
+		break;
+	case 0xffffff3f:
+		val |= SSI_NETWORK_DAC_RXSLOT_6_7;
+		break;
+	default:
+		return -EINVAL;
+	};
+
+	snd_soc_update_bits(codec, MC13783_SSI_NETWORK, mask, val);
+
+	return 0;
+}
+
+static int mc13783_set_tdm_slot_codec(struct snd_soc_dai *dai,
+	unsigned int tx_mask, unsigned int rx_mask, int slots,
+	int slot_width)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	unsigned int val = 0;
+	unsigned int mask = 0x3f;
+
+	if (slots != 4)
+		return -EINVAL;
+
+	if (tx_mask != 0xfffffffc)
+		return -EINVAL;
+
+	val |= (0x00 << 2);	/* primary timeslot RX/TX(?) is 0 */
+	val |= (0x01 << 4);	/* secondary timeslot TX is 1 */
+
+	snd_soc_update_bits(codec, MC13783_SSI_NETWORK, mask, val);
+
+	return 0;
+}
+
+static int mc13783_set_tdm_slot_sync(struct snd_soc_dai *dai,
+	unsigned int tx_mask, unsigned int rx_mask, int slots,
+	int slot_width)
+{
+	int ret;
+
+	ret = mc13783_set_tdm_slot_dac(dai, tx_mask, rx_mask, slots,
+			slot_width);
+	if (ret)
+		return ret;
+
+	ret = mc13783_set_tdm_slot_codec(dai, tx_mask, rx_mask, slots,
+			slot_width);
+
+	return ret;
+}
+
+static const struct snd_kcontrol_new mc1l_amp_ctl =
+	SOC_DAPM_SINGLE("Switch", 38, 7, 1, 0);
+
+static const struct snd_kcontrol_new mc1r_amp_ctl =
+	SOC_DAPM_SINGLE("Switch", 38, 5, 1, 0);
+
+static const struct snd_kcontrol_new mc2_amp_ctl =
+	SOC_DAPM_SINGLE("Switch", 38, 9, 1, 0);
+
+static const struct snd_kcontrol_new atx_amp_ctl =
+	SOC_DAPM_SINGLE("Switch", 38, 11, 1, 0);
+
+
+/* Virtual mux. The chip does the input selection automatically
+ * as soon as we enable one input. */
+static const char * const adcl_enum_text[] = {
+	"MC1L", "RXINL",
+};
+
+static const struct soc_enum adcl_enum =
+	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(adcl_enum_text), adcl_enum_text);
+
+static const struct snd_kcontrol_new left_input_mux =
+	SOC_DAPM_ENUM_VIRT("Route", adcl_enum);
+
+static const char * const adcr_enum_text[] = {
+	"MC1R", "MC2", "RXINR", "TXIN",
+};
+
+static const struct soc_enum adcr_enum =
+	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(adcr_enum_text), adcr_enum_text);
+
+static const struct snd_kcontrol_new right_input_mux =
+	SOC_DAPM_ENUM_VIRT("Route", adcr_enum);
+
+static const struct snd_kcontrol_new samp_ctl =
+	SOC_DAPM_SINGLE("Switch", 36, 3, 1, 0);
+
+static const struct snd_kcontrol_new lamp_ctl =
+	SOC_DAPM_SINGLE("Switch", 36, 5, 1, 0);
+
+static const struct snd_kcontrol_new hlamp_ctl =
+	SOC_DAPM_SINGLE("Switch", 36, 10, 1, 0);
+
+static const struct snd_kcontrol_new hramp_ctl =
+	SOC_DAPM_SINGLE("Switch", 36, 9, 1, 0);
+
+static const struct snd_kcontrol_new llamp_ctl =
+	SOC_DAPM_SINGLE("Switch", 36, 16, 1, 0);
+
+static const struct snd_kcontrol_new lramp_ctl =
+	SOC_DAPM_SINGLE("Switch", 36, 15, 1, 0);
+
+static const struct snd_soc_dapm_widget mc13783_dapm_widgets[] = {
+/* Input */
+	SND_SOC_DAPM_INPUT("MC1LIN"),
+	SND_SOC_DAPM_INPUT("MC1RIN"),
+	SND_SOC_DAPM_INPUT("MC2IN"),
+	SND_SOC_DAPM_INPUT("RXINR"),
+	SND_SOC_DAPM_INPUT("RXINL"),
+	SND_SOC_DAPM_INPUT("TXIN"),
+
+	SND_SOC_DAPM_SUPPLY("MC1 Bias", 38, 0, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("MC2 Bias", 38, 1, 0, NULL, 0),
+
+	SND_SOC_DAPM_SWITCH("MC1L Amp", 38, 7, 0, &mc1l_amp_ctl),
+	SND_SOC_DAPM_SWITCH("MC1R Amp", 38, 5, 0, &mc1r_amp_ctl),
+	SND_SOC_DAPM_SWITCH("MC2 Amp", 38, 9, 0, &mc2_amp_ctl),
+	SND_SOC_DAPM_SWITCH("TXIN Amp", 38, 11, 0, &atx_amp_ctl),
+
+	SND_SOC_DAPM_VIRT_MUX("PGA Left Input Mux", SND_SOC_NOPM, 0, 0,
+			      &left_input_mux),
+	SND_SOC_DAPM_VIRT_MUX("PGA Right Input Mux", SND_SOC_NOPM, 0, 0,
+			      &right_input_mux),
+
+	SND_SOC_DAPM_PGA("PGA Left Input", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("PGA Right Input", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+	SND_SOC_DAPM_ADC("ADC", "Capture", 40, 11, 0),
+	SND_SOC_DAPM_SUPPLY("ADC_Reset", 40, 15, 0, NULL, 0),
+
+/* Output */
+	SND_SOC_DAPM_SUPPLY("DAC_E", 41, 11, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("DAC_Reset", 41, 15, 0, NULL, 0),
+	SND_SOC_DAPM_OUTPUT("RXOUTL"),
+	SND_SOC_DAPM_OUTPUT("RXOUTR"),
+	SND_SOC_DAPM_OUTPUT("HSL"),
+	SND_SOC_DAPM_OUTPUT("HSR"),
+	SND_SOC_DAPM_OUTPUT("LSP"),
+	SND_SOC_DAPM_OUTPUT("SP"),
+
+	SND_SOC_DAPM_SWITCH("Speaker Amp", 36, 3, 0, &samp_ctl),
+	SND_SOC_DAPM_SWITCH("Loudspeaker Amp", SND_SOC_NOPM, 0, 0, &lamp_ctl),
+	SND_SOC_DAPM_SWITCH("Headset Amp Left", 36, 10, 0, &hlamp_ctl),
+	SND_SOC_DAPM_SWITCH("Headset Amp Right", 36, 9, 0, &hramp_ctl),
+	SND_SOC_DAPM_SWITCH("Line out Amp Left", 36, 16, 0, &llamp_ctl),
+	SND_SOC_DAPM_SWITCH("Line out Amp Right", 36, 15, 0, &lramp_ctl),
+	SND_SOC_DAPM_DAC("DAC", "Playback", 36, 22, 0),
+	SND_SOC_DAPM_PGA("DAC PGA", 37, 5, 0, NULL, 0),
+};
+
+static struct snd_soc_dapm_route mc13783_routes[] = {
+/* Input */
+	{ "MC1L Amp", NULL, "MC1LIN"},
+	{ "MC1R Amp", NULL, "MC1RIN" },
+	{ "MC2 Amp", NULL, "MC2IN" },
+	{ "TXIN Amp", NULL, "TXIN"},
+
+	{ "PGA Left Input Mux", "MC1L", "MC1L Amp" },
+	{ "PGA Left Input Mux", "RXINL", "RXINL"},
+	{ "PGA Right Input Mux", "MC1R", "MC1R Amp" },
+	{ "PGA Right Input Mux", "MC2",  "MC2 Amp"},
+	{ "PGA Right Input Mux", "TXIN", "TXIN Amp"},
+	{ "PGA Right Input Mux", "RXINR", "RXINR"},
+
+	{ "PGA Left Input", NULL, "PGA Left Input Mux"},
+	{ "PGA Right Input", NULL, "PGA Right Input Mux"},
+
+	{ "ADC", NULL, "PGA Left Input"},
+	{ "ADC", NULL, "PGA Right Input"},
+	{ "ADC", NULL, "ADC_Reset"},
+
+/* Output */
+	{ "HSL", NULL, "Headset Amp Left" },
+	{ "HSR", NULL, "Headset Amp Right"},
+	{ "RXOUTL", NULL, "Line out Amp Left"},
+	{ "RXOUTR", NULL, "Line out Amp Right"},
+	{ "SP", NULL, "Speaker Amp"},
+	{ "Speaker Amp", NULL, "DAC PGA"},
+	{ "LSP", NULL, "DAC PGA"},
+	{ "Headset Amp Left", NULL, "DAC PGA"},
+	{ "Headset Amp Right", NULL, "DAC PGA"},
+	{ "Line out Amp Left", NULL, "DAC PGA"},
+	{ "Line out Amp Right", NULL, "DAC PGA"},
+	{ "DAC PGA", NULL, "DAC"},
+	{ "DAC", NULL, "DAC_E"},
+};
+
+static const char * const mc13783_3d_mixer[] = {"Stereo", "Phase Mix",
+						"Mono", "Mono Mix"};
+
+static const struct soc_enum mc13783_enum_3d_mixer =
+	SOC_ENUM_SINGLE(MC13783_AUDIO_RX1, 16, ARRAY_SIZE(mc13783_3d_mixer),
+			mc13783_3d_mixer);
+
+static struct snd_kcontrol_new mc13783_control_list[] = {
+	SOC_SINGLE("Loudspeaker enable", MC13783_AUDIO_RX0, 5, 1, 0),
+	SOC_SINGLE("PCM Playback Volume", MC13783_AUDIO_RX1, 6, 15, 0),
+	SOC_DOUBLE("PCM Capture Volume", MC13783_AUDIO_TX, 19, 14, 31, 0),
+	SOC_ENUM("3D Control", mc13783_enum_3d_mixer),
+};
+
+static int mc13783_probe(struct snd_soc_codec *codec)
+{
+	struct mc13783_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+	mc13xxx_lock(priv->mc13xxx);
+
+	/* these are the reset values */
+	mc13xxx_reg_write(priv->mc13xxx, MC13783_AUDIO_RX0, 0x25893);
+	mc13xxx_reg_write(priv->mc13xxx, MC13783_AUDIO_RX1, 0x00d35A);
+	mc13xxx_reg_write(priv->mc13xxx, MC13783_AUDIO_TX, 0x420000);
+	mc13xxx_reg_write(priv->mc13xxx, MC13783_SSI_NETWORK, 0x013060);
+	mc13xxx_reg_write(priv->mc13xxx, MC13783_AUDIO_CODEC, 0x180027);
+	mc13xxx_reg_write(priv->mc13xxx, MC13783_AUDIO_DAC, 0x0e0004);
+
+	if (priv->adc_ssi_port == MC13783_SSI1_PORT)
+		mc13xxx_reg_rmw(priv->mc13xxx, MC13783_AUDIO_CODEC,
+				AUDIO_SSI_SEL, 0);
+	else
+		mc13xxx_reg_rmw(priv->mc13xxx, MC13783_AUDIO_CODEC,
+				0, AUDIO_SSI_SEL);
+
+	if (priv->dac_ssi_port == MC13783_SSI1_PORT)
+		mc13xxx_reg_rmw(priv->mc13xxx, MC13783_AUDIO_DAC,
+				AUDIO_SSI_SEL, 0);
+	else
+		mc13xxx_reg_rmw(priv->mc13xxx, MC13783_AUDIO_DAC,
+				0, AUDIO_SSI_SEL);
+
+	mc13xxx_unlock(priv->mc13xxx);
+
+	return 0;
+}
+
+static int mc13783_remove(struct snd_soc_codec *codec)
+{
+	struct mc13783_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+	mc13xxx_lock(priv->mc13xxx);
+
+	/* Make sure VAUDIOON is off */
+	mc13xxx_reg_rmw(priv->mc13xxx, MC13783_AUDIO_RX0, 0x3, 0);
+
+	mc13xxx_unlock(priv->mc13xxx);
+
+	return 0;
+}
+
+#define MC13783_RATES_RECORD (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000)
+
+#define MC13783_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+	SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops mc13783_ops_dac = {
+	.hw_params	= mc13783_pcm_hw_params_dac,
+	.set_fmt	= mc13783_set_fmt_async,
+	.set_sysclk	= mc13783_set_sysclk_dac,
+	.set_tdm_slot	= mc13783_set_tdm_slot_dac,
+};
+
+static struct snd_soc_dai_ops mc13783_ops_codec = {
+	.hw_params	= mc13783_pcm_hw_params_codec,
+	.set_fmt	= mc13783_set_fmt_async,
+	.set_sysclk	= mc13783_set_sysclk_codec,
+	.set_tdm_slot	= mc13783_set_tdm_slot_codec,
+};
+
+/*
+ * The mc13783 has two SSI ports, both of them can be routed either
+ * to the voice codec or the stereo DAC. When two different SSI ports
+ * are used for the voice codec and the stereo DAC we can do different
+ * formats and sysclock settings for playback and capture
+ * (mc13783-hifi-playback and mc13783-hifi-capture). Using the same port
+ * forces us to use symmetric rates (mc13783-hifi).
+ */
+static struct snd_soc_dai_driver mc13783_dai_async[] = {
+	{
+		.name = "mc13783-hifi-playback",
+		.id = MC13783_ID_STEREO_DAC,
+		.playback = {
+			.stream_name = "Playback",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_96000,
+			.formats = MC13783_FORMATS,
+		},
+		.ops = &mc13783_ops_dac,
+	}, {
+		.name = "mc13783-hifi-capture",
+		.id = MC13783_ID_STEREO_CODEC,
+		.capture = {
+			.stream_name = "Capture",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = MC13783_RATES_RECORD,
+			.formats = MC13783_FORMATS,
+		},
+		.ops = &mc13783_ops_codec,
+	},
+};
+
+static struct snd_soc_dai_ops mc13783_ops_sync = {
+	.hw_params	= mc13783_pcm_hw_params_sync,
+	.set_fmt	= mc13783_set_fmt_sync,
+	.set_sysclk	= mc13783_set_sysclk_sync,
+	.set_tdm_slot	= mc13783_set_tdm_slot_sync,
+};
+
+static struct snd_soc_dai_driver mc13783_dai_sync[] = {
+	{
+		.name = "mc13783-hifi",
+		.id = MC13783_ID_SYNC,
+		.playback = {
+			.stream_name = "Playback",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_96000,
+			.formats = MC13783_FORMATS,
+		},
+		.capture = {
+			.stream_name = "Capture",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = MC13783_RATES_RECORD,
+			.formats = MC13783_FORMATS,
+		},
+		.ops = &mc13783_ops_sync,
+		.symmetric_rates = 1,
+	}
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_mc13783 = {
+	.probe		= mc13783_probe,
+	.remove		= mc13783_remove,
+	.read		= mc13783_read,
+	.write		= mc13783_write,
+	.controls	= mc13783_control_list,
+	.num_controls	= ARRAY_SIZE(mc13783_control_list),
+	.dapm_widgets	= mc13783_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(mc13783_dapm_widgets),
+	.dapm_routes	= mc13783_routes,
+	.num_dapm_routes = ARRAY_SIZE(mc13783_routes),
+};
+
+static int mc13783_codec_probe(struct platform_device *pdev)
+{
+	struct mc13xxx *mc13xxx;
+	struct mc13783_priv *priv;
+	struct mc13xxx_codec_platform_data *pdata = pdev->dev.platform_data;
+	int ret;
+
+	mc13xxx = dev_get_drvdata(pdev->dev.parent);
+
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (priv == NULL)
+		return -ENOMEM;
+
+	dev_set_drvdata(&pdev->dev, priv);
+	priv->mc13xxx = mc13xxx;
+	if (pdata) {
+		priv->adc_ssi_port = pdata->adc_ssi_port;
+		priv->dac_ssi_port = pdata->dac_ssi_port;
+	} else {
+		priv->adc_ssi_port = MC13783_SSI1_PORT;
+		priv->dac_ssi_port = MC13783_SSI2_PORT;
+	}
+
+	if (priv->adc_ssi_port == priv->dac_ssi_port)
+		ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_mc13783,
+			mc13783_dai_sync, ARRAY_SIZE(mc13783_dai_sync));
+	else
+		ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_mc13783,
+			mc13783_dai_async, ARRAY_SIZE(mc13783_dai_async));
+
+	if (ret)
+		goto err_register_codec;
+
+	return 0;
+
+err_register_codec:
+	dev_err(&pdev->dev, "register codec failed with %d\n", ret);
+
+	return ret;
+}
+
+static int mc13783_codec_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_codec(&pdev->dev);
+
+	return 0;
+}
+
+static struct platform_driver mc13783_codec_driver = {
+	.driver = {
+		   .name = "mc13783-codec",
+		   .owner = THIS_MODULE,
+		   },
+	.probe = mc13783_codec_probe,
+	.remove = __devexit_p(mc13783_codec_remove),
+};
+
+module_platform_driver(mc13783_codec_driver);
+
+MODULE_DESCRIPTION("ASoC MC13783 driver");
+MODULE_AUTHOR("Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>");
+MODULE_AUTHOR("Philippe Retornaz <philippe.retornaz@epfl.ch>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/mc13783.h b/sound/soc/codecs/mc13783.h
new file mode 100644
index 0000000..3a6d199
--- /dev/null
+++ b/sound/soc/codecs/mc13783.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2008 Juergen Beisert, kernel@pengutronix.de
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef MC13783_MIXER_H
+#define MC13783_MIXER_H
+
+#define MC13783_CLK_CLIA	1
+#define MC13783_CLK_CLIB	2
+
+#define MC13783_ID_STEREO_DAC	1
+#define MC13783_ID_STEREO_CODEC	2
+#define MC13783_ID_SYNC		3
+
+#endif /* MC13783_MIXER_H */
diff --git a/sound/soc/codecs/ml26124.c b/sound/soc/codecs/ml26124.c
new file mode 100644
index 0000000..22cb5bf
--- /dev/null
+++ b/sound/soc/codecs/ml26124.c
@@ -0,0 +1,681 @@
+/*
+ * Copyright (C) 2011 LAPIS Semiconductor Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include "ml26124.h"
+
+#define DVOL_CTL_DVMUTE_ON		BIT(4)	/* Digital volume MUTE On */
+#define DVOL_CTL_DVMUTE_OFF		0	/* Digital volume MUTE Off */
+#define ML26124_SAI_NO_DELAY	BIT(1)
+#define ML26124_SAI_FRAME_SYNC	(BIT(5) | BIT(0)) /* For mono (Telecodec) */
+#define ML26134_CACHESIZE 212
+#define ML26124_VMID	BIT(1)
+#define ML26124_RATES (SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 |\
+		       SNDRV_PCM_RATE_48000)
+#define ML26124_FORMATS (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |\
+			 SNDRV_PCM_FMTBIT_S32_LE)
+#define ML26124_NUM_REGISTER ML26134_CACHESIZE
+
+struct ml26124_priv {
+	u32 mclk;
+	u32 rate;
+	struct regmap *regmap;
+	int clk_in;
+	struct snd_pcm_substream *substream;
+};
+
+struct clk_coeff {
+	u32 mclk;
+	u32 rate;
+	u8 pllnl;
+	u8 pllnh;
+	u8 pllml;
+	u8 pllmh;
+	u8 plldiv;
+};
+
+/* ML26124 configuration */
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -7150, 50, 0);
+
+static const DECLARE_TLV_DB_SCALE(alclvl, -2250, 150, 0);
+static const DECLARE_TLV_DB_SCALE(mingain, -1200, 600, 0);
+static const DECLARE_TLV_DB_SCALE(maxgain, -675, 600, 0);
+static const DECLARE_TLV_DB_SCALE(boost_vol, -1200, 75, 0);
+static const DECLARE_TLV_DB_SCALE(ngth, -7650, 150, 0);
+
+static const char * const ml26124_companding[] = {"16bit PCM", "u-law",
+						  "A-law"};
+
+static const struct soc_enum ml26124_adc_companding_enum
+	= SOC_ENUM_SINGLE(ML26124_SAI_TRANS_CTL, 6, 3, ml26124_companding);
+
+static const struct soc_enum ml26124_dac_companding_enum
+	= SOC_ENUM_SINGLE(ML26124_SAI_RCV_CTL, 6, 3, ml26124_companding);
+
+static const struct snd_kcontrol_new ml26124_snd_controls[] = {
+	SOC_SINGLE_TLV("Capture Digital Volume", ML26124_RECORD_DIG_VOL, 0,
+			0xff, 1, digital_tlv),
+	SOC_SINGLE_TLV("Playback Digital Volume", ML26124_PLBAK_DIG_VOL, 0,
+			0xff, 1, digital_tlv),
+	SOC_SINGLE_TLV("Digital Boost Volume", ML26124_DIGI_BOOST_VOL, 0,
+			0x3f, 0, boost_vol),
+	SOC_SINGLE_TLV("EQ Band0 Volume", ML26124_EQ_GAIN_BRAND0, 0,
+			0xff, 1, digital_tlv),
+	SOC_SINGLE_TLV("EQ Band1 Volume", ML26124_EQ_GAIN_BRAND1, 0,
+			0xff, 1, digital_tlv),
+	SOC_SINGLE_TLV("EQ Band2 Volume", ML26124_EQ_GAIN_BRAND2, 0,
+			0xff, 1, digital_tlv),
+	SOC_SINGLE_TLV("EQ Band3 Volume", ML26124_EQ_GAIN_BRAND3, 0,
+			0xff, 1, digital_tlv),
+	SOC_SINGLE_TLV("EQ Band4 Volume", ML26124_EQ_GAIN_BRAND4, 0,
+			0xff, 1, digital_tlv),
+	SOC_SINGLE_TLV("ALC Target Level", ML26124_ALC_TARGET_LEV, 0,
+			0xf, 1, alclvl),
+	SOC_SINGLE_TLV("ALC Min Input Volume", ML26124_ALC_MAXMIN_GAIN, 0,
+			7, 0, mingain),
+	SOC_SINGLE_TLV("ALC Max Input Volume", ML26124_ALC_MAXMIN_GAIN, 4,
+			7, 1, maxgain),
+	SOC_SINGLE_TLV("Playback Limiter Min Input Volume",
+			ML26124_PL_MAXMIN_GAIN, 0, 7, 0, mingain),
+	SOC_SINGLE_TLV("Playback Limiter Max Input Volume",
+			ML26124_PL_MAXMIN_GAIN, 4, 7, 1, maxgain),
+	SOC_SINGLE_TLV("Playback Boost Volume", ML26124_PLYBAK_BOST_VOL, 0,
+			0x3f, 0, boost_vol),
+	SOC_SINGLE("DC High Pass Filter Switch", ML26124_FILTER_EN, 0, 1, 0),
+	SOC_SINGLE("Noise High Pass Filter Switch", ML26124_FILTER_EN, 1, 1, 0),
+	SOC_SINGLE("ZC Switch", ML26124_PW_ZCCMP_PW_MNG, 1,
+		    1, 0),
+	SOC_SINGLE("EQ Band0 Switch", ML26124_FILTER_EN, 2, 1, 0),
+	SOC_SINGLE("EQ Band1 Switch", ML26124_FILTER_EN, 3, 1, 0),
+	SOC_SINGLE("EQ Band2 Switch", ML26124_FILTER_EN, 4, 1, 0),
+	SOC_SINGLE("EQ Band3 Switch", ML26124_FILTER_EN, 5, 1, 0),
+	SOC_SINGLE("EQ Band4 Switch", ML26124_FILTER_EN, 6, 1, 0),
+	SOC_SINGLE("Play Limiter", ML26124_DVOL_CTL, 0, 1, 0),
+	SOC_SINGLE("Capture Limiter", ML26124_DVOL_CTL, 1, 1, 0),
+	SOC_SINGLE("Digital Volume Fade Switch", ML26124_DVOL_CTL, 3, 1, 0),
+	SOC_SINGLE("Digital Switch", ML26124_DVOL_CTL, 4, 1, 0),
+	SOC_ENUM("DAC Companding", ml26124_dac_companding_enum),
+	SOC_ENUM("ADC Companding", ml26124_adc_companding_enum),
+};
+
+static const struct snd_kcontrol_new ml26124_output_mixer_controls[] = {
+	SOC_DAPM_SINGLE("DAC Switch", ML26124_SPK_AMP_OUT, 1, 1, 0),
+	SOC_DAPM_SINGLE("Line in loopback Switch", ML26124_SPK_AMP_OUT, 3, 1,
+			 0),
+	SOC_DAPM_SINGLE("PGA Switch", ML26124_SPK_AMP_OUT, 5, 1, 0),
+};
+
+/* Input mux */
+static const char * const ml26124_input_select[] = {"Analog MIC SingleEnded in",
+				"Digital MIC in", "Analog MIC Differential in"};
+
+static const struct soc_enum ml26124_insel_enum =
+	SOC_ENUM_SINGLE(ML26124_MIC_IF_CTL, 0, 3, ml26124_input_select);
+
+static const struct snd_kcontrol_new ml26124_input_mux_controls =
+	SOC_DAPM_ENUM("Input Select", ml26124_insel_enum);
+
+static const struct snd_kcontrol_new ml26124_line_control =
+	SOC_DAPM_SINGLE("Switch", ML26124_PW_LOUT_PW_MNG, 1, 1, 0);
+
+static const struct snd_soc_dapm_widget ml26124_dapm_widgets[] = {
+	SND_SOC_DAPM_SUPPLY("MCLKEN", ML26124_CLK_EN, 0, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("PLLEN", ML26124_CLK_EN, 1, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("PLLOE", ML26124_CLK_EN, 2, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("MICBIAS", ML26124_PW_REF_PW_MNG, 2, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("Output Mixer", SND_SOC_NOPM, 0, 0,
+			   &ml26124_output_mixer_controls[0],
+			   ARRAY_SIZE(ml26124_output_mixer_controls)),
+	SND_SOC_DAPM_DAC("DAC", "Playback", ML26124_PW_DAC_PW_MNG, 1, 0),
+	SND_SOC_DAPM_ADC("ADC", "Capture", ML26124_PW_IN_PW_MNG, 1, 0),
+	SND_SOC_DAPM_PGA("PGA", ML26124_PW_IN_PW_MNG, 3, 0, NULL, 0),
+	SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0,
+			  &ml26124_input_mux_controls),
+	SND_SOC_DAPM_SWITCH("Line Out Enable", SND_SOC_NOPM, 0, 0,
+			     &ml26124_line_control),
+	SND_SOC_DAPM_INPUT("MDIN"),
+	SND_SOC_DAPM_INPUT("MIN"),
+	SND_SOC_DAPM_INPUT("LIN"),
+	SND_SOC_DAPM_OUTPUT("SPOUT"),
+	SND_SOC_DAPM_OUTPUT("LOUT"),
+};
+
+static const struct snd_soc_dapm_route ml26124_intercon[] = {
+	/* Supply */
+	{"DAC", NULL, "MCLKEN"},
+	{"ADC", NULL, "MCLKEN"},
+	{"DAC", NULL, "PLLEN"},
+	{"ADC", NULL, "PLLEN"},
+	{"DAC", NULL, "PLLOE"},
+	{"ADC", NULL, "PLLOE"},
+
+	/* output mixer */
+	{"Output Mixer", "DAC Switch", "DAC"},
+	{"Output Mixer", "Line in loopback Switch", "LIN"},
+
+	/* outputs */
+	{"LOUT", NULL, "Output Mixer"},
+	{"SPOUT", NULL, "Output Mixer"},
+	{"Line Out Enable", NULL, "LOUT"},
+
+	/* input */
+	{"ADC", NULL, "Input Mux"},
+	{"Input Mux", "Analog MIC SingleEnded in", "PGA"},
+	{"Input Mux", "Analog MIC Differential in", "PGA"},
+	{"PGA", NULL, "MIN"},
+};
+
+/* PLLOutputFreq(Hz) = InputMclkFreq(Hz) * PLLM / (PLLN * PLLDIV) */
+static const struct clk_coeff coeff_div[] = {
+	{12288000, 16000, 0xc, 0x0, 0x20, 0x0, 0x4},
+	{12288000, 32000, 0xc, 0x0, 0x20, 0x0, 0x4},
+	{12288000, 48000, 0xc, 0x0, 0x30, 0x0, 0x4},
+};
+
+static struct reg_default ml26124_reg[] = {
+	/* CLOCK control Register */
+	{0x00, 0x00 },	/* Sampling Rate */
+	{0x02, 0x00},	/* PLL NL */
+	{0x04, 0x00},	/* PLLNH */
+	{0x06, 0x00},	/* PLLML */
+	{0x08, 0x00},	/* MLLMH */
+	{0x0a, 0x00},	/* PLLDIV */
+	{0x0c, 0x00},	/* Clock Enable */
+	{0x0e, 0x00},	/* CLK Input/Output Control */
+
+	/* System Control Register */
+	{0x10, 0x00},	/* Software RESET */
+	{0x12, 0x00},	/* Record/Playback Run */
+	{0x14, 0x00},	/* Mic Input/Output control */
+
+	/* Power Management Register */
+	{0x20, 0x00},	/* Reference Power Management */
+	{0x22, 0x00},	/* Input Power Management */
+	{0x24, 0x00},	/* DAC Power Management */
+	{0x26, 0x00},	/* SP-AMP Power Management */
+	{0x28, 0x00},	/* LINEOUT Power Management */
+	{0x2a, 0x00},	/* VIDEO Power Management */
+	{0x2e, 0x00},	/* AC-CMP Power Management */
+
+	/* Analog reference Control Register */
+	{0x30, 0x04},	/* MICBIAS Voltage Control */
+
+	/* Input/Output Amplifier Control Register */
+	{0x32, 0x10},	/* MIC Input Volume */
+	{0x38, 0x00},	/* Mic Boost Volume */
+	{0x3a, 0x33},	/* Speaker AMP Volume */
+	{0x48, 0x00},	/* AMP Volume Control Function Enable */
+	{0x4a, 0x00},	/* Amplifier Volume Fader Control */
+
+	/* Analog Path Control Register */
+	{0x54, 0x00},	/* Speaker AMP Output Control */
+	{0x5a, 0x00},	/* Mic IF Control */
+	{0xe8, 0x01},	/* Mic Select Control */
+
+	/* Audio Interface Control Register */
+	{0x60, 0x00},	/* SAI-Trans Control */
+	{0x62, 0x00},	/* SAI-Receive Control */
+	{0x64, 0x00},	/* SAI Mode select */
+
+	/* DSP Control Register */
+	{0x66, 0x01},	/* Filter Func Enable */
+	{0x68, 0x00},	/* Volume Control Func Enable */
+	{0x6A, 0x00},	/* Mixer & Volume Control*/
+	{0x6C, 0xff},	/* Record Digital Volume */
+	{0x70, 0xff},	/* Playback Digital Volume */
+	{0x72, 0x10},	/* Digital Boost Volume */
+	{0x74, 0xe7},	/* EQ gain Band0 */
+	{0x76, 0xe7},	/* EQ gain Band1 */
+	{0x78, 0xe7},	/* EQ gain Band2 */
+	{0x7A, 0xe7},	/* EQ gain Band3 */
+	{0x7C, 0xe7},	/* EQ gain Band4 */
+	{0x7E, 0x00},	/* HPF2 CutOff*/
+	{0x80, 0x00},	/* EQ Band0 Coef0L */
+	{0x82, 0x00},	/* EQ Band0 Coef0H */
+	{0x84, 0x00},	/* EQ Band0 Coef0L */
+	{0x86, 0x00},	/* EQ Band0 Coef0H */
+	{0x88, 0x00},	/* EQ Band1 Coef0L */
+	{0x8A, 0x00},	/* EQ Band1 Coef0H */
+	{0x8C, 0x00},	/* EQ Band1 Coef0L */
+	{0x8E, 0x00},	/* EQ Band1 Coef0H */
+	{0x90, 0x00},	/* EQ Band2 Coef0L */
+	{0x92, 0x00},	/* EQ Band2 Coef0H */
+	{0x94, 0x00},	/* EQ Band2 Coef0L */
+	{0x96, 0x00},	/* EQ Band2 Coef0H */
+	{0x98, 0x00},	/* EQ Band3 Coef0L */
+	{0x9A, 0x00},	/* EQ Band3 Coef0H */
+	{0x9C, 0x00},	/* EQ Band3 Coef0L */
+	{0x9E, 0x00},	/* EQ Band3 Coef0H */
+	{0xA0, 0x00},	/* EQ Band4 Coef0L */
+	{0xA2, 0x00},	/* EQ Band4 Coef0H */
+	{0xA4, 0x00},	/* EQ Band4 Coef0L */
+	{0xA6, 0x00},	/* EQ Band4 Coef0H */
+
+	/* ALC Control Register */
+	{0xb0, 0x00},	/* ALC Mode */
+	{0xb2, 0x02},	/* ALC Attack Time */
+	{0xb4, 0x03},	/* ALC Decay Time */
+	{0xb6, 0x00},	/* ALC Hold Time */
+	{0xb8, 0x0b},	/* ALC Target Level */
+	{0xba, 0x70},	/* ALC Max/Min Gain */
+	{0xbc, 0x00},	/* Noise Gate Threshold */
+	{0xbe, 0x00},	/* ALC ZeroCross TimeOut */
+
+	/* Playback Limiter Control Register */
+	{0xc0, 0x04},	/* PL Attack Time */
+	{0xc2, 0x05},	/* PL Decay Time */
+	{0xc4, 0x0d},	/* PL Target Level */
+	{0xc6, 0x70},	/* PL Max/Min Gain */
+	{0xc8, 0x10},	/* Playback Boost Volume */
+	{0xca, 0x00},	/* PL ZeroCross TimeOut */
+
+	/* Video Amplifier Control Register */
+	{0xd0, 0x01},	/* VIDEO AMP Gain Control */
+	{0xd2, 0x01},	/* VIDEO AMP Setup 1 */
+	{0xd4, 0x01},	/* VIDEO AMP Control2 */
+};
+
+/* Get sampling rate value of sampling rate setting register (0x0) */
+static inline int get_srate(int rate)
+{
+	int srate;
+
+	switch (rate) {
+	case 16000:
+		srate = 3;
+		break;
+	case 32000:
+		srate = 6;
+		break;
+	case 48000:
+		srate = 8;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return srate;
+}
+
+static inline int get_coeff(int mclk, int rate)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
+		if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
+			return i;
+	}
+	return -EINVAL;
+}
+
+static int ml26124_hw_params(struct snd_pcm_substream *substream,
+			    struct snd_pcm_hw_params *hw_params,
+			    struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec);
+	int i = get_coeff(priv->mclk, params_rate(hw_params));
+
+	priv->substream = substream;
+	priv->rate = params_rate(hw_params);
+
+	if (priv->clk_in) {
+		switch (priv->mclk / params_rate(hw_params)) {
+		case 256:
+			snd_soc_update_bits(codec, ML26124_CLK_CTL,
+					    BIT(0) | BIT(1), 1);
+			break;
+		case 512:
+			snd_soc_update_bits(codec, ML26124_CLK_CTL,
+					    BIT(0) | BIT(1), 2);
+			break;
+		case 1024:
+			snd_soc_update_bits(codec, ML26124_CLK_CTL,
+					    BIT(0) | BIT(1), 3);
+			break;
+		default:
+			dev_err(codec->dev, "Unsupported MCLKI\n");
+			break;
+		}
+	} else {
+		snd_soc_update_bits(codec, ML26124_CLK_CTL,
+				    BIT(0) | BIT(1), 0);
+	}
+
+	switch (params_rate(hw_params)) {
+	case 16000:
+		snd_soc_update_bits(codec, ML26124_SMPLING_RATE, 0xf,
+				    get_srate(params_rate(hw_params)));
+		snd_soc_update_bits(codec, ML26124_PLLNL, 0xff,
+				    coeff_div[i].pllnl);
+		snd_soc_update_bits(codec, ML26124_PLLNH, 0x1,
+				    coeff_div[i].pllnh);
+		snd_soc_update_bits(codec, ML26124_PLLML, 0xff,
+				    coeff_div[i].pllml);
+		snd_soc_update_bits(codec, ML26124_PLLMH, 0x3f,
+				    coeff_div[i].pllmh);
+		snd_soc_update_bits(codec, ML26124_PLLDIV, 0x1f,
+				    coeff_div[i].plldiv);
+		break;
+	case 32000:
+		snd_soc_update_bits(codec, ML26124_SMPLING_RATE, 0xf,
+				    get_srate(params_rate(hw_params)));
+		snd_soc_update_bits(codec, ML26124_PLLNL, 0xff,
+				    coeff_div[i].pllnl);
+		snd_soc_update_bits(codec, ML26124_PLLNH, 0x1,
+				    coeff_div[i].pllnh);
+		snd_soc_update_bits(codec, ML26124_PLLML, 0xff,
+				    coeff_div[i].pllml);
+		snd_soc_update_bits(codec, ML26124_PLLMH, 0x3f,
+				    coeff_div[i].pllmh);
+		snd_soc_update_bits(codec, ML26124_PLLDIV, 0x1f,
+				    coeff_div[i].plldiv);
+		break;
+	case 48000:
+		snd_soc_update_bits(codec, ML26124_SMPLING_RATE, 0xf,
+				    get_srate(params_rate(hw_params)));
+		snd_soc_update_bits(codec, ML26124_PLLNL, 0xff,
+				    coeff_div[i].pllnl);
+		snd_soc_update_bits(codec, ML26124_PLLNH, 0x1,
+				    coeff_div[i].pllnh);
+		snd_soc_update_bits(codec, ML26124_PLLML, 0xff,
+				    coeff_div[i].pllml);
+		snd_soc_update_bits(codec, ML26124_PLLMH, 0x3f,
+				    coeff_div[i].pllmh);
+		snd_soc_update_bits(codec, ML26124_PLLDIV, 0x1f,
+				    coeff_div[i].plldiv);
+		break;
+	default:
+		pr_err("%s:this rate is no support for ml26124\n", __func__);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int ml26124_mute(struct snd_soc_dai *dai, int mute)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+	switch (priv->substream->stream) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		snd_soc_update_bits(codec, ML26124_REC_PLYBAK_RUN, BIT(0), 1);
+		break;
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		snd_soc_update_bits(codec, ML26124_REC_PLYBAK_RUN, BIT(1), 2);
+		break;
+	}
+
+	if (mute)
+		snd_soc_update_bits(codec, ML26124_DVOL_CTL, BIT(4),
+				    DVOL_CTL_DVMUTE_ON);
+	else
+		snd_soc_update_bits(codec, ML26124_DVOL_CTL, BIT(4),
+				    DVOL_CTL_DVMUTE_OFF);
+
+	return 0;
+}
+
+static int ml26124_set_dai_fmt(struct snd_soc_dai *codec_dai,
+		unsigned int fmt)
+{
+	unsigned char mode;
+	struct snd_soc_codec *codec = codec_dai->codec;
+
+	/* set master/slave audio interface */
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		mode = 1;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		mode = 0;
+		break;
+	default:
+		return -EINVAL;
+	}
+	snd_soc_update_bits(codec, ML26124_SAI_MODE_SEL, BIT(0), mode);
+
+	/* interface format */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* clock inversion */
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int ml26124_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+		int clk_id, unsigned int freq, int dir)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+	switch (clk_id) {
+	case ML26124_USE_PLLOUT:
+		priv->clk_in = ML26124_USE_PLLOUT;
+		break;
+	case ML26124_USE_MCLKI:
+		priv->clk_in = ML26124_USE_MCLKI;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	priv->mclk = freq;
+
+	return 0;
+}
+
+static int ml26124_set_bias_level(struct snd_soc_codec *codec,
+		enum snd_soc_bias_level level)
+{
+	struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		snd_soc_update_bits(codec, ML26124_PW_SPAMP_PW_MNG,
+				    ML26124_R26_MASK, ML26124_BLT_PREAMP_ON);
+		msleep(100);
+		snd_soc_update_bits(codec, ML26124_PW_SPAMP_PW_MNG,
+				    ML26124_R26_MASK,
+				    ML26124_MICBEN_ON | ML26124_BLT_ALL_ON);
+		break;
+	case SND_SOC_BIAS_PREPARE:
+		break;
+	case SND_SOC_BIAS_STANDBY:
+		/* VMID ON */
+		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+			snd_soc_update_bits(codec, ML26124_PW_REF_PW_MNG,
+					    ML26124_VMID, ML26124_VMID);
+			msleep(500);
+			regcache_sync(priv->regmap);
+		}
+		break;
+	case SND_SOC_BIAS_OFF:
+		/* VMID OFF */
+		snd_soc_update_bits(codec, ML26124_PW_REF_PW_MNG,
+				    ML26124_VMID, 0);
+		break;
+	}
+	codec->dapm.bias_level = level;
+	return 0;
+}
+
+static const struct snd_soc_dai_ops ml26124_dai_ops = {
+	.hw_params	= ml26124_hw_params,
+	.digital_mute	= ml26124_mute,
+	.set_fmt	= ml26124_set_dai_fmt,
+	.set_sysclk	= ml26124_set_dai_sysclk,
+};
+
+static struct snd_soc_dai_driver ml26124_dai = {
+	.name = "ml26124-hifi",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = ML26124_RATES,
+		.formats = ML26124_FORMATS,},
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = ML26124_RATES,
+		.formats = ML26124_FORMATS,},
+	.ops = &ml26124_dai_ops,
+	.symmetric_rates = 1,
+};
+
+#ifdef CONFIG_PM
+static int ml26124_suspend(struct snd_soc_codec *codec)
+{
+	ml26124_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+	return 0;
+}
+
+static int ml26124_resume(struct snd_soc_codec *codec)
+{
+	ml26124_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	return 0;
+}
+#else
+#define ml26124_suspend NULL
+#define ml26124_resume NULL
+#endif
+
+static int ml26124_probe(struct snd_soc_codec *codec)
+{
+	int ret;
+	struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec);
+	codec->control_data = priv->regmap;
+
+	ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_REGMAP);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+		return ret;
+	}
+
+	/* Software Reset */
+	snd_soc_update_bits(codec, ML26124_SW_RST, 0x01, 1);
+	snd_soc_update_bits(codec, ML26124_SW_RST, 0x01, 0);
+
+	ml26124_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_ml26124 = {
+	.probe =	ml26124_probe,
+	.suspend =	ml26124_suspend,
+	.resume =	ml26124_resume,
+	.set_bias_level = ml26124_set_bias_level,
+	.dapm_widgets = ml26124_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(ml26124_dapm_widgets),
+	.dapm_routes = ml26124_intercon,
+	.num_dapm_routes = ARRAY_SIZE(ml26124_intercon),
+	.controls = ml26124_snd_controls,
+	.num_controls = ARRAY_SIZE(ml26124_snd_controls),
+};
+
+static const struct regmap_config ml26124_i2c_regmap = {
+	.val_bits = 8,
+	.reg_bits = 8,
+	.max_register = ML26124_NUM_REGISTER,
+	.reg_defaults = ml26124_reg,
+	.num_reg_defaults = ARRAY_SIZE(ml26124_reg),
+	.cache_type = REGCACHE_RBTREE,
+	.write_flag_mask = 0x01,
+};
+
+static __devinit int ml26124_i2c_probe(struct i2c_client *i2c,
+				      const struct i2c_device_id *id)
+{
+	struct ml26124_priv *priv;
+	int ret;
+
+	priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	i2c_set_clientdata(i2c, priv);
+
+	priv->regmap = regmap_init_i2c(i2c, &ml26124_i2c_regmap);
+	if (IS_ERR(priv->regmap)) {
+		ret = PTR_ERR(priv->regmap);
+		dev_err(&i2c->dev, "regmap_init_i2c() failed: %d\n", ret);
+		return ret;
+	}
+
+	return snd_soc_register_codec(&i2c->dev,
+			&soc_codec_dev_ml26124, &ml26124_dai, 1);
+}
+
+static __devexit int ml26124_i2c_remove(struct i2c_client *client)
+{
+	struct ml26124_priv *priv = i2c_get_clientdata(client);
+
+	snd_soc_unregister_codec(&client->dev);
+	regmap_exit(priv->regmap);
+	return 0;
+}
+
+static const struct i2c_device_id ml26124_i2c_id[] = {
+	{ "ml26124", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ml26124_i2c_id);
+
+static struct i2c_driver ml26124_i2c_driver = {
+	.driver = {
+		.name = "ml26124",
+		.owner = THIS_MODULE,
+	},
+	.probe = ml26124_i2c_probe,
+	.remove = __devexit_p(ml26124_i2c_remove),
+	.id_table = ml26124_i2c_id,
+};
+
+module_i2c_driver(ml26124_i2c_driver);
+
+MODULE_AUTHOR("Tomoya MORINAGA <tomoya.rohm@gmail.com>");
+MODULE_DESCRIPTION("LAPIS Semiconductor ML26124 ALSA SoC codec driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ml26124.h b/sound/soc/codecs/ml26124.h
new file mode 100644
index 0000000..5ea0cbb
--- /dev/null
+++ b/sound/soc/codecs/ml26124.h
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2011 LAPIS Semiconductor Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#ifndef ML26124_H
+#define ML26124_H
+
+/* Clock Control Register */
+#define ML26124_SMPLING_RATE		0x00
+#define ML26124_PLLNL			0x02
+#define ML26124_PLLNH			0x04
+#define ML26124_PLLML			0x06
+#define ML26124_PLLMH			0x08
+#define ML26124_PLLDIV			0x0a
+#define ML26124_CLK_EN			0x0c
+#define ML26124_CLK_CTL			0x0e
+
+/* System Control Register */
+#define ML26124_SW_RST			0x10
+#define ML26124_REC_PLYBAK_RUN		0x12
+#define ML26124_MIC_TIM			0x14
+
+/* Power Mnagement Register */
+#define ML26124_PW_REF_PW_MNG		0x20
+#define ML26124_PW_IN_PW_MNG		0x22
+#define ML26124_PW_DAC_PW_MNG		0x24
+#define ML26124_PW_SPAMP_PW_MNG		0x26
+#define ML26124_PW_LOUT_PW_MNG		0x28
+#define ML26124_PW_VOUT_PW_MNG		0x2a
+#define ML26124_PW_ZCCMP_PW_MNG		0x2e
+
+/* Analog Reference Control Register */
+#define ML26124_PW_MICBIAS_VOL		0x30
+
+/* Input/Output Amplifier Control Register */
+#define ML26124_PW_MIC_IN_VOL		0x32
+#define ML26124_PW_MIC_BOST_VOL		0x38
+#define ML26124_PW_SPK_AMP_VOL		0x3a
+#define ML26124_PW_AMP_VOL_FUNC		0x48
+#define ML26124_PW_AMP_VOL_FADE		0x4a
+
+/* Analog Path Control Register */
+#define ML26124_SPK_AMP_OUT		0x54
+#define ML26124_MIC_IF_CTL		0x5a
+#define ML26124_MIC_SELECT		0xe8
+
+/* Audio Interface Control Register */
+#define ML26124_SAI_TRANS_CTL		0x60
+#define ML26124_SAI_RCV_CTL		0x62
+#define ML26124_SAI_MODE_SEL		0x64
+
+/* DSP Control Register */
+#define ML26124_FILTER_EN		0x66
+#define ML26124_DVOL_CTL		0x68
+#define ML26124_MIXER_VOL_CTL		0x6a
+#define ML26124_RECORD_DIG_VOL		0x6c
+#define ML26124_PLBAK_DIG_VOL		0x70
+#define ML26124_DIGI_BOOST_VOL		0x72
+#define ML26124_EQ_GAIN_BRAND0		0x74
+#define ML26124_EQ_GAIN_BRAND1		0x76
+#define ML26124_EQ_GAIN_BRAND2		0x78
+#define ML26124_EQ_GAIN_BRAND3		0x7a
+#define ML26124_EQ_GAIN_BRAND4		0x7c
+#define ML26124_HPF2_CUTOFF		0x7e
+#define ML26124_EQBRAND0_F0L		0x80
+#define ML26124_EQBRAND0_F0H		0x82
+#define ML26124_EQBRAND0_F1L		0x84
+#define ML26124_EQBRAND0_F1H		0x86
+#define ML26124_EQBRAND1_F0L		0x88
+#define ML26124_EQBRAND1_F0H		0x8a
+#define ML26124_EQBRAND1_F1L		0x8c
+#define ML26124_EQBRAND1_F1H		0x8e
+#define ML26124_EQBRAND2_F0L		0x90
+#define ML26124_EQBRAND2_F0H		0x92
+#define ML26124_EQBRAND2_F1L		0x94
+#define ML26124_EQBRAND2_F1H		0x96
+#define ML26124_EQBRAND3_F0L		0x98
+#define ML26124_EQBRAND3_F0H		0x9a
+#define ML26124_EQBRAND3_F1L		0x9c
+#define ML26124_EQBRAND3_F1H		0x9e
+#define ML26124_EQBRAND4_F0L		0xa0
+#define ML26124_EQBRAND4_F0H		0xa2
+#define ML26124_EQBRAND4_F1L		0xa4
+#define ML26124_EQBRAND4_F1H		0xa6
+
+/* ALC Control Register */
+#define ML26124_ALC_MODE		0xb0
+#define ML26124_ALC_ATTACK_TIM		0xb2
+#define ML26124_ALC_DECAY_TIM		0xb4
+#define ML26124_ALC_HOLD_TIM		0xb6
+#define ML26124_ALC_TARGET_LEV		0xb8
+#define ML26124_ALC_MAXMIN_GAIN		0xba
+#define ML26124_NOIS_GATE_THRSH		0xbc
+#define ML26124_ALC_ZERO_TIMOUT		0xbe
+
+/* Playback Limiter Control Register */
+#define ML26124_PL_ATTACKTIME		0xc0
+#define ML26124_PL_DECAYTIME		0xc2
+#define ML26124_PL_TARGETTIME		0xc4
+#define ML26124_PL_MAXMIN_GAIN		0xc6
+#define ML26124_PLYBAK_BOST_VOL		0xc8
+#define ML26124_PL_0CROSS_TIMOUT	0xca
+
+/* Video Amplifer Control Register */
+#define ML26124_VIDEO_AMP_GAIN_CTL	0xd0
+#define ML26124_VIDEO_AMP_SETUP1	0xd2
+#define ML26124_VIDEO_AMP_CTL2		0xd4
+
+/* Clock select for machine driver */
+#define ML26124_USE_PLL			0
+#define ML26124_USE_MCLKI_256FS		1
+#define ML26124_USE_MCLKI_512FS		2
+#define ML26124_USE_MCLKI_1024FS	3
+
+/* Register Mask */
+#define ML26124_R0_MASK	0xf
+#define ML26124_R2_MASK	0xff
+#define ML26124_R4_MASK	0x1
+#define ML26124_R6_MASK	0xf
+#define ML26124_R8_MASK	0x3f
+#define ML26124_Ra_MASK	0x1f
+#define ML26124_Rc_MASK	0x1f
+#define ML26124_Re_MASK	0x7
+#define ML26124_R10_MASK	0x1
+#define ML26124_R12_MASK	0x17
+#define ML26124_R14_MASK	0x3f
+#define ML26124_R20_MASK	0x47
+#define ML26124_R22_MASK	0xa
+#define ML26124_R24_MASK	0x2
+#define ML26124_R26_MASK	0x1f
+#define ML26124_R28_MASK	0x2
+#define ML26124_R2a_MASK	0x2
+#define ML26124_R2e_MASK	0x2
+#define ML26124_R30_MASK	0x7
+#define ML26124_R32_MASK	0x3f
+#define ML26124_R38_MASK	0x38
+#define ML26124_R3a_MASK	0x3f
+#define ML26124_R48_MASK	0x3
+#define ML26124_R4a_MASK	0x7
+#define ML26124_R54_MASK	0x2a
+#define ML26124_R5a_MASK	0x3
+#define ML26124_Re8_MASK	0x3
+#define ML26124_R60_MASK	0xff
+#define ML26124_R62_MASK	0xff
+#define ML26124_R64_MASK	0x1
+#define ML26124_R66_MASK	0xff
+#define ML26124_R68_MASK	0x3b
+#define ML26124_R6a_MASK	0xf3
+#define ML26124_R6c_MASK	0xff
+#define ML26124_R70_MASK	0xff
+
+#define ML26124_MCLKEN		BIT(0)
+#define ML26124_PLLEN		BIT(1)
+#define ML26124_PLLOE		BIT(2)
+#define ML26124_MCLKOE		BIT(3)
+
+#define ML26124_BLT_ALL_ON	0x1f
+#define ML26124_BLT_PREAMP_ON	0x13
+
+#define ML26124_MICBEN_ON	BIT(2)
+
+enum ml26124_regs {
+	ML26124_MCLK = 0,
+};
+
+enum ml26124_clk_in {
+	ML26124_USE_PLLOUT = 0,
+	ML26124_USE_MCLKI,
+};
+
+#endif
diff --git a/sound/soc/codecs/omap-hdmi.c b/sound/soc/codecs/omap-hdmi.c
new file mode 100644
index 0000000..1bf5c74
--- /dev/null
+++ b/sound/soc/codecs/omap-hdmi.c
@@ -0,0 +1,69 @@
+/*
+ * ALSA SoC codec driver for HDMI audio on OMAP processors.
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ * Author: Ricardo Neri <ricardo.neri@ti.com>
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+#include <linux/module.h>
+#include <sound/soc.h>
+
+#define DRV_NAME "hdmi-audio-codec"
+
+static struct snd_soc_codec_driver omap_hdmi_codec;
+
+static struct snd_soc_dai_driver omap_hdmi_codec_dai = {
+	.name = "omap-hdmi-hifi",
+	.playback = {
+		.channels_min = 2,
+		.channels_max = 8,
+		.rates = SNDRV_PCM_RATE_32000 |
+			SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
+			SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
+			SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE |
+			SNDRV_PCM_FMTBIT_S24_LE,
+	},
+};
+
+static __devinit int omap_hdmi_codec_probe(struct platform_device *pdev)
+{
+	return snd_soc_register_codec(&pdev->dev, &omap_hdmi_codec,
+			&omap_hdmi_codec_dai, 1);
+}
+
+static __devexit int omap_hdmi_codec_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_codec(&pdev->dev);
+	return 0;
+}
+
+static struct platform_driver omap_hdmi_codec_driver = {
+	.driver		= {
+		.name	= DRV_NAME,
+		.owner	= THIS_MODULE,
+	},
+
+	.probe		= omap_hdmi_codec_probe,
+	.remove		= __devexit_p(omap_hdmi_codec_remove),
+};
+
+module_platform_driver(omap_hdmi_codec_driver);
+
+MODULE_AUTHOR("Ricardo Neri <ricardo.neri@ti.com>");
+MODULE_DESCRIPTION("ASoC OMAP HDMI codec driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/codecs/rt5631.c b/sound/soc/codecs/rt5631.c
index 20c324c..960d0e9 100644
--- a/sound/soc/codecs/rt5631.c
+++ b/sound/soc/codecs/rt5631.c
@@ -18,7 +18,7 @@
 #include <linux/delay.h>
 #include <linux/pm.h>
 #include <linux/i2c.h>
-#include <linux/spi/spi.h>
+#include <linux/regmap.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -30,6 +30,7 @@
 #include "rt5631.h"
 
 struct rt5631_priv {
+	struct regmap *regmap;
 	int codec_version;
 	int master;
 	int sysclk;
@@ -38,33 +39,33 @@
 	int dmic_used_flag;
 };
 
-static const u16 rt5631_reg[RT5631_VENDOR_ID2 + 1] = {
-	[RT5631_SPK_OUT_VOL] = 0x8888,
-	[RT5631_HP_OUT_VOL] = 0x8080,
-	[RT5631_MONO_AXO_1_2_VOL] = 0xa080,
-	[RT5631_AUX_IN_VOL] = 0x0808,
-	[RT5631_ADC_REC_MIXER] = 0xf0f0,
-	[RT5631_VDAC_DIG_VOL] = 0x0010,
-	[RT5631_OUTMIXER_L_CTRL] = 0xffc0,
-	[RT5631_OUTMIXER_R_CTRL] = 0xffc0,
-	[RT5631_AXO1MIXER_CTRL] = 0x88c0,
-	[RT5631_AXO2MIXER_CTRL] = 0x88c0,
-	[RT5631_DIG_MIC_CTRL] = 0x3000,
-	[RT5631_MONO_INPUT_VOL] = 0x8808,
-	[RT5631_SPK_MIXER_CTRL] = 0xf8f8,
-	[RT5631_SPK_MONO_OUT_CTRL] = 0xfc00,
-	[RT5631_SPK_MONO_HP_OUT_CTRL] = 0x4440,
-	[RT5631_SDP_CTRL] = 0x8000,
-	[RT5631_MONO_SDP_CTRL] = 0x8000,
-	[RT5631_STEREO_AD_DA_CLK_CTRL] = 0x2010,
-	[RT5631_GEN_PUR_CTRL_REG] = 0x0e00,
-	[RT5631_INT_ST_IRQ_CTRL_2] = 0x071a,
-	[RT5631_MISC_CTRL] = 0x2040,
-	[RT5631_DEPOP_FUN_CTRL_2] = 0x8000,
-	[RT5631_SOFT_VOL_CTRL] = 0x07e0,
-	[RT5631_ALC_CTRL_1] = 0x0206,
-	[RT5631_ALC_CTRL_3] = 0x2000,
-	[RT5631_PSEUDO_SPATL_CTRL] = 0x0553,
+static const struct reg_default rt5631_reg[] = {
+	{ RT5631_SPK_OUT_VOL, 0x8888 },
+	{ RT5631_HP_OUT_VOL, 0x8080 },
+	{ RT5631_MONO_AXO_1_2_VOL, 0xa080 },
+	{ RT5631_AUX_IN_VOL, 0x0808 },
+	{ RT5631_ADC_REC_MIXER, 0xf0f0 },
+	{ RT5631_VDAC_DIG_VOL, 0x0010 },
+	{ RT5631_OUTMIXER_L_CTRL, 0xffc0 },
+	{ RT5631_OUTMIXER_R_CTRL, 0xffc0 },
+	{ RT5631_AXO1MIXER_CTRL, 0x88c0 },
+	{ RT5631_AXO2MIXER_CTRL, 0x88c0 },
+	{ RT5631_DIG_MIC_CTRL, 0x3000 },
+	{ RT5631_MONO_INPUT_VOL, 0x8808 },
+	{ RT5631_SPK_MIXER_CTRL, 0xf8f8 },
+	{ RT5631_SPK_MONO_OUT_CTRL, 0xfc00 },
+	{ RT5631_SPK_MONO_HP_OUT_CTRL, 0x4440 },
+	{ RT5631_SDP_CTRL, 0x8000 },
+	{ RT5631_MONO_SDP_CTRL, 0x8000 },
+	{ RT5631_STEREO_AD_DA_CLK_CTRL, 0x2010 },
+	{ RT5631_GEN_PUR_CTRL_REG, 0x0e00 },
+	{ RT5631_INT_ST_IRQ_CTRL_2, 0x071a },
+	{ RT5631_MISC_CTRL, 0x2040 },
+	{ RT5631_DEPOP_FUN_CTRL_2, 0x8000 },
+	{ RT5631_SOFT_VOL_CTRL, 0x07e0 },
+	{ RT5631_ALC_CTRL_1, 0x0206 },
+	{ RT5631_ALC_CTRL_3, 0x2000 },
+	{ RT5631_PSEUDO_SPATL_CTRL, 0x0553 },
 };
 
 /**
@@ -96,8 +97,7 @@
 	return snd_soc_write(codec, RT5631_RESET, 0);
 }
 
-static int rt5631_volatile_register(struct snd_soc_codec *codec,
-				    unsigned int reg)
+static bool rt5631_volatile_register(struct device *dev, unsigned int reg)
 {
 	switch (reg) {
 	case RT5631_RESET:
@@ -111,8 +111,7 @@
 	}
 }
 
-static int rt5631_readable_register(struct snd_soc_codec *codec,
-				    unsigned int reg)
+static bool rt5631_readable_register(struct device *dev, unsigned int reg)
 {
 	switch (reg) {
 	case RT5631_RESET:
@@ -1361,8 +1360,7 @@
 static int rt5631_hifi_pcm_params(struct snd_pcm_substream *substream,
 		struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
 	int timesofbclk = 32, coeff;
 	unsigned int iface = 0;
@@ -1544,6 +1542,8 @@
 static int rt5631_set_bias_level(struct snd_soc_codec *codec,
 			enum snd_soc_bias_level level)
 {
+	struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
+
 	switch (level) {
 	case SND_SOC_BIAS_ON:
 	case SND_SOC_BIAS_PREPARE:
@@ -1561,8 +1561,8 @@
 			snd_soc_update_bits(codec, RT5631_PWR_MANAG_ADD3,
 				RT5631_PWR_FAST_VREF_CTRL,
 				RT5631_PWR_FAST_VREF_CTRL);
-			codec->cache_only = false;
-			snd_soc_cache_sync(codec);
+			regcache_cache_only(rt5631->regmap, false);
+			regcache_sync(rt5631->regmap);
 		}
 		break;
 
@@ -1587,7 +1587,9 @@
 	unsigned int val;
 	int ret;
 
-	ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C);
+	codec->control_data = rt5631->regmap;
+
+	ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_REGMAP);
 	if (ret != 0) {
 		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
 		return ret;
@@ -1698,12 +1700,6 @@
 	.suspend = rt5631_suspend,
 	.resume = rt5631_resume,
 	.set_bias_level = rt5631_set_bias_level,
-	.reg_cache_size = RT5631_VENDOR_ID2 + 1,
-	.reg_word_size = sizeof(u16),
-	.reg_cache_default = rt5631_reg,
-	.volatile_register = rt5631_volatile_register,
-	.readable_register = rt5631_readable_register,
-	.reg_cache_step = 1,
 	.controls = rt5631_snd_controls,
 	.num_controls = ARRAY_SIZE(rt5631_snd_controls),
 	.dapm_widgets = rt5631_dapm_widgets,
@@ -1718,6 +1714,18 @@
 };
 MODULE_DEVICE_TABLE(i2c, rt5631_i2c_id);
 
+static const struct regmap_config rt5631_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 16,
+
+	.readable_reg = rt5631_readable_register,
+	.volatile_reg = rt5631_volatile_register,
+	.max_register = RT5631_VENDOR_ID2,
+	.reg_defaults = rt5631_reg,
+	.num_reg_defaults = ARRAY_SIZE(rt5631_reg),
+	.cache_type = REGCACHE_RBTREE,
+};
+
 static int rt5631_i2c_probe(struct i2c_client *i2c,
 		    const struct i2c_device_id *id)
 {
@@ -1731,6 +1739,10 @@
 
 	i2c_set_clientdata(i2c, rt5631);
 
+	rt5631->regmap = devm_regmap_init_i2c(i2c, &rt5631_regmap_config);
+	if (IS_ERR(rt5631->regmap))
+		return PTR_ERR(rt5631->regmap);
+
 	ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5631,
 			rt5631_dai, ARRAY_SIZE(rt5631_dai));
 	return ret;
@@ -1752,17 +1764,7 @@
 	.id_table = rt5631_i2c_id,
 };
 
-static int __init rt5631_modinit(void)
-{
-	return i2c_add_driver(&rt5631_i2c_driver);
-}
-module_init(rt5631_modinit);
-
-static void __exit rt5631_modexit(void)
-{
-	i2c_del_driver(&rt5631_i2c_driver);
-}
-module_exit(rt5631_modexit);
+module_i2c_driver(rt5631_i2c_driver);
 
 MODULE_DESCRIPTION("ASoC RT5631 driver");
 MODULE_AUTHOR("flove <flove@realtek.com>");
diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c
index c395ec3..8af6a52 100644
--- a/sound/soc/codecs/sgtl5000.c
+++ b/sound/soc/codecs/sgtl5000.c
@@ -84,8 +84,8 @@
 
 static struct regulator_init_data ldo_init_data = {
 	.constraints = {
-		.min_uV                 = 850000,
-		.max_uV                 = 1600000,
+		.min_uV                 = 1200000,
+		.max_uV                 = 1200000,
 		.valid_modes_mask       = REGULATOR_MODE_NORMAL,
 		.valid_ops_mask         = REGULATOR_CHANGE_STATUS,
 	},
@@ -197,9 +197,9 @@
 	SND_SOC_DAPM_OUTPUT("HP_OUT"),
 	SND_SOC_DAPM_OUTPUT("LINE_OUT"),
 
-	SND_SOC_DAPM_MICBIAS_E("Mic Bias", SGTL5000_CHIP_MIC_CTRL, 8, 0,
-				mic_bias_event,
-				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+	SND_SOC_DAPM_SUPPLY("Mic Bias", SGTL5000_CHIP_MIC_CTRL, 8, 0,
+			    mic_bias_event,
+			    SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
 
 	SND_SOC_DAPM_PGA("HP", SGTL5000_CHIP_ANA_POWER, 4, 0, NULL, 0),
 	SND_SOC_DAPM_PGA("LO", SGTL5000_CHIP_ANA_POWER, 0, 0, NULL, 0),
@@ -665,8 +665,7 @@
 				  struct snd_pcm_hw_params *params,
 				  struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
 	int channels = params_channels(params);
 	int i2s_ctl = 0;
@@ -1455,17 +1454,7 @@
 	.id_table = sgtl5000_id,
 };
 
-static int __init sgtl5000_modinit(void)
-{
-	return i2c_add_driver(&sgtl5000_i2c_driver);
-}
-module_init(sgtl5000_modinit);
-
-static void __exit sgtl5000_exit(void)
-{
-	i2c_del_driver(&sgtl5000_i2c_driver);
-}
-module_exit(sgtl5000_exit);
+module_i2c_driver(sgtl5000_i2c_driver);
 
 MODULE_DESCRIPTION("Freescale SGTL5000 ALSA SoC Codec Driver");
 MODULE_AUTHOR("Zeng Zhaoming <zengzm.kernel@gmail.com>");
diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c
index de2b205..079066f 100644
--- a/sound/soc/codecs/ssm2602.c
+++ b/sound/soc/codecs/ssm2602.c
@@ -33,6 +33,7 @@
 #include <linux/pm.h>
 #include <linux/i2c.h>
 #include <linux/spi/spi.h>
+#include <linux/regmap.h>
 #include <linux/slab.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -43,8 +44,6 @@
 
 #include "ssm2602.h"
 
-#define SSM2602_VERSION "0.1"
-
 enum ssm2602_type {
 	SSM2602,
 	SSM2604,
@@ -53,10 +52,12 @@
 /* codec private data */
 struct ssm2602_priv {
 	unsigned int sysclk;
-	enum snd_soc_control_type control_type;
+	struct snd_pcm_hw_constraint_list *sysclk_constraints;
 	struct snd_pcm_substream *master_substream;
 	struct snd_pcm_substream *slave_substream;
 
+	struct regmap *regmap;
+
 	enum ssm2602_type type;
 	unsigned int clk_out_pwr;
 };
@@ -73,7 +74,6 @@
 	0x0000, 0x0000
 };
 
-#define ssm2602_reset(c)	snd_soc_write(c, SSM2602_RESET, 0)
 
 /*Appending several "None"s just for OSS mixer use*/
 static const char *ssm2602_input_select[] = {
@@ -195,6 +195,24 @@
 	{"ADC", NULL, "Line Input"},
 };
 
+static const unsigned int ssm2602_rates_12288000[] = {
+	8000, 32000, 48000, 96000,
+};
+
+static struct snd_pcm_hw_constraint_list ssm2602_constraints_12288000 = {
+	.list = ssm2602_rates_12288000,
+	.count = ARRAY_SIZE(ssm2602_rates_12288000),
+};
+
+static const unsigned int ssm2602_rates_11289600[] = {
+	8000, 44100, 88200,
+};
+
+static struct snd_pcm_hw_constraint_list ssm2602_constraints_11289600 = {
+	.list = ssm2602_rates_11289600,
+	.count = ARRAY_SIZE(ssm2602_rates_11289600),
+};
+
 struct ssm2602_coeff {
 	u32 mclk;
 	u32 rate;
@@ -254,11 +272,10 @@
 	struct snd_pcm_hw_params *params,
 	struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
-	u16 iface = snd_soc_read(codec, SSM2602_IFACE) & 0xfff3;
 	int srate = ssm2602_get_coeff(ssm2602->sysclk, params_rate(params));
+	unsigned int iface;
 
 	if (substream == ssm2602->slave_substream) {
 		dev_dbg(codec->dev, "Ignoring hw_params for slave substream\n");
@@ -268,31 +285,34 @@
 	if (srate < 0)
 		return srate;
 
-	snd_soc_write(codec, SSM2602_SRATE, srate);
+	regmap_write(ssm2602->regmap, SSM2602_SRATE, srate);
 
 	/* bit size */
 	switch (params_format(params)) {
 	case SNDRV_PCM_FORMAT_S16_LE:
+		iface = 0x0;
 		break;
 	case SNDRV_PCM_FORMAT_S20_3LE:
-		iface |= 0x0004;
+		iface = 0x4;
 		break;
 	case SNDRV_PCM_FORMAT_S24_LE:
-		iface |= 0x0008;
+		iface = 0x8;
 		break;
 	case SNDRV_PCM_FORMAT_S32_LE:
-		iface |= 0x000c;
+		iface = 0xc;
 		break;
+	default:
+		return -EINVAL;
 	}
-	snd_soc_write(codec, SSM2602_IFACE, iface);
+	regmap_update_bits(ssm2602->regmap, SSM2602_IFACE,
+		IFACE_AUDIO_DATA_LEN, iface);
 	return 0;
 }
 
 static int ssm2602_startup(struct snd_pcm_substream *substream,
 			   struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
 	struct snd_pcm_runtime *master_runtime;
 
@@ -322,14 +342,19 @@
 	} else
 		ssm2602->master_substream = substream;
 
+	if (ssm2602->sysclk_constraints) {
+		snd_pcm_hw_constraint_list(substream->runtime, 0,
+				   SNDRV_PCM_HW_PARAM_RATE,
+				   ssm2602->sysclk_constraints);
+	}
+
 	return 0;
 }
 
 static void ssm2602_shutdown(struct snd_pcm_substream *substream,
 			     struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
 
 	if (ssm2602->master_substream == substream)
@@ -341,14 +366,14 @@
 
 static int ssm2602_mute(struct snd_soc_dai *dai, int mute)
 {
-	struct snd_soc_codec *codec = dai->codec;
+	struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(dai->codec);
 
 	if (mute)
-		snd_soc_update_bits(codec, SSM2602_APDIGI,
+		regmap_update_bits(ssm2602->regmap, SSM2602_APDIGI,
 				    APDIGI_ENABLE_DAC_MUTE,
 				    APDIGI_ENABLE_DAC_MUTE);
 	else
-		snd_soc_update_bits(codec, SSM2602_APDIGI,
+		regmap_update_bits(ssm2602->regmap, SSM2602_APDIGI,
 				    APDIGI_ENABLE_DAC_MUTE, 0);
 	return 0;
 }
@@ -364,16 +389,21 @@
 			return -EINVAL;
 
 		switch (freq) {
-		case 11289600:
-		case 12000000:
 		case 12288000:
-		case 16934400:
 		case 18432000:
-			ssm2602->sysclk = freq;
+			ssm2602->sysclk_constraints = &ssm2602_constraints_12288000;
+			break;
+		case 11289600:
+		case 16934400:
+			ssm2602->sysclk_constraints = &ssm2602_constraints_11289600;
+			break;
+		case 12000000:
+			ssm2602->sysclk_constraints = NULL;
 			break;
 		default:
 			return -EINVAL;
 		}
+		ssm2602->sysclk = freq;
 	} else {
 		unsigned int mask;
 
@@ -393,7 +423,7 @@
 		else
 			ssm2602->clk_out_pwr &= ~mask;
 
-		snd_soc_update_bits(codec, SSM2602_PWR,
+		regmap_update_bits(ssm2602->regmap, SSM2602_PWR,
 			PWR_CLK_OUT_PDN | PWR_OSC_PDN, ssm2602->clk_out_pwr);
 	}
 
@@ -403,8 +433,8 @@
 static int ssm2602_set_dai_fmt(struct snd_soc_dai *codec_dai,
 		unsigned int fmt)
 {
-	struct snd_soc_codec *codec = codec_dai->codec;
-	u16 iface = 0;
+	struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec_dai->codec);
+	unsigned int iface = 0;
 
 	/* set master/slave audio interface */
 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
@@ -455,7 +485,7 @@
 	}
 
 	/* set iface */
-	snd_soc_write(codec, SSM2602_IFACE, iface);
+	regmap_write(ssm2602->regmap, SSM2602_IFACE, iface);
 	return 0;
 }
 
@@ -467,7 +497,7 @@
 	switch (level) {
 	case SND_SOC_BIAS_ON:
 		/* vref/mid on, osc and clkout on if enabled */
-		snd_soc_update_bits(codec, SSM2602_PWR,
+		regmap_update_bits(ssm2602->regmap, SSM2602_PWR,
 			PWR_POWER_OFF | PWR_CLK_OUT_PDN | PWR_OSC_PDN,
 			ssm2602->clk_out_pwr);
 		break;
@@ -475,13 +505,13 @@
 		break;
 	case SND_SOC_BIAS_STANDBY:
 		/* everything off except vref/vmid, */
-		snd_soc_update_bits(codec, SSM2602_PWR,
+		regmap_update_bits(ssm2602->regmap, SSM2602_PWR,
 			PWR_POWER_OFF | PWR_CLK_OUT_PDN | PWR_OSC_PDN,
 			PWR_CLK_OUT_PDN | PWR_OSC_PDN);
 		break;
 	case SND_SOC_BIAS_OFF:
 		/* everything off */
-		snd_soc_update_bits(codec, SSM2602_PWR,
+		regmap_update_bits(ssm2602->regmap, SSM2602_PWR,
 			PWR_POWER_OFF, PWR_POWER_OFF);
 		break;
 
@@ -540,12 +570,13 @@
 
 static int ssm2602_probe(struct snd_soc_codec *codec)
 {
+	struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
 	struct snd_soc_dapm_context *dapm = &codec->dapm;
 	int ret;
 
-	snd_soc_update_bits(codec, SSM2602_LOUT1V,
+	regmap_update_bits(ssm2602->regmap, SSM2602_LOUT1V,
 			    LOUT1V_LRHP_BOTH, LOUT1V_LRHP_BOTH);
-	snd_soc_update_bits(codec, SSM2602_ROUT1V,
+	regmap_update_bits(ssm2602->regmap, SSM2602_ROUT1V,
 			    ROUT1V_RLHP_BOTH, ROUT1V_RLHP_BOTH);
 
 	ret = snd_soc_add_codec_controls(codec, ssm2602_snd_controls,
@@ -581,27 +612,26 @@
 	struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
 	int ret;
 
-	pr_info("ssm2602 Audio Codec %s", SSM2602_VERSION);
-
-	ret = snd_soc_codec_set_cache_io(codec, 7, 9, ssm2602->control_type);
+	codec->control_data = ssm2602->regmap;
+	ret = snd_soc_codec_set_cache_io(codec, 0, 0, SND_SOC_REGMAP);
 	if (ret < 0) {
 		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
 		return ret;
 	}
 
-	ret = ssm2602_reset(codec);
+	ret = regmap_write(ssm2602->regmap, SSM2602_RESET, 0);
 	if (ret < 0) {
 		dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
 		return ret;
 	}
 
 	/* set the update bits */
-	snd_soc_update_bits(codec, SSM2602_LINVOL,
+	regmap_update_bits(ssm2602->regmap, SSM2602_LINVOL,
 			    LINVOL_LRIN_BOTH, LINVOL_LRIN_BOTH);
-	snd_soc_update_bits(codec, SSM2602_RINVOL,
+	regmap_update_bits(ssm2602->regmap, SSM2602_RINVOL,
 			    RINVOL_RLIN_BOTH, RINVOL_RLIN_BOTH);
 	/*select Line in as default input*/
-	snd_soc_write(codec, SSM2602_APANA, APANA_SELECT_DAC |
+	regmap_write(ssm2602->regmap, SSM2602_APANA, APANA_SELECT_DAC |
 			APANA_ENABLE_MIC_BOOST);
 
 	switch (ssm2602->type) {
@@ -634,9 +664,6 @@
 	.suspend =	ssm2602_suspend,
 	.resume =	ssm2602_resume,
 	.set_bias_level = ssm2602_set_bias_level,
-	.reg_cache_size = ARRAY_SIZE(ssm2602_reg),
-	.reg_word_size = sizeof(u16),
-	.reg_cache_default = ssm2602_reg,
 
 	.controls = ssm260x_snd_controls,
 	.num_controls = ARRAY_SIZE(ssm260x_snd_controls),
@@ -646,6 +673,23 @@
 	.num_dapm_routes = ARRAY_SIZE(ssm260x_routes),
 };
 
+static bool ssm2602_register_volatile(struct device *dev, unsigned int reg)
+{
+	return reg == SSM2602_RESET;
+}
+
+static const struct regmap_config ssm2602_regmap_config = {
+	.val_bits = 9,
+	.reg_bits = 7,
+
+	.max_register = SSM2602_RESET,
+	.volatile_reg = ssm2602_register_volatile,
+
+	.cache_type = REGCACHE_RBTREE,
+	.reg_defaults_raw = ssm2602_reg,
+	.num_reg_defaults_raw = ARRAY_SIZE(ssm2602_reg),
+};
+
 #if defined(CONFIG_SPI_MASTER)
 static int __devinit ssm2602_spi_probe(struct spi_device *spi)
 {
@@ -658,9 +702,12 @@
 		return -ENOMEM;
 
 	spi_set_drvdata(spi, ssm2602);
-	ssm2602->control_type = SND_SOC_SPI;
 	ssm2602->type = SSM2602;
 
+	ssm2602->regmap = devm_regmap_init_spi(spi, &ssm2602_regmap_config);
+	if (IS_ERR(ssm2602->regmap))
+		return PTR_ERR(ssm2602->regmap);
+
 	ret = snd_soc_register_codec(&spi->dev,
 			&soc_codec_dev_ssm2602, &ssm2602_dai, 1);
 	return ret;
@@ -701,9 +748,12 @@
 		return -ENOMEM;
 
 	i2c_set_clientdata(i2c, ssm2602);
-	ssm2602->control_type = SND_SOC_I2C;
 	ssm2602->type = id->driver_data;
 
+	ssm2602->regmap = devm_regmap_init_i2c(i2c, &ssm2602_regmap_config);
+	if (IS_ERR(ssm2602->regmap))
+		return PTR_ERR(ssm2602->regmap);
+
 	ret = snd_soc_register_codec(&i2c->dev,
 			&soc_codec_dev_ssm2602, &ssm2602_dai, 1);
 	return ret;
diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c
index 7db6fa5..8d717f4 100644
--- a/sound/soc/codecs/sta32x.c
+++ b/sound/soc/codecs/sta32x.c
@@ -609,8 +609,7 @@
 			    struct snd_pcm_hw_params *params,
 			    struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
 	unsigned int rate;
 	int i, mcs = -1, ir = -1;
diff --git a/sound/soc/codecs/tlv320aic23.c b/sound/soc/codecs/tlv320aic23.c
index df1e07f..31762eb 100644
--- a/sound/soc/codecs/tlv320aic23.c
+++ b/sound/soc/codecs/tlv320aic23.c
@@ -34,8 +34,6 @@
 
 #include "tlv320aic23.h"
 
-#define AIC23_VERSION "0.1"
-
 /*
  * AIC23 register cache
  */
@@ -325,8 +323,7 @@
 				 struct snd_pcm_hw_params *params,
 				 struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	u16 iface_reg;
 	int ret;
 	struct aic23 *aic23 = snd_soc_codec_get_drvdata(codec);
@@ -371,8 +368,7 @@
 static int tlv320aic23_pcm_prepare(struct snd_pcm_substream *substream,
 				   struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 
 	/* set active */
 	snd_soc_write(codec, TLV320AIC23_ACTIVE, 0x0001);
@@ -383,8 +379,7 @@
 static void tlv320aic23_shutdown(struct snd_pcm_substream *substream,
 				 struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	struct aic23 *aic23 = snd_soc_codec_get_drvdata(codec);
 
 	/* deactivate */
@@ -548,8 +543,6 @@
 	struct aic23 *aic23 = snd_soc_codec_get_drvdata(codec);
 	int ret;
 
-	printk(KERN_INFO "AIC23 Audio Codec %s\n", AIC23_VERSION);
-
 	ret = snd_soc_codec_set_cache_io(codec, 7, 9, aic23->control_type);
 	if (ret < 0) {
 		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
diff --git a/sound/soc/codecs/tlv320aic26.c b/sound/soc/codecs/tlv320aic26.c
index 802064b..85944e9 100644
--- a/sound/soc/codecs/tlv320aic26.c
+++ b/sound/soc/codecs/tlv320aic26.c
@@ -126,8 +126,7 @@
 			   struct snd_pcm_hw_params *params,
 			   struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	struct aic26 *aic26 = snd_soc_codec_get_drvdata(codec);
 	int fsref, divisor, wlen, pval, jval, dval, qval;
 	u16 reg;
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index 8d20f6e..64d2a4f 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -802,8 +802,7 @@
 			   struct snd_pcm_hw_params *params,
 			   struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec =rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
 	int codec_clk = 0, bypass_pll = 0, fsref, last_clk = 0;
 	u8 data, j, r, p, pll_q, pll_p = 1, pll_r = 1, pll_j = 1;
@@ -1161,24 +1160,6 @@
 	return 0;
 }
 
-void aic3x_set_headset_detection(struct snd_soc_codec *codec, int detect,
-				 int headset_debounce, int button_debounce)
-{
-	u8 val;
-
-	val = ((detect & AIC3X_HEADSET_DETECT_MASK)
-		<< AIC3X_HEADSET_DETECT_SHIFT) |
-	      ((headset_debounce & AIC3X_HEADSET_DEBOUNCE_MASK)
-		<< AIC3X_HEADSET_DEBOUNCE_SHIFT) |
-	      ((button_debounce & AIC3X_BUTTON_DEBOUNCE_MASK)
-		<< AIC3X_BUTTON_DEBOUNCE_SHIFT);
-
-	if (detect & AIC3X_HEADSET_DETECT_MASK)
-		val |= AIC3X_HEADSET_DETECT_ENABLED;
-
-	snd_soc_write(codec, AIC3X_HEADSET_DETECT_CTRL_A, val);
-}
-
 #define AIC3X_RATES	SNDRV_PCM_RATE_8000_96000
 #define AIC3X_FORMATS	(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
 			 SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c
index 4587ddd..0dd4107 100644
--- a/sound/soc/codecs/tlv320dac33.c
+++ b/sound/soc/codecs/tlv320dac33.c
@@ -62,8 +62,10 @@
 #define UTHR_FROM_PERIOD_SIZE(samples, playrate, burstrate) \
 	(((samples)*5000) / (((burstrate)*5000) / ((burstrate) - (playrate))))
 
-static void dac33_calculate_times(struct snd_pcm_substream *substream);
-static int dac33_prepare_chip(struct snd_pcm_substream *substream);
+static void dac33_calculate_times(struct snd_pcm_substream *substream,
+				  struct snd_soc_codec *codec);
+static int dac33_prepare_chip(struct snd_pcm_substream *substream,
+			      struct snd_soc_codec *codec);
 
 enum dac33_state {
 	DAC33_IDLE = 0,
@@ -427,8 +429,8 @@
 	switch (event) {
 	case SND_SOC_DAPM_PRE_PMU:
 		if (likely(dac33->substream)) {
-			dac33_calculate_times(dac33->substream);
-			dac33_prepare_chip(dac33->substream);
+			dac33_calculate_times(dac33->substream, w->codec);
+			dac33_prepare_chip(dac33->substream, w->codec);
 		}
 		break;
 	case SND_SOC_DAPM_POST_PMD:
@@ -799,8 +801,7 @@
 static int dac33_startup(struct snd_pcm_substream *substream,
 			   struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
 
 	/* Stream started, save the substream pointer */
@@ -812,8 +813,7 @@
 static void dac33_shutdown(struct snd_pcm_substream *substream,
 			     struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
 
 	dac33->substream = NULL;
@@ -825,8 +825,7 @@
 			   struct snd_pcm_hw_params *params,
 			   struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
 
 	/* Check parameters for validity */
@@ -868,10 +867,9 @@
  * writes happens in different order, than dac33 might end up in unknown state.
  * Use the known, working sequence of register writes to initialize the dac33.
  */
-static int dac33_prepare_chip(struct snd_pcm_substream *substream)
+static int dac33_prepare_chip(struct snd_pcm_substream *substream,
+			      struct snd_soc_codec *codec)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
 	struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
 	unsigned int oscset, ratioset, pwr_ctrl, reg_tmp;
 	u8 aictrl_a, aictrl_b, fifoctrl_a;
@@ -1067,10 +1065,9 @@
 	return 0;
 }
 
-static void dac33_calculate_times(struct snd_pcm_substream *substream)
+static void dac33_calculate_times(struct snd_pcm_substream *substream,
+				  struct snd_soc_codec *codec)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
 	struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
 	unsigned int period_size = substream->runtime->period_size;
 	unsigned int rate = substream->runtime->rate;
@@ -1128,8 +1125,7 @@
 static int dac33_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
 			     struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
 	int ret = 0;
 
@@ -1161,8 +1157,7 @@
 			struct snd_pcm_substream *substream,
 			struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
 	unsigned long long t0, t1, t_now;
 	unsigned int time_delta, uthr;
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c
index 170cf9a..391fcfc 100644
--- a/sound/soc/codecs/twl4030.c
+++ b/sound/soc/codecs/twl4030.c
@@ -1685,8 +1685,7 @@
 static int twl4030_startup(struct snd_pcm_substream *substream,
 			   struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
 
 	if (twl4030->master_substream) {
@@ -1715,8 +1714,7 @@
 static void twl4030_shutdown(struct snd_pcm_substream *substream,
 			     struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
 
 	if (twl4030->master_substream == substream)
@@ -1740,8 +1738,7 @@
 			   struct snd_pcm_hw_params *params,
 			   struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
 	u8 mode, old_mode, format, old_format;
 
@@ -1974,8 +1971,7 @@
 static int twl4030_voice_startup(struct snd_pcm_substream *substream,
 		struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
 	u8 mode;
 
@@ -2007,8 +2003,7 @@
 static void twl4030_voice_shutdown(struct snd_pcm_substream *substream,
 				struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 
 	/* Enable voice digital filters */
 	twl4030_voice_enable(codec, substream->stream, 0);
@@ -2017,8 +2012,7 @@
 static int twl4030_voice_hw_params(struct snd_pcm_substream *substream,
 		struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
 	u8 old_mode, mode;
 
diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c
index dc7509b..a36e9fc 100644
--- a/sound/soc/codecs/twl6040.c
+++ b/sound/soc/codecs/twl6040.c
@@ -46,17 +46,6 @@
 #define TWL6040_OUTHF_0dB 0x03
 #define TWL6040_OUTHF_M52dB 0x1D
 
-#define TWL6040_RAMP_NONE	0
-#define TWL6040_RAMP_UP		1
-#define TWL6040_RAMP_DOWN	2
-
-#define TWL6040_HSL_VOL_MASK	0x0F
-#define TWL6040_HSL_VOL_SHIFT	0
-#define TWL6040_HSR_VOL_MASK	0xF0
-#define TWL6040_HSR_VOL_SHIFT	4
-#define TWL6040_HF_VOL_MASK	0x1F
-#define TWL6040_HF_VOL_SHIFT	0
-
 /* Shadow register used by the driver */
 #define TWL6040_REG_SW_SHADOW	0x2F
 #define TWL6040_CACHEREGNUM	(TWL6040_REG_SW_SHADOW + 1)
@@ -64,18 +53,6 @@
 /* TWL6040_REG_SW_SHADOW (0x2F) fields */
 #define TWL6040_EAR_PATH_ENABLE	0x01
 
-struct twl6040_output {
-	u16 active;
-	u16 left_vol;
-	u16 right_vol;
-	u16 left_step;
-	u16 right_step;
-	unsigned int step_delay;
-	u16 ramp;
-	struct delayed_work work;
-	struct completion ramp_done;
-};
-
 struct twl6040_jack_data {
 	struct snd_soc_jack *jack;
 	struct delayed_work work;
@@ -100,8 +77,6 @@
 	struct snd_soc_codec *codec;
 	struct workqueue_struct *workqueue;
 	struct mutex mutex;
-	struct twl6040_output headset;
-	struct twl6040_output handsfree;
 };
 
 /*
@@ -311,318 +286,6 @@
 	}
 }
 
-/*
- * Ramp HS PGA volume to minimise pops at stream startup and shutdown.
- */
-static inline int twl6040_hs_ramp_step(struct snd_soc_codec *codec,
-			unsigned int left_step, unsigned int right_step)
-{
-
-	struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
-	struct twl6040_output *headset = &priv->headset;
-	int left_complete = 0, right_complete = 0;
-	u8 reg, val;
-
-	/* left channel */
-	left_step = (left_step > 0xF) ? 0xF : left_step;
-	reg = twl6040_read_reg_cache(codec, TWL6040_REG_HSGAIN);
-	val = (~reg & TWL6040_HSL_VOL_MASK);
-
-	if (headset->ramp == TWL6040_RAMP_UP) {
-		/* ramp step up */
-		if (val < headset->left_vol) {
-			if (val + left_step > headset->left_vol)
-				val = headset->left_vol;
-			else
-				val += left_step;
-
-			reg &= ~TWL6040_HSL_VOL_MASK;
-			twl6040_write(codec, TWL6040_REG_HSGAIN,
-					(reg | (~val & TWL6040_HSL_VOL_MASK)));
-		} else {
-			left_complete = 1;
-		}
-	} else if (headset->ramp == TWL6040_RAMP_DOWN) {
-		/* ramp step down */
-		if (val > 0x0) {
-			if ((int)val - (int)left_step < 0)
-				val = 0;
-			else
-				val -= left_step;
-
-			reg &= ~TWL6040_HSL_VOL_MASK;
-			twl6040_write(codec, TWL6040_REG_HSGAIN, reg |
-						(~val & TWL6040_HSL_VOL_MASK));
-		} else {
-			left_complete = 1;
-		}
-	}
-
-	/* right channel */
-	right_step = (right_step > 0xF) ? 0xF : right_step;
-	reg = twl6040_read_reg_cache(codec, TWL6040_REG_HSGAIN);
-	val = (~reg & TWL6040_HSR_VOL_MASK) >> TWL6040_HSR_VOL_SHIFT;
-
-	if (headset->ramp == TWL6040_RAMP_UP) {
-		/* ramp step up */
-		if (val < headset->right_vol) {
-			if (val + right_step > headset->right_vol)
-				val = headset->right_vol;
-			else
-				val += right_step;
-
-			reg &= ~TWL6040_HSR_VOL_MASK;
-			twl6040_write(codec, TWL6040_REG_HSGAIN,
-				(reg | (~val << TWL6040_HSR_VOL_SHIFT)));
-		} else {
-			right_complete = 1;
-		}
-	} else if (headset->ramp == TWL6040_RAMP_DOWN) {
-		/* ramp step down */
-		if (val > 0x0) {
-			if ((int)val - (int)right_step < 0)
-				val = 0;
-			else
-				val -= right_step;
-
-			reg &= ~TWL6040_HSR_VOL_MASK;
-			twl6040_write(codec, TWL6040_REG_HSGAIN,
-					 reg | (~val << TWL6040_HSR_VOL_SHIFT));
-		} else {
-			right_complete = 1;
-		}
-	}
-
-	return left_complete & right_complete;
-}
-
-/*
- * Ramp HF PGA volume to minimise pops at stream startup and shutdown.
- */
-static inline int twl6040_hf_ramp_step(struct snd_soc_codec *codec,
-			unsigned int left_step, unsigned int right_step)
-{
-	struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
-	struct twl6040_output *handsfree = &priv->handsfree;
-	int left_complete = 0, right_complete = 0;
-	u16 reg, val;
-
-	/* left channel */
-	left_step = (left_step > 0x1D) ? 0x1D : left_step;
-	reg = twl6040_read_reg_cache(codec, TWL6040_REG_HFLGAIN);
-	reg = 0x1D - reg;
-	val = (reg & TWL6040_HF_VOL_MASK);
-	if (handsfree->ramp == TWL6040_RAMP_UP) {
-		/* ramp step up */
-		if (val < handsfree->left_vol) {
-			if (val + left_step > handsfree->left_vol)
-				val = handsfree->left_vol;
-			else
-				val += left_step;
-
-			reg &= ~TWL6040_HF_VOL_MASK;
-			twl6040_write(codec, TWL6040_REG_HFLGAIN,
-						reg | (0x1D - val));
-		} else {
-			left_complete = 1;
-		}
-	} else if (handsfree->ramp == TWL6040_RAMP_DOWN) {
-		/* ramp step down */
-		if (val > 0) {
-			if ((int)val - (int)left_step < 0)
-				val = 0;
-			else
-				val -= left_step;
-
-			reg &= ~TWL6040_HF_VOL_MASK;
-			twl6040_write(codec, TWL6040_REG_HFLGAIN,
-						reg | (0x1D - val));
-		} else {
-			left_complete = 1;
-		}
-	}
-
-	/* right channel */
-	right_step = (right_step > 0x1D) ? 0x1D : right_step;
-	reg = twl6040_read_reg_cache(codec, TWL6040_REG_HFRGAIN);
-	reg = 0x1D - reg;
-	val = (reg & TWL6040_HF_VOL_MASK);
-	if (handsfree->ramp == TWL6040_RAMP_UP) {
-		/* ramp step up */
-		if (val < handsfree->right_vol) {
-			if (val + right_step > handsfree->right_vol)
-				val = handsfree->right_vol;
-			else
-				val += right_step;
-
-			reg &= ~TWL6040_HF_VOL_MASK;
-			twl6040_write(codec, TWL6040_REG_HFRGAIN,
-						reg | (0x1D - val));
-		} else {
-			right_complete = 1;
-		}
-	} else if (handsfree->ramp == TWL6040_RAMP_DOWN) {
-		/* ramp step down */
-		if (val > 0) {
-			if ((int)val - (int)right_step < 0)
-				val = 0;
-			else
-				val -= right_step;
-
-			reg &= ~TWL6040_HF_VOL_MASK;
-			twl6040_write(codec, TWL6040_REG_HFRGAIN,
-						reg | (0x1D - val));
-		}
-	}
-
-	return left_complete & right_complete;
-}
-
-/*
- * This work ramps both output PGAs at stream start/stop time to
- * minimise pop associated with DAPM power switching.
- */
-static void twl6040_pga_hs_work(struct work_struct *work)
-{
-	struct twl6040_data *priv =
-		container_of(work, struct twl6040_data, headset.work.work);
-	struct snd_soc_codec *codec = priv->codec;
-	struct twl6040_output *headset = &priv->headset;
-	int i, headset_complete;
-
-	/* do we need to ramp at all ? */
-	if (headset->ramp == TWL6040_RAMP_NONE)
-		return;
-
-	/* HS PGA gain range: 0x0 - 0xf (0 - 15) */
-	for (i = 0; i < 16; i++) {
-		headset_complete = twl6040_hs_ramp_step(codec,
-						headset->left_step,
-						headset->right_step);
-
-		/* ramp finished ? */
-		if (headset_complete)
-			break;
-
-		schedule_timeout_interruptible(
-				msecs_to_jiffies(headset->step_delay));
-	}
-
-	if (headset->ramp == TWL6040_RAMP_DOWN) {
-		headset->active = 0;
-		complete(&headset->ramp_done);
-	} else {
-		headset->active = 1;
-	}
-	headset->ramp = TWL6040_RAMP_NONE;
-}
-
-static void twl6040_pga_hf_work(struct work_struct *work)
-{
-	struct twl6040_data *priv =
-		container_of(work, struct twl6040_data, handsfree.work.work);
-	struct snd_soc_codec *codec = priv->codec;
-	struct twl6040_output *handsfree = &priv->handsfree;
-	int i, handsfree_complete;
-
-	/* do we need to ramp at all ? */
-	if (handsfree->ramp == TWL6040_RAMP_NONE)
-		return;
-
-	/*
-	 * HF PGA gain range: 0x00 - 0x1d (0 - 29) */
-	for (i = 0; i < 30; i++) {
-		handsfree_complete = twl6040_hf_ramp_step(codec,
-						handsfree->left_step,
-						handsfree->right_step);
-
-		/* ramp finished ? */
-		if (handsfree_complete)
-			break;
-
-		schedule_timeout_interruptible(
-				msecs_to_jiffies(handsfree->step_delay));
-	}
-
-
-	if (handsfree->ramp == TWL6040_RAMP_DOWN) {
-		handsfree->active = 0;
-		complete(&handsfree->ramp_done);
-	} else
-		handsfree->active = 1;
-	handsfree->ramp = TWL6040_RAMP_NONE;
-}
-
-static int out_drv_event(struct snd_soc_dapm_widget *w,
-			struct snd_kcontrol *kcontrol, int event)
-{
-	struct snd_soc_codec *codec = w->codec;
-	struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
-	struct twl6040_output *out;
-	struct delayed_work *work;
-
-	switch (w->shift) {
-	case 2: /* Headset output driver */
-		out = &priv->headset;
-		work = &out->work;
-		/*
-		 * Make sure, that we do not mess up variables for already
-		 * executing work.
-		 */
-		cancel_delayed_work_sync(work);
-
-		out->left_step = priv->hs_left_step;
-		out->right_step = priv->hs_right_step;
-		out->step_delay = 5;	/* 5 ms between volume ramp steps */
-		break;
-	case 4: /* Handsfree output driver */
-		out = &priv->handsfree;
-		work = &out->work;
-		/*
-		 * Make sure, that we do not mess up variables for already
-		 * executing work.
-		 */
-		cancel_delayed_work_sync(work);
-
-		out->left_step = priv->hf_left_step;
-		out->right_step = priv->hf_right_step;
-		out->step_delay = 5;	/* 5 ms between volume ramp steps */
-		break;
-	default:
-		return -1;
-	}
-
-	switch (event) {
-	case SND_SOC_DAPM_POST_PMU:
-		if (out->active)
-			break;
-
-		/* don't use volume ramp for power-up */
-		out->ramp = TWL6040_RAMP_UP;
-		out->left_step = out->left_vol;
-		out->right_step = out->right_vol;
-
-		queue_delayed_work(priv->workqueue, work, msecs_to_jiffies(1));
-		break;
-
-	case SND_SOC_DAPM_PRE_PMD:
-		if (!out->active)
-			break;
-
-		/* use volume ramp for power-down */
-		out->ramp = TWL6040_RAMP_DOWN;
-		INIT_COMPLETION(out->ramp_done);
-
-		queue_delayed_work(priv->workqueue, work, msecs_to_jiffies(1));
-
-		wait_for_completion_timeout(&out->ramp_done,
-					    msecs_to_jiffies(2000));
-		break;
-	}
-
-	return 0;
-}
-
 /* set headset dac and driver power mode */
 static int headset_power_mode(struct snd_soc_codec *codec, int high_perf)
 {
@@ -747,71 +410,6 @@
 	return IRQ_HANDLED;
 }
 
-static int twl6040_put_volsw(struct snd_kcontrol *kcontrol,
-				  struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
-	struct twl6040_data *twl6040_priv = snd_soc_codec_get_drvdata(codec);
-	struct twl6040_output *out = NULL;
-	struct soc_mixer_control *mc =
-		(struct soc_mixer_control *)kcontrol->private_value;
-	int ret;
-
-	/* For HS and HF we shadow the values and only actually write
-	 * them out when active in order to ensure the amplifier comes on
-	 * as quietly as possible. */
-	switch (mc->reg) {
-	case TWL6040_REG_HSGAIN:
-		out = &twl6040_priv->headset;
-		break;
-	case TWL6040_REG_HFLGAIN:
-		out = &twl6040_priv->handsfree;
-		break;
-	default:
-		dev_warn(codec->dev, "%s: Unexpected register: 0x%02x\n",
-					__func__, mc->reg);
-		return -EINVAL;
-	}
-
-	out->left_vol = ucontrol->value.integer.value[0];
-	out->right_vol = ucontrol->value.integer.value[1];
-	if (!out->active)
-		return 1;
-
-	ret = snd_soc_put_volsw(kcontrol, ucontrol);
-	if (ret < 0)
-		return ret;
-
-	return 1;
-}
-
-static int twl6040_get_volsw(struct snd_kcontrol *kcontrol,
-				struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
-	struct twl6040_data *twl6040_priv = snd_soc_codec_get_drvdata(codec);
-	struct twl6040_output *out = &twl6040_priv->headset;
-	struct soc_mixer_control *mc =
-		(struct soc_mixer_control *)kcontrol->private_value;
-
-	switch (mc->reg) {
-	case TWL6040_REG_HSGAIN:
-		out = &twl6040_priv->headset;
-		break;
-	case TWL6040_REG_HFLGAIN:
-		out = &twl6040_priv->handsfree;
-		break;
-	default:
-		dev_warn(codec->dev, "%s: Unexpected register: 0x%02x\n",
-					__func__, mc->reg);
-		return -EINVAL;
-	}
-
-	ucontrol->value.integer.value[0] = out->left_vol;
-	ucontrol->value.integer.value[1] = out->right_vol;
-	return 0;
-}
-
 static int twl6040_soc_dapm_put_vibra_enum(struct snd_kcontrol *kcontrol,
 	struct snd_ctl_elem_value *ucontrol)
 {
@@ -1076,12 +674,10 @@
 		TWL6040_REG_LINEGAIN, 0, 3, 7, 0, afm_amp_tlv),
 
 	/* Playback gains */
-	SOC_DOUBLE_EXT_TLV("Headset Playback Volume",
-		TWL6040_REG_HSGAIN, 0, 4, 0xF, 1, twl6040_get_volsw,
-		twl6040_put_volsw, hs_tlv),
-	SOC_DOUBLE_R_EXT_TLV("Handsfree Playback Volume",
-		TWL6040_REG_HFLGAIN, TWL6040_REG_HFRGAIN, 0, 0x1D, 1,
-		twl6040_get_volsw, twl6040_put_volsw, hf_tlv),
+	SOC_DOUBLE_TLV("Headset Playback Volume",
+		TWL6040_REG_HSGAIN, 0, 4, 0xF, 1, hs_tlv),
+	SOC_DOUBLE_R_TLV("Handsfree Playback Volume",
+		TWL6040_REG_HFLGAIN, TWL6040_REG_HFRGAIN, 0, 0x1D, 1, hf_tlv),
 	SOC_SINGLE_TLV("Earphone Playback Volume",
 		TWL6040_REG_EARCTL, 1, 0xF, 1, ep_tlv),
 
@@ -1180,22 +776,14 @@
 			&auxr_switch_control),
 
 	/* Analog playback drivers */
-	SND_SOC_DAPM_OUT_DRV_E("HF Left Driver",
-			TWL6040_REG_HFLCTL, 4, 0, NULL, 0,
-			out_drv_event,
-			SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
-	SND_SOC_DAPM_OUT_DRV_E("HF Right Driver",
-			TWL6040_REG_HFRCTL, 4, 0, NULL, 0,
-			out_drv_event,
-			SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
-	SND_SOC_DAPM_OUT_DRV_E("HS Left Driver",
-			TWL6040_REG_HSLCTL, 2, 0, NULL, 0,
-			out_drv_event,
-			SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
-	SND_SOC_DAPM_OUT_DRV_E("HS Right Driver",
-			TWL6040_REG_HSRCTL, 2, 0, NULL, 0,
-			out_drv_event,
-			SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+	SND_SOC_DAPM_OUT_DRV("HF Left Driver",
+			TWL6040_REG_HFLCTL, 4, 0, NULL, 0),
+	SND_SOC_DAPM_OUT_DRV("HF Right Driver",
+			TWL6040_REG_HFRCTL, 4, 0, NULL, 0),
+	SND_SOC_DAPM_OUT_DRV("HS Left Driver",
+			TWL6040_REG_HSLCTL, 2, 0, NULL, 0),
+	SND_SOC_DAPM_OUT_DRV("HS Right Driver",
+			TWL6040_REG_HSRCTL, 2, 0, NULL, 0),
 	SND_SOC_DAPM_OUT_DRV_E("Earphone Driver",
 			TWL6040_REG_EARCTL, 0, 0, NULL, 0,
 			twl6040_ep_drv_event,
@@ -1339,8 +927,7 @@
 static int twl6040_startup(struct snd_pcm_substream *substream,
 			struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
 
 	snd_pcm_hw_constraint_list(substream->runtime, 0,
@@ -1354,8 +941,7 @@
 			struct snd_pcm_hw_params *params,
 			struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
 	int rate;
 
@@ -1391,8 +977,7 @@
 static int twl6040_prepare(struct snd_pcm_substream *substream,
 			struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	struct twl6040 *twl6040 = codec->control_data;
 	struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
 	int ret;
@@ -1570,14 +1155,9 @@
 	}
 
 	INIT_DELAYED_WORK(&priv->hs_jack.work, twl6040_accessory_work);
-	INIT_DELAYED_WORK(&priv->headset.work, twl6040_pga_hs_work);
-	INIT_DELAYED_WORK(&priv->handsfree.work, twl6040_pga_hf_work);
 
 	mutex_init(&priv->mutex);
 
-	init_completion(&priv->headset.ramp_done);
-	init_completion(&priv->handsfree.ramp_done);
-
 	ret = request_threaded_irq(priv->plug_irq, NULL, twl6040_audio_handler,
 				   0, "twl6040_irq_plug", codec);
 	if (ret) {
diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c
index 797b0dd..6c3d43b 100644
--- a/sound/soc/codecs/uda134x.c
+++ b/sound/soc/codecs/uda134x.c
@@ -159,8 +159,7 @@
 static int uda134x_startup(struct snd_pcm_substream *substream,
 	struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec =rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(codec);
 	struct snd_pcm_runtime *master_runtime;
 
@@ -191,8 +190,7 @@
 static void uda134x_shutdown(struct snd_pcm_substream *substream,
 	struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(codec);
 
 	if (uda134x->master_substream == substream)
diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c
index 4f1b23d..2502214 100644
--- a/sound/soc/codecs/uda1380.c
+++ b/sound/soc/codecs/uda1380.c
@@ -502,8 +502,7 @@
 static int uda1380_trigger(struct snd_pcm_substream *substream, int cmd,
 		struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	struct uda1380_priv *uda1380 = snd_soc_codec_get_drvdata(codec);
 	int mixer = uda1380_read_reg_cache(codec, UDA1380_MIXER);
 
@@ -528,8 +527,7 @@
 				 struct snd_pcm_hw_params *params,
 				 struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	u16 clk = uda1380_read_reg_cache(codec, UDA1380_CLK);
 
 	/* set WSPLL power and divider if running from this clock */
diff --git a/sound/soc/codecs/wl1273.c b/sound/soc/codecs/wl1273.c
index 3d868dc..7b24d6d 100644
--- a/sound/soc/codecs/wl1273.c
+++ b/sound/soc/codecs/wl1273.c
@@ -293,8 +293,7 @@
 static int wl1273_startup(struct snd_pcm_substream *substream,
 			  struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(codec);
 
 	switch (wl1273->mode) {
@@ -329,8 +328,7 @@
 			    struct snd_pcm_hw_params *params,
 			    struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(rtd->codec);
+	struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(dai->codec);
 	struct wl1273_core *core = wl1273->core;
 	unsigned int rate, width, r;
 
diff --git a/sound/soc/codecs/wm1250-ev1.c b/sound/soc/codecs/wm1250-ev1.c
index aefb4f8..e0b51e9 100644
--- a/sound/soc/codecs/wm1250-ev1.c
+++ b/sound/soc/codecs/wm1250-ev1.c
@@ -79,22 +79,65 @@
 	{ "WM1250 Output", NULL, "DAC" },
 };
 
+static int wm1250_ev1_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params,
+				struct snd_soc_dai *dai)
+{
+	struct wm1250_priv *wm1250 = snd_soc_codec_get_drvdata(dai->codec);
+
+	switch (params_rate(params)) {
+	case 8000:
+		gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL0].gpio,
+			       1);
+		gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL1].gpio,
+			       1);
+		break;
+	case 16000:
+		gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL0].gpio,
+			       0);
+		gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL1].gpio,
+			       1);
+		break;
+	case 32000:
+		gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL0].gpio,
+			       1);
+		gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL1].gpio,
+			       0);
+		break;
+	case 64000:
+		gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL0].gpio,
+			       0);
+		gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL1].gpio,
+			       0);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static const struct snd_soc_dai_ops wm1250_ev1_ops = {
+	.hw_params = wm1250_ev1_hw_params,
+};
+
 static struct snd_soc_dai_driver wm1250_ev1_dai = {
 	.name = "wm1250-ev1",
 	.playback = {
 		.stream_name = "Playback",
 		.channels_min = 1,
-		.channels_max = 1,
+		.channels_max = 2,
 		.rates = SNDRV_PCM_RATE_8000,
 		.formats = SNDRV_PCM_FMTBIT_S16_LE,
 	},
 	.capture = {
 		.stream_name = "Capture",
 		.channels_min = 1,
-		.channels_max = 1,
+		.channels_max = 2,
 		.rates = SNDRV_PCM_RATE_8000,
 		.formats = SNDRV_PCM_FMTBIT_S16_LE,
 	},
+	.ops = &wm1250_ev1_ops,
 };
 
 static struct snd_soc_codec_driver soc_codec_dev_wm1250_ev1 = {
@@ -215,23 +258,7 @@
 	.id_table = wm1250_ev1_i2c_id,
 };
 
-static int __init wm1250_ev1_modinit(void)
-{
-	int ret = 0;
-
-	ret = i2c_add_driver(&wm1250_ev1_i2c_driver);
-	if (ret != 0)
-		pr_err("Failed to register WM1250-EV1 I2C driver: %d\n", ret);
-
-	return ret;
-}
-module_init(wm1250_ev1_modinit);
-
-static void __exit wm1250_ev1_exit(void)
-{
-	i2c_del_driver(&wm1250_ev1_i2c_driver);
-}
-module_exit(wm1250_ev1_exit);
+module_i2c_driver(wm1250_ev1_i2c_driver);
 
 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
 MODULE_DESCRIPTION("WM1250-EV1 audio I/O module driver");
diff --git a/sound/soc/codecs/wm5100-tables.c b/sound/soc/codecs/wm5100-tables.c
index 9a18fae..e167207 100644
--- a/sound/soc/codecs/wm5100-tables.c
+++ b/sound/soc/codecs/wm5100-tables.c
@@ -32,7 +32,18 @@
 	case WM5100_MIC_DETECT_3:
 		return 1;
 	default:
-		return 0;
+		if ((reg >= WM5100_DSP1_PM_0 && reg <= WM5100_DSP1_PM_1535) ||
+		    (reg >= WM5100_DSP1_ZM_0 && reg <= WM5100_DSP1_ZM_2047) ||
+		    (reg >= WM5100_DSP1_DM_0 && reg <= WM5100_DSP1_DM_511) ||
+		    (reg >= WM5100_DSP2_PM_0 && reg <= WM5100_DSP2_PM_1535) ||
+		    (reg >= WM5100_DSP2_ZM_0 && reg <= WM5100_DSP2_ZM_2047) ||
+		    (reg >= WM5100_DSP2_DM_0 && reg <= WM5100_DSP2_DM_511) ||
+		    (reg >= WM5100_DSP3_PM_0 && reg <= WM5100_DSP3_PM_1535) ||
+		    (reg >= WM5100_DSP3_ZM_0 && reg <= WM5100_DSP3_ZM_2047) ||
+		    (reg >= WM5100_DSP3_DM_0 && reg <= WM5100_DSP3_DM_511))
+			return 1;
+		else
+			return 0;
 	}
 }
 
@@ -697,9 +708,110 @@
 	case WM5100_HPLPF3_2:
 	case WM5100_HPLPF4_1:
 	case WM5100_HPLPF4_2:
+	case WM5100_DSP1_CONTROL_1:
+	case WM5100_DSP1_CONTROL_2:
+	case WM5100_DSP1_CONTROL_3:
+	case WM5100_DSP1_CONTROL_4:
+	case WM5100_DSP1_CONTROL_5:
+	case WM5100_DSP1_CONTROL_6:
+	case WM5100_DSP1_CONTROL_7:
+	case WM5100_DSP1_CONTROL_8:
+	case WM5100_DSP1_CONTROL_9:
+	case WM5100_DSP1_CONTROL_10:
+	case WM5100_DSP1_CONTROL_11:
+	case WM5100_DSP1_CONTROL_12:
+	case WM5100_DSP1_CONTROL_13:
+	case WM5100_DSP1_CONTROL_14:
+	case WM5100_DSP1_CONTROL_15:
+	case WM5100_DSP1_CONTROL_16:
+	case WM5100_DSP1_CONTROL_17:
+	case WM5100_DSP1_CONTROL_18:
+	case WM5100_DSP1_CONTROL_19:
+	case WM5100_DSP1_CONTROL_20:
+	case WM5100_DSP1_CONTROL_21:
+	case WM5100_DSP1_CONTROL_22:
+	case WM5100_DSP1_CONTROL_23:
+	case WM5100_DSP1_CONTROL_24:
+	case WM5100_DSP1_CONTROL_25:
+	case WM5100_DSP1_CONTROL_26:
+	case WM5100_DSP1_CONTROL_27:
+	case WM5100_DSP1_CONTROL_28:
+	case WM5100_DSP1_CONTROL_29:
+	case WM5100_DSP1_CONTROL_30:
+	case WM5100_DSP2_CONTROL_1:
+	case WM5100_DSP2_CONTROL_2:
+	case WM5100_DSP2_CONTROL_3:
+	case WM5100_DSP2_CONTROL_4:
+	case WM5100_DSP2_CONTROL_5:
+	case WM5100_DSP2_CONTROL_6:
+	case WM5100_DSP2_CONTROL_7:
+	case WM5100_DSP2_CONTROL_8:
+	case WM5100_DSP2_CONTROL_9:
+	case WM5100_DSP2_CONTROL_10:
+	case WM5100_DSP2_CONTROL_11:
+	case WM5100_DSP2_CONTROL_12:
+	case WM5100_DSP2_CONTROL_13:
+	case WM5100_DSP2_CONTROL_14:
+	case WM5100_DSP2_CONTROL_15:
+	case WM5100_DSP2_CONTROL_16:
+	case WM5100_DSP2_CONTROL_17:
+	case WM5100_DSP2_CONTROL_18:
+	case WM5100_DSP2_CONTROL_19:
+	case WM5100_DSP2_CONTROL_20:
+	case WM5100_DSP2_CONTROL_21:
+	case WM5100_DSP2_CONTROL_22:
+	case WM5100_DSP2_CONTROL_23:
+	case WM5100_DSP2_CONTROL_24:
+	case WM5100_DSP2_CONTROL_25:
+	case WM5100_DSP2_CONTROL_26:
+	case WM5100_DSP2_CONTROL_27:
+	case WM5100_DSP2_CONTROL_28:
+	case WM5100_DSP2_CONTROL_29:
+	case WM5100_DSP2_CONTROL_30:
+	case WM5100_DSP3_CONTROL_1:
+	case WM5100_DSP3_CONTROL_2:
+	case WM5100_DSP3_CONTROL_3:
+	case WM5100_DSP3_CONTROL_4:
+	case WM5100_DSP3_CONTROL_5:
+	case WM5100_DSP3_CONTROL_6:
+	case WM5100_DSP3_CONTROL_7:
+	case WM5100_DSP3_CONTROL_8:
+	case WM5100_DSP3_CONTROL_9:
+	case WM5100_DSP3_CONTROL_10:
+	case WM5100_DSP3_CONTROL_11:
+	case WM5100_DSP3_CONTROL_12:
+	case WM5100_DSP3_CONTROL_13:
+	case WM5100_DSP3_CONTROL_14:
+	case WM5100_DSP3_CONTROL_15:
+	case WM5100_DSP3_CONTROL_16:
+	case WM5100_DSP3_CONTROL_17:
+	case WM5100_DSP3_CONTROL_18:
+	case WM5100_DSP3_CONTROL_19:
+	case WM5100_DSP3_CONTROL_20:
+	case WM5100_DSP3_CONTROL_21:
+	case WM5100_DSP3_CONTROL_22:
+	case WM5100_DSP3_CONTROL_23:
+	case WM5100_DSP3_CONTROL_24:
+	case WM5100_DSP3_CONTROL_25:
+	case WM5100_DSP3_CONTROL_26:
+	case WM5100_DSP3_CONTROL_27:
+	case WM5100_DSP3_CONTROL_28:
+	case WM5100_DSP3_CONTROL_29:
+	case WM5100_DSP3_CONTROL_30:
 		return 1;
 	default:
-		return 0;
+		if ((reg >= WM5100_DSP1_PM_0 && reg <= WM5100_DSP1_PM_1535) ||
+		    (reg >= WM5100_DSP1_ZM_0 && reg <= WM5100_DSP1_ZM_2047) ||
+		    (reg >= WM5100_DSP1_DM_0 && reg <= WM5100_DSP1_DM_511) ||
+		    (reg >= WM5100_DSP2_PM_0 && reg <= WM5100_DSP2_PM_1535) ||
+		    (reg >= WM5100_DSP2_ZM_0 && reg <= WM5100_DSP2_ZM_2047) ||
+		    (reg >= WM5100_DSP2_DM_0 && reg <= WM5100_DSP2_DM_511) ||
+		    (reg >= WM5100_DSP3_PM_0 && reg <= WM5100_DSP3_PM_1535) ||
+		    (reg >= WM5100_DSP3_ZM_0 && reg <= WM5100_DSP3_ZM_2047) ||
+		    (reg >= WM5100_DSP3_DM_0 && reg <= WM5100_DSP3_DM_511))
+			return 1;
+		else
+			return 0;
 	}
 }
 
@@ -1361,4 +1473,13 @@
 	{ 0x0EC9, 0x0000 },  /* R3785  - HPLPF3_2 */
 	{ 0x0ECC, 0x0000 },  /* R3788  - HPLPF4_1 */
 	{ 0x0ECD, 0x0000 },  /* R3789  - HPLPF4_2 */
+	{ 0x0F02, 0x0000 },  /* R3842  - DSP1 Control 2 */
+	{ 0x0F03, 0x0000 },  /* R3843  - DSP1 Control 3 */
+	{ 0x0F04, 0x0000 },  /* R3844  - DSP1 Control 4 */
+	{ 0x1002, 0x0000 },  /* R4098  - DSP2 Control 2 */
+	{ 0x1003, 0x0000 },  /* R4099  - DSP2 Control 3 */
+	{ 0x1004, 0x0000 },  /* R4100  - DSP2 Control 4 */
+	{ 0x1102, 0x0000 },  /* R4354  - DSP3 Control 2 */
+	{ 0x1103, 0x0000 },  /* R4355  - DSP3 Control 3 */
+	{ 0x1104, 0x0000 },  /* R4356  - DSP3 Control 4 */
 };
diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c
index b9c185c..cb6d537 100644
--- a/sound/soc/codecs/wm5100.c
+++ b/sound/soc/codecs/wm5100.c
@@ -1265,29 +1265,12 @@
 	{ WM5100_AUDIO_IF_3_19, 1 },
 };
 
-static int wm5100_dai_to_base(struct snd_soc_dai *dai)
-{
-	switch (dai->id) {
-	case 0:
-		return WM5100_AUDIO_IF_1_1 - 1;
-	case 1:
-		return WM5100_AUDIO_IF_2_1 - 1;
-	case 2:
-		return WM5100_AUDIO_IF_3_1 - 1;
-	default:
-		BUG();
-		return -EINVAL;
-	}
-}
-
 static int wm5100_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 {
 	struct snd_soc_codec *codec = dai->codec;
 	int lrclk, bclk, mask, base;
 
-	base = wm5100_dai_to_base(dai);
-	if (base < 0)
-		return base;
+	base = dai->driver->base;
 
 	lrclk = 0;
 	bclk = 0;
@@ -1414,9 +1397,7 @@
 	int i, base, bclk, aif_rate, lrclk, wl, fl, sr;
 	int *bclk_rates;
 
-	base = wm5100_dai_to_base(dai);
-	if (base < 0)
-		return base;
+	base = dai->driver->base;
 
 	/* Data sizes if not using TDM */
 	wl = snd_pcm_format_width(params_format(params));
@@ -1897,6 +1878,7 @@
 static struct snd_soc_dai_driver wm5100_dai[] = {
 	{
 		.name = "wm5100-aif1",
+		.base = WM5100_AUDIO_IF_1_1 - 1,
 		.playback = {
 			.stream_name = "AIF1 Playback",
 			.channels_min = 2,
@@ -1916,6 +1898,7 @@
 	{
 		.name = "wm5100-aif2",
 		.id = 1,
+		.base = WM5100_AUDIO_IF_2_1 - 1,
 		.playback = {
 			.stream_name = "AIF2 Playback",
 			.channels_min = 2,
@@ -1935,6 +1918,7 @@
 	{
 		.name = "wm5100-aif3",
 		.id = 2,
+		.base = WM5100_AUDIO_IF_3_1 - 1,
 		.playback = {
 			.stream_name = "AIF3 Playback",
 			.channels_min = 2,
@@ -2454,7 +2438,7 @@
 
 	wm5100->dev = &i2c->dev;
 
-	wm5100->regmap = regmap_init_i2c(i2c, &wm5100_regmap);
+	wm5100->regmap = devm_regmap_init_i2c(i2c, &wm5100_regmap);
 	if (IS_ERR(wm5100->regmap)) {
 		ret = PTR_ERR(wm5100->regmap);
 		dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
@@ -2479,7 +2463,7 @@
 	if (ret != 0) {
 		dev_err(&i2c->dev, "Failed to request core supplies: %d\n",
 			ret);
-		goto err_regmap;
+		goto err;
 	}
 
 	ret = regulator_bulk_enable(ARRAY_SIZE(wm5100->core_supplies),
@@ -2487,7 +2471,7 @@
 	if (ret != 0) {
 		dev_err(&i2c->dev, "Failed to enable core supplies: %d\n",
 			ret);
-		goto err_regmap;
+		goto err;
 	}
 
 	if (wm5100->pdata.ldo_ena) {
@@ -2660,8 +2644,6 @@
 err_enable:
 	regulator_bulk_disable(ARRAY_SIZE(wm5100->core_supplies),
 			       wm5100->core_supplies);
-err_regmap:
-	regmap_exit(wm5100->regmap);
 err:
 	return ret;
 }
@@ -2682,7 +2664,6 @@
 		gpio_set_value_cansleep(wm5100->pdata.ldo_ena, 0);
 		gpio_free(wm5100->pdata.ldo_ena);
 	}
-	regmap_exit(wm5100->regmap);
 
 	return 0;
 }
@@ -2749,17 +2730,7 @@
 	.id_table = wm5100_i2c_id,
 };
 
-static int __init wm5100_modinit(void)
-{
-	return i2c_add_driver(&wm5100_i2c_driver);
-}
-module_init(wm5100_modinit);
-
-static void __exit wm5100_exit(void)
-{
-	i2c_del_driver(&wm5100_i2c_driver);
-}
-module_exit(wm5100_exit);
+module_i2c_driver(wm5100_i2c_driver);
 
 MODULE_DESCRIPTION("ASoC WM5100 driver");
 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
diff --git a/sound/soc/codecs/wm5100.h b/sound/soc/codecs/wm5100.h
index 25cb601..935a9b7 100644
--- a/sound/soc/codecs/wm5100.h
+++ b/sound/soc/codecs/wm5100.h
@@ -709,6 +709,96 @@
 #define WM5100_HPLPF3_2                         0xEC9
 #define WM5100_HPLPF4_1                         0xECC
 #define WM5100_HPLPF4_2                         0xECD
+#define WM5100_DSP1_CONTROL_1                   0xF00
+#define WM5100_DSP1_CONTROL_2                   0xF02
+#define WM5100_DSP1_CONTROL_3                   0xF03
+#define WM5100_DSP1_CONTROL_4                   0xF04
+#define WM5100_DSP1_CONTROL_5                   0xF06
+#define WM5100_DSP1_CONTROL_6                   0xF07
+#define WM5100_DSP1_CONTROL_7                   0xF08
+#define WM5100_DSP1_CONTROL_8                   0xF09
+#define WM5100_DSP1_CONTROL_9                   0xF0A
+#define WM5100_DSP1_CONTROL_10                  0xF0B
+#define WM5100_DSP1_CONTROL_11                  0xF0C
+#define WM5100_DSP1_CONTROL_12                  0xF0D
+#define WM5100_DSP1_CONTROL_13                  0xF0F
+#define WM5100_DSP1_CONTROL_14                  0xF10
+#define WM5100_DSP1_CONTROL_15                  0xF11
+#define WM5100_DSP1_CONTROL_16                  0xF12
+#define WM5100_DSP1_CONTROL_17                  0xF13
+#define WM5100_DSP1_CONTROL_18                  0xF14
+#define WM5100_DSP1_CONTROL_19                  0xF16
+#define WM5100_DSP1_CONTROL_20                  0xF17
+#define WM5100_DSP1_CONTROL_21                  0xF18
+#define WM5100_DSP1_CONTROL_22                  0xF1A
+#define WM5100_DSP1_CONTROL_23                  0xF1B
+#define WM5100_DSP1_CONTROL_24                  0xF1C
+#define WM5100_DSP1_CONTROL_25                  0xF1E
+#define WM5100_DSP1_CONTROL_26                  0xF20
+#define WM5100_DSP1_CONTROL_27                  0xF21
+#define WM5100_DSP1_CONTROL_28                  0xF22
+#define WM5100_DSP1_CONTROL_29                  0xF23
+#define WM5100_DSP1_CONTROL_30                  0xF24
+#define WM5100_DSP2_CONTROL_1                   0x1000
+#define WM5100_DSP2_CONTROL_2                   0x1002
+#define WM5100_DSP2_CONTROL_3                   0x1003
+#define WM5100_DSP2_CONTROL_4                   0x1004
+#define WM5100_DSP2_CONTROL_5                   0x1006
+#define WM5100_DSP2_CONTROL_6                   0x1007
+#define WM5100_DSP2_CONTROL_7                   0x1008
+#define WM5100_DSP2_CONTROL_8                   0x1009
+#define WM5100_DSP2_CONTROL_9                   0x100A
+#define WM5100_DSP2_CONTROL_10                  0x100B
+#define WM5100_DSP2_CONTROL_11                  0x100C
+#define WM5100_DSP2_CONTROL_12                  0x100D
+#define WM5100_DSP2_CONTROL_13                  0x100F
+#define WM5100_DSP2_CONTROL_14                  0x1010
+#define WM5100_DSP2_CONTROL_15                  0x1011
+#define WM5100_DSP2_CONTROL_16                  0x1012
+#define WM5100_DSP2_CONTROL_17                  0x1013
+#define WM5100_DSP2_CONTROL_18                  0x1014
+#define WM5100_DSP2_CONTROL_19                  0x1016
+#define WM5100_DSP2_CONTROL_20                  0x1017
+#define WM5100_DSP2_CONTROL_21                  0x1018
+#define WM5100_DSP2_CONTROL_22                  0x101A
+#define WM5100_DSP2_CONTROL_23                  0x101B
+#define WM5100_DSP2_CONTROL_24                  0x101C
+#define WM5100_DSP2_CONTROL_25                  0x101E
+#define WM5100_DSP2_CONTROL_26                  0x1020
+#define WM5100_DSP2_CONTROL_27                  0x1021
+#define WM5100_DSP2_CONTROL_28                  0x1022
+#define WM5100_DSP2_CONTROL_29                  0x1023
+#define WM5100_DSP2_CONTROL_30                  0x1024
+#define WM5100_DSP3_CONTROL_1                   0x1100
+#define WM5100_DSP3_CONTROL_2                   0x1102
+#define WM5100_DSP3_CONTROL_3                   0x1103
+#define WM5100_DSP3_CONTROL_4                   0x1104
+#define WM5100_DSP3_CONTROL_5                   0x1106
+#define WM5100_DSP3_CONTROL_6                   0x1107
+#define WM5100_DSP3_CONTROL_7                   0x1108
+#define WM5100_DSP3_CONTROL_8                   0x1109
+#define WM5100_DSP3_CONTROL_9                   0x110A
+#define WM5100_DSP3_CONTROL_10                  0x110B
+#define WM5100_DSP3_CONTROL_11                  0x110C
+#define WM5100_DSP3_CONTROL_12                  0x110D
+#define WM5100_DSP3_CONTROL_13                  0x110F
+#define WM5100_DSP3_CONTROL_14                  0x1110
+#define WM5100_DSP3_CONTROL_15                  0x1111
+#define WM5100_DSP3_CONTROL_16                  0x1112
+#define WM5100_DSP3_CONTROL_17                  0x1113
+#define WM5100_DSP3_CONTROL_18                  0x1114
+#define WM5100_DSP3_CONTROL_19                  0x1116
+#define WM5100_DSP3_CONTROL_20                  0x1117
+#define WM5100_DSP3_CONTROL_21                  0x1118
+#define WM5100_DSP3_CONTROL_22                  0x111A
+#define WM5100_DSP3_CONTROL_23                  0x111B
+#define WM5100_DSP3_CONTROL_24                  0x111C
+#define WM5100_DSP3_CONTROL_25                  0x111E
+#define WM5100_DSP3_CONTROL_26                  0x1120
+#define WM5100_DSP3_CONTROL_27                  0x1121
+#define WM5100_DSP3_CONTROL_28                  0x1122
+#define WM5100_DSP3_CONTROL_29                  0x1123
+#define WM5100_DSP3_CONTROL_30                  0x1124
 #define WM5100_DSP1_DM_0                        0x4000
 #define WM5100_DSP1_DM_1                        0x4001
 #define WM5100_DSP1_DM_2                        0x4002
@@ -4561,6 +4651,75 @@
 #define WM5100_LHPF4_COEFF_WIDTH                    16  /* LHPF4_COEFF - [15:0] */
 
 /*
+ * R4132 (0x1024) - DSP2 Control 30
+ */
+#define WM5100_DSP2_RATE_MASK                   0xC000  /* DSP2_RATE - [15:14] */
+#define WM5100_DSP2_RATE_SHIFT                      14  /* DSP2_RATE - [15:14] */
+#define WM5100_DSP2_RATE_WIDTH                       2  /* DSP2_RATE - [15:14] */
+#define WM5100_DSP2_DBG_CLK_ENA                 0x0008  /* DSP2_DBG_CLK_ENA */
+#define WM5100_DSP2_DBG_CLK_ENA_MASK            0x0008  /* DSP2_DBG_CLK_ENA */
+#define WM5100_DSP2_DBG_CLK_ENA_SHIFT                3  /* DSP2_DBG_CLK_ENA */
+#define WM5100_DSP2_DBG_CLK_ENA_WIDTH                1  /* DSP2_DBG_CLK_ENA */
+#define WM5100_DSP2_SYS_ENA                     0x0004  /* DSP2_SYS_ENA */
+#define WM5100_DSP2_SYS_ENA_MASK                0x0004  /* DSP2_SYS_ENA */
+#define WM5100_DSP2_SYS_ENA_SHIFT                    2  /* DSP2_SYS_ENA */
+#define WM5100_DSP2_SYS_ENA_WIDTH                    1  /* DSP2_SYS_ENA */
+#define WM5100_DSP2_CORE_ENA                    0x0002  /* DSP2_CORE_ENA */
+#define WM5100_DSP2_CORE_ENA_MASK               0x0002  /* DSP2_CORE_ENA */
+#define WM5100_DSP2_CORE_ENA_SHIFT                   1  /* DSP2_CORE_ENA */
+#define WM5100_DSP2_CORE_ENA_WIDTH                   1  /* DSP2_CORE_ENA */
+#define WM5100_DSP2_START                       0x0001  /* DSP2_START */
+#define WM5100_DSP2_START_MASK                  0x0001  /* DSP2_START */
+#define WM5100_DSP2_START_SHIFT                      0  /* DSP2_START */
+#define WM5100_DSP2_START_WIDTH                      1  /* DSP2_START */
+
+/*
+ * R3876 (0xF24) - DSP1 Control 30
+ */
+#define WM5100_DSP1_RATE_MASK                   0xC000  /* DSP1_RATE - [15:14] */
+#define WM5100_DSP1_RATE_SHIFT                      14  /* DSP1_RATE - [15:14] */
+#define WM5100_DSP1_RATE_WIDTH                       2  /* DSP1_RATE - [15:14] */
+#define WM5100_DSP1_DBG_CLK_ENA                 0x0008  /* DSP1_DBG_CLK_ENA */
+#define WM5100_DSP1_DBG_CLK_ENA_MASK            0x0008  /* DSP1_DBG_CLK_ENA */
+#define WM5100_DSP1_DBG_CLK_ENA_SHIFT                3  /* DSP1_DBG_CLK_ENA */
+#define WM5100_DSP1_DBG_CLK_ENA_WIDTH                1  /* DSP1_DBG_CLK_ENA */
+#define WM5100_DSP1_SYS_ENA                     0x0004  /* DSP1_SYS_ENA */
+#define WM5100_DSP1_SYS_ENA_MASK                0x0004  /* DSP1_SYS_ENA */
+#define WM5100_DSP1_SYS_ENA_SHIFT                    2  /* DSP1_SYS_ENA */
+#define WM5100_DSP1_SYS_ENA_WIDTH                    1  /* DSP1_SYS_ENA */
+#define WM5100_DSP1_CORE_ENA                    0x0002  /* DSP1_CORE_ENA */
+#define WM5100_DSP1_CORE_ENA_MASK               0x0002  /* DSP1_CORE_ENA */
+#define WM5100_DSP1_CORE_ENA_SHIFT                   1  /* DSP1_CORE_ENA */
+#define WM5100_DSP1_CORE_ENA_WIDTH                   1  /* DSP1_CORE_ENA */
+#define WM5100_DSP1_START                       0x0001  /* DSP1_START */
+#define WM5100_DSP1_START_MASK                  0x0001  /* DSP1_START */
+#define WM5100_DSP1_START_SHIFT                      0  /* DSP1_START */
+#define WM5100_DSP1_START_WIDTH                      1  /* DSP1_START */
+
+/*
+ * R4388 (0x1124) - DSP3 Control 30
+ */
+#define WM5100_DSP3_RATE_MASK                   0xC000  /* DSP3_RATE - [15:14] */
+#define WM5100_DSP3_RATE_SHIFT                      14  /* DSP3_RATE - [15:14] */
+#define WM5100_DSP3_RATE_WIDTH                       2  /* DSP3_RATE - [15:14] */
+#define WM5100_DSP3_DBG_CLK_ENA                 0x0008  /* DSP3_DBG_CLK_ENA */
+#define WM5100_DSP3_DBG_CLK_ENA_MASK            0x0008  /* DSP3_DBG_CLK_ENA */
+#define WM5100_DSP3_DBG_CLK_ENA_SHIFT                3  /* DSP3_DBG_CLK_ENA */
+#define WM5100_DSP3_DBG_CLK_ENA_WIDTH                1  /* DSP3_DBG_CLK_ENA */
+#define WM5100_DSP3_SYS_ENA                     0x0004  /* DSP3_SYS_ENA */
+#define WM5100_DSP3_SYS_ENA_MASK                0x0004  /* DSP3_SYS_ENA */
+#define WM5100_DSP3_SYS_ENA_SHIFT                    2  /* DSP3_SYS_ENA */
+#define WM5100_DSP3_SYS_ENA_WIDTH                    1  /* DSP3_SYS_ENA */
+#define WM5100_DSP3_CORE_ENA                    0x0002  /* DSP3_CORE_ENA */
+#define WM5100_DSP3_CORE_ENA_MASK               0x0002  /* DSP3_CORE_ENA */
+#define WM5100_DSP3_CORE_ENA_SHIFT                   1  /* DSP3_CORE_ENA */
+#define WM5100_DSP3_CORE_ENA_WIDTH                   1  /* DSP3_CORE_ENA */
+#define WM5100_DSP3_START                       0x0001  /* DSP3_START */
+#define WM5100_DSP3_START_MASK                  0x0001  /* DSP3_START */
+#define WM5100_DSP3_START_SHIFT                      0  /* DSP3_START */
+#define WM5100_DSP3_START_WIDTH                      1  /* DSP3_START */
+
+/*
  * R16384 (0x4000) - DSP1 DM 0
  */
 #define WM5100_DSP1_DM_START_1_MASK             0x00FF  /* DSP1_DM_START - [7:0] */
diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c
index aa12c6b..555ee14 100644
--- a/sound/soc/codecs/wm8350.c
+++ b/sound/soc/codecs/wm8350.c
@@ -71,13 +71,6 @@
 	int fll_freq_in;
 };
 
-static unsigned int wm8350_codec_cache_read(struct snd_soc_codec *codec,
-					    unsigned int reg)
-{
-	struct wm8350 *wm8350 = codec->control_data;
-	return wm8350->reg_cache[reg];
-}
-
 static unsigned int wm8350_codec_read(struct snd_soc_codec *codec,
 				      unsigned int reg)
 {
@@ -99,7 +92,7 @@
 {
 	struct wm8350_data *wm8350_data = snd_soc_codec_get_drvdata(codec);
 	struct wm8350_output *out1 = &wm8350_data->out1;
-	struct wm8350 *wm8350 = codec->control_data;
+	struct wm8350 *wm8350 = wm8350_data->wm8350;
 	int left_complete = 0, right_complete = 0;
 	u16 reg, val;
 
@@ -165,7 +158,7 @@
 {
 	struct wm8350_data *wm8350_data = snd_soc_codec_get_drvdata(codec);
 	struct wm8350_output *out2 = &wm8350_data->out2;
-	struct wm8350 *wm8350 = codec->control_data;
+	struct wm8350 *wm8350 = wm8350_data->wm8350;
 	int left_complete = 0, right_complete = 0;
 	u16 reg, val;
 
@@ -360,8 +353,8 @@
 		return ret;
 
 	/* now hit the volume update bits (always bit 8) */
-	val = wm8350_codec_read(codec, reg);
-	wm8350_codec_write(codec, reg, val | WM8350_OUT1_VU);
+	val = snd_soc_read(codec, reg);
+	snd_soc_write(codec, reg, val | WM8350_OUT1_VU);
 	return 1;
 }
 
@@ -781,7 +774,8 @@
 				 int clk_id, unsigned int freq, int dir)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
-	struct wm8350 *wm8350 = codec->control_data;
+	struct wm8350_data *wm8350_data = snd_soc_codec_get_drvdata(codec);
+	struct wm8350 *wm8350 = wm8350_data->wm8350;
 	u16 fll_4;
 
 	switch (clk_id) {
@@ -795,9 +789,9 @@
 	case WM8350_MCLK_SEL_PLL_32K:
 		wm8350_set_bits(wm8350, WM8350_CLOCK_CONTROL_1,
 				WM8350_MCLK_SEL);
-		fll_4 = wm8350_codec_read(codec, WM8350_FLL_CONTROL_4) &
+		fll_4 = snd_soc_read(codec, WM8350_FLL_CONTROL_4) &
 		    ~WM8350_FLL_CLK_SRC_MASK;
-		wm8350_codec_write(codec, WM8350_FLL_CONTROL_4, fll_4 | clk_id);
+		snd_soc_write(codec, WM8350_FLL_CONTROL_4, fll_4 | clk_id);
 		break;
 	}
 
@@ -819,39 +813,39 @@
 
 	switch (div_id) {
 	case WM8350_ADC_CLKDIV:
-		val = wm8350_codec_read(codec, WM8350_ADC_DIVIDER) &
+		val = snd_soc_read(codec, WM8350_ADC_DIVIDER) &
 		    ~WM8350_ADC_CLKDIV_MASK;
-		wm8350_codec_write(codec, WM8350_ADC_DIVIDER, val | div);
+		snd_soc_write(codec, WM8350_ADC_DIVIDER, val | div);
 		break;
 	case WM8350_DAC_CLKDIV:
-		val = wm8350_codec_read(codec, WM8350_DAC_CLOCK_CONTROL) &
+		val = snd_soc_read(codec, WM8350_DAC_CLOCK_CONTROL) &
 		    ~WM8350_DAC_CLKDIV_MASK;
-		wm8350_codec_write(codec, WM8350_DAC_CLOCK_CONTROL, val | div);
+		snd_soc_write(codec, WM8350_DAC_CLOCK_CONTROL, val | div);
 		break;
 	case WM8350_BCLK_CLKDIV:
-		val = wm8350_codec_read(codec, WM8350_CLOCK_CONTROL_1) &
+		val = snd_soc_read(codec, WM8350_CLOCK_CONTROL_1) &
 		    ~WM8350_BCLK_DIV_MASK;
-		wm8350_codec_write(codec, WM8350_CLOCK_CONTROL_1, val | div);
+		snd_soc_write(codec, WM8350_CLOCK_CONTROL_1, val | div);
 		break;
 	case WM8350_OPCLK_CLKDIV:
-		val = wm8350_codec_read(codec, WM8350_CLOCK_CONTROL_1) &
+		val = snd_soc_read(codec, WM8350_CLOCK_CONTROL_1) &
 		    ~WM8350_OPCLK_DIV_MASK;
-		wm8350_codec_write(codec, WM8350_CLOCK_CONTROL_1, val | div);
+		snd_soc_write(codec, WM8350_CLOCK_CONTROL_1, val | div);
 		break;
 	case WM8350_SYS_CLKDIV:
-		val = wm8350_codec_read(codec, WM8350_CLOCK_CONTROL_1) &
+		val = snd_soc_read(codec, WM8350_CLOCK_CONTROL_1) &
 		    ~WM8350_MCLK_DIV_MASK;
-		wm8350_codec_write(codec, WM8350_CLOCK_CONTROL_1, val | div);
+		snd_soc_write(codec, WM8350_CLOCK_CONTROL_1, val | div);
 		break;
 	case WM8350_DACLR_CLKDIV:
-		val = wm8350_codec_read(codec, WM8350_DAC_LR_RATE) &
+		val = snd_soc_read(codec, WM8350_DAC_LR_RATE) &
 		    ~WM8350_DACLRC_RATE_MASK;
-		wm8350_codec_write(codec, WM8350_DAC_LR_RATE, val | div);
+		snd_soc_write(codec, WM8350_DAC_LR_RATE, val | div);
 		break;
 	case WM8350_ADCLR_CLKDIV:
-		val = wm8350_codec_read(codec, WM8350_ADC_LR_RATE) &
+		val = snd_soc_read(codec, WM8350_ADC_LR_RATE) &
 		    ~WM8350_ADCLRC_RATE_MASK;
-		wm8350_codec_write(codec, WM8350_ADC_LR_RATE, val | div);
+		snd_soc_write(codec, WM8350_ADC_LR_RATE, val | div);
 		break;
 	default:
 		return -EINVAL;
@@ -863,13 +857,13 @@
 static int wm8350_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
-	u16 iface = wm8350_codec_read(codec, WM8350_AI_FORMATING) &
+	u16 iface = snd_soc_read(codec, WM8350_AI_FORMATING) &
 	    ~(WM8350_AIF_BCLK_INV | WM8350_AIF_LRCLK_INV | WM8350_AIF_FMT_MASK);
-	u16 master = wm8350_codec_read(codec, WM8350_AI_DAC_CONTROL) &
+	u16 master = snd_soc_read(codec, WM8350_AI_DAC_CONTROL) &
 	    ~WM8350_BCLK_MSTR;
-	u16 dac_lrc = wm8350_codec_read(codec, WM8350_DAC_LR_RATE) &
+	u16 dac_lrc = snd_soc_read(codec, WM8350_DAC_LR_RATE) &
 	    ~WM8350_DACLRC_ENA;
-	u16 adc_lrc = wm8350_codec_read(codec, WM8350_ADC_LR_RATE) &
+	u16 adc_lrc = snd_soc_read(codec, WM8350_ADC_LR_RATE) &
 	    ~WM8350_ADCLRC_ENA;
 
 	/* set master/slave audio interface */
@@ -922,42 +916,10 @@
 		return -EINVAL;
 	}
 
-	wm8350_codec_write(codec, WM8350_AI_FORMATING, iface);
-	wm8350_codec_write(codec, WM8350_AI_DAC_CONTROL, master);
-	wm8350_codec_write(codec, WM8350_DAC_LR_RATE, dac_lrc);
-	wm8350_codec_write(codec, WM8350_ADC_LR_RATE, adc_lrc);
-	return 0;
-}
-
-static int wm8350_pcm_trigger(struct snd_pcm_substream *substream,
-			      int cmd, struct snd_soc_dai *codec_dai)
-{
-	struct snd_soc_codec *codec = codec_dai->codec;
-	int master = wm8350_codec_cache_read(codec, WM8350_AI_DAC_CONTROL) &
-	    WM8350_BCLK_MSTR;
-	int enabled = 0;
-
-	/* Check that the DACs or ADCs are enabled since they are
-	 * required for LRC in master mode. The DACs or ADCs need a
-	 * valid audio path i.e. pin -> ADC or DAC -> pin before
-	 * the LRC will be enabled in master mode. */
-	if (!master || cmd != SNDRV_PCM_TRIGGER_START)
-		return 0;
-
-	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
-		enabled = wm8350_codec_cache_read(codec, WM8350_POWER_MGMT_4) &
-		    (WM8350_ADCR_ENA | WM8350_ADCL_ENA);
-	} else {
-		enabled = wm8350_codec_cache_read(codec, WM8350_POWER_MGMT_4) &
-		    (WM8350_DACR_ENA | WM8350_DACL_ENA);
-	}
-
-	if (!enabled) {
-		dev_err(codec->dev,
-		       "%s: invalid audio path - no clocks available\n",
-		       __func__);
-		return -EINVAL;
-	}
+	snd_soc_write(codec, WM8350_AI_FORMATING, iface);
+	snd_soc_write(codec, WM8350_AI_DAC_CONTROL, master);
+	snd_soc_write(codec, WM8350_DAC_LR_RATE, dac_lrc);
+	snd_soc_write(codec, WM8350_ADC_LR_RATE, adc_lrc);
 	return 0;
 }
 
@@ -966,8 +928,9 @@
 				struct snd_soc_dai *codec_dai)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
-	struct wm8350 *wm8350 = codec->control_data;
-	u16 iface = wm8350_codec_read(codec, WM8350_AI_FORMATING) &
+	struct wm8350_data *wm8350_data = snd_soc_codec_get_drvdata(codec);
+	struct wm8350 *wm8350 = wm8350_data->wm8350;
+	u16 iface = snd_soc_read(codec, WM8350_AI_FORMATING) &
 	    ~WM8350_AIF_WL_MASK;
 
 	/* bit size */
@@ -985,7 +948,7 @@
 		break;
 	}
 
-	wm8350_codec_write(codec, WM8350_AI_FORMATING, iface);
+	snd_soc_write(codec, WM8350_AI_FORMATING, iface);
 
 	/* The sloping stopband filter is recommended for use with
 	 * lower sample rates to improve performance.
@@ -1005,12 +968,15 @@
 static int wm8350_mute(struct snd_soc_dai *dai, int mute)
 {
 	struct snd_soc_codec *codec = dai->codec;
-	struct wm8350 *wm8350 = codec->control_data;
+	unsigned int val;
 
 	if (mute)
-		wm8350_set_bits(wm8350, WM8350_DAC_MUTE, WM8350_DAC_MUTE_ENA);
+		val = WM8350_DAC_MUTE_ENA;
 	else
-		wm8350_clear_bits(wm8350, WM8350_DAC_MUTE, WM8350_DAC_MUTE_ENA);
+		val = 0;
+
+	snd_soc_update_bits(codec, WM8350_DAC_MUTE, WM8350_DAC_MUTE_ENA, val);
+
 	return 0;
 }
 
@@ -1079,8 +1045,8 @@
 			  unsigned int freq_out)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
-	struct wm8350 *wm8350 = codec->control_data;
 	struct wm8350_data *priv = snd_soc_codec_get_drvdata(codec);
+	struct wm8350 *wm8350 = priv->wm8350;
 	struct _fll_div fll_div;
 	int ret = 0;
 	u16 fll_1, fll_4;
@@ -1104,17 +1070,17 @@
 		fll_div.ratio);
 
 	/* set up N.K & dividers */
-	fll_1 = wm8350_codec_read(codec, WM8350_FLL_CONTROL_1) &
+	fll_1 = snd_soc_read(codec, WM8350_FLL_CONTROL_1) &
 	    ~(WM8350_FLL_OUTDIV_MASK | WM8350_FLL_RSP_RATE_MASK | 0xc000);
-	wm8350_codec_write(codec, WM8350_FLL_CONTROL_1,
+	snd_soc_write(codec, WM8350_FLL_CONTROL_1,
 			   fll_1 | (fll_div.div << 8) | 0x50);
-	wm8350_codec_write(codec, WM8350_FLL_CONTROL_2,
+	snd_soc_write(codec, WM8350_FLL_CONTROL_2,
 			   (fll_div.ratio << 11) | (fll_div.
 						    n & WM8350_FLL_N_MASK));
-	wm8350_codec_write(codec, WM8350_FLL_CONTROL_3, fll_div.k);
-	fll_4 = wm8350_codec_read(codec, WM8350_FLL_CONTROL_4) &
+	snd_soc_write(codec, WM8350_FLL_CONTROL_3, fll_div.k);
+	fll_4 = snd_soc_read(codec, WM8350_FLL_CONTROL_4) &
 	    ~(WM8350_FLL_FRAC | WM8350_FLL_SLOW_LOCK_REF);
-	wm8350_codec_write(codec, WM8350_FLL_CONTROL_4,
+	snd_soc_write(codec, WM8350_FLL_CONTROL_4,
 			   fll_4 | (fll_div.k ? WM8350_FLL_FRAC : 0) |
 			   (fll_div.ratio == 8 ? WM8350_FLL_SLOW_LOCK_REF : 0));
 
@@ -1131,8 +1097,8 @@
 static int wm8350_set_bias_level(struct snd_soc_codec *codec,
 				 enum snd_soc_bias_level level)
 {
-	struct wm8350 *wm8350 = codec->control_data;
 	struct wm8350_data *priv = snd_soc_codec_get_drvdata(codec);
+	struct wm8350 *wm8350 = priv->wm8350;
 	struct wm8350_audio_platform_data *platform =
 		wm8350->codec.platform_data;
 	u16 pm1;
@@ -1339,35 +1305,36 @@
 	wm8350_hp_work(priv, &priv->hpr, WM8350_JACK_R_LVL);
 }
 
-static irqreturn_t wm8350_hp_jack_handler(int irq, void *data)
+static irqreturn_t wm8350_hpl_jack_handler(int irq, void *data)
 {
 	struct wm8350_data *priv = data;
 	struct wm8350 *wm8350 = priv->wm8350;
-	struct wm8350_jack_data *jack = NULL;
 
-	switch (irq - wm8350->irq_base) {
-	case WM8350_IRQ_CODEC_JCK_DET_L:
 #ifndef CONFIG_SND_SOC_WM8350_MODULE
-		trace_snd_soc_jack_irq("WM8350 HPL");
+	trace_snd_soc_jack_irq("WM8350 HPL");
 #endif
-		jack = &priv->hpl;
-		break;
-
-	case WM8350_IRQ_CODEC_JCK_DET_R:
-#ifndef CONFIG_SND_SOC_WM8350_MODULE
-		trace_snd_soc_jack_irq("WM8350 HPR");
-#endif
-		jack = &priv->hpr;
-		break;
-
-	default:
-		BUG();
-	}
 
 	if (device_may_wakeup(wm8350->dev))
 		pm_wakeup_event(wm8350->dev, 250);
 
-	schedule_delayed_work(&jack->work, 200);
+	schedule_delayed_work(&priv->hpl.work, 200);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t wm8350_hpr_jack_handler(int irq, void *data)
+{
+	struct wm8350_data *priv = data;
+	struct wm8350 *wm8350 = priv->wm8350;
+
+#ifndef CONFIG_SND_SOC_WM8350_MODULE
+	trace_snd_soc_jack_irq("WM8350 HPR");
+#endif
+
+	if (device_may_wakeup(wm8350->dev))
+		pm_wakeup_event(wm8350->dev, 250);
+
+	schedule_delayed_work(&priv->hpr.work, 200);
 
 	return IRQ_HANDLED;
 }
@@ -1387,7 +1354,7 @@
 			  struct snd_soc_jack *jack, int report)
 {
 	struct wm8350_data *priv = snd_soc_codec_get_drvdata(codec);
-	struct wm8350 *wm8350 = codec->control_data;
+	struct wm8350 *wm8350 = priv->wm8350;
 	int irq;
 	int ena;
 
@@ -1418,7 +1385,14 @@
 	}
 
 	/* Sync status */
-	wm8350_hp_jack_handler(irq + wm8350->irq_base, priv);
+	switch (which) {
+	case WM8350_JDL:
+		wm8350_hpl_jack_handler(0, priv);
+		break;
+	case WM8350_JDR:
+		wm8350_hpr_jack_handler(0, priv);
+		break;
+	}
 
 	return 0;
 }
@@ -1463,7 +1437,7 @@
 			   int detect_report, int short_report)
 {
 	struct wm8350_data *priv = snd_soc_codec_get_drvdata(codec);
-	struct wm8350 *wm8350 = codec->control_data;
+	struct wm8350 *wm8350 = priv->wm8350;
 
 	priv->mic.jack = jack;
 	priv->mic.report = detect_report;
@@ -1491,7 +1465,6 @@
 static const struct snd_soc_dai_ops wm8350_dai_ops = {
 	 .hw_params	= wm8350_pcm_hw_params,
 	 .digital_mute	= wm8350_mute,
-	 .trigger	= wm8350_pcm_trigger,
 	 .set_fmt	= wm8350_set_dai_fmt,
 	 .set_sysclk	= wm8350_set_dai_sysclk,
 	 .set_pll	= wm8350_set_fll,
@@ -1559,9 +1532,9 @@
 	wm8350_set_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA);
 
 	/* Enable robust clocking mode in ADC */
-	wm8350_codec_write(codec, WM8350_SECURITY, 0xa7);
-	wm8350_codec_write(codec, 0xde, 0x13);
-	wm8350_codec_write(codec, WM8350_SECURITY, 0);
+	snd_soc_write(codec, WM8350_SECURITY, 0xa7);
+	snd_soc_write(codec, 0xde, 0x13);
+	snd_soc_write(codec, WM8350_SECURITY, 0);
 
 	/* read OUT1 & OUT2 volumes */
 	out1 = &priv->out1;
@@ -1601,10 +1574,10 @@
 			  WM8350_JDL_ENA | WM8350_JDR_ENA);
 
 	wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L,
-			    wm8350_hp_jack_handler, 0, "Left jack detect",
+			    wm8350_hpl_jack_handler, 0, "Left jack detect",
 			    priv);
 	wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R,
-			    wm8350_hp_jack_handler, 0, "Right jack detect",
+			    wm8350_hpr_jack_handler, 0, "Right jack detect",
 			    priv);
 	wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_MICSCD,
 			    wm8350_mic_handler, 0, "Microphone short", priv);
diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c
index 898979d..5dc31eb 100644
--- a/sound/soc/codecs/wm8400.c
+++ b/sound/soc/codecs/wm8400.c
@@ -138,8 +138,8 @@
                 return ret;
 
         /* now hit the volume update bits (always bit 8) */
-        val = wm8400_read(codec, reg);
-        return wm8400_write(codec, reg, val | 0x0100);
+        val = snd_soc_read(codec, reg);
+        return snd_soc_write(codec, reg, val | 0x0100);
 }
 
 #define WM8400_OUTPGA_SINGLE_R_TLV(xname, reg, shift, max, invert, tlv_array) \
@@ -362,8 +362,8 @@
 {
 	u16 reg, fakepower;
 
-	reg = wm8400_read(w->codec, WM8400_POWER_MANAGEMENT_2);
-	fakepower = wm8400_read(w->codec, WM8400_INTDRIVBITS);
+	reg = snd_soc_read(w->codec, WM8400_POWER_MANAGEMENT_2);
+	fakepower = snd_soc_read(w->codec, WM8400_INTDRIVBITS);
 
 	if (fakepower & ((1 << WM8400_INMIXL_PWR) |
 		(1 << WM8400_AINLMUX_PWR))) {
@@ -378,7 +378,7 @@
 	} else {
 		reg &= ~WM8400_AINR_ENA;
 	}
-	wm8400_write(w->codec, WM8400_POWER_MANAGEMENT_2, reg);
+	snd_soc_write(w->codec, WM8400_POWER_MANAGEMENT_2, reg);
 
 	return 0;
 }
@@ -394,7 +394,7 @@
 
 	switch (reg_shift) {
 	case WM8400_SPEAKER_MIXER | (WM8400_LDSPK << 8) :
-		reg = wm8400_read(w->codec, WM8400_OUTPUT_MIXER1);
+		reg = snd_soc_read(w->codec, WM8400_OUTPUT_MIXER1);
 		if (reg & WM8400_LDLO) {
 			printk(KERN_WARNING
 			"Cannot set as Output Mixer 1 LDLO Set\n");
@@ -402,7 +402,7 @@
 		}
 		break;
 	case WM8400_SPEAKER_MIXER | (WM8400_RDSPK << 8):
-		reg = wm8400_read(w->codec, WM8400_OUTPUT_MIXER2);
+		reg = snd_soc_read(w->codec, WM8400_OUTPUT_MIXER2);
 		if (reg & WM8400_RDRO) {
 			printk(KERN_WARNING
 			"Cannot set as Output Mixer 2 RDRO Set\n");
@@ -410,7 +410,7 @@
 		}
 		break;
 	case WM8400_OUTPUT_MIXER1 | (WM8400_LDLO << 8):
-		reg = wm8400_read(w->codec, WM8400_SPEAKER_MIXER);
+		reg = snd_soc_read(w->codec, WM8400_SPEAKER_MIXER);
 		if (reg & WM8400_LDSPK) {
 			printk(KERN_WARNING
 			"Cannot set as Speaker Mixer LDSPK Set\n");
@@ -418,7 +418,7 @@
 		}
 		break;
 	case WM8400_OUTPUT_MIXER2 | (WM8400_RDRO << 8):
-		reg = wm8400_read(w->codec, WM8400_SPEAKER_MIXER);
+		reg = snd_soc_read(w->codec, WM8400_SPEAKER_MIXER);
 		if (reg & WM8400_RDSPK) {
 			printk(KERN_WARNING
 			"Cannot set as Speaker Mixer RDSPK Set\n");
@@ -1021,13 +1021,13 @@
 	wm8400->fll_in = freq_in;
 
 	/* We *must* disable the FLL before any changes */
-	reg = wm8400_read(codec, WM8400_POWER_MANAGEMENT_2);
+	reg = snd_soc_read(codec, WM8400_POWER_MANAGEMENT_2);
 	reg &= ~WM8400_FLL_ENA;
-	wm8400_write(codec, WM8400_POWER_MANAGEMENT_2, reg);
+	snd_soc_write(codec, WM8400_POWER_MANAGEMENT_2, reg);
 
-	reg = wm8400_read(codec, WM8400_FLL_CONTROL_1);
+	reg = snd_soc_read(codec, WM8400_FLL_CONTROL_1);
 	reg &= ~WM8400_FLL_OSC_ENA;
-	wm8400_write(codec, WM8400_FLL_CONTROL_1, reg);
+	snd_soc_write(codec, WM8400_FLL_CONTROL_1, reg);
 
 	if (!freq_out)
 		return 0;
@@ -1035,15 +1035,15 @@
 	reg &= ~(WM8400_FLL_REF_FREQ | WM8400_FLL_FRATIO_MASK);
 	reg |= WM8400_FLL_FRAC | factors.fratio;
 	reg |= factors.freq_ref << WM8400_FLL_REF_FREQ_SHIFT;
-	wm8400_write(codec, WM8400_FLL_CONTROL_1, reg);
+	snd_soc_write(codec, WM8400_FLL_CONTROL_1, reg);
 
-	wm8400_write(codec, WM8400_FLL_CONTROL_2, factors.k);
-	wm8400_write(codec, WM8400_FLL_CONTROL_3, factors.n);
+	snd_soc_write(codec, WM8400_FLL_CONTROL_2, factors.k);
+	snd_soc_write(codec, WM8400_FLL_CONTROL_3, factors.n);
 
-	reg = wm8400_read(codec, WM8400_FLL_CONTROL_4);
+	reg = snd_soc_read(codec, WM8400_FLL_CONTROL_4);
 	reg &= ~WM8400_FLL_OUTDIV_MASK;
 	reg |= factors.outdiv;
-	wm8400_write(codec, WM8400_FLL_CONTROL_4, reg);
+	snd_soc_write(codec, WM8400_FLL_CONTROL_4, reg);
 
 	return 0;
 }
@@ -1057,8 +1057,8 @@
 	struct snd_soc_codec *codec = codec_dai->codec;
 	u16 audio1, audio3;
 
-	audio1 = wm8400_read(codec, WM8400_AUDIO_INTERFACE_1);
-	audio3 = wm8400_read(codec, WM8400_AUDIO_INTERFACE_3);
+	audio1 = snd_soc_read(codec, WM8400_AUDIO_INTERFACE_1);
+	audio3 = snd_soc_read(codec, WM8400_AUDIO_INTERFACE_3);
 
 	/* set master/slave audio interface */
 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
@@ -1099,8 +1099,8 @@
 		return -EINVAL;
 	}
 
-	wm8400_write(codec, WM8400_AUDIO_INTERFACE_1, audio1);
-	wm8400_write(codec, WM8400_AUDIO_INTERFACE_3, audio3);
+	snd_soc_write(codec, WM8400_AUDIO_INTERFACE_1, audio1);
+	snd_soc_write(codec, WM8400_AUDIO_INTERFACE_3, audio3);
 	return 0;
 }
 
@@ -1112,24 +1112,24 @@
 
 	switch (div_id) {
 	case WM8400_MCLK_DIV:
-		reg = wm8400_read(codec, WM8400_CLOCKING_2) &
+		reg = snd_soc_read(codec, WM8400_CLOCKING_2) &
 			~WM8400_MCLK_DIV_MASK;
-		wm8400_write(codec, WM8400_CLOCKING_2, reg | div);
+		snd_soc_write(codec, WM8400_CLOCKING_2, reg | div);
 		break;
 	case WM8400_DACCLK_DIV:
-		reg = wm8400_read(codec, WM8400_CLOCKING_2) &
+		reg = snd_soc_read(codec, WM8400_CLOCKING_2) &
 			~WM8400_DAC_CLKDIV_MASK;
-		wm8400_write(codec, WM8400_CLOCKING_2, reg | div);
+		snd_soc_write(codec, WM8400_CLOCKING_2, reg | div);
 		break;
 	case WM8400_ADCCLK_DIV:
-		reg = wm8400_read(codec, WM8400_CLOCKING_2) &
+		reg = snd_soc_read(codec, WM8400_CLOCKING_2) &
 			~WM8400_ADC_CLKDIV_MASK;
-		wm8400_write(codec, WM8400_CLOCKING_2, reg | div);
+		snd_soc_write(codec, WM8400_CLOCKING_2, reg | div);
 		break;
 	case WM8400_BCLK_DIV:
-		reg = wm8400_read(codec, WM8400_CLOCKING_1) &
+		reg = snd_soc_read(codec, WM8400_CLOCKING_1) &
 			~WM8400_BCLK_DIV_MASK;
-		wm8400_write(codec, WM8400_CLOCKING_1, reg | div);
+		snd_soc_write(codec, WM8400_CLOCKING_1, reg | div);
 		break;
 	default:
 		return -EINVAL;
@@ -1145,9 +1145,8 @@
 	struct snd_pcm_hw_params *params,
 	struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
-	u16 audio1 = wm8400_read(codec, WM8400_AUDIO_INTERFACE_1);
+	struct snd_soc_codec *codec = dai->codec;
+	u16 audio1 = snd_soc_read(codec, WM8400_AUDIO_INTERFACE_1);
 
 	audio1 &= ~WM8400_AIF_WL_MASK;
 	/* bit size */
@@ -1165,19 +1164,19 @@
 		break;
 	}
 
-	wm8400_write(codec, WM8400_AUDIO_INTERFACE_1, audio1);
+	snd_soc_write(codec, WM8400_AUDIO_INTERFACE_1, audio1);
 	return 0;
 }
 
 static int wm8400_mute(struct snd_soc_dai *dai, int mute)
 {
 	struct snd_soc_codec *codec = dai->codec;
-	u16 val = wm8400_read(codec, WM8400_DAC_CTRL) & ~WM8400_DAC_MUTE;
+	u16 val = snd_soc_read(codec, WM8400_DAC_CTRL) & ~WM8400_DAC_MUTE;
 
 	if (mute)
-		wm8400_write(codec, WM8400_DAC_CTRL, val | WM8400_DAC_MUTE);
+		snd_soc_write(codec, WM8400_DAC_CTRL, val | WM8400_DAC_MUTE);
 	else
-		wm8400_write(codec, WM8400_DAC_CTRL, val);
+		snd_soc_write(codec, WM8400_DAC_CTRL, val);
 
 	return 0;
 }
@@ -1196,9 +1195,9 @@
 
 	case SND_SOC_BIAS_PREPARE:
 		/* VMID=2*50k */
-		val = wm8400_read(codec, WM8400_POWER_MANAGEMENT_1) &
+		val = snd_soc_read(codec, WM8400_POWER_MANAGEMENT_1) &
 			~WM8400_VMID_MODE_MASK;
-		wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, val | 0x2);
+		snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1, val | 0x2);
 		break;
 
 	case SND_SOC_BIAS_STANDBY:
@@ -1212,74 +1211,74 @@
 				return ret;
 			}
 
-			wm8400_write(codec, WM8400_POWER_MANAGEMENT_1,
+			snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1,
 				     WM8400_CODEC_ENA | WM8400_SYSCLK_ENA);
 
 			/* Enable POBCTRL, SOFT_ST, VMIDTOG and BUFDCOPEN */
-			wm8400_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST |
+			snd_soc_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST |
 				     WM8400_BUFDCOPEN | WM8400_POBCTRL);
 
 			msleep(50);
 
 			/* Enable VREF & VMID at 2x50k */
-			val = wm8400_read(codec, WM8400_POWER_MANAGEMENT_1);
+			val = snd_soc_read(codec, WM8400_POWER_MANAGEMENT_1);
 			val |= 0x2 | WM8400_VREF_ENA;
-			wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, val);
+			snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1, val);
 
 			/* Enable BUFIOEN */
-			wm8400_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST |
+			snd_soc_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST |
 				     WM8400_BUFDCOPEN | WM8400_POBCTRL |
 				     WM8400_BUFIOEN);
 
 			/* disable POBCTRL, SOFT_ST and BUFDCOPEN */
-			wm8400_write(codec, WM8400_ANTIPOP2, WM8400_BUFIOEN);
+			snd_soc_write(codec, WM8400_ANTIPOP2, WM8400_BUFIOEN);
 		}
 
 		/* VMID=2*300k */
-		val = wm8400_read(codec, WM8400_POWER_MANAGEMENT_1) &
+		val = snd_soc_read(codec, WM8400_POWER_MANAGEMENT_1) &
 			~WM8400_VMID_MODE_MASK;
-		wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, val | 0x4);
+		snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1, val | 0x4);
 		break;
 
 	case SND_SOC_BIAS_OFF:
 		/* Enable POBCTRL and SOFT_ST */
-		wm8400_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST |
+		snd_soc_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST |
 			WM8400_POBCTRL | WM8400_BUFIOEN);
 
 		/* Enable POBCTRL, SOFT_ST and BUFDCOPEN */
-		wm8400_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST |
+		snd_soc_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST |
 			WM8400_BUFDCOPEN | WM8400_POBCTRL |
 			WM8400_BUFIOEN);
 
 		/* mute DAC */
-		val = wm8400_read(codec, WM8400_DAC_CTRL);
-		wm8400_write(codec, WM8400_DAC_CTRL, val | WM8400_DAC_MUTE);
+		val = snd_soc_read(codec, WM8400_DAC_CTRL);
+		snd_soc_write(codec, WM8400_DAC_CTRL, val | WM8400_DAC_MUTE);
 
 		/* Enable any disabled outputs */
-		val = wm8400_read(codec, WM8400_POWER_MANAGEMENT_1);
+		val = snd_soc_read(codec, WM8400_POWER_MANAGEMENT_1);
 		val |= WM8400_SPK_ENA | WM8400_OUT3_ENA |
 			WM8400_OUT4_ENA | WM8400_LOUT_ENA |
 			WM8400_ROUT_ENA;
-		wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, val);
+		snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1, val);
 
 		/* Disable VMID */
 		val &= ~WM8400_VMID_MODE_MASK;
-		wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, val);
+		snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1, val);
 
 		msleep(300);
 
 		/* Enable all output discharge bits */
-		wm8400_write(codec, WM8400_ANTIPOP1, WM8400_DIS_LLINE |
+		snd_soc_write(codec, WM8400_ANTIPOP1, WM8400_DIS_LLINE |
 			WM8400_DIS_RLINE | WM8400_DIS_OUT3 |
 			WM8400_DIS_OUT4 | WM8400_DIS_LOUT |
 			WM8400_DIS_ROUT);
 
 		/* Disable VREF */
 		val &= ~WM8400_VREF_ENA;
-		wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, val);
+		snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1, val);
 
 		/* disable POBCTRL, SOFT_ST and BUFDCOPEN */
-		wm8400_write(codec, WM8400_ANTIPOP2, 0x0);
+		snd_soc_write(codec, WM8400_ANTIPOP2, 0x0);
 
 		ret = regulator_bulk_disable(ARRAY_SIZE(power),
 					     &power[0]);
@@ -1385,19 +1384,19 @@
 
 	wm8400_codec_reset(codec);
 
-	reg = wm8400_read(codec, WM8400_POWER_MANAGEMENT_1);
-	wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, reg | WM8400_CODEC_ENA);
+	reg = snd_soc_read(codec, WM8400_POWER_MANAGEMENT_1);
+	snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1, reg | WM8400_CODEC_ENA);
 
 	/* Latch volume update bits */
-	reg = wm8400_read(codec, WM8400_LEFT_LINE_INPUT_1_2_VOLUME);
-	wm8400_write(codec, WM8400_LEFT_LINE_INPUT_1_2_VOLUME,
+	reg = snd_soc_read(codec, WM8400_LEFT_LINE_INPUT_1_2_VOLUME);
+	snd_soc_write(codec, WM8400_LEFT_LINE_INPUT_1_2_VOLUME,
 		     reg & WM8400_IPVU);
-	reg = wm8400_read(codec, WM8400_RIGHT_LINE_INPUT_1_2_VOLUME);
-	wm8400_write(codec, WM8400_RIGHT_LINE_INPUT_1_2_VOLUME,
+	reg = snd_soc_read(codec, WM8400_RIGHT_LINE_INPUT_1_2_VOLUME);
+	snd_soc_write(codec, WM8400_RIGHT_LINE_INPUT_1_2_VOLUME,
 		     reg & WM8400_IPVU);
 
-	wm8400_write(codec, WM8400_LEFT_OUTPUT_VOLUME, 0x50 | (1<<8));
-	wm8400_write(codec, WM8400_RIGHT_OUTPUT_VOLUME, 0x50 | (1<<8));
+	snd_soc_write(codec, WM8400_LEFT_OUTPUT_VOLUME, 0x50 | (1<<8));
+	snd_soc_write(codec, WM8400_RIGHT_OUTPUT_VOLUME, 0x50 | (1<<8));
 
 	if (!schedule_work(&priv->work)) {
 		ret = -EINVAL;
@@ -1414,8 +1413,8 @@
 {
 	u16 reg;
 
-	reg = wm8400_read(codec, WM8400_POWER_MANAGEMENT_1);
-	wm8400_write(codec, WM8400_POWER_MANAGEMENT_1,
+	reg = snd_soc_read(codec, WM8400_POWER_MANAGEMENT_1);
+	snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1,
 		     reg & (~WM8400_CODEC_ENA));
 
 	regulator_bulk_free(ARRAY_SIZE(power), power);
@@ -1428,7 +1427,7 @@
 	.remove =	wm8400_codec_remove,
 	.suspend =	wm8400_suspend,
 	.resume =	wm8400_resume,
-	.read = wm8400_read,
+	.read = snd_soc_read,
 	.write = wm8400_write,
 	.set_bias_level = wm8400_set_bias_level,
 
diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c
index 9166126..56a0495 100644
--- a/sound/soc/codecs/wm8510.c
+++ b/sound/soc/codecs/wm8510.c
@@ -392,8 +392,7 @@
 				struct snd_pcm_hw_params *params,
 				struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	u16 iface = snd_soc_read(codec, WM8510_IFACE) & 0x19f;
 	u16 adn = snd_soc_read(codec, WM8510_ADD) & 0x1f1;
 
diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c
index 7fea2c3..1c3ffb2 100644
--- a/sound/soc/codecs/wm8523.c
+++ b/sound/soc/codecs/wm8523.c
@@ -145,8 +145,7 @@
 			    struct snd_pcm_hw_params *params,
 			    struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	struct wm8523_priv *wm8523 = snd_soc_codec_get_drvdata(codec);
 	int i;
 	u16 aifctrl1 = snd_soc_read(codec, WM8523_AIF_CTRL1);
diff --git a/sound/soc/codecs/wm8728.c b/sound/soc/codecs/wm8728.c
index fc3d59e..1467f97 100644
--- a/sound/soc/codecs/wm8728.c
+++ b/sound/soc/codecs/wm8728.c
@@ -88,8 +88,7 @@
 	struct snd_pcm_hw_params *params,
 	struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	u16 dac = snd_soc_read(codec, WM8728_DACCTL);
 
 	dac &= ~0x18;
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
index a32caa7..9d1b9b02 100644
--- a/sound/soc/codecs/wm8731.c
+++ b/sound/soc/codecs/wm8731.c
@@ -635,16 +635,17 @@
 	struct wm8731_priv *wm8731;
 	int ret;
 
-	wm8731 = kzalloc(sizeof(struct wm8731_priv), GFP_KERNEL);
+	wm8731 = devm_kzalloc(&spi->dev, sizeof(struct wm8731_priv),
+			      GFP_KERNEL);
 	if (wm8731 == NULL)
 		return -ENOMEM;
 
-	wm8731->regmap = regmap_init_spi(spi, &wm8731_regmap);
+	wm8731->regmap = devm_regmap_init_spi(spi, &wm8731_regmap);
 	if (IS_ERR(wm8731->regmap)) {
 		ret = PTR_ERR(wm8731->regmap);
 		dev_err(&spi->dev, "Failed to allocate register map: %d\n",
 			ret);
-		goto err;
+		return ret;
 	}
 
 	spi_set_drvdata(spi, wm8731);
@@ -653,25 +654,15 @@
 			&soc_codec_dev_wm8731, &wm8731_dai, 1);
 	if (ret != 0) {
 		dev_err(&spi->dev, "Failed to register CODEC: %d\n", ret);
-		goto err_regmap;
+		return ret;
 	}
 
 	return 0;
-
-err_regmap:
-	regmap_exit(wm8731->regmap);
-err:
-	kfree(wm8731);
-	return ret;
 }
 
 static int __devexit wm8731_spi_remove(struct spi_device *spi)
 {
-	struct wm8731_priv *wm8731 = spi_get_drvdata(spi);
-
 	snd_soc_unregister_codec(&spi->dev);
-	regmap_exit(wm8731->regmap);
-	kfree(wm8731);
 	return 0;
 }
 
@@ -693,16 +684,17 @@
 	struct wm8731_priv *wm8731;
 	int ret;
 
-	wm8731 = kzalloc(sizeof(struct wm8731_priv), GFP_KERNEL);
+	wm8731 = devm_kzalloc(&i2c->dev, sizeof(struct wm8731_priv),
+			      GFP_KERNEL);
 	if (wm8731 == NULL)
 		return -ENOMEM;
 
-	wm8731->regmap = regmap_init_i2c(i2c, &wm8731_regmap);
+	wm8731->regmap = devm_regmap_init_i2c(i2c, &wm8731_regmap);
 	if (IS_ERR(wm8731->regmap)) {
 		ret = PTR_ERR(wm8731->regmap);
 		dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
 			ret);
-		goto err;
+		return ret;
 	}
 
 	i2c_set_clientdata(i2c, wm8731);
@@ -711,24 +703,15 @@
 			&soc_codec_dev_wm8731, &wm8731_dai, 1);
 	if (ret != 0) {
 		dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret);
-		goto err_regmap;
+		return ret;
 	}
 
 	return 0;
-
-err_regmap:
-	regmap_exit(wm8731->regmap);
-err:
-	kfree(wm8731);
-	return ret;
 }
 
 static __devexit int wm8731_i2c_remove(struct i2c_client *client)
 {
-	struct wm8731_priv *wm8731 = i2c_get_clientdata(client);
 	snd_soc_unregister_codec(&client->dev);
-	regmap_exit(wm8731->regmap);
-	kfree(wm8731);
 	return 0;
 }
 
diff --git a/sound/soc/codecs/wm8737.c b/sound/soc/codecs/wm8737.c
index 4fe9d19..d052012 100644
--- a/sound/soc/codecs/wm8737.c
+++ b/sound/soc/codecs/wm8737.c
@@ -329,8 +329,7 @@
 			    struct snd_pcm_hw_params *params,
 			    struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	struct wm8737_priv *wm8737 = snd_soc_codec_get_drvdata(codec);
 	int i;
 	u16 clocking = 0;
diff --git a/sound/soc/codecs/wm8741.c b/sound/soc/codecs/wm8741.c
index 3941f50..6e849cb 100644
--- a/sound/soc/codecs/wm8741.c
+++ b/sound/soc/codecs/wm8741.c
@@ -203,8 +203,7 @@
 			    struct snd_pcm_hw_params *params,
 			    struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec);
 	u16 iface = snd_soc_read(codec, WM8741_FORMAT_CONTROL) & 0x1FC;
 	int i;
diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c
index e4c50ce..89151ca 100644
--- a/sound/soc/codecs/wm8750.c
+++ b/sound/soc/codecs/wm8750.c
@@ -547,8 +547,7 @@
 				struct snd_pcm_hw_params *params,
 				struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	struct wm8750_priv *wm8750 = snd_soc_codec_get_drvdata(codec);
 	u16 iface = snd_soc_read(codec, WM8750_IFACE) & 0x1f3;
 	u16 srate = snd_soc_read(codec, WM8750_SRATE) & 0x1c0;
diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c
index e27e7b6..a26482c 100644
--- a/sound/soc/codecs/wm8753.c
+++ b/sound/soc/codecs/wm8753.c
@@ -931,8 +931,7 @@
 				struct snd_pcm_hw_params *params,
 				struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
 	u16 voice = snd_soc_read(codec, WM8753_PCM) & 0x01f3;
 	u16 srate = snd_soc_read(codec, WM8753_SRATE1) & 0x017f;
@@ -1161,8 +1160,7 @@
 				struct snd_pcm_hw_params *params,
 				struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
 	u16 srate = snd_soc_read(codec, WM8753_SRATE1) & 0x01c0;
 	u16 hifi = snd_soc_read(codec, WM8753_HIFI) & 0x01f3;
diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c
index f18c554..077c962 100644
--- a/sound/soc/codecs/wm8900.c
+++ b/sound/soc/codecs/wm8900.c
@@ -610,8 +610,7 @@
 	struct snd_pcm_hw_params *params,
 	struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	u16 reg;
 
 	reg = snd_soc_read(codec, WM8900_REG_AUDIO1) & ~0x60;
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c
index c91fb2f..86b8a29 100644
--- a/sound/soc/codecs/wm8903.c
+++ b/sound/soc/codecs/wm8903.c
@@ -1432,8 +1432,7 @@
 			    struct snd_pcm_hw_params *params,
 			    struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec =rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
 	int fs = params_rate(params);
 	int bclk;
diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c
index d2883af..481a3d9 100644
--- a/sound/soc/codecs/wm8940.c
+++ b/sound/soc/codecs/wm8940.c
@@ -371,8 +371,7 @@
 				struct snd_pcm_hw_params *params,
 				struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	u16 iface = snd_soc_read(codec, WM8940_IFACE) & 0xFD9F;
 	u16 addcntrl = snd_soc_read(codec, WM8940_ADDCNTRL) & 0xFFF1;
 	u16 companding =  snd_soc_read(codec,
diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c
index 840d720..8bc659d 100644
--- a/sound/soc/codecs/wm8960.c
+++ b/sound/soc/codecs/wm8960.c
@@ -505,8 +505,7 @@
 			    struct snd_pcm_hw_params *params,
 			    struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
 	u16 iface = snd_soc_read(codec, WM8960_IFACE1) & 0xfff3;
 	int i;
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c
index 15d467f..0cfce99 100644
--- a/sound/soc/codecs/wm8962.c
+++ b/sound/soc/codecs/wm8962.c
@@ -1478,7 +1478,8 @@
 
 static int wm8962_dsp2_write_config(struct snd_soc_codec *codec)
 {
-	return 0;
+	return regcache_sync_region(codec->control_data,
+				    WM8962_HDBASS_AI_1, WM8962_MAX_REGISTER);
 }
 
 static int wm8962_dsp2_set_enable(struct snd_soc_codec *codec, u16 val)
@@ -1755,10 +1756,22 @@
 SOC_DOUBLE_R_TLV("EQ5 Volume", WM8962_EQ3, WM8962_EQ23,
 		 WM8962_EQL_B5_GAIN_SHIFT, 31, 0, eq_tlv),
 
+SOC_SINGLE("3D Switch", WM8962_THREED1, 0, 1, 0),
+SND_SOC_BYTES_MASK("3D Coefficients", WM8962_THREED1, 4, WM8962_THREED_ENA),
+
+SOC_SINGLE("DF1 Switch", WM8962_DF1, 0, 1, 0),
+SND_SOC_BYTES_MASK("DF1 Coefficients", WM8962_DF1, 7, WM8962_DF1_ENA),
+
+SOC_SINGLE("DRC Switch", WM8962_DRC_1, 0, 1, 0),
+SND_SOC_BYTES_MASK("DRC Coefficients", WM8962_DRC_1, 5, WM8962_DRC_ENA),
+
 WM8962_DSP2_ENABLE("VSS Switch", WM8962_VSS_ENA_SHIFT),
+SND_SOC_BYTES("VSS Coefficients", WM8962_VSS_XHD2_1, 148),
 WM8962_DSP2_ENABLE("HPF1 Switch", WM8962_HPF1_ENA_SHIFT),
 WM8962_DSP2_ENABLE("HPF2 Switch", WM8962_HPF2_ENA_SHIFT),
+SND_SOC_BYTES("HPF Coefficients", WM8962_LHPF2, 1),
 WM8962_DSP2_ENABLE("HD Bass Switch", WM8962_HDBASS_ENA_SHIFT),
+SND_SOC_BYTES("HD Bass Coefficients", WM8962_HDBASS_AI_1, 30),
 };
 
 static const struct snd_kcontrol_new wm8962_spk_mono_controls[] = {
@@ -2519,8 +2532,7 @@
 			    struct snd_pcm_hw_params *params,
 			    struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
 	int i;
 	int aif0 = 0;
diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c
index 28fe59e..eef783f 100644
--- a/sound/soc/codecs/wm8971.c
+++ b/sound/soc/codecs/wm8971.c
@@ -478,8 +478,7 @@
 	struct snd_pcm_hw_params *params,
 	struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	struct wm8971_priv *wm8971 = snd_soc_codec_get_drvdata(codec);
 	u16 iface = snd_soc_read(codec, WM8971_IFACE) & 0x1f3;
 	u16 srate = snd_soc_read(codec, WM8971_SRATE) & 0x1c0;
diff --git a/sound/soc/codecs/wm8978.c b/sound/soc/codecs/wm8978.c
index 72d5fdc..a5be3ad 100644
--- a/sound/soc/codecs/wm8978.c
+++ b/sound/soc/codecs/wm8978.c
@@ -723,8 +723,7 @@
 			    struct snd_pcm_hw_params *params,
 			    struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	struct wm8978_priv *wm8978 = snd_soc_codec_get_drvdata(codec);
 	/* Word length mask = 0x60 */
 	u16 iface_ctl = snd_soc_read(codec, WM8978_AUDIO_INTERFACE) & ~0x60;
diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c
index 6cdf6a2..1d4c5cf 100644
--- a/sound/soc/codecs/wm8988.c
+++ b/sound/soc/codecs/wm8988.c
@@ -668,8 +668,7 @@
 				struct snd_pcm_hw_params *params,
 				struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	struct wm8988_priv *wm8988 = snd_soc_codec_get_drvdata(codec);
 	u16 iface = snd_soc_read(codec, WM8988_IFACE) & 0x1f3;
 	u16 srate = snd_soc_read(codec, WM8988_SRATE) & 0x180;
diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c
index 9d24235..db63c97 100644
--- a/sound/soc/codecs/wm8990.c
+++ b/sound/soc/codecs/wm8990.c
@@ -1112,8 +1112,7 @@
 			    struct snd_pcm_hw_params *params,
 			    struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	u16 audio1 = snd_soc_read(codec, WM8990_AUDIO_INTERFACE_1);
 
 	audio1 &= ~WM8990_AIF_WL_MASK;
diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c
index d256a93..36acfcc 100644
--- a/sound/soc/codecs/wm8993.c
+++ b/sound/soc/codecs/wm8993.c
@@ -218,7 +218,6 @@
 	unsigned int sysclk_rate;
 	unsigned int fs;
 	unsigned int bclk;
-	int class_w_users;
 	unsigned int fll_fref;
 	unsigned int fll_fout;
 	int fll_src;
@@ -824,84 +823,6 @@
 	return 0;
 }
 
-/*
- * When used with DAC outputs only the WM8993 charge pump supports
- * operation in class W mode, providing very low power consumption
- * when used with digital sources.  Enable and disable this mode
- * automatically depending on the mixer configuration.
- *
- * Currently the only supported paths are the direct DAC->headphone
- * paths (which provide minimum power consumption anyway).
- */
-static int class_w_put(struct snd_kcontrol *kcontrol,
-		       struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
-	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
-	struct snd_soc_codec *codec = widget->codec;
-	struct wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec);
-	int ret;
-
-	/* Turn it off if we're using the main output mixer */
-	if (ucontrol->value.integer.value[0] == 0) {
-		if (wm8993->class_w_users == 0) {
-			dev_dbg(codec->dev, "Disabling Class W\n");
-			snd_soc_update_bits(codec, WM8993_CLASS_W_0,
-					    WM8993_CP_DYN_FREQ |
-					    WM8993_CP_DYN_V,
-					    0);
-		}
-		wm8993->class_w_users++;
-		wm8993->hubs_data.class_w = true;
-	}
-
-	/* Implement the change */
-	ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
-
-	/* Enable it if we're using the direct DAC path */
-	if (ucontrol->value.integer.value[0] == 1) {
-		if (wm8993->class_w_users == 1) {
-			dev_dbg(codec->dev, "Enabling Class W\n");
-			snd_soc_update_bits(codec, WM8993_CLASS_W_0,
-					    WM8993_CP_DYN_FREQ |
-					    WM8993_CP_DYN_V,
-					    WM8993_CP_DYN_FREQ |
-					    WM8993_CP_DYN_V);
-		}
-		wm8993->class_w_users--;
-		wm8993->hubs_data.class_w = false;
-	}
-
-	dev_dbg(codec->dev, "Indirect DAC use count now %d\n",
-		wm8993->class_w_users);
-
-	return ret;
-}
-
-#define SOC_DAPM_ENUM_W(xname, xenum) \
-{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
-	.info = snd_soc_info_enum_double, \
-	.get = snd_soc_dapm_get_enum_double, \
-	.put = class_w_put, \
-	.private_value = (unsigned long)&xenum }
-
-static const char *hp_mux_text[] = {
-	"Mixer",
-	"DAC",
-};
-
-static const struct soc_enum hpl_enum =
-	SOC_ENUM_SINGLE(WM8993_OUTPUT_MIXER1, 8, 2, hp_mux_text);
-
-static const struct snd_kcontrol_new hpl_mux =
-	SOC_DAPM_ENUM_W("Left Headphone Mux", hpl_enum);
-
-static const struct soc_enum hpr_enum =
-	SOC_ENUM_SINGLE(WM8993_OUTPUT_MIXER2, 8, 2, hp_mux_text);
-
-static const struct snd_kcontrol_new hpr_mux =
-	SOC_DAPM_ENUM_W("Right Headphone Mux", hpr_enum);
-
 static const struct snd_kcontrol_new left_speaker_mixer[] = {
 SOC_DAPM_SINGLE("Input Switch", WM8993_SPEAKER_MIXER, 7, 1, 0),
 SOC_DAPM_SINGLE("IN1LP Switch", WM8993_SPEAKER_MIXER, 5, 1, 0),
@@ -988,8 +909,8 @@
 SND_SOC_DAPM_DAC("DACL", NULL, WM8993_POWER_MANAGEMENT_3, 1, 0),
 SND_SOC_DAPM_DAC("DACR", NULL, WM8993_POWER_MANAGEMENT_3, 0, 0),
 
-SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &hpl_mux),
-SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &hpr_mux),
+SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpl_mux),
+SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpr_mux),
 
 SND_SOC_DAPM_MIXER("SPKL", WM8993_POWER_MANAGEMENT_3, 8, 0,
 		   left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer)),
@@ -1579,9 +1500,6 @@
 		return ret;
 	}
 
-	/* By default we're using the output mixers */
-	wm8993->class_w_users = 2;
-
 	/* Latch volume update bits and default ZC on */
 	snd_soc_update_bits(codec, WM8993_RIGHT_DAC_DIGITAL_VOLUME,
 			    WM8993_DAC_VU, WM8993_DAC_VU);
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index 2de12eb..993639d 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -70,8 +70,8 @@
 static const struct wm8958_micd_rate jackdet_rates[] = {
 	{ 32768,       true,  0, 1 },
 	{ 32768,       false, 0, 1 },
-	{ 44100 * 256, true,  7, 10 },
-	{ 44100 * 256, false, 7, 10 },
+	{ 44100 * 256, true,  10, 10 },
+	{ 44100 * 256, false, 7, 8 },
 };
 
 static void wm8958_micd_set_rate(struct snd_soc_codec *codec)
@@ -82,7 +82,8 @@
 	const struct wm8958_micd_rate *rates;
 	int num_rates;
 
-	if (wm8994->jack_cb != wm8958_default_micdet)
+	if (!(wm8994->pdata && wm8994->pdata->micd_rates) &&
+	    wm8994->jack_cb != wm8958_default_micdet)
 		return;
 
 	idle = !wm8994->jack_mic;
@@ -118,6 +119,10 @@
 	val = rates[best].start << WM8958_MICD_BIAS_STARTTIME_SHIFT
 		| rates[best].rate << WM8958_MICD_RATE_SHIFT;
 
+	dev_dbg(codec->dev, "MICD rate %d,%d for %dHz %s\n",
+		rates[best].start, rates[best].rate, sysclk,
+		idle ? "idle" : "active");
+
 	snd_soc_update_bits(codec, WM8958_MIC_DETECT_1,
 			    WM8958_MICD_BIAS_STARTTIME_MASK |
 			    WM8958_MICD_RATE_MASK, val);
@@ -398,7 +403,7 @@
 		wm8994->dac_rates[iface]);
 
 	/* The EQ will be disabled while reconfiguring it, remember the
-	 * current configuration. 
+	 * current configuration.
 	 */
 	save = snd_soc_read(codec, base);
 	save &= WM8994_AIF1DAC1_EQ_ENA;
@@ -689,6 +694,9 @@
 	if (!wm8994->jackdet || !wm8994->jack_cb)
 		return;
 
+	if (!wm8994->jackdet || !wm8994->jack_cb)
+		return;
+
 	if (wm8994->active_refcount)
 		mode = WM1811_JACKDET_MODE_AUDIO;
 
@@ -784,7 +792,7 @@
 
 		switch (wm8994->vmid_mode) {
 		default:
-			WARN_ON(0 == "Invalid VMID mode");
+			WARN_ON(NULL == "Invalid VMID mode");
 		case WM8994_VMID_NORMAL:
 			/* Startup bias, VMID ramp & buffer */
 			snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
@@ -937,27 +945,12 @@
 	return 0;
 }
 
-static void wm8994_update_class_w(struct snd_soc_codec *codec)
+static bool wm8994_check_class_w_digital(struct snd_soc_codec *codec)
 {
-	struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
-	int enable = 1;
 	int source = 0;  /* GCC flow analysis can't track enable */
 	int reg, reg_r;
 
-	/* Only support direct DAC->headphone paths */
-	reg = snd_soc_read(codec, WM8994_OUTPUT_MIXER_1);
-	if (!(reg & WM8994_DAC1L_TO_HPOUT1L)) {
-		dev_vdbg(codec->dev, "HPL connected to output mixer\n");
-		enable = 0;
-	}
-
-	reg = snd_soc_read(codec, WM8994_OUTPUT_MIXER_2);
-	if (!(reg & WM8994_DAC1R_TO_HPOUT1R)) {
-		dev_vdbg(codec->dev, "HPR connected to output mixer\n");
-		enable = 0;
-	}
-
-	/* We also need the same setting for L/R and only one path */
+	/* We also need the same AIF source for L/R and only one path */
 	reg = snd_soc_read(codec, WM8994_DAC1_LEFT_MIXER_ROUTING);
 	switch (reg) {
 	case WM8994_AIF2DACL_TO_DAC1L:
@@ -974,30 +967,20 @@
 		break;
 	default:
 		dev_vdbg(codec->dev, "DAC mixer setting: %x\n", reg);
-		enable = 0;
-		break;
+		return false;
 	}
 
 	reg_r = snd_soc_read(codec, WM8994_DAC1_RIGHT_MIXER_ROUTING);
 	if (reg_r != reg) {
 		dev_vdbg(codec->dev, "Left and right DAC mixers different\n");
-		enable = 0;
+		return false;
 	}
 
-	if (enable) {
-		dev_dbg(codec->dev, "Class W enabled\n");
-		snd_soc_update_bits(codec, WM8994_CLASS_W_1,
-				    WM8994_CP_DYN_PWR |
-				    WM8994_CP_DYN_SRC_SEL_MASK,
-				    source | WM8994_CP_DYN_PWR);
-		wm8994->hubs.class_w = true;
-		
-	} else {
-		dev_dbg(codec->dev, "Class W disabled\n");
-		snd_soc_update_bits(codec, WM8994_CLASS_W_1,
-				    WM8994_CP_DYN_PWR, 0);
-		wm8994->hubs.class_w = false;
-	}
+	/* Set the source up */
+	snd_soc_update_bits(codec, WM8994_CLASS_W_1,
+			    WM8994_CP_DYN_SRC_SEL_MASK, source);
+
+	return true;
 }
 
 static int aif1clk_ev(struct snd_soc_dapm_widget *w,
@@ -1280,45 +1263,6 @@
 	return 0;
 }
 
-static const char *hp_mux_text[] = {
-	"Mixer",
-	"DAC",
-};
-
-#define WM8994_HP_ENUM(xname, xenum) \
-{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
-	.info = snd_soc_info_enum_double, \
- 	.get = snd_soc_dapm_get_enum_double, \
- 	.put = wm8994_put_hp_enum, \
-  	.private_value = (unsigned long)&xenum }
-
-static int wm8994_put_hp_enum(struct snd_kcontrol *kcontrol,
-			      struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
-	struct snd_soc_dapm_widget *w = wlist->widgets[0];
-	struct snd_soc_codec *codec = w->codec;
-	int ret;
-
-	ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
-
-	wm8994_update_class_w(codec);
-
-	return ret;
-}
-
-static const struct soc_enum hpl_enum =
-	SOC_ENUM_SINGLE(WM8994_OUTPUT_MIXER_1, 8, 2, hp_mux_text);
-
-static const struct snd_kcontrol_new hpl_mux =
-	WM8994_HP_ENUM("Left Headphone Mux", hpl_enum);
-
-static const struct soc_enum hpr_enum =
-	SOC_ENUM_SINGLE(WM8994_OUTPUT_MIXER_2, 8, 2, hp_mux_text);
-
-static const struct snd_kcontrol_new hpr_mux =
-	WM8994_HP_ENUM("Right Headphone Mux", hpr_enum);
-
 static const char *adc_mux_text[] = {
 	"ADC",
 	"DMIC",
@@ -1430,7 +1374,7 @@
 
 	ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol);
 
-	wm8994_update_class_w(codec);
+	wm_hubs_update_class_w(codec);
 
 	return ret;
 }
@@ -1524,7 +1468,7 @@
 	SOC_DAPM_ENUM("AIF3ADC Mux", wm8958_aif3adc_enum);
 
 static const char *mono_pcm_out_text[] = {
-	"None", "AIF2ADCL", "AIF2ADCR", 
+	"None", "AIF2ADCL", "AIF2ADCR",
 };
 
 static const struct soc_enum mono_pcm_out_enum =
@@ -1573,9 +1517,9 @@
 SND_SOC_DAPM_MIXER_E("SPKR", WM8994_POWER_MANAGEMENT_3, 9, 0,
 		     right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer),
 		     late_enable_ev, SND_SOC_DAPM_PRE_PMU),
-SND_SOC_DAPM_MUX_E("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &hpl_mux,
+SND_SOC_DAPM_MUX_E("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpl_mux,
 		   late_enable_ev, SND_SOC_DAPM_PRE_PMU),
-SND_SOC_DAPM_MUX_E("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &hpr_mux,
+SND_SOC_DAPM_MUX_E("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpr_mux,
 		   late_enable_ev, SND_SOC_DAPM_PRE_PMU),
 
 SND_SOC_DAPM_POST("Late Disable PGA", late_disable_ev)
@@ -1591,8 +1535,8 @@
 		   left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer)),
 SND_SOC_DAPM_MIXER("SPKR", WM8994_POWER_MANAGEMENT_3, 9, 0,
 		   right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer)),
-SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &hpl_mux),
-SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &hpr_mux),
+SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpl_mux),
+SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpr_mux),
 };
 
 static const struct snd_soc_dapm_widget wm8994_dac_revd_widgets[] = {
@@ -1732,6 +1676,7 @@
 };
 
 static const struct snd_soc_dapm_widget wm8958_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("AIF3", WM8994_POWER_MANAGEMENT_6, 5, 1, NULL, 0),
 SND_SOC_DAPM_MUX("Mono PCM Out Mux", SND_SOC_NOPM, 0, 0, &mono_pcm_out_mux),
 SND_SOC_DAPM_MUX("AIF2DACL Mux", SND_SOC_NOPM, 0, 0, &aif2dacl_src_mux),
 SND_SOC_DAPM_MUX("AIF2DACR Mux", SND_SOC_NOPM, 0, 0, &aif2dacr_src_mux),
@@ -1972,6 +1917,9 @@
 	{ "AIF2DACR Mux", "AIF2", "AIF2DAC Mux" },
 	{ "AIF2DACR Mux", "AIF3", "AIF3DACDAT" },
 
+	{ "AIF3DACDAT", NULL, "AIF3" },
+	{ "AIF3ADCDAT", NULL, "AIF3" },
+
 	{ "Mono PCM Out Mux", "AIF2ADCL", "AIF2ADCL" },
 	{ "Mono PCM Out Mux", "AIF2ADCR", "AIF2ADCR" },
 
@@ -2068,24 +2016,20 @@
 	struct wm8994 *control = wm8994->wm8994;
 	int reg_offset, ret;
 	struct fll_div fll;
-	u16 reg, aif1, aif2;
+	u16 reg, clk1, aif_reg, aif_src;
 	unsigned long timeout;
 	bool was_enabled;
 
-	aif1 = snd_soc_read(codec, WM8994_AIF1_CLOCKING_1)
-		& WM8994_AIF1CLK_ENA;
-
-	aif2 = snd_soc_read(codec, WM8994_AIF2_CLOCKING_1)
-		& WM8994_AIF2CLK_ENA;
-
 	switch (id) {
 	case WM8994_FLL1:
 		reg_offset = 0;
 		id = 0;
+		aif_src = 0x10;
 		break;
 	case WM8994_FLL2:
 		reg_offset = 0x20;
 		id = 1;
+		aif_src = 0x18;
 		break;
 	default:
 		return -EINVAL;
@@ -2127,16 +2071,33 @@
 	if (ret < 0)
 		return ret;
 
-	/* Gate the AIF clocks while we reclock */
-	snd_soc_update_bits(codec, WM8994_AIF1_CLOCKING_1,
-			    WM8994_AIF1CLK_ENA, 0);
-	snd_soc_update_bits(codec, WM8994_AIF2_CLOCKING_1,
-			    WM8994_AIF2CLK_ENA, 0);
+	/* Make sure that we're not providing SYSCLK right now */
+	clk1 = snd_soc_read(codec, WM8994_CLOCKING_1);
+	if (clk1 & WM8994_SYSCLK_SRC)
+		aif_reg = WM8994_AIF2_CLOCKING_1;
+	else
+		aif_reg = WM8994_AIF1_CLOCKING_1;
+	reg = snd_soc_read(codec, aif_reg);
+
+	if ((reg & WM8994_AIF1CLK_ENA) &&
+	    (reg & WM8994_AIF1CLK_SRC_MASK) == aif_src) {
+		dev_err(codec->dev, "FLL%d is currently providing SYSCLK\n",
+			id + 1);
+		return -EBUSY;
+	}
 
 	/* We always need to disable the FLL while reconfiguring */
 	snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_1 + reg_offset,
 			    WM8994_FLL1_ENA, 0);
 
+	if (wm8994->fll_byp && src == WM8994_FLL_SRC_BCLK &&
+	    freq_in == freq_out && freq_out) {
+		dev_dbg(codec->dev, "Bypassing FLL%d\n", id + 1);
+		snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_5 + reg_offset,
+				    WM8958_FLL1_BYP, WM8958_FLL1_BYP);
+		goto out;
+	}
+
 	reg = (fll.outdiv << WM8994_FLL1_OUTDIV_SHIFT) |
 		(fll.fll_fratio << WM8994_FLL1_FRATIO_SHIFT);
 	snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_2 + reg_offset,
@@ -2151,6 +2112,7 @@
 				    fll.n << WM8994_FLL1_N_SHIFT);
 
 	snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_5 + reg_offset,
+			    WM8958_FLL1_BYP |
 			    WM8994_FLL1_REFCLK_DIV_MASK |
 			    WM8994_FLL1_REFCLK_SRC_MASK,
 			    (fll.clk_ref_div << WM8994_FLL1_REFCLK_DIV_SHIFT) |
@@ -2213,16 +2175,11 @@
 		}
 	}
 
+out:
 	wm8994->fll[id].in = freq_in;
 	wm8994->fll[id].out = freq_out;
 	wm8994->fll[id].src = src;
 
-	/* Enable any gated AIF clocks */
-	snd_soc_update_bits(codec, WM8994_AIF1_CLOCKING_1,
-			    WM8994_AIF1CLK_ENA, aif1);
-	snd_soc_update_bits(codec, WM8994_AIF2_CLOCKING_1,
-			    WM8994_AIF2CLK_ENA, aif2);
-
 	configure_clock(codec);
 
 	return 0;
@@ -2290,7 +2247,7 @@
 
 	case WM8994_SYSCLK_OPCLK:
 		/* Special case - a division (times 10) is given and
-		 * no effect on main clocking. 
+		 * no effect on main clocking.
 		 */
 		if (freq) {
 			for (i = 0; i < ARRAY_SIZE(opclk_divs); i++)
@@ -2792,33 +2749,6 @@
 	return snd_soc_update_bits(codec, aif1_reg, WM8994_AIF1_WL_MASK, aif1);
 }
 
-static void wm8994_aif_shutdown(struct snd_pcm_substream *substream,
-				struct snd_soc_dai *dai)
-{
-	struct snd_soc_codec *codec = dai->codec;
-	int rate_reg = 0;
-
-	switch (dai->id) {
-	case 1:
-		rate_reg = WM8994_AIF1_RATE;
-		break;
-	case 2:
-		rate_reg = WM8994_AIF2_RATE;
-		break;
-	default:
-		break;
-	}
-
-	/* If the DAI is idle then configure the divider tree for the
-	 * lowest output rate to save a little power if the clock is
-	 * still active (eg, because it is system clock).
-	 */
-	if (rate_reg && !dai->playback_active && !dai->capture_active)
-		snd_soc_update_bits(codec, rate_reg,
-				    WM8994_AIF1_SR_MASK |
-				    WM8994_AIF1CLK_RATE_MASK, 0x9);
-}
-
 static int wm8994_aif_mute(struct snd_soc_dai *codec_dai, int mute)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
@@ -2860,10 +2790,6 @@
 		reg = WM8994_AIF2_MASTER_SLAVE;
 		mask = WM8994_AIF2_TRI;
 		break;
-	case 3:
-		reg = WM8994_POWER_MANAGEMENT_6;
-		mask = WM8994_AIF3_TRI;
-		break;
 	default:
 		return -EINVAL;
 	}
@@ -2900,7 +2826,6 @@
 	.set_sysclk	= wm8994_set_dai_sysclk,
 	.set_fmt	= wm8994_set_dai_fmt,
 	.hw_params	= wm8994_hw_params,
-	.shutdown	= wm8994_aif_shutdown,
 	.digital_mute	= wm8994_aif_mute,
 	.set_pll	= wm8994_set_fll,
 	.set_tristate	= wm8994_set_tristate,
@@ -2910,7 +2835,6 @@
 	.set_sysclk	= wm8994_set_dai_sysclk,
 	.set_fmt	= wm8994_set_dai_fmt,
 	.hw_params	= wm8994_hw_params,
-	.shutdown	= wm8994_aif_shutdown,
 	.digital_mute   = wm8994_aif_mute,
 	.set_pll	= wm8994_set_fll,
 	.set_tristate	= wm8994_set_tristate,
@@ -2918,7 +2842,6 @@
 
 static const struct snd_soc_dai_ops wm8994_aif3_dai_ops = {
 	.hw_params	= wm8994_aif3_hw_params,
-	.set_tristate	= wm8994_set_tristate,
 };
 
 static struct snd_soc_dai_driver wm8994_dai[] = {
@@ -3126,14 +3049,14 @@
 
 		/* Expand the array... */
 		t = krealloc(wm8994->retune_mobile_texts,
-			     sizeof(char *) * 
+			     sizeof(char *) *
 			     (wm8994->num_retune_mobile_texts + 1),
 			     GFP_KERNEL);
 		if (t == NULL)
 			continue;
 
 		/* ...store the new entry... */
-		t[wm8994->num_retune_mobile_texts] = 
+		t[wm8994->num_retune_mobile_texts] =
 			pdata->retune_mobile_cfgs[i].name;
 
 		/* ...and remember the new version. */
@@ -3304,25 +3227,25 @@
 }
 EXPORT_SYMBOL_GPL(wm8994_mic_detect);
 
-static irqreturn_t wm8994_mic_irq(int irq, void *data)
+static void wm8994_mic_work(struct work_struct *work)
 {
-	struct wm8994_priv *priv = data;
-	struct snd_soc_codec *codec = priv->codec;
-	int reg;
+	struct wm8994_priv *priv = container_of(work,
+						struct wm8994_priv,
+						mic_work.work);
+	struct regmap *regmap = priv->wm8994->regmap;
+	struct device *dev = priv->wm8994->dev;
+	unsigned int reg;
+	int ret;
 	int report;
 
-#ifndef CONFIG_SND_SOC_WM8994_MODULE
-	trace_snd_soc_jack_irq(dev_name(codec->dev));
-#endif
-
-	reg = snd_soc_read(codec, WM8994_INTERRUPT_RAW_STATUS_2);
-	if (reg < 0) {
-		dev_err(codec->dev, "Failed to read microphone status: %d\n",
-			reg);
-		return IRQ_HANDLED;
+	ret = regmap_read(regmap, WM8994_INTERRUPT_RAW_STATUS_2, &reg);
+	if (ret < 0) {
+		dev_err(dev, "Failed to read microphone status: %d\n",
+			ret);
+		return;
 	}
 
-	dev_dbg(codec->dev, "Microphone status: %x\n", reg);
+	dev_dbg(dev, "Microphone status: %x\n", reg);
 
 	report = 0;
 	if (reg & WM8994_MIC1_DET_STS) {
@@ -3361,6 +3284,20 @@
 
 	snd_soc_jack_report(priv->micdet[1].jack, report,
 			    SND_JACK_HEADSET | SND_JACK_BTN_0);
+}
+
+static irqreturn_t wm8994_mic_irq(int irq, void *data)
+{
+	struct wm8994_priv *priv = data;
+	struct snd_soc_codec *codec = priv->codec;
+
+#ifndef CONFIG_SND_SOC_WM8994_MODULE
+	trace_snd_soc_jack_irq(dev_name(codec->dev));
+#endif
+
+	pm_wakeup_event(codec->dev, 300);
+
+	schedule_delayed_work(&priv->mic_work, msecs_to_jiffies(250));
 
 	return IRQ_HANDLED;
 }
@@ -3415,9 +3352,6 @@
 
 		wm8958_micd_set_rate(codec);
 
-		snd_soc_jack_report(wm8994->micdet[0].jack, SND_JACK_HEADPHONE,
-				    SND_JACK_HEADSET);
-
 		/* If we have jackdet that will detect removal */
 		if (wm8994->jackdet) {
 			mutex_lock(&wm8994->accdet_lock);
@@ -3430,14 +3364,13 @@
 
 			mutex_unlock(&wm8994->accdet_lock);
 
-			if (wm8994->pdata->jd_ext_cap) {
-				mutex_lock(&codec->mutex);
+			if (wm8994->pdata->jd_ext_cap)
 				snd_soc_dapm_disable_pin(&codec->dapm,
 							 "MICBIAS2");
-				snd_soc_dapm_sync(&codec->dapm);
-				mutex_unlock(&codec->mutex);
-			}
 		}
+
+		snd_soc_jack_report(wm8994->micdet[0].jack, SND_JACK_HEADPHONE,
+				    SND_JACK_HEADSET);
 	}
 
 	/* Report short circuit as a button */
@@ -3489,6 +3422,8 @@
 	if (present) {
 		dev_dbg(codec->dev, "Jack detected\n");
 
+		wm8958_micd_set_rate(codec);
+
 		snd_soc_update_bits(codec, WM8958_MICBIAS2,
 				    WM8958_MICB2_DISCH, 0);
 
@@ -3526,16 +3461,11 @@
 
 	/* If required for an external cap force MICBIAS on */
 	if (wm8994->pdata->jd_ext_cap) {
-		mutex_lock(&codec->mutex);
-
 		if (present)
 			snd_soc_dapm_force_enable_pin(&codec->dapm,
 						      "MICBIAS2");
 		else
 			snd_soc_dapm_disable_pin(&codec->dapm, "MICBIAS2");
-
-		snd_soc_dapm_sync(&codec->dapm);
-		mutex_unlock(&codec->mutex);
 	}
 
 	if (present)
@@ -3740,6 +3670,7 @@
 	wm8994->codec = codec;
 
 	mutex_init(&wm8994->accdet_lock);
+	INIT_DELAYED_WORK(&wm8994->mic_work, wm8994_mic_work);
 
 	for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++)
 		init_completion(&wm8994->fll_locked[i]);
@@ -3783,13 +3714,22 @@
 	case WM8958:
 		wm8994->hubs.dcs_readback_mode = 1;
 		wm8994->hubs.hp_startup_mode = 1;
+
+		switch (wm8994->revision) {
+		case 0:
+			break;
+		default:
+			wm8994->fll_byp = true;
+			break;
+		}
 		break;
 
 	case WM1811:
 		wm8994->hubs.dcs_readback_mode = 2;
 		wm8994->hubs.no_series_update = 1;
 		wm8994->hubs.hp_startup_mode = 1;
-		wm8994->hubs.no_cache_class_w = true;
+		wm8994->hubs.no_cache_dac_hp_direct = true;
+		wm8994->fll_byp = true;
 
 		switch (wm8994->revision) {
 		case 0:
@@ -4010,7 +3950,8 @@
 		break;
 	}
 
-	wm8994_update_class_w(codec);
+	wm8994->hubs.check_class_w_digital = wm8994_check_class_w_digital;
+	wm_hubs_update_class_w(codec);
 
 	wm8994_handle_pdata(wm8994);
 
@@ -4075,7 +4016,6 @@
 					  ARRAY_SIZE(wm8994_dac_widgets));
 		break;
 	}
-		
 
 	wm_hubs_add_analogue_routes(codec, 0, 0);
 	snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
@@ -4140,7 +4080,7 @@
 	return ret;
 }
 
-static int  wm8994_codec_remove(struct snd_soc_codec *codec)
+static int wm8994_codec_remove(struct snd_soc_codec *codec)
 {
 	struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 	struct wm8994 *control = wm8994->wm8994;
@@ -4181,14 +4121,10 @@
 			free_irq(wm8994->micdet_irq, wm8994);
 		break;
 	}
-	if (wm8994->mbc)
-		release_firmware(wm8994->mbc);
-	if (wm8994->mbc_vss)
-		release_firmware(wm8994->mbc_vss);
-	if (wm8994->enh_eq)
-		release_firmware(wm8994->enh_eq);
+	release_firmware(wm8994->mbc);
+	release_firmware(wm8994->mbc_vss);
+	release_firmware(wm8994->enh_eq);
 	kfree(wm8994->retune_mobile_texts);
-
 	return 0;
 }
 
diff --git a/sound/soc/codecs/wm8994.h b/sound/soc/codecs/wm8994.h
index c724112..d77e06f 100644
--- a/sound/soc/codecs/wm8994.h
+++ b/sound/soc/codecs/wm8994.h
@@ -12,6 +12,7 @@
 #include <sound/soc.h>
 #include <linux/firmware.h>
 #include <linux/completion.h>
+#include <linux/workqueue.h>
 
 #include "wm_hubs.h"
 
@@ -79,6 +80,7 @@
 	struct wm8994_fll_config fll[2], fll_suspend[2];
 	struct completion fll_locked[2];
 	bool fll_locked_irq;
+	bool fll_byp;
 
 	int vmid_refcount;
 	int active_refcount;
@@ -126,6 +128,7 @@
 
 	struct mutex accdet_lock;
 	struct wm8994_micdet micdet[2];
+	struct delayed_work mic_work;
 	bool mic_detecting;
 	bool jack_mic;
 	int btn_mask;
diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c
index 1fd6354..8af422e 100644
--- a/sound/soc/codecs/wm8996.c
+++ b/sound/soc/codecs/wm8996.c
@@ -1770,7 +1770,13 @@
 
 	switch (level) {
 	case SND_SOC_BIAS_ON:
+		break;
 	case SND_SOC_BIAS_PREPARE:
+		/* Put the MICBIASes into regulating mode */
+		snd_soc_update_bits(codec, WM8996_MICBIAS_1,
+				    WM8996_MICB1_MODE, 0);
+		snd_soc_update_bits(codec, WM8996_MICBIAS_2,
+				    WM8996_MICB2_MODE, 0);
 		break;
 
 	case SND_SOC_BIAS_STANDBY:
@@ -1793,6 +1799,12 @@
 			regcache_cache_only(codec->control_data, false);
 			regcache_sync(codec->control_data);
 		}
+
+		/* Bypass the MICBIASes for lowest power */
+		snd_soc_update_bits(codec, WM8996_MICBIAS_1,
+				    WM8996_MICB1_MODE, WM8996_MICB1_MODE);
+		snd_soc_update_bits(codec, WM8996_MICBIAS_2,
+				    WM8996_MICB2_MODE, WM8996_MICB2_MODE);
 		break;
 
 	case SND_SOC_BIAS_OFF:
diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c
index 076c126..9328270 100644
--- a/sound/soc/codecs/wm9081.c
+++ b/sound/soc/codecs/wm9081.c
@@ -774,7 +774,7 @@
 SND_SOC_DAPM_INPUT("IN1"),
 SND_SOC_DAPM_INPUT("IN2"),
 
-SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM9081_POWER_MANAGEMENT, 0, 0),
+SND_SOC_DAPM_DAC("DAC", NULL, WM9081_POWER_MANAGEMENT, 0, 0),
 
 SND_SOC_DAPM_MIXER_NAMED_CTL("Mixer", SND_SOC_NOPM, 0, 0,
 			     mixer, ARRAY_SIZE(mixer)),
@@ -799,6 +799,7 @@
 static const struct snd_soc_dapm_route wm9081_audio_paths[] = {
 	{ "DAC", NULL, "CLK_SYS" },
 	{ "DAC", NULL, "CLK_DSP" },
+	{ "DAC", NULL, "AIF" },
 
 	{ "Mixer", "IN1 Switch", "IN1" },
 	{ "Mixer", "IN2 Switch", "IN2" },
@@ -1252,7 +1253,7 @@
 static struct snd_soc_dai_driver wm9081_dai = {
 	.name = "wm9081-hifi",
 	.playback = {
-		.stream_name = "HiFi Playback",
+		.stream_name = "AIF",
 		.channels_min = 1,
 		.channels_max = 2,
 		.rates = WM9081_RATES,
diff --git a/sound/soc/codecs/wm9705.c b/sound/soc/codecs/wm9705.c
index cacc6a8..e8e782a 100644
--- a/sound/soc/codecs/wm9705.c
+++ b/sound/soc/codecs/wm9705.c
@@ -236,9 +236,7 @@
 static int ac97_prepare(struct snd_pcm_substream *substream,
 			struct snd_soc_dai *dai)
 {
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	int reg;
 	u16 vra;
 
@@ -250,7 +248,7 @@
 	else
 		reg = AC97_PCM_LR_ADC_RATE;
 
-	return ac97_write(codec, reg, runtime->rate);
+	return ac97_write(codec, reg, substream->runtime->rate);
 }
 
 #define WM9705_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | \
diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c
index b342ae5..a154141 100644
--- a/sound/soc/codecs/wm9712.c
+++ b/sound/soc/codecs/wm9712.c
@@ -467,11 +467,10 @@
 static int ac97_prepare(struct snd_pcm_substream *substream,
 			struct snd_soc_dai *dai)
 {
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec =rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	int reg;
 	u16 vra;
+	struct snd_pcm_runtime *runtime = substream->runtime;
 
 	vra = ac97_read(codec, AC97_EXTENDED_STATUS);
 	ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1);
@@ -487,10 +486,9 @@
 static int ac97_aux_prepare(struct snd_pcm_substream *substream,
 			    struct snd_soc_dai *dai)
 {
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	u16 vra, xsle;
+	struct snd_pcm_runtime *runtime = substream->runtime;
 
 	vra = ac97_read(codec, AC97_EXTENDED_STATUS);
 	ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1);
diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c
index 6c028c4..dfe957a 100644
--- a/sound/soc/codecs/wm_hubs.c
+++ b/sound/soc/codecs/wm_hubs.c
@@ -109,12 +109,103 @@
 }
 EXPORT_SYMBOL_GPL(wm_hubs_dcs_done);
 
+static bool wm_hubs_dac_hp_direct(struct snd_soc_codec *codec)
+{
+	int reg;
+
+	/* If we're going via the mixer we'll need to do additional checks */
+	reg = snd_soc_read(codec, WM8993_OUTPUT_MIXER1);
+	if (!(reg & WM8993_DACL_TO_HPOUT1L)) {
+		if (reg & ~WM8993_DACL_TO_MIXOUTL) {
+			dev_vdbg(codec->dev, "Analogue paths connected: %x\n",
+				 reg & ~WM8993_DACL_TO_HPOUT1L);
+			return false;
+		} else {
+			dev_vdbg(codec->dev, "HPL connected to mixer\n");
+		}
+	} else {
+		dev_vdbg(codec->dev, "HPL connected to DAC\n");
+	}
+
+	reg = snd_soc_read(codec, WM8993_OUTPUT_MIXER2);
+	if (!(reg & WM8993_DACR_TO_HPOUT1R)) {
+		if (reg & ~WM8993_DACR_TO_MIXOUTR) {
+			dev_vdbg(codec->dev, "Analogue paths connected: %x\n",
+				 reg & ~WM8993_DACR_TO_HPOUT1R);
+			return false;
+		} else {
+			dev_vdbg(codec->dev, "HPR connected to mixer\n");
+		}
+	} else {
+		dev_vdbg(codec->dev, "HPR connected to DAC\n");
+	}
+
+	return true;
+}
+
+struct wm_hubs_dcs_cache {
+	struct list_head list;
+	unsigned int left;
+	unsigned int right;
+	u16 dcs_cfg;
+};
+
+static bool wm_hubs_dcs_cache_get(struct snd_soc_codec *codec,
+				  struct wm_hubs_dcs_cache **entry)
+{
+	struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
+	struct wm_hubs_dcs_cache *cache;
+	unsigned int left, right;
+
+	left = snd_soc_read(codec, WM8993_LEFT_OUTPUT_VOLUME);
+	left &= WM8993_HPOUT1L_VOL_MASK;
+
+	right = snd_soc_read(codec, WM8993_RIGHT_OUTPUT_VOLUME);
+	right &= WM8993_HPOUT1R_VOL_MASK;
+
+	list_for_each_entry(cache, &hubs->dcs_cache, list) {
+		if (cache->left != left || cache->right != right)
+			continue;
+
+		*entry = cache;
+		return true;
+	}
+
+	return false;
+}
+
+static void wm_hubs_dcs_cache_set(struct snd_soc_codec *codec, u16 dcs_cfg)
+{
+	struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
+	struct wm_hubs_dcs_cache *cache;
+
+	if (hubs->no_cache_dac_hp_direct)
+		return;
+
+	cache = devm_kzalloc(codec->dev, sizeof(*cache), GFP_KERNEL);
+	if (!cache) {
+		dev_err(codec->dev, "Failed to allocate DCS cache entry\n");
+		return;
+	}
+
+	cache->left = snd_soc_read(codec, WM8993_LEFT_OUTPUT_VOLUME);
+	cache->left &= WM8993_HPOUT1L_VOL_MASK;
+
+	cache->right = snd_soc_read(codec, WM8993_RIGHT_OUTPUT_VOLUME);
+	cache->right &= WM8993_HPOUT1R_VOL_MASK;
+
+	cache->dcs_cfg = dcs_cfg;
+
+	list_add_tail(&cache->list, &hubs->dcs_cache);
+}
+
 /*
  * Startup calibration of the DC servo
  */
 static void calibrate_dc_servo(struct snd_soc_codec *codec)
 {
 	struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
+	struct wm_hubs_dcs_cache *cache;
 	s8 offset;
 	u16 reg, reg_l, reg_r, dcs_cfg, dcs_reg;
 
@@ -129,10 +220,11 @@
 
 	/* If we're using a digital only path and have a previously
 	 * callibrated DC servo offset stored then use that. */
-	if (hubs->class_w && hubs->class_w_dcs) {
-		dev_dbg(codec->dev, "Using cached DC servo offset %x\n",
-			hubs->class_w_dcs);
-		snd_soc_write(codec, dcs_reg, hubs->class_w_dcs);
+	if (wm_hubs_dac_hp_direct(codec) &&
+	    wm_hubs_dcs_cache_get(codec, &cache)) {
+		dev_dbg(codec->dev, "Using cached DCS offset %x for %d,%d\n",
+			cache->dcs_cfg, cache->left, cache->right);
+		snd_soc_write(codec, dcs_reg, cache->dcs_cfg);
 		wait_for_dc_servo(codec,
 				  WM8993_DCS_TRIG_DAC_WR_0 |
 				  WM8993_DCS_TRIG_DAC_WR_1);
@@ -207,8 +299,8 @@
 
 	/* Save the callibrated offset if we're in class W mode and
 	 * therefore don't have any analogue signal mixed in. */
-	if (hubs->class_w && !hubs->no_cache_class_w)
-		hubs->class_w_dcs = dcs_cfg;
+	if (wm_hubs_dac_hp_direct(codec))
+		wm_hubs_dcs_cache_set(codec, dcs_cfg);
 }
 
 /*
@@ -223,9 +315,6 @@
 
 	ret = snd_soc_put_volsw(kcontrol, ucontrol);
 
-	/* Updating the analogue gains invalidates the DC servo cache */
-	hubs->class_w_dcs = 0;
-
 	/* If we're applying an offset correction then updating the
 	 * callibration would be likely to introduce further offsets. */
 	if (hubs->dcs_codes_l || hubs->dcs_codes_r || hubs->no_series_update)
@@ -530,6 +619,86 @@
 	return 0;
 }
 
+void wm_hubs_update_class_w(struct snd_soc_codec *codec)
+{
+	struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
+	int enable = WM8993_CP_DYN_V | WM8993_CP_DYN_FREQ;
+
+	if (!wm_hubs_dac_hp_direct(codec))
+		enable = false;
+
+	if (hubs->check_class_w_digital && !hubs->check_class_w_digital(codec))
+		enable = false;
+
+	dev_vdbg(codec->dev, "Class W %s\n", enable ? "enabled" : "disabled");
+
+	snd_soc_update_bits(codec, WM8993_CLASS_W_0,
+			    WM8993_CP_DYN_V | WM8993_CP_DYN_FREQ, enable);
+}
+EXPORT_SYMBOL_GPL(wm_hubs_update_class_w);
+
+#define WM_HUBS_SINGLE_W(xname, reg, shift, max, invert) \
+{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+	.info = snd_soc_info_volsw, \
+	.get = snd_soc_dapm_get_volsw, .put = class_w_put_volsw, \
+	.private_value =  SOC_SINGLE_VALUE(reg, shift, max, invert) }
+
+static int class_w_put_volsw(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct snd_soc_codec *codec = widget->codec;
+	int ret;
+
+	ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol);
+
+	wm_hubs_update_class_w(codec);
+
+	return ret;
+}
+
+#define WM_HUBS_ENUM_W(xname, xenum) \
+{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+	.info = snd_soc_info_enum_double, \
+	.get = snd_soc_dapm_get_enum_double, \
+	.put = class_w_put_double, \
+	.private_value = (unsigned long)&xenum }
+
+static int class_w_put_double(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct snd_soc_codec *codec = widget->codec;
+	int ret;
+
+	ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+
+	wm_hubs_update_class_w(codec);
+
+	return ret;
+}
+
+static const char *hp_mux_text[] = {
+	"Mixer",
+	"DAC",
+};
+
+static const struct soc_enum hpl_enum =
+	SOC_ENUM_SINGLE(WM8993_OUTPUT_MIXER1, 8, 2, hp_mux_text);
+
+const struct snd_kcontrol_new wm_hubs_hpl_mux =
+	WM_HUBS_ENUM_W("Left Headphone Mux", hpl_enum);
+EXPORT_SYMBOL_GPL(wm_hubs_hpl_mux);
+
+static const struct soc_enum hpr_enum =
+	SOC_ENUM_SINGLE(WM8993_OUTPUT_MIXER2, 8, 2, hp_mux_text);
+
+const struct snd_kcontrol_new wm_hubs_hpr_mux =
+	WM_HUBS_ENUM_W("Right Headphone Mux", hpr_enum);
+EXPORT_SYMBOL_GPL(wm_hubs_hpr_mux);
+
 static const struct snd_kcontrol_new in1l_pga[] = {
 SOC_DAPM_SINGLE("IN1LP Switch", WM8993_INPUT_MIXER2, 5, 1, 0),
 SOC_DAPM_SINGLE("IN1LN Switch", WM8993_INPUT_MIXER2, 4, 1, 0),
@@ -561,25 +730,25 @@
 };
 
 static const struct snd_kcontrol_new left_output_mixer[] = {
-SOC_DAPM_SINGLE("Right Input Switch", WM8993_OUTPUT_MIXER1, 7, 1, 0),
-SOC_DAPM_SINGLE("Left Input Switch", WM8993_OUTPUT_MIXER1, 6, 1, 0),
-SOC_DAPM_SINGLE("IN2RN Switch", WM8993_OUTPUT_MIXER1, 5, 1, 0),
-SOC_DAPM_SINGLE("IN2LN Switch", WM8993_OUTPUT_MIXER1, 4, 1, 0),
-SOC_DAPM_SINGLE("IN2LP Switch", WM8993_OUTPUT_MIXER1, 1, 1, 0),
-SOC_DAPM_SINGLE("IN1R Switch", WM8993_OUTPUT_MIXER1, 3, 1, 0),
-SOC_DAPM_SINGLE("IN1L Switch", WM8993_OUTPUT_MIXER1, 2, 1, 0),
-SOC_DAPM_SINGLE("DAC Switch", WM8993_OUTPUT_MIXER1, 0, 1, 0),
+WM_HUBS_SINGLE_W("Right Input Switch", WM8993_OUTPUT_MIXER1, 7, 1, 0),
+WM_HUBS_SINGLE_W("Left Input Switch", WM8993_OUTPUT_MIXER1, 6, 1, 0),
+WM_HUBS_SINGLE_W("IN2RN Switch", WM8993_OUTPUT_MIXER1, 5, 1, 0),
+WM_HUBS_SINGLE_W("IN2LN Switch", WM8993_OUTPUT_MIXER1, 4, 1, 0),
+WM_HUBS_SINGLE_W("IN2LP Switch", WM8993_OUTPUT_MIXER1, 1, 1, 0),
+WM_HUBS_SINGLE_W("IN1R Switch", WM8993_OUTPUT_MIXER1, 3, 1, 0),
+WM_HUBS_SINGLE_W("IN1L Switch", WM8993_OUTPUT_MIXER1, 2, 1, 0),
+WM_HUBS_SINGLE_W("DAC Switch", WM8993_OUTPUT_MIXER1, 0, 1, 0),
 };
 
 static const struct snd_kcontrol_new right_output_mixer[] = {
-SOC_DAPM_SINGLE("Left Input Switch", WM8993_OUTPUT_MIXER2, 7, 1, 0),
-SOC_DAPM_SINGLE("Right Input Switch", WM8993_OUTPUT_MIXER2, 6, 1, 0),
-SOC_DAPM_SINGLE("IN2LN Switch", WM8993_OUTPUT_MIXER2, 5, 1, 0),
-SOC_DAPM_SINGLE("IN2RN Switch", WM8993_OUTPUT_MIXER2, 4, 1, 0),
-SOC_DAPM_SINGLE("IN1L Switch", WM8993_OUTPUT_MIXER2, 3, 1, 0),
-SOC_DAPM_SINGLE("IN1R Switch", WM8993_OUTPUT_MIXER2, 2, 1, 0),
-SOC_DAPM_SINGLE("IN2RP Switch", WM8993_OUTPUT_MIXER2, 1, 1, 0),
-SOC_DAPM_SINGLE("DAC Switch", WM8993_OUTPUT_MIXER2, 0, 1, 0),
+WM_HUBS_SINGLE_W("Left Input Switch", WM8993_OUTPUT_MIXER2, 7, 1, 0),
+WM_HUBS_SINGLE_W("Right Input Switch", WM8993_OUTPUT_MIXER2, 6, 1, 0),
+WM_HUBS_SINGLE_W("IN2LN Switch", WM8993_OUTPUT_MIXER2, 5, 1, 0),
+WM_HUBS_SINGLE_W("IN2RN Switch", WM8993_OUTPUT_MIXER2, 4, 1, 0),
+WM_HUBS_SINGLE_W("IN1L Switch", WM8993_OUTPUT_MIXER2, 3, 1, 0),
+WM_HUBS_SINGLE_W("IN1R Switch", WM8993_OUTPUT_MIXER2, 2, 1, 0),
+WM_HUBS_SINGLE_W("IN2RP Switch", WM8993_OUTPUT_MIXER2, 1, 1, 0),
+WM_HUBS_SINGLE_W("DAC Switch", WM8993_OUTPUT_MIXER2, 0, 1, 0),
 };
 
 static const struct snd_kcontrol_new earpiece_mixer[] = {
@@ -943,6 +1112,7 @@
 	struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
 	struct snd_soc_dapm_context *dapm = &codec->dapm;
 
+	INIT_LIST_HEAD(&hubs->dcs_cache);
 	init_completion(&hubs->dcs_done);
 
 	snd_soc_dapm_add_routes(dapm, analogue_routes,
diff --git a/sound/soc/codecs/wm_hubs.h b/sound/soc/codecs/wm_hubs.h
index 5705276..da2dc89 100644
--- a/sound/soc/codecs/wm_hubs.h
+++ b/sound/soc/codecs/wm_hubs.h
@@ -16,6 +16,8 @@
 
 #include <linux/completion.h>
 #include <linux/interrupt.h>
+#include <linux/list.h>
+#include <sound/control.h>
 
 struct snd_soc_codec;
 
@@ -30,9 +32,9 @@
 	int series_startup;
 	int no_series_update;
 
-	bool no_cache_class_w;
-	bool class_w;
-	u16 class_w_dcs;
+	bool no_cache_dac_hp_direct;
+	struct list_head dcs_cache;
+	bool (*check_class_w_digital)(struct snd_soc_codec *);
 
 	bool lineout1_se;
 	bool lineout1n_ena;
@@ -58,5 +60,9 @@
 extern void wm_hubs_vmid_ena(struct snd_soc_codec *codec);
 extern void wm_hubs_set_bias_level(struct snd_soc_codec *codec,
 				   enum snd_soc_bias_level level);
+extern void wm_hubs_update_class_w(struct snd_soc_codec *codec);
+
+extern const struct snd_kcontrol_new wm_hubs_hpl_mux;
+extern const struct snd_kcontrol_new wm_hubs_hpr_mux;
 
 #endif
diff --git a/sound/soc/ep93xx/ep93xx-ac97.c b/sound/soc/ep93xx/ep93xx-ac97.c
index 0678637..bdffab3 100644
--- a/sound/soc/ep93xx/ep93xx-ac97.c
+++ b/sound/soc/ep93xx/ep93xx-ac97.c
@@ -87,17 +87,13 @@
  * struct ep93xx_ac97_info - EP93xx AC97 controller info structure
  * @lock: mutex serializing access to the bus (slot 1 & 2 ops)
  * @dev: pointer to the platform device dev structure
- * @mem: physical memory resource for the registers
  * @regs: mapped AC97 controller registers
- * @irq: AC97 interrupt number
  * @done: bus ops wait here for an interrupt
  */
 struct ep93xx_ac97_info {
 	struct mutex		lock;
 	struct device		*dev;
-	struct resource		*mem;
 	void __iomem		*regs;
-	int			irq;
 	struct completion	done;
 };
 
@@ -359,66 +355,50 @@
 static int __devinit ep93xx_ac97_probe(struct platform_device *pdev)
 {
 	struct ep93xx_ac97_info *info;
+	struct resource *res;
+	unsigned int irq;
 	int ret;
 
-	info = kzalloc(sizeof(struct ep93xx_ac97_info), GFP_KERNEL);
+	info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
 	if (!info)
 		return -ENOMEM;
 
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENODEV;
+
+	info->regs = devm_request_and_ioremap(&pdev->dev, res);
+	if (!info->regs)
+		return -ENXIO;
+
+	irq = platform_get_irq(pdev, 0);
+	if (!irq)
+		return -ENODEV;
+
+	ret = devm_request_irq(&pdev->dev, irq, ep93xx_ac97_interrupt,
+			       IRQF_TRIGGER_HIGH, pdev->name, info);
+	if (ret)
+		goto fail;
+
 	dev_set_drvdata(&pdev->dev, info);
 
 	mutex_init(&info->lock);
 	init_completion(&info->done);
 	info->dev = &pdev->dev;
 
-	info->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (!info->mem) {
-		ret = -ENXIO;
-		goto fail_free_info;
-	}
-
-	info->irq = platform_get_irq(pdev, 0);
-	if (!info->irq) {
-		ret = -ENXIO;
-		goto fail_free_info;
-	}
-
-	if (!request_mem_region(info->mem->start, resource_size(info->mem),
-				pdev->name)) {
-		ret = -EBUSY;
-		goto fail_free_info;
-	}
-
-	info->regs = ioremap(info->mem->start, resource_size(info->mem));
-	if (!info->regs) {
-		ret = -ENOMEM;
-		goto fail_release_mem;
-	}
-
-	ret = request_irq(info->irq, ep93xx_ac97_interrupt, IRQF_TRIGGER_HIGH,
-			  pdev->name, info);
-	if (ret)
-		goto fail_unmap_mem;
-
 	ep93xx_ac97_info = info;
 	platform_set_drvdata(pdev, info);
 
 	ret = snd_soc_register_dai(&pdev->dev, &ep93xx_ac97_dai);
 	if (ret)
-		goto fail_free_irq;
+		goto fail;
 
 	return 0;
 
-fail_free_irq:
+fail:
 	platform_set_drvdata(pdev, NULL);
-	free_irq(info->irq, info);
-fail_unmap_mem:
-	iounmap(info->regs);
-fail_release_mem:
-	release_mem_region(info->mem->start, resource_size(info->mem));
-fail_free_info:
-	kfree(info);
-
+	ep93xx_ac97_info = NULL;
+	dev_set_drvdata(&pdev->dev, NULL);
 	return ret;
 }
 
@@ -431,11 +411,9 @@
 	/* disable the AC97 controller */
 	ep93xx_ac97_write_reg(info, AC97GCR, 0);
 
-	free_irq(info->irq, info);
-	iounmap(info->regs);
-	release_mem_region(info->mem->start, resource_size(info->mem));
 	platform_set_drvdata(pdev, NULL);
-	kfree(info);
+	ep93xx_ac97_info = NULL;
+	dev_set_drvdata(&pdev->dev, NULL);
 
 	return 0;
 }
diff --git a/sound/soc/ep93xx/ep93xx-i2s.c b/sound/soc/ep93xx/ep93xx-i2s.c
index f7a6234..8df8f6d 100644
--- a/sound/soc/ep93xx/ep93xx-i2s.c
+++ b/sound/soc/ep93xx/ep93xx-i2s.c
@@ -63,7 +63,6 @@
 	struct clk			*sclk;
 	struct clk			*lrclk;
 	struct ep93xx_pcm_dma_params	*dma_params;
-	struct resource			*mem;
 	void __iomem			*regs;
 };
 
@@ -373,38 +372,22 @@
 	struct resource *res;
 	int err;
 
-	info = kzalloc(sizeof(struct ep93xx_i2s_info), GFP_KERNEL);
-	if (!info) {
-		err = -ENOMEM;
-		goto fail;
-	}
-
-	dev_set_drvdata(&pdev->dev, info);
-	info->dma_params = ep93xx_i2s_dma_params;
+	info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (!res) {
-		err = -ENODEV;
-		goto fail_free_info;
-	}
+	if (!res)
+		return -ENODEV;
 
-	info->mem = request_mem_region(res->start, resource_size(res),
-				       pdev->name);
-	if (!info->mem) {
-		err = -EBUSY;
-		goto fail_free_info;
-	}
-
-	info->regs = ioremap(info->mem->start, resource_size(info->mem));
-	if (!info->regs) {
-		err = -ENXIO;
-		goto fail_release_mem;
-	}
+	info->regs = devm_request_and_ioremap(&pdev->dev, res);
+	if (!info->regs)
+		return -ENXIO;
 
 	info->mclk = clk_get(&pdev->dev, "mclk");
 	if (IS_ERR(info->mclk)) {
 		err = PTR_ERR(info->mclk);
-		goto fail_unmap_mem;
+		goto fail;
 	}
 
 	info->sclk = clk_get(&pdev->dev, "sclk");
@@ -419,6 +402,9 @@
 		goto fail_put_sclk;
 	}
 
+	dev_set_drvdata(&pdev->dev, info);
+	info->dma_params = ep93xx_i2s_dma_params;
+
 	err = snd_soc_register_dai(&pdev->dev, &ep93xx_i2s_dai);
 	if (err)
 		goto fail_put_lrclk;
@@ -426,17 +412,12 @@
 	return 0;
 
 fail_put_lrclk:
+	dev_set_drvdata(&pdev->dev, NULL);
 	clk_put(info->lrclk);
 fail_put_sclk:
 	clk_put(info->sclk);
 fail_put_mclk:
 	clk_put(info->mclk);
-fail_unmap_mem:
-	iounmap(info->regs);
-fail_release_mem:
-	release_mem_region(info->mem->start, resource_size(info->mem));
-fail_free_info:
-	kfree(info);
 fail:
 	return err;
 }
@@ -446,12 +427,10 @@
 	struct ep93xx_i2s_info *info = dev_get_drvdata(&pdev->dev);
 
 	snd_soc_unregister_dai(&pdev->dev);
+	dev_set_drvdata(&pdev->dev, NULL);
 	clk_put(info->lrclk);
 	clk_put(info->sclk);
 	clk_put(info->mclk);
-	iounmap(info->regs);
-	release_mem_region(info->mem->start, resource_size(info->mem));
-	kfree(info);
 	return 0;
 }
 
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index d754d34..d701330 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -1,18 +1,31 @@
+config SND_SOC_FSL_SSI
+	tristate
+
+config SND_SOC_FSL_UTILS
+	tristate
+
+menuconfig SND_POWERPC_SOC
+	tristate "SoC Audio for Freescale PowerPC CPUs"
+	depends on FSL_SOC
+	help
+	  Say Y or M if you want to add support for codecs attached to
+	  the PowerPC CPUs.
+
+if SND_POWERPC_SOC
+
 config SND_MPC52xx_DMA
 	tristate
 
-# ASoC platform support for the Freescale PowerPC SOCs that have an SSI and
-# an Elo DMA controller, such as the MPC8610 and P1022.  You will still need to
-# select a platform driver and a codec driver.
-config SND_SOC_POWERPC_SSI
+config SND_SOC_POWERPC_DMA
 	tristate
-	depends on FSL_SOC
 
 config SND_SOC_MPC8610_HPCD
 	tristate "ALSA SoC support for the Freescale MPC8610 HPCD board"
 	# I2C is necessary for the CS4270 driver
 	depends on MPC8610_HPCD && I2C
-	select SND_SOC_POWERPC_SSI
+	select SND_SOC_FSL_SSI
+	select SND_SOC_FSL_UTILS
+	select SND_SOC_POWERPC_DMA
 	select SND_SOC_CS4270
 	select SND_SOC_CS4270_VD33_ERRATA
 	default y if MPC8610_HPCD
@@ -23,7 +36,9 @@
 	tristate "ALSA SoC support for the Freescale P1022 DS board"
 	# I2C is necessary for the WM8776 driver
 	depends on P1022_DS && I2C
-	select SND_SOC_POWERPC_SSI
+	select SND_SOC_FSL_SSI
+	select SND_SOC_FSL_UTILS
+	select SND_SOC_POWERPC_DMA
 	select SND_SOC_WM8776
 	default y if P1022_DS
 	help
@@ -65,3 +80,103 @@
 	help
 	  Say Y if you want to add support for sound on the Efika.
 
+endif # SND_POWERPC_SOC
+
+menuconfig SND_IMX_SOC
+	tristate "SoC Audio for Freescale i.MX CPUs"
+	depends on ARCH_MXC
+	help
+	  Say Y or M if you want to add support for codecs attached to
+	  the i.MX CPUs.
+
+if SND_IMX_SOC
+
+config SND_SOC_IMX_SSI
+	tristate
+
+config SND_SOC_IMX_PCM
+	tristate
+
+config SND_SOC_IMX_PCM_FIQ
+	tristate
+	select FIQ
+	select SND_SOC_IMX_PCM
+
+config SND_SOC_IMX_PCM_DMA
+	tristate
+	select SND_SOC_DMAENGINE_PCM
+	select SND_SOC_IMX_PCM
+
+config SND_SOC_IMX_AUDMUX
+	tristate
+
+config SND_MXC_SOC_WM1133_EV1
+	tristate "Audio on the i.MX31ADS with WM1133-EV1 fitted"
+	depends on MACH_MX31ADS_WM1133_EV1 && EXPERIMENTAL
+	select SND_SOC_WM8350
+	select SND_SOC_IMX_PCM_FIQ
+	select SND_SOC_IMX_AUDMUX
+	select SND_SOC_IMX_SSI
+	help
+	  Enable support for audio on the i.MX31ADS with the WM1133-EV1
+	  PMIC board with WM8835x fitted.
+
+config SND_SOC_MX27VIS_AIC32X4
+	tristate "SoC audio support for Visstrim M10 boards"
+	depends on MACH_IMX27_VISSTRIM_M10 && I2C
+	select SND_SOC_TLV320AIC32X4
+	select SND_SOC_IMX_PCM_DMA
+	select SND_SOC_IMX_AUDMUX
+	select SND_SOC_IMX_SSI
+	help
+	  Say Y if you want to add support for SoC audio on Visstrim SM10
+	  board with TLV320AIC32X4 codec.
+
+config SND_SOC_PHYCORE_AC97
+	tristate "SoC Audio support for Phytec phyCORE (and phyCARD) boards"
+	depends on MACH_PCM043 || MACH_PCA100
+	select SND_SOC_AC97_BUS
+	select SND_SOC_WM9712
+	select SND_SOC_IMX_PCM_FIQ
+	select SND_SOC_IMX_AUDMUX
+	select SND_SOC_IMX_SSI
+	help
+	  Say Y if you want to add support for SoC audio on Phytec phyCORE
+	  and phyCARD boards in AC97 mode
+
+config SND_SOC_EUKREA_TLV320
+	tristate "Eukrea TLV320"
+	depends on MACH_EUKREA_MBIMX27_BASEBOARD \
+		|| MACH_EUKREA_MBIMXSD25_BASEBOARD \
+		|| MACH_EUKREA_MBIMXSD35_BASEBOARD \
+		|| MACH_EUKREA_MBIMXSD51_BASEBOARD
+	depends on I2C
+	select SND_SOC_TLV320AIC23
+	select SND_SOC_IMX_PCM_FIQ
+	select SND_SOC_IMX_AUDMUX
+	select SND_SOC_IMX_SSI
+	help
+	  Enable I2S based access to the TLV320AIC23B codec attached
+	  to the SSI interface
+
+config SND_SOC_IMX_SGTL5000
+	tristate "SoC Audio support for i.MX boards with sgtl5000"
+	depends on OF && I2C
+	select SND_SOC_SGTL5000
+	select SND_SOC_IMX_PCM_DMA
+	select SND_SOC_IMX_AUDMUX
+	select SND_SOC_FSL_SSI
+	select SND_SOC_FSL_UTILS
+	help
+	  Say Y if you want to add support for SoC audio on an i.MX board with
+	  a sgtl5000 codec.
+
+config SND_SOC_IMX_MC13783
+	tristate "SoC Audio support for I.MX boards with mc13783"
+	depends on MFD_MC13783
+	select SND_SOC_IMX_SSI
+	select SND_SOC_IMX_AUDMUX
+	select SND_SOC_MC13783
+	select SND_SOC_IMX_PCM_DMA
+
+endif # SND_IMX_SOC
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index b4a38c0..5f3cf3f 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -8,8 +8,11 @@
 
 # Freescale PowerPC SSI/DMA Platform Support
 snd-soc-fsl-ssi-objs := fsl_ssi.o
+snd-soc-fsl-utils-objs := fsl_utils.o
 snd-soc-fsl-dma-objs := fsl_dma.o
-obj-$(CONFIG_SND_SOC_POWERPC_SSI) += snd-soc-fsl-ssi.o snd-soc-fsl-dma.o
+obj-$(CONFIG_SND_SOC_FSL_SSI) += snd-soc-fsl-ssi.o
+obj-$(CONFIG_SND_SOC_FSL_UTILS) += snd-soc-fsl-utils.o
+obj-$(CONFIG_SND_SOC_POWERPC_DMA) += snd-soc-fsl-dma.o
 
 # MPC5200 Platform Support
 obj-$(CONFIG_SND_MPC52xx_DMA) += mpc5200_dma.o
@@ -20,3 +23,29 @@
 obj-$(CONFIG_SND_MPC52xx_SOC_PCM030) += pcm030-audio-fabric.o
 obj-$(CONFIG_SND_MPC52xx_SOC_EFIKA) += efika-audio-fabric.o
 
+# i.MX Platform Support
+snd-soc-imx-ssi-objs := imx-ssi.o
+snd-soc-imx-audmux-objs := imx-audmux.o
+
+obj-$(CONFIG_SND_SOC_IMX_SSI) += snd-soc-imx-ssi.o
+obj-$(CONFIG_SND_SOC_IMX_AUDMUX) += snd-soc-imx-audmux.o
+
+obj-$(CONFIG_SND_SOC_IMX_PCM) += snd-soc-imx-pcm.o
+snd-soc-imx-pcm-y := imx-pcm.o
+snd-soc-imx-pcm-$(CONFIG_SND_SOC_IMX_PCM_FIQ) += imx-pcm-fiq.o
+snd-soc-imx-pcm-$(CONFIG_SND_SOC_IMX_PCM_DMA) += imx-pcm-dma.o
+
+# i.MX Machine Support
+snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o
+snd-soc-phycore-ac97-objs := phycore-ac97.o
+snd-soc-mx27vis-aic32x4-objs := mx27vis-aic32x4.o
+snd-soc-wm1133-ev1-objs := wm1133-ev1.o
+snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o
+snd-soc-imx-mc13783-objs := imx-mc13783.o
+
+obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o
+obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o
+obj-$(CONFIG_SND_SOC_MX27VIS_AIC32X4) += snd-soc-mx27vis-aic32x4.o
+obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o
+obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o
+obj-$(CONFIG_SND_SOC_IMX_MC13783) += snd-soc-imx-mc13783.o
diff --git a/sound/soc/imx/eukrea-tlv320.c b/sound/soc/fsl/eukrea-tlv320.c
similarity index 99%
rename from sound/soc/imx/eukrea-tlv320.c
rename to sound/soc/fsl/eukrea-tlv320.c
index 7d4475c..efb9ede 100644
--- a/sound/soc/imx/eukrea-tlv320.c
+++ b/sound/soc/fsl/eukrea-tlv320.c
@@ -7,7 +7,7 @@
  * which is Copyright 2009 Simtec Electronics
  * and on sound/soc/imx/phycore-ac97.c which is
  * Copyright 2009 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
- * 
+ *
  *  This program is free software; you can redistribute  it and/or modify it
  *  under  the terms of  the GNU General  Public License as published by the
  *  Free Software Foundation;  either version 2 of the  License, or (at your
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index 2eb407f..4ed2afd 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -11,11 +11,15 @@
  */
 
 #include <linux/init.h>
+#include <linux/io.h>
 #include <linux/module.h>
 #include <linux/interrupt.h>
+#include <linux/clk.h>
 #include <linux/device.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
 #include <linux/of_platform.h>
 
 #include <sound/core.h>
@@ -25,6 +29,26 @@
 #include <sound/soc.h>
 
 #include "fsl_ssi.h"
+#include "imx-pcm.h"
+
+#ifdef PPC
+#define read_ssi(addr)			 in_be32(addr)
+#define write_ssi(val, addr)		 out_be32(addr, val)
+#define write_ssi_mask(addr, clear, set) clrsetbits_be32(addr, clear, set)
+#elif defined ARM
+#define read_ssi(addr)			 readl(addr)
+#define write_ssi(val, addr)		 writel(val, addr)
+/*
+ * FIXME: Proper locking should be added at write_ssi_mask caller level
+ * to ensure this register read/modify/write sequence is race free.
+ */
+static inline void write_ssi_mask(u32 __iomem *addr, u32 clear, u32 set)
+{
+	u32 val = readl(addr);
+	val = (val & ~clear) | set;
+	writel(val, addr);
+}
+#endif
 
 /**
  * FSLSSI_I2S_RATES: sample rates supported by the I2S
@@ -94,6 +118,13 @@
 	struct device_attribute dev_attr;
 	struct platform_device *pdev;
 
+	bool new_binding;
+	bool ssi_on_imx;
+	struct clk *clk;
+	struct platform_device *imx_pcm_pdev;
+	struct imx_pcm_dma_params dma_params_tx;
+	struct imx_pcm_dma_params dma_params_rx;
+
 	struct {
 		unsigned int rfrc;
 		unsigned int tfrc;
@@ -145,7 +176,7 @@
 	   were interrupted for.  We mask it with the Interrupt Enable register
 	   so that we only check for events that we're interested in.
 	 */
-	sisr = in_be32(&ssi->sisr) & SIER_FLAGS;
+	sisr = read_ssi(&ssi->sisr) & SIER_FLAGS;
 
 	if (sisr & CCSR_SSI_SISR_RFRC) {
 		ssi_private->stats.rfrc++;
@@ -260,7 +291,7 @@
 
 	/* Clear the bits that we set */
 	if (sisr2)
-		out_be32(&ssi->sisr, sisr2);
+		write_ssi(sisr2, &ssi->sisr);
 
 	return ret;
 }
@@ -295,7 +326,7 @@
 		 * SSI needs to be disabled before updating the registers we set
 		 * here.
 		 */
-		clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN);
+		write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0);
 
 		/*
 		 * Program the SSI into I2S Slave Non-Network Synchronous mode.
@@ -303,20 +334,18 @@
 		 *
 		 * FIXME: Little-endian samples require a different shift dir
 		 */
-		clrsetbits_be32(&ssi->scr,
+		write_ssi_mask(&ssi->scr,
 			CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_SYN,
 			CCSR_SSI_SCR_TFR_CLK_DIS | CCSR_SSI_SCR_I2S_MODE_SLAVE
 			| (synchronous ? CCSR_SSI_SCR_SYN : 0));
 
-		out_be32(&ssi->stcr,
-			 CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 |
+		write_ssi(CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 |
 			 CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TEFS |
-			 CCSR_SSI_STCR_TSCKP);
+			 CCSR_SSI_STCR_TSCKP, &ssi->stcr);
 
-		out_be32(&ssi->srcr,
-			 CCSR_SSI_SRCR_RXBIT0 | CCSR_SSI_SRCR_RFEN0 |
+		write_ssi(CCSR_SSI_SRCR_RXBIT0 | CCSR_SSI_SRCR_RFEN0 |
 			 CCSR_SSI_SRCR_RFSI | CCSR_SSI_SRCR_REFS |
-			 CCSR_SSI_SRCR_RSCKP);
+			 CCSR_SSI_SRCR_RSCKP, &ssi->srcr);
 
 		/*
 		 * The DC and PM bits are only used if the SSI is the clock
@@ -324,7 +353,7 @@
 		 */
 
 		/* Enable the interrupts and DMA requests */
-		out_be32(&ssi->sier, SIER_FLAGS);
+		write_ssi(SIER_FLAGS, &ssi->sier);
 
 		/*
 		 * Set the watermark for transmit FIFI 0 and receive FIFO 0. We
@@ -339,9 +368,9 @@
 		 * make this value larger (and maybe we should), but this way
 		 * data will be written to memory as soon as it's available.
 		 */
-		out_be32(&ssi->sfcsr,
-			CCSR_SSI_SFCSR_TFWM0(ssi_private->fifo_depth - 2) |
-			CCSR_SSI_SFCSR_RFWM0(ssi_private->fifo_depth - 2));
+		write_ssi(CCSR_SSI_SFCSR_TFWM0(ssi_private->fifo_depth - 2) |
+			CCSR_SSI_SFCSR_RFWM0(ssi_private->fifo_depth - 2),
+			&ssi->sfcsr);
 
 		/*
 		 * We keep the SSI disabled because if we enable it, then the
@@ -393,6 +422,12 @@
 		ssi_private->second_stream = substream;
 	}
 
+	if (ssi_private->ssi_on_imx)
+		snd_soc_dai_set_dma_data(dai, substream,
+			(substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+				&ssi_private->dma_params_tx :
+				&ssi_private->dma_params_rx);
+
 	return 0;
 }
 
@@ -417,7 +452,7 @@
 	unsigned int sample_size =
 		snd_pcm_format_width(params_format(hw_params));
 	u32 wl = CCSR_SSI_SxCCR_WL(sample_size);
-	int enabled = in_be32(&ssi->scr) & CCSR_SSI_SCR_SSIEN;
+	int enabled = read_ssi(&ssi->scr) & CCSR_SSI_SCR_SSIEN;
 
 	/*
 	 * If we're in synchronous mode, and the SSI is already enabled,
@@ -439,9 +474,9 @@
 	/* In synchronous mode, the SSI uses STCCR for capture */
 	if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ||
 	    ssi_private->cpu_dai_drv.symmetric_rates)
-		clrsetbits_be32(&ssi->stccr, CCSR_SSI_SxCCR_WL_MASK, wl);
+		write_ssi_mask(&ssi->stccr, CCSR_SSI_SxCCR_WL_MASK, wl);
 	else
-		clrsetbits_be32(&ssi->srccr, CCSR_SSI_SxCCR_WL_MASK, wl);
+		write_ssi_mask(&ssi->srccr, CCSR_SSI_SxCCR_WL_MASK, wl);
 
 	return 0;
 }
@@ -466,19 +501,19 @@
 	case SNDRV_PCM_TRIGGER_START:
 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-			setbits32(&ssi->scr,
+			write_ssi_mask(&ssi->scr, 0,
 				CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE);
 		else
-			setbits32(&ssi->scr,
+			write_ssi_mask(&ssi->scr, 0,
 				CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_RE);
 		break;
 
 	case SNDRV_PCM_TRIGGER_STOP:
 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-			clrbits32(&ssi->scr, CCSR_SSI_SCR_TE);
+			write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_TE, 0);
 		else
-			clrbits32(&ssi->scr, CCSR_SSI_SCR_RE);
+			write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_RE, 0);
 		break;
 
 	default:
@@ -510,7 +545,7 @@
 	if (!ssi_private->first_stream) {
 		struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
 
-		clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN);
+		write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0);
 	}
 }
 
@@ -622,12 +657,6 @@
 	if (!of_device_is_available(np))
 		return -ENODEV;
 
-	/* Check for a codec-handle property. */
-	if (!of_get_property(np, "codec-handle", NULL)) {
-		dev_err(&pdev->dev, "missing codec-handle property\n");
-		return -ENODEV;
-	}
-
 	/* We only support the SSI in "I2S Slave" mode */
 	sprop = of_get_property(np, "fsl,mode", NULL);
 	if (!sprop || strcmp(sprop, "i2s-slave")) {
@@ -692,6 +721,50 @@
                 /* Older 8610 DTs didn't have the fifo-depth property */
 		ssi_private->fifo_depth = 8;
 
+	if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx21-ssi")) {
+		u32 dma_events[2];
+		ssi_private->ssi_on_imx = true;
+
+		ssi_private->clk = clk_get(&pdev->dev, NULL);
+		if (IS_ERR(ssi_private->clk)) {
+			ret = PTR_ERR(ssi_private->clk);
+			dev_err(&pdev->dev, "could not get clock: %d\n", ret);
+			goto error_irq;
+		}
+		clk_prepare_enable(ssi_private->clk);
+
+		/*
+		 * We have burstsize be "fifo_depth - 2" to match the SSI
+		 * watermark setting in fsl_ssi_startup().
+		 */
+		ssi_private->dma_params_tx.burstsize =
+			ssi_private->fifo_depth - 2;
+		ssi_private->dma_params_rx.burstsize =
+			ssi_private->fifo_depth - 2;
+		ssi_private->dma_params_tx.dma_addr =
+			ssi_private->ssi_phys + offsetof(struct ccsr_ssi, stx0);
+		ssi_private->dma_params_rx.dma_addr =
+			ssi_private->ssi_phys + offsetof(struct ccsr_ssi, srx0);
+		/*
+		 * TODO: This is a temporary solution and should be changed
+		 * to use generic DMA binding later when the helplers get in.
+		 */
+		ret = of_property_read_u32_array(pdev->dev.of_node,
+					"fsl,ssi-dma-events", dma_events, 2);
+		if (ret) {
+			dev_err(&pdev->dev, "could not get dma events\n");
+			goto error_clk;
+		}
+		ssi_private->dma_params_tx.dma = dma_events[0];
+		ssi_private->dma_params_rx.dma = dma_events[1];
+
+		ssi_private->dma_params_tx.shared_peripheral =
+				of_device_is_compatible(of_get_parent(np),
+							"fsl,spba-bus");
+		ssi_private->dma_params_rx.shared_peripheral =
+				ssi_private->dma_params_tx.shared_peripheral;
+	}
+
 	/* Initialize the the device_attribute structure */
 	dev_attr = &ssi_private->dev_attr;
 	sysfs_attr_init(&dev_attr->attr);
@@ -715,6 +788,26 @@
 		goto error_dev;
 	}
 
+	if (ssi_private->ssi_on_imx) {
+		ssi_private->imx_pcm_pdev =
+			platform_device_register_simple("imx-pcm-audio",
+							-1, NULL, 0);
+		if (IS_ERR(ssi_private->imx_pcm_pdev)) {
+			ret = PTR_ERR(ssi_private->imx_pcm_pdev);
+			goto error_dev;
+		}
+	}
+
+	/*
+	 * If codec-handle property is missing from SSI node, we assume
+	 * that the machine driver uses new binding which does not require
+	 * SSI driver to trigger machine driver's probe.
+	 */
+	if (!of_get_property(np, "codec-handle", NULL)) {
+		ssi_private->new_binding = true;
+		goto done;
+	}
+
 	/* Trigger the machine driver's probe function.  The platform driver
 	 * name of the machine driver is taken from /compatible property of the
 	 * device tree.  We also pass the address of the CPU DAI driver
@@ -736,15 +829,24 @@
 		goto error_dai;
 	}
 
+done:
 	return 0;
 
 error_dai:
+	if (ssi_private->ssi_on_imx)
+		platform_device_unregister(ssi_private->imx_pcm_pdev);
 	snd_soc_unregister_dai(&pdev->dev);
 
 error_dev:
 	dev_set_drvdata(&pdev->dev, NULL);
 	device_remove_file(&pdev->dev, dev_attr);
 
+error_clk:
+	if (ssi_private->ssi_on_imx) {
+		clk_disable_unprepare(ssi_private->clk);
+		clk_put(ssi_private->clk);
+	}
+
 error_irq:
 	free_irq(ssi_private->irq, ssi_private);
 
@@ -764,7 +866,13 @@
 {
 	struct fsl_ssi_private *ssi_private = dev_get_drvdata(&pdev->dev);
 
-	platform_device_unregister(ssi_private->pdev);
+	if (!ssi_private->new_binding)
+		platform_device_unregister(ssi_private->pdev);
+	if (ssi_private->ssi_on_imx) {
+		platform_device_unregister(ssi_private->imx_pcm_pdev);
+		clk_disable_unprepare(ssi_private->clk);
+		clk_put(ssi_private->clk);
+	}
 	snd_soc_unregister_dai(&pdev->dev);
 	device_remove_file(&pdev->dev, &ssi_private->dev_attr);
 
@@ -779,6 +887,7 @@
 
 static const struct of_device_id fsl_ssi_ids[] = {
 	{ .compatible = "fsl,mpc8610-ssi", },
+	{ .compatible = "fsl,imx21-ssi", },
 	{}
 };
 MODULE_DEVICE_TABLE(of, fsl_ssi_ids);
diff --git a/sound/soc/fsl/fsl_utils.c b/sound/soc/fsl/fsl_utils.c
new file mode 100644
index 0000000..b9e42b5
--- /dev/null
+++ b/sound/soc/fsl/fsl_utils.c
@@ -0,0 +1,91 @@
+/**
+ * Freescale ALSA SoC Machine driver utility
+ *
+ * Author: Timur Tabi <timur@freescale.com>
+ *
+ * Copyright 2010 Freescale Semiconductor, Inc.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2.  This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <sound/soc.h>
+
+#include "fsl_utils.h"
+
+/**
+ * fsl_asoc_get_dma_channel - determine the dma channel for a SSI node
+ *
+ * @ssi_np: pointer to the SSI device tree node
+ * @name: name of the phandle pointing to the dma channel
+ * @dai: ASoC DAI link pointer to be filled with platform_name
+ * @dma_channel_id: dma channel id to be returned
+ * @dma_id: dma id to be returned
+ *
+ * This function determines the dma and channel id for given SSI node.  It
+ * also discovers the platform_name for the ASoC DAI link.
+ */
+int fsl_asoc_get_dma_channel(struct device_node *ssi_np,
+			     const char *name,
+			     struct snd_soc_dai_link *dai,
+			     unsigned int *dma_channel_id,
+			     unsigned int *dma_id)
+{
+	struct resource res;
+	struct device_node *dma_channel_np, *dma_np;
+	const u32 *iprop;
+	int ret;
+
+	dma_channel_np = of_parse_phandle(ssi_np, name, 0);
+	if (!dma_channel_np)
+		return -EINVAL;
+
+	if (!of_device_is_compatible(dma_channel_np, "fsl,ssi-dma-channel")) {
+		of_node_put(dma_channel_np);
+		return -EINVAL;
+	}
+
+	/* Determine the dev_name for the device_node.  This code mimics the
+	 * behavior of of_device_make_bus_id(). We need this because ASoC uses
+	 * the dev_name() of the device to match the platform (DMA) device with
+	 * the CPU (SSI) device.  It's all ugly and hackish, but it works (for
+	 * now).
+	 *
+	 * dai->platform name should already point to an allocated buffer.
+	 */
+	ret = of_address_to_resource(dma_channel_np, 0, &res);
+	if (ret) {
+		of_node_put(dma_channel_np);
+		return ret;
+	}
+	snprintf((char *)dai->platform_name, DAI_NAME_SIZE, "%llx.%s",
+		 (unsigned long long) res.start, dma_channel_np->name);
+
+	iprop = of_get_property(dma_channel_np, "cell-index", NULL);
+	if (!iprop) {
+		of_node_put(dma_channel_np);
+		return -EINVAL;
+	}
+	*dma_channel_id = be32_to_cpup(iprop);
+
+	dma_np = of_get_parent(dma_channel_np);
+	iprop = of_get_property(dma_np, "cell-index", NULL);
+	if (!iprop) {
+		of_node_put(dma_np);
+		return -EINVAL;
+	}
+	*dma_id = be32_to_cpup(iprop);
+
+	of_node_put(dma_np);
+	of_node_put(dma_channel_np);
+
+	return 0;
+}
+EXPORT_SYMBOL(fsl_asoc_get_dma_channel);
+
+MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
+MODULE_DESCRIPTION("Freescale ASoC utility code");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/fsl/fsl_utils.h b/sound/soc/fsl/fsl_utils.h
new file mode 100644
index 0000000..b295112
--- /dev/null
+++ b/sound/soc/fsl/fsl_utils.h
@@ -0,0 +1,26 @@
+/**
+ * Freescale ALSA SoC Machine driver utility
+ *
+ * Author: Timur Tabi <timur@freescale.com>
+ *
+ * Copyright 2010 Freescale Semiconductor, Inc.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2.  This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#ifndef _FSL_UTILS_H
+#define _FSL_UTILS_H
+
+#define DAI_NAME_SIZE	32
+
+struct snd_soc_dai_link;
+struct device_node;
+
+int fsl_asoc_get_dma_channel(struct device_node *ssi_np, const char *name,
+			     struct snd_soc_dai_link *dai,
+			     unsigned int *dma_channel_id,
+			     unsigned int *dma_id);
+
+#endif /* _FSL_UTILS_H */
diff --git a/sound/soc/imx/imx-audmux.c b/sound/soc/fsl/imx-audmux.c
similarity index 100%
rename from sound/soc/imx/imx-audmux.c
rename to sound/soc/fsl/imx-audmux.c
diff --git a/sound/soc/imx/imx-audmux.h b/sound/soc/fsl/imx-audmux.h
similarity index 100%
rename from sound/soc/imx/imx-audmux.h
rename to sound/soc/fsl/imx-audmux.h
diff --git a/sound/soc/fsl/imx-mc13783.c b/sound/soc/fsl/imx-mc13783.c
new file mode 100644
index 0000000..f59c349
--- /dev/null
+++ b/sound/soc/fsl/imx-mc13783.c
@@ -0,0 +1,156 @@
+/*
+ * imx-mc13783.c  --  SoC audio for imx based boards with mc13783 codec
+ *
+ * Copyright 2012 Philippe Retornaz, <philippe.retornaz@epfl.ch>
+ *
+ * Heavly based on phycore-mc13783:
+ * Copyright 2009 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <asm/mach-types.h>
+
+#include "../codecs/mc13783.h"
+#include "imx-ssi.h"
+#include "imx-audmux.h"
+
+#define FMT_SSI (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF | \
+		SND_SOC_DAIFMT_CBM_CFM)
+
+static int imx_mc13783_hifi_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	int ret;
+
+	ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xfffffffc, 0xfffffffc,
+					4, 16);
+	if (ret)
+		return ret;
+
+	ret = snd_soc_dai_set_sysclk(codec_dai, MC13783_CLK_CLIA, 26000000, 0);
+	if (ret)
+		return ret;
+
+	ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0x0, 0xfffffffc, 2, 16);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static struct snd_soc_ops imx_mc13783_hifi_ops = {
+	.hw_params = imx_mc13783_hifi_hw_params,
+};
+
+static struct snd_soc_dai_link imx_mc13783_dai_mc13783[] = {
+	{
+		.name = "MC13783",
+		.stream_name	 = "Sound",
+		.codec_dai_name	 = "mc13783-hifi",
+		.codec_name	 = "mc13783-codec",
+		.cpu_dai_name	 = "imx-ssi.0",
+		.platform_name	 = "imx-pcm-audio.0",
+		.ops		 = &imx_mc13783_hifi_ops,
+		.symmetric_rates = 1,
+		.dai_fmt 	 = FMT_SSI,
+	},
+};
+
+static const struct snd_soc_dapm_widget imx_mc13783_widget[] = {
+	SND_SOC_DAPM_MIC("Mic", NULL),
+	SND_SOC_DAPM_HP("Headphone", NULL),
+	SND_SOC_DAPM_SPK("Speaker", NULL),
+};
+
+static const struct snd_soc_dapm_route imx_mc13783_routes[] = {
+	{"Speaker", NULL, "LSP"},
+	{"Headphone", NULL, "HSL"},
+	{"Headphone", NULL, "HSR"},
+
+	{"MC1LIN", NULL, "MC1 Bias"},
+	{"MC2IN", NULL, "MC2 Bias"},
+	{"MC1 Bias", NULL, "Mic"},
+	{"MC2 Bias", NULL, "Mic"},
+};
+
+static struct snd_soc_card imx_mc13783 = {
+	.name		= "imx_mc13783",
+	.dai_link	= imx_mc13783_dai_mc13783,
+	.num_links	= ARRAY_SIZE(imx_mc13783_dai_mc13783),
+	.dapm_widgets	= imx_mc13783_widget,
+	.num_dapm_widgets = ARRAY_SIZE(imx_mc13783_widget),
+	.dapm_routes	= imx_mc13783_routes,
+	.num_dapm_routes = ARRAY_SIZE(imx_mc13783_routes),
+};
+
+static int __devinit imx_mc13783_probe(struct platform_device *pdev)
+{
+	int ret;
+
+	imx_mc13783.dev = &pdev->dev;
+
+	ret = snd_soc_register_card(&imx_mc13783);
+	if (ret) {
+		dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
+			ret);
+		return ret;
+	}
+
+	imx_audmux_v2_configure_port(MX31_AUDMUX_PORT4_SSI_PINS_4,
+		IMX_AUDMUX_V2_PTCR_SYN,
+		IMX_AUDMUX_V2_PDCR_RXDSEL(MX31_AUDMUX_PORT1_SSI0) |
+		IMX_AUDMUX_V2_PDCR_MODE(1) |
+		IMX_AUDMUX_V2_PDCR_INMMASK(0xfc));
+	imx_audmux_v2_configure_port(MX31_AUDMUX_PORT1_SSI0,
+		IMX_AUDMUX_V2_PTCR_SYN |
+		IMX_AUDMUX_V2_PTCR_TFSDIR |
+		IMX_AUDMUX_V2_PTCR_TFSEL(MX31_AUDMUX_PORT4_SSI_PINS_4) |
+		IMX_AUDMUX_V2_PTCR_TCLKDIR |
+		IMX_AUDMUX_V2_PTCR_TCSEL(MX31_AUDMUX_PORT4_SSI_PINS_4) |
+		IMX_AUDMUX_V2_PTCR_RFSDIR |
+		IMX_AUDMUX_V2_PTCR_RFSEL(MX31_AUDMUX_PORT4_SSI_PINS_4) |
+		IMX_AUDMUX_V2_PTCR_RCLKDIR |
+		IMX_AUDMUX_V2_PTCR_RCSEL(MX31_AUDMUX_PORT4_SSI_PINS_4),
+		IMX_AUDMUX_V2_PDCR_RXDSEL(MX31_AUDMUX_PORT4_SSI_PINS_4));
+
+	return ret;
+}
+
+static int __devexit imx_mc13783_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_card(&imx_mc13783);
+
+	return 0;
+}
+
+static struct platform_driver imx_mc13783_audio_driver = {
+	.driver = {
+		.name = "imx_mc13783",
+		.owner = THIS_MODULE,
+	},
+	.probe = imx_mc13783_probe,
+	.remove = __devexit_p(imx_mc13783_remove)
+};
+
+module_platform_driver(imx_mc13783_audio_driver);
+
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
+MODULE_AUTHOR("Philippe Retornaz <philippe.retornaz@epfl.ch");
+MODULE_DESCRIPTION("imx with mc13783 codec ALSA SoC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:imx_mc13783");
diff --git a/sound/soc/imx/imx-pcm-dma-mx2.c b/sound/soc/fsl/imx-pcm-dma.c
similarity index 96%
rename from sound/soc/imx/imx-pcm-dma-mx2.c
rename to sound/soc/fsl/imx-pcm-dma.c
index 6b818de..f3c0a5e 100644
--- a/sound/soc/imx/imx-pcm-dma-mx2.c
+++ b/sound/soc/fsl/imx-pcm-dma.c
@@ -109,7 +109,8 @@
 	dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
 
 	dma_data = kzalloc(sizeof(*dma_data), GFP_KERNEL);
-	dma_data->peripheral_type = IMX_DMATYPE_SSI;
+	dma_data->peripheral_type = dma_params->shared_peripheral ?
+					IMX_DMATYPE_SSI_SP : IMX_DMATYPE_SSI;
 	dma_data->priority = DMA_PRIO_HIGH;
 	dma_data->dma_request = dma_params->dma;
 
diff --git a/sound/soc/imx/imx-pcm-fiq.c b/sound/soc/fsl/imx-pcm-fiq.c
similarity index 100%
rename from sound/soc/imx/imx-pcm-fiq.c
rename to sound/soc/fsl/imx-pcm-fiq.c
diff --git a/sound/soc/imx/imx-pcm.c b/sound/soc/fsl/imx-pcm.c
similarity index 100%
rename from sound/soc/imx/imx-pcm.c
rename to sound/soc/fsl/imx-pcm.c
diff --git a/sound/soc/imx/imx-pcm.h b/sound/soc/fsl/imx-pcm.h
similarity index 93%
rename from sound/soc/imx/imx-pcm.h
rename to sound/soc/fsl/imx-pcm.h
index b5f5c3a..83c0ed7 100644
--- a/sound/soc/imx/imx-pcm.h
+++ b/sound/soc/fsl/imx-pcm.h
@@ -22,6 +22,7 @@
 	int dma;
 	unsigned long dma_addr;
 	int burstsize;
+	bool shared_peripheral;	/* The peripheral is on SPBA bus */
 };
 
 int snd_imx_pcm_mmap(struct snd_pcm_substream *substream,
diff --git a/sound/soc/fsl/imx-sgtl5000.c b/sound/soc/fsl/imx-sgtl5000.c
new file mode 100644
index 0000000..3a729ca
--- /dev/null
+++ b/sound/soc/fsl/imx-sgtl5000.c
@@ -0,0 +1,221 @@
+/*
+ * Copyright 2012 Freescale Semiconductor, Inc.
+ * Copyright 2012 Linaro Ltd.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_i2c.h>
+#include <linux/clk.h>
+#include <sound/soc.h>
+
+#include "../codecs/sgtl5000.h"
+#include "imx-audmux.h"
+
+#define DAI_NAME_SIZE	32
+
+struct imx_sgtl5000_data {
+	struct snd_soc_dai_link dai;
+	struct snd_soc_card card;
+	char codec_dai_name[DAI_NAME_SIZE];
+	char platform_name[DAI_NAME_SIZE];
+	struct clk *codec_clk;
+	unsigned int clk_frequency;
+};
+
+static int imx_sgtl5000_dai_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct imx_sgtl5000_data *data = container_of(rtd->card,
+					struct imx_sgtl5000_data, card);
+	struct device *dev = rtd->card->dev;
+	int ret;
+
+	ret = snd_soc_dai_set_sysclk(rtd->codec_dai, SGTL5000_SYSCLK,
+				     data->clk_frequency, SND_SOC_CLOCK_IN);
+	if (ret) {
+		dev_err(dev, "could not set codec driver clock params\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct snd_soc_dapm_widget imx_sgtl5000_dapm_widgets[] = {
+	SND_SOC_DAPM_MIC("Mic Jack", NULL),
+	SND_SOC_DAPM_LINE("Line In Jack", NULL),
+	SND_SOC_DAPM_HP("Headphone Jack", NULL),
+	SND_SOC_DAPM_SPK("Line Out Jack", NULL),
+	SND_SOC_DAPM_SPK("Ext Spk", NULL),
+};
+
+static int __devinit imx_sgtl5000_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct device_node *ssi_np, *codec_np;
+	struct platform_device *ssi_pdev;
+	struct i2c_client *codec_dev;
+	struct imx_sgtl5000_data *data;
+	int int_port, ext_port;
+	int ret;
+
+	ret = of_property_read_u32(np, "mux-int-port", &int_port);
+	if (ret) {
+		dev_err(&pdev->dev, "mux-int-port missing or invalid\n");
+		return ret;
+	}
+	ret = of_property_read_u32(np, "mux-ext-port", &ext_port);
+	if (ret) {
+		dev_err(&pdev->dev, "mux-ext-port missing or invalid\n");
+		return ret;
+	}
+
+	/*
+	 * The port numbering in the hardware manual starts at 1, while
+	 * the audmux API expects it starts at 0.
+	 */
+	int_port--;
+	ext_port--;
+	ret = imx_audmux_v2_configure_port(int_port,
+			IMX_AUDMUX_V2_PTCR_SYN |
+			IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) |
+			IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) |
+			IMX_AUDMUX_V2_PTCR_TFSDIR |
+			IMX_AUDMUX_V2_PTCR_TCLKDIR,
+			IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port));
+	if (ret) {
+		dev_err(&pdev->dev, "audmux internal port setup failed\n");
+		return ret;
+	}
+	imx_audmux_v2_configure_port(ext_port,
+			IMX_AUDMUX_V2_PTCR_SYN |
+			IMX_AUDMUX_V2_PTCR_TCSEL(int_port),
+			IMX_AUDMUX_V2_PDCR_RXDSEL(int_port));
+	if (ret) {
+		dev_err(&pdev->dev, "audmux external port setup failed\n");
+		return ret;
+	}
+
+	ssi_np = of_parse_phandle(pdev->dev.of_node, "ssi-controller", 0);
+	codec_np = of_parse_phandle(pdev->dev.of_node, "audio-codec", 0);
+	if (!ssi_np || !codec_np) {
+		dev_err(&pdev->dev, "phandle missing or invalid\n");
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	ssi_pdev = of_find_device_by_node(ssi_np);
+	if (!ssi_pdev) {
+		dev_err(&pdev->dev, "failed to find SSI platform device\n");
+		ret = -EINVAL;
+		goto fail;
+	}
+	codec_dev = of_find_i2c_device_by_node(codec_np);
+	if (!codec_dev) {
+		dev_err(&pdev->dev, "failed to find codec platform device\n");
+		return -EINVAL;
+	}
+
+	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+	if (!data) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	data->codec_clk = clk_get(&codec_dev->dev, NULL);
+	if (IS_ERR(data->codec_clk)) {
+		/* assuming clock enabled by default */
+		data->codec_clk = NULL;
+		ret = of_property_read_u32(codec_np, "clock-frequency",
+					&data->clk_frequency);
+		if (ret) {
+			dev_err(&codec_dev->dev,
+				"clock-frequency missing or invalid\n");
+			goto fail;
+		}
+	} else {
+		data->clk_frequency = clk_get_rate(data->codec_clk);
+		clk_prepare_enable(data->codec_clk);
+	}
+
+	data->dai.name = "HiFi";
+	data->dai.stream_name = "HiFi";
+	data->dai.codec_dai_name = "sgtl5000";
+	data->dai.codec_of_node = codec_np;
+	data->dai.cpu_dai_name = dev_name(&ssi_pdev->dev);
+	data->dai.platform_name = "imx-pcm-audio";
+	data->dai.init = &imx_sgtl5000_dai_init;
+	data->dai.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+			    SND_SOC_DAIFMT_CBM_CFM;
+
+	data->card.dev = &pdev->dev;
+	ret = snd_soc_of_parse_card_name(&data->card, "model");
+	if (ret)
+		goto clk_fail;
+	ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing");
+	if (ret)
+		goto clk_fail;
+	data->card.num_links = 1;
+	data->card.dai_link = &data->dai;
+	data->card.dapm_widgets = imx_sgtl5000_dapm_widgets;
+	data->card.num_dapm_widgets = ARRAY_SIZE(imx_sgtl5000_dapm_widgets);
+
+	ret = snd_soc_register_card(&data->card);
+	if (ret) {
+		dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
+		goto clk_fail;
+	}
+
+	platform_set_drvdata(pdev, data);
+clk_fail:
+	clk_put(data->codec_clk);
+fail:
+	if (ssi_np)
+		of_node_put(ssi_np);
+	if (codec_np)
+		of_node_put(codec_np);
+
+	return ret;
+}
+
+static int __devexit imx_sgtl5000_remove(struct platform_device *pdev)
+{
+	struct imx_sgtl5000_data *data = platform_get_drvdata(pdev);
+
+	if (data->codec_clk) {
+		clk_disable_unprepare(data->codec_clk);
+		clk_put(data->codec_clk);
+	}
+	snd_soc_unregister_card(&data->card);
+
+	return 0;
+}
+
+static const struct of_device_id imx_sgtl5000_dt_ids[] = {
+	{ .compatible = "fsl,imx-audio-sgtl5000", },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx_sgtl5000_dt_ids);
+
+static struct platform_driver imx_sgtl5000_driver = {
+	.driver = {
+		.name = "imx-sgtl5000",
+		.owner = THIS_MODULE,
+		.of_match_table = imx_sgtl5000_dt_ids,
+	},
+	.probe = imx_sgtl5000_probe,
+	.remove = __devexit_p(imx_sgtl5000_remove),
+};
+module_platform_driver(imx_sgtl5000_driver);
+
+MODULE_AUTHOR("Shawn Guo <shawn.guo@linaro.org>");
+MODULE_DESCRIPTION("Freescale i.MX SGTL5000 ASoC machine driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:imx-sgtl5000");
diff --git a/sound/soc/imx/imx-ssi.c b/sound/soc/fsl/imx-ssi.c
similarity index 99%
rename from sound/soc/imx/imx-ssi.c
rename to sound/soc/fsl/imx-ssi.c
index 4f81ed4..cf3ed03 100644
--- a/sound/soc/imx/imx-ssi.c
+++ b/sound/soc/fsl/imx-ssi.c
@@ -28,7 +28,7 @@
  * value. When we read the same register two times (and the register still
  * contains the same value) these status bits are not set. We work
  * around this by not polling these bits but only wait a fixed delay.
- * 
+ *
  */
 
 #include <linux/clk.h>
diff --git a/sound/soc/imx/imx-ssi.h b/sound/soc/fsl/imx-ssi.h
similarity index 100%
rename from sound/soc/imx/imx-ssi.h
rename to sound/soc/fsl/imx-ssi.h
diff --git a/sound/soc/fsl/mpc8610_hpcd.c b/sound/soc/fsl/mpc8610_hpcd.c
index 3fea5a1..60bcba1 100644
--- a/sound/soc/fsl/mpc8610_hpcd.c
+++ b/sound/soc/fsl/mpc8610_hpcd.c
@@ -14,18 +14,16 @@
 #include <linux/interrupt.h>
 #include <linux/of_device.h>
 #include <linux/slab.h>
-#include <linux/of_i2c.h>
 #include <sound/soc.h>
 #include <asm/fsl_guts.h>
 
 #include "fsl_dma.h"
 #include "fsl_ssi.h"
+#include "fsl_utils.h"
 
 /* There's only one global utilities register */
 static phys_addr_t guts_phys;
 
-#define DAI_NAME_SIZE	32
-
 /**
  * mpc8610_hpcd_data: machine-specific ASoC device data
  *
@@ -43,7 +41,6 @@
 	unsigned int dma_id[2];		/* 0 = DMA1, 1 = DMA2, etc */
 	unsigned int dma_channel_id[2]; /* 0 = ch 0, 1 = ch 1, etc*/
 	char codec_dai_name[DAI_NAME_SIZE];
-	char codec_name[DAI_NAME_SIZE];
 	char platform_name[2][DAI_NAME_SIZE]; /* One for each DMA channel */
 };
 
@@ -181,141 +178,6 @@
 };
 
 /**
- * get_node_by_phandle_name - get a node by its phandle name
- *
- * This function takes a node, the name of a property in that node, and a
- * compatible string.  Assuming the property is a phandle to another node,
- * it returns that node, (optionally) if that node is compatible.
- *
- * If the property is not a phandle, or the node it points to is not compatible
- * with the specific string, then NULL is returned.
- */
-static struct device_node *get_node_by_phandle_name(struct device_node *np,
-					       const char *name,
-					       const char *compatible)
-{
-	const phandle *ph;
-	int len;
-
-	ph = of_get_property(np, name, &len);
-	if (!ph || (len != sizeof(phandle)))
-		return NULL;
-
-	np = of_find_node_by_phandle(*ph);
-	if (!np)
-		return NULL;
-
-	if (compatible && !of_device_is_compatible(np, compatible)) {
-		of_node_put(np);
-		return NULL;
-	}
-
-	return np;
-}
-
-/**
- * get_parent_cell_index -- return the cell-index of the parent of a node
- *
- * Return the value of the cell-index property of the parent of the given
- * node.  This is used for DMA channel nodes that need to know the DMA ID
- * of the controller they are on.
- */
-static int get_parent_cell_index(struct device_node *np)
-{
-	struct device_node *parent = of_get_parent(np);
-	const u32 *iprop;
-
-	if (!parent)
-		return -1;
-
-	iprop = of_get_property(parent, "cell-index", NULL);
-	of_node_put(parent);
-
-	if (!iprop)
-		return -1;
-
-	return be32_to_cpup(iprop);
-}
-
-/**
- * codec_node_dev_name - determine the dev_name for a codec node
- *
- * This function determines the dev_name for an I2C node.  This is the name
- * that would be returned by dev_name() if this device_node were part of a
- * 'struct device'  It's ugly and hackish, but it works.
- *
- * The dev_name for such devices include the bus number and I2C address. For
- * example, "cs4270.0-004f".
- */
-static int codec_node_dev_name(struct device_node *np, char *buf, size_t len)
-{
-	const u32 *iprop;
-	int addr;
-	char temp[DAI_NAME_SIZE];
-	struct i2c_client *i2c;
-
-	of_modalias_node(np, temp, DAI_NAME_SIZE);
-
-	iprop = of_get_property(np, "reg", NULL);
-	if (!iprop)
-		return -EINVAL;
-
-	addr = be32_to_cpup(iprop);
-
-	/* We need the adapter number */
-	i2c = of_find_i2c_device_by_node(np);
-	if (!i2c)
-		return -ENODEV;
-
-	snprintf(buf, len, "%s.%u-%04x", temp, i2c->adapter->nr, addr);
-
-	return 0;
-}
-
-static int get_dma_channel(struct device_node *ssi_np,
-			   const char *name,
-			   struct snd_soc_dai_link *dai,
-			   unsigned int *dma_channel_id,
-			   unsigned int *dma_id)
-{
-	struct resource res;
-	struct device_node *dma_channel_np;
-	const u32 *iprop;
-	int ret;
-
-	dma_channel_np = get_node_by_phandle_name(ssi_np, name,
-						  "fsl,ssi-dma-channel");
-	if (!dma_channel_np)
-		return -EINVAL;
-
-	/* Determine the dev_name for the device_node.  This code mimics the
-	 * behavior of of_device_make_bus_id(). We need this because ASoC uses
-	 * the dev_name() of the device to match the platform (DMA) device with
-	 * the CPU (SSI) device.  It's all ugly and hackish, but it works (for
-	 * now).
-	 *
-	 * dai->platform name should already point to an allocated buffer.
-	 */
-	ret = of_address_to_resource(dma_channel_np, 0, &res);
-	if (ret)
-		return ret;
-	snprintf((char *)dai->platform_name, DAI_NAME_SIZE, "%llx.%s",
-		 (unsigned long long) res.start, dma_channel_np->name);
-
-	iprop = of_get_property(dma_channel_np, "cell-index", NULL);
-	if (!iprop) {
-		of_node_put(dma_channel_np);
-		return -EINVAL;
-	}
-
-	*dma_channel_id = be32_to_cpup(iprop);
-	*dma_id = get_parent_cell_index(dma_channel_np);
-	of_node_put(dma_channel_np);
-
-	return 0;
-}
-
-/**
  * mpc8610_hpcd_probe: platform probe function for the machine driver
  *
  * Although this is a machine driver, the SSI node is the "master" node with
@@ -352,16 +214,8 @@
 	machine_data->dai[0].cpu_dai_name = dev_name(&ssi_pdev->dev);
 	machine_data->dai[0].ops = &mpc8610_hpcd_ops;
 
-	/* Determine the codec name, it will be used as the codec DAI name */
-	ret = codec_node_dev_name(codec_np, machine_data->codec_name,
-				  DAI_NAME_SIZE);
-	if (ret) {
-		dev_err(&pdev->dev, "invalid codec node %s\n",
-			codec_np->full_name);
-		ret = -EINVAL;
-		goto error;
-	}
-	machine_data->dai[0].codec_name = machine_data->codec_name;
+	/* ASoC core can match codec with device node */
+	machine_data->dai[0].codec_of_node = codec_np;
 
 	/* The DAI name from the codec (snd_soc_dai_driver.name) */
 	machine_data->dai[0].codec_dai_name = "cs4270-hifi";
@@ -458,9 +312,10 @@
 
 	/* Find the playback DMA channel to use. */
 	machine_data->dai[0].platform_name = machine_data->platform_name[0];
-	ret = get_dma_channel(np, "fsl,playback-dma", &machine_data->dai[0],
-			      &machine_data->dma_channel_id[0],
-			      &machine_data->dma_id[0]);
+	ret = fsl_asoc_get_dma_channel(np, "fsl,playback-dma",
+				       &machine_data->dai[0],
+				       &machine_data->dma_channel_id[0],
+				       &machine_data->dma_id[0]);
 	if (ret) {
 		dev_err(&pdev->dev, "missing/invalid playback DMA phandle\n");
 		goto error;
@@ -468,9 +323,10 @@
 
 	/* Find the capture DMA channel to use. */
 	machine_data->dai[1].platform_name = machine_data->platform_name[1];
-	ret = get_dma_channel(np, "fsl,capture-dma", &machine_data->dai[1],
-			      &machine_data->dma_channel_id[1],
-			      &machine_data->dma_id[1]);
+	ret = fsl_asoc_get_dma_channel(np, "fsl,capture-dma",
+				       &machine_data->dai[1],
+				       &machine_data->dma_channel_id[1],
+				       &machine_data->dma_id[1]);
 	if (ret) {
 		dev_err(&pdev->dev, "missing/invalid capture DMA phandle\n");
 		goto error;
diff --git a/sound/soc/imx/mx27vis-aic32x4.c b/sound/soc/fsl/mx27vis-aic32x4.c
similarity index 100%
rename from sound/soc/imx/mx27vis-aic32x4.c
rename to sound/soc/fsl/mx27vis-aic32x4.c
diff --git a/sound/soc/fsl/p1022_ds.c b/sound/soc/fsl/p1022_ds.c
index 982a1c9..50adf40 100644
--- a/sound/soc/fsl/p1022_ds.c
+++ b/sound/soc/fsl/p1022_ds.c
@@ -14,12 +14,12 @@
 #include <linux/interrupt.h>
 #include <linux/of_device.h>
 #include <linux/slab.h>
-#include <linux/of_i2c.h>
 #include <sound/soc.h>
 #include <asm/fsl_guts.h>
 
 #include "fsl_dma.h"
 #include "fsl_ssi.h"
+#include "fsl_utils.h"
 
 /* P1022-specific PMUXCR and DMUXCR bit definitions */
 
@@ -57,8 +57,6 @@
 /* There's only one global utilities register */
 static phys_addr_t guts_phys;
 
-#define DAI_NAME_SIZE	32
-
 /**
  * machine_data: machine-specific ASoC device data
  *
@@ -75,7 +73,6 @@
 	unsigned int ssi_id;		/* 0 = SSI1, 1 = SSI2, etc */
 	unsigned int dma_id[2];		/* 0 = DMA1, 1 = DMA2, etc */
 	unsigned int dma_channel_id[2]; /* 0 = ch 0, 1 = ch 1, etc*/
-	char codec_name[DAI_NAME_SIZE];
 	char platform_name[2][DAI_NAME_SIZE]; /* One for each DMA channel */
 };
 
@@ -191,136 +188,6 @@
 };
 
 /**
- * get_node_by_phandle_name - get a node by its phandle name
- *
- * This function takes a node, the name of a property in that node, and a
- * compatible string.  Assuming the property is a phandle to another node,
- * it returns that node, (optionally) if that node is compatible.
- *
- * If the property is not a phandle, or the node it points to is not compatible
- * with the specific string, then NULL is returned.
- */
-static struct device_node *get_node_by_phandle_name(struct device_node *np,
-	const char *name, const char *compatible)
-{
-	np = of_parse_phandle(np, name, 0);
-	if (!np)
-		return NULL;
-
-	if (!of_device_is_compatible(np, compatible)) {
-		of_node_put(np);
-		return NULL;
-	}
-
-	return np;
-}
-
-/**
- * get_parent_cell_index -- return the cell-index of the parent of a node
- *
- * Return the value of the cell-index property of the parent of the given
- * node.  This is used for DMA channel nodes that need to know the DMA ID
- * of the controller they are on.
- */
-static int get_parent_cell_index(struct device_node *np)
-{
-	struct device_node *parent = of_get_parent(np);
-	const u32 *iprop;
-	int ret = -1;
-
-	if (!parent)
-		return -1;
-
-	iprop = of_get_property(parent, "cell-index", NULL);
-	if (iprop)
-		ret = be32_to_cpup(iprop);
-
-	of_node_put(parent);
-
-	return ret;
-}
-
-/**
- * codec_node_dev_name - determine the dev_name for a codec node
- *
- * This function determines the dev_name for an I2C node.  This is the name
- * that would be returned by dev_name() if this device_node were part of a
- * 'struct device'  It's ugly and hackish, but it works.
- *
- * The dev_name for such devices include the bus number and I2C address. For
- * example, "cs4270-codec.0-004f".
- */
-static int codec_node_dev_name(struct device_node *np, char *buf, size_t len)
-{
-	const u32 *iprop;
-	int addr;
-	char temp[DAI_NAME_SIZE];
-	struct i2c_client *i2c;
-
-	of_modalias_node(np, temp, DAI_NAME_SIZE);
-
-	iprop = of_get_property(np, "reg", NULL);
-	if (!iprop)
-		return -EINVAL;
-
-	addr = be32_to_cpup(iprop);
-
-	/* We need the adapter number */
-	i2c = of_find_i2c_device_by_node(np);
-	if (!i2c)
-		return -ENODEV;
-
-	snprintf(buf, len, "%s.%u-%04x", temp, i2c->adapter->nr, addr);
-
-	return 0;
-}
-
-static int get_dma_channel(struct device_node *ssi_np,
-			   const char *name,
-			   struct snd_soc_dai_link *dai,
-			   unsigned int *dma_channel_id,
-			   unsigned int *dma_id)
-{
-	struct resource res;
-	struct device_node *dma_channel_np;
-	const u32 *iprop;
-	int ret;
-
-	dma_channel_np = get_node_by_phandle_name(ssi_np, name,
-						  "fsl,ssi-dma-channel");
-	if (!dma_channel_np)
-		return -EINVAL;
-
-	/* Determine the dev_name for the device_node.  This code mimics the
-	 * behavior of of_device_make_bus_id(). We need this because ASoC uses
-	 * the dev_name() of the device to match the platform (DMA) device with
-	 * the CPU (SSI) device.  It's all ugly and hackish, but it works (for
-	 * now).
-	 *
-	 * dai->platform name should already point to an allocated buffer.
-	 */
-	ret = of_address_to_resource(dma_channel_np, 0, &res);
-	if (ret) {
-		of_node_put(dma_channel_np);
-		return ret;
-	}
-	snprintf((char *)dai->platform_name, DAI_NAME_SIZE, "%llx.%s",
-		 (unsigned long long) res.start, dma_channel_np->name);
-
-	iprop = of_get_property(dma_channel_np, "cell-index", NULL);
-	if (!iprop) {
-		of_node_put(dma_channel_np);
-		return -EINVAL;
-	}
-
-	*dma_channel_id = be32_to_cpup(iprop);
-	*dma_id = get_parent_cell_index(dma_channel_np);
-	of_node_put(dma_channel_np);
-
-	return 0;
-}
-
-/**
  * p1022_ds_probe: platform probe function for the machine driver
  *
  * Although this is a machine driver, the SSI node is the "master" node with
@@ -357,15 +224,8 @@
 	mdata->dai[0].cpu_dai_name = dev_name(&ssi_pdev->dev);
 	mdata->dai[0].ops = &p1022_ds_ops;
 
-	/* Determine the codec name, it will be used as the codec DAI name */
-	ret = codec_node_dev_name(codec_np, mdata->codec_name, DAI_NAME_SIZE);
-	if (ret) {
-		dev_err(&pdev->dev, "invalid codec node %s\n",
-			codec_np->full_name);
-		ret = -EINVAL;
-		goto error;
-	}
-	mdata->dai[0].codec_name = mdata->codec_name;
+	/* ASoC core can match codec with device node */
+	mdata->dai[0].codec_of_node = codec_np;
 
 	/* We register two DAIs per SSI, one for playback and the other for
 	 * capture.  We support codecs that have separate DAIs for both playback
@@ -462,9 +322,9 @@
 
 	/* Find the playback DMA channel to use. */
 	mdata->dai[0].platform_name = mdata->platform_name[0];
-	ret = get_dma_channel(np, "fsl,playback-dma", &mdata->dai[0],
-			      &mdata->dma_channel_id[0],
-			      &mdata->dma_id[0]);
+	ret = fsl_asoc_get_dma_channel(np, "fsl,playback-dma", &mdata->dai[0],
+				       &mdata->dma_channel_id[0],
+				       &mdata->dma_id[0]);
 	if (ret) {
 		dev_err(&pdev->dev, "missing/invalid playback DMA phandle\n");
 		goto error;
@@ -472,9 +332,9 @@
 
 	/* Find the capture DMA channel to use. */
 	mdata->dai[1].platform_name = mdata->platform_name[1];
-	ret = get_dma_channel(np, "fsl,capture-dma", &mdata->dai[1],
-			      &mdata->dma_channel_id[1],
-			      &mdata->dma_id[1]);
+	ret = fsl_asoc_get_dma_channel(np, "fsl,capture-dma", &mdata->dai[1],
+				       &mdata->dma_channel_id[1],
+				       &mdata->dma_id[1]);
 	if (ret) {
 		dev_err(&pdev->dev, "missing/invalid capture DMA phandle\n");
 		goto error;
diff --git a/sound/soc/imx/phycore-ac97.c b/sound/soc/fsl/phycore-ac97.c
similarity index 100%
rename from sound/soc/imx/phycore-ac97.c
rename to sound/soc/fsl/phycore-ac97.c
diff --git a/sound/soc/imx/wm1133-ev1.c b/sound/soc/fsl/wm1133-ev1.c
similarity index 100%
rename from sound/soc/imx/wm1133-ev1.c
rename to sound/soc/fsl/wm1133-ev1.c
diff --git a/sound/soc/generic/Kconfig b/sound/soc/generic/Kconfig
new file mode 100644
index 0000000..610f612
--- /dev/null
+++ b/sound/soc/generic/Kconfig
@@ -0,0 +1,4 @@
+config SND_SIMPLE_CARD
+	tristate "ASoC Simple sound card support"
+	help
+	  This option enables generic simple sound card support
diff --git a/sound/soc/generic/Makefile b/sound/soc/generic/Makefile
new file mode 100644
index 0000000..9c3b246
--- /dev/null
+++ b/sound/soc/generic/Makefile
@@ -0,0 +1,3 @@
+snd-soc-simple-card-objs	:= simple-card.o
+
+obj-$(CONFIG_SND_SIMPLE_CARD)	+= snd-soc-simple-card.o
diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c
new file mode 100644
index 0000000..b4b4cab
--- /dev/null
+++ b/sound/soc/generic/simple-card.c
@@ -0,0 +1,114 @@
+/*
+ * ASoC simple sound card support
+ *
+ * Copyright (C) 2012 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * 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.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <sound/simple_card.h>
+
+#define asoc_simple_get_card_info(p) \
+	container_of(p->dai_link, struct asoc_simple_card_info, snd_link)
+
+static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct asoc_simple_card_info *cinfo = asoc_simple_get_card_info(rtd);
+	struct asoc_simple_dai_init_info *iinfo = cinfo->init;
+	struct snd_soc_dai *codec = rtd->codec_dai;
+	struct snd_soc_dai *cpu = rtd->cpu_dai;
+	unsigned int cpu_daifmt = iinfo->fmt | iinfo->cpu_daifmt;
+	unsigned int codec_daifmt = iinfo->fmt | iinfo->codec_daifmt;
+	int ret;
+
+	if (codec_daifmt) {
+		ret = snd_soc_dai_set_fmt(codec, codec_daifmt);
+		if (ret < 0)
+			return ret;
+	}
+
+	if (iinfo->sysclk) {
+		ret = snd_soc_dai_set_sysclk(codec, 0, iinfo->sysclk, 0);
+		if (ret < 0)
+			return ret;
+	}
+
+	if (cpu_daifmt) {
+		ret = snd_soc_dai_set_fmt(cpu, cpu_daifmt);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int asoc_simple_card_probe(struct platform_device *pdev)
+{
+	struct asoc_simple_card_info *cinfo = pdev->dev.platform_data;
+
+	if (!cinfo) {
+		dev_err(&pdev->dev, "no info for asoc-simple-card\n");
+		return -EINVAL;
+	}
+
+	if (!cinfo->name	||
+	    !cinfo->card	||
+	    !cinfo->cpu_dai	||
+	    !cinfo->codec	||
+	    !cinfo->platform	||
+	    !cinfo->codec_dai) {
+		dev_err(&pdev->dev, "insufficient asoc_simple_card_info settings\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * init snd_soc_dai_link
+	 */
+	cinfo->snd_link.name		= cinfo->name;
+	cinfo->snd_link.stream_name	= cinfo->name;
+	cinfo->snd_link.cpu_dai_name	= cinfo->cpu_dai;
+	cinfo->snd_link.platform_name	= cinfo->platform;
+	cinfo->snd_link.codec_name	= cinfo->codec;
+	cinfo->snd_link.codec_dai_name	= cinfo->codec_dai;
+
+	/* enable snd_link.init if cinfo has settings */
+	if (cinfo->init)
+		cinfo->snd_link.init	= asoc_simple_card_dai_init;
+
+	/*
+	 * init snd_soc_card
+	 */
+	cinfo->snd_card.name		= cinfo->card;
+	cinfo->snd_card.owner		= THIS_MODULE;
+	cinfo->snd_card.dai_link	= &cinfo->snd_link;
+	cinfo->snd_card.num_links	= 1;
+	cinfo->snd_card.dev		= &pdev->dev;
+
+	return snd_soc_register_card(&cinfo->snd_card);
+}
+
+static int asoc_simple_card_remove(struct platform_device *pdev)
+{
+	struct asoc_simple_card_info *cinfo = pdev->dev.platform_data;
+
+	return snd_soc_unregister_card(&cinfo->snd_card);
+}
+
+static struct platform_driver asoc_simple_card = {
+	.driver = {
+		.name	= "asoc-simple-card",
+	},
+	.probe		= asoc_simple_card_probe,
+	.remove		= asoc_simple_card_remove,
+};
+
+module_platform_driver(asoc_simple_card);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("ASoC Simple Sound Card");
+MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
diff --git a/sound/soc/imx/Kconfig b/sound/soc/imx/Kconfig
deleted file mode 100644
index d83e5d0..0000000
--- a/sound/soc/imx/Kconfig
+++ /dev/null
@@ -1,79 +0,0 @@
-menuconfig SND_IMX_SOC
-	tristate "SoC Audio for Freescale i.MX CPUs"
-	depends on ARCH_MXC
-	help
-	  Say Y or M if you want to add support for codecs attached to
-	  the i.MX SSI interface.
-
-
-if SND_IMX_SOC
-
-config SND_SOC_IMX_SSI
-	tristate
-
-config SND_SOC_IMX_PCM
-	tristate
-
-config SND_MXC_SOC_FIQ
-	tristate
-	select FIQ
-	select SND_SOC_IMX_PCM
-
-config SND_MXC_SOC_MX2
-	select SND_SOC_DMAENGINE_PCM
-	tristate
-	select SND_SOC_IMX_PCM
-
-config SND_SOC_IMX_AUDMUX
-	tristate
-
-config SND_MXC_SOC_WM1133_EV1
-	tristate "Audio on the i.MX31ADS with WM1133-EV1 fitted"
-	depends on MACH_MX31ADS_WM1133_EV1 && EXPERIMENTAL
-	select SND_SOC_WM8350
-	select SND_MXC_SOC_FIQ
-	select SND_SOC_IMX_AUDMUX
-	select SND_SOC_IMX_SSI
-	help
-	  Enable support for audio on the i.MX31ADS with the WM1133-EV1
-	  PMIC board with WM8835x fitted.
-
-config SND_SOC_MX27VIS_AIC32X4
-	tristate "SoC audio support for Visstrim M10 boards"
-	depends on MACH_IMX27_VISSTRIM_M10 && I2C
-	select SND_SOC_TLV320AIC32X4
-	select SND_MXC_SOC_MX2
-	select SND_SOC_IMX_AUDMUX
-	select SND_SOC_IMX_SSI
-	help
-	  Say Y if you want to add support for SoC audio on Visstrim SM10
-	  board with TLV320AIC32X4 codec.
-
-config SND_SOC_PHYCORE_AC97
-	tristate "SoC Audio support for Phytec phyCORE (and phyCARD) boards"
-	depends on MACH_PCM043 || MACH_PCA100
-	select SND_SOC_AC97_BUS
-	select SND_SOC_WM9712
-	select SND_MXC_SOC_FIQ
-	select SND_SOC_IMX_AUDMUX
-	select SND_SOC_IMX_SSI
-	help
-	  Say Y if you want to add support for SoC audio on Phytec phyCORE
-	  and phyCARD boards in AC97 mode
-
-config SND_SOC_EUKREA_TLV320
-	tristate "Eukrea TLV320"
-	depends on MACH_EUKREA_MBIMX27_BASEBOARD \
-		|| MACH_EUKREA_MBIMXSD25_BASEBOARD \
-		|| MACH_EUKREA_MBIMXSD35_BASEBOARD \
-		|| MACH_EUKREA_MBIMXSD51_BASEBOARD
-	depends on I2C
-	select SND_SOC_TLV320AIC23
-	select SND_MXC_SOC_FIQ
-	select SND_SOC_IMX_AUDMUX
-	select SND_SOC_IMX_SSI
-	help
-	  Enable I2S based access to the TLV320AIC23B codec attached
-	  to the SSI interface
-
-endif	# SND_IMX_SOC
diff --git a/sound/soc/imx/Makefile b/sound/soc/imx/Makefile
deleted file mode 100644
index f5db3e9..0000000
--- a/sound/soc/imx/Makefile
+++ /dev/null
@@ -1,22 +0,0 @@
-# i.MX Platform Support
-snd-soc-imx-ssi-objs := imx-ssi.o
-snd-soc-imx-audmux-objs := imx-audmux.o
-
-obj-$(CONFIG_SND_SOC_IMX_SSI) += snd-soc-imx-ssi.o
-obj-$(CONFIG_SND_SOC_IMX_AUDMUX) += snd-soc-imx-audmux.o
-
-obj-$(CONFIG_SND_SOC_IMX_PCM) += snd-soc-imx-pcm.o
-snd-soc-imx-pcm-y := imx-pcm.o
-snd-soc-imx-pcm-$(CONFIG_SND_MXC_SOC_FIQ) += imx-pcm-fiq.o
-snd-soc-imx-pcm-$(CONFIG_SND_MXC_SOC_MX2) += imx-pcm-dma-mx2.o
-
-# i.MX Machine Support
-snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o
-snd-soc-phycore-ac97-objs := phycore-ac97.o
-snd-soc-mx27vis-aic32x4-objs := mx27vis-aic32x4.o
-snd-soc-wm1133-ev1-objs := wm1133-ev1.o
-
-obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o
-obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o
-obj-$(CONFIG_SND_SOC_MX27VIS_AIC32X4) += snd-soc-mx27vis-aic32x4.o
-obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o
diff --git a/sound/soc/jz4740/jz4740-i2s.c b/sound/soc/jz4740/jz4740-i2s.c
index a5af7c4..4134967 100644
--- a/sound/soc/jz4740/jz4740-i2s.c
+++ b/sound/soc/jz4740/jz4740-i2s.c
@@ -346,7 +346,7 @@
 
 	/* Playback */
 	dma_config = &i2s->pcm_config_playback.dma_config;
-	dma_config->src_width = JZ4740_DMA_WIDTH_32BIT,
+	dma_config->src_width = JZ4740_DMA_WIDTH_32BIT;
 	dma_config->transfer_size = JZ4740_DMA_TRANSFER_SIZE_16BYTE;
 	dma_config->request_type = JZ4740_DMA_TYPE_AIC_TRANSMIT;
 	dma_config->flags = JZ4740_DMA_SRC_AUTOINC;
@@ -355,7 +355,7 @@
 
 	/* Capture */
 	dma_config = &i2s->pcm_config_capture.dma_config;
-	dma_config->dst_width = JZ4740_DMA_WIDTH_32BIT,
+	dma_config->dst_width = JZ4740_DMA_WIDTH_32BIT;
 	dma_config->transfer_size = JZ4740_DMA_TRANSFER_SIZE_16BYTE;
 	dma_config->request_type = JZ4740_DMA_TYPE_AIC_RECEIVE;
 	dma_config->flags = JZ4740_DMA_DST_AUTOINC;
diff --git a/sound/soc/mxs/mxs-pcm.c b/sound/soc/mxs/mxs-pcm.c
index e373fbb..373dec9 100644
--- a/sound/soc/mxs/mxs-pcm.c
+++ b/sound/soc/mxs/mxs-pcm.c
@@ -220,28 +220,16 @@
 	.pcm_free	= mxs_pcm_free,
 };
 
-static int __devinit mxs_soc_platform_probe(struct platform_device *pdev)
+int __devinit mxs_pcm_platform_register(struct device *dev)
 {
-	return snd_soc_register_platform(&pdev->dev, &mxs_soc_platform);
+	return snd_soc_register_platform(dev, &mxs_soc_platform);
 }
+EXPORT_SYMBOL_GPL(mxs_pcm_platform_register);
 
-static int __devexit mxs_soc_platform_remove(struct platform_device *pdev)
+void __devexit mxs_pcm_platform_unregister(struct device *dev)
 {
-	snd_soc_unregister_platform(&pdev->dev);
-
-	return 0;
+	snd_soc_unregister_platform(dev);
 }
-
-static struct platform_driver mxs_pcm_driver = {
-	.driver = {
-		.name = "mxs-pcm-audio",
-		.owner = THIS_MODULE,
-	},
-	.probe = mxs_soc_platform_probe,
-	.remove = __devexit_p(mxs_soc_platform_remove),
-};
-
-module_platform_driver(mxs_pcm_driver);
+EXPORT_SYMBOL_GPL(mxs_pcm_platform_unregister);
 
 MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:mxs-pcm-audio");
diff --git a/sound/soc/mxs/mxs-pcm.h b/sound/soc/mxs/mxs-pcm.h
index 5f01a91..35ba2ca 100644
--- a/sound/soc/mxs/mxs-pcm.h
+++ b/sound/soc/mxs/mxs-pcm.h
@@ -24,4 +24,7 @@
 	int chan_num;
 };
 
+int mxs_pcm_platform_register(struct device *dev);
+void mxs_pcm_platform_unregister(struct device *dev);
+
 #endif
diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c
index 7fd224b..aba71bf 100644
--- a/sound/soc/mxs/mxs-saif.c
+++ b/sound/soc/mxs/mxs-saif.c
@@ -18,6 +18,8 @@
 
 #include <linux/module.h>
 #include <linux/init.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/dma-mapping.h>
@@ -621,37 +623,57 @@
 	return IRQ_HANDLED;
 }
 
-static int mxs_saif_probe(struct platform_device *pdev)
+static int __devinit mxs_saif_probe(struct platform_device *pdev)
 {
+	struct device_node *np = pdev->dev.of_node;
 	struct resource *iores, *dmares;
 	struct mxs_saif *saif;
 	struct mxs_saif_platform_data *pdata;
 	struct pinctrl *pinctrl;
 	int ret = 0;
 
-	if (pdev->id >= ARRAY_SIZE(mxs_saif))
+
+	if (!np && pdev->id >= ARRAY_SIZE(mxs_saif))
 		return -EINVAL;
 
 	saif = devm_kzalloc(&pdev->dev, sizeof(*saif), GFP_KERNEL);
 	if (!saif)
 		return -ENOMEM;
 
-	mxs_saif[pdev->id] = saif;
-	saif->id = pdev->id;
-
-	pdata = pdev->dev.platform_data;
-	if (pdata && !pdata->master_mode) {
-		saif->master_id = pdata->master_id;
-		if (saif->master_id < 0 ||
-			saif->master_id >= ARRAY_SIZE(mxs_saif) ||
-			saif->master_id == saif->id) {
-			dev_err(&pdev->dev, "get wrong master id\n");
-			return -EINVAL;
+	if (np) {
+		struct device_node *master;
+		saif->id = of_alias_get_id(np, "saif");
+		if (saif->id < 0)
+			return saif->id;
+		/*
+		 * If there is no "fsl,saif-master" phandle, it's a saif
+		 * master.  Otherwise, it's a slave and its phandle points
+		 * to the master.
+		 */
+		master = of_parse_phandle(np, "fsl,saif-master", 0);
+		if (!master) {
+			saif->master_id = saif->id;
+		} else {
+			saif->master_id = of_alias_get_id(master, "saif");
+			if (saif->master_id < 0)
+				return saif->master_id;
 		}
 	} else {
-		saif->master_id = saif->id;
+		saif->id = pdev->id;
+		pdata = pdev->dev.platform_data;
+		if (pdata && !pdata->master_mode)
+			saif->master_id = pdata->master_id;
+		else
+			saif->master_id = saif->id;
 	}
 
+	if (saif->master_id < 0 || saif->master_id >= ARRAY_SIZE(mxs_saif)) {
+		dev_err(&pdev->dev, "get wrong master id\n");
+		return -EINVAL;
+	}
+
+	mxs_saif[saif->id] = saif;
+
 	pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
 	if (IS_ERR(pinctrl)) {
 		ret = PTR_ERR(pinctrl);
@@ -677,12 +699,19 @@
 
 	dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0);
 	if (!dmares) {
-		ret = -ENODEV;
-		dev_err(&pdev->dev, "failed to get dma resource: %d\n",
-			ret);
-		goto failed_get_resource;
+		/*
+		 * TODO: This is a temporary solution and should be changed
+		 * to use generic DMA binding later when the helplers get in.
+		 */
+		ret = of_property_read_u32(np, "fsl,saif-dma-channel",
+					   &saif->dma_param.chan_num);
+		if (ret) {
+			dev_err(&pdev->dev, "failed to get dma channel\n");
+			goto failed_get_resource;
+		}
+	} else {
+		saif->dma_param.chan_num = dmares->start;
 	}
-	saif->dma_param.chan_num = dmares->start;
 
 	saif->irq = platform_get_irq(pdev, 0);
 	if (saif->irq < 0) {
@@ -716,24 +745,14 @@
 		goto failed_get_resource;
 	}
 
-	saif->soc_platform_pdev = platform_device_alloc(
-					"mxs-pcm-audio", pdev->id);
-	if (!saif->soc_platform_pdev) {
-		ret = -ENOMEM;
-		goto failed_pdev_alloc;
-	}
-
-	platform_set_drvdata(saif->soc_platform_pdev, saif);
-	ret = platform_device_add(saif->soc_platform_pdev);
+	ret = mxs_pcm_platform_register(&pdev->dev);
 	if (ret) {
-		dev_err(&pdev->dev, "failed to add soc platform device\n");
-		goto failed_pdev_add;
+		dev_err(&pdev->dev, "register PCM failed: %d\n", ret);
+		goto failed_pdev_alloc;
 	}
 
 	return 0;
 
-failed_pdev_add:
-	platform_device_put(saif->soc_platform_pdev);
 failed_pdev_alloc:
 	snd_soc_unregister_dai(&pdev->dev);
 failed_get_resource:
@@ -746,13 +765,19 @@
 {
 	struct mxs_saif *saif = platform_get_drvdata(pdev);
 
-	platform_device_unregister(saif->soc_platform_pdev);
+	mxs_pcm_platform_unregister(&pdev->dev);
 	snd_soc_unregister_dai(&pdev->dev);
 	clk_put(saif->clk);
 
 	return 0;
 }
 
+static const struct of_device_id mxs_saif_dt_ids[] = {
+	{ .compatible = "fsl,imx28-saif", },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mxs_saif_dt_ids);
+
 static struct platform_driver mxs_saif_driver = {
 	.probe = mxs_saif_probe,
 	.remove = __devexit_p(mxs_saif_remove),
@@ -760,6 +785,7 @@
 	.driver = {
 		.name = "mxs-saif",
 		.owner = THIS_MODULE,
+		.of_match_table = mxs_saif_dt_ids,
 	},
 };
 
diff --git a/sound/soc/mxs/mxs-saif.h b/sound/soc/mxs/mxs-saif.h
index 12c91e4..3cb342e 100644
--- a/sound/soc/mxs/mxs-saif.h
+++ b/sound/soc/mxs/mxs-saif.h
@@ -123,7 +123,6 @@
 	unsigned int cur_rate;
 	unsigned int ongoing;
 
-	struct platform_device *soc_platform_pdev;
 	u32 fifo_underrun;
 	u32 fifo_overrun;
 };
diff --git a/sound/soc/mxs/mxs-sgtl5000.c b/sound/soc/mxs/mxs-sgtl5000.c
index 60f052b..3e6e876 100644
--- a/sound/soc/mxs/mxs-sgtl5000.c
+++ b/sound/soc/mxs/mxs-sgtl5000.c
@@ -18,6 +18,8 @@
 
 #include <linux/module.h>
 #include <linux/device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/soc.h>
@@ -90,7 +92,7 @@
 		.codec_dai_name	= "sgtl5000",
 		.codec_name	= "sgtl5000.0-000a",
 		.cpu_dai_name	= "mxs-saif.0",
-		.platform_name	= "mxs-pcm-audio.0",
+		.platform_name	= "mxs-saif.0",
 		.ops		= &mxs_sgtl5000_hifi_ops,
 	}, {
 		.name		= "HiFi Rx",
@@ -98,7 +100,7 @@
 		.codec_dai_name	= "sgtl5000",
 		.codec_name	= "sgtl5000.0-000a",
 		.cpu_dai_name	= "mxs-saif.1",
-		.platform_name	= "mxs-pcm-audio.1",
+		.platform_name	= "mxs-saif.1",
 		.ops		= &mxs_sgtl5000_hifi_ops,
 	},
 };
@@ -110,11 +112,48 @@
 	.num_links	= ARRAY_SIZE(mxs_sgtl5000_dai),
 };
 
+static int __devinit mxs_sgtl5000_probe_dt(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct device_node *saif_np[2], *codec_np;
+	int i, ret = 0;
+
+	if (!np)
+		return 1; /* no device tree */
+
+	saif_np[0] = of_parse_phandle(np, "saif-controllers", 0);
+	saif_np[1] = of_parse_phandle(np, "saif-controllers", 1);
+	codec_np = of_parse_phandle(np, "audio-codec", 0);
+	if (!saif_np[0] || !saif_np[1] || !codec_np) {
+		dev_err(&pdev->dev, "phandle missing or invalid\n");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < 2; i++) {
+		mxs_sgtl5000_dai[i].codec_name = NULL;
+		mxs_sgtl5000_dai[i].codec_of_node = codec_np;
+		mxs_sgtl5000_dai[i].cpu_dai_name = NULL;
+		mxs_sgtl5000_dai[i].cpu_dai_of_node = saif_np[i];
+		mxs_sgtl5000_dai[i].platform_name = NULL;
+		mxs_sgtl5000_dai[i].platform_of_node = saif_np[i];
+	}
+
+	of_node_put(codec_np);
+	of_node_put(saif_np[0]);
+	of_node_put(saif_np[1]);
+
+	return ret;
+}
+
 static int __devinit mxs_sgtl5000_probe(struct platform_device *pdev)
 {
 	struct snd_soc_card *card = &mxs_sgtl5000;
 	int ret;
 
+	ret = mxs_sgtl5000_probe_dt(pdev);
+	if (ret < 0)
+		return ret;
+
 	/*
 	 * Set an init clock(11.28Mhz) for sgtl5000 initialization(i2c r/w).
 	 * The Sgtl5000 sysclk is derived from saif0 mclk and it's range
@@ -148,10 +187,17 @@
 	return 0;
 }
 
+static const struct of_device_id mxs_sgtl5000_dt_ids[] = {
+	{ .compatible = "fsl,mxs-audio-sgtl5000", },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mxs_sgtl5000_dt_ids);
+
 static struct platform_driver mxs_sgtl5000_audio_driver = {
 	.driver = {
 		.name = "mxs-sgtl5000",
 		.owner = THIS_MODULE,
+		.of_match_table = mxs_sgtl5000_dt_ids,
 	},
 	.probe = mxs_sgtl5000_probe,
 	.remove = __devexit_p(mxs_sgtl5000_remove),
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig
index deafbfa..9ccfa5e 100644
--- a/sound/soc/omap/Kconfig
+++ b/sound/soc/omap/Kconfig
@@ -113,6 +113,7 @@
 	tristate "SoC Audio support for Texas Instruments OMAP4 HDMI"
 	depends on SND_OMAP_SOC && OMAP4_DSS_HDMI && OMAP2_DSS && ARCH_OMAP4
 	select SND_OMAP_SOC_HDMI
+	select SND_SOC_OMAP_HDMI_CODEC
 	help
 	  Say Y if you want to add support for SoC HDMI audio on Texas Instruments
 	  OMAP4 chips
diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c
index fd04ce1..1c2aa7f 100644
--- a/sound/soc/pxa/pxa-ssp.c
+++ b/sound/soc/pxa/pxa-ssp.c
@@ -85,14 +85,12 @@
 	char name[20];
 };
 
-static struct pxa2xx_pcm_dma_params *
-pxa_ssp_get_dma_params(struct ssp_device *ssp, int width4, int out)
+static void pxa_ssp_set_dma_params(struct ssp_device *ssp, int width4,
+			int out, struct pxa2xx_pcm_dma_params *dma_data)
 {
 	struct pxa2xx_pcm_dma_data *dma;
 
-	dma = kzalloc(sizeof(struct pxa2xx_pcm_dma_data), GFP_KERNEL);
-	if (dma == NULL)
-		return NULL;
+	dma = container_of(dma_data, struct pxa2xx_pcm_dma_data, params);
 
 	snprintf(dma->name, 20, "SSP%d PCM %s %s", ssp->port_id,
 			width4 ? "32-bit" : "16-bit", out ? "out" : "in");
@@ -103,8 +101,6 @@
 				  (DCMD_INCTRGADDR | DCMD_FLOWSRC)) |
 			(width4 ? DCMD_WIDTH4 : DCMD_WIDTH2) | DCMD_BURST16;
 	dma->params.dev_addr = ssp->phys_base + SSDR;
-
-	return &dma->params;
 }
 
 static int pxa_ssp_startup(struct snd_pcm_substream *substream,
@@ -112,6 +108,7 @@
 {
 	struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai);
 	struct ssp_device *ssp = priv->ssp;
+	struct pxa2xx_pcm_dma_data *dma;
 	int ret = 0;
 
 	if (!cpu_dai->active) {
@@ -119,8 +116,10 @@
 		pxa_ssp_disable(ssp);
 	}
 
-	kfree(snd_soc_dai_get_dma_data(cpu_dai, substream));
-	snd_soc_dai_set_dma_data(cpu_dai, substream, NULL);
+	dma = kzalloc(sizeof(struct pxa2xx_pcm_dma_data), GFP_KERNEL);
+	if (!dma)
+		return -ENOMEM;
+	snd_soc_dai_set_dma_data(cpu_dai, substream, &dma->params);
 
 	return ret;
 }
@@ -573,18 +572,13 @@
 
 	dma_data = snd_soc_dai_get_dma_data(cpu_dai, substream);
 
-	/* generate correct DMA params */
-	kfree(dma_data);
-
 	/* Network mode with one active slot (ttsa == 1) can be used
 	 * to force 16-bit frame width on the wire (for S16_LE), even
 	 * with two channels. Use 16-bit DMA transfers for this case.
 	 */
-	dma_data = pxa_ssp_get_dma_params(ssp,
-			((chn == 2) && (ttsa != 1)) || (width == 32),
-			substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
-
-	snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data);
+	pxa_ssp_set_dma_params(ssp,
+		((chn == 2) && (ttsa != 1)) || (width == 32),
+		substream->stream == SNDRV_PCM_STREAM_PLAYBACK, dma_data);
 
 	/* we can only change the settings if the port is not in use */
 	if (pxa_ssp_read_reg(ssp, SSCR0) & SSCR0_SSE)
diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c
index d085837..3075a42 100644
--- a/sound/soc/pxa/pxa2xx-i2s.c
+++ b/sound/soc/pxa/pxa2xx-i2s.c
@@ -166,7 +166,7 @@
 	struct pxa2xx_pcm_dma_params *dma_data;
 
 	BUG_ON(IS_ERR(clk_i2s));
-	clk_enable(clk_i2s);
+	clk_prepare_enable(clk_i2s);
 	clk_ena = 1;
 	pxa_i2s_wait();
 
@@ -259,7 +259,7 @@
 		SACR0 &= ~SACR0_ENB;
 		pxa_i2s_wait();
 		if (clk_ena) {
-			clk_disable(clk_i2s);
+			clk_disable_unprepare(clk_i2s);
 			clk_ena = 0;
 		}
 	}
diff --git a/sound/soc/samsung/littlemill.c b/sound/soc/samsung/littlemill.c
index e741685..c82c646 100644
--- a/sound/soc/samsung/littlemill.c
+++ b/sound/soc/samsung/littlemill.c
@@ -23,10 +23,10 @@
 					  struct snd_soc_dapm_context *dapm,
 					  enum snd_soc_bias_level level)
 {
-	struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
+	struct snd_soc_dai *aif1_dai = card->rtd[0].codec_dai;
 	int ret;
 
-	if (dapm->dev != codec_dai->dev)
+	if (dapm->dev != aif1_dai->dev)
 		return 0;
 
 	switch (level) {
@@ -36,7 +36,7 @@
 		 * then do so now, otherwise these are noops.
 		 */
 		if (dapm->bias_level == SND_SOC_BIAS_STANDBY) {
-			ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL1,
+			ret = snd_soc_dai_set_pll(aif1_dai, WM8994_FLL1,
 						  WM8994_FLL_SRC_MCLK2, 32768,
 						  sample_rate * 512);
 			if (ret < 0) {
@@ -44,7 +44,7 @@
 				return ret;
 			}
 
-			ret = snd_soc_dai_set_sysclk(codec_dai,
+			ret = snd_soc_dai_set_sysclk(aif1_dai,
 						     WM8994_SYSCLK_FLL1,
 						     sample_rate * 512,
 						     SND_SOC_CLOCK_IN);
@@ -66,25 +66,25 @@
 					       struct snd_soc_dapm_context *dapm,
 					       enum snd_soc_bias_level level)
 {
-	struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
+	struct snd_soc_dai *aif1_dai = card->rtd[0].codec_dai;
 	int ret;
 
-	if (dapm->dev != codec_dai->dev)
+	if (dapm->dev != aif1_dai->dev)
 		return 0;
 
 	switch (level) {
 	case SND_SOC_BIAS_STANDBY:
-		ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_MCLK2,
+		ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_MCLK2,
 					     32768, SND_SOC_CLOCK_IN);
 		if (ret < 0) {
-			pr_err("Failed to switch away from FLL: %d\n", ret);
+			pr_err("Failed to switch away from FLL1: %d\n", ret);
 			return ret;
 		}
 
-		ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL1,
+		ret = snd_soc_dai_set_pll(aif1_dai, WM8994_FLL1,
 					  0, 0, 0);
 		if (ret < 0) {
-			pr_err("Failed to stop FLL: %d\n", ret);
+			pr_err("Failed to stop FLL1: %d\n", ret);
 			return ret;
 		}
 		break;
@@ -131,6 +131,14 @@
 	.hw_params = littlemill_hw_params,
 };
 
+static const struct snd_soc_pcm_stream baseband_params = {
+	.formats = SNDRV_PCM_FMTBIT_S32_LE,
+	.rate_min = 8000,
+	.rate_max = 8000,
+	.channels_min = 2,
+	.channels_max = 2,
+};
+
 static struct snd_soc_dai_link littlemill_dai[] = {
 	{
 		.name = "CPU",
@@ -143,13 +151,75 @@
 				| SND_SOC_DAIFMT_CBM_CFM,
 		.ops = &littlemill_ops,
 	},
+	{
+		.name = "Baseband",
+		.stream_name = "Baseband",
+		.cpu_dai_name = "wm8994-aif2",
+		.codec_dai_name = "wm1250-ev1",
+		.codec_name = "wm1250-ev1.1-0027",
+		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+				| SND_SOC_DAIFMT_CBM_CFM,
+		.ignore_suspend = 1,
+		.params = &baseband_params,
+	},
 };
 
+static int bbclk_ev(struct snd_soc_dapm_widget *w,
+		    struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_card *card = w->dapm->card;
+	struct snd_soc_dai *aif2_dai = card->rtd[1].cpu_dai;
+	int ret;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		ret = snd_soc_dai_set_pll(aif2_dai, WM8994_FLL2,
+					  WM8994_FLL_SRC_BCLK, 64 * 8000,
+					  8000 * 256);
+		if (ret < 0) {
+			pr_err("Failed to start FLL: %d\n", ret);
+			return ret;
+		}
+
+		ret = snd_soc_dai_set_sysclk(aif2_dai, WM8994_SYSCLK_FLL2,
+					     8000 * 256,
+					     SND_SOC_CLOCK_IN);
+		if (ret < 0) {
+			pr_err("Failed to set SYSCLK: %d\n", ret);
+			return ret;
+		}
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		ret = snd_soc_dai_set_sysclk(aif2_dai, WM8994_SYSCLK_MCLK2,
+					     32768, SND_SOC_CLOCK_IN);
+		if (ret < 0) {
+			pr_err("Failed to switch away from FLL2: %d\n", ret);
+			return ret;
+		}
+
+		ret = snd_soc_dai_set_pll(aif2_dai, WM8994_FLL2,
+					  0, 0, 0);
+		if (ret < 0) {
+			pr_err("Failed to stop FLL2: %d\n", ret);
+			return ret;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 static struct snd_soc_dapm_widget widgets[] = {
 	SND_SOC_DAPM_HP("Headphone", NULL),
 
 	SND_SOC_DAPM_MIC("AMIC", NULL),
 	SND_SOC_DAPM_MIC("DMIC", NULL),
+
+	SND_SOC_DAPM_SUPPLY_S("Baseband Clock", -1, SND_SOC_NOPM, 0, 0,
+			      bbclk_ev,
+			      SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
 };
 
 static struct snd_soc_dapm_route audio_paths[] = {
@@ -162,6 +232,8 @@
 	{ "DMIC", NULL, "MICBIAS2" },   /* Default for DMICBIAS jumper */
 	{ "DMIC1DAT", NULL, "DMIC" },
 	{ "DMIC2DAT", NULL, "DMIC" },
+
+	{ "AIF2CLK", NULL, "Baseband Clock" },
 };
 
 static struct snd_soc_jack littlemill_headset;
@@ -169,10 +241,16 @@
 static int littlemill_late_probe(struct snd_soc_card *card)
 {
 	struct snd_soc_codec *codec = card->rtd[0].codec;
-	struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
+	struct snd_soc_dai *aif1_dai = card->rtd[0].codec_dai;
+	struct snd_soc_dai *aif2_dai = card->rtd[1].cpu_dai;
 	int ret;
 
-	ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_MCLK2,
+	ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_MCLK2,
+				     32768, SND_SOC_CLOCK_IN);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_dai_set_sysclk(aif2_dai, WM8994_SYSCLK_MCLK2,
 				     32768, SND_SOC_CLOCK_IN);
 	if (ret < 0)
 		return ret;
diff --git a/sound/soc/samsung/lowland.c b/sound/soc/samsung/lowland.c
index 4adff93..6abf341 100644
--- a/sound/soc/samsung/lowland.c
+++ b/sound/soc/samsung/lowland.c
@@ -21,33 +21,6 @@
 #define MCLK1_RATE (44100 * 512)
 #define CLKOUT_RATE (44100 * 256)
 
-static int lowland_hw_params(struct snd_pcm_substream *substream,
-			     struct snd_pcm_hw_params *params)
-{
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-	struct snd_soc_dai *codec_dai = rtd->codec_dai;
-	int ret;
-
-	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S
-					 | SND_SOC_DAIFMT_NB_NF
-					 | SND_SOC_DAIFMT_CBM_CFM);
-	if (ret < 0)
-		return ret;
-
-	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S
-					 | SND_SOC_DAIFMT_NB_NF
-					 | SND_SOC_DAIFMT_CBM_CFM);
-	if (ret < 0)
-		return ret;
-
-	return 0;
-}
-
-static struct snd_soc_ops lowland_ops = {
-	.hw_params = lowland_hw_params,
-};
-
 static struct snd_soc_jack lowland_headset;
 
 /* Headset jack detection DAPM pins */
@@ -101,6 +74,25 @@
 	return 0;
 }
 
+static int lowland_wm9081_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_soc_codec *codec = rtd->codec;
+
+	snd_soc_dapm_nc_pin(&codec->dapm, "LINEOUT");
+
+	/* At any time the WM9081 is active it will have this clock */
+	return snd_soc_codec_set_sysclk(codec, WM9081_SYSCLK_MCLK, 0,
+					CLKOUT_RATE, 0);
+}
+
+static const struct snd_soc_pcm_stream sub_params = {
+	.formats = SNDRV_PCM_FMTBIT_S32_LE,
+	.rate_min = 44100,
+	.rate_max = 44100,
+	.channels_min = 2,
+	.channels_max = 2,
+};
+
 static struct snd_soc_dai_link lowland_dai[] = {
 	{
 		.name = "CPU",
@@ -109,7 +101,8 @@
 		.codec_dai_name = "wm5100-aif1",
 		.platform_name = "samsung-audio",
 		.codec_name = "wm5100.1-001a",
-		.ops = &lowland_ops,
+		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+				SND_SOC_DAIFMT_CBM_CFM,
 		.init = lowland_wm5100_init,
 	},
 	{
@@ -118,24 +111,20 @@
 		.cpu_dai_name = "wm5100-aif2",
 		.codec_dai_name = "wm1250-ev1",
 		.codec_name = "wm1250-ev1.1-0027",
-		.ops = &lowland_ops,
+		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+				SND_SOC_DAIFMT_CBM_CFM,
 		.ignore_suspend = 1,
 	},
-};
-
-static int lowland_wm9081_init(struct snd_soc_dapm_context *dapm)
-{
-	snd_soc_dapm_nc_pin(dapm, "LINEOUT");
-
-	/* At any time the WM9081 is active it will have this clock */
-	return snd_soc_codec_set_sysclk(dapm->codec, WM9081_SYSCLK_MCLK, 0,
-					CLKOUT_RATE, 0);
-}
-
-static struct snd_soc_aux_dev lowland_aux_dev[] = {
 	{
-		.name = "wm9081",
+		.name = "Sub Speaker",
+		.stream_name = "Sub Speaker",
+		.cpu_dai_name = "wm5100-aif3",
+		.codec_dai_name = "wm9081-hifi",
 		.codec_name = "wm9081.1-006c",
+		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+				SND_SOC_DAIFMT_CBM_CFM,
+		.ignore_suspend = 1,
+		.params = &sub_params,
 		.init = lowland_wm9081_init,
 	},
 };
@@ -180,8 +169,6 @@
 	.owner = THIS_MODULE,
 	.dai_link = lowland_dai,
 	.num_links = ARRAY_SIZE(lowland_dai),
-	.aux_dev = lowland_aux_dev,
-	.num_aux_devs = ARRAY_SIZE(lowland_aux_dev),
 	.codec_conf = lowland_codec_conf,
 	.num_configs = ARRAY_SIZE(lowland_codec_conf),
 
diff --git a/sound/soc/samsung/speyside.c b/sound/soc/samsung/speyside.c
index f9ab770..a4a9fc7 100644
--- a/sound/soc/samsung/speyside.c
+++ b/sound/soc/samsung/speyside.c
@@ -92,33 +92,6 @@
 	return 0;
 }
 
-static int speyside_hw_params(struct snd_pcm_substream *substream,
-			      struct snd_pcm_hw_params *params)
-{
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-	struct snd_soc_dai *codec_dai = rtd->codec_dai;
-	int ret;
-
-	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S
-					 | SND_SOC_DAIFMT_NB_NF
-					 | SND_SOC_DAIFMT_CBM_CFM);
-	if (ret < 0)
-		return ret;
-
-	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S
-					 | SND_SOC_DAIFMT_NB_NF
-					 | SND_SOC_DAIFMT_CBM_CFM);
-	if (ret < 0)
-		return ret;
-
-	return 0;
-}
-
-static struct snd_soc_ops speyside_ops = {
-	.hw_params = speyside_hw_params,
-};
-
 static struct snd_soc_jack speyside_headset;
 
 /* Headset jack detection DAPM pins */
@@ -208,7 +181,8 @@
 		.platform_name = "samsung-audio",
 		.codec_name = "wm8996.1-001a",
 		.init = speyside_wm8996_init,
-		.ops = &speyside_ops,
+		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+				| SND_SOC_DAIFMT_CBM_CFM,
 	},
 	{
 		.name = "Baseband",
@@ -216,7 +190,8 @@
 		.cpu_dai_name = "wm8996-aif2",
 		.codec_dai_name = "wm1250-ev1",
 		.codec_name = "wm1250-ev1.1-0027",
-		.ops = &speyside_ops,
+		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+				| SND_SOC_DAIFMT_CBM_CFM,
 		.ignore_suspend = 1,
 	},
 };
diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig
index d8e06a6..6bcb116 100644
--- a/sound/soc/sh/Kconfig
+++ b/sound/soc/sh/Kconfig
@@ -22,6 +22,7 @@
 
 config SND_SOC_SH4_FSI
 	tristate "SH4 FSI support"
+	select SND_SIMPLE_CARD
 	help
 	  This option enables FSI sound support
 
@@ -46,29 +47,6 @@
 	  This option enables generic sound support for the first
 	  AC97 unit of the SH7760.
 
-config SND_FSI_AK4642
-	tristate "FSI-AK4642 sound support"
-	depends on SND_SOC_SH4_FSI && I2C
-	select SND_SOC_AK4642
-	help
-	  This option enables generic sound support for the
-	  FSI - AK4642 unit
-
-config SND_FSI_DA7210
-	tristate "FSI-DA7210 sound support"
-	depends on SND_SOC_SH4_FSI && I2C
-	select SND_SOC_DA7210
-	help
-	  This option enables generic sound support for the
-	  FSI - DA7210 unit
-
-config SND_FSI_HDMI
-	tristate "FSI-HDMI sound support"
-	depends on SND_SOC_SH4_FSI && FB_SH_MOBILE_HDMI
-	help
-	  This option enables generic sound support for the
-	  FSI - HDMI unit
-
 config SND_SIU_MIGOR
 	tristate "SIU sound support on Migo-R"
 	depends on SH_MIGOR
diff --git a/sound/soc/sh/Makefile b/sound/soc/sh/Makefile
index 94476d4..849b387 100644
--- a/sound/soc/sh/Makefile
+++ b/sound/soc/sh/Makefile
@@ -14,13 +14,7 @@
 
 ## boards
 snd-soc-sh7760-ac97-objs	:= sh7760-ac97.o
-snd-soc-fsi-ak4642-objs		:= fsi-ak4642.o
-snd-soc-fsi-da7210-objs		:= fsi-da7210.o
-snd-soc-fsi-hdmi-objs		:= fsi-hdmi.o
 snd-soc-migor-objs		:= migor.o
 
 obj-$(CONFIG_SND_SH7760_AC97)	+= snd-soc-sh7760-ac97.o
-obj-$(CONFIG_SND_FSI_AK4642)	+= snd-soc-fsi-ak4642.o
-obj-$(CONFIG_SND_FSI_DA7210)	+= snd-soc-fsi-da7210.o
-obj-$(CONFIG_SND_FSI_HDMI)	+= snd-soc-fsi-hdmi.o
 obj-$(CONFIG_SND_SIU_MIGOR)	+= snd-soc-migor.o
diff --git a/sound/soc/sh/fsi-ak4642.c b/sound/soc/sh/fsi-ak4642.c
deleted file mode 100644
index 97f540a..0000000
--- a/sound/soc/sh/fsi-ak4642.c
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * FSI-AK464x sound support for ms7724se
- *
- * Copyright (C) 2009 Renesas Solutions Corp.
- * Kuninori Morimoto <morimoto.kuninori@renesas.com>
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file "COPYING" in the main directory of this archive
- * for more details.
- */
-
-#include <linux/platform_device.h>
-#include <linux/module.h>
-#include <sound/sh_fsi.h>
-
-struct fsi_ak4642_data {
-	const char *name;
-	const char *card;
-	const char *cpu_dai;
-	const char *codec;
-	const char *platform;
-	int id;
-};
-
-static int fsi_ak4642_dai_init(struct snd_soc_pcm_runtime *rtd)
-{
-	struct snd_soc_dai *codec = rtd->codec_dai;
-	struct snd_soc_dai *cpu = rtd->cpu_dai;
-	int ret;
-
-	ret = snd_soc_dai_set_fmt(codec, SND_SOC_DAIFMT_LEFT_J |
-					 SND_SOC_DAIFMT_CBM_CFM);
-	if (ret < 0)
-		return ret;
-
-	ret = snd_soc_dai_set_sysclk(codec, 0, 11289600, 0);
-	if (ret < 0)
-		return ret;
-
-	ret = snd_soc_dai_set_fmt(cpu, SND_SOC_DAIFMT_LEFT_J |
-				       SND_SOC_DAIFMT_CBS_CFS);
-
-	return ret;
-}
-
-static struct snd_soc_dai_link fsi_dai_link = {
-	.codec_dai_name	= "ak4642-hifi",
-	.init		= fsi_ak4642_dai_init,
-};
-
-static struct snd_soc_card fsi_soc_card  = {
-	.owner		= THIS_MODULE,
-	.dai_link	= &fsi_dai_link,
-	.num_links	= 1,
-};
-
-static struct platform_device *fsi_snd_device;
-
-static int fsi_ak4642_probe(struct platform_device *pdev)
-{
-	int ret = -ENOMEM;
-	struct fsi_ak4642_info *pinfo = pdev->dev.platform_data;
-
-	if (!pinfo) {
-		dev_err(&pdev->dev, "no info for fsi ak4642\n");
-		goto out;
-	}
-
-	fsi_snd_device = platform_device_alloc("soc-audio", pinfo->id);
-	if (!fsi_snd_device)
-		goto out;
-
-	fsi_dai_link.name		= pinfo->name;
-	fsi_dai_link.stream_name	= pinfo->name;
-	fsi_dai_link.cpu_dai_name	= pinfo->cpu_dai;
-	fsi_dai_link.platform_name	= pinfo->platform;
-	fsi_dai_link.codec_name		= pinfo->codec;
-	fsi_soc_card.name		= pinfo->card;
-
-	platform_set_drvdata(fsi_snd_device, &fsi_soc_card);
-	ret = platform_device_add(fsi_snd_device);
-
-	if (ret)
-		platform_device_put(fsi_snd_device);
-
-out:
-	return ret;
-}
-
-static int fsi_ak4642_remove(struct platform_device *pdev)
-{
-	platform_device_unregister(fsi_snd_device);
-	return 0;
-}
-
-static struct platform_driver fsi_ak4642 = {
-	.driver = {
-		.name	= "fsi-ak4642-audio",
-	},
-	.probe		= fsi_ak4642_probe,
-	.remove		= fsi_ak4642_remove,
-};
-
-module_platform_driver(fsi_ak4642);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Generic SH4 FSI-AK4642 sound card");
-MODULE_AUTHOR("Kuninori Morimoto <morimoto.kuninori@renesas.com>");
diff --git a/sound/soc/sh/fsi-da7210.c b/sound/soc/sh/fsi-da7210.c
deleted file mode 100644
index 1dd3354..0000000
--- a/sound/soc/sh/fsi-da7210.c
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * fsi-da7210.c
- *
- * Copyright (C) 2009 Renesas Solutions Corp.
- * Kuninori Morimoto <morimoto.kuninori@renesas.com>
- *
- *  This program is free software; you can redistribute  it and/or modify it
- *  under  the terms of  the GNU General  Public License as published by the
- *  Free Software Foundation;  either version 2 of the  License, or (at your
- *  option) any later version.
- */
-
-#include <linux/platform_device.h>
-#include <linux/module.h>
-#include <sound/sh_fsi.h>
-
-static int fsi_da7210_init(struct snd_soc_pcm_runtime *rtd)
-{
-	struct snd_soc_dai *codec = rtd->codec_dai;
-	struct snd_soc_dai *cpu = rtd->cpu_dai;
-	int ret;
-
-	ret = snd_soc_dai_set_fmt(codec,
-				   SND_SOC_DAIFMT_I2S |
-				   SND_SOC_DAIFMT_CBM_CFM);
-	if (ret < 0)
-		return ret;
-
-	ret = snd_soc_dai_set_fmt(cpu, SND_SOC_DAIFMT_I2S |
-				       SND_SOC_DAIFMT_CBS_CFS);
-
-	return ret;
-}
-
-static struct snd_soc_dai_link fsi_da7210_dai = {
-	.name		= "DA7210",
-	.stream_name	= "DA7210",
-	.cpu_dai_name	= "fsib-dai", /* FSI B */
-	.codec_dai_name	= "da7210-hifi",
-	.platform_name	= "sh_fsi.0",
-	.codec_name	= "da7210-codec.0-001a",
-	.init		= fsi_da7210_init,
-};
-
-static struct snd_soc_card fsi_soc_card = {
-	.name		= "FSI-DA7210",
-	.owner		= THIS_MODULE,
-	.dai_link	= &fsi_da7210_dai,
-	.num_links	= 1,
-};
-
-static struct platform_device *fsi_da7210_snd_device;
-
-static int __init fsi_da7210_sound_init(void)
-{
-	int ret;
-
-	fsi_da7210_snd_device = platform_device_alloc("soc-audio", FSI_PORT_B);
-	if (!fsi_da7210_snd_device)
-		return -ENOMEM;
-
-	platform_set_drvdata(fsi_da7210_snd_device, &fsi_soc_card);
-	ret = platform_device_add(fsi_da7210_snd_device);
-	if (ret)
-		platform_device_put(fsi_da7210_snd_device);
-
-	return ret;
-}
-
-static void __exit fsi_da7210_sound_exit(void)
-{
-	platform_device_unregister(fsi_da7210_snd_device);
-}
-
-module_init(fsi_da7210_sound_init);
-module_exit(fsi_da7210_sound_exit);
-
-/* Module information */
-MODULE_DESCRIPTION("ALSA SoC FSI DA2710");
-MODULE_AUTHOR("Kuninori Morimoto <morimoto.kuninori@renesas.com>");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/sh/fsi-hdmi.c b/sound/soc/sh/fsi-hdmi.c
deleted file mode 100644
index 6e41908..0000000
--- a/sound/soc/sh/fsi-hdmi.c
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * FSI - HDMI sound support
- *
- * Copyright (C) 2010 Renesas Solutions Corp.
- * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file "COPYING" in the main directory of this archive
- * for more details.
- */
-
-#include <linux/platform_device.h>
-#include <linux/module.h>
-#include <sound/sh_fsi.h>
-
-struct fsi_hdmi_data {
-	const char *cpu_dai;
-	const char *card;
-	int id;
-};
-
-static int fsi_hdmi_dai_init(struct snd_soc_pcm_runtime *rtd)
-{
-	struct snd_soc_dai *cpu = rtd->cpu_dai;
-	int ret;
-
-	ret = snd_soc_dai_set_fmt(cpu, SND_SOC_DAIFMT_CBM_CFM);
-
-	return ret;
-}
-
-static struct snd_soc_dai_link fsi_dai_link = {
-	.name		= "HDMI",
-	.stream_name	= "HDMI",
-	.codec_dai_name	= "sh_mobile_hdmi-hifi",
-	.platform_name	= "sh_fsi2",
-	.codec_name	= "sh-mobile-hdmi",
-	.init		= fsi_hdmi_dai_init,
-};
-
-static struct snd_soc_card fsi_soc_card  = {
-	.owner		= THIS_MODULE,
-	.dai_link	= &fsi_dai_link,
-	.num_links	= 1,
-};
-
-static struct platform_device *fsi_snd_device;
-
-static int fsi_hdmi_probe(struct platform_device *pdev)
-{
-	int ret = -ENOMEM;
-	const struct platform_device_id	*id_entry;
-	struct fsi_hdmi_data *pdata;
-
-	id_entry = pdev->id_entry;
-	if (!id_entry) {
-		dev_err(&pdev->dev, "unknown fsi hdmi\n");
-		return -ENODEV;
-	}
-
-	pdata = (struct fsi_hdmi_data *)id_entry->driver_data;
-
-	fsi_snd_device = platform_device_alloc("soc-audio", pdata->id);
-	if (!fsi_snd_device)
-		goto out;
-
-	fsi_dai_link.cpu_dai_name	= pdata->cpu_dai;
-	fsi_soc_card.name		= pdata->card;
-
-	platform_set_drvdata(fsi_snd_device, &fsi_soc_card);
-	ret = platform_device_add(fsi_snd_device);
-
-	if (ret)
-		platform_device_put(fsi_snd_device);
-
-out:
-	return ret;
-}
-
-static int fsi_hdmi_remove(struct platform_device *pdev)
-{
-	platform_device_unregister(fsi_snd_device);
-	return 0;
-}
-
-static struct fsi_hdmi_data fsi2_a_hdmi = {
-	.cpu_dai	= "fsia-dai",
-	.card		= "FSI2A-HDMI",
-	.id		= FSI_PORT_A,
-};
-
-static struct fsi_hdmi_data fsi2_b_hdmi = {
-	.cpu_dai	= "fsib-dai",
-	.card		= "FSI2B-HDMI",
-	.id		= FSI_PORT_B,
-};
-
-static struct platform_device_id fsi_id_table[] = {
-	/* FSI 2 */
-	{ "sh_fsi2_a_hdmi",	(kernel_ulong_t)&fsi2_a_hdmi },
-	{ "sh_fsi2_b_hdmi",	(kernel_ulong_t)&fsi2_b_hdmi },
-	{},
-};
-
-static struct platform_driver fsi_hdmi = {
-	.driver = {
-		.name	= "fsi-hdmi-audio",
-	},
-	.probe		= fsi_hdmi_probe,
-	.remove		= fsi_hdmi_remove,
-	.id_table	= fsi_id_table,
-};
-
-module_platform_driver(fsi_hdmi);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Generic SH4 FSI-HDMI sound card");
-MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c
index 74ed2df..7cee225 100644
--- a/sound/soc/sh/fsi.c
+++ b/sound/soc/sh/fsi.c
@@ -132,6 +132,25 @@
 typedef int (*set_rate_func)(struct device *dev, int rate, int enable);
 
 /*
+ * bus options
+ *
+ * 0x000000BA
+ *
+ * A : sample widtht 16bit setting
+ * B : sample widtht 24bit setting
+ */
+
+#define SHIFT_16DATA		0
+#define SHIFT_24DATA		4
+
+#define PACKAGE_24BITBUS_BACK		0
+#define PACKAGE_24BITBUS_FRONT		1
+#define PACKAGE_16BITBUS_STREAM		2
+
+#define BUSOP_SET(s, a)	((a) << SHIFT_ ## s ## DATA)
+#define BUSOP_GET(s, a)	(((a) >> SHIFT_ ## s ## DATA) & 0xF)
+
+/*
  * FSI driver use below type name for variable
  *
  * xxx_num	: number of data
@@ -189,6 +208,11 @@
 	int oerr_num;
 
 	/*
+	 * bus options
+	 */
+	u32 bus_option;
+
+	/*
 	 * thse are initialized by fsi_handler_init()
 	 */
 	struct fsi_stream_handler *handler;
@@ -211,8 +235,7 @@
 	struct fsi_stream playback;
 	struct fsi_stream capture;
 
-	u32 do_fmt;
-	u32 di_fmt;
+	u32 fmt;
 
 	int chan_num:16;
 	int clk_master:1;
@@ -321,6 +344,10 @@
 /*
  *		basic function
  */
+static int fsi_version(struct fsi_master *master)
+{
+	return master->core->ver;
+}
 
 static struct fsi_master *fsi_get_master(struct fsi_priv *fsi)
 {
@@ -495,6 +522,7 @@
 	io->period_samples	= fsi_frame2sample(fsi, runtime->period_size);
 	io->period_pos		= 0;
 	io->sample_width	= samples_to_bytes(runtime, 1);
+	io->bus_option		= 0;
 	io->oerr_num	= -1; /* ignore 1st err */
 	io->uerr_num	= -1; /* ignore 1st err */
 	fsi_stream_handler_call(io, init, fsi, io);
@@ -522,6 +550,7 @@
 	io->period_samples	= 0;
 	io->period_pos		= 0;
 	io->sample_width	= 0;
+	io->bus_option		= 0;
 	io->oerr_num	= 0;
 	io->uerr_num	= 0;
 	spin_unlock_irqrestore(&master->lock, flags);
@@ -581,6 +610,53 @@
 }
 
 /*
+ *	format/bus/dma setting
+ */
+static void fsi_format_bus_setup(struct fsi_priv *fsi, struct fsi_stream *io,
+				 u32 bus, struct device *dev)
+{
+	struct fsi_master *master = fsi_get_master(fsi);
+	int is_play = fsi_stream_is_play(fsi, io);
+	u32 fmt = fsi->fmt;
+
+	if (fsi_version(master) >= 2) {
+		u32 dma = 0;
+
+		/*
+		 * FSI2 needs DMA/Bus setting
+		 */
+		switch (bus) {
+		case PACKAGE_24BITBUS_FRONT:
+			fmt |= CR_BWS_24;
+			dma |= VDMD_FRONT;
+			dev_dbg(dev, "24bit bus / package in front\n");
+			break;
+		case PACKAGE_16BITBUS_STREAM:
+			fmt |= CR_BWS_16;
+			dma |= VDMD_STREAM;
+			dev_dbg(dev, "16bit bus / stream mode\n");
+			break;
+		case PACKAGE_24BITBUS_BACK:
+		default:
+			fmt |= CR_BWS_24;
+			dma |= VDMD_BACK;
+			dev_dbg(dev, "24bit bus / package in back\n");
+			break;
+		}
+
+		if (is_play)
+			fsi_reg_write(fsi, OUT_DMAC,	dma);
+		else
+			fsi_reg_write(fsi, IN_DMAC,	dma);
+	}
+
+	if (is_play)
+		fsi_reg_write(fsi, DO_FMT, fmt);
+	else
+		fsi_reg_write(fsi, DI_FMT, fmt);
+}
+
+/*
  *		irq function
  */
 
@@ -629,11 +705,6 @@
 	struct fsi_master *master = fsi_get_master(fsi);
 	u32 mask, val;
 
-	if (master->core->ver < 2) {
-		pr_err("fsi: register access err (%s)\n", __func__);
-		return;
-	}
-
 	mask = BP | SE;
 	val = enable ? mask : 0;
 
@@ -648,9 +719,7 @@
 static int fsi_set_master_clk(struct device *dev, struct fsi_priv *fsi,
 			      long rate, int enable)
 {
-	struct fsi_master *master = fsi_get_master(fsi);
 	set_rate_func set_rate = fsi_get_info_set_rate(fsi);
-	int fsi_ver = master->core->ver;
 	int ret;
 
 	if (!set_rate)
@@ -682,10 +751,7 @@
 			data |= (0x3 << 12);
 			break;
 		case SH_FSI_ACKMD_32:
-			if (fsi_ver < 2)
-				dev_err(dev, "unsupported ACKMD\n");
-			else
-				data |= (0x4 << 12);
+			data |= (0x4 << 12);
 			break;
 		}
 
@@ -708,10 +774,7 @@
 			data |= (0x4 << 8);
 			break;
 		case SH_FSI_BPFMD_16:
-			if (fsi_ver < 2)
-				dev_err(dev, "unsupported ACKMD\n");
-			else
-				data |= (0x7 << 8);
+			data |= (0x7 << 8);
 			break;
 		}
 
@@ -728,11 +791,26 @@
  */
 static void fsi_pio_push16(struct fsi_priv *fsi, u8 *_buf, int samples)
 {
-	u16 *buf = (u16 *)_buf;
+	u32 enable_stream = fsi_get_info_flags(fsi) & SH_FSI_ENABLE_STREAM_MODE;
 	int i;
 
-	for (i = 0; i < samples; i++)
-		fsi_reg_write(fsi, DODT, ((u32)*(buf + i) << 8));
+	if (enable_stream) {
+		/*
+		 * stream mode
+		 * see
+		 *	fsi_pio_push_init()
+		 */
+		u32 *buf = (u32 *)_buf;
+
+		for (i = 0; i < samples / 2; i++)
+			fsi_reg_write(fsi, DODT, buf[i]);
+	} else {
+		/* normal mode */
+		u16 *buf = (u16 *)_buf;
+
+		for (i = 0; i < samples; i++)
+			fsi_reg_write(fsi, DODT, ((u32)*(buf + i) << 8));
+	}
 }
 
 static void fsi_pio_pop16(struct fsi_priv *fsi, u8 *_buf, int samples)
@@ -872,12 +950,44 @@
 		fsi_master_mask_set(master, CLK_RST, clk, (enable) ? clk : 0);
 }
 
+static int fsi_pio_push_init(struct fsi_priv *fsi, struct fsi_stream *io)
+{
+	u32 enable_stream = fsi_get_info_flags(fsi) & SH_FSI_ENABLE_STREAM_MODE;
+
+	/*
+	 * we can use 16bit stream mode
+	 * when "playback" and "16bit data"
+	 * and platform allows "stream mode"
+	 * see
+	 *	fsi_pio_push16()
+	 */
+	if (enable_stream)
+		io->bus_option = BUSOP_SET(24, PACKAGE_24BITBUS_BACK) |
+				 BUSOP_SET(16, PACKAGE_16BITBUS_STREAM);
+	else
+		io->bus_option = BUSOP_SET(24, PACKAGE_24BITBUS_BACK) |
+				 BUSOP_SET(16, PACKAGE_24BITBUS_BACK);
+	return 0;
+}
+
+static int fsi_pio_pop_init(struct fsi_priv *fsi, struct fsi_stream *io)
+{
+	/*
+	 * always 24bit bus, package back when "capture"
+	 */
+	io->bus_option = BUSOP_SET(24, PACKAGE_24BITBUS_BACK) |
+			 BUSOP_SET(16, PACKAGE_24BITBUS_BACK);
+	return 0;
+}
+
 static struct fsi_stream_handler fsi_pio_push_handler = {
+	.init		= fsi_pio_push_init,
 	.transfer	= fsi_pio_push,
 	.start_stop	= fsi_pio_start_stop,
 };
 
 static struct fsi_stream_handler fsi_pio_pop_handler = {
+	.init		= fsi_pio_pop_init,
 	.transfer	= fsi_pio_pop,
 	.start_stop	= fsi_pio_start_stop,
 };
@@ -919,6 +1029,13 @@
 	enum dma_data_direction dir = fsi_stream_is_play(fsi, io) ?
 				DMA_TO_DEVICE : DMA_FROM_DEVICE;
 
+	/*
+	 * 24bit data : 24bit bus / package in back
+	 * 16bit data : 16bit bus / stream mode
+	 */
+	io->bus_option = BUSOP_SET(24, PACKAGE_24BITBUS_BACK) |
+			 BUSOP_SET(16, PACKAGE_16BITBUS_STREAM);
+
 	io->dma = dma_map_single(dai->dev, runtime->dma_area,
 				 snd_pcm_lib_buffer_bytes(io->substream), dir);
 	return 0;
@@ -1055,25 +1172,9 @@
 static void fsi_dma_push_start_stop(struct fsi_priv *fsi, struct fsi_stream *io,
 				 int start)
 {
-	u32 bws;
-	u32 dma;
+	u32 enable = start ? DMA_ON : 0;
 
-	switch (io->sample_width * start) {
-	case 2:
-		bws = CR_BWS_16;
-		dma = VDMD_STREAM | DMA_ON;
-		break;
-	case 4:
-		bws = CR_BWS_24;
-		dma = VDMD_BACK | DMA_ON;
-		break;
-	default:
-		bws = 0;
-		dma = 0;
-	}
-
-	fsi_reg_mask_set(fsi, DO_FMT, CR_BWS_MASK, bws);
-	fsi_reg_write(fsi, OUT_DMAC, dma);
+	fsi_reg_mask_set(fsi, OUT_DMAC, DMA_ON, enable);
 }
 
 static int fsi_dma_probe(struct fsi_priv *fsi, struct fsi_stream *io)
@@ -1176,8 +1277,6 @@
 			  struct fsi_stream *io,
 			  struct device *dev)
 {
-	struct fsi_master *master = fsi_get_master(fsi);
-	int fsi_ver = master->core->ver;
 	u32 flags = fsi_get_info_flags(fsi);
 	u32 data = 0;
 
@@ -1200,10 +1299,6 @@
 
 	fsi_reg_write(fsi, CKG2, data);
 
-	/* set format */
-	fsi_reg_write(fsi, DO_FMT, fsi->do_fmt);
-	fsi_reg_write(fsi, DI_FMT, fsi->di_fmt);
-
 	/* spdif ? */
 	if (fsi_is_spdif(fsi)) {
 		fsi_spdif_clk_ctrl(fsi, 1);
@@ -1211,15 +1306,18 @@
 	}
 
 	/*
-	 * FIXME
-	 *
-	 * FSI driver assumed that data package is in-back.
-	 * FSI2 chip can select it.
+	 * get bus settings
 	 */
-	if (fsi_ver >= 2) {
-		fsi_reg_write(fsi, OUT_DMAC,	(1 << 4));
-		fsi_reg_write(fsi, IN_DMAC,	(1 << 4));
+	data = 0;
+	switch (io->sample_width) {
+	case 2:
+		data = BUSOP_GET(16, io->bus_option);
+		break;
+	case 4:
+		data = BUSOP_GET(24, io->bus_option);
+		break;
 	}
+	fsi_format_bus_setup(fsi, io, data, dev);
 
 	/* irq clear */
 	fsi_irq_disable(fsi, io);
@@ -1243,7 +1341,9 @@
 {
 	struct fsi_priv *fsi = fsi_get_priv(substream);
 
-	return fsi_hw_startup(fsi, fsi_stream_get(fsi, substream), dai->dev);
+	fsi->rate = 0;
+
+	return 0;
 }
 
 static void fsi_dai_shutdown(struct snd_pcm_substream *substream,
@@ -1251,7 +1351,6 @@
 {
 	struct fsi_priv *fsi = fsi_get_priv(substream);
 
-	fsi_hw_shutdown(fsi, dai->dev);
 	fsi->rate = 0;
 }
 
@@ -1265,11 +1364,13 @@
 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_START:
 		fsi_stream_init(fsi, io, substream);
+		fsi_hw_startup(fsi, io, dai->dev);
 		ret = fsi_stream_transfer(io);
 		if (0 == ret)
 			fsi_stream_start(fsi, io);
 		break;
 	case SNDRV_PCM_TRIGGER_STOP:
+		fsi_hw_shutdown(fsi, dai->dev);
 		fsi_stream_stop(fsi, io);
 		fsi_stream_quit(fsi, io);
 		break;
@@ -1280,42 +1381,33 @@
 
 static int fsi_set_fmt_dai(struct fsi_priv *fsi, unsigned int fmt)
 {
-	u32 data = 0;
-
 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
 	case SND_SOC_DAIFMT_I2S:
-		data = CR_I2S;
+		fsi->fmt = CR_I2S;
 		fsi->chan_num = 2;
 		break;
 	case SND_SOC_DAIFMT_LEFT_J:
-		data = CR_PCM;
+		fsi->fmt = CR_PCM;
 		fsi->chan_num = 2;
 		break;
 	default:
 		return -EINVAL;
 	}
 
-	fsi->do_fmt = data;
-	fsi->di_fmt = data;
-
 	return 0;
 }
 
 static int fsi_set_fmt_spdif(struct fsi_priv *fsi)
 {
 	struct fsi_master *master = fsi_get_master(fsi);
-	u32 data = 0;
 
-	if (master->core->ver < 2)
+	if (fsi_version(master) < 2)
 		return -EINVAL;
 
-	data = CR_BWS_16 | CR_DTMD_SPDIF_PCM | CR_PCM;
+	fsi->fmt = CR_DTMD_SPDIF_PCM | CR_PCM;
 	fsi->chan_num = 2;
 	fsi->spdif = 1;
 
-	fsi->do_fmt = data;
-	fsi->di_fmt = data;
-
 	return 0;
 }
 
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index c88d974..b37ee80 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -39,6 +39,7 @@
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
+#include <sound/soc-dpcm.h>
 #include <sound/initval.h>
 
 #define CREATE_TRACE_POINTS
@@ -54,7 +55,6 @@
 #endif
 
 static DEFINE_MUTEX(client_mutex);
-static LIST_HEAD(card_list);
 static LIST_HEAD(dai_list);
 static LIST_HEAD(platform_list);
 static LIST_HEAD(codec_list);
@@ -465,6 +465,35 @@
 }
 #endif
 
+struct snd_pcm_substream *snd_soc_get_dai_substream(struct snd_soc_card *card,
+		const char *dai_link, int stream)
+{
+	int i;
+
+	for (i = 0; i < card->num_links; i++) {
+		if (card->rtd[i].dai_link->no_pcm &&
+			!strcmp(card->rtd[i].dai_link->name, dai_link))
+			return card->rtd[i].pcm->streams[stream].substream;
+	}
+	dev_dbg(card->dev, "failed to find dai link %s\n", dai_link);
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_dai_substream);
+
+struct snd_soc_pcm_runtime *snd_soc_get_pcm_runtime(struct snd_soc_card *card,
+		const char *dai_link)
+{
+	int i;
+
+	for (i = 0; i < card->num_links; i++) {
+		if (!strcmp(card->rtd[i].dai_link->name, dai_link))
+			return &card->rtd[i];
+	}
+	dev_dbg(card->dev, "failed to find rtd %s\n", dai_link);
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_pcm_runtime);
+
 #ifdef CONFIG_SND_SOC_AC97_BUS
 /* unregister ac97 codec */
 static int soc_ac97_dev_unregister(struct snd_soc_codec *codec)
@@ -567,19 +596,16 @@
 	}
 
 	for (i = 0; i < card->num_rtd; i++) {
-		struct snd_soc_dai *codec_dai = card->rtd[i].codec_dai;
 
 		if (card->rtd[i].dai_link->ignore_suspend)
 			continue;
 
 		snd_soc_dapm_stream_event(&card->rtd[i],
 					  SNDRV_PCM_STREAM_PLAYBACK,
-					  codec_dai,
 					  SND_SOC_DAPM_STREAM_SUSPEND);
 
 		snd_soc_dapm_stream_event(&card->rtd[i],
 					  SNDRV_PCM_STREAM_CAPTURE,
-					  codec_dai,
 					  SND_SOC_DAPM_STREAM_SUSPEND);
 	}
 
@@ -683,17 +709,16 @@
 	}
 
 	for (i = 0; i < card->num_rtd; i++) {
-		struct snd_soc_dai *codec_dai = card->rtd[i].codec_dai;
 
 		if (card->rtd[i].dai_link->ignore_suspend)
 			continue;
 
 		snd_soc_dapm_stream_event(&card->rtd[i],
-					  SNDRV_PCM_STREAM_PLAYBACK, codec_dai,
+					  SNDRV_PCM_STREAM_PLAYBACK,
 					  SND_SOC_DAPM_STREAM_RESUME);
 
 		snd_soc_dapm_stream_event(&card->rtd[i],
-					  SNDRV_PCM_STREAM_CAPTURE, codec_dai,
+					  SNDRV_PCM_STREAM_CAPTURE,
 					  SND_SOC_DAPM_STREAM_RESUME);
 	}
 
@@ -783,15 +808,9 @@
 	struct snd_soc_dai *codec_dai, *cpu_dai;
 	const char *platform_name;
 
-	if (rtd->complete)
-		return 1;
 	dev_dbg(card->dev, "binding %s at idx %d\n", dai_link->name, num);
 
-	/* do we already have the CPU DAI for this link ? */
-	if (rtd->cpu_dai) {
-		goto find_codec;
-	}
-	/* no, then find CPU DAI from registered DAIs*/
+	/* Find CPU DAI from registered DAIs*/
 	list_for_each_entry(cpu_dai, &dai_list, list) {
 		if (dai_link->cpu_dai_of_node) {
 			if (cpu_dai->dev->of_node != dai_link->cpu_dai_of_node)
@@ -802,18 +821,15 @@
 		}
 
 		rtd->cpu_dai = cpu_dai;
-		goto find_codec;
 	}
-	dev_dbg(card->dev, "CPU DAI %s not registered\n",
+
+	if (!rtd->cpu_dai) {
+		dev_dbg(card->dev, "CPU DAI %s not registered\n",
 			dai_link->cpu_dai_name);
-
-find_codec:
-	/* do we already have the CODEC for this link ? */
-	if (rtd->codec) {
-		goto find_platform;
+		return -EPROBE_DEFER;
 	}
 
-	/* no, then find CODEC from registered CODECs*/
+	/* Find CODEC from registered CODECs */
 	list_for_each_entry(codec, &codec_list, list) {
 		if (dai_link->codec_of_node) {
 			if (codec->dev->of_node != dai_link->codec_of_node)
@@ -835,28 +851,28 @@
 					dai_link->codec_dai_name)) {
 
 				rtd->codec_dai = codec_dai;
-				goto find_platform;
 			}
 		}
-		dev_dbg(card->dev, "CODEC DAI %s not registered\n",
+
+		if (!rtd->codec_dai) {
+			dev_dbg(card->dev, "CODEC DAI %s not registered\n",
 				dai_link->codec_dai_name);
-
-		goto find_platform;
+			return -EPROBE_DEFER;
+		}
 	}
-	dev_dbg(card->dev, "CODEC %s not registered\n",
-			dai_link->codec_name);
 
-find_platform:
-	/* do we need a platform? */
-	if (rtd->platform)
-		goto out;
+	if (!rtd->codec) {
+		dev_dbg(card->dev, "CODEC %s not registered\n",
+			dai_link->codec_name);
+		return -EPROBE_DEFER;
+	}
 
 	/* if there's no platform we match on the empty platform */
 	platform_name = dai_link->platform_name;
 	if (!platform_name && !dai_link->platform_of_node)
 		platform_name = "snd-soc-dummy";
 
-	/* no, then find one from the set of registered platforms */
+	/* find one from the set of registered platforms */
 	list_for_each_entry(platform, &platform_list, list) {
 		if (dai_link->platform_of_node) {
 			if (platform->dev->of_node !=
@@ -868,20 +884,16 @@
 		}
 
 		rtd->platform = platform;
-		goto out;
 	}
-
-	dev_dbg(card->dev, "platform %s not registered\n",
+	if (!rtd->platform) {
+		dev_dbg(card->dev, "platform %s not registered\n",
 			dai_link->platform_name);
-	return 0;
-
-out:
-	/* mark rtd as complete if we found all 4 of our client devices */
-	if (rtd->codec && rtd->codec_dai && rtd->platform && rtd->cpu_dai) {
-		rtd->complete = 1;
-		card->num_rtd++;
+		return -EPROBE_DEFER;
 	}
-	return 1;
+
+	card->num_rtd++;
+
+	return 0;
 }
 
 static void soc_remove_codec(struct snd_soc_codec *codec)
@@ -1068,6 +1080,7 @@
 {
 	int ret = 0;
 	const struct snd_soc_platform_driver *driver = platform->driver;
+	struct snd_soc_dai *dai;
 
 	platform->card = card;
 	platform->dapm.card = card;
@@ -1081,6 +1094,14 @@
 		snd_soc_dapm_new_controls(&platform->dapm,
 			driver->dapm_widgets, driver->num_dapm_widgets);
 
+	/* Create DAPM widgets for each DAI stream */
+	list_for_each_entry(dai, &dai_list, list) {
+		if (dai->dev != platform->dev)
+			continue;
+
+		snd_soc_dapm_new_dai_widgets(&platform->dapm, dai);
+	}
+
 	platform->dapm.idle_bias_off = 1;
 
 	if (driver->probe) {
@@ -1170,6 +1191,10 @@
 	rtd->dev->init_name = name;
 	dev_set_drvdata(rtd->dev, rtd);
 	mutex_init(&rtd->pcm_mutex);
+	INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_PLAYBACK].be_clients);
+	INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_CAPTURE].be_clients);
+	INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_PLAYBACK].fe_clients);
+	INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_CAPTURE].fe_clients);
 	ret = device_add(rtd->dev);
 	if (ret < 0) {
 		dev_err(card->dev,
@@ -1191,6 +1216,17 @@
 		dev_err(codec->dev,
 			"asoc: failed to add codec sysfs files: %d\n", ret);
 
+#ifdef CONFIG_DEBUG_FS
+	/* add DPCM sysfs entries */
+	if (!dailess && !dai_link->dynamic)
+		goto out;
+
+	ret = soc_dpcm_debugfs_add(rtd);
+	if (ret < 0)
+		dev_err(rtd->dev, "asoc: failed to add dpcm sysfs entries: %d\n", ret);
+
+out:
+#endif
 	return 0;
 }
 
@@ -1200,14 +1236,15 @@
 	struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
 	struct snd_soc_codec *codec = rtd->codec;
 	struct snd_soc_platform *platform = rtd->platform;
-	struct snd_soc_dai *codec_dai = rtd->codec_dai, *cpu_dai = rtd->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	struct snd_soc_dapm_widget *play_w, *capture_w;
 	int ret;
 
 	dev_dbg(card->dev, "probe %s dai link %d late %d\n",
 			card->name, num, order);
 
 	/* config components */
-	codec_dai->codec = codec;
 	cpu_dai->platform = platform;
 	codec_dai->card = card;
 	cpu_dai->card = card;
@@ -1218,9 +1255,12 @@
 	/* probe the cpu_dai */
 	if (!cpu_dai->probed &&
 			cpu_dai->driver->probe_order == order) {
+		cpu_dai->dapm.card = card;
 		if (!try_module_get(cpu_dai->dev->driver->owner))
 			return -ENODEV;
 
+		snd_soc_dapm_new_dai_widgets(&cpu_dai->dapm, cpu_dai);
+
 		if (cpu_dai->driver->probe) {
 			ret = cpu_dai->driver->probe(cpu_dai);
 			if (ret < 0) {
@@ -1279,12 +1319,39 @@
 	if (ret < 0)
 		pr_warn("asoc: failed to add pmdown_time sysfs:%d\n", ret);
 
-	/* create the pcm */
-	ret = soc_new_pcm(rtd, num);
-	if (ret < 0) {
-		pr_err("asoc: can't create pcm %s :%d\n",
-				dai_link->stream_name, ret);
-		return ret;
+	if (!dai_link->params) {
+		/* create the pcm */
+		ret = soc_new_pcm(rtd, num);
+		if (ret < 0) {
+			pr_err("asoc: can't create pcm %s :%d\n",
+			       dai_link->stream_name, ret);
+			return ret;
+		}
+	} else {
+		/* link the DAI widgets */
+		play_w = codec_dai->playback_widget;
+		capture_w = cpu_dai->capture_widget;
+		if (play_w && capture_w) {
+			ret = snd_soc_dapm_new_pcm(card, dai_link->params,
+						   capture_w, play_w);
+			if (ret != 0) {
+				dev_err(card->dev, "Can't link %s to %s: %d\n",
+					play_w->name, capture_w->name, ret);
+				return ret;
+			}
+		}
+
+		play_w = cpu_dai->playback_widget;
+		capture_w = codec_dai->capture_widget;
+		if (play_w && capture_w) {
+			ret = snd_soc_dapm_new_pcm(card, dai_link->params,
+						   capture_w, play_w);
+			if (ret != 0) {
+				dev_err(card->dev, "Can't link %s to %s: %d\n",
+					play_w->name, capture_w->name, ret);
+				return ret;
+			}
+		}
 	}
 
 	/* add platform data for AC97 devices */
@@ -1334,6 +1401,20 @@
 }
 #endif
 
+static int soc_check_aux_dev(struct snd_soc_card *card, int num)
+{
+	struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num];
+	struct snd_soc_codec *codec;
+
+	/* find CODEC from registered CODECs*/
+	list_for_each_entry(codec, &codec_list, list) {
+		if (!strcmp(codec->name, aux_dev->codec_name))
+			return 0;
+	}
+
+	return -EPROBE_DEFER;
+}
+
 static int soc_probe_aux_dev(struct snd_soc_card *card, int num)
 {
 	struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num];
@@ -1354,7 +1435,7 @@
 	}
 	/* codec not found */
 	dev_err(card->dev, "asoc: codec %s not found", aux_dev->codec_name);
-	goto out;
+	return -EPROBE_DEFER;
 
 found:
 	ret = soc_probe_codec(card, codec);
@@ -1404,29 +1485,28 @@
 	return 0;
 }
 
-static void snd_soc_instantiate_card(struct snd_soc_card *card)
+static int snd_soc_instantiate_card(struct snd_soc_card *card)
 {
 	struct snd_soc_codec *codec;
 	struct snd_soc_codec_conf *codec_conf;
 	enum snd_soc_compress_type compress_type;
 	struct snd_soc_dai_link *dai_link;
-	int ret, i, order;
+	int ret, i, order, dai_fmt;
 
-	mutex_lock(&card->mutex);
-
-	if (card->instantiated) {
-		mutex_unlock(&card->mutex);
-		return;
-	}
+	mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_INIT);
 
 	/* bind DAIs */
-	for (i = 0; i < card->num_links; i++)
-		soc_bind_dai_link(card, i);
+	for (i = 0; i < card->num_links; i++) {
+		ret = soc_bind_dai_link(card, i);
+		if (ret != 0)
+			goto base_error;
+	}
 
-	/* bind completed ? */
-	if (card->num_rtd != card->num_links) {
-		mutex_unlock(&card->mutex);
-		return;
+	/* check aux_devs too */
+	for (i = 0; i < card->num_aux_devs; i++) {
+		ret = soc_check_aux_dev(card, i);
+		if (ret != 0)
+			goto base_error;
 	}
 
 	/* initialize the register cache for each available codec */
@@ -1446,10 +1526,8 @@
 			}
 		}
 		ret = snd_soc_init_codec_cache(codec, compress_type);
-		if (ret < 0) {
-			mutex_unlock(&card->mutex);
-			return;
-		}
+		if (ret < 0)
+			goto base_error;
 	}
 
 	/* card bind complete so register a sound card */
@@ -1458,8 +1536,7 @@
 	if (ret < 0) {
 		pr_err("asoc: can't create sound card for card %s: %d\n",
 			card->name, ret);
-		mutex_unlock(&card->mutex);
-		return;
+		goto base_error;
 	}
 	card->snd_card->dev = card->dev;
 
@@ -1523,17 +1600,47 @@
 
 	for (i = 0; i < card->num_links; i++) {
 		dai_link = &card->dai_link[i];
+		dai_fmt = dai_link->dai_fmt;
 
-		if (dai_link->dai_fmt) {
+		if (dai_fmt) {
 			ret = snd_soc_dai_set_fmt(card->rtd[i].codec_dai,
-						  dai_link->dai_fmt);
+						  dai_fmt);
 			if (ret != 0 && ret != -ENOTSUPP)
 				dev_warn(card->rtd[i].codec_dai->dev,
 					 "Failed to set DAI format: %d\n",
 					 ret);
+		}
+
+		/* If this is a regular CPU link there will be a platform */
+		if (dai_fmt &&
+		    (dai_link->platform_name || dai_link->platform_of_node)) {
+			ret = snd_soc_dai_set_fmt(card->rtd[i].cpu_dai,
+						  dai_fmt);
+			if (ret != 0 && ret != -ENOTSUPP)
+				dev_warn(card->rtd[i].cpu_dai->dev,
+					 "Failed to set DAI format: %d\n",
+					 ret);
+		} else if (dai_fmt) {
+			/* Flip the polarity for the "CPU" end */
+			dai_fmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
+			switch (dai_link->dai_fmt &
+				SND_SOC_DAIFMT_MASTER_MASK) {
+			case SND_SOC_DAIFMT_CBM_CFM:
+				dai_fmt |= SND_SOC_DAIFMT_CBS_CFS;
+				break;
+			case SND_SOC_DAIFMT_CBM_CFS:
+				dai_fmt |= SND_SOC_DAIFMT_CBS_CFM;
+				break;
+			case SND_SOC_DAIFMT_CBS_CFM:
+				dai_fmt |= SND_SOC_DAIFMT_CBM_CFS;
+				break;
+			case SND_SOC_DAIFMT_CBS_CFS:
+				dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
+				break;
+			}
 
 			ret = snd_soc_dai_set_fmt(card->rtd[i].cpu_dai,
-						  dai_link->dai_fmt);
+						  dai_fmt);
 			if (ret != 0 && ret != -ENOTSUPP)
 				dev_warn(card->rtd[i].cpu_dai->dev,
 					 "Failed to set DAI format: %d\n",
@@ -1599,7 +1706,8 @@
 	card->instantiated = 1;
 	snd_soc_dapm_sync(&card->dapm);
 	mutex_unlock(&card->mutex);
-	return;
+
+	return 0;
 
 probe_aux_dev_err:
 	for (i = 0; i < card->num_aux_devs; i++)
@@ -1614,18 +1722,10 @@
 
 	snd_card_free(card->snd_card);
 
+base_error:
 	mutex_unlock(&card->mutex);
-}
 
-/*
- * Attempt to initialise any uninitialised cards.  Must be called with
- * client_mutex.
- */
-static void snd_soc_instantiate_cards(void)
-{
-	struct snd_soc_card *card;
-	list_for_each_entry(card, &card_list, list)
-		snd_soc_instantiate_card(card);
+	return ret;
 }
 
 /* probes a new socdev */
@@ -2527,6 +2627,87 @@
 EXPORT_SYMBOL_GPL(snd_soc_put_volsw);
 
 /**
+ * snd_soc_get_volsw_sx - single mixer get callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to get the value of a single mixer control, or a double mixer
+ * control that spans 2 registers.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_volsw_sx(struct snd_kcontrol *kcontrol,
+		      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct soc_mixer_control *mc =
+	    (struct soc_mixer_control *)kcontrol->private_value;
+
+	unsigned int reg = mc->reg;
+	unsigned int reg2 = mc->rreg;
+	unsigned int shift = mc->shift;
+	unsigned int rshift = mc->rshift;
+	int max = mc->max;
+	int min = mc->min;
+	int mask = (1 << (fls(min + max) - 1)) - 1;
+
+	ucontrol->value.integer.value[0] =
+	    ((snd_soc_read(codec, reg) >> shift) - min) & mask;
+
+	if (snd_soc_volsw_is_stereo(mc))
+		ucontrol->value.integer.value[1] =
+			((snd_soc_read(codec, reg2) >> rshift) - min) & mask;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_volsw_sx);
+
+/**
+ * snd_soc_put_volsw_sx - double mixer set callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to set the value of a double mixer control that spans 2 registers.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_put_volsw_sx(struct snd_kcontrol *kcontrol,
+			 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct soc_mixer_control *mc =
+	    (struct soc_mixer_control *)kcontrol->private_value;
+
+	unsigned int reg = mc->reg;
+	unsigned int reg2 = mc->rreg;
+	unsigned int shift = mc->shift;
+	unsigned int rshift = mc->rshift;
+	int max = mc->max;
+	int min = mc->min;
+	int mask = (1 << (fls(min + max) - 1)) - 1;
+	int err = 0;
+	unsigned short val, val_mask, val2 = 0;
+
+	val_mask = mask << shift;
+	val = (ucontrol->value.integer.value[0] + min) & mask;
+	val = val << shift;
+
+	if (snd_soc_update_bits_locked(codec, reg, val_mask, val))
+			return err;
+
+	if (snd_soc_volsw_is_stereo(mc)) {
+		val_mask = mask << rshift;
+		val2 = (ucontrol->value.integer.value[1] + min) & mask;
+		val2 = val2 << rshift;
+
+		if (snd_soc_update_bits_locked(codec, reg2, val_mask, val2))
+			return err;
+	}
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_volsw_sx);
+
+/**
  * snd_soc_info_volsw_s8 - signed mixer info callback
  * @kcontrol: mixer control
  * @uinfo: control element information
@@ -2647,99 +2828,6 @@
 }
 EXPORT_SYMBOL_GPL(snd_soc_limit_volume);
 
-/**
- * snd_soc_info_volsw_2r_sx - double with tlv and variable data size
- *  mixer info callback
- * @kcontrol: mixer control
- * @uinfo: control element information
- *
- * Returns 0 for success.
- */
-int snd_soc_info_volsw_2r_sx(struct snd_kcontrol *kcontrol,
-			struct snd_ctl_elem_info *uinfo)
-{
-	struct soc_mixer_control *mc =
-		(struct soc_mixer_control *)kcontrol->private_value;
-	int max = mc->max;
-	int min = mc->min;
-
-	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-	uinfo->count = 2;
-	uinfo->value.integer.min = 0;
-	uinfo->value.integer.max = max-min;
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_info_volsw_2r_sx);
-
-/**
- * snd_soc_get_volsw_2r_sx - double with tlv and variable data size
- *  mixer get callback
- * @kcontrol: mixer control
- * @uinfo: control element information
- *
- * Returns 0 for success.
- */
-int snd_soc_get_volsw_2r_sx(struct snd_kcontrol *kcontrol,
-			struct snd_ctl_elem_value *ucontrol)
-{
-	struct soc_mixer_control *mc =
-		(struct soc_mixer_control *)kcontrol->private_value;
-	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
-	unsigned int mask = (1<<mc->shift)-1;
-	int min = mc->min;
-	int val = snd_soc_read(codec, mc->reg) & mask;
-	int valr = snd_soc_read(codec, mc->rreg) & mask;
-
-	ucontrol->value.integer.value[0] = ((val & 0xff)-min) & mask;
-	ucontrol->value.integer.value[1] = ((valr & 0xff)-min) & mask;
-	return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_get_volsw_2r_sx);
-
-/**
- * snd_soc_put_volsw_2r_sx - double with tlv and variable data size
- *  mixer put callback
- * @kcontrol: mixer control
- * @uinfo: control element information
- *
- * Returns 0 for success.
- */
-int snd_soc_put_volsw_2r_sx(struct snd_kcontrol *kcontrol,
-			struct snd_ctl_elem_value *ucontrol)
-{
-	struct soc_mixer_control *mc =
-		(struct soc_mixer_control *)kcontrol->private_value;
-	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
-	unsigned int mask = (1<<mc->shift)-1;
-	int min = mc->min;
-	int ret;
-	unsigned int val, valr, oval, ovalr;
-
-	val = ((ucontrol->value.integer.value[0]+min) & 0xff);
-	val &= mask;
-	valr = ((ucontrol->value.integer.value[1]+min) & 0xff);
-	valr &= mask;
-
-	oval = snd_soc_read(codec, mc->reg) & mask;
-	ovalr = snd_soc_read(codec, mc->rreg) & mask;
-
-	ret = 0;
-	if (oval != val) {
-		ret = snd_soc_write(codec, mc->reg, val);
-		if (ret < 0)
-			return ret;
-	}
-	if (ovalr != valr) {
-		ret = snd_soc_write(codec, mc->rreg, valr);
-		if (ret < 0)
-			return ret;
-	}
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_put_volsw_2r_sx);
-
 int snd_soc_bytes_info(struct snd_kcontrol *kcontrol,
 		       struct snd_ctl_elem_info *uinfo)
 {
@@ -2850,6 +2938,186 @@
 EXPORT_SYMBOL_GPL(snd_soc_bytes_put);
 
 /**
+ * snd_soc_info_xr_sx - signed multi register info callback
+ * @kcontrol: mreg control
+ * @uinfo: control element information
+ *
+ * Callback to provide information of a control that can
+ * span multiple codec registers which together
+ * forms a single signed value in a MSB/LSB manner.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_info_xr_sx(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_info *uinfo)
+{
+	struct soc_mreg_control *mc =
+		(struct soc_mreg_control *)kcontrol->private_value;
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = mc->min;
+	uinfo->value.integer.max = mc->max;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_info_xr_sx);
+
+/**
+ * snd_soc_get_xr_sx - signed multi register get callback
+ * @kcontrol: mreg control
+ * @ucontrol: control element information
+ *
+ * Callback to get the value of a control that can span
+ * multiple codec registers which together forms a single
+ * signed value in a MSB/LSB manner. The control supports
+ * specifying total no of bits used to allow for bitfields
+ * across the multiple codec registers.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_xr_sx(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct soc_mreg_control *mc =
+		(struct soc_mreg_control *)kcontrol->private_value;
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	unsigned int regbase = mc->regbase;
+	unsigned int regcount = mc->regcount;
+	unsigned int regwshift = codec->driver->reg_word_size * BITS_PER_BYTE;
+	unsigned int regwmask = (1<<regwshift)-1;
+	unsigned int invert = mc->invert;
+	unsigned long mask = (1UL<<mc->nbits)-1;
+	long min = mc->min;
+	long max = mc->max;
+	long val = 0;
+	unsigned long regval;
+	unsigned int i;
+
+	for (i = 0; i < regcount; i++) {
+		regval = snd_soc_read(codec, regbase+i) & regwmask;
+		val |= regval << (regwshift*(regcount-i-1));
+	}
+	val &= mask;
+	if (min < 0 && val > max)
+		val |= ~mask;
+	if (invert)
+		val = max - val;
+	ucontrol->value.integer.value[0] = val;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_xr_sx);
+
+/**
+ * snd_soc_put_xr_sx - signed multi register get callback
+ * @kcontrol: mreg control
+ * @ucontrol: control element information
+ *
+ * Callback to set the value of a control that can span
+ * multiple codec registers which together forms a single
+ * signed value in a MSB/LSB manner. The control supports
+ * specifying total no of bits used to allow for bitfields
+ * across the multiple codec registers.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_put_xr_sx(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct soc_mreg_control *mc =
+		(struct soc_mreg_control *)kcontrol->private_value;
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	unsigned int regbase = mc->regbase;
+	unsigned int regcount = mc->regcount;
+	unsigned int regwshift = codec->driver->reg_word_size * BITS_PER_BYTE;
+	unsigned int regwmask = (1<<regwshift)-1;
+	unsigned int invert = mc->invert;
+	unsigned long mask = (1UL<<mc->nbits)-1;
+	long max = mc->max;
+	long val = ucontrol->value.integer.value[0];
+	unsigned int i, regval, regmask;
+	int err;
+
+	if (invert)
+		val = max - val;
+	val &= mask;
+	for (i = 0; i < regcount; i++) {
+		regval = (val >> (regwshift*(regcount-i-1))) & regwmask;
+		regmask = (mask >> (regwshift*(regcount-i-1))) & regwmask;
+		err = snd_soc_update_bits_locked(codec, regbase+i,
+				regmask, regval);
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_xr_sx);
+
+/**
+ * snd_soc_get_strobe - strobe get callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback get the value of a strobe mixer control.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_strobe(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	unsigned int reg = mc->reg;
+	unsigned int shift = mc->shift;
+	unsigned int mask = 1 << shift;
+	unsigned int invert = mc->invert != 0;
+	unsigned int val = snd_soc_read(codec, reg) & mask;
+
+	if (shift != 0 && val != 0)
+		val = val >> shift;
+	ucontrol->value.enumerated.item[0] = val ^ invert;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_strobe);
+
+/**
+ * snd_soc_put_strobe - strobe put callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback strobe a register bit to high then low (or the inverse)
+ * in one pass of a single mixer enum control.
+ *
+ * Returns 1 for success.
+ */
+int snd_soc_put_strobe(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	unsigned int reg = mc->reg;
+	unsigned int shift = mc->shift;
+	unsigned int mask = 1 << shift;
+	unsigned int invert = mc->invert != 0;
+	unsigned int strobe = ucontrol->value.enumerated.item[0] != 0;
+	unsigned int val1 = (strobe ^ invert) ? mask : 0;
+	unsigned int val2 = (strobe ^ invert) ? 0 : mask;
+	int err;
+
+	err = snd_soc_update_bits_locked(codec, reg, mask, val1);
+	if (err < 0)
+		return err;
+
+	err = snd_soc_update_bits_locked(codec, reg, mask, val2);
+	return err;
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_strobe);
+
+/**
  * snd_soc_dai_set_sysclk - configure DAI system or master clock.
  * @dai: DAI
  * @clk_id: DAI specific clock ID
@@ -3048,7 +3316,7 @@
 	if (dai->driver && dai->driver->ops->digital_mute)
 		return dai->driver->ops->digital_mute(dai, mute);
 	else
-		return -EINVAL;
+		return -ENOTSUPP;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute);
 
@@ -3060,7 +3328,7 @@
  */
 int snd_soc_register_card(struct snd_soc_card *card)
 {
-	int i;
+	int i, ret;
 
 	if (!card->name || !card->dev)
 		return -EINVAL;
@@ -3123,15 +3391,13 @@
 	INIT_LIST_HEAD(&card->dapm_dirty);
 	card->instantiated = 0;
 	mutex_init(&card->mutex);
+	mutex_init(&card->dapm_mutex);
 
-	mutex_lock(&client_mutex);
-	list_add(&card->list, &card_list);
-	snd_soc_instantiate_cards();
-	mutex_unlock(&client_mutex);
+	ret = snd_soc_instantiate_card(card);
+	if (ret != 0)
+		soc_cleanup_card_debugfs(card);
 
-	dev_dbg(card->dev, "Registered card '%s'\n", card->name);
-
-	return 0;
+	return ret;
 }
 EXPORT_SYMBOL_GPL(snd_soc_register_card);
 
@@ -3145,9 +3411,6 @@
 {
 	if (card->instantiated)
 		soc_cleanup_card_resources(card);
-	mutex_lock(&client_mutex);
-	list_del(&card->list);
-	mutex_unlock(&client_mutex);
 	dev_dbg(card->dev, "Unregistered card '%s'\n", card->name);
 
 	return 0;
@@ -3221,6 +3484,7 @@
 int snd_soc_register_dai(struct device *dev,
 		struct snd_soc_dai_driver *dai_drv)
 {
+	struct snd_soc_codec *codec;
 	struct snd_soc_dai *dai;
 
 	dev_dbg(dev, "dai register %s\n", dev_name(dev));
@@ -3238,12 +3502,23 @@
 
 	dai->dev = dev;
 	dai->driver = dai_drv;
+	dai->dapm.dev = dev;
 	if (!dai->driver->ops)
 		dai->driver->ops = &null_dai_ops;
 
 	mutex_lock(&client_mutex);
+
+	list_for_each_entry(codec, &codec_list, list) {
+		if (codec->dev == dev) {
+			dev_dbg(dev, "Mapped DAI %s to CODEC %s\n",
+				dai->name, codec->name);
+			dai->codec = codec;
+			break;
+		}
+	}
+
 	list_add(&dai->list, &dai_list);
-	snd_soc_instantiate_cards();
+
 	mutex_unlock(&client_mutex);
 
 	pr_debug("Registered DAI '%s'\n", dai->name);
@@ -3287,6 +3562,7 @@
 int snd_soc_register_dais(struct device *dev,
 		struct snd_soc_dai_driver *dai_drv, size_t count)
 {
+	struct snd_soc_codec *codec;
 	struct snd_soc_dai *dai;
 	int i, ret = 0;
 
@@ -3314,19 +3590,28 @@
 			dai->id = dai->driver->id;
 		else
 			dai->id = i;
+		dai->dapm.dev = dev;
 		if (!dai->driver->ops)
 			dai->driver->ops = &null_dai_ops;
 
 		mutex_lock(&client_mutex);
+
+		list_for_each_entry(codec, &codec_list, list) {
+			if (codec->dev == dev) {
+				dev_dbg(dev, "Mapped DAI %s to CODEC %s\n",
+					dai->name, codec->name);
+				dai->codec = codec;
+				break;
+			}
+		}
+
 		list_add(&dai->list, &dai_list);
+
 		mutex_unlock(&client_mutex);
 
 		pr_debug("Registered DAI '%s'\n", dai->name);
 	}
 
-	mutex_lock(&client_mutex);
-	snd_soc_instantiate_cards();
-	mutex_unlock(&client_mutex);
 	return 0;
 
 err:
@@ -3384,7 +3669,6 @@
 
 	mutex_lock(&client_mutex);
 	list_add(&platform->list, &platform_list);
-	snd_soc_instantiate_cards();
 	mutex_unlock(&client_mutex);
 
 	pr_debug("Registered platform '%s'\n", platform->name);
@@ -3534,18 +3818,18 @@
 		fixup_codec_formats(&dai_drv[i].capture);
 	}
 
+	mutex_lock(&client_mutex);
+	list_add(&codec->list, &codec_list);
+	mutex_unlock(&client_mutex);
+
 	/* register any DAIs */
 	if (num_dai) {
 		ret = snd_soc_register_dais(dev, dai_drv, num_dai);
 		if (ret < 0)
-			goto fail;
+			dev_err(codec->dev, "Failed to regster DAIs: %d\n",
+				ret);
 	}
 
-	mutex_lock(&client_mutex);
-	list_add(&codec->list, &codec_list);
-	snd_soc_instantiate_cards();
-	mutex_unlock(&client_mutex);
-
 	pr_debug("Registered codec '%s'\n", codec->name);
 	return 0;
 
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 1bb6d4a..90ee77d 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -52,6 +52,7 @@
 	[snd_soc_dapm_supply] = 1,
 	[snd_soc_dapm_regulator_supply] = 1,
 	[snd_soc_dapm_micbias] = 2,
+	[snd_soc_dapm_dai_link] = 2,
 	[snd_soc_dapm_dai] = 3,
 	[snd_soc_dapm_aif_in] = 3,
 	[snd_soc_dapm_aif_out] = 3,
@@ -90,9 +91,10 @@
 	[snd_soc_dapm_aif_in] = 10,
 	[snd_soc_dapm_aif_out] = 10,
 	[snd_soc_dapm_dai] = 10,
-	[snd_soc_dapm_regulator_supply] = 11,
-	[snd_soc_dapm_supply] = 11,
-	[snd_soc_dapm_post] = 12,
+	[snd_soc_dapm_dai_link] = 11,
+	[snd_soc_dapm_regulator_supply] = 12,
+	[snd_soc_dapm_supply] = 12,
+	[snd_soc_dapm_post] = 13,
 };
 
 static void pop_wait(u32 pop_time)
@@ -208,7 +210,23 @@
 	return -1;
 }
 
-static int soc_widget_update_bits(struct snd_soc_dapm_widget *w,
+static inline void soc_widget_lock(struct snd_soc_dapm_widget *w)
+{
+	if (w->codec && !w->codec->using_regmap)
+		mutex_lock(&w->codec->mutex);
+	else if (w->platform)
+		mutex_lock(&w->platform->mutex);
+}
+
+static inline void soc_widget_unlock(struct snd_soc_dapm_widget *w)
+{
+	if (w->codec && !w->codec->using_regmap)
+		mutex_unlock(&w->codec->mutex);
+	else if (w->platform)
+		mutex_unlock(&w->platform->mutex);
+}
+
+static int soc_widget_update_bits_locked(struct snd_soc_dapm_widget *w,
 	unsigned short reg, unsigned int mask, unsigned int value)
 {
 	bool change;
@@ -221,18 +239,24 @@
 		if (ret != 0)
 			return ret;
 	} else {
+		soc_widget_lock(w);
 		ret = soc_widget_read(w, reg);
-		if (ret < 0)
+		if (ret < 0) {
+			soc_widget_unlock(w);
 			return ret;
+		}
 
 		old = ret;
 		new = (old & ~mask) | (value & mask);
 		change = old != new;
 		if (change) {
 			ret = soc_widget_write(w, reg, new);
-			if (ret < 0)
+			if (ret < 0) {
+				soc_widget_unlock(w);
 				return ret;
+			}
 		}
+		soc_widget_unlock(w);
 	}
 
 	return change;
@@ -374,6 +398,7 @@
 	case snd_soc_dapm_mic:
 	case snd_soc_dapm_spk:
 	case snd_soc_dapm_line:
+	case snd_soc_dapm_dai_link:
 		p->connect = 1;
 	break;
 	/* does affect routing - dynamically connected */
@@ -682,11 +707,51 @@
 	}
 }
 
+/* add widget to list if it's not already in the list */
+static int dapm_list_add_widget(struct snd_soc_dapm_widget_list **list,
+	struct snd_soc_dapm_widget *w)
+{
+	struct snd_soc_dapm_widget_list *wlist;
+	int wlistsize, wlistentries, i;
+
+	if (*list == NULL)
+		return -EINVAL;
+
+	wlist = *list;
+
+	/* is this widget already in the list */
+	for (i = 0; i < wlist->num_widgets; i++) {
+		if (wlist->widgets[i] == w)
+			return 0;
+	}
+
+	/* allocate some new space */
+	wlistentries = wlist->num_widgets + 1;
+	wlistsize = sizeof(struct snd_soc_dapm_widget_list) +
+			wlistentries * sizeof(struct snd_soc_dapm_widget *);
+	*list = krealloc(wlist, wlistsize, GFP_KERNEL);
+	if (*list == NULL) {
+		dev_err(w->dapm->dev, "can't allocate widget list for %s\n",
+			w->name);
+		return -ENOMEM;
+	}
+	wlist = *list;
+
+	/* insert the widget */
+	dev_dbg(w->dapm->dev, "added %s in widget list pos %d\n",
+			w->name, wlist->num_widgets);
+
+	wlist->widgets[wlist->num_widgets] = w;
+	wlist->num_widgets++;
+	return 1;
+}
+
 /*
  * Recursively check for a completed path to an active or physically connected
  * output widget. Returns number of complete paths.
  */
-static int is_connected_output_ep(struct snd_soc_dapm_widget *widget)
+static int is_connected_output_ep(struct snd_soc_dapm_widget *widget,
+	struct snd_soc_dapm_widget_list **list)
 {
 	struct snd_soc_dapm_path *path;
 	int con = 0;
@@ -742,9 +807,23 @@
 		if (path->walked)
 			continue;
 
+		trace_snd_soc_dapm_output_path(widget, path);
+
 		if (path->sink && path->connect) {
 			path->walked = 1;
-			con += is_connected_output_ep(path->sink);
+
+			/* do we need to add this widget to the list ? */
+			if (list) {
+				int err;
+				err = dapm_list_add_widget(list, path->sink);
+				if (err < 0) {
+					dev_err(widget->dapm->dev, "could not add widget %s\n",
+						widget->name);
+					return con;
+				}
+			}
+
+			con += is_connected_output_ep(path->sink, list);
 		}
 	}
 
@@ -757,7 +836,8 @@
  * Recursively check for a completed path to an active or physically connected
  * input widget. Returns number of complete paths.
  */
-static int is_connected_input_ep(struct snd_soc_dapm_widget *widget)
+static int is_connected_input_ep(struct snd_soc_dapm_widget *widget,
+	struct snd_soc_dapm_widget_list **list)
 {
 	struct snd_soc_dapm_path *path;
 	int con = 0;
@@ -825,9 +905,23 @@
 		if (path->walked)
 			continue;
 
+		trace_snd_soc_dapm_input_path(widget, path);
+
 		if (path->source && path->connect) {
 			path->walked = 1;
-			con += is_connected_input_ep(path->source);
+
+			/* do we need to add this widget to the list ? */
+			if (list) {
+				int err;
+				err = dapm_list_add_widget(list, path->sink);
+				if (err < 0) {
+					dev_err(widget->dapm->dev, "could not add widget %s\n",
+						widget->name);
+					return con;
+				}
+			}
+
+			con += is_connected_input_ep(path->source, list);
 		}
 	}
 
@@ -836,6 +930,39 @@
 	return con;
 }
 
+/**
+ * snd_soc_dapm_get_connected_widgets - query audio path and it's widgets.
+ * @dai: the soc DAI.
+ * @stream: stream direction.
+ * @list: list of active widgets for this stream.
+ *
+ * Queries DAPM graph as to whether an valid audio stream path exists for
+ * the initial stream specified by name. This takes into account
+ * current mixer and mux kcontrol settings. Creates list of valid widgets.
+ *
+ * Returns the number of valid paths or negative error.
+ */
+int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
+	struct snd_soc_dapm_widget_list **list)
+{
+	struct snd_soc_card *card = dai->card;
+	int paths;
+
+	mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+	dapm_reset(card);
+
+	if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+		paths = is_connected_output_ep(dai->playback_widget, list);
+	else
+		paths = is_connected_input_ep(dai->playback_widget, list);
+
+	trace_snd_soc_dapm_connected(paths, stream);
+	dapm_clear_walk(&card->dapm);
+	mutex_unlock(&card->dapm_mutex);
+
+	return paths;
+}
+
 /*
  * Handler for generic register modifier widget.
  */
@@ -849,7 +976,7 @@
 	else
 		val = w->off_val;
 
-	soc_widget_update_bits(w, -(w->reg + 1),
+	soc_widget_update_bits_locked(w, -(w->reg + 1),
 			    w->mask << w->shift, val << w->shift);
 
 	return 0;
@@ -863,9 +990,9 @@
 		   struct snd_kcontrol *kcontrol, int event)
 {
 	if (SND_SOC_DAPM_EVENT_ON(event))
-		return regulator_enable(w->priv);
+		return regulator_enable(w->regulator);
 	else
-		return regulator_disable_deferred(w->priv, w->shift);
+		return regulator_disable_deferred(w->regulator, w->shift);
 }
 EXPORT_SYMBOL_GPL(dapm_regulator_event);
 
@@ -892,9 +1019,9 @@
 
 	DAPM_UPDATE_STAT(w, power_checks);
 
-	in = is_connected_input_ep(w);
+	in = is_connected_input_ep(w, NULL);
 	dapm_clear_walk(w->dapm);
-	out = is_connected_output_ep(w);
+	out = is_connected_output_ep(w, NULL);
 	dapm_clear_walk(w->dapm);
 	return out != 0 && in != 0;
 }
@@ -903,7 +1030,10 @@
 {
 	DAPM_UPDATE_STAT(w, power_checks);
 
-	return w->active;
+	if (w->active)
+		return w->active;
+
+	return dapm_generic_check_power(w);
 }
 
 /* Check to see if an ADC has power */
@@ -914,7 +1044,7 @@
 	DAPM_UPDATE_STAT(w, power_checks);
 
 	if (w->active) {
-		in = is_connected_input_ep(w);
+		in = is_connected_input_ep(w, NULL);
 		dapm_clear_walk(w->dapm);
 		return in != 0;
 	} else {
@@ -930,7 +1060,7 @@
 	DAPM_UPDATE_STAT(w, power_checks);
 
 	if (w->active) {
-		out = is_connected_output_ep(w);
+		out = is_connected_output_ep(w, NULL);
 		dapm_clear_walk(w->dapm);
 		return out != 0;
 	} else {
@@ -1107,7 +1237,7 @@
 			"pop test : Applying 0x%x/0x%x to %x in %dms\n",
 			value, mask, reg, card->pop_time);
 		pop_wait(card->pop_time);
-		soc_widget_update_bits(w, reg, mask, value);
+		soc_widget_update_bits_locked(w, reg, mask, value);
 	}
 
 	list_for_each_entry(w, pending, power_list) {
@@ -1237,7 +1367,7 @@
 			       w->name, ret);
 	}
 
-	ret = snd_soc_update_bits(w->codec, update->reg, update->mask,
+	ret = soc_widget_update_bits_locked(w, update->reg, update->mask,
 				  update->val);
 	if (ret < 0)
 		pr_err("%s DAPM update failed: %d\n", w->name, ret);
@@ -1421,12 +1551,10 @@
 	trace_snd_soc_dapm_start(card);
 
 	list_for_each_entry(d, &card->dapm_list, list) {
-		if (d->n_widgets || d->codec == NULL) {
-			if (d->idle_bias_off)
-				d->target_bias_level = SND_SOC_BIAS_OFF;
-			else
-				d->target_bias_level = SND_SOC_BIAS_STANDBY;
-		}
+		if (d->idle_bias_off)
+			d->target_bias_level = SND_SOC_BIAS_OFF;
+		else
+			d->target_bias_level = SND_SOC_BIAS_STANDBY;
 	}
 
 	dapm_reset(card);
@@ -1471,32 +1599,6 @@
 
 	}
 
-	/* If there are no DAPM widgets then try to figure out power from the
-	 * event type.
-	 */
-	if (!dapm->n_widgets) {
-		switch (event) {
-		case SND_SOC_DAPM_STREAM_START:
-		case SND_SOC_DAPM_STREAM_RESUME:
-			dapm->target_bias_level = SND_SOC_BIAS_ON;
-			break;
-		case SND_SOC_DAPM_STREAM_STOP:
-			if (dapm->codec && dapm->codec->active)
-				dapm->target_bias_level = SND_SOC_BIAS_ON;
-			else
-				dapm->target_bias_level = SND_SOC_BIAS_STANDBY;
-			break;
-		case SND_SOC_DAPM_STREAM_SUSPEND:
-			dapm->target_bias_level = SND_SOC_BIAS_STANDBY;
-			break;
-		case SND_SOC_DAPM_STREAM_NOP:
-			dapm->target_bias_level = dapm->bias_level;
-			break;
-		default:
-			break;
-		}
-	}
-
 	/* Force all contexts in the card to the same bias state if
 	 * they're not ground referenced.
 	 */
@@ -1560,9 +1662,9 @@
 	if (!buf)
 		return -ENOMEM;
 
-	in = is_connected_input_ep(w);
+	in = is_connected_input_ep(w, NULL);
 	dapm_clear_walk(w->dapm);
-	out = is_connected_output_ep(w);
+	out = is_connected_output_ep(w, NULL);
 	dapm_clear_walk(w->dapm);
 
 	ret = snprintf(buf, PAGE_SIZE, "%s: %s%s  in %d out %d",
@@ -1709,7 +1811,7 @@
 #endif
 
 /* test and update the power status of a mux widget */
-int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
+static int soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
 				 struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e)
 {
 	struct snd_soc_dapm_path *path;
@@ -1746,12 +1848,26 @@
 		dapm_power_widgets(widget->dapm, SND_SOC_DAPM_STREAM_NOP);
 	}
 
-	return 0;
+	return found;
+}
+
+int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
+		struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e)
+{
+	struct snd_soc_card *card = widget->dapm->card;
+	int ret;
+
+	mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+	ret = soc_dapm_mux_update_power(widget, kcontrol, mux, e);
+	mutex_unlock(&card->dapm_mutex);
+	if (ret > 0)
+		soc_dpcm_runtime_update(widget);
+	return ret;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_mux_update_power);
 
 /* test and update the power status of a mixer or switch widget */
-int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
+static int soc_dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
 				   struct snd_kcontrol *kcontrol, int connect)
 {
 	struct snd_soc_dapm_path *path;
@@ -1778,7 +1894,21 @@
 		dapm_power_widgets(widget->dapm, SND_SOC_DAPM_STREAM_NOP);
 	}
 
-	return 0;
+	return found;
+}
+
+int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
+				struct snd_kcontrol *kcontrol, int connect)
+{
+	struct snd_soc_card *card = widget->dapm->card;
+	int ret;
+
+	mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+	ret = soc_dapm_mixer_update_power(widget, kcontrol, connect);
+	mutex_unlock(&card->dapm_mutex);
+	if (ret > 0)
+		soc_dpcm_runtime_update(widget);
+	return ret;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_mixer_update_power);
 
@@ -1939,6 +2069,8 @@
  */
 int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm)
 {
+	int ret;
+
 	/*
 	 * Suppress early reports (eg, jacks syncing their state) to avoid
 	 * silly DAPM runs during card startup.
@@ -1946,7 +2078,10 @@
 	if (!dapm->card || !dapm->card->instantiated)
 		return 0;
 
-	return dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP);
+	mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+	ret = dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP);
+	mutex_unlock(&dapm->card->dapm_mutex);
+	return ret;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_sync);
 
@@ -2055,6 +2190,7 @@
 	case snd_soc_dapm_aif_in:
 	case snd_soc_dapm_aif_out:
 	case snd_soc_dapm_dai:
+	case snd_soc_dapm_dai_link:
 		list_add(&path->list, &dapm->card->paths);
 		list_add(&path->list_sink, &wsink->sources);
 		list_add(&path->list_source, &wsource->sinks);
@@ -2110,19 +2246,21 @@
 int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm,
 			    const struct snd_soc_dapm_route *route, int num)
 {
-	int i, ret;
+	int i, ret = 0;
 
+	mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT);
 	for (i = 0; i < num; i++) {
 		ret = snd_soc_dapm_add_route(dapm, route);
 		if (ret < 0) {
 			dev_err(dapm->dev, "Failed to add route %s->%s\n",
 				route->source, route->sink);
-			return ret;
+			break;
 		}
 		route++;
 	}
+	mutex_unlock(&dapm->card->dapm_mutex);
 
-	return 0;
+	return ret;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_add_routes);
 
@@ -2193,12 +2331,14 @@
 	int i, err;
 	int ret = 0;
 
+	mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT);
 	for (i = 0; i < num; i++) {
 		err = snd_soc_dapm_weak_route(dapm, route);
 		if (err)
 			ret = err;
 		route++;
 	}
+	mutex_unlock(&dapm->card->dapm_mutex);
 
 	return ret;
 }
@@ -2217,6 +2357,8 @@
 	struct snd_soc_dapm_widget *w;
 	unsigned int val;
 
+	mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT);
+
 	list_for_each_entry(w, &dapm->card->widgets, list)
 	{
 		if (w->new)
@@ -2226,8 +2368,10 @@
 			w->kcontrols = kzalloc(w->num_kcontrols *
 						sizeof(struct snd_kcontrol *),
 						GFP_KERNEL);
-			if (!w->kcontrols)
+			if (!w->kcontrols) {
+				mutex_unlock(&dapm->card->dapm_mutex);
 				return -ENOMEM;
+			}
 		}
 
 		switch(w->id) {
@@ -2267,6 +2411,7 @@
 	}
 
 	dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP);
+	mutex_unlock(&dapm->card->dapm_mutex);
 	return 0;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets);
@@ -2326,6 +2471,7 @@
 	struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
 	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
 	struct snd_soc_codec *codec = widget->codec;
+	struct snd_soc_card *card = codec->card;
 	struct soc_mixer_control *mc =
 		(struct soc_mixer_control *)kcontrol->private_value;
 	unsigned int reg = mc->reg;
@@ -2352,7 +2498,7 @@
 		/* old connection must be powered down */
 		connect = invert ? 1 : 0;
 
-	mutex_lock(&codec->mutex);
+	mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
 
 	change = snd_soc_test_bits(widget->codec, reg, mask, val);
 	if (change) {
@@ -2368,13 +2514,13 @@
 			update.val = val;
 			widget->dapm->update = &update;
 
-			snd_soc_dapm_mixer_update_power(widget, kcontrol, connect);
+			soc_dapm_mixer_update_power(widget, kcontrol, connect);
 
 			widget->dapm->update = NULL;
 		}
 	}
 
-	mutex_unlock(&codec->mutex);
+	mutex_unlock(&card->dapm_mutex);
 	return 0;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw);
@@ -2423,6 +2569,7 @@
 	struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
 	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
 	struct snd_soc_codec *codec = widget->codec;
+	struct snd_soc_card *card = codec->card;
 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
 	unsigned int val, mux, change;
 	unsigned int mask, bitmask;
@@ -2443,7 +2590,7 @@
 		mask |= (bitmask - 1) << e->shift_r;
 	}
 
-	mutex_lock(&codec->mutex);
+	mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
 
 	change = snd_soc_test_bits(widget->codec, e->reg, mask, val);
 	if (change) {
@@ -2459,13 +2606,13 @@
 			update.val = val;
 			widget->dapm->update = &update;
 
-			snd_soc_dapm_mux_update_power(widget, kcontrol, mux, e);
+			soc_dapm_mux_update_power(widget, kcontrol, mux, e);
 
 			widget->dapm->update = NULL;
 		}
 	}
 
-	mutex_unlock(&codec->mutex);
+	mutex_unlock(&card->dapm_mutex);
 	return change;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double);
@@ -2502,6 +2649,7 @@
 	struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
 	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
 	struct snd_soc_codec *codec = widget->codec;
+	struct snd_soc_card *card = codec->card;
 	struct soc_enum *e =
 		(struct soc_enum *)kcontrol->private_value;
 	int change;
@@ -2511,7 +2659,7 @@
 	if (ucontrol->value.enumerated.item[0] >= e->max)
 		return -EINVAL;
 
-	mutex_lock(&codec->mutex);
+	mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
 
 	change = widget->value != ucontrol->value.enumerated.item[0];
 	if (change) {
@@ -2520,11 +2668,11 @@
 
 			widget->value = ucontrol->value.enumerated.item[0];
 
-			snd_soc_dapm_mux_update_power(widget, kcontrol, widget->value, e);
+			soc_dapm_mux_update_power(widget, kcontrol, widget->value, e);
 		}
 	}
 
-	mutex_unlock(&codec->mutex);
+	mutex_unlock(&card->dapm_mutex);
 	return ret;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_virt);
@@ -2589,6 +2737,7 @@
 	struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
 	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
 	struct snd_soc_codec *codec = widget->codec;
+	struct snd_soc_card *card = codec->card;
 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
 	unsigned int val, mux, change;
 	unsigned int mask;
@@ -2607,7 +2756,7 @@
 		mask |= e->mask << e->shift_r;
 	}
 
-	mutex_lock(&codec->mutex);
+	mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
 
 	change = snd_soc_test_bits(widget->codec, e->reg, mask, val);
 	if (change) {
@@ -2623,13 +2772,13 @@
 			update.val = val;
 			widget->dapm->update = &update;
 
-			snd_soc_dapm_mux_update_power(widget, kcontrol, mux, e);
+			soc_dapm_mux_update_power(widget, kcontrol, mux, e);
 
 			widget->dapm->update = NULL;
 		}
 	}
 
-	mutex_unlock(&codec->mutex);
+	mutex_unlock(&card->dapm_mutex);
 	return change;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_put_value_enum_double);
@@ -2666,12 +2815,12 @@
 	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
 	const char *pin = (const char *)kcontrol->private_value;
 
-	mutex_lock(&card->mutex);
+	mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
 
 	ucontrol->value.integer.value[0] =
 		snd_soc_dapm_get_pin_status(&card->dapm, pin);
 
-	mutex_unlock(&card->mutex);
+	mutex_unlock(&card->dapm_mutex);
 
 	return 0;
 }
@@ -2689,17 +2838,16 @@
 	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
 	const char *pin = (const char *)kcontrol->private_value;
 
-	mutex_lock(&card->mutex);
+	mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
 
 	if (ucontrol->value.integer.value[0])
 		snd_soc_dapm_enable_pin(&card->dapm, pin);
 	else
 		snd_soc_dapm_disable_pin(&card->dapm, pin);
 
+	mutex_unlock(&card->dapm_mutex);
+
 	snd_soc_dapm_sync(&card->dapm);
-
-	mutex_unlock(&card->mutex);
-
 	return 0;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_put_pin_switch);
@@ -2717,9 +2865,9 @@
 
 	switch (w->id) {
 	case snd_soc_dapm_regulator_supply:
-		w->priv = devm_regulator_get(dapm->dev, w->name);
-		if (IS_ERR(w->priv)) {
-			ret = PTR_ERR(w->priv);
+		w->regulator = devm_regulator_get(dapm->dev, w->name);
+		if (IS_ERR(w->regulator)) {
+			ret = PTR_ERR(w->regulator);
 			dev_err(dapm->dev, "Failed to request %s: %d\n",
 				w->name, ret);
 			return NULL;
@@ -2771,6 +2919,7 @@
 	case snd_soc_dapm_hp:
 	case snd_soc_dapm_mic:
 	case snd_soc_dapm_line:
+	case snd_soc_dapm_dai_link:
 		w->power_check = dapm_generic_check_power;
 		break;
 	case snd_soc_dapm_supply:
@@ -2816,21 +2965,177 @@
 {
 	struct snd_soc_dapm_widget *w;
 	int i;
+	int ret = 0;
 
+	mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT);
 	for (i = 0; i < num; i++) {
 		w = snd_soc_dapm_new_control(dapm, widget);
 		if (!w) {
 			dev_err(dapm->dev,
 				"ASoC: Failed to create DAPM control %s\n",
 				widget->name);
-			return -ENOMEM;
+			ret = -ENOMEM;
+			break;
 		}
 		widget++;
 	}
-	return 0;
+	mutex_unlock(&dapm->card->dapm_mutex);
+	return ret;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_new_controls);
 
+static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w,
+				  struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_dapm_path *source_p, *sink_p;
+	struct snd_soc_dai *source, *sink;
+	const struct snd_soc_pcm_stream *config = w->params;
+	struct snd_pcm_substream substream;
+	struct snd_pcm_hw_params *params = NULL;
+	u64 fmt;
+	int ret;
+
+	BUG_ON(!config);
+	BUG_ON(list_empty(&w->sources) || list_empty(&w->sinks));
+
+	/* We only support a single source and sink, pick the first */
+	source_p = list_first_entry(&w->sources, struct snd_soc_dapm_path,
+				    list_sink);
+	sink_p = list_first_entry(&w->sinks, struct snd_soc_dapm_path,
+				  list_source);
+
+	BUG_ON(!source_p || !sink_p);
+	BUG_ON(!sink_p->source || !source_p->sink);
+	BUG_ON(!source_p->source || !sink_p->sink);
+
+	source = source_p->source->priv;
+	sink = sink_p->sink->priv;
+
+	/* Be a little careful as we don't want to overflow the mask array */
+	if (config->formats) {
+		fmt = ffs(config->formats) - 1;
+	} else {
+		dev_warn(w->dapm->dev, "Invalid format %llx specified\n",
+			 config->formats);
+		fmt = 0;
+	}
+
+	/* Currently very limited parameter selection */
+	params = kzalloc(sizeof(*params), GFP_KERNEL);
+	if (!params) {
+		ret = -ENOMEM;
+		goto out;
+	}
+	snd_mask_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), fmt);
+
+	hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE)->min =
+		config->rate_min;
+	hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE)->max =
+		config->rate_max;
+
+	hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS)->min
+		= config->channels_min;
+	hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS)->max
+		= config->channels_max;
+
+	memset(&substream, 0, sizeof(substream));
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		if (source->driver->ops && source->driver->ops->hw_params) {
+			substream.stream = SNDRV_PCM_STREAM_CAPTURE;
+			ret = source->driver->ops->hw_params(&substream,
+							     params, source);
+			if (ret != 0) {
+				dev_err(source->dev,
+					"hw_params() failed: %d\n", ret);
+				goto out;
+			}
+		}
+
+		if (sink->driver->ops && sink->driver->ops->hw_params) {
+			substream.stream = SNDRV_PCM_STREAM_PLAYBACK;
+			ret = sink->driver->ops->hw_params(&substream, params,
+							   sink);
+			if (ret != 0) {
+				dev_err(sink->dev,
+					"hw_params() failed: %d\n", ret);
+				goto out;
+			}
+		}
+		break;
+
+	case SND_SOC_DAPM_POST_PMU:
+		ret = snd_soc_dai_digital_mute(sink, 0);
+		if (ret != 0 && ret != -ENOTSUPP)
+			dev_warn(sink->dev, "Failed to unmute: %d\n", ret);
+		ret = 0;
+		break;
+
+	case SND_SOC_DAPM_PRE_PMD:
+		ret = snd_soc_dai_digital_mute(sink, 1);
+		if (ret != 0 && ret != -ENOTSUPP)
+			dev_warn(sink->dev, "Failed to mute: %d\n", ret);
+		ret = 0;
+		break;
+
+	default:
+		BUG();
+		return -EINVAL;
+	}
+
+out:
+	kfree(params);
+	return ret;
+}
+
+int snd_soc_dapm_new_pcm(struct snd_soc_card *card,
+			 const struct snd_soc_pcm_stream *params,
+			 struct snd_soc_dapm_widget *source,
+			 struct snd_soc_dapm_widget *sink)
+{
+	struct snd_soc_dapm_route routes[2];
+	struct snd_soc_dapm_widget template;
+	struct snd_soc_dapm_widget *w;
+	size_t len;
+	char *link_name;
+
+	len = strlen(source->name) + strlen(sink->name) + 2;
+	link_name = devm_kzalloc(card->dev, len, GFP_KERNEL);
+	if (!link_name)
+		return -ENOMEM;
+	snprintf(link_name, len, "%s-%s", source->name, sink->name);
+
+	memset(&template, 0, sizeof(template));
+	template.reg = SND_SOC_NOPM;
+	template.id = snd_soc_dapm_dai_link;
+	template.name = link_name;
+	template.event = snd_soc_dai_link_event;
+	template.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_PRE_PMD;
+
+	dev_dbg(card->dev, "adding %s widget\n", link_name);
+
+	w = snd_soc_dapm_new_control(&card->dapm, &template);
+	if (!w) {
+		dev_err(card->dev, "Failed to create %s widget\n",
+			link_name);
+		return -ENOMEM;
+	}
+
+	w->params = params;
+
+	memset(&routes, 0, sizeof(routes));
+
+	routes[0].source = source->name;
+	routes[0].sink = link_name;
+	routes[1].source = link_name;
+	routes[1].sink = sink->name;
+
+	return snd_soc_dapm_add_routes(&card->dapm, routes,
+				       ARRAY_SIZE(routes));
+}
+
 int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm,
 				 struct snd_soc_dai *dai)
 {
@@ -2934,37 +3239,61 @@
 	return 0;
 }
 
-static void soc_dapm_stream_event(struct snd_soc_dapm_context *dapm,
-				  int stream, struct snd_soc_dai *dai,
-				  int event)
+static void soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream,
+	int event)
 {
-	struct snd_soc_dapm_widget *w;
 
-	if (stream == SNDRV_PCM_STREAM_PLAYBACK)
-		w = dai->playback_widget;
-	else
-		w = dai->capture_widget;
+	struct snd_soc_dapm_widget *w_cpu, *w_codec;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
 
-	if (!w)
-		return;
-
-	dapm_mark_dirty(w, "stream event");
-
-	switch (event) {
-	case SND_SOC_DAPM_STREAM_START:
-		w->active = 1;
-		break;
-	case SND_SOC_DAPM_STREAM_STOP:
-		w->active = 0;
-		break;
-	case SND_SOC_DAPM_STREAM_SUSPEND:
-	case SND_SOC_DAPM_STREAM_RESUME:
-	case SND_SOC_DAPM_STREAM_PAUSE_PUSH:
-	case SND_SOC_DAPM_STREAM_PAUSE_RELEASE:
-		break;
+	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		w_cpu = cpu_dai->playback_widget;
+		w_codec = codec_dai->playback_widget;
+	} else {
+		w_cpu = cpu_dai->capture_widget;
+		w_codec = codec_dai->capture_widget;
 	}
 
-	dapm_power_widgets(dapm, event);
+	if (w_cpu) {
+
+		dapm_mark_dirty(w_cpu, "stream event");
+
+		switch (event) {
+		case SND_SOC_DAPM_STREAM_START:
+			w_cpu->active = 1;
+			break;
+		case SND_SOC_DAPM_STREAM_STOP:
+			w_cpu->active = 0;
+			break;
+		case SND_SOC_DAPM_STREAM_SUSPEND:
+		case SND_SOC_DAPM_STREAM_RESUME:
+		case SND_SOC_DAPM_STREAM_PAUSE_PUSH:
+		case SND_SOC_DAPM_STREAM_PAUSE_RELEASE:
+			break;
+		}
+	}
+
+	if (w_codec) {
+
+		dapm_mark_dirty(w_codec, "stream event");
+
+		switch (event) {
+		case SND_SOC_DAPM_STREAM_START:
+			w_codec->active = 1;
+			break;
+		case SND_SOC_DAPM_STREAM_STOP:
+			w_codec->active = 0;
+			break;
+		case SND_SOC_DAPM_STREAM_SUSPEND:
+		case SND_SOC_DAPM_STREAM_RESUME:
+		case SND_SOC_DAPM_STREAM_PAUSE_PUSH:
+		case SND_SOC_DAPM_STREAM_PAUSE_RELEASE:
+			break;
+		}
+	}
+
+	dapm_power_widgets(&rtd->card->dapm, event);
 }
 
 /**
@@ -2978,15 +3307,14 @@
  *
  * Returns 0 for success else error.
  */
-int snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream,
-			      struct snd_soc_dai *dai, int event)
+void snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream,
+			      int event)
 {
-	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_card *card = rtd->card;
 
-	mutex_lock(&codec->mutex);
-	soc_dapm_stream_event(&codec->dapm, stream, dai, event);
-	mutex_unlock(&codec->mutex);
-	return 0;
+	mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+	soc_dapm_stream_event(rtd, stream, event);
+	mutex_unlock(&card->dapm_mutex);
 }
 
 /**
diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c
index ee4353f..7f8b3b7 100644
--- a/sound/soc/soc-jack.c
+++ b/sound/soc/soc-jack.c
@@ -36,6 +36,7 @@
 int snd_soc_jack_new(struct snd_soc_codec *codec, const char *id, int type,
 		     struct snd_soc_jack *jack)
 {
+	mutex_init(&jack->mutex);
 	jack->codec = codec;
 	INIT_LIST_HEAD(&jack->pins);
 	INIT_LIST_HEAD(&jack->jack_zones);
@@ -75,7 +76,7 @@
 	codec = jack->codec;
 	dapm =  &codec->dapm;
 
-	mutex_lock(&codec->mutex);
+	mutex_lock(&jack->mutex);
 
 	oldstatus = jack->status;
 
@@ -109,7 +110,7 @@
 	snd_jack_report(jack->jack, jack->status);
 
 out:
-	mutex_unlock(&codec->mutex);
+	mutex_unlock(&jack->mutex);
 }
 EXPORT_SYMBOL_GPL(snd_soc_jack_report);
 
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index 0ad8dca..bedd171 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -22,12 +22,38 @@
 #include <linux/pm_runtime.h>
 #include <linux/slab.h>
 #include <linux/workqueue.h>
+#include <linux/export.h>
+#include <linux/debugfs.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
+#include <sound/soc-dpcm.h>
 #include <sound/initval.h>
 
+#define DPCM_MAX_BE_USERS	8
+
+/* DPCM stream event, send event to FE and all active BEs. */
+static int dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir,
+	int event)
+{
+	struct snd_soc_dpcm *dpcm;
+
+	list_for_each_entry(dpcm, &fe->dpcm[dir].be_clients, list_be) {
+
+		struct snd_soc_pcm_runtime *be = dpcm->be;
+
+		dev_dbg(be->dev, "pm: BE %s event %d dir %d\n",
+				be->dai_link->name, event, dir);
+
+		snd_soc_dapm_stream_event(be, dir, event);
+	}
+
+	snd_soc_dapm_stream_event(fe, dir, event);
+
+	return 0;
+}
+
 static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream,
 					struct snd_soc_dai *soc_dai)
 {
@@ -156,6 +182,10 @@
 		}
 	}
 
+	/* Dynamic PCM DAI links compat checks use dynamic capabilities */
+	if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm)
+		goto dynamic;
+
 	/* Check that the codec and cpu DAIs are compatible */
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 		runtime->hw.rate_min =
@@ -248,6 +278,7 @@
 	pr_debug("asoc: min rate %d max rate %d\n", runtime->hw.rate_min,
 		 runtime->hw.rate_max);
 
+dynamic:
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 		cpu_dai->playback_active++;
 		codec_dai->playback_active++;
@@ -308,7 +339,7 @@
 	if (codec_dai->pop_wait == 1) {
 		codec_dai->pop_wait = 0;
 		snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK,
-					  codec_dai, SND_SOC_DAPM_STREAM_STOP);
+					  SND_SOC_DAPM_STREAM_STOP);
 	}
 
 	mutex_unlock(&rtd->pcm_mutex);
@@ -373,7 +404,6 @@
 			/* powered down playback stream now */
 			snd_soc_dapm_stream_event(rtd,
 						  SNDRV_PCM_STREAM_PLAYBACK,
-						  codec_dai,
 						  SND_SOC_DAPM_STREAM_STOP);
 		} else {
 			/* start delayed pop wq here for playback streams */
@@ -384,7 +414,7 @@
 	} else {
 		/* capture streams can be powered down now */
 		snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_CAPTURE,
-					  codec_dai, SND_SOC_DAPM_STREAM_STOP);
+					  SND_SOC_DAPM_STREAM_STOP);
 	}
 
 	mutex_unlock(&rtd->pcm_mutex);
@@ -453,8 +483,8 @@
 		cancel_delayed_work(&rtd->delayed_work);
 	}
 
-	snd_soc_dapm_stream_event(rtd, substream->stream, codec_dai,
-				  SND_SOC_DAPM_STREAM_START);
+	snd_soc_dapm_stream_event(rtd, substream->stream,
+			SND_SOC_DAPM_STREAM_START);
 
 	snd_soc_dai_digital_mute(codec_dai, 0);
 
@@ -602,6 +632,34 @@
 	return 0;
 }
 
+static int soc_pcm_bespoke_trigger(struct snd_pcm_substream *substream,
+				   int cmd)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_platform *platform = rtd->platform;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	int ret;
+
+	if (codec_dai->driver->ops->bespoke_trigger) {
+		ret = codec_dai->driver->ops->bespoke_trigger(substream, cmd, codec_dai);
+		if (ret < 0)
+			return ret;
+	}
+
+	if (platform->driver->bespoke_trigger) {
+		ret = platform->driver->bespoke_trigger(substream, cmd);
+		if (ret < 0)
+			return ret;
+	}
+
+	if (cpu_dai->driver->ops->bespoke_trigger) {
+		ret = cpu_dai->driver->ops->bespoke_trigger(substream, cmd, cpu_dai);
+		if (ret < 0)
+			return ret;
+	}
+	return 0;
+}
 /*
  * soc level wrapper for pointer callback
  * If cpu_dai, codec_dai, platform driver has the delay callback, than
@@ -634,6 +692,1308 @@
 	return offset;
 }
 
+/* connect a FE and BE */
+static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe,
+		struct snd_soc_pcm_runtime *be, int stream)
+{
+	struct snd_soc_dpcm *dpcm;
+
+	/* only add new dpcms */
+	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+		if (dpcm->be == be && dpcm->fe == fe)
+			return 0;
+	}
+
+	dpcm = kzalloc(sizeof(struct snd_soc_dpcm), GFP_KERNEL);
+	if (!dpcm)
+		return -ENOMEM;
+
+	dpcm->be = be;
+	dpcm->fe = fe;
+	be->dpcm[stream].runtime = fe->dpcm[stream].runtime;
+	dpcm->state = SND_SOC_DPCM_LINK_STATE_NEW;
+	list_add(&dpcm->list_be, &fe->dpcm[stream].be_clients);
+	list_add(&dpcm->list_fe, &be->dpcm[stream].fe_clients);
+
+	dev_dbg(fe->dev, "  connected new DPCM %s path %s %s %s\n",
+			stream ? "capture" : "playback",  fe->dai_link->name,
+			stream ? "<-" : "->", be->dai_link->name);
+
+#ifdef CONFIG_DEBUG_FS
+	dpcm->debugfs_state = debugfs_create_u32(be->dai_link->name, 0644,
+			fe->debugfs_dpcm_root, &dpcm->state);
+#endif
+	return 1;
+}
+
+/* reparent a BE onto another FE */
+static void dpcm_be_reparent(struct snd_soc_pcm_runtime *fe,
+			struct snd_soc_pcm_runtime *be, int stream)
+{
+	struct snd_soc_dpcm *dpcm;
+	struct snd_pcm_substream *fe_substream, *be_substream;
+
+	/* reparent if BE is connected to other FEs */
+	if (!be->dpcm[stream].users)
+		return;
+
+	be_substream = snd_soc_dpcm_get_substream(be, stream);
+
+	list_for_each_entry(dpcm, &be->dpcm[stream].fe_clients, list_fe) {
+		if (dpcm->fe == fe)
+			continue;
+
+		dev_dbg(fe->dev, "  reparent %s path %s %s %s\n",
+			stream ? "capture" : "playback",
+			dpcm->fe->dai_link->name,
+			stream ? "<-" : "->", dpcm->be->dai_link->name);
+
+		fe_substream = snd_soc_dpcm_get_substream(dpcm->fe, stream);
+		be_substream->runtime = fe_substream->runtime;
+		break;
+	}
+}
+
+/* disconnect a BE and FE */
+static void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream)
+{
+	struct snd_soc_dpcm *dpcm, *d;
+
+	list_for_each_entry_safe(dpcm, d, &fe->dpcm[stream].be_clients, list_be) {
+		dev_dbg(fe->dev, "BE %s disconnect check for %s\n",
+				stream ? "capture" : "playback",
+				dpcm->be->dai_link->name);
+
+		if (dpcm->state != SND_SOC_DPCM_LINK_STATE_FREE)
+			continue;
+
+		dev_dbg(fe->dev, "  freed DSP %s path %s %s %s\n",
+			stream ? "capture" : "playback", fe->dai_link->name,
+			stream ? "<-" : "->", dpcm->be->dai_link->name);
+
+		/* BEs still alive need new FE */
+		dpcm_be_reparent(fe, dpcm->be, stream);
+
+#ifdef CONFIG_DEBUG_FS
+		debugfs_remove(dpcm->debugfs_state);
+#endif
+		list_del(&dpcm->list_be);
+		list_del(&dpcm->list_fe);
+		kfree(dpcm);
+	}
+}
+
+/* get BE for DAI widget and stream */
+static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card,
+		struct snd_soc_dapm_widget *widget, int stream)
+{
+	struct snd_soc_pcm_runtime *be;
+	int i;
+
+	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		for (i = 0; i < card->num_links; i++) {
+			be = &card->rtd[i];
+
+			if (be->cpu_dai->playback_widget == widget ||
+				be->codec_dai->playback_widget == widget)
+				return be;
+		}
+	} else {
+
+		for (i = 0; i < card->num_links; i++) {
+			be = &card->rtd[i];
+
+			if (be->cpu_dai->capture_widget == widget ||
+				be->codec_dai->capture_widget == widget)
+				return be;
+		}
+	}
+
+	dev_err(card->dev, "can't get %s BE for %s\n",
+		stream ? "capture" : "playback", widget->name);
+	return NULL;
+}
+
+static inline struct snd_soc_dapm_widget *
+	rtd_get_cpu_widget(struct snd_soc_pcm_runtime *rtd, int stream)
+{
+	if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+		return rtd->cpu_dai->playback_widget;
+	else
+		return rtd->cpu_dai->capture_widget;
+}
+
+static inline struct snd_soc_dapm_widget *
+	rtd_get_codec_widget(struct snd_soc_pcm_runtime *rtd, int stream)
+{
+	if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+		return rtd->codec_dai->playback_widget;
+	else
+		return rtd->codec_dai->capture_widget;
+}
+
+static int widget_in_list(struct snd_soc_dapm_widget_list *list,
+		struct snd_soc_dapm_widget *widget)
+{
+	int i;
+
+	for (i = 0; i < list->num_widgets; i++) {
+		if (widget == list->widgets[i])
+			return 1;
+	}
+
+	return 0;
+}
+
+static int dpcm_path_get(struct snd_soc_pcm_runtime *fe,
+	int stream, struct snd_soc_dapm_widget_list **list_)
+{
+	struct snd_soc_dai *cpu_dai = fe->cpu_dai;
+	struct snd_soc_dapm_widget_list *list;
+	int paths;
+
+	list = kzalloc(sizeof(struct snd_soc_dapm_widget_list) +
+			sizeof(struct snd_soc_dapm_widget *), GFP_KERNEL);
+	if (list == NULL)
+		return -ENOMEM;
+
+	/* get number of valid DAI paths and their widgets */
+	paths = snd_soc_dapm_dai_get_connected_widgets(cpu_dai, stream, &list);
+
+	dev_dbg(fe->dev, "found %d audio %s paths\n", paths,
+			stream ? "capture" : "playback");
+
+	*list_ = list;
+	return paths;
+}
+
+static inline void dpcm_path_put(struct snd_soc_dapm_widget_list **list)
+{
+	kfree(*list);
+}
+
+static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream,
+	struct snd_soc_dapm_widget_list **list_)
+{
+	struct snd_soc_dpcm *dpcm;
+	struct snd_soc_dapm_widget_list *list = *list_;
+	struct snd_soc_dapm_widget *widget;
+	int prune = 0;
+
+	/* Destroy any old FE <--> BE connections */
+	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+
+		/* is there a valid CPU DAI widget for this BE */
+		widget = rtd_get_cpu_widget(dpcm->be, stream);
+
+		/* prune the BE if it's no longer in our active list */
+		if (widget && widget_in_list(list, widget))
+			continue;
+
+		/* is there a valid CODEC DAI widget for this BE */
+		widget = rtd_get_codec_widget(dpcm->be, stream);
+
+		/* prune the BE if it's no longer in our active list */
+		if (widget && widget_in_list(list, widget))
+			continue;
+
+		dev_dbg(fe->dev, "pruning %s BE %s for %s\n",
+			stream ? "capture" : "playback",
+			dpcm->be->dai_link->name, fe->dai_link->name);
+		dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE;
+		dpcm->be->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE;
+		prune++;
+	}
+
+	dev_dbg(fe->dev, "found %d old BE paths for pruning\n", prune);
+	return prune;
+}
+
+static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream,
+	struct snd_soc_dapm_widget_list **list_)
+{
+	struct snd_soc_card *card = fe->card;
+	struct snd_soc_dapm_widget_list *list = *list_;
+	struct snd_soc_pcm_runtime *be;
+	int i, new = 0, err;
+
+	/* Create any new FE <--> BE connections */
+	for (i = 0; i < list->num_widgets; i++) {
+
+		if (list->widgets[i]->id != snd_soc_dapm_dai)
+			continue;
+
+		/* is there a valid BE rtd for this widget */
+		be = dpcm_get_be(card, list->widgets[i], stream);
+		if (!be) {
+			dev_err(fe->dev, "no BE found for %s\n",
+					list->widgets[i]->name);
+			continue;
+		}
+
+		/* make sure BE is a real BE */
+		if (!be->dai_link->no_pcm)
+			continue;
+
+		/* don't connect if FE is not running */
+		if (!fe->dpcm[stream].runtime)
+			continue;
+
+		/* newly connected FE and BE */
+		err = dpcm_be_connect(fe, be, stream);
+		if (err < 0) {
+			dev_err(fe->dev, "can't connect %s\n",
+				list->widgets[i]->name);
+			break;
+		} else if (err == 0) /* already connected */
+			continue;
+
+		/* new */
+		be->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE;
+		new++;
+	}
+
+	dev_dbg(fe->dev, "found %d new BE paths\n", new);
+	return new;
+}
+
+/*
+ * Find the corresponding BE DAIs that source or sink audio to this
+ * FE substream.
+ */
+static int dpcm_process_paths(struct snd_soc_pcm_runtime *fe,
+	int stream, struct snd_soc_dapm_widget_list **list, int new)
+{
+	if (new)
+		return dpcm_add_paths(fe, stream, list);
+	else
+		return dpcm_prune_paths(fe, stream, list);
+}
+
+static void dpcm_clear_pending_state(struct snd_soc_pcm_runtime *fe, int stream)
+{
+	struct snd_soc_dpcm *dpcm;
+
+	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be)
+		dpcm->be->dpcm[stream].runtime_update =
+						SND_SOC_DPCM_UPDATE_NO;
+}
+
+static void dpcm_be_dai_startup_unwind(struct snd_soc_pcm_runtime *fe,
+	int stream)
+{
+	struct snd_soc_dpcm *dpcm;
+
+	/* disable any enabled and non active backends */
+	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+
+		struct snd_soc_pcm_runtime *be = dpcm->be;
+		struct snd_pcm_substream *be_substream =
+			snd_soc_dpcm_get_substream(be, stream);
+
+		if (be->dpcm[stream].users == 0)
+			dev_err(be->dev, "no users %s at close - state %d\n",
+				stream ? "capture" : "playback",
+				be->dpcm[stream].state);
+
+		if (--be->dpcm[stream].users != 0)
+			continue;
+
+		if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN)
+			continue;
+
+		soc_pcm_close(be_substream);
+		be_substream->runtime = NULL;
+		be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
+	}
+}
+
+static int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream)
+{
+	struct snd_soc_dpcm *dpcm;
+	int err, count = 0;
+
+	/* only startup BE DAIs that are either sinks or sources to this FE DAI */
+	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+
+		struct snd_soc_pcm_runtime *be = dpcm->be;
+		struct snd_pcm_substream *be_substream =
+			snd_soc_dpcm_get_substream(be, stream);
+
+		/* is this op for this BE ? */
+		if (!snd_soc_dpcm_be_can_update(fe, be, stream))
+			continue;
+
+		/* first time the dpcm is open ? */
+		if (be->dpcm[stream].users == DPCM_MAX_BE_USERS)
+			dev_err(be->dev, "too many users %s at open %d\n",
+				stream ? "capture" : "playback",
+				be->dpcm[stream].state);
+
+		if (be->dpcm[stream].users++ != 0)
+			continue;
+
+		if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_NEW) &&
+		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_CLOSE))
+			continue;
+
+		dev_dbg(be->dev, "dpcm: open BE %s\n", be->dai_link->name);
+
+		be_substream->runtime = be->dpcm[stream].runtime;
+		err = soc_pcm_open(be_substream);
+		if (err < 0) {
+			dev_err(be->dev, "BE open failed %d\n", err);
+			be->dpcm[stream].users--;
+			if (be->dpcm[stream].users < 0)
+				dev_err(be->dev, "no users %s at unwind %d\n",
+					stream ? "capture" : "playback",
+					be->dpcm[stream].state);
+
+			be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
+			goto unwind;
+		}
+
+		be->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN;
+		count++;
+	}
+
+	return count;
+
+unwind:
+	/* disable any enabled and non active backends */
+	list_for_each_entry_continue_reverse(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+		struct snd_soc_pcm_runtime *be = dpcm->be;
+		struct snd_pcm_substream *be_substream =
+			snd_soc_dpcm_get_substream(be, stream);
+
+		if (!snd_soc_dpcm_be_can_update(fe, be, stream))
+			continue;
+
+		if (be->dpcm[stream].users == 0)
+			dev_err(be->dev, "no users %s at close %d\n",
+				stream ? "capture" : "playback",
+				be->dpcm[stream].state);
+
+		if (--be->dpcm[stream].users != 0)
+			continue;
+
+		if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN)
+			continue;
+
+		soc_pcm_close(be_substream);
+		be_substream->runtime = NULL;
+		be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
+	}
+
+	return err;
+}
+
+static void dpcm_set_fe_runtime(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	struct snd_soc_dai_driver *cpu_dai_drv = cpu_dai->driver;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		runtime->hw.rate_min = cpu_dai_drv->playback.rate_min;
+		runtime->hw.rate_max = cpu_dai_drv->playback.rate_max;
+		runtime->hw.channels_min = cpu_dai_drv->playback.channels_min;
+		runtime->hw.channels_max = cpu_dai_drv->playback.channels_max;
+		runtime->hw.formats &= cpu_dai_drv->playback.formats;
+		runtime->hw.rates = cpu_dai_drv->playback.rates;
+	} else {
+		runtime->hw.rate_min = cpu_dai_drv->capture.rate_min;
+		runtime->hw.rate_max = cpu_dai_drv->capture.rate_max;
+		runtime->hw.channels_min = cpu_dai_drv->capture.channels_min;
+		runtime->hw.channels_max = cpu_dai_drv->capture.channels_max;
+		runtime->hw.formats &= cpu_dai_drv->capture.formats;
+		runtime->hw.rates = cpu_dai_drv->capture.rates;
+	}
+}
+
+static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream)
+{
+	struct snd_soc_pcm_runtime *fe = fe_substream->private_data;
+	struct snd_pcm_runtime *runtime = fe_substream->runtime;
+	int stream = fe_substream->stream, ret = 0;
+
+	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
+
+	ret = dpcm_be_dai_startup(fe, fe_substream->stream);
+	if (ret < 0) {
+		dev_err(fe->dev,"dpcm: failed to start some BEs %d\n", ret);
+		goto be_err;
+	}
+
+	dev_dbg(fe->dev, "dpcm: open FE %s\n", fe->dai_link->name);
+
+	/* start the DAI frontend */
+	ret = soc_pcm_open(fe_substream);
+	if (ret < 0) {
+		dev_err(fe->dev,"dpcm: failed to start FE %d\n", ret);
+		goto unwind;
+	}
+
+	fe->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN;
+
+	dpcm_set_fe_runtime(fe_substream);
+	snd_pcm_limit_hw_rates(runtime);
+
+	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+	return 0;
+
+unwind:
+	dpcm_be_dai_startup_unwind(fe, fe_substream->stream);
+be_err:
+	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+	return ret;
+}
+
+static int dpcm_be_dai_shutdown(struct snd_soc_pcm_runtime *fe, int stream)
+{
+	struct snd_soc_dpcm *dpcm;
+
+	/* only shutdown BEs that are either sinks or sources to this FE DAI */
+	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+
+		struct snd_soc_pcm_runtime *be = dpcm->be;
+		struct snd_pcm_substream *be_substream =
+			snd_soc_dpcm_get_substream(be, stream);
+
+		/* is this op for this BE ? */
+		if (!snd_soc_dpcm_be_can_update(fe, be, stream))
+			continue;
+
+		if (be->dpcm[stream].users == 0)
+			dev_err(be->dev, "no users %s at close - state %d\n",
+				stream ? "capture" : "playback",
+				be->dpcm[stream].state);
+
+		if (--be->dpcm[stream].users != 0)
+			continue;
+
+		if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) &&
+		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN))
+			continue;
+
+		dev_dbg(be->dev, "dpcm: close BE %s\n",
+			dpcm->fe->dai_link->name);
+
+		soc_pcm_close(be_substream);
+		be_substream->runtime = NULL;
+
+		be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
+	}
+	return 0;
+}
+
+static int dpcm_fe_dai_shutdown(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *fe = substream->private_data;
+	int stream = substream->stream;
+
+	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
+
+	/* shutdown the BEs */
+	dpcm_be_dai_shutdown(fe, substream->stream);
+
+	dev_dbg(fe->dev, "dpcm: close FE %s\n", fe->dai_link->name);
+
+	/* now shutdown the frontend */
+	soc_pcm_close(substream);
+
+	/* run the stream event for each BE */
+	dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP);
+
+	fe->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
+	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+	return 0;
+}
+
+static int dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream)
+{
+	struct snd_soc_dpcm *dpcm;
+
+	/* only hw_params backends that are either sinks or sources
+	 * to this frontend DAI */
+	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+
+		struct snd_soc_pcm_runtime *be = dpcm->be;
+		struct snd_pcm_substream *be_substream =
+			snd_soc_dpcm_get_substream(be, stream);
+
+		/* is this op for this BE ? */
+		if (!snd_soc_dpcm_be_can_update(fe, be, stream))
+			continue;
+
+		/* only free hw when no longer used - check all FEs */
+		if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
+				continue;
+
+		if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) &&
+		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) &&
+		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) &&
+		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP))
+			continue;
+
+		dev_dbg(be->dev, "dpcm: hw_free BE %s\n",
+			dpcm->fe->dai_link->name);
+
+		soc_pcm_hw_free(be_substream);
+
+		be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE;
+	}
+
+	return 0;
+}
+
+static int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *fe = substream->private_data;
+	int err, stream = substream->stream;
+
+	mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
+	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
+
+	dev_dbg(fe->dev, "dpcm: hw_free FE %s\n", fe->dai_link->name);
+
+	/* call hw_free on the frontend */
+	err = soc_pcm_hw_free(substream);
+	if (err < 0)
+		dev_err(fe->dev,"dpcm: hw_free FE %s failed\n",
+			fe->dai_link->name);
+
+	/* only hw_params backends that are either sinks or sources
+	 * to this frontend DAI */
+	err = dpcm_be_dai_hw_free(fe, stream);
+
+	fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE;
+	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+
+	mutex_unlock(&fe->card->mutex);
+	return 0;
+}
+
+static int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream)
+{
+	struct snd_soc_dpcm *dpcm;
+	int ret;
+
+	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+
+		struct snd_soc_pcm_runtime *be = dpcm->be;
+		struct snd_pcm_substream *be_substream =
+			snd_soc_dpcm_get_substream(be, stream);
+
+		/* is this op for this BE ? */
+		if (!snd_soc_dpcm_be_can_update(fe, be, stream))
+			continue;
+
+		/* only allow hw_params() if no connected FEs are running */
+		if (!snd_soc_dpcm_can_be_params(fe, be, stream))
+			continue;
+
+		if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) &&
+		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) &&
+		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE))
+			continue;
+
+		dev_dbg(be->dev, "dpcm: hw_params BE %s\n",
+			dpcm->fe->dai_link->name);
+
+		/* copy params for each dpcm */
+		memcpy(&dpcm->hw_params, &fe->dpcm[stream].hw_params,
+				sizeof(struct snd_pcm_hw_params));
+
+		/* perform any hw_params fixups */
+		if (be->dai_link->be_hw_params_fixup) {
+			ret = be->dai_link->be_hw_params_fixup(be,
+					&dpcm->hw_params);
+			if (ret < 0) {
+				dev_err(be->dev,
+					"dpcm: hw_params BE fixup failed %d\n",
+					ret);
+				goto unwind;
+			}
+		}
+
+		ret = soc_pcm_hw_params(be_substream, &dpcm->hw_params);
+		if (ret < 0) {
+			dev_err(dpcm->be->dev,
+				"dpcm: hw_params BE failed %d\n", ret);
+			goto unwind;
+		}
+
+		be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_PARAMS;
+	}
+	return 0;
+
+unwind:
+	/* disable any enabled and non active backends */
+	list_for_each_entry_continue_reverse(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+		struct snd_soc_pcm_runtime *be = dpcm->be;
+		struct snd_pcm_substream *be_substream =
+			snd_soc_dpcm_get_substream(be, stream);
+
+		if (!snd_soc_dpcm_be_can_update(fe, be, stream))
+			continue;
+
+		/* only allow hw_free() if no connected FEs are running */
+		if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
+			continue;
+
+		if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) &&
+		   (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) &&
+		   (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) &&
+		   (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP))
+			continue;
+
+		soc_pcm_hw_free(be_substream);
+	}
+
+	return ret;
+}
+
+static int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream,
+				 struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *fe = substream->private_data;
+	int ret, stream = substream->stream;
+
+	mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
+	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
+
+	memcpy(&fe->dpcm[substream->stream].hw_params, params,
+			sizeof(struct snd_pcm_hw_params));
+	ret = dpcm_be_dai_hw_params(fe, substream->stream);
+	if (ret < 0) {
+		dev_err(fe->dev,"dpcm: hw_params BE failed %d\n", ret);
+		goto out;
+	}
+
+	dev_dbg(fe->dev, "dpcm: hw_params FE %s rate %d chan %x fmt %d\n",
+			fe->dai_link->name, params_rate(params),
+			params_channels(params), params_format(params));
+
+	/* call hw_params on the frontend */
+	ret = soc_pcm_hw_params(substream, params);
+	if (ret < 0) {
+		dev_err(fe->dev,"dpcm: hw_params FE failed %d\n", ret);
+		dpcm_be_dai_hw_free(fe, stream);
+	 } else
+		fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_PARAMS;
+
+out:
+	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+	mutex_unlock(&fe->card->mutex);
+	return ret;
+}
+
+static int dpcm_do_trigger(struct snd_soc_dpcm *dpcm,
+		struct snd_pcm_substream *substream, int cmd)
+{
+	int ret;
+
+	dev_dbg(dpcm->be->dev, "dpcm: trigger BE %s cmd %d\n",
+			dpcm->fe->dai_link->name, cmd);
+
+	ret = soc_pcm_trigger(substream, cmd);
+	if (ret < 0)
+		dev_err(dpcm->be->dev,"dpcm: trigger BE failed %d\n", ret);
+
+	return ret;
+}
+
+static int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
+			       int cmd)
+{
+	struct snd_soc_dpcm *dpcm;
+	int ret = 0;
+
+	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+
+		struct snd_soc_pcm_runtime *be = dpcm->be;
+		struct snd_pcm_substream *be_substream =
+			snd_soc_dpcm_get_substream(be, stream);
+
+		/* is this op for this BE ? */
+		if (!snd_soc_dpcm_be_can_update(fe, be, stream))
+			continue;
+
+		switch (cmd) {
+		case SNDRV_PCM_TRIGGER_START:
+			if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) &&
+			    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP))
+				continue;
+
+			ret = dpcm_do_trigger(dpcm, be_substream, cmd);
+			if (ret)
+				return ret;
+
+			be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
+			break;
+		case SNDRV_PCM_TRIGGER_RESUME:
+			if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_SUSPEND))
+				continue;
+
+			ret = dpcm_do_trigger(dpcm, be_substream, cmd);
+			if (ret)
+				return ret;
+
+			be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
+			break;
+		case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+			if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED))
+				continue;
+
+			ret = dpcm_do_trigger(dpcm, be_substream, cmd);
+			if (ret)
+				return ret;
+
+			be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
+			break;
+		case SNDRV_PCM_TRIGGER_STOP:
+			if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START)
+				continue;
+
+			if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
+				continue;
+
+			ret = dpcm_do_trigger(dpcm, be_substream, cmd);
+			if (ret)
+				return ret;
+
+			be->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP;
+			break;
+		case SNDRV_PCM_TRIGGER_SUSPEND:
+			if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP)
+				continue;
+
+			if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
+				continue;
+
+			ret = dpcm_do_trigger(dpcm, be_substream, cmd);
+			if (ret)
+				return ret;
+
+			be->dpcm[stream].state = SND_SOC_DPCM_STATE_SUSPEND;
+			break;
+		case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+			if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START)
+				continue;
+
+			if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
+				continue;
+
+			ret = dpcm_do_trigger(dpcm, be_substream, cmd);
+			if (ret)
+				return ret;
+
+			be->dpcm[stream].state = SND_SOC_DPCM_STATE_PAUSED;
+			break;
+		}
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(dpcm_be_dai_trigger);
+
+static int dpcm_fe_dai_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_soc_pcm_runtime *fe = substream->private_data;
+	int stream = substream->stream, ret;
+	enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream];
+
+	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
+
+	switch (trigger) {
+	case SND_SOC_DPCM_TRIGGER_PRE:
+		/* call trigger on the frontend before the backend. */
+
+		dev_dbg(fe->dev, "dpcm: pre trigger FE %s cmd %d\n",
+				fe->dai_link->name, cmd);
+
+		ret = soc_pcm_trigger(substream, cmd);
+		if (ret < 0) {
+			dev_err(fe->dev,"dpcm: trigger FE failed %d\n", ret);
+			goto out;
+		}
+
+		ret = dpcm_be_dai_trigger(fe, substream->stream, cmd);
+		break;
+	case SND_SOC_DPCM_TRIGGER_POST:
+		/* call trigger on the frontend after the backend. */
+
+		ret = dpcm_be_dai_trigger(fe, substream->stream, cmd);
+		if (ret < 0) {
+			dev_err(fe->dev,"dpcm: trigger FE failed %d\n", ret);
+			goto out;
+		}
+
+		dev_dbg(fe->dev, "dpcm: post trigger FE %s cmd %d\n",
+				fe->dai_link->name, cmd);
+
+		ret = soc_pcm_trigger(substream, cmd);
+		break;
+	case SND_SOC_DPCM_TRIGGER_BESPOKE:
+		/* bespoke trigger() - handles both FE and BEs */
+
+		dev_dbg(fe->dev, "dpcm: bespoke trigger FE %s cmd %d\n",
+				fe->dai_link->name, cmd);
+
+		ret = soc_pcm_bespoke_trigger(substream, cmd);
+		if (ret < 0) {
+			dev_err(fe->dev,"dpcm: trigger FE failed %d\n", ret);
+			goto out;
+		}
+		break;
+	default:
+		dev_err(fe->dev, "dpcm: invalid trigger cmd %d for %s\n", cmd,
+				fe->dai_link->name);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		fe->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		fe->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP;
+		break;
+	}
+
+out:
+	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+	return ret;
+}
+
+static int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream)
+{
+	struct snd_soc_dpcm *dpcm;
+	int ret = 0;
+
+	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+
+		struct snd_soc_pcm_runtime *be = dpcm->be;
+		struct snd_pcm_substream *be_substream =
+			snd_soc_dpcm_get_substream(be, stream);
+
+		/* is this op for this BE ? */
+		if (!snd_soc_dpcm_be_can_update(fe, be, stream))
+			continue;
+
+		if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) &&
+		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP))
+			continue;
+
+		dev_dbg(be->dev, "dpcm: prepare BE %s\n",
+			dpcm->fe->dai_link->name);
+
+		ret = soc_pcm_prepare(be_substream);
+		if (ret < 0) {
+			dev_err(be->dev, "dpcm: backend prepare failed %d\n",
+				ret);
+			break;
+		}
+
+		be->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE;
+	}
+	return ret;
+}
+
+static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *fe = substream->private_data;
+	int stream = substream->stream, ret = 0;
+
+	mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
+
+	dev_dbg(fe->dev, "dpcm: prepare FE %s\n", fe->dai_link->name);
+
+	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
+
+	/* there is no point preparing this FE if there are no BEs */
+	if (list_empty(&fe->dpcm[stream].be_clients)) {
+		dev_err(fe->dev, "dpcm: no backend DAIs enabled for %s\n",
+				fe->dai_link->name);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	ret = dpcm_be_dai_prepare(fe, substream->stream);
+	if (ret < 0)
+		goto out;
+
+	/* call prepare on the frontend */
+	ret = soc_pcm_prepare(substream);
+	if (ret < 0) {
+		dev_err(fe->dev,"dpcm: prepare FE %s failed\n",
+			fe->dai_link->name);
+		goto out;
+	}
+
+	/* run the stream event for each BE */
+	dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START);
+	fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE;
+
+out:
+	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+	mutex_unlock(&fe->card->mutex);
+
+	return ret;
+}
+
+static int soc_pcm_ioctl(struct snd_pcm_substream *substream,
+		     unsigned int cmd, void *arg)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_platform *platform = rtd->platform;
+
+	if (platform->driver->ops->ioctl)
+		return platform->driver->ops->ioctl(substream, cmd, arg);
+	return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+
+static int dpcm_run_update_shutdown(struct snd_soc_pcm_runtime *fe, int stream)
+{
+	struct snd_pcm_substream *substream =
+		snd_soc_dpcm_get_substream(fe, stream);
+	enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream];
+	int err;
+
+	dev_dbg(fe->dev, "runtime %s close on FE %s\n",
+			stream ? "capture" : "playback", fe->dai_link->name);
+
+	if (trigger == SND_SOC_DPCM_TRIGGER_BESPOKE) {
+		/* call bespoke trigger - FE takes care of all BE triggers */
+		dev_dbg(fe->dev, "dpcm: bespoke trigger FE %s cmd stop\n",
+				fe->dai_link->name);
+
+		err = soc_pcm_bespoke_trigger(substream, SNDRV_PCM_TRIGGER_STOP);
+		if (err < 0)
+			dev_err(fe->dev,"dpcm: trigger FE failed %d\n", err);
+	} else {
+		dev_dbg(fe->dev, "dpcm: trigger FE %s cmd stop\n",
+			fe->dai_link->name);
+
+		err = dpcm_be_dai_trigger(fe, stream, SNDRV_PCM_TRIGGER_STOP);
+		if (err < 0)
+			dev_err(fe->dev,"dpcm: trigger FE failed %d\n", err);
+	}
+
+	err = dpcm_be_dai_hw_free(fe, stream);
+	if (err < 0)
+		dev_err(fe->dev,"dpcm: hw_free FE failed %d\n", err);
+
+	err = dpcm_be_dai_shutdown(fe, stream);
+	if (err < 0)
+		dev_err(fe->dev,"dpcm: shutdown FE failed %d\n", err);
+
+	/* run the stream event for each BE */
+	dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_NOP);
+
+	return 0;
+}
+
+static int dpcm_run_update_startup(struct snd_soc_pcm_runtime *fe, int stream)
+{
+	struct snd_pcm_substream *substream =
+		snd_soc_dpcm_get_substream(fe, stream);
+	struct snd_soc_dpcm *dpcm;
+	enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream];
+	int ret;
+
+	dev_dbg(fe->dev, "runtime %s open on FE %s\n",
+			stream ? "capture" : "playback", fe->dai_link->name);
+
+	/* Only start the BE if the FE is ready */
+	if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_HW_FREE ||
+		fe->dpcm[stream].state == SND_SOC_DPCM_STATE_CLOSE)
+		return -EINVAL;
+
+	/* startup must always be called for new BEs */
+	ret = dpcm_be_dai_startup(fe, stream);
+	if (ret < 0) {
+		goto disconnect;
+		return ret;
+	}
+
+	/* keep going if FE state is > open */
+	if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_OPEN)
+		return 0;
+
+	ret = dpcm_be_dai_hw_params(fe, stream);
+	if (ret < 0) {
+		goto close;
+		return ret;
+	}
+
+	/* keep going if FE state is > hw_params */
+	if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_HW_PARAMS)
+		return 0;
+
+
+	ret = dpcm_be_dai_prepare(fe, stream);
+	if (ret < 0) {
+		goto hw_free;
+		return ret;
+	}
+
+	/* run the stream event for each BE */
+	dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_NOP);
+
+	/* keep going if FE state is > prepare */
+	if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_PREPARE ||
+		fe->dpcm[stream].state == SND_SOC_DPCM_STATE_STOP)
+		return 0;
+
+	if (trigger == SND_SOC_DPCM_TRIGGER_BESPOKE) {
+		/* call trigger on the frontend - FE takes care of all BE triggers */
+		dev_dbg(fe->dev, "dpcm: bespoke trigger FE %s cmd start\n",
+				fe->dai_link->name);
+
+		ret = soc_pcm_bespoke_trigger(substream, SNDRV_PCM_TRIGGER_START);
+		if (ret < 0) {
+			dev_err(fe->dev,"dpcm: bespoke trigger FE failed %d\n", ret);
+			goto hw_free;
+		}
+	} else {
+		dev_dbg(fe->dev, "dpcm: trigger FE %s cmd start\n",
+			fe->dai_link->name);
+
+		ret = dpcm_be_dai_trigger(fe, stream,
+					SNDRV_PCM_TRIGGER_START);
+		if (ret < 0) {
+			dev_err(fe->dev,"dpcm: trigger FE failed %d\n", ret);
+			goto hw_free;
+		}
+	}
+
+	return 0;
+
+hw_free:
+	dpcm_be_dai_hw_free(fe, stream);
+close:
+	dpcm_be_dai_shutdown(fe, stream);
+disconnect:
+	/* disconnect any non started BEs */
+	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+		struct snd_soc_pcm_runtime *be = dpcm->be;
+		if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START)
+				dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE;
+	}
+
+	return ret;
+}
+
+static int dpcm_run_new_update(struct snd_soc_pcm_runtime *fe, int stream)
+{
+	int ret;
+
+	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE;
+	ret = dpcm_run_update_startup(fe, stream);
+	if (ret < 0)
+		dev_err(fe->dev, "failed to startup some BEs\n");
+	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+
+	return ret;
+}
+
+static int dpcm_run_old_update(struct snd_soc_pcm_runtime *fe, int stream)
+{
+	int ret;
+
+	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE;
+	ret = dpcm_run_update_shutdown(fe, stream);
+	if (ret < 0)
+		dev_err(fe->dev, "failed to shutdown some BEs\n");
+	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+
+	return ret;
+}
+
+/* Called by DAPM mixer/mux changes to update audio routing between PCMs and
+ * any DAI links.
+ */
+int soc_dpcm_runtime_update(struct snd_soc_dapm_widget *widget)
+{
+	struct snd_soc_card *card;
+	int i, old, new, paths;
+
+	if (widget->codec)
+		card = widget->codec->card;
+	else if (widget->platform)
+		card = widget->platform->card;
+	else
+		return -EINVAL;
+
+	mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
+	for (i = 0; i < card->num_rtd; i++) {
+		struct snd_soc_dapm_widget_list *list;
+		struct snd_soc_pcm_runtime *fe = &card->rtd[i];
+
+		/* make sure link is FE */
+		if (!fe->dai_link->dynamic)
+			continue;
+
+		/* only check active links */
+		if (!fe->cpu_dai->active)
+			continue;
+
+		/* DAPM sync will call this to update DSP paths */
+		dev_dbg(fe->dev, "DPCM runtime update for FE %s\n",
+			fe->dai_link->name);
+
+		/* skip if FE doesn't have playback capability */
+		if (!fe->cpu_dai->driver->playback.channels_min)
+			goto capture;
+
+		paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_PLAYBACK, &list);
+		if (paths < 0) {
+			dev_warn(fe->dev, "%s no valid %s path\n",
+					fe->dai_link->name,  "playback");
+			mutex_unlock(&card->mutex);
+			return paths;
+		}
+
+		/* update any new playback paths */
+		new = dpcm_process_paths(fe, SNDRV_PCM_STREAM_PLAYBACK, &list, 1);
+		if (new) {
+			dpcm_run_new_update(fe, SNDRV_PCM_STREAM_PLAYBACK);
+			dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_PLAYBACK);
+			dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_PLAYBACK);
+		}
+
+		/* update any old playback paths */
+		old = dpcm_process_paths(fe, SNDRV_PCM_STREAM_PLAYBACK, &list, 0);
+		if (old) {
+			dpcm_run_old_update(fe, SNDRV_PCM_STREAM_PLAYBACK);
+			dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_PLAYBACK);
+			dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_PLAYBACK);
+		}
+
+capture:
+		/* skip if FE doesn't have capture capability */
+		if (!fe->cpu_dai->driver->capture.channels_min)
+			continue;
+
+		paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_CAPTURE, &list);
+		if (paths < 0) {
+			dev_warn(fe->dev, "%s no valid %s path\n",
+					fe->dai_link->name,  "capture");
+			mutex_unlock(&card->mutex);
+			return paths;
+		}
+
+		/* update any new capture paths */
+		new = dpcm_process_paths(fe, SNDRV_PCM_STREAM_CAPTURE, &list, 1);
+		if (new) {
+			dpcm_run_new_update(fe, SNDRV_PCM_STREAM_CAPTURE);
+			dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_CAPTURE);
+			dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_CAPTURE);
+		}
+
+		/* update any old capture paths */
+		old = dpcm_process_paths(fe, SNDRV_PCM_STREAM_CAPTURE, &list, 0);
+		if (old) {
+			dpcm_run_old_update(fe, SNDRV_PCM_STREAM_CAPTURE);
+			dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_CAPTURE);
+			dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_CAPTURE);
+		}
+
+		dpcm_path_put(&list);
+	}
+
+	mutex_unlock(&card->mutex);
+	return 0;
+}
+int soc_dpcm_be_digital_mute(struct snd_soc_pcm_runtime *fe, int mute)
+{
+	struct snd_soc_dpcm *dpcm;
+	struct list_head *clients =
+		&fe->dpcm[SNDRV_PCM_STREAM_PLAYBACK].be_clients;
+
+	list_for_each_entry(dpcm, clients, list_be) {
+
+		struct snd_soc_pcm_runtime *be = dpcm->be;
+		struct snd_soc_dai *dai = be->codec_dai;
+		struct snd_soc_dai_driver *drv = dai->driver;
+
+		if (be->dai_link->ignore_suspend)
+			continue;
+
+		dev_dbg(be->dev, "BE digital mute %s\n", be->dai_link->name);
+
+		if (drv->ops->digital_mute && dai->playback_active)
+				drv->ops->digital_mute(dai, mute);
+	}
+
+	return 0;
+}
+
+static int dpcm_fe_dai_open(struct snd_pcm_substream *fe_substream)
+{
+	struct snd_soc_pcm_runtime *fe = fe_substream->private_data;
+	struct snd_soc_dpcm *dpcm;
+	struct snd_soc_dapm_widget_list *list;
+	int ret;
+	int stream = fe_substream->stream;
+
+	mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
+	fe->dpcm[stream].runtime = fe_substream->runtime;
+
+	if (dpcm_path_get(fe, stream, &list) <= 0) {
+		dev_warn(fe->dev, "asoc: %s no valid %s route\n",
+			fe->dai_link->name, stream ? "capture" : "playback");
+			mutex_unlock(&fe->card->mutex);
+			return -EINVAL;
+	}
+
+	/* calculate valid and active FE <-> BE dpcms */
+	dpcm_process_paths(fe, stream, &list, 1);
+
+	ret = dpcm_fe_dai_startup(fe_substream);
+	if (ret < 0) {
+		/* clean up all links */
+		list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be)
+			dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE;
+
+		dpcm_be_disconnect(fe, stream);
+		fe->dpcm[stream].runtime = NULL;
+	}
+
+	dpcm_clear_pending_state(fe, stream);
+	dpcm_path_put(&list);
+	mutex_unlock(&fe->card->mutex);
+	return ret;
+}
+
+static int dpcm_fe_dai_close(struct snd_pcm_substream *fe_substream)
+{
+	struct snd_soc_pcm_runtime *fe = fe_substream->private_data;
+	struct snd_soc_dpcm *dpcm;
+	int stream = fe_substream->stream, ret;
+
+	mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
+	ret = dpcm_fe_dai_shutdown(fe_substream);
+
+	/* mark FE's links ready to prune */
+	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be)
+		dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE;
+
+	dpcm_be_disconnect(fe, stream);
+
+	fe->dpcm[stream].runtime = NULL;
+	mutex_unlock(&fe->card->mutex);
+	return ret;
+}
+
 /* create a new pcm */
 int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
 {
@@ -641,56 +2001,94 @@
 	struct snd_soc_platform *platform = rtd->platform;
 	struct snd_soc_dai *codec_dai = rtd->codec_dai;
 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-	struct snd_pcm_ops *soc_pcm_ops = &rtd->ops;
 	struct snd_pcm *pcm;
 	char new_name[64];
 	int ret = 0, playback = 0, capture = 0;
 
-	soc_pcm_ops->open	= soc_pcm_open;
-	soc_pcm_ops->close	= soc_pcm_close;
-	soc_pcm_ops->hw_params	= soc_pcm_hw_params;
-	soc_pcm_ops->hw_free	= soc_pcm_hw_free;
-	soc_pcm_ops->prepare	= soc_pcm_prepare;
-	soc_pcm_ops->trigger	= soc_pcm_trigger;
-	soc_pcm_ops->pointer	= soc_pcm_pointer;
+	if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm) {
+		if (cpu_dai->driver->playback.channels_min)
+			playback = 1;
+		if (cpu_dai->driver->capture.channels_min)
+			capture = 1;
+	} else {
+		if (codec_dai->driver->playback.channels_min)
+			playback = 1;
+		if (codec_dai->driver->capture.channels_min)
+			capture = 1;
+	}
 
-	/* check client and interface hw capabilities */
-	snprintf(new_name, sizeof(new_name), "%s %s-%d",
-			rtd->dai_link->stream_name, codec_dai->name, num);
+	/* create the PCM */
+	if (rtd->dai_link->no_pcm) {
+		snprintf(new_name, sizeof(new_name), "(%s)",
+			rtd->dai_link->stream_name);
 
-	if (codec_dai->driver->playback.channels_min)
-		playback = 1;
-	if (codec_dai->driver->capture.channels_min)
-		capture = 1;
+		ret = snd_pcm_new_internal(rtd->card->snd_card, new_name, num,
+				playback, capture, &pcm);
+	} else {
+		if (rtd->dai_link->dynamic)
+			snprintf(new_name, sizeof(new_name), "%s (*)",
+				rtd->dai_link->stream_name);
+		else
+			snprintf(new_name, sizeof(new_name), "%s %s-%d",
+				rtd->dai_link->stream_name, codec_dai->name, num);
 
-	dev_dbg(rtd->card->dev, "registered pcm #%d %s\n",num,new_name);
-	ret = snd_pcm_new(rtd->card->snd_card, new_name,
-			num, playback, capture, &pcm);
+		ret = snd_pcm_new(rtd->card->snd_card, new_name, num, playback,
+			capture, &pcm);
+	}
 	if (ret < 0) {
 		printk(KERN_ERR "asoc: can't create pcm for codec %s\n", codec->name);
 		return ret;
 	}
+	dev_dbg(rtd->card->dev, "registered pcm #%d %s\n",num, new_name);
 
 	/* DAPM dai link stream work */
 	INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work);
 
 	rtd->pcm = pcm;
 	pcm->private_data = rtd;
+
+	if (rtd->dai_link->no_pcm) {
+		if (playback)
+			pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->private_data = rtd;
+		if (capture)
+			pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream->private_data = rtd;
+		goto out;
+	}
+
+	/* ASoC PCM operations */
+	if (rtd->dai_link->dynamic) {
+		rtd->ops.open		= dpcm_fe_dai_open;
+		rtd->ops.hw_params	= dpcm_fe_dai_hw_params;
+		rtd->ops.prepare	= dpcm_fe_dai_prepare;
+		rtd->ops.trigger	= dpcm_fe_dai_trigger;
+		rtd->ops.hw_free	= dpcm_fe_dai_hw_free;
+		rtd->ops.close		= dpcm_fe_dai_close;
+		rtd->ops.pointer	= soc_pcm_pointer;
+		rtd->ops.ioctl		= soc_pcm_ioctl;
+	} else {
+		rtd->ops.open		= soc_pcm_open;
+		rtd->ops.hw_params	= soc_pcm_hw_params;
+		rtd->ops.prepare	= soc_pcm_prepare;
+		rtd->ops.trigger	= soc_pcm_trigger;
+		rtd->ops.hw_free	= soc_pcm_hw_free;
+		rtd->ops.close		= soc_pcm_close;
+		rtd->ops.pointer	= soc_pcm_pointer;
+		rtd->ops.ioctl		= soc_pcm_ioctl;
+	}
+
 	if (platform->driver->ops) {
-		soc_pcm_ops->mmap = platform->driver->ops->mmap;
-		soc_pcm_ops->pointer = platform->driver->ops->pointer;
-		soc_pcm_ops->ioctl = platform->driver->ops->ioctl;
-		soc_pcm_ops->copy = platform->driver->ops->copy;
-		soc_pcm_ops->silence = platform->driver->ops->silence;
-		soc_pcm_ops->ack = platform->driver->ops->ack;
-		soc_pcm_ops->page = platform->driver->ops->page;
+		rtd->ops.ack		= platform->driver->ops->ack;
+		rtd->ops.copy		= platform->driver->ops->copy;
+		rtd->ops.silence	= platform->driver->ops->silence;
+		rtd->ops.page		= platform->driver->ops->page;
+		rtd->ops.mmap		= platform->driver->ops->mmap;
 	}
 
 	if (playback)
-		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, soc_pcm_ops);
+		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &rtd->ops);
 
 	if (capture)
-		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, soc_pcm_ops);
+		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &rtd->ops);
 
 	if (platform->driver->pcm_new) {
 		ret = platform->driver->pcm_new(rtd);
@@ -701,7 +2099,257 @@
 	}
 
 	pcm->private_free = platform->driver->pcm_free;
+out:
 	printk(KERN_INFO "asoc: %s <-> %s mapping ok\n", codec_dai->name,
 		cpu_dai->name);
 	return ret;
 }
+
+/* is the current PCM operation for this FE ? */
+int snd_soc_dpcm_fe_can_update(struct snd_soc_pcm_runtime *fe, int stream)
+{
+	if (fe->dpcm[stream].runtime_update == SND_SOC_DPCM_UPDATE_FE)
+		return 1;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dpcm_fe_can_update);
+
+/* is the current PCM operation for this BE ? */
+int snd_soc_dpcm_be_can_update(struct snd_soc_pcm_runtime *fe,
+		struct snd_soc_pcm_runtime *be, int stream)
+{
+	if ((fe->dpcm[stream].runtime_update == SND_SOC_DPCM_UPDATE_FE) ||
+	   ((fe->dpcm[stream].runtime_update == SND_SOC_DPCM_UPDATE_BE) &&
+		  be->dpcm[stream].runtime_update))
+		return 1;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dpcm_be_can_update);
+
+/* get the substream for this BE */
+struct snd_pcm_substream *
+	snd_soc_dpcm_get_substream(struct snd_soc_pcm_runtime *be, int stream)
+{
+	return be->pcm->streams[stream].substream;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dpcm_get_substream);
+
+/* get the BE runtime state */
+enum snd_soc_dpcm_state
+	snd_soc_dpcm_be_get_state(struct snd_soc_pcm_runtime *be, int stream)
+{
+	return be->dpcm[stream].state;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dpcm_be_get_state);
+
+/* set the BE runtime state */
+void snd_soc_dpcm_be_set_state(struct snd_soc_pcm_runtime *be,
+		int stream, enum snd_soc_dpcm_state state)
+{
+	be->dpcm[stream].state = state;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dpcm_be_set_state);
+
+/*
+ * We can only hw_free, stop, pause or suspend a BE DAI if any of it's FE
+ * are not running, paused or suspended for the specified stream direction.
+ */
+int snd_soc_dpcm_can_be_free_stop(struct snd_soc_pcm_runtime *fe,
+		struct snd_soc_pcm_runtime *be, int stream)
+{
+	struct snd_soc_dpcm *dpcm;
+	int state;
+
+	list_for_each_entry(dpcm, &be->dpcm[stream].fe_clients, list_fe) {
+
+		if (dpcm->fe == fe)
+			continue;
+
+		state = dpcm->fe->dpcm[stream].state;
+		if (state == SND_SOC_DPCM_STATE_START ||
+			state == SND_SOC_DPCM_STATE_PAUSED ||
+			state == SND_SOC_DPCM_STATE_SUSPEND)
+			return 0;
+	}
+
+	/* it's safe to free/stop this BE DAI */
+	return 1;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dpcm_can_be_free_stop);
+
+/*
+ * We can only change hw params a BE DAI if any of it's FE are not prepared,
+ * running, paused or suspended for the specified stream direction.
+ */
+int snd_soc_dpcm_can_be_params(struct snd_soc_pcm_runtime *fe,
+		struct snd_soc_pcm_runtime *be, int stream)
+{
+	struct snd_soc_dpcm *dpcm;
+	int state;
+
+	list_for_each_entry(dpcm, &be->dpcm[stream].fe_clients, list_fe) {
+
+		if (dpcm->fe == fe)
+			continue;
+
+		state = dpcm->fe->dpcm[stream].state;
+		if (state == SND_SOC_DPCM_STATE_START ||
+			state == SND_SOC_DPCM_STATE_PAUSED ||
+			state == SND_SOC_DPCM_STATE_SUSPEND ||
+			state == SND_SOC_DPCM_STATE_PREPARE)
+			return 0;
+	}
+
+	/* it's safe to change hw_params */
+	return 1;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dpcm_can_be_params);
+
+int snd_soc_platform_trigger(struct snd_pcm_substream *substream,
+		int cmd, struct snd_soc_platform *platform)
+{
+	if (platform->driver->ops->trigger)
+		return platform->driver->ops->trigger(substream, cmd);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_platform_trigger);
+
+#ifdef CONFIG_DEBUG_FS
+static char *dpcm_state_string(enum snd_soc_dpcm_state state)
+{
+	switch (state) {
+	case SND_SOC_DPCM_STATE_NEW:
+		return "new";
+	case SND_SOC_DPCM_STATE_OPEN:
+		return "open";
+	case SND_SOC_DPCM_STATE_HW_PARAMS:
+		return "hw_params";
+	case SND_SOC_DPCM_STATE_PREPARE:
+		return "prepare";
+	case SND_SOC_DPCM_STATE_START:
+		return "start";
+	case SND_SOC_DPCM_STATE_STOP:
+		return "stop";
+	case SND_SOC_DPCM_STATE_SUSPEND:
+		return "suspend";
+	case SND_SOC_DPCM_STATE_PAUSED:
+		return "paused";
+	case SND_SOC_DPCM_STATE_HW_FREE:
+		return "hw_free";
+	case SND_SOC_DPCM_STATE_CLOSE:
+		return "close";
+	}
+
+	return "unknown";
+}
+
+static ssize_t dpcm_show_state(struct snd_soc_pcm_runtime *fe,
+				int stream, char *buf, size_t size)
+{
+	struct snd_pcm_hw_params *params = &fe->dpcm[stream].hw_params;
+	struct snd_soc_dpcm *dpcm;
+	ssize_t offset = 0;
+
+	/* FE state */
+	offset += snprintf(buf + offset, size - offset,
+			"[%s - %s]\n", fe->dai_link->name,
+			stream ? "Capture" : "Playback");
+
+	offset += snprintf(buf + offset, size - offset, "State: %s\n",
+	                dpcm_state_string(fe->dpcm[stream].state));
+
+	if ((fe->dpcm[stream].state >= SND_SOC_DPCM_STATE_HW_PARAMS) &&
+	    (fe->dpcm[stream].state <= SND_SOC_DPCM_STATE_STOP))
+		offset += snprintf(buf + offset, size - offset,
+				"Hardware Params: "
+				"Format = %s, Channels = %d, Rate = %d\n",
+				snd_pcm_format_name(params_format(params)),
+				params_channels(params),
+				params_rate(params));
+
+	/* BEs state */
+	offset += snprintf(buf + offset, size - offset, "Backends:\n");
+
+	if (list_empty(&fe->dpcm[stream].be_clients)) {
+		offset += snprintf(buf + offset, size - offset,
+				" No active DSP links\n");
+		goto out;
+	}
+
+	list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+		struct snd_soc_pcm_runtime *be = dpcm->be;
+		params = &dpcm->hw_params;
+
+		offset += snprintf(buf + offset, size - offset,
+				"- %s\n", be->dai_link->name);
+
+		offset += snprintf(buf + offset, size - offset,
+				"   State: %s\n",
+				dpcm_state_string(be->dpcm[stream].state));
+
+		if ((be->dpcm[stream].state >= SND_SOC_DPCM_STATE_HW_PARAMS) &&
+		    (be->dpcm[stream].state <= SND_SOC_DPCM_STATE_STOP))
+			offset += snprintf(buf + offset, size - offset,
+				"   Hardware Params: "
+				"Format = %s, Channels = %d, Rate = %d\n",
+				snd_pcm_format_name(params_format(params)),
+				params_channels(params),
+				params_rate(params));
+	}
+
+out:
+	return offset;
+}
+
+static ssize_t dpcm_state_read_file(struct file *file, char __user *user_buf,
+				size_t count, loff_t *ppos)
+{
+	struct snd_soc_pcm_runtime *fe = file->private_data;
+	ssize_t out_count = PAGE_SIZE, offset = 0, ret = 0;
+	char *buf;
+
+	buf = kmalloc(out_count, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	if (fe->cpu_dai->driver->playback.channels_min)
+		offset += dpcm_show_state(fe, SNDRV_PCM_STREAM_PLAYBACK,
+					buf + offset, out_count - offset);
+
+	if (fe->cpu_dai->driver->capture.channels_min)
+		offset += dpcm_show_state(fe, SNDRV_PCM_STREAM_CAPTURE,
+					buf + offset, out_count - offset);
+
+	ret = simple_read_from_buffer(user_buf, count, ppos, buf, offset);
+
+	kfree(buf);
+	return ret;
+}
+
+static const struct file_operations dpcm_state_fops = {
+	.open = simple_open,
+	.read = dpcm_state_read_file,
+	.llseek = default_llseek,
+};
+
+int soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd)
+{
+	if (!rtd->dai_link)
+		return 0;
+
+	rtd->debugfs_dpcm_root = debugfs_create_dir(rtd->dai_link->name,
+			rtd->card->debugfs_card_root);
+	if (!rtd->debugfs_dpcm_root) {
+		dev_dbg(rtd->dev,
+			 "ASoC: Failed to create dpcm debugfs directory %s\n",
+			 rtd->dai_link->name);
+		return -EINVAL;
+	}
+
+	rtd->debugfs_dpcm_state = debugfs_create_file("state", 0444,
+						rtd->debugfs_dpcm_root,
+						rtd, &dpcm_state_fops);
+
+	return 0;
+}
+#endif
diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig
index ce1b773..c1c8e95 100644
--- a/sound/soc/tegra/Kconfig
+++ b/sound/soc/tegra/Kconfig
@@ -1,26 +1,63 @@
 config SND_SOC_TEGRA
 	tristate "SoC Audio for the Tegra System-on-Chip"
 	depends on ARCH_TEGRA && TEGRA_SYSTEM_DMA
+	select REGMAP_MMIO
 	help
 	  Say Y or M here if you want support for SoC audio on Tegra.
 
-config SND_SOC_TEGRA_I2S
+config SND_SOC_TEGRA20_DAS
 	tristate
-	depends on SND_SOC_TEGRA
+	depends on SND_SOC_TEGRA && ARCH_TEGRA_2x_SOC
+	help
+	  Say Y or M if you want to add support for the Tegra20 DAS module.
+	  You will also need to select the individual machine drivers to
+	  support below.
+
+config SND_SOC_TEGRA20_I2S
+	tristate
+	depends on SND_SOC_TEGRA && ARCH_TEGRA_2x_SOC
+	select SND_SOC_TEGRA20_DAS
 	help
 	  Say Y or M if you want to add support for codecs attached to the
-	  Tegra I2S interface. You will also need to select the individual
+	  Tegra20 I2S interface. You will also need to select the individual
 	  machine drivers to support below.
 
-config SND_SOC_TEGRA_SPDIF
+config SND_SOC_TEGRA20_SPDIF
 	tristate
-	depends on SND_SOC_TEGRA
+	depends on SND_SOC_TEGRA && ARCH_TEGRA_2x_SOC
 	default m
 	help
-	  Say Y or M if you want to add support for the SPDIF interface.
+	  Say Y or M if you want to add support for the Tegra20 SPDIF interface.
 	  You will also need to select the individual machine drivers to support
 	  below.
 
+config SND_SOC_TEGRA30_AHUB
+	tristate
+	depends on SND_SOC_TEGRA && ARCH_TEGRA_3x_SOC
+	help
+	  Say Y or M if you want to add support for the Tegra20 AHUB module.
+	  You will also need to select the individual machine drivers to
+	  support below.
+
+config SND_SOC_TEGRA30_I2S
+	tristate
+	depends on SND_SOC_TEGRA && ARCH_TEGRA_3x_SOC
+	select SND_SOC_TEGRA30_AHUB
+	help
+	  Say Y or M if you want to add support for codecs attached to the
+	  Tegra30 I2S interface. You will also need to select the individual
+	  machine drivers to support below.
+
+config SND_SOC_TEGRA_WM8753
+	tristate "SoC Audio support for Tegra boards using a WM8753 codec"
+	depends on SND_SOC_TEGRA && I2C
+	select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC
+	select SND_SOC_TEGRA30_I2S if ARCH_TEGRA_3x_SOC
+	select SND_SOC_WM8753
+	help
+	  Say Y or M here if you want to add support for SoC audio on Tegra
+	  boards using the WM8753 codec, such as Whistler.
+
 config MACH_HAS_SND_SOC_TEGRA_WM8903
 	bool
 	help
@@ -32,7 +69,8 @@
 	tristate "SoC Audio support for Tegra boards using a WM8903 codec"
 	depends on SND_SOC_TEGRA && I2C
 	depends on MACH_HAS_SND_SOC_TEGRA_WM8903
-	select SND_SOC_TEGRA_I2S
+	select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC
+	select SND_SOC_TEGRA30_I2S if ARCH_TEGRA_3x_SOC
 	select SND_SOC_WM8903
 	help
 	  Say Y or M here if you want to add support for SoC audio on Tegra
@@ -42,17 +80,17 @@
 config SND_SOC_TEGRA_TRIMSLICE
 	tristate "SoC Audio support for TrimSlice board"
 	depends on SND_SOC_TEGRA && MACH_TRIMSLICE && I2C
-	select SND_SOC_TEGRA_I2S
+	select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC
 	select SND_SOC_TLV320AIC23
 	help
 	  Say Y or M here if you want to add support for SoC audio on the
 	  TrimSlice platform.
 
 config SND_SOC_TEGRA_ALC5632
-       tristate "SoC Audio support for Tegra boards using an ALC5632 codec"
-       depends on SND_SOC_TEGRA && I2C
-       select SND_SOC_TEGRA_I2S
-       select SND_SOC_ALC5632
-       help
-         Say Y or M here if you want to add support for SoC audio on the
-         Toshiba AC100 netbook.
+	tristate "SoC Audio support for Tegra boards using an ALC5632 codec"
+	depends on SND_SOC_TEGRA && I2C
+	select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC
+	select SND_SOC_ALC5632
+	help
+	  Say Y or M here if you want to add support for SoC audio on the
+	  Toshiba AC100 netbook.
diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile
index 8e584b8..391e78a 100644
--- a/sound/soc/tegra/Makefile
+++ b/sound/soc/tegra/Makefile
@@ -1,21 +1,27 @@
 # Tegra platform Support
-snd-soc-tegra-das-objs := tegra_das.o
 snd-soc-tegra-pcm-objs := tegra_pcm.o
-snd-soc-tegra-i2s-objs := tegra_i2s.o
-snd-soc-tegra-spdif-objs := tegra_spdif.o
 snd-soc-tegra-utils-objs += tegra_asoc_utils.o
+snd-soc-tegra20-das-objs := tegra20_das.o
+snd-soc-tegra20-i2s-objs := tegra20_i2s.o
+snd-soc-tegra20-spdif-objs := tegra20_spdif.o
+snd-soc-tegra30-ahub-objs := tegra30_ahub.o
+snd-soc-tegra30-i2s-objs := tegra30_i2s.o
 
-obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-utils.o
-obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-das.o
 obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-pcm.o
-obj-$(CONFIG_SND_SOC_TEGRA_I2S) += snd-soc-tegra-i2s.o
-obj-$(CONFIG_SND_SOC_TEGRA_SPDIF) += snd-soc-tegra-spdif.o
+obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-utils.o
+obj-$(CONFIG_SND_SOC_TEGRA20_DAS) += snd-soc-tegra20-das.o
+obj-$(CONFIG_SND_SOC_TEGRA20_I2S) += snd-soc-tegra20-i2s.o
+obj-$(CONFIG_SND_SOC_TEGRA20_SPDIF) += snd-soc-tegra20-spdif.o
+obj-$(CONFIG_SND_SOC_TEGRA30_AHUB) += snd-soc-tegra30-ahub.o
+obj-$(CONFIG_SND_SOC_TEGRA30_I2S) += snd-soc-tegra30-i2s.o
 
 # Tegra machine Support
+snd-soc-tegra-wm8753-objs := tegra_wm8753.o
 snd-soc-tegra-wm8903-objs := tegra_wm8903.o
 snd-soc-tegra-trimslice-objs := trimslice.o
 snd-soc-tegra-alc5632-objs := tegra_alc5632.o
 
+obj-$(CONFIG_SND_SOC_TEGRA_WM8753) += snd-soc-tegra-wm8753.o
 obj-$(CONFIG_SND_SOC_TEGRA_WM8903) += snd-soc-tegra-wm8903.o
 obj-$(CONFIG_SND_SOC_TEGRA_TRIMSLICE) += snd-soc-tegra-trimslice.o
 obj-$(CONFIG_SND_SOC_TEGRA_ALC5632) += snd-soc-tegra-alc5632.o
diff --git a/sound/soc/tegra/tegra20_das.c b/sound/soc/tegra/tegra20_das.c
new file mode 100644
index 0000000..bf99296
--- /dev/null
+++ b/sound/soc/tegra/tegra20_das.c
@@ -0,0 +1,233 @@
+/*
+ * tegra20_das.c - Tegra20 DAS driver
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 2010 - NVIDIA, 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/soc.h>
+#include "tegra20_das.h"
+
+#define DRV_NAME "tegra20-das"
+
+static struct tegra20_das *das;
+
+static inline void tegra20_das_write(u32 reg, u32 val)
+{
+	regmap_write(das->regmap, reg, val);
+}
+
+static inline u32 tegra20_das_read(u32 reg)
+{
+	u32 val;
+	regmap_read(das->regmap, reg, &val);
+	return val;
+}
+
+int tegra20_das_connect_dap_to_dac(int dap, int dac)
+{
+	u32 addr;
+	u32 reg;
+
+	if (!das)
+		return -ENODEV;
+
+	addr = TEGRA20_DAS_DAP_CTRL_SEL +
+		(dap * TEGRA20_DAS_DAP_CTRL_SEL_STRIDE);
+	reg = dac << TEGRA20_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P;
+
+	tegra20_das_write(addr, reg);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(tegra20_das_connect_dap_to_dac);
+
+int tegra20_das_connect_dap_to_dap(int dap, int otherdap, int master,
+				   int sdata1rx, int sdata2rx)
+{
+	u32 addr;
+	u32 reg;
+
+	if (!das)
+		return -ENODEV;
+
+	addr = TEGRA20_DAS_DAP_CTRL_SEL +
+		(dap * TEGRA20_DAS_DAP_CTRL_SEL_STRIDE);
+	reg = otherdap << TEGRA20_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P |
+		!!sdata2rx << TEGRA20_DAS_DAP_CTRL_SEL_DAP_SDATA2_TX_RX_P |
+		!!sdata1rx << TEGRA20_DAS_DAP_CTRL_SEL_DAP_SDATA1_TX_RX_P |
+		!!master << TEGRA20_DAS_DAP_CTRL_SEL_DAP_MS_SEL_P;
+
+	tegra20_das_write(addr, reg);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(tegra20_das_connect_dap_to_dap);
+
+int tegra20_das_connect_dac_to_dap(int dac, int dap)
+{
+	u32 addr;
+	u32 reg;
+
+	if (!das)
+		return -ENODEV;
+
+	addr = TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL +
+		(dac * TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_STRIDE);
+	reg = dap << TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL_P |
+		dap << TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL_P |
+		dap << TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL_P;
+
+	tegra20_das_write(addr, reg);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(tegra20_das_connect_dac_to_dap);
+
+#define LAST_REG(name) \
+	(TEGRA20_DAS_##name + \
+	 (TEGRA20_DAS_##name##_STRIDE * (TEGRA20_DAS_##name##_COUNT - 1)))
+
+static bool tegra20_das_wr_rd_reg(struct device *dev, unsigned int reg)
+{
+	if ((reg >= TEGRA20_DAS_DAP_CTRL_SEL) &&
+	    (reg <= LAST_REG(DAP_CTRL_SEL)))
+		return true;
+	if ((reg >= TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL) &&
+	    (reg <= LAST_REG(DAC_INPUT_DATA_CLK_SEL)))
+		return true;
+
+	return false;
+}
+
+static const struct regmap_config tegra20_das_regmap_config = {
+	.reg_bits = 32,
+	.reg_stride = 4,
+	.val_bits = 32,
+	.max_register = LAST_REG(DAC_INPUT_DATA_CLK_SEL),
+	.writeable_reg = tegra20_das_wr_rd_reg,
+	.readable_reg = tegra20_das_wr_rd_reg,
+	.cache_type = REGCACHE_RBTREE,
+};
+
+static int __devinit tegra20_das_probe(struct platform_device *pdev)
+{
+	struct resource *res, *region;
+	void __iomem *regs;
+	int ret = 0;
+
+	if (das)
+		return -ENODEV;
+
+	das = devm_kzalloc(&pdev->dev, sizeof(struct tegra20_das), GFP_KERNEL);
+	if (!das) {
+		dev_err(&pdev->dev, "Can't allocate tegra20_das\n");
+		ret = -ENOMEM;
+		goto err;
+	}
+	das->dev = &pdev->dev;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "No memory resource\n");
+		ret = -ENODEV;
+		goto err;
+	}
+
+	region = devm_request_mem_region(&pdev->dev, res->start,
+					 resource_size(res), pdev->name);
+	if (!region) {
+		dev_err(&pdev->dev, "Memory region already claimed\n");
+		ret = -EBUSY;
+		goto err;
+	}
+
+	regs = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+	if (!regs) {
+		dev_err(&pdev->dev, "ioremap failed\n");
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	das->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
+					    &tegra20_das_regmap_config);
+	if (IS_ERR(das->regmap)) {
+		dev_err(&pdev->dev, "regmap init failed\n");
+		ret = PTR_ERR(das->regmap);
+		goto err;
+	}
+
+	ret = tegra20_das_connect_dap_to_dac(TEGRA20_DAS_DAP_ID_1,
+					     TEGRA20_DAS_DAP_SEL_DAC1);
+	if (ret) {
+		dev_err(&pdev->dev, "Can't set up DAS DAP connection\n");
+		goto err;
+	}
+	ret = tegra20_das_connect_dac_to_dap(TEGRA20_DAS_DAC_ID_1,
+					     TEGRA20_DAS_DAC_SEL_DAP1);
+	if (ret) {
+		dev_err(&pdev->dev, "Can't set up DAS DAC connection\n");
+		goto err;
+	}
+
+	platform_set_drvdata(pdev, das);
+
+	return 0;
+
+err:
+	das = NULL;
+	return ret;
+}
+
+static int __devexit tegra20_das_remove(struct platform_device *pdev)
+{
+	if (!das)
+		return -ENODEV;
+
+	das = NULL;
+
+	return 0;
+}
+
+static const struct of_device_id tegra20_das_of_match[] __devinitconst = {
+	{ .compatible = "nvidia,tegra20-das", },
+	{},
+};
+
+static struct platform_driver tegra20_das_driver = {
+	.probe = tegra20_das_probe,
+	.remove = __devexit_p(tegra20_das_remove),
+	.driver = {
+		.name = DRV_NAME,
+		.owner = THIS_MODULE,
+		.of_match_table = tegra20_das_of_match,
+	},
+};
+module_platform_driver(tegra20_das_driver);
+
+MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
+MODULE_DESCRIPTION("Tegra20 DAS driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, tegra20_das_of_match);
diff --git a/sound/soc/tegra/tegra20_das.h b/sound/soc/tegra/tegra20_das.h
new file mode 100644
index 0000000..be217f3
--- /dev/null
+++ b/sound/soc/tegra/tegra20_das.h
@@ -0,0 +1,134 @@
+/*
+ * tegra20_das.h - Definitions for Tegra20 DAS driver
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 2010,2012 - NVIDIA, 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __TEGRA20_DAS_H__
+#define __TEGRA20_DAS_H__
+
+/* Register TEGRA20_DAS_DAP_CTRL_SEL */
+#define TEGRA20_DAS_DAP_CTRL_SEL			0x00
+#define TEGRA20_DAS_DAP_CTRL_SEL_COUNT			5
+#define TEGRA20_DAS_DAP_CTRL_SEL_STRIDE			4
+#define TEGRA20_DAS_DAP_CTRL_SEL_DAP_MS_SEL_P		31
+#define TEGRA20_DAS_DAP_CTRL_SEL_DAP_MS_SEL_S		1
+#define TEGRA20_DAS_DAP_CTRL_SEL_DAP_SDATA1_TX_RX_P	30
+#define TEGRA20_DAS_DAP_CTRL_SEL_DAP_SDATA1_TX_RX_S	1
+#define TEGRA20_DAS_DAP_CTRL_SEL_DAP_SDATA2_TX_RX_P	29
+#define TEGRA20_DAS_DAP_CTRL_SEL_DAP_SDATA2_TX_RX_S	1
+#define TEGRA20_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P		0
+#define TEGRA20_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_S		5
+
+/* Values for field TEGRA20_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL */
+#define TEGRA20_DAS_DAP_SEL_DAC1	0
+#define TEGRA20_DAS_DAP_SEL_DAC2	1
+#define TEGRA20_DAS_DAP_SEL_DAC3	2
+#define TEGRA20_DAS_DAP_SEL_DAP1	16
+#define TEGRA20_DAS_DAP_SEL_DAP2	17
+#define TEGRA20_DAS_DAP_SEL_DAP3	18
+#define TEGRA20_DAS_DAP_SEL_DAP4	19
+#define TEGRA20_DAS_DAP_SEL_DAP5	20
+
+/* Register TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL */
+#define TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL			0x40
+#define TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_COUNT		3
+#define TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_STRIDE		4
+#define TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL_P	28
+#define TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL_S	4
+#define TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL_P	24
+#define TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL_S	4
+#define TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL_P	0
+#define TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL_S	4
+
+/*
+ * Values for:
+ * TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL
+ * TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL
+ * TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL
+ */
+#define TEGRA20_DAS_DAC_SEL_DAP1	0
+#define TEGRA20_DAS_DAC_SEL_DAP2	1
+#define TEGRA20_DAS_DAC_SEL_DAP3	2
+#define TEGRA20_DAS_DAC_SEL_DAP4	3
+#define TEGRA20_DAS_DAC_SEL_DAP5	4
+
+/*
+ * Names/IDs of the DACs/DAPs.
+ */
+
+#define TEGRA20_DAS_DAP_ID_1 0
+#define TEGRA20_DAS_DAP_ID_2 1
+#define TEGRA20_DAS_DAP_ID_3 2
+#define TEGRA20_DAS_DAP_ID_4 3
+#define TEGRA20_DAS_DAP_ID_5 4
+
+#define TEGRA20_DAS_DAC_ID_1 0
+#define TEGRA20_DAS_DAC_ID_2 1
+#define TEGRA20_DAS_DAC_ID_3 2
+
+struct tegra20_das {
+	struct device *dev;
+	struct regmap *regmap;
+};
+
+/*
+ * Terminology:
+ * DAS: Digital audio switch (HW module controlled by this driver)
+ * DAP: Digital audio port (port/pins on Tegra device)
+ * DAC: Digital audio controller (e.g. I2S or AC97 controller elsewhere)
+ *
+ * The Tegra DAS is a mux/cross-bar which can connect each DAP to a specific
+ * DAC, or another DAP. When DAPs are connected, one must be the master and
+ * one the slave. Each DAC allows selection of a specific DAP for input, to
+ * cater for the case where N DAPs are connected to 1 DAC for broadcast
+ * output.
+ *
+ * This driver is dumb; no attempt is made to ensure that a valid routing
+ * configuration is programmed.
+ */
+
+/*
+ * Connect a DAP to to a DAC
+ * dap_id: DAP to connect: TEGRA20_DAS_DAP_ID_*
+ * dac_sel: DAC to connect to: TEGRA20_DAS_DAP_SEL_DAC*
+ */
+extern int tegra20_das_connect_dap_to_dac(int dap_id, int dac_sel);
+
+/*
+ * Connect a DAP to to another DAP
+ * dap_id: DAP to connect: TEGRA20_DAS_DAP_ID_*
+ * other_dap_sel: DAP to connect to: TEGRA20_DAS_DAP_SEL_DAP*
+ * master: Is this DAP the master (1) or slave (0)
+ * sdata1rx: Is this DAP's SDATA1 pin RX (1) or TX (0)
+ * sdata2rx: Is this DAP's SDATA2 pin RX (1) or TX (0)
+ */
+extern int tegra20_das_connect_dap_to_dap(int dap_id, int other_dap_sel,
+					  int master, int sdata1rx,
+					  int sdata2rx);
+
+/*
+ * Connect a DAC's input to a DAP
+ * (DAC outputs are selected by the DAP)
+ * dac_id: DAC ID to connect: TEGRA20_DAS_DAC_ID_*
+ * dap_sel: DAP to receive input from: TEGRA20_DAS_DAC_SEL_DAP*
+ */
+extern int tegra20_das_connect_dac_to_dap(int dac_id, int dap_sel);
+
+#endif
diff --git a/sound/soc/tegra/tegra20_i2s.c b/sound/soc/tegra/tegra20_i2s.c
new file mode 100644
index 0000000..0c7af63
--- /dev/null
+++ b/sound/soc/tegra/tegra20_i2s.c
@@ -0,0 +1,494 @@
+/*
+ * tegra20_i2s.c - Tegra20 I2S driver
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 2010,2012 - NVIDIA, Inc.
+ *
+ * Based on code copyright/by:
+ *
+ * Copyright (c) 2009-2010, NVIDIA Corporation.
+ * Scott Peterson <speterson@nvidia.com>
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Iliyan Malchev <malchev@google.com>
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "tegra20_i2s.h"
+
+#define DRV_NAME "tegra20-i2s"
+
+static inline void tegra20_i2s_write(struct tegra20_i2s *i2s, u32 reg, u32 val)
+{
+	regmap_write(i2s->regmap, reg, val);
+}
+
+static inline u32 tegra20_i2s_read(struct tegra20_i2s *i2s, u32 reg)
+{
+	u32 val;
+	regmap_read(i2s->regmap, reg, &val);
+	return val;
+}
+
+static int tegra20_i2s_runtime_suspend(struct device *dev)
+{
+	struct tegra20_i2s *i2s = dev_get_drvdata(dev);
+
+	clk_disable(i2s->clk_i2s);
+
+	return 0;
+}
+
+static int tegra20_i2s_runtime_resume(struct device *dev)
+{
+	struct tegra20_i2s *i2s = dev_get_drvdata(dev);
+	int ret;
+
+	ret = clk_enable(i2s->clk_i2s);
+	if (ret) {
+		dev_err(dev, "clk_enable failed: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int tegra20_i2s_set_fmt(struct snd_soc_dai *dai,
+				unsigned int fmt)
+{
+	struct tegra20_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	i2s->reg_ctrl &= ~TEGRA20_I2S_CTRL_MASTER_ENABLE;
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		i2s->reg_ctrl |= TEGRA20_I2S_CTRL_MASTER_ENABLE;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFM:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	i2s->reg_ctrl &= ~(TEGRA20_I2S_CTRL_BIT_FORMAT_MASK |
+			   TEGRA20_I2S_CTRL_LRCK_MASK);
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_DSP_A:
+		i2s->reg_ctrl |= TEGRA20_I2S_CTRL_BIT_FORMAT_DSP;
+		i2s->reg_ctrl |= TEGRA20_I2S_CTRL_LRCK_L_LOW;
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		i2s->reg_ctrl |= TEGRA20_I2S_CTRL_BIT_FORMAT_DSP;
+		i2s->reg_ctrl |= TEGRA20_I2S_CTRL_LRCK_R_LOW;
+		break;
+	case SND_SOC_DAIFMT_I2S:
+		i2s->reg_ctrl |= TEGRA20_I2S_CTRL_BIT_FORMAT_I2S;
+		i2s->reg_ctrl |= TEGRA20_I2S_CTRL_LRCK_L_LOW;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		i2s->reg_ctrl |= TEGRA20_I2S_CTRL_BIT_FORMAT_RJM;
+		i2s->reg_ctrl |= TEGRA20_I2S_CTRL_LRCK_L_LOW;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		i2s->reg_ctrl |= TEGRA20_I2S_CTRL_BIT_FORMAT_LJM;
+		i2s->reg_ctrl |= TEGRA20_I2S_CTRL_LRCK_L_LOW;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int tegra20_i2s_hw_params(struct snd_pcm_substream *substream,
+				 struct snd_pcm_hw_params *params,
+				 struct snd_soc_dai *dai)
+{
+	struct device *dev = substream->pcm->card->dev;
+	struct tegra20_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+	u32 reg;
+	int ret, sample_size, srate, i2sclock, bitcnt;
+
+	i2s->reg_ctrl &= ~TEGRA20_I2S_CTRL_BIT_SIZE_MASK;
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		i2s->reg_ctrl |= TEGRA20_I2S_CTRL_BIT_SIZE_16;
+		sample_size = 16;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		i2s->reg_ctrl |= TEGRA20_I2S_CTRL_BIT_SIZE_24;
+		sample_size = 24;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		i2s->reg_ctrl |= TEGRA20_I2S_CTRL_BIT_SIZE_32;
+		sample_size = 32;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	srate = params_rate(params);
+
+	/* Final "* 2" required by Tegra hardware */
+	i2sclock = srate * params_channels(params) * sample_size * 2;
+
+	ret = clk_set_rate(i2s->clk_i2s, i2sclock);
+	if (ret) {
+		dev_err(dev, "Can't set I2S clock rate: %d\n", ret);
+		return ret;
+	}
+
+	bitcnt = (i2sclock / (2 * srate)) - 1;
+	if (bitcnt < 0 || bitcnt > TEGRA20_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US)
+		return -EINVAL;
+	reg = bitcnt << TEGRA20_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT;
+
+	if (i2sclock % (2 * srate))
+		reg |= TEGRA20_I2S_TIMING_NON_SYM_ENABLE;
+
+	tegra20_i2s_write(i2s, TEGRA20_I2S_TIMING, reg);
+
+	tegra20_i2s_write(i2s, TEGRA20_I2S_FIFO_SCR,
+		TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_FOUR_SLOTS |
+		TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_FOUR_SLOTS);
+
+	return 0;
+}
+
+static void tegra20_i2s_start_playback(struct tegra20_i2s *i2s)
+{
+	i2s->reg_ctrl |= TEGRA20_I2S_CTRL_FIFO1_ENABLE;
+	tegra20_i2s_write(i2s, TEGRA20_I2S_CTRL, i2s->reg_ctrl);
+}
+
+static void tegra20_i2s_stop_playback(struct tegra20_i2s *i2s)
+{
+	i2s->reg_ctrl &= ~TEGRA20_I2S_CTRL_FIFO1_ENABLE;
+	tegra20_i2s_write(i2s, TEGRA20_I2S_CTRL, i2s->reg_ctrl);
+}
+
+static void tegra20_i2s_start_capture(struct tegra20_i2s *i2s)
+{
+	i2s->reg_ctrl |= TEGRA20_I2S_CTRL_FIFO2_ENABLE;
+	tegra20_i2s_write(i2s, TEGRA20_I2S_CTRL, i2s->reg_ctrl);
+}
+
+static void tegra20_i2s_stop_capture(struct tegra20_i2s *i2s)
+{
+	i2s->reg_ctrl &= ~TEGRA20_I2S_CTRL_FIFO2_ENABLE;
+	tegra20_i2s_write(i2s, TEGRA20_I2S_CTRL, i2s->reg_ctrl);
+}
+
+static int tegra20_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+			       struct snd_soc_dai *dai)
+{
+	struct tegra20_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+			tegra20_i2s_start_playback(i2s);
+		else
+			tegra20_i2s_start_capture(i2s);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+			tegra20_i2s_stop_playback(i2s);
+		else
+			tegra20_i2s_stop_capture(i2s);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int tegra20_i2s_probe(struct snd_soc_dai *dai)
+{
+	struct tegra20_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+
+	dai->capture_dma_data = &i2s->capture_dma_data;
+	dai->playback_dma_data = &i2s->playback_dma_data;
+
+	return 0;
+}
+
+static const struct snd_soc_dai_ops tegra20_i2s_dai_ops = {
+	.set_fmt	= tegra20_i2s_set_fmt,
+	.hw_params	= tegra20_i2s_hw_params,
+	.trigger	= tegra20_i2s_trigger,
+};
+
+static const struct snd_soc_dai_driver tegra20_i2s_dai_template = {
+	.probe = tegra20_i2s_probe,
+	.playback = {
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_8000_96000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	},
+	.capture = {
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_8000_96000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	},
+	.ops = &tegra20_i2s_dai_ops,
+	.symmetric_rates = 1,
+};
+
+static bool tegra20_i2s_wr_rd_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case TEGRA20_I2S_CTRL:
+	case TEGRA20_I2S_STATUS:
+	case TEGRA20_I2S_TIMING:
+	case TEGRA20_I2S_FIFO_SCR:
+	case TEGRA20_I2S_PCM_CTRL:
+	case TEGRA20_I2S_NW_CTRL:
+	case TEGRA20_I2S_TDM_CTRL:
+	case TEGRA20_I2S_TDM_TX_RX_CTRL:
+	case TEGRA20_I2S_FIFO1:
+	case TEGRA20_I2S_FIFO2:
+		return true;
+	default:
+		return false;
+	};
+}
+
+static bool tegra20_i2s_volatile_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case TEGRA20_I2S_STATUS:
+	case TEGRA20_I2S_FIFO_SCR:
+	case TEGRA20_I2S_FIFO1:
+	case TEGRA20_I2S_FIFO2:
+		return true;
+	default:
+		return false;
+	};
+}
+
+static bool tegra20_i2s_precious_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case TEGRA20_I2S_FIFO1:
+	case TEGRA20_I2S_FIFO2:
+		return true;
+	default:
+		return false;
+	};
+}
+
+static const struct regmap_config tegra20_i2s_regmap_config = {
+	.reg_bits = 32,
+	.reg_stride = 4,
+	.val_bits = 32,
+	.max_register = TEGRA20_I2S_FIFO2,
+	.writeable_reg = tegra20_i2s_wr_rd_reg,
+	.readable_reg = tegra20_i2s_wr_rd_reg,
+	.volatile_reg = tegra20_i2s_volatile_reg,
+	.precious_reg = tegra20_i2s_precious_reg,
+	.cache_type = REGCACHE_RBTREE,
+};
+
+static __devinit int tegra20_i2s_platform_probe(struct platform_device *pdev)
+{
+	struct tegra20_i2s *i2s;
+	struct resource *mem, *memregion, *dmareq;
+	u32 of_dma[2];
+	u32 dma_ch;
+	void __iomem *regs;
+	int ret;
+
+	i2s = devm_kzalloc(&pdev->dev, sizeof(struct tegra20_i2s), GFP_KERNEL);
+	if (!i2s) {
+		dev_err(&pdev->dev, "Can't allocate tegra20_i2s\n");
+		ret = -ENOMEM;
+		goto err;
+	}
+	dev_set_drvdata(&pdev->dev, i2s);
+
+	i2s->dai = tegra20_i2s_dai_template;
+	i2s->dai.name = dev_name(&pdev->dev);
+
+	i2s->clk_i2s = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(i2s->clk_i2s)) {
+		dev_err(&pdev->dev, "Can't retrieve i2s clock\n");
+		ret = PTR_ERR(i2s->clk_i2s);
+		goto err;
+	}
+
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!mem) {
+		dev_err(&pdev->dev, "No memory resource\n");
+		ret = -ENODEV;
+		goto err_clk_put;
+	}
+
+	dmareq = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+	if (!dmareq) {
+		if (of_property_read_u32_array(pdev->dev.of_node,
+					"nvidia,dma-request-selector",
+					of_dma, 2) < 0) {
+			dev_err(&pdev->dev, "No DMA resource\n");
+			ret = -ENODEV;
+			goto err_clk_put;
+		}
+		dma_ch = of_dma[1];
+	} else {
+		dma_ch = dmareq->start;
+	}
+
+	memregion = devm_request_mem_region(&pdev->dev, mem->start,
+					    resource_size(mem), DRV_NAME);
+	if (!memregion) {
+		dev_err(&pdev->dev, "Memory region already claimed\n");
+		ret = -EBUSY;
+		goto err_clk_put;
+	}
+
+	regs = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
+	if (!regs) {
+		dev_err(&pdev->dev, "ioremap failed\n");
+		ret = -ENOMEM;
+		goto err_clk_put;
+	}
+
+	i2s->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
+					    &tegra20_i2s_regmap_config);
+	if (IS_ERR(i2s->regmap)) {
+		dev_err(&pdev->dev, "regmap init failed\n");
+		ret = PTR_ERR(i2s->regmap);
+		goto err_clk_put;
+	}
+
+	i2s->capture_dma_data.addr = mem->start + TEGRA20_I2S_FIFO2;
+	i2s->capture_dma_data.wrap = 4;
+	i2s->capture_dma_data.width = 32;
+	i2s->capture_dma_data.req_sel = dma_ch;
+
+	i2s->playback_dma_data.addr = mem->start + TEGRA20_I2S_FIFO1;
+	i2s->playback_dma_data.wrap = 4;
+	i2s->playback_dma_data.width = 32;
+	i2s->playback_dma_data.req_sel = dma_ch;
+
+	i2s->reg_ctrl = TEGRA20_I2S_CTRL_FIFO_FORMAT_PACKED;
+
+	pm_runtime_enable(&pdev->dev);
+	if (!pm_runtime_enabled(&pdev->dev)) {
+		ret = tegra20_i2s_runtime_resume(&pdev->dev);
+		if (ret)
+			goto err_pm_disable;
+	}
+
+	ret = snd_soc_register_dai(&pdev->dev, &i2s->dai);
+	if (ret) {
+		dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
+		ret = -ENOMEM;
+		goto err_suspend;
+	}
+
+	ret = tegra_pcm_platform_register(&pdev->dev);
+	if (ret) {
+		dev_err(&pdev->dev, "Could not register PCM: %d\n", ret);
+		goto err_unregister_dai;
+	}
+
+	return 0;
+
+err_unregister_dai:
+	snd_soc_unregister_dai(&pdev->dev);
+err_suspend:
+	if (!pm_runtime_status_suspended(&pdev->dev))
+		tegra20_i2s_runtime_suspend(&pdev->dev);
+err_pm_disable:
+	pm_runtime_disable(&pdev->dev);
+err_clk_put:
+	clk_put(i2s->clk_i2s);
+err:
+	return ret;
+}
+
+static int __devexit tegra20_i2s_platform_remove(struct platform_device *pdev)
+{
+	struct tegra20_i2s *i2s = dev_get_drvdata(&pdev->dev);
+
+	pm_runtime_disable(&pdev->dev);
+	if (!pm_runtime_status_suspended(&pdev->dev))
+		tegra20_i2s_runtime_suspend(&pdev->dev);
+
+	tegra_pcm_platform_unregister(&pdev->dev);
+	snd_soc_unregister_dai(&pdev->dev);
+
+	clk_put(i2s->clk_i2s);
+
+	return 0;
+}
+
+static const struct of_device_id tegra20_i2s_of_match[] __devinitconst = {
+	{ .compatible = "nvidia,tegra20-i2s", },
+	{},
+};
+
+static const struct dev_pm_ops tegra20_i2s_pm_ops __devinitconst = {
+	SET_RUNTIME_PM_OPS(tegra20_i2s_runtime_suspend,
+			   tegra20_i2s_runtime_resume, NULL)
+};
+
+static struct platform_driver tegra20_i2s_driver = {
+	.driver = {
+		.name = DRV_NAME,
+		.owner = THIS_MODULE,
+		.of_match_table = tegra20_i2s_of_match,
+		.pm = &tegra20_i2s_pm_ops,
+	},
+	.probe = tegra20_i2s_platform_probe,
+	.remove = __devexit_p(tegra20_i2s_platform_remove),
+};
+module_platform_driver(tegra20_i2s_driver);
+
+MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
+MODULE_DESCRIPTION("Tegra20 I2S ASoC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, tegra20_i2s_of_match);
diff --git a/sound/soc/tegra/tegra20_i2s.h b/sound/soc/tegra/tegra20_i2s.h
new file mode 100644
index 0000000..a57efc6
--- /dev/null
+++ b/sound/soc/tegra/tegra20_i2s.h
@@ -0,0 +1,164 @@
+/*
+ * tegra20_i2s.h - Definitions for Tegra20 I2S driver
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 2010,2012 - NVIDIA, Inc.
+ *
+ * Based on code copyright/by:
+ *
+ * Copyright (c) 2009-2010, NVIDIA Corporation.
+ * Scott Peterson <speterson@nvidia.com>
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Iliyan Malchev <malchev@google.com>
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __TEGRA20_I2S_H__
+#define __TEGRA20_I2S_H__
+
+#include "tegra_pcm.h"
+
+/* Register offsets from TEGRA20_I2S1_BASE and TEGRA20_I2S2_BASE */
+
+#define TEGRA20_I2S_CTRL				0x00
+#define TEGRA20_I2S_STATUS				0x04
+#define TEGRA20_I2S_TIMING				0x08
+#define TEGRA20_I2S_FIFO_SCR				0x0c
+#define TEGRA20_I2S_PCM_CTRL				0x10
+#define TEGRA20_I2S_NW_CTRL				0x14
+#define TEGRA20_I2S_TDM_CTRL				0x20
+#define TEGRA20_I2S_TDM_TX_RX_CTRL			0x24
+#define TEGRA20_I2S_FIFO1				0x40
+#define TEGRA20_I2S_FIFO2				0x80
+
+/* Fields in TEGRA20_I2S_CTRL */
+
+#define TEGRA20_I2S_CTRL_FIFO2_TX_ENABLE		(1 << 30)
+#define TEGRA20_I2S_CTRL_FIFO1_ENABLE			(1 << 29)
+#define TEGRA20_I2S_CTRL_FIFO2_ENABLE			(1 << 28)
+#define TEGRA20_I2S_CTRL_FIFO1_RX_ENABLE		(1 << 27)
+#define TEGRA20_I2S_CTRL_FIFO_LPBK_ENABLE		(1 << 26)
+#define TEGRA20_I2S_CTRL_MASTER_ENABLE			(1 << 25)
+
+#define TEGRA20_I2S_LRCK_LEFT_LOW				0
+#define TEGRA20_I2S_LRCK_RIGHT_LOW			1
+
+#define TEGRA20_I2S_CTRL_LRCK_SHIFT			24
+#define TEGRA20_I2S_CTRL_LRCK_MASK			(1                          << TEGRA20_I2S_CTRL_LRCK_SHIFT)
+#define TEGRA20_I2S_CTRL_LRCK_L_LOW			(TEGRA20_I2S_LRCK_LEFT_LOW  << TEGRA20_I2S_CTRL_LRCK_SHIFT)
+#define TEGRA20_I2S_CTRL_LRCK_R_LOW			(TEGRA20_I2S_LRCK_RIGHT_LOW << TEGRA20_I2S_CTRL_LRCK_SHIFT)
+
+#define TEGRA20_I2S_BIT_FORMAT_I2S			0
+#define TEGRA20_I2S_BIT_FORMAT_RJM			1
+#define TEGRA20_I2S_BIT_FORMAT_LJM			2
+#define TEGRA20_I2S_BIT_FORMAT_DSP			3
+
+#define TEGRA20_I2S_CTRL_BIT_FORMAT_SHIFT		10
+#define TEGRA20_I2S_CTRL_BIT_FORMAT_MASK		(3                          << TEGRA20_I2S_CTRL_BIT_FORMAT_SHIFT)
+#define TEGRA20_I2S_CTRL_BIT_FORMAT_I2S			(TEGRA20_I2S_BIT_FORMAT_I2S << TEGRA20_I2S_CTRL_BIT_FORMAT_SHIFT)
+#define TEGRA20_I2S_CTRL_BIT_FORMAT_RJM			(TEGRA20_I2S_BIT_FORMAT_RJM << TEGRA20_I2S_CTRL_BIT_FORMAT_SHIFT)
+#define TEGRA20_I2S_CTRL_BIT_FORMAT_LJM			(TEGRA20_I2S_BIT_FORMAT_LJM << TEGRA20_I2S_CTRL_BIT_FORMAT_SHIFT)
+#define TEGRA20_I2S_CTRL_BIT_FORMAT_DSP			(TEGRA20_I2S_BIT_FORMAT_DSP << TEGRA20_I2S_CTRL_BIT_FORMAT_SHIFT)
+
+#define TEGRA20_I2S_BIT_SIZE_16				0
+#define TEGRA20_I2S_BIT_SIZE_20				1
+#define TEGRA20_I2S_BIT_SIZE_24				2
+#define TEGRA20_I2S_BIT_SIZE_32				3
+
+#define TEGRA20_I2S_CTRL_BIT_SIZE_SHIFT			8
+#define TEGRA20_I2S_CTRL_BIT_SIZE_MASK			(3                       << TEGRA20_I2S_CTRL_BIT_SIZE_SHIFT)
+#define TEGRA20_I2S_CTRL_BIT_SIZE_16			(TEGRA20_I2S_BIT_SIZE_16 << TEGRA20_I2S_CTRL_BIT_SIZE_SHIFT)
+#define TEGRA20_I2S_CTRL_BIT_SIZE_20			(TEGRA20_I2S_BIT_SIZE_20 << TEGRA20_I2S_CTRL_BIT_SIZE_SHIFT)
+#define TEGRA20_I2S_CTRL_BIT_SIZE_24			(TEGRA20_I2S_BIT_SIZE_24 << TEGRA20_I2S_CTRL_BIT_SIZE_SHIFT)
+#define TEGRA20_I2S_CTRL_BIT_SIZE_32			(TEGRA20_I2S_BIT_SIZE_32 << TEGRA20_I2S_CTRL_BIT_SIZE_SHIFT)
+
+#define TEGRA20_I2S_FIFO_16_LSB				0
+#define TEGRA20_I2S_FIFO_20_LSB				1
+#define TEGRA20_I2S_FIFO_24_LSB				2
+#define TEGRA20_I2S_FIFO_32				3
+#define TEGRA20_I2S_FIFO_PACKED				7
+
+#define TEGRA20_I2S_CTRL_FIFO_FORMAT_SHIFT		4
+#define TEGRA20_I2S_CTRL_FIFO_FORMAT_MASK		(7                       << TEGRA20_I2S_CTRL_FIFO_FORMAT_SHIFT)
+#define TEGRA20_I2S_CTRL_FIFO_FORMAT_16_LSB		(TEGRA20_I2S_FIFO_16_LSB << TEGRA20_I2S_CTRL_FIFO_FORMAT_SHIFT)
+#define TEGRA20_I2S_CTRL_FIFO_FORMAT_20_LSB		(TEGRA20_I2S_FIFO_20_LSB << TEGRA20_I2S_CTRL_FIFO_FORMAT_SHIFT)
+#define TEGRA20_I2S_CTRL_FIFO_FORMAT_24_LSB		(TEGRA20_I2S_FIFO_24_LSB << TEGRA20_I2S_CTRL_FIFO_FORMAT_SHIFT)
+#define TEGRA20_I2S_CTRL_FIFO_FORMAT_32			(TEGRA20_I2S_FIFO_32     << TEGRA20_I2S_CTRL_FIFO_FORMAT_SHIFT)
+#define TEGRA20_I2S_CTRL_FIFO_FORMAT_PACKED		(TEGRA20_I2S_FIFO_PACKED << TEGRA20_I2S_CTRL_FIFO_FORMAT_SHIFT)
+
+#define TEGRA20_I2S_CTRL_IE_FIFO1_ERR			(1 << 3)
+#define TEGRA20_I2S_CTRL_IE_FIFO2_ERR			(1 << 2)
+#define TEGRA20_I2S_CTRL_QE_FIFO1			(1 << 1)
+#define TEGRA20_I2S_CTRL_QE_FIFO2			(1 << 0)
+
+/* Fields in TEGRA20_I2S_STATUS */
+
+#define TEGRA20_I2S_STATUS_FIFO1_RDY			(1 << 31)
+#define TEGRA20_I2S_STATUS_FIFO2_RDY			(1 << 30)
+#define TEGRA20_I2S_STATUS_FIFO1_BSY			(1 << 29)
+#define TEGRA20_I2S_STATUS_FIFO2_BSY			(1 << 28)
+#define TEGRA20_I2S_STATUS_FIFO1_ERR			(1 << 3)
+#define TEGRA20_I2S_STATUS_FIFO2_ERR			(1 << 2)
+#define TEGRA20_I2S_STATUS_QS_FIFO1			(1 << 1)
+#define TEGRA20_I2S_STATUS_QS_FIFO2			(1 << 0)
+
+/* Fields in TEGRA20_I2S_TIMING */
+
+#define TEGRA20_I2S_TIMING_NON_SYM_ENABLE		(1 << 12)
+#define TEGRA20_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT	0
+#define TEGRA20_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US	0x7fff
+#define TEGRA20_I2S_TIMING_CHANNEL_BIT_COUNT_MASK	(TEGRA20_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US << TEGRA20_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT)
+
+/* Fields in TEGRA20_I2S_FIFO_SCR */
+
+#define TEGRA20_I2S_FIFO_SCR_FIFO2_FULL_EMPTY_COUNT_SHIFT	24
+#define TEGRA20_I2S_FIFO_SCR_FIFO1_FULL_EMPTY_COUNT_SHIFT	16
+#define TEGRA20_I2S_FIFO_SCR_FIFO_FULL_EMPTY_COUNT_MASK		0x3f
+
+#define TEGRA20_I2S_FIFO_SCR_FIFO2_CLR			(1 << 12)
+#define TEGRA20_I2S_FIFO_SCR_FIFO1_CLR			(1 << 8)
+
+#define TEGRA20_I2S_FIFO_ATN_LVL_ONE_SLOT		0
+#define TEGRA20_I2S_FIFO_ATN_LVL_FOUR_SLOTS		1
+#define TEGRA20_I2S_FIFO_ATN_LVL_EIGHT_SLOTS		2
+#define TEGRA20_I2S_FIFO_ATN_LVL_TWELVE_SLOTS		3
+
+#define TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT	4
+#define TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_MASK		(3 << TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT)
+#define TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_ONE_SLOT	(TEGRA20_I2S_FIFO_ATN_LVL_ONE_SLOT     << TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT)
+#define TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_FOUR_SLOTS	(TEGRA20_I2S_FIFO_ATN_LVL_FOUR_SLOTS   << TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT)
+#define TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_EIGHT_SLOTS	(TEGRA20_I2S_FIFO_ATN_LVL_EIGHT_SLOTS  << TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT)
+#define TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_TWELVE_SLOTS	(TEGRA20_I2S_FIFO_ATN_LVL_TWELVE_SLOTS << TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT)
+
+#define TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT	0
+#define TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_MASK		(3 << TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT)
+#define TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_ONE_SLOT	(TEGRA20_I2S_FIFO_ATN_LVL_ONE_SLOT     << TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT)
+#define TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_FOUR_SLOTS	(TEGRA20_I2S_FIFO_ATN_LVL_FOUR_SLOTS   << TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT)
+#define TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_EIGHT_SLOTS	(TEGRA20_I2S_FIFO_ATN_LVL_EIGHT_SLOTS  << TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT)
+#define TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_TWELVE_SLOTS	(TEGRA20_I2S_FIFO_ATN_LVL_TWELVE_SLOTS << TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT)
+
+struct tegra20_i2s {
+	struct snd_soc_dai_driver dai;
+	struct clk *clk_i2s;
+	struct tegra_pcm_dma_params capture_dma_data;
+	struct tegra_pcm_dma_params playback_dma_data;
+	struct regmap *regmap;
+	u32 reg_ctrl;
+};
+
+#endif
diff --git a/sound/soc/tegra/tegra20_spdif.c b/sound/soc/tegra/tegra20_spdif.c
new file mode 100644
index 0000000..f9b5741
--- /dev/null
+++ b/sound/soc/tegra/tegra20_spdif.c
@@ -0,0 +1,404 @@
+/*
+ * tegra20_spdif.c - Tegra20 SPDIF driver
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 2011-2012 - NVIDIA, 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "tegra20_spdif.h"
+
+#define DRV_NAME "tegra20-spdif"
+
+static inline void tegra20_spdif_write(struct tegra20_spdif *spdif, u32 reg,
+					u32 val)
+{
+	regmap_write(spdif->regmap, reg, val);
+}
+
+static inline u32 tegra20_spdif_read(struct tegra20_spdif *spdif, u32 reg)
+{
+	u32 val;
+	regmap_read(spdif->regmap, reg, &val);
+	return val;
+}
+
+static int tegra20_spdif_runtime_suspend(struct device *dev)
+{
+	struct tegra20_spdif *spdif = dev_get_drvdata(dev);
+
+	clk_disable(spdif->clk_spdif_out);
+
+	return 0;
+}
+
+static int tegra20_spdif_runtime_resume(struct device *dev)
+{
+	struct tegra20_spdif *spdif = dev_get_drvdata(dev);
+	int ret;
+
+	ret = clk_enable(spdif->clk_spdif_out);
+	if (ret) {
+		dev_err(dev, "clk_enable failed: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int tegra20_spdif_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params,
+				struct snd_soc_dai *dai)
+{
+	struct device *dev = substream->pcm->card->dev;
+	struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai);
+	int ret, spdifclock;
+
+	spdif->reg_ctrl &= ~TEGRA20_SPDIF_CTRL_PACK;
+	spdif->reg_ctrl &= ~TEGRA20_SPDIF_CTRL_BIT_MODE_MASK;
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		spdif->reg_ctrl |= TEGRA20_SPDIF_CTRL_PACK;
+		spdif->reg_ctrl |= TEGRA20_SPDIF_CTRL_BIT_MODE_16BIT;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (params_rate(params)) {
+	case 32000:
+		spdifclock = 4096000;
+		break;
+	case 44100:
+		spdifclock = 5644800;
+		break;
+	case 48000:
+		spdifclock = 6144000;
+		break;
+	case 88200:
+		spdifclock = 11289600;
+		break;
+	case 96000:
+		spdifclock = 12288000;
+		break;
+	case 176400:
+		spdifclock = 22579200;
+		break;
+	case 192000:
+		spdifclock = 24576000;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = clk_set_rate(spdif->clk_spdif_out, spdifclock);
+	if (ret) {
+		dev_err(dev, "Can't set SPDIF clock rate: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void tegra20_spdif_start_playback(struct tegra20_spdif *spdif)
+{
+	spdif->reg_ctrl |= TEGRA20_SPDIF_CTRL_TX_EN;
+	tegra20_spdif_write(spdif, TEGRA20_SPDIF_CTRL, spdif->reg_ctrl);
+}
+
+static void tegra20_spdif_stop_playback(struct tegra20_spdif *spdif)
+{
+	spdif->reg_ctrl &= ~TEGRA20_SPDIF_CTRL_TX_EN;
+	tegra20_spdif_write(spdif, TEGRA20_SPDIF_CTRL, spdif->reg_ctrl);
+}
+
+static int tegra20_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
+				struct snd_soc_dai *dai)
+{
+	struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		tegra20_spdif_start_playback(spdif);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		tegra20_spdif_stop_playback(spdif);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int tegra20_spdif_probe(struct snd_soc_dai *dai)
+{
+	struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai);
+
+	dai->capture_dma_data = NULL;
+	dai->playback_dma_data = &spdif->playback_dma_data;
+
+	return 0;
+}
+
+static const struct snd_soc_dai_ops tegra20_spdif_dai_ops = {
+	.hw_params	= tegra20_spdif_hw_params,
+	.trigger	= tegra20_spdif_trigger,
+};
+
+static struct snd_soc_dai_driver tegra20_spdif_dai = {
+	.name = DRV_NAME,
+	.probe = tegra20_spdif_probe,
+	.playback = {
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+				SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	},
+	.ops = &tegra20_spdif_dai_ops,
+};
+
+static bool tegra20_spdif_wr_rd_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case TEGRA20_SPDIF_CTRL:
+	case TEGRA20_SPDIF_STATUS:
+	case TEGRA20_SPDIF_STROBE_CTRL:
+	case TEGRA20_SPDIF_DATA_FIFO_CSR:
+	case TEGRA20_SPDIF_DATA_OUT:
+	case TEGRA20_SPDIF_DATA_IN:
+	case TEGRA20_SPDIF_CH_STA_RX_A:
+	case TEGRA20_SPDIF_CH_STA_RX_B:
+	case TEGRA20_SPDIF_CH_STA_RX_C:
+	case TEGRA20_SPDIF_CH_STA_RX_D:
+	case TEGRA20_SPDIF_CH_STA_RX_E:
+	case TEGRA20_SPDIF_CH_STA_RX_F:
+	case TEGRA20_SPDIF_CH_STA_TX_A:
+	case TEGRA20_SPDIF_CH_STA_TX_B:
+	case TEGRA20_SPDIF_CH_STA_TX_C:
+	case TEGRA20_SPDIF_CH_STA_TX_D:
+	case TEGRA20_SPDIF_CH_STA_TX_E:
+	case TEGRA20_SPDIF_CH_STA_TX_F:
+	case TEGRA20_SPDIF_USR_STA_RX_A:
+	case TEGRA20_SPDIF_USR_DAT_TX_A:
+		return true;
+	default:
+		return false;
+	};
+}
+
+static bool tegra20_spdif_volatile_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case TEGRA20_SPDIF_STATUS:
+	case TEGRA20_SPDIF_DATA_FIFO_CSR:
+	case TEGRA20_SPDIF_DATA_OUT:
+	case TEGRA20_SPDIF_DATA_IN:
+	case TEGRA20_SPDIF_CH_STA_RX_A:
+	case TEGRA20_SPDIF_CH_STA_RX_B:
+	case TEGRA20_SPDIF_CH_STA_RX_C:
+	case TEGRA20_SPDIF_CH_STA_RX_D:
+	case TEGRA20_SPDIF_CH_STA_RX_E:
+	case TEGRA20_SPDIF_CH_STA_RX_F:
+	case TEGRA20_SPDIF_USR_STA_RX_A:
+	case TEGRA20_SPDIF_USR_DAT_TX_A:
+		return true;
+	default:
+		return false;
+	};
+}
+
+static bool tegra20_spdif_precious_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case TEGRA20_SPDIF_DATA_OUT:
+	case TEGRA20_SPDIF_DATA_IN:
+	case TEGRA20_SPDIF_USR_STA_RX_A:
+	case TEGRA20_SPDIF_USR_DAT_TX_A:
+		return true;
+	default:
+		return false;
+	};
+}
+
+static const struct regmap_config tegra20_spdif_regmap_config = {
+	.reg_bits = 32,
+	.reg_stride = 4,
+	.val_bits = 32,
+	.max_register = TEGRA20_SPDIF_USR_DAT_TX_A,
+	.writeable_reg = tegra20_spdif_wr_rd_reg,
+	.readable_reg = tegra20_spdif_wr_rd_reg,
+	.volatile_reg = tegra20_spdif_volatile_reg,
+	.precious_reg = tegra20_spdif_precious_reg,
+	.cache_type = REGCACHE_RBTREE,
+};
+
+static __devinit int tegra20_spdif_platform_probe(struct platform_device *pdev)
+{
+	struct tegra20_spdif *spdif;
+	struct resource *mem, *memregion, *dmareq;
+	void __iomem *regs;
+	int ret;
+
+	spdif = devm_kzalloc(&pdev->dev, sizeof(struct tegra20_spdif),
+			     GFP_KERNEL);
+	if (!spdif) {
+		dev_err(&pdev->dev, "Can't allocate tegra20_spdif\n");
+		ret = -ENOMEM;
+		goto err;
+	}
+	dev_set_drvdata(&pdev->dev, spdif);
+
+	spdif->clk_spdif_out = clk_get(&pdev->dev, "spdif_out");
+	if (IS_ERR(spdif->clk_spdif_out)) {
+		pr_err("Can't retrieve spdif clock\n");
+		ret = PTR_ERR(spdif->clk_spdif_out);
+		goto err;
+	}
+
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!mem) {
+		dev_err(&pdev->dev, "No memory resource\n");
+		ret = -ENODEV;
+		goto err_clk_put;
+	}
+
+	dmareq = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+	if (!dmareq) {
+		dev_err(&pdev->dev, "No DMA resource\n");
+		ret = -ENODEV;
+		goto err_clk_put;
+	}
+
+	memregion = devm_request_mem_region(&pdev->dev, mem->start,
+					    resource_size(mem), DRV_NAME);
+	if (!memregion) {
+		dev_err(&pdev->dev, "Memory region already claimed\n");
+		ret = -EBUSY;
+		goto err_clk_put;
+	}
+
+	regs = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
+	if (!regs) {
+		dev_err(&pdev->dev, "ioremap failed\n");
+		ret = -ENOMEM;
+		goto err_clk_put;
+	}
+
+	spdif->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
+					    &tegra20_spdif_regmap_config);
+	if (IS_ERR(spdif->regmap)) {
+		dev_err(&pdev->dev, "regmap init failed\n");
+		ret = PTR_ERR(spdif->regmap);
+		goto err_clk_put;
+	}
+
+	spdif->playback_dma_data.addr = mem->start + TEGRA20_SPDIF_DATA_OUT;
+	spdif->playback_dma_data.wrap = 4;
+	spdif->playback_dma_data.width = 32;
+	spdif->playback_dma_data.req_sel = dmareq->start;
+
+	pm_runtime_enable(&pdev->dev);
+	if (!pm_runtime_enabled(&pdev->dev)) {
+		ret = tegra20_spdif_runtime_resume(&pdev->dev);
+		if (ret)
+			goto err_pm_disable;
+	}
+
+	ret = snd_soc_register_dai(&pdev->dev, &tegra20_spdif_dai);
+	if (ret) {
+		dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
+		ret = -ENOMEM;
+		goto err_suspend;
+	}
+
+	ret = tegra_pcm_platform_register(&pdev->dev);
+	if (ret) {
+		dev_err(&pdev->dev, "Could not register PCM: %d\n", ret);
+		goto err_unregister_dai;
+	}
+
+	return 0;
+
+err_unregister_dai:
+	snd_soc_unregister_dai(&pdev->dev);
+err_suspend:
+	if (!pm_runtime_status_suspended(&pdev->dev))
+		tegra20_spdif_runtime_suspend(&pdev->dev);
+err_pm_disable:
+	pm_runtime_disable(&pdev->dev);
+err_clk_put:
+	clk_put(spdif->clk_spdif_out);
+err:
+	return ret;
+}
+
+static int __devexit tegra20_spdif_platform_remove(struct platform_device *pdev)
+{
+	struct tegra20_spdif *spdif = dev_get_drvdata(&pdev->dev);
+
+	pm_runtime_disable(&pdev->dev);
+	if (!pm_runtime_status_suspended(&pdev->dev))
+		tegra20_spdif_runtime_suspend(&pdev->dev);
+
+	tegra_pcm_platform_unregister(&pdev->dev);
+	snd_soc_unregister_dai(&pdev->dev);
+
+	clk_put(spdif->clk_spdif_out);
+
+	return 0;
+}
+
+static const struct dev_pm_ops tegra20_spdif_pm_ops __devinitconst = {
+	SET_RUNTIME_PM_OPS(tegra20_spdif_runtime_suspend,
+			   tegra20_spdif_runtime_resume, NULL)
+};
+
+static struct platform_driver tegra20_spdif_driver = {
+	.driver = {
+		.name = DRV_NAME,
+		.owner = THIS_MODULE,
+		.pm = &tegra20_spdif_pm_ops,
+	},
+	.probe = tegra20_spdif_platform_probe,
+	.remove = __devexit_p(tegra20_spdif_platform_remove),
+};
+
+module_platform_driver(tegra20_spdif_driver);
+
+MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
+MODULE_DESCRIPTION("Tegra20 SPDIF ASoC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/tegra/tegra20_spdif.h b/sound/soc/tegra/tegra20_spdif.h
new file mode 100644
index 0000000..ed75652
--- /dev/null
+++ b/sound/soc/tegra/tegra20_spdif.h
@@ -0,0 +1,471 @@
+/*
+ * tegra20_spdif.h - Definitions for Tegra20 SPDIF driver
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 2011 - NVIDIA, Inc.
+ *
+ * Based on code copyright/by:
+ * Copyright (c) 2008-2009, NVIDIA Corporation
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __TEGRA20_SPDIF_H__
+#define __TEGRA20_SPDIF_H__
+
+#include "tegra_pcm.h"
+
+/* Offsets from TEGRA20_SPDIF_BASE */
+
+#define TEGRA20_SPDIF_CTRL					0x0
+#define TEGRA20_SPDIF_STATUS					0x4
+#define TEGRA20_SPDIF_STROBE_CTRL				0x8
+#define TEGRA20_SPDIF_DATA_FIFO_CSR				0x0C
+#define TEGRA20_SPDIF_DATA_OUT					0x40
+#define TEGRA20_SPDIF_DATA_IN					0x80
+#define TEGRA20_SPDIF_CH_STA_RX_A				0x100
+#define TEGRA20_SPDIF_CH_STA_RX_B				0x104
+#define TEGRA20_SPDIF_CH_STA_RX_C				0x108
+#define TEGRA20_SPDIF_CH_STA_RX_D				0x10C
+#define TEGRA20_SPDIF_CH_STA_RX_E				0x110
+#define TEGRA20_SPDIF_CH_STA_RX_F				0x114
+#define TEGRA20_SPDIF_CH_STA_TX_A				0x140
+#define TEGRA20_SPDIF_CH_STA_TX_B				0x144
+#define TEGRA20_SPDIF_CH_STA_TX_C				0x148
+#define TEGRA20_SPDIF_CH_STA_TX_D				0x14C
+#define TEGRA20_SPDIF_CH_STA_TX_E				0x150
+#define TEGRA20_SPDIF_CH_STA_TX_F				0x154
+#define TEGRA20_SPDIF_USR_STA_RX_A				0x180
+#define TEGRA20_SPDIF_USR_DAT_TX_A				0x1C0
+
+/* Fields in TEGRA20_SPDIF_CTRL */
+
+/* Start capturing from 0=right, 1=left channel */
+#define TEGRA20_SPDIF_CTRL_CAP_LC				(1 << 30)
+
+/* SPDIF receiver(RX) enable */
+#define TEGRA20_SPDIF_CTRL_RX_EN				(1 << 29)
+
+/* SPDIF Transmitter(TX) enable */
+#define TEGRA20_SPDIF_CTRL_TX_EN				(1 << 28)
+
+/* Transmit Channel status */
+#define TEGRA20_SPDIF_CTRL_TC_EN				(1 << 27)
+
+/* Transmit user Data */
+#define TEGRA20_SPDIF_CTRL_TU_EN				(1 << 26)
+
+/* Interrupt on transmit error */
+#define TEGRA20_SPDIF_CTRL_IE_TXE				(1 << 25)
+
+/* Interrupt on receive error */
+#define TEGRA20_SPDIF_CTRL_IE_RXE				(1 << 24)
+
+/* Interrupt on invalid preamble */
+#define TEGRA20_SPDIF_CTRL_IE_P					(1 << 23)
+
+/* Interrupt on "B" preamble */
+#define TEGRA20_SPDIF_CTRL_IE_B					(1 << 22)
+
+/* Interrupt when block of channel status received */
+#define TEGRA20_SPDIF_CTRL_IE_C					(1 << 21)
+
+/* Interrupt when a valid information unit (IU) is received */
+#define TEGRA20_SPDIF_CTRL_IE_U					(1 << 20)
+
+/* Interrupt when RX user FIFO attention level is reached */
+#define TEGRA20_SPDIF_CTRL_QE_RU				(1 << 19)
+
+/* Interrupt when TX user FIFO attention level is reached */
+#define TEGRA20_SPDIF_CTRL_QE_TU				(1 << 18)
+
+/* Interrupt when RX data FIFO attention level is reached */
+#define TEGRA20_SPDIF_CTRL_QE_RX				(1 << 17)
+
+/* Interrupt when TX data FIFO attention level is reached */
+#define TEGRA20_SPDIF_CTRL_QE_TX				(1 << 16)
+
+/* Loopback test mode enable */
+#define TEGRA20_SPDIF_CTRL_LBK_EN				(1 << 15)
+
+/*
+ * Pack data mode:
+ * 0 = Single data (16 bit needs to be  padded to match the
+ *     interface data bit size).
+ * 1 = Packeted left/right channel data into a single word.
+ */
+#define TEGRA20_SPDIF_CTRL_PACK					(1 << 14)
+
+/*
+ * 00 = 16bit data
+ * 01 = 20bit data
+ * 10 = 24bit data
+ * 11 = raw data
+ */
+#define TEGRA20_SPDIF_BIT_MODE_16BIT				0
+#define TEGRA20_SPDIF_BIT_MODE_20BIT				1
+#define TEGRA20_SPDIF_BIT_MODE_24BIT				2
+#define TEGRA20_SPDIF_BIT_MODE_RAW				3
+
+#define TEGRA20_SPDIF_CTRL_BIT_MODE_SHIFT			12
+#define TEGRA20_SPDIF_CTRL_BIT_MODE_MASK			(3                            << TEGRA20_SPDIF_CTRL_BIT_MODE_SHIFT)
+#define TEGRA20_SPDIF_CTRL_BIT_MODE_16BIT			(TEGRA20_SPDIF_BIT_MODE_16BIT << TEGRA20_SPDIF_CTRL_BIT_MODE_SHIFT)
+#define TEGRA20_SPDIF_CTRL_BIT_MODE_20BIT			(TEGRA20_SPDIF_BIT_MODE_20BIT << TEGRA20_SPDIF_CTRL_BIT_MODE_SHIFT)
+#define TEGRA20_SPDIF_CTRL_BIT_MODE_24BIT			(TEGRA20_SPDIF_BIT_MODE_24BIT << TEGRA20_SPDIF_CTRL_BIT_MODE_SHIFT)
+#define TEGRA20_SPDIF_CTRL_BIT_MODE_RAW				(TEGRA20_SPDIF_BIT_MODE_RAW   << TEGRA20_SPDIF_CTRL_BIT_MODE_SHIFT)
+
+/* Fields in TEGRA20_SPDIF_STATUS */
+
+/*
+ * Note: IS_P, IS_B, IS_C, and IS_U are sticky bits. Software must
+ * write a 1 to the corresponding bit location to clear the status.
+ */
+
+/*
+ * Receiver(RX) shifter is busy receiving data.
+ * This bit is asserted when the receiver first locked onto the
+ * preamble of the data stream after RX_EN is asserted. This bit is
+ * deasserted when either,
+ * (a) the end of a frame is reached after RX_EN is deeasserted, or
+ * (b) the SPDIF data stream becomes inactive.
+ */
+#define TEGRA20_SPDIF_STATUS_RX_BSY				(1 << 29)
+
+/*
+ * Transmitter(TX) shifter is busy transmitting data.
+ * This bit is asserted when TX_EN is asserted.
+ * This bit is deasserted when the end of a frame is reached after
+ * TX_EN is deasserted.
+ */
+#define TEGRA20_SPDIF_STATUS_TX_BSY				(1 << 28)
+
+/*
+ * TX is busy shifting out channel status.
+ * This bit is asserted when both TX_EN and TC_EN are asserted and
+ * data from CH_STA_TX_A register is loaded into the internal shifter.
+ * This bit is deasserted when either,
+ * (a) the end of a frame is reached after TX_EN is deasserted, or
+ * (b) CH_STA_TX_F register is loaded into the internal shifter.
+ */
+#define TEGRA20_SPDIF_STATUS_TC_BSY				(1 << 27)
+
+/*
+ * TX User data FIFO busy.
+ * This bit is asserted when TX_EN and TXU_EN are asserted and
+ * there's data in the TX user FIFO.  This bit is deassert when either,
+ * (a) the end of a frame is reached after TX_EN is deasserted, or
+ * (b) there's no data left in the TX user FIFO.
+ */
+#define TEGRA20_SPDIF_STATUS_TU_BSY				(1 << 26)
+
+/* TX FIFO Underrun error status */
+#define TEGRA20_SPDIF_STATUS_TX_ERR				(1 << 25)
+
+/* RX FIFO Overrun error status */
+#define TEGRA20_SPDIF_STATUS_RX_ERR				(1 << 24)
+
+/* Preamble status: 0=Preamble OK, 1=bad/missing preamble */
+#define TEGRA20_SPDIF_STATUS_IS_P				(1 << 23)
+
+/* B-preamble detection status: 0=not detected, 1=B-preamble detected */
+#define TEGRA20_SPDIF_STATUS_IS_B				(1 << 22)
+
+/*
+ * RX channel block data receive status:
+ * 0=entire block not recieved yet.
+ * 1=received entire block of channel status,
+ */
+#define TEGRA20_SPDIF_STATUS_IS_C				(1 << 21)
+
+/* RX User Data Valid flag:  1=valid IU detected, 0 = no IU detected. */
+#define TEGRA20_SPDIF_STATUS_IS_U				(1 << 20)
+
+/*
+ * RX User FIFO Status:
+ * 1=attention level reached, 0=attention level not reached.
+ */
+#define TEGRA20_SPDIF_STATUS_QS_RU				(1 << 19)
+
+/*
+ * TX User FIFO Status:
+ * 1=attention level reached, 0=attention level not reached.
+ */
+#define TEGRA20_SPDIF_STATUS_QS_TU				(1 << 18)
+
+/*
+ * RX Data FIFO Status:
+ * 1=attention level reached, 0=attention level not reached.
+ */
+#define TEGRA20_SPDIF_STATUS_QS_RX				(1 << 17)
+
+/*
+ * TX Data FIFO Status:
+ * 1=attention level reached, 0=attention level not reached.
+ */
+#define TEGRA20_SPDIF_STATUS_QS_TX				(1 << 16)
+
+/* Fields in TEGRA20_SPDIF_STROBE_CTRL */
+
+/*
+ * Indicates the approximate number of detected SPDIFIN clocks within a
+ * bi-phase period.
+ */
+#define TEGRA20_SPDIF_STROBE_CTRL_PERIOD_SHIFT			16
+#define TEGRA20_SPDIF_STROBE_CTRL_PERIOD_MASK			(0xff << TEGRA20_SPDIF_STROBE_CTRL_PERIOD_SHIFT)
+
+/* Data strobe mode: 0=Auto-locked 1=Manual locked */
+#define TEGRA20_SPDIF_STROBE_CTRL_STROBE			(1 << 15)
+
+/*
+ * Manual data strobe time within the bi-phase clock period (in terms of
+ * the number of over-sampling clocks).
+ */
+#define TEGRA20_SPDIF_STROBE_CTRL_DATA_STROBES_SHIFT		8
+#define TEGRA20_SPDIF_STROBE_CTRL_DATA_STROBES_MASK		(0x1f << TEGRA20_SPDIF_STROBE_CTRL_DATA_STROBES_SHIFT)
+
+/*
+ * Manual SPDIFIN bi-phase clock period (in terms of the number of
+ * over-sampling clocks).
+ */
+#define TEGRA20_SPDIF_STROBE_CTRL_CLOCK_PERIOD_SHIFT		0
+#define TEGRA20_SPDIF_STROBE_CTRL_CLOCK_PERIOD_MASK		(0x3f << TEGRA20_SPDIF_STROBE_CTRL_CLOCK_PERIOD_SHIFT)
+
+/* Fields in SPDIF_DATA_FIFO_CSR */
+
+/* Clear Receiver User FIFO (RX USR.FIFO) */
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_RU_CLR			(1 << 31)
+
+#define TEGRA20_SPDIF_FIFO_ATN_LVL_U_ONE_SLOT			0
+#define TEGRA20_SPDIF_FIFO_ATN_LVL_U_TWO_SLOTS			1
+#define TEGRA20_SPDIF_FIFO_ATN_LVL_U_THREE_SLOTS		2
+#define TEGRA20_SPDIF_FIFO_ATN_LVL_U_FOUR_SLOTS			3
+
+/* RU FIFO attention level */
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT		29
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_MASK		\
+		(0x3                                      << TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT)
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_RU1_WORD_FULL	\
+		(TEGRA20_SPDIF_FIFO_ATN_LVL_U_ONE_SLOT    << TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT)
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_RU2_WORD_FULL	\
+		(TEGRA20_SPDIF_FIFO_ATN_LVL_U_TWO_SLOTS   << TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT)
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_RU3_WORD_FULL	\
+		(TEGRA20_SPDIF_FIFO_ATN_LVL_U_THREE_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT)
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_RU4_WORD_FULL	\
+		(TEGRA20_SPDIF_FIFO_ATN_LVL_U_FOUR_SLOTS  << TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT)
+
+/* Number of RX USR.FIFO levels with valid data. */
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_RU_FULL_COUNT_SHIFT		24
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_RU_FULL_COUNT_MASK		(0x1f << TEGRA20_SPDIF_DATA_FIFO_CSR_RU_FULL_COUNT_SHIFT)
+
+/* Clear Transmitter User FIFO (TX USR.FIFO) */
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_TU_CLR			(1 << 23)
+
+/* TU FIFO attention level */
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT		21
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_MASK		\
+		(0x3                                      << TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT)
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_TU1_WORD_FULL	\
+		(TEGRA20_SPDIF_FIFO_ATN_LVL_U_ONE_SLOT    << TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT)
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_TU2_WORD_FULL	\
+		(TEGRA20_SPDIF_FIFO_ATN_LVL_U_TWO_SLOTS   << TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT)
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_TU3_WORD_FULL	\
+		(TEGRA20_SPDIF_FIFO_ATN_LVL_U_THREE_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT)
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_TU4_WORD_FULL	\
+		(TEGRA20_SPDIF_FIFO_ATN_LVL_U_FOUR_SLOTS  << TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT)
+
+/* Number of TX USR.FIFO levels that could be filled. */
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_TU_EMPTY_COUNT_SHIFT	16
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_TU_EMPTY_COUNT_MASK		(0x1f << SPDIF_DATA_FIFO_CSR_TU_EMPTY_COUNT_SHIFT)
+
+/* Clear Receiver Data FIFO (RX DATA.FIFO) */
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_RX_CLR			(1 << 15)
+
+#define TEGRA20_SPDIF_FIFO_ATN_LVL_D_ONE_SLOT			0
+#define TEGRA20_SPDIF_FIFO_ATN_LVL_D_FOUR_SLOTS			1
+#define TEGRA20_SPDIF_FIFO_ATN_LVL_D_EIGHT_SLOTS		2
+#define TEGRA20_SPDIF_FIFO_ATN_LVL_D_TWELVE_SLOTS		3
+
+/* RU FIFO attention level */
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT		13
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_MASK		\
+		(0x3                                       << TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT)
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_RU1_WORD_FULL	\
+		(TEGRA20_SPDIF_FIFO_ATN_LVL_D_ONE_SLOT     << TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT)
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_RU4_WORD_FULL	\
+		(TEGRA20_SPDIF_FIFO_ATN_LVL_D_FOUR_SLOTS   << TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT)
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_RU8_WORD_FULL	\
+		(TEGRA20_SPDIF_FIFO_ATN_LVL_D_EIGHT_SLOTS  << TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT)
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_RU12_WORD_FULL	\
+		(TEGRA20_SPDIF_FIFO_ATN_LVL_D_TWELVE_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT)
+
+/* Number of RX DATA.FIFO levels with valid data. */
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_RX_FULL_COUNT_SHIFT		8
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_RX_FULL_COUNT_MASK		(0x1f << TEGRA20_SPDIF_DATA_FIFO_CSR_RX_FULL_COUNT_SHIFT)
+
+/* Clear Transmitter Data FIFO (TX DATA.FIFO) */
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_TX_CLR			(1 << 7)
+
+/* TU FIFO attention level */
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT		5
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_MASK		\
+		(0x3                                       << TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT)
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU1_WORD_FULL	\
+		(TEGRA20_SPDIF_FIFO_ATN_LVL_D_ONE_SLOT     << TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT)
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU4_WORD_FULL	\
+		(TEGRA20_SPDIF_FIFO_ATN_LVL_D_FOUR_SLOTS   << TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT)
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU8_WORD_FULL	\
+		(TEGRA20_SPDIF_FIFO_ATN_LVL_D_EIGHT_SLOTS  << TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT)
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU12_WORD_FULL	\
+		(TEGRA20_SPDIF_FIFO_ATN_LVL_D_TWELVE_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT)
+
+/* Number of TX DATA.FIFO levels that could be filled. */
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_TX_EMPTY_COUNT_SHIFT	0
+#define TEGRA20_SPDIF_DATA_FIFO_CSR_TX_EMPTY_COUNT_MASK		(0x1f << SPDIF_DATA_FIFO_CSR_TX_EMPTY_COUNT_SHIFT)
+
+/* Fields in TEGRA20_SPDIF_DATA_OUT */
+
+/*
+ * This register has 5 different formats:
+ * 16-bit        (BIT_MODE=00, PACK=0)
+ * 20-bit        (BIT_MODE=01, PACK=0)
+ * 24-bit        (BIT_MODE=10, PACK=0)
+ * raw           (BIT_MODE=11, PACK=0)
+ * 16-bit packed (BIT_MODE=00, PACK=1)
+ */
+
+#define TEGRA20_SPDIF_DATA_OUT_DATA_16_SHIFT			0
+#define TEGRA20_SPDIF_DATA_OUT_DATA_16_MASK			(0xffff << TEGRA20_SPDIF_DATA_OUT_DATA_16_SHIFT)
+
+#define TEGRA20_SPDIF_DATA_OUT_DATA_20_SHIFT			0
+#define TEGRA20_SPDIF_DATA_OUT_DATA_20_MASK			(0xfffff << TEGRA20_SPDIF_DATA_OUT_DATA_20_SHIFT)
+
+#define TEGRA20_SPDIF_DATA_OUT_DATA_24_SHIFT			0
+#define TEGRA20_SPDIF_DATA_OUT_DATA_24_MASK			(0xffffff << TEGRA20_SPDIF_DATA_OUT_DATA_24_SHIFT)
+
+#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_P			(1 << 31)
+#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_C			(1 << 30)
+#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_U			(1 << 29)
+#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_V			(1 << 28)
+
+#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_DATA_SHIFT		8
+#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_DATA_MASK		(0xfffff << TEGRA20_SPDIF_DATA_OUT_DATA_RAW_DATA_SHIFT)
+
+#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_AUX_SHIFT		4
+#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_AUX_MASK		(0xf << TEGRA20_SPDIF_DATA_OUT_DATA_RAW_AUX_SHIFT)
+
+#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_PREAMBLE_SHIFT		0
+#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_PREAMBLE_MASK		(0xf << TEGRA20_SPDIF_DATA_OUT_DATA_RAW_PREAMBLE_SHIFT)
+
+#define TEGRA20_SPDIF_DATA_OUT_DATA_16_PACKED_RIGHT_SHIFT	16
+#define TEGRA20_SPDIF_DATA_OUT_DATA_16_PACKED_RIGHT_MASK	(0xffff << TEGRA20_SPDIF_DATA_OUT_DATA_16_PACKED_RIGHT_SHIFT)
+
+#define TEGRA20_SPDIF_DATA_OUT_DATA_16_PACKED_LEFT_SHIFT	0
+#define TEGRA20_SPDIF_DATA_OUT_DATA_16_PACKED_LEFT_MASK		(0xffff << TEGRA20_SPDIF_DATA_OUT_DATA_16_PACKED_LEFT_SHIFT)
+
+/* Fields in TEGRA20_SPDIF_DATA_IN */
+
+/*
+ * This register has 5 different formats:
+ * 16-bit        (BIT_MODE=00, PACK=0)
+ * 20-bit        (BIT_MODE=01, PACK=0)
+ * 24-bit        (BIT_MODE=10, PACK=0)
+ * raw           (BIT_MODE=11, PACK=0)
+ * 16-bit packed (BIT_MODE=00, PACK=1)
+ *
+ * Bits 31:24 are common to all modes except 16-bit packed
+ */
+
+#define TEGRA20_SPDIF_DATA_IN_DATA_P				(1 << 31)
+#define TEGRA20_SPDIF_DATA_IN_DATA_C				(1 << 30)
+#define TEGRA20_SPDIF_DATA_IN_DATA_U				(1 << 29)
+#define TEGRA20_SPDIF_DATA_IN_DATA_V				(1 << 28)
+
+#define TEGRA20_SPDIF_DATA_IN_DATA_PREAMBLE_SHIFT		24
+#define TEGRA20_SPDIF_DATA_IN_DATA_PREAMBLE_MASK		(0xf << TEGRA20_SPDIF_DATA_IN_DATA_PREAMBLE_SHIFT)
+
+#define TEGRA20_SPDIF_DATA_IN_DATA_16_SHIFT			0
+#define TEGRA20_SPDIF_DATA_IN_DATA_16_MASK			(0xffff << TEGRA20_SPDIF_DATA_IN_DATA_16_SHIFT)
+
+#define TEGRA20_SPDIF_DATA_IN_DATA_20_SHIFT			0
+#define TEGRA20_SPDIF_DATA_IN_DATA_20_MASK			(0xfffff << TEGRA20_SPDIF_DATA_IN_DATA_20_SHIFT)
+
+#define TEGRA20_SPDIF_DATA_IN_DATA_24_SHIFT			0
+#define TEGRA20_SPDIF_DATA_IN_DATA_24_MASK			(0xffffff << TEGRA20_SPDIF_DATA_IN_DATA_24_SHIFT)
+
+#define TEGRA20_SPDIF_DATA_IN_DATA_RAW_DATA_SHIFT		8
+#define TEGRA20_SPDIF_DATA_IN_DATA_RAW_DATA_MASK		(0xfffff << TEGRA20_SPDIF_DATA_IN_DATA_RAW_DATA_SHIFT)
+
+#define TEGRA20_SPDIF_DATA_IN_DATA_RAW_AUX_SHIFT		4
+#define TEGRA20_SPDIF_DATA_IN_DATA_RAW_AUX_MASK			(0xf << TEGRA20_SPDIF_DATA_IN_DATA_RAW_AUX_SHIFT)
+
+#define TEGRA20_SPDIF_DATA_IN_DATA_RAW_PREAMBLE_SHIFT		0
+#define TEGRA20_SPDIF_DATA_IN_DATA_RAW_PREAMBLE_MASK		(0xf << TEGRA20_SPDIF_DATA_IN_DATA_RAW_PREAMBLE_SHIFT)
+
+#define TEGRA20_SPDIF_DATA_IN_DATA_16_PACKED_RIGHT_SHIFT	16
+#define TEGRA20_SPDIF_DATA_IN_DATA_16_PACKED_RIGHT_MASK		(0xffff << TEGRA20_SPDIF_DATA_IN_DATA_16_PACKED_RIGHT_SHIFT)
+
+#define TEGRA20_SPDIF_DATA_IN_DATA_16_PACKED_LEFT_SHIFT		0
+#define TEGRA20_SPDIF_DATA_IN_DATA_16_PACKED_LEFT_MASK		(0xffff << TEGRA20_SPDIF_DATA_IN_DATA_16_PACKED_LEFT_SHIFT)
+
+/* Fields in TEGRA20_SPDIF_CH_STA_RX_A */
+/* Fields in TEGRA20_SPDIF_CH_STA_RX_B */
+/* Fields in TEGRA20_SPDIF_CH_STA_RX_C */
+/* Fields in TEGRA20_SPDIF_CH_STA_RX_D */
+/* Fields in TEGRA20_SPDIF_CH_STA_RX_E */
+/* Fields in TEGRA20_SPDIF_CH_STA_RX_F */
+
+/*
+ * The 6-word receive channel data page buffer holds a block (192 frames) of
+ * channel status information. The order of receive is from LSB to MSB
+ * bit, and from CH_STA_RX_A to CH_STA_RX_F then back to CH_STA_RX_A.
+ */
+
+/* Fields in TEGRA20_SPDIF_CH_STA_TX_A */
+/* Fields in TEGRA20_SPDIF_CH_STA_TX_B */
+/* Fields in TEGRA20_SPDIF_CH_STA_TX_C */
+/* Fields in TEGRA20_SPDIF_CH_STA_TX_D */
+/* Fields in TEGRA20_SPDIF_CH_STA_TX_E */
+/* Fields in TEGRA20_SPDIF_CH_STA_TX_F */
+
+/*
+ * The 6-word transmit channel data page buffer holds a block (192 frames) of
+ * channel status information. The order of transmission is from LSB to MSB
+ * bit, and from CH_STA_TX_A to CH_STA_TX_F then back to CH_STA_TX_A.
+ */
+
+/* Fields in TEGRA20_SPDIF_USR_STA_RX_A */
+
+/*
+ * This 4-word deep FIFO receives user FIFO field information. The order of
+ * receive is from LSB to MSB bit.
+ */
+
+/* Fields in TEGRA20_SPDIF_USR_DAT_TX_A */
+
+/*
+ * This 4-word deep FIFO transmits user FIFO field information. The order of
+ * transmission is from LSB to MSB bit.
+ */
+
+struct tegra20_spdif {
+	struct clk *clk_spdif_out;
+	struct tegra_pcm_dma_params capture_dma_data;
+	struct tegra_pcm_dma_params playback_dma_data;
+	struct regmap *regmap;
+	u32 reg_ctrl;
+};
+
+#endif
diff --git a/sound/soc/tegra/tegra30_ahub.c b/sound/soc/tegra/tegra30_ahub.c
new file mode 100644
index 0000000..57cd419
--- /dev/null
+++ b/sound/soc/tegra/tegra30_ahub.c
@@ -0,0 +1,631 @@
+/*
+ * tegra30_ahub.c - Tegra30 AHUB driver
+ *
+ * Copyright (c) 2011,2012, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <mach/clk.h>
+#include <mach/dma.h>
+#include <sound/soc.h>
+#include "tegra30_ahub.h"
+
+#define DRV_NAME "tegra30-ahub"
+
+static struct tegra30_ahub *ahub;
+
+static inline void tegra30_apbif_write(u32 reg, u32 val)
+{
+	regmap_write(ahub->regmap_apbif, reg, val);
+}
+
+static inline u32 tegra30_apbif_read(u32 reg)
+{
+	u32 val;
+	regmap_read(ahub->regmap_apbif, reg, &val);
+	return val;
+}
+
+static inline void tegra30_audio_write(u32 reg, u32 val)
+{
+	regmap_write(ahub->regmap_ahub, reg, val);
+}
+
+static int tegra30_ahub_runtime_suspend(struct device *dev)
+{
+	regcache_cache_only(ahub->regmap_apbif, true);
+	regcache_cache_only(ahub->regmap_ahub, true);
+
+	clk_disable(ahub->clk_apbif);
+	clk_disable(ahub->clk_d_audio);
+
+	return 0;
+}
+
+/*
+ * clk_apbif isn't required for an I2S<->I2S configuration where no PCM data
+ * is read from or sent to memory. However, that's not something the rest of
+ * the driver supports right now, so we'll just treat the two clocks as one
+ * for now.
+ *
+ * These functions should not be a plain ref-count. Instead, each active stream
+ * contributes some requirement to the minimum clock rate, so starting or
+ * stopping streams should dynamically adjust the clock as required.  However,
+ * this is not yet implemented.
+ */
+static int tegra30_ahub_runtime_resume(struct device *dev)
+{
+	int ret;
+
+	ret = clk_enable(ahub->clk_d_audio);
+	if (ret) {
+		dev_err(dev, "clk_enable d_audio failed: %d\n", ret);
+		return ret;
+	}
+	ret = clk_enable(ahub->clk_apbif);
+	if (ret) {
+		dev_err(dev, "clk_enable apbif failed: %d\n", ret);
+		clk_disable(ahub->clk_d_audio);
+		return ret;
+	}
+
+	regcache_cache_only(ahub->regmap_apbif, false);
+	regcache_cache_only(ahub->regmap_ahub, false);
+
+	return 0;
+}
+
+int tegra30_ahub_allocate_rx_fifo(enum tegra30_ahub_rxcif *rxcif,
+				  unsigned long *fiforeg,
+				  unsigned long *reqsel)
+{
+	int channel;
+	u32 reg, val;
+
+	channel = find_first_zero_bit(ahub->rx_usage,
+				      TEGRA30_AHUB_CHANNEL_CTRL_COUNT);
+	if (channel >= TEGRA30_AHUB_CHANNEL_CTRL_COUNT)
+		return -EBUSY;
+
+	__set_bit(channel, ahub->rx_usage);
+
+	*rxcif = TEGRA30_AHUB_RXCIF_APBIF_RX0 + channel;
+	*fiforeg = ahub->apbif_addr + TEGRA30_AHUB_CHANNEL_RXFIFO +
+		   (channel * TEGRA30_AHUB_CHANNEL_RXFIFO_STRIDE);
+	*reqsel = ahub->dma_sel + channel;
+
+	reg = TEGRA30_AHUB_CHANNEL_CTRL +
+	      (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE);
+	val = tegra30_apbif_read(reg);
+	val &= ~(TEGRA30_AHUB_CHANNEL_CTRL_RX_THRESHOLD_MASK |
+		 TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_MASK);
+	val |= (7 << TEGRA30_AHUB_CHANNEL_CTRL_RX_THRESHOLD_SHIFT) |
+	       TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_EN |
+	       TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_16;
+	tegra30_apbif_write(reg, val);
+
+	reg = TEGRA30_AHUB_CIF_RX_CTRL +
+	      (channel * TEGRA30_AHUB_CIF_RX_CTRL_STRIDE);
+	val = (0 << TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT) |
+	      (1 << TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT) |
+	      (1 << TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT) |
+	      TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_16 |
+	      TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_16 |
+	      TEGRA30_AUDIOCIF_CTRL_DIRECTION_RX;
+	tegra30_apbif_write(reg, val);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(tegra30_ahub_allocate_rx_fifo);
+
+int tegra30_ahub_enable_rx_fifo(enum tegra30_ahub_rxcif rxcif)
+{
+	int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0;
+	int reg, val;
+
+	reg = TEGRA30_AHUB_CHANNEL_CTRL +
+	      (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE);
+	val = tegra30_apbif_read(reg);
+	val |= TEGRA30_AHUB_CHANNEL_CTRL_RX_EN;
+	tegra30_apbif_write(reg, val);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(tegra30_ahub_enable_rx_fifo);
+
+int tegra30_ahub_disable_rx_fifo(enum tegra30_ahub_rxcif rxcif)
+{
+	int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0;
+	int reg, val;
+
+	reg = TEGRA30_AHUB_CHANNEL_CTRL +
+	      (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE);
+	val = tegra30_apbif_read(reg);
+	val &= ~TEGRA30_AHUB_CHANNEL_CTRL_RX_EN;
+	tegra30_apbif_write(reg, val);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(tegra30_ahub_disable_rx_fifo);
+
+int tegra30_ahub_free_rx_fifo(enum tegra30_ahub_rxcif rxcif)
+{
+	int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0;
+
+	__clear_bit(channel, ahub->rx_usage);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(tegra30_ahub_free_rx_fifo);
+
+int tegra30_ahub_allocate_tx_fifo(enum tegra30_ahub_txcif *txcif,
+				  unsigned long *fiforeg,
+				  unsigned long *reqsel)
+{
+	int channel;
+	u32 reg, val;
+
+	channel = find_first_zero_bit(ahub->tx_usage,
+				      TEGRA30_AHUB_CHANNEL_CTRL_COUNT);
+	if (channel >= TEGRA30_AHUB_CHANNEL_CTRL_COUNT)
+		return -EBUSY;
+
+	__set_bit(channel, ahub->tx_usage);
+
+	*txcif = TEGRA30_AHUB_TXCIF_APBIF_TX0 + channel;
+	*fiforeg = ahub->apbif_addr + TEGRA30_AHUB_CHANNEL_TXFIFO +
+		   (channel * TEGRA30_AHUB_CHANNEL_TXFIFO_STRIDE);
+	*reqsel = ahub->dma_sel + channel;
+
+	reg = TEGRA30_AHUB_CHANNEL_CTRL +
+	      (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE);
+	val = tegra30_apbif_read(reg);
+	val &= ~(TEGRA30_AHUB_CHANNEL_CTRL_TX_THRESHOLD_MASK |
+		 TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_MASK);
+	val |= (7 << TEGRA30_AHUB_CHANNEL_CTRL_TX_THRESHOLD_SHIFT) |
+	       TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_EN |
+	       TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_16;
+	tegra30_apbif_write(reg, val);
+
+	reg = TEGRA30_AHUB_CIF_TX_CTRL +
+	      (channel * TEGRA30_AHUB_CIF_TX_CTRL_STRIDE);
+	val = (0 << TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT) |
+	      (1 << TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT) |
+	      (1 << TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT) |
+	      TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_16 |
+	      TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_16 |
+	      TEGRA30_AUDIOCIF_CTRL_DIRECTION_TX;
+	tegra30_apbif_write(reg, val);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(tegra30_ahub_allocate_tx_fifo);
+
+int tegra30_ahub_enable_tx_fifo(enum tegra30_ahub_txcif txcif)
+{
+	int channel = txcif - TEGRA30_AHUB_TXCIF_APBIF_TX0;
+	int reg, val;
+
+	reg = TEGRA30_AHUB_CHANNEL_CTRL +
+	      (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE);
+	val = tegra30_apbif_read(reg);
+	val |= TEGRA30_AHUB_CHANNEL_CTRL_TX_EN;
+	tegra30_apbif_write(reg, val);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(tegra30_ahub_enable_tx_fifo);
+
+int tegra30_ahub_disable_tx_fifo(enum tegra30_ahub_txcif txcif)
+{
+	int channel = txcif - TEGRA30_AHUB_TXCIF_APBIF_TX0;
+	int reg, val;
+
+	reg = TEGRA30_AHUB_CHANNEL_CTRL +
+	      (channel * TEGRA30_AHUB_CHANNEL_CTRL_STRIDE);
+	val = tegra30_apbif_read(reg);
+	val &= ~TEGRA30_AHUB_CHANNEL_CTRL_TX_EN;
+	tegra30_apbif_write(reg, val);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(tegra30_ahub_disable_tx_fifo);
+
+int tegra30_ahub_free_tx_fifo(enum tegra30_ahub_txcif txcif)
+{
+	int channel = txcif - TEGRA30_AHUB_TXCIF_APBIF_TX0;
+
+	__clear_bit(channel, ahub->tx_usage);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(tegra30_ahub_free_tx_fifo);
+
+int tegra30_ahub_set_rx_cif_source(enum tegra30_ahub_rxcif rxcif,
+				   enum tegra30_ahub_txcif txcif)
+{
+	int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0;
+	int reg;
+
+	reg = TEGRA30_AHUB_AUDIO_RX +
+	      (channel * TEGRA30_AHUB_AUDIO_RX_STRIDE);
+	tegra30_audio_write(reg, 1 << txcif);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(tegra30_ahub_set_rx_cif_source);
+
+int tegra30_ahub_unset_rx_cif_source(enum tegra30_ahub_rxcif rxcif)
+{
+	int channel = rxcif - TEGRA30_AHUB_RXCIF_APBIF_RX0;
+	int reg;
+
+	reg = TEGRA30_AHUB_AUDIO_RX +
+	      (channel * TEGRA30_AHUB_AUDIO_RX_STRIDE);
+	tegra30_audio_write(reg, 0);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(tegra30_ahub_unset_rx_cif_source);
+
+static const char * const configlink_clocks[] __devinitconst = {
+	"i2s0",
+	"i2s1",
+	"i2s2",
+	"i2s3",
+	"i2s4",
+	"dam0",
+	"dam1",
+	"dam2",
+	"spdif_in",
+};
+
+struct of_dev_auxdata ahub_auxdata[] __devinitdata = {
+	OF_DEV_AUXDATA("nvidia,tegra30-i2s", 0x70080300, "tegra30-i2s.0", NULL),
+	OF_DEV_AUXDATA("nvidia,tegra30-i2s", 0x70080400, "tegra30-i2s.1", NULL),
+	OF_DEV_AUXDATA("nvidia,tegra30-i2s", 0x70080500, "tegra30-i2s.2", NULL),
+	OF_DEV_AUXDATA("nvidia,tegra30-i2s", 0x70080600, "tegra30-i2s.3", NULL),
+	OF_DEV_AUXDATA("nvidia,tegra30-i2s", 0x70080700, "tegra30-i2s.4", NULL),
+	{}
+};
+
+#define LAST_REG(name) \
+	(TEGRA30_AHUB_##name + \
+	 (TEGRA30_AHUB_##name##_STRIDE * TEGRA30_AHUB_##name##_COUNT) - 4)
+
+#define REG_IN_ARRAY(reg, name) \
+	((reg >= TEGRA30_AHUB_##name) && \
+	 (reg <= LAST_REG(name) && \
+	 (!((reg - TEGRA30_AHUB_##name) % TEGRA30_AHUB_##name##_STRIDE))))
+
+static bool tegra30_ahub_apbif_wr_rd_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case TEGRA30_AHUB_CONFIG_LINK_CTRL:
+	case TEGRA30_AHUB_MISC_CTRL:
+	case TEGRA30_AHUB_APBDMA_LIVE_STATUS:
+	case TEGRA30_AHUB_I2S_LIVE_STATUS:
+	case TEGRA30_AHUB_SPDIF_LIVE_STATUS:
+	case TEGRA30_AHUB_I2S_INT_MASK:
+	case TEGRA30_AHUB_DAM_INT_MASK:
+	case TEGRA30_AHUB_SPDIF_INT_MASK:
+	case TEGRA30_AHUB_APBIF_INT_MASK:
+	case TEGRA30_AHUB_I2S_INT_STATUS:
+	case TEGRA30_AHUB_DAM_INT_STATUS:
+	case TEGRA30_AHUB_SPDIF_INT_STATUS:
+	case TEGRA30_AHUB_APBIF_INT_STATUS:
+	case TEGRA30_AHUB_I2S_INT_SOURCE:
+	case TEGRA30_AHUB_DAM_INT_SOURCE:
+	case TEGRA30_AHUB_SPDIF_INT_SOURCE:
+	case TEGRA30_AHUB_APBIF_INT_SOURCE:
+	case TEGRA30_AHUB_I2S_INT_SET:
+	case TEGRA30_AHUB_DAM_INT_SET:
+	case TEGRA30_AHUB_SPDIF_INT_SET:
+	case TEGRA30_AHUB_APBIF_INT_SET:
+		return true;
+	default:
+		break;
+	};
+
+	if (REG_IN_ARRAY(reg, CHANNEL_CTRL) ||
+	    REG_IN_ARRAY(reg, CHANNEL_CLEAR) ||
+	    REG_IN_ARRAY(reg, CHANNEL_STATUS) ||
+	    REG_IN_ARRAY(reg, CHANNEL_TXFIFO) ||
+	    REG_IN_ARRAY(reg, CHANNEL_RXFIFO) ||
+	    REG_IN_ARRAY(reg, CIF_TX_CTRL) ||
+	    REG_IN_ARRAY(reg, CIF_RX_CTRL) ||
+	    REG_IN_ARRAY(reg, DAM_LIVE_STATUS))
+		return true;
+
+	return false;
+}
+
+static bool tegra30_ahub_apbif_volatile_reg(struct device *dev,
+					    unsigned int reg)
+{
+	switch (reg) {
+	case TEGRA30_AHUB_CONFIG_LINK_CTRL:
+	case TEGRA30_AHUB_MISC_CTRL:
+	case TEGRA30_AHUB_APBDMA_LIVE_STATUS:
+	case TEGRA30_AHUB_I2S_LIVE_STATUS:
+	case TEGRA30_AHUB_SPDIF_LIVE_STATUS:
+	case TEGRA30_AHUB_I2S_INT_STATUS:
+	case TEGRA30_AHUB_DAM_INT_STATUS:
+	case TEGRA30_AHUB_SPDIF_INT_STATUS:
+	case TEGRA30_AHUB_APBIF_INT_STATUS:
+	case TEGRA30_AHUB_I2S_INT_SET:
+	case TEGRA30_AHUB_DAM_INT_SET:
+	case TEGRA30_AHUB_SPDIF_INT_SET:
+	case TEGRA30_AHUB_APBIF_INT_SET:
+		return true;
+	default:
+		break;
+	};
+
+	if (REG_IN_ARRAY(reg, CHANNEL_CLEAR) ||
+	    REG_IN_ARRAY(reg, CHANNEL_STATUS) ||
+	    REG_IN_ARRAY(reg, CHANNEL_TXFIFO) ||
+	    REG_IN_ARRAY(reg, CHANNEL_RXFIFO) ||
+	    REG_IN_ARRAY(reg, DAM_LIVE_STATUS))
+		return true;
+
+	return false;
+}
+
+static bool tegra30_ahub_apbif_precious_reg(struct device *dev,
+					    unsigned int reg)
+{
+	if (REG_IN_ARRAY(reg, CHANNEL_TXFIFO) ||
+	    REG_IN_ARRAY(reg, CHANNEL_RXFIFO))
+		return true;
+
+	return false;
+}
+
+static const struct regmap_config tegra30_ahub_apbif_regmap_config = {
+	.name = "apbif",
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+	.max_register = TEGRA30_AHUB_APBIF_INT_SET,
+	.writeable_reg = tegra30_ahub_apbif_wr_rd_reg,
+	.readable_reg = tegra30_ahub_apbif_wr_rd_reg,
+	.volatile_reg = tegra30_ahub_apbif_volatile_reg,
+	.precious_reg = tegra30_ahub_apbif_precious_reg,
+	.cache_type = REGCACHE_RBTREE,
+};
+
+static bool tegra30_ahub_ahub_wr_rd_reg(struct device *dev, unsigned int reg)
+{
+	if (REG_IN_ARRAY(reg, AUDIO_RX))
+		return true;
+
+	return false;
+}
+
+static const struct regmap_config tegra30_ahub_ahub_regmap_config = {
+	.name = "ahub",
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+	.max_register = LAST_REG(AUDIO_RX),
+	.writeable_reg = tegra30_ahub_ahub_wr_rd_reg,
+	.readable_reg = tegra30_ahub_ahub_wr_rd_reg,
+	.cache_type = REGCACHE_RBTREE,
+};
+
+static int __devinit tegra30_ahub_probe(struct platform_device *pdev)
+{
+	struct clk *clk;
+	int i;
+	struct resource *res0, *res1, *region;
+	u32 of_dma[2];
+	void __iomem *regs_apbif, *regs_ahub;
+	int ret = 0;
+
+	if (ahub)
+		return -ENODEV;
+
+	/*
+	 * The AHUB hosts a register bus: the "configlink". For this to
+	 * operate correctly, all devices on this bus must be out of reset.
+	 * Ensure that here.
+	 */
+	for (i = 0; i < ARRAY_SIZE(configlink_clocks); i++) {
+		clk = clk_get_sys(NULL, configlink_clocks[i]);
+		if (IS_ERR(clk)) {
+			dev_err(&pdev->dev, "Can't get clock %s\n",
+				configlink_clocks[i]);
+			ret = PTR_ERR(clk);
+			goto err;
+		}
+		tegra_periph_reset_deassert(clk);
+		clk_put(clk);
+	}
+
+	ahub = devm_kzalloc(&pdev->dev, sizeof(struct tegra30_ahub),
+			    GFP_KERNEL);
+	if (!ahub) {
+		dev_err(&pdev->dev, "Can't allocate tegra30_ahub\n");
+		ret = -ENOMEM;
+		goto err;
+	}
+	dev_set_drvdata(&pdev->dev, ahub);
+
+	ahub->dev = &pdev->dev;
+
+	ahub->clk_d_audio = clk_get(&pdev->dev, "d_audio");
+	if (IS_ERR(ahub->clk_d_audio)) {
+		dev_err(&pdev->dev, "Can't retrieve ahub d_audio clock\n");
+		ret = PTR_ERR(ahub->clk_d_audio);
+		goto err;
+	}
+
+	ahub->clk_apbif = clk_get(&pdev->dev, "apbif");
+	if (IS_ERR(ahub->clk_apbif)) {
+		dev_err(&pdev->dev, "Can't retrieve ahub apbif clock\n");
+		ret = PTR_ERR(ahub->clk_apbif);
+		goto err_clk_put_d_audio;
+	}
+
+	if (of_property_read_u32_array(pdev->dev.of_node,
+				"nvidia,dma-request-selector",
+				of_dma, 2) < 0) {
+		dev_err(&pdev->dev,
+			"Missing property nvidia,dma-request-selector\n");
+		ret = -ENODEV;
+		goto err_clk_put_d_audio;
+	}
+	ahub->dma_sel = of_dma[1];
+
+	res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res0) {
+		dev_err(&pdev->dev, "No apbif memory resource\n");
+		ret = -ENODEV;
+		goto err_clk_put_apbif;
+	}
+
+	region = devm_request_mem_region(&pdev->dev, res0->start,
+					 resource_size(res0), DRV_NAME);
+	if (!region) {
+		dev_err(&pdev->dev, "request region apbif failed\n");
+		ret = -EBUSY;
+		goto err_clk_put_apbif;
+	}
+	ahub->apbif_addr = res0->start;
+
+	regs_apbif = devm_ioremap(&pdev->dev, res0->start,
+				  resource_size(res0));
+	if (!regs_apbif) {
+		dev_err(&pdev->dev, "ioremap apbif failed\n");
+		ret = -ENOMEM;
+		goto err_clk_put_apbif;
+	}
+
+	ahub->regmap_apbif = devm_regmap_init_mmio(&pdev->dev, regs_apbif,
+					&tegra30_ahub_apbif_regmap_config);
+	if (IS_ERR(ahub->regmap_apbif)) {
+		dev_err(&pdev->dev, "apbif regmap init failed\n");
+		ret = PTR_ERR(ahub->regmap_apbif);
+		goto err_clk_put_apbif;
+	}
+	regcache_cache_only(ahub->regmap_apbif, true);
+
+	res1 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (!res1) {
+		dev_err(&pdev->dev, "No ahub memory resource\n");
+		ret = -ENODEV;
+		goto err_clk_put_apbif;
+	}
+
+	region = devm_request_mem_region(&pdev->dev, res1->start,
+					 resource_size(res1), DRV_NAME);
+	if (!region) {
+		dev_err(&pdev->dev, "request region ahub failed\n");
+		ret = -EBUSY;
+		goto err_clk_put_apbif;
+	}
+
+	regs_ahub = devm_ioremap(&pdev->dev, res1->start,
+				 resource_size(res1));
+	if (!regs_ahub) {
+		dev_err(&pdev->dev, "ioremap ahub failed\n");
+		ret = -ENOMEM;
+		goto err_clk_put_apbif;
+	}
+
+	ahub->regmap_ahub = devm_regmap_init_mmio(&pdev->dev, regs_ahub,
+					&tegra30_ahub_ahub_regmap_config);
+	if (IS_ERR(ahub->regmap_ahub)) {
+		dev_err(&pdev->dev, "ahub regmap init failed\n");
+		ret = PTR_ERR(ahub->regmap_ahub);
+		goto err_clk_put_apbif;
+	}
+	regcache_cache_only(ahub->regmap_ahub, true);
+
+	pm_runtime_enable(&pdev->dev);
+	if (!pm_runtime_enabled(&pdev->dev)) {
+		ret = tegra30_ahub_runtime_resume(&pdev->dev);
+		if (ret)
+			goto err_pm_disable;
+	}
+
+	of_platform_populate(pdev->dev.of_node, NULL, ahub_auxdata,
+			     &pdev->dev);
+
+	return 0;
+
+err_pm_disable:
+	pm_runtime_disable(&pdev->dev);
+err_clk_put_apbif:
+	clk_put(ahub->clk_apbif);
+err_clk_put_d_audio:
+	clk_put(ahub->clk_d_audio);
+	ahub = 0;
+err:
+	return ret;
+}
+
+static int __devexit tegra30_ahub_remove(struct platform_device *pdev)
+{
+	if (!ahub)
+		return -ENODEV;
+
+	pm_runtime_disable(&pdev->dev);
+	if (!pm_runtime_status_suspended(&pdev->dev))
+		tegra30_ahub_runtime_suspend(&pdev->dev);
+
+	clk_put(ahub->clk_apbif);
+	clk_put(ahub->clk_d_audio);
+
+	ahub = 0;
+
+	return 0;
+}
+
+static const struct of_device_id tegra30_ahub_of_match[] __devinitconst = {
+	{ .compatible = "nvidia,tegra30-ahub", },
+	{},
+};
+
+static const struct dev_pm_ops tegra30_ahub_pm_ops __devinitconst = {
+	SET_RUNTIME_PM_OPS(tegra30_ahub_runtime_suspend,
+			   tegra30_ahub_runtime_resume, NULL)
+};
+
+static struct platform_driver tegra30_ahub_driver = {
+	.probe = tegra30_ahub_probe,
+	.remove = __devexit_p(tegra30_ahub_remove),
+	.driver = {
+		.name = DRV_NAME,
+		.owner = THIS_MODULE,
+		.of_match_table = tegra30_ahub_of_match,
+		.pm = &tegra30_ahub_pm_ops,
+	},
+};
+module_platform_driver(tegra30_ahub_driver);
+
+MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
+MODULE_DESCRIPTION("Tegra30 AHUB driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/tegra/tegra30_ahub.h b/sound/soc/tegra/tegra30_ahub.h
new file mode 100644
index 0000000..e690e2e
--- /dev/null
+++ b/sound/soc/tegra/tegra30_ahub.h
@@ -0,0 +1,483 @@
+/*
+ * tegra30_ahub.h - Definitions for Tegra30 AHUB driver
+ *
+ * Copyright (c) 2011,2012, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __TEGRA30_AHUB_H__
+#define __TEGRA30_AHUB_H__
+
+/* Fields in *_CIF_RX/TX_CTRL; used by AHUB FIFOs, and all other audio modules */
+
+#define TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT	28
+#define TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_MASK_US	0xf
+#define TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_MASK	(TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_MASK_US << TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT)
+
+/* Channel count minus 1 */
+#define TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT	24
+#define TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_MASK_US	7
+#define TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_MASK	(TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_MASK_US << TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT)
+
+/* Channel count minus 1 */
+#define TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT	16
+#define TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_MASK_US	7
+#define TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_MASK	(TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_MASK_US << TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT)
+
+#define TEGRA30_AUDIOCIF_BITS_4				0
+#define TEGRA30_AUDIOCIF_BITS_8				1
+#define TEGRA30_AUDIOCIF_BITS_12			2
+#define TEGRA30_AUDIOCIF_BITS_16			3
+#define TEGRA30_AUDIOCIF_BITS_20			4
+#define TEGRA30_AUDIOCIF_BITS_24			5
+#define TEGRA30_AUDIOCIF_BITS_28			6
+#define TEGRA30_AUDIOCIF_BITS_32			7
+
+#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT		12
+#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_MASK		(7                        << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_4		(TEGRA30_AUDIOCIF_BITS_4  << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_8		(TEGRA30_AUDIOCIF_BITS_8  << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_12		(TEGRA30_AUDIOCIF_BITS_12 << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_16		(TEGRA30_AUDIOCIF_BITS_16 << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_20		(TEGRA30_AUDIOCIF_BITS_20 << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_24		(TEGRA30_AUDIOCIF_BITS_24 << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_28		(TEGRA30_AUDIOCIF_BITS_28 << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_32		(TEGRA30_AUDIOCIF_BITS_32 << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT)
+
+#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT		8
+#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_MASK		(7                        << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_4		(TEGRA30_AUDIOCIF_BITS_4  << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_8		(TEGRA30_AUDIOCIF_BITS_8  << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_12		(TEGRA30_AUDIOCIF_BITS_12 << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_16		(TEGRA30_AUDIOCIF_BITS_16 << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_20		(TEGRA30_AUDIOCIF_BITS_20 << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_24		(TEGRA30_AUDIOCIF_BITS_24 << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_28		(TEGRA30_AUDIOCIF_BITS_28 << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_32		(TEGRA30_AUDIOCIF_BITS_32 << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT)
+
+#define TEGRA30_AUDIOCIF_EXPAND_ZERO			0
+#define TEGRA30_AUDIOCIF_EXPAND_ONE			1
+#define TEGRA30_AUDIOCIF_EXPAND_LFSR			2
+
+#define TEGRA30_AUDIOCIF_CTRL_EXPAND_SHIFT		6
+#define TEGRA30_AUDIOCIF_CTRL_EXPAND_MASK		(3                            << TEGRA30_AUDIOCIF_CTRL_EXPAND_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_EXPAND_ZERO		(TEGRA30_AUDIOCIF_EXPAND_ZERO << TEGRA30_AUDIOCIF_CTRL_EXPAND_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_EXPAND_ONE		(TEGRA30_AUDIOCIF_EXPAND_ONE  << TEGRA30_AUDIOCIF_CTRL_EXPAND_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_EXPAND_LFSR		(TEGRA30_AUDIOCIF_EXPAND_LFSR << TEGRA30_AUDIOCIF_CTRL_EXPAND_SHIFT)
+
+#define TEGRA30_AUDIOCIF_STEREO_CONV_CH0		0
+#define TEGRA30_AUDIOCIF_STEREO_CONV_CH1		1
+#define TEGRA30_AUDIOCIF_STEREO_CONV_AVG		2
+
+#define TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT		4
+#define TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_MASK		(3                                << TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_CH0		(TEGRA30_AUDIOCIF_STEREO_CONV_CH0 << TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_CH1		(TEGRA30_AUDIOCIF_STEREO_CONV_CH1 << TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_AVG		(TEGRA30_AUDIOCIF_STEREO_CONV_AVG << TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT)
+
+#define TEGRA30_AUDIOCIF_CTRL_REPLICATE			3
+
+#define TEGRA30_AUDIOCIF_DIRECTION_TX			0
+#define TEGRA30_AUDIOCIF_DIRECTION_RX			1
+
+#define TEGRA30_AUDIOCIF_CTRL_DIRECTION_SHIFT		2
+#define TEGRA30_AUDIOCIF_CTRL_DIRECTION_MASK		(1                             << TEGRA30_AUDIOCIF_CTRL_DIRECTION_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_DIRECTION_TX		(TEGRA30_AUDIOCIF_DIRECTION_TX << TEGRA30_AUDIOCIF_CTRL_DIRECTION_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_DIRECTION_RX		(TEGRA30_AUDIOCIF_DIRECTION_RX << TEGRA30_AUDIOCIF_CTRL_DIRECTION_SHIFT)
+
+#define TEGRA30_AUDIOCIF_TRUNCATE_ROUND			0
+#define TEGRA30_AUDIOCIF_TRUNCATE_CHOP			1
+
+#define TEGRA30_AUDIOCIF_CTRL_TRUNCATE_SHIFT		1
+#define TEGRA30_AUDIOCIF_CTRL_TRUNCATE_MASK		(1                               << TEGRA30_AUDIOCIF_CTRL_TRUNCATE_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_TRUNCATE_ROUND		(TEGRA30_AUDIOCIF_TRUNCATE_ROUND << TEGRA30_AUDIOCIF_CTRL_TRUNCATE_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_TRUNCATE_CHOP		(TEGRA30_AUDIOCIF_TRUNCATE_CHOP  << TEGRA30_AUDIOCIF_CTRL_TRUNCATE_SHIFT)
+
+#define TEGRA30_AUDIOCIF_MONO_CONV_ZERO			0
+#define TEGRA30_AUDIOCIF_MONO_CONV_COPY			1
+
+#define TEGRA30_AUDIOCIF_CTRL_MONO_CONV_SHIFT		0
+#define TEGRA30_AUDIOCIF_CTRL_MONO_CONV_MASK		(1                               << TEGRA30_AUDIOCIF_CTRL_MONO_CONV_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_MONO_CONV_ZERO		(TEGRA30_AUDIOCIF_MONO_CONV_ZERO << TEGRA30_AUDIOCIF_CTRL_MONO_CONV_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_MONO_CONV_COPY		(TEGRA30_AUDIOCIF_MONO_CONV_COPY << TEGRA30_AUDIOCIF_CTRL_MONO_CONV_SHIFT)
+
+/* Registers within TEGRA30_AUDIO_CLUSTER_BASE */
+
+/* TEGRA30_AHUB_CHANNEL_CTRL */
+
+#define TEGRA30_AHUB_CHANNEL_CTRL			0x0
+#define TEGRA30_AHUB_CHANNEL_CTRL_STRIDE		0x20
+#define TEGRA30_AHUB_CHANNEL_CTRL_COUNT			4
+#define TEGRA30_AHUB_CHANNEL_CTRL_TX_EN			(1 << 31)
+#define TEGRA30_AHUB_CHANNEL_CTRL_RX_EN			(1 << 30)
+#define TEGRA30_AHUB_CHANNEL_CTRL_LOOPBACK		(1 << 29)
+
+#define TEGRA30_AHUB_CHANNEL_CTRL_TX_THRESHOLD_SHIFT	16
+#define TEGRA30_AHUB_CHANNEL_CTRL_TX_THRESHOLD_MASK_US	0xff
+#define TEGRA30_AHUB_CHANNEL_CTRL_TX_THRESHOLD_MASK	(TEGRA30_AHUB_CHANNEL_CTRL_TX_THRESHOLD_MASK_US << TEGRA30_AHUB_CHANNEL_CTRL_TX_THRESHOLD_SHIFT)
+
+#define TEGRA30_AHUB_CHANNEL_CTRL_RX_THRESHOLD_SHIFT	8
+#define TEGRA30_AHUB_CHANNEL_CTRL_RX_THRESHOLD_MASK_US	0xff
+#define TEGRA30_AHUB_CHANNEL_CTRL_RX_THRESHOLD_MASK	(TEGRA30_AHUB_CHANNEL_CTRL_RX_THRESHOLD_MASK_US << TEGRA30_AHUB_CHANNEL_CTRL_RX_THRESHOLD_SHIFT)
+
+#define TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_EN		(1 << 6)
+
+#define TEGRA30_PACK_8_4				2
+#define TEGRA30_PACK_16					3
+
+#define TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_SHIFT		4
+#define TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_MASK_US	3
+#define TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_MASK		(TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_MASK_US << TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_SHIFT)
+#define TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_8_4		(TEGRA30_PACK_8_4                          << TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_SHIFT)
+#define TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_16		(TEGRA30_PACK_16                           << TEGRA30_AHUB_CHANNEL_CTRL_TX_PACK_SHIFT)
+
+#define TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_EN		(1 << 2)
+
+#define TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_SHIFT		0
+#define TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_MASK_US	3
+#define TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_MASK		(TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_MASK_US << TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_SHIFT)
+#define TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_8_4		(TEGRA30_PACK_8_4                          << TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_SHIFT)
+#define TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_16		(TEGRA30_PACK_16                           << TEGRA30_AHUB_CHANNEL_CTRL_RX_PACK_SHIFT)
+
+/* TEGRA30_AHUB_CHANNEL_CLEAR */
+
+#define TEGRA30_AHUB_CHANNEL_CLEAR			0x4
+#define TEGRA30_AHUB_CHANNEL_CLEAR_STRIDE		0x20
+#define TEGRA30_AHUB_CHANNEL_CLEAR_COUNT		4
+#define TEGRA30_AHUB_CHANNEL_CLEAR_TX_SOFT_RESET	(1 << 31)
+#define TEGRA30_AHUB_CHANNEL_CLEAR_RX_SOFT_RESET	(1 << 30)
+
+/* TEGRA30_AHUB_CHANNEL_STATUS */
+
+#define TEGRA30_AHUB_CHANNEL_STATUS			0x8
+#define TEGRA30_AHUB_CHANNEL_STATUS_STRIDE		0x20
+#define TEGRA30_AHUB_CHANNEL_STATUS_COUNT		4
+#define TEGRA30_AHUB_CHANNEL_STATUS_TX_FREE_SHIFT	24
+#define TEGRA30_AHUB_CHANNEL_STATUS_TX_FREE_MASK_US	0xff
+#define TEGRA30_AHUB_CHANNEL_STATUS_TX_FREE_MASK	(TEGRA30_AHUB_CHANNEL_STATUS_TX_FREE_MASK_US << TEGRA30_AHUB_CHANNEL_STATUS_TX_FREE_SHIFT)
+#define TEGRA30_AHUB_CHANNEL_STATUS_RX_FREE_SHIFT	16
+#define TEGRA30_AHUB_CHANNEL_STATUS_RX_FREE_MASK_US	0xff
+#define TEGRA30_AHUB_CHANNEL_STATUS_RX_FREE_MASK	(TEGRA30_AHUB_CHANNEL_STATUS_RX_FREE_MASK_US << TEGRA30_AHUB_CHANNEL_STATUS_RX_FREE_SHIFT)
+#define TEGRA30_AHUB_CHANNEL_STATUS_TX_TRIG		(1 << 1)
+#define TEGRA30_AHUB_CHANNEL_STATUS_RX_TRIG		(1 << 0)
+
+/* TEGRA30_AHUB_CHANNEL_TXFIFO */
+
+#define TEGRA30_AHUB_CHANNEL_TXFIFO			0xc
+#define TEGRA30_AHUB_CHANNEL_TXFIFO_STRIDE		0x20
+#define TEGRA30_AHUB_CHANNEL_TXFIFO_COUNT		4
+
+/* TEGRA30_AHUB_CHANNEL_RXFIFO */
+
+#define TEGRA30_AHUB_CHANNEL_RXFIFO			0x10
+#define TEGRA30_AHUB_CHANNEL_RXFIFO_STRIDE		0x20
+#define TEGRA30_AHUB_CHANNEL_RXFIFO_COUNT		4
+
+/* TEGRA30_AHUB_CIF_TX_CTRL */
+
+#define TEGRA30_AHUB_CIF_TX_CTRL			0x14
+#define TEGRA30_AHUB_CIF_TX_CTRL_STRIDE			0x20
+#define TEGRA30_AHUB_CIF_TX_CTRL_COUNT			4
+/* Uses field from TEGRA30_AUDIOCIF_CTRL_* */
+
+/* TEGRA30_AHUB_CIF_RX_CTRL */
+
+#define TEGRA30_AHUB_CIF_RX_CTRL			0x18
+#define TEGRA30_AHUB_CIF_RX_CTRL_STRIDE			0x20
+#define TEGRA30_AHUB_CIF_RX_CTRL_COUNT			4
+/* Uses field from TEGRA30_AUDIOCIF_CTRL_* */
+
+/* TEGRA30_AHUB_CONFIG_LINK_CTRL */
+
+#define TEGRA30_AHUB_CONFIG_LINK_CTRL					0x80
+#define TEGRA30_AHUB_CONFIG_LINK_CTRL_MASTER_FIFO_FULL_CNT_SHIFT	28
+#define TEGRA30_AHUB_CONFIG_LINK_CTRL_MASTER_FIFO_FULL_CNT_MASK_US	0xf
+#define TEGRA30_AHUB_CONFIG_LINK_CTRL_MASTER_FIFO_FULL_CNT_MASK		(TEGRA30_AHUB_CONFIG_LINK_CTRL_MASTER_FIFO_FULL_CNT_MASK_US << TEGRA30_AHUB_CONFIG_LINK_CTRL_MASTER_FIFO_FULL_CNT_SHIFT)
+#define TEGRA30_AHUB_CONFIG_LINK_CTRL_TIMEOUT_CNT_SHIFT			16
+#define TEGRA30_AHUB_CONFIG_LINK_CTRL_TIMEOUT_CNT_MASK_US		0xfff
+#define TEGRA30_AHUB_CONFIG_LINK_CTRL_TIMEOUT_CNT_MASK			(TEGRA30_AHUB_CONFIG_LINK_CTRL_TIMEOUT_CNT_MASK_US << TEGRA30_AHUB_CONFIG_LINK_CTRL_TIMEOUT_CNT_SHIFT)
+#define TEGRA30_AHUB_CONFIG_LINK_CTRL_IDLE_CNT_SHIFT			4
+#define TEGRA30_AHUB_CONFIG_LINK_CTRL_IDLE_CNT_MASK_US			0xfff
+#define TEGRA30_AHUB_CONFIG_LINK_CTRL_IDLE_CNT_MASK			(TEGRA30_AHUB_CONFIG_LINK_CTRL_IDLE_CNT_MASK_US << TEGRA30_AHUB_CONFIG_LINK_CTRL_IDLE_CNT_SHIFT)
+#define TEGRA30_AHUB_CONFIG_LINK_CTRL_CG_EN				(1 << 2)
+#define TEGRA30_AHUB_CONFIG_LINK_CTRL_CLEAR_TIMEOUT_CNTR		(1 << 1)
+#define TEGRA30_AHUB_CONFIG_LINK_CTRL_SOFT_RESET			(1 << 0)
+
+/* TEGRA30_AHUB_MISC_CTRL */
+
+#define TEGRA30_AHUB_MISC_CTRL				0x84
+#define TEGRA30_AHUB_MISC_CTRL_AUDIO_ACTIVE		(1 << 31)
+#define TEGRA30_AHUB_MISC_CTRL_AUDIO_CG_EN		(1 << 8)
+#define TEGRA30_AHUB_MISC_CTRL_AUDIO_OBS_SEL_SHIFT	0
+#define TEGRA30_AHUB_MISC_CTRL_AUDIO_OBS_SEL_MASK	(0x1f << TEGRA30_AHUB_MISC_CTRL_AUDIO_OBS_SEL_SHIFT)
+
+/* TEGRA30_AHUB_APBDMA_LIVE_STATUS */
+
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS				0x88
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH3_RX_CIF_FIFO_FULL	(1 << 31)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH3_TX_CIF_FIFO_FULL	(1 << 30)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH2_RX_CIF_FIFO_FULL	(1 << 29)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH2_TX_CIF_FIFO_FULL	(1 << 28)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH1_RX_CIF_FIFO_FULL	(1 << 27)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH1_TX_CIF_FIFO_FULL	(1 << 26)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH0_RX_CIF_FIFO_FULL	(1 << 25)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH0_TX_CIF_FIFO_FULL	(1 << 24)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH3_RX_CIF_FIFO_EMPTY	(1 << 23)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH3_TX_CIF_FIFO_EMPTY	(1 << 22)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH2_RX_CIF_FIFO_EMPTY	(1 << 21)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH2_TX_CIF_FIFO_EMPTY	(1 << 20)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH1_RX_CIF_FIFO_EMPTY	(1 << 19)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH1_TX_CIF_FIFO_EMPTY	(1 << 18)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH0_RX_CIF_FIFO_EMPTY	(1 << 17)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH0_TX_CIF_FIFO_EMPTY	(1 << 16)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH3_RX_DMA_FIFO_FULL	(1 << 15)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH3_TX_DMA_FIFO_FULL	(1 << 14)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH2_RX_DMA_FIFO_FULL	(1 << 13)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH2_TX_DMA_FIFO_FULL	(1 << 12)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH1_RX_DMA_FIFO_FULL	(1 << 11)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH1_TX_DMA_FIFO_FULL	(1 << 10)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH0_RX_DMA_FIFO_FULL	(1 << 9)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH0_TX_DMA_FIFO_FULL	(1 << 8)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH3_RX_DMA_FIFO_EMPTY	(1 << 7)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH3_TX_DMA_FIFO_EMPTY	(1 << 6)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH2_RX_DMA_FIFO_EMPTY	(1 << 5)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH2_TX_DMA_FIFO_EMPTY	(1 << 4)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH1_RX_DMA_FIFO_EMPTY	(1 << 3)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH1_TX_DMA_FIFO_EMPTY	(1 << 2)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH0_RX_DMA_FIFO_EMPTY	(1 << 1)
+#define TEGRA30_AHUB_APBDMA_LIVE_STATUS_CH0_TX_DMA_FIFO_EMPTY	(1 << 0)
+
+/* TEGRA30_AHUB_I2S_LIVE_STATUS */
+
+#define TEGRA30_AHUB_I2S_LIVE_STATUS				0x8c
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S4_RX_FIFO_FULL		(1 << 29)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S4_TX_FIFO_FULL		(1 << 28)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S3_RX_FIFO_FULL		(1 << 27)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S3_TX_FIFO_FULL		(1 << 26)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S2_RX_FIFO_FULL		(1 << 25)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S2_TX_FIFO_FULL		(1 << 24)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S1_RX_FIFO_FULL		(1 << 23)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S1_TX_FIFO_FULL		(1 << 22)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S0_RX_FIFO_FULL		(1 << 21)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S0_TX_FIFO_FULL		(1 << 20)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S4_RX_FIFO_ENABLED	(1 << 19)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S4_TX_FIFO_ENABLED	(1 << 18)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S3_RX_FIFO_ENABLED	(1 << 17)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S3_TX_FIFO_ENABLED	(1 << 16)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S2_RX_FIFO_ENABLED	(1 << 15)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S2_TX_FIFO_ENABLED	(1 << 14)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S1_RX_FIFO_ENABLED	(1 << 13)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S1_TX_FIFO_ENABLED	(1 << 12)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S0_RX_FIFO_ENABLED	(1 << 11)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S0_TX_FIFO_ENABLED	(1 << 10)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S4_RX_FIFO_EMPTY		(1 << 9)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S4_TX_FIFO_EMPTY		(1 << 8)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S3_RX_FIFO_EMPTY		(1 << 7)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S3_TX_FIFO_EMPTY		(1 << 6)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S2_RX_FIFO_EMPTY		(1 << 5)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S2_TX_FIFO_EMPTY		(1 << 4)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S1_RX_FIFO_EMPTY		(1 << 3)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S1_TX_FIFO_EMPTY		(1 << 2)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S0_RX_FIFO_EMPTY		(1 << 1)
+#define TEGRA30_AHUB_I2S_LIVE_STATUS_I2S0_TX_FIFO_EMPTY		(1 << 0)
+
+/* TEGRA30_AHUB_DAM0_LIVE_STATUS */
+
+#define TEGRA30_AHUB_DAM_LIVE_STATUS				0x90
+#define TEGRA30_AHUB_DAM_LIVE_STATUS_STRIDE			0x8
+#define TEGRA30_AHUB_DAM_LIVE_STATUS_COUNT			3
+#define TEGRA30_AHUB_DAM_LIVE_STATUS_TX_ENABLED			(1 << 26)
+#define TEGRA30_AHUB_DAM_LIVE_STATUS_RX1_ENABLED		(1 << 25)
+#define TEGRA30_AHUB_DAM_LIVE_STATUS_RX0_ENABLED		(1 << 24)
+#define TEGRA30_AHUB_DAM_LIVE_STATUS_TXFIFO_FULL		(1 << 15)
+#define TEGRA30_AHUB_DAM_LIVE_STATUS_RX1FIFO_FULL		(1 << 9)
+#define TEGRA30_AHUB_DAM_LIVE_STATUS_RX0FIFO_FULL		(1 << 8)
+#define TEGRA30_AHUB_DAM_LIVE_STATUS_TXFIFO_EMPTY		(1 << 7)
+#define TEGRA30_AHUB_DAM_LIVE_STATUS_RX1FIFO_EMPTY		(1 << 1)
+#define TEGRA30_AHUB_DAM_LIVE_STATUS_RX0FIFO_EMPTY		(1 << 0)
+
+/* TEGRA30_AHUB_SPDIF_LIVE_STATUS */
+
+#define TEGRA30_AHUB_SPDIF_LIVE_STATUS				0xa8
+#define TEGRA30_AHUB_SPDIF_LIVE_STATUS_USER_TX_ENABLED		(1 << 11)
+#define TEGRA30_AHUB_SPDIF_LIVE_STATUS_USER_RX_ENABLED		(1 << 10)
+#define TEGRA30_AHUB_SPDIF_LIVE_STATUS_DATA_TX_ENABLED		(1 << 9)
+#define TEGRA30_AHUB_SPDIF_LIVE_STATUS_DATA_RX_ENABLED		(1 << 8)
+#define TEGRA30_AHUB_SPDIF_LIVE_STATUS_USER_TXFIFO_FULL		(1 << 7)
+#define TEGRA30_AHUB_SPDIF_LIVE_STATUS_USER_RXFIFO_FULL		(1 << 6)
+#define TEGRA30_AHUB_SPDIF_LIVE_STATUS_DATA_TXFIFO_FULL		(1 << 5)
+#define TEGRA30_AHUB_SPDIF_LIVE_STATUS_DATA_RXFIFO_FULL		(1 << 4)
+#define TEGRA30_AHUB_SPDIF_LIVE_STATUS_USER_TXFIFO_EMPTY	(1 << 3)
+#define TEGRA30_AHUB_SPDIF_LIVE_STATUS_USER_RXFIFO_EMPTY	(1 << 2)
+#define TEGRA30_AHUB_SPDIF_LIVE_STATUS_DATA_TXFIFO_EMPTY	(1 << 1)
+#define TEGRA30_AHUB_SPDIF_LIVE_STATUS_DATA_RXFIFO_EMPTY	(1 << 0)
+
+/* TEGRA30_AHUB_I2S_INT_MASK */
+
+#define TEGRA30_AHUB_I2S_INT_MASK				0xb0
+
+/* TEGRA30_AHUB_DAM_INT_MASK */
+
+#define TEGRA30_AHUB_DAM_INT_MASK				0xb4
+
+/* TEGRA30_AHUB_SPDIF_INT_MASK */
+
+#define TEGRA30_AHUB_SPDIF_INT_MASK				0xbc
+
+/* TEGRA30_AHUB_APBIF_INT_MASK */
+
+#define TEGRA30_AHUB_APBIF_INT_MASK				0xc0
+
+/* TEGRA30_AHUB_I2S_INT_STATUS */
+
+#define TEGRA30_AHUB_I2S_INT_STATUS				0xc8
+
+/* TEGRA30_AHUB_DAM_INT_STATUS */
+
+#define TEGRA30_AHUB_DAM_INT_STATUS				0xcc
+
+/* TEGRA30_AHUB_SPDIF_INT_STATUS */
+
+#define TEGRA30_AHUB_SPDIF_INT_STATUS				0xd4
+
+/* TEGRA30_AHUB_APBIF_INT_STATUS */
+
+#define TEGRA30_AHUB_APBIF_INT_STATUS				0xd8
+
+/* TEGRA30_AHUB_I2S_INT_SOURCE */
+
+#define TEGRA30_AHUB_I2S_INT_SOURCE				0xe0
+
+/* TEGRA30_AHUB_DAM_INT_SOURCE */
+
+#define TEGRA30_AHUB_DAM_INT_SOURCE				0xe4
+
+/* TEGRA30_AHUB_SPDIF_INT_SOURCE */
+
+#define TEGRA30_AHUB_SPDIF_INT_SOURCE				0xec
+
+/* TEGRA30_AHUB_APBIF_INT_SOURCE */
+
+#define TEGRA30_AHUB_APBIF_INT_SOURCE				0xf0
+
+/* TEGRA30_AHUB_I2S_INT_SET */
+
+#define TEGRA30_AHUB_I2S_INT_SET				0xf8
+
+/* TEGRA30_AHUB_DAM_INT_SET */
+
+#define TEGRA30_AHUB_DAM_INT_SET				0xfc
+
+/* TEGRA30_AHUB_SPDIF_INT_SET */
+
+#define TEGRA30_AHUB_SPDIF_INT_SET				0x100
+
+/* TEGRA30_AHUB_APBIF_INT_SET */
+
+#define TEGRA30_AHUB_APBIF_INT_SET				0x104
+
+/* Registers within TEGRA30_AHUB_BASE */
+
+#define TEGRA30_AHUB_AUDIO_RX					0x0
+#define TEGRA30_AHUB_AUDIO_RX_STRIDE				0x4
+#define TEGRA30_AHUB_AUDIO_RX_COUNT				17
+/* This register repeats once for each entry in enum tegra30_ahub_rxcif */
+/* The fields in this register are 1 bit per entry in tegra30_ahub_txcif */
+
+/*
+ * Terminology:
+ * AHUB: Audio Hub; a cross-bar switch between the audio devices: DMA FIFOs,
+ *       I2S controllers, SPDIF controllers, and DAMs.
+ * XBAR: The core cross-bar component of the AHUB.
+ * CIF:  Client Interface; the HW module connecting an audio device to the
+ *       XBAR.
+ * DAM:  Digital Audio Mixer: A HW module that mixes multiple audio streams,
+ *       possibly including sample-rate conversion.
+ *
+ * Each TX CIF transmits data into the XBAR. Each RX CIF can receive audio
+ * transmitted by a particular TX CIF.
+ *
+ * This driver is currently very simplistic; many HW features are not
+ * exposed; DAMs are not supported, only 16-bit stereo audio is supported,
+ * etc.
+ */
+
+enum tegra30_ahub_txcif {
+	TEGRA30_AHUB_TXCIF_APBIF_TX0,
+	TEGRA30_AHUB_TXCIF_APBIF_TX1,
+	TEGRA30_AHUB_TXCIF_APBIF_TX2,
+	TEGRA30_AHUB_TXCIF_APBIF_TX3,
+	TEGRA30_AHUB_TXCIF_I2S0_TX0,
+	TEGRA30_AHUB_TXCIF_I2S1_TX0,
+	TEGRA30_AHUB_TXCIF_I2S2_TX0,
+	TEGRA30_AHUB_TXCIF_I2S3_TX0,
+	TEGRA30_AHUB_TXCIF_I2S4_TX0,
+	TEGRA30_AHUB_TXCIF_DAM0_TX0,
+	TEGRA30_AHUB_TXCIF_DAM1_TX0,
+	TEGRA30_AHUB_TXCIF_DAM2_TX0,
+	TEGRA30_AHUB_TXCIF_SPDIF_TX0,
+	TEGRA30_AHUB_TXCIF_SPDIF_TX1,
+};
+
+enum tegra30_ahub_rxcif {
+	TEGRA30_AHUB_RXCIF_APBIF_RX0,
+	TEGRA30_AHUB_RXCIF_APBIF_RX1,
+	TEGRA30_AHUB_RXcIF_APBIF_RX2,
+	TEGRA30_AHUB_RXCIF_APBIF_RX3,
+	TEGRA30_AHUB_RXCIF_I2S0_RX0,
+	TEGRA30_AHUB_RXCIF_I2S1_RX0,
+	TEGRA30_AHUB_RXCIF_I2S2_RX0,
+	TEGRA30_AHUB_RXCIF_I2S3_RX0,
+	TEGRA30_AHUB_RXCIF_I2S4_RX0,
+	TEGRA30_AHUB_RXCIF_DAM0_RX0,
+	TEGRA30_AHUB_RXCIF_DAM0_RX1,
+	TEGRA30_AHUB_RXCIF_DAM1_RX0,
+	TEGRA30_AHUB_RXCIF_DAM2_RX1,
+	TEGRA30_AHUB_RXCIF_DAM3_RX0,
+	TEGRA30_AHUB_RXCIF_DAM3_RX1,
+	TEGRA30_AHUB_RXCIF_SPDIF_RX0,
+	TEGRA30_AHUB_RXCIF_SPDIF_RX1,
+};
+
+extern int tegra30_ahub_allocate_rx_fifo(enum tegra30_ahub_rxcif *rxcif,
+					 unsigned long *fiforeg,
+					 unsigned long *reqsel);
+extern int tegra30_ahub_enable_rx_fifo(enum tegra30_ahub_rxcif rxcif);
+extern int tegra30_ahub_disable_rx_fifo(enum tegra30_ahub_rxcif rxcif);
+extern int tegra30_ahub_free_rx_fifo(enum tegra30_ahub_rxcif rxcif);
+
+extern int tegra30_ahub_allocate_tx_fifo(enum tegra30_ahub_txcif *txcif,
+					 unsigned long *fiforeg,
+					 unsigned long *reqsel);
+extern int tegra30_ahub_enable_tx_fifo(enum tegra30_ahub_txcif txcif);
+extern int tegra30_ahub_disable_tx_fifo(enum tegra30_ahub_txcif txcif);
+extern int tegra30_ahub_free_tx_fifo(enum tegra30_ahub_txcif txcif);
+
+extern int tegra30_ahub_set_rx_cif_source(enum tegra30_ahub_rxcif rxcif,
+					  enum tegra30_ahub_txcif txcif);
+extern int tegra30_ahub_unset_rx_cif_source(enum tegra30_ahub_rxcif rxcif);
+
+struct tegra30_ahub {
+	struct device *dev;
+	struct clk *clk_d_audio;
+	struct clk *clk_apbif;
+	int dma_sel;
+	resource_size_t apbif_addr;
+	struct regmap *regmap_apbif;
+	struct regmap *regmap_ahub;
+	DECLARE_BITMAP(rx_usage, TEGRA30_AHUB_CHANNEL_CTRL_COUNT);
+	DECLARE_BITMAP(tx_usage, TEGRA30_AHUB_CHANNEL_CTRL_COUNT);
+};
+
+#endif
diff --git a/sound/soc/tegra/tegra30_i2s.c b/sound/soc/tegra/tegra30_i2s.c
new file mode 100644
index 0000000..8596032
--- /dev/null
+++ b/sound/soc/tegra/tegra30_i2s.c
@@ -0,0 +1,536 @@
+/*
+ * tegra30_i2s.c - Tegra30 I2S driver
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (c) 2010-2012, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * Based on code copyright/by:
+ *
+ * Copyright (c) 2009-2010, NVIDIA Corporation.
+ * Scott Peterson <speterson@nvidia.com>
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Iliyan Malchev <malchev@google.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "tegra30_ahub.h"
+#include "tegra30_i2s.h"
+
+#define DRV_NAME "tegra30-i2s"
+
+static inline void tegra30_i2s_write(struct tegra30_i2s *i2s, u32 reg, u32 val)
+{
+	regmap_write(i2s->regmap, reg, val);
+}
+
+static inline u32 tegra30_i2s_read(struct tegra30_i2s *i2s, u32 reg)
+{
+	u32 val;
+	regmap_read(i2s->regmap, reg, &val);
+	return val;
+}
+
+static int tegra30_i2s_runtime_suspend(struct device *dev)
+{
+	struct tegra30_i2s *i2s = dev_get_drvdata(dev);
+
+	regcache_cache_only(i2s->regmap, true);
+
+	clk_disable(i2s->clk_i2s);
+
+	return 0;
+}
+
+static int tegra30_i2s_runtime_resume(struct device *dev)
+{
+	struct tegra30_i2s *i2s = dev_get_drvdata(dev);
+	int ret;
+
+	ret = clk_enable(i2s->clk_i2s);
+	if (ret) {
+		dev_err(dev, "clk_enable failed: %d\n", ret);
+		return ret;
+	}
+
+	regcache_cache_only(i2s->regmap, false);
+
+	return 0;
+}
+
+int tegra30_i2s_startup(struct snd_pcm_substream *substream,
+			struct snd_soc_dai *dai)
+{
+	struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+	int ret;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		ret = tegra30_ahub_allocate_tx_fifo(&i2s->playback_fifo_cif,
+					&i2s->playback_dma_data.addr,
+					&i2s->playback_dma_data.req_sel);
+		i2s->playback_dma_data.wrap = 4;
+		i2s->playback_dma_data.width = 32;
+		tegra30_ahub_set_rx_cif_source(i2s->playback_i2s_cif,
+					       i2s->playback_fifo_cif);
+	} else {
+		ret = tegra30_ahub_allocate_rx_fifo(&i2s->capture_fifo_cif,
+					&i2s->capture_dma_data.addr,
+					&i2s->capture_dma_data.req_sel);
+		i2s->capture_dma_data.wrap = 4;
+		i2s->capture_dma_data.width = 32;
+		tegra30_ahub_set_rx_cif_source(i2s->capture_fifo_cif,
+					       i2s->capture_i2s_cif);
+	}
+
+	return ret;
+}
+
+void tegra30_i2s_shutdown(struct snd_pcm_substream *substream,
+			struct snd_soc_dai *dai)
+{
+	struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		tegra30_ahub_unset_rx_cif_source(i2s->playback_i2s_cif);
+		tegra30_ahub_free_tx_fifo(i2s->playback_fifo_cif);
+	} else {
+		tegra30_ahub_unset_rx_cif_source(i2s->capture_fifo_cif);
+		tegra30_ahub_free_rx_fifo(i2s->capture_fifo_cif);
+	}
+}
+
+static int tegra30_i2s_set_fmt(struct snd_soc_dai *dai,
+				unsigned int fmt)
+{
+	struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	i2s->reg_ctrl &= ~TEGRA30_I2S_CTRL_MASTER_ENABLE;
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		i2s->reg_ctrl |= TEGRA30_I2S_CTRL_MASTER_ENABLE;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFM:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	i2s->reg_ctrl &= ~(TEGRA30_I2S_CTRL_FRAME_FORMAT_MASK |
+			   TEGRA30_I2S_CTRL_LRCK_MASK);
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_DSP_A:
+		i2s->reg_ctrl |= TEGRA30_I2S_CTRL_FRAME_FORMAT_FSYNC;
+		i2s->reg_ctrl |= TEGRA30_I2S_CTRL_LRCK_L_LOW;
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		i2s->reg_ctrl |= TEGRA30_I2S_CTRL_FRAME_FORMAT_FSYNC;
+		i2s->reg_ctrl |= TEGRA30_I2S_CTRL_LRCK_R_LOW;
+		break;
+	case SND_SOC_DAIFMT_I2S:
+		i2s->reg_ctrl |= TEGRA30_I2S_CTRL_FRAME_FORMAT_LRCK;
+		i2s->reg_ctrl |= TEGRA30_I2S_CTRL_LRCK_L_LOW;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		i2s->reg_ctrl |= TEGRA30_I2S_CTRL_FRAME_FORMAT_LRCK;
+		i2s->reg_ctrl |= TEGRA30_I2S_CTRL_LRCK_L_LOW;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		i2s->reg_ctrl |= TEGRA30_I2S_CTRL_FRAME_FORMAT_LRCK;
+		i2s->reg_ctrl |= TEGRA30_I2S_CTRL_LRCK_L_LOW;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int tegra30_i2s_hw_params(struct snd_pcm_substream *substream,
+				 struct snd_pcm_hw_params *params,
+				 struct snd_soc_dai *dai)
+{
+	struct device *dev = substream->pcm->card->dev;
+	struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+	u32 val;
+	int ret, sample_size, srate, i2sclock, bitcnt;
+
+	if (params_channels(params) != 2)
+		return -EINVAL;
+
+	i2s->reg_ctrl &= ~TEGRA30_I2S_CTRL_BIT_SIZE_MASK;
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		i2s->reg_ctrl |= TEGRA30_I2S_CTRL_BIT_SIZE_16;
+		sample_size = 16;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	srate = params_rate(params);
+
+	/* Final "* 2" required by Tegra hardware */
+	i2sclock = srate * params_channels(params) * sample_size * 2;
+
+	bitcnt = (i2sclock / (2 * srate)) - 1;
+	if (bitcnt < 0 || bitcnt > TEGRA30_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US)
+		return -EINVAL;
+
+	ret = clk_set_rate(i2s->clk_i2s, i2sclock);
+	if (ret) {
+		dev_err(dev, "Can't set I2S clock rate: %d\n", ret);
+		return ret;
+	}
+
+	val = bitcnt << TEGRA30_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT;
+
+	if (i2sclock % (2 * srate))
+		val |= TEGRA30_I2S_TIMING_NON_SYM_ENABLE;
+
+	tegra30_i2s_write(i2s, TEGRA30_I2S_TIMING, val);
+
+	val = (0 << TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT) |
+	      (1 << TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT) |
+	      (1 << TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT) |
+	      TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_16 |
+	      TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_16;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		val |= TEGRA30_AUDIOCIF_CTRL_DIRECTION_RX;
+		tegra30_i2s_write(i2s, TEGRA30_I2S_CIF_RX_CTRL, val);
+	} else {
+		val |= TEGRA30_AUDIOCIF_CTRL_DIRECTION_TX;
+		tegra30_i2s_write(i2s, TEGRA30_I2S_CIF_TX_CTRL, val);
+	}
+
+	val = (1 << TEGRA30_I2S_OFFSET_RX_DATA_OFFSET_SHIFT) |
+	      (1 << TEGRA30_I2S_OFFSET_TX_DATA_OFFSET_SHIFT);
+	tegra30_i2s_write(i2s, TEGRA30_I2S_OFFSET, val);
+
+	return 0;
+}
+
+static void tegra30_i2s_start_playback(struct tegra30_i2s *i2s)
+{
+	tegra30_ahub_enable_tx_fifo(i2s->playback_fifo_cif);
+	i2s->reg_ctrl |= TEGRA30_I2S_CTRL_XFER_EN_TX;
+	tegra30_i2s_write(i2s, TEGRA30_I2S_CTRL, i2s->reg_ctrl);
+}
+
+static void tegra30_i2s_stop_playback(struct tegra30_i2s *i2s)
+{
+	tegra30_ahub_disable_tx_fifo(i2s->playback_fifo_cif);
+	i2s->reg_ctrl &= ~TEGRA30_I2S_CTRL_XFER_EN_TX;
+	tegra30_i2s_write(i2s, TEGRA30_I2S_CTRL, i2s->reg_ctrl);
+}
+
+static void tegra30_i2s_start_capture(struct tegra30_i2s *i2s)
+{
+	tegra30_ahub_enable_rx_fifo(i2s->capture_fifo_cif);
+	i2s->reg_ctrl |= TEGRA30_I2S_CTRL_XFER_EN_RX;
+	tegra30_i2s_write(i2s, TEGRA30_I2S_CTRL, i2s->reg_ctrl);
+}
+
+static void tegra30_i2s_stop_capture(struct tegra30_i2s *i2s)
+{
+	tegra30_ahub_disable_rx_fifo(i2s->capture_fifo_cif);
+	i2s->reg_ctrl &= ~TEGRA30_I2S_CTRL_XFER_EN_RX;
+	tegra30_i2s_write(i2s, TEGRA30_I2S_CTRL, i2s->reg_ctrl);
+}
+
+static int tegra30_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+				struct snd_soc_dai *dai)
+{
+	struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+			tegra30_i2s_start_playback(i2s);
+		else
+			tegra30_i2s_start_capture(i2s);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+			tegra30_i2s_stop_playback(i2s);
+		else
+			tegra30_i2s_stop_capture(i2s);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int tegra30_i2s_probe(struct snd_soc_dai *dai)
+{
+	struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+
+	dai->capture_dma_data = &i2s->capture_dma_data;
+	dai->playback_dma_data = &i2s->playback_dma_data;
+
+	return 0;
+}
+
+static struct snd_soc_dai_ops tegra30_i2s_dai_ops = {
+	.startup	= tegra30_i2s_startup,
+	.shutdown	= tegra30_i2s_shutdown,
+	.set_fmt	= tegra30_i2s_set_fmt,
+	.hw_params	= tegra30_i2s_hw_params,
+	.trigger	= tegra30_i2s_trigger,
+};
+
+static const struct snd_soc_dai_driver tegra30_i2s_dai_template = {
+	.probe = tegra30_i2s_probe,
+	.playback = {
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_8000_96000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	},
+	.capture = {
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_8000_96000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	},
+	.ops = &tegra30_i2s_dai_ops,
+	.symmetric_rates = 1,
+};
+
+static bool tegra30_i2s_wr_rd_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case TEGRA30_I2S_CTRL:
+	case TEGRA30_I2S_TIMING:
+	case TEGRA30_I2S_OFFSET:
+	case TEGRA30_I2S_CH_CTRL:
+	case TEGRA30_I2S_SLOT_CTRL:
+	case TEGRA30_I2S_CIF_RX_CTRL:
+	case TEGRA30_I2S_CIF_TX_CTRL:
+	case TEGRA30_I2S_FLOWCTL:
+	case TEGRA30_I2S_TX_STEP:
+	case TEGRA30_I2S_FLOW_STATUS:
+	case TEGRA30_I2S_FLOW_TOTAL:
+	case TEGRA30_I2S_FLOW_OVER:
+	case TEGRA30_I2S_FLOW_UNDER:
+	case TEGRA30_I2S_LCOEF_1_4_0:
+	case TEGRA30_I2S_LCOEF_1_4_1:
+	case TEGRA30_I2S_LCOEF_1_4_2:
+	case TEGRA30_I2S_LCOEF_1_4_3:
+	case TEGRA30_I2S_LCOEF_1_4_4:
+	case TEGRA30_I2S_LCOEF_1_4_5:
+	case TEGRA30_I2S_LCOEF_2_4_0:
+	case TEGRA30_I2S_LCOEF_2_4_1:
+	case TEGRA30_I2S_LCOEF_2_4_2:
+		return true;
+	default:
+		return false;
+	};
+}
+
+static bool tegra30_i2s_volatile_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case TEGRA30_I2S_FLOW_STATUS:
+	case TEGRA30_I2S_FLOW_TOTAL:
+	case TEGRA30_I2S_FLOW_OVER:
+	case TEGRA30_I2S_FLOW_UNDER:
+		return true;
+	default:
+		return false;
+	};
+}
+
+static const struct regmap_config tegra30_i2s_regmap_config = {
+	.reg_bits = 32,
+	.reg_stride = 4,
+	.val_bits = 32,
+	.max_register = TEGRA30_I2S_LCOEF_2_4_2,
+	.writeable_reg = tegra30_i2s_wr_rd_reg,
+	.readable_reg = tegra30_i2s_wr_rd_reg,
+	.volatile_reg = tegra30_i2s_volatile_reg,
+	.cache_type = REGCACHE_RBTREE,
+};
+
+static __devinit int tegra30_i2s_platform_probe(struct platform_device *pdev)
+{
+	struct tegra30_i2s *i2s;
+	u32 cif_ids[2];
+	struct resource *mem, *memregion;
+	void __iomem *regs;
+	int ret;
+
+	i2s = devm_kzalloc(&pdev->dev, sizeof(struct tegra30_i2s), GFP_KERNEL);
+	if (!i2s) {
+		dev_err(&pdev->dev, "Can't allocate tegra30_i2s\n");
+		ret = -ENOMEM;
+		goto err;
+	}
+	dev_set_drvdata(&pdev->dev, i2s);
+
+	i2s->dai = tegra30_i2s_dai_template;
+	i2s->dai.name = dev_name(&pdev->dev);
+
+	ret = of_property_read_u32_array(pdev->dev.of_node,
+					 "nvidia,ahub-cif-ids", cif_ids,
+					 ARRAY_SIZE(cif_ids));
+	if (ret < 0)
+		goto err;
+
+	i2s->playback_i2s_cif = cif_ids[0];
+	i2s->capture_i2s_cif = cif_ids[1];
+
+	i2s->clk_i2s = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(i2s->clk_i2s)) {
+		dev_err(&pdev->dev, "Can't retrieve i2s clock\n");
+		ret = PTR_ERR(i2s->clk_i2s);
+		goto err;
+	}
+
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!mem) {
+		dev_err(&pdev->dev, "No memory resource\n");
+		ret = -ENODEV;
+		goto err_clk_put;
+	}
+
+	memregion = devm_request_mem_region(&pdev->dev, mem->start,
+					    resource_size(mem), DRV_NAME);
+	if (!memregion) {
+		dev_err(&pdev->dev, "Memory region already claimed\n");
+		ret = -EBUSY;
+		goto err_clk_put;
+	}
+
+	regs = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
+	if (!regs) {
+		dev_err(&pdev->dev, "ioremap failed\n");
+		ret = -ENOMEM;
+		goto err_clk_put;
+	}
+
+	i2s->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
+					    &tegra30_i2s_regmap_config);
+	if (IS_ERR(i2s->regmap)) {
+		dev_err(&pdev->dev, "regmap init failed\n");
+		ret = PTR_ERR(i2s->regmap);
+		goto err_clk_put;
+	}
+	regcache_cache_only(i2s->regmap, true);
+
+	pm_runtime_enable(&pdev->dev);
+	if (!pm_runtime_enabled(&pdev->dev)) {
+		ret = tegra30_i2s_runtime_resume(&pdev->dev);
+		if (ret)
+			goto err_pm_disable;
+	}
+
+	ret = snd_soc_register_dai(&pdev->dev, &i2s->dai);
+	if (ret) {
+		dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
+		ret = -ENOMEM;
+		goto err_suspend;
+	}
+
+	ret = tegra_pcm_platform_register(&pdev->dev);
+	if (ret) {
+		dev_err(&pdev->dev, "Could not register PCM: %d\n", ret);
+		goto err_unregister_dai;
+	}
+
+	return 0;
+
+err_unregister_dai:
+	snd_soc_unregister_dai(&pdev->dev);
+err_suspend:
+	if (!pm_runtime_status_suspended(&pdev->dev))
+		tegra30_i2s_runtime_suspend(&pdev->dev);
+err_pm_disable:
+	pm_runtime_disable(&pdev->dev);
+err_clk_put:
+	clk_put(i2s->clk_i2s);
+err:
+	return ret;
+}
+
+static int __devexit tegra30_i2s_platform_remove(struct platform_device *pdev)
+{
+	struct tegra30_i2s *i2s = dev_get_drvdata(&pdev->dev);
+
+	pm_runtime_disable(&pdev->dev);
+	if (!pm_runtime_status_suspended(&pdev->dev))
+		tegra30_i2s_runtime_suspend(&pdev->dev);
+
+	tegra_pcm_platform_unregister(&pdev->dev);
+	snd_soc_unregister_dai(&pdev->dev);
+
+	clk_put(i2s->clk_i2s);
+
+	return 0;
+}
+
+static const struct of_device_id tegra30_i2s_of_match[] __devinitconst = {
+	{ .compatible = "nvidia,tegra30-i2s", },
+	{},
+};
+
+static const struct dev_pm_ops tegra30_i2s_pm_ops __devinitconst = {
+	SET_RUNTIME_PM_OPS(tegra30_i2s_runtime_suspend,
+			   tegra30_i2s_runtime_resume, NULL)
+};
+
+static struct platform_driver tegra30_i2s_driver = {
+	.driver = {
+		.name = DRV_NAME,
+		.owner = THIS_MODULE,
+		.of_match_table = tegra30_i2s_of_match,
+		.pm = &tegra30_i2s_pm_ops,
+	},
+	.probe = tegra30_i2s_platform_probe,
+	.remove = __devexit_p(tegra30_i2s_platform_remove),
+};
+module_platform_driver(tegra30_i2s_driver);
+
+MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
+MODULE_DESCRIPTION("Tegra30 I2S ASoC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, tegra30_i2s_of_match);
diff --git a/sound/soc/tegra/tegra30_i2s.h b/sound/soc/tegra/tegra30_i2s.h
new file mode 100644
index 0000000..91adf29
--- /dev/null
+++ b/sound/soc/tegra/tegra30_i2s.h
@@ -0,0 +1,242 @@
+/*
+ * tegra30_i2s.h - Definitions for Tegra30 I2S driver
+ *
+ * Copyright (c) 2011,2012, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __TEGRA30_I2S_H__
+#define __TEGRA30_I2S_H__
+
+#include "tegra_pcm.h"
+
+/* Register offsets from TEGRA30_I2S*_BASE */
+
+#define TEGRA30_I2S_CTRL				0x0
+#define TEGRA30_I2S_TIMING				0x4
+#define TEGRA30_I2S_OFFSET				0x08
+#define TEGRA30_I2S_CH_CTRL				0x0c
+#define TEGRA30_I2S_SLOT_CTRL				0x10
+#define TEGRA30_I2S_CIF_RX_CTRL				0x14
+#define TEGRA30_I2S_CIF_TX_CTRL				0x18
+#define TEGRA30_I2S_FLOWCTL				0x1c
+#define TEGRA30_I2S_TX_STEP				0x20
+#define TEGRA30_I2S_FLOW_STATUS				0x24
+#define TEGRA30_I2S_FLOW_TOTAL				0x28
+#define TEGRA30_I2S_FLOW_OVER				0x2c
+#define TEGRA30_I2S_FLOW_UNDER				0x30
+#define TEGRA30_I2S_LCOEF_1_4_0				0x34
+#define TEGRA30_I2S_LCOEF_1_4_1				0x38
+#define TEGRA30_I2S_LCOEF_1_4_2				0x3c
+#define TEGRA30_I2S_LCOEF_1_4_3				0x40
+#define TEGRA30_I2S_LCOEF_1_4_4				0x44
+#define TEGRA30_I2S_LCOEF_1_4_5				0x48
+#define TEGRA30_I2S_LCOEF_2_4_0				0x4c
+#define TEGRA30_I2S_LCOEF_2_4_1				0x50
+#define TEGRA30_I2S_LCOEF_2_4_2				0x54
+
+/* Fields in TEGRA30_I2S_CTRL */
+
+#define TEGRA30_I2S_CTRL_XFER_EN_TX			(1 << 31)
+#define TEGRA30_I2S_CTRL_XFER_EN_RX			(1 << 30)
+#define TEGRA30_I2S_CTRL_CG_EN				(1 << 29)
+#define TEGRA30_I2S_CTRL_SOFT_RESET			(1 << 28)
+#define TEGRA30_I2S_CTRL_TX_FLOWCTL_EN			(1 << 27)
+
+#define TEGRA30_I2S_CTRL_OBS_SEL_SHIFT			24
+#define TEGRA30_I2S_CTRL_OBS_SEL_MASK			(7 << TEGRA30_I2S_CTRL_OBS_SEL_SHIFT)
+
+#define TEGRA30_I2S_FRAME_FORMAT_LRCK			0
+#define TEGRA30_I2S_FRAME_FORMAT_FSYNC			1
+
+#define TEGRA30_I2S_CTRL_FRAME_FORMAT_SHIFT		12
+#define TEGRA30_I2S_CTRL_FRAME_FORMAT_MASK		(7                              << TEGRA30_I2S_CTRL_FRAME_FORMAT_SHIFT)
+#define TEGRA30_I2S_CTRL_FRAME_FORMAT_LRCK		(TEGRA30_I2S_FRAME_FORMAT_LRCK  << TEGRA30_I2S_CTRL_FRAME_FORMAT_SHIFT)
+#define TEGRA30_I2S_CTRL_FRAME_FORMAT_FSYNC		(TEGRA30_I2S_FRAME_FORMAT_FSYNC << TEGRA30_I2S_CTRL_FRAME_FORMAT_SHIFT)
+
+#define TEGRA30_I2S_CTRL_MASTER_ENABLE			(1 << 10)
+
+#define TEGRA30_I2S_LRCK_LEFT_LOW			0
+#define TEGRA30_I2S_LRCK_RIGHT_LOW			1
+
+#define TEGRA30_I2S_CTRL_LRCK_SHIFT			9
+#define TEGRA30_I2S_CTRL_LRCK_MASK			(1                          << TEGRA30_I2S_CTRL_LRCK_SHIFT)
+#define TEGRA30_I2S_CTRL_LRCK_L_LOW			(TEGRA30_I2S_LRCK_LEFT_LOW  << TEGRA30_I2S_CTRL_LRCK_SHIFT)
+#define TEGRA30_I2S_CTRL_LRCK_R_LOW			(TEGRA30_I2S_LRCK_RIGHT_LOW << TEGRA30_I2S_CTRL_LRCK_SHIFT)
+
+#define TEGRA30_I2S_CTRL_LPBK_ENABLE			(1 << 8)
+
+#define TEGRA30_I2S_BIT_CODE_LINEAR			0
+#define TEGRA30_I2S_BIT_CODE_ULAW			1
+#define TEGRA30_I2S_BIT_CODE_ALAW			2
+
+#define TEGRA30_I2S_CTRL_BIT_CODE_SHIFT			4
+#define TEGRA30_I2S_CTRL_BIT_CODE_MASK			(3                           << TEGRA30_I2S_CTRL_BIT_CODE_SHIFT)
+#define TEGRA30_I2S_CTRL_BIT_CODE_LINEAR		(TEGRA30_I2S_BIT_CODE_LINEAR << TEGRA30_I2S_CTRL_BIT_CODE_SHIFT)
+#define TEGRA30_I2S_CTRL_BIT_CODE_ULAW			(TEGRA30_I2S_BIT_CODE_ULAW   << TEGRA30_I2S_CTRL_BIT_CODE_SHIFT)
+#define TEGRA30_I2S_CTRL_BIT_CODE_ALAW			(TEGRA30_I2S_BIT_CODE_ALAW   << TEGRA30_I2S_CTRL_BIT_CODE_SHIFT)
+
+#define TEGRA30_I2S_BITS_8				1
+#define TEGRA30_I2S_BITS_12				2
+#define TEGRA30_I2S_BITS_16				3
+#define TEGRA30_I2S_BITS_20				4
+#define TEGRA30_I2S_BITS_24				5
+#define TEGRA30_I2S_BITS_28				6
+#define TEGRA30_I2S_BITS_32				7
+
+/* Sample container size; see {RX,TX}_MASK field in CH_CTRL below */
+#define TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT			0
+#define TEGRA30_I2S_CTRL_BIT_SIZE_MASK			(7                   << TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT)
+#define TEGRA30_I2S_CTRL_BIT_SIZE_8			(TEGRA30_I2S_BITS_8  << TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT)
+#define TEGRA30_I2S_CTRL_BIT_SIZE_12			(TEGRA30_I2S_BITS_12 << TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT)
+#define TEGRA30_I2S_CTRL_BIT_SIZE_16			(TEGRA30_I2S_BITS_16 << TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT)
+#define TEGRA30_I2S_CTRL_BIT_SIZE_20			(TEGRA30_I2S_BITS_20 << TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT)
+#define TEGRA30_I2S_CTRL_BIT_SIZE_24			(TEGRA30_I2S_BITS_24 << TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT)
+#define TEGRA30_I2S_CTRL_BIT_SIZE_28			(TEGRA30_I2S_BITS_28 << TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT)
+#define TEGRA30_I2S_CTRL_BIT_SIZE_32			(TEGRA30_I2S_BITS_32 << TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT)
+
+/* Fields in TEGRA30_I2S_TIMING */
+
+#define TEGRA30_I2S_TIMING_NON_SYM_ENABLE		(1 << 12)
+#define TEGRA30_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT	0
+#define TEGRA30_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US	0x7fff
+#define TEGRA30_I2S_TIMING_CHANNEL_BIT_COUNT_MASK	(TEGRA30_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US << TEGRA30_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT)
+
+/* Fields in TEGRA30_I2S_OFFSET */
+
+#define TEGRA30_I2S_OFFSET_RX_DATA_OFFSET_SHIFT		16
+#define TEGRA30_I2S_OFFSET_RX_DATA_OFFSET_MASK_US	0x7ff
+#define TEGRA30_I2S_OFFSET_RX_DATA_OFFSET_MASK		(TEGRA30_I2S_OFFSET_RX_DATA_OFFSET_MASK_US << TEGRA30_I2S_OFFSET_RX_DATA_OFFSET_SHIFT)
+#define TEGRA30_I2S_OFFSET_TX_DATA_OFFSET_SHIFT		0
+#define TEGRA30_I2S_OFFSET_TX_DATA_OFFSET_MASK_US	0x7ff
+#define TEGRA30_I2S_OFFSET_TX_DATA_OFFSET_MASK		(TEGRA30_I2S_OFFSET_TX_DATA_OFFSET_MASK_US << TEGRA30_I2S_OFFSET_TX_DATA_OFFSET_SHIFT)
+
+/* Fields in TEGRA30_I2S_CH_CTRL */
+
+/* (FSYNC width - 1) in bit clocks */
+#define TEGRA30_I2S_CH_CTRL_FSYNC_WIDTH_SHIFT		24
+#define TEGRA30_I2S_CH_CTRL_FSYNC_WIDTH_MASK_US		0xff
+#define TEGRA30_I2S_CH_CTRL_FSYNC_WIDTH_MASK		(TEGRA30_I2S_CH_CTRL_FSYNC_WIDTH_MASK_US << TEGRA30_I2S_CH_CTRL_FSYNC_WIDTH_SHIFT)
+
+#define TEGRA30_I2S_HIGHZ_NO				0
+#define TEGRA30_I2S_HIGHZ_YES				1
+#define TEGRA30_I2S_HIGHZ_ON_HALF_BIT_CLK		2
+
+#define TEGRA30_I2S_CH_CTRL_HIGHZ_CTRL_SHIFT		12
+#define TEGRA30_I2S_CH_CTRL_HIGHZ_CTRL_MASK		(3                                 << TEGRA30_I2S_CH_CTRL_HIGHZ_CTRL_SHIFT)
+#define TEGRA30_I2S_CH_CTRL_HIGHZ_CTRL_NO		(TEGRA30_I2S_HIGHZ_NO              << TEGRA30_I2S_CH_CTRL_HIGHZ_CTRL_SHIFT)
+#define TEGRA30_I2S_CH_CTRL_HIGHZ_CTRL_YES		(TEGRA30_I2S_HIGHZ_YES             << TEGRA30_I2S_CH_CTRL_HIGHZ_CTRL_SHIFT)
+#define TEGRA30_I2S_CH_CTRL_HIGHZ_CTRL_ON_HALF_BIT_CLK	(TEGRA30_I2S_HIGHZ_ON_HALF_BIT_CLK << TEGRA30_I2S_CH_CTRL_HIGHZ_CTRL_SHIFT)
+
+#define TEGRA30_I2S_MSB_FIRST				0
+#define TEGRA30_I2S_LSB_FIRST				1
+
+#define TEGRA30_I2S_CH_CTRL_RX_BIT_ORDER_SHIFT		10
+#define TEGRA30_I2S_CH_CTRL_RX_BIT_ORDER_MASK		(1                     << TEGRA30_I2S_CH_CTRL_RX_BIT_ORDER_SHIFT)
+#define TEGRA30_I2S_CH_CTRL_RX_BIT_ORDER_MSB_FIRST	(TEGRA30_I2S_MSB_FIRST << TEGRA30_I2S_CH_CTRL_RX_BIT_ORDER_SHIFT)
+#define TEGRA30_I2S_CH_CTRL_RX_BIT_ORDER_LSB_FIRST	(TEGRA30_I2S_LSB_FIRST << TEGRA30_I2S_CH_CTRL_RX_BIT_ORDER_SHIFT)
+#define TEGRA30_I2S_CH_CTRL_TX_BIT_ORDER_SHIFT		9
+#define TEGRA30_I2S_CH_CTRL_TX_BIT_ORDER_MASK		(1                     << TEGRA30_I2S_CH_CTRL_TX_BIT_ORDER_SHIFT)
+#define TEGRA30_I2S_CH_CTRL_TX_BIT_ORDER_MSB_FIRST	(TEGRA30_I2S_MSB_FIRST << TEGRA30_I2S_CH_CTRL_TX_BIT_ORDER_SHIFT)
+#define TEGRA30_I2S_CH_CTRL_TX_BIT_ORDER_LSB_FIRST	(TEGRA30_I2S_LSB_FIRST << TEGRA30_I2S_CH_CTRL_TX_BIT_ORDER_SHIFT)
+
+#define TEGRA30_I2S_POS_EDGE				0
+#define TEGRA30_I2S_NEG_EDGE				1
+
+#define TEGRA30_I2S_CH_CTRL_EGDE_CTRL_SHIFT		8
+#define TEGRA30_I2S_CH_CTRL_EGDE_CTRL_MASK		(1                    << TEGRA30_I2S_CH_CTRL_EGDE_CTRL_SHIFT)
+#define TEGRA30_I2S_CH_CTRL_EGDE_CTRL_POS_EDGE		(TEGRA30_I2S_POS_EDGE << TEGRA30_I2S_CH_CTRL_EGDE_CTRL_SHIFT)
+#define TEGRA30_I2S_CH_CTRL_EGDE_CTRL_NEG_EDGE		(TEGRA30_I2S_NEG_EDGE << TEGRA30_I2S_CH_CTRL_EGDE_CTRL_SHIFT)
+
+/* Sample size is # bits from BIT_SIZE minus this field */
+#define TEGRA30_I2S_CH_CTRL_RX_MASK_BITS_SHIFT		4
+#define TEGRA30_I2S_CH_CTRL_RX_MASK_BITS_MASK_US	7
+#define TEGRA30_I2S_CH_CTRL_RX_MASK_BITS_MASK		(TEGRA30_I2S_CH_CTRL_RX_MASK_BITS_MASK_US << TEGRA30_I2S_CH_CTRL_RX_MASK_BITS_SHIFT)
+
+#define TEGRA30_I2S_CH_CTRL_TX_MASK_BITS_SHIFT		0
+#define TEGRA30_I2S_CH_CTRL_TX_MASK_BITS_MASK_US	7
+#define TEGRA30_I2S_CH_CTRL_TX_MASK_BITS_MASK		(TEGRA30_I2S_CH_CTRL_TX_MASK_BITS_MASK_US << TEGRA30_I2S_CH_CTRL_TX_MASK_BITS_SHIFT)
+
+/* Fields in TEGRA30_I2S_SLOT_CTRL */
+
+/* Number of slots in frame, minus 1 */
+#define TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_SHIFT		16
+#define TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_MASK_US	7
+#define TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_MASK		(TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOT_MASK_US << TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOT_SHIFT)
+
+/* TDM mode slot enable bitmask */
+#define TEGRA30_I2S_SLOT_CTRL_RX_SLOT_ENABLES_SHIFT	8
+#define TEGRA30_I2S_SLOT_CTRL_RX_SLOT_ENABLES_MASK	(0xff << TEGRA30_I2S_SLOT_CTRL_RX_SLOT_ENABLES_SHIFT)
+
+#define TEGRA30_I2S_SLOT_CTRL_TX_SLOT_ENABLES_SHIFT	0
+#define TEGRA30_I2S_SLOT_CTRL_TX_SLOT_ENABLES_MASK	(0xff << TEGRA30_I2S_SLOT_CTRL_TX_SLOT_ENABLES_SHIFT)
+
+/* Fields in TEGRA30_I2S_CIF_RX_CTRL */
+/* Uses field from TEGRA30_AUDIOCIF_CTRL_* in tegra30_ahub.h */
+
+/* Fields in TEGRA30_I2S_CIF_TX_CTRL */
+/* Uses field from TEGRA30_AUDIOCIF_CTRL_* in tegra30_ahub.h */
+
+/* Fields in TEGRA30_I2S_FLOWCTL */
+
+#define TEGRA30_I2S_FILTER_LINEAR			0
+#define TEGRA30_I2S_FILTER_QUAD				1
+
+#define TEGRA30_I2S_FLOWCTL_FILTER_SHIFT		31
+#define TEGRA30_I2S_FLOWCTL_FILTER_MASK			(1                         << TEGRA30_I2S_FLOWCTL_FILTER_SHIFT)
+#define TEGRA30_I2S_FLOWCTL_FILTER_LINEAR		(TEGRA30_I2S_FILTER_LINEAR << TEGRA30_I2S_FLOWCTL_FILTER_SHIFT)
+#define TEGRA30_I2S_FLOWCTL_FILTER_QUAD			(TEGRA30_I2S_FILTER_QUAD   << TEGRA30_I2S_FLOWCTL_FILTER_SHIFT)
+
+/* Fields in TEGRA30_I2S_TX_STEP */
+
+#define TEGRA30_I2S_TX_STEP_SHIFT			0
+#define TEGRA30_I2S_TX_STEP_MASK_US			0xffff
+#define TEGRA30_I2S_TX_STEP_MASK			(TEGRA30_I2S_TX_STEP_MASK_US << TEGRA30_I2S_TX_STEP_SHIFT)
+
+/* Fields in TEGRA30_I2S_FLOW_STATUS */
+
+#define TEGRA30_I2S_FLOW_STATUS_UNDERFLOW		(1 << 31)
+#define TEGRA30_I2S_FLOW_STATUS_OVERFLOW		(1 << 30)
+#define TEGRA30_I2S_FLOW_STATUS_MONITOR_INT_EN		(1 << 4)
+#define TEGRA30_I2S_FLOW_STATUS_COUNTER_CLR		(1 << 3)
+#define TEGRA30_I2S_FLOW_STATUS_MONITOR_CLR		(1 << 2)
+#define TEGRA30_I2S_FLOW_STATUS_COUNTER_EN		(1 << 1)
+#define TEGRA30_I2S_FLOW_STATUS_MONITOR_EN		(1 << 0)
+
+/*
+ * There are no fields in TEGRA30_I2S_FLOW_TOTAL, TEGRA30_I2S_FLOW_OVER,
+ * TEGRA30_I2S_FLOW_UNDER; they are counters taking the whole register.
+ */
+
+/* Fields in TEGRA30_I2S_LCOEF_* */
+
+#define TEGRA30_I2S_LCOEF_COEF_SHIFT			0
+#define TEGRA30_I2S_LCOEF_COEF_MASK_US			0xffff
+#define TEGRA30_I2S_LCOEF_COEF_MASK			(TEGRA30_I2S_LCOEF_COEF_MASK_US << TEGRA30_I2S_LCOEF_COEF_SHIFT)
+
+struct tegra30_i2s {
+	struct snd_soc_dai_driver dai;
+	int cif_id;
+	struct clk *clk_i2s;
+	enum tegra30_ahub_txcif capture_i2s_cif;
+	enum tegra30_ahub_rxcif capture_fifo_cif;
+	struct tegra_pcm_dma_params capture_dma_data;
+	enum tegra30_ahub_rxcif playback_i2s_cif;
+	enum tegra30_ahub_txcif playback_fifo_cif;
+	struct tegra_pcm_dma_params playback_dma_data;
+	struct regmap *regmap;
+	u32 reg_ctrl;
+};
+
+#endif
diff --git a/sound/soc/tegra/tegra_alc5632.c b/sound/soc/tegra/tegra_alc5632.c
index e45ccd8..32de700 100644
--- a/sound/soc/tegra/tegra_alc5632.c
+++ b/sound/soc/tegra/tegra_alc5632.c
@@ -1,16 +1,17 @@
 /*
-* tegra_alc5632.c  --  Toshiba AC100(PAZ00) machine ASoC driver
-*
-* Copyright (C) 2011 The AC100 Kernel Team <ac100@lists.lauchpad.net>
-*
-* Authors:  Leon Romanovsky <leon@leon.nu>
-*           Andrey Danin <danindrey@mail.ru>
-*           Marc Dietrich <marvin24@gmx.de>
-*
-* 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.
-*/
+ * tegra_alc5632.c  --  Toshiba AC100(PAZ00) machine ASoC driver
+ *
+ * Copyright (C) 2011 The AC100 Kernel Team <ac100@lists.lauchpad.net>
+ * Copyright (C) 2012 - NVIDIA, Inc.
+ *
+ * Authors:  Leon Romanovsky <leon@leon.nu>
+ *           Andrey Danin <danindrey@mail.ru>
+ *           Marc Dietrich <marvin24@gmx.de>
+ *
+ * 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.
+ */
 
 #include <asm/mach-types.h>
 
@@ -28,9 +29,6 @@
 
 #include "../codecs/alc5632.h"
 
-#include "tegra_das.h"
-#include "tegra_i2s.h"
-#include "tegra_pcm.h"
 #include "tegra_asoc_utils.h"
 
 #define DRV_NAME "tegra-alc5632"
@@ -39,7 +37,6 @@
 
 struct tegra_alc5632 {
 	struct tegra_asoc_utils_data util_data;
-	struct platform_device *pcm_dev;
 	int gpio_requested;
 	int gpio_hp_det;
 };
@@ -140,7 +137,6 @@
 static struct snd_soc_dai_link tegra_alc5632_dai = {
 	.name = "ALC5632",
 	.stream_name = "ALC5632 PCM",
-	.platform_name = "tegra-pcm-audio",
 	.codec_dai_name = "alc5632-hifi",
 	.init = tegra_alc5632_asoc_init,
 	.ops = &tegra_alc5632_asoc_ops,
@@ -179,8 +175,6 @@
 	platform_set_drvdata(pdev, card);
 	snd_soc_card_set_drvdata(card, alc5632);
 
-	alc5632->pcm_dev = ERR_PTR(-EINVAL);
-
 	if (!(pdev->dev.of_node)) {
 		dev_err(&pdev->dev, "Must be instantiated using device tree\n");
 		ret = -EINVAL;
@@ -214,18 +208,11 @@
 		goto err;
 	}
 
-	alc5632->pcm_dev = platform_device_register_simple(
-		"tegra-pcm-audio", -1, NULL, 0);
-	if (IS_ERR(alc5632->pcm_dev)) {
-		dev_err(&pdev->dev,
-			"Can't instantiate tegra-pcm-audio\n");
-		ret = PTR_ERR(alc5632->pcm_dev);
-		goto err;
-	}
+	tegra_alc5632_dai.platform_of_node = tegra_alc5632_dai.cpu_dai_of_node;
 
 	ret = tegra_asoc_utils_init(&alc5632->util_data, &pdev->dev);
 	if (ret)
-		goto err_unregister;
+		goto err;
 
 	ret = snd_soc_register_card(card);
 	if (ret) {
@@ -238,9 +225,6 @@
 
 err_fini_utils:
 	tegra_asoc_utils_fini(&alc5632->util_data);
-err_unregister:
-	if (!IS_ERR(alc5632->pcm_dev))
-		platform_device_unregister(alc5632->pcm_dev);
 err:
 	return ret;
 }
@@ -259,8 +243,6 @@
 	snd_soc_unregister_card(card);
 
 	tegra_asoc_utils_fini(&machine->util_data);
-	if (!IS_ERR(machine->pcm_dev))
-		platform_device_unregister(machine->pcm_dev);
 
 	return 0;
 }
diff --git a/sound/soc/tegra/tegra_asoc_utils.c b/sound/soc/tegra/tegra_asoc_utils.c
index f8428e4..9515ce5 100644
--- a/sound/soc/tegra/tegra_asoc_utils.c
+++ b/sound/soc/tegra/tegra_asoc_utils.c
@@ -2,7 +2,7 @@
  * tegra_asoc_utils.c - Harmony machine ASoC driver
  *
  * Author: Stephen Warren <swarren@nvidia.com>
- * Copyright (C) 2010 - NVIDIA, Inc.
+ * Copyright (C) 2010,2012 - NVIDIA, Inc.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -25,6 +25,7 @@
 #include <linux/err.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/of.h>
 
 #include "tegra_asoc_utils.h"
 
@@ -40,7 +41,10 @@
 	case 22050:
 	case 44100:
 	case 88200:
-		new_baseclock = 56448000;
+		if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA20)
+			new_baseclock = 56448000;
+		else
+			new_baseclock = 564480000;
 		break;
 	case 8000:
 	case 16000:
@@ -48,7 +52,10 @@
 	case 48000:
 	case 64000:
 	case 96000:
-		new_baseclock = 73728000;
+		if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA20)
+			new_baseclock = 73728000;
+		else
+			new_baseclock = 552960000;
 		break;
 	default:
 		return -EINVAL;
@@ -78,7 +85,7 @@
 		return err;
 	}
 
-	/* Don't set cdev1 rate; its locked to pll_a_out0 */
+	/* Don't set cdev1/extern1 rate; it's locked to pll_a_out0 */
 
 	err = clk_enable(data->clk_pll_a);
 	if (err) {
@@ -112,6 +119,17 @@
 
 	data->dev = dev;
 
+	if (of_machine_is_compatible("nvidia,tegra20"))
+		data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA20;
+	else if (of_machine_is_compatible("nvidia,tegra30"))
+		data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA30;
+	else if (!dev->of_node)
+		/* non-DT is always Tegra20 */
+		data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA20;
+	else
+		/* DT boot, but unknown SoC */
+		return -EINVAL;
+
 	data->clk_pll_a = clk_get_sys(NULL, "pll_a");
 	if (IS_ERR(data->clk_pll_a)) {
 		dev_err(data->dev, "Can't retrieve clk pll_a\n");
@@ -126,15 +144,24 @@
 		goto err_put_pll_a;
 	}
 
-	data->clk_cdev1 = clk_get_sys(NULL, "cdev1");
+	if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA20)
+		data->clk_cdev1 = clk_get_sys(NULL, "cdev1");
+	else
+		data->clk_cdev1 = clk_get_sys("extern1", NULL);
 	if (IS_ERR(data->clk_cdev1)) {
 		dev_err(data->dev, "Can't retrieve clk cdev1\n");
 		ret = PTR_ERR(data->clk_cdev1);
 		goto err_put_pll_a_out0;
 	}
 
+	ret = tegra_asoc_utils_set_rate(data, 44100, 256 * 44100);
+	if (ret)
+		goto err_put_cdev1;
+
 	return 0;
 
+err_put_cdev1:
+	clk_put(data->clk_cdev1);
 err_put_pll_a_out0:
 	clk_put(data->clk_pll_a_out0);
 err_put_pll_a:
diff --git a/sound/soc/tegra/tegra_asoc_utils.h b/sound/soc/tegra/tegra_asoc_utils.h
index 4818195..44db1db 100644
--- a/sound/soc/tegra/tegra_asoc_utils.h
+++ b/sound/soc/tegra/tegra_asoc_utils.h
@@ -2,7 +2,7 @@
  * tegra_asoc_utils.h - Definitions for Tegra DAS driver
  *
  * Author: Stephen Warren <swarren@nvidia.com>
- * Copyright (C) 2010 - NVIDIA, Inc.
+ * Copyright (C) 2010,2012 - NVIDIA, Inc.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -26,8 +26,14 @@
 struct clk;
 struct device;
 
+enum tegra_asoc_utils_soc {
+	TEGRA_ASOC_UTILS_SOC_TEGRA20,
+	TEGRA_ASOC_UTILS_SOC_TEGRA30,
+};
+
 struct tegra_asoc_utils_data {
 	struct device *dev;
+	enum tegra_asoc_utils_soc soc;
 	struct clk *clk_pll_a;
 	struct clk *clk_pll_a_out0;
 	struct clk *clk_cdev1;
@@ -42,4 +48,3 @@
 void tegra_asoc_utils_fini(struct tegra_asoc_utils_data *data);
 
 #endif
-
diff --git a/sound/soc/tegra/tegra_das.c b/sound/soc/tegra/tegra_das.c
deleted file mode 100644
index 3b3c1ba..0000000
--- a/sound/soc/tegra/tegra_das.c
+++ /dev/null
@@ -1,261 +0,0 @@
-/*
- * tegra_das.c - Tegra DAS driver
- *
- * Author: Stephen Warren <swarren@nvidia.com>
- * Copyright (C) 2010 - NVIDIA, 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#include <linux/module.h>
-#include <linux/debugfs.h>
-#include <linux/device.h>
-#include <linux/platform_device.h>
-#include <linux/seq_file.h>
-#include <linux/slab.h>
-#include <linux/io.h>
-#include <mach/iomap.h>
-#include <sound/soc.h>
-#include "tegra_das.h"
-
-#define DRV_NAME "tegra-das"
-
-static struct tegra_das *das;
-
-static inline void tegra_das_write(u32 reg, u32 val)
-{
-	__raw_writel(val, das->regs + reg);
-}
-
-static inline u32 tegra_das_read(u32 reg)
-{
-	return __raw_readl(das->regs + reg);
-}
-
-int tegra_das_connect_dap_to_dac(int dap, int dac)
-{
-	u32 addr;
-	u32 reg;
-
-	if (!das)
-		return -ENODEV;
-
-	addr = TEGRA_DAS_DAP_CTRL_SEL +
-		(dap * TEGRA_DAS_DAP_CTRL_SEL_STRIDE);
-	reg = dac << TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P;
-
-	tegra_das_write(addr, reg);
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(tegra_das_connect_dap_to_dac);
-
-int tegra_das_connect_dap_to_dap(int dap, int otherdap, int master,
-					int sdata1rx, int sdata2rx)
-{
-	u32 addr;
-	u32 reg;
-
-	if (!das)
-		return -ENODEV;
-
-	addr = TEGRA_DAS_DAP_CTRL_SEL +
-		(dap * TEGRA_DAS_DAP_CTRL_SEL_STRIDE);
-	reg = otherdap << TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P |
-		!!sdata2rx << TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA2_TX_RX_P |
-		!!sdata1rx << TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA1_TX_RX_P |
-		!!master << TEGRA_DAS_DAP_CTRL_SEL_DAP_MS_SEL_P;
-
-	tegra_das_write(addr, reg);
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(tegra_das_connect_dap_to_dap);
-
-int tegra_das_connect_dac_to_dap(int dac, int dap)
-{
-	u32 addr;
-	u32 reg;
-
-	if (!das)
-		return -ENODEV;
-
-	addr = TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL +
-		(dac * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_STRIDE);
-	reg = dap << TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL_P |
-		dap << TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL_P |
-		dap << TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL_P;
-
-	tegra_das_write(addr, reg);
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(tegra_das_connect_dac_to_dap);
-
-#ifdef CONFIG_DEBUG_FS
-static int tegra_das_show(struct seq_file *s, void *unused)
-{
-	int i;
-	u32 addr;
-	u32 reg;
-
-	for (i = 0; i < TEGRA_DAS_DAP_CTRL_SEL_COUNT; i++) {
-		addr = TEGRA_DAS_DAP_CTRL_SEL +
-			(i * TEGRA_DAS_DAP_CTRL_SEL_STRIDE);
-		reg = tegra_das_read(addr);
-		seq_printf(s, "TEGRA_DAS_DAP_CTRL_SEL[%d] = %08x\n", i, reg);
-	}
-
-	for (i = 0; i < TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_COUNT; i++) {
-		addr = TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL +
-			(i * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_STRIDE);
-		reg = tegra_das_read(addr);
-		seq_printf(s, "TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL[%d] = %08x\n",
-				 i, reg);
-	}
-
-	return 0;
-}
-
-static int tegra_das_debug_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, tegra_das_show, inode->i_private);
-}
-
-static const struct file_operations tegra_das_debug_fops = {
-	.open    = tegra_das_debug_open,
-	.read    = seq_read,
-	.llseek  = seq_lseek,
-	.release = single_release,
-};
-
-static void tegra_das_debug_add(struct tegra_das *das)
-{
-	das->debug = debugfs_create_file(DRV_NAME, S_IRUGO,
-					 snd_soc_debugfs_root, das,
-					 &tegra_das_debug_fops);
-}
-
-static void tegra_das_debug_remove(struct tegra_das *das)
-{
-	if (das->debug)
-		debugfs_remove(das->debug);
-}
-#else
-static inline void tegra_das_debug_add(struct tegra_das *das)
-{
-}
-
-static inline void tegra_das_debug_remove(struct tegra_das *das)
-{
-}
-#endif
-
-static int __devinit tegra_das_probe(struct platform_device *pdev)
-{
-	struct resource *res, *region;
-	int ret = 0;
-
-	if (das)
-		return -ENODEV;
-
-	das = devm_kzalloc(&pdev->dev, sizeof(struct tegra_das), GFP_KERNEL);
-	if (!das) {
-		dev_err(&pdev->dev, "Can't allocate tegra_das\n");
-		ret = -ENOMEM;
-		goto err;
-	}
-	das->dev = &pdev->dev;
-
-	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (!res) {
-		dev_err(&pdev->dev, "No memory resource\n");
-		ret = -ENODEV;
-		goto err;
-	}
-
-	region = devm_request_mem_region(&pdev->dev, res->start,
-					 resource_size(res), pdev->name);
-	if (!region) {
-		dev_err(&pdev->dev, "Memory region already claimed\n");
-		ret = -EBUSY;
-		goto err;
-	}
-
-	das->regs = devm_ioremap(&pdev->dev, res->start, resource_size(res));
-	if (!das->regs) {
-		dev_err(&pdev->dev, "ioremap failed\n");
-		ret = -ENOMEM;
-		goto err;
-	}
-
-	ret = tegra_das_connect_dap_to_dac(TEGRA_DAS_DAP_ID_1,
-					   TEGRA_DAS_DAP_SEL_DAC1);
-	if (ret) {
-		dev_err(&pdev->dev, "Can't set up DAS DAP connection\n");
-		goto err;
-	}
-	ret = tegra_das_connect_dac_to_dap(TEGRA_DAS_DAC_ID_1,
-					   TEGRA_DAS_DAC_SEL_DAP1);
-	if (ret) {
-		dev_err(&pdev->dev, "Can't set up DAS DAC connection\n");
-		goto err;
-	}
-
-	tegra_das_debug_add(das);
-
-	platform_set_drvdata(pdev, das);
-
-	return 0;
-
-err:
-	das = NULL;
-	return ret;
-}
-
-static int __devexit tegra_das_remove(struct platform_device *pdev)
-{
-	if (!das)
-		return -ENODEV;
-
-	tegra_das_debug_remove(das);
-
-	das = NULL;
-
-	return 0;
-}
-
-static const struct of_device_id tegra_das_of_match[] __devinitconst = {
-	{ .compatible = "nvidia,tegra20-das", },
-	{},
-};
-
-static struct platform_driver tegra_das_driver = {
-	.probe = tegra_das_probe,
-	.remove = __devexit_p(tegra_das_remove),
-	.driver = {
-		.name = DRV_NAME,
-		.owner = THIS_MODULE,
-		.of_match_table = tegra_das_of_match,
-	},
-};
-module_platform_driver(tegra_das_driver);
-
-MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
-MODULE_DESCRIPTION("Tegra DAS driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:" DRV_NAME);
-MODULE_DEVICE_TABLE(of, tegra_das_of_match);
diff --git a/sound/soc/tegra/tegra_das.h b/sound/soc/tegra/tegra_das.h
deleted file mode 100644
index 2c96c7b..0000000
--- a/sound/soc/tegra/tegra_das.h
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * tegra_das.h - Definitions for Tegra DAS driver
- *
- * Author: Stephen Warren <swarren@nvidia.com>
- * Copyright (C) 2010 - NVIDIA, 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#ifndef __TEGRA_DAS_H__
-#define __TEGRA_DAS_H__
-
-/* Register TEGRA_DAS_DAP_CTRL_SEL */
-#define TEGRA_DAS_DAP_CTRL_SEL				0x00
-#define TEGRA_DAS_DAP_CTRL_SEL_COUNT			5
-#define TEGRA_DAS_DAP_CTRL_SEL_STRIDE			4
-#define TEGRA_DAS_DAP_CTRL_SEL_DAP_MS_SEL_P		31
-#define TEGRA_DAS_DAP_CTRL_SEL_DAP_MS_SEL_S		1
-#define TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA1_TX_RX_P	30
-#define TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA1_TX_RX_S	1
-#define TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA2_TX_RX_P	29
-#define TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA2_TX_RX_S	1
-#define TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P		0
-#define TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_S		5
-
-/* Values for field TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL */
-#define TEGRA_DAS_DAP_SEL_DAC1	0
-#define TEGRA_DAS_DAP_SEL_DAC2	1
-#define TEGRA_DAS_DAP_SEL_DAC3	2
-#define TEGRA_DAS_DAP_SEL_DAP1	16
-#define TEGRA_DAS_DAP_SEL_DAP2	17
-#define TEGRA_DAS_DAP_SEL_DAP3	18
-#define TEGRA_DAS_DAP_SEL_DAP4	19
-#define TEGRA_DAS_DAP_SEL_DAP5	20
-
-/* Register TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL */
-#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL			0x40
-#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_COUNT			3
-#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_STRIDE			4
-#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL_P	28
-#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL_S	4
-#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL_P	24
-#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL_S	4
-#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL_P		0
-#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL_S		4
-
-/*
- * Values for:
- * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL
- * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL
- * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL
- */
-#define TEGRA_DAS_DAC_SEL_DAP1	0
-#define TEGRA_DAS_DAC_SEL_DAP2	1
-#define TEGRA_DAS_DAC_SEL_DAP3	2
-#define TEGRA_DAS_DAC_SEL_DAP4	3
-#define TEGRA_DAS_DAC_SEL_DAP5	4
-
-/*
- * Names/IDs of the DACs/DAPs.
- */
-
-#define TEGRA_DAS_DAP_ID_1 0
-#define TEGRA_DAS_DAP_ID_2 1
-#define TEGRA_DAS_DAP_ID_3 2
-#define TEGRA_DAS_DAP_ID_4 3
-#define TEGRA_DAS_DAP_ID_5 4
-
-#define TEGRA_DAS_DAC_ID_1 0
-#define TEGRA_DAS_DAC_ID_2 1
-#define TEGRA_DAS_DAC_ID_3 2
-
-struct tegra_das {
-	struct device *dev;
-	void __iomem *regs;
-	struct dentry *debug;
-};
-
-/*
- * Terminology:
- * DAS: Digital audio switch (HW module controlled by this driver)
- * DAP: Digital audio port (port/pins on Tegra device)
- * DAC: Digital audio controller (e.g. I2S or AC97 controller elsewhere)
- * 
- * The Tegra DAS is a mux/cross-bar which can connect each DAP to a specific
- * DAC, or another DAP. When DAPs are connected, one must be the master and
- * one the slave. Each DAC allows selection of a specific DAP for input, to
- * cater for the case where N DAPs are connected to 1 DAC for broadcast
- * output.
- *
- * This driver is dumb; no attempt is made to ensure that a valid routing
- * configuration is programmed.
- */
-
-/*
- * Connect a DAP to to a DAC
- * dap_id: DAP to connect: TEGRA_DAS_DAP_ID_*
- * dac_sel: DAC to connect to: TEGRA_DAS_DAP_SEL_DAC*
- */
-extern int tegra_das_connect_dap_to_dac(int dap_id, int dac_sel);
-
-/*
- * Connect a DAP to to another DAP
- * dap_id: DAP to connect: TEGRA_DAS_DAP_ID_*
- * other_dap_sel: DAP to connect to: TEGRA_DAS_DAP_SEL_DAP*
- * master: Is this DAP the master (1) or slave (0)
- * sdata1rx: Is this DAP's SDATA1 pin RX (1) or TX (0)
- * sdata2rx: Is this DAP's SDATA2 pin RX (1) or TX (0)
- */
-extern int tegra_das_connect_dap_to_dap(int dap_id, int other_dap_sel,
-					int master, int sdata1rx,
-					int sdata2rx);
-
-/*
- * Connect a DAC's input to a DAP
- * (DAC outputs are selected by the DAP)
- * dac_id: DAC ID to connect: TEGRA_DAS_DAC_ID_*
- * dap_sel: DAP to receive input from: TEGRA_DAS_DAC_SEL_DAP*
- */
-extern int tegra_das_connect_dac_to_dap(int dac_id, int dap_sel);
-
-#endif
diff --git a/sound/soc/tegra/tegra_i2s.c b/sound/soc/tegra/tegra_i2s.c
deleted file mode 100644
index e533499..0000000
--- a/sound/soc/tegra/tegra_i2s.c
+++ /dev/null
@@ -1,459 +0,0 @@
-/*
- * tegra_i2s.c - Tegra I2S driver
- *
- * Author: Stephen Warren <swarren@nvidia.com>
- * Copyright (C) 2010 - NVIDIA, Inc.
- *
- * Based on code copyright/by:
- *
- * Copyright (c) 2009-2010, NVIDIA Corporation.
- * Scott Peterson <speterson@nvidia.com>
- *
- * Copyright (C) 2010 Google, Inc.
- * Iliyan Malchev <malchev@google.com>
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#include <linux/clk.h>
-#include <linux/module.h>
-#include <linux/debugfs.h>
-#include <linux/device.h>
-#include <linux/platform_device.h>
-#include <linux/seq_file.h>
-#include <linux/slab.h>
-#include <linux/io.h>
-#include <linux/of.h>
-#include <mach/iomap.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-
-#include "tegra_i2s.h"
-
-#define DRV_NAME "tegra-i2s"
-
-static inline void tegra_i2s_write(struct tegra_i2s *i2s, u32 reg, u32 val)
-{
-	__raw_writel(val, i2s->regs + reg);
-}
-
-static inline u32 tegra_i2s_read(struct tegra_i2s *i2s, u32 reg)
-{
-	return __raw_readl(i2s->regs + reg);
-}
-
-#ifdef CONFIG_DEBUG_FS
-static int tegra_i2s_show(struct seq_file *s, void *unused)
-{
-#define REG(r) { r, #r }
-	static const struct {
-		int offset;
-		const char *name;
-	} regs[] = {
-		REG(TEGRA_I2S_CTRL),
-		REG(TEGRA_I2S_STATUS),
-		REG(TEGRA_I2S_TIMING),
-		REG(TEGRA_I2S_FIFO_SCR),
-		REG(TEGRA_I2S_PCM_CTRL),
-		REG(TEGRA_I2S_NW_CTRL),
-		REG(TEGRA_I2S_TDM_CTRL),
-		REG(TEGRA_I2S_TDM_TX_RX_CTRL),
-	};
-#undef REG
-
-	struct tegra_i2s *i2s = s->private;
-	int i;
-
-	clk_enable(i2s->clk_i2s);
-
-	for (i = 0; i < ARRAY_SIZE(regs); i++) {
-		u32 val = tegra_i2s_read(i2s, regs[i].offset);
-		seq_printf(s, "%s = %08x\n", regs[i].name, val);
-	}
-
-	clk_disable(i2s->clk_i2s);
-
-	return 0;
-}
-
-static int tegra_i2s_debug_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, tegra_i2s_show, inode->i_private);
-}
-
-static const struct file_operations tegra_i2s_debug_fops = {
-	.open    = tegra_i2s_debug_open,
-	.read    = seq_read,
-	.llseek  = seq_lseek,
-	.release = single_release,
-};
-
-static void tegra_i2s_debug_add(struct tegra_i2s *i2s)
-{
-	i2s->debug = debugfs_create_file(i2s->dai.name, S_IRUGO,
-					 snd_soc_debugfs_root, i2s,
-					 &tegra_i2s_debug_fops);
-}
-
-static void tegra_i2s_debug_remove(struct tegra_i2s *i2s)
-{
-	if (i2s->debug)
-		debugfs_remove(i2s->debug);
-}
-#else
-static inline void tegra_i2s_debug_add(struct tegra_i2s *i2s)
-{
-}
-
-static inline void tegra_i2s_debug_remove(struct tegra_i2s *i2s)
-{
-}
-#endif
-
-static int tegra_i2s_set_fmt(struct snd_soc_dai *dai,
-				unsigned int fmt)
-{
-	struct tegra_i2s *i2s = snd_soc_dai_get_drvdata(dai);
-
-	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
-	case SND_SOC_DAIFMT_NB_NF:
-		break;
-	default:
-		return -EINVAL;
-	}
-
-	i2s->reg_ctrl &= ~TEGRA_I2S_CTRL_MASTER_ENABLE;
-	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
-	case SND_SOC_DAIFMT_CBS_CFS:
-		i2s->reg_ctrl |= TEGRA_I2S_CTRL_MASTER_ENABLE;
-		break;
-	case SND_SOC_DAIFMT_CBM_CFM:
-		break;
-	default:
-		return -EINVAL;
-	}
-
-	i2s->reg_ctrl &= ~(TEGRA_I2S_CTRL_BIT_FORMAT_MASK | 
-				TEGRA_I2S_CTRL_LRCK_MASK);
-	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
-	case SND_SOC_DAIFMT_DSP_A:
-		i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_DSP;
-		i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_L_LOW;
-		break;
-	case SND_SOC_DAIFMT_DSP_B:
-		i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_DSP;
-		i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_R_LOW;
-		break;
-	case SND_SOC_DAIFMT_I2S:
-		i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_I2S;
-		i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_L_LOW;
-		break;
-	case SND_SOC_DAIFMT_RIGHT_J:
-		i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_RJM;
-		i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_L_LOW;
-		break;
-	case SND_SOC_DAIFMT_LEFT_J:
-		i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_LJM;
-		i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_L_LOW;
-		break;
-	default:
-		return -EINVAL;
-	}
-
-	return 0;
-}
-
-static int tegra_i2s_hw_params(struct snd_pcm_substream *substream,
-				struct snd_pcm_hw_params *params,
-				struct snd_soc_dai *dai)
-{
-        struct device *dev = substream->pcm->card->dev;
-	struct tegra_i2s *i2s = snd_soc_dai_get_drvdata(dai);
-	u32 reg;
-	int ret, sample_size, srate, i2sclock, bitcnt;
-
-	i2s->reg_ctrl &= ~TEGRA_I2S_CTRL_BIT_SIZE_MASK;
-	switch (params_format(params)) {
-	case SNDRV_PCM_FORMAT_S16_LE:
-		i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_SIZE_16;
-		sample_size = 16;
-		break;
-	case SNDRV_PCM_FORMAT_S24_LE:
-		i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_SIZE_24;
-		sample_size = 24;
-		break;
-	case SNDRV_PCM_FORMAT_S32_LE:
-		i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_SIZE_32;
-		sample_size = 32;
-		break;
-	default:
-		return -EINVAL;
-	}
-
-	srate = params_rate(params);
-
-	/* Final "* 2" required by Tegra hardware */
-	i2sclock = srate * params_channels(params) * sample_size * 2;
-
-	ret = clk_set_rate(i2s->clk_i2s, i2sclock);
-	if (ret) {
-		dev_err(dev, "Can't set I2S clock rate: %d\n", ret);
-		return ret;
-	}
-
-	bitcnt = (i2sclock / (2 * srate)) - 1;
-	if (bitcnt < 0 || bitcnt > TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US)
-		return -EINVAL;
-	reg = bitcnt << TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT;
-
-	if (i2sclock % (2 * srate))
-		reg |= TEGRA_I2S_TIMING_NON_SYM_ENABLE;
-
-	if (!i2s->clk_refs)
-		clk_enable(i2s->clk_i2s);
-
-	tegra_i2s_write(i2s, TEGRA_I2S_TIMING, reg);
-
-	tegra_i2s_write(i2s, TEGRA_I2S_FIFO_SCR,
-		TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_FOUR_SLOTS |
-		TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_FOUR_SLOTS);
-
-	if (!i2s->clk_refs)
-		clk_disable(i2s->clk_i2s);
-
-	return 0;
-}
-
-static void tegra_i2s_start_playback(struct tegra_i2s *i2s)
-{
-	i2s->reg_ctrl |= TEGRA_I2S_CTRL_FIFO1_ENABLE;
-	tegra_i2s_write(i2s, TEGRA_I2S_CTRL, i2s->reg_ctrl);
-}
-
-static void tegra_i2s_stop_playback(struct tegra_i2s *i2s)
-{
-	i2s->reg_ctrl &= ~TEGRA_I2S_CTRL_FIFO1_ENABLE;
-	tegra_i2s_write(i2s, TEGRA_I2S_CTRL, i2s->reg_ctrl);
-}
-
-static void tegra_i2s_start_capture(struct tegra_i2s *i2s)
-{
-	i2s->reg_ctrl |= TEGRA_I2S_CTRL_FIFO2_ENABLE;
-	tegra_i2s_write(i2s, TEGRA_I2S_CTRL, i2s->reg_ctrl);
-}
-
-static void tegra_i2s_stop_capture(struct tegra_i2s *i2s)
-{
-	i2s->reg_ctrl &= ~TEGRA_I2S_CTRL_FIFO2_ENABLE;
-	tegra_i2s_write(i2s, TEGRA_I2S_CTRL, i2s->reg_ctrl);
-}
-
-static int tegra_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
-				struct snd_soc_dai *dai)
-{
-	struct tegra_i2s *i2s = snd_soc_dai_get_drvdata(dai);
-
-	switch (cmd) {
-	case SNDRV_PCM_TRIGGER_START:
-	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-	case SNDRV_PCM_TRIGGER_RESUME:
-		if (!i2s->clk_refs)
-			clk_enable(i2s->clk_i2s);
-		i2s->clk_refs++;
-		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-			tegra_i2s_start_playback(i2s);
-		else
-			tegra_i2s_start_capture(i2s);
-		break;
-	case SNDRV_PCM_TRIGGER_STOP:
-	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-	case SNDRV_PCM_TRIGGER_SUSPEND:
-		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-			tegra_i2s_stop_playback(i2s);
-		else
-			tegra_i2s_stop_capture(i2s);
-		i2s->clk_refs--;
-		if (!i2s->clk_refs)
-			clk_disable(i2s->clk_i2s);
-		break;
-	default:
-		return -EINVAL;
-	}
-
-	return 0;
-}
-
-static int tegra_i2s_probe(struct snd_soc_dai *dai)
-{
-	struct tegra_i2s * i2s = snd_soc_dai_get_drvdata(dai);
-
-	dai->capture_dma_data = &i2s->capture_dma_data;
-	dai->playback_dma_data = &i2s->playback_dma_data;
-
-	return 0;
-}
-
-static const struct snd_soc_dai_ops tegra_i2s_dai_ops = {
-	.set_fmt	= tegra_i2s_set_fmt,
-	.hw_params	= tegra_i2s_hw_params,
-	.trigger	= tegra_i2s_trigger,
-};
-
-static const struct snd_soc_dai_driver tegra_i2s_dai_template = {
-	.probe = tegra_i2s_probe,
-	.playback = {
-		.channels_min = 2,
-		.channels_max = 2,
-		.rates = SNDRV_PCM_RATE_8000_96000,
-		.formats = SNDRV_PCM_FMTBIT_S16_LE,
-	},
-	.capture = {
-		.channels_min = 2,
-		.channels_max = 2,
-		.rates = SNDRV_PCM_RATE_8000_96000,
-		.formats = SNDRV_PCM_FMTBIT_S16_LE,
-	},
-	.ops = &tegra_i2s_dai_ops,
-	.symmetric_rates = 1,
-};
-
-static __devinit int tegra_i2s_platform_probe(struct platform_device *pdev)
-{
-	struct tegra_i2s * i2s;
-	struct resource *mem, *memregion, *dmareq;
-	u32 of_dma[2];
-	u32 dma_ch;
-	int ret;
-
-	i2s = devm_kzalloc(&pdev->dev, sizeof(struct tegra_i2s), GFP_KERNEL);
-	if (!i2s) {
-		dev_err(&pdev->dev, "Can't allocate tegra_i2s\n");
-		ret = -ENOMEM;
-		goto err;
-	}
-	dev_set_drvdata(&pdev->dev, i2s);
-
-	i2s->dai = tegra_i2s_dai_template;
-	i2s->dai.name = dev_name(&pdev->dev);
-
-	i2s->clk_i2s = clk_get(&pdev->dev, NULL);
-	if (IS_ERR(i2s->clk_i2s)) {
-		dev_err(&pdev->dev, "Can't retrieve i2s clock\n");
-		ret = PTR_ERR(i2s->clk_i2s);
-		goto err;
-	}
-
-	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (!mem) {
-		dev_err(&pdev->dev, "No memory resource\n");
-		ret = -ENODEV;
-		goto err_clk_put;
-	}
-
-	dmareq = platform_get_resource(pdev, IORESOURCE_DMA, 0);
-	if (!dmareq) {
-		if (of_property_read_u32_array(pdev->dev.of_node,
-					"nvidia,dma-request-selector",
-					of_dma, 2) < 0) {
-			dev_err(&pdev->dev, "No DMA resource\n");
-			ret = -ENODEV;
-			goto err_clk_put;
-		}
-		dma_ch = of_dma[1];
-	} else {
-		dma_ch = dmareq->start;
-	}
-
-	memregion = devm_request_mem_region(&pdev->dev, mem->start,
-					    resource_size(mem), DRV_NAME);
-	if (!memregion) {
-		dev_err(&pdev->dev, "Memory region already claimed\n");
-		ret = -EBUSY;
-		goto err_clk_put;
-	}
-
-	i2s->regs = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
-	if (!i2s->regs) {
-		dev_err(&pdev->dev, "ioremap failed\n");
-		ret = -ENOMEM;
-		goto err_clk_put;
-	}
-
-	i2s->capture_dma_data.addr = mem->start + TEGRA_I2S_FIFO2;
-	i2s->capture_dma_data.wrap = 4;
-	i2s->capture_dma_data.width = 32;
-	i2s->capture_dma_data.req_sel = dma_ch;
-
-	i2s->playback_dma_data.addr = mem->start + TEGRA_I2S_FIFO1;
-	i2s->playback_dma_data.wrap = 4;
-	i2s->playback_dma_data.width = 32;
-	i2s->playback_dma_data.req_sel = dma_ch;
-
-	i2s->reg_ctrl = TEGRA_I2S_CTRL_FIFO_FORMAT_PACKED;
-
-	ret = snd_soc_register_dai(&pdev->dev, &i2s->dai);
-	if (ret) {
-		dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
-		ret = -ENOMEM;
-		goto err_clk_put;
-	}
-
-	tegra_i2s_debug_add(i2s);
-
-	return 0;
-
-err_clk_put:
-	clk_put(i2s->clk_i2s);
-err:
-	return ret;
-}
-
-static int __devexit tegra_i2s_platform_remove(struct platform_device *pdev)
-{
-	struct tegra_i2s *i2s = dev_get_drvdata(&pdev->dev);
-
-	snd_soc_unregister_dai(&pdev->dev);
-
-	tegra_i2s_debug_remove(i2s);
-
-	clk_put(i2s->clk_i2s);
-
-	return 0;
-}
-
-static const struct of_device_id tegra_i2s_of_match[] __devinitconst = {
-	{ .compatible = "nvidia,tegra20-i2s", },
-	{},
-};
-
-static struct platform_driver tegra_i2s_driver = {
-	.driver = {
-		.name = DRV_NAME,
-		.owner = THIS_MODULE,
-		.of_match_table = tegra_i2s_of_match,
-	},
-	.probe = tegra_i2s_platform_probe,
-	.remove = __devexit_p(tegra_i2s_platform_remove),
-};
-module_platform_driver(tegra_i2s_driver);
-
-MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
-MODULE_DESCRIPTION("Tegra I2S ASoC driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:" DRV_NAME);
-MODULE_DEVICE_TABLE(of, tegra_i2s_of_match);
diff --git a/sound/soc/tegra/tegra_i2s.h b/sound/soc/tegra/tegra_i2s.h
deleted file mode 100644
index 15ce1e2..0000000
--- a/sound/soc/tegra/tegra_i2s.h
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * tegra_i2s.h - Definitions for Tegra I2S driver
- *
- * Author: Stephen Warren <swarren@nvidia.com>
- * Copyright (C) 2010 - NVIDIA, Inc.
- *
- * Based on code copyright/by:
- *
- * Copyright (c) 2009-2010, NVIDIA Corporation.
- * Scott Peterson <speterson@nvidia.com>
- *
- * Copyright (C) 2010 Google, Inc.
- * Iliyan Malchev <malchev@google.com>
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#ifndef __TEGRA_I2S_H__
-#define __TEGRA_I2S_H__
-
-#include "tegra_pcm.h"
-
-/* Register offsets from TEGRA_I2S1_BASE and TEGRA_I2S2_BASE */
-
-#define TEGRA_I2S_CTRL					0x00
-#define TEGRA_I2S_STATUS				0x04
-#define TEGRA_I2S_TIMING				0x08
-#define TEGRA_I2S_FIFO_SCR				0x0c
-#define TEGRA_I2S_PCM_CTRL				0x10
-#define TEGRA_I2S_NW_CTRL				0x14
-#define TEGRA_I2S_TDM_CTRL				0x20
-#define TEGRA_I2S_TDM_TX_RX_CTRL			0x24
-#define TEGRA_I2S_FIFO1					0x40
-#define TEGRA_I2S_FIFO2					0x80
-
-/* Fields in TEGRA_I2S_CTRL */
-
-#define TEGRA_I2S_CTRL_FIFO2_TX_ENABLE			(1 << 30)
-#define TEGRA_I2S_CTRL_FIFO1_ENABLE			(1 << 29)
-#define TEGRA_I2S_CTRL_FIFO2_ENABLE			(1 << 28)
-#define TEGRA_I2S_CTRL_FIFO1_RX_ENABLE			(1 << 27)
-#define TEGRA_I2S_CTRL_FIFO_LPBK_ENABLE			(1 << 26)
-#define TEGRA_I2S_CTRL_MASTER_ENABLE			(1 << 25)
-
-#define TEGRA_I2S_LRCK_LEFT_LOW				0
-#define TEGRA_I2S_LRCK_RIGHT_LOW			1
-
-#define TEGRA_I2S_CTRL_LRCK_SHIFT			24
-#define TEGRA_I2S_CTRL_LRCK_MASK			(1                        << TEGRA_I2S_CTRL_LRCK_SHIFT)
-#define TEGRA_I2S_CTRL_LRCK_L_LOW			(TEGRA_I2S_LRCK_LEFT_LOW  << TEGRA_I2S_CTRL_LRCK_SHIFT)
-#define TEGRA_I2S_CTRL_LRCK_R_LOW			(TEGRA_I2S_LRCK_RIGHT_LOW << TEGRA_I2S_CTRL_LRCK_SHIFT)
-
-#define TEGRA_I2S_BIT_FORMAT_I2S			0
-#define TEGRA_I2S_BIT_FORMAT_RJM			1
-#define TEGRA_I2S_BIT_FORMAT_LJM			2
-#define TEGRA_I2S_BIT_FORMAT_DSP			3
-
-#define TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT			10
-#define TEGRA_I2S_CTRL_BIT_FORMAT_MASK			(3                        << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT)
-#define TEGRA_I2S_CTRL_BIT_FORMAT_I2S			(TEGRA_I2S_BIT_FORMAT_I2S << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT)
-#define TEGRA_I2S_CTRL_BIT_FORMAT_RJM			(TEGRA_I2S_BIT_FORMAT_RJM << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT)
-#define TEGRA_I2S_CTRL_BIT_FORMAT_LJM			(TEGRA_I2S_BIT_FORMAT_LJM << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT)
-#define TEGRA_I2S_CTRL_BIT_FORMAT_DSP			(TEGRA_I2S_BIT_FORMAT_DSP << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT)
-
-#define TEGRA_I2S_BIT_SIZE_16				0
-#define TEGRA_I2S_BIT_SIZE_20				1
-#define TEGRA_I2S_BIT_SIZE_24				2
-#define TEGRA_I2S_BIT_SIZE_32				3
-
-#define TEGRA_I2S_CTRL_BIT_SIZE_SHIFT			8
-#define TEGRA_I2S_CTRL_BIT_SIZE_MASK			(3                     << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT)
-#define TEGRA_I2S_CTRL_BIT_SIZE_16			(TEGRA_I2S_BIT_SIZE_16 << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT)
-#define TEGRA_I2S_CTRL_BIT_SIZE_20			(TEGRA_I2S_BIT_SIZE_20 << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT)
-#define TEGRA_I2S_CTRL_BIT_SIZE_24			(TEGRA_I2S_BIT_SIZE_24 << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT)
-#define TEGRA_I2S_CTRL_BIT_SIZE_32			(TEGRA_I2S_BIT_SIZE_32 << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT)
-
-#define TEGRA_I2S_FIFO_16_LSB				0
-#define TEGRA_I2S_FIFO_20_LSB				1
-#define TEGRA_I2S_FIFO_24_LSB				2
-#define TEGRA_I2S_FIFO_32				3
-#define TEGRA_I2S_FIFO_PACKED				7
-
-#define TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT		4
-#define TEGRA_I2S_CTRL_FIFO_FORMAT_MASK			(7                     << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT)
-#define TEGRA_I2S_CTRL_FIFO_FORMAT_16_LSB		(TEGRA_I2S_FIFO_16_LSB << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT)
-#define TEGRA_I2S_CTRL_FIFO_FORMAT_20_LSB		(TEGRA_I2S_FIFO_20_LSB << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT)
-#define TEGRA_I2S_CTRL_FIFO_FORMAT_24_LSB		(TEGRA_I2S_FIFO_24_LSB << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT)
-#define TEGRA_I2S_CTRL_FIFO_FORMAT_32			(TEGRA_I2S_FIFO_32     << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT)
-#define TEGRA_I2S_CTRL_FIFO_FORMAT_PACKED		(TEGRA_I2S_FIFO_PACKED << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT)
-
-#define TEGRA_I2S_CTRL_IE_FIFO1_ERR			(1 << 3)
-#define TEGRA_I2S_CTRL_IE_FIFO2_ERR			(1 << 2)
-#define TEGRA_I2S_CTRL_QE_FIFO1				(1 << 1)
-#define TEGRA_I2S_CTRL_QE_FIFO2				(1 << 0)
-
-/* Fields in TEGRA_I2S_STATUS */
-
-#define TEGRA_I2S_STATUS_FIFO1_RDY			(1 << 31)
-#define TEGRA_I2S_STATUS_FIFO2_RDY			(1 << 30)
-#define TEGRA_I2S_STATUS_FIFO1_BSY			(1 << 29)
-#define TEGRA_I2S_STATUS_FIFO2_BSY			(1 << 28)
-#define TEGRA_I2S_STATUS_FIFO1_ERR			(1 << 3)
-#define TEGRA_I2S_STATUS_FIFO2_ERR			(1 << 2)
-#define TEGRA_I2S_STATUS_QS_FIFO1			(1 << 1)
-#define TEGRA_I2S_STATUS_QS_FIFO2			(1 << 0)
-
-/* Fields in TEGRA_I2S_TIMING */
-
-#define TEGRA_I2S_TIMING_NON_SYM_ENABLE			(1 << 12)
-#define TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT	0
-#define TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US	0x7fff
-#define TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_MASK		(TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US << TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT)
-
-/* Fields in TEGRA_I2S_FIFO_SCR */
-
-#define TEGRA_I2S_FIFO_SCR_FIFO2_FULL_EMPTY_COUNT_SHIFT	24
-#define TEGRA_I2S_FIFO_SCR_FIFO1_FULL_EMPTY_COUNT_SHIFT	16
-#define TEGRA_I2S_FIFO_SCR_FIFO_FULL_EMPTY_COUNT_MASK	0x3f
-
-#define TEGRA_I2S_FIFO_SCR_FIFO2_CLR			(1 << 12)
-#define TEGRA_I2S_FIFO_SCR_FIFO1_CLR			(1 << 8)
-
-#define TEGRA_I2S_FIFO_ATN_LVL_ONE_SLOT			0
-#define TEGRA_I2S_FIFO_ATN_LVL_FOUR_SLOTS		1
-#define TEGRA_I2S_FIFO_ATN_LVL_EIGHT_SLOTS		2
-#define TEGRA_I2S_FIFO_ATN_LVL_TWELVE_SLOTS		3
-
-#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT		4
-#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_MASK		(3 << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT)
-#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_ONE_SLOT	(TEGRA_I2S_FIFO_ATN_LVL_ONE_SLOT     << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT)
-#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_FOUR_SLOTS	(TEGRA_I2S_FIFO_ATN_LVL_FOUR_SLOTS   << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT)
-#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_EIGHT_SLOTS	(TEGRA_I2S_FIFO_ATN_LVL_EIGHT_SLOTS  << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT)
-#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_TWELVE_SLOTS	(TEGRA_I2S_FIFO_ATN_LVL_TWELVE_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT)
-
-#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT		0
-#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_MASK		(3 << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT)
-#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_ONE_SLOT	(TEGRA_I2S_FIFO_ATN_LVL_ONE_SLOT     << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT)
-#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_FOUR_SLOTS	(TEGRA_I2S_FIFO_ATN_LVL_FOUR_SLOTS   << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT)
-#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_EIGHT_SLOTS	(TEGRA_I2S_FIFO_ATN_LVL_EIGHT_SLOTS  << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT)
-#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_TWELVE_SLOTS	(TEGRA_I2S_FIFO_ATN_LVL_TWELVE_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT)
-
-struct tegra_i2s {
-	struct snd_soc_dai_driver dai;
-	struct clk *clk_i2s;
-	int clk_refs;
-	struct tegra_pcm_dma_params capture_dma_data;
-	struct tegra_pcm_dma_params playback_dma_data;
-	void __iomem *regs;
-	struct dentry *debug;
-	u32 reg_ctrl;
-};
-
-#endif
diff --git a/sound/soc/tegra/tegra_pcm.c b/sound/soc/tegra/tegra_pcm.c
index 8b44571..127348d 100644
--- a/sound/soc/tegra/tegra_pcm.c
+++ b/sound/soc/tegra/tegra_pcm.c
@@ -2,7 +2,7 @@
  * tegra_pcm.c - Tegra PCM driver
  *
  * Author: Stephen Warren <swarren@nvidia.com>
- * Copyright (C) 2010 - NVIDIA, Inc.
+ * Copyright (C) 2010,2012 - NVIDIA, Inc.
  *
  * Based on code copyright/by:
  *
@@ -29,8 +29,8 @@
  *
  */
 
-#include <linux/module.h>
 #include <linux/dma-mapping.h>
+#include <linux/module.h>
 #include <linux/slab.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -39,8 +39,6 @@
 
 #include "tegra_pcm.h"
 
-#define DRV_NAME "tegra-pcm-audio"
-
 static const struct snd_pcm_hardware tegra_pcm_hardware = {
 	.info			= SNDRV_PCM_INFO_MMAP |
 				  SNDRV_PCM_INFO_MMAP_VALID |
@@ -372,28 +370,18 @@
 	.pcm_free	= tegra_pcm_free,
 };
 
-static int __devinit tegra_pcm_platform_probe(struct platform_device *pdev)
+int __devinit tegra_pcm_platform_register(struct device *dev)
 {
-	return snd_soc_register_platform(&pdev->dev, &tegra_pcm_platform);
+	return snd_soc_register_platform(dev, &tegra_pcm_platform);
 }
+EXPORT_SYMBOL_GPL(tegra_pcm_platform_register);
 
-static int __devexit tegra_pcm_platform_remove(struct platform_device *pdev)
+void __devexit tegra_pcm_platform_unregister(struct device *dev)
 {
-	snd_soc_unregister_platform(&pdev->dev);
-	return 0;
+	snd_soc_unregister_platform(dev);
 }
-
-static struct platform_driver tegra_pcm_driver = {
-	.driver = {
-		.name = DRV_NAME,
-		.owner = THIS_MODULE,
-	},
-	.probe = tegra_pcm_platform_probe,
-	.remove = __devexit_p(tegra_pcm_platform_remove),
-};
-module_platform_driver(tegra_pcm_driver);
+EXPORT_SYMBOL_GPL(tegra_pcm_platform_unregister);
 
 MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
 MODULE_DESCRIPTION("Tegra PCM ASoC driver");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/tegra/tegra_pcm.h b/sound/soc/tegra/tegra_pcm.h
index dbb9033..985d418 100644
--- a/sound/soc/tegra/tegra_pcm.h
+++ b/sound/soc/tegra/tegra_pcm.h
@@ -2,7 +2,7 @@
  * tegra_pcm.h - Definitions for Tegra PCM driver
  *
  * Author: Stephen Warren <swarren@nvidia.com>
- * Copyright (C) 2010 - NVIDIA, Inc.
+ * Copyright (C) 2010,2012 - NVIDIA, Inc.
  *
  * Based on code copyright/by:
  *
@@ -52,4 +52,7 @@
 	struct tegra_dma_channel *dma_chan;
 };
 
+int tegra_pcm_platform_register(struct device *dev);
+void tegra_pcm_platform_unregister(struct device *dev);
+
 #endif
diff --git a/sound/soc/tegra/tegra_spdif.c b/sound/soc/tegra/tegra_spdif.c
deleted file mode 100644
index 9ff2c60..0000000
--- a/sound/soc/tegra/tegra_spdif.c
+++ /dev/null
@@ -1,364 +0,0 @@
-/*
- * tegra_spdif.c - Tegra SPDIF driver
- *
- * Author: Stephen Warren <swarren@nvidia.com>
- * Copyright (C) 2011 - NVIDIA, 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#include <linux/clk.h>
-#include <linux/module.h>
-#include <linux/debugfs.h>
-#include <linux/device.h>
-#include <linux/platform_device.h>
-#include <linux/seq_file.h>
-#include <linux/slab.h>
-#include <linux/io.h>
-#include <mach/iomap.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-
-#include "tegra_spdif.h"
-
-#define DRV_NAME "tegra-spdif"
-
-static inline void tegra_spdif_write(struct tegra_spdif *spdif, u32 reg,
-					u32 val)
-{
-	__raw_writel(val, spdif->regs + reg);
-}
-
-static inline u32 tegra_spdif_read(struct tegra_spdif *spdif, u32 reg)
-{
-	return __raw_readl(spdif->regs + reg);
-}
-
-#ifdef CONFIG_DEBUG_FS
-static int tegra_spdif_show(struct seq_file *s, void *unused)
-{
-#define REG(r) { r, #r }
-	static const struct {
-		int offset;
-		const char *name;
-	} regs[] = {
-		REG(TEGRA_SPDIF_CTRL),
-		REG(TEGRA_SPDIF_STATUS),
-		REG(TEGRA_SPDIF_STROBE_CTRL),
-		REG(TEGRA_SPDIF_DATA_FIFO_CSR),
-		REG(TEGRA_SPDIF_CH_STA_RX_A),
-		REG(TEGRA_SPDIF_CH_STA_RX_B),
-		REG(TEGRA_SPDIF_CH_STA_RX_C),
-		REG(TEGRA_SPDIF_CH_STA_RX_D),
-		REG(TEGRA_SPDIF_CH_STA_RX_E),
-		REG(TEGRA_SPDIF_CH_STA_RX_F),
-		REG(TEGRA_SPDIF_CH_STA_TX_A),
-		REG(TEGRA_SPDIF_CH_STA_TX_B),
-		REG(TEGRA_SPDIF_CH_STA_TX_C),
-		REG(TEGRA_SPDIF_CH_STA_TX_D),
-		REG(TEGRA_SPDIF_CH_STA_TX_E),
-		REG(TEGRA_SPDIF_CH_STA_TX_F),
-	};
-#undef REG
-
-	struct tegra_spdif *spdif = s->private;
-	int i;
-
-	clk_enable(spdif->clk_spdif_out);
-
-	for (i = 0; i < ARRAY_SIZE(regs); i++) {
-		u32 val = tegra_spdif_read(spdif, regs[i].offset);
-		seq_printf(s, "%s = %08x\n", regs[i].name, val);
-	}
-
-	clk_disable(spdif->clk_spdif_out);
-
-	return 0;
-}
-
-static int tegra_spdif_debug_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, tegra_spdif_show, inode->i_private);
-}
-
-static const struct file_operations tegra_spdif_debug_fops = {
-	.open    = tegra_spdif_debug_open,
-	.read    = seq_read,
-	.llseek  = seq_lseek,
-	.release = single_release,
-};
-
-static void tegra_spdif_debug_add(struct tegra_spdif *spdif)
-{
-	spdif->debug = debugfs_create_file(DRV_NAME, S_IRUGO,
-						snd_soc_debugfs_root, spdif,
-						&tegra_spdif_debug_fops);
-}
-
-static void tegra_spdif_debug_remove(struct tegra_spdif *spdif)
-{
-	if (spdif->debug)
-		debugfs_remove(spdif->debug);
-}
-#else
-static inline void tegra_spdif_debug_add(struct tegra_spdif *spdif)
-{
-}
-
-static inline void tegra_spdif_debug_remove(struct tegra_spdif *spdif)
-{
-}
-#endif
-
-static int tegra_spdif_hw_params(struct snd_pcm_substream *substream,
-				struct snd_pcm_hw_params *params,
-				struct snd_soc_dai *dai)
-{
-	struct device *dev = substream->pcm->card->dev;
-	struct tegra_spdif *spdif = snd_soc_dai_get_drvdata(dai);
-	int ret, spdifclock;
-
-	spdif->reg_ctrl &= ~TEGRA_SPDIF_CTRL_PACK;
-	spdif->reg_ctrl &= ~TEGRA_SPDIF_CTRL_BIT_MODE_MASK;
-	switch (params_format(params)) {
-	case SNDRV_PCM_FORMAT_S16_LE:
-		spdif->reg_ctrl |= TEGRA_SPDIF_CTRL_PACK;
-		spdif->reg_ctrl |= TEGRA_SPDIF_CTRL_BIT_MODE_16BIT;
-		break;
-	default:
-		return -EINVAL;
-	}
-
-	switch (params_rate(params)) {
-	case 32000:
-		spdifclock = 4096000;
-		break;
-	case 44100:
-		spdifclock = 5644800;
-		break;
-	case 48000:
-		spdifclock = 6144000;
-		break;
-	case 88200:
-		spdifclock = 11289600;
-		break;
-	case 96000:
-		spdifclock = 12288000;
-		break;
-	case 176400:
-		spdifclock = 22579200;
-		break;
-	case 192000:
-		spdifclock = 24576000;
-		break;
-	default:
-		return -EINVAL;
-	}
-
-	ret = clk_set_rate(spdif->clk_spdif_out, spdifclock);
-	if (ret) {
-		dev_err(dev, "Can't set SPDIF clock rate: %d\n", ret);
-		return ret;
-	}
-
-	return 0;
-}
-
-static void tegra_spdif_start_playback(struct tegra_spdif *spdif)
-{
-	spdif->reg_ctrl |= TEGRA_SPDIF_CTRL_TX_EN;
-	tegra_spdif_write(spdif, TEGRA_SPDIF_CTRL, spdif->reg_ctrl);
-}
-
-static void tegra_spdif_stop_playback(struct tegra_spdif *spdif)
-{
-	spdif->reg_ctrl &= ~TEGRA_SPDIF_CTRL_TX_EN;
-	tegra_spdif_write(spdif, TEGRA_SPDIF_CTRL, spdif->reg_ctrl);
-}
-
-static int tegra_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
-				struct snd_soc_dai *dai)
-{
-	struct tegra_spdif *spdif = snd_soc_dai_get_drvdata(dai);
-
-	switch (cmd) {
-	case SNDRV_PCM_TRIGGER_START:
-	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-	case SNDRV_PCM_TRIGGER_RESUME:
-		if (!spdif->clk_refs)
-			clk_enable(spdif->clk_spdif_out);
-		spdif->clk_refs++;
-		tegra_spdif_start_playback(spdif);
-		break;
-	case SNDRV_PCM_TRIGGER_STOP:
-	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-	case SNDRV_PCM_TRIGGER_SUSPEND:
-		tegra_spdif_stop_playback(spdif);
-		spdif->clk_refs--;
-		if (!spdif->clk_refs)
-			clk_disable(spdif->clk_spdif_out);
-		break;
-	default:
-		return -EINVAL;
-	}
-
-	return 0;
-}
-
-static int tegra_spdif_probe(struct snd_soc_dai *dai)
-{
-	struct tegra_spdif *spdif = snd_soc_dai_get_drvdata(dai);
-
-	dai->capture_dma_data = NULL;
-	dai->playback_dma_data = &spdif->playback_dma_data;
-
-	return 0;
-}
-
-static const struct snd_soc_dai_ops tegra_spdif_dai_ops = {
-	.hw_params	= tegra_spdif_hw_params,
-	.trigger	= tegra_spdif_trigger,
-};
-
-static struct snd_soc_dai_driver tegra_spdif_dai = {
-	.name = DRV_NAME,
-	.probe = tegra_spdif_probe,
-	.playback = {
-		.channels_min = 2,
-		.channels_max = 2,
-		.rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
-				SNDRV_PCM_RATE_48000,
-		.formats = SNDRV_PCM_FMTBIT_S16_LE,
-	},
-	.ops = &tegra_spdif_dai_ops,
-};
-
-static __devinit int tegra_spdif_platform_probe(struct platform_device *pdev)
-{
-	struct tegra_spdif *spdif;
-	struct resource *mem, *memregion, *dmareq;
-	int ret;
-
-	spdif = kzalloc(sizeof(struct tegra_spdif), GFP_KERNEL);
-	if (!spdif) {
-		dev_err(&pdev->dev, "Can't allocate tegra_spdif\n");
-		ret = -ENOMEM;
-		goto exit;
-	}
-	dev_set_drvdata(&pdev->dev, spdif);
-
-	spdif->clk_spdif_out = clk_get(&pdev->dev, "spdif_out");
-	if (IS_ERR(spdif->clk_spdif_out)) {
-		pr_err("Can't retrieve spdif clock\n");
-		ret = PTR_ERR(spdif->clk_spdif_out);
-		goto err_free;
-	}
-
-	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (!mem) {
-		dev_err(&pdev->dev, "No memory resource\n");
-		ret = -ENODEV;
-		goto err_clk_put;
-	}
-
-	dmareq = platform_get_resource(pdev, IORESOURCE_DMA, 0);
-	if (!dmareq) {
-		dev_err(&pdev->dev, "No DMA resource\n");
-		ret = -ENODEV;
-		goto err_clk_put;
-	}
-
-	memregion = request_mem_region(mem->start, resource_size(mem),
-					DRV_NAME);
-	if (!memregion) {
-		dev_err(&pdev->dev, "Memory region already claimed\n");
-		ret = -EBUSY;
-		goto err_clk_put;
-	}
-
-	spdif->regs = ioremap(mem->start, resource_size(mem));
-	if (!spdif->regs) {
-		dev_err(&pdev->dev, "ioremap failed\n");
-		ret = -ENOMEM;
-		goto err_release;
-	}
-
-	spdif->playback_dma_data.addr = mem->start + TEGRA_SPDIF_DATA_OUT;
-	spdif->playback_dma_data.wrap = 4;
-	spdif->playback_dma_data.width = 32;
-	spdif->playback_dma_data.req_sel = dmareq->start;
-
-	ret = snd_soc_register_dai(&pdev->dev, &tegra_spdif_dai);
-	if (ret) {
-		dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
-		ret = -ENOMEM;
-		goto err_unmap;
-	}
-
-	tegra_spdif_debug_add(spdif);
-
-	return 0;
-
-err_unmap:
-	iounmap(spdif->regs);
-err_release:
-	release_mem_region(mem->start, resource_size(mem));
-err_clk_put:
-	clk_put(spdif->clk_spdif_out);
-err_free:
-	kfree(spdif);
-exit:
-	return ret;
-}
-
-static int __devexit tegra_spdif_platform_remove(struct platform_device *pdev)
-{
-	struct tegra_spdif *spdif = dev_get_drvdata(&pdev->dev);
-	struct resource *res;
-
-	snd_soc_unregister_dai(&pdev->dev);
-
-	tegra_spdif_debug_remove(spdif);
-
-	iounmap(spdif->regs);
-
-	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	release_mem_region(res->start, resource_size(res));
-
-	clk_put(spdif->clk_spdif_out);
-
-	kfree(spdif);
-
-	return 0;
-}
-
-static struct platform_driver tegra_spdif_driver = {
-	.driver = {
-		.name = DRV_NAME,
-		.owner = THIS_MODULE,
-	},
-	.probe = tegra_spdif_platform_probe,
-	.remove = __devexit_p(tegra_spdif_platform_remove),
-};
-
-module_platform_driver(tegra_spdif_driver);
-
-MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
-MODULE_DESCRIPTION("Tegra SPDIF ASoC driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/tegra/tegra_spdif.h b/sound/soc/tegra/tegra_spdif.h
deleted file mode 100644
index 2e03db4..0000000
--- a/sound/soc/tegra/tegra_spdif.h
+++ /dev/null
@@ -1,473 +0,0 @@
-/*
- * tegra_spdif.h - Definitions for Tegra SPDIF driver
- *
- * Author: Stephen Warren <swarren@nvidia.com>
- * Copyright (C) 2011 - NVIDIA, Inc.
- *
- * Based on code copyright/by:
- * Copyright (c) 2008-2009, NVIDIA Corporation
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#ifndef __TEGRA_SPDIF_H__
-#define __TEGRA_SPDIF_H__
-
-#include "tegra_pcm.h"
-
-/* Offsets from TEGRA_SPDIF_BASE */
-
-#define TEGRA_SPDIF_CTRL					0x0
-#define TEGRA_SPDIF_STATUS					0x4
-#define TEGRA_SPDIF_STROBE_CTRL					0x8
-#define TEGRA_SPDIF_DATA_FIFO_CSR				0x0C
-#define TEGRA_SPDIF_DATA_OUT					0x40
-#define TEGRA_SPDIF_DATA_IN					0x80
-#define TEGRA_SPDIF_CH_STA_RX_A					0x100
-#define TEGRA_SPDIF_CH_STA_RX_B					0x104
-#define TEGRA_SPDIF_CH_STA_RX_C					0x108
-#define TEGRA_SPDIF_CH_STA_RX_D					0x10C
-#define TEGRA_SPDIF_CH_STA_RX_E					0x110
-#define TEGRA_SPDIF_CH_STA_RX_F					0x114
-#define TEGRA_SPDIF_CH_STA_TX_A					0x140
-#define TEGRA_SPDIF_CH_STA_TX_B					0x144
-#define TEGRA_SPDIF_CH_STA_TX_C					0x148
-#define TEGRA_SPDIF_CH_STA_TX_D					0x14C
-#define TEGRA_SPDIF_CH_STA_TX_E					0x150
-#define TEGRA_SPDIF_CH_STA_TX_F					0x154
-#define TEGRA_SPDIF_USR_STA_RX_A				0x180
-#define TEGRA_SPDIF_USR_DAT_TX_A				0x1C0
-
-/* Fields in TEGRA_SPDIF_CTRL */
-
-/* Start capturing from 0=right, 1=left channel */
-#define TEGRA_SPDIF_CTRL_CAP_LC					(1 << 30)
-
-/* SPDIF receiver(RX) enable */
-#define TEGRA_SPDIF_CTRL_RX_EN					(1 << 29)
-
-/* SPDIF Transmitter(TX) enable */
-#define TEGRA_SPDIF_CTRL_TX_EN					(1 << 28)
-
-/* Transmit Channel status */
-#define TEGRA_SPDIF_CTRL_TC_EN					(1 << 27)
-
-/* Transmit user Data */
-#define TEGRA_SPDIF_CTRL_TU_EN					(1 << 26)
-
-/* Interrupt on transmit error */
-#define TEGRA_SPDIF_CTRL_IE_TXE					(1 << 25)
-
-/* Interrupt on receive error */
-#define TEGRA_SPDIF_CTRL_IE_RXE					(1 << 24)
-
-/* Interrupt on invalid preamble */
-#define TEGRA_SPDIF_CTRL_IE_P					(1 << 23)
-
-/* Interrupt on "B" preamble */
-#define TEGRA_SPDIF_CTRL_IE_B					(1 << 22)
-
-/* Interrupt when block of channel status received */
-#define TEGRA_SPDIF_CTRL_IE_C					(1 << 21)
-
-/* Interrupt when a valid information unit (IU) is received */
-#define TEGRA_SPDIF_CTRL_IE_U					(1 << 20)
-
-/* Interrupt when RX user FIFO attention level is reached */
-#define TEGRA_SPDIF_CTRL_QE_RU					(1 << 19)
-
-/* Interrupt when TX user FIFO attention level is reached */
-#define TEGRA_SPDIF_CTRL_QE_TU					(1 << 18)
-
-/* Interrupt when RX data FIFO attention level is reached */
-#define TEGRA_SPDIF_CTRL_QE_RX					(1 << 17)
-
-/* Interrupt when TX data FIFO attention level is reached */
-#define TEGRA_SPDIF_CTRL_QE_TX					(1 << 16)
-
-/* Loopback test mode enable */
-#define TEGRA_SPDIF_CTRL_LBK_EN					(1 << 15)
-
-/*
- * Pack data mode:
- * 0 = Single data (16 bit needs to be  padded to match the
- *     interface data bit size).
- * 1 = Packeted left/right channel data into a single word.
- */
-#define TEGRA_SPDIF_CTRL_PACK					(1 << 14)
-
-/*
- * 00 = 16bit data
- * 01 = 20bit data
- * 10 = 24bit data
- * 11 = raw data
- */
-#define TEGRA_SPDIF_BIT_MODE_16BIT				0
-#define TEGRA_SPDIF_BIT_MODE_20BIT				1
-#define TEGRA_SPDIF_BIT_MODE_24BIT				2
-#define TEGRA_SPDIF_BIT_MODE_RAW				3
-
-#define TEGRA_SPDIF_CTRL_BIT_MODE_SHIFT				12
-#define TEGRA_SPDIF_CTRL_BIT_MODE_MASK				(3                          << TEGRA_SPDIF_CTRL_BIT_MODE_SHIFT)
-#define TEGRA_SPDIF_CTRL_BIT_MODE_16BIT				(TEGRA_SPDIF_BIT_MODE_16BIT << TEGRA_SPDIF_CTRL_BIT_MODE_SHIFT)
-#define TEGRA_SPDIF_CTRL_BIT_MODE_20BIT				(TEGRA_SPDIF_BIT_MODE_20BIT << TEGRA_SPDIF_CTRL_BIT_MODE_SHIFT)
-#define TEGRA_SPDIF_CTRL_BIT_MODE_24BIT				(TEGRA_SPDIF_BIT_MODE_24BIT << TEGRA_SPDIF_CTRL_BIT_MODE_SHIFT)
-#define TEGRA_SPDIF_CTRL_BIT_MODE_RAW				(TEGRA_SPDIF_BIT_MODE_RAW   << TEGRA_SPDIF_CTRL_BIT_MODE_SHIFT)
-
-/* Fields in TEGRA_SPDIF_STATUS */
-
-/*
- * Note: IS_P, IS_B, IS_C, and IS_U are sticky bits. Software must
- * write a 1 to the corresponding bit location to clear the status.
- */
-
-/*
- * Receiver(RX) shifter is busy receiving data.
- * This bit is asserted when the receiver first locked onto the
- * preamble of the data stream after RX_EN is asserted. This bit is
- * deasserted when either,
- * (a) the end of a frame is reached after RX_EN is deeasserted, or
- * (b) the SPDIF data stream becomes inactive.
- */
-#define TEGRA_SPDIF_STATUS_RX_BSY				(1 << 29)
-
-/*
- * Transmitter(TX) shifter is busy transmitting data.
- * This bit is asserted when TX_EN is asserted.
- * This bit is deasserted when the end of a frame is reached after
- * TX_EN is deasserted.
- */
-#define TEGRA_SPDIF_STATUS_TX_BSY				(1 << 28)
-
-/*
- * TX is busy shifting out channel status.
- * This bit is asserted when both TX_EN and TC_EN are asserted and
- * data from CH_STA_TX_A register is loaded into the internal shifter.
- * This bit is deasserted when either,
- * (a) the end of a frame is reached after TX_EN is deasserted, or
- * (b) CH_STA_TX_F register is loaded into the internal shifter.
- */
-#define TEGRA_SPDIF_STATUS_TC_BSY				(1 << 27)
-
-/*
- * TX User data FIFO busy.
- * This bit is asserted when TX_EN and TXU_EN are asserted and
- * there's data in the TX user FIFO.  This bit is deassert when either,
- * (a) the end of a frame is reached after TX_EN is deasserted, or
- * (b) there's no data left in the TX user FIFO.
- */
-#define TEGRA_SPDIF_STATUS_TU_BSY				(1 << 26)
-
-/* TX FIFO Underrun error status */
-#define TEGRA_SPDIF_STATUS_TX_ERR				(1 << 25)
-
-/* RX FIFO Overrun error status */
-#define TEGRA_SPDIF_STATUS_RX_ERR				(1 << 24)
-
-/* Preamble status: 0=Preamble OK, 1=bad/missing preamble */
-#define TEGRA_SPDIF_STATUS_IS_P					(1 << 23)
-
-/* B-preamble detection status: 0=not detected, 1=B-preamble detected */
-#define TEGRA_SPDIF_STATUS_IS_B					(1 << 22)
-
-/*
- * RX channel block data receive status:
- * 0=entire block not recieved yet.
- * 1=received entire block of channel status,
- */
-#define TEGRA_SPDIF_STATUS_IS_C					(1 << 21)
-
-/* RX User Data Valid flag:  1=valid IU detected, 0 = no IU detected. */
-#define TEGRA_SPDIF_STATUS_IS_U					(1 << 20)
-
-/*
- * RX User FIFO Status:
- * 1=attention level reached, 0=attention level not reached.
- */
-#define TEGRA_SPDIF_STATUS_QS_RU				(1 << 19)
-
-/*
- * TX User FIFO Status:
- * 1=attention level reached, 0=attention level not reached.
- */
-#define TEGRA_SPDIF_STATUS_QS_TU				(1 << 18)
-
-/*
- * RX Data FIFO Status:
- * 1=attention level reached, 0=attention level not reached.
- */
-#define TEGRA_SPDIF_STATUS_QS_RX				(1 << 17)
-
-/*
- * TX Data FIFO Status:
- * 1=attention level reached, 0=attention level not reached.
- */
-#define TEGRA_SPDIF_STATUS_QS_TX				(1 << 16)
-
-/* Fields in TEGRA_SPDIF_STROBE_CTRL */
-
-/*
- * Indicates the approximate number of detected SPDIFIN clocks within a
- * bi-phase period.
- */
-#define TEGRA_SPDIF_STROBE_CTRL_PERIOD_SHIFT			16
-#define TEGRA_SPDIF_STROBE_CTRL_PERIOD_MASK			(0xff << TEGRA_SPDIF_STROBE_CTRL_PERIOD_SHIFT)
-
-/* Data strobe mode: 0=Auto-locked 1=Manual locked */
-#define TEGRA_SPDIF_STROBE_CTRL_STROBE				(1 << 15)
-
-/*
- * Manual data strobe time within the bi-phase clock period (in terms of
- * the number of over-sampling clocks).
- */
-#define TEGRA_SPDIF_STROBE_CTRL_DATA_STROBES_SHIFT		8
-#define TEGRA_SPDIF_STROBE_CTRL_DATA_STROBES_MASK		(0x1f << TEGRA_SPDIF_STROBE_CTRL_DATA_STROBES_SHIFT)
-
-/*
- * Manual SPDIFIN bi-phase clock period (in terms of the number of
- * over-sampling clocks).
- */
-#define TEGRA_SPDIF_STROBE_CTRL_CLOCK_PERIOD_SHIFT		0
-#define TEGRA_SPDIF_STROBE_CTRL_CLOCK_PERIOD_MASK		(0x3f << TEGRA_SPDIF_STROBE_CTRL_CLOCK_PERIOD_SHIFT)
-
-/* Fields in SPDIF_DATA_FIFO_CSR */
-
-/* Clear Receiver User FIFO (RX USR.FIFO) */
-#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_CLR			(1 << 31)
-
-#define TEGRA_SPDIF_FIFO_ATN_LVL_U_ONE_SLOT			0
-#define TEGRA_SPDIF_FIFO_ATN_LVL_U_TWO_SLOTS			1
-#define TEGRA_SPDIF_FIFO_ATN_LVL_U_THREE_SLOTS			2
-#define TEGRA_SPDIF_FIFO_ATN_LVL_U_FOUR_SLOTS			3
-
-/* RU FIFO attention level */
-#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT		29
-#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_MASK		\
-		(0x3                                    << TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT)
-#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_RU1_WORD_FULL	\
-		(TEGRA_SPDIF_FIFO_ATN_LVL_U_ONE_SLOT    << TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT)
-#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_RU2_WORD_FULL	\
-		(TEGRA_SPDIF_FIFO_ATN_LVL_U_TWO_SLOTS   << TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT)
-#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_RU3_WORD_FULL	\
-		(TEGRA_SPDIF_FIFO_ATN_LVL_U_THREE_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT)
-#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_RU4_WORD_FULL	\
-		(TEGRA_SPDIF_FIFO_ATN_LVL_U_FOUR_SLOTS  << TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT)
-
-/* Number of RX USR.FIFO levels with valid data. */
-#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_FULL_COUNT_SHIFT		24
-#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_FULL_COUNT_MASK		(0x1f << TEGRA_SPDIF_DATA_FIFO_CSR_RU_FULL_COUNT_SHIFT)
-
-/* Clear Transmitter User FIFO (TX USR.FIFO) */
-#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_CLR			(1 << 23)
-
-/* TU FIFO attention level */
-#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT		21
-#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_MASK		\
-		(0x3                                   << TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT)
-#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_TU1_WORD_FULL	\
-		(TEGRA_SPDIF_FIFO_ATN_LVL_U_ONE_SLOT    << TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT)
-#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_TU2_WORD_FULL	\
-		(TEGRA_SPDIF_FIFO_ATN_LVL_U_TWO_SLOTS   << TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT)
-#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_TU3_WORD_FULL	\
-		(TEGRA_SPDIF_FIFO_ATN_LVL_U_THREE_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT)
-#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_TU4_WORD_FULL	\
-		(TEGRA_SPDIF_FIFO_ATN_LVL_U_FOUR_SLOTS  << TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT)
-
-/* Number of TX USR.FIFO levels that could be filled. */
-#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_EMPTY_COUNT_SHIFT		16
-#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_EMPTY_COUNT_MASK		(0x1f << SPDIF_DATA_FIFO_CSR_TU_EMPTY_COUNT_SHIFT)
-
-/* Clear Receiver Data FIFO (RX DATA.FIFO) */
-#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_CLR			(1 << 15)
-
-#define TEGRA_SPDIF_FIFO_ATN_LVL_D_ONE_SLOT			0
-#define TEGRA_SPDIF_FIFO_ATN_LVL_D_FOUR_SLOTS			1
-#define TEGRA_SPDIF_FIFO_ATN_LVL_D_EIGHT_SLOTS			2
-#define TEGRA_SPDIF_FIFO_ATN_LVL_D_TWELVE_SLOTS			3
-
-/* RU FIFO attention level */
-#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT		13
-#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_MASK		\
-		(0x3                                     << TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT)
-#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_RU1_WORD_FULL	\
-		(TEGRA_SPDIF_FIFO_ATN_LVL_D_ONE_SLOT     << TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT)
-#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_RU4_WORD_FULL	\
-		(TEGRA_SPDIF_FIFO_ATN_LVL_D_FOUR_SLOTS   << TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT)
-#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_RU8_WORD_FULL	\
-		(TEGRA_SPDIF_FIFO_ATN_LVL_D_EIGHT_SLOTS  << TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT)
-#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_RU12_WORD_FULL	\
-		(TEGRA_SPDIF_FIFO_ATN_LVL_D_TWELVE_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT)
-
-/* Number of RX DATA.FIFO levels with valid data. */
-#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_FULL_COUNT_SHIFT		8
-#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_FULL_COUNT_MASK		(0x1f << TEGRA_SPDIF_DATA_FIFO_CSR_RX_FULL_COUNT_SHIFT)
-
-/* Clear Transmitter Data FIFO (TX DATA.FIFO) */
-#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_CLR			(1 << 7)
-
-/* TU FIFO attention level */
-#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT		5
-#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_MASK		\
-		(0x3                                     << TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT)
-#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU1_WORD_FULL	\
-		(TEGRA_SPDIF_FIFO_ATN_LVL_D_ONE_SLOT     << TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT)
-#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU4_WORD_FULL	\
-		(TEGRA_SPDIF_FIFO_ATN_LVL_D_FOUR_SLOTS   << TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT)
-#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU8_WORD_FULL	\
-		(TEGRA_SPDIF_FIFO_ATN_LVL_D_EIGHT_SLOTS  << TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT)
-#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU12_WORD_FULL	\
-		(TEGRA_SPDIF_FIFO_ATN_LVL_D_TWELVE_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT)
-
-/* Number of TX DATA.FIFO levels that could be filled. */
-#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_EMPTY_COUNT_SHIFT		0
-#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_EMPTY_COUNT_MASK		(0x1f << SPDIF_DATA_FIFO_CSR_TX_EMPTY_COUNT_SHIFT)
-
-/* Fields in TEGRA_SPDIF_DATA_OUT */
-
-/*
- * This register has 5 different formats:
- * 16-bit        (BIT_MODE=00, PACK=0)
- * 20-bit        (BIT_MODE=01, PACK=0)
- * 24-bit        (BIT_MODE=10, PACK=0)
- * raw           (BIT_MODE=11, PACK=0)
- * 16-bit packed (BIT_MODE=00, PACK=1)
- */
-
-#define TEGRA_SPDIF_DATA_OUT_DATA_16_SHIFT			0
-#define TEGRA_SPDIF_DATA_OUT_DATA_16_MASK			(0xffff << TEGRA_SPDIF_DATA_OUT_DATA_16_SHIFT)
-
-#define TEGRA_SPDIF_DATA_OUT_DATA_20_SHIFT			0
-#define TEGRA_SPDIF_DATA_OUT_DATA_20_MASK			(0xfffff << TEGRA_SPDIF_DATA_OUT_DATA_20_SHIFT)
-
-#define TEGRA_SPDIF_DATA_OUT_DATA_24_SHIFT			0
-#define TEGRA_SPDIF_DATA_OUT_DATA_24_MASK			(0xffffff << TEGRA_SPDIF_DATA_OUT_DATA_24_SHIFT)
-
-#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_P				(1 << 31)
-#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_C				(1 << 30)
-#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_U				(1 << 29)
-#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_V				(1 << 28)
-
-#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_DATA_SHIFT		8
-#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_DATA_MASK			(0xfffff << TEGRA_SPDIF_DATA_OUT_DATA_RAW_DATA_SHIFT)
-
-#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_AUX_SHIFT			4
-#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_AUX_MASK			(0xf << TEGRA_SPDIF_DATA_OUT_DATA_RAW_AUX_SHIFT)
-
-#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_PREAMBLE_SHIFT		0
-#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_PREAMBLE_MASK		(0xf << TEGRA_SPDIF_DATA_OUT_DATA_RAW_PREAMBLE_SHIFT)
-
-#define TEGRA_SPDIF_DATA_OUT_DATA_16_PACKED_RIGHT_SHIFT		16
-#define TEGRA_SPDIF_DATA_OUT_DATA_16_PACKED_RIGHT_MASK		(0xffff << TEGRA_SPDIF_DATA_OUT_DATA_16_PACKED_RIGHT_SHIFT)
-
-#define TEGRA_SPDIF_DATA_OUT_DATA_16_PACKED_LEFT_SHIFT		0
-#define TEGRA_SPDIF_DATA_OUT_DATA_16_PACKED_LEFT_MASK		(0xffff << TEGRA_SPDIF_DATA_OUT_DATA_16_PACKED_LEFT_SHIFT)
-
-/* Fields in TEGRA_SPDIF_DATA_IN */
-
-/*
- * This register has 5 different formats:
- * 16-bit        (BIT_MODE=00, PACK=0)
- * 20-bit        (BIT_MODE=01, PACK=0)
- * 24-bit        (BIT_MODE=10, PACK=0)
- * raw           (BIT_MODE=11, PACK=0)
- * 16-bit packed (BIT_MODE=00, PACK=1)
- *
- * Bits 31:24 are common to all modes except 16-bit packed
- */
-
-#define TEGRA_SPDIF_DATA_IN_DATA_P				(1 << 31)
-#define TEGRA_SPDIF_DATA_IN_DATA_C				(1 << 30)
-#define TEGRA_SPDIF_DATA_IN_DATA_U				(1 << 29)
-#define TEGRA_SPDIF_DATA_IN_DATA_V				(1 << 28)
-
-#define TEGRA_SPDIF_DATA_IN_DATA_PREAMBLE_SHIFT			24
-#define TEGRA_SPDIF_DATA_IN_DATA_PREAMBLE_MASK			(0xf << TEGRA_SPDIF_DATA_IN_DATA_PREAMBLE_SHIFT)
-
-#define TEGRA_SPDIF_DATA_IN_DATA_16_SHIFT			0
-#define TEGRA_SPDIF_DATA_IN_DATA_16_MASK			(0xffff << TEGRA_SPDIF_DATA_IN_DATA_16_SHIFT)
-
-#define TEGRA_SPDIF_DATA_IN_DATA_20_SHIFT			0
-#define TEGRA_SPDIF_DATA_IN_DATA_20_MASK			(0xfffff << TEGRA_SPDIF_DATA_IN_DATA_20_SHIFT)
-
-#define TEGRA_SPDIF_DATA_IN_DATA_24_SHIFT			0
-#define TEGRA_SPDIF_DATA_IN_DATA_24_MASK			(0xffffff << TEGRA_SPDIF_DATA_IN_DATA_24_SHIFT)
-
-#define TEGRA_SPDIF_DATA_IN_DATA_RAW_DATA_SHIFT			8
-#define TEGRA_SPDIF_DATA_IN_DATA_RAW_DATA_MASK			(0xfffff << TEGRA_SPDIF_DATA_IN_DATA_RAW_DATA_SHIFT)
-
-#define TEGRA_SPDIF_DATA_IN_DATA_RAW_AUX_SHIFT			4
-#define TEGRA_SPDIF_DATA_IN_DATA_RAW_AUX_MASK			(0xf << TEGRA_SPDIF_DATA_IN_DATA_RAW_AUX_SHIFT)
-
-#define TEGRA_SPDIF_DATA_IN_DATA_RAW_PREAMBLE_SHIFT		0
-#define TEGRA_SPDIF_DATA_IN_DATA_RAW_PREAMBLE_MASK		(0xf << TEGRA_SPDIF_DATA_IN_DATA_RAW_PREAMBLE_SHIFT)
-
-#define TEGRA_SPDIF_DATA_IN_DATA_16_PACKED_RIGHT_SHIFT		16
-#define TEGRA_SPDIF_DATA_IN_DATA_16_PACKED_RIGHT_MASK		(0xffff << TEGRA_SPDIF_DATA_IN_DATA_16_PACKED_RIGHT_SHIFT)
-
-#define TEGRA_SPDIF_DATA_IN_DATA_16_PACKED_LEFT_SHIFT		0
-#define TEGRA_SPDIF_DATA_IN_DATA_16_PACKED_LEFT_MASK		(0xffff << TEGRA_SPDIF_DATA_IN_DATA_16_PACKED_LEFT_SHIFT)
-
-/* Fields in TEGRA_SPDIF_CH_STA_RX_A */
-/* Fields in TEGRA_SPDIF_CH_STA_RX_B */
-/* Fields in TEGRA_SPDIF_CH_STA_RX_C */
-/* Fields in TEGRA_SPDIF_CH_STA_RX_D */
-/* Fields in TEGRA_SPDIF_CH_STA_RX_E */
-/* Fields in TEGRA_SPDIF_CH_STA_RX_F */
-
-/*
- * The 6-word receive channel data page buffer holds a block (192 frames) of
- * channel status information. The order of receive is from LSB to MSB
- * bit, and from CH_STA_RX_A to CH_STA_RX_F then back to CH_STA_RX_A.
- */
-
-/* Fields in TEGRA_SPDIF_CH_STA_TX_A */
-/* Fields in TEGRA_SPDIF_CH_STA_TX_B */
-/* Fields in TEGRA_SPDIF_CH_STA_TX_C */
-/* Fields in TEGRA_SPDIF_CH_STA_TX_D */
-/* Fields in TEGRA_SPDIF_CH_STA_TX_E */
-/* Fields in TEGRA_SPDIF_CH_STA_TX_F */
-
-/*
- * The 6-word transmit channel data page buffer holds a block (192 frames) of
- * channel status information. The order of transmission is from LSB to MSB
- * bit, and from CH_STA_TX_A to CH_STA_TX_F then back to CH_STA_TX_A.
- */
-
-/* Fields in TEGRA_SPDIF_USR_STA_RX_A */
-
-/*
- * This 4-word deep FIFO receives user FIFO field information. The order of
- * receive is from LSB to MSB bit.
- */
-
-/* Fields in TEGRA_SPDIF_USR_DAT_TX_A */
-
-/*
- * This 4-word deep FIFO transmits user FIFO field information. The order of
- * transmission is from LSB to MSB bit.
- */
-
-struct tegra_spdif {
-	struct clk *clk_spdif_out;
-	int clk_refs;
-	struct tegra_pcm_dma_params capture_dma_data;
-	struct tegra_pcm_dma_params playback_dma_data;
-	void __iomem *regs;
-	struct dentry *debug;
-	u32 reg_ctrl;
-};
-
-#endif
diff --git a/sound/soc/tegra/tegra_wm8753.c b/sound/soc/tegra/tegra_wm8753.c
new file mode 100644
index 0000000..4e77026
--- /dev/null
+++ b/sound/soc/tegra/tegra_wm8753.c
@@ -0,0 +1,224 @@
+/*
+ * tegra_wm8753.c - Tegra machine ASoC driver for boards using WM8753 codec.
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 2010-2012 - NVIDIA, Inc.
+ *
+ * Based on code copyright/by:
+ *
+ * (c) 2009, 2010 Nvidia Graphics Pvt. Ltd.
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC.
+ * Author: Graeme Gregory
+ *         graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <asm/mach-types.h>
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "../codecs/wm8753.h"
+
+#include "tegra_asoc_utils.h"
+
+#define DRV_NAME "tegra-snd-wm8753"
+
+struct tegra_wm8753 {
+	struct tegra_asoc_utils_data util_data;
+};
+
+static int tegra_wm8753_hw_params(struct snd_pcm_substream *substream,
+					struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_card *card = codec->card;
+	struct tegra_wm8753 *machine = snd_soc_card_get_drvdata(card);
+	int srate, mclk;
+	int err;
+
+	srate = params_rate(params);
+	switch (srate) {
+	case 11025:
+	case 22050:
+	case 44100:
+	case 88200:
+		mclk = 11289600;
+		break;
+	default:
+		mclk = 12288000;
+		break;
+	}
+
+	err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk);
+	if (err < 0) {
+		dev_err(card->dev, "Can't configure clocks\n");
+		return err;
+	}
+
+	err = snd_soc_dai_set_sysclk(codec_dai, WM8753_MCLK, mclk,
+					SND_SOC_CLOCK_IN);
+	if (err < 0) {
+		dev_err(card->dev, "codec_dai clock not set\n");
+		return err;
+	}
+
+	return 0;
+}
+
+static struct snd_soc_ops tegra_wm8753_ops = {
+	.hw_params = tegra_wm8753_hw_params,
+};
+
+static const struct snd_soc_dapm_widget tegra_wm8753_dapm_widgets[] = {
+	SND_SOC_DAPM_HP("Headphone Jack", NULL),
+	SND_SOC_DAPM_MIC("Mic Jack", NULL),
+};
+
+static struct snd_soc_dai_link tegra_wm8753_dai = {
+	.name = "WM8753",
+	.stream_name = "WM8753 PCM",
+	.codec_dai_name = "wm8753-hifi",
+	.ops = &tegra_wm8753_ops,
+	.dai_fmt = SND_SOC_DAIFMT_I2S |
+			SND_SOC_DAIFMT_NB_NF |
+			SND_SOC_DAIFMT_CBS_CFS,
+};
+
+static struct snd_soc_card snd_soc_tegra_wm8753 = {
+	.name = "tegra-wm8753",
+	.owner = THIS_MODULE,
+	.dai_link = &tegra_wm8753_dai,
+	.num_links = 1,
+
+	.dapm_widgets = tegra_wm8753_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(tegra_wm8753_dapm_widgets),
+	.fully_routed = true,
+};
+
+static __devinit int tegra_wm8753_driver_probe(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = &snd_soc_tegra_wm8753;
+	struct tegra_wm8753 *machine;
+	int ret;
+
+	machine = devm_kzalloc(&pdev->dev, sizeof(struct tegra_wm8753),
+			       GFP_KERNEL);
+	if (!machine) {
+		dev_err(&pdev->dev, "Can't allocate tegra_wm8753 struct\n");
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	card->dev = &pdev->dev;
+	platform_set_drvdata(pdev, card);
+	snd_soc_card_set_drvdata(card, machine);
+
+	ret = snd_soc_of_parse_card_name(card, "nvidia,model");
+	if (ret)
+		goto err;
+
+	ret = snd_soc_of_parse_audio_routing(card, "nvidia,audio-routing");
+	if (ret)
+		goto err;
+
+	tegra_wm8753_dai.codec_of_node = of_parse_phandle(
+			pdev->dev.of_node, "nvidia,audio-codec", 0);
+	if (!tegra_wm8753_dai.codec_of_node) {
+		dev_err(&pdev->dev,
+			"Property 'nvidia,audio-codec' missing or invalid\n");
+		ret = -EINVAL;
+		goto err;
+	}
+
+	tegra_wm8753_dai.cpu_dai_of_node = of_parse_phandle(
+			pdev->dev.of_node, "nvidia,i2s-controller", 0);
+	if (!tegra_wm8753_dai.cpu_dai_of_node) {
+		dev_err(&pdev->dev,
+			"Property 'nvidia,i2s-controller' missing or invalid\n");
+		ret = -EINVAL;
+		goto err;
+	}
+
+	tegra_wm8753_dai.platform_of_node =
+				tegra_wm8753_dai.cpu_dai_of_node;
+
+	ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev);
+	if (ret)
+		goto err;
+
+	ret = snd_soc_register_card(card);
+	if (ret) {
+		dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
+			ret);
+		goto err_fini_utils;
+	}
+
+	return 0;
+
+err_fini_utils:
+	tegra_asoc_utils_fini(&machine->util_data);
+err:
+	return ret;
+}
+
+static int __devexit tegra_wm8753_driver_remove(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = platform_get_drvdata(pdev);
+	struct tegra_wm8753 *machine = snd_soc_card_get_drvdata(card);
+
+	snd_soc_unregister_card(card);
+
+	tegra_asoc_utils_fini(&machine->util_data);
+
+	return 0;
+}
+
+static const struct of_device_id tegra_wm8753_of_match[] __devinitconst = {
+	{ .compatible = "nvidia,tegra-audio-wm8753", },
+	{},
+};
+
+static struct platform_driver tegra_wm8753_driver = {
+	.driver = {
+		.name = DRV_NAME,
+		.owner = THIS_MODULE,
+		.pm = &snd_soc_pm_ops,
+		.of_match_table = tegra_wm8753_of_match,
+	},
+	.probe = tegra_wm8753_driver_probe,
+	.remove = __devexit_p(tegra_wm8753_driver_remove),
+};
+module_platform_driver(tegra_wm8753_driver);
+
+MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
+MODULE_DESCRIPTION("Tegra+WM8753 machine ASoC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, tegra_wm8753_of_match);
diff --git a/sound/soc/tegra/tegra_wm8903.c b/sound/soc/tegra/tegra_wm8903.c
index 566655e..0b0df49 100644
--- a/sound/soc/tegra/tegra_wm8903.c
+++ b/sound/soc/tegra/tegra_wm8903.c
@@ -2,7 +2,7 @@
  * tegra_wm8903.c - Tegra machine ASoC driver for boards using WM8903 codec.
  *
  * Author: Stephen Warren <swarren@nvidia.com>
- * Copyright (C) 2010-2011 - NVIDIA, Inc.
+ * Copyright (C) 2010-2012 - NVIDIA, Inc.
  *
  * Based on code copyright/by:
  *
@@ -46,9 +46,6 @@
 
 #include "../codecs/wm8903.h"
 
-#include "tegra_das.h"
-#include "tegra_i2s.h"
-#include "tegra_pcm.h"
 #include "tegra_asoc_utils.h"
 
 #define DRV_NAME "tegra-snd-wm8903"
@@ -61,7 +58,6 @@
 
 struct tegra_wm8903 {
 	struct tegra_wm8903_platform_data pdata;
-	struct platform_device *pcm_dev;
 	struct tegra_asoc_utils_data util_data;
 	int gpio_requested;
 };
@@ -354,8 +350,8 @@
 	.name = "WM8903",
 	.stream_name = "WM8903 PCM",
 	.codec_name = "wm8903.0-001a",
-	.platform_name = "tegra-pcm-audio",
-	.cpu_dai_name = "tegra-i2s.0",
+	.platform_name = "tegra20-i2s.0",
+	.cpu_dai_name = "tegra20-i2s.0",
 	.codec_dai_name = "wm8903-hifi",
 	.init = tegra_wm8903_init,
 	.ops = &tegra_wm8903_ops,
@@ -392,7 +388,6 @@
 		ret = -ENOMEM;
 		goto err;
 	}
-	machine->pcm_dev = ERR_PTR(-EINVAL);
 
 	card->dev = &pdev->dev;
 	platform_set_drvdata(pdev, card);
@@ -428,14 +423,9 @@
 			goto err;
 		}
 
-		machine->pcm_dev = platform_device_register_simple(
-					"tegra-pcm-audio", -1, NULL, 0);
-		if (IS_ERR(machine->pcm_dev)) {
-			dev_err(&pdev->dev,
-				"Can't instantiate tegra-pcm-audio\n");
-			ret = PTR_ERR(machine->pcm_dev);
-			goto err;
-		}
+		tegra_wm8903_dai.platform_name = NULL;
+		tegra_wm8903_dai.platform_of_node =
+					tegra_wm8903_dai.cpu_dai_of_node;
 	} else {
 		if (machine_is_harmony()) {
 			card->dapm_routes = harmony_audio_map;
@@ -454,7 +444,7 @@
 
 	ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev);
 	if (ret)
-		goto err_unregister;
+		goto err;
 
 	ret = snd_soc_register_card(card);
 	if (ret) {
@@ -467,9 +457,6 @@
 
 err_fini_utils:
 	tegra_asoc_utils_fini(&machine->util_data);
-err_unregister:
-	if (!IS_ERR(machine->pcm_dev))
-		platform_device_unregister(machine->pcm_dev);
 err:
 	return ret;
 }
@@ -497,8 +484,6 @@
 	snd_soc_unregister_card(card);
 
 	tegra_asoc_utils_fini(&machine->util_data);
-	if (!IS_ERR(machine->pcm_dev))
-		platform_device_unregister(machine->pcm_dev);
 
 	return 0;
 }
diff --git a/sound/soc/tegra/trimslice.c b/sound/soc/tegra/trimslice.c
index 2bdfc550..4a8d5b6 100644
--- a/sound/soc/tegra/trimslice.c
+++ b/sound/soc/tegra/trimslice.c
@@ -27,6 +27,7 @@
 #include <asm/mach-types.h>
 
 #include <linux/module.h>
+#include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 
@@ -38,9 +39,6 @@
 
 #include "../codecs/tlv320aic23.h"
 
-#include "tegra_das.h"
-#include "tegra_i2s.h"
-#include "tegra_pcm.h"
 #include "tegra_asoc_utils.h"
 
 #define DRV_NAME "tegra-snd-trimslice"
@@ -119,8 +117,8 @@
 	.name = "TLV320AIC23",
 	.stream_name = "AIC23",
 	.codec_name = "tlv320aic23-codec.2-001a",
-	.platform_name = "tegra-pcm-audio",
-	.cpu_dai_name = "tegra-i2s.0",
+	.platform_name = "tegra20-i2s.0",
+	.cpu_dai_name = "tegra20-i2s.0",
 	.codec_dai_name = "tlv320aic23-hifi",
 	.ops = &trimslice_asoc_ops,
 };
@@ -152,6 +150,32 @@
 		goto err;
 	}
 
+	if (pdev->dev.of_node) {
+		trimslice_tlv320aic23_dai.codec_name = NULL;
+		trimslice_tlv320aic23_dai.codec_of_node = of_parse_phandle(
+				pdev->dev.of_node, "nvidia,audio-codec", 0);
+		if (!trimslice_tlv320aic23_dai.codec_of_node) {
+			dev_err(&pdev->dev,
+				"Property 'nvidia,audio-codec' missing or invalid\n");
+			ret = -EINVAL;
+			goto err;
+		}
+
+		trimslice_tlv320aic23_dai.cpu_dai_name = NULL;
+		trimslice_tlv320aic23_dai.cpu_dai_of_node = of_parse_phandle(
+				pdev->dev.of_node, "nvidia,i2s-controller", 0);
+		if (!trimslice_tlv320aic23_dai.cpu_dai_of_node) {
+			dev_err(&pdev->dev,
+				"Property 'nvidia,i2s-controller' missing or invalid\n");
+			ret = -EINVAL;
+			goto err;
+		}
+
+		trimslice_tlv320aic23_dai.platform_name = NULL;
+		trimslice_tlv320aic23_dai.platform_of_node =
+				trimslice_tlv320aic23_dai.cpu_dai_of_node;
+	}
+
 	ret = tegra_asoc_utils_init(&trimslice->util_data, &pdev->dev);
 	if (ret)
 		goto err;
@@ -187,10 +211,17 @@
 	return 0;
 }
 
+static const struct of_device_id trimslice_of_match[] __devinitconst = {
+	{ .compatible = "nvidia,tegra-audio-trimslice", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, trimslice_of_match);
+
 static struct platform_driver tegra_snd_trimslice_driver = {
 	.driver = {
 		.name = DRV_NAME,
 		.owner = THIS_MODULE,
+		.of_match_table = trimslice_of_match,
 	},
 	.probe = tegra_snd_trimslice_probe,
 	.remove = __devexit_p(tegra_snd_trimslice_remove),
diff --git a/sound/soc/ux500/Kconfig b/sound/soc/ux500/Kconfig
new file mode 100644
index 0000000..44cf434
--- /dev/null
+++ b/sound/soc/ux500/Kconfig
@@ -0,0 +1,14 @@
+#
+# Ux500 SoC audio configuration
+#
+menuconfig SND_SOC_UX500
+	tristate "SoC Audio support for Ux500 platform"
+	depends on SND_SOC
+	depends on MFD_DB8500_PRCMU
+	help
+		Say Y if you want to enable ASoC-support for
+		any of the Ux500 platforms (e.g. U8500).
+
+config SND_SOC_UX500_PLAT_MSP_I2S
+	tristate
+	depends on SND_SOC_UX500
diff --git a/sound/soc/ux500/Makefile b/sound/soc/ux500/Makefile
new file mode 100644
index 0000000..19974c5
--- /dev/null
+++ b/sound/soc/ux500/Makefile
@@ -0,0 +1,4 @@
+# Ux500 Platform Support
+
+snd-soc-ux500-plat-msp-i2s-objs := ux500_msp_dai.o ux500_msp_i2s.o
+obj-$(CONFIG_SND_SOC_UX500_PLAT_MSP_I2S) += snd-soc-ux500-plat-msp-i2s.o
diff --git a/sound/soc/ux500/ux500_msp_dai.c b/sound/soc/ux500/ux500_msp_dai.c
new file mode 100644
index 0000000..93c6c40
--- /dev/null
+++ b/sound/soc/ux500/ux500_msp_dai.c
@@ -0,0 +1,843 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2012
+ *
+ * Author: Ola Lilja <ola.o.lilja@stericsson.com>,
+ *         Roger Nilsson <roger.xr.nilsson@stericsson.com>
+ *         for ST-Ericsson.
+ *
+ * License terms:
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/bitops.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mfd/dbx500-prcmu.h>
+
+#include <mach/hardware.h>
+#include <mach/board-mop500-msp.h>
+
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include "ux500_msp_i2s.h"
+#include "ux500_msp_dai.h"
+
+static int setup_pcm_multichan(struct snd_soc_dai *dai,
+			struct ux500_msp_config *msp_config)
+{
+	struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
+	struct msp_multichannel_config *multi =
+					&msp_config->multichannel_config;
+
+	if (drvdata->slots > 1) {
+		msp_config->multichannel_configured = 1;
+
+		multi->tx_multichannel_enable = true;
+		multi->rx_multichannel_enable = true;
+		multi->rx_comparison_enable_mode = MSP_COMPARISON_DISABLED;
+
+		multi->tx_channel_0_enable = drvdata->tx_mask;
+		multi->tx_channel_1_enable = 0;
+		multi->tx_channel_2_enable = 0;
+		multi->tx_channel_3_enable = 0;
+
+		multi->rx_channel_0_enable = drvdata->rx_mask;
+		multi->rx_channel_1_enable = 0;
+		multi->rx_channel_2_enable = 0;
+		multi->rx_channel_3_enable = 0;
+
+		dev_dbg(dai->dev,
+			"%s: Multichannel enabled. Slots: %d, TX: %u, RX: %u\n",
+			__func__, drvdata->slots, multi->tx_channel_0_enable,
+			multi->rx_channel_0_enable);
+	}
+
+	return 0;
+}
+
+static int setup_frameper(struct snd_soc_dai *dai, unsigned int rate,
+			struct msp_protdesc *prot_desc)
+{
+	struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
+
+	switch (drvdata->slots) {
+	case 1:
+		switch (rate) {
+		case 8000:
+			prot_desc->frame_period =
+				FRAME_PER_SINGLE_SLOT_8_KHZ;
+			break;
+
+		case 16000:
+			prot_desc->frame_period =
+				FRAME_PER_SINGLE_SLOT_16_KHZ;
+			break;
+
+		case 44100:
+			prot_desc->frame_period =
+				FRAME_PER_SINGLE_SLOT_44_1_KHZ;
+			break;
+
+		case 48000:
+			prot_desc->frame_period =
+				FRAME_PER_SINGLE_SLOT_48_KHZ;
+			break;
+
+		default:
+			dev_err(dai->dev,
+				"%s: Error: Unsupported sample-rate (freq = %d)!\n",
+				__func__, rate);
+			return -EINVAL;
+		}
+		break;
+
+	case 2:
+		prot_desc->frame_period = FRAME_PER_2_SLOTS;
+		break;
+
+	case 8:
+		prot_desc->frame_period = FRAME_PER_8_SLOTS;
+		break;
+
+	case 16:
+		prot_desc->frame_period = FRAME_PER_16_SLOTS;
+		break;
+	default:
+		dev_err(dai->dev,
+			"%s: Error: Unsupported slot-count (slots = %d)!\n",
+			__func__, drvdata->slots);
+		return -EINVAL;
+	}
+
+	prot_desc->clocks_per_frame =
+			prot_desc->frame_period+1;
+
+	dev_dbg(dai->dev, "%s: Clocks per frame: %u\n",
+		__func__,
+		prot_desc->clocks_per_frame);
+
+	return 0;
+}
+
+static int setup_pcm_framing(struct snd_soc_dai *dai, unsigned int rate,
+			struct msp_protdesc *prot_desc)
+{
+	struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
+
+	u32 frame_length = MSP_FRAME_LEN_1;
+	prot_desc->frame_width = 0;
+
+	switch (drvdata->slots) {
+	case 1:
+		frame_length = MSP_FRAME_LEN_1;
+		break;
+
+	case 2:
+		frame_length = MSP_FRAME_LEN_2;
+		break;
+
+	case 8:
+		frame_length = MSP_FRAME_LEN_8;
+		break;
+
+	case 16:
+		frame_length = MSP_FRAME_LEN_16;
+		break;
+	default:
+		dev_err(dai->dev,
+			"%s: Error: Unsupported slot-count (slots = %d)!\n",
+			__func__, drvdata->slots);
+		return -EINVAL;
+	}
+
+	prot_desc->tx_frame_len_1 = frame_length;
+	prot_desc->rx_frame_len_1 = frame_length;
+	prot_desc->tx_frame_len_2 = frame_length;
+	prot_desc->rx_frame_len_2 = frame_length;
+
+	prot_desc->tx_elem_len_1 = MSP_ELEM_LEN_16;
+	prot_desc->rx_elem_len_1 = MSP_ELEM_LEN_16;
+	prot_desc->tx_elem_len_2 = MSP_ELEM_LEN_16;
+	prot_desc->rx_elem_len_2 = MSP_ELEM_LEN_16;
+
+	return setup_frameper(dai, rate, prot_desc);
+}
+
+static int setup_clocking(struct snd_soc_dai *dai,
+			unsigned int fmt,
+			struct ux500_msp_config *msp_config)
+{
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		break;
+
+	case SND_SOC_DAIFMT_NB_IF:
+		msp_config->tx_fsync_pol ^= 1 << TFSPOL_SHIFT;
+		msp_config->rx_fsync_pol ^= 1 << RFSPOL_SHIFT;
+
+		break;
+
+	default:
+		dev_err(dai->dev,
+			"%s: Error: Unsopported inversion (fmt = 0x%x)!\n",
+			__func__, fmt);
+
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		dev_dbg(dai->dev, "%s: Codec is master.\n", __func__);
+
+		msp_config->iodelay = 0x20;
+		msp_config->rx_fsync_sel = 0;
+		msp_config->tx_fsync_sel = 1 << TFSSEL_SHIFT;
+		msp_config->tx_clk_sel = 0;
+		msp_config->rx_clk_sel = 0;
+		msp_config->srg_clk_sel = 0x2 << SCKSEL_SHIFT;
+
+		break;
+
+	case SND_SOC_DAIFMT_CBS_CFS:
+		dev_dbg(dai->dev, "%s: Codec is slave.\n", __func__);
+
+		msp_config->tx_clk_sel = TX_CLK_SEL_SRG;
+		msp_config->tx_fsync_sel = TX_SYNC_SRG_PROG;
+		msp_config->rx_clk_sel = RX_CLK_SEL_SRG;
+		msp_config->rx_fsync_sel = RX_SYNC_SRG;
+		msp_config->srg_clk_sel = 1 << SCKSEL_SHIFT;
+
+		break;
+
+	default:
+		dev_err(dai->dev, "%s: Error: Unsopported master (fmt = 0x%x)!\n",
+			__func__, fmt);
+
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int setup_pcm_protdesc(struct snd_soc_dai *dai,
+				unsigned int fmt,
+				struct msp_protdesc *prot_desc)
+{
+	prot_desc->rx_phase_mode = MSP_SINGLE_PHASE;
+	prot_desc->tx_phase_mode = MSP_SINGLE_PHASE;
+	prot_desc->rx_phase2_start_mode = MSP_PHASE2_START_MODE_IMEDIATE;
+	prot_desc->tx_phase2_start_mode = MSP_PHASE2_START_MODE_IMEDIATE;
+	prot_desc->rx_byte_order = MSP_BTF_MS_BIT_FIRST;
+	prot_desc->tx_byte_order = MSP_BTF_MS_BIT_FIRST;
+	prot_desc->tx_fsync_pol = MSP_FSYNC_POL(MSP_FSYNC_POL_ACT_HI);
+	prot_desc->rx_fsync_pol = MSP_FSYNC_POL_ACT_HI << RFSPOL_SHIFT;
+
+	if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_DSP_A) {
+		dev_dbg(dai->dev, "%s: DSP_A.\n", __func__);
+		prot_desc->rx_clk_pol = MSP_RISING_EDGE;
+		prot_desc->tx_clk_pol = MSP_FALLING_EDGE;
+
+		prot_desc->rx_data_delay = MSP_DELAY_1;
+		prot_desc->tx_data_delay = MSP_DELAY_1;
+	} else {
+		dev_dbg(dai->dev, "%s: DSP_B.\n", __func__);
+		prot_desc->rx_clk_pol = MSP_FALLING_EDGE;
+		prot_desc->tx_clk_pol = MSP_RISING_EDGE;
+
+		prot_desc->rx_data_delay = MSP_DELAY_0;
+		prot_desc->tx_data_delay = MSP_DELAY_0;
+	}
+
+	prot_desc->rx_half_word_swap = MSP_SWAP_NONE;
+	prot_desc->tx_half_word_swap = MSP_SWAP_NONE;
+	prot_desc->compression_mode = MSP_COMPRESS_MODE_LINEAR;
+	prot_desc->expansion_mode = MSP_EXPAND_MODE_LINEAR;
+	prot_desc->frame_sync_ignore = MSP_FSYNC_IGNORE;
+
+	return 0;
+}
+
+static int setup_i2s_protdesc(struct msp_protdesc *prot_desc)
+{
+	prot_desc->rx_phase_mode = MSP_DUAL_PHASE;
+	prot_desc->tx_phase_mode = MSP_DUAL_PHASE;
+	prot_desc->rx_phase2_start_mode = MSP_PHASE2_START_MODE_FSYNC;
+	prot_desc->tx_phase2_start_mode = MSP_PHASE2_START_MODE_FSYNC;
+	prot_desc->rx_byte_order = MSP_BTF_MS_BIT_FIRST;
+	prot_desc->tx_byte_order = MSP_BTF_MS_BIT_FIRST;
+	prot_desc->tx_fsync_pol = MSP_FSYNC_POL(MSP_FSYNC_POL_ACT_LO);
+	prot_desc->rx_fsync_pol = MSP_FSYNC_POL_ACT_LO << RFSPOL_SHIFT;
+
+	prot_desc->rx_frame_len_1 = MSP_FRAME_LEN_1;
+	prot_desc->rx_frame_len_2 = MSP_FRAME_LEN_1;
+	prot_desc->tx_frame_len_1 = MSP_FRAME_LEN_1;
+	prot_desc->tx_frame_len_2 = MSP_FRAME_LEN_1;
+	prot_desc->rx_elem_len_1 = MSP_ELEM_LEN_16;
+	prot_desc->rx_elem_len_2 = MSP_ELEM_LEN_16;
+	prot_desc->tx_elem_len_1 = MSP_ELEM_LEN_16;
+	prot_desc->tx_elem_len_2 = MSP_ELEM_LEN_16;
+
+	prot_desc->rx_clk_pol = MSP_RISING_EDGE;
+	prot_desc->tx_clk_pol = MSP_FALLING_EDGE;
+
+	prot_desc->rx_data_delay = MSP_DELAY_0;
+	prot_desc->tx_data_delay = MSP_DELAY_0;
+
+	prot_desc->tx_half_word_swap = MSP_SWAP_NONE;
+	prot_desc->rx_half_word_swap = MSP_SWAP_NONE;
+	prot_desc->compression_mode = MSP_COMPRESS_MODE_LINEAR;
+	prot_desc->expansion_mode = MSP_EXPAND_MODE_LINEAR;
+	prot_desc->frame_sync_ignore = MSP_FSYNC_IGNORE;
+
+	return 0;
+}
+
+static int setup_msp_config(struct snd_pcm_substream *substream,
+			struct snd_soc_dai *dai,
+			struct ux500_msp_config *msp_config)
+{
+	struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
+	struct msp_protdesc *prot_desc = &msp_config->protdesc;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	unsigned int fmt = drvdata->fmt;
+	int ret;
+
+	memset(msp_config, 0, sizeof(*msp_config));
+
+	msp_config->f_inputclk = drvdata->master_clk;
+
+	msp_config->tx_fifo_config = TX_FIFO_ENABLE;
+	msp_config->rx_fifo_config = RX_FIFO_ENABLE;
+	msp_config->def_elem_len = 1;
+	msp_config->direction = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+				MSP_DIR_TX : MSP_DIR_RX;
+	msp_config->data_size = MSP_DATA_BITS_32;
+	msp_config->frame_freq = runtime->rate;
+
+	dev_dbg(dai->dev, "%s: f_inputclk = %u, frame_freq = %u.\n",
+	       __func__, msp_config->f_inputclk, msp_config->frame_freq);
+	/* To avoid division by zero */
+	prot_desc->clocks_per_frame = 1;
+
+	dev_dbg(dai->dev, "%s: rate: %u, channels: %d.\n", __func__,
+		runtime->rate, runtime->channels);
+	switch (fmt &
+		(SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_MASTER_MASK)) {
+	case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS:
+		dev_dbg(dai->dev, "%s: SND_SOC_DAIFMT_I2S.\n", __func__);
+
+		msp_config->default_protdesc = 1;
+		msp_config->protocol = MSP_I2S_PROTOCOL;
+		break;
+
+	case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM:
+		dev_dbg(dai->dev, "%s: SND_SOC_DAIFMT_I2S.\n", __func__);
+
+		msp_config->data_size = MSP_DATA_BITS_16;
+		msp_config->protocol = MSP_I2S_PROTOCOL;
+
+		ret = setup_i2s_protdesc(prot_desc);
+		if (ret < 0)
+			return ret;
+
+		break;
+
+	case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS:
+	case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM:
+	case SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBS_CFS:
+	case SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM:
+		dev_dbg(dai->dev, "%s: PCM format.\n", __func__);
+
+		msp_config->data_size = MSP_DATA_BITS_16;
+		msp_config->protocol = MSP_PCM_PROTOCOL;
+
+		ret = setup_pcm_protdesc(dai, fmt, prot_desc);
+		if (ret < 0)
+			return ret;
+
+		ret = setup_pcm_multichan(dai, msp_config);
+		if (ret < 0)
+			return ret;
+
+		ret = setup_pcm_framing(dai, runtime->rate, prot_desc);
+		if (ret < 0)
+			return ret;
+
+		break;
+
+	default:
+		dev_err(dai->dev, "%s: Error: Unsopported format (%d)!\n",
+			__func__, fmt);
+		return -EINVAL;
+	}
+
+	return setup_clocking(dai, fmt, msp_config);
+}
+
+static int ux500_msp_dai_startup(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	int ret = 0;
+	struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
+
+	dev_dbg(dai->dev, "%s: MSP %d (%s): Enter.\n", __func__, dai->id,
+		snd_pcm_stream_str(substream));
+
+	/* Enable regulator */
+	ret = regulator_enable(drvdata->reg_vape);
+	if (ret != 0) {
+		dev_err(drvdata->msp->dev,
+			"%s: Failed to enable regulator!\n", __func__);
+		return ret;
+	}
+
+	/* Enable clock */
+	dev_dbg(dai->dev, "%s: Enabling MSP-clock.\n", __func__);
+	clk_enable(drvdata->clk);
+
+	return 0;
+}
+
+static void ux500_msp_dai_shutdown(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	int ret;
+	struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
+	bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+
+	dev_dbg(dai->dev, "%s: MSP %d (%s): Enter.\n", __func__, dai->id,
+		snd_pcm_stream_str(substream));
+
+	if (drvdata->vape_opp_constraint == 1) {
+		prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP,
+					"ux500_msp_i2s", 50);
+		drvdata->vape_opp_constraint = 0;
+	}
+
+	if (ux500_msp_i2s_close(drvdata->msp,
+				is_playback ? MSP_DIR_TX : MSP_DIR_RX)) {
+		dev_err(dai->dev,
+			"%s: Error: MSP %d (%s): Unable to close i2s.\n",
+			__func__, dai->id, snd_pcm_stream_str(substream));
+	}
+
+	/* Disable clock */
+	clk_disable(drvdata->clk);
+
+	/* Disable regulator */
+	ret = regulator_disable(drvdata->reg_vape);
+	if (ret < 0)
+		dev_err(dai->dev,
+			"%s: ERROR: Failed to disable regulator (%d)!\n",
+			__func__, ret);
+}
+
+static int ux500_msp_dai_prepare(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	int ret = 0;
+	struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct ux500_msp_config msp_config;
+
+	dev_dbg(dai->dev, "%s: MSP %d (%s): Enter (rate = %d).\n", __func__,
+		dai->id, snd_pcm_stream_str(substream), runtime->rate);
+
+	setup_msp_config(substream, dai, &msp_config);
+
+	ret = ux500_msp_i2s_open(drvdata->msp, &msp_config);
+	if (ret < 0) {
+		dev_err(dai->dev, "%s: Error: msp_setup failed (ret = %d)!\n",
+			__func__, ret);
+		return ret;
+	}
+
+	/* Set OPP-level */
+	if ((drvdata->fmt & SND_SOC_DAIFMT_MASTER_MASK) &&
+		(drvdata->msp->f_bitclk > 19200000)) {
+		/* If the bit-clock is higher than 19.2MHz, Vape should be
+		 * run in 100% OPP. Only when bit-clock is used (MSP master) */
+		prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP,
+					"ux500-msp-i2s", 100);
+		drvdata->vape_opp_constraint = 1;
+	} else {
+		prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP,
+					"ux500-msp-i2s", 50);
+		drvdata->vape_opp_constraint = 0;
+	}
+
+	return ret;
+}
+
+static int ux500_msp_dai_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params,
+				struct snd_soc_dai *dai)
+{
+	unsigned int mask, slots_active;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
+
+	dev_dbg(dai->dev, "%s: MSP %d (%s): Enter.\n",
+			__func__, dai->id, snd_pcm_stream_str(substream));
+
+	switch (drvdata->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		snd_pcm_hw_constraint_minmax(runtime,
+				SNDRV_PCM_HW_PARAM_CHANNELS,
+				1, 2);
+		break;
+
+	case SND_SOC_DAIFMT_DSP_B:
+	case SND_SOC_DAIFMT_DSP_A:
+		mask = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+			drvdata->tx_mask :
+			drvdata->rx_mask;
+
+		slots_active = hweight32(mask);
+		dev_dbg(dai->dev, "TDM-slots active: %d", slots_active);
+
+		snd_pcm_hw_constraint_minmax(runtime,
+				SNDRV_PCM_HW_PARAM_CHANNELS,
+				slots_active, slots_active);
+		break;
+
+	default:
+		dev_err(dai->dev,
+			"%s: Error: Unsupported protocol (fmt = 0x%x)!\n",
+			__func__, drvdata->fmt);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int ux500_msp_dai_set_dai_fmt(struct snd_soc_dai *dai,
+				unsigned int fmt)
+{
+	struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
+
+	dev_dbg(dai->dev, "%s: MSP %d: Enter.\n", __func__, dai->id);
+
+	switch (fmt & (SND_SOC_DAIFMT_FORMAT_MASK |
+		SND_SOC_DAIFMT_MASTER_MASK)) {
+	case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS:
+	case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM:
+	case SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBS_CFS:
+	case SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM:
+	case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS:
+	case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM:
+		break;
+
+	default:
+		dev_err(dai->dev,
+			"%s: Error: Unsupported protocol/master (fmt = 0x%x)!\n",
+			__func__, drvdata->fmt);
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+	case SND_SOC_DAIFMT_NB_IF:
+	case SND_SOC_DAIFMT_IB_IF:
+		break;
+
+	default:
+		dev_err(dai->dev,
+			"%s: Error: Unsupported inversion (fmt = 0x%x)!\n",
+			__func__, drvdata->fmt);
+		return -EINVAL;
+	}
+
+	drvdata->fmt = fmt;
+	return 0;
+}
+
+static int ux500_msp_dai_set_tdm_slot(struct snd_soc_dai *dai,
+				unsigned int tx_mask,
+				unsigned int rx_mask,
+				int slots, int slot_width)
+{
+	struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
+	unsigned int cap;
+
+	switch (slots) {
+	case 1:
+		cap = 0x01;
+		break;
+	case 2:
+		cap = 0x03;
+		break;
+	case 8:
+		cap = 0xFF;
+		break;
+	case 16:
+		cap = 0xFFFF;
+		break;
+	default:
+		dev_err(dai->dev, "%s: Error: Unsupported slot-count (%d)!\n",
+			__func__, slots);
+		return -EINVAL;
+	}
+	drvdata->slots = slots;
+
+	if (!(slot_width == 16)) {
+		dev_err(dai->dev, "%s: Error: Unsupported slot-width (%d)!\n",
+			__func__, slot_width);
+		return -EINVAL;
+	}
+	drvdata->slot_width = slot_width;
+
+	drvdata->tx_mask = tx_mask & cap;
+	drvdata->rx_mask = rx_mask & cap;
+
+	return 0;
+}
+
+static int ux500_msp_dai_set_dai_sysclk(struct snd_soc_dai *dai,
+					int clk_id, unsigned int freq, int dir)
+{
+	struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
+
+	dev_dbg(dai->dev, "%s: MSP %d: Enter. clk-id: %d, freq: %u.\n",
+		__func__, dai->id, clk_id, freq);
+
+	switch (clk_id) {
+	case UX500_MSP_MASTER_CLOCK:
+		drvdata->master_clk = freq;
+		break;
+
+	default:
+		dev_err(dai->dev, "%s: MSP %d: Invalid clk-id (%d)!\n",
+			__func__, dai->id, clk_id);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int ux500_msp_dai_trigger(struct snd_pcm_substream *substream,
+				int cmd, struct snd_soc_dai *dai)
+{
+	int ret = 0;
+	struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
+
+	dev_dbg(dai->dev, "%s: MSP %d (%s): Enter (msp->id = %d, cmd = %d).\n",
+		__func__, dai->id, snd_pcm_stream_str(substream),
+		(int)drvdata->msp->id, cmd);
+
+	ret = ux500_msp_i2s_trigger(drvdata->msp, cmd, substream->stream);
+
+	return ret;
+}
+
+static int ux500_msp_dai_probe(struct snd_soc_dai *dai)
+{
+	struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
+
+	drvdata->playback_dma_data.dma_cfg = drvdata->msp->dma_cfg_tx;
+	drvdata->capture_dma_data.dma_cfg = drvdata->msp->dma_cfg_rx;
+
+	dai->playback_dma_data = &drvdata->playback_dma_data;
+	dai->capture_dma_data = &drvdata->capture_dma_data;
+
+	drvdata->playback_dma_data.data_size = drvdata->slot_width;
+	drvdata->capture_dma_data.data_size = drvdata->slot_width;
+
+	return 0;
+}
+
+static struct snd_soc_dai_ops ux500_msp_dai_ops[] = {
+	{
+		.set_sysclk = ux500_msp_dai_set_dai_sysclk,
+		.set_fmt = ux500_msp_dai_set_dai_fmt,
+		.set_tdm_slot = ux500_msp_dai_set_tdm_slot,
+		.startup = ux500_msp_dai_startup,
+		.shutdown = ux500_msp_dai_shutdown,
+		.prepare = ux500_msp_dai_prepare,
+		.trigger = ux500_msp_dai_trigger,
+		.hw_params = ux500_msp_dai_hw_params,
+	}
+};
+
+static struct snd_soc_dai_driver ux500_msp_dai_drv[UX500_NBR_OF_DAI] = {
+	{
+		.name = "ux500-msp-i2s.0",
+		.probe = ux500_msp_dai_probe,
+		.id = 0,
+		.suspend = NULL,
+		.resume = NULL,
+		.playback = {
+			.channels_min = UX500_MSP_MIN_CHANNELS,
+			.channels_max = UX500_MSP_MAX_CHANNELS,
+			.rates = UX500_I2S_RATES,
+			.formats = UX500_I2S_FORMATS,
+		},
+		.capture = {
+			.channels_min = UX500_MSP_MIN_CHANNELS,
+			.channels_max = UX500_MSP_MAX_CHANNELS,
+			.rates = UX500_I2S_RATES,
+			.formats = UX500_I2S_FORMATS,
+		},
+		.ops = ux500_msp_dai_ops,
+	},
+	{
+		.name = "ux500-msp-i2s.1",
+		.probe = ux500_msp_dai_probe,
+		.id = 1,
+		.suspend = NULL,
+		.resume = NULL,
+		.playback = {
+			.channels_min = UX500_MSP_MIN_CHANNELS,
+			.channels_max = UX500_MSP_MAX_CHANNELS,
+			.rates = UX500_I2S_RATES,
+			.formats = UX500_I2S_FORMATS,
+		},
+		.capture = {
+			.channels_min = UX500_MSP_MIN_CHANNELS,
+			.channels_max = UX500_MSP_MAX_CHANNELS,
+			.rates = UX500_I2S_RATES,
+			.formats = UX500_I2S_FORMATS,
+		},
+		.ops = ux500_msp_dai_ops,
+	},
+	{
+		.name = "ux500-msp-i2s.2",
+		.id = 2,
+		.probe = ux500_msp_dai_probe,
+		.suspend = NULL,
+		.resume = NULL,
+		.playback = {
+			.channels_min = UX500_MSP_MIN_CHANNELS,
+			.channels_max = UX500_MSP_MAX_CHANNELS,
+			.rates = UX500_I2S_RATES,
+			.formats = UX500_I2S_FORMATS,
+		},
+		.capture = {
+			.channels_min = UX500_MSP_MIN_CHANNELS,
+			.channels_max = UX500_MSP_MAX_CHANNELS,
+			.rates = UX500_I2S_RATES,
+			.formats = UX500_I2S_FORMATS,
+		},
+		.ops = ux500_msp_dai_ops,
+	},
+	{
+		.name = "ux500-msp-i2s.3",
+		.probe = ux500_msp_dai_probe,
+		.id = 3,
+		.suspend = NULL,
+		.resume = NULL,
+		.playback = {
+			.channels_min = UX500_MSP_MIN_CHANNELS,
+			.channels_max = UX500_MSP_MAX_CHANNELS,
+			.rates = UX500_I2S_RATES,
+			.formats = UX500_I2S_FORMATS,
+		},
+		.capture = {
+			.channels_min = UX500_MSP_MIN_CHANNELS,
+			.channels_max = UX500_MSP_MAX_CHANNELS,
+			.rates = UX500_I2S_RATES,
+			.formats = UX500_I2S_FORMATS,
+		},
+		.ops = ux500_msp_dai_ops,
+	},
+};
+
+static int __devinit ux500_msp_drv_probe(struct platform_device *pdev)
+{
+	struct ux500_msp_i2s_drvdata *drvdata;
+	int ret = 0;
+
+	dev_dbg(&pdev->dev, "%s: Enter (pdev->name = %s).\n", __func__,
+		pdev->name);
+
+	drvdata = devm_kzalloc(&pdev->dev,
+				sizeof(struct ux500_msp_i2s_drvdata),
+				GFP_KERNEL);
+	drvdata->fmt = 0;
+	drvdata->slots = 1;
+	drvdata->tx_mask = 0x01;
+	drvdata->rx_mask = 0x01;
+	drvdata->slot_width = 16;
+	drvdata->master_clk = MSP_INPUT_FREQ_APB;
+
+	drvdata->reg_vape = devm_regulator_get(&pdev->dev, "v-ape");
+	if (IS_ERR(drvdata->reg_vape)) {
+		ret = (int)PTR_ERR(drvdata->reg_vape);
+		dev_err(&pdev->dev,
+			"%s: ERROR: Failed to get Vape supply (%d)!\n",
+			__func__, ret);
+		return ret;
+	}
+	prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP, (char *)pdev->name, 50);
+
+	drvdata->clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(drvdata->clk)) {
+		ret = (int)PTR_ERR(drvdata->clk);
+		dev_err(&pdev->dev, "%s: ERROR: clk_get failed (%d)!\n",
+			__func__, ret);
+		goto err_clk;
+	}
+
+	ret = ux500_msp_i2s_init_msp(pdev, &drvdata->msp,
+				pdev->dev.platform_data);
+	if (!drvdata->msp) {
+		dev_err(&pdev->dev,
+			"%s: ERROR: Failed to init MSP-struct (%d)!",
+			__func__, ret);
+		goto err_init_msp;
+	}
+	dev_set_drvdata(&pdev->dev, drvdata);
+
+	ret = snd_soc_register_dai(&pdev->dev,
+				&ux500_msp_dai_drv[drvdata->msp->id]);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Error: %s: Failed to register MSP%d!\n",
+			__func__, drvdata->msp->id);
+		goto err_init_msp;
+	}
+
+	return 0;
+
+err_init_msp:
+	clk_put(drvdata->clk);
+
+err_clk:
+	devm_regulator_put(drvdata->reg_vape);
+
+	return ret;
+}
+
+static int __devexit ux500_msp_drv_remove(struct platform_device *pdev)
+{
+	struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(&pdev->dev);
+
+	snd_soc_unregister_dais(&pdev->dev, ARRAY_SIZE(ux500_msp_dai_drv));
+
+	devm_regulator_put(drvdata->reg_vape);
+	prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, "ux500_msp_i2s");
+
+	clk_put(drvdata->clk);
+
+	ux500_msp_i2s_cleanup_msp(pdev, drvdata->msp);
+
+	return 0;
+}
+
+static struct platform_driver msp_i2s_driver = {
+	.driver = {
+		.name = "ux500-msp-i2s",
+		.owner = THIS_MODULE,
+	},
+	.probe = ux500_msp_drv_probe,
+	.remove = ux500_msp_drv_remove,
+};
+module_platform_driver(msp_i2s_driver);
+
+MODULE_LICENSE("GPLv2");
diff --git a/sound/soc/ux500/ux500_msp_dai.h b/sound/soc/ux500/ux500_msp_dai.h
new file mode 100644
index 0000000..98202a3
--- /dev/null
+++ b/sound/soc/ux500/ux500_msp_dai.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2012
+ *
+ * Author: Ola Lilja <ola.o.lilja@stericsson.com>,
+ *         Roger Nilsson <roger.xr.nilsson@stericsson.com>
+ *         for ST-Ericsson.
+ *
+ * License terms:
+ *
+ * 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.
+ */
+
+#ifndef UX500_msp_dai_H
+#define UX500_msp_dai_H
+
+#include <linux/types.h>
+#include <linux/spinlock.h>
+
+#include "ux500_msp_i2s.h"
+
+#define UX500_NBR_OF_DAI	4
+
+#define UX500_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |	\
+			SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+
+#define UX500_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
+
+#define FRAME_PER_SINGLE_SLOT_8_KHZ		31
+#define FRAME_PER_SINGLE_SLOT_16_KHZ	124
+#define FRAME_PER_SINGLE_SLOT_44_1_KHZ	63
+#define FRAME_PER_SINGLE_SLOT_48_KHZ	49
+#define FRAME_PER_2_SLOTS				31
+#define FRAME_PER_8_SLOTS				138
+#define FRAME_PER_16_SLOTS				277
+
+#ifndef CONFIG_SND_SOC_UX500_AB5500
+#define UX500_MSP_INTERNAL_CLOCK_FREQ  40000000
+#define UX500_MSP1_INTERNAL_CLOCK_FREQ UX500_MSP_INTERNAL_CLOCK_FREQ
+#else
+#define UX500_MSP_INTERNAL_CLOCK_FREQ 13000000
+#define UX500_MSP1_INTERNAL_CLOCK_FREQ (UX500_MSP_INTERNAL_CLOCK_FREQ * 2)
+#endif
+
+#define UX500_MSP_MIN_CHANNELS		1
+#define UX500_MSP_MAX_CHANNELS		8
+
+#define PLAYBACK_CONFIGURED		1
+#define CAPTURE_CONFIGURED		2
+
+enum ux500_msp_clock_id {
+	UX500_MSP_MASTER_CLOCK,
+};
+
+struct ux500_msp_i2s_drvdata {
+	struct ux500_msp *msp;
+	struct regulator *reg_vape;
+	struct ux500_msp_dma_params playback_dma_data;
+	struct ux500_msp_dma_params capture_dma_data;
+	unsigned int fmt;
+	unsigned int tx_mask;
+	unsigned int rx_mask;
+	int slots;
+	int slot_width;
+	u8 configured;
+	int data_delay;
+
+	/* Clocks */
+	unsigned int master_clk;
+	struct clk *clk;
+
+	/* Regulators */
+	int vape_opp_constraint;
+};
+
+int ux500_msp_dai_set_data_delay(struct snd_soc_dai *dai, int delay);
+
+#endif
diff --git a/sound/soc/ux500/ux500_msp_i2s.c b/sound/soc/ux500/ux500_msp_i2s.c
new file mode 100644
index 0000000..496dec1
--- /dev/null
+++ b/sound/soc/ux500/ux500_msp_i2s.c
@@ -0,0 +1,742 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2012
+ *
+ * Author: Ola Lilja <ola.o.lilja@stericsson.com>,
+ *         Roger Nilsson <roger.xr.nilsson@stericsson.com>,
+ *         Sandeep Kaushik <sandeep.kaushik@st.com>
+ *         for ST-Ericsson.
+ *
+ * License terms:
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#include <mach/hardware.h>
+#include <mach/board-mop500-msp.h>
+
+#include <sound/soc.h>
+
+#include "ux500_msp_i2s.h"
+
+ /* Protocol desciptors */
+static const struct msp_protdesc prot_descs[] = {
+	{ /* I2S */
+		MSP_SINGLE_PHASE,
+		MSP_SINGLE_PHASE,
+		MSP_PHASE2_START_MODE_IMEDIATE,
+		MSP_PHASE2_START_MODE_IMEDIATE,
+		MSP_BTF_MS_BIT_FIRST,
+		MSP_BTF_MS_BIT_FIRST,
+		MSP_FRAME_LEN_1,
+		MSP_FRAME_LEN_1,
+		MSP_FRAME_LEN_1,
+		MSP_FRAME_LEN_1,
+		MSP_ELEM_LEN_32,
+		MSP_ELEM_LEN_32,
+		MSP_ELEM_LEN_32,
+		MSP_ELEM_LEN_32,
+		MSP_DELAY_1,
+		MSP_DELAY_1,
+		MSP_RISING_EDGE,
+		MSP_FALLING_EDGE,
+		MSP_FSYNC_POL_ACT_LO,
+		MSP_FSYNC_POL_ACT_LO,
+		MSP_SWAP_NONE,
+		MSP_SWAP_NONE,
+		MSP_COMPRESS_MODE_LINEAR,
+		MSP_EXPAND_MODE_LINEAR,
+		MSP_FSYNC_IGNORE,
+		31,
+		15,
+		32,
+	}, { /* PCM */
+		MSP_DUAL_PHASE,
+		MSP_DUAL_PHASE,
+		MSP_PHASE2_START_MODE_FSYNC,
+		MSP_PHASE2_START_MODE_FSYNC,
+		MSP_BTF_MS_BIT_FIRST,
+		MSP_BTF_MS_BIT_FIRST,
+		MSP_FRAME_LEN_1,
+		MSP_FRAME_LEN_1,
+		MSP_FRAME_LEN_1,
+		MSP_FRAME_LEN_1,
+		MSP_ELEM_LEN_16,
+		MSP_ELEM_LEN_16,
+		MSP_ELEM_LEN_16,
+		MSP_ELEM_LEN_16,
+		MSP_DELAY_0,
+		MSP_DELAY_0,
+		MSP_RISING_EDGE,
+		MSP_FALLING_EDGE,
+		MSP_FSYNC_POL_ACT_HI,
+		MSP_FSYNC_POL_ACT_HI,
+		MSP_SWAP_NONE,
+		MSP_SWAP_NONE,
+		MSP_COMPRESS_MODE_LINEAR,
+		MSP_EXPAND_MODE_LINEAR,
+		MSP_FSYNC_IGNORE,
+		255,
+		0,
+		256,
+	}, { /* Companded PCM */
+		MSP_SINGLE_PHASE,
+		MSP_SINGLE_PHASE,
+		MSP_PHASE2_START_MODE_FSYNC,
+		MSP_PHASE2_START_MODE_FSYNC,
+		MSP_BTF_MS_BIT_FIRST,
+		MSP_BTF_MS_BIT_FIRST,
+		MSP_FRAME_LEN_1,
+		MSP_FRAME_LEN_1,
+		MSP_FRAME_LEN_1,
+		MSP_FRAME_LEN_1,
+		MSP_ELEM_LEN_8,
+		MSP_ELEM_LEN_8,
+		MSP_ELEM_LEN_8,
+		MSP_ELEM_LEN_8,
+		MSP_DELAY_0,
+		MSP_DELAY_0,
+		MSP_RISING_EDGE,
+		MSP_RISING_EDGE,
+		MSP_FSYNC_POL_ACT_HI,
+		MSP_FSYNC_POL_ACT_HI,
+		MSP_SWAP_NONE,
+		MSP_SWAP_NONE,
+		MSP_COMPRESS_MODE_LINEAR,
+		MSP_EXPAND_MODE_LINEAR,
+		MSP_FSYNC_IGNORE,
+		255,
+		0,
+		256,
+	},
+};
+
+static void set_prot_desc_tx(struct ux500_msp *msp,
+			struct msp_protdesc *protdesc,
+			enum msp_data_size data_size)
+{
+	u32 temp_reg = 0;
+
+	temp_reg |= MSP_P2_ENABLE_BIT(protdesc->tx_phase_mode);
+	temp_reg |= MSP_P2_START_MODE_BIT(protdesc->tx_phase2_start_mode);
+	temp_reg |= MSP_P1_FRAME_LEN_BITS(protdesc->tx_frame_len_1);
+	temp_reg |= MSP_P2_FRAME_LEN_BITS(protdesc->tx_frame_len_2);
+	if (msp->def_elem_len) {
+		temp_reg |= MSP_P1_ELEM_LEN_BITS(protdesc->tx_elem_len_1);
+		temp_reg |= MSP_P2_ELEM_LEN_BITS(protdesc->tx_elem_len_2);
+	} else {
+		temp_reg |= MSP_P1_ELEM_LEN_BITS(data_size);
+		temp_reg |= MSP_P2_ELEM_LEN_BITS(data_size);
+	}
+	temp_reg |= MSP_DATA_DELAY_BITS(protdesc->tx_data_delay);
+	temp_reg |= MSP_SET_ENDIANNES_BIT(protdesc->tx_byte_order);
+	temp_reg |= MSP_FSYNC_POL(protdesc->tx_fsync_pol);
+	temp_reg |= MSP_DATA_WORD_SWAP(protdesc->tx_half_word_swap);
+	temp_reg |= MSP_SET_COMPANDING_MODE(protdesc->compression_mode);
+	temp_reg |= MSP_SET_FSYNC_IGNORE(protdesc->frame_sync_ignore);
+
+	writel(temp_reg, msp->registers + MSP_TCF);
+}
+
+static void set_prot_desc_rx(struct ux500_msp *msp,
+			struct msp_protdesc *protdesc,
+			enum msp_data_size data_size)
+{
+	u32 temp_reg = 0;
+
+	temp_reg |= MSP_P2_ENABLE_BIT(protdesc->rx_phase_mode);
+	temp_reg |= MSP_P2_START_MODE_BIT(protdesc->rx_phase2_start_mode);
+	temp_reg |= MSP_P1_FRAME_LEN_BITS(protdesc->rx_frame_len_1);
+	temp_reg |= MSP_P2_FRAME_LEN_BITS(protdesc->rx_frame_len_2);
+	if (msp->def_elem_len) {
+		temp_reg |= MSP_P1_ELEM_LEN_BITS(protdesc->rx_elem_len_1);
+		temp_reg |= MSP_P2_ELEM_LEN_BITS(protdesc->rx_elem_len_2);
+	} else {
+		temp_reg |= MSP_P1_ELEM_LEN_BITS(data_size);
+		temp_reg |= MSP_P2_ELEM_LEN_BITS(data_size);
+	}
+
+	temp_reg |= MSP_DATA_DELAY_BITS(protdesc->rx_data_delay);
+	temp_reg |= MSP_SET_ENDIANNES_BIT(protdesc->rx_byte_order);
+	temp_reg |= MSP_FSYNC_POL(protdesc->rx_fsync_pol);
+	temp_reg |= MSP_DATA_WORD_SWAP(protdesc->rx_half_word_swap);
+	temp_reg |= MSP_SET_COMPANDING_MODE(protdesc->expansion_mode);
+	temp_reg |= MSP_SET_FSYNC_IGNORE(protdesc->frame_sync_ignore);
+
+	writel(temp_reg, msp->registers + MSP_RCF);
+}
+
+static int configure_protocol(struct ux500_msp *msp,
+			struct ux500_msp_config *config)
+{
+	struct msp_protdesc *protdesc;
+	enum msp_data_size data_size;
+	u32 temp_reg = 0;
+
+	data_size = config->data_size;
+	msp->def_elem_len = config->def_elem_len;
+	if (config->default_protdesc == 1) {
+		if (config->protocol >= MSP_INVALID_PROTOCOL) {
+			dev_err(msp->dev, "%s: ERROR: Invalid protocol!\n",
+				__func__);
+			return -EINVAL;
+		}
+		protdesc =
+		    (struct msp_protdesc *)&prot_descs[config->protocol];
+	} else {
+		protdesc = (struct msp_protdesc *)&config->protdesc;
+	}
+
+	if (data_size < MSP_DATA_BITS_DEFAULT || data_size > MSP_DATA_BITS_32) {
+		dev_err(msp->dev,
+			"%s: ERROR: Invalid data-size requested (data_size = %d)!\n",
+			__func__, data_size);
+		return -EINVAL;
+	}
+
+	if (config->direction & MSP_DIR_TX)
+		set_prot_desc_tx(msp, protdesc, data_size);
+	if (config->direction & MSP_DIR_RX)
+		set_prot_desc_rx(msp, protdesc, data_size);
+
+	/* The code below should not be separated. */
+	temp_reg = readl(msp->registers + MSP_GCR) & ~TX_CLK_POL_RISING;
+	temp_reg |= MSP_TX_CLKPOL_BIT(~protdesc->tx_clk_pol);
+	writel(temp_reg, msp->registers + MSP_GCR);
+	temp_reg = readl(msp->registers + MSP_GCR) & ~RX_CLK_POL_RISING;
+	temp_reg |= MSP_RX_CLKPOL_BIT(protdesc->rx_clk_pol);
+	writel(temp_reg, msp->registers + MSP_GCR);
+
+	return 0;
+}
+
+static int setup_bitclk(struct ux500_msp *msp, struct ux500_msp_config *config)
+{
+	u32 reg_val_GCR;
+	u32 frame_per = 0;
+	u32 sck_div = 0;
+	u32 frame_width = 0;
+	u32 temp_reg = 0;
+	struct msp_protdesc *protdesc = NULL;
+
+	reg_val_GCR = readl(msp->registers + MSP_GCR);
+	writel(reg_val_GCR & ~SRG_ENABLE, msp->registers + MSP_GCR);
+
+	if (config->default_protdesc)
+		protdesc =
+			(struct msp_protdesc *)&prot_descs[config->protocol];
+	else
+		protdesc = (struct msp_protdesc *)&config->protdesc;
+
+	switch (config->protocol) {
+	case MSP_PCM_PROTOCOL:
+	case MSP_PCM_COMPAND_PROTOCOL:
+		frame_width = protdesc->frame_width;
+		sck_div = config->f_inputclk / (config->frame_freq *
+			(protdesc->clocks_per_frame));
+		frame_per = protdesc->frame_period;
+		break;
+	case MSP_I2S_PROTOCOL:
+		frame_width = protdesc->frame_width;
+		sck_div = config->f_inputclk / (config->frame_freq *
+			(protdesc->clocks_per_frame));
+		frame_per = protdesc->frame_period;
+		break;
+	default:
+		dev_err(msp->dev, "%s: ERROR: Unknown protocol (%d)!\n",
+			__func__,
+			config->protocol);
+		return -EINVAL;
+	}
+
+	temp_reg = (sck_div - 1) & SCK_DIV_MASK;
+	temp_reg |= FRAME_WIDTH_BITS(frame_width);
+	temp_reg |= FRAME_PERIOD_BITS(frame_per);
+	writel(temp_reg, msp->registers + MSP_SRG);
+
+	msp->f_bitclk = (config->f_inputclk)/(sck_div + 1);
+
+	/* Enable bit-clock */
+	udelay(100);
+	reg_val_GCR = readl(msp->registers + MSP_GCR);
+	writel(reg_val_GCR | SRG_ENABLE, msp->registers + MSP_GCR);
+	udelay(100);
+
+	return 0;
+}
+
+static int configure_multichannel(struct ux500_msp *msp,
+				struct ux500_msp_config *config)
+{
+	struct msp_protdesc *protdesc;
+	struct msp_multichannel_config *mcfg;
+	u32 reg_val_MCR;
+
+	if (config->default_protdesc == 1) {
+		if (config->protocol >= MSP_INVALID_PROTOCOL) {
+			dev_err(msp->dev,
+				"%s: ERROR: Invalid protocol (%d)!\n",
+				__func__, config->protocol);
+			return -EINVAL;
+		}
+		protdesc = (struct msp_protdesc *)
+				&prot_descs[config->protocol];
+	} else {
+		protdesc = (struct msp_protdesc *)&config->protdesc;
+	}
+
+	mcfg = &config->multichannel_config;
+	if (mcfg->tx_multichannel_enable) {
+		if (protdesc->tx_phase_mode == MSP_SINGLE_PHASE) {
+			reg_val_MCR = readl(msp->registers + MSP_MCR);
+			writel(reg_val_MCR | (mcfg->tx_multichannel_enable ?
+						1 << TMCEN_BIT : 0),
+				msp->registers + MSP_MCR);
+			writel(mcfg->tx_channel_0_enable,
+				msp->registers + MSP_TCE0);
+			writel(mcfg->tx_channel_1_enable,
+				msp->registers + MSP_TCE1);
+			writel(mcfg->tx_channel_2_enable,
+				msp->registers + MSP_TCE2);
+			writel(mcfg->tx_channel_3_enable,
+				msp->registers + MSP_TCE3);
+		} else {
+			dev_err(msp->dev,
+				"%s: ERROR: Only single-phase supported (TX-mode: %d)!\n",
+				__func__, protdesc->tx_phase_mode);
+			return -EINVAL;
+		}
+	}
+	if (mcfg->rx_multichannel_enable) {
+		if (protdesc->rx_phase_mode == MSP_SINGLE_PHASE) {
+			reg_val_MCR = readl(msp->registers + MSP_MCR);
+			writel(reg_val_MCR | (mcfg->rx_multichannel_enable ?
+						1 << RMCEN_BIT : 0),
+				msp->registers + MSP_MCR);
+			writel(mcfg->rx_channel_0_enable,
+					msp->registers + MSP_RCE0);
+			writel(mcfg->rx_channel_1_enable,
+					msp->registers + MSP_RCE1);
+			writel(mcfg->rx_channel_2_enable,
+					msp->registers + MSP_RCE2);
+			writel(mcfg->rx_channel_3_enable,
+					msp->registers + MSP_RCE3);
+		} else {
+			dev_err(msp->dev,
+				"%s: ERROR: Only single-phase supported (RX-mode: %d)!\n",
+				__func__, protdesc->rx_phase_mode);
+			return -EINVAL;
+		}
+		if (mcfg->rx_comparison_enable_mode) {
+			reg_val_MCR = readl(msp->registers + MSP_MCR);
+			writel(reg_val_MCR |
+				(mcfg->rx_comparison_enable_mode << RCMPM_BIT),
+				msp->registers + MSP_MCR);
+
+			writel(mcfg->comparison_mask,
+					msp->registers + MSP_RCM);
+			writel(mcfg->comparison_value,
+					msp->registers + MSP_RCV);
+
+		}
+	}
+
+	return 0;
+}
+
+static int enable_msp(struct ux500_msp *msp, struct ux500_msp_config *config)
+{
+	int status = 0;
+	u32 reg_val_DMACR, reg_val_GCR;
+
+	/* Check msp state whether in RUN or CONFIGURED Mode */
+	if ((msp->msp_state == MSP_STATE_IDLE) && (msp->plat_init)) {
+		status = msp->plat_init();
+		if (status) {
+			dev_err(msp->dev, "%s: ERROR: Failed to init MSP (%d)!\n",
+				__func__, status);
+			return status;
+		}
+	}
+
+	/* Configure msp with protocol dependent settings */
+	configure_protocol(msp, config);
+	setup_bitclk(msp, config);
+	if (config->multichannel_configured == 1) {
+		status = configure_multichannel(msp, config);
+		if (status)
+			dev_warn(msp->dev,
+				"%s: WARN: configure_multichannel failed (%d)!\n",
+				__func__, status);
+	}
+
+	/* Make sure the correct DMA-directions are configured */
+	if ((config->direction & MSP_DIR_RX) && (!msp->dma_cfg_rx)) {
+		dev_err(msp->dev, "%s: ERROR: MSP RX-mode is not configured!",
+			__func__);
+		return -EINVAL;
+	}
+	if ((config->direction == MSP_DIR_TX) && (!msp->dma_cfg_tx)) {
+		dev_err(msp->dev, "%s: ERROR: MSP TX-mode is not configured!",
+			__func__);
+		return -EINVAL;
+	}
+
+	reg_val_DMACR = readl(msp->registers + MSP_DMACR);
+	if (config->direction & MSP_DIR_RX)
+		reg_val_DMACR |= RX_DMA_ENABLE;
+	if (config->direction & MSP_DIR_TX)
+		reg_val_DMACR |= TX_DMA_ENABLE;
+	writel(reg_val_DMACR, msp->registers + MSP_DMACR);
+
+	writel(config->iodelay, msp->registers + MSP_IODLY);
+
+	/* Enable frame generation logic */
+	reg_val_GCR = readl(msp->registers + MSP_GCR);
+	writel(reg_val_GCR | FRAME_GEN_ENABLE, msp->registers + MSP_GCR);
+
+	return status;
+}
+
+static void flush_fifo_rx(struct ux500_msp *msp)
+{
+	u32 reg_val_DR, reg_val_GCR, reg_val_FLR;
+	u32 limit = 32;
+
+	reg_val_GCR = readl(msp->registers + MSP_GCR);
+	writel(reg_val_GCR | RX_ENABLE, msp->registers + MSP_GCR);
+
+	reg_val_FLR = readl(msp->registers + MSP_FLR);
+	while (!(reg_val_FLR & RX_FIFO_EMPTY) && limit--) {
+		reg_val_DR = readl(msp->registers + MSP_DR);
+		reg_val_FLR = readl(msp->registers + MSP_FLR);
+	}
+
+	writel(reg_val_GCR, msp->registers + MSP_GCR);
+}
+
+static void flush_fifo_tx(struct ux500_msp *msp)
+{
+	u32 reg_val_TSTDR, reg_val_GCR, reg_val_FLR;
+	u32 limit = 32;
+
+	reg_val_GCR = readl(msp->registers + MSP_GCR);
+	writel(reg_val_GCR | TX_ENABLE, msp->registers + MSP_GCR);
+	writel(MSP_ITCR_ITEN | MSP_ITCR_TESTFIFO, msp->registers + MSP_ITCR);
+
+	reg_val_FLR = readl(msp->registers + MSP_FLR);
+	while (!(reg_val_FLR & TX_FIFO_EMPTY) && limit--) {
+		reg_val_TSTDR = readl(msp->registers + MSP_TSTDR);
+		reg_val_FLR = readl(msp->registers + MSP_FLR);
+	}
+	writel(0x0, msp->registers + MSP_ITCR);
+	writel(reg_val_GCR, msp->registers + MSP_GCR);
+}
+
+int ux500_msp_i2s_open(struct ux500_msp *msp,
+		struct ux500_msp_config *config)
+{
+	u32 old_reg, new_reg, mask;
+	int res;
+	unsigned int tx_sel, rx_sel, tx_busy, rx_busy;
+
+	if (in_interrupt()) {
+		dev_err(msp->dev,
+			"%s: ERROR: Open called in interrupt context!\n",
+			__func__);
+		return -1;
+	}
+
+	tx_sel = (config->direction & MSP_DIR_TX) > 0;
+	rx_sel = (config->direction & MSP_DIR_RX) > 0;
+	if (!tx_sel && !rx_sel) {
+		dev_err(msp->dev, "%s: Error: No direction selected!\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	tx_busy = (msp->dir_busy & MSP_DIR_TX) > 0;
+	rx_busy = (msp->dir_busy & MSP_DIR_RX) > 0;
+	if (tx_busy && tx_sel) {
+		dev_err(msp->dev, "%s: Error: TX is in use!\n", __func__);
+		return -EBUSY;
+	}
+	if (rx_busy && rx_sel) {
+		dev_err(msp->dev, "%s: Error: RX is in use!\n", __func__);
+		return -EBUSY;
+	}
+
+	msp->dir_busy |= (tx_sel ? MSP_DIR_TX : 0) | (rx_sel ? MSP_DIR_RX : 0);
+
+	/* First do the global config register */
+	mask = RX_CLK_SEL_MASK | TX_CLK_SEL_MASK | RX_FSYNC_MASK |
+	    TX_FSYNC_MASK | RX_SYNC_SEL_MASK | TX_SYNC_SEL_MASK |
+	    RX_FIFO_ENABLE_MASK | TX_FIFO_ENABLE_MASK | SRG_CLK_SEL_MASK |
+	    LOOPBACK_MASK | TX_EXTRA_DELAY_MASK;
+
+	new_reg = (config->tx_clk_sel | config->rx_clk_sel |
+		config->rx_fsync_pol | config->tx_fsync_pol |
+		config->rx_fsync_sel | config->tx_fsync_sel |
+		config->rx_fifo_config | config->tx_fifo_config |
+		config->srg_clk_sel | config->loopback_enable |
+		config->tx_data_enable);
+
+	old_reg = readl(msp->registers + MSP_GCR);
+	old_reg &= ~mask;
+	new_reg |= old_reg;
+	writel(new_reg, msp->registers + MSP_GCR);
+
+	res = enable_msp(msp, config);
+	if (res < 0) {
+		dev_err(msp->dev, "%s: ERROR: enable_msp failed (%d)!\n",
+			__func__, res);
+		return -EBUSY;
+	}
+	if (config->loopback_enable & 0x80)
+		msp->loopback_enable = 1;
+
+	/* Flush FIFOs */
+	flush_fifo_tx(msp);
+	flush_fifo_rx(msp);
+
+	msp->msp_state = MSP_STATE_CONFIGURED;
+	return 0;
+}
+
+static void disable_msp_rx(struct ux500_msp *msp)
+{
+	u32 reg_val_GCR, reg_val_DMACR, reg_val_IMSC;
+
+	reg_val_GCR = readl(msp->registers + MSP_GCR);
+	writel(reg_val_GCR & ~RX_ENABLE, msp->registers + MSP_GCR);
+	reg_val_DMACR = readl(msp->registers + MSP_DMACR);
+	writel(reg_val_DMACR & ~RX_DMA_ENABLE, msp->registers + MSP_DMACR);
+	reg_val_IMSC = readl(msp->registers + MSP_IMSC);
+	writel(reg_val_IMSC &
+			~(RX_SERVICE_INT | RX_OVERRUN_ERROR_INT),
+			msp->registers + MSP_IMSC);
+
+	msp->dir_busy &= ~MSP_DIR_RX;
+}
+
+static void disable_msp_tx(struct ux500_msp *msp)
+{
+	u32 reg_val_GCR, reg_val_DMACR, reg_val_IMSC;
+
+	reg_val_GCR = readl(msp->registers + MSP_GCR);
+	writel(reg_val_GCR & ~TX_ENABLE, msp->registers + MSP_GCR);
+	reg_val_DMACR = readl(msp->registers + MSP_DMACR);
+	writel(reg_val_DMACR & ~TX_DMA_ENABLE, msp->registers + MSP_DMACR);
+	reg_val_IMSC = readl(msp->registers + MSP_IMSC);
+	writel(reg_val_IMSC &
+			~(TX_SERVICE_INT | TX_UNDERRUN_ERR_INT),
+			msp->registers + MSP_IMSC);
+
+	msp->dir_busy &= ~MSP_DIR_TX;
+}
+
+static int disable_msp(struct ux500_msp *msp, unsigned int dir)
+{
+	u32 reg_val_GCR;
+	int status = 0;
+	unsigned int disable_tx, disable_rx;
+
+	reg_val_GCR = readl(msp->registers + MSP_GCR);
+	disable_tx = dir & MSP_DIR_TX;
+	disable_rx = dir & MSP_DIR_TX;
+	if (disable_tx && disable_rx) {
+		reg_val_GCR = readl(msp->registers + MSP_GCR);
+		writel(reg_val_GCR | LOOPBACK_MASK,
+				msp->registers + MSP_GCR);
+
+		/* Flush TX-FIFO */
+		flush_fifo_tx(msp);
+
+		/* Disable TX-channel */
+		writel((readl(msp->registers + MSP_GCR) &
+			       (~TX_ENABLE)), msp->registers + MSP_GCR);
+
+		/* Flush RX-FIFO */
+		flush_fifo_rx(msp);
+
+		/* Disable Loopback and Receive channel */
+		writel((readl(msp->registers + MSP_GCR) &
+				(~(RX_ENABLE | LOOPBACK_MASK))),
+				msp->registers + MSP_GCR);
+
+		disable_msp_tx(msp);
+		disable_msp_rx(msp);
+	} else if (disable_tx)
+		disable_msp_tx(msp);
+	else if (disable_rx)
+		disable_msp_rx(msp);
+
+	return status;
+}
+
+int ux500_msp_i2s_trigger(struct ux500_msp *msp, int cmd, int direction)
+{
+	u32 reg_val_GCR, enable_bit;
+
+	if (msp->msp_state == MSP_STATE_IDLE) {
+		dev_err(msp->dev, "%s: ERROR: MSP is not configured!\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		if (direction == SNDRV_PCM_STREAM_PLAYBACK)
+			enable_bit = TX_ENABLE;
+		else
+			enable_bit = RX_ENABLE;
+		reg_val_GCR = readl(msp->registers + MSP_GCR);
+		writel(reg_val_GCR | enable_bit, msp->registers + MSP_GCR);
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		if (direction == SNDRV_PCM_STREAM_PLAYBACK)
+			disable_msp_tx(msp);
+		else
+			disable_msp_rx(msp);
+		break;
+	default:
+		return -EINVAL;
+		break;
+	}
+
+	return 0;
+}
+
+int ux500_msp_i2s_close(struct ux500_msp *msp, unsigned int dir)
+{
+	int status = 0;
+
+	dev_dbg(msp->dev, "%s: Enter (dir = 0x%01x).\n", __func__, dir);
+
+	status = disable_msp(msp, dir);
+	if (msp->dir_busy == 0) {
+		/* disable sample rate and frame generators */
+		msp->msp_state = MSP_STATE_IDLE;
+		writel((readl(msp->registers + MSP_GCR) &
+			       (~(FRAME_GEN_ENABLE | SRG_ENABLE))),
+			      msp->registers + MSP_GCR);
+		if (msp->plat_exit)
+			status = msp->plat_exit();
+			if (status)
+				dev_warn(msp->dev,
+					"%s: WARN: ux500_msp_i2s_exit failed (%d)!\n",
+					__func__, status);
+		writel(0, msp->registers + MSP_GCR);
+		writel(0, msp->registers + MSP_TCF);
+		writel(0, msp->registers + MSP_RCF);
+		writel(0, msp->registers + MSP_DMACR);
+		writel(0, msp->registers + MSP_SRG);
+		writel(0, msp->registers + MSP_MCR);
+		writel(0, msp->registers + MSP_RCM);
+		writel(0, msp->registers + MSP_RCV);
+		writel(0, msp->registers + MSP_TCE0);
+		writel(0, msp->registers + MSP_TCE1);
+		writel(0, msp->registers + MSP_TCE2);
+		writel(0, msp->registers + MSP_TCE3);
+		writel(0, msp->registers + MSP_RCE0);
+		writel(0, msp->registers + MSP_RCE1);
+		writel(0, msp->registers + MSP_RCE2);
+		writel(0, msp->registers + MSP_RCE3);
+	}
+
+	return status;
+
+}
+
+int ux500_msp_i2s_init_msp(struct platform_device *pdev,
+			struct ux500_msp **msp_p,
+			struct msp_i2s_platform_data *platform_data)
+{
+	int ret = 0;
+	struct resource *res = NULL;
+	struct i2s_controller *i2s_cont;
+	struct ux500_msp *msp;
+
+	dev_dbg(&pdev->dev, "%s: Enter (name: %s, id: %d).\n", __func__,
+		pdev->name, platform_data->id);
+
+	*msp_p = devm_kzalloc(&pdev->dev, sizeof(struct ux500_msp), GFP_KERNEL);
+	msp = *msp_p;
+
+	msp->id = platform_data->id;
+	msp->dev = &pdev->dev;
+	msp->plat_init = platform_data->msp_i2s_init;
+	msp->plat_exit = platform_data->msp_i2s_exit;
+	msp->dma_cfg_rx = platform_data->msp_i2s_dma_rx;
+	msp->dma_cfg_tx = platform_data->msp_i2s_dma_tx;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res == NULL) {
+		dev_err(&pdev->dev, "%s: ERROR: Unable to get resource!\n",
+			__func__);
+		ret = -ENOMEM;
+		goto err_res;
+	}
+
+	msp->registers = ioremap(res->start, (res->end - res->start + 1));
+	if (msp->registers == NULL) {
+		dev_err(&pdev->dev, "%s: ERROR: ioremap failed!\n", __func__);
+		ret = -ENOMEM;
+		goto err_res;
+	}
+
+	msp->msp_state = MSP_STATE_IDLE;
+	msp->loopback_enable = 0;
+
+	/* I2S-controller is allocated and added in I2S controller class. */
+	i2s_cont = devm_kzalloc(&pdev->dev, sizeof(*i2s_cont), GFP_KERNEL);
+	if (!i2s_cont) {
+		dev_err(&pdev->dev,
+			"%s: ERROR: Failed to allocate I2S-controller!\n",
+			__func__);
+		goto err_i2s_cont;
+	}
+	i2s_cont->dev.parent = &pdev->dev;
+	i2s_cont->data = (void *)msp;
+	i2s_cont->id = (s16)msp->id;
+	snprintf(i2s_cont->name, sizeof(i2s_cont->name), "ux500-msp-i2s.%04x",
+		msp->id);
+	dev_dbg(&pdev->dev, "I2S device-name: '%s'\n", i2s_cont->name);
+	msp->i2s_cont = i2s_cont;
+
+	return 0;
+
+err_i2s_cont:
+	iounmap(msp->registers);
+
+err_res:
+	devm_kfree(&pdev->dev, msp);
+
+	return ret;
+}
+
+void ux500_msp_i2s_cleanup_msp(struct platform_device *pdev,
+			struct ux500_msp *msp)
+{
+	dev_dbg(msp->dev, "%s: Enter (id = %d).\n", __func__, msp->id);
+
+	device_unregister(&msp->i2s_cont->dev);
+	devm_kfree(&pdev->dev, msp->i2s_cont);
+
+	iounmap(msp->registers);
+
+	devm_kfree(&pdev->dev, msp);
+}
+
+MODULE_LICENSE("GPLv2");
diff --git a/sound/soc/ux500/ux500_msp_i2s.h b/sound/soc/ux500/ux500_msp_i2s.h
new file mode 100644
index 0000000..7f71b4a
--- /dev/null
+++ b/sound/soc/ux500/ux500_msp_i2s.h
@@ -0,0 +1,553 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2012
+ *
+ * Author: Ola Lilja <ola.o.lilja@stericsson.com>,
+ *         for ST-Ericsson.
+ *
+ * License terms:
+ *
+ * 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.
+ */
+
+
+#ifndef UX500_MSP_I2S_H
+#define UX500_MSP_I2S_H
+
+#include <linux/platform_device.h>
+
+#include <mach/board-mop500-msp.h>
+
+#define MSP_INPUT_FREQ_APB 48000000
+
+/*** Stereo mode. Used for APB data accesses as 16 bits accesses (mono),
+ *   32 bits accesses (stereo).
+ ***/
+enum msp_stereo_mode {
+	MSP_MONO,
+	MSP_STEREO
+};
+
+/* Direction (Transmit/Receive mode) */
+enum msp_direction {
+	MSP_TX = 1,
+	MSP_RX = 2
+};
+
+/* Transmit and receive configuration register */
+#define MSP_BIG_ENDIAN           0x00000000
+#define MSP_LITTLE_ENDIAN        0x00001000
+#define MSP_UNEXPECTED_FS_ABORT  0x00000000
+#define MSP_UNEXPECTED_FS_IGNORE 0x00008000
+#define MSP_NON_MODE_BIT_MASK    0x00009000
+
+/* Global configuration register */
+#define RX_ENABLE             0x00000001
+#define RX_FIFO_ENABLE        0x00000002
+#define RX_SYNC_SRG           0x00000010
+#define RX_CLK_POL_RISING     0x00000020
+#define RX_CLK_SEL_SRG        0x00000040
+#define TX_ENABLE             0x00000100
+#define TX_FIFO_ENABLE        0x00000200
+#define TX_SYNC_SRG_PROG      0x00001800
+#define TX_SYNC_SRG_AUTO      0x00001000
+#define TX_CLK_POL_RISING     0x00002000
+#define TX_CLK_SEL_SRG        0x00004000
+#define TX_EXTRA_DELAY_ENABLE 0x00008000
+#define SRG_ENABLE            0x00010000
+#define FRAME_GEN_ENABLE      0x00100000
+#define SRG_CLK_SEL_APB       0x00000000
+#define RX_FIFO_SYNC_HI       0x00000000
+#define TX_FIFO_SYNC_HI       0x00000000
+#define SPI_CLK_MODE_NORMAL   0x00000000
+
+#define MSP_FRAME_SIZE_AUTO -1
+
+#define MSP_DR		0x00
+#define MSP_GCR		0x04
+#define MSP_TCF		0x08
+#define MSP_RCF		0x0c
+#define MSP_SRG		0x10
+#define MSP_FLR		0x14
+#define MSP_DMACR	0x18
+
+#define MSP_IMSC	0x20
+#define MSP_RIS		0x24
+#define MSP_MIS		0x28
+#define MSP_ICR		0x2c
+#define MSP_MCR		0x30
+#define MSP_RCV		0x34
+#define MSP_RCM		0x38
+
+#define MSP_TCE0	0x40
+#define MSP_TCE1	0x44
+#define MSP_TCE2	0x48
+#define MSP_TCE3	0x4c
+
+#define MSP_RCE0	0x60
+#define MSP_RCE1	0x64
+#define MSP_RCE2	0x68
+#define MSP_RCE3	0x6c
+#define MSP_IODLY	0x70
+
+#define MSP_ITCR	0x80
+#define MSP_ITIP	0x84
+#define MSP_ITOP	0x88
+#define MSP_TSTDR	0x8c
+
+#define MSP_PID0	0xfe0
+#define MSP_PID1	0xfe4
+#define MSP_PID2	0xfe8
+#define MSP_PID3	0xfec
+
+#define MSP_CID0	0xff0
+#define MSP_CID1	0xff4
+#define MSP_CID2	0xff8
+#define MSP_CID3	0xffc
+
+/* Protocol dependant parameters list */
+#define RX_ENABLE_MASK		BIT(0)
+#define RX_FIFO_ENABLE_MASK	BIT(1)
+#define RX_FSYNC_MASK		BIT(2)
+#define DIRECT_COMPANDING_MASK	BIT(3)
+#define RX_SYNC_SEL_MASK	BIT(4)
+#define RX_CLK_POL_MASK		BIT(5)
+#define RX_CLK_SEL_MASK		BIT(6)
+#define LOOPBACK_MASK		BIT(7)
+#define TX_ENABLE_MASK		BIT(8)
+#define TX_FIFO_ENABLE_MASK	BIT(9)
+#define TX_FSYNC_MASK		BIT(10)
+#define TX_MSP_TDR_TSR		BIT(11)
+#define TX_SYNC_SEL_MASK	(BIT(12) | BIT(11))
+#define TX_CLK_POL_MASK		BIT(13)
+#define TX_CLK_SEL_MASK		BIT(14)
+#define TX_EXTRA_DELAY_MASK	BIT(15)
+#define SRG_ENABLE_MASK		BIT(16)
+#define SRG_CLK_POL_MASK	BIT(17)
+#define SRG_CLK_SEL_MASK	(BIT(19) | BIT(18))
+#define FRAME_GEN_EN_MASK	BIT(20)
+#define SPI_CLK_MODE_MASK	(BIT(22) | BIT(21))
+#define SPI_BURST_MODE_MASK	BIT(23)
+
+#define RXEN_SHIFT		0
+#define RFFEN_SHIFT		1
+#define RFSPOL_SHIFT		2
+#define DCM_SHIFT		3
+#define RFSSEL_SHIFT		4
+#define RCKPOL_SHIFT		5
+#define RCKSEL_SHIFT		6
+#define LBM_SHIFT		7
+#define TXEN_SHIFT		8
+#define TFFEN_SHIFT		9
+#define TFSPOL_SHIFT		10
+#define TFSSEL_SHIFT		11
+#define TCKPOL_SHIFT		13
+#define TCKSEL_SHIFT		14
+#define TXDDL_SHIFT		15
+#define SGEN_SHIFT		16
+#define SCKPOL_SHIFT		17
+#define SCKSEL_SHIFT		18
+#define FGEN_SHIFT		20
+#define SPICKM_SHIFT		21
+#define TBSWAP_SHIFT		28
+
+#define RCKPOL_MASK		BIT(0)
+#define TCKPOL_MASK		BIT(0)
+#define SPICKM_MASK		(BIT(1) | BIT(0))
+#define MSP_RX_CLKPOL_BIT(n)     ((n & RCKPOL_MASK) << RCKPOL_SHIFT)
+#define MSP_TX_CLKPOL_BIT(n)     ((n & TCKPOL_MASK) << TCKPOL_SHIFT)
+
+#define P1ELEN_SHIFT		0
+#define P1FLEN_SHIFT		3
+#define DTYP_SHIFT		10
+#define ENDN_SHIFT		12
+#define DDLY_SHIFT		13
+#define FSIG_SHIFT		15
+#define P2ELEN_SHIFT		16
+#define P2FLEN_SHIFT		19
+#define P2SM_SHIFT		26
+#define P2EN_SHIFT		27
+#define FSYNC_SHIFT		15
+
+#define P1ELEN_MASK		0x00000007
+#define P2ELEN_MASK		0x00070000
+#define P1FLEN_MASK		0x00000378
+#define P2FLEN_MASK		0x03780000
+#define DDLY_MASK		0x00003000
+#define DTYP_MASK		0x00000600
+#define P2SM_MASK		0x04000000
+#define P2EN_MASK		0x08000000
+#define ENDN_MASK		0x00001000
+#define TFSPOL_MASK		0x00000400
+#define TBSWAP_MASK		0x30000000
+#define COMPANDING_MODE_MASK	0x00000c00
+#define FSYNC_MASK		0x00008000
+
+#define MSP_P1_ELEM_LEN_BITS(n)		(n & P1ELEN_MASK)
+#define MSP_P2_ELEM_LEN_BITS(n)		(((n) << P2ELEN_SHIFT) & P2ELEN_MASK)
+#define MSP_P1_FRAME_LEN_BITS(n)	(((n) << P1FLEN_SHIFT) & P1FLEN_MASK)
+#define MSP_P2_FRAME_LEN_BITS(n)	(((n) << P2FLEN_SHIFT) & P2FLEN_MASK)
+#define MSP_DATA_DELAY_BITS(n)		(((n) << DDLY_SHIFT) & DDLY_MASK)
+#define MSP_DATA_TYPE_BITS(n)		(((n) << DTYP_SHIFT) & DTYP_MASK)
+#define MSP_P2_START_MODE_BIT(n)	((n << P2SM_SHIFT) & P2SM_MASK)
+#define MSP_P2_ENABLE_BIT(n)		((n << P2EN_SHIFT) & P2EN_MASK)
+#define MSP_SET_ENDIANNES_BIT(n)	((n << ENDN_SHIFT) & ENDN_MASK)
+#define MSP_FSYNC_POL(n)		((n << TFSPOL_SHIFT) & TFSPOL_MASK)
+#define MSP_DATA_WORD_SWAP(n)		((n << TBSWAP_SHIFT) & TBSWAP_MASK)
+#define MSP_SET_COMPANDING_MODE(n)	((n << DTYP_SHIFT) & \
+						COMPANDING_MODE_MASK)
+#define MSP_SET_FSYNC_IGNORE(n)		((n << FSYNC_SHIFT) & FSYNC_MASK)
+
+/* Flag register */
+#define RX_BUSY			BIT(0)
+#define RX_FIFO_EMPTY		BIT(1)
+#define RX_FIFO_FULL		BIT(2)
+#define TX_BUSY			BIT(3)
+#define TX_FIFO_EMPTY		BIT(4)
+#define TX_FIFO_FULL		BIT(5)
+
+#define RBUSY_SHIFT		0
+#define RFE_SHIFT		1
+#define RFU_SHIFT		2
+#define TBUSY_SHIFT		3
+#define TFE_SHIFT		4
+#define TFU_SHIFT		5
+
+/* Multichannel control register */
+#define RMCEN_SHIFT		0
+#define RMCSF_SHIFT		1
+#define RCMPM_SHIFT		3
+#define TMCEN_SHIFT		5
+#define TNCSF_SHIFT		6
+
+/* Sample rate generator register */
+#define SCKDIV_SHIFT		0
+#define FRWID_SHIFT		10
+#define FRPER_SHIFT		16
+
+#define SCK_DIV_MASK		0x0000003FF
+#define FRAME_WIDTH_BITS(n)	(((n) << FRWID_SHIFT)  & 0x0000FC00)
+#define FRAME_PERIOD_BITS(n)	(((n) << FRPER_SHIFT) & 0x1FFF0000)
+
+/* DMA controller register */
+#define RX_DMA_ENABLE		BIT(0)
+#define TX_DMA_ENABLE		BIT(1)
+
+#define RDMAE_SHIFT		0
+#define TDMAE_SHIFT		1
+
+/* Interrupt Register */
+#define RX_SERVICE_INT		BIT(0)
+#define RX_OVERRUN_ERROR_INT	BIT(1)
+#define RX_FSYNC_ERR_INT	BIT(2)
+#define RX_FSYNC_INT		BIT(3)
+#define TX_SERVICE_INT		BIT(4)
+#define TX_UNDERRUN_ERR_INT	BIT(5)
+#define TX_FSYNC_ERR_INT	BIT(6)
+#define TX_FSYNC_INT		BIT(7)
+#define ALL_INT			0x000000ff
+
+/* MSP test control register */
+#define MSP_ITCR_ITEN		BIT(0)
+#define MSP_ITCR_TESTFIFO	BIT(1)
+
+#define RMCEN_BIT   0
+#define RMCSF_BIT   1
+#define RCMPM_BIT   3
+#define TMCEN_BIT   5
+#define TNCSF_BIT   6
+
+/* Single or dual phase mode */
+enum msp_phase_mode {
+	MSP_SINGLE_PHASE,
+	MSP_DUAL_PHASE
+};
+
+/* Frame length */
+enum msp_frame_length {
+	MSP_FRAME_LEN_1 = 0,
+	MSP_FRAME_LEN_2 = 1,
+	MSP_FRAME_LEN_4 = 3,
+	MSP_FRAME_LEN_8 = 7,
+	MSP_FRAME_LEN_12 = 11,
+	MSP_FRAME_LEN_16 = 15,
+	MSP_FRAME_LEN_20 = 19,
+	MSP_FRAME_LEN_32 = 31,
+	MSP_FRAME_LEN_48 = 47,
+	MSP_FRAME_LEN_64 = 63
+};
+
+/* Element length */
+enum msp_elem_length {
+	MSP_ELEM_LEN_8 = 0,
+	MSP_ELEM_LEN_10 = 1,
+	MSP_ELEM_LEN_12 = 2,
+	MSP_ELEM_LEN_14 = 3,
+	MSP_ELEM_LEN_16 = 4,
+	MSP_ELEM_LEN_20 = 5,
+	MSP_ELEM_LEN_24 = 6,
+	MSP_ELEM_LEN_32 = 7
+};
+
+enum msp_data_xfer_width {
+	MSP_DATA_TRANSFER_WIDTH_BYTE,
+	MSP_DATA_TRANSFER_WIDTH_HALFWORD,
+	MSP_DATA_TRANSFER_WIDTH_WORD
+};
+
+enum msp_frame_sync {
+	MSP_FSYNC_UNIGNORE = 0,
+	MSP_FSYNC_IGNORE = 1,
+};
+
+enum msp_phase2_start_mode {
+	MSP_PHASE2_START_MODE_IMEDIATE,
+	MSP_PHASE2_START_MODE_FSYNC
+};
+
+enum msp_btf {
+	MSP_BTF_MS_BIT_FIRST = 0,
+	MSP_BTF_LS_BIT_FIRST = 1
+};
+
+enum msp_fsync_pol {
+	MSP_FSYNC_POL_ACT_HI = 0,
+	MSP_FSYNC_POL_ACT_LO = 1
+};
+
+/* Data delay (in bit clock cycles) */
+enum msp_delay {
+	MSP_DELAY_0 = 0,
+	MSP_DELAY_1 = 1,
+	MSP_DELAY_2 = 2,
+	MSP_DELAY_3 = 3
+};
+
+/* Configurations of clocks (transmit, receive or sample rate generator) */
+enum msp_edge {
+	MSP_FALLING_EDGE = 0,
+	MSP_RISING_EDGE = 1,
+};
+
+enum msp_hws {
+	MSP_SWAP_NONE = 0,
+	MSP_SWAP_BYTE_PER_WORD = 1,
+	MSP_SWAP_BYTE_PER_HALF_WORD = 2,
+	MSP_SWAP_HALF_WORD_PER_WORD = 3
+};
+
+enum msp_compress_mode {
+	MSP_COMPRESS_MODE_LINEAR = 0,
+	MSP_COMPRESS_MODE_MU_LAW = 2,
+	MSP_COMPRESS_MODE_A_LAW = 3
+};
+
+enum msp_spi_burst_mode {
+	MSP_SPI_BURST_MODE_DISABLE = 0,
+	MSP_SPI_BURST_MODE_ENABLE = 1
+};
+
+enum msp_expand_mode {
+	MSP_EXPAND_MODE_LINEAR = 0,
+	MSP_EXPAND_MODE_LINEAR_SIGNED = 1,
+	MSP_EXPAND_MODE_MU_LAW = 2,
+	MSP_EXPAND_MODE_A_LAW = 3
+};
+
+#define MSP_FRAME_PERIOD_IN_MONO_MODE 256
+#define MSP_FRAME_PERIOD_IN_STEREO_MODE 32
+#define MSP_FRAME_WIDTH_IN_STEREO_MODE 16
+
+enum msp_protocol {
+	MSP_I2S_PROTOCOL,
+	MSP_PCM_PROTOCOL,
+	MSP_PCM_COMPAND_PROTOCOL,
+	MSP_INVALID_PROTOCOL
+};
+
+/*
+ * No of registers to backup during
+ * suspend resume
+ */
+#define MAX_MSP_BACKUP_REGS 36
+
+enum enum_i2s_controller {
+	MSP_0_I2S_CONTROLLER = 0,
+	MSP_1_I2S_CONTROLLER,
+	MSP_2_I2S_CONTROLLER,
+	MSP_3_I2S_CONTROLLER,
+};
+
+enum i2s_direction_t {
+	MSP_DIR_TX = 0x01,
+	MSP_DIR_RX = 0x02,
+};
+
+enum msp_data_size {
+	MSP_DATA_BITS_DEFAULT = -1,
+	MSP_DATA_BITS_8 = 0x00,
+	MSP_DATA_BITS_10,
+	MSP_DATA_BITS_12,
+	MSP_DATA_BITS_14,
+	MSP_DATA_BITS_16,
+	MSP_DATA_BITS_20,
+	MSP_DATA_BITS_24,
+	MSP_DATA_BITS_32,
+};
+
+enum msp_state {
+	MSP_STATE_IDLE = 0,
+	MSP_STATE_CONFIGURED = 1,
+	MSP_STATE_RUNNING = 2,
+};
+
+enum msp_rx_comparison_enable_mode {
+	MSP_COMPARISON_DISABLED = 0,
+	MSP_COMPARISON_NONEQUAL_ENABLED = 2,
+	MSP_COMPARISON_EQUAL_ENABLED = 3
+};
+
+struct msp_multichannel_config {
+	bool rx_multichannel_enable;
+	bool tx_multichannel_enable;
+	enum msp_rx_comparison_enable_mode rx_comparison_enable_mode;
+	u8 padding;
+	u32 comparison_value;
+	u32 comparison_mask;
+	u32 rx_channel_0_enable;
+	u32 rx_channel_1_enable;
+	u32 rx_channel_2_enable;
+	u32 rx_channel_3_enable;
+	u32 tx_channel_0_enable;
+	u32 tx_channel_1_enable;
+	u32 tx_channel_2_enable;
+	u32 tx_channel_3_enable;
+};
+
+struct msp_protdesc {
+	u32 rx_phase_mode;
+	u32 tx_phase_mode;
+	u32 rx_phase2_start_mode;
+	u32 tx_phase2_start_mode;
+	u32 rx_byte_order;
+	u32 tx_byte_order;
+	u32 rx_frame_len_1;
+	u32 rx_frame_len_2;
+	u32 tx_frame_len_1;
+	u32 tx_frame_len_2;
+	u32 rx_elem_len_1;
+	u32 rx_elem_len_2;
+	u32 tx_elem_len_1;
+	u32 tx_elem_len_2;
+	u32 rx_data_delay;
+	u32 tx_data_delay;
+	u32 rx_clk_pol;
+	u32 tx_clk_pol;
+	u32 rx_fsync_pol;
+	u32 tx_fsync_pol;
+	u32 rx_half_word_swap;
+	u32 tx_half_word_swap;
+	u32 compression_mode;
+	u32 expansion_mode;
+	u32 frame_sync_ignore;
+	u32 frame_period;
+	u32 frame_width;
+	u32 clocks_per_frame;
+};
+
+struct i2s_message {
+	enum i2s_direction_t i2s_direction;
+	void *txdata;
+	void *rxdata;
+	size_t txbytes;
+	size_t rxbytes;
+	int dma_flag;
+	int tx_offset;
+	int rx_offset;
+	bool cyclic_dma;
+	dma_addr_t buf_addr;
+	size_t buf_len;
+	size_t period_len;
+};
+
+struct i2s_controller {
+	struct module *owner;
+	unsigned int id;
+	unsigned int class;
+	const struct i2s_algorithm *algo; /* the algorithm to access the bus */
+	void *data;
+	struct mutex bus_lock;
+	struct device dev; /* the controller device */
+	char name[48];
+};
+
+struct ux500_msp_config {
+	unsigned int f_inputclk;
+	unsigned int rx_clk_sel;
+	unsigned int tx_clk_sel;
+	unsigned int srg_clk_sel;
+	unsigned int rx_fsync_pol;
+	unsigned int tx_fsync_pol;
+	unsigned int rx_fsync_sel;
+	unsigned int tx_fsync_sel;
+	unsigned int rx_fifo_config;
+	unsigned int tx_fifo_config;
+	unsigned int spi_clk_mode;
+	unsigned int spi_burst_mode;
+	unsigned int loopback_enable;
+	unsigned int tx_data_enable;
+	unsigned int default_protdesc;
+	struct msp_protdesc protdesc;
+	int multichannel_configured;
+	struct msp_multichannel_config multichannel_config;
+	unsigned int direction;
+	unsigned int protocol;
+	unsigned int frame_freq;
+	unsigned int frame_size;
+	enum msp_data_size data_size;
+	unsigned int def_elem_len;
+	unsigned int iodelay;
+	void (*handler) (void *data);
+	void *tx_callback_data;
+	void *rx_callback_data;
+};
+
+struct ux500_msp {
+	enum enum_i2s_controller id;
+	void __iomem *registers;
+	struct device *dev;
+	struct i2s_controller *i2s_cont;
+	struct stedma40_chan_cfg *dma_cfg_rx;
+	struct stedma40_chan_cfg *dma_cfg_tx;
+	struct dma_chan *tx_pipeid;
+	struct dma_chan *rx_pipeid;
+	enum msp_state msp_state;
+	int (*transfer) (struct ux500_msp *msp, struct i2s_message *message);
+	int (*plat_init) (void);
+	int (*plat_exit) (void);
+	struct timer_list notify_timer;
+	int def_elem_len;
+	unsigned int dir_busy;
+	int loopback_enable;
+	u32 backup_regs[MAX_MSP_BACKUP_REGS];
+	unsigned int f_bitclk;
+};
+
+struct ux500_msp_dma_params {
+	unsigned int data_size;
+	struct stedma40_chan_cfg *dma_cfg;
+};
+
+int ux500_msp_i2s_init_msp(struct platform_device *pdev,
+			struct ux500_msp **msp_p,
+			struct msp_i2s_platform_data *platform_data);
+void ux500_msp_i2s_cleanup_msp(struct platform_device *pdev,
+			struct ux500_msp *msp);
+int ux500_msp_i2s_open(struct ux500_msp *msp, struct ux500_msp_config *config);
+int ux500_msp_i2s_close(struct ux500_msp *msp,
+			unsigned int dir);
+int ux500_msp_i2s_trigger(struct ux500_msp *msp, int cmd,
+			int direction);
+
+#endif
diff --git a/sound/sound_core.c b/sound/sound_core.c
index c6e81fb..fb9255c 100644
--- a/sound/sound_core.c
+++ b/sound/sound_core.c
@@ -361,7 +361,7 @@
 				  struct device *dev)
 {
 	const int chain = unit % SOUND_STEP;
-	int max_unit = 128 + chain;
+	int max_unit = 256;
 	const char *name;
 	char _name[16];
 
diff --git a/sound/usb/card.c b/sound/usb/card.c
index 4a7be7b..d5b5c33 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -131,8 +131,9 @@
 		subs = &as->substream[idx];
 		if (!subs->num_formats)
 			continue;
-		snd_usb_release_substream_urbs(subs, 1);
 		subs->interface = -1;
+		subs->data_endpoint = NULL;
+		subs->sync_endpoint = NULL;
 	}
 }
 
@@ -276,6 +277,7 @@
 
 static int snd_usb_audio_free(struct snd_usb_audio *chip)
 {
+	mutex_destroy(&chip->mutex);
 	kfree(chip);
 	return 0;
 }
@@ -336,6 +338,7 @@
 		return -ENOMEM;
 	}
 
+	mutex_init(&chip->mutex);
 	mutex_init(&chip->shutdown_mutex);
 	chip->index = idx;
 	chip->dev = dev;
@@ -348,6 +351,7 @@
 	chip->usb_id = USB_ID(le16_to_cpu(dev->descriptor.idVendor),
 			      le16_to_cpu(dev->descriptor.idProduct));
 	INIT_LIST_HEAD(&chip->pcm_list);
+	INIT_LIST_HEAD(&chip->ep_list);
 	INIT_LIST_HEAD(&chip->midi_list);
 	INIT_LIST_HEAD(&chip->mixer_list);
 
@@ -565,6 +569,10 @@
 		list_for_each(p, &chip->pcm_list) {
 			snd_usb_stream_disconnect(p);
 		}
+		/* release the endpoint resources */
+		list_for_each(p, &chip->ep_list) {
+			snd_usb_endpoint_free(p);
+		}
 		/* release the midi resources */
 		list_for_each(p, &chip->midi_list) {
 			snd_usbmidi_disconnect(p);
diff --git a/sound/usb/card.h b/sound/usb/card.h
index da5fa1a..0d37238 100644
--- a/sound/usb/card.h
+++ b/sound/usb/card.h
@@ -30,20 +30,71 @@
 };
 
 struct snd_usb_substream;
+struct snd_usb_endpoint;
 
 struct snd_urb_ctx {
 	struct urb *urb;
 	unsigned int buffer_size;	/* size of data buffer, if data URB */
 	struct snd_usb_substream *subs;
+	struct snd_usb_endpoint *ep;
 	int index;	/* index for urb array */
 	int packets;	/* number of packets per urb */
+	int packet_size[MAX_PACKS_HS]; /* size of packets for next submission */
+	struct list_head ready_list;
 };
 
-struct snd_urb_ops {
-	int (*prepare)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);
-	int (*retire)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);
-	int (*prepare_sync)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);
-	int (*retire_sync)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);
+struct snd_usb_endpoint {
+	struct snd_usb_audio *chip;
+
+	int use_count;
+	int ep_num;		/* the referenced endpoint number */
+	int type;		/* SND_USB_ENDPOINT_TYPE_* */
+	unsigned long flags;
+
+	void (*prepare_data_urb) (struct snd_usb_substream *subs,
+				  struct urb *urb);
+	void (*retire_data_urb) (struct snd_usb_substream *subs,
+				 struct urb *urb);
+
+	struct snd_usb_substream *data_subs;
+	struct snd_usb_endpoint *sync_master;
+	struct snd_usb_endpoint *sync_slave;
+
+	struct snd_urb_ctx urb[MAX_URBS];
+
+	struct snd_usb_packet_info {
+		uint32_t packet_size[MAX_PACKS_HS];
+		int packets;
+	} next_packet[MAX_URBS];
+	int next_packet_read_pos, next_packet_write_pos;
+	struct list_head ready_playback_urbs;
+
+	unsigned int nurbs;		/* # urbs */
+	unsigned long active_mask;	/* bitmask of active urbs */
+	unsigned long unlink_mask;	/* bitmask of unlinked urbs */
+	char *syncbuf;			/* sync buffer for all sync URBs */
+	dma_addr_t sync_dma;		/* DMA address of syncbuf */
+
+	unsigned int pipe;		/* the data i/o pipe */
+	unsigned int freqn;		/* nominal sampling rate in fs/fps in Q16.16 format */
+	unsigned int freqm;		/* momentary sampling rate in fs/fps in Q16.16 format */
+	int	   freqshift;		/* how much to shift the feedback value to get Q16.16 */
+	unsigned int freqmax;		/* maximum sampling rate, used for buffer management */
+	unsigned int phase;		/* phase accumulator */
+	unsigned int maxpacksize;	/* max packet size in bytes */
+	unsigned int maxframesize;      /* max packet size in frames */
+	unsigned int curpacksize;	/* current packet size in bytes (for capture) */
+	unsigned int curframesize;      /* current packet size in frames (for capture) */
+	unsigned int syncmaxsize;	/* sync endpoint packet size */
+	unsigned int fill_max:1;	/* fill max packet size always */
+	unsigned int datainterval;      /* log_2 of data packet interval */
+	unsigned int syncinterval;	/* P for adaptive mode, 0 otherwise */
+	unsigned char silence_value;
+	unsigned int stride;
+	int iface, alt_idx;
+
+	spinlock_t lock;
+	struct list_head list;
 };
 
 struct snd_usb_substream {
@@ -57,21 +108,6 @@
 	unsigned int cur_rate;		/* current rate (for hw_params callback) */
 	unsigned int period_bytes;	/* current period bytes (for hw_params callback) */
 	unsigned int altset_idx;     /* USB data format: index of alternate setting */
-	unsigned int datapipe;   /* the data i/o pipe */
-	unsigned int syncpipe;   /* 1 - async out or adaptive in */
-	unsigned int datainterval;	/* log_2 of data packet interval */
-	unsigned int syncinterval;  /* P for adaptive mode, 0 otherwise */
-	unsigned int freqn;      /* nominal sampling rate in fs/fps in Q16.16 format */
-	unsigned int freqm;      /* momentary sampling rate in fs/fps in Q16.16 format */
-	int          freqshift;  /* how much to shift the feedback value to get Q16.16 */
-	unsigned int freqmax;    /* maximum sampling rate, used for buffer management */
-	unsigned int phase;      /* phase accumulator */
-	unsigned int maxpacksize;	/* max packet size in bytes */
-	unsigned int maxframesize;	/* max packet size in frames */
-	unsigned int curpacksize;	/* current packet size in bytes (for capture) */
-	unsigned int curframesize;	/* current packet size in frames (for capture) */
-	unsigned int syncmaxsize;	/* sync endpoint packet size */
-	unsigned int fill_max: 1;	/* fill max packet size always */
 	unsigned int txfr_quirk:1;	/* allow sub-frame alignment */
 	unsigned int fmt_type;		/* USB audio format type (1-3) */
 
@@ -82,11 +118,10 @@
 	unsigned long active_mask;	/* bitmask of active urbs */
 	unsigned long unlink_mask;	/* bitmask of unlinked urbs */
 
-	unsigned int nurbs;			/* # urbs */
-	struct snd_urb_ctx dataurb[MAX_URBS];	/* data urb table */
-	struct snd_urb_ctx syncurb[SYNC_URBS];	/* sync urb table */
-	char *syncbuf;				/* sync buffer for all sync URBs */
-	dma_addr_t sync_dma;			/* DMA address of syncbuf */
+	/* data and sync endpoints for this stream */
+	struct snd_usb_endpoint *data_endpoint;
+	struct snd_usb_endpoint *sync_endpoint;
+	unsigned long flags;
 
 	u64 formats;			/* format bitmasks (all or'ed) */
 	unsigned int num_formats;		/* number of supported audio formats (list) */
@@ -94,7 +129,6 @@
 	struct snd_pcm_hw_constraint_list rate_list;	/* limited rates */
 	spinlock_t lock;
 
-	struct snd_urb_ops ops;		/* callbacks (must be filled at init) */
 	int last_frame_number;          /* stored frame number */
 	int last_delay;                 /* stored delay */
 };
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index 08dcce5..e690690 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -20,9 +20,11 @@
 #include <linux/ratelimit.h>
 #include <linux/usb.h>
 #include <linux/usb/audio.h>
+#include <linux/slab.h>
 
 #include <sound/core.h>
 #include <sound/pcm.h>
+#include <sound/pcm_params.h>
 
 #include "usbaudio.h"
 #include "helper.h"
@@ -30,6 +32,36 @@
 #include "endpoint.h"
 #include "pcm.h"
 
+#define EP_FLAG_ACTIVATED	0
+#define EP_FLAG_RUNNING		1
+
+/*
+ * snd_usb_endpoint is a model that abstracts everything related to an
+ * USB endpoint and its streaming.
+ *
+ * There are functions to activate and deactivate the streaming URBs and
+ * optional callbacks to let the pcm logic handle the actual content of the
+ * packets for playback and record. Thus, the bus streaming and the audio
+ * handlers are fully decoupled.
+ *
+ * There are two different types of endpoints in audio applications.
+ *
+ * SND_USB_ENDPOINT_TYPE_DATA handles full audio data payload for both
+ * inbound and outbound traffic.
+ *
+ * SND_USB_ENDPOINT_TYPE_SYNC endpoints are for inbound traffic only and
+ * expect the payload to carry Q10.14 / Q16.16 formatted sync information
+ * (3 or 4 bytes).
+ *
+ * Each endpoint has to be configured prior to being used by calling
+ * snd_usb_endpoint_set_params().
+ *
+ * The model incorporates a reference counting, so that multiple users
+ * can call snd_usb_endpoint_start() and snd_usb_endpoint_stop(), and
+ * only the first user will effectively start the URBs, and only the last
+ * one to stop it will tear the URBs down again.
+ */
+
 /*
  * convert a sampling rate into our full speed format (fs/1000 in Q16.16)
  * this will overflow at approx 524 kHz
@@ -49,727 +81,16 @@
 }
 
 /*
- * unlink active urbs.
- */
-static int deactivate_urbs(struct snd_usb_substream *subs, int force, int can_sleep)
-{
-	struct snd_usb_audio *chip = subs->stream->chip;
-	unsigned int i;
-	int async;
-
-	subs->running = 0;
-
-	if (!force && subs->stream->chip->shutdown) /* to be sure... */
-		return -EBADFD;
-
-	async = !can_sleep && chip->async_unlink;
-
-	if (!async && in_interrupt())
-		return 0;
-
-	for (i = 0; i < subs->nurbs; i++) {
-		if (test_bit(i, &subs->active_mask)) {
-			if (!test_and_set_bit(i, &subs->unlink_mask)) {
-				struct urb *u = subs->dataurb[i].urb;
-				if (async)
-					usb_unlink_urb(u);
-				else
-					usb_kill_urb(u);
-			}
-		}
-	}
-	if (subs->syncpipe) {
-		for (i = 0; i < SYNC_URBS; i++) {
-			if (test_bit(i+16, &subs->active_mask)) {
-				if (!test_and_set_bit(i+16, &subs->unlink_mask)) {
-					struct urb *u = subs->syncurb[i].urb;
-					if (async)
-						usb_unlink_urb(u);
-					else
-						usb_kill_urb(u);
-				}
-			}
-		}
-	}
-	return 0;
-}
-
-
-/*
  * release a urb data
  */
 static void release_urb_ctx(struct snd_urb_ctx *u)
 {
-	if (u->urb) {
-		if (u->buffer_size)
-			usb_free_coherent(u->subs->dev, u->buffer_size,
-					u->urb->transfer_buffer,
-					u->urb->transfer_dma);
-		usb_free_urb(u->urb);
-		u->urb = NULL;
-	}
-}
-
-/*
- *  wait until all urbs are processed.
- */
-static int wait_clear_urbs(struct snd_usb_substream *subs)
-{
-	unsigned long end_time = jiffies + msecs_to_jiffies(1000);
-	unsigned int i;
-	int alive;
-
-	do {
-		alive = 0;
-		for (i = 0; i < subs->nurbs; i++) {
-			if (test_bit(i, &subs->active_mask))
-				alive++;
-		}
-		if (subs->syncpipe) {
-			for (i = 0; i < SYNC_URBS; i++) {
-				if (test_bit(i + 16, &subs->active_mask))
-					alive++;
-			}
-		}
-		if (! alive)
-			break;
-		schedule_timeout_uninterruptible(1);
-	} while (time_before(jiffies, end_time));
-	if (alive)
-		snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive);
-	return 0;
-}
-
-/*
- * release a substream
- */
-void snd_usb_release_substream_urbs(struct snd_usb_substream *subs, int force)
-{
-	int i;
-
-	/* stop urbs (to be sure) */
-	deactivate_urbs(subs, force, 1);
-	wait_clear_urbs(subs);
-
-	for (i = 0; i < MAX_URBS; i++)
-		release_urb_ctx(&subs->dataurb[i]);
-	for (i = 0; i < SYNC_URBS; i++)
-		release_urb_ctx(&subs->syncurb[i]);
-	usb_free_coherent(subs->dev, SYNC_URBS * 4,
-			subs->syncbuf, subs->sync_dma);
-	subs->syncbuf = NULL;
-	subs->nurbs = 0;
-}
-
-/*
- * complete callback from data urb
- */
-static void snd_complete_urb(struct urb *urb)
-{
-	struct snd_urb_ctx *ctx = urb->context;
-	struct snd_usb_substream *subs = ctx->subs;
-	struct snd_pcm_substream *substream = ctx->subs->pcm_substream;
-	int err = 0;
-
-	if ((subs->running && subs->ops.retire(subs, substream->runtime, urb)) ||
-	    !subs->running || /* can be stopped during retire callback */
-	    (err = subs->ops.prepare(subs, substream->runtime, urb)) < 0 ||
-	    (err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
-		clear_bit(ctx->index, &subs->active_mask);
-		if (err < 0) {
-			snd_printd(KERN_ERR "cannot submit urb (err = %d)\n", err);
-			snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
-		}
-	}
-}
-
-
-/*
- * complete callback from sync urb
- */
-static void snd_complete_sync_urb(struct urb *urb)
-{
-	struct snd_urb_ctx *ctx = urb->context;
-	struct snd_usb_substream *subs = ctx->subs;
-	struct snd_pcm_substream *substream = ctx->subs->pcm_substream;
-	int err = 0;
-
-	if ((subs->running && subs->ops.retire_sync(subs, substream->runtime, urb)) ||
-	    !subs->running || /* can be stopped during retire callback */
-	    (err = subs->ops.prepare_sync(subs, substream->runtime, urb)) < 0 ||
-	    (err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
-		clear_bit(ctx->index + 16, &subs->active_mask);
-		if (err < 0) {
-			snd_printd(KERN_ERR "cannot submit sync urb (err = %d)\n", err);
-			snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
-		}
-	}
-}
-
-
-/*
- * initialize a substream for plaback/capture
- */
-int snd_usb_init_substream_urbs(struct snd_usb_substream *subs,
-				unsigned int period_bytes,
-				unsigned int rate,
-				unsigned int frame_bits)
-{
-	unsigned int maxsize, i;
-	int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK;
-	unsigned int urb_packs, total_packs, packs_per_ms;
-	struct snd_usb_audio *chip = subs->stream->chip;
-
-	/* calculate the frequency in 16.16 format */
-	if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL)
-		subs->freqn = get_usb_full_speed_rate(rate);
-	else
-		subs->freqn = get_usb_high_speed_rate(rate);
-	subs->freqm = subs->freqn;
-	subs->freqshift = INT_MIN;
-	/* calculate max. frequency */
-	if (subs->maxpacksize) {
-		/* whatever fits into a max. size packet */
-		maxsize = subs->maxpacksize;
-		subs->freqmax = (maxsize / (frame_bits >> 3))
-				<< (16 - subs->datainterval);
-	} else {
-		/* no max. packet size: just take 25% higher than nominal */
-		subs->freqmax = subs->freqn + (subs->freqn >> 2);
-		maxsize = ((subs->freqmax + 0xffff) * (frame_bits >> 3))
-				>> (16 - subs->datainterval);
-	}
-	subs->phase = 0;
-
-	if (subs->fill_max)
-		subs->curpacksize = subs->maxpacksize;
-	else
-		subs->curpacksize = maxsize;
-
-	if (snd_usb_get_speed(subs->dev) != USB_SPEED_FULL)
-		packs_per_ms = 8 >> subs->datainterval;
-	else
-		packs_per_ms = 1;
-
-	if (is_playback) {
-		urb_packs = max(chip->nrpacks, 1);
-		urb_packs = min(urb_packs, (unsigned int)MAX_PACKS);
-	} else
-		urb_packs = 1;
-	urb_packs *= packs_per_ms;
-	if (subs->syncpipe)
-		urb_packs = min(urb_packs, 1U << subs->syncinterval);
-
-	/* decide how many packets to be used */
-	if (is_playback) {
-		unsigned int minsize, maxpacks;
-		/* determine how small a packet can be */
-		minsize = (subs->freqn >> (16 - subs->datainterval))
-			  * (frame_bits >> 3);
-		/* with sync from device, assume it can be 12% lower */
-		if (subs->syncpipe)
-			minsize -= minsize >> 3;
-		minsize = max(minsize, 1u);
-		total_packs = (period_bytes + minsize - 1) / minsize;
-		/* we need at least two URBs for queueing */
-		if (total_packs < 2) {
-			total_packs = 2;
-		} else {
-			/* and we don't want too long a queue either */
-			maxpacks = max(MAX_QUEUE * packs_per_ms, urb_packs * 2);
-			total_packs = min(total_packs, maxpacks);
-		}
-	} else {
-		while (urb_packs > 1 && urb_packs * maxsize >= period_bytes)
-			urb_packs >>= 1;
-		total_packs = MAX_URBS * urb_packs;
-	}
-	subs->nurbs = (total_packs + urb_packs - 1) / urb_packs;
-	if (subs->nurbs > MAX_URBS) {
-		/* too much... */
-		subs->nurbs = MAX_URBS;
-		total_packs = MAX_URBS * urb_packs;
-	} else if (subs->nurbs < 2) {
-		/* too little - we need at least two packets
-		 * to ensure contiguous playback/capture
-		 */
-		subs->nurbs = 2;
-	}
-
-	/* allocate and initialize data urbs */
-	for (i = 0; i < subs->nurbs; i++) {
-		struct snd_urb_ctx *u = &subs->dataurb[i];
-		u->index = i;
-		u->subs = subs;
-		u->packets = (i + 1) * total_packs / subs->nurbs
-			- i * total_packs / subs->nurbs;
-		u->buffer_size = maxsize * u->packets;
-		if (subs->fmt_type == UAC_FORMAT_TYPE_II)
-			u->packets++; /* for transfer delimiter */
-		u->urb = usb_alloc_urb(u->packets, GFP_KERNEL);
-		if (!u->urb)
-			goto out_of_memory;
-		u->urb->transfer_buffer =
-			usb_alloc_coherent(subs->dev, u->buffer_size,
-					   GFP_KERNEL, &u->urb->transfer_dma);
-		if (!u->urb->transfer_buffer)
-			goto out_of_memory;
-		u->urb->pipe = subs->datapipe;
-		u->urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
-		u->urb->interval = 1 << subs->datainterval;
-		u->urb->context = u;
-		u->urb->complete = snd_complete_urb;
-	}
-
-	if (subs->syncpipe) {
-		/* allocate and initialize sync urbs */
-		subs->syncbuf = usb_alloc_coherent(subs->dev, SYNC_URBS * 4,
-						 GFP_KERNEL, &subs->sync_dma);
-		if (!subs->syncbuf)
-			goto out_of_memory;
-		for (i = 0; i < SYNC_URBS; i++) {
-			struct snd_urb_ctx *u = &subs->syncurb[i];
-			u->index = i;
-			u->subs = subs;
-			u->packets = 1;
-			u->urb = usb_alloc_urb(1, GFP_KERNEL);
-			if (!u->urb)
-				goto out_of_memory;
-			u->urb->transfer_buffer = subs->syncbuf + i * 4;
-			u->urb->transfer_dma = subs->sync_dma + i * 4;
-			u->urb->transfer_buffer_length = 4;
-			u->urb->pipe = subs->syncpipe;
-			u->urb->transfer_flags = URB_ISO_ASAP |
-						 URB_NO_TRANSFER_DMA_MAP;
-			u->urb->number_of_packets = 1;
-			u->urb->interval = 1 << subs->syncinterval;
-			u->urb->context = u;
-			u->urb->complete = snd_complete_sync_urb;
-		}
-	}
-	return 0;
-
-out_of_memory:
-	snd_usb_release_substream_urbs(subs, 0);
-	return -ENOMEM;
-}
-
-/*
- * prepare urb for full speed capture sync pipe
- *
- * fill the length and offset of each urb descriptor.
- * the fixed 10.14 frequency is passed through the pipe.
- */
-static int prepare_capture_sync_urb(struct snd_usb_substream *subs,
-				    struct snd_pcm_runtime *runtime,
-				    struct urb *urb)
-{
-	unsigned char *cp = urb->transfer_buffer;
-	struct snd_urb_ctx *ctx = urb->context;
-
-	urb->dev = ctx->subs->dev; /* we need to set this at each time */
-	urb->iso_frame_desc[0].length = 3;
-	urb->iso_frame_desc[0].offset = 0;
-	cp[0] = subs->freqn >> 2;
-	cp[1] = subs->freqn >> 10;
-	cp[2] = subs->freqn >> 18;
-	return 0;
-}
-
-/*
- * prepare urb for high speed capture sync pipe
- *
- * fill the length and offset of each urb descriptor.
- * the fixed 12.13 frequency is passed as 16.16 through the pipe.
- */
-static int prepare_capture_sync_urb_hs(struct snd_usb_substream *subs,
-				       struct snd_pcm_runtime *runtime,
-				       struct urb *urb)
-{
-	unsigned char *cp = urb->transfer_buffer;
-	struct snd_urb_ctx *ctx = urb->context;
-
-	urb->dev = ctx->subs->dev; /* we need to set this at each time */
-	urb->iso_frame_desc[0].length = 4;
-	urb->iso_frame_desc[0].offset = 0;
-	cp[0] = subs->freqn;
-	cp[1] = subs->freqn >> 8;
-	cp[2] = subs->freqn >> 16;
-	cp[3] = subs->freqn >> 24;
-	return 0;
-}
-
-/*
- * process after capture sync complete
- * - nothing to do
- */
-static int retire_capture_sync_urb(struct snd_usb_substream *subs,
-				   struct snd_pcm_runtime *runtime,
-				   struct urb *urb)
-{
-	return 0;
-}
-
-/*
- * prepare urb for capture data pipe
- *
- * fill the offset and length of each descriptor.
- *
- * we use a temporary buffer to write the captured data.
- * since the length of written data is determined by host, we cannot
- * write onto the pcm buffer directly...  the data is thus copied
- * later at complete callback to the global buffer.
- */
-static int prepare_capture_urb(struct snd_usb_substream *subs,
-			       struct snd_pcm_runtime *runtime,
-			       struct urb *urb)
-{
-	int i, offs;
-	struct snd_urb_ctx *ctx = urb->context;
-
-	offs = 0;
-	urb->dev = ctx->subs->dev; /* we need to set this at each time */
-	for (i = 0; i < ctx->packets; i++) {
-		urb->iso_frame_desc[i].offset = offs;
-		urb->iso_frame_desc[i].length = subs->curpacksize;
-		offs += subs->curpacksize;
-	}
-	urb->transfer_buffer_length = offs;
-	urb->number_of_packets = ctx->packets;
-	return 0;
-}
-
-/*
- * process after capture complete
- *
- * copy the data from each desctiptor to the pcm buffer, and
- * update the current position.
- */
-static int retire_capture_urb(struct snd_usb_substream *subs,
-			      struct snd_pcm_runtime *runtime,
-			      struct urb *urb)
-{
-	unsigned long flags;
-	unsigned char *cp;
-	int i;
-	unsigned int stride, frames, bytes, oldptr;
-	int period_elapsed = 0;
-
-	stride = runtime->frame_bits >> 3;
-
-	for (i = 0; i < urb->number_of_packets; i++) {
-		cp = (unsigned char *)urb->transfer_buffer + urb->iso_frame_desc[i].offset;
-		if (urb->iso_frame_desc[i].status && printk_ratelimit()) {
-			snd_printdd("frame %d active: %d\n", i, urb->iso_frame_desc[i].status);
-			// continue;
-		}
-		bytes = urb->iso_frame_desc[i].actual_length;
-		frames = bytes / stride;
-		if (!subs->txfr_quirk)
-			bytes = frames * stride;
-		if (bytes % (runtime->sample_bits >> 3) != 0) {
-#ifdef CONFIG_SND_DEBUG_VERBOSE
-			int oldbytes = bytes;
-#endif
-			bytes = frames * stride;
-			snd_printdd(KERN_ERR "Corrected urb data len. %d->%d\n",
-							oldbytes, bytes);
-		}
-		/* update the current pointer */
-		spin_lock_irqsave(&subs->lock, flags);
-		oldptr = subs->hwptr_done;
-		subs->hwptr_done += bytes;
-		if (subs->hwptr_done >= runtime->buffer_size * stride)
-			subs->hwptr_done -= runtime->buffer_size * stride;
-		frames = (bytes + (oldptr % stride)) / stride;
-		subs->transfer_done += frames;
-		if (subs->transfer_done >= runtime->period_size) {
-			subs->transfer_done -= runtime->period_size;
-			period_elapsed = 1;
-		}
-		spin_unlock_irqrestore(&subs->lock, flags);
-		/* copy a data chunk */
-		if (oldptr + bytes > runtime->buffer_size * stride) {
-			unsigned int bytes1 =
-					runtime->buffer_size * stride - oldptr;
-			memcpy(runtime->dma_area + oldptr, cp, bytes1);
-			memcpy(runtime->dma_area, cp + bytes1, bytes - bytes1);
-		} else {
-			memcpy(runtime->dma_area + oldptr, cp, bytes);
-		}
-	}
-	if (period_elapsed)
-		snd_pcm_period_elapsed(subs->pcm_substream);
-	return 0;
-}
-
-/*
- * Process after capture complete when paused.  Nothing to do.
- */
-static int retire_paused_capture_urb(struct snd_usb_substream *subs,
-				     struct snd_pcm_runtime *runtime,
-				     struct urb *urb)
-{
-	return 0;
-}
-
-
-/*
- * prepare urb for playback sync pipe
- *
- * set up the offset and length to receive the current frequency.
- */
-static int prepare_playback_sync_urb(struct snd_usb_substream *subs,
-				     struct snd_pcm_runtime *runtime,
-				     struct urb *urb)
-{
-	struct snd_urb_ctx *ctx = urb->context;
-
-	urb->dev = ctx->subs->dev; /* we need to set this at each time */
-	urb->iso_frame_desc[0].length = min(4u, ctx->subs->syncmaxsize);
-	urb->iso_frame_desc[0].offset = 0;
-	return 0;
-}
-
-/*
- * process after playback sync complete
- *
- * Full speed devices report feedback values in 10.14 format as samples per
- * frame, high speed devices in 16.16 format as samples per microframe.
- * Because the Audio Class 1 spec was written before USB 2.0, many high speed
- * devices use a wrong interpretation, some others use an entirely different
- * format.  Therefore, we cannot predict what format any particular device uses
- * and must detect it automatically.
- */
-static int retire_playback_sync_urb(struct snd_usb_substream *subs,
-				    struct snd_pcm_runtime *runtime,
-				    struct urb *urb)
-{
-	unsigned int f;
-	int shift;
-	unsigned long flags;
-
-	if (urb->iso_frame_desc[0].status != 0 ||
-	    urb->iso_frame_desc[0].actual_length < 3)
-		return 0;
-
-	f = le32_to_cpup(urb->transfer_buffer);
-	if (urb->iso_frame_desc[0].actual_length == 3)
-		f &= 0x00ffffff;
-	else
-		f &= 0x0fffffff;
-	if (f == 0)
-		return 0;
-
-	if (unlikely(subs->freqshift == INT_MIN)) {
-		/*
-		 * The first time we see a feedback value, determine its format
-		 * by shifting it left or right until it matches the nominal
-		 * frequency value.  This assumes that the feedback does not
-		 * differ from the nominal value more than +50% or -25%.
-		 */
-		shift = 0;
-		while (f < subs->freqn - subs->freqn / 4) {
-			f <<= 1;
-			shift++;
-		}
-		while (f > subs->freqn + subs->freqn / 2) {
-			f >>= 1;
-			shift--;
-		}
-		subs->freqshift = shift;
-	}
-	else if (subs->freqshift >= 0)
-		f <<= subs->freqshift;
-	else
-		f >>= -subs->freqshift;
-
-	if (likely(f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax)) {
-		/*
-		 * If the frequency looks valid, set it.
-		 * This value is referred to in prepare_playback_urb().
-		 */
-		spin_lock_irqsave(&subs->lock, flags);
-		subs->freqm = f;
-		spin_unlock_irqrestore(&subs->lock, flags);
-	} else {
-		/*
-		 * Out of range; maybe the shift value is wrong.
-		 * Reset it so that we autodetect again the next time.
-		 */
-		subs->freqshift = INT_MIN;
-	}
-
-	return 0;
-}
-
-/* determine the number of frames in the next packet */
-static int snd_usb_audio_next_packet_size(struct snd_usb_substream *subs)
-{
-	if (subs->fill_max)
-		return subs->maxframesize;
-	else {
-		subs->phase = (subs->phase & 0xffff)
-			+ (subs->freqm << subs->datainterval);
-		return min(subs->phase >> 16, subs->maxframesize);
-	}
-}
-
-/*
- * Prepare urb for streaming before playback starts or when paused.
- *
- * We don't have any data, so we send silence.
- */
-static int prepare_nodata_playback_urb(struct snd_usb_substream *subs,
-				       struct snd_pcm_runtime *runtime,
-				       struct urb *urb)
-{
-	unsigned int i, offs, counts;
-	struct snd_urb_ctx *ctx = urb->context;
-	int stride = runtime->frame_bits >> 3;
-
-	offs = 0;
-	urb->dev = ctx->subs->dev;
-	for (i = 0; i < ctx->packets; ++i) {
-		counts = snd_usb_audio_next_packet_size(subs);
-		urb->iso_frame_desc[i].offset = offs * stride;
-		urb->iso_frame_desc[i].length = counts * stride;
-		offs += counts;
-	}
-	urb->number_of_packets = ctx->packets;
-	urb->transfer_buffer_length = offs * stride;
-	memset(urb->transfer_buffer,
-	       runtime->format == SNDRV_PCM_FORMAT_U8 ? 0x80 : 0,
-	       offs * stride);
-	return 0;
-}
-
-/*
- * prepare urb for playback data pipe
- *
- * Since a URB can handle only a single linear buffer, we must use double
- * buffering when the data to be transferred overflows the buffer boundary.
- * To avoid inconsistencies when updating hwptr_done, we use double buffering
- * for all URBs.
- */
-static int prepare_playback_urb(struct snd_usb_substream *subs,
-				struct snd_pcm_runtime *runtime,
-				struct urb *urb)
-{
-	int i, stride;
-	unsigned int counts, frames, bytes;
-	unsigned long flags;
-	int period_elapsed = 0;
-	struct snd_urb_ctx *ctx = urb->context;
-
-	stride = runtime->frame_bits >> 3;
-
-	frames = 0;
-	urb->dev = ctx->subs->dev; /* we need to set this at each time */
-	urb->number_of_packets = 0;
-	spin_lock_irqsave(&subs->lock, flags);
-	for (i = 0; i < ctx->packets; i++) {
-		counts = snd_usb_audio_next_packet_size(subs);
-		/* set up descriptor */
-		urb->iso_frame_desc[i].offset = frames * stride;
-		urb->iso_frame_desc[i].length = counts * stride;
-		frames += counts;
-		urb->number_of_packets++;
-		subs->transfer_done += counts;
-		if (subs->transfer_done >= runtime->period_size) {
-			subs->transfer_done -= runtime->period_size;
-			period_elapsed = 1;
-			if (subs->fmt_type == UAC_FORMAT_TYPE_II) {
-				if (subs->transfer_done > 0) {
-					/* FIXME: fill-max mode is not
-					 * supported yet */
-					frames -= subs->transfer_done;
-					counts -= subs->transfer_done;
-					urb->iso_frame_desc[i].length =
-						counts * stride;
-					subs->transfer_done = 0;
-				}
-				i++;
-				if (i < ctx->packets) {
-					/* add a transfer delimiter */
-					urb->iso_frame_desc[i].offset =
-						frames * stride;
-					urb->iso_frame_desc[i].length = 0;
-					urb->number_of_packets++;
-				}
-				break;
-			}
-		}
-		if (period_elapsed) /* finish at the period boundary */
-			break;
-	}
-	bytes = frames * stride;
-	if (subs->hwptr_done + bytes > runtime->buffer_size * stride) {
-		/* err, the transferred area goes over buffer boundary. */
-		unsigned int bytes1 =
-			runtime->buffer_size * stride - subs->hwptr_done;
-		memcpy(urb->transfer_buffer,
-		       runtime->dma_area + subs->hwptr_done, bytes1);
-		memcpy(urb->transfer_buffer + bytes1,
-		       runtime->dma_area, bytes - bytes1);
-	} else {
-		memcpy(urb->transfer_buffer,
-		       runtime->dma_area + subs->hwptr_done, bytes);
-	}
-	subs->hwptr_done += bytes;
-	if (subs->hwptr_done >= runtime->buffer_size * stride)
-		subs->hwptr_done -= runtime->buffer_size * stride;
-
-	/* update delay with exact number of samples queued */
-	runtime->delay = subs->last_delay;
-	runtime->delay += frames;
-	subs->last_delay = runtime->delay;
-
-	/* realign last_frame_number */
-	subs->last_frame_number = usb_get_current_frame_number(subs->dev);
-	subs->last_frame_number &= 0xFF; /* keep 8 LSBs */
-
-	spin_unlock_irqrestore(&subs->lock, flags);
-	urb->transfer_buffer_length = bytes;
-	if (period_elapsed)
-		snd_pcm_period_elapsed(subs->pcm_substream);
-	return 0;
-}
-
-/*
- * process after playback data complete
- * - decrease the delay count again
- */
-static int retire_playback_urb(struct snd_usb_substream *subs,
-			       struct snd_pcm_runtime *runtime,
-			       struct urb *urb)
-{
-	unsigned long flags;
-	int stride = runtime->frame_bits >> 3;
-	int processed = urb->transfer_buffer_length / stride;
-	int est_delay;
-
-	spin_lock_irqsave(&subs->lock, flags);
-
-	est_delay = snd_usb_pcm_delay(subs, runtime->rate);
-	/* update delay with exact number of samples played */
-	if (processed > subs->last_delay)
-		subs->last_delay = 0;
-	else
-		subs->last_delay -= processed;
-	runtime->delay = subs->last_delay;
-
-	/*
-	 * Report when delay estimate is off by more than 2ms.
-	 * The error should be lower than 2ms since the estimate relies
-	 * on two reads of a counter updated every ms.
-	 */
-	if (abs(est_delay - subs->last_delay) * 1000 > runtime->rate * 2)
-		snd_printk(KERN_DEBUG "delay: estimated %d, actual %d\n",
-			est_delay, subs->last_delay);
-
-	spin_unlock_irqrestore(&subs->lock, flags);
-	return 0;
+	if (u->buffer_size)
+		usb_free_coherent(u->ep->chip->dev, u->buffer_size,
+				  u->urb->transfer_buffer,
+				  u->urb->transfer_dma);
+	usb_free_urb(u->urb);
+	u->urb = NULL;
 }
 
 static const char *usb_error_string(int err)
@@ -797,170 +118,1044 @@
 	}
 }
 
-/*
- * set up and start data/sync urbs
+/**
+ * snd_usb_endpoint_implicit_feedback_sink: Report endpoint usage type
+ *
+ * @ep: The snd_usb_endpoint
+ *
+ * Determine whether an endpoint is driven by an implicit feedback
+ * data endpoint source.
  */
-static int start_urbs(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime)
+int snd_usb_endpoint_implict_feedback_sink(struct snd_usb_endpoint *ep)
 {
-	unsigned int i;
+	return  ep->sync_master &&
+		ep->sync_master->type == SND_USB_ENDPOINT_TYPE_DATA &&
+		ep->type == SND_USB_ENDPOINT_TYPE_DATA &&
+		usb_pipeout(ep->pipe);
+}
+
+/*
+ * For streaming based on information derived from sync endpoints,
+ * prepare_outbound_urb_sizes() will call next_packet_size() to
+ * determine the number of samples to be sent in the next packet.
+ *
+ * For implicit feedback, next_packet_size() is unused.
+ */
+static int next_packet_size(struct snd_usb_endpoint *ep)
+{
+	unsigned long flags;
+	int ret;
+
+	if (ep->fill_max)
+		return ep->maxframesize;
+
+	spin_lock_irqsave(&ep->lock, flags);
+	ep->phase = (ep->phase & 0xffff)
+		+ (ep->freqm << ep->datainterval);
+	ret = min(ep->phase >> 16, ep->maxframesize);
+	spin_unlock_irqrestore(&ep->lock, flags);
+
+	return ret;
+}
+
+static void retire_outbound_urb(struct snd_usb_endpoint *ep,
+				struct snd_urb_ctx *urb_ctx)
+{
+	if (ep->retire_data_urb)
+		ep->retire_data_urb(ep->data_subs, urb_ctx->urb);
+}
+
+static void retire_inbound_urb(struct snd_usb_endpoint *ep,
+			       struct snd_urb_ctx *urb_ctx)
+{
+	struct urb *urb = urb_ctx->urb;
+
+	if (ep->sync_slave)
+		snd_usb_handle_sync_urb(ep->sync_slave, ep, urb);
+
+	if (ep->retire_data_urb)
+		ep->retire_data_urb(ep->data_subs, urb);
+}
+
+static void prepare_outbound_urb_sizes(struct snd_usb_endpoint *ep,
+				       struct snd_urb_ctx *ctx)
+{
+	int i;
+
+	for (i = 0; i < ctx->packets; ++i)
+		ctx->packet_size[i] = next_packet_size(ep);
+}
+
+/*
+ * Prepare a PLAYBACK urb for submission to the bus.
+ */
+static void prepare_outbound_urb(struct snd_usb_endpoint *ep,
+				 struct snd_urb_ctx *ctx)
+{
+	int i;
+	struct urb *urb = ctx->urb;
+	unsigned char *cp = urb->transfer_buffer;
+
+	urb->dev = ep->chip->dev; /* we need to set this at each time */
+
+	switch (ep->type) {
+	case SND_USB_ENDPOINT_TYPE_DATA:
+		if (ep->prepare_data_urb) {
+			ep->prepare_data_urb(ep->data_subs, urb);
+		} else {
+			/* no data provider, so send silence */
+			unsigned int offs = 0;
+			for (i = 0; i < ctx->packets; ++i) {
+				int counts = ctx->packet_size[i];
+				urb->iso_frame_desc[i].offset = offs * ep->stride;
+				urb->iso_frame_desc[i].length = counts * ep->stride;
+				offs += counts;
+			}
+
+			urb->number_of_packets = ctx->packets;
+			urb->transfer_buffer_length = offs * ep->stride;
+			memset(urb->transfer_buffer, ep->silence_value,
+			       offs * ep->stride);
+		}
+		break;
+
+	case SND_USB_ENDPOINT_TYPE_SYNC:
+		if (snd_usb_get_speed(ep->chip->dev) >= USB_SPEED_HIGH) {
+			/*
+			 * fill the length and offset of each urb descriptor.
+			 * the fixed 12.13 frequency is passed as 16.16 through the pipe.
+			 */
+			urb->iso_frame_desc[0].length = 4;
+			urb->iso_frame_desc[0].offset = 0;
+			cp[0] = ep->freqn;
+			cp[1] = ep->freqn >> 8;
+			cp[2] = ep->freqn >> 16;
+			cp[3] = ep->freqn >> 24;
+		} else {
+			/*
+			 * fill the length and offset of each urb descriptor.
+			 * the fixed 10.14 frequency is passed through the pipe.
+			 */
+			urb->iso_frame_desc[0].length = 3;
+			urb->iso_frame_desc[0].offset = 0;
+			cp[0] = ep->freqn >> 2;
+			cp[1] = ep->freqn >> 10;
+			cp[2] = ep->freqn >> 18;
+		}
+
+		break;
+	}
+}
+
+/*
+ * Prepare a CAPTURE or SYNC urb for submission to the bus.
+ */
+static inline void prepare_inbound_urb(struct snd_usb_endpoint *ep,
+				       struct snd_urb_ctx *urb_ctx)
+{
+	int i, offs;
+	struct urb *urb = urb_ctx->urb;
+
+	urb->dev = ep->chip->dev; /* we need to set this at each time */
+
+	switch (ep->type) {
+	case SND_USB_ENDPOINT_TYPE_DATA:
+		offs = 0;
+		for (i = 0; i < urb_ctx->packets; i++) {
+			urb->iso_frame_desc[i].offset = offs;
+			urb->iso_frame_desc[i].length = ep->curpacksize;
+			offs += ep->curpacksize;
+		}
+
+		urb->transfer_buffer_length = offs;
+		urb->number_of_packets = urb_ctx->packets;
+		break;
+
+	case SND_USB_ENDPOINT_TYPE_SYNC:
+		urb->iso_frame_desc[0].length = min(4u, ep->syncmaxsize);
+		urb->iso_frame_desc[0].offset = 0;
+		break;
+	}
+}
+
+/*
+ * Send output urbs that have been prepared previously. URBs are dequeued
+ * from ep->ready_playback_urbs and in case there there aren't any available
+ * or there are no packets that have been prepared, this function does
+ * nothing.
+ *
+ * The reason why the functionality of sending and preparing URBs is separated
+ * is that host controllers don't guarantee the order in which they return
+ * inbound and outbound packets to their submitters.
+ *
+ * This function is only used for implicit feedback endpoints. For endpoints
+ * driven by dedicated sync endpoints, URBs are immediately re-submitted
+ * from their completion handler.
+ */
+static void queue_pending_output_urbs(struct snd_usb_endpoint *ep)
+{
+	while (test_bit(EP_FLAG_RUNNING, &ep->flags)) {
+
+		unsigned long flags;
+		struct snd_usb_packet_info *uninitialized_var(packet);
+		struct snd_urb_ctx *ctx = NULL;
+		struct urb *urb;
+		int err, i;
+
+		spin_lock_irqsave(&ep->lock, flags);
+		if (ep->next_packet_read_pos != ep->next_packet_write_pos) {
+			packet = ep->next_packet + ep->next_packet_read_pos;
+			ep->next_packet_read_pos++;
+			ep->next_packet_read_pos %= MAX_URBS;
+
+			/* take URB out of FIFO */
+			if (!list_empty(&ep->ready_playback_urbs))
+				ctx = list_first_entry(&ep->ready_playback_urbs,
+					       struct snd_urb_ctx, ready_list);
+		}
+		spin_unlock_irqrestore(&ep->lock, flags);
+
+		if (ctx == NULL)
+			return;
+
+		list_del_init(&ctx->ready_list);
+		urb = ctx->urb;
+
+		/* copy over the length information */
+		for (i = 0; i < packet->packets; i++)
+			ctx->packet_size[i] = packet->packet_size[i];
+
+		/* call the data handler to fill in playback data */
+		prepare_outbound_urb(ep, ctx);
+
+		err = usb_submit_urb(ctx->urb, GFP_ATOMIC);
+		if (err < 0)
+			snd_printk(KERN_ERR "Unable to submit urb #%d: %d (urb %p)\n",
+				   ctx->index, err, ctx->urb);
+		else
+			set_bit(ctx->index, &ep->active_mask);
+	}
+}
+
+/*
+ * complete callback for urbs
+ */
+static void snd_complete_urb(struct urb *urb)
+{
+	struct snd_urb_ctx *ctx = urb->context;
+	struct snd_usb_endpoint *ep = ctx->ep;
 	int err;
 
-	if (subs->stream->chip->shutdown)
-		return -EBADFD;
+	if (unlikely(urb->status == -ENOENT ||		/* unlinked */
+		     urb->status == -ENODEV ||		/* device removed */
+		     urb->status == -ECONNRESET ||	/* unlinked */
+		     urb->status == -ESHUTDOWN ||	/* device disabled */
+		     ep->chip->shutdown))		/* device disconnected */
+		goto exit_clear;
 
-	for (i = 0; i < subs->nurbs; i++) {
-		if (snd_BUG_ON(!subs->dataurb[i].urb))
-			return -EINVAL;
-		if (subs->ops.prepare(subs, runtime, subs->dataurb[i].urb) < 0) {
-			snd_printk(KERN_ERR "cannot prepare datapipe for urb %d\n", i);
-			goto __error;
+	if (usb_pipeout(ep->pipe)) {
+		retire_outbound_urb(ep, ctx);
+		/* can be stopped during retire callback */
+		if (unlikely(!test_bit(EP_FLAG_RUNNING, &ep->flags)))
+			goto exit_clear;
+
+		if (snd_usb_endpoint_implict_feedback_sink(ep)) {
+			unsigned long flags;
+
+			spin_lock_irqsave(&ep->lock, flags);
+			list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs);
+			spin_unlock_irqrestore(&ep->lock, flags);
+			queue_pending_output_urbs(ep);
+
+			goto exit_clear;
+		}
+
+		prepare_outbound_urb_sizes(ep, ctx);
+		prepare_outbound_urb(ep, ctx);
+	} else {
+		retire_inbound_urb(ep, ctx);
+		/* can be stopped during retire callback */
+		if (unlikely(!test_bit(EP_FLAG_RUNNING, &ep->flags)))
+			goto exit_clear;
+
+		prepare_inbound_urb(ep, ctx);
+	}
+
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err == 0)
+		return;
+
+	snd_printk(KERN_ERR "cannot submit urb (err = %d)\n", err);
+	//snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+
+exit_clear:
+	clear_bit(ctx->index, &ep->active_mask);
+}
+
+/**
+ * snd_usb_add_endpoint: Add an endpoint to an USB audio chip
+ *
+ * @chip: The chip
+ * @alts: The USB host interface
+ * @ep_num: The number of the endpoint to use
+ * @direction: SNDRV_PCM_STREAM_PLAYBACK or SNDRV_PCM_STREAM_CAPTURE
+ * @type: SND_USB_ENDPOINT_TYPE_DATA or SND_USB_ENDPOINT_TYPE_SYNC
+ *
+ * If the requested endpoint has not been added to the given chip before,
+ * a new instance is created. Otherwise, a pointer to the previoulsy
+ * created instance is returned. In case of any error, NULL is returned.
+ *
+ * New endpoints will be added to chip->ep_list and must be freed by
+ * calling snd_usb_endpoint_free().
+ */
+struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip,
+					      struct usb_host_interface *alts,
+					      int ep_num, int direction, int type)
+{
+	struct list_head *p;
+	struct snd_usb_endpoint *ep;
+	int ret, is_playback = direction == SNDRV_PCM_STREAM_PLAYBACK;
+
+	mutex_lock(&chip->mutex);
+
+	list_for_each(p, &chip->ep_list) {
+		ep = list_entry(p, struct snd_usb_endpoint, list);
+		if (ep->ep_num == ep_num &&
+		    ep->iface == alts->desc.bInterfaceNumber &&
+		    ep->alt_idx == alts->desc.bAlternateSetting) {
+			snd_printdd(KERN_DEBUG "Re-using EP %x in iface %d,%d @%p\n",
+					ep_num, ep->iface, ep->alt_idx, ep);
+			goto __exit_unlock;
 		}
 	}
-	if (subs->syncpipe) {
-		for (i = 0; i < SYNC_URBS; i++) {
-			if (snd_BUG_ON(!subs->syncurb[i].urb))
-				return -EINVAL;
-			if (subs->ops.prepare_sync(subs, runtime, subs->syncurb[i].urb) < 0) {
-				snd_printk(KERN_ERR "cannot prepare syncpipe for urb %d\n", i);
-				goto __error;
+
+	snd_printdd(KERN_DEBUG "Creating new %s %s endpoint #%x\n",
+		    is_playback ? "playback" : "capture",
+		    type == SND_USB_ENDPOINT_TYPE_DATA ? "data" : "sync",
+		    ep_num);
+
+	/* select the alt setting once so the endpoints become valid */
+	ret = usb_set_interface(chip->dev, alts->desc.bInterfaceNumber,
+				alts->desc.bAlternateSetting);
+	if (ret < 0) {
+		snd_printk(KERN_ERR "%s(): usb_set_interface() failed, ret = %d\n",
+					__func__, ret);
+		ep = NULL;
+		goto __exit_unlock;
+	}
+
+	ep = kzalloc(sizeof(*ep), GFP_KERNEL);
+	if (!ep)
+		goto __exit_unlock;
+
+	ep->chip = chip;
+	spin_lock_init(&ep->lock);
+	ep->type = type;
+	ep->ep_num = ep_num;
+	ep->iface = alts->desc.bInterfaceNumber;
+	ep->alt_idx = alts->desc.bAlternateSetting;
+	INIT_LIST_HEAD(&ep->ready_playback_urbs);
+	ep_num &= USB_ENDPOINT_NUMBER_MASK;
+
+	if (is_playback)
+		ep->pipe = usb_sndisocpipe(chip->dev, ep_num);
+	else
+		ep->pipe = usb_rcvisocpipe(chip->dev, ep_num);
+
+	if (type == SND_USB_ENDPOINT_TYPE_SYNC) {
+		if (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
+		    get_endpoint(alts, 1)->bRefresh >= 1 &&
+		    get_endpoint(alts, 1)->bRefresh <= 9)
+			ep->syncinterval = get_endpoint(alts, 1)->bRefresh;
+		else if (snd_usb_get_speed(chip->dev) == USB_SPEED_FULL)
+			ep->syncinterval = 1;
+		else if (get_endpoint(alts, 1)->bInterval >= 1 &&
+			 get_endpoint(alts, 1)->bInterval <= 16)
+			ep->syncinterval = get_endpoint(alts, 1)->bInterval - 1;
+		else
+			ep->syncinterval = 3;
+
+		ep->syncmaxsize = le16_to_cpu(get_endpoint(alts, 1)->wMaxPacketSize);
+	}
+
+	list_add_tail(&ep->list, &chip->ep_list);
+
+__exit_unlock:
+	mutex_unlock(&chip->mutex);
+
+	return ep;
+}
+
+/*
+ *  wait until all urbs are processed.
+ */
+static int wait_clear_urbs(struct snd_usb_endpoint *ep)
+{
+	unsigned long end_time = jiffies + msecs_to_jiffies(1000);
+	unsigned int i;
+	int alive;
+
+	do {
+		alive = 0;
+		for (i = 0; i < ep->nurbs; i++)
+			if (test_bit(i, &ep->active_mask))
+				alive++;
+
+		if (!alive)
+			break;
+
+		schedule_timeout_uninterruptible(1);
+	} while (time_before(jiffies, end_time));
+
+	if (alive)
+		snd_printk(KERN_ERR "timeout: still %d active urbs on EP #%x\n",
+					alive, ep->ep_num);
+
+	return 0;
+}
+
+/*
+ * unlink active urbs.
+ */
+static int deactivate_urbs(struct snd_usb_endpoint *ep, int force, int can_sleep)
+{
+	unsigned int i;
+	int async;
+
+	if (!force && ep->chip->shutdown) /* to be sure... */
+		return -EBADFD;
+
+	async = !can_sleep && ep->chip->async_unlink;
+
+	clear_bit(EP_FLAG_RUNNING, &ep->flags);
+
+	INIT_LIST_HEAD(&ep->ready_playback_urbs);
+	ep->next_packet_read_pos = 0;
+	ep->next_packet_write_pos = 0;
+
+	if (!async && in_interrupt())
+		return 0;
+
+	for (i = 0; i < ep->nurbs; i++) {
+		if (test_bit(i, &ep->active_mask)) {
+			if (!test_and_set_bit(i, &ep->unlink_mask)) {
+				struct urb *u = ep->urb[i].urb;
+				if (async)
+					usb_unlink_urb(u);
+				else
+					usb_kill_urb(u);
 			}
 		}
 	}
 
-	subs->active_mask = 0;
-	subs->unlink_mask = 0;
-	subs->running = 1;
-	for (i = 0; i < subs->nurbs; i++) {
-		err = usb_submit_urb(subs->dataurb[i].urb, GFP_ATOMIC);
+	return 0;
+}
+
+/*
+ * release an endpoint's urbs
+ */
+static void release_urbs(struct snd_usb_endpoint *ep, int force)
+{
+	int i;
+
+	/* route incoming urbs to nirvana */
+	ep->retire_data_urb = NULL;
+	ep->prepare_data_urb = NULL;
+
+	/* stop urbs */
+	deactivate_urbs(ep, force, 1);
+	wait_clear_urbs(ep);
+
+	for (i = 0; i < ep->nurbs; i++)
+		release_urb_ctx(&ep->urb[i]);
+
+	if (ep->syncbuf)
+		usb_free_coherent(ep->chip->dev, SYNC_URBS * 4,
+				  ep->syncbuf, ep->sync_dma);
+
+	ep->syncbuf = NULL;
+	ep->nurbs = 0;
+}
+
+/*
+ * configure a data endpoint
+ */
+static int data_ep_set_params(struct snd_usb_endpoint *ep,
+			      struct snd_pcm_hw_params *hw_params,
+			      struct audioformat *fmt,
+			      struct snd_usb_endpoint *sync_ep)
+{
+	unsigned int maxsize, i, urb_packs, total_packs, packs_per_ms;
+	int period_bytes = params_period_bytes(hw_params);
+	int format = params_format(hw_params);
+	int is_playback = usb_pipeout(ep->pipe);
+	int frame_bits = snd_pcm_format_physical_width(params_format(hw_params)) *
+							params_channels(hw_params);
+
+	ep->datainterval = fmt->datainterval;
+	ep->stride = frame_bits >> 3;
+	ep->silence_value = format == SNDRV_PCM_FORMAT_U8 ? 0x80 : 0;
+
+	/* calculate max. frequency */
+	if (ep->maxpacksize) {
+		/* whatever fits into a max. size packet */
+		maxsize = ep->maxpacksize;
+		ep->freqmax = (maxsize / (frame_bits >> 3))
+				<< (16 - ep->datainterval);
+	} else {
+		/* no max. packet size: just take 25% higher than nominal */
+		ep->freqmax = ep->freqn + (ep->freqn >> 2);
+		maxsize = ((ep->freqmax + 0xffff) * (frame_bits >> 3))
+				>> (16 - ep->datainterval);
+	}
+
+	if (ep->fill_max)
+		ep->curpacksize = ep->maxpacksize;
+	else
+		ep->curpacksize = maxsize;
+
+	if (snd_usb_get_speed(ep->chip->dev) != USB_SPEED_FULL)
+		packs_per_ms = 8 >> ep->datainterval;
+	else
+		packs_per_ms = 1;
+
+	if (is_playback && !snd_usb_endpoint_implict_feedback_sink(ep)) {
+		urb_packs = max(ep->chip->nrpacks, 1);
+		urb_packs = min(urb_packs, (unsigned int) MAX_PACKS);
+	} else {
+		urb_packs = 1;
+	}
+
+	urb_packs *= packs_per_ms;
+
+	if (sync_ep && !snd_usb_endpoint_implict_feedback_sink(ep))
+		urb_packs = min(urb_packs, 1U << sync_ep->syncinterval);
+
+	/* decide how many packets to be used */
+	if (is_playback && !snd_usb_endpoint_implict_feedback_sink(ep)) {
+		unsigned int minsize, maxpacks;
+		/* determine how small a packet can be */
+		minsize = (ep->freqn >> (16 - ep->datainterval))
+			  * (frame_bits >> 3);
+		/* with sync from device, assume it can be 12% lower */
+		if (sync_ep)
+			minsize -= minsize >> 3;
+		minsize = max(minsize, 1u);
+		total_packs = (period_bytes + minsize - 1) / minsize;
+		/* we need at least two URBs for queueing */
+		if (total_packs < 2) {
+			total_packs = 2;
+		} else {
+			/* and we don't want too long a queue either */
+			maxpacks = max(MAX_QUEUE * packs_per_ms, urb_packs * 2);
+			total_packs = min(total_packs, maxpacks);
+		}
+	} else {
+		while (urb_packs > 1 && urb_packs * maxsize >= period_bytes)
+			urb_packs >>= 1;
+		total_packs = MAX_URBS * urb_packs;
+	}
+
+	ep->nurbs = (total_packs + urb_packs - 1) / urb_packs;
+	if (ep->nurbs > MAX_URBS) {
+		/* too much... */
+		ep->nurbs = MAX_URBS;
+		total_packs = MAX_URBS * urb_packs;
+	} else if (ep->nurbs < 2) {
+		/* too little - we need at least two packets
+		 * to ensure contiguous playback/capture
+		 */
+		ep->nurbs = 2;
+	}
+
+	/* allocate and initialize data urbs */
+	for (i = 0; i < ep->nurbs; i++) {
+		struct snd_urb_ctx *u = &ep->urb[i];
+		u->index = i;
+		u->ep = ep;
+		u->packets = (i + 1) * total_packs / ep->nurbs
+			- i * total_packs / ep->nurbs;
+		u->buffer_size = maxsize * u->packets;
+
+		if (fmt->fmt_type == UAC_FORMAT_TYPE_II)
+			u->packets++; /* for transfer delimiter */
+		u->urb = usb_alloc_urb(u->packets, GFP_KERNEL);
+		if (!u->urb)
+			goto out_of_memory;
+
+		u->urb->transfer_buffer =
+			usb_alloc_coherent(ep->chip->dev, u->buffer_size,
+					   GFP_KERNEL, &u->urb->transfer_dma);
+		if (!u->urb->transfer_buffer)
+			goto out_of_memory;
+		u->urb->pipe = ep->pipe;
+		u->urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
+		u->urb->interval = 1 << ep->datainterval;
+		u->urb->context = u;
+		u->urb->complete = snd_complete_urb;
+		INIT_LIST_HEAD(&u->ready_list);
+	}
+
+	return 0;
+
+out_of_memory:
+	release_urbs(ep, 0);
+	return -ENOMEM;
+}
+
+/*
+ * configure a sync endpoint
+ */
+static int sync_ep_set_params(struct snd_usb_endpoint *ep,
+			      struct snd_pcm_hw_params *hw_params,
+			      struct audioformat *fmt)
+{
+	int i;
+
+	ep->syncbuf = usb_alloc_coherent(ep->chip->dev, SYNC_URBS * 4,
+					 GFP_KERNEL, &ep->sync_dma);
+	if (!ep->syncbuf)
+		return -ENOMEM;
+
+	for (i = 0; i < SYNC_URBS; i++) {
+		struct snd_urb_ctx *u = &ep->urb[i];
+		u->index = i;
+		u->ep = ep;
+		u->packets = 1;
+		u->urb = usb_alloc_urb(1, GFP_KERNEL);
+		if (!u->urb)
+			goto out_of_memory;
+		u->urb->transfer_buffer = ep->syncbuf + i * 4;
+		u->urb->transfer_dma = ep->sync_dma + i * 4;
+		u->urb->transfer_buffer_length = 4;
+		u->urb->pipe = ep->pipe;
+		u->urb->transfer_flags = URB_ISO_ASAP |
+					 URB_NO_TRANSFER_DMA_MAP;
+		u->urb->number_of_packets = 1;
+		u->urb->interval = 1 << ep->syncinterval;
+		u->urb->context = u;
+		u->urb->complete = snd_complete_urb;
+	}
+
+	ep->nurbs = SYNC_URBS;
+
+	return 0;
+
+out_of_memory:
+	release_urbs(ep, 0);
+	return -ENOMEM;
+}
+
+/**
+ * snd_usb_endpoint_set_params: configure an snd_usb_endpoint
+ *
+ * @ep: the snd_usb_endpoint to configure
+ * @hw_params: the hardware parameters
+ * @fmt: the USB audio format information
+ * @sync_ep: the sync endpoint to use, if any
+ *
+ * Determine the number of URBs to be used on this endpoint.
+ * An endpoint must be configured before it can be started.
+ * An endpoint that is already running can not be reconfigured.
+ */
+int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep,
+				struct snd_pcm_hw_params *hw_params,
+				struct audioformat *fmt,
+				struct snd_usb_endpoint *sync_ep)
+{
+	int err;
+
+	if (ep->use_count != 0) {
+		snd_printk(KERN_WARNING "Unable to change format on ep #%x: already in use\n",
+			   ep->ep_num);
+		return -EBUSY;
+	}
+
+	/* release old buffers, if any */
+	release_urbs(ep, 0);
+
+	ep->datainterval = fmt->datainterval;
+	ep->maxpacksize = fmt->maxpacksize;
+	ep->fill_max = !!(fmt->attributes & UAC_EP_CS_ATTR_FILL_MAX);
+
+	if (snd_usb_get_speed(ep->chip->dev) == USB_SPEED_FULL)
+		ep->freqn = get_usb_full_speed_rate(params_rate(hw_params));
+	else
+		ep->freqn = get_usb_high_speed_rate(params_rate(hw_params));
+
+	/* calculate the frequency in 16.16 format */
+	ep->freqm = ep->freqn;
+	ep->freqshift = INT_MIN;
+
+	ep->phase = 0;
+
+	switch (ep->type) {
+	case  SND_USB_ENDPOINT_TYPE_DATA:
+		err = data_ep_set_params(ep, hw_params, fmt, sync_ep);
+		break;
+	case  SND_USB_ENDPOINT_TYPE_SYNC:
+		err = sync_ep_set_params(ep, hw_params, fmt);
+		break;
+	default:
+		err = -EINVAL;
+	}
+
+	snd_printdd(KERN_DEBUG "Setting params for ep #%x (type %d, %d urbs), ret=%d\n",
+		   ep->ep_num, ep->type, ep->nurbs, err);
+
+	return err;
+}
+
+/**
+ * snd_usb_endpoint_start: start an snd_usb_endpoint
+ *
+ * @ep: the endpoint to start
+ *
+ * A call to this function will increment the use count of the endpoint.
+ * In case it is not already running, the URBs for this endpoint will be
+ * submitted. Otherwise, this function does nothing.
+ *
+ * Must be balanced to calls of snd_usb_endpoint_stop().
+ *
+ * Returns an error if the URB submission failed, 0 in all other cases.
+ */
+int snd_usb_endpoint_start(struct snd_usb_endpoint *ep)
+{
+	int err;
+	unsigned int i;
+
+	if (ep->chip->shutdown)
+		return -EBADFD;
+
+	/* already running? */
+	if (++ep->use_count != 1)
+		return 0;
+
+	if (snd_BUG_ON(!test_bit(EP_FLAG_ACTIVATED, &ep->flags)))
+		return -EINVAL;
+
+	/* just to be sure */
+	deactivate_urbs(ep, 0, 1);
+	wait_clear_urbs(ep);
+
+	ep->active_mask = 0;
+	ep->unlink_mask = 0;
+	ep->phase = 0;
+
+	/*
+	 * If this endpoint has a data endpoint as implicit feedback source,
+	 * don't start the urbs here. Instead, mark them all as available,
+	 * wait for the record urbs to return and queue the playback urbs
+	 * from that context.
+	 */
+
+	set_bit(EP_FLAG_RUNNING, &ep->flags);
+
+	if (snd_usb_endpoint_implict_feedback_sink(ep)) {
+		for (i = 0; i < ep->nurbs; i++) {
+			struct snd_urb_ctx *ctx = ep->urb + i;
+			list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs);
+		}
+
+		return 0;
+	}
+
+	for (i = 0; i < ep->nurbs; i++) {
+		struct urb *urb = ep->urb[i].urb;
+
+		if (snd_BUG_ON(!urb))
+			goto __error;
+
+		if (usb_pipeout(ep->pipe)) {
+			prepare_outbound_urb_sizes(ep, urb->context);
+			prepare_outbound_urb(ep, urb->context);
+		} else {
+			prepare_inbound_urb(ep, urb->context);
+		}
+
+		err = usb_submit_urb(urb, GFP_ATOMIC);
 		if (err < 0) {
-			snd_printk(KERN_ERR "cannot submit datapipe "
-				   "for urb %d, error %d: %s\n",
+			snd_printk(KERN_ERR "cannot submit urb %d, error %d: %s\n",
 				   i, err, usb_error_string(err));
 			goto __error;
 		}
-		set_bit(i, &subs->active_mask);
+		set_bit(i, &ep->active_mask);
 	}
-	if (subs->syncpipe) {
-		for (i = 0; i < SYNC_URBS; i++) {
-			err = usb_submit_urb(subs->syncurb[i].urb, GFP_ATOMIC);
-			if (err < 0) {
-				snd_printk(KERN_ERR "cannot submit syncpipe "
-					   "for urb %d, error %d: %s\n",
-					   i, err, usb_error_string(err));
-				goto __error;
-			}
-			set_bit(i + 16, &subs->active_mask);
-		}
-	}
+
 	return 0;
 
- __error:
-	// snd_pcm_stop(subs->pcm_substream, SNDRV_PCM_STATE_XRUN);
-	deactivate_urbs(subs, 0, 0);
+__error:
+	clear_bit(EP_FLAG_RUNNING, &ep->flags);
+	ep->use_count--;
+	deactivate_urbs(ep, 0, 0);
 	return -EPIPE;
 }
 
-
-/*
+/**
+ * snd_usb_endpoint_stop: stop an snd_usb_endpoint
+ *
+ * @ep: the endpoint to stop (may be NULL)
+ *
+ * A call to this function will decrement the use count of the endpoint.
+ * In case the last user has requested the endpoint stop, the URBs will
+ * actually be deactivated.
+ *
+ * Must be balanced to calls of snd_usb_endpoint_start().
  */
-static struct snd_urb_ops audio_urb_ops[2] = {
-	{
-		.prepare =	prepare_nodata_playback_urb,
-		.retire =	retire_playback_urb,
-		.prepare_sync =	prepare_playback_sync_urb,
-		.retire_sync =	retire_playback_sync_urb,
-	},
-	{
-		.prepare =	prepare_capture_urb,
-		.retire =	retire_capture_urb,
-		.prepare_sync =	prepare_capture_sync_urb,
-		.retire_sync =	retire_capture_sync_urb,
-	},
-};
+void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep,
+			   int force, int can_sleep, int wait)
+{
+	if (!ep)
+		return;
 
-/*
- * initialize the substream instance.
+	if (snd_BUG_ON(ep->use_count == 0))
+		return;
+
+	if (snd_BUG_ON(!test_bit(EP_FLAG_ACTIVATED, &ep->flags)))
+		return;
+
+	if (--ep->use_count == 0) {
+		deactivate_urbs(ep, force, can_sleep);
+		ep->data_subs = NULL;
+		ep->sync_slave = NULL;
+		ep->retire_data_urb = NULL;
+		ep->prepare_data_urb = NULL;
+
+		if (wait)
+			wait_clear_urbs(ep);
+	}
+}
+
+/**
+ * snd_usb_endpoint_activate: activate an snd_usb_endpoint
+ *
+ * @ep: the endpoint to activate
+ *
+ * If the endpoint is not currently in use, this functions will select the
+ * correct alternate interface setting for the interface of this endpoint.
+ *
+ * In case of any active users, this functions does nothing.
+ *
+ * Returns an error if usb_set_interface() failed, 0 in all other
+ * cases.
  */
-
-void snd_usb_init_substream(struct snd_usb_stream *as,
-			    int stream, struct audioformat *fp)
+int snd_usb_endpoint_activate(struct snd_usb_endpoint *ep)
 {
-	struct snd_usb_substream *subs = &as->substream[stream];
-
-	INIT_LIST_HEAD(&subs->fmt_list);
-	spin_lock_init(&subs->lock);
-
-	subs->stream = as;
-	subs->direction = stream;
-	subs->dev = as->chip->dev;
-	subs->txfr_quirk = as->chip->txfr_quirk;
-	subs->ops = audio_urb_ops[stream];
-	if (snd_usb_get_speed(subs->dev) >= USB_SPEED_HIGH)
-		subs->ops.prepare_sync = prepare_capture_sync_urb_hs;
-
-	snd_usb_set_pcm_ops(as->pcm, stream);
-
-	list_add_tail(&fp->list, &subs->fmt_list);
-	subs->formats |= fp->formats;
-	subs->endpoint = fp->endpoint;
-	subs->num_formats++;
-	subs->fmt_type = fp->fmt_type;
-}
-
-int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substream, int cmd)
-{
-	struct snd_usb_substream *subs = substream->runtime->private_data;
-
-	switch (cmd) {
-	case SNDRV_PCM_TRIGGER_START:
-	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-		subs->ops.prepare = prepare_playback_urb;
+	if (ep->use_count != 0)
 		return 0;
-	case SNDRV_PCM_TRIGGER_STOP:
-		return deactivate_urbs(subs, 0, 0);
-	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-		subs->ops.prepare = prepare_nodata_playback_urb;
+
+	if (!ep->chip->shutdown &&
+	    !test_and_set_bit(EP_FLAG_ACTIVATED, &ep->flags)) {
+		int ret;
+
+		ret = usb_set_interface(ep->chip->dev, ep->iface, ep->alt_idx);
+		if (ret < 0) {
+			snd_printk(KERN_ERR "%s() usb_set_interface() failed, ret = %d\n",
+						__func__, ret);
+			clear_bit(EP_FLAG_ACTIVATED, &ep->flags);
+			return ret;
+		}
+
 		return 0;
 	}
 
-	return -EINVAL;
+	return -EBUSY;
 }
 
-int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+/**
+ * snd_usb_endpoint_deactivate: deactivate an snd_usb_endpoint
+ *
+ * @ep: the endpoint to deactivate
+ *
+ * If the endpoint is not currently in use, this functions will select the
+ * alternate interface setting 0 for the interface of this endpoint.
+ *
+ * In case of any active users, this functions does nothing.
+ *
+ * Returns an error if usb_set_interface() failed, 0 in all other
+ * cases.
+ */
+int snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep)
 {
-	struct snd_usb_substream *subs = substream->runtime->private_data;
+	if (!ep)
+		return -EINVAL;
 
-	switch (cmd) {
-	case SNDRV_PCM_TRIGGER_START:
-		subs->ops.retire = retire_capture_urb;
-		return start_urbs(subs, substream->runtime);
-	case SNDRV_PCM_TRIGGER_STOP:
-		return deactivate_urbs(subs, 0, 0);
-	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-		subs->ops.retire = retire_paused_capture_urb;
+	if (ep->use_count != 0)
 		return 0;
-	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-		subs->ops.retire = retire_capture_urb;
+
+	if (!ep->chip->shutdown &&
+	    test_and_clear_bit(EP_FLAG_ACTIVATED, &ep->flags)) {
+		int ret;
+
+		ret = usb_set_interface(ep->chip->dev, ep->iface, 0);
+		if (ret < 0) {
+			snd_printk(KERN_ERR "%s(): usb_set_interface() failed, ret = %d\n",
+						__func__, ret);
+			return ret;
+		}
+
 		return 0;
 	}
 
-	return -EINVAL;
+	return -EBUSY;
 }
 
-int snd_usb_substream_prepare(struct snd_usb_substream *subs,
-			      struct snd_pcm_runtime *runtime)
+/**
+ * snd_usb_endpoint_free: Free the resources of an snd_usb_endpoint
+ *
+ * @ep: the list header of the endpoint to free
+ *
+ * This function does not care for the endpoint's use count but will tear
+ * down all the streaming URBs immediately and free all resources.
+ */
+void snd_usb_endpoint_free(struct list_head *head)
 {
-	/* clear urbs (to be sure) */
-	deactivate_urbs(subs, 0, 1);
-	wait_clear_urbs(subs);
+	struct snd_usb_endpoint *ep;
 
-	/* for playback, submit the URBs now; otherwise, the first hwptr_done
-	 * updates for all URBs would happen at the same time when starting */
-	if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) {
-		subs->ops.prepare = prepare_nodata_playback_urb;
-		return start_urbs(subs, runtime);
+	ep = list_entry(head, struct snd_usb_endpoint, list);
+	release_urbs(ep, 1);
+	kfree(ep);
+}
+
+/**
+ * snd_usb_handle_sync_urb: parse an USB sync packet
+ *
+ * @ep: the endpoint to handle the packet
+ * @sender: the sending endpoint
+ * @urb: the received packet
+ *
+ * This function is called from the context of an endpoint that received
+ * the packet and is used to let another endpoint object handle the payload.
+ */
+void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep,
+			     struct snd_usb_endpoint *sender,
+			     const struct urb *urb)
+{
+	int shift;
+	unsigned int f;
+	unsigned long flags;
+
+	snd_BUG_ON(ep == sender);
+
+	/*
+	 * In case the endpoint is operating in implicit feedback mode, prepare
+	 * a new outbound URB that has the same layout as the received packet
+	 * and add it to the list of pending urbs. queue_pending_output_urbs()
+	 * will take care of them later.
+	 */
+	if (snd_usb_endpoint_implict_feedback_sink(ep) &&
+	    ep->use_count != 0) {
+
+		/* implicit feedback case */
+		int i, bytes = 0;
+		struct snd_urb_ctx *in_ctx;
+		struct snd_usb_packet_info *out_packet;
+
+		in_ctx = urb->context;
+
+		/* Count overall packet size */
+		for (i = 0; i < in_ctx->packets; i++)
+			if (urb->iso_frame_desc[i].status == 0)
+				bytes += urb->iso_frame_desc[i].actual_length;
+
+		/*
+		 * skip empty packets. At least M-Audio's Fast Track Ultra stops
+		 * streaming once it received a 0-byte OUT URB
+		 */
+		if (bytes == 0)
+			return;
+
+		spin_lock_irqsave(&ep->lock, flags);
+		out_packet = ep->next_packet + ep->next_packet_write_pos;
+
+		/*
+		 * Iterate through the inbound packet and prepare the lengths
+		 * for the output packet. The OUT packet we are about to send
+		 * will have the same amount of payload bytes than the IN
+		 * packet we just received.
+		 */
+
+		out_packet->packets = in_ctx->packets;
+		for (i = 0; i < in_ctx->packets; i++) {
+			if (urb->iso_frame_desc[i].status == 0)
+				out_packet->packet_size[i] =
+					urb->iso_frame_desc[i].actual_length / ep->stride;
+			else
+				out_packet->packet_size[i] = 0;
+		}
+
+		ep->next_packet_write_pos++;
+		ep->next_packet_write_pos %= MAX_URBS;
+		spin_unlock_irqrestore(&ep->lock, flags);
+		queue_pending_output_urbs(ep);
+
+		return;
 	}
 
-	return 0;
+	/*
+	 * process after playback sync complete
+	 *
+	 * Full speed devices report feedback values in 10.14 format as samples
+	 * per frame, high speed devices in 16.16 format as samples per
+	 * microframe.
+	 *
+	 * Because the Audio Class 1 spec was written before USB 2.0, many high
+	 * speed devices use a wrong interpretation, some others use an
+	 * entirely different format.
+	 *
+	 * Therefore, we cannot predict what format any particular device uses
+	 * and must detect it automatically.
+	 */
+
+	if (urb->iso_frame_desc[0].status != 0 ||
+	    urb->iso_frame_desc[0].actual_length < 3)
+		return;
+
+	f = le32_to_cpup(urb->transfer_buffer);
+	if (urb->iso_frame_desc[0].actual_length == 3)
+		f &= 0x00ffffff;
+	else
+		f &= 0x0fffffff;
+
+	if (f == 0)
+		return;
+
+	if (unlikely(ep->freqshift == INT_MIN)) {
+		/*
+		 * The first time we see a feedback value, determine its format
+		 * by shifting it left or right until it matches the nominal
+		 * frequency value.  This assumes that the feedback does not
+		 * differ from the nominal value more than +50% or -25%.
+		 */
+		shift = 0;
+		while (f < ep->freqn - ep->freqn / 4) {
+			f <<= 1;
+			shift++;
+		}
+		while (f > ep->freqn + ep->freqn / 2) {
+			f >>= 1;
+			shift--;
+		}
+		ep->freqshift = shift;
+	} else if (ep->freqshift >= 0)
+		f <<= ep->freqshift;
+	else
+		f >>= -ep->freqshift;
+
+	if (likely(f >= ep->freqn - ep->freqn / 8 && f <= ep->freqmax)) {
+		/*
+		 * If the frequency looks valid, set it.
+		 * This value is referred to in prepare_playback_urb().
+		 */
+		spin_lock_irqsave(&ep->lock, flags);
+		ep->freqm = f;
+		spin_unlock_irqrestore(&ep->lock, flags);
+	} else {
+		/*
+		 * Out of range; maybe the shift value is wrong.
+		 * Reset it so that we autodetect again the next time.
+		 */
+		ep->freqshift = INT_MIN;
+	}
 }
 
diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h
index 88eb63a..ee2723f 100644
--- a/sound/usb/endpoint.h
+++ b/sound/usb/endpoint.h
@@ -1,21 +1,29 @@
 #ifndef __USBAUDIO_ENDPOINT_H
 #define __USBAUDIO_ENDPOINT_H
 
-void snd_usb_init_substream(struct snd_usb_stream *as,
-			    int stream,
-			    struct audioformat *fp);
+#define SND_USB_ENDPOINT_TYPE_DATA     0
+#define SND_USB_ENDPOINT_TYPE_SYNC     1
 
-int snd_usb_init_substream_urbs(struct snd_usb_substream *subs,
-				unsigned int period_bytes,
-				unsigned int rate,
-				unsigned int frame_bits);
+struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip,
+					      struct usb_host_interface *alts,
+					      int ep_num, int direction, int type);
 
-void snd_usb_release_substream_urbs(struct snd_usb_substream *subs, int force);
+int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep,
+				struct snd_pcm_hw_params *hw_params,
+				struct audioformat *fmt,
+				struct snd_usb_endpoint *sync_ep);
 
-int snd_usb_substream_prepare(struct snd_usb_substream *subs,
-			      struct snd_pcm_runtime *runtime);
+int  snd_usb_endpoint_start(struct snd_usb_endpoint *ep);
+void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep,
+			   int force, int can_sleep, int wait);
+int  snd_usb_endpoint_activate(struct snd_usb_endpoint *ep);
+int  snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep);
+void snd_usb_endpoint_free(struct list_head *head);
 
-int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substream, int cmd);
-int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream, int cmd);
+int snd_usb_endpoint_implict_feedback_sink(struct snd_usb_endpoint *ep);
+
+void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep,
+			     struct snd_usb_endpoint *sender,
+			     const struct urb *urb);
 
 #endif /* __USBAUDIO_ENDPOINT_H */
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index ab23869..4f40ba8 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -486,7 +486,7 @@
 /*
  * TLV callback for mixer volume controls
  */
-static int mixer_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+int snd_usb_mixer_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag,
 			 unsigned int size, unsigned int __user *_tlv)
 {
 	struct usb_mixer_elem_info *cval = kcontrol->private_data;
@@ -770,6 +770,26 @@
 				  struct snd_kcontrol *kctl)
 {
 	switch (cval->mixer->chip->usb_id) {
+	case USB_ID(0x0763, 0x2081): /* M-Audio Fast Track Ultra 8R */
+	case USB_ID(0x0763, 0x2080): /* M-Audio Fast Track Ultra */
+		if (strcmp(kctl->id.name, "Effect Duration") == 0) {
+			snd_printk(KERN_INFO
+				"usb-audio: set quirk for FTU Effect Duration\n");
+			cval->min = 0x0000;
+			cval->max = 0x7f00;
+			cval->res = 0x0100;
+			break;
+		}
+		if (strcmp(kctl->id.name, "Effect Volume") == 0 ||
+		    strcmp(kctl->id.name, "Effect Feedback Volume") == 0) {
+			snd_printk(KERN_INFO
+				"usb-audio: set quirks for FTU Effect Feedback/Volume\n");
+			cval->min = 0x00;
+			cval->max = 0x7f;
+			break;
+		}
+		break;
+
 	case USB_ID(0x0471, 0x0101):
 	case USB_ID(0x0471, 0x0104):
 	case USB_ID(0x0471, 0x0105):
@@ -1121,9 +1141,6 @@
 		len = snd_usb_copy_string_desc(state, nameid,
 				kctl->id.name, sizeof(kctl->id.name));
 
-	/* get min/max values */
-	get_min_max_with_quirks(cval, 0, kctl);
-
 	switch (control) {
 	case UAC_FU_MUTE:
 	case UAC_FU_VOLUME:
@@ -1155,17 +1172,7 @@
 		}
 		append_ctl_name(kctl, control == UAC_FU_MUTE ?
 				" Switch" : " Volume");
-		if (control == UAC_FU_VOLUME) {
-			check_mapped_dB(map, cval);
-			if (cval->dBmin < cval->dBmax || !cval->initialized) {
-				kctl->tlv.c = mixer_vol_tlv;
-				kctl->vd[0].access |= 
-					SNDRV_CTL_ELEM_ACCESS_TLV_READ |
-					SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
-			}
-		}
 		break;
-
 	default:
 		if (! len)
 			strlcpy(kctl->id.name, audio_feature_info[control-1].name,
@@ -1173,6 +1180,19 @@
 		break;
 	}
 
+	/* get min/max values */
+	get_min_max_with_quirks(cval, 0, kctl);
+
+	if (control == UAC_FU_VOLUME) {
+		check_mapped_dB(map, cval);
+		if (cval->dBmin < cval->dBmax || !cval->initialized) {
+			kctl->tlv.c = snd_usb_mixer_vol_tlv;
+			kctl->vd[0].access |=
+				SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+				SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
+		}
+	}
+
 	range = (cval->max - cval->min) / cval->res;
 	/* Are there devices with volume range more than 255? I use a bit more
 	 * to be sure. 384 is a resolution magic number found on Logitech
@@ -1388,7 +1408,7 @@
 	for (pin = 0; pin < input_pins; pin++) {
 		err = parse_audio_unit(state, desc->baSourceID[pin]);
 		if (err < 0)
-			return err;
+			continue;
 		err = check_input_term(state, desc->baSourceID[pin], &iterm);
 		if (err < 0)
 			return err;
diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h
index 81b2d8a..a7f3d45 100644
--- a/sound/usb/mixer.h
+++ b/sound/usb/mixer.h
@@ -68,4 +68,7 @@
 int snd_usb_mixer_add_control(struct usb_mixer_interface *mixer,
 			      struct snd_kcontrol *kctl);
 
+int snd_usb_mixer_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+			  unsigned int size, unsigned int __user *_tlv);
+
 #endif /* __USBMIXER_H */
diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c
index f1324c4..41daaa2 100644
--- a/sound/usb/mixer_maps.c
+++ b/sound/usb/mixer_maps.c
@@ -288,6 +288,15 @@
 	{ 0 } /* terminator */
 };
 
+static struct usbmix_name_map ebox44_map[] = {
+	{ 4, NULL }, /* FU */
+	{ 6, NULL }, /* MU */
+	{ 7, NULL }, /* FU */
+	{ 10, NULL }, /* FU */
+	{ 11, NULL }, /* MU */
+	{ 0 }
+};
+
 /* "Gamesurround Muse Pocket LT" looks same like "Sound Blaster MP3+"
  *  most importand difference is SU[8], it should be set to "Capture Source"
  *  to make alsamixer and PA working properly.
@@ -371,6 +380,10 @@
 		.map = scratch_live_map,
 		.ignore_ctl_error = 1,
 	},
+	{
+		.id = USB_ID(0x200c, 0x1018),
+		.map = ebox44_map,
+	},
 	{ 0 } /* terminator */
 };
 
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index ab125ee..41f4b69 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -42,6 +42,77 @@
 
 extern struct snd_kcontrol_new *snd_usb_feature_unit_ctl;
 
+/* private_free callback */
+static void usb_mixer_elem_free(struct snd_kcontrol *kctl)
+{
+	kfree(kctl->private_data);
+	kctl->private_data = NULL;
+}
+
+/* This function allows for the creation of standard UAC controls.
+ * See the quirks for M-Audio FTUs or Ebox-44.
+ * If you don't want to set a TLV callback pass NULL.
+ *
+ * Since there doesn't seem to be a devices that needs a multichannel
+ * version, we keep it mono for simplicity.
+ */
+static int snd_create_std_mono_ctl(struct usb_mixer_interface *mixer,
+				unsigned int unitid,
+				unsigned int control,
+				unsigned int cmask,
+				int val_type,
+				const char *name,
+				snd_kcontrol_tlv_rw_t *tlv_callback)
+{
+	int err;
+	struct usb_mixer_elem_info *cval;
+	struct snd_kcontrol *kctl;
+
+	cval = kzalloc(sizeof(*cval), GFP_KERNEL);
+	if (!cval)
+		return -ENOMEM;
+
+	cval->id = unitid;
+	cval->mixer = mixer;
+	cval->val_type = val_type;
+	cval->channels = 1;
+	cval->control = control;
+	cval->cmask = cmask;
+
+	/* get_min_max() is called only for integer volumes later,
+	 * so provide a short-cut for booleans */
+	cval->min = 0;
+	cval->max = 1;
+	cval->res = 0;
+	cval->dBmin = 0;
+	cval->dBmax = 0;
+
+	/* Create control */
+	kctl = snd_ctl_new1(snd_usb_feature_unit_ctl, cval);
+	if (!kctl) {
+		kfree(cval);
+		return -ENOMEM;
+	}
+
+	/* Set name */
+	snprintf(kctl->id.name, sizeof(kctl->id.name), name);
+	kctl->private_free = usb_mixer_elem_free;
+
+	/* set TLV */
+	if (tlv_callback) {
+		kctl->tlv.c = tlv_callback;
+		kctl->vd[0].access |=
+			SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+			SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
+	}
+	/* Add control to mixer */
+	err = snd_usb_mixer_add_control(mixer, kctl);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
 /*
  * Sound Blaster remote control configuration
  *
@@ -495,60 +566,218 @@
 }
 
 /* M-Audio FastTrack Ultra quirks */
+/* FTU Effect switch */
+struct snd_ftu_eff_switch_priv_val {
+	struct usb_mixer_interface *mixer;
+	int cached_value;
+	int is_cached;
+};
 
-/* private_free callback */
-static void usb_mixer_elem_free(struct snd_kcontrol *kctl)
+static int snd_ftu_eff_switch_info(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_info *uinfo)
 {
-	kfree(kctl->private_data);
-	kctl->private_data = NULL;
+	static const char *texts[8] = {"Room 1",
+				       "Room 2",
+				       "Room 3",
+				       "Hall 1",
+				       "Hall 2",
+				       "Plate",
+				       "Delay",
+				       "Echo"
+	};
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 8;
+	if (uinfo->value.enumerated.item > 7)
+		uinfo->value.enumerated.item = 7;
+	strcpy(uinfo->value.enumerated.name,
+		texts[uinfo->value.enumerated.item]);
+
+	return 0;
 }
 
-static int snd_maudio_ftu_create_ctl(struct usb_mixer_interface *mixer,
-				     int in, int out, const char *name)
+static int snd_ftu_eff_switch_get(struct snd_kcontrol *kctl,
+					struct snd_ctl_elem_value *ucontrol)
 {
-	struct usb_mixer_elem_info *cval;
-	struct snd_kcontrol *kctl;
+	struct snd_usb_audio *chip;
+	struct usb_mixer_interface *mixer;
+	struct snd_ftu_eff_switch_priv_val *pval;
+	int err;
+	unsigned char value[2];
 
-	cval = kzalloc(sizeof(*cval), GFP_KERNEL);
-	if (!cval)
+	const int id = 6;
+	const int validx = 1;
+	const int val_len = 2;
+
+	value[0] = 0x00;
+	value[1] = 0x00;
+
+	pval = (struct snd_ftu_eff_switch_priv_val *)
+		kctl->private_value;
+
+	if (pval->is_cached) {
+		ucontrol->value.enumerated.item[0] = pval->cached_value;
+		return 0;
+	}
+
+	mixer = (struct usb_mixer_interface *) pval->mixer;
+	if (snd_BUG_ON(!mixer))
+		return -EINVAL;
+
+	chip = (struct snd_usb_audio *) mixer->chip;
+	if (snd_BUG_ON(!chip))
+		return -EINVAL;
+
+
+	err = snd_usb_ctl_msg(chip->dev,
+			usb_rcvctrlpipe(chip->dev, 0), UAC_GET_CUR,
+			USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
+			validx << 8, snd_usb_ctrl_intf(chip) | (id << 8),
+			value, val_len);
+	if (err < 0)
+		return err;
+
+	ucontrol->value.enumerated.item[0] = value[0];
+	pval->cached_value = value[0];
+	pval->is_cached = 1;
+
+	return 0;
+}
+
+static int snd_ftu_eff_switch_put(struct snd_kcontrol *kctl,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_usb_audio *chip;
+	struct snd_ftu_eff_switch_priv_val *pval;
+
+	struct usb_mixer_interface *mixer;
+	int changed, cur_val, err, new_val;
+	unsigned char value[2];
+
+
+	const int id = 6;
+	const int validx = 1;
+	const int val_len = 2;
+
+	changed = 0;
+
+	pval = (struct snd_ftu_eff_switch_priv_val *)
+		kctl->private_value;
+	cur_val = pval->cached_value;
+	new_val = ucontrol->value.enumerated.item[0];
+
+	mixer = (struct usb_mixer_interface *) pval->mixer;
+	if (snd_BUG_ON(!mixer))
+		return -EINVAL;
+
+	chip = (struct snd_usb_audio *) mixer->chip;
+	if (snd_BUG_ON(!chip))
+		return -EINVAL;
+
+	if (!pval->is_cached) {
+		/* Read current value */
+		err = snd_usb_ctl_msg(chip->dev,
+				usb_rcvctrlpipe(chip->dev, 0), UAC_GET_CUR,
+				USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
+				validx << 8, snd_usb_ctrl_intf(chip) | (id << 8),
+				value, val_len);
+		if (err < 0)
+			return err;
+
+		cur_val = value[0];
+		pval->cached_value = cur_val;
+		pval->is_cached = 1;
+	}
+	/* update value if needed */
+	if (cur_val != new_val) {
+		value[0] = new_val;
+		value[1] = 0;
+		err = snd_usb_ctl_msg(chip->dev,
+				usb_sndctrlpipe(chip->dev, 0), UAC_SET_CUR,
+				USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
+				validx << 8, snd_usb_ctrl_intf(chip) | (id << 8),
+				value, val_len);
+		if (err < 0)
+			return err;
+
+		pval->cached_value = new_val;
+		pval->is_cached = 1;
+		changed = 1;
+	}
+
+	return changed;
+}
+
+static int snd_ftu_create_effect_switch(struct usb_mixer_interface *mixer)
+{
+	static struct snd_kcontrol_new template = {
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Effect Program Switch",
+		.index = 0,
+		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+		.info = snd_ftu_eff_switch_info,
+		.get = snd_ftu_eff_switch_get,
+		.put = snd_ftu_eff_switch_put
+	};
+
+	int err;
+	struct snd_kcontrol *kctl;
+	struct snd_ftu_eff_switch_priv_val *pval;
+
+	pval = kzalloc(sizeof(*pval), GFP_KERNEL);
+	if (!pval)
 		return -ENOMEM;
 
-	cval->id = 5;
-	cval->mixer = mixer;
-	cval->val_type = USB_MIXER_S16;
-	cval->channels = 1;
-	cval->control = out + 1;
-	cval->cmask = 1 << in;
+	pval->cached_value = 0;
+	pval->is_cached = 0;
+	pval->mixer = mixer;
 
-	kctl = snd_ctl_new1(snd_usb_feature_unit_ctl, cval);
+	template.private_value = (unsigned long) pval;
+	kctl = snd_ctl_new1(&template, mixer->chip);
 	if (!kctl) {
-		kfree(cval);
+		kfree(pval);
 		return -ENOMEM;
 	}
 
-	snprintf(kctl->id.name, sizeof(kctl->id.name), name);
-	kctl->private_free = usb_mixer_elem_free;
-	return snd_usb_mixer_add_control(mixer, kctl);
+	err = snd_ctl_add(mixer->chip->card, kctl);
+	if (err < 0)
+		return err;
+
+	return 0;
 }
 
-static int snd_maudio_ftu_create_mixer(struct usb_mixer_interface *mixer)
+/* Create volume controls for FTU devices*/
+static int snd_ftu_create_volume_ctls(struct usb_mixer_interface *mixer)
 {
 	char name[64];
+	unsigned int control, cmask;
 	int in, out, err;
 
+	const unsigned int id = 5;
+	const int val_type = USB_MIXER_S16;
+
 	for (out = 0; out < 8; out++) {
+		control = out + 1;
 		for (in = 0; in < 8; in++) {
+			cmask = 1 << in;
 			snprintf(name, sizeof(name),
-				 "AIn%d - Out%d Capture Volume", in  + 1, out + 1);
-			err = snd_maudio_ftu_create_ctl(mixer, in, out, name);
+				"AIn%d - Out%d Capture Volume",
+				in  + 1, out + 1);
+			err = snd_create_std_mono_ctl(mixer, id, control,
+							cmask, val_type, name,
+							&snd_usb_mixer_vol_tlv);
 			if (err < 0)
 				return err;
 		}
-
 		for (in = 8; in < 16; in++) {
+			cmask = 1 << in;
 			snprintf(name, sizeof(name),
-				 "DIn%d - Out%d Playback Volume", in - 7, out + 1);
-			err = snd_maudio_ftu_create_ctl(mixer, in, out, name);
+				"DIn%d - Out%d Playback Volume",
+				in - 7, out + 1);
+			err = snd_create_std_mono_ctl(mixer, id, control,
+							cmask, val_type, name,
+							&snd_usb_mixer_vol_tlv);
 			if (err < 0)
 				return err;
 		}
@@ -557,6 +786,191 @@
 	return 0;
 }
 
+/* This control needs a volume quirk, see mixer.c */
+static int snd_ftu_create_effect_volume_ctl(struct usb_mixer_interface *mixer)
+{
+	static const char name[] = "Effect Volume";
+	const unsigned int id = 6;
+	const int val_type = USB_MIXER_U8;
+	const unsigned int control = 2;
+	const unsigned int cmask = 0;
+
+	return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type,
+					name, snd_usb_mixer_vol_tlv);
+}
+
+/* This control needs a volume quirk, see mixer.c */
+static int snd_ftu_create_effect_duration_ctl(struct usb_mixer_interface *mixer)
+{
+	static const char name[] = "Effect Duration";
+	const unsigned int id = 6;
+	const int val_type = USB_MIXER_S16;
+	const unsigned int control = 3;
+	const unsigned int cmask = 0;
+
+	return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type,
+					name, snd_usb_mixer_vol_tlv);
+}
+
+/* This control needs a volume quirk, see mixer.c */
+static int snd_ftu_create_effect_feedback_ctl(struct usb_mixer_interface *mixer)
+{
+	static const char name[] = "Effect Feedback Volume";
+	const unsigned int id = 6;
+	const int val_type = USB_MIXER_U8;
+	const unsigned int control = 4;
+	const unsigned int cmask = 0;
+
+	return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type,
+					name, NULL);
+}
+
+static int snd_ftu_create_effect_return_ctls(struct usb_mixer_interface *mixer)
+{
+	unsigned int cmask;
+	int err, ch;
+	char name[48];
+
+	const unsigned int id = 7;
+	const int val_type = USB_MIXER_S16;
+	const unsigned int control = 7;
+
+	for (ch = 0; ch < 4; ++ch) {
+		cmask = 1 << ch;
+		snprintf(name, sizeof(name),
+			"Effect Return %d Volume", ch + 1);
+		err = snd_create_std_mono_ctl(mixer, id, control,
+						cmask, val_type, name,
+						snd_usb_mixer_vol_tlv);
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+
+static int snd_ftu_create_effect_send_ctls(struct usb_mixer_interface *mixer)
+{
+	unsigned int  cmask;
+	int err, ch;
+	char name[48];
+
+	const unsigned int id = 5;
+	const int val_type = USB_MIXER_S16;
+	const unsigned int control = 9;
+
+	for (ch = 0; ch < 8; ++ch) {
+		cmask = 1 << ch;
+		snprintf(name, sizeof(name),
+			"Effect Send AIn%d Volume", ch + 1);
+		err = snd_create_std_mono_ctl(mixer, id, control, cmask,
+						val_type, name,
+						snd_usb_mixer_vol_tlv);
+		if (err < 0)
+			return err;
+	}
+	for (ch = 8; ch < 16; ++ch) {
+		cmask = 1 << ch;
+		snprintf(name, sizeof(name),
+			"Effect Send DIn%d Volume", ch - 7);
+		err = snd_create_std_mono_ctl(mixer, id, control, cmask,
+						val_type, name,
+						snd_usb_mixer_vol_tlv);
+		if (err < 0)
+			return err;
+	}
+	return 0;
+}
+
+static int snd_ftu_create_mixer(struct usb_mixer_interface *mixer)
+{
+	int err;
+
+	err = snd_ftu_create_volume_ctls(mixer);
+	if (err < 0)
+		return err;
+
+	err = snd_ftu_create_effect_switch(mixer);
+	if (err < 0)
+		return err;
+	err = snd_ftu_create_effect_volume_ctl(mixer);
+	if (err < 0)
+		return err;
+
+	err = snd_ftu_create_effect_duration_ctl(mixer);
+	if (err < 0)
+		return err;
+
+	err = snd_ftu_create_effect_feedback_ctl(mixer);
+	if (err < 0)
+		return err;
+
+	err = snd_ftu_create_effect_return_ctls(mixer);
+	if (err < 0)
+		return err;
+
+	err = snd_ftu_create_effect_send_ctls(mixer);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
+
+/*
+ * Create mixer for Electrix Ebox-44
+ *
+ * The mixer units from this device are corrupt, and even where they
+ * are valid they presents mono controls as L and R channels of
+ * stereo. So we create a good mixer in code.
+ */
+
+static int snd_ebox44_create_mixer(struct usb_mixer_interface *mixer)
+{
+	int err;
+
+	err = snd_create_std_mono_ctl(mixer, 4, 1, 0x0, USB_MIXER_INV_BOOLEAN,
+				"Headphone Playback Switch", NULL);
+	if (err < 0)
+		return err;
+	err = snd_create_std_mono_ctl(mixer, 4, 2, 0x1, USB_MIXER_S16,
+				"Headphone A Mix Playback Volume", NULL);
+	if (err < 0)
+		return err;
+	err = snd_create_std_mono_ctl(mixer, 4, 2, 0x2, USB_MIXER_S16,
+				"Headphone B Mix Playback Volume", NULL);
+	if (err < 0)
+		return err;
+
+	err = snd_create_std_mono_ctl(mixer, 7, 1, 0x0, USB_MIXER_INV_BOOLEAN,
+				"Output Playback Switch", NULL);
+	if (err < 0)
+		return err;
+	err = snd_create_std_mono_ctl(mixer, 7, 2, 0x1, USB_MIXER_S16,
+				"Output A Playback Volume", NULL);
+	if (err < 0)
+		return err;
+	err = snd_create_std_mono_ctl(mixer, 7, 2, 0x2, USB_MIXER_S16,
+				"Output B Playback Volume", NULL);
+	if (err < 0)
+		return err;
+
+	err = snd_create_std_mono_ctl(mixer, 10, 1, 0x0, USB_MIXER_INV_BOOLEAN,
+				"Input Capture Switch", NULL);
+	if (err < 0)
+		return err;
+	err = snd_create_std_mono_ctl(mixer, 10, 2, 0x1, USB_MIXER_S16,
+				"Input A Capture Volume", NULL);
+	if (err < 0)
+		return err;
+	err = snd_create_std_mono_ctl(mixer, 10, 2, 0x2, USB_MIXER_S16,
+				"Input B Capture Volume", NULL);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
 void snd_emuusb_set_samplerate(struct snd_usb_audio *chip,
 			       unsigned char samplerate_id)
 {
@@ -600,7 +1014,7 @@
 
 	case USB_ID(0x0763, 0x2080): /* M-Audio Fast Track Ultra */
 	case USB_ID(0x0763, 0x2081): /* M-Audio Fast Track Ultra 8R */
-		err = snd_maudio_ftu_create_mixer(mixer);
+		err = snd_ftu_create_mixer(mixer);
 		break;
 
 	case USB_ID(0x0b05, 0x1739):
@@ -619,6 +1033,10 @@
 				snd_nativeinstruments_ta10_mixers,
 				ARRAY_SIZE(snd_nativeinstruments_ta10_mixers));
 		break;
+
+	case USB_ID(0x200c, 0x1018): /* Electrix Ebox-44 */
+		err = snd_ebox44_create_mixer(mixer);
+		break;
 	}
 
 	return err;
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index 0eed611..24839d9 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -16,6 +16,7 @@
 
 #include <linux/init.h>
 #include <linux/slab.h>
+#include <linux/ratelimit.h>
 #include <linux/usb.h>
 #include <linux/usb/audio.h>
 #include <linux/usb/audio-v2.h>
@@ -34,6 +35,9 @@
 #include "clock.h"
 #include "power.h"
 
+#define SUBSTREAM_FLAG_DATA_EP_STARTED	0
+#define SUBSTREAM_FLAG_SYNC_EP_STARTED	1
+
 /* return the estimated delay based on USB frame counters */
 snd_pcm_uframes_t snd_usb_pcm_delay(struct snd_usb_substream *subs,
 				    unsigned int rate)
@@ -208,6 +212,84 @@
 	}
 }
 
+static int start_endpoints(struct snd_usb_substream *subs)
+{
+	int err;
+
+	if (!subs->data_endpoint)
+		return -EINVAL;
+
+	if (!test_and_set_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags)) {
+		struct snd_usb_endpoint *ep = subs->data_endpoint;
+
+		snd_printdd(KERN_DEBUG "Starting data EP @%p\n", ep);
+
+		ep->data_subs = subs;
+		err = snd_usb_endpoint_start(ep);
+		if (err < 0) {
+			clear_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags);
+			return err;
+		}
+	}
+
+	if (subs->sync_endpoint &&
+	    !test_and_set_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags)) {
+		struct snd_usb_endpoint *ep = subs->sync_endpoint;
+
+		snd_printdd(KERN_DEBUG "Starting sync EP @%p\n", ep);
+
+		ep->sync_slave = subs->data_endpoint;
+		err = snd_usb_endpoint_start(ep);
+		if (err < 0) {
+			clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags);
+			return err;
+		}
+	}
+
+	return 0;
+}
+
+static void stop_endpoints(struct snd_usb_substream *subs,
+			   int force, int can_sleep, int wait)
+{
+	if (test_and_clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags))
+		snd_usb_endpoint_stop(subs->sync_endpoint,
+				      force, can_sleep, wait);
+
+	if (test_and_clear_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags))
+		snd_usb_endpoint_stop(subs->data_endpoint,
+				      force, can_sleep, wait);
+}
+
+static int activate_endpoints(struct snd_usb_substream *subs)
+{
+	if (subs->sync_endpoint) {
+		int ret;
+
+		ret = snd_usb_endpoint_activate(subs->sync_endpoint);
+		if (ret < 0)
+			return ret;
+	}
+
+	return snd_usb_endpoint_activate(subs->data_endpoint);
+}
+
+static int deactivate_endpoints(struct snd_usb_substream *subs)
+{
+	int reta, retb;
+
+	reta = snd_usb_endpoint_deactivate(subs->sync_endpoint);
+	retb = snd_usb_endpoint_deactivate(subs->data_endpoint);
+
+	if (reta < 0)
+		return reta;
+
+	if (retb < 0)
+		return retb;
+
+	return 0;
+}
+
 /*
  * find a matching format and set up the interface
  */
@@ -219,7 +301,7 @@
 	struct usb_interface *iface;
 	unsigned int ep, attr;
 	int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK;
-	int err;
+	int err, implicit_fb = 0;
 
 	iface = usb_ifnum_to_if(dev, fmt->iface);
 	if (WARN_ON(!iface))
@@ -232,40 +314,11 @@
 	if (fmt == subs->cur_audiofmt)
 		return 0;
 
-	/* close the old interface */
-	if (subs->interface >= 0 && subs->interface != fmt->iface) {
-		if (usb_set_interface(subs->dev, subs->interface, 0) < 0) {
-			snd_printk(KERN_ERR "%d:%d:%d: return to setting 0 failed\n",
-				dev->devnum, fmt->iface, fmt->altsetting);
-			return -EIO;
-		}
-		subs->interface = -1;
-		subs->altset_idx = 0;
-	}
-
-	/* set interface */
-	if (subs->interface != fmt->iface || subs->altset_idx != fmt->altset_idx) {
-		if (usb_set_interface(dev, fmt->iface, fmt->altsetting) < 0) {
-			snd_printk(KERN_ERR "%d:%d:%d: usb_set_interface failed\n",
-				   dev->devnum, fmt->iface, fmt->altsetting);
-			return -EIO;
-		}
-		snd_printdd(KERN_INFO "setting usb interface %d:%d\n", fmt->iface, fmt->altsetting);
-		subs->interface = fmt->iface;
-		subs->altset_idx = fmt->altset_idx;
-	}
-
-	/* create a data pipe */
-	ep = fmt->endpoint & USB_ENDPOINT_NUMBER_MASK;
-	if (is_playback)
-		subs->datapipe = usb_sndisocpipe(dev, ep);
-	else
-		subs->datapipe = usb_rcvisocpipe(dev, ep);
-	subs->datainterval = fmt->datainterval;
-	subs->syncpipe = subs->syncinterval = 0;
-	subs->maxpacksize = fmt->maxpacksize;
-	subs->syncmaxsize = 0;
-	subs->fill_max = 0;
+	subs->data_endpoint = snd_usb_add_endpoint(subs->stream->chip,
+						   alts, fmt->endpoint, subs->direction,
+						   SND_USB_ENDPOINT_TYPE_DATA);
+	if (!subs->data_endpoint)
+		return -EINVAL;
 
 	/* we need a sync pipe in async OUT or adaptive IN mode */
 	/* check the number of EP, since some devices have broken
@@ -273,8 +326,25 @@
 	 * assume it as adaptive-out or sync-in.
 	 */
 	attr = fmt->ep_attr & USB_ENDPOINT_SYNCTYPE;
+
+	switch (subs->stream->chip->usb_id) {
+	case USB_ID(0x0763, 0x2080): /* M-Audio FastTrack Ultra */
+	case USB_ID(0x0763, 0x2081):
+		if (is_playback) {
+			implicit_fb = 1;
+			ep = 0x81;
+			iface = usb_ifnum_to_if(dev, 2);
+
+			if (!iface || iface->num_altsetting == 0)
+				return -EINVAL;
+
+			alts = &iface->altsetting[1];
+			goto add_sync_ep;
+		}
+	}
+
 	if (((is_playback && attr == USB_ENDPOINT_SYNC_ASYNC) ||
-	     (! is_playback && attr == USB_ENDPOINT_SYNC_ADAPTIVE)) &&
+	     (!is_playback && attr == USB_ENDPOINT_SYNC_ADAPTIVE)) &&
 	    altsd->bNumEndpoints >= 2) {
 		/* check sync-pipe endpoint */
 		/* ... and check descriptor size before accessing bSynchAddress
@@ -282,7 +352,8 @@
 		   the audio fields in the endpoint descriptors */
 		if ((get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != 0x01 ||
 		    (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
-		     get_endpoint(alts, 1)->bSynchAddress != 0)) {
+		     get_endpoint(alts, 1)->bSynchAddress != 0 &&
+		     !implicit_fb)) {
 			snd_printk(KERN_ERR "%d:%d:%d : invalid synch pipe\n",
 				   dev->devnum, fmt->iface, fmt->altsetting);
 			return -EINVAL;
@@ -290,33 +361,27 @@
 		ep = get_endpoint(alts, 1)->bEndpointAddress;
 		if (get_endpoint(alts, 0)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
 		    (( is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress | USB_DIR_IN)) ||
-		     (!is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress & ~USB_DIR_IN)))) {
+		     (!is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress & ~USB_DIR_IN)) ||
+		     ( is_playback && !implicit_fb))) {
 			snd_printk(KERN_ERR "%d:%d:%d : invalid synch pipe\n",
 				   dev->devnum, fmt->iface, fmt->altsetting);
 			return -EINVAL;
 		}
-		ep &= USB_ENDPOINT_NUMBER_MASK;
-		if (is_playback)
-			subs->syncpipe = usb_rcvisocpipe(dev, ep);
-		else
-			subs->syncpipe = usb_sndisocpipe(dev, ep);
-		if (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
-		    get_endpoint(alts, 1)->bRefresh >= 1 &&
-		    get_endpoint(alts, 1)->bRefresh <= 9)
-			subs->syncinterval = get_endpoint(alts, 1)->bRefresh;
-		else if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL)
-			subs->syncinterval = 1;
-		else if (get_endpoint(alts, 1)->bInterval >= 1 &&
-			 get_endpoint(alts, 1)->bInterval <= 16)
-			subs->syncinterval = get_endpoint(alts, 1)->bInterval - 1;
-		else
-			subs->syncinterval = 3;
-		subs->syncmaxsize = le16_to_cpu(get_endpoint(alts, 1)->wMaxPacketSize);
-	}
 
-	/* always fill max packet size */
-	if (fmt->attributes & UAC_EP_CS_ATTR_FILL_MAX)
-		subs->fill_max = 1;
+		implicit_fb = (get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_USAGE_MASK)
+				== USB_ENDPOINT_USAGE_IMPLICIT_FB;
+
+add_sync_ep:
+		subs->sync_endpoint = snd_usb_add_endpoint(subs->stream->chip,
+							   alts, ep, !subs->direction,
+							   implicit_fb ?
+								SND_USB_ENDPOINT_TYPE_DATA :
+								SND_USB_ENDPOINT_TYPE_SYNC);
+		if (!subs->sync_endpoint)
+			return -EINVAL;
+
+		subs->data_endpoint->sync_master = subs->sync_endpoint;
+	}
 
 	if ((err = snd_usb_init_pitch(subs->stream->chip, subs->interface, alts, fmt)) < 0)
 		return err;
@@ -390,15 +455,30 @@
 	if (changed) {
 		mutex_lock(&subs->stream->chip->shutdown_mutex);
 		/* format changed */
-		snd_usb_release_substream_urbs(subs, 0);
-		/* influenced: period_bytes, channels, rate, format, */
-		ret = snd_usb_init_substream_urbs(subs, params_period_bytes(hw_params),
-						  params_rate(hw_params),
-						  snd_pcm_format_physical_width(params_format(hw_params)) *
-							params_channels(hw_params));
+		stop_endpoints(subs, 0, 0, 0);
+		deactivate_endpoints(subs);
+
+		ret = activate_endpoints(subs);
+		if (ret < 0)
+			goto unlock;
+
+		ret = snd_usb_endpoint_set_params(subs->data_endpoint, hw_params, fmt,
+						  subs->sync_endpoint);
+		if (ret < 0)
+			goto unlock;
+
+		if (subs->sync_endpoint)
+			ret = snd_usb_endpoint_set_params(subs->sync_endpoint,
+							  hw_params, fmt, NULL);
+unlock:
 		mutex_unlock(&subs->stream->chip->shutdown_mutex);
 	}
 
+	if (ret == 0) {
+		subs->interface = fmt->iface;
+		subs->altset_idx = fmt->altset_idx;
+	}
+
 	return ret;
 }
 
@@ -415,7 +495,7 @@
 	subs->cur_rate = 0;
 	subs->period_bytes = 0;
 	mutex_lock(&subs->stream->chip->shutdown_mutex);
-	snd_usb_release_substream_urbs(subs, 0);
+	stop_endpoints(subs, 0, 1, 1);
 	mutex_unlock(&subs->stream->chip->shutdown_mutex);
 	return snd_pcm_lib_free_vmalloc_buffer(substream);
 }
@@ -435,19 +515,28 @@
 		return -ENXIO;
 	}
 
+	if (snd_BUG_ON(!subs->data_endpoint))
+		return -EIO;
+
 	/* some unit conversions in runtime */
-	subs->maxframesize = bytes_to_frames(runtime, subs->maxpacksize);
-	subs->curframesize = bytes_to_frames(runtime, subs->curpacksize);
+	subs->data_endpoint->maxframesize =
+		bytes_to_frames(runtime, subs->data_endpoint->maxpacksize);
+	subs->data_endpoint->curframesize =
+		bytes_to_frames(runtime, subs->data_endpoint->curpacksize);
 
 	/* reset the pointer */
 	subs->hwptr_done = 0;
 	subs->transfer_done = 0;
-	subs->phase = 0;
 	subs->last_delay = 0;
 	subs->last_frame_number = 0;
 	runtime->delay = 0;
 
-	return snd_usb_substream_prepare(subs, runtime);
+	/* for playback, submit the URBs now; otherwise, the first hwptr_done
+	 * updates for all URBs would happen at the same time when starting */
+	if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK)
+		return start_endpoints(subs);
+
+	return 0;
 }
 
 static struct snd_pcm_hardware snd_usb_hardware =
@@ -842,16 +931,171 @@
 
 static int snd_usb_pcm_close(struct snd_pcm_substream *substream, int direction)
 {
+	int ret;
 	struct snd_usb_stream *as = snd_pcm_substream_chip(substream);
 	struct snd_usb_substream *subs = &as->substream[direction];
 
-	if (!as->chip->shutdown && subs->interface >= 0) {
-		usb_set_interface(subs->dev, subs->interface, 0);
-		subs->interface = -1;
-	}
+	stop_endpoints(subs, 0, 0, 0);
+	ret = deactivate_endpoints(subs);
 	subs->pcm_substream = NULL;
 	snd_usb_autosuspend(subs->stream->chip);
-	return 0;
+
+	return ret;
+}
+
+/* Since a URB can handle only a single linear buffer, we must use double
+ * buffering when the data to be transferred overflows the buffer boundary.
+ * To avoid inconsistencies when updating hwptr_done, we use double buffering
+ * for all URBs.
+ */
+static void retire_capture_urb(struct snd_usb_substream *subs,
+			       struct urb *urb)
+{
+	struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
+	unsigned int stride, frames, bytes, oldptr;
+	int i, period_elapsed = 0;
+	unsigned long flags;
+	unsigned char *cp;
+
+	stride = runtime->frame_bits >> 3;
+
+	for (i = 0; i < urb->number_of_packets; i++) {
+		cp = (unsigned char *)urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+		if (urb->iso_frame_desc[i].status && printk_ratelimit()) {
+			snd_printdd(KERN_ERR "frame %d active: %d\n", i, urb->iso_frame_desc[i].status);
+			// continue;
+		}
+		bytes = urb->iso_frame_desc[i].actual_length;
+		frames = bytes / stride;
+		if (!subs->txfr_quirk)
+			bytes = frames * stride;
+		if (bytes % (runtime->sample_bits >> 3) != 0) {
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+			int oldbytes = bytes;
+#endif
+			bytes = frames * stride;
+			snd_printdd(KERN_ERR "Corrected urb data len. %d->%d\n",
+							oldbytes, bytes);
+		}
+		/* update the current pointer */
+		spin_lock_irqsave(&subs->lock, flags);
+		oldptr = subs->hwptr_done;
+		subs->hwptr_done += bytes;
+		if (subs->hwptr_done >= runtime->buffer_size * stride)
+			subs->hwptr_done -= runtime->buffer_size * stride;
+		frames = (bytes + (oldptr % stride)) / stride;
+		subs->transfer_done += frames;
+		if (subs->transfer_done >= runtime->period_size) {
+			subs->transfer_done -= runtime->period_size;
+			period_elapsed = 1;
+		}
+		spin_unlock_irqrestore(&subs->lock, flags);
+		/* copy a data chunk */
+		if (oldptr + bytes > runtime->buffer_size * stride) {
+			unsigned int bytes1 =
+					runtime->buffer_size * stride - oldptr;
+			memcpy(runtime->dma_area + oldptr, cp, bytes1);
+			memcpy(runtime->dma_area, cp + bytes1, bytes - bytes1);
+		} else {
+			memcpy(runtime->dma_area + oldptr, cp, bytes);
+		}
+	}
+
+	if (period_elapsed)
+		snd_pcm_period_elapsed(subs->pcm_substream);
+}
+
+static void prepare_playback_urb(struct snd_usb_substream *subs,
+				 struct urb *urb)
+{
+	struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
+	struct snd_urb_ctx *ctx = urb->context;
+	unsigned int counts, frames, bytes;
+	int i, stride, period_elapsed = 0;
+	unsigned long flags;
+
+	stride = runtime->frame_bits >> 3;
+
+	frames = 0;
+	urb->number_of_packets = 0;
+	spin_lock_irqsave(&subs->lock, flags);
+	for (i = 0; i < ctx->packets; i++) {
+		counts = ctx->packet_size[i];
+		/* set up descriptor */
+		urb->iso_frame_desc[i].offset = frames * stride;
+		urb->iso_frame_desc[i].length = counts * stride;
+		frames += counts;
+		urb->number_of_packets++;
+		subs->transfer_done += counts;
+		if (subs->transfer_done >= runtime->period_size) {
+			subs->transfer_done -= runtime->period_size;
+			period_elapsed = 1;
+			if (subs->fmt_type == UAC_FORMAT_TYPE_II) {
+				if (subs->transfer_done > 0) {
+					/* FIXME: fill-max mode is not
+					 * supported yet */
+					frames -= subs->transfer_done;
+					counts -= subs->transfer_done;
+					urb->iso_frame_desc[i].length =
+						counts * stride;
+					subs->transfer_done = 0;
+				}
+				i++;
+				if (i < ctx->packets) {
+					/* add a transfer delimiter */
+					urb->iso_frame_desc[i].offset =
+						frames * stride;
+					urb->iso_frame_desc[i].length = 0;
+					urb->number_of_packets++;
+				}
+				break;
+			}
+		}
+		if (period_elapsed &&
+		    !snd_usb_endpoint_implict_feedback_sink(subs->data_endpoint)) /* finish at the period boundary */
+			break;
+	}
+	bytes = frames * stride;
+	if (subs->hwptr_done + bytes > runtime->buffer_size * stride) {
+		/* err, the transferred area goes over buffer boundary. */
+		unsigned int bytes1 =
+			runtime->buffer_size * stride - subs->hwptr_done;
+		memcpy(urb->transfer_buffer,
+		       runtime->dma_area + subs->hwptr_done, bytes1);
+		memcpy(urb->transfer_buffer + bytes1,
+		       runtime->dma_area, bytes - bytes1);
+	} else {
+		memcpy(urb->transfer_buffer,
+		       runtime->dma_area + subs->hwptr_done, bytes);
+	}
+	subs->hwptr_done += bytes;
+	if (subs->hwptr_done >= runtime->buffer_size * stride)
+		subs->hwptr_done -= runtime->buffer_size * stride;
+	runtime->delay += frames;
+	spin_unlock_irqrestore(&subs->lock, flags);
+	urb->transfer_buffer_length = bytes;
+	if (period_elapsed)
+		snd_pcm_period_elapsed(subs->pcm_substream);
+}
+
+/*
+ * process after playback data complete
+ * - decrease the delay count again
+ */
+static void retire_playback_urb(struct snd_usb_substream *subs,
+			       struct urb *urb)
+{
+	unsigned long flags;
+	struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
+	int stride = runtime->frame_bits >> 3;
+	int processed = urb->transfer_buffer_length / stride;
+
+	spin_lock_irqsave(&subs->lock, flags);
+	if (processed > runtime->delay)
+		runtime->delay = 0;
+	else
+		runtime->delay -= processed;
+	spin_unlock_irqrestore(&subs->lock, flags);
 }
 
 static int snd_usb_playback_open(struct snd_pcm_substream *substream)
@@ -874,6 +1118,63 @@
 	return snd_usb_pcm_close(substream, SNDRV_PCM_STREAM_CAPTURE);
 }
 
+static int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substream,
+					      int cmd)
+{
+	struct snd_usb_substream *subs = substream->runtime->private_data;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		subs->data_endpoint->prepare_data_urb = prepare_playback_urb;
+		subs->data_endpoint->retire_data_urb = retire_playback_urb;
+		subs->running = 1;
+		return 0;
+	case SNDRV_PCM_TRIGGER_STOP:
+		stop_endpoints(subs, 0, 0, 0);
+		subs->running = 0;
+		return 0;
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		subs->data_endpoint->prepare_data_urb = NULL;
+		subs->data_endpoint->retire_data_urb = NULL;
+		subs->running = 0;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	int err;
+	struct snd_usb_substream *subs = substream->runtime->private_data;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		err = start_endpoints(subs);
+		if (err < 0)
+			return err;
+
+		subs->data_endpoint->retire_data_urb = retire_capture_urb;
+		subs->running = 1;
+		return 0;
+	case SNDRV_PCM_TRIGGER_STOP:
+		stop_endpoints(subs, 0, 0, 0);
+		subs->running = 0;
+		return 0;
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		subs->data_endpoint->retire_data_urb = NULL;
+		subs->running = 0;
+		return 0;
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		subs->data_endpoint->retire_data_urb = retire_capture_urb;
+		subs->running = 1;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
 static struct snd_pcm_ops snd_usb_playback_ops = {
 	.open =		snd_usb_playback_open,
 	.close =	snd_usb_playback_close,
diff --git a/sound/usb/proc.c b/sound/usb/proc.c
index 961c9a2..ebc1a5b 100644
--- a/sound/usb/proc.c
+++ b/sound/usb/proc.c
@@ -25,6 +25,7 @@
 #include "usbaudio.h"
 #include "helper.h"
 #include "card.h"
+#include "endpoint.h"
 #include "proc.h"
 
 /* convert our full speed USB rate into sampling rate in Hz */
@@ -115,28 +116,33 @@
 	}
 }
 
+static void proc_dump_ep_status(struct snd_usb_substream *subs,
+				struct snd_usb_endpoint *ep,
+				struct snd_info_buffer *buffer)
+{
+	if (!ep)
+		return;
+	snd_iprintf(buffer, "    Packet Size = %d\n", ep->curpacksize);
+	snd_iprintf(buffer, "    Momentary freq = %u Hz (%#x.%04x)\n",
+		    snd_usb_get_speed(subs->dev) == USB_SPEED_FULL
+		    ? get_full_speed_hz(ep->freqm)
+		    : get_high_speed_hz(ep->freqm),
+		    ep->freqm >> 16, ep->freqm & 0xffff);
+	if (ep->freqshift != INT_MIN) {
+		int res = 16 - ep->freqshift;
+		snd_iprintf(buffer, "    Feedback Format = %d.%d\n",
+			    (ep->syncmaxsize > 3 ? 32 : 24) - res, res);
+	}
+}
+
 static void proc_dump_substream_status(struct snd_usb_substream *subs, struct snd_info_buffer *buffer)
 {
 	if (subs->running) {
-		unsigned int i;
 		snd_iprintf(buffer, "  Status: Running\n");
 		snd_iprintf(buffer, "    Interface = %d\n", subs->interface);
 		snd_iprintf(buffer, "    Altset = %d\n", subs->altset_idx);
-		snd_iprintf(buffer, "    URBs = %d [ ", subs->nurbs);
-		for (i = 0; i < subs->nurbs; i++)
-			snd_iprintf(buffer, "%d ", subs->dataurb[i].packets);
-		snd_iprintf(buffer, "]\n");
-		snd_iprintf(buffer, "    Packet Size = %d\n", subs->curpacksize);
-		snd_iprintf(buffer, "    Momentary freq = %u Hz (%#x.%04x)\n",
-			    snd_usb_get_speed(subs->dev) == USB_SPEED_FULL
-			    ? get_full_speed_hz(subs->freqm)
-			    : get_high_speed_hz(subs->freqm),
-			    subs->freqm >> 16, subs->freqm & 0xffff);
-		if (subs->freqshift != INT_MIN)
-			snd_iprintf(buffer, "    Feedback Format = %d.%d\n",
-				    (subs->syncmaxsize > 3 ? 32 : 24)
-						- (16 - subs->freqshift),
-				    16 - subs->freqshift);
+		proc_dump_ep_status(subs, subs->data_endpoint, buffer);
+		proc_dump_ep_status(subs, subs->sync_endpoint, buffer);
 	} else {
 		snd_iprintf(buffer, "  Status: Stop\n");
 	}
diff --git a/sound/usb/stream.c b/sound/usb/stream.c
index 5ff8010..6b7d7a2 100644
--- a/sound/usb/stream.c
+++ b/sound/usb/stream.c
@@ -73,6 +73,31 @@
 	}
 }
 
+/*
+ * initialize the substream instance.
+ */
+
+static void snd_usb_init_substream(struct snd_usb_stream *as,
+				   int stream,
+				   struct audioformat *fp)
+{
+	struct snd_usb_substream *subs = &as->substream[stream];
+
+	INIT_LIST_HEAD(&subs->fmt_list);
+	spin_lock_init(&subs->lock);
+
+	subs->stream = as;
+	subs->direction = stream;
+	subs->dev = as->chip->dev;
+	subs->txfr_quirk = as->chip->txfr_quirk;
+
+	snd_usb_set_pcm_ops(as->pcm, stream);
+
+	list_add_tail(&fp->list, &subs->fmt_list);
+	subs->formats |= fp->formats;
+	subs->num_formats++;
+	subs->fmt_type = fp->fmt_type;
+}
 
 /*
  * add this endpoint to the chip instance.
@@ -94,9 +119,9 @@
 		if (as->fmt_type != fp->fmt_type)
 			continue;
 		subs = &as->substream[stream];
-		if (!subs->endpoint)
+		if (!subs->data_endpoint)
 			continue;
-		if (subs->endpoint == fp->endpoint) {
+		if (subs->data_endpoint->ep_num == fp->endpoint) {
 			list_add_tail(&fp->list, &subs->fmt_list);
 			subs->num_formats++;
 			subs->formats |= fp->formats;
@@ -109,7 +134,7 @@
 		if (as->fmt_type != fp->fmt_type)
 			continue;
 		subs = &as->substream[stream];
-		if (subs->endpoint)
+		if (subs->data_endpoint)
 			continue;
 		err = snd_pcm_new_stream(as->pcm, stream, 1);
 		if (err < 0)
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
index 3e2b035..b8233eb 100644
--- a/sound/usb/usbaudio.h
+++ b/sound/usb/usbaudio.h
@@ -36,6 +36,7 @@
 	struct snd_card *card;
 	struct usb_interface *pm_intf;
 	u32 usb_id;
+	struct mutex mutex;
 	struct mutex shutdown_mutex;
 	unsigned int shutdown:1;
 	unsigned int probing:1;
@@ -46,6 +47,7 @@
 	int num_suspended_intf;
 
 	struct list_head pcm_list;	/* list of pcm streams */
+	struct list_head ep_list;	/* list of audio-related endpoints */
 	int pcm_devs;
 
 	struct list_head midi_list;	/* list of midi interfaces */
diff --git a/tools/perf/Documentation/perf-evlist.txt b/tools/perf/Documentation/perf-evlist.txt
index 0507ec7..1521734 100644
--- a/tools/perf/Documentation/perf-evlist.txt
+++ b/tools/perf/Documentation/perf-evlist.txt
@@ -20,6 +20,14 @@
 --input=::
         Input file name. (default: perf.data unless stdin is a fifo)
 
+-F::
+--freq=::
+	Show just the sample frequency used for each event.
+
+-v::
+--verbose=::
+	Show all fields.
+
 SEE ALSO
 --------
 linkperf:perf-record[1], linkperf:perf-list[1],
diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt
index a1386b2..b38a1f9 100644
--- a/tools/perf/Documentation/perf-record.txt
+++ b/tools/perf/Documentation/perf-record.txt
@@ -168,7 +168,7 @@
         - any:  any type of branches
         - any_call: any function call or system call
         - any_ret: any function return or system call return
-        - any_ind: any indirect branch
+        - ind_call: any indirect branch
         - u:  only when the branch target is at the user level
         - k: only when the branch target is in the kernel
         - hv: only when the target is at the hypervisor level
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index fa37cd5..1d3d513 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -83,7 +83,13 @@
   CFLAGS_OPTIMIZE = -O6
 endif
 
-CFLAGS = -fno-omit-frame-pointer -ggdb3 -Wall -Wextra -std=gnu99 $(CFLAGS_WERROR) $(CFLAGS_OPTIMIZE) -D_FORTIFY_SOURCE=2 $(EXTRA_WARNINGS) $(EXTRA_CFLAGS)
+ifdef PARSER_DEBUG
+	PARSER_DEBUG_BISON  := -t
+	PARSER_DEBUG_FLEX   := -d
+	PARSER_DEBUG_CFLAGS := -DPARSER_DEBUG
+endif
+
+CFLAGS = -fno-omit-frame-pointer -ggdb3 -Wall -Wextra -std=gnu99 $(CFLAGS_WERROR) $(CFLAGS_OPTIMIZE) -D_FORTIFY_SOURCE=2 $(EXTRA_WARNINGS) $(EXTRA_CFLAGS) $(PARSER_DEBUG_CFLAGS)
 EXTLIBS = -lpthread -lrt -lelf -lm
 ALL_CFLAGS = $(CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE
 ALL_LDFLAGS = $(LDFLAGS)
@@ -149,7 +155,7 @@
 
 ### --- END CONFIGURATION SECTION ---
 
-BASIC_CFLAGS = -Iutil/include -Iarch/$(ARCH)/include -I$(OUTPUT)/util -I$(EVENT_PARSE_DIR) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE
+BASIC_CFLAGS = -Iutil/include -Iarch/$(ARCH)/include -I$(OUTPUT)/util -I$(TRACE_EVENT_DIR) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE
 BASIC_LDFLAGS =
 
 # Guard against environment variables
@@ -178,16 +184,16 @@
 
 SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH))
 
-EVENT_PARSE_DIR = ../lib/traceevent/
+TRACE_EVENT_DIR = ../lib/traceevent/
 
 ifeq ("$(origin O)", "command line")
-	EP_PATH=$(OUTPUT)/
+	TE_PATH=$(OUTPUT)/
 else
-	EP_PATH=$(EVENT_PARSE_DIR)/
+	TE_PATH=$(TRACE_EVENT_DIR)/
 endif
 
-LIBPARSEVENT = $(EP_PATH)libtraceevent.a
-EP_LIB := -L$(EP_PATH) -ltraceevent
+LIBTRACEEVENT = $(TE_PATH)libtraceevent.a
+TE_LIB := -L$(TE_PATH) -ltraceevent
 
 #
 # Single 'perf' binary right now:
@@ -216,10 +222,10 @@
 BISON= bison
 
 $(OUTPUT)util/parse-events-flex.c: util/parse-events.l
-	$(QUIET_FLEX)$(FLEX) --header-file=$(OUTPUT)util/parse-events-flex.h -t util/parse-events.l > $(OUTPUT)util/parse-events-flex.c
+	$(QUIET_FLEX)$(FLEX) --header-file=$(OUTPUT)util/parse-events-flex.h $(PARSER_DEBUG_FLEX) -t util/parse-events.l > $(OUTPUT)util/parse-events-flex.c
 
 $(OUTPUT)util/parse-events-bison.c: util/parse-events.y
-	$(QUIET_BISON)$(BISON) -v util/parse-events.y -d -o $(OUTPUT)util/parse-events-bison.c
+	$(QUIET_BISON)$(BISON) -v util/parse-events.y -d $(PARSER_DEBUG_BISON) -o $(OUTPUT)util/parse-events-bison.c
 
 $(OUTPUT)util/pmu-flex.c: util/pmu.l
 	$(QUIET_FLEX)$(FLEX) --header-file=$(OUTPUT)util/pmu-flex.h -t util/pmu.l > $(OUTPUT)util/pmu-flex.c
@@ -311,7 +317,7 @@
 LIB_H += util/top.h
 LIB_H += $(ARCH_INCLUDE)
 LIB_H += util/cgroup.h
-LIB_H += $(EVENT_PARSE_DIR)event-parse.h
+LIB_H += $(TRACE_EVENT_DIR)event-parse.h
 LIB_H += util/target.h
 
 LIB_OBJS += $(OUTPUT)util/abspath.o
@@ -332,6 +338,7 @@
 LIB_OBJS += $(OUTPUT)util/levenshtein.o
 LIB_OBJS += $(OUTPUT)util/parse-options.o
 LIB_OBJS += $(OUTPUT)util/parse-events.o
+LIB_OBJS += $(OUTPUT)util/parse-events-test.o
 LIB_OBJS += $(OUTPUT)util/path.o
 LIB_OBJS += $(OUTPUT)util/rbtree.o
 LIB_OBJS += $(OUTPUT)util/bitmap.o
@@ -410,7 +417,7 @@
 BUILTIN_OBJS += $(OUTPUT)builtin-test.o
 BUILTIN_OBJS += $(OUTPUT)builtin-inject.o
 
-PERFLIBS = $(LIB_FILE) $(LIBPARSEVENT)
+PERFLIBS = $(LIB_FILE) $(LIBTRACEEVENT)
 
 # Files needed for the python binding, perf.so
 # pyrf is just an internal name needed for all those wrappers.
@@ -819,9 +826,9 @@
 $(LIB_FILE): $(LIB_OBJS)
 	$(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIB_OBJS)
 
-# libparsevent.a
-$(LIBPARSEVENT):
-	make -C $(EVENT_PARSE_DIR) $(COMMAND_O) libtraceevent.a
+# libtraceevent.a
+$(LIBTRACEEVENT):
+	$(QUIET_SUBDIR0)$(TRACE_EVENT_DIR) $(QUIET_SUBDIR1) $(COMMAND_O) libtraceevent.a
 
 help:
 	@echo 'Perf make targets:'
@@ -969,6 +976,6 @@
 	$(RM) $(OUTPUT)util/*-{bison,flex}*
 	$(python-clean)
 
-.PHONY: all install clean strip
+.PHONY: all install clean strip $(LIBTRACEEVENT)
 .PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell
 .PHONY: .FORCE-PERF-VERSION-FILE TAGS tags cscope .FORCE-PERF-CFLAGS
diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c
index 5248046..6b2bcfb 100644
--- a/tools/perf/builtin-buildid-list.c
+++ b/tools/perf/builtin-buildid-list.c
@@ -84,7 +84,11 @@
 	if (filename__fprintf_build_id(session->filename, stdout))
 		goto out;
 
-	if (with_hits)
+	/*
+	 * in pipe-mode, the only way to get the buildids is to parse
+	 * the record stream. Buildids are stored as RECORD_HEADER_BUILD_ID
+	 */
+	if (with_hits || session->fd_pipe)
 		perf_session__process_events(session, &build_id__mark_dso_hit_ops);
 
 	perf_session__fprintf_dsos_buildid(session, stdout, with_hits);
diff --git a/tools/perf/builtin-evlist.c b/tools/perf/builtin-evlist.c
index 2676032..e52d77e 100644
--- a/tools/perf/builtin-evlist.c
+++ b/tools/perf/builtin-evlist.c
@@ -15,9 +15,40 @@
 #include "util/parse-options.h"
 #include "util/session.h"
 
-static const char *input_name;
+struct perf_attr_details {
+	bool freq;
+	bool verbose;
+};
 
-static int __cmd_evlist(void)
+static int comma_printf(bool *first, const char *fmt, ...)
+{
+	va_list args;
+	int ret = 0;
+
+	if (!*first) {
+		ret += printf(",");
+	} else {
+		ret += printf(":");
+		*first = false;
+	}
+
+	va_start(args, fmt);
+	ret += vprintf(fmt, args);
+	va_end(args);
+	return ret;
+}
+
+static int __if_print(bool *first, const char *field, u64 value)
+{
+	if (value == 0)
+		return 0;
+
+	return comma_printf(first, " %s: %" PRIu64, field, value);
+}
+
+#define if_print(field) __if_print(&first, #field, pos->attr.field)
+
+static int __cmd_evlist(const char *input_name, struct perf_attr_details *details)
 {
 	struct perf_session *session;
 	struct perf_evsel *pos;
@@ -26,8 +57,52 @@
 	if (session == NULL)
 		return -ENOMEM;
 
-	list_for_each_entry(pos, &session->evlist->entries, node)
-		printf("%s\n", event_name(pos));
+	list_for_each_entry(pos, &session->evlist->entries, node) {
+		bool first = true;
+
+		printf("%s", event_name(pos));
+
+		if (details->verbose || details->freq) {
+			comma_printf(&first, " sample_freq=%" PRIu64,
+				     (u64)pos->attr.sample_freq);
+		}
+
+		if (details->verbose) {
+			if_print(type);
+			if_print(config);
+			if_print(config1);
+			if_print(config2);
+			if_print(size);
+			if_print(sample_type);
+			if_print(read_format);
+			if_print(disabled);
+			if_print(inherit);
+			if_print(pinned);
+			if_print(exclusive);
+			if_print(exclude_user);
+			if_print(exclude_kernel);
+			if_print(exclude_hv);
+			if_print(exclude_idle);
+			if_print(mmap);
+			if_print(comm);
+			if_print(freq);
+			if_print(inherit_stat);
+			if_print(enable_on_exec);
+			if_print(task);
+			if_print(watermark);
+			if_print(precise_ip);
+			if_print(mmap_data);
+			if_print(sample_id_all);
+			if_print(exclude_host);
+			if_print(exclude_guest);
+			if_print(__reserved_1);
+			if_print(wakeup_events);
+			if_print(bp_type);
+			if_print(branch_sample_type);
+		}
+
+		putchar('\n');
+	}
 
 	perf_session__delete(session);
 	return 0;
@@ -38,17 +113,23 @@
 	NULL
 };
 
-static const struct option options[] = {
-	OPT_STRING('i', "input", &input_name, "file",
-		    "input file name"),
-	OPT_END()
-};
-
 int cmd_evlist(int argc, const char **argv, const char *prefix __used)
 {
+	struct perf_attr_details details = { .verbose = false, };
+	const char *input_name;
+	const struct option options[] = {
+		OPT_STRING('i', "input", &input_name, "file",
+			    "Input file name"),
+		OPT_BOOLEAN('F', "freq", &details.freq,
+			    "Show the sample frequency"),
+		OPT_BOOLEAN('v', "verbose", &details.verbose,
+			    "Show all event attr details"),
+		OPT_END()
+	};
+
 	argc = parse_options(argc, argv, options, evlist_usage, 0);
 	if (argc)
 		usage_with_options(evlist_usage, options);
 
-	return __cmd_evlist();
+	return __cmd_evlist(input_name, &details);
 }
diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c
index 09c1061..3beab48 100644
--- a/tools/perf/builtin-inject.c
+++ b/tools/perf/builtin-inject.c
@@ -60,6 +60,11 @@
 static int perf_event__repipe_attr(union perf_event *event,
 				   struct perf_evlist **pevlist __used)
 {
+	int ret;
+	ret = perf_event__process_attr(event, pevlist);
+	if (ret)
+		return ret;
+
 	return perf_event__repipe_synth(NULL, event, NULL);
 }
 
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 8a3dfac..e5cb084 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -396,7 +396,7 @@
 			perf_record__mmap_read(rec, &rec->evlist->mmap[i]);
 	}
 
-	if (perf_header__has_feat(&rec->session->header, HEADER_TRACE_INFO))
+	if (perf_header__has_feat(&rec->session->header, HEADER_TRACING_DATA))
 		write_output(rec, &finished_round_event, sizeof(finished_round_event));
 }
 
@@ -478,7 +478,7 @@
 		perf_header__clear_feat(&session->header, HEADER_BUILD_ID);
 
 	if (!have_tracepoints(&evsel_list->entries))
-		perf_header__clear_feat(&session->header, HEADER_TRACE_INFO);
+		perf_header__clear_feat(&session->header, HEADER_TRACING_DATA);
 
 	if (!rec->opts.branch_stack)
 		perf_header__clear_feat(&session->header, HEADER_BRANCH_STACK);
@@ -753,7 +753,7 @@
 		.mmap_pages	     = UINT_MAX,
 		.user_freq	     = UINT_MAX,
 		.user_interval	     = ULLONG_MAX,
-		.freq		     = 1000,
+		.freq		     = 4000,
 		.target		     = {
 			.uses_mmap   = true,
 		},
diff --git a/tools/perf/builtin-test.c b/tools/perf/builtin-test.c
index 6c47376..5a8727c 100644
--- a/tools/perf/builtin-test.c
+++ b/tools/perf/builtin-test.c
@@ -604,556 +604,6 @@
 #undef nsyscalls
 }
 
-#define TEST_ASSERT_VAL(text, cond) \
-do { \
-	if (!(cond)) { \
-		pr_debug("FAILED %s:%d %s\n", __FILE__, __LINE__, text); \
-		return -1; \
-	} \
-} while (0)
-
-static int test__checkevent_tracepoint(struct perf_evlist *evlist)
-{
-	struct perf_evsel *evsel = list_entry(evlist->entries.next,
-					      struct perf_evsel, node);
-
-	TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
-	TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->attr.type);
-	TEST_ASSERT_VAL("wrong sample_type",
-		(PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | PERF_SAMPLE_CPU) ==
-		evsel->attr.sample_type);
-	TEST_ASSERT_VAL("wrong sample_period", 1 == evsel->attr.sample_period);
-	return 0;
-}
-
-static int test__checkevent_tracepoint_multi(struct perf_evlist *evlist)
-{
-	struct perf_evsel *evsel;
-
-	TEST_ASSERT_VAL("wrong number of entries", evlist->nr_entries > 1);
-
-	list_for_each_entry(evsel, &evlist->entries, node) {
-		TEST_ASSERT_VAL("wrong type",
-			PERF_TYPE_TRACEPOINT == evsel->attr.type);
-		TEST_ASSERT_VAL("wrong sample_type",
-			(PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | PERF_SAMPLE_CPU)
-			== evsel->attr.sample_type);
-		TEST_ASSERT_VAL("wrong sample_period",
-			1 == evsel->attr.sample_period);
-	}
-	return 0;
-}
-
-static int test__checkevent_raw(struct perf_evlist *evlist)
-{
-	struct perf_evsel *evsel = list_entry(evlist->entries.next,
-					      struct perf_evsel, node);
-
-	TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
-	TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);
-	TEST_ASSERT_VAL("wrong config", 0x1a == evsel->attr.config);
-	return 0;
-}
-
-static int test__checkevent_numeric(struct perf_evlist *evlist)
-{
-	struct perf_evsel *evsel = list_entry(evlist->entries.next,
-					      struct perf_evsel, node);
-
-	TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
-	TEST_ASSERT_VAL("wrong type", 1 == evsel->attr.type);
-	TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config);
-	return 0;
-}
-
-static int test__checkevent_symbolic_name(struct perf_evlist *evlist)
-{
-	struct perf_evsel *evsel = list_entry(evlist->entries.next,
-					      struct perf_evsel, node);
-
-	TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
-	TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
-	TEST_ASSERT_VAL("wrong config",
-			PERF_COUNT_HW_INSTRUCTIONS == evsel->attr.config);
-	return 0;
-}
-
-static int test__checkevent_symbolic_name_config(struct perf_evlist *evlist)
-{
-	struct perf_evsel *evsel = list_entry(evlist->entries.next,
-					      struct perf_evsel, node);
-
-	TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
-	TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
-	TEST_ASSERT_VAL("wrong config",
-			PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config);
-	TEST_ASSERT_VAL("wrong period",
-			100000 == evsel->attr.sample_period);
-	TEST_ASSERT_VAL("wrong config1",
-			0 == evsel->attr.config1);
-	TEST_ASSERT_VAL("wrong config2",
-			1 == evsel->attr.config2);
-	return 0;
-}
-
-static int test__checkevent_symbolic_alias(struct perf_evlist *evlist)
-{
-	struct perf_evsel *evsel = list_entry(evlist->entries.next,
-					      struct perf_evsel, node);
-
-	TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
-	TEST_ASSERT_VAL("wrong type", PERF_TYPE_SOFTWARE == evsel->attr.type);
-	TEST_ASSERT_VAL("wrong config",
-			PERF_COUNT_SW_PAGE_FAULTS == evsel->attr.config);
-	return 0;
-}
-
-static int test__checkevent_genhw(struct perf_evlist *evlist)
-{
-	struct perf_evsel *evsel = list_entry(evlist->entries.next,
-					      struct perf_evsel, node);
-
-	TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
-	TEST_ASSERT_VAL("wrong type", PERF_TYPE_HW_CACHE == evsel->attr.type);
-	TEST_ASSERT_VAL("wrong config", (1 << 16) == evsel->attr.config);
-	return 0;
-}
-
-static int test__checkevent_breakpoint(struct perf_evlist *evlist)
-{
-	struct perf_evsel *evsel = list_entry(evlist->entries.next,
-					      struct perf_evsel, node);
-
-	TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
-	TEST_ASSERT_VAL("wrong type", PERF_TYPE_BREAKPOINT == evsel->attr.type);
-	TEST_ASSERT_VAL("wrong config", 0 == evsel->attr.config);
-	TEST_ASSERT_VAL("wrong bp_type", (HW_BREAKPOINT_R | HW_BREAKPOINT_W) ==
-					 evsel->attr.bp_type);
-	TEST_ASSERT_VAL("wrong bp_len", HW_BREAKPOINT_LEN_4 ==
-					evsel->attr.bp_len);
-	return 0;
-}
-
-static int test__checkevent_breakpoint_x(struct perf_evlist *evlist)
-{
-	struct perf_evsel *evsel = list_entry(evlist->entries.next,
-					      struct perf_evsel, node);
-
-	TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
-	TEST_ASSERT_VAL("wrong type", PERF_TYPE_BREAKPOINT == evsel->attr.type);
-	TEST_ASSERT_VAL("wrong config", 0 == evsel->attr.config);
-	TEST_ASSERT_VAL("wrong bp_type",
-			HW_BREAKPOINT_X == evsel->attr.bp_type);
-	TEST_ASSERT_VAL("wrong bp_len", sizeof(long) == evsel->attr.bp_len);
-	return 0;
-}
-
-static int test__checkevent_breakpoint_r(struct perf_evlist *evlist)
-{
-	struct perf_evsel *evsel = list_entry(evlist->entries.next,
-					      struct perf_evsel, node);
-
-	TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
-	TEST_ASSERT_VAL("wrong type",
-			PERF_TYPE_BREAKPOINT == evsel->attr.type);
-	TEST_ASSERT_VAL("wrong config", 0 == evsel->attr.config);
-	TEST_ASSERT_VAL("wrong bp_type",
-			HW_BREAKPOINT_R == evsel->attr.bp_type);
-	TEST_ASSERT_VAL("wrong bp_len",
-			HW_BREAKPOINT_LEN_4 == evsel->attr.bp_len);
-	return 0;
-}
-
-static int test__checkevent_breakpoint_w(struct perf_evlist *evlist)
-{
-	struct perf_evsel *evsel = list_entry(evlist->entries.next,
-					      struct perf_evsel, node);
-
-	TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
-	TEST_ASSERT_VAL("wrong type",
-			PERF_TYPE_BREAKPOINT == evsel->attr.type);
-	TEST_ASSERT_VAL("wrong config", 0 == evsel->attr.config);
-	TEST_ASSERT_VAL("wrong bp_type",
-			HW_BREAKPOINT_W == evsel->attr.bp_type);
-	TEST_ASSERT_VAL("wrong bp_len",
-			HW_BREAKPOINT_LEN_4 == evsel->attr.bp_len);
-	return 0;
-}
-
-static int test__checkevent_tracepoint_modifier(struct perf_evlist *evlist)
-{
-	struct perf_evsel *evsel = list_entry(evlist->entries.next,
-					      struct perf_evsel, node);
-
-	TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
-	TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
-	TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
-	TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
-
-	return test__checkevent_tracepoint(evlist);
-}
-
-static int
-test__checkevent_tracepoint_multi_modifier(struct perf_evlist *evlist)
-{
-	struct perf_evsel *evsel;
-
-	TEST_ASSERT_VAL("wrong number of entries", evlist->nr_entries > 1);
-
-	list_for_each_entry(evsel, &evlist->entries, node) {
-		TEST_ASSERT_VAL("wrong exclude_user",
-				!evsel->attr.exclude_user);
-		TEST_ASSERT_VAL("wrong exclude_kernel",
-				evsel->attr.exclude_kernel);
-		TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
-		TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
-	}
-
-	return test__checkevent_tracepoint_multi(evlist);
-}
-
-static int test__checkevent_raw_modifier(struct perf_evlist *evlist)
-{
-	struct perf_evsel *evsel = list_entry(evlist->entries.next,
-					      struct perf_evsel, node);
-
-	TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
-	TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
-	TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
-	TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip);
-
-	return test__checkevent_raw(evlist);
-}
-
-static int test__checkevent_numeric_modifier(struct perf_evlist *evlist)
-{
-	struct perf_evsel *evsel = list_entry(evlist->entries.next,
-					      struct perf_evsel, node);
-
-	TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
-	TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
-	TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
-	TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip);
-
-	return test__checkevent_numeric(evlist);
-}
-
-static int test__checkevent_symbolic_name_modifier(struct perf_evlist *evlist)
-{
-	struct perf_evsel *evsel = list_entry(evlist->entries.next,
-					      struct perf_evsel, node);
-
-	TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
-	TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
-	TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
-	TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
-
-	return test__checkevent_symbolic_name(evlist);
-}
-
-static int test__checkevent_exclude_host_modifier(struct perf_evlist *evlist)
-{
-	struct perf_evsel *evsel = list_entry(evlist->entries.next,
-					      struct perf_evsel, node);
-
-	TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
-	TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host);
-
-	return test__checkevent_symbolic_name(evlist);
-}
-
-static int test__checkevent_exclude_guest_modifier(struct perf_evlist *evlist)
-{
-	struct perf_evsel *evsel = list_entry(evlist->entries.next,
-					      struct perf_evsel, node);
-
-	TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest);
-	TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
-
-	return test__checkevent_symbolic_name(evlist);
-}
-
-static int test__checkevent_symbolic_alias_modifier(struct perf_evlist *evlist)
-{
-	struct perf_evsel *evsel = list_entry(evlist->entries.next,
-					      struct perf_evsel, node);
-
-	TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
-	TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
-	TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
-	TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
-
-	return test__checkevent_symbolic_alias(evlist);
-}
-
-static int test__checkevent_genhw_modifier(struct perf_evlist *evlist)
-{
-	struct perf_evsel *evsel = list_entry(evlist->entries.next,
-					      struct perf_evsel, node);
-
-	TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
-	TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
-	TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
-	TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip);
-
-	return test__checkevent_genhw(evlist);
-}
-
-static int test__checkevent_breakpoint_modifier(struct perf_evlist *evlist)
-{
-	struct perf_evsel *evsel = list_entry(evlist->entries.next,
-					      struct perf_evsel, node);
-
-	TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
-	TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
-	TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
-	TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
-
-	return test__checkevent_breakpoint(evlist);
-}
-
-static int test__checkevent_breakpoint_x_modifier(struct perf_evlist *evlist)
-{
-	struct perf_evsel *evsel = list_entry(evlist->entries.next,
-					      struct perf_evsel, node);
-
-	TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
-	TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
-	TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
-	TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
-
-	return test__checkevent_breakpoint_x(evlist);
-}
-
-static int test__checkevent_breakpoint_r_modifier(struct perf_evlist *evlist)
-{
-	struct perf_evsel *evsel = list_entry(evlist->entries.next,
-					      struct perf_evsel, node);
-
-	TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
-	TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
-	TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
-	TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip);
-
-	return test__checkevent_breakpoint_r(evlist);
-}
-
-static int test__checkevent_breakpoint_w_modifier(struct perf_evlist *evlist)
-{
-	struct perf_evsel *evsel = list_entry(evlist->entries.next,
-					      struct perf_evsel, node);
-
-	TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
-	TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
-	TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
-	TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip);
-
-	return test__checkevent_breakpoint_w(evlist);
-}
-
-static int test__checkevent_pmu(struct perf_evlist *evlist)
-{
-
-	struct perf_evsel *evsel = list_entry(evlist->entries.next,
-					      struct perf_evsel, node);
-
-	TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
-	TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);
-	TEST_ASSERT_VAL("wrong config",    10 == evsel->attr.config);
-	TEST_ASSERT_VAL("wrong config1",    1 == evsel->attr.config1);
-	TEST_ASSERT_VAL("wrong config2",    3 == evsel->attr.config2);
-	TEST_ASSERT_VAL("wrong period",  1000 == evsel->attr.sample_period);
-
-	return 0;
-}
-
-static int test__checkevent_list(struct perf_evlist *evlist)
-{
-	struct perf_evsel *evsel;
-
-	TEST_ASSERT_VAL("wrong number of entries", 3 == evlist->nr_entries);
-
-	/* r1 */
-	evsel = list_entry(evlist->entries.next, struct perf_evsel, node);
-	TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);
-	TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config);
-	TEST_ASSERT_VAL("wrong config1", 0 == evsel->attr.config1);
-	TEST_ASSERT_VAL("wrong config2", 0 == evsel->attr.config2);
-	TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
-	TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
-	TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
-	TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
-
-	/* syscalls:sys_enter_open:k */
-	evsel = list_entry(evsel->node.next, struct perf_evsel, node);
-	TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->attr.type);
-	TEST_ASSERT_VAL("wrong sample_type",
-		(PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | PERF_SAMPLE_CPU) ==
-		evsel->attr.sample_type);
-	TEST_ASSERT_VAL("wrong sample_period", 1 == evsel->attr.sample_period);
-	TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
-	TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
-	TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
-	TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
-
-	/* 1:1:hp */
-	evsel = list_entry(evsel->node.next, struct perf_evsel, node);
-	TEST_ASSERT_VAL("wrong type", 1 == evsel->attr.type);
-	TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config);
-	TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
-	TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
-	TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
-	TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip);
-
-	return 0;
-}
-
-static struct test__event_st {
-	const char *name;
-	__u32 type;
-	int (*check)(struct perf_evlist *evlist);
-} test__events[] = {
-	{
-		.name  = "syscalls:sys_enter_open",
-		.check = test__checkevent_tracepoint,
-	},
-	{
-		.name  = "syscalls:*",
-		.check = test__checkevent_tracepoint_multi,
-	},
-	{
-		.name  = "r1a",
-		.check = test__checkevent_raw,
-	},
-	{
-		.name  = "1:1",
-		.check = test__checkevent_numeric,
-	},
-	{
-		.name  = "instructions",
-		.check = test__checkevent_symbolic_name,
-	},
-	{
-		.name  = "cycles/period=100000,config2/",
-		.check = test__checkevent_symbolic_name_config,
-	},
-	{
-		.name  = "faults",
-		.check = test__checkevent_symbolic_alias,
-	},
-	{
-		.name  = "L1-dcache-load-miss",
-		.check = test__checkevent_genhw,
-	},
-	{
-		.name  = "mem:0",
-		.check = test__checkevent_breakpoint,
-	},
-	{
-		.name  = "mem:0:x",
-		.check = test__checkevent_breakpoint_x,
-	},
-	{
-		.name  = "mem:0:r",
-		.check = test__checkevent_breakpoint_r,
-	},
-	{
-		.name  = "mem:0:w",
-		.check = test__checkevent_breakpoint_w,
-	},
-	{
-		.name  = "syscalls:sys_enter_open:k",
-		.check = test__checkevent_tracepoint_modifier,
-	},
-	{
-		.name  = "syscalls:*:u",
-		.check = test__checkevent_tracepoint_multi_modifier,
-	},
-	{
-		.name  = "r1a:kp",
-		.check = test__checkevent_raw_modifier,
-	},
-	{
-		.name  = "1:1:hp",
-		.check = test__checkevent_numeric_modifier,
-	},
-	{
-		.name  = "instructions:h",
-		.check = test__checkevent_symbolic_name_modifier,
-	},
-	{
-		.name  = "faults:u",
-		.check = test__checkevent_symbolic_alias_modifier,
-	},
-	{
-		.name  = "L1-dcache-load-miss:kp",
-		.check = test__checkevent_genhw_modifier,
-	},
-	{
-		.name  = "mem:0:u",
-		.check = test__checkevent_breakpoint_modifier,
-	},
-	{
-		.name  = "mem:0:x:k",
-		.check = test__checkevent_breakpoint_x_modifier,
-	},
-	{
-		.name  = "mem:0:r:hp",
-		.check = test__checkevent_breakpoint_r_modifier,
-	},
-	{
-		.name  = "mem:0:w:up",
-		.check = test__checkevent_breakpoint_w_modifier,
-	},
-	{
-		.name  = "cpu/config=10,config1,config2=3,period=1000/u",
-		.check = test__checkevent_pmu,
-	},
-	{
-		.name  = "r1,syscalls:sys_enter_open:k,1:1:hp",
-		.check = test__checkevent_list,
-	},
-	{
-		.name  = "instructions:G",
-		.check = test__checkevent_exclude_host_modifier,
-	},
-	{
-		.name  = "instructions:H",
-		.check = test__checkevent_exclude_guest_modifier,
-	},
-};
-
-#define TEST__EVENTS_CNT (sizeof(test__events) / sizeof(struct test__event_st))
-
-static int test__parse_events(void)
-{
-	struct perf_evlist *evlist;
-	u_int i;
-	int ret = 0;
-
-	for (i = 0; i < TEST__EVENTS_CNT; i++) {
-		struct test__event_st *e = &test__events[i];
-
-		evlist = perf_evlist__new(NULL, NULL);
-		if (evlist == NULL)
-			break;
-
-		ret = parse_events(evlist, e->name, 0);
-		if (ret) {
-			pr_debug("failed to parse event '%s', err %d\n",
-				 e->name, ret);
-			break;
-		}
-
-		ret = e->check(evlist);
-		perf_evlist__delete(evlist);
-		if (ret)
-			break;
-	}
-
-	return ret;
-}
-
 static int sched__get_first_possible_cpu(pid_t pid, cpu_set_t **maskp,
 					 size_t *sizep)
 {
@@ -1675,7 +1125,7 @@
 	},
 	{
 		.desc = "parse events tests",
-		.func = test__parse_events,
+		.func = parse_events__test,
 	},
 #if defined(__x86_64__) || defined(__i386__)
 	{
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index 3e981a7..6031dce 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -900,6 +900,9 @@
 			attr->read_format |= PERF_FORMAT_ID;
 		}
 
+		if (perf_target__has_cpu(&top->target))
+			attr->sample_type |= PERF_SAMPLE_CPU;
+
 		if (symbol_conf.use_callchain)
 			attr->sample_type |= PERF_SAMPLE_CALLCHAIN;
 
@@ -1159,7 +1162,7 @@
 	struct perf_top top = {
 		.count_filter	     = 5,
 		.delay_secs	     = 2,
-		.freq		     = 1000, /* 1 KHz */
+		.freq		     = 4000, /* 4 KHz */
 		.mmap_pages	     = 128,
 		.sym_pcnt_filter     = 5,
 		.target		     = {
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c
index dff9c7a..fd9a594 100644
--- a/tools/perf/util/build-id.c
+++ b/tools/perf/util/build-id.c
@@ -65,6 +65,8 @@
 	.mmap	= perf_event__process_mmap,
 	.fork	= perf_event__process_task,
 	.exit	= perf_event__exit_del_thread,
+	.attr		 = perf_event__process_attr,
+	.build_id	 = perf_event__process_build_id,
 };
 
 char *dso__build_id_filename(struct dso *self, char *bf, size_t size)
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index f4f427c..57e4ce5 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -108,7 +108,7 @@
 	if (opts->call_graph)
 		attr->sample_type	|= PERF_SAMPLE_CALLCHAIN;
 
-	if (opts->target.system_wide)
+	if (perf_target__has_cpu(&opts->target))
 		attr->sample_type	|= PERF_SAMPLE_CPU;
 
 	if (opts->period)
@@ -462,10 +462,7 @@
 	 * used for cross-endian analysis. See git commit 65014ab3
 	 * for why this goofiness is needed.
 	 */
-	union {
-		u64 val64;
-		u32 val32[2];
-	} u;
+	union u64_swap u;
 
 	memset(data, 0, sizeof(*data));
 	data->cpu = data->pid = data->tid = -1;
@@ -608,10 +605,7 @@
 	 * used for cross-endian analysis. See git commit 65014ab3
 	 * for why this goofiness is needed.
 	 */
-	union {
-		u64 val64;
-		u32 val32[2];
-	} u;
+	union u64_swap u;
 
 	array = event->sample.array;
 
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 5385980..2dd5edf 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -437,7 +437,7 @@
 	return ret;
 }
 
-static int write_trace_info(int fd, struct perf_header *h __used,
+static int write_tracing_data(int fd, struct perf_header *h __used,
 			    struct perf_evlist *evlist)
 {
 	return read_tracing_data(fd, &evlist->entries);
@@ -1472,7 +1472,7 @@
 	return err;
 }
 
-static int process_trace_info(struct perf_file_section *section __unused,
+static int process_tracing_data(struct perf_file_section *section __unused,
 			      struct perf_header *ph __unused,
 			      int feat __unused, int fd)
 {
@@ -1508,11 +1508,11 @@
 		.full_only = true }
 
 /* feature_ops not implemented: */
-#define print_trace_info		NULL
-#define print_build_id			NULL
+#define print_tracing_data	NULL
+#define print_build_id		NULL
 
 static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = {
-	FEAT_OPP(HEADER_TRACE_INFO,	trace_info),
+	FEAT_OPP(HEADER_TRACING_DATA,	tracing_data),
 	FEAT_OPP(HEADER_BUILD_ID,	build_id),
 	FEAT_OPA(HEADER_HOSTNAME,	hostname),
 	FEAT_OPA(HEADER_OSRELEASE,	osrelease),
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h
index 21a6be0..2d42b3e 100644
--- a/tools/perf/util/header.h
+++ b/tools/perf/util/header.h
@@ -12,7 +12,7 @@
 enum {
 	HEADER_RESERVED		= 0,	/* always cleared */
 	HEADER_FIRST_FEATURE	= 1,
-	HEADER_TRACE_INFO	= 1,
+	HEADER_TRACING_DATA	= 1,
 	HEADER_BUILD_ID,
 
 	HEADER_HOSTNAME,
diff --git a/tools/perf/util/parse-events-test.c b/tools/perf/util/parse-events-test.c
new file mode 100644
index 0000000..76b98e2
--- /dev/null
+++ b/tools/perf/util/parse-events-test.c
@@ -0,0 +1,625 @@
+
+#include "parse-events.h"
+#include "evsel.h"
+#include "evlist.h"
+#include "sysfs.h"
+#include "../../../include/linux/hw_breakpoint.h"
+
+#define TEST_ASSERT_VAL(text, cond) \
+do { \
+	if (!(cond)) { \
+		pr_debug("FAILED %s:%d %s\n", __FILE__, __LINE__, text); \
+		return -1; \
+	} \
+} while (0)
+
+static int test__checkevent_tracepoint(struct perf_evlist *evlist)
+{
+	struct perf_evsel *evsel = list_entry(evlist->entries.next,
+					      struct perf_evsel, node);
+
+	TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
+	TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->attr.type);
+	TEST_ASSERT_VAL("wrong sample_type",
+		(PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | PERF_SAMPLE_CPU) ==
+		evsel->attr.sample_type);
+	TEST_ASSERT_VAL("wrong sample_period", 1 == evsel->attr.sample_period);
+	return 0;
+}
+
+static int test__checkevent_tracepoint_multi(struct perf_evlist *evlist)
+{
+	struct perf_evsel *evsel;
+
+	TEST_ASSERT_VAL("wrong number of entries", evlist->nr_entries > 1);
+
+	list_for_each_entry(evsel, &evlist->entries, node) {
+		TEST_ASSERT_VAL("wrong type",
+			PERF_TYPE_TRACEPOINT == evsel->attr.type);
+		TEST_ASSERT_VAL("wrong sample_type",
+			(PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | PERF_SAMPLE_CPU)
+			== evsel->attr.sample_type);
+		TEST_ASSERT_VAL("wrong sample_period",
+			1 == evsel->attr.sample_period);
+	}
+	return 0;
+}
+
+static int test__checkevent_raw(struct perf_evlist *evlist)
+{
+	struct perf_evsel *evsel = list_entry(evlist->entries.next,
+					      struct perf_evsel, node);
+
+	TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
+	TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);
+	TEST_ASSERT_VAL("wrong config", 0x1a == evsel->attr.config);
+	return 0;
+}
+
+static int test__checkevent_numeric(struct perf_evlist *evlist)
+{
+	struct perf_evsel *evsel = list_entry(evlist->entries.next,
+					      struct perf_evsel, node);
+
+	TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
+	TEST_ASSERT_VAL("wrong type", 1 == evsel->attr.type);
+	TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config);
+	return 0;
+}
+
+static int test__checkevent_symbolic_name(struct perf_evlist *evlist)
+{
+	struct perf_evsel *evsel = list_entry(evlist->entries.next,
+					      struct perf_evsel, node);
+
+	TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
+	TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+	TEST_ASSERT_VAL("wrong config",
+			PERF_COUNT_HW_INSTRUCTIONS == evsel->attr.config);
+	return 0;
+}
+
+static int test__checkevent_symbolic_name_config(struct perf_evlist *evlist)
+{
+	struct perf_evsel *evsel = list_entry(evlist->entries.next,
+					      struct perf_evsel, node);
+
+	TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
+	TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+	TEST_ASSERT_VAL("wrong config",
+			PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config);
+	TEST_ASSERT_VAL("wrong period",
+			100000 == evsel->attr.sample_period);
+	TEST_ASSERT_VAL("wrong config1",
+			0 == evsel->attr.config1);
+	TEST_ASSERT_VAL("wrong config2",
+			1 == evsel->attr.config2);
+	return 0;
+}
+
+static int test__checkevent_symbolic_alias(struct perf_evlist *evlist)
+{
+	struct perf_evsel *evsel = list_entry(evlist->entries.next,
+					      struct perf_evsel, node);
+
+	TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
+	TEST_ASSERT_VAL("wrong type", PERF_TYPE_SOFTWARE == evsel->attr.type);
+	TEST_ASSERT_VAL("wrong config",
+			PERF_COUNT_SW_PAGE_FAULTS == evsel->attr.config);
+	return 0;
+}
+
+static int test__checkevent_genhw(struct perf_evlist *evlist)
+{
+	struct perf_evsel *evsel = list_entry(evlist->entries.next,
+					      struct perf_evsel, node);
+
+	TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
+	TEST_ASSERT_VAL("wrong type", PERF_TYPE_HW_CACHE == evsel->attr.type);
+	TEST_ASSERT_VAL("wrong config", (1 << 16) == evsel->attr.config);
+	return 0;
+}
+
+static int test__checkevent_breakpoint(struct perf_evlist *evlist)
+{
+	struct perf_evsel *evsel = list_entry(evlist->entries.next,
+					      struct perf_evsel, node);
+
+	TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
+	TEST_ASSERT_VAL("wrong type", PERF_TYPE_BREAKPOINT == evsel->attr.type);
+	TEST_ASSERT_VAL("wrong config", 0 == evsel->attr.config);
+	TEST_ASSERT_VAL("wrong bp_type", (HW_BREAKPOINT_R | HW_BREAKPOINT_W) ==
+					 evsel->attr.bp_type);
+	TEST_ASSERT_VAL("wrong bp_len", HW_BREAKPOINT_LEN_4 ==
+					evsel->attr.bp_len);
+	return 0;
+}
+
+static int test__checkevent_breakpoint_x(struct perf_evlist *evlist)
+{
+	struct perf_evsel *evsel = list_entry(evlist->entries.next,
+					      struct perf_evsel, node);
+
+	TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
+	TEST_ASSERT_VAL("wrong type", PERF_TYPE_BREAKPOINT == evsel->attr.type);
+	TEST_ASSERT_VAL("wrong config", 0 == evsel->attr.config);
+	TEST_ASSERT_VAL("wrong bp_type",
+			HW_BREAKPOINT_X == evsel->attr.bp_type);
+	TEST_ASSERT_VAL("wrong bp_len", sizeof(long) == evsel->attr.bp_len);
+	return 0;
+}
+
+static int test__checkevent_breakpoint_r(struct perf_evlist *evlist)
+{
+	struct perf_evsel *evsel = list_entry(evlist->entries.next,
+					      struct perf_evsel, node);
+
+	TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
+	TEST_ASSERT_VAL("wrong type",
+			PERF_TYPE_BREAKPOINT == evsel->attr.type);
+	TEST_ASSERT_VAL("wrong config", 0 == evsel->attr.config);
+	TEST_ASSERT_VAL("wrong bp_type",
+			HW_BREAKPOINT_R == evsel->attr.bp_type);
+	TEST_ASSERT_VAL("wrong bp_len",
+			HW_BREAKPOINT_LEN_4 == evsel->attr.bp_len);
+	return 0;
+}
+
+static int test__checkevent_breakpoint_w(struct perf_evlist *evlist)
+{
+	struct perf_evsel *evsel = list_entry(evlist->entries.next,
+					      struct perf_evsel, node);
+
+	TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
+	TEST_ASSERT_VAL("wrong type",
+			PERF_TYPE_BREAKPOINT == evsel->attr.type);
+	TEST_ASSERT_VAL("wrong config", 0 == evsel->attr.config);
+	TEST_ASSERT_VAL("wrong bp_type",
+			HW_BREAKPOINT_W == evsel->attr.bp_type);
+	TEST_ASSERT_VAL("wrong bp_len",
+			HW_BREAKPOINT_LEN_4 == evsel->attr.bp_len);
+	return 0;
+}
+
+static int test__checkevent_tracepoint_modifier(struct perf_evlist *evlist)
+{
+	struct perf_evsel *evsel = list_entry(evlist->entries.next,
+					      struct perf_evsel, node);
+
+	TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
+	TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
+	TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
+	TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+
+	return test__checkevent_tracepoint(evlist);
+}
+
+static int
+test__checkevent_tracepoint_multi_modifier(struct perf_evlist *evlist)
+{
+	struct perf_evsel *evsel;
+
+	TEST_ASSERT_VAL("wrong number of entries", evlist->nr_entries > 1);
+
+	list_for_each_entry(evsel, &evlist->entries, node) {
+		TEST_ASSERT_VAL("wrong exclude_user",
+				!evsel->attr.exclude_user);
+		TEST_ASSERT_VAL("wrong exclude_kernel",
+				evsel->attr.exclude_kernel);
+		TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
+		TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+	}
+
+	return test__checkevent_tracepoint_multi(evlist);
+}
+
+static int test__checkevent_raw_modifier(struct perf_evlist *evlist)
+{
+	struct perf_evsel *evsel = list_entry(evlist->entries.next,
+					      struct perf_evsel, node);
+
+	TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
+	TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
+	TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
+	TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip);
+
+	return test__checkevent_raw(evlist);
+}
+
+static int test__checkevent_numeric_modifier(struct perf_evlist *evlist)
+{
+	struct perf_evsel *evsel = list_entry(evlist->entries.next,
+					      struct perf_evsel, node);
+
+	TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
+	TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
+	TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
+	TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip);
+
+	return test__checkevent_numeric(evlist);
+}
+
+static int test__checkevent_symbolic_name_modifier(struct perf_evlist *evlist)
+{
+	struct perf_evsel *evsel = list_entry(evlist->entries.next,
+					      struct perf_evsel, node);
+
+	TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
+	TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
+	TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
+	TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+
+	return test__checkevent_symbolic_name(evlist);
+}
+
+static int test__checkevent_exclude_host_modifier(struct perf_evlist *evlist)
+{
+	struct perf_evsel *evsel = list_entry(evlist->entries.next,
+					      struct perf_evsel, node);
+
+	TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
+	TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host);
+
+	return test__checkevent_symbolic_name(evlist);
+}
+
+static int test__checkevent_exclude_guest_modifier(struct perf_evlist *evlist)
+{
+	struct perf_evsel *evsel = list_entry(evlist->entries.next,
+					      struct perf_evsel, node);
+
+	TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest);
+	TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
+
+	return test__checkevent_symbolic_name(evlist);
+}
+
+static int test__checkevent_symbolic_alias_modifier(struct perf_evlist *evlist)
+{
+	struct perf_evsel *evsel = list_entry(evlist->entries.next,
+					      struct perf_evsel, node);
+
+	TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
+	TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
+	TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
+	TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+
+	return test__checkevent_symbolic_alias(evlist);
+}
+
+static int test__checkevent_genhw_modifier(struct perf_evlist *evlist)
+{
+	struct perf_evsel *evsel = list_entry(evlist->entries.next,
+					      struct perf_evsel, node);
+
+	TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
+	TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
+	TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
+	TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip);
+
+	return test__checkevent_genhw(evlist);
+}
+
+static int test__checkevent_breakpoint_modifier(struct perf_evlist *evlist)
+{
+	struct perf_evsel *evsel = list_entry(evlist->entries.next,
+					      struct perf_evsel, node);
+
+	TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
+	TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
+	TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
+	TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+
+	return test__checkevent_breakpoint(evlist);
+}
+
+static int test__checkevent_breakpoint_x_modifier(struct perf_evlist *evlist)
+{
+	struct perf_evsel *evsel = list_entry(evlist->entries.next,
+					      struct perf_evsel, node);
+
+	TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
+	TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
+	TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
+	TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+
+	return test__checkevent_breakpoint_x(evlist);
+}
+
+static int test__checkevent_breakpoint_r_modifier(struct perf_evlist *evlist)
+{
+	struct perf_evsel *evsel = list_entry(evlist->entries.next,
+					      struct perf_evsel, node);
+
+	TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
+	TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
+	TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
+	TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip);
+
+	return test__checkevent_breakpoint_r(evlist);
+}
+
+static int test__checkevent_breakpoint_w_modifier(struct perf_evlist *evlist)
+{
+	struct perf_evsel *evsel = list_entry(evlist->entries.next,
+					      struct perf_evsel, node);
+
+	TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
+	TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
+	TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
+	TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip);
+
+	return test__checkevent_breakpoint_w(evlist);
+}
+
+static int test__checkevent_pmu(struct perf_evlist *evlist)
+{
+
+	struct perf_evsel *evsel = list_entry(evlist->entries.next,
+					      struct perf_evsel, node);
+
+	TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
+	TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);
+	TEST_ASSERT_VAL("wrong config",    10 == evsel->attr.config);
+	TEST_ASSERT_VAL("wrong config1",    1 == evsel->attr.config1);
+	TEST_ASSERT_VAL("wrong config2",    3 == evsel->attr.config2);
+	TEST_ASSERT_VAL("wrong period",  1000 == evsel->attr.sample_period);
+
+	return 0;
+}
+
+static int test__checkevent_list(struct perf_evlist *evlist)
+{
+	struct perf_evsel *evsel;
+
+	TEST_ASSERT_VAL("wrong number of entries", 3 == evlist->nr_entries);
+
+	/* r1 */
+	evsel = list_entry(evlist->entries.next, struct perf_evsel, node);
+	TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);
+	TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config);
+	TEST_ASSERT_VAL("wrong config1", 0 == evsel->attr.config1);
+	TEST_ASSERT_VAL("wrong config2", 0 == evsel->attr.config2);
+	TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
+	TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
+	TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
+	TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+
+	/* syscalls:sys_enter_open:k */
+	evsel = list_entry(evsel->node.next, struct perf_evsel, node);
+	TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->attr.type);
+	TEST_ASSERT_VAL("wrong sample_type",
+		(PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | PERF_SAMPLE_CPU) ==
+		evsel->attr.sample_type);
+	TEST_ASSERT_VAL("wrong sample_period", 1 == evsel->attr.sample_period);
+	TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
+	TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
+	TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
+	TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+
+	/* 1:1:hp */
+	evsel = list_entry(evsel->node.next, struct perf_evsel, node);
+	TEST_ASSERT_VAL("wrong type", 1 == evsel->attr.type);
+	TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config);
+	TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
+	TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
+	TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
+	TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip);
+
+	return 0;
+}
+
+static int test__checkevent_pmu_name(struct perf_evlist *evlist)
+{
+	struct perf_evsel *evsel;
+
+	/* cpu/config=1,name=krava1/u */
+	evsel = list_entry(evlist->entries.next, struct perf_evsel, node);
+	TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
+	TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);
+	TEST_ASSERT_VAL("wrong config",  1 == evsel->attr.config);
+	TEST_ASSERT_VAL("wrong name", !strcmp(evsel->name, "krava"));
+
+	/* cpu/config=2/" */
+	evsel = list_entry(evsel->node.next, struct perf_evsel, node);
+	TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
+	TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);
+	TEST_ASSERT_VAL("wrong config",  2 == evsel->attr.config);
+	TEST_ASSERT_VAL("wrong name", !strcmp(evsel->name, "raw 0x2"));
+
+	return 0;
+}
+
+struct test__event_st {
+	const char *name;
+	__u32 type;
+	int (*check)(struct perf_evlist *evlist);
+};
+
+static struct test__event_st test__events[] = {
+	[0] = {
+		.name  = "syscalls:sys_enter_open",
+		.check = test__checkevent_tracepoint,
+	},
+	[1] = {
+		.name  = "syscalls:*",
+		.check = test__checkevent_tracepoint_multi,
+	},
+	[2] = {
+		.name  = "r1a",
+		.check = test__checkevent_raw,
+	},
+	[3] = {
+		.name  = "1:1",
+		.check = test__checkevent_numeric,
+	},
+	[4] = {
+		.name  = "instructions",
+		.check = test__checkevent_symbolic_name,
+	},
+	[5] = {
+		.name  = "cycles/period=100000,config2/",
+		.check = test__checkevent_symbolic_name_config,
+	},
+	[6] = {
+		.name  = "faults",
+		.check = test__checkevent_symbolic_alias,
+	},
+	[7] = {
+		.name  = "L1-dcache-load-miss",
+		.check = test__checkevent_genhw,
+	},
+	[8] = {
+		.name  = "mem:0",
+		.check = test__checkevent_breakpoint,
+	},
+	[9] = {
+		.name  = "mem:0:x",
+		.check = test__checkevent_breakpoint_x,
+	},
+	[10] = {
+		.name  = "mem:0:r",
+		.check = test__checkevent_breakpoint_r,
+	},
+	[11] = {
+		.name  = "mem:0:w",
+		.check = test__checkevent_breakpoint_w,
+	},
+	[12] = {
+		.name  = "syscalls:sys_enter_open:k",
+		.check = test__checkevent_tracepoint_modifier,
+	},
+	[13] = {
+		.name  = "syscalls:*:u",
+		.check = test__checkevent_tracepoint_multi_modifier,
+	},
+	[14] = {
+		.name  = "r1a:kp",
+		.check = test__checkevent_raw_modifier,
+	},
+	[15] = {
+		.name  = "1:1:hp",
+		.check = test__checkevent_numeric_modifier,
+	},
+	[16] = {
+		.name  = "instructions:h",
+		.check = test__checkevent_symbolic_name_modifier,
+	},
+	[17] = {
+		.name  = "faults:u",
+		.check = test__checkevent_symbolic_alias_modifier,
+	},
+	[18] = {
+		.name  = "L1-dcache-load-miss:kp",
+		.check = test__checkevent_genhw_modifier,
+	},
+	[19] = {
+		.name  = "mem:0:u",
+		.check = test__checkevent_breakpoint_modifier,
+	},
+	[20] = {
+		.name  = "mem:0:x:k",
+		.check = test__checkevent_breakpoint_x_modifier,
+	},
+	[21] = {
+		.name  = "mem:0:r:hp",
+		.check = test__checkevent_breakpoint_r_modifier,
+	},
+	[22] = {
+		.name  = "mem:0:w:up",
+		.check = test__checkevent_breakpoint_w_modifier,
+	},
+	[23] = {
+		.name  = "r1,syscalls:sys_enter_open:k,1:1:hp",
+		.check = test__checkevent_list,
+	},
+	[24] = {
+		.name  = "instructions:G",
+		.check = test__checkevent_exclude_host_modifier,
+	},
+	[25] = {
+		.name  = "instructions:H",
+		.check = test__checkevent_exclude_guest_modifier,
+	},
+};
+
+#define TEST__EVENTS_CNT (sizeof(test__events) / sizeof(struct test__event_st))
+
+static struct test__event_st test__events_pmu[] = {
+	[0] = {
+		.name  = "cpu/config=10,config1,config2=3,period=1000/u",
+		.check = test__checkevent_pmu,
+	},
+	[1] = {
+		.name  = "cpu/config=1,name=krava/u,cpu/config=2/u",
+		.check = test__checkevent_pmu_name,
+	},
+};
+
+#define TEST__EVENTS_PMU_CNT (sizeof(test__events_pmu) / \
+			      sizeof(struct test__event_st))
+
+static int test(struct test__event_st *e)
+{
+	struct perf_evlist *evlist;
+	int ret;
+
+	evlist = perf_evlist__new(NULL, NULL);
+	if (evlist == NULL)
+		return -ENOMEM;
+
+	ret = parse_events(evlist, e->name, 0);
+	if (ret) {
+		pr_debug("failed to parse event '%s', err %d\n",
+			 e->name, ret);
+		return ret;
+	}
+
+	ret = e->check(evlist);
+	perf_evlist__delete(evlist);
+
+	return ret;
+}
+
+static int test_events(struct test__event_st *events, unsigned cnt)
+{
+	int ret = 0;
+	unsigned i;
+
+	for (i = 0; i < cnt; i++) {
+		struct test__event_st *e = &events[i];
+
+		pr_debug("running test %d '%s'\n", i, e->name);
+		ret = test(e);
+		if (ret)
+			break;
+	}
+
+	return ret;
+}
+
+static int test_pmu(void)
+{
+	struct stat st;
+	char path[PATH_MAX];
+	int ret;
+
+	snprintf(path, PATH_MAX, "%s/bus/event_source/devices/cpu/format/",
+		 sysfs_find_mountpoint());
+
+	ret = stat(path, &st);
+	if (ret)
+		pr_debug("ommiting PMU cpu tests\n");
+	return !ret;
+}
+
+int parse_events__test(void)
+{
+	int ret;
+
+	ret = test_events(test__events, TEST__EVENTS_CNT);
+	if (!ret && test_pmu())
+		ret = test_events(test__events_pmu, TEST__EVENTS_PMU_CNT);
+
+	return ret;
+}
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index c7fc18a..fac7d59 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -23,8 +23,10 @@
 	const char	*alias;
 };
 
-int parse_events_parse(struct list_head *list, struct list_head *list_tmp,
-		       int *idx);
+#ifdef PARSER_DEBUG
+extern int parse_events_debug;
+#endif
+int parse_events_parse(struct list_head *list, int *idx);
 
 #define CHW(x) .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_##x
 #define CSW(x) .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_##x
@@ -355,20 +357,30 @@
 	return "unknown";
 }
 
-static int add_event(struct list_head *list, int *idx,
+static int add_event(struct list_head **_list, int *idx,
 		     struct perf_event_attr *attr, char *name)
 {
 	struct perf_evsel *evsel;
+	struct list_head *list = *_list;
+
+	if (!list) {
+		list = malloc(sizeof(*list));
+		if (!list)
+			return -ENOMEM;
+		INIT_LIST_HEAD(list);
+	}
 
 	event_attr_init(attr);
 
 	evsel = perf_evsel__new(attr, (*idx)++);
-	if (!evsel)
+	if (!evsel) {
+		free(list);
 		return -ENOMEM;
-
-	list_add_tail(&evsel->node, list);
+	}
 
 	evsel->name = strdup(name);
+	list_add_tail(&evsel->node, list);
+	*_list = list;
 	return 0;
 }
 
@@ -390,7 +402,7 @@
 	return -1;
 }
 
-int parse_events_add_cache(struct list_head *list, int *idx,
+int parse_events_add_cache(struct list_head **list, int *idx,
 			   char *type, char *op_result1, char *op_result2)
 {
 	struct perf_event_attr attr;
@@ -451,7 +463,7 @@
 	return add_event(list, idx, &attr, name);
 }
 
-static int add_tracepoint(struct list_head *list, int *idx,
+static int add_tracepoint(struct list_head **list, int *idx,
 			  char *sys_name, char *evt_name)
 {
 	struct perf_event_attr attr;
@@ -488,7 +500,7 @@
 	return add_event(list, idx, &attr, name);
 }
 
-static int add_tracepoint_multi(struct list_head *list, int *idx,
+static int add_tracepoint_multi(struct list_head **list, int *idx,
 				char *sys_name, char *evt_name)
 {
 	char evt_path[MAXPATHLEN];
@@ -519,7 +531,7 @@
 	return ret;
 }
 
-int parse_events_add_tracepoint(struct list_head *list, int *idx,
+int parse_events_add_tracepoint(struct list_head **list, int *idx,
 				char *sys, char *event)
 {
 	int ret;
@@ -563,7 +575,7 @@
 	return 0;
 }
 
-int parse_events_add_breakpoint(struct list_head *list, int *idx,
+int parse_events_add_breakpoint(struct list_head **list, int *idx,
 				void *ptr, char *type)
 {
 	struct perf_event_attr attr;
@@ -622,6 +634,9 @@
 		 * attr->branch_sample_type = term->val.num;
 		 */
 		break;
+	case PARSE_EVENTS__TERM_TYPE_NAME:
+		CHECK_TYPE_VAL(STR);
+		break;
 	default:
 		return -EINVAL;
 	}
@@ -642,7 +657,7 @@
 	return 0;
 }
 
-int parse_events_add_numeric(struct list_head *list, int *idx,
+int parse_events_add_numeric(struct list_head **list, int *idx,
 			     unsigned long type, unsigned long config,
 			     struct list_head *head_config)
 {
@@ -660,7 +675,24 @@
 			 (char *) __event_name(type, config));
 }
 
-int parse_events_add_pmu(struct list_head *list, int *idx,
+static int parse_events__is_name_term(struct parse_events__term *term)
+{
+	return term->type_term == PARSE_EVENTS__TERM_TYPE_NAME;
+}
+
+static char *pmu_event_name(struct perf_event_attr *attr,
+			    struct list_head *head_terms)
+{
+	struct parse_events__term *term;
+
+	list_for_each_entry(term, head_terms, list)
+		if (parse_events__is_name_term(term))
+			return term->val.str;
+
+	return (char *) __event_name(PERF_TYPE_RAW, attr->config);
+}
+
+int parse_events_add_pmu(struct list_head **list, int *idx,
 			 char *name, struct list_head *head_config)
 {
 	struct perf_event_attr attr;
@@ -681,7 +713,8 @@
 	if (perf_pmu__config(pmu, &attr, head_config))
 		return -EINVAL;
 
-	return add_event(list, idx, &attr, (char *) "pmu");
+	return add_event(list, idx, &attr,
+			 pmu_event_name(&attr, head_config));
 }
 
 void parse_events_update_lists(struct list_head *list_event,
@@ -693,7 +726,7 @@
 	 * list, for next event definition.
 	 */
 	list_splice_tail(list_event, list_all);
-	INIT_LIST_HEAD(list_event);
+	free(list_event);
 }
 
 int parse_events_modifier(struct list_head *list, char *str)
@@ -768,10 +801,14 @@
 
 	buffer = parse_events__scan_string(str);
 
-	ret = parse_events_parse(&list, &list_tmp, &idx);
+#ifdef PARSER_DEBUG
+	parse_events_debug = 1;
+#endif
+	ret = parse_events_parse(&list, &idx);
 
 	parse_events__flush_buffer(buffer);
 	parse_events__delete_buffer(buffer);
+	parse_events_lex_destroy();
 
 	if (!ret) {
 		int entries = idx - evlist->nr_entries;
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h
index 3fddd61..8cac57a 100644
--- a/tools/perf/util/parse-events.h
+++ b/tools/perf/util/parse-events.h
@@ -4,7 +4,9 @@
  * Parse symbolic events/counts passed in as options:
  */
 
+#include <linux/list.h>
 #include <stdbool.h>
+#include "types.h"
 #include "../../../include/linux/perf_event.h"
 #include "types.h"
 
@@ -45,6 +47,7 @@
 	PARSE_EVENTS__TERM_TYPE_CONFIG,
 	PARSE_EVENTS__TERM_TYPE_CONFIG1,
 	PARSE_EVENTS__TERM_TYPE_CONFIG2,
+	PARSE_EVENTS__TERM_TYPE_NAME,
 	PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD,
 	PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE,
 };
@@ -66,26 +69,23 @@
 int parse_events__term_str(struct parse_events__term **_term,
 			   int type_term, char *config, char *str);
 void parse_events__free_terms(struct list_head *terms);
-int parse_events_modifier(struct list_head *list __used, char *str __used);
-int parse_events_add_tracepoint(struct list_head *list, int *idx,
+int parse_events_modifier(struct list_head *list, char *str);
+int parse_events_add_tracepoint(struct list_head **list, int *idx,
 				char *sys, char *event);
-int parse_events_add_raw(struct perf_evlist *evlist, unsigned long config,
-			 unsigned long config1, unsigned long config2,
-			 char *mod);
-int parse_events_add_numeric(struct list_head *list, int *idx,
+int parse_events_add_numeric(struct list_head **list, int *idx,
 			     unsigned long type, unsigned long config,
 			     struct list_head *head_config);
-int parse_events_add_cache(struct list_head *list, int *idx,
+int parse_events_add_cache(struct list_head **list, int *idx,
 			   char *type, char *op_result1, char *op_result2);
-int parse_events_add_breakpoint(struct list_head *list, int *idx,
+int parse_events_add_breakpoint(struct list_head **list, int *idx,
 				void *ptr, char *type);
-int parse_events_add_pmu(struct list_head *list, int *idx,
+int parse_events_add_pmu(struct list_head **list, int *idx,
 			 char *pmu , struct list_head *head_config);
 void parse_events_update_lists(struct list_head *list_event,
 			       struct list_head *list_all);
 void parse_events_error(struct list_head *list_all,
-			struct list_head *list_event,
 			int *idx, char const *msg);
+int parse_events__test(void);
 
 void print_events(const char *event_glob);
 void print_events_type(u8 type);
diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l
index 1fcf1bb..618a8e7 100644
--- a/tools/perf/util/parse-events.l
+++ b/tools/perf/util/parse-events.l
@@ -1,5 +1,6 @@
 
 %option prefix="parse_events_"
+%option stack
 
 %{
 #include <errno.h>
@@ -50,6 +51,8 @@
 
 %}
 
+%x mem
+
 num_dec		[0-9]+
 num_hex		0x[a-fA-F0-9]+
 num_raw_hex	[a-fA-F0-9]+
@@ -102,16 +105,16 @@
 config			{ return term(PARSE_EVENTS__TERM_TYPE_CONFIG); }
 config1			{ return term(PARSE_EVENTS__TERM_TYPE_CONFIG1); }
 config2			{ return term(PARSE_EVENTS__TERM_TYPE_CONFIG2); }
+name			{ return term(PARSE_EVENTS__TERM_TYPE_NAME); }
 period			{ return term(PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD); }
 branch_type		{ return term(PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE); }
 
-mem:			{ return PE_PREFIX_MEM; }
+mem:			{ BEGIN(mem); return PE_PREFIX_MEM; }
 r{num_raw_hex}		{ return raw(); }
 {num_dec}		{ return value(10); }
 {num_hex}		{ return value(16); }
 
 {modifier_event}	{ return str(PE_MODIFIER_EVENT); }
-{modifier_bp}		{ return str(PE_MODIFIER_BP); }
 {name}			{ return str(PE_NAME); }
 "/"			{ return '/'; }
 -			{ return '-'; }
@@ -119,6 +122,25 @@
 :			{ return ':'; }
 =			{ return '='; }
 
+<mem>{
+{modifier_bp}		{ return str(PE_MODIFIER_BP); }
+:			{ return ':'; }
+{num_dec}		{ return value(10); }
+{num_hex}		{ return value(16); }
+	/*
+	 * We need to separate 'mem:' scanner part, in order to get specific
+	 * modifier bits parsed out. Otherwise we would need to handle PE_NAME
+	 * and we'd need to parse it manually. During the escape from <mem>
+	 * state we need to put the escaping char back, so we dont miss it.
+	 */
+.			{ unput(*parse_events_text); BEGIN(INITIAL); }
+	/*
+	 * We destroy the scanner after reaching EOF,
+	 * but anyway just to be sure get back to INIT state.
+	 */
+<<EOF>>			{ BEGIN(INITIAL); }
+}
+
 %%
 
 int parse_events_wrap(void)
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y
index 936913e..362cc59 100644
--- a/tools/perf/util/parse-events.y
+++ b/tools/perf/util/parse-events.y
@@ -1,7 +1,6 @@
 
 %name-prefix "parse_events_"
 %parse-param {struct list_head *list_all}
-%parse-param {struct list_head *list_event}
 %parse-param {int *idx}
 
 %{
@@ -41,6 +40,14 @@
 %type <str> PE_MODIFIER_BP
 %type <head> event_config
 %type <term> event_term
+%type <head> event_pmu
+%type <head> event_legacy_symbol
+%type <head> event_legacy_cache
+%type <head> event_legacy_mem
+%type <head> event_legacy_tracepoint
+%type <head> event_legacy_numeric
+%type <head> event_legacy_raw
+%type <head> event_def
 
 %union
 {
@@ -62,13 +69,13 @@
 	 * (there could be more events added for multiple tracepoint
 	 * definitions via '*?'.
 	 */
-	ABORT_ON(parse_events_modifier(list_event, $2));
-	parse_events_update_lists(list_event, list_all);
+	ABORT_ON(parse_events_modifier($1, $2));
+	parse_events_update_lists($1, list_all);
 }
 |
 event_def
 {
-	parse_events_update_lists(list_event, list_all);
+	parse_events_update_lists($1, list_all);
 }
 
 event_def: event_pmu |
@@ -82,71 +89,102 @@
 event_pmu:
 PE_NAME '/' event_config '/'
 {
-	ABORT_ON(parse_events_add_pmu(list_event, idx, $1, $3));
+	struct list_head *list = NULL;
+
+	ABORT_ON(parse_events_add_pmu(&list, idx, $1, $3));
 	parse_events__free_terms($3);
+	$$ = list;
 }
 
 event_legacy_symbol:
 PE_VALUE_SYM '/' event_config '/'
 {
+	struct list_head *list = NULL;
 	int type = $1 >> 16;
 	int config = $1 & 255;
 
-	ABORT_ON(parse_events_add_numeric(list_event, idx, type, config, $3));
+	ABORT_ON(parse_events_add_numeric(&list, idx, type, config, $3));
 	parse_events__free_terms($3);
+	$$ = list;
 }
 |
 PE_VALUE_SYM sep_slash_dc
 {
+	struct list_head *list = NULL;
 	int type = $1 >> 16;
 	int config = $1 & 255;
 
-	ABORT_ON(parse_events_add_numeric(list_event, idx, type, config, NULL));
+	ABORT_ON(parse_events_add_numeric(&list, idx, type, config, NULL));
+	$$ = list;
 }
 
 event_legacy_cache:
 PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT '-' PE_NAME_CACHE_OP_RESULT
 {
-	ABORT_ON(parse_events_add_cache(list_event, idx, $1, $3, $5));
+	struct list_head *list = NULL;
+
+	ABORT_ON(parse_events_add_cache(&list, idx, $1, $3, $5));
+	$$ = list;
 }
 |
 PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT
 {
-	ABORT_ON(parse_events_add_cache(list_event, idx, $1, $3, NULL));
+	struct list_head *list = NULL;
+
+	ABORT_ON(parse_events_add_cache(&list, idx, $1, $3, NULL));
+	$$ = list;
 }
 |
 PE_NAME_CACHE_TYPE
 {
-	ABORT_ON(parse_events_add_cache(list_event, idx, $1, NULL, NULL));
+	struct list_head *list = NULL;
+
+	ABORT_ON(parse_events_add_cache(&list, idx, $1, NULL, NULL));
+	$$ = list;
 }
 
 event_legacy_mem:
 PE_PREFIX_MEM PE_VALUE ':' PE_MODIFIER_BP sep_dc
 {
-	ABORT_ON(parse_events_add_breakpoint(list_event, idx, (void *) $2, $4));
+	struct list_head *list = NULL;
+
+	ABORT_ON(parse_events_add_breakpoint(&list, idx, (void *) $2, $4));
+	$$ = list;
 }
 |
 PE_PREFIX_MEM PE_VALUE sep_dc
 {
-	ABORT_ON(parse_events_add_breakpoint(list_event, idx, (void *) $2, NULL));
+	struct list_head *list = NULL;
+
+	ABORT_ON(parse_events_add_breakpoint(&list, idx, (void *) $2, NULL));
+	$$ = list;
 }
 
 event_legacy_tracepoint:
 PE_NAME ':' PE_NAME
 {
-	ABORT_ON(parse_events_add_tracepoint(list_event, idx, $1, $3));
+	struct list_head *list = NULL;
+
+	ABORT_ON(parse_events_add_tracepoint(&list, idx, $1, $3));
+	$$ = list;
 }
 
 event_legacy_numeric:
 PE_VALUE ':' PE_VALUE
 {
-	ABORT_ON(parse_events_add_numeric(list_event, idx, $1, $3, NULL));
+	struct list_head *list = NULL;
+
+	ABORT_ON(parse_events_add_numeric(&list, idx, $1, $3, NULL));
+	$$ = list;
 }
 
 event_legacy_raw:
 PE_RAW
 {
-	ABORT_ON(parse_events_add_numeric(list_event, idx, PERF_TYPE_RAW, $1, NULL));
+	struct list_head *list = NULL;
+
+	ABORT_ON(parse_events_add_numeric(&list, idx, PERF_TYPE_RAW, $1, NULL));
+	$$ = list;
 }
 
 event_config:
@@ -199,6 +237,14 @@
 	$$ = term;
 }
 |
+PE_TERM '=' PE_NAME
+{
+	struct parse_events__term *term;
+
+	ABORT_ON(parse_events__term_str(&term, $1, NULL, $3));
+	$$ = term;
+}
+|
 PE_TERM '=' PE_VALUE
 {
 	struct parse_events__term *term;
@@ -222,7 +268,6 @@
 %%
 
 void parse_events_error(struct list_head *list_all __used,
-			struct list_head *list_event __used,
 			int *idx __used,
 			char const *msg __used)
 {
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 8ee219b..a119a53 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -258,9 +258,9 @@
 static int pmu_config(struct list_head *formats, struct perf_event_attr *attr,
 		      struct list_head *head_terms)
 {
-	struct parse_events__term *term, *h;
+	struct parse_events__term *term;
 
-	list_for_each_entry_safe(term, h, head_terms, list)
+	list_for_each_entry(term, head_terms, list)
 		if (pmu_config_term(formats, attr, term))
 			return -EINVAL;
 
diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c
index e30749e..4c1b3d7 100644
--- a/tools/perf/util/scripting-engines/trace-event-perl.c
+++ b/tools/perf/util/scripting-engines/trace-event-perl.c
@@ -56,7 +56,7 @@
 #define FTRACE_MAX_EVENT				\
 	((1 << (sizeof(unsigned short) * 8)) - 1)
 
-struct event *events[FTRACE_MAX_EVENT];
+struct event_format *events[FTRACE_MAX_EVENT];
 
 extern struct scripting_context *scripting_context;
 
@@ -181,7 +181,7 @@
 	LEAVE;
 }
 
-static void define_event_symbols(struct event *event,
+static void define_event_symbols(struct event_format *event,
 				 const char *ev_name,
 				 struct print_arg *args)
 {
@@ -209,6 +209,8 @@
 		define_symbolic_values(args->symbol.symbols, ev_name,
 				       cur_field_name);
 		break;
+	case PRINT_BSTRING:
+	case PRINT_DYNAMIC_ARRAY:
 	case PRINT_STRING:
 		break;
 	case PRINT_TYPE:
@@ -220,7 +222,9 @@
 		define_event_symbols(event, ev_name, args->op.left);
 		define_event_symbols(event, ev_name, args->op.right);
 		break;
+	case PRINT_FUNC:
 	default:
+		pr_err("Unsupported print arg type\n");
 		/* we should warn... */
 		return;
 	}
@@ -229,10 +233,10 @@
 		define_event_symbols(event, ev_name, args->next);
 }
 
-static inline struct event *find_cache_event(int type)
+static inline struct event_format *find_cache_event(int type)
 {
 	static char ev_name[256];
-	struct event *event;
+	struct event_format *event;
 
 	if (events[type])
 		return events[type];
@@ -258,7 +262,7 @@
 	static char handler[256];
 	unsigned long long val;
 	unsigned long s, ns;
-	struct event *event;
+	struct event_format *event;
 	int type;
 	int pid;
 	int cpu = sample->cpu;
@@ -446,7 +450,7 @@
 
 static int perl_generate_script(const char *outfile)
 {
-	struct event *event = NULL;
+	struct event_format *event = NULL;
 	struct format_field *f;
 	char fname[PATH_MAX];
 	int not_first, count;
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 4dcc8f3..93d355d 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -481,6 +481,38 @@
 	event->read.id		 = bswap_64(event->read.id);
 }
 
+static u8 revbyte(u8 b)
+{
+	int rev = (b >> 4) | ((b & 0xf) << 4);
+	rev = ((rev & 0xcc) >> 2) | ((rev & 0x33) << 2);
+	rev = ((rev & 0xaa) >> 1) | ((rev & 0x55) << 1);
+	return (u8) rev;
+}
+
+/*
+ * XXX this is hack in attempt to carry flags bitfield
+ * throught endian village. ABI says:
+ *
+ * Bit-fields are allocated from right to left (least to most significant)
+ * on little-endian implementations and from left to right (most to least
+ * significant) on big-endian implementations.
+ *
+ * The above seems to be byte specific, so we need to reverse each
+ * byte of the bitfield. 'Internet' also says this might be implementation
+ * specific and we probably need proper fix and carry perf_event_attr
+ * bitfield flags in separate data file FEAT_ section. Thought this seems
+ * to work for now.
+ */
+static void swap_bitfield(u8 *p, unsigned len)
+{
+	unsigned i;
+
+	for (i = 0; i < len; i++) {
+		*p = revbyte(*p);
+		p++;
+	}
+}
+
 /* exported for swapping attributes in file header */
 void perf_event__attr_swap(struct perf_event_attr *attr)
 {
@@ -494,6 +526,8 @@
 	attr->bp_type		= bswap_32(attr->bp_type);
 	attr->bp_addr		= bswap_64(attr->bp_addr);
 	attr->bp_len		= bswap_64(attr->bp_len);
+
+	swap_bitfield((u8 *) (&attr->read_format + 1), sizeof(u64));
 }
 
 static void perf_event__hdr_attr_swap(union perf_event *event)
@@ -1064,8 +1098,9 @@
 static int __perf_session__process_pipe_events(struct perf_session *self,
 					       struct perf_tool *tool)
 {
-	union perf_event event;
-	uint32_t size;
+	union perf_event *event;
+	uint32_t size, cur_size = 0;
+	void *buf = NULL;
 	int skip = 0;
 	u64 head;
 	int err;
@@ -1074,8 +1109,14 @@
 	perf_tool__fill_defaults(tool);
 
 	head = 0;
+	cur_size = sizeof(union perf_event);
+
+	buf = malloc(cur_size);
+	if (!buf)
+		return -errno;
 more:
-	err = readn(self->fd, &event, sizeof(struct perf_event_header));
+	event = buf;
+	err = readn(self->fd, event, sizeof(struct perf_event_header));
 	if (err <= 0) {
 		if (err == 0)
 			goto done;
@@ -1085,13 +1126,23 @@
 	}
 
 	if (self->header.needs_swap)
-		perf_event_header__bswap(&event.header);
+		perf_event_header__bswap(&event->header);
 
-	size = event.header.size;
+	size = event->header.size;
 	if (size == 0)
 		size = 8;
 
-	p = &event;
+	if (size > cur_size) {
+		void *new = realloc(buf, size);
+		if (!new) {
+			pr_err("failed to allocate memory to read event\n");
+			goto out_err;
+		}
+		buf = new;
+		cur_size = size;
+		event = buf;
+	}
+	p = event;
 	p += sizeof(struct perf_event_header);
 
 	if (size - sizeof(struct perf_event_header)) {
@@ -1107,9 +1158,9 @@
 		}
 	}
 
-	if ((skip = perf_session__process_event(self, &event, tool, head)) < 0) {
+	if ((skip = perf_session__process_event(self, event, tool, head)) < 0) {
 		pr_err("%#" PRIx64 " [%#x]: failed to process type: %d\n",
-		       head, event.header.size, event.header.type);
+		       head, event->header.size, event->header.type);
 		err = -EINVAL;
 		goto out_err;
 	}
@@ -1124,6 +1175,7 @@
 done:
 	err = 0;
 out_err:
+	free(buf);
 	perf_session__warn_about_errors(self, tool);
 	perf_session_free_sample_buffers(self);
 	return err;
diff --git a/tools/perf/util/types.h b/tools/perf/util/types.h
index 5f3689a..c51fa6b 100644
--- a/tools/perf/util/types.h
+++ b/tools/perf/util/types.h
@@ -16,4 +16,9 @@
 typedef unsigned char	   u8;
 typedef signed char	   s8;
 
+union u64_swap {
+	u64 val64;
+	u32 val32[2];
+};
+
 #endif /* __PERF_TYPES_H */
diff --git a/tools/testing/ktest/sample.conf b/tools/testing/ktest/sample.conf
index 0e8191b..cf362b3 100644
--- a/tools/testing/ktest/sample.conf
+++ b/tools/testing/ktest/sample.conf
@@ -158,7 +158,7 @@
 #
 # TEST_START IF (DEFINED ALL_TESTS || ${MYTEST} == boottest) && ${MACHINE} == gandalf
 #
-# Notice the use of paranthesis. Without any paranthesis the above would be
+# Notice the use of parentheses. Without any parentheses the above would be
 # processed the same as:
 #
 # TEST_START IF DEFINED ALL_TESTS || (${MYTEST} == boottest && ${MACHINE} == gandalf)