Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/lrg/voltage-2.6
Linus Torvalds [Tue, 14 Oct 2008 19:28:55 +0000 (12:28 -0700)]
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/lrg/voltage-2.6: (26 commits)
  mfd: Fix warning in WM8350
  mfd: Add placeholders for WM8350 client devices
  da903x: add regulator support for DA9030/DA9034
  mfd: Add WM8350 subdevice registration helper
  regulator: Add WM8350 regulator support
  mfd: Add WM8350 interrupt support
  mfd: Add initialisation callback for WM8350
  mfd: Add GPIO pin configuration support for WM8350
  mfd: Add I2C control support for WM8350
  mfd: Core support for the WM8350 AudioPlus PMIC
  mfd: Add WM8350 watchdog register definitions
  mfd: Add WM8350 RTC register definitions
  mfd: Add WM8350 comparator register definitions
  mfd: Add WM8350 PMU register definitions
  mfd: Add WM8350 PMIC register definitions
  mfd: Add WM8350 GPIO register definitions
  mfd: Add WM8350 audio register definitions
  regulator: Export regulator name via sysfs
  regulator: Add WM8400 regulator support
  mfd: Core support for the WM8400 AudioPlus HiFi CODEC and PMU
  ...

31 files changed:
Documentation/ABI/testing/sysfs-class-regulator
Documentation/power/regulator/machine.txt
Documentation/power/regulator/regulator.txt
MAINTAINERS
drivers/mfd/Kconfig
drivers/mfd/Makefile
drivers/mfd/wm8350-core.c [new file with mode: 0644]
drivers/mfd/wm8350-gpio.c [new file with mode: 0644]
drivers/mfd/wm8350-i2c.c [new file with mode: 0644]
drivers/mfd/wm8350-regmap.c [new file with mode: 0644]
drivers/mfd/wm8400-core.c [new file with mode: 0644]
drivers/regulator/Kconfig
drivers/regulator/Makefile
drivers/regulator/bq24022.c
drivers/regulator/core.c
drivers/regulator/da903x.c [new file with mode: 0644]
drivers/regulator/wm8350-regulator.c [new file with mode: 0644]
drivers/regulator/wm8400-regulator.c [new file with mode: 0644]
include/linux/mfd/wm8350/audio.h [new file with mode: 0644]
include/linux/mfd/wm8350/comparator.h [new file with mode: 0644]
include/linux/mfd/wm8350/core.h [new file with mode: 0644]
include/linux/mfd/wm8350/gpio.h [new file with mode: 0644]
include/linux/mfd/wm8350/pmic.h [new file with mode: 0644]
include/linux/mfd/wm8350/rtc.h [new file with mode: 0644]
include/linux/mfd/wm8350/supply.h [new file with mode: 0644]
include/linux/mfd/wm8350/wdt.h [new file with mode: 0644]
include/linux/mfd/wm8400-audio.h [new file with mode: 0644]
include/linux/mfd/wm8400-private.h [new file with mode: 0644]
include/linux/mfd/wm8400.h [new file with mode: 0644]
include/linux/regulator/driver.h
include/linux/regulator/machine.h

index 79a4a75..3731f6f 100644 (file)
@@ -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 @@ Description:
 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 @@ Description:
 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 @@ Description:
 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 @@ Description:
 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 @@ Description:
 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 @@ Description:
 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 @@ Description:
 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 @@ Description:
 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 @@ Description:
                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 @@ Description:
 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 @@ Description:
 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 @@ Description:
 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 @@ Description:
 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 @@ Description:
 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 @@ Description:
 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 @@ Description:
 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 @@ Description:
 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 @@ Description:
 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 @@ Description:
 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 @@ Description:
 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
index c9a3566..ce3487d 100644 (file)
@@ -2,17 +2,8 @@ Regulator Machine Driver Interface
 ===================================
 
 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 @@ Consider the following machine :-
 
 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.
+
+struct regulator_consumer_supply {
+       struct device *dev;     /* consumer */
+       const char *supply;     /* consumer supply - e.g. "vcc" */
+};
 
-int regulator_set_device_supply(const char *regulator, struct device *dev,
-                               const char *supply);
+e.g. for the machine above
 
-and is shown with the following code :-
+static struct regulator_consumer_supply regulator1_consumers[] = {
+{
+       .dev    = &platform_consumerB_device.dev,
+       .supply = "Vcc",
+},};
 
-regulator_set_device_supply("Regulator-1", devB, "Vcc");
-regulator_set_device_supply("Regulator-2", devA, "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.
 
-
-2. Regulator supply configuration.
-==================================
-Consider the following machine (again) :-
-
-  Regulator-1 -+-> Regulator-2 --> [Consumer A @ 1.8 - 2.0V]
-               |
-               +-> [Consumer B @ 3.3V]
+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 :-
+
+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).
-
-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,
+supply (Regulator-2). The supply regulator is set by the supply_regulator_dev
+field below:-
+
+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.
+
+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]);
 
-regulator_set_platform_constraints("Regulator-1", &domain_1);
-regulator_set_platform_constraints("Regulator-2", &domain_2);
+/* register regulator 2 device */
+platform_device_register(&wm8350_regulator_devices[1]);
index a690501..4200acc 100644 (file)
@@ -10,11 +10,11 @@ Registration
 
 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 :-
 
index 988b0a8..5d0b8a2 100644 (file)
@@ -4520,10 +4520,11 @@ S:      Maintained
 
 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
 
index 0dae245..5eff8ad 100644 (file)
@@ -87,6 +87,44 @@ config MFD_TC6393XB
        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"
index 6abebe3..759b1fe 100644 (file)
@@ -12,6 +12,11 @@ obj-$(CONFIG_MFD_T7L66XB)    += t7l66xb.o
 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 (file)
index 0000000..25a7a5d
--- /dev/null
@@ -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 (file)
index 0000000..ebf99be
--- /dev/null
@@ -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 (file)
index 0000000..8dfe21b
--- /dev/null
@@ -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 (file)
index 0000000..974678d
--- /dev/null
@@ -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 (file)
index 0000000..6a0cedb
--- /dev/null
@@ -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>");
index a656128..4dada6e 100644 (file)
@@ -56,4 +56,28 @@ config REGULATOR_BQ24022
          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
index ac2c64e..254d40c 100644 (file)
@@ -8,5 +8,8 @@ obj-$(CONFIG_REGULATOR_FIXED_VOLTAGE) += fixed.o
 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
index 263699d..366565a 100644 (file)
 #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_set_current_limit(struct regulator_dev *rdev,
 
 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_enable(struct regulator_dev *rdev)
 
 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 @@ static int __init bq24022_probe(struct platform_device *pdev)
        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);
index 9c79862..02a7744 100644 (file)
@@ -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 regulator_map {
        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 device_requested_uA_show(struct device *dev,
 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_uV_show(struct device *dev,
 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_opmode_show(struct device *dev,
 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_state_show(struct device *dev,
 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_min_uA_show(struct device *dev,
 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_max_uA_show(struct device *dev,
 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_min_uV_show(struct device *dev,
 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_max_uV_show(struct device *dev,
 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_total_uA_show(struct device *dev,
 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_type_show(struct device *dev,
 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_mem_uV_show(struct device *dev,
 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_disk_uV_show(struct device *dev,
 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 suspend_opmode_show(struct regulator_dev *rdev,
 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_mem_mode_show(struct device *dev,
 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_disk_mode_show(struct device *dev,
 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_standby_mode_show(struct device *dev,
 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_mem_state_show(struct device *dev,
 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_disk_state_show(struct device *dev,
 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 @@ static ssize_t regulator_suspend_standby_state_show(struct device *dev,
        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 struct device_attribute regulator_dev_attrs[] = {
 
 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 @@ static int suspend_set_state(struct regulator_dev *rdev,
 
        /* 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 @@ static void print_constraints(struct regulator_dev *rdev)
        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 *regulator_get(struct device *dev, const char *id)
        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 @@ struct regulator *regulator_get(struct device *dev, const char *id)
        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 @@ struct regulator *regulator_get(struct device *dev, const char *id)
        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 @@ EXPORT_SYMBOL_GPL(regulator_notifier_call_chain);
  * 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 @@ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc,
            !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 @@ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc,
        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 @@ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc,
        INIT_LIST_HEAD(&rdev->slist);
        BLOCKING_INIT_NOTIFIER_HEAD(&rdev->notifier);
 
+       /* 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;
+               }
+       }
+
+       /* 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;
-       device_initialize(&rdev->dev);
+       rdev->dev.parent = 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);
-
-       ret = device_add(&rdev->dev);
-       if (ret == 0)
-               list_add(&rdev->list, &regulator_list);
-       else {
+                "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 @@ void regulator_unregister(struct regulator_dev *rdev)
 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 @@ int rdev_get_id(struct regulator_dev *rdev)
 }
 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 (file)
index 0000000..3688e33
--- /dev/null
@@ -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 (file)
index 0000000..1f44b17
--- /dev/null
@@ -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 (file)
index 0000000..48b372e
--- /dev/null
@@ -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/include/linux/mfd/wm8350/audio.h b/include/linux/mfd/wm8350/audio.h
new file mode 100644 (file)
index 0000000..217bb22
--- /dev/null
@@ -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
+ */