Merge branch 'i2c-for-linus' of git://jdelvare.pck.nerim.net/jdelvare-2.6

* 'i2c-for-linus' of git://jdelvare.pck.nerim.net/jdelvare-2.6:
  i2c-viapro: Add support for SMBus Process Call transactions
  i2c: Restore i2c_smbus_process_call function
  i2c: Do earlier driver model init
  i2c: Only build Tyan SMBus mux drivers on x86
  i2c: Guard against oopses from bad init sequences
  i2c: Document the implementation details of the /dev interface
  i2c: Improve dev-interface documentation
  i2c-parport-light: Don't register a platform device resource
  hwmon: (dme1737) Convert to a new-style i2c driver
  hwmon: (dme1737) Be less i2c-centric
  i2c/tps65010: Vibrator hookup to gpiolib
  i2c-viapro: Add VX800/VX820 support
  i2c: Renesas Highlander FPGA SMBus support
  i2c-pca-isa: Don't grab arbitrary resources
  i2c/isp1301_omap: Convert to a new-style i2c driver, part 2
  i2c/isp1301_omap: Convert to a new-style i2c driver, part 1
diff --git a/Documentation/ABI/testing/sysfs-class-regulator b/Documentation/ABI/testing/sysfs-class-regulator
index 79a4a75..3731f6f 100644
--- a/Documentation/ABI/testing/sysfs-class-regulator
+++ b/Documentation/ABI/testing/sysfs-class-regulator
@@ -1,7 +1,7 @@
 What:		/sys/class/regulator/.../state
 Date:		April 2008
 KernelVersion:	2.6.26
-Contact:	Liam Girdwood <lg@opensource.wolfsonmicro.com>
+Contact:	Liam Girdwood <lrg@slimlogic.co.uk>
 Description:
 		Each regulator directory will contain a field called
 		state. This holds the regulator output state.
@@ -27,7 +27,7 @@
 What:		/sys/class/regulator/.../type
 Date:		April 2008
 KernelVersion:	2.6.26
-Contact:	Liam Girdwood <lg@opensource.wolfsonmicro.com>
+Contact:	Liam Girdwood <lrg@slimlogic.co.uk>
 Description:
 		Each regulator directory will contain a field called
 		type. This holds the regulator type.
@@ -51,7 +51,7 @@
 What:		/sys/class/regulator/.../microvolts
 Date:		April 2008
 KernelVersion:	2.6.26
-Contact:	Liam Girdwood <lg@opensource.wolfsonmicro.com>
+Contact:	Liam Girdwood <lrg@slimlogic.co.uk>
 Description:
 		Each regulator directory will contain a field called
 		microvolts. This holds the regulator output voltage setting
@@ -65,7 +65,7 @@
 What:		/sys/class/regulator/.../microamps
 Date:		April 2008
 KernelVersion:	2.6.26
-Contact:	Liam Girdwood <lg@opensource.wolfsonmicro.com>
+Contact:	Liam Girdwood <lrg@slimlogic.co.uk>
 Description:
 		Each regulator directory will contain a field called
 		microamps. This holds the regulator output current limit
@@ -79,7 +79,7 @@
 What:		/sys/class/regulator/.../opmode
 Date:		April 2008
 KernelVersion:	2.6.26
-Contact:	Liam Girdwood <lg@opensource.wolfsonmicro.com>
+Contact:	Liam Girdwood <lrg@slimlogic.co.uk>
 Description:
 		Each regulator directory will contain a field called
 		opmode. This holds the regulator operating mode setting.
@@ -102,7 +102,7 @@
 What:		/sys/class/regulator/.../min_microvolts
 Date:		April 2008
 KernelVersion:	2.6.26
-Contact:	Liam Girdwood <lg@opensource.wolfsonmicro.com>
+Contact:	Liam Girdwood <lrg@slimlogic.co.uk>
 Description:
 		Each regulator directory will contain a field called
 		min_microvolts. This holds the minimum safe working regulator
@@ -116,7 +116,7 @@
 What:		/sys/class/regulator/.../max_microvolts
 Date:		April 2008
 KernelVersion:	2.6.26
-Contact:	Liam Girdwood <lg@opensource.wolfsonmicro.com>
+Contact:	Liam Girdwood <lrg@slimlogic.co.uk>
 Description:
 		Each regulator directory will contain a field called
 		max_microvolts. This holds the maximum safe working regulator
@@ -130,7 +130,7 @@
 What:		/sys/class/regulator/.../min_microamps
 Date:		April 2008
 KernelVersion:	2.6.26
-Contact:	Liam Girdwood <lg@opensource.wolfsonmicro.com>
+Contact:	Liam Girdwood <lrg@slimlogic.co.uk>
 Description:
 		Each regulator directory will contain a field called
 		min_microamps. This holds the minimum safe working regulator
@@ -145,7 +145,7 @@
 What:		/sys/class/regulator/.../max_microamps
 Date:		April 2008
 KernelVersion:	2.6.26
-Contact:	Liam Girdwood <lg@opensource.wolfsonmicro.com>
+Contact:	Liam Girdwood <lrg@slimlogic.co.uk>
 Description:
 		Each regulator directory will contain a field called
 		max_microamps. This holds the maximum safe working regulator
@@ -157,10 +157,23 @@
 		platform code.
 
 
+What:		/sys/class/regulator/.../name
+Date:		October 2008
+KernelVersion:	2.6.28
+Contact:	Liam Girdwood <lrg@slimlogic.co.uk>
+Description:
+		Each regulator directory will contain a field called
+		name. This holds a string identifying the regulator for
+		display purposes.
+
+		NOTE: this will be empty if no suitable name is provided
+		by platform or regulator drivers.
+
+
 What:		/sys/class/regulator/.../num_users
 Date:		April 2008
 KernelVersion:	2.6.26
-Contact:	Liam Girdwood <lg@opensource.wolfsonmicro.com>
+Contact:	Liam Girdwood <lrg@slimlogic.co.uk>
 Description:
 		Each regulator directory will contain a field called
 		num_users. This holds the number of consumer devices that
@@ -170,7 +183,7 @@
 What:		/sys/class/regulator/.../requested_microamps
 Date:		April 2008
 KernelVersion:	2.6.26
-Contact:	Liam Girdwood <lg@opensource.wolfsonmicro.com>
+Contact:	Liam Girdwood <lrg@slimlogic.co.uk>
 Description:
 		Each regulator directory will contain a field called
 		requested_microamps. This holds the total requested load
@@ -181,7 +194,7 @@
 What:		/sys/class/regulator/.../parent
 Date:		April 2008
 KernelVersion:	2.6.26
-Contact:	Liam Girdwood <lg@opensource.wolfsonmicro.com>
+Contact:	Liam Girdwood <lrg@slimlogic.co.uk>
 Description:
 		Some regulator directories will contain a link called parent.
 		This points to the parent or supply regulator if one exists.
@@ -189,7 +202,7 @@
 What:		/sys/class/regulator/.../suspend_mem_microvolts
 Date:		May 2008
 KernelVersion:	2.6.26
-Contact:	Liam Girdwood <lg@opensource.wolfsonmicro.com>
+Contact:	Liam Girdwood <lrg@slimlogic.co.uk>
 Description:
 		Each regulator directory will contain a field called
 		suspend_mem_microvolts. This holds the regulator output
@@ -203,7 +216,7 @@
 What:		/sys/class/regulator/.../suspend_disk_microvolts
 Date:		May 2008
 KernelVersion:	2.6.26
-Contact:	Liam Girdwood <lg@opensource.wolfsonmicro.com>
+Contact:	Liam Girdwood <lrg@slimlogic.co.uk>
 Description:
 		Each regulator directory will contain a field called
 		suspend_disk_microvolts. This holds the regulator output
@@ -217,7 +230,7 @@
 What:		/sys/class/regulator/.../suspend_standby_microvolts
 Date:		May 2008
 KernelVersion:	2.6.26
-Contact:	Liam Girdwood <lg@opensource.wolfsonmicro.com>
+Contact:	Liam Girdwood <lrg@slimlogic.co.uk>
 Description:
 		Each regulator directory will contain a field called
 		suspend_standby_microvolts. This holds the regulator output
@@ -231,7 +244,7 @@
 What:		/sys/class/regulator/.../suspend_mem_mode
 Date:		May 2008
 KernelVersion:	2.6.26
-Contact:	Liam Girdwood <lg@opensource.wolfsonmicro.com>
+Contact:	Liam Girdwood <lrg@slimlogic.co.uk>
 Description:
 		Each regulator directory will contain a field called
 		suspend_mem_mode. This holds the regulator operating mode
@@ -245,7 +258,7 @@
 What:		/sys/class/regulator/.../suspend_disk_mode
 Date:		May 2008
 KernelVersion:	2.6.26
-Contact:	Liam Girdwood <lg@opensource.wolfsonmicro.com>
+Contact:	Liam Girdwood <lrg@slimlogic.co.uk>
 Description:
 		Each regulator directory will contain a field called
 		suspend_disk_mode. This holds the regulator operating mode
@@ -258,7 +271,7 @@
 What:		/sys/class/regulator/.../suspend_standby_mode
 Date:		May 2008
 KernelVersion:	2.6.26
-Contact:	Liam Girdwood <lg@opensource.wolfsonmicro.com>
+Contact:	Liam Girdwood <lrg@slimlogic.co.uk>
 Description:
 		Each regulator directory will contain a field called
 		suspend_standby_mode. This holds the regulator operating mode
@@ -272,7 +285,7 @@
 What:		/sys/class/regulator/.../suspend_mem_state
 Date:		May 2008
 KernelVersion:	2.6.26
-Contact:	Liam Girdwood <lg@opensource.wolfsonmicro.com>
+Contact:	Liam Girdwood <lrg@slimlogic.co.uk>
 Description:
 		Each regulator directory will contain a field called
 		suspend_mem_state. This holds the regulator operating state
@@ -287,7 +300,7 @@
 What:		/sys/class/regulator/.../suspend_disk_state
 Date:		May 2008
 KernelVersion:	2.6.26
-Contact:	Liam Girdwood <lg@opensource.wolfsonmicro.com>
+Contact:	Liam Girdwood <lrg@slimlogic.co.uk>
 Description:
 		Each regulator directory will contain a field called
 		suspend_disk_state. This holds the regulator operating state
@@ -302,7 +315,7 @@
 What:		/sys/class/regulator/.../suspend_standby_state
 Date:		May 2008
 KernelVersion:	2.6.26
-Contact:	Liam Girdwood <lg@opensource.wolfsonmicro.com>
+Contact:	Liam Girdwood <lrg@slimlogic.co.uk>
 Description:
 		Each regulator directory will contain a field called
 		suspend_standby_state. This holds the regulator operating
diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt
index cc8093c1..4d2566a 100644
--- a/Documentation/feature-removal-schedule.txt
+++ b/Documentation/feature-removal-schedule.txt
@@ -287,6 +287,13 @@
 
 ---------------------------
 
+What:	remove HID compat support
+When:	2.6.29
+Why:	needed only as a temporary solution until distros fix themselves up
+Who:	Jiri Slaby <jirislaby@gmail.com>
+
+---------------------------
+
 What:	/sys/o2cb symlink
 When:	January 2010
 Why:	/sys/fs/o2cb is the proper location for this information - /sys/o2cb
diff --git a/Documentation/filesystems/ocfs2.txt b/Documentation/filesystems/ocfs2.txt
index c318a8b..4340cc8 100644
--- a/Documentation/filesystems/ocfs2.txt
+++ b/Documentation/filesystems/ocfs2.txt
@@ -76,3 +76,9 @@
 			large, the fs will silently revert it to the default.
 			Localalloc is not enabled for local mounts.
 localflocks		This disables cluster aware flock.
+inode64			Indicates that Ocfs2 is allowed to create inodes at
+			any location in the filesystem, including those which
+			will result in inode numbers occupying more than 32
+			bits of significance.
+user_xattr	(*)	Enables Extended User Attributes.
+nouser_xattr		Disables Extended User Attributes.
diff --git a/Documentation/power/regulator/machine.txt b/Documentation/power/regulator/machine.txt
index c9a3566..ce3487d 100644
--- a/Documentation/power/regulator/machine.txt
+++ b/Documentation/power/regulator/machine.txt
@@ -2,17 +2,8 @@
 ===================================
 
 The regulator machine driver interface is intended for board/machine specific
-initialisation code to configure the regulator subsystem. Typical things that
-machine drivers would do are :-
+initialisation code to configure the regulator subsystem.
 
- 1. Regulator -> Device mapping.
- 2. Regulator supply configuration.
- 3. Power Domain constraint setting.
-
-
-
-1. Regulator -> device mapping
-==============================
 Consider the following machine :-
 
   Regulator-1 -+-> Regulator-2 --> [Consumer A @ 1.8 - 2.0V]
@@ -21,81 +12,82 @@
 
 The drivers for consumers A & B must be mapped to the correct regulator in
 order to control their power supply. This mapping can be achieved in machine
-initialisation code by calling :-
+initialisation code by creating a struct regulator_consumer_supply for
+each regulator.
 
-int regulator_set_device_supply(const char *regulator, struct device *dev,
-				const char *supply);
+struct regulator_consumer_supply {
+	struct device *dev;	/* consumer */
+	const char *supply;	/* consumer supply - e.g. "vcc" */
+};
 
-and is shown with the following code :-
+e.g. for the machine above
 
-regulator_set_device_supply("Regulator-1", devB, "Vcc");
-regulator_set_device_supply("Regulator-2", devA, "Vcc");
+static struct regulator_consumer_supply regulator1_consumers[] = {
+{
+	.dev	= &platform_consumerB_device.dev,
+	.supply	= "Vcc",
+},};
+
+static struct regulator_consumer_supply regulator2_consumers[] = {
+{
+	.dev	= &platform_consumerA_device.dev,
+	.supply	= "Vcc",
+},};
 
 This maps Regulator-1 to the 'Vcc' supply for Consumer B and maps Regulator-2
 to the 'Vcc' supply for Consumer A.
 
+Constraints can now be registered by defining a struct regulator_init_data
+for each regulator power domain. This structure also maps the consumers
+to their supply regulator :-
 
-2. Regulator supply configuration.
-==================================
-Consider the following machine (again) :-
-
-  Regulator-1 -+-> Regulator-2 --> [Consumer A @ 1.8 - 2.0V]
-               |
-               +-> [Consumer B @ 3.3V]
+static struct regulator_init_data regulator1_data = {
+	.constraints = {
+		.min_uV = 3300000,
+		.max_uV = 3300000,
+		.valid_modes_mask = REGULATOR_MODE_NORMAL,
+	},
+	.num_consumer_supplies = ARRAY_SIZE(regulator1_consumers),
+	.consumer_supplies = regulator1_consumers,
+};
 
 Regulator-1 supplies power to Regulator-2. This relationship must be registered
 with the core so that Regulator-1 is also enabled when Consumer A enables it's
-supply (Regulator-2).
+supply (Regulator-2). The supply regulator is set by the supply_regulator_dev
+field below:-
 
-This relationship can be register with the core via :-
-
-int regulator_set_supply(const char *regulator, const char *regulator_supply);
-
-In this example we would use the following code :-
-
-regulator_set_supply("Regulator-2", "Regulator-1");
-
-Relationships can be queried by calling :-
-
-const char *regulator_get_supply(const char *regulator);
-
-
-3. Power Domain constraint setting.
-===================================
-Each power domain within a system has physical constraints on voltage and
-current. This must be defined in software so that the power domain is always
-operated within specifications.
-
-Consider the following machine (again) :-
-
-  Regulator-1 -+-> Regulator-2 --> [Consumer A @ 1.8 - 2.0V]
-               |
-               +-> [Consumer B @ 3.3V]
-
-This gives us two regulators and two power domains:
-
-                   Domain 1: Regulator-2, Consumer B.
-                   Domain 2: Consumer A.
-
-Constraints can be registered by calling :-
-
-int regulator_set_platform_constraints(const char *regulator,
-	struct regulation_constraints *constraints);
-
-The example is defined as follows :-
-
-struct regulation_constraints domain_1 = {
-	.min_uV = 3300000,
-	.max_uV = 3300000,
-	.valid_modes_mask = REGULATOR_MODE_NORMAL,
+static struct regulator_init_data regulator2_data = {
+	.supply_regulator_dev = &platform_regulator1_device.dev,
+	.constraints = {
+		.min_uV = 1800000,
+		.max_uV = 2000000,
+		.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+		.valid_modes_mask = REGULATOR_MODE_NORMAL,
+	},
+	.num_consumer_supplies = ARRAY_SIZE(regulator2_consumers),
+	.consumer_supplies = regulator2_consumers,
 };
 
-struct regulation_constraints domain_2 = {
-	.min_uV = 1800000,
-	.max_uV = 2000000,
-	.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
-	.valid_modes_mask = REGULATOR_MODE_NORMAL,
-};
+Finally the regulator devices must be registered in the usual manner.
 
-regulator_set_platform_constraints("Regulator-1", &domain_1);
-regulator_set_platform_constraints("Regulator-2", &domain_2);
+static struct platform_device regulator_devices[] = {
+{
+	.name = "regulator",
+	.id = DCDC_1,
+	.dev = {
+		.platform_data = &regulator1_data,
+	},
+},
+{
+	.name = "regulator",
+	.id = DCDC_2,
+	.dev = {
+		.platform_data = &regulator2_data,
+	},
+},
+};
+/* register regulator 1 device */
+platform_device_register(&wm8350_regulator_devices[0]);
+
+/* register regulator 2 device */
+platform_device_register(&wm8350_regulator_devices[1]);
diff --git a/Documentation/power/regulator/regulator.txt b/Documentation/power/regulator/regulator.txt
index a690501..4200acc 100644
--- a/Documentation/power/regulator/regulator.txt
+++ b/Documentation/power/regulator/regulator.txt
@@ -10,11 +10,11 @@
 
 Drivers can register a regulator by calling :-
 
-struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc,
-					  void *reg_data);
+struct regulator_dev *regulator_register(struct device *dev,
+	struct regulator_desc *regulator_desc);
 
-This will register the regulators capabilities and operations the regulator
-core. The core does not touch reg_data (private to regulator driver).
+This will register the regulators capabilities and operations to the regulator
+core.
 
 Regulators can be unregistered by calling :-
 
diff --git a/MAINTAINERS b/MAINTAINERS
index 988b0a8..5d0b8a2 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4520,10 +4520,11 @@
 
 VOLTAGE AND CURRENT REGULATOR FRAMEWORK
 P:	Liam Girdwood
-M:	lg@opensource.wolfsonmicro.com
+M:	lrg@slimlogic.co.uk
 P:	Mark Brown
 M:	broonie@opensource.wolfsonmicro.com
 W:	http://opensource.wolfsonmicro.com/node/15
+W:	http://www.slimlogic.co.uk/?page_id=5
 T:	git kernel.org/pub/scm/linux/kernel/git/lrg/voltage-2.6.git
 S:	Supported
 
diff --git a/arch/m68k/Kconfig b/arch/m68k/Kconfig
index 41e5bf0..677c93a 100644
--- a/arch/m68k/Kconfig
+++ b/arch/m68k/Kconfig
@@ -105,21 +105,9 @@
 	  To compile this driver as modules, choose M here: the
 	  modules will be called pcmcia_core and ds.
 
-config SUN3
-	bool "Sun3 support"
-	select M68020
-	select MMU_SUN3 if MMU
-	help
-	  This option enables support for the Sun 3 series of workstations
-	  (3/50, 3/60, 3/1xx, 3/2xx systems). Enabling this option requires
-	  that all other hardware types must be disabled, as Sun 3 kernels
-	  are incompatible with all other m68k targets (including Sun 3x!).
-
-	  If you don't want to compile a kernel exclusively for a Sun 3, say N.
-
 config AMIGA
 	bool "Amiga support"
-	depends on !MMU_SUN3
+	select MMU_MOTOROLA if MMU
 	help
 	  This option enables support for the Amiga series of computers. If
 	  you plan to use this kernel on an Amiga, say Y here and browse the
@@ -127,33 +115,16 @@
 
 config ATARI
 	bool "Atari support"
-	depends on !MMU_SUN3
+	select MMU_MOTOROLA if MMU
 	help
 	  This option enables support for the 68000-based Atari series of
 	  computers (including the TT, Falcon and Medusa). If you plan to use
 	  this kernel on an Atari, say Y here and browse the material
 	  available in <file:Documentation/m68k>; otherwise say N.
 
-config HADES
-	bool "Hades support"
-	depends on ATARI && BROKEN
-	help
-	  This option enables support for the Hades Atari clone. If you plan
-	  to use this kernel on a Hades, say Y here; otherwise say N.
-
-config PCI
-	bool
-	depends on HADES
-	default y
-	help
-	  Find out whether you have a PCI motherboard. PCI is the name of a
-	  bus system, i.e. the way the CPU talks to the other stuff inside
-	  your box. Other bus systems are ISA, EISA, MicroChannel (MCA) or
-	  VESA. If you have PCI, say Y, otherwise N.
-
 config MAC
 	bool "Macintosh support"
-	depends on !MMU_SUN3
+	select MMU_MOTOROLA if MMU
 	help
 	  This option enables support for the Apple Macintosh series of
 	  computers (yes, there is experimental support now, at least for part
@@ -174,14 +145,14 @@
 
 config APOLLO
 	bool "Apollo support"
-	depends on !MMU_SUN3
+	select MMU_MOTOROLA if MMU
 	help
 	  Say Y here if you want to run Linux on an MC680x0-based Apollo
 	  Domain workstation such as the DN3500.
 
 config VME
 	bool "VME (Motorola and BVM) support"
-	depends on !MMU_SUN3
+	select MMU_MOTOROLA if MMU
 	help
 	  Say Y here if you want to build a kernel for a 680x0 based VME
 	  board.  Boards currently supported include Motorola boards MVME147,
@@ -218,7 +189,7 @@
 
 config HP300
 	bool "HP9000/300 and HP9000/400 support"
-	depends on !MMU_SUN3
+	select MMU_MOTOROLA if MMU
 	help
 	  This option enables support for the HP9000/300 and HP9000/400 series
 	  of workstations. Support for these machines is still somewhat
@@ -237,7 +208,7 @@
 
 config SUN3X
 	bool "Sun3x support"
-	depends on !MMU_SUN3
+	select MMU_MOTOROLA if MMU
 	select M68030
 	help
 	  This option enables support for the Sun 3x series of workstations.
@@ -250,7 +221,7 @@
 
 config Q40
 	bool "Q40/Q60 support"
-	depends on !MMU_SUN3
+	select MMU_MOTOROLA if MMU
 	help
 	  The Q40 is a Motorola 68040-based successor to the Sinclair QL
 	  manufactured in Germany.  There is an official Q40 home page at
@@ -258,6 +229,19 @@
 	  Q60. Select your CPU below.  For 68LC060 don't forget to enable FPU
 	  emulation.
 
+config SUN3
+	bool "Sun3 support"
+	depends on !MMU_MOTOROLA
+	select MMU_SUN3 if MMU
+	select M68020
+	help
+	  This option enables support for the Sun 3 series of workstations
+	  (3/50, 3/60, 3/1xx, 3/2xx systems). Enabling this option requires
+	  that all other hardware types must be disabled, as Sun 3 kernels
+	  are incompatible with all other m68k targets (including Sun 3x!).
+
+	  If you don't want to compile a kernel exclusively for a Sun 3, say N.
+
 comment "Processor type"
 
 config M68020
@@ -295,10 +279,10 @@
 config MMU_MOTOROLA
 	bool
 	depends on MMU && !MMU_SUN3
-	default y
 
 config MMU_SUN3
 	bool
+	depends on MMU && !MMU_MOTOROLA
 
 config M68KFPU_EMU
 	bool "Math emulation support (EXPERIMENTAL)"
diff --git a/arch/m68k/amiga/config.c b/arch/m68k/amiga/config.c
index df679d9..0a3f9e8 100644
--- a/arch/m68k/amiga/config.c
+++ b/arch/m68k/amiga/config.c
@@ -24,6 +24,7 @@
 #include <linux/interrupt.h>
 #include <linux/zorro.h>
 #include <linux/module.h>
+#include <linux/keyboard.h>
 
 #include <asm/bootinfo.h>
 #include <asm/setup.h>
@@ -984,3 +985,11 @@
 
 	return len;
 }
+
+/*
+ * The Amiga keyboard driver needs key_maps, but we cannot export it in
+ * drivers/char/defkeymap.c, as it is autogenerated
+ */
+#ifdef CONFIG_HW_CONSOLE
+EXPORT_SYMBOL_GPL(key_maps);
+#endif
diff --git a/arch/m68k/atari/Makefile b/arch/m68k/atari/Makefile
index 2cd905e..0cac723 100644
--- a/arch/m68k/atari/Makefile
+++ b/arch/m68k/atari/Makefile
@@ -5,7 +5,4 @@
 obj-y		:= config.o time.o debug.o ataints.o stdma.o \
 			atasound.o stram.o
 
-ifeq ($(CONFIG_PCI),y)
-obj-$(CONFIG_HADES)	+= hades-pci.o
-endif
 obj-$(CONFIG_ATARI_KBD_CORE)	+= atakeyb.o
diff --git a/arch/m68k/atari/ataints.c b/arch/m68k/atari/ataints.c
index b45593a..dba4afa 100644
--- a/arch/m68k/atari/ataints.c
+++ b/arch/m68k/atari/ataints.c
@@ -407,10 +407,8 @@
 		 * gets overruns)
 		 */
 
-		if (!MACH_IS_HADES) {
-			vectors[VEC_INT2] = falcon_hblhandler;
-			vectors[VEC_INT4] = falcon_hblhandler;
-		}
+		vectors[VEC_INT2] = falcon_hblhandler;
+		vectors[VEC_INT4] = falcon_hblhandler;
 	}
 
 	if (ATARIHW_PRESENT(PCM_8BIT) && ATARIHW_PRESENT(MICROWIRE)) {
diff --git a/arch/m68k/atari/atakeyb.c b/arch/m68k/atari/atakeyb.c
index bb959fb..c038b7c 100644
--- a/arch/m68k/atari/atakeyb.c
+++ b/arch/m68k/atari/atakeyb.c
@@ -635,15 +635,3 @@
 	return 0;
 }
 EXPORT_SYMBOL_GPL(atari_keyb_init);
-
-int atari_kbd_translate(unsigned char keycode, unsigned char *keycodep, char raw_mode)
-{
-#ifdef CONFIG_MAGIC_SYSRQ
-	/* ALT+HELP pressed? */
-	if ((keycode == 98) && ((shift_state & 0xff) == 8))
-		*keycodep = 0xff;
-	else
-#endif
-		*keycodep = keycode;
-	return 1;
-}
diff --git a/arch/m68k/atari/config.c b/arch/m68k/atari/config.c
index 5945e15..af03185 100644
--- a/arch/m68k/atari/config.c
+++ b/arch/m68k/atari/config.c
@@ -231,7 +231,7 @@
 	 */
 
 	printk("Atari hardware found: ");
-	if (MACH_IS_MEDUSA || MACH_IS_HADES) {
+	if (MACH_IS_MEDUSA) {
 		/* There's no Atari video hardware on the Medusa, but all the
 		 * addresses below generate a DTACK so no bus error occurs! */
 	} else if (hwreg_present(f030_xreg)) {
@@ -269,10 +269,6 @@
 		ATARIHW_SET(SCSI_DMA);
 		printk("TT_SCSI_DMA ");
 	}
-	if (!MACH_IS_HADES && hwreg_present(&st_dma.dma_hi)) {
-		ATARIHW_SET(STND_DMA);
-		printk("STND_DMA ");
-	}
 	/*
 	 * The ST-DMA address registers aren't readable
 	 * on all Medusas, so the test below may fail
@@ -294,12 +290,11 @@
 		ATARIHW_SET(YM_2149);
 		printk("YM2149 ");
 	}
-	if (!MACH_IS_MEDUSA && !MACH_IS_HADES &&
-		hwreg_present(&tt_dmasnd.ctrl)) {
+	if (!MACH_IS_MEDUSA && hwreg_present(&tt_dmasnd.ctrl)) {
 		ATARIHW_SET(PCM_8BIT);
 		printk("PCM ");
 	}
-	if (!MACH_IS_HADES && hwreg_present(&falcon_codec.unused5)) {
+	if (hwreg_present(&falcon_codec.unused5)) {
 		ATARIHW_SET(CODEC);
 		printk("CODEC ");
 	}
@@ -313,7 +308,7 @@
 	    (tt_scc_dma.dma_ctrl = 0x01, (tt_scc_dma.dma_ctrl & 1) == 1) &&
 	    (tt_scc_dma.dma_ctrl = 0x00, (tt_scc_dma.dma_ctrl & 1) == 0)
 #else
-	    !MACH_IS_MEDUSA && !MACH_IS_HADES
+	    !MACH_IS_MEDUSA
 #endif
 	    ) {
 		ATARIHW_SET(SCC_DMA);
@@ -327,10 +322,7 @@
 		ATARIHW_SET(ST_ESCC);
 		printk("ST_ESCC ");
 	}
-	if (MACH_IS_HADES) {
-		ATARIHW_SET(VME);
-		printk("VME ");
-	} else if (hwreg_present(&tt_scu.sys_mask)) {
+	if (hwreg_present(&tt_scu.sys_mask)) {
 		ATARIHW_SET(SCU);
 		/* Assume a VME bus if there's a SCU */
 		ATARIHW_SET(VME);
@@ -340,7 +332,7 @@
 		ATARIHW_SET(ANALOG_JOY);
 		printk("ANALOG_JOY ");
 	}
-	if (!MACH_IS_HADES && hwreg_present(blitter.halftone)) {
+	if (hwreg_present(blitter.halftone)) {
 		ATARIHW_SET(BLITTER);
 		printk("BLITTER ");
 	}
@@ -349,8 +341,7 @@
 		printk("IDE ");
 	}
 #if 1 /* This maybe wrong */
-	if (!MACH_IS_MEDUSA && !MACH_IS_HADES &&
-	    hwreg_present(&tt_microwire.data) &&
+	if (!MACH_IS_MEDUSA && hwreg_present(&tt_microwire.data) &&
 	    hwreg_present(&tt_microwire.mask) &&
 	    (tt_microwire.mask = 0x7ff,
 	     udelay(1),
@@ -369,19 +360,18 @@
 		mach_hwclk = atari_tt_hwclk;
 		mach_set_clock_mmss = atari_tt_set_clock_mmss;
 	}
-	if (!MACH_IS_HADES && hwreg_present(&mste_rtc.sec_ones)) {
+	if (hwreg_present(&mste_rtc.sec_ones)) {
 		ATARIHW_SET(MSTE_CLK);
 		printk("MSTE_CLK ");
 		mach_hwclk = atari_mste_hwclk;
 		mach_set_clock_mmss = atari_mste_set_clock_mmss;
 	}
-	if (!MACH_IS_MEDUSA && !MACH_IS_HADES &&
-	    hwreg_present(&dma_wd.fdc_speed) &&
+	if (!MACH_IS_MEDUSA && hwreg_present(&dma_wd.fdc_speed) &&
 	    hwreg_write(&dma_wd.fdc_speed, 0)) {
 		ATARIHW_SET(FDCSPEED);
 		printk("FDC_SPEED ");
 	}
-	if (!MACH_IS_HADES && !ATARIHW_PRESENT(ST_SCSI)) {
+	if (!ATARIHW_PRESENT(ST_SCSI)) {
 		ATARIHW_SET(ACSI);
 		printk("ACSI ");
 	}
@@ -449,7 +439,7 @@
 	 * 0xFFxxxxxx -> 0x00xxxxxx, so that the first 16MB is accessible
 	 * in the last 16MB of the address space.
 	 */
-	tos_version = (MACH_IS_MEDUSA || MACH_IS_HADES) ?
+	tos_version = (MACH_IS_MEDUSA) ?
 			0xfff : *(unsigned short *)0xff000002;
 	atari_rtc_year_offset = (tos_version < 0x306) ? 70 : 68;
 }
@@ -511,8 +501,7 @@
 	 * On the Medusa, phys. 0x4 may contain garbage because it's no
 	 * ROM.  See above for explanation why we cannot use PTOV(4).
 	 */
-	reset_addr = MACH_IS_HADES ? 0x7fe00030 :
-		     MACH_IS_MEDUSA || MACH_IS_AB40 ? 0xe00030 :
+	reset_addr = MACH_IS_MEDUSA || MACH_IS_AB40 ? 0xe00030 :
 		     *(unsigned long *) 0xff000004;
 
 	/* reset ACIA for switch off OverScan, if it's active */
@@ -606,8 +595,6 @@
 		if (MACH_IS_MEDUSA)
 			/* Medusa has TT _MCH cookie */
 			strcat(model, "Medusa");
-		else if (MACH_IS_HADES)
-			strcat(model, "Hades");
 		else
 			strcat(model, "TT");
 		break;
diff --git a/arch/m68k/atari/hades-pci.c b/arch/m68k/atari/hades-pci.c
deleted file mode 100644
index 2bbabc0..0000000
--- a/arch/m68k/atari/hades-pci.c
+++ /dev/null
@@ -1,440 +0,0 @@
-/*
- * hades-pci.c - Hardware specific PCI BIOS functions the Hades Atari clone.
- *
- * Written by Wout Klaren.
- */
-
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <asm/io.h>
-
-#if 0
-# define DBG_DEVS(args)		printk args
-#else
-# define DBG_DEVS(args)
-#endif
-
-#if defined(CONFIG_PCI) && defined(CONFIG_HADES)
-
-#include <linux/slab.h>
-#include <linux/mm.h>
-#include <linux/pci.h>
-
-#include <asm/atarihw.h>
-#include <asm/atariints.h>
-#include <asm/byteorder.h>
-#include <asm/pci.h>
-
-#define HADES_MEM_BASE		0x80000000
-#define HADES_MEM_SIZE		0x20000000
-#define HADES_CONFIG_BASE	0xA0000000
-#define HADES_CONFIG_SIZE	0x10000000
-#define HADES_IO_BASE		0xB0000000
-#define HADES_IO_SIZE		0x10000000
-#define HADES_VIRT_IO_SIZE	0x00010000	/* Only 64k is remapped and actually used. */
-
-#define N_SLOTS				4			/* Number of PCI slots. */
-
-static const char pci_mem_name[] = "PCI memory space";
-static const char pci_io_name[] = "PCI I/O space";
-static const char pci_config_name[] = "PCI config space";
-
-static struct resource config_space = {
-    .name = pci_config_name,
-    .start = HADES_CONFIG_BASE,
-    .end = HADES_CONFIG_BASE + HADES_CONFIG_SIZE - 1
-};
-static struct resource io_space = {
-    .name = pci_io_name,
-    .start = HADES_IO_BASE,
-    .end = HADES_IO_BASE + HADES_IO_SIZE - 1
-};
-
-static const unsigned long pci_conf_base_phys[] = {
-    0xA0080000, 0xA0040000, 0xA0020000, 0xA0010000
-};
-static unsigned long pci_conf_base_virt[N_SLOTS];
-static unsigned long pci_io_base_virt;
-
-/*
- * static void *mk_conf_addr(unsigned char bus, unsigned char device_fn,
- *			     unsigned char where)
- *
- * Calculate the address of the PCI configuration area of the given
- * device.
- *
- * BUG: boards with multiple functions are probably not correctly
- * supported.
- */
-
-static void *mk_conf_addr(struct pci_dev *dev, int where)
-{
-	int device = dev->devfn >> 3, function = dev->devfn & 7;
-	void *result;
-
-	DBG_DEVS(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x, pci_addr=0x%p)\n",
-		  dev->bus->number, dev->devfn, where, pci_addr));
-
-	if (device > 3)
-	{
-		DBG_DEVS(("mk_conf_addr: device (%d) > 3, returning NULL\n", device));
-		return NULL;
-	}
-
-	if (dev->bus->number != 0)
-	{
-		DBG_DEVS(("mk_conf_addr: bus (%d) > 0, returning NULL\n", device));
-		return NULL;
-	}
-
-	result = (void *) (pci_conf_base_virt[device] | (function << 8) | (where));
-	DBG_DEVS(("mk_conf_addr: returning pci_addr 0x%lx\n", (unsigned long) result));
-	return result;
-}
-
-static int hades_read_config_byte(struct pci_dev *dev, int where, u8 *value)
-{
-	volatile unsigned char *pci_addr;
-
-	*value = 0xff;
-
-	if ((pci_addr = (unsigned char *) mk_conf_addr(dev, where)) == NULL)
-		return PCIBIOS_DEVICE_NOT_FOUND;
-
-	*value = *pci_addr;
-
-	return PCIBIOS_SUCCESSFUL;
-}
-
-static int hades_read_config_word(struct pci_dev *dev, int where, u16 *value)
-{
-	volatile unsigned short *pci_addr;
-
-	*value = 0xffff;
-
-	if (where & 0x1)
-		return PCIBIOS_BAD_REGISTER_NUMBER;
-
-	if ((pci_addr = (unsigned short *) mk_conf_addr(dev, where)) == NULL)
-		return PCIBIOS_DEVICE_NOT_FOUND;
-
-	*value = le16_to_cpu(*pci_addr);
-
-	return PCIBIOS_SUCCESSFUL;
-}
-
-static int hades_read_config_dword(struct pci_dev *dev, int where, u32 *value)
-{
-	volatile unsigned int *pci_addr;
-	unsigned char header_type;
-	int result;
-
-	*value = 0xffffffff;
-
-	if (where & 0x3)
-		return PCIBIOS_BAD_REGISTER_NUMBER;
-
-	if ((pci_addr = (unsigned int *) mk_conf_addr(dev, where)) == NULL)
-		return PCIBIOS_DEVICE_NOT_FOUND;
-
-	*value = le32_to_cpu(*pci_addr);
-
-	/*
-	 * Check if the value is an address on the bus. If true, add the
-	 * base address of the PCI memory or PCI I/O area on the Hades.
-	 */
-
-	if ((result = hades_read_config_byte(dev, PCI_HEADER_TYPE,
-					     &header_type)) != PCIBIOS_SUCCESSFUL)
-		return result;
-
-	if (((where >= PCI_BASE_ADDRESS_0) && (where <= PCI_BASE_ADDRESS_1)) ||
-	    ((header_type != PCI_HEADER_TYPE_BRIDGE) && ((where >= PCI_BASE_ADDRESS_2) &&
-							 (where <= PCI_BASE_ADDRESS_5))))
-	{
-		if ((*value & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO)
-		{
-			/*
-			 * Base address register that contains an I/O address. If the
-			 * address is valid on the Hades (0 <= *value < HADES_VIRT_IO_SIZE),
-			 * add 'pci_io_base_virt' to the value.
-			 */
-
-			if (*value < HADES_VIRT_IO_SIZE)
-				*value += pci_io_base_virt;
-		}
-		else
-		{
-			/*
-			 * Base address register that contains an memory address. If the
-			 * address is valid on the Hades (0 <= *value < HADES_MEM_SIZE),
-			 * add HADES_MEM_BASE to the value.
-			 */
-
-			if (*value == 0)
-			{
-				/*
-				 * Base address is 0. Test if this base
-				 * address register is used.
-				 */
-
-				*pci_addr = 0xffffffff;
-				if (*pci_addr != 0)
-				{
-					*pci_addr = *value;
-					if (*value < HADES_MEM_SIZE)
-						*value += HADES_MEM_BASE;
-				}
-			}
-			else
-			{
-				if (*value < HADES_MEM_SIZE)
-					*value += HADES_MEM_BASE;
-			}
-		}
-	}
-
-	return PCIBIOS_SUCCESSFUL;
-}
-
-static int hades_write_config_byte(struct pci_dev *dev, int where, u8 value)
-{
-	volatile unsigned char *pci_addr;
-
-	if ((pci_addr = (unsigned char *) mk_conf_addr(dev, where)) == NULL)
-		return PCIBIOS_DEVICE_NOT_FOUND;
-
-	*pci_addr = value;
-
-	return PCIBIOS_SUCCESSFUL;
-}
-
-static int hades_write_config_word(struct pci_dev *dev, int where, u16 value)
-{
-	volatile unsigned short *pci_addr;
-
-	if ((pci_addr = (unsigned short *) mk_conf_addr(dev, where)) == NULL)
-		return PCIBIOS_DEVICE_NOT_FOUND;
-
-	*pci_addr = cpu_to_le16(value);
-
-	return PCIBIOS_SUCCESSFUL;
-}
-
-static int hades_write_config_dword(struct pci_dev *dev, int where, u32 value)
-{
-	volatile unsigned int *pci_addr;
-	unsigned char header_type;
-	int result;
-
-	if ((pci_addr = (unsigned int *) mk_conf_addr(dev, where)) == NULL)
-		return PCIBIOS_DEVICE_NOT_FOUND;
-
-	/*
-	 * Check if the value is an address on the bus. If true, subtract the
-	 * base address of the PCI memory or PCI I/O area on the Hades.
-	 */
-
-	if ((result = hades_read_config_byte(dev, PCI_HEADER_TYPE,
-					     &header_type)) != PCIBIOS_SUCCESSFUL)
-		return result;
-
-	if (((where >= PCI_BASE_ADDRESS_0) && (where <= PCI_BASE_ADDRESS_1)) ||
-	    ((header_type != PCI_HEADER_TYPE_BRIDGE) && ((where >= PCI_BASE_ADDRESS_2) &&
-							 (where <= PCI_BASE_ADDRESS_5))))
-	{
-		if ((value & PCI_BASE_ADDRESS_SPACE) ==
-		    PCI_BASE_ADDRESS_SPACE_IO)
-		{
-			/*
-			 * I/O address. Check if the address is valid address on
-			 * the Hades (pci_io_base_virt <= value < pci_io_base_virt +
-			 * HADES_VIRT_IO_SIZE) or if the value is 0xffffffff. If not
-			 * true do not write the base address register. If it is a
-			 * valid base address subtract 'pci_io_base_virt' from the value.
-			 */
-
-			if ((value >= pci_io_base_virt) && (value < (pci_io_base_virt +
-														 HADES_VIRT_IO_SIZE)))
-				value -= pci_io_base_virt;
-			else
-			{
-				if (value != 0xffffffff)
-					return PCIBIOS_SET_FAILED;
-			}
-		}
-		else
-		{
-			/*
-			 * Memory address. Check if the address is valid address on
-			 * the Hades (HADES_MEM_BASE <= value < HADES_MEM_BASE + HADES_MEM_SIZE) or
-			 * if the value is 0xffffffff. If not true do not write
-			 * the base address register. If it is a valid base address
-			 * subtract HADES_MEM_BASE from the value.
-			 */
-
-			if ((value >= HADES_MEM_BASE) && (value < (HADES_MEM_BASE + HADES_MEM_SIZE)))
-				value -= HADES_MEM_BASE;
-			else
-			{
-				if (value != 0xffffffff)
-					return PCIBIOS_SET_FAILED;
-			}
-		}
-	}
-
-	*pci_addr = cpu_to_le32(value);
-
-	return PCIBIOS_SUCCESSFUL;
-}
-
-/*
- * static inline void hades_fixup(void)
- *
- * Assign IRQ numbers as used by Linux to the interrupt pins
- * of the PCI cards.
- */
-
-static void __init hades_fixup(int pci_modify)
-{
-	char irq_tab[4] = {
-		[0] = IRQ_TT_MFP_IO0,		/* Slot 0. */
-		[1] = IRQ_TT_MFP_IO1,		/* Slot 1. */
-		[2] = IRQ_TT_MFP_SCC,		/* Slot 2. */
-		[3] = IRQ_TT_MFP_SCSIDMA	/* Slot 3. */
-	};
-	struct pci_dev *dev = NULL;
-	unsigned char slot;
-
-	/*
-	 * Go through all devices, fixing up irqs as we see fit:
-	 */
-
-	while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL)
-	{
-		if (dev->class >> 16 != PCI_BASE_CLASS_BRIDGE)
-		{
-			slot = PCI_SLOT(dev->devfn);	/* Determine slot number. */
-			dev->irq = irq_tab[slot];
-			if (pci_modify)
-				pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq);
-		}
-	}
-}
-
-/*
- * static void hades_conf_device(struct pci_dev *dev)
- *
- * Machine dependent Configure the given device.
- *
- * Parameters:
- *
- * dev		- the pci device.
- */
-
-static void __init hades_conf_device(struct pci_dev *dev)
-{
-	pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 0);
-}
-
-static struct pci_ops hades_pci_ops = {
-	.read_byte =	hades_read_config_byte,
-	.read_word =	hades_read_config_word,
-	.read_dword =	hades_read_config_dword,
-	.write_byte =	hades_write_config_byte,
-	.write_word =	hades_write_config_word,
-	.write_dword =	hades_write_config_dword
-};
-
-/*
- * struct pci_bus_info *init_hades_pci(void)
- *
- * Machine specific initialisation:
- *
- * - Allocate and initialise a 'pci_bus_info' structure
- * - Initialise hardware
- *
- * Result: pointer to 'pci_bus_info' structure.
- */
-
-struct pci_bus_info * __init init_hades_pci(void)
-{
-	struct pci_bus_info *bus;
-	int i;
-
-	/*
-	 * Remap I/O and configuration space.
-	 */
-
-	pci_io_base_virt = (unsigned long) ioremap(HADES_IO_BASE, HADES_VIRT_IO_SIZE);
-
-	for (i = 0; i < N_SLOTS; i++)
-		pci_conf_base_virt[i] = (unsigned long) ioremap(pci_conf_base_phys[i], 0x10000);
-
-	/*
-	 * Allocate memory for bus info structure.
-	 */
-
-	bus = kzalloc(sizeof(struct pci_bus_info), GFP_KERNEL);
-	if (unlikely(!bus))
-		goto iounmap_base_virt;
-
-	/*
-	 * Claim resources. The m68k has no separate I/O space, both
-	 * PCI memory space and PCI I/O space are in memory space. Therefore
-	 * the I/O resources are requested in memory space as well.
-	 */
-
-	if (unlikely(request_resource(&iomem_resource, &config_space) != 0))
-		goto free_bus;
-
-	if (unlikely(request_resource(&iomem_resource, &io_space) != 0))
-		goto release_config_space;
-
-	bus->mem_space.start = HADES_MEM_BASE;
-	bus->mem_space.end = HADES_MEM_BASE + HADES_MEM_SIZE - 1;
-	bus->mem_space.name = pci_mem_name;
-#if 1
-	if (unlikely(request_resource(&iomem_resource, &bus->mem_space) != 0))
-		goto release_io_space;
-#endif
-	bus->io_space.start = pci_io_base_virt;
-	bus->io_space.end = pci_io_base_virt + HADES_VIRT_IO_SIZE - 1;
-	bus->io_space.name = pci_io_name;
-#if 1
-	if (unlikely(request_resource(&ioport_resource, &bus->io_space) != 0))
-		goto release_bus_mem_space;
-#endif
-	/*
-	 * Set hardware dependent functions.
-	 */
-
-	bus->m68k_pci_ops = &hades_pci_ops;
-	bus->fixup = hades_fixup;
-	bus->conf_device = hades_conf_device;
-
-	/*
-	 * Select high to low edge for PCI interrupts.
-	 */
-
-	tt_mfp.active_edge &= ~0x27;
-
-	return bus;
-
-release_bus_mem_space:
-	release_resource(&bus->mem_space);
-release_io_space:
-	release_resource(&io_space);
-release_config_space:
-	release_resource(&config_space);
-free_bus:
-	kfree(bus);
-iounmap_base_virt:
-	iounmap((void *)pci_io_base_virt);
-
-	for (i = 0; i < N_SLOTS; i++)
-		iounmap((void *)pci_conf_base_virt[i]);
-
-	return NULL;
-}
-#endif
diff --git a/arch/m68k/atari/time.c b/arch/m68k/atari/time.c
index e0d3c8b..1edde27 100644
--- a/arch/m68k/atari/time.c
+++ b/arch/m68k/atari/time.c
@@ -20,6 +20,9 @@
 
 #include <asm/atariints.h>
 
+DEFINE_SPINLOCK(rtc_lock);
+EXPORT_SYMBOL_GPL(rtc_lock);
+
 void __init
 atari_sched_init(irq_handler_t timer_routine)
 {
@@ -191,13 +194,14 @@
         }
 
         if (!(ctrl & RTC_DM_BINARY)) {
-            BIN_TO_BCD(sec);
-            BIN_TO_BCD(min);
-            BIN_TO_BCD(hour);
-            BIN_TO_BCD(day);
-            BIN_TO_BCD(mon);
-            BIN_TO_BCD(year);
-            if (wday >= 0) BIN_TO_BCD(wday);
+	    sec = bin2bcd(sec);
+	    min = bin2bcd(min);
+	    hour = bin2bcd(hour);
+	    day = bin2bcd(day);
+	    mon = bin2bcd(mon);
+	    year = bin2bcd(year);
+	    if (wday >= 0)
+		wday = bin2bcd(wday);
         }
     }
 
@@ -252,13 +256,13 @@
 	}
 
 	if (!(ctrl & RTC_DM_BINARY)) {
-            BCD_TO_BIN(sec);
-            BCD_TO_BIN(min);
-            BCD_TO_BIN(hour);
-            BCD_TO_BIN(day);
-            BCD_TO_BIN(mon);
-            BCD_TO_BIN(year);
-            BCD_TO_BIN(wday);
+	    sec = bcd2bin(sec);
+	    min = bcd2bin(min);
+	    hour = bcd2bin(hour);
+	    day = bcd2bin(day);
+	    mon = bcd2bin(mon);
+	    year = bcd2bin(year);
+	    wday = bcd2bin(wday);
         }
 
         if (!(ctrl & RTC_24H)) {
@@ -318,7 +322,7 @@
 
     rtc_minutes = RTC_READ (RTC_MINUTES);
     if (!(save_control & RTC_DM_BINARY))
-        BCD_TO_BIN (rtc_minutes);
+	rtc_minutes = bcd2bin(rtc_minutes);
 
     /* Since we're only adjusting minutes and seconds, don't interfere
        with hour overflow.  This avoids messing with unknown time zones
@@ -329,8 +333,8 @@
         {
             if (!(save_control & RTC_DM_BINARY))
                 {
-                    BIN_TO_BCD (real_seconds);
-                    BIN_TO_BCD (real_minutes);
+		    real_seconds = bin2bcd(real_seconds);
+		    real_minutes = bin2bcd(real_minutes);
                 }
             RTC_WRITE (RTC_SECONDS, real_seconds);
             RTC_WRITE (RTC_MINUTES, real_minutes);
diff --git a/arch/m68k/bvme6000/config.c b/arch/m68k/bvme6000/config.c
index 9433a88..65c9204 100644
--- a/arch/m68k/bvme6000/config.c
+++ b/arch/m68k/bvme6000/config.c
@@ -25,6 +25,7 @@
 #include <linux/genhd.h>
 #include <linux/rtc.h>
 #include <linux/interrupt.h>
+#include <linux/bcd.h>
 
 #include <asm/bootinfo.h>
 #include <asm/system.h>
@@ -46,9 +47,6 @@
 extern void bvme6000_waitbut(void);
 void bvme6000_set_vectors (void);
 
-static unsigned char bcd2bin (unsigned char b);
-static unsigned char bin2bcd (unsigned char b);
-
 /* Save tick handler routine pointer, will point to do_timer() in
  * kernel/sched.c, called via bvme6000_process_int() */
 
@@ -264,17 +262,6 @@
     return v;
 }
 
-static unsigned char bcd2bin (unsigned char b)
-{
-	return ((b>>4)*10 + (b&15));
-}
-
-static unsigned char bin2bcd (unsigned char b)
-{
-	return (((b/10)*16) + (b%10));
-}
-
-
 /*
  * Looks like op is non-zero for setting the clock, and zero for
  * reading the clock.
diff --git a/arch/m68k/bvme6000/rtc.c b/arch/m68k/bvme6000/rtc.c
index e8ac3f7..808c901 100644
--- a/arch/m68k/bvme6000/rtc.c
+++ b/arch/m68k/bvme6000/rtc.c
@@ -57,16 +57,16 @@
 		rtc->msr = 0x40;
 		memset(&wtime, 0, sizeof(struct rtc_time));
 		do {
-			wtime.tm_sec =  BCD2BIN(rtc->bcd_sec);
-			wtime.tm_min =  BCD2BIN(rtc->bcd_min);
-			wtime.tm_hour = BCD2BIN(rtc->bcd_hr);
-			wtime.tm_mday =  BCD2BIN(rtc->bcd_dom);
-			wtime.tm_mon =  BCD2BIN(rtc->bcd_mth)-1;
-			wtime.tm_year = BCD2BIN(rtc->bcd_year);
+			wtime.tm_sec =  bcd2bin(rtc->bcd_sec);
+			wtime.tm_min =  bcd2bin(rtc->bcd_min);
+			wtime.tm_hour = bcd2bin(rtc->bcd_hr);
+			wtime.tm_mday =  bcd2bin(rtc->bcd_dom);
+			wtime.tm_mon =  bcd2bin(rtc->bcd_mth)-1;
+			wtime.tm_year = bcd2bin(rtc->bcd_year);
 			if (wtime.tm_year < 70)
 				wtime.tm_year += 100;
-			wtime.tm_wday = BCD2BIN(rtc->bcd_dow)-1;
-		} while (wtime.tm_sec != BCD2BIN(rtc->bcd_sec));
+			wtime.tm_wday = bcd2bin(rtc->bcd_dow)-1;
+		} while (wtime.tm_sec != bcd2bin(rtc->bcd_sec));
 		rtc->msr = msr;
 		local_irq_restore(flags);
 		return copy_to_user(argp, &wtime, sizeof wtime) ?
@@ -114,14 +114,14 @@
 
 		rtc->t0cr_rtmr = yrs%4;
 		rtc->bcd_tenms = 0;
-		rtc->bcd_sec   = BIN2BCD(sec);
-		rtc->bcd_min   = BIN2BCD(min);
-		rtc->bcd_hr    = BIN2BCD(hrs);
-		rtc->bcd_dom   = BIN2BCD(day);
-		rtc->bcd_mth   = BIN2BCD(mon);
-		rtc->bcd_year  = BIN2BCD(yrs%100);
+		rtc->bcd_sec   = bin2bcd(sec);
+		rtc->bcd_min   = bin2bcd(min);
+		rtc->bcd_hr    = bin2bcd(hrs);
+		rtc->bcd_dom   = bin2bcd(day);
+		rtc->bcd_mth   = bin2bcd(mon);
+		rtc->bcd_year  = bin2bcd(yrs%100);
 		if (rtc_tm.tm_wday >= 0)
-			rtc->bcd_dow = BIN2BCD(rtc_tm.tm_wday+1);
+			rtc->bcd_dow = bin2bcd(rtc_tm.tm_wday+1);
 		rtc->t0cr_rtmr = yrs%4 | 0x08;
 
 		rtc->msr = msr;
diff --git a/arch/m68k/kernel/Makefile b/arch/m68k/kernel/Makefile
index 3a7f622..55d5d6b 100644
--- a/arch/m68k/kernel/Makefile
+++ b/arch/m68k/kernel/Makefile
@@ -14,5 +14,4 @@
 
 devres-y = ../../../kernel/irq/devres.o
 
-obj-$(CONFIG_PCI)	+= bios32.o
 obj-y$(CONFIG_MMU_SUN3) += dma.o	# no, it's not a typo
diff --git a/arch/m68k/kernel/bios32.c b/arch/m68k/kernel/bios32.c
deleted file mode 100644
index af170c2b..0000000
--- a/arch/m68k/kernel/bios32.c
+++ /dev/null
@@ -1,514 +0,0 @@
-/*
- * bios32.c - PCI BIOS functions for m68k systems.
- *
- * Written by Wout Klaren.
- *
- * Based on the DEC Alpha bios32.c by Dave Rusling and David Mosberger.
- */
-
-#include <linux/init.h>
-#include <linux/kernel.h>
-
-#if 0
-# define DBG_DEVS(args)		printk args
-#else
-# define DBG_DEVS(args)
-#endif
-
-#ifdef CONFIG_PCI
-
-/*
- * PCI support for Linux/m68k. Currently only the Hades is supported.
- *
- * The support for PCI bridges in the DEC Alpha version has
- * been removed in this version.
- */
-
-#include <linux/pci.h>
-#include <linux/slab.h>
-#include <linux/mm.h>
-
-#include <asm/io.h>
-#include <asm/pci.h>
-#include <asm/uaccess.h>
-
-#define KB		1024
-#define MB		(1024*KB)
-#define GB		(1024*MB)
-
-#define MAJOR_REV	0
-#define MINOR_REV	5
-
-/*
- * Align VAL to ALIGN, which must be a power of two.
- */
-
-#define ALIGN(val,align)	(((val) + ((align) - 1)) & ~((align) - 1))
-
-/*
- * Offsets relative to the I/O and memory base addresses from where resources
- * are allocated.
- */
-
-#define IO_ALLOC_OFFSET		0x00004000
-#define MEM_ALLOC_OFFSET	0x04000000
-
-/*
- * Declarations of hardware specific initialisation functions.
- */
-
-extern struct pci_bus_info *init_hades_pci(void);
-
-/*
- * Bus info structure of the PCI bus. A pointer to this structure is
- * put in the sysdata member of the pci_bus structure.
- */
-
-static struct pci_bus_info *bus_info;
-
-static int pci_modify = 1;		/* If set, layout the PCI bus ourself. */
-static int skip_vga;			/* If set do not modify base addresses
-					   of vga cards.*/
-static int disable_pci_burst;		/* If set do not allow PCI bursts. */
-
-static unsigned int io_base;
-static unsigned int mem_base;
-
-/*
- * static void disable_dev(struct pci_dev *dev)
- *
- * Disable PCI device DEV so that it does not respond to I/O or memory
- * accesses.
- *
- * Parameters:
- *
- * dev	- device to disable.
- */
-
-static void __init disable_dev(struct pci_dev *dev)
-{
-	unsigned short cmd;
-
-	if (((dev->class >> 8 == PCI_CLASS_NOT_DEFINED_VGA) ||
-	     (dev->class >> 8 == PCI_CLASS_DISPLAY_VGA) ||
-	     (dev->class >> 8 == PCI_CLASS_DISPLAY_XGA)) && skip_vga)
-		return;
-
-	pci_read_config_word(dev, PCI_COMMAND, &cmd);
-
-	cmd &= (~PCI_COMMAND_IO & ~PCI_COMMAND_MEMORY & ~PCI_COMMAND_MASTER);
-	pci_write_config_word(dev, PCI_COMMAND, cmd);
-}
-
-/*
- * static void layout_dev(struct pci_dev *dev)
- *
- * Layout memory and I/O for a device.
- *
- * Parameters:
- *
- * device	- device to layout memory and I/O for.
- */
-
-static void __init layout_dev(struct pci_dev *dev)
-{
-	unsigned short cmd;
-	unsigned int base, mask, size, reg;
-	unsigned int alignto;
-	int i;
-
-	/*
-	 * Skip video cards if requested.
-	 */
-
-	if (((dev->class >> 8 == PCI_CLASS_NOT_DEFINED_VGA) ||
-	     (dev->class >> 8 == PCI_CLASS_DISPLAY_VGA) ||
-	     (dev->class >> 8 == PCI_CLASS_DISPLAY_XGA)) && skip_vga)
-		return;
-
-	pci_read_config_word(dev, PCI_COMMAND, &cmd);
-
-	for (reg = PCI_BASE_ADDRESS_0, i = 0; reg <= PCI_BASE_ADDRESS_5; reg += 4, i++)
-	{
-		/*
-		 * Figure out how much space and of what type this
-		 * device wants.
-		 */
-
-		pci_write_config_dword(dev, reg, 0xffffffff);
-		pci_read_config_dword(dev, reg, &base);
-
-		if (!base)
-		{
-			/* this base-address register is unused */
-			dev->resource[i].start = 0;
-			dev->resource[i].end = 0;
-			dev->resource[i].flags = 0;
-			continue;
-		}
-
-		/*
-		 * We've read the base address register back after
-		 * writing all ones and so now we must decode it.
-		 */
-
-		if (base & PCI_BASE_ADDRESS_SPACE_IO)
-		{
-			/*
-			 * I/O space base address register.
-			 */
-
-			cmd |= PCI_COMMAND_IO;
-
-			base &= PCI_BASE_ADDRESS_IO_MASK;
-			mask = (~base << 1) | 0x1;
-			size = (mask & base) & 0xffffffff;
-
-			/*
-			 * Align to multiple of size of minimum base.
-			 */
-
-			alignto = max_t(unsigned int, 0x040, size);
-			base = ALIGN(io_base, alignto);
-			io_base = base + size;
-			pci_write_config_dword(dev, reg, base | PCI_BASE_ADDRESS_SPACE_IO);
-
-			dev->resource[i].start = base;
-			dev->resource[i].end = dev->resource[i].start + size - 1;
-			dev->resource[i].flags = IORESOURCE_IO | PCI_BASE_ADDRESS_SPACE_IO;
-
-			DBG_DEVS(("layout_dev: IO address: %lX\n", base));
-		}
-		else
-		{
-			unsigned int type;
-
-			/*
-			 * Memory space base address register.
-			 */
-
-			cmd |= PCI_COMMAND_MEMORY;
-			type = base & PCI_BASE_ADDRESS_MEM_TYPE_MASK;
-			base &= PCI_BASE_ADDRESS_MEM_MASK;
-			mask = (~base << 1) | 0x1;
-			size = (mask & base) & 0xffffffff;
-			switch (type)
-			{
-			case PCI_BASE_ADDRESS_MEM_TYPE_32:
-			case PCI_BASE_ADDRESS_MEM_TYPE_64:
-				break;
-
-			case PCI_BASE_ADDRESS_MEM_TYPE_1M:
-				printk("bios32 WARNING: slot %d, function %d "
-				       "requests memory below 1MB---don't "
-				       "know how to do that.\n",
-				       PCI_SLOT(dev->devfn),
-				       PCI_FUNC(dev->devfn));
-				continue;
-			}
-
-			/*
-			 * Align to multiple of size of minimum base.
-			 */
-
-			alignto = max_t(unsigned int, 0x1000, size);
-			base = ALIGN(mem_base, alignto);
-			mem_base = base + size;
-			pci_write_config_dword(dev, reg, base);
-
-			dev->resource[i].start = base;
-			dev->resource[i].end = dev->resource[i].start + size - 1;
-			dev->resource[i].flags = IORESOURCE_MEM;
-
-			if (type == PCI_BASE_ADDRESS_MEM_TYPE_64)
-			{
-				/*
-				 * 64-bit address, set the highest 32 bits
-				 * to zero.
-				 */
-
-				reg += 4;
-				pci_write_config_dword(dev, reg, 0);
-
-				i++;
-				dev->resource[i].start = 0;
-				dev->resource[i].end = 0;
-				dev->resource[i].flags = 0;
-			}
-		}
-	}
-
-	/*
-	 * Enable device:
-	 */
-
-	if (dev->class >> 8 == PCI_CLASS_NOT_DEFINED ||
-	    dev->class >> 8 == PCI_CLASS_NOT_DEFINED_VGA ||
-	    dev->class >> 8 == PCI_CLASS_DISPLAY_VGA ||
-	    dev->class >> 8 == PCI_CLASS_DISPLAY_XGA)
-	{
-		/*
-		 * All of these (may) have I/O scattered all around
-		 * and may not use i/o-base address registers at all.
-		 * So we just have to always enable I/O to these
-		 * devices.
-		 */
-		cmd |= PCI_COMMAND_IO;
-	}
-
-	pci_write_config_word(dev, PCI_COMMAND, cmd | PCI_COMMAND_MASTER);
-
-	pci_write_config_byte(dev, PCI_LATENCY_TIMER, (disable_pci_burst) ? 0 : 32);
-
-	if (bus_info != NULL)
-		bus_info->conf_device(dev);	/* Machine dependent configuration. */
-
-	DBG_DEVS(("layout_dev: bus %d  slot 0x%x  VID 0x%x  DID 0x%x  class 0x%x\n",
-		  dev->bus->number, PCI_SLOT(dev->devfn), dev->vendor, dev->device, dev->class));
-}
-
-/*
- * static void layout_bus(struct pci_bus *bus)
- *
- * Layout memory and I/O for all devices on the given bus.
- *
- * Parameters:
- *
- * bus	- bus.
- */
-
-static void __init layout_bus(struct pci_bus *bus)
-{
-	unsigned int bio, bmem;
-	struct pci_dev *dev;
-
-	DBG_DEVS(("layout_bus: starting bus %d\n", bus->number));
-
-	if (!bus->devices && !bus->children)
-		return;
-
-	/*
-	 * Align the current bases on appropriate boundaries (4K for
-	 * IO and 1MB for memory).
-	 */
-
-	bio = io_base = ALIGN(io_base, 4*KB);
-	bmem = mem_base = ALIGN(mem_base, 1*MB);
-
-	/*
-	 * PCI devices might have been setup by a PCI BIOS emulation
-	 * running under TOS. In these cases there is a
-	 * window during which two devices may have an overlapping
-	 * address range. To avoid this causing trouble, we first
-	 * turn off the I/O and memory address decoders for all PCI
-	 * devices.  They'll be re-enabled only once all address
-	 * decoders are programmed consistently.
-	 */
-
-	DBG_DEVS(("layout_bus: disable_dev for bus %d\n", bus->number));
-
-	for (dev = bus->devices; dev; dev = dev->sibling)
-	{
-		if ((dev->class >> 16 != PCI_BASE_CLASS_BRIDGE) ||
-		    (dev->class >> 8 == PCI_CLASS_BRIDGE_PCMCIA))
-			disable_dev(dev);
-	}
-
-	/*
-	 * Allocate space to each device:
-	 */
-
-	DBG_DEVS(("layout_bus: starting bus %d devices\n", bus->number));
-
-	for (dev = bus->devices; dev; dev = dev->sibling)
-	{
-		if ((dev->class >> 16 != PCI_BASE_CLASS_BRIDGE) ||
-		    (dev->class >> 8 == PCI_CLASS_BRIDGE_PCMCIA))
-			layout_dev(dev);
-	}
-
-	DBG_DEVS(("layout_bus: bus %d finished\n", bus->number));
-}
-
-/*
- * static void pcibios_fixup(void)
- *
- * Layout memory and I/O of all devices on the PCI bus if 'pci_modify' is
- * true. This might be necessary because not every m68k machine with a PCI
- * bus has a PCI BIOS. This function should be called right after
- * pci_scan_bus() in pcibios_init().
- */
-
-static void __init pcibios_fixup(void)
-{
-	if (pci_modify)
-	{
-		/*
-		 * Set base addresses for allocation of I/O and memory space.
-		 */
-
-		io_base = bus_info->io_space.start + IO_ALLOC_OFFSET;
-		mem_base = bus_info->mem_space.start + MEM_ALLOC_OFFSET;
-
-		/*
-		 * Scan the tree, allocating PCI memory and I/O space.
-		 */
-
-		layout_bus(pci_bus_b(pci_root.next));
-	}
-
-	/*
-	 * Fix interrupt assignments, etc.
-	 */
-
-	bus_info->fixup(pci_modify);
-}
-
-/*
- * static void pcibios_claim_resources(struct pci_bus *bus)
- *
- * Claim all resources that are assigned to devices on the given bus.
- *
- * Parameters:
- *
- * bus	- bus.
- */
-
-static void __init pcibios_claim_resources(struct pci_bus *bus)
-{
-	struct pci_dev *dev;
-	int i;
-
-	while (bus)
-	{
-		for (dev = bus->devices; (dev != NULL); dev = dev->sibling)
-		{
-			for (i = 0; i < PCI_NUM_RESOURCES; i++)
-			{
-				struct resource *r = &dev->resource[i];
-				struct resource *pr;
-				struct pci_bus_info *bus_info = (struct pci_bus_info *) dev->sysdata;
-
-				if ((r->start == 0) || (r->parent != NULL))
-					continue;
-#if 1
-				if (r->flags & IORESOURCE_IO)
-					pr = &bus_info->io_space;
-				else
-					pr = &bus_info->mem_space;
-#else
-				if (r->flags & IORESOURCE_IO)
-					pr = &ioport_resource;
-				else
-					pr = &iomem_resource;
-#endif
-				if (request_resource(pr, r) < 0)
-				{
-					printk(KERN_ERR "PCI: Address space collision on region %d of device %s\n", i, dev->name);
-				}
-			}
-		}
-
-		if (bus->children)
-			pcibios_claim_resources(bus->children);
-
-		bus = bus->next;
-	}
-}
-
-/*
- * int pcibios_assign_resource(struct pci_dev *dev, int i)
- *
- * Assign a new address to a PCI resource.
- *
- * Parameters:
- *
- * dev	- device.
- * i	- resource.
- *
- * Result: 0 if successful.
- */
-
-int __init pcibios_assign_resource(struct pci_dev *dev, int i)
-{
-	struct resource *r = &dev->resource[i];
-	struct resource *pr = pci_find_parent_resource(dev, r);
-	unsigned long size = r->end + 1;
-
-	if (!pr)
-		return -EINVAL;
-
-	if (r->flags & IORESOURCE_IO)
-	{
-		if (size > 0x100)
-			return -EFBIG;
-
-		if (allocate_resource(pr, r, size, bus_info->io_space.start +
-				      IO_ALLOC_OFFSET,  bus_info->io_space.end, 1024))
-			return -EBUSY;
-	}
-	else
-	{
-		if (allocate_resource(pr, r, size, bus_info->mem_space.start +
-				      MEM_ALLOC_OFFSET, bus_info->mem_space.end, size))
-			return -EBUSY;
-	}
-
-	if (i < 6)
-		pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + 4 * i, r->start);
-
-	return 0;
-}
-
-void __init pcibios_fixup_bus(struct pci_bus *bus)
-{
-	struct pci_dev *dev;
-	void *sysdata;
-
-	sysdata = (bus->parent) ? bus->parent->sysdata : bus->sysdata;
-
-	for (dev = bus->devices; (dev != NULL); dev = dev->sibling)
-		dev->sysdata = sysdata;
-}
-
-void __init pcibios_init(void)
-{
-	printk("Linux/m68k PCI BIOS32 revision %x.%02x\n", MAJOR_REV, MINOR_REV);
-
-	bus_info = NULL;
-#ifdef CONFIG_HADES
-	if (MACH_IS_HADES)
-		bus_info = init_hades_pci();
-#endif
-	if (bus_info != NULL)
-	{
-		printk("PCI: Probing PCI hardware\n");
-		pci_scan_bus(0, bus_info->m68k_pci_ops, bus_info);
-		pcibios_fixup();
-		pcibios_claim_resources(pci_root);
-	}
-	else
-		printk("PCI: No PCI bus detected\n");
-}
-
-char * __init pcibios_setup(char *str)
-{
-	if (!strcmp(str, "nomodify"))
-	{
-		pci_modify = 0;
-		return NULL;
-	}
-	else if (!strcmp(str, "skipvga"))
-	{
-		skip_vga = 1;
-		return NULL;
-	}
-	else if (!strcmp(str, "noburst"))
-	{
-		disable_pci_burst = 1;
-		return NULL;
-	}
-
-	return str;
-}
-#endif /* CONFIG_PCI */
diff --git a/arch/m68k/kernel/dma.c b/arch/m68k/kernel/dma.c
index 6f8c080..2bb4245 100644
--- a/arch/m68k/kernel/dma.c
+++ b/arch/m68k/kernel/dma.c
@@ -66,8 +66,8 @@
 }
 EXPORT_SYMBOL(dma_free_coherent);
 
-inline void dma_sync_single_for_device(struct device *dev, dma_addr_t handle, size_t size,
-				       enum dma_data_direction dir)
+void dma_sync_single_for_device(struct device *dev, dma_addr_t handle,
+				size_t size, enum dma_data_direction dir)
 {
 	switch (dir) {
 	case DMA_TO_DEVICE:
diff --git a/arch/m68k/kernel/ints.c b/arch/m68k/kernel/ints.c
index ded7dd2..7e8a0d3 100644
--- a/arch/m68k/kernel/ints.c
+++ b/arch/m68k/kernel/ints.c
@@ -429,8 +429,9 @@
 	return 0;
 }
 
+#ifdef CONFIG_PROC_FS
 void init_irq_proc(void)
 {
 	/* Insert /proc/irq driver here */
 }
-
+#endif
diff --git a/arch/m68k/kernel/process.c b/arch/m68k/kernel/process.c
index 7888cdf..3042c2b 100644
--- a/arch/m68k/kernel/process.c
+++ b/arch/m68k/kernel/process.c
@@ -78,7 +78,7 @@
 static void default_idle(void)
 {
 	if (!need_resched())
-#if defined(MACH_ATARI_ONLY) && !defined(CONFIG_HADES)
+#if defined(MACH_ATARI_ONLY)
 		/* block out HSYNC on the atari (falcon) */
 		__asm__("stop #0x2200" : : : "cc");
 #else
diff --git a/arch/m68k/kernel/traps.c b/arch/m68k/kernel/traps.c
index 75b8340..6d813de 100644
--- a/arch/m68k/kernel/traps.c
+++ b/arch/m68k/kernel/traps.c
@@ -883,8 +883,7 @@
 			if (i % 5 == 0)
 				printk("\n       ");
 #endif
-			printk(" [<%08lx>]", addr);
-			print_symbol(" %s\n", addr);
+			printk(" [<%08lx>] %pS\n", addr, (void *)addr);
 			i++;
 		}
 	}
@@ -900,10 +899,8 @@
 	int i;
 
 	print_modules();
-	printk("PC: [<%08lx>]",regs->pc);
-	print_symbol(" %s", regs->pc);
-	printk("\nSR: %04x  SP: %p  a2: %08lx\n",
-	       regs->sr, regs, regs->a2);
+	printk("PC: [<%08lx>] %pS\n", regs->pc, (void *)regs->pc);
+	printk("SR: %04x  SP: %p  a2: %08lx\n", regs->sr, regs, regs->a2);
 	printk("d0: %08lx    d1: %08lx    d2: %08lx    d3: %08lx\n",
 	       regs->d0, regs->d1, regs->d2, regs->d3);
 	printk("d4: %08lx    d5: %08lx    a0: %08lx    a1: %08lx\n",
diff --git a/arch/m68k/kernel/vmlinux-std.lds b/arch/m68k/kernel/vmlinux-std.lds
index 99b0784..f846d4e 100644
--- a/arch/m68k/kernel/vmlinux-std.lds
+++ b/arch/m68k/kernel/vmlinux-std.lds
@@ -34,10 +34,10 @@
 	CONSTRUCTORS
 	}
 
-  .bss : { *(.bss) }		/* BSS */
-
   . = ALIGN(16);
-  .data.cacheline_aligned : { *(.data.cacheline_aligned) } :data
+  .data.cacheline_aligned : { *(.data.cacheline_aligned) }
+
+  .bss : { *(.bss) }		/* BSS */
 
   _edata = .;			/* End of data section */
 
@@ -48,7 +48,7 @@
 	_sinittext = .;
 	INIT_TEXT
 	_einittext = .;
-  }
+  } :data
   .init.data : { INIT_DATA }
   . = ALIGN(16);
   __setup_start = .;
@@ -74,6 +74,7 @@
   .init.ramfs : { *(.init.ramfs) }
   __initramfs_end = .;
 #endif
+  NOTES
   . = ALIGN(8192);
   __init_end = .;
 
diff --git a/arch/m68k/mm/kmap.c b/arch/m68k/mm/kmap.c
index 46b7d60..df620ac 100644
--- a/arch/m68k/mm/kmap.c
+++ b/arch/m68k/mm/kmap.c
@@ -66,8 +66,10 @@
 	for (p = &iolist; (tmp = *p) ; p = &tmp->next) {
 		if (size + addr < (unsigned long)tmp->addr)
 			break;
-		if (addr > KMAP_END-size)
+		if (addr > KMAP_END-size) {
+			kfree(area);
 			return NULL;
+		}
 		addr = tmp->size + (unsigned long)tmp->addr;
 	}
 	area->addr = (void *)addr;
diff --git a/arch/m68k/mvme16x/rtc.c b/arch/m68k/mvme16x/rtc.c
index 432a9f1..cea5e3e 100644
--- a/arch/m68k/mvme16x/rtc.c
+++ b/arch/m68k/mvme16x/rtc.c
@@ -52,15 +52,15 @@
 		/* Ensure clock and real-time-mode-register are accessible */
 		rtc->ctrl = RTC_READ;
 		memset(&wtime, 0, sizeof(struct rtc_time));
-		wtime.tm_sec =  BCD2BIN(rtc->bcd_sec);
-		wtime.tm_min =  BCD2BIN(rtc->bcd_min);
-		wtime.tm_hour = BCD2BIN(rtc->bcd_hr);
-		wtime.tm_mday =  BCD2BIN(rtc->bcd_dom);
-		wtime.tm_mon =  BCD2BIN(rtc->bcd_mth)-1;
-		wtime.tm_year = BCD2BIN(rtc->bcd_year);
+		wtime.tm_sec =  bcd2bin(rtc->bcd_sec);
+		wtime.tm_min =  bcd2bin(rtc->bcd_min);
+		wtime.tm_hour = bcd2bin(rtc->bcd_hr);
+		wtime.tm_mday =  bcd2bin(rtc->bcd_dom);
+		wtime.tm_mon =  bcd2bin(rtc->bcd_mth)-1;
+		wtime.tm_year = bcd2bin(rtc->bcd_year);
 		if (wtime.tm_year < 70)
 			wtime.tm_year += 100;
-		wtime.tm_wday = BCD2BIN(rtc->bcd_dow)-1;
+		wtime.tm_wday = bcd2bin(rtc->bcd_dow)-1;
 		rtc->ctrl = 0;
 		local_irq_restore(flags);
 		return copy_to_user(argp, &wtime, sizeof wtime) ?
@@ -104,12 +104,12 @@
 		local_irq_save(flags);
 		rtc->ctrl     = RTC_WRITE;
 
-		rtc->bcd_sec  = BIN2BCD(sec);
-		rtc->bcd_min  = BIN2BCD(min);
-		rtc->bcd_hr   = BIN2BCD(hrs);
-		rtc->bcd_dom  = BIN2BCD(day);
-		rtc->bcd_mth  = BIN2BCD(mon);
-		rtc->bcd_year = BIN2BCD(yrs%100);
+		rtc->bcd_sec  = bin2bcd(sec);
+		rtc->bcd_min  = bin2bcd(min);
+		rtc->bcd_hr   = bin2bcd(hrs);
+		rtc->bcd_dom  = bin2bcd(day);
+		rtc->bcd_mth  = bin2bcd(mon);
+		rtc->bcd_year = bin2bcd(yrs%100);
 
 		rtc->ctrl     = 0;
 		local_irq_restore(flags);
diff --git a/arch/m68k/q40/config.c b/arch/m68k/q40/config.c
index be9de2f..9c7eefa 100644
--- a/arch/m68k/q40/config.c
+++ b/arch/m68k/q40/config.c
@@ -23,6 +23,7 @@
 #include <linux/serial_reg.h>
 #include <linux/rtc.h>
 #include <linux/vt_kern.h>
+#include <linux/bcd.h>
 
 #include <asm/io.h>
 #include <asm/rtc.h>
@@ -216,17 +217,6 @@
 }
 
 
-static inline unsigned char bcd2bin(unsigned char b)
-{
-	return (b >> 4) * 10 + (b & 15);
-}
-
-static inline unsigned char bin2bcd(unsigned char b)
-{
-	return (b / 10) * 16 + (b % 10);
-}
-
-
 static unsigned long q40_gettimeoffset(void)
 {
 	return 5000 * (ql_ticks != 0);
diff --git a/arch/m68k/sun3x/time.c b/arch/m68k/sun3x/time.c
index f5eaafb..536a04a 100644
--- a/arch/m68k/sun3x/time.c
+++ b/arch/m68k/sun3x/time.c
@@ -47,23 +47,23 @@
 
 	if(set) {
 		h->csr |= C_WRITE;
-		h->sec = BIN2BCD(t->tm_sec);
-		h->min = BIN2BCD(t->tm_min);
-		h->hour = BIN2BCD(t->tm_hour);
-		h->wday = BIN2BCD(t->tm_wday);
-		h->mday = BIN2BCD(t->tm_mday);
-		h->month = BIN2BCD(t->tm_mon);
-		h->year = BIN2BCD(t->tm_year);
+		h->sec = bin2bcd(t->tm_sec);
+		h->min = bin2bcd(t->tm_min);
+		h->hour = bin2bcd(t->tm_hour);
+		h->wday = bin2bcd(t->tm_wday);
+		h->mday = bin2bcd(t->tm_mday);
+		h->month = bin2bcd(t->tm_mon);
+		h->year = bin2bcd(t->tm_year);
 		h->csr &= ~C_WRITE;
 	} else {
 		h->csr |= C_READ;
-		t->tm_sec = BCD2BIN(h->sec);
-		t->tm_min = BCD2BIN(h->min);
-		t->tm_hour = BCD2BIN(h->hour);
-		t->tm_wday = BCD2BIN(h->wday);
-		t->tm_mday = BCD2BIN(h->mday);
-		t->tm_mon = BCD2BIN(h->month);
-		t->tm_year = BCD2BIN(h->year);
+		t->tm_sec = bcd2bin(h->sec);
+		t->tm_min = bcd2bin(h->min);
+		t->tm_hour = bcd2bin(h->hour);
+		t->tm_wday = bcd2bin(h->wday);
+		t->tm_mday = bcd2bin(h->mday);
+		t->tm_mon = bcd2bin(h->month);
+		t->tm_year = bcd2bin(h->year);
 		h->csr &= ~C_READ;
 	}
 
diff --git a/arch/x86/kernel/rtc.c b/arch/x86/kernel/rtc.c
index 05191bb..0a23b57 100644
--- a/arch/x86/kernel/rtc.c
+++ b/arch/x86/kernel/rtc.c
@@ -223,11 +223,25 @@
 static __init int add_rtc_cmos(void)
 {
 #ifdef CONFIG_PNP
-	if (!pnp_platform_devices)
-		platform_device_register(&rtc_device);
-#else
+	static const char *ids[] __initconst =
+	    { "PNP0b00", "PNP0b01", "PNP0b02", };
+	struct pnp_dev *dev;
+	struct pnp_id *id;
+	int i;
+
+	pnp_for_each_dev(dev) {
+		for (id = dev->id; id; id = id->next) {
+			for (i = 0; i < ARRAY_SIZE(ids); i++) {
+				if (compare_pnp_id(id, ids[i]) != 0)
+					return 0;
+			}
+		}
+	}
+#endif
+
 	platform_device_register(&rtc_device);
-#endif /* CONFIG_PNP */
+	dev_info(&rtc_device.dev,
+		 "registered platform RTC device (no PNP device found)\n");
 	return 0;
 }
 device_initcall(add_rtc_cmos);
diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c
index 3c578ef..24649ad 100644
--- a/drivers/acpi/glue.c
+++ b/drivers/acpi/glue.c
@@ -260,115 +260,3 @@
 }
 
 arch_initcall(init_acpi_device_notify);
-
-
-#if defined(CONFIG_RTC_DRV_CMOS) || defined(CONFIG_RTC_DRV_CMOS_MODULE)
-
-#ifdef CONFIG_PM
-static u32 rtc_handler(void *context)
-{
-	acpi_clear_event(ACPI_EVENT_RTC);
-	acpi_disable_event(ACPI_EVENT_RTC, 0);
-	return ACPI_INTERRUPT_HANDLED;
-}
-
-static inline void rtc_wake_setup(void)
-{
-	acpi_install_fixed_event_handler(ACPI_EVENT_RTC, rtc_handler, NULL);
-	/*
-	 * After the RTC handler is installed, the Fixed_RTC event should
-	 * be disabled. Only when the RTC alarm is set will it be enabled.
-	 */
-	acpi_clear_event(ACPI_EVENT_RTC);
-	acpi_disable_event(ACPI_EVENT_RTC, 0);
-}
-
-static void rtc_wake_on(struct device *dev)
-{
-	acpi_clear_event(ACPI_EVENT_RTC);
-	acpi_enable_event(ACPI_EVENT_RTC, 0);
-}
-
-static void rtc_wake_off(struct device *dev)
-{
-	acpi_disable_event(ACPI_EVENT_RTC, 0);
-}
-#else
-#define rtc_wake_setup()	do{}while(0)
-#define rtc_wake_on		NULL
-#define rtc_wake_off		NULL
-#endif
-
-/* Every ACPI platform has a mc146818 compatible "cmos rtc".  Here we find
- * its device node and pass extra config data.  This helps its driver use
- * capabilities that the now-obsolete mc146818 didn't have, and informs it
- * that this board's RTC is wakeup-capable (per ACPI spec).
- */
-#include <linux/mc146818rtc.h>
-
-static struct cmos_rtc_board_info rtc_info;
-
-
-/* PNP devices are registered in a subsys_initcall();
- * ACPI specifies the PNP IDs to use.
- */
-#include <linux/pnp.h>
-
-static int __init pnp_match(struct device *dev, void *data)
-{
-	static const char *ids[] = { "PNP0b00", "PNP0b01", "PNP0b02", };
-	struct pnp_dev *pnp = to_pnp_dev(dev);
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(ids); i++) {
-		if (compare_pnp_id(pnp->id, ids[i]) != 0)
-			return 1;
-	}
-	return 0;
-}
-
-static struct device *__init get_rtc_dev(void)
-{
-	return bus_find_device(&pnp_bus_type, NULL, NULL, pnp_match);
-}
-
-static int __init acpi_rtc_init(void)
-{
-	struct device *dev = get_rtc_dev();
-
-	if (acpi_disabled)
-		return 0;
-
-	if (dev) {
-		rtc_wake_setup();
-		rtc_info.wake_on = rtc_wake_on;
-		rtc_info.wake_off = rtc_wake_off;
-
-		/* workaround bug in some ACPI tables */
-		if (acpi_gbl_FADT.month_alarm && !acpi_gbl_FADT.day_alarm) {
-			DBG("bogus FADT month_alarm\n");
-			acpi_gbl_FADT.month_alarm = 0;
-		}
-
-		rtc_info.rtc_day_alarm = acpi_gbl_FADT.day_alarm;
-		rtc_info.rtc_mon_alarm = acpi_gbl_FADT.month_alarm;
-		rtc_info.rtc_century = acpi_gbl_FADT.century;
-
-		/* NOTE:  S4_RTC_WAKE is NOT currently useful to Linux */
-		if (acpi_gbl_FADT.flags & ACPI_FADT_S4_RTC_WAKE)
-			printk(PREFIX "RTC can wake from S4\n");
-
-
-		dev->platform_data = &rtc_info;
-
-		/* RTC always wakes from S1/S2/S3, and often S4/STD */
-		device_init_wakeup(dev, 1);
-
-		put_device(dev);
-	} else
-		DBG("RTC unavailable?\n");
-	return 0;
-}
-module_init(acpi_rtc_init);
-
-#endif
diff --git a/drivers/block/ataflop.c b/drivers/block/ataflop.c
index 49f2741..432cf40 100644
--- a/drivers/block/ataflop.c
+++ b/drivers/block/ataflop.c
@@ -1882,10 +1882,6 @@
 		/* Amiga, Mac, ... don't have Atari-compatible floppy :-) */
 		return -ENODEV;
 
-	if (MACH_IS_HADES)
-		/* Hades doesn't have Atari-compatible floppy */
-		return -ENODEV;
-
 	if (register_blkdev(FLOPPY_MAJOR,"fd"))
 		return -EBUSY;
 
diff --git a/drivers/char/vt.c b/drivers/char/vt.c
index 57029fe..a0f7ffb 100644
--- a/drivers/char/vt.c
+++ b/drivers/char/vt.c
@@ -301,7 +301,7 @@
 	d = (unsigned short *)(vc->vc_origin + vc->vc_size_row * t);
 	s = (unsigned short *)(vc->vc_origin + vc->vc_size_row * (t + nr));
 	scr_memmovew(d, s, (b - t - nr) * vc->vc_size_row);
-	scr_memsetw(d + (b - t - nr) * vc->vc_cols, vc->vc_scrl_erase_char,
+	scr_memsetw(d + (b - t - nr) * vc->vc_cols, vc->vc_video_erase_char,
 		    vc->vc_size_row * nr);
 }
 
@@ -319,7 +319,7 @@
 	s = (unsigned short *)(vc->vc_origin + vc->vc_size_row * t);
 	step = vc->vc_cols * nr;
 	scr_memmovew(s + step, s, (b - t - nr) * vc->vc_size_row);
-	scr_memsetw(s, vc->vc_scrl_erase_char, 2 * step);
+	scr_memsetw(s, vc->vc_video_erase_char, 2 * step);
 }
 
 static void do_update_region(struct vc_data *vc, unsigned long start, int count)
@@ -434,7 +434,6 @@
 	              vc->vc_blink, vc->vc_underline,
 	              vc->vc_reverse ^ vc->vc_decscnm, vc->vc_italic);
 	vc->vc_video_erase_char = (build_attr(vc, vc->vc_color, 1, vc->vc_blink, 0, vc->vc_decscnm, 0) << 8) | ' ';
-	vc->vc_scrl_erase_char = (build_attr(vc, vc->vc_def_color, 1, false, false, vc->vc_decscnm, false) << 8) | ' ';
 }
 
 /* Note: inverting the screen twice should revert to the original state */
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index cacf89e..da64108 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -17,6 +17,25 @@
 	tristate "Generic HID support"
 	depends on INPUT
 	default y
+        select HID_A4TECH if !EMBEDDED
+        select HID_APPLE if !EMBEDDED
+        select HID_BELKIN if !EMBEDDED
+        select HID_BRIGHT if !EMBEDDED
+        select HID_CHERRY if !EMBEDDED
+        select HID_CHICONY if !EMBEDDED
+        select HID_CYPRESS if !EMBEDDED
+        select HID_DELL if !EMBEDDED
+        select HID_EZKEY if !EMBEDDED
+        select HID_GYRATION if !EMBEDDED
+        select HID_LOGITECH if !EMBEDDED
+        select HID_MICROSOFT if !EMBEDDED
+        select HID_MONTEREY if !EMBEDDED
+        select HID_PANTHERLORD if !EMBEDDED
+        select HID_PETALYNX if !EMBEDDED
+        select HID_SAMSUNG if !EMBEDDED
+        select HID_SONY if !EMBEDDED
+        select HID_SUNPLUS if !EMBEDDED
+
 	---help---
 	  A human interface device (HID) is a type of computer device that
 	  interacts directly with and takes input from humans. The term "HID"
@@ -67,4 +86,206 @@
 
 source "drivers/hid/usbhid/Kconfig"
 
+menu "Special HID drivers"
+	depends on HID
+
+config HID_COMPAT
+	bool "Load all HID drivers on hid core load"
+	default y
+	---help---
+	Compatible option for older userspace. If you have system without udev
+	support of module loading through aliases and also old
+	module-init-tools which can't handle hid bus, choose Y here. Otherwise
+	say N. If you say N and your userspace is old enough, the only
+	functionality you lose is modules autoloading.
+
+	If unsure, say Y.
+
+config HID_A4TECH
+	tristate "A4 tech"
+	default m
+	depends on USB_HID
+	---help---
+	Support for A4 tech X5 and WOP-35 / Trust 450L mice.
+
+config HID_APPLE
+	tristate "Apple"
+	default m
+	depends on (USB_HID || BT_HIDP)
+	---help---
+	Support for some Apple devices which less or more break
+	HID specification.
+
+	Say Y here if you want support for the special keys (Fn, Numlock) on
+	Apple iBooks, PowerBooks, MacBooks, MacBook Pros and aluminum USB
+	keyboards.
+
+	If unsure, say M.
+
+config HID_BELKIN
+	tristate "Belkin"
+	default m
+	depends on USB_HID
+	---help---
+	Support for Belkin Flip KVM and Wireless keyboard.
+
+config HID_BRIGHT
+	tristate "Bright"
+	default m
+	depends on USB_HID
+	---help---
+	Support for Bright ABNT-2 keyboard.
+
+config HID_CHERRY
+	tristate "Cherry"
+	default m
+	depends on USB_HID
+	---help---
+	Support for Cherry Cymotion.
+
+config HID_CHICONY
+	tristate "Chicony"
+	default m
+	depends on USB_HID
+	---help---
+	Support for Chicony Tactical pad.
+
+config HID_CYPRESS
+	tristate "Cypress"
+	default m
+	depends on USB_HID
+	---help---
+	Support for Cypress mouse and barcodes.
+
+config HID_DELL
+	tristate "Dell"
+	default m
+	depends on USB_HID
+	---help---
+	Support for Dell W7658.
+
+config HID_EZKEY
+	tristate "Ezkey"
+	default m
+	depends on USB_HID
+	---help---
+	Support for Ezkey mouse and barcodes.
+
+config HID_GYRATION
+	tristate "Gyration"
+	default m
+	depends on USB_HID
+	---help---
+	Support for Gyration remote.
+
+config HID_LOGITECH
+	tristate "Logitech"
+	default m
+	depends on USB_HID
+	---help---
+	Support for some Logitech devices which breaks less or more
+	HID specification.
+
+config LOGITECH_FF
+	bool "Logitech force feedback"
+	depends on HID_LOGITECH
+	select INPUT_FF_MEMLESS
+	help
+	  Say Y here if you have one of these devices:
+	  - Logitech WingMan Cordless RumblePad
+	  - Logitech WingMan Cordless RumblePad 2
+	  - Logitech WingMan Force 3D
+	  - Logitech Formula Force EX
+	  - Logitech MOMO Force wheel
+
+	  and if you want to enable force feedback for them.
+	  Note: if you say N here, this device will still be supported, but without
+	  force feedback.
+
+config LOGIRUMBLEPAD2_FF
+	bool "Logitech Rumblepad 2 force feedback"
+	depends on HID_LOGITECH
+	select INPUT_FF_MEMLESS
+	help
+	  Say Y here if you want to enable force feedback support for Logitech
+	  Rumblepad 2 devices.
+
+config HID_MICROSOFT
+	tristate "Microsoft"
+	default m
+	depends on USB_HID
+	---help---
+	Support for some Microsoft devices which breaks less or more
+	HID specification.
+
+config HID_MONTEREY
+	tristate "Monterey"
+	default m
+	depends on USB_HID
+	---help---
+	Support for Monterey Genius KB29E.
+
+config HID_PANTHERLORD
+	tristate "Pantherlord devices support"
+	default m
+	depends on USB_HID
+	---help---
+	Support for PantherLord/GreenAsia based device support.
+
+
+config PANTHERLORD_FF
+	bool "Pantherlord force feedback support"
+	depends on HID_PANTHERLORD
+	select INPUT_FF_MEMLESS
+	help
+	  Say Y here if you have a PantherLord/GreenAsia based game controller
+	  or adapter and want to enable force feedback support for it.
+
+config HID_PETALYNX
+	tristate "Petalynx"
+	default m
+	depends on USB_HID
+	---help---
+	Support for Petalynx Maxter remote.
+
+config HID_SAMSUNG
+	tristate "Samsung"
+	default m
+	depends on USB_HID
+	---help---
+	Support for Samsung IR remote.
+
+config HID_SONY
+	tristate "Sony"
+	default m
+	depends on USB_HID
+	---help---
+	Support for Sony PS3 controller.
+
+config HID_SUNPLUS
+	tristate "Sunplus"
+	default m
+	depends on USB_HID
+	---help---
+	Support for Sunplus WDesktop input device.
+
+config THRUSTMASTER_FF
+	tristate "ThrustMaster devices support"
+	default m
+	depends on USB_HID
+	select INPUT_FF_MEMLESS
+	help
+	  Say Y here if you have a THRUSTMASTER FireStore Dual Power 2 or
+	  a THRUSTMASTER Ferrari GT Rumble Force or Force Feedback Wheel.
+
+config ZEROPLUS_FF
+	tristate "Zeroplus based game controller support"
+	default m
+	depends on USB_HID
+	select INPUT_FF_MEMLESS
+	help
+	  Say Y here if you have a Zeroplus based game controller.
+
+endmenu
+
 endif # HID_SUPPORT
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index 275dc52..b09e43e 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -1,13 +1,46 @@
 #
 # Makefile for the HID driver
 #
-hid-objs			:= hid-core.o hid-input.o hid-input-quirks.o
+hid-objs			:= hid-core.o hid-input.o
 
 obj-$(CONFIG_HID)		+= hid.o
 
 hid-$(CONFIG_HID_DEBUG)		+= hid-debug.o
 hid-$(CONFIG_HIDRAW)		+= hidraw.o
 
+ifdef CONFIG_HID_COMPAT
+obj-m				+= hid-dummy.o
+endif
+
+hid-logitech-objs		:= hid-lg.o
+ifdef CONFIG_LOGITECH_FF
+	hid-logitech-objs	+= hid-lgff.o
+endif
+ifdef CONFIG_LOGIRUMBLEPAD2_FF
+	hid-logitech-objs	+= hid-lg2ff.o
+endif
+
+obj-$(CONFIG_HID_A4TECH)	+= hid-a4tech.o
+obj-$(CONFIG_HID_APPLE)		+= hid-apple.o
+obj-$(CONFIG_HID_BELKIN)	+= hid-belkin.o
+obj-$(CONFIG_HID_BRIGHT)	+= hid-bright.o
+obj-$(CONFIG_HID_CHERRY)	+= hid-cherry.o
+obj-$(CONFIG_HID_CHICONY)	+= hid-chicony.o
+obj-$(CONFIG_HID_CYPRESS)	+= hid-cypress.o
+obj-$(CONFIG_HID_DELL)		+= hid-dell.o
+obj-$(CONFIG_HID_EZKEY)		+= hid-ezkey.o
+obj-$(CONFIG_HID_GYRATION)	+= hid-gyration.o
+obj-$(CONFIG_HID_LOGITECH)	+= hid-logitech.o
+obj-$(CONFIG_HID_MICROSOFT)	+= hid-microsoft.o
+obj-$(CONFIG_HID_MONTEREY)	+= hid-monterey.o
+obj-$(CONFIG_HID_PANTHERLORD)	+= hid-pl.o
+obj-$(CONFIG_HID_PETALYNX)	+= hid-petalynx.o
+obj-$(CONFIG_HID_SAMSUNG)	+= hid-samsung.o
+obj-$(CONFIG_HID_SONY)		+= hid-sony.o
+obj-$(CONFIG_HID_SUNPLUS)	+= hid-sunplus.o
+obj-$(CONFIG_THRUSTMASTER_FF)	+= hid-tmff.o
+obj-$(CONFIG_ZEROPLUS_FF)	+= hid-zpff.o
+
 obj-$(CONFIG_USB_HID)		+= usbhid/
 obj-$(CONFIG_USB_MOUSE)		+= usbhid/
 obj-$(CONFIG_USB_KBD)		+= usbhid/
diff --git a/drivers/hid/hid-a4tech.c b/drivers/hid/hid-a4tech.c
new file mode 100644
index 0000000..ebca00e
--- /dev/null
+++ b/drivers/hid/hid-a4tech.c
@@ -0,0 +1,162 @@
+/*
+ *  HID driver for some a4tech "special" devices
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby
+ */
+
+/*
+ * 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/device.h>
+#include <linux/input.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+#define A4_2WHEEL_MOUSE_HACK_7	0x01
+#define A4_2WHEEL_MOUSE_HACK_B8	0x02
+
+struct a4tech_sc {
+	unsigned long quirks;
+	unsigned int hw_wheel;
+	__s32 delayed_value;
+};
+
+static int a4_input_mapped(struct hid_device *hdev, struct hid_input *hi,
+		struct hid_field *field, struct hid_usage *usage,
+		unsigned long **bit, int *max)
+{
+	struct a4tech_sc *a4 = hid_get_drvdata(hdev);
+
+	if (usage->type == EV_REL && usage->code == REL_WHEEL)
+		set_bit(REL_HWHEEL, *bit);
+
+	if ((a4->quirks & A4_2WHEEL_MOUSE_HACK_7) && usage->hid == 0x00090007)
+		return -1;
+
+	return 0;
+}
+
+static int a4_event(struct hid_device *hdev, struct hid_field *field,
+		struct hid_usage *usage, __s32 value)
+{
+	struct a4tech_sc *a4 = hid_get_drvdata(hdev);
+	struct input_dev *input;
+
+	if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput ||
+			!usage->type)
+		return 0;
+
+	input = field->hidinput->input;
+
+	if (a4->quirks & A4_2WHEEL_MOUSE_HACK_B8) {
+		if (usage->type == EV_REL && usage->code == REL_WHEEL) {
+			a4->delayed_value = value;
+			return 1;
+		}
+
+		if (usage->hid == 0x000100b8) {
+			input_event(input, EV_REL, value ? REL_HWHEEL :
+					REL_WHEEL, a4->delayed_value);
+			return 1;
+		}
+	}
+
+	if ((a4->quirks & A4_2WHEEL_MOUSE_HACK_7) && usage->hid == 0x00090007) {
+		a4->hw_wheel = !!value;
+		return 1;
+	}
+
+	if (usage->code == REL_WHEEL && a4->hw_wheel) {
+		input_event(input, usage->type, REL_HWHEEL, value);
+		return 1;
+	}
+
+	return 0;
+}
+
+static int a4_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+	struct a4tech_sc *a4;
+	int ret;
+
+	a4 = kzalloc(sizeof(*a4), GFP_KERNEL);
+	if (a4 == NULL) {
+		dev_err(&hdev->dev, "can't alloc device descriptor\n");
+		ret = -ENOMEM;
+		goto err_free;
+	}
+
+	a4->quirks = id->driver_data;
+
+	hid_set_drvdata(hdev, a4);
+
+	ret = hid_parse(hdev);
+	if (ret) {
+		dev_err(&hdev->dev, "parse failed\n");
+		goto err_free;
+	}
+
+	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+	if (ret) {
+		dev_err(&hdev->dev, "hw start failed\n");
+		goto err_free;
+	}
+
+	return 0;
+err_free:
+	kfree(a4);
+	return ret;
+}
+
+static void a4_remove(struct hid_device *hdev)
+{
+	struct a4tech_sc *a4 = hid_get_drvdata(hdev);
+
+	hid_hw_stop(hdev);
+	kfree(a4);
+}
+
+static const struct hid_device_id a4_devices[] = {
+	{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU),
+		.driver_data = A4_2WHEEL_MOUSE_HACK_7 },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D),
+		.driver_data = A4_2WHEEL_MOUSE_HACK_B8 },
+	{ }
+};
+MODULE_DEVICE_TABLE(hid, a4_devices);
+
+static struct hid_driver a4_driver = {
+	.name = "a4tech",
+	.id_table = a4_devices,
+	.input_mapped = a4_input_mapped,
+	.event = a4_event,
+	.probe = a4_probe,
+	.remove = a4_remove,
+};
+
+static int a4_init(void)
+{
+	return hid_register_driver(&a4_driver);
+}
+
+static void a4_exit(void)
+{
+	hid_unregister_driver(&a4_driver);
+}
+
+module_init(a4_init);
+module_exit(a4_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(a4tech);
diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c
new file mode 100644
index 0000000..fd7f896
--- /dev/null
+++ b/drivers/hid/hid-apple.c
@@ -0,0 +1,484 @@
+/*
+ *  USB HID quirks support for Linux
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby <jirislaby@gmail.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/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+
+#include "hid-ids.h"
+
+#define APPLE_RDESC_JIS		0x0001
+#define APPLE_IGNORE_MOUSE	0x0002
+#define APPLE_HAS_FN		0x0004
+#define APPLE_HIDDEV		0x0008
+#define APPLE_ISO_KEYBOARD	0x0010
+#define APPLE_MIGHTYMOUSE	0x0020
+#define APPLE_INVERT_HWHEEL	0x0040
+#define APPLE_IGNORE_HIDINPUT	0x0080
+#define APPLE_NUMLOCK_EMULATION	0x0100
+
+#define APPLE_FLAG_FKEY		0x01
+
+static unsigned int fnmode = 1;
+module_param(fnmode, uint, 0644);
+MODULE_PARM_DESC(fnmode, "Mode of fn key on Apple keyboards (0 = disabled, "
+		"[1] = fkeyslast, 2 = fkeysfirst)");
+
+struct apple_sc {
+	unsigned long quirks;
+	unsigned int fn_on;
+	DECLARE_BITMAP(pressed_fn, KEY_CNT);
+	DECLARE_BITMAP(pressed_numlock, KEY_CNT);
+};
+
+struct apple_key_translation {
+	u16 from;
+	u16 to;
+	u8 flags;
+};
+
+static struct apple_key_translation apple_fn_keys[] = {
+	{ KEY_BACKSPACE, KEY_DELETE },
+	{ KEY_F1,	KEY_BRIGHTNESSDOWN, APPLE_FLAG_FKEY },
+	{ KEY_F2,	KEY_BRIGHTNESSUP,   APPLE_FLAG_FKEY },
+	{ KEY_F3,	KEY_FN_F5,          APPLE_FLAG_FKEY }, /* Exposé */
+	{ KEY_F4,	KEY_FN_F4,          APPLE_FLAG_FKEY }, /* Dashboard */
+	{ KEY_F5,	KEY_KBDILLUMDOWN,   APPLE_FLAG_FKEY },
+	{ KEY_F6,	KEY_KBDILLUMUP,     APPLE_FLAG_FKEY },
+	{ KEY_F7,	KEY_PREVIOUSSONG,   APPLE_FLAG_FKEY },
+	{ KEY_F8,	KEY_PLAYPAUSE,      APPLE_FLAG_FKEY },
+	{ KEY_F9,	KEY_NEXTSONG,       APPLE_FLAG_FKEY },
+	{ KEY_F10,	KEY_MUTE,           APPLE_FLAG_FKEY },
+	{ KEY_F11,	KEY_VOLUMEDOWN,     APPLE_FLAG_FKEY },
+	{ KEY_F12,	KEY_VOLUMEUP,       APPLE_FLAG_FKEY },
+	{ KEY_UP,	KEY_PAGEUP },
+	{ KEY_DOWN,	KEY_PAGEDOWN },
+	{ KEY_LEFT,	KEY_HOME },
+	{ KEY_RIGHT,	KEY_END },
+	{ }
+};
+
+static struct apple_key_translation powerbook_fn_keys[] = {
+	{ KEY_BACKSPACE, KEY_DELETE },
+	{ KEY_F1,	KEY_BRIGHTNESSDOWN,     APPLE_FLAG_FKEY },
+	{ KEY_F2,	KEY_BRIGHTNESSUP,       APPLE_FLAG_FKEY },
+	{ KEY_F3,	KEY_MUTE,               APPLE_FLAG_FKEY },
+	{ KEY_F4,	KEY_VOLUMEDOWN,         APPLE_FLAG_FKEY },
+	{ KEY_F5,	KEY_VOLUMEUP,           APPLE_FLAG_FKEY },
+	{ KEY_F6,	KEY_NUMLOCK,            APPLE_FLAG_FKEY },
+	{ KEY_F7,	KEY_SWITCHVIDEOMODE,    APPLE_FLAG_FKEY },
+	{ KEY_F8,	KEY_KBDILLUMTOGGLE,     APPLE_FLAG_FKEY },
+	{ KEY_F9,	KEY_KBDILLUMDOWN,       APPLE_FLAG_FKEY },
+	{ KEY_F10,	KEY_KBDILLUMUP,         APPLE_FLAG_FKEY },
+	{ KEY_UP,	KEY_PAGEUP },
+	{ KEY_DOWN,	KEY_PAGEDOWN },
+	{ KEY_LEFT,	KEY_HOME },
+	{ KEY_RIGHT,	KEY_END },
+	{ }
+};
+
+static struct apple_key_translation powerbook_numlock_keys[] = {
+	{ KEY_J,	KEY_KP1 },
+	{ KEY_K,	KEY_KP2 },
+	{ KEY_L,	KEY_KP3 },
+	{ KEY_U,	KEY_KP4 },
+	{ KEY_I,	KEY_KP5 },
+	{ KEY_O,	KEY_KP6 },
+	{ KEY_7,	KEY_KP7 },
+	{ KEY_8,	KEY_KP8 },
+	{ KEY_9,	KEY_KP9 },
+	{ KEY_M,	KEY_KP0 },
+	{ KEY_DOT,	KEY_KPDOT },
+	{ KEY_SLASH,	KEY_KPPLUS },
+	{ KEY_SEMICOLON, KEY_KPMINUS },
+	{ KEY_P,	KEY_KPASTERISK },
+	{ KEY_MINUS,	KEY_KPEQUAL },
+	{ KEY_0,	KEY_KPSLASH },
+	{ KEY_F6,	KEY_NUMLOCK },
+	{ KEY_KPENTER,	KEY_KPENTER },
+	{ KEY_BACKSPACE, KEY_BACKSPACE },
+	{ }
+};
+
+static struct apple_key_translation apple_iso_keyboard[] = {
+	{ KEY_GRAVE,	KEY_102ND },
+	{ KEY_102ND,	KEY_GRAVE },
+	{ }
+};
+
+static struct apple_key_translation *apple_find_translation(
+		struct apple_key_translation *table, u16 from)
+{
+	struct apple_key_translation *trans;
+
+	/* Look for the translation */
+	for (trans = table; trans->from; trans++)
+		if (trans->from == from)
+			return trans;
+
+	return NULL;
+}
+
+static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input,
+		struct hid_usage *usage, __s32 value)
+{
+	struct apple_sc *asc = hid_get_drvdata(hid);
+	struct apple_key_translation *trans;
+
+	if (usage->code == KEY_FN) {
+		asc->fn_on = !!value;
+		input_event(input, usage->type, usage->code, value);
+		return 1;
+	}
+
+	if (fnmode) {
+		int do_translate;
+
+		trans = apple_find_translation((hid->product < 0x220 ||
+					hid->product >= 0x300) ?
+					powerbook_fn_keys : apple_fn_keys,
+					usage->code);
+		if (trans) {
+			if (test_bit(usage->code, asc->pressed_fn))
+				do_translate = 1;
+			else if (trans->flags & APPLE_FLAG_FKEY)
+				do_translate = (fnmode == 2 && asc->fn_on) ||
+					(fnmode == 1 && !asc->fn_on);
+			else
+				do_translate = asc->fn_on;
+
+			if (do_translate) {
+				if (value)
+					set_bit(usage->code, asc->pressed_fn);
+				else
+					clear_bit(usage->code, asc->pressed_fn);
+
+				input_event(input, usage->type, trans->to,
+						value);
+
+				return 1;
+			}
+		}
+
+		if (asc->quirks & APPLE_NUMLOCK_EMULATION &&
+				(test_bit(usage->code, asc->pressed_numlock) ||
+				test_bit(LED_NUML, input->led))) {
+			trans = apple_find_translation(powerbook_numlock_keys,
+					usage->code);
+
+			if (trans) {
+				if (value)
+					set_bit(usage->code,
+							asc->pressed_numlock);
+				else
+					clear_bit(usage->code,
+							asc->pressed_numlock);
+
+				input_event(input, usage->type, trans->to,
+						value);
+			}
+
+			return 1;
+		}
+	}
+
+	if (asc->quirks & APPLE_ISO_KEYBOARD) {
+		trans = apple_find_translation(apple_iso_keyboard, usage->code);
+		if (trans) {
+			input_event(input, usage->type, trans->to, value);
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+static int apple_event(struct hid_device *hdev, struct hid_field *field,
+		struct hid_usage *usage, __s32 value)
+{
+	struct apple_sc *asc = hid_get_drvdata(hdev);
+
+	if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput ||
+			!usage->type)
+		return 0;
+
+	if ((asc->quirks & APPLE_INVERT_HWHEEL) &&
+			usage->code == REL_HWHEEL) {
+		input_event(field->hidinput->input, usage->type, usage->code,
+				-value);
+		return 1;
+	}
+
+	if ((asc->quirks & APPLE_HAS_FN) &&
+			hidinput_apple_event(hdev, field->hidinput->input,
+				usage, value))
+		return 1;
+
+
+	return 0;
+}
+
+/*
+ * MacBook JIS keyboard has wrong logical maximum
+ */
+static void apple_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+		unsigned int rsize)
+{
+	struct apple_sc *asc = hid_get_drvdata(hdev);
+
+	if ((asc->quirks & APPLE_RDESC_JIS) && rsize >= 60 &&
+			rdesc[53] == 0x65 && rdesc[59] == 0x65) {
+		dev_info(&hdev->dev, "fixing up MacBook JIS keyboard report "
+				"descriptor\n");
+		rdesc[53] = rdesc[59] = 0xe7;
+	}
+}
+
+static void apple_setup_input(struct input_dev *input)
+{
+	struct apple_key_translation *trans;
+
+	set_bit(KEY_NUMLOCK, input->keybit);
+
+	/* Enable all needed keys */
+	for (trans = apple_fn_keys; trans->from; trans++)
+		set_bit(trans->to, input->keybit);
+
+	for (trans = powerbook_fn_keys; trans->from; trans++)
+		set_bit(trans->to, input->keybit);
+
+	for (trans = powerbook_numlock_keys; trans->from; trans++)
+		set_bit(trans->to, input->keybit);
+
+	for (trans = apple_iso_keyboard; trans->from; trans++)
+		set_bit(trans->to, input->keybit);
+}
+
+static int apple_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+		struct hid_field *field, struct hid_usage *usage,
+		unsigned long **bit, int *max)
+{
+	if (usage->hid == (HID_UP_CUSTOM | 0x0003)) {
+		/* The fn key on Apple USB keyboards */
+		set_bit(EV_REP, hi->input->evbit);
+		hid_map_usage_clear(hi, usage, bit, max, EV_KEY, KEY_FN);
+		apple_setup_input(hi->input);
+		return 1;
+	}
+
+	/* we want the hid layer to go through standard path (set and ignore) */
+	return 0;
+}
+
+static int apple_input_mapped(struct hid_device *hdev, struct hid_input *hi,
+		struct hid_field *field, struct hid_usage *usage,
+		unsigned long **bit, int *max)
+{
+	struct apple_sc *asc = hid_get_drvdata(hdev);
+
+	if (asc->quirks & APPLE_MIGHTYMOUSE) {
+		if (usage->hid == HID_GD_Z)
+			hid_map_usage(hi, usage, bit, max, EV_REL, REL_HWHEEL);
+		else if (usage->code == BTN_1)
+			hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_2);
+		else if (usage->code == BTN_2)
+			hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_1);
+	}
+
+	return 0;
+}
+
+static int apple_probe(struct hid_device *hdev,
+		const struct hid_device_id *id)
+{
+	unsigned long quirks = id->driver_data;
+	struct apple_sc *asc;
+	unsigned int connect_mask = HID_CONNECT_DEFAULT;
+	int ret;
+
+	/* return something else or move to hid layer? device will reside
+	   allocated */
+	if (id->bus == BUS_USB && (quirks & APPLE_IGNORE_MOUSE) &&
+			to_usb_interface(hdev->dev.parent)->cur_altsetting->
+			desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE)
+		return -ENODEV;
+
+	asc = kzalloc(sizeof(*asc), GFP_KERNEL);
+	if (asc == NULL) {
+		dev_err(&hdev->dev, "can't alloc apple descriptor\n");
+		return -ENOMEM;
+	}
+
+	asc->quirks = quirks;
+
+	hid_set_drvdata(hdev, asc);
+
+	ret = hid_parse(hdev);
+	if (ret) {
+		dev_err(&hdev->dev, "parse failed\n");
+		goto err_free;
+	}
+
+	if (quirks & APPLE_HIDDEV)
+		connect_mask |= HID_CONNECT_HIDDEV_FORCE;
+	if (quirks & APPLE_IGNORE_HIDINPUT)
+		connect_mask &= ~HID_CONNECT_HIDINPUT;
+
+	ret = hid_hw_start(hdev, connect_mask);
+	if (ret) {
+		dev_err(&hdev->dev, "hw start failed\n");
+		goto err_free;
+	}
+
+	return 0;
+err_free:
+	kfree(asc);
+	return ret;
+}
+
+static void apple_remove(struct hid_device *hdev)
+{
+	hid_hw_stop(hdev);
+	kfree(hid_get_drvdata(hdev));
+}
+
+static const struct hid_device_id apple_devices[] = {
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL),
+		.driver_data = APPLE_HIDDEV | APPLE_IGNORE_HIDINPUT },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4),
+		.driver_data = APPLE_HIDDEV | APPLE_IGNORE_HIDINPUT },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE),
+		.driver_data = APPLE_MIGHTYMOUSE | APPLE_INVERT_HWHEEL },
+
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI),
+		.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+			APPLE_IGNORE_MOUSE },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ISO),
+		.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+			APPLE_IGNORE_MOUSE },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ANSI),
+		.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+			APPLE_IGNORE_MOUSE },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ISO),
+		.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+			APPLE_IGNORE_MOUSE | APPLE_ISO_KEYBOARD },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_JIS),
+		.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+			APPLE_IGNORE_MOUSE },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ANSI),
+		.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+			APPLE_IGNORE_MOUSE },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ISO),
+		.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+			APPLE_IGNORE_MOUSE | APPLE_ISO_KEYBOARD },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_JIS),
+		.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+			APPLE_IGNORE_MOUSE | APPLE_RDESC_JIS },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ANSI),
+		.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+			APPLE_IGNORE_MOUSE },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ISO),
+		.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+			APPLE_IGNORE_MOUSE | APPLE_ISO_KEYBOARD },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS),
+		.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+			APPLE_IGNORE_MOUSE | APPLE_RDESC_JIS},
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ANSI),
+		.driver_data = APPLE_HAS_FN },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ISO),
+		.driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_JIS),
+		.driver_data = APPLE_HAS_FN },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ANSI),
+		.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+			APPLE_IGNORE_MOUSE },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ISO),
+		.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+			APPLE_IGNORE_MOUSE },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_JIS),
+		.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+			APPLE_IGNORE_MOUSE | APPLE_RDESC_JIS },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI),
+		.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO),
+		.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+			APPLE_ISO_KEYBOARD },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS),
+		.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ANSI),
+		.driver_data = APPLE_HAS_FN | APPLE_IGNORE_MOUSE },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ISO),
+		.driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD |
+			APPLE_IGNORE_MOUSE },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_JIS),
+		.driver_data = APPLE_HAS_FN | APPLE_IGNORE_MOUSE | APPLE_RDESC_JIS },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI),
+		.driver_data = APPLE_HAS_FN | APPLE_IGNORE_MOUSE },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ISO),
+		.driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD |
+			APPLE_IGNORE_MOUSE },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_JIS),
+		.driver_data = APPLE_HAS_FN | APPLE_IGNORE_MOUSE | APPLE_RDESC_JIS },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY),
+		.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+			APPLE_IGNORE_MOUSE },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY),
+		.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+			APPLE_IGNORE_MOUSE },
+
+	/* Apple wireless Mighty Mouse */
+	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, 0x030c),
+		.driver_data = APPLE_MIGHTYMOUSE | APPLE_INVERT_HWHEEL },
+
+	{ }
+};
+MODULE_DEVICE_TABLE(hid, apple_devices);
+
+static struct hid_driver apple_driver = {
+	.name = "apple",
+	.id_table = apple_devices,
+	.report_fixup = apple_report_fixup,
+	.probe = apple_probe,
+	.remove = apple_remove,
+	.event = apple_event,
+	.input_mapping = apple_input_mapping,
+	.input_mapped = apple_input_mapped,
+};
+
+static int apple_init(void)
+{
+	int ret;
+
+	ret = hid_register_driver(&apple_driver);
+	if (ret)
+		printk(KERN_ERR "can't register apple driver\n");
+
+	return ret;
+}
+
+static void apple_exit(void)
+{
+	hid_unregister_driver(&apple_driver);
+}
+
+module_init(apple_init);
+module_exit(apple_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(apple);
diff --git a/drivers/hid/hid-belkin.c b/drivers/hid/hid-belkin.c
new file mode 100644
index 0000000..12c8a9b
--- /dev/null
+++ b/drivers/hid/hid-belkin.c
@@ -0,0 +1,105 @@
+/*
+ *  HID driver for some belkin "special" devices
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby
+ */
+
+/*
+ * 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/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+#define BELKIN_HIDDEV	0x01
+#define BELKIN_WKBD	0x02
+
+#define belkin_map_key_clear(c)	hid_map_usage_clear(hi, usage, bit, max, \
+					EV_KEY, (c))
+static int belkin_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+		struct hid_field *field, struct hid_usage *usage,
+		unsigned long **bit, int *max)
+{
+	unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
+
+	if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER ||
+			!(quirks & BELKIN_WKBD))
+		return 0;
+
+	switch (usage->hid & HID_USAGE) {
+	case 0x03a: belkin_map_key_clear(KEY_SOUND);		break;
+	case 0x03b: belkin_map_key_clear(KEY_CAMERA);		break;
+	case 0x03c: belkin_map_key_clear(KEY_DOCUMENTS);	break;
+	default:
+		return 0;
+	}
+	return 1;
+}
+
+static int belkin_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+	unsigned long quirks = id->driver_data;
+	int ret;
+
+	hid_set_drvdata(hdev, (void *)quirks);
+
+	ret = hid_parse(hdev);
+	if (ret) {
+		dev_err(&hdev->dev, "parse failed\n");
+		goto err_free;
+	}
+
+	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT |
+		((quirks & BELKIN_HIDDEV) ? HID_CONNECT_HIDDEV_FORCE : 0));
+	if (ret) {
+		dev_err(&hdev->dev, "hw start failed\n");
+		goto err_free;
+	}
+
+	return 0;
+err_free:
+	return ret;
+}
+
+static const struct hid_device_id belkin_devices[] = {
+	{ HID_USB_DEVICE(USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM),
+		.driver_data = BELKIN_HIDDEV },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LABTEC, USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD),
+		.driver_data = BELKIN_WKBD },
+	{ }
+};
+MODULE_DEVICE_TABLE(hid, belkin_devices);
+
+static struct hid_driver belkin_driver = {
+	.name = "belkin",
+	.id_table = belkin_devices,
+	.input_mapping = belkin_input_mapping,
+	.probe = belkin_probe,
+};
+
+static int belkin_init(void)
+{
+	return hid_register_driver(&belkin_driver);
+}
+
+static void belkin_exit(void)
+{
+	hid_unregister_driver(&belkin_driver);
+}
+
+module_init(belkin_init);
+module_exit(belkin_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(belkin);
diff --git a/drivers/hid/hid-bright.c b/drivers/hid/hid-bright.c
new file mode 100644
index 0000000..38517a1
--- /dev/null
+++ b/drivers/hid/hid-bright.c
@@ -0,0 +1,71 @@
+/*
+ *  HID driver for some bright "special" devices
+ *
+ *  Copyright (c) 2008 Mauro Carvalho Chehab <mchehab@redhat.com>
+ *
+ * Based on hid-dell driver
+ */
+
+/*
+ * 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/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+static int bright_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+	int ret;
+
+	ret = hid_parse(hdev);
+	if (ret) {
+		dev_err(&hdev->dev, "parse failed\n");
+		goto err_free;
+	}
+
+	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+	if (ret) {
+		dev_err(&hdev->dev, "hw start failed\n");
+		goto err_free;
+	}
+
+	usbhid_set_leds(hdev);
+
+	return 0;
+err_free:
+	return ret;
+}
+
+static const struct hid_device_id bright_devices[] = {
+	{ HID_USB_DEVICE(USB_VENDOR_ID_BRIGHT, USB_DEVICE_ID_BRIGHT_ABNT2) },
+	{ }
+};
+MODULE_DEVICE_TABLE(hid, bright_devices);
+
+static struct hid_driver bright_driver = {
+	.name = "bright",
+	.id_table = bright_devices,
+	.probe = bright_probe,
+};
+
+static int bright_init(void)
+{
+	return hid_register_driver(&bright_driver);
+}
+
+static void bright_exit(void)
+{
+	hid_unregister_driver(&bright_driver);
+}
+
+module_init(bright_init);
+module_exit(bright_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(bright);
diff --git a/drivers/hid/hid-cherry.c b/drivers/hid/hid-cherry.c
new file mode 100644
index 0000000..b833b97
--- /dev/null
+++ b/drivers/hid/hid-cherry.c
@@ -0,0 +1,87 @@
+/*
+ *  HID driver for some cherry "special" devices
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby
+ */
+
+/*
+ * 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/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+/*
+ * Cherry Cymotion keyboard have an invalid HID report descriptor,
+ * that needs fixing before we can parse it.
+ */
+static void ch_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+		unsigned int rsize)
+{
+	if (rsize >= 17 && rdesc[11] == 0x3c && rdesc[12] == 0x02) {
+		dev_info(&hdev->dev, "fixing up Cherry Cymotion report "
+				"descriptor\n");
+		rdesc[11] = rdesc[16] = 0xff;
+		rdesc[12] = rdesc[17] = 0x03;
+	}
+}
+
+#define ch_map_key_clear(c)	hid_map_usage_clear(hi, usage, bit, max, \
+					EV_KEY, (c))
+static int ch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+		struct hid_field *field, struct hid_usage *usage,
+		unsigned long **bit, int *max)
+{
+	if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
+		return 0;
+
+	switch (usage->hid & HID_USAGE) {
+	case 0x301: ch_map_key_clear(KEY_PROG1);	break;
+	case 0x302: ch_map_key_clear(KEY_PROG2);	break;
+	case 0x303: ch_map_key_clear(KEY_PROG3);	break;
+	default:
+		return 0;
+	}
+
+	return 1;
+}
+
+static const struct hid_device_id ch_devices[] = {
+	{ HID_USB_DEVICE(USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION) },
+	{ }
+};
+MODULE_DEVICE_TABLE(hid, ch_devices);
+
+static struct hid_driver ch_driver = {
+	.name = "cherry",
+	.id_table = ch_devices,
+	.report_fixup = ch_report_fixup,
+	.input_mapping = ch_input_mapping,
+};
+
+static int ch_init(void)
+{
+	return hid_register_driver(&ch_driver);
+}
+
+static void ch_exit(void)
+{
+	hid_unregister_driver(&ch_driver);
+}
+
+module_init(ch_init);
+module_exit(ch_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(cherry);
diff --git a/drivers/hid/hid-chicony.c b/drivers/hid/hid-chicony.c
new file mode 100644
index 0000000..a54d409
--- /dev/null
+++ b/drivers/hid/hid-chicony.c
@@ -0,0 +1,80 @@
+/*
+ *  HID driver for some chicony "special" devices
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby
+ */
+
+/*
+ * 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/device.h>
+#include <linux/input.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+#define ch_map_key_clear(c)	hid_map_usage_clear(hi, usage, bit, max, \
+					EV_KEY, (c))
+static int ch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+		struct hid_field *field, struct hid_usage *usage,
+		unsigned long **bit, int *max)
+{
+	if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR)
+		return 0;
+
+	set_bit(EV_REP, hi->input->evbit);
+	switch (usage->hid & HID_USAGE) {
+	case 0xff01: ch_map_key_clear(BTN_1);	break;
+	case 0xff02: ch_map_key_clear(BTN_2);	break;
+	case 0xff03: ch_map_key_clear(BTN_3);	break;
+	case 0xff04: ch_map_key_clear(BTN_4);	break;
+	case 0xff05: ch_map_key_clear(BTN_5);	break;
+	case 0xff06: ch_map_key_clear(BTN_6);	break;
+	case 0xff07: ch_map_key_clear(BTN_7);	break;
+	case 0xff08: ch_map_key_clear(BTN_8);	break;
+	case 0xff09: ch_map_key_clear(BTN_9);	break;
+	case 0xff0a: ch_map_key_clear(BTN_A);	break;
+	case 0xff0b: ch_map_key_clear(BTN_B);	break;
+	default:
+		return 0;
+	}
+	return 1;
+}
+
+static const struct hid_device_id ch_devices[] = {
+	{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_TACTICAL_PAD) },
+	{ }
+};
+MODULE_DEVICE_TABLE(hid, ch_devices);
+
+static struct hid_driver ch_driver = {
+	.name = "chicony",
+	.id_table = ch_devices,
+	.input_mapping = ch_input_mapping,
+};
+
+static int ch_init(void)
+{
+	return hid_register_driver(&ch_driver);
+}
+
+static void ch_exit(void)
+{
+	hid_unregister_driver(&ch_driver);
+}
+
+module_init(ch_init);
+module_exit(ch_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(chicony);
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 426ac5a..8a7d9db 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -33,6 +33,8 @@
 #include <linux/hid-debug.h>
 #include <linux/hidraw.h>
 
+#include "hid-ids.h"
+
 /*
  * Version Information
  */
@@ -268,9 +270,9 @@
 static u32 item_udata(struct hid_item *item)
 {
 	switch (item->size) {
-		case 1: return item->data.u8;
-		case 2: return item->data.u16;
-		case 4: return item->data.u32;
+	case 1: return item->data.u8;
+	case 2: return item->data.u16;
+	case 4: return item->data.u32;
 	}
 	return 0;
 }
@@ -278,9 +280,9 @@
 static s32 item_sdata(struct hid_item *item)
 {
 	switch (item->size) {
-		case 1: return item->data.s8;
-		case 2: return item->data.s16;
-		case 4: return item->data.s32;
+	case 1: return item->data.s8;
+	case 2: return item->data.s16;
+	case 4: return item->data.s32;
 	}
 	return 0;
 }
@@ -292,87 +294,91 @@
 static int hid_parser_global(struct hid_parser *parser, struct hid_item *item)
 {
 	switch (item->tag) {
+	case HID_GLOBAL_ITEM_TAG_PUSH:
 
-		case HID_GLOBAL_ITEM_TAG_PUSH:
-
-			if (parser->global_stack_ptr == HID_GLOBAL_STACK_SIZE) {
-				dbg_hid("global enviroment stack overflow\n");
-				return -1;
-			}
-
-			memcpy(parser->global_stack + parser->global_stack_ptr++,
-				&parser->global, sizeof(struct hid_global));
-			return 0;
-
-		case HID_GLOBAL_ITEM_TAG_POP:
-
-			if (!parser->global_stack_ptr) {
-				dbg_hid("global enviroment stack underflow\n");
-				return -1;
-			}
-
-			memcpy(&parser->global, parser->global_stack + --parser->global_stack_ptr,
-				sizeof(struct hid_global));
-			return 0;
-
-		case HID_GLOBAL_ITEM_TAG_USAGE_PAGE:
-			parser->global.usage_page = item_udata(item);
-			return 0;
-
-		case HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM:
-			parser->global.logical_minimum = item_sdata(item);
-			return 0;
-
-		case HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM:
-			if (parser->global.logical_minimum < 0)
-				parser->global.logical_maximum = item_sdata(item);
-			else
-				parser->global.logical_maximum = item_udata(item);
-			return 0;
-
-		case HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM:
-			parser->global.physical_minimum = item_sdata(item);
-			return 0;
-
-		case HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM:
-			if (parser->global.physical_minimum < 0)
-				parser->global.physical_maximum = item_sdata(item);
-			else
-				parser->global.physical_maximum = item_udata(item);
-			return 0;
-
-		case HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT:
-			parser->global.unit_exponent = item_sdata(item);
-			return 0;
-
-		case HID_GLOBAL_ITEM_TAG_UNIT:
-			parser->global.unit = item_udata(item);
-			return 0;
-
-		case HID_GLOBAL_ITEM_TAG_REPORT_SIZE:
-			if ((parser->global.report_size = item_udata(item)) > 32) {
-				dbg_hid("invalid report_size %d\n", parser->global.report_size);
-				return -1;
-			}
-			return 0;
-
-		case HID_GLOBAL_ITEM_TAG_REPORT_COUNT:
-			if ((parser->global.report_count = item_udata(item)) > HID_MAX_USAGES) {
-				dbg_hid("invalid report_count %d\n", parser->global.report_count);
-				return -1;
-			}
-			return 0;
-
-		case HID_GLOBAL_ITEM_TAG_REPORT_ID:
-			if ((parser->global.report_id = item_udata(item)) == 0) {
-				dbg_hid("report_id 0 is invalid\n");
-				return -1;
-			}
-			return 0;
-
-		default:
-			dbg_hid("unknown global tag 0x%x\n", item->tag);
+		if (parser->global_stack_ptr == HID_GLOBAL_STACK_SIZE) {
+			dbg_hid("global enviroment stack overflow\n");
 			return -1;
+		}
+
+		memcpy(parser->global_stack + parser->global_stack_ptr++,
+			&parser->global, sizeof(struct hid_global));
+		return 0;
+
+	case HID_GLOBAL_ITEM_TAG_POP:
+
+		if (!parser->global_stack_ptr) {
+			dbg_hid("global enviroment stack underflow\n");
+			return -1;
+		}
+
+		memcpy(&parser->global, parser->global_stack +
+			--parser->global_stack_ptr, sizeof(struct hid_global));
+		return 0;
+
+	case HID_GLOBAL_ITEM_TAG_USAGE_PAGE:
+		parser->global.usage_page = item_udata(item);
+		return 0;
+
+	case HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM:
+		parser->global.logical_minimum = item_sdata(item);
+		return 0;
+
+	case HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM:
+		if (parser->global.logical_minimum < 0)
+			parser->global.logical_maximum = item_sdata(item);
+		else
+			parser->global.logical_maximum = item_udata(item);
+		return 0;
+
+	case HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM:
+		parser->global.physical_minimum = item_sdata(item);
+		return 0;
+
+	case HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM:
+		if (parser->global.physical_minimum < 0)
+			parser->global.physical_maximum = item_sdata(item);
+		else
+			parser->global.physical_maximum = item_udata(item);
+		return 0;
+
+	case HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT:
+		parser->global.unit_exponent = item_sdata(item);
+		return 0;
+
+	case HID_GLOBAL_ITEM_TAG_UNIT:
+		parser->global.unit = item_udata(item);
+		return 0;
+
+	case HID_GLOBAL_ITEM_TAG_REPORT_SIZE:
+		parser->global.report_size = item_udata(item);
+		if (parser->global.report_size > 32) {
+			dbg_hid("invalid report_size %d\n",
+					parser->global.report_size);
+			return -1;
+		}
+		return 0;
+
+	case HID_GLOBAL_ITEM_TAG_REPORT_COUNT:
+		parser->global.report_count = item_udata(item);
+		if (parser->global.report_count > HID_MAX_USAGES) {
+			dbg_hid("invalid report_count %d\n",
+					parser->global.report_count);
+			return -1;
+		}
+		return 0;
+
+	case HID_GLOBAL_ITEM_TAG_REPORT_ID:
+		parser->global.report_id = item_udata(item);
+		if (parser->global.report_id == 0) {
+			dbg_hid("report_id 0 is invalid\n");
+			return -1;
+		}
+		return 0;
+
+	default:
+		dbg_hid("unknown global tag 0x%x\n", item->tag);
+		return -1;
 	}
 }
 
@@ -393,77 +399,76 @@
 	data = item_udata(item);
 
 	switch (item->tag) {
+	case HID_LOCAL_ITEM_TAG_DELIMITER:
 
-		case HID_LOCAL_ITEM_TAG_DELIMITER:
-
-			if (data) {
-				/*
-				 * We treat items before the first delimiter
-				 * as global to all usage sets (branch 0).
-				 * In the moment we process only these global
-				 * items and the first delimiter set.
-				 */
-				if (parser->local.delimiter_depth != 0) {
-					dbg_hid("nested delimiters\n");
-					return -1;
-				}
-				parser->local.delimiter_depth++;
-				parser->local.delimiter_branch++;
-			} else {
-				if (parser->local.delimiter_depth < 1) {
-					dbg_hid("bogus close delimiter\n");
-					return -1;
-				}
-				parser->local.delimiter_depth--;
+		if (data) {
+			/*
+			 * We treat items before the first delimiter
+			 * as global to all usage sets (branch 0).
+			 * In the moment we process only these global
+			 * items and the first delimiter set.
+			 */
+			if (parser->local.delimiter_depth != 0) {
+				dbg_hid("nested delimiters\n");
+				return -1;
 			}
-			return 1;
-
-		case HID_LOCAL_ITEM_TAG_USAGE:
-
-			if (parser->local.delimiter_branch > 1) {
-				dbg_hid("alternative usage ignored\n");
-				return 0;
+			parser->local.delimiter_depth++;
+			parser->local.delimiter_branch++;
+		} else {
+			if (parser->local.delimiter_depth < 1) {
+				dbg_hid("bogus close delimiter\n");
+				return -1;
 			}
+			parser->local.delimiter_depth--;
+		}
+		return 1;
 
-			if (item->size <= 2)
-				data = (parser->global.usage_page << 16) + data;
+	case HID_LOCAL_ITEM_TAG_USAGE:
 
-			return hid_add_usage(parser, data);
-
-		case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM:
-
-			if (parser->local.delimiter_branch > 1) {
-				dbg_hid("alternative usage ignored\n");
-				return 0;
-			}
-
-			if (item->size <= 2)
-				data = (parser->global.usage_page << 16) + data;
-
-			parser->local.usage_minimum = data;
+		if (parser->local.delimiter_branch > 1) {
+			dbg_hid("alternative usage ignored\n");
 			return 0;
+		}
 
-		case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM:
+		if (item->size <= 2)
+			data = (parser->global.usage_page << 16) + data;
 
-			if (parser->local.delimiter_branch > 1) {
-				dbg_hid("alternative usage ignored\n");
-				return 0;
+		return hid_add_usage(parser, data);
+
+	case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM:
+
+		if (parser->local.delimiter_branch > 1) {
+			dbg_hid("alternative usage ignored\n");
+			return 0;
+		}
+
+		if (item->size <= 2)
+			data = (parser->global.usage_page << 16) + data;
+
+		parser->local.usage_minimum = data;
+		return 0;
+
+	case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM:
+
+		if (parser->local.delimiter_branch > 1) {
+			dbg_hid("alternative usage ignored\n");
+			return 0;
+		}
+
+		if (item->size <= 2)
+			data = (parser->global.usage_page << 16) + data;
+
+		for (n = parser->local.usage_minimum; n <= data; n++)
+			if (hid_add_usage(parser, n)) {
+				dbg_hid("hid_add_usage failed\n");
+				return -1;
 			}
+		return 0;
 
-			if (item->size <= 2)
-				data = (parser->global.usage_page << 16) + data;
+	default:
 
-			for (n = parser->local.usage_minimum; n <= data; n++)
-				if (hid_add_usage(parser, n)) {
-					dbg_hid("hid_add_usage failed\n");
-					return -1;
-				}
-			return 0;
-
-		default:
-
-			dbg_hid("unknown local item tag 0x%x\n", item->tag);
-			return 0;
+		dbg_hid("unknown local item tag 0x%x\n", item->tag);
+		return 0;
 	}
 	return 0;
 }
@@ -480,24 +485,24 @@
 	data = item_udata(item);
 
 	switch (item->tag) {
-		case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION:
-			ret = open_collection(parser, data & 0xff);
-			break;
-		case HID_MAIN_ITEM_TAG_END_COLLECTION:
-			ret = close_collection(parser);
-			break;
-		case HID_MAIN_ITEM_TAG_INPUT:
-			ret = hid_add_field(parser, HID_INPUT_REPORT, data);
-			break;
-		case HID_MAIN_ITEM_TAG_OUTPUT:
-			ret = hid_add_field(parser, HID_OUTPUT_REPORT, data);
-			break;
-		case HID_MAIN_ITEM_TAG_FEATURE:
-			ret = hid_add_field(parser, HID_FEATURE_REPORT, data);
-			break;
-		default:
-			dbg_hid("unknown main item tag 0x%x\n", item->tag);
-			ret = 0;
+	case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION:
+		ret = open_collection(parser, data & 0xff);
+		break;
+	case HID_MAIN_ITEM_TAG_END_COLLECTION:
+		ret = close_collection(parser);
+		break;
+	case HID_MAIN_ITEM_TAG_INPUT:
+		ret = hid_add_field(parser, HID_INPUT_REPORT, data);
+		break;
+	case HID_MAIN_ITEM_TAG_OUTPUT:
+		ret = hid_add_field(parser, HID_OUTPUT_REPORT, data);
+		break;
+	case HID_MAIN_ITEM_TAG_FEATURE:
+		ret = hid_add_field(parser, HID_FEATURE_REPORT, data);
+		break;
+	default:
+		dbg_hid("unknown main item tag 0x%x\n", item->tag);
+		ret = 0;
 	}
 
 	memset(&parser->local, 0, sizeof(parser->local));	/* Reset the local parser environment */
@@ -534,9 +539,10 @@
  * Free a device structure, all reports, and all fields.
  */
 
-void hid_free_device(struct hid_device *device)
+static void hid_device_release(struct device *dev)
 {
-	unsigned i,j;
+	struct hid_device *device = container_of(dev, struct hid_device, dev);
+	unsigned i, j;
 
 	for (i = 0; i < HID_REPORT_TYPES; i++) {
 		struct hid_report_enum *report_enum = device->report_enum + i;
@@ -552,7 +558,6 @@
 	kfree(device->collection);
 	kfree(device);
 }
-EXPORT_SYMBOL_GPL(hid_free_device);
 
 /*
  * Fetch a report description item from the data stream. We support long
@@ -593,47 +598,52 @@
 	item->size = b & 3;
 
 	switch (item->size) {
+	case 0:
+		return start;
 
-		case 0:
-			return start;
+	case 1:
+		if ((end - start) < 1)
+			return NULL;
+		item->data.u8 = *start++;
+		return start;
 
-		case 1:
-			if ((end - start) < 1)
-				return NULL;
-			item->data.u8 = *start++;
-			return start;
+	case 2:
+		if ((end - start) < 2)
+			return NULL;
+		item->data.u16 = get_unaligned_le16(start);
+		start = (__u8 *)((__le16 *)start + 1);
+		return start;
 
-		case 2:
-			if ((end - start) < 2)
-				return NULL;
-			item->data.u16 = get_unaligned_le16(start);
-			start = (__u8 *)((__le16 *)start + 1);
-			return start;
-
-		case 3:
-			item->size++;
-			if ((end - start) < 4)
-				return NULL;
-			item->data.u32 = get_unaligned_le32(start);
-			start = (__u8 *)((__le32 *)start + 1);
-			return start;
+	case 3:
+		item->size++;
+		if ((end - start) < 4)
+			return NULL;
+		item->data.u32 = get_unaligned_le32(start);
+		start = (__u8 *)((__le32 *)start + 1);
+		return start;
 	}
 
 	return NULL;
 }
 
-/*
+/**
+ * hid_parse_report - parse device report
+ *
+ * @device: hid device
+ * @start: report start
+ * @size: report size
+ *
  * Parse a report description into a hid_device structure. Reports are
  * enumerated, fields are attached to these reports.
+ * 0 returned on success, otherwise nonzero error value.
  */
-
-struct hid_device *hid_parse_report(__u8 *start, unsigned size)
+int hid_parse_report(struct hid_device *device, __u8 *start,
+		unsigned size)
 {
-	struct hid_device *device;
 	struct hid_parser *parser;
 	struct hid_item item;
 	__u8 *end;
-	unsigned i;
+	int ret;
 	static int (*dispatch_type[])(struct hid_parser *parser,
 				      struct hid_item *item) = {
 		hid_parser_main,
@@ -642,76 +652,57 @@
 		hid_parser_reserved
 	};
 
-	if (!(device = kzalloc(sizeof(struct hid_device), GFP_KERNEL)))
-		return NULL;
+	if (device->driver->report_fixup)
+		device->driver->report_fixup(device, start, size);
 
-	if (!(device->collection = kzalloc(sizeof(struct hid_collection) *
-				   HID_DEFAULT_NUM_COLLECTIONS, GFP_KERNEL))) {
-		kfree(device);
-		return NULL;
-	}
-	device->collection_size = HID_DEFAULT_NUM_COLLECTIONS;
-
-	for (i = 0; i < HID_REPORT_TYPES; i++)
-		INIT_LIST_HEAD(&device->report_enum[i].report_list);
-
-	if (!(device->rdesc = kmalloc(size, GFP_KERNEL))) {
-		kfree(device->collection);
-		kfree(device);
-		return NULL;
-	}
+	device->rdesc = kmalloc(size, GFP_KERNEL);
+	if (device->rdesc == NULL)
+		return -ENOMEM;
 	memcpy(device->rdesc, start, size);
 	device->rsize = size;
 
-	if (!(parser = vmalloc(sizeof(struct hid_parser)))) {
-		kfree(device->rdesc);
-		kfree(device->collection);
-		kfree(device);
-		return NULL;
+	parser = vmalloc(sizeof(struct hid_parser));
+	if (!parser) {
+		ret = -ENOMEM;
+		goto err;
 	}
+
 	memset(parser, 0, sizeof(struct hid_parser));
 	parser->device = device;
 
 	end = start + size;
+	ret = -EINVAL;
 	while ((start = fetch_item(start, end, &item)) != NULL) {
 
 		if (item.format != HID_ITEM_FORMAT_SHORT) {
 			dbg_hid("unexpected long global item\n");
-			hid_free_device(device);
-			vfree(parser);
-			return NULL;
+			goto err;
 		}
 
 		if (dispatch_type[item.type](parser, &item)) {
 			dbg_hid("item %u %u %u %u parsing failed\n",
 				item.format, (unsigned)item.size, (unsigned)item.type, (unsigned)item.tag);
-			hid_free_device(device);
-			vfree(parser);
-			return NULL;
+			goto err;
 		}
 
 		if (start == end) {
 			if (parser->collection_stack_ptr) {
 				dbg_hid("unbalanced collection at end of report description\n");
-				hid_free_device(device);
-				vfree(parser);
-				return NULL;
+				goto err;
 			}
 			if (parser->local.delimiter_depth) {
 				dbg_hid("unbalanced delimiter at end of report description\n");
-				hid_free_device(device);
-				vfree(parser);
-				return NULL;
+				goto err;
 			}
 			vfree(parser);
-			return device;
+			return 0;
 		}
 	}
 
 	dbg_hid("item fetching failed at offset %d\n", (int)(end - start));
-	hid_free_device(device);
+err:
 	vfree(parser);
-	return NULL;
+	return ret;
 }
 EXPORT_SYMBOL_GPL(hid_parse_report);
 
@@ -724,9 +715,9 @@
 static s32 snto32(__u32 value, unsigned n)
 {
 	switch (n) {
-		case 8:  return ((__s8)value);
-		case 16: return ((__s16)value);
-		case 32: return ((__s32)value);
+	case 8:  return ((__s8)value);
+	case 16: return ((__s16)value);
+	case 32: return ((__s32)value);
 	}
 	return value & (1 << (n - 1)) ? value | (-1 << n) : value;
 }
@@ -815,9 +806,73 @@
 	return -1;
 }
 
-static void hid_process_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value, int interrupt)
+/**
+ * hid_match_report - check if driver's raw_event should be called
+ *
+ * @hid: hid device
+ * @report_type: type to match against
+ *
+ * compare hid->driver->report_table->report_type to report->type
+ */
+static int hid_match_report(struct hid_device *hid, struct hid_report *report)
 {
+	const struct hid_report_id *id = hid->driver->report_table;
+
+	if (!id) /* NULL means all */
+		return 1;
+
+	for (; id->report_type != HID_TERMINATOR; id++)
+		if (id->report_type == HID_ANY_ID ||
+				id->report_type == report->type)
+			return 1;
+	return 0;
+}
+
+/**
+ * hid_match_usage - check if driver's event should be called
+ *
+ * @hid: hid device
+ * @usage: usage to match against
+ *
+ * compare hid->driver->usage_table->usage_{type,code} to
+ * usage->usage_{type,code}
+ */
+static int hid_match_usage(struct hid_device *hid, struct hid_usage *usage)
+{
+	const struct hid_usage_id *id = hid->driver->usage_table;
+
+	if (!id) /* NULL means all */
+		return 1;
+
+	for (; id->usage_type != HID_ANY_ID - 1; id++)
+		if ((id->usage_hid == HID_ANY_ID ||
+				id->usage_hid == usage->hid) &&
+				(id->usage_type == HID_ANY_ID ||
+				id->usage_type == usage->type) &&
+				(id->usage_code == HID_ANY_ID ||
+				 id->usage_code == usage->code))
+			return 1;
+	return 0;
+}
+
+static void hid_process_event(struct hid_device *hid, struct hid_field *field,
+		struct hid_usage *usage, __s32 value, int interrupt)
+{
+	struct hid_driver *hdrv = hid->driver;
+	int ret;
+
 	hid_dump_input(usage, value);
+
+	if (hdrv && hdrv->event && hid_match_usage(hid, usage)) {
+		ret = hdrv->event(hid, field, usage, value);
+		if (ret != 0) {
+			if (ret < 0)
+				dbg_hid("%s's event failed with %d\n",
+						hdrv->name, ret);
+			return;
+		}
+	}
+
 	if (hid->claimed & HID_CLAIMED_INPUT)
 		hidinput_hid_event(hid, field, usage, value);
 	if (hid->claimed & HID_CLAIMED_HIDDEV && interrupt && hid->hiddev_hid_event)
@@ -946,44 +1001,47 @@
 }
 EXPORT_SYMBOL_GPL(hid_set_field);
 
-int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int interrupt)
+static struct hid_report *hid_get_report(struct hid_report_enum *report_enum,
+		const u8 *data)
+{
+	struct hid_report *report;
+	unsigned int n = 0;	/* Normally report number is 0 */
+
+	/* Device uses numbered reports, data[0] is report number */
+	if (report_enum->numbered)
+		n = *data;
+
+	report = report_enum->report_id_hash[n];
+	if (report == NULL)
+		dbg_hid("undefined report_id %u received\n", n);
+
+	return report;
+}
+
+void hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size,
+		int interrupt)
 {
 	struct hid_report_enum *report_enum = hid->report_enum + type;
 	struct hid_report *report;
-	int n, rsize, i;
+	unsigned int a;
+	int rsize, csize = size;
+	u8 *cdata = data;
 
-	if (!hid)
-		return -ENODEV;
+	report = hid_get_report(report_enum, data);
+	if (!report)
+		return;
 
-	if (!size) {
-		dbg_hid("empty report\n");
-		return -1;
-	}
-
-	dbg_hid("report (size %u) (%snumbered)\n", size, report_enum->numbered ? "" : "un");
-
-	n = 0;                          /* Normally report number is 0 */
-	if (report_enum->numbered) {    /* Device uses numbered reports, data[0] is report number */
-		n = *data++;
-		size--;
-	}
-
-	/* dump the report */
-	dbg_hid("report %d (size %u) = ", n, size);
-	for (i = 0; i < size; i++)
-		dbg_hid_line(" %02x", data[i]);
-	dbg_hid_line("\n");
-
-	if (!(report = report_enum->report_id_hash[n])) {
-		dbg_hid("undefined report_id %d received\n", n);
-		return -1;
+	if (report_enum->numbered) {
+		cdata++;
+		csize--;
 	}
 
 	rsize = ((report->size - 1) >> 3) + 1;
 
-	if (size < rsize) {
-		dbg_hid("report %d is too short, (%d < %d)\n", report->id, size, rsize);
-		memset(data + size, 0, rsize - size);
+	if (csize < rsize) {
+		dbg_hid("report %d is too short, (%d < %d)\n", report->id,
+				csize, rsize);
+		memset(cdata + csize, 0, rsize - csize);
 	}
 
 	if ((hid->claimed & HID_CLAIMED_HIDDEV) && hid->hiddev_report_event)
@@ -996,24 +1054,661 @@
 			hidraw_report_event(hid, data, size);
 	}
 
-	for (n = 0; n < report->maxfield; n++)
-		hid_input_field(hid, report->field[n], data, interrupt);
+	for (a = 0; a < report->maxfield; a++)
+		hid_input_field(hid, report->field[a], cdata, interrupt);
 
 	if (hid->claimed & HID_CLAIMED_INPUT)
 		hidinput_report_event(hid, report);
+}
+EXPORT_SYMBOL_GPL(hid_report_raw_event);
+
+/**
+ * hid_input_report - report data from lower layer (usb, bt...)
+ *
+ * @hid: hid device
+ * @type: HID report type (HID_*_REPORT)
+ * @data: report contents
+ * @size: size of data parameter
+ * @interrupt: called from atomic?
+ *
+ * This is data entry for lower layers.
+ */
+int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int interrupt)
+{
+	struct hid_report_enum *report_enum = hid->report_enum + type;
+	struct hid_driver *hdrv = hid->driver;
+	struct hid_report *report;
+	unsigned int i;
+	int ret;
+
+	if (!hid || !hid->driver)
+		return -ENODEV;
+
+	if (!size) {
+		dbg_hid("empty report\n");
+		return -1;
+	}
+
+	dbg_hid("report (size %u) (%snumbered)\n", size, report_enum->numbered ? "" : "un");
+
+	report = hid_get_report(report_enum, data);
+	if (!report)
+		return -1;
+
+	/* dump the report */
+	dbg_hid("report %d (size %u) = ", report->id, size);
+	for (i = 0; i < size; i++)
+		dbg_hid_line(" %02x", data[i]);
+	dbg_hid_line("\n");
+
+	if (hdrv && hdrv->raw_event && hid_match_report(hid, report)) {
+		ret = hdrv->raw_event(hid, report, data, size);
+		if (ret != 0)
+			return ret < 0 ? ret : 0;
+	}
+
+	hid_report_raw_event(hid, type, data, size, interrupt);
 
 	return 0;
 }
 EXPORT_SYMBOL_GPL(hid_input_report);
 
+static bool hid_match_one_id(struct hid_device *hdev,
+		const struct hid_device_id *id)
+{
+	return id->bus == hdev->bus &&
+		(id->vendor == HID_ANY_ID || id->vendor == hdev->vendor) &&
+		(id->product == HID_ANY_ID || id->product == hdev->product);
+}
+
+static const struct hid_device_id *hid_match_id(struct hid_device *hdev,
+		const struct hid_device_id *id)
+{
+	for (; id->bus; id++)
+		if (hid_match_one_id(hdev, id))
+			return id;
+
+	return NULL;
+}
+
+static const struct hid_device_id hid_hiddev_list[] = {
+	{ HID_USB_DEVICE(USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS1) },
+	{ }
+};
+
+static bool hid_hiddev(struct hid_device *hdev)
+{
+	return !!hid_match_id(hdev, hid_hiddev_list);
+}
+
+int hid_connect(struct hid_device *hdev, unsigned int connect_mask)
+{
+	static const char *types[] = { "Device", "Pointer", "Mouse", "Device",
+		"Joystick", "Gamepad", "Keyboard", "Keypad",
+		"Multi-Axis Controller"
+	};
+	const char *type, *bus;
+	char buf[64];
+	unsigned int i;
+	int len;
+
+	if (hdev->bus != BUS_USB)
+		connect_mask &= ~HID_CONNECT_HIDDEV;
+	if (hid_hiddev(hdev))
+		connect_mask |= HID_CONNECT_HIDDEV_FORCE;
+
+	if ((connect_mask & HID_CONNECT_HIDINPUT) && !hidinput_connect(hdev,
+				connect_mask & HID_CONNECT_HIDINPUT_FORCE))
+		hdev->claimed |= HID_CLAIMED_INPUT;
+	if ((connect_mask & HID_CONNECT_HIDDEV) && hdev->hiddev_connect &&
+			!hdev->hiddev_connect(hdev,
+				connect_mask & HID_CONNECT_HIDDEV_FORCE))
+		hdev->claimed |= HID_CLAIMED_HIDDEV;
+	if ((connect_mask & HID_CONNECT_HIDRAW) && !hidraw_connect(hdev))
+		hdev->claimed |= HID_CLAIMED_HIDRAW;
+
+	if (!hdev->claimed) {
+		dev_err(&hdev->dev, "claimed by neither input, hiddev nor "
+				"hidraw\n");
+		return -ENODEV;
+	}
+
+	if ((hdev->claimed & HID_CLAIMED_INPUT) &&
+			(connect_mask & HID_CONNECT_FF) && hdev->ff_init)
+		hdev->ff_init(hdev);
+
+	len = 0;
+	if (hdev->claimed & HID_CLAIMED_INPUT)
+		len += sprintf(buf + len, "input");
+	if (hdev->claimed & HID_CLAIMED_HIDDEV)
+		len += sprintf(buf + len, "%shiddev%d", len ? "," : "",
+				hdev->minor);
+	if (hdev->claimed & HID_CLAIMED_HIDRAW)
+		len += sprintf(buf + len, "%shidraw%d", len ? "," : "",
+				((struct hidraw *)hdev->hidraw)->minor);
+
+	type = "Device";
+	for (i = 0; i < hdev->maxcollection; i++) {
+		struct hid_collection *col = &hdev->collection[i];
+		if (col->type == HID_COLLECTION_APPLICATION &&
+		   (col->usage & HID_USAGE_PAGE) == HID_UP_GENDESK &&
+		   (col->usage & 0xffff) < ARRAY_SIZE(types)) {
+			type = types[col->usage & 0xffff];
+			break;
+		}
+	}
+
+	switch (hdev->bus) {
+	case BUS_USB:
+		bus = "USB";
+		break;
+	case BUS_BLUETOOTH:
+		bus = "BLUETOOTH";
+		break;
+	default:
+		bus = "<UNKNOWN>";
+	}
+
+	dev_info(&hdev->dev, "%s: %s HID v%x.%02x %s [%s] on %s\n",
+			buf, bus, hdev->version >> 8, hdev->version & 0xff,
+			type, hdev->name, hdev->phys);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(hid_connect);
+
+static const struct hid_device_id hid_blacklist[] = {
+	{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ISO) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ANSI) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ISO) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_JIS) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ANSI) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ISO) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_JIS) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ANSI) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ISO) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ANSI) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ISO) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_JIS) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ANSI) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ISO) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_JIS) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ANSI) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ISO) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_JIS) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ISO) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_JIS) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_AVERMEDIA, USB_DEVICE_ID_AVER_FM_MR800) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_BRIGHT, USB_DEVICE_ID_BRIGHT_ABNT2) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_TACTICAL_PAD) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_W7658) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_SK8115) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_EZKEY, USB_DEVICE_ID_BTC_8193) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LABTEC, USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_KBD) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_LX3) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_V150) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_LK6K) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_USB) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) },
+
+	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, 0x030c) },
+	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT) },
+	{ }
+};
+
+static int hid_bus_match(struct device *dev, struct device_driver *drv)
+{
+	struct hid_driver *hdrv = container_of(drv, struct hid_driver, driver);
+	struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+
+	if (!hid_match_id(hdev, hdrv->id_table))
+		return 0;
+
+	/* generic wants all non-blacklisted */
+	if (!strncmp(hdrv->name, "generic-", 8))
+		return !hid_match_id(hdev, hid_blacklist);
+
+	return 1;
+}
+
+static int hid_device_probe(struct device *dev)
+{
+	struct hid_driver *hdrv = container_of(dev->driver,
+			struct hid_driver, driver);
+	struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+	const struct hid_device_id *id;
+	int ret = 0;
+
+	if (!hdev->driver) {
+		id = hid_match_id(hdev, hdrv->id_table);
+		if (id == NULL)
+			return -ENODEV;
+
+		hdev->driver = hdrv;
+		if (hdrv->probe) {
+			ret = hdrv->probe(hdev, id);
+		} else { /* default probe */
+			ret = hid_parse(hdev);
+			if (!ret)
+				ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+		}
+		if (ret)
+			hdev->driver = NULL;
+	}
+	return ret;
+}
+
+static int hid_device_remove(struct device *dev)
+{
+	struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+	struct hid_driver *hdrv = hdev->driver;
+
+	if (hdrv) {
+		if (hdrv->remove)
+			hdrv->remove(hdev);
+		else /* default remove */
+			hid_hw_stop(hdev);
+		hdev->driver = NULL;
+	}
+
+	return 0;
+}
+
+static int hid_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+	struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+
+	if (add_uevent_var(env, "HID_ID=%04X:%08X:%08X",
+			hdev->bus, hdev->vendor, hdev->product))
+		return -ENOMEM;
+
+	if (add_uevent_var(env, "HID_NAME=%s", hdev->name))
+		return -ENOMEM;
+
+	if (add_uevent_var(env, "HID_PHYS=%s", hdev->phys))
+		return -ENOMEM;
+
+	if (add_uevent_var(env, "HID_UNIQ=%s", hdev->uniq))
+		return -ENOMEM;
+
+	if (add_uevent_var(env, "MODALIAS=hid:b%04Xv%08Xp%08X",
+			hdev->bus, hdev->vendor, hdev->product))
+		return -ENOMEM;
+
+	return 0;
+}
+
+static struct bus_type hid_bus_type = {
+	.name		= "hid",
+	.match		= hid_bus_match,
+	.probe		= hid_device_probe,
+	.remove		= hid_device_remove,
+	.uevent		= hid_uevent,
+};
+
+static const struct hid_device_id hid_ignore_list[] = {
+	{ HID_USB_DEVICE(USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_FLAIR) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_302) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_ADS_TECH, USB_DEVICE_ID_ADS_TECH_RADIO_SI470X) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_01) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_10) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_20) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_21) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_22) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_23) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_AIRCABLE, USB_DEVICE_ID_AIRCABLE1) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_ALCOR, USB_DEVICE_ID_ALCOR_USBRS232) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUS_LCM)},
+	{ HID_USB_DEVICE(USB_VENDOR_ID_BERKSHIRE, USB_DEVICE_ID_BERKSHIRE_PCWD) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_CIDC, 0x0103) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_CYGNAL, USB_DEVICE_ID_CYGNAL_RADIO_SI470X) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_CMEDIA, USB_DEVICE_ID_CM109) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_HIDCOM) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_ULTRAMOUSE) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EARTHMATE) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EM_LT20) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0001) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0002) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0003) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0004) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_4_PHIDGETSERVO_30) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_1_PHIDGETSERVO_30) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_0_4_IF_KIT) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_16_16_IF_KIT) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_8_8_8_IF_KIT) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_7_IF_KIT) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_8_IF_KIT) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_PHIDGET_MOTORCONTROL) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_SUPER_Q2) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_GOGOPEN) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_PENPOWER) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GREENASIA, 0x0003) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GRETAGMACBETH, USB_DEVICE_ID_GRETAGMACBETH_HUEY) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_POWERMATE) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_SOUNDKNOB) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_90) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_100) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_101) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_103) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_104) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_105) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_106) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_107) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_108) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_200) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_201) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_202) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_203) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_204) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_205) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_206) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_207) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_300) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_301) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_302) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_303) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_304) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_305) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_306) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_307) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_308) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_309) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_400) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_401) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_402) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_403) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_404) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_405) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_500) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_501) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_502) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_503) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_504) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1000) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1001) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1002) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1003) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1004) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1005) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1006) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1007) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_IMATION, USB_DEVICE_ID_DISC_STAKKA) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_KBGEAR, USB_DEVICE_ID_KBGEAR_JAMSTUDIO) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_GPEN_560) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_CASSY) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POCKETCASSY) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOBILECASSY) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_JWM) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_DMMP) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_UMIP) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY1) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY2) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_VIDEOCOM) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_COM3LAB) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_TELEPORT) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_NETWORKANALYSER) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POWERCONTROL) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MACHINETEST) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1024LS) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1208LS) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICKIT1) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICKIT2) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR, USB_DEVICE_ID_N_S_HARMONY) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 20) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 30) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 100) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 108) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 118) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 200) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 300) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 400) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 500) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0001) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0002) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0003) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0004) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_SOUNDGRAPH, USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_TENX, USB_DEVICE_ID_TENX_IBUDDY1) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_TENX, USB_DEVICE_ID_TENX_IBUDDY2) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb651) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb654) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LABPRO) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_GOTEMP) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LCSPEC) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_WACOM, HID_ANY_ID) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_4_PHIDGETSERVO_20) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_8_8_4_IF_KIT) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0005) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0030) },
+	{ }
+};
+
+static bool hid_ignore(struct hid_device *hdev)
+{
+	switch (hdev->vendor) {
+	case USB_VENDOR_ID_CODEMERCS:
+		/* ignore all Code Mercenaries IOWarrior devices */
+		if (hdev->product >= USB_DEVICE_ID_CODEMERCS_IOW_FIRST &&
+				hdev->product <= USB_DEVICE_ID_CODEMERCS_IOW_LAST)
+			return true;
+		break;
+	case USB_VENDOR_ID_LOGITECH:
+		if (hdev->product >= USB_DEVICE_ID_LOGITECH_HARMONY_FIRST &&
+				hdev->product <= USB_DEVICE_ID_LOGITECH_HARMONY_LAST)
+			return true;
+		break;
+	}
+
+	return !!hid_match_id(hdev, hid_ignore_list);
+}
+
+int hid_add_device(struct hid_device *hdev)
+{
+	static atomic_t id = ATOMIC_INIT(0);
+	int ret;
+
+	if (WARN_ON(hdev->status & HID_STAT_ADDED))
+		return -EBUSY;
+
+	/* we need to kill them here, otherwise they will stay allocated to
+	 * wait for coming driver */
+	if (hid_ignore(hdev))
+		return -ENODEV;
+
+	/* XXX hack, any other cleaner solution < 20 bus_id bytes? */
+	sprintf(hdev->dev.bus_id, "%04X:%04X:%04X.%04X", hdev->bus,
+			hdev->vendor, hdev->product, atomic_inc_return(&id));
+
+	ret = device_add(&hdev->dev);
+	if (!ret)
+		hdev->status |= HID_STAT_ADDED;
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(hid_add_device);
+
+/**
+ * hid_allocate_device - allocate new hid device descriptor
+ *
+ * Allocate and initialize hid device, so that hid_destroy_device might be
+ * used to free it.
+ *
+ * New hid_device pointer is returned on success, otherwise ERR_PTR encoded
+ * error value.
+ */
+struct hid_device *hid_allocate_device(void)
+{
+	struct hid_device *hdev;
+	unsigned int i;
+	int ret = -ENOMEM;
+
+	hdev = kzalloc(sizeof(*hdev), GFP_KERNEL);
+	if (hdev == NULL)
+		return ERR_PTR(ret);
+
+	device_initialize(&hdev->dev);
+	hdev->dev.release = hid_device_release;
+	hdev->dev.bus = &hid_bus_type;
+
+	hdev->collection = kcalloc(HID_DEFAULT_NUM_COLLECTIONS,
+			sizeof(struct hid_collection), GFP_KERNEL);
+	if (hdev->collection == NULL)
+		goto err;
+	hdev->collection_size = HID_DEFAULT_NUM_COLLECTIONS;
+
+	for (i = 0; i < HID_REPORT_TYPES; i++)
+		INIT_LIST_HEAD(&hdev->report_enum[i].report_list);
+
+	return hdev;
+err:
+	put_device(&hdev->dev);
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(hid_allocate_device);
+
+static void hid_remove_device(struct hid_device *hdev)
+{
+	if (hdev->status & HID_STAT_ADDED) {
+		device_del(&hdev->dev);
+		hdev->status &= ~HID_STAT_ADDED;
+	}
+}
+
+/**
+ * hid_destroy_device - free previously allocated device
+ *
+ * @hdev: hid device
+ *
+ * If you allocate hid_device through hid_allocate_device, you should ever
+ * free by this function.
+ */
+void hid_destroy_device(struct hid_device *hdev)
+{
+	hid_remove_device(hdev);
+	put_device(&hdev->dev);
+}
+EXPORT_SYMBOL_GPL(hid_destroy_device);
+
+int __hid_register_driver(struct hid_driver *hdrv, struct module *owner,
+		const char *mod_name)
+{
+	hdrv->driver.name = hdrv->name;
+	hdrv->driver.bus = &hid_bus_type;
+	hdrv->driver.owner = owner;
+	hdrv->driver.mod_name = mod_name;
+
+	return driver_register(&hdrv->driver);
+}
+EXPORT_SYMBOL_GPL(__hid_register_driver);
+
+void hid_unregister_driver(struct hid_driver *hdrv)
+{
+	driver_unregister(&hdrv->driver);
+}
+EXPORT_SYMBOL_GPL(hid_unregister_driver);
+
+#ifdef CONFIG_HID_COMPAT
+static void hid_compat_load(struct work_struct *ws)
+{
+	request_module("hid-dummy");
+}
+static DECLARE_WORK(hid_compat_work, hid_compat_load);
+static struct workqueue_struct *hid_compat_wq;
+#endif
+
 static int __init hid_init(void)
 {
-	return hidraw_init();
+	int ret;
+
+	ret = bus_register(&hid_bus_type);
+	if (ret) {
+		printk(KERN_ERR "HID: can't register hid bus\n");
+		goto err;
+	}
+
+	ret = hidraw_init();
+	if (ret)
+		goto err_bus;
+
+#ifdef CONFIG_HID_COMPAT
+	hid_compat_wq = create_workqueue("hid_compat");
+	if (!hid_compat_wq) {
+		hidraw_exit();
+		goto err;
+	}
+	queue_work(hid_compat_wq, &hid_compat_work);
+#endif
+
+	return 0;
+err_bus:
+	bus_unregister(&hid_bus_type);
+err:
+	return ret;
 }
 
 static void __exit hid_exit(void)
 {
+#ifdef CONFIG_HID_COMPAT
+	destroy_workqueue(hid_compat_wq);
+#endif
 	hidraw_exit();
+	bus_unregister(&hid_bus_type);
 }
 
 module_init(hid_init);
diff --git a/drivers/hid/hid-cypress.c b/drivers/hid/hid-cypress.c
new file mode 100644
index 0000000..5d69d27
--- /dev/null
+++ b/drivers/hid/hid-cypress.c
@@ -0,0 +1,158 @@
+/*
+ *  HID driver for some cypress "special" devices
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby
+ */
+
+/*
+ * 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/device.h>
+#include <linux/hid.h>
+#include <linux/input.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+#define CP_RDESC_SWAPPED_MIN_MAX	0x01
+#define CP_2WHEEL_MOUSE_HACK		0x02
+#define CP_2WHEEL_MOUSE_HACK_ON		0x04
+
+/*
+ * Some USB barcode readers from cypress have usage min and usage max in
+ * the wrong order
+ */
+static void cp_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+		unsigned int rsize)
+{
+	unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
+	unsigned int i;
+
+	if (!(quirks & CP_RDESC_SWAPPED_MIN_MAX))
+		return;
+
+	for (i = 0; i < rsize - 4; i++)
+		if (rdesc[i] == 0x29 && rdesc[i + 2] == 0x19) {
+			__u8 tmp;
+
+			rdesc[i] = 0x19;
+			rdesc[i + 2] = 0x29;
+			tmp = rdesc[i + 3];
+			rdesc[i + 3] = rdesc[i + 1];
+			rdesc[i + 1] = tmp;
+		}
+}
+
+static int cp_input_mapped(struct hid_device *hdev, struct hid_input *hi,
+		struct hid_field *field, struct hid_usage *usage,
+		unsigned long **bit, int *max)
+{
+	unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
+
+	if (!(quirks & CP_2WHEEL_MOUSE_HACK))
+		return 0;
+
+	if (usage->type == EV_REL && usage->code == REL_WHEEL)
+		set_bit(REL_HWHEEL, *bit);
+	if (usage->hid == 0x00090005)
+		return -1;
+
+	return 0;
+}
+
+static int cp_event(struct hid_device *hdev, struct hid_field *field,
+		struct hid_usage *usage, __s32 value)
+{
+	unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
+
+	if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput ||
+			!usage->type || !(quirks & CP_2WHEEL_MOUSE_HACK))
+		return 0;
+
+	if (usage->hid == 0x00090005) {
+		if (value)
+			quirks |=  CP_2WHEEL_MOUSE_HACK_ON;
+		else
+			quirks &= ~CP_2WHEEL_MOUSE_HACK_ON;
+		hid_set_drvdata(hdev, (void *)quirks);
+		return 1;
+	}
+
+	if (usage->code == REL_WHEEL && (quirks & CP_2WHEEL_MOUSE_HACK_ON)) {
+		struct input_dev *input = field->hidinput->input;
+
+		input_event(input, usage->type, REL_HWHEEL, value);
+		return 1;
+	}
+
+	return 0;
+}
+
+static int cp_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+	unsigned long quirks = id->driver_data;
+	int ret;
+
+	hid_set_drvdata(hdev, (void *)quirks);
+
+	ret = hid_parse(hdev);
+	if (ret) {
+		dev_err(&hdev->dev, "parse failed\n");
+		goto err_free;
+	}
+
+	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+	if (ret) {
+		dev_err(&hdev->dev, "hw start failed\n");
+		goto err_free;
+	}
+
+	return 0;
+err_free:
+	return ret;
+}
+
+static const struct hid_device_id cp_devices[] = {
+	{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1),
+		.driver_data = CP_RDESC_SWAPPED_MIN_MAX },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2),
+		.driver_data = CP_RDESC_SWAPPED_MIN_MAX },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE),
+		.driver_data = CP_2WHEEL_MOUSE_HACK },
+	{ }
+};
+MODULE_DEVICE_TABLE(hid, cp_devices);
+
+static struct hid_driver cp_driver = {
+	.name = "cypress",
+	.id_table = cp_devices,
+	.report_fixup = cp_report_fixup,
+	.input_mapped = cp_input_mapped,
+	.event = cp_event,
+	.probe = cp_probe,
+};
+
+static int cp_init(void)
+{
+	return hid_register_driver(&cp_driver);
+}
+
+static void cp_exit(void)
+{
+	hid_unregister_driver(&cp_driver);
+}
+
+module_init(cp_init);
+module_exit(cp_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(cypress);
diff --git a/drivers/hid/hid-dell.c b/drivers/hid/hid-dell.c
new file mode 100644
index 0000000..1a0d0df
--- /dev/null
+++ b/drivers/hid/hid-dell.c
@@ -0,0 +1,75 @@
+/*
+ *  HID driver for some dell "special" devices
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby
+ */
+
+/*
+ * 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/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+static int dell_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+	int ret;
+
+	ret = hid_parse(hdev);
+	if (ret) {
+		dev_err(&hdev->dev, "parse failed\n");
+		goto err_free;
+	}
+
+	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+	if (ret) {
+		dev_err(&hdev->dev, "hw start failed\n");
+		goto err_free;
+	}
+
+	usbhid_set_leds(hdev);
+
+	return 0;
+err_free:
+	return ret;
+}
+
+static const struct hid_device_id dell_devices[] = {
+	{ HID_USB_DEVICE(USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_W7658) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_SK8115) },
+	{ }
+};
+MODULE_DEVICE_TABLE(hid, dell_devices);
+
+static struct hid_driver dell_driver = {
+	.name = "dell",
+	.id_table = dell_devices,
+	.probe = dell_probe,
+};
+
+static int dell_init(void)
+{
+	return hid_register_driver(&dell_driver);
+}
+
+static void dell_exit(void)
+{
+	hid_unregister_driver(&dell_driver);
+}
+
+module_init(dell_init);
+module_exit(dell_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(dell);
diff --git a/drivers/hid/hid-dummy.c b/drivers/hid/hid-dummy.c
new file mode 100644
index 0000000..e148f86
--- /dev/null
+++ b/drivers/hid/hid-dummy.c
@@ -0,0 +1,72 @@
+#include <linux/autoconf.h>
+#include <linux/module.h>
+#include <linux/hid.h>
+
+static int __init hid_dummy_init(void)
+{
+#ifdef CONFIG_HID_A4TECH_MODULE
+	HID_COMPAT_CALL_DRIVER(a4tech);
+#endif
+#ifdef CONFIG_HID_APPLE_MODULE
+	HID_COMPAT_CALL_DRIVER(apple);
+#endif
+#ifdef CONFIG_HID_BELKIN_MODULE
+	HID_COMPAT_CALL_DRIVER(belkin);
+#endif
+#ifdef CONFIG_HID_BRIGHT_MODULE
+	HID_COMPAT_CALL_DRIVER(bright);
+#endif
+#ifdef CONFIG_HID_CHERRY_MODULE
+	HID_COMPAT_CALL_DRIVER(cherry);
+#endif
+#ifdef CONFIG_HID_CHICONY_MODULE
+	HID_COMPAT_CALL_DRIVER(chicony);
+#endif
+#ifdef CONFIG_HID_CYPRESS_MODULE
+	HID_COMPAT_CALL_DRIVER(cypress);
+#endif
+#ifdef CONFIG_HID_DELL_MODULE
+	HID_COMPAT_CALL_DRIVER(dell);
+#endif
+#ifdef CONFIG_HID_EZKEY_MODULE
+	HID_COMPAT_CALL_DRIVER(ezkey);
+#endif
+#ifdef CONFIG_HID_GYRATION_MODULE
+	HID_COMPAT_CALL_DRIVER(gyration);
+#endif
+#ifdef CONFIG_HID_LOGITECH_MODULE
+	HID_COMPAT_CALL_DRIVER(logitech);
+#endif
+#ifdef CONFIG_HID_MICROSOFT_MODULE
+	HID_COMPAT_CALL_DRIVER(microsoft);
+#endif
+#ifdef CONFIG_HID_MONTEREY_MODULE
+	HID_COMPAT_CALL_DRIVER(monterey);
+#endif
+#ifdef CONFIG_HID_PANTHERLORD_MODULE
+	HID_COMPAT_CALL_DRIVER(pantherlord);
+#endif
+#ifdef CONFIG_HID_PETALYNX_MODULE
+	HID_COMPAT_CALL_DRIVER(petalynx);
+#endif
+#ifdef CONFIG_HID_SAMSUNG_MODULE
+	HID_COMPAT_CALL_DRIVER(samsung);
+#endif
+#ifdef CONFIG_HID_SONY_MODULE
+	HID_COMPAT_CALL_DRIVER(sony);
+#endif
+#ifdef CONFIG_HID_SUNPLUS_MODULE
+	HID_COMPAT_CALL_DRIVER(sunplus);
+#endif
+#ifdef CONFIG_THRUSTMASTER_FF_MODULE
+	HID_COMPAT_CALL_DRIVER(thrustmaster);
+#endif
+#ifdef CONFIG_ZEROPLUS_FF_MODULE
+	HID_COMPAT_CALL_DRIVER(zeroplus);
+#endif
+
+	return -EIO;
+}
+module_init(hid_dummy_init);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/hid/hid-ezkey.c b/drivers/hid/hid-ezkey.c
new file mode 100644
index 0000000..deb42f9
--- /dev/null
+++ b/drivers/hid/hid-ezkey.c
@@ -0,0 +1,95 @@
+/*
+ *  HID driver for some ezkey "special" devices
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby
+ */
+
+/*
+ * 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/device.h>
+#include <linux/input.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+#define ez_map_rel(c)	hid_map_usage(hi, usage, bit, max, EV_REL, (c))
+#define ez_map_key(c)	hid_map_usage(hi, usage, bit, max, EV_KEY, (c))
+
+static int ez_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+		struct hid_field *field, struct hid_usage *usage,
+		unsigned long **bit, int *max)
+{
+	if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
+		return 0;
+
+	switch (usage->hid & HID_USAGE) {
+	case 0x230: ez_map_key(BTN_MOUSE);	break;
+	case 0x231: ez_map_rel(REL_WHEEL);	break;
+	/*
+	 * this keyboard has a scrollwheel implemented in
+	 * totally broken way. We map this usage temporarily
+	 * to HWHEEL and handle it in the event quirk handler
+	 */
+	case 0x232: ez_map_rel(REL_HWHEEL);	break;
+	default:
+		return 0;
+	}
+	return 1;
+}
+
+static int ez_event(struct hid_device *hdev, struct hid_field *field,
+		struct hid_usage *usage, __s32 value)
+{
+	if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput ||
+			!usage->type)
+		return 0;
+
+	/* handle the temporary quirky mapping to HWHEEL */
+	if (usage->type == EV_REL && usage->code == REL_HWHEEL) {
+		struct input_dev *input = field->hidinput->input;
+		input_event(input, usage->type, REL_WHEEL, -value);
+		return 1;
+	}
+
+	return 0;
+}
+
+static const struct hid_device_id ez_devices[] = {
+	{ HID_USB_DEVICE(USB_VENDOR_ID_EZKEY, USB_DEVICE_ID_BTC_8193) },
+	{ }
+};
+MODULE_DEVICE_TABLE(hid, ez_devices);
+
+static struct hid_driver ez_driver = {
+	.name = "ezkey",
+	.id_table = ez_devices,
+	.input_mapping = ez_input_mapping,
+	.event = ez_event,
+};
+
+static int ez_init(void)
+{
+	return hid_register_driver(&ez_driver);
+}
+
+static void ez_exit(void)
+{
+	hid_unregister_driver(&ez_driver);
+}
+
+module_init(ez_init);
+module_exit(ez_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(ezkey);
diff --git a/drivers/hid/hid-gyration.c b/drivers/hid/hid-gyration.c
new file mode 100644
index 0000000..ac5120f
--- /dev/null
+++ b/drivers/hid/hid-gyration.c
@@ -0,0 +1,96 @@
+/*
+ *  HID driver for some gyration "special" devices
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby
+ */
+
+/*
+ * 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/device.h>
+#include <linux/input.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+#define gy_map_key_clear(c)	hid_map_usage_clear(hi, usage, bit, max, \
+					EV_KEY, (c))
+static int gyration_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+		struct hid_field *field, struct hid_usage *usage,
+		unsigned long **bit, int *max)
+{
+	if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR)
+		return 0;
+
+	set_bit(EV_REP, hi->input->evbit);
+	switch (usage->hid & HID_USAGE) {
+	/* Reported on Gyration MCE Remote */
+	case 0x00d: gy_map_key_clear(KEY_HOME);		break;
+	case 0x024: gy_map_key_clear(KEY_DVD);		break;
+	case 0x025: gy_map_key_clear(KEY_PVR);		break;
+	case 0x046: gy_map_key_clear(KEY_MEDIA);	break;
+	case 0x047: gy_map_key_clear(KEY_MP3);		break;
+	case 0x049: gy_map_key_clear(KEY_CAMERA);	break;
+	case 0x04a: gy_map_key_clear(KEY_VIDEO);	break;
+
+	default:
+		return 0;
+	}
+	return 1;
+}
+
+static int gyration_event(struct hid_device *hdev, struct hid_field *field,
+		struct hid_usage *usage, __s32 value)
+{
+	struct input_dev *input = field->hidinput->input;
+
+	if ((usage->hid & HID_USAGE_PAGE) == HID_UP_GENDESK &&
+			(usage->hid & 0xff) == 0x82) {
+		input_event(input, usage->type, usage->code, 1);
+		input_sync(input);
+		input_event(input, usage->type, usage->code, 0);
+		input_sync(input);
+		return 1;
+	}
+
+	return 0;
+}
+
+static const struct hid_device_id gyration_devices[] = {
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE) },
+	{ }
+};
+MODULE_DEVICE_TABLE(hid, gyration_devices);
+
+static struct hid_driver gyration_driver = {
+	.name = "gyration",
+	.id_table = gyration_devices,
+	.input_mapping = gyration_input_mapping,
+	.event = gyration_event,
+};
+
+static int gyration_init(void)
+{
+	return hid_register_driver(&gyration_driver);
+}
+
+static void gyration_exit(void)
+{
+	hid_unregister_driver(&gyration_driver);
+}
+
+module_init(gyration_init);
+module_exit(gyration_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(gyration);
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
new file mode 100644
index 0000000..aad9ed1
--- /dev/null
+++ b/drivers/hid/hid-ids.h
@@ -0,0 +1,404 @@
+/*
+ *  USB HID quirks support for Linux
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ */
+
+/*
+ * 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.
+ */
+
+#ifndef HID_IDS_H_FILE
+#define HID_IDS_H_FILE
+
+#define USB_VENDOR_ID_A4TECH		0x09da
+#define USB_DEVICE_ID_A4TECH_WCP32PU	0x0006
+#define USB_DEVICE_ID_A4TECH_X5_005D	0x000a
+
+#define USB_VENDOR_ID_AASHIMA		0x06d6
+#define USB_DEVICE_ID_AASHIMA_GAMEPAD	0x0025
+#define USB_DEVICE_ID_AASHIMA_PREDATOR	0x0026
+
+#define USB_VENDOR_ID_ACECAD		0x0460
+#define USB_DEVICE_ID_ACECAD_FLAIR	0x0004
+#define USB_DEVICE_ID_ACECAD_302	0x0008
+
+#define USB_VENDOR_ID_ADS_TECH 		0x06e1
+#define USB_DEVICE_ID_ADS_TECH_RADIO_SI470X	0xa155
+
+#define USB_VENDOR_ID_AFATECH		0x15a4
+#define USB_DEVICE_ID_AFATECH_AF9016	0x9016
+
+#define USB_VENDOR_ID_AIPTEK		0x08ca
+#define USB_DEVICE_ID_AIPTEK_01		0x0001
+#define USB_DEVICE_ID_AIPTEK_10		0x0010
+#define USB_DEVICE_ID_AIPTEK_20		0x0020
+#define USB_DEVICE_ID_AIPTEK_21		0x0021
+#define USB_DEVICE_ID_AIPTEK_22		0x0022
+#define USB_DEVICE_ID_AIPTEK_23		0x0023
+#define USB_DEVICE_ID_AIPTEK_24		0x0024
+
+#define USB_VENDOR_ID_AIRCABLE		0x16CA
+#define USB_DEVICE_ID_AIRCABLE1		0x1502
+
+#define USB_VENDOR_ID_ALCOR		0x058f
+#define USB_DEVICE_ID_ALCOR_USBRS232	0x9720
+
+#define USB_VENDOR_ID_ALPS		0x0433
+#define USB_DEVICE_ID_IBM_GAMEPAD	0x1101
+
+#define USB_VENDOR_ID_APPLE		0x05ac
+#define USB_DEVICE_ID_APPLE_MIGHTYMOUSE	0x0304
+#define USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI	0x020e
+#define USB_DEVICE_ID_APPLE_FOUNTAIN_ISO	0x020f
+#define USB_DEVICE_ID_APPLE_GEYSER_ANSI	0x0214
+#define USB_DEVICE_ID_APPLE_GEYSER_ISO	0x0215
+#define USB_DEVICE_ID_APPLE_GEYSER_JIS	0x0216
+#define USB_DEVICE_ID_APPLE_GEYSER3_ANSI	0x0217
+#define USB_DEVICE_ID_APPLE_GEYSER3_ISO	0x0218
+#define USB_DEVICE_ID_APPLE_GEYSER3_JIS	0x0219
+#define USB_DEVICE_ID_APPLE_GEYSER4_ANSI	0x021a
+#define USB_DEVICE_ID_APPLE_GEYSER4_ISO	0x021b
+#define USB_DEVICE_ID_APPLE_GEYSER4_JIS	0x021c
+#define USB_DEVICE_ID_APPLE_ALU_ANSI	0x0220
+#define USB_DEVICE_ID_APPLE_ALU_ISO	0x0221
+#define USB_DEVICE_ID_APPLE_ALU_JIS	0x0222
+#define USB_DEVICE_ID_APPLE_WELLSPRING_ANSI	0x0223
+#define USB_DEVICE_ID_APPLE_WELLSPRING_ISO	0x0224
+#define USB_DEVICE_ID_APPLE_WELLSPRING_JIS	0x0225
+#define USB_DEVICE_ID_APPLE_GEYSER4_HF_ANSI    0x0229
+#define USB_DEVICE_ID_APPLE_GEYSER4_HF_ISO     0x022a
+#define USB_DEVICE_ID_APPLE_GEYSER4_HF_JIS     0x022b
+#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI  0x022c
+#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO   0x022d
+#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS   0x022e
+#define USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI	0x0230
+#define USB_DEVICE_ID_APPLE_WELLSPRING2_ISO	0x0231
+#define USB_DEVICE_ID_APPLE_WELLSPRING2_JIS	0x0232
+#define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY	0x030a
+#define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY	0x030b
+#define USB_DEVICE_ID_APPLE_ATV_IRCONTROL	0x8241
+#define USB_DEVICE_ID_APPLE_IRCONTROL4	0x8242
+
+#define USB_VENDOR_ID_ASUS		0x0b05
+#define USB_DEVICE_ID_ASUS_LCM		0x1726
+
+#define USB_VENDOR_ID_ATEN		0x0557
+#define USB_DEVICE_ID_ATEN_UC100KM	0x2004
+#define USB_DEVICE_ID_ATEN_CS124U	0x2202
+#define USB_DEVICE_ID_ATEN_2PORTKVM	0x2204
+#define USB_DEVICE_ID_ATEN_4PORTKVM	0x2205
+#define USB_DEVICE_ID_ATEN_4PORTKVMC	0x2208
+
+#define USB_VENDOR_ID_AVERMEDIA		0x07ca
+#define USB_DEVICE_ID_AVER_FM_MR800	0xb800
+
+#define USB_VENDOR_ID_BELKIN           0x050d
+#define USB_DEVICE_ID_FLIP_KVM         0x3201
+
+#define USB_VENDOR_ID_BRIGHT		0x1241
+#define USB_DEVICE_ID_BRIGHT_ABNT2	0x1503
+
+#define USB_VENDOR_ID_BERKSHIRE		0x0c98
+#define USB_DEVICE_ID_BERKSHIRE_PCWD	0x1140
+
+#define USB_VENDOR_ID_CHERRY		0x046a
+#define USB_DEVICE_ID_CHERRY_CYMOTION	0x0023
+
+#define USB_VENDOR_ID_CHIC		0x05fe
+#define USB_DEVICE_ID_CHIC_GAMEPAD	0x0014
+
+#define USB_VENDOR_ID_CHICONY		0x04f2
+#define USB_DEVICE_ID_CHICONY_TACTICAL_PAD	0x0418
+
+#define USB_VENDOR_ID_CIDC		0x1677
+
+#define USB_VENDOR_ID_CMEDIA		0x0d8c
+#define USB_DEVICE_ID_CM109		0x000e
+
+#define USB_VENDOR_ID_CODEMERCS		0x07c0
+#define USB_DEVICE_ID_CODEMERCS_IOW_FIRST	0x1500
+#define USB_DEVICE_ID_CODEMERCS_IOW_LAST	0x15ff
+
+#define USB_VENDOR_ID_CYGNAL		0x10c4
+#define USB_DEVICE_ID_CYGNAL_RADIO_SI470X	0x818a
+
+#define USB_VENDOR_ID_CYPRESS		0x04b4
+#define USB_DEVICE_ID_CYPRESS_MOUSE	0x0001
+#define USB_DEVICE_ID_CYPRESS_HIDCOM	0x5500
+#define USB_DEVICE_ID_CYPRESS_ULTRAMOUSE	0x7417
+#define USB_DEVICE_ID_CYPRESS_BARCODE_1	0xde61
+#define USB_DEVICE_ID_CYPRESS_BARCODE_2	0xde64
+
+#define USB_VENDOR_ID_DELL		0x413c
+#define USB_DEVICE_ID_DELL_W7658	0x2005
+#define USB_DEVICE_ID_DELL_SK8115	0x2105
+
+#define USB_VENDOR_ID_DELORME		0x1163
+#define USB_DEVICE_ID_DELORME_EARTHMATE 0x0100
+#define USB_DEVICE_ID_DELORME_EM_LT20	0x0200
+
+#define USB_VENDOR_ID_DMI		0x0c0b
+#define USB_DEVICE_ID_DMI_ENC		0x5fab
+
+#define USB_VENDOR_ID_ELO		0x04E7
+#define USB_DEVICE_ID_ELO_TS2700	0x0020
+
+#define USB_VENDOR_ID_ESSENTIAL_REALITY	0x0d7f
+#define USB_DEVICE_ID_ESSENTIAL_REALITY_P5 0x0100
+
+#define USB_VENDOR_ID_EZKEY 		0x0518
+#define USB_DEVICE_ID_BTC_8193		0x0002
+
+#define USB_VENDOR_ID_GAMERON		0x0810
+#define USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR	0x0001
+
+#define USB_VENDOR_ID_GENERAL_TOUCH	0x0dfc
+
+#define USB_VENDOR_ID_GLAB		0x06c2
+#define USB_DEVICE_ID_4_PHIDGETSERVO_30	0x0038
+#define USB_DEVICE_ID_1_PHIDGETSERVO_30	0x0039
+#define USB_DEVICE_ID_0_0_4_IF_KIT	0x0040
+#define USB_DEVICE_ID_0_16_16_IF_KIT	0x0044
+#define USB_DEVICE_ID_8_8_8_IF_KIT	0x0045
+#define USB_DEVICE_ID_0_8_7_IF_KIT	0x0051
+#define USB_DEVICE_ID_0_8_8_IF_KIT	0x0053
+#define USB_DEVICE_ID_PHIDGET_MOTORCONTROL	0x0058
+
+#define USB_VENDOR_ID_GOTOP		0x08f2
+#define USB_DEVICE_ID_SUPER_Q2		0x007f
+#define USB_DEVICE_ID_GOGOPEN		0x00ce
+#define USB_DEVICE_ID_PENPOWER		0x00f4
+
+#define USB_VENDOR_ID_GREENASIA		0x0e8f
+
+#define USB_VENDOR_ID_GRETAGMACBETH	0x0971
+#define USB_DEVICE_ID_GRETAGMACBETH_HUEY	0x2005
+
+#define USB_VENDOR_ID_GRIFFIN		0x077d
+#define USB_DEVICE_ID_POWERMATE		0x0410
+#define USB_DEVICE_ID_SOUNDKNOB		0x04AA
+
+#define USB_VENDOR_ID_GTCO		0x078c
+#define USB_DEVICE_ID_GTCO_90		0x0090
+#define USB_DEVICE_ID_GTCO_100		0x0100
+#define USB_DEVICE_ID_GTCO_101		0x0101
+#define USB_DEVICE_ID_GTCO_103		0x0103
+#define USB_DEVICE_ID_GTCO_104		0x0104
+#define USB_DEVICE_ID_GTCO_105		0x0105
+#define USB_DEVICE_ID_GTCO_106		0x0106
+#define USB_DEVICE_ID_GTCO_107		0x0107
+#define USB_DEVICE_ID_GTCO_108		0x0108
+#define USB_DEVICE_ID_GTCO_200		0x0200
+#define USB_DEVICE_ID_GTCO_201		0x0201
+#define USB_DEVICE_ID_GTCO_202		0x0202
+#define USB_DEVICE_ID_GTCO_203		0x0203
+#define USB_DEVICE_ID_GTCO_204		0x0204
+#define USB_DEVICE_ID_GTCO_205		0x0205
+#define USB_DEVICE_ID_GTCO_206		0x0206
+#define USB_DEVICE_ID_GTCO_207		0x0207
+#define USB_DEVICE_ID_GTCO_300		0x0300
+#define USB_DEVICE_ID_GTCO_301		0x0301
+#define USB_DEVICE_ID_GTCO_302		0x0302
+#define USB_DEVICE_ID_GTCO_303		0x0303
+#define USB_DEVICE_ID_GTCO_304		0x0304
+#define USB_DEVICE_ID_GTCO_305		0x0305
+#define USB_DEVICE_ID_GTCO_306		0x0306
+#define USB_DEVICE_ID_GTCO_307		0x0307
+#define USB_DEVICE_ID_GTCO_308		0x0308
+#define USB_DEVICE_ID_GTCO_309		0x0309
+#define USB_DEVICE_ID_GTCO_400		0x0400
+#define USB_DEVICE_ID_GTCO_401		0x0401
+#define USB_DEVICE_ID_GTCO_402		0x0402
+#define USB_DEVICE_ID_GTCO_403		0x0403
+#define USB_DEVICE_ID_GTCO_404		0x0404
+#define USB_DEVICE_ID_GTCO_405		0x0405
+#define USB_DEVICE_ID_GTCO_500		0x0500
+#define USB_DEVICE_ID_GTCO_501		0x0501
+#define USB_DEVICE_ID_GTCO_502		0x0502
+#define USB_DEVICE_ID_GTCO_503		0x0503
+#define USB_DEVICE_ID_GTCO_504		0x0504
+#define USB_DEVICE_ID_GTCO_1000		0x1000
+#define USB_DEVICE_ID_GTCO_1001		0x1001
+#define USB_DEVICE_ID_GTCO_1002		0x1002
+#define USB_DEVICE_ID_GTCO_1003		0x1003
+#define USB_DEVICE_ID_GTCO_1004		0x1004
+#define USB_DEVICE_ID_GTCO_1005		0x1005
+#define USB_DEVICE_ID_GTCO_1006		0x1006
+#define USB_DEVICE_ID_GTCO_1007		0x1007
+
+#define USB_VENDOR_ID_GYRATION		0x0c16
+#define USB_DEVICE_ID_GYRATION_REMOTE	0x0002
+
+#define USB_VENDOR_ID_HAPP		0x078b
+#define USB_DEVICE_ID_UGCI_DRIVING	0x0010
+#define USB_DEVICE_ID_UGCI_FLYING	0x0020
+#define USB_DEVICE_ID_UGCI_FIGHTING	0x0030
+
+#define USB_VENDOR_ID_IMATION		0x0718
+#define USB_DEVICE_ID_DISC_STAKKA	0xd000
+
+#define USB_VENDOR_ID_KBGEAR		0x084e
+#define USB_DEVICE_ID_KBGEAR_JAMSTUDIO	0x1001
+
+#define USB_VENDOR_ID_LABTEC		0x1020
+#define USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD	0x0006
+
+#define USB_VENDOR_ID_LD		0x0f11
+#define USB_DEVICE_ID_LD_CASSY		0x1000
+#define USB_DEVICE_ID_LD_POCKETCASSY	0x1010
+#define USB_DEVICE_ID_LD_MOBILECASSY	0x1020
+#define USB_DEVICE_ID_LD_JWM		0x1080
+#define USB_DEVICE_ID_LD_DMMP		0x1081
+#define USB_DEVICE_ID_LD_UMIP		0x1090
+#define USB_DEVICE_ID_LD_XRAY1		0x1100
+#define USB_DEVICE_ID_LD_XRAY2		0x1101
+#define USB_DEVICE_ID_LD_VIDEOCOM	0x1200
+#define USB_DEVICE_ID_LD_COM3LAB	0x2000
+#define USB_DEVICE_ID_LD_TELEPORT	0x2010
+#define USB_DEVICE_ID_LD_NETWORKANALYSER 0x2020
+#define USB_DEVICE_ID_LD_POWERCONTROL	0x2030
+#define USB_DEVICE_ID_LD_MACHINETEST	0x2040
+
+#define USB_VENDOR_ID_LOGITECH		0x046d
+#define USB_DEVICE_ID_LOGITECH_LX3	0xc044
+#define USB_DEVICE_ID_LOGITECH_V150	0xc047
+#define USB_DEVICE_ID_LOGITECH_RECEIVER	0xc101
+#define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST  0xc110
+#define USB_DEVICE_ID_LOGITECH_HARMONY_LAST 0xc14f
+#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD	0xc211
+#define USB_DEVICE_ID_LOGITECH_EXTREME_3D	0xc215
+#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2	0xc218
+#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2	0xc219
+#define USB_DEVICE_ID_LOGITECH_WINGMAN_F3D	0xc283
+#define USB_DEVICE_ID_LOGITECH_FORCE3D_PRO	0xc286
+#define USB_DEVICE_ID_LOGITECH_WHEEL	0xc294
+#define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL	0xc295
+#define USB_DEVICE_ID_LOGITECH_ELITE_KBD	0xc30a
+#define USB_DEVICE_ID_LOGITECH_KBD	0xc311
+#define USB_DEVICE_ID_S510_RECEIVER	0xc50c
+#define USB_DEVICE_ID_S510_RECEIVER_2	0xc517
+#define USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500	0xc512
+#define USB_DEVICE_ID_MX3000_RECEIVER	0xc513
+#define USB_DEVICE_ID_DINOVO_DESKTOP	0xc704
+#define USB_DEVICE_ID_DINOVO_EDGE	0xc714
+#define USB_DEVICE_ID_DINOVO_MINI	0xc71f
+#define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2	0xca03
+
+#define USB_VENDOR_ID_MCC		0x09db
+#define USB_DEVICE_ID_MCC_PMD1024LS	0x0076
+#define USB_DEVICE_ID_MCC_PMD1208LS	0x007a
+
+#define USB_VENDOR_ID_MGE		0x0463
+#define USB_DEVICE_ID_MGE_UPS		0xffff
+#define USB_DEVICE_ID_MGE_UPS1		0x0001
+
+#define USB_VENDOR_ID_MICROCHIP		0x04d8
+#define USB_DEVICE_ID_PICKIT1		0x0032
+#define USB_DEVICE_ID_PICKIT2		0x0033
+
+#define USB_VENDOR_ID_MICROSOFT		0x045e
+#define USB_DEVICE_ID_SIDEWINDER_GV	0x003b
+#define USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0 0x009d
+#define USB_DEVICE_ID_MS_NE4K		0x00db
+#define USB_DEVICE_ID_MS_LK6K		0x00f9
+#define USB_DEVICE_ID_MS_PRESENTER_8K_BT	0x0701
+#define USB_DEVICE_ID_MS_PRESENTER_8K_USB	0x0713
+
+
+#define USB_VENDOR_ID_MONTEREY		0x0566
+#define USB_DEVICE_ID_GENIUS_KB29E	0x3004
+
+#define USB_VENDOR_ID_NCR		0x0404
+#define USB_DEVICE_ID_NCR_FIRST		0x0300
+#define USB_DEVICE_ID_NCR_LAST		0x03ff
+
+#define USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR 0x0400
+#define USB_DEVICE_ID_N_S_HARMONY       0xc359
+
+#define USB_VENDOR_ID_NATSU             0x08b7
+#define USB_DEVICE_ID_NATSU_GAMEPAD     0x0001
+
+#define USB_VENDOR_ID_NEC		0x073e
+#define USB_DEVICE_ID_NEC_USB_GAME_PAD	0x0301
+
+#define USB_VENDOR_ID_ONTRAK		0x0a07
+#define USB_DEVICE_ID_ONTRAK_ADU100	0x0064
+
+#define USB_VENDOR_ID_PANJIT		0x134c
+
+#define USB_VENDOR_ID_PANTHERLORD	0x0810
+#define USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK	0x0001
+
+#define USB_VENDOR_ID_PETALYNX		0x18b1
+#define USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE	0x0037
+
+#define USB_VENDOR_ID_PLAYDOTCOM	0x0b43
+#define USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII	0x0003
+
+#define USB_VENDOR_ID_SAITEK		0x06a3
+#define USB_DEVICE_ID_SAITEK_RUMBLEPAD	0xff17
+
+#define USB_VENDOR_ID_SAMSUNG		0x0419
+#define USB_DEVICE_ID_SAMSUNG_IR_REMOTE	0x0001
+
+#define USB_VENDOR_ID_SONY			0x054c
+#define USB_DEVICE_ID_SONY_PS3_CONTROLLER	0x0268
+
+#define USB_VENDOR_ID_SOUNDGRAPH	0x15c2
+#define USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD	0x0038
+
+#define USB_VENDOR_ID_SUN		0x0430
+#define USB_DEVICE_ID_RARITAN_KVM_DONGLE	0xcdab
+
+#define USB_VENDOR_ID_SUNPLUS		0x04fc
+#define USB_DEVICE_ID_SUNPLUS_WDESKTOP	0x05d8
+
+#define USB_VENDOR_ID_TENX		0x1130
+#define USB_DEVICE_ID_TENX_IBUDDY1	0x0001
+#define USB_DEVICE_ID_TENX_IBUDDY2	0x0002
+
+#define USB_VENDOR_ID_THRUSTMASTER	0x044f
+
+#define USB_VENDOR_ID_TOPMAX		0x0663
+#define USB_DEVICE_ID_TOPMAX_COBRAPAD	0x0103
+
+#define USB_VENDOR_ID_TURBOX		0x062a
+#define USB_DEVICE_ID_TURBOX_KEYBOARD	0x0201
+
+#define USB_VENDOR_ID_VERNIER		0x08f7
+#define USB_DEVICE_ID_VERNIER_LABPRO	0x0001
+#define USB_DEVICE_ID_VERNIER_GOTEMP	0x0002
+#define USB_DEVICE_ID_VERNIER_SKIP	0x0003
+#define USB_DEVICE_ID_VERNIER_CYCLOPS	0x0004
+#define USB_DEVICE_ID_VERNIER_LCSPEC	0x0006
+
+#define USB_VENDOR_ID_WACOM		0x056a
+
+#define USB_VENDOR_ID_WISEGROUP		0x0925
+#define USB_DEVICE_ID_1_PHIDGETSERVO_20	0x8101
+#define USB_DEVICE_ID_4_PHIDGETSERVO_20	0x8104
+#define USB_DEVICE_ID_8_8_4_IF_KIT	0x8201
+#define USB_DEVICE_ID_QUAD_USB_JOYPAD	0x8800
+#define USB_DEVICE_ID_DUAL_USB_JOYPAD	0x8866
+
+#define USB_VENDOR_ID_WISEGROUP_LTD	0x6666
+#define USB_VENDOR_ID_WISEGROUP_LTD2	0x6677
+#define USB_DEVICE_ID_SMARTJOY_DUAL_PLUS 0x8802
+
+#define USB_VENDOR_ID_YEALINK		0x6993
+#define USB_DEVICE_ID_YEALINK_P1K_P4K_B2K	0xb001
+
+#define USB_VENDOR_ID_ZEROPLUS		0x0c12
+
+#define USB_VENDOR_ID_KYE		0x0458
+#define USB_DEVICE_ID_KYE_GPEN_560	0x5003
+
+#endif
diff --git a/drivers/hid/hid-input-quirks.c b/drivers/hid/hid-input-quirks.c
deleted file mode 100644
index 16feea0..0000000
--- a/drivers/hid/hid-input-quirks.c
+++ /dev/null
@@ -1,484 +0,0 @@
-/*
- *  HID-input usage mapping quirks
- *
- *  This is used to handle HID-input mappings for devices violating
- *  HUT 1.12 specification.
- *
- * Copyright (c) 2007-2008 Jiri Kosina
- */
-
-/*
- * 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
- */
-
-#include <linux/input.h>
-#include <linux/hid.h>
-
-#define map_abs(c)      do { usage->code = c; usage->type = EV_ABS; *bit = input->absbit; *max = ABS_MAX; } while (0)
-#define map_rel(c)      do { usage->code = c; usage->type = EV_REL; *bit = input->relbit; *max = REL_MAX; } while (0)
-#define map_key(c)      do { usage->code = c; usage->type = EV_KEY; *bit = input->keybit; *max = KEY_MAX; } while (0)
-#define map_led(c)      do { usage->code = c; usage->type = EV_LED; *bit = input->ledbit; *max = LED_MAX; } while (0)
-
-#define map_abs_clear(c)        do { map_abs(c); clear_bit(c, *bit); } while (0)
-#define map_key_clear(c)        do { map_key(c); clear_bit(c, *bit); } while (0)
-
-static int quirk_belkin_wkbd(struct hid_usage *usage, struct input_dev *input,
-			      unsigned long **bit, int *max)
-{
-	if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
-		return 0;
-
-	switch (usage->hid & HID_USAGE) {
-		case 0x03a: map_key_clear(KEY_SOUND);		break;
-		case 0x03b: map_key_clear(KEY_CAMERA);		break;
-		case 0x03c: map_key_clear(KEY_DOCUMENTS);	break;
-		default:
-			return 0;
-	}
-	return 1;
-}
-
-static int quirk_cherry_cymotion(struct hid_usage *usage, struct input_dev *input,
-			      unsigned long **bit, int *max)
-{
-	if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
-		return 0;
-
-	switch (usage->hid & HID_USAGE) {
-		case 0x301: map_key_clear(KEY_PROG1);		break;
-		case 0x302: map_key_clear(KEY_PROG2);		break;
-		case 0x303: map_key_clear(KEY_PROG3);		break;
-		default:
-			return 0;
-	}
-	return 1;
-}
-
-static int quirk_logitech_ultrax_remote(struct hid_usage *usage, struct input_dev *input,
-			      unsigned long **bit, int *max)
-{
-	if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR)
-		return 0;
-
-	set_bit(EV_REP, input->evbit);
-	switch(usage->hid & HID_USAGE) {
-		/* Reported on Logitech Ultra X Media Remote */
-		case 0x004: map_key_clear(KEY_AGAIN);		break;
-		case 0x00d: map_key_clear(KEY_HOME);		break;
-		case 0x024: map_key_clear(KEY_SHUFFLE);		break;
-		case 0x025: map_key_clear(KEY_TV);		break;
-		case 0x026: map_key_clear(KEY_MENU);		break;
-		case 0x031: map_key_clear(KEY_AUDIO);		break;
-		case 0x032: map_key_clear(KEY_TEXT);		break;
-		case 0x033: map_key_clear(KEY_LAST);		break;
-		case 0x047: map_key_clear(KEY_MP3);		break;
-		case 0x048: map_key_clear(KEY_DVD);		break;
-		case 0x049: map_key_clear(KEY_MEDIA);		break;
-		case 0x04a: map_key_clear(KEY_VIDEO);		break;
-		case 0x04b: map_key_clear(KEY_ANGLE);		break;
-		case 0x04c: map_key_clear(KEY_LANGUAGE);	break;
-		case 0x04d: map_key_clear(KEY_SUBTITLE);	break;
-		case 0x051: map_key_clear(KEY_RED);		break;
-		case 0x052: map_key_clear(KEY_CLOSE);		break;
-
-		default:
-			return 0;
-	}
-	return 1;
-}
-
-static int quirk_gyration_remote(struct hid_usage *usage, struct input_dev *input,
-			      unsigned long **bit, int *max)
-{
-	if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR)
-		return 0;
-
-	set_bit(EV_REP, input->evbit);
-	switch(usage->hid & HID_USAGE) {
-		/* Reported on Gyration MCE Remote */
-		case 0x00d: map_key_clear(KEY_HOME);		break;
-		case 0x024: map_key_clear(KEY_DVD);		break;
-		case 0x025: map_key_clear(KEY_PVR);		break;
-		case 0x046: map_key_clear(KEY_MEDIA);		break;
-		case 0x047: map_key_clear(KEY_MP3);		break;
-		case 0x049: map_key_clear(KEY_CAMERA);		break;
-		case 0x04a: map_key_clear(KEY_VIDEO);		break;
-
-		default:
-			return 0;
-	}
-	return 1;
-}
-
-static int quirk_chicony_tactical_pad(struct hid_usage *usage, struct input_dev *input,
-			      unsigned long **bit, int *max)
-{
-	if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR)
-		return 0;
-
-	set_bit(EV_REP, input->evbit);
-	switch (usage->hid & HID_USAGE) {
-		case 0xff01: map_key_clear(BTN_1);		break;
-		case 0xff02: map_key_clear(BTN_2);		break;
-		case 0xff03: map_key_clear(BTN_3);		break;
-		case 0xff04: map_key_clear(BTN_4);		break;
-		case 0xff05: map_key_clear(BTN_5);		break;
-		case 0xff06: map_key_clear(BTN_6);		break;
-		case 0xff07: map_key_clear(BTN_7);		break;
-		case 0xff08: map_key_clear(BTN_8);		break;
-		case 0xff09: map_key_clear(BTN_9);		break;
-		case 0xff0a: map_key_clear(BTN_A);		break;
-		case 0xff0b: map_key_clear(BTN_B);		break;
-		default:
-			return 0;
-	}
-	return 1;
-}
-
-static int quirk_microsoft_ergonomy_kb(struct hid_usage *usage, struct input_dev *input,
-			      unsigned long **bit, int *max)
-{
-	if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR)
-		return 0;
-
-	switch(usage->hid & HID_USAGE) {
-		case 0xfd06: map_key_clear(KEY_CHAT);		break;
-		case 0xfd07: map_key_clear(KEY_PHONE);		break;
-		case 0xff05:
-			set_bit(EV_REP, input->evbit);
-			map_key_clear(KEY_F13);
-			set_bit(KEY_F14, input->keybit);
-			set_bit(KEY_F15, input->keybit);
-			set_bit(KEY_F16, input->keybit);
-			set_bit(KEY_F17, input->keybit);
-			set_bit(KEY_F18, input->keybit);
-		default:
-			return 0;
-	}
-	return 1;
-}
-
-static int quirk_microsoft_presenter_8k(struct hid_usage *usage, struct input_dev *input,
-			      unsigned long **bit, int *max)
-{
-	if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR)
-		return 0;
-
-	set_bit(EV_REP, input->evbit);
-	switch(usage->hid & HID_USAGE) {
-		case 0xfd08: map_key_clear(KEY_FORWARD);	break;
-		case 0xfd09: map_key_clear(KEY_BACK);		break;
-		case 0xfd0b: map_key_clear(KEY_PLAYPAUSE);	break;
-		case 0xfd0e: map_key_clear(KEY_CLOSE);		break;
-		case 0xfd0f: map_key_clear(KEY_PLAY);		break;
-		default:
-			return 0;
-	}
-	return 1;
-}
-
-static int quirk_petalynx_remote(struct hid_usage *usage, struct input_dev *input,
-			      unsigned long **bit, int *max)
-{
-	if (((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR) &&
-			((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER))
-		return 0;
-
-	if ((usage->hid & HID_USAGE_PAGE) == HID_UP_LOGIVENDOR)
-		switch(usage->hid & HID_USAGE) {
-			case 0x05a: map_key_clear(KEY_TEXT);		break;
-			case 0x05b: map_key_clear(KEY_RED);		break;
-			case 0x05c: map_key_clear(KEY_GREEN);		break;
-			case 0x05d: map_key_clear(KEY_YELLOW);		break;
-			case 0x05e: map_key_clear(KEY_BLUE);		break;
-			default:
-				return 0;
-		}
-
-	if ((usage->hid & HID_USAGE_PAGE) == HID_UP_CONSUMER)
-		switch(usage->hid & HID_USAGE) {
-			case 0x0f6: map_key_clear(KEY_NEXT);            break;
-			case 0x0fa: map_key_clear(KEY_BACK);            break;
-			default:
-				return 0;
-		}
-	return 1;
-}
-
-static int quirk_logitech_wireless(struct hid_usage *usage, struct input_dev *input,
-			      unsigned long **bit, int *max)
-{
-	if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
-		return 0;
-
-	switch (usage->hid & HID_USAGE) {
-		case 0x1001: map_key_clear(KEY_MESSENGER);	break;
-		case 0x1003: map_key_clear(KEY_SOUND);		break;
-		case 0x1004: map_key_clear(KEY_VIDEO);		break;
-		case 0x1005: map_key_clear(KEY_AUDIO);		break;
-		case 0x100a: map_key_clear(KEY_DOCUMENTS);	break;
-		case 0x1011: map_key_clear(KEY_PREVIOUSSONG);	break;
-		case 0x1012: map_key_clear(KEY_NEXTSONG);	break;
-		case 0x1013: map_key_clear(KEY_CAMERA);		break;
-		case 0x1014: map_key_clear(KEY_MESSENGER);	break;
-		case 0x1015: map_key_clear(KEY_RECORD);		break;
-		case 0x1016: map_key_clear(KEY_PLAYER);		break;
-		case 0x1017: map_key_clear(KEY_EJECTCD);	break;
-		case 0x1018: map_key_clear(KEY_MEDIA);		break;
-		case 0x1019: map_key_clear(KEY_PROG1);		break;
-		case 0x101a: map_key_clear(KEY_PROG2);		break;
-		case 0x101b: map_key_clear(KEY_PROG3);		break;
-		case 0x101f: map_key_clear(KEY_ZOOMIN);		break;
-		case 0x1020: map_key_clear(KEY_ZOOMOUT);	break;
-		case 0x1021: map_key_clear(KEY_ZOOMRESET);	break;
-		case 0x1023: map_key_clear(KEY_CLOSE);		break;
-		case 0x1027: map_key_clear(KEY_MENU);		break;
-		/* this one is marked as 'Rotate' */
-		case 0x1028: map_key_clear(KEY_ANGLE);		break;
-		case 0x1029: map_key_clear(KEY_SHUFFLE);	break;
-		case 0x102a: map_key_clear(KEY_BACK);		break;
-		case 0x102b: map_key_clear(KEY_CYCLEWINDOWS);	break;
-		case 0x1041: map_key_clear(KEY_BATTERY);	break;
-		case 0x1042: map_key_clear(KEY_WORDPROCESSOR);	break;
-		case 0x1043: map_key_clear(KEY_SPREADSHEET);	break;
-		case 0x1044: map_key_clear(KEY_PRESENTATION);	break;
-		case 0x1045: map_key_clear(KEY_UNDO);		break;
-		case 0x1046: map_key_clear(KEY_REDO);		break;
-		case 0x1047: map_key_clear(KEY_PRINT);		break;
-		case 0x1048: map_key_clear(KEY_SAVE);		break;
-		case 0x1049: map_key_clear(KEY_PROG1);		break;
-		case 0x104a: map_key_clear(KEY_PROG2);		break;
-		case 0x104b: map_key_clear(KEY_PROG3);		break;
-		case 0x104c: map_key_clear(KEY_PROG4);		break;
-
-		default:
-			return 0;
-	}
-	return 1;
-}
-
-static int quirk_cherry_genius_29e(struct hid_usage *usage, struct input_dev *input,
-			      unsigned long **bit, int *max)
-{
-	if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
-		return 0;
-
-	switch (usage->hid & HID_USAGE) {
-		case 0x156: map_key_clear(KEY_WORDPROCESSOR);	break;
-		case 0x157: map_key_clear(KEY_SPREADSHEET);	break;
-		case 0x158: map_key_clear(KEY_PRESENTATION);	break;
-		case 0x15c: map_key_clear(KEY_STOP);		break;
-
-		default:
-			return 0;
-	}
-	return 1;
-}
-
-static int quirk_btc_8193(struct hid_usage *usage, struct input_dev *input,
-			      unsigned long **bit, int *max)
-{
-	if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
-		return 0;
-
-	switch (usage->hid & HID_USAGE) {
-		case 0x230: map_key(BTN_MOUSE);			break;
-		case 0x231: map_rel(REL_WHEEL);			break;
-		/* 
-		 * this keyboard has a scrollwheel implemented in
-		 * totally broken way. We map this usage temporarily
-		 * to HWHEEL and handle it in the event quirk handler
-		 */
-		case 0x232: map_rel(REL_HWHEEL);		break;
-
-		default:
-			return 0;
-	}
-	return 1;
-}
-
-static int quirk_sunplus_wdesktop(struct hid_usage *usage, struct input_dev *input,
-			      unsigned long **bit, int *max)
-{
-	if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
-		return 0;
-
-	switch (usage->hid & HID_USAGE) {
-		case 0x2003: map_key_clear(KEY_ZOOMIN);		break;
-		case 0x2103: map_key_clear(KEY_ZOOMOUT);	break;
-		default:
-			return 0;
-	}
-	return 1;
-}
-
-#define VENDOR_ID_BELKIN			0x1020
-#define DEVICE_ID_BELKIN_WIRELESS_KEYBOARD	0x0006
-
-#define VENDOR_ID_CHERRY			0x046a
-#define DEVICE_ID_CHERRY_CYMOTION		0x0023
-
-#define VENDOR_ID_CHICONY			0x04f2
-#define DEVICE_ID_CHICONY_TACTICAL_PAD		0x0418
-
-#define VENDOR_ID_EZKEY				0x0518
-#define DEVICE_ID_BTC_8193			0x0002
-
-#define VENDOR_ID_GYRATION			0x0c16
-#define DEVICE_ID_GYRATION_REMOTE		0x0002
-
-#define VENDOR_ID_LOGITECH			0x046d
-#define DEVICE_ID_LOGITECH_RECEIVER		0xc101
-#define DEVICE_ID_S510_RECEIVER			0xc50c
-#define DEVICE_ID_S510_RECEIVER_2		0xc517
-#define DEVICE_ID_MX3000_RECEIVER		0xc513
-
-#define VENDOR_ID_MICROSOFT			0x045e
-#define DEVICE_ID_MS4K				0x00db
-#define DEVICE_ID_MS6K				0x00f9
-#define DEVICE_IS_MS_PRESENTER_8K_BT		0x0701
-#define DEVICE_ID_MS_PRESENTER_8K_USB		0x0713
-
-#define VENDOR_ID_MONTEREY			0x0566
-#define DEVICE_ID_GENIUS_KB29E			0x3004
-
-#define VENDOR_ID_PETALYNX			0x18b1
-#define DEVICE_ID_PETALYNX_MAXTER_REMOTE	0x0037
-
-#define VENDOR_ID_SUNPLUS			0x04fc
-#define DEVICE_ID_SUNPLUS_WDESKTOP		0x05d8
-
-static const struct hid_input_blacklist {
-	__u16 idVendor;
-	__u16 idProduct;
-	int (*quirk)(struct hid_usage *, struct input_dev *, unsigned long **, int *);
-} hid_input_blacklist[] = {
-	{ VENDOR_ID_BELKIN, DEVICE_ID_BELKIN_WIRELESS_KEYBOARD, quirk_belkin_wkbd },
-
-	{ VENDOR_ID_CHERRY, DEVICE_ID_CHERRY_CYMOTION, quirk_cherry_cymotion },
-
-	{ VENDOR_ID_CHICONY, DEVICE_ID_CHICONY_TACTICAL_PAD, quirk_chicony_tactical_pad },
-
-	{ VENDOR_ID_EZKEY, DEVICE_ID_BTC_8193, quirk_btc_8193 },
-
-	{ VENDOR_ID_GYRATION, DEVICE_ID_GYRATION_REMOTE, quirk_gyration_remote },
-
-	{ VENDOR_ID_LOGITECH, DEVICE_ID_LOGITECH_RECEIVER, quirk_logitech_ultrax_remote },
-	{ VENDOR_ID_LOGITECH, DEVICE_ID_S510_RECEIVER, quirk_logitech_wireless },
-	{ VENDOR_ID_LOGITECH, DEVICE_ID_S510_RECEIVER_2, quirk_logitech_wireless },
-	{ VENDOR_ID_LOGITECH, DEVICE_ID_MX3000_RECEIVER, quirk_logitech_wireless },
-
-	{ VENDOR_ID_MICROSOFT, DEVICE_ID_MS4K, quirk_microsoft_ergonomy_kb },
-	{ VENDOR_ID_MICROSOFT, DEVICE_ID_MS6K, quirk_microsoft_ergonomy_kb },
-	{ VENDOR_ID_MICROSOFT, DEVICE_IS_MS_PRESENTER_8K_BT, quirk_microsoft_presenter_8k },
-	{ VENDOR_ID_MICROSOFT, DEVICE_ID_MS_PRESENTER_8K_USB, quirk_microsoft_presenter_8k },
-
-	{ VENDOR_ID_MONTEREY, DEVICE_ID_GENIUS_KB29E, quirk_cherry_genius_29e },
-
-	{ VENDOR_ID_PETALYNX, DEVICE_ID_PETALYNX_MAXTER_REMOTE, quirk_petalynx_remote },
-
-	{ VENDOR_ID_SUNPLUS, DEVICE_ID_SUNPLUS_WDESKTOP, quirk_sunplus_wdesktop },
-
-	{ 0, 0, NULL }
-};
-
-int hidinput_mapping_quirks(struct hid_usage *usage, 
-				   struct input_dev *input, 
-				   unsigned long **bit, int *max)
-{
-	struct hid_device *device = input_get_drvdata(input);
-	int i = 0;
-	
-	while (hid_input_blacklist[i].quirk) {
-		if (hid_input_blacklist[i].idVendor == device->vendor &&
-				hid_input_blacklist[i].idProduct == device->product)
-			return hid_input_blacklist[i].quirk(usage, input, bit, max);
-		i++;
-	}
-	return 0;
-}
-
-int hidinput_event_quirks(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value)
-{
-	struct input_dev *input;
-
-	input = field->hidinput->input;
-
-	if (((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_5) && (usage->hid == 0x00090005))
-		|| ((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_7) && (usage->hid == 0x00090007))) {
-		if (value) hid->quirks |=  HID_QUIRK_2WHEEL_MOUSE_HACK_ON;
-		else       hid->quirks &= ~HID_QUIRK_2WHEEL_MOUSE_HACK_ON;
-		return 1;
-	}
-
-	if ((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_B8) &&
-			(usage->type == EV_REL) &&
-			(usage->code == REL_WHEEL)) {
-		hid->delayed_value = value;
-		return 1;
-	}
-
-	if ((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_B8) &&
-			(usage->hid == 0x000100b8)) {
-		input_event(input, EV_REL, value ? REL_HWHEEL : REL_WHEEL, hid->delayed_value);
-		return 1;
-	}
-
-	if ((hid->quirks & HID_QUIRK_INVERT_HWHEEL) && (usage->code == REL_HWHEEL)) {
-		input_event(input, usage->type, usage->code, -value);
-		return 1;
-	}
-
-	if ((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_ON) && (usage->code == REL_WHEEL)) {
-		input_event(input, usage->type, REL_HWHEEL, value);
-		return 1;
-	}
-
-	if ((hid->quirks & HID_QUIRK_APPLE_HAS_FN) && hidinput_apple_event(hid, input, usage, value))
-		return 1;
-
-	/* Handling MS keyboards special buttons */
-	if (hid->quirks & HID_QUIRK_MICROSOFT_KEYS && 
-			usage->hid == (HID_UP_MSVENDOR | 0xff05)) {
-		int key = 0;
-		static int last_key = 0;
-		switch (value) {
-			case 0x01: key = KEY_F14; break;
-			case 0x02: key = KEY_F15; break;
-			case 0x04: key = KEY_F16; break;
-			case 0x08: key = KEY_F17; break;
-			case 0x10: key = KEY_F18; break;
-			default: break;
-		}
-		if (key) {
-			input_event(input, usage->type, key, 1);
-			last_key = key;
-		} else {
-			input_event(input, usage->type, last_key, 0);
-		}
-	}
-
-	/* handle the temporary quirky mapping to HWHEEL */
-	if (hid->quirks & HID_QUIRK_HWHEEL_WHEEL_INVERT &&
-			usage->type == EV_REL && usage->code == REL_HWHEEL) {
-		input_event(input, usage->type, REL_WHEEL, -value);
-		return 1;
-	}
-
-	/* Gyration MCE remote "Sleep" key */
-	if (hid->vendor == VENDOR_ID_GYRATION &&
-	    hid->product == DEVICE_ID_GYRATION_REMOTE &&
-	    (usage->hid & HID_USAGE_PAGE) == HID_UP_GENDESK &&
-	    (usage->hid & 0xff) == 0x82) {
-		input_event(input, usage->type, usage->code, 1);
-		input_sync(input);
-		input_event(input, usage->type, usage->code, 0);
-		input_sync(input);
-		return 1;
-	}
-	return 0;
-}
-
-
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index 1b2e8dc..7f183b7 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -32,11 +32,6 @@
 #include <linux/hid.h>
 #include <linux/hid-debug.h>
 
-static int hid_apple_fnmode = 1;
-module_param_named(pb_fnmode, hid_apple_fnmode, int, 0644);
-MODULE_PARM_DESC(pb_fnmode,
-		"Mode of fn key on Apple keyboards (0 = disabled, 1 = fkeyslast, 2 = fkeysfirst)");
-
 #define unk	KEY_UNKNOWN
 
 static const unsigned char hid_keyboard[256] = {
@@ -58,227 +53,20 @@
 	150,158,159,128,136,177,178,176,142,152,173,140,unk,unk,unk,unk
 };
 
-/* extended mapping for certain Logitech hardware (Logitech cordless desktop LX500) */
-#define LOGITECH_EXPANDED_KEYMAP_SIZE 80
-static int logitech_expanded_keymap[LOGITECH_EXPANDED_KEYMAP_SIZE] = {
-	  0,216,  0,213,175,156,  0,  0,  0,  0,
-	144,  0,  0,  0,  0,  0,  0,  0,  0,212,
-	174,167,152,161,112,  0,  0,  0,154,  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,183,184,185,186,187,
-	188,189,190,191,192,193,194,  0,  0,  0
-};
-
 static const struct {
 	__s32 x;
 	__s32 y;
 }  hid_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
 
-#define map_abs(c)	do { usage->code = c; usage->type = EV_ABS; bit = input->absbit; max = ABS_MAX; } while (0)
-#define map_rel(c)	do { usage->code = c; usage->type = EV_REL; bit = input->relbit; max = REL_MAX; } while (0)
-#define map_key(c)	do { usage->code = c; usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; } while (0)
-#define map_led(c)	do { usage->code = c; usage->type = EV_LED; bit = input->ledbit; max = LED_MAX; } while (0)
+#define map_abs(c)	hid_map_usage(hidinput, usage, &bit, &max, EV_ABS, (c))
+#define map_rel(c)	hid_map_usage(hidinput, usage, &bit, &max, EV_REL, (c))
+#define map_key(c)	hid_map_usage(hidinput, usage, &bit, &max, EV_KEY, (c))
+#define map_led(c)	hid_map_usage(hidinput, usage, &bit, &max, EV_LED, (c))
 
-#define map_abs_clear(c)	do { map_abs(c); clear_bit(c, bit); } while (0)
-#define map_key_clear(c)	do { map_key(c); clear_bit(c, bit); } while (0)
-
-#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
-
-struct hidinput_key_translation {
-	u16 from;
-	u16 to;
-	u8 flags;
-};
-
-#define APPLE_FLAG_FKEY 0x01
-
-static struct hidinput_key_translation apple_fn_keys[] = {
-	{ KEY_BACKSPACE, KEY_DELETE },
-	{ KEY_F1,       KEY_BRIGHTNESSDOWN, APPLE_FLAG_FKEY },
-	{ KEY_F2,       KEY_BRIGHTNESSUP,   APPLE_FLAG_FKEY },
-	{ KEY_F3,       KEY_FN_F5,          APPLE_FLAG_FKEY }, /* Exposé */
-	{ KEY_F4,       KEY_FN_F4,          APPLE_FLAG_FKEY }, /* Dashboard */
-	{ KEY_F5,       KEY_KBDILLUMDOWN,   APPLE_FLAG_FKEY },
-	{ KEY_F6,       KEY_KBDILLUMUP,     APPLE_FLAG_FKEY },
-	{ KEY_F7,       KEY_PREVIOUSSONG,   APPLE_FLAG_FKEY },
-	{ KEY_F8,       KEY_PLAYPAUSE,      APPLE_FLAG_FKEY },
-	{ KEY_F9,       KEY_NEXTSONG,       APPLE_FLAG_FKEY },
-	{ KEY_F10,      KEY_MUTE,           APPLE_FLAG_FKEY },
-	{ KEY_F11,      KEY_VOLUMEDOWN,     APPLE_FLAG_FKEY },
-	{ KEY_F12,      KEY_VOLUMEUP,       APPLE_FLAG_FKEY },
-	{ KEY_UP,       KEY_PAGEUP },
-	{ KEY_DOWN,     KEY_PAGEDOWN },
-	{ KEY_LEFT,     KEY_HOME },
-	{ KEY_RIGHT,    KEY_END },
-	{ }
-};
-
-static struct hidinput_key_translation powerbook_fn_keys[] = {
-	{ KEY_BACKSPACE, KEY_DELETE },
-	{ KEY_F1,       KEY_BRIGHTNESSDOWN,     APPLE_FLAG_FKEY },
-	{ KEY_F2,       KEY_BRIGHTNESSUP,       APPLE_FLAG_FKEY },
-	{ KEY_F3,       KEY_MUTE,               APPLE_FLAG_FKEY },
-	{ KEY_F4,       KEY_VOLUMEDOWN,         APPLE_FLAG_FKEY },
-	{ KEY_F5,       KEY_VOLUMEUP,           APPLE_FLAG_FKEY },
-	{ KEY_F6,       KEY_NUMLOCK,            APPLE_FLAG_FKEY },
-	{ KEY_F7,       KEY_SWITCHVIDEOMODE,    APPLE_FLAG_FKEY },
-	{ KEY_F8,       KEY_KBDILLUMTOGGLE,     APPLE_FLAG_FKEY },
-	{ KEY_F9,       KEY_KBDILLUMDOWN,       APPLE_FLAG_FKEY },
-	{ KEY_F10,      KEY_KBDILLUMUP,         APPLE_FLAG_FKEY },
-	{ KEY_UP,       KEY_PAGEUP },
-	{ KEY_DOWN,     KEY_PAGEDOWN },
-	{ KEY_LEFT,     KEY_HOME },
-	{ KEY_RIGHT,    KEY_END },
-	{ }
-};
-
-static struct hidinput_key_translation powerbook_numlock_keys[] = {
-	{ KEY_J,        KEY_KP1 },
-	{ KEY_K,        KEY_KP2 },
-	{ KEY_L,        KEY_KP3 },
-	{ KEY_U,        KEY_KP4 },
-	{ KEY_I,        KEY_KP5 },
-	{ KEY_O,        KEY_KP6 },
-	{ KEY_7,        KEY_KP7 },
-	{ KEY_8,        KEY_KP8 },
-	{ KEY_9,        KEY_KP9 },
-	{ KEY_M,        KEY_KP0 },
-	{ KEY_DOT,      KEY_KPDOT },
-	{ KEY_SLASH,    KEY_KPPLUS },
-	{ KEY_SEMICOLON, KEY_KPMINUS },
-	{ KEY_P,        KEY_KPASTERISK },
-	{ KEY_MINUS,    KEY_KPEQUAL },
-	{ KEY_0,        KEY_KPSLASH },
-	{ KEY_F6,       KEY_NUMLOCK },
-	{ KEY_KPENTER,  KEY_KPENTER },
-	{ KEY_BACKSPACE, KEY_BACKSPACE },
-	{ }
-};
-
-static struct hidinput_key_translation apple_iso_keyboard[] = {
-	{ KEY_GRAVE,    KEY_102ND },
-	{ KEY_102ND,    KEY_GRAVE },
-	{ }
-};
-
-static struct hidinput_key_translation *find_translation(struct hidinput_key_translation *table, u16 from)
-{
-	struct hidinput_key_translation *trans;
-
-	/* Look for the translation */
-	for (trans = table; trans->from; trans++)
-		if (trans->from == from)
-			return trans;
-
-	return NULL;
-}
-
-int hidinput_apple_event(struct hid_device *hid, struct input_dev *input,
-		struct hid_usage *usage, __s32 value)
-{
-	struct hidinput_key_translation *trans;
-
-	if (usage->code == KEY_FN) {
-		if (value) hid->quirks |=  HID_QUIRK_APPLE_FN_ON;
-		else       hid->quirks &= ~HID_QUIRK_APPLE_FN_ON;
-
-		input_event(input, usage->type, usage->code, value);
-
-		return 1;
-	}
-
-	if (hid_apple_fnmode) {
-		int do_translate;
-
-		trans = find_translation((hid->product < 0x220 ||
-					  hid->product >= 0x300) ?
-					 powerbook_fn_keys : apple_fn_keys,
-					 usage->code);
-		if (trans) {
-			if (test_bit(usage->code, hid->apple_pressed_fn))
-				do_translate = 1;
-			else if (trans->flags & APPLE_FLAG_FKEY)
-				do_translate =
-					(hid_apple_fnmode == 2 &&  (hid->quirks & HID_QUIRK_APPLE_FN_ON)) ||
-					(hid_apple_fnmode == 1 && !(hid->quirks & HID_QUIRK_APPLE_FN_ON));
-			else
-				do_translate = (hid->quirks & HID_QUIRK_APPLE_FN_ON);
-
-			if (do_translate) {
-				if (value)
-					set_bit(usage->code, hid->apple_pressed_fn);
-				else
-					clear_bit(usage->code, hid->apple_pressed_fn);
-
-				input_event(input, usage->type, trans->to, value);
-
-				return 1;
-			}
-		}
-
-		if (hid->quirks & HID_QUIRK_APPLE_NUMLOCK_EMULATION && (
-				test_bit(usage->code, hid->pb_pressed_numlock) ||
-				test_bit(LED_NUML, input->led))) {
-			trans = find_translation(powerbook_numlock_keys, usage->code);
-
-			if (trans) {
-				if (value)
-					set_bit(usage->code, hid->pb_pressed_numlock);
-				else
-					clear_bit(usage->code, hid->pb_pressed_numlock);
-
-				input_event(input, usage->type, trans->to, value);
-			}
-
-			return 1;
-		}
-	}
-
-	if (hid->quirks & HID_QUIRK_APPLE_ISO_KEYBOARD) {
-		trans = find_translation(apple_iso_keyboard, usage->code);
-		if (trans) {
-			input_event(input, usage->type, trans->to, value);
-			return 1;
-		}
-	}
-
-	return 0;
-}
-
-static void hidinput_apple_setup(struct input_dev *input)
-{
-	struct hidinput_key_translation *trans;
-
-	set_bit(KEY_NUMLOCK, input->keybit);
-
-	/* Enable all needed keys */
-	for (trans = apple_fn_keys; trans->from; trans++)
-		set_bit(trans->to, input->keybit);
-
-	for (trans = powerbook_fn_keys; trans->from; trans++)
-		set_bit(trans->to, input->keybit);
-
-	for (trans = powerbook_numlock_keys; trans->from; trans++)
-		set_bit(trans->to, input->keybit);
-
-	for (trans = apple_iso_keyboard; trans->from; trans++)
-		set_bit(trans->to, input->keybit);
-
-}
-#else
-inline int hidinput_apple_event(struct hid_device *hid,
-				       struct input_dev *input,
-				       struct hid_usage *usage, __s32 value)
-{
-	return 0;
-}
-
-static inline void hidinput_apple_setup(struct input_dev *input)
-{
-}
-#endif
+#define map_abs_clear(c)	hid_map_usage_clear(hidinput, usage, &bit, \
+		&max, EV_ABS, (c))
+#define map_key_clear(c)	hid_map_usage_clear(hidinput, usage, &bit, \
+		&max, EV_KEY, (c))
 
 static inline int match_scancode(int code, int scancode)
 {
@@ -366,7 +154,7 @@
 {
 	struct input_dev *input = hidinput->input;
 	struct hid_device *device = input_get_drvdata(input);
-	int max = 0, code, ret;
+	int max = 0, code;
 	unsigned long *bit = NULL;
 
 	field->hidinput = hidinput;
@@ -385,406 +173,345 @@
 		goto ignore;
 	}
 
-	/* handle input mappings for quirky devices */
-	ret = hidinput_mapping_quirks(usage, input, &bit, &max);
-	if (ret)
-		goto mapped;
+	if (device->driver->input_mapping) {
+		int ret = device->driver->input_mapping(device, hidinput, field,
+				usage, &bit, &max);
+		if (ret > 0)
+			goto mapped;
+		if (ret < 0)
+			goto ignore;
+	}
 
 	switch (usage->hid & HID_USAGE_PAGE) {
+	case HID_UP_UNDEFINED:
+		goto ignore;
 
-		case HID_UP_UNDEFINED:
-			goto ignore;
+	case HID_UP_KEYBOARD:
+		set_bit(EV_REP, input->evbit);
 
-		case HID_UP_KEYBOARD:
+		if ((usage->hid & HID_USAGE) < 256) {
+			if (!hid_keyboard[usage->hid & HID_USAGE]) goto ignore;
+			map_key_clear(hid_keyboard[usage->hid & HID_USAGE]);
+		} else
+			map_key(KEY_UNKNOWN);
 
-			set_bit(EV_REP, input->evbit);
+		break;
 
-			if ((usage->hid & HID_USAGE) < 256) {
-				if (!hid_keyboard[usage->hid & HID_USAGE]) goto ignore;
-				map_key_clear(hid_keyboard[usage->hid & HID_USAGE]);
-			} else
-				map_key(KEY_UNKNOWN);
+	case HID_UP_BUTTON:
+		code = ((usage->hid - 1) & 0xf);
 
-			break;
-
-		case HID_UP_BUTTON:
-
-			code = ((usage->hid - 1) & 0xf);
-
-			switch (field->application) {
-				case HID_GD_MOUSE:
-				case HID_GD_POINTER:  code += 0x110; break;
-				case HID_GD_JOYSTICK: code += 0x120; break;
-				case HID_GD_GAMEPAD:  code += 0x130; break;
-				default:
-					switch (field->physical) {
-						case HID_GD_MOUSE:
-						case HID_GD_POINTER:  code += 0x110; break;
-						case HID_GD_JOYSTICK: code += 0x120; break;
-						case HID_GD_GAMEPAD:  code += 0x130; break;
-						default:              code += 0x100;
-					}
-			}
-
-			/* Special handling for Logitech Cordless Desktop */
-			if (field->application != HID_GD_MOUSE) {
-				if (device->quirks & HID_QUIRK_LOGITECH_EXPANDED_KEYMAP) {
-					int hid = usage->hid & HID_USAGE;
-					if (hid < LOGITECH_EXPANDED_KEYMAP_SIZE && logitech_expanded_keymap[hid] != 0)
-						code = logitech_expanded_keymap[hid];
-				}
-			} else {
-				if (device->quirks & HID_QUIRK_LOGITECH_IGNORE_DOUBLED_WHEEL) {
-					int hid = usage->hid & HID_USAGE;
-					if (hid == 7 || hid == 8)
-						goto ignore;
-				}
-			}
-
-			map_key(code);
-			break;
-
-
-		case HID_UP_SIMULATION:
-
-			switch (usage->hid & 0xffff) {
-				case 0xba: map_abs(ABS_RUDDER);   break;
-				case 0xbb: map_abs(ABS_THROTTLE); break;
-				case 0xc4: map_abs(ABS_GAS);      break;
-				case 0xc5: map_abs(ABS_BRAKE);    break;
-				case 0xc8: map_abs(ABS_WHEEL);    break;
-				default:   goto ignore;
-			}
-			break;
-
-		case HID_UP_GENDESK:
-
-			if ((usage->hid & 0xf0) == 0x80) {	/* SystemControl */
-				switch (usage->hid & 0xf) {
-					case 0x1: map_key_clear(KEY_POWER);  break;
-					case 0x2: map_key_clear(KEY_SLEEP);  break;
-					case 0x3: map_key_clear(KEY_WAKEUP); break;
-					default: goto unknown;
-				}
-				break;
-			}
-
-			if ((usage->hid & 0xf0) == 0x90) {	/* D-pad */
-				switch (usage->hid) {
-					case HID_GD_UP:	   usage->hat_dir = 1; break;
-					case HID_GD_DOWN:  usage->hat_dir = 5; break;
-					case HID_GD_RIGHT: usage->hat_dir = 3; break;
-					case HID_GD_LEFT:  usage->hat_dir = 7; break;
-					default: goto unknown;
-				}
-				if (field->dpad) {
-					map_abs(field->dpad);
-					goto ignore;
-				}
-				map_abs(ABS_HAT0X);
-				break;
-			}
-
-			switch (usage->hid) {
-
-				/* These usage IDs map directly to the usage codes. */
-				case HID_GD_X: case HID_GD_Y: case HID_GD_Z:
-				case HID_GD_RX: case HID_GD_RY: case HID_GD_RZ:
-				case HID_GD_SLIDER: case HID_GD_DIAL: case HID_GD_WHEEL:
-					if (field->flags & HID_MAIN_ITEM_RELATIVE)
-						map_rel(usage->hid & 0xf);
-					else
-						map_abs(usage->hid & 0xf);
-					break;
-
-				case HID_GD_HATSWITCH:
-					usage->hat_min = field->logical_minimum;
-					usage->hat_max = field->logical_maximum;
-					map_abs(ABS_HAT0X);
-					break;
-
-				case HID_GD_START:	map_key_clear(BTN_START);	break;
-				case HID_GD_SELECT:	map_key_clear(BTN_SELECT);	break;
-
-				default: goto unknown;
-			}
-
-			break;
-
-		case HID_UP_LED:
-
-			switch (usage->hid & 0xffff) {                        /* HID-Value:                   */
-				case 0x01:  map_led (LED_NUML);     break;    /*   "Num Lock"                 */
-				case 0x02:  map_led (LED_CAPSL);    break;    /*   "Caps Lock"                */
-				case 0x03:  map_led (LED_SCROLLL);  break;    /*   "Scroll Lock"              */
-				case 0x04:  map_led (LED_COMPOSE);  break;    /*   "Compose"                  */
-				case 0x05:  map_led (LED_KANA);     break;    /*   "Kana"                     */
-				case 0x27:  map_led (LED_SLEEP);    break;    /*   "Stand-By"                 */
-				case 0x4c:  map_led (LED_SUSPEND);  break;    /*   "System Suspend"           */
-				case 0x09:  map_led (LED_MUTE);     break;    /*   "Mute"                     */
-				case 0x4b:  map_led (LED_MISC);     break;    /*   "Generic Indicator"        */
-				case 0x19:  map_led (LED_MAIL);     break;    /*   "Message Waiting"          */
-				case 0x4d:  map_led (LED_CHARGING); break;    /*   "External Power Connected" */
-
-				default: goto ignore;
-			}
-			break;
-
-		case HID_UP_DIGITIZER:
-
-			switch (usage->hid & 0xff) {
-
-				case 0x30: /* TipPressure */
-					if (!test_bit(BTN_TOUCH, input->keybit)) {
-						device->quirks |= HID_QUIRK_NOTOUCH;
-						set_bit(EV_KEY, input->evbit);
-						set_bit(BTN_TOUCH, input->keybit);
-					}
-
-					map_abs_clear(ABS_PRESSURE);
-					break;
-
-				case 0x32: /* InRange */
-					switch (field->physical & 0xff) {
-						case 0x21: map_key(BTN_TOOL_MOUSE); break;
-						case 0x22: map_key(BTN_TOOL_FINGER); break;
-						default: map_key(BTN_TOOL_PEN); break;
-					}
-					break;
-
-				case 0x3c: /* Invert */
-					map_key_clear(BTN_TOOL_RUBBER);
-					break;
-
-				case 0x33: /* Touch */
-				case 0x42: /* TipSwitch */
-				case 0x43: /* TipSwitch2 */
-					device->quirks &= ~HID_QUIRK_NOTOUCH;
-					map_key_clear(BTN_TOUCH);
-					break;
-
-				case 0x44: /* BarrelSwitch */
-					map_key_clear(BTN_STYLUS);
-					break;
-
-				default:  goto unknown;
-			}
-			break;
-
-		case HID_UP_CONSUMER:	/* USB HUT v1.1, pages 56-62 */
-
-			switch (usage->hid & HID_USAGE) {
-				case 0x000: goto ignore;
-				case 0x034: map_key_clear(KEY_SLEEP);		break;
-				case 0x036: map_key_clear(BTN_MISC);		break;
-
-				case 0x040: map_key_clear(KEY_MENU);		break;
-				case 0x045: map_key_clear(KEY_RADIO);		break;
-
-				case 0x083: map_key_clear(KEY_LAST);		break;
-				case 0x088: map_key_clear(KEY_PC);		break;
-				case 0x089: map_key_clear(KEY_TV);		break;
-				case 0x08a: map_key_clear(KEY_WWW);		break;
-				case 0x08b: map_key_clear(KEY_DVD);		break;
-				case 0x08c: map_key_clear(KEY_PHONE);		break;
-				case 0x08d: map_key_clear(KEY_PROGRAM);		break;
-				case 0x08e: map_key_clear(KEY_VIDEOPHONE);	break;
-				case 0x08f: map_key_clear(KEY_GAMES);		break;
-				case 0x090: map_key_clear(KEY_MEMO);		break;
-				case 0x091: map_key_clear(KEY_CD);		break;
-				case 0x092: map_key_clear(KEY_VCR);		break;
-				case 0x093: map_key_clear(KEY_TUNER);		break;
-				case 0x094: map_key_clear(KEY_EXIT);		break;
-				case 0x095: map_key_clear(KEY_HELP);		break;
-				case 0x096: map_key_clear(KEY_TAPE);		break;
-				case 0x097: map_key_clear(KEY_TV2);		break;
-				case 0x098: map_key_clear(KEY_SAT);		break;
-				case 0x09a: map_key_clear(KEY_PVR);		break;
-
-				case 0x09c: map_key_clear(KEY_CHANNELUP);	break;
-				case 0x09d: map_key_clear(KEY_CHANNELDOWN);	break;
-				case 0x0a0: map_key_clear(KEY_VCR2);		break;
-
-				case 0x0b0: map_key_clear(KEY_PLAY);		break;
-				case 0x0b1: map_key_clear(KEY_PAUSE);		break;
-				case 0x0b2: map_key_clear(KEY_RECORD);		break;
-				case 0x0b3: map_key_clear(KEY_FASTFORWARD);	break;
-				case 0x0b4: map_key_clear(KEY_REWIND);		break;
-				case 0x0b5: map_key_clear(KEY_NEXTSONG);	break;
-				case 0x0b6: map_key_clear(KEY_PREVIOUSSONG);	break;
-				case 0x0b7: map_key_clear(KEY_STOPCD);		break;
-				case 0x0b8: map_key_clear(KEY_EJECTCD);		break;
-				case 0x0bc: map_key_clear(KEY_MEDIA_REPEAT);	break;
-
-				case 0x0cd: map_key_clear(KEY_PLAYPAUSE);	break;
-			        case 0x0e0: map_abs_clear(ABS_VOLUME);		break;
-				case 0x0e2: map_key_clear(KEY_MUTE);		break;
-				case 0x0e5: map_key_clear(KEY_BASSBOOST);	break;
-				case 0x0e9: map_key_clear(KEY_VOLUMEUP);	break;
-				case 0x0ea: map_key_clear(KEY_VOLUMEDOWN);	break;
-
-				case 0x182: map_key_clear(KEY_BOOKMARKS);	break;
-				case 0x183: map_key_clear(KEY_CONFIG);		break;
-				case 0x184: map_key_clear(KEY_WORDPROCESSOR);	break;
-				case 0x185: map_key_clear(KEY_EDITOR);		break;
-				case 0x186: map_key_clear(KEY_SPREADSHEET);	break;
-				case 0x187: map_key_clear(KEY_GRAPHICSEDITOR);	break;
-				case 0x188: map_key_clear(KEY_PRESENTATION);	break;
-				case 0x189: map_key_clear(KEY_DATABASE);	break;
-				case 0x18a: map_key_clear(KEY_MAIL);		break;
-				case 0x18b: map_key_clear(KEY_NEWS);		break;
-				case 0x18c: map_key_clear(KEY_VOICEMAIL);	break;
-				case 0x18d: map_key_clear(KEY_ADDRESSBOOK);	break;
-				case 0x18e: map_key_clear(KEY_CALENDAR);	break;
-				case 0x191: map_key_clear(KEY_FINANCE);		break;
-				case 0x192: map_key_clear(KEY_CALC);		break;
-				case 0x194: map_key_clear(KEY_FILE);		break;
-				case 0x196: map_key_clear(KEY_WWW);		break;
-				case 0x19c: map_key_clear(KEY_LOGOFF);		break;
-				case 0x19e: map_key_clear(KEY_COFFEE);		break;
-				case 0x1a6: map_key_clear(KEY_HELP);		break;
-				case 0x1a7: map_key_clear(KEY_DOCUMENTS);	break;
-				case 0x1ab: map_key_clear(KEY_SPELLCHECK);	break;
-				case 0x1b6: map_key_clear(KEY_MEDIA);		break;
-				case 0x1b7: map_key_clear(KEY_SOUND);		break;
-				case 0x1bc: map_key_clear(KEY_MESSENGER);	break;
-				case 0x1bd: map_key_clear(KEY_INFO);		break;
-				case 0x201: map_key_clear(KEY_NEW);		break;
-				case 0x202: map_key_clear(KEY_OPEN);		break;
-				case 0x203: map_key_clear(KEY_CLOSE);		break;
-				case 0x204: map_key_clear(KEY_EXIT);		break;
-				case 0x207: map_key_clear(KEY_SAVE);		break;
-				case 0x208: map_key_clear(KEY_PRINT);		break;
-				case 0x209: map_key_clear(KEY_PROPS);		break;
-				case 0x21a: map_key_clear(KEY_UNDO);		break;
-				case 0x21b: map_key_clear(KEY_COPY);		break;
-				case 0x21c: map_key_clear(KEY_CUT);		break;
-				case 0x21d: map_key_clear(KEY_PASTE);		break;
-				case 0x21f: map_key_clear(KEY_FIND);		break;
-				case 0x221: map_key_clear(KEY_SEARCH);		break;
-				case 0x222: map_key_clear(KEY_GOTO);		break;
-				case 0x223: map_key_clear(KEY_HOMEPAGE);	break;
-				case 0x224: map_key_clear(KEY_BACK);		break;
-				case 0x225: map_key_clear(KEY_FORWARD);		break;
-				case 0x226: map_key_clear(KEY_STOP);		break;
-				case 0x227: map_key_clear(KEY_REFRESH);		break;
-				case 0x22a: map_key_clear(KEY_BOOKMARKS);	break;
-				case 0x22d: map_key_clear(KEY_ZOOMIN);		break;
-				case 0x22e: map_key_clear(KEY_ZOOMOUT);		break;
-				case 0x22f: map_key_clear(KEY_ZOOMRESET);	break;
-				case 0x233: map_key_clear(KEY_SCROLLUP);	break;
-				case 0x234: map_key_clear(KEY_SCROLLDOWN);	break;
-				case 0x238: map_rel(REL_HWHEEL);		break;
-				case 0x25f: map_key_clear(KEY_CANCEL);		break;
-				case 0x279: map_key_clear(KEY_REDO);		break;
-
-				case 0x289: map_key_clear(KEY_REPLY);		break;
-				case 0x28b: map_key_clear(KEY_FORWARDMAIL);	break;
-				case 0x28c: map_key_clear(KEY_SEND);		break;
-
-				default:    goto ignore;
-			}
-			break;
-
-		case HID_UP_HPVENDOR:	/* Reported on a Dutch layout HP5308 */
-
-			set_bit(EV_REP, input->evbit);
-			switch (usage->hid & HID_USAGE) {
-			        case 0x021: map_key_clear(KEY_PRINT);           break;
-				case 0x070: map_key_clear(KEY_HP);		break;
-				case 0x071: map_key_clear(KEY_CAMERA);		break;
-				case 0x072: map_key_clear(KEY_SOUND);		break;
-				case 0x073: map_key_clear(KEY_QUESTION);	break;
-				case 0x080: map_key_clear(KEY_EMAIL);		break;
-				case 0x081: map_key_clear(KEY_CHAT);		break;
-				case 0x082: map_key_clear(KEY_SEARCH);		break;
-				case 0x083: map_key_clear(KEY_CONNECT);	        break;
-				case 0x084: map_key_clear(KEY_FINANCE);		break;
-				case 0x085: map_key_clear(KEY_SPORT);		break;
-				case 0x086: map_key_clear(KEY_SHOP);	        break;
-				default:    goto ignore;
-			}
-			break;
-
-		case HID_UP_MSVENDOR:
-
-			goto ignore;
-
-		case HID_UP_CUSTOM: /* Reported on Logitech and Apple USB keyboards */
-
-			set_bit(EV_REP, input->evbit);
-			switch(usage->hid & HID_USAGE) {
-				case 0x003:
-					/* The fn key on Apple USB keyboards */
-					map_key_clear(KEY_FN);
-					hidinput_apple_setup(input);
-					break;
-
-				default:    goto ignore;
-			}
-			break;
-
-		case HID_UP_LOGIVENDOR:
-
-			goto ignore;
-		
-		case HID_UP_PID:
-
-			switch(usage->hid & HID_USAGE) {
-				case 0xa4: map_key_clear(BTN_DEAD);	break;
-				default: goto ignore;
-			}
-			break;
-
+		switch (field->application) {
+		case HID_GD_MOUSE:
+		case HID_GD_POINTER:  code += 0x110; break;
+		case HID_GD_JOYSTICK: code += 0x120; break;
+		case HID_GD_GAMEPAD:  code += 0x130; break;
 		default:
-		unknown:
-			if (field->report_size == 1) {
-				if (field->report->type == HID_OUTPUT_REPORT) {
-					map_led(LED_MISC);
-					break;
-				}
-				map_key(BTN_MISC);
-				break;
+			switch (field->physical) {
+			case HID_GD_MOUSE:
+			case HID_GD_POINTER:  code += 0x110; break;
+			case HID_GD_JOYSTICK: code += 0x120; break;
+			case HID_GD_GAMEPAD:  code += 0x130; break;
+			default:              code += 0x100;
 			}
-			if (field->flags & HID_MAIN_ITEM_RELATIVE) {
-				map_rel(REL_MISC);
-				break;
+		}
+
+		map_key(code);
+		break;
+
+	case HID_UP_SIMULATION:
+		switch (usage->hid & 0xffff) {
+		case 0xba: map_abs(ABS_RUDDER);   break;
+		case 0xbb: map_abs(ABS_THROTTLE); break;
+		case 0xc4: map_abs(ABS_GAS);      break;
+		case 0xc5: map_abs(ABS_BRAKE);    break;
+		case 0xc8: map_abs(ABS_WHEEL);    break;
+		default:   goto ignore;
+		}
+		break;
+
+	case HID_UP_GENDESK:
+		if ((usage->hid & 0xf0) == 0x80) {	/* SystemControl */
+			switch (usage->hid & 0xf) {
+			case 0x1: map_key_clear(KEY_POWER);  break;
+			case 0x2: map_key_clear(KEY_SLEEP);  break;
+			case 0x3: map_key_clear(KEY_WAKEUP); break;
+			default: goto unknown;
 			}
-			map_abs(ABS_MISC);
 			break;
+		}
+
+		if ((usage->hid & 0xf0) == 0x90) {	/* D-pad */
+			switch (usage->hid) {
+			case HID_GD_UP:	   usage->hat_dir = 1; break;
+			case HID_GD_DOWN:  usage->hat_dir = 5; break;
+			case HID_GD_RIGHT: usage->hat_dir = 3; break;
+			case HID_GD_LEFT:  usage->hat_dir = 7; break;
+			default: goto unknown;
+			}
+			if (field->dpad) {
+				map_abs(field->dpad);
+				goto ignore;
+			}
+			map_abs(ABS_HAT0X);
+			break;
+		}
+
+		switch (usage->hid) {
+		/* These usage IDs map directly to the usage codes. */
+		case HID_GD_X: case HID_GD_Y: case HID_GD_Z:
+		case HID_GD_RX: case HID_GD_RY: case HID_GD_RZ:
+		case HID_GD_SLIDER: case HID_GD_DIAL: case HID_GD_WHEEL:
+			if (field->flags & HID_MAIN_ITEM_RELATIVE)
+				map_rel(usage->hid & 0xf);
+			else
+				map_abs(usage->hid & 0xf);
+			break;
+
+		case HID_GD_HATSWITCH:
+			usage->hat_min = field->logical_minimum;
+			usage->hat_max = field->logical_maximum;
+			map_abs(ABS_HAT0X);
+			break;
+
+		case HID_GD_START:	map_key_clear(BTN_START);	break;
+		case HID_GD_SELECT:	map_key_clear(BTN_SELECT);	break;
+
+		default: goto unknown;
+		}
+
+		break;
+
+	case HID_UP_LED:
+		switch (usage->hid & 0xffff) {		      /* HID-Value:                   */
+		case 0x01:  map_led (LED_NUML);     break;    /*   "Num Lock"                 */
+		case 0x02:  map_led (LED_CAPSL);    break;    /*   "Caps Lock"                */
+		case 0x03:  map_led (LED_SCROLLL);  break;    /*   "Scroll Lock"              */
+		case 0x04:  map_led (LED_COMPOSE);  break;    /*   "Compose"                  */
+		case 0x05:  map_led (LED_KANA);     break;    /*   "Kana"                     */
+		case 0x27:  map_led (LED_SLEEP);    break;    /*   "Stand-By"                 */
+		case 0x4c:  map_led (LED_SUSPEND);  break;    /*   "System Suspend"           */
+		case 0x09:  map_led (LED_MUTE);     break;    /*   "Mute"                     */
+		case 0x4b:  map_led (LED_MISC);     break;    /*   "Generic Indicator"        */
+		case 0x19:  map_led (LED_MAIL);     break;    /*   "Message Waiting"          */
+		case 0x4d:  map_led (LED_CHARGING); break;    /*   "External Power Connected" */
+
+		default: goto ignore;
+		}
+		break;
+
+	case HID_UP_DIGITIZER:
+		switch (usage->hid & 0xff) {
+		case 0x30: /* TipPressure */
+			if (!test_bit(BTN_TOUCH, input->keybit)) {
+				device->quirks |= HID_QUIRK_NOTOUCH;
+				set_bit(EV_KEY, input->evbit);
+				set_bit(BTN_TOUCH, input->keybit);
+			}
+			map_abs_clear(ABS_PRESSURE);
+			break;
+
+		case 0x32: /* InRange */
+			switch (field->physical & 0xff) {
+			case 0x21: map_key(BTN_TOOL_MOUSE); break;
+			case 0x22: map_key(BTN_TOOL_FINGER); break;
+			default: map_key(BTN_TOOL_PEN); break;
+			}
+			break;
+
+		case 0x3c: /* Invert */
+			map_key_clear(BTN_TOOL_RUBBER);
+			break;
+
+		case 0x33: /* Touch */
+		case 0x42: /* TipSwitch */
+		case 0x43: /* TipSwitch2 */
+			device->quirks &= ~HID_QUIRK_NOTOUCH;
+			map_key_clear(BTN_TOUCH);
+			break;
+
+		case 0x44: /* BarrelSwitch */
+			map_key_clear(BTN_STYLUS);
+			break;
+
+		default:  goto unknown;
+		}
+		break;
+
+	case HID_UP_CONSUMER:	/* USB HUT v1.1, pages 56-62 */
+		switch (usage->hid & HID_USAGE) {
+		case 0x000: goto ignore;
+		case 0x034: map_key_clear(KEY_SLEEP);		break;
+		case 0x036: map_key_clear(BTN_MISC);		break;
+
+		case 0x040: map_key_clear(KEY_MENU);		break;
+		case 0x045: map_key_clear(KEY_RADIO);		break;
+
+		case 0x083: map_key_clear(KEY_LAST);		break;
+		case 0x088: map_key_clear(KEY_PC);		break;
+		case 0x089: map_key_clear(KEY_TV);		break;
+		case 0x08a: map_key_clear(KEY_WWW);		break;
+		case 0x08b: map_key_clear(KEY_DVD);		break;
+		case 0x08c: map_key_clear(KEY_PHONE);		break;
+		case 0x08d: map_key_clear(KEY_PROGRAM);		break;
+		case 0x08e: map_key_clear(KEY_VIDEOPHONE);	break;
+		case 0x08f: map_key_clear(KEY_GAMES);		break;
+		case 0x090: map_key_clear(KEY_MEMO);		break;
+		case 0x091: map_key_clear(KEY_CD);		break;
+		case 0x092: map_key_clear(KEY_VCR);		break;
+		case 0x093: map_key_clear(KEY_TUNER);		break;
+		case 0x094: map_key_clear(KEY_EXIT);		break;
+		case 0x095: map_key_clear(KEY_HELP);		break;
+		case 0x096: map_key_clear(KEY_TAPE);		break;
+		case 0x097: map_key_clear(KEY_TV2);		break;
+		case 0x098: map_key_clear(KEY_SAT);		break;
+		case 0x09a: map_key_clear(KEY_PVR);		break;
+
+		case 0x09c: map_key_clear(KEY_CHANNELUP);	break;
+		case 0x09d: map_key_clear(KEY_CHANNELDOWN);	break;
+		case 0x0a0: map_key_clear(KEY_VCR2);		break;
+
+		case 0x0b0: map_key_clear(KEY_PLAY);		break;
+		case 0x0b1: map_key_clear(KEY_PAUSE);		break;
+		case 0x0b2: map_key_clear(KEY_RECORD);		break;
+		case 0x0b3: map_key_clear(KEY_FASTFORWARD);	break;
+		case 0x0b4: map_key_clear(KEY_REWIND);		break;
+		case 0x0b5: map_key_clear(KEY_NEXTSONG);	break;
+		case 0x0b6: map_key_clear(KEY_PREVIOUSSONG);	break;
+		case 0x0b7: map_key_clear(KEY_STOPCD);		break;
+		case 0x0b8: map_key_clear(KEY_EJECTCD);		break;
+		case 0x0bc: map_key_clear(KEY_MEDIA_REPEAT);	break;
+
+		case 0x0cd: map_key_clear(KEY_PLAYPAUSE);	break;
+		case 0x0e0: map_abs_clear(ABS_VOLUME);		break;
+		case 0x0e2: map_key_clear(KEY_MUTE);		break;
+		case 0x0e5: map_key_clear(KEY_BASSBOOST);	break;
+		case 0x0e9: map_key_clear(KEY_VOLUMEUP);	break;
+		case 0x0ea: map_key_clear(KEY_VOLUMEDOWN);	break;
+
+		case 0x182: map_key_clear(KEY_BOOKMARKS);	break;
+		case 0x183: map_key_clear(KEY_CONFIG);		break;
+		case 0x184: map_key_clear(KEY_WORDPROCESSOR);	break;
+		case 0x185: map_key_clear(KEY_EDITOR);		break;
+		case 0x186: map_key_clear(KEY_SPREADSHEET);	break;
+		case 0x187: map_key_clear(KEY_GRAPHICSEDITOR);	break;
+		case 0x188: map_key_clear(KEY_PRESENTATION);	break;
+		case 0x189: map_key_clear(KEY_DATABASE);	break;
+		case 0x18a: map_key_clear(KEY_MAIL);		break;
+		case 0x18b: map_key_clear(KEY_NEWS);		break;
+		case 0x18c: map_key_clear(KEY_VOICEMAIL);	break;
+		case 0x18d: map_key_clear(KEY_ADDRESSBOOK);	break;
+		case 0x18e: map_key_clear(KEY_CALENDAR);	break;
+		case 0x191: map_key_clear(KEY_FINANCE);		break;
+		case 0x192: map_key_clear(KEY_CALC);		break;
+		case 0x194: map_key_clear(KEY_FILE);		break;
+		case 0x196: map_key_clear(KEY_WWW);		break;
+		case 0x19c: map_key_clear(KEY_LOGOFF);		break;
+		case 0x19e: map_key_clear(KEY_COFFEE);		break;
+		case 0x1a6: map_key_clear(KEY_HELP);		break;
+		case 0x1a7: map_key_clear(KEY_DOCUMENTS);	break;
+		case 0x1ab: map_key_clear(KEY_SPELLCHECK);	break;
+		case 0x1b6: map_key_clear(KEY_MEDIA);		break;
+		case 0x1b7: map_key_clear(KEY_SOUND);		break;
+		case 0x1bc: map_key_clear(KEY_MESSENGER);	break;
+		case 0x1bd: map_key_clear(KEY_INFO);		break;
+		case 0x201: map_key_clear(KEY_NEW);		break;
+		case 0x202: map_key_clear(KEY_OPEN);		break;
+		case 0x203: map_key_clear(KEY_CLOSE);		break;
+		case 0x204: map_key_clear(KEY_EXIT);		break;
+		case 0x207: map_key_clear(KEY_SAVE);		break;
+		case 0x208: map_key_clear(KEY_PRINT);		break;
+		case 0x209: map_key_clear(KEY_PROPS);		break;
+		case 0x21a: map_key_clear(KEY_UNDO);		break;
+		case 0x21b: map_key_clear(KEY_COPY);		break;
+		case 0x21c: map_key_clear(KEY_CUT);		break;
+		case 0x21d: map_key_clear(KEY_PASTE);		break;
+		case 0x21f: map_key_clear(KEY_FIND);		break;
+		case 0x221: map_key_clear(KEY_SEARCH);		break;
+		case 0x222: map_key_clear(KEY_GOTO);		break;
+		case 0x223: map_key_clear(KEY_HOMEPAGE);	break;
+		case 0x224: map_key_clear(KEY_BACK);		break;
+		case 0x225: map_key_clear(KEY_FORWARD);		break;
+		case 0x226: map_key_clear(KEY_STOP);		break;
+		case 0x227: map_key_clear(KEY_REFRESH);		break;
+		case 0x22a: map_key_clear(KEY_BOOKMARKS);	break;
+		case 0x22d: map_key_clear(KEY_ZOOMIN);		break;
+		case 0x22e: map_key_clear(KEY_ZOOMOUT);		break;
+		case 0x22f: map_key_clear(KEY_ZOOMRESET);	break;
+		case 0x233: map_key_clear(KEY_SCROLLUP);	break;
+		case 0x234: map_key_clear(KEY_SCROLLDOWN);	break;
+		case 0x238: map_rel(REL_HWHEEL);		break;
+		case 0x25f: map_key_clear(KEY_CANCEL);		break;
+		case 0x279: map_key_clear(KEY_REDO);		break;
+
+		case 0x289: map_key_clear(KEY_REPLY);		break;
+		case 0x28b: map_key_clear(KEY_FORWARDMAIL);	break;
+		case 0x28c: map_key_clear(KEY_SEND);		break;
+
+		default:    goto ignore;
+		}
+		break;
+
+	case HID_UP_HPVENDOR:	/* Reported on a Dutch layout HP5308 */
+		set_bit(EV_REP, input->evbit);
+		switch (usage->hid & HID_USAGE) {
+		case 0x021: map_key_clear(KEY_PRINT);           break;
+		case 0x070: map_key_clear(KEY_HP);		break;
+		case 0x071: map_key_clear(KEY_CAMERA);		break;
+		case 0x072: map_key_clear(KEY_SOUND);		break;
+		case 0x073: map_key_clear(KEY_QUESTION);	break;
+		case 0x080: map_key_clear(KEY_EMAIL);		break;
+		case 0x081: map_key_clear(KEY_CHAT);		break;
+		case 0x082: map_key_clear(KEY_SEARCH);		break;
+		case 0x083: map_key_clear(KEY_CONNECT);	        break;
+		case 0x084: map_key_clear(KEY_FINANCE);		break;
+		case 0x085: map_key_clear(KEY_SPORT);		break;
+		case 0x086: map_key_clear(KEY_SHOP);	        break;
+		default:    goto ignore;
+		}
+		break;
+
+	case HID_UP_MSVENDOR:
+		goto ignore;
+
+	case HID_UP_CUSTOM: /* Reported on Logitech and Apple USB keyboards */
+		set_bit(EV_REP, input->evbit);
+		goto ignore;
+
+	case HID_UP_LOGIVENDOR:
+		goto ignore;
+	
+	case HID_UP_PID:
+		switch (usage->hid & HID_USAGE) {
+		case 0xa4: map_key_clear(BTN_DEAD);	break;
+		default: goto ignore;
+		}
+		break;
+
+	default:
+	unknown:
+		if (field->report_size == 1) {
+			if (field->report->type == HID_OUTPUT_REPORT) {
+				map_led(LED_MISC);
+				break;
+			}
+			map_key(BTN_MISC);
+			break;
+		}
+		if (field->flags & HID_MAIN_ITEM_RELATIVE) {
+			map_rel(REL_MISC);
+			break;
+		}
+		map_abs(ABS_MISC);
+		break;
 	}
 
 mapped:
-	if (device->quirks & HID_QUIRK_MIGHTYMOUSE) {
-		if (usage->hid == HID_GD_Z)
-			map_rel(REL_HWHEEL);
-		else if (usage->code == BTN_1)
-			map_key(BTN_2);
-		else if (usage->code == BTN_2)
-			map_key(BTN_1);
-	}
-
-	if ((device->quirks & (HID_QUIRK_2WHEEL_MOUSE_HACK_7 | HID_QUIRK_2WHEEL_MOUSE_HACK_5 |
-			HID_QUIRK_2WHEEL_MOUSE_HACK_B8)) && (usage->type == EV_REL) &&
-			(usage->code == REL_WHEEL))
-		set_bit(REL_HWHEEL, bit);
-
-	if (((device->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_5) && (usage->hid == 0x00090005))
-		|| ((device->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_7) && (usage->hid == 0x00090007)))
+	if (device->driver->input_mapped && device->driver->input_mapped(device,
+				hidinput, field, usage, &bit, &max) < 0)
 		goto ignore;
 
-	if ((device->quirks & HID_QUIRK_BAD_RELATIVE_KEYS) &&
-		usage->type == EV_KEY && (field->flags & HID_MAIN_ITEM_RELATIVE))
-		field->flags &= ~HID_MAIN_ITEM_RELATIVE;
-
 	set_bit(usage->type, input->evbit);
 
-	if (device->quirks & HID_QUIRK_DUPLICATE_USAGES &&
-			(usage->type == EV_KEY ||
-			 usage->type == EV_REL ||
-			 usage->type == EV_ABS))
-		clear_bit(usage->code, bit);
-
 	while (usage->code <= max && test_and_set_bit(usage->code, bit))
 		usage->code = find_next_zero_bit(bit, max + 1, usage->code);
 
@@ -858,10 +585,6 @@
 	if (!usage->type)
 		return;
 
-	/* handle input events for quirky devices */
-	if (hidinput_event_quirks(hid, field, usage, value))
-		return;
-
 	if (usage->hat_min < usage->hat_max || usage->hat_dir) {
 		int hat_dir = usage->hat_dir;
 		if (!hat_dir)
@@ -961,14 +684,14 @@
 {
 	struct hid_device *hid = input_get_drvdata(dev);
 
-	return hid->hid_open(hid);
+	return hid->ll_driver->open(hid);
 }
 
 static void hidinput_close(struct input_dev *dev)
 {
 	struct hid_device *hid = input_get_drvdata(dev);
 
-	hid->hid_close(hid);
+	hid->ll_driver->close(hid);
 }
 
 /*
@@ -977,7 +700,7 @@
  * Read all reports and initialize the absolute field values.
  */
 
-int hidinput_connect(struct hid_device *hid)
+int hidinput_connect(struct hid_device *hid, unsigned int force)
 {
 	struct hid_report *report;
 	struct hid_input *hidinput = NULL;
@@ -985,19 +708,20 @@
 	int i, j, k;
 	int max_report_type = HID_OUTPUT_REPORT;
 
-	if (hid->quirks & HID_QUIRK_IGNORE_HIDINPUT)
-		return -1;
-
 	INIT_LIST_HEAD(&hid->inputs);
 
-	for (i = 0; i < hid->maxcollection; i++)
-		if (hid->collection[i].type == HID_COLLECTION_APPLICATION ||
-		    hid->collection[i].type == HID_COLLECTION_PHYSICAL)
-			if (IS_INPUT_APPLICATION(hid->collection[i].usage))
-				break;
+	if (!force) {
+		for (i = 0; i < hid->maxcollection; i++) {
+			struct hid_collection *col = &hid->collection[i];
+			if (col->type == HID_COLLECTION_APPLICATION ||
+					col->type == HID_COLLECTION_PHYSICAL)
+				if (IS_INPUT_APPLICATION(col->usage))
+					break;
+		}
 
-	if (i == hid->maxcollection && (hid->quirks & HID_QUIRK_HIDINPUT) == 0)
-		return -1;
+		if (i == hid->maxcollection)
+			return -1;
+	}
 
 	if (hid->quirks & HID_QUIRK_SKIP_OUTPUT_REPORTS)
 		max_report_type = HID_INPUT_REPORT;
@@ -1019,7 +743,8 @@
 				}
 
 				input_set_drvdata(input_dev, hid);
-				input_dev->event = hid->hidinput_input_event;
+				input_dev->event =
+					hid->ll_driver->hidinput_input_event;
 				input_dev->open = hidinput_open;
 				input_dev->close = hidinput_close;
 				input_dev->setkeycode = hidinput_setkeycode;
@@ -1032,7 +757,7 @@
 				input_dev->id.vendor  = hid->vendor;
 				input_dev->id.product = hid->product;
 				input_dev->id.version = hid->version;
-				input_dev->dev.parent = hid->dev;
+				input_dev->dev.parent = hid->dev.parent;
 				hidinput->input = input_dev;
 				list_add_tail(&hidinput->list, &hid->inputs);
 			}
diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c
new file mode 100644
index 0000000..406d8c8
--- /dev/null
+++ b/drivers/hid/hid-lg.c
@@ -0,0 +1,342 @@
+/*
+ *  HID driver for some logitech "special" devices
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby
+ */
+
+/*
+ * 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/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+#include "hid-lg.h"
+
+#define LG_RDESC		0x001
+#define LG_BAD_RELATIVE_KEYS	0x002
+#define LG_DUPLICATE_USAGES	0x004
+#define LG_RESET_LEDS		0x008
+#define LG_EXPANDED_KEYMAP	0x010
+#define LG_IGNORE_DOUBLED_WHEEL	0x020
+#define LG_WIRELESS		0x040
+#define LG_INVERT_HWHEEL	0x080
+#define LG_NOGET		0x100
+#define LG_FF			0x200
+#define LG_FF2			0x400
+
+/*
+ * Certain Logitech keyboards send in report #3 keys which are far
+ * above the logical maximum described in descriptor. This extends
+ * the original value of 0x28c of logical maximum to 0x104d
+ */
+static void lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+		unsigned int rsize)
+{
+	unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
+
+	if ((quirks & LG_RDESC) && rsize >= 90 && rdesc[83] == 0x26 &&
+			rdesc[84] == 0x8c && rdesc[85] == 0x02) {
+		dev_info(&hdev->dev, "fixing up Logitech keyboard report "
+				"descriptor\n");
+		rdesc[84] = rdesc[89] = 0x4d;
+		rdesc[85] = rdesc[90] = 0x10;
+	}
+}
+
+#define lg_map_key_clear(c)	hid_map_usage_clear(hi, usage, bit, max, \
+		EV_KEY, (c))
+
+static int lg_ultrax_remote_mapping(struct hid_input *hi,
+		struct hid_usage *usage, unsigned long **bit, int *max)
+{
+	if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR)
+		return 0;
+
+	set_bit(EV_REP, hi->input->evbit);
+	switch (usage->hid & HID_USAGE) {
+	/* Reported on Logitech Ultra X Media Remote */
+	case 0x004: lg_map_key_clear(KEY_AGAIN);	break;
+	case 0x00d: lg_map_key_clear(KEY_HOME);		break;
+	case 0x024: lg_map_key_clear(KEY_SHUFFLE);	break;
+	case 0x025: lg_map_key_clear(KEY_TV);		break;
+	case 0x026: lg_map_key_clear(KEY_MENU);		break;
+	case 0x031: lg_map_key_clear(KEY_AUDIO);	break;
+	case 0x032: lg_map_key_clear(KEY_TEXT);		break;
+	case 0x033: lg_map_key_clear(KEY_LAST);		break;
+	case 0x047: lg_map_key_clear(KEY_MP3);		break;
+	case 0x048: lg_map_key_clear(KEY_DVD);		break;
+	case 0x049: lg_map_key_clear(KEY_MEDIA);	break;
+	case 0x04a: lg_map_key_clear(KEY_VIDEO);	break;
+	case 0x04b: lg_map_key_clear(KEY_ANGLE);	break;
+	case 0x04c: lg_map_key_clear(KEY_LANGUAGE);	break;
+	case 0x04d: lg_map_key_clear(KEY_SUBTITLE);	break;
+	case 0x051: lg_map_key_clear(KEY_RED);		break;
+	case 0x052: lg_map_key_clear(KEY_CLOSE);	break;
+
+	default:
+		return 0;
+	}
+	return 1;
+}
+
+static int lg_wireless_mapping(struct hid_input *hi, struct hid_usage *usage,
+		unsigned long **bit, int *max)
+{
+	if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
+		return 0;
+
+	switch (usage->hid & HID_USAGE) {
+	case 0x1001: lg_map_key_clear(KEY_MESSENGER);		break;
+	case 0x1003: lg_map_key_clear(KEY_SOUND);		break;
+	case 0x1004: lg_map_key_clear(KEY_VIDEO);		break;
+	case 0x1005: lg_map_key_clear(KEY_AUDIO);		break;
+	case 0x100a: lg_map_key_clear(KEY_DOCUMENTS);		break;
+	case 0x1011: lg_map_key_clear(KEY_PREVIOUSSONG);	break;
+	case 0x1012: lg_map_key_clear(KEY_NEXTSONG);		break;
+	case 0x1013: lg_map_key_clear(KEY_CAMERA);		break;
+	case 0x1014: lg_map_key_clear(KEY_MESSENGER);		break;
+	case 0x1015: lg_map_key_clear(KEY_RECORD);		break;
+	case 0x1016: lg_map_key_clear(KEY_PLAYER);		break;
+	case 0x1017: lg_map_key_clear(KEY_EJECTCD);		break;
+	case 0x1018: lg_map_key_clear(KEY_MEDIA);		break;
+	case 0x1019: lg_map_key_clear(KEY_PROG1);		break;
+	case 0x101a: lg_map_key_clear(KEY_PROG2);		break;
+	case 0x101b: lg_map_key_clear(KEY_PROG3);		break;
+	case 0x101f: lg_map_key_clear(KEY_ZOOMIN);		break;
+	case 0x1020: lg_map_key_clear(KEY_ZOOMOUT);		break;
+	case 0x1021: lg_map_key_clear(KEY_ZOOMRESET);		break;
+	case 0x1023: lg_map_key_clear(KEY_CLOSE);		break;
+	case 0x1027: lg_map_key_clear(KEY_MENU);		break;
+	/* this one is marked as 'Rotate' */
+	case 0x1028: lg_map_key_clear(KEY_ANGLE);		break;
+	case 0x1029: lg_map_key_clear(KEY_SHUFFLE);		break;
+	case 0x102a: lg_map_key_clear(KEY_BACK);		break;
+	case 0x102b: lg_map_key_clear(KEY_CYCLEWINDOWS);	break;
+	case 0x1041: lg_map_key_clear(KEY_BATTERY);		break;
+	case 0x1042: lg_map_key_clear(KEY_WORDPROCESSOR);	break;
+	case 0x1043: lg_map_key_clear(KEY_SPREADSHEET);		break;
+	case 0x1044: lg_map_key_clear(KEY_PRESENTATION);	break;
+	case 0x1045: lg_map_key_clear(KEY_UNDO);		break;
+	case 0x1046: lg_map_key_clear(KEY_REDO);		break;
+	case 0x1047: lg_map_key_clear(KEY_PRINT);		break;
+	case 0x1048: lg_map_key_clear(KEY_SAVE);		break;
+	case 0x1049: lg_map_key_clear(KEY_PROG1);		break;
+	case 0x104a: lg_map_key_clear(KEY_PROG2);		break;
+	case 0x104b: lg_map_key_clear(KEY_PROG3);		break;
+	case 0x104c: lg_map_key_clear(KEY_PROG4);		break;
+
+	default:
+		return 0;
+	}
+	return 1;
+}
+
+static int lg_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+		struct hid_field *field, struct hid_usage *usage,
+		unsigned long **bit, int *max)
+{
+	/* extended mapping for certain Logitech hardware (Logitech cordless
+	   desktop LX500) */
+	static const u8 e_keymap[] = {
+		  0,216,  0,213,175,156,  0,  0,  0,  0,
+		144,  0,  0,  0,  0,  0,  0,  0,  0,212,
+		174,167,152,161,112,  0,  0,  0,154,  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,183,184,185,186,187,
+		188,189,190,191,192,193,194,  0,  0,  0
+	};
+	unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
+	unsigned int hid = usage->hid;
+
+	if (hdev->product == USB_DEVICE_ID_LOGITECH_RECEIVER &&
+			lg_ultrax_remote_mapping(hi, usage, bit, max))
+		return 1;
+
+	if ((quirks & LG_WIRELESS) && lg_wireless_mapping(hi, usage, bit, max))
+		return 1;
+
+	if ((hid & HID_USAGE_PAGE) != HID_UP_BUTTON)
+		return 0;
+
+	hid &= HID_USAGE;
+
+	/* Special handling for Logitech Cordless Desktop */
+	if (field->application == HID_GD_MOUSE) {
+		if ((quirks & LG_IGNORE_DOUBLED_WHEEL) &&
+				(hid == 7 || hid == 8))
+			return -1;
+	} else {
+		if ((quirks & LG_EXPANDED_KEYMAP) &&
+				hid < ARRAY_SIZE(e_keymap) &&
+				e_keymap[hid] != 0) {
+			hid_map_usage(hi, usage, bit, max, EV_KEY,
+					e_keymap[hid]);
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi,
+		struct hid_field *field, struct hid_usage *usage,
+		unsigned long **bit, int *max)
+{
+	unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
+
+	if ((quirks & LG_BAD_RELATIVE_KEYS) && usage->type == EV_KEY &&
+			(field->flags & HID_MAIN_ITEM_RELATIVE))
+		field->flags &= ~HID_MAIN_ITEM_RELATIVE;
+
+	if ((quirks & LG_DUPLICATE_USAGES) && (usage->type == EV_KEY ||
+			 usage->type == EV_REL || usage->type == EV_ABS))
+		clear_bit(usage->code, *bit);
+
+	return 0;
+}
+
+static int lg_event(struct hid_device *hdev, struct hid_field *field,
+		struct hid_usage *usage, __s32 value)
+{
+	unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
+
+	if ((quirks & LG_INVERT_HWHEEL) && usage->code == REL_HWHEEL) {
+		input_event(field->hidinput->input, usage->type, usage->code,
+				-value);
+		return 1;
+	}
+
+	return 0;
+}
+
+static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+	unsigned long quirks = id->driver_data;
+	unsigned int connect_mask = HID_CONNECT_DEFAULT;
+	int ret;
+
+	hid_set_drvdata(hdev, (void *)quirks);
+
+	if (quirks & LG_NOGET)
+		hdev->quirks |= HID_QUIRK_NOGET;
+
+	ret = hid_parse(hdev);
+	if (ret) {
+		dev_err(&hdev->dev, "parse failed\n");
+		goto err_free;
+	}
+
+	if (quirks & (LG_FF | LG_FF2))
+		connect_mask &= ~HID_CONNECT_FF;
+
+	ret = hid_hw_start(hdev, connect_mask);
+	if (ret) {
+		dev_err(&hdev->dev, "hw start failed\n");
+		goto err_free;
+	}
+
+	if (quirks & LG_RESET_LEDS)
+		usbhid_set_leds(hdev);
+
+	if (quirks & LG_FF)
+		lgff_init(hdev);
+	if (quirks & LG_FF2)
+		lg2ff_init(hdev);
+
+	return 0;
+err_free:
+	return ret;
+}
+
+static const struct hid_device_id lg_devices[] = {
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER),
+		.driver_data = LG_RDESC | LG_WIRELESS },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER),
+		.driver_data = LG_RDESC | LG_WIRELESS },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2),
+		.driver_data = LG_RDESC | LG_WIRELESS },
+
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER),
+		.driver_data = LG_BAD_RELATIVE_KEYS },
+
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP),
+		.driver_data = LG_DUPLICATE_USAGES },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE),
+		.driver_data = LG_DUPLICATE_USAGES },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI),
+		.driver_data = LG_DUPLICATE_USAGES },
+
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_KBD),
+		.driver_data = LG_RESET_LEDS },
+
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD),
+		.driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500),
+		.driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP },
+
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_LX3),
+		.driver_data = LG_INVERT_HWHEEL },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_V150),
+		.driver_data = LG_INVERT_HWHEEL },
+
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D),
+		.driver_data = LG_NOGET },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL),
+		.driver_data = LG_NOGET | LG_FF },
+
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD),
+		.driver_data = LG_FF },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2),
+		.driver_data = LG_FF },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D),
+		.driver_data = LG_FF },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO),
+		.driver_data = LG_FF },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL),
+		.driver_data = LG_FF },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2),
+		.driver_data = LG_FF },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2),
+		.driver_data = LG_FF2 },
+	{ }
+};
+MODULE_DEVICE_TABLE(hid, lg_devices);
+
+static struct hid_driver lg_driver = {
+	.name = "logitech",
+	.id_table = lg_devices,
+	.report_fixup = lg_report_fixup,
+	.input_mapping = lg_input_mapping,
+	.input_mapped = lg_input_mapped,
+	.event = lg_event,
+	.probe = lg_probe,
+};
+
+static int lg_init(void)
+{
+	return hid_register_driver(&lg_driver);
+}
+
+static void lg_exit(void)
+{
+	hid_unregister_driver(&lg_driver);
+}
+
+module_init(lg_init);
+module_exit(lg_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(logitech);
diff --git a/drivers/hid/hid-lg.h b/drivers/hid/hid-lg.h
new file mode 100644
index 0000000..27ae750
--- /dev/null
+++ b/drivers/hid/hid-lg.h
@@ -0,0 +1,18 @@
+#ifndef __HID_LG_H
+#define __HID_LG_H
+
+#include <linux/autoconf.h>
+
+#ifdef CONFIG_LOGITECH_FF
+int lgff_init(struct hid_device *hdev);
+#else
+static inline int lgff_init(struct hid_device *hdev) { return -1; }
+#endif
+
+#ifdef CONFIG_LOGIRUMBLEPAD2_FF
+int lg2ff_init(struct hid_device *hdev);
+#else
+static inline int lg2ff_init(struct hid_device *hdev) { return -1; }
+#endif
+
+#endif
diff --git a/drivers/hid/usbhid/hid-lg2ff.c b/drivers/hid/hid-lg2ff.c
similarity index 89%
rename from drivers/hid/usbhid/hid-lg2ff.c
rename to drivers/hid/hid-lg2ff.c
index d469bd0..4e6dc6e 100644
--- a/drivers/hid/usbhid/hid-lg2ff.c
+++ b/drivers/hid/hid-lg2ff.c
@@ -24,7 +24,9 @@
 #include <linux/input.h>
 #include <linux/usb.h>
 #include <linux/hid.h>
-#include "usbhid.h"
+
+#include "usbhid/usbhid.h"
+#include "hid-lg.h"
 
 struct lg2ff_device {
 	struct hid_report *report;
@@ -57,7 +59,7 @@
 	return 0;
 }
 
-int hid_lg2ff_init(struct hid_device *hid)
+int lg2ff_init(struct hid_device *hid)
 {
 	struct lg2ff_device *lg2ff;
 	struct hid_report *report;
@@ -69,18 +71,18 @@
 	int error;
 
 	if (list_empty(report_list)) {
-		printk(KERN_ERR "hid-lg2ff: no output report found\n");
+		dev_err(&hid->dev, "no output report found\n");
 		return -ENODEV;
 	}
 
 	report = list_entry(report_list->next, struct hid_report, list);
 
 	if (report->maxfield < 1) {
-		printk(KERN_ERR "hid-lg2ff: output report is empty\n");
+		dev_err(&hid->dev, "output report is empty\n");
 		return -ENODEV;
 	}
 	if (report->field[0]->report_count < 7) {
-		printk(KERN_ERR "hid-lg2ff: not enough values in the field\n");
+		dev_err(&hid->dev, "not enough values in the field\n");
 		return -ENODEV;
 	}
 
@@ -107,7 +109,7 @@
 
 	usbhid_submit_report(hid, report, USB_DIR_OUT);
 
-	printk(KERN_INFO "Force feedback for Logitech Rumblepad 2 by "
+	dev_info(&hid->dev, "Force feedback for Logitech Rumblepad 2 by "
 	       "Anssi Hannula <anssi.hannula@gmail.com>\n");
 
 	return 0;
diff --git a/drivers/hid/usbhid/hid-lgff.c b/drivers/hid/hid-lgff.c
similarity index 81%
rename from drivers/hid/usbhid/hid-lgff.c
rename to drivers/hid/hid-lgff.c
index 4b7ab6a..51aff08 100644
--- a/drivers/hid/usbhid/hid-lgff.c
+++ b/drivers/hid/hid-lgff.c
@@ -30,7 +30,9 @@
 #include <linux/input.h>
 #include <linux/usb.h>
 #include <linux/hid.h>
-#include "usbhid.h"
+
+#include "usbhid/usbhid.h"
+#include "hid-lg.h"
 
 struct dev_type {
 	u16 idVendor;
@@ -48,6 +50,12 @@
 	-1
 };
 
+static const signed short ff_wheel[] = {
+	FF_CONSTANT,
+	FF_AUTOCENTER,
+	-1
+};
+
 static const struct dev_type devices[] = {
 	{ 0x046d, 0xc211, ff_rumble },
 	{ 0x046d, 0xc219, ff_rumble },
@@ -55,7 +63,7 @@
 	{ 0x046d, 0xc286, ff_joystick },
 	{ 0x046d, 0xc294, ff_joystick },
 	{ 0x046d, 0xc295, ff_joystick },
-	{ 0x046d, 0xca03, ff_joystick },
+	{ 0x046d, 0xca03, ff_wheel },
 };
 
 static int hid_lgff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
@@ -100,7 +108,24 @@
 	return 0;
 }
 
-int hid_lgff_init(struct hid_device* hid)
+static void hid_lgff_set_autocenter(struct input_dev *dev, u16 magnitude)
+{
+	struct hid_device *hid = input_get_drvdata(dev);
+	struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
+	struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
+	__s32 *value = report->field[0]->value;
+	magnitude = (magnitude >> 12) & 0xf;
+	*value++ = 0xfe;
+	*value++ = 0x0d;
+	*value++ = magnitude;   /* clockwise strength */
+	*value++ = magnitude;   /* counter-clockwise strength */
+	*value++ = 0x80;
+	*value++ = 0x00;
+	*value = 0x00;
+	usbhid_submit_report(hid, report, USB_DIR_OUT);
+}
+
+int lgff_init(struct hid_device* hid)
 {
 	struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
 	struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
@@ -145,6 +170,9 @@
 	if (error)
 		return error;
 
+	if ( test_bit(FF_AUTOCENTER, dev->ffbit) )
+		dev->ff->set_autocenter = hid_lgff_set_autocenter;
+
 	printk(KERN_INFO "Force feedback for Logitech force feedback devices by Johann Deneux <johann.deneux@it.uu.se>\n");
 
 	return 0;
diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c
new file mode 100644
index 0000000..d718b16
--- /dev/null
+++ b/drivers/hid/hid-microsoft.c
@@ -0,0 +1,219 @@
+/*
+ *  HID driver for some microsoft "special" devices
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby
+ */
+
+/*
+ * 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/device.h>
+#include <linux/input.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+#define MS_HIDINPUT	0x01
+#define MS_ERGONOMY	0x02
+#define MS_PRESENTER	0x04
+#define MS_RDESC	0x08
+#define MS_NOGET	0x10
+
+/*
+ * Microsoft Wireless Desktop Receiver (Model 1028) has several
+ * 'Usage Min/Max' where it ought to have 'Physical Min/Max'
+ */
+static void ms_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+		unsigned int rsize)
+{
+	unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
+
+	if ((quirks & MS_RDESC) && rsize == 571 && rdesc[284] == 0x19 &&
+			rdesc[286] == 0x2a && rdesc[304] == 0x19 &&
+			rdesc[306] == 0x29 && rdesc[352] == 0x1a &&
+			rdesc[355] == 0x2a && rdesc[557] == 0x19 &&
+			rdesc[559] == 0x29) {
+		dev_info(&hdev->dev, "fixing up Microsoft Wireless Receiver "
+				"Model 1028 report descriptor\n");
+		rdesc[284] = rdesc[304] = rdesc[557] = 0x35;
+		rdesc[352] = 0x36;
+		rdesc[286] = rdesc[355] = 0x46;
+		rdesc[306] = rdesc[559] = 0x45;
+	}
+}
+
+#define ms_map_key_clear(c)	hid_map_usage_clear(hi, usage, bit, max, \
+					EV_KEY, (c))
+static int ms_ergonomy_kb_quirk(struct hid_input *hi, struct hid_usage *usage,
+		unsigned long **bit, int *max)
+{
+	struct input_dev *input = hi->input;
+
+	switch (usage->hid & HID_USAGE) {
+	case 0xfd06: ms_map_key_clear(KEY_CHAT);	break;
+	case 0xfd07: ms_map_key_clear(KEY_PHONE);	break;
+	case 0xff05:
+		set_bit(EV_REP, input->evbit);
+		ms_map_key_clear(KEY_F13);
+		set_bit(KEY_F14, input->keybit);
+		set_bit(KEY_F15, input->keybit);
+		set_bit(KEY_F16, input->keybit);
+		set_bit(KEY_F17, input->keybit);
+		set_bit(KEY_F18, input->keybit);
+	default:
+		return 0;
+	}
+	return 1;
+}
+
+static int ms_presenter_8k_quirk(struct hid_input *hi, struct hid_usage *usage,
+		unsigned long **bit, int *max)
+{
+	set_bit(EV_REP, hi->input->evbit);
+	switch (usage->hid & HID_USAGE) {
+	case 0xfd08: ms_map_key_clear(KEY_FORWARD);	break;
+	case 0xfd09: ms_map_key_clear(KEY_BACK);	break;
+	case 0xfd0b: ms_map_key_clear(KEY_PLAYPAUSE);	break;
+	case 0xfd0e: ms_map_key_clear(KEY_CLOSE);	break;
+	case 0xfd0f: ms_map_key_clear(KEY_PLAY);	break;
+	default:
+		return 0;
+	}
+	return 1;
+}
+
+static int ms_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+		struct hid_field *field, struct hid_usage *usage,
+		unsigned long **bit, int *max)
+{
+	unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
+
+	if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR)
+		return 0;
+
+	if (quirks & MS_ERGONOMY) {
+		int ret = ms_ergonomy_kb_quirk(hi, usage, bit, max);
+		if (ret)
+			return ret;
+	}
+
+	if ((quirks & MS_PRESENTER) &&
+			ms_presenter_8k_quirk(hi, usage, bit, max))
+		return 1;
+
+	return 0;
+}
+
+static int ms_event(struct hid_device *hdev, struct hid_field *field,
+		struct hid_usage *usage, __s32 value)
+{
+	unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
+
+	if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput ||
+			!usage->type)
+		return 0;
+
+	/* Handling MS keyboards special buttons */
+	if (quirks & MS_ERGONOMY && usage->hid == (HID_UP_MSVENDOR | 0xff05)) {
+		struct input_dev *input = field->hidinput->input;
+		static unsigned int last_key = 0;
+		unsigned int key = 0;
+		switch (value) {
+		case 0x01: key = KEY_F14; break;
+		case 0x02: key = KEY_F15; break;
+		case 0x04: key = KEY_F16; break;
+		case 0x08: key = KEY_F17; break;
+		case 0x10: key = KEY_F18; break;
+		}
+		if (key) {
+			input_event(input, usage->type, key, 1);
+			last_key = key;
+		} else
+			input_event(input, usage->type, last_key, 0);
+
+		return 1;
+	}
+
+	return 0;
+}
+
+static int ms_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+	unsigned long quirks = id->driver_data;
+	int ret;
+
+	hid_set_drvdata(hdev, (void *)quirks);
+
+	if (quirks & MS_NOGET)
+		hdev->quirks |= HID_QUIRK_NOGET;
+
+	ret = hid_parse(hdev);
+	if (ret) {
+		dev_err(&hdev->dev, "parse failed\n");
+		goto err_free;
+	}
+
+	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT | ((quirks & MS_HIDINPUT) ?
+				HID_CONNECT_HIDINPUT_FORCE : 0));
+	if (ret) {
+		dev_err(&hdev->dev, "hw start failed\n");
+		goto err_free;
+	}
+
+	return 0;
+err_free:
+	return ret;
+}
+
+static const struct hid_device_id ms_devices[] = {
+	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV),
+		.driver_data = MS_HIDINPUT },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K),
+		.driver_data = MS_ERGONOMY },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_LK6K),
+		.driver_data = MS_ERGONOMY | MS_RDESC },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_USB),
+		.driver_data = MS_PRESENTER },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0),
+		.driver_data = MS_NOGET },
+
+	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT),
+		.driver_data = MS_PRESENTER },
+	{ }
+};
+MODULE_DEVICE_TABLE(hid, ms_devices);
+
+static struct hid_driver ms_driver = {
+	.name = "microsoft",
+	.id_table = ms_devices,
+	.report_fixup = ms_report_fixup,
+	.input_mapping = ms_input_mapping,
+	.event = ms_event,
+	.probe = ms_probe,
+};
+
+static int ms_init(void)
+{
+	return hid_register_driver(&ms_driver);
+}
+
+static void ms_exit(void)
+{
+	hid_unregister_driver(&ms_driver);
+}
+
+module_init(ms_init);
+module_exit(ms_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(microsoft);
diff --git a/drivers/hid/hid-monterey.c b/drivers/hid/hid-monterey.c
new file mode 100644
index 0000000..f3a85a0
--- /dev/null
+++ b/drivers/hid/hid-monterey.c
@@ -0,0 +1,82 @@
+/*
+ *  HID driver for some monterey "special" devices
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby
+ */
+
+/*
+ * 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/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+static void mr_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+		unsigned int rsize)
+{
+	if (rsize >= 30 && rdesc[29] == 0x05 && rdesc[30] == 0x09) {
+		dev_info(&hdev->dev, "fixing up button/consumer in HID report "
+				"descriptor\n");
+		rdesc[30] = 0x0c;
+	}
+}
+
+#define mr_map_key_clear(c)	hid_map_usage_clear(hi, usage, bit, max, \
+					EV_KEY, (c))
+static int mr_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+		struct hid_field *field, struct hid_usage *usage,
+		unsigned long **bit, int *max)
+{
+	if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
+		return 0;
+
+	switch (usage->hid & HID_USAGE) {
+	case 0x156: mr_map_key_clear(KEY_WORDPROCESSOR);	break;
+	case 0x157: mr_map_key_clear(KEY_SPREADSHEET);		break;
+	case 0x158: mr_map_key_clear(KEY_PRESENTATION);		break;
+	case 0x15c: mr_map_key_clear(KEY_STOP);			break;
+	default:
+		return 0;
+	}
+	return 1;
+}
+
+static const struct hid_device_id mr_devices[] = {
+	{ HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) },
+	{ }
+};
+MODULE_DEVICE_TABLE(hid, mr_devices);
+
+static struct hid_driver mr_driver = {
+	.name = "monterey",
+	.id_table = mr_devices,
+	.report_fixup = mr_report_fixup,
+	.input_mapping = mr_input_mapping,
+};
+
+static int mr_init(void)
+{
+	return hid_register_driver(&mr_driver);
+}
+
+static void mr_exit(void)
+{
+	hid_unregister_driver(&mr_driver);
+}
+
+module_init(mr_init);
+module_exit(mr_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(monterey);
diff --git a/drivers/hid/hid-petalynx.c b/drivers/hid/hid-petalynx.c
new file mode 100644
index 0000000..10945fe
--- /dev/null
+++ b/drivers/hid/hid-petalynx.c
@@ -0,0 +1,122 @@
+/*
+ *  HID driver for some petalynx "special" devices
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby
+ */
+
+/*
+ * 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/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+/* Petalynx Maxter Remote has maximum for consumer page set too low */
+static void pl_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+		unsigned int rsize)
+{
+	if (rsize >= 60 && rdesc[39] == 0x2a && rdesc[40] == 0xf5 &&
+			rdesc[41] == 0x00 && rdesc[59] == 0x26 &&
+			rdesc[60] == 0xf9 && rdesc[61] == 0x00) {
+		dev_info(&hdev->dev, "fixing up Petalynx Maxter Remote report "
+				"descriptor\n");
+		rdesc[60] = 0xfa;
+		rdesc[40] = 0xfa;
+	}
+}
+
+#define pl_map_key_clear(c)	hid_map_usage_clear(hi, usage, bit, max, \
+					EV_KEY, (c))
+static int pl_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+		struct hid_field *field, struct hid_usage *usage,
+		unsigned long **bit, int *max)
+{
+	if ((usage->hid & HID_USAGE_PAGE) == HID_UP_LOGIVENDOR) {
+		switch (usage->hid & HID_USAGE) {
+		case 0x05a: pl_map_key_clear(KEY_TEXT);		break;
+		case 0x05b: pl_map_key_clear(KEY_RED);		break;
+		case 0x05c: pl_map_key_clear(KEY_GREEN);	break;
+		case 0x05d: pl_map_key_clear(KEY_YELLOW);	break;
+		case 0x05e: pl_map_key_clear(KEY_BLUE);		break;
+		default:
+			return 0;
+		}
+		return 1;
+	}
+
+	if ((usage->hid & HID_USAGE_PAGE) == HID_UP_CONSUMER) {
+		switch (usage->hid & HID_USAGE) {
+		case 0x0f6: pl_map_key_clear(KEY_NEXT);		break;
+		case 0x0fa: pl_map_key_clear(KEY_BACK);		break;
+		default:
+			return 0;
+		}
+		return 1;
+	}
+
+	return 0;
+}
+
+static int pl_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+	int ret;
+
+	hdev->quirks |= HID_QUIRK_NOGET;
+
+	ret = hid_parse(hdev);
+	if (ret) {
+		dev_err(&hdev->dev, "parse failed\n");
+		goto err_free;
+	}
+
+	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+	if (ret) {
+		dev_err(&hdev->dev, "hw start failed\n");
+		goto err_free;
+	}
+
+	return 0;
+err_free:
+	return ret;
+}
+
+static const struct hid_device_id pl_devices[] = {
+	{ HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) },
+	{ }
+};
+MODULE_DEVICE_TABLE(hid, pl_devices);
+
+static struct hid_driver pl_driver = {
+	.name = "petalynx",
+	.id_table = pl_devices,
+	.report_fixup = pl_report_fixup,
+	.input_mapping = pl_input_mapping,
+	.probe = pl_probe,
+};
+
+static int pl_init(void)
+{
+	return hid_register_driver(&pl_driver);
+}
+
+static void pl_exit(void)
+{
+	hid_unregister_driver(&pl_driver);
+}
+
+module_init(pl_init);
+module_exit(pl_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(petalynx);
diff --git a/drivers/hid/usbhid/hid-plff.c b/drivers/hid/hid-pl.c
similarity index 68%
rename from drivers/hid/usbhid/hid-plff.c
rename to drivers/hid/hid-pl.c
index 9eb83cf..acd8155 100644
--- a/drivers/hid/usbhid/hid-plff.c
+++ b/drivers/hid/hid-pl.c
@@ -9,7 +9,7 @@
  *   - contains two reports, one for each port (HID_QUIRK_MULTI_INPUT)
  *
  *  0e8f:0003 "GreenAsia Inc.    USB Joystick     "
- *   - tested with Köng Gaming gamepad
+ *   - tested with K??ng Gaming gamepad
  *
  *  Copyright (c) 2007 Anssi Hannula <anssi.hannula@gmail.com>
  */
@@ -38,7 +38,11 @@
 #include <linux/input.h>
 #include <linux/usb.h>
 #include <linux/hid.h>
-#include "usbhid.h"
+
+#include "hid-ids.h"
+
+#ifdef CONFIG_PANTHERLORD_FF
+#include "usbhid/usbhid.h"
 
 struct plff_device {
 	struct hid_report *report;
@@ -66,7 +70,7 @@
 	return 0;
 }
 
-int hid_plff_init(struct hid_device *hid)
+static int plff_init(struct hid_device *hid)
 {
 	struct plff_device *plff;
 	struct hid_report *report;
@@ -86,7 +90,7 @@
 	   currently unknown. */
 
 	if (list_empty(report_list)) {
-		printk(KERN_ERR "hid-plff: no output reports found\n");
+		dev_err(&hid->dev, "no output reports found\n");
 		return -ENODEV;
 	}
 
@@ -95,18 +99,19 @@
 		report_ptr = report_ptr->next;
 
 		if (report_ptr == report_list) {
-			printk(KERN_ERR "hid-plff: required output report is missing\n");
+			dev_err(&hid->dev, "required output report is "
+					"missing\n");
 			return -ENODEV;
 		}
 
 		report = list_entry(report_ptr, struct hid_report, list);
 		if (report->maxfield < 1) {
-			printk(KERN_ERR "hid-plff: no fields in the report\n");
+			dev_err(&hid->dev, "no fields in the report\n");
 			return -ENODEV;
 		}
 
 		if (report->field[0]->report_count < 4) {
-			printk(KERN_ERR "hid-plff: not enough values in the field\n");
+			dev_err(&hid->dev, "not enough values in the field\n");
 			return -ENODEV;
 		}
 
@@ -132,8 +137,70 @@
 		usbhid_submit_report(hid, plff->report, USB_DIR_OUT);
 	}
 
-	printk(KERN_INFO "hid-plff: Force feedback for PantherLord/GreenAsia "
+	dev_info(&hid->dev, "Force feedback for PantherLord/GreenAsia "
 	       "devices by Anssi Hannula <anssi.hannula@gmail.com>\n");
 
 	return 0;
 }
+#else
+static inline int plff_init(struct hid_device *hid)
+{
+	return 0;
+}
+#endif
+
+static int pl_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+	int ret;
+
+	if (id->driver_data)
+		hdev->quirks |= HID_QUIRK_MULTI_INPUT;
+
+	ret = hid_parse(hdev);
+	if (ret) {
+		dev_err(&hdev->dev, "parse failed\n");
+		goto err;
+	}
+
+	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
+	if (ret) {
+		dev_err(&hdev->dev, "hw start failed\n");
+		goto err;
+	}
+
+	plff_init(hdev);
+
+	return 0;
+err:
+	return ret;
+}
+
+static const struct hid_device_id pl_devices[] = {
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR),
+		.driver_data = 1 }, /* Twin USB Joystick */
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GREENASIA, 0x0003), }, /* GreenAsia Inc. USB Joystick */
+	{ }
+};
+MODULE_DEVICE_TABLE(hid, pl_devices);
+
+static struct hid_driver pl_driver = {
+	.name = "pantherlord",
+	.id_table = pl_devices,
+	.probe = pl_probe,
+};
+
+static int pl_init(void)
+{
+	return hid_register_driver(&pl_driver);
+}
+
+static void pl_exit(void)
+{
+	hid_unregister_driver(&pl_driver);
+}
+
+module_init(pl_init);
+module_exit(pl_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(pantherlord);
diff --git a/drivers/hid/hid-samsung.c b/drivers/hid/hid-samsung.c
new file mode 100644
index 0000000..15f3c04
--- /dev/null
+++ b/drivers/hid/hid-samsung.c
@@ -0,0 +1,100 @@
+/*
+ *  HID driver for some samsung "special" devices
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby
+ */
+
+/*
+ * 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/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+/*
+ * Samsung IrDA remote controller (reports as Cypress USB Mouse).
+ *
+ * Vendor specific report #4 has a size of 48 bit,
+ * and therefore is not accepted when inspecting the descriptors.
+ * As a workaround we reinterpret the report as:
+ *   Variable type, count 6, size 8 bit, log. maximum 255
+ * The burden to reconstruct the data is moved into user space.
+ */
+static void samsung_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+		unsigned int rsize)
+{
+	if (rsize >= 182 && rdesc[175] == 0x25 && rdesc[176] == 0x40 &&
+			rdesc[177] == 0x75 && rdesc[178] == 0x30 &&
+			rdesc[179] == 0x95 && rdesc[180] == 0x01 &&
+			rdesc[182] == 0x40) {
+		dev_info(&hdev->dev, "fixing up Samsung IrDA report "
+				"descriptor\n");
+		rdesc[176] = 0xff;
+		rdesc[178] = 0x08;
+		rdesc[180] = 0x06;
+		rdesc[182] = 0x42;
+	}
+}
+
+static int samsung_probe(struct hid_device *hdev,
+		const struct hid_device_id *id)
+{
+	int ret;
+
+	ret = hid_parse(hdev);
+	if (ret) {
+		dev_err(&hdev->dev, "parse failed\n");
+		goto err_free;
+	}
+
+	ret = hid_hw_start(hdev, (HID_CONNECT_DEFAULT & ~HID_CONNECT_HIDINPUT) |
+			HID_CONNECT_HIDDEV_FORCE);
+	if (ret) {
+		dev_err(&hdev->dev, "hw start failed\n");
+		goto err_free;
+	}
+
+	return 0;
+err_free:
+	return ret;
+}
+
+static const struct hid_device_id samsung_devices[] = {
+	{ HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) },
+	{ }
+};
+MODULE_DEVICE_TABLE(hid, samsung_devices);
+
+static struct hid_driver samsung_driver = {
+	.name = "samsung",
+	.id_table = samsung_devices,
+	.report_fixup = samsung_report_fixup,
+	.probe = samsung_probe,
+};
+
+static int samsung_init(void)
+{
+	return hid_register_driver(&samsung_driver);
+}
+
+static void samsung_exit(void)
+{
+	hid_unregister_driver(&samsung_driver);
+}
+
+module_init(samsung_init);
+module_exit(samsung_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(samsung);
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
new file mode 100644
index 0000000..3af8095
--- /dev/null
+++ b/drivers/hid/hid-sony.c
@@ -0,0 +1,110 @@
+/*
+ *  HID driver for some sony "special" devices
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby
+ */
+
+/*
+ * 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/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+
+#include "hid-ids.h"
+
+/*
+ * Sending HID_REQ_GET_REPORT changes the operation mode of the ps3 controller
+ * to "operational".  Without this, the ps3 controller will not report any
+ * events.
+ */
+static int sony_set_operational(struct hid_device *hdev)
+{
+	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
+	struct usb_device *dev = interface_to_usbdev(intf);
+	__u16 ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
+	int ret;
+	char *buf = kmalloc(18, GFP_KERNEL);
+
+	if (!buf)
+		return -ENOMEM;
+
+	ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+				 HID_REQ_GET_REPORT,
+				 USB_DIR_IN | USB_TYPE_CLASS |
+				 USB_RECIP_INTERFACE,
+				 (3 << 8) | 0xf2, ifnum, buf, 17,
+				 USB_CTRL_GET_TIMEOUT);
+	if (ret < 0)
+		dev_err(&hdev->dev, "can't set operational mode\n");
+
+	kfree(buf);
+
+	return ret;
+}
+
+static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+	int ret;
+
+	ret = hid_parse(hdev);
+	if (ret) {
+		dev_err(&hdev->dev, "parse failed\n");
+		goto err_free;
+	}
+
+	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT |
+			HID_CONNECT_HIDDEV_FORCE);
+	if (ret) {
+		dev_err(&hdev->dev, "hw start failed\n");
+		goto err_free;
+	}
+
+	ret = sony_set_operational(hdev);
+	if (ret)
+		goto err_stop;
+
+	return 0;
+err_stop:
+	hid_hw_stop(hdev);
+err_free:
+	return ret;
+}
+
+static const struct hid_device_id sony_devices[] = {
+	{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
+	{ }
+};
+MODULE_DEVICE_TABLE(hid, sony_devices);
+
+static struct hid_driver sony_driver = {
+	.name = "sony",
+	.id_table = sony_devices,
+	.probe = sony_probe,
+};
+
+static int sony_init(void)
+{
+	return hid_register_driver(&sony_driver);
+}
+
+static void sony_exit(void)
+{
+	hid_unregister_driver(&sony_driver);
+}
+
+module_init(sony_init);
+module_exit(sony_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(sony);
diff --git a/drivers/hid/hid-sunplus.c b/drivers/hid/hid-sunplus.c
new file mode 100644
index 0000000..5ba68f7
--- /dev/null
+++ b/drivers/hid/hid-sunplus.c
@@ -0,0 +1,82 @@
+/*
+ *  HID driver for some sunplus "special" devices
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby
+ */
+
+/*
+ * 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/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+static void sp_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+		unsigned int rsize)
+{
+	if (rsize >= 107 && rdesc[104] == 0x26 && rdesc[105] == 0x80 &&
+			rdesc[106] == 0x03) {
+		dev_info(&hdev->dev, "fixing up Sunplus Wireless Desktop "
+				"report descriptor\n");
+		rdesc[105] = rdesc[110] = 0x03;
+		rdesc[106] = rdesc[111] = 0x21;
+	}
+}
+
+#define sp_map_key_clear(c)	hid_map_usage_clear(hi, usage, bit, max, \
+		EV_KEY, (c))
+static int sp_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+		struct hid_field *field, struct hid_usage *usage,
+		unsigned long **bit, int *max)
+{
+	if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
+		return 0;
+
+	switch (usage->hid & HID_USAGE) {
+	case 0x2003: sp_map_key_clear(KEY_ZOOMIN);		break;
+	case 0x2103: sp_map_key_clear(KEY_ZOOMOUT);	break;
+	default:
+		return 0;
+	}
+	return 1;
+}
+
+static const struct hid_device_id sp_devices[] = {
+	{ HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) },
+	{ }
+};
+MODULE_DEVICE_TABLE(hid, sp_devices);
+
+static struct hid_driver sp_driver = {
+	.name = "sunplus",
+	.id_table = sp_devices,
+	.report_fixup = sp_report_fixup,
+	.input_mapping = sp_input_mapping,
+};
+
+static int sp_init(void)
+{
+	return hid_register_driver(&sp_driver);
+}
+
+static void sp_exit(void)
+{
+	hid_unregister_driver(&sp_driver);
+}
+
+module_init(sp_init);
+module_exit(sp_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(sunplus);
diff --git a/drivers/hid/usbhid/hid-tmff.c b/drivers/hid/hid-tmff.c
similarity index 60%
rename from drivers/hid/usbhid/hid-tmff.c
rename to drivers/hid/hid-tmff.c
index 144578b..1b7cba0 100644
--- a/drivers/hid/usbhid/hid-tmff.c
+++ b/drivers/hid/hid-tmff.c
@@ -27,23 +27,17 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
+#include <linux/hid.h>
 #include <linux/input.h>
-
-#undef DEBUG
 #include <linux/usb.h>
 
-#include <linux/hid.h>
-#include "usbhid.h"
+#include "hid-ids.h"
+
+#include "usbhid/usbhid.h"
 
 /* Usages for thrustmaster devices I know about */
 #define THRUSTMASTER_USAGE_FF	(HID_UP_GENDESK | 0xbb)
 
-struct dev_type {
-	u16 idVendor;
-	u16 idProduct;
-	const signed short *ff;
-};
-
 static const signed short ff_rumble[] = {
 	FF_RUMBLE,
 	-1
@@ -54,21 +48,13 @@
 	-1
 };
 
-static const struct dev_type devices[] = {
-	{ 0x44f, 0xb300, ff_rumble },
-	{ 0x44f, 0xb304, ff_rumble },
-	{ 0x44f, 0xb651, ff_rumble },	/* FGT Rumble Force Wheel */
-	{ 0x44f, 0xb654, ff_joystick },	/* FGT Force Feedback Wheel */
-};
-
 struct tmff_device {
 	struct hid_report *report;
 	struct hid_field *ff_field;
 };
 
 /* Changes values from 0 to 0xffff into values from minimum to maximum */
-static inline int hid_tmff_scale_u16(unsigned int in,
-				int minimum, int maximum)
+static inline int tmff_scale_u16(unsigned int in, int minimum, int maximum)
 {
 	int ret;
 
@@ -81,8 +67,7 @@
 }
 
 /* Changes values from -0x80 to 0x7f into values from minimum to maximum */
-static inline int hid_tmff_scale_s8(int in,
-				    int minimum, int maximum)
+static inline int tmff_scale_s8(int in, int minimum, int maximum)
 {
 	int ret;
 
@@ -94,7 +79,8 @@
 	return ret;
 }
 
-static int hid_tmff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
+static int tmff_play(struct input_dev *dev, void *data,
+		struct ff_effect *effect)
 {
 	struct hid_device *hid = input_get_drvdata(dev);
 	struct tmff_device *tmff = data;
@@ -104,10 +90,10 @@
 
 	switch (effect->type) {
 	case FF_CONSTANT:
-		x = hid_tmff_scale_s8(effect->u.ramp.start_level,
+		x = tmff_scale_s8(effect->u.ramp.start_level,
 					ff_field->logical_minimum,
 					ff_field->logical_maximum);
-		y = hid_tmff_scale_s8(effect->u.ramp.end_level,
+		y = tmff_scale_s8(effect->u.ramp.end_level,
 					ff_field->logical_minimum,
 					ff_field->logical_maximum);
 
@@ -118,10 +104,10 @@
 		break;
 
 	case FF_RUMBLE:
-		left = hid_tmff_scale_u16(effect->u.rumble.weak_magnitude,
+		left = tmff_scale_u16(effect->u.rumble.weak_magnitude,
 					ff_field->logical_minimum,
 					ff_field->logical_maximum);
-		right = hid_tmff_scale_u16(effect->u.rumble.strong_magnitude,
+		right = tmff_scale_u16(effect->u.rumble.strong_magnitude,
 					ff_field->logical_minimum,
 					ff_field->logical_maximum);
 
@@ -134,14 +120,14 @@
 	return 0;
 }
 
-int hid_tmff_init(struct hid_device *hid)
+static int tmff_init(struct hid_device *hid, const signed short *ff_bits)
 {
 	struct tmff_device *tmff;
 	struct hid_report *report;
 	struct list_head *report_list;
-	struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
+	struct hid_input *hidinput = list_entry(hid->inputs.next,
+							struct hid_input, list);
 	struct input_dev *input_dev = hidinput->input;
-	const signed short *ff_bits = ff_joystick;
 	int error;
 	int i;
 
@@ -163,63 +149,121 @@
 			switch (field->usage[0].hid) {
 			case THRUSTMASTER_USAGE_FF:
 				if (field->report_count < 2) {
-					warn("ignoring FF field with report_count < 2");
+					dev_warn(&hid->dev, "ignoring FF field "
+						"with report_count < 2\n");
 					continue;
 				}
 
-				if (field->logical_maximum == field->logical_minimum) {
-					warn("ignoring FF field with logical_maximum == logical_minimum");
+				if (field->logical_maximum ==
+						field->logical_minimum) {
+					dev_warn(&hid->dev, "ignoring FF field "
+							"with logical_maximum "
+							"== logical_minimum\n");
 					continue;
 				}
 
 				if (tmff->report && tmff->report != report) {
-					warn("ignoring FF field in other report");
+					dev_warn(&hid->dev, "ignoring FF field "
+							"in other report\n");
 					continue;
 				}
 
 				if (tmff->ff_field && tmff->ff_field != field) {
-					warn("ignoring duplicate FF field");
+					dev_warn(&hid->dev, "ignoring "
+							"duplicate FF field\n");
 					continue;
 				}
 
 				tmff->report = report;
 				tmff->ff_field = field;
 
-				for (i = 0; i < ARRAY_SIZE(devices); i++) {
-					if (input_dev->id.vendor == devices[i].idVendor &&
-					    input_dev->id.product == devices[i].idProduct) {
-						ff_bits = devices[i].ff;
-						break;
-					}
-				}
-
 				for (i = 0; ff_bits[i] >= 0; i++)
 					set_bit(ff_bits[i], input_dev->ffbit);
 
 				break;
 
 			default:
-				warn("ignoring unknown output usage %08x", field->usage[0].hid);
+				dev_warn(&hid->dev, "ignoring unknown output "
+						"usage %08x\n",
+						field->usage[0].hid);
 				continue;
 			}
 		}
 	}
 
 	if (!tmff->report) {
-		err("cant find FF field in output reports\n");
+		dev_err(&hid->dev, "can't find FF field in output reports\n");
 		error = -ENODEV;
 		goto fail;
 	}
 
-	error = input_ff_create_memless(input_dev, tmff, hid_tmff_play);
+	error = input_ff_create_memless(input_dev, tmff, tmff_play);
 	if (error)
 		goto fail;
 
-	info("Force feedback for ThrustMaster devices by Zinx Verituse <zinx@epicsol.org>");
+	dev_info(&hid->dev, "force feedback for ThrustMaster devices by Zinx "
+			"Verituse <zinx@epicsol.org>");
 	return 0;
 
- fail:
+fail:
 	kfree(tmff);
 	return error;
 }
 
+static int tm_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+	int ret;
+
+	ret = hid_parse(hdev);
+	if (ret) {
+		dev_err(&hdev->dev, "parse failed\n");
+		goto err;
+	}
+
+	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
+	if (ret) {
+		dev_err(&hdev->dev, "hw start failed\n");
+		goto err;
+	}
+
+	tmff_init(hdev, (void *)id->driver_data);
+
+	return 0;
+err:
+	return ret;
+}
+
+static const struct hid_device_id tm_devices[] = {
+	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300),
+		.driver_data = (unsigned long)ff_rumble },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304),
+		.driver_data = (unsigned long)ff_rumble },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb651),	/* FGT Rumble Force Wheel */
+		.driver_data = (unsigned long)ff_rumble },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb654),	/* FGT Force Feedback Wheel */
+		.driver_data = (unsigned long)ff_joystick },
+	{ }
+};
+MODULE_DEVICE_TABLE(hid, tm_devices);
+
+static struct hid_driver tm_driver = {
+	.name = "thrustmaster",
+	.id_table = tm_devices,
+	.probe = tm_probe,
+};
+
+static int tm_init(void)
+{
+	return hid_register_driver(&tm_driver);
+}
+
+static void tm_exit(void)
+{
+	hid_unregister_driver(&tm_driver);
+}
+
+module_init(tm_init);
+module_exit(tm_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(thrustmaster);
diff --git a/drivers/hid/usbhid/hid-zpff.c b/drivers/hid/hid-zpff.c
similarity index 67%
rename from drivers/hid/usbhid/hid-zpff.c
rename to drivers/hid/hid-zpff.c
index 5a68827..ea82f37 100644
--- a/drivers/hid/usbhid/hid-zpff.c
+++ b/drivers/hid/hid-zpff.c
@@ -21,16 +21,19 @@
  */
 
 
+#include <linux/hid.h>
 #include <linux/input.h>
 #include <linux/usb.h>
-#include <linux/hid.h>
-#include "usbhid.h"
+
+#include "hid-ids.h"
+
+#include "usbhid/usbhid.h"
 
 struct zpff_device {
 	struct hid_report *report;
 };
 
-static int hid_zpff_play(struct input_dev *dev, void *data,
+static int zpff_play(struct input_dev *dev, void *data,
 			 struct ff_effect *effect)
 {
 	struct hid_device *hid = input_get_drvdata(dev);
@@ -58,7 +61,7 @@
 	return 0;
 }
 
-int hid_zpff_init(struct hid_device *hid)
+static int zpff_init(struct hid_device *hid)
 {
 	struct zpff_device *zpff;
 	struct hid_report *report;
@@ -70,14 +73,14 @@
 	int error;
 
 	if (list_empty(report_list)) {
-		printk(KERN_ERR "hid-zpff: no output report found\n");
+		dev_err(&hid->dev, "no output report found\n");
 		return -ENODEV;
 	}
 
 	report = list_entry(report_list->next, struct hid_report, list);
 
 	if (report->maxfield < 4) {
-		printk(KERN_ERR "hid-zpff: not enough fields in report\n");
+		dev_err(&hid->dev, "not enough fields in report\n");
 		return -ENODEV;
 	}
 
@@ -87,7 +90,7 @@
 
 	set_bit(FF_RUMBLE, dev->ffbit);
 
-	error = input_ff_create_memless(dev, zpff, hid_zpff_play);
+	error = input_ff_create_memless(dev, zpff, zpff_play);
 	if (error) {
 		kfree(zpff);
 		return error;
@@ -100,8 +103,60 @@
 	zpff->report->field[3]->value[0] = 0x00;
 	usbhid_submit_report(hid, zpff->report, USB_DIR_OUT);
 
-	printk(KERN_INFO "Force feedback for Zeroplus based devices by "
+	dev_info(&hid->dev, "force feedback for Zeroplus based devices by "
 	       "Anssi Hannula <anssi.hannula@gmail.com>\n");
 
 	return 0;
 }
+
+static int zp_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+	int ret;
+
+	ret = hid_parse(hdev);
+	if (ret) {
+		dev_err(&hdev->dev, "parse failed\n");
+		goto err;
+	}
+
+	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
+	if (ret) {
+		dev_err(&hdev->dev, "hw start failed\n");
+		goto err;
+	}
+
+	zpff_init(hdev);
+
+	return 0;
+err:
+	return ret;
+}
+
+static const struct hid_device_id zp_devices[] = {
+	{ HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0005) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0030) },
+	{ }
+};
+MODULE_DEVICE_TABLE(hid, zp_devices);
+
+static struct hid_driver zp_driver = {
+	.name = "zeroplus",
+	.id_table = zp_devices,
+	.probe = zp_probe,
+};
+
+static int zp_init(void)
+{
+	return hid_register_driver(&zp_driver);
+}
+
+static void zp_exit(void)
+{
+	hid_unregister_driver(&zp_driver);
+}
+
+module_init(zp_init);
+module_exit(zp_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(zeroplus);
diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c
index c40f040..497e0d1 100644
--- a/drivers/hid/hidraw.c
+++ b/drivers/hid/hidraw.c
@@ -113,7 +113,7 @@
 	if (!dev->hid_output_raw_report)
 		return -ENODEV;
 
-	if (count > HID_MIN_BUFFER_SIZE) {
+	if (count > HID_MAX_BUFFER_SIZE) {
 		printk(KERN_WARNING "hidraw: pid %d passed too large report\n",
 				task_pid_nr(current));
 		return -EINVAL;
@@ -181,7 +181,7 @@
 
 	dev = hidraw_table[minor];
 	if (!dev->open++)
-		dev->hid->hid_open(dev->hid);
+		dev->hid->ll_driver->open(dev->hid);
 
 out_unlock:
 	spin_unlock(&minors_lock);
@@ -207,7 +207,7 @@
 	dev = hidraw_table[minor];
 	if (!dev->open--) {
 		if (list->hidraw->exist)
-			dev->hid->hid_close(dev->hid);
+			dev->hid->ll_driver->close(dev->hid);
 		else
 			kfree(list->hidraw);
 	}
@@ -367,7 +367,7 @@
 	device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor));
 
 	if (hidraw->open) {
-		hid->hid_close(hid);
+		hid->ll_driver->close(hid);
 		wake_up_interruptible(&hidraw->wait);
 	} else {
 		kfree(hidraw);
diff --git a/drivers/hid/usbhid/Kconfig b/drivers/hid/usbhid/Kconfig
index 18f0910..5d9aa95 100644
--- a/drivers/hid/usbhid/Kconfig
+++ b/drivers/hid/usbhid/Kconfig
@@ -24,88 +24,13 @@
 comment "Input core support is needed for USB HID input layer or HIDBP support"
 	depends on USB_HID && INPUT=n
 
-config USB_HIDINPUT_POWERBOOK
-	bool "Enable support for Apple laptop/aluminum USB special keys"
-	default n
-	depends on USB_HID
-	help
-	  Say Y here if you want support for the special keys (Fn, Numlock) on
-	  Apple iBooks, PowerBooks, MacBooks, MacBook Pros and aluminum USB
-	  keyboards.
-
-	  If unsure, say N.
-
-config HID_FF
-	bool "Force feedback support (EXPERIMENTAL)"
-	depends on USB_HID && EXPERIMENTAL
-	help
-	  Say Y here is you want force feedback support for a few HID devices.
-	  See below for a list of supported devices.
-
-	  See <file:Documentation/input/ff.txt> for a description of the force
-	  feedback API.
-
-	  If unsure, say N.
-
 config HID_PID
 	bool "PID device support"
-	depends on HID_FF
 	help
 	  Say Y here if you have a PID-compliant device and wish to enable force
 	  feedback for it. Microsoft Sidewinder Force Feedback 2 is one of such
 	  devices.
 
-config LOGITECH_FF
-	bool "Logitech devices support"
-	depends on HID_FF
-	select INPUT_FF_MEMLESS if USB_HID
-	help
-	  Say Y here if you have one of these devices:
-	  - Logitech WingMan Cordless RumblePad
-	  - Logitech WingMan Cordless RumblePad 2
-	  - Logitech WingMan Force 3D
-	  - Logitech Formula Force EX
-	  - Logitech MOMO Force wheel
-
-	  and if you want to enable force feedback for them.
-	  Note: if you say N here, this device will still be supported, but without
-	  force feedback.
-
-config LOGIRUMBLEPAD2_FF
-	bool "Logitech Rumblepad 2 support"
-	depends on HID_FF
-	select INPUT_FF_MEMLESS if USB_HID
-	help
-	  Say Y here if you want to enable force feedback support for Logitech
-	  Rumblepad 2 devices.
-
-config PANTHERLORD_FF
-	bool "PantherLord/GreenAsia based device support"
-	depends on HID_FF
-	select INPUT_FF_MEMLESS if USB_HID
-	help
-	  Say Y here if you have a PantherLord/GreenAsia based game controller
-	  or adapter and want to enable force feedback support for it.
-
-config THRUSTMASTER_FF
-	bool "ThrustMaster devices support"
-	depends on HID_FF
-	select INPUT_FF_MEMLESS if USB_HID
-	help
-	  Say Y here if you have a THRUSTMASTER FireStore Dual Power 2 or
-	  a THRUSTMASTER Ferrari GT Rumble Force or Force Feedback Wheel,
-	  and want to enable force feedback support for it.
-	  Note: if you say N here, this device will still be supported, but without
-	  force feedback.
-
-config ZEROPLUS_FF
-	bool "Zeroplus based game controller support"
-	depends on HID_FF
-	select INPUT_FF_MEMLESS if USB_HID
-	help
-	  Say Y here if you have a Zeroplus based game controller and want to
-	  enable force feedback for it.
-
 config USB_HIDDEV
 	bool "/dev/hiddev raw HID device support"
 	depends on USB_HID
diff --git a/drivers/hid/usbhid/Makefile b/drivers/hid/usbhid/Makefile
index 00a7b70..1329ecb 100644
--- a/drivers/hid/usbhid/Makefile
+++ b/drivers/hid/usbhid/Makefile
@@ -13,24 +13,6 @@
 ifeq ($(CONFIG_HID_PID),y)
 	usbhid-objs	+= hid-pidff.o
 endif
-ifeq ($(CONFIG_LOGITECH_FF),y)
-	usbhid-objs	+= hid-lgff.o
-endif
-ifeq ($(CONFIG_LOGIRUMBLEPAD2_FF),y)
-	usbhid-objs	+= hid-lg2ff.o
-endif
-ifeq ($(CONFIG_PANTHERLORD_FF),y)
-	usbhid-objs	+= hid-plff.o
-endif
-ifeq ($(CONFIG_THRUSTMASTER_FF),y)
-	usbhid-objs	+= hid-tmff.o
-endif
-ifeq ($(CONFIG_ZEROPLUS_FF),y)
-	usbhid-objs	+= hid-zpff.o
-endif
-ifeq ($(CONFIG_HID_FF),y)
-	usbhid-objs	+= hid-ff.o
-endif
 
 obj-$(CONFIG_USB_HID)		+= usbhid.o
 obj-$(CONFIG_USB_KBD)		+= usbkbd.o
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
index 27fe4d8..1d3b8a3 100644
--- a/drivers/hid/usbhid/hid-core.c
+++ b/drivers/hid/usbhid/hid-core.c
@@ -44,8 +44,6 @@
 #define DRIVER_DESC "USB HID core driver"
 #define DRIVER_LICENSE "GPL"
 
-static char *hid_types[] = {"Device", "Pointer", "Mouse", "Device", "Joystick",
-				"Gamepad", "Keyboard", "Keypad", "Multi-Axis Controller"};
 /*
  * Module parameters.
  */
@@ -61,12 +59,6 @@
 		" quirks=vendorID:productID:quirks"
 		" where vendorID, productID, and quirks are all in"
 		" 0x-prefixed hex");
-static char *rdesc_quirks_param[MAX_USBHID_BOOT_QUIRKS] = { [ 0 ... (MAX_USBHID_BOOT_QUIRKS - 1) ] = NULL };
-module_param_array_named(rdesc_quirks, rdesc_quirks_param, charp, NULL, 0444);
-MODULE_PARM_DESC(rdesc_quirks, "Add/modify report descriptor quirks by specifying "
-		" rdesc_quirks=vendorID:productID:rdesc_quirks"
-		" where vendorID, productID, and rdesc_quirks are all in"
-		" 0x-prefixed hex");
 /*
  * Input submission and I/O error handler.
  */
@@ -197,31 +189,32 @@
 	int			status;
 
 	switch (urb->status) {
-		case 0:			/* success */
-			usbhid->retry_delay = 0;
-			hid_input_report(urb->context, HID_INPUT_REPORT,
-					 urb->transfer_buffer,
-					 urb->actual_length, 1);
-			break;
-		case -EPIPE:		/* stall */
-			clear_bit(HID_IN_RUNNING, &usbhid->iofl);
-			set_bit(HID_CLEAR_HALT, &usbhid->iofl);
-			schedule_work(&usbhid->reset_work);
-			return;
-		case -ECONNRESET:	/* unlink */
-		case -ENOENT:
-		case -ESHUTDOWN:	/* unplug */
-			clear_bit(HID_IN_RUNNING, &usbhid->iofl);
-			return;
-		case -EILSEQ:		/* protocol error or unplug */
-		case -EPROTO:		/* protocol error or unplug */
-		case -ETIME:		/* protocol error or unplug */
-		case -ETIMEDOUT:	/* Should never happen, but... */
-			clear_bit(HID_IN_RUNNING, &usbhid->iofl);
-			hid_io_error(hid);
-			return;
-		default:		/* error */
-			warn("input irq status %d received", urb->status);
+	case 0:			/* success */
+		usbhid->retry_delay = 0;
+		hid_input_report(urb->context, HID_INPUT_REPORT,
+				 urb->transfer_buffer,
+				 urb->actual_length, 1);
+		break;
+	case -EPIPE:		/* stall */
+		clear_bit(HID_IN_RUNNING, &usbhid->iofl);
+		set_bit(HID_CLEAR_HALT, &usbhid->iofl);
+		schedule_work(&usbhid->reset_work);
+		return;
+	case -ECONNRESET:	/* unlink */
+	case -ENOENT:
+	case -ESHUTDOWN:	/* unplug */
+		clear_bit(HID_IN_RUNNING, &usbhid->iofl);
+		return;
+	case -EILSEQ:		/* protocol error or unplug */
+	case -EPROTO:		/* protocol error or unplug */
+	case -ETIME:		/* protocol error or unplug */
+	case -ETIMEDOUT:	/* Should never happen, but... */
+		clear_bit(HID_IN_RUNNING, &usbhid->iofl);
+		hid_io_error(hid);
+		return;
+	default:		/* error */
+		dev_warn(&urb->dev->dev, "input irq status %d  "
+				"received\n", urb->status);
 	}
 
 	status = usb_submit_urb(urb, GFP_ATOMIC);
@@ -240,13 +233,16 @@
 static int hid_submit_out(struct hid_device *hid)
 {
 	struct hid_report *report;
+	char *raw_report;
 	struct usbhid_device *usbhid = hid->driver_data;
 
-	report = usbhid->out[usbhid->outtail];
+	report = usbhid->out[usbhid->outtail].report;
+	raw_report = usbhid->out[usbhid->outtail].raw_report;
 
-	hid_output_report(report, usbhid->outbuf);
 	usbhid->urbout->transfer_buffer_length = ((report->size - 1) >> 3) + 1 + (report->id > 0);
 	usbhid->urbout->dev = hid_to_usb_dev(hid);
+	memcpy(usbhid->outbuf, raw_report, usbhid->urbout->transfer_buffer_length);
+	kfree(raw_report);
 
 	dbg_hid("submitting out urb\n");
 
@@ -262,17 +258,20 @@
 {
 	struct hid_report *report;
 	unsigned char dir;
+	char *raw_report;
 	int len;
 	struct usbhid_device *usbhid = hid->driver_data;
 
 	report = usbhid->ctrl[usbhid->ctrltail].report;
+	raw_report = usbhid->ctrl[usbhid->ctrltail].raw_report;
 	dir = usbhid->ctrl[usbhid->ctrltail].dir;
 
 	len = ((report->size - 1) >> 3) + 1 + (report->id > 0);
 	if (dir == USB_DIR_OUT) {
-		hid_output_report(report, usbhid->ctrlbuf);
 		usbhid->urbctrl->pipe = usb_sndctrlpipe(hid_to_usb_dev(hid), 0);
 		usbhid->urbctrl->transfer_buffer_length = len;
+		memcpy(usbhid->ctrlbuf, raw_report, len);
+		kfree(raw_report);
 	} else {
 		int maxpacket, padlen;
 
@@ -319,17 +318,18 @@
 	int unplug = 0;
 
 	switch (urb->status) {
-		case 0:			/* success */
-			break;
-		case -ESHUTDOWN:	/* unplug */
-			unplug = 1;
-		case -EILSEQ:		/* protocol error or unplug */
-		case -EPROTO:		/* protocol error or unplug */
-		case -ECONNRESET:	/* unlink */
-		case -ENOENT:
-			break;
-		default:		/* error */
-			warn("output irq status %d received", urb->status);
+	case 0:			/* success */
+		break;
+	case -ESHUTDOWN:	/* unplug */
+		unplug = 1;
+	case -EILSEQ:		/* protocol error or unplug */
+	case -EPROTO:		/* protocol error or unplug */
+	case -ECONNRESET:	/* unlink */
+	case -ENOENT:
+		break;
+	default:		/* error */
+		dev_warn(&urb->dev->dev, "output irq status %d "
+				"received\n", urb->status);
 	}
 
 	spin_lock_irqsave(&usbhid->outlock, flags);
@@ -367,21 +367,23 @@
 	spin_lock_irqsave(&usbhid->ctrllock, flags);
 
 	switch (urb->status) {
-		case 0:			/* success */
-			if (usbhid->ctrl[usbhid->ctrltail].dir == USB_DIR_IN)
-				hid_input_report(urb->context, usbhid->ctrl[usbhid->ctrltail].report->type,
-						urb->transfer_buffer, urb->actual_length, 0);
-			break;
-		case -ESHUTDOWN:	/* unplug */
-			unplug = 1;
-		case -EILSEQ:		/* protocol error or unplug */
-		case -EPROTO:		/* protocol error or unplug */
-		case -ECONNRESET:	/* unlink */
-		case -ENOENT:
-		case -EPIPE:		/* report not available */
-			break;
-		default:		/* error */
-			warn("ctrl urb status %d received", urb->status);
+	case 0:			/* success */
+		if (usbhid->ctrl[usbhid->ctrltail].dir == USB_DIR_IN)
+			hid_input_report(urb->context,
+				usbhid->ctrl[usbhid->ctrltail].report->type,
+				urb->transfer_buffer, urb->actual_length, 0);
+		break;
+	case -ESHUTDOWN:	/* unplug */
+		unplug = 1;
+	case -EILSEQ:		/* protocol error or unplug */
+	case -EPROTO:		/* protocol error or unplug */
+	case -ECONNRESET:	/* unlink */
+	case -ENOENT:
+	case -EPIPE:		/* report not available */
+		break;
+	default:		/* error */
+		dev_warn(&urb->dev->dev, "ctrl urb status %d "
+				"received\n", urb->status);
 	}
 
 	if (unplug)
@@ -408,6 +410,7 @@
 	int head;
 	unsigned long flags;
 	struct usbhid_device *usbhid = hid->driver_data;
+	int len = ((report->size - 1) >> 3) + 1 + (report->id > 0);
 
 	if ((hid->quirks & HID_QUIRK_NOGET) && dir == USB_DIR_IN)
 		return;
@@ -418,11 +421,18 @@
 
 		if ((head = (usbhid->outhead + 1) & (HID_OUTPUT_FIFO_SIZE - 1)) == usbhid->outtail) {
 			spin_unlock_irqrestore(&usbhid->outlock, flags);
-			warn("output queue full");
+			dev_warn(&hid->dev, "output queue full\n");
 			return;
 		}
 
-		usbhid->out[usbhid->outhead] = report;
+		usbhid->out[usbhid->outhead].raw_report = kmalloc(len, GFP_ATOMIC);
+		if (!usbhid->out[usbhid->outhead].raw_report) {
+			spin_unlock_irqrestore(&usbhid->outlock, flags);
+			warn("output queueing failed");
+			return;
+		}
+		hid_output_report(report, usbhid->out[usbhid->outhead].raw_report);
+		usbhid->out[usbhid->outhead].report = report;
 		usbhid->outhead = head;
 
 		if (!test_and_set_bit(HID_OUT_RUNNING, &usbhid->iofl))
@@ -437,10 +447,19 @@
 
 	if ((head = (usbhid->ctrlhead + 1) & (HID_CONTROL_FIFO_SIZE - 1)) == usbhid->ctrltail) {
 		spin_unlock_irqrestore(&usbhid->ctrllock, flags);
-		warn("control queue full");
+		dev_warn(&hid->dev, "control queue full\n");
 		return;
 	}
 
+	if (dir == USB_DIR_OUT) {
+		usbhid->ctrl[usbhid->ctrlhead].raw_report = kmalloc(len, GFP_ATOMIC);
+		if (!usbhid->ctrl[usbhid->ctrlhead].raw_report) {
+			spin_unlock_irqrestore(&usbhid->ctrllock, flags);
+			warn("control queueing failed");
+			return;
+		}
+		hid_output_report(report, usbhid->ctrl[usbhid->ctrlhead].raw_report);
+	}
 	usbhid->ctrl[usbhid->ctrlhead].report = report;
 	usbhid->ctrl[usbhid->ctrlhead].dir = dir;
 	usbhid->ctrlhead = head;
@@ -451,6 +470,7 @@
 
 	spin_unlock_irqrestore(&usbhid->ctrllock, flags);
 }
+EXPORT_SYMBOL_GPL(usbhid_submit_report);
 
 static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
 {
@@ -465,7 +485,7 @@
 		return -1;
 
 	if ((offset = hidinput_find_field(hid, type, code, &field)) == -1) {
-		warn("event field not found");
+		dev_warn(&dev->dev, "event field not found\n");
 		return -1;
 	}
 
@@ -568,7 +588,7 @@
 	}
 
 	if (err)
-		warn("timeout initializing reports");
+		dev_warn(&hid->dev, "timeout initializing reports\n");
 }
 
 /*
@@ -598,7 +618,7 @@
 	return -1;
 }
 
-static void usbhid_set_leds(struct hid_device *hid)
+void usbhid_set_leds(struct hid_device *hid)
 {
 	struct hid_field *field;
 	int offset;
@@ -608,6 +628,7 @@
 		usbhid_submit_report(hid, field->report, USB_DIR_OUT);
 	}
 }
+EXPORT_SYMBOL_GPL(usbhid_set_leds);
 
 /*
  * Traverse the supplied list of reports and find the longest
@@ -675,43 +696,16 @@
 	usb_buffer_free(dev, usbhid->bufsize, usbhid->ctrlbuf, usbhid->ctrlbuf_dma);
 }
 
-/*
- * Sending HID_REQ_GET_REPORT changes the operation mode of the ps3 controller
- * to "operational".  Without this, the ps3 controller will not report any
- * events.
- */
-static void hid_fixup_sony_ps3_controller(struct usb_device *dev, int ifnum)
+static int usbhid_parse(struct hid_device *hid)
 {
-	int result;
-	char *buf = kmalloc(18, GFP_KERNEL);
-
-	if (!buf)
-		return;
-
-	result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
-				 HID_REQ_GET_REPORT,
-				 USB_DIR_IN | USB_TYPE_CLASS |
-				 USB_RECIP_INTERFACE,
-				 (3 << 8) | 0xf2, ifnum, buf, 17,
-				 USB_CTRL_GET_TIMEOUT);
-
-	if (result < 0)
-		err_hid("%s failed: %d\n", __func__, result);
-
-	kfree(buf);
-}
-
-static struct hid_device *usb_hid_configure(struct usb_interface *intf)
-{
+	struct usb_interface *intf = to_usb_interface(hid->dev.parent);
 	struct usb_host_interface *interface = intf->cur_altsetting;
 	struct usb_device *dev = interface_to_usbdev (intf);
 	struct hid_descriptor *hdesc;
-	struct hid_device *hid;
 	u32 quirks = 0;
-	unsigned int insize = 0, rsize = 0;
+	unsigned int rsize = 0;
 	char *rdesc;
-	int n, len;
-	struct usbhid_device *usbhid;
+	int ret, n;
 
 	quirks = usbhid_lookup_quirk(le16_to_cpu(dev->descriptor.idVendor),
 			le16_to_cpu(dev->descriptor.idProduct));
@@ -724,63 +718,75 @@
 				quirks |= HID_QUIRK_NOGET;
 	}
 
-	if (quirks & HID_QUIRK_IGNORE)
-		return NULL;
-
-	if ((quirks & HID_QUIRK_IGNORE_MOUSE) &&
-		(interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE))
-			return NULL;
-
-
 	if (usb_get_extra_descriptor(interface, HID_DT_HID, &hdesc) &&
 	    (!interface->desc.bNumEndpoints ||
 	     usb_get_extra_descriptor(&interface->endpoint[0], HID_DT_HID, &hdesc))) {
 		dbg_hid("class descriptor not present\n");
-		return NULL;
+		return -ENODEV;
 	}
 
+	hid->version = le16_to_cpu(hdesc->bcdHID);
+	hid->country = hdesc->bCountryCode;
+
 	for (n = 0; n < hdesc->bNumDescriptors; n++)
 		if (hdesc->desc[n].bDescriptorType == HID_DT_REPORT)
 			rsize = le16_to_cpu(hdesc->desc[n].wDescriptorLength);
 
 	if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) {
 		dbg_hid("weird size of report descriptor (%u)\n", rsize);
-		return NULL;
+		return -EINVAL;
 	}
 
 	if (!(rdesc = kmalloc(rsize, GFP_KERNEL))) {
 		dbg_hid("couldn't allocate rdesc memory\n");
-		return NULL;
+		return -ENOMEM;
 	}
 
 	hid_set_idle(dev, interface->desc.bInterfaceNumber, 0, 0);
 
-	if ((n = hid_get_class_descriptor(dev, interface->desc.bInterfaceNumber, HID_DT_REPORT, rdesc, rsize)) < 0) {
+	ret = hid_get_class_descriptor(dev, interface->desc.bInterfaceNumber,
+			HID_DT_REPORT, rdesc, rsize);
+	if (ret < 0) {
 		dbg_hid("reading report descriptor failed\n");
 		kfree(rdesc);
-		return NULL;
+		goto err;
 	}
 
-	usbhid_fixup_report_descriptor(le16_to_cpu(dev->descriptor.idVendor),
-			le16_to_cpu(dev->descriptor.idProduct), rdesc,
-			rsize, rdesc_quirks_param);
-
 	dbg_hid("report descriptor (size %u, read %d) = ", rsize, n);
 	for (n = 0; n < rsize; n++)
 		dbg_hid_line(" %02x", (unsigned char) rdesc[n]);
 	dbg_hid_line("\n");
 
-	if (!(hid = hid_parse_report(rdesc, n))) {
+	ret = hid_parse_report(hid, rdesc, rsize);
+	kfree(rdesc);
+	if (ret) {
 		dbg_hid("parsing report descriptor failed\n");
-		kfree(rdesc);
-		return NULL;
+		goto err;
 	}
 
-	kfree(rdesc);
 	hid->quirks = quirks;
 
-	if (!(usbhid = kzalloc(sizeof(struct usbhid_device), GFP_KERNEL)))
-		goto fail_no_usbhid;
+	return 0;
+err:
+	return ret;
+}
+
+static int usbhid_start(struct hid_device *hid)
+{
+	struct usb_interface *intf = to_usb_interface(hid->dev.parent);
+	struct usb_host_interface *interface = intf->cur_altsetting;
+	struct usb_device *dev = interface_to_usbdev(intf);
+	struct usbhid_device *usbhid;
+	unsigned int n, insize = 0;
+	int ret;
+
+	WARN_ON(hid->driver_data);
+
+	usbhid = kzalloc(sizeof(struct usbhid_device), GFP_KERNEL);
+	if (usbhid == NULL) {
+		ret = -ENOMEM;
+		goto err;
+	}
 
 	hid->driver_data = usbhid;
 	usbhid->hid = hid;
@@ -799,28 +805,11 @@
 		insize = HID_MAX_BUFFER_SIZE;
 
 	if (hid_alloc_buffers(dev, hid)) {
-		hid_free_buffers(dev, hid);
+		ret = -ENOMEM;
 		goto fail;
 	}
 
-	hid->name[0] = 0;
-
-	if (dev->manufacturer)
-		strlcpy(hid->name, dev->manufacturer, sizeof(hid->name));
-
-	if (dev->product) {
-		if (dev->manufacturer)
-			strlcat(hid->name, " ", sizeof(hid->name));
-		strlcat(hid->name, dev->product, sizeof(hid->name));
-	}
-
-	if (!strlen(hid->name))
-		snprintf(hid->name, sizeof(hid->name), "HID %04x:%04x",
-			 le16_to_cpu(dev->descriptor.idVendor),
-			 le16_to_cpu(dev->descriptor.idProduct));
-
 	for (n = 0; n < interface->desc.bNumEndpoints; n++) {
-
 		struct usb_endpoint_descriptor *endpoint;
 		int pipe;
 		int interval;
@@ -832,7 +821,7 @@
 		interval = endpoint->bInterval;
 
 		/* Some vendors give fullspeed interval on highspeed devides */
-		if (quirks & HID_QUIRK_FULLSPEED_INTERVAL  &&
+		if (hid->quirks & HID_QUIRK_FULLSPEED_INTERVAL &&
 		    dev->speed == USB_SPEED_HIGH) {
 			interval = fls(endpoint->bInterval*8);
 			printk(KERN_INFO "%s: Fixing fullspeed to highspeed interval: %d -> %d\n",
@@ -843,6 +832,7 @@
 		if (hid->collection->usage == HID_GD_MOUSE && hid_mousepoll_interval > 0)
 			interval = hid_mousepoll_interval;
 
+		ret = -ENOMEM;
 		if (usb_endpoint_dir_in(endpoint)) {
 			if (usbhid->urbin)
 				continue;
@@ -868,6 +858,7 @@
 
 	if (!usbhid->urbin) {
 		err_hid("couldn't find an input interrupt endpoint");
+		ret = -ENODEV;
 		goto fail;
 	}
 
@@ -879,44 +870,25 @@
 	spin_lock_init(&usbhid->outlock);
 	spin_lock_init(&usbhid->ctrllock);
 
-	hid->version = le16_to_cpu(hdesc->bcdHID);
-	hid->country = hdesc->bCountryCode;
-	hid->dev = &intf->dev;
 	usbhid->intf = intf;
 	usbhid->ifnum = interface->desc.bInterfaceNumber;
 
-	hid->bus = BUS_USB;
-	hid->vendor = le16_to_cpu(dev->descriptor.idVendor);
-	hid->product = le16_to_cpu(dev->descriptor.idProduct);
-
-	usb_make_path(dev, hid->phys, sizeof(hid->phys));
-	strlcat(hid->phys, "/input", sizeof(hid->phys));
-	len = strlen(hid->phys);
-	if (len < sizeof(hid->phys) - 1)
-		snprintf(hid->phys + len, sizeof(hid->phys) - len,
-			 "%d", intf->altsetting[0].desc.bInterfaceNumber);
-
-	if (usb_string(dev, dev->descriptor.iSerialNumber, hid->uniq, 64) <= 0)
-		hid->uniq[0] = 0;
-
 	usbhid->urbctrl = usb_alloc_urb(0, GFP_KERNEL);
-	if (!usbhid->urbctrl)
+	if (!usbhid->urbctrl) {
+		ret = -ENOMEM;
 		goto fail;
+	}
 
 	usb_fill_control_urb(usbhid->urbctrl, dev, 0, (void *) usbhid->cr,
 			     usbhid->ctrlbuf, 1, hid_ctrl, hid);
 	usbhid->urbctrl->setup_dma = usbhid->cr_dma;
 	usbhid->urbctrl->transfer_dma = usbhid->ctrlbuf_dma;
 	usbhid->urbctrl->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP);
-	hid->hidinput_input_event = usb_hidinput_input_event;
-	hid->hid_open = usbhid_open;
-	hid->hid_close = usbhid_close;
-#ifdef CONFIG_USB_HIDDEV
-	hid->hiddev_hid_event = hiddev_hid_event;
-	hid->hiddev_report_event = hiddev_report_event;
-#endif
-	hid->hid_output_raw_report = usbhid_output_raw_report;
-	return hid;
+
+	usbhid_init_reports(hid);
+	hid_dump_device(hid);
+
+	return 0;
 
 fail:
 	usb_free_urb(usbhid->urbin);
@@ -924,24 +896,18 @@
 	usb_free_urb(usbhid->urbctrl);
 	hid_free_buffers(dev, hid);
 	kfree(usbhid);
-fail_no_usbhid:
-	hid_free_device(hid);
-
-	return NULL;
+err:
+	return ret;
 }
 
-static void hid_disconnect(struct usb_interface *intf)
+static void usbhid_stop(struct hid_device *hid)
 {
-	struct hid_device *hid = usb_get_intfdata (intf);
-	struct usbhid_device *usbhid;
+	struct usbhid_device *usbhid = hid->driver_data;
 
-	if (!hid)
+	if (WARN_ON(!usbhid))
 		return;
 
-	usbhid = hid->driver_data;
-
 	spin_lock_irq(&usbhid->inlock);	/* Sync with error handler */
-	usb_set_intfdata(intf, NULL);
 	set_bit(HID_DISCONNECTED, &usbhid->iofl);
 	spin_unlock_irq(&usbhid->inlock);
 	usb_kill_urb(usbhid->urbin);
@@ -958,86 +924,100 @@
 	if (hid->claimed & HID_CLAIMED_HIDRAW)
 		hidraw_disconnect(hid);
 
+	hid->claimed = 0;
+
 	usb_free_urb(usbhid->urbin);
 	usb_free_urb(usbhid->urbctrl);
 	usb_free_urb(usbhid->urbout);
 
 	hid_free_buffers(hid_to_usb_dev(hid), hid);
 	kfree(usbhid);
-	hid_free_device(hid);
+	hid->driver_data = NULL;
 }
 
+static struct hid_ll_driver usb_hid_driver = {
+	.parse = usbhid_parse,
+	.start = usbhid_start,
+	.stop = usbhid_stop,
+	.open = usbhid_open,
+	.close = usbhid_close,
+	.hidinput_input_event = usb_hidinput_input_event,
+};
+
 static int hid_probe(struct usb_interface *intf, const struct usb_device_id *id)
 {
+	struct usb_device *dev = interface_to_usbdev(intf);
 	struct hid_device *hid;
-	char path[64];
-	int i;
-	char *c;
+	size_t len;
+	int ret;
 
 	dbg_hid("HID probe called for ifnum %d\n",
 			intf->altsetting->desc.bInterfaceNumber);
 
-	if (!(hid = usb_hid_configure(intf)))
-		return -ENODEV;
-
-	usbhid_init_reports(hid);
-	hid_dump_device(hid);
-	if (hid->quirks & HID_QUIRK_RESET_LEDS)
-		usbhid_set_leds(hid);
-
-	if (!hidinput_connect(hid))
-		hid->claimed |= HID_CLAIMED_INPUT;
-	if (!hiddev_connect(hid))
-		hid->claimed |= HID_CLAIMED_HIDDEV;
-	if (!hidraw_connect(hid))
-		hid->claimed |= HID_CLAIMED_HIDRAW;
+	hid = hid_allocate_device();
+	if (IS_ERR(hid))
+		return PTR_ERR(hid);
 
 	usb_set_intfdata(intf, hid);
+	hid->ll_driver = &usb_hid_driver;
+	hid->hid_output_raw_report = usbhid_output_raw_report;
+	hid->ff_init = hid_pidff_init;
+#ifdef CONFIG_USB_HIDDEV
+	hid->hiddev_connect = hiddev_connect;
+	hid->hiddev_hid_event = hiddev_hid_event;
+	hid->hiddev_report_event = hiddev_report_event;
+#endif
+	hid->dev.parent = &intf->dev;
+	hid->bus = BUS_USB;
+	hid->vendor = le16_to_cpu(dev->descriptor.idVendor);
+	hid->product = le16_to_cpu(dev->descriptor.idProduct);
+	hid->name[0] = 0;
 
-	if (!hid->claimed) {
-		printk ("HID device claimed by neither input, hiddev nor hidraw\n");
-		hid_disconnect(intf);
-		return -ENODEV;
+	if (dev->manufacturer)
+		strlcpy(hid->name, dev->manufacturer, sizeof(hid->name));
+
+	if (dev->product) {
+		if (dev->manufacturer)
+			strlcat(hid->name, " ", sizeof(hid->name));
+		strlcat(hid->name, dev->product, sizeof(hid->name));
 	}
 
-	if ((hid->claimed & HID_CLAIMED_INPUT))
-		hid_ff_init(hid);
+	if (!strlen(hid->name))
+		snprintf(hid->name, sizeof(hid->name), "HID %04x:%04x",
+			 le16_to_cpu(dev->descriptor.idVendor),
+			 le16_to_cpu(dev->descriptor.idProduct));
 
-	if (hid->quirks & HID_QUIRK_SONY_PS3_CONTROLLER)
-		hid_fixup_sony_ps3_controller(interface_to_usbdev(intf),
-			intf->cur_altsetting->desc.bInterfaceNumber);
+	usb_make_path(dev, hid->phys, sizeof(hid->phys));
+	strlcat(hid->phys, "/input", sizeof(hid->phys));
+	len = strlen(hid->phys);
+	if (len < sizeof(hid->phys) - 1)
+		snprintf(hid->phys + len, sizeof(hid->phys) - len,
+			 "%d", intf->altsetting[0].desc.bInterfaceNumber);
 
-	printk(KERN_INFO);
+	if (usb_string(dev, dev->descriptor.iSerialNumber, hid->uniq, 64) <= 0)
+		hid->uniq[0] = 0;
 
-	if (hid->claimed & HID_CLAIMED_INPUT)
-		printk("input");
-	if ((hid->claimed & HID_CLAIMED_INPUT) && ((hid->claimed & HID_CLAIMED_HIDDEV) ||
-				hid->claimed & HID_CLAIMED_HIDRAW))
-		printk(",");
-	if (hid->claimed & HID_CLAIMED_HIDDEV)
-		printk("hiddev%d", hid->minor);
-	if ((hid->claimed & HID_CLAIMED_INPUT) && (hid->claimed & HID_CLAIMED_HIDDEV) &&
-			(hid->claimed & HID_CLAIMED_HIDRAW))
-		printk(",");
-	if (hid->claimed & HID_CLAIMED_HIDRAW)
-		printk("hidraw%d", ((struct hidraw*)hid->hidraw)->minor);
-
-	c = "Device";
-	for (i = 0; i < hid->maxcollection; i++) {
-		if (hid->collection[i].type == HID_COLLECTION_APPLICATION &&
-		    (hid->collection[i].usage & HID_USAGE_PAGE) == HID_UP_GENDESK &&
-		    (hid->collection[i].usage & 0xffff) < ARRAY_SIZE(hid_types)) {
-			c = hid_types[hid->collection[i].usage & 0xffff];
-			break;
-		}
+	ret = hid_add_device(hid);
+	if (ret) {
+		if (ret != -ENODEV)
+			dev_err(&intf->dev, "can't add hid device: %d\n", ret);
+		goto err;
 	}
 
-	usb_make_path(interface_to_usbdev(intf), path, 63);
-
-	printk(": USB HID v%x.%02x %s [%s] on %s\n",
-		hid->version >> 8, hid->version & 0xff, c, hid->name, path);
-
 	return 0;
+err:
+	hid_destroy_device(hid);
+	return ret;
+}
+
+static void hid_disconnect(struct usb_interface *intf)
+{
+	struct hid_device *hid = usb_get_intfdata(intf);
+
+	if (WARN_ON(!hid))
+		return;
+
+	hid_destroy_device(hid);
 }
 
 static int hid_suspend(struct usb_interface *intf, pm_message_t message)
@@ -1107,9 +1087,22 @@
 	.supports_autosuspend = 1,
 };
 
+static const struct hid_device_id hid_usb_table[] = {
+	{ HID_USB_DEVICE(HID_ANY_ID, HID_ANY_ID) },
+	{ }
+};
+
+static struct hid_driver hid_usb_driver = {
+	.name = "generic-usb",
+	.id_table = hid_usb_table,
+};
+
 static int __init hid_init(void)
 {
 	int retval;
+	retval = hid_register_driver(&hid_usb_driver);
+	if (retval)
+		goto hid_register_fail;
 	retval = usbhid_quirks_init(quirks_param);
 	if (retval)
 		goto usbhid_quirks_init_fail;
@@ -1119,7 +1112,8 @@
 	retval = usb_register(&hid_driver);
 	if (retval)
 		goto usb_register_fail;
-	info(DRIVER_VERSION ":" DRIVER_DESC);
+	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
+			DRIVER_DESC "\n");
 
 	return 0;
 usb_register_fail:
@@ -1127,6 +1121,8 @@
 hiddev_init_fail:
 	usbhid_quirks_exit();
 usbhid_quirks_init_fail:
+	hid_unregister_driver(&hid_usb_driver);
+hid_register_fail:
 	return retval;
 }
 
@@ -1135,6 +1131,7 @@
 	usb_deregister(&hid_driver);
 	hiddev_exit();
 	usbhid_quirks_exit();
+	hid_unregister_driver(&hid_usb_driver);
 }
 
 module_init(hid_init);
diff --git a/drivers/hid/usbhid/hid-ff.c b/drivers/hid/usbhid/hid-ff.c
deleted file mode 100644
index 1d0dac5..0000000
--- a/drivers/hid/usbhid/hid-ff.c
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- *  Force feedback support for hid devices.
- *  Not all hid devices use the same protocol. For example, some use PID,
- *  other use their own proprietary procotol.
- *
- *  Copyright (c) 2002-2004 Johann Deneux
- */
-
-/*
- * 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
- *
- * Should you need to contact me, the author, you can do so by
- * e-mail - mail your message to <johann.deneux@it.uu.se>
- */
-
-#include <linux/input.h>
-
-#undef DEBUG
-#include <linux/usb.h>
-
-#include <linux/hid.h>
-#include "usbhid.h"
-
-/*
- * This table contains pointers to initializers. To add support for new
- * devices, you need to add the USB vendor and product ids here.
- */
-struct hid_ff_initializer {
-	u16 idVendor;
-	u16 idProduct;
-	int (*init)(struct hid_device*);
-};
-
-/*
- * We try pidff when no other driver is found because PID is the
- * standards compliant way of implementing force feedback in HID.
- * pidff_init() will quickly abort if the device doesn't appear to
- * be a PID device
- */
-static struct hid_ff_initializer inits[] = {
-#ifdef CONFIG_LOGITECH_FF
-	{ 0x46d, 0xc211, hid_lgff_init }, /* Logitech Cordless rumble pad */
-	{ 0x46d, 0xc219, hid_lgff_init }, /* Logitech Cordless rumble pad 2 */
-	{ 0x46d, 0xc283, hid_lgff_init }, /* Logitech Wingman Force 3d */
-	{ 0x46d, 0xc286, hid_lgff_init }, /* Logitech Force 3D Pro Joystick */
-	{ 0x46d, 0xc294, hid_lgff_init }, /* Logitech Formula Force EX */
-	{ 0x46d, 0xc295, hid_lgff_init }, /* Logitech MOMO force wheel */
-	{ 0x46d, 0xca03, hid_lgff_init }, /* Logitech MOMO force wheel */
-#endif
-#ifdef CONFIG_LOGIRUMBLEPAD2_FF
-	{ 0x46d, 0xc218, hid_lg2ff_init }, /* Logitech Rumblepad 2 */
-#endif
-#ifdef CONFIG_PANTHERLORD_FF
-	{ 0x810, 0x0001, hid_plff_init }, /* "Twin USB Joystick" */
-	{ 0xe8f, 0x0003, hid_plff_init }, /* "GreenAsia Inc.    USB Joystick     " */
-#endif
-#ifdef CONFIG_THRUSTMASTER_FF
-	{ 0x44f, 0xb300, hid_tmff_init },
-	{ 0x44f, 0xb304, hid_tmff_init },
-	{ 0x44f, 0xb651, hid_tmff_init }, /* FGT Rumble Force Wheel */
-	{ 0x44f, 0xb654, hid_tmff_init }, /* FGT Force Feedback Wheel */
-#endif
-#ifdef CONFIG_ZEROPLUS_FF
-	{ 0xc12, 0x0005, hid_zpff_init },
-	{ 0xc12, 0x0030, hid_zpff_init },
-#endif
-	{ 0,	 0,	 hid_pidff_init}  /* Matches anything */
-};
-
-int hid_ff_init(struct hid_device* hid)
-{
-	struct hid_ff_initializer *init;
-	int vendor = le16_to_cpu(hid_to_usb_dev(hid)->descriptor.idVendor);
-	int product = le16_to_cpu(hid_to_usb_dev(hid)->descriptor.idProduct);
-
-	for (init = inits; init->idVendor; init++)
-		if (init->idVendor == vendor && init->idProduct == product)
-			break;
-
-	return init->init(hid);
-}
-EXPORT_SYMBOL_GPL(hid_ff_init);
-
diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c
index 0113261..484e3ee 100644
--- a/drivers/hid/usbhid/hid-pidff.c
+++ b/drivers/hid/usbhid/hid-pidff.c
@@ -397,7 +397,6 @@
 			  effect->u.condition[i].left_saturation);
 		pidff_set(&pidff->set_condition[PID_DEAD_BAND],
 			  effect->u.condition[i].deadband);
-		usbhid_wait_io(pidff->hid);
 		usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_CONDITION],
 				  USB_DIR_OUT);
 	}
@@ -512,7 +511,6 @@
 		pidff->effect_operation[PID_LOOP_COUNT].value[0] = n;
 	}
 
-	usbhid_wait_io(pidff->hid);
 	usbhid_submit_report(pidff->hid, pidff->reports[PID_EFFECT_OPERATION],
 			  USB_DIR_OUT);
 }
@@ -548,6 +546,9 @@
 	int pid_id = pidff->pid_id[effect_id];
 
 	debug("starting to erase %d/%d", effect_id, pidff->pid_id[effect_id]);
+	/* Wait for the queue to clear. We do not want a full fifo to
+	   prevent the effect removal. */
+	usbhid_wait_io(pidff->hid);
 	pidff_playback_pid(pidff, pid_id, 0);
 	pidff_erase_pid(pidff, pid_id);
 
diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
index b15f882..47ebe04 100644
--- a/drivers/hid/usbhid/hid-quirks.c
+++ b/drivers/hid/usbhid/hid-quirks.c
@@ -17,412 +17,7 @@
 
 #include <linux/hid.h>
 
-#define USB_VENDOR_ID_A4TECH		0x09da
-#define USB_DEVICE_ID_A4TECH_WCP32PU	0x0006
-#define USB_DEVICE_ID_A4TECH_X5_005D	0x000a
-
-#define USB_VENDOR_ID_AASHIMA		0x06d6
-#define USB_DEVICE_ID_AASHIMA_GAMEPAD	0x0025
-#define USB_DEVICE_ID_AASHIMA_PREDATOR	0x0026
-
-#define USB_VENDOR_ID_ACECAD		0x0460
-#define USB_DEVICE_ID_ACECAD_FLAIR	0x0004
-#define USB_DEVICE_ID_ACECAD_302	0x0008
-
-#define USB_VENDOR_ID_ADS_TECH 		0x06e1
-#define USB_DEVICE_ID_ADS_TECH_RADIO_SI470X	0xa155
-
-#define USB_VENDOR_ID_AFATECH		0x15a4
-#define USB_DEVICE_ID_AFATECH_AF9016	0x9016
-
-#define USB_VENDOR_ID_AIPTEK		0x08ca
-#define USB_DEVICE_ID_AIPTEK_01		0x0001
-#define USB_DEVICE_ID_AIPTEK_10		0x0010
-#define USB_DEVICE_ID_AIPTEK_20		0x0020
-#define USB_DEVICE_ID_AIPTEK_21		0x0021
-#define USB_DEVICE_ID_AIPTEK_22		0x0022
-#define USB_DEVICE_ID_AIPTEK_23		0x0023
-#define USB_DEVICE_ID_AIPTEK_24		0x0024
-
-#define USB_VENDOR_ID_AIRCABLE		0x16CA
-#define USB_DEVICE_ID_AIRCABLE1		0x1502
-
-#define USB_VENDOR_ID_ALCOR		0x058f
-#define USB_DEVICE_ID_ALCOR_USBRS232	0x9720
-
-#define USB_VENDOR_ID_ALPS		0x0433
-#define USB_DEVICE_ID_IBM_GAMEPAD	0x1101
-
-#define USB_VENDOR_ID_APPLE		0x05ac
-#define USB_DEVICE_ID_APPLE_MIGHTYMOUSE	0x0304
-#define USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI	0x020e
-#define USB_DEVICE_ID_APPLE_FOUNTAIN_ISO	0x020f
-#define USB_DEVICE_ID_APPLE_GEYSER_ANSI	0x0214
-#define USB_DEVICE_ID_APPLE_GEYSER_ISO	0x0215
-#define USB_DEVICE_ID_APPLE_GEYSER_JIS	0x0216
-#define USB_DEVICE_ID_APPLE_GEYSER3_ANSI	0x0217
-#define USB_DEVICE_ID_APPLE_GEYSER3_ISO	0x0218
-#define USB_DEVICE_ID_APPLE_GEYSER3_JIS	0x0219
-#define USB_DEVICE_ID_APPLE_GEYSER4_ANSI	0x021a
-#define USB_DEVICE_ID_APPLE_GEYSER4_ISO	0x021b
-#define USB_DEVICE_ID_APPLE_GEYSER4_JIS	0x021c
-#define USB_DEVICE_ID_APPLE_ALU_ANSI	0x0220
-#define USB_DEVICE_ID_APPLE_ALU_ISO	0x0221
-#define USB_DEVICE_ID_APPLE_ALU_JIS	0x0222
-#define USB_DEVICE_ID_APPLE_WELLSPRING_ANSI	0x0223
-#define USB_DEVICE_ID_APPLE_WELLSPRING_ISO	0x0224
-#define USB_DEVICE_ID_APPLE_WELLSPRING_JIS	0x0225
-#define USB_DEVICE_ID_APPLE_GEYSER4_HF_ANSI    0x0229
-#define USB_DEVICE_ID_APPLE_GEYSER4_HF_ISO     0x022a
-#define USB_DEVICE_ID_APPLE_GEYSER4_HF_JIS     0x022b
-#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI  0x022c
-#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO   0x022d
-#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS   0x022e
-#define USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI	0x0230
-#define USB_DEVICE_ID_APPLE_WELLSPRING2_ISO	0x0231
-#define USB_DEVICE_ID_APPLE_WELLSPRING2_JIS	0x0232
-#define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY	0x030a
-#define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY	0x030b
-#define USB_DEVICE_ID_APPLE_IRCONTROL4	0x8242
-
-#define USB_VENDOR_ID_ASUS		0x0b05
-#define USB_DEVICE_ID_ASUS_LCM		0x1726
-
-#define USB_VENDOR_ID_ATEN		0x0557
-#define USB_DEVICE_ID_ATEN_UC100KM	0x2004
-#define USB_DEVICE_ID_ATEN_CS124U	0x2202
-#define USB_DEVICE_ID_ATEN_2PORTKVM	0x2204
-#define USB_DEVICE_ID_ATEN_4PORTKVM	0x2205
-#define USB_DEVICE_ID_ATEN_4PORTKVMC	0x2208
-
-#define USB_VENDOR_ID_BELKIN           0x050d
-#define USB_DEVICE_ID_FLIP_KVM         0x3201
-
-#define USB_VENDOR_ID_BERKSHIRE		0x0c98
-#define USB_DEVICE_ID_BERKSHIRE_PCWD	0x1140
-
-#define USB_VENDOR_ID_CHERRY		0x046a
-#define USB_DEVICE_ID_CHERRY_CYMOTION	0x0023
-
-#define USB_VENDOR_ID_CHIC		0x05fe
-#define USB_DEVICE_ID_CHIC_GAMEPAD	0x0014
-
-#define USB_VENDOR_ID_CIDC		0x1677
-
-#define USB_VENDOR_ID_CMEDIA		0x0d8c
-#define USB_DEVICE_ID_CM109		0x000e
-
-#define USB_VENDOR_ID_CODEMERCS		0x07c0
-#define USB_DEVICE_ID_CODEMERCS_IOW_FIRST	0x1500
-#define USB_DEVICE_ID_CODEMERCS_IOW_LAST	0x15ff
-
-#define USB_VENDOR_ID_CYGNAL		0x10c4
-#define USB_DEVICE_ID_CYGNAL_RADIO_SI470X	0x818a
-
-#define USB_VENDOR_ID_CYPRESS		0x04b4
-#define USB_DEVICE_ID_CYPRESS_MOUSE	0x0001
-#define USB_DEVICE_ID_CYPRESS_HIDCOM	0x5500
-#define USB_DEVICE_ID_CYPRESS_ULTRAMOUSE	0x7417
-#define USB_DEVICE_ID_CYPRESS_BARCODE_1	0xde61
-#define USB_DEVICE_ID_CYPRESS_BARCODE_2	0xde64
-
-#define USB_VENDOR_ID_DELL		0x413c
-#define USB_DEVICE_ID_DELL_W7658	0x2005
-
-#define USB_VENDOR_ID_DELORME		0x1163
-#define USB_DEVICE_ID_DELORME_EARTHMATE 0x0100
-#define USB_DEVICE_ID_DELORME_EM_LT20	0x0200
-
-#define USB_VENDOR_ID_DMI		0x0c0b
-#define USB_DEVICE_ID_DMI_ENC		0x5fab
-
-#define USB_VENDOR_ID_ELO		0x04E7
-#define USB_DEVICE_ID_ELO_TS2700	0x0020
-
-#define USB_VENDOR_ID_ESSENTIAL_REALITY	0x0d7f
-#define USB_DEVICE_ID_ESSENTIAL_REALITY_P5 0x0100
-
-#define USB_VENDOR_ID_EZKEY 		0x0518
-#define USB_DEVICE_ID_BTC_8193		0x0002
-
-#define USB_VENDOR_ID_GAMERON		0x0810
-#define USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR	0x0001
-
-#define USB_VENDOR_ID_GENERAL_TOUCH	0x0dfc
-
-#define USB_VENDOR_ID_GLAB		0x06c2
-#define USB_DEVICE_ID_4_PHIDGETSERVO_30	0x0038
-#define USB_DEVICE_ID_1_PHIDGETSERVO_30	0x0039
-#define USB_DEVICE_ID_0_0_4_IF_KIT	0x0040
-#define USB_DEVICE_ID_0_16_16_IF_KIT	0x0044
-#define USB_DEVICE_ID_8_8_8_IF_KIT	0x0045
-#define USB_DEVICE_ID_0_8_7_IF_KIT	0x0051
-#define USB_DEVICE_ID_0_8_8_IF_KIT	0x0053
-#define USB_DEVICE_ID_PHIDGET_MOTORCONTROL	0x0058
-
-#define USB_VENDOR_ID_GOTOP		0x08f2
-#define USB_DEVICE_ID_SUPER_Q2		0x007f
-#define USB_DEVICE_ID_GOGOPEN		0x00ce
-#define USB_DEVICE_ID_PENPOWER		0x00f4
-
-#define USB_VENDOR_ID_GRETAGMACBETH	0x0971
-#define USB_DEVICE_ID_GRETAGMACBETH_HUEY	0x2005
-
-#define USB_VENDOR_ID_GRIFFIN		0x077d
-#define USB_DEVICE_ID_POWERMATE		0x0410
-#define USB_DEVICE_ID_SOUNDKNOB		0x04AA
-
-#define USB_VENDOR_ID_GTCO		0x078c
-#define USB_DEVICE_ID_GTCO_90		0x0090
-#define USB_DEVICE_ID_GTCO_100		0x0100
-#define USB_DEVICE_ID_GTCO_101		0x0101
-#define USB_DEVICE_ID_GTCO_103		0x0103
-#define USB_DEVICE_ID_GTCO_104		0x0104
-#define USB_DEVICE_ID_GTCO_105		0x0105
-#define USB_DEVICE_ID_GTCO_106		0x0106
-#define USB_DEVICE_ID_GTCO_107		0x0107
-#define USB_DEVICE_ID_GTCO_108		0x0108
-#define USB_DEVICE_ID_GTCO_200		0x0200
-#define USB_DEVICE_ID_GTCO_201		0x0201
-#define USB_DEVICE_ID_GTCO_202		0x0202
-#define USB_DEVICE_ID_GTCO_203		0x0203
-#define USB_DEVICE_ID_GTCO_204		0x0204
-#define USB_DEVICE_ID_GTCO_205		0x0205
-#define USB_DEVICE_ID_GTCO_206		0x0206
-#define USB_DEVICE_ID_GTCO_207		0x0207
-#define USB_DEVICE_ID_GTCO_300		0x0300
-#define USB_DEVICE_ID_GTCO_301		0x0301
-#define USB_DEVICE_ID_GTCO_302		0x0302
-#define USB_DEVICE_ID_GTCO_303		0x0303
-#define USB_DEVICE_ID_GTCO_304		0x0304
-#define USB_DEVICE_ID_GTCO_305		0x0305
-#define USB_DEVICE_ID_GTCO_306		0x0306
-#define USB_DEVICE_ID_GTCO_307		0x0307
-#define USB_DEVICE_ID_GTCO_308		0x0308
-#define USB_DEVICE_ID_GTCO_309		0x0309
-#define USB_DEVICE_ID_GTCO_400		0x0400
-#define USB_DEVICE_ID_GTCO_401		0x0401
-#define USB_DEVICE_ID_GTCO_402		0x0402
-#define USB_DEVICE_ID_GTCO_403		0x0403
-#define USB_DEVICE_ID_GTCO_404		0x0404
-#define USB_DEVICE_ID_GTCO_405		0x0405
-#define USB_DEVICE_ID_GTCO_500		0x0500
-#define USB_DEVICE_ID_GTCO_501		0x0501
-#define USB_DEVICE_ID_GTCO_502		0x0502
-#define USB_DEVICE_ID_GTCO_503		0x0503
-#define USB_DEVICE_ID_GTCO_504		0x0504
-#define USB_DEVICE_ID_GTCO_1000		0x1000
-#define USB_DEVICE_ID_GTCO_1001		0x1001
-#define USB_DEVICE_ID_GTCO_1002		0x1002
-#define USB_DEVICE_ID_GTCO_1003		0x1003
-#define USB_DEVICE_ID_GTCO_1004		0x1004
-#define USB_DEVICE_ID_GTCO_1005		0x1005
-#define USB_DEVICE_ID_GTCO_1006		0x1006
-#define USB_DEVICE_ID_GTCO_1007		0x1007
-#define USB_VENDOR_ID_HAPP		0x078b
-#define USB_DEVICE_ID_UGCI_DRIVING	0x0010
-#define USB_DEVICE_ID_UGCI_FLYING	0x0020
-#define USB_DEVICE_ID_UGCI_FIGHTING	0x0030
-
-#define USB_VENDOR_ID_IMATION		0x0718
-#define USB_DEVICE_ID_DISC_STAKKA	0xd000
-
-#define USB_VENDOR_ID_KBGEAR		0x084e
-#define USB_DEVICE_ID_KBGEAR_JAMSTUDIO	0x1001
-
-#define USB_VENDOR_ID_LD		0x0f11
-#define USB_DEVICE_ID_LD_CASSY		0x1000
-#define USB_DEVICE_ID_LD_POCKETCASSY	0x1010
-#define USB_DEVICE_ID_LD_MOBILECASSY	0x1020
-#define USB_DEVICE_ID_LD_JWM		0x1080
-#define USB_DEVICE_ID_LD_DMMP		0x1081
-#define USB_DEVICE_ID_LD_UMIP		0x1090
-#define USB_DEVICE_ID_LD_XRAY1		0x1100
-#define USB_DEVICE_ID_LD_XRAY2		0x1101
-#define USB_DEVICE_ID_LD_VIDEOCOM	0x1200
-#define USB_DEVICE_ID_LD_COM3LAB	0x2000
-#define USB_DEVICE_ID_LD_TELEPORT	0x2010
-#define USB_DEVICE_ID_LD_NETWORKANALYSER 0x2020
-#define USB_DEVICE_ID_LD_POWERCONTROL	0x2030
-#define USB_DEVICE_ID_LD_MACHINETEST	0x2040
-
-#define USB_VENDOR_ID_LOGITECH		0x046d
-#define USB_DEVICE_ID_LOGITECH_LX3	0xc044
-#define USB_DEVICE_ID_LOGITECH_V150	0xc047
-#define USB_DEVICE_ID_LOGITECH_RECEIVER	0xc101
-#define USB_DEVICE_ID_LOGITECH_HARMONY  0xc110
-#define USB_DEVICE_ID_LOGITECH_HARMONY_2 0xc111
-#define USB_DEVICE_ID_LOGITECH_HARMONY_3 0xc112
-#define USB_DEVICE_ID_LOGITECH_HARMONY_4 0xc113
-#define USB_DEVICE_ID_LOGITECH_HARMONY_5 0xc114
-#define USB_DEVICE_ID_LOGITECH_HARMONY_6 0xc115
-#define USB_DEVICE_ID_LOGITECH_HARMONY_7 0xc116
-#define USB_DEVICE_ID_LOGITECH_HARMONY_8 0xc117
-#define USB_DEVICE_ID_LOGITECH_HARMONY_9 0xc118
-#define USB_DEVICE_ID_LOGITECH_HARMONY_10 0xc119
-#define USB_DEVICE_ID_LOGITECH_HARMONY_11 0xc11a
-#define USB_DEVICE_ID_LOGITECH_HARMONY_12 0xc11b
-#define USB_DEVICE_ID_LOGITECH_HARMONY_13 0xc11c
-#define USB_DEVICE_ID_LOGITECH_HARMONY_14 0xc11d
-#define USB_DEVICE_ID_LOGITECH_HARMONY_15 0xc11e
-#define USB_DEVICE_ID_LOGITECH_HARMONY_16 0xc11f
-#define USB_DEVICE_ID_LOGITECH_HARMONY_17 0xc120
-#define USB_DEVICE_ID_LOGITECH_HARMONY_18 0xc121
-#define USB_DEVICE_ID_LOGITECH_HARMONY_19 0xc122
-#define USB_DEVICE_ID_LOGITECH_HARMONY_20 0xc123
-#define USB_DEVICE_ID_LOGITECH_HARMONY_21 0xc124
-#define USB_DEVICE_ID_LOGITECH_HARMONY_22 0xc125
-#define USB_DEVICE_ID_LOGITECH_HARMONY_23 0xc126
-#define USB_DEVICE_ID_LOGITECH_HARMONY_24 0xc127
-#define USB_DEVICE_ID_LOGITECH_HARMONY_25 0xc128
-#define USB_DEVICE_ID_LOGITECH_HARMONY_26 0xc129
-#define USB_DEVICE_ID_LOGITECH_HARMONY_27 0xc12a
-#define USB_DEVICE_ID_LOGITECH_HARMONY_28 0xc12b
-#define USB_DEVICE_ID_LOGITECH_HARMONY_29 0xc12c
-#define USB_DEVICE_ID_LOGITECH_HARMONY_30 0xc12d
-#define USB_DEVICE_ID_LOGITECH_HARMONY_31 0xc12e
-#define USB_DEVICE_ID_LOGITECH_HARMONY_32 0xc12f
-#define USB_DEVICE_ID_LOGITECH_HARMONY_33 0xc130
-#define USB_DEVICE_ID_LOGITECH_HARMONY_34 0xc131
-#define USB_DEVICE_ID_LOGITECH_HARMONY_35 0xc132
-#define USB_DEVICE_ID_LOGITECH_HARMONY_36 0xc133
-#define USB_DEVICE_ID_LOGITECH_HARMONY_37 0xc134
-#define USB_DEVICE_ID_LOGITECH_HARMONY_38 0xc135
-#define USB_DEVICE_ID_LOGITECH_HARMONY_39 0xc136
-#define USB_DEVICE_ID_LOGITECH_HARMONY_40 0xc137
-#define USB_DEVICE_ID_LOGITECH_HARMONY_41 0xc138
-#define USB_DEVICE_ID_LOGITECH_HARMONY_42 0xc139
-#define USB_DEVICE_ID_LOGITECH_HARMONY_43 0xc13a
-#define USB_DEVICE_ID_LOGITECH_HARMONY_44 0xc13b
-#define USB_DEVICE_ID_LOGITECH_HARMONY_45 0xc13c
-#define USB_DEVICE_ID_LOGITECH_HARMONY_46 0xc13d
-#define USB_DEVICE_ID_LOGITECH_HARMONY_47 0xc13e
-#define USB_DEVICE_ID_LOGITECH_HARMONY_48 0xc13f
-#define USB_DEVICE_ID_LOGITECH_HARMONY_49 0xc140
-#define USB_DEVICE_ID_LOGITECH_HARMONY_50 0xc141
-#define USB_DEVICE_ID_LOGITECH_HARMONY_51 0xc142
-#define USB_DEVICE_ID_LOGITECH_HARMONY_52 0xc143
-#define USB_DEVICE_ID_LOGITECH_HARMONY_53 0xc144
-#define USB_DEVICE_ID_LOGITECH_HARMONY_54 0xc145
-#define USB_DEVICE_ID_LOGITECH_HARMONY_55 0xc146
-#define USB_DEVICE_ID_LOGITECH_HARMONY_56 0xc147
-#define USB_DEVICE_ID_LOGITECH_HARMONY_57 0xc148
-#define USB_DEVICE_ID_LOGITECH_HARMONY_58 0xc149
-#define USB_DEVICE_ID_LOGITECH_HARMONY_59 0xc14a
-#define USB_DEVICE_ID_LOGITECH_HARMONY_60 0xc14b
-#define USB_DEVICE_ID_LOGITECH_HARMONY_61 0xc14c
-#define USB_DEVICE_ID_LOGITECH_HARMONY_62 0xc14d
-#define USB_DEVICE_ID_LOGITECH_HARMONY_63 0xc14e
-#define USB_DEVICE_ID_LOGITECH_HARMONY_64 0xc14f
-#define USB_DEVICE_ID_LOGITECH_EXTREME_3D	0xc215
-#define USB_DEVICE_ID_LOGITECH_WHEEL	0xc294
-#define USB_DEVICE_ID_LOGITECH_ELITE_KBD	0xc30a
-#define USB_DEVICE_ID_LOGITECH_KBD	0xc311
-#define USB_DEVICE_ID_S510_RECEIVER	0xc50c
-#define USB_DEVICE_ID_S510_RECEIVER_2	0xc517
-#define USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500	0xc512
-#define USB_DEVICE_ID_MX3000_RECEIVER	0xc513
-#define USB_DEVICE_ID_DINOVO_DESKTOP	0xc704
-#define USB_DEVICE_ID_DINOVO_EDGE	0xc714
-#define USB_DEVICE_ID_DINOVO_MINI	0xc71f
-
-#define USB_VENDOR_ID_MCC		0x09db
-#define USB_DEVICE_ID_MCC_PMD1024LS	0x0076
-#define USB_DEVICE_ID_MCC_PMD1208LS	0x007a
-
-#define USB_VENDOR_ID_MGE		0x0463
-#define USB_DEVICE_ID_MGE_UPS		0xffff
-#define USB_DEVICE_ID_MGE_UPS1		0x0001
-
-#define USB_VENDOR_ID_MICROCHIP		0x04d8
-#define USB_DEVICE_ID_PICKIT1		0x0032
-#define USB_DEVICE_ID_PICKIT2		0x0033
-
-#define USB_VENDOR_ID_MICROSOFT		0x045e
-#define USB_DEVICE_ID_SIDEWINDER_GV	0x003b
-#define USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0 0x009d
-#define USB_DEVICE_ID_DESKTOP_RECV_1028 0x00f9
-#define USB_DEVICE_ID_MS_NE4K		0x00db
-#define USB_DEVICE_ID_MS_LK6K		0x00f9
-
-#define USB_VENDOR_ID_MONTEREY		0x0566
-#define USB_DEVICE_ID_GENIUS_KB29E	0x3004
-
-#define USB_VENDOR_ID_NCR		0x0404
-#define USB_DEVICE_ID_NCR_FIRST		0x0300
-#define USB_DEVICE_ID_NCR_LAST		0x03ff
-
-#define USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR 0x0400
-#define USB_DEVICE_ID_N_S_HARMONY       0xc359
-
-#define USB_VENDOR_ID_NATSU             0x08b7
-#define USB_DEVICE_ID_NATSU_GAMEPAD     0x0001
-
-#define USB_VENDOR_ID_NEC		0x073e
-#define USB_DEVICE_ID_NEC_USB_GAME_PAD	0x0301
-
-#define USB_VENDOR_ID_ONTRAK		0x0a07
-#define USB_DEVICE_ID_ONTRAK_ADU100	0x0064
-
-#define USB_VENDOR_ID_PANJIT		0x134c
-
-#define USB_VENDOR_ID_PANTHERLORD	0x0810
-#define USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK	0x0001
-
-#define USB_VENDOR_ID_PETALYNX		0x18b1
-#define USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE	0x0037
-
-#define USB_VENDOR_ID_PLAYDOTCOM	0x0b43
-#define USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII	0x0003
-
-#define USB_VENDOR_ID_SAITEK		0x06a3
-#define USB_DEVICE_ID_SAITEK_RUMBLEPAD	0xff17
-
-#define USB_VENDOR_ID_SAMSUNG		0x0419
-#define USB_DEVICE_ID_SAMSUNG_IR_REMOTE	0x0001
-
-#define USB_VENDOR_ID_SONY			0x054c
-#define USB_DEVICE_ID_SONY_PS3_CONTROLLER	0x0268
-
-#define USB_VENDOR_ID_SOUNDGRAPH	0x15c2
-#define USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD	0x0038
-
-#define USB_VENDOR_ID_SUN		0x0430
-#define USB_DEVICE_ID_RARITAN_KVM_DONGLE	0xcdab
-
-#define USB_VENDOR_ID_SUNPLUS		0x04fc
-#define USB_DEVICE_ID_SUNPLUS_WDESKTOP	0x05d8
-
-#define USB_VENDOR_ID_TOPMAX		0x0663
-#define USB_DEVICE_ID_TOPMAX_COBRAPAD	0x0103
-
-#define USB_VENDOR_ID_TURBOX		0x062a
-#define USB_DEVICE_ID_TURBOX_KEYBOARD	0x0201
-
-#define USB_VENDOR_ID_VERNIER		0x08f7
-#define USB_DEVICE_ID_VERNIER_LABPRO	0x0001
-#define USB_DEVICE_ID_VERNIER_GOTEMP	0x0002
-#define USB_DEVICE_ID_VERNIER_SKIP	0x0003
-#define USB_DEVICE_ID_VERNIER_CYCLOPS	0x0004
-#define USB_DEVICE_ID_VERNIER_LCSPEC	0x0006
-
-#define USB_VENDOR_ID_WACOM		0x056a
-
-#define USB_VENDOR_ID_WISEGROUP		0x0925
-#define USB_DEVICE_ID_1_PHIDGETSERVO_20	0x8101
-#define USB_DEVICE_ID_4_PHIDGETSERVO_20	0x8104
-#define USB_DEVICE_ID_8_8_4_IF_KIT	0x8201
-#define USB_DEVICE_ID_QUAD_USB_JOYPAD	0x8800
-#define USB_DEVICE_ID_DUAL_USB_JOYPAD	0x8866
-
-#define USB_VENDOR_ID_WISEGROUP_LTD	0x6677
-#define USB_DEVICE_ID_SMARTJOY_DUAL_PLUS 0x8802
-
-#define USB_VENDOR_ID_YEALINK		0x6993
-#define USB_DEVICE_ID_YEALINK_P1K_P4K_B2K	0xb001
-
-#define USB_VENDOR_ID_KYE		0x0458
-#define USB_DEVICE_ID_KYE_GPEN_560	0x5003
+#include "../hid-ids.h"
 
 /*
  * Alphabetically sorted blacklist by quirk type.
@@ -433,18 +28,10 @@
 	__u16 idProduct;
 	__u32 quirks;
 } hid_blacklist[] = {
-
-	{ USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU, HID_QUIRK_2WHEEL_MOUSE_HACK_7 },
-	{ USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D, HID_QUIRK_2WHEEL_MOUSE_HACK_B8 },
-	{ USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE, HID_QUIRK_2WHEEL_MOUSE_HACK_5 },
-
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER, HID_QUIRK_BAD_RELATIVE_KEYS },
-
 	{ USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_GAMEPAD, HID_QUIRK_BADPAD },
 	{ USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_PREDATOR, HID_QUIRK_BADPAD },
 	{ USB_VENDOR_ID_ALPS, USB_DEVICE_ID_IBM_GAMEPAD, HID_QUIRK_BADPAD },
 	{ USB_VENDOR_ID_CHIC, USB_DEVICE_ID_CHIC_GAMEPAD, HID_QUIRK_BADPAD },
-	{ USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR, HID_QUIRK_MULTI_INPUT },
 	{ USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_DRIVING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },
 	{ USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FLYING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },
 	{ USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FIGHTING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },
@@ -453,169 +40,11 @@
 	{ USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RUMBLEPAD, HID_QUIRK_BADPAD },
 	{ USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD, HID_QUIRK_BADPAD },
 
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP, HID_QUIRK_DUPLICATE_USAGES },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE, HID_QUIRK_DUPLICATE_USAGES },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI, HID_QUIRK_DUPLICATE_USAGES },
-
 	{ USB_VENDOR_ID_AFATECH, USB_DEVICE_ID_AFATECH_AF9016, HID_QUIRK_FULLSPEED_INTERVAL },
 
-	{ USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM, HID_QUIRK_HIDDEV },
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4, HID_QUIRK_HIDDEV | HID_QUIRK_IGNORE_HIDINPUT },
-	{ USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE, HID_QUIRK_HIDDEV | HID_QUIRK_IGNORE_HIDINPUT },
-	{ USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV, HID_QUIRK_HIDINPUT },
-
-	{ USB_VENDOR_ID_EZKEY, USB_DEVICE_ID_BTC_8193, HID_QUIRK_HWHEEL_WHEEL_INVERT },
-
-	{ USB_VENDOR_ID_ADS_TECH, USB_DEVICE_ID_ADS_TECH_RADIO_SI470X, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_01, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_10, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_20, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_21, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_22, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_23, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_AIRCABLE, USB_DEVICE_ID_AIRCABLE1, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_ALCOR, USB_DEVICE_ID_ALCOR_USBRS232, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUS_LCM, HID_QUIRK_IGNORE},
-	{ USB_VENDOR_ID_BERKSHIRE, USB_DEVICE_ID_BERKSHIRE_PCWD, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_CIDC, 0x0103, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_CYGNAL, USB_DEVICE_ID_CYGNAL_RADIO_SI470X, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_CMEDIA, USB_DEVICE_ID_CM109, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_HIDCOM, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_ULTRAMOUSE, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EARTHMATE, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EM_LT20, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GENERAL_TOUCH, 0x0001, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GENERAL_TOUCH, 0x0002, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GENERAL_TOUCH, 0x0003, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GENERAL_TOUCH, 0x0004, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_4_PHIDGETSERVO_30, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_1_PHIDGETSERVO_30, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_0_4_IF_KIT, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_16_16_IF_KIT, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_8_8_8_IF_KIT, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_7_IF_KIT, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_8_IF_KIT, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_PHIDGET_MOTORCONTROL, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_SUPER_Q2, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_GOGOPEN, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_PENPOWER, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GRETAGMACBETH, USB_DEVICE_ID_GRETAGMACBETH_HUEY, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_POWERMATE, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_SOUNDKNOB, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_90, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_100, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_101, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_103, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_104, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_105, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_106, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_107, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_108, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_200, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_201, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_202, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_203, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_204, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_205, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_206, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_207, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_300, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_301, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_302, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_303, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_304, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_305, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_306, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_307, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_308, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_309, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_400, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_401, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_402, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_403, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_404, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_405, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_500, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_501, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_502, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_503, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_504, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1000, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1001, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1002, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1003, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1004, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1005, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1006, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1007, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_IMATION, USB_DEVICE_ID_DISC_STAKKA, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_KBGEAR, USB_DEVICE_ID_KBGEAR_JAMSTUDIO, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_CASSY, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POCKETCASSY, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOBILECASSY, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_JWM, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_DMMP, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_UMIP, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY1, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY2, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_VIDEOCOM, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_COM3LAB, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_TELEPORT, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_NETWORKANALYSER, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POWERCONTROL, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MACHINETEST, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1024LS, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1208LS, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS1, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 20, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 30, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 100, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 108, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 118, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 200, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 300, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 400, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 500, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_PANJIT, 0x0001, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_PANJIT, 0x0002, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_PANJIT, 0x0003, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_PANJIT, 0x0004, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_SOUNDGRAPH, USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LABPRO, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_GOTEMP, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LCSPEC, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_4_PHIDGETSERVO_20, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_8_8_4_IF_KIT, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K, HID_QUIRK_IGNORE },
-
-	{ USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_FLAIR, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_302, HID_QUIRK_IGNORE },
-
-	{ USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICKIT1, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICKIT2, HID_QUIRK_IGNORE },
-
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD, HID_QUIRK_LOGITECH_IGNORE_DOUBLED_WHEEL | HID_QUIRK_LOGITECH_EXPANDED_KEYMAP },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500, HID_QUIRK_LOGITECH_IGNORE_DOUBLED_WHEEL | HID_QUIRK_LOGITECH_EXPANDED_KEYMAP },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_LX3, HID_QUIRK_INVERT_HWHEEL },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_V150, HID_QUIRK_INVERT_HWHEEL },
-
-	{ USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K, HID_QUIRK_MICROSOFT_KEYS },
-	{ USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_LK6K, HID_QUIRK_MICROSOFT_KEYS },
-
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE, HID_QUIRK_MIGHTYMOUSE | HID_QUIRK_INVERT_HWHEEL },
-
 	{ USB_VENDOR_ID_PANTHERLORD, USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK, HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS },
 	{ USB_VENDOR_ID_PLAYDOTCOM, USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII, HID_QUIRK_MULTI_INPUT },
 
-	{ USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER, HID_QUIRK_SONY_PS3_CONTROLLER | HID_QUIRK_HIDDEV },
-
 	{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_UC100KM, HID_QUIRK_NOGET },
 	{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS124U, HID_QUIRK_NOGET },
 	{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_2PORTKVM, HID_QUIRK_NOGET },
@@ -623,144 +52,13 @@
 	{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVMC, HID_QUIRK_NOGET },
 	{ USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET },
 	{ USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D, HID_QUIRK_NOGET },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL, HID_QUIRK_NOGET },
-	{ USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0, HID_QUIRK_NOGET },
-	{ USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE, HID_QUIRK_NOGET },
 	{ USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET },
 	{ USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_KEYBOARD, HID_QUIRK_NOGET },
 	{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS },
 	{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_QUAD_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
 
 	{ USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SMARTJOY_DUAL_PLUS, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
-
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ISO, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ANSI, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ISO, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE | HID_QUIRK_APPLE_ISO_KEYBOARD},
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_JIS, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ANSI, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ISO, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE | HID_QUIRK_APPLE_ISO_KEYBOARD},
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_JIS, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ANSI, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ISO, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE | HID_QUIRK_APPLE_ISO_KEYBOARD},
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ANSI, HID_QUIRK_APPLE_HAS_FN },
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ISO, HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_APPLE_ISO_KEYBOARD },
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_JIS, HID_QUIRK_APPLE_HAS_FN },
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ANSI, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ISO, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_JIS, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN },
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_APPLE_ISO_KEYBOARD },
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN },
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ANSI, HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ISO, HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_APPLE_ISO_KEYBOARD | HID_QUIRK_IGNORE_MOUSE},
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_JIS, HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE},
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI, HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE},
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ISO, HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_APPLE_ISO_KEYBOARD | HID_QUIRK_IGNORE_MOUSE },
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_JIS, HID_QUIRK_APPLE_HAS_FN  | HID_QUIRK_IGNORE_MOUSE },
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-
-	{ USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_W7658, HID_QUIRK_RESET_LEDS },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_KBD, HID_QUIRK_RESET_LEDS },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_2, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_3, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_4, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_5, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_6, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_7, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_8, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_9, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_10, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_11, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_12, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_13, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_14, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_15, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_16, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_17, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_18, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_19, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_20, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_21, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_22, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_23, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_24, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_25, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_26, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_27, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_28, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_29, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_30, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_31, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_32, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_33, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_34, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_35, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_36, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_37, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_38, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_39, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_40, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_41, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_42, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_43, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_44, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_45, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_46, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_47, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_48, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_49, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_50, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_51, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_52, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_53, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_54, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_55, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_56, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_57, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_58, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_59, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_60, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_61, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_62, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_63, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_64, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR, USB_DEVICE_ID_N_S_HARMONY, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_GPEN_560, HID_QUIRK_IGNORE },
-
-	{ 0, 0 }
-};
-
-/* Quirks for devices which require report descriptor fixup go here */
-static const struct hid_rdesc_blacklist {
-	__u16 idVendor;
-	__u16 idProduct;
-	__u32 quirks;
-} hid_rdesc_blacklist[] = {
-
-	{ USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION, HID_QUIRK_RDESC_CYMOTION },
-
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER, HID_QUIRK_RDESC_LOGITECH },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER, HID_QUIRK_RDESC_LOGITECH },
-	{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2, HID_QUIRK_RDESC_LOGITECH },
-	{ USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_DESKTOP_RECV_1028, HID_QUIRK_RDESC_MICROSOFT_RECV_1028 },
-
-	{ USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E, HID_QUIRK_RDESC_BUTTON_CONSUMER },
-
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS, HID_QUIRK_RDESC_MACBOOK_JIS },
-
-	{ USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE, HID_QUIRK_RDESC_PETALYNX },
-
-	{ USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE, HID_QUIRK_RDESC_SAMSUNG_REMOTE },
-
-	{ USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP, HID_QUIRK_RDESC_SUNPLUS_WDESKTOP },
-
-	{ USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1, HID_QUIRK_RDESC_SWAPPED_MIN_MAX },
-	{ USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2, HID_QUIRK_RDESC_SWAPPED_MIN_MAX },
+	{ USB_VENDOR_ID_WISEGROUP_LTD2, USB_DEVICE_ID_SMARTJOY_DUAL_PLUS, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
 
 	{ 0, 0 }
 };
@@ -974,16 +272,6 @@
 	u32 quirks = 0;
 	const struct hid_blacklist *bl_entry = NULL;
 
-	/* Ignore all Wacom devices */
-	if (idVendor == USB_VENDOR_ID_WACOM)
-		return HID_QUIRK_IGNORE;
-
-	/* ignore all Code Mercenaries IOWarrior devices */
-	if (idVendor == USB_VENDOR_ID_CODEMERCS)
-		if (idProduct >= USB_DEVICE_ID_CODEMERCS_IOW_FIRST &&
-				idProduct <= USB_DEVICE_ID_CODEMERCS_IOW_LAST)
-			return HID_QUIRK_IGNORE;
-
 	/* NCR devices must not be queried for reports */
 	if (idVendor == USB_VENDOR_ID_NCR &&
 			idProduct >= USB_DEVICE_ID_NCR_FIRST &&
@@ -1002,221 +290,3 @@
 }
 
 EXPORT_SYMBOL_GPL(usbhid_lookup_quirk);
-
-/*
- * Cherry Cymotion keyboard have an invalid HID report descriptor,
- * that needs fixing before we can parse it.
- */
-static void usbhid_fixup_cymotion_descriptor(char *rdesc, int rsize)
-{
-	if (rsize >= 17 && rdesc[11] == 0x3c && rdesc[12] == 0x02) {
-		printk(KERN_INFO "Fixing up Cherry Cymotion report descriptor\n");
-		rdesc[11] = rdesc[16] = 0xff;
-		rdesc[12] = rdesc[17] = 0x03;
-	}
-}
-
-
-/*
- * Certain Logitech keyboards send in report #3 keys which are far
- * above the logical maximum described in descriptor. This extends
- * the original value of 0x28c of logical maximum to 0x104d
- */
-static void usbhid_fixup_logitech_descriptor(unsigned char *rdesc, int rsize)
-{
-	if (rsize >= 90 && rdesc[83] == 0x26
-			&& rdesc[84] == 0x8c
-			&& rdesc[85] == 0x02) {
-		printk(KERN_INFO "Fixing up Logitech keyboard report descriptor\n");
-		rdesc[84] = rdesc[89] = 0x4d;
-		rdesc[85] = rdesc[90] = 0x10;
-	}
-}
-
-static void usbhid_fixup_sunplus_wdesktop(unsigned char *rdesc, int rsize)
-{
-	if (rsize >= 107 && rdesc[104] == 0x26
-			 && rdesc[105] == 0x80
-			 && rdesc[106] == 0x03) {
-		printk(KERN_INFO "Fixing up Sunplus Wireless Desktop report descriptor\n");
-		rdesc[105] = rdesc[110] = 0x03;
-		rdesc[106] = rdesc[111] = 0x21;
-	}
-}
-
-/*
- * Samsung IrDA remote controller (reports as Cypress USB Mouse).
- *
- * Vendor specific report #4 has a size of 48 bit,
- * and therefore is not accepted when inspecting the descriptors.
- * As a workaround we reinterpret the report as:
- *   Variable type, count 6, size 8 bit, log. maximum 255
- * The burden to reconstruct the data is moved into user space.
- */
-static void usbhid_fixup_samsung_irda_descriptor(unsigned char *rdesc,
-						  int rsize)
-{
-	if (rsize >= 182 && rdesc[175] == 0x25
-			 && rdesc[176] == 0x40
-			 && rdesc[177] == 0x75
-			 && rdesc[178] == 0x30
-			 && rdesc[179] == 0x95
-			 && rdesc[180] == 0x01
-			 && rdesc[182] == 0x40) {
-		printk(KERN_INFO "Fixing up Samsung IrDA report descriptor\n");
-		rdesc[176] = 0xff;
-		rdesc[178] = 0x08;
-		rdesc[180] = 0x06;
-		rdesc[182] = 0x42;
-	}
-}
-
-/* Petalynx Maxter Remote has maximum for consumer page set too low */
-static void usbhid_fixup_petalynx_descriptor(unsigned char *rdesc, int rsize)
-{
-	if (rsize >= 60 && rdesc[39] == 0x2a
-			&& rdesc[40] == 0xf5
-			&& rdesc[41] == 0x00
-			&& rdesc[59] == 0x26
-			&& rdesc[60] == 0xf9
-			&& rdesc[61] == 0x00) {
-		printk(KERN_INFO "Fixing up Petalynx Maxter Remote report descriptor\n");
-		rdesc[60] = 0xfa;
-		rdesc[40] = 0xfa;
-	}
-}
-
-/*
- * Some USB barcode readers from cypress have usage min and usage max in
- * the wrong order
- */
-static void usbhid_fixup_cypress_descriptor(unsigned char *rdesc, int rsize)
-{
-	short fixed = 0;
-	int i;
-
-	for (i = 0; i < rsize - 4; i++) {
-		if (rdesc[i] == 0x29 && rdesc [i+2] == 0x19) {
-			unsigned char tmp;
-
-			rdesc[i] = 0x19; rdesc[i+2] = 0x29;
-			tmp = rdesc[i+3];
-			rdesc[i+3] = rdesc[i+1];
-			rdesc[i+1] = tmp;
-		}
-	}
-
-	if (fixed)
-		printk(KERN_INFO "Fixing up Cypress report descriptor\n");
-}
-
-/*
- * MacBook JIS keyboard has wrong logical maximum
- */
-static void usbhid_fixup_macbook_descriptor(unsigned char *rdesc, int rsize)
-{
-	if (rsize >= 60 && rdesc[53] == 0x65
-			&& rdesc[59] == 0x65) {
-		printk(KERN_INFO "Fixing up MacBook JIS keyboard report descriptor\n");
-		rdesc[53] = rdesc[59] = 0xe7;
-	}
-}
-
-static void usbhid_fixup_button_consumer_descriptor(unsigned char *rdesc, int rsize)
-{
-	if (rsize >= 30 && rdesc[29] == 0x05
-			&& rdesc[30] == 0x09) {
-		printk(KERN_INFO "Fixing up button/consumer in HID report descriptor\n");
-		rdesc[30] = 0x0c;
-	}
-}
-
-/*
- * Microsoft Wireless Desktop Receiver (Model 1028) has several
- * 'Usage Min/Max' where it ought to have 'Physical Min/Max'
- */
-static void usbhid_fixup_microsoft_descriptor(unsigned char *rdesc, int rsize)
-{
-	if (rsize == 571 && rdesc[284] == 0x19
-	                 && rdesc[286] == 0x2a
-	                 && rdesc[304] == 0x19
-	                 && rdesc[306] == 0x29
-	                 && rdesc[352] == 0x1a
-	                 && rdesc[355] == 0x2a
-			 && rdesc[557] == 0x19
-			 && rdesc[559] == 0x29) {
-		printk(KERN_INFO "Fixing up Microsoft Wireless Receiver Model 1028 report descriptor\n");
-		rdesc[284] = rdesc[304] = rdesc[557] = 0x35;
-		rdesc[352] = 0x36;
-		rdesc[286] = rdesc[355] = 0x46;
-		rdesc[306] = rdesc[559] = 0x45;
-	}
-}
-
-static void __usbhid_fixup_report_descriptor(__u32 quirks, char *rdesc, unsigned rsize)
-{
-	if ((quirks & HID_QUIRK_RDESC_CYMOTION))
-		usbhid_fixup_cymotion_descriptor(rdesc, rsize);
-
-	if (quirks & HID_QUIRK_RDESC_LOGITECH)
-		usbhid_fixup_logitech_descriptor(rdesc, rsize);
-
-	if (quirks & HID_QUIRK_RDESC_SWAPPED_MIN_MAX)
-		usbhid_fixup_cypress_descriptor(rdesc, rsize);
-
-	if (quirks & HID_QUIRK_RDESC_PETALYNX)
-		usbhid_fixup_petalynx_descriptor(rdesc, rsize);
-
-	if (quirks & HID_QUIRK_RDESC_MACBOOK_JIS)
-		usbhid_fixup_macbook_descriptor(rdesc, rsize);
-
-	if (quirks & HID_QUIRK_RDESC_BUTTON_CONSUMER)
-		usbhid_fixup_button_consumer_descriptor(rdesc, rsize);
-
-	if (quirks & HID_QUIRK_RDESC_SAMSUNG_REMOTE)
-		usbhid_fixup_samsung_irda_descriptor(rdesc, rsize);
-
-	if (quirks & HID_QUIRK_RDESC_MICROSOFT_RECV_1028)
-		usbhid_fixup_microsoft_descriptor(rdesc, rsize);
-
-	if (quirks & HID_QUIRK_RDESC_SUNPLUS_WDESKTOP)
-		usbhid_fixup_sunplus_wdesktop(rdesc, rsize);
-}
-
-/**
- * usbhid_fixup_report_descriptor: check if report descriptor needs fixup
- *
- * Description:
- *	Walks the hid_rdesc_blacklist[] array and checks whether the device
- *	is known to have broken report descriptor that needs to be fixed up
- *	prior to entering the HID parser
- *
- * Returns: nothing
- */
-void usbhid_fixup_report_descriptor(const u16 idVendor, const u16 idProduct,
-				    char *rdesc, unsigned rsize, char **quirks_param)
-{
-	int n, m;
-	u16 paramVendor, paramProduct;
-	u32 quirks;
-
-	/* static rdesc quirk entries */
-	for (n = 0; hid_rdesc_blacklist[n].idVendor; n++)
-		if (hid_rdesc_blacklist[n].idVendor == idVendor &&
-				hid_rdesc_blacklist[n].idProduct == idProduct)
-			__usbhid_fixup_report_descriptor(hid_rdesc_blacklist[n].quirks,
-					rdesc, rsize);
-
-	/* runtime rdesc quirk entries handling */
-	for (n = 0; quirks_param[n] && n < MAX_USBHID_BOOT_QUIRKS; n++) {
-		m = sscanf(quirks_param[n], "0x%hx:0x%hx:0x%x",
-				&paramVendor, &paramProduct, &quirks);
-
-		if (m != 3)
-			printk(KERN_WARNING
-				"Could not parse HID quirk module param %s\n",
-				quirks_param[n]);
-		else if (paramVendor == idVendor && paramProduct == idProduct)
-			__usbhid_fixup_report_descriptor(quirks, rdesc, rsize);
-	}
-}
diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c
index 842e9ed..babd65d 100644
--- a/drivers/hid/usbhid/hiddev.c
+++ b/drivers/hid/usbhid/hiddev.c
@@ -790,21 +790,23 @@
 /*
  * This is where hid.c calls us to connect a hid device to the hiddev driver
  */
-int hiddev_connect(struct hid_device *hid)
+int hiddev_connect(struct hid_device *hid, unsigned int force)
 {
 	struct hiddev *hiddev;
 	struct usbhid_device *usbhid = hid->driver_data;
-	int i;
 	int retval;
 
-	for (i = 0; i < hid->maxcollection; i++)
-		if (hid->collection[i].type ==
-		    HID_COLLECTION_APPLICATION &&
-		    !IS_INPUT_APPLICATION(hid->collection[i].usage))
-			break;
+	if (!force) {
+		unsigned int i;
+		for (i = 0; i < hid->maxcollection; i++)
+			if (hid->collection[i].type ==
+			    HID_COLLECTION_APPLICATION &&
+			    !IS_INPUT_APPLICATION(hid->collection[i].usage))
+				break;
 
-	if (i == hid->maxcollection && (hid->quirks & HID_QUIRK_HIDDEV) == 0)
-		return -1;
+		if (i == hid->maxcollection)
+			return -1;
+	}
 
 	if (!(hiddev = kzalloc(sizeof(struct hiddev), GFP_KERNEL)))
 		return -1;
diff --git a/drivers/hid/usbhid/usbhid.h b/drivers/hid/usbhid/usbhid.h
index 62d2d7c..abedb13 100644
--- a/drivers/hid/usbhid/usbhid.h
+++ b/drivers/hid/usbhid/usbhid.h
@@ -67,7 +67,7 @@
 	spinlock_t ctrllock;                                            /* Control fifo spinlock */
 
 	struct urb *urbout;                                             /* Output URB */
-	struct hid_report *out[HID_CONTROL_FIFO_SIZE];                  /* Output pipe fifo */
+	struct hid_output_fifo out[HID_CONTROL_FIFO_SIZE];              /* Output pipe fifo */
 	unsigned char outhead, outtail;                                 /* Output pipe fifo head & tail */
 	char *outbuf;                                                   /* Output buffer */
 	dma_addr_t outbuf_dma;                                          /* Output buffer dma */
@@ -82,7 +82,7 @@
 };
 
 #define	hid_to_usb_dev(hid_dev) \
-	container_of(hid_dev->dev->parent, struct usb_device, dev)
+	container_of(hid_dev->dev.parent->parent, struct usb_device, dev)
 
 #endif
 
diff --git a/drivers/hid/usbhid/usbkbd.c b/drivers/hid/usbhid/usbkbd.c
index 0caaafe..b342926 100644
--- a/drivers/hid/usbhid/usbkbd.c
+++ b/drivers/hid/usbhid/usbkbd.c
@@ -105,14 +105,16 @@
 			if (usb_kbd_keycode[kbd->old[i]])
 				input_report_key(kbd->dev, usb_kbd_keycode[kbd->old[i]], 0);
 			else
-				info("Unknown key (scancode %#x) released.", kbd->old[i]);
+				dev_info(&urb->dev->dev,
+						"Unknown key (scancode %#x) released.\n", kbd->old[i]);
 		}
 
 		if (kbd->new[i] > 3 && memscan(kbd->old + 2, kbd->new[i], 6) == kbd->old + 8) {
 			if (usb_kbd_keycode[kbd->new[i]])
 				input_report_key(kbd->dev, usb_kbd_keycode[kbd->new[i]], 1);
 			else
-				info("Unknown key (scancode %#x) pressed.", kbd->new[i]);
+				dev_info(&urb->dev->dev,
+						"Unknown key (scancode %#x) released.\n", kbd->new[i]);
 		}
 	}
 
@@ -159,7 +161,8 @@
 	struct usb_kbd *kbd = urb->context;
 
 	if (urb->status)
-		warn("led urb status %d received", urb->status);
+		dev_warn(&urb->dev->dev, "led urb status %d received\n",
+			 urb->status);
 
 	if (*(kbd->leds) == kbd->newleds)
 		return;
@@ -352,7 +355,8 @@
 {
 	int result = usb_register(&usb_kbd_driver);
 	if (result == 0)
-		info(DRIVER_VERSION ":" DRIVER_DESC);
+		printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
+				DRIVER_DESC "\n");
 	return result;
 }
 
diff --git a/drivers/hid/usbhid/usbmouse.c b/drivers/hid/usbhid/usbmouse.c
index 35689ef..72ab4b2 100644
--- a/drivers/hid/usbhid/usbmouse.c
+++ b/drivers/hid/usbhid/usbmouse.c
@@ -31,6 +31,11 @@
 #include <linux/usb/input.h>
 #include <linux/hid.h>
 
+/* for apple IDs */
+#ifdef CONFIG_USB_HID_MODULE
+#include "../hid-ids.h"
+#endif
+
 /*
  * Version Information
  */
@@ -240,7 +245,8 @@
 {
 	int retval = usb_register(&usb_mouse_driver);
 	if (retval == 0)
-		info(DRIVER_VERSION ":" DRIVER_DESC);
+		printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
+				DRIVER_DESC "\n");
 	return retval;
 }
 
diff --git a/drivers/input/misc/hp_sdc_rtc.c b/drivers/input/misc/hp_sdc_rtc.c
index daa9d42..82ec6b1 100644
--- a/drivers/input/misc/hp_sdc_rtc.c
+++ b/drivers/input/misc/hp_sdc_rtc.c
@@ -458,35 +458,35 @@
 		p += sprintf(p, "i8042 rtc\t: READ FAILED!\n");
 	} else {
 		p += sprintf(p, "i8042 rtc\t: %ld.%02d seconds\n", 
-			     tv.tv_sec, tv.tv_usec/1000);
+			     tv.tv_sec, (int)tv.tv_usec/1000);
 	}
 
 	if (hp_sdc_rtc_read_fhs(&tv)) {
 		p += sprintf(p, "handshake\t: READ FAILED!\n");
 	} else {
         	p += sprintf(p, "handshake\t: %ld.%02d seconds\n", 
-			     tv.tv_sec, tv.tv_usec/1000);
+			     tv.tv_sec, (int)tv.tv_usec/1000);
 	}
 
 	if (hp_sdc_rtc_read_mt(&tv)) {
 		p += sprintf(p, "alarm\t\t: READ FAILED!\n");
 	} else {
 		p += sprintf(p, "alarm\t\t: %ld.%02d seconds\n", 
-			     tv.tv_sec, tv.tv_usec/1000);
+			     tv.tv_sec, (int)tv.tv_usec/1000);
 	}
 
 	if (hp_sdc_rtc_read_dt(&tv)) {
 		p += sprintf(p, "delay\t\t: READ FAILED!\n");
 	} else {
 		p += sprintf(p, "delay\t\t: %ld.%02d seconds\n", 
-			     tv.tv_sec, tv.tv_usec/1000);
+			     tv.tv_sec, (int)tv.tv_usec/1000);
 	}
 
 	if (hp_sdc_rtc_read_ct(&tv)) {
 		p += sprintf(p, "periodic\t: READ FAILED!\n");
 	} else {
 		p += sprintf(p, "periodic\t: %ld.%02d seconds\n", 
-			     tv.tv_sec, tv.tv_usec/1000);
+			     tv.tv_sec, (int)tv.tv_usec/1000);
 	}
 
         p += sprintf(p,
diff --git a/drivers/input/serio/hp_sdc.c b/drivers/input/serio/hp_sdc.c
index 0d39597..bfe4924 100644
--- a/drivers/input/serio/hp_sdc.c
+++ b/drivers/input/serio/hp_sdc.c
@@ -323,7 +323,7 @@
 			 * it back to the application. and be less verbose.
 			 */
 			printk(KERN_WARNING PREFIX "read timeout (%ius)!\n",
-			       tv.tv_usec - hp_sdc.rtv.tv_usec);
+			       (int)(tv.tv_usec - hp_sdc.rtv.tv_usec));
 			curr->idx += hp_sdc.rqty;
 			hp_sdc.rqty = 0;
 			tmp = curr->seq[curr->actidx];
diff --git a/drivers/isdn/mISDN/dsp_cmx.c b/drivers/isdn/mISDN/dsp_cmx.c
index e92b1ba..c2f51cc 100644
--- a/drivers/isdn/mISDN/dsp_cmx.c
+++ b/drivers/isdn/mISDN/dsp_cmx.c
@@ -452,10 +452,10 @@
 			if (finddsp->features.pcm_id == dsp->features.pcm_id) {
 				if (finddsp->pcm_slot_rx >= 0 &&
 				    finddsp->pcm_slot_rx < sizeof(freeslots))
-					freeslots[finddsp->pcm_slot_tx] = 0;
+					freeslots[finddsp->pcm_slot_rx] = 0;
 				if (finddsp->pcm_slot_tx >= 0 &&
 				    finddsp->pcm_slot_tx < sizeof(freeslots))
-					freeslots[finddsp->pcm_slot_rx] = 0;
+					freeslots[finddsp->pcm_slot_tx] = 0;
 			}
 		}
 		i = 0;
diff --git a/drivers/isdn/mISDN/timerdev.c b/drivers/isdn/mISDN/timerdev.c
index e746292..875fabe 100644
--- a/drivers/isdn/mISDN/timerdev.c
+++ b/drivers/isdn/mISDN/timerdev.c
@@ -61,7 +61,7 @@
 	init_waitqueue_head(&dev->wait);
 	filep->private_data = dev;
 	__module_get(THIS_MODULE);
-	return 0;
+	return nonseekable_open(ino, filep);
 }
 
 static int
diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig
index 07d92c1..2281b50 100644
--- a/drivers/md/Kconfig
+++ b/drivers/md/Kconfig
@@ -30,6 +30,20 @@
 
 	  If unsure, say N.
 
+config MD_AUTODETECT
+	bool "Autodetect RAID arrays during kernel boot"
+	depends on BLK_DEV_MD=y
+	default y
+	---help---
+	  If you say Y here, then the kernel will try to autodetect raid
+	  arrays as part of its boot process. 
+
+	  If you don't use raid and say Y, this autodetection can cause 
+	  a several-second delay in the boot time due to various
+	  synchronisation steps that are part of this step.
+
+	  If unsure, say Y.
+
 config MD_LINEAR
 	tristate "Linear (append) mode"
 	depends on BLK_DEV_MD
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 0dae245..5eff8ad 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -87,6 +87,44 @@
 	help
 	  Support for Toshiba Mobile IO Controller TC6393XB
 
+config MFD_WM8400
+	tristate "Support Wolfson Microelectronics WM8400"
+	help
+	  Support for the Wolfson Microelecronics WM8400 PMIC and audio
+	  CODEC.  This driver adds provides common support for accessing
+	  the device, additional drivers must be enabled in order to use
+	  the functionality of the device.
+
+config MFD_WM8350
+	tristate
+
+config MFD_WM8350_CONFIG_MODE_0
+	bool
+	depends on MFD_WM8350
+
+config MFD_WM8350_CONFIG_MODE_1
+	bool
+	depends on MFD_WM8350
+
+config MFD_WM8350_CONFIG_MODE_2
+	bool
+	depends on MFD_WM8350
+
+config MFD_WM8350_CONFIG_MODE_3
+	bool
+	depends on MFD_WM8350
+
+config MFD_WM8350_I2C
+	tristate "Support Wolfson Microelectronics WM8350 with I2C"
+	select MFD_WM8350
+	depends on I2C
+	help
+	  The WM8350 is an integrated audio and power management
+	  subsystem with watchdog and RTC functionality for embedded
+	  systems.  This option enables core support for the WM8350 with
+	  I2C as the control interface.  Additional options must be
+	  selected to enable support for the functionality of the chip.
+
 endmenu
 
 menu "Multimedia Capabilities Port drivers"
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 6abebe3..759b1fe 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -12,6 +12,11 @@
 obj-$(CONFIG_MFD_TC6387XB)	+= tc6387xb.o
 obj-$(CONFIG_MFD_TC6393XB)	+= tc6393xb.o
 
+obj-$(CONFIG_MFD_WM8400)	+= wm8400-core.o
+wm8350-objs			:= wm8350-core.o wm8350-regmap.o wm8350-gpio.o
+obj-$(CONFIG_MFD_WM8350)	+= wm8350.o
+obj-$(CONFIG_MFD_WM8350_I2C)	+= wm8350-i2c.o
+
 obj-$(CONFIG_MFD_CORE)		+= mfd-core.o
 
 obj-$(CONFIG_MCP)		+= mcp-core.o
diff --git a/drivers/mfd/wm8350-core.c b/drivers/mfd/wm8350-core.c
new file mode 100644
index 0000000..25a7a5d
--- /dev/null
+++ b/drivers/mfd/wm8350-core.c
@@ -0,0 +1,1273 @@
+/*
+ * wm8350-core.c  --  Device access for Wolfson WM8350
+ *
+ * Copyright 2007, 2008 Wolfson Microelectronics PLC.
+ *
+ * Author: Liam Girdwood, Mark Brown
+ *
+ *  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/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/bug.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+
+#include <linux/mfd/wm8350/core.h>
+#include <linux/mfd/wm8350/audio.h>
+#include <linux/mfd/wm8350/comparator.h>
+#include <linux/mfd/wm8350/gpio.h>
+#include <linux/mfd/wm8350/pmic.h>
+#include <linux/mfd/wm8350/rtc.h>
+#include <linux/mfd/wm8350/supply.h>
+#include <linux/mfd/wm8350/wdt.h>
+
+#define WM8350_UNLOCK_KEY		0x0013
+#define WM8350_LOCK_KEY			0x0000
+
+#define WM8350_CLOCK_CONTROL_1		0x28
+#define WM8350_AIF_TEST			0x74
+
+/* debug */
+#define WM8350_BUS_DEBUG 0
+#if WM8350_BUS_DEBUG
+#define dump(regs, src) do { \
+	int i_; \
+	u16 *src_ = src; \
+	printk(KERN_DEBUG); \
+	for (i_ = 0; i_ < regs; i_++) \
+		printk(" 0x%4.4x", *src_++); \
+	printk("\n"); \
+} while (0);
+#else
+#define dump(bytes, src)
+#endif
+
+#define WM8350_LOCK_DEBUG 0
+#if WM8350_LOCK_DEBUG
+#define ldbg(format, arg...) printk(format, ## arg)
+#else
+#define ldbg(format, arg...)
+#endif
+
+/*
+ * WM8350 Device IO
+ */
+static DEFINE_MUTEX(io_mutex);
+static DEFINE_MUTEX(reg_lock_mutex);
+static DEFINE_MUTEX(auxadc_mutex);
+
+/* Perform a physical read from the device.
+ */
+static int wm8350_phys_read(struct wm8350 *wm8350, u8 reg, int num_regs,
+			    u16 *dest)
+{
+	int i, ret;
+	int bytes = num_regs * 2;
+
+	dev_dbg(wm8350->dev, "volatile read\n");
+	ret = wm8350->read_dev(wm8350, reg, bytes, (char *)dest);
+
+	for (i = reg; i < reg + num_regs; i++) {
+		/* Cache is CPU endian */
+		dest[i - reg] = be16_to_cpu(dest[i - reg]);
+
+		/* Satisfy non-volatile bits from cache */
+		dest[i - reg] &= wm8350_reg_io_map[i].vol;
+		dest[i - reg] |= wm8350->reg_cache[i];
+
+		/* Mask out non-readable bits */
+		dest[i - reg] &= wm8350_reg_io_map[i].readable;
+	}
+
+	dump(num_regs, dest);
+
+	return ret;
+}
+
+static int wm8350_read(struct wm8350 *wm8350, u8 reg, int num_regs, u16 *dest)
+{
+	int i;
+	int end = reg + num_regs;
+	int ret = 0;
+	int bytes = num_regs * 2;
+
+	if (wm8350->read_dev == NULL)
+		return -ENODEV;
+
+	if ((reg + num_regs - 1) > WM8350_MAX_REGISTER) {
+		dev_err(wm8350->dev, "invalid reg %x\n",
+			reg + num_regs - 1);
+		return -EINVAL;
+	}
+
+	dev_dbg(wm8350->dev,
+		"%s R%d(0x%2.2x) %d regs\n", __func__, reg, reg, num_regs);
+
+#if WM8350_BUS_DEBUG
+	/* we can _safely_ read any register, but warn if read not supported */
+	for (i = reg; i < end; i++) {
+		if (!wm8350_reg_io_map[i].readable)
+			dev_warn(wm8350->dev,
+				"reg R%d is not readable\n", i);
+	}
+#endif
+
+	/* if any volatile registers are required, then read back all */
+	for (i = reg; i < end; i++)
+		if (wm8350_reg_io_map[i].vol)
+			return wm8350_phys_read(wm8350, reg, num_regs, dest);
+
+	/* no volatiles, then cache is good */
+	dev_dbg(wm8350->dev, "cache read\n");
+	memcpy(dest, &wm8350->reg_cache[reg], bytes);
+	dump(num_regs, dest);
+	return ret;
+}
+
+static inline int is_reg_locked(struct wm8350 *wm8350, u8 reg)
+{
+	if (reg == WM8350_SECURITY ||
+	    wm8350->reg_cache[WM8350_SECURITY] == WM8350_UNLOCK_KEY)
+		return 0;
+
+	if ((reg == WM8350_GPIO_CONFIGURATION_I_O) ||
+	    (reg >= WM8350_GPIO_FUNCTION_SELECT_1 &&
+	     reg <= WM8350_GPIO_FUNCTION_SELECT_4) ||
+	    (reg >= WM8350_BATTERY_CHARGER_CONTROL_1 &&
+	     reg <= WM8350_BATTERY_CHARGER_CONTROL_3))
+		return 1;
+	return 0;
+}
+
+static int wm8350_write(struct wm8350 *wm8350, u8 reg, int num_regs, u16 *src)
+{
+	int i;
+	int end = reg + num_regs;
+	int bytes = num_regs * 2;
+
+	if (wm8350->write_dev == NULL)
+		return -ENODEV;
+
+	if ((reg + num_regs - 1) > WM8350_MAX_REGISTER) {
+		dev_err(wm8350->dev, "invalid reg %x\n",
+			reg + num_regs - 1);
+		return -EINVAL;
+	}
+
+	/* it's generally not a good idea to write to RO or locked registers */
+	for (i = reg; i < end; i++) {
+		if (!wm8350_reg_io_map[i].writable) {
+			dev_err(wm8350->dev,
+				"attempted write to read only reg R%d\n", i);
+			return -EINVAL;
+		}
+
+		if (is_reg_locked(wm8350, i)) {
+			dev_err(wm8350->dev,
+			       "attempted write to locked reg R%d\n", i);
+			return -EINVAL;
+		}
+
+		src[i - reg] &= wm8350_reg_io_map[i].writable;
+
+		wm8350->reg_cache[i] =
+			(wm8350->reg_cache[i] & ~wm8350_reg_io_map[i].writable)
+			| src[i - reg];
+
+		src[i - reg] = cpu_to_be16(src[i - reg]);
+	}
+
+	/* Actually write it out */
+	return wm8350->write_dev(wm8350, reg, bytes, (char *)src);
+}
+
+/*
+ * Safe read, modify, write methods
+ */
+int wm8350_clear_bits(struct wm8350 *wm8350, u16 reg, u16 mask)
+{
+	u16 data;
+	int err;
+
+	mutex_lock(&io_mutex);
+	err = wm8350_read(wm8350, reg, 1, &data);
+	if (err) {
+		dev_err(wm8350->dev, "read from reg R%d failed\n", reg);
+		goto out;
+	}
+
+	data &= ~mask;
+	err = wm8350_write(wm8350, reg, 1, &data);
+	if (err)
+		dev_err(wm8350->dev, "write to reg R%d failed\n", reg);
+out:
+	mutex_unlock(&io_mutex);
+	return err;
+}
+EXPORT_SYMBOL_GPL(wm8350_clear_bits);
+
+int wm8350_set_bits(struct wm8350 *wm8350, u16 reg, u16 mask)
+{
+	u16 data;
+	int err;
+
+	mutex_lock(&io_mutex);
+	err = wm8350_read(wm8350, reg, 1, &data);
+	if (err) {
+		dev_err(wm8350->dev, "read from reg R%d failed\n", reg);
+		goto out;
+	}
+
+	data |= mask;
+	err = wm8350_write(wm8350, reg, 1, &data);
+	if (err)
+		dev_err(wm8350->dev, "write to reg R%d failed\n", reg);
+out:
+	mutex_unlock(&io_mutex);
+	return err;
+}
+EXPORT_SYMBOL_GPL(wm8350_set_bits);
+
+u16 wm8350_reg_read(struct wm8350 *wm8350, int reg)
+{
+	u16 data;
+	int err;
+
+	mutex_lock(&io_mutex);
+	err = wm8350_read(wm8350, reg, 1, &data);
+	if (err)
+		dev_err(wm8350->dev, "read from reg R%d failed\n", reg);
+
+	mutex_unlock(&io_mutex);
+	return data;
+}
+EXPORT_SYMBOL_GPL(wm8350_reg_read);
+
+int wm8350_reg_write(struct wm8350 *wm8350, int reg, u16 val)
+{
+	int ret;
+	u16 data = val;
+
+	mutex_lock(&io_mutex);
+	ret = wm8350_write(wm8350, reg, 1, &data);
+	if (ret)
+		dev_err(wm8350->dev, "write to reg R%d failed\n", reg);
+	mutex_unlock(&io_mutex);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wm8350_reg_write);
+
+int wm8350_block_read(struct wm8350 *wm8350, int start_reg, int regs,
+		      u16 *dest)
+{
+	int err = 0;
+
+	mutex_lock(&io_mutex);
+	err = wm8350_read(wm8350, start_reg, regs, dest);
+	if (err)
+		dev_err(wm8350->dev, "block read starting from R%d failed\n",
+			start_reg);
+	mutex_unlock(&io_mutex);
+	return err;
+}
+EXPORT_SYMBOL_GPL(wm8350_block_read);
+
+int wm8350_block_write(struct wm8350 *wm8350, int start_reg, int regs,
+		       u16 *src)
+{
+	int ret = 0;
+
+	mutex_lock(&io_mutex);
+	ret = wm8350_write(wm8350, start_reg, regs, src);
+	if (ret)
+		dev_err(wm8350->dev, "block write starting at R%d failed\n",
+			start_reg);
+	mutex_unlock(&io_mutex);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wm8350_block_write);
+
+int wm8350_reg_lock(struct wm8350 *wm8350)
+{
+	u16 key = WM8350_LOCK_KEY;
+	int ret;
+
+	ldbg(__func__);
+	mutex_lock(&io_mutex);
+	ret = wm8350_write(wm8350, WM8350_SECURITY, 1, &key);
+	if (ret)
+		dev_err(wm8350->dev, "lock failed\n");
+	mutex_unlock(&io_mutex);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wm8350_reg_lock);
+
+int wm8350_reg_unlock(struct wm8350 *wm8350)
+{
+	u16 key = WM8350_UNLOCK_KEY;
+	int ret;
+
+	ldbg(__func__);
+	mutex_lock(&io_mutex);
+	ret = wm8350_write(wm8350, WM8350_SECURITY, 1, &key);
+	if (ret)
+		dev_err(wm8350->dev, "unlock failed\n");
+	mutex_unlock(&io_mutex);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wm8350_reg_unlock);
+
+static void wm8350_irq_call_handler(struct wm8350 *wm8350, int irq)
+{
+	mutex_lock(&wm8350->irq_mutex);
+
+	if (wm8350->irq[irq].handler)
+		wm8350->irq[irq].handler(wm8350, irq, wm8350->irq[irq].data);
+	else {
+		dev_err(wm8350->dev, "irq %d nobody cared. now masked.\n",
+			irq);
+		wm8350_mask_irq(wm8350, irq);
+	}
+
+	mutex_unlock(&wm8350->irq_mutex);
+}
+
+/*
+ * wm8350_irq_worker actually handles the interrupts.  Since all
+ * interrupts are clear on read the IRQ line will be reasserted and
+ * the physical IRQ will be handled again if another interrupt is
+ * asserted while we run - in the normal course of events this is a
+ * rare occurrence so we save I2C/SPI reads.
+ */
+static void wm8350_irq_worker(struct work_struct *work)
+{
+	struct wm8350 *wm8350 = container_of(work, struct wm8350, irq_work);
+	u16 level_one, status1, status2, comp;
+
+	/* TODO: Use block reads to improve performance? */
+	level_one = wm8350_reg_read(wm8350, WM8350_SYSTEM_INTERRUPTS)
+		& ~wm8350_reg_read(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK);
+	status1 = wm8350_reg_read(wm8350, WM8350_INT_STATUS_1)
+		& ~wm8350_reg_read(wm8350, WM8350_INT_STATUS_1_MASK);
+	status2 = wm8350_reg_read(wm8350, WM8350_INT_STATUS_2)
+		& ~wm8350_reg_read(wm8350, WM8350_INT_STATUS_2_MASK);
+	comp = wm8350_reg_read(wm8350, WM8350_COMPARATOR_INT_STATUS)
+		& ~wm8350_reg_read(wm8350, WM8350_COMPARATOR_INT_STATUS_MASK);
+
+	/* over current */
+	if (level_one & WM8350_OC_INT) {
+		u16 oc;
+
+		oc = wm8350_reg_read(wm8350, WM8350_OVER_CURRENT_INT_STATUS);
+		oc &= ~wm8350_reg_read(wm8350,
+				       WM8350_OVER_CURRENT_INT_STATUS_MASK);
+
+		if (oc & WM8350_OC_LS_EINT)	/* limit switch */
+			wm8350_irq_call_handler(wm8350, WM8350_IRQ_OC_LS);
+	}
+
+	/* under voltage */
+	if (level_one & WM8350_UV_INT) {
+		u16 uv;
+
+		uv = wm8350_reg_read(wm8350, WM8350_UNDER_VOLTAGE_INT_STATUS);
+		uv &= ~wm8350_reg_read(wm8350,
+				       WM8350_UNDER_VOLTAGE_INT_STATUS_MASK);
+
+		if (uv & WM8350_UV_DC1_EINT)
+			wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_DC1);
+		if (uv & WM8350_UV_DC2_EINT)
+			wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_DC2);
+		if (uv & WM8350_UV_DC3_EINT)
+			wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_DC3);
+		if (uv & WM8350_UV_DC4_EINT)
+			wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_DC4);
+		if (uv & WM8350_UV_DC5_EINT)
+			wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_DC5);
+		if (uv & WM8350_UV_DC6_EINT)
+			wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_DC6);
+		if (uv & WM8350_UV_LDO1_EINT)
+			wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_LDO1);
+		if (uv & WM8350_UV_LDO2_EINT)
+			wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_LDO2);
+		if (uv & WM8350_UV_LDO3_EINT)
+			wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_LDO3);
+		if (uv & WM8350_UV_LDO4_EINT)
+			wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_LDO4);
+	}
+
+	/* charger, RTC */
+	if (status1) {
+		if (status1 & WM8350_CHG_BAT_HOT_EINT)
+			wm8350_irq_call_handler(wm8350,
+						WM8350_IRQ_CHG_BAT_HOT);
+		if (status1 & WM8350_CHG_BAT_COLD_EINT)
+			wm8350_irq_call_handler(wm8350,
+						WM8350_IRQ_CHG_BAT_COLD);
+		if (status1 & WM8350_CHG_BAT_FAIL_EINT)
+			wm8350_irq_call_handler(wm8350,
+						WM8350_IRQ_CHG_BAT_FAIL);
+		if (status1 & WM8350_CHG_TO_EINT)
+			wm8350_irq_call_handler(wm8350, WM8350_IRQ_CHG_TO);
+		if (status1 & WM8350_CHG_END_EINT)
+			wm8350_irq_call_handler(wm8350, WM8350_IRQ_CHG_END);
+		if (status1 & WM8350_CHG_START_EINT)
+			wm8350_irq_call_handler(wm8350, WM8350_IRQ_CHG_START);
+		if (status1 & WM8350_CHG_FAST_RDY_EINT)
+			wm8350_irq_call_handler(wm8350,
+						WM8350_IRQ_CHG_FAST_RDY);
+		if (status1 & WM8350_CHG_VBATT_LT_3P9_EINT)
+			wm8350_irq_call_handler(wm8350,
+						WM8350_IRQ_CHG_VBATT_LT_3P9);
+		if (status1 & WM8350_CHG_VBATT_LT_3P1_EINT)
+			wm8350_irq_call_handler(wm8350,
+						WM8350_IRQ_CHG_VBATT_LT_3P1);
+		if (status1 & WM8350_CHG_VBATT_LT_2P85_EINT)
+			wm8350_irq_call_handler(wm8350,
+						WM8350_IRQ_CHG_VBATT_LT_2P85);
+		if (status1 & WM8350_RTC_ALM_EINT)
+			wm8350_irq_call_handler(wm8350, WM8350_IRQ_RTC_ALM);
+		if (status1 & WM8350_RTC_SEC_EINT)
+			wm8350_irq_call_handler(wm8350, WM8350_IRQ_RTC_SEC);
+		if (status1 & WM8350_RTC_PER_EINT)
+			wm8350_irq_call_handler(wm8350, WM8350_IRQ_RTC_PER);
+	}
+
+	/* current sink, system, aux adc */
+	if (status2) {
+		if (status2 & WM8350_CS1_EINT)
+			wm8350_irq_call_handler(wm8350, WM8350_IRQ_CS1);
+		if (status2 & WM8350_CS2_EINT)
+			wm8350_irq_call_handler(wm8350, WM8350_IRQ_CS2);
+
+		if (status2 & WM8350_SYS_HYST_COMP_FAIL_EINT)
+			wm8350_irq_call_handler(wm8350,
+						WM8350_IRQ_SYS_HYST_COMP_FAIL);
+		if (status2 & WM8350_SYS_CHIP_GT115_EINT)
+			wm8350_irq_call_handler(wm8350,
+						WM8350_IRQ_SYS_CHIP_GT115);
+		if (status2 & WM8350_SYS_CHIP_GT140_EINT)
+			wm8350_irq_call_handler(wm8350,
+						WM8350_IRQ_SYS_CHIP_GT140);
+		if (status2 & WM8350_SYS_WDOG_TO_EINT)
+			wm8350_irq_call_handler(wm8350,
+						WM8350_IRQ_SYS_WDOG_TO);
+
+		if (status2 & WM8350_AUXADC_DATARDY_EINT)
+			wm8350_irq_call_handler(wm8350,
+						WM8350_IRQ_AUXADC_DATARDY);
+		if (status2 & WM8350_AUXADC_DCOMP4_EINT)
+			wm8350_irq_call_handler(wm8350,
+						WM8350_IRQ_AUXADC_DCOMP4);
+		if (status2 & WM8350_AUXADC_DCOMP3_EINT)
+			wm8350_irq_call_handler(wm8350,
+						WM8350_IRQ_AUXADC_DCOMP3);
+		if (status2 & WM8350_AUXADC_DCOMP2_EINT)
+			wm8350_irq_call_handler(wm8350,
+						WM8350_IRQ_AUXADC_DCOMP2);
+		if (status2 & WM8350_AUXADC_DCOMP1_EINT)
+			wm8350_irq_call_handler(wm8350,
+						WM8350_IRQ_AUXADC_DCOMP1);
+
+		if (status2 & WM8350_USB_LIMIT_EINT)
+			wm8350_irq_call_handler(wm8350, WM8350_IRQ_USB_LIMIT);
+	}
+
+	/* wake, codec, ext */
+	if (comp) {
+		if (comp & WM8350_WKUP_OFF_STATE_EINT)
+			wm8350_irq_call_handler(wm8350,
+						WM8350_IRQ_WKUP_OFF_STATE);
+		if (comp & WM8350_WKUP_HIB_STATE_EINT)
+			wm8350_irq_call_handler(wm8350,
+						WM8350_IRQ_WKUP_HIB_STATE);
+		if (comp & WM8350_WKUP_CONV_FAULT_EINT)
+			wm8350_irq_call_handler(wm8350,
+						WM8350_IRQ_WKUP_CONV_FAULT);
+		if (comp & WM8350_WKUP_WDOG_RST_EINT)
+			wm8350_irq_call_handler(wm8350,
+						WM8350_IRQ_WKUP_WDOG_RST);
+		if (comp & WM8350_WKUP_GP_PWR_ON_EINT)
+			wm8350_irq_call_handler(wm8350,
+						WM8350_IRQ_WKUP_GP_PWR_ON);
+		if (comp & WM8350_WKUP_ONKEY_EINT)
+			wm8350_irq_call_handler(wm8350, WM8350_IRQ_WKUP_ONKEY);
+		if (comp & WM8350_WKUP_GP_WAKEUP_EINT)
+			wm8350_irq_call_handler(wm8350,
+						WM8350_IRQ_WKUP_GP_WAKEUP);
+
+		if (comp & WM8350_CODEC_JCK_DET_L_EINT)
+			wm8350_irq_call_handler(wm8350,
+						WM8350_IRQ_CODEC_JCK_DET_L);
+		if (comp & WM8350_CODEC_JCK_DET_R_EINT)
+			wm8350_irq_call_handler(wm8350,
+						WM8350_IRQ_CODEC_JCK_DET_R);
+		if (comp & WM8350_CODEC_MICSCD_EINT)
+			wm8350_irq_call_handler(wm8350,
+						WM8350_IRQ_CODEC_MICSCD);
+		if (comp & WM8350_CODEC_MICD_EINT)
+			wm8350_irq_call_handler(wm8350, WM8350_IRQ_CODEC_MICD);
+
+		if (comp & WM8350_EXT_USB_FB_EINT)
+			wm8350_irq_call_handler(wm8350, WM8350_IRQ_EXT_USB_FB);
+		if (comp & WM8350_EXT_WALL_FB_EINT)
+			wm8350_irq_call_handler(wm8350,
+						WM8350_IRQ_EXT_WALL_FB);
+		if (comp & WM8350_EXT_BAT_FB_EINT)
+			wm8350_irq_call_handler(wm8350, WM8350_IRQ_EXT_BAT_FB);
+	}
+
+	if (level_one & WM8350_GP_INT) {
+		int i;
+		u16 gpio;
+
+		gpio = wm8350_reg_read(wm8350, WM8350_GPIO_INT_STATUS);
+		gpio &= ~wm8350_reg_read(wm8350,
+					 WM8350_GPIO_INT_STATUS_MASK);
+
+		for (i = 0; i < 12; i++) {
+			if (gpio & (1 << i))
+				wm8350_irq_call_handler(wm8350,
+							WM8350_IRQ_GPIO(i));
+		}
+	}
+
+	enable_irq(wm8350->chip_irq);
+}
+
+static irqreturn_t wm8350_irq(int irq, void *data)
+{
+	struct wm8350 *wm8350 = data;
+
+	disable_irq_nosync(irq);
+	schedule_work(&wm8350->irq_work);
+
+	return IRQ_HANDLED;
+}
+
+int wm8350_register_irq(struct wm8350 *wm8350, int irq,
+			void (*handler) (struct wm8350 *, int, void *),
+			void *data)
+{
+	if (irq < 0 || irq > WM8350_NUM_IRQ || !handler)
+		return -EINVAL;
+
+	if (wm8350->irq[irq].handler)
+		return -EBUSY;
+
+	mutex_lock(&wm8350->irq_mutex);
+	wm8350->irq[irq].handler = handler;
+	wm8350->irq[irq].data = data;
+	mutex_unlock(&wm8350->irq_mutex);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(wm8350_register_irq);
+
+int wm8350_free_irq(struct wm8350 *wm8350, int irq)
+{
+	if (irq < 0 || irq > WM8350_NUM_IRQ)
+		return -EINVAL;
+
+	mutex_lock(&wm8350->irq_mutex);
+	wm8350->irq[irq].handler = NULL;
+	mutex_unlock(&wm8350->irq_mutex);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(wm8350_free_irq);
+
+int wm8350_mask_irq(struct wm8350 *wm8350, int irq)
+{
+	switch (irq) {
+	case WM8350_IRQ_CHG_BAT_HOT:
+		return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+				       WM8350_IM_CHG_BAT_HOT_EINT);
+	case WM8350_IRQ_CHG_BAT_COLD:
+		return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+				       WM8350_IM_CHG_BAT_COLD_EINT);
+	case WM8350_IRQ_CHG_BAT_FAIL:
+		return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+				       WM8350_IM_CHG_BAT_FAIL_EINT);
+	case WM8350_IRQ_CHG_TO:
+		return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+				       WM8350_IM_CHG_TO_EINT);
+	case WM8350_IRQ_CHG_END:
+		return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+				       WM8350_IM_CHG_END_EINT);
+	case WM8350_IRQ_CHG_START:
+		return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+				       WM8350_IM_CHG_START_EINT);
+	case WM8350_IRQ_CHG_FAST_RDY:
+		return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+				       WM8350_IM_CHG_FAST_RDY_EINT);
+	case WM8350_IRQ_RTC_PER:
+		return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+				       WM8350_IM_RTC_PER_EINT);
+	case WM8350_IRQ_RTC_SEC:
+		return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+				       WM8350_IM_RTC_SEC_EINT);
+	case WM8350_IRQ_RTC_ALM:
+		return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+				       WM8350_IM_RTC_ALM_EINT);
+	case WM8350_IRQ_CHG_VBATT_LT_3P9:
+		return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+				       WM8350_IM_CHG_VBATT_LT_3P9_EINT);
+	case WM8350_IRQ_CHG_VBATT_LT_3P1:
+		return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+				       WM8350_IM_CHG_VBATT_LT_3P1_EINT);
+	case WM8350_IRQ_CHG_VBATT_LT_2P85:
+		return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+				       WM8350_IM_CHG_VBATT_LT_2P85_EINT);
+	case WM8350_IRQ_CS1:
+		return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK,
+				       WM8350_IM_CS1_EINT);
+	case WM8350_IRQ_CS2:
+		return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK,
+				       WM8350_IM_CS2_EINT);
+	case WM8350_IRQ_USB_LIMIT:
+		return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK,
+				       WM8350_IM_USB_LIMIT_EINT);
+	case WM8350_IRQ_AUXADC_DATARDY:
+		return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK,
+				       WM8350_IM_AUXADC_DATARDY_EINT);
+	case WM8350_IRQ_AUXADC_DCOMP4:
+		return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK,
+				       WM8350_IM_AUXADC_DCOMP4_EINT);
+	case WM8350_IRQ_AUXADC_DCOMP3:
+		return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK,
+				       WM8350_IM_AUXADC_DCOMP3_EINT);
+	case WM8350_IRQ_AUXADC_DCOMP2:
+		return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK,
+				       WM8350_IM_AUXADC_DCOMP2_EINT);
+	case WM8350_IRQ_AUXADC_DCOMP1:
+		return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK,
+				       WM8350_IM_AUXADC_DCOMP1_EINT);
+	case WM8350_IRQ_SYS_HYST_COMP_FAIL:
+		return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK,
+				       WM8350_IM_SYS_HYST_COMP_FAIL_EINT);
+	case WM8350_IRQ_SYS_CHIP_GT115:
+		return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK,
+				       WM8350_IM_SYS_CHIP_GT115_EINT);
+	case WM8350_IRQ_SYS_CHIP_GT140:
+		return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK,
+				       WM8350_IM_SYS_CHIP_GT140_EINT);
+	case WM8350_IRQ_SYS_WDOG_TO:
+		return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK,
+				       WM8350_IM_SYS_WDOG_TO_EINT);
+	case WM8350_IRQ_UV_LDO4:
+		return wm8350_set_bits(wm8350,
+				       WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
+				       WM8350_IM_UV_LDO4_EINT);
+	case WM8350_IRQ_UV_LDO3:
+		return wm8350_set_bits(wm8350,
+				       WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
+				       WM8350_IM_UV_LDO3_EINT);
+	case WM8350_IRQ_UV_LDO2:
+		return wm8350_set_bits(wm8350,
+				       WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
+				       WM8350_IM_UV_LDO2_EINT);
+	case WM8350_IRQ_UV_LDO1:
+		return wm8350_set_bits(wm8350,
+				       WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
+				       WM8350_IM_UV_LDO1_EINT);
+	case WM8350_IRQ_UV_DC6:
+		return wm8350_set_bits(wm8350,
+				       WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
+				       WM8350_IM_UV_DC6_EINT);
+	case WM8350_IRQ_UV_DC5:
+		return wm8350_set_bits(wm8350,
+				       WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
+				       WM8350_IM_UV_DC5_EINT);
+	case WM8350_IRQ_UV_DC4:
+		return wm8350_set_bits(wm8350,
+				       WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
+				       WM8350_IM_UV_DC4_EINT);
+	case WM8350_IRQ_UV_DC3:
+		return wm8350_set_bits(wm8350,
+				       WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
+				       WM8350_IM_UV_DC3_EINT);
+	case WM8350_IRQ_UV_DC2:
+		return wm8350_set_bits(wm8350,
+				       WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
+				       WM8350_IM_UV_DC2_EINT);
+	case WM8350_IRQ_UV_DC1:
+		return wm8350_set_bits(wm8350,
+				       WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
+				       WM8350_IM_UV_DC1_EINT);
+	case WM8350_IRQ_OC_LS:
+		return wm8350_set_bits(wm8350,
+				       WM8350_OVER_CURRENT_INT_STATUS_MASK,
+				       WM8350_IM_OC_LS_EINT);
+	case WM8350_IRQ_EXT_USB_FB:
+		return wm8350_set_bits(wm8350,
+				       WM8350_COMPARATOR_INT_STATUS_MASK,
+				       WM8350_IM_EXT_USB_FB_EINT);
+	case WM8350_IRQ_EXT_WALL_FB:
+		return wm8350_set_bits(wm8350,
+				       WM8350_COMPARATOR_INT_STATUS_MASK,
+				       WM8350_IM_EXT_WALL_FB_EINT);
+	case WM8350_IRQ_EXT_BAT_FB:
+		return wm8350_set_bits(wm8350,
+				       WM8350_COMPARATOR_INT_STATUS_MASK,
+				       WM8350_IM_EXT_BAT_FB_EINT);
+	case WM8350_IRQ_CODEC_JCK_DET_L:
+		return wm8350_set_bits(wm8350,
+				       WM8350_COMPARATOR_INT_STATUS_MASK,
+				       WM8350_IM_CODEC_JCK_DET_L_EINT);
+	case WM8350_IRQ_CODEC_JCK_DET_R:
+		return wm8350_set_bits(wm8350,
+				       WM8350_COMPARATOR_INT_STATUS_MASK,
+				       WM8350_IM_CODEC_JCK_DET_R_EINT);
+	case WM8350_IRQ_CODEC_MICSCD:
+		return wm8350_set_bits(wm8350,
+				       WM8350_COMPARATOR_INT_STATUS_MASK,
+				       WM8350_IM_CODEC_MICSCD_EINT);
+	case WM8350_IRQ_CODEC_MICD:
+		return wm8350_set_bits(wm8350,
+				       WM8350_COMPARATOR_INT_STATUS_MASK,
+				       WM8350_IM_CODEC_MICD_EINT);
+	case WM8350_IRQ_WKUP_OFF_STATE:
+		return wm8350_set_bits(wm8350,
+				       WM8350_COMPARATOR_INT_STATUS_MASK,
+				       WM8350_IM_WKUP_OFF_STATE_EINT);
+	case WM8350_IRQ_WKUP_HIB_STATE:
+		return wm8350_set_bits(wm8350,
+				       WM8350_COMPARATOR_INT_STATUS_MASK,
+				       WM8350_IM_WKUP_HIB_STATE_EINT);
+	case WM8350_IRQ_WKUP_CONV_FAULT:
+		return wm8350_set_bits(wm8350,
+				       WM8350_COMPARATOR_INT_STATUS_MASK,
+				       WM8350_IM_WKUP_CONV_FAULT_EINT);
+	case WM8350_IRQ_WKUP_WDOG_RST:
+		return wm8350_set_bits(wm8350,
+				       WM8350_COMPARATOR_INT_STATUS_MASK,
+				       WM8350_IM_WKUP_OFF_STATE_EINT);
+	case WM8350_IRQ_WKUP_GP_PWR_ON:
+		return wm8350_set_bits(wm8350,
+				       WM8350_COMPARATOR_INT_STATUS_MASK,
+				       WM8350_IM_WKUP_GP_PWR_ON_EINT);
+	case WM8350_IRQ_WKUP_ONKEY:
+		return wm8350_set_bits(wm8350,
+				       WM8350_COMPARATOR_INT_STATUS_MASK,
+				       WM8350_IM_WKUP_ONKEY_EINT);
+	case WM8350_IRQ_WKUP_GP_WAKEUP:
+		return wm8350_set_bits(wm8350,
+				       WM8350_COMPARATOR_INT_STATUS_MASK,
+				       WM8350_IM_WKUP_GP_WAKEUP_EINT);
+	case WM8350_IRQ_GPIO(0):
+		return wm8350_set_bits(wm8350,
+				       WM8350_GPIO_INT_STATUS_MASK,
+				       WM8350_IM_GP0_EINT);
+	case WM8350_IRQ_GPIO(1):
+		return wm8350_set_bits(wm8350,
+				       WM8350_GPIO_INT_STATUS_MASK,
+				       WM8350_IM_GP1_EINT);
+	case WM8350_IRQ_GPIO(2):
+		return wm8350_set_bits(wm8350,
+				       WM8350_GPIO_INT_STATUS_MASK,
+				       WM8350_IM_GP2_EINT);
+	case WM8350_IRQ_GPIO(3):
+		return wm8350_set_bits(wm8350,
+				       WM8350_GPIO_INT_STATUS_MASK,
+				       WM8350_IM_GP3_EINT);
+	case WM8350_IRQ_GPIO(4):
+		return wm8350_set_bits(wm8350,
+				       WM8350_GPIO_INT_STATUS_MASK,
+				       WM8350_IM_GP4_EINT);
+	case WM8350_IRQ_GPIO(5):
+		return wm8350_set_bits(wm8350,
+				       WM8350_GPIO_INT_STATUS_MASK,
+				       WM8350_IM_GP5_EINT);
+	case WM8350_IRQ_GPIO(6):
+		return wm8350_set_bits(wm8350,
+				       WM8350_GPIO_INT_STATUS_MASK,
+				       WM8350_IM_GP6_EINT);
+	case WM8350_IRQ_GPIO(7):
+		return wm8350_set_bits(wm8350,
+				       WM8350_GPIO_INT_STATUS_MASK,
+				       WM8350_IM_GP7_EINT);
+	case WM8350_IRQ_GPIO(8):
+		return wm8350_set_bits(wm8350,
+				       WM8350_GPIO_INT_STATUS_MASK,
+				       WM8350_IM_GP8_EINT);
+	case WM8350_IRQ_GPIO(9):
+		return wm8350_set_bits(wm8350,
+				       WM8350_GPIO_INT_STATUS_MASK,
+				       WM8350_IM_GP9_EINT);
+	case WM8350_IRQ_GPIO(10):
+		return wm8350_set_bits(wm8350,
+				       WM8350_GPIO_INT_STATUS_MASK,
+				       WM8350_IM_GP10_EINT);
+	case WM8350_IRQ_GPIO(11):
+		return wm8350_set_bits(wm8350,
+				       WM8350_GPIO_INT_STATUS_MASK,
+				       WM8350_IM_GP11_EINT);
+	case WM8350_IRQ_GPIO(12):
+		return wm8350_set_bits(wm8350,
+				       WM8350_GPIO_INT_STATUS_MASK,
+				       WM8350_IM_GP12_EINT);
+	default:
+		dev_warn(wm8350->dev, "Attempting to mask unknown IRQ %d\n",
+			 irq);
+		return -EINVAL;
+	}
+	return 0;
+}
+EXPORT_SYMBOL_GPL(wm8350_mask_irq);
+
+int wm8350_unmask_irq(struct wm8350 *wm8350, int irq)
+{
+	switch (irq) {
+	case WM8350_IRQ_CHG_BAT_HOT:
+		return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+					 WM8350_IM_CHG_BAT_HOT_EINT);
+	case WM8350_IRQ_CHG_BAT_COLD:
+		return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+					 WM8350_IM_CHG_BAT_COLD_EINT);
+	case WM8350_IRQ_CHG_BAT_FAIL:
+		return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+					 WM8350_IM_CHG_BAT_FAIL_EINT);
+	case WM8350_IRQ_CHG_TO:
+		return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+					 WM8350_IM_CHG_TO_EINT);
+	case WM8350_IRQ_CHG_END:
+		return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+					 WM8350_IM_CHG_END_EINT);
+	case WM8350_IRQ_CHG_START:
+		return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+					 WM8350_IM_CHG_START_EINT);
+	case WM8350_IRQ_CHG_FAST_RDY:
+		return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+					 WM8350_IM_CHG_FAST_RDY_EINT);
+	case WM8350_IRQ_RTC_PER:
+		return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+					 WM8350_IM_RTC_PER_EINT);
+	case WM8350_IRQ_RTC_SEC:
+		return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+					 WM8350_IM_RTC_SEC_EINT);
+	case WM8350_IRQ_RTC_ALM:
+		return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+					 WM8350_IM_RTC_ALM_EINT);
+	case WM8350_IRQ_CHG_VBATT_LT_3P9:
+		return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+					 WM8350_IM_CHG_VBATT_LT_3P9_EINT);
+	case WM8350_IRQ_CHG_VBATT_LT_3P1:
+		return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+					 WM8350_IM_CHG_VBATT_LT_3P1_EINT);
+	case WM8350_IRQ_CHG_VBATT_LT_2P85:
+		return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+					 WM8350_IM_CHG_VBATT_LT_2P85_EINT);
+	case WM8350_IRQ_CS1:
+		return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK,
+					 WM8350_IM_CS1_EINT);
+	case WM8350_IRQ_CS2:
+		return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK,
+					 WM8350_IM_CS2_EINT);
+	case WM8350_IRQ_USB_LIMIT:
+		return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK,
+					 WM8350_IM_USB_LIMIT_EINT);
+	case WM8350_IRQ_AUXADC_DATARDY:
+		return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK,
+					 WM8350_IM_AUXADC_DATARDY_EINT);
+	case WM8350_IRQ_AUXADC_DCOMP4:
+		return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK,
+					 WM8350_IM_AUXADC_DCOMP4_EINT);
+	case WM8350_IRQ_AUXADC_DCOMP3:
+		return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK,
+					 WM8350_IM_AUXADC_DCOMP3_EINT);
+	case WM8350_IRQ_AUXADC_DCOMP2:
+		return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK,
+					 WM8350_IM_AUXADC_DCOMP2_EINT);
+	case WM8350_IRQ_AUXADC_DCOMP1:
+		return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK,
+					 WM8350_IM_AUXADC_DCOMP1_EINT);
+	case WM8350_IRQ_SYS_HYST_COMP_FAIL:
+		return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK,
+					 WM8350_IM_SYS_HYST_COMP_FAIL_EINT);
+	case WM8350_IRQ_SYS_CHIP_GT115:
+		return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK,
+					 WM8350_IM_SYS_CHIP_GT115_EINT);
+	case WM8350_IRQ_SYS_CHIP_GT140:
+		return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK,
+					 WM8350_IM_SYS_CHIP_GT140_EINT);
+	case WM8350_IRQ_SYS_WDOG_TO:
+		return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK,
+					 WM8350_IM_SYS_WDOG_TO_EINT);
+	case WM8350_IRQ_UV_LDO4:
+		return wm8350_clear_bits(wm8350,
+					 WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
+					 WM8350_IM_UV_LDO4_EINT);
+	case WM8350_IRQ_UV_LDO3:
+		return wm8350_clear_bits(wm8350,
+					 WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
+					 WM8350_IM_UV_LDO3_EINT);
+	case WM8350_IRQ_UV_LDO2:
+		return wm8350_clear_bits(wm8350,
+					 WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
+					 WM8350_IM_UV_LDO2_EINT);
+	case WM8350_IRQ_UV_LDO1:
+		return wm8350_clear_bits(wm8350,
+					 WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
+					 WM8350_IM_UV_LDO1_EINT);
+	case WM8350_IRQ_UV_DC6:
+		return wm8350_clear_bits(wm8350,
+					 WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
+					 WM8350_IM_UV_DC6_EINT);
+	case WM8350_IRQ_UV_DC5:
+		return wm8350_clear_bits(wm8350,
+					 WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
+					 WM8350_IM_UV_DC5_EINT);
+	case WM8350_IRQ_UV_DC4:
+		return wm8350_clear_bits(wm8350,
+					 WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
+					 WM8350_IM_UV_DC4_EINT);
+	case WM8350_IRQ_UV_DC3:
+		return wm8350_clear_bits(wm8350,
+					 WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
+					 WM8350_IM_UV_DC3_EINT);
+	case WM8350_IRQ_UV_DC2:
+		return wm8350_clear_bits(wm8350,
+					 WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
+					 WM8350_IM_UV_DC2_EINT);
+	case WM8350_IRQ_UV_DC1:
+		return wm8350_clear_bits(wm8350,
+					 WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
+					 WM8350_IM_UV_DC1_EINT);
+	case WM8350_IRQ_OC_LS:
+		return wm8350_clear_bits(wm8350,
+					 WM8350_OVER_CURRENT_INT_STATUS_MASK,
+					 WM8350_IM_OC_LS_EINT);
+	case WM8350_IRQ_EXT_USB_FB:
+		return wm8350_clear_bits(wm8350,
+					 WM8350_COMPARATOR_INT_STATUS_MASK,
+					 WM8350_IM_EXT_USB_FB_EINT);
+	case WM8350_IRQ_EXT_WALL_FB:
+		return wm8350_clear_bits(wm8350,
+					 WM8350_COMPARATOR_INT_STATUS_MASK,
+					 WM8350_IM_EXT_WALL_FB_EINT);
+	case WM8350_IRQ_EXT_BAT_FB:
+		return wm8350_clear_bits(wm8350,
+					 WM8350_COMPARATOR_INT_STATUS_MASK,
+					 WM8350_IM_EXT_BAT_FB_EINT);
+	case WM8350_IRQ_CODEC_JCK_DET_L:
+		return wm8350_clear_bits(wm8350,
+					 WM8350_COMPARATOR_INT_STATUS_MASK,
+					 WM8350_IM_CODEC_JCK_DET_L_EINT);
+	case WM8350_IRQ_CODEC_JCK_DET_R:
+		return wm8350_clear_bits(wm8350,
+					 WM8350_COMPARATOR_INT_STATUS_MASK,
+					 WM8350_IM_CODEC_JCK_DET_R_EINT);
+	case WM8350_IRQ_CODEC_MICSCD:
+		return wm8350_clear_bits(wm8350,
+					 WM8350_COMPARATOR_INT_STATUS_MASK,
+					 WM8350_IM_CODEC_MICSCD_EINT);
+	case WM8350_IRQ_CODEC_MICD:
+		return wm8350_clear_bits(wm8350,
+					 WM8350_COMPARATOR_INT_STATUS_MASK,
+					 WM8350_IM_CODEC_MICD_EINT);
+	case WM8350_IRQ_WKUP_OFF_STATE:
+		return wm8350_clear_bits(wm8350,
+					 WM8350_COMPARATOR_INT_STATUS_MASK,
+					 WM8350_IM_WKUP_OFF_STATE_EINT);
+	case WM8350_IRQ_WKUP_HIB_STATE:
+		return wm8350_clear_bits(wm8350,
+					 WM8350_COMPARATOR_INT_STATUS_MASK,
+					 WM8350_IM_WKUP_HIB_STATE_EINT);
+	case WM8350_IRQ_WKUP_CONV_FAULT:
+		return wm8350_clear_bits(wm8350,
+					 WM8350_COMPARATOR_INT_STATUS_MASK,
+					 WM8350_IM_WKUP_CONV_FAULT_EINT);
+	case WM8350_IRQ_WKUP_WDOG_RST:
+		return wm8350_clear_bits(wm8350,
+					 WM8350_COMPARATOR_INT_STATUS_MASK,
+					 WM8350_IM_WKUP_OFF_STATE_EINT);
+	case WM8350_IRQ_WKUP_GP_PWR_ON:
+		return wm8350_clear_bits(wm8350,
+					 WM8350_COMPARATOR_INT_STATUS_MASK,
+					 WM8350_IM_WKUP_GP_PWR_ON_EINT);
+	case WM8350_IRQ_WKUP_ONKEY:
+		return wm8350_clear_bits(wm8350,
+					 WM8350_COMPARATOR_INT_STATUS_MASK,
+					 WM8350_IM_WKUP_ONKEY_EINT);
+	case WM8350_IRQ_WKUP_GP_WAKEUP:
+		return wm8350_clear_bits(wm8350,
+					 WM8350_COMPARATOR_INT_STATUS_MASK,
+					 WM8350_IM_WKUP_GP_WAKEUP_EINT);
+	case WM8350_IRQ_GPIO(0):
+		return wm8350_clear_bits(wm8350,
+					 WM8350_GPIO_INT_STATUS_MASK,
+					 WM8350_IM_GP0_EINT);
+	case WM8350_IRQ_GPIO(1):
+		return wm8350_clear_bits(wm8350,
+					 WM8350_GPIO_INT_STATUS_MASK,
+					 WM8350_IM_GP1_EINT);
+	case WM8350_IRQ_GPIO(2):
+		return wm8350_clear_bits(wm8350,
+					 WM8350_GPIO_INT_STATUS_MASK,
+					 WM8350_IM_GP2_EINT);
+	case WM8350_IRQ_GPIO(3):
+		return wm8350_clear_bits(wm8350,
+					 WM8350_GPIO_INT_STATUS_MASK,
+					 WM8350_IM_GP3_EINT);
+	case WM8350_IRQ_GPIO(4):
+		return wm8350_clear_bits(wm8350,
+					 WM8350_GPIO_INT_STATUS_MASK,
+					 WM8350_IM_GP4_EINT);
+	case WM8350_IRQ_GPIO(5):
+		return wm8350_clear_bits(wm8350,
+					 WM8350_GPIO_INT_STATUS_MASK,
+					 WM8350_IM_GP5_EINT);
+	case WM8350_IRQ_GPIO(6):
+		return wm8350_clear_bits(wm8350,
+					 WM8350_GPIO_INT_STATUS_MASK,
+					 WM8350_IM_GP6_EINT);
+	case WM8350_IRQ_GPIO(7):
+		return wm8350_clear_bits(wm8350,
+					 WM8350_GPIO_INT_STATUS_MASK,
+					 WM8350_IM_GP7_EINT);
+	case WM8350_IRQ_GPIO(8):
+		return wm8350_clear_bits(wm8350,
+					 WM8350_GPIO_INT_STATUS_MASK,
+					 WM8350_IM_GP8_EINT);
+	case WM8350_IRQ_GPIO(9):
+		return wm8350_clear_bits(wm8350,
+					 WM8350_GPIO_INT_STATUS_MASK,
+					 WM8350_IM_GP9_EINT);
+	case WM8350_IRQ_GPIO(10):
+		return wm8350_clear_bits(wm8350,
+					 WM8350_GPIO_INT_STATUS_MASK,
+					 WM8350_IM_GP10_EINT);
+	case WM8350_IRQ_GPIO(11):
+		return wm8350_clear_bits(wm8350,
+					 WM8350_GPIO_INT_STATUS_MASK,
+					 WM8350_IM_GP11_EINT);
+	case WM8350_IRQ_GPIO(12):
+		return wm8350_clear_bits(wm8350,
+					 WM8350_GPIO_INT_STATUS_MASK,
+					 WM8350_IM_GP12_EINT);
+	default:
+		dev_warn(wm8350->dev, "Attempting to unmask unknown IRQ %d\n",
+			 irq);
+		return -EINVAL;
+	}
+	return 0;
+}
+EXPORT_SYMBOL_GPL(wm8350_unmask_irq);
+
+/*
+ * Cache is always host endian.
+ */
+static int wm8350_create_cache(struct wm8350 *wm8350, int mode)
+{
+	int i, ret = 0;
+	u16 value;
+	const u16 *reg_map;
+
+	switch (mode) {
+#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_0
+	case 0:
+		reg_map = wm8350_mode0_defaults;
+		break;
+#endif
+#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_1
+	case 1:
+		reg_map = wm8350_mode1_defaults;
+		break;
+#endif
+#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_2
+	case 2:
+		reg_map = wm8350_mode2_defaults;
+		break;
+#endif
+#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_3
+	case 3:
+		reg_map = wm8350_mode3_defaults;
+		break;
+#endif
+	default:
+		dev_err(wm8350->dev, "Configuration mode %d not supported\n",
+			mode);
+		return -EINVAL;
+	}
+
+	wm8350->reg_cache =
+	    kzalloc(sizeof(u16) * (WM8350_MAX_REGISTER + 1), GFP_KERNEL);
+	if (wm8350->reg_cache == NULL)
+		return -ENOMEM;
+
+	/* Read the initial cache state back from the device - this is
+	 * a PMIC so the device many not be in a virgin state and we
+	 * can't rely on the silicon values.
+	 */
+	for (i = 0; i < WM8350_MAX_REGISTER; i++) {
+		/* audio register range */
+		if (wm8350_reg_io_map[i].readable &&
+		    (i < WM8350_CLOCK_CONTROL_1 || i > WM8350_AIF_TEST)) {
+			ret = wm8350->read_dev(wm8350, i, 2, (char *)&value);
+			if (ret < 0) {
+				dev_err(wm8350->dev,
+				       "failed to read initial cache value\n");
+				goto out;
+			}
+			value = be16_to_cpu(value);
+			value &= wm8350_reg_io_map[i].readable;
+			wm8350->reg_cache[i] = value;
+		} else
+			wm8350->reg_cache[i] = reg_map[i];
+	}
+
+out:
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wm8350_create_cache);
+
+/*
+ * Register a client device.  This is non-fatal since there is no need to
+ * fail the entire device init due to a single platform device failing.
+ */
+static void wm8350_client_dev_register(struct wm8350 *wm8350,
+				       const char *name,
+				       struct platform_device **pdev)
+{
+	int ret;
+
+	*pdev = platform_device_alloc(name, -1);
+	if (pdev == NULL) {
+		dev_err(wm8350->dev, "Failed to allocate %s\n", name);
+		return;
+	}
+
+	(*pdev)->dev.parent = wm8350->dev;
+	platform_set_drvdata(*pdev, wm8350);
+	ret = platform_device_add(*pdev);
+	if (ret != 0) {
+		dev_err(wm8350->dev, "Failed to register %s: %d\n", name, ret);
+		platform_device_put(*pdev);
+		*pdev = NULL;
+	}
+}
+
+int wm8350_device_init(struct wm8350 *wm8350, int irq,
+		       struct wm8350_platform_data *pdata)
+{
+	int ret = -EINVAL;
+	u16 id1, id2, mask, mode;
+
+	/* get WM8350 revision and config mode */
+	wm8350->read_dev(wm8350, WM8350_RESET_ID, sizeof(id1), &id1);
+	wm8350->read_dev(wm8350, WM8350_ID, sizeof(id2), &id2);
+
+	id1 = be16_to_cpu(id1);
+	id2 = be16_to_cpu(id2);
+
+	if (id1 == 0x6143) {
+		switch ((id2 & WM8350_CHIP_REV_MASK) >> 12) {
+		case WM8350_REV_E:
+			dev_info(wm8350->dev, "Found Rev E device\n");
+			wm8350->rev = WM8350_REV_E;
+			break;
+		case WM8350_REV_F:
+			dev_info(wm8350->dev, "Found Rev F device\n");
+			wm8350->rev = WM8350_REV_F;
+			break;
+		case WM8350_REV_G:
+			dev_info(wm8350->dev, "Found Rev G device\n");
+			wm8350->rev = WM8350_REV_G;
+			break;
+		default:
+			/* For safety we refuse to run on unknown hardware */
+			dev_info(wm8350->dev, "Found unknown rev\n");
+			ret = -ENODEV;
+			goto err;
+		}
+	} else {
+		dev_info(wm8350->dev, "Device with ID %x is not a WM8350\n",
+			 id1);
+		ret = -ENODEV;
+		goto err;
+	}
+
+	mode = id2 & WM8350_CONF_STS_MASK >> 10;
+	mask = id2 & WM8350_CUST_ID_MASK;
+	dev_info(wm8350->dev, "Config mode %d, ROM mask %d\n", mode, mask);
+
+	ret = wm8350_create_cache(wm8350, mode);
+	if (ret < 0) {
+		printk(KERN_ERR "wm8350: failed to create register cache\n");
+		return ret;
+	}
+
+	if (pdata->init) {
+		ret = pdata->init(wm8350);
+		if (ret != 0) {
+			dev_err(wm8350->dev, "Platform init() failed: %d\n",
+				ret);
+			goto err;
+		}
+	}
+
+	mutex_init(&wm8350->irq_mutex);
+	INIT_WORK(&wm8350->irq_work, wm8350_irq_worker);
+	if (irq != NO_IRQ) {
+		ret = request_irq(irq, wm8350_irq, 0,
+				  "wm8350", wm8350);
+		if (ret != 0) {
+			dev_err(wm8350->dev, "Failed to request IRQ: %d\n",
+				ret);
+			goto err;
+		}
+	} else {
+		dev_err(wm8350->dev, "No IRQ configured\n");
+		goto err;
+	}
+	wm8350->chip_irq = irq;
+
+	wm8350_reg_write(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK, 0x0);
+
+	wm8350_client_dev_register(wm8350, "wm8350-codec",
+				   &(wm8350->codec.pdev));
+	wm8350_client_dev_register(wm8350, "wm8350-gpio",
+				   &(wm8350->gpio.pdev));
+	wm8350_client_dev_register(wm8350, "wm8350-power",
+				   &(wm8350->power.pdev));
+	wm8350_client_dev_register(wm8350, "wm8350-rtc", &(wm8350->rtc.pdev));
+	wm8350_client_dev_register(wm8350, "wm8350-wdt", &(wm8350->wdt.pdev));
+
+	return 0;
+
+err:
+	kfree(wm8350->reg_cache);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wm8350_device_init);
+
+void wm8350_device_exit(struct wm8350 *wm8350)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(wm8350->pmic.pdev); i++)
+		platform_device_unregister(wm8350->pmic.pdev[i]);
+
+	platform_device_unregister(wm8350->wdt.pdev);
+	platform_device_unregister(wm8350->rtc.pdev);
+	platform_device_unregister(wm8350->power.pdev);
+	platform_device_unregister(wm8350->gpio.pdev);
+	platform_device_unregister(wm8350->codec.pdev);
+
+	free_irq(wm8350->chip_irq, wm8350);
+	flush_work(&wm8350->irq_work);
+	kfree(wm8350->reg_cache);
+}
+EXPORT_SYMBOL_GPL(wm8350_device_exit);
+
+MODULE_DESCRIPTION("WM8350 AudioPlus PMIC core driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/wm8350-gpio.c b/drivers/mfd/wm8350-gpio.c
new file mode 100644
index 0000000..ebf99be
--- /dev/null
+++ b/drivers/mfd/wm8350-gpio.c
@@ -0,0 +1,222 @@
+/*
+ * wm8350-core.c  --  Device access for Wolfson WM8350
+ *
+ * Copyright 2007, 2008 Wolfson Microelectronics PLC.
+ *
+ * Author: Liam Girdwood
+ *
+ *  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/kernel.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+
+#include <linux/mfd/wm8350/core.h>
+#include <linux/mfd/wm8350/gpio.h>
+#include <linux/mfd/wm8350/pmic.h>
+
+static int gpio_set_dir(struct wm8350 *wm8350, int gpio, int dir)
+{
+	int ret;
+
+	wm8350_reg_unlock(wm8350);
+	if (dir == WM8350_GPIO_DIR_OUT)
+		ret = wm8350_clear_bits(wm8350,
+					WM8350_GPIO_CONFIGURATION_I_O,
+					1 << gpio);
+	else
+		ret = wm8350_set_bits(wm8350,
+				      WM8350_GPIO_CONFIGURATION_I_O,
+				      1 << gpio);
+	wm8350_reg_lock(wm8350);
+	return ret;
+}
+
+static int gpio_set_debounce(struct wm8350 *wm8350, int gpio, int db)
+{
+	if (db == WM8350_GPIO_DEBOUNCE_ON)
+		return wm8350_set_bits(wm8350, WM8350_GPIO_DEBOUNCE,
+				       1 << gpio);
+	else
+		return wm8350_clear_bits(wm8350,
+					 WM8350_GPIO_DEBOUNCE, 1 << gpio);
+}
+
+static int gpio_set_func(struct wm8350 *wm8350, int gpio, int func)
+{
+	u16 reg;
+
+	wm8350_reg_unlock(wm8350);
+	switch (gpio) {
+	case 0:
+		reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_1)
+		    & ~WM8350_GP0_FN_MASK;
+		wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_1,
+				 reg | ((func & 0xf) << 0));
+		break;
+	case 1:
+		reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_1)
+		    & ~WM8350_GP1_FN_MASK;
+		wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_1,
+				 reg | ((func & 0xf) << 4));
+		break;
+	case 2:
+		reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_1)
+		    & ~WM8350_GP2_FN_MASK;
+		wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_1,
+				 reg | ((func & 0xf) << 8));
+		break;
+	case 3:
+		reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_1)
+		    & ~WM8350_GP3_FN_MASK;
+		wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_1,
+				 reg | ((func & 0xf) << 12));
+		break;
+	case 4:
+		reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_2)
+		    & ~WM8350_GP4_FN_MASK;
+		wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_2,
+				 reg | ((func & 0xf) << 0));
+		break;
+	case 5:
+		reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_2)
+		    & ~WM8350_GP5_FN_MASK;
+		wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_2,
+				 reg | ((func & 0xf) << 4));
+		break;
+	case 6:
+		reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_2)
+		    & ~WM8350_GP6_FN_MASK;
+		wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_2,
+				 reg | ((func & 0xf) << 8));
+		break;
+	case 7:
+		reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_2)
+		    & ~WM8350_GP7_FN_MASK;
+		wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_2,
+				 reg | ((func & 0xf) << 12));
+		break;
+	case 8:
+		reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_3)
+		    & ~WM8350_GP8_FN_MASK;
+		wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_3,
+				 reg | ((func & 0xf) << 0));
+		break;
+	case 9:
+		reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_3)
+		    & ~WM8350_GP9_FN_MASK;
+		wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_3,
+				 reg | ((func & 0xf) << 4));
+		break;
+	case 10:
+		reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_3)
+		    & ~WM8350_GP10_FN_MASK;
+		wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_3,
+				 reg | ((func & 0xf) << 8));
+		break;
+	case 11:
+		reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_3)
+		    & ~WM8350_GP11_FN_MASK;
+		wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_3,
+				 reg | ((func & 0xf) << 12));
+		break;
+	case 12:
+		reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_4)
+		    & ~WM8350_GP12_FN_MASK;
+		wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_4,
+				 reg | ((func & 0xf) << 0));
+		break;
+	default:
+		wm8350_reg_lock(wm8350);
+		return -EINVAL;
+	}
+
+	wm8350_reg_lock(wm8350);
+	return 0;
+}
+
+static int gpio_set_pull_up(struct wm8350 *wm8350, int gpio, int up)
+{
+	if (up)
+		return wm8350_set_bits(wm8350,
+				       WM8350_GPIO_PIN_PULL_UP_CONTROL,
+				       1 << gpio);
+	else
+		return wm8350_clear_bits(wm8350,
+					 WM8350_GPIO_PIN_PULL_UP_CONTROL,
+					 1 << gpio);
+}
+
+static int gpio_set_pull_down(struct wm8350 *wm8350, int gpio, int down)
+{
+	if (down)
+		return wm8350_set_bits(wm8350,
+				       WM8350_GPIO_PULL_DOWN_CONTROL,
+				       1 << gpio);
+	else
+		return wm8350_clear_bits(wm8350,
+					 WM8350_GPIO_PULL_DOWN_CONTROL,
+					 1 << gpio);
+}
+
+static int gpio_set_polarity(struct wm8350 *wm8350, int gpio, int pol)
+{
+	if (pol == WM8350_GPIO_ACTIVE_HIGH)
+		return wm8350_set_bits(wm8350,
+				       WM8350_GPIO_PIN_POLARITY_TYPE,
+				       1 << gpio);
+	else
+		return wm8350_clear_bits(wm8350,
+					 WM8350_GPIO_PIN_POLARITY_TYPE,
+					 1 << gpio);
+}
+
+static int gpio_set_invert(struct wm8350 *wm8350, int gpio, int invert)
+{
+	if (invert == WM8350_GPIO_INVERT_ON)
+		return wm8350_set_bits(wm8350, WM8350_GPIO_INT_MODE, 1 << gpio);
+	else
+		return wm8350_clear_bits(wm8350,
+					 WM8350_GPIO_INT_MODE, 1 << gpio);
+}
+
+int wm8350_gpio_config(struct wm8350 *wm8350, int gpio, int dir, int func,
+		       int pol, int pull, int invert, int debounce)
+{
+	/* make sure we never pull up and down at the same time */
+	if (pull == WM8350_GPIO_PULL_NONE) {
+		if (gpio_set_pull_up(wm8350, gpio, 0))
+			goto err;
+		if (gpio_set_pull_down(wm8350, gpio, 0))
+			goto err;
+	} else if (pull == WM8350_GPIO_PULL_UP) {
+		if (gpio_set_pull_down(wm8350, gpio, 0))
+			goto err;
+		if (gpio_set_pull_up(wm8350, gpio, 1))
+			goto err;
+	} else if (pull == WM8350_GPIO_PULL_DOWN) {
+		if (gpio_set_pull_up(wm8350, gpio, 0))
+			goto err;
+		if (gpio_set_pull_down(wm8350, gpio, 1))
+			goto err;
+	}
+
+	if (gpio_set_invert(wm8350, gpio, invert))
+		goto err;
+	if (gpio_set_polarity(wm8350, gpio, pol))
+		goto err;
+	if (gpio_set_debounce(wm8350, gpio, debounce))
+		goto err;
+	if (gpio_set_dir(wm8350, gpio, dir))
+		goto err;
+	return gpio_set_func(wm8350, gpio, func);
+
+err:
+	return -EIO;
+}
+EXPORT_SYMBOL_GPL(wm8350_gpio_config);
diff --git a/drivers/mfd/wm8350-i2c.c b/drivers/mfd/wm8350-i2c.c
new file mode 100644
index 0000000..8dfe21b
--- /dev/null
+++ b/drivers/mfd/wm8350-i2c.c
@@ -0,0 +1,120 @@
+/*
+ * wm8350-i2c.c  --  Generic I2C driver for Wolfson WM8350 PMIC
+ *
+ * This driver defines and configures the WM8350 for the Freescale i.MX32ADS.
+ *
+ * Copyright 2007, 2008 Wolfson Microelectronics PLC.
+ *
+ * Author: Liam Girdwood
+ *         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 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/init.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/wm8350/core.h>
+
+static int wm8350_i2c_read_device(struct wm8350 *wm8350, char reg,
+				  int bytes, void *dest)
+{
+	int ret;
+
+	ret = i2c_master_send(wm8350->i2c_client, &reg, 1);
+	if (ret < 0)
+		return ret;
+	return i2c_master_recv(wm8350->i2c_client, dest, bytes);
+}
+
+static int wm8350_i2c_write_device(struct wm8350 *wm8350, char reg,
+				   int bytes, void *src)
+{
+	/* we add 1 byte for device register */
+	u8 msg[(WM8350_MAX_REGISTER << 1) + 1];
+
+	if (bytes > ((WM8350_MAX_REGISTER << 1) + 1))
+		return -EINVAL;
+
+	msg[0] = reg;
+	memcpy(&msg[1], src, bytes);
+	return i2c_master_send(wm8350->i2c_client, msg, bytes + 1);
+}
+
+static int wm8350_i2c_probe(struct i2c_client *i2c,
+			    const struct i2c_device_id *id)
+{
+	struct wm8350 *wm8350;
+	int ret = 0;
+
+	wm8350 = kzalloc(sizeof(struct wm8350), GFP_KERNEL);
+	if (wm8350 == NULL) {
+		kfree(i2c);
+		return -ENOMEM;
+	}
+
+	i2c_set_clientdata(i2c, wm8350);
+	wm8350->dev = &i2c->dev;
+	wm8350->i2c_client = i2c;
+	wm8350->read_dev = wm8350_i2c_read_device;
+	wm8350->write_dev = wm8350_i2c_write_device;
+
+	ret = wm8350_device_init(wm8350, i2c->irq, i2c->dev.platform_data);
+	if (ret < 0)
+		goto err;
+
+	return ret;
+
+err:
+	kfree(wm8350);
+	return ret;
+}
+
+static int wm8350_i2c_remove(struct i2c_client *i2c)
+{
+	struct wm8350 *wm8350 = i2c_get_clientdata(i2c);
+
+	wm8350_device_exit(wm8350);
+	kfree(wm8350);
+
+	return 0;
+}
+
+static const struct i2c_device_id wm8350_i2c_id[] = {
+       { "wm8350", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8350_i2c_id);
+
+
+static struct i2c_driver wm8350_i2c_driver = {
+	.driver = {
+		   .name = "wm8350",
+		   .owner = THIS_MODULE,
+	},
+	.probe = wm8350_i2c_probe,
+	.remove = wm8350_i2c_remove,
+	.id_table = wm8350_i2c_id,
+};
+
+static int __init wm8350_i2c_init(void)
+{
+	return i2c_add_driver(&wm8350_i2c_driver);
+}
+/* init early so consumer devices can complete system boot */
+subsys_initcall(wm8350_i2c_init);
+
+static void __exit wm8350_i2c_exit(void)
+{
+	i2c_del_driver(&wm8350_i2c_driver);
+}
+module_exit(wm8350_i2c_exit);
+
+MODULE_DESCRIPTION("I2C support for the WM8350 AudioPlus PMIC");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/wm8350-regmap.c b/drivers/mfd/wm8350-regmap.c
new file mode 100644
index 0000000..974678d
--- /dev/null
+++ b/drivers/mfd/wm8350-regmap.c
@@ -0,0 +1,1347 @@
+/*
+ * wm8350-regmap.c  --  Wolfson Microelectronics WM8350 register map
+ *
+ * This file splits out the tables describing the defaults and access
+ * status of the WM8350 registers since they are rather large.
+ *
+ * Copyright 2007, 2008 Wolfson Microelectronics PLC.
+ *
+ *  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/mfd/wm8350/core.h>
+
+#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_0
+
+#undef WM8350_HAVE_CONFIG_MODE
+#define WM8350_HAVE_CONFIG_MODE
+
+const u16 wm8350_mode0_defaults[] = {
+	0x17FF,     /* R0   - Reset/ID */
+	0x1000,     /* R1   - ID */
+	0x0000,     /* R2 */
+	0x1002,     /* R3   - System Control 1 */
+	0x0004,     /* R4   - System Control 2 */
+	0x0000,     /* R5   - System Hibernate */
+	0x8A00,     /* R6   - Interface Control */
+	0x0000,     /* R7 */
+	0x8000,     /* R8   - Power mgmt (1) */
+	0x0000,     /* R9   - Power mgmt (2) */
+	0x0000,     /* R10  - Power mgmt (3) */
+	0x2000,     /* R11  - Power mgmt (4) */
+	0x0E00,     /* R12  - Power mgmt (5) */
+	0x0000,     /* R13  - Power mgmt (6) */
+	0x0000,     /* R14  - Power mgmt (7) */
+	0x0000,     /* R15 */
+	0x0000,     /* R16  - RTC Seconds/Minutes */
+	0x0100,     /* R17  - RTC Hours/Day */
+	0x0101,     /* R18  - RTC Date/Month */
+	0x1400,     /* R19  - RTC Year */
+	0x0000,     /* R20  - Alarm Seconds/Minutes */
+	0x0000,     /* R21  - Alarm Hours/Day */
+	0x0000,     /* R22  - Alarm Date/Month */
+	0x0320,     /* R23  - RTC Time Control */
+	0x0000,     /* R24  - System Interrupts */
+	0x0000,     /* R25  - Interrupt Status 1 */
+	0x0000,     /* R26  - Interrupt Status 2 */
+	0x0000,     /* R27  - Power Up Interrupt Status */
+	0x0000,     /* R28  - Under Voltage Interrupt status */
+	0x0000,     /* R29  - Over Current Interrupt status */
+	0x0000,     /* R30  - GPIO Interrupt Status */
+	0x0000,     /* R31  - Comparator Interrupt Status */
+	0x3FFF,     /* R32  - System Interrupts Mask */
+	0x0000,     /* R33  - Interrupt Status 1 Mask */
+	0x0000,     /* R34  - Interrupt Status 2 Mask */
+	0x0000,     /* R35  - Power Up Interrupt Status Mask */
+	0x0000,     /* R36  - Under Voltage Interrupt status Mask */
+	0x0000,     /* R37  - Over Current Interrupt status Mask */
+	0x0000,     /* R38  - GPIO Interrupt Status Mask */
+	0x0000,     /* R39  - Comparator Interrupt Status Mask */
+	0x0040,     /* R40  - Clock Control 1 */
+	0x0000,     /* R41  - Clock Control 2 */
+	0x3B00,     /* R42  - FLL Control 1 */
+	0x7086,     /* R43  - FLL Control 2 */
+	0xC226,     /* R44  - FLL Control 3 */
+	0x0000,     /* R45  - FLL Control 4 */
+	0x0000,     /* R46 */
+	0x0000,     /* R47 */
+	0x0000,     /* R48  - DAC Control */
+	0x0000,     /* R49 */
+	0x00C0,     /* R50  - DAC Digital Volume L */
+	0x00C0,     /* R51  - DAC Digital Volume R */
+	0x0000,     /* R52 */
+	0x0040,     /* R53  - DAC LR Rate */
+	0x0000,     /* R54  - DAC Clock Control */
+	0x0000,     /* R55 */
+	0x0000,     /* R56 */
+	0x0000,     /* R57 */
+	0x4000,     /* R58  - DAC Mute */
+	0x0000,     /* R59  - DAC Mute Volume */
+	0x0000,     /* R60  - DAC Side */
+	0x0000,     /* R61 */
+	0x0000,     /* R62 */
+	0x0000,     /* R63 */
+	0x8000,     /* R64  - ADC Control */
+	0x0000,     /* R65 */
+	0x00C0,     /* R66  - ADC Digital Volume L */
+	0x00C0,     /* R67  - ADC Digital Volume R */
+	0x0000,     /* R68  - ADC Divider */
+	0x0000,     /* R69 */
+	0x0040,     /* R70  - ADC LR Rate */
+	0x0000,     /* R71 */
+	0x0303,     /* R72  - Input Control */
+	0x0000,     /* R73  - IN3 Input Control */
+	0x0000,     /* R74  - Mic Bias Control */
+	0x0000,     /* R75 */
+	0x0000,     /* R76  - Output Control */
+	0x0000,     /* R77  - Jack Detect */
+	0x0000,     /* R78  - Anti Pop Control */
+	0x0000,     /* R79 */
+	0x0040,     /* R80  - Left Input Volume */
+	0x0040,     /* R81  - Right Input Volume */
+	0x0000,     /* R82 */
+	0x0000,     /* R83 */
+	0x0000,     /* R84 */
+	0x0000,     /* R85 */
+	0x0000,     /* R86 */
+	0x0000,     /* R87 */
+	0x0800,     /* R88  - Left Mixer Control */
+	0x1000,     /* R89  - Right Mixer Control */
+	0x0000,     /* R90 */
+	0x0000,     /* R91 */
+	0x0000,     /* R92  - OUT3 Mixer Control */
+	0x0000,     /* R93  - OUT4 Mixer Control */
+	0x0000,     /* R94 */
+	0x0000,     /* R95 */
+	0x0000,     /* R96  - Output Left Mixer Volume */
+	0x0000,     /* R97  - Output Right Mixer Volume */
+	0x0000,     /* R98  - Input Mixer Volume L */
+	0x0000,     /* R99  - Input Mixer Volume R */
+	0x0000,     /* R100 - Input Mixer Volume */
+	0x0000,     /* R101 */
+	0x0000,     /* R102 */
+	0x0000,     /* R103 */
+	0x00E4,     /* R104 - LOUT1 Volume */
+	0x00E4,     /* R105 - ROUT1 Volume */
+	0x00E4,     /* R106 - LOUT2 Volume */
+	0x02E4,     /* R107 - ROUT2 Volume */
+	0x0000,     /* R108 */
+	0x0000,     /* R109 */
+	0x0000,     /* R110 */
+	0x0000,     /* R111 - BEEP Volume */
+	0x0A00,     /* R112 - AI Formating */
+	0x0000,     /* R113 - ADC DAC COMP */
+	0x0020,     /* R114 - AI ADC Control */
+	0x0020,     /* R115 - AI DAC Control */
+	0x0000,     /* R116 - AIF Test */
+	0x0000,     /* R117 */
+	0x0000,     /* R118 */
+	0x0000,     /* R119 */
+	0x0000,     /* R120 */
+	0x0000,     /* R121 */
+	0x0000,     /* R122 */
+	0x0000,     /* R123 */
+	0x0000,     /* R124 */
+	0x0000,     /* R125 */
+	0x0000,     /* R126 */
+	0x0000,     /* R127 */
+	0x1FFF,     /* R128 - GPIO Debounce */
+	0x0000,     /* R129 - GPIO Pin pull up Control */
+	0x03FC,     /* R130 - GPIO Pull down Control */
+	0x0000,     /* R131 - GPIO Interrupt Mode */
+	0x0000,     /* R132 */
+	0x0000,     /* R133 - GPIO Control */
+	0x0FFC,     /* R134 - GPIO Configuration (i/o) */
+	0x0FFC,     /* R135 - GPIO Pin Polarity / Type */
+	0x0000,     /* R136 */
+	0x0000,     /* R137 */
+	0x0000,     /* R138 */
+	0x0000,     /* R139 */
+	0x0013,     /* R140 - GPIO Function Select 1 */
+	0x0000,     /* R141 - GPIO Function Select 2 */
+	0x0000,     /* R142 - GPIO Function Select 3 */
+	0x0003,     /* R143 - GPIO Function Select 4 */
+	0x0000,     /* R144 - Digitiser Control (1) */
+	0x0002,     /* R145 - Digitiser Control (2) */
+	0x0000,     /* R146 */
+	0x0000,     /* R147 */
+	0x0000,     /* R148 */
+	0x0000,     /* R149 */
+	0x0000,     /* R150 */
+	0x0000,     /* R151 */
+	0x7000,     /* R152 - AUX1 Readback */
+	0x7000,     /* R153 - AUX2 Readback */
+	0x7000,     /* R154 - AUX3 Readback */
+	0x7000,     /* R155 - AUX4 Readback */
+	0x0000,     /* R156 - USB Voltage Readback */
+	0x0000,     /* R157 - LINE Voltage Readback */
+	0x0000,     /* R158 - BATT Voltage Readback */
+	0x0000,     /* R159 - Chip Temp Readback */
+	0x0000,     /* R160 */
+	0x0000,     /* R161 */
+	0x0000,     /* R162 */
+	0x0000,     /* R163 - Generic Comparator Control */
+	0x0000,     /* R164 - Generic comparator 1 */
+	0x0000,     /* R165 - Generic comparator 2 */
+	0x0000,     /* R166 - Generic comparator 3 */
+	0x0000,     /* R167 - Generic comparator 4 */
+	0xA00F,     /* R168 - Battery Charger Control 1 */
+	0x0B06,     /* R169 - Battery Charger Control 2 */
+	0x0000,     /* R170 - Battery Charger Control 3 */
+	0x0000,     /* R171 */
+	0x0000,     /* R172 - Current Sink Driver A */
+	0x0000,     /* R173 - CSA Flash control */
+	0x0000,     /* R174 - Current Sink Driver B */
+	0x0000,     /* R175 - CSB Flash control */
+	0x0000,     /* R176 - DCDC/LDO requested */
+	0x002D,     /* R177 - DCDC Active options */
+	0x0000,     /* R178 - DCDC Sleep options */
+	0x0025,     /* R179 - Power-check comparator */
+	0x000E,     /* R180 - DCDC1 Control */
+	0x0000,     /* R181 - DCDC1 Timeouts */
+	0x1006,     /* R182 - DCDC1 Low Power */
+	0x0018,     /* R183 - DCDC2 Control */
+	0x0000,     /* R184 - DCDC2 Timeouts */
+	0x0000,     /* R185 */
+	0x0000,     /* R186 - DCDC3 Control */
+	0x0000,     /* R187 - DCDC3 Timeouts */
+	0x0006,     /* R188 - DCDC3 Low Power */
+	0x0000,     /* R189 - DCDC4 Control */
+	0x0000,     /* R190 - DCDC4 Timeouts */
+	0x0006,     /* R191 - DCDC4 Low Power */
+	0x0008,     /* R192 - DCDC5 Control */
+	0x0000,     /* R193 - DCDC5 Timeouts */
+	0x0000,     /* R194 */
+	0x0000,     /* R195 - DCDC6 Control */
+	0x0000,     /* R196 - DCDC6 Timeouts */
+	0x0006,     /* R197 - DCDC6 Low Power */
+	0x0000,     /* R198 */
+	0x0003,     /* R199 - Limit Switch Control */
+	0x001C,     /* R200 - LDO1 Control */
+	0x0000,     /* R201 - LDO1 Timeouts */
+	0x001C,     /* R202 - LDO1 Low Power */
+	0x001B,     /* R203 - LDO2 Control */
+	0x0000,     /* R204 - LDO2 Timeouts */
+	0x001C,     /* R205 - LDO2 Low Power */
+	0x001B,     /* R206 - LDO3 Control */
+	0x0000,     /* R207 - LDO3 Timeouts */
+	0x001C,     /* R208 - LDO3 Low Power */
+	0x001B,     /* R209 - LDO4 Control */
+	0x0000,     /* R210 - LDO4 Timeouts */
+	0x001C,     /* R211 - LDO4 Low Power */
+	0x0000,     /* R212 */
+	0x0000,     /* R213 */
+	0x0000,     /* R214 */
+	0x0000,     /* R215 - VCC_FAULT Masks */
+	0x001F,     /* R216 - Main Bandgap Control */
+	0x0000,     /* R217 - OSC Control */
+	0x9000,     /* R218 - RTC Tick Control */
+	0x0000,     /* R219 */
+	0x4000,     /* R220 - RAM BIST 1 */
+	0x0000,     /* R221 */
+	0x0000,     /* R222 */
+	0x0000,     /* R223 */
+	0x0000,     /* R224 */
+	0x0000,     /* R225 - DCDC/LDO status */
+	0x0000,     /* R226 */
+	0x0000,     /* R227 */
+	0x0000,     /* R228 */
+	0x0000,     /* R229 */
+	0xE000,     /* R230 - GPIO Pin Status */
+	0x0000,     /* R231 */
+	0x0000,     /* R232 */
+	0x0000,     /* R233 */
+	0x0000,     /* R234 */
+	0x0000,     /* R235 */
+	0x0000,     /* R236 */
+	0x0000,     /* R237 */
+	0x0000,     /* R238 */
+	0x0000,     /* R239 */
+	0x0000,     /* R240 */
+	0x0000,     /* R241 */
+	0x0000,     /* R242 */
+	0x0000,     /* R243 */
+	0x0000,     /* R244 */
+	0x0000,     /* R245 */
+	0x0000,     /* R246 */
+	0x0000,     /* R247 */
+	0x0000,     /* R248 */
+	0x0000,     /* R249 */
+	0x0000,     /* R250 */
+	0x0000,     /* R251 */
+	0x0000,     /* R252 */
+	0x0000,     /* R253 */
+	0x0000,     /* R254 */
+	0x0000,     /* R255 */
+};
+#endif
+
+#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_1
+
+#undef WM8350_HAVE_CONFIG_MODE
+#define WM8350_HAVE_CONFIG_MODE
+
+const u16 wm8350_mode1_defaults[] = {
+	0x17FF,     /* R0   - Reset/ID */
+	0x1000,     /* R1   - ID */
+	0x0000,     /* R2 */
+	0x1002,     /* R3   - System Control 1 */
+	0x0014,     /* R4   - System Control 2 */
+	0x0000,     /* R5   - System Hibernate */
+	0x8A00,     /* R6   - Interface Control */
+	0x0000,     /* R7 */
+	0x8000,     /* R8   - Power mgmt (1) */
+	0x0000,     /* R9   - Power mgmt (2) */
+	0x0000,     /* R10  - Power mgmt (3) */
+	0x2000,     /* R11  - Power mgmt (4) */
+	0x0E00,     /* R12  - Power mgmt (5) */
+	0x0000,     /* R13  - Power mgmt (6) */
+	0x0000,     /* R14  - Power mgmt (7) */
+	0x0000,     /* R15 */
+	0x0000,     /* R16  - RTC Seconds/Minutes */
+	0x0100,     /* R17  - RTC Hours/Day */
+	0x0101,     /* R18  - RTC Date/Month */
+	0x1400,     /* R19  - RTC Year */
+	0x0000,     /* R20  - Alarm Seconds/Minutes */
+	0x0000,     /* R21  - Alarm Hours/Day */
+	0x0000,     /* R22  - Alarm Date/Month */
+	0x0320,     /* R23  - RTC Time Control */
+	0x0000,     /* R24  - System Interrupts */
+	0x0000,     /* R25  - Interrupt Status 1 */
+	0x0000,     /* R26  - Interrupt Status 2 */
+	0x0000,     /* R27  - Power Up Interrupt Status */
+	0x0000,     /* R28  - Under Voltage Interrupt status */
+	0x0000,     /* R29  - Over Current Interrupt status */
+	0x0000,     /* R30  - GPIO Interrupt Status */
+	0x0000,     /* R31  - Comparator Interrupt Status */
+	0x3FFF,     /* R32  - System Interrupts Mask */
+	0x0000,     /* R33  - Interrupt Status 1 Mask */
+	0x0000,     /* R34  - Interrupt Status 2 Mask */
+	0x0000,     /* R35  - Power Up Interrupt Status Mask */
+	0x0000,     /* R36  - Under Voltage Interrupt status Mask */
+	0x0000,     /* R37  - Over Current Interrupt status Mask */
+	0x0000,     /* R38  - GPIO Interrupt Status Mask */
+	0x0000,     /* R39  - Comparator Interrupt Status Mask */
+	0x0040,     /* R40  - Clock Control 1 */
+	0x0000,     /* R41  - Clock Control 2 */
+	0x3B00,     /* R42  - FLL Control 1 */
+	0x7086,     /* R43  - FLL Control 2 */
+	0xC226,     /* R44  - FLL Control 3 */
+	0x0000,     /* R45  - FLL Control 4 */
+	0x0000,     /* R46 */
+	0x0000,     /* R47 */
+	0x0000,     /* R48  - DAC Control */
+	0x0000,     /* R49 */
+	0x00C0,     /* R50  - DAC Digital Volume L */
+	0x00C0,     /* R51  - DAC Digital Volume R */
+	0x0000,     /* R52 */
+	0x0040,     /* R53  - DAC LR Rate */
+	0x0000,     /* R54  - DAC Clock Control */
+	0x0000,     /* R55 */
+	0x0000,     /* R56 */
+	0x0000,     /* R57 */
+	0x4000,     /* R58  - DAC Mute */
+	0x0000,     /* R59  - DAC Mute Volume */
+	0x0000,     /* R60  - DAC Side */
+	0x0000,     /* R61 */
+	0x0000,     /* R62 */
+	0x0000,     /* R63 */
+	0x8000,     /* R64  - ADC Control */
+	0x0000,     /* R65 */
+	0x00C0,     /* R66  - ADC Digital Volume L */
+	0x00C0,     /* R67  - ADC Digital Volume R */
+	0x0000,     /* R68  - ADC Divider */
+	0x0000,     /* R69 */
+	0x0040,     /* R70  - ADC LR Rate */
+	0x0000,     /* R71 */
+	0x0303,     /* R72  - Input Control */
+	0x0000,     /* R73  - IN3 Input Control */
+	0x0000,     /* R74  - Mic Bias Control */
+	0x0000,     /* R75 */
+	0x0000,     /* R76  - Output Control */
+	0x0000,     /* R77  - Jack Detect */
+	0x0000,     /* R78  - Anti Pop Control */
+	0x0000,     /* R79 */
+	0x0040,     /* R80  - Left Input Volume */
+	0x0040,     /* R81  - Right Input Volume */
+	0x0000,     /* R82 */
+	0x0000,     /* R83 */
+	0x0000,     /* R84 */
+	0x0000,     /* R85 */
+	0x0000,     /* R86 */
+	0x0000,     /* R87 */
+	0x0800,     /* R88  - Left Mixer Control */
+	0x1000,     /* R89  - Right Mixer Control */
+	0x0000,     /* R90 */
+	0x0000,     /* R91 */
+	0x0000,     /* R92  - OUT3 Mixer Control */
+	0x0000,     /* R93  - OUT4 Mixer Control */
+	0x0000,     /* R94 */
+	0x0000,     /* R95 */
+	0x0000,     /* R96  - Output Left Mixer Volume */
+	0x0000,     /* R97  - Output Right Mixer Volume */
+	0x0000,     /* R98  - Input Mixer Volume L */
+	0x0000,     /* R99  - Input Mixer Volume R */
+	0x0000,     /* R100 - Input Mixer Volume */
+	0x0000,     /* R101 */
+	0x0000,     /* R102 */
+	0x0000,     /* R103 */
+	0x00E4,     /* R104 - LOUT1 Volume */
+	0x00E4,     /* R105 - ROUT1 Volume */
+	0x00E4,     /* R106 - LOUT2 Volume */
+	0x02E4,     /* R107 - ROUT2 Volume */
+	0x0000,     /* R108 */
+	0x0000,     /* R109 */
+	0x0000,     /* R110 */
+	0x0000,     /* R111 - BEEP Volume */
+	0x0A00,     /* R112 - AI Formating */
+	0x0000,     /* R113 - ADC DAC COMP */
+	0x0020,     /* R114 - AI ADC Control */
+	0x0020,     /* R115 - AI DAC Control */
+	0x0000,     /* R116 - AIF Test */
+	0x0000,     /* R117 */
+	0x0000,     /* R118 */
+	0x0000,     /* R119 */
+	0x0000,     /* R120 */
+	0x0000,     /* R121 */
+	0x0000,     /* R122 */
+	0x0000,     /* R123 */
+	0x0000,     /* R124 */
+	0x0000,     /* R125 */
+	0x0000,     /* R126 */
+	0x0000,     /* R127 */
+	0x1FFF,     /* R128 - GPIO Debounce */
+	0x0000,     /* R129 - GPIO Pin pull up Control */
+	0x03FC,     /* R130 - GPIO Pull down Control */
+	0x0000,     /* R131 - GPIO Interrupt Mode */
+	0x0000,     /* R132 */
+	0x0000,     /* R133 - GPIO Control */
+	0x00FB,     /* R134 - GPIO Configuration (i/o) */
+	0x04FE,     /* R135 - GPIO Pin Polarity / Type */
+	0x0000,     /* R136 */
+	0x0000,     /* R137 */
+	0x0000,     /* R138 */
+	0x0000,     /* R139 */
+	0x0312,     /* R140 - GPIO Function Select 1 */
+	0x1003,     /* R141 - GPIO Function Select 2 */
+	0x1331,     /* R142 - GPIO Function Select 3 */
+	0x0003,     /* R143 - GPIO Function Select 4 */
+	0x0000,     /* R144 - Digitiser Control (1) */
+	0x0002,     /* R145 - Digitiser Control (2) */
+	0x0000,     /* R146 */
+	0x0000,     /* R147 */
+	0x0000,     /* R148 */
+	0x0000,     /* R149 */
+	0x0000,     /* R150 */
+	0x0000,     /* R151 */
+	0x7000,     /* R152 - AUX1 Readback */
+	0x7000,     /* R153 - AUX2 Readback */
+	0x7000,     /* R154 - AUX3 Readback */
+	0x7000,     /* R155 - AUX4 Readback */
+	0x0000,     /* R156 - USB Voltage Readback */
+	0x0000,     /* R157 - LINE Voltage Readback */
+	0x0000,     /* R158 - BATT Voltage Readback */
+	0x0000,     /* R159 - Chip Temp Readback */
+	0x0000,     /* R160 */
+	0x0000,     /* R161 */
+	0x0000,     /* R162 */
+	0x0000,     /* R163 - Generic Comparator Control */
+	0x0000,     /* R164 - Generic comparator 1 */
+	0x0000,     /* R165 - Generic comparator 2 */
+	0x0000,     /* R166 - Generic comparator 3 */
+	0x0000,     /* R167 - Generic comparator 4 */
+	0xA00F,     /* R168 - Battery Charger Control 1 */
+	0x0B06,     /* R169 - Battery Charger Control 2 */
+	0x0000,     /* R170 - Battery Charger Control 3 */
+	0x0000,     /* R171 */
+	0x0000,     /* R172 - Current Sink Driver A */
+	0x0000,     /* R173 - CSA Flash control */
+	0x0000,     /* R174 - Current Sink Driver B */
+	0x0000,     /* R175 - CSB Flash control */
+	0x0000,     /* R176 - DCDC/LDO requested */
+	0x002D,     /* R177 - DCDC Active options */
+	0x0000,     /* R178 - DCDC Sleep options */
+	0x0025,     /* R179 - Power-check comparator */
+	0x0062,     /* R180 - DCDC1 Control */
+	0x0400,     /* R181 - DCDC1 Timeouts */
+	0x1006,     /* R182 - DCDC1 Low Power */
+	0x0018,     /* R183 - DCDC2 Control */
+	0x0000,     /* R184 - DCDC2 Timeouts */
+	0x0000,     /* R185 */
+	0x0026,     /* R186 - DCDC3 Control */
+	0x0400,     /* R187 - DCDC3 Timeouts */
+	0x0006,     /* R188 - DCDC3 Low Power */
+	0x0062,     /* R189 - DCDC4 Control */
+	0x0400,     /* R190 - DCDC4 Timeouts */
+	0x0006,     /* R191 - DCDC4 Low Power */
+	0x0008,     /* R192 - DCDC5 Control */
+	0x0000,     /* R193 - DCDC5 Timeouts */
+	0x0000,     /* R194 */
+	0x0026,     /* R195 - DCDC6 Control */
+	0x0800,     /* R196 - DCDC6 Timeouts */
+	0x0006,     /* R197 - DCDC6 Low Power */
+	0x0000,     /* R198 */
+	0x0003,     /* R199 - Limit Switch Control */
+	0x0006,     /* R200 - LDO1 Control */
+	0x0400,     /* R201 - LDO1 Timeouts */
+	0x001C,     /* R202 - LDO1 Low Power */
+	0x0006,     /* R203 - LDO2 Control */
+	0x0400,     /* R204 - LDO2 Timeouts */
+	0x001C,     /* R205 - LDO2 Low Power */
+	0x001B,     /* R206 - LDO3 Control */
+	0x0000,     /* R207 - LDO3 Timeouts */
+	0x001C,     /* R208 - LDO3 Low Power */
+	0x001B,     /* R209 - LDO4 Control */
+	0x0000,     /* R210 - LDO4 Timeouts */
+	0x001C,     /* R211 - LDO4 Low Power */
+	0x0000,     /* R212 */
+	0x0000,     /* R213 */
+	0x0000,     /* R214 */
+	0x0000,     /* R215 - VCC_FAULT Masks */
+	0x001F,     /* R216 - Main Bandgap Control */
+	0x0000,     /* R217 - OSC Control */
+	0x9000,     /* R218 - RTC Tick Control */
+	0x0000,     /* R219 */
+	0x4000,     /* R220 - RAM BIST 1 */
+	0x0000,     /* R221 */
+	0x0000,     /* R222 */
+	0x0000,     /* R223 */
+	0x0000,     /* R224 */
+	0x0000,     /* R225 - DCDC/LDO status */
+	0x0000,     /* R226 */
+	0x0000,     /* R227 */
+	0x0000,     /* R228 */
+	0x0000,     /* R229 */
+	0xE000,     /* R230 - GPIO Pin Status */
+	0x0000,     /* R231 */
+	0x0000,     /* R232 */
+	0x0000,     /* R233 */
+	0x0000,     /* R234 */
+	0x0000,     /* R235 */
+	0x0000,     /* R236 */
+	0x0000,     /* R237 */
+	0x0000,     /* R238 */
+	0x0000,     /* R239 */
+	0x0000,     /* R240 */
+	0x0000,     /* R241 */
+	0x0000,     /* R242 */
+	0x0000,     /* R243 */
+	0x0000,     /* R244 */
+	0x0000,     /* R245 */
+	0x0000,     /* R246 */
+	0x0000,     /* R247 */
+	0x0000,     /* R248 */
+	0x0000,     /* R249 */
+	0x0000,     /* R250 */
+	0x0000,     /* R251 */
+	0x0000,     /* R252 */
+	0x0000,     /* R253 */
+	0x0000,     /* R254 */
+	0x0000,     /* R255 */
+};
+#endif
+
+#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_2
+
+#undef WM8350_HAVE_CONFIG_MODE
+#define WM8350_HAVE_CONFIG_MODE
+
+const u16 wm8350_mode2_defaults[] = {
+	0x17FF,     /* R0   - Reset/ID */
+	0x1000,     /* R1   - ID */
+	0x0000,     /* R2 */
+	0x1002,     /* R3   - System Control 1 */
+	0x0014,     /* R4   - System Control 2 */
+	0x0000,     /* R5   - System Hibernate */
+	0x8A00,     /* R6   - Interface Control */
+	0x0000,     /* R7 */
+	0x8000,     /* R8   - Power mgmt (1) */
+	0x0000,     /* R9   - Power mgmt (2) */
+	0x0000,     /* R10  - Power mgmt (3) */
+	0x2000,     /* R11  - Power mgmt (4) */
+	0x0E00,     /* R12  - Power mgmt (5) */
+	0x0000,     /* R13  - Power mgmt (6) */
+	0x0000,     /* R14  - Power mgmt (7) */
+	0x0000,     /* R15 */
+	0x0000,     /* R16  - RTC Seconds/Minutes */
+	0x0100,     /* R17  - RTC Hours/Day */
+	0x0101,     /* R18  - RTC Date/Month */
+	0x1400,     /* R19  - RTC Year */
+	0x0000,     /* R20  - Alarm Seconds/Minutes */
+	0x0000,     /* R21  - Alarm Hours/Day */
+	0x0000,     /* R22  - Alarm Date/Month */
+	0x0320,     /* R23  - RTC Time Control */
+	0x0000,     /* R24  - System Interrupts */
+	0x0000,     /* R25  - Interrupt Status 1 */
+	0x0000,     /* R26  - Interrupt Status 2 */
+	0x0000,     /* R27  - Power Up Interrupt Status */
+	0x0000,     /* R28  - Under Voltage Interrupt status */
+	0x0000,     /* R29  - Over Current Interrupt status */
+	0x0000,     /* R30  - GPIO Interrupt Status */
+	0x0000,     /* R31  - Comparator Interrupt Status */
+	0x3FFF,     /* R32  - System Interrupts Mask */
+	0x0000,     /* R33  - Interrupt Status 1 Mask */
+	0x0000,     /* R34  - Interrupt Status 2 Mask */
+	0x0000,     /* R35  - Power Up Interrupt Status Mask */
+	0x0000,     /* R36  - Under Voltage Interrupt status Mask */
+	0x0000,     /* R37  - Over Current Interrupt status Mask */
+	0x0000,     /* R38  - GPIO Interrupt Status Mask */
+	0x0000,     /* R39  - Comparator Interrupt Status Mask */
+	0x0040,     /* R40  - Clock Control 1 */
+	0x0000,     /* R41  - Clock Control 2 */
+	0x3B00,     /* R42  - FLL Control 1 */
+	0x7086,     /* R43  - FLL Control 2 */
+	0xC226,     /* R44  - FLL Control 3 */
+	0x0000,     /* R45  - FLL Control 4 */
+	0x0000,     /* R46 */
+	0x0000,     /* R47 */
+	0x0000,     /* R48  - DAC Control */
+	0x0000,     /* R49 */
+	0x00C0,     /* R50  - DAC Digital Volume L */
+	0x00C0,     /* R51  - DAC Digital Volume R */
+	0x0000,     /* R52 */
+	0x0040,     /* R53  - DAC LR Rate */
+	0x0000,     /* R54  - DAC Clock Control */
+	0x0000,     /* R55 */
+	0x0000,     /* R56 */
+	0x0000,     /* R57 */
+	0x4000,     /* R58  - DAC Mute */
+	0x0000,     /* R59  - DAC Mute Volume */
+	0x0000,     /* R60  - DAC Side */
+	0x0000,     /* R61 */
+	0x0000,     /* R62 */
+	0x0000,     /* R63 */
+	0x8000,     /* R64  - ADC Control */
+	0x0000,     /* R65 */
+	0x00C0,     /* R66  - ADC Digital Volume L */
+	0x00C0,     /* R67  - ADC Digital Volume R */
+	0x0000,     /* R68  - ADC Divider */
+	0x0000,     /* R69 */
+	0x0040,     /* R70  - ADC LR Rate */
+	0x0000,     /* R71 */
+	0x0303,     /* R72  - Input Control */
+	0x0000,     /* R73  - IN3 Input Control */
+	0x0000,     /* R74  - Mic Bias Control */
+	0x0000,     /* R75 */
+	0x0000,     /* R76  - Output Control */
+	0x0000,     /* R77  - Jack Detect */
+	0x0000,     /* R78  - Anti Pop Control */
+	0x0000,     /* R79 */
+	0x0040,     /* R80  - Left Input Volume */
+	0x0040,     /* R81  - Right Input Volume */
+	0x0000,     /* R82 */
+	0x0000,     /* R83 */
+	0x0000,     /* R84 */
+	0x0000,     /* R85 */
+	0x0000,     /* R86 */
+	0x0000,     /* R87 */
+	0x0800,     /* R88  - Left Mixer Control */
+	0x1000,     /* R89  - Right Mixer Control */
+	0x0000,     /* R90 */
+	0x0000,     /* R91 */
+	0x0000,     /* R92  - OUT3 Mixer Control */
+	0x0000,     /* R93  - OUT4 Mixer Control */
+	0x0000,     /* R94 */
+	0x0000,     /* R95 */
+	0x0000,     /* R96  - Output Left Mixer Volume */
+	0x0000,     /* R97  - Output Right Mixer Volume */
+	0x0000,     /* R98  - Input Mixer Volume L */
+	0x0000,     /* R99  - Input Mixer Volume R */
+	0x0000,     /* R100 - Input Mixer Volume */
+	0x0000,     /* R101 */
+	0x0000,     /* R102 */
+	0x0000,     /* R103 */
+	0x00E4,     /* R104 - LOUT1 Volume */
+	0x00E4,     /* R105 - ROUT1 Volume */
+	0x00E4,     /* R106 - LOUT2 Volume */
+	0x02E4,     /* R107 - ROUT2 Volume */
+	0x0000,     /* R108 */
+	0x0000,     /* R109 */
+	0x0000,     /* R110 */
+	0x0000,     /* R111 - BEEP Volume */
+	0x0A00,     /* R112 - AI Formating */
+	0x0000,     /* R113 - ADC DAC COMP */
+	0x0020,     /* R114 - AI ADC Control */
+	0x0020,     /* R115 - AI DAC Control */
+	0x0000,     /* R116 - AIF Test */
+	0x0000,     /* R117 */
+	0x0000,     /* R118 */
+	0x0000,     /* R119 */
+	0x0000,     /* R120 */
+	0x0000,     /* R121 */
+	0x0000,     /* R122 */
+	0x0000,     /* R123 */
+	0x0000,     /* R124 */
+	0x0000,     /* R125 */
+	0x0000,     /* R126 */
+	0x0000,     /* R127 */
+	0x1FFF,     /* R128 - GPIO Debounce */
+	0x0000,     /* R129 - GPIO Pin pull up Control */
+	0x03FC,     /* R130 - GPIO Pull down Control */
+	0x0000,     /* R131 - GPIO Interrupt Mode */
+	0x0000,     /* R132 */
+	0x0000,     /* R133 - GPIO Control */
+	0x08FB,     /* R134 - GPIO Configuration (i/o) */
+	0x0CFE,     /* R135 - GPIO Pin Polarity / Type */
+	0x0000,     /* R136 */
+	0x0000,     /* R137 */
+	0x0000,     /* R138 */
+	0x0000,     /* R139 */
+	0x0312,     /* R140 - GPIO Function Select 1 */
+	0x0003,     /* R141 - GPIO Function Select 2 */
+	0x2331,     /* R142 - GPIO Function Select 3 */
+	0x0003,     /* R143 - GPIO Function Select 4 */
+	0x0000,     /* R144 - Digitiser Control (1) */
+	0x0002,     /* R145 - Digitiser Control (2) */
+	0x0000,     /* R146 */
+	0x0000,     /* R147 */
+	0x0000,     /* R148 */
+	0x0000,     /* R149 */
+	0x0000,     /* R150 */
+	0x0000,     /* R151 */
+	0x7000,     /* R152 - AUX1 Readback */
+	0x7000,     /* R153 - AUX2 Readback */
+	0x7000,     /* R154 - AUX3 Readback */
+	0x7000,     /* R155 - AUX4 Readback */
+	0x0000,     /* R156 - USB Voltage Readback */
+	0x0000,     /* R157 - LINE Voltage Readback */
+	0x0000,     /* R158 - BATT Voltage Readback */
+	0x0000,     /* R159 - Chip Temp Readback */
+	0x0000,     /* R160 */
+	0x0000,     /* R161 */
+	0x0000,     /* R162 */
+	0x0000,     /* R163 - Generic Comparator Control */
+	0x0000,     /* R164 - Generic comparator 1 */
+	0x0000,     /* R165 - Generic comparator 2 */
+	0x0000,     /* R166 - Generic comparator 3 */
+	0x0000,     /* R167 - Generic comparator 4 */
+	0xA00F,     /* R168 - Battery Charger Control 1 */
+	0x0B06,     /* R169 - Battery Charger Control 2 */
+	0x0000,     /* R170 - Battery Charger Control 3 */
+	0x0000,     /* R171 */
+	0x0000,     /* R172 - Current Sink Driver A */
+	0x0000,     /* R173 - CSA Flash control */
+	0x0000,     /* R174 - Current Sink Driver B */
+	0x0000,     /* R175 - CSB Flash control */
+	0x0000,     /* R176 - DCDC/LDO requested */
+	0x002D,     /* R177 - DCDC Active options */
+	0x0000,     /* R178 - DCDC Sleep options */
+	0x0025,     /* R179 - Power-check comparator */
+	0x000E,     /* R180 - DCDC1 Control */
+	0x0400,     /* R181 - DCDC1 Timeouts */
+	0x1006,     /* R182 - DCDC1 Low Power */
+	0x0018,     /* R183 - DCDC2 Control */
+	0x0000,     /* R184 - DCDC2 Timeouts */
+	0x0000,     /* R185 */
+	0x002E,     /* R186 - DCDC3 Control */
+	0x0800,     /* R187 - DCDC3 Timeouts */
+	0x0006,     /* R188 - DCDC3 Low Power */
+	0x000E,     /* R189 - DCDC4 Control */
+	0x0800,     /* R190 - DCDC4 Timeouts */
+	0x0006,     /* R191 - DCDC4 Low Power */
+	0x0008,     /* R192 - DCDC5 Control */
+	0x0000,     /* R193 - DCDC5 Timeouts */
+	0x0000,     /* R194 */
+	0x0026,     /* R195 - DCDC6 Control */
+	0x0C00,     /* R196 - DCDC6 Timeouts */
+	0x0006,     /* R197 - DCDC6 Low Power */
+	0x0000,     /* R198 */
+	0x0003,     /* R199 - Limit Switch Control */
+	0x001A,     /* R200 - LDO1 Control */
+	0x0800,     /* R201 - LDO1 Timeouts */
+	0x001C,     /* R202 - LDO1 Low Power */
+	0x0010,     /* R203 - LDO2 Control */
+	0x0800,     /* R204 - LDO2 Timeouts */
+	0x001C,     /* R205 - LDO2 Low Power */
+	0x000A,     /* R206 - LDO3 Control */
+	0x0C00,     /* R207 - LDO3 Timeouts */
+	0x001C,     /* R208 - LDO3 Low Power */
+	0x001A,     /* R209 - LDO4 Control */
+	0x0800,     /* R210 - LDO4 Timeouts */
+	0x001C,     /* R211 - LDO4 Low Power */
+	0x0000,     /* R212 */
+	0x0000,     /* R213 */
+	0x0000,     /* R214 */
+	0x0000,     /* R215 - VCC_FAULT Masks */
+	0x001F,     /* R216 - Main Bandgap Control */
+	0x0000,     /* R217 - OSC Control */
+	0x9000,     /* R218 - RTC Tick Control */
+	0x0000,     /* R219 */
+	0x4000,     /* R220 - RAM BIST 1 */
+	0x0000,     /* R221 */
+	0x0000,     /* R222 */
+	0x0000,     /* R223 */
+	0x0000,     /* R224 */
+	0x0000,     /* R225 - DCDC/LDO status */
+	0x0000,     /* R226 */
+	0x0000,     /* R227 */
+	0x0000,     /* R228 */
+	0x0000,     /* R229 */
+	0xE000,     /* R230 - GPIO Pin Status */
+	0x0000,     /* R231 */
+	0x0000,     /* R232 */
+	0x0000,     /* R233 */
+	0x0000,     /* R234 */
+	0x0000,     /* R235 */
+	0x0000,     /* R236 */
+	0x0000,     /* R237 */
+	0x0000,     /* R238 */
+	0x0000,     /* R239 */
+	0x0000,     /* R240 */
+	0x0000,     /* R241 */
+	0x0000,     /* R242 */
+	0x0000,     /* R243 */
+	0x0000,     /* R244 */
+	0x0000,     /* R245 */
+	0x0000,     /* R246 */
+	0x0000,     /* R247 */
+	0x0000,     /* R248 */
+	0x0000,     /* R249 */
+	0x0000,     /* R250 */
+	0x0000,     /* R251 */
+	0x0000,     /* R252 */
+	0x0000,     /* R253 */
+	0x0000,     /* R254 */
+	0x0000,     /* R255 */
+};
+#endif
+
+#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_3
+
+#undef WM8350_HAVE_CONFIG_MODE
+#define WM8350_HAVE_CONFIG_MODE
+
+const u16 wm8350_mode3_defaults[] = {
+	0x17FF,     /* R0   - Reset/ID */
+	0x1000,     /* R1   - ID */
+	0x0000,     /* R2 */
+	0x1000,     /* R3   - System Control 1 */
+	0x0004,     /* R4   - System Control 2 */
+	0x0000,     /* R5   - System Hibernate */
+	0x8A00,     /* R6   - Interface Control */
+	0x0000,     /* R7 */
+	0x8000,     /* R8   - Power mgmt (1) */
+	0x0000,     /* R9   - Power mgmt (2) */
+	0x0000,     /* R10  - Power mgmt (3) */
+	0x2000,     /* R11  - Power mgmt (4) */
+	0x0E00,     /* R12  - Power mgmt (5) */
+	0x0000,     /* R13  - Power mgmt (6) */
+	0x0000,     /* R14  - Power mgmt (7) */
+	0x0000,     /* R15 */
+	0x0000,     /* R16  - RTC Seconds/Minutes */
+	0x0100,     /* R17  - RTC Hours/Day */
+	0x0101,     /* R18  - RTC Date/Month */
+	0x1400,     /* R19  - RTC Year */
+	0x0000,     /* R20  - Alarm Seconds/Minutes */
+	0x0000,     /* R21  - Alarm Hours/Day */
+	0x0000,     /* R22  - Alarm Date/Month */
+	0x0320,     /* R23  - RTC Time Control */
+	0x0000,     /* R24  - System Interrupts */
+	0x0000,     /* R25  - Interrupt Status 1 */
+	0x0000,     /* R26  - Interrupt Status 2 */
+	0x0000,     /* R27  - Power Up Interrupt Status */
+	0x0000,     /* R28  - Under Voltage Interrupt status */
+	0x0000,     /* R29  - Over Current Interrupt status */
+	0x0000,     /* R30  - GPIO Interrupt Status */
+	0x0000,     /* R31  - Comparator Interrupt Status */
+	0x3FFF,     /* R32  - System Interrupts Mask */
+	0x0000,     /* R33  - Interrupt Status 1 Mask */
+	0x0000,     /* R34  - Interrupt Status 2 Mask */
+	0x0000,     /* R35  - Power Up Interrupt Status Mask */
+	0x0000,     /* R36  - Under Voltage Interrupt status Mask */
+	0x0000,     /* R37  - Over Current Interrupt status Mask */
+	0x0000,     /* R38  - GPIO Interrupt Status Mask */
+	0x0000,     /* R39  - Comparator Interrupt Status Mask */
+	0x0040,     /* R40  - Clock Control 1 */
+	0x0000,     /* R41  - Clock Control 2 */
+	0x3B00,     /* R42  - FLL Control 1 */
+	0x7086,     /* R43  - FLL Control 2 */
+	0xC226,     /* R44  - FLL Control 3 */
+	0x0000,     /* R45  - FLL Control 4 */
+	0x0000,     /* R46 */
+	0x0000,     /* R47 */
+	0x0000,     /* R48  - DAC Control */
+	0x0000,     /* R49 */
+	0x00C0,     /* R50  - DAC Digital Volume L */
+	0x00C0,     /* R51  - DAC Digital Volume R */
+	0x0000,     /* R52 */
+	0x0040,     /* R53  - DAC LR Rate */
+	0x0000,     /* R54  - DAC Clock Control */
+	0x0000,     /* R55 */
+	0x0000,     /* R56 */
+	0x0000,     /* R57 */
+	0x4000,     /* R58  - DAC Mute */
+	0x0000,     /* R59  - DAC Mute Volume */
+	0x0000,     /* R60  - DAC Side */
+	0x0000,     /* R61 */
+	0x0000,     /* R62 */
+	0x0000,     /* R63 */
+	0x8000,     /* R64  - ADC Control */
+	0x0000,     /* R65 */
+	0x00C0,     /* R66  - ADC Digital Volume L */
+	0x00C0,     /* R67  - ADC Digital Volume R */
+	0x0000,     /* R68  - ADC Divider */
+	0x0000,     /* R69 */
+	0x0040,     /* R70  - ADC LR Rate */
+	0x0000,     /* R71 */
+	0x0303,     /* R72  - Input Control */
+	0x0000,     /* R73  - IN3 Input Control */
+	0x0000,     /* R74  - Mic Bias Control */
+	0x0000,     /* R75 */
+	0x0000,     /* R76  - Output Control */
+	0x0000,     /* R77  - Jack Detect */
+	0x0000,     /* R78  - Anti Pop Control */
+	0x0000,     /* R79 */
+	0x0040,     /* R80  - Left Input Volume */
+	0x0040,     /* R81  - Right Input Volume */
+	0x0000,     /* R82 */
+	0x0000,     /* R83 */
+	0x0000,     /* R84 */
+	0x0000,     /* R85 */
+	0x0000,     /* R86 */
+	0x0000,     /* R87 */
+	0x0800,     /* R88  - Left Mixer Control */
+	0x1000,     /* R89  - Right Mixer Control */
+	0x0000,     /* R90 */
+	0x0000,     /* R91 */
+	0x0000,     /* R92  - OUT3 Mixer Control */
+	0x0000,     /* R93  - OUT4 Mixer Control */
+	0x0000,     /* R94 */
+	0x0000,     /* R95 */
+	0x0000,     /* R96  - Output Left Mixer Volume */
+	0x0000,     /* R97  - Output Right Mixer Volume */
+	0x0000,     /* R98  - Input Mixer Volume L */
+	0x0000,     /* R99  - Input Mixer Volume R */
+	0x0000,     /* R100 - Input Mixer Volume */
+	0x0000,     /* R101 */
+	0x0000,     /* R102 */
+	0x0000,     /* R103 */
+	0x00E4,     /* R104 - LOUT1 Volume */
+	0x00E4,     /* R105 - ROUT1 Volume */
+	0x00E4,     /* R106 - LOUT2 Volume */
+	0x02E4,     /* R107 - ROUT2 Volume */
+	0x0000,     /* R108 */
+	0x0000,     /* R109 */
+	0x0000,     /* R110 */
+	0x0000,     /* R111 - BEEP Volume */
+	0x0A00,     /* R112 - AI Formating */
+	0x0000,     /* R113 - ADC DAC COMP */
+	0x0020,     /* R114 - AI ADC Control */
+	0x0020,     /* R115 - AI DAC Control */
+	0x0000,     /* R116 - AIF Test */
+	0x0000,     /* R117 */
+	0x0000,     /* R118 */
+	0x0000,     /* R119 */
+	0x0000,     /* R120 */
+	0x0000,     /* R121 */
+	0x0000,     /* R122 */
+	0x0000,     /* R123 */
+	0x0000,     /* R124 */
+	0x0000,     /* R125 */
+	0x0000,     /* R126 */
+	0x0000,     /* R127 */
+	0x1FFF,     /* R128 - GPIO Debounce */
+	0x0000,     /* R129 - GPIO Pin pull up Control */
+	0x03FC,     /* R130 - GPIO Pull down Control */
+	0x0000,     /* R131 - GPIO Interrupt Mode */
+	0x0000,     /* R132 */
+	0x0000,     /* R133 - GPIO Control */
+	0x0A7B,     /* R134 - GPIO Configuration (i/o) */
+	0x06FE,     /* R135 - GPIO Pin Polarity / Type */
+	0x0000,     /* R136 */
+	0x0000,     /* R137 */
+	0x0000,     /* R138 */
+	0x0000,     /* R139 */
+	0x1312,     /* R140 - GPIO Function Select 1 */
+	0x1030,     /* R141 - GPIO Function Select 2 */
+	0x2231,     /* R142 - GPIO Function Select 3 */
+	0x0003,     /* R143 - GPIO Function Select 4 */
+	0x0000,     /* R144 - Digitiser Control (1) */
+	0x0002,     /* R145 - Digitiser Control (2) */
+	0x0000,     /* R146 */
+	0x0000,     /* R147 */
+	0x0000,     /* R148 */
+	0x0000,     /* R149 */
+	0x0000,     /* R150 */
+	0x0000,     /* R151 */
+	0x7000,     /* R152 - AUX1 Readback */
+	0x7000,     /* R153 - AUX2 Readback */
+	0x7000,     /* R154 - AUX3 Readback */
+	0x7000,     /* R155 - AUX4 Readback */
+	0x0000,     /* R156 - USB Voltage Readback */
+	0x0000,     /* R157 - LINE Voltage Readback */
+	0x0000,     /* R158 - BATT Voltage Readback */
+	0x0000,     /* R159 - Chip Temp Readback */
+	0x0000,     /* R160 */
+	0x0000,     /* R161 */
+	0x0000,     /* R162 */
+	0x0000,     /* R163 - Generic Comparator Control */
+	0x0000,     /* R164 - Generic comparator 1 */
+	0x0000,     /* R165 - Generic comparator 2 */
+	0x0000,     /* R166 - Generic comparator 3 */
+	0x0000,     /* R167 - Generic comparator 4 */
+	0xA00F,     /* R168 - Battery Charger Control 1 */
+	0x0B06,     /* R169 - Battery Charger Control 2 */
+	0x0000,     /* R170 - Battery Charger Control 3 */
+	0x0000,     /* R171 */
+	0x0000,     /* R172 - Current Sink Driver A */
+	0x0000,     /* R173 - CSA Flash control */
+	0x0000,     /* R174 - Current Sink Driver B */
+	0x0000,     /* R175 - CSB Flash control */
+	0x0000,     /* R176 - DCDC/LDO requested */
+	0x002D,     /* R177 - DCDC Active options */
+	0x0000,     /* R178 - DCDC Sleep options */
+	0x0025,     /* R179 - Power-check comparator */
+	0x000E,     /* R180 - DCDC1 Control */
+	0x0400,     /* R181 - DCDC1 Timeouts */
+	0x1006,     /* R182 - DCDC1 Low Power */
+	0x0018,     /* R183 - DCDC2 Control */
+	0x0000,     /* R184 - DCDC2 Timeouts */
+	0x0000,     /* R185 */
+	0x000E,     /* R186 - DCDC3 Control */
+	0x0400,     /* R187 - DCDC3 Timeouts */
+	0x0006,     /* R188 - DCDC3 Low Power */
+	0x0026,     /* R189 - DCDC4 Control */
+	0x0400,     /* R190 - DCDC4 Timeouts */
+	0x0006,     /* R191 - DCDC4 Low Power */
+	0x0008,     /* R192 - DCDC5 Control */
+	0x0000,     /* R193 - DCDC5 Timeouts */
+	0x0000,     /* R194 */
+	0x0026,     /* R195 - DCDC6 Control */
+	0x0400,     /* R196 - DCDC6 Timeouts */
+	0x0006,     /* R197 - DCDC6 Low Power */
+	0x0000,     /* R198 */
+	0x0003,     /* R199 - Limit Switch Control */
+	0x001C,     /* R200 - LDO1 Control */
+	0x0000,     /* R201 - LDO1 Timeouts */
+	0x001C,     /* R202 - LDO1 Low Power */
+	0x001C,     /* R203 - LDO2 Control */
+	0x0400,     /* R204 - LDO2 Timeouts */
+	0x001C,     /* R205 - LDO2 Low Power */
+	0x001C,     /* R206 - LDO3 Control */
+	0x0400,     /* R207 - LDO3 Timeouts */
+	0x001C,     /* R208 - LDO3 Low Power */
+	0x001F,     /* R209 - LDO4 Control */
+	0x0400,     /* R210 - LDO4 Timeouts */
+	0x001C,     /* R211 - LDO4 Low Power */
+	0x0000,     /* R212 */
+	0x0000,     /* R213 */
+	0x0000,     /* R214 */
+	0x0000,     /* R215 - VCC_FAULT Masks */
+	0x001F,     /* R216 - Main Bandgap Control */
+	0x0000,     /* R217 - OSC Control */
+	0x9000,     /* R218 - RTC Tick Control */
+	0x0000,     /* R219 */
+	0x4000,     /* R220 - RAM BIST 1 */
+	0x0000,     /* R221 */
+	0x0000,     /* R222 */
+	0x0000,     /* R223 */
+	0x0000,     /* R224 */
+	0x0000,     /* R225 - DCDC/LDO status */
+	0x0000,     /* R226 */
+	0x0000,     /* R227 */
+	0x0000,     /* R228 */
+	0x0000,     /* R229 */
+	0xE000,     /* R230 - GPIO Pin Status */
+	0x0000,     /* R231 */
+	0x0000,     /* R232 */
+	0x0000,     /* R233 */
+	0x0000,     /* R234 */
+	0x0000,     /* R235 */
+	0x0000,     /* R236 */
+	0x0000,     /* R237 */
+	0x0000,     /* R238 */
+	0x0000,     /* R239 */
+	0x0000,     /* R240 */
+	0x0000,     /* R241 */
+	0x0000,     /* R242 */
+	0x0000,     /* R243 */
+	0x0000,     /* R244 */
+	0x0000,     /* R245 */
+	0x0000,     /* R246 */
+	0x0000,     /* R247 */
+	0x0000,     /* R248 */
+	0x0000,     /* R249 */
+	0x0000,     /* R250 */
+	0x0000,     /* R251 */
+	0x0000,     /* R252 */
+	0x0000,     /* R253 */
+	0x0000,     /* R254 */
+	0x0000,     /* R255 */
+};
+#endif
+
+/* The register defaults for the config mode used must be compiled in but
+ * due to the impact on kernel size it is possible to disable
+ */
+#ifndef WM8350_HAVE_CONFIG_MODE
+#warning No WM8350 config modes supported - select at least one of the
+#warning MFD_WM8350_CONFIG_MODE_n options from the board driver.
+#endif
+
+/*
+ * Access masks.
+ */
+
+const struct wm8350_reg_access wm8350_reg_io_map[] = {
+	/*  read    write volatile */
+	{ 0xFFFF, 0xFFFF, 0xFFFF }, /* R0   - Reset/ID */
+	{ 0x7CFF, 0x0C00, 0x7FFF }, /* R1   - ID */
+	{ 0x0000, 0x0000, 0x0000 }, /* R2 */
+	{ 0xBE3B, 0xBE3B, 0x8000 }, /* R3   - System Control 1 */
+	{ 0xFCF7, 0xFCF7, 0xF800 }, /* R4   - System Control 2 */
+	{ 0x80FF, 0x80FF, 0x8000 }, /* R5   - System Hibernate */
+	{ 0xFB0E, 0xFB0E, 0x0000 }, /* R6   - Interface Control */
+	{ 0x0000, 0x0000, 0x0000 }, /* R7 */
+	{ 0xE537, 0xE537, 0xFFFF }, /* R8   - Power mgmt (1) */
+	{ 0x0FF3, 0x0FF3, 0xFFFF }, /* R9   - Power mgmt (2) */
+	{ 0x008F, 0x008F, 0xFFFF }, /* R10  - Power mgmt (3) */
+	{ 0x6D3C, 0x6D3C, 0xFFFF }, /* R11  - Power mgmt (4) */
+	{ 0x1F8F, 0x1F8F, 0xFFFF }, /* R12  - Power mgmt (5) */
+	{ 0x8F3F, 0x8F3F, 0xFFFF }, /* R13  - Power mgmt (6) */
+	{ 0x0003, 0x0003, 0xFFFF }, /* R14  - Power mgmt (7) */
+	{ 0x0000, 0x0000, 0x0000 }, /* R15 */
+	{ 0x7F7F, 0x7F7F, 0xFFFF }, /* R16  - RTC Seconds/Minutes */
+	{ 0x073F, 0x073F, 0xFFFF }, /* R17  - RTC Hours/Day */
+	{ 0x1F3F, 0x1F3F, 0xFFFF }, /* R18  - RTC Date/Month */
+	{ 0x3FFF, 0x00FF, 0xFFFF }, /* R19  - RTC Year */
+	{ 0x7F7F, 0x7F7F, 0x0000 }, /* R20  - Alarm Seconds/Minutes */
+	{ 0x0F3F, 0x0F3F, 0x0000 }, /* R21  - Alarm Hours/Day */
+	{ 0x1F3F, 0x1F3F, 0x0000 }, /* R22  - Alarm Date/Month */
+	{ 0xEF7F, 0xEA7F, 0xFFFF }, /* R23  - RTC Time Control */
+	{ 0x3BFF, 0x0000, 0xFFFF }, /* R24  - System Interrupts */
+	{ 0xFEE7, 0x0000, 0xFFFF }, /* R25  - Interrupt Status 1 */
+	{ 0x35FF, 0x0000, 0xFFFF }, /* R26  - Interrupt Status 2 */
+	{ 0x0F3F, 0x0000, 0xFFFF }, /* R27  - Power Up Interrupt Status */
+	{ 0x0F3F, 0x0000, 0xFFFF }, /* R28  - Under Voltage Interrupt status */
+	{ 0x8000, 0x0000, 0xFFFF }, /* R29  - Over Current Interrupt status */
+	{ 0x1FFF, 0x0000, 0xFFFF }, /* R30  - GPIO Interrupt Status */
+	{ 0xEF7F, 0x0000, 0xFFFF }, /* R31  - Comparator Interrupt Status */
+	{ 0x3FFF, 0x3FFF, 0x0000 }, /* R32  - System Interrupts Mask */
+	{ 0xFEE7, 0xFEE7, 0x0000 }, /* R33  - Interrupt Status 1 Mask */
+	{ 0xF5FF, 0xF5FF, 0x0000 }, /* R34  - Interrupt Status 2 Mask */
+	{ 0x0F3F, 0x0F3F, 0x0000 }, /* R35  - Power Up Interrupt Status Mask */
+	{ 0x0F3F, 0x0F3F, 0x0000 }, /* R36  - Under Voltage Int status Mask */
+	{ 0x8000, 0x8000, 0x0000 }, /* R37  - Over Current Int status Mask */
+	{ 0x1FFF, 0x1FFF, 0x0000 }, /* R38  - GPIO Interrupt Status Mask */
+	{ 0xEF7F, 0xEF7F, 0x0000 }, /* R39  - Comparator IntStatus Mask */
+	{ 0xC9F7, 0xC9F7, 0xFFFF }, /* R40  - Clock Control 1 */
+	{ 0x8001, 0x8001, 0x0000 }, /* R41  - Clock Control 2 */
+	{ 0xFFF7, 0xFFF7, 0xFFFF }, /* R42  - FLL Control 1 */
+	{ 0xFBFF, 0xFBFF, 0x0000 }, /* R43  - FLL Control 2 */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R44  - FLL Control 3 */
+	{ 0x0033, 0x0033, 0x0000 }, /* R45  - FLL Control 4 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R46 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R47 */
+	{ 0x3033, 0x3033, 0x0000 }, /* R48  - DAC Control */
+	{ 0x0000, 0x0000, 0x0000 }, /* R49 */
+	{ 0x81FF, 0x81FF, 0xFFFF }, /* R50  - DAC Digital Volume L */
+	{ 0x81FF, 0x81FF, 0xFFFF }, /* R51  - DAC Digital Volume R */
+	{ 0x0000, 0x0000, 0x0000 }, /* R52 */
+	{ 0x0FFF, 0x0FFF, 0xFFFF }, /* R53  - DAC LR Rate */
+	{ 0x0017, 0x0017, 0x0000 }, /* R54  - DAC Clock Control */
+	{ 0x0000, 0x0000, 0x0000 }, /* R55 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R56 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R57 */
+	{ 0x4000, 0x4000, 0x0000 }, /* R58  - DAC Mute */
+	{ 0x7000, 0x7000, 0x0000 }, /* R59  - DAC Mute Volume */
+	{ 0x3C00, 0x3C00, 0x0000 }, /* R60  - DAC Side */
+	{ 0x0000, 0x0000, 0x0000 }, /* R61 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R62 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R63 */
+	{ 0x8303, 0x8303, 0xFFFF }, /* R64  - ADC Control */
+	{ 0x0000, 0x0000, 0x0000 }, /* R65 */
+	{ 0x81FF, 0x81FF, 0xFFFF }, /* R66  - ADC Digital Volume L */
+	{ 0x81FF, 0x81FF, 0xFFFF }, /* R67  - ADC Digital Volume R */
+	{ 0x0FFF, 0x0FFF, 0x0000 }, /* R68  - ADC Divider */
+	{ 0x0000, 0x0000, 0x0000 }, /* R69 */
+	{ 0x0FFF, 0x0FFF, 0xFFFF }, /* R70  - ADC LR Rate */
+	{ 0x0000, 0x0000, 0x0000 }, /* R71 */
+	{ 0x0707, 0x0707, 0xFFFF }, /* R72  - Input Control */
+	{ 0xC0C0, 0xC0C0, 0xFFFF }, /* R73  - IN3 Input Control */
+	{ 0xC09F, 0xC09F, 0xFFFF }, /* R74  - Mic Bias Control */
+	{ 0x0000, 0x0000, 0x0000 }, /* R75 */
+	{ 0x0F15, 0x0F15, 0xFFFF }, /* R76  - Output Control */
+	{ 0xC000, 0xC000, 0xFFFF }, /* R77  - Jack Detect */
+	{ 0x03FF, 0x03FF, 0x0000 }, /* R78  - Anti Pop Control */
+	{ 0x0000, 0x0000, 0x0000 }, /* R79 */
+	{ 0xE1FC, 0xE1FC, 0x8000 }, /* R80  - Left Input Volume */
+	{ 0xE1FC, 0xE1FC, 0x8000 }, /* R81  - Right Input Volume */
+	{ 0x0000, 0x0000, 0x0000 }, /* R82 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R83 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R84 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R85 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R86 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R87 */
+	{ 0x9807, 0x9807, 0xFFFF }, /* R88  - Left Mixer Control */
+	{ 0x980B, 0x980B, 0xFFFF }, /* R89  - Right Mixer Control */
+	{ 0x0000, 0x0000, 0x0000 }, /* R90 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R91 */
+	{ 0x8909, 0x8909, 0xFFFF }, /* R92  - OUT3 Mixer Control */
+	{ 0x9E07, 0x9E07, 0xFFFF }, /* R93  - OUT4 Mixer Control */
+	{ 0x0000, 0x0000, 0x0000 }, /* R94 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R95 */
+	{ 0x0EEE, 0x0EEE, 0x0000 }, /* R96  - Output Left Mixer Volume */
+	{ 0xE0EE, 0xE0EE, 0x0000 }, /* R97  - Output Right Mixer Volume */
+	{ 0x0E0F, 0x0E0F, 0x0000 }, /* R98  - Input Mixer Volume L */
+	{ 0xE0E1, 0xE0E1, 0x0000 }, /* R99  - Input Mixer Volume R */
+	{ 0x800E, 0x800E, 0x0000 }, /* R100 - Input Mixer Volume */
+	{ 0x0000, 0x0000, 0x0000 }, /* R101 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R102 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R103 */
+	{ 0xE1FC, 0xE1FC, 0xFFFF }, /* R104 - LOUT1 Volume */
+	{ 0xE1FC, 0xE1FC, 0xFFFF }, /* R105 - ROUT1 Volume */
+	{ 0xE1FC, 0xE1FC, 0xFFFF }, /* R106 - LOUT2 Volume */
+	{ 0xE7FC, 0xE7FC, 0xFFFF }, /* R107 - ROUT2 Volume */
+	{ 0x0000, 0x0000, 0x0000 }, /* R108 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R109 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R110 */
+	{ 0x80E0, 0x80E0, 0xFFFF }, /* R111 - BEEP Volume */
+	{ 0xBF00, 0xBF00, 0x0000 }, /* R112 - AI Formating */
+	{ 0x00F1, 0x00F1, 0x0000 }, /* R113 - ADC DAC COMP */
+	{ 0x00F8, 0x00F8, 0x0000 }, /* R114 - AI ADC Control */
+	{ 0x40FB, 0x40FB, 0x0000 }, /* R115 - AI DAC Control */
+	{ 0x7C30, 0x7C30, 0x0000 }, /* R116 - AIF Test */
+	{ 0x0000, 0x0000, 0x0000 }, /* R117 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R118 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R119 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R120 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R121 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R122 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R123 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R124 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R125 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R126 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R127 */
+	{ 0x1FFF, 0x1FFF, 0x0000 }, /* R128 - GPIO Debounce */
+	{ 0x1FFF, 0x1FFF, 0x0000 }, /* R129 - GPIO Pin pull up Control */
+	{ 0x1FFF, 0x1FFF, 0x0000 }, /* R130 - GPIO Pull down Control */
+	{ 0x1FFF, 0x1FFF, 0x0000 }, /* R131 - GPIO Interrupt Mode */
+	{ 0x0000, 0x0000, 0x0000 }, /* R132 */
+	{ 0x00C0, 0x00C0, 0x0000 }, /* R133 - GPIO Control */
+	{ 0x1FFF, 0x1FFF, 0x0000 }, /* R134 - GPIO Configuration (i/o) */
+	{ 0x1FFF, 0x1FFF, 0x0000 }, /* R135 - GPIO Pin Polarity / Type */
+	{ 0x0000, 0x0000, 0x0000 }, /* R136 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R137 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R138 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R139 */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R140 - GPIO Function Select 1 */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R141 - GPIO Function Select 2 */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R142 - GPIO Function Select 3 */
+	{ 0x000F, 0x000F, 0x0000 }, /* R143 - GPIO Function Select 4 */
+	{ 0xF0FF, 0xF0FF, 0xA000 }, /* R144 - Digitiser Control (1) */
+	{ 0x3707, 0x3707, 0x0000 }, /* R145 - Digitiser Control (2) */
+	{ 0x0000, 0x0000, 0x0000 }, /* R146 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R147 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R148 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R149 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R150 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R151 */
+	{ 0x7FFF, 0x7000, 0xFFFF }, /* R152 - AUX1 Readback */
+	{ 0x7FFF, 0x7000, 0xFFFF }, /* R153 - AUX2 Readback */
+	{ 0x7FFF, 0x7000, 0xFFFF }, /* R154 - AUX3 Readback */
+	{ 0x7FFF, 0x7000, 0xFFFF }, /* R155 - AUX4 Readback */
+	{ 0x0FFF, 0x0000, 0xFFFF }, /* R156 - USB Voltage Readback */
+	{ 0x0FFF, 0x0000, 0xFFFF }, /* R157 - LINE Voltage Readback */
+	{ 0x0FFF, 0x0000, 0xFFFF }, /* R158 - BATT Voltage Readback */
+	{ 0x0FFF, 0x0000, 0xFFFF }, /* R159 - Chip Temp Readback */
+	{ 0x0000, 0x0000, 0x0000 }, /* R160 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R161 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R162 */
+	{ 0x000F, 0x000F, 0x0000 }, /* R163 - Generic Comparator Control */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R164 - Generic comparator 1 */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R165 - Generic comparator 2 */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R166 - Generic comparator 3 */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R167 - Generic comparator 4 */
+	{ 0xBFFF, 0xBFFF, 0x8000 }, /* R168 - Battery Charger Control 1 */
+	{ 0xFFFF, 0x4FFF, 0xB000 }, /* R169 - Battery Charger Control 2 */
+	{ 0x007F, 0x007F, 0x0000 }, /* R170 - Battery Charger Control 3 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R171 */
+	{ 0x903F, 0x903F, 0xFFFF }, /* R172 - Current Sink Driver A */
+	{ 0xE333, 0xE333, 0xFFFF }, /* R173 - CSA Flash control */
+	{ 0x903F, 0x903F, 0xFFFF }, /* R174 - Current Sink Driver B */
+	{ 0xE333, 0xE333, 0xFFFF }, /* R175 - CSB Flash control */
+	{ 0x8F3F, 0x8F3F, 0xFFFF }, /* R176 - DCDC/LDO requested */
+	{ 0x332D, 0x332D, 0x0000 }, /* R177 - DCDC Active options */
+	{ 0x002D, 0x002D, 0x0000 }, /* R178 - DCDC Sleep options */
+	{ 0x5177, 0x5177, 0x8000 }, /* R179 - Power-check comparator */
+	{ 0x047F, 0x047F, 0x0000 }, /* R180 - DCDC1 Control */
+	{ 0xFFC0, 0xFFC0, 0x0000 }, /* R181 - DCDC1 Timeouts */
+	{ 0x737F, 0x737F, 0x0000 }, /* R182 - DCDC1 Low Power */
+	{ 0x535B, 0x535B, 0x0000 }, /* R183 - DCDC2 Control */
+	{ 0xFFC0, 0xFFC0, 0x0000 }, /* R184 - DCDC2 Timeouts */
+	{ 0x0000, 0x0000, 0x0000 }, /* R185 */
+	{ 0x047F, 0x047F, 0x0000 }, /* R186 - DCDC3 Control */
+	{ 0xFFC0, 0xFFC0, 0x0000 }, /* R187 - DCDC3 Timeouts */
+	{ 0x737F, 0x737F, 0x0000 }, /* R188 - DCDC3 Low Power */
+	{ 0x047F, 0x047F, 0x0000 }, /* R189 - DCDC4 Control */
+	{ 0xFFC0, 0xFFC0, 0x0000 }, /* R190 - DCDC4 Timeouts */
+	{ 0x737F, 0x737F, 0x0000 }, /* R191 - DCDC4 Low Power */
+	{ 0x535B, 0x535B, 0x0000 }, /* R192 - DCDC5 Control */
+	{ 0xFFC0, 0xFFC0, 0x0000 }, /* R193 - DCDC5 Timeouts */
+	{ 0x0000, 0x0000, 0x0000 }, /* R194 */
+	{ 0x047F, 0x047F, 0x0000 }, /* R195 - DCDC6 Control */
+	{ 0xFFC0, 0xFFC0, 0x0000 }, /* R196 - DCDC6 Timeouts */
+	{ 0x737F, 0x737F, 0x0000 }, /* R197 - DCDC6 Low Power */
+	{ 0x0000, 0x0000, 0x0000 }, /* R198 */
+	{ 0xFFD3, 0xFFD3, 0x0000 }, /* R199 - Limit Switch Control */
+	{ 0x441F, 0x441F, 0x0000 }, /* R200 - LDO1 Control */
+	{ 0xFFC0, 0xFFC0, 0x0000 }, /* R201 - LDO1 Timeouts */
+	{ 0x331F, 0x331F, 0x0000 }, /* R202 - LDO1 Low Power */
+	{ 0x441F, 0x441F, 0x0000 }, /* R203 - LDO2 Control */
+	{ 0xFFC0, 0xFFC0, 0x0000 }, /* R204 - LDO2 Timeouts */
+	{ 0x331F, 0x331F, 0x0000 }, /* R205 - LDO2 Low Power */
+	{ 0x441F, 0x441F, 0x0000 }, /* R206 - LDO3 Control */
+	{ 0xFFC0, 0xFFC0, 0x0000 }, /* R207 - LDO3 Timeouts */
+	{ 0x331F, 0x331F, 0x0000 }, /* R208 - LDO3 Low Power */
+	{ 0x441F, 0x441F, 0x0000 }, /* R209 - LDO4 Control */
+	{ 0xFFC0, 0xFFC0, 0x0000 }, /* R210 - LDO4 Timeouts */
+	{ 0x331F, 0x331F, 0x0000 }, /* R211 - LDO4 Low Power */
+	{ 0x0000, 0x0000, 0x0000 }, /* R212 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R213 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R214 */
+	{ 0x8F3F, 0x8F3F, 0x0000 }, /* R215 - VCC_FAULT Masks */
+	{ 0xFF3F, 0xE03F, 0x0000 }, /* R216 - Main Bandgap Control */
+	{ 0xEF2F, 0xE02F, 0x0000 }, /* R217 - OSC Control */
+	{ 0xF3FF, 0xB3FF, 0xc000 }, /* R218 - RTC Tick Control */
+	{ 0xFFFF, 0xFFFF, 0xFFFF }, /* R219 */
+	{ 0x09FF, 0x01FF, 0x0000 }, /* R220 - RAM BIST 1 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R221 */
+	{ 0xFFFF, 0xFFFF, 0xFFFF }, /* R222 */
+	{ 0xFFFF, 0xFFFF, 0xFFFF }, /* R223 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R224 */
+	{ 0x8F3F, 0x0000, 0xFFFF }, /* R225 - DCDC/LDO status */
+	{ 0x0000, 0x0000, 0x0000 }, /* R226 */
+	{ 0x0000, 0x0000, 0xFFFF }, /* R227 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R228 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R229 */
+	{ 0xFFFF, 0x1FFF, 0xFFFF }, /* R230 - GPIO Pin Status */
+	{ 0xFFFF, 0x1FFF, 0xFFFF }, /* R231 */
+	{ 0xFFFF, 0x1FFF, 0xFFFF }, /* R232 */
+	{ 0xFFFF, 0x1FFF, 0xFFFF }, /* R233 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R234 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R235 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R236 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R237 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R238 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R239 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R240 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R241 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R242 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R243 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R244 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R245 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R246 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R247 */
+	{ 0xFFFF, 0x0010, 0xFFFF }, /* R248 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R249 */
+	{ 0xFFFF, 0x0010, 0xFFFF }, /* R250 */
+	{ 0xFFFF, 0x0010, 0xFFFF }, /* R251 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R252 */
+	{ 0xFFFF, 0x0010, 0xFFFF }, /* R253 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R254 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R255 */
+};
diff --git a/drivers/mfd/wm8400-core.c b/drivers/mfd/wm8400-core.c
new file mode 100644
index 0000000..6a0cedb
--- /dev/null
+++ b/drivers/mfd/wm8400-core.c
@@ -0,0 +1,455 @@
+/*
+ * Core driver for WM8400.
+ *
+ * Copyright 2008 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.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/bug.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/mfd/wm8400-private.h>
+#include <linux/mfd/wm8400-audio.h>
+
+static struct {
+	u16  readable;    /* Mask of readable bits */
+	u16  writable;    /* Mask of writable bits */
+	u16  vol;         /* Mask of volatile bits */
+	int  is_codec;    /* Register controlled by codec reset */
+	u16  default_val; /* Value on reset */
+} reg_data[] = {
+	{ 0xFFFF, 0xFFFF, 0x0000, 0, 0x6172 }, /* R0 */
+	{ 0x7000, 0x0000, 0x8000, 0, 0x0000 }, /* R1 */
+	{ 0xFF17, 0xFF17, 0x0000, 0, 0x0000 }, /* R2 */
+	{ 0xEBF3, 0xEBF3, 0x0000, 1, 0x6000 }, /* R3 */
+	{ 0x3CF3, 0x3CF3, 0x0000, 1, 0x0000 }, /* R4  */
+	{ 0xF1F8, 0xF1F8, 0x0000, 1, 0x4050 }, /* R5  */
+	{ 0xFC1F, 0xFC1F, 0x0000, 1, 0x4000 }, /* R6  */
+	{ 0xDFDE, 0xDFDE, 0x0000, 1, 0x01C8 }, /* R7  */
+	{ 0xFCFC, 0xFCFC, 0x0000, 1, 0x0000 }, /* R8  */
+	{ 0xEFFF, 0xEFFF, 0x0000, 1, 0x0040 }, /* R9  */
+	{ 0xEFFF, 0xEFFF, 0x0000, 1, 0x0040 }, /* R10 */
+	{ 0x27F7, 0x27F7, 0x0000, 1, 0x0004 }, /* R11 */
+	{ 0x01FF, 0x01FF, 0x0000, 1, 0x00C0 }, /* R12 */
+	{ 0x01FF, 0x01FF, 0x0000, 1, 0x00C0 }, /* R13 */
+	{ 0x1FEF, 0x1FEF, 0x0000, 1, 0x0000 }, /* R14 */
+	{ 0x0163, 0x0163, 0x0000, 1, 0x0100 }, /* R15 */
+	{ 0x01FF, 0x01FF, 0x0000, 1, 0x00C0 }, /* R16 */
+	{ 0x01FF, 0x01FF, 0x0000, 1, 0x00C0 }, /* R17 */
+	{ 0x1FFF, 0x0FFF, 0x0000, 1, 0x0000 }, /* R18 */
+	{ 0xFFFF, 0xFFFF, 0x0000, 1, 0x1000 }, /* R19 */
+	{ 0xFFFF, 0xFFFF, 0x0000, 1, 0x1010 }, /* R20 */
+	{ 0xFFFF, 0xFFFF, 0x0000, 1, 0x1010 }, /* R21 */
+	{ 0x0FDD, 0x0FDD, 0x0000, 1, 0x8000 }, /* R22 */
+	{ 0x1FFF, 0x1FFF, 0x0000, 1, 0x0800 }, /* R23 */
+	{ 0x0000, 0x01DF, 0x0000, 1, 0x008B }, /* R24 */
+	{ 0x0000, 0x01DF, 0x0000, 1, 0x008B }, /* R25 */
+	{ 0x0000, 0x01DF, 0x0000, 1, 0x008B }, /* R26 */
+	{ 0x0000, 0x01DF, 0x0000, 1, 0x008B }, /* R27 */
+	{ 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R28 */
+	{ 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R29 */
+	{ 0x0000, 0x0077, 0x0000, 1, 0x0066 }, /* R30 */
+	{ 0x0000, 0x0033, 0x0000, 1, 0x0022 }, /* R31 */
+	{ 0x0000, 0x01FF, 0x0000, 1, 0x0079 }, /* R32 */
+	{ 0x0000, 0x01FF, 0x0000, 1, 0x0079 }, /* R33 */
+	{ 0x0000, 0x0003, 0x0000, 1, 0x0003 }, /* R34 */
+	{ 0x0000, 0x01FF, 0x0000, 1, 0x0003 }, /* R35 */
+	{ 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R36 */
+	{ 0x0000, 0x003F, 0x0000, 1, 0x0100 }, /* R37 */
+	{ 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R38 */
+	{ 0x0000, 0x000F, 0x0000, 0, 0x0000 }, /* R39 */
+	{ 0x0000, 0x00FF, 0x0000, 1, 0x0000 }, /* R40 */
+	{ 0x0000, 0x01B7, 0x0000, 1, 0x0000 }, /* R41 */
+	{ 0x0000, 0x01B7, 0x0000, 1, 0x0000 }, /* R42 */
+	{ 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R43 */
+	{ 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R44 */
+	{ 0x0000, 0x00FD, 0x0000, 1, 0x0000 }, /* R45 */
+	{ 0x0000, 0x00FD, 0x0000, 1, 0x0000 }, /* R46 */
+	{ 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R47 */
+	{ 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R48 */
+	{ 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R49 */
+	{ 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R50 */
+	{ 0x0000, 0x01B3, 0x0000, 1, 0x0180 }, /* R51 */
+	{ 0x0000, 0x0077, 0x0000, 1, 0x0000 }, /* R52 */
+	{ 0x0000, 0x0077, 0x0000, 1, 0x0000 }, /* R53 */
+	{ 0x0000, 0x00FF, 0x0000, 1, 0x0000 }, /* R54 */
+	{ 0x0000, 0x0001, 0x0000, 1, 0x0000 }, /* R55 */
+	{ 0x0000, 0x003F, 0x0000, 1, 0x0000 }, /* R56 */
+	{ 0x0000, 0x004F, 0x0000, 1, 0x0000 }, /* R57 */
+	{ 0x0000, 0x00FD, 0x0000, 1, 0x0000 }, /* R58 */
+	{ 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R59 */
+	{ 0x1FFF, 0x1FFF, 0x0000, 1, 0x0000 }, /* R60 */
+	{ 0xFFFF, 0xFFFF, 0x0000, 1, 0x0000 }, /* R61 */
+	{ 0x03FF, 0x03FF, 0x0000, 1, 0x0000 }, /* R62 */
+	{ 0x007F, 0x007F, 0x0000, 1, 0x0000 }, /* R63 */
+	{ 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R64 */
+	{ 0xDFFF, 0xDFFF, 0x0000, 0, 0x0000 }, /* R65 */
+	{ 0xDFFF, 0xDFFF, 0x0000, 0, 0x0000 }, /* R66 */
+	{ 0xDFFF, 0xDFFF, 0x0000, 0, 0x0000 }, /* R67 */
+	{ 0xDFFF, 0xDFFF, 0x0000, 0, 0x0000 }, /* R68 */
+	{ 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R69 */
+	{ 0xFFFF, 0xFFFF, 0x0000, 0, 0x4400 }, /* R70 */
+	{ 0x23FF, 0x23FF, 0x0000, 0, 0x0000 }, /* R71 */
+	{ 0xFFFF, 0xFFFF, 0x0000, 0, 0x4400 }, /* R72 */
+	{ 0x23FF, 0x23FF, 0x0000, 0, 0x0000 }, /* R73 */
+	{ 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R74 */
+	{ 0x000E, 0x000E, 0x0000, 0, 0x0008 }, /* R75 */
+	{ 0xE00F, 0xE00F, 0x0000, 0, 0x0000 }, /* R76 */
+	{ 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R77 */
+	{ 0x03C0, 0x03C0, 0x0000, 0, 0x02C0 }, /* R78 */
+	{ 0xFFFF, 0x0000, 0xffff, 0, 0x0000 }, /* R79 */
+	{ 0xFFFF, 0xFFFF, 0x0000, 0, 0x0000 }, /* R80 */
+	{ 0xFFFF, 0x0000, 0xffff, 0, 0x0000 }, /* R81 */
+	{ 0x2BFF, 0x0000, 0xffff, 0, 0x0000 }, /* R82 */
+	{ 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R83 */
+	{ 0x80FF, 0x80FF, 0x0000, 0, 0x00ff }, /* R84 */
+};
+
+static int wm8400_read(struct wm8400 *wm8400, u8 reg, int num_regs, u16 *dest)
+{
+	int i, ret = 0;
+
+	BUG_ON(reg + num_regs - 1 > ARRAY_SIZE(wm8400->reg_cache));
+
+	/* If there are any volatile reads then read back the entire block */
+	for (i = reg; i < reg + num_regs; i++)
+		if (reg_data[i].vol) {
+			ret = wm8400->read_dev(wm8400->io_data, reg,
+					       num_regs, dest);
+			if (ret != 0)
+				return ret;
+			for (i = 0; i < num_regs; i++)
+				dest[i] = be16_to_cpu(dest[i]);
+
+			return 0;
+		}
+
+	/* Otherwise use the cache */
+	memcpy(dest, &wm8400->reg_cache[reg], num_regs * sizeof(u16));
+
+	return 0;
+}
+
+static int wm8400_write(struct wm8400 *wm8400, u8 reg, int num_regs,
+			u16 *src)
+{
+	int ret, i;
+
+	BUG_ON(reg + num_regs - 1 > ARRAY_SIZE(wm8400->reg_cache));
+
+	for (i = 0; i < num_regs; i++) {
+		BUG_ON(!reg_data[reg + i].writable);
+		wm8400->reg_cache[reg + i] = src[i];
+		src[i] = cpu_to_be16(src[i]);
+	}
+
+	/* Do the actual I/O */
+	ret = wm8400->write_dev(wm8400->io_data, reg, num_regs, src);
+	if (ret != 0)
+		return -EIO;
+
+	return 0;
+}
+
+/**
+ * wm8400_reg_read - Single register read
+ *
+ * @wm8400: Pointer to wm8400 control structure
+ * @reg:    Register to read
+ *
+ * @return  Read value
+ */
+u16 wm8400_reg_read(struct wm8400 *wm8400, u8 reg)
+{
+	u16 val;
+
+	mutex_lock(&wm8400->io_lock);
+
+	wm8400_read(wm8400, reg, 1, &val);
+
+	mutex_unlock(&wm8400->io_lock);
+
+	return val;
+}
+EXPORT_SYMBOL_GPL(wm8400_reg_read);
+
+int wm8400_block_read(struct wm8400 *wm8400, u8 reg, int count, u16 *data)
+{
+	int ret;
+
+	mutex_lock(&wm8400->io_lock);
+
+	ret = wm8400_read(wm8400, reg, count, data);
+
+	mutex_unlock(&wm8400->io_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wm8400_block_read);
+
+/**
+ * wm8400_set_bits - Bitmask write
+ *
+ * @wm8400: Pointer to wm8400 control structure
+ * @reg:    Register to access
+ * @mask:   Mask of bits to change
+ * @val:    Value to set for masked bits
+ */
+int wm8400_set_bits(struct wm8400 *wm8400, u8 reg, u16 mask, u16 val)
+{
+	u16 tmp;
+	int ret;
+
+	mutex_lock(&wm8400->io_lock);
+
+	ret = wm8400_read(wm8400, reg, 1, &tmp);
+	tmp = (tmp & ~mask) | val;
+	if (ret == 0)
+		ret = wm8400_write(wm8400, reg, 1, &tmp);
+
+	mutex_unlock(&wm8400->io_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wm8400_set_bits);
+
+/**
+ * wm8400_reset_codec_reg_cache - Reset cached codec registers to
+ * their default values.
+ */
+void wm8400_reset_codec_reg_cache(struct wm8400 *wm8400)
+{
+	int i;
+
+	mutex_lock(&wm8400->io_lock);
+
+	/* Reset all codec registers to their initial value */
+	for (i = 0; i < ARRAY_SIZE(wm8400->reg_cache); i++)
+		if (reg_data[i].is_codec)
+			wm8400->reg_cache[i] = reg_data[i].default_val;
+
+	mutex_unlock(&wm8400->io_lock);
+}
+EXPORT_SYMBOL_GPL(wm8400_reset_codec_reg_cache);
+
+/*
+ * wm8400_init - Generic initialisation
+ *
+ * The WM8400 can be configured as either an I2C or SPI device.  Probe
+ * functions for each bus set up the accessors then call into this to
+ * set up the device itself.
+ */
+static int wm8400_init(struct wm8400 *wm8400,
+		       struct wm8400_platform_data *pdata)
+{
+	u16 reg;
+	int ret, i;
+
+	mutex_init(&wm8400->io_lock);
+
+	wm8400->dev->driver_data = wm8400;
+
+	/* Check that this is actually a WM8400 */
+	ret = wm8400->read_dev(wm8400->io_data, WM8400_RESET_ID, 1, &reg);
+	if (ret != 0) {
+		dev_err(wm8400->dev, "Chip ID register read failed\n");
+		return -EIO;
+	}
+	if (be16_to_cpu(reg) != reg_data[WM8400_RESET_ID].default_val) {
+		dev_err(wm8400->dev, "Device is not a WM8400, ID is %x\n",
+			be16_to_cpu(reg));
+		return -ENODEV;
+	}
+
+	/* We don't know what state the hardware is in and since this
+	 * is a PMIC we can't reset it safely so initialise the register
+	 * cache from the hardware.
+	 */
+	ret = wm8400->read_dev(wm8400->io_data, 0,
+			       ARRAY_SIZE(wm8400->reg_cache),
+			       wm8400->reg_cache);
+	if (ret != 0) {
+		dev_err(wm8400->dev, "Register cache read failed\n");
+		return -EIO;
+	}
+	for (i = 0; i < ARRAY_SIZE(wm8400->reg_cache); i++)
+		wm8400->reg_cache[i] = be16_to_cpu(wm8400->reg_cache[i]);
+
+	/* If the codec is in reset use hard coded values */
+	if (!(wm8400->reg_cache[WM8400_POWER_MANAGEMENT_1] & WM8400_CODEC_ENA))
+		for (i = 0; i < ARRAY_SIZE(wm8400->reg_cache); i++)
+			if (reg_data[i].is_codec)
+				wm8400->reg_cache[i] = reg_data[i].default_val;
+
+	ret = wm8400_read(wm8400, WM8400_ID, 1, &reg);
+	if (ret != 0) {
+		dev_err(wm8400->dev, "ID register read failed: %d\n", ret);
+		return ret;
+	}
+	reg = (reg & WM8400_CHIP_REV_MASK) >> WM8400_CHIP_REV_SHIFT;
+	dev_info(wm8400->dev, "WM8400 revision %x\n", reg);
+
+	if (pdata && pdata->platform_init) {
+		ret = pdata->platform_init(wm8400->dev);
+		if (ret != 0)
+			dev_err(wm8400->dev, "Platform init failed: %d\n",
+				ret);
+	} else
+		dev_warn(wm8400->dev, "No platform initialisation supplied\n");
+
+	return ret;
+}
+
+static void wm8400_release(struct wm8400 *wm8400)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(wm8400->regulators); i++)
+		if (wm8400->regulators[i].name)
+			platform_device_unregister(&wm8400->regulators[i]);
+}
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+static int wm8400_i2c_read(void *io_data, char reg, int count, u16 *dest)
+{
+	struct i2c_client *i2c = io_data;
+	struct i2c_msg xfer[2];
+	int ret;
+
+	/* Write register */
+	xfer[0].addr = i2c->addr;
+	xfer[0].flags = 0;
+	xfer[0].len = 1;
+	xfer[0].buf = &reg;
+
+	/* Read data */
+	xfer[1].addr = i2c->addr;
+	xfer[1].flags = I2C_M_RD;
+	xfer[1].len = count * sizeof(u16);
+	xfer[1].buf = (u8 *)dest;
+
+	ret = i2c_transfer(i2c->adapter, xfer, 2);
+	if (ret == 2)
+		ret = 0;
+	else if (ret >= 0)
+		ret = -EIO;
+
+	return ret;
+}
+
+static int wm8400_i2c_write(void *io_data, char reg, int count, const u16 *src)
+{
+	struct i2c_client *i2c = io_data;
+	u8 *msg;
+	int ret;
+
+	/* We add 1 byte for device register - ideally I2C would gather. */
+	msg = kmalloc((count * sizeof(u16)) + 1, GFP_KERNEL);
+	if (msg == NULL)
+		return -ENOMEM;
+
+	msg[0] = reg;
+	memcpy(&msg[1], src, count * sizeof(u16));
+
+	ret = i2c_master_send(i2c, msg, (count * sizeof(u16)) + 1);
+
+	if (ret == (count * 2) + 1)
+		ret = 0;
+	else if (ret >= 0)
+		ret = -EIO;
+
+	kfree(msg);
+
+	return ret;
+}
+
+static int wm8400_i2c_probe(struct i2c_client *i2c,
+			    const struct i2c_device_id *id)
+{
+	struct wm8400 *wm8400;
+	int ret;
+
+	wm8400 = kzalloc(sizeof(struct wm8400), GFP_KERNEL);
+	if (wm8400 == NULL) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	wm8400->io_data = i2c;
+	wm8400->read_dev = wm8400_i2c_read;
+	wm8400->write_dev = wm8400_i2c_write;
+	wm8400->dev = &i2c->dev;
+	i2c_set_clientdata(i2c, wm8400);
+
+	ret = wm8400_init(wm8400, i2c->dev.platform_data);
+	if (ret != 0)
+		goto struct_err;
+
+	return 0;
+
+struct_err:
+	i2c_set_clientdata(i2c, NULL);
+	kfree(wm8400);
+err:
+	return ret;
+}
+
+static int wm8400_i2c_remove(struct i2c_client *i2c)
+{
+	struct wm8400 *wm8400 = i2c_get_clientdata(i2c);
+
+	wm8400_release(wm8400);
+	i2c_set_clientdata(i2c, NULL);
+	kfree(wm8400);
+
+	return 0;
+}
+
+static const struct i2c_device_id wm8400_i2c_id[] = {
+       { "wm8400", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8400_i2c_id);
+
+static struct i2c_driver wm8400_i2c_driver = {
+	.driver = {
+		.name = "WM8400",
+		.owner = THIS_MODULE,
+	},
+	.probe    = wm8400_i2c_probe,
+	.remove   = wm8400_i2c_remove,
+	.id_table = wm8400_i2c_id,
+};
+#endif
+
+static int __init wm8400_module_init(void)
+{
+	int ret = -ENODEV;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	ret = i2c_add_driver(&wm8400_i2c_driver);
+	if (ret != 0)
+		pr_err("Failed to register I2C driver: %d\n", ret);
+#endif
+
+	return ret;
+}
+module_init(wm8400_module_init);
+
+static void __exit wm8400_module_exit(void)
+{
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	i2c_del_driver(&wm8400_i2c_driver);
+#endif
+}
+module_exit(wm8400_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
diff --git a/drivers/net/3c501.c b/drivers/net/3c501.c
index 5ba4bab..7d15e7c 100644
--- a/drivers/net/3c501.c
+++ b/drivers/net/3c501.c
@@ -17,7 +17,7 @@
 	Annapolis MD 21403
 
     Fixed (again!) the missing interrupt locking on TX/RX shifting.
-	Alan Cox <Alan.Cox@linux.org>
+	Alan Cox <alan@lxorguk.ukuu.org.uk>
 
     Removed calls to init_etherdev since they are no longer needed, and
     cleaned up modularization just a bit. The driver still allows only
@@ -29,16 +29,16 @@
     the board. Now getting 150K/second FTP with a 3c501 card. Still playing
     with a TX-TX optimisation to see if we can touch 180-200K/second as seems
     theoretically maximum.
-		19950402 Alan Cox <Alan.Cox@linux.org>
+		19950402 Alan Cox <alan@lxorguk.ukuu.org.uk>
 
     Cleaned up for 2.3.x because we broke SMP now.
-		20000208 Alan Cox <alan@redhat.com>
+		20000208 Alan Cox <alan@lxorguk.ukuu.org.uk>
 
     Check up pass for 2.5. Nothing significant changed
-		20021009 Alan Cox <alan@redhat.com>
+		20021009 Alan Cox <alan@lxorguk.ukuu.org.uk>
 
     Fixed zero fill corner case
-		20030104 Alan Cox <alan@redhat.com>
+		20030104 Alan Cox <alan@lxorguk.ukuu.org.uk>
 
 
    For the avoidance of doubt the "preferred form" of this code is one which
@@ -104,7 +104,7 @@
 
 
 static const char version[] =
-	DRV_NAME ".c: " DRV_VERSION " Alan Cox (alan@redhat.com).\n";
+	DRV_NAME ".c: " DRV_VERSION " Alan Cox (alan@lxorguk.ukuu.org.uk).\n";
 
 /*
  *	Braindamage remaining:
diff --git a/drivers/net/3c515.c b/drivers/net/3c515.c
index e4e3241..a0f8b6e 100644
--- a/drivers/net/3c515.c
+++ b/drivers/net/3c515.c
@@ -18,7 +18,7 @@
 
 	2001/11/17 - Added ethtool support (jgarzik)
 
-	2002/10/28 - Locking updates for 2.5 (alan@redhat.com)
+	2002/10/28 - Locking updates for 2.5 (alan@lxorguk.ukuu.org.uk)
 
 */
 
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index e9d5294..1d8af33 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -2400,7 +2400,7 @@
 	  will be called ehea.
 
 config ENIC
-	tristate "E, the Cisco 10G Ethernet NIC"
+	tristate "Cisco 10G Ethernet NIC support"
 	depends on PCI && INET
 	select INET_LRO
 	help
diff --git a/drivers/net/appletalk/cops.c b/drivers/net/appletalk/cops.c
index a0b4c85..735fc94 100644
--- a/drivers/net/appletalk/cops.c
+++ b/drivers/net/appletalk/cops.c
@@ -4,7 +4,7 @@
  *      - Jay Schulist <jschlst@samba.org>
  *
  *	With more than a little help from;
- *	- Alan Cox <Alan.Cox@linux.org> 
+ *	- Alan Cox <alan@lxorguk.ukuu.org.uk>
  *
  *      Derived from:
  *      - skeleton.c: A network driver outline for linux.
diff --git a/drivers/net/cxgb3/adapter.h b/drivers/net/cxgb3/adapter.h
index 455ef52..bc8e241 100644
--- a/drivers/net/cxgb3/adapter.h
+++ b/drivers/net/cxgb3/adapter.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003-2007 Chelsio, Inc. All rights reserved.
+ * Copyright (c) 2003-2008 Chelsio, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
diff --git a/drivers/net/cxgb3/ael1002.c b/drivers/net/cxgb3/ael1002.c
index 744fac0..5c3c05d 100644
--- a/drivers/net/cxgb3/ael1002.c
+++ b/drivers/net/cxgb3/ael1002.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005-2007 Chelsio, Inc. All rights reserved.
+ * Copyright (c) 2005-2008 Chelsio, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
diff --git a/drivers/net/cxgb3/common.h b/drivers/net/cxgb3/common.h
index 593fb64..e312d31 100644
--- a/drivers/net/cxgb3/common.h
+++ b/drivers/net/cxgb3/common.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005-2007 Chelsio, Inc. All rights reserved.
+ * Copyright (c) 2005-2008 Chelsio, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
diff --git a/drivers/net/cxgb3/cxgb3_ctl_defs.h b/drivers/net/cxgb3/cxgb3_ctl_defs.h
index 6ad9240..1d8d46e 100644
--- a/drivers/net/cxgb3/cxgb3_ctl_defs.h
+++ b/drivers/net/cxgb3/cxgb3_ctl_defs.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003-2007 Chelsio, Inc. All rights reserved.
+ * Copyright (c) 2003-2008 Chelsio, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
diff --git a/drivers/net/cxgb3/cxgb3_defs.h b/drivers/net/cxgb3/cxgb3_defs.h
index 45e9216..47e5376 100644
--- a/drivers/net/cxgb3/cxgb3_defs.h
+++ b/drivers/net/cxgb3/cxgb3_defs.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006-2007 Chelsio, Inc. All rights reserved.
+ * Copyright (c) 2006-2008 Chelsio, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
diff --git a/drivers/net/cxgb3/cxgb3_ioctl.h b/drivers/net/cxgb3/cxgb3_ioctl.h
index 3e8d5fa..b19e437 100644
--- a/drivers/net/cxgb3/cxgb3_ioctl.h
+++ b/drivers/net/cxgb3/cxgb3_ioctl.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003-2007 Chelsio, Inc. All rights reserved.
+ * Copyright (c) 2003-2008 Chelsio, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
diff --git a/drivers/net/cxgb3/cxgb3_main.c b/drivers/net/cxgb3/cxgb3_main.c
index f31985d..1ace41a 100644
--- a/drivers/net/cxgb3/cxgb3_main.c
+++ b/drivers/net/cxgb3/cxgb3_main.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003-2007 Chelsio, Inc. All rights reserved.
+ * Copyright (c) 2003-2008 Chelsio, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
diff --git a/drivers/net/cxgb3/cxgb3_offload.c b/drivers/net/cxgb3/cxgb3_offload.c
index 0f6fd63..265aa8a 100644
--- a/drivers/net/cxgb3/cxgb3_offload.c
+++ b/drivers/net/cxgb3/cxgb3_offload.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006-2007 Chelsio, Inc. All rights reserved.
+ * Copyright (c) 2006-2008 Chelsio, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
diff --git a/drivers/net/cxgb3/cxgb3_offload.h b/drivers/net/cxgb3/cxgb3_offload.h
index 7a37913..d514e50 100644
--- a/drivers/net/cxgb3/cxgb3_offload.h
+++ b/drivers/net/cxgb3/cxgb3_offload.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006-2007 Chelsio, Inc. All rights reserved.
+ * Copyright (c) 2006-2008 Chelsio, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
diff --git a/drivers/net/cxgb3/firmware_exports.h b/drivers/net/cxgb3/firmware_exports.h
index b75ddd8..0d9b0e6 100644
--- a/drivers/net/cxgb3/firmware_exports.h
+++ b/drivers/net/cxgb3/firmware_exports.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004-2007 Chelsio, Inc. All rights reserved.
+ * Copyright (c) 2004-2008 Chelsio, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
diff --git a/drivers/net/cxgb3/l2t.c b/drivers/net/cxgb3/l2t.c
index b2c5314..4407ac9 100644
--- a/drivers/net/cxgb3/l2t.c
+++ b/drivers/net/cxgb3/l2t.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003-2007 Chelsio, Inc. All rights reserved.
+ * Copyright (c) 2003-2008 Chelsio, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
diff --git a/drivers/net/cxgb3/l2t.h b/drivers/net/cxgb3/l2t.h
index 42ce65f..fd3eb07 100644
--- a/drivers/net/cxgb3/l2t.h
+++ b/drivers/net/cxgb3/l2t.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003-2007 Chelsio, Inc. All rights reserved.
+ * Copyright (c) 2003-2008 Chelsio, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
diff --git a/drivers/net/cxgb3/mc5.c b/drivers/net/cxgb3/mc5.c
index 4c4d6e8..3b5517b 100644
--- a/drivers/net/cxgb3/mc5.c
+++ b/drivers/net/cxgb3/mc5.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003-2007 Chelsio, Inc. All rights reserved.
+ * Copyright (c) 2003-2008 Chelsio, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
diff --git a/drivers/net/cxgb3/sge.c b/drivers/net/cxgb3/sge.c
index 8791941..c6480be 100644
--- a/drivers/net/cxgb3/sge.c
+++ b/drivers/net/cxgb3/sge.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005-2007 Chelsio, Inc. All rights reserved.
+ * Copyright (c) 2005-2008 Chelsio, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
diff --git a/drivers/net/cxgb3/t3_cpl.h b/drivers/net/cxgb3/t3_cpl.h
index 917970e..852c399 100644
--- a/drivers/net/cxgb3/t3_cpl.h
+++ b/drivers/net/cxgb3/t3_cpl.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004-2007 Chelsio, Inc. All rights reserved.
+ * Copyright (c) 2004-2008 Chelsio, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
diff --git a/drivers/net/cxgb3/t3_hw.c b/drivers/net/cxgb3/t3_hw.c
index 4da5b09..968f64b 100644
--- a/drivers/net/cxgb3/t3_hw.c
+++ b/drivers/net/cxgb3/t3_hw.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003-2007 Chelsio, Inc. All rights reserved.
+ * Copyright (c) 2003-2008 Chelsio, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
diff --git a/drivers/net/cxgb3/t3cdev.h b/drivers/net/cxgb3/t3cdev.h
index 0a21cfb..be55e9a 100644
--- a/drivers/net/cxgb3/t3cdev.h
+++ b/drivers/net/cxgb3/t3cdev.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006-2007 Chelsio Communications.  All rights reserved.
+ * Copyright (C) 2006-2008 Chelsio Communications.  All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
diff --git a/drivers/net/cxgb3/version.h b/drivers/net/cxgb3/version.h
index 29db711..bb8698a 100644
--- a/drivers/net/cxgb3/version.h
+++ b/drivers/net/cxgb3/version.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003-2007 Chelsio, Inc. All rights reserved.
+ * Copyright (c) 2003-2008 Chelsio, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
@@ -35,7 +35,7 @@
 #define DRV_DESC "Chelsio T3 Network Driver"
 #define DRV_NAME "cxgb3"
 /* Driver version */
-#define DRV_VERSION "1.0-ko"
+#define DRV_VERSION "1.1.0-ko"
 
 /* Firmware version */
 #define FW_VERSION_MAJOR 7
diff --git a/drivers/net/cxgb3/vsc8211.c b/drivers/net/cxgb3/vsc8211.c
index 306c2dc..33f956b 100644
--- a/drivers/net/cxgb3/vsc8211.c
+++ b/drivers/net/cxgb3/vsc8211.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005-2007 Chelsio, Inc. All rights reserved.
+ * Copyright (c) 2005-2008 Chelsio, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
diff --git a/drivers/net/cxgb3/xgmac.c b/drivers/net/cxgb3/xgmac.c
index ffdc0a1..9d77869 100644
--- a/drivers/net/cxgb3/xgmac.c
+++ b/drivers/net/cxgb3/xgmac.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005-2007 Chelsio, Inc. All rights reserved.
+ * Copyright (c) 2005-2008 Chelsio, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
diff --git a/drivers/net/eexpress.c b/drivers/net/eexpress.c
index 795c594..b751c1b 100644
--- a/drivers/net/eexpress.c
+++ b/drivers/net/eexpress.c
@@ -8,7 +8,7 @@
  *
  * Many modifications, and currently maintained, by
  *  Philip Blundell <philb@gnu.org>
- * Added the Compaq LTE  Alan Cox <alan@redhat.com>
+ * Added the Compaq LTE  Alan Cox <alan@lxorguk.ukuu.org.uk>
  * Added MCA support Adam Fritzler
  *
  * Note - this driver is experimental still - it has problems on faster
diff --git a/drivers/net/enic/enic_main.c b/drivers/net/enic/enic_main.c
index f3a47a8..180e968 100644
--- a/drivers/net/enic/enic_main.c
+++ b/drivers/net/enic/enic_main.c
@@ -34,6 +34,7 @@
 #include <linux/ip.h>
 #include <linux/ipv6.h>
 #include <linux/tcp.h>
+#include <net/ip6_checksum.h>
 
 #include "cq_enet_desc.h"
 #include "vnic_dev.h"
diff --git a/drivers/net/ibmlana.c b/drivers/net/ibmlana.c
index 95e3464..f027647 100644
--- a/drivers/net/ibmlana.c
+++ b/drivers/net/ibmlana.c
@@ -71,7 +71,7 @@
   June 1st, 2000
 	corrected version codes, added support for the latest 2.3 changes
   Oct 28th, 2002
-  	cleaned up for the 2.5 tree <alan@redhat.com>
+	cleaned up for the 2.5 tree <alan@lxorguk.ukuu.org.uk>
 
  *************************************************************************/
 
diff --git a/drivers/net/jme.c b/drivers/net/jme.c
index 156f159..81c6cdc 100644
--- a/drivers/net/jme.c
+++ b/drivers/net/jme.c
@@ -37,6 +37,7 @@
 #include <linux/tcp.h>
 #include <linux/udp.h>
 #include <linux/if_vlan.h>
+#include <net/ip6_checksum.h>
 #include "jme.h"
 
 static int force_pseudohp = -1;
diff --git a/drivers/net/macmace.c b/drivers/net/macmace.c
index 51ad376..85587a66 100644
--- a/drivers/net/macmace.c
+++ b/drivers/net/macmace.c
@@ -9,7 +9,7 @@
  *	2 of the License, or (at your option) any later version.
  *
  *	Copyright (C) 1996 Paul Mackerras.
- *	Copyright (C) 1998 Alan Cox <alan@redhat.com>
+ *	Copyright (C) 1998 Alan Cox <alan@lxorguk.ukuu.org.uk>
  *
  *	Modified heavily by Joshua M. Thompson based on Dave Huang's NetBSD driver
  *
diff --git a/drivers/net/pcmcia/3c589_cs.c b/drivers/net/pcmcia/3c589_cs.c
index 0b28d0d..c235cdb 100644
--- a/drivers/net/pcmcia/3c589_cs.c
+++ b/drivers/net/pcmcia/3c589_cs.c
@@ -15,7 +15,7 @@
     incorporated herein by reference.
     Donald Becker may be reached at becker@scyld.com
     
-    Updated for 2.5.x by Alan Cox <alan@redhat.com>
+    Updated for 2.5.x by Alan Cox <alan@lxorguk.ukuu.org.uk>
 
 ======================================================================*/
 
diff --git a/drivers/net/pcmcia/nmclan_cs.c b/drivers/net/pcmcia/nmclan_cs.c
index 54df34f..448cd40 100644
--- a/drivers/net/pcmcia/nmclan_cs.c
+++ b/drivers/net/pcmcia/nmclan_cs.c
@@ -69,7 +69,7 @@
 History
 -------------------------------------------------------------------------------
 Log: nmclan_cs.c,v
- * 2.5.75-ac1 2003/07/11 Alan Cox <alan@redhat.com>
+ * 2.5.75-ac1 2003/07/11 Alan Cox <alan@lxorguk.ukuu.org.uk>
  * Fixed hang on card eject as we probe it
  * Cleaned up to use new style locking.
  *
diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
index 6671e2d..d0ed1ef 100644
--- a/drivers/net/phy/mdio_bus.c
+++ b/drivers/net/phy/mdio_bus.c
@@ -55,6 +55,7 @@
 
 /**
  * mdiobus_release - mii_bus device release callback
+ * @d: the target struct device that contains the mii_bus
  *
  * Description: called when the last reference to an mii_bus is
  * dropped, to free the underlying memory.
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 1716274..f11e900 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -557,6 +557,7 @@
 
 	return ctl;
 }
+EXPORT_SYMBOL(genphy_restart_aneg);
 
 
 /**
diff --git a/drivers/net/qlge/qlge.h b/drivers/net/qlge/qlge.h
index c37ea43..38116f9 100644
--- a/drivers/net/qlge/qlge.h
+++ b/drivers/net/qlge/qlge.h
@@ -58,7 +58,7 @@
  */
 #if (PAGE_SHIFT == 12) || (PAGE_SHIFT == 13)	/* 4k & 8k pages */
 #define TX_DESC_PER_OAL ((MAX_SKB_FRAGS - TX_DESC_PER_IOCB) + 2)
-#elif (PAGE_SHIFT == 16)	/* 64k pages */
+#else /* all other page sizes */
 #define TX_DESC_PER_OAL 0
 #endif
 
diff --git a/drivers/net/qlge/qlge_main.c b/drivers/net/qlge/qlge_main.c
index 297877b..4b2caa6 100644
--- a/drivers/net/qlge/qlge_main.c
+++ b/drivers/net/qlge/qlge_main.c
@@ -39,6 +39,7 @@
 #include <linux/delay.h>
 #include <linux/mm.h>
 #include <linux/vmalloc.h>
+#include <net/ip6_checksum.h>
 
 #include "qlge.h"
 
diff --git a/drivers/net/tlan.c b/drivers/net/tlan.c
index ec871f6..c41d687 100644
--- a/drivers/net/tlan.c
+++ b/drivers/net/tlan.c
@@ -29,7 +29,8 @@
  *
  *	Tigran Aivazian <tigran@sco.com>:	TLan_PciProbe() now uses
  *						new PCI BIOS interface.
- *	Alan Cox	<alan@redhat.com>:	Fixed the out of memory
+ *	Alan Cox	<alan@lxorguk.ukuu.org.uk>:
+ *						Fixed the out of memory
  *						handling.
  *
  *	Torben Mathiasen <torben.mathiasen@compaq.com> New Maintainer!
diff --git a/drivers/net/tokenring/smctr.c b/drivers/net/tokenring/smctr.c
index fa73e6e..ed50d28 100644
--- a/drivers/net/tokenring/smctr.c
+++ b/drivers/net/tokenring/smctr.c
@@ -25,7 +25,7 @@
  *  To do:
  *    1. Multicast support.
  *
- *  Initial 2.5 cleanup Alan Cox <alan@redhat.com>  2002/10/28
+ *  Initial 2.5 cleanup Alan Cox <alan@lxorguk.ukuu.org.uk>  2002/10/28
  */
 
 #include <linux/module.h>
diff --git a/drivers/net/tulip/de2104x.c b/drivers/net/tulip/de2104x.c
index f54c450..124d5d6 100644
--- a/drivers/net/tulip/de2104x.c
+++ b/drivers/net/tulip/de2104x.c
@@ -1688,6 +1688,7 @@
 	unsigned i;
 
 	dw32 (ROMCmd, 0);	/* Reset the pointer with a dummy write. */
+	udelay(5);
 
 	for (i = 0; i < 6; i++) {
 		int value, boguscnt = 100000;
diff --git a/drivers/net/tulip/dmfe.c b/drivers/net/tulip/dmfe.c
index 6562004..8e46a51 100644
--- a/drivers/net/tulip/dmfe.c
+++ b/drivers/net/tulip/dmfe.c
@@ -23,7 +23,7 @@
     Marcelo Tosatti <marcelo@conectiva.com.br> :
     Made it compile in 2.3 (device to net_device)
 
-    Alan Cox <alan@redhat.com> :
+    Alan Cox <alan@lxorguk.ukuu.org.uk> :
     Cleaned up for kernel merge.
     Removed the back compatibility support
     Reformatted, fixing spelling etc as I went
@@ -49,7 +49,7 @@
     support.  Updated PCI resource allocation.  Do not
     forget to unmap PCI mapped skbs.
 
-    Alan Cox <alan@redhat.com>
+    Alan Cox <alan@lxorguk.ukuu.org.uk>
     Added new PCI identifiers provided by Clear Zhang at ALi
     for their 1563 ethernet device.
 
diff --git a/drivers/net/via-velocity.c b/drivers/net/via-velocity.c
index ad20f96..2dced38 100644
--- a/drivers/net/via-velocity.c
+++ b/drivers/net/via-velocity.c
@@ -12,7 +12,7 @@
  *	Scatter gather
  *	More testing
  *
- * The changes are (c) Copyright 2004, Red Hat Inc. <alan@redhat.com>
+ * The changes are (c) Copyright 2004, Red Hat Inc. <alan@lxorguk.ukuu.org.uk>
  * Additional fixes and clean up: Francois Romieu
  *
  * This source has not been verified for use in safety critical systems.
diff --git a/drivers/net/wan/z85230.c b/drivers/net/wan/z85230.c
index 243bd8d..ccd9cd3 100644
--- a/drivers/net/wan/z85230.c
+++ b/drivers/net/wan/z85230.c
@@ -18,7 +18,8 @@
  *	DMA now uses get_free_page as kmalloc buffers may span a 64K 
  *	boundary.
  *
- *	Modified for SMP safety and SMP locking by Alan Cox <alan@redhat.com>
+ *	Modified for SMP safety and SMP locking by Alan Cox
+ *					<alan@lxorguk.ukuu.org.uk>
  *
  *	Performance
  *
diff --git a/drivers/net/wan/z85230.h b/drivers/net/wan/z85230.h
index 4f37239..85b3e78 100644
--- a/drivers/net/wan/z85230.h
+++ b/drivers/net/wan/z85230.h
@@ -2,7 +2,7 @@
  *	Description of Z8530 Z85C30 and Z85230 communications chips
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1998 Alan Cox <alan@redhat.com>
+ * Copyright (C) 1998 Alan Cox <alan@lxorguk.ukuu.org.uk>
  */
 
 #ifndef _Z8530_H
diff --git a/drivers/net/wireless/wavelan.c b/drivers/net/wireless/wavelan.c
index 136220b..e939a73 100644
--- a/drivers/net/wireless/wavelan.c
+++ b/drivers/net/wireless/wavelan.c
@@ -4387,7 +4387,7 @@
  *
  * Thanks go also to:
  *	James Ashton (jaa101@syseng.anu.edu.au),
- *	Alan Cox (alan@redhat.com),
+ *	Alan Cox (alan@lxorguk.ukuu.org.uk),
  *	Allan Creighton (allanc@cs.usyd.edu.au),
  *	Matthew Geier (matthew@cs.usyd.edu.au),
  *	Remo di Giovanni (remo@cs.usyd.edu.au),
diff --git a/drivers/net/wireless/wavelan.p.h b/drivers/net/wireless/wavelan.p.h
index b33ac47..44d31bb 100644
--- a/drivers/net/wireless/wavelan.p.h
+++ b/drivers/net/wireless/wavelan.p.h
@@ -186,7 +186,7 @@
  *
  * Thanks go also to:
  *	James Ashton <jaa101@syseng.anu.edu.au>,
- *	Alan Cox <alan@redhat.com>,
+ *	Alan Cox <alan@lxorguk.ukuu.org.uk>,
  *	Allan Creighton <allanc@cs.usyd.edu.au>,
  *	Matthew Geier <matthew@cs.usyd.edu.au>,
  *	Remo di Giovanni <remo@cs.usyd.edu.au>,
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index a656128..4dada6e 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -56,4 +56,28 @@
 	  charging select between 100 mA and 500 mA charging current
 	  limit.
 
+config REGULATOR_WM8350
+	tristate "Wolfson Microelectroncis WM8350 AudioPlus PMIC"
+	depends on MFD_WM8350
+	select REGULATOR
+	help
+	  This driver provides support for the voltage and current regulators
+          of the WM8350 AudioPlus PMIC.
+
+config REGULATOR_WM8400
+	tristate "Wolfson Microelectroncis WM8400 AudioPlus PMIC"
+	depends on MFD_WM8400
+	select REGULATOR
+	help
+	  This driver provides support for the voltage regulators of the
+	  WM8400 AudioPlus PMIC.
+
+config REGULATOR_DA903X
+	tristate "Support regulators on Dialog Semiconductor DA9030/DA9034 PMIC"
+	depends on PMIC_DA903X
+	select REGULATOR
+	help
+	  Say y here to support the BUCKs and LDOs regulators found on
+	  Dialog Semiconductor DA9030/DA9034 PMIC.
+
 endmenu
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index ac2c64e..254d40c 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -8,5 +8,8 @@
 obj-$(CONFIG_REGULATOR_VIRTUAL_CONSUMER) += virtual.o
 
 obj-$(CONFIG_REGULATOR_BQ24022) += bq24022.o
+obj-$(CONFIG_REGULATOR_WM8350) += wm8350-regulator.o
+obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o
+obj-$(CONFIG_REGULATOR_DA903X)	+= da903x.o
 
 ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG
diff --git a/drivers/regulator/bq24022.c b/drivers/regulator/bq24022.c
index 263699d..366565a 100644
--- a/drivers/regulator/bq24022.c
+++ b/drivers/regulator/bq24022.c
@@ -18,13 +18,13 @@
 #include <linux/regulator/bq24022.h>
 #include <linux/regulator/driver.h>
 
+
 static int bq24022_set_current_limit(struct regulator_dev *rdev,
 					int min_uA, int max_uA)
 {
-	struct platform_device *pdev = rdev_get_drvdata(rdev);
-	struct bq24022_mach_info *pdata = pdev->dev.platform_data;
+	struct bq24022_mach_info *pdata = rdev_get_drvdata(rdev);
 
-	dev_dbg(&pdev->dev, "setting current limit to %s mA\n",
+	dev_dbg(rdev_get_dev(rdev), "setting current limit to %s mA\n",
 		max_uA >= 500000 ? "500" : "100");
 
 	/* REVISIT: maybe return error if min_uA != 0 ? */
@@ -34,18 +34,16 @@
 
 static int bq24022_get_current_limit(struct regulator_dev *rdev)
 {
-	struct platform_device *pdev = rdev_get_drvdata(rdev);
-	struct bq24022_mach_info *pdata = pdev->dev.platform_data;
+	struct bq24022_mach_info *pdata = rdev_get_drvdata(rdev);
 
 	return gpio_get_value(pdata->gpio_iset2) ? 500000 : 100000;
 }
 
 static int bq24022_enable(struct regulator_dev *rdev)
 {
-	struct platform_device *pdev = rdev_get_drvdata(rdev);
-	struct bq24022_mach_info *pdata = pdev->dev.platform_data;
+	struct bq24022_mach_info *pdata = rdev_get_drvdata(rdev);
 
-	dev_dbg(&pdev->dev, "enabling charger\n");
+	dev_dbg(rdev_get_dev(rdev), "enabling charger\n");
 
 	gpio_set_value(pdata->gpio_nce, 0);
 	return 0;
@@ -53,10 +51,9 @@
 
 static int bq24022_disable(struct regulator_dev *rdev)
 {
-	struct platform_device *pdev = rdev_get_drvdata(rdev);
-	struct bq24022_mach_info *pdata = pdev->dev.platform_data;
+	struct bq24022_mach_info *pdata = rdev_get_drvdata(rdev);
 
-	dev_dbg(&pdev->dev, "disabling charger\n");
+	dev_dbg(rdev_get_dev(rdev), "disabling charger\n");
 
 	gpio_set_value(pdata->gpio_nce, 1);
 	return 0;
@@ -108,7 +105,7 @@
 	ret = gpio_direction_output(pdata->gpio_iset2, 0);
 	ret = gpio_direction_output(pdata->gpio_nce, 1);
 
-	bq24022 = regulator_register(&bq24022_desc, pdev);
+	bq24022 = regulator_register(&bq24022_desc, &pdev->dev, pdata);
 	if (IS_ERR(bq24022)) {
 		dev_dbg(&pdev->dev, "couldn't register regulator\n");
 		ret = PTR_ERR(bq24022);
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 9c79862..02a7744 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -2,8 +2,9 @@
  * core.c  --  Voltage/Current Regulator framework.
  *
  * Copyright 2007, 2008 Wolfson Microelectronics PLC.
+ * Copyright 2008 SlimLogic Ltd.
  *
- * Author: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
+ * Author: Liam Girdwood <lrg@slimlogic.co.uk>
  *
  *  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
@@ -64,14 +65,9 @@
 	struct list_head list;
 	struct device *dev;
 	const char *supply;
-	const char *regulator;
+	struct regulator_dev *regulator;
 };
 
-static inline struct regulator_dev *to_rdev(struct device *d)
-{
-	return container_of(d, struct regulator_dev, dev);
-}
-
 /*
  * struct regulator
  *
@@ -227,7 +223,7 @@
 static ssize_t regulator_uV_show(struct device *dev,
 				struct device_attribute *attr, char *buf)
 {
-	struct regulator_dev *rdev = to_rdev(dev);
+	struct regulator_dev *rdev = dev_get_drvdata(dev);
 	ssize_t ret;
 
 	mutex_lock(&rdev->mutex);
@@ -240,15 +236,31 @@
 static ssize_t regulator_uA_show(struct device *dev,
 				struct device_attribute *attr, char *buf)
 {
-	struct regulator_dev *rdev = to_rdev(dev);
+	struct regulator_dev *rdev = dev_get_drvdata(dev);
 
 	return sprintf(buf, "%d\n", _regulator_get_current_limit(rdev));
 }
 
+static ssize_t regulator_name_show(struct device *dev,
+			     struct device_attribute *attr, char *buf)
+{
+	struct regulator_dev *rdev = dev_get_drvdata(dev);
+	const char *name;
+
+	if (rdev->constraints->name)
+		name = rdev->constraints->name;
+	else if (rdev->desc->name)
+		name = rdev->desc->name;
+	else
+		name = "";
+
+	return sprintf(buf, "%s\n", name);
+}
+
 static ssize_t regulator_opmode_show(struct device *dev,
 				    struct device_attribute *attr, char *buf)
 {
-	struct regulator_dev *rdev = to_rdev(dev);
+	struct regulator_dev *rdev = dev_get_drvdata(dev);
 	int mode = _regulator_get_mode(rdev);
 
 	switch (mode) {
@@ -267,7 +279,7 @@
 static ssize_t regulator_state_show(struct device *dev,
 				   struct device_attribute *attr, char *buf)
 {
-	struct regulator_dev *rdev = to_rdev(dev);
+	struct regulator_dev *rdev = dev_get_drvdata(dev);
 	int state = _regulator_is_enabled(rdev);
 
 	if (state > 0)
@@ -281,7 +293,7 @@
 static ssize_t regulator_min_uA_show(struct device *dev,
 				    struct device_attribute *attr, char *buf)
 {
-	struct regulator_dev *rdev = to_rdev(dev);
+	struct regulator_dev *rdev = dev_get_drvdata(dev);
 
 	if (!rdev->constraints)
 		return sprintf(buf, "constraint not defined\n");
@@ -292,7 +304,7 @@
 static ssize_t regulator_max_uA_show(struct device *dev,
 				    struct device_attribute *attr, char *buf)
 {
-	struct regulator_dev *rdev = to_rdev(dev);
+	struct regulator_dev *rdev = dev_get_drvdata(dev);
 
 	if (!rdev->constraints)
 		return sprintf(buf, "constraint not defined\n");
@@ -303,7 +315,7 @@
 static ssize_t regulator_min_uV_show(struct device *dev,
 				    struct device_attribute *attr, char *buf)
 {
-	struct regulator_dev *rdev = to_rdev(dev);
+	struct regulator_dev *rdev = dev_get_drvdata(dev);
 
 	if (!rdev->constraints)
 		return sprintf(buf, "constraint not defined\n");
@@ -314,7 +326,7 @@
 static ssize_t regulator_max_uV_show(struct device *dev,
 				    struct device_attribute *attr, char *buf)
 {
-	struct regulator_dev *rdev = to_rdev(dev);
+	struct regulator_dev *rdev = dev_get_drvdata(dev);
 
 	if (!rdev->constraints)
 		return sprintf(buf, "constraint not defined\n");
@@ -325,7 +337,7 @@
 static ssize_t regulator_total_uA_show(struct device *dev,
 				      struct device_attribute *attr, char *buf)
 {
-	struct regulator_dev *rdev = to_rdev(dev);
+	struct regulator_dev *rdev = dev_get_drvdata(dev);
 	struct regulator *regulator;
 	int uA = 0;
 
@@ -339,14 +351,14 @@
 static ssize_t regulator_num_users_show(struct device *dev,
 				      struct device_attribute *attr, char *buf)
 {
-	struct regulator_dev *rdev = to_rdev(dev);
+	struct regulator_dev *rdev = dev_get_drvdata(dev);
 	return sprintf(buf, "%d\n", rdev->use_count);
 }
 
 static ssize_t regulator_type_show(struct device *dev,
 				  struct device_attribute *attr, char *buf)
 {
-	struct regulator_dev *rdev = to_rdev(dev);
+	struct regulator_dev *rdev = dev_get_drvdata(dev);
 
 	switch (rdev->desc->type) {
 	case REGULATOR_VOLTAGE:
@@ -360,7 +372,7 @@
 static ssize_t regulator_suspend_mem_uV_show(struct device *dev,
 				struct device_attribute *attr, char *buf)
 {
-	struct regulator_dev *rdev = to_rdev(dev);
+	struct regulator_dev *rdev = dev_get_drvdata(dev);
 
 	if (!rdev->constraints)
 		return sprintf(buf, "not defined\n");
@@ -370,7 +382,7 @@
 static ssize_t regulator_suspend_disk_uV_show(struct device *dev,
 				struct device_attribute *attr, char *buf)
 {
-	struct regulator_dev *rdev = to_rdev(dev);
+	struct regulator_dev *rdev = dev_get_drvdata(dev);
 
 	if (!rdev->constraints)
 		return sprintf(buf, "not defined\n");
@@ -380,7 +392,7 @@
 static ssize_t regulator_suspend_standby_uV_show(struct device *dev,
 				struct device_attribute *attr, char *buf)
 {
-	struct regulator_dev *rdev = to_rdev(dev);
+	struct regulator_dev *rdev = dev_get_drvdata(dev);
 
 	if (!rdev->constraints)
 		return sprintf(buf, "not defined\n");
@@ -406,7 +418,7 @@
 static ssize_t regulator_suspend_mem_mode_show(struct device *dev,
 				struct device_attribute *attr, char *buf)
 {
-	struct regulator_dev *rdev = to_rdev(dev);
+	struct regulator_dev *rdev = dev_get_drvdata(dev);
 
 	if (!rdev->constraints)
 		return sprintf(buf, "not defined\n");
@@ -417,7 +429,7 @@
 static ssize_t regulator_suspend_disk_mode_show(struct device *dev,
 				struct device_attribute *attr, char *buf)
 {
-	struct regulator_dev *rdev = to_rdev(dev);
+	struct regulator_dev *rdev = dev_get_drvdata(dev);
 
 	if (!rdev->constraints)
 		return sprintf(buf, "not defined\n");
@@ -428,7 +440,7 @@
 static ssize_t regulator_suspend_standby_mode_show(struct device *dev,
 				struct device_attribute *attr, char *buf)
 {
-	struct regulator_dev *rdev = to_rdev(dev);
+	struct regulator_dev *rdev = dev_get_drvdata(dev);
 
 	if (!rdev->constraints)
 		return sprintf(buf, "not defined\n");
@@ -439,7 +451,7 @@
 static ssize_t regulator_suspend_mem_state_show(struct device *dev,
 				   struct device_attribute *attr, char *buf)
 {
-	struct regulator_dev *rdev = to_rdev(dev);
+	struct regulator_dev *rdev = dev_get_drvdata(dev);
 
 	if (!rdev->constraints)
 		return sprintf(buf, "not defined\n");
@@ -453,7 +465,7 @@
 static ssize_t regulator_suspend_disk_state_show(struct device *dev,
 				   struct device_attribute *attr, char *buf)
 {
-	struct regulator_dev *rdev = to_rdev(dev);
+	struct regulator_dev *rdev = dev_get_drvdata(dev);
 
 	if (!rdev->constraints)
 		return sprintf(buf, "not defined\n");
@@ -467,7 +479,7 @@
 static ssize_t regulator_suspend_standby_state_show(struct device *dev,
 				   struct device_attribute *attr, char *buf)
 {
-	struct regulator_dev *rdev = to_rdev(dev);
+	struct regulator_dev *rdev = dev_get_drvdata(dev);
 
 	if (!rdev->constraints)
 		return sprintf(buf, "not defined\n");
@@ -477,7 +489,9 @@
 	else
 		return sprintf(buf, "disabled\n");
 }
+
 static struct device_attribute regulator_dev_attrs[] = {
+	__ATTR(name, 0444, regulator_name_show, NULL),
 	__ATTR(microvolts, 0444, regulator_uV_show, NULL),
 	__ATTR(microamps, 0444, regulator_uA_show, NULL),
 	__ATTR(opmode, 0444, regulator_opmode_show, NULL),
@@ -512,7 +526,7 @@
 
 static void regulator_dev_release(struct device *dev)
 {
-	struct regulator_dev *rdev = to_rdev(dev);
+	struct regulator_dev *rdev = dev_get_drvdata(dev);
 	kfree(rdev);
 }
 
@@ -569,8 +583,11 @@
 
 	/* enable & disable are mandatory for suspend control */
 	if (!rdev->desc->ops->set_suspend_enable ||
-		!rdev->desc->ops->set_suspend_disable)
+		!rdev->desc->ops->set_suspend_disable) {
+		printk(KERN_ERR "%s: no way to set suspend state\n",
+			__func__);
 		return -EINVAL;
+	}
 
 	if (rstate->enabled)
 		ret = rdev->desc->ops->set_suspend_enable(rdev);
@@ -656,6 +673,155 @@
 	printk(KERN_INFO "regulator: %s: %s\n", rdev->desc->name, buf);
 }
 
+/**
+ * set_machine_constraints - sets regulator constraints
+ * @regulator: regulator source
+ *
+ * Allows platform initialisation code to define and constrain
+ * regulator circuits e.g. valid voltage/current ranges, etc.  NOTE:
+ * Constraints *must* be set by platform code in order for some
+ * regulator operations to proceed i.e. set_voltage, set_current_limit,
+ * set_mode.
+ */
+static int set_machine_constraints(struct regulator_dev *rdev,
+	struct regulation_constraints *constraints)
+{
+	int ret = 0;
+	const char *name;
+	struct regulator_ops *ops = rdev->desc->ops;
+
+	if (constraints->name)
+		name = constraints->name;
+	else if (rdev->desc->name)
+		name = rdev->desc->name;
+	else
+		name = "regulator";
+
+	rdev->constraints = constraints;
+
+	/* do we need to apply the constraint voltage */
+	if (rdev->constraints->apply_uV &&
+		rdev->constraints->min_uV == rdev->constraints->max_uV &&
+		ops->set_voltage) {
+		ret = ops->set_voltage(rdev,
+			rdev->constraints->min_uV, rdev->constraints->max_uV);
+			if (ret < 0) {
+				printk(KERN_ERR "%s: failed to apply %duV constraint to %s\n",
+				       __func__,
+				       rdev->constraints->min_uV, name);
+				rdev->constraints = NULL;
+				goto out;
+			}
+	}
+
+	/* are we enabled at boot time by firmware / bootloader */
+	if (rdev->constraints->boot_on)
+		rdev->use_count = 1;
+
+	/* do we need to setup our suspend state */
+	if (constraints->initial_state) {
+		ret = suspend_prepare(rdev, constraints->initial_state);
+		if (ret < 0) {
+			printk(KERN_ERR "%s: failed to set suspend state for %s\n",
+			       __func__, name);
+			rdev->constraints = NULL;
+			goto out;
+		}
+	}
+
+	/* if always_on is set then turn the regulator on if it's not
+	 * already on. */
+	if (constraints->always_on && ops->enable &&
+	    ((ops->is_enabled && !ops->is_enabled(rdev)) ||
+	     (!ops->is_enabled && !constraints->boot_on))) {
+		ret = ops->enable(rdev);
+		if (ret < 0) {
+			printk(KERN_ERR "%s: failed to enable %s\n",
+			       __func__, name);
+			rdev->constraints = NULL;
+			goto out;
+		}
+	}
+
+	print_constraints(rdev);
+out:
+	return ret;
+}
+
+/**
+ * set_supply - set regulator supply regulator
+ * @regulator: regulator name
+ * @supply: supply regulator name
+ *
+ * Called by platform initialisation code to set the supply regulator for this
+ * regulator. This ensures that a regulators supply will also be enabled by the
+ * core if it's child is enabled.
+ */
+static int set_supply(struct regulator_dev *rdev,
+	struct regulator_dev *supply_rdev)
+{
+	int err;
+
+	err = sysfs_create_link(&rdev->dev.kobj, &supply_rdev->dev.kobj,
+				"supply");
+	if (err) {
+		printk(KERN_ERR
+		       "%s: could not add device link %s err %d\n",
+		       __func__, supply_rdev->dev.kobj.name, err);
+		       goto out;
+	}
+	rdev->supply = supply_rdev;
+	list_add(&rdev->slist, &supply_rdev->supply_list);
+out:
+	return err;
+}
+
+/**
+ * set_consumer_device_supply: Bind a regulator to a symbolic supply
+ * @regulator: regulator source
+ * @dev:       device the supply applies to
+ * @supply:    symbolic name for supply
+ *
+ * Allows platform initialisation code to map physical regulator
+ * sources to symbolic names for supplies for use by devices.  Devices
+ * should use these symbolic names to request regulators, avoiding the
+ * need to provide board-specific regulator names as platform data.
+ */
+static int set_consumer_device_supply(struct regulator_dev *rdev,
+	struct device *consumer_dev, const char *supply)
+{
+	struct regulator_map *node;
+
+	if (supply == NULL)
+		return -EINVAL;
+
+	node = kmalloc(sizeof(struct regulator_map), GFP_KERNEL);
+	if (node == NULL)
+		return -ENOMEM;
+
+	node->regulator = rdev;
+	node->dev = consumer_dev;
+	node->supply = supply;
+
+	list_add(&node->list, &regulator_map_list);
+	return 0;
+}
+
+static void unset_consumer_device_supply(struct regulator_dev *rdev,
+	struct device *consumer_dev)
+{
+	struct regulator_map *node, *n;
+
+	list_for_each_entry_safe(node, n, &regulator_map_list, list) {
+		if (rdev == node->regulator &&
+			consumer_dev == node->dev) {
+			list_del(&node->list);
+			kfree(node);
+			return;
+		}
+	}
+}
+
 #define REG_STR_SIZE	32
 
 static struct regulator *create_regulator(struct regulator_dev *rdev,
@@ -746,7 +912,6 @@
 	struct regulator_dev *rdev;
 	struct regulator_map *map;
 	struct regulator *regulator = ERR_PTR(-ENODEV);
-	const char *supply = id;
 
 	if (id == NULL) {
 		printk(KERN_ERR "regulator: get() with no identifier\n");
@@ -758,15 +923,9 @@
 	list_for_each_entry(map, &regulator_map_list, list) {
 		if (dev == map->dev &&
 		    strcmp(map->supply, id) == 0) {
-			supply = map->regulator;
-			break;
-		}
-	}
-
-	list_for_each_entry(rdev, &regulator_list, list) {
-		if (strcmp(supply, rdev->desc->name) == 0 &&
-		    try_module_get(rdev->owner))
+			rdev = map->regulator;
 			goto found;
+		}
 	}
 	printk(KERN_ERR "regulator: Unable to get requested regulator: %s\n",
 	       id);
@@ -774,12 +933,16 @@
 	return regulator;
 
 found:
+	if (!try_module_get(rdev->owner))
+		goto out;
+
 	regulator = create_regulator(rdev, dev, id);
 	if (regulator == NULL) {
 		regulator = ERR_PTR(-ENOMEM);
 		module_put(rdev->owner);
 	}
 
+out:
 	mutex_unlock(&regulator_list_mutex);
 	return regulator;
 }
@@ -1559,11 +1722,12 @@
  * Returns 0 on success.
  */
 struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc,
-					  void *reg_data)
+	struct device *dev, void *driver_data)
 {
 	static atomic_t regulator_no = ATOMIC_INIT(0);
 	struct regulator_dev *rdev;
-	int ret;
+	struct regulator_init_data *init_data = dev->platform_data;
+	int ret, i;
 
 	if (regulator_desc == NULL)
 		return ERR_PTR(-EINVAL);
@@ -1575,6 +1739,9 @@
 	    !regulator_desc->type == REGULATOR_CURRENT)
 		return ERR_PTR(-EINVAL);
 
+	if (!init_data)
+		return ERR_PTR(-EINVAL);
+
 	rdev = kzalloc(sizeof(struct regulator_dev), GFP_KERNEL);
 	if (rdev == NULL)
 		return ERR_PTR(-ENOMEM);
@@ -1582,7 +1749,7 @@
 	mutex_lock(&regulator_list_mutex);
 
 	mutex_init(&rdev->mutex);
-	rdev->reg_data = reg_data;
+	rdev->reg_data = driver_data;
 	rdev->owner = regulator_desc->owner;
 	rdev->desc = regulator_desc;
 	INIT_LIST_HEAD(&rdev->consumer_list);
@@ -1591,20 +1758,68 @@
 	INIT_LIST_HEAD(&rdev->slist);
 	BLOCKING_INIT_NOTIFIER_HEAD(&rdev->notifier);
 
-	rdev->dev.class = &regulator_class;
-	device_initialize(&rdev->dev);
-	snprintf(rdev->dev.bus_id, sizeof(rdev->dev.bus_id),
-		 "regulator_%ld_%s",
-		 (unsigned long)atomic_inc_return(&regulator_no) - 1,
-		 regulator_desc->name);
+	/* preform any regulator specific init */
+	if (init_data->regulator_init) {
+		ret = init_data->regulator_init(rdev->reg_data);
+		if (ret < 0) {
+			kfree(rdev);
+			rdev = ERR_PTR(ret);
+			goto out;
+		}
+	}
 
-	ret = device_add(&rdev->dev);
-	if (ret == 0)
-		list_add(&rdev->list, &regulator_list);
-	else {
+	/* set regulator constraints */
+	ret = set_machine_constraints(rdev, &init_data->constraints);
+	if (ret < 0) {
 		kfree(rdev);
 		rdev = ERR_PTR(ret);
+		goto out;
 	}
+
+	/* register with sysfs */
+	rdev->dev.class = &regulator_class;
+	rdev->dev.parent = dev;
+	snprintf(rdev->dev.bus_id, sizeof(rdev->dev.bus_id),
+		 "regulator.%d", atomic_inc_return(&regulator_no) - 1);
+	ret = device_register(&rdev->dev);
+	if (ret != 0) {
+		kfree(rdev);
+		rdev = ERR_PTR(ret);
+		goto out;
+	}
+
+	dev_set_drvdata(&rdev->dev, rdev);
+
+	/* set supply regulator if it exists */
+	if (init_data->supply_regulator_dev) {
+		ret = set_supply(rdev,
+			dev_get_drvdata(init_data->supply_regulator_dev));
+		if (ret < 0) {
+			device_unregister(&rdev->dev);
+			kfree(rdev);
+			rdev = ERR_PTR(ret);
+			goto out;
+		}
+	}
+
+	/* add consumers devices */
+	for (i = 0; i < init_data->num_consumer_supplies; i++) {
+		ret = set_consumer_device_supply(rdev,
+			init_data->consumer_supplies[i].dev,
+			init_data->consumer_supplies[i].supply);
+		if (ret < 0) {
+			for (--i; i >= 0; i--)
+				unset_consumer_device_supply(rdev,
+					init_data->consumer_supplies[i].dev);
+			device_unregister(&rdev->dev);
+			kfree(rdev);
+			rdev = ERR_PTR(ret);
+			goto out;
+		}
+	}
+
+	list_add(&rdev->list, &regulator_list);
+out:
 	mutex_unlock(&regulator_list_mutex);
 	return rdev;
 }
@@ -1631,187 +1846,6 @@
 EXPORT_SYMBOL_GPL(regulator_unregister);
 
 /**
- * regulator_set_supply - set regulator supply regulator
- * @regulator: regulator name
- * @supply: supply regulator name
- *
- * Called by platform initialisation code to set the supply regulator for this
- * regulator. This ensures that a regulators supply will also be enabled by the
- * core if it's child is enabled.
- */
-int regulator_set_supply(const char *regulator, const char *supply)
-{
-	struct regulator_dev *rdev, *supply_rdev;
-	int err;
-
-	if (regulator == NULL || supply == NULL)
-		return -EINVAL;
-
-	mutex_lock(&regulator_list_mutex);
-
-	list_for_each_entry(rdev, &regulator_list, list) {
-		if (!strcmp(rdev->desc->name, regulator))
-			goto found_regulator;
-	}
-	mutex_unlock(&regulator_list_mutex);
-	return -ENODEV;
-
-found_regulator:
-	list_for_each_entry(supply_rdev, &regulator_list, list) {
-		if (!strcmp(supply_rdev->desc->name, supply))
-			goto found_supply;
-	}
-	mutex_unlock(&regulator_list_mutex);
-	return -ENODEV;
-
-found_supply:
-	err = sysfs_create_link(&rdev->dev.kobj, &supply_rdev->dev.kobj,
-				"supply");
-	if (err) {
-		printk(KERN_ERR
-		       "%s: could not add device link %s err %d\n",
-		       __func__, supply_rdev->dev.kobj.name, err);
-		       goto out;
-	}
-	rdev->supply = supply_rdev;
-	list_add(&rdev->slist, &supply_rdev->supply_list);
-out:
-	mutex_unlock(&regulator_list_mutex);
-	return err;
-}
-EXPORT_SYMBOL_GPL(regulator_set_supply);
-
-/**
- * regulator_get_supply - get regulator supply regulator
- * @regulator: regulator name
- *
- * Returns the supply supply regulator name or NULL if no supply regulator
- * exists (i.e the regulator is supplied directly from USB, Line, Battery, etc)
- */
-const char *regulator_get_supply(const char *regulator)
-{
-	struct regulator_dev *rdev;
-
-	if (regulator == NULL)
-		return NULL;
-
-	mutex_lock(&regulator_list_mutex);
-	list_for_each_entry(rdev, &regulator_list, list) {
-		if (!strcmp(rdev->desc->name, regulator))
-			goto found;
-	}
-	mutex_unlock(&regulator_list_mutex);
-	return NULL;
-
-found:
-	mutex_unlock(&regulator_list_mutex);
-	if (rdev->supply)
-		return rdev->supply->desc->name;
-	else
-		return NULL;
-}
-EXPORT_SYMBOL_GPL(regulator_get_supply);
-
-/**
- * regulator_set_machine_constraints - sets regulator constraints
- * @regulator: regulator source
- *
- * Allows platform initialisation code to define and constrain
- * regulator circuits e.g. valid voltage/current ranges, etc.  NOTE:
- * Constraints *must* be set by platform code in order for some
- * regulator operations to proceed i.e. set_voltage, set_current_limit,
- * set_mode.
- */
-int regulator_set_machine_constraints(const char *regulator_name,
-	struct regulation_constraints *constraints)
-{
-	struct regulator_dev *rdev;
-	int ret = 0;
-
-	if (regulator_name == NULL)
-		return -EINVAL;
-
-	mutex_lock(&regulator_list_mutex);
-
-	list_for_each_entry(rdev, &regulator_list, list) {
-		if (!strcmp(regulator_name, rdev->desc->name))
-			goto found;
-	}
-	ret = -ENODEV;
-	goto out;
-
-found:
-	mutex_lock(&rdev->mutex);
-	rdev->constraints = constraints;
-
-	/* do we need to apply the constraint voltage */
-	if (rdev->constraints->apply_uV &&
-		rdev->constraints->min_uV == rdev->constraints->max_uV &&
-		rdev->desc->ops->set_voltage) {
-		ret = rdev->desc->ops->set_voltage(rdev,
-			rdev->constraints->min_uV, rdev->constraints->max_uV);
-			if (ret < 0) {
-				printk(KERN_ERR "%s: failed to apply %duV"
-					" constraint\n", __func__,
-					rdev->constraints->min_uV);
-				rdev->constraints = NULL;
-				goto out;
-			}
-	}
-
-	/* are we enabled at boot time by firmware / bootloader */
-	if (rdev->constraints->boot_on)
-		rdev->use_count = 1;
-
-	/* do we need to setup our suspend state */
-	if (constraints->initial_state)
-		ret = suspend_prepare(rdev, constraints->initial_state);
-
-	print_constraints(rdev);
-	mutex_unlock(&rdev->mutex);
-
-out:
-	mutex_unlock(&regulator_list_mutex);
-	return ret;
-}
-EXPORT_SYMBOL_GPL(regulator_set_machine_constraints);
-
-
-/**
- * regulator_set_device_supply: Bind a regulator to a symbolic supply
- * @regulator: regulator source
- * @dev:       device the supply applies to
- * @supply:    symbolic name for supply
- *
- * Allows platform initialisation code to map physical regulator
- * sources to symbolic names for supplies for use by devices.  Devices
- * should use these symbolic names to request regulators, avoiding the
- * need to provide board-specific regulator names as platform data.
- */
-int regulator_set_device_supply(const char *regulator, struct device *dev,
-				const char *supply)
-{
-	struct regulator_map *node;
-
-	if (regulator == NULL || supply == NULL)
-		return -EINVAL;
-
-	node = kmalloc(sizeof(struct regulator_map), GFP_KERNEL);
-	if (node == NULL)
-		return -ENOMEM;
-
-	node->regulator = regulator;
-	node->dev = dev;
-	node->supply = supply;
-
-	mutex_lock(&regulator_list_mutex);
-	list_add(&node->list, &regulator_map_list);
-	mutex_unlock(&regulator_list_mutex);
-	return 0;
-}
-EXPORT_SYMBOL_GPL(regulator_set_device_supply);
-
-/**
  * regulator_suspend_prepare: prepare regulators for system wide suspend
  * @state: system suspend state
  *
@@ -1893,6 +1927,18 @@
 }
 EXPORT_SYMBOL_GPL(rdev_get_id);
 
+struct device *rdev_get_dev(struct regulator_dev *rdev)
+{
+	return &rdev->dev;
+}
+EXPORT_SYMBOL_GPL(rdev_get_dev);
+
+void *regulator_get_init_drvdata(struct regulator_init_data *reg_init_data)
+{
+	return reg_init_data->driver_data;
+}
+EXPORT_SYMBOL_GPL(regulator_get_init_drvdata);
+
 static int __init regulator_init(void)
 {
 	printk(KERN_INFO "regulator: core version %s\n", REGULATOR_VERSION);
diff --git a/drivers/regulator/da903x.c b/drivers/regulator/da903x.c
new file mode 100644
index 0000000..3688e33
--- /dev/null
+++ b/drivers/regulator/da903x.c
@@ -0,0 +1,513 @@
+/*
+ * Regulators driver for Dialog Semiconductor DA903x
+ *
+ * Copyright (C) 2006-2008 Marvell International Ltd.
+ * Copyright (C) 2008 Compulab Ltd.
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/mfd/da903x.h>
+
+/* DA9030 Registers */
+#define DA9030_INVAL		(-1)
+#define DA9030_LDO1011		(0x10)
+#define DA9030_LDO15		(0x11)
+#define DA9030_LDO1416		(0x12)
+#define DA9030_LDO1819		(0x13)
+#define DA9030_LDO17		(0x14)
+#define DA9030_BUCK2DVM1	(0x15)
+#define DA9030_BUCK2DVM2	(0x16)
+#define DA9030_RCTL11		(0x17)
+#define DA9030_RCTL21		(0x18)
+#define DA9030_LDO1		(0x90)
+#define DA9030_LDO23		(0x91)
+#define DA9030_LDO45		(0x92)
+#define DA9030_LDO6		(0x93)
+#define DA9030_LDO78		(0x94)
+#define DA9030_LDO912		(0x95)
+#define DA9030_BUCK		(0x96)
+#define DA9030_RCTL12		(0x97)
+#define DA9030_RCTL22		(0x98)
+#define DA9030_LDO_UNLOCK	(0xa0)
+#define DA9030_LDO_UNLOCK_MASK	(0xe0)
+#define DA9034_OVER1		(0x10)
+
+/* DA9034 Registers */
+#define DA9034_INVAL		(-1)
+#define DA9034_OVER2		(0x11)
+#define DA9034_OVER3		(0x12)
+#define DA9034_LDO643		(0x13)
+#define DA9034_LDO987		(0x14)
+#define DA9034_LDO1110		(0x15)
+#define DA9034_LDO1312		(0x16)
+#define DA9034_LDO1514		(0x17)
+#define DA9034_VCC1		(0x20)
+#define DA9034_ADTV1		(0x23)
+#define DA9034_ADTV2		(0x24)
+#define DA9034_AVRC		(0x25)
+#define DA9034_CDTV1		(0x26)
+#define DA9034_CDTV2		(0x27)
+#define DA9034_CVRC		(0x28)
+#define DA9034_SDTV1		(0x29)
+#define DA9034_SDTV2		(0x2a)
+#define DA9034_SVRC		(0x2b)
+#define DA9034_MDTV1		(0x32)
+#define DA9034_MDTV2		(0x33)
+#define DA9034_MVRC		(0x34)
+
+struct da903x_regulator_info {
+	struct regulator_desc desc;
+
+	int	min_uV;
+	int	max_uV;
+	int	step_uV;
+	int	vol_reg;
+	int	vol_shift;
+	int	vol_nbits;
+	int	update_reg;
+	int	update_bit;
+	int	enable_reg;
+	int	enable_bit;
+};
+
+static inline int check_range(struct da903x_regulator_info *info,
+				int min_uV, int max_uV)
+{
+	if (min_uV < info->min_uV || min_uV > info->max_uV)
+		return -EINVAL;
+
+	return 0;
+}
+
+/* DA9030/DA9034 common operations */
+static int da903x_set_ldo_voltage(struct regulator_dev *rdev,
+				  int min_uV, int max_uV)
+{
+	struct da903x_regulator_info *info = rdev_get_drvdata(rdev);
+	struct device *da9034_dev = rdev_get_dev(rdev)->parent;
+	uint8_t val, mask;
+
+	if (check_range(info, min_uV, max_uV)) {
+		pr_err("invalid voltage range (%d, %d) uV", min_uV, max_uV);
+		return -EINVAL;
+	}
+
+	val = (min_uV - info->min_uV + info->step_uV - 1) / info->step_uV;
+	val <<= info->vol_shift;
+	mask = ((1 << info->vol_nbits) - 1)  << info->vol_shift;
+
+	return da903x_update(da9034_dev, info->vol_reg, val, mask);
+}
+
+static int da903x_get_voltage(struct regulator_dev *rdev)
+{
+	struct da903x_regulator_info *info = rdev_get_drvdata(rdev);
+	struct device *da9034_dev = rdev_get_dev(rdev)->parent;
+	uint8_t val, mask;
+	int ret;
+
+	ret = da903x_read(da9034_dev, info->vol_reg, &val);
+	if (ret)
+		return ret;
+
+	mask = ((1 << info->vol_nbits) - 1)  << info->vol_shift;
+	val = (val & mask) >> info->vol_shift;
+
+	return info->min_uV + info->step_uV * val;
+}
+
+static int da903x_enable(struct regulator_dev *rdev)
+{
+	struct da903x_regulator_info *info = rdev_get_drvdata(rdev);
+	struct device *da9034_dev = rdev_get_dev(rdev)->parent;
+
+	return da903x_set_bits(da9034_dev, info->enable_reg,
+					1 << info->enable_bit);
+}
+
+static int da903x_disable(struct regulator_dev *rdev)
+{
+	struct da903x_regulator_info *info = rdev_get_drvdata(rdev);
+	struct device *da9034_dev = rdev_get_dev(rdev)->parent;
+
+	return da903x_clr_bits(da9034_dev, info->enable_reg,
+					1 << info->enable_bit);
+}
+
+static int da903x_is_enabled(struct regulator_dev *rdev)
+{
+	struct da903x_regulator_info *info = rdev_get_drvdata(rdev);
+	struct device *da9034_dev = rdev_get_dev(rdev)->parent;
+	uint8_t reg_val;
+	int ret;
+
+	ret = da903x_read(da9034_dev, info->enable_reg, &reg_val);
+	if (ret)
+		return ret;
+
+	return reg_val & (1 << info->enable_bit);
+}
+
+/* DA9030 specific operations */
+static int da9030_set_ldo1_15_voltage(struct regulator_dev *rdev,
+				       int min_uV, int max_uV)
+{
+	struct da903x_regulator_info *info = rdev_get_drvdata(rdev);
+	struct device *da903x_dev = rdev_get_dev(rdev)->parent;
+	uint8_t val, mask;
+	int ret;
+
+	if (check_range(info, min_uV, max_uV)) {
+		pr_err("invalid voltage range (%d, %d) uV", min_uV, max_uV);
+		return -EINVAL;
+	}
+
+	val = (min_uV - info->min_uV + info->step_uV - 1) / info->step_uV;
+	val <<= info->vol_shift;
+	mask = ((1 << info->vol_nbits) - 1)  << info->vol_shift;
+	val |= DA9030_LDO_UNLOCK; /* have to set UNLOCK bits */
+	mask |= DA9030_LDO_UNLOCK_MASK;
+
+	/* write twice */
+	ret = da903x_update(da903x_dev, info->vol_reg, val, mask);
+	if (ret)
+		return ret;
+
+	return da903x_update(da903x_dev, info->vol_reg, val, mask);
+}
+
+static int da9030_set_ldo14_voltage(struct regulator_dev *rdev,
+				  int min_uV, int max_uV)
+{
+	struct da903x_regulator_info *info = rdev_get_drvdata(rdev);
+	struct device *da903x_dev = rdev_get_dev(rdev)->parent;
+	uint8_t val, mask;
+	int thresh;
+
+	if (check_range(info, min_uV, max_uV)) {
+		pr_err("invalid voltage range (%d, %d) uV", min_uV, max_uV);
+		return -EINVAL;
+	}
+
+	thresh = (info->max_uV + info->min_uV) / 2;
+	if (min_uV < thresh) {
+		val = (thresh - min_uV + info->step_uV - 1) / info->step_uV;
+		val |= 0x4;
+	} else {
+		val = (min_uV - thresh + info->step_uV - 1) / info->step_uV;
+	}
+
+	val <<= info->vol_shift;
+	mask = ((1 << info->vol_nbits) - 1)  << info->vol_shift;
+
+	return da903x_update(da903x_dev, info->vol_reg, val, mask);
+}
+
+static int da9030_get_ldo14_voltage(struct regulator_dev *rdev)
+{
+	struct da903x_regulator_info *info = rdev_get_drvdata(rdev);
+	struct device *da903x_dev = rdev_get_dev(rdev)->parent;
+	uint8_t val, mask;
+	int ret;
+
+	ret = da903x_read(da903x_dev, info->vol_reg, &val);
+	if (ret)
+		return ret;
+
+	mask = ((1 << info->vol_nbits) - 1)  << info->vol_shift;
+	val = (val & mask) >> info->vol_shift;
+
+	if (val & 0x4)
+		return info->min_uV + info->step_uV * (3 - (val & ~0x4));
+	else
+		return (info->max_uV + info->min_uV) / 2 +
+			info->step_uV * (val & ~0x4);
+}
+
+/* DA9034 specific operations */
+static int da9034_set_dvc_voltage(struct regulator_dev *rdev,
+				  int min_uV, int max_uV)
+{
+	struct da903x_regulator_info *info = rdev_get_drvdata(rdev);
+	struct device *da9034_dev = rdev_get_dev(rdev)->parent;
+	uint8_t val, mask;
+	int ret;
+
+	if (check_range(info, min_uV, max_uV)) {
+		pr_err("invalid voltage range (%d, %d) uV", min_uV, max_uV);
+		return -EINVAL;
+	}
+
+	val = (min_uV - info->min_uV + info->step_uV - 1) / info->step_uV;
+	val <<= info->vol_shift;
+	mask = ((1 << info->vol_nbits) - 1)  << info->vol_shift;
+
+	ret = da903x_update(da9034_dev, info->vol_reg, val, mask);
+	if (ret)
+		return ret;
+
+	ret = da903x_set_bits(da9034_dev, info->update_reg,
+					1 << info->update_bit);
+	return ret;
+}
+
+static int da9034_set_ldo12_voltage(struct regulator_dev *rdev,
+				    int min_uV, int max_uV)
+{
+	struct da903x_regulator_info *info = rdev_get_drvdata(rdev);
+	struct device *da9034_dev = rdev_get_dev(rdev)->parent;
+	uint8_t val, mask;
+
+	if (check_range(info, min_uV, max_uV)) {
+		pr_err("invalid voltage range (%d, %d) uV", min_uV, max_uV);
+		return -EINVAL;
+	}
+
+	val = (min_uV - info->min_uV + info->step_uV - 1) / info->step_uV;
+	val = (val > 7 || val < 20) ? 8 : val - 12;
+	val <<= info->vol_shift;
+	mask = ((1 << info->vol_nbits) - 1)  << info->vol_shift;
+
+	return da903x_update(da9034_dev, info->vol_reg, val, mask);
+}
+
+static int da9034_get_ldo12_voltage(struct regulator_dev *rdev)
+{
+	struct da903x_regulator_info *info = rdev_get_drvdata(rdev);
+	struct device *da9034_dev = rdev_get_dev(rdev)->parent;
+	uint8_t val, mask;
+	int ret;
+
+	ret = da903x_read(da9034_dev, info->vol_reg, &val);
+	if (ret)
+		return ret;
+
+	mask = ((1 << info->vol_nbits) - 1)  << info->vol_shift;
+	val = (val & mask) >> info->vol_shift;
+
+	if (val >= 8)
+		return 2700000 + info->step_uV * (val - 8);
+
+	return info->min_uV + info->step_uV * val;
+}
+
+static struct regulator_ops da903x_regulator_ldo_ops = {
+	.set_voltage	= da903x_set_ldo_voltage,
+	.get_voltage	= da903x_get_voltage,
+	.enable		= da903x_enable,
+	.disable	= da903x_disable,
+	.is_enabled	= da903x_is_enabled,
+};
+
+/* NOTE: this is dedicated for the insane DA9030 LDO14 */
+static struct regulator_ops da9030_regulator_ldo14_ops = {
+	.set_voltage	= da9030_set_ldo14_voltage,
+	.get_voltage	= da9030_get_ldo14_voltage,
+	.enable		= da903x_enable,
+	.disable	= da903x_disable,
+	.is_enabled	= da903x_is_enabled,
+};
+
+/* NOTE: this is dedicated for the DA9030 LDO1 and LDO15 that have locks  */
+static struct regulator_ops da9030_regulator_ldo1_15_ops = {
+	.set_voltage	= da9030_set_ldo1_15_voltage,
+	.get_voltage	= da903x_get_voltage,
+	.enable		= da903x_enable,
+	.disable	= da903x_disable,
+	.is_enabled	= da903x_is_enabled,
+};
+
+static struct regulator_ops da9034_regulator_dvc_ops = {
+	.set_voltage	= da9034_set_dvc_voltage,
+	.get_voltage	= da903x_get_voltage,
+	.enable		= da903x_enable,
+	.disable	= da903x_disable,
+	.is_enabled	= da903x_is_enabled,
+};
+
+/* NOTE: this is dedicated for the insane LDO12 */
+static struct regulator_ops da9034_regulator_ldo12_ops = {
+	.set_voltage	= da9034_set_ldo12_voltage,
+	.get_voltage	= da9034_get_ldo12_voltage,
+	.enable		= da903x_enable,
+	.disable	= da903x_disable,
+	.is_enabled	= da903x_is_enabled,
+};
+
+#define DA903x_LDO(_pmic, _id, min, max, step, vreg, shift, nbits, ereg, ebit)	\
+{									\
+	.desc	= {							\
+		.name	= "LDO" #_id,					\
+		.ops	= &da903x_regulator_ldo_ops,			\
+		.type	= REGULATOR_VOLTAGE,				\
+		.id	= _pmic##_ID_LDO##_id,				\
+		.owner	= THIS_MODULE,					\
+	},								\
+	.min_uV		= (min) * 1000,					\
+	.max_uV		= (max) * 1000,					\
+	.step_uV	= (step) * 1000,				\
+	.vol_reg	= _pmic##_##vreg,				\
+	.vol_shift	= (shift),					\
+	.vol_nbits	= (nbits),					\
+	.enable_reg	= _pmic##_##ereg,				\
+	.enable_bit	= (ebit),					\
+}
+
+#define DA9034_DVC(_id, min, max, step, vreg, nbits, ureg, ubit, ereg, ebit) \
+{									\
+	.desc	= {							\
+		.name	= #_id,						\
+		.ops	= &da9034_regulator_dvc_ops,			\
+		.type	= REGULATOR_VOLTAGE,				\
+		.id	= DA9034_ID_##_id,				\
+		.owner	= THIS_MODULE,					\
+	},								\
+	.min_uV		= (min) * 1000,					\
+	.max_uV		= (max) * 1000,					\
+	.step_uV	= (step) * 1000,				\
+	.vol_reg	= DA9034_##vreg,				\
+	.vol_shift	= (0),						\
+	.vol_nbits	= (nbits),					\
+	.update_reg	= DA9034_##ureg,				\
+	.update_bit	= (ubit),					\
+	.enable_reg	= DA9034_##ereg,				\
+	.enable_bit	= (ebit),					\
+}
+
+#define DA9034_LDO(_id, min, max, step, vreg, shift, nbits, ereg, ebit)	\
+	DA903x_LDO(DA9034, _id, min, max, step, vreg, shift, nbits, ereg, ebit)
+
+#define DA9030_LDO(_id, min, max, step, vreg, shift, nbits, ereg, ebit)	\
+	DA903x_LDO(DA9030, _id, min, max, step, vreg, shift, nbits, ereg, ebit)
+
+static struct da903x_regulator_info da903x_regulator_info[] = {
+	/* DA9030 */
+	DA9030_LDO( 1, 1200, 3200, 100,    LDO1, 0, 5, RCTL12, 1),
+	DA9030_LDO( 2, 1800, 3200, 100,   LDO23, 0, 4, RCTL12, 2),
+	DA9030_LDO( 3, 1800, 3200, 100,   LDO23, 4, 4, RCTL12, 3),
+	DA9030_LDO( 4, 1800, 3200, 100,   LDO45, 0, 4, RCTL12, 4),
+	DA9030_LDO( 5, 1800, 3200, 100,   LDO45, 4, 4, RCTL12, 5),
+	DA9030_LDO( 6, 1800, 3200, 100,    LDO6, 0, 4, RCTL12, 6),
+	DA9030_LDO( 7, 1800, 3200, 100,   LDO78, 0, 4, RCTL12, 7),
+	DA9030_LDO( 8, 1800, 3200, 100,   LDO78, 4, 4, RCTL22, 0),
+	DA9030_LDO( 9, 1800, 3200, 100,  LDO912, 0, 4, RCTL22, 1),
+	DA9030_LDO(10, 1800, 3200, 100, LDO1011, 0, 4, RCTL22, 2),
+	DA9030_LDO(11, 1800, 3200, 100, LDO1011, 4, 4, RCTL22, 3),
+	DA9030_LDO(12, 1800, 3200, 100,  LDO912, 4, 4, RCTL22, 4),
+	DA9030_LDO(14, 2760, 2940,  30, LDO1416, 0, 3, RCTL11, 4),
+	DA9030_LDO(15, 1100, 2650,  50,	  LDO15, 0, 5, RCTL11, 5),
+	DA9030_LDO(16, 1100, 2650,  50, LDO1416, 3, 5, RCTL11, 6),
+	DA9030_LDO(17, 1800, 3200, 100,   LDO17, 0, 4, RCTL11, 7),
+	DA9030_LDO(18, 1800, 3200, 100, LDO1819, 0, 4, RCTL21, 2),
+	DA9030_LDO(19, 1800, 3200, 100, LDO1819, 4, 4, RCTL21, 1),
+	DA9030_LDO(13, 2100, 2100, 0, INVAL, 0, 0, RCTL11, 3), /* fixed @2.1V */
+
+	/* DA9034 */
+	DA9034_DVC(BUCK1, 725, 1500, 25, ADTV1, 5, VCC1, 0, OVER1, 0),
+	DA9034_DVC(BUCK2, 725, 1500, 25, CDTV1, 5, VCC1, 2, OVER1, 1),
+	DA9034_DVC(LDO2,  725, 1500, 25, SDTV1, 5, VCC1, 4, OVER1, 2),
+	DA9034_DVC(LDO1, 1700, 2075, 25, MDTV1, 4, VCC1, 6, OVER3, 4),
+
+	DA9034_LDO( 3, 1800, 3300, 100,  LDO643, 0, 4, OVER3, 5),
+	DA9034_LDO( 4, 1800, 2900,1100,  LDO643, 4, 1, OVER3, 6),
+	DA9034_LDO( 6, 2500, 2850,  50,  LDO643, 5, 3, OVER2, 0),
+	DA9034_LDO( 7, 2700, 3050,  50,  LDO987, 0, 3, OVER2, 1),
+	DA9034_LDO( 8, 2700, 2850,  50,  LDO987, 3, 2, OVER2, 2),
+	DA9034_LDO( 9, 2700, 3050,  50,  LDO987, 5, 3, OVER2, 3),
+	DA9034_LDO(10, 2700, 3050,  50, LDO1110, 0, 3, OVER2, 4),
+	DA9034_LDO(11, 1800, 3300, 100, LDO1110, 4, 4, OVER2, 5),
+	DA9034_LDO(12, 1700, 3050,  50, LDO1312, 0, 4, OVER3, 6),
+	DA9034_LDO(13, 1800, 3300, 100, LDO1312, 4, 4, OVER2, 7),
+	DA9034_LDO(14, 1800, 3300, 100, LDO1514, 0, 4, OVER3, 0),
+	DA9034_LDO(15, 1800, 3300, 100, LDO1514, 4, 4, OVER3, 1),
+	DA9034_LDO(5, 3100, 3100, 0, INVAL, 0, 0, OVER3, 7), /* fixed @3.1V */
+};
+
+static inline struct da903x_regulator_info *find_regulator_info(int id)
+{
+	struct da903x_regulator_info *ri;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(da903x_regulator_info); i++) {
+		ri = &da903x_regulator_info[i];
+		if (ri->desc.id == id)
+			return ri;
+	}
+	return NULL;
+}
+
+static int __devinit da903x_regulator_probe(struct platform_device *pdev)
+{
+	struct da903x_regulator_info *ri = NULL;
+	struct regulator_dev *rdev;
+
+	ri = find_regulator_info(pdev->id);
+	if (ri == NULL) {
+		dev_err(&pdev->dev, "invalid regulator ID specified\n");
+		return -EINVAL;
+	}
+
+	/* Workaround for the weird LDO12 voltage setting */
+	if (ri->desc.id == DA9034_ID_LDO12)
+		ri->desc.ops = &da9034_regulator_ldo12_ops;
+
+	if (ri->desc.id == DA9030_ID_LDO14)
+		ri->desc.ops = &da9030_regulator_ldo14_ops;
+
+	if (ri->desc.id == DA9030_ID_LDO1 || ri->desc.id == DA9030_ID_LDO15)
+		ri->desc.ops = &da9030_regulator_ldo1_15_ops;
+
+	rdev = regulator_register(&ri->desc, pdev->dev.parent, ri);
+	if (IS_ERR(rdev)) {
+		dev_err(&pdev->dev, "failed to register regulator %s\n",
+				ri->desc.name);
+		return PTR_ERR(rdev);
+	}
+
+	platform_set_drvdata(pdev, rdev);
+	return 0;
+}
+
+static int __devexit da903x_regulator_remove(struct platform_device *pdev)
+{
+	struct regulator_dev *rdev = platform_get_drvdata(pdev);
+
+	regulator_unregister(rdev);
+	return 0;
+}
+
+static struct platform_driver da903x_regulator_driver = {
+	.driver	= {
+		.name	= "da903x-regulator",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= da903x_regulator_probe,
+	.remove		= da903x_regulator_remove,
+};
+
+static int __init da903x_regulator_init(void)
+{
+	return platform_driver_register(&da903x_regulator_driver);
+}
+module_init(da903x_regulator_init);
+
+static void __exit da903x_regulator_exit(void)
+{
+	platform_driver_unregister(&da903x_regulator_driver);
+}
+module_exit(da903x_regulator_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>"
+	      "Mike Rapoport <mike@compulab.co.il>");
+MODULE_DESCRIPTION("Regulator Driver for Dialog Semiconductor DA903X PMIC");
+MODULE_ALIAS("platform:da903x-regulator");
diff --git a/drivers/regulator/wm8350-regulator.c b/drivers/regulator/wm8350-regulator.c
new file mode 100644
index 0000000..1f44b17
--- /dev/null
+++ b/drivers/regulator/wm8350-regulator.c
@@ -0,0 +1,1431 @@
+/*
+ * wm8350.c  --  Voltage and current regulation for the Wolfson WM8350 PMIC
+ *
+ * Copyright 2007, 2008 Wolfson Microelectronics PLC.
+ *
+ * Author: Liam Girdwood
+ *         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 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/init.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/mfd/wm8350/core.h>
+#include <linux/mfd/wm8350/pmic.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+
+/* Microamps */
+static const int isink_cur[] = {
+	4,
+	5,
+	6,
+	7,
+	8,
+	10,
+	11,
+	14,
+	16,
+	19,
+	23,
+	27,
+	32,
+	39,
+	46,
+	54,
+	65,
+	77,
+	92,
+	109,
+	130,
+	154,
+	183,
+	218,
+	259,
+	308,
+	367,
+	436,
+	518,
+	616,
+	733,
+	872,
+	1037,
+	1233,
+	1466,
+	1744,
+	2073,
+	2466,
+	2933,
+	3487,
+	4147,
+	4932,
+	5865,
+	6975,
+	8294,
+	9864,
+	11730,
+	13949,
+	16589,
+	19728,
+	23460,
+	27899,
+	33178,
+	39455,
+	46920,
+	55798,
+	66355,
+	78910,
+	93840,
+	111596,
+	132710,
+	157820,
+	187681,
+	223191
+};
+
+static int get_isink_val(int min_uA, int max_uA, u16 *setting)
+{
+	int i;
+
+	for (i = ARRAY_SIZE(isink_cur) - 1; i >= 0; i--) {
+		if (min_uA <= isink_cur[i] && max_uA >= isink_cur[i]) {
+			*setting = i;
+			return 0;
+		}
+	}
+	return -EINVAL;
+}
+
+static inline int wm8350_ldo_val_to_mvolts(unsigned int val)
+{
+	if (val < 16)
+		return (val * 50) + 900;
+	else
+		return ((val - 16) * 100) + 1800;
+
+}
+
+static inline unsigned int wm8350_ldo_mvolts_to_val(int mV)
+{
+	if (mV < 1800)
+		return (mV - 900) / 50;
+	else
+		return ((mV - 1800) / 100) + 16;
+}
+
+static inline int wm8350_dcdc_val_to_mvolts(unsigned int val)
+{
+	return (val * 25) + 850;
+}
+
+static inline unsigned int wm8350_dcdc_mvolts_to_val(int mV)
+{
+	return (mV - 850) / 25;
+}
+
+static int wm8350_isink_set_current(struct regulator_dev *rdev, int min_uA,
+	int max_uA)
+{
+	struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+	int isink = rdev_get_id(rdev);
+	u16 val, setting;
+	int ret;
+
+	ret = get_isink_val(min_uA, max_uA, &setting);
+	if (ret != 0)
+		return ret;
+
+	switch (isink) {
+	case WM8350_ISINK_A:
+		val = wm8350_reg_read(wm8350, WM8350_CURRENT_SINK_DRIVER_A) &
+		    ~WM8350_CS1_ISEL_MASK;
+		wm8350_reg_write(wm8350, WM8350_CURRENT_SINK_DRIVER_A,
+				 val | setting);
+		break;
+	case WM8350_ISINK_B:
+		val = wm8350_reg_read(wm8350, WM8350_CURRENT_SINK_DRIVER_B) &
+		    ~WM8350_CS1_ISEL_MASK;
+		wm8350_reg_write(wm8350, WM8350_CURRENT_SINK_DRIVER_B,
+				 val | setting);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int wm8350_isink_get_current(struct regulator_dev *rdev)
+{
+	struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+	int isink = rdev_get_id(rdev);
+	u16 val;
+
+	switch (isink) {
+	case WM8350_ISINK_A:
+		val = wm8350_reg_read(wm8350, WM8350_CURRENT_SINK_DRIVER_A) &
+		    WM8350_CS1_ISEL_MASK;
+		break;
+	case WM8350_ISINK_B:
+		val = wm8350_reg_read(wm8350, WM8350_CURRENT_SINK_DRIVER_B) &
+		    WM8350_CS1_ISEL_MASK;
+		break;
+	default:
+		return 0;
+	}
+
+	return (isink_cur[val] + 50) / 100;
+}
+
+/* turn on ISINK followed by DCDC */
+static int wm8350_isink_enable(struct regulator_dev *rdev)
+{
+	struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+	int isink = rdev_get_id(rdev);
+
+	switch (isink) {
+	case WM8350_ISINK_A:
+		switch (wm8350->pmic.isink_A_dcdc) {
+		case WM8350_DCDC_2:
+		case WM8350_DCDC_5:
+			wm8350_set_bits(wm8350, WM8350_POWER_MGMT_7,
+					WM8350_CS1_ENA);
+			wm8350_set_bits(wm8350, WM8350_CSA_FLASH_CONTROL,
+					WM8350_CS1_DRIVE);
+			wm8350_set_bits(wm8350, WM8350_DCDC_LDO_REQUESTED,
+					1 << (wm8350->pmic.isink_A_dcdc -
+					      WM8350_DCDC_1));
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	case WM8350_ISINK_B:
+		switch (wm8350->pmic.isink_B_dcdc) {
+		case WM8350_DCDC_2:
+		case WM8350_DCDC_5:
+			wm8350_set_bits(wm8350, WM8350_POWER_MGMT_7,
+					WM8350_CS2_ENA);
+			wm8350_set_bits(wm8350, WM8350_CSB_FLASH_CONTROL,
+					WM8350_CS2_DRIVE);
+			wm8350_set_bits(wm8350, WM8350_DCDC_LDO_REQUESTED,
+					1 << (wm8350->pmic.isink_B_dcdc -
+					      WM8350_DCDC_1));
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int wm8350_isink_disable(struct regulator_dev *rdev)
+{
+	struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+	int isink = rdev_get_id(rdev);
+
+	switch (isink) {
+	case WM8350_ISINK_A:
+		switch (wm8350->pmic.isink_A_dcdc) {
+		case WM8350_DCDC_2:
+		case WM8350_DCDC_5:
+			wm8350_clear_bits(wm8350, WM8350_DCDC_LDO_REQUESTED,
+					  1 << (wm8350->pmic.isink_A_dcdc -
+						WM8350_DCDC_1));
+			wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_7,
+					  WM8350_CS1_ENA);
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	case WM8350_ISINK_B:
+		switch (wm8350->pmic.isink_B_dcdc) {
+		case WM8350_DCDC_2:
+		case WM8350_DCDC_5:
+			wm8350_clear_bits(wm8350, WM8350_DCDC_LDO_REQUESTED,
+					  1 << (wm8350->pmic.isink_B_dcdc -
+						WM8350_DCDC_1));
+			wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_7,
+					  WM8350_CS2_ENA);
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int wm8350_isink_is_enabled(struct regulator_dev *rdev)
+{
+	struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+	int isink = rdev_get_id(rdev);
+
+	switch (isink) {
+	case WM8350_ISINK_A:
+		return wm8350_reg_read(wm8350, WM8350_CURRENT_SINK_DRIVER_A) &
+		    0x8000;
+	case WM8350_ISINK_B:
+		return wm8350_reg_read(wm8350, WM8350_CURRENT_SINK_DRIVER_B) &
+		    0x8000;
+	}
+	return -EINVAL;
+}
+
+int wm8350_isink_set_flash(struct wm8350 *wm8350, int isink, u16 mode,
+			   u16 trigger, u16 duration, u16 on_ramp, u16 off_ramp,
+			   u16 drive)
+{
+	switch (isink) {
+	case WM8350_ISINK_A:
+		wm8350_reg_write(wm8350, WM8350_CSA_FLASH_CONTROL,
+				 (mode ? WM8350_CS1_FLASH_MODE : 0) |
+				 (trigger ? WM8350_CS1_TRIGSRC : 0) |
+				 duration | on_ramp | off_ramp | drive);
+		break;
+	case WM8350_ISINK_B:
+		wm8350_reg_write(wm8350, WM8350_CSB_FLASH_CONTROL,
+				 (mode ? WM8350_CS2_FLASH_MODE : 0) |
+				 (trigger ? WM8350_CS2_TRIGSRC : 0) |
+				 duration | on_ramp | off_ramp | drive);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+EXPORT_SYMBOL_GPL(wm8350_isink_set_flash);
+
+static int wm8350_dcdc_set_voltage(struct regulator_dev *rdev, int min_uV,
+	int max_uV)
+{
+	struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+	int volt_reg, dcdc = rdev_get_id(rdev), mV,
+		min_mV = min_uV / 1000, max_mV = max_uV / 1000;
+	u16 val;
+
+	if (min_mV < 850 || min_mV > 4025)
+		return -EINVAL;
+	if (max_mV < 850 || max_mV > 4025)
+		return -EINVAL;
+
+	/* step size is 25mV */
+	mV = (min_mV - 826) / 25;
+	if (wm8350_dcdc_val_to_mvolts(mV) > max_mV)
+		return -EINVAL;
+	BUG_ON(wm8350_dcdc_val_to_mvolts(mV) < min_mV);
+
+	switch (dcdc) {
+	case WM8350_DCDC_1:
+		volt_reg = WM8350_DCDC1_CONTROL;
+		break;
+	case WM8350_DCDC_3:
+		volt_reg = WM8350_DCDC3_CONTROL;
+		break;
+	case WM8350_DCDC_4:
+		volt_reg = WM8350_DCDC4_CONTROL;
+		break;
+	case WM8350_DCDC_6:
+		volt_reg = WM8350_DCDC6_CONTROL;
+		break;
+	case WM8350_DCDC_2:
+	case WM8350_DCDC_5:
+	default:
+		return -EINVAL;
+	}
+
+	/* all DCDCs have same mV bits */
+	val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_DC1_VSEL_MASK;
+	wm8350_reg_write(wm8350, volt_reg, val | mV);
+	return 0;
+}
+
+static int wm8350_dcdc_get_voltage(struct regulator_dev *rdev)
+{
+	struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+	int volt_reg, dcdc = rdev_get_id(rdev);
+	u16 val;
+
+	switch (dcdc) {
+	case WM8350_DCDC_1:
+		volt_reg = WM8350_DCDC1_CONTROL;
+		break;
+	case WM8350_DCDC_3:
+		volt_reg = WM8350_DCDC3_CONTROL;
+		break;
+	case WM8350_DCDC_4:
+		volt_reg = WM8350_DCDC4_CONTROL;
+		break;
+	case WM8350_DCDC_6:
+		volt_reg = WM8350_DCDC6_CONTROL;
+		break;
+	case WM8350_DCDC_2:
+	case WM8350_DCDC_5:
+	default:
+		return -EINVAL;
+	}
+
+	/* all DCDCs have same mV bits */
+	val = wm8350_reg_read(wm8350, volt_reg) & WM8350_DC1_VSEL_MASK;
+	return wm8350_dcdc_val_to_mvolts(val) * 1000;
+}
+
+static int wm8350_dcdc_set_suspend_voltage(struct regulator_dev *rdev, int uV)
+{
+	struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+	int volt_reg, mV = uV / 1000, dcdc = rdev_get_id(rdev);
+	u16 val;
+
+	dev_dbg(wm8350->dev, "%s %d mV %d\n", __func__, dcdc, mV);
+
+	if (mV && (mV < 850 || mV > 4025)) {
+		dev_err(wm8350->dev,
+			"DCDC%d suspend voltage %d mV out of range\n",
+			dcdc, mV);
+		return -EINVAL;
+	}
+	if (mV == 0)
+		mV = 850;
+
+	switch (dcdc) {
+	case WM8350_DCDC_1:
+		volt_reg = WM8350_DCDC1_LOW_POWER;
+		break;
+	case WM8350_DCDC_3:
+		volt_reg = WM8350_DCDC3_LOW_POWER;
+		break;
+	case WM8350_DCDC_4:
+		volt_reg = WM8350_DCDC4_LOW_POWER;
+		break;
+	case WM8350_DCDC_6:
+		volt_reg = WM8350_DCDC6_LOW_POWER;
+		break;
+	case WM8350_DCDC_2:
+	case WM8350_DCDC_5:
+	default:
+		return -EINVAL;
+	}
+
+	/* all DCDCs have same mV bits */
+	val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_DC1_VSEL_MASK;
+	wm8350_reg_write(wm8350, volt_reg,
+			 val | wm8350_dcdc_mvolts_to_val(mV));
+	return 0;
+}
+
+static int wm8350_dcdc_set_suspend_enable(struct regulator_dev *rdev)
+{
+	struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+	int dcdc = rdev_get_id(rdev);
+	u16 val;
+
+	switch (dcdc) {
+	case WM8350_DCDC_1:
+		val = wm8350_reg_read(wm8350, WM8350_DCDC1_LOW_POWER)
+			& ~WM8350_DCDC_HIB_MODE_MASK;
+		wm8350_reg_write(wm8350, WM8350_DCDC1_LOW_POWER,
+			wm8350->pmic.dcdc1_hib_mode);
+		break;
+	case WM8350_DCDC_3:
+		val = wm8350_reg_read(wm8350, WM8350_DCDC3_LOW_POWER)
+			& ~WM8350_DCDC_HIB_MODE_MASK;
+		wm8350_reg_write(wm8350, WM8350_DCDC3_LOW_POWER,
+			wm8350->pmic.dcdc3_hib_mode);
+		break;
+	case WM8350_DCDC_4:
+		val = wm8350_reg_read(wm8350, WM8350_DCDC4_LOW_POWER)
+			& ~WM8350_DCDC_HIB_MODE_MASK;
+		wm8350_reg_write(wm8350, WM8350_DCDC4_LOW_POWER,
+			wm8350->pmic.dcdc4_hib_mode);
+		break;
+	case WM8350_DCDC_6:
+		val = wm8350_reg_read(wm8350, WM8350_DCDC6_LOW_POWER)
+			& ~WM8350_DCDC_HIB_MODE_MASK;
+		wm8350_reg_write(wm8350, WM8350_DCDC6_LOW_POWER,
+			wm8350->pmic.dcdc6_hib_mode);
+		break;
+	case WM8350_DCDC_2:
+	case WM8350_DCDC_5:
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int wm8350_dcdc_set_suspend_disable(struct regulator_dev *rdev)
+{
+	struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+	int dcdc = rdev_get_id(rdev);
+	u16 val;
+
+	switch (dcdc) {
+	case WM8350_DCDC_1:
+		val = wm8350_reg_read(wm8350, WM8350_DCDC1_LOW_POWER);
+		wm8350->pmic.dcdc1_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK;
+		wm8350_reg_write(wm8350, WM8350_DCDC1_LOW_POWER,
+			WM8350_DCDC_HIB_MODE_DIS);
+		break;
+	case WM8350_DCDC_3:
+		val = wm8350_reg_read(wm8350, WM8350_DCDC3_LOW_POWER);
+		wm8350->pmic.dcdc3_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK;
+		wm8350_reg_write(wm8350, WM8350_DCDC3_LOW_POWER,
+			WM8350_DCDC_HIB_MODE_DIS);
+		break;
+	case WM8350_DCDC_4:
+		val = wm8350_reg_read(wm8350, WM8350_DCDC4_LOW_POWER);
+		wm8350->pmic.dcdc4_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK;
+		wm8350_reg_write(wm8350, WM8350_DCDC4_LOW_POWER,
+			WM8350_DCDC_HIB_MODE_DIS);
+		break;
+	case WM8350_DCDC_6:
+		val = wm8350_reg_read(wm8350, WM8350_DCDC6_LOW_POWER);
+		wm8350->pmic.dcdc6_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK;
+		wm8350_reg_write(wm8350, WM8350_DCDC6_LOW_POWER,
+			WM8350_DCDC_HIB_MODE_DIS);
+		break;
+	case WM8350_DCDC_2:
+	case WM8350_DCDC_5:
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int wm8350_dcdc25_set_suspend_enable(struct regulator_dev *rdev)
+{
+	struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+	int dcdc = rdev_get_id(rdev);
+	u16 val;
+
+	switch (dcdc) {
+	case WM8350_DCDC_2:
+		val = wm8350_reg_read(wm8350, WM8350_DCDC2_CONTROL)
+		    & ~WM8350_DC2_HIB_MODE_MASK;
+		wm8350_reg_write(wm8350, WM8350_DCDC2_CONTROL, val |
+				 WM8350_DC2_HIB_MODE_ACTIVE);
+		break;
+	case WM8350_DCDC_5:
+		val = wm8350_reg_read(wm8350, WM8350_DCDC5_CONTROL)
+		    & ~WM8350_DC2_HIB_MODE_MASK;
+		wm8350_reg_write(wm8350, WM8350_DCDC5_CONTROL, val |
+				 WM8350_DC5_HIB_MODE_ACTIVE);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int wm8350_dcdc25_set_suspend_disable(struct regulator_dev *rdev)
+{
+	struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+	int dcdc = rdev_get_id(rdev);
+	u16 val;
+
+	switch (dcdc) {
+	case WM8350_DCDC_2:
+		val = wm8350_reg_read(wm8350, WM8350_DCDC2_CONTROL)
+		    & ~WM8350_DC2_HIB_MODE_MASK;
+		wm8350_reg_write(wm8350, WM8350_DCDC2_CONTROL, val |
+				 WM8350_DC2_HIB_MODE_DISABLE);
+		break;
+	case WM8350_DCDC_5:
+		val = wm8350_reg_read(wm8350, WM8350_DCDC5_CONTROL)
+		    & ~WM8350_DC2_HIB_MODE_MASK;
+		wm8350_reg_write(wm8350, WM8350_DCDC5_CONTROL, val |
+				 WM8350_DC2_HIB_MODE_DISABLE);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int wm8350_dcdc_set_suspend_mode(struct regulator_dev *rdev,
+	unsigned int mode)
+{
+	struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+	int dcdc = rdev_get_id(rdev);
+	u16 *hib_mode;
+
+	switch (dcdc) {
+	case WM8350_DCDC_1:
+		hib_mode = &wm8350->pmic.dcdc1_hib_mode;
+		break;
+	case WM8350_DCDC_3:
+		hib_mode = &wm8350->pmic.dcdc3_hib_mode;
+		break;
+	case WM8350_DCDC_4:
+		hib_mode = &wm8350->pmic.dcdc4_hib_mode;
+		break;
+	case WM8350_DCDC_6:
+		hib_mode = &wm8350->pmic.dcdc6_hib_mode;
+		break;
+	case WM8350_DCDC_2:
+	case WM8350_DCDC_5:
+	default:
+		return -EINVAL;
+	}
+
+	switch (mode) {
+	case REGULATOR_MODE_NORMAL:
+		*hib_mode = WM8350_DCDC_HIB_MODE_IMAGE;
+		break;
+	case REGULATOR_MODE_IDLE:
+		*hib_mode = WM8350_DCDC_HIB_MODE_STANDBY;
+		break;
+	case REGULATOR_MODE_STANDBY:
+		*hib_mode = WM8350_DCDC_HIB_MODE_LDO_IM;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int wm8350_ldo_set_suspend_voltage(struct regulator_dev *rdev, int uV)
+{
+	struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+	int volt_reg, mV = uV / 1000, ldo = rdev_get_id(rdev);
+	u16 val;
+
+	dev_dbg(wm8350->dev, "%s %d mV %d\n", __func__, ldo, mV);
+
+	if (mV < 900 || mV > 3300) {
+		dev_err(wm8350->dev, "LDO%d voltage %d mV out of range\n",
+			ldo, mV);
+		return -EINVAL;
+	}
+
+	switch (ldo) {
+	case WM8350_LDO_1:
+		volt_reg = WM8350_LDO1_LOW_POWER;
+		break;
+	case WM8350_LDO_2:
+		volt_reg = WM8350_LDO2_LOW_POWER;
+		break;
+	case WM8350_LDO_3:
+		volt_reg = WM8350_LDO3_LOW_POWER;
+		break;
+	case WM8350_LDO_4:
+		volt_reg = WM8350_LDO4_LOW_POWER;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* all LDOs have same mV bits */
+	val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_LDO1_VSEL_MASK;
+	wm8350_reg_write(wm8350, volt_reg,
+			 val | wm8350_ldo_mvolts_to_val(mV));
+	return 0;
+}
+
+static int wm8350_ldo_set_suspend_enable(struct regulator_dev *rdev)
+{
+	struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+	int volt_reg, ldo = rdev_get_id(rdev);
+	u16 val;
+
+	switch (ldo) {
+	case WM8350_LDO_1:
+		volt_reg = WM8350_LDO1_LOW_POWER;
+		break;
+	case WM8350_LDO_2:
+		volt_reg = WM8350_LDO2_LOW_POWER;
+		break;
+	case WM8350_LDO_3:
+		volt_reg = WM8350_LDO3_LOW_POWER;
+		break;
+	case WM8350_LDO_4:
+		volt_reg = WM8350_LDO4_LOW_POWER;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* all LDOs have same mV bits */
+	val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_LDO1_HIB_MODE_MASK;
+	wm8350_reg_write(wm8350, volt_reg, val);
+	return 0;
+}
+
+static int wm8350_ldo_set_suspend_disable(struct regulator_dev *rdev)
+{
+	struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+	int volt_reg, ldo = rdev_get_id(rdev);
+	u16 val;
+
+	switch (ldo) {
+	case WM8350_LDO_1:
+		volt_reg = WM8350_LDO1_LOW_POWER;
+		break;
+	case WM8350_LDO_2:
+		volt_reg = WM8350_LDO2_LOW_POWER;
+		break;
+	case WM8350_LDO_3:
+		volt_reg = WM8350_LDO3_LOW_POWER;
+		break;
+	case WM8350_LDO_4:
+		volt_reg = WM8350_LDO4_LOW_POWER;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* all LDOs have same mV bits */
+	val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_LDO1_HIB_MODE_MASK;
+	wm8350_reg_write(wm8350, volt_reg, WM8350_LDO1_HIB_MODE_DIS);
+	return 0;
+}
+
+static int wm8350_ldo_set_voltage(struct regulator_dev *rdev, int min_uV,
+	int max_uV)
+{
+	struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+	int volt_reg, ldo = rdev_get_id(rdev), mV, min_mV = min_uV / 1000,
+		max_mV = max_uV / 1000;
+	u16 val;
+
+	if (min_mV < 900 || min_mV > 3300)
+		return -EINVAL;
+	if (max_mV < 900 || max_mV > 3300)
+		return -EINVAL;
+
+	if (min_mV < 1800) {
+		/* step size is 50mV < 1800mV */
+		mV = (min_mV - 851) / 50;
+		if (wm8350_ldo_val_to_mvolts(mV) > max_mV)
+			return -EINVAL;
+		BUG_ON(wm8350_ldo_val_to_mvolts(mV) < min_mV);
+	} else {
+		/* step size is 100mV > 1800mV */
+		mV = ((min_mV - 1701) / 100) + 16;
+		if (wm8350_ldo_val_to_mvolts(mV) > max_mV)
+			return -EINVAL;
+		BUG_ON(wm8350_ldo_val_to_mvolts(mV) < min_mV);
+	}
+
+	switch (ldo) {
+	case WM8350_LDO_1:
+		volt_reg = WM8350_LDO1_CONTROL;
+		break;
+	case WM8350_LDO_2:
+		volt_reg = WM8350_LDO2_CONTROL;
+		break;
+	case WM8350_LDO_3:
+		volt_reg = WM8350_LDO3_CONTROL;
+		break;
+	case WM8350_LDO_4:
+		volt_reg = WM8350_LDO4_CONTROL;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* all LDOs have same mV bits */
+	val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_LDO1_VSEL_MASK;
+	wm8350_reg_write(wm8350, volt_reg, val | mV);
+	return 0;
+}
+
+static int wm8350_ldo_get_voltage(struct regulator_dev *rdev)
+{
+	struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+	int volt_reg, ldo = rdev_get_id(rdev);
+	u16 val;
+
+	switch (ldo) {
+	case WM8350_LDO_1:
+		volt_reg = WM8350_LDO1_CONTROL;
+		break;
+	case WM8350_LDO_2:
+		volt_reg = WM8350_LDO2_CONTROL;
+		break;
+	case WM8350_LDO_3:
+		volt_reg = WM8350_LDO3_CONTROL;
+		break;
+	case WM8350_LDO_4:
+		volt_reg = WM8350_LDO4_CONTROL;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* all LDOs have same mV bits */
+	val = wm8350_reg_read(wm8350, volt_reg) & WM8350_LDO1_VSEL_MASK;
+	return wm8350_ldo_val_to_mvolts(val) * 1000;
+}
+
+int wm8350_dcdc_set_slot(struct wm8350 *wm8350, int dcdc, u16 start,
+			 u16 stop, u16 fault)
+{
+	int slot_reg;
+	u16 val;
+
+	dev_dbg(wm8350->dev, "%s %d start %d stop %d\n",
+		__func__, dcdc, start, stop);
+
+	/* slot valid ? */
+	if (start > 15 || stop > 15)
+		return -EINVAL;
+
+	switch (dcdc) {
+	case WM8350_DCDC_1:
+		slot_reg = WM8350_DCDC1_TIMEOUTS;
+		break;
+	case WM8350_DCDC_2:
+		slot_reg = WM8350_DCDC2_TIMEOUTS;
+		break;
+	case WM8350_DCDC_3:
+		slot_reg = WM8350_DCDC3_TIMEOUTS;
+		break;
+	case WM8350_DCDC_4:
+		slot_reg = WM8350_DCDC4_TIMEOUTS;
+		break;
+	case WM8350_DCDC_5:
+		slot_reg = WM8350_DCDC5_TIMEOUTS;
+		break;
+	case WM8350_DCDC_6:
+		slot_reg = WM8350_DCDC6_TIMEOUTS;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	val = wm8350_reg_read(wm8350, slot_reg) &
+	    ~(WM8350_DC1_ENSLOT_MASK | WM8350_DC1_SDSLOT_MASK |
+	      WM8350_DC1_ERRACT_MASK);
+	wm8350_reg_write(wm8350, slot_reg,
+			 val | (start << WM8350_DC1_ENSLOT_SHIFT) |
+			 (stop << WM8350_DC1_SDSLOT_SHIFT) |
+			 (fault << WM8350_DC1_ERRACT_SHIFT));
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(wm8350_dcdc_set_slot);
+
+int wm8350_ldo_set_slot(struct wm8350 *wm8350, int ldo, u16 start, u16 stop)
+{
+	int slot_reg;
+	u16 val;
+
+	dev_dbg(wm8350->dev, "%s %d start %d stop %d\n",
+		__func__, ldo, start, stop);
+
+	/* slot valid ? */
+	if (start > 15 || stop > 15)
+		return -EINVAL;
+
+	switch (ldo) {
+	case WM8350_LDO_1:
+		slot_reg = WM8350_LDO1_TIMEOUTS;
+		break;
+	case WM8350_LDO_2:
+		slot_reg = WM8350_LDO2_TIMEOUTS;
+		break;
+	case WM8350_LDO_3:
+		slot_reg = WM8350_LDO3_TIMEOUTS;
+		break;
+	case WM8350_LDO_4:
+		slot_reg = WM8350_LDO4_TIMEOUTS;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	val = wm8350_reg_read(wm8350, slot_reg) & ~WM8350_LDO1_SDSLOT_MASK;
+	wm8350_reg_write(wm8350, slot_reg, val | ((start << 10) | (stop << 6)));
+	return 0;
+}
+EXPORT_SYMBOL_GPL(wm8350_ldo_set_slot);
+
+int wm8350_dcdc25_set_mode(struct wm8350 *wm8350, int dcdc, u16 mode,
+			   u16 ilim, u16 ramp, u16 feedback)
+{
+	u16 val;
+
+	dev_dbg(wm8350->dev, "%s %d mode: %s %s\n", __func__, dcdc,
+		mode ? "normal" : "boost", ilim ? "low" : "normal");
+
+	switch (dcdc) {
+	case WM8350_DCDC_2:
+		val = wm8350_reg_read(wm8350, WM8350_DCDC2_CONTROL)
+		    & ~(WM8350_DC2_MODE_MASK | WM8350_DC2_ILIM_MASK |
+			WM8350_DC2_RMP_MASK | WM8350_DC2_FBSRC_MASK);
+		wm8350_reg_write(wm8350, WM8350_DCDC2_CONTROL, val |
+				 (mode << WM8350_DC2_MODE_SHIFT) |
+				 (ilim << WM8350_DC2_ILIM_SHIFT) |
+				 (ramp << WM8350_DC2_RMP_SHIFT) |
+				 (feedback << WM8350_DC2_FBSRC_SHIFT));
+		break;
+	case WM8350_DCDC_5:
+		val = wm8350_reg_read(wm8350, WM8350_DCDC5_CONTROL)
+		    & ~(WM8350_DC5_MODE_MASK | WM8350_DC5_ILIM_MASK |
+			WM8350_DC5_RMP_MASK | WM8350_DC5_FBSRC_MASK);
+		wm8350_reg_write(wm8350, WM8350_DCDC5_CONTROL, val |
+				 (mode << WM8350_DC5_MODE_SHIFT) |
+				 (ilim << WM8350_DC5_ILIM_SHIFT) |
+				 (ramp << WM8350_DC5_RMP_SHIFT) |
+				 (feedback << WM8350_DC5_FBSRC_SHIFT));
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(wm8350_dcdc25_set_mode);
+
+static int wm8350_dcdc_enable(struct regulator_dev *rdev)
+{
+	struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+	int dcdc = rdev_get_id(rdev);
+	u16 shift;
+
+	if (dcdc < WM8350_DCDC_1 || dcdc > WM8350_DCDC_6)
+		return -EINVAL;
+
+	shift = dcdc - WM8350_DCDC_1;
+	wm8350_set_bits(wm8350, WM8350_DCDC_LDO_REQUESTED, 1 << shift);
+	return 0;
+}
+
+static int wm8350_dcdc_disable(struct regulator_dev *rdev)
+{
+	struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+	int dcdc = rdev_get_id(rdev);
+	u16 shift;
+
+	if (dcdc < WM8350_DCDC_1 || dcdc > WM8350_DCDC_6)
+		return -EINVAL;
+
+	shift = dcdc - WM8350_DCDC_1;
+	wm8350_clear_bits(wm8350, WM8350_DCDC_LDO_REQUESTED, 1 << shift);
+
+	return 0;
+}
+
+static int wm8350_ldo_enable(struct regulator_dev *rdev)
+{
+	struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+	int ldo = rdev_get_id(rdev);
+	u16 shift;
+
+	if (ldo < WM8350_LDO_1 || ldo > WM8350_LDO_4)
+		return -EINVAL;
+
+	shift = (ldo - WM8350_LDO_1) + 8;
+	wm8350_set_bits(wm8350, WM8350_DCDC_LDO_REQUESTED, 1 << shift);
+	return 0;
+}
+
+static int wm8350_ldo_disable(struct regulator_dev *rdev)
+{
+	struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+	int ldo = rdev_get_id(rdev);
+	u16 shift;
+
+	if (ldo < WM8350_LDO_1 || ldo > WM8350_LDO_4)
+		return -EINVAL;
+
+	shift = (ldo - WM8350_LDO_1) + 8;
+	wm8350_clear_bits(wm8350, WM8350_DCDC_LDO_REQUESTED, 1 << shift);
+	return 0;
+}
+
+static int force_continuous_enable(struct wm8350 *wm8350, int dcdc, int enable)
+{
+	int reg = 0, ret;
+
+	switch (dcdc) {
+	case WM8350_DCDC_1:
+		reg = WM8350_DCDC1_FORCE_PWM;
+		break;
+	case WM8350_DCDC_3:
+		reg = WM8350_DCDC3_FORCE_PWM;
+		break;
+	case WM8350_DCDC_4:
+		reg = WM8350_DCDC4_FORCE_PWM;
+		break;
+	case WM8350_DCDC_6:
+		reg = WM8350_DCDC6_FORCE_PWM;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (enable)
+		ret = wm8350_set_bits(wm8350, reg,
+			WM8350_DCDC1_FORCE_PWM_ENA);
+	else
+		ret = wm8350_clear_bits(wm8350, reg,
+			WM8350_DCDC1_FORCE_PWM_ENA);
+	return ret;
+}
+
+static int wm8350_dcdc_set_mode(struct regulator_dev *rdev, unsigned int mode)
+{
+	struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+	int dcdc = rdev_get_id(rdev);
+	u16 val;
+
+	if (dcdc < WM8350_DCDC_1 || dcdc > WM8350_DCDC_6)
+		return -EINVAL;
+
+	if (dcdc == WM8350_DCDC_2 || dcdc == WM8350_DCDC_5)
+		return -EINVAL;
+
+	val = 1 << (dcdc - WM8350_DCDC_1);
+
+	switch (mode) {
+	case REGULATOR_MODE_FAST:
+		/* force continuous mode */
+		wm8350_set_bits(wm8350, WM8350_DCDC_ACTIVE_OPTIONS, val);
+		wm8350_clear_bits(wm8350, WM8350_DCDC_SLEEP_OPTIONS, val);
+		force_continuous_enable(wm8350, dcdc, 1);
+		break;
+	case REGULATOR_MODE_NORMAL:
+		/* active / pulse skipping */
+		wm8350_set_bits(wm8350, WM8350_DCDC_ACTIVE_OPTIONS, val);
+		wm8350_clear_bits(wm8350, WM8350_DCDC_SLEEP_OPTIONS, val);
+		force_continuous_enable(wm8350, dcdc, 0);
+		break;
+	case REGULATOR_MODE_IDLE:
+		/* standby mode */
+		force_continuous_enable(wm8350, dcdc, 0);
+		wm8350_clear_bits(wm8350, WM8350_DCDC_SLEEP_OPTIONS, val);
+		wm8350_clear_bits(wm8350, WM8350_DCDC_ACTIVE_OPTIONS, val);
+		break;
+	case REGULATOR_MODE_STANDBY:
+		/* LDO mode */
+		force_continuous_enable(wm8350, dcdc, 0);
+		wm8350_set_bits(wm8350, WM8350_DCDC_SLEEP_OPTIONS, val);
+		break;
+	}
+
+	return 0;
+}
+
+static unsigned int wm8350_dcdc_get_mode(struct regulator_dev *rdev)
+{
+	struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+	int dcdc = rdev_get_id(rdev);
+	u16 mask, sleep, active, force;
+	int mode = REGULATOR_MODE_NORMAL;
+
+	if (dcdc < WM8350_DCDC_1 || dcdc > WM8350_DCDC_6)
+		return -EINVAL;
+
+	if (dcdc == WM8350_DCDC_2 || dcdc == WM8350_DCDC_5)
+		return -EINVAL;
+
+	mask = 1 << (dcdc - WM8350_DCDC_1);
+	active = wm8350_reg_read(wm8350, WM8350_DCDC_ACTIVE_OPTIONS) & mask;
+	sleep = wm8350_reg_read(wm8350, WM8350_DCDC_SLEEP_OPTIONS) & mask;
+	force = wm8350_reg_read(wm8350, WM8350_DCDC1_FORCE_PWM)
+	    & WM8350_DCDC1_FORCE_PWM_ENA;
+	dev_dbg(wm8350->dev, "mask %x active %x sleep %x force %x",
+		mask, active, sleep, force);
+
+	if (active && !sleep) {
+		if (force)
+			mode = REGULATOR_MODE_FAST;
+		else
+			mode = REGULATOR_MODE_NORMAL;
+	} else if (!active && !sleep)
+		mode = REGULATOR_MODE_IDLE;
+	else if (!sleep)
+		mode = REGULATOR_MODE_STANDBY;
+
+	return mode;
+}
+
+static unsigned int wm8350_ldo_get_mode(struct regulator_dev *rdev)
+{
+	return REGULATOR_MODE_NORMAL;
+}
+
+struct wm8350_dcdc_efficiency {
+	int uA_load_min;
+	int uA_load_max;
+	unsigned int mode;
+};
+
+static const struct wm8350_dcdc_efficiency dcdc1_6_efficiency[] = {
+	{0, 10000, REGULATOR_MODE_STANDBY},       /* 0 - 10mA - LDO */
+	{10000, 100000, REGULATOR_MODE_IDLE},     /* 10mA - 100mA - Standby */
+	{100000, 1000000, REGULATOR_MODE_NORMAL}, /* > 100mA - Active */
+	{-1, -1, REGULATOR_MODE_NORMAL},
+};
+
+static const struct wm8350_dcdc_efficiency dcdc3_4_efficiency[] = {
+	{0, 10000, REGULATOR_MODE_STANDBY},      /* 0 - 10mA - LDO */
+	{10000, 100000, REGULATOR_MODE_IDLE},    /* 10mA - 100mA - Standby */
+	{100000, 800000, REGULATOR_MODE_NORMAL}, /* > 100mA - Active */
+	{-1, -1, REGULATOR_MODE_NORMAL},
+};
+
+static unsigned int get_mode(int uA, const struct wm8350_dcdc_efficiency *eff)
+{
+	int i = 0;
+
+	while (eff[i].uA_load_min != -1) {
+		if (uA >= eff[i].uA_load_min && uA <= eff[i].uA_load_max)
+			return eff[i].mode;
+	}
+	return REGULATOR_MODE_NORMAL;
+}
+
+/* Query the regulator for it's most efficient mode @ uV,uA
+ * WM8350 regulator efficiency is pretty similar over
+ * different input and output uV.
+ */
+static unsigned int wm8350_dcdc_get_optimum_mode(struct regulator_dev *rdev,
+						 int input_uV, int output_uV,
+						 int output_uA)
+{
+	int dcdc = rdev_get_id(rdev), mode;
+
+	switch (dcdc) {
+	case WM8350_DCDC_1:
+	case WM8350_DCDC_6:
+		mode = get_mode(output_uA, dcdc1_6_efficiency);
+		break;
+	case WM8350_DCDC_3:
+	case WM8350_DCDC_4:
+		mode = get_mode(output_uA, dcdc3_4_efficiency);
+		break;
+	default:
+		mode = REGULATOR_MODE_NORMAL;
+		break;
+	}
+	return mode;
+}
+
+static int wm8350_dcdc_is_enabled(struct regulator_dev *rdev)
+{
+	struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+	int dcdc = rdev_get_id(rdev), shift;
+
+	if (dcdc < WM8350_DCDC_1 || dcdc > WM8350_DCDC_6)
+		return -EINVAL;
+
+	shift = dcdc - WM8350_DCDC_1;
+	return wm8350_reg_read(wm8350, WM8350_DCDC_LDO_REQUESTED)
+	    & (1 << shift);
+}
+
+static int wm8350_ldo_is_enabled(struct regulator_dev *rdev)
+{
+	struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+	int ldo = rdev_get_id(rdev), shift;
+
+	if (ldo < WM8350_LDO_1 || ldo > WM8350_LDO_4)
+		return -EINVAL;
+
+	shift = (ldo - WM8350_LDO_1) + 8;
+	return wm8350_reg_read(wm8350, WM8350_DCDC_LDO_REQUESTED)
+	    & (1 << shift);
+}
+
+static struct regulator_ops wm8350_dcdc_ops = {
+	.set_voltage = wm8350_dcdc_set_voltage,
+	.get_voltage = wm8350_dcdc_get_voltage,
+	.enable = wm8350_dcdc_enable,
+	.disable = wm8350_dcdc_disable,
+	.get_mode = wm8350_dcdc_get_mode,
+	.set_mode = wm8350_dcdc_set_mode,
+	.get_optimum_mode = wm8350_dcdc_get_optimum_mode,
+	.is_enabled = wm8350_dcdc_is_enabled,
+	.set_suspend_voltage = wm8350_dcdc_set_suspend_voltage,
+	.set_suspend_enable = wm8350_dcdc_set_suspend_enable,
+	.set_suspend_disable = wm8350_dcdc_set_suspend_disable,
+	.set_suspend_mode = wm8350_dcdc_set_suspend_mode,
+};
+
+static struct regulator_ops wm8350_dcdc2_5_ops = {
+	.enable = wm8350_dcdc_enable,
+	.disable = wm8350_dcdc_disable,
+	.is_enabled = wm8350_dcdc_is_enabled,
+	.set_suspend_enable = wm8350_dcdc25_set_suspend_enable,
+	.set_suspend_disable = wm8350_dcdc25_set_suspend_disable,
+};
+
+static struct regulator_ops wm8350_ldo_ops = {
+	.set_voltage = wm8350_ldo_set_voltage,
+	.get_voltage = wm8350_ldo_get_voltage,
+	.enable = wm8350_ldo_enable,
+	.disable = wm8350_ldo_disable,
+	.is_enabled = wm8350_ldo_is_enabled,
+	.get_mode = wm8350_ldo_get_mode,
+	.set_suspend_voltage = wm8350_ldo_set_suspend_voltage,
+	.set_suspend_enable = wm8350_ldo_set_suspend_enable,
+	.set_suspend_disable = wm8350_ldo_set_suspend_disable,
+};
+
+static struct regulator_ops wm8350_isink_ops = {
+	.set_current_limit = wm8350_isink_set_current,
+	.get_current_limit = wm8350_isink_get_current,
+	.enable = wm8350_isink_enable,
+	.disable = wm8350_isink_disable,
+	.is_enabled = wm8350_isink_is_enabled,
+};
+
+static struct regulator_desc wm8350_reg[NUM_WM8350_REGULATORS] = {
+	{
+		.name = "DCDC1",
+		.id = WM8350_DCDC_1,
+		.ops = &wm8350_dcdc_ops,
+		.irq = WM8350_IRQ_UV_DC1,
+		.type = REGULATOR_VOLTAGE,
+		.owner = THIS_MODULE,
+	},
+	{
+		.name = "DCDC2",
+		.id = WM8350_DCDC_2,
+		.ops = &wm8350_dcdc2_5_ops,
+		.irq = WM8350_IRQ_UV_DC2,
+		.type = REGULATOR_VOLTAGE,
+		.owner = THIS_MODULE,
+	},
+	{
+		.name = "DCDC3",
+		.id = WM8350_DCDC_3,
+		.ops = &wm8350_dcdc_ops,
+		.irq = WM8350_IRQ_UV_DC3,
+		.type = REGULATOR_VOLTAGE,
+		.owner = THIS_MODULE,
+	},
+	{
+		.name = "DCDC4",
+		.id = WM8350_DCDC_4,
+		.ops = &wm8350_dcdc_ops,
+		.irq = WM8350_IRQ_UV_DC4,
+		.type = REGULATOR_VOLTAGE,
+		.owner = THIS_MODULE,
+	},
+	{
+		.name = "DCDC5",
+		.id = WM8350_DCDC_5,
+		.ops = &wm8350_dcdc2_5_ops,
+		.irq = WM8350_IRQ_UV_DC5,
+		.type = REGULATOR_VOLTAGE,
+		.owner = THIS_MODULE,
+	 },
+	{
+		.name = "DCDC6",
+		.id = WM8350_DCDC_6,
+		.ops = &wm8350_dcdc_ops,
+		.irq = WM8350_IRQ_UV_DC6,
+		.type = REGULATOR_VOLTAGE,
+		.owner = THIS_MODULE,
+	},
+	{
+		.name = "LDO1",
+		.id = WM8350_LDO_1,
+		.ops = &wm8350_ldo_ops,
+		.irq = WM8350_IRQ_UV_LDO1,
+		.type = REGULATOR_VOLTAGE,
+		.owner = THIS_MODULE,
+	},
+	{
+		.name = "LDO2",
+		.id = WM8350_LDO_2,
+		.ops = &wm8350_ldo_ops,
+		.irq = WM8350_IRQ_UV_LDO2,
+		.type = REGULATOR_VOLTAGE,
+		.owner = THIS_MODULE,
+	},
+	{
+		.name = "LDO3",
+		.id = WM8350_LDO_3,
+		.ops = &wm8350_ldo_ops,
+		.irq = WM8350_IRQ_UV_LDO3,
+		.type = REGULATOR_VOLTAGE,
+		.owner = THIS_MODULE,
+	},
+	{
+		.name = "LDO4",
+		.id = WM8350_LDO_4,
+		.ops = &wm8350_ldo_ops,
+		.irq = WM8350_IRQ_UV_LDO4,
+		.type = REGULATOR_VOLTAGE,
+		.owner = THIS_MODULE,
+	},
+	{
+		.name = "ISINKA",
+		.id = WM8350_ISINK_A,
+		.ops = &wm8350_isink_ops,
+		.irq = WM8350_IRQ_CS1,
+		.type = REGULATOR_CURRENT,
+		.owner = THIS_MODULE,
+	 },
+	{
+		.name = "ISINKB",
+		.id = WM8350_ISINK_B,
+		.ops = &wm8350_isink_ops,
+		.irq = WM8350_IRQ_CS2,
+		.type = REGULATOR_CURRENT,
+		.owner = THIS_MODULE,
+	 },
+};
+
+static void pmic_uv_handler(struct wm8350 *wm8350, int irq, void *data)
+{
+	struct regulator_dev *rdev = (struct regulator_dev *)data;
+
+	if (irq == WM8350_IRQ_CS1 || irq == WM8350_IRQ_CS2)
+		regulator_notifier_call_chain(rdev,
+					      REGULATOR_EVENT_REGULATION_OUT,
+					      wm8350);
+	else
+		regulator_notifier_call_chain(rdev,
+					      REGULATOR_EVENT_UNDER_VOLTAGE,
+					      wm8350);
+}
+
+static int wm8350_regulator_probe(struct platform_device *pdev)
+{
+	struct wm8350 *wm8350 = dev_get_drvdata(&pdev->dev);
+	struct regulator_dev *rdev;
+	int ret;
+	u16 val;
+
+	if (pdev->id < WM8350_DCDC_1 || pdev->id > WM8350_ISINK_B)
+		return -ENODEV;
+
+	/* do any regulatior specific init */
+	switch (pdev->id) {
+	case WM8350_DCDC_1:
+		val = wm8350_reg_read(wm8350, WM8350_DCDC1_LOW_POWER);
+		wm8350->pmic.dcdc1_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK;
+		break;
+	case WM8350_DCDC_3:
+		val = wm8350_reg_read(wm8350, WM8350_DCDC3_LOW_POWER);
+		wm8350->pmic.dcdc3_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK;
+		break;
+	case WM8350_DCDC_4:
+		val = wm8350_reg_read(wm8350, WM8350_DCDC4_LOW_POWER);
+		wm8350->pmic.dcdc4_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK;
+		break;
+	case WM8350_DCDC_6:
+		val = wm8350_reg_read(wm8350, WM8350_DCDC6_LOW_POWER);
+		wm8350->pmic.dcdc6_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK;
+		break;
+	}
+
+
+	/* register regulator */
+	rdev = regulator_register(&wm8350_reg[pdev->id], &pdev->dev,
+				  dev_get_drvdata(&pdev->dev));
+	if (IS_ERR(rdev)) {
+		dev_err(&pdev->dev, "failed to register %s\n",
+			wm8350_reg[pdev->id].name);
+		return PTR_ERR(rdev);
+	}
+
+	/* register regulator IRQ */
+	ret = wm8350_register_irq(wm8350, wm8350_reg[pdev->id].irq,
+				  pmic_uv_handler, rdev);
+	if (ret < 0) {
+		regulator_unregister(rdev);
+		dev_err(&pdev->dev, "failed to register regulator %s IRQ\n",
+			wm8350_reg[pdev->id].name);
+		return ret;
+	}
+
+	wm8350_unmask_irq(wm8350, wm8350_reg[pdev->id].irq);
+
+	return 0;
+}
+
+static int wm8350_regulator_remove(struct platform_device *pdev)
+{
+	struct regulator_dev *rdev = platform_get_drvdata(pdev);
+	struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+
+	wm8350_mask_irq(wm8350, wm8350_reg[pdev->id].irq);
+	wm8350_free_irq(wm8350, wm8350_reg[pdev->id].irq);
+
+	regulator_unregister(rdev);
+
+	return 0;
+}
+
+int wm8350_register_regulator(struct wm8350 *wm8350, int reg,
+			      struct regulator_init_data *initdata)
+{
+	struct platform_device *pdev;
+	int ret;
+
+	if (wm8350->pmic.pdev[reg])
+		return -EBUSY;
+
+	pdev = platform_device_alloc("wm8350-regulator", reg);
+	if (!pdev)
+		return -ENOMEM;
+
+	wm8350->pmic.pdev[reg] = pdev;
+
+	initdata->driver_data = wm8350;
+
+	pdev->dev.platform_data = initdata;
+	pdev->dev.parent = wm8350->dev;
+	platform_set_drvdata(pdev, wm8350);
+
+	ret = platform_device_add(pdev);
+
+	if (ret != 0) {
+		dev_err(wm8350->dev, "Failed to register regulator %d: %d\n",
+			reg, ret);
+		platform_device_del(pdev);
+		wm8350->pmic.pdev[reg] = NULL;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wm8350_register_regulator);
+
+static struct platform_driver wm8350_regulator_driver = {
+	.probe = wm8350_regulator_probe,
+	.remove = wm8350_regulator_remove,
+	.driver		= {
+		.name	= "wm8350-regulator",
+	},
+};
+
+static int __init wm8350_regulator_init(void)
+{
+	return platform_driver_register(&wm8350_regulator_driver);
+}
+subsys_initcall(wm8350_regulator_init);
+
+static void __exit wm8350_regulator_exit(void)
+{
+	platform_driver_unregister(&wm8350_regulator_driver);
+}
+module_exit(wm8350_regulator_exit);
+
+/* Module information */
+MODULE_AUTHOR("Liam Girdwood");
+MODULE_DESCRIPTION("WM8350 voltage and current regulator driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/wm8400-regulator.c b/drivers/regulator/wm8400-regulator.c
new file mode 100644
index 0000000..48b372e
--- /dev/null
+++ b/drivers/regulator/wm8400-regulator.c
@@ -0,0 +1,368 @@
+/*
+ * Regulator support for WM8400
+ *
+ * Copyright 2008 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.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/bug.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/regulator/driver.h>
+#include <linux/mfd/wm8400-private.h>
+
+static int wm8400_ldo_is_enabled(struct regulator_dev *dev)
+{
+	struct wm8400 *wm8400 = rdev_get_drvdata(dev);
+	u16 val;
+
+	val = wm8400_reg_read(wm8400, WM8400_LDO1_CONTROL + rdev_get_id(dev));
+	return (val & WM8400_LDO1_ENA) != 0;
+}
+
+static int wm8400_ldo_enable(struct regulator_dev *dev)
+{
+	struct wm8400 *wm8400 = rdev_get_drvdata(dev);
+
+	return wm8400_set_bits(wm8400, WM8400_LDO1_CONTROL + rdev_get_id(dev),
+			       WM8400_LDO1_ENA, WM8400_LDO1_ENA);
+}
+
+static int wm8400_ldo_disable(struct regulator_dev *dev)
+{
+	struct wm8400 *wm8400 = rdev_get_drvdata(dev);
+
+	return wm8400_set_bits(wm8400, WM8400_LDO1_CONTROL + rdev_get_id(dev),
+			       WM8400_LDO1_ENA, 0);
+}
+
+static int wm8400_ldo_get_voltage(struct regulator_dev *dev)
+{
+	struct wm8400 *wm8400 = rdev_get_drvdata(dev);
+	u16 val;
+
+	val = wm8400_reg_read(wm8400, WM8400_LDO1_CONTROL + rdev_get_id(dev));
+	val &= WM8400_LDO1_VSEL_MASK;
+
+	if (val < 15)
+		return 900000 + (val * 50000);
+	else
+		return 1600000 + ((val - 14) * 100000);
+}
+
+static int wm8400_ldo_set_voltage(struct regulator_dev *dev,
+				  int min_uV, int max_uV)
+{
+	struct wm8400 *wm8400 = rdev_get_drvdata(dev);
+	u16 val;
+
+	if (min_uV < 900000 || min_uV > 3300000)
+		return -EINVAL;
+
+	if (min_uV < 1700000) {
+		/* Steps of 50mV from 900mV;  */
+		val = (min_uV - 850001) / 50000;
+
+		if ((val * 50000) + 900000 > max_uV)
+			return -EINVAL;
+		BUG_ON((val * 50000) + 900000 < min_uV);
+	} else {
+		/* Steps of 100mV from 1700mV */
+		val = ((min_uV - 1600001) / 100000);
+
+		if ((val * 100000) + 1700000 > max_uV)
+			return -EINVAL;
+		BUG_ON((val * 100000) + 1700000 < min_uV);
+
+		val += 0xf;
+	}
+
+	return wm8400_set_bits(wm8400, WM8400_LDO1_CONTROL + rdev_get_id(dev),
+			       WM8400_LDO1_VSEL_MASK, val);
+}
+
+static struct regulator_ops wm8400_ldo_ops = {
+	.is_enabled = wm8400_ldo_is_enabled,
+	.enable = wm8400_ldo_enable,
+	.disable = wm8400_ldo_disable,
+	.get_voltage = wm8400_ldo_get_voltage,
+	.set_voltage = wm8400_ldo_set_voltage,
+};
+
+static int wm8400_dcdc_is_enabled(struct regulator_dev *dev)
+{
+	struct wm8400 *wm8400 = rdev_get_drvdata(dev);
+	int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2;
+	u16 val;
+
+	val = wm8400_reg_read(wm8400, WM8400_DCDC1_CONTROL_1 + offset);
+	return (val & WM8400_DC1_ENA) != 0;
+}
+
+static int wm8400_dcdc_enable(struct regulator_dev *dev)
+{
+	struct wm8400 *wm8400 = rdev_get_drvdata(dev);
+	int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2;
+
+	return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset,
+			       WM8400_DC1_ENA, WM8400_DC1_ENA);
+}
+
+static int wm8400_dcdc_disable(struct regulator_dev *dev)
+{
+	struct wm8400 *wm8400 = rdev_get_drvdata(dev);
+	int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2;
+
+	return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset,
+			       WM8400_DC1_ENA, 0);
+}
+
+static int wm8400_dcdc_get_voltage(struct regulator_dev *dev)
+{
+	struct wm8400 *wm8400 = rdev_get_drvdata(dev);
+	u16 val;
+	int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2;
+
+	val = wm8400_reg_read(wm8400, WM8400_DCDC1_CONTROL_1 + offset);
+	val &= WM8400_DC1_VSEL_MASK;
+
+	return 850000 + (25000 * val);
+}
+
+static int wm8400_dcdc_set_voltage(struct regulator_dev *dev,
+				  int min_uV, int max_uV)
+{
+	struct wm8400 *wm8400 = rdev_get_drvdata(dev);
+	u16 val;
+	int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2;
+
+	if (min_uV < 850000)
+		return -EINVAL;
+
+	val = (min_uV - 825001) / 25000;
+
+	if (850000 + (25000 * val) > max_uV)
+		return -EINVAL;
+	BUG_ON(850000 + (25000 * val) < min_uV);
+
+	return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset,
+			       WM8400_DC1_VSEL_MASK, val);
+}
+
+static unsigned int wm8400_dcdc_get_mode(struct regulator_dev *dev)
+{
+	struct wm8400 *wm8400 = rdev_get_drvdata(dev);
+	int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2;
+	u16 data[2];
+	int ret;
+
+	ret = wm8400_block_read(wm8400, WM8400_DCDC1_CONTROL_1 + offset, 2,
+				data);
+	if (ret != 0)
+		return 0;
+
+	/* Datasheet: hibernate */
+	if (data[0] & WM8400_DC1_SLEEP)
+		return REGULATOR_MODE_STANDBY;
+
+	/* Datasheet: standby */
+	if (!(data[0] & WM8400_DC1_ACTIVE))
+		return REGULATOR_MODE_IDLE;
+
+	/* Datasheet: active with or without force PWM */
+	if (data[1] & WM8400_DC1_FRC_PWM)
+		return REGULATOR_MODE_FAST;
+	else
+		return REGULATOR_MODE_NORMAL;
+}
+
+static int wm8400_dcdc_set_mode(struct regulator_dev *dev, unsigned int mode)
+{
+	struct wm8400 *wm8400 = rdev_get_drvdata(dev);
+	int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2;
+	int ret;
+
+	switch (mode) {
+	case REGULATOR_MODE_FAST:
+		/* Datasheet: active with force PWM */
+		ret = wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_2 + offset,
+				      WM8400_DC1_FRC_PWM, WM8400_DC1_FRC_PWM);
+		if (ret != 0)
+			return ret;
+
+		return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset,
+				       WM8400_DC1_ACTIVE | WM8400_DC1_SLEEP,
+				       WM8400_DC1_ACTIVE);
+
+	case REGULATOR_MODE_NORMAL:
+		/* Datasheet: active */
+		ret = wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_2 + offset,
+				      WM8400_DC1_FRC_PWM, 0);
+		if (ret != 0)
+			return ret;
+
+		return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset,
+				       WM8400_DC1_ACTIVE | WM8400_DC1_SLEEP,
+				       WM8400_DC1_ACTIVE);
+
+	case REGULATOR_MODE_IDLE:
+		/* Datasheet: standby */
+		ret = wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset,
+				      WM8400_DC1_ACTIVE, 0);
+		if (ret != 0)
+			return ret;
+		return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset,
+				       WM8400_DC1_SLEEP, 0);
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static unsigned int wm8400_dcdc_get_optimum_mode(struct regulator_dev *dev,
+						 int input_uV, int output_uV,
+						 int load_uA)
+{
+	return REGULATOR_MODE_NORMAL;
+}
+
+static struct regulator_ops wm8400_dcdc_ops = {
+	.is_enabled = wm8400_dcdc_is_enabled,
+	.enable = wm8400_dcdc_enable,
+	.disable = wm8400_dcdc_disable,
+	.get_voltage = wm8400_dcdc_get_voltage,
+	.set_voltage = wm8400_dcdc_set_voltage,
+	.get_mode = wm8400_dcdc_get_mode,
+	.set_mode = wm8400_dcdc_set_mode,
+	.get_optimum_mode = wm8400_dcdc_get_optimum_mode,
+};
+
+static struct regulator_desc regulators[] = {
+	{
+		.name = "LDO1",
+		.id = WM8400_LDO1,
+		.ops = &wm8400_ldo_ops,
+		.type = REGULATOR_VOLTAGE,
+		.owner = THIS_MODULE,
+	},
+	{
+		.name = "LDO2",
+		.id = WM8400_LDO2,
+		.ops = &wm8400_ldo_ops,
+		.type = REGULATOR_VOLTAGE,
+		.owner = THIS_MODULE,
+	},
+	{
+		.name = "LDO3",
+		.id = WM8400_LDO3,
+		.ops = &wm8400_ldo_ops,
+		.type = REGULATOR_VOLTAGE,
+		.owner = THIS_MODULE,
+	},
+	{
+		.name = "LDO4",
+		.id = WM8400_LDO4,
+		.ops = &wm8400_ldo_ops,
+		.type = REGULATOR_VOLTAGE,
+		.owner = THIS_MODULE,
+	},
+	{
+		.name = "DCDC1",
+		.id = WM8400_DCDC1,
+		.ops = &wm8400_dcdc_ops,
+		.type = REGULATOR_VOLTAGE,
+		.owner = THIS_MODULE,
+	},
+	{
+		.name = "DCDC2",
+		.id = WM8400_DCDC2,
+		.ops = &wm8400_dcdc_ops,
+		.type = REGULATOR_VOLTAGE,
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init wm8400_regulator_probe(struct platform_device *pdev)
+{
+	struct regulator_dev *rdev;
+
+	rdev = regulator_register(&regulators[pdev->id], &pdev->dev,
+		pdev->dev.driver_data);
+
+	if (IS_ERR(rdev))
+		return PTR_ERR(rdev);
+
+	return 0;
+}
+
+static int __devexit wm8400_regulator_remove(struct platform_device *pdev)
+{
+	struct regulator_dev *rdev = platform_get_drvdata(pdev);
+
+	regulator_unregister(rdev);
+
+	return 0;
+}
+
+static struct platform_driver wm8400_regulator_driver = {
+	.driver = {
+		.name = "wm8400-regulator",
+	},
+	.probe = wm8400_regulator_probe,
+	.remove = __devexit_p(wm8400_regulator_remove),
+};
+
+/**
+ * wm8400_register_regulator - enable software control of a WM8400 regulator
+ *
+ * This function enables software control of a WM8400 regulator via
+ * the regulator API.  It is intended to be called from the
+ * platform_init() callback of the WM8400 MFD driver.
+ *
+ * @param dev      The WM8400 device to operate on.
+ * @param reg      The regulator to control.
+ * @param initdata Regulator initdata for the regulator.
+ */
+int wm8400_register_regulator(struct device *dev, int reg,
+			      struct regulator_init_data *initdata)
+{
+	struct wm8400 *wm8400 = dev->driver_data;
+
+	if (wm8400->regulators[reg].name)
+		return -EBUSY;
+
+	initdata->driver_data = wm8400;
+
+	wm8400->regulators[reg].name = "wm8400-regulator";
+	wm8400->regulators[reg].id = reg;
+	wm8400->regulators[reg].dev.parent = dev;
+	wm8400->regulators[reg].dev.driver_data = wm8400;
+	wm8400->regulators[reg].dev.platform_data = initdata;
+
+	return platform_device_register(&wm8400->regulators[reg]);
+}
+EXPORT_SYMBOL_GPL(wm8400_register_regulator);
+
+static int __init wm8400_regulator_init(void)
+{
+	return platform_driver_register(&wm8400_regulator_driver);
+}
+module_init(wm8400_regulator_init);
+
+static void __exit wm8400_regulator_exit(void)
+{
+	platform_driver_unregister(&wm8400_regulator_driver);
+}
+module_exit(wm8400_regulator_exit);
+
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("WM8400 regulator driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm8400-regulator");
diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c
index b23af0c..963ad0b 100644
--- a/drivers/rtc/rtc-cmos.c
+++ b/drivers/rtc/rtc-cmos.c
@@ -913,6 +913,92 @@
  * predate even PNPBIOS should set up platform_bus devices.
  */
 
+#ifdef	CONFIG_ACPI
+
+#include <linux/acpi.h>
+
+#ifdef	CONFIG_PM
+static u32 rtc_handler(void *context)
+{
+	acpi_clear_event(ACPI_EVENT_RTC);
+	acpi_disable_event(ACPI_EVENT_RTC, 0);
+	return ACPI_INTERRUPT_HANDLED;
+}
+
+static inline void rtc_wake_setup(void)
+{
+	acpi_install_fixed_event_handler(ACPI_EVENT_RTC, rtc_handler, NULL);
+	/*
+	 * After the RTC handler is installed, the Fixed_RTC event should
+	 * be disabled. Only when the RTC alarm is set will it be enabled.
+	 */
+	acpi_clear_event(ACPI_EVENT_RTC);
+	acpi_disable_event(ACPI_EVENT_RTC, 0);
+}
+
+static void rtc_wake_on(struct device *dev)
+{
+	acpi_clear_event(ACPI_EVENT_RTC);
+	acpi_enable_event(ACPI_EVENT_RTC, 0);
+}
+
+static void rtc_wake_off(struct device *dev)
+{
+	acpi_disable_event(ACPI_EVENT_RTC, 0);
+}
+#else
+#define rtc_wake_setup()	do{}while(0)
+#define rtc_wake_on		NULL
+#define rtc_wake_off		NULL
+#endif
+
+/* Every ACPI platform has a mc146818 compatible "cmos rtc".  Here we find
+ * its device node and pass extra config data.  This helps its driver use
+ * capabilities that the now-obsolete mc146818 didn't have, and informs it
+ * that this board's RTC is wakeup-capable (per ACPI spec).
+ */
+static struct cmos_rtc_board_info acpi_rtc_info;
+
+static void __devinit
+cmos_wake_setup(struct device *dev)
+{
+	if (acpi_disabled)
+		return;
+
+	rtc_wake_setup();
+	acpi_rtc_info.wake_on = rtc_wake_on;
+	acpi_rtc_info.wake_off = rtc_wake_off;
+
+	/* workaround bug in some ACPI tables */
+	if (acpi_gbl_FADT.month_alarm && !acpi_gbl_FADT.day_alarm) {
+		dev_dbg(dev, "bogus FADT month_alarm (%d)\n",
+			acpi_gbl_FADT.month_alarm);
+		acpi_gbl_FADT.month_alarm = 0;
+	}
+
+	acpi_rtc_info.rtc_day_alarm = acpi_gbl_FADT.day_alarm;
+	acpi_rtc_info.rtc_mon_alarm = acpi_gbl_FADT.month_alarm;
+	acpi_rtc_info.rtc_century = acpi_gbl_FADT.century;
+
+	/* NOTE:  S4_RTC_WAKE is NOT currently useful to Linux */
+	if (acpi_gbl_FADT.flags & ACPI_FADT_S4_RTC_WAKE)
+		dev_info(dev, "RTC can wake from S4\n");
+
+	dev->platform_data = &acpi_rtc_info;
+
+	/* RTC always wakes from S1/S2/S3, and often S4/STD */
+	device_init_wakeup(dev, 1);
+}
+
+#else
+
+static void __devinit
+cmos_wake_setup(struct device *dev)
+{
+}
+
+#endif
+
 #ifdef	CONFIG_PNP
 
 #include <linux/pnp.h>
@@ -920,6 +1006,8 @@
 static int __devinit
 cmos_pnp_probe(struct pnp_dev *pnp, const struct pnp_device_id *id)
 {
+	cmos_wake_setup(&pnp->dev);
+
 	if (pnp_port_start(pnp,0) == 0x70 && !pnp_irq_valid(pnp,0))
 		/* Some machines contain a PNP entry for the RTC, but
 		 * don't define the IRQ. It should always be safe to
@@ -997,6 +1085,7 @@
 
 static int __init cmos_platform_probe(struct platform_device *pdev)
 {
+	cmos_wake_setup(&pdev->dev);
 	return cmos_do_probe(&pdev->dev,
 			platform_get_resource(pdev, IORESOURCE_IO, 0),
 			platform_get_irq(pdev, 0));
@@ -1031,29 +1120,32 @@
 
 static int __init cmos_init(void)
 {
+	int retval = 0;
+
 #ifdef	CONFIG_PNP
-	if (pnp_platform_devices)
-		return pnp_register_driver(&cmos_pnp_driver);
-	else
-		return platform_driver_probe(&cmos_platform_driver,
-			cmos_platform_probe);
-#else
-	return platform_driver_probe(&cmos_platform_driver,
-			cmos_platform_probe);
-#endif /* CONFIG_PNP */
+	pnp_register_driver(&cmos_pnp_driver);
+#endif
+
+	if (!cmos_rtc.dev)
+		retval = platform_driver_probe(&cmos_platform_driver,
+					       cmos_platform_probe);
+
+	if (retval == 0)
+		return 0;
+
+#ifdef	CONFIG_PNP
+	pnp_unregister_driver(&cmos_pnp_driver);
+#endif
+	return retval;
 }
 module_init(cmos_init);
 
 static void __exit cmos_exit(void)
 {
 #ifdef	CONFIG_PNP
-	if (pnp_platform_devices)
-		pnp_unregister_driver(&cmos_pnp_driver);
-	else
-		platform_driver_unregister(&cmos_platform_driver);
-#else
+	pnp_unregister_driver(&cmos_pnp_driver);
+#endif
 	platform_driver_unregister(&cmos_platform_driver);
-#endif /* CONFIG_PNP */
 }
 module_exit(cmos_exit);
 
diff --git a/drivers/s390/net/claw.c b/drivers/s390/net/claw.c
index 8f83fc9..f5e6185 100644
--- a/drivers/s390/net/claw.c
+++ b/drivers/s390/net/claw.c
@@ -2913,7 +2913,7 @@
 	if (ret != 0) {
 		printk(KERN_WARNING
 			"claw: ccw_device_set_online %s WRITE failed "
-		       "with ret = %d\n", dev_name(&cgdev->cdev[WRITE]->dev)
+		       "with ret = %d\n", dev_name(&cgdev->cdev[WRITE]->dev),
 		       ret);
 		goto out;
 	}
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index d3b211a..403ecad 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -1640,6 +1640,7 @@
 	tristate "Atari native SCSI support"
 	depends on ATARI && SCSI
 	select SCSI_SPI_ATTRS
+	select NVRAM
 	---help---
 	  If you have an Atari with built-in NCR5380 SCSI controller (TT,
 	  Falcon, ...) say Y to get it supported. Of course also, if you have
@@ -1670,14 +1671,6 @@
 	  boot process fractionally longer but may assist recovery from errors
 	  that leave the devices with SCSI operations partway completed.
 
-config TT_DMA_EMUL
-	bool "Hades SCSI DMA emulator"
-	depends on ATARI_SCSI && HADES
-	help
-	  This option enables code which emulates the TT SCSI DMA chip on the
-	  Hades. This increases the SCSI transfer rates at least ten times
-	  compared to PIO transfers.
-
 config MAC_SCSI
 	bool "Macintosh NCR5380 SCSI"
 	depends on MAC && SCSI=y
diff --git a/drivers/scsi/atari_dma_emul.c b/drivers/scsi/atari_dma_emul.c
deleted file mode 100644
index cdc710e..0000000
--- a/drivers/scsi/atari_dma_emul.c
+++ /dev/null
@@ -1,468 +0,0 @@
-/*
- * atari_dma_emul.c -- TT SCSI DMA emulator for the Hades.
- *
- * Copyright 1997 Wout Klaren <W.Klaren@inter.nl.net>
- *
- * 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.
- *
- * This code was written using the Hades TOS source code as a
- * reference. This source code can be found on the home page
- * of Medusa Computer Systems.
- *
- * Version 0.1, 1997-09-24.
- * 
- * This code should be considered experimental. It has only been
- * tested on a Hades with a 68060. It might not work on a Hades
- * with a 68040. Make backups of your hard drives before using
- * this code.
- */
-
-#include <linux/compiler.h>
-#include <asm/thread_info.h>
-#include <asm/uaccess.h>
-
-#define hades_dma_ctrl		(*(unsigned char *) 0xffff8717)
-#define hades_psdm_reg		(*(unsigned char *) 0xffff8741)
-
-#define TRANSFER_SIZE		16
-
-struct m68040_frame {
-	unsigned long  effaddr;  /* effective address */
-	unsigned short ssw;      /* special status word */
-	unsigned short wb3s;     /* write back 3 status */
-	unsigned short wb2s;     /* write back 2 status */
-	unsigned short wb1s;     /* write back 1 status */
-	unsigned long  faddr;    /* fault address */
-	unsigned long  wb3a;     /* write back 3 address */
-	unsigned long  wb3d;     /* write back 3 data */
-	unsigned long  wb2a;     /* write back 2 address */
-	unsigned long  wb2d;     /* write back 2 data */
-	unsigned long  wb1a;     /* write back 1 address */
-	unsigned long  wb1dpd0;  /* write back 1 data/push data 0*/
-	unsigned long  pd1;      /* push data 1*/
-	unsigned long  pd2;      /* push data 2*/
-	unsigned long  pd3;      /* push data 3*/
-};
-
-static void writeback (unsigned short wbs, unsigned long wba,
-		       unsigned long wbd, void *old_buserr)
-{
-	mm_segment_t fs = get_fs();
-	static void *save_buserr;
-
-	__asm__ __volatile__ ("movec.l	%%vbr,%%a0\n\t"
-			      "move.l	%0,8(%%a0)\n\t"
-			      :
-			      : "r" (&&bus_error)
-			      : "a0" );
-
-	save_buserr = old_buserr;
-
-	set_fs (MAKE_MM_SEG(wbs & WBTM_040));
-
-	switch (wbs & WBSIZ_040) {
-	    case BA_SIZE_BYTE:
-		put_user (wbd & 0xff, (char *)wba);
-		break;
-	    case BA_SIZE_WORD:
-		put_user (wbd & 0xffff, (short *)wba);
-		break;
-	    case BA_SIZE_LONG:
-		put_user (wbd, (int *)wba);
-		break;
-	}
-
-	set_fs (fs);
-	return;
-
-bus_error:
-	__asm__ __volatile__ ("cmp.l	%0,2(%%sp)\n\t"
-			      "bcs.s	.jump_old\n\t"
-			      "cmp.l	%1,2(%%sp)\n\t"
-			      "bls.s	.restore_old\n"
-			".jump_old:\n\t"
-			      "move.l	%2,-(%%sp)\n\t"
-			      "rts\n"
-			".restore_old:\n\t"
-			      "move.l	%%a0,-(%%sp)\n\t"
-			      "movec.l	%%vbr,%%a0\n\t"
-			      "move.l	%2,8(%%a0)\n\t"
-			      "move.l	(%%sp)+,%%a0\n\t"
-			      "rte\n\t"
-			      :
-			      : "i" (writeback), "i" (&&bus_error),
-			        "m" (save_buserr) );
-}
-
-/*
- * static inline void set_restdata_reg(unsigned char *cur_addr)
- *
- * Set the rest data register if necessary.
- */
-
-static inline void set_restdata_reg(unsigned char *cur_addr)
-{
-	if (((long) cur_addr & ~3) != 0)
-		tt_scsi_dma.dma_restdata =
-			*((unsigned long *) ((long) cur_addr & ~3));
-}
-
-/*
- * void hades_dma_emulator(int irq, void *dummy)
- * 
- * This code emulates TT SCSI DMA on the Hades.
- * 
- * Note the following:
- * 
- * 1. When there is no byte available to read from the SCSI bus, or
- *    when a byte cannot yet bet written to the SCSI bus, a bus
- *    error occurs when reading or writing the pseudo DMA data
- *    register (hades_psdm_reg). We have to catch this bus error
- *    and try again to read or write the byte. If after several tries
- *    we still get a bus error, the interrupt handler is left. When
- *    the byte can be read or written, the interrupt handler is
- *    called again.
- * 
- * 2. The SCSI interrupt must be disabled in this interrupt handler.
- * 
- * 3. If we set the EOP signal, the SCSI controller still expects one
- *    byte to be read or written. Therefore the last byte is transferred
- *    separately, after setting the EOP signal.
- * 
- * 4. When this function is left, the address pointer (start_addr) is
- *    converted to a physical address. Because it points one byte
- *    further than the last transferred byte, it can point outside the
- *    current page. If virt_to_phys() is called with this address we
- *    might get an access error. Therefore virt_to_phys() is called with
- *    start_addr - 1 if the count has reached zero. The result is
- *    increased with one.
- */
-
-static irqreturn_t hades_dma_emulator(int irq, void *dummy)
-{
-	unsigned long dma_base;
-	register unsigned long dma_cnt asm ("d3");
-	static long save_buserr;
-	register unsigned long save_sp asm ("d4");
-	register int tries asm ("d5");
-	register unsigned char *start_addr asm ("a3"), *end_addr asm ("a4");
-	register unsigned char *eff_addr;
-	register unsigned char *psdm_reg;
-	unsigned long rem;
-
-	atari_disable_irq(IRQ_TT_MFP_SCSI);
-
-	/*
-	 * Read the dma address and count registers.
-	 */
-
-	dma_base = SCSI_DMA_READ_P(dma_addr);
-	dma_cnt = SCSI_DMA_READ_P(dma_cnt);
-
-	/*
-	 * Check if DMA is still enabled.
-	 */
-
-	if ((tt_scsi_dma.dma_ctrl & 2) == 0)
-	{
-		atari_enable_irq(IRQ_TT_MFP_SCSI);
-		return IRQ_HANDLED;
-	}
-
-	if (dma_cnt == 0)
-	{
-		printk(KERN_NOTICE "DMA emulation: count is zero.\n");
-		tt_scsi_dma.dma_ctrl &= 0xfd;	/* DMA ready. */
-		atari_enable_irq(IRQ_TT_MFP_SCSI);
-		return IRQ_HANDLED;
-	}
-
-	/*
-	 * Install new bus error routine.
-	 */
-
-	__asm__ __volatile__ ("movec.l	%%vbr,%%a0\n\t"
-			      "move.l	8(%%a0),%0\n\t"
-			      "move.l	%1,8(%%a0)\n\t"
-			      : "=&r" (save_buserr)
-			      : "r" (&&scsi_bus_error)
-			      : "a0" );
-
-	hades_dma_ctrl &= 0xfc;		/* Bus error and EOP off. */
-
-	/*
-	 * Save the stack pointer.
-	 */
-
-	__asm__ __volatile__ ("move.l	%%sp,%0\n\t"
-			      : "=&r" (save_sp) );
-
-	tries = 100;			/* Maximum number of bus errors. */
-	start_addr = phys_to_virt(dma_base);
-	end_addr = start_addr + dma_cnt;
-
-scsi_loop:
-	dma_cnt--;
-	rem = dma_cnt & (TRANSFER_SIZE - 1);
-	dma_cnt &= ~(TRANSFER_SIZE - 1);
-	psdm_reg = &hades_psdm_reg;
-
-	if (tt_scsi_dma.dma_ctrl & 1)	/* Read or write? */
-	{
-		/*
-		 * SCSI write. Abort when count is zero.
-		 */
-
-		switch (rem)
-		{
-		case 0:
-			while (dma_cnt > 0)
-			{
-				dma_cnt -= TRANSFER_SIZE;
-
-				*psdm_reg = *start_addr++;
-		case 15:
-				*psdm_reg = *start_addr++;
-		case 14:
-				*psdm_reg = *start_addr++;
-		case 13:
-				*psdm_reg = *start_addr++;
-		case 12:
-				*psdm_reg = *start_addr++;
-		case 11:
-				*psdm_reg = *start_addr++;
-		case 10:
-				*psdm_reg = *start_addr++;
-		case 9:
-				*psdm_reg = *start_addr++;
-		case 8:
-				*psdm_reg = *start_addr++;
-		case 7:
-				*psdm_reg = *start_addr++;
-		case 6:
-				*psdm_reg = *start_addr++;
-		case 5:
-				*psdm_reg = *start_addr++;
-		case 4:
-				*psdm_reg = *start_addr++;
-		case 3:
-				*psdm_reg = *start_addr++;
-		case 2:
-				*psdm_reg = *start_addr++;
-		case 1:
-				*psdm_reg = *start_addr++;
-			}
-		}
-
-		hades_dma_ctrl |= 1;	/* Set EOP. */
-		udelay(10);
-		*psdm_reg = *start_addr++;	/* Dummy byte. */
-		tt_scsi_dma.dma_ctrl &= 0xfd;	/* DMA ready. */
-	}
-	else
-	{
-		/*
-		 * SCSI read. Abort when count is zero.
-		 */
-
-		switch (rem)
-		{
-		case 0:
-			while (dma_cnt > 0)
-			{
-				dma_cnt -= TRANSFER_SIZE;
-
-				*start_addr++ = *psdm_reg;
-		case 15:
-				*start_addr++ = *psdm_reg;
-		case 14:
-				*start_addr++ = *psdm_reg;
-		case 13:
-				*start_addr++ = *psdm_reg;
-		case 12:
-				*start_addr++ = *psdm_reg;
-		case 11:
-				*start_addr++ = *psdm_reg;
-		case 10:
-				*start_addr++ = *psdm_reg;
-		case 9:
-				*start_addr++ = *psdm_reg;
-		case 8:
-				*start_addr++ = *psdm_reg;
-		case 7:
-				*start_addr++ = *psdm_reg;
-		case 6:
-				*start_addr++ = *psdm_reg;
-		case 5:
-				*start_addr++ = *psdm_reg;
-		case 4:
-				*start_addr++ = *psdm_reg;
-		case 3:
-				*start_addr++ = *psdm_reg;
-		case 2:
-				*start_addr++ = *psdm_reg;
-		case 1:
-				*start_addr++ = *psdm_reg;
-			}
-		}
-
-		hades_dma_ctrl |= 1;	/* Set EOP. */
-		udelay(10);
-		*start_addr++ = *psdm_reg;
-		tt_scsi_dma.dma_ctrl &= 0xfd;	/* DMA ready. */
-
-		set_restdata_reg(start_addr);
-	}
-
-	if (start_addr != end_addr)
-		printk(KERN_CRIT "DMA emulation: FATAL: Count is not zero at end of transfer.\n");
-
-	dma_cnt = end_addr - start_addr;
-
-scsi_end:
-	dma_base = (dma_cnt == 0) ? virt_to_phys(start_addr - 1) + 1 :  
-				    virt_to_phys(start_addr);
-
-	SCSI_DMA_WRITE_P(dma_addr, dma_base);
-	SCSI_DMA_WRITE_P(dma_cnt, dma_cnt);
-
-	/*
-	 * Restore old bus error routine.
-	 */
-
-	__asm__ __volatile__ ("movec.l	%%vbr,%%a0\n\t"
-			      "move.l	%0,8(%%a0)\n\t"
-			      :
-			      : "r" (save_buserr)
-			      : "a0" );
-
-	atari_enable_irq(IRQ_TT_MFP_SCSI);
-
-	return IRQ_HANDLED;
-
-scsi_bus_error:
-	/*
-	 * First check if the bus error is caused by our code.
-	 * If not, call the original handler.
-	 */
-
-	__asm__ __volatile__ ("cmp.l	%0,2(%%sp)\n\t"
-			      "bcs.s	.old_vector\n\t"
-			      "cmp.l	%1,2(%%sp)\n\t"
-			      "bls.s	.scsi_buserr\n"
-			".old_vector:\n\t"
-			      "move.l	%2,-(%%sp)\n\t"
-			      "rts\n"
-			".scsi_buserr:\n\t"
-			      :
-			      : "i" (&&scsi_loop), "i" (&&scsi_end),
-			        "m" (save_buserr) );
-
-	if (CPU_IS_060)
-	{
-		/*
-		 * Get effective address and restore the stack.
-		 */
-
-		__asm__ __volatile__ ("move.l	8(%%sp),%0\n\t"
-				      "move.l	%1,%%sp\n\t"
-				      : "=a&" (eff_addr)
-				      : "r" (save_sp) );
-	}
-	else
-	{
-		register struct m68040_frame *frame;
-
-		__asm__ __volatile__ ("lea	8(%%sp),%0\n\t"
-				      : "=a&" (frame) );
-
-		if (tt_scsi_dma.dma_ctrl & 1)
-		{
-			/*
-			 * Bus error while writing.
-			 */
-
-			if (frame->wb3s & WBV_040)
-			{
-				if (frame->wb3a == (long) &hades_psdm_reg)
-					start_addr--;
-				else
-					writeback(frame->wb3s, frame->wb3a,
-						  frame->wb3d, &&scsi_bus_error);
-			}
-
-			if (frame->wb2s & WBV_040)
-			{
-				if (frame->wb2a == (long) &hades_psdm_reg)
-					start_addr--;
-				else
-					writeback(frame->wb2s, frame->wb2a,
-						  frame->wb2d, &&scsi_bus_error);
-			}
-
-			if (frame->wb1s & WBV_040)
-			{
-				if (frame->wb1a == (long) &hades_psdm_reg)
-					start_addr--;
-			}
-		}
-		else
-		{
-			/*
-			 * Bus error while reading.
-			 */
-
-			if (frame->wb3s & WBV_040)
-				writeback(frame->wb3s, frame->wb3a,
-					  frame->wb3d, &&scsi_bus_error);
-		}
-
-		eff_addr = (unsigned char *) frame->faddr;
-
-		__asm__ __volatile__ ("move.l	%0,%%sp\n\t"
-				      :
-				      : "r" (save_sp) );
-	}
-
-	dma_cnt = end_addr - start_addr;
-
-	if (eff_addr == &hades_psdm_reg)
-	{
-		/*
-		 * Bus error occurred while reading the pseudo
-		 * DMA register. Time out.
-		 */
-
-		tries--;
-
-		if (tries <= 0)
-		{
-			if ((tt_scsi_dma.dma_ctrl & 1) == 0)	/* Read or write? */
-				set_restdata_reg(start_addr);
-
-			if (dma_cnt <= 1)
-				printk(KERN_CRIT "DMA emulation: Fatal "
-				       "error while %s the last byte.\n",
-				       (tt_scsi_dma.dma_ctrl & 1)
-				       ? "writing" : "reading");
-
-			goto scsi_end;
-		}
-		else
-			goto scsi_loop;
-	}
-	else
-	{
-		/*
-		 * Bus error during pseudo DMA transfer.
-		 * Terminate the DMA transfer.
-		 */
-
-		hades_dma_ctrl |= 3;	/* Set EOP and bus error. */
-		if ((tt_scsi_dma.dma_ctrl & 1) == 0)	/* Read or write? */
-			set_restdata_reg(start_addr);
-		goto scsi_end;
-	}
-}
diff --git a/drivers/scsi/atari_scsi.c b/drivers/scsi/atari_scsi.c
index f5732d8..21fe07f 100644
--- a/drivers/scsi/atari_scsi.c
+++ b/drivers/scsi/atari_scsi.c
@@ -249,10 +249,6 @@
 module_param(setup_hostid, int, 0);
 
 
-#if defined(CONFIG_TT_DMA_EMUL)
-#include "atari_dma_emul.c"
-#endif
-
 #if defined(REAL_DMA)
 
 static int scsi_dma_is_ignored_buserr(unsigned char dma_stat)
@@ -695,21 +691,8 @@
 #ifdef REAL_DMA
 		tt_scsi_dma.dma_ctrl = 0;
 		atari_dma_residual = 0;
-#ifdef CONFIG_TT_DMA_EMUL
-		if (MACH_IS_HADES) {
-			if (request_irq(IRQ_AUTO_2, hades_dma_emulator,
-					 IRQ_TYPE_PRIO, "Hades DMA emulator",
-					 hades_dma_emulator)) {
-				printk(KERN_ERR "atari_scsi_detect: cannot allocate irq %d, aborting (MACH_IS_HADES)",IRQ_AUTO_2);
-				free_irq(IRQ_TT_MFP_SCSI, instance);
-				scsi_unregister(atari_scsi_host);
-				atari_stram_free(atari_dma_buffer);
-				atari_dma_buffer = 0;
-				return 0;
-			}
-		}
-#endif
-		if (MACH_IS_MEDUSA || MACH_IS_HADES) {
+
+		if (MACH_IS_MEDUSA) {
 			/* While the read overruns (described by Drew Eckhardt in
 			 * NCR5380.c) never happened on TTs, they do in fact on the Medusa
 			 * (This was the cause why SCSI didn't work right for so long
@@ -1007,11 +990,7 @@
 					Scsi_Cmnd *cmd, int write_flag)
 {
 	unsigned long	possible_len, limit;
-#ifndef CONFIG_TT_DMA_EMUL
-	if (MACH_IS_HADES)
-		/* Hades has no SCSI DMA at all :-( Always force use of PIO */
-		return 0;
-#endif
+
 	if (IS_A_TT())
 		/* TT SCSI DMA can transfer arbitrary #bytes */
 		return wanted_len;
diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c
index d4104a3b..d3ca7d3 100644
--- a/drivers/serial/8250.c
+++ b/drivers/serial/8250.c
@@ -2969,6 +2969,9 @@
 		"%d ports, IRQ sharing %sabled\n", nr_uarts,
 		share_irqs ? "en" : "dis");
 
+	for (i = 0; i < NR_IRQS; i++)
+		spin_lock_init(&irq_lists[i].lock);
+
 #ifdef CONFIG_SPARC
 	ret = sunserial_register_minors(&serial8250_reg, UART_NR);
 #else
diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c
index 9cbff84..da91bb1 100644
--- a/drivers/video/console/fbcon.c
+++ b/drivers/video/console/fbcon.c
@@ -1855,8 +1855,6 @@
 	struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
 	struct display *p = &fb_display[vc->vc_num];
 	int scroll_partial = info->flags & FBINFO_PARTIAL_PAN_OK;
-	unsigned short saved_ec;
-	int ret;
 
 	if (fbcon_is_inactive(vc, info))
 		return -EINVAL;
@@ -1869,11 +1867,6 @@
 	 *           whole screen (prevents flicker).
 	 */
 
-	saved_ec = vc->vc_video_erase_char;
-	vc->vc_video_erase_char = vc->vc_scrl_erase_char;
-
-	ret = 0;
-
 	switch (dir) {
 	case SM_UP:
 		if (count > vc->vc_rows)	/* Maximum realistic size */
@@ -1890,9 +1883,9 @@
 			scr_memsetw((unsigned short *) (vc->vc_origin +
 							vc->vc_size_row *
 							(b - count)),
-				    vc->vc_scrl_erase_char,
+				    vc->vc_video_erase_char,
 				    vc->vc_size_row * count);
-			ret = 1;
+			return 1;
 			break;
 
 		case SCROLL_WRAP_MOVE:
@@ -1962,10 +1955,9 @@
 			scr_memsetw((unsigned short *) (vc->vc_origin +
 							vc->vc_size_row *
 							(b - count)),
-				    vc->vc_scrl_erase_char,
+				    vc->vc_video_erase_char,
 				    vc->vc_size_row * count);
-			ret = 1;
-			break;
+			return 1;
 		}
 		break;
 
@@ -1982,9 +1974,9 @@
 			scr_memsetw((unsigned short *) (vc->vc_origin +
 							vc->vc_size_row *
 							t),
-				    vc->vc_scrl_erase_char,
+				    vc->vc_video_erase_char,
 				    vc->vc_size_row * count);
-			ret = 1;
+			return 1;
 			break;
 
 		case SCROLL_WRAP_MOVE:
@@ -2052,15 +2044,12 @@
 			scr_memsetw((unsigned short *) (vc->vc_origin +
 							vc->vc_size_row *
 							t),
-				    vc->vc_scrl_erase_char,
+				    vc->vc_video_erase_char,
 				    vc->vc_size_row * count);
-			ret = 1;
-			break;
+			return 1;
 		}
-		break;
 	}
-	vc->vc_video_erase_char = saved_ec;
-	return ret;
+	return 0;
 }
 
 
@@ -2522,9 +2511,6 @@
 			c = vc->vc_video_erase_char;
 			vc->vc_video_erase_char =
 			    ((c & 0xfe00) >> 1) | (c & 0xff);
-			c = vc->vc_scrl_erase_char;
-			vc->vc_scrl_erase_char =
-			    ((c & 0xFE00) >> 1) | (c & 0xFF);
 			vc->vc_attr >>= 1;
 		}
 	} else if (!vc->vc_hi_font_mask && cnt == 512) {
@@ -2555,14 +2541,9 @@
 			if (vc->vc_can_do_color) {
 				vc->vc_video_erase_char =
 				    ((c & 0xff00) << 1) | (c & 0xff);
-				c = vc->vc_scrl_erase_char;
-				vc->vc_scrl_erase_char =
-				    ((c & 0xFF00) << 1) | (c & 0xFF);
 				vc->vc_attr <<= 1;
-			} else {
+			} else
 				vc->vc_video_erase_char = c & ~0x100;
-				vc->vc_scrl_erase_char = c & ~0x100;
-			}
 		}
 
 	}
diff --git a/drivers/video/console/mdacon.c b/drivers/video/console/mdacon.c
index 9901064..dd3eaaa 100644
--- a/drivers/video/console/mdacon.c
+++ b/drivers/video/console/mdacon.c
@@ -533,7 +533,7 @@
 
 static int mdacon_scroll(struct vc_data *c, int t, int b, int dir, int lines)
 {
-	u16 eattr = mda_convert_attr(c->vc_scrl_erase_char);
+	u16 eattr = mda_convert_attr(c->vc_video_erase_char);
 
 	if (!lines)
 		return 0;
diff --git a/drivers/video/console/sticon.c b/drivers/video/console/sticon.c
index 4055dbd..491c1c1 100644
--- a/drivers/video/console/sticon.c
+++ b/drivers/video/console/sticon.c
@@ -170,12 +170,12 @@
     switch (dir) {
     case SM_UP:
 	sti_bmove(sti, t + count, 0, t, 0, b - t - count, conp->vc_cols);
-	sti_clear(sti, b - count, 0, count, conp->vc_cols, conp->vc_scrl_erase_char);
+	sti_clear(sti, b - count, 0, count, conp->vc_cols, conp->vc_video_erase_char);
 	break;
 
     case SM_DOWN:
 	sti_bmove(sti, t, 0, t + count, 0, b - t - count, conp->vc_cols);
-	sti_clear(sti, t, 0, count, conp->vc_cols, conp->vc_scrl_erase_char);
+	sti_clear(sti, t, 0, count, conp->vc_cols, conp->vc_video_erase_char);
 	break;
     }
 
diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c
index bd1f57b..6df29a6 100644
--- a/drivers/video/console/vgacon.c
+++ b/drivers/video/console/vgacon.c
@@ -1350,7 +1350,7 @@
 		} else
 			c->vc_origin += delta;
 		scr_memsetw((u16 *) (c->vc_origin + c->vc_screenbuf_size -
-				     delta), c->vc_scrl_erase_char,
+				     delta), c->vc_video_erase_char,
 			    delta);
 	} else {
 		if (oldo - delta < vga_vram_base) {
@@ -1363,7 +1363,7 @@
 		} else
 			c->vc_origin -= delta;
 		c->vc_scr_end = c->vc_origin + c->vc_screenbuf_size;
-		scr_memsetw((u16 *) (c->vc_origin), c->vc_scrl_erase_char,
+		scr_memsetw((u16 *) (c->vc_origin), c->vc_video_erase_char,
 			    delta);
 	}
 	c->vc_scr_end = c->vc_origin + c->vc_screenbuf_size;
diff --git a/fs/Kconfig b/fs/Kconfig
index f54a157..9e9d70c 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -220,17 +220,16 @@
 	tristate
 	help
 	  This is a generic journalling layer for block devices.  It is
-	  currently used by the ext3 and OCFS2 file systems, but it could
-	  also be used to add journal support to other file systems or block
+	  currently used by the ext3 file system, but it could also be
+	  used to add journal support to other file systems or block
 	  devices such as RAID or LVM.
 
-	  If you are using the ext3 or OCFS2 file systems, you need to
-	  say Y here. If you are not using ext3 OCFS2 then you will probably
-	  want to say N.
+	  If you are using the ext3 file system, you need to say Y here.
+	  If you are not using ext3 then you will probably want to say N.
 
 	  To compile this device as a module, choose M here: the module will be
-	  called jbd.  If you are compiling ext3 or OCFS2 into the kernel,
-	  you cannot compile this code as a module.
+	  called jbd.  If you are compiling ext3 into the kernel, you
+	  cannot compile this code as a module.
 
 config JBD_DEBUG
 	bool "JBD (ext3) debugging support"
@@ -254,15 +253,16 @@
 	help
 	  This is a generic journaling layer for block devices that support
 	  both 32-bit and 64-bit block numbers.  It is currently used by
-	  the ext4 filesystem, but it could also be used to add
+	  the ext4 and OCFS2 filesystems, but it could also be used to add
 	  journal support to other file systems or block devices such
 	  as RAID or LVM.
 
-	  If you are using ext4, you need to say Y here. If you are not
-	  using ext4 then you will probably want to say N.
+	  If you are using ext4 or OCFS2, you need to say Y here.
+	  If you are not using ext4 or OCFS2 then you will
+	  probably want to say N.
 
 	  To compile this device as a module, choose M here. The module will be
-	  called jbd2.  If you are compiling ext4 into the kernel,
+	  called jbd2.  If you are compiling ext4 or OCFS2 into the kernel,
 	  you cannot compile this code as a module.
 
 config JBD2_DEBUG
@@ -433,6 +433,14 @@
 	bool
 	default n
 
+config FILE_LOCKING
+	bool "Enable POSIX file locking API" if EMBEDDED
+	default y
+	help
+	  This option enables standard file locking support, required
+          for filesystems like NFS and for the flock() system
+          call. Disabling this option saves about 11k.
+
 source "fs/xfs/Kconfig"
 source "fs/gfs2/Kconfig"
 
@@ -440,7 +448,7 @@
 	tristate "OCFS2 file system support"
 	depends on NET && SYSFS
 	select CONFIGFS_FS
-	select JBD
+	select JBD2
 	select CRC32
 	help
 	  OCFS2 is a general purpose extent based shared disk cluster file
@@ -511,6 +519,16 @@
 	  this option for debugging only as it is likely to decrease
 	  performance of the filesystem.
 
+config OCFS2_COMPAT_JBD
+	bool "Use JBD for compatibility"
+	depends on OCFS2_FS
+	default n
+	select JBD
+	help
+	  The ocfs2 filesystem now uses JBD2 for its journalling.  JBD2
+	  is backwards compatible with JBD.  It is safe to say N here.
+	  However, if you really want to use the original JBD, say Y here.
+
 endif # BLOCK
 
 config DNOTIFY
@@ -1779,6 +1797,28 @@
 
 	  If unsure, say N.
 
+config SUNRPC_REGISTER_V4
+	bool "Register local RPC services via rpcbind v4 (EXPERIMENTAL)"
+	depends on SUNRPC && EXPERIMENTAL
+	default n
+	help
+	  Sun added support for registering RPC services at an IPv6
+	  address by creating two new versions of the rpcbind protocol
+	  (RFC 1833).
+
+	  This option enables support in the kernel RPC server for
+	  registering kernel RPC services via version 4 of the rpcbind
+	  protocol.  If you enable this option, you must run a portmapper
+	  daemon that supports rpcbind protocol version 4.
+
+	  Serving NFS over IPv6 from knfsd (the kernel's NFS server)
+	  requires that you enable this option and use a portmapper that
+	  supports rpcbind version 4.
+
+	  If unsure, say N to get traditional behavior (register kernel
+	  RPC services using only rpcbind version 2).  Distributions
+	  using the legacy Linux portmapper daemon must say N here.
+
 config RPCSEC_GSS_KRB5
 	tristate "Secure RPC: Kerberos V mechanism (EXPERIMENTAL)"
 	depends on SUNRPC && EXPERIMENTAL
diff --git a/fs/Makefile b/fs/Makefile
index de404b0..b6f27dc 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -7,7 +7,7 @@
 
 obj-y :=	open.o read_write.o file_table.o super.o \
 		char_dev.o stat.o exec.o pipe.o namei.o fcntl.o \
-		ioctl.o readdir.o select.o fifo.o locks.o dcache.o inode.o \
+		ioctl.o readdir.o select.o fifo.o dcache.o inode.o \
 		attr.o bad_inode.o file.o filesystems.o namespace.o aio.o \
 		seq_file.o xattr.o libfs.o fs-writeback.o \
 		pnode.o drop_caches.o splice.o sync.o utimes.o \
@@ -27,6 +27,7 @@
 obj-$(CONFIG_SIGNALFD)		+= signalfd.o
 obj-$(CONFIG_TIMERFD)		+= timerfd.o
 obj-$(CONFIG_EVENTFD)		+= eventfd.o
+obj-$(CONFIG_FILE_LOCKING)      += locks.o
 obj-$(CONFIG_COMPAT)		+= compat.o compat_ioctl.o
 
 nfsd-$(CONFIG_NFSD)		:= nfsctl.o
diff --git a/fs/lockd/Makefile b/fs/lockd/Makefile
index 7725a0a..97f6073 100644
--- a/fs/lockd/Makefile
+++ b/fs/lockd/Makefile
@@ -5,6 +5,6 @@
 obj-$(CONFIG_LOCKD) += lockd.o
 
 lockd-objs-y := clntlock.o clntproc.o host.o svc.o svclock.o svcshare.o \
-	        svcproc.o svcsubs.o mon.o xdr.o
+	        svcproc.o svcsubs.o mon.o xdr.o grace.o
 lockd-objs-$(CONFIG_LOCKD_V4) += xdr4.o svc4proc.o
 lockd-objs		      := $(lockd-objs-y)
diff --git a/fs/lockd/clntlock.c b/fs/lockd/clntlock.c
index 0b45fd3..8307dd6 100644
--- a/fs/lockd/clntlock.c
+++ b/fs/lockd/clntlock.c
@@ -54,14 +54,13 @@
 	u32 nlm_version = (nlm_init->nfs_version == 2) ? 1 : 4;
 	int status;
 
-	status = lockd_up(nlm_init->protocol);
+	status = lockd_up();
 	if (status < 0)
 		return ERR_PTR(status);
 
-	host = nlmclnt_lookup_host((struct sockaddr_in *)nlm_init->address,
+	host = nlmclnt_lookup_host(nlm_init->address, nlm_init->addrlen,
 				   nlm_init->protocol, nlm_version,
-				   nlm_init->hostname,
-				   strlen(nlm_init->hostname));
+				   nlm_init->hostname);
 	if (host == NULL) {
 		lockd_down();
 		return ERR_PTR(-ENOLCK);
@@ -142,7 +141,7 @@
 /*
  * The server lockd has called us back to tell us the lock was granted
  */
-__be32 nlmclnt_grant(const struct sockaddr_in *addr, const struct nlm_lock *lock)
+__be32 nlmclnt_grant(const struct sockaddr *addr, const struct nlm_lock *lock)
 {
 	const struct file_lock *fl = &lock->fl;
 	const struct nfs_fh *fh = &lock->fh;
@@ -166,7 +165,7 @@
 		 */
 		if (fl_blocked->fl_u.nfs_fl.owner->pid != lock->svid)
 			continue;
-		if (!nlm_cmp_addr(&block->b_host->h_addr, addr))
+		if (!nlm_cmp_addr(nlm_addr(block->b_host), addr))
 			continue;
 		if (nfs_compare_fh(NFS_FH(fl_blocked->fl_file->f_path.dentry->d_inode) ,fh) != 0)
 			continue;
@@ -216,7 +215,7 @@
 	/* This one ensures that our parent doesn't terminate while the
 	 * reclaim is in progress */
 	lock_kernel();
-	lockd_up(0); /* note: this cannot fail as lockd is already running */
+	lockd_up();	/* note: this cannot fail as lockd is already running */
 
 	dprintk("lockd: reclaiming locks for host %s\n", host->h_name);
 
diff --git a/fs/lockd/grace.c b/fs/lockd/grace.c
new file mode 100644
index 0000000..183cc1f
--- /dev/null
+++ b/fs/lockd/grace.c
@@ -0,0 +1,59 @@
+/*
+ * Common code for control of lockd and nfsv4 grace periods.
+ */
+
+#include <linux/module.h>
+#include <linux/lockd/bind.h>
+
+static LIST_HEAD(grace_list);
+static DEFINE_SPINLOCK(grace_lock);
+
+/**
+ * locks_start_grace
+ * @lm: who this grace period is for
+ *
+ * A grace period is a period during which locks should not be given
+ * out.  Currently grace periods are only enforced by the two lock
+ * managers (lockd and nfsd), using the locks_in_grace() function to
+ * check when they are in a grace period.
+ *
+ * This function is called to start a grace period.
+ */
+void locks_start_grace(struct lock_manager *lm)
+{
+	spin_lock(&grace_lock);
+	list_add(&lm->list, &grace_list);
+	spin_unlock(&grace_lock);
+}
+EXPORT_SYMBOL_GPL(locks_start_grace);
+
+/**
+ * locks_end_grace
+ * @lm: who this grace period is for
+ *
+ * Call this function to state that the given lock manager is ready to
+ * resume regular locking.  The grace period will not end until all lock
+ * managers that called locks_start_grace() also call locks_end_grace().
+ * Note that callers count on it being safe to call this more than once,
+ * and the second call should be a no-op.
+ */
+void locks_end_grace(struct lock_manager *lm)
+{
+	spin_lock(&grace_lock);
+	list_del_init(&lm->list);
+	spin_unlock(&grace_lock);
+}
+EXPORT_SYMBOL_GPL(locks_end_grace);
+
+/**
+ * locks_in_grace
+ *
+ * Lock managers call this function to determine when it is OK for them
+ * to answer ordinary lock requests, and when they should accept only
+ * lock reclaims.
+ */
+int locks_in_grace(void)
+{
+	return !list_empty(&grace_list);
+}
+EXPORT_SYMBOL_GPL(locks_in_grace);
diff --git a/fs/lockd/host.c b/fs/lockd/host.c
index a17664c..9fd8889 100644
--- a/fs/lockd/host.c
+++ b/fs/lockd/host.c
@@ -11,16 +11,17 @@
 #include <linux/types.h>
 #include <linux/slab.h>
 #include <linux/in.h>
+#include <linux/in6.h>
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/svc.h>
 #include <linux/lockd/lockd.h>
 #include <linux/lockd/sm_inter.h>
 #include <linux/mutex.h>
 
+#include <net/ipv6.h>
 
 #define NLMDBG_FACILITY		NLMDBG_HOSTCACHE
 #define NLM_HOST_NRHASH		32
-#define NLM_ADDRHASH(addr)	(ntohl(addr) & (NLM_HOST_NRHASH-1))
 #define NLM_HOST_REBIND		(60 * HZ)
 #define NLM_HOST_EXPIRE		(300 * HZ)
 #define NLM_HOST_COLLECT	(120 * HZ)
@@ -30,42 +31,115 @@
 static int			nrhosts;
 static DEFINE_MUTEX(nlm_host_mutex);
 
-
 static void			nlm_gc_hosts(void);
-static struct nsm_handle *	__nsm_find(const struct sockaddr_in *,
-					const char *, unsigned int, int);
-static struct nsm_handle *	nsm_find(const struct sockaddr_in *sin,
-					 const char *hostname,
-					 unsigned int hostname_len);
+static struct nsm_handle	*nsm_find(const struct sockaddr *sap,
+						const size_t salen,
+						const char *hostname,
+						const size_t hostname_len,
+						const int create);
+
+struct nlm_lookup_host_info {
+	const int		server;		/* search for server|client */
+	const struct sockaddr	*sap;		/* address to search for */
+	const size_t		salen;		/* it's length */
+	const unsigned short	protocol;	/* transport to search for*/
+	const u32		version;	/* NLM version to search for */
+	const char		*hostname;	/* remote's hostname */
+	const size_t		hostname_len;	/* it's length */
+	const struct sockaddr	*src_sap;	/* our address (optional) */
+	const size_t		src_len;	/* it's length */
+};
+
+/*
+ * Hash function must work well on big- and little-endian platforms
+ */
+static unsigned int __nlm_hash32(const __be32 n)
+{
+	unsigned int hash = (__force u32)n ^ ((__force u32)n >> 16);
+	return hash ^ (hash >> 8);
+}
+
+static unsigned int __nlm_hash_addr4(const struct sockaddr *sap)
+{
+	const struct sockaddr_in *sin = (struct sockaddr_in *)sap;
+	return __nlm_hash32(sin->sin_addr.s_addr);
+}
+
+static unsigned int __nlm_hash_addr6(const struct sockaddr *sap)
+{
+	const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
+	const struct in6_addr addr = sin6->sin6_addr;
+	return __nlm_hash32(addr.s6_addr32[0]) ^
+	       __nlm_hash32(addr.s6_addr32[1]) ^
+	       __nlm_hash32(addr.s6_addr32[2]) ^
+	       __nlm_hash32(addr.s6_addr32[3]);
+}
+
+static unsigned int nlm_hash_address(const struct sockaddr *sap)
+{
+	unsigned int hash;
+
+	switch (sap->sa_family) {
+	case AF_INET:
+		hash = __nlm_hash_addr4(sap);
+		break;
+	case AF_INET6:
+		hash = __nlm_hash_addr6(sap);
+		break;
+	default:
+		hash = 0;
+	}
+	return hash & (NLM_HOST_NRHASH - 1);
+}
+
+static void nlm_clear_port(struct sockaddr *sap)
+{
+	switch (sap->sa_family) {
+	case AF_INET:
+		((struct sockaddr_in *)sap)->sin_port = 0;
+		break;
+	case AF_INET6:
+		((struct sockaddr_in6 *)sap)->sin6_port = 0;
+		break;
+	}
+}
+
+static void nlm_display_address(const struct sockaddr *sap,
+				char *buf, const size_t len)
+{
+	const struct sockaddr_in *sin = (struct sockaddr_in *)sap;
+	const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
+
+	switch (sap->sa_family) {
+	case AF_UNSPEC:
+		snprintf(buf, len, "unspecified");
+		break;
+	case AF_INET:
+		snprintf(buf, len, NIPQUAD_FMT, NIPQUAD(sin->sin_addr.s_addr));
+		break;
+	case AF_INET6:
+		if (ipv6_addr_v4mapped(&sin6->sin6_addr))
+			snprintf(buf, len, NIPQUAD_FMT,
+				 NIPQUAD(sin6->sin6_addr.s6_addr32[3]));
+		else
+			snprintf(buf, len, NIP6_FMT, NIP6(sin6->sin6_addr));
+		break;
+	default:
+		snprintf(buf, len, "unsupported address family");
+		break;
+	}
+}
 
 /*
  * Common host lookup routine for server & client
  */
-static struct nlm_host *nlm_lookup_host(int server,
-					const struct sockaddr_in *sin,
-					int proto, u32 version,
-					const char *hostname,
-					unsigned int hostname_len,
-					const struct sockaddr_in *ssin)
+static struct nlm_host *nlm_lookup_host(struct nlm_lookup_host_info *ni)
 {
 	struct hlist_head *chain;
 	struct hlist_node *pos;
 	struct nlm_host	*host;
 	struct nsm_handle *nsm = NULL;
-	int		hash;
 
-	dprintk("lockd: nlm_lookup_host("NIPQUAD_FMT"->"NIPQUAD_FMT
-			", p=%d, v=%u, my role=%s, name=%.*s)\n",
-			NIPQUAD(ssin->sin_addr.s_addr),
-			NIPQUAD(sin->sin_addr.s_addr), proto, version,
-			server? "server" : "client",
-			hostname_len,
-			hostname? hostname : "<none>");
-
-
-	hash = NLM_ADDRHASH(sin->sin_addr.s_addr);
-
-	/* Lock hash table */
 	mutex_lock(&nlm_host_mutex);
 
 	if (time_after_eq(jiffies, next_gc))
@@ -78,22 +152,22 @@
 	 * different NLM rpc_clients into one single nlm_host object.
 	 * This would allow us to have one nlm_host per address.
 	 */
-	chain = &nlm_hosts[hash];
+	chain = &nlm_hosts[nlm_hash_address(ni->sap)];
 	hlist_for_each_entry(host, pos, chain, h_hash) {
-		if (!nlm_cmp_addr(&host->h_addr, sin))
+		if (!nlm_cmp_addr(nlm_addr(host), ni->sap))
 			continue;
 
 		/* See if we have an NSM handle for this client */
 		if (!nsm)
 			nsm = host->h_nsmhandle;
 
-		if (host->h_proto != proto)
+		if (host->h_proto != ni->protocol)
 			continue;
-		if (host->h_version != version)
+		if (host->h_version != ni->version)
 			continue;
-		if (host->h_server != server)
+		if (host->h_server != ni->server)
 			continue;
-		if (!nlm_cmp_addr(&host->h_saddr, ssin))
+		if (!nlm_cmp_addr(nlm_srcaddr(host), ni->src_sap))
 			continue;
 
 		/* Move to head of hash chain. */
@@ -101,30 +175,41 @@
 		hlist_add_head(&host->h_hash, chain);
 
 		nlm_get_host(host);
+		dprintk("lockd: nlm_lookup_host found host %s (%s)\n",
+				host->h_name, host->h_addrbuf);
 		goto out;
 	}
+
+	/*
+	 * The host wasn't in our hash table.  If we don't
+	 * have an NSM handle for it yet, create one.
+	 */
 	if (nsm)
 		atomic_inc(&nsm->sm_count);
-
-	host = NULL;
-
-	/* Sadly, the host isn't in our hash table yet. See if
-	 * we have an NSM handle for it. If not, create one.
-	 */
-	if (!nsm && !(nsm = nsm_find(sin, hostname, hostname_len)))
-		goto out;
+	else {
+		host = NULL;
+		nsm = nsm_find(ni->sap, ni->salen,
+				ni->hostname, ni->hostname_len, 1);
+		if (!nsm) {
+			dprintk("lockd: nlm_lookup_host failed; "
+				"no nsm handle\n");
+			goto out;
+		}
+	}
 
 	host = kzalloc(sizeof(*host), GFP_KERNEL);
 	if (!host) {
 		nsm_release(nsm);
+		dprintk("lockd: nlm_lookup_host failed; no memory\n");
 		goto out;
 	}
 	host->h_name	   = nsm->sm_name;
-	host->h_addr       = *sin;
-	host->h_addr.sin_port = 0;	/* ouch! */
-	host->h_saddr	   = *ssin;
-	host->h_version    = version;
-	host->h_proto      = proto;
+	memcpy(nlm_addr(host), ni->sap, ni->salen);
+	host->h_addrlen = ni->salen;
+	nlm_clear_port(nlm_addr(host));
+	memcpy(nlm_srcaddr(host), ni->src_sap, ni->src_len);
+	host->h_version    = ni->version;
+	host->h_proto      = ni->protocol;
 	host->h_rpcclnt    = NULL;
 	mutex_init(&host->h_mutex);
 	host->h_nextrebind = jiffies + NLM_HOST_REBIND;
@@ -135,7 +220,7 @@
 	host->h_state      = 0;			/* pseudo NSM state */
 	host->h_nsmstate   = 0;			/* real NSM state */
 	host->h_nsmhandle  = nsm;
-	host->h_server	   = server;
+	host->h_server	   = ni->server;
 	hlist_add_head(&host->h_hash, chain);
 	INIT_LIST_HEAD(&host->h_lockowners);
 	spin_lock_init(&host->h_lock);
@@ -143,6 +228,15 @@
 	INIT_LIST_HEAD(&host->h_reclaim);
 
 	nrhosts++;
+
+	nlm_display_address((struct sockaddr *)&host->h_addr,
+				host->h_addrbuf, sizeof(host->h_addrbuf));
+	nlm_display_address((struct sockaddr *)&host->h_srcaddr,
+				host->h_srcaddrbuf, sizeof(host->h_srcaddrbuf));
+
+	dprintk("lockd: nlm_lookup_host created host %s\n",
+			host->h_name);
+
 out:
 	mutex_unlock(&nlm_host_mutex);
 	return host;
@@ -170,33 +264,103 @@
 	kfree(host);
 }
 
-/*
- * Find an NLM server handle in the cache. If there is none, create it.
+/**
+ * nlmclnt_lookup_host - Find an NLM host handle matching a remote server
+ * @sap: network address of server
+ * @salen: length of server address
+ * @protocol: transport protocol to use
+ * @version: NLM protocol version
+ * @hostname: '\0'-terminated hostname of server
+ *
+ * Returns an nlm_host structure that matches the passed-in
+ * [server address, transport protocol, NLM version, server hostname].
+ * If one doesn't already exist in the host cache, a new handle is
+ * created and returned.
  */
-struct nlm_host *nlmclnt_lookup_host(const struct sockaddr_in *sin,
-				     int proto, u32 version,
-				     const char *hostname,
-				     unsigned int hostname_len)
+struct nlm_host *nlmclnt_lookup_host(const struct sockaddr *sap,
+				     const size_t salen,
+				     const unsigned short protocol,
+				     const u32 version, const char *hostname)
 {
-	struct sockaddr_in ssin = {0};
+	const struct sockaddr source = {
+		.sa_family	= AF_UNSPEC,
+	};
+	struct nlm_lookup_host_info ni = {
+		.server		= 0,
+		.sap		= sap,
+		.salen		= salen,
+		.protocol	= protocol,
+		.version	= version,
+		.hostname	= hostname,
+		.hostname_len	= strlen(hostname),
+		.src_sap	= &source,
+		.src_len	= sizeof(source),
+	};
 
-	return nlm_lookup_host(0, sin, proto, version,
-			       hostname, hostname_len, &ssin);
+	dprintk("lockd: %s(host='%s', vers=%u, proto=%s)\n", __func__,
+			(hostname ? hostname : "<none>"), version,
+			(protocol == IPPROTO_UDP ? "udp" : "tcp"));
+
+	return nlm_lookup_host(&ni);
 }
 
-/*
- * Find an NLM client handle in the cache. If there is none, create it.
+/**
+ * nlmsvc_lookup_host - Find an NLM host handle matching a remote client
+ * @rqstp: incoming NLM request
+ * @hostname: name of client host
+ * @hostname_len: length of client hostname
+ *
+ * Returns an nlm_host structure that matches the [client address,
+ * transport protocol, NLM version, client hostname] of the passed-in
+ * NLM request.  If one doesn't already exist in the host cache, a
+ * new handle is created and returned.
+ *
+ * Before possibly creating a new nlm_host, construct a sockaddr
+ * for a specific source address in case the local system has
+ * multiple network addresses.  The family of the address in
+ * rq_daddr is guaranteed to be the same as the family of the
+ * address in rq_addr, so it's safe to use the same family for
+ * the source address.
  */
-struct nlm_host *
-nlmsvc_lookup_host(struct svc_rqst *rqstp,
-			const char *hostname, unsigned int hostname_len)
+struct nlm_host *nlmsvc_lookup_host(const struct svc_rqst *rqstp,
+				    const char *hostname,
+				    const size_t hostname_len)
 {
-	struct sockaddr_in ssin = {0};
+	struct sockaddr_in sin = {
+		.sin_family	= AF_INET,
+	};
+	struct sockaddr_in6 sin6 = {
+		.sin6_family	= AF_INET6,
+	};
+	struct nlm_lookup_host_info ni = {
+		.server		= 1,
+		.sap		= svc_addr(rqstp),
+		.salen		= rqstp->rq_addrlen,
+		.protocol	= rqstp->rq_prot,
+		.version	= rqstp->rq_vers,
+		.hostname	= hostname,
+		.hostname_len	= hostname_len,
+		.src_len	= rqstp->rq_addrlen,
+	};
 
-	ssin.sin_addr = rqstp->rq_daddr.addr;
-	return nlm_lookup_host(1, svc_addr_in(rqstp),
-			       rqstp->rq_prot, rqstp->rq_vers,
-			       hostname, hostname_len, &ssin);
+	dprintk("lockd: %s(host='%*s', vers=%u, proto=%s)\n", __func__,
+			(int)hostname_len, hostname, rqstp->rq_vers,
+			(rqstp->rq_prot == IPPROTO_UDP ? "udp" : "tcp"));
+
+	switch (ni.sap->sa_family) {
+	case AF_INET:
+		sin.sin_addr.s_addr = rqstp->rq_daddr.addr.s_addr;
+		ni.src_sap = (struct sockaddr *)&sin;
+		break;
+	case AF_INET6:
+		ipv6_addr_copy(&sin6.sin6_addr, &rqstp->rq_daddr.addr6);
+		ni.src_sap = (struct sockaddr *)&sin6;
+		break;
+	default:
+		return NULL;
+	}
+
+	return nlm_lookup_host(&ni);
 }
 
 /*
@@ -207,9 +371,8 @@
 {
 	struct rpc_clnt	*clnt;
 
-	dprintk("lockd: nlm_bind_host("NIPQUAD_FMT"->"NIPQUAD_FMT")\n",
-			NIPQUAD(host->h_saddr.sin_addr),
-			NIPQUAD(host->h_addr.sin_addr));
+	dprintk("lockd: nlm_bind_host %s (%s), my addr=%s\n",
+			host->h_name, host->h_addrbuf, host->h_srcaddrbuf);
 
 	/* Lock host handle */
 	mutex_lock(&host->h_mutex);
@@ -221,7 +384,7 @@
 		if (time_after_eq(jiffies, host->h_nextrebind)) {
 			rpc_force_rebind(clnt);
 			host->h_nextrebind = jiffies + NLM_HOST_REBIND;
-			dprintk("lockd: next rebind in %ld jiffies\n",
+			dprintk("lockd: next rebind in %lu jiffies\n",
 					host->h_nextrebind - jiffies);
 		}
 	} else {
@@ -234,9 +397,9 @@
 		};
 		struct rpc_create_args args = {
 			.protocol	= host->h_proto,
-			.address	= (struct sockaddr *)&host->h_addr,
-			.addrsize	= sizeof(host->h_addr),
-			.saddress	= (struct sockaddr *)&host->h_saddr,
+			.address	= nlm_addr(host),
+			.addrsize	= host->h_addrlen,
+			.saddress	= nlm_srcaddr(host),
 			.timeout	= &timeparms,
 			.servername	= host->h_name,
 			.program	= &nlm_program,
@@ -324,12 +487,16 @@
 	struct nsm_handle *nsm;
 	struct nlm_host	*host;
 
-	dprintk("lockd: nlm_host_rebooted(%s, %u.%u.%u.%u)\n",
-			hostname, NIPQUAD(sin->sin_addr));
-
-	/* Find the NSM handle for this peer */
-	if (!(nsm = __nsm_find(sin, hostname, hostname_len, 0)))
+	nsm = nsm_find((struct sockaddr *)sin, sizeof(*sin),
+			hostname, hostname_len, 0);
+	if (nsm == NULL) {
+		dprintk("lockd: never saw rebooted peer '%.*s' before\n",
+				hostname_len, hostname);
 		return;
+	}
+
+	dprintk("lockd: nlm_host_rebooted(%.*s, %s)\n",
+			hostname_len, hostname, nsm->sm_addrbuf);
 
 	/* When reclaiming locks on this peer, make sure that
 	 * we set up a new notification */
@@ -461,22 +628,23 @@
 static LIST_HEAD(nsm_handles);
 static DEFINE_SPINLOCK(nsm_lock);
 
-static struct nsm_handle *
-__nsm_find(const struct sockaddr_in *sin,
-		const char *hostname, unsigned int hostname_len,
-		int create)
+static struct nsm_handle *nsm_find(const struct sockaddr *sap,
+				   const size_t salen,
+				   const char *hostname,
+				   const size_t hostname_len,
+				   const int create)
 {
 	struct nsm_handle *nsm = NULL;
 	struct nsm_handle *pos;
 
-	if (!sin)
+	if (!sap)
 		return NULL;
 
 	if (hostname && memchr(hostname, '/', hostname_len) != NULL) {
 		if (printk_ratelimit()) {
 			printk(KERN_WARNING "Invalid hostname \"%.*s\" "
 					    "in NFS lock request\n",
-				hostname_len, hostname);
+				(int)hostname_len, hostname);
 		}
 		return NULL;
 	}
@@ -489,7 +657,7 @@
 			if (strlen(pos->sm_name) != hostname_len
 			 || memcmp(pos->sm_name, hostname, hostname_len))
 				continue;
-		} else if (!nlm_cmp_addr(&pos->sm_addr, sin))
+		} else if (!nlm_cmp_addr(nsm_addr(pos), sap))
 			continue;
 		atomic_inc(&pos->sm_count);
 		kfree(nsm);
@@ -509,10 +677,13 @@
 	if (nsm == NULL)
 		return NULL;
 
-	nsm->sm_addr = *sin;
+	memcpy(nsm_addr(nsm), sap, salen);
+	nsm->sm_addrlen = salen;
 	nsm->sm_name = (char *) (nsm + 1);
 	memcpy(nsm->sm_name, hostname, hostname_len);
 	nsm->sm_name[hostname_len] = '\0';
+	nlm_display_address((struct sockaddr *)&nsm->sm_addr,
+				nsm->sm_addrbuf, sizeof(nsm->sm_addrbuf));
 	atomic_set(&nsm->sm_count, 1);
 	goto retry;
 
@@ -521,13 +692,6 @@
 	return nsm;
 }
 
-static struct nsm_handle *
-nsm_find(const struct sockaddr_in *sin, const char *hostname,
-	 unsigned int hostname_len)
-{
-	return __nsm_find(sin, hostname, hostname_len, 1);
-}
-
 /*
  * Release an NSM handle
  */
diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c
index e4d5635..4e7e958 100644
--- a/fs/lockd/mon.c
+++ b/fs/lockd/mon.c
@@ -51,7 +51,7 @@
 
 	memset(&args, 0, sizeof(args));
 	args.mon_name = nsm->sm_name;
-	args.addr = nsm->sm_addr.sin_addr.s_addr;
+	args.addr = nsm_addr_in(nsm)->sin_addr.s_addr;
 	args.prog = NLM_PROGRAM;
 	args.vers = 3;
 	args.proc = NLMPROC_NSM_NOTIFY;
diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c
index 5bd9bf0..c631a83 100644
--- a/fs/lockd/svc.c
+++ b/fs/lockd/svc.c
@@ -51,7 +51,6 @@
 static unsigned int		nlmsvc_users;
 static struct task_struct	*nlmsvc_task;
 static struct svc_rqst		*nlmsvc_rqst;
-int				nlmsvc_grace_period;
 unsigned long			nlmsvc_timeout;
 
 /*
@@ -85,27 +84,23 @@
 		return nlm_timeout * 5 * HZ;
 }
 
-unsigned long get_nfs_grace_period(void)
+static struct lock_manager lockd_manager = {
+};
+
+static void grace_ender(struct work_struct *not_used)
 {
-	unsigned long lockdgrace = get_lockd_grace_period();
-	unsigned long nfsdgrace = 0;
-
-	if (nlmsvc_ops)
-		nfsdgrace = nlmsvc_ops->get_grace_period();
-
-	return max(lockdgrace, nfsdgrace);
-}
-EXPORT_SYMBOL(get_nfs_grace_period);
-
-static unsigned long set_grace_period(void)
-{
-	nlmsvc_grace_period = 1;
-	return get_nfs_grace_period() + jiffies;
+	locks_end_grace(&lockd_manager);
 }
 
-static inline void clear_grace_period(void)
+static DECLARE_DELAYED_WORK(grace_period_end, grace_ender);
+
+static void set_grace_period(void)
 {
-	nlmsvc_grace_period = 0;
+	unsigned long grace_period = get_lockd_grace_period();
+
+	locks_start_grace(&lockd_manager);
+	cancel_delayed_work_sync(&grace_period_end);
+	schedule_delayed_work(&grace_period_end, grace_period);
 }
 
 /*
@@ -116,7 +111,6 @@
 {
 	int		err = 0, preverr = 0;
 	struct svc_rqst *rqstp = vrqstp;
-	unsigned long grace_period_expire;
 
 	/* try_to_freeze() is called from svc_recv() */
 	set_freezable();
@@ -139,7 +133,7 @@
 		nlm_timeout = LOCKD_DFLT_TIMEO;
 	nlmsvc_timeout = nlm_timeout * HZ;
 
-	grace_period_expire = set_grace_period();
+	set_grace_period();
 
 	/*
 	 * The main request loop. We don't terminate until the last
@@ -153,21 +147,12 @@
 			flush_signals(current);
 			if (nlmsvc_ops) {
 				nlmsvc_invalidate_all();
-				grace_period_expire = set_grace_period();
+				set_grace_period();
 			}
 			continue;
 		}
 
-		/*
-		 * Retry any blocked locks that have been notified by
-		 * the VFS. Don't do this during grace period.
-		 * (Theoretically, there shouldn't even be blocked locks
-		 * during grace period).
-		 */
-		if (!nlmsvc_grace_period) {
-			timeout = nlmsvc_retry_blocked();
-		} else if (time_before(grace_period_expire, jiffies))
-			clear_grace_period();
+		timeout = nlmsvc_retry_blocked();
 
 		/*
 		 * Find a socket with data available and call its
@@ -195,6 +180,7 @@
 		svc_process(rqstp);
 	}
 	flush_signals(current);
+	cancel_delayed_work_sync(&grace_period_end);
 	if (nlmsvc_ops)
 		nlmsvc_invalidate_all();
 	nlm_shutdown_hosts();
@@ -203,25 +189,28 @@
 }
 
 /*
- * Make any sockets that are needed but not present.
- * If nlm_udpport or nlm_tcpport were set as module
- * options, make those sockets unconditionally
+ * Ensure there are active UDP and TCP listeners for lockd.
+ *
+ * Even if we have only TCP NFS mounts and/or TCP NFSDs, some
+ * local services (such as rpc.statd) still require UDP, and
+ * some NFS servers do not yet support NLM over TCP.
+ *
+ * Returns zero if all listeners are available; otherwise a
+ * negative errno value is returned.
  */
-static int make_socks(struct svc_serv *serv, int proto)
+static int make_socks(struct svc_serv *serv)
 {
 	static int warned;
 	struct svc_xprt *xprt;
 	int err = 0;
 
-	if (proto == IPPROTO_UDP || nlm_udpport) {
-		xprt = svc_find_xprt(serv, "udp", 0, 0);
-		if (!xprt)
-			err = svc_create_xprt(serv, "udp", nlm_udpport,
-					      SVC_SOCK_DEFAULTS);
-		else
-			svc_xprt_put(xprt);
-	}
-	if (err >= 0 && (proto == IPPROTO_TCP || nlm_tcpport)) {
+	xprt = svc_find_xprt(serv, "udp", 0, 0);
+	if (!xprt)
+		err = svc_create_xprt(serv, "udp", nlm_udpport,
+				      SVC_SOCK_DEFAULTS);
+	else
+		svc_xprt_put(xprt);
+	if (err >= 0) {
 		xprt = svc_find_xprt(serv, "tcp", 0, 0);
 		if (!xprt)
 			err = svc_create_xprt(serv, "tcp", nlm_tcpport,
@@ -241,8 +230,7 @@
 /*
  * Bring up the lockd process if it's not already up.
  */
-int
-lockd_up(int proto) /* Maybe add a 'family' option when IPv6 is supported ?? */
+int lockd_up(void)
 {
 	struct svc_serv *serv;
 	int		error = 0;
@@ -251,11 +239,8 @@
 	/*
 	 * Check whether we're already up and running.
 	 */
-	if (nlmsvc_rqst) {
-		if (proto)
-			error = make_socks(nlmsvc_rqst->rq_server, proto);
+	if (nlmsvc_rqst)
 		goto out;
-	}
 
 	/*
 	 * Sanity check: if there's no pid,
@@ -266,13 +251,14 @@
 			"lockd_up: no pid, %d users??\n", nlmsvc_users);
 
 	error = -ENOMEM;
-	serv = svc_create(&nlmsvc_program, LOCKD_BUFSIZE, NULL);
+	serv = svc_create(&nlmsvc_program, LOCKD_BUFSIZE, AF_INET, NULL);
 	if (!serv) {
 		printk(KERN_WARNING "lockd_up: create service failed\n");
 		goto out;
 	}
 
-	if ((error = make_socks(serv, proto)) < 0)
+	error = make_socks(serv);
+	if (error < 0)
 		goto destroy_and_out;
 
 	/*
diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c
index 4a714f64..014f6ce 100644
--- a/fs/lockd/svc4proc.c
+++ b/fs/lockd/svc4proc.c
@@ -88,12 +88,6 @@
 	dprintk("lockd: TEST4        called\n");
 	resp->cookie = argp->cookie;
 
-	/* Don't accept test requests during grace period */
-	if (nlmsvc_grace_period) {
-		resp->status = nlm_lck_denied_grace_period;
-		return rc;
-	}
-
 	/* Obtain client and file */
 	if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file)))
 		return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;
@@ -122,12 +116,6 @@
 
 	resp->cookie = argp->cookie;
 
-	/* Don't accept new lock requests during grace period */
-	if (nlmsvc_grace_period && !argp->reclaim) {
-		resp->status = nlm_lck_denied_grace_period;
-		return rc;
-	}
-
 	/* Obtain client and file */
 	if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file)))
 		return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;
@@ -146,7 +134,8 @@
 
 	/* Now try to lock the file */
 	resp->status = nlmsvc_lock(rqstp, file, host, &argp->lock,
-					argp->block, &argp->cookie);
+					argp->block, &argp->cookie,
+					argp->reclaim);
 	if (resp->status == nlm_drop_reply)
 		rc = rpc_drop_reply;
 	else
@@ -169,7 +158,7 @@
 	resp->cookie = argp->cookie;
 
 	/* Don't accept requests during grace period */
-	if (nlmsvc_grace_period) {
+	if (locks_in_grace()) {
 		resp->status = nlm_lck_denied_grace_period;
 		return rpc_success;
 	}
@@ -202,7 +191,7 @@
 	resp->cookie = argp->cookie;
 
 	/* Don't accept new lock requests during grace period */
-	if (nlmsvc_grace_period) {
+	if (locks_in_grace()) {
 		resp->status = nlm_lck_denied_grace_period;
 		return rpc_success;
 	}
@@ -231,7 +220,7 @@
 	resp->cookie = argp->cookie;
 
 	dprintk("lockd: GRANTED       called\n");
-	resp->status = nlmclnt_grant(svc_addr_in(rqstp), &argp->lock);
+	resp->status = nlmclnt_grant(svc_addr(rqstp), &argp->lock);
 	dprintk("lockd: GRANTED       status %d\n", ntohl(resp->status));
 	return rpc_success;
 }
@@ -341,7 +330,7 @@
 	resp->cookie = argp->cookie;
 
 	/* Don't accept new lock requests during grace period */
-	if (nlmsvc_grace_period && !argp->reclaim) {
+	if (locks_in_grace() && !argp->reclaim) {
 		resp->status = nlm_lck_denied_grace_period;
 		return rpc_success;
 	}
@@ -374,7 +363,7 @@
 	resp->cookie = argp->cookie;
 
 	/* Don't accept requests during grace period */
-	if (nlmsvc_grace_period) {
+	if (locks_in_grace()) {
 		resp->status = nlm_lck_denied_grace_period;
 		return rpc_success;
 	}
@@ -432,11 +421,9 @@
 {
 	struct sockaddr_in	saddr;
 
-	memcpy(&saddr, svc_addr_in(rqstp), sizeof(saddr));
-
 	dprintk("lockd: SM_NOTIFY     called\n");
-	if (saddr.sin_addr.s_addr != htonl(INADDR_LOOPBACK)
-	 || ntohs(saddr.sin_port) >= 1024) {
+
+	if (!nlm_privileged_requester(rqstp)) {
 		char buf[RPC_MAX_ADDRBUFLEN];
 		printk(KERN_WARNING "lockd: rejected NSM callback from %s\n",
 				svc_print_addr(rqstp, buf, sizeof(buf)));
diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c
index cf0d5c2c..6063a8e 100644
--- a/fs/lockd/svclock.c
+++ b/fs/lockd/svclock.c
@@ -360,7 +360,7 @@
 __be32
 nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file,
 	    struct nlm_host *host, struct nlm_lock *lock, int wait,
-	    struct nlm_cookie *cookie)
+	    struct nlm_cookie *cookie, int reclaim)
 {
 	struct nlm_block	*block = NULL;
 	int			error;
@@ -406,6 +406,15 @@
 		goto out;
 	}
 
+	if (locks_in_grace() && !reclaim) {
+		ret = nlm_lck_denied_grace_period;
+		goto out;
+	}
+	if (reclaim && !locks_in_grace()) {
+		ret = nlm_lck_denied_grace_period;
+		goto out;
+	}
+
 	if (!wait)
 		lock->fl.fl_flags &= ~FL_SLEEP;
 	error = vfs_lock_file(file->f_file, F_SETLK, &lock->fl, NULL);
@@ -502,6 +511,10 @@
 		goto out;
 	}
 
+	if (locks_in_grace()) {
+		ret = nlm_lck_denied_grace_period;
+		goto out;
+	}
 	error = vfs_test_lock(file->f_file, &lock->fl);
 	if (error == FILE_LOCK_DEFERRED) {
 		ret = nlmsvc_defer_lock_rqst(rqstp, block);
@@ -582,6 +595,9 @@
 				(long long)lock->fl.fl_start,
 				(long long)lock->fl.fl_end);
 
+	if (locks_in_grace())
+		return nlm_lck_denied_grace_period;
+
 	mutex_lock(&file->f_mutex);
 	block = nlmsvc_lookup_block(file, lock);
 	mutex_unlock(&file->f_mutex);
diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c
index 76262c1..548b0bb 100644
--- a/fs/lockd/svcproc.c
+++ b/fs/lockd/svcproc.c
@@ -117,12 +117,6 @@
 	dprintk("lockd: TEST          called\n");
 	resp->cookie = argp->cookie;
 
-	/* Don't accept test requests during grace period */
-	if (nlmsvc_grace_period) {
-		resp->status = nlm_lck_denied_grace_period;
-		return rc;
-	}
-
 	/* Obtain client and file */
 	if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file)))
 		return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;
@@ -152,12 +146,6 @@
 
 	resp->cookie = argp->cookie;
 
-	/* Don't accept new lock requests during grace period */
-	if (nlmsvc_grace_period && !argp->reclaim) {
-		resp->status = nlm_lck_denied_grace_period;
-		return rc;
-	}
-
 	/* Obtain client and file */
 	if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file)))
 		return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;
@@ -176,7 +164,8 @@
 
 	/* Now try to lock the file */
 	resp->status = cast_status(nlmsvc_lock(rqstp, file, host, &argp->lock,
-					       argp->block, &argp->cookie));
+					       argp->block, &argp->cookie,
+					       argp->reclaim));
 	if (resp->status == nlm_drop_reply)
 		rc = rpc_drop_reply;
 	else
@@ -199,7 +188,7 @@
 	resp->cookie = argp->cookie;
 
 	/* Don't accept requests during grace period */
-	if (nlmsvc_grace_period) {
+	if (locks_in_grace()) {
 		resp->status = nlm_lck_denied_grace_period;
 		return rpc_success;
 	}
@@ -232,7 +221,7 @@
 	resp->cookie = argp->cookie;
 
 	/* Don't accept new lock requests during grace period */
-	if (nlmsvc_grace_period) {
+	if (locks_in_grace()) {
 		resp->status = nlm_lck_denied_grace_period;
 		return rpc_success;
 	}
@@ -261,7 +250,7 @@
 	resp->cookie = argp->cookie;
 
 	dprintk("lockd: GRANTED       called\n");
-	resp->status = nlmclnt_grant(svc_addr_in(rqstp), &argp->lock);
+	resp->status = nlmclnt_grant(svc_addr(rqstp), &argp->lock);
 	dprintk("lockd: GRANTED       status %d\n", ntohl(resp->status));
 	return rpc_success;
 }
@@ -373,7 +362,7 @@
 	resp->cookie = argp->cookie;
 
 	/* Don't accept new lock requests during grace period */
-	if (nlmsvc_grace_period && !argp->reclaim) {
+	if (locks_in_grace() && !argp->reclaim) {
 		resp->status = nlm_lck_denied_grace_period;
 		return rpc_success;
 	}
@@ -406,7 +395,7 @@
 	resp->cookie = argp->cookie;
 
 	/* Don't accept requests during grace period */
-	if (nlmsvc_grace_period) {
+	if (locks_in_grace()) {
 		resp->status = nlm_lck_denied_grace_period;
 		return rpc_success;
 	}
@@ -464,11 +453,9 @@
 {
 	struct sockaddr_in	saddr;
 
-	memcpy(&saddr, svc_addr_in(rqstp), sizeof(saddr));
-
 	dprintk("lockd: SM_NOTIFY     called\n");
-	if (saddr.sin_addr.s_addr != htonl(INADDR_LOOPBACK)
-	 || ntohs(saddr.sin_port) >= 1024) {
+
+	if (!nlm_privileged_requester(rqstp)) {
 		char buf[RPC_MAX_ADDRBUFLEN];
 		printk(KERN_WARNING "lockd: rejected NSM callback from %s\n",
 				svc_print_addr(rqstp, buf, sizeof(buf)));
diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c
index 198b4e5..34c2766e 100644
--- a/fs/lockd/svcsubs.c
+++ b/fs/lockd/svcsubs.c
@@ -418,7 +418,7 @@
 static int
 nlmsvc_match_ip(void *datap, struct nlm_host *host)
 {
-	return nlm_cmp_addr(&host->h_saddr, datap);
+	return nlm_cmp_addr(nlm_srcaddr(host), datap);
 }
 
 /**
diff --git a/fs/lockd/xdr.c b/fs/lockd/xdr.c
index 3e459e1..1f22629 100644
--- a/fs/lockd/xdr.c
+++ b/fs/lockd/xdr.c
@@ -351,8 +351,6 @@
 	argp->state = ntohl(*p++);
 	/* Preserve the address in network byte order */
 	argp->addr = *p++;
-	argp->vers = *p++;
-	argp->proto = *p++;
 	return xdr_argsize_check(rqstp, p);
 }
 
diff --git a/fs/lockd/xdr4.c b/fs/lockd/xdr4.c
index 43ff939..50c493a 100644
--- a/fs/lockd/xdr4.c
+++ b/fs/lockd/xdr4.c
@@ -358,8 +358,6 @@
 	argp->state = ntohl(*p++);
 	/* Preserve the address in network byte order */
 	argp->addr  = *p++;
-	argp->vers  = *p++;
-	argp->proto = *p++;
 	return xdr_argsize_check(rqstp, p);
 }
 
diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c
index f447f4b..6a09760 100644
--- a/fs/nfs/callback.c
+++ b/fs/nfs/callback.c
@@ -105,7 +105,8 @@
 	mutex_lock(&nfs_callback_mutex);
 	if (nfs_callback_info.users++ || nfs_callback_info.task != NULL)
 		goto out;
-	serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, NULL);
+	serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE,
+				AF_INET, NULL);
 	ret = -ENOMEM;
 	if (!serv)
 		goto out_err;
diff --git a/fs/nfsd/lockd.c b/fs/nfsd/lockd.c
index 15c6fae..b2786a5 100644
--- a/fs/nfsd/lockd.c
+++ b/fs/nfsd/lockd.c
@@ -70,7 +70,6 @@
 static struct nlmsvc_binding	nfsd_nlm_ops = {
 	.fopen		= nlm_fopen,		/* open file for locking */
 	.fclose		= nlm_fclose,		/* close file */
-	.get_grace_period = get_nfs4_grace_period,
 };
 
 void
diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c
index 4d617ea..9dbd2eb 100644
--- a/fs/nfsd/nfs3proc.c
+++ b/fs/nfsd/nfs3proc.c
@@ -63,7 +63,8 @@
 		SVCFH_fmt(&argp->fh));
 
 	fh_copy(&resp->fh, &argp->fh);
-	nfserr = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_NOP);
+	nfserr = fh_verify(rqstp, &resp->fh, 0,
+			NFSD_MAY_NOP | NFSD_MAY_BYPASS_GSS_ON_ROOT);
 	if (nfserr)
 		RETURN_STATUS(nfserr);
 
@@ -530,7 +531,7 @@
 	dprintk("nfsd: FSSTAT(3)   %s\n",
 				SVCFH_fmt(&argp->fh));
 
-	nfserr = nfsd_statfs(rqstp, &argp->fh, &resp->stats);
+	nfserr = nfsd_statfs(rqstp, &argp->fh, &resp->stats, 0);
 	fh_put(&argp->fh);
 	RETURN_STATUS(nfserr);
 }
@@ -558,7 +559,8 @@
 	resp->f_maxfilesize = ~(u32) 0;
 	resp->f_properties = NFS3_FSF_DEFAULT;
 
-	nfserr = fh_verify(rqstp, &argp->fh, 0, NFSD_MAY_NOP);
+	nfserr = fh_verify(rqstp, &argp->fh, 0,
+			NFSD_MAY_NOP | NFSD_MAY_BYPASS_GSS_ON_ROOT);
 
 	/* Check special features of the file system. May request
 	 * different read/write sizes for file systems known to have
diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
index 702fa57..094747a 100644
--- a/fs/nfsd/nfs4callback.c
+++ b/fs/nfsd/nfs4callback.c
@@ -225,7 +225,8 @@
 
 	RESERVE_SPACE(12+sizeof(cb_rec->cbr_stateid) + len);
 	WRITE32(OP_CB_RECALL);
-	WRITEMEM(&cb_rec->cbr_stateid, sizeof(stateid_t));
+	WRITE32(cb_rec->cbr_stateid.si_generation);
+	WRITEMEM(&cb_rec->cbr_stateid.si_opaque, sizeof(stateid_opaque_t));
 	WRITE32(cb_rec->cbr_trunc);
 	WRITE32(len);
 	WRITEMEM(cb_rec->cbr_fhval, len);
@@ -379,6 +380,7 @@
 		.addrsize	= sizeof(addr),
 		.timeout	= &timeparms,
 		.program	= &cb_program,
+		.prognumber	= cb->cb_prog,
 		.version	= nfs_cb_version[1]->number,
 		.authflavor	= RPC_AUTH_UNIX, /* XXX: need AUTH_GSS... */
 		.flags		= (RPC_CLNT_CREATE_NOPING | RPC_CLNT_CREATE_QUIET),
@@ -396,9 +398,6 @@
 	addr.sin_port = htons(cb->cb_port);
 	addr.sin_addr.s_addr = htonl(cb->cb_addr);
 
-	/* Initialize rpc_stat */
-	memset(args.program->stats, 0, sizeof(struct rpc_stat));
-
 	/* Create RPC client */
 	client = rpc_create(&args);
 	if (IS_ERR(client)) {
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index e5b51ff..669461e 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -201,10 +201,10 @@
 	/* Openowner is now set, so sequence id will get bumped.  Now we need
 	 * these checks before we do any creates: */
 	status = nfserr_grace;
-	if (nfs4_in_grace() && open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS)
+	if (locks_in_grace() && open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS)
 		goto out;
 	status = nfserr_no_grace;
-	if (!nfs4_in_grace() && open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS)
+	if (!locks_in_grace() && open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS)
 		goto out;
 
 	switch (open->op_claim_type) {
@@ -575,7 +575,7 @@
 {
 	__be32 status;
 
-	if (nfs4_in_grace())
+	if (locks_in_grace())
 		return nfserr_grace;
 	status = nfsd_unlink(rqstp, &cstate->current_fh, 0,
 			     remove->rm_name, remove->rm_namelen);
@@ -596,7 +596,7 @@
 
 	if (!cstate->save_fh.fh_dentry)
 		return status;
-	if (nfs4_in_grace() && !(cstate->save_fh.fh_export->ex_flags
+	if (locks_in_grace() && !(cstate->save_fh.fh_export->ex_flags
 					& NFSEXP_NOSUBTREECHECK))
 		return nfserr_grace;
 	status = nfsd_rename(rqstp, &cstate->save_fh, rename->rn_sname,
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 1578d7a..0cc7ff5 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -61,7 +61,6 @@
 static time_t lease_time = 90;     /* default lease time */
 static time_t user_lease_time = 90;
 static time_t boot_time;
-static int in_grace = 1;
 static u32 current_ownerid = 1;
 static u32 current_fileid = 1;
 static u32 current_delegid = 1;
@@ -1640,7 +1639,7 @@
 		case NFS4_OPEN_CLAIM_NULL:
 			/* Let's not give out any delegations till everyone's
 			 * had the chance to reclaim theirs.... */
-			if (nfs4_in_grace())
+			if (locks_in_grace())
 				goto out;
 			if (!atomic_read(&cb->cb_set) || !sop->so_confirmed)
 				goto out;
@@ -1816,12 +1815,15 @@
 	return status;
 }
 
+struct lock_manager nfsd4_manager = {
+};
+
 static void
-end_grace(void)
+nfsd4_end_grace(void)
 {
 	dprintk("NFSD: end of grace period\n");
 	nfsd4_recdir_purge_old();
-	in_grace = 0;
+	locks_end_grace(&nfsd4_manager);
 }
 
 static time_t
@@ -1838,8 +1840,8 @@
 	nfs4_lock_state();
 
 	dprintk("NFSD: laundromat service - starting\n");
-	if (in_grace)
-		end_grace();
+	if (locks_in_grace())
+		nfsd4_end_grace();
 	list_for_each_safe(pos, next, &client_lru) {
 		clp = list_entry(pos, struct nfs4_client, cl_lru);
 		if (time_after((unsigned long)clp->cl_time, (unsigned long)cutoff)) {
@@ -1974,7 +1976,7 @@
 		return nfserr_bad_stateid;
 	else if (ONE_STATEID(stateid) && (flags & RD_STATE))
 		return nfs_ok;
-	else if (nfs4_in_grace()) {
+	else if (locks_in_grace()) {
 		/* Answer in remaining cases depends on existance of
 		 * conflicting state; so we must wait out the grace period. */
 		return nfserr_grace;
@@ -1993,7 +1995,7 @@
 static inline int
 io_during_grace_disallowed(struct inode *inode, int flags)
 {
-	return nfs4_in_grace() && (flags & (RD_STATE | WR_STATE))
+	return locks_in_grace() && (flags & (RD_STATE | WR_STATE))
 		&& mandatory_lock(inode);
 }
 
@@ -2693,10 +2695,10 @@
 	filp = lock_stp->st_vfs_file;
 
 	status = nfserr_grace;
-	if (nfs4_in_grace() && !lock->lk_reclaim)
+	if (locks_in_grace() && !lock->lk_reclaim)
 		goto out;
 	status = nfserr_no_grace;
-	if (!nfs4_in_grace() && lock->lk_reclaim)
+	if (!locks_in_grace() && lock->lk_reclaim)
 		goto out;
 
 	locks_init_lock(&file_lock);
@@ -2779,7 +2781,7 @@
 	int error;
 	__be32 status;
 
-	if (nfs4_in_grace())
+	if (locks_in_grace())
 		return nfserr_grace;
 
 	if (check_lock_length(lockt->lt_offset, lockt->lt_length))
@@ -3192,9 +3194,9 @@
 	unsigned long grace_time;
 
 	boot_time = get_seconds();
-	grace_time = get_nfs_grace_period();
+	grace_time = get_nfs4_grace_period();
 	lease_time = user_lease_time;
-	in_grace = 1;
+	locks_start_grace(&nfsd4_manager);
 	printk(KERN_INFO "NFSD: starting %ld-second grace period\n",
 	       grace_time/HZ);
 	laundry_wq = create_singlethread_workqueue("nfsd4");
@@ -3213,12 +3215,6 @@
 	return;
 }
 
-int
-nfs4_in_grace(void)
-{
-	return in_grace;
-}
-
 time_t
 nfs4_lease_time(void)
 {
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 14ba4d9..afcdf4b 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -413,6 +413,18 @@
 }
 
 static __be32
+nfsd4_decode_stateid(struct nfsd4_compoundargs *argp, stateid_t *sid)
+{
+	DECODE_HEAD;
+
+	READ_BUF(sizeof(stateid_t));
+	READ32(sid->si_generation);
+	COPYMEM(&sid->si_opaque, sizeof(stateid_opaque_t));
+
+	DECODE_TAIL;
+}
+
+static __be32
 nfsd4_decode_access(struct nfsd4_compoundargs *argp, struct nfsd4_access *access)
 {
 	DECODE_HEAD;
@@ -429,10 +441,9 @@
 	DECODE_HEAD;
 
 	close->cl_stateowner = NULL;
-	READ_BUF(4 + sizeof(stateid_t));
+	READ_BUF(4);
 	READ32(close->cl_seqid);
-	READ32(close->cl_stateid.si_generation);
-	COPYMEM(&close->cl_stateid.si_opaque, sizeof(stateid_opaque_t));
+	return nfsd4_decode_stateid(argp, &close->cl_stateid);
 
 	DECODE_TAIL;
 }
@@ -493,13 +504,7 @@
 static inline __be32
 nfsd4_decode_delegreturn(struct nfsd4_compoundargs *argp, struct nfsd4_delegreturn *dr)
 {
-	DECODE_HEAD;
-
-	READ_BUF(sizeof(stateid_t));
-	READ32(dr->dr_stateid.si_generation);
-	COPYMEM(&dr->dr_stateid.si_opaque, sizeof(stateid_opaque_t));
-
-	DECODE_TAIL;
+	return nfsd4_decode_stateid(argp, &dr->dr_stateid);
 }
 
 static inline __be32
@@ -542,20 +547,22 @@
 	READ32(lock->lk_is_new);
 
 	if (lock->lk_is_new) {
-		READ_BUF(36);
+		READ_BUF(4);
 		READ32(lock->lk_new_open_seqid);
-		READ32(lock->lk_new_open_stateid.si_generation);
-
-		COPYMEM(&lock->lk_new_open_stateid.si_opaque, sizeof(stateid_opaque_t));
+		status = nfsd4_decode_stateid(argp, &lock->lk_new_open_stateid);
+		if (status)
+			return status;
+		READ_BUF(8 + sizeof(clientid_t));
 		READ32(lock->lk_new_lock_seqid);
 		COPYMEM(&lock->lk_new_clientid, sizeof(clientid_t));
 		READ32(lock->lk_new_owner.len);
 		READ_BUF(lock->lk_new_owner.len);
 		READMEM(lock->lk_new_owner.data, lock->lk_new_owner.len);
 	} else {
-		READ_BUF(20);
-		READ32(lock->lk_old_lock_stateid.si_generation);
-		COPYMEM(&lock->lk_old_lock_stateid.si_opaque, sizeof(stateid_opaque_t));
+		status = nfsd4_decode_stateid(argp, &lock->lk_old_lock_stateid);
+		if (status)
+			return status;
+		READ_BUF(4);
 		READ32(lock->lk_old_lock_seqid);
 	}
 
@@ -587,13 +594,15 @@
 	DECODE_HEAD;
 
 	locku->lu_stateowner = NULL;
-	READ_BUF(24 + sizeof(stateid_t));
+	READ_BUF(8);
 	READ32(locku->lu_type);
 	if ((locku->lu_type < NFS4_READ_LT) || (locku->lu_type > NFS4_WRITEW_LT))
 		goto xdr_error;
 	READ32(locku->lu_seqid);
-	READ32(locku->lu_stateid.si_generation);
-	COPYMEM(&locku->lu_stateid.si_opaque, sizeof(stateid_opaque_t));
+	status = nfsd4_decode_stateid(argp, &locku->lu_stateid);
+	if (status)
+		return status;
+	READ_BUF(16);
 	READ64(locku->lu_offset);
 	READ64(locku->lu_length);
 
@@ -678,8 +687,10 @@
 		READ32(open->op_delegate_type);
 		break;
 	case NFS4_OPEN_CLAIM_DELEGATE_CUR:
-		READ_BUF(sizeof(stateid_t) + 4);
-		COPYMEM(&open->op_delegate_stateid, sizeof(stateid_t));
+		status = nfsd4_decode_stateid(argp, &open->op_delegate_stateid);
+		if (status)
+			return status;
+		READ_BUF(4);
 		READ32(open->op_fname.len);
 		READ_BUF(open->op_fname.len);
 		SAVEMEM(open->op_fname.data, open->op_fname.len);
@@ -699,9 +710,10 @@
 	DECODE_HEAD;
 		    
 	open_conf->oc_stateowner = NULL;
-	READ_BUF(4 + sizeof(stateid_t));
-	READ32(open_conf->oc_req_stateid.si_generation);
-	COPYMEM(&open_conf->oc_req_stateid.si_opaque, sizeof(stateid_opaque_t));
+	status = nfsd4_decode_stateid(argp, &open_conf->oc_req_stateid);
+	if (status)
+		return status;
+	READ_BUF(4);
 	READ32(open_conf->oc_seqid);
 						        
 	DECODE_TAIL;
@@ -713,9 +725,10 @@
 	DECODE_HEAD;
 		    
 	open_down->od_stateowner = NULL;
-	READ_BUF(12 + sizeof(stateid_t));
-	READ32(open_down->od_stateid.si_generation);
-	COPYMEM(&open_down->od_stateid.si_opaque, sizeof(stateid_opaque_t));
+	status = nfsd4_decode_stateid(argp, &open_down->od_stateid);
+	if (status)
+		return status;
+	READ_BUF(12);
 	READ32(open_down->od_seqid);
 	READ32(open_down->od_share_access);
 	READ32(open_down->od_share_deny);
@@ -743,9 +756,10 @@
 {
 	DECODE_HEAD;
 
-	READ_BUF(sizeof(stateid_t) + 12);
-	READ32(read->rd_stateid.si_generation);
-	COPYMEM(&read->rd_stateid.si_opaque, sizeof(stateid_opaque_t));
+	status = nfsd4_decode_stateid(argp, &read->rd_stateid);
+	if (status)
+		return status;
+	READ_BUF(12);
 	READ64(read->rd_offset);
 	READ32(read->rd_length);
 
@@ -834,15 +848,13 @@
 static __be32
 nfsd4_decode_setattr(struct nfsd4_compoundargs *argp, struct nfsd4_setattr *setattr)
 {
-	DECODE_HEAD;
+	__be32 status;
 
-	READ_BUF(sizeof(stateid_t));
-	READ32(setattr->sa_stateid.si_generation);
-	COPYMEM(&setattr->sa_stateid.si_opaque, sizeof(stateid_opaque_t));
-	if ((status = nfsd4_decode_fattr(argp, setattr->sa_bmval, &setattr->sa_iattr, &setattr->sa_acl)))
-		goto out;
-
-	DECODE_TAIL;
+	status = nfsd4_decode_stateid(argp, &setattr->sa_stateid);
+	if (status)
+		return status;
+	return nfsd4_decode_fattr(argp, setattr->sa_bmval,
+				  &setattr->sa_iattr, &setattr->sa_acl);
 }
 
 static __be32
@@ -927,9 +939,10 @@
 	int len;
 	DECODE_HEAD;
 
-	READ_BUF(sizeof(stateid_opaque_t) + 20);
-	READ32(write->wr_stateid.si_generation);
-	COPYMEM(&write->wr_stateid.si_opaque, sizeof(stateid_opaque_t));
+	status = nfsd4_decode_stateid(argp, &write->wr_stateid);
+	if (status)
+		return status;
+	READ_BUF(16);
 	READ64(write->wr_offset);
 	READ32(write->wr_stable_how);
 	if (write->wr_stable_how > 2)
@@ -1183,7 +1196,6 @@
  * Header routine to setup seqid operation replay cache
  */
 #define ENCODE_SEQID_OP_HEAD					\
-	__be32 *p;						\
 	__be32 *save;						\
 								\
 	save = resp->p;
@@ -1950,6 +1962,17 @@
 	return -EINVAL;
 }
 
+static void
+nfsd4_encode_stateid(struct nfsd4_compoundres *resp, stateid_t *sid)
+{
+	ENCODE_HEAD;
+
+	RESERVE_SPACE(sizeof(stateid_t));
+	WRITE32(sid->si_generation);
+	WRITEMEM(&sid->si_opaque, sizeof(stateid_opaque_t));
+	ADJUST_ARGS();
+}
+
 static __be32
 nfsd4_encode_access(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_access *access)
 {
@@ -1969,12 +1992,9 @@
 {
 	ENCODE_SEQID_OP_HEAD;
 
-	if (!nfserr) {
-		RESERVE_SPACE(sizeof(stateid_t));
-		WRITE32(close->cl_stateid.si_generation);
-		WRITEMEM(&close->cl_stateid.si_opaque, sizeof(stateid_opaque_t));
-		ADJUST_ARGS();
-	}
+	if (!nfserr)
+		nfsd4_encode_stateid(resp, &close->cl_stateid);
+
 	ENCODE_SEQID_OP_TAIL(close->cl_stateowner);
 	return nfserr;
 }
@@ -2074,12 +2094,9 @@
 {
 	ENCODE_SEQID_OP_HEAD;
 
-	if (!nfserr) {
-		RESERVE_SPACE(4 + sizeof(stateid_t));
-		WRITE32(lock->lk_resp_stateid.si_generation);
-		WRITEMEM(&lock->lk_resp_stateid.si_opaque, sizeof(stateid_opaque_t));
-		ADJUST_ARGS();
-	} else if (nfserr == nfserr_denied)
+	if (!nfserr)
+		nfsd4_encode_stateid(resp, &lock->lk_resp_stateid);
+	else if (nfserr == nfserr_denied)
 		nfsd4_encode_lock_denied(resp, &lock->lk_denied);
 
 	ENCODE_SEQID_OP_TAIL(lock->lk_replay_owner);
@@ -2099,13 +2116,9 @@
 {
 	ENCODE_SEQID_OP_HEAD;
 
-	if (!nfserr) {
-		RESERVE_SPACE(sizeof(stateid_t));
-		WRITE32(locku->lu_stateid.si_generation);
-		WRITEMEM(&locku->lu_stateid.si_opaque, sizeof(stateid_opaque_t));
-		ADJUST_ARGS();
-	}
-				        
+	if (!nfserr)
+		nfsd4_encode_stateid(resp, &locku->lu_stateid);
+
 	ENCODE_SEQID_OP_TAIL(locku->lu_stateowner);
 	return nfserr;
 }
@@ -2128,14 +2141,14 @@
 static __be32
 nfsd4_encode_open(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_open *open)
 {
+	ENCODE_HEAD;
 	ENCODE_SEQID_OP_HEAD;
 
 	if (nfserr)
 		goto out;
 
-	RESERVE_SPACE(36 + sizeof(stateid_t));
-	WRITE32(open->op_stateid.si_generation);
-	WRITEMEM(&open->op_stateid.si_opaque, sizeof(stateid_opaque_t));
+	nfsd4_encode_stateid(resp, &open->op_stateid);
+	RESERVE_SPACE(40);
 	WRITECINFO(open->op_cinfo);
 	WRITE32(open->op_rflags);
 	WRITE32(2);
@@ -2148,8 +2161,8 @@
 	case NFS4_OPEN_DELEGATE_NONE:
 		break;
 	case NFS4_OPEN_DELEGATE_READ:
-		RESERVE_SPACE(20 + sizeof(stateid_t));
-		WRITEMEM(&open->op_delegate_stateid, sizeof(stateid_t));
+		nfsd4_encode_stateid(resp, &open->op_delegate_stateid);
+		RESERVE_SPACE(20);
 		WRITE32(open->op_recall);
 
 		/*
@@ -2162,8 +2175,8 @@
 		ADJUST_ARGS();
 		break;
 	case NFS4_OPEN_DELEGATE_WRITE:
-		RESERVE_SPACE(32 + sizeof(stateid_t));
-		WRITEMEM(&open->op_delegate_stateid, sizeof(stateid_t));
+		nfsd4_encode_stateid(resp, &open->op_delegate_stateid);
+		RESERVE_SPACE(32);
 		WRITE32(0);
 
 		/*
@@ -2195,13 +2208,9 @@
 nfsd4_encode_open_confirm(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_open_confirm *oc)
 {
 	ENCODE_SEQID_OP_HEAD;
-				        
-	if (!nfserr) {
-		RESERVE_SPACE(sizeof(stateid_t));
-		WRITE32(oc->oc_resp_stateid.si_generation);
-		WRITEMEM(&oc->oc_resp_stateid.si_opaque, sizeof(stateid_opaque_t));
-		ADJUST_ARGS();
-	}
+
+	if (!nfserr)
+		nfsd4_encode_stateid(resp, &oc->oc_resp_stateid);
 
 	ENCODE_SEQID_OP_TAIL(oc->oc_stateowner);
 	return nfserr;
@@ -2211,13 +2220,9 @@
 nfsd4_encode_open_downgrade(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_open_downgrade *od)
 {
 	ENCODE_SEQID_OP_HEAD;
-				        
-	if (!nfserr) {
-		RESERVE_SPACE(sizeof(stateid_t));
-		WRITE32(od->od_stateid.si_generation);
-		WRITEMEM(&od->od_stateid.si_opaque, sizeof(stateid_opaque_t));
-		ADJUST_ARGS();
-	}
+
+	if (!nfserr)
+		nfsd4_encode_stateid(resp, &od->od_stateid);
 
 	ENCODE_SEQID_OP_TAIL(od->od_stateowner);
 	return nfserr;
diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
index c53e65f..97543df 100644
--- a/fs/nfsd/nfsctl.c
+++ b/fs/nfsd/nfsctl.c
@@ -614,10 +614,9 @@
 			return -EINVAL;
 		err = nfsd_create_serv();
 		if (!err) {
-			int proto = 0;
-			err = svc_addsock(nfsd_serv, fd, buf, &proto);
+			err = svc_addsock(nfsd_serv, fd, buf);
 			if (err >= 0) {
-				err = lockd_up(proto);
+				err = lockd_up();
 				if (err < 0)
 					svc_sock_names(buf+strlen(buf)+1, nfsd_serv, buf);
 			}
diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c
index ea37c96..cd25d91 100644
--- a/fs/nfsd/nfsfh.c
+++ b/fs/nfsd/nfsfh.c
@@ -302,17 +302,27 @@
 	if (error)
 		goto out;
 
-	if (!(access & NFSD_MAY_LOCK)) {
-		/*
-		 * pseudoflavor restrictions are not enforced on NLM,
-		 * which clients virtually always use auth_sys for,
-		 * even while using RPCSEC_GSS for NFS.
-		 */
-		error = check_nfsd_access(exp, rqstp);
-		if (error)
-			goto out;
-	}
+	/*
+	 * pseudoflavor restrictions are not enforced on NLM,
+	 * which clients virtually always use auth_sys for,
+	 * even while using RPCSEC_GSS for NFS.
+	 */
+	if (access & NFSD_MAY_LOCK)
+		goto skip_pseudoflavor_check;
+	/*
+	 * Clients may expect to be able to use auth_sys during mount,
+	 * even if they use gss for everything else; see section 2.3.2
+	 * of rfc 2623.
+	 */
+	if (access & NFSD_MAY_BYPASS_GSS_ON_ROOT
+			&& exp->ex_path.dentry == dentry)
+		goto skip_pseudoflavor_check;
 
+	error = check_nfsd_access(exp, rqstp);
+	if (error)
+		goto out;
+
+skip_pseudoflavor_check:
 	/* Finally, check access permissions. */
 	error = nfsd_permission(rqstp, exp, dentry, access);
 
diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c
index 0766f95..5cffeca 100644
--- a/fs/nfsd/nfsproc.c
+++ b/fs/nfsd/nfsproc.c
@@ -65,7 +65,8 @@
 	dprintk("nfsd: GETATTR  %s\n", SVCFH_fmt(&argp->fh));
 
 	fh_copy(&resp->fh, &argp->fh);
-	nfserr = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_NOP);
+	nfserr = fh_verify(rqstp, &resp->fh, 0,
+			NFSD_MAY_NOP | NFSD_MAY_BYPASS_GSS_ON_ROOT);
 	return nfsd_return_attrs(nfserr, resp);
 }
 
@@ -521,7 +522,8 @@
 
 	dprintk("nfsd: STATFS   %s\n", SVCFH_fmt(&argp->fh));
 
-	nfserr = nfsd_statfs(rqstp, &argp->fh, &resp->stats);
+	nfserr = nfsd_statfs(rqstp, &argp->fh, &resp->stats,
+			NFSD_MAY_BYPASS_GSS_ON_ROOT);
 	fh_put(&argp->fh);
 	return nfserr;
 }
diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c
index 80292ff..59eeb46 100644
--- a/fs/nfsd/nfssvc.c
+++ b/fs/nfsd/nfssvc.c
@@ -229,6 +229,7 @@
 
 	atomic_set(&nfsd_busy, 0);
 	nfsd_serv = svc_create_pooled(&nfsd_program, nfsd_max_blksize,
+				      AF_INET,
 				      nfsd_last_thread, nfsd, THIS_MODULE);
 	if (nfsd_serv == NULL)
 		err = -ENOMEM;
@@ -243,25 +244,20 @@
 	if (!list_empty(&nfsd_serv->sv_permsocks))
 		return 0;
 
-	error = lockd_up(IPPROTO_UDP);
-	if (error >= 0) {
-		error = svc_create_xprt(nfsd_serv, "udp", port,
+	error = svc_create_xprt(nfsd_serv, "udp", port,
 					SVC_SOCK_DEFAULTS);
-		if (error < 0)
-			lockd_down();
-	}
 	if (error < 0)
 		return error;
 
-	error = lockd_up(IPPROTO_TCP);
-	if (error >= 0) {
-		error = svc_create_xprt(nfsd_serv, "tcp", port,
+	error = svc_create_xprt(nfsd_serv, "tcp", port,
 					SVC_SOCK_DEFAULTS);
-		if (error < 0)
-			lockd_down();
-	}
 	if (error < 0)
 		return error;
+
+	error = lockd_up();
+	if (error < 0)
+		return error;
+
 	return 0;
 }
 
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 18060be..aa1d0d6 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -83,7 +83,6 @@
 	spinlock_t		pb_lock;
 } ____cacheline_aligned_in_smp;
 
-static struct raparms *		raparml;
 #define RAPARM_HASH_BITS	4
 #define RAPARM_HASH_SIZE	(1<<RAPARM_HASH_BITS)
 #define RAPARM_HASH_MASK	(RAPARM_HASH_SIZE-1)
@@ -1866,9 +1865,9 @@
  * N.B. After this call fhp needs an fh_put
  */
 __be32
-nfsd_statfs(struct svc_rqst *rqstp, struct svc_fh *fhp, struct kstatfs *stat)
+nfsd_statfs(struct svc_rqst *rqstp, struct svc_fh *fhp, struct kstatfs *stat, int access)
 {
-	__be32 err = fh_verify(rqstp, fhp, 0, NFSD_MAY_NOP);
+	__be32 err = fh_verify(rqstp, fhp, 0, NFSD_MAY_NOP | access);
 	if (!err && vfs_statfs(fhp->fh_dentry,stat))
 		err = nfserr_io;
 	return err;
@@ -1966,11 +1965,20 @@
 void
 nfsd_racache_shutdown(void)
 {
-	if (!raparml)
-		return;
+	struct raparms *raparm, *last_raparm;
+	unsigned int i;
+
 	dprintk("nfsd: freeing readahead buffers.\n");
-	kfree(raparml);
-	raparml = NULL;
+
+	for (i = 0; i < RAPARM_HASH_SIZE; i++) {
+		raparm = raparm_hash[i].pb_head;
+		while(raparm) {
+			last_raparm = raparm;
+			raparm = raparm->p_next;
+			kfree(last_raparm);
+		}
+		raparm_hash[i].pb_head = NULL;
+	}
 }
 /*
  * Initialize readahead param cache
@@ -1981,35 +1989,38 @@
 	int	i;
 	int	j = 0;
 	int	nperbucket;
+	struct raparms **raparm = NULL;
 
 
-	if (raparml)
+	if (raparm_hash[0].pb_head)
 		return 0;
-	if (cache_size < 2*RAPARM_HASH_SIZE)
-		cache_size = 2*RAPARM_HASH_SIZE;
-	raparml = kcalloc(cache_size, sizeof(struct raparms), GFP_KERNEL);
-
-	if (!raparml) {
-		printk(KERN_WARNING
-			"nfsd: Could not allocate memory read-ahead cache.\n");
-		return -ENOMEM;
-	}
+	nperbucket = DIV_ROUND_UP(cache_size, RAPARM_HASH_SIZE);
+	if (nperbucket < 2)
+		nperbucket = 2;
+	cache_size = nperbucket * RAPARM_HASH_SIZE;
 
 	dprintk("nfsd: allocating %d readahead buffers.\n", cache_size);
-	for (i = 0 ; i < RAPARM_HASH_SIZE ; i++) {
-		raparm_hash[i].pb_head = NULL;
+
+	for (i = 0; i < RAPARM_HASH_SIZE; i++) {
 		spin_lock_init(&raparm_hash[i].pb_lock);
-	}
-	nperbucket = DIV_ROUND_UP(cache_size, RAPARM_HASH_SIZE);
-	for (i = 0; i < cache_size - 1; i++) {
-		if (i % nperbucket == 0)
-			raparm_hash[j++].pb_head = raparml + i;
-		if (i % nperbucket < nperbucket-1)
-			raparml[i].p_next = raparml + i + 1;
+
+		raparm = &raparm_hash[i].pb_head;
+		for (j = 0; j < nperbucket; j++) {
+			*raparm = kzalloc(sizeof(struct raparms), GFP_KERNEL);
+			if (!*raparm)
+				goto out_nomem;
+			raparm = &(*raparm)->p_next;
+		}
+		*raparm = NULL;
 	}
 
 	nfsdstats.ra_size = cache_size;
 	return 0;
+
+out_nomem:
+	dprintk("nfsd: kmalloc failed, freeing readahead buffers\n");
+	nfsd_racache_shutdown();
+	return -ENOMEM;
 }
 
 #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
diff --git a/fs/ocfs2/Makefile b/fs/ocfs2/Makefile
index f6956de..589dcdf 100644
--- a/fs/ocfs2/Makefile
+++ b/fs/ocfs2/Makefile
@@ -34,7 +34,8 @@
 	symlink.o 		\
 	sysfile.o 		\
 	uptodate.o		\
-	ver.o
+	ver.o			\
+	xattr.o
 
 ocfs2_stackglue-objs := stackglue.o
 ocfs2_stack_o2cb-objs := stack_o2cb.o
diff --git a/fs/ocfs2/alloc.c b/fs/ocfs2/alloc.c
index 29ff57e..0cc2deb 100644
--- a/fs/ocfs2/alloc.c
+++ b/fs/ocfs2/alloc.c
@@ -49,6 +49,340 @@
 
 #include "buffer_head_io.h"
 
+
+/*
+ * Operations for a specific extent tree type.
+ *
+ * To implement an on-disk btree (extent tree) type in ocfs2, add
+ * an ocfs2_extent_tree_operations structure and the matching
+ * ocfs2_init_<thingy>_extent_tree() function.  That's pretty much it
+ * for the allocation portion of the extent tree.
+ */
+struct ocfs2_extent_tree_operations {
+	/*
+	 * last_eb_blk is the block number of the right most leaf extent
+	 * block.  Most on-disk structures containing an extent tree store
+	 * this value for fast access.  The ->eo_set_last_eb_blk() and
+	 * ->eo_get_last_eb_blk() operations access this value.  They are
+	 *  both required.
+	 */
+	void (*eo_set_last_eb_blk)(struct ocfs2_extent_tree *et,
+				   u64 blkno);
+	u64 (*eo_get_last_eb_blk)(struct ocfs2_extent_tree *et);
+
+	/*
+	 * The on-disk structure usually keeps track of how many total
+	 * clusters are stored in this extent tree.  This function updates
+	 * that value.  new_clusters is the delta, and must be
+	 * added to the total.  Required.
+	 */
+	void (*eo_update_clusters)(struct inode *inode,
+				   struct ocfs2_extent_tree *et,
+				   u32 new_clusters);
+
+	/*
+	 * If ->eo_insert_check() exists, it is called before rec is
+	 * inserted into the extent tree.  It is optional.
+	 */
+	int (*eo_insert_check)(struct inode *inode,
+			       struct ocfs2_extent_tree *et,
+			       struct ocfs2_extent_rec *rec);
+	int (*eo_sanity_check)(struct inode *inode, struct ocfs2_extent_tree *et);
+
+	/*
+	 * --------------------------------------------------------------
+	 * The remaining are internal to ocfs2_extent_tree and don't have
+	 * accessor functions
+	 */
+
+	/*
+	 * ->eo_fill_root_el() takes et->et_object and sets et->et_root_el.
+	 * It is required.
+	 */
+	void (*eo_fill_root_el)(struct ocfs2_extent_tree *et);
+
+	/*
+	 * ->eo_fill_max_leaf_clusters sets et->et_max_leaf_clusters if
+	 * it exists.  If it does not, et->et_max_leaf_clusters is set
+	 * to 0 (unlimited).  Optional.
+	 */
+	void (*eo_fill_max_leaf_clusters)(struct inode *inode,
+					  struct ocfs2_extent_tree *et);
+};
+
+
+/*
+ * Pre-declare ocfs2_dinode_et_ops so we can use it as a sanity check
+ * in the methods.
+ */
+static u64 ocfs2_dinode_get_last_eb_blk(struct ocfs2_extent_tree *et);
+static void ocfs2_dinode_set_last_eb_blk(struct ocfs2_extent_tree *et,
+					 u64 blkno);
+static void ocfs2_dinode_update_clusters(struct inode *inode,
+					 struct ocfs2_extent_tree *et,
+					 u32 clusters);
+static int ocfs2_dinode_insert_check(struct inode *inode,
+				     struct ocfs2_extent_tree *et,
+				     struct ocfs2_extent_rec *rec);
+static int ocfs2_dinode_sanity_check(struct inode *inode,
+				     struct ocfs2_extent_tree *et);
+static void ocfs2_dinode_fill_root_el(struct ocfs2_extent_tree *et);
+static struct ocfs2_extent_tree_operations ocfs2_dinode_et_ops = {
+	.eo_set_last_eb_blk	= ocfs2_dinode_set_last_eb_blk,
+	.eo_get_last_eb_blk	= ocfs2_dinode_get_last_eb_blk,
+	.eo_update_clusters	= ocfs2_dinode_update_clusters,
+	.eo_insert_check	= ocfs2_dinode_insert_check,
+	.eo_sanity_check	= ocfs2_dinode_sanity_check,
+	.eo_fill_root_el	= ocfs2_dinode_fill_root_el,
+};
+
+static void ocfs2_dinode_set_last_eb_blk(struct ocfs2_extent_tree *et,
+					 u64 blkno)
+{
+	struct ocfs2_dinode *di = et->et_object;
+
+	BUG_ON(et->et_ops != &ocfs2_dinode_et_ops);
+	di->i_last_eb_blk = cpu_to_le64(blkno);
+}
+
+static u64 ocfs2_dinode_get_last_eb_blk(struct ocfs2_extent_tree *et)
+{
+	struct ocfs2_dinode *di = et->et_object;
+
+	BUG_ON(et->et_ops != &ocfs2_dinode_et_ops);
+	return le64_to_cpu(di->i_last_eb_blk);
+}
+
+static void ocfs2_dinode_update_clusters(struct inode *inode,
+					 struct ocfs2_extent_tree *et,
+					 u32 clusters)
+{
+	struct ocfs2_dinode *di = et->et_object;
+
+	le32_add_cpu(&di->i_clusters, clusters);
+	spin_lock(&OCFS2_I(inode)->ip_lock);
+	OCFS2_I(inode)->ip_clusters = le32_to_cpu(di->i_clusters);
+	spin_unlock(&OCFS2_I(inode)->ip_lock);
+}
+
+static int ocfs2_dinode_insert_check(struct inode *inode,
+				     struct ocfs2_extent_tree *et,
+				     struct ocfs2_extent_rec *rec)
+{
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+	BUG_ON(OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_DATA_FL);
+	mlog_bug_on_msg(!ocfs2_sparse_alloc(osb) &&
+			(OCFS2_I(inode)->ip_clusters != rec->e_cpos),
+			"Device %s, asking for sparse allocation: inode %llu, "
+			"cpos %u, clusters %u\n",
+			osb->dev_str,
+			(unsigned long long)OCFS2_I(inode)->ip_blkno,
+			rec->e_cpos,
+			OCFS2_I(inode)->ip_clusters);
+
+	return 0;
+}
+
+static int ocfs2_dinode_sanity_check(struct inode *inode,
+				     struct ocfs2_extent_tree *et)
+{
+	int ret = 0;
+	struct ocfs2_dinode *di;
+
+	BUG_ON(et->et_ops != &ocfs2_dinode_et_ops);
+
+	di = et->et_object;
+	if (!OCFS2_IS_VALID_DINODE(di)) {
+		ret = -EIO;
+		ocfs2_error(inode->i_sb,
+			"Inode %llu has invalid path root",
+			(unsigned long long)OCFS2_I(inode)->ip_blkno);
+	}
+
+	return ret;
+}
+
+static void ocfs2_dinode_fill_root_el(struct ocfs2_extent_tree *et)
+{
+	struct ocfs2_dinode *di = et->et_object;
+
+	et->et_root_el = &di->id2.i_list;
+}
+
+
+static void ocfs2_xattr_value_fill_root_el(struct ocfs2_extent_tree *et)
+{
+	struct ocfs2_xattr_value_root *xv = et->et_object;
+
+	et->et_root_el = &xv->xr_list;
+}
+
+static void ocfs2_xattr_value_set_last_eb_blk(struct ocfs2_extent_tree *et,
+					      u64 blkno)
+{
+	struct ocfs2_xattr_value_root *xv =
+		(struct ocfs2_xattr_value_root *)et->et_object;
+
+	xv->xr_last_eb_blk = cpu_to_le64(blkno);
+}
+
+static u64 ocfs2_xattr_value_get_last_eb_blk(struct ocfs2_extent_tree *et)
+{
+	struct ocfs2_xattr_value_root *xv =
+		(struct ocfs2_xattr_value_root *) et->et_object;
+
+	return le64_to_cpu(xv->xr_last_eb_blk);
+}
+
+static void ocfs2_xattr_value_update_clusters(struct inode *inode,
+					      struct ocfs2_extent_tree *et,
+					      u32 clusters)
+{
+	struct ocfs2_xattr_value_root *xv =
+		(struct ocfs2_xattr_value_root *)et->et_object;
+
+	le32_add_cpu(&xv->xr_clusters, clusters);
+}
+
+static struct ocfs2_extent_tree_operations ocfs2_xattr_value_et_ops = {
+	.eo_set_last_eb_blk	= ocfs2_xattr_value_set_last_eb_blk,
+	.eo_get_last_eb_blk	= ocfs2_xattr_value_get_last_eb_blk,
+	.eo_update_clusters	= ocfs2_xattr_value_update_clusters,
+	.eo_fill_root_el	= ocfs2_xattr_value_fill_root_el,
+};
+
+static void ocfs2_xattr_tree_fill_root_el(struct ocfs2_extent_tree *et)
+{
+	struct ocfs2_xattr_block *xb = et->et_object;
+
+	et->et_root_el = &xb->xb_attrs.xb_root.xt_list;
+}
+
+static void ocfs2_xattr_tree_fill_max_leaf_clusters(struct inode *inode,
+						    struct ocfs2_extent_tree *et)
+{
+	et->et_max_leaf_clusters =
+		ocfs2_clusters_for_bytes(inode->i_sb,
+					 OCFS2_MAX_XATTR_TREE_LEAF_SIZE);
+}
+
+static void ocfs2_xattr_tree_set_last_eb_blk(struct ocfs2_extent_tree *et,
+					     u64 blkno)
+{
+	struct ocfs2_xattr_block *xb = et->et_object;
+	struct ocfs2_xattr_tree_root *xt = &xb->xb_attrs.xb_root;
+
+	xt->xt_last_eb_blk = cpu_to_le64(blkno);
+}
+
+static u64 ocfs2_xattr_tree_get_last_eb_blk(struct ocfs2_extent_tree *et)
+{
+	struct ocfs2_xattr_block *xb = et->et_object;
+	struct ocfs2_xattr_tree_root *xt = &xb->xb_attrs.xb_root;
+
+	return le64_to_cpu(xt->xt_last_eb_blk);
+}
+
+static void ocfs2_xattr_tree_update_clusters(struct inode *inode,
+					     struct ocfs2_extent_tree *et,
+					     u32 clusters)
+{
+	struct ocfs2_xattr_block *xb = et->et_object;
+
+	le32_add_cpu(&xb->xb_attrs.xb_root.xt_clusters, clusters);
+}
+
+static struct ocfs2_extent_tree_operations ocfs2_xattr_tree_et_ops = {
+	.eo_set_last_eb_blk	= ocfs2_xattr_tree_set_last_eb_blk,
+	.eo_get_last_eb_blk	= ocfs2_xattr_tree_get_last_eb_blk,
+	.eo_update_clusters	= ocfs2_xattr_tree_update_clusters,
+	.eo_fill_root_el	= ocfs2_xattr_tree_fill_root_el,
+	.eo_fill_max_leaf_clusters = ocfs2_xattr_tree_fill_max_leaf_clusters,
+};
+
+static void __ocfs2_init_extent_tree(struct ocfs2_extent_tree *et,
+				     struct inode *inode,
+				     struct buffer_head *bh,
+				     void *obj,
+				     struct ocfs2_extent_tree_operations *ops)
+{
+	et->et_ops = ops;
+	et->et_root_bh = bh;
+	if (!obj)
+		obj = (void *)bh->b_data;
+	et->et_object = obj;
+
+	et->et_ops->eo_fill_root_el(et);
+	if (!et->et_ops->eo_fill_max_leaf_clusters)
+		et->et_max_leaf_clusters = 0;
+	else
+		et->et_ops->eo_fill_max_leaf_clusters(inode, et);
+}
+
+void ocfs2_init_dinode_extent_tree(struct ocfs2_extent_tree *et,
+				   struct inode *inode,
+				   struct buffer_head *bh)
+{
+	__ocfs2_init_extent_tree(et, inode, bh, NULL, &ocfs2_dinode_et_ops);
+}
+
+void ocfs2_init_xattr_tree_extent_tree(struct ocfs2_extent_tree *et,
+				       struct inode *inode,
+				       struct buffer_head *bh)
+{
+	__ocfs2_init_extent_tree(et, inode, bh, NULL,
+				 &ocfs2_xattr_tree_et_ops);
+}
+
+void ocfs2_init_xattr_value_extent_tree(struct ocfs2_extent_tree *et,
+					struct inode *inode,
+					struct buffer_head *bh,
+					struct ocfs2_xattr_value_root *xv)
+{
+	__ocfs2_init_extent_tree(et, inode, bh, xv,
+				 &ocfs2_xattr_value_et_ops);
+}
+
+static inline void ocfs2_et_set_last_eb_blk(struct ocfs2_extent_tree *et,
+					    u64 new_last_eb_blk)
+{
+	et->et_ops->eo_set_last_eb_blk(et, new_last_eb_blk);
+}
+
+static inline u64 ocfs2_et_get_last_eb_blk(struct ocfs2_extent_tree *et)
+{
+	return et->et_ops->eo_get_last_eb_blk(et);
+}
+
+static inline void ocfs2_et_update_clusters(struct inode *inode,
+					    struct ocfs2_extent_tree *et,
+					    u32 clusters)
+{
+	et->et_ops->eo_update_clusters(inode, et, clusters);
+}
+
+static inline int ocfs2_et_insert_check(struct inode *inode,
+					struct ocfs2_extent_tree *et,
+					struct ocfs2_extent_rec *rec)
+{
+	int ret = 0;
+
+	if (et->et_ops->eo_insert_check)
+		ret = et->et_ops->eo_insert_check(inode, et, rec);
+	return ret;
+}
+
+static inline int ocfs2_et_sanity_check(struct inode *inode,
+					struct ocfs2_extent_tree *et)
+{
+	int ret = 0;
+
+	if (et->et_ops->eo_sanity_check)
+		ret = et->et_ops->eo_sanity_check(inode, et);
+	return ret;
+}
+
 static void ocfs2_free_truncate_context(struct ocfs2_truncate_context *tc);
 static int ocfs2_cache_extent_block_free(struct ocfs2_cached_dealloc_ctxt *ctxt,
 					 struct ocfs2_extent_block *eb);
@@ -205,17 +539,6 @@
 }
 
 /*
- * Allocate and initialize a new path based on a disk inode tree.
- */
-static struct ocfs2_path *ocfs2_new_inode_path(struct buffer_head *di_bh)
-{
-	struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data;
-	struct ocfs2_extent_list *el = &di->id2.i_list;
-
-	return ocfs2_new_path(di_bh, el);
-}
-
-/*
  * Convenience function to journal all components in a path.
  */
 static int ocfs2_journal_access_path(struct inode *inode, handle_t *handle,
@@ -368,39 +691,35 @@
  */
 int ocfs2_num_free_extents(struct ocfs2_super *osb,
 			   struct inode *inode,
-			   struct ocfs2_dinode *fe)
+			   struct ocfs2_extent_tree *et)
 {
 	int retval;
-	struct ocfs2_extent_list *el;
+	struct ocfs2_extent_list *el = NULL;
 	struct ocfs2_extent_block *eb;
 	struct buffer_head *eb_bh = NULL;
+	u64 last_eb_blk = 0;
 
 	mlog_entry_void();
 
-	if (!OCFS2_IS_VALID_DINODE(fe)) {
-		OCFS2_RO_ON_INVALID_DINODE(inode->i_sb, fe);
-		retval = -EIO;
-		goto bail;
-	}
+	el = et->et_root_el;
+	last_eb_blk = ocfs2_et_get_last_eb_blk(et);
 
-	if (fe->i_last_eb_blk) {
-		retval = ocfs2_read_block(osb, le64_to_cpu(fe->i_last_eb_blk),
-					  &eb_bh, OCFS2_BH_CACHED, inode);
+	if (last_eb_blk) {
+		retval = ocfs2_read_block(inode, last_eb_blk,
+					  &eb_bh);
 		if (retval < 0) {
 			mlog_errno(retval);
 			goto bail;
 		}
 		eb = (struct ocfs2_extent_block *) eb_bh->b_data;
 		el = &eb->h_list;
-	} else
-		el = &fe->id2.i_list;
+	}
 
 	BUG_ON(el->l_tree_depth != 0);
 
 	retval = le16_to_cpu(el->l_count) - le16_to_cpu(el->l_next_free_rec);
 bail:
-	if (eb_bh)
-		brelse(eb_bh);
+	brelse(eb_bh);
 
 	mlog_exit(retval);
 	return retval;
@@ -486,8 +805,7 @@
 bail:
 	if (status < 0) {
 		for(i = 0; i < wanted; i++) {
-			if (bhs[i])
-				brelse(bhs[i]);
+			brelse(bhs[i]);
 			bhs[i] = NULL;
 		}
 	}
@@ -531,7 +849,7 @@
 static int ocfs2_add_branch(struct ocfs2_super *osb,
 			    handle_t *handle,
 			    struct inode *inode,
-			    struct buffer_head *fe_bh,
+			    struct ocfs2_extent_tree *et,
 			    struct buffer_head *eb_bh,
 			    struct buffer_head **last_eb_bh,
 			    struct ocfs2_alloc_context *meta_ac)
@@ -540,7 +858,6 @@
 	u64 next_blkno, new_last_eb_blk;
 	struct buffer_head *bh;
 	struct buffer_head **new_eb_bhs = NULL;
-	struct ocfs2_dinode *fe;
 	struct ocfs2_extent_block *eb;
 	struct ocfs2_extent_list  *eb_el;
 	struct ocfs2_extent_list  *el;
@@ -550,13 +867,11 @@
 
 	BUG_ON(!last_eb_bh || !*last_eb_bh);
 
-	fe = (struct ocfs2_dinode *) fe_bh->b_data;
-
 	if (eb_bh) {
 		eb = (struct ocfs2_extent_block *) eb_bh->b_data;
 		el = &eb->h_list;
 	} else
-		el = &fe->id2.i_list;
+		el = et->et_root_el;
 
 	/* we never add a branch to a leaf. */
 	BUG_ON(!el->l_tree_depth);
@@ -646,7 +961,7 @@
 		mlog_errno(status);
 		goto bail;
 	}
-	status = ocfs2_journal_access(handle, inode, fe_bh,
+	status = ocfs2_journal_access(handle, inode, et->et_root_bh,
 				      OCFS2_JOURNAL_ACCESS_WRITE);
 	if (status < 0) {
 		mlog_errno(status);
@@ -662,7 +977,7 @@
 	}
 
 	/* Link the new branch into the rest of the tree (el will
-	 * either be on the fe, or the extent block passed in. */
+	 * either be on the root_bh, or the extent block passed in. */
 	i = le16_to_cpu(el->l_next_free_rec);
 	el->l_recs[i].e_blkno = cpu_to_le64(next_blkno);
 	el->l_recs[i].e_cpos = cpu_to_le32(new_cpos);
@@ -671,7 +986,7 @@
 
 	/* fe needs a new last extent block pointer, as does the
 	 * next_leaf on the previously last-extent-block. */
-	fe->i_last_eb_blk = cpu_to_le64(new_last_eb_blk);
+	ocfs2_et_set_last_eb_blk(et, new_last_eb_blk);
 
 	eb = (struct ocfs2_extent_block *) (*last_eb_bh)->b_data;
 	eb->h_next_leaf_blk = cpu_to_le64(new_last_eb_blk);
@@ -679,7 +994,7 @@
 	status = ocfs2_journal_dirty(handle, *last_eb_bh);
 	if (status < 0)
 		mlog_errno(status);
-	status = ocfs2_journal_dirty(handle, fe_bh);
+	status = ocfs2_journal_dirty(handle, et->et_root_bh);
 	if (status < 0)
 		mlog_errno(status);
 	if (eb_bh) {
@@ -700,8 +1015,7 @@
 bail:
 	if (new_eb_bhs) {
 		for (i = 0; i < new_blocks; i++)
-			if (new_eb_bhs[i])
-				brelse(new_eb_bhs[i]);
+			brelse(new_eb_bhs[i]);
 		kfree(new_eb_bhs);
 	}
 
@@ -717,16 +1031,15 @@
 static int ocfs2_shift_tree_depth(struct ocfs2_super *osb,
 				  handle_t *handle,
 				  struct inode *inode,
-				  struct buffer_head *fe_bh,
+				  struct ocfs2_extent_tree *et,
 				  struct ocfs2_alloc_context *meta_ac,
 				  struct buffer_head **ret_new_eb_bh)
 {
 	int status, i;
 	u32 new_clusters;
 	struct buffer_head *new_eb_bh = NULL;
-	struct ocfs2_dinode *fe;
 	struct ocfs2_extent_block *eb;
-	struct ocfs2_extent_list  *fe_el;
+	struct ocfs2_extent_list  *root_el;
 	struct ocfs2_extent_list  *eb_el;
 
 	mlog_entry_void();
@@ -746,8 +1059,7 @@
 	}
 
 	eb_el = &eb->h_list;
-	fe = (struct ocfs2_dinode *) fe_bh->b_data;
-	fe_el = &fe->id2.i_list;
+	root_el = et->et_root_el;
 
 	status = ocfs2_journal_access(handle, inode, new_eb_bh,
 				      OCFS2_JOURNAL_ACCESS_CREATE);
@@ -756,11 +1068,11 @@
 		goto bail;
 	}
 
-	/* copy the fe data into the new extent block */
-	eb_el->l_tree_depth = fe_el->l_tree_depth;
-	eb_el->l_next_free_rec = fe_el->l_next_free_rec;
-	for(i = 0; i < le16_to_cpu(fe_el->l_next_free_rec); i++)
-		eb_el->l_recs[i] = fe_el->l_recs[i];
+	/* copy the root extent list data into the new extent block */
+	eb_el->l_tree_depth = root_el->l_tree_depth;
+	eb_el->l_next_free_rec = root_el->l_next_free_rec;
+	for (i = 0; i < le16_to_cpu(root_el->l_next_free_rec); i++)
+		eb_el->l_recs[i] = root_el->l_recs[i];
 
 	status = ocfs2_journal_dirty(handle, new_eb_bh);
 	if (status < 0) {
@@ -768,7 +1080,7 @@
 		goto bail;
 	}
 
-	status = ocfs2_journal_access(handle, inode, fe_bh,
+	status = ocfs2_journal_access(handle, inode, et->et_root_bh,
 				      OCFS2_JOURNAL_ACCESS_WRITE);
 	if (status < 0) {
 		mlog_errno(status);
@@ -777,21 +1089,21 @@
 
 	new_clusters = ocfs2_sum_rightmost_rec(eb_el);
 
-	/* update fe now */
-	le16_add_cpu(&fe_el->l_tree_depth, 1);
-	fe_el->l_recs[0].e_cpos = 0;
-	fe_el->l_recs[0].e_blkno = eb->h_blkno;
-	fe_el->l_recs[0].e_int_clusters = cpu_to_le32(new_clusters);
-	for(i = 1; i < le16_to_cpu(fe_el->l_next_free_rec); i++)
-		memset(&fe_el->l_recs[i], 0, sizeof(struct ocfs2_extent_rec));
-	fe_el->l_next_free_rec = cpu_to_le16(1);
+	/* update root_bh now */
+	le16_add_cpu(&root_el->l_tree_depth, 1);
+	root_el->l_recs[0].e_cpos = 0;
+	root_el->l_recs[0].e_blkno = eb->h_blkno;
+	root_el->l_recs[0].e_int_clusters = cpu_to_le32(new_clusters);
+	for (i = 1; i < le16_to_cpu(root_el->l_next_free_rec); i++)
+		memset(&root_el->l_recs[i], 0, sizeof(struct ocfs2_extent_rec));
+	root_el->l_next_free_rec = cpu_to_le16(1);
 
 	/* If this is our 1st tree depth shift, then last_eb_blk
 	 * becomes the allocated extent block */
-	if (fe_el->l_tree_depth == cpu_to_le16(1))
-		fe->i_last_eb_blk = eb->h_blkno;
+	if (root_el->l_tree_depth == cpu_to_le16(1))
+		ocfs2_et_set_last_eb_blk(et, le64_to_cpu(eb->h_blkno));
 
-	status = ocfs2_journal_dirty(handle, fe_bh);
+	status = ocfs2_journal_dirty(handle, et->et_root_bh);
 	if (status < 0) {
 		mlog_errno(status);
 		goto bail;
@@ -801,8 +1113,7 @@
 	new_eb_bh = NULL;
 	status = 0;
 bail:
-	if (new_eb_bh)
-		brelse(new_eb_bh);
+	brelse(new_eb_bh);
 
 	mlog_exit(status);
 	return status;
@@ -817,22 +1128,21 @@
  * 1) a lowest extent block is found, then we pass it back in
  *    *lowest_eb_bh and return '0'
  *
- * 2) the search fails to find anything, but the dinode has room. We
+ * 2) the search fails to find anything, but the root_el has room. We
  *    pass NULL back in *lowest_eb_bh, but still return '0'
  *
- * 3) the search fails to find anything AND the dinode is full, in
+ * 3) the search fails to find anything AND the root_el is full, in
  *    which case we return > 0
  *
  * return status < 0 indicates an error.
  */
 static int ocfs2_find_branch_target(struct ocfs2_super *osb,
 				    struct inode *inode,
-				    struct buffer_head *fe_bh,
+				    struct ocfs2_extent_tree *et,
 				    struct buffer_head **target_bh)
 {
 	int status = 0, i;
 	u64 blkno;
-	struct ocfs2_dinode *fe;
 	struct ocfs2_extent_block *eb;
 	struct ocfs2_extent_list  *el;
 	struct buffer_head *bh = NULL;
@@ -842,8 +1152,7 @@
 
 	*target_bh = NULL;
 
-	fe = (struct ocfs2_dinode *) fe_bh->b_data;
-	el = &fe->id2.i_list;
+	el = et->et_root_el;
 
 	while(le16_to_cpu(el->l_tree_depth) > 1) {
 		if (le16_to_cpu(el->l_next_free_rec) == 0) {
@@ -864,13 +1173,10 @@
 			goto bail;
 		}
 
-		if (bh) {
-			brelse(bh);
-			bh = NULL;
-		}
+		brelse(bh);
+		bh = NULL;
 
-		status = ocfs2_read_block(osb, blkno, &bh, OCFS2_BH_CACHED,
-					  inode);
+		status = ocfs2_read_block(inode, blkno, &bh);
 		if (status < 0) {
 			mlog_errno(status);
 			goto bail;
@@ -886,8 +1192,7 @@
 
 		if (le16_to_cpu(el->l_next_free_rec) <
 		    le16_to_cpu(el->l_count)) {
-			if (lowest_bh)
-				brelse(lowest_bh);
+			brelse(lowest_bh);
 			lowest_bh = bh;
 			get_bh(lowest_bh);
 		}
@@ -895,14 +1200,13 @@
 
 	/* If we didn't find one and the fe doesn't have any room,
 	 * then return '1' */
-	if (!lowest_bh
-	    && (fe->id2.i_list.l_next_free_rec == fe->id2.i_list.l_count))
+	el = et->et_root_el;
+	if (!lowest_bh && (el->l_next_free_rec == el->l_count))
 		status = 1;
 
 	*target_bh = lowest_bh;
 bail:
-	if (bh)
-		brelse(bh);
+	brelse(bh);
 
 	mlog_exit(status);
 	return status;
@@ -919,19 +1223,19 @@
  * *last_eb_bh will be updated by ocfs2_add_branch().
  */
 static int ocfs2_grow_tree(struct inode *inode, handle_t *handle,
-			   struct buffer_head *di_bh, int *final_depth,
+			   struct ocfs2_extent_tree *et, int *final_depth,
 			   struct buffer_head **last_eb_bh,
 			   struct ocfs2_alloc_context *meta_ac)
 {
 	int ret, shift;
-	struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data;
-	int depth = le16_to_cpu(di->id2.i_list.l_tree_depth);
+	struct ocfs2_extent_list *el = et->et_root_el;
+	int depth = le16_to_cpu(el->l_tree_depth);
 	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
 	struct buffer_head *bh = NULL;
 
 	BUG_ON(meta_ac == NULL);
 
-	shift = ocfs2_find_branch_target(osb, inode, di_bh, &bh);
+	shift = ocfs2_find_branch_target(osb, inode, et, &bh);
 	if (shift < 0) {
 		ret = shift;
 		mlog_errno(ret);
@@ -948,7 +1252,7 @@
 		/* ocfs2_shift_tree_depth will return us a buffer with
 		 * the new extent block (so we can pass that to
 		 * ocfs2_add_branch). */
-		ret = ocfs2_shift_tree_depth(osb, handle, inode, di_bh,
+		ret = ocfs2_shift_tree_depth(osb, handle, inode, et,
 					     meta_ac, &bh);
 		if (ret < 0) {
 			mlog_errno(ret);
@@ -975,7 +1279,7 @@
 	/* call ocfs2_add_branch to add the final part of the tree with
 	 * the new data. */
 	mlog(0, "add branch. bh = %p\n", bh);
-	ret = ocfs2_add_branch(osb, handle, inode, di_bh, bh, last_eb_bh,
+	ret = ocfs2_add_branch(osb, handle, inode, et, bh, last_eb_bh,
 			       meta_ac);
 	if (ret < 0) {
 		mlog_errno(ret);
@@ -1236,8 +1540,7 @@
 
 		brelse(bh);
 		bh = NULL;
-		ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), blkno,
-				       &bh, OCFS2_BH_CACHED, inode);
+		ret = ocfs2_read_block(inode, blkno, &bh);
 		if (ret) {
 			mlog_errno(ret);
 			goto out;
@@ -2058,11 +2361,11 @@
 				     struct ocfs2_path *right_path,
 				     int subtree_index,
 				     struct ocfs2_cached_dealloc_ctxt *dealloc,
-				     int *deleted)
+				     int *deleted,
+				     struct ocfs2_extent_tree *et)
 {
 	int ret, i, del_right_subtree = 0, right_has_empty = 0;
-	struct buffer_head *root_bh, *di_bh = path_root_bh(right_path);
-	struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data;
+	struct buffer_head *root_bh, *et_root_bh = path_root_bh(right_path);
 	struct ocfs2_extent_list *right_leaf_el, *left_leaf_el;
 	struct ocfs2_extent_block *eb;
 
@@ -2114,7 +2417,7 @@
 		 * We have to update i_last_eb_blk during the meta
 		 * data delete.
 		 */
-		ret = ocfs2_journal_access(handle, inode, di_bh,
+		ret = ocfs2_journal_access(handle, inode, et_root_bh,
 					   OCFS2_JOURNAL_ACCESS_WRITE);
 		if (ret) {
 			mlog_errno(ret);
@@ -2189,7 +2492,7 @@
 		ocfs2_update_edge_lengths(inode, handle, left_path);
 
 		eb = (struct ocfs2_extent_block *)path_leaf_bh(left_path)->b_data;
-		di->i_last_eb_blk = eb->h_blkno;
+		ocfs2_et_set_last_eb_blk(et, le64_to_cpu(eb->h_blkno));
 
 		/*
 		 * Removal of the extent in the left leaf was skipped
@@ -2199,7 +2502,7 @@
 		if (right_has_empty)
 			ocfs2_remove_empty_extent(left_leaf_el);
 
-		ret = ocfs2_journal_dirty(handle, di_bh);
+		ret = ocfs2_journal_dirty(handle, et_root_bh);
 		if (ret)
 			mlog_errno(ret);
 
@@ -2322,7 +2625,8 @@
 				    handle_t *handle, int orig_credits,
 				    struct ocfs2_path *path,
 				    struct ocfs2_cached_dealloc_ctxt *dealloc,
-				    struct ocfs2_path **empty_extent_path)
+				    struct ocfs2_path **empty_extent_path,
+				    struct ocfs2_extent_tree *et)
 {
 	int ret, subtree_root, deleted;
 	u32 right_cpos;
@@ -2395,7 +2699,7 @@
 
 		ret = ocfs2_rotate_subtree_left(inode, handle, left_path,
 						right_path, subtree_root,
-						dealloc, &deleted);
+						dealloc, &deleted, et);
 		if (ret == -EAGAIN) {
 			/*
 			 * The rotation has to temporarily stop due to
@@ -2438,29 +2742,20 @@
 }
 
 static int ocfs2_remove_rightmost_path(struct inode *inode, handle_t *handle,
-				       struct ocfs2_path *path,
-				       struct ocfs2_cached_dealloc_ctxt *dealloc)
+				struct ocfs2_path *path,
+				struct ocfs2_cached_dealloc_ctxt *dealloc,
+				struct ocfs2_extent_tree *et)
 {
 	int ret, subtree_index;
 	u32 cpos;
 	struct ocfs2_path *left_path = NULL;
-	struct ocfs2_dinode *di;
 	struct ocfs2_extent_block *eb;
 	struct ocfs2_extent_list *el;
 
-	/*
-	 * XXX: This code assumes that the root is an inode, which is
-	 * true for now but may change as tree code gets generic.
-	 */
-	di = (struct ocfs2_dinode *)path_root_bh(path)->b_data;
-	if (!OCFS2_IS_VALID_DINODE(di)) {
-		ret = -EIO;
-		ocfs2_error(inode->i_sb,
-			    "Inode %llu has invalid path root",
-			    (unsigned long long)OCFS2_I(inode)->ip_blkno);
-		goto out;
-	}
 
+	ret = ocfs2_et_sanity_check(inode, et);
+	if (ret)
+		goto out;
 	/*
 	 * There's two ways we handle this depending on
 	 * whether path is the only existing one.
@@ -2517,7 +2812,7 @@
 		ocfs2_update_edge_lengths(inode, handle, left_path);
 
 		eb = (struct ocfs2_extent_block *)path_leaf_bh(left_path)->b_data;
-		di->i_last_eb_blk = eb->h_blkno;
+		ocfs2_et_set_last_eb_blk(et, le64_to_cpu(eb->h_blkno));
 	} else {
 		/*
 		 * 'path' is also the leftmost path which
@@ -2528,12 +2823,12 @@
 		 */
 		ocfs2_unlink_path(inode, handle, dealloc, path, 1);
 
-		el = &di->id2.i_list;
+		el = et->et_root_el;
 		el->l_tree_depth = 0;
 		el->l_next_free_rec = 0;
 		memset(&el->l_recs[0], 0, sizeof(struct ocfs2_extent_rec));
 
-		di->i_last_eb_blk = 0;
+		ocfs2_et_set_last_eb_blk(et, 0);
 	}
 
 	ocfs2_journal_dirty(handle, path_root_bh(path));
@@ -2561,7 +2856,8 @@
  */
 static int ocfs2_rotate_tree_left(struct inode *inode, handle_t *handle,
 				  struct ocfs2_path *path,
-				  struct ocfs2_cached_dealloc_ctxt *dealloc)
+				  struct ocfs2_cached_dealloc_ctxt *dealloc,
+				  struct ocfs2_extent_tree *et)
 {
 	int ret, orig_credits = handle->h_buffer_credits;
 	struct ocfs2_path *tmp_path = NULL, *restart_path = NULL;
@@ -2575,7 +2871,7 @@
 	if (path->p_tree_depth == 0) {
 rightmost_no_delete:
 		/*
-		 * In-inode extents. This is trivially handled, so do
+		 * Inline extents. This is trivially handled, so do
 		 * it up front.
 		 */
 		ret = ocfs2_rotate_rightmost_leaf_left(inode, handle,
@@ -2629,7 +2925,7 @@
 		 */
 
 		ret = ocfs2_remove_rightmost_path(inode, handle, path,
-						  dealloc);
+						  dealloc, et);
 		if (ret)
 			mlog_errno(ret);
 		goto out;
@@ -2641,7 +2937,7 @@
 	 */
 try_rotate:
 	ret = __ocfs2_rotate_tree_left(inode, handle, orig_credits, path,
-				       dealloc, &restart_path);
+				       dealloc, &restart_path, et);
 	if (ret && ret != -EAGAIN) {
 		mlog_errno(ret);
 		goto out;
@@ -2653,7 +2949,7 @@
 
 		ret = __ocfs2_rotate_tree_left(inode, handle, orig_credits,
 					       tmp_path, dealloc,
-					       &restart_path);
+					       &restart_path, et);
 		if (ret && ret != -EAGAIN) {
 			mlog_errno(ret);
 			goto out;
@@ -2939,6 +3235,7 @@
 				handle_t *handle,
 				struct ocfs2_extent_rec *split_rec,
 				struct ocfs2_cached_dealloc_ctxt *dealloc,
+				struct ocfs2_extent_tree *et,
 				int index)
 {
 	int ret, i, subtree_index = 0, has_empty_extent = 0;
@@ -3059,7 +3356,8 @@
 		    le16_to_cpu(el->l_next_free_rec) == 1) {
 
 			ret = ocfs2_remove_rightmost_path(inode, handle,
-							  right_path, dealloc);
+							  right_path,
+							  dealloc, et);
 			if (ret) {
 				mlog_errno(ret);
 				goto out;
@@ -3086,7 +3384,8 @@
 				     int split_index,
 				     struct ocfs2_extent_rec *split_rec,
 				     struct ocfs2_cached_dealloc_ctxt *dealloc,
-				     struct ocfs2_merge_ctxt *ctxt)
+				     struct ocfs2_merge_ctxt *ctxt,
+				     struct ocfs2_extent_tree *et)
 
 {
 	int ret = 0;
@@ -3104,7 +3403,7 @@
 		 * illegal.
 		 */
 		ret = ocfs2_rotate_tree_left(inode, handle, path,
-					     dealloc);
+					     dealloc, et);
 		if (ret) {
 			mlog_errno(ret);
 			goto out;
@@ -3147,7 +3446,8 @@
 		BUG_ON(!ocfs2_is_empty_extent(&el->l_recs[0]));
 
 		/* The merge left us with an empty extent, remove it. */
-		ret = ocfs2_rotate_tree_left(inode, handle, path, dealloc);
+		ret = ocfs2_rotate_tree_left(inode, handle, path,
+					     dealloc, et);
 		if (ret) {
 			mlog_errno(ret);
 			goto out;
@@ -3161,7 +3461,7 @@
 		 */
 		ret = ocfs2_merge_rec_left(inode, path,
 					   handle, rec,
-					   dealloc,
+					   dealloc, et,
 					   split_index);
 
 		if (ret) {
@@ -3170,7 +3470,7 @@
 		}
 
 		ret = ocfs2_rotate_tree_left(inode, handle, path,
-					     dealloc);
+					     dealloc, et);
 		/*
 		 * Error from this last rotate is not critical, so
 		 * print but don't bubble it up.
@@ -3190,7 +3490,7 @@
 			ret = ocfs2_merge_rec_left(inode,
 						   path,
 						   handle, split_rec,
-						   dealloc,
+						   dealloc, et,
 						   split_index);
 			if (ret) {
 				mlog_errno(ret);
@@ -3213,7 +3513,7 @@
 			 * our leaf. Try to rotate it away.
 			 */
 			ret = ocfs2_rotate_tree_left(inode, handle, path,
-						     dealloc);
+						     dealloc, et);
 			if (ret)
 				mlog_errno(ret);
 			ret = 0;
@@ -3347,16 +3647,6 @@
 	ocfs2_rotate_leaf(el, insert_rec);
 }
 
-static inline void ocfs2_update_dinode_clusters(struct inode *inode,
-						struct ocfs2_dinode *di,
-						u32 clusters)
-{
-	le32_add_cpu(&di->i_clusters, clusters);
-	spin_lock(&OCFS2_I(inode)->ip_lock);
-	OCFS2_I(inode)->ip_clusters = le32_to_cpu(di->i_clusters);
-	spin_unlock(&OCFS2_I(inode)->ip_lock);
-}
-
 static void ocfs2_adjust_rightmost_records(struct inode *inode,
 					   handle_t *handle,
 					   struct ocfs2_path *path,
@@ -3558,8 +3848,8 @@
 }
 
 /*
- * This function only does inserts on an allocation b-tree. For dinode
- * lists, ocfs2_insert_at_leaf() is called directly.
+ * This function only does inserts on an allocation b-tree. For tree
+ * depth = 0, ocfs2_insert_at_leaf() is called directly.
  *
  * right_path is the path we want to do the actual insert
  * in. left_path should only be passed in if we need to update that
@@ -3656,7 +3946,7 @@
 
 static int ocfs2_do_insert_extent(struct inode *inode,
 				  handle_t *handle,
-				  struct buffer_head *di_bh,
+				  struct ocfs2_extent_tree *et,
 				  struct ocfs2_extent_rec *insert_rec,
 				  struct ocfs2_insert_type *type)
 {
@@ -3664,13 +3954,11 @@
 	u32 cpos;
 	struct ocfs2_path *right_path = NULL;
 	struct ocfs2_path *left_path = NULL;
-	struct ocfs2_dinode *di;
 	struct ocfs2_extent_list *el;
 
-	di = (struct ocfs2_dinode *) di_bh->b_data;
-	el = &di->id2.i_list;
+	el = et->et_root_el;
 
-	ret = ocfs2_journal_access(handle, inode, di_bh,
+	ret = ocfs2_journal_access(handle, inode, et->et_root_bh,
 				   OCFS2_JOURNAL_ACCESS_WRITE);
 	if (ret) {
 		mlog_errno(ret);
@@ -3682,7 +3970,7 @@
 		goto out_update_clusters;
 	}
 
-	right_path = ocfs2_new_inode_path(di_bh);
+	right_path = ocfs2_new_path(et->et_root_bh, et->et_root_el);
 	if (!right_path) {
 		ret = -ENOMEM;
 		mlog_errno(ret);
@@ -3732,7 +4020,7 @@
 		 * ocfs2_rotate_tree_right() might have extended the
 		 * transaction without re-journaling our tree root.
 		 */
-		ret = ocfs2_journal_access(handle, inode, di_bh,
+		ret = ocfs2_journal_access(handle, inode, et->et_root_bh,
 					   OCFS2_JOURNAL_ACCESS_WRITE);
 		if (ret) {
 			mlog_errno(ret);
@@ -3757,10 +4045,10 @@
 
 out_update_clusters:
 	if (type->ins_split == SPLIT_NONE)
-		ocfs2_update_dinode_clusters(inode, di,
-					     le16_to_cpu(insert_rec->e_leaf_clusters));
+		ocfs2_et_update_clusters(inode, et,
+					 le16_to_cpu(insert_rec->e_leaf_clusters));
 
-	ret = ocfs2_journal_dirty(handle, di_bh);
+	ret = ocfs2_journal_dirty(handle, et->et_root_bh);
 	if (ret)
 		mlog_errno(ret);
 
@@ -3890,7 +4178,8 @@
 static void ocfs2_figure_contig_type(struct inode *inode,
 				     struct ocfs2_insert_type *insert,
 				     struct ocfs2_extent_list *el,
-				     struct ocfs2_extent_rec *insert_rec)
+				     struct ocfs2_extent_rec *insert_rec,
+				     struct ocfs2_extent_tree *et)
 {
 	int i;
 	enum ocfs2_contig_type contig_type = CONTIG_NONE;
@@ -3906,6 +4195,21 @@
 		}
 	}
 	insert->ins_contig = contig_type;
+
+	if (insert->ins_contig != CONTIG_NONE) {
+		struct ocfs2_extent_rec *rec =
+				&el->l_recs[insert->ins_contig_index];
+		unsigned int len = le16_to_cpu(rec->e_leaf_clusters) +
+				   le16_to_cpu(insert_rec->e_leaf_clusters);
+
+		/*
+		 * Caller might want us to limit the size of extents, don't
+		 * calculate contiguousness if we might exceed that limit.
+		 */
+		if (et->et_max_leaf_clusters &&
+		    (len > et->et_max_leaf_clusters))
+			insert->ins_contig = CONTIG_NONE;
+	}
 }
 
 /*
@@ -3914,8 +4218,8 @@
  * ocfs2_figure_appending_type() will figure out whether we'll have to
  * insert at the tail of the rightmost leaf.
  *
- * This should also work against the dinode list for tree's with 0
- * depth. If we consider the dinode list to be the rightmost leaf node
+ * This should also work against the root extent list for tree's with 0
+ * depth. If we consider the root extent list to be the rightmost leaf node
  * then the logic here makes sense.
  */
 static void ocfs2_figure_appending_type(struct ocfs2_insert_type *insert,
@@ -3966,14 +4270,13 @@
  * structure.
  */
 static int ocfs2_figure_insert_type(struct inode *inode,
-				    struct buffer_head *di_bh,
+				    struct ocfs2_extent_tree *et,
 				    struct buffer_head **last_eb_bh,
 				    struct ocfs2_extent_rec *insert_rec,
 				    int *free_records,
 				    struct ocfs2_insert_type *insert)
 {
 	int ret;
-	struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data;
 	struct ocfs2_extent_block *eb;
 	struct ocfs2_extent_list *el;
 	struct ocfs2_path *path = NULL;
@@ -3981,7 +4284,7 @@
 
 	insert->ins_split = SPLIT_NONE;
 
-	el = &di->id2.i_list;
+	el = et->et_root_el;
 	insert->ins_tree_depth = le16_to_cpu(el->l_tree_depth);
 
 	if (el->l_tree_depth) {
@@ -3991,9 +4294,7 @@
 		 * ocfs2_figure_insert_type() and ocfs2_add_branch()
 		 * may want it later.
 		 */
-		ret = ocfs2_read_block(OCFS2_SB(inode->i_sb),
-				       le64_to_cpu(di->i_last_eb_blk), &bh,
-				       OCFS2_BH_CACHED, inode);
+		ret = ocfs2_read_block(inode, ocfs2_et_get_last_eb_blk(et), &bh);
 		if (ret) {
 			mlog_exit(ret);
 			goto out;
@@ -4014,12 +4315,12 @@
 		le16_to_cpu(el->l_next_free_rec);
 
 	if (!insert->ins_tree_depth) {
-		ocfs2_figure_contig_type(inode, insert, el, insert_rec);
+		ocfs2_figure_contig_type(inode, insert, el, insert_rec, et);
 		ocfs2_figure_appending_type(insert, el, insert_rec);
 		return 0;
 	}
 
-	path = ocfs2_new_inode_path(di_bh);
+	path = ocfs2_new_path(et->et_root_bh, et->et_root_el);
 	if (!path) {
 		ret = -ENOMEM;
 		mlog_errno(ret);
@@ -4048,7 +4349,7 @@
          *     into two types of appends: simple record append, or a
          *     rotate inside the tail leaf.
 	 */
-	ocfs2_figure_contig_type(inode, insert, el, insert_rec);
+	ocfs2_figure_contig_type(inode, insert, el, insert_rec, et);
 
 	/*
 	 * The insert code isn't quite ready to deal with all cases of
@@ -4069,7 +4370,8 @@
 	 * the case that we're doing a tail append, so maybe we can
 	 * take advantage of that information somehow.
 	 */
-	if (le64_to_cpu(di->i_last_eb_blk) == path_leaf_bh(path)->b_blocknr) {
+	if (ocfs2_et_get_last_eb_blk(et) ==
+	    path_leaf_bh(path)->b_blocknr) {
 		/*
 		 * Ok, ocfs2_find_path() returned us the rightmost
 		 * tree path. This might be an appending insert. There are
@@ -4099,7 +4401,7 @@
 int ocfs2_insert_extent(struct ocfs2_super *osb,
 			handle_t *handle,
 			struct inode *inode,
-			struct buffer_head *fe_bh,
+			struct ocfs2_extent_tree *et,
 			u32 cpos,
 			u64 start_blk,
 			u32 new_clusters,
@@ -4112,26 +4414,21 @@
 	struct ocfs2_insert_type insert = {0, };
 	struct ocfs2_extent_rec rec;
 
-	BUG_ON(OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_DATA_FL);
-
 	mlog(0, "add %u clusters at position %u to inode %llu\n",
 	     new_clusters, cpos, (unsigned long long)OCFS2_I(inode)->ip_blkno);
 
-	mlog_bug_on_msg(!ocfs2_sparse_alloc(osb) &&
-			(OCFS2_I(inode)->ip_clusters != cpos),
-			"Device %s, asking for sparse allocation: inode %llu, "
-			"cpos %u, clusters %u\n",
-			osb->dev_str,
-			(unsigned long long)OCFS2_I(inode)->ip_blkno, cpos,
-			OCFS2_I(inode)->ip_clusters);
-
 	memset(&rec, 0, sizeof(rec));
 	rec.e_cpos = cpu_to_le32(cpos);
 	rec.e_blkno = cpu_to_le64(start_blk);
 	rec.e_leaf_clusters = cpu_to_le16(new_clusters);
 	rec.e_flags = flags;
+	status = ocfs2_et_insert_check(inode, et, &rec);
+	if (status) {
+		mlog_errno(status);
+		goto bail;
+	}
 
-	status = ocfs2_figure_insert_type(inode, fe_bh, &last_eb_bh, &rec,
+	status = ocfs2_figure_insert_type(inode, et, &last_eb_bh, &rec,
 					  &free_records, &insert);
 	if (status < 0) {
 		mlog_errno(status);
@@ -4145,7 +4442,7 @@
 	     free_records, insert.ins_tree_depth);
 
 	if (insert.ins_contig == CONTIG_NONE && free_records == 0) {
-		status = ocfs2_grow_tree(inode, handle, fe_bh,
+		status = ocfs2_grow_tree(inode, handle, et,
 					 &insert.ins_tree_depth, &last_eb_bh,
 					 meta_ac);
 		if (status) {
@@ -4155,20 +4452,127 @@
 	}
 
 	/* Finally, we can add clusters. This might rotate the tree for us. */
-	status = ocfs2_do_insert_extent(inode, handle, fe_bh, &rec, &insert);
+	status = ocfs2_do_insert_extent(inode, handle, et, &rec, &insert);
 	if (status < 0)
 		mlog_errno(status);
-	else
+	else if (et->et_ops == &ocfs2_dinode_et_ops)
 		ocfs2_extent_map_insert_rec(inode, &rec);
 
 bail:
-	if (last_eb_bh)
-		brelse(last_eb_bh);
+	brelse(last_eb_bh);
 
 	mlog_exit(status);
 	return status;
 }
 
+/*
+ * Allcate and add clusters into the extent b-tree.
+ * The new clusters(clusters_to_add) will be inserted at logical_offset.
+ * The extent b-tree's root is specified by et, and
+ * it is not limited to the file storage. Any extent tree can use this
+ * function if it implements the proper ocfs2_extent_tree.
+ */
+int ocfs2_add_clusters_in_btree(struct ocfs2_super *osb,
+				struct inode *inode,
+				u32 *logical_offset,
+				u32 clusters_to_add,
+				int mark_unwritten,
+				struct ocfs2_extent_tree *et,
+				handle_t *handle,
+				struct ocfs2_alloc_context *data_ac,
+				struct ocfs2_alloc_context *meta_ac,
+				enum ocfs2_alloc_restarted *reason_ret)
+{
+	int status = 0;
+	int free_extents;
+	enum ocfs2_alloc_restarted reason = RESTART_NONE;
+	u32 bit_off, num_bits;
+	u64 block;
+	u8 flags = 0;
+
+	BUG_ON(!clusters_to_add);
+
+	if (mark_unwritten)
+		flags = OCFS2_EXT_UNWRITTEN;
+
+	free_extents = ocfs2_num_free_extents(osb, inode, et);
+	if (free_extents < 0) {
+		status = free_extents;
+		mlog_errno(status);
+		goto leave;
+	}
+
+	/* there are two cases which could cause us to EAGAIN in the
+	 * we-need-more-metadata case:
+	 * 1) we haven't reserved *any*
+	 * 2) we are so fragmented, we've needed to add metadata too
+	 *    many times. */
+	if (!free_extents && !meta_ac) {
+		mlog(0, "we haven't reserved any metadata!\n");
+		status = -EAGAIN;
+		reason = RESTART_META;
+		goto leave;
+	} else if ((!free_extents)
+		   && (ocfs2_alloc_context_bits_left(meta_ac)
+		       < ocfs2_extend_meta_needed(et->et_root_el))) {
+		mlog(0, "filesystem is really fragmented...\n");
+		status = -EAGAIN;
+		reason = RESTART_META;
+		goto leave;
+	}
+
+	status = __ocfs2_claim_clusters(osb, handle, data_ac, 1,
+					clusters_to_add, &bit_off, &num_bits);
+	if (status < 0) {
+		if (status != -ENOSPC)
+			mlog_errno(status);
+		goto leave;
+	}
+
+	BUG_ON(num_bits > clusters_to_add);
+
+	/* reserve our write early -- insert_extent may update the inode */
+	status = ocfs2_journal_access(handle, inode, et->et_root_bh,
+				      OCFS2_JOURNAL_ACCESS_WRITE);
+	if (status < 0) {
+		mlog_errno(status);
+		goto leave;
+	}
+
+	block = ocfs2_clusters_to_blocks(osb->sb, bit_off);
+	mlog(0, "Allocating %u clusters at block %u for inode %llu\n",
+	     num_bits, bit_off, (unsigned long long)OCFS2_I(inode)->ip_blkno);
+	status = ocfs2_insert_extent(osb, handle, inode, et,
+				     *logical_offset, block,
+				     num_bits, flags, meta_ac);
+	if (status < 0) {
+		mlog_errno(status);
+		goto leave;
+	}
+
+	status = ocfs2_journal_dirty(handle, et->et_root_bh);
+	if (status < 0) {
+		mlog_errno(status);
+		goto leave;
+	}
+
+	clusters_to_add -= num_bits;
+	*logical_offset += num_bits;
+
+	if (clusters_to_add) {
+		mlog(0, "need to alloc once more, wanted = %u\n",
+		     clusters_to_add);
+		status = -EAGAIN;
+		reason = RESTART_TRANS;
+	}
+
+leave:
+	mlog_exit(status);
+	if (reason_ret)
+		*reason_ret = reason;
+	return status;
+}
+
 static void ocfs2_make_right_split_rec(struct super_block *sb,
 				       struct ocfs2_extent_rec *split_rec,
 				       u32 cpos,
@@ -4192,7 +4596,7 @@
 static int ocfs2_split_and_insert(struct inode *inode,
 				  handle_t *handle,
 				  struct ocfs2_path *path,
-				  struct buffer_head *di_bh,
+				  struct ocfs2_extent_tree *et,
 				  struct buffer_head **last_eb_bh,
 				  int split_index,
 				  struct ocfs2_extent_rec *orig_split_rec,
@@ -4206,7 +4610,6 @@
 	struct ocfs2_extent_rec split_rec = *orig_split_rec;
 	struct ocfs2_insert_type insert;
 	struct ocfs2_extent_block *eb;
-	struct ocfs2_dinode *di;
 
 leftright:
 	/*
@@ -4215,8 +4618,7 @@
 	 */
 	rec = path_leaf_el(path)->l_recs[split_index];
 
-	di = (struct ocfs2_dinode *)di_bh->b_data;
-	rightmost_el = &di->id2.i_list;
+	rightmost_el = et->et_root_el;
 
 	depth = le16_to_cpu(rightmost_el->l_tree_depth);
 	if (depth) {
@@ -4227,8 +4629,8 @@
 
 	if (le16_to_cpu(rightmost_el->l_next_free_rec) ==
 	    le16_to_cpu(rightmost_el->l_count)) {
-		ret = ocfs2_grow_tree(inode, handle, di_bh, &depth, last_eb_bh,
-				      meta_ac);
+		ret = ocfs2_grow_tree(inode, handle, et,
+				      &depth, last_eb_bh, meta_ac);
 		if (ret) {
 			mlog_errno(ret);
 			goto out;
@@ -4265,8 +4667,7 @@
 		do_leftright = 1;
 	}
 
-	ret = ocfs2_do_insert_extent(inode, handle, di_bh, &split_rec,
-				     &insert);
+	ret = ocfs2_do_insert_extent(inode, handle, et, &split_rec, &insert);
 	if (ret) {
 		mlog_errno(ret);
 		goto out;
@@ -4308,8 +4709,9 @@
  * of the tree is required. All other cases will degrade into a less
  * optimal tree layout.
  *
- * last_eb_bh should be the rightmost leaf block for any inode with a
- * btree. Since a split may grow the tree or a merge might shrink it, the caller cannot trust the contents of that buffer after this call.
+ * last_eb_bh should be the rightmost leaf block for any extent
+ * btree. Since a split may grow the tree or a merge might shrink it,
+ * the caller cannot trust the contents of that buffer after this call.
  *
  * This code is optimized for readability - several passes might be
  * made over certain portions of the tree. All of those blocks will
@@ -4317,7 +4719,7 @@
  * extra overhead is not expressed in terms of disk reads.
  */
 static int __ocfs2_mark_extent_written(struct inode *inode,
-				       struct buffer_head *di_bh,
+				       struct ocfs2_extent_tree *et,
 				       handle_t *handle,
 				       struct ocfs2_path *path,
 				       int split_index,
@@ -4357,11 +4759,9 @@
 	 */
 	if (path->p_tree_depth) {
 		struct ocfs2_extent_block *eb;
-		struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data;
 
-		ret = ocfs2_read_block(OCFS2_SB(inode->i_sb),
-				       le64_to_cpu(di->i_last_eb_blk),
-				       &last_eb_bh, OCFS2_BH_CACHED, inode);
+		ret = ocfs2_read_block(inode, ocfs2_et_get_last_eb_blk(et),
+				       &last_eb_bh);
 		if (ret) {
 			mlog_exit(ret);
 			goto out;
@@ -4394,7 +4794,7 @@
 		if (ctxt.c_split_covers_rec)
 			el->l_recs[split_index] = *split_rec;
 		else
-			ret = ocfs2_split_and_insert(inode, handle, path, di_bh,
+			ret = ocfs2_split_and_insert(inode, handle, path, et,
 						     &last_eb_bh, split_index,
 						     split_rec, meta_ac);
 		if (ret)
@@ -4402,7 +4802,7 @@
 	} else {
 		ret = ocfs2_try_to_merge_extent(inode, handle, path,
 						split_index, split_rec,
-						dealloc, &ctxt);
+						dealloc, &ctxt, et);
 		if (ret)
 			mlog_errno(ret);
 	}
@@ -4420,7 +4820,8 @@
  *
  * The caller is responsible for passing down meta_ac if we'll need it.
  */
-int ocfs2_mark_extent_written(struct inode *inode, struct buffer_head *di_bh,
+int ocfs2_mark_extent_written(struct inode *inode,
+			      struct ocfs2_extent_tree *et,
 			      handle_t *handle, u32 cpos, u32 len, u32 phys,
 			      struct ocfs2_alloc_context *meta_ac,
 			      struct ocfs2_cached_dealloc_ctxt *dealloc)
@@ -4446,10 +4847,14 @@
 	/*
 	 * XXX: This should be fixed up so that we just re-insert the
 	 * next extent records.
+	 *
+	 * XXX: This is a hack on the extent tree, maybe it should be
+	 * an op?
 	 */
-	ocfs2_extent_map_trunc(inode, 0);
+	if (et->et_ops == &ocfs2_dinode_et_ops)
+		ocfs2_extent_map_trunc(inode, 0);
 
-	left_path = ocfs2_new_inode_path(di_bh);
+	left_path = ocfs2_new_path(et->et_root_bh, et->et_root_el);
 	if (!left_path) {
 		ret = -ENOMEM;
 		mlog_errno(ret);
@@ -4480,8 +4885,9 @@
 	split_rec.e_flags = path_leaf_el(left_path)->l_recs[index].e_flags;
 	split_rec.e_flags &= ~OCFS2_EXT_UNWRITTEN;
 
-	ret = __ocfs2_mark_extent_written(inode, di_bh, handle, left_path,
-					  index, &split_rec, meta_ac, dealloc);
+	ret = __ocfs2_mark_extent_written(inode, et, handle, left_path,
+					  index, &split_rec, meta_ac,
+					  dealloc);
 	if (ret)
 		mlog_errno(ret);
 
@@ -4490,13 +4896,12 @@
 	return ret;
 }
 
-static int ocfs2_split_tree(struct inode *inode, struct buffer_head *di_bh,
+static int ocfs2_split_tree(struct inode *inode, struct ocfs2_extent_tree *et,
 			    handle_t *handle, struct ocfs2_path *path,
 			    int index, u32 new_range,
 			    struct ocfs2_alloc_context *meta_ac)
 {
 	int ret, depth, credits = handle->h_buffer_credits;
-	struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data;
 	struct buffer_head *last_eb_bh = NULL;
 	struct ocfs2_extent_block *eb;
 	struct ocfs2_extent_list *rightmost_el, *el;
@@ -4513,9 +4918,8 @@
 
 	depth = path->p_tree_depth;
 	if (depth > 0) {
-		ret = ocfs2_read_block(OCFS2_SB(inode->i_sb),
-				       le64_to_cpu(di->i_last_eb_blk),
-				       &last_eb_bh, OCFS2_BH_CACHED, inode);
+		ret = ocfs2_read_block(inode, ocfs2_et_get_last_eb_blk(et),
+				       &last_eb_bh);
 		if (ret < 0) {
 			mlog_errno(ret);
 			goto out;
@@ -4526,7 +4930,8 @@
 	} else
 		rightmost_el = path_leaf_el(path);
 
-	credits += path->p_tree_depth + ocfs2_extend_meta_needed(di);
+	credits += path->p_tree_depth +
+		   ocfs2_extend_meta_needed(et->et_root_el);
 	ret = ocfs2_extend_trans(handle, credits);
 	if (ret) {
 		mlog_errno(ret);
@@ -4535,7 +4940,7 @@
 
 	if (le16_to_cpu(rightmost_el->l_next_free_rec) ==
 	    le16_to_cpu(rightmost_el->l_count)) {
-		ret = ocfs2_grow_tree(inode, handle, di_bh, &depth, &last_eb_bh,
+		ret = ocfs2_grow_tree(inode, handle, et, &depth, &last_eb_bh,
 				      meta_ac);
 		if (ret) {
 			mlog_errno(ret);
@@ -4549,7 +4954,7 @@
 	insert.ins_split = SPLIT_RIGHT;
 	insert.ins_tree_depth = depth;
 
-	ret = ocfs2_do_insert_extent(inode, handle, di_bh, &split_rec, &insert);
+	ret = ocfs2_do_insert_extent(inode, handle, et, &split_rec, &insert);
 	if (ret)
 		mlog_errno(ret);
 
@@ -4561,7 +4966,8 @@
 static int ocfs2_truncate_rec(struct inode *inode, handle_t *handle,
 			      struct ocfs2_path *path, int index,
 			      struct ocfs2_cached_dealloc_ctxt *dealloc,
-			      u32 cpos, u32 len)
+			      u32 cpos, u32 len,
+			      struct ocfs2_extent_tree *et)
 {
 	int ret;
 	u32 left_cpos, rec_range, trunc_range;
@@ -4573,7 +4979,7 @@
 	struct ocfs2_extent_block *eb;
 
 	if (ocfs2_is_empty_extent(&el->l_recs[0]) && index > 0) {
-		ret = ocfs2_rotate_tree_left(inode, handle, path, dealloc);
+		ret = ocfs2_rotate_tree_left(inode, handle, path, dealloc, et);
 		if (ret) {
 			mlog_errno(ret);
 			goto out;
@@ -4704,7 +5110,7 @@
 
 	ocfs2_journal_dirty(handle, path_leaf_bh(path));
 
-	ret = ocfs2_rotate_tree_left(inode, handle, path, dealloc);
+	ret = ocfs2_rotate_tree_left(inode, handle, path, dealloc, et);
 	if (ret) {
 		mlog_errno(ret);
 		goto out;
@@ -4715,7 +5121,8 @@
 	return ret;
 }
 
-int ocfs2_remove_extent(struct inode *inode, struct buffer_head *di_bh,
+int ocfs2_remove_extent(struct inode *inode,
+			struct ocfs2_extent_tree *et,
 			u32 cpos, u32 len, handle_t *handle,
 			struct ocfs2_alloc_context *meta_ac,
 			struct ocfs2_cached_dealloc_ctxt *dealloc)
@@ -4724,11 +5131,11 @@
 	u32 rec_range, trunc_range;
 	struct ocfs2_extent_rec *rec;
 	struct ocfs2_extent_list *el;
-	struct ocfs2_path *path;
+	struct ocfs2_path *path = NULL;
 
 	ocfs2_extent_map_trunc(inode, 0);
 
-	path = ocfs2_new_inode_path(di_bh);
+	path = ocfs2_new_path(et->et_root_bh, et->et_root_el);
 	if (!path) {
 		ret = -ENOMEM;
 		mlog_errno(ret);
@@ -4781,13 +5188,13 @@
 
 	if (le32_to_cpu(rec->e_cpos) == cpos || rec_range == trunc_range) {
 		ret = ocfs2_truncate_rec(inode, handle, path, index, dealloc,
-					 cpos, len);
+					 cpos, len, et);
 		if (ret) {
 			mlog_errno(ret);
 			goto out;
 		}
 	} else {
-		ret = ocfs2_split_tree(inode, di_bh, handle, path, index,
+		ret = ocfs2_split_tree(inode, et, handle, path, index,
 				       trunc_range, meta_ac);
 		if (ret) {
 			mlog_errno(ret);
@@ -4836,7 +5243,7 @@
 		}
 
 		ret = ocfs2_truncate_rec(inode, handle, path, index, dealloc,
-					 cpos, len);
+					 cpos, len, et);
 		if (ret) {
 			mlog_errno(ret);
 			goto out;
@@ -5179,8 +5586,7 @@
 		goto bail;
 	}
 
-	status = ocfs2_read_block(osb, OCFS2_I(inode)->ip_blkno, &bh,
-				  OCFS2_BH_CACHED, inode);
+	status = ocfs2_read_block(inode, OCFS2_I(inode)->ip_blkno, &bh);
 	if (status < 0) {
 		iput(inode);
 		mlog_errno(status);
@@ -5255,8 +5661,7 @@
 bail:
 	if (tl_inode)
 		iput(tl_inode);
-	if (tl_bh)
-		brelse(tl_bh);
+	brelse(tl_bh);
 
 	if (status < 0 && (*tl_copy)) {
 		kfree(*tl_copy);
@@ -5999,20 +6404,13 @@
 	return status;
 }
 
-static int ocfs2_writeback_zero_func(handle_t *handle, struct buffer_head *bh)
+static int ocfs2_zero_func(handle_t *handle, struct buffer_head *bh)
 {
 	set_buffer_uptodate(bh);
 	mark_buffer_dirty(bh);
 	return 0;
 }
 
-static int ocfs2_ordered_zero_func(handle_t *handle, struct buffer_head *bh)
-{
-	set_buffer_uptodate(bh);
-	mark_buffer_dirty(bh);
-	return ocfs2_journal_dirty_data(handle, bh);
-}
-
 static void ocfs2_map_and_dirty_page(struct inode *inode, handle_t *handle,
 				     unsigned int from, unsigned int to,
 				     struct page *page, int zero, u64 *phys)
@@ -6031,17 +6429,18 @@
 	 * here if they aren't - ocfs2_map_page_blocks()
 	 * might've skipped some
 	 */
-	if (ocfs2_should_order_data(inode)) {
-		ret = walk_page_buffers(handle,
-					page_buffers(page),
-					from, to, &partial,
-					ocfs2_ordered_zero_func);
-		if (ret < 0)
-			mlog_errno(ret);
-	} else {
+	ret = walk_page_buffers(handle, page_buffers(page),
+				from, to, &partial,
+				ocfs2_zero_func);
+	if (ret < 0)
+		mlog_errno(ret);
+	else if (ocfs2_should_order_data(inode)) {
+		ret = ocfs2_jbd2_file_inode(handle, inode);
+#ifdef CONFIG_OCFS2_COMPAT_JBD
 		ret = walk_page_buffers(handle, page_buffers(page),
 					from, to, &partial,
-					ocfs2_writeback_zero_func);
+					ocfs2_journal_dirty_data);
+#endif
 		if (ret < 0)
 			mlog_errno(ret);
 	}
@@ -6206,20 +6605,29 @@
 	return ret;
 }
 
-static void ocfs2_zero_dinode_id2(struct inode *inode, struct ocfs2_dinode *di)
+static void ocfs2_zero_dinode_id2_with_xattr(struct inode *inode,
+					     struct ocfs2_dinode *di)
 {
 	unsigned int blocksize = 1 << inode->i_sb->s_blocksize_bits;
+	unsigned int xattrsize = le16_to_cpu(di->i_xattr_inline_size);
 
-	memset(&di->id2, 0, blocksize - offsetof(struct ocfs2_dinode, id2));
+	if (le16_to_cpu(di->i_dyn_features) & OCFS2_INLINE_XATTR_FL)
+		memset(&di->id2, 0, blocksize -
+				    offsetof(struct ocfs2_dinode, id2) -
+				    xattrsize);
+	else
+		memset(&di->id2, 0, blocksize -
+				    offsetof(struct ocfs2_dinode, id2));
 }
 
 void ocfs2_dinode_new_extent_list(struct inode *inode,
 				  struct ocfs2_dinode *di)
 {
-	ocfs2_zero_dinode_id2(inode, di);
+	ocfs2_zero_dinode_id2_with_xattr(inode, di);
 	di->id2.i_list.l_tree_depth = 0;
 	di->id2.i_list.l_next_free_rec = 0;
-	di->id2.i_list.l_count = cpu_to_le16(ocfs2_extent_recs_per_inode(inode->i_sb));
+	di->id2.i_list.l_count = cpu_to_le16(
+		ocfs2_extent_recs_per_inode_with_xattr(inode->i_sb, di));
 }
 
 void ocfs2_set_inode_data_inline(struct inode *inode, struct ocfs2_dinode *di)
@@ -6236,9 +6644,10 @@
 	 * We clear the entire i_data structure here so that all
 	 * fields can be properly initialized.
 	 */
-	ocfs2_zero_dinode_id2(inode, di);
+	ocfs2_zero_dinode_id2_with_xattr(inode, di);
 
-	idata->id_count = cpu_to_le16(ocfs2_max_inline_data(inode->i_sb));
+	idata->id_count = cpu_to_le16(
+			ocfs2_max_inline_data_with_xattr(inode->i_sb, di));
 }
 
 int ocfs2_convert_inline_data_to_extents(struct inode *inode,
@@ -6253,6 +6662,7 @@
 	struct ocfs2_alloc_context *data_ac = NULL;
 	struct page **pages = NULL;
 	loff_t end = osb->s_clustersize;
+	struct ocfs2_extent_tree et;
 
 	has_data = i_size_read(inode) ? 1 : 0;
 
@@ -6352,7 +6762,8 @@
 		 * this proves to be false, we could always re-build
 		 * the in-inode data from our pages.
 		 */
-		ret = ocfs2_insert_extent(osb, handle, inode, di_bh,
+		ocfs2_init_dinode_extent_tree(&et, inode, di_bh);
+		ret = ocfs2_insert_extent(osb, handle, inode, &et,
 					  0, block, 1, 0, NULL);
 		if (ret) {
 			mlog_errno(ret);
@@ -6395,13 +6806,14 @@
 	handle_t *handle = NULL;
 	struct inode *tl_inode = osb->osb_tl_inode;
 	struct ocfs2_path *path = NULL;
+	struct ocfs2_dinode *di = (struct ocfs2_dinode *)fe_bh->b_data;
 
 	mlog_entry_void();
 
 	new_highest_cpos = ocfs2_clusters_for_bytes(osb->sb,
 						     i_size_read(inode));
 
-	path = ocfs2_new_inode_path(fe_bh);
+	path = ocfs2_new_path(fe_bh, &di->id2.i_list);
 	if (!path) {
 		status = -ENOMEM;
 		mlog_errno(status);
@@ -6572,8 +6984,8 @@
 	ocfs2_init_dealloc_ctxt(&(*tc)->tc_dealloc);
 
 	if (fe->id2.i_list.l_tree_depth) {
-		status = ocfs2_read_block(osb, le64_to_cpu(fe->i_last_eb_blk),
-					  &last_eb_bh, OCFS2_BH_CACHED, inode);
+		status = ocfs2_read_block(inode, le64_to_cpu(fe->i_last_eb_blk),
+					  &last_eb_bh);
 		if (status < 0) {
 			mlog_errno(status);
 			goto bail;
@@ -6686,8 +7098,7 @@
 		mlog(ML_NOTICE,
 		     "Truncate completion has non-empty dealloc context\n");
 
-	if (tc->tc_last_eb_bh)
-		brelse(tc->tc_last_eb_bh);
+	brelse(tc->tc_last_eb_bh);
 
 	kfree(tc);
 }
diff --git a/fs/ocfs2/alloc.h b/fs/ocfs2/alloc.h
index 60cd3d5..70257c8 100644
--- a/fs/ocfs2/alloc.h
+++ b/fs/ocfs2/alloc.h
@@ -26,30 +26,102 @@
 #ifndef OCFS2_ALLOC_H
 #define OCFS2_ALLOC_H
 
+
+/*
+ * For xattr tree leaf, we limit the leaf byte size to be 64K.
+ */
+#define OCFS2_MAX_XATTR_TREE_LEAF_SIZE 65536
+
+/*
+ * ocfs2_extent_tree and ocfs2_extent_tree_operations are used to abstract
+ * the b-tree operations in ocfs2. Now all the b-tree operations are not
+ * limited to ocfs2_dinode only. Any data which need to allocate clusters
+ * to store can use b-tree. And it only needs to implement its ocfs2_extent_tree
+ * and operation.
+ *
+ * ocfs2_extent_tree becomes the first-class object for extent tree
+ * manipulation.  Callers of the alloc.c code need to fill it via one of
+ * the ocfs2_init_*_extent_tree() operations below.
+ *
+ * ocfs2_extent_tree contains info for the root of the b-tree, it must have a
+ * root ocfs2_extent_list and a root_bh so that they can be used in the b-tree
+ * functions.
+ * ocfs2_extent_tree_operations abstract the normal operations we do for
+ * the root of extent b-tree.
+ */
+struct ocfs2_extent_tree_operations;
+struct ocfs2_extent_tree {
+	struct ocfs2_extent_tree_operations	*et_ops;
+	struct buffer_head			*et_root_bh;
+	struct ocfs2_extent_list		*et_root_el;
+	void					*et_object;
+	unsigned int				et_max_leaf_clusters;
+};
+
+/*
+ * ocfs2_init_*_extent_tree() will fill an ocfs2_extent_tree from the
+ * specified object buffer.
+ */
+void ocfs2_init_dinode_extent_tree(struct ocfs2_extent_tree *et,
+				   struct inode *inode,
+				   struct buffer_head *bh);
+void ocfs2_init_xattr_tree_extent_tree(struct ocfs2_extent_tree *et,
+				       struct inode *inode,
+				       struct buffer_head *bh);
+void ocfs2_init_xattr_value_extent_tree(struct ocfs2_extent_tree *et,
+					struct inode *inode,
+					struct buffer_head *bh,
+					struct ocfs2_xattr_value_root *xv);
+
 struct ocfs2_alloc_context;
 int ocfs2_insert_extent(struct ocfs2_super *osb,
 			handle_t *handle,
 			struct inode *inode,
-			struct buffer_head *fe_bh,
+			struct ocfs2_extent_tree *et,
 			u32 cpos,
 			u64 start_blk,
 			u32 new_clusters,
 			u8 flags,
 			struct ocfs2_alloc_context *meta_ac);
+
+enum ocfs2_alloc_restarted {
+	RESTART_NONE = 0,
+	RESTART_TRANS,
+	RESTART_META
+};
+int ocfs2_add_clusters_in_btree(struct ocfs2_super *osb,
+				struct inode *inode,
+				u32 *logical_offset,
+				u32 clusters_to_add,
+				int mark_unwritten,
+				struct ocfs2_extent_tree *et,
+				handle_t *handle,
+				struct ocfs2_alloc_context *data_ac,
+				struct ocfs2_alloc_context *meta_ac,
+				enum ocfs2_alloc_restarted *reason_ret);
 struct ocfs2_cached_dealloc_ctxt;
-int ocfs2_mark_extent_written(struct inode *inode, struct buffer_head *di_bh,
+int ocfs2_mark_extent_written(struct inode *inode,
+			      struct ocfs2_extent_tree *et,
 			      handle_t *handle, u32 cpos, u32 len, u32 phys,
 			      struct ocfs2_alloc_context *meta_ac,
 			      struct ocfs2_cached_dealloc_ctxt *dealloc);
-int ocfs2_remove_extent(struct inode *inode, struct buffer_head *di_bh,
+int ocfs2_remove_extent(struct inode *inode,
+			struct ocfs2_extent_tree *et,
 			u32 cpos, u32 len, handle_t *handle,
 			struct ocfs2_alloc_context *meta_ac,
 			struct ocfs2_cached_dealloc_ctxt *dealloc);
 int ocfs2_num_free_extents(struct ocfs2_super *osb,
 			   struct inode *inode,
-			   struct ocfs2_dinode *fe);
-/* how many new metadata chunks would an allocation need at maximum? */
-static inline int ocfs2_extend_meta_needed(struct ocfs2_dinode *fe)
+			   struct ocfs2_extent_tree *et);
+
+/*
+ * how many new metadata chunks would an allocation need at maximum?
+ *
+ * Please note that the caller must make sure that root_el is the root
+ * of extent tree. So for an inode, it should be &fe->id2.i_list. Otherwise
+ * the result may be wrong.
+ */
+static inline int ocfs2_extend_meta_needed(struct ocfs2_extent_list *root_el)
 {
 	/*
 	 * Rather than do all the work of determining how much we need
@@ -59,7 +131,7 @@
 	 * new tree_depth==0 extent_block, and one block at the new
 	 * top-of-the tree.
 	 */
-	return le16_to_cpu(fe->id2.i_list.l_tree_depth) + 2;
+	return le16_to_cpu(root_el->l_tree_depth) + 2;
 }
 
 void ocfs2_dinode_new_extent_list(struct inode *inode, struct ocfs2_dinode *di);
diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c
index a53da14..c22543b 100644
--- a/fs/ocfs2/aops.c
+++ b/fs/ocfs2/aops.c
@@ -68,9 +68,7 @@
 		goto bail;
 	}
 
-	status = ocfs2_read_block(OCFS2_SB(inode->i_sb),
-				  OCFS2_I(inode)->ip_blkno,
-				  &bh, OCFS2_BH_CACHED, inode);
+	status = ocfs2_read_block(inode, OCFS2_I(inode)->ip_blkno, &bh);
 	if (status < 0) {
 		mlog_errno(status);
 		goto bail;
@@ -128,8 +126,7 @@
 	err = 0;
 
 bail:
-	if (bh)
-		brelse(bh);
+	brelse(bh);
 
 	mlog_exit(err);
 	return err;
@@ -261,13 +258,11 @@
 {
 	int ret;
 	struct buffer_head *di_bh = NULL;
-	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
 
 	BUG_ON(!PageLocked(page));
 	BUG_ON(!(OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_DATA_FL));
 
-	ret = ocfs2_read_block(osb, OCFS2_I(inode)->ip_blkno, &di_bh,
-			       OCFS2_BH_CACHED, inode);
+	ret = ocfs2_read_block(inode, OCFS2_I(inode)->ip_blkno, &di_bh);
 	if (ret) {
 		mlog_errno(ret);
 		goto out;
@@ -485,11 +480,14 @@
 	}
 
 	if (ocfs2_should_order_data(inode)) {
+		ret = ocfs2_jbd2_file_inode(handle, inode);
+#ifdef CONFIG_OCFS2_COMPAT_JBD
 		ret = walk_page_buffers(handle,
 					page_buffers(page),
 					from, to, NULL,
 					ocfs2_journal_dirty_data);
-		if (ret < 0) 
+#endif
+		if (ret < 0)
 			mlog_errno(ret);
 	}
 out:
@@ -669,7 +667,7 @@
 {
 	journal_t *journal = OCFS2_SB(page->mapping->host->i_sb)->journal->j_journal;
 
-	journal_invalidatepage(journal, page, offset);
+	jbd2_journal_invalidatepage(journal, page, offset);
 }
 
 static int ocfs2_releasepage(struct page *page, gfp_t wait)
@@ -678,7 +676,7 @@
 
 	if (!page_has_buffers(page))
 		return 0;
-	return journal_try_to_free_buffers(journal, page, wait);
+	return jbd2_journal_try_to_free_buffers(journal, page, wait);
 }
 
 static ssize_t ocfs2_direct_IO(int rw,
@@ -1074,11 +1072,15 @@
 		tmppage = wc->w_pages[i];
 
 		if (page_has_buffers(tmppage)) {
-			if (ocfs2_should_order_data(inode))
+			if (ocfs2_should_order_data(inode)) {
+				ocfs2_jbd2_file_inode(wc->w_handle, inode);
+#ifdef CONFIG_OCFS2_COMPAT_JBD
 				walk_page_buffers(wc->w_handle,
 						  page_buffers(tmppage),
 						  from, to, NULL,
 						  ocfs2_journal_dirty_data);
+#endif
+			}
 
 			block_commit_write(tmppage, from, to);
 		}
@@ -1242,6 +1244,7 @@
 	int ret, i, new, should_zero = 0;
 	u64 v_blkno, p_blkno;
 	struct inode *inode = mapping->host;
+	struct ocfs2_extent_tree et;
 
 	new = phys == 0 ? 1 : 0;
 	if (new || unwritten)
@@ -1255,10 +1258,10 @@
 		 * any additional semaphores or cluster locks.
 		 */
 		tmp_pos = cpos;
-		ret = ocfs2_do_extend_allocation(OCFS2_SB(inode->i_sb), inode,
-						 &tmp_pos, 1, 0, wc->w_di_bh,
-						 wc->w_handle, data_ac,
-						 meta_ac, NULL);
+		ret = ocfs2_add_inode_data(OCFS2_SB(inode->i_sb), inode,
+					   &tmp_pos, 1, 0, wc->w_di_bh,
+					   wc->w_handle, data_ac,
+					   meta_ac, NULL);
 		/*
 		 * This shouldn't happen because we must have already
 		 * calculated the correct meta data allocation required. The
@@ -1276,7 +1279,8 @@
 			goto out;
 		}
 	} else if (unwritten) {
-		ret = ocfs2_mark_extent_written(inode, wc->w_di_bh,
+		ocfs2_init_dinode_extent_tree(&et, inode, wc->w_di_bh);
+		ret = ocfs2_mark_extent_written(inode, &et,
 						wc->w_handle, cpos, 1, phys,
 						meta_ac, &wc->w_dealloc);
 		if (ret < 0) {
@@ -1665,6 +1669,7 @@
 	struct ocfs2_alloc_context *data_ac = NULL;
 	struct ocfs2_alloc_context *meta_ac = NULL;
 	handle_t *handle;
+	struct ocfs2_extent_tree et;
 
 	ret = ocfs2_alloc_write_ctxt(&wc, osb, pos, len, di_bh);
 	if (ret) {
@@ -1712,14 +1717,23 @@
 		 * ocfs2_lock_allocators(). It greatly over-estimates
 		 * the work to be done.
 		 */
-		ret = ocfs2_lock_allocators(inode, di, clusters_to_alloc,
-					    extents_to_split, &data_ac, &meta_ac);
+		mlog(0, "extend inode %llu, i_size = %lld, di->i_clusters = %u,"
+		     " clusters_to_add = %u, extents_to_split = %u\n",
+		     (unsigned long long)OCFS2_I(inode)->ip_blkno,
+		     (long long)i_size_read(inode), le32_to_cpu(di->i_clusters),
+		     clusters_to_alloc, extents_to_split);
+
+		ocfs2_init_dinode_extent_tree(&et, inode, wc->w_di_bh);
+		ret = ocfs2_lock_allocators(inode, &et,
+					    clusters_to_alloc, extents_to_split,
+					    &data_ac, &meta_ac);
 		if (ret) {
 			mlog_errno(ret);
 			goto out;
 		}
 
-		credits = ocfs2_calc_extend_credits(inode->i_sb, di,
+		credits = ocfs2_calc_extend_credits(inode->i_sb,
+						    &di->id2.i_list,
 						    clusters_to_alloc);
 
 	}
@@ -1905,11 +1919,15 @@
 		}
 
 		if (page_has_buffers(tmppage)) {
-			if (ocfs2_should_order_data(inode))
+			if (ocfs2_should_order_data(inode)) {
+				ocfs2_jbd2_file_inode(wc->w_handle, inode);
+#ifdef CONFIG_OCFS2_COMPAT_JBD
 				walk_page_buffers(wc->w_handle,
 						  page_buffers(tmppage),
 						  from, to, NULL,
 						  ocfs2_journal_dirty_data);
+#endif
+			}
 			block_commit_write(tmppage, from, to);
 		}
 	}
diff --git a/fs/ocfs2/buffer_head_io.c b/fs/ocfs2/buffer_head_io.c
index f136639..7e947c6 100644
--- a/fs/ocfs2/buffer_head_io.c
+++ b/fs/ocfs2/buffer_head_io.c
@@ -66,7 +66,7 @@
 	/* remove from dirty list before I/O. */
 	clear_buffer_dirty(bh);
 
-	get_bh(bh); /* for end_buffer_write_sync() */                   
+	get_bh(bh); /* for end_buffer_write_sync() */
 	bh->b_end_io = end_buffer_write_sync;
 	submit_bh(WRITE, bh);
 
@@ -88,22 +88,103 @@
 	return ret;
 }
 
-int ocfs2_read_blocks(struct ocfs2_super *osb, u64 block, int nr,
-		      struct buffer_head *bhs[], int flags,
-		      struct inode *inode)
+int ocfs2_read_blocks_sync(struct ocfs2_super *osb, u64 block,
+			   unsigned int nr, struct buffer_head *bhs[])
 {
 	int status = 0;
-	struct super_block *sb;
+	unsigned int i;
+	struct buffer_head *bh;
+
+	if (!nr) {
+		mlog(ML_BH_IO, "No buffers will be read!\n");
+		goto bail;
+	}
+
+	for (i = 0 ; i < nr ; i++) {
+		if (bhs[i] == NULL) {
+			bhs[i] = sb_getblk(osb->sb, block++);
+			if (bhs[i] == NULL) {
+				status = -EIO;
+				mlog_errno(status);
+				goto bail;
+			}
+		}
+		bh = bhs[i];
+
+		if (buffer_jbd(bh)) {
+			mlog(ML_ERROR,
+			     "trying to sync read a jbd "
+			     "managed bh (blocknr = %llu), skipping\n",
+			     (unsigned long long)bh->b_blocknr);
+			continue;
+		}
+
+		if (buffer_dirty(bh)) {
+			/* This should probably be a BUG, or
+			 * at least return an error. */
+			mlog(ML_ERROR,
+			     "trying to sync read a dirty "
+			     "buffer! (blocknr = %llu), skipping\n",
+			     (unsigned long long)bh->b_blocknr);
+			continue;
+		}
+
+		lock_buffer(bh);
+		if (buffer_jbd(bh)) {
+			mlog(ML_ERROR,
+			     "block %llu had the JBD bit set "
+			     "while I was in lock_buffer!",
+			     (unsigned long long)bh->b_blocknr);
+			BUG();
+		}
+
+		clear_buffer_uptodate(bh);
+		get_bh(bh); /* for end_buffer_read_sync() */
+		bh->b_end_io = end_buffer_read_sync;
+		submit_bh(READ, bh);
+	}
+
+	for (i = nr; i > 0; i--) {
+		bh = bhs[i - 1];
+
+		if (buffer_jbd(bh)) {
+			mlog(ML_ERROR,
+			     "the journal got the buffer while it was "
+			     "locked for io! (blocknr = %llu)\n",
+			     (unsigned long long)bh->b_blocknr);
+			BUG();
+		}
+
+		wait_on_buffer(bh);
+		if (!buffer_uptodate(bh)) {
+			/* Status won't be cleared from here on out,
+			 * so we can safely record this and loop back
+			 * to cleanup the other buffers. */
+			status = -EIO;
+			put_bh(bh);
+			bhs[i - 1] = NULL;
+		}
+	}
+
+bail:
+	return status;
+}
+
+int ocfs2_read_blocks(struct inode *inode, u64 block, int nr,
+		      struct buffer_head *bhs[], int flags)
+{
+	int status = 0;
 	int i, ignore_cache = 0;
 	struct buffer_head *bh;
 
-	mlog_entry("(block=(%llu), nr=(%d), flags=%d, inode=%p)\n",
-		   (unsigned long long)block, nr, flags, inode);
+	mlog_entry("(inode=%p, block=(%llu), nr=(%d), flags=%d)\n",
+		   inode, (unsigned long long)block, nr, flags);
 
+	BUG_ON(!inode);
 	BUG_ON((flags & OCFS2_BH_READAHEAD) &&
-	       (!inode || !(flags & OCFS2_BH_CACHED)));
+	       (flags & OCFS2_BH_IGNORE_CACHE));
 
-	if (osb == NULL || osb->sb == NULL || bhs == NULL) {
+	if (bhs == NULL) {
 		status = -EINVAL;
 		mlog_errno(status);
 		goto bail;
@@ -122,26 +203,19 @@
 		goto bail;
 	}
 
-	sb = osb->sb;
-
-	if (flags & OCFS2_BH_CACHED && !inode)
-		flags &= ~OCFS2_BH_CACHED;
-
-	if (inode)
-		mutex_lock(&OCFS2_I(inode)->ip_io_mutex);
+	mutex_lock(&OCFS2_I(inode)->ip_io_mutex);
 	for (i = 0 ; i < nr ; i++) {
 		if (bhs[i] == NULL) {
-			bhs[i] = sb_getblk(sb, block++);
+			bhs[i] = sb_getblk(inode->i_sb, block++);
 			if (bhs[i] == NULL) {
-				if (inode)
-					mutex_unlock(&OCFS2_I(inode)->ip_io_mutex);
+				mutex_unlock(&OCFS2_I(inode)->ip_io_mutex);
 				status = -EIO;
 				mlog_errno(status);
 				goto bail;
 			}
 		}
 		bh = bhs[i];
-		ignore_cache = 0;
+		ignore_cache = (flags & OCFS2_BH_IGNORE_CACHE);
 
 		/* There are three read-ahead cases here which we need to
 		 * be concerned with. All three assume a buffer has
@@ -167,26 +241,27 @@
 		 *    before our is-it-in-flight check.
 		 */
 
-		if (flags & OCFS2_BH_CACHED &&
-		    !ocfs2_buffer_uptodate(inode, bh)) {
+		if (!ignore_cache && !ocfs2_buffer_uptodate(inode, bh)) {
 			mlog(ML_UPTODATE,
 			     "bh (%llu), inode %llu not uptodate\n",
 			     (unsigned long long)bh->b_blocknr,
 			     (unsigned long long)OCFS2_I(inode)->ip_blkno);
+			/* We're using ignore_cache here to say
+			 * "go to disk" */
 			ignore_cache = 1;
 		}
 
 		/* XXX: Can we ever get this and *not* have the cached
 		 * flag set? */
 		if (buffer_jbd(bh)) {
-			if (!(flags & OCFS2_BH_CACHED) || ignore_cache)
+			if (ignore_cache)
 				mlog(ML_BH_IO, "trying to sync read a jbd "
 					       "managed bh (blocknr = %llu)\n",
 				     (unsigned long long)bh->b_blocknr);
 			continue;
 		}
 
-		if (!(flags & OCFS2_BH_CACHED) || ignore_cache) {
+		if (ignore_cache) {
 			if (buffer_dirty(bh)) {
 				/* This should probably be a BUG, or
 				 * at least return an error. */
@@ -221,7 +296,7 @@
 			 * previously read-ahead buffer may have
 			 * completed I/O while we were waiting for the
 			 * buffer lock. */
-			if ((flags & OCFS2_BH_CACHED)
+			if (!(flags & OCFS2_BH_IGNORE_CACHE)
 			    && !(flags & OCFS2_BH_READAHEAD)
 			    && ocfs2_buffer_uptodate(inode, bh)) {
 				unlock_buffer(bh);
@@ -265,15 +340,14 @@
 		/* Always set the buffer in the cache, even if it was
 		 * a forced read, or read-ahead which hasn't yet
 		 * completed. */
-		if (inode)
-			ocfs2_set_buffer_uptodate(inode, bh);
+		ocfs2_set_buffer_uptodate(inode, bh);
 	}
-	if (inode)
-		mutex_unlock(&OCFS2_I(inode)->ip_io_mutex);
+	mutex_unlock(&OCFS2_I(inode)->ip_io_mutex);
 
 	mlog(ML_BH_IO, "block=(%llu), nr=(%d), cached=%s, flags=0x%x\n", 
 	     (unsigned long long)block, nr,
-	     (!(flags & OCFS2_BH_CACHED) || ignore_cache) ? "no" : "yes", flags);
+	     ((flags & OCFS2_BH_IGNORE_CACHE) || ignore_cache) ? "no" : "yes",
+	     flags);
 
 bail:
 
diff --git a/fs/ocfs2/buffer_head_io.h b/fs/ocfs2/buffer_head_io.h
index c2e7861..75e1dcb 100644
--- a/fs/ocfs2/buffer_head_io.h
+++ b/fs/ocfs2/buffer_head_io.h
@@ -31,31 +31,29 @@
 void ocfs2_end_buffer_io_sync(struct buffer_head *bh,
 			     int uptodate);
 
-static inline int ocfs2_read_block(struct ocfs2_super          *osb,
+static inline int ocfs2_read_block(struct inode	       *inode,
 				   u64                  off,
-				   struct buffer_head **bh,
-				   int                  flags,
-				   struct inode        *inode);
+				   struct buffer_head **bh);
 
 int ocfs2_write_block(struct ocfs2_super          *osb,
 		      struct buffer_head  *bh,
 		      struct inode        *inode);
-int ocfs2_read_blocks(struct ocfs2_super          *osb,
+int ocfs2_read_blocks(struct inode	  *inode,
 		      u64                  block,
 		      int                  nr,
 		      struct buffer_head  *bhs[],
-		      int                  flags,
-		      struct inode        *inode);
+		      int                  flags);
+int ocfs2_read_blocks_sync(struct ocfs2_super *osb, u64 block,
+			   unsigned int nr, struct buffer_head *bhs[]);
 
 int ocfs2_write_super_or_backup(struct ocfs2_super *osb,
 				struct buffer_head *bh);
 
-#define OCFS2_BH_CACHED            1
+#define OCFS2_BH_IGNORE_CACHE      1
 #define OCFS2_BH_READAHEAD         8
 
-static inline int ocfs2_read_block(struct ocfs2_super * osb, u64 off,
-				   struct buffer_head **bh, int flags,
-				   struct inode *inode)
+static inline int ocfs2_read_block(struct inode *inode, u64 off,
+				   struct buffer_head **bh)
 {
 	int status = 0;
 
@@ -65,8 +63,7 @@
 		goto bail;
 	}
 
-	status = ocfs2_read_blocks(osb, off, 1, bh,
-				   flags, inode);
+	status = ocfs2_read_blocks(inode, off, 1, bh, 0);
 
 bail:
 	return status;
diff --git a/fs/ocfs2/cluster/masklog.c b/fs/ocfs2/cluster/masklog.c
index 23c732f..d8a0cb9 100644
--- a/fs/ocfs2/cluster/masklog.c
+++ b/fs/ocfs2/cluster/masklog.c
@@ -109,6 +109,7 @@
 	define_mask(CONN),
 	define_mask(QUORUM),
 	define_mask(EXPORT),
+	define_mask(XATTR),
 	define_mask(ERROR),
 	define_mask(NOTICE),
 	define_mask(KTHREAD),
diff --git a/fs/ocfs2/cluster/masklog.h b/fs/ocfs2/cluster/masklog.h
index 597e064..57670c6 100644
--- a/fs/ocfs2/cluster/masklog.h
+++ b/fs/ocfs2/cluster/masklog.h
@@ -112,6 +112,7 @@
 #define ML_CONN		0x0000000004000000ULL /* net connection management */
 #define ML_QUORUM	0x0000000008000000ULL /* net connection quorum */
 #define ML_EXPORT	0x0000000010000000ULL /* ocfs2 export operations */
+#define ML_XATTR	0x0000000020000000ULL /* ocfs2 extended attributes */
 /* bits that are infrequently given and frequently matched in the high word */
 #define ML_ERROR	0x0000000100000000ULL /* sent to KERN_ERR */
 #define ML_NOTICE	0x0000000200000000ULL /* setn to KERN_NOTICE */
diff --git a/fs/ocfs2/dir.c b/fs/ocfs2/dir.c
index 9cce563..026e6eb 100644
--- a/fs/ocfs2/dir.c
+++ b/fs/ocfs2/dir.c
@@ -82,6 +82,49 @@
 			       struct ocfs2_alloc_context *meta_ac,
 			       struct buffer_head **new_bh);
 
+static struct buffer_head *ocfs2_bread(struct inode *inode,
+				       int block, int *err, int reada)
+{
+	struct buffer_head *bh = NULL;
+	int tmperr;
+	u64 p_blkno;
+	int readflags = 0;
+
+	if (reada)
+		readflags |= OCFS2_BH_READAHEAD;
+
+	if (((u64)block << inode->i_sb->s_blocksize_bits) >=
+	    i_size_read(inode)) {
+		BUG_ON(!reada);
+		return NULL;
+	}
+
+	down_read(&OCFS2_I(inode)->ip_alloc_sem);
+	tmperr = ocfs2_extent_map_get_blocks(inode, block, &p_blkno, NULL,
+					     NULL);
+	up_read(&OCFS2_I(inode)->ip_alloc_sem);
+	if (tmperr < 0) {
+		mlog_errno(tmperr);
+		goto fail;
+	}
+
+	tmperr = ocfs2_read_blocks(inode, p_blkno, 1, &bh, readflags);
+	if (tmperr < 0)
+		goto fail;
+
+	tmperr = 0;
+
+	*err = 0;
+	return bh;
+
+fail:
+	brelse(bh);
+	bh = NULL;
+
+	*err = -EIO;
+	return NULL;
+}
+
 /*
  * bh passed here can be an inode block or a dir data block, depending
  * on the inode inline data flag.
@@ -188,8 +231,7 @@
 	struct ocfs2_dinode *di;
 	struct ocfs2_inline_data *data;
 
-	ret = ocfs2_read_block(OCFS2_SB(dir->i_sb), OCFS2_I(dir)->ip_blkno,
-			       &di_bh, OCFS2_BH_CACHED, dir);
+	ret = ocfs2_read_block(dir, OCFS2_I(dir)->ip_blkno, &di_bh);
 	if (ret) {
 		mlog_errno(ret);
 		goto out;
@@ -260,14 +302,13 @@
 		}
 		if ((bh = bh_use[ra_ptr++]) == NULL)
 			goto next;
-		wait_on_buffer(bh);
-		if (!buffer_uptodate(bh)) {
-			/* read error, skip block & hope for the best */
+		if (ocfs2_read_block(dir, block, &bh)) {
+			/* read error, skip block & hope for the best.
+			 * ocfs2_read_block() has released the bh. */
 			ocfs2_error(dir->i_sb, "reading directory %llu, "
 				    "offset %lu\n",
 				    (unsigned long long)OCFS2_I(dir)->ip_blkno,
 				    block);
-			brelse(bh);
 			goto next;
 		}
 		i = ocfs2_search_dirblock(bh, dir, name, namelen,
@@ -417,8 +458,7 @@
 	struct ocfs2_dinode *di;
 	struct ocfs2_inline_data *data;
 
-	ret = ocfs2_read_block(OCFS2_SB(dir->i_sb), OCFS2_I(dir)->ip_blkno,
-			       &di_bh, OCFS2_BH_CACHED, dir);
+	ret = ocfs2_read_block(dir, OCFS2_I(dir)->ip_blkno, &di_bh);
 	if (ret) {
 		mlog_errno(ret);
 		goto out;
@@ -596,8 +636,7 @@
 	struct ocfs2_inline_data *data;
 	struct ocfs2_dir_entry *de;
 
-	ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), OCFS2_I(inode)->ip_blkno,
-			       &di_bh, OCFS2_BH_CACHED, inode);
+	ret = ocfs2_read_block(inode, OCFS2_I(inode)->ip_blkno, &di_bh);
 	if (ret) {
 		mlog(ML_ERROR, "Unable to read inode block for dir %llu\n",
 		     (unsigned long long)OCFS2_I(inode)->ip_blkno);
@@ -716,8 +755,7 @@
 			for (i = ra_sectors >> (sb->s_blocksize_bits - 9);
 			     i > 0; i--) {
 				tmp = ocfs2_bread(inode, ++blk, &err, 1);
-				if (tmp)
-					brelse(tmp);
+				brelse(tmp);
 			}
 			last_ra_blk = blk;
 			ra_sectors = 8;
@@ -899,10 +937,8 @@
 leave:
 	if (status < 0) {
 		*dirent = NULL;
-		if (*dirent_bh) {
-			brelse(*dirent_bh);
-			*dirent_bh = NULL;
-		}
+		brelse(*dirent_bh);
+		*dirent_bh = NULL;
 	}
 
 	mlog_exit(status);
@@ -951,8 +987,7 @@
 
 	ret = 0;
 bail:
-	if (dirent_bh)
-		brelse(dirent_bh);
+	brelse(dirent_bh);
 
 	mlog_exit(ret);
 	return ret;
@@ -1127,8 +1162,7 @@
 
 	status = 0;
 bail:
-	if (new_bh)
-		brelse(new_bh);
+	brelse(new_bh);
 
 	mlog_exit(status);
 	return status;
@@ -1192,6 +1226,9 @@
 	struct buffer_head *dirdata_bh = NULL;
 	struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data;
 	handle_t *handle;
+	struct ocfs2_extent_tree et;
+
+	ocfs2_init_dinode_extent_tree(&et, dir, di_bh);
 
 	alloc = ocfs2_clusters_for_bytes(sb, bytes);
 
@@ -1305,8 +1342,8 @@
 	 * This should never fail as our extent list is empty and all
 	 * related blocks have been journaled already.
 	 */
-	ret = ocfs2_insert_extent(osb, handle, dir, di_bh, 0, blkno, len, 0,
-				  NULL);
+	ret = ocfs2_insert_extent(osb, handle, dir, &et, 0, blkno, len,
+				  0, NULL);
 	if (ret) {
 		mlog_errno(ret);
 		goto out_commit;
@@ -1337,8 +1374,8 @@
 		}
 		blkno = ocfs2_clusters_to_blocks(dir->i_sb, bit_off);
 
-		ret = ocfs2_insert_extent(osb, handle, dir, di_bh, 1, blkno,
-					  len, 0, NULL);
+		ret = ocfs2_insert_extent(osb, handle, dir, &et, 1,
+					  blkno, len, 0, NULL);
 		if (ret) {
 			mlog_errno(ret);
 			goto out_commit;
@@ -1383,9 +1420,9 @@
 	if (extend) {
 		u32 offset = OCFS2_I(dir)->ip_clusters;
 
-		status = ocfs2_do_extend_allocation(OCFS2_SB(sb), dir, &offset,
-						    1, 0, parent_fe_bh, handle,
-						    data_ac, meta_ac, NULL);
+		status = ocfs2_add_inode_data(OCFS2_SB(sb), dir, &offset,
+					      1, 0, parent_fe_bh, handle,
+					      data_ac, meta_ac, NULL);
 		BUG_ON(status == -EAGAIN);
 		if (status < 0) {
 			mlog_errno(status);
@@ -1430,12 +1467,14 @@
 	int credits, num_free_extents, drop_alloc_sem = 0;
 	loff_t dir_i_size;
 	struct ocfs2_dinode *fe = (struct ocfs2_dinode *) parent_fe_bh->b_data;
+	struct ocfs2_extent_list *el = &fe->id2.i_list;
 	struct ocfs2_alloc_context *data_ac = NULL;
 	struct ocfs2_alloc_context *meta_ac = NULL;
 	handle_t *handle = NULL;
 	struct buffer_head *new_bh = NULL;
 	struct ocfs2_dir_entry * de;
 	struct super_block *sb = osb->sb;
+	struct ocfs2_extent_tree et;
 
 	mlog_entry_void();
 
@@ -1479,7 +1518,8 @@
 	spin_lock(&OCFS2_I(dir)->ip_lock);
 	if (dir_i_size == ocfs2_clusters_to_bytes(sb, OCFS2_I(dir)->ip_clusters)) {
 		spin_unlock(&OCFS2_I(dir)->ip_lock);
-		num_free_extents = ocfs2_num_free_extents(osb, dir, fe);
+		ocfs2_init_dinode_extent_tree(&et, dir, parent_fe_bh);
+		num_free_extents = ocfs2_num_free_extents(osb, dir, &et);
 		if (num_free_extents < 0) {
 			status = num_free_extents;
 			mlog_errno(status);
@@ -1487,7 +1527,7 @@
 		}
 
 		if (!num_free_extents) {
-			status = ocfs2_reserve_new_metadata(osb, fe, &meta_ac);
+			status = ocfs2_reserve_new_metadata(osb, el, &meta_ac);
 			if (status < 0) {
 				if (status != -ENOSPC)
 					mlog_errno(status);
@@ -1502,7 +1542,7 @@
 			goto bail;
 		}
 
-		credits = ocfs2_calc_extend_credits(sb, fe, 1);
+		credits = ocfs2_calc_extend_credits(sb, el, 1);
 	} else {
 		spin_unlock(&OCFS2_I(dir)->ip_lock);
 		credits = OCFS2_SIMPLE_DIR_EXTEND_CREDITS;
@@ -1568,8 +1608,7 @@
 	if (meta_ac)
 		ocfs2_free_alloc_context(meta_ac);
 
-	if (new_bh)
-		brelse(new_bh);
+	brelse(new_bh);
 
 	mlog_exit(status);
 	return status;
@@ -1696,8 +1735,7 @@
 
 	status = 0;
 bail:
-	if (bh)
-		brelse(bh);
+	brelse(bh);
 
 	mlog_exit(status);
 	return status;
@@ -1756,7 +1794,6 @@
 	*ret_de_bh = bh;
 	bh = NULL;
 out:
-	if (bh)
-		brelse(bh);
+	brelse(bh);
 	return ret;
 }
diff --git a/fs/ocfs2/dlmglue.c b/fs/ocfs2/dlmglue.c
index eae3d64..ec68442 100644
--- a/fs/ocfs2/dlmglue.c
+++ b/fs/ocfs2/dlmglue.c
@@ -2024,8 +2024,7 @@
 	} else {
 		/* Boo, we have to go to disk. */
 		/* read bh, cast, ocfs2_refresh_inode */
-		status = ocfs2_read_block(OCFS2_SB(inode->i_sb), oi->ip_blkno,
-					  bh, OCFS2_BH_CACHED, inode);
+		status = ocfs2_read_block(inode, oi->ip_blkno, bh);
 		if (status < 0) {
 			mlog_errno(status);
 			goto bail_refresh;
@@ -2086,11 +2085,7 @@
 		return 0;
 	}
 
-	status = ocfs2_read_block(OCFS2_SB(inode->i_sb),
-				  OCFS2_I(inode)->ip_blkno,
-				  ret_bh,
-				  OCFS2_BH_CACHED,
-				  inode);
+	status = ocfs2_read_block(inode, OCFS2_I(inode)->ip_blkno, ret_bh);
 	if (status < 0)
 		mlog_errno(status);
 
diff --git a/fs/ocfs2/extent_map.c b/fs/ocfs2/extent_map.c
index aed268e..2baedac 100644
--- a/fs/ocfs2/extent_map.c
+++ b/fs/ocfs2/extent_map.c
@@ -293,8 +293,7 @@
 	struct ocfs2_extent_block *eb;
 	struct ocfs2_extent_list *el;
 
-	ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), last_eb_blk,
-			       &eb_bh, OCFS2_BH_CACHED, inode);
+	ret = ocfs2_read_block(inode, last_eb_blk, &eb_bh);
 	if (ret) {
 		mlog_errno(ret);
 		goto out;
@@ -382,9 +381,9 @@
 		if (le64_to_cpu(eb->h_next_leaf_blk) == 0ULL)
 			goto no_more_extents;
 
-		ret = ocfs2_read_block(OCFS2_SB(inode->i_sb),
+		ret = ocfs2_read_block(inode,
 				       le64_to_cpu(eb->h_next_leaf_blk),
-				       &next_eb_bh, OCFS2_BH_CACHED, inode);
+				       &next_eb_bh);
 		if (ret) {
 			mlog_errno(ret);
 			goto out;
@@ -551,6 +550,66 @@
 		*num_clusters = le16_to_cpu(rec->e_leaf_clusters) - coff;
 }
 
+int ocfs2_xattr_get_clusters(struct inode *inode, u32 v_cluster,
+			     u32 *p_cluster, u32 *num_clusters,
+			     struct ocfs2_extent_list *el)
+{
+	int ret = 0, i;
+	struct buffer_head *eb_bh = NULL;
+	struct ocfs2_extent_block *eb;
+	struct ocfs2_extent_rec *rec;
+	u32 coff;
+
+	if (el->l_tree_depth) {
+		ret = ocfs2_find_leaf(inode, el, v_cluster, &eb_bh);
+		if (ret) {
+			mlog_errno(ret);
+			goto out;
+		}
+
+		eb = (struct ocfs2_extent_block *) eb_bh->b_data;
+		el = &eb->h_list;
+
+		if (el->l_tree_depth) {
+			ocfs2_error(inode->i_sb,
+				    "Inode %lu has non zero tree depth in "
+				    "xattr leaf block %llu\n", inode->i_ino,
+				    (unsigned long long)eb_bh->b_blocknr);
+			ret = -EROFS;
+			goto out;
+		}
+	}
+
+	i = ocfs2_search_extent_list(el, v_cluster);
+	if (i == -1) {
+		ret = -EROFS;
+		mlog_errno(ret);
+		goto out;
+	} else {
+		rec = &el->l_recs[i];
+		BUG_ON(v_cluster < le32_to_cpu(rec->e_cpos));
+
+		if (!rec->e_blkno) {
+			ocfs2_error(inode->i_sb, "Inode %lu has bad extent "
+				    "record (%u, %u, 0) in xattr", inode->i_ino,
+				    le32_to_cpu(rec->e_cpos),
+				    ocfs2_rec_clusters(el, rec));
+			ret = -EROFS;
+			goto out;
+		}
+		coff = v_cluster - le32_to_cpu(rec->e_cpos);
+		*p_cluster = ocfs2_blocks_to_clusters(inode->i_sb,
+						    le64_to_cpu(rec->e_blkno));
+		*p_cluster = *p_cluster + coff;
+		if (num_clusters)
+			*num_clusters = ocfs2_rec_clusters(el, rec) - coff;
+	}
+out:
+	if (eb_bh)
+		brelse(eb_bh);
+	return ret;
+}
+
 int ocfs2_get_clusters(struct inode *inode, u32 v_cluster,
 		       u32 *p_cluster, u32 *num_clusters,
 		       unsigned int *extent_flags)
@@ -571,8 +630,7 @@
 	if (ret == 0)
 		goto out;
 
-	ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), OCFS2_I(inode)->ip_blkno,
-			       &di_bh, OCFS2_BH_CACHED, inode);
+	ret = ocfs2_read_block(inode, OCFS2_I(inode)->ip_blkno, &di_bh);
 	if (ret) {
 		mlog_errno(ret);
 		goto out;
diff --git a/fs/ocfs2/extent_map.h b/fs/ocfs2/extent_map.h
index 1b97490..1c4aa8b 100644
--- a/fs/ocfs2/extent_map.h
+++ b/fs/ocfs2/extent_map.h
@@ -53,4 +53,8 @@
 int ocfs2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
 		 u64 map_start, u64 map_len);
 
+int ocfs2_xattr_get_clusters(struct inode *inode, u32 v_cluster,
+			     u32 *p_cluster, u32 *num_clusters,
+			     struct ocfs2_extent_list *el);
+
 #endif  /* _EXTENT_MAP_H */
diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c
index ed38796..8d3225a 100644
--- a/fs/ocfs2/file.c
+++ b/fs/ocfs2/file.c
@@ -55,6 +55,7 @@
 #include "mmap.h"
 #include "suballoc.h"
 #include "super.h"
+#include "xattr.h"
 
 #include "buffer_head_io.h"
 
@@ -184,7 +185,7 @@
 		goto bail;
 
 	journal = osb->journal->j_journal;
-	err = journal_force_commit(journal);
+	err = jbd2_journal_force_commit(journal);
 
 bail:
 	mlog_exit(err);
@@ -488,7 +489,7 @@
 }
 
 /*
- * extend allocation only here.
+ * extend file allocation only here.
  * we'll update all the disk stuff, and oip->alloc_size
  *
  * expect stuff to be locked, a transaction started and enough data /
@@ -497,189 +498,25 @@
  * Will return -EAGAIN, and a reason if a restart is needed.
  * If passed in, *reason will always be set, even in error.
  */
-int ocfs2_do_extend_allocation(struct ocfs2_super *osb,
-			       struct inode *inode,
-			       u32 *logical_offset,
-			       u32 clusters_to_add,
-			       int mark_unwritten,
-			       struct buffer_head *fe_bh,
-			       handle_t *handle,
-			       struct ocfs2_alloc_context *data_ac,
-			       struct ocfs2_alloc_context *meta_ac,
-			       enum ocfs2_alloc_restarted *reason_ret)
+int ocfs2_add_inode_data(struct ocfs2_super *osb,
+			 struct inode *inode,
+			 u32 *logical_offset,
+			 u32 clusters_to_add,
+			 int mark_unwritten,
+			 struct buffer_head *fe_bh,
+			 handle_t *handle,
+			 struct ocfs2_alloc_context *data_ac,
+			 struct ocfs2_alloc_context *meta_ac,
+			 enum ocfs2_alloc_restarted *reason_ret)
 {
-	int status = 0;
-	int free_extents;
-	struct ocfs2_dinode *fe = (struct ocfs2_dinode *) fe_bh->b_data;
-	enum ocfs2_alloc_restarted reason = RESTART_NONE;
-	u32 bit_off, num_bits;
-	u64 block;
-	u8 flags = 0;
+	int ret;
+	struct ocfs2_extent_tree et;
 
-	BUG_ON(!clusters_to_add);
-
-	if (mark_unwritten)
-		flags = OCFS2_EXT_UNWRITTEN;
-
-	free_extents = ocfs2_num_free_extents(osb, inode, fe);
-	if (free_extents < 0) {
-		status = free_extents;
-		mlog_errno(status);
-		goto leave;
-	}
-
-	/* there are two cases which could cause us to EAGAIN in the
-	 * we-need-more-metadata case:
-	 * 1) we haven't reserved *any*
-	 * 2) we are so fragmented, we've needed to add metadata too
-	 *    many times. */
-	if (!free_extents && !meta_ac) {
-		mlog(0, "we haven't reserved any metadata!\n");
-		status = -EAGAIN;
-		reason = RESTART_META;
-		goto leave;
-	} else if ((!free_extents)
-		   && (ocfs2_alloc_context_bits_left(meta_ac)
-		       < ocfs2_extend_meta_needed(fe))) {
-		mlog(0, "filesystem is really fragmented...\n");
-		status = -EAGAIN;
-		reason = RESTART_META;
-		goto leave;
-	}
-
-	status = __ocfs2_claim_clusters(osb, handle, data_ac, 1,
-					clusters_to_add, &bit_off, &num_bits);
-	if (status < 0) {
-		if (status != -ENOSPC)
-			mlog_errno(status);
-		goto leave;
-	}
-
-	BUG_ON(num_bits > clusters_to_add);
-
-	/* reserve our write early -- insert_extent may update the inode */
-	status = ocfs2_journal_access(handle, inode, fe_bh,
-				      OCFS2_JOURNAL_ACCESS_WRITE);
-	if (status < 0) {
-		mlog_errno(status);
-		goto leave;
-	}
-
-	block = ocfs2_clusters_to_blocks(osb->sb, bit_off);
-	mlog(0, "Allocating %u clusters at block %u for inode %llu\n",
-	     num_bits, bit_off, (unsigned long long)OCFS2_I(inode)->ip_blkno);
-	status = ocfs2_insert_extent(osb, handle, inode, fe_bh,
-				     *logical_offset, block, num_bits,
-				     flags, meta_ac);
-	if (status < 0) {
-		mlog_errno(status);
-		goto leave;
-	}
-
-	status = ocfs2_journal_dirty(handle, fe_bh);
-	if (status < 0) {
-		mlog_errno(status);
-		goto leave;
-	}
-
-	clusters_to_add -= num_bits;
-	*logical_offset += num_bits;
-
-	if (clusters_to_add) {
-		mlog(0, "need to alloc once more, clusters = %u, wanted = "
-		     "%u\n", fe->i_clusters, clusters_to_add);
-		status = -EAGAIN;
-		reason = RESTART_TRANS;
-	}
-
-leave:
-	mlog_exit(status);
-	if (reason_ret)
-		*reason_ret = reason;
-	return status;
-}
-
-/*
- * For a given allocation, determine which allocators will need to be
- * accessed, and lock them, reserving the appropriate number of bits.
- *
- * Sparse file systems call this from ocfs2_write_begin_nolock()
- * and ocfs2_allocate_unwritten_extents().
- *
- * File systems which don't support holes call this from
- * ocfs2_extend_allocation().
- */
-int ocfs2_lock_allocators(struct inode *inode, struct ocfs2_dinode *di,
-			  u32 clusters_to_add, u32 extents_to_split,
-			  struct ocfs2_alloc_context **data_ac,
-			  struct ocfs2_alloc_context **meta_ac)
-{
-	int ret = 0, num_free_extents;
-	unsigned int max_recs_needed = clusters_to_add + 2 * extents_to_split;
-	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
-
-	*meta_ac = NULL;
-	if (data_ac)
-		*data_ac = NULL;
-
-	BUG_ON(clusters_to_add != 0 && data_ac == NULL);
-
-	mlog(0, "extend inode %llu, i_size = %lld, di->i_clusters = %u, "
-	     "clusters_to_add = %u, extents_to_split = %u\n",
-	     (unsigned long long)OCFS2_I(inode)->ip_blkno, (long long)i_size_read(inode),
-	     le32_to_cpu(di->i_clusters), clusters_to_add, extents_to_split);
-
-	num_free_extents = ocfs2_num_free_extents(osb, inode, di);
-	if (num_free_extents < 0) {
-		ret = num_free_extents;
-		mlog_errno(ret);
-		goto out;
-	}
-
-	/*
-	 * Sparse allocation file systems need to be more conservative
-	 * with reserving room for expansion - the actual allocation
-	 * happens while we've got a journal handle open so re-taking
-	 * a cluster lock (because we ran out of room for another
-	 * extent) will violate ordering rules.
-	 *
-	 * Most of the time we'll only be seeing this 1 cluster at a time
-	 * anyway.
-	 *
-	 * Always lock for any unwritten extents - we might want to
-	 * add blocks during a split.
-	 */
-	if (!num_free_extents ||
-	    (ocfs2_sparse_alloc(osb) && num_free_extents < max_recs_needed)) {
-		ret = ocfs2_reserve_new_metadata(osb, di, meta_ac);
-		if (ret < 0) {
-			if (ret != -ENOSPC)
-				mlog_errno(ret);
-			goto out;
-		}
-	}
-
-	if (clusters_to_add == 0)
-		goto out;
-
-	ret = ocfs2_reserve_clusters(osb, clusters_to_add, data_ac);
-	if (ret < 0) {
-		if (ret != -ENOSPC)
-			mlog_errno(ret);
-		goto out;
-	}
-
-out:
-	if (ret) {
-		if (*meta_ac) {
-			ocfs2_free_alloc_context(*meta_ac);
-			*meta_ac = NULL;
-		}
-
-		/*
-		 * We cannot have an error and a non null *data_ac.
-		 */
-	}
+	ocfs2_init_dinode_extent_tree(&et, inode, fe_bh);
+	ret = ocfs2_add_clusters_in_btree(osb, inode, logical_offset,
+					   clusters_to_add, mark_unwritten,
+					   &et, handle,
+					   data_ac, meta_ac, reason_ret);
 
 	return ret;
 }
@@ -698,6 +535,7 @@
 	struct ocfs2_alloc_context *meta_ac = NULL;
 	enum ocfs2_alloc_restarted why;
 	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+	struct ocfs2_extent_tree et;
 
 	mlog_entry("(clusters_to_add = %u)\n", clusters_to_add);
 
@@ -707,8 +545,7 @@
 	 */
 	BUG_ON(mark_unwritten && !ocfs2_sparse_alloc(osb));
 
-	status = ocfs2_read_block(osb, OCFS2_I(inode)->ip_blkno, &bh,
-				  OCFS2_BH_CACHED, inode);
+	status = ocfs2_read_block(inode, OCFS2_I(inode)->ip_blkno, &bh);
 	if (status < 0) {
 		mlog_errno(status);
 		goto leave;
@@ -724,14 +561,21 @@
 restart_all:
 	BUG_ON(le32_to_cpu(fe->i_clusters) != OCFS2_I(inode)->ip_clusters);
 
-	status = ocfs2_lock_allocators(inode, fe, clusters_to_add, 0, &data_ac,
-				       &meta_ac);
+	mlog(0, "extend inode %llu, i_size = %lld, di->i_clusters = %u, "
+	     "clusters_to_add = %u\n",
+	     (unsigned long long)OCFS2_I(inode)->ip_blkno,
+	     (long long)i_size_read(inode), le32_to_cpu(fe->i_clusters),
+	     clusters_to_add);
+	ocfs2_init_dinode_extent_tree(&et, inode, bh);
+	status = ocfs2_lock_allocators(inode, &et, clusters_to_add, 0,
+				       &data_ac, &meta_ac);
 	if (status) {
 		mlog_errno(status);
 		goto leave;
 	}
 
-	credits = ocfs2_calc_extend_credits(osb->sb, fe, clusters_to_add);
+	credits = ocfs2_calc_extend_credits(osb->sb, &fe->id2.i_list,
+					    clusters_to_add);
 	handle = ocfs2_start_trans(osb, credits);
 	if (IS_ERR(handle)) {
 		status = PTR_ERR(handle);
@@ -753,16 +597,16 @@
 
 	prev_clusters = OCFS2_I(inode)->ip_clusters;
 
-	status = ocfs2_do_extend_allocation(osb,
-					    inode,
-					    &logical_start,
-					    clusters_to_add,
-					    mark_unwritten,
-					    bh,
-					    handle,
-					    data_ac,
-					    meta_ac,
-					    &why);
+	status = ocfs2_add_inode_data(osb,
+				      inode,
+				      &logical_start,
+				      clusters_to_add,
+				      mark_unwritten,
+				      bh,
+				      handle,
+				      data_ac,
+				      meta_ac,
+				      &why);
 	if ((status < 0) && (status != -EAGAIN)) {
 		if (status != -ENOSPC)
 			mlog_errno(status);
@@ -789,7 +633,7 @@
 			mlog(0, "restarting transaction.\n");
 			/* TODO: This can be more intelligent. */
 			credits = ocfs2_calc_extend_credits(osb->sb,
-							    fe,
+							    &fe->id2.i_list,
 							    clusters_to_add);
 			status = ocfs2_extend_trans(handle, credits);
 			if (status < 0) {
@@ -826,10 +670,8 @@
 		restart_func = 0;
 		goto restart_all;
 	}
-	if (bh) {
-		brelse(bh);
-		bh = NULL;
-	}
+	brelse(bh);
+	bh = NULL;
 
 	mlog_exit(status);
 	return status;
@@ -1096,9 +938,15 @@
 			goto bail_unlock;
 		}
 
-		if (i_size_read(inode) > attr->ia_size)
+		if (i_size_read(inode) > attr->ia_size) {
+			if (ocfs2_should_order_data(inode)) {
+				status = ocfs2_begin_ordered_truncate(inode,
+								      attr->ia_size);
+				if (status)
+					goto bail_unlock;
+			}
 			status = ocfs2_truncate_file(inode, bh, attr->ia_size);
-		else
+		} else
 			status = ocfs2_extend_file(inode, bh, attr->ia_size);
 		if (status < 0) {
 			if (status != -ENOSPC)
@@ -1140,8 +988,7 @@
 	if (size_change)
 		ocfs2_rw_unlock(inode, 1);
 bail:
-	if (bh)
-		brelse(bh);
+	brelse(bh);
 
 	mlog_exit(status);
 	return status;
@@ -1284,8 +1131,7 @@
 	struct buffer_head *bh = NULL;
 	struct ocfs2_inode_info *oi = OCFS2_I(inode);
 
-	ret = ocfs2_read_block(OCFS2_SB(inode->i_sb),
-			       oi->ip_blkno, &bh, OCFS2_BH_CACHED, inode);
+	ret = ocfs2_read_block(inode, oi->ip_blkno, &bh);
 	if (ret < 0) {
 		mlog_errno(ret);
 		goto out;
@@ -1311,9 +1157,8 @@
 	struct buffer_head *di_bh = NULL;
 
 	if (OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_DATA_FL) {
-		ret = ocfs2_read_block(OCFS2_SB(inode->i_sb),
-				       OCFS2_I(inode)->ip_blkno, &di_bh,
-				       OCFS2_BH_CACHED, inode);
+		ret = ocfs2_read_block(inode, OCFS2_I(inode)->ip_blkno,
+				       &di_bh);
 		if (ret) {
 			mlog_errno(ret);
 			goto out;
@@ -1394,8 +1239,11 @@
 	handle_t *handle;
 	struct ocfs2_alloc_context *meta_ac = NULL;
 	struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data;
+	struct ocfs2_extent_tree et;
 
-	ret = ocfs2_lock_allocators(inode, di, 0, 1, NULL, &meta_ac);
+	ocfs2_init_dinode_extent_tree(&et, inode, di_bh);
+
+	ret = ocfs2_lock_allocators(inode, &et, 0, 1, NULL, &meta_ac);
 	if (ret) {
 		mlog_errno(ret);
 		return ret;
@@ -1425,7 +1273,7 @@
 		goto out;
 	}
 
-	ret = ocfs2_remove_extent(inode, di_bh, cpos, len, handle, meta_ac,
+	ret = ocfs2_remove_extent(inode, &et, cpos, len, handle, meta_ac,
 				  dealloc);
 	if (ret) {
 		mlog_errno(ret);
@@ -2040,7 +1888,7 @@
 		 */
 		if (old_size != i_size_read(inode) ||
 		    old_clusters != OCFS2_I(inode)->ip_clusters) {
-			ret = journal_force_commit(osb->journal->j_journal);
+			ret = jbd2_journal_force_commit(osb->journal->j_journal);
 			if (ret < 0)
 				written = ret;
 		}
@@ -2227,6 +2075,10 @@
 	.setattr	= ocfs2_setattr,
 	.getattr	= ocfs2_getattr,
 	.permission	= ocfs2_permission,
+	.setxattr	= generic_setxattr,
+	.getxattr	= generic_getxattr,
+	.listxattr	= ocfs2_listxattr,
+	.removexattr	= generic_removexattr,
 	.fallocate	= ocfs2_fallocate,
 	.fiemap		= ocfs2_fiemap,
 };
@@ -2237,6 +2089,10 @@
 	.permission	= ocfs2_permission,
 };
 
+/*
+ * Other than ->lock, keep ocfs2_fops and ocfs2_dops in sync with
+ * ocfs2_fops_no_plocks and ocfs2_dops_no_plocks!
+ */
 const struct file_operations ocfs2_fops = {
 	.llseek		= generic_file_llseek,
 	.read		= do_sync_read,
@@ -2251,12 +2107,59 @@
 #ifdef CONFIG_COMPAT
 	.compat_ioctl   = ocfs2_compat_ioctl,
 #endif
+	.lock		= ocfs2_lock,
+	.flock		= ocfs2_flock,
+	.splice_read	= ocfs2_file_splice_read,
+	.splice_write	= ocfs2_file_splice_write,
+};
+
+const struct file_operations ocfs2_dops = {
+	.llseek		= generic_file_llseek,
+	.read		= generic_read_dir,
+	.readdir	= ocfs2_readdir,
+	.fsync		= ocfs2_sync_file,
+	.release	= ocfs2_dir_release,
+	.open		= ocfs2_dir_open,
+	.unlocked_ioctl	= ocfs2_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl   = ocfs2_compat_ioctl,
+#endif
+	.lock		= ocfs2_lock,
+	.flock		= ocfs2_flock,
+};
+
+/*
+ * POSIX-lockless variants of our file_operations.
+ *
+ * These will be used if the underlying cluster stack does not support
+ * posix file locking, if the user passes the "localflocks" mount
+ * option, or if we have a local-only fs.
+ *
+ * ocfs2_flock is in here because all stacks handle UNIX file locks,
+ * so we still want it in the case of no stack support for
+ * plocks. Internally, it will do the right thing when asked to ignore
+ * the cluster.
+ */
+const struct file_operations ocfs2_fops_no_plocks = {
+	.llseek		= generic_file_llseek,
+	.read		= do_sync_read,
+	.write		= do_sync_write,
+	.mmap		= ocfs2_mmap,
+	.fsync		= ocfs2_sync_file,
+	.release	= ocfs2_file_release,
+	.open		= ocfs2_file_open,
+	.aio_read	= ocfs2_file_aio_read,
+	.aio_write	= ocfs2_file_aio_write,
+	.unlocked_ioctl	= ocfs2_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl   = ocfs2_compat_ioctl,
+#endif
 	.flock		= ocfs2_flock,
 	.splice_read	= ocfs2_file_splice_read,
 	.splice_write	= ocfs2_file_splice_write,
 };
 
-const struct file_operations ocfs2_dops = {
+const struct file_operations ocfs2_dops_no_plocks = {
 	.llseek		= generic_file_llseek,
 	.read		= generic_read_dir,
 	.readdir	= ocfs2_readdir,
diff --git a/fs/ocfs2/file.h b/fs/ocfs2/file.h
index 1e27b4d..e92382c 100644
--- a/fs/ocfs2/file.h
+++ b/fs/ocfs2/file.h
@@ -28,9 +28,12 @@
 
 extern const struct file_operations ocfs2_fops;
 extern const struct file_operations ocfs2_dops;
+extern const struct file_operations ocfs2_fops_no_plocks;
+extern const struct file_operations ocfs2_dops_no_plocks;
 extern const struct inode_operations ocfs2_file_iops;
 extern const struct inode_operations ocfs2_special_file_iops;
 struct ocfs2_alloc_context;
+enum ocfs2_alloc_restarted;
 
 struct ocfs2_file_private {
 	struct file		*fp_file;
@@ -38,27 +41,18 @@
 	struct ocfs2_lock_res	fp_flock;
 };
 
-enum ocfs2_alloc_restarted {
-	RESTART_NONE = 0,
-	RESTART_TRANS,
-	RESTART_META
-};
-int ocfs2_do_extend_allocation(struct ocfs2_super *osb,
-			       struct inode *inode,
-			       u32 *logical_offset,
-			       u32 clusters_to_add,
-			       int mark_unwritten,
-			       struct buffer_head *fe_bh,
-			       handle_t *handle,
-			       struct ocfs2_alloc_context *data_ac,
-			       struct ocfs2_alloc_context *meta_ac,
-			       enum ocfs2_alloc_restarted *reason_ret);
+int ocfs2_add_inode_data(struct ocfs2_super *osb,
+			 struct inode *inode,
+			 u32 *logical_offset,
+			 u32 clusters_to_add,
+			 int mark_unwritten,
+			 struct buffer_head *fe_bh,
+			 handle_t *handle,
+			 struct ocfs2_alloc_context *data_ac,
+			 struct ocfs2_alloc_context *meta_ac,
+			 enum ocfs2_alloc_restarted *reason_ret);
 int ocfs2_extend_no_holes(struct inode *inode, u64 new_i_size,
 			  u64 zero_to);
-int ocfs2_lock_allocators(struct inode *inode, struct ocfs2_dinode *di,
-			  u32 clusters_to_add, u32 extents_to_split,
-			  struct ocfs2_alloc_context **data_ac,
-			  struct ocfs2_alloc_context **meta_ac);
 int ocfs2_setattr(struct dentry *dentry, struct iattr *attr);
 int ocfs2_getattr(struct vfsmount *mnt, struct dentry *dentry,
 		  struct kstat *stat);
diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c
index 7e9e4c79..4903688 100644
--- a/fs/ocfs2/inode.c
+++ b/fs/ocfs2/inode.c
@@ -49,6 +49,7 @@
 #include "symlink.h"
 #include "sysfile.h"
 #include "uptodate.h"
+#include "xattr.h"
 
 #include "buffer_head_io.h"
 
@@ -219,6 +220,7 @@
 	struct super_block *sb;
 	struct ocfs2_super *osb;
 	int status = -EINVAL;
+	int use_plocks = 1;
 
 	mlog_entry("(0x%p, size:%llu)\n", inode,
 		   (unsigned long long)le64_to_cpu(fe->i_size));
@@ -226,6 +228,10 @@
 	sb = inode->i_sb;
 	osb = OCFS2_SB(sb);
 
+	if ((osb->s_mount_opt & OCFS2_MOUNT_LOCALFLOCKS) ||
+	    ocfs2_mount_local(osb) || !ocfs2_stack_supports_plocks())
+		use_plocks = 0;
+
 	/* this means that read_inode cannot create a superblock inode
 	 * today.  change if needed. */
 	if (!OCFS2_IS_VALID_DINODE(fe) ||
@@ -295,13 +301,19 @@
 
 	switch (inode->i_mode & S_IFMT) {
 	    case S_IFREG:
-		    inode->i_fop = &ocfs2_fops;
+		    if (use_plocks)
+			    inode->i_fop = &ocfs2_fops;
+		    else
+			    inode->i_fop = &ocfs2_fops_no_plocks;
 		    inode->i_op = &ocfs2_file_iops;
 		    i_size_write(inode, le64_to_cpu(fe->i_size));
 		    break;
 	    case S_IFDIR:
 		    inode->i_op = &ocfs2_dir_iops;
-		    inode->i_fop = &ocfs2_dops;
+		    if (use_plocks)
+			    inode->i_fop = &ocfs2_dops;
+		    else
+			    inode->i_fop = &ocfs2_dops_no_plocks;
 		    i_size_write(inode, le64_to_cpu(fe->i_size));
 		    break;
 	    case S_IFLNK:
@@ -448,8 +460,11 @@
 		}
 	}
 
-	status = ocfs2_read_block(osb, args->fi_blkno, &bh, 0,
-				  can_lock ? inode : NULL);
+	if (can_lock)
+		status = ocfs2_read_blocks(inode, args->fi_blkno, 1, &bh,
+					   OCFS2_BH_IGNORE_CACHE);
+	else
+		status = ocfs2_read_blocks_sync(osb, args->fi_blkno, 1, &bh);
 	if (status < 0) {
 		mlog_errno(status);
 		goto bail;
@@ -522,6 +537,9 @@
 	 * data and fast symlinks.
 	 */
 	if (fe->i_clusters) {
+		if (ocfs2_should_order_data(inode))
+			ocfs2_begin_ordered_truncate(inode, 0);
+
 		handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS);
 		if (IS_ERR(handle)) {
 			status = PTR_ERR(handle);
@@ -730,6 +748,13 @@
 		goto bail_unlock_dir;
 	}
 
+	/*Free extended attribute resources associated with this inode.*/
+	status = ocfs2_xattr_remove(inode, di_bh);
+	if (status < 0) {
+		mlog_errno(status);
+		goto bail_unlock_dir;
+	}
+
 	status = ocfs2_remove_inode(inode, di_bh, orphan_dir_inode,
 				    orphan_dir_bh);
 	if (status < 0)
@@ -1081,6 +1106,8 @@
 	oi->ip_last_trans = 0;
 	oi->ip_dir_start_lookup = 0;
 	oi->ip_blkno = 0ULL;
+	jbd2_journal_release_jbd_inode(OCFS2_SB(inode->i_sb)->journal->j_journal,
+				       &oi->ip_jinode);
 
 bail:
 	mlog_exit_void();
@@ -1107,58 +1134,6 @@
 }
 
 /*
- * TODO: this should probably be merged into ocfs2_get_block
- *
- * However, you now need to pay attention to the cont_prepare_write()
- * stuff in ocfs2_get_block (that is, ocfs2_get_block pretty much
- * expects never to extend).
- */
-struct buffer_head *ocfs2_bread(struct inode *inode,
-				int block, int *err, int reada)
-{
-	struct buffer_head *bh = NULL;
-	int tmperr;
-	u64 p_blkno;
-	int readflags = OCFS2_BH_CACHED;
-
-	if (reada)
-		readflags |= OCFS2_BH_READAHEAD;
-
-	if (((u64)block << inode->i_sb->s_blocksize_bits) >=
-	    i_size_read(inode)) {
-		BUG_ON(!reada);
-		return NULL;
-	}
-
-	down_read(&OCFS2_I(inode)->ip_alloc_sem);
-	tmperr = ocfs2_extent_map_get_blocks(inode, block, &p_blkno, NULL,
-					     NULL);
-	up_read(&OCFS2_I(inode)->ip_alloc_sem);
-	if (tmperr < 0) {
-		mlog_errno(tmperr);
-		goto fail;
-	}
-
-	tmperr = ocfs2_read_block(OCFS2_SB(inode->i_sb), p_blkno, &bh,
-				  readflags, inode);
-	if (tmperr < 0)
-		goto fail;
-
-	tmperr = 0;
-
-	*err = 0;
-	return bh;
-
-fail:
-	if (bh) {
-		brelse(bh);
-		bh = NULL;
-	}
-	*err = -EIO;
-	return NULL;
-}
-
-/*
  * This is called from our getattr.
  */
 int ocfs2_inode_revalidate(struct dentry *dentry)
diff --git a/fs/ocfs2/inode.h b/fs/ocfs2/inode.h
index 390a855..2f37af9 100644
--- a/fs/ocfs2/inode.h
+++ b/fs/ocfs2/inode.h
@@ -40,6 +40,9 @@
 	/* protects allocation changes on this inode. */
 	struct rw_semaphore		ip_alloc_sem;
 
+	/* protects extended attribute changes on this inode */
+	struct rw_semaphore		ip_xattr_sem;
+
 	/* These fields are protected by ip_lock */
 	spinlock_t			ip_lock;
 	u32				ip_open_count;
@@ -68,6 +71,7 @@
 	struct ocfs2_extent_map		ip_extent_map;
 
 	struct inode			vfs_inode;
+	struct jbd2_inode		ip_jinode;
 };
 
 /*
@@ -113,8 +117,6 @@
 
 extern const struct address_space_operations ocfs2_aops;
 
-struct buffer_head *ocfs2_bread(struct inode *inode, int block,
-				int *err, int reada);
 void ocfs2_clear_inode(struct inode *inode);
 void ocfs2_delete_inode(struct inode *inode);
 void ocfs2_drop_inode(struct inode *inode);
diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c
index 7b142f0..9fcd36d 100644
--- a/fs/ocfs2/ioctl.c
+++ b/fs/ocfs2/ioctl.c
@@ -102,8 +102,7 @@
 bail:
 	mutex_unlock(&inode->i_mutex);
 
-	if (bh)
-		brelse(bh);
+	brelse(bh);
 
 	mlog_exit(status);
 	return status;
diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c
index c47bc2a..81e4067 100644
--- a/fs/ocfs2/journal.c
+++ b/fs/ocfs2/journal.c
@@ -215,9 +215,9 @@
 		goto finally;
 	}
 
-	journal_lock_updates(journal->j_journal);
-	status = journal_flush(journal->j_journal);
-	journal_unlock_updates(journal->j_journal);
+	jbd2_journal_lock_updates(journal->j_journal);
+	status = jbd2_journal_flush(journal->j_journal);
+	jbd2_journal_unlock_updates(journal->j_journal);
 	if (status < 0) {
 		up_write(&journal->j_trans_barrier);
 		mlog_errno(status);
@@ -264,7 +264,7 @@
 
 	down_read(&osb->journal->j_trans_barrier);
 
-	handle = journal_start(journal, max_buffs);
+	handle = jbd2_journal_start(journal, max_buffs);
 	if (IS_ERR(handle)) {
 		up_read(&osb->journal->j_trans_barrier);
 
@@ -290,7 +290,7 @@
 
 	BUG_ON(!handle);
 
-	ret = journal_stop(handle);
+	ret = jbd2_journal_stop(handle);
 	if (ret < 0)
 		mlog_errno(ret);
 
@@ -304,7 +304,7 @@
  * transaction. extend_trans will either extend the current handle by
  * nblocks, or commit it and start a new one with nblocks credits.
  *
- * This might call journal_restart() which will commit dirty buffers
+ * This might call jbd2_journal_restart() which will commit dirty buffers
  * and then restart the transaction. Before calling
  * ocfs2_extend_trans(), any changed blocks should have been
  * dirtied. After calling it, all blocks which need to be changed must
@@ -332,7 +332,7 @@
 #ifdef CONFIG_OCFS2_DEBUG_FS
 	status = 1;
 #else
-	status = journal_extend(handle, nblocks);
+	status = jbd2_journal_extend(handle, nblocks);
 	if (status < 0) {
 		mlog_errno(status);
 		goto bail;
@@ -340,8 +340,10 @@
 #endif
 
 	if (status > 0) {
-		mlog(0, "journal_extend failed, trying journal_restart\n");
-		status = journal_restart(handle, nblocks);
+		mlog(0,
+		     "jbd2_journal_extend failed, trying "
+		     "jbd2_journal_restart\n");
+		status = jbd2_journal_restart(handle, nblocks);
 		if (status < 0) {
 			mlog_errno(status);
 			goto bail;
@@ -393,11 +395,11 @@
 	switch (type) {
 	case OCFS2_JOURNAL_ACCESS_CREATE:
 	case OCFS2_JOURNAL_ACCESS_WRITE:
-		status = journal_get_write_access(handle, bh);
+		status = jbd2_journal_get_write_access(handle, bh);
 		break;
 
 	case OCFS2_JOURNAL_ACCESS_UNDO:
-		status = journal_get_undo_access(handle, bh);
+		status = jbd2_journal_get_undo_access(handle, bh);
 		break;
 
 	default:
@@ -422,7 +424,7 @@
 	mlog_entry("(bh->b_blocknr=%llu)\n",
 		   (unsigned long long)bh->b_blocknr);
 
-	status = journal_dirty_metadata(handle, bh);
+	status = jbd2_journal_dirty_metadata(handle, bh);
 	if (status < 0)
 		mlog(ML_ERROR, "Could not dirty metadata buffer. "
 		     "(bh->b_blocknr=%llu)\n",
@@ -432,6 +434,7 @@
 	return status;
 }
 
+#ifdef CONFIG_OCFS2_COMPAT_JBD
 int ocfs2_journal_dirty_data(handle_t *handle,
 			     struct buffer_head *bh)
 {
@@ -443,8 +446,9 @@
 
 	return err;
 }
+#endif
 
-#define OCFS2_DEFAULT_COMMIT_INTERVAL 	(HZ * JBD_DEFAULT_MAX_COMMIT_AGE)
+#define OCFS2_DEFAULT_COMMIT_INTERVAL	(HZ * JBD2_DEFAULT_MAX_COMMIT_AGE)
 
 void ocfs2_set_journal_params(struct ocfs2_super *osb)
 {
@@ -457,9 +461,9 @@
 	spin_lock(&journal->j_state_lock);
 	journal->j_commit_interval = commit_interval;
 	if (osb->s_mount_opt & OCFS2_MOUNT_BARRIER)
-		journal->j_flags |= JFS_BARRIER;
+		journal->j_flags |= JBD2_BARRIER;
 	else
-		journal->j_flags &= ~JFS_BARRIER;
+		journal->j_flags &= ~JBD2_BARRIER;
 	spin_unlock(&journal->j_state_lock);
 }
 
@@ -524,14 +528,14 @@
 	mlog(0, "inode->ip_clusters = %u\n", OCFS2_I(inode)->ip_clusters);
 
 	/* call the kernels journal init function now */
-	j_journal = journal_init_inode(inode);
+	j_journal = jbd2_journal_init_inode(inode);
 	if (j_journal == NULL) {
 		mlog(ML_ERROR, "Linux journal layer error\n");
 		status = -EINVAL;
 		goto done;
 	}
 
-	mlog(0, "Returned from journal_init_inode\n");
+	mlog(0, "Returned from jbd2_journal_init_inode\n");
 	mlog(0, "j_journal->j_maxlen = %u\n", j_journal->j_maxlen);
 
 	*dirty = (le32_to_cpu(di->id1.journal1.ij_flags) &
@@ -550,8 +554,7 @@
 	if (status < 0) {
 		if (inode_lock)
 			ocfs2_inode_unlock(inode, 1);
-		if (bh != NULL)
-			brelse(bh);
+		brelse(bh);
 		if (inode) {
 			OCFS2_I(inode)->ip_open_count--;
 			iput(inode);
@@ -639,7 +642,7 @@
 	if (journal->j_state != OCFS2_JOURNAL_LOADED)
 		goto done;
 
-	/* need to inc inode use count as journal_destroy will iput. */
+	/* need to inc inode use count - jbd2_journal_destroy will iput. */
 	if (!igrab(inode))
 		BUG();
 
@@ -668,9 +671,9 @@
 	BUG_ON(atomic_read(&(osb->journal->j_num_trans)) != 0);
 
 	if (ocfs2_mount_local(osb)) {
-		journal_lock_updates(journal->j_journal);
-		status = journal_flush(journal->j_journal);
-		journal_unlock_updates(journal->j_journal);
+		jbd2_journal_lock_updates(journal->j_journal);
+		status = jbd2_journal_flush(journal->j_journal);
+		jbd2_journal_unlock_updates(journal->j_journal);
 		if (status < 0)
 			mlog_errno(status);
 	}
@@ -686,7 +689,7 @@
 	}
 
 	/* Shutdown the kernel journal system */
-	journal_destroy(journal->j_journal);
+	jbd2_journal_destroy(journal->j_journal);
 
 	OCFS2_I(inode)->ip_open_count--;
 
@@ -711,15 +714,15 @@
 {
 	int olderr;
 
-	olderr = journal_errno(journal);
+	olderr = jbd2_journal_errno(journal);
 	if (olderr) {
 		mlog(ML_ERROR, "File system error %d recorded in "
 		     "journal %u.\n", olderr, slot);
 		mlog(ML_ERROR, "File system on device %s needs checking.\n",
 		     sb->s_id);
 
-		journal_ack_err(journal);
-		journal_clear_err(journal);
+		jbd2_journal_ack_err(journal);
+		jbd2_journal_clear_err(journal);
 	}
 }
 
@@ -734,7 +737,7 @@
 
 	osb = journal->j_osb;
 
-	status = journal_load(journal->j_journal);
+	status = jbd2_journal_load(journal->j_journal);
 	if (status < 0) {
 		mlog(ML_ERROR, "Failed to load journal!\n");
 		goto done;
@@ -778,7 +781,7 @@
 
 	BUG_ON(!journal);
 
-	status = journal_wipe(journal->j_journal, full);
+	status = jbd2_journal_wipe(journal->j_journal, full);
 	if (status < 0) {
 		mlog_errno(status);
 		goto bail;
@@ -847,9 +850,8 @@
 
 		/* We are reading journal data which should not
 		 * be put in the uptodate cache */
-		status = ocfs2_read_blocks(OCFS2_SB(inode->i_sb),
-					   p_blkno, p_blocks, bhs, 0,
-					   NULL);
+		status = ocfs2_read_blocks_sync(OCFS2_SB(inode->i_sb),
+						p_blkno, p_blocks, bhs);
 		if (status < 0) {
 			mlog_errno(status);
 			goto bail;
@@ -865,8 +867,7 @@
 
 bail:
 	for(i = 0; i < CONCURRENT_JOURNAL_FILL; i++)
-		if (bhs[i])
-			brelse(bhs[i]);
+		brelse(bhs[i]);
 	mlog_exit(status);
 	return status;
 }
@@ -1133,7 +1134,8 @@
 	}
 	SET_INODE_JOURNAL(inode);
 
-	status = ocfs2_read_block(osb, OCFS2_I(inode)->ip_blkno, bh, 0, inode);
+	status = ocfs2_read_blocks(inode, OCFS2_I(inode)->ip_blkno, 1, bh,
+				   OCFS2_BH_IGNORE_CACHE);
 	if (status < 0) {
 		mlog_errno(status);
 		goto bail;
@@ -1229,19 +1231,19 @@
 	}
 
 	mlog(0, "calling journal_init_inode\n");
-	journal = journal_init_inode(inode);
+	journal = jbd2_journal_init_inode(inode);
 	if (journal == NULL) {
 		mlog(ML_ERROR, "Linux journal layer error\n");
 		status = -EIO;
 		goto done;
 	}
 
-	status = journal_load(journal);
+	status = jbd2_journal_load(journal);
 	if (status < 0) {
 		mlog_errno(status);
 		if (!igrab(inode))
 			BUG();
-		journal_destroy(journal);
+		jbd2_journal_destroy(journal);
 		goto done;
 	}
 
@@ -1249,9 +1251,9 @@
 
 	/* wipe the journal */
 	mlog(0, "flushing the journal.\n");
-	journal_lock_updates(journal);
-	status = journal_flush(journal);
-	journal_unlock_updates(journal);
+	jbd2_journal_lock_updates(journal);
+	status = jbd2_journal_flush(journal);
+	jbd2_journal_unlock_updates(journal);
 	if (status < 0)
 		mlog_errno(status);
 
@@ -1272,7 +1274,7 @@
 	if (!igrab(inode))
 		BUG();
 
-	journal_destroy(journal);
+	jbd2_journal_destroy(journal);
 
 done:
 	/* drop the lock on this nodes journal */
@@ -1282,8 +1284,7 @@
 	if (inode)
 		iput(inode);
 
-	if (bh)
-		brelse(bh);
+	brelse(bh);
 
 	mlog_exit(status);
 	return status;
diff --git a/fs/ocfs2/journal.h b/fs/ocfs2/journal.h
index 2178ebf..d4d14e9 100644
--- a/fs/ocfs2/journal.h
+++ b/fs/ocfs2/journal.h
@@ -27,7 +27,12 @@
 #define OCFS2_JOURNAL_H
 
 #include <linux/fs.h>
-#include <linux/jbd.h>
+#ifndef CONFIG_OCFS2_COMPAT_JBD
+# include <linux/jbd2.h>
+#else
+# include <linux/jbd.h>
+# include "ocfs2_jbd_compat.h"
+#endif
 
 enum ocfs2_journal_state {
 	OCFS2_JOURNAL_FREE = 0,
@@ -215,8 +220,8 @@
  *                          buffer. Will have to call ocfs2_journal_dirty once
  *                          we've actually dirtied it. Type is one of . or .
  *  ocfs2_journal_dirty    - Mark a journalled buffer as having dirty data.
- *  ocfs2_journal_dirty_data - Indicate that a data buffer should go out before
- *                             the current handle commits.
+ *  ocfs2_jbd2_file_inode  - Mark an inode so that its data goes out before
+ *                           the current handle commits.
  */
 
 /* You must always start_trans with a number of buffs > 0, but it's
@@ -268,8 +273,10 @@
  */
 int                  ocfs2_journal_dirty(handle_t *handle,
 					 struct buffer_head *bh);
+#ifdef CONFIG_OCFS2_COMPAT_JBD
 int                  ocfs2_journal_dirty_data(handle_t *handle,
 					      struct buffer_head *bh);
+#endif
 
 /*
  *  Credit Macros:
@@ -283,6 +290,9 @@
 /* simple file updates like chmod, etc. */
 #define OCFS2_INODE_UPDATE_CREDITS 1
 
+/* extended attribute block update */
+#define OCFS2_XATTR_BLOCK_UPDATE_CREDITS 1
+
 /* group extend. inode update and last group update. */
 #define OCFS2_GROUP_EXTEND_CREDITS	(OCFS2_INODE_UPDATE_CREDITS + 1)
 
@@ -340,11 +350,23 @@
 #define OCFS2_RENAME_CREDITS (3 * OCFS2_INODE_UPDATE_CREDITS + 3              \
 			     + OCFS2_UNLINK_CREDITS)
 
+/* global bitmap dinode, group desc., relinked group,
+ * suballocator dinode, group desc., relinked group,
+ * dinode, xattr block */
+#define OCFS2_XATTR_BLOCK_CREATE_CREDITS (OCFS2_SUBALLOC_ALLOC * 2 + \
+					  + OCFS2_INODE_UPDATE_CREDITS \
+					  + OCFS2_XATTR_BLOCK_UPDATE_CREDITS)
+
+/*
+ * Please note that the caller must make sure that root_el is the root
+ * of extent tree. So for an inode, it should be &fe->id2.i_list. Otherwise
+ * the result may be wrong.
+ */
 static inline int ocfs2_calc_extend_credits(struct super_block *sb,
-					    struct ocfs2_dinode *fe,
+					    struct ocfs2_extent_list *root_el,
 					    u32 bits_wanted)
 {
-	int bitmap_blocks, sysfile_bitmap_blocks, dinode_blocks;
+	int bitmap_blocks, sysfile_bitmap_blocks, extent_blocks;
 
 	/* bitmap dinode, group desc. + relinked group. */
 	bitmap_blocks = OCFS2_SUBALLOC_ALLOC;
@@ -355,16 +377,16 @@
 	 * however many metadata chunks needed * a remaining suballoc
 	 * alloc. */
 	sysfile_bitmap_blocks = 1 +
-		(OCFS2_SUBALLOC_ALLOC - 1) * ocfs2_extend_meta_needed(fe);
+		(OCFS2_SUBALLOC_ALLOC - 1) * ocfs2_extend_meta_needed(root_el);
 
 	/* this does not include *new* metadata blocks, which are
-	 * accounted for in sysfile_bitmap_blocks. fe +
+	 * accounted for in sysfile_bitmap_blocks. root_el +
 	 * prev. last_eb_blk + blocks along edge of tree.
 	 * calc_symlink_credits passes because we just need 1
 	 * credit for the dinode there. */
-	dinode_blocks = 1 + 1 + le16_to_cpu(fe->id2.i_list.l_tree_depth);
+	extent_blocks = 1 + 1 + le16_to_cpu(root_el->l_tree_depth);
 
-	return bitmap_blocks + sysfile_bitmap_blocks + dinode_blocks;
+	return bitmap_blocks + sysfile_bitmap_blocks + extent_blocks;
 }
 
 static inline int ocfs2_calc_symlink_credits(struct super_block *sb)
@@ -415,4 +437,16 @@
 	return credits;
 }
 
+static inline int ocfs2_jbd2_file_inode(handle_t *handle, struct inode *inode)
+{
+	return jbd2_journal_file_inode(handle, &OCFS2_I(inode)->ip_jinode);
+}
+
+static inline int ocfs2_begin_ordered_truncate(struct inode *inode,
+					       loff_t new_size)
+{
+	return jbd2_journal_begin_ordered_truncate(&OCFS2_I(inode)->ip_jinode,
+						   new_size);
+}
+
 #endif /* OCFS2_JOURNAL_H */
diff --git a/fs/ocfs2/localalloc.c b/fs/ocfs2/localalloc.c
index 28e492e..687b287 100644
--- a/fs/ocfs2/localalloc.c
+++ b/fs/ocfs2/localalloc.c
@@ -28,6 +28,7 @@
 #include <linux/slab.h>
 #include <linux/highmem.h>
 #include <linux/bitops.h>
+#include <linux/debugfs.h>
 
 #define MLOG_MASK_PREFIX ML_DISK_ALLOC
 #include <cluster/masklog.h>
@@ -47,8 +48,6 @@
 
 #define OCFS2_LOCAL_ALLOC(dinode)	(&((dinode)->id2.i_lab))
 
-static inline int ocfs2_local_alloc_window_bits(struct ocfs2_super *osb);
-
 static u32 ocfs2_local_alloc_count_bits(struct ocfs2_dinode *alloc);
 
 static int ocfs2_local_alloc_find_clear_bits(struct ocfs2_super *osb,
@@ -75,24 +74,129 @@
 static int ocfs2_local_alloc_slide_window(struct ocfs2_super *osb,
 					  struct inode *local_alloc_inode);
 
-static inline int ocfs2_local_alloc_window_bits(struct ocfs2_super *osb)
-{
-	BUG_ON(osb->s_clustersize_bits > 20);
+#ifdef CONFIG_OCFS2_FS_STATS
 
-	/* Size local alloc windows by the megabyte */
-	return osb->local_alloc_size << (20 - osb->s_clustersize_bits);
+static int ocfs2_la_debug_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+#define LA_DEBUG_BUF_SZ	PAGE_CACHE_SIZE
+#define LA_DEBUG_VER	1
+static ssize_t ocfs2_la_debug_read(struct file *file, char __user *userbuf,
+				   size_t count, loff_t *ppos)
+{
+	static DEFINE_MUTEX(la_debug_mutex);
+	struct ocfs2_super *osb = file->private_data;
+	int written, ret;
+	char *buf = osb->local_alloc_debug_buf;
+
+	mutex_lock(&la_debug_mutex);
+	memset(buf, 0, LA_DEBUG_BUF_SZ);
+
+	written = snprintf(buf, LA_DEBUG_BUF_SZ,
+			   "0x%x\t0x%llx\t%u\t%u\t0x%x\n",
+			   LA_DEBUG_VER,
+			   (unsigned long long)osb->la_last_gd,
+			   osb->local_alloc_default_bits,
+			   osb->local_alloc_bits, osb->local_alloc_state);
+
+	ret = simple_read_from_buffer(userbuf, count, ppos, buf, written);
+
+	mutex_unlock(&la_debug_mutex);
+	return ret;
+}
+
+static const struct file_operations ocfs2_la_debug_fops = {
+	.open =		ocfs2_la_debug_open,
+	.read =		ocfs2_la_debug_read,
+};
+
+static void ocfs2_init_la_debug(struct ocfs2_super *osb)
+{
+	osb->local_alloc_debug_buf = kmalloc(LA_DEBUG_BUF_SZ, GFP_NOFS);
+	if (!osb->local_alloc_debug_buf)
+		return;
+
+	osb->local_alloc_debug = debugfs_create_file("local_alloc_stats",
+						     S_IFREG|S_IRUSR,
+						     osb->osb_debug_root,
+						     osb,
+						     &ocfs2_la_debug_fops);
+	if (!osb->local_alloc_debug) {
+		kfree(osb->local_alloc_debug_buf);
+		osb->local_alloc_debug_buf = NULL;
+	}
+}
+
+static void ocfs2_shutdown_la_debug(struct ocfs2_super *osb)
+{
+	if (osb->local_alloc_debug)
+		debugfs_remove(osb->local_alloc_debug);
+
+	if (osb->local_alloc_debug_buf)
+		kfree(osb->local_alloc_debug_buf);
+
+	osb->local_alloc_debug_buf = NULL;
+	osb->local_alloc_debug = NULL;
+}
+#else	/* CONFIG_OCFS2_FS_STATS */
+static void ocfs2_init_la_debug(struct ocfs2_super *osb)
+{
+	return;
+}
+static void ocfs2_shutdown_la_debug(struct ocfs2_super *osb)
+{
+	return;
+}
+#endif
+
+static inline int ocfs2_la_state_enabled(struct ocfs2_super *osb)
+{
+	return (osb->local_alloc_state == OCFS2_LA_THROTTLED ||
+		osb->local_alloc_state == OCFS2_LA_ENABLED);
+}
+
+void ocfs2_local_alloc_seen_free_bits(struct ocfs2_super *osb,
+				      unsigned int num_clusters)
+{
+	spin_lock(&osb->osb_lock);
+	if (osb->local_alloc_state == OCFS2_LA_DISABLED ||
+	    osb->local_alloc_state == OCFS2_LA_THROTTLED)
+		if (num_clusters >= osb->local_alloc_default_bits) {
+			cancel_delayed_work(&osb->la_enable_wq);
+			osb->local_alloc_state = OCFS2_LA_ENABLED;
+		}
+	spin_unlock(&osb->osb_lock);
+}
+
+void ocfs2_la_enable_worker(struct work_struct *work)
+{
+	struct ocfs2_super *osb =
+		container_of(work, struct ocfs2_super,
+			     la_enable_wq.work);
+	spin_lock(&osb->osb_lock);
+	osb->local_alloc_state = OCFS2_LA_ENABLED;
+	spin_unlock(&osb->osb_lock);
 }
 
 /*
  * Tell us whether a given allocation should use the local alloc
  * file. Otherwise, it has to go to the main bitmap.
+ *
+ * This function does semi-dirty reads of local alloc size and state!
+ * This is ok however, as the values are re-checked once under mutex.
  */
 int ocfs2_alloc_should_use_local(struct ocfs2_super *osb, u64 bits)
 {
-	int la_bits = ocfs2_local_alloc_window_bits(osb);
 	int ret = 0;
+	int la_bits;
 
-	if (osb->local_alloc_state != OCFS2_LA_ENABLED)
+	spin_lock(&osb->osb_lock);
+	la_bits = osb->local_alloc_bits;
+
+	if (!ocfs2_la_state_enabled(osb))
 		goto bail;
 
 	/* la_bits should be at least twice the size (in clusters) of
@@ -106,6 +210,7 @@
 bail:
 	mlog(0, "state=%d, bits=%llu, la_bits=%d, ret=%d\n",
 	     osb->local_alloc_state, (unsigned long long)bits, la_bits, ret);
+	spin_unlock(&osb->osb_lock);
 	return ret;
 }
 
@@ -120,14 +225,18 @@
 
 	mlog_entry_void();
 
-	if (osb->local_alloc_size == 0)
+	ocfs2_init_la_debug(osb);
+
+	if (osb->local_alloc_bits == 0)
 		goto bail;
 
-	if (ocfs2_local_alloc_window_bits(osb) >= osb->bitmap_cpg) {
+	if (osb->local_alloc_bits >= osb->bitmap_cpg) {
 		mlog(ML_NOTICE, "Requested local alloc window %d is larger "
 		     "than max possible %u. Using defaults.\n",
-		     ocfs2_local_alloc_window_bits(osb), (osb->bitmap_cpg - 1));
-		osb->local_alloc_size = OCFS2_DEFAULT_LOCAL_ALLOC_SIZE;
+		     osb->local_alloc_bits, (osb->bitmap_cpg - 1));
+		osb->local_alloc_bits =
+			ocfs2_megabytes_to_clusters(osb->sb,
+						    OCFS2_DEFAULT_LOCAL_ALLOC_SIZE);
 	}
 
 	/* read the alloc off disk */
@@ -139,8 +248,8 @@
 		goto bail;
 	}
 
-	status = ocfs2_read_block(osb, OCFS2_I(inode)->ip_blkno,
-				  &alloc_bh, 0, inode);
+	status = ocfs2_read_blocks(inode, OCFS2_I(inode)->ip_blkno, 1,
+				   &alloc_bh, OCFS2_BH_IGNORE_CACHE);
 	if (status < 0) {
 		mlog_errno(status);
 		goto bail;
@@ -185,13 +294,14 @@
 
 bail:
 	if (status < 0)
-		if (alloc_bh)
-			brelse(alloc_bh);
+		brelse(alloc_bh);
 	if (inode)
 		iput(inode);
 
-	mlog(0, "Local alloc window bits = %d\n",
-	     ocfs2_local_alloc_window_bits(osb));
+	if (status < 0)
+		ocfs2_shutdown_la_debug(osb);
+
+	mlog(0, "Local alloc window bits = %d\n", osb->local_alloc_bits);
 
 	mlog_exit(status);
 	return status;
@@ -217,6 +327,11 @@
 
 	mlog_entry_void();
 
+	cancel_delayed_work(&osb->la_enable_wq);
+	flush_workqueue(ocfs2_wq);
+
+	ocfs2_shutdown_la_debug(osb);
+
 	if (osb->local_alloc_state == OCFS2_LA_UNUSED)
 		goto out;
 
@@ -295,8 +410,7 @@
 	ocfs2_commit_trans(osb, handle);
 
 out_unlock:
-	if (main_bm_bh)
-		brelse(main_bm_bh);
+	brelse(main_bm_bh);
 
 	ocfs2_inode_unlock(main_bm_inode, 1);
 
@@ -345,8 +459,8 @@
 
 	mutex_lock(&inode->i_mutex);
 
-	status = ocfs2_read_block(osb, OCFS2_I(inode)->ip_blkno,
-				  &alloc_bh, 0, inode);
+	status = ocfs2_read_blocks(inode, OCFS2_I(inode)->ip_blkno, 1,
+				   &alloc_bh, OCFS2_BH_IGNORE_CACHE);
 	if (status < 0) {
 		mlog_errno(status);
 		goto bail;
@@ -372,8 +486,7 @@
 		*alloc_copy = NULL;
 	}
 
-	if (alloc_bh)
-		brelse(alloc_bh);
+	brelse(alloc_bh);
 
 	if (inode) {
 		mutex_unlock(&inode->i_mutex);
@@ -441,8 +554,7 @@
 out_mutex:
 	mutex_unlock(&main_bm_inode->i_mutex);
 
-	if (main_bm_bh)
-		brelse(main_bm_bh);
+	brelse(main_bm_bh);
 
 	iput(main_bm_inode);
 
@@ -453,8 +565,48 @@
 	return status;
 }
 
+/* Check to see if the local alloc window is within ac->ac_max_block */
+static int ocfs2_local_alloc_in_range(struct inode *inode,
+				      struct ocfs2_alloc_context *ac,
+				      u32 bits_wanted)
+{
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+	struct ocfs2_dinode *alloc;
+	struct ocfs2_local_alloc *la;
+	int start;
+	u64 block_off;
+
+	if (!ac->ac_max_block)
+		return 1;
+
+	alloc = (struct ocfs2_dinode *) osb->local_alloc_bh->b_data;
+	la = OCFS2_LOCAL_ALLOC(alloc);
+
+	start = ocfs2_local_alloc_find_clear_bits(osb, alloc, bits_wanted);
+	if (start == -1) {
+		mlog_errno(-ENOSPC);
+		return 0;
+	}
+
+	/*
+	 * Converting (bm_off + start + bits_wanted) to blocks gives us
+	 * the blkno just past our actual allocation.  This is perfect
+	 * to compare with ac_max_block.
+	 */
+	block_off = ocfs2_clusters_to_blocks(inode->i_sb,
+					     le32_to_cpu(la->la_bm_off) +
+					     start + bits_wanted);
+	mlog(0, "Checking %llu against %llu\n",
+	     (unsigned long long)block_off,
+	     (unsigned long long)ac->ac_max_block);
+	if (block_off > ac->ac_max_block)
+		return 0;
+
+	return 1;
+}
+
 /*
- * make sure we've got at least bitswanted contiguous bits in the
+ * make sure we've got at least bits_wanted contiguous bits in the
  * local alloc. You lose them when you drop i_mutex.
  *
  * We will add ourselves to the transaction passed in, but may start
@@ -485,16 +637,18 @@
 
 	mutex_lock(&local_alloc_inode->i_mutex);
 
-	if (osb->local_alloc_state != OCFS2_LA_ENABLED) {
+	/*
+	 * We must double check state and allocator bits because
+	 * another process may have changed them while holding i_mutex.
+	 */
+	spin_lock(&osb->osb_lock);
+	if (!ocfs2_la_state_enabled(osb) ||
+	    (bits_wanted > osb->local_alloc_bits)) {
+		spin_unlock(&osb->osb_lock);
 		status = -ENOSPC;
 		goto bail;
 	}
-
-	if (bits_wanted > ocfs2_local_alloc_window_bits(osb)) {
-		mlog(0, "Asking for more than my max window size!\n");
-		status = -ENOSPC;
-		goto bail;
-	}
+	spin_unlock(&osb->osb_lock);
 
 	alloc = (struct ocfs2_dinode *) osb->local_alloc_bh->b_data;
 
@@ -522,6 +676,36 @@
 				mlog_errno(status);
 			goto bail;
 		}
+
+		/*
+		 * Under certain conditions, the window slide code
+		 * might have reduced the number of bits available or
+		 * disabled the the local alloc entirely. Re-check
+		 * here and return -ENOSPC if necessary.
+		 */
+		status = -ENOSPC;
+		if (!ocfs2_la_state_enabled(osb))
+			goto bail;
+
+		free_bits = le32_to_cpu(alloc->id1.bitmap1.i_total) -
+			le32_to_cpu(alloc->id1.bitmap1.i_used);
+		if (bits_wanted > free_bits)
+			goto bail;
+	}
+
+	if (ac->ac_max_block)
+		mlog(0, "Calling in_range for max block %llu\n",
+		     (unsigned long long)ac->ac_max_block);
+
+	if (!ocfs2_local_alloc_in_range(local_alloc_inode, ac,
+					bits_wanted)) {
+		/*
+		 * The window is outside ac->ac_max_block.
+		 * This errno tells the caller to keep localalloc enabled
+		 * but to get the allocation from the main bitmap.
+		 */
+		status = -EFBIG;
+		goto bail;
 	}
 
 	ac->ac_inode = local_alloc_inode;
@@ -789,6 +973,85 @@
 	return status;
 }
 
+enum ocfs2_la_event {
+	OCFS2_LA_EVENT_SLIDE,		/* Normal window slide. */
+	OCFS2_LA_EVENT_FRAGMENTED,	/* The global bitmap has
+					 * enough bits theoretically
+					 * free, but a contiguous
+					 * allocation could not be
+					 * found. */
+	OCFS2_LA_EVENT_ENOSPC,		/* Global bitmap doesn't have
+					 * enough bits free to satisfy
+					 * our request. */
+};
+#define OCFS2_LA_ENABLE_INTERVAL (30 * HZ)
+/*
+ * Given an event, calculate the size of our next local alloc window.
+ *
+ * This should always be called under i_mutex of the local alloc inode
+ * so that local alloc disabling doesn't race with processes trying to
+ * use the allocator.
+ *
+ * Returns the state which the local alloc was left in. This value can
+ * be ignored by some paths.
+ */
+static int ocfs2_recalc_la_window(struct ocfs2_super *osb,
+				  enum ocfs2_la_event event)
+{
+	unsigned int bits;
+	int state;
+
+	spin_lock(&osb->osb_lock);
+	if (osb->local_alloc_state == OCFS2_LA_DISABLED) {
+		WARN_ON_ONCE(osb->local_alloc_state == OCFS2_LA_DISABLED);
+		goto out_unlock;
+	}
+
+	/*
+	 * ENOSPC and fragmentation are treated similarly for now.
+	 */
+	if (event == OCFS2_LA_EVENT_ENOSPC ||
+	    event == OCFS2_LA_EVENT_FRAGMENTED) {
+		/*
+		 * We ran out of contiguous space in the primary
+		 * bitmap. Drastically reduce the number of bits used
+		 * by local alloc until we have to disable it.
+		 */
+		bits = osb->local_alloc_bits >> 1;
+		if (bits > ocfs2_megabytes_to_clusters(osb->sb, 1)) {
+			/*
+			 * By setting state to THROTTLED, we'll keep
+			 * the number of local alloc bits used down
+			 * until an event occurs which would give us
+			 * reason to assume the bitmap situation might
+			 * have changed.
+			 */
+			osb->local_alloc_state = OCFS2_LA_THROTTLED;
+			osb->local_alloc_bits = bits;
+		} else {
+			osb->local_alloc_state = OCFS2_LA_DISABLED;
+		}
+		queue_delayed_work(ocfs2_wq, &osb->la_enable_wq,
+				   OCFS2_LA_ENABLE_INTERVAL);
+		goto out_unlock;
+	}
+
+	/*
+	 * Don't increase the size of the local alloc window until we
+	 * know we might be able to fulfill the request. Otherwise, we
+	 * risk bouncing around the global bitmap during periods of
+	 * low space.
+	 */
+	if (osb->local_alloc_state != OCFS2_LA_THROTTLED)
+		osb->local_alloc_bits = osb->local_alloc_default_bits;
+
+out_unlock:
+	state = osb->local_alloc_state;
+	spin_unlock(&osb->osb_lock);
+
+	return state;
+}
+
 static int ocfs2_local_alloc_reserve_for_window(struct ocfs2_super *osb,
 						struct ocfs2_alloc_context **ac,
 						struct inode **bitmap_inode,
@@ -803,12 +1066,21 @@
 		goto bail;
 	}
 
-	(*ac)->ac_bits_wanted = ocfs2_local_alloc_window_bits(osb);
+retry_enospc:
+	(*ac)->ac_bits_wanted = osb->local_alloc_bits;
 
 	status = ocfs2_reserve_cluster_bitmap_bits(osb, *ac);
+	if (status == -ENOSPC) {
+		if (ocfs2_recalc_la_window(osb, OCFS2_LA_EVENT_ENOSPC) ==
+		    OCFS2_LA_DISABLED)
+			goto bail;
+
+		ocfs2_free_ac_resource(*ac);
+		memset(*ac, 0, sizeof(struct ocfs2_alloc_context));
+		goto retry_enospc;
+	}
 	if (status < 0) {
-		if (status != -ENOSPC)
-			mlog_errno(status);
+		mlog_errno(status);
 		goto bail;
 	}
 
@@ -849,7 +1121,7 @@
 		     "one\n");
 
 	mlog(0, "Allocating %u clusters for a new window.\n",
-	     ocfs2_local_alloc_window_bits(osb));
+	     osb->local_alloc_bits);
 
 	/* Instruct the allocation code to try the most recently used
 	 * cluster group. We'll re-record the group used this pass
@@ -859,9 +1131,36 @@
 	/* we used the generic suballoc reserve function, but we set
 	 * everything up nicely, so there's no reason why we can't use
 	 * the more specific cluster api to claim bits. */
-	status = ocfs2_claim_clusters(osb, handle, ac,
-				      ocfs2_local_alloc_window_bits(osb),
+	status = ocfs2_claim_clusters(osb, handle, ac, osb->local_alloc_bits,
 				      &cluster_off, &cluster_count);
+	if (status == -ENOSPC) {
+retry_enospc:
+		/*
+		 * Note: We could also try syncing the journal here to
+		 * allow use of any free bits which the current
+		 * transaction can't give us access to. --Mark
+		 */
+		if (ocfs2_recalc_la_window(osb, OCFS2_LA_EVENT_FRAGMENTED) ==
+		    OCFS2_LA_DISABLED)
+			goto bail;
+
+		status = ocfs2_claim_clusters(osb, handle, ac,
+					      osb->local_alloc_bits,
+					      &cluster_off,
+					      &cluster_count);
+		if (status == -ENOSPC)
+			goto retry_enospc;
+		/*
+		 * We only shrunk the *minimum* number of in our
+		 * request - it's entirely possible that the allocator
+		 * might give us more than we asked for.
+		 */
+		if (status == 0) {
+			spin_lock(&osb->osb_lock);
+			osb->local_alloc_bits = cluster_count;
+			spin_unlock(&osb->osb_lock);
+		}
+	}
 	if (status < 0) {
 		if (status != -ENOSPC)
 			mlog_errno(status);
@@ -905,6 +1204,8 @@
 
 	mlog_entry_void();
 
+	ocfs2_recalc_la_window(osb, OCFS2_LA_EVENT_SLIDE);
+
 	/* This will lock the main bitmap for us. */
 	status = ocfs2_local_alloc_reserve_for_window(osb,
 						      &ac,
@@ -976,8 +1277,7 @@
 	if (handle)
 		ocfs2_commit_trans(osb, handle);
 
-	if (main_bm_bh)
-		brelse(main_bm_bh);
+	brelse(main_bm_bh);
 
 	if (main_bm_inode)
 		iput(main_bm_inode);
diff --git a/fs/ocfs2/localalloc.h b/fs/ocfs2/localalloc.h
index 3f76631..ac5ea9f8 100644
--- a/fs/ocfs2/localalloc.h
+++ b/fs/ocfs2/localalloc.h
@@ -52,4 +52,8 @@
 				 u32 *bit_off,
 				 u32 *num_bits);
 
+void ocfs2_local_alloc_seen_free_bits(struct ocfs2_super *osb,
+				      unsigned int num_clusters);
+void ocfs2_la_enable_worker(struct work_struct *work);
+
 #endif /* OCFS2_LOCALALLOC_H */
diff --git a/fs/ocfs2/locks.c b/fs/ocfs2/locks.c
index 203f871..544ac62 100644
--- a/fs/ocfs2/locks.c
+++ b/fs/ocfs2/locks.c
@@ -24,6 +24,7 @@
  */
 
 #include <linux/fs.h>
+#include <linux/fcntl.h>
 
 #define MLOG_MASK_PREFIX ML_INODE
 #include <cluster/masklog.h>
@@ -32,6 +33,7 @@
 
 #include "dlmglue.h"
 #include "file.h"
+#include "inode.h"
 #include "locks.h"
 
 static int ocfs2_do_flock(struct file *file, struct inode *inode,
@@ -123,3 +125,16 @@
 	else
 		return ocfs2_do_flock(file, inode, cmd, fl);
 }
+
+int ocfs2_lock(struct file *file, int cmd, struct file_lock *fl)
+{
+	struct inode *inode = file->f_mapping->host;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+	if (!(fl->fl_flags & FL_POSIX))
+		return -ENOLCK;
+	if (__mandatory_lock(inode))
+		return -ENOLCK;
+
+	return ocfs2_plock(osb->cconn, OCFS2_I(inode)->ip_blkno, file, cmd, fl);
+}
diff --git a/fs/ocfs2/locks.h b/fs/ocfs2/locks.h
index 9743ef2..496d488 100644
--- a/fs/ocfs2/locks.h
+++ b/fs/ocfs2/locks.h
@@ -27,5 +27,6 @@
 #define OCFS2_LOCKS_H
 
 int ocfs2_flock(struct file *file, int cmd, struct file_lock *fl);
+int ocfs2_lock(struct file *file, int cmd, struct file_lock *fl);
 
 #endif /* OCFS2_LOCKS_H */
diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c
index d5d808f..485a6aa 100644
--- a/fs/ocfs2/namei.c
+++ b/fs/ocfs2/namei.c
@@ -60,6 +60,7 @@
 #include "symlink.h"
 #include "sysfile.h"
 #include "uptodate.h"
+#include "xattr.h"
 
 #include "buffer_head_io.h"
 
@@ -327,14 +328,9 @@
 	if (status == -ENOSPC)
 		mlog(0, "Disk is full\n");
 
-	if (new_fe_bh)
-		brelse(new_fe_bh);
-
-	if (de_bh)
-		brelse(de_bh);
-
-	if (parent_fe_bh)
-		brelse(parent_fe_bh);
+	brelse(new_fe_bh);
+	brelse(de_bh);
+	brelse(parent_fe_bh);
 
 	if ((status < 0) && inode)
 		iput(inode);
@@ -647,12 +643,9 @@
 out:
 	ocfs2_inode_unlock(dir, 1);
 
-	if (de_bh)
-		brelse(de_bh);
-	if (fe_bh)
-		brelse(fe_bh);
-	if (parent_fe_bh)
-		brelse(parent_fe_bh);
+	brelse(de_bh);
+	brelse(fe_bh);
+	brelse(parent_fe_bh);
 
 	mlog_exit(err);
 
@@ -851,17 +844,10 @@
 		iput(orphan_dir);
 	}
 
-	if (fe_bh)
-		brelse(fe_bh);
-
-	if (dirent_bh)
-		brelse(dirent_bh);
-
-	if (parent_node_bh)
-		brelse(parent_node_bh);
-
-	if (orphan_entry_bh)
-		brelse(orphan_entry_bh);
+	brelse(fe_bh);
+	brelse(dirent_bh);
+	brelse(parent_node_bh);
+	brelse(orphan_entry_bh);
 
 	mlog_exit(status);
 
@@ -1372,24 +1358,15 @@
 
 	if (new_inode)
 		iput(new_inode);
-	if (newfe_bh)
-		brelse(newfe_bh);
-	if (old_inode_bh)
-		brelse(old_inode_bh);
-	if (old_dir_bh)
-		brelse(old_dir_bh);
-	if (new_dir_bh)
-		brelse(new_dir_bh);
-	if (new_de_bh)
-		brelse(new_de_bh);
-	if (old_de_bh)
-		brelse(old_de_bh);
-	if (old_inode_de_bh)
-		brelse(old_inode_de_bh);
-	if (orphan_entry_bh)
-		brelse(orphan_entry_bh);
-	if (insert_entry_bh)
-		brelse(insert_entry_bh);
+	brelse(newfe_bh);
+	brelse(old_inode_bh);
+	brelse(old_dir_bh);
+	brelse(new_dir_bh);
+	brelse(new_de_bh);
+	brelse(old_de_bh);
+	brelse(old_inode_de_bh);
+	brelse(orphan_entry_bh);
+	brelse(insert_entry_bh);
 
 	mlog_exit(status);
 
@@ -1492,8 +1469,7 @@
 
 	if (bhs) {
 		for(i = 0; i < blocks; i++)
-			if (bhs[i])
-				brelse(bhs[i]);
+			brelse(bhs[i]);
 		kfree(bhs);
 	}
 
@@ -1598,10 +1574,10 @@
 		u32 offset = 0;
 
 		inode->i_op = &ocfs2_symlink_inode_operations;
-		status = ocfs2_do_extend_allocation(osb, inode, &offset, 1, 0,
-						    new_fe_bh,
-						    handle, data_ac, NULL,
-						    NULL);
+		status = ocfs2_add_inode_data(osb, inode, &offset, 1, 0,
+					      new_fe_bh,
+					      handle, data_ac, NULL,
+					      NULL);
 		if (status < 0) {
 			if (status != -ENOSPC && status != -EINTR) {
 				mlog(ML_ERROR,
@@ -1659,12 +1635,9 @@
 
 	ocfs2_inode_unlock(dir, 1);
 
-	if (new_fe_bh)
-		brelse(new_fe_bh);
-	if (parent_fe_bh)
-		brelse(parent_fe_bh);
-	if (de_bh)
-		brelse(de_bh);
+	brelse(new_fe_bh);
+	brelse(parent_fe_bh);
+	brelse(de_bh);
 	if (inode_ac)
 		ocfs2_free_alloc_context(inode_ac);
 	if (data_ac)
@@ -1759,8 +1732,7 @@
 		iput(orphan_dir_inode);
 	}
 
-	if (orphan_dir_bh)
-		brelse(orphan_dir_bh);
+	brelse(orphan_dir_bh);
 
 	mlog_exit(status);
 	return status;
@@ -1780,10 +1752,9 @@
 
 	mlog_entry("(inode->i_ino = %lu)\n", inode->i_ino);
 
-	status = ocfs2_read_block(osb,
+	status = ocfs2_read_block(orphan_dir_inode,
 				  OCFS2_I(orphan_dir_inode)->ip_blkno,
-				  &orphan_dir_bh, OCFS2_BH_CACHED,
-				  orphan_dir_inode);
+				  &orphan_dir_bh);
 	if (status < 0) {
 		mlog_errno(status);
 		goto leave;
@@ -1829,8 +1800,7 @@
 	     (unsigned long long)OCFS2_I(inode)->ip_blkno, osb->slot_num);
 
 leave:
-	if (orphan_dir_bh)
-		brelse(orphan_dir_bh);
+	brelse(orphan_dir_bh);
 
 	mlog_exit(status);
 	return status;
@@ -1898,8 +1868,7 @@
 	}
 
 leave:
-	if (target_de_bh)
-		brelse(target_de_bh);
+	brelse(target_de_bh);
 
 	mlog_exit(status);
 	return status;
@@ -1918,4 +1887,8 @@
 	.setattr	= ocfs2_setattr,
 	.getattr	= ocfs2_getattr,
 	.permission	= ocfs2_permission,
+	.setxattr	= generic_setxattr,
+	.getxattr	= generic_getxattr,
+	.listxattr	= ocfs2_listxattr,
+	.removexattr	= generic_removexattr,
 };
diff --git a/fs/ocfs2/ocfs2.h b/fs/ocfs2/ocfs2.h
index 7f625f2..a21a465 100644
--- a/fs/ocfs2/ocfs2.h
+++ b/fs/ocfs2/ocfs2.h
@@ -34,7 +34,12 @@
 #include <linux/workqueue.h>
 #include <linux/kref.h>
 #include <linux/mutex.h>
-#include <linux/jbd.h>
+#ifndef CONFIG_OCFS2_COMPAT_JBD
+# include <linux/jbd2.h>
+#else
+# include <linux/jbd.h>
+# include "ocfs2_jbd_compat.h"
+#endif
 
 /* For union ocfs2_dlm_lksb */
 #include "stackglue.h"
@@ -171,9 +176,13 @@
 
 enum ocfs2_local_alloc_state
 {
-	OCFS2_LA_UNUSED = 0,
-	OCFS2_LA_ENABLED,
-	OCFS2_LA_DISABLED
+	OCFS2_LA_UNUSED = 0,	/* Local alloc will never be used for
+				 * this mountpoint. */
+	OCFS2_LA_ENABLED,	/* Local alloc is in use. */
+	OCFS2_LA_THROTTLED,	/* Local alloc is in use, but number
+				 * of bits has been reduced. */
+	OCFS2_LA_DISABLED	/* Local alloc has temporarily been
+				 * disabled. */
 };
 
 enum ocfs2_mount_options
@@ -184,6 +193,8 @@
 	OCFS2_MOUNT_ERRORS_PANIC = 1 << 3, /* Panic on errors */
 	OCFS2_MOUNT_DATA_WRITEBACK = 1 << 4, /* No data ordering */
 	OCFS2_MOUNT_LOCALFLOCKS = 1 << 5, /* No cluster aware user file locks */
+	OCFS2_MOUNT_NOUSERXATTR = 1 << 6, /* No user xattr */
+	OCFS2_MOUNT_INODE64 = 1 << 7,	/* Allow inode numbers > 2^32 */
 };
 
 #define OCFS2_OSB_SOFT_RO	0x0001
@@ -214,6 +225,7 @@
 	u32 bitmap_cpg;
 	u8 *uuid;
 	char *uuid_str;
+	u32 uuid_hash;
 	u8 *vol_label;
 	u64 first_cluster_group_blkno;
 	u32 fs_generation;
@@ -241,6 +253,7 @@
 	int s_sectsize_bits;
 	int s_clustersize;
 	int s_clustersize_bits;
+	unsigned int s_xattr_inline_size;
 
 	atomic_t vol_state;
 	struct mutex recovery_lock;
@@ -252,11 +265,27 @@
 	struct ocfs2_journal *journal;
 	unsigned long osb_commit_interval;
 
-	int local_alloc_size;
-	enum ocfs2_local_alloc_state local_alloc_state;
+	struct delayed_work		la_enable_wq;
+
+	/*
+	 * Must hold local alloc i_mutex and osb->osb_lock to change
+	 * local_alloc_bits. Reads can be done under either lock.
+	 */
+	unsigned int local_alloc_bits;
+	unsigned int local_alloc_default_bits;
+
+	enum ocfs2_local_alloc_state local_alloc_state; /* protected
+							 * by osb_lock */
+
 	struct buffer_head *local_alloc_bh;
+
 	u64 la_last_gd;
 
+#ifdef CONFIG_OCFS2_FS_STATS
+	struct dentry *local_alloc_debug;
+	char *local_alloc_debug_buf;
+#endif
+
 	/* Next two fields are for local node slot recovery during
 	 * mount. */
 	int dirty;
@@ -340,6 +369,13 @@
 	return 0;
 }
 
+static inline int ocfs2_supports_xattr(struct ocfs2_super *osb)
+{
+	if (osb->s_feature_incompat & OCFS2_FEATURE_INCOMPAT_XATTR)
+		return 1;
+	return 0;
+}
+
 /* set / clear functions because cluster events can make these happen
  * in parallel so we want the transitions to be atomic. this also
  * means that any future flags osb_flags must be protected by spinlock
@@ -554,6 +590,14 @@
 	return pages_per_cluster;
 }
 
+static inline unsigned int ocfs2_megabytes_to_clusters(struct super_block *sb,
+						       unsigned int megs)
+{
+	BUILD_BUG_ON(OCFS2_MAX_CLUSTERSIZE > 1048576);
+
+	return megs << (20 - OCFS2_SB(sb)->s_clustersize_bits);
+}
+
 static inline void ocfs2_init_inode_steal_slot(struct ocfs2_super *osb)
 {
 	spin_lock(&osb->osb_lock);
diff --git a/fs/ocfs2/ocfs2_fs.h b/fs/ocfs2/ocfs2_fs.h
index 4f61985..f24ce3d 100644
--- a/fs/ocfs2/ocfs2_fs.h
+++ b/fs/ocfs2/ocfs2_fs.h
@@ -64,6 +64,7 @@
 #define OCFS2_INODE_SIGNATURE		"INODE01"
 #define OCFS2_EXTENT_BLOCK_SIGNATURE	"EXBLK01"
 #define OCFS2_GROUP_DESC_SIGNATURE      "GROUP01"
+#define OCFS2_XATTR_BLOCK_SIGNATURE	"XATTR01"
 
 /* Compatibility flags */
 #define OCFS2_HAS_COMPAT_FEATURE(sb,mask)			\
@@ -90,7 +91,8 @@
 					 | OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC \
 					 | OCFS2_FEATURE_INCOMPAT_INLINE_DATA \
 					 | OCFS2_FEATURE_INCOMPAT_EXTENDED_SLOT_MAP \
-					 | OCFS2_FEATURE_INCOMPAT_USERSPACE_STACK)
+					 | OCFS2_FEATURE_INCOMPAT_USERSPACE_STACK \
+					 | OCFS2_FEATURE_INCOMPAT_XATTR)
 #define OCFS2_FEATURE_RO_COMPAT_SUPP	OCFS2_FEATURE_RO_COMPAT_UNWRITTEN
 
 /*
@@ -127,10 +129,6 @@
 /* Support for data packed into inode blocks */
 #define OCFS2_FEATURE_INCOMPAT_INLINE_DATA	0x0040
 
-/* Support for the extended slot map */
-#define OCFS2_FEATURE_INCOMPAT_EXTENDED_SLOT_MAP 0x100
-
-
 /*
  * Support for alternate, userspace cluster stacks.  If set, the superblock
  * field s_cluster_info contains a tag for the alternate stack in use as
@@ -142,6 +140,12 @@
  */
 #define OCFS2_FEATURE_INCOMPAT_USERSPACE_STACK	0x0080
 
+/* Support for the extended slot map */
+#define OCFS2_FEATURE_INCOMPAT_EXTENDED_SLOT_MAP 0x100
+
+/* Support for extended attributes */
+#define OCFS2_FEATURE_INCOMPAT_XATTR		0x0200
+
 /*
  * backup superblock flag is used to indicate that this volume
  * has backup superblocks.
@@ -299,6 +303,12 @@
  */
 #define OCFS2_DEFAULT_LOCAL_ALLOC_SIZE	8
 
+/*
+ * Inline extended attribute size (in bytes)
+ * The value chosen should be aligned to 16 byte boundaries.
+ */
+#define OCFS2_MIN_XATTR_INLINE_SIZE     256
+
 struct ocfs2_system_inode_info {
 	char	*si_name;
 	int	si_iflags;
@@ -563,7 +573,7 @@
 /*40*/	__le16 s_max_slots;		/* Max number of simultaneous mounts
 					   before tunefs required */
 	__le16 s_tunefs_flag;
-	__le32 s_reserved1;
+	__le32 s_uuid_hash;		/* hash value of uuid */
 	__le64 s_first_cluster_group;	/* Block offset of 1st cluster
 					 * group header */
 /*50*/	__u8  s_label[OCFS2_MAX_VOL_LABEL_LEN];	/* Label for mounting, etc. */
@@ -571,7 +581,11 @@
 /*A0*/  struct ocfs2_cluster_info s_cluster_info; /* Selected userspace
 						     stack.  Only valid
 						     with INCOMPAT flag. */
-/*B8*/  __le64 s_reserved2[17];		/* Fill out superblock */
+/*B8*/	__le16 s_xattr_inline_size;	/* extended attribute inline size
+					   for this fs*/
+	__le16 s_reserved0;
+	__le32 s_reserved1;
+/*C0*/  __le64 s_reserved2[16];		/* Fill out superblock */
 /*140*/
 
 	/*
@@ -621,7 +635,8 @@
 					   belongs to */
 	__le16 i_suballoc_bit;		/* Bit offset in suballocator
 					   block group */
-/*10*/	__le32 i_reserved0;
+/*10*/	__le16 i_reserved0;
+	__le16 i_xattr_inline_size;
 	__le32 i_clusters;		/* Cluster count */
 	__le32 i_uid;			/* Owner UID */
 	__le32 i_gid;			/* Owning GID */
@@ -640,11 +655,12 @@
 	__le32 i_atime_nsec;
 	__le32 i_ctime_nsec;
 	__le32 i_mtime_nsec;
-	__le32 i_attr;
+/*70*/	__le32 i_attr;
 	__le16 i_orphaned_slot;		/* Only valid when OCFS2_ORPHANED_FL
 					   was set in i_flags */
 	__le16 i_dyn_features;
-/*70*/	__le64 i_reserved2[8];
+	__le64 i_xattr_loc;
+/*80*/	__le64 i_reserved2[7];
 /*B8*/	union {
 		__le64 i_pad1;		/* Generic way to refer to this
 					   64bit union */
@@ -715,6 +731,136 @@
 /*40*/	__u8    bg_bitmap[0];
 };
 
+/*
+ * On disk extended attribute structure for OCFS2.
+ */
+
+/*
+ * ocfs2_xattr_entry indicates one extend attribute.
+ *
+ * Note that it can be stored in inode, one block or one xattr bucket.
+ */
+struct ocfs2_xattr_entry {
+	__le32	xe_name_hash;    /* hash value of xattr prefix+suffix. */
+	__le16	xe_name_offset;  /* byte offset from the 1st etnry in the local
+				    local xattr storage(inode, xattr block or
+				    xattr bucket). */
+	__u8	xe_name_len;	 /* xattr name len, does't include prefix. */
+	__u8	xe_type;         /* the low 7 bits indicates the name prefix's
+				  * type and the highest 1 bits indicate whether
+				  * the EA is stored in the local storage. */
+	__le64	xe_value_size;	 /* real xattr value length. */
+};
+
+/*
+ * On disk structure for xattr header.
+ *
+ * One ocfs2_xattr_header describes how many ocfs2_xattr_entry records in
+ * the local xattr storage.
+ */
+struct ocfs2_xattr_header {
+	__le16	xh_count;                       /* contains the count of how
+						   many records are in the
+						   local xattr storage. */
+	__le16	xh_free_start;                  /* current offset for storing
+						   xattr. */
+	__le16	xh_name_value_len;              /* total length of name/value
+						   length in this bucket. */
+	__le16	xh_num_buckets;                 /* bucket nums in one extent
+						   record, only valid in the
+						   first bucket. */
+	__le64  xh_csum;
+	struct ocfs2_xattr_entry xh_entries[0]; /* xattr entry list. */
+};
+
+/*
+ * On disk structure for xattr value root.
+ *
+ * It is used when one extended attribute's size is larger, and we will save it
+ * in an outside cluster. It will stored in a b-tree like file content.
+ */
+struct ocfs2_xattr_value_root {
+/*00*/	__le32	xr_clusters;              /* clusters covered by xattr value. */
+	__le32	xr_reserved0;
+	__le64	xr_last_eb_blk;           /* Pointer to last extent block */
+/*10*/	struct ocfs2_extent_list xr_list; /* Extent record list */
+};
+
+/*
+ * On disk structure for xattr tree root.
+ *
+ * It is used when there are too many extended attributes for one file. These
+ * attributes will be organized and stored in an indexed-btree.
+ */
+struct ocfs2_xattr_tree_root {
+/*00*/	__le32	xt_clusters;              /* clusters covered by xattr. */
+	__le32	xt_reserved0;
+	__le64	xt_last_eb_blk;           /* Pointer to last extent block */
+/*10*/	struct ocfs2_extent_list xt_list; /* Extent record list */
+};
+
+#define OCFS2_XATTR_INDEXED	0x1
+#define OCFS2_HASH_SHIFT	5
+#define OCFS2_XATTR_ROUND	3
+#define OCFS2_XATTR_SIZE(size)	(((size) + OCFS2_XATTR_ROUND) & \
+				~(OCFS2_XATTR_ROUND))
+
+#define OCFS2_XATTR_BUCKET_SIZE			4096
+#define OCFS2_XATTR_MAX_BLOCKS_PER_BUCKET 	(OCFS2_XATTR_BUCKET_SIZE \
+						 / OCFS2_MIN_BLOCKSIZE)
+
+/*
+ * On disk structure for xattr block.
+ */
+struct ocfs2_xattr_block {
+/*00*/	__u8	xb_signature[8];     /* Signature for verification */
+	__le16	xb_suballoc_slot;    /* Slot suballocator this
+					block belongs to. */
+	__le16	xb_suballoc_bit;     /* Bit offset in suballocator
+					block group */
+	__le32	xb_fs_generation;    /* Must match super block */
+/*10*/	__le64	xb_blkno;            /* Offset on disk, in blocks */
+	__le64	xb_csum;
+/*20*/	__le16	xb_flags;            /* Indicates whether this block contains
+					real xattr or a xattr tree. */
+	__le16	xb_reserved0;
+	__le32  xb_reserved1;
+	__le64	xb_reserved2;
+/*30*/	union {
+		struct ocfs2_xattr_header xb_header; /* xattr header if this
+							block contains xattr */
+		struct ocfs2_xattr_tree_root xb_root;/* xattr tree root if this
+							block cotains xattr
+							tree. */
+	} xb_attrs;
+};
+
+#define OCFS2_XATTR_ENTRY_LOCAL		0x80
+#define OCFS2_XATTR_TYPE_MASK		0x7F
+static inline void ocfs2_xattr_set_local(struct ocfs2_xattr_entry *xe,
+					 int local)
+{
+	if (local)
+		xe->xe_type |= OCFS2_XATTR_ENTRY_LOCAL;
+	else
+		xe->xe_type &= ~OCFS2_XATTR_ENTRY_LOCAL;
+}
+
+static inline int ocfs2_xattr_is_local(struct ocfs2_xattr_entry *xe)
+{
+	return xe->xe_type & OCFS2_XATTR_ENTRY_LOCAL;
+}
+
+static inline void ocfs2_xattr_set_type(struct ocfs2_xattr_entry *xe, int type)
+{
+	xe->xe_type |= type & OCFS2_XATTR_TYPE_MASK;
+}
+
+static inline int ocfs2_xattr_get_type(struct ocfs2_xattr_entry *xe)
+{
+	return xe->xe_type & OCFS2_XATTR_TYPE_MASK;
+}
+
 #ifdef __KERNEL__
 static inline int ocfs2_fast_symlink_chars(struct super_block *sb)
 {
@@ -728,6 +874,20 @@
 		offsetof(struct ocfs2_dinode, id2.i_data.id_data);
 }
 
+static inline int ocfs2_max_inline_data_with_xattr(struct super_block *sb,
+						   struct ocfs2_dinode *di)
+{
+	unsigned int xattrsize = le16_to_cpu(di->i_xattr_inline_size);
+
+	if (le16_to_cpu(di->i_dyn_features) & OCFS2_INLINE_XATTR_FL)
+		return sb->s_blocksize -
+			offsetof(struct ocfs2_dinode, id2.i_data.id_data) -
+			xattrsize;
+	else
+		return sb->s_blocksize -
+			offsetof(struct ocfs2_dinode, id2.i_data.id_data);
+}
+
 static inline int ocfs2_extent_recs_per_inode(struct super_block *sb)
 {
 	int size;
@@ -738,6 +898,24 @@
 	return size / sizeof(struct ocfs2_extent_rec);
 }
 
+static inline int ocfs2_extent_recs_per_inode_with_xattr(
+						struct super_block *sb,
+						struct ocfs2_dinode *di)
+{
+	int size;
+	unsigned int xattrsize = le16_to_cpu(di->i_xattr_inline_size);
+
+	if (le16_to_cpu(di->i_dyn_features) & OCFS2_INLINE_XATTR_FL)
+		size = sb->s_blocksize -
+			offsetof(struct ocfs2_dinode, id2.i_list.l_recs) -
+			xattrsize;
+	else
+		size = sb->s_blocksize -
+			offsetof(struct ocfs2_dinode, id2.i_list.l_recs);
+
+	return size / sizeof(struct ocfs2_extent_rec);
+}
+
 static inline int ocfs2_chain_recs_per_inode(struct super_block *sb)
 {
 	int size;
@@ -801,6 +979,17 @@
 	return 0;
 
 }
+
+static inline u16 ocfs2_xattr_recs_per_xb(struct super_block *sb)
+{
+	int size;
+
+	size = sb->s_blocksize -
+		offsetof(struct ocfs2_xattr_block,
+			 xb_attrs.xb_root.xt_list.l_recs);
+
+	return size / sizeof(struct ocfs2_extent_rec);
+}
 #else
 static inline int ocfs2_fast_symlink_chars(int blocksize)
 {
@@ -884,6 +1073,17 @@
 
 	return 0;
 }
+
+static inline int ocfs2_xattr_recs_per_xb(int blocksize)
+{
+	int size;
+
+	size = blocksize -
+		offsetof(struct ocfs2_xattr_block,
+			 xb_attrs.xb_root.xt_list.l_recs);
+
+	return size / sizeof(struct ocfs2_extent_rec);
+}
 #endif  /* __KERNEL__ */
 
 
diff --git a/fs/ocfs2/ocfs2_jbd_compat.h b/fs/ocfs2/ocfs2_jbd_compat.h
new file mode 100644
index 0000000..b91c78f
--- /dev/null
+++ b/fs/ocfs2/ocfs2_jbd_compat.h
@@ -0,0 +1,82 @@
+/* -*- mode: c; c-basic-offset: 8; -*-
+ * vim: noexpandtab sw=8 ts=8 sts=0:
+ *
+ * ocfs2_jbd_compat.h
+ *
+ * Compatibility defines for JBD.
+ *
+ * Copyright (C) 2008 Oracle.  All rights reserved.
+ *
+ * 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.
+ */
+
+#ifndef OCFS2_JBD_COMPAT_H
+#define OCFS2_JBD_COMPAT_H
+
+#ifndef CONFIG_OCFS2_COMPAT_JBD
+# error Should not have been included
+#endif
+
+struct jbd2_inode {
+	unsigned int dummy;
+};
+
+#define JBD2_BARRIER			JFS_BARRIER
+#define JBD2_DEFAULT_MAX_COMMIT_AGE	JBD_DEFAULT_MAX_COMMIT_AGE
+
+#define jbd2_journal_ack_err			journal_ack_err
+#define jbd2_journal_clear_err			journal_clear_err
+#define jbd2_journal_destroy			journal_destroy
+#define jbd2_journal_dirty_metadata		journal_dirty_metadata
+#define jbd2_journal_errno			journal_errno
+#define jbd2_journal_extend			journal_extend
+#define jbd2_journal_flush			journal_flush
+#define jbd2_journal_force_commit		journal_force_commit
+#define jbd2_journal_get_write_access		journal_get_write_access
+#define jbd2_journal_get_undo_access		journal_get_undo_access
+#define jbd2_journal_init_inode			journal_init_inode
+#define jbd2_journal_invalidatepage		journal_invalidatepage
+#define jbd2_journal_load			journal_load
+#define jbd2_journal_lock_updates		journal_lock_updates
+#define jbd2_journal_restart			journal_restart
+#define jbd2_journal_start			journal_start
+#define jbd2_journal_start_commit		journal_start_commit
+#define jbd2_journal_stop			journal_stop
+#define jbd2_journal_try_to_free_buffers	journal_try_to_free_buffers
+#define jbd2_journal_unlock_updates		journal_unlock_updates
+#define jbd2_journal_wipe			journal_wipe
+#define jbd2_log_wait_commit			log_wait_commit
+
+static inline int jbd2_journal_file_inode(handle_t *handle,
+					  struct jbd2_inode *inode)
+{
+	return 0;
+}
+
+static inline int jbd2_journal_begin_ordered_truncate(struct jbd2_inode *inode,
+						      loff_t new_size)
+{
+	return 0;
+}
+
+static inline void jbd2_journal_init_jbd_inode(struct jbd2_inode *jinode,
+					       struct inode *inode)
+{
+	return;
+}
+
+static inline void jbd2_journal_release_jbd_inode(journal_t *journal,
+						  struct jbd2_inode *jinode)
+{
+	return;
+}
+
+
+#endif  /* OCFS2_JBD_COMPAT_H */
diff --git a/fs/ocfs2/resize.c b/fs/ocfs2/resize.c
index 8166968..ffd48db 100644
--- a/fs/ocfs2/resize.c
+++ b/fs/ocfs2/resize.c
@@ -200,7 +200,7 @@
 		if (cluster > clusters)
 			break;
 
-		ret = ocfs2_read_block(osb, blkno, &backup, 0, NULL);
+		ret = ocfs2_read_blocks_sync(osb, blkno, 1, &backup);
 		if (ret < 0) {
 			mlog_errno(ret);
 			break;
@@ -236,8 +236,8 @@
 	 * update the superblock last.
 	 * It doesn't matter if the write failed.
 	 */
-	ret = ocfs2_read_block(osb, OCFS2_SUPER_BLOCK_BLKNO,
-			       &super_bh, 0, NULL);
+	ret = ocfs2_read_blocks_sync(osb, OCFS2_SUPER_BLOCK_BLKNO, 1,
+				     &super_bh);
 	if (ret < 0) {
 		mlog_errno(ret);
 		goto out;
@@ -332,8 +332,7 @@
 	lgd_blkno = ocfs2_which_cluster_group(main_bm_inode,
 					      first_new_cluster - 1);
 
-	ret = ocfs2_read_block(osb, lgd_blkno, &group_bh, OCFS2_BH_CACHED,
-			       main_bm_inode);
+	ret = ocfs2_read_block(main_bm_inode, lgd_blkno, &group_bh);
 	if (ret < 0) {
 		mlog_errno(ret);
 		goto out_unlock;
@@ -540,7 +539,7 @@
 		goto out_unlock;
 	}
 
-	ret = ocfs2_read_block(osb, input->group, &group_bh, 0, NULL);
+	ret = ocfs2_read_blocks_sync(osb, input->group, 1, &group_bh);
 	if (ret < 0) {
 		mlog(ML_ERROR, "Can't read the group descriptor # %llu "
 		     "from the device.", (unsigned long long)input->group);
diff --git a/fs/ocfs2/slot_map.c b/fs/ocfs2/slot_map.c
index bb5ff89..bdda2d8 100644
--- a/fs/ocfs2/slot_map.c
+++ b/fs/ocfs2/slot_map.c
@@ -150,8 +150,8 @@
 	 * be !NULL.  Thus, ocfs2_read_blocks() will ignore blocknr.  If
 	 * this is not true, the read of -1 (UINT64_MAX) will fail.
 	 */
-	ret = ocfs2_read_blocks(osb, -1, si->si_blocks, si->si_bh, 0,
-				si->si_inode);
+	ret = ocfs2_read_blocks(si->si_inode, -1, si->si_blocks, si->si_bh,
+				OCFS2_BH_IGNORE_CACHE);
 	if (ret == 0) {
 		spin_lock(&osb->osb_lock);
 		ocfs2_update_slot_info(si);
@@ -404,7 +404,8 @@
 		     (unsigned long long)blkno);
 
 		bh = NULL;  /* Acquire a fresh bh */
-		status = ocfs2_read_block(osb, blkno, &bh, 0, si->si_inode);
+		status = ocfs2_read_blocks(si->si_inode, blkno, 1, &bh,
+					   OCFS2_BH_IGNORE_CACHE);
 		if (status < 0) {
 			mlog_errno(status);
 			goto bail;
diff --git a/fs/ocfs2/stack_user.c b/fs/ocfs2/stack_user.c
index 353fc35..faec2d8 100644
--- a/fs/ocfs2/stack_user.c
+++ b/fs/ocfs2/stack_user.c
@@ -28,6 +28,7 @@
 #include "ocfs2.h"  /* For struct ocfs2_lock_res */
 #include "stackglue.h"
 
+#include <linux/dlm_plock.h>
 
 /*
  * The control protocol starts with a handshake.  Until the handshake
@@ -746,6 +747,37 @@
 {
 }
 
+static int user_plock(struct ocfs2_cluster_connection *conn,
+		      u64 ino,
+		      struct file *file,
+		      int cmd,
+		      struct file_lock *fl)
+{
+	/*
+	 * This more or less just demuxes the plock request into any
+	 * one of three dlm calls.
+	 *
+	 * Internally, fs/dlm will pass these to a misc device, which
+	 * a userspace daemon will read and write to.
+	 *
+	 * For now, cancel requests (which happen internally only),
+	 * are turned into unlocks. Most of this function taken from
+	 * gfs2_lock.
+	 */
+
+	if (cmd == F_CANCELLK) {
+		cmd = F_SETLK;
+		fl->fl_type = F_UNLCK;
+	}
+
+	if (IS_GETLK(cmd))
+		return dlm_posix_get(conn->cc_lockspace, ino, file, fl);
+	else if (fl->fl_type == F_UNLCK)
+		return dlm_posix_unlock(conn->cc_lockspace, ino, file, fl);
+	else
+		return dlm_posix_lock(conn->cc_lockspace, ino, file, cmd, fl);
+}
+
 /*
  * Compare a requested locking protocol version against the current one.
  *
@@ -839,6 +871,7 @@
 	.dlm_unlock	= user_dlm_unlock,
 	.lock_status	= user_dlm_lock_status,
 	.lock_lvb	= user_dlm_lvb,
+	.plock		= user_plock,
 	.dump_lksb	= user_dlm_dump_lksb,
 };
 
diff --git a/fs/ocfs2/stackglue.c b/fs/ocfs2/stackglue.c
index 07f348b..68b668b 100644
--- a/fs/ocfs2/stackglue.c
+++ b/fs/ocfs2/stackglue.c
@@ -288,6 +288,26 @@
 }
 EXPORT_SYMBOL_GPL(ocfs2_dlm_dump_lksb);
 
+int ocfs2_stack_supports_plocks(void)
+{
+	return active_stack && active_stack->sp_ops->plock;
+}
+EXPORT_SYMBOL_GPL(ocfs2_stack_supports_plocks);
+
+/*
+ * ocfs2_plock() can only be safely called if
+ * ocfs2_stack_supports_plocks() returned true
+ */
+int ocfs2_plock(struct ocfs2_cluster_connection *conn, u64 ino,
+		struct file *file, int cmd, struct file_lock *fl)
+{
+	WARN_ON_ONCE(active_stack->sp_ops->plock == NULL);
+	if (active_stack->sp_ops->plock)
+		return active_stack->sp_ops->plock(conn, ino, file, cmd, fl);
+	return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL_GPL(ocfs2_plock);
+
 int ocfs2_cluster_connect(const char *stack_name,
 			  const char *group,
 			  int grouplen,
diff --git a/fs/ocfs2/stackglue.h b/fs/ocfs2/stackglue.h
index db56281..c571af3 100644
--- a/fs/ocfs2/stackglue.h
+++ b/fs/ocfs2/stackglue.h
@@ -28,6 +28,10 @@
 #include "dlm/dlmapi.h"
 #include <linux/dlm.h>
 
+/* Needed for plock-related prototypes */
+struct file;
+struct file_lock;
+
 /*
  * dlmconstants.h does not have a LOCAL flag.  We hope to remove it
  * some day, but right now we need it.  Let's fake it.  This value is larger
@@ -187,6 +191,17 @@
 	void *(*lock_lvb)(union ocfs2_dlm_lksb *lksb);
 
 	/*
+	 * Cluster-aware posix locks
+	 *
+	 * This is NULL for stacks which do not support posix locks.
+	 */
+	int (*plock)(struct ocfs2_cluster_connection *conn,
+		     u64 ino,
+		     struct file *file,
+		     int cmd,
+		     struct file_lock *fl);
+
+	/*
 	 * This is an optoinal debugging hook.  If provided, the
 	 * stack can dump debugging information about this lock.
 	 */
@@ -240,6 +255,10 @@
 void *ocfs2_dlm_lvb(union ocfs2_dlm_lksb *lksb);
 void ocfs2_dlm_dump_lksb(union ocfs2_dlm_lksb *lksb);
 
+int ocfs2_stack_supports_plocks(void);
+int ocfs2_plock(struct ocfs2_cluster_connection *conn, u64 ino,
+		struct file *file, int cmd, struct file_lock *fl);
+
 void ocfs2_stack_glue_set_locking_protocol(struct ocfs2_locking_protocol *proto);
 
 
diff --git a/fs/ocfs2/suballoc.c b/fs/ocfs2/suballoc.c
index d2d278f..c5ff18b 100644
--- a/fs/ocfs2/suballoc.c
+++ b/fs/ocfs2/suballoc.c
@@ -62,15 +62,18 @@
 				  struct ocfs2_chain_list *cl);
 static int ocfs2_block_group_alloc(struct ocfs2_super *osb,
 				   struct inode *alloc_inode,
-				   struct buffer_head *bh);
+				   struct buffer_head *bh,
+				   u64 max_block);
 
 static int ocfs2_cluster_group_search(struct inode *inode,
 				      struct buffer_head *group_bh,
 				      u32 bits_wanted, u32 min_bits,
+				      u64 max_block,
 				      u16 *bit_off, u16 *bits_found);
 static int ocfs2_block_group_search(struct inode *inode,
 				    struct buffer_head *group_bh,
 				    u32 bits_wanted, u32 min_bits,
+				    u64 max_block,
 				    u16 *bit_off, u16 *bits_found);
 static int ocfs2_claim_suballoc_bits(struct ocfs2_super *osb,
 				     struct ocfs2_alloc_context *ac,
@@ -110,8 +113,11 @@
 						u64 data_blkno,
 						u64 *bg_blkno,
 						u16 *bg_bit_off);
+static int ocfs2_reserve_clusters_with_limit(struct ocfs2_super *osb,
+					     u32 bits_wanted, u64 max_block,
+					     struct ocfs2_alloc_context **ac);
 
-static void ocfs2_free_ac_resource(struct ocfs2_alloc_context *ac)
+void ocfs2_free_ac_resource(struct ocfs2_alloc_context *ac)
 {
 	struct inode *inode = ac->ac_inode;
 
@@ -124,10 +130,8 @@
 		iput(inode);
 		ac->ac_inode = NULL;
 	}
-	if (ac->ac_bh) {
-		brelse(ac->ac_bh);
-		ac->ac_bh = NULL;
-	}
+	brelse(ac->ac_bh);
+	ac->ac_bh = NULL;
 }
 
 void ocfs2_free_alloc_context(struct ocfs2_alloc_context *ac)
@@ -276,7 +280,8 @@
  */
 static int ocfs2_block_group_alloc(struct ocfs2_super *osb,
 				   struct inode *alloc_inode,
-				   struct buffer_head *bh)
+				   struct buffer_head *bh,
+				   u64 max_block)
 {
 	int status, credits;
 	struct ocfs2_dinode *fe = (struct ocfs2_dinode *) bh->b_data;
@@ -294,9 +299,9 @@
 	mlog_entry_void();
 
 	cl = &fe->id2.i_chain;
-	status = ocfs2_reserve_clusters(osb,
-					le16_to_cpu(cl->cl_cpg),
-					&ac);
+	status = ocfs2_reserve_clusters_with_limit(osb,
+						   le16_to_cpu(cl->cl_cpg),
+						   max_block, &ac);
 	if (status < 0) {
 		if (status != -ENOSPC)
 			mlog_errno(status);
@@ -394,8 +399,7 @@
 	if (ac)
 		ocfs2_free_alloc_context(ac);
 
-	if (bg_bh)
-		brelse(bg_bh);
+	brelse(bg_bh);
 
 	mlog_exit(status);
 	return status;
@@ -469,7 +473,8 @@
 			goto bail;
 		}
 
-		status = ocfs2_block_group_alloc(osb, alloc_inode, bh);
+		status = ocfs2_block_group_alloc(osb, alloc_inode, bh,
+						 ac->ac_max_block);
 		if (status < 0) {
 			if (status != -ENOSPC)
 				mlog_errno(status);
@@ -486,16 +491,15 @@
 	get_bh(bh);
 	ac->ac_bh = bh;
 bail:
-	if (bh)
-		brelse(bh);
+	brelse(bh);
 
 	mlog_exit(status);
 	return status;
 }
 
-int ocfs2_reserve_new_metadata(struct ocfs2_super *osb,
-			       struct ocfs2_dinode *fe,
-			       struct ocfs2_alloc_context **ac)
+int ocfs2_reserve_new_metadata_blocks(struct ocfs2_super *osb,
+				      int blocks,
+				      struct ocfs2_alloc_context **ac)
 {
 	int status;
 	u32 slot;
@@ -507,7 +511,7 @@
 		goto bail;
 	}
 
-	(*ac)->ac_bits_wanted = ocfs2_extend_meta_needed(fe);
+	(*ac)->ac_bits_wanted = blocks;
 	(*ac)->ac_which = OCFS2_AC_USE_META;
 	slot = osb->slot_num;
 	(*ac)->ac_group_search = ocfs2_block_group_search;
@@ -532,6 +536,15 @@
 	return status;
 }
 
+int ocfs2_reserve_new_metadata(struct ocfs2_super *osb,
+			       struct ocfs2_extent_list *root_el,
+			       struct ocfs2_alloc_context **ac)
+{
+	return ocfs2_reserve_new_metadata_blocks(osb,
+					ocfs2_extend_meta_needed(root_el),
+					ac);
+}
+
 static int ocfs2_steal_inode_from_other_nodes(struct ocfs2_super *osb,
 					      struct ocfs2_alloc_context *ac)
 {
@@ -582,6 +595,14 @@
 	(*ac)->ac_group_search = ocfs2_block_group_search;
 
 	/*
+	 * stat(2) can't handle i_ino > 32bits, so we tell the
+	 * lower levels not to allocate us a block group past that
+	 * limit.  The 'inode64' mount option avoids this behavior.
+	 */
+	if (!(osb->s_mount_opt & OCFS2_MOUNT_INODE64))
+		(*ac)->ac_max_block = (u32)~0U;
+
+	/*
 	 * slot is set when we successfully steal inode from other nodes.
 	 * It is reset in 3 places:
 	 * 1. when we flush the truncate log
@@ -661,9 +682,9 @@
 /* Callers don't need to care which bitmap (local alloc or main) to
  * use so we figure it out for them, but unfortunately this clutters
  * things a bit. */
-int ocfs2_reserve_clusters(struct ocfs2_super *osb,
-			   u32 bits_wanted,
-			   struct ocfs2_alloc_context **ac)
+static int ocfs2_reserve_clusters_with_limit(struct ocfs2_super *osb,
+					     u32 bits_wanted, u64 max_block,
+					     struct ocfs2_alloc_context **ac)
 {
 	int status;
 
@@ -677,24 +698,20 @@
 	}
 
 	(*ac)->ac_bits_wanted = bits_wanted;
+	(*ac)->ac_max_block = max_block;
 
 	status = -ENOSPC;
 	if (ocfs2_alloc_should_use_local(osb, bits_wanted)) {
 		status = ocfs2_reserve_local_alloc_bits(osb,
 							bits_wanted,
 							*ac);
-		if ((status < 0) && (status != -ENOSPC)) {
+		if (status == -EFBIG) {
+			/* The local alloc window is outside ac_max_block.
+			 * use the main bitmap. */
+			status = -ENOSPC;
+		} else if ((status < 0) && (status != -ENOSPC)) {
 			mlog_errno(status);
 			goto bail;
-		} else if (status == -ENOSPC) {
-			/* reserve_local_bits will return enospc with
-			 * the local alloc inode still locked, so we
-			 * can change this safely here. */
-			mlog(0, "Disabling local alloc\n");
-			/* We set to OCFS2_LA_DISABLED so that umount
-			 * can clean up what's left of the local
-			 * allocation */
-			osb->local_alloc_state = OCFS2_LA_DISABLED;
 		}
 	}
 
@@ -718,6 +735,13 @@
 	return status;
 }
 
+int ocfs2_reserve_clusters(struct ocfs2_super *osb,
+			   u32 bits_wanted,
+			   struct ocfs2_alloc_context **ac)
+{
+	return ocfs2_reserve_clusters_with_limit(osb, bits_wanted, 0, ac);
+}
+
 /*
  * More or less lifted from ext3. I'll leave their description below:
  *
@@ -1000,11 +1024,14 @@
 static int ocfs2_cluster_group_search(struct inode *inode,
 				      struct buffer_head *group_bh,
 				      u32 bits_wanted, u32 min_bits,
+				      u64 max_block,
 				      u16 *bit_off, u16 *bits_found)
 {
 	int search = -ENOSPC;
 	int ret;
+	u64 blkoff;
 	struct ocfs2_group_desc *gd = (struct ocfs2_group_desc *) group_bh->b_data;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
 	u16 tmp_off, tmp_found;
 	unsigned int max_bits, gd_cluster_off;
 
@@ -1037,6 +1064,17 @@
 		if (ret)
 			return ret;
 
+		if (max_block) {
+			blkoff = ocfs2_clusters_to_blocks(inode->i_sb,
+							  gd_cluster_off +
+							  tmp_off + tmp_found);
+			mlog(0, "Checking %llu against %llu\n",
+			     (unsigned long long)blkoff,
+			     (unsigned long long)max_block);
+			if (blkoff > max_block)
+				return -ENOSPC;
+		}
+
 		/* ocfs2_block_group_find_clear_bits() might
 		 * return success, but we still want to return
 		 * -ENOSPC unless it found the minimum number
@@ -1045,6 +1083,12 @@
 			*bit_off = tmp_off;
 			*bits_found = tmp_found;
 			search = 0; /* success */
+		} else if (tmp_found) {
+			/*
+			 * Don't show bits which we'll be returning
+			 * for allocation to the local alloc bitmap.
+			 */
+			ocfs2_local_alloc_seen_free_bits(osb, tmp_found);
 		}
 	}
 
@@ -1054,19 +1098,31 @@
 static int ocfs2_block_group_search(struct inode *inode,
 				    struct buffer_head *group_bh,
 				    u32 bits_wanted, u32 min_bits,
+				    u64 max_block,
 				    u16 *bit_off, u16 *bits_found)
 {
 	int ret = -ENOSPC;
+	u64 blkoff;
 	struct ocfs2_group_desc *bg = (struct ocfs2_group_desc *) group_bh->b_data;
 
 	BUG_ON(min_bits != 1);
 	BUG_ON(ocfs2_is_cluster_bitmap(inode));
 
-	if (bg->bg_free_bits_count)
+	if (bg->bg_free_bits_count) {
 		ret = ocfs2_block_group_find_clear_bits(OCFS2_SB(inode->i_sb),
 							group_bh, bits_wanted,
 							le16_to_cpu(bg->bg_bits),
 							bit_off, bits_found);
+		if (!ret && max_block) {
+			blkoff = le64_to_cpu(bg->bg_blkno) + *bit_off +
+				*bits_found;
+			mlog(0, "Checking %llu against %llu\n",
+			     (unsigned long long)blkoff,
+			     (unsigned long long)max_block);
+			if (blkoff > max_block)
+				ret = -ENOSPC;
+		}
+	}
 
 	return ret;
 }
@@ -1116,8 +1172,7 @@
 	struct ocfs2_group_desc *gd;
 	struct inode *alloc_inode = ac->ac_inode;
 
-	ret = ocfs2_read_block(OCFS2_SB(alloc_inode->i_sb), gd_blkno,
-			       &group_bh, OCFS2_BH_CACHED, alloc_inode);
+	ret = ocfs2_read_block(alloc_inode, gd_blkno, &group_bh);
 	if (ret < 0) {
 		mlog_errno(ret);
 		return ret;
@@ -1131,7 +1186,7 @@
 	}
 
 	ret = ac->ac_group_search(alloc_inode, group_bh, bits_wanted, min_bits,
-				  bit_off, &found);
+				  ac->ac_max_block, bit_off, &found);
 	if (ret < 0) {
 		if (ret != -ENOSPC)
 			mlog_errno(ret);
@@ -1186,9 +1241,9 @@
 	     bits_wanted, chain,
 	     (unsigned long long)OCFS2_I(alloc_inode)->ip_blkno);
 
-	status = ocfs2_read_block(OCFS2_SB(alloc_inode->i_sb),
+	status = ocfs2_read_block(alloc_inode,
 				  le64_to_cpu(cl->cl_recs[chain].c_blkno),
-				  &group_bh, OCFS2_BH_CACHED, alloc_inode);
+				  &group_bh);
 	if (status < 0) {
 		mlog_errno(status);
 		goto bail;
@@ -1204,21 +1259,20 @@
 	/* for now, the chain search is a bit simplistic. We just use
 	 * the 1st group with any empty bits. */
 	while ((status = ac->ac_group_search(alloc_inode, group_bh,
-					     bits_wanted, min_bits, bit_off,
+					     bits_wanted, min_bits,
+					     ac->ac_max_block, bit_off,
 					     &tmp_bits)) == -ENOSPC) {
 		if (!bg->bg_next_group)
 			break;
 
-		if (prev_group_bh) {
-			brelse(prev_group_bh);
-			prev_group_bh = NULL;
-		}
+		brelse(prev_group_bh);
+		prev_group_bh = NULL;
+
 		next_group = le64_to_cpu(bg->bg_next_group);
 		prev_group_bh = group_bh;
 		group_bh = NULL;
-		status = ocfs2_read_block(OCFS2_SB(alloc_inode->i_sb),
-					  next_group, &group_bh,
-					  OCFS2_BH_CACHED, alloc_inode);
+		status = ocfs2_read_block(alloc_inode,
+					  next_group, &group_bh);
 		if (status < 0) {
 			mlog_errno(status);
 			goto bail;
@@ -1307,10 +1361,8 @@
 	*bg_blkno = le64_to_cpu(bg->bg_blkno);
 	*bits_left = le16_to_cpu(bg->bg_free_bits_count);
 bail:
-	if (group_bh)
-		brelse(group_bh);
-	if (prev_group_bh)
-		brelse(prev_group_bh);
+	brelse(group_bh);
+	brelse(prev_group_bh);
 
 	mlog_exit(status);
 	return status;
@@ -1723,7 +1775,6 @@
 {
 	int status = 0;
 	u32 tmp_used;
-	struct ocfs2_super *osb = OCFS2_SB(alloc_inode->i_sb);
 	struct ocfs2_dinode *fe = (struct ocfs2_dinode *) alloc_bh->b_data;
 	struct ocfs2_chain_list *cl = &fe->id2.i_chain;
 	struct buffer_head *group_bh = NULL;
@@ -1742,8 +1793,7 @@
 	     (unsigned long long)OCFS2_I(alloc_inode)->ip_blkno, count,
 	     (unsigned long long)bg_blkno, start_bit);
 
-	status = ocfs2_read_block(osb, bg_blkno, &group_bh, OCFS2_BH_CACHED,
-				  alloc_inode);
+	status = ocfs2_read_block(alloc_inode, bg_blkno, &group_bh);
 	if (status < 0) {
 		mlog_errno(status);
 		goto bail;
@@ -1784,8 +1834,7 @@
 	}
 
 bail:
-	if (group_bh)
-		brelse(group_bh);
+	brelse(group_bh);
 
 	mlog_exit(status);
 	return status;
@@ -1838,9 +1887,15 @@
 	status = ocfs2_free_suballoc_bits(handle, bitmap_inode, bitmap_bh,
 					  bg_start_bit, bg_blkno,
 					  num_clusters);
-	if (status < 0)
+	if (status < 0) {
 		mlog_errno(status);
+		goto out;
+	}
 
+	ocfs2_local_alloc_seen_free_bits(OCFS2_SB(bitmap_inode->i_sb),
+					 num_clusters);
+
+out:
 	mlog_exit(status);
 	return status;
 }
@@ -1891,3 +1946,84 @@
 		       (unsigned long long)fe->id2.i_chain.cl_recs[i].c_blkno);
 	}
 }
+
+/*
+ * For a given allocation, determine which allocators will need to be
+ * accessed, and lock them, reserving the appropriate number of bits.
+ *
+ * Sparse file systems call this from ocfs2_write_begin_nolock()
+ * and ocfs2_allocate_unwritten_extents().
+ *
+ * File systems which don't support holes call this from
+ * ocfs2_extend_allocation().
+ */
+int ocfs2_lock_allocators(struct inode *inode,
+			  struct ocfs2_extent_tree *et,
+			  u32 clusters_to_add, u32 extents_to_split,
+			  struct ocfs2_alloc_context **data_ac,
+			  struct ocfs2_alloc_context **meta_ac)
+{
+	int ret = 0, num_free_extents;
+	unsigned int max_recs_needed = clusters_to_add + 2 * extents_to_split;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+	*meta_ac = NULL;
+	if (data_ac)
+		*data_ac = NULL;
+
+	BUG_ON(clusters_to_add != 0 && data_ac == NULL);
+
+	num_free_extents = ocfs2_num_free_extents(osb, inode, et);
+	if (num_free_extents < 0) {
+		ret = num_free_extents;
+		mlog_errno(ret);
+		goto out;
+	}
+
+	/*
+	 * Sparse allocation file systems need to be more conservative
+	 * with reserving room for expansion - the actual allocation
+	 * happens while we've got a journal handle open so re-taking
+	 * a cluster lock (because we ran out of room for another
+	 * extent) will violate ordering rules.
+	 *
+	 * Most of the time we'll only be seeing this 1 cluster at a time
+	 * anyway.
+	 *
+	 * Always lock for any unwritten extents - we might want to
+	 * add blocks during a split.
+	 */
+	if (!num_free_extents ||
+	    (ocfs2_sparse_alloc(osb) && num_free_extents < max_recs_needed)) {
+		ret = ocfs2_reserve_new_metadata(osb, et->et_root_el, meta_ac);
+		if (ret < 0) {
+			if (ret != -ENOSPC)
+				mlog_errno(ret);
+			goto out;
+		}
+	}
+
+	if (clusters_to_add == 0)
+		goto out;
+
+	ret = ocfs2_reserve_clusters(osb, clusters_to_add, data_ac);
+	if (ret < 0) {
+		if (ret != -ENOSPC)
+			mlog_errno(ret);
+		goto out;
+	}
+
+out:
+	if (ret) {
+		if (*meta_ac) {
+			ocfs2_free_alloc_context(*meta_ac);
+			*meta_ac = NULL;
+		}
+
+		/*
+		 * We cannot have an error and a non null *data_ac.
+		 */
+	}
+
+	return ret;
+}
diff --git a/fs/ocfs2/suballoc.h b/fs/ocfs2/suballoc.h
index 544c600..4df159d 100644
--- a/fs/ocfs2/suballoc.h
+++ b/fs/ocfs2/suballoc.h
@@ -28,10 +28,11 @@
 
 typedef int (group_search_t)(struct inode *,
 			     struct buffer_head *,
-			     u32,
-			     u32,
-			     u16 *,
-			     u16 *);
+			     u32,			/* bits_wanted */
+			     u32,			/* min_bits */
+			     u64,			/* max_block */
+			     u16 *,			/* *bit_off */
+			     u16 *);			/* *bits_found */
 
 struct ocfs2_alloc_context {
 	struct inode *ac_inode;    /* which bitmap are we allocating from? */
@@ -51,6 +52,8 @@
 	group_search_t *ac_group_search;
 
 	u64    ac_last_group;
+	u64    ac_max_block;  /* Highest block number to allocate. 0 is
+				 is the same as ~0 - unlimited */
 };
 
 void ocfs2_free_alloc_context(struct ocfs2_alloc_context *ac);
@@ -59,9 +62,17 @@
 	return ac->ac_bits_wanted - ac->ac_bits_given;
 }
 
+/*
+ * Please note that the caller must make sure that root_el is the root
+ * of extent tree. So for an inode, it should be &fe->id2.i_list. Otherwise
+ * the result may be wrong.
+ */
 int ocfs2_reserve_new_metadata(struct ocfs2_super *osb,
-			       struct ocfs2_dinode *fe,
+			       struct ocfs2_extent_list *root_el,
 			       struct ocfs2_alloc_context **ac);
+int ocfs2_reserve_new_metadata_blocks(struct ocfs2_super *osb,
+				      int blocks,
+				      struct ocfs2_alloc_context **ac);
 int ocfs2_reserve_new_inode(struct ocfs2_super *osb,
 			    struct ocfs2_alloc_context **ac);
 int ocfs2_reserve_clusters(struct ocfs2_super *osb,
@@ -147,6 +158,7 @@
  * apis above. */
 int ocfs2_reserve_cluster_bitmap_bits(struct ocfs2_super *osb,
 				      struct ocfs2_alloc_context *ac);
+void ocfs2_free_ac_resource(struct ocfs2_alloc_context *ac);
 
 /* given a cluster offset, calculate which block group it belongs to
  * and return that block offset. */
@@ -156,4 +168,8 @@
 int ocfs2_check_group_descriptor(struct super_block *sb,
 				 struct ocfs2_dinode *di,
 				 struct ocfs2_group_desc *gd);
+int ocfs2_lock_allocators(struct inode *inode, struct ocfs2_extent_tree *et,
+			  u32 clusters_to_add, u32 extents_to_split,
+			  struct ocfs2_alloc_context **data_ac,
+			  struct ocfs2_alloc_context **meta_ac);
 #endif /* _CHAINALLOC_H_ */
diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c
index 70334d8..304b63a 100644
--- a/fs/ocfs2/super.c
+++ b/fs/ocfs2/super.c
@@ -64,6 +64,7 @@
 #include "sysfile.h"
 #include "uptodate.h"
 #include "ver.h"
+#include "xattr.h"
 
 #include "buffer_head_io.h"
 
@@ -154,6 +155,9 @@
 	Opt_localalloc,
 	Opt_localflocks,
 	Opt_stack,
+	Opt_user_xattr,
+	Opt_nouser_xattr,
+	Opt_inode64,
 	Opt_err,
 };
 
@@ -173,6 +177,9 @@
 	{Opt_localalloc, "localalloc=%d"},
 	{Opt_localflocks, "localflocks"},
 	{Opt_stack, "cluster_stack=%s"},
+	{Opt_user_xattr, "user_xattr"},
+	{Opt_nouser_xattr, "nouser_xattr"},
+	{Opt_inode64, "inode64"},
 	{Opt_err, NULL}
 };
 
@@ -205,10 +212,11 @@
 		ocfs2_schedule_truncate_log_flush(osb, 0);
 	}
 
-	if (journal_start_commit(OCFS2_SB(sb)->journal->j_journal, &target)) {
+	if (jbd2_journal_start_commit(OCFS2_SB(sb)->journal->j_journal,
+				      &target)) {
 		if (wait)
-			log_wait_commit(OCFS2_SB(sb)->journal->j_journal,
-					target);
+			jbd2_log_wait_commit(OCFS2_SB(sb)->journal->j_journal,
+					     target);
 	}
 	return 0;
 }
@@ -325,6 +333,7 @@
 	if (!oi)
 		return NULL;
 
+	jbd2_journal_init_jbd_inode(&oi->ip_jinode, &oi->vfs_inode);
 	return &oi->vfs_inode;
 }
 
@@ -406,6 +415,15 @@
 		goto out;
 	}
 
+	/* Probably don't want this on remount; it might
+	 * mess with other nodes */
+	if (!(osb->s_mount_opt & OCFS2_MOUNT_INODE64) &&
+	    (parsed_options.mount_opt & OCFS2_MOUNT_INODE64)) {
+		ret = -EINVAL;
+		mlog(ML_ERROR, "Cannot enable inode64 on remount\n");
+		goto out;
+	}
+
 	/* We're going to/from readonly mode. */
 	if ((*flags & MS_RDONLY) != (sb->s_flags & MS_RDONLY)) {
 		/* Lock here so the check of HARD_RO and the potential
@@ -637,7 +655,8 @@
 	osb->s_atime_quantum = parsed_options.atime_quantum;
 	osb->preferred_slot = parsed_options.slot;
 	osb->osb_commit_interval = parsed_options.commit_interval;
-	osb->local_alloc_size = parsed_options.localalloc_opt;
+	osb->local_alloc_default_bits = ocfs2_megabytes_to_clusters(sb, parsed_options.localalloc_opt);
+	osb->local_alloc_bits = osb->local_alloc_default_bits;
 
 	status = ocfs2_verify_userspace_stack(osb, &parsed_options);
 	if (status)
@@ -743,8 +762,7 @@
 	return status;
 
 read_super_error:
-	if (bh != NULL)
-		brelse(bh);
+	brelse(bh);
 
 	if (inode)
 		iput(inode);
@@ -847,6 +865,12 @@
 		case Opt_data_writeback:
 			mopt->mount_opt |= OCFS2_MOUNT_DATA_WRITEBACK;
 			break;
+		case Opt_user_xattr:
+			mopt->mount_opt &= ~OCFS2_MOUNT_NOUSERXATTR;
+			break;
+		case Opt_nouser_xattr:
+			mopt->mount_opt |= OCFS2_MOUNT_NOUSERXATTR;
+			break;
 		case Opt_atime_quantum:
 			if (match_int(&args[0], &option)) {
 				status = 0;
@@ -873,7 +897,7 @@
 			if (option < 0)
 				return 0;
 			if (option == 0)
-				option = JBD_DEFAULT_MAX_COMMIT_AGE;
+				option = JBD2_DEFAULT_MAX_COMMIT_AGE;
 			mopt->commit_interval = HZ * option;
 			break;
 		case Opt_localalloc:
@@ -918,6 +942,9 @@
 			       OCFS2_STACK_LABEL_LEN);
 			mopt->cluster_stack[OCFS2_STACK_LABEL_LEN] = '\0';
 			break;
+		case Opt_inode64:
+			mopt->mount_opt |= OCFS2_MOUNT_INODE64;
+			break;
 		default:
 			mlog(ML_ERROR,
 			     "Unrecognized mount option \"%s\" "
@@ -938,6 +965,7 @@
 {
 	struct ocfs2_super *osb = OCFS2_SB(mnt->mnt_sb);
 	unsigned long opts = osb->s_mount_opt;
+	unsigned int local_alloc_megs;
 
 	if (opts & OCFS2_MOUNT_HB_LOCAL)
 		seq_printf(s, ",_netdev,heartbeat=local");
@@ -970,8 +998,9 @@
 		seq_printf(s, ",commit=%u",
 			   (unsigned) (osb->osb_commit_interval / HZ));
 
-	if (osb->local_alloc_size != OCFS2_DEFAULT_LOCAL_ALLOC_SIZE)
-		seq_printf(s, ",localalloc=%d", osb->local_alloc_size);
+	local_alloc_megs = osb->local_alloc_bits >> (20 - osb->s_clustersize_bits);
+	if (local_alloc_megs != OCFS2_DEFAULT_LOCAL_ALLOC_SIZE)
+		seq_printf(s, ",localalloc=%d", local_alloc_megs);
 
 	if (opts & OCFS2_MOUNT_LOCALFLOCKS)
 		seq_printf(s, ",localflocks,");
@@ -980,6 +1009,14 @@
 		seq_printf(s, ",cluster_stack=%.*s", OCFS2_STACK_LABEL_LEN,
 			   osb->osb_cluster_stack);
 
+	if (opts & OCFS2_MOUNT_NOUSERXATTR)
+		seq_printf(s, ",nouser_xattr");
+	else
+		seq_printf(s, ",user_xattr");
+
+	if (opts & OCFS2_MOUNT_INODE64)
+		seq_printf(s, ",inode64");
+
 	return 0;
 }
 
@@ -1132,6 +1169,7 @@
 	oi->ip_dir_start_lookup = 0;
 
 	init_rwsem(&oi->ip_alloc_sem);
+	init_rwsem(&oi->ip_xattr_sem);
 	mutex_init(&oi->ip_io_mutex);
 
 	oi->ip_blkno = 0ULL;
@@ -1375,6 +1413,7 @@
 	sb->s_fs_info = osb;
 	sb->s_op = &ocfs2_sops;
 	sb->s_export_op = &ocfs2_export_ops;
+	sb->s_xattr = ocfs2_xattr_handlers;
 	sb->s_time_gran = 1;
 	sb->s_flags |= MS_NOATIME;
 	/* this is needed to support O_LARGEFILE */
@@ -1421,8 +1460,12 @@
 
 	osb->slot_num = OCFS2_INVALID_SLOT;
 
+	osb->s_xattr_inline_size = le16_to_cpu(
+					di->id2.i_super.s_xattr_inline_size);
+
 	osb->local_alloc_state = OCFS2_LA_UNUSED;
 	osb->local_alloc_bh = NULL;
+	INIT_DELAYED_WORK(&osb->la_enable_wq, ocfs2_la_enable_worker);
 
 	init_waitqueue_head(&osb->osb_mount_event);
 
@@ -1568,6 +1611,7 @@
 	osb->first_cluster_group_blkno =
 		le64_to_cpu(di->id2.i_super.s_first_cluster_group);
 	osb->fs_generation = le32_to_cpu(di->i_fs_generation);
+	osb->uuid_hash = le32_to_cpu(di->id2.i_super.s_uuid_hash);
 	mlog(0, "vol_label: %s\n", osb->vol_label);
 	mlog(0, "uuid: %s\n", osb->uuid_str);
 	mlog(0, "root_blkno=%llu, system_dir_blkno=%llu\n",
diff --git a/fs/ocfs2/symlink.c b/fs/ocfs2/symlink.c
index ba9dbb5..cbd03df 100644
--- a/fs/ocfs2/symlink.c
+++ b/fs/ocfs2/symlink.c
@@ -50,6 +50,7 @@
 #include "inode.h"
 #include "journal.h"
 #include "symlink.h"
+#include "xattr.h"
 
 #include "buffer_head_io.h"
 
@@ -83,11 +84,7 @@
 
 	mlog_entry_void();
 
-	status = ocfs2_read_block(OCFS2_SB(inode->i_sb),
-				  OCFS2_I(inode)->ip_blkno,
-				  bh,
-				  OCFS2_BH_CACHED,
-				  inode);
+	status = ocfs2_read_block(inode, OCFS2_I(inode)->ip_blkno, bh);
 	if (status < 0) {
 		mlog_errno(status);
 		link = ERR_PTR(status);
@@ -157,8 +154,7 @@
 		kunmap(page);
 		page_cache_release(page);
 	}
-	if (bh)
-		brelse(bh);
+	brelse(bh);
 
 	return ERR_PTR(status);
 }
@@ -168,10 +164,18 @@
 	.follow_link	= ocfs2_follow_link,
 	.getattr	= ocfs2_getattr,
 	.setattr	= ocfs2_setattr,
+	.setxattr	= generic_setxattr,
+	.getxattr	= generic_getxattr,
+	.listxattr	= ocfs2_listxattr,
+	.removexattr	= generic_removexattr,
 };
 const struct inode_operations ocfs2_fast_symlink_inode_operations = {
 	.readlink	= ocfs2_readlink,
 	.follow_link	= ocfs2_follow_link,
 	.getattr	= ocfs2_getattr,
 	.setattr	= ocfs2_setattr,
+	.setxattr	= generic_setxattr,
+	.getxattr	= generic_getxattr,
+	.listxattr	= ocfs2_listxattr,
+	.removexattr	= generic_removexattr,
 };
diff --git a/fs/ocfs2/uptodate.c b/fs/ocfs2/uptodate.c
index 4da8851..187b99f 100644
--- a/fs/ocfs2/uptodate.c
+++ b/fs/ocfs2/uptodate.c
@@ -53,7 +53,11 @@
 #include <linux/highmem.h>
 #include <linux/buffer_head.h>
 #include <linux/rbtree.h>
-#include <linux/jbd.h>
+#ifndef CONFIG_OCFS2_COMPAT_JBD
+# include <linux/jbd2.h>
+#else
+# include <linux/jbd.h>
+#endif
 
 #define MLOG_MASK_PREFIX ML_UPTODATE
 
@@ -511,14 +515,10 @@
 	ci->ci_num_cached--;
 }
 
-/* Called when we remove a chunk of metadata from an inode. We don't
- * bother reverting things to an inlined array in the case of a remove
- * which moves us back under the limit. */
-void ocfs2_remove_from_cache(struct inode *inode,
-			     struct buffer_head *bh)
+static void ocfs2_remove_block_from_cache(struct inode *inode,
+					  sector_t block)
 {
 	int index;
-	sector_t block = bh->b_blocknr;
 	struct ocfs2_meta_cache_item *item = NULL;
 	struct ocfs2_inode_info *oi = OCFS2_I(inode);
 	struct ocfs2_caching_info *ci = &oi->ip_metadata_cache;
@@ -544,6 +544,30 @@
 		kmem_cache_free(ocfs2_uptodate_cachep, item);
 }
 
+/*
+ * Called when we remove a chunk of metadata from an inode. We don't
+ * bother reverting things to an inlined array in the case of a remove
+ * which moves us back under the limit.
+ */
+void ocfs2_remove_from_cache(struct inode *inode,
+			     struct buffer_head *bh)
+{
+	sector_t block = bh->b_blocknr;
+
+	ocfs2_remove_block_from_cache(inode, block);
+}
+
+/* Called when we remove xattr clusters from an inode. */
+void ocfs2_remove_xattr_clusters_from_cache(struct inode *inode,
+					    sector_t block,
+					    u32 c_len)
+{
+	unsigned int i, b_len = ocfs2_clusters_to_blocks(inode->i_sb, 1) * c_len;
+
+	for (i = 0; i < b_len; i++, block++)
+		ocfs2_remove_block_from_cache(inode, block);
+}
+
 int __init init_ocfs2_uptodate_cache(void)
 {
 	ocfs2_uptodate_cachep = kmem_cache_create("ocfs2_uptodate",
diff --git a/fs/ocfs2/uptodate.h b/fs/ocfs2/uptodate.h
index 2e73206..531b4b3 100644
--- a/fs/ocfs2/uptodate.h
+++ b/fs/ocfs2/uptodate.h
@@ -40,6 +40,9 @@
 				   struct buffer_head *bh);
 void ocfs2_remove_from_cache(struct inode *inode,
 			     struct buffer_head *bh);
+void ocfs2_remove_xattr_clusters_from_cache(struct inode *inode,
+					    sector_t block,
+					    u32 c_len);
 int ocfs2_buffer_read_ahead(struct inode *inode,
 			    struct buffer_head *bh);
 
diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c
new file mode 100644
index 0000000..c25780a
--- /dev/null
+++ b/fs/ocfs2/xattr.c
@@ -0,0 +1,4834 @@
+/* -*- mode: c; c-basic-offset: 8; -*-
+ * vim: noexpandtab sw=8 ts=8 sts=0:
+ *
+ * xattr.c
+ *
+ * Copyright (C) 2008 Oracle.  All rights reserved.
+ *
+ * CREDITS:
+ * Lots of code in this file is taken from ext3.
+ *
+ * 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 021110-1307, USA.
+ */
+
+#include <linux/capability.h>
+#include <linux/fs.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/highmem.h>
+#include <linux/pagemap.h>
+#include <linux/uio.h>
+#include <linux/sched.h>
+#include <linux/splice.h>
+#include <linux/mount.h>
+#include <linux/writeback.h>
+#include <linux/falloc.h>
+#include <linux/sort.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/string.h>
+
+#define MLOG_MASK_PREFIX ML_XATTR
+#include <cluster/masklog.h>
+
+#include "ocfs2.h"
+#include "alloc.h"
+#include "dlmglue.h"
+#include "file.h"
+#include "symlink.h"
+#include "sysfile.h"
+#include "inode.h"
+#include "journal.h"
+#include "ocfs2_fs.h"
+#include "suballoc.h"
+#include "uptodate.h"
+#include "buffer_head_io.h"
+#include "super.h"
+#include "xattr.h"
+
+
+struct ocfs2_xattr_def_value_root {
+	struct ocfs2_xattr_value_root	xv;
+	struct ocfs2_extent_rec		er;
+};
+
+struct ocfs2_xattr_bucket {
+	struct buffer_head *bhs[OCFS2_XATTR_MAX_BLOCKS_PER_BUCKET];
+	struct ocfs2_xattr_header *xh;
+};
+
+#define OCFS2_XATTR_ROOT_SIZE	(sizeof(struct ocfs2_xattr_def_value_root))
+#define OCFS2_XATTR_INLINE_SIZE	80
+
+static struct ocfs2_xattr_def_value_root def_xv = {
+	.xv.xr_list.l_count = cpu_to_le16(1),
+};
+
+struct xattr_handler *ocfs2_xattr_handlers[] = {
+	&ocfs2_xattr_user_handler,
+	&ocfs2_xattr_trusted_handler,
+	NULL
+};
+
+static struct xattr_handler *ocfs2_xattr_handler_map[] = {
+	[OCFS2_XATTR_INDEX_USER]	= &ocfs2_xattr_user_handler,
+	[OCFS2_XATTR_INDEX_TRUSTED]	= &ocfs2_xattr_trusted_handler,
+};
+
+struct ocfs2_xattr_info {
+	int name_index;
+	const char *name;
+	const void *value;
+	size_t value_len;
+};
+
+struct ocfs2_xattr_search {
+	struct buffer_head *inode_bh;
+	/*
+	 * xattr_bh point to the block buffer head which has extended attribute
+	 * when extended attribute in inode, xattr_bh is equal to inode_bh.
+	 */
+	struct buffer_head *xattr_bh;
+	struct ocfs2_xattr_header *header;
+	struct ocfs2_xattr_bucket bucket;
+	void *base;
+	void *end;
+	struct ocfs2_xattr_entry *here;
+	int not_found;
+};
+
+static int ocfs2_xattr_bucket_get_name_value(struct inode *inode,
+					     struct ocfs2_xattr_header *xh,
+					     int index,
+					     int *block_off,
+					     int *new_offset);
+
+static int ocfs2_xattr_index_block_find(struct inode *inode,
+					struct buffer_head *root_bh,
+					int name_index,
+					const char *name,
+					struct ocfs2_xattr_search *xs);
+
+static int ocfs2_xattr_tree_list_index_block(struct inode *inode,
+					struct ocfs2_xattr_tree_root *xt,
+					char *buffer,
+					size_t buffer_size);
+
+static int ocfs2_xattr_create_index_block(struct inode *inode,
+					  struct ocfs2_xattr_search *xs);
+
+static int ocfs2_xattr_set_entry_index_block(struct inode *inode,
+					     struct ocfs2_xattr_info *xi,
+					     struct ocfs2_xattr_search *xs);
+
+static int ocfs2_delete_xattr_index_block(struct inode *inode,
+					  struct buffer_head *xb_bh);
+
+static inline const char *ocfs2_xattr_prefix(int name_index)
+{
+	struct xattr_handler *handler = NULL;
+
+	if (name_index > 0 && name_index < OCFS2_XATTR_MAX)
+		handler = ocfs2_xattr_handler_map[name_index];
+
+	return handler ? handler->prefix : NULL;
+}
+
+static u32 ocfs2_xattr_name_hash(struct inode *inode,
+				 const char *name,
+				 int name_len)
+{
+	/* Get hash value of uuid from super block */
+	u32 hash = OCFS2_SB(inode->i_sb)->uuid_hash;
+	int i;
+
+	/* hash extended attribute name */
+	for (i = 0; i < name_len; i++) {
+		hash = (hash << OCFS2_HASH_SHIFT) ^
+		       (hash >> (8*sizeof(hash) - OCFS2_HASH_SHIFT)) ^
+		       *name++;
+	}
+
+	return hash;
+}
+
+/*
+ * ocfs2_xattr_hash_entry()
+ *
+ * Compute the hash of an extended attribute.
+ */
+static void ocfs2_xattr_hash_entry(struct inode *inode,
+				   struct ocfs2_xattr_header *header,
+				   struct ocfs2_xattr_entry *entry)
+{
+	u32 hash = 0;
+	char *name = (char *)header + le16_to_cpu(entry->xe_name_offset);
+
+	hash = ocfs2_xattr_name_hash(inode, name, entry->xe_name_len);
+	entry->xe_name_hash = cpu_to_le32(hash);
+
+	return;
+}
+
+static int ocfs2_xattr_extend_allocation(struct inode *inode,
+					 u32 clusters_to_add,
+					 struct buffer_head *xattr_bh,
+					 struct ocfs2_xattr_value_root *xv)
+{
+	int status = 0;
+	int restart_func = 0;
+	int credits = 0;
+	handle_t *handle = NULL;
+	struct ocfs2_alloc_context *data_ac = NULL;
+	struct ocfs2_alloc_context *meta_ac = NULL;
+	enum ocfs2_alloc_restarted why;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+	u32 prev_clusters, logical_start = le32_to_cpu(xv->xr_clusters);
+	struct ocfs2_extent_tree et;
+
+	mlog(0, "(clusters_to_add for xattr= %u)\n", clusters_to_add);
+
+	ocfs2_init_xattr_value_extent_tree(&et, inode, xattr_bh, xv);
+
+restart_all:
+
+	status = ocfs2_lock_allocators(inode, &et, clusters_to_add, 0,
+				       &data_ac, &meta_ac);
+	if (status) {
+		mlog_errno(status);
+		goto leave;
+	}
+
+	credits = ocfs2_calc_extend_credits(osb->sb, et.et_root_el,
+					    clusters_to_add);
+	handle = ocfs2_start_trans(osb, credits);
+	if (IS_ERR(handle)) {
+		status = PTR_ERR(handle);
+		handle = NULL;
+		mlog_errno(status);
+		goto leave;
+	}
+
+restarted_transaction:
+	status = ocfs2_journal_access(handle, inode, xattr_bh,
+				      OCFS2_JOURNAL_ACCESS_WRITE);
+	if (status < 0) {
+		mlog_errno(status);
+		goto leave;
+	}
+
+	prev_clusters = le32_to_cpu(xv->xr_clusters);
+	status = ocfs2_add_clusters_in_btree(osb,
+					     inode,
+					     &logical_start,
+					     clusters_to_add,
+					     0,
+					     &et,
+					     handle,
+					     data_ac,
+					     meta_ac,
+					     &why);
+	if ((status < 0) && (status != -EAGAIN)) {
+		if (status != -ENOSPC)
+			mlog_errno(status);
+		goto leave;
+	}
+
+	status = ocfs2_journal_dirty(handle, xattr_bh);
+	if (status < 0) {
+		mlog_errno(status);
+		goto leave;
+	}
+
+	clusters_to_add -= le32_to_cpu(xv->xr_clusters) - prev_clusters;
+
+	if (why != RESTART_NONE && clusters_to_add) {
+		if (why == RESTART_META) {
+			mlog(0, "restarting function.\n");
+			restart_func = 1;
+		} else {
+			BUG_ON(why != RESTART_TRANS);
+
+			mlog(0, "restarting transaction.\n");
+			/* TODO: This can be more intelligent. */
+			credits = ocfs2_calc_extend_credits(osb->sb,
+							    et.et_root_el,
+							    clusters_to_add);
+			status = ocfs2_extend_trans(handle, credits);
+			if (status < 0) {
+				/* handle still has to be committed at
+				 * this point. */
+				status = -ENOMEM;
+				mlog_errno(status);
+				goto leave;
+			}
+			goto restarted_transaction;
+		}
+	}
+
+leave:
+	if (handle) {
+		ocfs2_commit_trans(osb, handle);
+		handle = NULL;
+	}
+	if (data_ac) {
+		ocfs2_free_alloc_context(data_ac);
+		data_ac = NULL;
+	}
+	if (meta_ac) {
+		ocfs2_free_alloc_context(meta_ac);
+		meta_ac = NULL;
+	}
+	if ((!status) && restart_func) {
+		restart_func = 0;
+		goto restart_all;
+	}
+
+	return status;
+}
+
+static int __ocfs2_remove_xattr_range(struct inode *inode,
+				      struct buffer_head *root_bh,
+				      struct ocfs2_xattr_value_root *xv,
+				      u32 cpos, u32 phys_cpos, u32 len,
+				      struct ocfs2_cached_dealloc_ctxt *dealloc)
+{
+	int ret;
+	u64 phys_blkno = ocfs2_clusters_to_blocks(inode->i_sb, phys_cpos);
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+	struct inode *tl_inode = osb->osb_tl_inode;
+	handle_t *handle;
+	struct ocfs2_alloc_context *meta_ac = NULL;
+	struct ocfs2_extent_tree et;
+
+	ocfs2_init_xattr_value_extent_tree(&et, inode, root_bh, xv);
+
+	ret = ocfs2_lock_allocators(inode, &et, 0, 1, NULL, &meta_ac);
+	if (ret) {
+		mlog_errno(ret);
+		return ret;
+	}
+
+	mutex_lock(&tl_inode->i_mutex);
+
+	if (ocfs2_truncate_log_needs_flush(osb)) {
+		ret = __ocfs2_flush_truncate_log(osb);
+		if (ret < 0) {
+			mlog_errno(ret);
+			goto out;
+		}
+	}
+
+	handle = ocfs2_start_trans(osb, OCFS2_REMOVE_EXTENT_CREDITS);
+	if (IS_ERR(handle)) {
+		ret = PTR_ERR(handle);
+		mlog_errno(ret);
+		goto out;
+	}
+
+	ret = ocfs2_journal_access(handle, inode, root_bh,
+				   OCFS2_JOURNAL_ACCESS_WRITE);
+	if (ret) {
+		mlog_errno(ret);
+		goto out_commit;
+	}
+
+	ret = ocfs2_remove_extent(inode, &et, cpos, len, handle, meta_ac,
+				  dealloc);
+	if (ret) {
+		mlog_errno(ret);
+		goto out_commit;
+	}
+
+	le32_add_cpu(&xv->xr_clusters, -len);
+
+	ret = ocfs2_journal_dirty(handle, root_bh);
+	if (ret) {
+		mlog_errno(ret);
+		goto out_commit;
+	}
+
+	ret = ocfs2_truncate_log_append(osb, handle, phys_blkno, len);
+	if (ret)
+		mlog_errno(ret);
+
+out_commit:
+	ocfs2_commit_trans(osb, handle);
+out:
+	mutex_unlock(&tl_inode->i_mutex);
+
+	if (meta_ac)
+		ocfs2_free_alloc_context(meta_ac);
+
+	return ret;
+}
+
+static int ocfs2_xattr_shrink_size(struct inode *inode,
+				   u32 old_clusters,
+				   u32 new_clusters,
+				   struct buffer_head *root_bh,
+				   struct ocfs2_xattr_value_root *xv)
+{
+	int ret = 0;
+	u32 trunc_len, cpos, phys_cpos, alloc_size;
+	u64 block;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+	struct ocfs2_cached_dealloc_ctxt dealloc;
+
+	ocfs2_init_dealloc_ctxt(&dealloc);
+
+	if (old_clusters <= new_clusters)
+		return 0;
+
+	cpos = new_clusters;
+	trunc_len = old_clusters - new_clusters;
+	while (trunc_len) {
+		ret = ocfs2_xattr_get_clusters(inode, cpos, &phys_cpos,
+					       &alloc_size, &xv->xr_list);
+		if (ret) {
+			mlog_errno(ret);
+			goto out;
+		}
+
+		if (alloc_size > trunc_len)
+			alloc_size = trunc_len;
+
+		ret = __ocfs2_remove_xattr_range(inode, root_bh, xv, cpos,
+						 phys_cpos, alloc_size,
+						 &dealloc);
+		if (ret) {
+			mlog_errno(ret);
+			goto out;
+		}
+
+		block = ocfs2_clusters_to_blocks(inode->i_sb, phys_cpos);
+		ocfs2_remove_xattr_clusters_from_cache(inode, block,
+						       alloc_size);
+		cpos += alloc_size;
+		trunc_len -= alloc_size;
+	}
+
+out:
+	ocfs2_schedule_truncate_log_flush(osb, 1);
+	ocfs2_run_deallocs(osb, &dealloc);
+
+	return ret;
+}
+
+static int ocfs2_xattr_value_truncate(struct inode *inode,
+				      struct buffer_head *root_bh,
+				      struct ocfs2_xattr_value_root *xv,
+				      int len)
+{
+	int ret;
+	u32 new_clusters = ocfs2_clusters_for_bytes(inode->i_sb, len);
+	u32 old_clusters = le32_to_cpu(xv->xr_clusters);
+
+	if (new_clusters == old_clusters)
+		return 0;
+
+	if (new_clusters > old_clusters)
+		ret = ocfs2_xattr_extend_allocation(inode,
+						    new_clusters - old_clusters,
+						    root_bh, xv);
+	else
+		ret = ocfs2_xattr_shrink_size(inode,
+					      old_clusters, new_clusters,
+					      root_bh, xv);
+
+	return ret;
+}
+
+static int ocfs2_xattr_list_entry(char *buffer, size_t size,
+				  size_t *result, const char *prefix,
+				  const char *name, int name_len)
+{
+	char *p = buffer + *result;
+	int prefix_len = strlen(prefix);
+	int total_len = prefix_len + name_len + 1;
+
+	*result += total_len;
+
+	/* we are just looking for how big our buffer needs to be */
+	if (!size)
+		return 0;
+
+	if (*result > size)
+		return -ERANGE;
+
+	memcpy(p, prefix, prefix_len);
+	memcpy(p + prefix_len, name, name_len);
+	p[prefix_len + name_len] = '\0';
+
+	return 0;
+}
+
+static int ocfs2_xattr_list_entries(struct inode *inode,
+				    struct ocfs2_xattr_header *header,
+				    char *buffer, size_t buffer_size)
+{
+	size_t result = 0;
+	int i, type, ret;
+	const char *prefix, *name;
+
+	for (i = 0 ; i < le16_to_cpu(header->xh_count); i++) {
+		struct ocfs2_xattr_entry *entry = &header->xh_entries[i];
+		type = ocfs2_xattr_get_type(entry);
+		prefix = ocfs2_xattr_prefix(type);
+
+		if (prefix) {
+			name = (const char *)header +
+				le16_to_cpu(entry->xe_name_offset);
+
+			ret = ocfs2_xattr_list_entry(buffer, buffer_size,
+						     &result, prefix, name,
+						     entry->xe_name_len);
+			if (ret)
+				return ret;
+		}
+	}
+
+	return result;
+}
+
+static int ocfs2_xattr_ibody_list(struct inode *inode,
+				  struct ocfs2_dinode *di,
+				  char *buffer,
+				  size_t buffer_size)
+{
+	struct ocfs2_xattr_header *header = NULL;
+	struct ocfs2_inode_info *oi = OCFS2_I(inode);
+	int ret = 0;
+
+	if (!(oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL))
+		return ret;
+
+	header = (struct ocfs2_xattr_header *)
+		 ((void *)di + inode->i_sb->s_blocksize -
+		 le16_to_cpu(di->i_xattr_inline_size));
+
+	ret = ocfs2_xattr_list_entries(inode, header, buffer, buffer_size);
+
+	return ret;
+}
+
+static int ocfs2_xattr_block_list(struct inode *inode,
+				  struct ocfs2_dinode *di,
+				  char *buffer,
+				  size_t buffer_size)
+{
+	struct buffer_head *blk_bh = NULL;
+	struct ocfs2_xattr_block *xb;
+	int ret = 0;
+
+	if (!di->i_xattr_loc)
+		return ret;
+
+	ret = ocfs2_read_block(inode, le64_to_cpu(di->i_xattr_loc), &blk_bh);
+	if (ret < 0) {
+		mlog_errno(ret);
+		return ret;
+	}
+	/*Verify the signature of xattr block*/
+	if (memcmp((void *)blk_bh->b_data, OCFS2_XATTR_BLOCK_SIGNATURE,
+		   strlen(OCFS2_XATTR_BLOCK_SIGNATURE))) {
+		ret = -EFAULT;
+		goto cleanup;
+	}
+
+	xb = (struct ocfs2_xattr_block *)blk_bh->b_data;
+
+	if (!(le16_to_cpu(xb->xb_flags) & OCFS2_XATTR_INDEXED)) {
+		struct ocfs2_xattr_header *header = &xb->xb_attrs.xb_header;
+		ret = ocfs2_xattr_list_entries(inode, header,
+					       buffer, buffer_size);
+	} else {
+		struct ocfs2_xattr_tree_root *xt = &xb->xb_attrs.xb_root;
+		ret = ocfs2_xattr_tree_list_index_block(inode, xt,
+						   buffer, buffer_size);
+	}
+cleanup:
+	brelse(blk_bh);
+
+	return ret;
+}
+
+ssize_t ocfs2_listxattr(struct dentry *dentry,
+			char *buffer,
+			size_t size)
+{
+	int ret = 0, i_ret = 0, b_ret = 0;
+	struct buffer_head *di_bh = NULL;
+	struct ocfs2_dinode *di = NULL;
+	struct ocfs2_inode_info *oi = OCFS2_I(dentry->d_inode);
+
+	if (!ocfs2_supports_xattr(OCFS2_SB(dentry->d_sb)))
+		return -EOPNOTSUPP;
+
+	if (!(oi->ip_dyn_features & OCFS2_HAS_XATTR_FL))
+		return ret;
+
+	ret = ocfs2_inode_lock(dentry->d_inode, &di_bh, 0);
+	if (ret < 0) {
+		mlog_errno(ret);
+		return ret;
+	}
+
+	di = (struct ocfs2_dinode *)di_bh->b_data;
+
+	down_read(&oi->ip_xattr_sem);
+	i_ret = ocfs2_xattr_ibody_list(dentry->d_inode, di, buffer, size);
+	if (i_ret < 0)
+		b_ret = 0;
+	else {
+		if (buffer) {
+			buffer += i_ret;
+			size -= i_ret;
+		}
+		b_ret = ocfs2_xattr_block_list(dentry->d_inode, di,
+					       buffer, size);
+		if (b_ret < 0)
+			i_ret = 0;
+	}
+	up_read(&oi->ip_xattr_sem);
+	ocfs2_inode_unlock(dentry->d_inode, 0);
+
+	brelse(di_bh);
+
+	return i_ret + b_ret;
+}
+
+static int ocfs2_xattr_find_entry(int name_index,
+				  const char *name,
+				  struct ocfs2_xattr_search *xs)
+{
+	struct ocfs2_xattr_entry *entry;
+	size_t name_len;
+	int i, cmp = 1;
+
+	if (name == NULL)
+		return -EINVAL;
+
+	name_len = strlen(name);
+	entry = xs->here;
+	for (i = 0; i < le16_to_cpu(xs->header->xh_count); i++) {
+		cmp = name_index - ocfs2_xattr_get_type(entry);
+		if (!cmp)
+			cmp = name_len - entry->xe_name_len;
+		if (!cmp)
+			cmp = memcmp(name, (xs->base +
+				     le16_to_cpu(entry->xe_name_offset)),
+				     name_len);
+		if (cmp == 0)
+			break;
+		entry += 1;
+	}
+	xs->here = entry;
+
+	return cmp ? -ENODATA : 0;
+}
+
+static int ocfs2_xattr_get_value_outside(struct inode *inode,
+					 struct ocfs2_xattr_value_root *xv,
+					 void *buffer,
+					 size_t len)
+{
+	u32 cpos, p_cluster, num_clusters, bpc, clusters;
+	u64 blkno;
+	int i, ret = 0;
+	size_t cplen, blocksize;
+	struct buffer_head *bh = NULL;
+	struct ocfs2_extent_list *el;
+
+	el = &xv->xr_list;
+	clusters = le32_to_cpu(xv->xr_clusters);
+	bpc = ocfs2_clusters_to_blocks(inode->i_sb, 1);
+	blocksize = inode->i_sb->s_blocksize;
+
+	cpos = 0;
+	while (cpos < clusters) {
+		ret = ocfs2_xattr_get_clusters(inode, cpos, &p_cluster,
+					       &num_clusters, el);
+		if (ret) {
+			mlog_errno(ret);
+			goto out;
+		}
+
+		blkno = ocfs2_clusters_to_blocks(inode->i_sb, p_cluster);
+		/* Copy ocfs2_xattr_value */
+		for (i = 0; i < num_clusters * bpc; i++, blkno++) {
+			ret = ocfs2_read_block(inode, blkno, &bh);
+			if (ret) {
+				mlog_errno(ret);
+				goto out;
+			}
+
+			cplen = len >= blocksize ? blocksize : len;
+			memcpy(buffer, bh->b_data, cplen);
+			len -= cplen;
+			buffer += cplen;
+
+			brelse(bh);
+			bh = NULL;
+			if (len == 0)
+				break;
+		}
+		cpos += num_clusters;
+	}
+out:
+	return ret;
+}
+
+static int ocfs2_xattr_ibody_get(struct inode *inode,
+				 int name_index,
+				 const char *name,
+				 void *buffer,
+				 size_t buffer_size,
+				 struct ocfs2_xattr_search *xs)
+{
+	struct ocfs2_inode_info *oi = OCFS2_I(inode);
+	struct ocfs2_dinode *di = (struct ocfs2_dinode *)xs->inode_bh->b_data;
+	struct ocfs2_xattr_value_root *xv;
+	size_t size;
+	int ret = 0;
+
+	if (!(oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL))
+		return -ENODATA;
+
+	xs->end = (void *)di + inode->i_sb->s_blocksize;
+	xs->header = (struct ocfs2_xattr_header *)
+			(xs->end - le16_to_cpu(di->i_xattr_inline_size));
+	xs->base = (void *)xs->header;
+	xs->here = xs->header->xh_entries;
+
+	ret = ocfs2_xattr_find_entry(name_index, name, xs);
+	if (ret)
+		return ret;
+	size = le64_to_cpu(xs->here->xe_value_size);
+	if (buffer) {
+		if (size > buffer_size)
+			return -ERANGE;
+		if (ocfs2_xattr_is_local(xs->here)) {
+			memcpy(buffer, (void *)xs->base +
+			       le16_to_cpu(xs->here->xe_name_offset) +
+			       OCFS2_XATTR_SIZE(xs->here->xe_name_len), size);
+		} else {
+			xv = (struct ocfs2_xattr_value_root *)
+				(xs->base + le16_to_cpu(
+				 xs->here->xe_name_offset) +
+				OCFS2_XATTR_SIZE(xs->here->xe_name_len));
+			ret = ocfs2_xattr_get_value_outside(inode, xv,
+							    buffer, size);
+			if (ret < 0) {
+				mlog_errno(ret);
+				return ret;
+			}
+		}
+	}
+
+	return size;
+}
+
+static int ocfs2_xattr_block_get(struct inode *inode,
+				 int name_index,
+				 const char *name,
+				 void *buffer,
+				 size_t buffer_size,
+				 struct ocfs2_xattr_search *xs)
+{
+	struct ocfs2_dinode *di = (struct ocfs2_dinode *)xs->inode_bh->b_data;
+	struct buffer_head *blk_bh = NULL;
+	struct ocfs2_xattr_block *xb;
+	struct ocfs2_xattr_value_root *xv;
+	size_t size;
+	int ret = -ENODATA, name_offset, name_len, block_off, i;
+
+	if (!di->i_xattr_loc)
+		return ret;
+
+	memset(&xs->bucket, 0, sizeof(xs->bucket));
+
+	ret = ocfs2_read_block(inode, le64_to_cpu(di->i_xattr_loc), &blk_bh);
+	if (ret < 0) {
+		mlog_errno(ret);
+		return ret;
+	}
+	/*Verify the signature of xattr block*/
+	if (memcmp((void *)blk_bh->b_data, OCFS2_XATTR_BLOCK_SIGNATURE,
+		   strlen(OCFS2_XATTR_BLOCK_SIGNATURE))) {
+		ret = -EFAULT;
+		goto cleanup;
+	}
+
+	xs->xattr_bh = blk_bh;
+	xb = (struct ocfs2_xattr_block *)blk_bh->b_data;
+
+	if (!(le16_to_cpu(xb->xb_flags) & OCFS2_XATTR_INDEXED)) {
+		xs->header = &xb->xb_attrs.xb_header;
+		xs->base = (void *)xs->header;
+		xs->end = (void *)(blk_bh->b_data) + blk_bh->b_size;
+		xs->here = xs->header->xh_entries;
+
+		ret = ocfs2_xattr_find_entry(name_index, name, xs);
+	} else
+		ret = ocfs2_xattr_index_block_find(inode, blk_bh,
+						   name_index,
+						   name, xs);
+
+	if (ret)
+		goto cleanup;
+	size = le64_to_cpu(xs->here->xe_value_size);
+	if (buffer) {
+		ret = -ERANGE;
+		if (size > buffer_size)
+			goto cleanup;
+
+		name_offset = le16_to_cpu(xs->here->xe_name_offset);
+		name_len = OCFS2_XATTR_SIZE(xs->here->xe_name_len);
+		i = xs->here - xs->header->xh_entries;
+
+		if (le16_to_cpu(xb->xb_flags) & OCFS2_XATTR_INDEXED) {
+			ret = ocfs2_xattr_bucket_get_name_value(inode,
+								xs->bucket.xh,
+								i,
+								&block_off,
+								&name_offset);
+			xs->base = xs->bucket.bhs[block_off]->b_data;
+		}
+		if (ocfs2_xattr_is_local(xs->here)) {
+			memcpy(buffer, (void *)xs->base +
+			       name_offset + name_len, size);
+		} else {
+			xv = (struct ocfs2_xattr_value_root *)
+				(xs->base + name_offset + name_len);
+			ret = ocfs2_xattr_get_value_outside(inode, xv,
+							    buffer, size);
+			if (ret < 0) {
+				mlog_errno(ret);
+				goto cleanup;
+			}
+		}
+	}
+	ret = size;
+cleanup:
+	for (i = 0; i < OCFS2_XATTR_MAX_BLOCKS_PER_BUCKET; i++)
+		brelse(xs->bucket.bhs[i]);
+	memset(&xs->bucket, 0, sizeof(xs->bucket));
+
+	brelse(blk_bh);
+	return ret;
+}
+
+/* ocfs2_xattr_get()
+ *
+ * Copy an extended attribute into the buffer provided.
+ * Buffer is NULL to compute the size of buffer required.
+ */
+int ocfs2_xattr_get(struct inode *inode,
+		    int name_index,
+		    const char *name,
+		    void *buffer,
+		    size_t buffer_size)
+{
+	int ret;
+	struct ocfs2_dinode *di = NULL;
+	struct buffer_head *di_bh = NULL;
+	struct ocfs2_inode_info *oi = OCFS2_I(inode);
+	struct ocfs2_xattr_search xis = {
+		.not_found = -ENODATA,
+	};
+	struct ocfs2_xattr_search xbs = {
+		.not_found = -ENODATA,
+	};
+
+	if (!ocfs2_supports_xattr(OCFS2_SB(inode->i_sb)))
+		return -EOPNOTSUPP;
+
+	if (!(oi->ip_dyn_features & OCFS2_HAS_XATTR_FL))
+		ret = -ENODATA;
+
+	ret = ocfs2_inode_lock(inode, &di_bh, 0);
+	if (ret < 0) {
+		mlog_errno(ret);
+		return ret;
+	}
+	xis.inode_bh = xbs.inode_bh = di_bh;
+	di = (struct ocfs2_dinode *)di_bh->b_data;
+
+	down_read(&oi->ip_xattr_sem);
+	ret = ocfs2_xattr_ibody_get(inode, name_index, name, buffer,
+				    buffer_size, &xis);
+	if (ret == -ENODATA)
+		ret = ocfs2_xattr_block_get(inode, name_index, name, buffer,
+					    buffer_size, &xbs);
+	up_read(&oi->ip_xattr_sem);
+	ocfs2_inode_unlock(inode, 0);
+
+	brelse(di_bh);
+
+	return ret;
+}
+
+static int __ocfs2_xattr_set_value_outside(struct inode *inode,
+					   struct ocfs2_xattr_value_root *xv,
+					   const void *value,
+					   int value_len)
+{
+	int ret = 0, i, cp_len, credits;
+	u16 blocksize = inode->i_sb->s_blocksize;
+	u32 p_cluster, num_clusters;
+	u32 cpos = 0, bpc = ocfs2_clusters_to_blocks(inode->i_sb, 1);
+	u32 clusters = ocfs2_clusters_for_bytes(inode->i_sb, value_len);
+	u64 blkno;
+	struct buffer_head *bh = NULL;
+	handle_t *handle;
+
+	BUG_ON(clusters > le32_to_cpu(xv->xr_clusters));
+
+	credits = clusters * bpc;
+	handle = ocfs2_start_trans(OCFS2_SB(inode->i_sb), credits);
+	if (IS_ERR(handle)) {
+		ret = PTR_ERR(handle);
+		mlog_errno(ret);
+		goto out;
+	}
+
+	while (cpos < clusters) {
+		ret = ocfs2_xattr_get_clusters(inode, cpos, &p_cluster,
+					       &num_clusters, &xv->xr_list);
+		if (ret) {
+			mlog_errno(ret);
+			goto out_commit;
+		}
+
+		blkno = ocfs2_clusters_to_blocks(inode->i_sb, p_cluster);
+
+		for (i = 0; i < num_clusters * bpc; i++, blkno++) {
+			ret = ocfs2_read_block(inode, blkno, &bh);
+			if (ret) {
+				mlog_errno(ret);
+				goto out_commit;
+			}
+
+			ret = ocfs2_journal_access(handle,
+						   inode,
+						   bh,
+						   OCFS2_JOURNAL_ACCESS_WRITE);
+			if (ret < 0) {
+				mlog_errno(ret);
+				goto out_commit;
+			}
+
+			cp_len = value_len > blocksize ? blocksize : value_len;
+			memcpy(bh->b_data, value, cp_len);
+			value_len -= cp_len;
+			value += cp_len;
+			if (cp_len < blocksize)
+				memset(bh->b_data + cp_len, 0,
+				       blocksize - cp_len);
+
+			ret = ocfs2_journal_dirty(handle, bh);
+			if (ret < 0) {
+				mlog_errno(ret);
+				goto out_commit;
+			}
+			brelse(bh);
+			bh = NULL;
+
+			/*
+			 * XXX: do we need to empty all the following
+			 * blocks in this cluster?
+			 */
+			if (!value_len)
+				break;
+		}
+		cpos += num_clusters;
+	}
+out_commit:
+	ocfs2_commit_trans(OCFS2_SB(inode->i_sb), handle);
+out:
+	brelse(bh);
+
+	return ret;
+}
+
+static int ocfs2_xattr_cleanup(struct inode *inode,
+			       struct ocfs2_xattr_info *xi,
+			       struct ocfs2_xattr_search *xs,
+			       size_t offs)
+{
+	handle_t *handle = NULL;
+	int ret = 0;
+	size_t name_len = strlen(xi->name);
+	void *val = xs->base + offs;
+	size_t size = OCFS2_XATTR_SIZE(name_len) + OCFS2_XATTR_ROOT_SIZE;
+
+	handle = ocfs2_start_trans((OCFS2_SB(inode->i_sb)),
+				   OCFS2_XATTR_BLOCK_UPDATE_CREDITS);
+	if (IS_ERR(handle)) {
+		ret = PTR_ERR(handle);
+		mlog_errno(ret);
+		goto out;
+	}
+	ret = ocfs2_journal_access(handle, inode, xs->xattr_bh,
+				   OCFS2_JOURNAL_ACCESS_WRITE);
+	if (ret) {
+		mlog_errno(ret);
+		goto out_commit;
+	}
+	/* Decrease xattr count */
+	le16_add_cpu(&xs->header->xh_count, -1);
+	/* Remove the xattr entry and tree root which has already be set*/
+	memset((void *)xs->here, 0, sizeof(struct ocfs2_xattr_entry));
+	memset(val, 0, size);
+
+	ret = ocfs2_journal_dirty(handle, xs->xattr_bh);
+	if (ret < 0)
+		mlog_errno(ret);
+out_commit:
+	ocfs2_commit_trans(OCFS2_SB(inode->i_sb), handle);
+out:
+	return ret;
+}
+
+static int ocfs2_xattr_update_entry(struct inode *inode,
+				    struct ocfs2_xattr_info *xi,
+				    struct ocfs2_xattr_search *xs,
+				    size_t offs)
+{
+	handle_t *handle = NULL;
+	int ret = 0;
+
+	handle = ocfs2_start_trans((OCFS2_SB(inode->i_sb)),
+				   OCFS2_XATTR_BLOCK_UPDATE_CREDITS);
+	if (IS_ERR(handle)) {
+		ret = PTR_ERR(handle);
+		mlog_errno(ret);
+		goto out;
+	}
+	ret = ocfs2_journal_access(handle, inode, xs->xattr_bh,
+				   OCFS2_JOURNAL_ACCESS_WRITE);
+	if (ret) {
+		mlog_errno(ret);
+		goto out_commit;
+	}
+
+	xs->here->xe_name_offset = cpu_to_le16(offs);
+	xs->here->xe_value_size = cpu_to_le64(xi->value_len);
+	if (xi->value_len <= OCFS2_XATTR_INLINE_SIZE)
+		ocfs2_xattr_set_local(xs->here, 1);
+	else
+		ocfs2_xattr_set_local(xs->here, 0);
+	ocfs2_xattr_hash_entry(inode, xs->header, xs->here);
+
+	ret = ocfs2_journal_dirty(handle, xs->xattr_bh);
+	if (ret < 0)
+		mlog_errno(ret);
+out_commit:
+	ocfs2_commit_trans(OCFS2_SB(inode->i_sb), handle);
+out:
+	return ret;
+}
+
+/*
+ * ocfs2_xattr_set_value_outside()
+ *
+ * Set large size value in B tree.
+ */
+static int ocfs2_xattr_set_value_outside(struct inode *inode,
+					 struct ocfs2_xattr_info *xi,
+					 struct ocfs2_xattr_search *xs,
+					 size_t offs)
+{
+	size_t name_len = strlen(xi->name);
+	void *val = xs->base + offs;
+	struct ocfs2_xattr_value_root *xv = NULL;
+	size_t size = OCFS2_XATTR_SIZE(name_len) + OCFS2_XATTR_ROOT_SIZE;
+	int ret = 0;
+
+	memset(val, 0, size);
+	memcpy(val, xi->name, name_len);
+	xv = (struct ocfs2_xattr_value_root *)
+		(val + OCFS2_XATTR_SIZE(name_len));
+	xv->xr_clusters = 0;
+	xv->xr_last_eb_blk = 0;
+	xv->xr_list.l_tree_depth = 0;
+	xv->xr_list.l_count = cpu_to_le16(1);
+	xv->xr_list.l_next_free_rec = 0;
+
+	ret = ocfs2_xattr_value_truncate(inode, xs->xattr_bh, xv,
+					 xi->value_len);
+	if (ret < 0) {
+		mlog_errno(ret);
+		return ret;
+	}
+	ret = __ocfs2_xattr_set_value_outside(inode, xv, xi->value,
+					      xi->value_len);
+	if (ret < 0) {
+		mlog_errno(ret);
+		return ret;
+	}
+	ret = ocfs2_xattr_update_entry(inode, xi, xs, offs);
+	if (ret < 0)
+		mlog_errno(ret);
+
+	return ret;
+}
+
+/*
+ * ocfs2_xattr_set_entry_local()
+ *
+ * Set, replace or remove extended attribute in local.
+ */
+static void ocfs2_xattr_set_entry_local(struct inode *inode,
+					struct ocfs2_xattr_info *xi,
+					struct ocfs2_xattr_search *xs,
+					struct ocfs2_xattr_entry *last,
+					size_t min_offs)
+{
+	size_t name_len = strlen(xi->name);
+	int i;
+
+	if (xi->value && xs->not_found) {
+		/* Insert the new xattr entry. */
+		le16_add_cpu(&xs->header->xh_count, 1);
+		ocfs2_xattr_set_type(last, xi->name_index);
+		ocfs2_xattr_set_local(last, 1);
+		last->xe_name_len = name_len;
+	} else {
+		void *first_val;
+		void *val;
+		size_t offs, size;
+
+		first_val = xs->base + min_offs;
+		offs = le16_to_cpu(xs->here->xe_name_offset);
+		val = xs->base + offs;
+
+		if (le64_to_cpu(xs->here->xe_value_size) >
+		    OCFS2_XATTR_INLINE_SIZE)
+			size = OCFS2_XATTR_SIZE(name_len) +
+				OCFS2_XATTR_ROOT_SIZE;
+		else
+			size = OCFS2_XATTR_SIZE(name_len) +
+			OCFS2_XATTR_SIZE(le64_to_cpu(xs->here->xe_value_size));
+
+		if (xi->value && size == OCFS2_XATTR_SIZE(name_len) +
+				OCFS2_XATTR_SIZE(xi->value_len)) {
+			/* The old and the new value have the
+			   same size. Just replace the value. */
+			ocfs2_xattr_set_local(xs->here, 1);
+			xs->here->xe_value_size = cpu_to_le64(xi->value_len);
+			/* Clear value bytes. */
+			memset(val + OCFS2_XATTR_SIZE(name_len),
+			       0,
+			       OCFS2_XATTR_SIZE(xi->value_len));
+			memcpy(val + OCFS2_XATTR_SIZE(name_len),
+			       xi->value,
+			       xi->value_len);
+			return;
+		}
+		/* Remove the old name+value. */
+		memmove(first_val + size, first_val, val - first_val);
+		memset(first_val, 0, size);
+		xs->here->xe_name_hash = 0;
+		xs->here->xe_name_offset = 0;
+		ocfs2_xattr_set_local(xs->here, 1);
+		xs->here->xe_value_size = 0;
+
+		min_offs += size;
+
+		/* Adjust all value offsets. */
+		last = xs->header->xh_entries;
+		for (i = 0 ; i < le16_to_cpu(xs->header->xh_count); i++) {
+			size_t o = le16_to_cpu(last->xe_name_offset);
+
+			if (o < offs)
+				last->xe_name_offset = cpu_to_le16(o + size);
+			last += 1;
+		}
+
+		if (!xi->value) {
+			/* Remove the old entry. */
+			last -= 1;
+			memmove(xs->here, xs->here + 1,
+				(void *)last - (void *)xs->here);
+			memset(last, 0, sizeof(struct ocfs2_xattr_entry));
+			le16_add_cpu(&xs->header->xh_count, -1);
+		}
+	}
+	if (xi->value) {
+		/* Insert the new name+value. */
+		size_t size = OCFS2_XATTR_SIZE(name_len) +
+				OCFS2_XATTR_SIZE(xi->value_len);
+		void *val = xs->base + min_offs - size;
+
+		xs->here->xe_name_offset = cpu_to_le16(min_offs - size);
+		memset(val, 0, size);
+		memcpy(val, xi->name, name_len);
+		memcpy(val + OCFS2_XATTR_SIZE(name_len),
+		       xi->value,
+		       xi->value_len);
+		xs->here->xe_value_size = cpu_to_le64(xi->value_len);
+		ocfs2_xattr_set_local(xs->here, 1);
+		ocfs2_xattr_hash_entry(inode, xs->header, xs->here);
+	}
+
+	return;
+}
+
+/*
+ * ocfs2_xattr_set_entry()
+ *
+ * Set extended attribute entry into inode or block.
+ *
+ * If extended attribute value size > OCFS2_XATTR_INLINE_SIZE,
+ * We first insert tree root(ocfs2_xattr_value_root) with set_entry_local(),
+ * then set value in B tree with set_value_outside().
+ */
+static int ocfs2_xattr_set_entry(struct inode *inode,
+				 struct ocfs2_xattr_info *xi,
+				 struct ocfs2_xattr_search *xs,
+				 int flag)
+{
+	struct ocfs2_xattr_entry *last;
+	struct ocfs2_inode_info *oi = OCFS2_I(inode);
+	struct ocfs2_dinode *di = (struct ocfs2_dinode *)xs->inode_bh->b_data;
+	size_t min_offs = xs->end - xs->base, name_len = strlen(xi->name);
+	size_t size_l = 0;
+	handle_t *handle = NULL;
+	int free, i, ret;
+	struct ocfs2_xattr_info xi_l = {
+		.name_index = xi->name_index,
+		.name = xi->name,
+		.value = xi->value,
+		.value_len = xi->value_len,
+	};
+
+	/* Compute min_offs, last and free space. */
+	last = xs->header->xh_entries;
+
+	for (i = 0 ; i < le16_to_cpu(xs->header->xh_count); i++) {
+		size_t offs = le16_to_cpu(last->xe_name_offset);
+		if (offs < min_offs)
+			min_offs = offs;
+		last += 1;
+	}
+
+	free = min_offs - ((void *)last - xs->base) - sizeof(__u32);
+	if (free < 0)
+		return -EFAULT;
+
+	if (!xs->not_found) {
+		size_t size = 0;
+		if (ocfs2_xattr_is_local(xs->here))
+			size = OCFS2_XATTR_SIZE(name_len) +
+			OCFS2_XATTR_SIZE(le64_to_cpu(xs->here->xe_value_size));
+		else
+			size = OCFS2_XATTR_SIZE(name_len) +
+				OCFS2_XATTR_ROOT_SIZE;
+		free += (size + sizeof(struct ocfs2_xattr_entry));
+	}
+	/* Check free space in inode or block */
+	if (xi->value && xi->value_len > OCFS2_XATTR_INLINE_SIZE) {
+		if (free < sizeof(struct ocfs2_xattr_entry) +
+			   OCFS2_XATTR_SIZE(name_len) +
+			   OCFS2_XATTR_ROOT_SIZE) {
+			ret = -ENOSPC;
+			goto out;
+		}
+		size_l = OCFS2_XATTR_SIZE(name_len) + OCFS2_XATTR_ROOT_SIZE;
+		xi_l.value = (void *)&def_xv;
+		xi_l.value_len = OCFS2_XATTR_ROOT_SIZE;
+	} else if (xi->value) {
+		if (free < sizeof(struct ocfs2_xattr_entry) +
+			   OCFS2_XATTR_SIZE(name_len) +
+			   OCFS2_XATTR_SIZE(xi->value_len)) {
+			ret = -ENOSPC;
+			goto out;
+		}
+	}
+
+	if (!xs->not_found) {
+		/* For existing extended attribute */
+		size_t size = OCFS2_XATTR_SIZE(name_len) +
+			OCFS2_XATTR_SIZE(le64_to_cpu(xs->here->xe_value_size));
+		size_t offs = le16_to_cpu(xs->here->xe_name_offset);
+		void *val = xs->base + offs;
+
+		if (ocfs2_xattr_is_local(xs->here) && size == size_l) {
+			/* Replace existing local xattr with tree root */
+			ret = ocfs2_xattr_set_value_outside(inode, xi, xs,
+							    offs);
+			if (ret < 0)
+				mlog_errno(ret);
+			goto out;
+		} else if (!ocfs2_xattr_is_local(xs->here)) {
+			/* For existing xattr which has value outside */
+			struct ocfs2_xattr_value_root *xv = NULL;
+			xv = (struct ocfs2_xattr_value_root *)(val +
+				OCFS2_XATTR_SIZE(name_len));
+
+			if (xi->value_len > OCFS2_XATTR_INLINE_SIZE) {
+				/*
+				 * If new value need set outside also,
+				 * first truncate old value to new value,
+				 * then set new value with set_value_outside().
+				 */
+				ret = ocfs2_xattr_value_truncate(inode,
+								 xs->xattr_bh,
+								 xv,
+								 xi->value_len);
+				if (ret < 0) {
+					mlog_errno(ret);
+					goto out;
+				}
+
+				ret = __ocfs2_xattr_set_value_outside(inode,
+								xv,
+								xi->value,
+								xi->value_len);
+				if (ret < 0) {
+					mlog_errno(ret);
+					goto out;
+				}
+
+				ret = ocfs2_xattr_update_entry(inode,
+							       xi,
+							       xs,
+							       offs);
+				if (ret < 0)
+					mlog_errno(ret);
+				goto out;
+			} else {
+				/*
+				 * If new value need set in local,
+				 * just trucate old value to zero.
+				 */
+				 ret = ocfs2_xattr_value_truncate(inode,
+								 xs->xattr_bh,
+								 xv,
+								 0);
+				if (ret < 0)
+					mlog_errno(ret);
+			}
+		}
+	}
+
+	handle = ocfs2_start_trans((OCFS2_SB(inode->i_sb)),
+				   OCFS2_INODE_UPDATE_CREDITS);
+	if (IS_ERR(handle)) {
+		ret = PTR_ERR(handle);
+		mlog_errno(ret);
+		goto out;
+	}
+
+	ret = ocfs2_journal_access(handle, inode, xs->inode_bh,
+				   OCFS2_JOURNAL_ACCESS_WRITE);
+	if (ret) {
+		mlog_errno(ret);
+		goto out_commit;
+	}
+
+	if (!(flag & OCFS2_INLINE_XATTR_FL)) {
+		/* set extended attribute in external block. */
+		ret = ocfs2_extend_trans(handle,
+					 OCFS2_INODE_UPDATE_CREDITS +
+					 OCFS2_XATTR_BLOCK_UPDATE_CREDITS);
+		if (ret) {
+			mlog_errno(ret);
+			goto out_commit;
+		}
+		ret = ocfs2_journal_access(handle, inode, xs->xattr_bh,
+					   OCFS2_JOURNAL_ACCESS_WRITE);
+		if (ret) {
+			mlog_errno(ret);
+			goto out_commit;
+		}
+	}
+
+	/*
+	 * Set value in local, include set tree root in local.
+	 * This is the first step for value size >INLINE_SIZE.
+	 */
+	ocfs2_xattr_set_entry_local(inode, &xi_l, xs, last, min_offs);
+
+	if (!(flag & OCFS2_INLINE_XATTR_FL)) {
+		ret = ocfs2_journal_dirty(handle, xs->xattr_bh);
+		if (ret < 0) {
+			mlog_errno(ret);
+			goto out_commit;
+		}
+	}
+
+	if (!(oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL) &&
+	    (flag & OCFS2_INLINE_XATTR_FL)) {
+		struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+		unsigned int xattrsize = osb->s_xattr_inline_size;
+
+		/*
+		 * Adjust extent record count or inline data size
+		 * to reserve space for extended attribute.
+		 */
+		if (oi->ip_dyn_features & OCFS2_INLINE_DATA_FL) {
+			struct ocfs2_inline_data *idata = &di->id2.i_data;
+			le16_add_cpu(&idata->id_count, -xattrsize);
+		} else if (!(ocfs2_inode_is_fast_symlink(inode))) {
+			struct ocfs2_extent_list *el = &di->id2.i_list;
+			le16_add_cpu(&el->l_count, -(xattrsize /
+					sizeof(struct ocfs2_extent_rec)));
+		}
+		di->i_xattr_inline_size = cpu_to_le16(xattrsize);
+	}
+	/* Update xattr flag */
+	spin_lock(&oi->ip_lock);
+	oi->ip_dyn_features |= flag;
+	di->i_dyn_features = cpu_to_le16(oi->ip_dyn_features);
+	spin_unlock(&oi->ip_lock);
+	/* Update inode ctime */
+	inode->i_ctime = CURRENT_TIME;
+	di->i_ctime = cpu_to_le64(inode->i_ctime.tv_sec);
+	di->i_ctime_nsec = cpu_to_le32(inode->i_ctime.tv_nsec);
+
+	ret = ocfs2_journal_dirty(handle, xs->inode_bh);
+	if (ret < 0)
+		mlog_errno(ret);
+
+out_commit:
+	ocfs2_commit_trans(OCFS2_SB(inode->i_sb), handle);
+
+	if (!ret && xi->value_len > OCFS2_XATTR_INLINE_SIZE) {
+		/*
+		 * Set value outside in B tree.
+		 * This is the second step for value size > INLINE_SIZE.
+		 */
+		size_t offs = le16_to_cpu(xs->here->xe_name_offset);
+		ret = ocfs2_xattr_set_value_outside(inode, xi, xs, offs);
+		if (ret < 0) {
+			int ret2;
+
+			mlog_errno(ret);
+			/*
+			 * If set value outside failed, we have to clean
+			 * the junk tree root we have already set in local.
+			 */
+			ret2 = ocfs2_xattr_cleanup(inode, xi, xs, offs);
+			if (ret2 < 0)
+				mlog_errno(ret2);
+		}
+	}
+out:
+	return ret;
+
+}
+
+static int ocfs2_remove_value_outside(struct inode*inode,
+				      struct buffer_head *bh,
+				      struct ocfs2_xattr_header *header)
+{
+	int ret = 0, i;
+
+	for (i = 0; i < le16_to_cpu(header->xh_count); i++) {
+		struct ocfs2_xattr_entry *entry = &header->xh_entries[i];
+
+		if (!ocfs2_xattr_is_local(entry)) {
+			struct ocfs2_xattr_value_root *xv;
+			void *val;
+
+			val = (void *)header +
+				le16_to_cpu(entry->xe_name_offset);
+			xv = (struct ocfs2_xattr_value_root *)
+				(val + OCFS2_XATTR_SIZE(entry->xe_name_len));
+			ret = ocfs2_xattr_value_truncate(inode, bh, xv, 0);
+			if (ret < 0) {
+				mlog_errno(ret);
+				return ret;
+			}
+		}
+	}
+
+	return ret;
+}
+
+static int ocfs2_xattr_ibody_remove(struct inode *inode,
+				    struct buffer_head *di_bh)
+{
+
+	struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data;
+	struct ocfs2_xattr_header *header;
+	int ret;
+
+	header = (struct ocfs2_xattr_header *)
+		 ((void *)di + inode->i_sb->s_blocksize -
+		 le16_to_cpu(di->i_xattr_inline_size));
+
+	ret = ocfs2_remove_value_outside(inode, di_bh, header);
+
+	return ret;
+}
+
+static int ocfs2_xattr_block_remove(struct inode *inode,
+				    struct buffer_head *blk_bh)
+{
+	struct ocfs2_xattr_block *xb;
+	int ret = 0;
+
+	xb = (struct ocfs2_xattr_block *)blk_bh->b_data;
+	if (!(le16_to_cpu(xb->xb_flags) & OCFS2_XATTR_INDEXED)) {
+		struct ocfs2_xattr_header *header = &(xb->xb_attrs.xb_header);
+		ret = ocfs2_remove_value_outside(inode, blk_bh, header);
+	} else
+		ret = ocfs2_delete_xattr_index_block(inode, blk_bh);
+
+	return ret;
+}
+
+static int ocfs2_xattr_free_block(struct inode *inode,
+				  u64 block)
+{
+	struct inode *xb_alloc_inode;
+	struct buffer_head *xb_alloc_bh = NULL;
+	struct buffer_head *blk_bh = NULL;
+	struct ocfs2_xattr_block *xb;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+	handle_t *handle;
+	int ret = 0;
+	u64 blk, bg_blkno;
+	u16 bit;
+
+	ret = ocfs2_read_block(inode, block, &blk_bh);
+	if (ret < 0) {
+		mlog_errno(ret);
+		goto out;
+	}
+
+	/*Verify the signature of xattr block*/
+	if (memcmp((void *)blk_bh->b_data, OCFS2_XATTR_BLOCK_SIGNATURE,
+		   strlen(OCFS2_XATTR_BLOCK_SIGNATURE))) {
+		ret = -EFAULT;
+		goto out;
+	}
+
+	ret = ocfs2_xattr_block_remove(inode, blk_bh);
+	if (ret < 0) {
+		mlog_errno(ret);
+		goto out;
+	}
+
+	xb = (struct ocfs2_xattr_block *)blk_bh->b_data;
+	blk = le64_to_cpu(xb->xb_blkno);
+	bit = le16_to_cpu(xb->xb_suballoc_bit);
+	bg_blkno = ocfs2_which_suballoc_group(blk, bit);
+
+	xb_alloc_inode = ocfs2_get_system_file_inode(osb,
+				EXTENT_ALLOC_SYSTEM_INODE,
+				le16_to_cpu(xb->xb_suballoc_slot));
+	if (!xb_alloc_inode) {
+		ret = -ENOMEM;
+		mlog_errno(ret);
+		goto out;
+	}
+	mutex_lock(&xb_alloc_inode->i_mutex);
+
+	ret = ocfs2_inode_lock(xb_alloc_inode, &xb_alloc_bh, 1);
+	if (ret < 0) {
+		mlog_errno(ret);
+		goto out_mutex;
+	}
+
+	handle = ocfs2_start_trans(osb, OCFS2_SUBALLOC_FREE);
+	if (IS_ERR(handle)) {
+		ret = PTR_ERR(handle);
+		mlog_errno(ret);
+		goto out_unlock;
+	}
+
+	ret = ocfs2_free_suballoc_bits(handle, xb_alloc_inode, xb_alloc_bh,
+				       bit, bg_blkno, 1);
+	if (ret < 0)
+		mlog_errno(ret);
+
+	ocfs2_commit_trans(osb, handle);
+out_unlock:
+	ocfs2_inode_unlock(xb_alloc_inode, 1);
+	brelse(xb_alloc_bh);
+out_mutex:
+	mutex_unlock(&xb_alloc_inode->i_mutex);
+	iput(xb_alloc_inode);
+out:
+	brelse(blk_bh);
+	return ret;
+}
+
+/*
+ * ocfs2_xattr_remove()
+ *
+ * Free extended attribute resources associated with this inode.
+ */
+int ocfs2_xattr_remove(struct inode *inode, struct buffer_head *di_bh)
+{
+	struct ocfs2_inode_info *oi = OCFS2_I(inode);
+	struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data;
+	handle_t *handle;
+	int ret;
+
+	if (!ocfs2_supports_xattr(OCFS2_SB(inode->i_sb)))
+		return 0;
+
+	if (!(oi->ip_dyn_features & OCFS2_HAS_XATTR_FL))
+		return 0;
+
+	if (oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL) {
+		ret = ocfs2_xattr_ibody_remove(inode, di_bh);
+		if (ret < 0) {
+			mlog_errno(ret);
+			goto out;
+		}
+	}
+
+	if (di->i_xattr_loc) {
+		ret = ocfs2_xattr_free_block(inode,
+					     le64_to_cpu(di->i_xattr_loc));
+		if (ret < 0) {
+			mlog_errno(ret);
+			goto out;
+		}
+	}
+
+	handle = ocfs2_start_trans((OCFS2_SB(inode->i_sb)),
+				   OCFS2_INODE_UPDATE_CREDITS);
+	if (IS_ERR(handle)) {
+		ret = PTR_ERR(handle);
+		mlog_errno(ret);
+		goto out;
+	}
+	ret = ocfs2_journal_access(handle, inode, di_bh,
+				   OCFS2_JOURNAL_ACCESS_WRITE);
+	if (ret) {
+		mlog_errno(ret);
+		goto out_commit;
+	}
+
+	di->i_xattr_loc = 0;
+
+	spin_lock(&oi->ip_lock);
+	oi->ip_dyn_features &= ~(OCFS2_INLINE_XATTR_FL | OCFS2_HAS_XATTR_FL);
+	di->i_dyn_features = cpu_to_le16(oi->ip_dyn_features);
+	spin_unlock(&oi->ip_lock);
+
+	ret = ocfs2_journal_dirty(handle, di_bh);
+	if (ret < 0)
+		mlog_errno(ret);
+out_commit:
+	ocfs2_commit_trans(OCFS2_SB(inode->i_sb), handle);
+out:
+	return ret;
+}
+
+static int ocfs2_xattr_has_space_inline(struct inode *inode,
+					struct ocfs2_dinode *di)
+{
+	struct ocfs2_inode_info *oi = OCFS2_I(inode);
+	unsigned int xattrsize = OCFS2_SB(inode->i_sb)->s_xattr_inline_size;
+	int free;
+
+	if (xattrsize < OCFS2_MIN_XATTR_INLINE_SIZE)
+		return 0;
+
+	if (oi->ip_dyn_features & OCFS2_INLINE_DATA_FL) {
+		struct ocfs2_inline_data *idata = &di->id2.i_data;
+		free = le16_to_cpu(idata->id_count) - le64_to_cpu(di->i_size);
+	} else if (ocfs2_inode_is_fast_symlink(inode)) {
+		free = ocfs2_fast_symlink_chars(inode->i_sb) -
+			le64_to_cpu(di->i_size);
+	} else {
+		struct ocfs2_extent_list *el = &di->id2.i_list;
+		free = (le16_to_cpu(el->l_count) -
+			le16_to_cpu(el->l_next_free_rec)) *
+			sizeof(struct ocfs2_extent_rec);
+	}
+	if (free >= xattrsize)
+		return 1;
+
+	return 0;
+}
+
+/*
+ * ocfs2_xattr_ibody_find()
+ *
+ * Find extended attribute in inode block and
+ * fill search info into struct ocfs2_xattr_search.
+ */
+static int ocfs2_xattr_ibody_find(struct inode *inode,
+				  int name_index,
+				  const char *name,
+				  struct ocfs2_xattr_search *xs)
+{
+	struct ocfs2_inode_info *oi = OCFS2_I(inode);
+	struct ocfs2_dinode *di = (struct ocfs2_dinode *)xs->inode_bh->b_data;
+	int ret;
+	int has_space = 0;
+
+	if (inode->i_sb->s_blocksize == OCFS2_MIN_BLOCKSIZE)
+		return 0;
+
+	if (!(oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL)) {
+		down_read(&oi->ip_alloc_sem);
+		has_space = ocfs2_xattr_has_space_inline(inode, di);
+		up_read(&oi->ip_alloc_sem);
+		if (!has_space)
+			return 0;
+	}
+
+	xs->xattr_bh = xs->inode_bh;
+	xs->end = (void *)di + inode->i_sb->s_blocksize;
+	if (oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL)
+		xs->header = (struct ocfs2_xattr_header *)
+			(xs->end - le16_to_cpu(di->i_xattr_inline_size));
+	else
+		xs->header = (struct ocfs2_xattr_header *)
+			(xs->end - OCFS2_SB(inode->i_sb)->s_xattr_inline_size);
+	xs->base = (void *)xs->header;
+	xs->here = xs->header->xh_entries;
+
+	/* Find the named attribute. */
+	if (oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL) {
+		ret = ocfs2_xattr_find_entry(name_index, name, xs);
+		if (ret && ret != -ENODATA)
+			return ret;
+		xs->not_found = ret;
+	}
+
+	return 0;
+}
+
+/*
+ * ocfs2_xattr_ibody_set()
+ *
+ * Set, replace or remove an extended attribute into inode block.
+ *
+ */
+static int ocfs2_xattr_ibody_set(struct inode *inode,
+				 struct ocfs2_xattr_info *xi,
+				 struct ocfs2_xattr_search *xs)
+{
+	struct ocfs2_inode_info *oi = OCFS2_I(inode);
+	struct ocfs2_dinode *di = (struct ocfs2_dinode *)xs->inode_bh->b_data;
+	int ret;
+
+	if (inode->i_sb->s_blocksize == OCFS2_MIN_BLOCKSIZE)
+		return -ENOSPC;
+
+	down_write(&oi->ip_alloc_sem);
+	if (!(oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL)) {
+		if (!ocfs2_xattr_has_space_inline(inode, di)) {
+			ret = -ENOSPC;
+			goto out;
+		}
+	}
+
+	ret = ocfs2_xattr_set_entry(inode, xi, xs,
+				(OCFS2_INLINE_XATTR_FL | OCFS2_HAS_XATTR_FL));
+out:
+	up_write(&oi->ip_alloc_sem);
+
+	return ret;
+}
+
+/*
+ * ocfs2_xattr_block_find()
+ *
+ * Find extended attribute in external block and
+ * fill search info into struct ocfs2_xattr_search.
+ */
+static int ocfs2_xattr_block_find(struct inode *inode,
+				  int name_index,
+				  const char *name,
+				  struct ocfs2_xattr_search *xs)
+{
+	struct ocfs2_dinode *di = (struct ocfs2_dinode *)xs->inode_bh->b_data;
+	struct buffer_head *blk_bh = NULL;
+	struct ocfs2_xattr_block *xb;
+	int ret = 0;
+
+	if (!di->i_xattr_loc)
+		return ret;
+
+	ret = ocfs2_read_block(inode, le64_to_cpu(di->i_xattr_loc), &blk_bh);
+	if (ret < 0) {
+		mlog_errno(ret);
+		return ret;
+	}
+	/*Verify the signature of xattr block*/
+	if (memcmp((void *)blk_bh->b_data, OCFS2_XATTR_BLOCK_SIGNATURE,
+		   strlen(OCFS2_XATTR_BLOCK_SIGNATURE))) {
+			ret = -EFAULT;
+			goto cleanup;
+	}
+
+	xs->xattr_bh = blk_bh;
+	xb = (struct ocfs2_xattr_block *)blk_bh->b_data;
+
+	if (!(le16_to_cpu(xb->xb_flags) & OCFS2_XATTR_INDEXED)) {
+		xs->header = &xb->xb_attrs.xb_header;
+		xs->base = (void *)xs->header;
+		xs->end = (void *)(blk_bh->b_data) + blk_bh->b_size;
+		xs->here = xs->header->xh_entries;
+
+		ret = ocfs2_xattr_find_entry(name_index, name, xs);
+	} else
+		ret = ocfs2_xattr_index_block_find(inode, blk_bh,
+						   name_index,
+						   name, xs);
+
+	if (ret && ret != -ENODATA) {
+		xs->xattr_bh = NULL;
+		goto cleanup;
+	}
+	xs->not_found = ret;
+	return 0;
+cleanup:
+	brelse(blk_bh);
+
+	return ret;
+}
+
+/*
+ * When all the xattrs are deleted from index btree, the ocfs2_xattr_tree
+ * will be erased and ocfs2_xattr_block will have its ocfs2_xattr_header
+ * re-initialized.
+ */
+static int ocfs2_restore_xattr_block(struct inode *inode,
+				     struct ocfs2_xattr_search *xs)
+{
+	int ret;
+	handle_t *handle;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+	struct ocfs2_xattr_block *xb =
+		(struct ocfs2_xattr_block *)xs->xattr_bh->b_data;
+	struct ocfs2_extent_list *el = &xb->xb_attrs.xb_root.xt_list;
+	u16 xb_flags = le16_to_cpu(xb->xb_flags);
+
+	BUG_ON(!(xb_flags & OCFS2_XATTR_INDEXED) ||
+		le16_to_cpu(el->l_next_free_rec) != 0);
+
+	handle = ocfs2_start_trans(osb, OCFS2_XATTR_BLOCK_UPDATE_CREDITS);
+	if (IS_ERR(handle)) {
+		ret = PTR_ERR(handle);
+		handle = NULL;
+		goto out;
+	}
+
+	ret = ocfs2_journal_access(handle, inode, xs->xattr_bh,
+				   OCFS2_JOURNAL_ACCESS_WRITE);
+	if (ret < 0) {
+		mlog_errno(ret);
+		goto out_commit;
+	}
+
+	memset(&xb->xb_attrs, 0, inode->i_sb->s_blocksize -
+	       offsetof(struct ocfs2_xattr_block, xb_attrs));
+
+	xb->xb_flags = cpu_to_le16(xb_flags & ~OCFS2_XATTR_INDEXED);
+
+	ocfs2_journal_dirty(handle, xs->xattr_bh);
+
+out_commit:
+	ocfs2_commit_trans(osb, handle);
+out:
+	return ret;
+}
+
+/*
+ * ocfs2_xattr_block_set()
+ *
+ * Set, replace or remove an extended attribute into external block.
+ *
+ */
+static int ocfs2_xattr_block_set(struct inode *inode,
+				 struct ocfs2_xattr_info *xi,
+				 struct ocfs2_xattr_search *xs)
+{
+	struct buffer_head *new_bh = NULL;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+	struct ocfs2_dinode *di =  (struct ocfs2_dinode *)xs->inode_bh->b_data;
+	struct ocfs2_alloc_context *meta_ac = NULL;
+	handle_t *handle = NULL;
+	struct ocfs2_xattr_block *xblk = NULL;
+	u16 suballoc_bit_start;
+	u32 num_got;
+	u64 first_blkno;
+	int ret;
+
+	if (!xs->xattr_bh) {
+		/*
+		 * Alloc one external block for extended attribute
+		 * outside of inode.
+		 */
+		ret = ocfs2_reserve_new_metadata_blocks(osb, 1, &meta_ac);
+		if (ret < 0) {
+			mlog_errno(ret);
+			goto out;
+		}
+		handle = ocfs2_start_trans(osb,
+					   OCFS2_XATTR_BLOCK_CREATE_CREDITS);
+		if (IS_ERR(handle)) {
+			ret = PTR_ERR(handle);
+			mlog_errno(ret);
+			goto out;
+		}
+		ret = ocfs2_journal_access(handle, inode, xs->inode_bh,
+					   OCFS2_JOURNAL_ACCESS_CREATE);
+		if (ret < 0) {
+			mlog_errno(ret);
+			goto out_commit;
+		}
+
+		ret = ocfs2_claim_metadata(osb, handle, meta_ac, 1,
+					   &suballoc_bit_start, &num_got,
+					   &first_blkno);
+		if (ret < 0) {
+			mlog_errno(ret);
+			goto out_commit;
+		}
+
+		new_bh = sb_getblk(inode->i_sb, first_blkno);
+		ocfs2_set_new_buffer_uptodate(inode, new_bh);
+
+		ret = ocfs2_journal_access(handle, inode, new_bh,
+					   OCFS2_JOURNAL_ACCESS_CREATE);
+		if (ret < 0) {
+			mlog_errno(ret);
+			goto out_commit;
+		}
+
+		/* Initialize ocfs2_xattr_block */
+		xs->xattr_bh = new_bh;
+		xblk = (struct ocfs2_xattr_block *)new_bh->b_data;
+		memset(xblk, 0, inode->i_sb->s_blocksize);
+		strcpy((void *)xblk, OCFS2_XATTR_BLOCK_SIGNATURE);
+		xblk->xb_suballoc_slot = cpu_to_le16(osb->slot_num);
+		xblk->xb_suballoc_bit = cpu_to_le16(suballoc_bit_start);
+		xblk->xb_fs_generation = cpu_to_le32(osb->fs_generation);
+		xblk->xb_blkno = cpu_to_le64(first_blkno);
+
+		xs->header = &xblk->xb_attrs.xb_header;
+		xs->base = (void *)xs->header;
+		xs->end = (void *)xblk + inode->i_sb->s_blocksize;
+		xs->here = xs->header->xh_entries;
+
+
+		ret = ocfs2_journal_dirty(handle, new_bh);
+		if (ret < 0) {
+			mlog_errno(ret);
+			goto out_commit;
+		}
+		di->i_xattr_loc = cpu_to_le64(first_blkno);
+		ret = ocfs2_journal_dirty(handle, xs->inode_bh);
+		if (ret < 0)
+			mlog_errno(ret);
+out_commit:
+		ocfs2_commit_trans(osb, handle);
+out:
+		if (meta_ac)
+			ocfs2_free_alloc_context(meta_ac);
+		if (ret < 0)
+			return ret;
+	} else
+		xblk = (struct ocfs2_xattr_block *)xs->xattr_bh->b_data;
+
+	if (!(le16_to_cpu(xblk->xb_flags) & OCFS2_XATTR_INDEXED)) {
+		/* Set extended attribute into external block */
+		ret = ocfs2_xattr_set_entry(inode, xi, xs, OCFS2_HAS_XATTR_FL);
+		if (!ret || ret != -ENOSPC)
+			goto end;
+
+		ret = ocfs2_xattr_create_index_block(inode, xs);
+		if (ret)
+			goto end;
+	}
+
+	ret = ocfs2_xattr_set_entry_index_block(inode, xi, xs);
+	if (!ret && xblk->xb_attrs.xb_root.xt_list.l_next_free_rec == 0)
+		ret = ocfs2_restore_xattr_block(inode, xs);
+
+end:
+
+	return ret;
+}
+
+/*
+ * ocfs2_xattr_set()
+ *
+ * Set, replace or remove an extended attribute for this inode.
+ * value is NULL to remove an existing extended attribute, else either
+ * create or replace an extended attribute.
+ */
+int ocfs2_xattr_set(struct inode *inode,
+		    int name_index,
+		    const char *name,
+		    const void *value,
+		    size_t value_len,
+		    int flags)
+{
+	struct buffer_head *di_bh = NULL;
+	struct ocfs2_dinode *di;
+	int ret;
+	u16 i, blk_per_bucket = ocfs2_blocks_per_xattr_bucket(inode->i_sb);
+
+	struct ocfs2_xattr_info xi = {
+		.name_index = name_index,
+		.name = name,
+		.value = value,
+		.value_len = value_len,
+	};
+
+	struct ocfs2_xattr_search xis = {
+		.not_found = -ENODATA,
+	};
+
+	struct ocfs2_xattr_search xbs = {
+		.not_found = -ENODATA,
+	};
+
+	if (!ocfs2_supports_xattr(OCFS2_SB(inode->i_sb)))
+		return -EOPNOTSUPP;
+
+	ret = ocfs2_inode_lock(inode, &di_bh, 1);
+	if (ret < 0) {
+		mlog_errno(ret);
+		return ret;
+	}
+	xis.inode_bh = xbs.inode_bh = di_bh;
+	di = (struct ocfs2_dinode *)di_bh->b_data;
+
+	down_write(&OCFS2_I(inode)->ip_xattr_sem);
+	/*
+	 * Scan inode and external block to find the same name
+	 * extended attribute and collect search infomation.
+	 */
+	ret = ocfs2_xattr_ibody_find(inode, name_index, name, &xis);
+	if (ret)
+		goto cleanup;
+	if (xis.not_found) {
+		ret = ocfs2_xattr_block_find(inode, name_index, name, &xbs);
+		if (ret)
+			goto cleanup;
+	}
+
+	if (xis.not_found && xbs.not_found) {
+		ret = -ENODATA;
+		if (flags & XATTR_REPLACE)
+			goto cleanup;
+		ret = 0;
+		if (!value)
+			goto cleanup;
+	} else {
+		ret = -EEXIST;
+		if (flags & XATTR_CREATE)
+			goto cleanup;
+	}
+
+	if (!value) {
+		/* Remove existing extended attribute */
+		if (!xis.not_found)
+			ret = ocfs2_xattr_ibody_set(inode, &xi, &xis);
+		else if (!xbs.not_found)
+			ret = ocfs2_xattr_block_set(inode, &xi, &xbs);
+	} else {
+		/* We always try to set extended attribute into inode first*/
+		ret = ocfs2_xattr_ibody_set(inode, &xi, &xis);
+		if (!ret && !xbs.not_found) {
+			/*
+			 * If succeed and that extended attribute existing in
+			 * external block, then we will remove it.
+			 */
+			xi.value = NULL;
+			xi.value_len = 0;
+			ret = ocfs2_xattr_block_set(inode, &xi, &xbs);
+		} else if (ret == -ENOSPC) {
+			if (di->i_xattr_loc && !xbs.xattr_bh) {
+				ret = ocfs2_xattr_block_find(inode, name_index,
+							     name, &xbs);
+				if (ret)
+					goto cleanup;
+			}
+			/*
+			 * If no space in inode, we will set extended attribute
+			 * into external block.
+			 */
+			ret = ocfs2_xattr_block_set(inode, &xi, &xbs);
+			if (ret)
+				goto cleanup;
+			if (!xis.not_found) {
+				/*
+				 * If succeed and that extended attribute
+				 * existing in inode, we will remove it.
+				 */
+				xi.value = NULL;
+				xi.value_len = 0;
+				ret = ocfs2_xattr_ibody_set(inode, &xi, &xis);
+			}
+		}
+	}
+cleanup:
+	up_write(&OCFS2_I(inode)->ip_xattr_sem);
+	ocfs2_inode_unlock(inode, 1);
+	brelse(di_bh);
+	brelse(xbs.xattr_bh);
+	for (i = 0; i < blk_per_bucket; i++)
+		brelse(xbs.bucket.bhs[i]);
+
+	return ret;
+}
+
+/*
+ * Find the xattr extent rec which may contains name_hash.
+ * e_cpos will be the first name hash of the xattr rec.
+ * el must be the ocfs2_xattr_header.xb_attrs.xb_root.xt_list.
+ */
+static int ocfs2_xattr_get_rec(struct inode *inode,
+			       u32 name_hash,
+			       u64 *p_blkno,
+			       u32 *e_cpos,
+			       u32 *num_clusters,
+			       struct ocfs2_extent_list *el)
+{
+	int ret = 0, i;
+	struct buffer_head *eb_bh = NULL;
+	struct ocfs2_extent_block *eb;
+	struct ocfs2_extent_rec *rec = NULL;
+	u64 e_blkno = 0;
+
+	if (el->l_tree_depth) {
+		ret = ocfs2_find_leaf(inode, el, name_hash, &eb_bh);
+		if (ret) {
+			mlog_errno(ret);
+			goto out;
+		}
+
+		eb = (struct ocfs2_extent_block *) eb_bh->b_data;
+		el = &eb->h_list;
+
+		if (el->l_tree_depth) {
+			ocfs2_error(inode->i_sb,
+				    "Inode %lu has non zero tree depth in "
+				    "xattr tree block %llu\n", inode->i_ino,
+				    (unsigned long long)eb_bh->b_blocknr);
+			ret = -EROFS;
+			goto out;
+		}
+	}
+
+	for (i = le16_to_cpu(el->l_next_free_rec) - 1; i >= 0; i--) {
+		rec = &el->l_recs[i];
+
+		if (le32_to_cpu(rec->e_cpos) <= name_hash) {
+			e_blkno = le64_to_cpu(rec->e_blkno);
+			break;
+		}
+	}
+
+	if (!e_blkno) {
+		ocfs2_error(inode->i_sb, "Inode %lu has bad extent "
+			    "record (%u, %u, 0) in xattr", inode->i_ino,
+			    le32_to_cpu(rec->e_cpos),
+			    ocfs2_rec_clusters(el, rec));
+		ret = -EROFS;
+		goto out;
+	}
+
+	*p_blkno = le64_to_cpu(rec->e_blkno);
+	*num_clusters = le16_to_cpu(rec->e_leaf_clusters);
+	if (e_cpos)
+		*e_cpos = le32_to_cpu(rec->e_cpos);
+out:
+	brelse(eb_bh);
+	return ret;
+}
+
+typedef int (xattr_bucket_func)(struct inode *inode,
+				struct ocfs2_xattr_bucket *bucket,
+				void *para);
+
+static int ocfs2_find_xe_in_bucket(struct inode *inode,
+				   struct buffer_head *header_bh,
+				   int name_index,
+				   const char *name,
+				   u32 name_hash,
+				   u16 *xe_index,
+				   int *found)
+{
+	int i, ret = 0, cmp = 1, block_off, new_offset;
+	struct ocfs2_xattr_header *xh =
+			(struct ocfs2_xattr_header *)header_bh->b_data;
+	size_t name_len = strlen(name);
+	struct ocfs2_xattr_entry *xe = NULL;
+	struct buffer_head *name_bh = NULL;
+	char *xe_name;
+
+	/*
+	 * We don't use binary search in the bucket because there
+	 * may be multiple entries with the same name hash.
+	 */
+	for (i = 0; i < le16_to_cpu(xh->xh_count); i++) {
+		xe = &xh->xh_entries[i];
+
+		if (name_hash > le32_to_cpu(xe->xe_name_hash))
+			continue;
+		else if (name_hash < le32_to_cpu(xe->xe_name_hash))
+			break;
+
+		cmp = name_index - ocfs2_xattr_get_type(xe);
+		if (!cmp)
+			cmp = name_len - xe->xe_name_len;
+		if (cmp)
+			continue;
+
+		ret = ocfs2_xattr_bucket_get_name_value(inode,
+							xh,
+							i,
+							&block_off,
+							&new_offset);
+		if (ret) {
+			mlog_errno(ret);
+			break;
+		}
+
+		ret = ocfs2_read_block(inode, header_bh->b_blocknr + block_off,
+				       &name_bh);
+		if (ret) {
+			mlog_errno(ret);
+			break;
+		}
+		xe_name = name_bh->b_data + new_offset;
+
+		cmp = memcmp(name, xe_name, name_len);
+		brelse(name_bh);
+		name_bh = NULL;
+
+		if (cmp == 0) {
+			*xe_index = i;
+			*found = 1;
+			ret = 0;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+/*
+ * Find the specified xattr entry in a series of buckets.
+ * This series start from p_blkno and last for num_clusters.
+ * The ocfs2_xattr_header.xh_num_buckets of the first bucket contains
+ * the num of the valid buckets.
+ *
+ * Return the buffer_head this xattr should reside in. And if the xattr's
+ * hash is in the gap of 2 buckets, return the lower bucket.
+ */
+static int ocfs2_xattr_bucket_find(struct inode *inode,
+				   int name_index,
+				   const char *name,
+				   u32 name_hash,
+				   u64 p_blkno,
+				   u32 first_hash,
+				   u32 num_clusters,
+				   struct ocfs2_xattr_search *xs)
+{
+	int ret, found = 0;
+	struct buffer_head *bh = NULL;
+	struct buffer_head *lower_bh = NULL;
+	struct ocfs2_xattr_header *xh = NULL;
+	struct ocfs2_xattr_entry *xe = NULL;
+	u16 index = 0;
+	u16 blk_per_bucket = ocfs2_blocks_per_xattr_bucket(inode->i_sb);
+	int low_bucket = 0, bucket, high_bucket;
+	u32 last_hash;
+	u64 blkno;
+
+	ret = ocfs2_read_block(inode, p_blkno, &bh);
+	if (ret) {
+		mlog_errno(ret);
+		goto out;
+	}
+
+	xh = (struct ocfs2_xattr_header *)bh->b_data;
+	high_bucket = le16_to_cpu(xh->xh_num_buckets) - 1;
+
+	while (low_bucket <= high_bucket) {
+		brelse(bh);
+		bh = NULL;
+		bucket = (low_bucket + high_bucket) / 2;
+
+		blkno = p_blkno + bucket * blk_per_bucket;
+
+		ret = ocfs2_read_block(inode, blkno, &bh);
+		if (ret) {
+			mlog_errno(ret);
+			goto out;
+		}
+
+		xh = (struct ocfs2_xattr_header *)bh->b_data;
+		xe = &xh->xh_entries[0];
+		if (name_hash < le32_to_cpu(xe->xe_name_hash)) {
+			high_bucket = bucket - 1;
+			continue;
+		}
+
+		/*
+		 * Check whether the hash of the last entry in our
+		 * bucket is larger than the search one. for an empty
+		 * bucket, the last one is also the first one.
+		 */
+		if (xh->xh_count)
+			xe = &xh->xh_entries[le16_to_cpu(xh->xh_count) - 1];
+
+		last_hash = le32_to_cpu(xe->xe_name_hash);
+
+		/* record lower_bh which may be the insert place. */
+		brelse(lower_bh);
+		lower_bh = bh;
+		bh = NULL;
+
+		if (name_hash > le32_to_cpu(xe->xe_name_hash)) {
+			low_bucket = bucket + 1;
+			continue;
+		}
+
+		/* the searched xattr should reside in this bucket if exists. */
+		ret = ocfs2_find_xe_in_bucket(inode, lower_bh,
+					      name_index, name, name_hash,
+					      &index, &found);
+		if (ret) {
+			mlog_errno(ret);
+			goto out;
+		}
+		break;
+	}
+
+	/*
+	 * Record the bucket we have found.
+	 * When the xattr's hash value is in the gap of 2 buckets, we will
+	 * always set it to the previous bucket.
+	 */
+	if (!lower_bh) {
+		/*
+		 * We can't find any bucket whose first name_hash is less
+		 * than the find name_hash.
+		 */
+		BUG_ON(bh->b_blocknr != p_blkno);
+		lower_bh = bh;
+		bh = NULL;
+	}
+	xs->bucket.bhs[0] = lower_bh;
+	xs->bucket.xh = (struct ocfs2_xattr_header *)
+					xs->bucket.bhs[0]->b_data;
+	lower_bh = NULL;
+
+	xs->header = xs->bucket.xh;
+	xs->base = xs->bucket.bhs[0]->b_data;
+	xs->end = xs->base + inode->i_sb->s_blocksize;
+
+	if (found) {
+		/*
+		 * If we have found the xattr enty, read all the blocks in
+		 * this bucket.
+		 */
+		ret = ocfs2_read_blocks(inode, xs->bucket.bhs[0]->b_blocknr + 1,
+					blk_per_bucket - 1, &xs->bucket.bhs[1],
+					OCFS2_BH_CACHED);
+		if (ret) {
+			mlog_errno(ret);
+			goto out;
+		}
+
+		xs->here = &xs->header->xh_entries[index];
+		mlog(0, "find xattr %s in bucket %llu, entry = %u\n", name,
+		     (unsigned long long)xs->bucket.bhs[0]->b_blocknr, index);
+	} else
+		ret = -ENODATA;
+
+out:
+	brelse(bh);
+	brelse(lower_bh);
+	return ret;
+}
+
+static int ocfs2_xattr_index_block_find(struct inode *inode,
+					struct buffer_head *root_bh,
+					int name_index,
+					const char *name,
+					struct ocfs2_xattr_search *xs)
+{
+	int ret;
+	struct ocfs2_xattr_block *xb =
+			(struct ocfs2_xattr_block *)root_bh->b_data;
+	struct ocfs2_xattr_tree_root *xb_root = &xb->xb_attrs.xb_root;
+	struct ocfs2_extent_list *el = &xb_root->xt_list;
+	u64 p_blkno = 0;
+	u32 first_hash, num_clusters = 0;
+	u32 name_hash = ocfs2_xattr_name_hash(inode, name, strlen(name));
+
+	if (le16_to_cpu(el->l_next_free_rec) == 0)
+		return -ENODATA;
+
+	mlog(0, "find xattr %s, hash = %u, index = %d in xattr tree\n",
+	     name, name_hash, name_index);
+
+	ret = ocfs2_xattr_get_rec(inode, name_hash, &p_blkno, &first_hash,
+				  &num_clusters, el);
+	if (ret) {
+		mlog_errno(ret);
+		goto out;
+	}
+
+	BUG_ON(p_blkno == 0 || num_clusters == 0 || first_hash > name_hash);
+
+	mlog(0, "find xattr extent rec %u clusters from %llu, the first hash "
+	     "in the rec is %u\n", num_clusters, p_blkno, first_hash);
+
+	ret = ocfs2_xattr_bucket_find(inode, name_index, name, name_hash,
+				      p_blkno, first_hash, num_clusters, xs);
+
+out:
+	return ret;
+}
+
+static int ocfs2_iterate_xattr_buckets(struct inode *inode,
+				       u64 blkno,
+				       u32 clusters,
+				       xattr_bucket_func *func,
+				       void *para)
+{
+	int i, j, ret = 0;
+	int blk_per_bucket = ocfs2_blocks_per_xattr_bucket(inode->i_sb);
+	u32 bpc = ocfs2_xattr_buckets_per_cluster(OCFS2_SB(inode->i_sb));
+	u32 num_buckets = clusters * bpc;
+	struct ocfs2_xattr_bucket bucket;
+
+	memset(&bucket, 0, sizeof(bucket));
+
+	mlog(0, "iterating xattr buckets in %u clusters starting from %llu\n",
+	     clusters, blkno);
+
+	for (i = 0; i < num_buckets; i++, blkno += blk_per_bucket) {
+		ret = ocfs2_read_blocks(inode, blkno, blk_per_bucket,
+					bucket.bhs, OCFS2_BH_CACHED);
+		if (ret) {
+			mlog_errno(ret);
+			goto out;
+		}
+
+		bucket.xh = (struct ocfs2_xattr_header *)bucket.bhs[0]->b_data;
+		/*
+		 * The real bucket num in this series of blocks is stored
+		 * in the 1st bucket.
+		 */
+		if (i == 0)
+			num_buckets = le16_to_cpu(bucket.xh->xh_num_buckets);
+
+		mlog(0, "iterating xattr bucket %llu, first hash %u\n", blkno,
+		     le32_to_cpu(bucket.xh->xh_entries[0].xe_name_hash));
+		if (func) {
+			ret = func(inode, &bucket, para);
+			if (ret) {
+				mlog_errno(ret);
+				break;
+			}
+		}
+
+		for (j = 0; j < blk_per_bucket; j++)
+			brelse(bucket.bhs[j]);
+		memset(&bucket, 0, sizeof(bucket));
+	}
+
+out:
+	for (j = 0; j < blk_per_bucket; j++)
+		brelse(bucket.bhs[j]);
+
+	return ret;
+}
+
+struct ocfs2_xattr_tree_list {
+	char *buffer;
+	size_t buffer_size;
+	size_t result;
+};
+
+static int ocfs2_xattr_bucket_get_name_value(struct inode *inode,
+					     struct ocfs2_xattr_header *xh,
+					     int index,
+					     int *block_off,
+					     int *new_offset)
+{
+	u16 name_offset;
+
+	if (index < 0 || index >= le16_to_cpu(xh->xh_count))
+		return -EINVAL;
+
+	name_offset = le16_to_cpu(xh->xh_entries[index].xe_name_offset);
+
+	*block_off = name_offset >> inode->i_sb->s_blocksize_bits;
+	*new_offset = name_offset % inode->i_sb->s_blocksize;
+
+	return 0;
+}
+
+static int ocfs2_list_xattr_bucket(struct inode *inode,
+				   struct ocfs2_xattr_bucket *bucket,
+				   void *para)
+{
+	int ret = 0, type;
+	struct ocfs2_xattr_tree_list *xl = (struct ocfs2_xattr_tree_list *)para;
+	int i, block_off, new_offset;
+	const char *prefix, *name;
+
+	for (i = 0 ; i < le16_to_cpu(bucket->xh->xh_count); i++) {
+		struct ocfs2_xattr_entry *entry = &bucket->xh->xh_entries[i];
+		type = ocfs2_xattr_get_type(entry);
+		prefix = ocfs2_xattr_prefix(type);
+
+		if (prefix) {
+			ret = ocfs2_xattr_bucket_get_name_value(inode,
+								bucket->xh,
+								i,
+								&block_off,
+								&new_offset);
+			if (ret)
+				break;
+
+			name = (const char *)bucket->bhs[block_off]->b_data +
+				new_offset;
+			ret = ocfs2_xattr_list_entry(xl->buffer,
+						     xl->buffer_size,
+						     &xl->result,
+						     prefix, name,
+						     entry->xe_name_len);
+			if (ret)
+				break;
+		}
+	}
+
+	return ret;
+}
+
+static int ocfs2_xattr_tree_list_index_block(struct inode *inode,
+					     struct ocfs2_xattr_tree_root *xt,
+					     char *buffer,
+					     size_t buffer_size)
+{
+	struct ocfs2_extent_list *el = &xt->xt_list;
+	int ret = 0;
+	u32 name_hash = UINT_MAX, e_cpos = 0, num_clusters = 0;
+	u64 p_blkno = 0;
+	struct ocfs2_xattr_tree_list xl = {
+		.buffer = buffer,
+		.buffer_size = buffer_size,
+		.result = 0,
+	};
+
+	if (le16_to_cpu(el->l_next_free_rec) == 0)
+		return 0;
+
+	while (name_hash > 0) {
+		ret = ocfs2_xattr_get_rec(inode, name_hash, &p_blkno,
+					  &e_cpos, &num_clusters, el);
+		if (ret) {
+			mlog_errno(ret);
+			goto out;
+		}
+
+		ret = ocfs2_iterate_xattr_buckets(inode, p_blkno, num_clusters,
+						  ocfs2_list_xattr_bucket,
+						  &xl);
+		if (ret) {
+			mlog_errno(ret);
+			goto out;
+		}
+
+		if (e_cpos == 0)
+			break;
+
+		name_hash = e_cpos - 1;
+	}
+
+	ret = xl.result;
+out:
+	return ret;
+}
+
+static int cmp_xe(const void *a, const void *b)
+{
+	const struct ocfs2_xattr_entry *l = a, *r = b;
+	u32 l_hash = le32_to_cpu(l->xe_name_hash);
+	u32 r_hash = le32_to_cpu(r->xe_name_hash);
+
+	if (l_hash > r_hash)
+		return 1;
+	if (l_hash < r_hash)
+		return -1;
+	return 0;
+}
+
+static void swap_xe(void *a, void *b, int size)
+{
+	struct ocfs2_xattr_entry *l = a, *r = b, tmp;
+
+	tmp = *l;
+	memcpy(l, r, sizeof(struct ocfs2_xattr_entry));
+	memcpy(r, &tmp, sizeof(struct ocfs2_xattr_entry));
+}
+
+/*
+ * When the ocfs2_xattr_block is filled up, new bucket will be created
+ * and all the xattr entries will be moved to the new bucket.
+ * Note: we need to sort the entries since they are not saved in order
+ * in the ocfs2_xattr_block.
+ */
+static void ocfs2_cp_xattr_block_to_bucket(struct inode *inode,
+					   struct buffer_head *xb_bh,
+					   struct buffer_head *xh_bh,
+					   struct buffer_head *data_bh)
+{
+	int i, blocksize = inode->i_sb->s_blocksize;
+	u16 offset, size, off_change;
+	struct ocfs2_xattr_entry *xe;
+	struct ocfs2_xattr_block *xb =
+				(struct ocfs2_xattr_block *)xb_bh->b_data;
+	struct ocfs2_xattr_header *xb_xh = &xb->xb_attrs.xb_header;
+	struct ocfs2_xattr_header *xh =
+				(struct ocfs2_xattr_header *)xh_bh->b_data;
+	u16 count = le16_to_cpu(xb_xh->xh_count);
+	char *target = xh_bh->b_data, *src = xb_bh->b_data;
+
+	mlog(0, "cp xattr from block %llu to bucket %llu\n",
+	     (unsigned long long)xb_bh->b_blocknr,
+	     (unsigned long long)xh_bh->b_blocknr);
+
+	memset(xh_bh->b_data, 0, blocksize);
+	if (data_bh)
+		memset(data_bh->b_data, 0, blocksize);
+	/*
+	 * Since the xe_name_offset is based on ocfs2_xattr_header,
+	 * there is a offset change corresponding to the change of
+	 * ocfs2_xattr_header's position.
+	 */
+	off_change = offsetof(struct ocfs2_xattr_block, xb_attrs.xb_header);
+	xe = &xb_xh->xh_entries[count - 1];
+	offset = le16_to_cpu(xe->xe_name_offset) + off_change;
+	size = blocksize - offset;
+
+	/* copy all the names and values. */
+	if (data_bh)
+		target = data_bh->b_data;
+	memcpy(target + offset, src + offset, size);
+
+	/* Init new header now. */
+	xh->xh_count = xb_xh->xh_count;
+	xh->xh_num_buckets = cpu_to_le16(1);
+	xh->xh_name_value_len = cpu_to_le16(size);
+	xh->xh_free_start = cpu_to_le16(OCFS2_XATTR_BUCKET_SIZE - size);
+
+	/* copy all the entries. */
+	target = xh_bh->b_data;
+	offset = offsetof(struct ocfs2_xattr_header, xh_entries);
+	size = count * sizeof(struct ocfs2_xattr_entry);
+	memcpy(target + offset, (char *)xb_xh + offset, size);
+
+	/* Change the xe offset for all the xe because of the move. */
+	off_change = OCFS2_XATTR_BUCKET_SIZE - blocksize +
+		 offsetof(struct ocfs2_xattr_block, xb_attrs.xb_header);
+	for (i = 0; i < count; i++)
+		le16_add_cpu(&xh->xh_entries[i].xe_name_offset, off_change);
+
+	mlog(0, "copy entry: start = %u, size = %u, offset_change = %u\n",
+	     offset, size, off_change);
+
+	sort(target + offset, count, sizeof(struct ocfs2_xattr_entry),
+	     cmp_xe, swap_xe);
+}
+
+/*
+ * After we move xattr from block to index btree, we have to
+ * update ocfs2_xattr_search to the new xe and base.
+ *
+ * When the entry is in xattr block, xattr_bh indicates the storage place.
+ * While if the entry is in index b-tree, "bucket" indicates the
+ * real place of the xattr.
+ */
+static int ocfs2_xattr_update_xattr_search(struct inode *inode,
+					   struct ocfs2_xattr_search *xs,
+					   struct buffer_head *old_bh,
+					   struct buffer_head *new_bh)
+{
+	int ret = 0;
+	char *buf = old_bh->b_data;
+	struct ocfs2_xattr_block *old_xb = (struct ocfs2_xattr_block *)buf;
+	struct ocfs2_xattr_header *old_xh = &old_xb->xb_attrs.xb_header;
+	int i, blocksize = inode->i_sb->s_blocksize;
+	u16 blk_per_bucket = ocfs2_blocks_per_xattr_bucket(inode->i_sb);
+
+	xs->bucket.bhs[0] = new_bh;
+	get_bh(new_bh);
+	xs->bucket.xh = (struct ocfs2_xattr_header *)xs->bucket.bhs[0]->b_data;
+	xs->header = xs->bucket.xh;
+
+	xs->base = new_bh->b_data;
+	xs->end = xs->base + inode->i_sb->s_blocksize;
+
+	if (!xs->not_found) {
+		if (OCFS2_XATTR_BUCKET_SIZE != blocksize) {
+			ret = ocfs2_read_blocks(inode,
+					xs->bucket.bhs[0]->b_blocknr + 1,
+					blk_per_bucket - 1, &xs->bucket.bhs[1],
+					OCFS2_BH_CACHED);
+			if (ret) {
+				mlog_errno(ret);
+				return ret;
+			}
+
+			i = xs->here - old_xh->xh_entries;
+			xs->here = &xs->header->xh_entries[i];
+		}
+	}
+
+	return ret;
+}
+
+static int ocfs2_xattr_create_index_block(struct inode *inode,
+					  struct ocfs2_xattr_search *xs)
+{
+	int ret, credits = OCFS2_SUBALLOC_ALLOC;
+	u32 bit_off, len;
+	u64 blkno;
+	handle_t *handle;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+	struct ocfs2_inode_info *oi = OCFS2_I(inode);
+	struct ocfs2_alloc_context *data_ac;
+	struct buffer_head *xh_bh = NULL, *data_bh = NULL;
+	struct buffer_head *xb_bh = xs->xattr_bh;
+	struct ocfs2_xattr_block *xb =
+			(struct ocfs2_xattr_block *)xb_bh->b_data;
+	struct ocfs2_xattr_tree_root *xr;
+	u16 xb_flags = le16_to_cpu(xb->xb_flags);
+	u16 bpb = ocfs2_blocks_per_xattr_bucket(inode->i_sb);
+
+	mlog(0, "create xattr index block for %llu\n",
+	     (unsigned long long)xb_bh->b_blocknr);
+
+	BUG_ON(xb_flags & OCFS2_XATTR_INDEXED);
+
+	ret = ocfs2_reserve_clusters(osb, 1, &data_ac);
+	if (ret) {
+		mlog_errno(ret);
+		goto out;
+	}
+
+	/*
+	 * XXX:
+	 * We can use this lock for now, and maybe move to a dedicated mutex
+	 * if performance becomes a problem later.
+	 */
+	down_write(&oi->ip_alloc_sem);
+
+	/*
+	 * 3 more credits, one for xattr block update, one for the 1st block
+	 * of the new xattr bucket and one for the value/data.
+	 */
+	credits += 3;
+	handle = ocfs2_start_trans(osb, credits);
+	if (IS_ERR(handle)) {
+		ret = PTR_ERR(handle);
+		mlog_errno(ret);
+		goto out_sem;
+	}
+
+	ret = ocfs2_journal_access(handle, inode, xb_bh,
+				   OCFS2_JOURNAL_ACCESS_WRITE);
+	if (ret) {
+		mlog_errno(ret);
+		goto out_commit;
+	}
+
+	ret = ocfs2_claim_clusters(osb, handle, data_ac, 1, &bit_off, &len);
+	if (ret) {
+		mlog_errno(ret);
+		goto out_commit;
+	}
+
+	/*
+	 * The bucket may spread in many blocks, and
+	 * we will only touch the 1st block and the last block
+	 * in the whole bucket(one for entry and one for data).
+	 */
+	blkno = ocfs2_clusters_to_blocks(inode->i_sb, bit_off);
+
+	mlog(0, "allocate 1 cluster from %llu to xattr block\n", blkno);
+
+	xh_bh = sb_getblk(inode->i_sb, blkno);
+	if (!xh_bh) {
+		ret = -EIO;
+		mlog_errno(ret);
+		goto out_commit;
+	}
+
+	ocfs2_set_new_buffer_uptodate(inode, xh_bh);
+
+	ret = ocfs2_journal_access(handle, inode, xh_bh,
+				   OCFS2_JOURNAL_ACCESS_CREATE);
+	if (ret) {
+		mlog_errno(ret);
+		goto out_commit;
+	}
+
+	if (bpb > 1) {
+		data_bh = sb_getblk(inode->i_sb, blkno + bpb - 1);
+		if (!data_bh) {
+			ret = -EIO;
+			mlog_errno(ret);
+			goto out_commit;
+		}
+
+		ocfs2_set_new_buffer_uptodate(inode, data_bh);
+
+		ret = ocfs2_journal_access(handle, inode, data_bh,
+					   OCFS2_JOURNAL_ACCESS_CREATE);
+		if (ret) {
+			mlog_errno(ret);
+			goto out_commit;
+		}
+	}
+
+	ocfs2_cp_xattr_block_to_bucket(inode, xb_bh, xh_bh, data_bh);
+
+	ocfs2_journal_dirty(handle, xh_bh);
+	if (data_bh)
+		ocfs2_journal_dirty(handle, data_bh);
+
+	ocfs2_xattr_update_xattr_search(inode, xs, xb_bh, xh_bh);
+
+	/* Change from ocfs2_xattr_header to ocfs2_xattr_tree_root */
+	memset(&xb->xb_attrs, 0, inode->i_sb->s_blocksize -
+	       offsetof(struct ocfs2_xattr_block, xb_attrs));
+
+	xr = &xb->xb_attrs.xb_root;
+	xr->xt_clusters = cpu_to_le32(1);
+	xr->xt_last_eb_blk = 0;
+	xr->xt_list.l_tree_depth = 0;
+	xr->xt_list.l_count = cpu_to_le16(ocfs2_xattr_recs_per_xb(inode->i_sb));
+	xr->xt_list.l_next_free_rec = cpu_to_le16(1);
+
+	xr->xt_list.l_recs[0].e_cpos = 0;
+	xr->xt_list.l_recs[0].e_blkno = cpu_to_le64(blkno);
+	xr->xt_list.l_recs[0].e_leaf_clusters = cpu_to_le16(1);
+
+	xb->xb_flags = cpu_to_le16(xb_flags | OCFS2_XATTR_INDEXED);
+
+	ret = ocfs2_journal_dirty(handle, xb_bh);
+	if (ret) {
+		mlog_errno(ret);
+		goto out_commit;
+	}
+
+out_commit:
+	ocfs2_commit_trans(osb, handle);
+
+out_sem:
+	up_write(&oi->ip_alloc_sem);
+
+out:
+	if (data_ac)
+		ocfs2_free_alloc_context(data_ac);
+
+	brelse(xh_bh);
+	brelse(data_bh);
+
+	return ret;
+}
+
+static int cmp_xe_offset(const void *a, const void *b)
+{
+	const struct ocfs2_xattr_entry *l = a, *r = b;
+	u32 l_name_offset = le16_to_cpu(l->xe_name_offset);
+	u32 r_name_offset = le16_to_cpu(r->xe_name_offset);
+
+	if (l_name_offset < r_name_offset)
+		return 1;
+	if (l_name_offset > r_name_offset)
+		return -1;
+	return 0;
+}
+
+/*
+ * defrag a xattr bucket if we find that the bucket has some
+ * holes beteen name/value pairs.
+ * We will move all the name/value pairs to the end of the bucket
+ * so that we can spare some space for insertion.
+ */
+static int ocfs2_defrag_xattr_bucket(struct inode *inode,
+				     struct ocfs2_xattr_bucket *bucket)
+{
+	int ret, i;
+	size_t end, offset, len, value_len;
+	struct ocfs2_xattr_header *xh;
+	char *entries, *buf, *bucket_buf = NULL;
+	u64 blkno = bucket->bhs[0]->b_blocknr;
+	u16 blk_per_bucket = ocfs2_blocks_per_xattr_bucket(inode->i_sb);
+	u16 xh_free_start;
+	size_t blocksize = inode->i_sb->s_blocksize;
+	handle_t *handle;
+	struct buffer_head **bhs;
+	struct ocfs2_xattr_entry *xe;
+
+	bhs = kzalloc(sizeof(struct buffer_head *) * blk_per_bucket,
+			GFP_NOFS);
+	if (!bhs)
+		return -ENOMEM;
+
+	ret = ocfs2_read_blocks(inode, blkno, blk_per_bucket, bhs,
+				OCFS2_BH_CACHED);
+	if (ret)
+		goto out;
+
+	/*
+	 * In order to make the operation more efficient and generic,
+	 * we copy all the blocks into a contiguous memory and do the
+	 * defragment there, so if anything is error, we will not touch
+	 * the real block.
+	 */
+	bucket_buf = kmalloc(OCFS2_XATTR_BUCKET_SIZE, GFP_NOFS);
+	if (!bucket_buf) {
+		ret = -EIO;
+		goto out;
+	}
+
+	buf = bucket_buf;
+	for (i = 0; i < blk_per_bucket; i++, buf += blocksize)
+		memcpy(buf, bhs[i]->b_data, blocksize);
+
+	handle = ocfs2_start_trans((OCFS2_SB(inode->i_sb)), blk_per_bucket);
+	if (IS_ERR(handle)) {
+		ret = PTR_ERR(handle);
+		handle = NULL;
+		mlog_errno(ret);
+		goto out;
+	}
+
+	for (i = 0; i < blk_per_bucket; i++) {
+		ret = ocfs2_journal_access(handle, inode, bhs[i],
+					   OCFS2_JOURNAL_ACCESS_WRITE);
+		if (ret < 0) {
+			mlog_errno(ret);
+			goto commit;
+		}
+	}
+
+	xh = (struct ocfs2_xattr_header *)bucket_buf;
+	entries = (char *)xh->xh_entries;
+	xh_free_start = le16_to_cpu(xh->xh_free_start);
+
+	mlog(0, "adjust xattr bucket in %llu, count = %u, "
+	     "xh_free_start = %u, xh_name_value_len = %u.\n",
+	     blkno, le16_to_cpu(xh->xh_count), xh_free_start,
+	     le16_to_cpu(xh->xh_name_value_len));
+
+	/*
+	 * sort all the entries by their offset.
+	 * the largest will be the first, so that we can
+	 * move them to the end one by one.
+	 */
+	sort(entries, le16_to_cpu(xh->xh_count),
+	     sizeof(struct ocfs2_xattr_entry),
+	     cmp_xe_offset, swap_xe);
+
+	/* Move all name/values to the end of the bucket. */
+	xe = xh->xh_entries;
+	end = OCFS2_XATTR_BUCKET_SIZE;
+	for (i = 0; i < le16_to_cpu(xh->xh_count); i++, xe++) {
+		offset = le16_to_cpu(xe->xe_name_offset);
+		if (ocfs2_xattr_is_local(xe))
+			value_len = OCFS2_XATTR_SIZE(
+					le64_to_cpu(xe->xe_value_size));
+		else
+			value_len = OCFS2_XATTR_ROOT_SIZE;
+		len = OCFS2_XATTR_SIZE(xe->xe_name_len) + value_len;
+
+		/*
+		 * We must make sure that the name/value pair
+		 * exist in the same block. So adjust end to
+		 * the previous block end if needed.
+		 */
+		if (((end - len) / blocksize !=
+			(end - 1) / blocksize))
+			end = end - end % blocksize;
+
+		if (end > offset + len) {
+			memmove(bucket_buf + end - len,
+				bucket_buf + offset, len);
+			xe->xe_name_offset = cpu_to_le16(end - len);
+		}
+
+		mlog_bug_on_msg(end < offset + len, "Defrag check failed for "
+				"bucket %llu\n", (unsigned long long)blkno);
+
+		end -= len;
+	}
+
+	mlog_bug_on_msg(xh_free_start > end, "Defrag check failed for "
+			"bucket %llu\n", (unsigned long long)blkno);
+
+	if (xh_free_start == end)
+		goto commit;
+
+	memset(bucket_buf + xh_free_start, 0, end - xh_free_start);
+	xh->xh_free_start = cpu_to_le16(end);
+
+	/* sort the entries by their name_hash. */
+	sort(entries, le16_to_cpu(xh->xh_count),
+	     sizeof(struct ocfs2_xattr_entry),
+	     cmp_xe, swap_xe);
+
+	buf = bucket_buf;
+	for (i = 0; i < blk_per_bucket; i++, buf += blocksize) {
+		memcpy(bhs[i]->b_data, buf, blocksize);
+		ocfs2_journal_dirty(handle, bhs[i]);
+	}
+
+commit:
+	ocfs2_commit_trans(OCFS2_SB(inode->i_sb), handle);
+out:
+
+	if (bhs) {
+		for (i = 0; i < blk_per_bucket; i++)
+			brelse(bhs[i]);
+	}
+	kfree(bhs);
+
+	kfree(bucket_buf);
+	return ret;
+}
+
+/*
+ * Move half nums of the xattr bucket in the previous cluster to this new
+ * cluster. We only touch the last cluster of the previous extend record.
+ *
+ * first_bh is the first buffer_head of a series of bucket in the same
+ * extent rec and header_bh is the header of one bucket in this cluster.
+ * They will be updated if we move the data header_bh contains to the new
+ * cluster. first_hash will be set as the 1st xe's name_hash of the new cluster.
+ */
+static int ocfs2_mv_xattr_bucket_cross_cluster(struct inode *inode,
+					       handle_t *handle,
+					       struct buffer_head **first_bh,
+					       struct buffer_head **header_bh,
+					       u64 new_blkno,
+					       u64 prev_blkno,
+					       u32 num_clusters,
+					       u32 *first_hash)
+{
+	int i, ret, credits;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+	int bpc = ocfs2_clusters_to_blocks(inode->i_sb, 1);
+	int num_buckets = ocfs2_xattr_buckets_per_cluster(osb);
+	int blocksize = inode->i_sb->s_blocksize;
+	struct buffer_head *old_bh, *new_bh, *prev_bh, *new_first_bh = NULL;
+	struct ocfs2_xattr_header *new_xh;
+	struct ocfs2_xattr_header *xh =
+			(struct ocfs2_xattr_header *)((*first_bh)->b_data);
+
+	BUG_ON(le16_to_cpu(xh->xh_num_buckets) < num_buckets);
+	BUG_ON(OCFS2_XATTR_BUCKET_SIZE == osb->s_clustersize);
+
+	prev_bh = *first_bh;
+	get_bh(prev_bh);
+	xh = (struct ocfs2_xattr_header *)prev_bh->b_data;
+
+	prev_blkno += (num_clusters - 1) * bpc + bpc / 2;
+
+	mlog(0, "move half of xattrs in cluster %llu to %llu\n",
+	     prev_blkno, new_blkno);
+
+	/*
+	 * We need to update the 1st half of the new cluster and
+	 * 1 more for the update of the 1st bucket of the previous
+	 * extent record.
+	 */
+	credits = bpc / 2 + 1;
+	ret = ocfs2_extend_trans(handle, credits);
+	if (ret) {
+		mlog_errno(ret);
+		goto out;
+	}
+
+	ret = ocfs2_journal_access(handle, inode, prev_bh,
+				   OCFS2_JOURNAL_ACCESS_WRITE);
+	if (ret) {
+		mlog_errno(ret);
+		goto out;
+	}
+
+	for (i = 0; i < bpc / 2; i++, prev_blkno++, new_blkno++) {
+		old_bh = new_bh = NULL;
+		new_bh = sb_getblk(inode->i_sb, new_blkno);
+		if (!new_bh) {
+			ret = -EIO;
+			mlog_errno(ret);
+			goto out;
+		}
+
+		ocfs2_set_new_buffer_uptodate(inode, new_bh);
+
+		ret = ocfs2_journal_access(handle, inode, new_bh,
+					   OCFS2_JOURNAL_ACCESS_CREATE);
+		if (ret < 0) {
+			mlog_errno(ret);
+			brelse(new_bh);
+			goto out;
+		}
+
+		ret = ocfs2_read_block(inode, prev_blkno, &old_bh);
+		if (ret < 0) {
+			mlog_errno(ret);
+			brelse(new_bh);
+			goto out;
+		}
+
+		memcpy(new_bh->b_data, old_bh->b_data, blocksize);
+
+		if (i == 0) {
+			new_xh = (struct ocfs2_xattr_header *)new_bh->b_data;
+			new_xh->xh_num_buckets = cpu_to_le16(num_buckets / 2);
+
+			if (first_hash)
+				*first_hash = le32_to_cpu(
+					new_xh->xh_entries[0].xe_name_hash);
+			new_first_bh = new_bh;
+			get_bh(new_first_bh);
+		}
+
+		ocfs2_journal_dirty(handle, new_bh);
+
+		if (*header_bh == old_bh) {
+			brelse(*header_bh);
+			*header_bh = new_bh;
+			get_bh(*header_bh);
+
+			brelse(*first_bh);
+			*first_bh = new_first_bh;
+			get_bh(*first_bh);
+		}
+		brelse(new_bh);
+		brelse(old_bh);
+	}
+
+	le16_add_cpu(&xh->xh_num_buckets, -(num_buckets / 2));
+
+	ocfs2_journal_dirty(handle, prev_bh);
+out:
+	brelse(prev_bh);
+	brelse(new_first_bh);
+	return ret;
+}
+
+static int ocfs2_read_xattr_bucket(struct inode *inode,
+				   u64 blkno,
+				   struct buffer_head **bhs,
+				   int new)
+{
+	int ret = 0;
+	u16 i, blk_per_bucket = ocfs2_blocks_per_xattr_bucket(inode->i_sb);
+
+	if (!new)
+		return ocfs2_read_blocks(inode, blkno,
+					 blk_per_bucket, bhs,
+					 OCFS2_BH_CACHED);
+
+	for (i = 0; i < blk_per_bucket; i++) {
+		bhs[i] = sb_getblk(inode->i_sb, blkno + i);
+		if (bhs[i] == NULL) {
+			ret = -EIO;
+			mlog_errno(ret);
+			break;
+		}
+		ocfs2_set_new_buffer_uptodate(inode, bhs[i]);
+	}
+
+	return ret;
+}
+
+/*
+ * Move half num of the xattrs in old bucket(blk) to new bucket(new_blk).
+ * first_hash will record the 1st hash of the new bucket.
+ */
+static int ocfs2_half_xattr_bucket(struct inode *inode,
+				   handle_t *handle,
+				   u64 blk,
+				   u64 new_blk,
+				   u32 *first_hash,
+				   int new_bucket_head)
+{
+	int ret, i;
+	u16 count, start, len, name_value_len, xe_len, name_offset;
+	u16 blk_per_bucket = ocfs2_blocks_per_xattr_bucket(inode->i_sb);
+	struct buffer_head **s_bhs, **t_bhs = NULL;
+	struct ocfs2_xattr_header *xh;
+	struct ocfs2_xattr_entry *xe;
+	int blocksize = inode->i_sb->s_blocksize;
+
+	mlog(0, "move half of xattrs from bucket %llu to %llu\n",
+	     blk, new_blk);
+
+	s_bhs = kcalloc(blk_per_bucket, sizeof(struct buffer_head *), GFP_NOFS);
+	if (!s_bhs)
+		return -ENOMEM;
+
+	ret = ocfs2_read_xattr_bucket(inode, blk, s_bhs, 0);
+	if (ret) {
+		mlog_errno(ret);
+		goto out;
+	}
+
+	ret = ocfs2_journal_access(handle, inode, s_bhs[0],
+				   OCFS2_JOURNAL_ACCESS_WRITE);
+	if (ret) {
+		mlog_errno(ret);
+		goto out;
+	}
+
+	t_bhs = kcalloc(blk_per_bucket, sizeof(struct buffer_head *), GFP_NOFS);
+	if (!t_bhs) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	ret = ocfs2_read_xattr_bucket(inode, new_blk, t_bhs, new_bucket_head);
+	if (ret) {
+		mlog_errno(ret);
+		goto out;
+	}
+
+	for (i = 0; i < blk_per_bucket; i++) {
+		ret = ocfs2_journal_access(handle, inode, t_bhs[i],
+					   OCFS2_JOURNAL_ACCESS_CREATE);
+		if (ret) {
+			mlog_errno(ret);
+			goto out;
+		}
+	}
+
+	/* copy the whole bucket to the new first. */
+	for (i = 0; i < blk_per_bucket; i++)
+		memcpy(t_bhs[i]->b_data, s_bhs[i]->b_data, blocksize);
+
+	/* update the new bucket. */
+	xh = (struct ocfs2_xattr_header *)t_bhs[0]->b_data;
+	count = le16_to_cpu(xh->xh_count);
+	start = count / 2;
+
+	/*
+	 * Calculate the total name/value len and xh_free_start for
+	 * the old bucket first.
+	 */
+	name_offset = OCFS2_XATTR_BUCKET_SIZE;
+	name_value_len = 0;
+	for (i = 0; i < start; i++) {
+		xe = &xh->xh_entries[i];
+		xe_len = OCFS2_XATTR_SIZE(xe->xe_name_len);
+		if (ocfs2_xattr_is_local(xe))
+			xe_len +=
+			   OCFS2_XATTR_SIZE(le64_to_cpu(xe->xe_value_size));
+		else
+			xe_len += OCFS2_XATTR_ROOT_SIZE;
+		name_value_len += xe_len;
+		if (le16_to_cpu(xe->xe_name_offset) < name_offset)
+			name_offset = le16_to_cpu(xe->xe_name_offset);
+	}
+
+	/*
+	 * Now begin the modification to the new bucket.
+	 *
+	 * In the new bucket, We just move the xattr entry to the beginning
+	 * and don't touch the name/value. So there will be some holes in the
+	 * bucket, and they will be removed when ocfs2_defrag_xattr_bucket is
+	 * called.
+	 */
+	xe = &xh->xh_entries[start];
+	len = sizeof(struct ocfs2_xattr_entry) * (count - start);
+	mlog(0, "mv xattr entry len %d from %d to %d\n", len,
+	     (int)((char *)xe - (char *)xh),
+	     (int)((char *)xh->xh_entries - (char *)xh));
+	memmove((char *)xh->xh_entries, (char *)xe, len);
+	xe = &xh->xh_entries[count - start];
+	len = sizeof(struct ocfs2_xattr_entry) * start;
+	memset((char *)xe, 0, len);
+
+	le16_add_cpu(&xh->xh_count, -start);
+	le16_add_cpu(&xh->xh_name_value_len, -name_value_len);
+
+	/* Calculate xh_free_start for the new bucket. */
+	xh->xh_free_start = cpu_to_le16(OCFS2_XATTR_BUCKET_SIZE);
+	for (i = 0; i < le16_to_cpu(xh->xh_count); i++) {
+		xe = &xh->xh_entries[i];
+		xe_len = OCFS2_XATTR_SIZE(xe->xe_name_len);
+		if (ocfs2_xattr_is_local(xe))
+			xe_len +=
+			   OCFS2_XATTR_SIZE(le64_to_cpu(xe->xe_value_size));
+		else
+			xe_len += OCFS2_XATTR_ROOT_SIZE;
+		if (le16_to_cpu(xe->xe_name_offset) <
+		    le16_to_cpu(xh->xh_free_start))
+			xh->xh_free_start = xe->xe_name_offset;
+	}
+
+	/* set xh->xh_num_buckets for the new xh. */
+	if (new_bucket_head)
+		xh->xh_num_buckets = cpu_to_le16(1);
+	else
+		xh->xh_num_buckets = 0;
+
+	for (i = 0; i < blk_per_bucket; i++) {
+		ocfs2_journal_dirty(handle, t_bhs[i]);
+		if (ret)
+			mlog_errno(ret);
+	}
+
+	/* store the first_hash of the new bucket. */
+	if (first_hash)
+		*first_hash = le32_to_cpu(xh->xh_entries[0].xe_name_hash);
+
+	/*
+	 * Now only update the 1st block of the old bucket.
+	 * Please note that the entry has been sorted already above.
+	 */
+	xh = (struct ocfs2_xattr_header *)s_bhs[0]->b_data;
+	memset(&xh->xh_entries[start], 0,
+	       sizeof(struct ocfs2_xattr_entry) * (count - start));
+	xh->xh_count = cpu_to_le16(start);
+	xh->xh_free_start = cpu_to_le16(name_offset);
+	xh->xh_name_value_len = cpu_to_le16(name_value_len);
+
+	ocfs2_journal_dirty(handle, s_bhs[0]);
+	if (ret)
+		mlog_errno(ret);
+
+out:
+	if (s_bhs) {
+		for (i = 0; i < blk_per_bucket; i++)
+			brelse(s_bhs[i]);
+	}
+	kfree(s_bhs);
+
+	if (t_bhs) {
+		for (i = 0; i < blk_per_bucket; i++)
+			brelse(t_bhs[i]);
+	}
+	kfree(t_bhs);
+
+	return ret;
+}
+
+/*
+ * Copy xattr from one bucket to another bucket.
+ *
+ * The caller must make sure that the journal transaction
+ * has enough space for journaling.
+ */
+static int ocfs2_cp_xattr_bucket(struct inode *inode,
+				 handle_t *handle,
+				 u64 s_blkno,
+				 u64 t_blkno,
+				 int t_is_new)
+{
+	int ret, i;
+	int blk_per_bucket = ocfs2_blocks_per_xattr_bucket(inode->i_sb);
+	int blocksize = inode->i_sb->s_blocksize;
+	struct buffer_head **s_bhs, **t_bhs = NULL;
+
+	BUG_ON(s_blkno == t_blkno);
+
+	mlog(0, "cp bucket %llu to %llu, target is %d\n",
+	     s_blkno, t_blkno, t_is_new);
+
+	s_bhs = kzalloc(sizeof(struct buffer_head *) * blk_per_bucket,
+			GFP_NOFS);
+	if (!s_bhs)
+		return -ENOMEM;
+
+	ret = ocfs2_read_xattr_bucket(inode, s_blkno, s_bhs, 0);
+	if (ret)
+		goto out;
+
+	t_bhs = kzalloc(sizeof(struct buffer_head *) * blk_per_bucket,
+			GFP_NOFS);
+	if (!t_bhs) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	ret = ocfs2_read_xattr_bucket(inode, t_blkno, t_bhs, t_is_new);
+	if (ret)
+		goto out;
+
+	for (i = 0; i < blk_per_bucket; i++) {
+		ret = ocfs2_journal_access(handle, inode, t_bhs[i],
+					   OCFS2_JOURNAL_ACCESS_WRITE);
+		if (ret)
+			goto out;
+	}
+
+	for (i = 0; i < blk_per_bucket; i++) {
+		memcpy(t_bhs[i]->b_data, s_bhs[i]->b_data, blocksize);
+		ocfs2_journal_dirty(handle, t_bhs[i]);
+	}
+
+out:
+	if (s_bhs) {
+		for (i = 0; i < blk_per_bucket; i++)
+			brelse(s_bhs[i]);
+	}
+	kfree(s_bhs);
+
+	if (t_bhs) {
+		for (i = 0; i < blk_per_bucket; i++)
+			brelse(t_bhs[i]);
+	}
+	kfree(t_bhs);
+
+	return ret;
+}
+
+/*
+ * Copy one xattr cluster from src_blk to to_blk.
+ * The to_blk will become the first bucket header of the cluster, so its
+ * xh_num_buckets will be initialized as the bucket num in the cluster.
+ */
+static int ocfs2_cp_xattr_cluster(struct inode *inode,
+				  handle_t *handle,
+				  struct buffer_head *first_bh,
+				  u64 src_blk,
+				  u64 to_blk,
+				  u32 *first_hash)
+{
+	int i, ret, credits;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+	int bpc = ocfs2_clusters_to_blocks(inode->i_sb, 1);
+	int num_buckets = ocfs2_xattr_buckets_per_cluster(osb);
+	struct buffer_head *bh = NULL;
+	struct ocfs2_xattr_header *xh;
+	u64 to_blk_start = to_blk;
+
+	mlog(0, "cp xattrs from cluster %llu to %llu\n", src_blk, to_blk);
+
+	/*
+	 * We need to update the new cluster and 1 more for the update of
+	 * the 1st bucket of the previous extent rec.
+	 */
+	credits = bpc + 1;
+	ret = ocfs2_extend_trans(handle, credits);
+	if (ret) {
+		mlog_errno(ret);
+		goto out;
+	}
+
+	ret = ocfs2_journal_access(handle, inode, first_bh,
+				   OCFS2_JOURNAL_ACCESS_WRITE);
+	if (ret) {
+		mlog_errno(ret);
+		goto out;
+	}
+
+	for (i = 0; i < num_buckets; i++) {
+		ret = ocfs2_cp_xattr_bucket(inode, handle,
+					    src_blk, to_blk, 1);
+		if (ret) {
+			mlog_errno(ret);
+			goto out;
+		}
+
+		src_blk += ocfs2_blocks_per_xattr_bucket(inode->i_sb);
+		to_blk += ocfs2_blocks_per_xattr_bucket(inode->i_sb);
+	}
+
+	/* update the old bucket header. */
+	xh = (struct ocfs2_xattr_header *)first_bh->b_data;
+	le16_add_cpu(&xh->xh_num_buckets, -num_buckets);
+
+	ocfs2_journal_dirty(handle, first_bh);
+
+	/* update the new bucket header. */
+	ret = ocfs2_read_block(inode, to_blk_start, &bh);
+	if (ret < 0) {
+		mlog_errno(ret);
+		goto out;
+	}
+
+	ret = ocfs2_journal_access(handle, inode, bh,
+				   OCFS2_JOURNAL_ACCESS_WRITE);
+	if (ret) {
+		mlog_errno(ret);
+		goto out;
+	}
+
+	xh = (struct ocfs2_xattr_header *)bh->b_data;
+	xh->xh_num_buckets = cpu_to_le16(num_buckets);
+
+	ocfs2_journal_dirty(handle, bh);
+
+	if (first_hash)
+		*first_hash = le32_to_cpu(xh->xh_entries[0].xe_name_hash);
+out:
+	brelse(bh);
+	return ret;
+}
+
+/*
+ * Move half of the xattrs in this cluster to the new cluster.
+ * This function should only be called when bucket size == cluster size.
+ * Otherwise ocfs2_mv_xattr_bucket_cross_cluster should be used instead.
+ */
+static int ocfs2_half_xattr_cluster(struct inode *inode,
+				    handle_t *handle,
+				    u64 prev_blk,
+				    u64 new_blk,
+				    u32 *first_hash)
+{
+	u16 blk_per_bucket = ocfs2_blocks_per_xattr_bucket(inode->i_sb);
+	int ret, credits = 2 * blk_per_bucket;
+
+	BUG_ON(OCFS2_XATTR_BUCKET_SIZE < OCFS2_SB(inode->i_sb)->s_clustersize);
+
+	ret = ocfs2_extend_trans(handle, credits);
+	if (ret) {
+		mlog_errno(ret);
+		return ret;
+	}
+
+	/* Move half of the xattr in start_blk to the next bucket. */
+	return  ocfs2_half_xattr_bucket(inode, handle, prev_blk,
+					new_blk, first_hash, 1);
+}
+
+/*
+ * Move some xattrs from the old cluster to the new one since they are not
+ * contiguous in ocfs2 xattr tree.
+ *
+ * new_blk starts a new separate cluster, and we will move some xattrs from
+ * prev_blk to it. v_start will be set as the first name hash value in this
+ * new cluster so that it can be used as e_cpos during tree insertion and
+ * don't collide with our original b-tree operations. first_bh and header_bh
+ * will also be updated since they will be used in ocfs2_extend_xattr_bucket
+ * to extend the insert bucket.
+ *
+ * The problem is how much xattr should we move to the new one and when should
+ * we update first_bh and header_bh?
+ * 1. If cluster size > bucket size, that means the previous cluster has more
+ *    than 1 bucket, so just move half nums of bucket into the new cluster and
+ *    update the first_bh and header_bh if the insert bucket has been moved
+ *    to the new cluster.
+ * 2. If cluster_size == bucket_size:
+ *    a) If the previous extent rec has more than one cluster and the insert
+ *       place isn't in the last cluster, copy the entire last cluster to the
+ *       new one. This time, we don't need to upate the first_bh and header_bh
+ *       since they will not be moved into the new cluster.
+ *    b) Otherwise, move the bottom half of the xattrs in the last cluster into
+ *       the new one. And we set the extend flag to zero if the insert place is
+ *       moved into the new allocated cluster since no extend is needed.
+ */
+static int ocfs2_adjust_xattr_cross_cluster(struct inode *inode,
+					    handle_t *handle,
+					    struct buffer_head **first_bh,
+					    struct buffer_head **header_bh,
+					    u64 new_blk,
+					    u64 prev_blk,
+					    u32 prev_clusters,
+					    u32 *v_start,
+					    int *extend)
+{
+	int ret = 0;
+	int bpc = ocfs2_clusters_to_blocks(inode->i_sb, 1);
+
+	mlog(0, "adjust xattrs from cluster %llu len %u to %llu\n",
+	     prev_blk, prev_clusters, new_blk);
+
+	if (ocfs2_xattr_buckets_per_cluster(OCFS2_SB(inode->i_sb)) > 1)
+		ret = ocfs2_mv_xattr_bucket_cross_cluster(inode,
+							  handle,
+							  first_bh,
+							  header_bh,
+							  new_blk,
+							  prev_blk,
+							  prev_clusters,
+							  v_start);
+	else {
+		u64 last_blk = prev_blk + bpc * (prev_clusters - 1);
+
+		if (prev_clusters > 1 && (*header_bh)->b_blocknr != last_blk)
+			ret = ocfs2_cp_xattr_cluster(inode, handle, *first_bh,
+						     last_blk, new_blk,
+						     v_start);
+		else {
+			ret = ocfs2_half_xattr_cluster(inode, handle,
+						       last_blk, new_blk,
+						       v_start);
+
+			if ((*header_bh)->b_blocknr == last_blk && extend)
+				*extend = 0;
+		}
+	}
+
+	return ret;
+}
+
+/*
+ * Add a new cluster for xattr storage.
+ *
+ * If the new cluster is contiguous with the previous one, it will be
+ * appended to the same extent record, and num_clusters will be updated.
+ * If not, we will insert a new extent for it and move some xattrs in
+ * the last cluster into the new allocated one.
+ * We also need to limit the maximum size of a btree leaf, otherwise we'll
+ * lose the benefits of hashing because we'll have to search large leaves.
+ * So now the maximum size is OCFS2_MAX_XATTR_TREE_LEAF_SIZE(or clustersize,
+ * if it's bigger).
+ *
+ * first_bh is the first block of the previous extent rec and header_bh
+ * indicates the bucket we will insert the new xattrs. They will be updated
+ * when the header_bh is moved into the new cluster.
+ */
+static int ocfs2_add_new_xattr_cluster(struct inode *inode,
+				       struct buffer_head *root_bh,
+				       struct buffer_head **first_bh,
+				       struct buffer_head **header_bh,
+				       u32 *num_clusters,
+				       u32 prev_cpos,
+				       u64 prev_blkno,
+				       int *extend)
+{
+	int ret, credits;
+	u16 bpc = ocfs2_clusters_to_blocks(inode->i_sb, 1);
+	u32 prev_clusters = *num_clusters;
+	u32 clusters_to_add = 1, bit_off, num_bits, v_start = 0;
+	u64 block;
+	handle_t *handle = NULL;
+	struct ocfs2_alloc_context *data_ac = NULL;
+	struct ocfs2_alloc_context *meta_ac = NULL;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+	struct ocfs2_extent_tree et;
+
+	mlog(0, "Add new xattr cluster for %llu, previous xattr hash = %u, "
+	     "previous xattr blkno = %llu\n",
+	     (unsigned long long)OCFS2_I(inode)->ip_blkno,
+	     prev_cpos, prev_blkno);
+
+	ocfs2_init_xattr_tree_extent_tree(&et, inode, root_bh);
+
+	ret = ocfs2_lock_allocators(inode, &et, clusters_to_add, 0,
+				    &data_ac, &meta_ac);
+	if (ret) {
+		mlog_errno(ret);
+		goto leave;
+	}
+
+	credits = ocfs2_calc_extend_credits(osb->sb, et.et_root_el,
+					    clusters_to_add);
+	handle = ocfs2_start_trans(osb, credits);
+	if (IS_ERR(handle)) {
+		ret = PTR_ERR(handle);
+		handle = NULL;
+		mlog_errno(ret);
+		goto leave;
+	}
+
+	ret = ocfs2_journal_access(handle, inode, root_bh,
+				   OCFS2_JOURNAL_ACCESS_WRITE);
+	if (ret < 0) {
+		mlog_errno(ret);
+		goto leave;
+	}
+
+	ret = __ocfs2_claim_clusters(osb, handle, data_ac, 1,
+				     clusters_to_add, &bit_off, &num_bits);
+	if (ret < 0) {
+		if (ret != -ENOSPC)
+			mlog_errno(ret);
+		goto leave;
+	}
+
+	BUG_ON(num_bits > clusters_to_add);
+
+	block = ocfs2_clusters_to_blocks(osb->sb, bit_off);
+	mlog(0, "Allocating %u clusters at block %u for xattr in inode %llu\n",
+	     num_bits, bit_off, (unsigned long long)OCFS2_I(inode)->ip_blkno);
+
+	if (prev_blkno + prev_clusters * bpc == block &&
+	    (prev_clusters + num_bits) << osb->s_clustersize_bits <=
+	     OCFS2_MAX_XATTR_TREE_LEAF_SIZE) {
+		/*
+		 * If this cluster is contiguous with the old one and
+		 * adding this new cluster, we don't surpass the limit of
+		 * OCFS2_MAX_XATTR_TREE_LEAF_SIZE, cool. We will let it be
+		 * initialized and used like other buckets in the previous
+		 * cluster.
+		 * So add it as a contiguous one. The caller will handle
+		 * its init process.
+		 */
+		v_start = prev_cpos + prev_clusters;
+		*num_clusters = prev_clusters + num_bits;
+		mlog(0, "Add contiguous %u clusters to previous extent rec.\n",
+		     num_bits);
+	} else {
+		ret = ocfs2_adjust_xattr_cross_cluster(inode,
+						       handle,
+						       first_bh,
+						       header_bh,
+						       block,
+						       prev_blkno,
+						       prev_clusters,
+						       &v_start,
+						       extend);
+		if (ret) {
+			mlog_errno(ret);
+			goto leave;
+		}
+	}
+
+	if (handle->h_buffer_credits < credits) {
+		/*
+		 * The journal has been restarted before, and don't
+		 * have enough space for the insertion, so extend it
+		 * here.
+		 */
+		ret = ocfs2_extend_trans(handle, credits);
+		if (ret) {
+			mlog_errno(ret);
+			goto leave;
+		}
+	}
+	mlog(0, "Insert %u clusters at block %llu for xattr at %u\n",
+	     num_bits, block, v_start);
+	ret = ocfs2_insert_extent(osb, handle, inode, &et, v_start, block,
+				  num_bits, 0, meta_ac);
+	if (ret < 0) {
+		mlog_errno(ret);
+		goto leave;
+	}
+
+	ret = ocfs2_journal_dirty(handle, root_bh);
+	if (ret < 0) {
+		mlog_errno(ret);
+		goto leave;
+	}
+
+leave:
+	if (handle)
+		ocfs2_commit_trans(osb, handle);
+	if (data_ac)
+		ocfs2_free_alloc_context(data_ac);
+	if (meta_ac)
+		ocfs2_free_alloc_context(meta_ac);
+
+	return ret;
+}
+
+/*
+ * Extend a new xattr bucket and move xattrs to the end one by one until
+ * We meet with start_bh. Only move half of the xattrs to the bucket after it.
+ */
+static int ocfs2_extend_xattr_bucket(struct inode *inode,
+				     struct buffer_head *first_bh,
+				     struct buffer_head *start_bh,
+				     u32 num_clusters)
+{
+	int ret, credits;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+	u16 blk_per_bucket = ocfs2_blocks_per_xattr_bucket(inode->i_sb);
+	u64 start_blk = start_bh->b_blocknr, end_blk;
+	u32 num_buckets = num_clusters * ocfs2_xattr_buckets_per_cluster(osb);
+	handle_t *handle;
+	struct ocfs2_xattr_header *first_xh =
+				(struct ocfs2_xattr_header *)first_bh->b_data;
+	u16 bucket = le16_to_cpu(first_xh->xh_num_buckets);
+
+	mlog(0, "extend xattr bucket in %llu, xattr extend rec starting "
+	     "from %llu, len = %u\n", start_blk,
+	     (unsigned long long)first_bh->b_blocknr, num_clusters);
+
+	BUG_ON(bucket >= num_buckets);
+
+	end_blk = first_bh->b_blocknr + (bucket - 1) * blk_per_bucket;
+
+	/*
+	 * We will touch all the buckets after the start_bh(include it).
+	 * Add one more bucket and modify the first_bh.
+	 */
+	credits = end_blk - start_blk + 2 * blk_per_bucket + 1;
+	handle = ocfs2_start_trans(osb, credits);
+	if (IS_ERR(handle)) {
+		ret = PTR_ERR(handle);
+		handle = NULL;
+		mlog_errno(ret);
+		goto out;
+	}
+
+	ret = ocfs2_journal_access(handle, inode, first_bh,
+				   OCFS2_JOURNAL_ACCESS_WRITE);
+	if (ret) {
+		mlog_errno(ret);
+		goto commit;
+	}
+
+	while (end_blk != start_blk) {
+		ret = ocfs2_cp_xattr_bucket(inode, handle, end_blk,
+					    end_blk + blk_per_bucket, 0);
+		if (ret)
+			goto commit;
+		end_blk -= blk_per_bucket;
+	}
+
+	/* Move half of the xattr in start_blk to the next bucket. */
+	ret = ocfs2_half_xattr_bucket(inode, handle, start_blk,
+				      start_blk + blk_per_bucket, NULL, 0);
+
+	le16_add_cpu(&first_xh->xh_num_buckets, 1);
+	ocfs2_journal_dirty(handle, first_bh);
+
+commit:
+	ocfs2_commit_trans(osb, handle);
+out:
+	return ret;
+}
+
+/*
+ * Add new xattr bucket in an extent record and adjust the buckets accordingly.
+ * xb_bh is the ocfs2_xattr_block.
+ * We will move all the buckets starting from header_bh to the next place. As
+ * for this one, half num of its xattrs will be moved to the next one.
+ *
+ * We will allocate a new cluster if current cluster is full and adjust
+ * header_bh and first_bh if the insert place is moved to the new cluster.
+ */
+static int ocfs2_add_new_xattr_bucket(struct inode *inode,
+				      struct buffer_head *xb_bh,
+				      struct buffer_head *header_bh)
+{
+	struct ocfs2_xattr_header *first_xh = NULL;
+	struct buffer_head *first_bh = NULL;
+	struct ocfs2_xattr_block *xb =
+			(struct ocfs2_xattr_block *)xb_bh->b_data;
+	struct ocfs2_xattr_tree_root *xb_root = &xb->xb_attrs.xb_root;
+	struct ocfs2_extent_list *el = &xb_root->xt_list;
+	struct ocfs2_xattr_header *xh =
+			(struct ocfs2_xattr_header *)header_bh->b_data;
+	u32 name_hash = le32_to_cpu(xh->xh_entries[0].xe_name_hash);
+	struct super_block *sb = inode->i_sb;
+	struct ocfs2_super *osb = OCFS2_SB(sb);
+	int ret, num_buckets, extend = 1;
+	u64 p_blkno;
+	u32 e_cpos, num_clusters;
+
+	mlog(0, "Add new xattr bucket starting form %llu\n",
+	     (unsigned long long)header_bh->b_blocknr);
+
+	/*
+	 * Add refrence for header_bh here because it may be
+	 * changed in ocfs2_add_new_xattr_cluster and we need
+	 * to free it in the end.
+	 */
+	get_bh(header_bh);
+
+	ret = ocfs2_xattr_get_rec(inode, name_hash, &p_blkno, &e_cpos,
+				  &num_clusters, el);
+	if (ret) {
+		mlog_errno(ret);
+		goto out;
+	}
+
+	ret = ocfs2_read_block(inode, p_blkno, &first_bh);
+	if (ret) {
+		mlog_errno(ret);
+		goto out;
+	}
+
+	num_buckets = ocfs2_xattr_buckets_per_cluster(osb) * num_clusters;
+	first_xh = (struct ocfs2_xattr_header *)first_bh->b_data;
+
+	if (num_buckets == le16_to_cpu(first_xh->xh_num_buckets)) {
+		ret = ocfs2_add_new_xattr_cluster(inode,
+						  xb_bh,
+						  &first_bh,
+						  &header_bh,
+						  &num_clusters,
+						  e_cpos,
+						  p_blkno,
+						  &extend);
+		if (ret) {
+			mlog_errno(ret);
+			goto out;
+		}
+	}
+
+	if (extend)
+		ret = ocfs2_extend_xattr_bucket(inode,
+						first_bh,
+						header_bh,
+						num_clusters);
+	if (ret)
+		mlog_errno(ret);
+out:
+	brelse(first_bh);
+	brelse(header_bh);
+	return ret;
+}
+
+static inline char *ocfs2_xattr_bucket_get_val(struct inode *inode,
+					struct ocfs2_xattr_bucket *bucket,
+					int offs)
+{
+	int block_off = offs >> inode->i_sb->s_blocksize_bits;
+
+	offs = offs % inode->i_sb->s_blocksize;
+	return bucket->bhs[block_off]->b_data + offs;
+}
+
+/*
+ * Handle the normal xattr set, including replace, delete and new.
+ *
+ * Note: "local" indicates the real data's locality. So we can't
+ * just its bucket locality by its length.
+ */
+static void ocfs2_xattr_set_entry_normal(struct inode *inode,
+					 struct ocfs2_xattr_info *xi,
+					 struct ocfs2_xattr_search *xs,
+					 u32 name_hash,
+					 int local)
+{
+	struct ocfs2_xattr_entry *last, *xe;
+	int name_len = strlen(xi->name);
+	struct ocfs2_xattr_header *xh = xs->header;
+	u16 count = le16_to_cpu(xh->xh_count), start;
+	size_t blocksize = inode->i_sb->s_blocksize;
+	char *val;
+	size_t offs, size, new_size;
+
+	last = &xh->xh_entries[count];
+	if (!xs->not_found) {
+		xe = xs->here;
+		offs = le16_to_cpu(xe->xe_name_offset);
+		if (ocfs2_xattr_is_local(xe))
+			size = OCFS2_XATTR_SIZE(name_len) +
+			OCFS2_XATTR_SIZE(le64_to_cpu(xe->xe_value_size));
+		else
+			size = OCFS2_XATTR_SIZE(name_len) +
+			OCFS2_XATTR_SIZE(OCFS2_XATTR_ROOT_SIZE);
+
+		/*
+		 * If the new value will be stored outside, xi->value has been
+		 * initalized as an empty ocfs2_xattr_value_root, and the same
+		 * goes with xi->value_len, so we can set new_size safely here.
+		 * See ocfs2_xattr_set_in_bucket.
+		 */
+		new_size = OCFS2_XATTR_SIZE(name_len) +
+			   OCFS2_XATTR_SIZE(xi->value_len);
+
+		le16_add_cpu(&xh->xh_name_value_len, -size);
+		if (xi->value) {
+			if (new_size > size)
+				goto set_new_name_value;
+
+			/* Now replace the old value with new one. */
+			if (local)
+				xe->xe_value_size = cpu_to_le64(xi->value_len);
+			else
+				xe->xe_value_size = 0;
+
+			val = ocfs2_xattr_bucket_get_val(inode,
+							 &xs->bucket, offs);
+			memset(val + OCFS2_XATTR_SIZE(name_len), 0,
+			       size - OCFS2_XATTR_SIZE(name_len));
+			if (OCFS2_XATTR_SIZE(xi->value_len) > 0)
+				memcpy(val + OCFS2_XATTR_SIZE(name_len),
+				       xi->value, xi->value_len);
+
+			le16_add_cpu(&xh->xh_name_value_len, new_size);
+			ocfs2_xattr_set_local(xe, local);
+			return;
+		} else {
+			/*
+			 * Remove the old entry if there is more than one.
+			 * We don't remove the last entry so that we can
+			 * use it to indicate the hash value of the empty
+			 * bucket.
+			 */
+			last -= 1;
+			le16_add_cpu(&xh->xh_count, -1);
+			if (xh->xh_count) {
+				memmove(xe, xe + 1,
+					(void *)last - (void *)xe);
+				memset(last, 0,
+				       sizeof(struct ocfs2_xattr_entry));
+			} else
+				xh->xh_free_start =
+					cpu_to_le16(OCFS2_XATTR_BUCKET_SIZE);
+
+			return;
+		}
+	} else {
+		/* find a new entry for insert. */
+		int low = 0, high = count - 1, tmp;
+		struct ocfs2_xattr_entry *tmp_xe;
+
+		while (low <= high && count) {
+			tmp = (low + high) / 2;
+			tmp_xe = &xh->xh_entries[tmp];
+
+			if (name_hash > le32_to_cpu(tmp_xe->xe_name_hash))
+				low = tmp + 1;
+			else if (name_hash <
+				 le32_to_cpu(tmp_xe->xe_name_hash))
+				high = tmp - 1;
+			else {
+				low = tmp;
+				break;
+			}
+		}
+
+		xe = &xh->xh_entries[low];
+		if (low != count)
+			memmove(xe + 1, xe, (void *)last - (void *)xe);
+
+		le16_add_cpu(&xh->xh_count, 1);
+		memset(xe, 0, sizeof(struct ocfs2_xattr_entry));
+		xe->xe_name_hash = cpu_to_le32(name_hash);
+		xe->xe_name_len = name_len;
+		ocfs2_xattr_set_type(xe, xi->name_index);
+	}
+
+set_new_name_value:
+	/* Insert the new name+value. */
+	size = OCFS2_XATTR_SIZE(name_len) + OCFS2_XATTR_SIZE(xi->value_len);
+
+	/*
+	 * We must make sure that the name/value pair
+	 * exists in the same block.
+	 */
+	offs = le16_to_cpu(xh->xh_free_start);
+	start = offs - size;
+
+	if (start >> inode->i_sb->s_blocksize_bits !=
+	    (offs - 1) >> inode->i_sb->s_blocksize_bits) {
+		offs = offs - offs % blocksize;
+		xh->xh_free_start = cpu_to_le16(offs);
+	}
+
+	val = ocfs2_xattr_bucket_get_val(inode,
+					 &xs->bucket, offs - size);
+	xe->xe_name_offset = cpu_to_le16(offs - size);
+
+	memset(val, 0, size);
+	memcpy(val, xi->name, name_len);
+	memcpy(val + OCFS2_XATTR_SIZE(name_len), xi->value, xi->value_len);
+
+	xe->xe_value_size = cpu_to_le64(xi->value_len);
+	ocfs2_xattr_set_local(xe, local);
+	xs->here = xe;
+	le16_add_cpu(&xh->xh_free_start, -size);
+	le16_add_cpu(&xh->xh_name_value_len, size);
+
+	return;
+}
+
+static int ocfs2_xattr_bucket_handle_journal(struct inode *inode,
+					     handle_t *handle,
+					     struct ocfs2_xattr_search *xs,
+					     struct buffer_head **bhs,
+					     u16 bh_num)
+{
+	int ret = 0, off, block_off;
+	struct ocfs2_xattr_entry *xe = xs->here;
+
+	/*
+	 * First calculate all the blocks we should journal_access
+	 * and journal_dirty. The first block should always be touched.
+	 */
+	ret = ocfs2_journal_dirty(handle, bhs[0]);
+	if (ret)
+		mlog_errno(ret);
+
+	/* calc the data. */
+	off = le16_to_cpu(xe->xe_name_offset);
+	block_off = off >> inode->i_sb->s_blocksize_bits;
+	ret = ocfs2_journal_dirty(handle, bhs[block_off]);
+	if (ret)
+		mlog_errno(ret);
+
+	return ret;
+}
+
+/*
+ * Set the xattr entry in the specified bucket.
+ * The bucket is indicated by xs->bucket and it should have the enough
+ * space for the xattr insertion.
+ */
+static int ocfs2_xattr_set_entry_in_bucket(struct inode *inode,
+					   struct ocfs2_xattr_info *xi,
+					   struct ocfs2_xattr_search *xs,
+					   u32 name_hash,
+					   int local)
+{
+	int i, ret;
+	handle_t *handle = NULL;
+	u16 blk_per_bucket = ocfs2_blocks_per_xattr_bucket(inode->i_sb);
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+	mlog(0, "Set xattr entry len = %lu index = %d in bucket %llu\n",
+	     (unsigned long)xi->value_len, xi->name_index,
+	     (unsigned long long)xs->bucket.bhs[0]->b_blocknr);
+
+	if (!xs->bucket.bhs[1]) {
+		ret = ocfs2_read_blocks(inode,
+					xs->bucket.bhs[0]->b_blocknr + 1,
+					blk_per_bucket - 1, &xs->bucket.bhs[1],
+					OCFS2_BH_CACHED);
+		if (ret) {
+			mlog_errno(ret);
+			goto out;
+		}
+	}
+
+	handle = ocfs2_start_trans(osb, blk_per_bucket);
+	if (IS_ERR(handle)) {
+		ret = PTR_ERR(handle);
+		handle = NULL;
+		mlog_errno(ret);
+		goto out;
+	}
+
+	for (i = 0; i < blk_per_bucket; i++) {
+		ret = ocfs2_journal_access(handle, inode, xs->bucket.bhs[i],
+					   OCFS2_JOURNAL_ACCESS_WRITE);
+		if (ret < 0) {
+			mlog_errno(ret);
+			goto out;
+		}
+	}
+
+	ocfs2_xattr_set_entry_normal(inode, xi, xs, name_hash, local);
+
+	/*Only dirty the blocks we have touched in set xattr. */
+	ret = ocfs2_xattr_bucket_handle_journal(inode, handle, xs,
+						xs->bucket.bhs, blk_per_bucket);
+	if (ret)
+		mlog_errno(ret);
+out:
+	ocfs2_commit_trans(osb, handle);
+
+	return ret;
+}
+
+static int ocfs2_xattr_value_update_size(struct inode *inode,
+					 struct buffer_head *xe_bh,
+					 struct ocfs2_xattr_entry *xe,
+					 u64 new_size)
+{
+	int ret;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+	handle_t *handle = NULL;
+
+	handle = ocfs2_start_trans(osb, 1);
+	if (handle == NULL) {
+		ret = -ENOMEM;
+		mlog_errno(ret);
+		goto out;
+	}
+
+	ret = ocfs2_journal_access(handle, inode, xe_bh,
+				   OCFS2_JOURNAL_ACCESS_WRITE);
+	if (ret < 0) {
+		mlog_errno(ret);
+		goto out_commit;
+	}
+
+	xe->xe_value_size = cpu_to_le64(new_size);
+
+	ret = ocfs2_journal_dirty(handle, xe_bh);
+	if (ret < 0)
+		mlog_errno(ret);
+
+out_commit:
+	ocfs2_commit_trans(osb, handle);
+out:
+	return ret;
+}
+
+/*
+ * Truncate the specified xe_off entry in xattr bucket.
+ * bucket is indicated by header_bh and len is the new length.
+ * Both the ocfs2_xattr_value_root and the entry will be updated here.
+ *
+ * Copy the new updated xe and xe_value_root to new_xe and new_xv if needed.
+ */
+static int ocfs2_xattr_bucket_value_truncate(struct inode *inode,
+					     struct buffer_head *header_bh,
+					     int xe_off,
+					     int len)
+{
+	int ret, offset;
+	u64 value_blk;
+	struct buffer_head *value_bh = NULL;
+	struct ocfs2_xattr_value_root *xv;
+	struct ocfs2_xattr_entry *xe;
+	struct ocfs2_xattr_header *xh =
+			(struct ocfs2_xattr_header *)header_bh->b_data;
+	size_t blocksize = inode->i_sb->s_blocksize;
+
+	xe = &xh->xh_entries[xe_off];
+
+	BUG_ON(!xe || ocfs2_xattr_is_local(xe));
+
+	offset = le16_to_cpu(xe->xe_name_offset) +
+		 OCFS2_XATTR_SIZE(xe->xe_name_len);
+
+	value_blk = offset / blocksize;
+
+	/* We don't allow ocfs2_xattr_value to be stored in different block. */
+	BUG_ON(value_blk != (offset + OCFS2_XATTR_ROOT_SIZE - 1) / blocksize);
+	value_blk += header_bh->b_blocknr;
+
+	ret = ocfs2_read_block(inode, value_blk, &value_bh);
+	if (ret) {
+		mlog_errno(ret);
+		goto out;
+	}
+
+	xv = (struct ocfs2_xattr_value_root *)
+		(value_bh->b_data + offset % blocksize);
+
+	mlog(0, "truncate %u in xattr bucket %llu to %d bytes.\n",
+	     xe_off, (unsigned long long)header_bh->b_blocknr, len);
+	ret = ocfs2_xattr_value_truncate(inode, value_bh, xv, len);
+	if (ret) {
+		mlog_errno(ret);
+		goto out;
+	}
+
+	ret = ocfs2_xattr_value_update_size(inode, header_bh, xe, len);
+	if (ret) {
+		mlog_errno(ret);
+		goto out;
+	}
+
+out:
+	brelse(value_bh);
+	return ret;
+}
+
+static int ocfs2_xattr_bucket_value_truncate_xs(struct inode *inode,
+						struct ocfs2_xattr_search *xs,
+						int len)
+{
+	int ret, offset;
+	struct ocfs2_xattr_entry *xe = xs->here;
+	struct ocfs2_xattr_header *xh = (struct ocfs2_xattr_header *)xs->base;
+
+	BUG_ON(!xs->bucket.bhs[0] || !xe || ocfs2_xattr_is_local(xe));
+
+	offset = xe - xh->xh_entries;
+	ret = ocfs2_xattr_bucket_value_truncate(inode, xs->bucket.bhs[0],
+						offset, len);
+	if (ret)
+		mlog_errno(ret);
+
+	return ret;
+}
+
+static int ocfs2_xattr_bucket_set_value_outside(struct inode *inode,
+						struct ocfs2_xattr_search *xs,
+						char *val,
+						int value_len)
+{
+	int offset;
+	struct ocfs2_xattr_value_root *xv;
+	struct ocfs2_xattr_entry *xe = xs->here;
+
+	BUG_ON(!xs->base || !xe || ocfs2_xattr_is_local(xe));
+
+	offset = le16_to_cpu(xe->xe_name_offset) +
+		 OCFS2_XATTR_SIZE(xe->xe_name_len);
+
+	xv = (struct ocfs2_xattr_value_root *)(xs->base + offset);
+
+	return __ocfs2_xattr_set_value_outside(inode, xv, val, value_len);
+}
+
+static int ocfs2_rm_xattr_cluster(struct inode *inode,
+				  struct buffer_head *root_bh,
+				  u64 blkno,
+				  u32 cpos,
+				  u32 len)
+{
+	int ret;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+	struct inode *tl_inode = osb->osb_tl_inode;
+	handle_t *handle;
+	struct ocfs2_xattr_block *xb =
+			(struct ocfs2_xattr_block *)root_bh->b_data;
+	struct ocfs2_alloc_context *meta_ac = NULL;
+	struct ocfs2_cached_dealloc_ctxt dealloc;
+	struct ocfs2_extent_tree et;
+
+	ocfs2_init_xattr_tree_extent_tree(&et, inode, root_bh);
+
+	ocfs2_init_dealloc_ctxt(&dealloc);
+
+	mlog(0, "rm xattr extent rec at %u len = %u, start from %llu\n",
+	     cpos, len, (unsigned long long)blkno);
+
+	ocfs2_remove_xattr_clusters_from_cache(inode, blkno, len);
+
+	ret = ocfs2_lock_allocators(inode, &et, 0, 1, NULL, &meta_ac);
+	if (ret) {
+		mlog_errno(ret);
+		return ret;
+	}
+
+	mutex_lock(&tl_inode->i_mutex);
+
+	if (ocfs2_truncate_log_needs_flush(osb)) {
+		ret = __ocfs2_flush_truncate_log(osb);
+		if (ret < 0) {
+			mlog_errno(ret);
+			goto out;
+		}
+	}
+
+	handle = ocfs2_start_trans(osb, OCFS2_REMOVE_EXTENT_CREDITS);
+	if (handle == NULL) {
+		ret = -ENOMEM;
+		mlog_errno(ret);
+		goto out;
+	}
+
+	ret = ocfs2_journal_access(handle, inode, root_bh,
+				   OCFS2_JOURNAL_ACCESS_WRITE);
+	if (ret) {
+		mlog_errno(ret);
+		goto out_commit;
+	}
+
+	ret = ocfs2_remove_extent(inode, &et, cpos, len, handle, meta_ac,
+				  &dealloc);
+	if (ret) {
+		mlog_errno(ret);
+		goto out_commit;
+	}
+
+	le32_add_cpu(&xb->xb_attrs.xb_root.xt_clusters, -len);
+
+	ret = ocfs2_journal_dirty(handle, root_bh);
+	if (ret) {
+		mlog_errno(ret);
+		goto out_commit;
+	}
+
+	ret = ocfs2_truncate_log_append(osb, handle, blkno, len);
+	if (ret)
+		mlog_errno(ret);
+
+out_commit:
+	ocfs2_commit_trans(osb, handle);
+out:
+	ocfs2_schedule_truncate_log_flush(osb, 1);
+
+	mutex_unlock(&tl_inode->i_mutex);
+
+	if (meta_ac)
+		ocfs2_free_alloc_context(meta_ac);
+
+	ocfs2_run_deallocs(osb, &dealloc);
+
+	return ret;
+}
+
+static void ocfs2_xattr_bucket_remove_xs(struct inode *inode,
+					 struct ocfs2_xattr_search *xs)
+{
+	handle_t *handle = NULL;
+	struct ocfs2_xattr_header *xh = xs->bucket.xh;
+	struct ocfs2_xattr_entry *last = &xh->xh_entries[
+						le16_to_cpu(xh->xh_count) - 1];
+	int ret = 0;
+
+	handle = ocfs2_start_trans((OCFS2_SB(inode->i_sb)), 1);
+	if (IS_ERR(handle)) {
+		ret = PTR_ERR(handle);
+		mlog_errno(ret);
+		return;
+	}
+
+	ret = ocfs2_journal_access(handle, inode, xs->bucket.bhs[0],
+				   OCFS2_JOURNAL_ACCESS_WRITE);
+	if (ret) {
+		mlog_errno(ret);
+		goto out_commit;
+	}
+
+	/* Remove the old entry. */
+	memmove(xs->here, xs->here + 1,
+		(void *)last - (void *)xs->here);
+	memset(last, 0, sizeof(struct ocfs2_xattr_entry));
+	le16_add_cpu(&xh->xh_count, -1);
+
+	ret = ocfs2_journal_dirty(handle, xs->bucket.bhs[0]);
+	if (ret < 0)
+		mlog_errno(ret);
+out_commit:
+	ocfs2_commit_trans(OCFS2_SB(inode->i_sb), handle);
+}
+
+/*
+ * Set the xattr name/value in the bucket specified in xs.
+ *
+ * As the new value in xi may be stored in the bucket or in an outside cluster,
+ * we divide the whole process into 3 steps:
+ * 1. insert name/value in the bucket(ocfs2_xattr_set_entry_in_bucket)
+ * 2. truncate of the outside cluster(ocfs2_xattr_bucket_value_truncate_xs)
+ * 3. Set the value to the outside cluster(ocfs2_xattr_bucket_set_value_outside)
+ * 4. If the clusters for the new outside value can't be allocated, we need
+ *    to free the xattr we allocated in set.
+ */
+static int ocfs2_xattr_set_in_bucket(struct inode *inode,
+				     struct ocfs2_xattr_info *xi,
+				     struct ocfs2_xattr_search *xs)
+{
+	int ret, local = 1;
+	size_t value_len;
+	char *val = (char *)xi->value;
+	struct ocfs2_xattr_entry *xe = xs->here;
+	u32 name_hash = ocfs2_xattr_name_hash(inode, xi->name,
+					      strlen(xi->name));
+
+	if (!xs->not_found && !ocfs2_xattr_is_local(xe)) {
+		/*
+		 * We need to truncate the xattr storage first.
+		 *
+		 * If both the old and new value are stored to
+		 * outside block, we only need to truncate
+		 * the storage and then set the value outside.
+		 *
+		 * If the new value should be stored within block,
+		 * we should free all the outside block first and
+		 * the modification to the xattr block will be done
+		 * by following steps.
+		 */
+		if (xi->value_len > OCFS2_XATTR_INLINE_SIZE)
+			value_len = xi->value_len;
+		else
+			value_len = 0;
+
+		ret = ocfs2_xattr_bucket_value_truncate_xs(inode, xs,
+							   value_len);
+		if (ret)
+			goto out;
+
+		if (value_len)
+			goto set_value_outside;
+	}
+
+	value_len = xi->value_len;
+	/* So we have to handle the inside block change now. */
+	if (value_len > OCFS2_XATTR_INLINE_SIZE) {
+		/*
+		 * If the new value will be stored outside of block,
+		 * initalize a new empty value root and insert it first.
+		 */
+		local = 0;
+		xi->value = &def_xv;
+		xi->value_len = OCFS2_XATTR_ROOT_SIZE;
+	}
+
+	ret = ocfs2_xattr_set_entry_in_bucket(inode, xi, xs, name_hash, local);
+	if (ret) {
+		mlog_errno(ret);
+		goto out;
+	}
+
+	if (value_len <= OCFS2_XATTR_INLINE_SIZE)
+		goto out;
+
+	/* allocate the space now for the outside block storage. */
+	ret = ocfs2_xattr_bucket_value_truncate_xs(inode, xs,
+						   value_len);
+	if (ret) {
+		mlog_errno(ret);
+
+		if (xs->not_found) {
+			/*
+			 * We can't allocate enough clusters for outside
+			 * storage and we have allocated xattr already,
+			 * so need to remove it.
+			 */
+			ocfs2_xattr_bucket_remove_xs(inode, xs);
+		}
+		goto out;
+	}
+
+set_value_outside:
+	ret = ocfs2_xattr_bucket_set_value_outside(inode, xs, val, value_len);
+out:
+	return ret;
+}
+
+/* check whether the xattr bucket is filled up with the same hash value. */
+static int ocfs2_check_xattr_bucket_collision(struct inode *inode,
+					      struct ocfs2_xattr_bucket *bucket)
+{
+	struct ocfs2_xattr_header *xh = bucket->xh;
+
+	if (xh->xh_entries[le16_to_cpu(xh->xh_count) - 1].xe_name_hash ==
+	    xh->xh_entries[0].xe_name_hash) {
+		mlog(ML_ERROR, "Too much hash collision in xattr bucket %llu, "
+		     "hash = %u\n",
+		     (unsigned long long)bucket->bhs[0]->b_blocknr,
+		     le32_to_cpu(xh->xh_entries[0].xe_name_hash));
+		return -ENOSPC;
+	}
+
+	return 0;
+}
+
+static int ocfs2_xattr_set_entry_index_block(struct inode *inode,
+					     struct ocfs2_xattr_info *xi,
+					     struct ocfs2_xattr_search *xs)
+{
+	struct ocfs2_xattr_header *xh;
+	struct ocfs2_xattr_entry *xe;
+	u16 count, header_size, xh_free_start;
+	int i, free, max_free, need, old;
+	size_t value_size = 0, name_len = strlen(xi->name);
+	size_t blocksize = inode->i_sb->s_blocksize;
+	int ret, allocation = 0;
+	u16 blk_per_bucket = ocfs2_blocks_per_xattr_bucket(inode->i_sb);
+
+	mlog_entry("Set xattr %s in xattr index block\n", xi->name);
+
+try_again:
+	xh = xs->header;
+	count = le16_to_cpu(xh->xh_count);
+	xh_free_start = le16_to_cpu(xh->xh_free_start);
+	header_size = sizeof(struct ocfs2_xattr_header) +
+			count * sizeof(struct ocfs2_xattr_entry);
+	max_free = OCFS2_XATTR_BUCKET_SIZE -
+		le16_to_cpu(xh->xh_name_value_len) - header_size;
+
+	mlog_bug_on_msg(header_size > blocksize, "bucket %llu has header size "
+			"of %u which exceed block size\n",
+			(unsigned long long)xs->bucket.bhs[0]->b_blocknr,
+			header_size);
+
+	if (xi->value && xi->value_len > OCFS2_XATTR_INLINE_SIZE)
+		value_size = OCFS2_XATTR_ROOT_SIZE;
+	else if (xi->value)
+		value_size = OCFS2_XATTR_SIZE(xi->value_len);
+
+	if (xs->not_found)
+		need = sizeof(struct ocfs2_xattr_entry) +
+			OCFS2_XATTR_SIZE(name_len) + value_size;
+	else {
+		need = value_size + OCFS2_XATTR_SIZE(name_len);
+
+		/*
+		 * We only replace the old value if the new length is smaller
+		 * than the old one. Otherwise we will allocate new space in the
+		 * bucket to store it.
+		 */
+		xe = xs->here;
+		if (ocfs2_xattr_is_local(xe))
+			old = OCFS2_XATTR_SIZE(le64_to_cpu(xe->xe_value_size));
+		else
+			old = OCFS2_XATTR_SIZE(OCFS2_XATTR_ROOT_SIZE);
+
+		if (old >= value_size)
+			need = 0;
+	}
+
+	free = xh_free_start - header_size;
+	/*
+	 * We need to make sure the new name/value pair
+	 * can exist in the same block.
+	 */
+	if (xh_free_start % blocksize < need)
+		free -= xh_free_start % blocksize;
+
+	mlog(0, "xs->not_found = %d, in xattr bucket %llu: free = %d, "
+	     "need = %d, max_free = %d, xh_free_start = %u, xh_name_value_len ="
+	     " %u\n", xs->not_found,
+	     (unsigned long long)xs->bucket.bhs[0]->b_blocknr,
+	     free, need, max_free, le16_to_cpu(xh->xh_free_start),
+	     le16_to_cpu(xh->xh_name_value_len));
+
+	if (free < need || count == ocfs2_xattr_max_xe_in_bucket(inode->i_sb)) {
+		if (need <= max_free &&
+		    count < ocfs2_xattr_max_xe_in_bucket(inode->i_sb)) {
+			/*
+			 * We can create the space by defragment. Since only the
+			 * name/value will be moved, the xe shouldn't be changed
+			 * in xs.
+			 */
+			ret = ocfs2_defrag_xattr_bucket(inode, &xs->bucket);
+			if (ret) {
+				mlog_errno(ret);
+				goto out;
+			}
+
+			xh_free_start = le16_to_cpu(xh->xh_free_start);
+			free = xh_free_start - header_size;
+			if (xh_free_start % blocksize < need)
+				free -= xh_free_start % blocksize;
+
+			if (free >= need)
+				goto xattr_set;
+
+			mlog(0, "Can't get enough space for xattr insert by "
+			     "defragment. Need %u bytes, but we have %d, so "
+			     "allocate new bucket for it.\n", need, free);
+		}
+
+		/*
+		 * We have to add new buckets or clusters and one
+		 * allocation should leave us enough space for insert.
+		 */
+		BUG_ON(allocation);
+
+		/*
+		 * We do not allow for overlapping ranges between buckets. And
+		 * the maximum number of collisions we will allow for then is
+		 * one bucket's worth, so check it here whether we need to
+		 * add a new bucket for the insert.
+		 */
+		ret = ocfs2_check_xattr_bucket_collision(inode, &xs->bucket);
+		if (ret) {
+			mlog_errno(ret);
+			goto out;
+		}
+
+		ret = ocfs2_add_new_xattr_bucket(inode,
+						 xs->xattr_bh,
+						 xs->bucket.bhs[0]);
+		if (ret) {
+			mlog_errno(ret);
+			goto out;
+		}
+
+		for (i = 0; i < blk_per_bucket; i++)
+			brelse(xs->bucket.bhs[i]);
+
+		memset(&xs->bucket, 0, sizeof(xs->bucket));
+
+		ret = ocfs2_xattr_index_block_find(inode, xs->xattr_bh,
+						   xi->name_index,
+						   xi->name, xs);
+		if (ret && ret != -ENODATA)
+			goto out;
+		xs->not_found = ret;
+		allocation = 1;
+		goto try_again;
+	}
+
+xattr_set:
+	ret = ocfs2_xattr_set_in_bucket(inode, xi, xs);
+out:
+	mlog_exit(ret);
+	return ret;
+}
+
+static int ocfs2_delete_xattr_in_bucket(struct inode *inode,
+					struct ocfs2_xattr_bucket *bucket,
+					void *para)
+{
+	int ret = 0;
+	struct ocfs2_xattr_header *xh = bucket->xh;
+	u16 i;
+	struct ocfs2_xattr_entry *xe;
+
+	for (i = 0; i < le16_to_cpu(xh->xh_count); i++) {
+		xe = &xh->xh_entries[i];
+		if (ocfs2_xattr_is_local(xe))
+			continue;
+
+		ret = ocfs2_xattr_bucket_value_truncate(inode,
+							bucket->bhs[0],
+							i, 0);
+		if (ret) {
+			mlog_errno(ret);
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static int ocfs2_delete_xattr_index_block(struct inode *inode,
+					  struct buffer_head *xb_bh)
+{
+	struct ocfs2_xattr_block *xb =
+			(struct ocfs2_xattr_block *)xb_bh->b_data;
+	struct ocfs2_extent_list *el = &xb->xb_attrs.xb_root.xt_list;
+	int ret = 0;
+	u32 name_hash = UINT_MAX, e_cpos, num_clusters;
+	u64 p_blkno;
+
+	if (le16_to_cpu(el->l_next_free_rec) == 0)
+		return 0;
+
+	while (name_hash > 0) {
+		ret = ocfs2_xattr_get_rec(inode, name_hash, &p_blkno,
+					  &e_cpos, &num_clusters, el);
+		if (ret) {
+			mlog_errno(ret);
+			goto out;
+		}
+
+		ret = ocfs2_iterate_xattr_buckets(inode, p_blkno, num_clusters,
+						  ocfs2_delete_xattr_in_bucket,
+						  NULL);
+		if (ret) {
+			mlog_errno(ret);
+			goto out;
+		}
+
+		ret = ocfs2_rm_xattr_cluster(inode, xb_bh,
+					     p_blkno, e_cpos, num_clusters);
+		if (ret) {
+			mlog_errno(ret);
+			break;
+		}
+
+		if (e_cpos == 0)
+			break;
+
+		name_hash = e_cpos - 1;
+	}
+
+out:
+	return ret;
+}
+
+/*
+ * 'trusted' attributes support
+ */
+
+#define XATTR_TRUSTED_PREFIX "trusted."
+
+static size_t ocfs2_xattr_trusted_list(struct inode *inode, char *list,
+				       size_t list_size, const char *name,
+				       size_t name_len)
+{
+	const size_t prefix_len = sizeof(XATTR_TRUSTED_PREFIX) - 1;
+	const size_t total_len = prefix_len + name_len + 1;
+
+	if (list && total_len <= list_size) {
+		memcpy(list, XATTR_TRUSTED_PREFIX, prefix_len);
+		memcpy(list + prefix_len, name, name_len);
+		list[prefix_len + name_len] = '\0';
+	}
+	return total_len;
+}
+
+static int ocfs2_xattr_trusted_get(struct inode *inode, const char *name,
+				   void *buffer, size_t size)
+{
+	if (strcmp(name, "") == 0)
+		return -EINVAL;
+	return ocfs2_xattr_get(inode, OCFS2_XATTR_INDEX_TRUSTED, name,
+			       buffer, size);
+}
+
+static int ocfs2_xattr_trusted_set(struct inode *inode, const char *name,
+				   const void *value, size_t size, int flags)
+{
+	if (strcmp(name, "") == 0)
+		return -EINVAL;
+
+	return ocfs2_xattr_set(inode, OCFS2_XATTR_INDEX_TRUSTED, name, value,
+			       size, flags);
+}
+
+struct xattr_handler ocfs2_xattr_trusted_handler = {
+	.prefix	= XATTR_TRUSTED_PREFIX,
+	.list	= ocfs2_xattr_trusted_list,
+	.get	= ocfs2_xattr_trusted_get,
+	.set	= ocfs2_xattr_trusted_set,
+};
+
+
+/*
+ * 'user' attributes support
+ */
+
+#define XATTR_USER_PREFIX "user."
+
+static size_t ocfs2_xattr_user_list(struct inode *inode, char *list,
+				    size_t list_size, const char *name,
+				    size_t name_len)
+{
+	const size_t prefix_len = sizeof(XATTR_USER_PREFIX) - 1;
+	const size_t total_len = prefix_len + name_len + 1;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+	if (osb->s_mount_opt & OCFS2_MOUNT_NOUSERXATTR)
+		return 0;
+
+	if (list && total_len <= list_size) {
+		memcpy(list, XATTR_USER_PREFIX, prefix_len);
+		memcpy(list + prefix_len, name, name_len);
+		list[prefix_len + name_len] = '\0';
+	}
+	return total_len;
+}
+
+static int ocfs2_xattr_user_get(struct inode *inode, const char *name,
+				void *buffer, size_t size)
+{
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+	if (strcmp(name, "") == 0)
+		return -EINVAL;
+	if (osb->s_mount_opt & OCFS2_MOUNT_NOUSERXATTR)
+		return -EOPNOTSUPP;
+	return ocfs2_xattr_get(inode, OCFS2_XATTR_INDEX_USER, name,
+			       buffer, size);
+}
+
+static int ocfs2_xattr_user_set(struct inode *inode, const char *name,
+				const void *value, size_t size, int flags)
+{
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+	if (strcmp(name, "") == 0)
+		return -EINVAL;
+	if (osb->s_mount_opt & OCFS2_MOUNT_NOUSERXATTR)
+		return -EOPNOTSUPP;
+
+	return ocfs2_xattr_set(inode, OCFS2_XATTR_INDEX_USER, name, value,
+			       size, flags);
+}
+
+struct xattr_handler ocfs2_xattr_user_handler = {
+	.prefix	= XATTR_USER_PREFIX,
+	.list	= ocfs2_xattr_user_list,
+	.get	= ocfs2_xattr_user_get,
+	.set	= ocfs2_xattr_user_set,
+};
diff --git a/fs/ocfs2/xattr.h b/fs/ocfs2/xattr.h
new file mode 100644
index 0000000..c25c7c6
--- /dev/null
+++ b/fs/ocfs2/xattr.h
@@ -0,0 +1,68 @@
+/* -*- mode: c; c-basic-offset: 8; -*-
+ * vim: noexpandtab sw=8 ts=8 sts=0:
+ *
+ * xattr.h
+ *
+ * Function prototypes
+ *
+ * Copyright (C) 2008 Oracle.  All rights reserved.
+ *
+ * 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 021110-1307, USA.
+ */
+
+#ifndef OCFS2_XATTR_H
+#define OCFS2_XATTR_H
+
+#include <linux/init.h>
+#include <linux/xattr.h>
+
+enum ocfs2_xattr_type {
+	OCFS2_XATTR_INDEX_USER = 1,
+	OCFS2_XATTR_INDEX_POSIX_ACL_ACCESS,
+	OCFS2_XATTR_INDEX_POSIX_ACL_DEFAULT,
+	OCFS2_XATTR_INDEX_TRUSTED,
+	OCFS2_XATTR_INDEX_SECURITY,
+	OCFS2_XATTR_MAX
+};
+
+extern struct xattr_handler ocfs2_xattr_user_handler;
+extern struct xattr_handler ocfs2_xattr_trusted_handler;
+
+extern ssize_t ocfs2_listxattr(struct dentry *, char *, size_t);
+extern int ocfs2_xattr_get(struct inode *, int, const char *, void *, size_t);
+extern int ocfs2_xattr_set(struct inode *, int, const char *, const void *,
+			   size_t, int);
+extern int ocfs2_xattr_remove(struct inode *inode, struct buffer_head *di_bh);
+extern struct xattr_handler *ocfs2_xattr_handlers[];
+
+static inline u16 ocfs2_xattr_buckets_per_cluster(struct ocfs2_super *osb)
+{
+	return (1 << osb->s_clustersize_bits) / OCFS2_XATTR_BUCKET_SIZE;
+}
+
+static inline u16 ocfs2_blocks_per_xattr_bucket(struct super_block *sb)
+{
+	return OCFS2_XATTR_BUCKET_SIZE / (1 << sb->s_blocksize_bits);
+}
+
+static inline u16 ocfs2_xattr_max_xe_in_bucket(struct super_block *sb)
+{
+	u16 len = sb->s_blocksize -
+		 offsetof(struct ocfs2_xattr_header, xh_entries);
+
+	return len / sizeof(struct ocfs2_xattr_entry);
+}
+#endif /* OCFS2_XATTR_H */
diff --git a/fs/proc/proc_misc.c b/fs/proc/proc_misc.c
index 66c1ab8..b675a49 100644
--- a/fs/proc/proc_misc.c
+++ b/fs/proc/proc_misc.c
@@ -683,6 +683,7 @@
 	return proc_calc_metrics(page, start, off, count, eof, len);
 }
 
+#ifdef CONFIG_FILE_LOCKING
 static int locks_open(struct inode *inode, struct file *filp)
 {
 	return seq_open(filp, &locks_seq_operations);
@@ -694,6 +695,7 @@
 	.llseek		= seq_lseek,
 	.release	= seq_release,
 };
+#endif /* CONFIG_FILE_LOCKING */
 
 static int execdomains_read_proc(char *page, char **start, off_t off,
 				 int count, int *eof, void *data)
@@ -887,7 +889,9 @@
 #ifdef CONFIG_PRINTK
 	proc_create("kmsg", S_IRUSR, NULL, &proc_kmsg_operations);
 #endif
+#ifdef CONFIG_FILE_LOCKING
 	proc_create("locks", 0, NULL, &proc_locks_operations);
+#endif
 	proc_create("devices", 0, NULL, &proc_devinfo_operations);
 	proc_create("cpuinfo", 0, NULL, &proc_cpuinfo_operations);
 #ifdef CONFIG_BLOCK
diff --git a/include/asm-m68k/atarihw.h b/include/asm-m68k/atarihw.h
index ecf007d..1412b4a 100644
--- a/include/asm-m68k/atarihw.h
+++ b/include/asm-m68k/atarihw.h
@@ -39,7 +39,6 @@
 #define MACH_IS_TT	((atari_mch_cookie >> 16) == ATARI_MCH_TT)
 #define MACH_IS_FALCON	((atari_mch_cookie >> 16) == ATARI_MCH_FALCON)
 #define MACH_IS_MEDUSA	(atari_mch_type == ATARI_MACH_MEDUSA)
-#define MACH_IS_HADES	(atari_mch_type == ATARI_MACH_HADES)
 #define MACH_IS_AB40	(atari_mch_type == ATARI_MACH_AB40)
 
 /* values for atari_switches */
diff --git a/include/asm-m68k/dma-mapping.h b/include/asm-m68k/dma-mapping.h
index 91f7944..26f5054 100644
--- a/include/asm-m68k/dma-mapping.h
+++ b/include/asm-m68k/dma-mapping.h
@@ -74,6 +74,14 @@
 extern void dma_sync_sg_for_device(struct device *, struct scatterlist *, int,
 				   enum dma_data_direction);
 
+static inline void dma_sync_single_range_for_device(struct device *dev,
+		dma_addr_t dma_handle, unsigned long offset, size_t size,
+		enum dma_data_direction direction)
+{
+	/* just sync everything for now */
+	dma_sync_single_for_device(dev, dma_handle, offset + size, direction);
+}
+
 static inline void dma_sync_single_for_cpu(struct device *dev, dma_addr_t handle,
 					   size_t size, enum dma_data_direction dir)
 {
@@ -84,6 +92,14 @@
 {
 }
 
+static inline void dma_sync_single_range_for_cpu(struct device *dev,
+		dma_addr_t dma_handle, unsigned long offset, size_t size,
+		enum dma_data_direction direction)
+{
+	/* just sync everything for now */
+	dma_sync_single_for_cpu(dev, dma_handle, offset + size, direction);
+}
+
 static inline int dma_mapping_error(struct device *dev, dma_addr_t handle)
 {
 	return 0;
diff --git a/include/asm-m68k/dma.h b/include/asm-m68k/dma.h
index d0c9e61..4240fbc 100644
--- a/include/asm-m68k/dma.h
+++ b/include/asm-m68k/dma.h
@@ -11,10 +11,6 @@
 extern int request_dma(unsigned int dmanr, const char * device_id);	/* reserve a DMA channel */
 extern void free_dma(unsigned int dmanr);	/* release it again */
 
-#ifdef CONFIG_PCI
-extern int isa_dma_bridge_buggy;
-#else
 #define isa_dma_bridge_buggy    (0)
-#endif
 
 #endif /* _M68K_DMA_H */
diff --git a/include/asm-m68k/entry.h b/include/asm-m68k/entry.h
index f8f6b18..5202f5a 100644
--- a/include/asm-m68k/entry.h
+++ b/include/asm-m68k/entry.h
@@ -31,7 +31,7 @@
  */
 
 /* the following macro is used when enabling interrupts */
-#if defined(MACH_ATARI_ONLY) && !defined(CONFIG_HADES)
+#if defined(MACH_ATARI_ONLY)
 	/* block out HSYNC on the atari */
 #define ALLOWINT	(~0x400)
 #define	MAX_NOINT_IPL	3
diff --git a/include/asm-m68k/io.h b/include/asm-m68k/io.h
index 657187f..9e673e3 100644
--- a/include/asm-m68k/io.h
+++ b/include/asm-m68k/io.h
@@ -7,15 +7,12 @@
  *            - added skeleton for GG-II and Amiga PCMCIA
  * 2/3/01 RZ: - moved a few more defs into raw_io.h
  *
- * inX/outX/readX/writeX should not be used by any driver unless it does
- * ISA or PCI access. Other drivers should use function defined in raw_io.h
+ * inX/outX should not be used by any driver unless it does
+ * ISA access. Other drivers should use function defined in raw_io.h
  * or define its own macros on top of these.
  *
- *    inX(),outX()              are for PCI and ISA I/O
- *    readX(),writeX()          are for PCI memory
+ *    inX(),outX()              are for ISA I/O
  *    isa_readX(),isa_writeX()  are for ISA memory
- *
- * moved mem{cpy,set}_*io inside CONFIG_PCI
  */
 
 #ifndef _IO_H
@@ -256,10 +253,7 @@
        (ISA_SEX ? raw_outsl(isa_itl(port), (u32 *)(buf), (nr)) :  \
                   raw_outsw_swapw(isa_itw(port), (u16 *)(buf), (nr)<<1))
 
-#endif  /* CONFIG_ISA */
 
-
-#if defined(CONFIG_ISA) && !defined(CONFIG_PCI)
 #define inb     isa_inb
 #define inb_p   isa_inb_p
 #define outb    isa_outb
@@ -282,55 +276,9 @@
 #define readw   isa_readw
 #define writeb  isa_writeb
 #define writew  isa_writew
-#endif /* CONFIG_ISA */
 
-#if defined(CONFIG_PCI)
+#else  /* CONFIG_ISA */
 
-#define readl(addr)      in_le32(addr)
-#define writel(val,addr) out_le32((addr),(val))
-
-/* those can be defined for both ISA and PCI - it won't work though */
-#define readb(addr)       in_8(addr)
-#define readw(addr)       in_le16(addr)
-#define writeb(val,addr)  out_8((addr),(val))
-#define writew(val,addr)  out_le16((addr),(val))
-
-#define readb_relaxed(addr) readb(addr)
-#define readw_relaxed(addr) readw(addr)
-#define readl_relaxed(addr) readl(addr)
-
-#ifndef CONFIG_ISA
-#define inb(port)      in_8(port)
-#define outb(val,port) out_8((port),(val))
-#define inw(port)      in_le16(port)
-#define outw(val,port) out_le16((port),(val))
-#define inl(port)      in_le32(port)
-#define outl(val,port) out_le32((port),(val))
-
-#else
-/*
- * kernel with both ISA and PCI compiled in, those have
- * conflicting defs for in/out. Simply consider port < 1024
- * ISA and everything else PCI. read,write not defined
- * in this case
- */
-#define inb(port) ((port)<1024 ? isa_inb(port) : in_8(port))
-#define inb_p(port) ((port)<1024 ? isa_inb_p(port) : in_8(port))
-#define inw(port) ((port)<1024 ? isa_inw(port) : in_le16(port))
-#define inw_p(port) ((port)<1024 ? isa_inw_p(port) : in_le16(port))
-#define inl(port) ((port)<1024 ? isa_inl(port) : in_le32(port))
-#define inl_p(port) ((port)<1024 ? isa_inl_p(port) : in_le32(port))
-
-#define outb(val,port) ((port)<1024 ? isa_outb((val),(port)) : out_8((port),(val)))
-#define outb_p(val,port) ((port)<1024 ? isa_outb_p((val),(port)) : out_8((port),(val)))
-#define outw(val,port) ((port)<1024 ? isa_outw((val),(port)) : out_le16((port),(val)))
-#define outw_p(val,port) ((port)<1024 ? isa_outw_p((val),(port)) : out_le16((port),(val)))
-#define outl(val,port) ((port)<1024 ? isa_outl((val),(port)) : out_le32((port),(val)))
-#define outl_p(val,port) ((port)<1024 ? isa_outl_p((val),(port)) : out_le32((port),(val)))
-#endif
-#endif /* CONFIG_PCI */
-
-#if !defined(CONFIG_ISA) && !defined(CONFIG_PCI)
 /*
  * We need to define dummy functions for GENERIC_IOMAP support.
  */
@@ -357,11 +305,11 @@
 #define writeb(val,addr) out_8((addr),(val))
 #define readw(addr)      in_le16(addr)
 #define writew(val,addr) out_le16((addr),(val))
-#endif
-#if !defined(CONFIG_PCI)
+
+#endif /* CONFIG_ISA */
+
 #define readl(addr)      in_le32(addr)
 #define writel(val,addr) out_le32((addr),(val))
-#endif
 
 #define mmiowb()
 
diff --git a/include/asm-m68k/pci.h b/include/asm-m68k/pci.h
index 678cb0b..4ad0aea 100644
--- a/include/asm-m68k/pci.h
+++ b/include/asm-m68k/pci.h
@@ -1,52 +1,7 @@
 #ifndef _ASM_M68K_PCI_H
 #define _ASM_M68K_PCI_H
 
-/*
- * asm-m68k/pci_m68k.h - m68k specific PCI declarations.
- *
- * Written by Wout Klaren.
- */
-
-#include <asm/scatterlist.h>
-
-struct pci_ops;
-
-/*
- * Structure with hardware dependent information and functions of the
- * PCI bus.
- */
-
-struct pci_bus_info
-{
-	/*
-	 * Resources of the PCI bus.
-	 */
-
-	struct resource mem_space;
-	struct resource io_space;
-
-	/*
-	 * System dependent functions.
-	 */
-
-	struct pci_ops *m68k_pci_ops;
-
-	void (*fixup)(int pci_modify);
-	void (*conf_device)(struct pci_dev *dev);
-};
-
-#define pcibios_assign_all_busses()	0
-#define pcibios_scan_all_fns(a, b)	0
-
-static inline void pcibios_set_master(struct pci_dev *dev)
-{
-	/* No special bus mastering setup handling */
-}
-
-static inline void pcibios_penalize_isa_irq(int irq, int active)
-{
-	/* We don't do dynamic PCI IRQ allocation */
-}
+#include <asm-generic/pci-dma-compat.h>
 
 /* The PCI address space does equal the physical memory
  * address space.  The networking and block device layers use
diff --git a/include/asm-m68k/virtconvert.h b/include/asm-m68k/virtconvert.h
index dea32fb..22ab05c 100644
--- a/include/asm-m68k/virtconvert.h
+++ b/include/asm-m68k/virtconvert.h
@@ -40,15 +40,9 @@
 
 /*
  * IO bus memory addresses are 1:1 with the physical address,
- * except on the PCI bus of the Hades.
  */
-#ifdef CONFIG_HADES
-#define virt_to_bus(a) (virt_to_phys(a) + (MACH_IS_HADES ? 0x80000000 : 0))
-#define bus_to_virt(a) (phys_to_virt((a) - (MACH_IS_HADES ? 0x80000000 : 0)))
-#else
 #define virt_to_bus virt_to_phys
 #define bus_to_virt phys_to_virt
-#endif
 
 #endif
 #endif
diff --git a/include/linux/console_struct.h b/include/linux/console_struct.h
index b03f80a..d71f7c0 100644
--- a/include/linux/console_struct.h
+++ b/include/linux/console_struct.h
@@ -53,7 +53,6 @@
 	unsigned short	vc_hi_font_mask;	/* [#] Attribute set for upper 256 chars of font or 0 if not supported */
 	struct console_font vc_font;		/* Current VC font set */
 	unsigned short	vc_video_erase_char;	/* Background erase character */
-	unsigned short	vc_scrl_erase_char;	/* Erase character for scroll */
 	/* VT terminal data */
 	unsigned int	vc_state;		/* Escape sequence parser state */
 	unsigned int	vc_npar,vc_par[NPAR];	/* Parameters of current escape sequence */
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 44e3cb2..a6a625b 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -947,6 +947,14 @@
 	int (*fl_change)(struct file_lock **, int);
 };
 
+struct lock_manager {
+	struct list_head list;
+};
+
+void locks_start_grace(struct lock_manager *);
+void locks_end_grace(struct lock_manager *);
+int locks_in_grace(void);
+
 /* that will die - we need it for nfs_lock_info */
 #include <linux/nfs_fs_i.h>
 
@@ -988,6 +996,13 @@
 
 #include <linux/fcntl.h>
 
+extern void send_sigio(struct fown_struct *fown, int fd, int band);
+
+/* fs/sync.c */
+extern int do_sync_mapping_range(struct address_space *mapping, loff_t offset,
+			loff_t endbyte, unsigned int flags);
+
+#ifdef CONFIG_FILE_LOCKING
 extern int fcntl_getlk(struct file *, struct flock __user *);
 extern int fcntl_setlk(unsigned int, struct file *, unsigned int,
 			struct flock __user *);
@@ -998,14 +1013,9 @@
 			struct flock64 __user *);
 #endif
 
-extern void send_sigio(struct fown_struct *fown, int fd, int band);
 extern int fcntl_setlease(unsigned int fd, struct file *filp, long arg);
 extern int fcntl_getlease(struct file *filp);
 
-/* fs/sync.c */
-extern int do_sync_mapping_range(struct address_space *mapping, loff_t offset,
-			loff_t endbyte, unsigned int flags);
-
 /* fs/locks.c */
 extern void locks_init_lock(struct file_lock *);
 extern void locks_copy_lock(struct file_lock *, struct file_lock *);
@@ -1028,6 +1038,37 @@
 extern int lock_may_read(struct inode *, loff_t start, unsigned long count);
 extern int lock_may_write(struct inode *, loff_t start, unsigned long count);
 extern struct seq_operations locks_seq_operations;
+#else /* !CONFIG_FILE_LOCKING */
+#define fcntl_getlk(a, b) ({ -EINVAL; })
+#define fcntl_setlk(a, b, c, d) ({ -EACCES; })
+#if BITS_PER_LONG == 32
+#define fcntl_getlk64(a, b) ({ -EINVAL; })
+#define fcntl_setlk64(a, b, c, d) ({ -EACCES; })
+#endif
+#define fcntl_setlease(a, b, c) ({ 0; })
+#define fcntl_getlease(a) ({ 0; })
+#define locks_init_lock(a) ({ })
+#define __locks_copy_lock(a, b) ({ })
+#define locks_copy_lock(a, b) ({ })
+#define locks_remove_posix(a, b) ({ })
+#define locks_remove_flock(a) ({ })
+#define posix_test_lock(a, b) ({ 0; })
+#define posix_lock_file(a, b, c) ({ -ENOLCK; })
+#define posix_lock_file_wait(a, b) ({ -ENOLCK; })
+#define posix_unblock_lock(a, b) (-ENOENT)
+#define vfs_test_lock(a, b) ({ 0; })
+#define vfs_lock_file(a, b, c, d) (-ENOLCK)
+#define vfs_cancel_lock(a, b) ({ 0; })
+#define flock_lock_file_wait(a, b) ({ -ENOLCK; })
+#define __break_lease(a, b) ({ 0; })
+#define lease_get_mtime(a, b) ({ })
+#define generic_setlease(a, b, c) ({ -EINVAL; })
+#define vfs_setlease(a, b, c) ({ -EINVAL; })
+#define lease_modify(a, b) ({ -EINVAL; })
+#define lock_may_read(a, b, c) ({ 1; })
+#define lock_may_write(a, b, c) ({ 1; })
+#endif /* !CONFIG_FILE_LOCKING */
+
 
 struct fasync_struct {
 	int	magic;
@@ -1575,9 +1616,12 @@
 /* /sys/fs */
 extern struct kobject *fs_kobj;
 
+extern int rw_verify_area(int, struct file *, loff_t *, size_t);
+
 #define FLOCK_VERIFY_READ  1
 #define FLOCK_VERIFY_WRITE 2
 
+#ifdef CONFIG_FILE_LOCKING
 extern int locks_mandatory_locked(struct inode *);
 extern int locks_mandatory_area(int, struct inode *, struct file *, loff_t, size_t);
 
@@ -1608,8 +1652,6 @@
 	return 0;
 }
 
-extern int rw_verify_area(int, struct file *, loff_t *, size_t);
-
 static inline int locks_verify_truncate(struct inode *inode,
 				    struct file *filp,
 				    loff_t size)
@@ -1630,6 +1672,15 @@
 		return __break_lease(inode, mode);
 	return 0;
 }
+#else /* !CONFIG_FILE_LOCKING */
+#define locks_mandatory_locked(a) ({ 0; })
+#define locks_mandatory_area(a, b, c, d, e) ({ 0; })
+#define __mandatory_lock(a) ({ 0; })
+#define mandatory_lock(a) ({ 0; })
+#define locks_verify_locked(a) ({ 0; })
+#define locks_verify_truncate(a, b, c) ({ 0; })
+#define break_lease(a, b) ({ 0; })
+#endif /* CONFIG_FILE_LOCKING */
 
 /* fs/open.c */
 
diff --git a/include/linux/hid.h b/include/linux/hid.h
index ac4e678..f13bca2 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -67,6 +67,7 @@
 #include <linux/types.h>
 #include <linux/slab.h>
 #include <linux/list.h>
+#include <linux/mod_devicetable.h> /* hid_device_id */
 #include <linux/timer.h>
 #include <linux/workqueue.h>
 #include <linux/input.h>
@@ -246,6 +247,19 @@
 #define HID_FEATURE_REPORT	2
 
 /*
+ * HID connect requests
+ */
+
+#define HID_CONNECT_HIDINPUT		0x01
+#define HID_CONNECT_HIDINPUT_FORCE	0x02
+#define HID_CONNECT_HIDRAW		0x04
+#define HID_CONNECT_HIDDEV		0x08
+#define HID_CONNECT_HIDDEV_FORCE	0x10
+#define HID_CONNECT_FF			0x20
+#define HID_CONNECT_DEFAULT	(HID_CONNECT_HIDINPUT|HID_CONNECT_HIDRAW| \
+		HID_CONNECT_HIDDEV|HID_CONNECT_FF)
+
+/*
  * HID device quirks.
  */
 
@@ -256,48 +270,11 @@
 
 #define HID_QUIRK_INVERT			0x00000001
 #define HID_QUIRK_NOTOUCH			0x00000002
-#define HID_QUIRK_IGNORE			0x00000004
 #define HID_QUIRK_NOGET				0x00000008
-#define HID_QUIRK_HIDDEV			0x00000010
 #define HID_QUIRK_BADPAD			0x00000020
 #define HID_QUIRK_MULTI_INPUT			0x00000040
-#define HID_QUIRK_2WHEEL_MOUSE_HACK_7		0x00000080
-#define HID_QUIRK_2WHEEL_MOUSE_HACK_5		0x00000100
-#define HID_QUIRK_2WHEEL_MOUSE_HACK_ON		0x00000200
-#define HID_QUIRK_MIGHTYMOUSE			0x00000400
-#define HID_QUIRK_APPLE_HAS_FN			0x00000800
-#define HID_QUIRK_APPLE_FN_ON			0x00001000
-#define HID_QUIRK_INVERT_HWHEEL			0x00002000
-#define HID_QUIRK_APPLE_ISO_KEYBOARD		0x00004000
-#define HID_QUIRK_BAD_RELATIVE_KEYS		0x00008000
 #define HID_QUIRK_SKIP_OUTPUT_REPORTS		0x00010000
-#define HID_QUIRK_IGNORE_MOUSE			0x00020000
-#define HID_QUIRK_SONY_PS3_CONTROLLER		0x00040000
-#define HID_QUIRK_DUPLICATE_USAGES		0x00080000
-#define HID_QUIRK_RESET_LEDS			0x00100000
-#define HID_QUIRK_HIDINPUT			0x00200000
-#define HID_QUIRK_LOGITECH_IGNORE_DOUBLED_WHEEL	0x00400000
-#define HID_QUIRK_LOGITECH_EXPANDED_KEYMAP	0x00800000
-#define HID_QUIRK_IGNORE_HIDINPUT		0x01000000
-#define HID_QUIRK_2WHEEL_MOUSE_HACK_B8		0x02000000
-#define HID_QUIRK_HWHEEL_WHEEL_INVERT		0x04000000
-#define HID_QUIRK_MICROSOFT_KEYS		0x08000000
 #define HID_QUIRK_FULLSPEED_INTERVAL		0x10000000
-#define HID_QUIRK_APPLE_NUMLOCK_EMULATION	0x20000000
-
-/*
- * Separate quirks for runtime report descriptor fixup
- */
-
-#define HID_QUIRK_RDESC_CYMOTION		0x00000001
-#define HID_QUIRK_RDESC_LOGITECH		0x00000002
-#define HID_QUIRK_RDESC_SWAPPED_MIN_MAX		0x00000004
-#define HID_QUIRK_RDESC_PETALYNX		0x00000008
-#define HID_QUIRK_RDESC_MACBOOK_JIS		0x00000010
-#define HID_QUIRK_RDESC_BUTTON_CONSUMER		0x00000020
-#define HID_QUIRK_RDESC_SAMSUNG_REMOTE		0x00000040
-#define HID_QUIRK_RDESC_MICROSOFT_RECV_1028	0x00000080
-#define HID_QUIRK_RDESC_SUNPLUS_WDESKTOP	0x00000100
 
 /*
  * This is the global environment of the parser. This information is
@@ -411,12 +388,21 @@
 struct hid_control_fifo {
 	unsigned char dir;
 	struct hid_report *report;
+	char *raw_report;
+};
+
+struct hid_output_fifo {
+	struct hid_report *report;
+	char *raw_report;
 };
 
 #define HID_CLAIMED_INPUT	1
 #define HID_CLAIMED_HIDDEV	2
 #define HID_CLAIMED_HIDRAW	4
 
+#define HID_STAT_ADDED		1
+#define HID_STAT_PARSED		2
+
 #define HID_CTRL_RUNNING	1
 #define HID_OUT_RUNNING		2
 #define HID_IN_RUNNING		3
@@ -431,22 +417,28 @@
 	struct input_dev *input;
 };
 
+struct hid_driver;
+struct hid_ll_driver;
+
 struct hid_device {							/* device report descriptor */
-	 __u8 *rdesc;
+	__u8 *rdesc;
 	unsigned rsize;
 	struct hid_collection *collection;				/* List of HID collections */
 	unsigned collection_size;					/* Number of allocated hid_collections */
 	unsigned maxcollection;						/* Number of parsed collections */
 	unsigned maxapplication;					/* Number of applications */
-	unsigned short bus;                                             /* BUS ID */
-	unsigned short vendor;                                          /* Vendor ID */
-	unsigned short product;                                         /* Product ID */
-	unsigned version;						/* HID version */
+	__u16 bus;							/* BUS ID */
+	__u32 vendor;							/* Vendor ID */
+	__u32 product;							/* Product ID */
+	__u32 version;							/* HID version */
 	unsigned country;						/* HID country */
 	struct hid_report_enum report_enum[HID_REPORT_TYPES];
 
-	struct device *dev;						/* device */
+	struct device dev;						/* device */
+	struct hid_driver *driver;
+	struct hid_ll_driver *ll_driver;
 
+	unsigned int status;						/* see STAT flags above */
 	unsigned claimed;						/* Claimed by hidinput, hiddev? */
 	unsigned quirks;						/* Various quirks the device can pull on us */
 
@@ -462,26 +454,29 @@
 
 	void *driver_data;
 
-	__s32 delayed_value;						/* For A4 Tech mice hwheel quirk */
-
-	/* device-specific function pointers */
-	int (*hidinput_input_event) (struct input_dev *, unsigned int, unsigned int, int);
-	int (*hid_open) (struct hid_device *);
-	void (*hid_close) (struct hid_device *);
+	/* temporary hid_ff handling (until moved to the drivers) */
+	int (*ff_init)(struct hid_device *);
 
 	/* hiddev event handler */
+	int (*hiddev_connect)(struct hid_device *, unsigned int);
 	void (*hiddev_hid_event) (struct hid_device *, struct hid_field *field,
 				  struct hid_usage *, __s32);
 	void (*hiddev_report_event) (struct hid_device *, struct hid_report *);
 
 	/* handler for raw output data, used by hidraw */
 	int (*hid_output_raw_report) (struct hid_device *, __u8 *, size_t);
-#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
-	unsigned long apple_pressed_fn[BITS_TO_LONGS(KEY_CNT)];
-	unsigned long pb_pressed_numlock[BITS_TO_LONGS(KEY_CNT)];
-#endif
 };
 
+static inline void *hid_get_drvdata(struct hid_device *hdev)
+{
+	return dev_get_drvdata(&hdev->dev);
+}
+
+static inline void hid_set_drvdata(struct hid_device *hdev, void *data)
+{
+	dev_set_drvdata(&hdev->dev, data);
+}
+
 #define HID_GLOBAL_STACK_SIZE 4
 #define HID_COLLECTION_STACK_SIZE 4
 
@@ -510,6 +505,107 @@
 	struct hid_class_descriptor desc[1];
 } __attribute__ ((packed));
 
+#define HID_DEVICE(b, ven, prod) \
+	.bus = (b), \
+	.vendor = (ven), .product = (prod)
+
+#define HID_USB_DEVICE(ven, prod)	HID_DEVICE(BUS_USB, ven, prod)
+#define HID_BLUETOOTH_DEVICE(ven, prod)	HID_DEVICE(BUS_BLUETOOTH, ven, prod)
+
+#define HID_REPORT_ID(rep) \
+	.report_type = (rep)
+#define HID_USAGE_ID(uhid, utype, ucode) \
+	.usage_hid = (uhid), .usage_type = (utype), .usage_code = (ucode)
+/* we don't want to catch types and codes equal to 0 */
+#define HID_TERMINATOR		(HID_ANY_ID - 1)
+
+struct hid_report_id {
+	__u32 report_type;
+};
+struct hid_usage_id {
+	__u32 usage_hid;
+	__u32 usage_type;
+	__u32 usage_code;
+};
+
+/**
+ * struct hid_driver
+ * @name: driver name (e.g. "Footech_bar-wheel")
+ * @id_table: which devices is this driver for (must be non-NULL for probe
+ * 	      to be called)
+ * @probe: new device inserted
+ * @remove: device removed (NULL if not a hot-plug capable driver)
+ * @report_table: on which reports to call raw_event (NULL means all)
+ * @raw_event: if report in report_table, this hook is called (NULL means nop)
+ * @usage_table: on which events to call event (NULL means all)
+ * @event: if usage in usage_table, this hook is called (NULL means nop)
+ * @report_fixup: called before report descriptor parsing (NULL means nop)
+ * @input_mapping: invoked on input registering before mapping an usage
+ * @input_mapped: invoked on input registering after mapping an usage
+ *
+ * raw_event and event should return 0 on no action performed, 1 when no
+ * further processing should be done and negative on error
+ *
+ * input_mapping shall return a negative value to completely ignore this usage
+ * (e.g. doubled or invalid usage), zero to continue with parsing of this
+ * usage by generic code (no special handling needed) or positive to skip
+ * generic parsing (needed special handling which was done in the hook already)
+ * input_mapped shall return negative to inform the layer that this usage
+ * should not be considered for further processing or zero to notify that
+ * no processing was performed and should be done in a generic manner
+ * Both these functions may be NULL which means the same behavior as returning
+ * zero from them.
+ */
+struct hid_driver {
+	char *name;
+	const struct hid_device_id *id_table;
+
+	int (*probe)(struct hid_device *dev, const struct hid_device_id *id);
+	void (*remove)(struct hid_device *dev);
+
+	const struct hid_report_id *report_table;
+	int (*raw_event)(struct hid_device *hdev, struct hid_report *report,
+			u8 *data, int size);
+	const struct hid_usage_id *usage_table;
+	int (*event)(struct hid_device *hdev, struct hid_field *field,
+			struct hid_usage *usage, __s32 value);
+
+	void (*report_fixup)(struct hid_device *hdev, __u8 *buf,
+			unsigned int size);
+
+	int (*input_mapping)(struct hid_device *hdev,
+			struct hid_input *hidinput, struct hid_field *field,
+			struct hid_usage *usage, unsigned long **bit, int *max);
+	int (*input_mapped)(struct hid_device *hdev,
+			struct hid_input *hidinput, struct hid_field *field,
+			struct hid_usage *usage, unsigned long **bit, int *max);
+/* private: */
+	struct device_driver driver;
+};
+
+/**
+ * hid_ll_driver - low level driver callbacks
+ * @start: called on probe to start the device
+ * @stop: called on remove
+ * @open: called by input layer on open
+ * @close: called by input layer on close
+ * @hidinput_input_event: event input event (e.g. ff or leds)
+ * @parse: this method is called only once to parse the device data,
+ *	   shouldn't allocate anything to not leak memory
+ */
+struct hid_ll_driver {
+	int (*start)(struct hid_device *hdev);
+	void (*stop)(struct hid_device *hdev);
+
+	int (*open)(struct hid_device *hdev);
+	void (*close)(struct hid_device *hdev);
+
+	int (*hidinput_input_event) (struct input_dev *idev, unsigned int type,
+			unsigned int code, int value);
+
+	int (*parse)(struct hid_device *hdev);
+};
+
 /* Applications from HID Usage Tables 4/8/99 Version 1.1 */
 /* We ignore a few input applications that are not widely used */
 #define IS_INPUT_APPLICATION(a) (((a >= 0x00010000) && (a <= 0x00010008)) || (a == 0x00010080) || (a == 0x000c0001) || (a == 0x000d0002))
@@ -520,43 +616,157 @@
 extern int hid_debug;
 #endif
 
+extern int hid_add_device(struct hid_device *);
+extern void hid_destroy_device(struct hid_device *);
+
+extern int __must_check __hid_register_driver(struct hid_driver *,
+		struct module *, const char *mod_name);
+static inline int __must_check hid_register_driver(struct hid_driver *driver)
+{
+	return __hid_register_driver(driver, THIS_MODULE, KBUILD_MODNAME);
+}
+extern void hid_unregister_driver(struct hid_driver *);
+
 extern void hidinput_hid_event(struct hid_device *, struct hid_field *, struct hid_usage *, __s32);
 extern void hidinput_report_event(struct hid_device *hid, struct hid_report *report);
-extern int hidinput_connect(struct hid_device *);
+extern int hidinput_connect(struct hid_device *hid, unsigned int force);
 extern void hidinput_disconnect(struct hid_device *);
 
 int hid_set_field(struct hid_field *, unsigned, __s32);
 int hid_input_report(struct hid_device *, int type, u8 *, int, int);
 int hidinput_find_field(struct hid_device *hid, unsigned int type, unsigned int code, struct hid_field **field);
-int hidinput_mapping_quirks(struct hid_usage *, struct input_dev *, unsigned long **, int *);
-int hidinput_event_quirks(struct hid_device *, struct hid_field *, struct hid_usage *, __s32);
-int hidinput_apple_event(struct hid_device *, struct input_dev *, struct hid_usage *, __s32);
 void hid_output_report(struct hid_report *report, __u8 *data);
-void hid_free_device(struct hid_device *device);
-struct hid_device *hid_parse_report(__u8 *start, unsigned size);
+struct hid_device *hid_allocate_device(void);
+int hid_parse_report(struct hid_device *hid, __u8 *start, unsigned size);
+int hid_connect(struct hid_device *hid, unsigned int connect_mask);
+
+/**
+ * hid_map_usage - map usage input bits
+ *
+ * @hidinput: hidinput which we are interested in
+ * @usage: usage to fill in
+ * @bit: pointer to input->{}bit (out parameter)
+ * @max: maximal valid usage->code to consider later (out parameter)
+ * @type: input event type (EV_KEY, EV_REL, ...)
+ * @c: code which corresponds to this usage and type
+ */
+static inline void hid_map_usage(struct hid_input *hidinput,
+		struct hid_usage *usage, unsigned long **bit, int *max,
+		__u8 type, __u16 c)
+{
+	struct input_dev *input = hidinput->input;
+
+	usage->type = type;
+	usage->code = c;
+
+	switch (type) {
+	case EV_ABS:
+		*bit = input->absbit;
+		*max = ABS_MAX;
+		break;
+	case EV_REL:
+		*bit = input->relbit;
+		*max = REL_MAX;
+		break;
+	case EV_KEY:
+		*bit = input->keybit;
+		*max = KEY_MAX;
+		break;
+	case EV_LED:
+		*bit = input->ledbit;
+		*max = LED_MAX;
+		break;
+	}
+}
+
+/**
+ * hid_map_usage_clear - map usage input bits and clear the input bit
+ *
+ * The same as hid_map_usage, except the @c bit is also cleared in supported
+ * bits (@bit).
+ */
+static inline void hid_map_usage_clear(struct hid_input *hidinput,
+		struct hid_usage *usage, unsigned long **bit, int *max,
+		__u8 type, __u16 c)
+{
+	hid_map_usage(hidinput, usage, bit, max, type, c);
+	clear_bit(c, *bit);
+}
+
+/**
+ * hid_parse - parse HW reports
+ *
+ * @hdev: hid device
+ *
+ * Call this from probe after you set up the device (if needed). Your
+ * report_fixup will be called (if non-NULL) after reading raw report from
+ * device before passing it to hid layer for real parsing.
+ */
+static inline int __must_check hid_parse(struct hid_device *hdev)
+{
+	int ret;
+
+	if (hdev->status & HID_STAT_PARSED)
+		return 0;
+
+	ret = hdev->ll_driver->parse(hdev);
+	if (!ret)
+		hdev->status |= HID_STAT_PARSED;
+
+	return ret;
+}
+
+/**
+ * hid_hw_start - start underlaying HW
+ *
+ * @hdev: hid device
+ * @connect_mask: which outputs to connect, see HID_CONNECT_*
+ *
+ * Call this in probe function *after* hid_parse. This will setup HW buffers
+ * and start the device (if not deffered to device open). hid_hw_stop must be
+ * called if this was successfull.
+ */
+static inline int __must_check hid_hw_start(struct hid_device *hdev,
+		unsigned int connect_mask)
+{
+	int ret = hdev->ll_driver->start(hdev);
+	if (ret || !connect_mask)
+		return ret;
+	ret = hid_connect(hdev, connect_mask);
+	if (ret)
+		hdev->ll_driver->stop(hdev);
+	return ret;
+}
+
+/**
+ * hid_hw_stop - stop underlaying HW
+ *
+ * @hdev: hid device
+ *
+ * This is usually called from remove function or from probe when something
+ * failed and hid_hw_start was called already.
+ */
+static inline void hid_hw_stop(struct hid_device *hdev)
+{
+	hdev->ll_driver->stop(hdev);
+}
+
+void hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size,
+		int interrupt);
+
+extern int hid_generic_init(void);
+extern void hid_generic_exit(void);
 
 /* HID quirks API */
 u32 usbhid_lookup_quirk(const u16 idVendor, const u16 idProduct);
 int usbhid_quirks_init(char **quirks_param);
 void usbhid_quirks_exit(void);
-void usbhid_fixup_report_descriptor(const u16, const u16, char *, unsigned, char **);
+void usbhid_set_leds(struct hid_device *hid);
 
-#ifdef CONFIG_HID_FF
-int hid_ff_init(struct hid_device *hid);
-
-int hid_lgff_init(struct hid_device *hid);
-int hid_lg2ff_init(struct hid_device *hid);
-int hid_plff_init(struct hid_device *hid);
-int hid_tmff_init(struct hid_device *hid);
-int hid_zpff_init(struct hid_device *hid);
 #ifdef CONFIG_HID_PID
 int hid_pidff_init(struct hid_device *hid);
 #else
-static inline int hid_pidff_init(struct hid_device *hid) { return -ENODEV; }
-#endif
-
-#else
-static inline int hid_ff_init(struct hid_device *hid) { return -1; }
+#define hid_pidff_init NULL
 #endif
 
 #ifdef CONFIG_HID_DEBUG
@@ -572,10 +782,23 @@
 	return 0;
 }
 #define dbg_hid_line dbg_hid
-#endif
+#endif /* HID_DEBUG */
 
 #define err_hid(format, arg...) printk(KERN_ERR "%s: " format "\n" , \
 		__FILE__ , ## arg)
-#endif
+#endif /* HID_FF */
+
+#ifdef CONFIG_HID_COMPAT
+#define HID_COMPAT_LOAD_DRIVER(name)	\
+void hid_compat_##name(void) { }	\
+EXPORT_SYMBOL(hid_compat_##name)
+#else
+#define HID_COMPAT_LOAD_DRIVER(name)
+#endif /* HID_COMPAT */
+#define HID_COMPAT_CALL_DRIVER(name)	do {	\
+	extern void hid_compat_##name(void);	\
+	hid_compat_##name();			\
+} while (0)
+
 #endif
 
diff --git a/include/linux/hiddev.h b/include/linux/hiddev.h
index a416b90..c760ae0 100644
--- a/include/linux/hiddev.h
+++ b/include/linux/hiddev.h
@@ -182,26 +182,28 @@
 /* To traverse the input report descriptor info for a HID device, perform the 
  * following:
  *
- *  rinfo.report_type = HID_REPORT_TYPE_INPUT;
- *  rinfo.report_id = HID_REPORT_ID_FIRST;
- *  ret = ioctl(fd, HIDIOCGREPORTINFO, &rinfo);
+ * rinfo.report_type = HID_REPORT_TYPE_INPUT;
+ * rinfo.report_id = HID_REPORT_ID_FIRST;
+ * ret = ioctl(fd, HIDIOCGREPORTINFO, &rinfo);
  *
- *  while (ret >= 0) {
- *      for (i = 0; i < rinfo.num_fields; i++) { 
- *	    finfo.report_type = rinfo.report_type;
- *          finfo.report_id = rinfo.report_id;
- *          finfo.field_index = i;
- *          ioctl(fd, HIDIOCGFIELDINFO, &finfo);
- *          for (j = 0; j < finfo.maxusage; j++) {
- *              uref.field_index = i;
- *		uref.usage_index = j;
- *		ioctl(fd, HIDIOCGUCODE, &uref);
- *		ioctl(fd, HIDIOCGUSAGE, &uref);
- *          }
- *	}
- *	rinfo.report_id |= HID_REPORT_ID_NEXT;
- *	ret = ioctl(fd, HIDIOCGREPORTINFO, &rinfo);
- *  }
+ * while (ret >= 0) {
+ * 	for (i = 0; i < rinfo.num_fields; i++) {
+ * 		finfo.report_type = rinfo.report_type;
+ * 		finfo.report_id = rinfo.report_id;
+ * 		finfo.field_index = i;
+ * 		ioctl(fd, HIDIOCGFIELDINFO, &finfo);
+ * 		for (j = 0; j < finfo.maxusage; j++) {
+ * 			uref.report_type = rinfo.report_type;
+ * 			uref.report_id = rinfo.report_id;
+ * 			uref.field_index = i;
+ * 			uref.usage_index = j;
+ * 			ioctl(fd, HIDIOCGUCODE, &uref);
+ * 			ioctl(fd, HIDIOCGUSAGE, &uref);
+ * 		}
+ * 	}
+ * 	rinfo.report_id |= HID_REPORT_ID_NEXT;
+ * 	ret = ioctl(fd, HIDIOCGREPORTINFO, &rinfo);
+ * }
  */
 
 
@@ -217,7 +219,7 @@
 struct hid_report;
 
 #ifdef CONFIG_USB_HIDDEV
-int hiddev_connect(struct hid_device *);
+int hiddev_connect(struct hid_device *hid, unsigned int force);
 void hiddev_disconnect(struct hid_device *);
 void hiddev_hid_event(struct hid_device *hid, struct hid_field *field,
 		      struct hid_usage *usage, __s32 value);
@@ -225,7 +227,9 @@
 int __init hiddev_init(void);
 void hiddev_exit(void);
 #else
-static inline int hiddev_connect(struct hid_device *hid) { return -1; }
+static inline int hiddev_connect(struct hid_device *hid,
+		unsigned int force)
+{ return -1; }
 static inline void hiddev_disconnect(struct hid_device *hid) { }
 static inline void hiddev_hid_event(struct hid_device *hid, struct hid_field *field,
 		      struct hid_usage *usage, __s32 value) { }
diff --git a/include/linux/if_ether.h b/include/linux/if_ether.h
index bf1a53b..7f3c735 100644
--- a/include/linux/if_ether.h
+++ b/include/linux/if_ether.h
@@ -9,7 +9,7 @@
  *
  * Author:	Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
  *		Donald Becker, <becker@super.org>
- *		Alan Cox, <alan@redhat.com>
+ *		Alan Cox, <alan@lxorguk.ukuu.org.uk>
  *		Steve Whitehouse, <gw7rrm@eeshack3.swan.ac.uk>
  *
  *		This program is free software; you can redistribute it and/or
diff --git a/include/linux/if_fddi.h b/include/linux/if_fddi.h
index ae77dae..45de104 100644
--- a/include/linux/if_fddi.h
+++ b/include/linux/if_fddi.h
@@ -12,7 +12,7 @@
  *		if_fddi.h is based on previous if_ether.h and if_tr.h work by
  *			Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
  *			Donald Becker, <becker@super.org>
- *			Alan Cox, <alan@redhat.com>
+ *			Alan Cox, <alan@lxorguk.ukuu.org.uk>
  *			Steve Whitehouse, <gw7rrm@eeshack3.swan.ac.uk>
  *			Peter De Schrijver, <stud11@cc4.kuleuven.ac.be>
  *
diff --git a/include/linux/if_hippi.h b/include/linux/if_hippi.h
index 94d31ca..f0f2351 100644
--- a/include/linux/if_hippi.h
+++ b/include/linux/if_hippi.h
@@ -9,7 +9,7 @@
  *
  * Author:	Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
  *		Donald Becker, <becker@super.org>
- *		Alan Cox, <alan@redhat.com>
+ *		Alan Cox, <alan@lxorguk.ukuu.org.uk>
  *		Steve Whitehouse, <gw7rrm@eeshack3.swan.ac.uk>
  *		Jes Sorensen, <Jes.Sorensen@cern.ch>
  *
diff --git a/include/linux/igmp.h b/include/linux/igmp.h
index 7bb3c09..f734a0b 100644
--- a/include/linux/igmp.h
+++ b/include/linux/igmp.h
@@ -2,7 +2,7 @@
  *	Linux NET3:	Internet Group Management Protocol  [IGMP]
  *
  *	Authors:
- *		Alan Cox <Alan.Cox@linux.org>
+ *		Alan Cox <alan@lxorguk.ukuu.org.uk>
  *
  *	Extended to talk the BSD extended IGMP protocol of mrouted 3.6
  *
diff --git a/include/linux/lockd/bind.h b/include/linux/lockd/bind.h
index 3d25bcd..e5872dc 100644
--- a/include/linux/lockd/bind.h
+++ b/include/linux/lockd/bind.h
@@ -27,7 +27,6 @@
 						struct nfs_fh *,
 						struct file **);
 	void			(*fclose)(struct file *);
-	unsigned long		(*get_grace_period)(void);
 };
 
 extern struct nlmsvc_binding *	nlmsvc_ops;
@@ -53,15 +52,7 @@
 
 extern int	nlmclnt_proc(struct nlm_host *host, int cmd,
 					struct file_lock *fl);
-extern int	lockd_up(int proto);
+extern int	lockd_up(void);
 extern void	lockd_down(void);
 
-unsigned long get_nfs_grace_period(void);
-
-#ifdef CONFIG_NFSD_V4
-unsigned long get_nfs4_grace_period(void);
-#else
-static inline unsigned long get_nfs4_grace_period(void) {return 0;}
-#endif
-
 #endif /* LINUX_LOCKD_BIND_H */
diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h
index dbb87ab..b56d5aa 100644
--- a/include/linux/lockd/lockd.h
+++ b/include/linux/lockd/lockd.h
@@ -12,6 +12,8 @@
 #ifdef __KERNEL__
 
 #include <linux/in.h>
+#include <linux/in6.h>
+#include <net/ipv6.h>
 #include <linux/fs.h>
 #include <linux/kref.h>
 #include <linux/utsname.h>
@@ -38,8 +40,9 @@
  */
 struct nlm_host {
 	struct hlist_node	h_hash;		/* doubly linked list */
-	struct sockaddr_in	h_addr;		/* peer address */
-	struct sockaddr_in	h_saddr;	/* our address (optional) */
+	struct sockaddr_storage	h_addr;		/* peer address */
+	size_t			h_addrlen;
+	struct sockaddr_storage	h_srcaddr;	/* our address (optional) */
 	struct rpc_clnt	*	h_rpcclnt;	/* RPC client to talk to peer */
 	char *			h_name;		/* remote hostname */
 	u32			h_version;	/* interface version */
@@ -61,18 +64,56 @@
 	struct list_head	h_granted;	/* Locks in GRANTED state */
 	struct list_head	h_reclaim;	/* Locks in RECLAIM state */
 	struct nsm_handle *	h_nsmhandle;	/* NSM status handle */
+
+	char			h_addrbuf[48],	/* address eyecatchers */
+				h_srcaddrbuf[48];
 };
 
 struct nsm_handle {
 	struct list_head	sm_link;
 	atomic_t		sm_count;
 	char *			sm_name;
-	struct sockaddr_in	sm_addr;
+	struct sockaddr_storage	sm_addr;
+	size_t			sm_addrlen;
 	unsigned int		sm_monitored : 1,
 				sm_sticky : 1;	/* don't unmonitor */
+	char			sm_addrbuf[48];	/* address eyecatcher */
 };
 
 /*
+ * Rigorous type checking on sockaddr type conversions
+ */
+static inline struct sockaddr_in *nlm_addr_in(const struct nlm_host *host)
+{
+	return (struct sockaddr_in *)&host->h_addr;
+}
+
+static inline struct sockaddr *nlm_addr(const struct nlm_host *host)
+{
+	return (struct sockaddr *)&host->h_addr;
+}
+
+static inline struct sockaddr_in *nlm_srcaddr_in(const struct nlm_host *host)
+{
+	return (struct sockaddr_in *)&host->h_srcaddr;
+}
+
+static inline struct sockaddr *nlm_srcaddr(const struct nlm_host *host)
+{
+	return (struct sockaddr *)&host->h_srcaddr;
+}
+
+static inline struct sockaddr_in *nsm_addr_in(const struct nsm_handle *handle)
+{
+	return (struct sockaddr_in *)&handle->sm_addr;
+}
+
+static inline struct sockaddr *nsm_addr(const struct nsm_handle *handle)
+{
+	return (struct sockaddr *)&handle->sm_addr;
+}
+
+/*
  * Map an fl_owner_t into a unique 32-bit "pid"
  */
 struct nlm_lockowner {
@@ -166,7 +207,8 @@
 struct nlm_wait * nlmclnt_prepare_block(struct nlm_host *host, struct file_lock *fl);
 void		  nlmclnt_finish_block(struct nlm_wait *block);
 int		  nlmclnt_block(struct nlm_wait *block, struct nlm_rqst *req, long timeout);
-__be32		  nlmclnt_grant(const struct sockaddr_in *addr, const struct nlm_lock *);
+__be32		  nlmclnt_grant(const struct sockaddr *addr,
+				const struct nlm_lock *lock);
 void		  nlmclnt_recovery(struct nlm_host *);
 int		  nlmclnt_reclaim(struct nlm_host *, struct file_lock *);
 void		  nlmclnt_next_cookie(struct nlm_cookie *);
@@ -174,12 +216,14 @@
 /*
  * Host cache
  */
-struct nlm_host  *nlmclnt_lookup_host(const struct sockaddr_in *sin,
-					int proto, u32 version,
+struct nlm_host  *nlmclnt_lookup_host(const struct sockaddr *sap,
+					const size_t salen,
+					const unsigned short protocol,
+					const u32 version,
+					const char *hostname);
+struct nlm_host  *nlmsvc_lookup_host(const struct svc_rqst *rqstp,
 					const char *hostname,
-					unsigned int hostname_len);
-struct nlm_host  *nlmsvc_lookup_host(struct svc_rqst *, const char *,
-					unsigned int);
+					const size_t hostname_len);
 struct rpc_clnt * nlm_bind_host(struct nlm_host *);
 void		  nlm_rebind_host(struct nlm_host *);
 struct nlm_host * nlm_get_host(struct nlm_host *);
@@ -201,7 +245,7 @@
  */
 __be32		  nlmsvc_lock(struct svc_rqst *, struct nlm_file *,
 			      struct nlm_host *, struct nlm_lock *, int,
-			      struct nlm_cookie *);
+			      struct nlm_cookie *, int);
 __be32		  nlmsvc_unlock(struct nlm_file *, struct nlm_lock *);
 __be32		  nlmsvc_testlock(struct svc_rqst *, struct nlm_file *,
 			struct nlm_host *, struct nlm_lock *,
@@ -233,15 +277,82 @@
 	return file->f_file->f_path.dentry->d_inode;
 }
 
-/*
- * Compare two host addresses (needs modifying for ipv6)
- */
-static inline int nlm_cmp_addr(const struct sockaddr_in *sin1,
-			       const struct sockaddr_in *sin2)
+static inline int __nlm_privileged_request4(const struct sockaddr *sap)
 {
+	const struct sockaddr_in *sin = (struct sockaddr_in *)sap;
+	return (sin->sin_addr.s_addr == htonl(INADDR_LOOPBACK)) &&
+			(ntohs(sin->sin_port) < 1024);
+}
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+static inline int __nlm_privileged_request6(const struct sockaddr *sap)
+{
+	const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
+	return (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LOOPBACK) &&
+			(ntohs(sin6->sin6_port) < 1024);
+}
+#else	/* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */
+static inline int __nlm_privileged_request6(const struct sockaddr *sap)
+{
+	return 0;
+}
+#endif	/* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */
+
+/*
+ * Ensure incoming requests are from local privileged callers.
+ *
+ * Return TRUE if sender is local and is connecting via a privileged port;
+ * otherwise return FALSE.
+ */
+static inline int nlm_privileged_requester(const struct svc_rqst *rqstp)
+{
+	const struct sockaddr *sap = svc_addr(rqstp);
+
+	switch (sap->sa_family) {
+	case AF_INET:
+		return __nlm_privileged_request4(sap);
+	case AF_INET6:
+		return __nlm_privileged_request6(sap);
+	default:
+		return 0;
+	}
+}
+
+static inline int __nlm_cmp_addr4(const struct sockaddr *sap1,
+				  const struct sockaddr *sap2)
+{
+	const struct sockaddr_in *sin1 = (const struct sockaddr_in *)sap1;
+	const struct sockaddr_in *sin2 = (const struct sockaddr_in *)sap2;
 	return sin1->sin_addr.s_addr == sin2->sin_addr.s_addr;
 }
 
+static inline int __nlm_cmp_addr6(const struct sockaddr *sap1,
+				  const struct sockaddr *sap2)
+{
+	const struct sockaddr_in6 *sin1 = (const struct sockaddr_in6 *)sap1;
+	const struct sockaddr_in6 *sin2 = (const struct sockaddr_in6 *)sap2;
+	return ipv6_addr_equal(&sin1->sin6_addr, &sin2->sin6_addr);
+}
+
+/*
+ * Compare two host addresses
+ *
+ * Return TRUE if the addresses are the same; otherwise FALSE.
+ */
+static inline int nlm_cmp_addr(const struct sockaddr *sap1,
+			       const struct sockaddr *sap2)
+{
+	if (sap1->sa_family == sap2->sa_family) {
+		switch (sap1->sa_family) {
+		case AF_INET:
+			return __nlm_cmp_addr4(sap1, sap2);
+		case AF_INET6:
+			return __nlm_cmp_addr6(sap1, sap2);
+		}
+	}
+	return 0;
+}
+
 /*
  * Compare two NLM locks.
  * When the second lock is of type F_UNLCK, this acts like a wildcard.
diff --git a/include/linux/lockd/xdr.h b/include/linux/lockd/xdr.h
index df18fa0..d6b3a80 100644
--- a/include/linux/lockd/xdr.h
+++ b/include/linux/lockd/xdr.h
@@ -81,8 +81,6 @@
 	unsigned int	len;
 	u32		state;
 	__be32		addr;
-	__be32		vers;
-	__be32		proto;
 };
 
 /*
diff --git a/include/linux/mfd/wm8350/audio.h b/include/linux/mfd/wm8350/audio.h
new file mode 100644
index 0000000..217bb22
--- /dev/null
+++ b/include/linux/mfd/wm8350/audio.h
@@ -0,0 +1,598 @@
+/*
+ * audio.h  --  Audio Driver for Wolfson WM8350 PMIC
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC
+ *
+ *  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.
+ *
+ */
+
+#ifndef __LINUX_MFD_WM8350_AUDIO_H_
+#define __LINUX_MFD_WM8350_AUDIO_H_
+
+#include <linux/platform_device.h>
+
+#define WM8350_CLOCK_CONTROL_1                  0x28
+#define WM8350_CLOCK_CONTROL_2                  0x29
+#define WM8350_FLL_CONTROL_1                    0x2A
+#define WM8350_FLL_CONTROL_2                    0x2B
+#define WM8350_FLL_CONTROL_3                    0x2C
+#define WM8350_FLL_CONTROL_4                    0x2D
+#define WM8350_DAC_CONTROL                      0x30
+#define WM8350_DAC_DIGITAL_VOLUME_L             0x32
+#define WM8350_DAC_DIGITAL_VOLUME_R             0x33
+#define WM8350_DAC_LR_RATE                      0x35
+#define WM8350_DAC_CLOCK_CONTROL                0x36
+#define WM8350_DAC_MUTE                         0x3A
+#define WM8350_DAC_MUTE_VOLUME                  0x3B
+#define WM8350_DAC_SIDE                         0x3C
+#define WM8350_ADC_CONTROL                      0x40
+#define WM8350_ADC_DIGITAL_VOLUME_L             0x42
+#define WM8350_ADC_DIGITAL_VOLUME_R             0x43
+#define WM8350_ADC_DIVIDER                      0x44
+#define WM8350_ADC_LR_RATE                      0x46
+#define WM8350_INPUT_CONTROL                    0x48
+#define WM8350_IN3_INPUT_CONTROL                0x49
+#define WM8350_MIC_BIAS_CONTROL                 0x4A
+#define WM8350_OUTPUT_CONTROL                   0x4C
+#define WM8350_JACK_DETECT                      0x4D
+#define WM8350_ANTI_POP_CONTROL                 0x4E
+#define WM8350_LEFT_INPUT_VOLUME                0x50
+#define WM8350_RIGHT_INPUT_VOLUME               0x51
+#define WM8350_LEFT_MIXER_CONTROL               0x58
+#define WM8350_RIGHT_MIXER_CONTROL              0x59
+#define WM8350_OUT3_MIXER_CONTROL               0x5C
+#define WM8350_OUT4_MIXER_CONTROL               0x5D
+#define WM8350_OUTPUT_LEFT_MIXER_VOLUME         0x60
+#define WM8350_OUTPUT_RIGHT_MIXER_VOLUME        0x61
+#define WM8350_INPUT_MIXER_VOLUME_L             0x62
+#define WM8350_INPUT_MIXER_VOLUME_R             0x63
+#define WM8350_INPUT_MIXER_VOLUME               0x64
+#define WM8350_LOUT1_VOLUME                     0x68
+#define WM8350_ROUT1_VOLUME                     0x69
+#define WM8350_LOUT2_VOLUME                     0x6A
+#define WM8350_ROUT2_VOLUME                     0x6B
+#define WM8350_BEEP_VOLUME                      0x6F
+#define WM8350_AI_FORMATING                     0x70
+#define WM8350_ADC_DAC_COMP                     0x71
+#define WM8350_AI_ADC_CONTROL                   0x72
+#define WM8350_AI_DAC_CONTROL                   0x73
+#define WM8350_AIF_TEST                         0x74
+#define WM8350_JACK_PIN_STATUS                  0xE7
+
+/* Bit values for R08 (0x08) */
+#define WM8350_CODEC_ISEL_1_5                   0	/* x1.5 */
+#define WM8350_CODEC_ISEL_1_0                   1	/* x1.0 */
+#define WM8350_CODEC_ISEL_0_75                  2	/* x0.75 */
+#define WM8350_CODEC_ISEL_0_5                   3	/* x0.5 */
+
+#define WM8350_VMID_OFF                         0
+#define WM8350_VMID_500K                        1
+#define WM8350_VMID_100K                        2
+#define WM8350_VMID_10K                         3
+
+/*
+ * R40 (0x28) - Clock Control 1
+ */
+#define WM8350_TOCLK_RATE                       0x4000
+#define WM8350_MCLK_SEL                         0x0800
+#define WM8350_MCLK_DIV_MASK                    0x0100
+#define WM8350_BCLK_DIV_MASK                    0x00F0
+#define WM8350_OPCLK_DIV_MASK                   0x0007
+
+/*
+ * R41 (0x29) - Clock Control 2
+ */
+#define WM8350_LRC_ADC_SEL                      0x8000
+#define WM8350_MCLK_DIR                         0x0001
+
+/*
+ * R42 (0x2A) - FLL Control 1
+ */
+#define WM8350_FLL_DITHER_WIDTH_MASK            0x3000
+#define WM8350_FLL_DITHER_HP                    0x0800
+#define WM8350_FLL_OUTDIV_MASK                  0x0700
+#define WM8350_FLL_RSP_RATE_MASK                0x00F0
+#define WM8350_FLL_RATE_MASK                    0x0007
+
+/*
+ * R43 (0x2B) - FLL Control 2
+ */
+#define WM8350_FLL_RATIO_MASK                   0xF800
+#define WM8350_FLL_N_MASK                       0x03FF
+
+/*
+ * R44 (0x2C) - FLL Control 3
+ */
+#define WM8350_FLL_K_MASK                       0xFFFF
+
+/*
+ * R45 (0x2D) - FLL Control 4
+ */
+#define WM8350_FLL_FRAC                         0x0020
+#define WM8350_FLL_SLOW_LOCK_REF                0x0010
+#define WM8350_FLL_CLK_SRC_MASK                 0x0003
+
+/*
+ * R48 (0x30) - DAC Control
+ */
+#define WM8350_DAC_MONO                         0x2000
+#define WM8350_AIF_LRCLKRATE                    0x1000
+#define WM8350_DEEMP_MASK                       0x0030
+#define WM8350_DACL_DATINV                      0x0002
+#define WM8350_DACR_DATINV                      0x0001
+
+/*
+ * R50 (0x32) - DAC Digital Volume L
+ */
+#define WM8350_DAC_VU                           0x0100
+#define WM8350_DACL_VOL_MASK                    0x00FF
+
+/*
+ * R51 (0x33) - DAC Digital Volume R
+ */
+#define WM8350_DAC_VU                           0x0100
+#define WM8350_DACR_VOL_MASK                    0x00FF
+
+/*
+ * R53 (0x35) - DAC LR Rate
+ */
+#define WM8350_DACLRC_ENA                       0x0800
+#define WM8350_DACLRC_RATE_MASK                 0x07FF
+
+/*
+ * R54 (0x36) - DAC Clock Control
+ */
+#define WM8350_DACCLK_POL                       0x0010
+#define WM8350_DAC_CLKDIV_MASK                  0x0007
+
+/*
+ * R58 (0x3A) - DAC Mute
+ */
+#define WM8350_DAC_MUTE_ENA                     0x4000
+
+/*
+ * R59 (0x3B) - DAC Mute Volume
+ */
+#define WM8350_DAC_MUTEMODE                     0x4000
+#define WM8350_DAC_MUTERATE                     0x2000
+#define WM8350_DAC_SB_FILT                      0x1000
+
+/*
+ * R60 (0x3C) - DAC Side
+ */
+#define WM8350_ADC_TO_DACL_MASK                 0x3000
+#define WM8350_ADC_TO_DACR_MASK                 0x0C00
+
+/*
+ * R64 (0x40) - ADC Control
+ */
+#define WM8350_ADC_HPF_CUT_MASK                 0x0300
+#define WM8350_ADCL_DATINV                      0x0002
+#define WM8350_ADCR_DATINV                      0x0001
+
+/*
+ * R66 (0x42) - ADC Digital Volume L
+ */
+#define WM8350_ADC_VU                           0x0100
+#define WM8350_ADCL_VOL_MASK                    0x00FF
+
+/*
+ * R67 (0x43) - ADC Digital Volume R
+ */
+#define WM8350_ADC_VU                           0x0100
+#define WM8350_ADCR_VOL_MASK                    0x00FF
+
+/*
+ * R68 (0x44) - ADC Divider
+ */
+#define WM8350_ADCL_DAC_SVOL_MASK               0x0F00
+#define WM8350_ADCR_DAC_SVOL_MASK               0x00F0
+#define WM8350_ADCCLK_POL                       0x0008
+#define WM8350_ADC_CLKDIV_MASK                  0x0007
+
+/*
+ * R70 (0x46) - ADC LR Rate
+ */
+#define WM8350_ADCLRC_ENA                       0x0800
+#define WM8350_ADCLRC_RATE_MASK                 0x07FF
+
+/*
+ * R72 (0x48) - Input Control
+ */
+#define WM8350_IN2R_ENA                         0x0400
+#define WM8350_IN1RN_ENA                        0x0200
+#define WM8350_IN1RP_ENA                        0x0100
+#define WM8350_IN2L_ENA                         0x0004
+#define WM8350_IN1LN_ENA                        0x0002
+#define WM8350_IN1LP_ENA                        0x0001
+
+/*
+ * R73 (0x49) - IN3 Input Control
+ */
+#define WM8350_IN3R_SHORT                       0x4000
+#define WM8350_IN3L_SHORT                       0x0040
+
+/*
+ * R74 (0x4A) - Mic Bias Control
+ */
+#define WM8350_MICBSEL                          0x4000
+#define WM8350_MCDTHR_MASK                      0x001C
+#define WM8350_MCDSCTHR_MASK                    0x0003
+
+/*
+ * R76 (0x4C) - Output Control
+ */
+#define WM8350_OUT4_VROI                        0x0800
+#define WM8350_OUT3_VROI                        0x0400
+#define WM8350_OUT2_VROI                        0x0200
+#define WM8350_OUT1_VROI                        0x0100
+#define WM8350_OUT2_FB                          0x0004
+#define WM8350_OUT1_FB                          0x0001
+
+/*
+ * R77 (0x4D) - Jack Detect
+ */
+#define WM8350_JDL_ENA                          0x8000
+#define WM8350_JDR_ENA                          0x4000
+
+/*
+ * R78 (0x4E) - Anti Pop Control
+ */
+#define WM8350_ANTI_POP_MASK                    0x0300
+#define WM8350_DIS_OP_LN4_MASK                  0x00C0
+#define WM8350_DIS_OP_LN3_MASK                  0x0030
+#define WM8350_DIS_OP_OUT2_MASK                 0x000C
+#define WM8350_DIS_OP_OUT1_MASK                 0x0003
+
+/*
+ * R80 (0x50) - Left Input Volume
+ */
+#define WM8350_INL_MUTE                         0x4000
+#define WM8350_INL_ZC                           0x2000
+#define WM8350_IN_VU                            0x0100
+#define WM8350_INL_VOL_MASK                     0x00FC
+
+/*
+ * R81 (0x51) - Right Input Volume
+ */
+#define WM8350_INR_MUTE                         0x4000
+#define WM8350_INR_ZC                           0x2000
+#define WM8350_IN_VU                            0x0100
+#define WM8350_INR_VOL_MASK                     0x00FC
+
+/*
+ * R88 (0x58) - Left Mixer Control
+ */
+#define WM8350_DACR_TO_MIXOUTL                  0x1000
+#define WM8350_DACL_TO_MIXOUTL                  0x0800
+#define WM8350_IN3L_TO_MIXOUTL                  0x0004
+#define WM8350_INR_TO_MIXOUTL                   0x0002
+#define WM8350_INL_TO_MIXOUTL                   0x0001
+
+/*
+ * R89 (0x59) - Right Mixer Control
+ */
+#define WM8350_DACR_TO_MIXOUTR                  0x1000
+#define WM8350_DACL_TO_MIXOUTR                  0x0800
+#define WM8350_IN3R_TO_MIXOUTR                  0x0008
+#define WM8350_INR_TO_MIXOUTR                   0x0002
+#define WM8350_INL_TO_MIXOUTR                   0x0001
+
+/*
+ * R92 (0x5C) - OUT3 Mixer Control
+ */
+#define WM8350_DACL_TO_OUT3                     0x0800
+#define WM8350_MIXINL_TO_OUT3                   0x0100
+#define WM8350_OUT4_TO_OUT3                     0x0008
+#define WM8350_MIXOUTL_TO_OUT3                  0x0001
+
+/*
+ * R93 (0x5D) - OUT4 Mixer Control
+ */
+#define WM8350_DACR_TO_OUT4                     0x1000
+#define WM8350_DACL_TO_OUT4                     0x0800
+#define WM8350_OUT4_ATTN                        0x0400
+#define WM8350_MIXINR_TO_OUT4                   0x0200
+#define WM8350_OUT3_TO_OUT4                     0x0004
+#define WM8350_MIXOUTR_TO_OUT4                  0x0002
+#define WM8350_MIXOUTL_TO_OUT4                  0x0001
+
+/*
+ * R96 (0x60) - Output Left Mixer Volume
+ */
+#define WM8350_IN3L_MIXOUTL_VOL_MASK            0x0E00
+#define WM8350_IN3L_MIXOUTL_VOL_SHIFT                9
+#define WM8350_INR_MIXOUTL_VOL_MASK             0x00E0
+#define WM8350_INR_MIXOUTL_VOL_SHIFT                 5
+#define WM8350_INL_MIXOUTL_VOL_MASK             0x000E
+#define WM8350_INL_MIXOUTL_VOL_SHIFT                 1
+
+/* Bit values for R96 (0x60) */
+#define WM8350_IN3L_MIXOUTL_VOL_OFF                  0
+#define WM8350_IN3L_MIXOUTL_VOL_M12DB                1
+#define WM8350_IN3L_MIXOUTL_VOL_M9DB                 2
+#define WM8350_IN3L_MIXOUTL_VOL_M6DB                 3
+#define WM8350_IN3L_MIXOUTL_VOL_M3DB                 4
+#define WM8350_IN3L_MIXOUTL_VOL_0DB                  5
+#define WM8350_IN3L_MIXOUTL_VOL_3DB                  6
+#define WM8350_IN3L_MIXOUTL_VOL_6DB                  7
+
+#define WM8350_INR_MIXOUTL_VOL_OFF                   0
+#define WM8350_INR_MIXOUTL_VOL_M12DB                 1
+#define WM8350_INR_MIXOUTL_VOL_M9DB                  2
+#define WM8350_INR_MIXOUTL_VOL_M6DB                  3
+#define WM8350_INR_MIXOUTL_VOL_M3DB                  4
+#define WM8350_INR_MIXOUTL_VOL_0DB                   5
+#define WM8350_INR_MIXOUTL_VOL_3DB                   6
+#define WM8350_INR_MIXOUTL_VOL_6DB                   7
+
+#define WM8350_INL_MIXOUTL_VOL_OFF                   0
+#define WM8350_INL_MIXOUTL_VOL_M12DB                 1
+#define WM8350_INL_MIXOUTL_VOL_M9DB                  2
+#define WM8350_INL_MIXOUTL_VOL_M6DB                  3
+#define WM8350_INL_MIXOUTL_VOL_M3DB                  4
+#define WM8350_INL_MIXOUTL_VOL_0DB                   5
+#define WM8350_INL_MIXOUTL_VOL_3DB                   6
+#define WM8350_INL_MIXOUTL_VOL_6DB                   7
+
+/*
+ * R97 (0x61) - Output Right Mixer Volume
+ */
+#define WM8350_IN3R_MIXOUTR_VOL_MASK            0xE000
+#define WM8350_IN3R_MIXOUTR_VOL_SHIFT               13
+#define WM8350_INR_MIXOUTR_VOL_MASK             0x00E0
+#define WM8350_INR_MIXOUTR_VOL_SHIFT                 5
+#define WM8350_INL_MIXOUTR_VOL_MASK             0x000E
+#define WM8350_INL_MIXOUTR_VOL_SHIFT                 1
+
+/* Bit values for R96 (0x60) */
+#define WM8350_IN3R_MIXOUTR_VOL_OFF                  0
+#define WM8350_IN3R_MIXOUTR_VOL_M12DB                1
+#define WM8350_IN3R_MIXOUTR_VOL_M9DB                 2
+#define WM8350_IN3R_MIXOUTR_VOL_M6DB                 3
+#define WM8350_IN3R_MIXOUTR_VOL_M3DB                 4
+#define WM8350_IN3R_MIXOUTR_VOL_0DB                  5
+#define WM8350_IN3R_MIXOUTR_VOL_3DB                  6
+#define WM8350_IN3R_MIXOUTR_VOL_6DB                  7
+
+#define WM8350_INR_MIXOUTR_VOL_OFF                   0
+#define WM8350_INR_MIXOUTR_VOL_M12DB                 1
+#define WM8350_INR_MIXOUTR_VOL_M9DB                  2
+#define WM8350_INR_MIXOUTR_VOL_M6DB                  3
+#define WM8350_INR_MIXOUTR_VOL_M3DB                  4
+#define WM8350_INR_MIXOUTR_VOL_0DB                   5
+#define WM8350_INR_MIXOUTR_VOL_3DB                   6
+#define WM8350_INR_MIXOUTR_VOL_6DB                   7
+
+#define WM8350_INL_MIXOUTR_VOL_OFF                   0
+#define WM8350_INL_MIXOUTR_VOL_M12DB                 1
+#define WM8350_INL_MIXOUTR_VOL_M9DB                  2
+#define WM8350_INL_MIXOUTR_VOL_M6DB                  3
+#define WM8350_INL_MIXOUTR_VOL_M3DB                  4
+#define WM8350_INL_MIXOUTR_VOL_0DB                   5
+#define WM8350_INL_MIXOUTR_VOL_3DB                   6
+#define WM8350_INL_MIXOUTR_VOL_6DB                   7
+
+/*
+ * R98 (0x62) - Input Mixer Volume L
+ */
+#define WM8350_IN3L_MIXINL_VOL_MASK             0x0E00
+#define WM8350_IN2L_MIXINL_VOL_MASK             0x000E
+#define WM8350_INL_MIXINL_VOL                   0x0001
+
+/*
+ * R99 (0x63) - Input Mixer Volume R
+ */
+#define WM8350_IN3R_MIXINR_VOL_MASK             0xE000
+#define WM8350_IN2R_MIXINR_VOL_MASK             0x00E0
+#define WM8350_INR_MIXINR_VOL                   0x0001
+
+/*
+ * R100 (0x64) - Input Mixer Volume
+ */
+#define WM8350_OUT4_MIXIN_DST                   0x8000
+#define WM8350_OUT4_MIXIN_VOL_MASK              0x000E
+
+/*
+ * R104 (0x68) - LOUT1 Volume
+ */
+#define WM8350_OUT1L_MUTE                       0x4000
+#define WM8350_OUT1L_ZC                         0x2000
+#define WM8350_OUT1_VU                          0x0100
+#define WM8350_OUT1L_VOL_MASK                   0x00FC
+#define WM8350_OUT1L_VOL_SHIFT                       2
+
+/*
+ * R105 (0x69) - ROUT1 Volume
+ */
+#define WM8350_OUT1R_MUTE                       0x4000
+#define WM8350_OUT1R_ZC                         0x2000
+#define WM8350_OUT1_VU                          0x0100
+#define WM8350_OUT1R_VOL_MASK                   0x00FC
+#define WM8350_OUT1R_VOL_SHIFT                       2
+
+/*
+ * R106 (0x6A) - LOUT2 Volume
+ */
+#define WM8350_OUT2L_MUTE                       0x4000
+#define WM8350_OUT2L_ZC                         0x2000
+#define WM8350_OUT2_VU                          0x0100
+#define WM8350_OUT2L_VOL_MASK                   0x00FC
+
+/*
+ * R107 (0x6B) - ROUT2 Volume
+ */
+#define WM8350_OUT2R_MUTE                       0x4000
+#define WM8350_OUT2R_ZC                         0x2000
+#define WM8350_OUT2R_INV                        0x0400
+#define WM8350_OUT2R_INV_MUTE                   0x0200
+#define WM8350_OUT2_VU                          0x0100
+#define WM8350_OUT2R_VOL_MASK                   0x00FC
+
+/*
+ * R111 (0x6F) - BEEP Volume
+ */
+#define WM8350_IN3R_OUT2R_VOL_MASK              0x00E0
+
+/*
+ * R112 (0x70) - AI Formating
+ */
+#define WM8350_AIF_BCLK_INV                     0x8000
+#define WM8350_AIF_TRI                          0x2000
+#define WM8350_AIF_LRCLK_INV                    0x1000
+#define WM8350_AIF_WL_MASK                      0x0C00
+#define WM8350_AIF_FMT_MASK                     0x0300
+
+/*
+ * R113 (0x71) - ADC DAC COMP
+ */
+#define WM8350_DAC_COMP                         0x0080
+#define WM8350_DAC_COMPMODE                     0x0040
+#define WM8350_ADC_COMP                         0x0020
+#define WM8350_ADC_COMPMODE                     0x0010
+#define WM8350_LOOPBACK                         0x0001
+
+/*
+ * R114 (0x72) - AI ADC Control
+ */
+#define WM8350_AIFADC_PD                        0x0080
+#define WM8350_AIFADCL_SRC                      0x0040
+#define WM8350_AIFADCR_SRC                      0x0020
+#define WM8350_AIFADC_TDM_CHAN                  0x0010
+#define WM8350_AIFADC_TDM                       0x0008
+
+/*
+ * R115 (0x73) - AI DAC Control
+ */
+#define WM8350_BCLK_MSTR                        0x4000
+#define WM8350_AIFDAC_PD                        0x0080
+#define WM8350_DACL_SRC                         0x0040
+#define WM8350_DACR_SRC                         0x0020
+#define WM8350_AIFDAC_TDM_CHAN                  0x0010
+#define WM8350_AIFDAC_TDM                       0x0008
+#define WM8350_DAC_BOOST_MASK                   0x0003
+
+/*
+ * R116 (0x74) - AIF Test
+ */
+#define WM8350_CODEC_BYP                        0x4000
+#define WM8350_AIFADC_WR_TST                    0x2000
+#define WM8350_AIFADC_RD_TST                    0x1000
+#define WM8350_AIFDAC_WR_TST                    0x0800
+#define WM8350_AIFDAC_RD_TST                    0x0400
+#define WM8350_AIFADC_ASYN                      0x0020
+#define WM8350_AIFDAC_ASYN                      0x0010
+
+/*
+ * R231 (0xE7) - Jack Status
+ */
+#define WM8350_JACK_R_LVL                       0x0400
+
+/*
+ * WM8350 Platform setup
+ */
+#define WM8350_S_CURVE_NONE			0x0
+#define WM8350_S_CURVE_FAST			0x1
+#define WM8350_S_CURVE_MEDIUM			0x2
+#define WM8350_S_CURVE_SLOW			0x3
+
+#define WM8350_DISCHARGE_OFF			0x0
+#define WM8350_DISCHARGE_FAST			0x1
+#define WM8350_DISCHARGE_MEDIUM			0x2
+#define WM8350_DISCHARGE_SLOW			0x3
+
+#define WM8350_TIE_OFF_500R			0x0
+#define WM8350_TIE_OFF_30K			0x1
+
+/*
+ * Clock sources & directions
+ */
+#define WM8350_SYSCLK				0
+
+#define WM8350_MCLK_SEL_PLL_MCLK		0
+#define WM8350_MCLK_SEL_PLL_DAC			1
+#define WM8350_MCLK_SEL_PLL_ADC			2
+#define WM8350_MCLK_SEL_PLL_32K			3
+#define WM8350_MCLK_SEL_MCLK			5
+
+#define WM8350_MCLK_DIR_OUT			0
+#define WM8350_MCLK_DIR_IN			1
+
+/* clock divider id's */
+#define WM8350_ADC_CLKDIV			0
+#define WM8350_DAC_CLKDIV			1
+#define WM8350_BCLK_CLKDIV			2
+#define WM8350_OPCLK_CLKDIV			3
+#define WM8350_TO_CLKDIV			4
+#define WM8350_SYS_CLKDIV			5
+#define WM8350_DACLR_CLKDIV			6
+#define WM8350_ADCLR_CLKDIV			7
+
+/* ADC clock dividers */
+#define WM8350_ADCDIV_1				0x0
+#define WM8350_ADCDIV_1_5			0x1
+#define WM8350_ADCDIV_2				0x2
+#define WM8350_ADCDIV_3				0x3
+#define WM8350_ADCDIV_4				0x4
+#define WM8350_ADCDIV_5_5			0x5
+#define WM8350_ADCDIV_6				0x6
+
+/* ADC clock dividers */
+#define WM8350_DACDIV_1				0x0
+#define WM8350_DACDIV_1_5			0x1
+#define WM8350_DACDIV_2				0x2
+#define WM8350_DACDIV_3				0x3
+#define WM8350_DACDIV_4				0x4
+#define WM8350_DACDIV_5_5			0x5
+#define WM8350_DACDIV_6				0x6
+
+/* BCLK clock dividers */
+#define WM8350_BCLK_DIV_1			(0x0 << 4)
+#define WM8350_BCLK_DIV_1_5			(0x1 << 4)
+#define WM8350_BCLK_DIV_2			(0x2 << 4)
+#define WM8350_BCLK_DIV_3			(0x3 << 4)
+#define WM8350_BCLK_DIV_4			(0x4 << 4)
+#define WM8350_BCLK_DIV_5_5			(0x5 << 4)
+#define WM8350_BCLK_DIV_6			(0x6 << 4)
+#define WM8350_BCLK_DIV_8			(0x7 << 4)
+#define WM8350_BCLK_DIV_11			(0x8 << 4)
+#define WM8350_BCLK_DIV_12			(0x9 << 4)
+#define WM8350_BCLK_DIV_16			(0xa << 4)
+#define WM8350_BCLK_DIV_22			(0xb << 4)
+#define WM8350_BCLK_DIV_24			(0xc << 4)
+#define WM8350_BCLK_DIV_32			(0xd << 4)
+#define WM8350_BCLK_DIV_44			(0xe << 4)
+#define WM8350_BCLK_DIV_48			(0xf << 4)
+
+/* Sys (MCLK) clock dividers */
+#define WM8350_MCLK_DIV_1			(0x0 << 8)
+#define WM8350_MCLK_DIV_2			(0x1 << 8)
+
+/* OP clock dividers */
+#define WM8350_OPCLK_DIV_1			0x0
+#define WM8350_OPCLK_DIV_2			0x1
+#define WM8350_OPCLK_DIV_3			0x2
+#define WM8350_OPCLK_DIV_4			0x3
+#define WM8350_OPCLK_DIV_5_5			0x4
+#define WM8350_OPCLK_DIV_6			0x5
+
+/* DAI ID */
+#define WM8350_HIFI_DAI				0
+
+/*
+ * Audio interrupts.
+ */
+#define WM8350_IRQ_CODEC_JCK_DET_L		39
+#define WM8350_IRQ_CODEC_JCK_DET_R		40
+#define WM8350_IRQ_CODEC_MICSCD			41
+#define WM8350_IRQ_CODEC_MICD			42
+
+struct wm8350_codec {
+	struct platform_device *pdev;
+};
+
+#endif
diff --git a/include/linux/mfd/wm8350/comparator.h b/include/linux/mfd/wm8350/comparator.h
new file mode 100644
index 0000000..0537886
--- /dev/null
+++ b/include/linux/mfd/wm8350/comparator.h
@@ -0,0 +1,167 @@
+/*
+ * comparator.h  --  Comparator Aux ADC for Wolfson WM8350 PMIC
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC
+ *
+ *  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.
+ */
+
+#ifndef __LINUX_MFD_WM8350_COMPARATOR_H_
+#define __LINUX_MFD_WM8350_COMPARATOR_H_
+
+/*
+ * Registers
+ */
+
+#define WM8350_DIGITISER_CONTROL_1              0x90
+#define WM8350_DIGITISER_CONTROL_2              0x91
+#define WM8350_AUX1_READBACK                    0x98
+#define WM8350_AUX2_READBACK                    0x99
+#define WM8350_AUX3_READBACK                    0x9A
+#define WM8350_AUX4_READBACK                    0x9B
+#define WM8350_CHIP_TEMP_READBACK               0x9F
+#define WM8350_GENERIC_COMPARATOR_CONTROL       0xA3
+#define WM8350_GENERIC_COMPARATOR_1             0xA4
+#define WM8350_GENERIC_COMPARATOR_2             0xA5
+#define WM8350_GENERIC_COMPARATOR_3             0xA6
+#define WM8350_GENERIC_COMPARATOR_4             0xA7
+
+/*
+ * R144 (0x90) - Digitiser Control (1)
+ */
+#define WM8350_AUXADC_CTC                       0x4000
+#define WM8350_AUXADC_POLL                      0x2000
+#define WM8350_AUXADC_HIB_MODE                  0x1000
+#define WM8350_AUXADC_SEL8                      0x0080
+#define WM8350_AUXADC_SEL7                      0x0040
+#define WM8350_AUXADC_SEL6                      0x0020
+#define WM8350_AUXADC_SEL5                      0x0010
+#define WM8350_AUXADC_SEL4                      0x0008
+#define WM8350_AUXADC_SEL3                      0x0004
+#define WM8350_AUXADC_SEL2                      0x0002
+#define WM8350_AUXADC_SEL1                      0x0001
+
+/*
+ * R145 (0x91) - Digitiser Control (2)
+ */
+#define WM8350_AUXADC_MASKMODE_MASK             0x3000
+#define WM8350_AUXADC_CRATE_MASK                0x0700
+#define WM8350_AUXADC_CAL                       0x0004
+#define WM8350_AUX_RBMODE                       0x0002
+#define WM8350_AUXADC_WAIT                      0x0001
+
+/*
+ * R152 (0x98) - AUX1 Readback
+ */
+#define WM8350_AUXADC_SCALE1_MASK               0x6000
+#define WM8350_AUXADC_REF1                      0x1000
+#define WM8350_AUXADC_DATA1_MASK                0x0FFF
+
+/*
+ * R153 (0x99) - AUX2 Readback
+ */
+#define WM8350_AUXADC_SCALE2_MASK               0x6000
+#define WM8350_AUXADC_REF2                      0x1000
+#define WM8350_AUXADC_DATA2_MASK                0x0FFF
+
+/*
+ * R154 (0x9A) - AUX3 Readback
+ */
+#define WM8350_AUXADC_SCALE3_MASK               0x6000
+#define WM8350_AUXADC_REF3                      0x1000
+#define WM8350_AUXADC_DATA3_MASK                0x0FFF
+
+/*
+ * R155 (0x9B) - AUX4 Readback
+ */
+#define WM8350_AUXADC_SCALE4_MASK               0x6000
+#define WM8350_AUXADC_REF4                      0x1000
+#define WM8350_AUXADC_DATA4_MASK                0x0FFF
+
+/*
+ * R156 (0x9C) - USB Voltage Readback
+ */
+#define WM8350_AUXADC_DATA_USB_MASK             0x0FFF
+
+/*
+ * R157 (0x9D) - LINE Voltage Readback
+ */
+#define WM8350_AUXADC_DATA_LINE_MASK            0x0FFF
+
+/*
+ * R158 (0x9E) - BATT Voltage Readback
+ */
+#define WM8350_AUXADC_DATA_BATT_MASK            0x0FFF
+
+/*
+ * R159 (0x9F) - Chip Temp Readback
+ */
+#define WM8350_AUXADC_DATA_CHIPTEMP_MASK        0x0FFF
+
+/*
+ * R163 (0xA3) - Generic Comparator Control
+ */
+#define WM8350_DCMP4_ENA                        0x0008
+#define WM8350_DCMP3_ENA                        0x0004
+#define WM8350_DCMP2_ENA                        0x0002
+#define WM8350_DCMP1_ENA                        0x0001
+
+/*
+ * R164 (0xA4) - Generic comparator 1
+ */
+#define WM8350_DCMP1_SRCSEL_MASK                0xE000
+#define WM8350_DCMP1_GT                         0x1000
+#define WM8350_DCMP1_THR_MASK                   0x0FFF
+
+/*
+ * R165 (0xA5) - Generic comparator 2
+ */
+#define WM8350_DCMP2_SRCSEL_MASK                0xE000
+#define WM8350_DCMP2_GT                         0x1000
+#define WM8350_DCMP2_THR_MASK                   0x0FFF
+
+/*
+ * R166 (0xA6) - Generic comparator 3
+ */
+#define WM8350_DCMP3_SRCSEL_MASK                0xE000
+#define WM8350_DCMP3_GT                         0x1000
+#define WM8350_DCMP3_THR_MASK                   0x0FFF
+
+/*
+ * R167 (0xA7) - Generic comparator 4
+ */
+#define WM8350_DCMP4_SRCSEL_MASK                0xE000
+#define WM8350_DCMP4_GT                         0x1000
+#define WM8350_DCMP4_THR_MASK                   0x0FFF
+
+/*
+ * Interrupts.
+ */
+#define WM8350_IRQ_AUXADC_DATARDY		16
+#define WM8350_IRQ_AUXADC_DCOMP4		17
+#define WM8350_IRQ_AUXADC_DCOMP3		18
+#define WM8350_IRQ_AUXADC_DCOMP2		19
+#define WM8350_IRQ_AUXADC_DCOMP1		20
+#define WM8350_IRQ_SYS_HYST_COMP_FAIL		21
+#define WM8350_IRQ_SYS_CHIP_GT115		22
+#define WM8350_IRQ_SYS_CHIP_GT140		23
+
+/*
+ * USB/2, LINE & BATT = ((VRTC * 2) / 4095)) * 10e6 uV
+ * Where VRTC = 2.7 V
+ */
+#define WM8350_AUX_COEFF			1319
+
+#define WM8350_AUXADC_AUX1			0
+#define WM8350_AUXADC_AUX2			1
+#define WM8350_AUXADC_AUX3			2
+#define WM8350_AUXADC_AUX4			3
+#define WM8350_AUXADC_USB			4
+#define WM8350_AUXADC_LINE			5
+#define WM8350_AUXADC_BATT			6
+#define WM8350_AUXADC_TEMP			7
+
+#endif
diff --git a/include/linux/mfd/wm8350/core.h b/include/linux/mfd/wm8350/core.h
new file mode 100644
index 0000000..6ebf97f
--- /dev/null
+++ b/include/linux/mfd/wm8350/core.h
@@ -0,0 +1,631 @@
+/*
+ * core.h  --  Core Driver for Wolfson WM8350 PMIC
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC
+ *
+ *  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.
+ *
+ */
+
+#ifndef __LINUX_MFD_WM8350_CORE_H_
+#define __LINUX_MFD_WM8350_CORE_H_
+
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+
+#include <linux/mfd/wm8350/audio.h>
+#include <linux/mfd/wm8350/gpio.h>
+#include <linux/mfd/wm8350/pmic.h>
+#include <linux/mfd/wm8350/rtc.h>
+#include <linux/mfd/wm8350/supply.h>
+#include <linux/mfd/wm8350/wdt.h>
+
+/*
+ * Register values.
+ */
+#define WM8350_RESET_ID                         0x00
+#define WM8350_ID                               0x01
+#define WM8350_SYSTEM_CONTROL_1                 0x03
+#define WM8350_SYSTEM_CONTROL_2                 0x04
+#define WM8350_SYSTEM_HIBERNATE                 0x05
+#define WM8350_INTERFACE_CONTROL                0x06
+#define WM8350_POWER_MGMT_1                     0x08
+#define WM8350_POWER_MGMT_2                     0x09
+#define WM8350_POWER_MGMT_3                     0x0A
+#define WM8350_POWER_MGMT_4                     0x0B
+#define WM8350_POWER_MGMT_5                     0x0C
+#define WM8350_POWER_MGMT_6                     0x0D
+#define WM8350_POWER_MGMT_7                     0x0E
+
+#define WM8350_SYSTEM_INTERRUPTS                0x18
+#define WM8350_INT_STATUS_1                     0x19
+#define WM8350_INT_STATUS_2                     0x1A
+#define WM8350_POWER_UP_INT_STATUS              0x1B
+#define WM8350_UNDER_VOLTAGE_INT_STATUS         0x1C
+#define WM8350_OVER_CURRENT_INT_STATUS          0x1D
+#define WM8350_GPIO_INT_STATUS                  0x1E
+#define WM8350_COMPARATOR_INT_STATUS            0x1F
+#define WM8350_SYSTEM_INTERRUPTS_MASK           0x20
+#define WM8350_INT_STATUS_1_MASK                0x21
+#define WM8350_INT_STATUS_2_MASK                0x22
+#define WM8350_POWER_UP_INT_STATUS_MASK         0x23
+#define WM8350_UNDER_VOLTAGE_INT_STATUS_MASK    0x24
+#define WM8350_OVER_CURRENT_INT_STATUS_MASK     0x25
+#define WM8350_GPIO_INT_STATUS_MASK             0x26
+#define WM8350_COMPARATOR_INT_STATUS_MASK       0x27
+
+#define WM8350_MAX_REGISTER                     0xFF
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - Reset/ID
+ */
+#define WM8350_SW_RESET_CHIP_ID_MASK            0xFFFF
+
+/*
+ * R1 (0x01) - ID
+ */
+#define WM8350_CHIP_REV_MASK                    0x7000
+#define WM8350_CONF_STS_MASK                    0x0C00
+#define WM8350_CUST_ID_MASK                     0x00FF
+
+/*
+ * R3 (0x03) - System Control 1
+ */
+#define WM8350_CHIP_ON                          0x8000
+#define WM8350_POWERCYCLE                       0x2000
+#define WM8350_VCC_FAULT_OV                     0x1000
+#define WM8350_REG_RSTB_TIME_MASK               0x0C00
+#define WM8350_BG_SLEEP                         0x0200
+#define WM8350_MEM_VALID                        0x0020
+#define WM8350_CHIP_SET_UP                      0x0010
+#define WM8350_ON_DEB_T                         0x0008
+#define WM8350_ON_POL                           0x0002
+#define WM8350_IRQ_POL                          0x0001
+
+/*
+ * R4 (0x04) - System Control 2
+ */
+#define WM8350_USB_SUSPEND_8MA                  0x8000
+#define WM8350_USB_SUSPEND                      0x4000
+#define WM8350_USB_MSTR                         0x2000
+#define WM8350_USB_MSTR_SRC                     0x1000
+#define WM8350_USB_500MA                        0x0800
+#define WM8350_USB_NOLIM                        0x0400
+
+/*
+ * R5 (0x05) - System Hibernate
+ */
+#define WM8350_HIBERNATE                        0x8000
+#define WM8350_WDOG_HIB_MODE                    0x0080
+#define WM8350_REG_HIB_STARTUP_SEQ              0x0040
+#define WM8350_REG_RESET_HIB_MODE               0x0020
+#define WM8350_RST_HIB_MODE                     0x0010
+#define WM8350_IRQ_HIB_MODE                     0x0008
+#define WM8350_MEMRST_HIB_MODE                  0x0004
+#define WM8350_PCCOMP_HIB_MODE                  0x0002
+#define WM8350_TEMPMON_HIB_MODE                 0x0001
+
+/*
+ * R6 (0x06) - Interface Control
+ */
+#define WM8350_USE_DEV_PINS                     0x8000
+#define WM8350_USE_DEV_PINS_MASK                0x8000
+#define WM8350_USE_DEV_PINS_SHIFT                   15
+#define WM8350_DEV_ADDR_MASK                    0x6000
+#define WM8350_DEV_ADDR_SHIFT                       13
+#define WM8350_CONFIG_DONE                      0x1000
+#define WM8350_CONFIG_DONE_MASK                 0x1000
+#define WM8350_CONFIG_DONE_SHIFT                    12
+#define WM8350_RECONFIG_AT_ON                   0x0800
+#define WM8350_RECONFIG_AT_ON_MASK              0x0800
+#define WM8350_RECONFIG_AT_ON_SHIFT                 11
+#define WM8350_AUTOINC                          0x0200
+#define WM8350_AUTOINC_MASK                     0x0200
+#define WM8350_AUTOINC_SHIFT                         9
+#define WM8350_ARA                              0x0100
+#define WM8350_ARA_MASK                         0x0100
+#define WM8350_ARA_SHIFT                             8
+#define WM8350_SPI_CFG                          0x0008
+#define WM8350_SPI_CFG_MASK                     0x0008
+#define WM8350_SPI_CFG_SHIFT                         3
+#define WM8350_SPI_4WIRE                        0x0004
+#define WM8350_SPI_4WIRE_MASK                   0x0004
+#define WM8350_SPI_4WIRE_SHIFT                       2
+#define WM8350_SPI_3WIRE                        0x0002
+#define WM8350_SPI_3WIRE_MASK                   0x0002
+#define WM8350_SPI_3WIRE_SHIFT                       1
+
+/* Bit values for R06 (0x06) */
+#define WM8350_USE_DEV_PINS_PRIMARY                  0
+#define WM8350_USE_DEV_PINS_DEV                      1
+
+#define WM8350_DEV_ADDR_34                           0
+#define WM8350_DEV_ADDR_36                           1
+#define WM8350_DEV_ADDR_3C                           2
+#define WM8350_DEV_ADDR_3E                           3
+
+#define WM8350_CONFIG_DONE_OFF                       0
+#define WM8350_CONFIG_DONE_DONE                      1
+
+#define WM8350_RECONFIG_AT_ON_OFF                    0
+#define WM8350_RECONFIG_AT_ON_ON                     1
+
+#define WM8350_AUTOINC_OFF                           0
+#define WM8350_AUTOINC_ON                            1
+
+#define WM8350_ARA_OFF                               0
+#define WM8350_ARA_ON                                1
+
+#define WM8350_SPI_CFG_CMOS                          0
+#define WM8350_SPI_CFG_OD                            1
+
+#define WM8350_SPI_4WIRE_3WIRE                       0
+#define WM8350_SPI_4WIRE_4WIRE                       1
+
+#define WM8350_SPI_3WIRE_I2C                         0
+#define WM8350_SPI_3WIRE_SPI                         1
+
+/*
+ * R8 (0x08) - Power mgmt (1)
+ */
+#define WM8350_CODEC_ISEL_MASK                  0xC000
+#define WM8350_VBUFEN                           0x2000
+#define WM8350_OUTPUT_DRAIN_EN                  0x0400
+#define WM8350_MIC_DET_ENA                      0x0100
+#define WM8350_BIASEN                           0x0020
+#define WM8350_MICBEN                           0x0010
+#define WM8350_VMIDEN                           0x0004
+#define WM8350_VMID_MASK                        0x0003
+#define WM8350_VMID_SHIFT                            0
+
+/*
+ * R9 (0x09) - Power mgmt (2)
+ */
+#define WM8350_IN3R_ENA                         0x0800
+#define WM8350_IN3L_ENA                         0x0400
+#define WM8350_INR_ENA                          0x0200
+#define WM8350_INL_ENA                          0x0100
+#define WM8350_MIXINR_ENA                       0x0080
+#define WM8350_MIXINL_ENA                       0x0040
+#define WM8350_OUT4_ENA                         0x0020
+#define WM8350_OUT3_ENA                         0x0010
+#define WM8350_MIXOUTR_ENA                      0x0002
+#define WM8350_MIXOUTL_ENA                      0x0001
+
+/*
+ * R10 (0x0A) - Power mgmt (3)
+ */
+#define WM8350_IN3R_TO_OUT2R                    0x0080
+#define WM8350_OUT2R_ENA                        0x0008
+#define WM8350_OUT2L_ENA                        0x0004
+#define WM8350_OUT1R_ENA                        0x0002
+#define WM8350_OUT1L_ENA                        0x0001
+
+/*
+ * R11 (0x0B) - Power mgmt (4)
+ */
+#define WM8350_SYSCLK_ENA                       0x4000
+#define WM8350_ADC_HPF_ENA                      0x2000
+#define WM8350_FLL_ENA                          0x0800
+#define WM8350_FLL_OSC_ENA                      0x0400
+#define WM8350_TOCLK_ENA                        0x0100
+#define WM8350_DACR_ENA                         0x0020
+#define WM8350_DACL_ENA                         0x0010
+#define WM8350_ADCR_ENA                         0x0008
+#define WM8350_ADCL_ENA                         0x0004
+
+/*
+ * R12 (0x0C) - Power mgmt (5)
+ */
+#define WM8350_CODEC_ENA                        0x1000
+#define WM8350_RTC_TICK_ENA                     0x0800
+#define WM8350_OSC32K_ENA                       0x0400
+#define WM8350_CHG_ENA                          0x0200
+#define WM8350_ACC_DET_ENA                      0x0100
+#define WM8350_AUXADC_ENA                       0x0080
+#define WM8350_DCMP4_ENA                        0x0008
+#define WM8350_DCMP3_ENA                        0x0004
+#define WM8350_DCMP2_ENA                        0x0002
+#define WM8350_DCMP1_ENA                        0x0001
+
+/*
+ * R13 (0x0D) - Power mgmt (6)
+ */
+#define WM8350_LS_ENA                           0x8000
+#define WM8350_LDO4_ENA                         0x0800
+#define WM8350_LDO3_ENA                         0x0400
+#define WM8350_LDO2_ENA                         0x0200
+#define WM8350_LDO1_ENA                         0x0100
+#define WM8350_DC6_ENA                          0x0020
+#define WM8350_DC5_ENA                          0x0010
+#define WM8350_DC4_ENA                          0x0008
+#define WM8350_DC3_ENA                          0x0004
+#define WM8350_DC2_ENA                          0x0002
+#define WM8350_DC1_ENA                          0x0001
+
+/*
+ * R14 (0x0E) - Power mgmt (7)
+ */
+#define WM8350_CS2_ENA                          0x0002
+#define WM8350_CS1_ENA                          0x0001
+
+/*
+ * R24 (0x18) - System Interrupts
+ */
+#define WM8350_OC_INT                           0x2000
+#define WM8350_UV_INT                           0x1000
+#define WM8350_PUTO_INT                         0x0800
+#define WM8350_CS_INT                           0x0200
+#define WM8350_EXT_INT                          0x0100
+#define WM8350_CODEC_INT                        0x0080
+#define WM8350_GP_INT                           0x0040
+#define WM8350_AUXADC_INT                       0x0020
+#define WM8350_RTC_INT                          0x0010
+#define WM8350_SYS_INT                          0x0008
+#define WM8350_CHG_INT                          0x0004
+#define WM8350_USB_INT                          0x0002
+#define WM8350_WKUP_INT                         0x0001
+
+/*
+ * R25 (0x19) - Interrupt Status 1
+ */
+#define WM8350_CHG_BAT_HOT_EINT                 0x8000
+#define WM8350_CHG_BAT_COLD_EINT                0x4000
+#define WM8350_CHG_BAT_FAIL_EINT                0x2000
+#define WM8350_CHG_TO_EINT                      0x1000
+#define WM8350_CHG_END_EINT                     0x0800
+#define WM8350_CHG_START_EINT                   0x0400
+#define WM8350_CHG_FAST_RDY_EINT                0x0200
+#define WM8350_RTC_PER_EINT                     0x0080
+#define WM8350_RTC_SEC_EINT                     0x0040
+#define WM8350_RTC_ALM_EINT                     0x0020
+#define WM8350_CHG_VBATT_LT_3P9_EINT            0x0004
+#define WM8350_CHG_VBATT_LT_3P1_EINT            0x0002
+#define WM8350_CHG_VBATT_LT_2P85_EINT           0x0001
+
+/*
+ * R26 (0x1A) - Interrupt Status 2
+ */
+#define WM8350_CS1_EINT                         0x2000
+#define WM8350_CS2_EINT                         0x1000
+#define WM8350_USB_LIMIT_EINT                   0x0400
+#define WM8350_AUXADC_DATARDY_EINT              0x0100
+#define WM8350_AUXADC_DCOMP4_EINT               0x0080
+#define WM8350_AUXADC_DCOMP3_EINT               0x0040
+#define WM8350_AUXADC_DCOMP2_EINT               0x0020
+#define WM8350_AUXADC_DCOMP1_EINT               0x0010
+#define WM8350_SYS_HYST_COMP_FAIL_EINT          0x0008
+#define WM8350_SYS_CHIP_GT115_EINT              0x0004
+#define WM8350_SYS_CHIP_GT140_EINT              0x0002
+#define WM8350_SYS_WDOG_TO_EINT                 0x0001
+
+/*
+ * R27 (0x1B) - Power Up Interrupt Status
+ */
+#define WM8350_PUTO_LDO4_EINT                   0x0800
+#define WM8350_PUTO_LDO3_EINT                   0x0400
+#define WM8350_PUTO_LDO2_EINT                   0x0200
+#define WM8350_PUTO_LDO1_EINT                   0x0100
+#define WM8350_PUTO_DC6_EINT                    0x0020
+#define WM8350_PUTO_DC5_EINT                    0x0010
+#define WM8350_PUTO_DC4_EINT                    0x0008
+#define WM8350_PUTO_DC3_EINT                    0x0004
+#define WM8350_PUTO_DC2_EINT                    0x0002
+#define WM8350_PUTO_DC1_EINT                    0x0001
+
+/*
+ * R28 (0x1C) - Under Voltage Interrupt status
+ */
+#define WM8350_UV_LDO4_EINT                     0x0800
+#define WM8350_UV_LDO3_EINT                     0x0400
+#define WM8350_UV_LDO2_EINT                     0x0200
+#define WM8350_UV_LDO1_EINT                     0x0100
+#define WM8350_UV_DC6_EINT                      0x0020
+#define WM8350_UV_DC5_EINT                      0x0010
+#define WM8350_UV_DC4_EINT                      0x0008
+#define WM8350_UV_DC3_EINT                      0x0004
+#define WM8350_UV_DC2_EINT                      0x0002
+#define WM8350_UV_DC1_EINT                      0x0001
+
+/*
+ * R29 (0x1D) - Over Current Interrupt status
+ */
+#define WM8350_OC_LS_EINT                       0x8000
+
+/*
+ * R30 (0x1E) - GPIO Interrupt Status
+ */
+#define WM8350_GP12_EINT                        0x1000
+#define WM8350_GP11_EINT                        0x0800
+#define WM8350_GP10_EINT                        0x0400
+#define WM8350_GP9_EINT                         0x0200
+#define WM8350_GP8_EINT                         0x0100
+#define WM8350_GP7_EINT                         0x0080
+#define WM8350_GP6_EINT                         0x0040
+#define WM8350_GP5_EINT                         0x0020
+#define WM8350_GP4_EINT                         0x0010
+#define WM8350_GP3_EINT                         0x0008
+#define WM8350_GP2_EINT                         0x0004
+#define WM8350_GP1_EINT                         0x0002
+#define WM8350_GP0_EINT                         0x0001
+
+/*
+ * R31 (0x1F) - Comparator Interrupt Status
+ */
+#define WM8350_EXT_USB_FB_EINT                  0x8000
+#define WM8350_EXT_WALL_FB_EINT                 0x4000
+#define WM8350_EXT_BAT_FB_EINT                  0x2000
+#define WM8350_CODEC_JCK_DET_L_EINT             0x0800
+#define WM8350_CODEC_JCK_DET_R_EINT             0x0400
+#define WM8350_CODEC_MICSCD_EINT                0x0200
+#define WM8350_CODEC_MICD_EINT                  0x0100
+#define WM8350_WKUP_OFF_STATE_EINT              0x0040
+#define WM8350_WKUP_HIB_STATE_EINT              0x0020
+#define WM8350_WKUP_CONV_FAULT_EINT             0x0010
+#define WM8350_WKUP_WDOG_RST_EINT               0x0008
+#define WM8350_WKUP_GP_PWR_ON_EINT              0x0004
+#define WM8350_WKUP_ONKEY_EINT                  0x0002
+#define WM8350_WKUP_GP_WAKEUP_EINT              0x0001
+
+/*
+ * R32 (0x20) - System Interrupts Mask
+ */
+#define WM8350_IM_OC_INT                        0x2000
+#define WM8350_IM_UV_INT                        0x1000
+#define WM8350_IM_PUTO_INT                      0x0800
+#define WM8350_IM_SPARE_INT                     0x0400
+#define WM8350_IM_CS_INT                        0x0200
+#define WM8350_IM_EXT_INT                       0x0100
+#define WM8350_IM_CODEC_INT                     0x0080
+#define WM8350_IM_GP_INT                        0x0040
+#define WM8350_IM_AUXADC_INT                    0x0020
+#define WM8350_IM_RTC_INT                       0x0010
+#define WM8350_IM_SYS_INT                       0x0008
+#define WM8350_IM_CHG_INT                       0x0004
+#define WM8350_IM_USB_INT                       0x0002
+#define WM8350_IM_WKUP_INT                      0x0001
+
+/*
+ * R33 (0x21) - Interrupt Status 1 Mask
+ */
+#define WM8350_IM_CHG_BAT_HOT_EINT              0x8000
+#define WM8350_IM_CHG_BAT_COLD_EINT             0x4000
+#define WM8350_IM_CHG_BAT_FAIL_EINT             0x2000
+#define WM8350_IM_CHG_TO_EINT                   0x1000
+#define WM8350_IM_CHG_END_EINT                  0x0800
+#define WM8350_IM_CHG_START_EINT                0x0400
+#define WM8350_IM_CHG_FAST_RDY_EINT             0x0200
+#define WM8350_IM_RTC_PER_EINT                  0x0080
+#define WM8350_IM_RTC_SEC_EINT                  0x0040
+#define WM8350_IM_RTC_ALM_EINT                  0x0020
+#define WM8350_IM_CHG_VBATT_LT_3P9_EINT         0x0004
+#define WM8350_IM_CHG_VBATT_LT_3P1_EINT         0x0002
+#define WM8350_IM_CHG_VBATT_LT_2P85_EINT        0x0001
+
+/*
+ * R34 (0x22) - Interrupt Status 2 Mask
+ */
+#define WM8350_IM_SPARE2_EINT                   0x8000
+#define WM8350_IM_SPARE1_EINT                   0x4000
+#define WM8350_IM_CS1_EINT                      0x2000
+#define WM8350_IM_CS2_EINT                      0x1000
+#define WM8350_IM_USB_LIMIT_EINT                0x0400
+#define WM8350_IM_AUXADC_DATARDY_EINT           0x0100
+#define WM8350_IM_AUXADC_DCOMP4_EINT            0x0080
+#define WM8350_IM_AUXADC_DCOMP3_EINT            0x0040
+#define WM8350_IM_AUXADC_DCOMP2_EINT            0x0020
+#define WM8350_IM_AUXADC_DCOMP1_EINT            0x0010
+#define WM8350_IM_SYS_HYST_COMP_FAIL_EINT       0x0008
+#define WM8350_IM_SYS_CHIP_GT115_EINT           0x0004
+#define WM8350_IM_SYS_CHIP_GT140_EINT           0x0002
+#define WM8350_IM_SYS_WDOG_TO_EINT              0x0001
+
+/*
+ * R35 (0x23) - Power Up Interrupt Status Mask
+ */
+#define WM8350_IM_PUTO_LDO4_EINT                0x0800
+#define WM8350_IM_PUTO_LDO3_EINT                0x0400
+#define WM8350_IM_PUTO_LDO2_EINT                0x0200
+#define WM8350_IM_PUTO_LDO1_EINT                0x0100
+#define WM8350_IM_PUTO_DC6_EINT                 0x0020
+#define WM8350_IM_PUTO_DC5_EINT                 0x0010
+#define WM8350_IM_PUTO_DC4_EINT                 0x0008
+#define WM8350_IM_PUTO_DC3_EINT                 0x0004
+#define WM8350_IM_PUTO_DC2_EINT                 0x0002
+#define WM8350_IM_PUTO_DC1_EINT                 0x0001
+
+/*
+ * R36 (0x24) - Under Voltage Interrupt status Mask
+ */
+#define WM8350_IM_UV_LDO4_EINT                  0x0800
+#define WM8350_IM_UV_LDO3_EINT                  0x0400
+#define WM8350_IM_UV_LDO2_EINT                  0x0200
+#define WM8350_IM_UV_LDO1_EINT                  0x0100
+#define WM8350_IM_UV_DC6_EINT                   0x0020
+#define WM8350_IM_UV_DC5_EINT                   0x0010
+#define WM8350_IM_UV_DC4_EINT                   0x0008
+#define WM8350_IM_UV_DC3_EINT                   0x0004
+#define WM8350_IM_UV_DC2_EINT                   0x0002
+#define WM8350_IM_UV_DC1_EINT                   0x0001
+
+/*
+ * R37 (0x25) - Over Current Interrupt status Mask
+ */
+#define WM8350_IM_OC_LS_EINT                    0x8000
+
+/*
+ * R38 (0x26) - GPIO Interrupt Status Mask
+ */
+#define WM8350_IM_GP12_EINT                     0x1000
+#define WM8350_IM_GP11_EINT                     0x0800
+#define WM8350_IM_GP10_EINT                     0x0400
+#define WM8350_IM_GP9_EINT                      0x0200
+#define WM8350_IM_GP8_EINT                      0x0100
+#define WM8350_IM_GP7_EINT                      0x0080
+#define WM8350_IM_GP6_EINT                      0x0040
+#define WM8350_IM_GP5_EINT                      0x0020
+#define WM8350_IM_GP4_EINT                      0x0010
+#define WM8350_IM_GP3_EINT                      0x0008
+#define WM8350_IM_GP2_EINT                      0x0004
+#define WM8350_IM_GP1_EINT                      0x0002
+#define WM8350_IM_GP0_EINT                      0x0001
+
+/*
+ * R39 (0x27) - Comparator Interrupt Status Mask
+ */
+#define WM8350_IM_EXT_USB_FB_EINT               0x8000
+#define WM8350_IM_EXT_WALL_FB_EINT              0x4000
+#define WM8350_IM_EXT_BAT_FB_EINT               0x2000
+#define WM8350_IM_CODEC_JCK_DET_L_EINT          0x0800
+#define WM8350_IM_CODEC_JCK_DET_R_EINT          0x0400
+#define WM8350_IM_CODEC_MICSCD_EINT             0x0200
+#define WM8350_IM_CODEC_MICD_EINT               0x0100
+#define WM8350_IM_WKUP_OFF_STATE_EINT           0x0040
+#define WM8350_IM_WKUP_HIB_STATE_EINT           0x0020
+#define WM8350_IM_WKUP_CONV_FAULT_EINT          0x0010
+#define WM8350_IM_WKUP_WDOG_RST_EINT            0x0008
+#define WM8350_IM_WKUP_GP_PWR_ON_EINT           0x0004
+#define WM8350_IM_WKUP_ONKEY_EINT               0x0002
+#define WM8350_IM_WKUP_GP_WAKEUP_EINT           0x0001
+
+/*
+ * R220 (0xDC) - RAM BIST 1
+ */
+#define WM8350_READ_STATUS                      0x0800
+#define WM8350_TSTRAM_CLK                       0x0100
+#define WM8350_TSTRAM_CLK_ENA                   0x0080
+#define WM8350_STARTSEQ                         0x0040
+#define WM8350_READ_SRC                         0x0020
+#define WM8350_COUNT_DIR                        0x0010
+#define WM8350_TSTRAM_MODE_MASK                 0x000E
+#define WM8350_TSTRAM_ENA                       0x0001
+
+/*
+ * R225 (0xE1) - DCDC/LDO status
+ */
+#define WM8350_LS_STS                           0x8000
+#define WM8350_LDO4_STS                         0x0800
+#define WM8350_LDO3_STS                         0x0400
+#define WM8350_LDO2_STS                         0x0200
+#define WM8350_LDO1_STS                         0x0100
+#define WM8350_DC6_STS                          0x0020
+#define WM8350_DC5_STS                          0x0010
+#define WM8350_DC4_STS                          0x0008
+#define WM8350_DC3_STS                          0x0004
+#define WM8350_DC2_STS                          0x0002
+#define WM8350_DC1_STS                          0x0001
+
+/* WM8350 wake up conditions */
+#define WM8350_IRQ_WKUP_OFF_STATE		43
+#define WM8350_IRQ_WKUP_HIB_STATE		44
+#define WM8350_IRQ_WKUP_CONV_FAULT		45
+#define WM8350_IRQ_WKUP_WDOG_RST		46
+#define WM8350_IRQ_WKUP_GP_PWR_ON		47
+#define WM8350_IRQ_WKUP_ONKEY			48
+#define WM8350_IRQ_WKUP_GP_WAKEUP		49
+
+/* wm8350 chip revisions */
+#define WM8350_REV_E				0x4
+#define WM8350_REV_F				0x5
+#define WM8350_REV_G				0x6
+
+#define WM8350_NUM_IRQ				63
+
+struct wm8350_reg_access {
+	u16 readable;		/* Mask of readable bits */
+	u16 writable;		/* Mask of writable bits */
+	u16 vol;		/* Mask of volatile bits */
+};
+extern const struct wm8350_reg_access wm8350_reg_io_map[];
+extern const u16 wm8350_mode0_defaults[];
+extern const u16 wm8350_mode1_defaults[];
+extern const u16 wm8350_mode2_defaults[];
+extern const u16 wm8350_mode3_defaults[];
+
+struct wm8350;
+
+struct wm8350_irq {
+	void (*handler) (struct wm8350 *, int, void *);
+	void *data;
+};
+
+struct wm8350 {
+	int rev;		/* chip revision */
+
+	struct device *dev;
+
+	/* device IO */
+	union {
+		struct i2c_client *i2c_client;
+		struct spi_device *spi_device;
+	};
+	int (*read_dev)(struct wm8350 *wm8350, char reg, int size, void *dest);
+	int (*write_dev)(struct wm8350 *wm8350, char reg, int size,
+			 void *src);
+	u16 *reg_cache;
+
+	/* Interrupt handling */
+	struct work_struct irq_work;
+	struct mutex irq_mutex; /* IRQ table mutex */
+	struct wm8350_irq irq[WM8350_NUM_IRQ];
+	int chip_irq;
+
+	/* Client devices */
+	struct wm8350_codec codec;
+	struct wm8350_gpio gpio;
+	struct wm8350_pmic pmic;
+	struct wm8350_power power;
+	struct wm8350_rtc rtc;
+	struct wm8350_wdt wdt;
+};
+
+/**
+ * Data to be supplied by the platform to initialise the WM8350.
+ *
+ * @init: Function called during driver initialisation.  Should be
+ *        used by the platform to configure GPIO functions and similar.
+ */
+struct wm8350_platform_data {
+	int (*init)(struct wm8350 *wm8350);
+};
+
+
+/*
+ * WM8350 device initialisation and exit.
+ */
+int wm8350_device_init(struct wm8350 *wm8350, int irq,
+		       struct wm8350_platform_data *pdata);
+void wm8350_device_exit(struct wm8350 *wm8350);
+
+/*
+ * WM8350 device IO
+ */
+int wm8350_clear_bits(struct wm8350 *wm8350, u16 reg, u16 mask);
+int wm8350_set_bits(struct wm8350 *wm8350, u16 reg, u16 mask);
+u16 wm8350_reg_read(struct wm8350 *wm8350, int reg);
+int wm8350_reg_write(struct wm8350 *wm8350, int reg, u16 val);
+int wm8350_reg_lock(struct wm8350 *wm8350);
+int wm8350_reg_unlock(struct wm8350 *wm8350);
+int wm8350_block_read(struct wm8350 *wm8350, int reg, int size, u16 *dest);
+int wm8350_block_write(struct wm8350 *wm8350, int reg, int size, u16 *src);
+
+/*
+ * WM8350 internal interrupts
+ */
+int wm8350_register_irq(struct wm8350 *wm8350, int irq,
+			void (*handler) (struct wm8350 *, int, void *),
+			void *data);
+int wm8350_free_irq(struct wm8350 *wm8350, int irq);
+int wm8350_mask_irq(struct wm8350 *wm8350, int irq);
+int wm8350_unmask_irq(struct wm8350 *wm8350, int irq);
+
+
+#endif
diff --git a/include/linux/mfd/wm8350/gpio.h b/include/linux/mfd/wm8350/gpio.h
new file mode 100644
index 0000000..ed91e8f
--- /dev/null
+++ b/include/linux/mfd/wm8350/gpio.h
@@ -0,0 +1,342 @@
+/*
+ * gpio.h  --  GPIO Driver for Wolfson WM8350 PMIC
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC
+ *
+ *  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.
+ *
+ */
+
+#ifndef __LINUX_MFD_WM8350_GPIO_H_
+#define __LINUX_MFD_WM8350_GPIO_H_
+
+#include <linux/platform_device.h>
+
+/*
+ * GPIO Registers.
+ */
+#define WM8350_GPIO_DEBOUNCE                    0x80
+#define WM8350_GPIO_PIN_PULL_UP_CONTROL         0x81
+#define WM8350_GPIO_PULL_DOWN_CONTROL           0x82
+#define WM8350_GPIO_INT_MODE                    0x83
+#define WM8350_GPIO_CONTROL                     0x85
+#define WM8350_GPIO_CONFIGURATION_I_O           0x86
+#define WM8350_GPIO_PIN_POLARITY_TYPE           0x87
+#define WM8350_GPIO_FUNCTION_SELECT_1           0x8C
+#define WM8350_GPIO_FUNCTION_SELECT_2           0x8D
+#define WM8350_GPIO_FUNCTION_SELECT_3           0x8E
+#define WM8350_GPIO_FUNCTION_SELECT_4           0x8F
+
+/*
+ * GPIO Functions
+ */
+#define WM8350_GPIO0_GPIO_IN			0x0
+#define WM8350_GPIO0_GPIO_OUT			0x0
+#define WM8350_GPIO0_PWR_ON_IN			0x1
+#define WM8350_GPIO0_PWR_ON_OUT			0x1
+#define WM8350_GPIO0_LDO_EN_IN			0x2
+#define WM8350_GPIO0_VRTC_OUT			0x2
+#define WM8350_GPIO0_LPWR1_IN			0x3
+#define WM8350_GPIO0_POR_B_OUT			0x3
+
+#define WM8350_GPIO1_GPIO_IN			0x0
+#define WM8350_GPIO1_GPIO_OUT			0x0
+#define WM8350_GPIO1_PWR_ON_IN			0x1
+#define WM8350_GPIO1_DO_CONF_OUT		0x1
+#define WM8350_GPIO1_LDO_EN_IN			0x2
+#define WM8350_GPIO1_RESET_OUT			0x2
+#define WM8350_GPIO1_LPWR2_IN			0x3
+#define WM8350_GPIO1_MEMRST_OUT			0x3
+
+#define WM8350_GPIO2_GPIO_IN			0x0
+#define WM8350_GPIO2_GPIO_OUT			0x0
+#define WM8350_GPIO2_PWR_ON_IN			0x1
+#define WM8350_GPIO2_PWR_ON_OUT			0x1
+#define WM8350_GPIO2_WAKE_UP_IN			0x2
+#define WM8350_GPIO2_VRTC_OUT			0x2
+#define WM8350_GPIO2_32KHZ_IN			0x3
+#define WM8350_GPIO2_32KHZ_OUT			0x3
+
+#define WM8350_GPIO3_GPIO_IN			0x0
+#define WM8350_GPIO3_GPIO_OUT			0x0
+#define WM8350_GPIO3_PWR_ON_IN			0x1
+#define WM8350_GPIO3_P_CLK_OUT			0x1
+#define WM8350_GPIO3_LDO_EN_IN			0x2
+#define WM8350_GPIO3_VRTC_OUT			0x2
+#define WM8350_GPIO3_PWR_OFF_IN			0x3
+#define WM8350_GPIO3_32KHZ_OUT			0x3
+
+#define WM8350_GPIO4_GPIO_IN			0x0
+#define WM8350_GPIO4_GPIO_OUT			0x0
+#define WM8350_GPIO4_MR_IN			0x1
+#define WM8350_GPIO4_MEM_RST_OUT		0x1
+#define WM8350_GPIO4_FLASH_IN			0x2
+#define WM8350_GPIO4_ADA_OUT			0x2
+#define WM8350_GPIO4_HIBERNATE_IN		0x3
+#define WM8350_GPIO4_FLASH_OUT			0x3
+#define WM8350_GPIO4_MICDET_OUT			0x4
+#define WM8350_GPIO4_MICSHT_OUT			0x5
+
+#define WM8350_GPIO5_GPIO_IN			0x0
+#define WM8350_GPIO5_GPIO_OUT			0x0
+#define WM8350_GPIO5_LPWR1_IN			0x1
+#define WM8350_GPIO5_P_CLK_OUT			0x1
+#define WM8350_GPIO5_ADCLRCLK_IN		0x2
+#define WM8350_GPIO5_ADCLRCLK_OUT		0x2
+#define WM8350_GPIO5_HIBERNATE_IN		0x3
+#define WM8350_GPIO5_32KHZ_OUT			0x3
+#define WM8350_GPIO5_MICDET_OUT			0x4
+#define WM8350_GPIO5_MICSHT_OUT			0x5
+#define WM8350_GPIO5_ADA_OUT			0x6
+#define WM8350_GPIO5_OPCLK_OUT			0x7
+
+#define WM8350_GPIO6_GPIO_IN			0x0
+#define WM8350_GPIO6_GPIO_OUT			0x0
+#define WM8350_GPIO6_LPWR2_IN			0x1
+#define WM8350_GPIO6_MEMRST_OUT			0x1
+#define WM8350_GPIO6_FLASH_IN			0x2
+#define WM8350_GPIO6_ADA_OUT			0x2
+#define WM8350_GPIO6_HIBERNATE_IN		0x3
+#define WM8350_GPIO6_RTC_OUT			0x3
+#define WM8350_GPIO6_MICDET_OUT			0x4
+#define WM8350_GPIO6_MICSHT_OUT			0x5
+#define WM8350_GPIO6_ADCLRCLKB_OUT		0x6
+#define WM8350_GPIO6_SDOUT_OUT			0x7
+
+#define WM8350_GPIO7_GPIO_IN			0x0
+#define WM8350_GPIO7_GPIO_OUT			0x0
+#define WM8350_GPIO7_LPWR3_IN			0x1
+#define WM8350_GPIO7_P_CLK_OUT			0x1
+#define WM8350_GPIO7_MASK_IN			0x2
+#define WM8350_GPIO7_VCC_FAULT_OUT		0x2
+#define WM8350_GPIO7_HIBERNATE_IN		0x3
+#define WM8350_GPIO7_BATT_FAULT_OUT		0x3
+#define WM8350_GPIO7_MICDET_OUT			0x4
+#define WM8350_GPIO7_MICSHT_OUT			0x5
+#define WM8350_GPIO7_ADA_OUT			0x6
+#define WM8350_GPIO7_CSB_IN			0x7
+
+#define WM8350_GPIO8_GPIO_IN			0x0
+#define WM8350_GPIO8_GPIO_OUT			0x0
+#define WM8350_GPIO8_MR_IN			0x1
+#define WM8350_GPIO8_VCC_FAULT_OUT		0x1
+#define WM8350_GPIO8_ADCBCLK_IN			0x2
+#define WM8350_GPIO8_ADCBCLK_OUT		0x2
+#define WM8350_GPIO8_PWR_OFF_IN			0x3
+#define WM8350_GPIO8_BATT_FAULT_OUT		0x3
+#define WM8350_GPIO8_ALTSCL_IN			0xf
+
+#define WM8350_GPIO9_GPIO_IN			0x0
+#define WM8350_GPIO9_GPIO_OUT			0x0
+#define WM8350_GPIO9_HEARTBEAT_IN		0x1
+#define WM8350_GPIO9_VCC_FAULT_OUT		0x1
+#define WM8350_GPIO9_MASK_IN			0x2
+#define WM8350_GPIO9_LINE_GT_BATT_OUT		0x2
+#define WM8350_GPIO9_PWR_OFF_IN			0x3
+#define WM8350_GPIO9_BATT_FAULT_OUT		0x3
+#define WM8350_GPIO9_ALTSDA_OUT			0xf
+
+#define WM8350_GPIO10_GPIO_IN			0x0
+#define WM8350_GPIO10_GPIO_OUT			0x0
+#define WM8350_GPIO10_ISINKC_OUT		0x1
+#define WM8350_GPIO10_PWR_OFF_IN		0x2
+#define WM8350_GPIO10_LINE_GT_BATT_OUT		0x2
+#define WM8350_GPIO10_CHD_IND_IN		0x3
+
+#define WM8350_GPIO11_GPIO_IN			0x0
+#define WM8350_GPIO11_GPIO_OUT			0x0
+#define WM8350_GPIO11_ISINKD_OUT		0x1
+#define WM8350_GPIO11_WAKEUP_IN			0x2
+#define WM8350_GPIO11_LINE_GT_BATT_OUT		0x2
+#define WM8350_GPIO11_CHD_IND_IN		0x3
+
+#define WM8350_GPIO12_GPIO_IN			0x0
+#define WM8350_GPIO12_GPIO_OUT			0x0
+#define WM8350_GPIO12_ISINKE_OUT		0x1
+#define WM8350_GPIO12_LINE_GT_BATT_OUT		0x2
+#define WM8350_GPIO12_LINE_EN_OUT		0x3
+#define WM8350_GPIO12_32KHZ_OUT			0x4
+
+#define WM8350_GPIO_DIR_IN			0
+#define WM8350_GPIO_DIR_OUT			1
+#define WM8350_GPIO_ACTIVE_LOW			0
+#define WM8350_GPIO_ACTIVE_HIGH			1
+#define WM8350_GPIO_PULL_NONE			0
+#define WM8350_GPIO_PULL_UP			1
+#define WM8350_GPIO_PULL_DOWN			2
+#define WM8350_GPIO_INVERT_OFF			0
+#define WM8350_GPIO_INVERT_ON			1
+#define WM8350_GPIO_DEBOUNCE_OFF		0
+#define WM8350_GPIO_DEBOUNCE_ON			1
+
+/*
+ * R128 (0x80) - GPIO Debounce
+ */
+#define WM8350_GP12_DB                          0x1000
+#define WM8350_GP11_DB                          0x0800
+#define WM8350_GP10_DB                          0x0400
+#define WM8350_GP9_DB                           0x0200
+#define WM8350_GP8_DB                           0x0100
+#define WM8350_GP7_DB                           0x0080
+#define WM8350_GP6_DB                           0x0040
+#define WM8350_GP5_DB                           0x0020
+#define WM8350_GP4_DB                           0x0010
+#define WM8350_GP3_DB                           0x0008
+#define WM8350_GP2_DB                           0x0004
+#define WM8350_GP1_DB                           0x0002
+#define WM8350_GP0_DB                           0x0001
+
+/*
+ * R129 (0x81) - GPIO Pin pull up Control
+ */
+#define WM8350_GP12_PU                          0x1000
+#define WM8350_GP11_PU                          0x0800
+#define WM8350_GP10_PU                          0x0400
+#define WM8350_GP9_PU                           0x0200
+#define WM8350_GP8_PU                           0x0100
+#define WM8350_GP7_PU                           0x0080
+#define WM8350_GP6_PU                           0x0040
+#define WM8350_GP5_PU                           0x0020
+#define WM8350_GP4_PU                           0x0010
+#define WM8350_GP3_PU                           0x0008
+#define WM8350_GP2_PU                           0x0004
+#define WM8350_GP1_PU                           0x0002
+#define WM8350_GP0_PU                           0x0001
+
+/*
+ * R130 (0x82) - GPIO Pull down Control
+ */
+#define WM8350_GP12_PD                          0x1000
+#define WM8350_GP11_PD                          0x0800
+#define WM8350_GP10_PD                          0x0400
+#define WM8350_GP9_PD                           0x0200
+#define WM8350_GP8_PD                           0x0100
+#define WM8350_GP7_PD                           0x0080
+#define WM8350_GP6_PD                           0x0040
+#define WM8350_GP5_PD                           0x0020
+#define WM8350_GP4_PD                           0x0010
+#define WM8350_GP3_PD                           0x0008
+#define WM8350_GP2_PD                           0x0004
+#define WM8350_GP1_PD                           0x0002
+#define WM8350_GP0_PD                           0x0001
+
+/*
+ * R131 (0x83) - GPIO Interrupt Mode
+ */
+#define WM8350_GP12_INTMODE                     0x1000
+#define WM8350_GP11_INTMODE                     0x0800
+#define WM8350_GP10_INTMODE                     0x0400
+#define WM8350_GP9_INTMODE                      0x0200
+#define WM8350_GP8_INTMODE                      0x0100
+#define WM8350_GP7_INTMODE                      0x0080
+#define WM8350_GP6_INTMODE                      0x0040
+#define WM8350_GP5_INTMODE                      0x0020
+#define WM8350_GP4_INTMODE                      0x0010
+#define WM8350_GP3_INTMODE                      0x0008
+#define WM8350_GP2_INTMODE                      0x0004
+#define WM8350_GP1_INTMODE                      0x0002
+#define WM8350_GP0_INTMODE                      0x0001
+
+/*
+ * R133 (0x85) - GPIO Control
+ */
+#define WM8350_GP_DBTIME_MASK                   0x00C0
+
+/*
+ * R134 (0x86) - GPIO Configuration (i/o)
+ */
+#define WM8350_GP12_DIR                         0x1000
+#define WM8350_GP11_DIR                         0x0800
+#define WM8350_GP10_DIR                         0x0400
+#define WM8350_GP9_DIR                          0x0200
+#define WM8350_GP8_DIR                          0x0100
+#define WM8350_GP7_DIR                          0x0080
+#define WM8350_GP6_DIR                          0x0040
+#define WM8350_GP5_DIR                          0x0020
+#define WM8350_GP4_DIR                          0x0010
+#define WM8350_GP3_DIR                          0x0008
+#define WM8350_GP2_DIR                          0x0004
+#define WM8350_GP1_DIR                          0x0002
+#define WM8350_GP0_DIR                          0x0001
+
+/*
+ * R135 (0x87) - GPIO Pin Polarity / Type
+ */
+#define WM8350_GP12_CFG                         0x1000
+#define WM8350_GP11_CFG                         0x0800
+#define WM8350_GP10_CFG                         0x0400
+#define WM8350_GP9_CFG                          0x0200
+#define WM8350_GP8_CFG                          0x0100
+#define WM8350_GP7_CFG                          0x0080
+#define WM8350_GP6_CFG                          0x0040
+#define WM8350_GP5_CFG                          0x0020
+#define WM8350_GP4_CFG                          0x0010
+#define WM8350_GP3_CFG                          0x0008
+#define WM8350_GP2_CFG                          0x0004
+#define WM8350_GP1_CFG                          0x0002
+#define WM8350_GP0_CFG                          0x0001
+
+/*
+ * R140 (0x8C) - GPIO Function Select 1
+ */
+#define WM8350_GP3_FN_MASK                      0xF000
+#define WM8350_GP2_FN_MASK                      0x0F00
+#define WM8350_GP1_FN_MASK                      0x00F0
+#define WM8350_GP0_FN_MASK                      0x000F
+
+/*
+ * R141 (0x8D) - GPIO Function Select 2
+ */
+#define WM8350_GP7_FN_MASK                      0xF000
+#define WM8350_GP6_FN_MASK                      0x0F00
+#define WM8350_GP5_FN_MASK                      0x00F0
+#define WM8350_GP4_FN_MASK                      0x000F
+
+/*
+ * R142 (0x8E) - GPIO Function Select 3
+ */
+#define WM8350_GP11_FN_MASK                     0xF000
+#define WM8350_GP10_FN_MASK                     0x0F00
+#define WM8350_GP9_FN_MASK                      0x00F0
+#define WM8350_GP8_FN_MASK                      0x000F
+
+/*
+ * R143 (0x8F) - GPIO Function Select 4
+ */
+#define WM8350_GP12_FN_MASK                     0x000F
+
+/*
+ * R230 (0xE6) - GPIO Pin Status
+ */
+#define WM8350_GP12_LVL                         0x1000
+#define WM8350_GP11_LVL                         0x0800
+#define WM8350_GP10_LVL                         0x0400
+#define WM8350_GP9_LVL                          0x0200
+#define WM8350_GP8_LVL                          0x0100
+#define WM8350_GP7_LVL                          0x0080
+#define WM8350_GP6_LVL                          0x0040
+#define WM8350_GP5_LVL                          0x0020
+#define WM8350_GP4_LVL                          0x0010
+#define WM8350_GP3_LVL                          0x0008
+#define WM8350_GP2_LVL                          0x0004
+#define WM8350_GP1_LVL                          0x0002
+#define WM8350_GP0_LVL                          0x0001
+
+struct wm8350;
+
+int wm8350_gpio_config(struct wm8350 *wm8350, int gpio, int dir, int func,
+		       int pol, int pull, int invert, int debounce);
+
+struct wm8350_gpio {
+	struct platform_device *pdev;
+};
+
+/*
+ * GPIO Interrupts
+ */
+#define WM8350_IRQ_GPIO(x)                      (50 + x)
+
+#endif
diff --git a/include/linux/mfd/wm8350/pmic.h b/include/linux/mfd/wm8350/pmic.h
new file mode 100644
index 0000000..69b69e0
--- /dev/null
+++ b/include/linux/mfd/wm8350/pmic.h
@@ -0,0 +1,741 @@
+/*
+ * pmic.h  --  Power Managment Driver for Wolfson WM8350 PMIC
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC
+ *
+ *  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.
+ *
+ */
+
+#ifndef __LINUX_MFD_WM8350_PMIC_H
+#define __LINUX_MFD_WM8350_PMIC_H
+
+/*
+ * Register values.
+ */
+
+#define WM8350_CURRENT_SINK_DRIVER_A            0xAC
+#define WM8350_CSA_FLASH_CONTROL                0xAD
+#define WM8350_CURRENT_SINK_DRIVER_B            0xAE
+#define WM8350_CSB_FLASH_CONTROL                0xAF
+#define WM8350_DCDC_LDO_REQUESTED               0xB0
+#define WM8350_DCDC_ACTIVE_OPTIONS              0xB1
+#define WM8350_DCDC_SLEEP_OPTIONS               0xB2
+#define WM8350_POWER_CHECK_COMPARATOR           0xB3
+#define WM8350_DCDC1_CONTROL                    0xB4
+#define WM8350_DCDC1_TIMEOUTS                   0xB5
+#define WM8350_DCDC1_LOW_POWER                  0xB6
+#define WM8350_DCDC2_CONTROL                    0xB7
+#define WM8350_DCDC2_TIMEOUTS                   0xB8
+#define WM8350_DCDC3_CONTROL                    0xBA
+#define WM8350_DCDC3_TIMEOUTS                   0xBB
+#define WM8350_DCDC3_LOW_POWER                  0xBC
+#define WM8350_DCDC4_CONTROL                    0xBD
+#define WM8350_DCDC4_TIMEOUTS                   0xBE
+#define WM8350_DCDC4_LOW_POWER                  0xBF
+#define WM8350_DCDC5_CONTROL                    0xC0
+#define WM8350_DCDC5_TIMEOUTS                   0xC1
+#define WM8350_DCDC6_CONTROL                    0xC3
+#define WM8350_DCDC6_TIMEOUTS                   0xC4
+#define WM8350_DCDC6_LOW_POWER                  0xC5
+#define WM8350_LIMIT_SWITCH_CONTROL             0xC7
+#define WM8350_LDO1_CONTROL                     0xC8
+#define WM8350_LDO1_TIMEOUTS                    0xC9
+#define WM8350_LDO1_LOW_POWER                   0xCA
+#define WM8350_LDO2_CONTROL                     0xCB
+#define WM8350_LDO2_TIMEOUTS                    0xCC
+#define WM8350_LDO2_LOW_POWER                   0xCD
+#define WM8350_LDO3_CONTROL                     0xCE
+#define WM8350_LDO3_TIMEOUTS                    0xCF
+#define WM8350_LDO3_LOW_POWER                   0xD0
+#define WM8350_LDO4_CONTROL                     0xD1
+#define WM8350_LDO4_TIMEOUTS                    0xD2
+#define WM8350_LDO4_LOW_POWER                   0xD3
+#define WM8350_VCC_FAULT_MASKS                  0xD7
+#define WM8350_MAIN_BANDGAP_CONTROL             0xD8
+#define WM8350_OSC_CONTROL                      0xD9
+#define WM8350_RTC_TICK_CONTROL                 0xDA
+#define WM8350_SECURITY                         0xDB
+#define WM8350_RAM_BIST_1                       0xDC
+#define WM8350_DCDC_LDO_STATUS                  0xE1
+#define WM8350_GPIO_PIN_STATUS                  0xE6
+
+#define WM8350_DCDC1_FORCE_PWM                  0xF8
+#define WM8350_DCDC3_FORCE_PWM                  0xFA
+#define WM8350_DCDC4_FORCE_PWM                  0xFB
+#define WM8350_DCDC6_FORCE_PWM                  0xFD
+
+/*
+ * R172 (0xAC) - Current Sink Driver A
+ */
+#define WM8350_CS1_HIB_MODE                     0x1000
+#define WM8350_CS1_HIB_MODE_MASK                0x1000
+#define WM8350_CS1_HIB_MODE_SHIFT                   12
+#define WM8350_CS1_ISEL_MASK                    0x003F
+#define WM8350_CS1_ISEL_SHIFT                        0
+
+/* Bit values for R172 (0xAC) */
+#define WM8350_CS1_HIB_MODE_DISABLE                  0
+#define WM8350_CS1_HIB_MODE_LEAVE                    1
+
+#define WM8350_CS1_ISEL_220M                      0x3F
+
+/*
+ * R173 (0xAD) - CSA Flash control
+ */
+#define WM8350_CS1_FLASH_MODE                   0x8000
+#define WM8350_CS1_TRIGSRC                      0x4000
+#define WM8350_CS1_DRIVE                        0x2000
+#define WM8350_CS1_FLASH_DUR_MASK               0x0300
+#define WM8350_CS1_OFF_RAMP_MASK                0x0030
+#define WM8350_CS1_ON_RAMP_MASK                 0x0003
+
+/*
+ * R174 (0xAE) - Current Sink Driver B
+ */
+#define WM8350_CS2_HIB_MODE                     0x1000
+#define WM8350_CS2_ISEL_MASK                    0x003F
+
+/*
+ * R175 (0xAF) - CSB Flash control
+ */
+#define WM8350_CS2_FLASH_MODE                   0x8000
+#define WM8350_CS2_TRIGSRC                      0x4000
+#define WM8350_CS2_DRIVE                        0x2000
+#define WM8350_CS2_FLASH_DUR_MASK               0x0300
+#define WM8350_CS2_OFF_RAMP_MASK                0x0030
+#define WM8350_CS2_ON_RAMP_MASK                 0x0003
+
+/*
+ * R176 (0xB0) - DCDC/LDO requested
+ */
+#define WM8350_LS_ENA                           0x8000
+#define WM8350_LDO4_ENA                         0x0800
+#define WM8350_LDO3_ENA                         0x0400
+#define WM8350_LDO2_ENA                         0x0200
+#define WM8350_LDO1_ENA                         0x0100
+#define WM8350_DC6_ENA                          0x0020
+#define WM8350_DC5_ENA                          0x0010
+#define WM8350_DC4_ENA                          0x0008
+#define WM8350_DC3_ENA                          0x0004
+#define WM8350_DC2_ENA                          0x0002
+#define WM8350_DC1_ENA                          0x0001
+
+/*
+ * R177 (0xB1) - DCDC Active options
+ */
+#define WM8350_PUTO_MASK                        0x3000
+#define WM8350_PWRUP_DELAY_MASK                 0x0300
+#define WM8350_DC6_ACTIVE                       0x0020
+#define WM8350_DC4_ACTIVE                       0x0008
+#define WM8350_DC3_ACTIVE                       0x0004
+#define WM8350_DC1_ACTIVE                       0x0001
+
+/*
+ * R178 (0xB2) - DCDC Sleep options
+ */
+#define WM8350_DC6_SLEEP                        0x0020
+#define WM8350_DC4_SLEEP                        0x0008
+#define WM8350_DC3_SLEEP                        0x0004
+#define WM8350_DC1_SLEEP                        0x0001
+
+/*
+ * R179 (0xB3) - Power-check comparator
+ */
+#define WM8350_PCCMP_ERRACT                     0x4000
+#define WM8350_PCCMP_RAIL                       0x0100
+#define WM8350_PCCMP_OFF_THR_MASK               0x0070
+#define WM8350_PCCMP_ON_THR_MASK                0x0007
+
+/*
+ * R180 (0xB4) - DCDC1 Control
+ */
+#define WM8350_DC1_OPFLT                        0x0400
+#define WM8350_DC1_VSEL_MASK                    0x007F
+#define WM8350_DC1_VSEL_SHIFT                        0
+
+/*
+ * R181 (0xB5) - DCDC1 Timeouts
+ */
+#define WM8350_DC1_ERRACT_MASK                  0xC000
+#define WM8350_DC1_ERRACT_SHIFT                     14
+#define WM8350_DC1_ENSLOT_MASK                  0x3C00
+#define WM8350_DC1_ENSLOT_SHIFT                     10
+#define WM8350_DC1_SDSLOT_MASK                  0x03C0
+#define WM8350_DC1_UVTO_MASK                    0x0030
+#define WM8350_DC1_SDSLOT_SHIFT                      6
+
+/* Bit values for R181 (0xB5) */
+#define WM8350_DC1_ERRACT_NONE                       0
+#define WM8350_DC1_ERRACT_SHUTDOWN_CONV              1
+#define WM8350_DC1_ERRACT_SHUTDOWN_SYS               2
+
+/*
+ * R182 (0xB6) - DCDC1 Low Power
+ */
+#define WM8350_DC1_HIB_MODE_MASK                0x7000
+#define WM8350_DC1_HIB_TRIG_MASK                0x0300
+#define WM8350_DC1_VIMG_MASK                    0x007F
+
+/*
+ * R183 (0xB7) - DCDC2 Control
+ */
+#define WM8350_DC2_MODE                         0x4000
+#define WM8350_DC2_MODE_MASK                    0x4000
+#define WM8350_DC2_MODE_SHIFT                       14
+#define WM8350_DC2_HIB_MODE                     0x1000
+#define WM8350_DC2_HIB_MODE_MASK                0x1000
+#define WM8350_DC2_HIB_MODE_SHIFT                   12
+#define WM8350_DC2_HIB_TRIG_MASK                0x0300
+#define WM8350_DC2_HIB_TRIG_SHIFT                    8
+#define WM8350_DC2_ILIM                         0x0040
+#define WM8350_DC2_ILIM_MASK                    0x0040
+#define WM8350_DC2_ILIM_SHIFT                        6
+#define WM8350_DC2_RMP_MASK                     0x0018
+#define WM8350_DC2_RMP_SHIFT                         3
+#define WM8350_DC2_FBSRC_MASK                   0x0003
+#define WM8350_DC2_FBSRC_SHIFT                       0
+
+/* Bit values for R183 (0xB7) */
+#define WM8350_DC2_MODE_BOOST                        0
+#define WM8350_DC2_MODE_SWITCH                       1
+
+#define WM8350_DC2_HIB_MODE_ACTIVE                   1
+#define WM8350_DC2_HIB_MODE_DISABLE                  0
+
+#define WM8350_DC2_HIB_TRIG_NONE                     0
+#define WM8350_DC2_HIB_TRIG_LPWR1                    1
+#define WM8350_DC2_HIB_TRIG_LPWR2                    2
+#define WM8350_DC2_HIB_TRIG_LPWR3                    3
+
+#define WM8350_DC2_ILIM_HIGH                         0
+#define WM8350_DC2_ILIM_LOW                          1
+
+#define WM8350_DC2_RMP_30V                           0
+#define WM8350_DC2_RMP_20V                           1
+#define WM8350_DC2_RMP_10V                           2
+#define WM8350_DC2_RMP_5V                            3
+
+#define WM8350_DC2_FBSRC_FB2                         0
+#define WM8350_DC2_FBSRC_ISINKA                      1
+#define WM8350_DC2_FBSRC_ISINKB                      2
+#define WM8350_DC2_FBSRC_USB                         3
+
+/*
+ * R184 (0xB8) - DCDC2 Timeouts
+ */
+#define WM8350_DC2_ERRACT_MASK                  0xC000
+#define WM8350_DC2_ERRACT_SHIFT                     14
+#define WM8350_DC2_ENSLOT_MASK                  0x3C00
+#define WM8350_DC2_ENSLOT_SHIFT                     10
+#define WM8350_DC2_SDSLOT_MASK                  0x03C0
+#define WM8350_DC2_UVTO_MASK                    0x0030
+
+/* Bit values for R184 (0xB8) */
+#define WM8350_DC2_ERRACT_NONE                       0
+#define WM8350_DC2_ERRACT_SHUTDOWN_CONV              1
+#define WM8350_DC2_ERRACT_SHUTDOWN_SYS               2
+
+/*
+ * R186 (0xBA) - DCDC3 Control
+ */
+#define WM8350_DC3_OPFLT                        0x0400
+#define WM8350_DC3_VSEL_MASK                    0x007F
+#define WM8350_DC3_VSEL_SHIFT                        0
+
+/*
+ * R187 (0xBB) - DCDC3 Timeouts
+ */
+#define WM8350_DC3_ERRACT_MASK                  0xC000
+#define WM8350_DC3_ERRACT_SHIFT                     14
+#define WM8350_DC3_ENSLOT_MASK                  0x3C00
+#define WM8350_DC3_ENSLOT_SHIFT                     10
+#define WM8350_DC3_SDSLOT_MASK                  0x03C0
+#define WM8350_DC3_UVTO_MASK                    0x0030
+#define WM8350_DC3_SDSLOT_SHIFT                      6
+
+/* Bit values for R187 (0xBB) */
+#define WM8350_DC3_ERRACT_NONE                       0
+#define WM8350_DC3_ERRACT_SHUTDOWN_CONV              1
+#define WM8350_DC3_ERRACT_SHUTDOWN_SYS               2
+/*
+ * R188 (0xBC) - DCDC3 Low Power
+ */
+#define WM8350_DC3_HIB_MODE_MASK                0x7000
+#define WM8350_DC3_HIB_TRIG_MASK                0x0300
+#define WM8350_DC3_VIMG_MASK                    0x007F
+
+/*
+ * R189 (0xBD) - DCDC4 Control
+ */
+#define WM8350_DC4_OPFLT                        0x0400
+#define WM8350_DC4_VSEL_MASK                    0x007F
+#define WM8350_DC4_VSEL_SHIFT                        0
+
+/*
+ * R190 (0xBE) - DCDC4 Timeouts
+ */
+#define WM8350_DC4_ERRACT_MASK                  0xC000
+#define WM8350_DC4_ERRACT_SHIFT                     14
+#define WM8350_DC4_ENSLOT_MASK                  0x3C00
+#define WM8350_DC4_ENSLOT_SHIFT                     10
+#define WM8350_DC4_SDSLOT_MASK                  0x03C0
+#define WM8350_DC4_UVTO_MASK                    0x0030
+#define WM8350_DC4_SDSLOT_SHIFT                      6
+
+/* Bit values for R190 (0xBE) */
+#define WM8350_DC4_ERRACT_NONE                       0
+#define WM8350_DC4_ERRACT_SHUTDOWN_CONV              1
+#define WM8350_DC4_ERRACT_SHUTDOWN_SYS               2
+
+/*
+ * R191 (0xBF) - DCDC4 Low Power
+ */
+#define WM8350_DC4_HIB_MODE_MASK                0x7000
+#define WM8350_DC4_HIB_TRIG_MASK                0x0300
+#define WM8350_DC4_VIMG_MASK                    0x007F
+
+/*
+ * R192 (0xC0) - DCDC5 Control
+ */
+#define WM8350_DC5_MODE                         0x4000
+#define WM8350_DC5_MODE_MASK                    0x4000
+#define WM8350_DC5_MODE_SHIFT                       14
+#define WM8350_DC5_HIB_MODE                     0x1000
+#define WM8350_DC5_HIB_MODE_MASK                0x1000
+#define WM8350_DC5_HIB_MODE_SHIFT                   12
+#define WM8350_DC5_HIB_TRIG_MASK                0x0300
+#define WM8350_DC5_HIB_TRIG_SHIFT                    8
+#define WM8350_DC5_ILIM                         0x0040
+#define WM8350_DC5_ILIM_MASK                    0x0040
+#define WM8350_DC5_ILIM_SHIFT                        6
+#define WM8350_DC5_RMP_MASK                     0x0018
+#define WM8350_DC5_RMP_SHIFT                         3
+#define WM8350_DC5_FBSRC_MASK                   0x0003
+#define WM8350_DC5_FBSRC_SHIFT                       0
+
+/* Bit values for R192 (0xC0) */
+#define WM8350_DC5_MODE_BOOST                        0
+#define WM8350_DC5_MODE_SWITCH                       1
+
+#define WM8350_DC5_HIB_MODE_ACTIVE                   1
+#define WM8350_DC5_HIB_MODE_DISABLE                  0
+
+#define WM8350_DC5_HIB_TRIG_NONE                     0
+#define WM8350_DC5_HIB_TRIG_LPWR1                    1
+#define WM8350_DC5_HIB_TRIG_LPWR2                    2
+#define WM8350_DC5_HIB_TRIG_LPWR3                    3
+
+#define WM8350_DC5_ILIM_HIGH                         0
+#define WM8350_DC5_ILIM_LOW                          1
+
+#define WM8350_DC5_RMP_30V                           0
+#define WM8350_DC5_RMP_20V                           1
+#define WM8350_DC5_RMP_10V                           2
+#define WM8350_DC5_RMP_5V                            3
+
+#define WM8350_DC5_FBSRC_FB2                         0
+#define WM8350_DC5_FBSRC_ISINKA                      1
+#define WM8350_DC5_FBSRC_ISINKB                      2
+#define WM8350_DC5_FBSRC_USB                         3
+
+/*
+ * R193 (0xC1) - DCDC5 Timeouts
+ */
+#define WM8350_DC5_ERRACT_MASK                  0xC000
+#define WM8350_DC5_ERRACT_SHIFT                     14
+#define WM8350_DC5_ENSLOT_MASK                  0x3C00
+#define WM8350_DC5_ENSLOT_SHIFT                     10
+#define WM8350_DC5_SDSLOT_MASK                  0x03C0
+#define WM8350_DC5_UVTO_MASK                    0x0030
+#define WM8350_DC5_SDSLOT_SHIFT                      6
+
+/* Bit values for R193 (0xC1) */
+#define WM8350_DC5_ERRACT_NONE                       0
+#define WM8350_DC5_ERRACT_SHUTDOWN_CONV              1
+#define WM8350_DC5_ERRACT_SHUTDOWN_SYS               2
+
+/*
+ * R195 (0xC3) - DCDC6 Control
+ */
+#define WM8350_DC6_OPFLT                        0x0400
+#define WM8350_DC6_VSEL_MASK                    0x007F
+#define WM8350_DC6_VSEL_SHIFT                        0
+
+/*
+ * R196 (0xC4) - DCDC6 Timeouts
+ */
+#define WM8350_DC6_ERRACT_MASK                  0xC000
+#define WM8350_DC6_ERRACT_SHIFT                     14
+#define WM8350_DC6_ENSLOT_MASK                  0x3C00
+#define WM8350_DC6_ENSLOT_SHIFT                     10
+#define WM8350_DC6_SDSLOT_MASK                  0x03C0
+#define WM8350_DC6_UVTO_MASK                    0x0030
+#define WM8350_DC6_SDSLOT_SHIFT                      6
+
+/* Bit values for R196 (0xC4) */
+#define WM8350_DC6_ERRACT_NONE                       0
+#define WM8350_DC6_ERRACT_SHUTDOWN_CONV              1
+#define WM8350_DC6_ERRACT_SHUTDOWN_SYS               2
+
+/*
+ * R197 (0xC5) - DCDC6 Low Power
+ */
+#define WM8350_DC6_HIB_MODE_MASK                0x7000
+#define WM8350_DC6_HIB_TRIG_MASK                0x0300
+#define WM8350_DC6_VIMG_MASK                    0x007F
+
+/*
+ * R199 (0xC7) - Limit Switch Control
+ */
+#define WM8350_LS_ERRACT_MASK                   0xC000
+#define WM8350_LS_ERRACT_SHIFT                      14
+#define WM8350_LS_ENSLOT_MASK                   0x3C00
+#define WM8350_LS_ENSLOT_SHIFT                      10
+#define WM8350_LS_SDSLOT_MASK                   0x03C0
+#define WM8350_LS_SDSLOT_SHIFT                       6
+#define WM8350_LS_HIB_MODE                      0x0010
+#define WM8350_LS_HIB_MODE_MASK                 0x0010
+#define WM8350_LS_HIB_MODE_SHIFT                     4
+#define WM8350_LS_HIB_PROT                      0x0002
+#define WM8350_LS_HIB_PROT_MASK                 0x0002
+#define WM8350_LS_HIB_PROT_SHIFT                     1
+#define WM8350_LS_PROT                          0x0001
+#define WM8350_LS_PROT_MASK                     0x0001
+#define WM8350_LS_PROT_SHIFT                         0
+
+/* Bit values for R199 (0xC7) */
+#define WM8350_LS_ERRACT_NONE                       0
+#define WM8350_LS_ERRACT_SHUTDOWN_CONV              1
+#define WM8350_LS_ERRACT_SHUTDOWN_SYS               2
+
+/*
+ * R200 (0xC8) - LDO1 Control
+ */
+#define WM8350_LDO1_SWI                         0x4000
+#define WM8350_LDO1_OPFLT                       0x0400
+#define WM8350_LDO1_VSEL_MASK                   0x001F
+#define WM8350_LDO1_VSEL_SHIFT                       0
+
+/*
+ * R201 (0xC9) - LDO1 Timeouts
+ */
+#define WM8350_LDO1_ERRACT_MASK                 0xC000
+#define WM8350_LDO1_ERRACT_SHIFT                    14
+#define WM8350_LDO1_ENSLOT_MASK                 0x3C00
+#define WM8350_LDO1_ENSLOT_SHIFT                    10
+#define WM8350_LDO1_SDSLOT_MASK                 0x03C0
+#define WM8350_LDO1_UVTO_MASK                   0x0030
+#define WM8350_LDO1_SDSLOT_SHIFT                     6
+
+/* Bit values for R201 (0xC9) */
+#define WM8350_LDO1_ERRACT_NONE                       0
+#define WM8350_LDO1_ERRACT_SHUTDOWN_CONV              1
+#define WM8350_LDO1_ERRACT_SHUTDOWN_SYS               2
+
+/*
+ * R202 (0xCA) - LDO1 Low Power
+ */
+#define WM8350_LDO1_HIB_MODE_MASK               0x3000
+#define WM8350_LDO1_HIB_TRIG_MASK               0x0300
+#define WM8350_LDO1_VIMG_MASK                   0x001F
+#define WM8350_LDO1_HIB_MODE_DIS		(0x1 << 12)
+
+
+/*
+ * R203 (0xCB) - LDO2 Control
+ */
+#define WM8350_LDO2_SWI                         0x4000
+#define WM8350_LDO2_OPFLT                       0x0400
+#define WM8350_LDO2_VSEL_MASK                   0x001F
+#define WM8350_LDO2_VSEL_SHIFT                       0
+
+/*
+ * R204 (0xCC) - LDO2 Timeouts
+ */
+#define WM8350_LDO2_ERRACT_MASK                 0xC000
+#define WM8350_LDO2_ERRACT_SHIFT                    14
+#define WM8350_LDO2_ENSLOT_MASK                 0x3C00
+#define WM8350_LDO2_ENSLOT_SHIFT                    10
+#define WM8350_LDO2_SDSLOT_MASK                 0x03C0
+#define WM8350_LDO2_SDSLOT_SHIFT                     6
+
+/* Bit values for R204 (0xCC) */
+#define WM8350_LDO2_ERRACT_NONE                       0
+#define WM8350_LDO2_ERRACT_SHUTDOWN_CONV              1
+#define WM8350_LDO2_ERRACT_SHUTDOWN_SYS               2
+
+/*
+ * R205 (0xCD) - LDO2 Low Power
+ */
+#define WM8350_LDO2_HIB_MODE_MASK               0x3000
+#define WM8350_LDO2_HIB_TRIG_MASK               0x0300
+#define WM8350_LDO2_VIMG_MASK                   0x001F
+
+/*
+ * R206 (0xCE) - LDO3 Control
+ */
+#define WM8350_LDO3_SWI                         0x4000
+#define WM8350_LDO3_OPFLT                       0x0400
+#define WM8350_LDO3_VSEL_MASK                   0x001F
+#define WM8350_LDO3_VSEL_SHIFT                       0
+
+/*
+ * R207 (0xCF) - LDO3 Timeouts
+ */
+#define WM8350_LDO3_ERRACT_MASK                 0xC000
+#define WM8350_LDO3_ERRACT_SHIFT                    14
+#define WM8350_LDO3_ENSLOT_MASK                 0x3C00
+#define WM8350_LDO3_ENSLOT_SHIFT                    10
+#define WM8350_LDO3_SDSLOT_MASK                 0x03C0
+#define WM8350_LDO3_UVTO_MASK                   0x0030
+#define WM8350_LDO3_SDSLOT_SHIFT                     6
+
+/* Bit values for R207 (0xCF) */
+#define WM8350_LDO3_ERRACT_NONE                       0
+#define WM8350_LDO3_ERRACT_SHUTDOWN_CONV              1
+#define WM8350_LDO3_ERRACT_SHUTDOWN_SYS               2
+
+/*
+ * R208 (0xD0) - LDO3 Low Power
+ */
+#define WM8350_LDO3_HIB_MODE_MASK               0x3000
+#define WM8350_LDO3_HIB_TRIG_MASK               0x0300
+#define WM8350_LDO3_VIMG_MASK                   0x001F
+
+/*
+ * R209 (0xD1) - LDO4 Control
+ */
+#define WM8350_LDO4_SWI                         0x4000
+#define WM8350_LDO4_OPFLT                       0x0400
+#define WM8350_LDO4_VSEL_MASK                   0x001F
+#define WM8350_LDO4_VSEL_SHIFT                       0
+
+/*
+ * R210 (0xD2) - LDO4 Timeouts
+ */
+#define WM8350_LDO4_ERRACT_MASK                 0xC000
+#define WM8350_LDO4_ERRACT_SHIFT                    14
+#define WM8350_LDO4_ENSLOT_MASK                 0x3C00
+#define WM8350_LDO4_ENSLOT_SHIFT                    10
+#define WM8350_LDO4_SDSLOT_MASK                 0x03C0
+#define WM8350_LDO4_UVTO_MASK                   0x0030
+#define WM8350_LDO4_SDSLOT_SHIFT                     6
+
+/* Bit values for R210 (0xD2) */
+#define WM8350_LDO4_ERRACT_NONE                       0
+#define WM8350_LDO4_ERRACT_SHUTDOWN_CONV              1
+#define WM8350_LDO4_ERRACT_SHUTDOWN_SYS               2
+
+/*
+ * R211 (0xD3) - LDO4 Low Power
+ */
+#define WM8350_LDO4_HIB_MODE_MASK               0x3000
+#define WM8350_LDO4_HIB_TRIG_MASK               0x0300
+#define WM8350_LDO4_VIMG_MASK                   0x001F
+
+/*
+ * R215 (0xD7) - VCC_FAULT Masks
+ */
+#define WM8350_LS_FAULT                         0x8000
+#define WM8350_LDO4_FAULT                       0x0800
+#define WM8350_LDO3_FAULT                       0x0400
+#define WM8350_LDO2_FAULT                       0x0200
+#define WM8350_LDO1_FAULT                       0x0100
+#define WM8350_DC6_FAULT                        0x0020
+#define WM8350_DC5_FAULT                        0x0010
+#define WM8350_DC4_FAULT                        0x0008
+#define WM8350_DC3_FAULT                        0x0004
+#define WM8350_DC2_FAULT                        0x0002
+#define WM8350_DC1_FAULT                        0x0001
+
+/*
+ * R216 (0xD8) - Main Bandgap Control
+ */
+#define WM8350_MBG_LOAD_FUSES                   0x8000
+#define WM8350_MBG_FUSE_WPREP                   0x4000
+#define WM8350_MBG_FUSE_WRITE                   0x2000
+#define WM8350_MBG_FUSE_TRIM_MASK               0x1F00
+#define WM8350_MBG_TRIM_SRC                     0x0020
+#define WM8350_MBG_USER_TRIM_MASK               0x001F
+
+/*
+ * R217 (0xD9) - OSC Control
+ */
+#define WM8350_OSC_LOAD_FUSES                   0x8000
+#define WM8350_OSC_FUSE_WPREP                   0x4000
+#define WM8350_OSC_FUSE_WRITE                   0x2000
+#define WM8350_OSC_FUSE_TRIM_MASK               0x0F00
+#define WM8350_OSC_TRIM_SRC                     0x0020
+#define WM8350_OSC_USER_TRIM_MASK               0x000F
+
+/*
+ * R248 (0xF8) - DCDC1 Force PWM
+ */
+#define WM8350_DCDC1_FORCE_PWM_ENA              0x0010
+
+/*
+ * R250 (0xFA) - DCDC3 Force PWM
+ */
+#define WM8350_DCDC3_FORCE_PWM_ENA              0x0010
+
+/*
+ * R251 (0xFB) - DCDC4 Force PWM
+ */
+#define WM8350_DCDC4_FORCE_PWM_ENA              0x0010
+
+/*
+ * R253 (0xFD) - DCDC1 Force PWM
+ */
+#define WM8350_DCDC6_FORCE_PWM_ENA              0x0010
+
+/*
+ * DCDC's
+ */
+#define WM8350_DCDC_1				0
+#define WM8350_DCDC_2				1
+#define WM8350_DCDC_3				2
+#define WM8350_DCDC_4				3
+#define WM8350_DCDC_5				4
+#define WM8350_DCDC_6				5
+
+/* DCDC modes */
+#define WM8350_DCDC_ACTIVE_STANDBY		0
+#define WM8350_DCDC_ACTIVE_PULSE		1
+#define WM8350_DCDC_SLEEP_NORMAL		0
+#define WM8350_DCDC_SLEEP_LOW			1
+
+/* DCDC Low power (Hibernate) mode */
+#define WM8350_DCDC_HIB_MODE_CUR		(0 << 12)
+#define WM8350_DCDC_HIB_MODE_IMAGE		(1 << 12)
+#define WM8350_DCDC_HIB_MODE_STANDBY		(2 << 12)
+#define WM8350_DCDC_HIB_MODE_LDO		(4 << 12)
+#define WM8350_DCDC_HIB_MODE_LDO_IM		(5 << 12)
+#define WM8350_DCDC_HIB_MODE_DIS		(7 << 12)
+#define WM8350_DCDC_HIB_MODE_MASK		(7 << 12)
+
+/* DCDC Low Power (Hibernate) signal */
+#define WM8350_DCDC_HIB_SIG_REG			(0 << 8)
+#define WM8350_DCDC_HIB_SIG_LPWR1		(1 << 8)
+#define WM8350_DCDC_HIB_SIG_LPWR2		(2 << 8)
+#define WM8350_DCDC_HIB_SIG_LPWR3		(3 << 8)
+
+/* LDO Low power (Hibernate) mode */
+#define WM8350_LDO_HIB_MODE_IMAGE		(0 << 0)
+#define WM8350_LDO_HIB_MODE_DIS			(1 << 0)
+
+/* LDO Low Power (Hibernate) signal */
+#define WM8350_LDO_HIB_SIG_REG			(0 << 8)
+#define WM8350_LDO_HIB_SIG_LPWR1		(1 << 8)
+#define WM8350_LDO_HIB_SIG_LPWR2		(2 << 8)
+#define WM8350_LDO_HIB_SIG_LPWR3		(3 << 8)
+
+/*
+ * LDOs
+ */
+#define WM8350_LDO_1				6
+#define WM8350_LDO_2				7
+#define WM8350_LDO_3				8
+#define WM8350_LDO_4				9
+
+/*
+ * ISINKs
+ */
+#define WM8350_ISINK_A				10
+#define WM8350_ISINK_B				11
+
+#define WM8350_ISINK_MODE_BOOST			0
+#define WM8350_ISINK_MODE_SWITCH		1
+#define WM8350_ISINK_ILIM_NORMAL		0
+#define WM8350_ISINK_ILIM_LOW			1
+
+#define WM8350_ISINK_FLASH_DISABLE		0
+#define WM8350_ISINK_FLASH_ENABLE		1
+#define WM8350_ISINK_FLASH_TRIG_BIT		0
+#define WM8350_ISINK_FLASH_TRIG_GPIO		1
+#define WM8350_ISINK_FLASH_MODE_EN		(1 << 13)
+#define WM8350_ISINK_FLASH_MODE_DIS		(0 << 13)
+#define WM8350_ISINK_FLASH_DUR_32MS		(0 << 8)
+#define WM8350_ISINK_FLASH_DUR_64MS		(1 << 8)
+#define WM8350_ISINK_FLASH_DUR_96MS		(2 << 8)
+#define WM8350_ISINK_FLASH_DUR_1024MS		(3 << 8)
+#define WM8350_ISINK_FLASH_ON_INSTANT		(0 << 4)
+#define WM8350_ISINK_FLASH_ON_0_25S		(1 << 4)
+#define WM8350_ISINK_FLASH_ON_0_50S		(2 << 4)
+#define WM8350_ISINK_FLASH_ON_1_00S		(3 << 4)
+#define WM8350_ISINK_FLASH_ON_1_95S		(1 << 4)
+#define WM8350_ISINK_FLASH_ON_3_91S		(2 << 4)
+#define WM8350_ISINK_FLASH_ON_7_80S		(3 << 4)
+#define WM8350_ISINK_FLASH_OFF_INSTANT		(0 << 0)
+#define WM8350_ISINK_FLASH_OFF_0_25S		(1 << 0)
+#define WM8350_ISINK_FLASH_OFF_0_50S		(2 << 0)
+#define WM8350_ISINK_FLASH_OFF_1_00S		(3 << 0)
+#define WM8350_ISINK_FLASH_OFF_1_95S		(1 << 0)
+#define WM8350_ISINK_FLASH_OFF_3_91S		(2 << 0)
+#define WM8350_ISINK_FLASH_OFF_7_80S		(3 << 0)
+
+/*
+ * Regulator Interrupts.
+ */
+#define WM8350_IRQ_CS1				13
+#define WM8350_IRQ_CS2				14
+#define WM8350_IRQ_UV_LDO4			25
+#define WM8350_IRQ_UV_LDO3			26
+#define WM8350_IRQ_UV_LDO2			27
+#define WM8350_IRQ_UV_LDO1			28
+#define WM8350_IRQ_UV_DC6			29
+#define WM8350_IRQ_UV_DC5			30
+#define WM8350_IRQ_UV_DC4			31
+#define WM8350_IRQ_UV_DC3			32
+#define WM8350_IRQ_UV_DC2			33
+#define WM8350_IRQ_UV_DC1			34
+#define WM8350_IRQ_OC_LS			35
+
+#define NUM_WM8350_REGULATORS			12
+
+struct wm8350;
+struct platform_device;
+struct regulator_init_data;
+
+struct wm8350_pmic {
+	/* ISINK to DCDC mapping */
+	int isink_A_dcdc;
+	int isink_B_dcdc;
+
+	/* hibernate configs */
+	u16 dcdc1_hib_mode;
+	u16 dcdc3_hib_mode;
+	u16 dcdc4_hib_mode;
+	u16 dcdc6_hib_mode;
+
+	/* regulator devices */
+	struct platform_device *pdev[NUM_WM8350_REGULATORS];
+};
+
+int wm8350_register_regulator(struct wm8350 *wm8350, int reg,
+			      struct regulator_init_data *initdata);
+
+/*
+ * Additional DCDC control not supported via regulator API
+ */
+int wm8350_dcdc_set_slot(struct wm8350 *wm8350, int dcdc, u16 start,
+			 u16 stop, u16 fault);
+int wm8350_dcdc25_set_mode(struct wm8350 *wm8350, int dcdc, u16 mode,
+			   u16 ilim, u16 ramp, u16 feedback);
+
+/*
+ * Additional LDO control not supported via regulator API
+ */
+int wm8350_ldo_set_slot(struct wm8350 *wm8350, int ldo, u16 start, u16 stop);
+
+/*
+ * Additional ISINK control not supported via regulator API
+ */
+int wm8350_isink_set_flash(struct wm8350 *wm8350, int isink, u16 mode,
+			   u16 trigger, u16 duration, u16 on_ramp,
+			   u16 off_ramp, u16 drive);
+
+#endif
diff --git a/include/linux/mfd/wm8350/rtc.h b/include/linux/mfd/wm8350/rtc.h
new file mode 100644
index 0000000..dfda69e
--- /dev/null
+++ b/include/linux/mfd/wm8350/rtc.h
@@ -0,0 +1,266 @@
+/*
+ * rtc.h  --  RTC driver for Wolfson WM8350 PMIC
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC
+ *
+ *  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.
+ */
+
+#ifndef __LINUX_MFD_WM8350_RTC_H
+#define __LINUX_MFD_WM8350_RTC_H
+
+#include <linux/platform_device.h>
+
+/*
+ * Register values.
+ */
+#define WM8350_RTC_SECONDS_MINUTES              0x10
+#define WM8350_RTC_HOURS_DAY                    0x11
+#define WM8350_RTC_DATE_MONTH                   0x12
+#define WM8350_RTC_YEAR                         0x13
+#define WM8350_ALARM_SECONDS_MINUTES            0x14
+#define WM8350_ALARM_HOURS_DAY                  0x15
+#define WM8350_ALARM_DATE_MONTH                 0x16
+#define WM8350_RTC_TIME_CONTROL                 0x17
+
+/*
+ * R16 (0x10) - RTC Seconds/Minutes
+ */
+#define WM8350_RTC_MINS_MASK                    0x7F00
+#define WM8350_RTC_MINS_SHIFT                        8
+#define WM8350_RTC_SECS_MASK                    0x007F
+#define WM8350_RTC_SECS_SHIFT                        0
+
+/*
+ * R17 (0x11) - RTC Hours/Day
+ */
+#define WM8350_RTC_DAY_MASK                     0x0700
+#define WM8350_RTC_DAY_SHIFT                         8
+#define WM8350_RTC_HPM_MASK                     0x0020
+#define WM8350_RTC_HPM_SHIFT                         5
+#define WM8350_RTC_HRS_MASK                     0x001F
+#define WM8350_RTC_HRS_SHIFT                         0
+
+/* Bit values for R21 (0x15) */
+#define WM8350_RTC_DAY_SUN                           1
+#define WM8350_RTC_DAY_MON                           2
+#define WM8350_RTC_DAY_TUE                           3
+#define WM8350_RTC_DAY_WED                           4
+#define WM8350_RTC_DAY_THU                           5
+#define WM8350_RTC_DAY_FRI                           6
+#define WM8350_RTC_DAY_SAT                           7
+
+#define WM8350_RTC_HPM_AM                            0
+#define WM8350_RTC_HPM_PM                            1
+
+/*
+ * R18 (0x12) - RTC Date/Month
+ */
+#define WM8350_RTC_MTH_MASK                     0x1F00
+#define WM8350_RTC_MTH_SHIFT                         8
+#define WM8350_RTC_DATE_MASK                    0x003F
+#define WM8350_RTC_DATE_SHIFT                        0
+
+/* Bit values for R22 (0x16) */
+#define WM8350_RTC_MTH_JAN                           1
+#define WM8350_RTC_MTH_FEB                           2
+#define WM8350_RTC_MTH_MAR                           3
+#define WM8350_RTC_MTH_APR                           4
+#define WM8350_RTC_MTH_MAY                           5
+#define WM8350_RTC_MTH_JUN                           6
+#define WM8350_RTC_MTH_JUL                           7
+#define WM8350_RTC_MTH_AUG                           8
+#define WM8350_RTC_MTH_SEP                           9
+#define WM8350_RTC_MTH_OCT                          10
+#define WM8350_RTC_MTH_NOV                          11
+#define WM8350_RTC_MTH_DEC                          12
+#define WM8350_RTC_MTH_JAN_BCD                    0x01
+#define WM8350_RTC_MTH_FEB_BCD                    0x02
+#define WM8350_RTC_MTH_MAR_BCD                    0x03
+#define WM8350_RTC_MTH_APR_BCD                    0x04
+#define WM8350_RTC_MTH_MAY_BCD                    0x05
+#define WM8350_RTC_MTH_JUN_BCD                    0x06
+#define WM8350_RTC_MTH_JUL_BCD                    0x07
+#define WM8350_RTC_MTH_AUG_BCD                    0x08
+#define WM8350_RTC_MTH_SEP_BCD                    0x09
+#define WM8350_RTC_MTH_OCT_BCD                    0x10
+#define WM8350_RTC_MTH_NOV_BCD                    0x11
+#define WM8350_RTC_MTH_DEC_BCD                    0x12
+
+/*
+ * R19 (0x13) - RTC Year
+ */
+#define WM8350_RTC_YHUNDREDS_MASK               0x3F00
+#define WM8350_RTC_YHUNDREDS_SHIFT                   8
+#define WM8350_RTC_YUNITS_MASK                  0x00FF
+#define WM8350_RTC_YUNITS_SHIFT                      0
+
+/*
+ * R20 (0x14) - Alarm Seconds/Minutes
+ */
+#define WM8350_RTC_ALMMINS_MASK                 0x7F00
+#define WM8350_RTC_ALMMINS_SHIFT                     8
+#define WM8350_RTC_ALMSECS_MASK                 0x007F
+#define WM8350_RTC_ALMSECS_SHIFT                     0
+
+/* Bit values for R20 (0x14) */
+#define WM8350_RTC_ALMMINS_DONT_CARE                -1
+#define WM8350_RTC_ALMSECS_DONT_CARE                -1
+
+/*
+ * R21 (0x15) - Alarm Hours/Day
+ */
+#define WM8350_RTC_ALMDAY_MASK                  0x0F00
+#define WM8350_RTC_ALMDAY_SHIFT                      8
+#define WM8350_RTC_ALMHPM_MASK                  0x0020
+#define WM8350_RTC_ALMHPM_SHIFT                      5
+#define WM8350_RTC_ALMHRS_MASK                  0x001F
+#define WM8350_RTC_ALMHRS_SHIFT                      0
+
+/* Bit values for R21 (0x15) */
+#define WM8350_RTC_ALMDAY_DONT_CARE                 -1
+#define WM8350_RTC_ALMDAY_SUN                        1
+#define WM8350_RTC_ALMDAY_MON                        2
+#define WM8350_RTC_ALMDAY_TUE                        3
+#define WM8350_RTC_ALMDAY_WED                        4
+#define WM8350_RTC_ALMDAY_THU                        5
+#define WM8350_RTC_ALMDAY_FRI                        6
+#define WM8350_RTC_ALMDAY_SAT                        7
+
+#define WM8350_RTC_ALMHPM_AM                         0
+#define WM8350_RTC_ALMHPM_PM                         1
+
+#define WM8350_RTC_ALMHRS_DONT_CARE                 -1
+
+/*
+ * R22 (0x16) - Alarm Date/Month
+ */
+#define WM8350_RTC_ALMMTH_MASK                  0x1F00
+#define WM8350_RTC_ALMMTH_SHIFT                      8
+#define WM8350_RTC_ALMDATE_MASK                 0x003F
+#define WM8350_RTC_ALMDATE_SHIFT                     0
+
+/* Bit values for R22 (0x16) */
+#define WM8350_RTC_ALMDATE_DONT_CARE                -1
+
+#define WM8350_RTC_ALMMTH_DONT_CARE                 -1
+#define WM8350_RTC_ALMMTH_JAN                        1
+#define WM8350_RTC_ALMMTH_FEB                        2
+#define WM8350_RTC_ALMMTH_MAR                        3
+#define WM8350_RTC_ALMMTH_APR                        4
+#define WM8350_RTC_ALMMTH_MAY                        5
+#define WM8350_RTC_ALMMTH_JUN                        6
+#define WM8350_RTC_ALMMTH_JUL                        7
+#define WM8350_RTC_ALMMTH_AUG                        8
+#define WM8350_RTC_ALMMTH_SEP                        9
+#define WM8350_RTC_ALMMTH_OCT                       10
+#define WM8350_RTC_ALMMTH_NOV                       11
+#define WM8350_RTC_ALMMTH_DEC                       12
+#define WM8350_RTC_ALMMTH_JAN_BCD                 0x01
+#define WM8350_RTC_ALMMTH_FEB_BCD                 0x02
+#define WM8350_RTC_ALMMTH_MAR_BCD                 0x03
+#define WM8350_RTC_ALMMTH_APR_BCD                 0x04
+#define WM8350_RTC_ALMMTH_MAY_BCD                 0x05
+#define WM8350_RTC_ALMMTH_JUN_BCD                 0x06
+#define WM8350_RTC_ALMMTH_JUL_BCD                 0x07
+#define WM8350_RTC_ALMMTH_AUG_BCD                 0x08
+#define WM8350_RTC_ALMMTH_SEP_BCD                 0x09
+#define WM8350_RTC_ALMMTH_OCT_BCD                 0x10
+#define WM8350_RTC_ALMMTH_NOV_BCD                 0x11
+#define WM8350_RTC_ALMMTH_DEC_BCD                 0x12
+
+/*
+ * R23 (0x17) - RTC Time Control
+ */
+#define WM8350_RTC_BCD                          0x8000
+#define WM8350_RTC_BCD_MASK                     0x8000
+#define WM8350_RTC_BCD_SHIFT                        15
+#define WM8350_RTC_12HR                         0x4000
+#define WM8350_RTC_12HR_MASK                    0x4000
+#define WM8350_RTC_12HR_SHIFT                       14
+#define WM8350_RTC_DST                          0x2000
+#define WM8350_RTC_DST_MASK                     0x2000
+#define WM8350_RTC_DST_SHIFT                        13
+#define WM8350_RTC_SET                          0x0800
+#define WM8350_RTC_SET_MASK                     0x0800
+#define WM8350_RTC_SET_SHIFT                        11
+#define WM8350_RTC_STS                          0x0400
+#define WM8350_RTC_STS_MASK                     0x0400
+#define WM8350_RTC_STS_SHIFT                        10
+#define WM8350_RTC_ALMSET                       0x0200
+#define WM8350_RTC_ALMSET_MASK                  0x0200
+#define WM8350_RTC_ALMSET_SHIFT                      9
+#define WM8350_RTC_ALMSTS                       0x0100
+#define WM8350_RTC_ALMSTS_MASK                  0x0100
+#define WM8350_RTC_ALMSTS_SHIFT                      8
+#define WM8350_RTC_PINT                         0x0070
+#define WM8350_RTC_PINT_MASK                    0x0070
+#define WM8350_RTC_PINT_SHIFT                        4
+#define WM8350_RTC_DSW                          0x000F
+#define WM8350_RTC_DSW_MASK                     0x000F
+#define WM8350_RTC_DSW_SHIFT                         0
+
+/* Bit values for R23 (0x17) */
+#define WM8350_RTC_BCD_BINARY                        0
+#define WM8350_RTC_BCD_BCD                           1
+
+#define WM8350_RTC_12HR_24HR                         0
+#define WM8350_RTC_12HR_12HR                         1
+
+#define WM8350_RTC_DST_DISABLED                      0
+#define WM8350_RTC_DST_ENABLED                       1
+
+#define WM8350_RTC_SET_RUN                           0
+#define WM8350_RTC_SET_SET                           1
+
+#define WM8350_RTC_STS_RUNNING                       0
+#define WM8350_RTC_STS_STOPPED                       1
+
+#define WM8350_RTC_ALMSET_RUN                        0
+#define WM8350_RTC_ALMSET_SET                        1
+
+#define WM8350_RTC_ALMSTS_RUNNING                    0
+#define WM8350_RTC_ALMSTS_STOPPED                    1
+
+#define WM8350_RTC_PINT_DISABLED                     0
+#define WM8350_RTC_PINT_SECS                         1
+#define WM8350_RTC_PINT_MINS                         2
+#define WM8350_RTC_PINT_HRS                          3
+#define WM8350_RTC_PINT_DAYS                         4
+#define WM8350_RTC_PINT_MTHS                         5
+
+#define WM8350_RTC_DSW_DISABLED                      0
+#define WM8350_RTC_DSW_1HZ                           1
+#define WM8350_RTC_DSW_2HZ                           2
+#define WM8350_RTC_DSW_4HZ                           3
+#define WM8350_RTC_DSW_8HZ                           4
+#define WM8350_RTC_DSW_16HZ                          5
+#define WM8350_RTC_DSW_32HZ                          6
+#define WM8350_RTC_DSW_64HZ                          7
+#define WM8350_RTC_DSW_128HZ                         8
+#define WM8350_RTC_DSW_256HZ                         9
+#define WM8350_RTC_DSW_512HZ                        10
+#define WM8350_RTC_DSW_1024HZ                       11
+
+/*
+ * R218 (0xDA) - RTC Tick Control
+ */
+#define WM8350_RTC_TICKSTS                      0x4000
+#define WM8350_RTC_CLKSRC                       0x2000
+#define WM8350_RTC_TRIM_MASK                    0x03FF
+
+/*
+ * RTC Interrupts.
+ */
+#define WM8350_IRQ_RTC_PER			7
+#define WM8350_IRQ_RTC_SEC			8
+#define WM8350_IRQ_RTC_ALM			9
+
+struct wm8350_rtc {
+	struct platform_device *pdev;
+};
+
+#endif
diff --git a/include/linux/mfd/wm8350/supply.h b/include/linux/mfd/wm8350/supply.h
new file mode 100644
index 0000000..1c8f3cd
--- /dev/null
+++ b/include/linux/mfd/wm8350/supply.h
@@ -0,0 +1,111 @@
+/*
+ * supply.h  --  Power Supply Driver for Wolfson WM8350 PMIC
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC
+ *
+ *  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.
+ *
+ */
+
+#ifndef __LINUX_MFD_WM8350_SUPPLY_H_
+#define __LINUX_MFD_WM8350_SUPPLY_H_
+
+#include <linux/platform_device.h>
+
+/*
+ * Charger registers
+ */
+#define WM8350_BATTERY_CHARGER_CONTROL_1        0xA8
+#define WM8350_BATTERY_CHARGER_CONTROL_2        0xA9
+#define WM8350_BATTERY_CHARGER_CONTROL_3        0xAA
+
+/*
+ * R168 (0xA8) - Battery Charger Control 1
+ */
+#define WM8350_CHG_ENA_R168                     0x8000
+#define WM8350_CHG_THR                          0x2000
+#define WM8350_CHG_EOC_SEL_MASK                 0x1C00
+#define WM8350_CHG_TRICKLE_TEMP_CHOKE           0x0200
+#define WM8350_CHG_TRICKLE_USB_CHOKE            0x0100
+#define WM8350_CHG_RECOVER_T                    0x0080
+#define WM8350_CHG_END_ACT                      0x0040
+#define WM8350_CHG_FAST                         0x0020
+#define WM8350_CHG_FAST_USB_THROTTLE            0x0010
+#define WM8350_CHG_NTC_MON                      0x0008
+#define WM8350_CHG_BATT_HOT_MON                 0x0004
+#define WM8350_CHG_BATT_COLD_MON                0x0002
+#define WM8350_CHG_CHIP_TEMP_MON                0x0001
+
+/*
+ * R169 (0xA9) - Battery Charger Control 2
+ */
+#define WM8350_CHG_ACTIVE                       0x8000
+#define WM8350_CHG_PAUSE                        0x4000
+#define WM8350_CHG_STS_MASK                     0x3000
+#define WM8350_CHG_TIME_MASK                    0x0F00
+#define WM8350_CHG_MASK_WALL_FB                 0x0080
+#define WM8350_CHG_TRICKLE_SEL                  0x0040
+#define WM8350_CHG_VSEL_MASK                    0x0030
+#define WM8350_CHG_ISEL_MASK                    0x000F
+#define WM8350_CHG_STS_OFF                      0x0000
+#define WM8350_CHG_STS_TRICKLE                  0x1000
+#define WM8350_CHG_STS_FAST                     0x2000
+
+/*
+ * R170 (0xAA) - Battery Charger Control 3
+ */
+#define WM8350_CHG_THROTTLE_T_MASK              0x0060
+#define WM8350_CHG_SMART                        0x0010
+#define WM8350_CHG_TIMER_ADJT_MASK              0x000F
+
+/*
+ * Charger Interrupts
+ */
+#define WM8350_IRQ_CHG_BAT_HOT			0
+#define WM8350_IRQ_CHG_BAT_COLD			1
+#define WM8350_IRQ_CHG_BAT_FAIL			2
+#define WM8350_IRQ_CHG_TO			3
+#define WM8350_IRQ_CHG_END			4
+#define WM8350_IRQ_CHG_START			5
+#define WM8350_IRQ_CHG_FAST_RDY			6
+#define WM8350_IRQ_CHG_VBATT_LT_3P9		10
+#define WM8350_IRQ_CHG_VBATT_LT_3P1		11
+#define WM8350_IRQ_CHG_VBATT_LT_2P85		12
+
+/*
+ * Charger Policy
+ */
+#define WM8350_CHG_TRICKLE_50mA			(0 << 6)
+#define WM8350_CHG_TRICKLE_100mA		(1 << 6)
+#define WM8350_CHG_4_05V			(0 << 4)
+#define WM8350_CHG_4_10V			(1 << 4)
+#define WM8350_CHG_4_15V			(2 << 4)
+#define WM8350_CHG_4_20V			(3 << 4)
+#define WM8350_CHG_FAST_LIMIT_mA(x)		((x / 50) & 0xf)
+#define WM8350_CHG_EOC_mA(x)			(((x - 10) & 0x7) << 10)
+#define WM8350_CHG_TRICKLE_3_1V			(0 << 13)
+#define WM8350_CHG_TRICKLE_3_9V			(1 << 13)
+
+/*
+ * Supply Registers.
+ */
+#define WM8350_USB_VOLTAGE_READBACK             0x9C
+#define WM8350_LINE_VOLTAGE_READBACK            0x9D
+#define WM8350_BATT_VOLTAGE_READBACK            0x9E
+
+/*
+ * Supply Interrupts.
+ */
+#define WM8350_IRQ_USB_LIMIT			15
+#define WM8350_IRQ_EXT_USB_FB			36
+#define WM8350_IRQ_EXT_WALL_FB			37
+#define WM8350_IRQ_EXT_BAT_FB			38
+
+struct wm8350_power {
+	struct platform_device *pdev;
+};
+
+#endif
diff --git a/include/linux/mfd/wm8350/wdt.h b/include/linux/mfd/wm8350/wdt.h
new file mode 100644
index 0000000..f6135b5
--- /dev/null
+++ b/include/linux/mfd/wm8350/wdt.h
@@ -0,0 +1,28 @@
+/*
+ * wdt.h  --  Watchdog Driver for Wolfson WM8350 PMIC
+ *
+ * Copyright 2007, 2008 Wolfson Microelectronics PLC
+ *
+ *  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.
+ */
+
+#ifndef __LINUX_MFD_WM8350_WDT_H_
+#define __LINUX_MFD_WM8350_WDT_H_
+
+#include <linux/platform_device.h>
+
+#define WM8350_WDOG_HIB_MODE                    0x0080
+#define WM8350_WDOG_DEBUG                       0x0040
+#define WM8350_WDOG_MODE_MASK                   0x0030
+#define WM8350_WDOG_TO_MASK                     0x0007
+
+#define WM8350_IRQ_SYS_WDOG_TO			24
+
+struct wm8350_wdt {
+	struct platform_device *pdev;
+};
+
+#endif
diff --git a/include/linux/mfd/wm8400-audio.h b/include/linux/mfd/wm8400-audio.h
new file mode 100644
index 0000000..b6640e0
--- /dev/null
+++ b/include/linux/mfd/wm8400-audio.h
@@ -0,0 +1,1186 @@
+/*
+ * wm8400 private definitions for audio
+ *
+ * Copyright 2008 Wolfson Microelectronics plc
+ *
+ * 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.
+ */
+
+#ifndef __LINUX_MFD_WM8400_AUDIO_H
+#define __LINUX_MFD_WM8400_AUDIO_H
+
+#include <linux/mfd/wm8400-audio.h>
+
+/*
+ * R2 (0x02) - Power Management (1)
+ */
+#define WM8400_CODEC_ENA                        0x8000  /* CODEC_ENA */
+#define WM8400_CODEC_ENA_MASK                   0x8000  /* CODEC_ENA */
+#define WM8400_CODEC_ENA_SHIFT                      15  /* CODEC_ENA */
+#define WM8400_CODEC_ENA_WIDTH                       1  /* CODEC_ENA */
+#define WM8400_SYSCLK_ENA                       0x4000  /* SYSCLK_ENA */
+#define WM8400_SYSCLK_ENA_MASK                  0x4000  /* SYSCLK_ENA */
+#define WM8400_SYSCLK_ENA_SHIFT                     14  /* SYSCLK_ENA */
+#define WM8400_SYSCLK_ENA_WIDTH                      1  /* SYSCLK_ENA */
+#define WM8400_SPK_MIX_ENA                      0x2000  /* SPK_MIX_ENA */
+#define WM8400_SPK_MIX_ENA_MASK                 0x2000  /* SPK_MIX_ENA */
+#define WM8400_SPK_MIX_ENA_SHIFT                    13  /* SPK_MIX_ENA */
+#define WM8400_SPK_MIX_ENA_WIDTH                     1  /* SPK_MIX_ENA */
+#define WM8400_SPK_ENA                          0x1000  /* SPK_ENA */
+#define WM8400_SPK_ENA_MASK                     0x1000  /* SPK_ENA */
+#define WM8400_SPK_ENA_SHIFT                        12  /* SPK_ENA */
+#define WM8400_SPK_ENA_WIDTH                         1  /* SPK_ENA */
+#define WM8400_OUT3_ENA                         0x0800  /* OUT3_ENA */
+#define WM8400_OUT3_ENA_MASK                    0x0800  /* OUT3_ENA */
+#define WM8400_OUT3_ENA_SHIFT                       11  /* OUT3_ENA */
+#define WM8400_OUT3_ENA_WIDTH                        1  /* OUT3_ENA */
+#define WM8400_OUT4_ENA                         0x0400  /* OUT4_ENA */
+#define WM8400_OUT4_ENA_MASK                    0x0400  /* OUT4_ENA */
+#define WM8400_OUT4_ENA_SHIFT                       10  /* OUT4_ENA */
+#define WM8400_OUT4_ENA_WIDTH                        1  /* OUT4_ENA */
+#define WM8400_LOUT_ENA                         0x0200  /* LOUT_ENA */
+#define WM8400_LOUT_ENA_MASK                    0x0200  /* LOUT_ENA */
+#define WM8400_LOUT_ENA_SHIFT                        9  /* LOUT_ENA */
+#define WM8400_LOUT_ENA_WIDTH                        1  /* LOUT_ENA */
+#define WM8400_ROUT_ENA                         0x0100  /* ROUT_ENA */
+#define WM8400_ROUT_ENA_MASK                    0x0100  /* ROUT_ENA */
+#define WM8400_ROUT_ENA_SHIFT                        8  /* ROUT_ENA */
+#define WM8400_ROUT_ENA_WIDTH                        1  /* ROUT_ENA */
+#define WM8400_MIC1BIAS_ENA                     0x0010  /* MIC1BIAS_ENA */
+#define WM8400_MIC1BIAS_ENA_MASK                0x0010  /* MIC1BIAS_ENA */
+#define WM8400_MIC1BIAS_ENA_SHIFT                    4  /* MIC1BIAS_ENA */
+#define WM8400_MIC1BIAS_ENA_WIDTH                    1  /* MIC1BIAS_ENA */
+#define WM8400_VMID_MODE_MASK                   0x0006  /* VMID_MODE - [2:1] */
+#define WM8400_VMID_MODE_SHIFT                       1  /* VMID_MODE - [2:1] */
+#define WM8400_VMID_MODE_WIDTH                       2  /* VMID_MODE - [2:1] */
+#define WM8400_VREF_ENA                         0x0001  /* VREF_ENA */
+#define WM8400_VREF_ENA_MASK                    0x0001  /* VREF_ENA */
+#define WM8400_VREF_ENA_SHIFT                        0  /* VREF_ENA */
+#define WM8400_VREF_ENA_WIDTH                        1  /* VREF_ENA */
+
+/*
+ * R3 (0x03) - Power Management (2)
+ */
+#define WM8400_FLL_ENA                          0x8000  /* FLL_ENA */
+#define WM8400_FLL_ENA_MASK                     0x8000  /* FLL_ENA */
+#define WM8400_FLL_ENA_SHIFT                        15  /* FLL_ENA */
+#define WM8400_FLL_ENA_WIDTH                         1  /* FLL_ENA */
+#define WM8400_TSHUT_ENA                        0x4000  /* TSHUT_ENA */
+#define WM8400_TSHUT_ENA_MASK                   0x4000  /* TSHUT_ENA */
+#define WM8400_TSHUT_ENA_SHIFT                      14  /* TSHUT_ENA */
+#define WM8400_TSHUT_ENA_WIDTH                       1  /* TSHUT_ENA */
+#define WM8400_TSHUT_OPDIS                      0x2000  /* TSHUT_OPDIS */
+#define WM8400_TSHUT_OPDIS_MASK                 0x2000  /* TSHUT_OPDIS */
+#define WM8400_TSHUT_OPDIS_SHIFT                    13  /* TSHUT_OPDIS */
+#define WM8400_TSHUT_OPDIS_WIDTH                     1  /* TSHUT_OPDIS */
+#define WM8400_OPCLK_ENA                        0x0800  /* OPCLK_ENA */
+#define WM8400_OPCLK_ENA_MASK                   0x0800  /* OPCLK_ENA */
+#define WM8400_OPCLK_ENA_SHIFT                      11  /* OPCLK_ENA */
+#define WM8400_OPCLK_ENA_WIDTH                       1  /* OPCLK_ENA */
+#define WM8400_AINL_ENA                         0x0200  /* AINL_ENA */
+#define WM8400_AINL_ENA_MASK                    0x0200  /* AINL_ENA */
+#define WM8400_AINL_ENA_SHIFT                        9  /* AINL_ENA */
+#define WM8400_AINL_ENA_WIDTH                        1  /* AINL_ENA */
+#define WM8400_AINR_ENA                         0x0100  /* AINR_ENA */
+#define WM8400_AINR_ENA_MASK                    0x0100  /* AINR_ENA */
+#define WM8400_AINR_ENA_SHIFT                        8  /* AINR_ENA */
+#define WM8400_AINR_ENA_WIDTH                        1  /* AINR_ENA */
+#define WM8400_LIN34_ENA                        0x0080  /* LIN34_ENA */
+#define WM8400_LIN34_ENA_MASK                   0x0080  /* LIN34_ENA */
+#define WM8400_LIN34_ENA_SHIFT                       7  /* LIN34_ENA */
+#define WM8400_LIN34_ENA_WIDTH                       1  /* LIN34_ENA */
+#define WM8400_LIN12_ENA                        0x0040  /* LIN12_ENA */
+#define WM8400_LIN12_ENA_MASK                   0x0040  /* LIN12_ENA */
+#define WM8400_LIN12_ENA_SHIFT                       6  /* LIN12_ENA */
+#define WM8400_LIN12_ENA_WIDTH                       1  /* LIN12_ENA */
+#define WM8400_RIN34_ENA                        0x0020  /* RIN34_ENA */
+#define WM8400_RIN34_ENA_MASK                   0x0020  /* RIN34_ENA */
+#define WM8400_RIN34_ENA_SHIFT                       5  /* RIN34_ENA */
+#define WM8400_RIN34_ENA_WIDTH                       1  /* RIN34_ENA */
+#define WM8400_RIN12_ENA                        0x0010  /* RIN12_ENA */
+#define WM8400_RIN12_ENA_MASK                   0x0010  /* RIN12_ENA */
+#define WM8400_RIN12_ENA_SHIFT                       4  /* RIN12_ENA */
+#define WM8400_RIN12_ENA_WIDTH                       1  /* RIN12_ENA */
+#define WM8400_ADCL_ENA                         0x0002  /* ADCL_ENA */
+#define WM8400_ADCL_ENA_MASK                    0x0002  /* ADCL_ENA */
+#define WM8400_ADCL_ENA_SHIFT                        1  /* ADCL_ENA */
+#define WM8400_ADCL_ENA_WIDTH                        1  /* ADCL_ENA */
+#define WM8400_ADCR_ENA                         0x0001  /* ADCR_ENA */
+#define WM8400_ADCR_ENA_MASK                    0x0001  /* ADCR_ENA */
+#define WM8400_ADCR_ENA_SHIFT                        0  /* ADCR_ENA */
+#define WM8400_ADCR_ENA_WIDTH                        1  /* ADCR_ENA */
+
+/*
+ * R4 (0x04) - Power Management (3)
+ */
+#define WM8400_LON_ENA                          0x2000  /* LON_ENA */
+#define WM8400_LON_ENA_MASK                     0x2000  /* LON_ENA */
+#define WM8400_LON_ENA_SHIFT                        13  /* LON_ENA */
+#define WM8400_LON_ENA_WIDTH                         1  /* LON_ENA */
+#define WM8400_LOP_ENA                          0x1000  /* LOP_ENA */
+#define WM8400_LOP_ENA_MASK                     0x1000  /* LOP_ENA */
+#define WM8400_LOP_ENA_SHIFT                        12  /* LOP_ENA */
+#define WM8400_LOP_ENA_WIDTH                         1  /* LOP_ENA */
+#define WM8400_RON_ENA                          0x0800  /* RON_ENA */
+#define WM8400_RON_ENA_MASK                     0x0800  /* RON_ENA */
+#define WM8400_RON_ENA_SHIFT                        11  /* RON_ENA */
+#define WM8400_RON_ENA_WIDTH                         1  /* RON_ENA */
+#define WM8400_ROP_ENA                          0x0400  /* ROP_ENA */
+#define WM8400_ROP_ENA_MASK                     0x0400  /* ROP_ENA */
+#define WM8400_ROP_ENA_SHIFT                        10  /* ROP_ENA */
+#define WM8400_ROP_ENA_WIDTH                         1  /* ROP_ENA */
+#define WM8400_LOPGA_ENA                        0x0080  /* LOPGA_ENA */
+#define WM8400_LOPGA_ENA_MASK                   0x0080  /* LOPGA_ENA */
+#define WM8400_LOPGA_ENA_SHIFT                       7  /* LOPGA_ENA */
+#define WM8400_LOPGA_ENA_WIDTH                       1  /* LOPGA_ENA */
+#define WM8400_ROPGA_ENA                        0x0040  /* ROPGA_ENA */
+#define WM8400_ROPGA_ENA_MASK                   0x0040  /* ROPGA_ENA */
+#define WM8400_ROPGA_ENA_SHIFT                       6  /* ROPGA_ENA */
+#define WM8400_ROPGA_ENA_WIDTH                       1  /* ROPGA_ENA */
+#define WM8400_LOMIX_ENA                        0x0020  /* LOMIX_ENA */
+#define WM8400_LOMIX_ENA_MASK                   0x0020  /* LOMIX_ENA */
+#define WM8400_LOMIX_ENA_SHIFT                       5  /* LOMIX_ENA */
+#define WM8400_LOMIX_ENA_WIDTH                       1  /* LOMIX_ENA */
+#define WM8400_ROMIX_ENA                        0x0010  /* ROMIX_ENA */
+#define WM8400_ROMIX_ENA_MASK                   0x0010  /* ROMIX_ENA */
+#define WM8400_ROMIX_ENA_SHIFT                       4  /* ROMIX_ENA */
+#define WM8400_ROMIX_ENA_WIDTH                       1  /* ROMIX_ENA */
+#define WM8400_DACL_ENA                         0x0002  /* DACL_ENA */
+#define WM8400_DACL_ENA_MASK                    0x0002  /* DACL_ENA */
+#define WM8400_DACL_ENA_SHIFT                        1  /* DACL_ENA */
+#define WM8400_DACL_ENA_WIDTH                        1  /* DACL_ENA */
+#define WM8400_DACR_ENA                         0x0001  /* DACR_ENA */
+#define WM8400_DACR_ENA_MASK                    0x0001  /* DACR_ENA */
+#define WM8400_DACR_ENA_SHIFT                        0  /* DACR_ENA */
+#define WM8400_DACR_ENA_WIDTH                        1  /* DACR_ENA */
+
+/*
+ * R5 (0x05) - Audio Interface (1)
+ */
+#define WM8400_AIFADCL_SRC                      0x8000  /* AIFADCL_SRC */
+#define WM8400_AIFADCL_SRC_MASK                 0x8000  /* AIFADCL_SRC */
+#define WM8400_AIFADCL_SRC_SHIFT                    15  /* AIFADCL_SRC */
+#define WM8400_AIFADCL_SRC_WIDTH                     1  /* AIFADCL_SRC */
+#define WM8400_AIFADCR_SRC                      0x4000  /* AIFADCR_SRC */
+#define WM8400_AIFADCR_SRC_MASK                 0x4000  /* AIFADCR_SRC */
+#define WM8400_AIFADCR_SRC_SHIFT                    14  /* AIFADCR_SRC */
+#define WM8400_AIFADCR_SRC_WIDTH                     1  /* AIFADCR_SRC */
+#define WM8400_AIFADC_TDM                       0x2000  /* AIFADC_TDM */
+#define WM8400_AIFADC_TDM_MASK                  0x2000  /* AIFADC_TDM */
+#define WM8400_AIFADC_TDM_SHIFT                     13  /* AIFADC_TDM */
+#define WM8400_AIFADC_TDM_WIDTH                      1  /* AIFADC_TDM */
+#define WM8400_AIFADC_TDM_CHAN                  0x1000  /* AIFADC_TDM_CHAN */
+#define WM8400_AIFADC_TDM_CHAN_MASK             0x1000  /* AIFADC_TDM_CHAN */
+#define WM8400_AIFADC_TDM_CHAN_SHIFT                12  /* AIFADC_TDM_CHAN */
+#define WM8400_AIFADC_TDM_CHAN_WIDTH                 1  /* AIFADC_TDM_CHAN */
+#define WM8400_AIF_BCLK_INV                     0x0100  /* AIF_BCLK_INV */
+#define WM8400_AIF_BCLK_INV_MASK                0x0100  /* AIF_BCLK_INV */
+#define WM8400_AIF_BCLK_INV_SHIFT                    8  /* AIF_BCLK_INV */
+#define WM8400_AIF_BCLK_INV_WIDTH                    1  /* AIF_BCLK_INV */
+#define WM8400_AIF_LRCLK_INV                    0x0080  /* AIF_LRCLK_INV */
+#define WM8400_AIF_LRCLK_INV_MASK               0x0080  /* AIF_LRCLK_INV */
+#define WM8400_AIF_LRCLK_INV_SHIFT                   7  /* AIF_LRCLK_INV */
+#define WM8400_AIF_LRCLK_INV_WIDTH                   1  /* AIF_LRCLK_INV */
+#define WM8400_AIF_WL_MASK                      0x0060  /* AIF_WL - [6:5] */
+#define WM8400_AIF_WL_SHIFT                          5  /* AIF_WL - [6:5] */
+#define WM8400_AIF_WL_WIDTH                          2  /* AIF_WL - [6:5] */
+#define WM8400_AIF_WL_16BITS			(0 << 5)
+#define WM8400_AIF_WL_20BITS			(1 << 5)
+#define WM8400_AIF_WL_24BITS			(2 << 5)
+#define WM8400_AIF_WL_32BITS			(3 << 5)
+#define WM8400_AIF_FMT_MASK                     0x0018  /* AIF_FMT - [4:3] */
+#define WM8400_AIF_FMT_SHIFT                         3  /* AIF_FMT - [4:3] */
+#define WM8400_AIF_FMT_WIDTH                         2  /* AIF_FMT - [4:3] */
+#define WM8400_AIF_FMT_RIGHTJ			(0 << 3)
+#define WM8400_AIF_FMT_LEFTJ			(1 << 3)
+#define WM8400_AIF_FMT_I2S			(2 << 3)
+#define WM8400_AIF_FMT_DSP			(3 << 3)
+
+/*
+ * R6 (0x06) - Audio Interface (2)
+ */
+#define WM8400_DACL_SRC                         0x8000  /* DACL_SRC */
+#define WM8400_DACL_SRC_MASK                    0x8000  /* DACL_SRC */
+#define WM8400_DACL_SRC_SHIFT                       15  /* DACL_SRC */
+#define WM8400_DACL_SRC_WIDTH                        1  /* DACL_SRC */
+#define WM8400_DACR_SRC                         0x4000  /* DACR_SRC */
+#define WM8400_DACR_SRC_MASK                    0x4000  /* DACR_SRC */
+#define WM8400_DACR_SRC_SHIFT                       14  /* DACR_SRC */
+#define WM8400_DACR_SRC_WIDTH                        1  /* DACR_SRC */
+#define WM8400_AIFDAC_TDM                       0x2000  /* AIFDAC_TDM */
+#define WM8400_AIFDAC_TDM_MASK                  0x2000  /* AIFDAC_TDM */
+#define WM8400_AIFDAC_TDM_SHIFT                     13  /* AIFDAC_TDM */
+#define WM8400_AIFDAC_TDM_WIDTH                      1  /* AIFDAC_TDM */
+#define WM8400_AIFDAC_TDM_CHAN                  0x1000  /* AIFDAC_TDM_CHAN */
+#define WM8400_AIFDAC_TDM_CHAN_MASK             0x1000  /* AIFDAC_TDM_CHAN */
+#define WM8400_AIFDAC_TDM_CHAN_SHIFT                12  /* AIFDAC_TDM_CHAN */
+#define WM8400_AIFDAC_TDM_CHAN_WIDTH                 1  /* AIFDAC_TDM_CHAN */
+#define WM8400_DAC_BOOST_MASK                   0x0C00  /* DAC_BOOST - [11:10] */
+#define WM8400_DAC_BOOST_SHIFT                      10  /* DAC_BOOST - [11:10] */
+#define WM8400_DAC_BOOST_WIDTH                       2  /* DAC_BOOST - [11:10] */
+#define WM8400_DAC_COMP                         0x0010  /* DAC_COMP */
+#define WM8400_DAC_COMP_MASK                    0x0010  /* DAC_COMP */
+#define WM8400_DAC_COMP_SHIFT                        4  /* DAC_COMP */
+#define WM8400_DAC_COMP_WIDTH                        1  /* DAC_COMP */
+#define WM8400_DAC_COMPMODE                     0x0008  /* DAC_COMPMODE */
+#define WM8400_DAC_COMPMODE_MASK                0x0008  /* DAC_COMPMODE */
+#define WM8400_DAC_COMPMODE_SHIFT                    3  /* DAC_COMPMODE */
+#define WM8400_DAC_COMPMODE_WIDTH                    1  /* DAC_COMPMODE */
+#define WM8400_ADC_COMP                         0x0004  /* ADC_COMP */
+#define WM8400_ADC_COMP_MASK                    0x0004  /* ADC_COMP */
+#define WM8400_ADC_COMP_SHIFT                        2  /* ADC_COMP */
+#define WM8400_ADC_COMP_WIDTH                        1  /* ADC_COMP */
+#define WM8400_ADC_COMPMODE                     0x0002  /* ADC_COMPMODE */
+#define WM8400_ADC_COMPMODE_MASK                0x0002  /* ADC_COMPMODE */
+#define WM8400_ADC_COMPMODE_SHIFT                    1  /* ADC_COMPMODE */
+#define WM8400_ADC_COMPMODE_WIDTH                    1  /* ADC_COMPMODE */
+#define WM8400_LOOPBACK                         0x0001  /* LOOPBACK */
+#define WM8400_LOOPBACK_MASK                    0x0001  /* LOOPBACK */
+#define WM8400_LOOPBACK_SHIFT                        0  /* LOOPBACK */
+#define WM8400_LOOPBACK_WIDTH                        1  /* LOOPBACK */
+
+/*
+ * R7 (0x07) - Clocking (1)
+ */
+#define WM8400_TOCLK_RATE                       0x8000  /* TOCLK_RATE */
+#define WM8400_TOCLK_RATE_MASK                  0x8000  /* TOCLK_RATE */
+#define WM8400_TOCLK_RATE_SHIFT                     15  /* TOCLK_RATE */
+#define WM8400_TOCLK_RATE_WIDTH                      1  /* TOCLK_RATE */
+#define WM8400_TOCLK_ENA                        0x4000  /* TOCLK_ENA */
+#define WM8400_TOCLK_ENA_MASK                   0x4000  /* TOCLK_ENA */
+#define WM8400_TOCLK_ENA_SHIFT                      14  /* TOCLK_ENA */
+#define WM8400_TOCLK_ENA_WIDTH                       1  /* TOCLK_ENA */
+#define WM8400_OPCLKDIV_MASK                    0x1E00  /* OPCLKDIV - [12:9] */
+#define WM8400_OPCLKDIV_SHIFT                        9  /* OPCLKDIV - [12:9] */
+#define WM8400_OPCLKDIV_WIDTH                        4  /* OPCLKDIV - [12:9] */
+#define WM8400_DCLKDIV_MASK                     0x01C0  /* DCLKDIV - [8:6] */
+#define WM8400_DCLKDIV_SHIFT                         6  /* DCLKDIV - [8:6] */
+#define WM8400_DCLKDIV_WIDTH                         3  /* DCLKDIV - [8:6] */
+#define WM8400_BCLK_DIV_MASK                    0x001E  /* BCLK_DIV - [4:1] */
+#define WM8400_BCLK_DIV_SHIFT                        1  /* BCLK_DIV - [4:1] */
+#define WM8400_BCLK_DIV_WIDTH                        4  /* BCLK_DIV - [4:1] */
+
+/*
+ * R8 (0x08) - Clocking (2)
+ */
+#define WM8400_MCLK_SRC                         0x8000  /* MCLK_SRC */
+#define WM8400_MCLK_SRC_MASK                    0x8000  /* MCLK_SRC */
+#define WM8400_MCLK_SRC_SHIFT                       15  /* MCLK_SRC */
+#define WM8400_MCLK_SRC_WIDTH                        1  /* MCLK_SRC */
+#define WM8400_SYSCLK_SRC                       0x4000  /* SYSCLK_SRC */
+#define WM8400_SYSCLK_SRC_MASK                  0x4000  /* SYSCLK_SRC */
+#define WM8400_SYSCLK_SRC_SHIFT                     14  /* SYSCLK_SRC */
+#define WM8400_SYSCLK_SRC_WIDTH                      1  /* SYSCLK_SRC */
+#define WM8400_CLK_FORCE                        0x2000  /* CLK_FORCE */
+#define WM8400_CLK_FORCE_MASK                   0x2000  /* CLK_FORCE */
+#define WM8400_CLK_FORCE_SHIFT                      13  /* CLK_FORCE */
+#define WM8400_CLK_FORCE_WIDTH                       1  /* CLK_FORCE */
+#define WM8400_MCLK_DIV_MASK                    0x1800  /* MCLK_DIV - [12:11] */
+#define WM8400_MCLK_DIV_SHIFT                       11  /* MCLK_DIV - [12:11] */
+#define WM8400_MCLK_DIV_WIDTH                        2  /* MCLK_DIV - [12:11] */
+#define WM8400_MCLK_INV                         0x0400  /* MCLK_INV */
+#define WM8400_MCLK_INV_MASK                    0x0400  /* MCLK_INV */
+#define WM8400_MCLK_INV_SHIFT                       10  /* MCLK_INV */
+#define WM8400_MCLK_INV_WIDTH                        1  /* MCLK_INV */
+#define WM8400_ADC_CLKDIV_MASK                  0x00E0  /* ADC_CLKDIV - [7:5] */
+#define WM8400_ADC_CLKDIV_SHIFT                      5  /* ADC_CLKDIV - [7:5] */
+#define WM8400_ADC_CLKDIV_WIDTH                      3  /* ADC_CLKDIV - [7:5] */
+#define WM8400_DAC_CLKDIV_MASK                  0x001C  /* DAC_CLKDIV - [4:2] */
+#define WM8400_DAC_CLKDIV_SHIFT                      2  /* DAC_CLKDIV - [4:2] */
+#define WM8400_DAC_CLKDIV_WIDTH                      3  /* DAC_CLKDIV - [4:2] */
+
+/*
+ * R9 (0x09) - Audio Interface (3)
+ */
+#define WM8400_AIF_MSTR1                        0x8000  /* AIF_MSTR1 */
+#define WM8400_AIF_MSTR1_MASK                   0x8000  /* AIF_MSTR1 */
+#define WM8400_AIF_MSTR1_SHIFT                      15  /* AIF_MSTR1 */
+#define WM8400_AIF_MSTR1_WIDTH                       1  /* AIF_MSTR1 */
+#define WM8400_AIF_MSTR2                        0x4000  /* AIF_MSTR2 */
+#define WM8400_AIF_MSTR2_MASK                   0x4000  /* AIF_MSTR2 */
+#define WM8400_AIF_MSTR2_SHIFT                      14  /* AIF_MSTR2 */
+#define WM8400_AIF_MSTR2_WIDTH                       1  /* AIF_MSTR2 */
+#define WM8400_AIF_SEL                          0x2000  /* AIF_SEL */
+#define WM8400_AIF_SEL_MASK                     0x2000  /* AIF_SEL */
+#define WM8400_AIF_SEL_SHIFT                        13  /* AIF_SEL */
+#define WM8400_AIF_SEL_WIDTH                         1  /* AIF_SEL */
+#define WM8400_ADCLRC_DIR                       0x0800  /* ADCLRC_DIR */
+#define WM8400_ADCLRC_DIR_MASK                  0x0800  /* ADCLRC_DIR */
+#define WM8400_ADCLRC_DIR_SHIFT                     11  /* ADCLRC_DIR */
+#define WM8400_ADCLRC_DIR_WIDTH                      1  /* ADCLRC_DIR */
+#define WM8400_ADCLRC_RATE_MASK                 0x07FF  /* ADCLRC_RATE - [10:0] */
+#define WM8400_ADCLRC_RATE_SHIFT                     0  /* ADCLRC_RATE - [10:0] */
+#define WM8400_ADCLRC_RATE_WIDTH                    11  /* ADCLRC_RATE - [10:0] */
+
+/*
+ * R10 (0x0A) - Audio Interface (4)
+ */
+#define WM8400_ALRCGPIO1                        0x8000  /* ALRCGPIO1 */
+#define WM8400_ALRCGPIO1_MASK                   0x8000  /* ALRCGPIO1 */
+#define WM8400_ALRCGPIO1_SHIFT                      15  /* ALRCGPIO1 */
+#define WM8400_ALRCGPIO1_WIDTH                       1  /* ALRCGPIO1 */
+#define WM8400_ALRCBGPIO6                       0x4000  /* ALRCBGPIO6 */
+#define WM8400_ALRCBGPIO6_MASK                  0x4000  /* ALRCBGPIO6 */
+#define WM8400_ALRCBGPIO6_SHIFT                     14  /* ALRCBGPIO6 */
+#define WM8400_ALRCBGPIO6_WIDTH                      1  /* ALRCBGPIO6 */
+#define WM8400_AIF_TRIS                         0x2000  /* AIF_TRIS */
+#define WM8400_AIF_TRIS_MASK                    0x2000  /* AIF_TRIS */
+#define WM8400_AIF_TRIS_SHIFT                       13  /* AIF_TRIS */
+#define WM8400_AIF_TRIS_WIDTH                        1  /* AIF_TRIS */
+#define WM8400_DACLRC_DIR                       0x0800  /* DACLRC_DIR */
+#define WM8400_DACLRC_DIR_MASK                  0x0800  /* DACLRC_DIR */
+#define WM8400_DACLRC_DIR_SHIFT                     11  /* DACLRC_DIR */
+#define WM8400_DACLRC_DIR_WIDTH                      1  /* DACLRC_DIR */
+#define WM8400_DACLRC_RATE_MASK                 0x07FF  /* DACLRC_RATE - [10:0] */
+#define WM8400_DACLRC_RATE_SHIFT                     0  /* DACLRC_RATE - [10:0] */
+#define WM8400_DACLRC_RATE_WIDTH                    11  /* DACLRC_RATE - [10:0] */
+
+/*
+ * R11 (0x0B) - DAC CTRL
+ */
+#define WM8400_DAC_SDMCLK_RATE                  0x2000  /* DAC_SDMCLK_RATE */
+#define WM8400_DAC_SDMCLK_RATE_MASK             0x2000  /* DAC_SDMCLK_RATE */
+#define WM8400_DAC_SDMCLK_RATE_SHIFT                13  /* DAC_SDMCLK_RATE */
+#define WM8400_DAC_SDMCLK_RATE_WIDTH                 1  /* DAC_SDMCLK_RATE */
+#define WM8400_AIF_LRCLKRATE                    0x0400  /* AIF_LRCLKRATE */
+#define WM8400_AIF_LRCLKRATE_MASK               0x0400  /* AIF_LRCLKRATE */
+#define WM8400_AIF_LRCLKRATE_SHIFT                  10  /* AIF_LRCLKRATE */
+#define WM8400_AIF_LRCLKRATE_WIDTH                   1  /* AIF_LRCLKRATE */
+#define WM8400_DAC_MONO                         0x0200  /* DAC_MONO */
+#define WM8400_DAC_MONO_MASK                    0x0200  /* DAC_MONO */
+#define WM8400_DAC_MONO_SHIFT                        9  /* DAC_MONO */
+#define WM8400_DAC_MONO_WIDTH                        1  /* DAC_MONO */
+#define WM8400_DAC_SB_FILT                      0x0100  /* DAC_SB_FILT */
+#define WM8400_DAC_SB_FILT_MASK                 0x0100  /* DAC_SB_FILT */
+#define WM8400_DAC_SB_FILT_SHIFT                     8  /* DAC_SB_FILT */
+#define WM8400_DAC_SB_FILT_WIDTH                     1  /* DAC_SB_FILT */
+#define WM8400_DAC_MUTERATE                     0x0080  /* DAC_MUTERATE */
+#define WM8400_DAC_MUTERATE_MASK                0x0080  /* DAC_MUTERATE */
+#define WM8400_DAC_MUTERATE_SHIFT                    7  /* DAC_MUTERATE */
+#define WM8400_DAC_MUTERATE_WIDTH                    1  /* DAC_MUTERATE */
+#define WM8400_DAC_MUTEMODE                     0x0040  /* DAC_MUTEMODE */
+#define WM8400_DAC_MUTEMODE_MASK                0x0040  /* DAC_MUTEMODE */
+#define WM8400_DAC_MUTEMODE_SHIFT                    6  /* DAC_MUTEMODE */
+#define WM8400_DAC_MUTEMODE_WIDTH                    1  /* DAC_MUTEMODE */
+#define WM8400_DEEMP_MASK                       0x0030  /* DEEMP - [5:4] */
+#define WM8400_DEEMP_SHIFT                           4  /* DEEMP - [5:4] */
+#define WM8400_DEEMP_WIDTH                           2  /* DEEMP - [5:4] */
+#define WM8400_DAC_MUTE                         0x0004  /* DAC_MUTE */
+#define WM8400_DAC_MUTE_MASK                    0x0004  /* DAC_MUTE */
+#define WM8400_DAC_MUTE_SHIFT                        2  /* DAC_MUTE */
+#define WM8400_DAC_MUTE_WIDTH                        1  /* DAC_MUTE */
+#define WM8400_DACL_DATINV                      0x0002  /* DACL_DATINV */
+#define WM8400_DACL_DATINV_MASK                 0x0002  /* DACL_DATINV */
+#define WM8400_DACL_DATINV_SHIFT                     1  /* DACL_DATINV */
+#define WM8400_DACL_DATINV_WIDTH                     1  /* DACL_DATINV */
+#define WM8400_DACR_DATINV                      0x0001  /* DACR_DATINV */
+#define WM8400_DACR_DATINV_MASK                 0x0001  /* DACR_DATINV */
+#define WM8400_DACR_DATINV_SHIFT                     0  /* DACR_DATINV */
+#define WM8400_DACR_DATINV_WIDTH                     1  /* DACR_DATINV */
+
+/*
+ * R12 (0x0C) - Left DAC Digital Volume
+ */
+#define WM8400_DAC_VU                           0x0100  /* DAC_VU */
+#define WM8400_DAC_VU_MASK                      0x0100  /* DAC_VU */
+#define WM8400_DAC_VU_SHIFT                          8  /* DAC_VU */
+#define WM8400_DAC_VU_WIDTH                          1  /* DAC_VU */
+#define WM8400_DACL_VOL_MASK                    0x00FF  /* DACL_VOL - [7:0] */
+#define WM8400_DACL_VOL_SHIFT                        0  /* DACL_VOL - [7:0] */
+#define WM8400_DACL_VOL_WIDTH                        8  /* DACL_VOL - [7:0] */
+
+/*
+ * R13 (0x0D) - Right DAC Digital Volume
+ */
+#define WM8400_DAC_VU                           0x0100  /* DAC_VU */
+#define WM8400_DAC_VU_MASK                      0x0100  /* DAC_VU */
+#define WM8400_DAC_VU_SHIFT                          8  /* DAC_VU */
+#define WM8400_DAC_VU_WIDTH                          1  /* DAC_VU */
+#define WM8400_DACR_VOL_MASK                    0x00FF  /* DACR_VOL - [7:0] */
+#define WM8400_DACR_VOL_SHIFT                        0  /* DACR_VOL - [7:0] */
+#define WM8400_DACR_VOL_WIDTH                        8  /* DACR_VOL - [7:0] */
+
+/*
+ * R14 (0x0E) - Digital Side Tone
+ */
+#define WM8400_ADCL_DAC_SVOL_MASK               0x1E00  /*   ADCL_DAC_SVOL - [12:9] */
+#define WM8400_ADCL_DAC_SVOL_SHIFT                   9  /*   ADCL_DAC_SVOL - [12:9] */
+#define WM8400_ADCL_DAC_SVOL_WIDTH                   4  /*   ADCL_DAC_SVOL - [12:9] */
+#define WM8400_ADCR_DAC_SVOL_MASK               0x01E0  /* ADCR_DAC_SVOL - [8:5] */
+#define WM8400_ADCR_DAC_SVOL_SHIFT                   5  /* ADCR_DAC_SVOL - [8:5] */
+#define WM8400_ADCR_DAC_SVOL_WIDTH                   4  /* ADCR_DAC_SVOL - [8:5] */
+#define WM8400_ADC_TO_DACL_MASK                 0x000C  /* ADC_TO_DACL - [3:2] */
+#define WM8400_ADC_TO_DACL_SHIFT                     2  /* ADC_TO_DACL - [3:2] */
+#define WM8400_ADC_TO_DACL_WIDTH                     2  /* ADC_TO_DACL - [3:2] */
+#define WM8400_ADC_TO_DACR_MASK                 0x0003  /* ADC_TO_DACR - [1:0] */
+#define WM8400_ADC_TO_DACR_SHIFT                     0  /* ADC_TO_DACR - [1:0] */
+#define WM8400_ADC_TO_DACR_WIDTH                     2  /* ADC_TO_DACR - [1:0] */
+
+/*
+ * R15 (0x0F) - ADC CTRL
+ */
+#define WM8400_ADC_HPF_ENA                      0x0100  /* ADC_HPF_ENA */
+#define WM8400_ADC_HPF_ENA_MASK                 0x0100  /* ADC_HPF_ENA */
+#define WM8400_ADC_HPF_ENA_SHIFT                     8  /* ADC_HPF_ENA */
+#define WM8400_ADC_HPF_ENA_WIDTH                     1  /* ADC_HPF_ENA */
+#define WM8400_ADC_HPF_CUT_MASK                 0x0060  /* ADC_HPF_CUT - [6:5] */
+#define WM8400_ADC_HPF_CUT_SHIFT                     5  /* ADC_HPF_CUT - [6:5] */
+#define WM8400_ADC_HPF_CUT_WIDTH                     2  /* ADC_HPF_CUT - [6:5] */
+#define WM8400_ADCL_DATINV                      0x0002  /* ADCL_DATINV */
+#define WM8400_ADCL_DATINV_MASK                 0x0002  /* ADCL_DATINV */
+#define WM8400_ADCL_DATINV_SHIFT                     1  /* ADCL_DATINV */
+#define WM8400_ADCL_DATINV_WIDTH                     1  /* ADCL_DATINV */
+#define WM8400_ADCR_DATINV                      0x0001  /* ADCR_DATINV */
+#define WM8400_ADCR_DATINV_MASK                 0x0001  /* ADCR_DATINV */
+#define WM8400_ADCR_DATINV_SHIFT                     0  /* ADCR_DATINV */
+#define WM8400_ADCR_DATINV_WIDTH                     1  /* ADCR_DATINV */
+
+/*
+ * R16 (0x10) - Left ADC Digital Volume
+ */
+#define WM8400_ADC_VU                           0x0100  /* ADC_VU */
+#define WM8400_ADC_VU_MASK                      0x0100  /* ADC_VU */
+#define WM8400_ADC_VU_SHIFT                          8  /* ADC_VU */
+#define WM8400_ADC_VU_WIDTH                          1  /* ADC_VU */
+#define WM8400_ADCL_VOL_MASK                    0x00FF  /* ADCL_VOL - [7:0] */
+#define WM8400_ADCL_VOL_SHIFT                        0  /* ADCL_VOL - [7:0] */
+#define WM8400_ADCL_VOL_WIDTH                        8  /* ADCL_VOL - [7:0] */
+
+/*
+ * R17 (0x11) - Right ADC Digital Volume
+ */
+#define WM8400_ADC_VU                           0x0100  /* ADC_VU */
+#define WM8400_ADC_VU_MASK                      0x0100  /* ADC_VU */
+#define WM8400_ADC_VU_SHIFT                          8  /* ADC_VU */
+#define WM8400_ADC_VU_WIDTH                          1  /* ADC_VU */
+#define WM8400_ADCR_VOL_MASK                    0x00FF  /* ADCR_VOL - [7:0] */
+#define WM8400_ADCR_VOL_SHIFT                        0  /* ADCR_VOL - [7:0] */
+#define WM8400_ADCR_VOL_WIDTH                        8  /* ADCR_VOL - [7:0] */
+
+/*
+ * R24 (0x18) - Left Line Input 1&2 Volume
+ */
+#define WM8400_IPVU                             0x0100  /* IPVU */
+#define WM8400_IPVU_MASK                        0x0100  /* IPVU */
+#define WM8400_IPVU_SHIFT                            8  /* IPVU */
+#define WM8400_IPVU_WIDTH                            1  /* IPVU */
+#define WM8400_LI12MUTE                         0x0080  /* LI12MUTE */
+#define WM8400_LI12MUTE_MASK                    0x0080  /* LI12MUTE */
+#define WM8400_LI12MUTE_SHIFT                        7  /* LI12MUTE */
+#define WM8400_LI12MUTE_WIDTH                        1  /* LI12MUTE */
+#define WM8400_LI12ZC                           0x0040  /* LI12ZC */
+#define WM8400_LI12ZC_MASK                      0x0040  /* LI12ZC */
+#define WM8400_LI12ZC_SHIFT                          6  /* LI12ZC */
+#define WM8400_LI12ZC_WIDTH                          1  /* LI12ZC */
+#define WM8400_LIN12VOL_MASK                    0x001F  /* LIN12VOL - [4:0] */
+#define WM8400_LIN12VOL_SHIFT                        0  /* LIN12VOL - [4:0] */
+#define WM8400_LIN12VOL_WIDTH                        5  /* LIN12VOL - [4:0] */
+
+/*
+ * R25 (0x19) - Left Line Input 3&4 Volume
+ */
+#define WM8400_IPVU                             0x0100  /* IPVU */
+#define WM8400_IPVU_MASK                        0x0100  /* IPVU */
+#define WM8400_IPVU_SHIFT                            8  /* IPVU */
+#define WM8400_IPVU_WIDTH                            1  /* IPVU */
+#define WM8400_LI34MUTE                         0x0080  /* LI34MUTE */
+#define WM8400_LI34MUTE_MASK                    0x0080  /* LI34MUTE */
+#define WM8400_LI34MUTE_SHIFT                        7  /* LI34MUTE */
+#define WM8400_LI34MUTE_WIDTH                        1  /* LI34MUTE */
+#define WM8400_LI34ZC                           0x0040  /* LI34ZC */
+#define WM8400_LI34ZC_MASK                      0x0040  /* LI34ZC */
+#define WM8400_LI34ZC_SHIFT                          6  /* LI34ZC */
+#define WM8400_LI34ZC_WIDTH                          1  /* LI34ZC */
+#define WM8400_LIN34VOL_MASK                    0x001F  /* LIN34VOL - [4:0] */
+#define WM8400_LIN34VOL_SHIFT                        0  /* LIN34VOL - [4:0] */
+#define WM8400_LIN34VOL_WIDTH                        5  /* LIN34VOL - [4:0] */
+
+/*
+ * R26 (0x1A) - Right Line Input 1&2 Volume
+ */
+#define WM8400_IPVU                             0x0100  /* IPVU */
+#define WM8400_IPVU_MASK                        0x0100  /* IPVU */
+#define WM8400_IPVU_SHIFT                            8  /* IPVU */
+#define WM8400_IPVU_WIDTH                            1  /* IPVU */
+#define WM8400_RI12MUTE                         0x0080  /* RI12MUTE */
+#define WM8400_RI12MUTE_MASK                    0x0080  /* RI12MUTE */
+#define WM8400_RI12MUTE_SHIFT                        7  /* RI12MUTE */
+#define WM8400_RI12MUTE_WIDTH                        1  /* RI12MUTE */
+#define WM8400_RI12ZC                           0x0040  /* RI12ZC */
+#define WM8400_RI12ZC_MASK                      0x0040  /* RI12ZC */
+#define WM8400_RI12ZC_SHIFT                          6  /* RI12ZC */
+#define WM8400_RI12ZC_WIDTH                          1  /* RI12ZC */
+#define WM8400_RIN12VOL_MASK                    0x001F  /* RIN12VOL - [4:0] */
+#define WM8400_RIN12VOL_SHIFT                        0  /* RIN12VOL - [4:0] */
+#define WM8400_RIN12VOL_WIDTH                        5  /* RIN12VOL - [4:0] */
+
+/*
+ * R27 (0x1B) - Right Line Input 3&4 Volume
+ */
+#define WM8400_IPVU                             0x0100  /* IPVU */
+#define WM8400_IPVU_MASK                        0x0100  /* IPVU */
+#define WM8400_IPVU_SHIFT                            8  /* IPVU */
+#define WM8400_IPVU_WIDTH                            1  /* IPVU */
+#define WM8400_RI34MUTE                         0x0080  /* RI34MUTE */
+#define WM8400_RI34MUTE_MASK                    0x0080  /* RI34MUTE */
+#define WM8400_RI34MUTE_SHIFT                        7  /* RI34MUTE */
+#define WM8400_RI34MUTE_WIDTH                        1  /* RI34MUTE */
+#define WM8400_RI34ZC                           0x0040  /* RI34ZC */
+#define WM8400_RI34ZC_MASK                      0x0040  /* RI34ZC */
+#define WM8400_RI34ZC_SHIFT                          6  /* RI34ZC */
+#define WM8400_RI34ZC_WIDTH                          1  /* RI34ZC */
+#define WM8400_RIN34VOL_MASK                    0x001F  /* RIN34VOL - [4:0] */
+#define WM8400_RIN34VOL_SHIFT                        0  /* RIN34VOL - [4:0] */
+#define WM8400_RIN34VOL_WIDTH                        5  /* RIN34VOL - [4:0] */
+
+/*
+ * R28 (0x1C) - Left Output Volume
+ */
+#define WM8400_OPVU                             0x0100  /* OPVU */
+#define WM8400_OPVU_MASK                        0x0100  /* OPVU */
+#define WM8400_OPVU_SHIFT                            8  /* OPVU */
+#define WM8400_OPVU_WIDTH                            1  /* OPVU */
+#define WM8400_LOZC                             0x0080  /* LOZC */
+#define WM8400_LOZC_MASK                        0x0080  /* LOZC */
+#define WM8400_LOZC_SHIFT                            7  /* LOZC */
+#define WM8400_LOZC_WIDTH                            1  /* LOZC */
+#define WM8400_LOUTVOL_MASK                     0x007F  /* LOUTVOL - [6:0] */
+#define WM8400_LOUTVOL_SHIFT                         0  /* LOUTVOL - [6:0] */
+#define WM8400_LOUTVOL_WIDTH                         7  /* LOUTVOL - [6:0] */
+
+/*
+ * R29 (0x1D) - Right Output Volume
+ */
+#define WM8400_OPVU                             0x0100  /* OPVU */
+#define WM8400_OPVU_MASK                        0x0100  /* OPVU */
+#define WM8400_OPVU_SHIFT                            8  /* OPVU */
+#define WM8400_OPVU_WIDTH                            1  /* OPVU */
+#define WM8400_ROZC                             0x0080  /* ROZC */
+#define WM8400_ROZC_MASK                        0x0080  /* ROZC */
+#define WM8400_ROZC_SHIFT                            7  /* ROZC */
+#define WM8400_ROZC_WIDTH                            1  /* ROZC */
+#define WM8400_ROUTVOL_MASK                     0x007F  /* ROUTVOL - [6:0] */
+#define WM8400_ROUTVOL_SHIFT                         0  /* ROUTVOL - [6:0] */
+#define WM8400_ROUTVOL_WIDTH                         7  /* ROUTVOL - [6:0] */
+
+/*
+ * R30 (0x1E) - Line Outputs Volume
+ */
+#define WM8400_LONMUTE                          0x0040  /* LONMUTE */
+#define WM8400_LONMUTE_MASK                     0x0040  /* LONMUTE */
+#define WM8400_LONMUTE_SHIFT                         6  /* LONMUTE */
+#define WM8400_LONMUTE_WIDTH                         1  /* LONMUTE */
+#define WM8400_LOPMUTE                          0x0020  /* LOPMUTE */
+#define WM8400_LOPMUTE_MASK                     0x0020  /* LOPMUTE */
+#define WM8400_LOPMUTE_SHIFT                         5  /* LOPMUTE */
+#define WM8400_LOPMUTE_WIDTH                         1  /* LOPMUTE */
+#define WM8400_LOATTN                           0x0010  /* LOATTN */
+#define WM8400_LOATTN_MASK                      0x0010  /* LOATTN */
+#define WM8400_LOATTN_SHIFT                          4  /* LOATTN */
+#define WM8400_LOATTN_WIDTH                          1  /* LOATTN */
+#define WM8400_RONMUTE                          0x0004  /* RONMUTE */
+#define WM8400_RONMUTE_MASK                     0x0004  /* RONMUTE */
+#define WM8400_RONMUTE_SHIFT                         2  /* RONMUTE */
+#define WM8400_RONMUTE_WIDTH                         1  /* RONMUTE */
+#define WM8400_ROPMUTE                          0x0002  /* ROPMUTE */
+#define WM8400_ROPMUTE_MASK                     0x0002  /* ROPMUTE */
+#define WM8400_ROPMUTE_SHIFT                         1  /* ROPMUTE */
+#define WM8400_ROPMUTE_WIDTH                         1  /* ROPMUTE */
+#define WM8400_ROATTN                           0x0001  /* ROATTN */
+#define WM8400_ROATTN_MASK                      0x0001  /* ROATTN */
+#define WM8400_ROATTN_SHIFT                          0  /* ROATTN */
+#define WM8400_ROATTN_WIDTH                          1  /* ROATTN */
+
+/*
+ * R31 (0x1F) - Out3/4 Volume
+ */
+#define WM8400_OUT3MUTE                         0x0020  /* OUT3MUTE */
+#define WM8400_OUT3MUTE_MASK                    0x0020  /* OUT3MUTE */
+#define WM8400_OUT3MUTE_SHIFT                        5  /* OUT3MUTE */
+#define WM8400_OUT3MUTE_WIDTH                        1  /* OUT3MUTE */
+#define WM8400_OUT3ATTN                         0x0010  /* OUT3ATTN */
+#define WM8400_OUT3ATTN_MASK                    0x0010  /* OUT3ATTN */
+#define WM8400_OUT3ATTN_SHIFT                        4  /* OUT3ATTN */
+#define WM8400_OUT3ATTN_WIDTH                        1  /* OUT3ATTN */
+#define WM8400_OUT4MUTE                         0x0002  /* OUT4MUTE */
+#define WM8400_OUT4MUTE_MASK                    0x0002  /* OUT4MUTE */
+#define WM8400_OUT4MUTE_SHIFT                        1  /* OUT4MUTE */
+#define WM8400_OUT4MUTE_WIDTH                        1  /* OUT4MUTE */
+#define WM8400_OUT4ATTN                         0x0001  /* OUT4ATTN */
+#define WM8400_OUT4ATTN_MASK                    0x0001  /* OUT4ATTN */
+#define WM8400_OUT4ATTN_SHIFT                        0  /* OUT4ATTN */
+#define WM8400_OUT4ATTN_WIDTH                        1  /* OUT4ATTN */
+
+/*
+ * R32 (0x20) - Left OPGA Volume
+ */
+#define WM8400_OPVU                             0x0100  /* OPVU */
+#define WM8400_OPVU_MASK                        0x0100  /* OPVU */
+#define WM8400_OPVU_SHIFT                            8  /* OPVU */
+#define WM8400_OPVU_WIDTH                            1  /* OPVU */
+#define WM8400_LOPGAZC                          0x0080  /* LOPGAZC */
+#define WM8400_LOPGAZC_MASK                     0x0080  /* LOPGAZC */
+#define WM8400_LOPGAZC_SHIFT                         7  /* LOPGAZC */
+#define WM8400_LOPGAZC_WIDTH                         1  /* LOPGAZC */
+#define WM8400_LOPGAVOL_MASK                    0x007F  /* LOPGAVOL - [6:0] */
+#define WM8400_LOPGAVOL_SHIFT                        0  /* LOPGAVOL - [6:0] */
+#define WM8400_LOPGAVOL_WIDTH                        7  /* LOPGAVOL - [6:0] */
+
+/*
+ * R33 (0x21) - Right OPGA Volume
+ */
+#define WM8400_OPVU                             0x0100  /* OPVU */
+#define WM8400_OPVU_MASK                        0x0100  /* OPVU */
+#define WM8400_OPVU_SHIFT                            8  /* OPVU */
+#define WM8400_OPVU_WIDTH                            1  /* OPVU */
+#define WM8400_ROPGAZC                          0x0080  /* ROPGAZC */
+#define WM8400_ROPGAZC_MASK                     0x0080  /* ROPGAZC */
+#define WM8400_ROPGAZC_SHIFT                         7  /* ROPGAZC */
+#define WM8400_ROPGAZC_WIDTH                         1  /* ROPGAZC */
+#define WM8400_ROPGAVOL_MASK                    0x007F  /* ROPGAVOL - [6:0] */
+#define WM8400_ROPGAVOL_SHIFT                        0  /* ROPGAVOL - [6:0] */
+#define WM8400_ROPGAVOL_WIDTH                        7  /* ROPGAVOL - [6:0] */
+
+/*
+ * R34 (0x22) - Speaker Volume
+ */
+#define WM8400_SPKATTN_MASK                     0x0003  /* SPKATTN - [1:0] */
+#define WM8400_SPKATTN_SHIFT                         0  /* SPKATTN - [1:0] */
+#define WM8400_SPKATTN_WIDTH                         2  /* SPKATTN - [1:0] */
+
+/*
+ * R35 (0x23) - ClassD1
+ */
+#define WM8400_CDMODE                           0x0100  /* CDMODE */
+#define WM8400_CDMODE_MASK                      0x0100  /* CDMODE */
+#define WM8400_CDMODE_SHIFT                          8  /* CDMODE */
+#define WM8400_CDMODE_WIDTH                          1  /* CDMODE */
+#define WM8400_CLASSD_CLK_SEL                   0x0080  /* CLASSD_CLK_SEL */
+#define WM8400_CLASSD_CLK_SEL_MASK              0x0080  /* CLASSD_CLK_SEL */
+#define WM8400_CLASSD_CLK_SEL_SHIFT                  7  /* CLASSD_CLK_SEL */
+#define WM8400_CLASSD_CLK_SEL_WIDTH                  1  /* CLASSD_CLK_SEL */
+#define WM8400_CD_SRCTRL                        0x0040  /* CD_SRCTRL */
+#define WM8400_CD_SRCTRL_MASK                   0x0040  /* CD_SRCTRL */
+#define WM8400_CD_SRCTRL_SHIFT                       6  /* CD_SRCTRL */
+#define WM8400_CD_SRCTRL_WIDTH                       1  /* CD_SRCTRL */
+#define WM8400_SPKNOPOP                         0x0020  /* SPKNOPOP */
+#define WM8400_SPKNOPOP_MASK                    0x0020  /* SPKNOPOP */
+#define WM8400_SPKNOPOP_SHIFT                        5  /* SPKNOPOP */
+#define WM8400_SPKNOPOP_WIDTH                        1  /* SPKNOPOP */
+#define WM8400_DBLERATE                         0x0010  /* DBLERATE */
+#define WM8400_DBLERATE_MASK                    0x0010  /* DBLERATE */
+#define WM8400_DBLERATE_SHIFT                        4  /* DBLERATE */
+#define WM8400_DBLERATE_WIDTH                        1  /* DBLERATE */
+#define WM8400_LOOPTEST                         0x0008  /* LOOPTEST */
+#define WM8400_LOOPTEST_MASK                    0x0008  /* LOOPTEST */
+#define WM8400_LOOPTEST_SHIFT                        3  /* LOOPTEST */
+#define WM8400_LOOPTEST_WIDTH                        1  /* LOOPTEST */
+#define WM8400_HALFABBIAS                       0x0004  /* HALFABBIAS */
+#define WM8400_HALFABBIAS_MASK                  0x0004  /* HALFABBIAS */
+#define WM8400_HALFABBIAS_SHIFT                      2  /* HALFABBIAS */
+#define WM8400_HALFABBIAS_WIDTH                      1  /* HALFABBIAS */
+#define WM8400_TRIDEL_MASK                      0x0003  /* TRIDEL - [1:0] */
+#define WM8400_TRIDEL_SHIFT                          0  /* TRIDEL - [1:0] */
+#define WM8400_TRIDEL_WIDTH                          2  /* TRIDEL - [1:0] */
+
+/*
+ * R37 (0x25) - ClassD3
+ */
+#define WM8400_DCGAIN_MASK                      0x0038  /* DCGAIN - [5:3] */
+#define WM8400_DCGAIN_SHIFT                          3  /* DCGAIN - [5:3] */
+#define WM8400_DCGAIN_WIDTH                          3  /* DCGAIN - [5:3] */
+#define WM8400_ACGAIN_MASK                      0x0007  /* ACGAIN - [2:0] */
+#define WM8400_ACGAIN_SHIFT                          0  /* ACGAIN - [2:0] */
+#define WM8400_ACGAIN_WIDTH                          3  /* ACGAIN - [2:0] */
+
+/*
+ * R39 (0x27) - Input Mixer1
+ */
+#define WM8400_AINLMODE_MASK                    0x000C  /* AINLMODE - [3:2] */
+#define WM8400_AINLMODE_SHIFT                        2  /* AINLMODE - [3:2] */
+#define WM8400_AINLMODE_WIDTH                        2  /* AINLMODE - [3:2] */
+#define WM8400_AINRMODE_MASK                    0x0003  /* AINRMODE - [1:0] */
+#define WM8400_AINRMODE_SHIFT                        0  /* AINRMODE - [1:0] */
+#define WM8400_AINRMODE_WIDTH                        2  /* AINRMODE - [1:0] */
+
+/*
+ * R40 (0x28) - Input Mixer2
+ */
+#define WM8400_LMP4                             0x0080  /* LMP4 */
+#define WM8400_LMP4_MASK                        0x0080  /* LMP4 */
+#define WM8400_LMP4_SHIFT                            7  /* LMP4 */
+#define WM8400_LMP4_WIDTH                            1  /* LMP4 */
+#define WM8400_LMN3                             0x0040  /* LMN3 */
+#define WM8400_LMN3_MASK                        0x0040  /* LMN3 */
+#define WM8400_LMN3_SHIFT                            6  /* LMN3 */
+#define WM8400_LMN3_WIDTH                            1  /* LMN3 */
+#define WM8400_LMP2                             0x0020  /* LMP2 */
+#define WM8400_LMP2_MASK                        0x0020  /* LMP2 */
+#define WM8400_LMP2_SHIFT                            5  /* LMP2 */
+#define WM8400_LMP2_WIDTH                            1  /* LMP2 */
+#define WM8400_LMN1                             0x0010  /* LMN1 */
+#define WM8400_LMN1_MASK                        0x0010  /* LMN1 */
+#define WM8400_LMN1_SHIFT                            4  /* LMN1 */
+#define WM8400_LMN1_WIDTH                            1  /* LMN1 */
+#define WM8400_RMP4                             0x0008  /* RMP4 */
+#define WM8400_RMP4_MASK                        0x0008  /* RMP4 */
+#define WM8400_RMP4_SHIFT                            3  /* RMP4 */
+#define WM8400_RMP4_WIDTH                            1  /* RMP4 */
+#define WM8400_RMN3                             0x0004  /* RMN3 */
+#define WM8400_RMN3_MASK                        0x0004  /* RMN3 */
+#define WM8400_RMN3_SHIFT                            2  /* RMN3 */
+#define WM8400_RMN3_WIDTH                            1  /* RMN3 */
+#define WM8400_RMP2                             0x0002  /* RMP2 */
+#define WM8400_RMP2_MASK                        0x0002  /* RMP2 */
+#define WM8400_RMP2_SHIFT                            1  /* RMP2 */
+#define WM8400_RMP2_WIDTH                            1  /* RMP2 */
+#define WM8400_RMN1                             0x0001  /* RMN1 */
+#define WM8400_RMN1_MASK                        0x0001  /* RMN1 */
+#define WM8400_RMN1_SHIFT                            0  /* RMN1 */
+#define WM8400_RMN1_WIDTH                            1  /* RMN1 */
+
+/*
+ * R41 (0x29) - Input Mixer3
+ */
+#define WM8400_L34MNB                           0x0100  /* L34MNB */
+#define WM8400_L34MNB_MASK                      0x0100  /* L34MNB */
+#define WM8400_L34MNB_SHIFT                          8  /* L34MNB */
+#define WM8400_L34MNB_WIDTH                          1  /* L34MNB */
+#define WM8400_L34MNBST                         0x0080  /* L34MNBST */
+#define WM8400_L34MNBST_MASK                    0x0080  /* L34MNBST */
+#define WM8400_L34MNBST_SHIFT                        7  /* L34MNBST */
+#define WM8400_L34MNBST_WIDTH                        1  /* L34MNBST */
+#define WM8400_L12MNB                           0x0020  /* L12MNB */
+#define WM8400_L12MNB_MASK                      0x0020  /* L12MNB */
+#define WM8400_L12MNB_SHIFT                          5  /* L12MNB */
+#define WM8400_L12MNB_WIDTH                          1  /* L12MNB */
+#define WM8400_L12MNBST                         0x0010  /* L12MNBST */
+#define WM8400_L12MNBST_MASK                    0x0010  /* L12MNBST */
+#define WM8400_L12MNBST_SHIFT                        4  /* L12MNBST */
+#define WM8400_L12MNBST_WIDTH                        1  /* L12MNBST */
+#define WM8400_LDBVOL_MASK                      0x0007  /* LDBVOL - [2:0] */
+#define WM8400_LDBVOL_SHIFT                          0  /* LDBVOL - [2:0] */
+#define WM8400_LDBVOL_WIDTH                          3  /* LDBVOL - [2:0] */
+
+/*
+ * R42 (0x2A) - Input Mixer4
+ */
+#define WM8400_R34MNB                           0x0100  /* R34MNB */
+#define WM8400_R34MNB_MASK                      0x0100  /* R34MNB */
+#define WM8400_R34MNB_SHIFT                          8  /* R34MNB */
+#define WM8400_R34MNB_WIDTH                          1  /* R34MNB */
+#define WM8400_R34MNBST                         0x0080  /* R34MNBST */
+#define WM8400_R34MNBST_MASK                    0x0080  /* R34MNBST */
+#define WM8400_R34MNBST_SHIFT                        7  /* R34MNBST */
+#define WM8400_R34MNBST_WIDTH                        1  /* R34MNBST */
+#define WM8400_R12MNB                           0x0020  /* R12MNB */
+#define WM8400_R12MNB_MASK                      0x0020  /* R12MNB */
+#define WM8400_R12MNB_SHIFT                          5  /* R12MNB */
+#define WM8400_R12MNB_WIDTH                          1  /* R12MNB */
+#define WM8400_R12MNBST                         0x0010  /* R12MNBST */
+#define WM8400_R12MNBST_MASK                    0x0010  /* R12MNBST */
+#define WM8400_R12MNBST_SHIFT                        4  /* R12MNBST */
+#define WM8400_R12MNBST_WIDTH                        1  /* R12MNBST */
+#define WM8400_RDBVOL_MASK                      0x0007  /* RDBVOL - [2:0] */
+#define WM8400_RDBVOL_SHIFT                          0  /* RDBVOL - [2:0] */
+#define WM8400_RDBVOL_WIDTH                          3  /* RDBVOL - [2:0] */
+
+/*
+ * R43 (0x2B) - Input Mixer5
+ */
+#define WM8400_LI2BVOL_MASK                     0x01C0  /* LI2BVOL - [8:6] */
+#define WM8400_LI2BVOL_SHIFT                         6  /* LI2BVOL - [8:6] */
+#define WM8400_LI2BVOL_WIDTH                         3  /* LI2BVOL - [8:6] */
+#define WM8400_LR4BVOL_MASK                     0x0038  /* LR4BVOL - [5:3] */
+#define WM8400_LR4BVOL_SHIFT                         3  /* LR4BVOL - [5:3] */
+#define WM8400_LR4BVOL_WIDTH                         3  /* LR4BVOL - [5:3] */
+#define WM8400_LL4BVOL_MASK                     0x0007  /* LL4BVOL - [2:0] */
+#define WM8400_LL4BVOL_SHIFT                         0  /* LL4BVOL - [2:0] */
+#define WM8400_LL4BVOL_WIDTH                         3  /* LL4BVOL - [2:0] */
+
+/*
+ * R44 (0x2C) - Input Mixer6
+ */
+#define WM8400_RI2BVOL_MASK                     0x01C0  /* RI2BVOL - [8:6] */
+#define WM8400_RI2BVOL_SHIFT                         6  /* RI2BVOL - [8:6] */
+#define WM8400_RI2BVOL_WIDTH                         3  /* RI2BVOL - [8:6] */
+#define WM8400_RL4BVOL_MASK                     0x0038  /* RL4BVOL - [5:3] */
+#define WM8400_RL4BVOL_SHIFT                         3  /* RL4BVOL - [5:3] */
+#define WM8400_RL4BVOL_WIDTH                         3  /* RL4BVOL - [5:3] */
+#define WM8400_RR4BVOL_MASK                     0x0007  /* RR4BVOL - [2:0] */
+#define WM8400_RR4BVOL_SHIFT                         0  /* RR4BVOL - [2:0] */
+#define WM8400_RR4BVOL_WIDTH                         3  /* RR4BVOL - [2:0] */
+
+/*
+ * R45 (0x2D) - Output Mixer1
+ */
+#define WM8400_LRBLO                            0x0080  /* LRBLO */
+#define WM8400_LRBLO_MASK                       0x0080  /* LRBLO */
+#define WM8400_LRBLO_SHIFT                           7  /* LRBLO */
+#define WM8400_LRBLO_WIDTH                           1  /* LRBLO */
+#define WM8400_LLBLO                            0x0040  /* LLBLO */
+#define WM8400_LLBLO_MASK                       0x0040  /* LLBLO */
+#define WM8400_LLBLO_SHIFT                           6  /* LLBLO */
+#define WM8400_LLBLO_WIDTH                           1  /* LLBLO */
+#define WM8400_LRI3LO                           0x0020  /* LRI3LO */
+#define WM8400_LRI3LO_MASK                      0x0020  /* LRI3LO */
+#define WM8400_LRI3LO_SHIFT                          5  /* LRI3LO */
+#define WM8400_LRI3LO_WIDTH                          1  /* LRI3LO */
+#define WM8400_LLI3LO                           0x0010  /* LLI3LO */
+#define WM8400_LLI3LO_MASK                      0x0010  /* LLI3LO */
+#define WM8400_LLI3LO_SHIFT                          4  /* LLI3LO */
+#define WM8400_LLI3LO_WIDTH                          1  /* LLI3LO */
+#define WM8400_LR12LO                           0x0008  /* LR12LO */
+#define WM8400_LR12LO_MASK                      0x0008  /* LR12LO */
+#define WM8400_LR12LO_SHIFT                          3  /* LR12LO */
+#define WM8400_LR12LO_WIDTH                          1  /* LR12LO */
+#define WM8400_LL12LO                           0x0004  /* LL12LO */
+#define WM8400_LL12LO_MASK                      0x0004  /* LL12LO */
+#define WM8400_LL12LO_SHIFT                          2  /* LL12LO */
+#define WM8400_LL12LO_WIDTH                          1  /* LL12LO */
+#define WM8400_LDLO                             0x0001  /* LDLO */
+#define WM8400_LDLO_MASK                        0x0001  /* LDLO */
+#define WM8400_LDLO_SHIFT                            0  /* LDLO */
+#define WM8400_LDLO_WIDTH                            1  /* LDLO */
+
+/*
+ * R46 (0x2E) - Output Mixer2
+ */
+#define WM8400_RLBRO                            0x0080  /* RLBRO */
+#define WM8400_RLBRO_MASK                       0x0080  /* RLBRO */
+#define WM8400_RLBRO_SHIFT                           7  /* RLBRO */
+#define WM8400_RLBRO_WIDTH                           1  /* RLBRO */
+#define WM8400_RRBRO                            0x0040  /* RRBRO */
+#define WM8400_RRBRO_MASK                       0x0040  /* RRBRO */
+#define WM8400_RRBRO_SHIFT                           6  /* RRBRO */
+#define WM8400_RRBRO_WIDTH                           1  /* RRBRO */
+#define WM8400_RLI3RO                           0x0020  /* RLI3RO */
+#define WM8400_RLI3RO_MASK                      0x0020  /* RLI3RO */
+#define WM8400_RLI3RO_SHIFT                          5  /* RLI3RO */
+#define WM8400_RLI3RO_WIDTH                          1  /* RLI3RO */
+#define WM8400_RRI3RO                           0x0010  /* RRI3RO */
+#define WM8400_RRI3RO_MASK                      0x0010  /* RRI3RO */
+#define WM8400_RRI3RO_SHIFT                          4  /* RRI3RO */
+#define WM8400_RRI3RO_WIDTH                          1  /* RRI3RO */
+#define WM8400_RL12RO                           0x0008  /* RL12RO */
+#define WM8400_RL12RO_MASK                      0x0008  /* RL12RO */
+#define WM8400_RL12RO_SHIFT                          3  /* RL12RO */
+#define WM8400_RL12RO_WIDTH                          1  /* RL12RO */
+#define WM8400_RR12RO                           0x0004  /* RR12RO */
+#define WM8400_RR12RO_MASK                      0x0004  /* RR12RO */
+#define WM8400_RR12RO_SHIFT                          2  /* RR12RO */
+#define WM8400_RR12RO_WIDTH                          1  /* RR12RO */
+#define WM8400_RDRO                             0x0001  /* RDRO */
+#define WM8400_RDRO_MASK                        0x0001  /* RDRO */
+#define WM8400_RDRO_SHIFT                            0  /* RDRO */
+#define WM8400_RDRO_WIDTH                            1  /* RDRO */
+
+/*
+ * R47 (0x2F) - Output Mixer3
+ */
+#define WM8400_LLI3LOVOL_MASK                   0x01C0  /* LLI3LOVOL - [8:6] */
+#define WM8400_LLI3LOVOL_SHIFT                       6  /* LLI3LOVOL - [8:6] */
+#define WM8400_LLI3LOVOL_WIDTH                       3  /* LLI3LOVOL - [8:6] */
+#define WM8400_LR12LOVOL_MASK                   0x0038  /* LR12LOVOL - [5:3] */
+#define WM8400_LR12LOVOL_SHIFT                       3  /* LR12LOVOL - [5:3] */
+#define WM8400_LR12LOVOL_WIDTH                       3  /* LR12LOVOL - [5:3] */
+#define WM8400_LL12LOVOL_MASK                   0x0007  /* LL12LOVOL - [2:0] */
+#define WM8400_LL12LOVOL_SHIFT                       0  /* LL12LOVOL - [2:0] */
+#define WM8400_LL12LOVOL_WIDTH                       3  /* LL12LOVOL - [2:0] */
+
+/*
+ * R48 (0x30) - Output Mixer4
+ */
+#define WM8400_RRI3ROVOL_MASK                   0x01C0  /* RRI3ROVOL - [8:6] */
+#define WM8400_RRI3ROVOL_SHIFT                       6  /* RRI3ROVOL - [8:6] */
+#define WM8400_RRI3ROVOL_WIDTH                       3  /* RRI3ROVOL - [8:6] */
+#define WM8400_RL12ROVOL_MASK                   0x0038  /* RL12ROVOL - [5:3] */
+#define WM8400_RL12ROVOL_SHIFT                       3  /* RL12ROVOL - [5:3] */
+#define WM8400_RL12ROVOL_WIDTH                       3  /* RL12ROVOL - [5:3] */
+#define WM8400_RR12ROVOL_MASK                   0x0007  /* RR12ROVOL - [2:0] */
+#define WM8400_RR12ROVOL_SHIFT                       0  /* RR12ROVOL - [2:0] */
+#define WM8400_RR12ROVOL_WIDTH                       3  /* RR12ROVOL - [2:0] */
+
+/*
+ * R49 (0x31) - Output Mixer5
+ */
+#define WM8400_LRI3LOVOL_MASK                   0x01C0  /* LRI3LOVOL - [8:6] */
+#define WM8400_LRI3LOVOL_SHIFT                       6  /* LRI3LOVOL - [8:6] */
+#define WM8400_LRI3LOVOL_WIDTH                       3  /* LRI3LOVOL - [8:6] */
+#define WM8400_LRBLOVOL_MASK                    0x0038  /* LRBLOVOL - [5:3] */
+#define WM8400_LRBLOVOL_SHIFT                        3  /* LRBLOVOL - [5:3] */
+#define WM8400_LRBLOVOL_WIDTH                        3  /* LRBLOVOL - [5:3] */
+#define WM8400_LLBLOVOL_MASK                    0x0007  /* LLBLOVOL - [2:0] */
+#define WM8400_LLBLOVOL_SHIFT                        0  /* LLBLOVOL - [2:0] */
+#define WM8400_LLBLOVOL_WIDTH                        3  /* LLBLOVOL - [2:0] */
+
+/*
+ * R50 (0x32) - Output Mixer6
+ */
+#define WM8400_RLI3ROVOL_MASK                   0x01C0  /* RLI3ROVOL - [8:6] */
+#define WM8400_RLI3ROVOL_SHIFT                       6  /* RLI3ROVOL - [8:6] */
+#define WM8400_RLI3ROVOL_WIDTH                       3  /* RLI3ROVOL - [8:6] */
+#define WM8400_RLBROVOL_MASK                    0x0038  /* RLBROVOL - [5:3] */
+#define WM8400_RLBROVOL_SHIFT                        3  /* RLBROVOL - [5:3] */
+#define WM8400_RLBROVOL_WIDTH                        3  /* RLBROVOL - [5:3] */
+#define WM8400_RRBROVOL_MASK                    0x0007  /* RRBROVOL - [2:0] */
+#define WM8400_RRBROVOL_SHIFT                        0  /* RRBROVOL - [2:0] */
+#define WM8400_RRBROVOL_WIDTH                        3  /* RRBROVOL - [2:0] */
+
+/*
+ * R51 (0x33) - Out3/4 Mixer
+ */
+#define WM8400_VSEL_MASK                        0x0180  /* VSEL - [8:7] */
+#define WM8400_VSEL_SHIFT                            7  /* VSEL - [8:7] */
+#define WM8400_VSEL_WIDTH                            2  /* VSEL - [8:7] */
+#define WM8400_LI4O3                            0x0020  /* LI4O3 */
+#define WM8400_LI4O3_MASK                       0x0020  /* LI4O3 */
+#define WM8400_LI4O3_SHIFT                           5  /* LI4O3 */
+#define WM8400_LI4O3_WIDTH                           1  /* LI4O3 */
+#define WM8400_LPGAO3                           0x0010  /* LPGAO3 */
+#define WM8400_LPGAO3_MASK                      0x0010  /* LPGAO3 */
+#define WM8400_LPGAO3_SHIFT                          4  /* LPGAO3 */
+#define WM8400_LPGAO3_WIDTH                          1  /* LPGAO3 */
+#define WM8400_RI4O4                            0x0002  /* RI4O4 */
+#define WM8400_RI4O4_MASK                       0x0002  /* RI4O4 */
+#define WM8400_RI4O4_SHIFT                           1  /* RI4O4 */
+#define WM8400_RI4O4_WIDTH                           1  /* RI4O4 */
+#define WM8400_RPGAO4                           0x0001  /* RPGAO4 */
+#define WM8400_RPGAO4_MASK                      0x0001  /* RPGAO4 */
+#define WM8400_RPGAO4_SHIFT                          0  /* RPGAO4 */
+#define WM8400_RPGAO4_WIDTH                          1  /* RPGAO4 */
+
+/*
+ * R52 (0x34) - Line Mixer1
+ */
+#define WM8400_LLOPGALON                        0x0040  /* LLOPGALON */
+#define WM8400_LLOPGALON_MASK                   0x0040  /* LLOPGALON */
+#define WM8400_LLOPGALON_SHIFT                       6  /* LLOPGALON */
+#define WM8400_LLOPGALON_WIDTH                       1  /* LLOPGALON */
+#define WM8400_LROPGALON                        0x0020  /* LROPGALON */
+#define WM8400_LROPGALON_MASK                   0x0020  /* LROPGALON */
+#define WM8400_LROPGALON_SHIFT                       5  /* LROPGALON */
+#define WM8400_LROPGALON_WIDTH                       1  /* LROPGALON */
+#define WM8400_LOPLON                           0x0010  /* LOPLON */
+#define WM8400_LOPLON_MASK                      0x0010  /* LOPLON */
+#define WM8400_LOPLON_SHIFT                          4  /* LOPLON */
+#define WM8400_LOPLON_WIDTH                          1  /* LOPLON */
+#define WM8400_LR12LOP                          0x0004  /* LR12LOP */
+#define WM8400_LR12LOP_MASK                     0x0004  /* LR12LOP */
+#define WM8400_LR12LOP_SHIFT                         2  /* LR12LOP */
+#define WM8400_LR12LOP_WIDTH                         1  /* LR12LOP */
+#define WM8400_LL12LOP                          0x0002  /* LL12LOP */
+#define WM8400_LL12LOP_MASK                     0x0002  /* LL12LOP */
+#define WM8400_LL12LOP_SHIFT                         1  /* LL12LOP */
+#define WM8400_LL12LOP_WIDTH                         1  /* LL12LOP */
+#define WM8400_LLOPGALOP                        0x0001  /* LLOPGALOP */
+#define WM8400_LLOPGALOP_MASK                   0x0001  /* LLOPGALOP */
+#define WM8400_LLOPGALOP_SHIFT                       0  /* LLOPGALOP */
+#define WM8400_LLOPGALOP_WIDTH                       1  /* LLOPGALOP */
+
+/*
+ * R53 (0x35) - Line Mixer2
+ */
+#define WM8400_RROPGARON                        0x0040  /* RROPGARON */
+#define WM8400_RROPGARON_MASK                   0x0040  /* RROPGARON */
+#define WM8400_RROPGARON_SHIFT                       6  /* RROPGARON */
+#define WM8400_RROPGARON_WIDTH                       1  /* RROPGARON */
+#define WM8400_RLOPGARON                        0x0020  /* RLOPGARON */
+#define WM8400_RLOPGARON_MASK                   0x0020  /* RLOPGARON */
+#define WM8400_RLOPGARON_SHIFT                       5  /* RLOPGARON */
+#define WM8400_RLOPGARON_WIDTH                       1  /* RLOPGARON */
+#define WM8400_ROPRON                           0x0010  /* ROPRON */
+#define WM8400_ROPRON_MASK                      0x0010  /* ROPRON */
+#define WM8400_ROPRON_SHIFT                          4  /* ROPRON */
+#define WM8400_ROPRON_WIDTH                          1  /* ROPRON */
+#define WM8400_RL12ROP                          0x0004  /* RL12ROP */
+#define WM8400_RL12ROP_MASK                     0x0004  /* RL12ROP */
+#define WM8400_RL12ROP_SHIFT                         2  /* RL12ROP */
+#define WM8400_RL12ROP_WIDTH                         1  /* RL12ROP */
+#define WM8400_RR12ROP                          0x0002  /* RR12ROP */
+#define WM8400_RR12ROP_MASK                     0x0002  /* RR12ROP */
+#define WM8400_RR12ROP_SHIFT                         1  /* RR12ROP */
+#define WM8400_RR12ROP_WIDTH                         1  /* RR12ROP */
+#define WM8400_RROPGAROP                        0x0001  /* RROPGAROP */
+#define WM8400_RROPGAROP_MASK                   0x0001  /* RROPGAROP */
+#define WM8400_RROPGAROP_SHIFT                       0  /* RROPGAROP */
+#define WM8400_RROPGAROP_WIDTH                       1  /* RROPGAROP */
+
+/*
+ * R54 (0x36) - Speaker Mixer
+ */
+#define WM8400_LB2SPK                           0x0080  /* LB2SPK */
+#define WM8400_LB2SPK_MASK                      0x0080  /* LB2SPK */
+#define WM8400_LB2SPK_SHIFT                          7  /* LB2SPK */
+#define WM8400_LB2SPK_WIDTH                          1  /* LB2SPK */
+#define WM8400_RB2SPK                           0x0040  /* RB2SPK */
+#define WM8400_RB2SPK_MASK                      0x0040  /* RB2SPK */
+#define WM8400_RB2SPK_SHIFT                          6  /* RB2SPK */
+#define WM8400_RB2SPK_WIDTH                          1  /* RB2SPK */
+#define WM8400_LI2SPK                           0x0020  /* LI2SPK */
+#define WM8400_LI2SPK_MASK                      0x0020  /* LI2SPK */
+#define WM8400_LI2SPK_SHIFT                          5  /* LI2SPK */
+#define WM8400_LI2SPK_WIDTH                          1  /* LI2SPK */
+#define WM8400_RI2SPK                           0x0010  /* RI2SPK */
+#define WM8400_RI2SPK_MASK                      0x0010  /* RI2SPK */
+#define WM8400_RI2SPK_SHIFT                          4  /* RI2SPK */
+#define WM8400_RI2SPK_WIDTH                          1  /* RI2SPK */
+#define WM8400_LOPGASPK                         0x0008  /* LOPGASPK */
+#define WM8400_LOPGASPK_MASK                    0x0008  /* LOPGASPK */
+#define WM8400_LOPGASPK_SHIFT                        3  /* LOPGASPK */
+#define WM8400_LOPGASPK_WIDTH                        1  /* LOPGASPK */
+#define WM8400_ROPGASPK                         0x0004  /* ROPGASPK */
+#define WM8400_ROPGASPK_MASK                    0x0004  /* ROPGASPK */
+#define WM8400_ROPGASPK_SHIFT                        2  /* ROPGASPK */
+#define WM8400_ROPGASPK_WIDTH                        1  /* ROPGASPK */
+#define WM8400_LDSPK                            0x0002  /* LDSPK */
+#define WM8400_LDSPK_MASK                       0x0002  /* LDSPK */
+#define WM8400_LDSPK_SHIFT                           1  /* LDSPK */
+#define WM8400_LDSPK_WIDTH                           1  /* LDSPK */
+#define WM8400_RDSPK                            0x0001  /* RDSPK */
+#define WM8400_RDSPK_MASK                       0x0001  /* RDSPK */
+#define WM8400_RDSPK_SHIFT                           0  /* RDSPK */
+#define WM8400_RDSPK_WIDTH                           1  /* RDSPK */
+
+/*
+ * R55 (0x37) - Additional Control
+ */
+#define WM8400_VROI                             0x0001  /* VROI */
+#define WM8400_VROI_MASK                        0x0001  /* VROI */
+#define WM8400_VROI_SHIFT                            0  /* VROI */
+#define WM8400_VROI_WIDTH                            1  /* VROI */
+
+/*
+ * R56 (0x38) - AntiPOP1
+ */
+#define WM8400_DIS_LLINE                        0x0020  /* DIS_LLINE */
+#define WM8400_DIS_LLINE_MASK                   0x0020  /* DIS_LLINE */
+#define WM8400_DIS_LLINE_SHIFT                       5  /* DIS_LLINE */
+#define WM8400_DIS_LLINE_WIDTH                       1  /* DIS_LLINE */
+#define WM8400_DIS_RLINE                        0x0010  /* DIS_RLINE */
+#define WM8400_DIS_RLINE_MASK                   0x0010  /* DIS_RLINE */
+#define WM8400_DIS_RLINE_SHIFT                       4  /* DIS_RLINE */
+#define WM8400_DIS_RLINE_WIDTH                       1  /* DIS_RLINE */
+#define WM8400_DIS_OUT3                         0x0008  /* DIS_OUT3 */
+#define WM8400_DIS_OUT3_MASK                    0x0008  /* DIS_OUT3 */
+#define WM8400_DIS_OUT3_SHIFT                        3  /* DIS_OUT3 */
+#define WM8400_DIS_OUT3_WIDTH                        1  /* DIS_OUT3 */
+#define WM8400_DIS_OUT4                         0x0004  /* DIS_OUT4 */
+#define WM8400_DIS_OUT4_MASK                    0x0004  /* DIS_OUT4 */
+#define WM8400_DIS_OUT4_SHIFT                        2  /* DIS_OUT4 */
+#define WM8400_DIS_OUT4_WIDTH                        1  /* DIS_OUT4 */
+#define WM8400_DIS_LOUT                         0x0002  /* DIS_LOUT */
+#define WM8400_DIS_LOUT_MASK                    0x0002  /* DIS_LOUT */
+#define WM8400_DIS_LOUT_SHIFT                        1  /* DIS_LOUT */
+#define WM8400_DIS_LOUT_WIDTH                        1  /* DIS_LOUT */
+#define WM8400_DIS_ROUT                         0x0001  /* DIS_ROUT */
+#define WM8400_DIS_ROUT_MASK                    0x0001  /* DIS_ROUT */
+#define WM8400_DIS_ROUT_SHIFT                        0  /* DIS_ROUT */
+#define WM8400_DIS_ROUT_WIDTH                        1  /* DIS_ROUT */
+
+/*
+ * R57 (0x39) - AntiPOP2
+ */
+#define WM8400_SOFTST                           0x0040  /* SOFTST */
+#define WM8400_SOFTST_MASK                      0x0040  /* SOFTST */
+#define WM8400_SOFTST_SHIFT                          6  /* SOFTST */
+#define WM8400_SOFTST_WIDTH                          1  /* SOFTST */
+#define WM8400_BUFIOEN                          0x0008  /* BUFIOEN */
+#define WM8400_BUFIOEN_MASK                     0x0008  /* BUFIOEN */
+#define WM8400_BUFIOEN_SHIFT                         3  /* BUFIOEN */
+#define WM8400_BUFIOEN_WIDTH                         1  /* BUFIOEN */
+#define WM8400_BUFDCOPEN                        0x0004  /* BUFDCOPEN */
+#define WM8400_BUFDCOPEN_MASK                   0x0004  /* BUFDCOPEN */
+#define WM8400_BUFDCOPEN_SHIFT                       2  /* BUFDCOPEN */
+#define WM8400_BUFDCOPEN_WIDTH                       1  /* BUFDCOPEN */
+#define WM8400_POBCTRL                          0x0002  /* POBCTRL */
+#define WM8400_POBCTRL_MASK                     0x0002  /* POBCTRL */
+#define WM8400_POBCTRL_SHIFT                         1  /* POBCTRL */
+#define WM8400_POBCTRL_WIDTH                         1  /* POBCTRL */
+#define WM8400_VMIDTOG                          0x0001  /* VMIDTOG */
+#define WM8400_VMIDTOG_MASK                     0x0001  /* VMIDTOG */
+#define WM8400_VMIDTOG_SHIFT                         0  /* VMIDTOG */
+#define WM8400_VMIDTOG_WIDTH                         1  /* VMIDTOG */
+
+/*
+ * R58 (0x3A) - MICBIAS
+ */
+#define WM8400_MCDSCTH_MASK                     0x00C0  /* MCDSCTH - [7:6] */
+#define WM8400_MCDSCTH_SHIFT                         6  /* MCDSCTH - [7:6] */
+#define WM8400_MCDSCTH_WIDTH                         2  /* MCDSCTH - [7:6] */
+#define WM8400_MCDTHR_MASK                      0x0038  /* MCDTHR - [5:3] */
+#define WM8400_MCDTHR_SHIFT                          3  /* MCDTHR - [5:3] */
+#define WM8400_MCDTHR_WIDTH                          3  /* MCDTHR - [5:3] */
+#define WM8400_MCD                              0x0004  /* MCD */
+#define WM8400_MCD_MASK                         0x0004  /* MCD */
+#define WM8400_MCD_SHIFT                             2  /* MCD */
+#define WM8400_MCD_WIDTH                             1  /* MCD */
+#define WM8400_MBSEL                            0x0001  /* MBSEL */
+#define WM8400_MBSEL_MASK                       0x0001  /* MBSEL */
+#define WM8400_MBSEL_SHIFT                           0  /* MBSEL */
+#define WM8400_MBSEL_WIDTH                           1  /* MBSEL */
+
+/*
+ * R60 (0x3C) - FLL Control 1
+ */
+#define WM8400_FLL_REF_FREQ                     0x1000  /* FLL_REF_FREQ */
+#define WM8400_FLL_REF_FREQ_MASK                0x1000  /* FLL_REF_FREQ */
+#define WM8400_FLL_REF_FREQ_SHIFT                   12  /* FLL_REF_FREQ */
+#define WM8400_FLL_REF_FREQ_WIDTH                    1  /* FLL_REF_FREQ */
+#define WM8400_FLL_CLK_SRC_MASK                 0x0C00  /* FLL_CLK_SRC - [11:10] */
+#define WM8400_FLL_CLK_SRC_SHIFT                    10  /* FLL_CLK_SRC - [11:10] */
+#define WM8400_FLL_CLK_SRC_WIDTH                     2  /* FLL_CLK_SRC - [11:10] */
+#define WM8400_FLL_FRAC                         0x0200  /* FLL_FRAC */
+#define WM8400_FLL_FRAC_MASK                    0x0200  /* FLL_FRAC */
+#define WM8400_FLL_FRAC_SHIFT                        9  /* FLL_FRAC */
+#define WM8400_FLL_FRAC_WIDTH                        1  /* FLL_FRAC */
+#define WM8400_FLL_OSC_ENA                      0x0100  /* FLL_OSC_ENA */
+#define WM8400_FLL_OSC_ENA_MASK                 0x0100  /* FLL_OSC_ENA */
+#define WM8400_FLL_OSC_ENA_SHIFT                     8  /* FLL_OSC_ENA */
+#define WM8400_FLL_OSC_ENA_WIDTH                     1  /* FLL_OSC_ENA */
+#define WM8400_FLL_CTRL_RATE_MASK               0x00E0  /* FLL_CTRL_RATE - [7:5] */
+#define WM8400_FLL_CTRL_RATE_SHIFT                   5  /* FLL_CTRL_RATE - [7:5] */
+#define WM8400_FLL_CTRL_RATE_WIDTH                   3  /* FLL_CTRL_RATE - [7:5] */
+#define WM8400_FLL_FRATIO_MASK                  0x001F  /* FLL_FRATIO - [4:0] */
+#define WM8400_FLL_FRATIO_SHIFT                      0  /* FLL_FRATIO - [4:0] */
+#define WM8400_FLL_FRATIO_WIDTH                      5  /* FLL_FRATIO - [4:0] */
+
+/*
+ * R61 (0x3D) - FLL Control 2
+ */
+#define WM8400_FLL_K_MASK                       0xFFFF  /* FLL_K - [15:0] */
+#define WM8400_FLL_K_SHIFT                           0  /* FLL_K - [15:0] */
+#define WM8400_FLL_K_WIDTH                          16  /* FLL_K - [15:0] */
+
+/*
+ * R62 (0x3E) - FLL Control 3
+ */
+#define WM8400_FLL_N_MASK                       0x03FF  /* FLL_N - [9:0] */
+#define WM8400_FLL_N_SHIFT                           0  /* FLL_N - [9:0] */
+#define WM8400_FLL_N_WIDTH                          10  /* FLL_N - [9:0] */
+
+/*
+ * R63 (0x3F) - FLL Control 4
+ */
+#define WM8400_FLL_TRK_GAIN_MASK                0x0078  /* FLL_TRK_GAIN - [6:3] */
+#define WM8400_FLL_TRK_GAIN_SHIFT                    3  /* FLL_TRK_GAIN - [6:3] */
+#define WM8400_FLL_TRK_GAIN_WIDTH                    4  /* FLL_TRK_GAIN - [6:3] */
+#define WM8400_FLL_OUTDIV_MASK                  0x0007  /* FLL_OUTDIV - [2:0] */
+#define WM8400_FLL_OUTDIV_SHIFT                      0  /* FLL_OUTDIV - [2:0] */
+#define WM8400_FLL_OUTDIV_WIDTH                      3  /* FLL_OUTDIV - [2:0] */
+
+void wm8400_reset_codec_reg_cache(struct wm8400 *wm8400);
+
+#endif
diff --git a/include/linux/mfd/wm8400-private.h b/include/linux/mfd/wm8400-private.h
new file mode 100644
index 0000000..2aab4e9
--- /dev/null
+++ b/include/linux/mfd/wm8400-private.h
@@ -0,0 +1,936 @@
+/*
+ * wm8400 private definitions.
+ *
+ * Copyright 2008 Wolfson Microelectronics plc
+ *
+ * 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.
+ */
+
+#ifndef __LINUX_MFD_WM8400_PRIV_H
+#define __LINUX_MFD_WM8400_PRIV_H
+
+#include <linux/mfd/wm8400.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+
+#define WM8400_REGISTER_COUNT 0x55
+
+struct wm8400 {
+	struct device *dev;
+
+	int (*read_dev)(void *data, char reg, int count, u16 *dst);
+	int (*write_dev)(void *data, char reg, int count, const u16 *src);
+
+	struct mutex io_lock;
+	void *io_data;
+
+	u16 reg_cache[WM8400_REGISTER_COUNT];
+
+	struct platform_device regulators[6];
+};
+
+/*
+ * Register values.
+ */
+#define WM8400_RESET_ID                         0x00
+#define WM8400_ID                               0x01
+#define WM8400_POWER_MANAGEMENT_1               0x02
+#define WM8400_POWER_MANAGEMENT_2               0x03
+#define WM8400_POWER_MANAGEMENT_3               0x04
+#define WM8400_AUDIO_INTERFACE_1                0x05
+#define WM8400_AUDIO_INTERFACE_2                0x06
+#define WM8400_CLOCKING_1                       0x07
+#define WM8400_CLOCKING_2                       0x08
+#define WM8400_AUDIO_INTERFACE_3                0x09
+#define WM8400_AUDIO_INTERFACE_4                0x0A
+#define WM8400_DAC_CTRL                         0x0B
+#define WM8400_LEFT_DAC_DIGITAL_VOLUME          0x0C
+#define WM8400_RIGHT_DAC_DIGITAL_VOLUME         0x0D
+#define WM8400_DIGITAL_SIDE_TONE                0x0E
+#define WM8400_ADC_CTRL                         0x0F
+#define WM8400_LEFT_ADC_DIGITAL_VOLUME          0x10
+#define WM8400_RIGHT_ADC_DIGITAL_VOLUME         0x11
+#define WM8400_GPIO_CTRL_1                      0x12
+#define WM8400_GPIO1_GPIO2                      0x13
+#define WM8400_GPIO3_GPIO4                      0x14
+#define WM8400_GPIO5_GPIO6                      0x15
+#define WM8400_GPIOCTRL_2                       0x16
+#define WM8400_GPIO_POL                         0x17
+#define WM8400_LEFT_LINE_INPUT_1_2_VOLUME       0x18
+#define WM8400_LEFT_LINE_INPUT_3_4_VOLUME       0x19
+#define WM8400_RIGHT_LINE_INPUT_1_2_VOLUME      0x1A
+#define WM8400_RIGHT_LINE_INPUT_3_4_VOLUME      0x1B
+#define WM8400_LEFT_OUTPUT_VOLUME               0x1C
+#define WM8400_RIGHT_OUTPUT_VOLUME              0x1D
+#define WM8400_LINE_OUTPUTS_VOLUME              0x1E
+#define WM8400_OUT3_4_VOLUME                    0x1F
+#define WM8400_LEFT_OPGA_VOLUME                 0x20
+#define WM8400_RIGHT_OPGA_VOLUME                0x21
+#define WM8400_SPEAKER_VOLUME                   0x22
+#define WM8400_CLASSD1                          0x23
+#define WM8400_CLASSD3                          0x25
+#define WM8400_INPUT_MIXER1                     0x27
+#define WM8400_INPUT_MIXER2                     0x28
+#define WM8400_INPUT_MIXER3                     0x29
+#define WM8400_INPUT_MIXER4                     0x2A
+#define WM8400_INPUT_MIXER5                     0x2B
+#define WM8400_INPUT_MIXER6                     0x2C
+#define WM8400_OUTPUT_MIXER1                    0x2D
+#define WM8400_OUTPUT_MIXER2                    0x2E
+#define WM8400_OUTPUT_MIXER3                    0x2F
+#define WM8400_OUTPUT_MIXER4                    0x30
+#define WM8400_OUTPUT_MIXER5                    0x31
+#define WM8400_OUTPUT_MIXER6                    0x32
+#define WM8400_OUT3_4_MIXER                     0x33
+#define WM8400_LINE_MIXER1                      0x34
+#define WM8400_LINE_MIXER2                      0x35
+#define WM8400_SPEAKER_MIXER                    0x36
+#define WM8400_ADDITIONAL_CONTROL               0x37
+#define WM8400_ANTIPOP1                         0x38
+#define WM8400_ANTIPOP2                         0x39
+#define WM8400_MICBIAS                          0x3A
+#define WM8400_FLL_CONTROL_1                    0x3C
+#define WM8400_FLL_CONTROL_2                    0x3D
+#define WM8400_FLL_CONTROL_3                    0x3E
+#define WM8400_FLL_CONTROL_4                    0x3F
+#define WM8400_LDO1_CONTROL                     0x41
+#define WM8400_LDO2_CONTROL                     0x42
+#define WM8400_LDO3_CONTROL                     0x43
+#define WM8400_LDO4_CONTROL                     0x44
+#define WM8400_DCDC1_CONTROL_1                  0x46
+#define WM8400_DCDC1_CONTROL_2                  0x47
+#define WM8400_DCDC2_CONTROL_1                  0x48
+#define WM8400_DCDC2_CONTROL_2                  0x49
+#define WM8400_INTERFACE                        0x4B
+#define WM8400_PM_GENERAL                       0x4C
+#define WM8400_PM_SHUTDOWN_CONTROL              0x4E
+#define WM8400_INTERRUPT_STATUS_1               0x4F
+#define WM8400_INTERRUPT_STATUS_1_MASK          0x50
+#define WM8400_INTERRUPT_LEVELS                 0x51
+#define WM8400_SHUTDOWN_REASON                  0x52
+#define WM8400_LINE_CIRCUITS                    0x54
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - Reset/ID
+ */
+#define WM8400_SW_RESET_CHIP_ID_MASK            0xFFFF  /* SW_RESET/CHIP_ID - [15:0] */
+#define WM8400_SW_RESET_CHIP_ID_SHIFT                0  /* SW_RESET/CHIP_ID - [15:0] */
+#define WM8400_SW_RESET_CHIP_ID_WIDTH               16  /* SW_RESET/CHIP_ID - [15:0] */
+
+/*
+ * R1 (0x01) - ID
+ */
+#define WM8400_CHIP_REV_MASK                    0x7000  /* CHIP_REV - [14:12] */
+#define WM8400_CHIP_REV_SHIFT                       12  /* CHIP_REV - [14:12] */
+#define WM8400_CHIP_REV_WIDTH                        3  /* CHIP_REV - [14:12] */
+
+/*
+ * R18 (0x12) - GPIO CTRL 1
+ */
+#define WM8400_IRQ                              0x1000  /* IRQ */
+#define WM8400_IRQ_MASK                         0x1000  /* IRQ */
+#define WM8400_IRQ_SHIFT                            12  /* IRQ */
+#define WM8400_IRQ_WIDTH                             1  /* IRQ */
+#define WM8400_TEMPOK                           0x0800  /* TEMPOK */
+#define WM8400_TEMPOK_MASK                      0x0800  /* TEMPOK */
+#define WM8400_TEMPOK_SHIFT                         11  /* TEMPOK */
+#define WM8400_TEMPOK_WIDTH                          1  /* TEMPOK */
+#define WM8400_MIC1SHRT                         0x0400  /* MIC1SHRT */
+#define WM8400_MIC1SHRT_MASK                    0x0400  /* MIC1SHRT */
+#define WM8400_MIC1SHRT_SHIFT                       10  /* MIC1SHRT */
+#define WM8400_MIC1SHRT_WIDTH                        1  /* MIC1SHRT */
+#define WM8400_MIC1DET                          0x0200  /* MIC1DET */
+#define WM8400_MIC1DET_MASK                     0x0200  /* MIC1DET */
+#define WM8400_MIC1DET_SHIFT                         9  /* MIC1DET */
+#define WM8400_MIC1DET_WIDTH                         1  /* MIC1DET */
+#define WM8400_FLL_LCK                          0x0100  /* FLL_LCK */
+#define WM8400_FLL_LCK_MASK                     0x0100  /* FLL_LCK */
+#define WM8400_FLL_LCK_SHIFT                         8  /* FLL_LCK */
+#define WM8400_FLL_LCK_WIDTH                         1  /* FLL_LCK */
+#define WM8400_GPIO_STATUS_MASK                 0x00FF  /* GPIO_STATUS - [7:0] */
+#define WM8400_GPIO_STATUS_SHIFT                     0  /* GPIO_STATUS - [7:0] */
+#define WM8400_GPIO_STATUS_WIDTH                     8  /* GPIO_STATUS - [7:0] */
+
+/*
+ * R19 (0x13) - GPIO1 & GPIO2
+ */
+#define WM8400_GPIO2_DEB_ENA                    0x8000  /* GPIO2_DEB_ENA */
+#define WM8400_GPIO2_DEB_ENA_MASK               0x8000  /* GPIO2_DEB_ENA */
+#define WM8400_GPIO2_DEB_ENA_SHIFT                  15  /* GPIO2_DEB_ENA */
+#define WM8400_GPIO2_DEB_ENA_WIDTH                   1  /* GPIO2_DEB_ENA */
+#define WM8400_GPIO2_IRQ_ENA                    0x4000  /* GPIO2_IRQ_ENA */
+#define WM8400_GPIO2_IRQ_ENA_MASK               0x4000  /* GPIO2_IRQ_ENA */
+#define WM8400_GPIO2_IRQ_ENA_SHIFT                  14  /* GPIO2_IRQ_ENA */
+#define WM8400_GPIO2_IRQ_ENA_WIDTH                   1  /* GPIO2_IRQ_ENA */
+#define WM8400_GPIO2_PU                         0x2000  /* GPIO2_PU */
+#define WM8400_GPIO2_PU_MASK                    0x2000  /* GPIO2_PU */
+#define WM8400_GPIO2_PU_SHIFT                       13  /* GPIO2_PU */
+#define WM8400_GPIO2_PU_WIDTH                        1  /* GPIO2_PU */
+#define WM8400_GPIO2_PD                         0x1000  /* GPIO2_PD */
+#define WM8400_GPIO2_PD_MASK                    0x1000  /* GPIO2_PD */
+#define WM8400_GPIO2_PD_SHIFT                       12  /* GPIO2_PD */
+#define WM8400_GPIO2_PD_WIDTH                        1  /* GPIO2_PD */
+#define WM8400_GPIO2_SEL_MASK                   0x0F00  /* GPIO2_SEL - [11:8] */
+#define WM8400_GPIO2_SEL_SHIFT                       8  /* GPIO2_SEL - [11:8] */
+#define WM8400_GPIO2_SEL_WIDTH                       4  /* GPIO2_SEL - [11:8] */
+#define WM8400_GPIO1_DEB_ENA                    0x0080  /* GPIO1_DEB_ENA */
+#define WM8400_GPIO1_DEB_ENA_MASK               0x0080  /* GPIO1_DEB_ENA */
+#define WM8400_GPIO1_DEB_ENA_SHIFT                   7  /* GPIO1_DEB_ENA */
+#define WM8400_GPIO1_DEB_ENA_WIDTH                   1  /* GPIO1_DEB_ENA */
+#define WM8400_GPIO1_IRQ_ENA                    0x0040  /* GPIO1_IRQ_ENA */
+#define WM8400_GPIO1_IRQ_ENA_MASK               0x0040  /* GPIO1_IRQ_ENA */
+#define WM8400_GPIO1_IRQ_ENA_SHIFT                   6  /* GPIO1_IRQ_ENA */
+#define WM8400_GPIO1_IRQ_ENA_WIDTH                   1  /* GPIO1_IRQ_ENA */
+#define WM8400_GPIO1_PU                         0x0020  /* GPIO1_PU */
+#define WM8400_GPIO1_PU_MASK                    0x0020  /* GPIO1_PU */
+#define WM8400_GPIO1_PU_SHIFT                        5  /* GPIO1_PU */
+#define WM8400_GPIO1_PU_WIDTH                        1  /* GPIO1_PU */
+#define WM8400_GPIO1_PD                         0x0010  /* GPIO1_PD */
+#define WM8400_GPIO1_PD_MASK                    0x0010  /* GPIO1_PD */
+#define WM8400_GPIO1_PD_SHIFT                        4  /* GPIO1_PD */
+#define WM8400_GPIO1_PD_WIDTH                        1  /* GPIO1_PD */
+#define WM8400_GPIO1_SEL_MASK                   0x000F  /* GPIO1_SEL - [3:0] */
+#define WM8400_GPIO1_SEL_SHIFT                       0  /* GPIO1_SEL - [3:0] */
+#define WM8400_GPIO1_SEL_WIDTH                       4  /* GPIO1_SEL - [3:0] */
+
+/*
+ * R20 (0x14) - GPIO3 & GPIO4
+ */
+#define WM8400_GPIO4_DEB_ENA                    0x8000  /* GPIO4_DEB_ENA */
+#define WM8400_GPIO4_DEB_ENA_MASK               0x8000  /* GPIO4_DEB_ENA */
+#define WM8400_GPIO4_DEB_ENA_SHIFT                  15  /* GPIO4_DEB_ENA */
+#define WM8400_GPIO4_DEB_ENA_WIDTH                   1  /* GPIO4_DEB_ENA */
+#define WM8400_GPIO4_IRQ_ENA                    0x4000  /* GPIO4_IRQ_ENA */
+#define WM8400_GPIO4_IRQ_ENA_MASK               0x4000  /* GPIO4_IRQ_ENA */
+#define WM8400_GPIO4_IRQ_ENA_SHIFT                  14  /* GPIO4_IRQ_ENA */
+#define WM8400_GPIO4_IRQ_ENA_WIDTH                   1  /* GPIO4_IRQ_ENA */
+#define WM8400_GPIO4_PU                         0x2000  /* GPIO4_PU */
+#define WM8400_GPIO4_PU_MASK                    0x2000  /* GPIO4_PU */
+#define WM8400_GPIO4_PU_SHIFT                       13  /* GPIO4_PU */
+#define WM8400_GPIO4_PU_WIDTH                        1  /* GPIO4_PU */
+#define WM8400_GPIO4_PD                         0x1000  /* GPIO4_PD */
+#define WM8400_GPIO4_PD_MASK                    0x1000  /* GPIO4_PD */
+#define WM8400_GPIO4_PD_SHIFT                       12  /* GPIO4_PD */
+#define WM8400_GPIO4_PD_WIDTH                        1  /* GPIO4_PD */
+#define WM8400_GPIO4_SEL_MASK                   0x0F00  /* GPIO4_SEL - [11:8] */
+#define WM8400_GPIO4_SEL_SHIFT                       8  /* GPIO4_SEL - [11:8] */
+#define WM8400_GPIO4_SEL_WIDTH                       4  /* GPIO4_SEL - [11:8] */
+#define WM8400_GPIO3_DEB_ENA                    0x0080  /* GPIO3_DEB_ENA */
+#define WM8400_GPIO3_DEB_ENA_MASK               0x0080  /* GPIO3_DEB_ENA */
+#define WM8400_GPIO3_DEB_ENA_SHIFT                   7  /* GPIO3_DEB_ENA */
+#define WM8400_GPIO3_DEB_ENA_WIDTH                   1  /* GPIO3_DEB_ENA */
+#define WM8400_GPIO3_IRQ_ENA                    0x0040  /* GPIO3_IRQ_ENA */
+#define WM8400_GPIO3_IRQ_ENA_MASK               0x0040  /* GPIO3_IRQ_ENA */
+#define WM8400_GPIO3_IRQ_ENA_SHIFT                   6  /* GPIO3_IRQ_ENA */
+#define WM8400_GPIO3_IRQ_ENA_WIDTH                   1  /* GPIO3_IRQ_ENA */
+#define WM8400_GPIO3_PU                         0x0020  /* GPIO3_PU */
+#define WM8400_GPIO3_PU_MASK                    0x0020  /* GPIO3_PU */
+#define WM8400_GPIO3_PU_SHIFT                        5  /* GPIO3_PU */
+#define WM8400_GPIO3_PU_WIDTH                        1  /* GPIO3_PU */
+#define WM8400_GPIO3_PD                         0x0010  /* GPIO3_PD */
+#define WM8400_GPIO3_PD_MASK                    0x0010  /* GPIO3_PD */
+#define WM8400_GPIO3_PD_SHIFT                        4  /* GPIO3_PD */
+#define WM8400_GPIO3_PD_WIDTH                        1  /* GPIO3_PD */
+#define WM8400_GPIO3_SEL_MASK                   0x000F  /* GPIO3_SEL - [3:0] */
+#define WM8400_GPIO3_SEL_SHIFT                       0  /* GPIO3_SEL - [3:0] */
+#define WM8400_GPIO3_SEL_WIDTH                       4  /* GPIO3_SEL - [3:0] */
+
+/*
+ * R21 (0x15) - GPIO5 & GPIO6
+ */
+#define WM8400_GPIO6_DEB_ENA                    0x8000  /* GPIO6_DEB_ENA */
+#define WM8400_GPIO6_DEB_ENA_MASK               0x8000  /* GPIO6_DEB_ENA */
+#define WM8400_GPIO6_DEB_ENA_SHIFT                  15  /* GPIO6_DEB_ENA */
+#define WM8400_GPIO6_DEB_ENA_WIDTH                   1  /* GPIO6_DEB_ENA */
+#define WM8400_GPIO6_IRQ_ENA                    0x4000  /* GPIO6_IRQ_ENA */
+#define WM8400_GPIO6_IRQ_ENA_MASK               0x4000  /* GPIO6_IRQ_ENA */
+#define WM8400_GPIO6_IRQ_ENA_SHIFT                  14  /* GPIO6_IRQ_ENA */
+#define WM8400_GPIO6_IRQ_ENA_WIDTH                   1  /* GPIO6_IRQ_ENA */
+#define WM8400_GPIO6_PU                         0x2000  /* GPIO6_PU */
+#define WM8400_GPIO6_PU_MASK                    0x2000  /* GPIO6_PU */
+#define WM8400_GPIO6_PU_SHIFT                       13  /* GPIO6_PU */
+#define WM8400_GPIO6_PU_WIDTH                        1  /* GPIO6_PU */
+#define WM8400_GPIO6_PD                         0x1000  /* GPIO6_PD */
+#define WM8400_GPIO6_PD_MASK                    0x1000  /* GPIO6_PD */
+#define WM8400_GPIO6_PD_SHIFT                       12  /* GPIO6_PD */
+#define WM8400_GPIO6_PD_WIDTH                        1  /* GPIO6_PD */
+#define WM8400_GPIO6_SEL_MASK                   0x0F00  /* GPIO6_SEL - [11:8] */
+#define WM8400_GPIO6_SEL_SHIFT                       8  /* GPIO6_SEL - [11:8] */
+#define WM8400_GPIO6_SEL_WIDTH                       4  /* GPIO6_SEL - [11:8] */
+#define WM8400_GPIO5_DEB_ENA                    0x0080  /* GPIO5_DEB_ENA */
+#define WM8400_GPIO5_DEB_ENA_MASK               0x0080  /* GPIO5_DEB_ENA */
+#define WM8400_GPIO5_DEB_ENA_SHIFT                   7  /* GPIO5_DEB_ENA */
+#define WM8400_GPIO5_DEB_ENA_WIDTH                   1  /* GPIO5_DEB_ENA */
+#define WM8400_GPIO5_IRQ_ENA                    0x0040  /* GPIO5_IRQ_ENA */
+#define WM8400_GPIO5_IRQ_ENA_MASK               0x0040  /* GPIO5_IRQ_ENA */
+#define WM8400_GPIO5_IRQ_ENA_SHIFT                   6  /* GPIO5_IRQ_ENA */
+#define WM8400_GPIO5_IRQ_ENA_WIDTH                   1  /* GPIO5_IRQ_ENA */
+#define WM8400_GPIO5_PU                         0x0020  /* GPIO5_PU */
+#define WM8400_GPIO5_PU_MASK                    0x0020  /* GPIO5_PU */
+#define WM8400_GPIO5_PU_SHIFT                        5  /* GPIO5_PU */
+#define WM8400_GPIO5_PU_WIDTH                        1  /* GPIO5_PU */
+#define WM8400_GPIO5_PD                         0x0010  /* GPIO5_PD */
+#define WM8400_GPIO5_PD_MASK                    0x0010  /* GPIO5_PD */
+#define WM8400_GPIO5_PD_SHIFT                        4  /* GPIO5_PD */
+#define WM8400_GPIO5_PD_WIDTH                        1  /* GPIO5_PD */
+#define WM8400_GPIO5_SEL_MASK                   0x000F  /* GPIO5_SEL - [3:0] */
+#define WM8400_GPIO5_SEL_SHIFT                       0  /* GPIO5_SEL - [3:0] */
+#define WM8400_GPIO5_SEL_WIDTH                       4  /* GPIO5_SEL - [3:0] */
+
+/*
+ * R22 (0x16) - GPIOCTRL 2
+ */
+#define WM8400_TEMPOK_IRQ_ENA                   0x0800  /* TEMPOK_IRQ_ENA */
+#define WM8400_TEMPOK_IRQ_ENA_MASK              0x0800  /* TEMPOK_IRQ_ENA */
+#define WM8400_TEMPOK_IRQ_ENA_SHIFT                 11  /* TEMPOK_IRQ_ENA */
+#define WM8400_TEMPOK_IRQ_ENA_WIDTH                  1  /* TEMPOK_IRQ_ENA */
+#define WM8400_MIC1SHRT_IRQ_ENA                 0x0400  /* MIC1SHRT_IRQ_ENA */
+#define WM8400_MIC1SHRT_IRQ_ENA_MASK            0x0400  /* MIC1SHRT_IRQ_ENA */
+#define WM8400_MIC1SHRT_IRQ_ENA_SHIFT               10  /* MIC1SHRT_IRQ_ENA */
+#define WM8400_MIC1SHRT_IRQ_ENA_WIDTH                1  /* MIC1SHRT_IRQ_ENA */
+#define WM8400_MIC1DET_IRQ_ENA                  0x0200  /* MIC1DET_IRQ_ENA */
+#define WM8400_MIC1DET_IRQ_ENA_MASK             0x0200  /* MIC1DET_IRQ_ENA */
+#define WM8400_MIC1DET_IRQ_ENA_SHIFT                 9  /* MIC1DET_IRQ_ENA */
+#define WM8400_MIC1DET_IRQ_ENA_WIDTH                 1  /* MIC1DET_IRQ_ENA */
+#define WM8400_FLL_LCK_IRQ_ENA                  0x0100  /* FLL_LCK_IRQ_ENA */
+#define WM8400_FLL_LCK_IRQ_ENA_MASK             0x0100  /* FLL_LCK_IRQ_ENA */
+#define WM8400_FLL_LCK_IRQ_ENA_SHIFT                 8  /* FLL_LCK_IRQ_ENA */
+#define WM8400_FLL_LCK_IRQ_ENA_WIDTH                 1  /* FLL_LCK_IRQ_ENA */
+#define WM8400_GPI8_DEB_ENA                     0x0080  /* GPI8_DEB_ENA */
+#define WM8400_GPI8_DEB_ENA_MASK                0x0080  /* GPI8_DEB_ENA */
+#define WM8400_GPI8_DEB_ENA_SHIFT                    7  /* GPI8_DEB_ENA */
+#define WM8400_GPI8_DEB_ENA_WIDTH                    1  /* GPI8_DEB_ENA */
+#define WM8400_GPI8_IRQ_ENA                     0x0040  /* GPI8_IRQ_ENA */
+#define WM8400_GPI8_IRQ_ENA_MASK                0x0040  /* GPI8_IRQ_ENA */
+#define WM8400_GPI8_IRQ_ENA_SHIFT                    6  /* GPI8_IRQ_ENA */
+#define WM8400_GPI8_IRQ_ENA_WIDTH                    1  /* GPI8_IRQ_ENA */
+#define WM8400_GPI8_ENA                         0x0010  /* GPI8_ENA */
+#define WM8400_GPI8_ENA_MASK                    0x0010  /* GPI8_ENA */
+#define WM8400_GPI8_ENA_SHIFT                        4  /* GPI8_ENA */
+#define WM8400_GPI8_ENA_WIDTH                        1  /* GPI8_ENA */
+#define WM8400_GPI7_DEB_ENA                     0x0008  /* GPI7_DEB_ENA */
+#define WM8400_GPI7_DEB_ENA_MASK                0x0008  /* GPI7_DEB_ENA */
+#define WM8400_GPI7_DEB_ENA_SHIFT                    3  /* GPI7_DEB_ENA */
+#define WM8400_GPI7_DEB_ENA_WIDTH                    1  /* GPI7_DEB_ENA */
+#define WM8400_GPI7_IRQ_ENA                     0x0004  /* GPI7_IRQ_ENA */
+#define WM8400_GPI7_IRQ_ENA_MASK                0x0004  /* GPI7_IRQ_ENA */
+#define WM8400_GPI7_IRQ_ENA_SHIFT                    2  /* GPI7_IRQ_ENA */
+#define WM8400_GPI7_IRQ_ENA_WIDTH                    1  /* GPI7_IRQ_ENA */
+#define WM8400_GPI7_ENA                         0x0001  /* GPI7_ENA */
+#define WM8400_GPI7_ENA_MASK                    0x0001  /* GPI7_ENA */
+#define WM8400_GPI7_ENA_SHIFT                        0  /* GPI7_ENA */
+#define WM8400_GPI7_ENA_WIDTH                        1  /* GPI7_ENA */
+
+/*
+ * R23 (0x17) - GPIO_POL
+ */
+#define WM8400_IRQ_INV                          0x1000  /* IRQ_INV */
+#define WM8400_IRQ_INV_MASK                     0x1000  /* IRQ_INV */
+#define WM8400_IRQ_INV_SHIFT                        12  /* IRQ_INV */
+#define WM8400_IRQ_INV_WIDTH                         1  /* IRQ_INV */
+#define WM8400_TEMPOK_POL                       0x0800  /* TEMPOK_POL */
+#define WM8400_TEMPOK_POL_MASK                  0x0800  /* TEMPOK_POL */
+#define WM8400_TEMPOK_POL_SHIFT                     11  /* TEMPOK_POL */
+#define WM8400_TEMPOK_POL_WIDTH                      1  /* TEMPOK_POL */
+#define WM8400_MIC1SHRT_POL                     0x0400  /* MIC1SHRT_POL */
+#define WM8400_MIC1SHRT_POL_MASK                0x0400  /* MIC1SHRT_POL */
+#define WM8400_MIC1SHRT_POL_SHIFT                   10  /* MIC1SHRT_POL */
+#define WM8400_MIC1SHRT_POL_WIDTH                    1  /* MIC1SHRT_POL */
+#define WM8400_MIC1DET_POL                      0x0200  /* MIC1DET_POL */
+#define WM8400_MIC1DET_POL_MASK                 0x0200  /* MIC1DET_POL */
+#define WM8400_MIC1DET_POL_SHIFT                     9  /* MIC1DET_POL */
+#define WM8400_MIC1DET_POL_WIDTH                     1  /* MIC1DET_POL */
+#define WM8400_FLL_LCK_POL                      0x0100  /* FLL_LCK_POL */
+#define WM8400_FLL_LCK_POL_MASK                 0x0100  /* FLL_LCK_POL */
+#define WM8400_FLL_LCK_POL_SHIFT                     8  /* FLL_LCK_POL */
+#define WM8400_FLL_LCK_POL_WIDTH                     1  /* FLL_LCK_POL */
+#define WM8400_GPIO_POL_MASK                    0x00FF  /* GPIO_POL - [7:0] */
+#define WM8400_GPIO_POL_SHIFT                        0  /* GPIO_POL - [7:0] */
+#define WM8400_GPIO_POL_WIDTH                        8  /* GPIO_POL - [7:0] */
+
+/*
+ * R65 (0x41) - LDO 1 Control
+ */
+#define WM8400_LDO1_ENA                         0x8000  /* LDO1_ENA */
+#define WM8400_LDO1_ENA_MASK                    0x8000  /* LDO1_ENA */
+#define WM8400_LDO1_ENA_SHIFT                       15  /* LDO1_ENA */
+#define WM8400_LDO1_ENA_WIDTH                        1  /* LDO1_ENA */
+#define WM8400_LDO1_SWI                         0x4000  /* LDO1_SWI */
+#define WM8400_LDO1_SWI_MASK                    0x4000  /* LDO1_SWI */
+#define WM8400_LDO1_SWI_SHIFT                       14  /* LDO1_SWI */
+#define WM8400_LDO1_SWI_WIDTH                        1  /* LDO1_SWI */
+#define WM8400_LDO1_OPFLT                       0x1000  /* LDO1_OPFLT */
+#define WM8400_LDO1_OPFLT_MASK                  0x1000  /* LDO1_OPFLT */
+#define WM8400_LDO1_OPFLT_SHIFT                     12  /* LDO1_OPFLT */
+#define WM8400_LDO1_OPFLT_WIDTH                      1  /* LDO1_OPFLT */
+#define WM8400_LDO1_ERRACT                      0x0800  /* LDO1_ERRACT */
+#define WM8400_LDO1_ERRACT_MASK                 0x0800  /* LDO1_ERRACT */
+#define WM8400_LDO1_ERRACT_SHIFT                    11  /* LDO1_ERRACT */
+#define WM8400_LDO1_ERRACT_WIDTH                     1  /* LDO1_ERRACT */
+#define WM8400_LDO1_HIB_MODE                    0x0400  /* LDO1_HIB_MODE */
+#define WM8400_LDO1_HIB_MODE_MASK               0x0400  /* LDO1_HIB_MODE */
+#define WM8400_LDO1_HIB_MODE_SHIFT                  10  /* LDO1_HIB_MODE */
+#define WM8400_LDO1_HIB_MODE_WIDTH                   1  /* LDO1_HIB_MODE */
+#define WM8400_LDO1_VIMG_MASK                   0x03E0  /* LDO1_VIMG - [9:5] */
+#define WM8400_LDO1_VIMG_SHIFT                       5  /* LDO1_VIMG - [9:5] */
+#define WM8400_LDO1_VIMG_WIDTH                       5  /* LDO1_VIMG - [9:5] */
+#define WM8400_LDO1_VSEL_MASK                   0x001F  /* LDO1_VSEL - [4:0] */
+#define WM8400_LDO1_VSEL_SHIFT                       0  /* LDO1_VSEL - [4:0] */
+#define WM8400_LDO1_VSEL_WIDTH                       5  /* LDO1_VSEL - [4:0] */
+
+/*
+ * R66 (0x42) - LDO 2 Control
+ */
+#define WM8400_LDO2_ENA                         0x8000  /* LDO2_ENA */
+#define WM8400_LDO2_ENA_MASK                    0x8000  /* LDO2_ENA */
+#define WM8400_LDO2_ENA_SHIFT                       15  /* LDO2_ENA */
+#define WM8400_LDO2_ENA_WIDTH                        1  /* LDO2_ENA */
+#define WM8400_LDO2_SWI                         0x4000  /* LDO2_SWI */
+#define WM8400_LDO2_SWI_MASK                    0x4000  /* LDO2_SWI */
+#define WM8400_LDO2_SWI_SHIFT                       14  /* LDO2_SWI */
+#define WM8400_LDO2_SWI_WIDTH                        1  /* LDO2_SWI */
+#define WM8400_LDO2_OPFLT                       0x1000  /* LDO2_OPFLT */
+#define WM8400_LDO2_OPFLT_MASK                  0x1000  /* LDO2_OPFLT */
+#define WM8400_LDO2_OPFLT_SHIFT                     12  /* LDO2_OPFLT */
+#define WM8400_LDO2_OPFLT_WIDTH                      1  /* LDO2_OPFLT */
+#define WM8400_LDO2_ERRACT                      0x0800  /* LDO2_ERRACT */
+#define WM8400_LDO2_ERRACT_MASK                 0x0800  /* LDO2_ERRACT */
+#define WM8400_LDO2_ERRACT_SHIFT                    11  /* LDO2_ERRACT */
+#define WM8400_LDO2_ERRACT_WIDTH                     1  /* LDO2_ERRACT */
+#define WM8400_LDO2_HIB_MODE                    0x0400  /* LDO2_HIB_MODE */
+#define WM8400_LDO2_HIB_MODE_MASK               0x0400  /* LDO2_HIB_MODE */
+#define WM8400_LDO2_HIB_MODE_SHIFT                  10  /* LDO2_HIB_MODE */
+#define WM8400_LDO2_HIB_MODE_WIDTH                   1  /* LDO2_HIB_MODE */
+#define WM8400_LDO2_VIMG_MASK                   0x03E0  /* LDO2_VIMG - [9:5] */
+#define WM8400_LDO2_VIMG_SHIFT                       5  /* LDO2_VIMG - [9:5] */
+#define WM8400_LDO2_VIMG_WIDTH                       5  /* LDO2_VIMG - [9:5] */
+#define WM8400_LDO2_VSEL_MASK                   0x001F  /* LDO2_VSEL - [4:0] */
+#define WM8400_LDO2_VSEL_SHIFT                       0  /* LDO2_VSEL - [4:0] */
+#define WM8400_LDO2_VSEL_WIDTH                       5  /* LDO2_VSEL - [4:0] */
+
+/*
+ * R67 (0x43) - LDO 3 Control
+ */
+#define WM8400_LDO3_ENA                         0x8000  /* LDO3_ENA */
+#define WM8400_LDO3_ENA_MASK                    0x8000  /* LDO3_ENA */
+#define WM8400_LDO3_ENA_SHIFT                       15  /* LDO3_ENA */
+#define WM8400_LDO3_ENA_WIDTH                        1  /* LDO3_ENA */
+#define WM8400_LDO3_SWI                         0x4000  /* LDO3_SWI */
+#define WM8400_LDO3_SWI_MASK                    0x4000  /* LDO3_SWI */
+#define WM8400_LDO3_SWI_SHIFT                       14  /* LDO3_SWI */
+#define WM8400_LDO3_SWI_WIDTH                        1  /* LDO3_SWI */
+#define WM8400_LDO3_OPFLT                       0x1000  /* LDO3_OPFLT */
+#define WM8400_LDO3_OPFLT_MASK                  0x1000  /* LDO3_OPFLT */
+#define WM8400_LDO3_OPFLT_SHIFT                     12  /* LDO3_OPFLT */
+#define WM8400_LDO3_OPFLT_WIDTH                      1  /* LDO3_OPFLT */
+#define WM8400_LDO3_ERRACT                      0x0800  /* LDO3_ERRACT */
+#define WM8400_LDO3_ERRACT_MASK                 0x0800  /* LDO3_ERRACT */
+#define WM8400_LDO3_ERRACT_SHIFT                    11  /* LDO3_ERRACT */
+#define WM8400_LDO3_ERRACT_WIDTH                     1  /* LDO3_ERRACT */
+#define WM8400_LDO3_HIB_MODE                    0x0400  /* LDO3_HIB_MODE */
+#define WM8400_LDO3_HIB_MODE_MASK               0x0400  /* LDO3_HIB_MODE */
+#define WM8400_LDO3_HIB_MODE_SHIFT                  10  /* LDO3_HIB_MODE */
+#define WM8400_LDO3_HIB_MODE_WIDTH                   1  /* LDO3_HIB_MODE */
+#define WM8400_LDO3_VIMG_MASK                   0x03E0  /* LDO3_VIMG - [9:5] */
+#define WM8400_LDO3_VIMG_SHIFT                       5  /* LDO3_VIMG - [9:5] */
+#define WM8400_LDO3_VIMG_WIDTH                       5  /* LDO3_VIMG - [9:5] */
+#define WM8400_LDO3_VSEL_MASK                   0x001F  /* LDO3_VSEL - [4:0] */
+#define WM8400_LDO3_VSEL_SHIFT                       0  /* LDO3_VSEL - [4:0] */
+#define WM8400_LDO3_VSEL_WIDTH                       5  /* LDO3_VSEL - [4:0] */
+
+/*
+ * R68 (0x44) - LDO 4 Control
+ */
+#define WM8400_LDO4_ENA                         0x8000  /* LDO4_ENA */
+#define WM8400_LDO4_ENA_MASK                    0x8000  /* LDO4_ENA */
+#define WM8400_LDO4_ENA_SHIFT                       15  /* LDO4_ENA */
+#define WM8400_LDO4_ENA_WIDTH                        1  /* LDO4_ENA */
+#define WM8400_LDO4_SWI                         0x4000  /* LDO4_SWI */
+#define WM8400_LDO4_SWI_MASK                    0x4000  /* LDO4_SWI */
+#define WM8400_LDO4_SWI_SHIFT                       14  /* LDO4_SWI */
+#define WM8400_LDO4_SWI_WIDTH                        1  /* LDO4_SWI */
+#define WM8400_LDO4_OPFLT                       0x1000  /* LDO4_OPFLT */
+#define WM8400_LDO4_OPFLT_MASK                  0x1000  /* LDO4_OPFLT */
+#define WM8400_LDO4_OPFLT_SHIFT                     12  /* LDO4_OPFLT */
+#define WM8400_LDO4_OPFLT_WIDTH                      1  /* LDO4_OPFLT */
+#define WM8400_LDO4_ERRACT                      0x0800  /* LDO4_ERRACT */
+#define WM8400_LDO4_ERRACT_MASK                 0x0800  /* LDO4_ERRACT */
+#define WM8400_LDO4_ERRACT_SHIFT                    11  /* LDO4_ERRACT */
+#define WM8400_LDO4_ERRACT_WIDTH                     1  /* LDO4_ERRACT */
+#define WM8400_LDO4_HIB_MODE                    0x0400  /* LDO4_HIB_MODE */
+#define WM8400_LDO4_HIB_MODE_MASK               0x0400  /* LDO4_HIB_MODE */
+#define WM8400_LDO4_HIB_MODE_SHIFT                  10  /* LDO4_HIB_MODE */
+#define WM8400_LDO4_HIB_MODE_WIDTH                   1  /* LDO4_HIB_MODE */
+#define WM8400_LDO4_VIMG_MASK                   0x03E0  /* LDO4_VIMG - [9:5] */
+#define WM8400_LDO4_VIMG_SHIFT                       5  /* LDO4_VIMG - [9:5] */
+#define WM8400_LDO4_VIMG_WIDTH                       5  /* LDO4_VIMG - [9:5] */
+#define WM8400_LDO4_VSEL_MASK                   0x001F  /* LDO4_VSEL - [4:0] */
+#define WM8400_LDO4_VSEL_SHIFT                       0  /* LDO4_VSEL - [4:0] */
+#define WM8400_LDO4_VSEL_WIDTH                       5  /* LDO4_VSEL - [4:0] */
+
+/*
+ * R70 (0x46) - DCDC1 Control 1
+ */
+#define WM8400_DC1_ENA                          0x8000  /* DC1_ENA */
+#define WM8400_DC1_ENA_MASK                     0x8000  /* DC1_ENA */
+#define WM8400_DC1_ENA_SHIFT                        15  /* DC1_ENA */
+#define WM8400_DC1_ENA_WIDTH                         1  /* DC1_ENA */
+#define WM8400_DC1_ACTIVE                       0x4000  /* DC1_ACTIVE */
+#define WM8400_DC1_ACTIVE_MASK                  0x4000  /* DC1_ACTIVE */
+#define WM8400_DC1_ACTIVE_SHIFT                     14  /* DC1_ACTIVE */
+#define WM8400_DC1_ACTIVE_WIDTH                      1  /* DC1_ACTIVE */
+#define WM8400_DC1_SLEEP                        0x2000  /* DC1_SLEEP */
+#define WM8400_DC1_SLEEP_MASK                   0x2000  /* DC1_SLEEP */
+#define WM8400_DC1_SLEEP_SHIFT                      13  /* DC1_SLEEP */
+#define WM8400_DC1_SLEEP_WIDTH                       1  /* DC1_SLEEP */
+#define WM8400_DC1_OPFLT                        0x1000  /* DC1_OPFLT */
+#define WM8400_DC1_OPFLT_MASK                   0x1000  /* DC1_OPFLT */
+#define WM8400_DC1_OPFLT_SHIFT                      12  /* DC1_OPFLT */
+#define WM8400_DC1_OPFLT_WIDTH                       1  /* DC1_OPFLT */
+#define WM8400_DC1_ERRACT                       0x0800  /* DC1_ERRACT */
+#define WM8400_DC1_ERRACT_MASK                  0x0800  /* DC1_ERRACT */
+#define WM8400_DC1_ERRACT_SHIFT                     11  /* DC1_ERRACT */
+#define WM8400_DC1_ERRACT_WIDTH                      1  /* DC1_ERRACT */
+#define WM8400_DC1_HIB_MODE                     0x0400  /* DC1_HIB_MODE */
+#define WM8400_DC1_HIB_MODE_MASK                0x0400  /* DC1_HIB_MODE */
+#define WM8400_DC1_HIB_MODE_SHIFT                   10  /* DC1_HIB_MODE */
+#define WM8400_DC1_HIB_MODE_WIDTH                    1  /* DC1_HIB_MODE */
+#define WM8400_DC1_SOFTST_MASK                  0x0300  /* DC1_SOFTST - [9:8] */
+#define WM8400_DC1_SOFTST_SHIFT                      8  /* DC1_SOFTST - [9:8] */
+#define WM8400_DC1_SOFTST_WIDTH                      2  /* DC1_SOFTST - [9:8] */
+#define WM8400_DC1_OV_PROT                      0x0080  /* DC1_OV_PROT */
+#define WM8400_DC1_OV_PROT_MASK                 0x0080  /* DC1_OV_PROT */
+#define WM8400_DC1_OV_PROT_SHIFT                     7  /* DC1_OV_PROT */
+#define WM8400_DC1_OV_PROT_WIDTH                     1  /* DC1_OV_PROT */
+#define WM8400_DC1_VSEL_MASK                    0x007F  /* DC1_VSEL - [6:0] */
+#define WM8400_DC1_VSEL_SHIFT                        0  /* DC1_VSEL - [6:0] */
+#define WM8400_DC1_VSEL_WIDTH                        7  /* DC1_VSEL - [6:0] */
+
+/*
+ * R71 (0x47) - DCDC1 Control 2
+ */
+#define WM8400_DC1_FRC_PWM                      0x2000  /* DC1_FRC_PWM */
+#define WM8400_DC1_FRC_PWM_MASK                 0x2000  /* DC1_FRC_PWM */
+#define WM8400_DC1_FRC_PWM_SHIFT                    13  /* DC1_FRC_PWM */
+#define WM8400_DC1_FRC_PWM_WIDTH                     1  /* DC1_FRC_PWM */
+#define WM8400_DC1_STBY_LIM_MASK                0x0300  /* DC1_STBY_LIM - [9:8] */
+#define WM8400_DC1_STBY_LIM_SHIFT                    8  /* DC1_STBY_LIM - [9:8] */
+#define WM8400_DC1_STBY_LIM_WIDTH                    2  /* DC1_STBY_LIM - [9:8] */
+#define WM8400_DC1_ACT_LIM                      0x0080  /* DC1_ACT_LIM */
+#define WM8400_DC1_ACT_LIM_MASK                 0x0080  /* DC1_ACT_LIM */
+#define WM8400_DC1_ACT_LIM_SHIFT                     7  /* DC1_ACT_LIM */
+#define WM8400_DC1_ACT_LIM_WIDTH                     1  /* DC1_ACT_LIM */
+#define WM8400_DC1_VIMG_MASK                    0x007F  /* DC1_VIMG - [6:0] */
+#define WM8400_DC1_VIMG_SHIFT                        0  /* DC1_VIMG - [6:0] */
+#define WM8400_DC1_VIMG_WIDTH                        7  /* DC1_VIMG - [6:0] */
+
+/*
+ * R72 (0x48) - DCDC2 Control 1
+ */
+#define WM8400_DC2_ENA                          0x8000  /* DC2_ENA */
+#define WM8400_DC2_ENA_MASK                     0x8000  /* DC2_ENA */
+#define WM8400_DC2_ENA_SHIFT                        15  /* DC2_ENA */
+#define WM8400_DC2_ENA_WIDTH                         1  /* DC2_ENA */
+#define WM8400_DC2_ACTIVE                       0x4000  /* DC2_ACTIVE */
+#define WM8400_DC2_ACTIVE_MASK                  0x4000  /* DC2_ACTIVE */
+#define WM8400_DC2_ACTIVE_SHIFT                     14  /* DC2_ACTIVE */
+#define WM8400_DC2_ACTIVE_WIDTH                      1  /* DC2_ACTIVE */
+#define WM8400_DC2_SLEEP                        0x2000  /* DC2_SLEEP */
+#define WM8400_DC2_SLEEP_MASK                   0x2000  /* DC2_SLEEP */
+#define WM8400_DC2_SLEEP_SHIFT                      13  /* DC2_SLEEP */
+#define WM8400_DC2_SLEEP_WIDTH                       1  /* DC2_SLEEP */
+#define WM8400_DC2_OPFLT                        0x1000  /* DC2_OPFLT */
+#define WM8400_DC2_OPFLT_MASK                   0x1000  /* DC2_OPFLT */
+#define WM8400_DC2_OPFLT_SHIFT                      12  /* DC2_OPFLT */
+#define WM8400_DC2_OPFLT_WIDTH                       1  /* DC2_OPFLT */
+#define WM8400_DC2_ERRACT                       0x0800  /* DC2_ERRACT */
+#define WM8400_DC2_ERRACT_MASK                  0x0800  /* DC2_ERRACT */
+#define WM8400_DC2_ERRACT_SHIFT                     11  /* DC2_ERRACT */
+#define WM8400_DC2_ERRACT_WIDTH                      1  /* DC2_ERRACT */
+#define WM8400_DC2_HIB_MODE                     0x0400  /* DC2_HIB_MODE */
+#define WM8400_DC2_HIB_MODE_MASK                0x0400  /* DC2_HIB_MODE */
+#define WM8400_DC2_HIB_MODE_SHIFT                   10  /* DC2_HIB_MODE */
+#define WM8400_DC2_HIB_MODE_WIDTH                    1  /* DC2_HIB_MODE */
+#define WM8400_DC2_SOFTST_MASK                  0x0300  /* DC2_SOFTST - [9:8] */
+#define WM8400_DC2_SOFTST_SHIFT                      8  /* DC2_SOFTST - [9:8] */
+#define WM8400_DC2_SOFTST_WIDTH                      2  /* DC2_SOFTST - [9:8] */
+#define WM8400_DC2_OV_PROT                      0x0080  /* DC2_OV_PROT */
+#define WM8400_DC2_OV_PROT_MASK                 0x0080  /* DC2_OV_PROT */
+#define WM8400_DC2_OV_PROT_SHIFT                     7  /* DC2_OV_PROT */
+#define WM8400_DC2_OV_PROT_WIDTH                     1  /* DC2_OV_PROT */
+#define WM8400_DC2_VSEL_MASK                    0x007F  /* DC2_VSEL - [6:0] */
+#define WM8400_DC2_VSEL_SHIFT                        0  /* DC2_VSEL - [6:0] */
+#define WM8400_DC2_VSEL_WIDTH                        7  /* DC2_VSEL - [6:0] */
+
+/*
+ * R73 (0x49) - DCDC2 Control 2
+ */
+#define WM8400_DC2_FRC_PWM                      0x2000  /* DC2_FRC_PWM */
+#define WM8400_DC2_FRC_PWM_MASK                 0x2000  /* DC2_FRC_PWM */
+#define WM8400_DC2_FRC_PWM_SHIFT                    13  /* DC2_FRC_PWM */
+#define WM8400_DC2_FRC_PWM_WIDTH                     1  /* DC2_FRC_PWM */
+#define WM8400_DC2_STBY_LIM_MASK                0x0300  /* DC2_STBY_LIM - [9:8] */
+#define WM8400_DC2_STBY_LIM_SHIFT                    8  /* DC2_STBY_LIM - [9:8] */
+#define WM8400_DC2_STBY_LIM_WIDTH                    2  /* DC2_STBY_LIM - [9:8] */
+#define WM8400_DC2_ACT_LIM                      0x0080  /* DC2_ACT_LIM */
+#define WM8400_DC2_ACT_LIM_MASK                 0x0080  /* DC2_ACT_LIM */
+#define WM8400_DC2_ACT_LIM_SHIFT                     7  /* DC2_ACT_LIM */
+#define WM8400_DC2_ACT_LIM_WIDTH                     1  /* DC2_ACT_LIM */
+#define WM8400_DC2_VIMG_MASK                    0x007F  /* DC2_VIMG - [6:0] */
+#define WM8400_DC2_VIMG_SHIFT                        0  /* DC2_VIMG - [6:0] */
+#define WM8400_DC2_VIMG_WIDTH                        7  /* DC2_VIMG - [6:0] */
+
+/*
+ * R75 (0x4B) - Interface
+ */
+#define WM8400_AUTOINC                          0x0008  /* AUTOINC */
+#define WM8400_AUTOINC_MASK                     0x0008  /* AUTOINC */
+#define WM8400_AUTOINC_SHIFT                         3  /* AUTOINC */
+#define WM8400_AUTOINC_WIDTH                         1  /* AUTOINC */
+#define WM8400_ARA_ENA                          0x0004  /* ARA_ENA */
+#define WM8400_ARA_ENA_MASK                     0x0004  /* ARA_ENA */
+#define WM8400_ARA_ENA_SHIFT                         2  /* ARA_ENA */
+#define WM8400_ARA_ENA_WIDTH                         1  /* ARA_ENA */
+#define WM8400_SPI_CFG                          0x0002  /* SPI_CFG */
+#define WM8400_SPI_CFG_MASK                     0x0002  /* SPI_CFG */
+#define WM8400_SPI_CFG_SHIFT                         1  /* SPI_CFG */
+#define WM8400_SPI_CFG_WIDTH                         1  /* SPI_CFG */
+
+/*
+ * R76 (0x4C) - PM GENERAL
+ */
+#define WM8400_CODEC_SOFTST                     0x8000  /* CODEC_SOFTST */
+#define WM8400_CODEC_SOFTST_MASK                0x8000  /* CODEC_SOFTST */
+#define WM8400_CODEC_SOFTST_SHIFT                   15  /* CODEC_SOFTST */
+#define WM8400_CODEC_SOFTST_WIDTH                    1  /* CODEC_SOFTST */
+#define WM8400_CODEC_SOFTSD                     0x4000  /* CODEC_SOFTSD */
+#define WM8400_CODEC_SOFTSD_MASK                0x4000  /* CODEC_SOFTSD */
+#define WM8400_CODEC_SOFTSD_SHIFT                   14  /* CODEC_SOFTSD */
+#define WM8400_CODEC_SOFTSD_WIDTH                    1  /* CODEC_SOFTSD */
+#define WM8400_CHIP_SOFTSD                      0x2000  /* CHIP_SOFTSD */
+#define WM8400_CHIP_SOFTSD_MASK                 0x2000  /* CHIP_SOFTSD */
+#define WM8400_CHIP_SOFTSD_SHIFT                    13  /* CHIP_SOFTSD */
+#define WM8400_CHIP_SOFTSD_WIDTH                     1  /* CHIP_SOFTSD */
+#define WM8400_DSLEEP1_POL                      0x0008  /* DSLEEP1_POL */
+#define WM8400_DSLEEP1_POL_MASK                 0x0008  /* DSLEEP1_POL */
+#define WM8400_DSLEEP1_POL_SHIFT                     3  /* DSLEEP1_POL */
+#define WM8400_DSLEEP1_POL_WIDTH                     1  /* DSLEEP1_POL */
+#define WM8400_DSLEEP2_POL                      0x0004  /* DSLEEP2_POL */
+#define WM8400_DSLEEP2_POL_MASK                 0x0004  /* DSLEEP2_POL */
+#define WM8400_DSLEEP2_POL_SHIFT                     2  /* DSLEEP2_POL */
+#define WM8400_DSLEEP2_POL_WIDTH                     1  /* DSLEEP2_POL */
+#define WM8400_PWR_STATE_MASK                   0x0003  /* PWR_STATE - [1:0] */
+#define WM8400_PWR_STATE_SHIFT                       0  /* PWR_STATE - [1:0] */
+#define WM8400_PWR_STATE_WIDTH                       2  /* PWR_STATE - [1:0] */
+
+/*
+ * R78 (0x4E) - PM Shutdown Control
+ */
+#define WM8400_CHIP_GT150_ERRACT                0x0200  /* CHIP_GT150_ERRACT */
+#define WM8400_CHIP_GT150_ERRACT_MASK           0x0200  /* CHIP_GT150_ERRACT */
+#define WM8400_CHIP_GT150_ERRACT_SHIFT               9  /* CHIP_GT150_ERRACT */
+#define WM8400_CHIP_GT150_ERRACT_WIDTH               1  /* CHIP_GT150_ERRACT */
+#define WM8400_CHIP_GT115_ERRACT                0x0100  /* CHIP_GT115_ERRACT */
+#define WM8400_CHIP_GT115_ERRACT_MASK           0x0100  /* CHIP_GT115_ERRACT */
+#define WM8400_CHIP_GT115_ERRACT_SHIFT               8  /* CHIP_GT115_ERRACT */
+#define WM8400_CHIP_GT115_ERRACT_WIDTH               1  /* CHIP_GT115_ERRACT */
+#define WM8400_LINE_CMP_ERRACT                  0x0080  /* LINE_CMP_ERRACT */
+#define WM8400_LINE_CMP_ERRACT_MASK             0x0080  /* LINE_CMP_ERRACT */
+#define WM8400_LINE_CMP_ERRACT_SHIFT                 7  /* LINE_CMP_ERRACT */
+#define WM8400_LINE_CMP_ERRACT_WIDTH                 1  /* LINE_CMP_ERRACT */
+#define WM8400_UVLO_ERRACT                      0x0040  /* UVLO_ERRACT */
+#define WM8400_UVLO_ERRACT_MASK                 0x0040  /* UVLO_ERRACT */
+#define WM8400_UVLO_ERRACT_SHIFT                     6  /* UVLO_ERRACT */
+#define WM8400_UVLO_ERRACT_WIDTH                     1  /* UVLO_ERRACT */
+
+/*
+ * R79 (0x4F) - Interrupt Status 1
+ */
+#define WM8400_MICD_CINT                        0x8000  /* MICD_CINT */
+#define WM8400_MICD_CINT_MASK                   0x8000  /* MICD_CINT */
+#define WM8400_MICD_CINT_SHIFT                      15  /* MICD_CINT */
+#define WM8400_MICD_CINT_WIDTH                       1  /* MICD_CINT */
+#define WM8400_MICSCD_CINT                      0x4000  /* MICSCD_CINT */
+#define WM8400_MICSCD_CINT_MASK                 0x4000  /* MICSCD_CINT */
+#define WM8400_MICSCD_CINT_SHIFT                    14  /* MICSCD_CINT */
+#define WM8400_MICSCD_CINT_WIDTH                     1  /* MICSCD_CINT */
+#define WM8400_JDL_CINT                         0x2000  /* JDL_CINT */
+#define WM8400_JDL_CINT_MASK                    0x2000  /* JDL_CINT */
+#define WM8400_JDL_CINT_SHIFT                       13  /* JDL_CINT */
+#define WM8400_JDL_CINT_WIDTH                        1  /* JDL_CINT */
+#define WM8400_JDR_CINT                         0x1000  /* JDR_CINT */
+#define WM8400_JDR_CINT_MASK                    0x1000  /* JDR_CINT */
+#define WM8400_JDR_CINT_SHIFT                       12  /* JDR_CINT */
+#define WM8400_JDR_CINT_WIDTH                        1  /* JDR_CINT */
+#define WM8400_CODEC_SEQ_END_EINT               0x0800  /* CODEC_SEQ_END_EINT */
+#define WM8400_CODEC_SEQ_END_EINT_MASK          0x0800  /* CODEC_SEQ_END_EINT */
+#define WM8400_CODEC_SEQ_END_EINT_SHIFT             11  /* CODEC_SEQ_END_EINT */
+#define WM8400_CODEC_SEQ_END_EINT_WIDTH              1  /* CODEC_SEQ_END_EINT */
+#define WM8400_CDEL_TO_EINT                     0x0400  /* CDEL_TO_EINT */
+#define WM8400_CDEL_TO_EINT_MASK                0x0400  /* CDEL_TO_EINT */
+#define WM8400_CDEL_TO_EINT_SHIFT                   10  /* CDEL_TO_EINT */
+#define WM8400_CDEL_TO_EINT_WIDTH                    1  /* CDEL_TO_EINT */
+#define WM8400_CHIP_GT150_EINT                  0x0200  /* CHIP_GT150_EINT */
+#define WM8400_CHIP_GT150_EINT_MASK             0x0200  /* CHIP_GT150_EINT */
+#define WM8400_CHIP_GT150_EINT_SHIFT                 9  /* CHIP_GT150_EINT */
+#define WM8400_CHIP_GT150_EINT_WIDTH                 1  /* CHIP_GT150_EINT */
+#define WM8400_CHIP_GT115_EINT                  0x0100  /* CHIP_GT115_EINT */
+#define WM8400_CHIP_GT115_EINT_MASK             0x0100  /* CHIP_GT115_EINT */
+#define WM8400_CHIP_GT115_EINT_SHIFT                 8  /* CHIP_GT115_EINT */
+#define WM8400_CHIP_GT115_EINT_WIDTH                 1  /* CHIP_GT115_EINT */
+#define WM8400_LINE_CMP_EINT                    0x0080  /* LINE_CMP_EINT */
+#define WM8400_LINE_CMP_EINT_MASK               0x0080  /* LINE_CMP_EINT */
+#define WM8400_LINE_CMP_EINT_SHIFT                   7  /* LINE_CMP_EINT */
+#define WM8400_LINE_CMP_EINT_WIDTH                   1  /* LINE_CMP_EINT */
+#define WM8400_UVLO_EINT                        0x0040  /* UVLO_EINT */
+#define WM8400_UVLO_EINT_MASK                   0x0040  /* UVLO_EINT */
+#define WM8400_UVLO_EINT_SHIFT                       6  /* UVLO_EINT */
+#define WM8400_UVLO_EINT_WIDTH                       1  /* UVLO_EINT */
+#define WM8400_DC2_UV_EINT                      0x0020  /* DC2_UV_EINT */
+#define WM8400_DC2_UV_EINT_MASK                 0x0020  /* DC2_UV_EINT */
+#define WM8400_DC2_UV_EINT_SHIFT                     5  /* DC2_UV_EINT */
+#define WM8400_DC2_UV_EINT_WIDTH                     1  /* DC2_UV_EINT */
+#define WM8400_DC1_UV_EINT                      0x0010  /* DC1_UV_EINT */
+#define WM8400_DC1_UV_EINT_MASK                 0x0010  /* DC1_UV_EINT */
+#define WM8400_DC1_UV_EINT_SHIFT                     4  /* DC1_UV_EINT */
+#define WM8400_DC1_UV_EINT_WIDTH                     1  /* DC1_UV_EINT */
+#define WM8400_LDO4_UV_EINT                     0x0008  /* LDO4_UV_EINT */
+#define WM8400_LDO4_UV_EINT_MASK                0x0008  /* LDO4_UV_EINT */
+#define WM8400_LDO4_UV_EINT_SHIFT                    3  /* LDO4_UV_EINT */
+#define WM8400_LDO4_UV_EINT_WIDTH                    1  /* LDO4_UV_EINT */
+#define WM8400_LDO3_UV_EINT                     0x0004  /* LDO3_UV_EINT */
+#define WM8400_LDO3_UV_EINT_MASK                0x0004  /* LDO3_UV_EINT */
+#define WM8400_LDO3_UV_EINT_SHIFT                    2  /* LDO3_UV_EINT */
+#define WM8400_LDO3_UV_EINT_WIDTH                    1  /* LDO3_UV_EINT */
+#define WM8400_LDO2_UV_EINT                     0x0002  /* LDO2_UV_EINT */
+#define WM8400_LDO2_UV_EINT_MASK                0x0002  /* LDO2_UV_EINT */
+#define WM8400_LDO2_UV_EINT_SHIFT                    1  /* LDO2_UV_EINT */
+#define WM8400_LDO2_UV_EINT_WIDTH                    1  /* LDO2_UV_EINT */
+#define WM8400_LDO1_UV_EINT                     0x0001  /* LDO1_UV_EINT */
+#define WM8400_LDO1_UV_EINT_MASK                0x0001  /* LDO1_UV_EINT */
+#define WM8400_LDO1_UV_EINT_SHIFT                    0  /* LDO1_UV_EINT */
+#define WM8400_LDO1_UV_EINT_WIDTH                    1  /* LDO1_UV_EINT */
+
+/*
+ * R80 (0x50) - Interrupt Status 1 Mask
+ */
+#define WM8400_IM_MICD_CINT                     0x8000  /* IM_MICD_CINT */
+#define WM8400_IM_MICD_CINT_MASK                0x8000  /* IM_MICD_CINT */
+#define WM8400_IM_MICD_CINT_SHIFT                   15  /* IM_MICD_CINT */
+#define WM8400_IM_MICD_CINT_WIDTH                    1  /* IM_MICD_CINT */
+#define WM8400_IM_MICSCD_CINT                   0x4000  /* IM_MICSCD_CINT */
+#define WM8400_IM_MICSCD_CINT_MASK              0x4000  /* IM_MICSCD_CINT */
+#define WM8400_IM_MICSCD_CINT_SHIFT                 14  /* IM_MICSCD_CINT */
+#define WM8400_IM_MICSCD_CINT_WIDTH                  1  /* IM_MICSCD_CINT */
+#define WM8400_IM_JDL_CINT                      0x2000  /* IM_JDL_CINT */
+#define WM8400_IM_JDL_CINT_MASK                 0x2000  /* IM_JDL_CINT */
+#define WM8400_IM_JDL_CINT_SHIFT                    13  /* IM_JDL_CINT */
+#define WM8400_IM_JDL_CINT_WIDTH                     1  /* IM_JDL_CINT */
+#define WM8400_IM_JDR_CINT                      0x1000  /* IM_JDR_CINT */
+#define WM8400_IM_JDR_CINT_MASK                 0x1000  /* IM_JDR_CINT */
+#define WM8400_IM_JDR_CINT_SHIFT                    12  /* IM_JDR_CINT */
+#define WM8400_IM_JDR_CINT_WIDTH                     1  /* IM_JDR_CINT */
+#define WM8400_IM_CODEC_SEQ_END_EINT            0x0800  /* IM_CODEC_SEQ_END_EINT */
+#define WM8400_IM_CODEC_SEQ_END_EINT_MASK       0x0800  /* IM_CODEC_SEQ_END_EINT */
+#define WM8400_IM_CODEC_SEQ_END_EINT_SHIFT          11  /* IM_CODEC_SEQ_END_EINT */
+#define WM8400_IM_CODEC_SEQ_END_EINT_WIDTH           1  /* IM_CODEC_SEQ_END_EINT */
+#define WM8400_IM_CDEL_TO_EINT                  0x0400  /* IM_CDEL_TO_EINT */
+#define WM8400_IM_CDEL_TO_EINT_MASK             0x0400  /* IM_CDEL_TO_EINT */
+#define WM8400_IM_CDEL_TO_EINT_SHIFT                10  /* IM_CDEL_TO_EINT */
+#define WM8400_IM_CDEL_TO_EINT_WIDTH                 1  /* IM_CDEL_TO_EINT */
+#define WM8400_IM_CHIP_GT150_EINT               0x0200  /* IM_CHIP_GT150_EINT */
+#define WM8400_IM_CHIP_GT150_EINT_MASK          0x0200  /* IM_CHIP_GT150_EINT */
+#define WM8400_IM_CHIP_GT150_EINT_SHIFT              9  /* IM_CHIP_GT150_EINT */
+#define WM8400_IM_CHIP_GT150_EINT_WIDTH              1  /* IM_CHIP_GT150_EINT */
+#define WM8400_IM_CHIP_GT115_EINT               0x0100  /* IM_CHIP_GT115_EINT */
+#define WM8400_IM_CHIP_GT115_EINT_MASK          0x0100  /* IM_CHIP_GT115_EINT */
+#define WM8400_IM_CHIP_GT115_EINT_SHIFT              8  /* IM_CHIP_GT115_EINT */
+#define WM8400_IM_CHIP_GT115_EINT_WIDTH              1  /* IM_CHIP_GT115_EINT */
+#define WM8400_IM_LINE_CMP_EINT                 0x0080  /* IM_LINE_CMP_EINT */
+#define WM8400_IM_LINE_CMP_EINT_MASK            0x0080  /* IM_LINE_CMP_EINT */
+#define WM8400_IM_LINE_CMP_EINT_SHIFT                7  /* IM_LINE_CMP_EINT */
+#define WM8400_IM_LINE_CMP_EINT_WIDTH                1  /* IM_LINE_CMP_EINT */
+#define WM8400_IM_UVLO_EINT                     0x0040  /* IM_UVLO_EINT */
+#define WM8400_IM_UVLO_EINT_MASK                0x0040  /* IM_UVLO_EINT */
+#define WM8400_IM_UVLO_EINT_SHIFT                    6  /* IM_UVLO_EINT */
+#define WM8400_IM_UVLO_EINT_WIDTH                    1  /* IM_UVLO_EINT */
+#define WM8400_IM_DC2_UV_EINT                   0x0020  /* IM_DC2_UV_EINT */
+#define WM8400_IM_DC2_UV_EINT_MASK              0x0020  /* IM_DC2_UV_EINT */
+#define WM8400_IM_DC2_UV_EINT_SHIFT                  5  /* IM_DC2_UV_EINT */
+#define WM8400_IM_DC2_UV_EINT_WIDTH                  1  /* IM_DC2_UV_EINT */
+#define WM8400_IM_DC1_UV_EINT                   0x0010  /* IM_DC1_UV_EINT */
+#define WM8400_IM_DC1_UV_EINT_MASK              0x0010  /* IM_DC1_UV_EINT */
+#define WM8400_IM_DC1_UV_EINT_SHIFT                  4  /* IM_DC1_UV_EINT */
+#define WM8400_IM_DC1_UV_EINT_WIDTH                  1  /* IM_DC1_UV_EINT */
+#define WM8400_IM_LDO4_UV_EINT                  0x0008  /* IM_LDO4_UV_EINT */
+#define WM8400_IM_LDO4_UV_EINT_MASK             0x0008  /* IM_LDO4_UV_EINT */
+#define WM8400_IM_LDO4_UV_EINT_SHIFT                 3  /* IM_LDO4_UV_EINT */
+#define WM8400_IM_LDO4_UV_EINT_WIDTH                 1  /* IM_LDO4_UV_EINT */
+#define WM8400_IM_LDO3_UV_EINT                  0x0004  /* IM_LDO3_UV_EINT */
+#define WM8400_IM_LDO3_UV_EINT_MASK             0x0004  /* IM_LDO3_UV_EINT */
+#define WM8400_IM_LDO3_UV_EINT_SHIFT                 2  /* IM_LDO3_UV_EINT */
+#define WM8400_IM_LDO3_UV_EINT_WIDTH                 1  /* IM_LDO3_UV_EINT */
+#define WM8400_IM_LDO2_UV_EINT                  0x0002  /* IM_LDO2_UV_EINT */
+#define WM8400_IM_LDO2_UV_EINT_MASK             0x0002  /* IM_LDO2_UV_EINT */
+#define WM8400_IM_LDO2_UV_EINT_SHIFT                 1  /* IM_LDO2_UV_EINT */
+#define WM8400_IM_LDO2_UV_EINT_WIDTH                 1  /* IM_LDO2_UV_EINT */
+#define WM8400_IM_LDO1_UV_EINT                  0x0001  /* IM_LDO1_UV_EINT */
+#define WM8400_IM_LDO1_UV_EINT_MASK             0x0001  /* IM_LDO1_UV_EINT */
+#define WM8400_IM_LDO1_UV_EINT_SHIFT                 0  /* IM_LDO1_UV_EINT */
+#define WM8400_IM_LDO1_UV_EINT_WIDTH                 1  /* IM_LDO1_UV_EINT */
+
+/*
+ * R81 (0x51) - Interrupt Levels
+ */
+#define WM8400_MICD_LVL                         0x8000  /* MICD_LVL */
+#define WM8400_MICD_LVL_MASK                    0x8000  /* MICD_LVL */
+#define WM8400_MICD_LVL_SHIFT                       15  /* MICD_LVL */
+#define WM8400_MICD_LVL_WIDTH                        1  /* MICD_LVL */
+#define WM8400_MICSCD_LVL                       0x4000  /* MICSCD_LVL */
+#define WM8400_MICSCD_LVL_MASK                  0x4000  /* MICSCD_LVL */
+#define WM8400_MICSCD_LVL_SHIFT                     14  /* MICSCD_LVL */
+#define WM8400_MICSCD_LVL_WIDTH                      1  /* MICSCD_LVL */
+#define WM8400_JDL_LVL                          0x2000  /* JDL_LVL */
+#define WM8400_JDL_LVL_MASK                     0x2000  /* JDL_LVL */
+#define WM8400_JDL_LVL_SHIFT                        13  /* JDL_LVL */
+#define WM8400_JDL_LVL_WIDTH                         1  /* JDL_LVL */
+#define WM8400_JDR_LVL                          0x1000  /* JDR_LVL */
+#define WM8400_JDR_LVL_MASK                     0x1000  /* JDR_LVL */
+#define WM8400_JDR_LVL_SHIFT                        12  /* JDR_LVL */
+#define WM8400_JDR_LVL_WIDTH                         1  /* JDR_LVL */
+#define WM8400_CODEC_SEQ_END_LVL                0x0800  /* CODEC_SEQ_END_LVL */
+#define WM8400_CODEC_SEQ_END_LVL_MASK           0x0800  /* CODEC_SEQ_END_LVL */
+#define WM8400_CODEC_SEQ_END_LVL_SHIFT              11  /* CODEC_SEQ_END_LVL */
+#define WM8400_CODEC_SEQ_END_LVL_WIDTH               1  /* CODEC_SEQ_END_LVL */
+#define WM8400_CDEL_TO_LVL                      0x0400  /* CDEL_TO_LVL */
+#define WM8400_CDEL_TO_LVL_MASK                 0x0400  /* CDEL_TO_LVL */
+#define WM8400_CDEL_TO_LVL_SHIFT                    10  /* CDEL_TO_LVL */
+#define WM8400_CDEL_TO_LVL_WIDTH                     1  /* CDEL_TO_LVL */
+#define WM8400_CHIP_GT150_LVL                   0x0200  /* CHIP_GT150_LVL */
+#define WM8400_CHIP_GT150_LVL_MASK              0x0200  /* CHIP_GT150_LVL */
+#define WM8400_CHIP_GT150_LVL_SHIFT                  9  /* CHIP_GT150_LVL */
+#define WM8400_CHIP_GT150_LVL_WIDTH                  1  /* CHIP_GT150_LVL */
+#define WM8400_CHIP_GT115_LVL                   0x0100  /* CHIP_GT115_LVL */
+#define WM8400_CHIP_GT115_LVL_MASK              0x0100  /* CHIP_GT115_LVL */
+#define WM8400_CHIP_GT115_LVL_SHIFT                  8  /* CHIP_GT115_LVL */
+#define WM8400_CHIP_GT115_LVL_WIDTH                  1  /* CHIP_GT115_LVL */
+#define WM8400_LINE_CMP_LVL                     0x0080  /* LINE_CMP_LVL */
+#define WM8400_LINE_CMP_LVL_MASK                0x0080  /* LINE_CMP_LVL */
+#define WM8400_LINE_CMP_LVL_SHIFT                    7  /* LINE_CMP_LVL */
+#define WM8400_LINE_CMP_LVL_WIDTH                    1  /* LINE_CMP_LVL */
+#define WM8400_UVLO_LVL                         0x0040  /* UVLO_LVL */
+#define WM8400_UVLO_LVL_MASK                    0x0040  /* UVLO_LVL */
+#define WM8400_UVLO_LVL_SHIFT                        6  /* UVLO_LVL */
+#define WM8400_UVLO_LVL_WIDTH                        1  /* UVLO_LVL */
+#define WM8400_DC2_UV_LVL                       0x0020  /* DC2_UV_LVL */
+#define WM8400_DC2_UV_LVL_MASK                  0x0020  /* DC2_UV_LVL */
+#define WM8400_DC2_UV_LVL_SHIFT                      5  /* DC2_UV_LVL */
+#define WM8400_DC2_UV_LVL_WIDTH                      1  /* DC2_UV_LVL */
+#define WM8400_DC1_UV_LVL                       0x0010  /* DC1_UV_LVL */
+#define WM8400_DC1_UV_LVL_MASK                  0x0010  /* DC1_UV_LVL */
+#define WM8400_DC1_UV_LVL_SHIFT                      4  /* DC1_UV_LVL */
+#define WM8400_DC1_UV_LVL_WIDTH                      1  /* DC1_UV_LVL */
+#define WM8400_LDO4_UV_LVL                      0x0008  /* LDO4_UV_LVL */
+#define WM8400_LDO4_UV_LVL_MASK                 0x0008  /* LDO4_UV_LVL */
+#define WM8400_LDO4_UV_LVL_SHIFT                     3  /* LDO4_UV_LVL */
+#define WM8400_LDO4_UV_LVL_WIDTH                     1  /* LDO4_UV_LVL */
+#define WM8400_LDO3_UV_LVL                      0x0004  /* LDO3_UV_LVL */
+#define WM8400_LDO3_UV_LVL_MASK                 0x0004  /* LDO3_UV_LVL */
+#define WM8400_LDO3_UV_LVL_SHIFT                     2  /* LDO3_UV_LVL */
+#define WM8400_LDO3_UV_LVL_WIDTH                     1  /* LDO3_UV_LVL */
+#define WM8400_LDO2_UV_LVL                      0x0002  /* LDO2_UV_LVL */
+#define WM8400_LDO2_UV_LVL_MASK                 0x0002  /* LDO2_UV_LVL */
+#define WM8400_LDO2_UV_LVL_SHIFT                     1  /* LDO2_UV_LVL */
+#define WM8400_LDO2_UV_LVL_WIDTH                     1  /* LDO2_UV_LVL */
+#define WM8400_LDO1_UV_LVL                      0x0001  /* LDO1_UV_LVL */
+#define WM8400_LDO1_UV_LVL_MASK                 0x0001  /* LDO1_UV_LVL */
+#define WM8400_LDO1_UV_LVL_SHIFT                     0  /* LDO1_UV_LVL */
+#define WM8400_LDO1_UV_LVL_WIDTH                     1  /* LDO1_UV_LVL */
+
+/*
+ * R82 (0x52) - Shutdown Reason
+ */
+#define WM8400_SDR_CHIP_SOFTSD                  0x2000  /* SDR_CHIP_SOFTSD */
+#define WM8400_SDR_CHIP_SOFTSD_MASK             0x2000  /* SDR_CHIP_SOFTSD */
+#define WM8400_SDR_CHIP_SOFTSD_SHIFT                13  /* SDR_CHIP_SOFTSD */
+#define WM8400_SDR_CHIP_SOFTSD_WIDTH                 1  /* SDR_CHIP_SOFTSD */
+#define WM8400_SDR_NPDN                         0x0800  /* SDR_NPDN */
+#define WM8400_SDR_NPDN_MASK                    0x0800  /* SDR_NPDN */
+#define WM8400_SDR_NPDN_SHIFT                       11  /* SDR_NPDN */
+#define WM8400_SDR_NPDN_WIDTH                        1  /* SDR_NPDN */
+#define WM8400_SDR_CHIP_GT150                   0x0200  /* SDR_CHIP_GT150 */
+#define WM8400_SDR_CHIP_GT150_MASK              0x0200  /* SDR_CHIP_GT150 */
+#define WM8400_SDR_CHIP_GT150_SHIFT                  9  /* SDR_CHIP_GT150 */
+#define WM8400_SDR_CHIP_GT150_WIDTH                  1  /* SDR_CHIP_GT150 */
+#define WM8400_SDR_CHIP_GT115                   0x0100  /* SDR_CHIP_GT115 */
+#define WM8400_SDR_CHIP_GT115_MASK              0x0100  /* SDR_CHIP_GT115 */
+#define WM8400_SDR_CHIP_GT115_SHIFT                  8  /* SDR_CHIP_GT115 */
+#define WM8400_SDR_CHIP_GT115_WIDTH                  1  /* SDR_CHIP_GT115 */
+#define WM8400_SDR_LINE_CMP                     0x0080  /* SDR_LINE_CMP */
+#define WM8400_SDR_LINE_CMP_MASK                0x0080  /* SDR_LINE_CMP */
+#define WM8400_SDR_LINE_CMP_SHIFT                    7  /* SDR_LINE_CMP */
+#define WM8400_SDR_LINE_CMP_WIDTH                    1  /* SDR_LINE_CMP */
+#define WM8400_SDR_UVLO                         0x0040  /* SDR_UVLO */
+#define WM8400_SDR_UVLO_MASK                    0x0040  /* SDR_UVLO */
+#define WM8400_SDR_UVLO_SHIFT                        6  /* SDR_UVLO */
+#define WM8400_SDR_UVLO_WIDTH                        1  /* SDR_UVLO */
+#define WM8400_SDR_DC2_UV                       0x0020  /* SDR_DC2_UV */
+#define WM8400_SDR_DC2_UV_MASK                  0x0020  /* SDR_DC2_UV */
+#define WM8400_SDR_DC2_UV_SHIFT                      5  /* SDR_DC2_UV */
+#define WM8400_SDR_DC2_UV_WIDTH                      1  /* SDR_DC2_UV */
+#define WM8400_SDR_DC1_UV                       0x0010  /* SDR_DC1_UV */
+#define WM8400_SDR_DC1_UV_MASK                  0x0010  /* SDR_DC1_UV */
+#define WM8400_SDR_DC1_UV_SHIFT                      4  /* SDR_DC1_UV */
+#define WM8400_SDR_DC1_UV_WIDTH                      1  /* SDR_DC1_UV */
+#define WM8400_SDR_LDO4_UV                      0x0008  /* SDR_LDO4_UV */
+#define WM8400_SDR_LDO4_UV_MASK                 0x0008  /* SDR_LDO4_UV */
+#define WM8400_SDR_LDO4_UV_SHIFT                     3  /* SDR_LDO4_UV */
+#define WM8400_SDR_LDO4_UV_WIDTH                     1  /* SDR_LDO4_UV */
+#define WM8400_SDR_LDO3_UV                      0x0004  /* SDR_LDO3_UV */
+#define WM8400_SDR_LDO3_UV_MASK                 0x0004  /* SDR_LDO3_UV */
+#define WM8400_SDR_LDO3_UV_SHIFT                     2  /* SDR_LDO3_UV */
+#define WM8400_SDR_LDO3_UV_WIDTH                     1  /* SDR_LDO3_UV */
+#define WM8400_SDR_LDO2_UV                      0x0002  /* SDR_LDO2_UV */
+#define WM8400_SDR_LDO2_UV_MASK                 0x0002  /* SDR_LDO2_UV */
+#define WM8400_SDR_LDO2_UV_SHIFT                     1  /* SDR_LDO2_UV */
+#define WM8400_SDR_LDO2_UV_WIDTH                     1  /* SDR_LDO2_UV */
+#define WM8400_SDR_LDO1_UV                      0x0001  /* SDR_LDO1_UV */
+#define WM8400_SDR_LDO1_UV_MASK                 0x0001  /* SDR_LDO1_UV */
+#define WM8400_SDR_LDO1_UV_SHIFT                     0  /* SDR_LDO1_UV */
+#define WM8400_SDR_LDO1_UV_WIDTH                     1  /* SDR_LDO1_UV */
+
+/*
+ * R84 (0x54) - Line Circuits
+ */
+#define WM8400_BG_LINE_COMP                     0x8000  /* BG_LINE_COMP */
+#define WM8400_BG_LINE_COMP_MASK                0x8000  /* BG_LINE_COMP */
+#define WM8400_BG_LINE_COMP_SHIFT                   15  /* BG_LINE_COMP */
+#define WM8400_BG_LINE_COMP_WIDTH                    1  /* BG_LINE_COMP */
+#define WM8400_LINE_CMP_VTHI_MASK               0x00F0  /* LINE_CMP_VTHI - [7:4] */
+#define WM8400_LINE_CMP_VTHI_SHIFT                   4  /* LINE_CMP_VTHI - [7:4] */
+#define WM8400_LINE_CMP_VTHI_WIDTH                   4  /* LINE_CMP_VTHI - [7:4] */
+#define WM8400_LINE_CMP_VTHD_MASK               0x000F  /* LINE_CMP_VTHD - [3:0] */
+#define WM8400_LINE_CMP_VTHD_SHIFT                   0  /* LINE_CMP_VTHD - [3:0] */
+#define WM8400_LINE_CMP_VTHD_WIDTH                   4  /* LINE_CMP_VTHD - [3:0] */
+
+u16 wm8400_reg_read(struct wm8400 *wm8400, u8 reg);
+int wm8400_block_read(struct wm8400 *wm8400, u8 reg, int count, u16 *data);
+int wm8400_set_bits(struct wm8400 *wm8400, u8 reg, u16 mask, u16 val);
+
+#endif
diff --git a/include/linux/mfd/wm8400.h b/include/linux/mfd/wm8400.h
new file mode 100644
index 0000000..b46b566
--- /dev/null
+++ b/include/linux/mfd/wm8400.h
@@ -0,0 +1,40 @@
+/*
+ * wm8400 client interface
+ *
+ * Copyright 2008 Wolfson Microelectronics plc
+ *
+ * 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.
+ */
+
+#ifndef __LINUX_MFD_WM8400_H
+#define __LINUX_MFD_WM8400_H
+
+#include <linux/regulator/machine.h>
+
+#define WM8400_LDO1  0
+#define WM8400_LDO2  1
+#define WM8400_LDO3  2
+#define WM8400_LDO4  3
+#define WM8400_DCDC1 4
+#define WM8400_DCDC2 5
+
+struct wm8400_platform_data {
+	int (*platform_init)(struct device *dev);
+};
+
+int wm8400_register_regulator(struct device *dev, int reg,
+			      struct regulator_init_data *initdata);
+
+#endif
diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
index 3481a7d..d6a3f47 100644
--- a/include/linux/mod_devicetable.h
+++ b/include/linux/mod_devicetable.h
@@ -131,6 +131,16 @@
 #define USB_DEVICE_ID_MATCH_INT_SUBCLASS	0x0100
 #define USB_DEVICE_ID_MATCH_INT_PROTOCOL	0x0200
 
+#define HID_ANY_ID				(~0)
+
+struct hid_device_id {
+	__u16 bus;
+	__u32 vendor;
+	__u32 product;
+	kernel_ulong_t driver_data
+		__attribute__((aligned(sizeof(kernel_ulong_t))));
+};
+
 /* s390 CCW devices */
 struct ccw_device_id {
 	__u16	match_flags;	/* which fields to match against */
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index d3ea3de..6487585 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -11,7 +11,7 @@
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
  *		Corey Minyard <wf-rch!minyard@relay.EU.net>
  *		Donald J. Becker, <becker@cesdis.gsfc.nasa.gov>
- *		Alan Cox, <Alan.Cox@linux.org>
+ *		Alan Cox, <alan@lxorguk.ukuu.org.uk>
  *		Bjorn Ekwall. <bj0rn@blox.se>
  *              Pekka Riikonen <priikone@poseidon.pspt.fi>
  *
diff --git a/include/linux/nfsd/nfsd.h b/include/linux/nfsd/nfsd.h
index 108f47e..2126940 100644
--- a/include/linux/nfsd/nfsd.h
+++ b/include/linux/nfsd/nfsd.h
@@ -38,6 +38,7 @@
 #define NFSD_MAY_LOCK		32
 #define NFSD_MAY_OWNER_OVERRIDE	64
 #define NFSD_MAY_LOCAL_ACCESS	128 /* IRIX doing local access check on device special file*/
+#define NFSD_MAY_BYPASS_GSS_ON_ROOT 256
 
 #define NFSD_MAY_CREATE		(NFSD_MAY_EXEC|NFSD_MAY_WRITE)
 #define NFSD_MAY_REMOVE		(NFSD_MAY_EXEC|NFSD_MAY_WRITE|NFSD_MAY_TRUNC)
@@ -125,7 +126,7 @@
 __be32		nfsd_readdir(struct svc_rqst *, struct svc_fh *,
 			     loff_t *, struct readdir_cd *, filldir_t);
 __be32		nfsd_statfs(struct svc_rqst *, struct svc_fh *,
-				struct kstatfs *);
+				struct kstatfs *, int access);
 
 int		nfsd_notify_change(struct inode *, struct iattr *);
 __be32		nfsd_permission(struct svc_rqst *, struct svc_export *,
diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h
index 1d712c7..e37d805 100644
--- a/include/linux/regulator/driver.h
+++ b/include/linux/regulator/driver.h
@@ -18,8 +18,8 @@
 #include <linux/device.h>
 #include <linux/regulator/consumer.h>
 
-struct regulator_constraints;
 struct regulator_dev;
+struct regulator_init_data;
 
 /**
  * struct regulator_ops - regulator operations.
@@ -51,7 +51,7 @@
 					  int output_uV, int load_uA);
 
 	/* the operations below are for configuration of regulator state when
-	 * it's parent PMIC enters a global STANBY/HIBERNATE state */
+	 * its parent PMIC enters a global STANDBY/HIBERNATE state */
 
 	/* set regulator suspend voltage */
 	int (*set_suspend_voltage) (struct regulator_dev *, int uV);
@@ -85,15 +85,17 @@
 	struct module *owner;
 };
 
-
 struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc,
-					  void *reg_data);
+	struct device *dev, void *driver_data);
 void regulator_unregister(struct regulator_dev *rdev);
 
 int regulator_notifier_call_chain(struct regulator_dev *rdev,
 				  unsigned long event, void *data);
 
 void *rdev_get_drvdata(struct regulator_dev *rdev);
+struct device *rdev_get_dev(struct regulator_dev *rdev);
 int rdev_get_id(struct regulator_dev *rdev);
 
+void *regulator_get_init_drvdata(struct regulator_init_data *reg_init_data);
+
 #endif
diff --git a/include/linux/regulator/machine.h b/include/linux/regulator/machine.h
index 11e737d..c6d6933 100644
--- a/include/linux/regulator/machine.h
+++ b/include/linux/regulator/machine.h
@@ -89,15 +89,33 @@
 	unsigned apply_uV:1;	/* apply uV constraint iff min == max */
 };
 
-int regulator_set_supply(const char *regulator, const char *regulator_supply);
+/**
+ * struct regulator_consumer_supply - supply -> device mapping
+ *
+ * This maps a supply name to a device.
+ */
+struct regulator_consumer_supply {
+	struct device *dev;	/* consumer */
+	const char *supply;	/* consumer supply - e.g. "vcc" */
+};
 
-const char *regulator_get_supply(const char *regulator);
+/**
+ * struct regulator_init_data - regulator platform initialisation data.
+ *
+ * Initialisation constraints, our supply and consumers supplies.
+ */
+struct regulator_init_data {
+	struct device *supply_regulator_dev; /* or NULL for LINE */
 
-int regulator_set_machine_constraints(const char *regulator,
-	struct regulation_constraints *constraints);
+	struct regulation_constraints constraints;
 
-int regulator_set_device_supply(const char *regulator, struct device *dev,
-				const char *supply);
+	int num_consumer_supplies;
+	struct regulator_consumer_supply *consumer_supplies;
+
+	/* optional regulator machine specific init */
+	int (*regulator_init)(void *driver_data);
+	void *driver_data;	/* core does not touch this */
+};
 
 int regulator_suspend_prepare(suspend_state_t state);
 
diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h
index e5bfe01..6f0ee1b 100644
--- a/include/linux/sunrpc/clnt.h
+++ b/include/linux/sunrpc/clnt.h
@@ -104,6 +104,7 @@
 	const struct rpc_timeout *timeout;
 	char			*servername;
 	struct rpc_program	*program;
+	u32			prognumber;	/* overrides program->number */
 	u32			version;
 	rpc_authflavor_t	authflavor;
 	unsigned long		flags;
@@ -124,10 +125,10 @@
 void		rpc_shutdown_client(struct rpc_clnt *);
 void		rpc_release_client(struct rpc_clnt *);
 
-int		rpcb_register(u32, u32, int, unsigned short, int *);
+int		rpcb_register(u32, u32, int, unsigned short);
 int		rpcb_v4_register(const u32 program, const u32 version,
 				 const struct sockaddr *address,
-				 const char *netid, int *result);
+				 const char *netid);
 int		rpcb_getport_sync(struct sockaddr_in *, u32, u32, int);
 void		rpcb_getport_async(struct rpc_task *);
 
diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h
index dc69068..3afe7fb 100644
--- a/include/linux/sunrpc/svc.h
+++ b/include/linux/sunrpc/svc.h
@@ -66,6 +66,7 @@
 	struct list_head	sv_tempsocks;	/* all temporary sockets */
 	int			sv_tmpcnt;	/* count of temporary sockets */
 	struct timer_list	sv_temptimer;	/* timer for aging temporary sockets */
+	sa_family_t		sv_family;	/* listener's address family */
 
 	char *			sv_name;	/* service name */
 
@@ -265,17 +266,17 @@
 /*
  * Rigorous type checking on sockaddr type conversions
  */
-static inline struct sockaddr_in *svc_addr_in(struct svc_rqst *rqst)
+static inline struct sockaddr_in *svc_addr_in(const struct svc_rqst *rqst)
 {
 	return (struct sockaddr_in *) &rqst->rq_addr;
 }
 
-static inline struct sockaddr_in6 *svc_addr_in6(struct svc_rqst *rqst)
+static inline struct sockaddr_in6 *svc_addr_in6(const struct svc_rqst *rqst)
 {
 	return (struct sockaddr_in6 *) &rqst->rq_addr;
 }
 
-static inline struct sockaddr *svc_addr(struct svc_rqst *rqst)
+static inline struct sockaddr *svc_addr(const struct svc_rqst *rqst)
 {
 	return (struct sockaddr *) &rqst->rq_addr;
 }
@@ -381,18 +382,20 @@
 /*
  * Function prototypes.
  */
-struct svc_serv *  svc_create(struct svc_program *, unsigned int,
-			      void (*shutdown)(struct svc_serv*));
+struct svc_serv *svc_create(struct svc_program *, unsigned int, sa_family_t,
+			    void (*shutdown)(struct svc_serv *));
 struct svc_rqst *svc_prepare_thread(struct svc_serv *serv,
 					struct svc_pool *pool);
 void		   svc_exit_thread(struct svc_rqst *);
 struct svc_serv *  svc_create_pooled(struct svc_program *, unsigned int,
-			void (*shutdown)(struct svc_serv*), svc_thread_fn,
-			struct module *);
+			sa_family_t, void (*shutdown)(struct svc_serv *),
+			svc_thread_fn, struct module *);
 int		   svc_set_num_threads(struct svc_serv *, struct svc_pool *, int);
 void		   svc_destroy(struct svc_serv *);
 int		   svc_process(struct svc_rqst *);
-int		   svc_register(struct svc_serv *, int, unsigned short);
+int		   svc_register(const struct svc_serv *, const unsigned short,
+				const unsigned short);
+
 void		   svc_wake_up(struct svc_serv *);
 void		   svc_reserve(struct svc_rqst *rqstp, int space);
 struct svc_pool *  svc_pool_for_cpu(struct svc_serv *serv, int cpu);
diff --git a/include/linux/sunrpc/svc_rdma.h b/include/linux/sunrpc/svc_rdma.h
index dc05b54..c14fe86 100644
--- a/include/linux/sunrpc/svc_rdma.h
+++ b/include/linux/sunrpc/svc_rdma.h
@@ -72,6 +72,7 @@
  */
 struct svc_rdma_op_ctxt {
 	struct svc_rdma_op_ctxt *read_hdr;
+	struct svc_rdma_fastreg_mr *frmr;
 	int hdr_count;
 	struct xdr_buf arg;
 	struct list_head dto_q;
@@ -103,16 +104,30 @@
 	int start;		/* sge no for this chunk */
 	int count;		/* sge count for this chunk */
 };
+struct svc_rdma_fastreg_mr {
+	struct ib_mr *mr;
+	void *kva;
+	struct ib_fast_reg_page_list *page_list;
+	int page_list_len;
+	unsigned long access_flags;
+	unsigned long map_len;
+	enum dma_data_direction direction;
+	struct list_head frmr_list;
+};
 struct svc_rdma_req_map {
+	struct svc_rdma_fastreg_mr *frmr;
 	unsigned long count;
 	union {
 		struct kvec sge[RPCSVC_MAXPAGES];
 		struct svc_rdma_chunk_sge ch[RPCSVC_MAXPAGES];
 	};
 };
-
+#define RDMACTXT_F_FAST_UNREG	1
 #define RDMACTXT_F_LAST_CTXT	2
 
+#define	SVCRDMA_DEVCAP_FAST_REG		1	/* fast mr registration */
+#define	SVCRDMA_DEVCAP_READ_W_INV	2	/* read w/ invalidate */
+
 struct svcxprt_rdma {
 	struct svc_xprt      sc_xprt;		/* SVC transport structure */
 	struct rdma_cm_id    *sc_cm_id;		/* RDMA connection id */
@@ -136,6 +151,11 @@
 	struct ib_cq         *sc_rq_cq;
 	struct ib_cq         *sc_sq_cq;
 	struct ib_mr         *sc_phys_mr;	/* MR for server memory */
+	u32		     sc_dev_caps;	/* distilled device caps */
+	u32		     sc_dma_lkey;	/* local dma key */
+	unsigned int	     sc_frmr_pg_list_len;
+	struct list_head     sc_frmr_q;
+	spinlock_t	     sc_frmr_q_lock;
 
 	spinlock_t	     sc_lock;		/* transport lock */
 
@@ -192,8 +212,13 @@
 extern int svc_rdma_create_listen(struct svc_serv *, int, struct sockaddr *);
 extern struct svc_rdma_op_ctxt *svc_rdma_get_context(struct svcxprt_rdma *);
 extern void svc_rdma_put_context(struct svc_rdma_op_ctxt *, int);
+extern void svc_rdma_unmap_dma(struct svc_rdma_op_ctxt *ctxt);
 extern struct svc_rdma_req_map *svc_rdma_get_req_map(void);
 extern void svc_rdma_put_req_map(struct svc_rdma_req_map *);
+extern int svc_rdma_fastreg(struct svcxprt_rdma *, struct svc_rdma_fastreg_mr *);
+extern struct svc_rdma_fastreg_mr *svc_rdma_get_frmr(struct svcxprt_rdma *);
+extern void svc_rdma_put_frmr(struct svcxprt_rdma *,
+			      struct svc_rdma_fastreg_mr *);
 extern void svc_sq_reap(struct svcxprt_rdma *);
 extern void svc_rq_reap(struct svcxprt_rdma *);
 extern struct svc_xprt_class svc_rdma_class;
diff --git a/include/linux/sunrpc/svcsock.h b/include/linux/sunrpc/svcsock.h
index 8cff696..483e103 100644
--- a/include/linux/sunrpc/svcsock.h
+++ b/include/linux/sunrpc/svcsock.h
@@ -39,10 +39,7 @@
 void		svc_drop(struct svc_rqst *);
 void		svc_sock_update_bufs(struct svc_serv *serv);
 int		svc_sock_names(char *buf, struct svc_serv *serv, char *toclose);
-int		svc_addsock(struct svc_serv *serv,
-			    int fd,
-			    char *name_return,
-			    int *proto);
+int		svc_addsock(struct svc_serv *serv, int fd, char *name_return);
 void		svc_init_xprt_sock(void);
 void		svc_cleanup_xprt_sock(void);
 
diff --git a/init/do_mounts_md.c b/init/do_mounts_md.c
index 693d246..48b3fad 100644
--- a/init/do_mounts_md.c
+++ b/init/do_mounts_md.c
@@ -12,7 +12,12 @@
  * The code for that is here.
  */
 
-static int __initdata raid_noautodetect, raid_autopart;
+#ifdef CONFIG_MD_AUTODETECT
+static int __initdata raid_noautodetect;
+#else
+static int __initdata raid_noautodetect=1;
+#endif
+static int __initdata raid_autopart;
 
 static struct {
 	int minor;
@@ -252,6 +257,8 @@
 
 		if (!strncmp(str, "noautodetect", wlen))
 			raid_noautodetect = 1;
+		if (!strncmp(str, "autodetect", wlen))
+			raid_noautodetect = 0;
 		if (strncmp(str, "partitionable", wlen)==0)
 			raid_autopart = 1;
 		if (strncmp(str, "part", wlen)==0)
@@ -264,17 +271,32 @@
 __setup("raid=", raid_setup);
 __setup("md=", md_setup);
 
+static void autodetect_raid(void)
+{
+	int fd;
+
+	/*
+	 * Since we don't want to detect and use half a raid array, we need to
+	 * wait for the known devices to complete their probing
+	 */
+	printk(KERN_INFO "md: Waiting for all devices to be available before autodetect\n");
+	printk(KERN_INFO "md: If you don't use raid, use raid=noautodetect\n");
+	while (driver_probe_done() < 0)
+		msleep(100);
+	fd = sys_open("/dev/md0", 0, 0);
+	if (fd >= 0) {
+		sys_ioctl(fd, RAID_AUTORUN, raid_autopart);
+		sys_close(fd);
+	}
+}
+
 void __init md_run_setup(void)
 {
 	create_dev("/dev/md0", MKDEV(MD_MAJOR, 0));
+
 	if (raid_noautodetect)
-		printk(KERN_INFO "md: Skipping autodetection of RAID arrays. (raid=noautodetect)\n");
-	else {
-		int fd = sys_open("/dev/md0", 0, 0);
-		if (fd >= 0) {
-			sys_ioctl(fd, RAID_AUTORUN, raid_autopart);
-			sys_close(fd);
-		}
-	}
+		printk(KERN_INFO "md: Skipping autodetection of RAID arrays. (raid=autodetect will force)\n");
+	else
+		autodetect_raid();
 	md_setup_drive();
 }
diff --git a/init/main.c b/init/main.c
index 3820323..27f6bf6 100644
--- a/init/main.c
+++ b/init/main.c
@@ -708,7 +708,7 @@
 	int result;
 
 	if (initcall_debug) {
-		printk("calling  %pF\n", fn);
+		printk("calling  %pF @ %i\n", fn, task_pid_nr(current));
 		t0 = ktime_get();
 	}
 
diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c
index 08d6e1b..503d8d4 100644
--- a/kernel/sys_ni.c
+++ b/kernel/sys_ni.c
@@ -125,6 +125,7 @@
 cond_syscall(sys_vm86);
 cond_syscall(compat_sys_ipc);
 cond_syscall(compat_sys_sysctl);
+cond_syscall(sys_flock);
 
 /* arch-specific weak syscall entries */
 cond_syscall(sys_pciconfig_read);
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index c468c3c..cfc5295 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -96,7 +96,7 @@
 static int neg_one = -1;
 #endif
 
-#ifdef CONFIG_MMU
+#if defined(CONFIG_MMU) && defined(CONFIG_FILE_LOCKING)
 static int two = 2;
 #endif
 
@@ -1248,6 +1248,7 @@
 		.extra1		= &minolduid,
 		.extra2		= &maxolduid,
 	},
+#ifdef CONFIG_FILE_LOCKING
 	{
 		.ctl_name	= FS_LEASES,
 		.procname	= "leases-enable",
@@ -1256,6 +1257,7 @@
 		.mode		= 0644,
 		.proc_handler	= &proc_dointvec,
 	},
+#endif
 #ifdef CONFIG_DNOTIFY
 	{
 		.ctl_name	= FS_DIR_NOTIFY,
@@ -1267,6 +1269,7 @@
 	},
 #endif
 #ifdef CONFIG_MMU
+#ifdef CONFIG_FILE_LOCKING
 	{
 		.ctl_name	= FS_LEASE_TIME,
 		.procname	= "lease-break-time",
@@ -1278,6 +1281,7 @@
 		.extra1		= &zero,
 		.extra2		= &two,
 	},
+#endif
 	{
 		.procname	= "aio-nr",
 		.data		= &aio_nr,
diff --git a/net/802/psnap.c b/net/802/psnap.c
index b3cfe5a..70980ba 100644
--- a/net/802/psnap.c
+++ b/net/802/psnap.c
@@ -1,7 +1,7 @@
 /*
  *	SNAP data link layer. Derived from 802.2
  *
- *		Alan Cox <Alan.Cox@linux.org>,
+ *		Alan Cox <alan@lxorguk.ukuu.org.uk>,
  *		from the 802.2 layer by Greg Page.
  *		Merged in additions from Greg Page's psnap.c.
  *
diff --git a/net/appletalk/ddp.c b/net/appletalk/ddp.c
index 0c85042..d3134e7 100644
--- a/net/appletalk/ddp.c
+++ b/net/appletalk/ddp.c
@@ -2,7 +2,7 @@
  *	DDP:	An implementation of the AppleTalk DDP protocol for
  *		Ethernet 'ELAP'.
  *
- *		Alan Cox  <Alan.Cox@linux.org>
+ *		Alan Cox  <alan@lxorguk.ukuu.org.uk>
  *
  *		With more than a little assistance from
  *
@@ -1934,6 +1934,6 @@
 module_exit(atalk_exit);
 
 MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Alan Cox <Alan.Cox@linux.org>");
+MODULE_AUTHOR("Alan Cox <alan@lxorguk.ukuu.org.uk>");
 MODULE_DESCRIPTION("AppleTalk 0.20\n");
 MODULE_ALIAS_NETPROTO(PF_APPLETALK);
diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c
index 96434d7..acdeab3 100644
--- a/net/bluetooth/hidp/core.c
+++ b/net/bluetooth/hidp/core.c
@@ -578,7 +578,7 @@
 	if (session->hid) {
 		if (session->hid->claimed & HID_CLAIMED_INPUT)
 			hidinput_disconnect(session->hid);
-		hid_free_device(session->hid);
+		hid_destroy_device(session->hid);
 	}
 
 	/* Wakeup user-space polling for socket errors */
@@ -623,9 +623,15 @@
 static int hidp_setup_input(struct hidp_session *session,
 				struct hidp_connadd_req *req)
 {
-	struct input_dev *input = session->input;
+	struct input_dev *input;
 	int i;
 
+	input = input_allocate_device();
+	if (!input)
+		return -ENOMEM;
+
+	session->input = input;
+
 	input_set_drvdata(input, session);
 
 	input->name = "Bluetooth HID Boot Protocol Device";
@@ -677,67 +683,114 @@
 {
 }
 
-static const struct {
-	__u16 idVendor;
-	__u16 idProduct;
-	unsigned quirks;
-} hidp_blacklist[] = {
-	/* Apple wireless Mighty Mouse */
-	{ 0x05ac, 0x030c, HID_QUIRK_MIGHTYMOUSE | HID_QUIRK_INVERT_HWHEEL },
-
-	{ }	/* Terminating entry */
-};
-
-static void hidp_setup_quirks(struct hid_device *hid)
+static int hidp_parse(struct hid_device *hid)
 {
-	unsigned int n;
+	struct hidp_session *session = hid->driver_data;
+	struct hidp_connadd_req *req = session->req;
+	unsigned char *buf;
+	int ret;
 
-	for (n = 0; hidp_blacklist[n].idVendor; n++)
-		if (hidp_blacklist[n].idVendor == le16_to_cpu(hid->vendor) &&
-				hidp_blacklist[n].idProduct == le16_to_cpu(hid->product))
-			hid->quirks = hidp_blacklist[n].quirks;
+	buf = kmalloc(req->rd_size, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	if (copy_from_user(buf, req->rd_data, req->rd_size)) {
+		kfree(buf);
+		return -EFAULT;
+	}
+
+	ret = hid_parse_report(session->hid, buf, req->rd_size);
+
+	kfree(buf);
+
+	if (ret)
+		return ret;
+
+	session->req = NULL;
+
+	return 0;
 }
 
-static void hidp_setup_hid(struct hidp_session *session,
+static int hidp_start(struct hid_device *hid)
+{
+	struct hidp_session *session = hid->driver_data;
+	struct hid_report *report;
+
+	list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].
+			report_list, list)
+		hidp_send_report(session, report);
+
+	list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT].
+			report_list, list)
+		hidp_send_report(session, report);
+
+	return 0;
+}
+
+static void hidp_stop(struct hid_device *hid)
+{
+	struct hidp_session *session = hid->driver_data;
+
+	skb_queue_purge(&session->ctrl_transmit);
+	skb_queue_purge(&session->intr_transmit);
+
+	if (hid->claimed & HID_CLAIMED_INPUT)
+		hidinput_disconnect(hid);
+	hid->claimed = 0;
+}
+
+static struct hid_ll_driver hidp_hid_driver = {
+	.parse = hidp_parse,
+	.start = hidp_start,
+	.stop = hidp_stop,
+	.open  = hidp_open,
+	.close = hidp_close,
+	.hidinput_input_event = hidp_hidinput_event,
+};
+
+static int hidp_setup_hid(struct hidp_session *session,
 				struct hidp_connadd_req *req)
 {
-	struct hid_device *hid = session->hid;
-	struct hid_report *report;
+	struct hid_device *hid;
 	bdaddr_t src, dst;
+	int ret;
+
+	hid = hid_allocate_device();
+	if (IS_ERR(hid)) {
+		ret = PTR_ERR(session->hid);
+		goto err;
+	}
+
+	session->hid = hid;
+	session->req = req;
+	hid->driver_data = session;
 
 	baswap(&src, &bt_sk(session->ctrl_sock->sk)->src);
 	baswap(&dst, &bt_sk(session->ctrl_sock->sk)->dst);
 
-	hid->driver_data = session;
-
-	hid->country = req->country;
-
 	hid->bus     = BUS_BLUETOOTH;
 	hid->vendor  = req->vendor;
 	hid->product = req->product;
 	hid->version = req->version;
+	hid->country = req->country;
 
 	strncpy(hid->name, req->name, 128);
 	strncpy(hid->phys, batostr(&src), 64);
 	strncpy(hid->uniq, batostr(&dst), 64);
 
-	hid->dev = hidp_get_device(session);
+	hid->dev.parent = hidp_get_device(session);
+	hid->ll_driver = &hidp_hid_driver;
 
-	hid->hid_open  = hidp_open;
-	hid->hid_close = hidp_close;
+	ret = hid_add_device(hid);
+	if (ret)
+		goto err_hid;
 
-	hid->hidinput_input_event = hidp_hidinput_event;
-
-	hidp_setup_quirks(hid);
-
-	list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].report_list, list)
-		hidp_send_report(session, report);
-
-	list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT].report_list, list)
-		hidp_send_report(session, report);
-
-	if (hidinput_connect(hid) == 0)
-		hid->claimed |= HID_CLAIMED_INPUT;
+	return 0;
+err_hid:
+	hid_destroy_device(hid);
+	session->hid = NULL;
+err:
+	return ret;
 }
 
 int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock)
@@ -757,38 +810,6 @@
 
 	BT_DBG("rd_data %p rd_size %d", req->rd_data, req->rd_size);
 
-	if (req->rd_size > 0) {
-		unsigned char *buf = kmalloc(req->rd_size, GFP_KERNEL);
-
-		if (!buf) {
-			kfree(session);
-			return -ENOMEM;
-		}
-
-		if (copy_from_user(buf, req->rd_data, req->rd_size)) {
-			kfree(buf);
-			kfree(session);
-			return -EFAULT;
-		}
-
-		session->hid = hid_parse_report(buf, req->rd_size);
-
-		kfree(buf);
-
-		if (!session->hid) {
-			kfree(session);
-			return -EINVAL;
-		}
-	}
-
-	if (!session->hid) {
-		session->input = input_allocate_device();
-		if (!session->input) {
-			kfree(session);
-			return -ENOMEM;
-		}
-	}
-
 	down_write(&hidp_session_sem);
 
 	s = __hidp_get_session(&bt_sk(ctrl_sock->sk)->dst);
@@ -816,14 +837,17 @@
 	session->flags   = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID);
 	session->idle_to = req->idle_to;
 
-	if (session->input) {
-		err = hidp_setup_input(session, req);
-		if (err < 0)
-			goto failed;
+	if (req->rd_size > 0) {
+		err = hidp_setup_hid(session, req);
+		if (err && err != -ENODEV)
+			goto err_skb;
 	}
 
-	if (session->hid)
-		hidp_setup_hid(session, req);
+	if (!session->hid) {
+		err = hidp_setup_input(session, req);
+		if (err < 0)
+			goto err_skb;
+	}
 
 	__hidp_link_session(session);
 
@@ -850,17 +874,16 @@
 
 	__hidp_unlink_session(session);
 
-	if (session->input) {
+	if (session->input)
 		input_unregister_device(session->input);
-		session->input = NULL; /* don't try to free it here */
-	}
-
+	if (session->hid)
+		hid_destroy_device(session->hid);
+err_skb:
+	skb_queue_purge(&session->ctrl_transmit);
+	skb_queue_purge(&session->intr_transmit);
 failed:
 	up_write(&hidp_session_sem);
 
-	if (session->hid)
-		hid_free_device(session->hid);
-
 	input_free_device(session->input);
 	kfree(session);
 	return err;
@@ -950,18 +973,43 @@
 	return err;
 }
 
+static const struct hid_device_id hidp_table[] = {
+	{ HID_BLUETOOTH_DEVICE(HID_ANY_ID, HID_ANY_ID) },
+	{ }
+};
+
+static struct hid_driver hidp_driver = {
+	.name = "generic-bluetooth",
+	.id_table = hidp_table,
+};
+
 static int __init hidp_init(void)
 {
+	int ret;
+
 	l2cap_load();
 
 	BT_INFO("HIDP (Human Interface Emulation) ver %s", VERSION);
 
-	return hidp_init_sockets();
+	ret = hid_register_driver(&hidp_driver);
+	if (ret)
+		goto err;
+
+	ret = hidp_init_sockets();
+	if (ret)
+		goto err_drv;
+
+	return 0;
+err_drv:
+	hid_unregister_driver(&hidp_driver);
+err:
+	return ret;
 }
 
 static void __exit hidp_exit(void)
 {
 	hidp_cleanup_sockets();
+	hid_unregister_driver(&hidp_driver);
 }
 
 module_init(hidp_init);
diff --git a/net/bluetooth/hidp/hidp.h b/net/bluetooth/hidp/hidp.h
index 343fb05..e503c89 100644
--- a/net/bluetooth/hidp/hidp.h
+++ b/net/bluetooth/hidp/hidp.h
@@ -151,6 +151,8 @@
 
 	struct sk_buff_head ctrl_transmit;
 	struct sk_buff_head intr_transmit;
+
+	struct hidp_connadd_req *req;
 };
 
 static inline void hidp_schedule(struct hidp_session *session)
diff --git a/net/core/datagram.c b/net/core/datagram.c
index 52f577a0..ee63184 100644
--- a/net/core/datagram.c
+++ b/net/core/datagram.c
@@ -9,7 +9,7 @@
  *	identical recvmsg() code. So we share it here. The poll was
  *	shared before but buried in udp.c so I moved it.
  *
- *	Authors:	Alan Cox <alan@redhat.com>. (datagram_poll() from old
+ *	Authors:	Alan Cox <alan@lxorguk.ukuu.org.uk>. (datagram_poll() from old
  *						     udp.c code)
  *
  *	Fixes:
diff --git a/net/core/dev_mcast.c b/net/core/dev_mcast.c
index 5402b3b..9e2fa39 100644
--- a/net/core/dev_mcast.c
+++ b/net/core/dev_mcast.c
@@ -6,7 +6,7 @@
  *		Richard Underwood <richard@wuzz.demon.co.uk>
  *
  *	Stir fried together from the IP multicast and CAP patches above
- *		Alan Cox <Alan.Cox@linux.org>
+ *		Alan Cox <alan@lxorguk.ukuu.org.uk>
  *
  *	Fixes:
  *		Alan Cox	:	Update the device on a real delete
diff --git a/net/core/pktgen.c b/net/core/pktgen.c
index a756847..99f656d 100644
--- a/net/core/pktgen.c
+++ b/net/core/pktgen.c
@@ -2474,7 +2474,7 @@
 				if (ret < 0) {
 					printk(KERN_ERR "Error expanding "
 					       "ipsec packet %d\n",ret);
-					return 0;
+					goto err;
 				}
 			}
 
@@ -2484,8 +2484,7 @@
 			if (ret) {
 				printk(KERN_ERR "Error creating ipsec "
 				       "packet %d\n",ret);
-				kfree_skb(skb);
-				return 0;
+				goto err;
 			}
 			/* restore ll */
 			eth = (__u8 *) skb_push(skb, ETH_HLEN);
@@ -2494,6 +2493,9 @@
 		}
 	}
 	return 1;
+err:
+	kfree_skb(skb);
+	return 0;
 }
 #endif
 
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 7f7bb1a..4e22e3a 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -1,7 +1,7 @@
 /*
  *	Routines having to do with the 'struct sk_buff' memory handlers.
  *
- *	Authors:	Alan Cox <iiitac@pyr.swan.ac.uk>
+ *	Authors:	Alan Cox <alan@lxorguk.ukuu.org.uk>
  *			Florian La Roche <rzsfl@rz.uni-sb.de>
  *
  *	Fixes:
diff --git a/net/core/stream.c b/net/core/stream.c
index a6b3437..8727cea 100644
--- a/net/core/stream.c
+++ b/net/core/stream.c
@@ -9,7 +9,7 @@
  *
  *     Authors:        Arnaldo Carvalho de Melo <acme@conectiva.com.br>
  *                     (from old tcp.c code)
- *                     Alan Cox <alan@redhat.com> (Borrowed comments 8-))
+ *                     Alan Cox <alan@lxorguk.ukuu.org.uk> (Borrowed comments 8-))
  */
 
 #include <linux/module.h>
diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig
index cdce4c6..49211b3 100644
--- a/net/dsa/Kconfig
+++ b/net/dsa/Kconfig
@@ -1,7 +1,7 @@
 menuconfig NET_DSA
 	bool "Distributed Switch Architecture support"
 	default n
-	depends on EXPERIMENTAL
+	depends on EXPERIMENTAL && !S390
 	select PHYLIB
 	---help---
 	  This allows you to use hardware switch chips that use
diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
index 55c355e..72b2de7 100644
--- a/net/ipv4/icmp.c
+++ b/net/ipv4/icmp.c
@@ -1,7 +1,7 @@
 /*
  *	NET3:	Implementation of the ICMP protocol layer.
  *
- *		Alan Cox, <alan@redhat.com>
+ *		Alan Cox, <alan@lxorguk.ukuu.org.uk>
  *
  *	This program is free software; you can redistribute it and/or
  *	modify it under the terms of the GNU General Public License
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index 7f9e337..a0d8645 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -9,7 +9,7 @@
  *	seems to fall out with gcc 2.6.2.
  *
  *	Authors:
- *		Alan Cox <Alan.Cox@linux.org>
+ *		Alan Cox <alan@lxorguk.ukuu.org.uk>
  *
  *	This program is free software; you can redistribute it and/or
  *	modify it under the terms of the GNU General Public License
diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c
index 2152d22..e4f81f5 100644
--- a/net/ipv4/ip_fragment.c
+++ b/net/ipv4/ip_fragment.c
@@ -6,7 +6,7 @@
  *		The IP fragmentation functionality.
  *
  * Authors:	Fred N. van Kempen <waltje@uWalt.NL.Mugnet.ORG>
- *		Alan Cox <Alan.Cox@linux.org>
+ *		Alan Cox <alan@lxorguk.ukuu.org.uk>
  *
  * Fixes:
  *		Alan Cox	:	Split from ip.c , see ip_input.c for history.
diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c
index e0bed56..861978a 100644
--- a/net/ipv4/ip_input.c
+++ b/net/ipv4/ip_input.c
@@ -8,7 +8,7 @@
  * Authors:	Ross Biro
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
  *		Donald Becker, <becker@super.org>
- *		Alan Cox, <Alan.Cox@linux.org>
+ *		Alan Cox, <alan@lxorguk.ukuu.org.uk>
  *		Richard Underwood
  *		Stefan Becker, <stefanb@yello.ping.de>
  *		Jorge Cwik, <jorge@laser.satlink.net>
diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c
index 4c6d2ca..29609d2 100644
--- a/net/ipv4/ipip.c
+++ b/net/ipv4/ipip.c
@@ -41,7 +41,7 @@
 		Made the tunnels use dev->name not tunnel: when error reporting.
 		Added tx_dropped stat
 
-		-Alan Cox	(Alan.Cox@linux.org) 21 March 95
+		-Alan Cox	(alan@lxorguk.ukuu.org.uk) 21 March 95
 
 	Reworked:
 		Changed to tunnel to destination gateway in addition to the
diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
index c519b8d..b42e082 100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c
@@ -1,7 +1,7 @@
 /*
  *	IP multicast routing support for mrouted 3.6/3.8
  *
- *		(c) 1995 Alan Cox, <alan@redhat.com>
+ *		(c) 1995 Alan Cox, <alan@lxorguk.ukuu.org.uk>
  *	  Linux Consultancy and Custom Driver Development
  *
  *	This program is free software; you can redistribute it and/or
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index eacf4cf..2095abc 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -8,7 +8,7 @@
  * Authors:	Ross Biro
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
  *		Arnt Gulbrandsen, <agulbra@nvg.unit.no>
- *		Alan Cox, <Alan.Cox@linux.org>
+ *		Alan Cox, <alan@lxorguk.ukuu.org.uk>
  *		Hirokazu Takahashi, <taka@valinux.co.jp>
  *
  * Fixes:
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index 050e14b..01edac8 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -834,7 +834,7 @@
 	snmp_mib_free((void **)net->mib.icmpv6msg_statistics);
 }
 
-static int inet6_net_init(struct net *net)
+static int __net_init inet6_net_init(struct net *net)
 {
 	int err = 0;
 
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index b0eacc0..2fd8afa 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -1,7 +1,7 @@
 /*
  * NETLINK      Kernel-user communication protocol.
  *
- * 		Authors:	Alan Cox <alan@redhat.com>
+ * 		Authors:	Alan Cox <alan@lxorguk.ukuu.org.uk>
  * 				Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
  *
  *		This program is free software; you can redistribute it and/or
diff --git a/net/rfkill/rfkill-input.c b/net/rfkill/rfkill-input.c
index e5b6955..21124ec 100644
--- a/net/rfkill/rfkill-input.c
+++ b/net/rfkill/rfkill-input.c
@@ -16,6 +16,7 @@
 #include <linux/workqueue.h>
 #include <linux/init.h>
 #include <linux/rfkill.h>
+#include <linux/sched.h>
 
 #include "rfkill-input.h"
 
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
index 76739e9..da0789f 100644
--- a/net/sunrpc/clnt.c
+++ b/net/sunrpc/clnt.c
@@ -174,7 +174,7 @@
 	clnt->cl_procinfo = version->procs;
 	clnt->cl_maxproc  = version->nrprocs;
 	clnt->cl_protname = program->name;
-	clnt->cl_prog     = program->number;
+	clnt->cl_prog     = args->prognumber ? : program->number;
 	clnt->cl_vers     = version->number;
 	clnt->cl_stats    = program->stats;
 	clnt->cl_metrics  = rpc_alloc_iostats(clnt);
diff --git a/net/sunrpc/rpcb_clnt.c b/net/sunrpc/rpcb_clnt.c
index 24db2b4..34abc91 100644
--- a/net/sunrpc/rpcb_clnt.c
+++ b/net/sunrpc/rpcb_clnt.c
@@ -20,6 +20,7 @@
 #include <linux/in6.h>
 #include <linux/kernel.h>
 #include <linux/errno.h>
+#include <net/ipv6.h>
 
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/sched.h>
@@ -176,13 +177,12 @@
 }
 
 static int rpcb_register_call(struct sockaddr *addr, size_t addrlen,
-			      u32 version, struct rpc_message *msg,
-			      int *result)
+			      u32 version, struct rpc_message *msg)
 {
 	struct rpc_clnt *rpcb_clnt;
-	int error = 0;
+	int result, error = 0;
 
-	*result = 0;
+	msg->rpc_resp = &result;
 
 	rpcb_clnt = rpcb_create_local(addr, addrlen, version);
 	if (!IS_ERR(rpcb_clnt)) {
@@ -191,12 +191,15 @@
 	} else
 		error = PTR_ERR(rpcb_clnt);
 
-	if (error < 0)
+	if (error < 0) {
 		printk(KERN_WARNING "RPC: failed to contact local rpcbind "
 				"server (errno %d).\n", -error);
-	dprintk("RPC:       registration status %d/%d\n", error, *result);
+		return error;
+	}
 
-	return error;
+	if (!result)
+		return -EACCES;
+	return 0;
 }
 
 /**
@@ -205,7 +208,11 @@
  * @vers: RPC version number to bind
  * @prot: transport protocol to register
  * @port: port value to register
- * @okay: OUT: result code
+ *
+ * Returns zero if the registration request was dispatched successfully
+ * and the rpcbind daemon returned success.  Otherwise, returns an errno
+ * value that reflects the nature of the error (request could not be
+ * dispatched, timed out, or rpcbind returned an error).
  *
  * RPC services invoke this function to advertise their contact
  * information via the system's rpcbind daemon.  RPC services
@@ -217,15 +224,6 @@
  * all registered transports for [program, version] from the local
  * rpcbind database.
  *
- * Returns zero if the registration request was dispatched
- * successfully and a reply was received.  The rpcbind daemon's
- * boolean result code is stored in *okay.
- *
- * Returns an errno value and sets *result to zero if there was
- * some problem that prevented the rpcbind request from being
- * dispatched, or if the rpcbind daemon did not respond within
- * the timeout.
- *
  * This function uses rpcbind protocol version 2 to contact the
  * local rpcbind daemon.
  *
@@ -236,7 +234,7 @@
  * IN6ADDR_ANY (ie available for all AF_INET and AF_INET6
  * addresses).
  */
-int rpcb_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay)
+int rpcb_register(u32 prog, u32 vers, int prot, unsigned short port)
 {
 	struct rpcbind_args map = {
 		.r_prog		= prog,
@@ -246,7 +244,6 @@
 	};
 	struct rpc_message msg = {
 		.rpc_argp	= &map,
-		.rpc_resp	= okay,
 	};
 
 	dprintk("RPC:       %sregistering (%u, %u, %d, %u) with local "
@@ -259,7 +256,7 @@
 
 	return rpcb_register_call((struct sockaddr *)&rpcb_inaddr_loopback,
 					sizeof(rpcb_inaddr_loopback),
-					RPCBVERS_2, &msg, okay);
+					RPCBVERS_2, &msg);
 }
 
 /*
@@ -290,7 +287,7 @@
 
 	return rpcb_register_call((struct sockaddr *)&rpcb_inaddr_loopback,
 					sizeof(rpcb_inaddr_loopback),
-					RPCBVERS_4, msg, msg->rpc_resp);
+					RPCBVERS_4, msg);
 }
 
 /*
@@ -304,10 +301,13 @@
 	char buf[64];
 
 	/* Construct AF_INET6 universal address */
-	snprintf(buf, sizeof(buf),
-			NIP6_FMT".%u.%u",
-			NIP6(address_to_register->sin6_addr),
-			port >> 8, port & 0xff);
+	if (ipv6_addr_any(&address_to_register->sin6_addr))
+		snprintf(buf, sizeof(buf), "::.%u.%u",
+				port >> 8, port & 0xff);
+	else
+		snprintf(buf, sizeof(buf), NIP6_FMT".%u.%u",
+				NIP6(address_to_register->sin6_addr),
+				port >> 8, port & 0xff);
 	map->r_addr = buf;
 
 	dprintk("RPC:       %sregistering [%u, %u, %s, '%s'] with "
@@ -321,7 +321,7 @@
 
 	return rpcb_register_call((struct sockaddr *)&rpcb_in6addr_loopback,
 					sizeof(rpcb_in6addr_loopback),
-					RPCBVERS_4, msg, msg->rpc_resp);
+					RPCBVERS_4, msg);
 }
 
 /**
@@ -330,7 +330,11 @@
  * @version: RPC version number of service to (un)register
  * @address: address family, IP address, and port to (un)register
  * @netid: netid of transport protocol to (un)register
- * @result: result code from rpcbind RPC call
+ *
+ * Returns zero if the registration request was dispatched successfully
+ * and the rpcbind daemon returned success.  Otherwise, returns an errno
+ * value that reflects the nature of the error (request could not be
+ * dispatched, timed out, or rpcbind returned an error).
  *
  * RPC services invoke this function to advertise their contact
  * information via the system's rpcbind daemon.  RPC services
@@ -342,15 +346,6 @@
  * to zero.  Callers pass a netid of "" to unregister all
  * transport netids associated with [program, version, address].
  *
- * Returns zero if the registration request was dispatched
- * successfully and a reply was received.  The rpcbind daemon's
- * result code is stored in *result.
- *
- * Returns an errno value and sets *result to zero if there was
- * some problem that prevented the rpcbind request from being
- * dispatched, or if the rpcbind daemon did not respond within
- * the timeout.
- *
  * This function uses rpcbind protocol version 4 to contact the
  * local rpcbind daemon.  The local rpcbind daemon must support
  * version 4 of the rpcbind protocol in order for these functions
@@ -372,8 +367,7 @@
  * advertises the service on all IPv4 and IPv6 addresses.
  */
 int rpcb_v4_register(const u32 program, const u32 version,
-		     const struct sockaddr *address, const char *netid,
-		     int *result)
+		     const struct sockaddr *address, const char *netid)
 {
 	struct rpcbind_args map = {
 		.r_prog		= program,
@@ -383,11 +377,8 @@
 	};
 	struct rpc_message msg = {
 		.rpc_argp	= &map,
-		.rpc_resp	= result,
 	};
 
-	*result = 0;
-
 	switch (address->sa_family) {
 	case AF_INET:
 		return rpcb_register_netid4((struct sockaddr_in *)address,
@@ -633,7 +624,7 @@
 static int rpcb_encode_mapping(struct rpc_rqst *req, __be32 *p,
 			       struct rpcbind_args *rpcb)
 {
-	dprintk("RPC:       rpcb_encode_mapping(%u, %u, %d, %u)\n",
+	dprintk("RPC:       encoding rpcb request (%u, %u, %d, %u)\n",
 			rpcb->r_prog, rpcb->r_vers, rpcb->r_prot, rpcb->r_port);
 	*p++ = htonl(rpcb->r_prog);
 	*p++ = htonl(rpcb->r_vers);
@@ -648,7 +639,7 @@
 			       unsigned short *portp)
 {
 	*portp = (unsigned short) ntohl(*p++);
-	dprintk("RPC:       rpcb_decode_getport result %u\n",
+	dprintk("RPC:       rpcb getport result: %u\n",
 			*portp);
 	return 0;
 }
@@ -657,7 +648,7 @@
 			   unsigned int *boolp)
 {
 	*boolp = (unsigned int) ntohl(*p++);
-	dprintk("RPC:       rpcb_decode_set: call %s\n",
+	dprintk("RPC:       rpcb set/unset call %s\n",
 			(*boolp ? "succeeded" : "failed"));
 	return 0;
 }
@@ -665,7 +656,7 @@
 static int rpcb_encode_getaddr(struct rpc_rqst *req, __be32 *p,
 			       struct rpcbind_args *rpcb)
 {
-	dprintk("RPC:       rpcb_encode_getaddr(%u, %u, %s)\n",
+	dprintk("RPC:       encoding rpcb request (%u, %u, %s)\n",
 			rpcb->r_prog, rpcb->r_vers, rpcb->r_addr);
 	*p++ = htonl(rpcb->r_prog);
 	*p++ = htonl(rpcb->r_vers);
diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c
index 5a32cb7..54c98d8 100644
--- a/net/sunrpc/svc.c
+++ b/net/sunrpc/svc.c
@@ -28,6 +28,8 @@
 
 #define RPCDBG_FACILITY	RPCDBG_SVCDSP
 
+static void svc_unregister(const struct svc_serv *serv);
+
 #define svc_serv_is_pooled(serv)    ((serv)->sv_function)
 
 /*
@@ -357,7 +359,7 @@
  */
 static struct svc_serv *
 __svc_create(struct svc_program *prog, unsigned int bufsize, int npools,
-	   void (*shutdown)(struct svc_serv *serv))
+	   sa_family_t family, void (*shutdown)(struct svc_serv *serv))
 {
 	struct svc_serv	*serv;
 	unsigned int vers;
@@ -366,6 +368,7 @@
 
 	if (!(serv = kzalloc(sizeof(*serv), GFP_KERNEL)))
 		return NULL;
+	serv->sv_family    = family;
 	serv->sv_name      = prog->pg_name;
 	serv->sv_program   = prog;
 	serv->sv_nrthreads = 1;
@@ -416,30 +419,29 @@
 		spin_lock_init(&pool->sp_lock);
 	}
 
-
 	/* Remove any stale portmap registrations */
-	svc_register(serv, 0, 0);
+	svc_unregister(serv);
 
 	return serv;
 }
 
 struct svc_serv *
 svc_create(struct svc_program *prog, unsigned int bufsize,
-		void (*shutdown)(struct svc_serv *serv))
+		sa_family_t family, void (*shutdown)(struct svc_serv *serv))
 {
-	return __svc_create(prog, bufsize, /*npools*/1, shutdown);
+	return __svc_create(prog, bufsize, /*npools*/1, family, shutdown);
 }
 EXPORT_SYMBOL(svc_create);
 
 struct svc_serv *
 svc_create_pooled(struct svc_program *prog, unsigned int bufsize,
-		void (*shutdown)(struct svc_serv *serv),
+		  sa_family_t family, void (*shutdown)(struct svc_serv *serv),
 		  svc_thread_fn func, struct module *mod)
 {
 	struct svc_serv *serv;
 	unsigned int npools = svc_pool_map_get();
 
-	serv = __svc_create(prog, bufsize, npools, shutdown);
+	serv = __svc_create(prog, bufsize, npools, family, shutdown);
 
 	if (serv != NULL) {
 		serv->sv_function = func;
@@ -486,8 +488,7 @@
 	if (svc_serv_is_pooled(serv))
 		svc_pool_map_put();
 
-	/* Unregister service with the portmapper */
-	svc_register(serv, 0, 0);
+	svc_unregister(serv);
 	kfree(serv->sv_pools);
 	kfree(serv);
 }
@@ -718,55 +719,245 @@
 }
 EXPORT_SYMBOL(svc_exit_thread);
 
+#ifdef CONFIG_SUNRPC_REGISTER_V4
+
 /*
- * Register an RPC service with the local portmapper.
- * To unregister a service, call this routine with
- * proto and port == 0.
+ * Register an "inet" protocol family netid with the local
+ * rpcbind daemon via an rpcbind v4 SET request.
+ *
+ * No netconfig infrastructure is available in the kernel, so
+ * we map IP_ protocol numbers to netids by hand.
+ *
+ * Returns zero on success; a negative errno value is returned
+ * if any error occurs.
  */
-int
-svc_register(struct svc_serv *serv, int proto, unsigned short port)
+static int __svc_rpcb_register4(const u32 program, const u32 version,
+				const unsigned short protocol,
+				const unsigned short port)
+{
+	struct sockaddr_in sin = {
+		.sin_family		= AF_INET,
+		.sin_addr.s_addr	= htonl(INADDR_ANY),
+		.sin_port		= htons(port),
+	};
+	char *netid;
+
+	switch (protocol) {
+	case IPPROTO_UDP:
+		netid = RPCBIND_NETID_UDP;
+		break;
+	case IPPROTO_TCP:
+		netid = RPCBIND_NETID_TCP;
+		break;
+	default:
+		return -EPROTONOSUPPORT;
+	}
+
+	return rpcb_v4_register(program, version,
+				(struct sockaddr *)&sin, netid);
+}
+
+/*
+ * Register an "inet6" protocol family netid with the local
+ * rpcbind daemon via an rpcbind v4 SET request.
+ *
+ * No netconfig infrastructure is available in the kernel, so
+ * we map IP_ protocol numbers to netids by hand.
+ *
+ * Returns zero on success; a negative errno value is returned
+ * if any error occurs.
+ */
+static int __svc_rpcb_register6(const u32 program, const u32 version,
+				const unsigned short protocol,
+				const unsigned short port)
+{
+	struct sockaddr_in6 sin6 = {
+		.sin6_family		= AF_INET6,
+		.sin6_addr		= IN6ADDR_ANY_INIT,
+		.sin6_port		= htons(port),
+	};
+	char *netid;
+
+	switch (protocol) {
+	case IPPROTO_UDP:
+		netid = RPCBIND_NETID_UDP6;
+		break;
+	case IPPROTO_TCP:
+		netid = RPCBIND_NETID_TCP6;
+		break;
+	default:
+		return -EPROTONOSUPPORT;
+	}
+
+	return rpcb_v4_register(program, version,
+				(struct sockaddr *)&sin6, netid);
+}
+
+/*
+ * Register a kernel RPC service via rpcbind version 4.
+ *
+ * Returns zero on success; a negative errno value is returned
+ * if any error occurs.
+ */
+static int __svc_register(const u32 program, const u32 version,
+			  const sa_family_t family,
+			  const unsigned short protocol,
+			  const unsigned short port)
+{
+	int error;
+
+	switch (family) {
+	case AF_INET:
+		return __svc_rpcb_register4(program, version,
+						protocol, port);
+	case AF_INET6:
+		error = __svc_rpcb_register6(program, version,
+						protocol, port);
+		if (error < 0)
+			return error;
+
+		/*
+		 * Work around bug in some versions of Linux rpcbind
+		 * which don't allow registration of both inet and
+		 * inet6 netids.
+		 *
+		 * Error return ignored for now.
+		 */
+		__svc_rpcb_register4(program, version,
+						protocol, port);
+		return 0;
+	}
+
+	return -EAFNOSUPPORT;
+}
+
+#else	/* CONFIG_SUNRPC_REGISTER_V4 */
+
+/*
+ * Register a kernel RPC service via rpcbind version 2.
+ *
+ * Returns zero on success; a negative errno value is returned
+ * if any error occurs.
+ */
+static int __svc_register(const u32 program, const u32 version,
+			  sa_family_t family,
+			  const unsigned short protocol,
+			  const unsigned short port)
+{
+	if (family != AF_INET)
+		return -EAFNOSUPPORT;
+
+	return rpcb_register(program, version, protocol, port);
+}
+
+#endif /* CONFIG_SUNRPC_REGISTER_V4 */
+
+/**
+ * svc_register - register an RPC service with the local portmapper
+ * @serv: svc_serv struct for the service to register
+ * @proto: transport protocol number to advertise
+ * @port: port to advertise
+ *
+ * Service is registered for any address in serv's address family
+ */
+int svc_register(const struct svc_serv *serv, const unsigned short proto,
+		 const unsigned short port)
 {
 	struct svc_program	*progp;
-	unsigned long		flags;
 	unsigned int		i;
-	int			error = 0, dummy;
+	int			error = 0;
 
-	if (!port)
-		clear_thread_flag(TIF_SIGPENDING);
+	BUG_ON(proto == 0 && port == 0);
 
 	for (progp = serv->sv_program; progp; progp = progp->pg_next) {
 		for (i = 0; i < progp->pg_nvers; i++) {
 			if (progp->pg_vers[i] == NULL)
 				continue;
 
-			dprintk("svc: svc_register(%s, %s, %d, %d)%s\n",
+			dprintk("svc: svc_register(%sv%d, %s, %u, %u)%s\n",
 					progp->pg_name,
+					i,
 					proto == IPPROTO_UDP?  "udp" : "tcp",
 					port,
-					i,
+					serv->sv_family,
 					progp->pg_vers[i]->vs_hidden?
 						" (but not telling portmap)" : "");
 
 			if (progp->pg_vers[i]->vs_hidden)
 				continue;
 
-			error = rpcb_register(progp->pg_prog, i, proto, port, &dummy);
+			error = __svc_register(progp->pg_prog, i,
+						serv->sv_family, proto, port);
 			if (error < 0)
 				break;
-			if (port && !dummy) {
-				error = -EACCES;
-				break;
-			}
 		}
 	}
 
-	if (!port) {
-		spin_lock_irqsave(&current->sighand->siglock, flags);
-		recalc_sigpending();
-		spin_unlock_irqrestore(&current->sighand->siglock, flags);
+	return error;
+}
+
+#ifdef CONFIG_SUNRPC_REGISTER_V4
+
+static void __svc_unregister(const u32 program, const u32 version,
+			     const char *progname)
+{
+	struct sockaddr_in6 sin6 = {
+		.sin6_family		= AF_INET6,
+		.sin6_addr		= IN6ADDR_ANY_INIT,
+		.sin6_port		= 0,
+	};
+	int error;
+
+	error = rpcb_v4_register(program, version,
+				(struct sockaddr *)&sin6, "");
+	dprintk("svc: %s(%sv%u), error %d\n",
+			__func__, progname, version, error);
+}
+
+#else	/* CONFIG_SUNRPC_REGISTER_V4 */
+
+static void __svc_unregister(const u32 program, const u32 version,
+			     const char *progname)
+{
+	int error;
+
+	error = rpcb_register(program, version, 0, 0);
+	dprintk("svc: %s(%sv%u), error %d\n",
+			__func__, progname, version, error);
+}
+
+#endif	/* CONFIG_SUNRPC_REGISTER_V4 */
+
+/*
+ * All netids, bind addresses and ports registered for [program, version]
+ * are removed from the local rpcbind database (if the service is not
+ * hidden) to make way for a new instance of the service.
+ *
+ * The result of unregistration is reported via dprintk for those who want
+ * verification of the result, but is otherwise not important.
+ */
+static void svc_unregister(const struct svc_serv *serv)
+{
+	struct svc_program *progp;
+	unsigned long flags;
+	unsigned int i;
+
+	clear_thread_flag(TIF_SIGPENDING);
+
+	for (progp = serv->sv_program; progp; progp = progp->pg_next) {
+		for (i = 0; i < progp->pg_nvers; i++) {
+			if (progp->pg_vers[i] == NULL)
+				continue;
+			if (progp->pg_vers[i]->vs_hidden)
+				continue;
+
+			__svc_unregister(progp->pg_prog, i, progp->pg_name);
+		}
 	}
 
-	return error;
+	spin_lock_irqsave(&current->sighand->siglock, flags);
+	recalc_sigpending();
+	spin_unlock_irqrestore(&current->sighand->siglock, flags);
 }
 
 /*
diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c
index e46c825..bf5b5cd 100644
--- a/net/sunrpc/svc_xprt.c
+++ b/net/sunrpc/svc_xprt.c
@@ -159,15 +159,44 @@
 }
 EXPORT_SYMBOL_GPL(svc_xprt_init);
 
-int svc_create_xprt(struct svc_serv *serv, char *xprt_name, unsigned short port,
-		    int flags)
+static struct svc_xprt *__svc_xpo_create(struct svc_xprt_class *xcl,
+					 struct svc_serv *serv,
+					 unsigned short port, int flags)
 {
-	struct svc_xprt_class *xcl;
 	struct sockaddr_in sin = {
 		.sin_family		= AF_INET,
 		.sin_addr.s_addr	= htonl(INADDR_ANY),
 		.sin_port		= htons(port),
 	};
+	struct sockaddr_in6 sin6 = {
+		.sin6_family		= AF_INET6,
+		.sin6_addr		= IN6ADDR_ANY_INIT,
+		.sin6_port		= htons(port),
+	};
+	struct sockaddr *sap;
+	size_t len;
+
+	switch (serv->sv_family) {
+	case AF_INET:
+		sap = (struct sockaddr *)&sin;
+		len = sizeof(sin);
+		break;
+	case AF_INET6:
+		sap = (struct sockaddr *)&sin6;
+		len = sizeof(sin6);
+		break;
+	default:
+		return ERR_PTR(-EAFNOSUPPORT);
+	}
+
+	return xcl->xcl_ops->xpo_create(serv, sap, len, flags);
+}
+
+int svc_create_xprt(struct svc_serv *serv, char *xprt_name, unsigned short port,
+		    int flags)
+{
+	struct svc_xprt_class *xcl;
+
 	dprintk("svc: creating transport %s[%d]\n", xprt_name, port);
 	spin_lock(&svc_xprt_class_lock);
 	list_for_each_entry(xcl, &svc_xprt_class_list, xcl_list) {
@@ -180,9 +209,7 @@
 			goto err;
 
 		spin_unlock(&svc_xprt_class_lock);
-		newxprt = xcl->xcl_ops->
-			xpo_create(serv, (struct sockaddr *)&sin, sizeof(sin),
-				   flags);
+		newxprt = __svc_xpo_create(xcl, serv, port, flags);
 		if (IS_ERR(newxprt)) {
 			module_put(xcl->xcl_owner);
 			return PTR_ERR(newxprt);
diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c
index 3e65719..95293f5 100644
--- a/net/sunrpc/svcsock.c
+++ b/net/sunrpc/svcsock.c
@@ -1114,6 +1114,7 @@
 	struct svc_sock	*svsk;
 	struct sock	*inet;
 	int		pmap_register = !(flags & SVC_SOCK_ANONYMOUS);
+	int		val;
 
 	dprintk("svc: svc_setup_socket %p\n", sock);
 	if (!(svsk = kzalloc(sizeof(*svsk), GFP_KERNEL))) {
@@ -1146,6 +1147,18 @@
 	else
 		svc_tcp_init(svsk, serv);
 
+	/*
+	 * We start one listener per sv_serv.  We want AF_INET
+	 * requests to be automatically shunted to our AF_INET6
+	 * listener using a mapped IPv4 address.  Make sure
+	 * no-one starts an equivalent IPv4 listener, which
+	 * would steal our incoming connections.
+	 */
+	val = 0;
+	if (serv->sv_family == AF_INET6)
+		kernel_setsockopt(sock, SOL_IPV6, IPV6_V6ONLY,
+					(char *)&val, sizeof(val));
+
 	dprintk("svc: svc_setup_socket created %p (inet %p)\n",
 				svsk, svsk->sk_sk);
 
@@ -1154,8 +1167,7 @@
 
 int svc_addsock(struct svc_serv *serv,
 		int fd,
-		char *name_return,
-		int *proto)
+		char *name_return)
 {
 	int err = 0;
 	struct socket *so = sockfd_lookup(fd, &err);
@@ -1190,7 +1202,6 @@
 		sockfd_put(so);
 		return err;
 	}
-	if (proto) *proto = so->sk->sk_protocol;
 	return one_sock_name(name_return, svsk);
 }
 EXPORT_SYMBOL_GPL(svc_addsock);
diff --git a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c
index 74de31a..a475657 100644
--- a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c
+++ b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c
@@ -116,7 +116,7 @@
  *
  * Assumptions:
  * - chunk[0]->position points to pages[0] at an offset of 0
- * - pages[] is not physically or virtually contigous and consists of
+ * - pages[] is not physically or virtually contiguous and consists of
  *   PAGE_SIZE elements.
  *
  * Output:
@@ -125,7 +125,7 @@
  *   chunk in the read list
  *
  */
-static int rdma_rcl_to_sge(struct svcxprt_rdma *xprt,
+static int map_read_chunks(struct svcxprt_rdma *xprt,
 			   struct svc_rqst *rqstp,
 			   struct svc_rdma_op_ctxt *head,
 			   struct rpcrdma_msg *rmsgp,
@@ -211,26 +211,128 @@
 	return sge_no;
 }
 
-static void rdma_set_ctxt_sge(struct svcxprt_rdma *xprt,
-			      struct svc_rdma_op_ctxt *ctxt,
-			      struct kvec *vec,
-			      u64 *sgl_offset,
-			      int count)
+/* Map a read-chunk-list to an XDR and fast register the page-list.
+ *
+ * Assumptions:
+ * - chunk[0]	position points to pages[0] at an offset of 0
+ * - pages[]	will be made physically contiguous by creating a one-off memory
+ *		region using the fastreg verb.
+ * - byte_count is # of bytes in read-chunk-list
+ * - ch_count	is # of chunks in read-chunk-list
+ *
+ * Output:
+ * - sge array pointing into pages[] array.
+ * - chunk_sge array specifying sge index and count for each
+ *   chunk in the read list
+ */
+static int fast_reg_read_chunks(struct svcxprt_rdma *xprt,
+				struct svc_rqst *rqstp,
+				struct svc_rdma_op_ctxt *head,
+				struct rpcrdma_msg *rmsgp,
+				struct svc_rdma_req_map *rpl_map,
+				struct svc_rdma_req_map *chl_map,
+				int ch_count,
+				int byte_count)
+{
+	int page_no;
+	int ch_no;
+	u32 offset;
+	struct rpcrdma_read_chunk *ch;
+	struct svc_rdma_fastreg_mr *frmr;
+	int ret = 0;
+
+	frmr = svc_rdma_get_frmr(xprt);
+	if (IS_ERR(frmr))
+		return -ENOMEM;
+
+	head->frmr = frmr;
+	head->arg.head[0] = rqstp->rq_arg.head[0];
+	head->arg.tail[0] = rqstp->rq_arg.tail[0];
+	head->arg.pages = &head->pages[head->count];
+	head->hdr_count = head->count; /* save count of hdr pages */
+	head->arg.page_base = 0;
+	head->arg.page_len = byte_count;
+	head->arg.len = rqstp->rq_arg.len + byte_count;
+	head->arg.buflen = rqstp->rq_arg.buflen + byte_count;
+
+	/* Fast register the page list */
+	frmr->kva = page_address(rqstp->rq_arg.pages[0]);
+	frmr->direction = DMA_FROM_DEVICE;
+	frmr->access_flags = (IB_ACCESS_LOCAL_WRITE|IB_ACCESS_REMOTE_WRITE);
+	frmr->map_len = byte_count;
+	frmr->page_list_len = PAGE_ALIGN(byte_count) >> PAGE_SHIFT;
+	for (page_no = 0; page_no < frmr->page_list_len; page_no++) {
+		frmr->page_list->page_list[page_no] =
+			ib_dma_map_single(xprt->sc_cm_id->device,
+					  page_address(rqstp->rq_arg.pages[page_no]),
+					  PAGE_SIZE, DMA_TO_DEVICE);
+		if (ib_dma_mapping_error(xprt->sc_cm_id->device,
+					 frmr->page_list->page_list[page_no]))
+			goto fatal_err;
+		atomic_inc(&xprt->sc_dma_used);
+		head->arg.pages[page_no] = rqstp->rq_arg.pages[page_no];
+	}
+	head->count += page_no;
+
+	/* rq_respages points one past arg pages */
+	rqstp->rq_respages = &rqstp->rq_arg.pages[page_no];
+
+	/* Create the reply and chunk maps */
+	offset = 0;
+	ch = (struct rpcrdma_read_chunk *)&rmsgp->rm_body.rm_chunks[0];
+	for (ch_no = 0; ch_no < ch_count; ch_no++) {
+		rpl_map->sge[ch_no].iov_base = frmr->kva + offset;
+		rpl_map->sge[ch_no].iov_len = ch->rc_target.rs_length;
+		chl_map->ch[ch_no].count = 1;
+		chl_map->ch[ch_no].start = ch_no;
+		offset += ch->rc_target.rs_length;
+		ch++;
+	}
+
+	ret = svc_rdma_fastreg(xprt, frmr);
+	if (ret)
+		goto fatal_err;
+
+	return ch_no;
+
+ fatal_err:
+	printk("svcrdma: error fast registering xdr for xprt %p", xprt);
+	svc_rdma_put_frmr(xprt, frmr);
+	return -EIO;
+}
+
+static int rdma_set_ctxt_sge(struct svcxprt_rdma *xprt,
+			     struct svc_rdma_op_ctxt *ctxt,
+			     struct svc_rdma_fastreg_mr *frmr,
+			     struct kvec *vec,
+			     u64 *sgl_offset,
+			     int count)
 {
 	int i;
 
 	ctxt->count = count;
 	ctxt->direction = DMA_FROM_DEVICE;
 	for (i = 0; i < count; i++) {
-		atomic_inc(&xprt->sc_dma_used);
-		ctxt->sge[i].addr =
-			ib_dma_map_single(xprt->sc_cm_id->device,
-					  vec[i].iov_base, vec[i].iov_len,
-					  DMA_FROM_DEVICE);
+		ctxt->sge[i].length = 0; /* in case map fails */
+		if (!frmr) {
+			ctxt->sge[i].addr =
+				ib_dma_map_single(xprt->sc_cm_id->device,
+						  vec[i].iov_base,
+						  vec[i].iov_len,
+						  DMA_FROM_DEVICE);
+			if (ib_dma_mapping_error(xprt->sc_cm_id->device,
+						 ctxt->sge[i].addr))
+				return -EINVAL;
+			ctxt->sge[i].lkey = xprt->sc_dma_lkey;
+			atomic_inc(&xprt->sc_dma_used);
+		} else {
+			ctxt->sge[i].addr = (unsigned long)vec[i].iov_base;
+			ctxt->sge[i].lkey = frmr->mr->lkey;
+		}
 		ctxt->sge[i].length = vec[i].iov_len;
-		ctxt->sge[i].lkey = xprt->sc_phys_mr->lkey;
 		*sgl_offset = *sgl_offset + vec[i].iov_len;
 	}
+	return 0;
 }
 
 static int rdma_read_max_sge(struct svcxprt_rdma *xprt, int sge_count)
@@ -278,6 +380,7 @@
 			 struct svc_rdma_op_ctxt *hdr_ctxt)
 {
 	struct ib_send_wr read_wr;
+	struct ib_send_wr inv_wr;
 	int err = 0;
 	int ch_no;
 	int ch_count;
@@ -301,9 +404,20 @@
 	svc_rdma_rcl_chunk_counts(ch, &ch_count, &byte_count);
 	if (ch_count > RPCSVC_MAXPAGES)
 		return -EINVAL;
-	sge_count = rdma_rcl_to_sge(xprt, rqstp, hdr_ctxt, rmsgp,
-				    rpl_map, chl_map,
-				    ch_count, byte_count);
+
+	if (!xprt->sc_frmr_pg_list_len)
+		sge_count = map_read_chunks(xprt, rqstp, hdr_ctxt, rmsgp,
+					    rpl_map, chl_map, ch_count,
+					    byte_count);
+	else
+		sge_count = fast_reg_read_chunks(xprt, rqstp, hdr_ctxt, rmsgp,
+						 rpl_map, chl_map, ch_count,
+						 byte_count);
+	if (sge_count < 0) {
+		err = -EIO;
+		goto out;
+	}
+
 	sgl_offset = 0;
 	ch_no = 0;
 
@@ -312,13 +426,16 @@
 next_sge:
 		ctxt = svc_rdma_get_context(xprt);
 		ctxt->direction = DMA_FROM_DEVICE;
+		ctxt->frmr = hdr_ctxt->frmr;
+		ctxt->read_hdr = NULL;
 		clear_bit(RDMACTXT_F_LAST_CTXT, &ctxt->flags);
+		clear_bit(RDMACTXT_F_FAST_UNREG, &ctxt->flags);
 
 		/* Prepare READ WR */
 		memset(&read_wr, 0, sizeof read_wr);
-		ctxt->wr_op = IB_WR_RDMA_READ;
 		read_wr.wr_id = (unsigned long)ctxt;
 		read_wr.opcode = IB_WR_RDMA_READ;
+		ctxt->wr_op = read_wr.opcode;
 		read_wr.send_flags = IB_SEND_SIGNALED;
 		read_wr.wr.rdma.rkey = ch->rc_target.rs_handle;
 		read_wr.wr.rdma.remote_addr =
@@ -327,10 +444,15 @@
 		read_wr.sg_list = ctxt->sge;
 		read_wr.num_sge =
 			rdma_read_max_sge(xprt, chl_map->ch[ch_no].count);
-		rdma_set_ctxt_sge(xprt, ctxt,
-				  &rpl_map->sge[chl_map->ch[ch_no].start],
-				  &sgl_offset,
-				  read_wr.num_sge);
+		err = rdma_set_ctxt_sge(xprt, ctxt, hdr_ctxt->frmr,
+					&rpl_map->sge[chl_map->ch[ch_no].start],
+					&sgl_offset,
+					read_wr.num_sge);
+		if (err) {
+			svc_rdma_unmap_dma(ctxt);
+			svc_rdma_put_context(ctxt, 0);
+			goto out;
+		}
 		if (((ch+1)->rc_discrim == 0) &&
 		    (read_wr.num_sge == chl_map->ch[ch_no].count)) {
 			/*
@@ -339,6 +461,29 @@
 			 * the client and the RPC needs to be enqueued.
 			 */
 			set_bit(RDMACTXT_F_LAST_CTXT, &ctxt->flags);
+			if (hdr_ctxt->frmr) {
+				set_bit(RDMACTXT_F_FAST_UNREG, &ctxt->flags);
+				/*
+				 * Invalidate the local MR used to map the data
+				 * sink.
+				 */
+				if (xprt->sc_dev_caps &
+				    SVCRDMA_DEVCAP_READ_W_INV) {
+					read_wr.opcode =
+						IB_WR_RDMA_READ_WITH_INV;
+					ctxt->wr_op = read_wr.opcode;
+					read_wr.ex.invalidate_rkey =
+						ctxt->frmr->mr->lkey;
+				} else {
+					/* Prepare INVALIDATE WR */
+					memset(&inv_wr, 0, sizeof inv_wr);
+					inv_wr.opcode = IB_WR_LOCAL_INV;
+					inv_wr.send_flags = IB_SEND_SIGNALED;
+					inv_wr.ex.invalidate_rkey =
+						hdr_ctxt->frmr->mr->lkey;
+					read_wr.next = &inv_wr;
+				}
+			}
 			ctxt->read_hdr = hdr_ctxt;
 		}
 		/* Post the read */
diff --git a/net/sunrpc/xprtrdma/svc_rdma_sendto.c b/net/sunrpc/xprtrdma/svc_rdma_sendto.c
index 84d3283..9a7a8e7 100644
--- a/net/sunrpc/xprtrdma/svc_rdma_sendto.c
+++ b/net/sunrpc/xprtrdma/svc_rdma_sendto.c
@@ -69,9 +69,127 @@
  * array is only concerned with the reply we are assured that we have
  * on extra page for the RPCRMDA header.
  */
-static void xdr_to_sge(struct svcxprt_rdma *xprt,
-		       struct xdr_buf *xdr,
-		       struct svc_rdma_req_map *vec)
+int fast_reg_xdr(struct svcxprt_rdma *xprt,
+		 struct xdr_buf *xdr,
+		 struct svc_rdma_req_map *vec)
+{
+	int sge_no;
+	u32 sge_bytes;
+	u32 page_bytes;
+	u32 page_off;
+	int page_no = 0;
+	u8 *frva;
+	struct svc_rdma_fastreg_mr *frmr;
+
+	frmr = svc_rdma_get_frmr(xprt);
+	if (IS_ERR(frmr))
+		return -ENOMEM;
+	vec->frmr = frmr;
+
+	/* Skip the RPCRDMA header */
+	sge_no = 1;
+
+	/* Map the head. */
+	frva = (void *)((unsigned long)(xdr->head[0].iov_base) & PAGE_MASK);
+	vec->sge[sge_no].iov_base = xdr->head[0].iov_base;
+	vec->sge[sge_no].iov_len = xdr->head[0].iov_len;
+	vec->count = 2;
+	sge_no++;
+
+	/* Build the FRMR */
+	frmr->kva = frva;
+	frmr->direction = DMA_TO_DEVICE;
+	frmr->access_flags = 0;
+	frmr->map_len = PAGE_SIZE;
+	frmr->page_list_len = 1;
+	frmr->page_list->page_list[page_no] =
+		ib_dma_map_single(xprt->sc_cm_id->device,
+				  (void *)xdr->head[0].iov_base,
+				  PAGE_SIZE, DMA_TO_DEVICE);
+	if (ib_dma_mapping_error(xprt->sc_cm_id->device,
+				 frmr->page_list->page_list[page_no]))
+		goto fatal_err;
+	atomic_inc(&xprt->sc_dma_used);
+
+	page_off = xdr->page_base;
+	page_bytes = xdr->page_len + page_off;
+	if (!page_bytes)
+		goto encode_tail;
+
+	/* Map the pages */
+	vec->sge[sge_no].iov_base = frva + frmr->map_len + page_off;
+	vec->sge[sge_no].iov_len = page_bytes;
+	sge_no++;
+	while (page_bytes) {
+		struct page *page;
+
+		page = xdr->pages[page_no++];
+		sge_bytes = min_t(u32, page_bytes, (PAGE_SIZE - page_off));
+		page_bytes -= sge_bytes;
+
+		frmr->page_list->page_list[page_no] =
+			ib_dma_map_page(xprt->sc_cm_id->device, page, 0,
+					  PAGE_SIZE, DMA_TO_DEVICE);
+		if (ib_dma_mapping_error(xprt->sc_cm_id->device,
+					 frmr->page_list->page_list[page_no]))
+			goto fatal_err;
+
+		atomic_inc(&xprt->sc_dma_used);
+		page_off = 0; /* reset for next time through loop */
+		frmr->map_len += PAGE_SIZE;
+		frmr->page_list_len++;
+	}
+	vec->count++;
+
+ encode_tail:
+	/* Map tail */
+	if (0 == xdr->tail[0].iov_len)
+		goto done;
+
+	vec->count++;
+	vec->sge[sge_no].iov_len = xdr->tail[0].iov_len;
+
+	if (((unsigned long)xdr->tail[0].iov_base & PAGE_MASK) ==
+	    ((unsigned long)xdr->head[0].iov_base & PAGE_MASK)) {
+		/*
+		 * If head and tail use the same page, we don't need
+		 * to map it again.
+		 */
+		vec->sge[sge_no].iov_base = xdr->tail[0].iov_base;
+	} else {
+		void *va;
+
+		/* Map another page for the tail */
+		page_off = (unsigned long)xdr->tail[0].iov_base & ~PAGE_MASK;
+		va = (void *)((unsigned long)xdr->tail[0].iov_base & PAGE_MASK);
+		vec->sge[sge_no].iov_base = frva + frmr->map_len + page_off;
+
+		frmr->page_list->page_list[page_no] =
+			ib_dma_map_single(xprt->sc_cm_id->device, va, PAGE_SIZE,
+					  DMA_TO_DEVICE);
+		if (ib_dma_mapping_error(xprt->sc_cm_id->device,
+					 frmr->page_list->page_list[page_no]))
+			goto fatal_err;
+		atomic_inc(&xprt->sc_dma_used);
+		frmr->map_len += PAGE_SIZE;
+		frmr->page_list_len++;
+	}
+
+ done:
+	if (svc_rdma_fastreg(xprt, frmr))
+		goto fatal_err;
+
+	return 0;
+
+ fatal_err:
+	printk("svcrdma: Error fast registering memory for xprt %p\n", xprt);
+	svc_rdma_put_frmr(xprt, frmr);
+	return -EIO;
+}
+
+static int map_xdr(struct svcxprt_rdma *xprt,
+		   struct xdr_buf *xdr,
+		   struct svc_rdma_req_map *vec)
 {
 	int sge_max = (xdr->len+PAGE_SIZE-1) / PAGE_SIZE + 3;
 	int sge_no;
@@ -83,6 +201,9 @@
 	BUG_ON(xdr->len !=
 	       (xdr->head[0].iov_len + xdr->page_len + xdr->tail[0].iov_len));
 
+	if (xprt->sc_frmr_pg_list_len)
+		return fast_reg_xdr(xprt, xdr, vec);
+
 	/* Skip the first sge, this is for the RPCRDMA header */
 	sge_no = 1;
 
@@ -116,9 +237,12 @@
 
 	BUG_ON(sge_no > sge_max);
 	vec->count = sge_no;
+	return 0;
 }
 
 /* Assumptions:
+ * - We are using FRMR
+ *     - or -
  * - The specified write_len can be represented in sc_max_sge * PAGE_SIZE
  */
 static int send_write(struct svcxprt_rdma *xprt, struct svc_rqst *rqstp,
@@ -158,30 +282,35 @@
 	sge_no = 0;
 
 	/* Copy the remaining SGE */
-	while (bc != 0 && xdr_sge_no < vec->count) {
-		sge[sge_no].lkey = xprt->sc_phys_mr->lkey;
-		sge_bytes = min((size_t)bc,
-				(size_t)(vec->sge[xdr_sge_no].iov_len-sge_off));
+	while (bc != 0) {
+		sge_bytes = min_t(size_t,
+			  bc, vec->sge[xdr_sge_no].iov_len-sge_off);
 		sge[sge_no].length = sge_bytes;
-		atomic_inc(&xprt->sc_dma_used);
-		sge[sge_no].addr =
-			ib_dma_map_single(xprt->sc_cm_id->device,
-					  (void *)
-					  vec->sge[xdr_sge_no].iov_base + sge_off,
-					  sge_bytes, DMA_TO_DEVICE);
-		if (dma_mapping_error(xprt->sc_cm_id->device->dma_device,
-					sge[sge_no].addr))
-			goto err;
+		if (!vec->frmr) {
+			sge[sge_no].addr =
+				ib_dma_map_single(xprt->sc_cm_id->device,
+						  (void *)
+						  vec->sge[xdr_sge_no].iov_base + sge_off,
+						  sge_bytes, DMA_TO_DEVICE);
+			if (ib_dma_mapping_error(xprt->sc_cm_id->device,
+						 sge[sge_no].addr))
+				goto err;
+			atomic_inc(&xprt->sc_dma_used);
+			sge[sge_no].lkey = xprt->sc_dma_lkey;
+		} else {
+			sge[sge_no].addr = (unsigned long)
+				vec->sge[xdr_sge_no].iov_base + sge_off;
+			sge[sge_no].lkey = vec->frmr->mr->lkey;
+		}
+		ctxt->count++;
+		ctxt->frmr = vec->frmr;
 		sge_off = 0;
 		sge_no++;
-		ctxt->count++;
 		xdr_sge_no++;
+		BUG_ON(xdr_sge_no > vec->count);
 		bc -= sge_bytes;
 	}
 
-	BUG_ON(bc != 0);
-	BUG_ON(xdr_sge_no > vec->count);
-
 	/* Prepare WRITE WR */
 	memset(&write_wr, 0, sizeof write_wr);
 	ctxt->wr_op = IB_WR_RDMA_WRITE;
@@ -226,7 +355,10 @@
 	res_ary = (struct rpcrdma_write_array *)
 		&rdma_resp->rm_body.rm_chunks[1];
 
-	max_write = xprt->sc_max_sge * PAGE_SIZE;
+	if (vec->frmr)
+		max_write = vec->frmr->map_len;
+	else
+		max_write = xprt->sc_max_sge * PAGE_SIZE;
 
 	/* Write chunks start at the pagelist */
 	for (xdr_off = rqstp->rq_res.head[0].iov_len, chunk_no = 0;
@@ -297,7 +429,10 @@
 	res_ary = (struct rpcrdma_write_array *)
 		&rdma_resp->rm_body.rm_chunks[2];
 
-	max_write = xprt->sc_max_sge * PAGE_SIZE;
+	if (vec->frmr)
+		max_write = vec->frmr->map_len;
+	else
+		max_write = xprt->sc_max_sge * PAGE_SIZE;
 
 	/* xdr offset starts at RPC message */
 	for (xdr_off = 0, chunk_no = 0;
@@ -307,7 +442,6 @@
 		ch = &arg_ary->wc_array[chunk_no].wc_target;
 		write_len = min(xfer_len, ch->rs_length);
 
-
 		/* Prepare the reply chunk given the length actually
 		 * written */
 		rs_offset = get_unaligned(&(ch->rs_offset));
@@ -366,6 +500,7 @@
 		      int byte_count)
 {
 	struct ib_send_wr send_wr;
+	struct ib_send_wr inv_wr;
 	int sge_no;
 	int sge_bytes;
 	int page_no;
@@ -385,27 +520,45 @@
 	/* Prepare the context */
 	ctxt->pages[0] = page;
 	ctxt->count = 1;
+	ctxt->frmr = vec->frmr;
+	if (vec->frmr)
+		set_bit(RDMACTXT_F_FAST_UNREG, &ctxt->flags);
+	else
+		clear_bit(RDMACTXT_F_FAST_UNREG, &ctxt->flags);
 
 	/* Prepare the SGE for the RPCRDMA Header */
-	atomic_inc(&rdma->sc_dma_used);
 	ctxt->sge[0].addr =
 		ib_dma_map_page(rdma->sc_cm_id->device,
 				page, 0, PAGE_SIZE, DMA_TO_DEVICE);
+	if (ib_dma_mapping_error(rdma->sc_cm_id->device, ctxt->sge[0].addr))
+		goto err;
+	atomic_inc(&rdma->sc_dma_used);
+
 	ctxt->direction = DMA_TO_DEVICE;
+
 	ctxt->sge[0].length = svc_rdma_xdr_get_reply_hdr_len(rdma_resp);
-	ctxt->sge[0].lkey = rdma->sc_phys_mr->lkey;
+	ctxt->sge[0].lkey = rdma->sc_dma_lkey;
 
 	/* Determine how many of our SGE are to be transmitted */
 	for (sge_no = 1; byte_count && sge_no < vec->count; sge_no++) {
 		sge_bytes = min_t(size_t, vec->sge[sge_no].iov_len, byte_count);
 		byte_count -= sge_bytes;
-		atomic_inc(&rdma->sc_dma_used);
-		ctxt->sge[sge_no].addr =
-			ib_dma_map_single(rdma->sc_cm_id->device,
-					  vec->sge[sge_no].iov_base,
-					  sge_bytes, DMA_TO_DEVICE);
+		if (!vec->frmr) {
+			ctxt->sge[sge_no].addr =
+				ib_dma_map_single(rdma->sc_cm_id->device,
+						  vec->sge[sge_no].iov_base,
+						  sge_bytes, DMA_TO_DEVICE);
+			if (ib_dma_mapping_error(rdma->sc_cm_id->device,
+						 ctxt->sge[sge_no].addr))
+				goto err;
+			atomic_inc(&rdma->sc_dma_used);
+			ctxt->sge[sge_no].lkey = rdma->sc_dma_lkey;
+		} else {
+			ctxt->sge[sge_no].addr = (unsigned long)
+				vec->sge[sge_no].iov_base;
+			ctxt->sge[sge_no].lkey = vec->frmr->mr->lkey;
+		}
 		ctxt->sge[sge_no].length = sge_bytes;
-		ctxt->sge[sge_no].lkey = rdma->sc_phys_mr->lkey;
 	}
 	BUG_ON(byte_count != 0);
 
@@ -417,11 +570,16 @@
 		ctxt->pages[page_no+1] = rqstp->rq_respages[page_no];
 		ctxt->count++;
 		rqstp->rq_respages[page_no] = NULL;
-		/* If there are more pages than SGE, terminate SGE list */
+		/*
+		 * If there are more pages than SGE, terminate SGE
+		 * list so that svc_rdma_unmap_dma doesn't attempt to
+		 * unmap garbage.
+		 */
 		if (page_no+1 >= sge_no)
 			ctxt->sge[page_no+1].length = 0;
 	}
 	BUG_ON(sge_no > rdma->sc_max_sge);
+	BUG_ON(sge_no > ctxt->count);
 	memset(&send_wr, 0, sizeof send_wr);
 	ctxt->wr_op = IB_WR_SEND;
 	send_wr.wr_id = (unsigned long)ctxt;
@@ -429,12 +587,26 @@
 	send_wr.num_sge = sge_no;
 	send_wr.opcode = IB_WR_SEND;
 	send_wr.send_flags =  IB_SEND_SIGNALED;
+	if (vec->frmr) {
+		/* Prepare INVALIDATE WR */
+		memset(&inv_wr, 0, sizeof inv_wr);
+		inv_wr.opcode = IB_WR_LOCAL_INV;
+		inv_wr.send_flags = IB_SEND_SIGNALED;
+		inv_wr.ex.invalidate_rkey =
+			vec->frmr->mr->lkey;
+		send_wr.next = &inv_wr;
+	}
 
 	ret = svc_rdma_send(rdma, &send_wr);
 	if (ret)
-		svc_rdma_put_context(ctxt, 1);
+		goto err;
 
-	return ret;
+	return 0;
+
+ err:
+	svc_rdma_put_frmr(rdma, vec->frmr);
+	svc_rdma_put_context(ctxt, 1);
+	return -EIO;
 }
 
 void svc_rdma_prep_reply_hdr(struct svc_rqst *rqstp)
@@ -477,8 +649,9 @@
 	ctxt = svc_rdma_get_context(rdma);
 	ctxt->direction = DMA_TO_DEVICE;
 	vec = svc_rdma_get_req_map();
-	xdr_to_sge(rdma, &rqstp->rq_res, vec);
-
+	ret = map_xdr(rdma, &rqstp->rq_res, vec);
+	if (ret)
+		goto err0;
 	inline_bytes = rqstp->rq_res.len;
 
 	/* Create the RDMA response header */
@@ -498,7 +671,7 @@
 	if (ret < 0) {
 		printk(KERN_ERR "svcrdma: failed to send write chunks, rc=%d\n",
 		       ret);
-		goto error;
+		goto err1;
 	}
 	inline_bytes -= ret;
 
@@ -508,7 +681,7 @@
 	if (ret < 0) {
 		printk(KERN_ERR "svcrdma: failed to send reply chunks, rc=%d\n",
 		       ret);
-		goto error;
+		goto err1;
 	}
 	inline_bytes -= ret;
 
@@ -517,9 +690,11 @@
 	svc_rdma_put_req_map(vec);
 	dprintk("svcrdma: send_reply returns %d\n", ret);
 	return ret;
- error:
+
+ err1:
+	put_page(res_page);
+ err0:
 	svc_rdma_put_req_map(vec);
 	svc_rdma_put_context(ctxt, 0);
-	put_page(res_page);
 	return ret;
 }
diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c
index 900cb69..6fb493c 100644
--- a/net/sunrpc/xprtrdma/svc_rdma_transport.c
+++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c
@@ -100,20 +100,29 @@
 	ctxt->xprt = xprt;
 	INIT_LIST_HEAD(&ctxt->dto_q);
 	ctxt->count = 0;
+	ctxt->frmr = NULL;
 	atomic_inc(&xprt->sc_ctxt_used);
 	return ctxt;
 }
 
-static void svc_rdma_unmap_dma(struct svc_rdma_op_ctxt *ctxt)
+void svc_rdma_unmap_dma(struct svc_rdma_op_ctxt *ctxt)
 {
 	struct svcxprt_rdma *xprt = ctxt->xprt;
 	int i;
 	for (i = 0; i < ctxt->count && ctxt->sge[i].length; i++) {
-		atomic_dec(&xprt->sc_dma_used);
-		ib_dma_unmap_single(xprt->sc_cm_id->device,
-				    ctxt->sge[i].addr,
-				    ctxt->sge[i].length,
-				    ctxt->direction);
+		/*
+		 * Unmap the DMA addr in the SGE if the lkey matches
+		 * the sc_dma_lkey, otherwise, ignore it since it is
+		 * an FRMR lkey and will be unmapped later when the
+		 * last WR that uses it completes.
+		 */
+		if (ctxt->sge[i].lkey == xprt->sc_dma_lkey) {
+			atomic_dec(&xprt->sc_dma_used);
+			ib_dma_unmap_single(xprt->sc_cm_id->device,
+					    ctxt->sge[i].addr,
+					    ctxt->sge[i].length,
+					    ctxt->direction);
+		}
 	}
 }
 
@@ -150,6 +159,7 @@
 		schedule_timeout_uninterruptible(msecs_to_jiffies(500));
 	}
 	map->count = 0;
+	map->frmr = NULL;
 	return map;
 }
 
@@ -316,6 +326,50 @@
 }
 
 /*
+ * Processs a completion context
+ */
+static void process_context(struct svcxprt_rdma *xprt,
+			    struct svc_rdma_op_ctxt *ctxt)
+{
+	svc_rdma_unmap_dma(ctxt);
+
+	switch (ctxt->wr_op) {
+	case IB_WR_SEND:
+		if (test_bit(RDMACTXT_F_FAST_UNREG, &ctxt->flags))
+			svc_rdma_put_frmr(xprt, ctxt->frmr);
+		svc_rdma_put_context(ctxt, 1);
+		break;
+
+	case IB_WR_RDMA_WRITE:
+		svc_rdma_put_context(ctxt, 0);
+		break;
+
+	case IB_WR_RDMA_READ:
+	case IB_WR_RDMA_READ_WITH_INV:
+		if (test_bit(RDMACTXT_F_LAST_CTXT, &ctxt->flags)) {
+			struct svc_rdma_op_ctxt *read_hdr = ctxt->read_hdr;
+			BUG_ON(!read_hdr);
+			if (test_bit(RDMACTXT_F_FAST_UNREG, &ctxt->flags))
+				svc_rdma_put_frmr(xprt, ctxt->frmr);
+			spin_lock_bh(&xprt->sc_rq_dto_lock);
+			set_bit(XPT_DATA, &xprt->sc_xprt.xpt_flags);
+			list_add_tail(&read_hdr->dto_q,
+				      &xprt->sc_read_complete_q);
+			spin_unlock_bh(&xprt->sc_rq_dto_lock);
+			svc_xprt_enqueue(&xprt->sc_xprt);
+		}
+		svc_rdma_put_context(ctxt, 0);
+		break;
+
+	default:
+		printk(KERN_ERR "svcrdma: unexpected completion type, "
+		       "opcode=%d\n",
+		       ctxt->wr_op);
+		break;
+	}
+}
+
+/*
  * Send Queue Completion Handler - potentially called on interrupt context.
  *
  * Note that caller must hold a transport reference.
@@ -327,17 +381,12 @@
 	struct ib_cq *cq = xprt->sc_sq_cq;
 	int ret;
 
-
 	if (!test_and_clear_bit(RDMAXPRT_SQ_PENDING, &xprt->sc_flags))
 		return;
 
 	ib_req_notify_cq(xprt->sc_sq_cq, IB_CQ_NEXT_COMP);
 	atomic_inc(&rdma_stat_sq_poll);
 	while ((ret = ib_poll_cq(cq, 1, &wc)) > 0) {
-		ctxt = (struct svc_rdma_op_ctxt *)(unsigned long)wc.wr_id;
-		xprt = ctxt->xprt;
-
-		svc_rdma_unmap_dma(ctxt);
 		if (wc.status != IB_WC_SUCCESS)
 			/* Close the transport */
 			set_bit(XPT_CLOSE, &xprt->sc_xprt.xpt_flags);
@@ -346,35 +395,10 @@
 		atomic_dec(&xprt->sc_sq_count);
 		wake_up(&xprt->sc_send_wait);
 
-		switch (ctxt->wr_op) {
-		case IB_WR_SEND:
-			svc_rdma_put_context(ctxt, 1);
-			break;
+		ctxt = (struct svc_rdma_op_ctxt *)(unsigned long)wc.wr_id;
+		if (ctxt)
+			process_context(xprt, ctxt);
 
-		case IB_WR_RDMA_WRITE:
-			svc_rdma_put_context(ctxt, 0);
-			break;
-
-		case IB_WR_RDMA_READ:
-			if (test_bit(RDMACTXT_F_LAST_CTXT, &ctxt->flags)) {
-				struct svc_rdma_op_ctxt *read_hdr = ctxt->read_hdr;
-				BUG_ON(!read_hdr);
-				spin_lock_bh(&xprt->sc_rq_dto_lock);
-				set_bit(XPT_DATA, &xprt->sc_xprt.xpt_flags);
-				list_add_tail(&read_hdr->dto_q,
-					      &xprt->sc_read_complete_q);
-				spin_unlock_bh(&xprt->sc_rq_dto_lock);
-				svc_xprt_enqueue(&xprt->sc_xprt);
-			}
-			svc_rdma_put_context(ctxt, 0);
-			break;
-
-		default:
-			printk(KERN_ERR "svcrdma: unexpected completion type, "
-			       "opcode=%d, status=%d\n",
-			       wc.opcode, wc.status);
-			break;
-		}
 		svc_xprt_put(&xprt->sc_xprt);
 	}
 
@@ -425,10 +449,12 @@
 	INIT_LIST_HEAD(&cma_xprt->sc_dto_q);
 	INIT_LIST_HEAD(&cma_xprt->sc_rq_dto_q);
 	INIT_LIST_HEAD(&cma_xprt->sc_read_complete_q);
+	INIT_LIST_HEAD(&cma_xprt->sc_frmr_q);
 	init_waitqueue_head(&cma_xprt->sc_send_wait);
 
 	spin_lock_init(&cma_xprt->sc_lock);
 	spin_lock_init(&cma_xprt->sc_rq_dto_lock);
+	spin_lock_init(&cma_xprt->sc_frmr_q_lock);
 
 	cma_xprt->sc_ord = svcrdma_ord;
 
@@ -462,7 +488,7 @@
 	struct ib_recv_wr recv_wr, *bad_recv_wr;
 	struct svc_rdma_op_ctxt *ctxt;
 	struct page *page;
-	unsigned long pa;
+	dma_addr_t pa;
 	int sge_no;
 	int buflen;
 	int ret;
@@ -474,13 +500,15 @@
 		BUG_ON(sge_no >= xprt->sc_max_sge);
 		page = svc_rdma_get_page();
 		ctxt->pages[sge_no] = page;
-		atomic_inc(&xprt->sc_dma_used);
 		pa = ib_dma_map_page(xprt->sc_cm_id->device,
 				     page, 0, PAGE_SIZE,
 				     DMA_FROM_DEVICE);
+		if (ib_dma_mapping_error(xprt->sc_cm_id->device, pa))
+			goto err_put_ctxt;
+		atomic_inc(&xprt->sc_dma_used);
 		ctxt->sge[sge_no].addr = pa;
 		ctxt->sge[sge_no].length = PAGE_SIZE;
-		ctxt->sge[sge_no].lkey = xprt->sc_phys_mr->lkey;
+		ctxt->sge[sge_no].lkey = xprt->sc_dma_lkey;
 		buflen += PAGE_SIZE;
 	}
 	ctxt->count = sge_no;
@@ -496,6 +524,10 @@
 		svc_rdma_put_context(ctxt, 1);
 	}
 	return ret;
+
+ err_put_ctxt:
+	svc_rdma_put_context(ctxt, 1);
+	return -ENOMEM;
 }
 
 /*
@@ -566,7 +598,7 @@
 		dprintk("svcrdma: Connect request on cma_id=%p, xprt = %p, "
 			"event=%d\n", cma_id, cma_id->context, event->event);
 		handle_connect_req(cma_id,
-				   event->param.conn.responder_resources);
+				   event->param.conn.initiator_depth);
 		break;
 
 	case RDMA_CM_EVENT_ESTABLISHED:
@@ -686,6 +718,97 @@
 	return ERR_PTR(ret);
 }
 
+static struct svc_rdma_fastreg_mr *rdma_alloc_frmr(struct svcxprt_rdma *xprt)
+{
+	struct ib_mr *mr;
+	struct ib_fast_reg_page_list *pl;
+	struct svc_rdma_fastreg_mr *frmr;
+
+	frmr = kmalloc(sizeof(*frmr), GFP_KERNEL);
+	if (!frmr)
+		goto err;
+
+	mr = ib_alloc_fast_reg_mr(xprt->sc_pd, RPCSVC_MAXPAGES);
+	if (!mr)
+		goto err_free_frmr;
+
+	pl = ib_alloc_fast_reg_page_list(xprt->sc_cm_id->device,
+					 RPCSVC_MAXPAGES);
+	if (!pl)
+		goto err_free_mr;
+
+	frmr->mr = mr;
+	frmr->page_list = pl;
+	INIT_LIST_HEAD(&frmr->frmr_list);
+	return frmr;
+
+ err_free_mr:
+	ib_dereg_mr(mr);
+ err_free_frmr:
+	kfree(frmr);
+ err:
+	return ERR_PTR(-ENOMEM);
+}
+
+static void rdma_dealloc_frmr_q(struct svcxprt_rdma *xprt)
+{
+	struct svc_rdma_fastreg_mr *frmr;
+
+	while (!list_empty(&xprt->sc_frmr_q)) {
+		frmr = list_entry(xprt->sc_frmr_q.next,
+				  struct svc_rdma_fastreg_mr, frmr_list);
+		list_del_init(&frmr->frmr_list);
+		ib_dereg_mr(frmr->mr);
+		ib_free_fast_reg_page_list(frmr->page_list);
+		kfree(frmr);
+	}
+}
+
+struct svc_rdma_fastreg_mr *svc_rdma_get_frmr(struct svcxprt_rdma *rdma)
+{
+	struct svc_rdma_fastreg_mr *frmr = NULL;
+
+	spin_lock_bh(&rdma->sc_frmr_q_lock);
+	if (!list_empty(&rdma->sc_frmr_q)) {
+		frmr = list_entry(rdma->sc_frmr_q.next,
+				  struct svc_rdma_fastreg_mr, frmr_list);
+		list_del_init(&frmr->frmr_list);
+		frmr->map_len = 0;
+		frmr->page_list_len = 0;
+	}
+	spin_unlock_bh(&rdma->sc_frmr_q_lock);
+	if (frmr)
+		return frmr;
+
+	return rdma_alloc_frmr(rdma);
+}
+
+static void frmr_unmap_dma(struct svcxprt_rdma *xprt,
+			   struct svc_rdma_fastreg_mr *frmr)
+{
+	int page_no;
+	for (page_no = 0; page_no < frmr->page_list_len; page_no++) {
+		dma_addr_t addr = frmr->page_list->page_list[page_no];
+		if (ib_dma_mapping_error(frmr->mr->device, addr))
+			continue;
+		atomic_dec(&xprt->sc_dma_used);
+		ib_dma_unmap_single(frmr->mr->device, addr, PAGE_SIZE,
+				    frmr->direction);
+	}
+}
+
+void svc_rdma_put_frmr(struct svcxprt_rdma *rdma,
+		       struct svc_rdma_fastreg_mr *frmr)
+{
+	if (frmr) {
+		frmr_unmap_dma(rdma, frmr);
+		spin_lock_bh(&rdma->sc_frmr_q_lock);
+		BUG_ON(!list_empty(&frmr->frmr_list));
+		list_add(&frmr->frmr_list, &rdma->sc_frmr_q);
+		spin_unlock_bh(&rdma->sc_frmr_q_lock);
+	}
+}
+
 /*
  * This is the xpo_recvfrom function for listening endpoints. Its
  * purpose is to accept incoming connections. The CMA callback handler
@@ -704,6 +827,8 @@
 	struct rdma_conn_param conn_param;
 	struct ib_qp_init_attr qp_attr;
 	struct ib_device_attr devattr;
+	int dma_mr_acc;
+	int need_dma_mr;
 	int ret;
 	int i;
 
@@ -819,15 +944,77 @@
 	}
 	newxprt->sc_qp = newxprt->sc_cm_id->qp;
 
-	/* Register all of physical memory */
-	newxprt->sc_phys_mr = ib_get_dma_mr(newxprt->sc_pd,
-					    IB_ACCESS_LOCAL_WRITE |
-					    IB_ACCESS_REMOTE_WRITE);
-	if (IS_ERR(newxprt->sc_phys_mr)) {
-		dprintk("svcrdma: Failed to create DMA MR ret=%d\n", ret);
+	/*
+	 * Use the most secure set of MR resources based on the
+	 * transport type and available memory management features in
+	 * the device. Here's the table implemented below:
+	 *
+	 *		Fast	Global	DMA	Remote WR
+	 *		Reg	LKEY	MR	Access
+	 *		Sup'd	Sup'd	Needed	Needed
+	 *
+	 * IWARP	N	N	Y	Y
+	 *		N	Y	Y	Y
+	 *		Y	N	Y	N
+	 *		Y	Y	N	-
+	 *
+	 * IB		N	N	Y	N
+	 *		N	Y	N	-
+	 *		Y	N	Y	N
+	 *		Y	Y	N	-
+	 *
+	 * NB:	iWARP requires remote write access for the data sink
+	 *	of an RDMA_READ. IB does not.
+	 */
+	if (devattr.device_cap_flags & IB_DEVICE_MEM_MGT_EXTENSIONS) {
+		newxprt->sc_frmr_pg_list_len =
+			devattr.max_fast_reg_page_list_len;
+		newxprt->sc_dev_caps |= SVCRDMA_DEVCAP_FAST_REG;
+	}
+
+	/*
+	 * Determine if a DMA MR is required and if so, what privs are required
+	 */
+	switch (rdma_node_get_transport(newxprt->sc_cm_id->device->node_type)) {
+	case RDMA_TRANSPORT_IWARP:
+		newxprt->sc_dev_caps |= SVCRDMA_DEVCAP_READ_W_INV;
+		if (!(newxprt->sc_dev_caps & SVCRDMA_DEVCAP_FAST_REG)) {
+			need_dma_mr = 1;
+			dma_mr_acc =
+				(IB_ACCESS_LOCAL_WRITE |
+				 IB_ACCESS_REMOTE_WRITE);
+		} else if (!(devattr.device_cap_flags & IB_DEVICE_LOCAL_DMA_LKEY)) {
+			need_dma_mr = 1;
+			dma_mr_acc = IB_ACCESS_LOCAL_WRITE;
+		} else
+			need_dma_mr = 0;
+		break;
+	case RDMA_TRANSPORT_IB:
+		if (!(devattr.device_cap_flags & IB_DEVICE_LOCAL_DMA_LKEY)) {
+			need_dma_mr = 1;
+			dma_mr_acc = IB_ACCESS_LOCAL_WRITE;
+		} else
+			need_dma_mr = 0;
+		break;
+	default:
 		goto errout;
 	}
 
+	/* Create the DMA MR if needed, otherwise, use the DMA LKEY */
+	if (need_dma_mr) {
+		/* Register all of physical memory */
+		newxprt->sc_phys_mr =
+			ib_get_dma_mr(newxprt->sc_pd, dma_mr_acc);
+		if (IS_ERR(newxprt->sc_phys_mr)) {
+			dprintk("svcrdma: Failed to create DMA MR ret=%d\n",
+				ret);
+			goto errout;
+		}
+		newxprt->sc_dma_lkey = newxprt->sc_phys_mr->lkey;
+	} else
+		newxprt->sc_dma_lkey =
+			newxprt->sc_cm_id->device->local_dma_lkey;
+
 	/* Post receive buffers */
 	for (i = 0; i < newxprt->sc_max_requests; i++) {
 		ret = svc_rdma_post_recv(newxprt);
@@ -961,6 +1148,9 @@
 	WARN_ON(atomic_read(&rdma->sc_ctxt_used) != 0);
 	WARN_ON(atomic_read(&rdma->sc_dma_used) != 0);
 
+	/* De-allocate fastreg mr */
+	rdma_dealloc_frmr_q(rdma);
+
 	/* Destroy the QP if present (not a listener) */
 	if (rdma->sc_qp && !IS_ERR(rdma->sc_qp))
 		ib_destroy_qp(rdma->sc_qp);
@@ -1014,21 +1204,59 @@
 	return 1;
 }
 
+/*
+ * Attempt to register the kvec representing the RPC memory with the
+ * device.
+ *
+ * Returns:
+ *  NULL : The device does not support fastreg or there were no more
+ *         fastreg mr.
+ *  frmr : The kvec register request was successfully posted.
+ *    <0 : An error was encountered attempting to register the kvec.
+ */
+int svc_rdma_fastreg(struct svcxprt_rdma *xprt,
+		     struct svc_rdma_fastreg_mr *frmr)
+{
+	struct ib_send_wr fastreg_wr;
+	u8 key;
+
+	/* Bump the key */
+	key = (u8)(frmr->mr->lkey & 0x000000FF);
+	ib_update_fast_reg_key(frmr->mr, ++key);
+
+	/* Prepare FASTREG WR */
+	memset(&fastreg_wr, 0, sizeof fastreg_wr);
+	fastreg_wr.opcode = IB_WR_FAST_REG_MR;
+	fastreg_wr.send_flags = IB_SEND_SIGNALED;
+	fastreg_wr.wr.fast_reg.iova_start = (unsigned long)frmr->kva;
+	fastreg_wr.wr.fast_reg.page_list = frmr->page_list;
+	fastreg_wr.wr.fast_reg.page_list_len = frmr->page_list_len;
+	fastreg_wr.wr.fast_reg.page_shift = PAGE_SHIFT;
+	fastreg_wr.wr.fast_reg.length = frmr->map_len;
+	fastreg_wr.wr.fast_reg.access_flags = frmr->access_flags;
+	fastreg_wr.wr.fast_reg.rkey = frmr->mr->lkey;
+	return svc_rdma_send(xprt, &fastreg_wr);
+}
+
 int svc_rdma_send(struct svcxprt_rdma *xprt, struct ib_send_wr *wr)
 {
-	struct ib_send_wr *bad_wr;
+	struct ib_send_wr *bad_wr, *n_wr;
+	int wr_count;
+	int i;
 	int ret;
 
 	if (test_bit(XPT_CLOSE, &xprt->sc_xprt.xpt_flags))
 		return -ENOTCONN;
 
 	BUG_ON(wr->send_flags != IB_SEND_SIGNALED);
-	BUG_ON(((struct svc_rdma_op_ctxt *)(unsigned long)wr->wr_id)->wr_op !=
-		wr->opcode);
+	wr_count = 1;
+	for (n_wr = wr->next; n_wr; n_wr = n_wr->next)
+		wr_count++;
+
 	/* If the SQ is full, wait until an SQ entry is available */
 	while (1) {
 		spin_lock_bh(&xprt->sc_lock);
-		if (xprt->sc_sq_depth == atomic_read(&xprt->sc_sq_count)) {
+		if (xprt->sc_sq_depth < atomic_read(&xprt->sc_sq_count) + wr_count) {
 			spin_unlock_bh(&xprt->sc_lock);
 			atomic_inc(&rdma_stat_sq_starve);
 
@@ -1043,19 +1271,26 @@
 				return 0;
 			continue;
 		}
-		/* Bumped used SQ WR count and post */
-		svc_xprt_get(&xprt->sc_xprt);
+		/* Take a transport ref for each WR posted */
+		for (i = 0; i < wr_count; i++)
+			svc_xprt_get(&xprt->sc_xprt);
+
+		/* Bump used SQ WR count and post */
+		atomic_add(wr_count, &xprt->sc_sq_count);
 		ret = ib_post_send(xprt->sc_qp, wr, &bad_wr);
-		if (!ret)
-			atomic_inc(&xprt->sc_sq_count);
-		else {
-			svc_xprt_put(&xprt->sc_xprt);
+		if (ret) {
+			set_bit(XPT_CLOSE, &xprt->sc_xprt.xpt_flags);
+			atomic_sub(wr_count, &xprt->sc_sq_count);
+			for (i = 0; i < wr_count; i ++)
+				svc_xprt_put(&xprt->sc_xprt);
 			dprintk("svcrdma: failed to post SQ WR rc=%d, "
 			       "sc_sq_count=%d, sc_sq_depth=%d\n",
 			       ret, atomic_read(&xprt->sc_sq_count),
 			       xprt->sc_sq_depth);
 		}
 		spin_unlock_bh(&xprt->sc_lock);
+		if (ret)
+			wake_up(&xprt->sc_send_wait);
 		break;
 	}
 	return ret;
@@ -1079,10 +1314,14 @@
 	length = svc_rdma_xdr_encode_error(xprt, rmsgp, err, va);
 
 	/* Prepare SGE for local address */
-	atomic_inc(&xprt->sc_dma_used);
 	sge.addr = ib_dma_map_page(xprt->sc_cm_id->device,
 				   p, 0, PAGE_SIZE, DMA_FROM_DEVICE);
-	sge.lkey = xprt->sc_phys_mr->lkey;
+	if (ib_dma_mapping_error(xprt->sc_cm_id->device, sge.addr)) {
+		put_page(p);
+		return;
+	}
+	atomic_inc(&xprt->sc_dma_used);
+	sge.lkey = xprt->sc_dma_lkey;
 	sge.length = length;
 
 	ctxt = svc_rdma_get_context(xprt);
@@ -1103,6 +1342,9 @@
 	if (ret) {
 		dprintk("svcrdma: Error %d posting send for protocol error\n",
 			ret);
+		ib_dma_unmap_page(xprt->sc_cm_id->device,
+				  sge.addr, PAGE_SIZE,
+				  DMA_FROM_DEVICE);
 		svc_rdma_put_context(ctxt, 1);
 	}
 }
diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c
index 4486c59..9a288d5 100644
--- a/net/sunrpc/xprtsock.c
+++ b/net/sunrpc/xprtsock.c
@@ -3,8 +3,8 @@
  *
  * Client-side transport implementation for sockets.
  *
- * TCP callback races fixes (C) 1998 Red Hat Software <alan@redhat.com>
- * TCP send fixes (C) 1998 Red Hat Software <alan@redhat.com>
+ * TCP callback races fixes (C) 1998 Red Hat
+ * TCP send fixes (C) 1998 Red Hat
  * TCP NFS related read + write fixes
  *  (C) 1999 Dave Airlie, University of Limerick, Ireland <airlied@linux.ie>
  *
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index 015606b..c647aab 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -1,7 +1,7 @@
 /*
  * NET4:	Implementation of BSD Unix domain sockets.
  *
- * Authors:	Alan Cox, <alan.cox@linux.org>
+ * Authors:	Alan Cox, <alan@lxorguk.ukuu.org.uk>
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
diff --git a/scripts/bootgraph.pl b/scripts/bootgraph.pl
new file mode 100644
index 0000000..2243353
--- /dev/null
+++ b/scripts/bootgraph.pl
@@ -0,0 +1,147 @@
+#!/usr/bin/perl
+
+# Copyright 2008, Intel Corporation
+#
+# This file is part of the Linux kernel
+#
+# This program file 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 in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
+#
+# Authors:
+# 	Arjan van de Ven <arjan@linux.intel.com>
+
+
+#
+# This script turns a dmesg output into a SVG graphic that shows which
+# functions take how much time. You can view SVG graphics with various
+# programs, including Inkscape, The Gimp and Firefox.
+#
+#
+# For this script to work, the kernel needs to be compiled with the
+# CONFIG_PRINTK_TIME configuration option enabled, and with
+# "initcall_debug" passed on the kernel command line.
+#
+# usage:
+# 	dmesg | perl scripts/bootgraph.pl > output.svg
+#
+
+my @rows;
+my %start, %end, %row;
+my $done = 0;
+my $rowcount = 0;
+my $maxtime = 0;
+my $firsttime = 100;
+my $count = 0;
+while (<>) {
+	my $line = $_;
+	if ($line =~ /([0-9\.]+)\] calling  ([a-zA-Z0-9\_]+)\+/) {
+		my $func = $2;
+		if ($done == 0) {
+			$start{$func} = $1;
+			if ($1 < $firsttime) {
+				$firsttime = $1;
+			}
+		}
+		$row{$func} = 1;
+		if ($line =~ /\@ ([0-9]+)/) {
+			my $pid = $1;
+			if (!defined($rows[$pid])) {
+				$rowcount = $rowcount + 1;
+				$rows[$pid] = $rowcount;
+			}
+			$row{$func} = $rows[$pid];
+		}
+		$count = $count + 1;
+	}
+
+	if ($line =~ /([0-9\.]+)\] initcall ([a-zA-Z0-9\_]+)\+.*returned/) {
+		if ($done == 0) {
+			$end{$2} = $1;
+			$maxtime = $1;
+		}
+	}
+	if ($line =~ /Write protecting the/) {
+		$done = 1;
+	}
+	if ($line =~ /Freeing unused kernel memory/) {
+		$done = 1;
+	}
+}
+
+if ($count == 0) {
+	print "No data found in the dmesg. Make sure that 'printk.time=1' and\n";
+	print "'initcall_debug' are passed on the kernel command line.\n\n";
+	print "Usage: \n";
+	print "      dmesg | perl scripts/bootgraph.pl > output.svg\n\n";
+	exit;
+}
+
+print "<?xml version=\"1.0\" standalone=\"no\"?> \n";
+print "<svg width=\"1000\" height=\"100%\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">\n";
+
+my @styles;
+
+$styles[0] = "fill:rgb(0,0,255);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)";
+$styles[1] = "fill:rgb(0,255,0);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)";
+$styles[2] = "fill:rgb(255,0,20);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)";
+$styles[3] = "fill:rgb(255,255,20);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)";
+$styles[4] = "fill:rgb(255,0,255);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)";
+$styles[5] = "fill:rgb(0,255,255);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)";
+$styles[6] = "fill:rgb(0,128,255);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)";
+$styles[7] = "fill:rgb(0,255,128);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)";
+$styles[8] = "fill:rgb(255,0,128);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)";
+$styles[9] = "fill:rgb(255,255,128);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)";
+$styles[10] = "fill:rgb(255,128,255);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)";
+$styles[11] = "fill:rgb(128,255,255);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)";
+
+my $mult = 950.0 / ($maxtime - $firsttime);
+my $threshold = ($maxtime - $firsttime) / 60.0;
+my $stylecounter = 0;
+while (($key,$value) = each %start) {
+	my $duration = $end{$key} - $start{$key};
+
+	if ($duration >= $threshold) {
+		my $s, $s2, $e, $y;
+		$s = ($value - $firsttime) * $mult;
+		$s2 = $s + 6;
+		$e = ($end{$key} - $firsttime) * $mult;
+		$w = $e - $s;
+
+		$y = $row{$key} * 150;
+		$y2 = $y + 4;
+
+		$style = $styles[$stylecounter];
+		$stylecounter = $stylecounter + 1;
+		if ($stylecounter > 11) {
+			$stylecounter = 0;
+		};
+
+		print "<rect x=\"$s\" width=\"$w\" y=\"$y\" height=\"145\" style=\"$style\"/>\n";
+		print "<text transform=\"translate($s2,$y2) rotate(90)\">$key</text>\n";
+	}
+}
+
+
+# print the time line on top
+my $time = $firsttime;
+my $step = ($maxtime - $firsttime) / 15;
+while ($time < $maxtime) {
+	my $s2 = ($time - $firsttime) * $mult;
+	my $tm = int($time * 100) / 100.0;
+	print "<text transform=\"translate($s2,89) rotate(90)\">$tm</text>\n";
+	$time = $time + $step;
+}
+
+print "</svg>\n";
diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c
index 473f94e..d4dc222 100644
--- a/scripts/mod/file2alias.c
+++ b/scripts/mod/file2alias.c
@@ -206,6 +206,20 @@
 		do_usb_entry_multi(symval + i, mod);
 }
 
+/* Looks like: hid:bNvNpN */
+static int do_hid_entry(const char *filename,
+			     struct hid_device_id *id, char *alias)
+{
+	id->vendor = TO_NATIVE(id->vendor);
+	id->product = TO_NATIVE(id->product);
+
+	sprintf(alias, "hid:b%04X", id->bus);
+	ADD(alias, "v", id->vendor != HID_ANY_ID, id->vendor);
+	ADD(alias, "p", id->product != HID_ANY_ID, id->product);
+
+	return 1;
+}
+
 /* Looks like: ieee1394:venNmoNspNverN */
 static int do_ieee1394_entry(const char *filename,
 			     struct ieee1394_device_id *id, char *alias)
@@ -745,6 +759,10 @@
 	else if (sym_is(symname, "__mod_usb_device_table"))
 		/* special case to handle bcdDevice ranges */
 		do_usb_table(symval, sym->st_size, mod);
+	else if (sym_is(symname, "__mod_hid_device_table"))
+		do_table(symval, sym->st_size,
+			 sizeof(struct hid_device_id), "hid",
+			 do_hid_entry, mod);
 	else if (sym_is(symname, "__mod_ieee1394_device_table"))
 		do_table(symval, sym->st_size,
 			 sizeof(struct ieee1394_device_id), "ieee1394",