diff --git a/drivers/Kconfig b/drivers/Kconfig
index f4076d9..77428e4 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -84,6 +84,8 @@ source "drivers/edac/Kconfig"
 
 source "drivers/rtc/Kconfig"
 
+source "drivers/regulator/Kconfig"
+
 source "drivers/dma/Kconfig"
 
 source "drivers/dca/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index 8cb37e3..b52a889 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -84,6 +84,7 @@ obj-y				+= firmware/
 obj-$(CONFIG_CRYPTO)		+= crypto/
 obj-$(CONFIG_SUPERH)		+= sh/
 obj-$(CONFIG_GENERIC_TIME)	+= clocksource/
+bj-$(CONFIG_REGULATOR)		+= regulator/
 obj-$(CONFIG_DMA_ENGINE)	+= dma/
 obj-$(CONFIG_DCA)		+= dca/
 obj-$(CONFIG_HID)		+= hid/
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index ec568fa..e397df4 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -38,6 +38,12 @@ config LEDS_SPITZ
 	help
 	  This option enables support for the LEDs on Sharp Zaurus
 	  SL-Cxx00 series (C1000, C3000, C3100).
+	  
+config LEDS_WM8350
+	tristate "LED Support for the WM8350"
+	depends on LEDS_CLASS && REGULATOR_WM8350
+	help
+	  This option enables support for the LEDs driven by the WM8350.
 
 config LEDS_IXP4XX
 	tristate "LED Support for GPIO connected LEDs on IXP4XX processors"
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index a60de1b..8d97961 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_LEDS_TRIGGERS)		+= led-triggers.o
 obj-$(CONFIG_LEDS_CORGI)		+= leds-corgi.o
 obj-$(CONFIG_LEDS_LOCOMO)		+= leds-locomo.o
 obj-$(CONFIG_LEDS_SPITZ)		+= leds-spitz.o
+obj-$(CONFIG_LEDS_WM8350)		+= leds-wm8350.o
 obj-$(CONFIG_LEDS_IXP4XX)		+= leds-ixp4xx-gpio.o
 obj-$(CONFIG_LEDS_TOSA)			+= leds-tosa.o
 obj-$(CONFIG_LEDS_S3C24XX)		+= leds-s3c24xx.o
diff --git a/drivers/leds/leds-wm8350.c b/drivers/leds/leds-wm8350.c
new file mode 100644
index 0000000..dd43a57
--- /dev/null
+++ b/drivers/leds/leds-wm8350.c
@@ -0,0 +1,279 @@
+/*
+ * LED driver for WM8350 driven LEDS.
+ *
+ * Copyright(C) 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 version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/err.h>
+#include <linux/regulator/wm8350/pmic.h>
+#include <linux/regulator/regulator.h>
+
+#define WM8350_LED_VERSION "0.2"
+
+struct wm8350_led {
+	struct work_struct work;
+	struct mutex mutex;
+	struct regulator *isink;
+	struct regulator *dcdc;
+	struct notifier_block notifier;
+	enum led_brightness value;
+	struct led_classdev cdev;
+	int retries;
+	int half_value;
+	int full_value;
+	int current_value;
+};
+#define to_wm8350_led(led_cdev) \
+	container_of(led_cdev, struct wm8350_led, cdev)
+
+static void led_work(struct work_struct *work)
+{
+	struct wm8350_led *led =
+		container_of(work, struct wm8350_led, work);
+
+	mutex_lock(&led->mutex);
+	switch (led->value) {
+	case LED_OFF:
+		led->current_value = 0;
+		break;
+	case LED_HALF:
+		led->retries = 0;
+		led->current_value = led->half_value;
+		break;
+	case LED_FULL:
+		led->retries = 0;
+		led->current_value = led->full_value;
+		break;
+	}
+
+	regulator_set_current(led->isink, led->current_value);
+	mutex_unlock(&led->mutex);
+}
+
+static void wm8350_led_set(struct led_classdev *led_cdev,
+	enum led_brightness value)
+{
+	struct wm8350_led *led = to_wm8350_led(led_cdev);
+
+	mutex_lock(&led->mutex);
+	led->value = value;
+	mutex_unlock(&led->mutex);
+	schedule_work(&led->work);
+}
+
+static int wm8350_led_notifier(struct notifier_block *self,
+	unsigned long event, void *data)
+{
+	struct wm8350_led *led =
+		container_of(self, struct wm8350_led, notifier);
+
+	if (event & REGULATOR_EVENT_UNDER_VOLTAGE)
+		printk(KERN_ERR "wm8350: LED DCDC undervoltage\n");
+	if (event & REGULATOR_EVENT_REGULATION_OUT)
+		printk(KERN_ERR "wm8350: LED ISINK out of regulation\n");
+
+	mutex_lock(&led->mutex);
+	if (led->retries) {
+		led->retries--;
+		regulator_set_current(led->isink, led->value);
+		regulator_enable(led->isink);
+	} else {
+		printk(KERN_ERR "wm8350: LED regulation retry failure - disabled\n");
+		led->current_value = 0;
+		regulator_disable(led->isink);
+	}
+	mutex_unlock(&led->mutex);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int wm8350_led_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct wm8350_led *led =
+		(struct wm8350_led *)platform_get_drvdata(pdev);
+
+	led_classdev_suspend(&led->cdev);
+	return 0;
+}
+
+static int wm8350_led_resume(struct platform_device *pdev)
+{
+	struct wm8350_led *led =
+		(struct wm8350_led *)platform_get_drvdata(pdev);
+
+	led_classdev_resume(&led->cdev);
+	return 0;
+}
+#endif
+
+static void wm8350_led_shutdown(struct platform_device *pdev)
+{
+	struct wm8350_led *led =
+		(struct wm8350_led *)platform_get_drvdata(pdev);
+
+	mutex_lock(&led->mutex);
+	led->value = LED_OFF;
+	regulator_disable(led->isink);
+	mutex_unlock(&led->mutex);
+}
+
+static int wm8350_led_probe(struct platform_device *pdev)
+{
+	struct regulator *isink, *dcdc;
+	struct wm8350_led *led;
+	struct wm8350_led_platform_data *pdata = pdev->dev.platform_data;
+	struct wm8350_pmic *pmic;
+	int ret;
+
+	printk(KERN_INFO "wm8350: LED driver %s\n", WM8350_LED_VERSION);
+
+	if (pdata == NULL) {
+		printk(KERN_ERR "%s: no platform data\n", __func__);
+		return -ENODEV;
+	}
+	if (pdata->isink != WM8350_ISINK_A && pdata->isink != WM8350_ISINK_B) {
+		printk(KERN_ERR "%s: invalid ISINK\n", __func__);
+		return -EINVAL;
+	}
+	if (pdata->dcdc != WM8350_DCDC_2 && pdata->dcdc != WM8350_DCDC_5) {
+		printk(KERN_ERR "%s: invalid DCDC\n", __func__);
+		return -EINVAL;
+	}
+
+	printk(KERN_INFO "wm8350: LED using %s and %s\n",
+		pdata->isink == WM8350_ISINK_A ? "ISINKA" : "ISINKB",
+		pdata->dcdc == WM8350_DCDC_2 ? "DCDC2" : "DCDC5");
+
+	isink = regulator_get(&pdev->dev,
+		pdata->isink == WM8350_ISINK_A ? "ISINKA" : "ISINKB");
+	if (IS_ERR(isink) || isink == NULL) {
+		printk(KERN_ERR "%s: cant get ISINK\n", __func__);
+		return PTR_ERR(isink);
+	}
+
+	dcdc = regulator_get(&pdev->dev,
+		pdata->dcdc == WM8350_DCDC_2 ? "DCDC2" : "DCDC5");
+	if (IS_ERR(dcdc) || dcdc == NULL) {
+		printk(KERN_ERR "%s: cant get DCDC\n", __func__);
+		regulator_put(isink);
+		return PTR_ERR(dcdc);
+	}
+
+	led = kzalloc(sizeof(*led), GFP_KERNEL);
+	if (led == NULL) {
+		regulator_put(isink);
+		regulator_put(dcdc);
+		return -ENOMEM;
+	}
+
+	led->retries = pdata->retries;
+	led->half_value = pdata->half_value;
+	led->full_value = pdata->full_value;
+	led->cdev.brightness_set = wm8350_led_set;
+	led->cdev.default_trigger = (char*) pdata->default_trigger;
+	led->cdev.name = pdata->name;
+	led->isink = isink;
+	led->dcdc = dcdc;
+	pmic = regulator_get_drvdata(led->isink);
+
+	mutex_init(&led->mutex);
+	INIT_WORK(&led->work, led_work);
+	led->value = LED_OFF;
+	platform_set_drvdata(pdev, led);
+
+	ret = led_classdev_register(&pdev->dev, &led->cdev);
+	if (ret < 0) {
+		regulator_put(isink);
+		regulator_put(dcdc);
+		kfree(led);
+		return ret;
+	}
+
+	led->notifier.notifier_call = wm8350_led_notifier;
+	regulator_register_client(isink, &led->notifier);
+	regulator_register_client(dcdc, &led->notifier);
+
+	/* WM8350 ISINK & DCDC setup */
+	if (pdata->isink == WM8350_ISINK_A)
+		pmic->isink_A_dcdc = pdata->dcdc;
+	else
+		pmic->isink_B_dcdc = pdata->dcdc;
+
+	wm8350_dcdc_set_slot(pmic, pdata->dcdc, 1, 1,
+		pdata->dcdc == WM8350_DCDC_2 ?
+		WM8350_DC2_ERRACT_SHUTDOWN_CONV :
+		WM8350_DC5_ERRACT_SHUTDOWN_CONV);
+
+	wm8350_isink_set_flash(pmic, pdata->isink,
+		WM8350_ISINK_FLASH_DISABLE,
+		WM8350_ISINK_FLASH_TRIG_BIT,
+		WM8350_ISINK_FLASH_DUR_32MS,
+		WM8350_ISINK_FLASH_ON_INSTANT,
+		WM8350_ISINK_FLASH_OFF_INSTANT,
+		WM8350_ISINK_FLASH_MODE_EN);
+
+	wm8350_dcdc25_set_mode(pmic, pdata->dcdc,
+		WM8350_ISINK_MODE_BOOST, WM8350_ISINK_ILIM_NORMAL,
+		pdata->voltage_ramp, pdata->isink == WM8350_ISINK_A ?
+		WM8350_DC5_FBSRC_ISINKA : WM8350_DC5_FBSRC_ISINKB);
+
+	regulator_set_current(isink, 0);
+	regulator_enable(led->isink);
+	return 0;
+}
+
+static int wm8350_led_remove(struct platform_device *pdev)
+{
+	struct wm8350_led *led =
+		(struct wm8350_led *)platform_get_drvdata(pdev);
+
+	led_classdev_unregister(&led->cdev);
+	schedule_work(&led->work);
+	flush_scheduled_work();
+	regulator_set_current(led->isink, 0);
+	regulator_disable(led->isink);
+	regulator_unregister_client(led->dcdc, &led->notifier);
+	regulator_unregister_client(led->isink, &led->notifier);
+	regulator_put(led->dcdc);
+	regulator_put(led->isink);
+	kfree(led);
+	return 0;
+}
+
+struct platform_driver wm8350_led_driver = {
+	.driver	= {
+		.name	= "wm8350-led",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= wm8350_led_probe,
+	.remove		= wm8350_led_remove,
+	.shutdown	= wm8350_led_shutdown,
+	.suspend	= wm8350_led_suspend,
+	.resume		= wm8350_led_resume,
+};
+
+static int __devinit wm8350_led_init(void)
+{
+	return platform_driver_register(&wm8350_led_driver);
+}
+
+static void wm8350_led_exit(void)
+{
+	platform_driver_unregister(&wm8350_led_driver);
+}
+
+module_init(wm8350_led_init);
+module_exit(wm8350_led_exit);
+
+MODULE_AUTHOR("Liam Girdwood <lg@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("WM8350 LED driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 58c806e..1371c52 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -22,6 +22,13 @@ config PDA_POWER
 	  one or two external power supplies (AC/USB) connected to main and
 	  backup batteries, and optional builtin charger.
 
+config BATTERY_WM8350
+	tristate "WM8350 Battery"
+	depends on REGULATOR_WM8350
+	help
+	  Say Y here to enable support for battery status on mx31ads with
+	  wm8350.
+
 config APM_POWER
 	tristate "APM emulation for class batteries"
 	depends on APM_EMULATION
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 6413ded..313cda9 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_POWER_SUPPLY)	+= power_supply.o
 obj-$(CONFIG_PDA_POWER)		+= pda_power.o
 obj-$(CONFIG_APM_POWER)		+= apm_power.o
 
+obj-$(CONFIG_BATTERY_WM8350) += wm8350_power.o
 obj-$(CONFIG_BATTERY_DS2760)	+= ds2760_battery.o
 obj-$(CONFIG_BATTERY_PMU)	+= pmu_battery.o
 obj-$(CONFIG_BATTERY_OLPC)	+= olpc_battery.o
diff --git a/drivers/power/wm8350_power.c b/drivers/power/wm8350_power.c
new file mode 100644
index 0000000..0071394
--- /dev/null
+++ b/drivers/power/wm8350_power.c
@@ -0,0 +1,546 @@
+/*
+ * Battery driver for wm8350 PMIC
+ *
+ * Copyright © 2007 Wolfson Microelectronics PLC.
+ *
+ * Based on OLCP Battery Driver
+ *
+ * Copyright © 2006  David Woodhouse <dwmw2@infradead.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/regulator/wm8350/supply.h>
+#include <linux/regulator/wm8350/bus.h>
+#include <linux/regulator/wm8350/comparator.h>
+
+#define WM8350_POWER_VERSION	"0.4"
+
+#define WM8350_BATT_SUPPLY	1
+#define WM8350_USB_SUPPLY	2
+#define WM8350_LINE_SUPPLY	4
+
+static int wm8350_charger_config (struct wm8350 *wm8350,
+	struct wm8350_charger_policy *policy)
+{
+	u16 reg;
+
+	/* make sure the end of charge (eoc) current is not greater or equal
+	 * to the actual charge current. */
+	if (policy->eoc_mA >= policy->trickle_charge_mA) {
+		printk(KERN_ERR "%s: eoc greater than charge\n", __func__);
+		return -EINVAL;
+	}
+
+	wm8350_reg_unlock(wm8350);
+	reg = wm8350_reg_read(wm8350, WM8350_BATTERY_CHARGER_CONTROL_1)
+		& WM8350_CHG_ENA_R168;
+	wm8350_reg_write(wm8350, WM8350_BATTERY_CHARGER_CONTROL_1,
+		reg |  policy->eoc_mA | policy->trickle_start_mV);
+	wm8350_reg_write(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2,
+		policy->charge_mV | policy->trickle_charge_mA |
+		policy->fast_limit_mA | policy->charge_timeout);
+	wm8350_reg_lock(wm8350);
+	return 0;
+}
+
+int wm8350_charger_enable(struct wm8350_power *power, int enable)
+{
+	struct wm8350 *wm8350 = to_wm8350_from_power(power);
+	struct wm8350_charger_policy *policy = &power->policy;
+
+	mutex_lock(&policy->mutex);
+	if (enable) {
+		/* load policy */
+		wm8350_charger_config(wm8350, policy);
+		/* enable */
+		policy->enable = 1;
+		wm8350_reg_unlock(wm8350);
+		wm8350_set_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CHG_ENA);
+		wm8350_reg_lock(wm8350);
+	} else {
+		/* disable */
+		policy->enable = 0;
+		wm8350_reg_unlock(wm8350);
+		wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CHG_ENA);
+		wm8350_reg_lock(wm8350);
+	}
+	mutex_unlock(&policy->mutex);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(wm8350_charger_enable);
+
+int wm8350_fast_charger_enable(struct wm8350_power *power, int enable)
+{
+	struct wm8350_charger_policy *policy = &power->policy;
+
+	mutex_lock(&policy->mutex);
+	if (enable)
+		/* enable */
+		policy->fast_enable = 1;
+	else
+		/* disable */
+		policy->fast_enable = 0;
+	mutex_unlock(&policy->mutex);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(wm8350_fast_charger_enable);
+
+static int wm8350_get_supplies(struct wm8350 *wm8350)
+{
+	u16 sm, ov, co, chrg;
+	int supplies=0;
+
+	sm = wm8350_reg_read(wm8350, 233);
+	ov = wm8350_reg_read(wm8350, 227);
+	co = wm8350_reg_read(wm8350, 231);
+	chrg = wm8350_reg_read(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2);
+
+	/* USB_SM */
+	sm >>= 8;
+	sm &= 0x7;
+
+	/* USB_LIMIT_OVRDE */
+	ov >>= 10;
+	ov &= 0x01;
+
+	/* WALL_FB_OVRDE */
+	co >>= 14;
+	co &= 0x01;
+
+	/* CHG_ISEL */
+	chrg &= WM8350_CHG_ISEL_MASK;
+
+	if (((sm == 0x01) || (sm == 0x05) || (sm == 0x07)) && !ov)
+		supplies = WM8350_USB_SUPPLY;
+	else if (((sm == 0x01) || (sm == 0x05) || (sm == 0x07)) && ov &&
+		(chrg == 0x00))
+		supplies = WM8350_USB_SUPPLY | WM8350_BATT_SUPPLY;
+	else if (co)
+		supplies = WM8350_LINE_SUPPLY;
+	else
+		supplies = WM8350_BATT_SUPPLY;
+
+	return supplies;
+}
+
+static int wm8350_batt_status(struct wm8350 *wm8350)
+{
+	u16 chrg;
+
+	chrg = wm8350_reg_read(wm8350,  WM8350_BATTERY_CHARGER_CONTROL_2);
+	chrg &= WM8350_CHG_STS_MASK;
+	chrg >>= 12;
+
+	switch (chrg) {
+	case 0x00:
+		return POWER_SUPPLY_STATUS_DISCHARGING;
+	case 0x01:
+	case 0x02:
+		return POWER_SUPPLY_STATUS_CHARGING;
+	}
+	return POWER_SUPPLY_STATUS_UNKNOWN;
+}
+
+static ssize_t supply_state_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct wm8350_power *power = to_wm8350_power_device(dev);
+	struct wm8350 *wm8350 = to_wm8350_from_power(power);
+	char *supply;
+	int state;
+
+	state = wm8350_get_supplies(wm8350);
+	switch (state) {
+	case WM8350_BATT_SUPPLY:
+		supply = "Battery";
+		break;
+	case WM8350_USB_SUPPLY:
+		supply = "USB";
+		break;
+	case WM8350_LINE_SUPPLY:
+		supply = "Line";
+		break;
+	case WM8350_USB_SUPPLY | WM8350_BATT_SUPPLY:
+		supply ="Battery and USB";
+		break;
+	default:
+		return 0;
+	}
+
+	return sprintf(buf, "%s\n", supply);
+}
+static DEVICE_ATTR(supply_state, 0444, supply_state_show, NULL);
+
+static ssize_t battery_state_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct wm8350_power *power = to_wm8350_power_device(dev);
+	struct wm8350 *wm8350 = to_wm8350_from_power(power);
+	char *batt;
+	int state;
+
+	state = wm8350_batt_status(wm8350);
+	switch (state) {
+	case POWER_SUPPLY_STATUS_DISCHARGING:
+		batt = "Discharging";
+		break;
+	case POWER_SUPPLY_STATUS_CHARGING:
+		batt = "Charging";
+		break;
+	case POWER_SUPPLY_STATUS_UNKNOWN:
+		batt = "Unknown";
+		break;
+	default:
+		return 0;
+	}
+
+	return sprintf(buf, "%s\n", batt);
+}
+static DEVICE_ATTR(battery_state, 0444, battery_state_show, NULL);
+
+static ssize_t charge_state_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct wm8350_power *power = to_wm8350_power_device(dev);
+	struct wm8350 *wm8350 = to_wm8350_from_power(power);
+	char *charge;
+	int state;
+
+	state =  wm8350_reg_read(wm8350,  WM8350_BATTERY_CHARGER_CONTROL_2) &
+		WM8350_CHG_STS_MASK;
+	switch (state) {
+	case WM8350_CHG_STS_OFF:
+		charge = "Charger Off";
+		break;
+	case WM8350_CHG_STS_TRICKLE:
+		charge = "Trickle Charging";
+		break;
+	case WM8350_CHG_STS_FAST:
+		charge = "Fast Charging";
+		break;
+	default:
+		return 0;
+	}
+
+	return sprintf(buf, "%s\n", charge);
+}
+static DEVICE_ATTR(charge_state, 0444, charge_state_show, NULL);
+
+static void wm8350_charger_handler(struct wm8350 *wm8350, int irq, void *data)
+{
+	struct wm8350_power *power = &wm8350->power;
+	struct wm8350_charger_policy *policy = &power->policy;
+
+	switch (irq) {
+	case WM8350_IRQ_CHG_BAT_HOT:
+		printk(KERN_ERR "wm8350-power: battery too hot\n");
+		break;
+	case WM8350_IRQ_CHG_BAT_COLD:
+		printk(KERN_ERR "wm8350-power: battery too cold\n");
+		break;
+	case WM8350_IRQ_CHG_BAT_FAIL:
+		printk(KERN_ERR "wm8350-power: battery failed\n");
+		break;
+	case WM8350_IRQ_CHG_TO:
+		printk(KERN_INFO "wm8350-power: charger timeout\n");
+		break;
+	case WM8350_IRQ_CHG_END:
+		printk(KERN_INFO "wm8350-power: charger stopped\n");
+		break;
+	case WM8350_IRQ_CHG_START:
+		printk(KERN_INFO "wm8350-power: charger started\n");
+		break;
+	case WM8350_IRQ_CHG_FAST_RDY:
+		mutex_lock(&policy->mutex);
+		if (policy->fast_enable) {
+			printk(KERN_INFO "wm8350-power: fast charger ready\n");
+			wm8350_charger_config(wm8350, policy);
+			wm8350_reg_unlock(wm8350);
+			wm8350_set_bits(wm8350, WM8350_BATTERY_CHARGER_CONTROL_1,
+				WM8350_CHG_FAST);
+			wm8350_reg_lock(wm8350);
+		} else
+			printk(KERN_INFO "wm8350-power: fast charger ready but disabled\n");
+		mutex_unlock(&policy->mutex);
+		break;
+	case WM8350_IRQ_CHG_VBATT_LT_3P9:
+		printk(KERN_WARNING "wm8350-power: battery < 3.9V\n");
+		break;
+	case WM8350_IRQ_CHG_VBATT_LT_3P1:
+		printk(KERN_WARNING "wm8350-power: battery < 3.1V\n");
+		break;
+	case WM8350_IRQ_CHG_VBATT_LT_2P85:
+		printk(KERN_WARNING "wm8350-power: battery < 2.85V\n");
+		break;
+	default:
+		printk(KERN_ERR "wm8350-power: irq %d don't care\n", irq);
+	}
+}
+
+/*********************************************************************
+ *		AC Power
+ *********************************************************************/
+static int wm8350_ac_get_prop(struct power_supply *psy,
+                            enum power_supply_property psp,
+                            union power_supply_propval *val)
+{
+	struct wm8350_power *wm8350_power =
+		to_wm8350_power_device(psy->dev->parent);
+	struct wm8350 *wm8350 = to_wm8350_from_power(wm8350_power);
+	int ret = 0;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_ONLINE:
+		val->intval = !!(wm8350_get_supplies(wm8350) &
+			WM8350_LINE_SUPPLY);
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		val->intval = wm8350_read_line_uvolts(wm8350);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
+static enum power_supply_property wm8350_ac_props[] = {
+	POWER_SUPPLY_PROP_ONLINE,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+};
+
+/*********************************************************************
+ *		USB Power
+ *********************************************************************/
+static int wm8350_usb_get_prop(struct power_supply *psy,
+                            enum power_supply_property psp,
+                            union power_supply_propval *val)
+{
+	struct wm8350_power *wm8350_power =
+		to_wm8350_power_device(psy->dev->parent);
+	struct wm8350 *wm8350 = to_wm8350_from_power(wm8350_power);
+	int ret = 0;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_ONLINE:
+		val->intval = !!(wm8350_get_supplies(wm8350) &
+			WM8350_USB_SUPPLY);
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		val->intval = wm8350_read_usb_uvolts(wm8350);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
+static enum power_supply_property wm8350_usb_props[] = {
+	POWER_SUPPLY_PROP_ONLINE,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+};
+
+/*********************************************************************
+ *		Battery properties
+ *********************************************************************/
+
+static int wm8350_bat_get_property(struct power_supply *psy,
+                                 enum power_supply_property psp,
+                                 union power_supply_propval *val)
+{
+	struct wm8350_power *wm8350_power =
+		to_wm8350_power_device(psy->dev->parent);
+	struct wm8350 *wm8350 = to_wm8350_from_power(wm8350_power);
+	int ret = 0;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_STATUS:
+		val->intval = wm8350_batt_status(wm8350);
+		break;
+	case POWER_SUPPLY_PROP_ONLINE:
+		val->intval = !!(wm8350_get_supplies(wm8350) &
+			WM8350_BATT_SUPPLY);
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		val->intval = wm8350_read_battery_uvolts(wm8350);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static enum power_supply_property wm8350_bat_props[] = {
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_ONLINE,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+};
+
+/*********************************************************************
+ *		Initialisation
+ *********************************************************************/
+
+static void free_charger_irq(struct wm8350 *wm8350)
+{
+	wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_BAT_HOT);
+	wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_HOT);
+	wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_BAT_COLD);
+	wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_COLD);
+	wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_BAT_FAIL);
+	wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_FAIL);
+	wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_TO);
+	wm8350_free_irq(wm8350, WM8350_IRQ_CHG_TO);
+	wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_END);
+	wm8350_free_irq(wm8350, WM8350_IRQ_CHG_END);
+	wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_START);
+	wm8350_free_irq(wm8350, WM8350_IRQ_CHG_START);
+	wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9);
+	wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9);
+	wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1);
+	wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1);
+	wm8350_mask_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85);
+	wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85);
+}
+
+static int wm8350_power_probe(struct device *dev)
+{
+	struct wm8350_power *power = to_wm8350_power_device(dev);
+	struct wm8350 *wm8350 = to_wm8350_from_power(power);
+	struct power_supply *usb = &power->usb;
+	struct power_supply *battery = &power->battery;
+	struct power_supply *ac = &power->ac;
+	int ret;
+
+	printk(KERN_INFO "wm8350: power driver %s\n", WM8350_POWER_VERSION);
+
+	mutex_init(&power->policy.mutex);
+
+	ac->name = "wm8350-ac";
+	ac->type = POWER_SUPPLY_TYPE_MAINS,
+	ac->properties = wm8350_ac_props,
+	ac->num_properties = ARRAY_SIZE(wm8350_ac_props),
+	ac->get_property = wm8350_ac_get_prop,
+	ret = power_supply_register(dev, ac);
+	if (ret)
+		return ret;
+
+	battery->name = "wm8350-battery",
+	battery->properties = wm8350_bat_props,
+	battery->num_properties = ARRAY_SIZE(wm8350_bat_props),
+	battery->get_property = wm8350_bat_get_property,
+	battery->use_for_apm = 1,
+	ret = power_supply_register(dev, battery);
+	if (ret)
+		goto battery_failed;
+
+	usb->name = "wm8350-usb",
+	usb->type = POWER_SUPPLY_TYPE_USB,
+	usb->properties = wm8350_usb_props,
+	usb->num_properties = ARRAY_SIZE(wm8350_usb_props),
+	usb->get_property = wm8350_usb_get_prop,
+	ret = power_supply_register(dev, usb);
+	if (ret)
+		goto usb_failed;
+
+	/* register our interest in charger events */
+	wm8350_register_irq(wm8350, WM8350_IRQ_CHG_BAT_HOT,
+			    wm8350_charger_handler, NULL);
+	wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_BAT_HOT);
+	wm8350_register_irq(wm8350, WM8350_IRQ_CHG_BAT_COLD,
+			    wm8350_charger_handler, NULL);
+	wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_BAT_COLD);
+	wm8350_register_irq(wm8350, WM8350_IRQ_CHG_BAT_FAIL,
+			    wm8350_charger_handler, NULL);
+	wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_BAT_FAIL);
+	wm8350_register_irq(wm8350, WM8350_IRQ_CHG_TO,
+			    wm8350_charger_handler, NULL);
+	wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_TO);
+	wm8350_register_irq(wm8350, WM8350_IRQ_CHG_END,
+			    wm8350_charger_handler, NULL);
+	wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_END);
+	wm8350_register_irq(wm8350, WM8350_IRQ_CHG_START,
+			    wm8350_charger_handler, NULL);
+	wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_START);
+	wm8350_register_irq(wm8350, WM8350_IRQ_CHG_FAST_RDY,
+			    wm8350_charger_handler, NULL);
+	wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_FAST_RDY);
+	wm8350_register_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9,
+			    wm8350_charger_handler, NULL);
+	wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9);
+	wm8350_register_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1,
+			    wm8350_charger_handler, NULL);
+	wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1);
+	wm8350_register_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85,
+			    wm8350_charger_handler, NULL);
+	wm8350_unmask_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85);
+
+	ret = device_create_file(dev, &dev_attr_supply_state);
+	if (ret < 0)
+		printk(KERN_WARNING "wm8350: failed to add supply sysfs\n");
+	ret = device_create_file(dev, &dev_attr_battery_state);
+	if (ret < 0)
+		printk(KERN_WARNING "wm8350: failed to add battery sysfs\n");
+	ret = device_create_file(dev, &dev_attr_charge_state);
+	if (ret < 0)
+		printk(KERN_WARNING "wm8350: failed to add charge sysfs\n");
+	ret = 0;
+	goto success;
+
+usb_failed:
+	power_supply_unregister(battery);
+battery_failed:
+	power_supply_unregister(ac);
+success:
+	return ret;
+}
+
+static int __exit wm8350_power_remove(struct device *dev)
+{
+	struct wm8350_power *power = to_wm8350_power_device(dev);
+	struct wm8350 *wm8350 = to_wm8350_from_power(power);
+
+	free_charger_irq(wm8350);
+	device_remove_file(dev, &dev_attr_supply_state);
+	device_remove_file(dev, &dev_attr_battery_state);
+	device_remove_file(dev, &dev_attr_charge_state);
+	power_supply_unregister(&power->battery);
+	power_supply_unregister(&power->ac);
+	power_supply_unregister(&power->usb);
+	return 0;
+}
+
+struct device_driver wm8350_power_driver = {
+	.name = "wm8350-power",
+	.bus = &wm8350_bus_type,
+	.owner = THIS_MODULE,
+	.probe = wm8350_power_probe,
+	.remove = wm8350_power_remove,
+};
+
+static int __init wm8350_bat_init(void)
+{
+	return driver_register(&wm8350_power_driver);
+}
+
+static void __exit wm8350_bat_exit(void)
+{
+	driver_unregister(&wm8350_power_driver);
+}
+
+module_init(wm8350_bat_init);
+module_exit(wm8350_bat_exit);
+
+MODULE_AUTHOR("Graeme Gregory <gg@opensource.wolfsonmicro.com,"
+	"Liam Girdwood <lg@opensource.wolfsonmicro.com");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Battery driver for WM8350");
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
new file mode 100644
index 0000000..211ba46
--- /dev/null
+++ b/drivers/regulator/Kconfig
@@ -0,0 +1,22 @@
+menu "Voltage and Current regulators"
+
+config REGULATOR_API
+	bool
+
+config REGULATOR
+	bool "Voltage and Current Regulator Support"
+	depends on EXPERIMENTAL
+	select REGULATOR_API
+	default n
+	help
+	  Generic Voltage and Current Regulator support.
+
+config REGULATOR_DEBUG
+	bool "Regulator debug support"
+	depends on REGULATOR
+	help
+	  Say yes here to enable debugging support.
+
+source "drivers/regulator/wm8350/Kconfig"
+
+endmenu
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
new file mode 100644
index 0000000..497e7e9
--- /dev/null
+++ b/drivers/regulator/Makefile
@@ -0,0 +1,11 @@
+#
+# Makefile for regulator drivers.
+#
+
+obj-$(CONFIG_REGULATOR) += reg-core.o 
+
+ifeq ($(CONFIG_REGULATOR_DEBUG),y)
+	EXTRA_CFLAGS		+= -DDEBUG
+endif
+
+obj-$(CONFIG_REGULATOR_WM8350) += wm8350/
diff --git a/drivers/regulator/reg-core.c b/drivers/regulator/reg-core.c
new file mode 100644
index 0000000..1434697
--- /dev/null
+++ b/drivers/regulator/reg-core.c
@@ -0,0 +1,1036 @@
+/*
+ * regulator.c  --  Voltage/Current Regulator framework.
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC.
+ *
+ * Author: Liam Girdwood <liam.girdwood@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/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/regulator/regulator.h>
+#include <linux/regulator/regulator-drv.h>
+#include <linux/regulator/regulator-platform.h>
+
+#define REGULATOR_VERSION "0.2"
+
+/* We need to undef the current macro (from include/asm/current.h) otherwise
+ * our "current" sysfs entry becomes "(get_current())".
+ */
+#undef current
+
+static DEFINE_MUTEX(list_mutex);
+static LIST_HEAD(regulator_cdevs);
+
+/**
+ * struct regulator_cdev
+ *
+ * Voltage / Current regulator class device. One for each regulator.
+ */
+struct regulator_cdev {
+	struct regulator_desc *desc;
+	int use_count;
+
+	struct list_head list;
+	struct list_head consumer_list;
+	struct blocking_notifier_head notifier;
+	struct mutex mutex;
+	struct module *owner;
+	struct class_device cdev;
+	struct regulation_constraints *constraints;
+	struct regulator_cdev *parent; /* for tree */
+
+	void *reg_data; /* regulator_cdev data */
+	void *vendor; /* regulator_cdev vendor extensions */
+};
+#define to_rcdev(cd) \
+	container_of(cd, struct regulator_cdev, cdev)
+
+/*
+ * struct regulator
+ *
+ * One for each consumer device.
+ */
+struct regulator {
+	struct device *dev;
+	struct list_head list;
+	int uA_load;
+	int uV_required;
+	int enabled;
+	struct device_attribute dev_attr;
+	struct regulator_cdev *rcdev;
+};
+
+static int _regulator_is_enabled(struct regulator_cdev *rcdev);
+static int _regulator_disable(struct regulator_cdev *rcdev);
+static int _regulator_get_voltage(struct regulator_cdev *rcdev);
+static int _regulator_get_current(struct regulator_cdev *rcdev);
+static unsigned int _regulator_get_mode(struct regulator_cdev *rcdev);
+
+static struct regulator *get_regulator_load(struct device *dev)
+{
+	struct regulator *regulator = NULL;
+	struct regulator_cdev *rcdev;
+
+	list_for_each_entry(rcdev, &regulator_cdevs, list) {
+		list_for_each_entry(regulator, &rcdev->consumer_list, list) {
+			if (regulator->dev == dev)
+				return regulator;
+		}
+	}
+	return NULL;
+}
+
+static int regulator_check_voltage(struct regulator_cdev *rcdev, int uV)
+{
+	if (!rcdev->constraints) {
+		printk(KERN_ERR "%s: no constraints for %s\n", __func__,
+			rcdev->desc->name);
+		return -ENODEV;
+	}
+	if (!rcdev->constraints->valid_ops_mask & REGULATOR_CHANGE_VOLTAGE) {
+		printk(KERN_ERR "%s: operation not allowed for %s\n",
+			__func__, rcdev->desc->name);
+		return -EPERM;
+	}
+	if (uV > rcdev->constraints->max_uV ||
+		uV < rcdev->constraints->min_uV) {
+		printk(KERN_ERR "%s: invalid voltage %duV for %s\n",
+			__func__, uV, rcdev->desc->name);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int regulator_check_current(struct regulator_cdev *rcdev, int uA)
+{
+	if (!rcdev->constraints) {
+		printk(KERN_ERR "%s: no constraints for %s\n", __func__,
+			rcdev->desc->name);
+		return -ENODEV;
+	}
+	if (!rcdev->constraints->valid_ops_mask & REGULATOR_CHANGE_CURRENT) {
+		printk(KERN_ERR "%s: operation not allowed for %s\n",
+			__func__, rcdev->desc->name);
+		return -EPERM;
+	}
+	if (uA > rcdev->constraints->max_uA ||
+		uA < rcdev->constraints->min_uA) {
+		printk(KERN_ERR "%s: invalid current %duA for %s\n",
+			__func__, uA, rcdev->desc->name);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int regulator_check_mode(struct regulator_cdev *rcdev, int mode)
+{
+	if (!rcdev->constraints) {
+		printk(KERN_ERR "%s: no constraints for %s\n", __func__,
+			rcdev->desc->name);
+		return -ENODEV;
+	}
+	if (!rcdev->constraints->valid_ops_mask & REGULATOR_CHANGE_MODE) {
+		printk(KERN_ERR "%s: operation not allowed for %s\n",
+			__func__, rcdev->desc->name);
+		return -EPERM;
+	}
+	if (!rcdev->constraints->valid_modes_mask & mode) {
+		printk(KERN_ERR "%s: invalid mode %x for %s\n",
+			__func__, mode, rcdev->desc->name);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int regulator_check_drms(struct regulator_cdev *rcdev)
+{
+	if (!rcdev->constraints) {
+		printk(KERN_ERR "%s: no constraints for %s\n", __func__,
+			rcdev->desc->name);
+		return -ENODEV;
+	}
+	if (!rcdev->constraints->valid_ops_mask & REGULATOR_CHANGE_DRMS) {
+		printk(KERN_ERR "%s: operation not allowed for %s\n",
+			__func__, rcdev->desc->name);
+		return -EPERM;
+	}
+	return 0;
+}
+
+static ssize_t dev_load_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct regulator *regulator;
+
+	regulator = get_regulator_load(dev);
+	if (regulator == NULL)
+		return 0;
+
+	return sprintf(buf, "%d\n", regulator->uA_load);
+}
+
+static ssize_t regulator_uV_show(struct class_device *cdev, char *buf)
+{
+	struct regulator_cdev *rcdev = to_rcdev(cdev);
+
+	return sprintf(buf, "%d\n", _regulator_get_voltage(rcdev));
+}
+
+static ssize_t regulator_uA_show(struct class_device *cdev, char *buf)
+{
+	struct regulator_cdev *rcdev = to_rcdev(cdev);
+
+	return sprintf(buf, "%d\n", _regulator_get_current(rcdev));
+}
+
+static ssize_t regulator_mode_show(struct class_device *cdev, char *buf)
+{
+	struct regulator_cdev *rcdev = to_rcdev(cdev);
+	int mode = _regulator_get_mode(rcdev);
+
+	switch (mode) {
+	case REGULATOR_MODE_FAST:
+		return sprintf(buf, "fast\n");
+	case REGULATOR_MODE_NORMAL:
+		return sprintf(buf, "normal\n");
+	case REGULATOR_MODE_IDLE:
+		return sprintf(buf, "idle\n");
+	case REGULATOR_MODE_STANDBY:
+		return sprintf(buf, "standby\n");
+	}
+	return sprintf(buf, "unknown\n");
+}
+
+static ssize_t regulator_state_show(struct class_device *cdev, char *buf)
+{
+	struct regulator_cdev *rcdev = to_rcdev(cdev);
+	int state = _regulator_is_enabled(rcdev);
+
+	if (state > 0)
+		return sprintf(buf, "enabled\n");
+	else if (state == 0)
+		return sprintf(buf, "disabled\n");
+	else
+		return sprintf(buf, "unknown\n");
+}
+
+static ssize_t regulator_constraint_uA_show(struct class_device *cdev, char *buf)
+{
+	struct regulator_cdev *rcdev = to_rcdev(cdev);
+
+	if (!rcdev->constraints)
+		return sprintf(buf, "constraints not defined\n");
+
+	return sprintf (buf, "%d %d\n", rcdev->constraints->min_uA,
+		rcdev->constraints->max_uA);
+}
+
+static ssize_t regulator_constraint_uV_show(struct class_device *cdev, char *buf)
+{
+	struct regulator_cdev *rcdev = to_rcdev(cdev);
+
+	if (!rcdev->constraints)
+		return sprintf(buf, "constraints not defined\n");
+
+	return sprintf (buf, "%d %d\n", rcdev->constraints->min_uV,
+		rcdev->constraints->max_uV);
+}
+
+static ssize_t regulator_constraint_modes_show(struct class_device *cdev, char *buf)
+{
+	struct regulator_cdev *rcdev = to_rcdev(cdev);
+	int count = 0;
+
+	if (!rcdev->constraints)
+		return sprintf(buf, "constraints not defined\n");
+
+	if (rcdev->constraints->valid_modes_mask & REGULATOR_MODE_FAST)
+		count = sprintf (buf, "fast ");
+	if (rcdev->constraints->valid_modes_mask & REGULATOR_MODE_NORMAL)
+		count += sprintf (buf + count, "normal ");
+	if (rcdev->constraints->valid_modes_mask & REGULATOR_MODE_IDLE)
+		count += sprintf (buf + count, "idle ");
+	if (rcdev->constraints->valid_modes_mask & REGULATOR_MODE_STANDBY)
+		count += sprintf (buf + count, "standby");
+	count += sprintf(buf + count, "\n");
+	return count;
+}
+
+static ssize_t regulator_total_dev_load(struct class_device *cdev, char *buf)
+{
+	struct regulator_cdev *rcdev = to_rcdev(cdev);
+	struct regulator *regulator;
+	int uA = 0;
+
+	list_for_each_entry(regulator, &rcdev->consumer_list, list)
+		uA =+ regulator->uA_load;
+	return sprintf(buf, "%d\n", uA);
+}
+
+static ssize_t regulator_enabled_use_count(struct class_device *cdev, char *buf)
+{
+	struct regulator_cdev *rcdev = to_rcdev(cdev);
+	return sprintf(buf, "%d\n", rcdev->use_count);
+}
+
+static ssize_t regulator_type_show(struct class_device *cdev, char *buf)
+{
+	struct regulator_cdev *rcdev = to_rcdev(cdev);
+
+	switch (rcdev->desc->type) {
+	case REGULATOR_VOLTAGE:
+		return sprintf(buf, "voltage\n");
+	case REGULATOR_CURRENT:
+		return sprintf(buf, "current\n");
+	}
+	return sprintf(buf, "unknown\n");
+}
+
+static struct class_device_attribute regulator_dev_attrs[] = {
+	__ATTR(voltage, 0444, regulator_uV_show, NULL),
+	__ATTR(current, 0444, regulator_uA_show, NULL),
+	__ATTR(opmode, 0444, regulator_mode_show, NULL),
+	__ATTR(enabled, 0444, regulator_state_show, NULL),
+	__ATTR(voltage_limits, 0444, regulator_constraint_uA_show, NULL),
+	__ATTR(current_limits, 0444, regulator_constraint_uV_show, NULL),
+	__ATTR(valid_opmodes, 0444, regulator_constraint_modes_show, NULL),
+	__ATTR(total_load, 0444, regulator_total_dev_load, NULL),
+	__ATTR(enabled_count, 0444, regulator_enabled_use_count, NULL),
+	__ATTR(type, 0444, regulator_type_show, NULL),
+	__ATTR_NULL,
+};
+
+static void regulator_dev_release(struct class_device *class_dev) {}
+
+struct class regulator_class = {
+	.name			= "regulator",
+	.release		= regulator_dev_release,
+	.class_dev_attrs	= regulator_dev_attrs,
+};
+
+/* find the lowest stable voltage that all enabled clients can operate at */
+static int get_lowest_stable_voltage(struct regulator_cdev *rcdev)
+{
+	struct regulator *regulator;
+	int highest_uV = 0;
+
+	list_for_each_entry(regulator, &rcdev->consumer_list, list) {
+		if (regulator->enabled && regulator->uV_required > highest_uV)
+			highest_uV = regulator->uV_required;
+	}
+	return highest_uV;
+}
+
+/* set the regulator voltage to the lowest possible value that can safely
+ * support all the client devices */
+static int regulator_set_stable_voltage(struct regulator_cdev *rcdev)
+{
+	int ret = 0, uV;
+
+	if (rcdev->desc->type == REGULATOR_VOLTAGE) {
+		uV = get_lowest_stable_voltage(rcdev);
+		if (uV && rcdev->desc->ops->set_voltage)
+			ret = rcdev->desc->ops->set_voltage(rcdev, uV);
+	}
+	return ret;
+}
+
+static void regulator_load_change (struct regulator_cdev *rcdev)
+{
+	struct regulator *sibling;
+	int current_uA = 0, output_uV, input_uV, err;
+	unsigned int mode;
+
+	err = regulator_check_drms(rcdev);
+	if (err < 0 || !rcdev->desc->ops->get_optimum_mode ||
+		!rcdev->desc->ops->get_voltage || !rcdev->desc->ops->set_mode);
+		return;
+
+	/* get output voltage */
+	output_uV = rcdev->desc->ops->get_voltage(rcdev);
+
+	/* get input voltage */
+	if (rcdev->parent && rcdev->parent->desc->ops->get_voltage)
+		input_uV = rcdev->parent->desc->ops->get_voltage(rcdev->parent);
+	else
+		input_uV = rcdev->constraints->input_uV;
+
+	/* calc total requested load */
+	list_for_each_entry(sibling, &rcdev->consumer_list, list)
+		current_uA += sibling->uA_load;
+
+	/* now get the optimum mode for our new total regulator load */
+	mode = rcdev->desc->ops->get_optimum_mode(rcdev, input_uV,
+		output_uV, current_uA);
+
+	/* check the new mode is allowed */
+	err = regulator_check_mode(rcdev, mode);
+	if (err == 0)
+		rcdev->desc->ops->set_mode(rcdev, mode);
+}
+
+static void print_constraints(struct regulator_cdev *rcdev)
+{
+	struct regulation_constraints *constraints = rcdev->constraints;
+	char buf[80];
+	int count;
+
+	if (rcdev->desc->type == REGULATOR_VOLTAGE) {
+		if (constraints->min_uV == constraints->max_uV)
+			count = sprintf(buf, "%d mV ",
+				uV_to_mV(constraints->min_uV));
+		else
+			count = sprintf(buf, "%d <--> %d mV ",
+				uV_to_mV(constraints->min_uV),
+				uV_to_mV(constraints->max_uV));
+	} else {
+		if (constraints->min_uA == constraints->max_uA)
+			count = sprintf(buf, "%d mA ",
+				uA_to_mA(constraints->min_uA));
+		else
+			count = sprintf(buf, "%d <--> %d mA ",
+				uA_to_mA(constraints->min_uA),
+				uA_to_mA(constraints->max_uA));
+	}
+	if (constraints->valid_modes_mask & REGULATOR_MODE_FAST)
+		count += sprintf (buf + count, "fast ");
+	if (constraints->valid_modes_mask & REGULATOR_MODE_NORMAL)
+		count += sprintf (buf + count, "normal ");
+	if (constraints->valid_modes_mask & REGULATOR_MODE_IDLE)
+		count += sprintf (buf + count, "idle ");
+	if (constraints->valid_modes_mask & REGULATOR_MODE_STANDBY)
+		count += sprintf (buf + count, "standby");
+
+	printk(KERN_INFO "regulator: %s: %s\n", rcdev->desc->name, buf);
+}
+
+static struct regulator *create_regulator(struct regulator_cdev *rcdev,
+	struct device *dev)
+{
+	struct regulator *regulator;
+	char buf[32];
+	int err;
+
+	regulator = kzalloc(sizeof(*regulator), GFP_KERNEL);
+	if (regulator == NULL)
+		return NULL;
+
+	regulator->rcdev = rcdev;
+	sprintf(buf, "current_requested-%s", regulator->rcdev->desc->name);
+	list_add(&regulator->list, &rcdev->consumer_list);
+
+	if (dev == NULL)
+		goto out;
+
+	regulator->dev = dev;
+	regulator->dev_attr.attr.name = kstrdup(buf, GFP_KERNEL);
+	if (regulator->dev_attr.attr.name == NULL)
+		goto err_out;
+	regulator->dev_attr.attr.owner = THIS_MODULE;
+	regulator->dev_attr.attr.mode = 0444;
+	regulator->dev_attr.show = dev_load_show;
+	err = device_create_file(dev, &regulator->dev_attr);
+	if (err < 0) {
+		printk(KERN_WARNING "%s: could not add regulator_cdev load"
+		" sysfs\n", __func__);
+		goto err_out;
+	}
+	err = sysfs_create_link(&rcdev->cdev.kobj, &dev->kobj,
+		dev->kobj.name);
+	if (err) {
+		printk(KERN_WARNING "%s : could not add device link %s err %d\n",
+			__func__, dev->kobj.name, err);
+		goto err_out;
+	}
+out:
+	return regulator;
+err_out:
+	kfree(regulator->dev_attr.attr.name);
+	list_del(&regulator->list);
+	kfree(regulator);
+	return NULL;
+}
+
+struct regulator *regulator_get(struct device *dev, const char *id)
+{
+	struct regulator_cdev *rcdev;
+	struct regulator *regulator = ERR_PTR(-ENODEV);
+
+	if (id == NULL)
+		return regulator;
+
+	mutex_lock(&list_mutex);
+	list_for_each_entry(rcdev, &regulator_cdevs, list) {
+		if (strcmp(id, rcdev->desc->name) == 0 &&
+			try_module_get(rcdev->owner)) {
+			goto found;
+		}
+	}
+	printk(KERN_ERR "regulator: Unable to get requested regulator: %s\n", id);
+	mutex_unlock(&list_mutex);
+	return regulator;
+
+found:
+	regulator = create_regulator(rcdev, dev);
+	if (regulator == NULL) {
+		regulator = ERR_PTR(-ENOMEM);
+		module_put(rcdev->owner);
+	}
+
+	mutex_unlock(&list_mutex);
+	return regulator;
+}
+EXPORT_SYMBOL_GPL(regulator_get);
+
+void regulator_put(struct regulator *regulator)
+{
+	struct regulator_cdev *rcdev = regulator->rcdev;
+
+	if (regulator == NULL || IS_ERR(regulator))
+		return;
+
+	/* remove any sysfs entries */
+	if (regulator->dev) {
+		sysfs_remove_link(&rcdev->cdev.kobj, regulator->dev->kobj.name);
+		device_remove_file(regulator->dev, &regulator->dev_attr);
+	}
+	list_del(&regulator->list);
+	kfree(regulator->dev_attr.attr.name);
+	kfree(regulator);
+
+	module_put(rcdev->owner);
+	mutex_unlock(&list_mutex);
+}
+EXPORT_SYMBOL_GPL(regulator_put);
+
+static int _regulator_enable(struct regulator_cdev *rcdev)
+{
+	int ret = 0;
+
+	/* if the regulator is currently disabled make sure we check
+	 * it's parent, mode and required voltage */
+	if (rcdev->use_count == 0) {
+		if (rcdev->parent) {
+			ret = _regulator_enable(rcdev->parent);
+			if (ret < 0) {
+				printk(KERN_ERR "%s: failed to enable %s\n",
+					__func__, rcdev->desc->name);
+				goto out;
+			}
+		}
+		if (rcdev->desc->ops->enable) {
+			ret = regulator_set_stable_voltage(rcdev);
+			if (ret < 0) {
+				printk(KERN_ERR "%s: invalid voltage for %s\n",
+					__func__, rcdev->desc->name);
+				goto out;
+			}
+			regulator_load_change(rcdev);
+			ret = rcdev->desc->ops->enable(rcdev);
+			if (ret < 0) {
+				printk(KERN_ERR "%s: failed to enable %s\n",
+					__func__, rcdev->desc->name);
+				goto out;
+			}
+		}
+	} else {
+		ret = regulator_set_stable_voltage(rcdev);
+		if (ret < 0) {
+			printk(KERN_ERR "%s: invalid voltage for %s\n",
+				__func__, rcdev->desc->name);
+			goto out;
+		}
+		regulator_load_change(rcdev);
+	}
+	rcdev->use_count++;
+out:
+	return ret;
+}
+
+int regulator_enable(struct regulator *regulator)
+{
+	int ret;
+
+	mutex_lock(&regulator->rcdev->mutex);
+	regulator->enabled = 1;
+	ret = _regulator_enable(regulator->rcdev);
+	if (ret < 0)
+		regulator->enabled = 0;
+	mutex_unlock(&regulator->rcdev->mutex);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(regulator_enable);
+
+static int _regulator_disable(struct regulator_cdev *rcdev)
+{
+	int ret = 0;
+
+	/* make sure we are the last user before disabling this regulator */
+	if (rcdev->use_count == 1) {
+		if (rcdev->desc->ops->disable) {
+			ret = rcdev->desc->ops->disable(rcdev);
+			if (ret < 0) {
+				printk(KERN_ERR "%s: failed to disable %s\n",
+					__func__, rcdev->desc->name);
+				goto out;
+			}
+		}
+		/* decrease parent ref count and disable if required */
+		if (rcdev->parent)
+			_regulator_disable(rcdev->parent);
+	} else {
+		/* set to next best requested voltage and mode */
+		ret = regulator_set_stable_voltage(rcdev);
+		regulator_load_change(rcdev);
+	}
+	rcdev->use_count--;
+out:
+	if (rcdev->use_count < 0) {
+		printk(KERN_ERR "%s: tried to disable too many times\n",
+			__func__);
+		rcdev->use_count = 0;
+	}
+	return ret;
+}
+
+int regulator_disable(struct regulator *regulator)
+{
+	int ret;
+
+	mutex_lock(&regulator->rcdev->mutex);
+	regulator->enabled = 0;
+	regulator->uA_load = 0;
+	ret = _regulator_disable(regulator->rcdev);
+	if (ret < 0)
+		regulator->enabled = 1;
+	mutex_unlock(&regulator->rcdev->mutex);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(regulator_disable);
+
+static int _regulator_is_enabled(struct regulator_cdev *rcdev)
+{
+	int ret;
+
+	mutex_lock(&rcdev->mutex);
+
+	/* sanity check */
+	if (!rcdev->desc->ops->is_enabled) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	ret = rcdev->desc->ops->is_enabled(rcdev);
+out:
+	mutex_unlock(&rcdev->mutex);
+	return ret;
+}
+
+int regulator_is_enabled(struct regulator *regulator)
+{
+	return _regulator_is_enabled(regulator->rcdev);
+}
+EXPORT_SYMBOL_GPL(regulator_is_enabled);
+
+int regulator_set_voltage(struct regulator *regulator, int uV)
+{
+	struct regulator_cdev *rcdev = regulator->rcdev;
+	int ret;
+
+	mutex_lock(&rcdev->mutex);
+
+	/* sanity check */
+	if (!rcdev->desc->ops->set_voltage) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	/* constraints check */
+	ret = regulator_check_voltage(rcdev, uV);
+	if (ret < 0)
+		goto out;
+	regulator->uV_required = uV;
+
+	/* only set the new voltage if it's all clients can use it */
+	ret = regulator_set_stable_voltage(rcdev);
+out:
+	mutex_unlock(&rcdev->mutex);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(regulator_set_voltage);
+
+static int _regulator_get_voltage(struct regulator_cdev *rcdev)
+{
+	int ret;
+
+	mutex_lock(&rcdev->mutex);
+
+	/* sanity check */
+	if (!rcdev->desc->ops->get_voltage) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	ret = rcdev->desc->ops->get_voltage(rcdev);
+out:
+	mutex_unlock(&rcdev->mutex);
+	return ret;
+}
+
+int regulator_get_voltage(struct regulator *regulator)
+{
+	return _regulator_get_voltage(regulator->rcdev);
+}
+EXPORT_SYMBOL_GPL(regulator_get_voltage);
+
+int regulator_set_current(struct regulator *regulator, int uA)
+{
+	struct regulator_cdev *rcdev = regulator->rcdev;
+	int ret;
+
+	mutex_lock(&rcdev->mutex);
+
+	/* sanity check */
+	if (!rcdev->desc->ops->set_current) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	/* constraints check */
+	ret = regulator_check_current(rcdev, uA);
+	if (ret < 0)
+		goto out;
+
+	ret = rcdev->desc->ops->set_current(rcdev, uA);
+out:
+	mutex_unlock(&rcdev->mutex);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(regulator_set_current);
+
+static int _regulator_get_current(struct regulator_cdev *rcdev)
+{
+	int ret;
+
+	mutex_lock(&rcdev->mutex);
+
+	/* sanity check */
+	if (!rcdev->desc->ops->get_current) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	ret = rcdev->desc->ops->get_current(rcdev);
+out:
+	mutex_unlock(&rcdev->mutex);
+	return ret;
+}
+
+int regulator_get_current(struct regulator *regulator)
+{
+	return _regulator_get_current(regulator->rcdev);
+}
+EXPORT_SYMBOL_GPL(regulator_get_current);
+
+int regulator_set_mode(struct regulator *regulator, unsigned int mode)
+{
+	struct regulator_cdev *rcdev = regulator->rcdev;
+	int ret;
+
+	mutex_lock(&rcdev->mutex);
+
+	/* sanity check */
+	if (!rcdev->desc->ops->set_mode) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	/* constraints check */
+	ret = regulator_check_mode(rcdev, mode);
+	if (ret < 0)
+		goto out;
+
+	ret = rcdev->desc->ops->set_mode(rcdev, mode);
+out:
+	mutex_unlock(&rcdev->mutex);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(regulator_set_mode);
+
+static unsigned int _regulator_get_mode(struct regulator_cdev *rcdev)
+{
+	int ret;
+
+	mutex_lock(&rcdev->mutex);
+
+	/* sanity check */
+	if (!rcdev->desc->ops->get_mode) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	ret = rcdev->desc->ops->get_mode(rcdev);
+out:
+	mutex_unlock(&rcdev->mutex);
+	return ret;
+}
+
+unsigned int regulator_get_mode(struct regulator *regulator)
+{
+	return _regulator_get_mode(regulator->rcdev);
+}
+EXPORT_SYMBOL_GPL(regulator_get_mode);
+
+static unsigned int _regulator_get_optimum_mode(struct regulator_cdev *rcdev,
+	int input_uV, int output_uV, int load_uA)
+{
+	int ret;
+
+	mutex_lock(&rcdev->mutex);
+
+	/* sanity check */
+	if (!rcdev->desc->ops->get_optimum_mode) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	ret = rcdev->desc->ops->get_optimum_mode(rcdev,
+		input_uV, output_uV, load_uA);
+out:
+	mutex_unlock(&rcdev->mutex);
+	return ret;
+}
+
+unsigned int regulator_get_optimum_mode(struct regulator *regulator,
+	int input_uV, int output_uV, int load_uA)
+{
+	return _regulator_get_optimum_mode(regulator->rcdev, input_uV,
+		output_uV, load_uA);
+}
+EXPORT_SYMBOL_GPL(regulator_get_optimum_mode);
+
+int regulator_register_client(struct regulator *regulator,
+	struct notifier_block *nb)
+{
+	return blocking_notifier_chain_register(&regulator->rcdev->notifier, nb);
+}
+EXPORT_SYMBOL_GPL(regulator_register_client);
+
+int regulator_unregister_client(struct regulator *regulator,
+	struct notifier_block *nb)
+{
+	return blocking_notifier_chain_unregister(&regulator->rcdev->notifier, nb);
+}
+EXPORT_SYMBOL_GPL(regulator_unregister_client);
+
+int regulator_notifier_call_chain(struct regulator_cdev *rcdev,
+	unsigned long event, void *data)
+{
+	return blocking_notifier_call_chain(&rcdev->notifier, event, data);
+}
+EXPORT_SYMBOL_GPL(regulator_notifier_call_chain);
+
+struct regulator_cdev *regulator_register(
+	struct regulator_desc *regulator_desc, void *reg_data)
+{
+	static atomic_t regulator_no = ATOMIC_INIT(0);
+	struct regulator_cdev *rcdev;
+	int ret;
+
+	if (regulator_desc == NULL)
+		return ERR_PTR(-EINVAL);
+
+	if (regulator_desc->name == NULL || regulator_desc->ops == NULL)
+		return ERR_PTR(-EINVAL);
+
+	if (!regulator_desc->type == REGULATOR_VOLTAGE &&
+		!regulator_desc->type == REGULATOR_CURRENT)
+		return ERR_PTR(-EINVAL);
+
+	rcdev = kzalloc(sizeof(struct regulator_cdev), GFP_KERNEL);
+	if (rcdev == NULL)
+		return ERR_PTR(-ENOMEM);
+
+	mutex_lock(&list_mutex);
+
+	mutex_init(&rcdev->mutex);
+	rcdev->reg_data = reg_data;
+	rcdev->owner = regulator_desc->owner;
+	rcdev->desc = regulator_desc;
+	INIT_LIST_HEAD(&rcdev->consumer_list);
+	INIT_LIST_HEAD(&rcdev->list);
+	BLOCKING_INIT_NOTIFIER_HEAD(&rcdev->notifier);
+
+	rcdev->cdev.class = &regulator_class;
+	class_device_initialize(&rcdev->cdev);
+	snprintf(rcdev->cdev.class_id, sizeof(rcdev->cdev.class_id),
+		"regulator-%ld-%s",
+		(unsigned long) atomic_inc_return(&regulator_no) - 1,
+		regulator_desc->name);
+
+	ret = class_device_add(&rcdev->cdev);
+	if (ret == 0)
+		list_add(&rcdev->list, &regulator_cdevs);
+	else {
+		kfree(rcdev);
+		rcdev = ERR_PTR(ret);
+	}
+	mutex_unlock(&list_mutex);
+	return rcdev;
+}
+EXPORT_SYMBOL_GPL(regulator_register);
+
+void regulator_unregister(struct regulator_cdev *rcdev)
+{
+	if (rcdev == NULL)
+		return;
+
+	mutex_lock(&list_mutex);
+	list_del(&rcdev->list);
+	if (rcdev->parent)
+		sysfs_remove_link(&rcdev->cdev.kobj, "source");
+	class_device_unregister(&rcdev->cdev);
+	kfree(rcdev);
+	mutex_unlock(&list_mutex);
+}
+EXPORT_SYMBOL_GPL(regulator_unregister);
+
+int regulator_set_platform_source(const char *regulator_source,
+	const char *regulator_parent)
+{
+	struct regulator_cdev *source_rcdev, *parent_rcdev;
+	int err;
+
+	if (regulator_source == NULL || regulator_parent == NULL)
+		return -EINVAL;
+
+	mutex_lock(&list_mutex);
+
+	list_for_each_entry(source_rcdev, &regulator_cdevs, list) {
+		if (!strcmp(source_rcdev->desc->name, regulator_source))
+			goto found_source;
+	}
+	mutex_unlock(&list_mutex);
+	return -ENODEV;
+
+found_source:
+	list_for_each_entry(parent_rcdev, &regulator_cdevs, list) {
+		if (!strcmp(parent_rcdev->desc->name, regulator_parent))
+			goto found_parent;
+	}
+	mutex_unlock(&list_mutex);
+	return -ENODEV;
+
+found_parent:
+	source_rcdev->parent = parent_rcdev;
+	err = sysfs_create_link(&source_rcdev->cdev.kobj, &parent_rcdev->cdev.kobj,
+		"source");
+	if (err)
+		printk(KERN_WARNING "%s : could not add device link %s err %d\n",
+			__func__, parent_rcdev->cdev.kobj.name, err);
+	mutex_unlock(&list_mutex);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(regulator_set_platform_source);
+
+const char *regulator_get_platform_source(const char *regulator_name)
+{
+	struct regulator_cdev *rcdev;
+
+	if (regulator_name == NULL)
+		return NULL;
+
+	mutex_lock(&list_mutex);
+	list_for_each_entry(rcdev, &regulator_cdevs, list) {
+		if (!strcmp(rcdev->desc->name, regulator_name))
+			goto found;
+	}
+	mutex_unlock(&list_mutex);
+	return NULL;
+
+found:
+	mutex_unlock(&list_mutex);
+	if (rcdev->parent)
+		return rcdev->parent->desc->name;
+	else
+		return NULL;
+}
+EXPORT_SYMBOL_GPL(regulator_get_platform_source);
+
+int regulator_set_platform_constraints(const char *regulator_name,
+	struct regulation_constraints *constraints)
+{
+	struct regulator_cdev *rcdev;
+
+	if (regulator_name == NULL)
+		return -EINVAL;
+
+	mutex_lock(&list_mutex);
+	list_for_each_entry(rcdev, &regulator_cdevs, list) {
+		if (!strcmp(regulator_name, rcdev->desc->name)) {
+			mutex_lock(&rcdev->mutex);
+			rcdev->constraints = constraints;
+			print_constraints(rcdev);
+			mutex_unlock(&rcdev->mutex);
+			mutex_unlock(&list_mutex);
+			return 0;
+		}
+	}
+	mutex_unlock(&list_mutex);
+	return -ENODEV;
+}
+EXPORT_SYMBOL_GPL(regulator_set_platform_constraints);
+
+void regulator_drms_notify_load(struct regulator *regulator, int uA)
+{
+	struct regulator_cdev *rcdev = regulator->rcdev;
+
+	mutex_lock(&rcdev->mutex);
+	regulator->uA_load = uA;
+	regulator_load_change (rcdev);
+	mutex_unlock(&rcdev->mutex);
+}
+EXPORT_SYMBOL_GPL(regulator_drms_notify_load);
+
+void *regulator_get_drvdata(struct regulator *regulator)
+{
+	return regulator->rcdev->reg_data;
+}
+EXPORT_SYMBOL_GPL(regulator_get_drvdata);
+
+void regulator_set_drvdata(struct regulator *regulator, void *data)
+{
+	regulator->rcdev->reg_data = data;
+}
+EXPORT_SYMBOL_GPL(regulator_set_drvdata);
+
+int rcdev_get_id (struct regulator_cdev *rcdev)
+{
+	return rcdev->desc->id;
+}
+EXPORT_SYMBOL_GPL(rcdev_get_id);
+
+void *rcdev_get_drvdata (struct regulator_cdev *rcdev)
+{
+	return rcdev->reg_data;
+}
+EXPORT_SYMBOL_GPL(rcdev_get_drvdata);
+
+static int __init regulator_init(void)
+{
+	printk(KERN_INFO "regulator: core version %s\n", REGULATOR_VERSION);
+	return class_register(&regulator_class);
+}
+
+/* init early to allow our consumers to complete system booting */
+core_initcall(regulator_init);
diff --git a/drivers/regulator/wm8350/Kconfig b/drivers/regulator/wm8350/Kconfig
new file mode 100644
index 0000000..9a98f88
--- /dev/null
+++ b/drivers/regulator/wm8350/Kconfig
@@ -0,0 +1,31 @@
+menu "Wolfson Microelectronics WM8350 PMIC"
+
+config REGULATOR_WM8350
+	bool
+	
+config PMIC_WM8350_MODE_0
+	bool
+
+config PMIC_WM8350_MODE_1
+	bool
+	
+config PMIC_WM8350_MODE_2
+	bool
+	
+config PMIC_WM8350_MODE_3
+	bool
+	
+config IMX32ADS_WM8350_PMU
+	bool "WOLFSON WM8350 IMX32ADS board PMU support"
+	depends on REGULATOR
+	select REGULATOR_WM8350
+	select I2C_MXC_SELECT2
+	select PMIC_WM8350_MODE_0
+	help
+	  If you say yes here you get support for the
+	  Wolfon WM8350 PMIC chip on the Freescale IMX32ADS.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called imx32ads-wm8350.
+
+endmenu
diff --git a/drivers/regulator/wm8350/Makefile b/drivers/regulator/wm8350/Makefile
new file mode 100644
index 0000000..bdcb357
--- /dev/null
+++ b/drivers/regulator/wm8350/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for WM8350 drivers.
+#
+
+obj-$(CONFIG_REGULATOR_WM8350) += reg-wm8350.o reg-wm8350-bus.o
+obj-$(CONFIG_IMX32ADS_WM8350_PMU) += imx32ads-wm8350.o
\ No newline at end of file
diff --git a/drivers/regulator/wm8350/imx32ads-wm8350.c b/drivers/regulator/wm8350/imx32ads-wm8350.c
new file mode 100644
index 0000000..bfa904a
--- /dev/null
+++ b/drivers/regulator/wm8350/imx32ads-wm8350.c
@@ -0,0 +1,394 @@
+/*
+ * imx32ads-wm8350.c  --  i.MX32ADS Board Driver for Wolfson WM8350 PMIC
+ *
+ * This driver defines and configures the WM8350 for the Freescale i.MX32ADS.
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC.
+ *
+ * Author: Liam Girdwood
+ *         liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ *  Revision history
+ *    23rd Jan 2007   Initial version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/bitops.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/regulator/regulator-platform.h>
+#include <linux/regulator/wm8350/pmic.h>
+#include <linux/regulator/wm8350/gpio.h>
+#include <linux/regulator/wm8350/bus.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/leds.h>
+#include <linux/fb.h>
+
+#include <asm/arch/dma.h>
+#include <asm/arch/spba.h>
+#include <asm/arch/clock.h>
+
+#define WM8350_IMX32ADS_VERSION "0.5"
+
+/*
+ * Set to 1 when testing battery that is connected otherwise spuriuos debug
+ */
+#define BATTERY 0
+
+static int wm8350_pmic_i2c_detect (struct i2c_adapter *adap, int addr, int kind);
+static struct platform_device *imx32ads_snd_device;
+
+#if BATTERY
+static int wm8350_init_battery(struct wm8350* wm8350)
+{
+	struct wm8350_power *power = &wm8350->power;
+	struct wm8350_charger_policy *policy = &power->policy;
+
+	policy->eoc_mA = WM8350_CHG_EOC_mA(10);
+	policy->charge_mV = WM8350_CHG_4_05V;
+	policy->fast_limit_mA = WM8350_CHG_FAST_LIMIT_mA(400);
+	policy->charge_timeout = WM8350_CHG_TIME_MIN(wm8350->rev, 60);
+	policy->trickle_start_mV = WM8350_CHG_TRICKLE_3_1V;
+	policy->trickle_charge_mA = WM8350_CHG_TRICKLE_50mA;
+
+	wm8350_charger_enable(power, 1);
+	wm8350_fast_charger_enable(power, 1);
+	return 0;
+}
+#endif
+
+/* this could be hooked up to ON/OFF switches */
+static void imx32ads_switch_handler(struct wm8350 *wm8350, int irq, void *data)
+{
+	printk("switch pressed %d\n", irq);
+}
+
+static int config_board_gpios(struct wm8350* wm8350)
+{
+	/* power on */
+	wm8350_gpio_config(wm8350, 0, WM8350_GPIO_DIR_IN,
+		WM8350_GPIO0_PWR_ON_IN, WM8350_GPIO_ACTIVE_LOW,
+		WM8350_GPIO_PULL_UP, WM8350_GPIO_INVERT_OFF,
+		WM8350_GPIO_DEBOUNCE_ON);
+
+	/* Sw3 --> PWR_OFF_GPIO3 */
+	/* lg - TODO: GPIO1_0 to be pulled down */
+	wm8350_gpio_config(wm8350, 3, WM8350_GPIO_DIR_IN,
+		WM8350_GPIO3_PWR_OFF_IN, WM8350_GPIO_ACTIVE_HIGH,
+		WM8350_GPIO_PULL_DOWN, WM8350_GPIO_INVERT_OFF,
+		WM8350_GPIO_DEBOUNCE_ON);
+
+	/* MR or MEMRST ????? */
+	wm8350_gpio_config(wm8350, 4, WM8350_GPIO_DIR_IN,
+		WM8350_GPIO4_MR_IN, WM8350_GPIO_ACTIVE_HIGH,
+		WM8350_GPIO_PULL_DOWN, WM8350_GPIO_INVERT_OFF,
+		WM8350_GPIO_DEBOUNCE_OFF);
+
+	/* Hibernate -- GPIO 7 */
+	wm8350_gpio_config(wm8350, 7, WM8350_GPIO_DIR_IN,
+		WM8350_GPIO7_HIBERNATE_IN, WM8350_GPIO_ACTIVE_HIGH,
+		WM8350_GPIO_PULL_DOWN, WM8350_GPIO_INVERT_OFF,
+		WM8350_GPIO_DEBOUNCE_OFF);
+
+	/* SDOUT */
+	wm8350_gpio_config(wm8350, 6, WM8350_GPIO_DIR_OUT,
+		WM8350_GPIO6_SDOUT_OUT, WM8350_GPIO_ACTIVE_HIGH,
+		WM8350_GPIO_PULL_NONE, WM8350_GPIO_INVERT_OFF,
+		WM8350_GPIO_DEBOUNCE_OFF);
+
+	/* GPIO switch SW2 */
+	wm8350_gpio_config(wm8350, 7, WM8350_GPIO_DIR_IN, WM8350_GPIO7_GPIO_IN,
+		WM8350_GPIO_ACTIVE_HIGH, WM8350_GPIO_PULL_DOWN,
+		WM8350_GPIO_INVERT_OFF, WM8350_GPIO_DEBOUNCE_ON);
+	wm8350_register_irq(wm8350, WM8350_IRQ_GPIO(7),
+			    imx32ads_switch_handler, NULL);
+	wm8350_unmask_irq(wm8350, WM8350_IRQ_GPIO(7));
+
+	/* PWR_FAIL */
+	wm8350_gpio_config(wm8350, 8, WM8350_GPIO_DIR_OUT,
+		WM8350_GPIO8_VCC_FAULT_OUT, WM8350_GPIO_ACTIVE_LOW,
+		WM8350_GPIO_PULL_NONE, WM8350_GPIO_INVERT_OFF,
+		WM8350_GPIO_DEBOUNCE_OFF);
+
+	/* BATT Fault */
+	wm8350_gpio_config(wm8350, 9, WM8350_GPIO_DIR_OUT,
+		WM8350_GPIO9_BATT_FAULT_OUT, WM8350_GPIO_ACTIVE_LOW,
+		WM8350_GPIO_PULL_NONE, WM8350_GPIO_INVERT_OFF,
+		WM8350_GPIO_DEBOUNCE_OFF);
+
+	return 0;
+}
+
+static int config_board_hibernate(struct wm8350* wm8350)
+{
+	struct wm8350_pmic *pmic = &wm8350->pmic;
+
+	/* dont assert RTS when hibernating */
+	wm8350_set_bits(wm8350, WM8350_SYSTEM_HIBERNATE, WM8350_RST_HIB_MODE);
+
+	/* set up hibernate voltages -- needs refining for each board */
+	wm8350_dcdc_set_image_voltage(pmic, WM8350_DCDC_1, 1400,
+		WM8350_DCDC_HIB_MODE_IMAGE, WM8350_DCDC_HIB_SIG_REG);
+	wm8350_dcdc_set_image_voltage(pmic, WM8350_DCDC_3, 2800,
+		WM8350_DCDC_HIB_MODE_IMAGE, WM8350_DCDC_HIB_SIG_REG);
+	wm8350_dcdc_set_image_voltage(pmic, WM8350_DCDC_4, 1800,
+		WM8350_DCDC_HIB_MODE_IMAGE, WM8350_DCDC_HIB_SIG_REG);
+	wm8350_dcdc_set_image_voltage(pmic, WM8350_DCDC_6, 1800,
+		WM8350_DCDC_HIB_MODE_IMAGE, WM8350_DCDC_HIB_SIG_REG);
+	wm8350_ldo_set_image_voltage(pmic, WM8350_LDO_1, 2800,
+		WM8350_LDO_HIB_MODE_IMAGE, WM8350_LDO_HIB_SIG_REG);
+	wm8350_ldo_set_image_voltage(pmic, WM8350_LDO_2, 3300,
+		WM8350_LDO_HIB_MODE_IMAGE, WM8350_LDO_HIB_SIG_REG);
+	wm8350_ldo_set_image_voltage(pmic, WM8350_LDO_3, 1500,
+		WM8350_LDO_HIB_MODE_IMAGE, WM8350_LDO_HIB_SIG_REG);
+	wm8350_ldo_set_image_voltage(pmic, WM8350_LDO_4, 2500,
+		WM8350_LDO_HIB_MODE_IMAGE, WM8350_LDO_HIB_MODE_DIS);
+	return 0;
+}
+
+struct regulation_constraints led = {
+	.min_uA		= 0,
+	.max_uA		= 27898,
+	.valid_ops_mask	= REGULATOR_CHANGE_CURRENT,
+	.valid_modes_mask	= REGULATOR_MODE_NORMAL,
+};
+
+struct regulation_constraints cpufreq = {
+	.min_uV		= mV_to_uV(1300),
+	.max_uV		= mV_to_uV(1600),
+	.valid_ops_mask	= REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE,
+	.valid_modes_mask	= REGULATOR_MODE_NORMAL | REGULATOR_MODE_FAST,
+};
+
+struct regulation_constraints avdd = {
+	.min_uV		= mV_to_uV(3300),
+	.max_uV		= mV_to_uV(3300),
+	.valid_ops_mask	= REGULATOR_CHANGE_VOLTAGE,
+	.valid_modes_mask	= REGULATOR_MODE_NORMAL,
+};
+
+static void set_regulator_constraints(struct wm8350 *wm8350)
+{
+	regulator_set_platform_constraints("DCDC1", &cpufreq);
+	regulator_set_platform_constraints("ISINKA", &led);
+	regulator_set_platform_constraints("LDO2", &avdd);
+}
+
+/*
+ * WM8350 2 wire address
+ */
+#define WM8350_I2C_ADDR         (0x34 >> 1)
+static unsigned short normal_i2c[] = { WM8350_I2C_ADDR, I2C_CLIENT_END };
+
+/* Magic definition of all other variables and things */
+I2C_CLIENT_INSMOD;
+
+static struct i2c_driver wm8350_i2c_driver;
+static struct i2c_client client_template;
+
+static void wm8350_irq_work(struct work_struct *work)
+{
+	wm8350_irq_worker(work);
+}
+
+static irqreturn_t wm8350_irq_handler(int irq, void *data)
+{
+	struct wm8350 *wm8350 = (struct wm8350*)data;
+
+	schedule_work(&wm8350->work);
+	return IRQ_HANDLED;
+}
+
+static int wm8350_i2c_detach(struct i2c_client *client)
+{
+	struct wm8350 *wm8350 = i2c_get_clientdata(client);
+
+#if BATTERY
+	struct wm8350_power *power = &wm8350->power;
+#endif
+	wm8350_mask_irq(wm8350, WM8350_IRQ_GPIO(7));
+	wm8350_free_irq(wm8350, WM8350_IRQ_GPIO(7));
+	wm8350_mask_irq(wm8350, WM8350_IRQ_WKUP_ONKEY);
+	wm8350_free_irq(wm8350, WM8350_IRQ_WKUP_ONKEY);
+
+#if BATTERY
+	wm8350_charger_enable(power, 0);
+	wm8350_fast_charger_enable(power, 0);
+#endif
+	if (wm8350->nirq)
+		free_irq(wm8350->nirq, wm8350);
+
+	flush_scheduled_work();
+
+	/* unregister any client devices */
+	if (wm8350->rtc.dev.is_registered)
+		device_unregister(&wm8350->rtc.dev);
+	if (wm8350->wdg.dev.is_registered)
+		device_unregister(&wm8350->wdg.dev);
+	if (wm8350->power.dev.is_registered)
+		device_unregister(&wm8350->power.dev);
+	platform_device_unregister(imx32ads_snd_device);
+
+	wm8350_pmu_exit(wm8350);
+
+	i2c_detach_client(client);
+	kfree(client);
+	return 0;
+}
+
+static int wm8350_i2c_attach(struct i2c_adapter *adap)
+{
+	return i2c_probe(adap, &addr_data, wm8350_pmic_i2c_detect);
+}
+
+#define I2C_DRIVERID_WM8350 0xfefe /* TODO: liam need proper id */
+
+static struct i2c_driver wm8350_i2c_driver = {
+	.driver = {
+		.name = "WM8350",
+		.owner = THIS_MODULE,
+	},
+	.id =             I2C_DRIVERID_WM8350,
+	.attach_adapter = wm8350_i2c_attach,
+	.detach_client =  wm8350_i2c_detach,
+	.command =        NULL,
+};
+
+static struct i2c_client client_template = {
+	.name =   "WM8350",
+	.driver = &wm8350_i2c_driver,
+};
+
+static int wm8350_init(struct wm8350* wm8350)
+{
+	int ret;
+
+	printk("wm8350: i.MX32ADS client driver version %s\n",
+		WM8350_IMX32ADS_VERSION);
+
+	/* register regulator and set constraints */
+	ret = wm8350_pmu_init(wm8350);
+	if (ret < 0) {
+		printk(KERN_ERR "%s: failed to initialise device\n", __func__);
+		goto err;
+	}
+
+	set_regulator_constraints(wm8350);
+
+	/* now register other clients */
+	wm8350_device_register_rtc(wm8350);
+	wm8350_device_register_wdg(wm8350);
+	wm8350_device_register_power(wm8350);
+
+	/* register sound */
+	imx32ads_snd_device = platform_device_alloc("imx31ads-audio", -1);
+	if (!imx32ads_snd_device) {
+		ret = -ENOMEM;
+		goto err;
+	}
+	platform_set_drvdata(imx32ads_snd_device, wm8350);
+	ret = platform_device_add(imx32ads_snd_device);
+	if (ret)
+		goto snd_err;
+
+	/* set up PMIC IRQ (active high) to i.MX32ADS */
+	INIT_WORK(&wm8350->work, wm8350_irq_work);
+	wm8350_reg_unlock(wm8350);
+	wm8350_set_bits(wm8350, WM8350_SYSTEM_CONTROL_1, WM8350_IRQ_POL);
+	wm8350_reg_lock(wm8350);
+	set_irq_type(IOMUX_TO_IRQ(MX31_PIN_GPIO1_3), IRQT_RISING);
+	ret = request_irq(IOMUX_TO_IRQ(MX31_PIN_GPIO1_3), wm8350_irq_handler,
+		IRQF_DISABLED, "wm8350-pmic", wm8350);
+	if (ret != 0) {
+		printk(KERN_ERR "wm8350: cant request irq %d\n",
+			INT_EXT_POWER);
+		goto err;
+	}
+	wm8350->nirq = IOMUX_TO_IRQ(MX31_PIN_GPIO1_3);
+
+	config_board_gpios(wm8350);
+	config_board_hibernate(wm8350);
+
+	/* Sw1 --> PWR_ON */
+	wm8350_register_irq(wm8350, WM8350_IRQ_WKUP_ONKEY,
+			    imx32ads_switch_handler, NULL);
+	wm8350_unmask_irq(wm8350, WM8350_IRQ_WKUP_ONKEY);
+
+	/* unmask all & clear sticky */
+	wm8350_reg_write(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK, 0x0);
+	schedule_work(&wm8350->work);
+
+#if BATTERY
+	/* not much use without a battery atm */
+	wm8350_init_battery(wm8350);
+#endif
+
+	return ret;
+snd_err:
+	platform_device_put(imx32ads_snd_device);
+	wm8350_pmu_exit(wm8350);
+err:
+	return ret;
+}
+
+static int wm8350_pmic_i2c_detect (struct i2c_adapter *adap, int addr, int kind)
+{
+	struct wm8350* wm8350;
+	struct i2c_client *i2c;
+	int ret = 0;
+
+	if (addr != WM8350_I2C_ADDR)
+		return -ENODEV;
+
+	client_template.adapter = adap;
+	client_template.addr = addr;
+
+	i2c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
+	if (i2c == NULL)
+		return -ENOMEM;
+
+	wm8350 = kzalloc(sizeof(struct wm8350), GFP_KERNEL);
+	if (wm8350 == NULL) {
+		kfree(i2c);
+		return -ENOMEM;
+	}
+
+	memcpy(i2c, &client_template, sizeof(struct i2c_client));
+	mutex_init(&wm8350->work_mutex);
+	i2c_set_clientdata(i2c, wm8350);
+	wm8350->i2c_client = i2c;
+	wm8350_set_io(wm8350, WM8350_IO_I2C, NULL, NULL);
+
+	ret = i2c_attach_client(i2c);
+	if (ret < 0) {
+		printk(KERN_ERR "wm8350: failed to attach device at addr 0x%x\n", addr);
+		goto err;
+	}
+	ret = wm8350_init(wm8350);
+	if (ret == 0)
+		return ret;
+
+err:
+	wm8350_i2c_detach(i2c);
+	return ret;
+}
+
+static int __init imx32ads_wm8350_pmic_init(void)
+{
+	return i2c_add_driver(&wm8350_i2c_driver);
+}
+
+/* init early so consumer devices can complete system boot */
+subsys_initcall(imx32ads_wm8350_pmic_init);
diff --git a/drivers/regulator/wm8350/reg-wm8350-bus.c b/drivers/regulator/wm8350/reg-wm8350-bus.c
new file mode 100644
index 0000000..6e2bcf4
--- /dev/null
+++ b/drivers/regulator/wm8350/reg-wm8350-bus.c
@@ -0,0 +1,1442 @@
+/*
+ * wm8350_bus.c  --  Power Management Driver for Wolfson WM8350 PMIC
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC.
+ *
+ * Author: Liam Girdwood
+ *         liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ *  Revision history
+ *    23rd Jan 2007   Initial version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+#include <linux/regulator/wm8350/cache.h>
+#include <linux/regulator/wm8350/bus.h>
+#include <linux/regulator/wm8350/pmic.h>
+#include <linux/regulator/wm8350/gpio.h>
+#include <linux/regulator/wm8350/comparator.h>
+#include <linux/regulator/wm8350/supply.h>
+#include <linux/regulator/wm8350/audio.h>
+#include <linux/delay.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 dbg(format, arg...) printk(format, ## arg)
+#define dump(regs, src) do { \
+	int i; \
+	u16 *src_ = src; \
+	for (i = 0; i < regs; i++) \
+		dbg(" 0x%4.4x", *src_++); \
+	dbg("\n"); \
+} while (0);
+#else
+#define dbg(format, arg...)
+#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
+
+#define BYTE_SWAP16(x) (x >> 8 | x << 8)  // lg replace with generic
+
+/*
+ * WM8350 can run in 1 of 4 configuration modes.
+ * Each mode has different default register values.
+ */
+#if defined(CONFIG_PMIC_WM8350_MODE_0)
+static const u16 wm8350_reg_map[] = WM8350_REGISTER_DEFAULTS_0;
+#elif defined(CONFIG_PMIC_WM8350_MODE_1)
+static const u16 wm8350_reg_map[] = WM8350_REGISTER_DEFAULTS_1;
+#elif defined(CONFIG_PMIC_WM8350_MODE_2)
+static const u16 wm8350_reg_map[] = WM8350_REGISTER_DEFAULTS_2;
+#elif defined(CONFIG_PMIC_WM8350_MODE_3)
+static const u16 wm8350_reg_map[] = WM8350_REGISTER_DEFAULTS_3;
+#else
+#error Invalid WM8350 configuration
+#endif
+
+/*
+ * WM8350 Register IO access map
+ */
+static const struct wm8350_reg_access wm8350_reg_io_map[] = WM8350_ACCESS;
+
+/*
+ * WM8350 Device IO
+ */
+static DEFINE_MUTEX(io_mutex);
+static DEFINE_MUTEX(reg_lock_mutex);
+static DEFINE_MUTEX(auxadc_mutex);
+
+#ifdef CONFIG_I2C
+static int wm8350_read_i2c_device(struct wm8350 *wm8350, char reg,
+	int bytes, char *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_write_i2c_device(struct wm8350 *wm8350, char reg,
+	int bytes, char *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);
+}
+#endif
+
+#ifdef CONFIG_SPI
+static int wm8350_read_spi_device(struct wm8350 *wm8350, char reg,
+	int bytes, char *dest)
+{
+	int ret;
+	u8 tx_msg[4], rx_msg[4];
+
+	/* don't support incremental write with SPI */
+	if (bytes != 2)
+		return -EIO;
+
+	tx_msg[0] = 0x80;
+	tx_msg[1] = reg;
+	tx_msg[2] = 0;
+	tx_msg[3] = 0;
+
+	ret = spi_write_then_read(wm8350->spi_device, tx_msg, 4, rx_msg, 4);
+	if (ret < 0) {
+		printk(KERN_ERR "%s: io failure %d\n", __func__, ret);
+		return 0;
+	}
+
+	*dest++ = rx_msg[2];
+	*dest = rx_msg[3];
+	return 0;
+}
+
+static int wm8350_write_spi_device(struct wm8350 *wm8350, char reg,
+	int bytes, char *src)
+{
+	u8 msg[4];
+
+	/* don't support incremental write with SPI */
+	if (bytes != 2)
+		return -EIO;
+
+	msg[0] = 0;
+	msg[1] = reg;
+	msg[2] = *src++;
+	msg[3] = *src;
+	return spi_write(wm8350->spi_device, msg, 4);
+}
+#endif
+
+/* mask in WM8350 read bits */
+static inline void wm8350_mask_read(u8 reg, int bytes, u16 *buf)
+{
+	int i;
+
+	for (i = reg; i < reg + (bytes >> 1); i++)
+		*buf++ &= wm8350_reg_io_map[i].readable;
+}
+
+/* mask in WM8350 write bits */
+static inline void wm8350_mask_write(u8 reg, int bytes, u16 *buf)
+{
+	int i;
+
+	for (i = reg; i < reg + (bytes >> 1); i++)
+		*buf++ &= wm8350_reg_io_map[i].writable;
+}
+
+/* WM8350 is big endian, swap if necessary */
+static inline void wm8350_endian_swap(u8 reg, int bytes, u16 *buf)
+{
+#ifdef  __LITTLE_ENDIAN
+	int i;
+	u16 tmp;
+
+	for (i = reg; i < reg + (bytes >> 1); i++) {
+		tmp = BYTE_SWAP16(*buf);
+		*buf++ = tmp;
+	}
+#endif
+}
+
+static inline void wm8350_cache_mask(struct wm8350 *wm8350, u8 reg,
+	int bytes, u16 *dest)
+{
+	int i;
+	u16 mask;
+
+	for (i = reg; i < reg + (bytes >> 1); i++) {
+		*dest &= wm8350_reg_io_map[i].vol;
+		mask = wm8350->reg_cache[reg] & ~wm8350_reg_io_map[i].vol;
+		*dest |= mask;
+		dest++;
+	}
+}
+
+static int wm8350_read(struct wm8350 *wm8350, u8 reg, int num_regs, u16 *dest)
+{
+	int i, end = reg + num_regs, ret = 0, bytes = num_regs << 1;
+
+	if (wm8350->read_dev == NULL)
+		return -ENODEV;
+
+	if ((reg + num_regs - 1) > WM8350_MAX_REGISTER) {
+		printk(KERN_ERR "wm8350: invalid reg %x\n", reg + num_regs - 1);
+		return -EINVAL;
+	}
+
+	dbg("%s R%d(0x%2.2x) %d regs ", __FUNCTION__, 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)
+			printk(KERN_WARNING "wm8350: 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) {
+			dbg("volatile read ");
+			ret = wm8350->read_dev(wm8350, reg,
+				bytes, (char*)dest);
+			wm8350_endian_swap(reg, bytes, dest);
+			wm8350_cache_mask(wm8350, reg, bytes, dest);
+			wm8350_mask_read(reg, bytes, dest);
+			dump(num_regs, dest);
+			return ret;
+		}
+	}
+
+	/* no volatiles, then cache is good */
+	dbg("cache read ");
+	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 ret, i, end = reg + num_regs, bytes = num_regs << 1;
+
+	if (wm8350->write_dev == NULL)
+		return -ENODEV;
+
+	if ((reg + num_regs - 1) > WM8350_MAX_REGISTER) {
+		printk(KERN_ERR "wm8350: invalid reg %x\n", reg + num_regs - 1);
+		return -EINVAL;
+	}
+
+	wm8350_mask_write(reg, bytes, src);
+	memcpy(&wm8350->reg_cache[reg], src, bytes);
+	dbg("%s R%d(0x%2.2x) %d regs ", __FUNCTION__, reg, reg, num_regs);
+	dump(num_regs, src);
+
+	wm8350_endian_swap(reg, bytes, src);
+
+	/* 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) {
+			printk(KERN_ERR "wm8350: attempted write to read only reg R%d\n", i);
+			return -EINVAL;
+		}
+
+		if (is_reg_locked(wm8350, i)) {
+			printk(KERN_ERR "wm8350: attempted write to locked reg R%d\n", i);
+			return -EINVAL;
+		}
+	}
+
+	/* write registers and update cache if successful */
+	ret = wm8350->write_dev(wm8350, reg, bytes, (char*)src);
+	return ret;
+}
+
+/*
+ * 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) {
+		printk(KERN_ERR "wm8350: read from reg R%d failed\n", reg);
+		goto out;
+	}
+
+	data &= ~mask;
+	err = wm8350_write(wm8350, reg, 1, &data);
+	if (err)
+		printk(KERN_ERR "wm8350: 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) {
+		printk(KERN_ERR "wm8350: read from reg R%d failed\n", reg);
+		goto out;
+	}
+
+	data |= mask;
+	err = wm8350_write(wm8350, reg, 1, &data);
+	if (err)
+		printk(KERN_ERR "wm8350: 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)
+		printk(KERN_ERR "wm8350: 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)
+		printk(KERN_ERR "wm8350: 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)
+		printk(KERN_ERR "wm8350: 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)
+		printk(KERN_ERR "wm8350: 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 ("%s\n", __FUNCTION__);
+	mutex_lock(&io_mutex);
+	ret = wm8350_write(wm8350, WM8350_SECURITY, 1, &key);
+	if (ret)
+		printk(KERN_ERR "wm8350: 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 ("%s\n", __FUNCTION__);
+	mutex_lock(&io_mutex);
+	ret = wm8350_write(wm8350, WM8350_SECURITY, 1, &key);
+	if (ret)
+		printk(KERN_ERR "wm8350: unlock failed\n");
+	mutex_unlock(&io_mutex);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wm8350_reg_unlock);
+
+/*
+ * For Switching between SPI and I2C IO
+ */
+int wm8350_set_io(struct wm8350 *wm8350, int io, wm8350_hw_read_t read_dev,
+	wm8350_hw_write_t write_dev)
+{
+	mutex_lock(&io_mutex);
+	switch (io) {
+	case WM8350_IO_I2C:
+#ifdef CONFIG_I2C
+		wm8350->read_dev = wm8350_read_i2c_device;
+		wm8350->write_dev = wm8350_write_i2c_device;
+		break;
+#else
+		printk(KERN_ERR "wm8350: I2C not selected.\n");
+		wm8350->read_dev = NULL;
+		wm8350->write_dev = NULL;
+		mutex_unlock(&io_mutex);
+		return -EINVAL;
+#endif
+	case WM8350_IO_SPI:
+#ifdef CONFIG_SPI
+		wm8350->read_dev = wm8350_read_spi_device;
+		wm8350->write_dev = wm8350_write_spi_device;
+		break;
+#else
+		printk(KERN_ERR "wm8350: SPI not selected.\n");
+		wm8350->read_dev = NULL;
+		wm8350->write_dev = NULL;
+		mutex_unlock(&io_mutex);
+		return -EINVAL;
+#endif
+	default:
+		printk(KERN_ERR "wm8350: invalid IO mechanism\n");
+		wm8350->read_dev = NULL;
+		wm8350->write_dev = NULL;
+		mutex_unlock(&io_mutex);
+		return -EINVAL;
+	}
+	mutex_unlock(&io_mutex);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(wm8350_set_io);
+
+/*
+ * Cache is always host endian.
+ */
+int wm8350_create_cache(struct wm8350 *wm8350)
+{
+	int i, ret = 0;
+	u16 value;
+
+	if (wm8350->read_dev == NULL)
+		return -ENODEV;
+
+	wm8350->reg_cache =
+		kzalloc(sizeof(u16) * (WM8350_MAX_REGISTER + 1), GFP_KERNEL);
+	if (wm8350->reg_cache == NULL)
+		return -ENOMEM;
+
+	/* TODO: check if we are virgin state so we don't have to do this */
+	/* refresh cache with chip regs as some registers can survive reboot */
+	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) {
+				printk(KERN_ERR
+				"wm8350: failed to create cache\n");
+				goto out;
+			}
+			wm8350_endian_swap(i, 2, &value);
+			wm8350_mask_read(i, 2, &value);
+			wm8350->reg_cache[i] = value;
+		} else
+			wm8350->reg_cache[i] = wm8350_reg_map[i];
+	}
+
+out:
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wm8350_create_cache);
+
+
+static void wm8350_irq_call_worker(struct wm8350 *wm8350, int irq)
+{
+	mutex_lock(&wm8350->work_mutex);
+
+	if (wm8350->irq[irq].handler)
+		wm8350->irq[irq].handler(wm8350, irq, wm8350->irq[irq].data);
+	else {
+		mutex_unlock(&wm8350->work_mutex);
+		printk(KERN_ERR "wm8350: irq %d nobody cared. now masked.\n",
+			irq);
+		wm8350_mask_irq(wm8350, irq);
+		return;
+	}
+	mutex_unlock(&wm8350->work_mutex);
+}
+
+void wm8350_irq_worker(struct work_struct *work)
+{
+	u16 level_one, status1, status2, comp, oc, gpio, uv;
+	struct wm8350 *wm8350 =
+		container_of(work, struct wm8350, work);
+
+	/* read this in 1 block read */
+	/* read 1st level irq sources and then read required 2nd sources */
+	level_one = wm8350_reg_read(wm8350, WM8350_SYSTEM_INTERRUPTS)
+		& ~wm8350_reg_read(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK);
+	uv = wm8350_reg_read(wm8350, WM8350_UNDER_VOLTAGE_INTERRUPT_STATUS)
+		& ~wm8350_reg_read(wm8350, WM8350_UNDER_VOLTAGE_INTERRUPT_STATUS_MASK);
+	oc = wm8350_reg_read(wm8350, WM8350_OVER_CURRENT_INTERRUPT_STATUS)
+		& ~wm8350_reg_read(wm8350, WM8350_OVER_CURRENT_INTERRUPT_STATUS_MASK);
+	status1 = wm8350_reg_read(wm8350, WM8350_INTERRUPT_STATUS_1)
+		& ~wm8350_reg_read(wm8350, WM8350_INTERRUPT_STATUS_1_MASK);
+	status2 = wm8350_reg_read(wm8350, WM8350_INTERRUPT_STATUS_2)
+		& ~wm8350_reg_read(wm8350, WM8350_INTERRUPT_STATUS_2_MASK);
+	comp = wm8350_reg_read(wm8350, WM8350_COMPARATOR_INTERRUPT_STATUS)
+		& ~wm8350_reg_read(wm8350, WM8350_COMPARATOR_INTERRUPT_STATUS_MASK);
+	gpio = wm8350_reg_read(wm8350, WM8350_GPIO_INTERRUPT_STATUS)
+		& ~wm8350_reg_read(wm8350, WM8350_GPIO_INTERRUPT_STATUS_MASK);
+
+	/* over current */
+	if (level_one & WM8350_OC_INT) {
+		if (oc & WM8350_OC_LS_EINT) /* limit switch */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_OC_LS);
+	}
+
+	/* under voltage */
+	if (level_one & WM8350_UV_INT) {
+		if (uv & WM8350_UV_DC1_EINT) /* DCDC1 */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_UV_DC1);
+		if (uv & WM8350_UV_DC2_EINT) /* DCDC2 */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_UV_DC2);
+		if (uv & WM8350_UV_DC3_EINT) /* DCDC3 */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_UV_DC3);
+		if (uv & WM8350_UV_DC4_EINT) /* DCDC4 */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_UV_DC4);
+		if (uv & WM8350_UV_DC5_EINT) /* DCDC5 */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_UV_DC5);
+		if (uv & WM8350_UV_DC6_EINT) /* DCDC6 */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_UV_DC6);
+		if (uv & WM8350_UV_LDO1_EINT) /* LDO1 */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_UV_LDO1);
+		if (uv & WM8350_UV_LDO2_EINT) /* LDO2 */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_UV_LDO2);
+		if (uv & WM8350_UV_LDO3_EINT) /* LDO3 */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_UV_LDO3);
+		if (uv & WM8350_UV_LDO4_EINT) /* LDO4 */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_UV_LDO4);
+	}
+
+	/* charger, RTC */
+	if (status1) {
+		if (status1 & WM8350_CHG_BAT_HOT_EINT) /* battery too hot */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_CHG_BAT_HOT);
+		if (status1 & WM8350_CHG_BAT_COLD_EINT) /* battery too cold */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_CHG_BAT_COLD);
+		if (status1 & WM8350_CHG_BAT_FAIL_EINT) /* battery fail */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_CHG_BAT_FAIL);
+		if (status1 & WM8350_CHG_TO_EINT) /* charger timeout */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_CHG_TO);
+		if (status1 & WM8350_CHG_END_EINT) /* fast charge current */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_CHG_END);
+		if (status1 & WM8350_CHG_START_EINT) /* charging started */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_CHG_START);
+		if (status1 & WM8350_CHG_FAST_RDY_EINT) /* fast charge ready */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_CHG_FAST_RDY);
+		if (status1 & WM8350_CHG_VBATT_LT_3P9_EINT) /* battery voltage < 3.9 */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9);
+		if (status1 & WM8350_CHG_VBATT_LT_3P1_EINT) /* battery voltage < 3.1 */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1);
+		if (status1 & WM8350_CHG_VBATT_LT_2P85_EINT) /* battery voltage < 2.85 */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85);
+
+		if (status1 & WM8350_RTC_ALM_EINT) /* alarm */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_RTC_ALM);
+		if (status1 & WM8350_RTC_SEC_EINT) /* second rollover */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_RTC_SEC);
+		if (status1 & WM8350_RTC_PER_EINT) /* periodic */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_RTC_PER);
+	}
+
+	/* current sink, system, aux adc */
+	if (status2) {
+		if (status2 & WM8350_CS1_EINT) /* current sink 1 */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_CS1);
+		if (status2 & WM8350_CS2_EINT) /* current sink 2 */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_CS2);
+
+		if (status2 & WM8350_SYS_HYST_COMP_FAIL_EINT) /* comp fail */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_SYS_HYST_COMP_FAIL);
+		if (status2 & WM8350_SYS_CHIP_GT115_EINT) /* chip > 115 C */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_SYS_CHIP_GT115);
+		if (status2 & WM8350_SYS_CHIP_GT140_EINT) /* chip > 140 C */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_SYS_CHIP_GT140);
+		if (status2 & WM8350_SYS_WDOG_TO_EINT) /* heartbeat missed */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_SYS_WDOG_TO);
+
+		if (status2 & WM8350_AUXADC_DATARDY_EINT) /* data ready */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_AUXADC_DATARDY);
+		if (status2 & WM8350_AUXADC_DCOMP4_EINT) /* exceeds comp 4 */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_AUXADC_DCOMP4);
+		if (status2 & WM8350_AUXADC_DCOMP3_EINT) /* exceeds comp 3 */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_AUXADC_DCOMP3);
+		if (status2 & WM8350_AUXADC_DCOMP2_EINT) /* exceeds comp 2 */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_AUXADC_DCOMP2);
+		if (status2 & WM8350_AUXADC_DCOMP1_EINT) /* exceeds comp 1 */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_AUXADC_DCOMP1);
+
+		if (status2 & WM8350_USB_LIMIT_EINT) /* usb limit */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_USB_LIMIT);
+	}
+
+	/* wake, codec, ext */
+	if (comp) {
+		if (comp & WM8350_WKUP_OFF_STATE_EINT) /* wake from off */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_WKUP_OFF_STATE);
+		if (comp & WM8350_WKUP_HIB_STATE_EINT) /* wake from hibernate */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_WKUP_HIB_STATE);
+		if (comp & WM8350_WKUP_CONV_FAULT_EINT) /* wake from fault */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_WKUP_CONV_FAULT);
+		if (comp & WM8350_WKUP_WDOG_RST_EINT) /* wake from reset */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_WKUP_WDOG_RST);
+		if (comp & WM8350_WKUP_GP_PWR_ON_EINT) /* power on changed */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_WKUP_GP_PWR_ON);
+		if (comp & WM8350_WKUP_ONKEY_EINT) /* on key > specified time */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_WKUP_ONKEY);
+		if (comp & WM8350_WKUP_GP_WAKEUP_EINT) /* wake from GPIO */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_WKUP_GP_WAKEUP);
+
+		if (comp & WM8350_CODEC_JCK_DET_L_EINT) /* left chn Jack detect */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_CODEC_JCK_DET_L);
+		if (comp & WM8350_CODEC_JCK_DET_R_EINT) /* right chn Jack detect */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_CODEC_JCK_DET_R);
+		if (comp & WM8350_CODEC_MICSCD_EINT) /* mic detect */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_CODEC_MICSCD);
+		if (comp & WM8350_CODEC_MICD_EINT) /* mic short circuit */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_CODEC_MICD);
+
+		if (comp & WM8350_EXT_USB_FB_EINT) /* usb connect */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_EXT_USB_FB);
+		if (comp & WM8350_EXT_WALL_FB_EINT) /* wall adaptor connect */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_EXT_WALL_FB);
+		if (comp & WM8350_EXT_BAT_FB_EINT) /* battery insertion */
+			wm8350_irq_call_worker(wm8350, WM8350_IRQ_EXT_BAT_FB);
+	}
+
+	if (level_one & WM8350_GP_INT) { /* gpio */
+		int i;
+
+		for (i = 0; i < 12; i++) {
+			if (gpio & (1 << i))
+				wm8350_irq_call_worker(wm8350, WM8350_IRQ_GPIO(i));
+		}
+	}
+}
+EXPORT_SYMBOL_GPL(wm8350_irq_worker);
+
+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->work_mutex);
+	wm8350->irq[irq].handler = handler;
+	wm8350->irq[irq].data = data;
+	mutex_unlock(&wm8350->work_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->work_mutex);
+	wm8350->irq[irq].handler = NULL;
+	mutex_unlock(&wm8350->work_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_INTERRUPT_STATUS_1_MASK,
+			WM8350_IM_CHG_BAT_HOT_EINT);
+	case WM8350_IRQ_CHG_BAT_COLD:
+		return wm8350_set_bits(wm8350, WM8350_INTERRUPT_STATUS_1_MASK,
+			WM8350_IM_CHG_BAT_COLD_EINT);
+	case WM8350_IRQ_CHG_BAT_FAIL:
+		return wm8350_set_bits(wm8350, WM8350_INTERRUPT_STATUS_1_MASK,
+			WM8350_IM_CHG_BAT_FAIL_EINT);
+	case WM8350_IRQ_CHG_TO:
+		return wm8350_set_bits(wm8350, WM8350_INTERRUPT_STATUS_1_MASK,
+			WM8350_IM_CHG_TO_EINT);
+	case WM8350_IRQ_CHG_END:
+		return wm8350_set_bits(wm8350, WM8350_INTERRUPT_STATUS_1_MASK,
+			WM8350_IM_CHG_END_EINT);
+	case WM8350_IRQ_CHG_START:
+		return wm8350_set_bits(wm8350, WM8350_INTERRUPT_STATUS_1_MASK,
+			WM8350_IM_CHG_START_EINT);
+	case WM8350_IRQ_CHG_FAST_RDY:
+		return wm8350_set_bits(wm8350, WM8350_INTERRUPT_STATUS_1_MASK,
+			WM8350_IM_CHG_FAST_RDY_EINT);
+	case WM8350_IRQ_RTC_PER:
+		return wm8350_set_bits(wm8350, WM8350_INTERRUPT_STATUS_1_MASK,
+			WM8350_IM_RTC_PER_EINT);
+	case WM8350_IRQ_RTC_SEC:
+		return wm8350_set_bits(wm8350, WM8350_INTERRUPT_STATUS_1_MASK,
+			WM8350_IM_RTC_SEC_EINT);
+	case WM8350_IRQ_RTC_ALM:
+		return wm8350_set_bits(wm8350, WM8350_INTERRUPT_STATUS_1_MASK,
+			WM8350_IM_RTC_ALM_EINT);
+	case WM8350_IRQ_CHG_VBATT_LT_3P9:
+		return wm8350_set_bits(wm8350, WM8350_INTERRUPT_STATUS_1_MASK,
+			WM8350_IM_CHG_VBATT_LT_3P9_EINT);
+	case WM8350_IRQ_CHG_VBATT_LT_3P1:
+		return wm8350_set_bits(wm8350, WM8350_INTERRUPT_STATUS_1_MASK,
+			WM8350_IM_CHG_VBATT_LT_3P1_EINT);
+	case WM8350_IRQ_CHG_VBATT_LT_2P85:
+		return wm8350_set_bits(wm8350, WM8350_INTERRUPT_STATUS_1_MASK,
+			WM8350_IM_CHG_VBATT_LT_2P85_EINT);
+	case WM8350_IRQ_CS1:
+		return wm8350_set_bits(wm8350, WM8350_INTERRUPT_STATUS_2_MASK,
+			WM8350_IM_CS1_EINT);
+	case WM8350_IRQ_CS2:
+		return wm8350_set_bits(wm8350, WM8350_INTERRUPT_STATUS_2_MASK,
+			WM8350_IM_CS2_EINT);
+	case WM8350_IRQ_USB_LIMIT:
+		return wm8350_set_bits(wm8350, WM8350_INTERRUPT_STATUS_2_MASK,
+			WM8350_IM_USB_LIMIT_EINT);
+	case WM8350_IRQ_AUXADC_DATARDY:
+		return wm8350_set_bits(wm8350, WM8350_INTERRUPT_STATUS_2_MASK,
+			WM8350_IM_AUXADC_DATARDY_EINT);
+	case WM8350_IRQ_AUXADC_DCOMP4:
+		return wm8350_set_bits(wm8350, WM8350_INTERRUPT_STATUS_2_MASK,
+			WM8350_IM_AUXADC_DCOMP4_EINT);
+	case WM8350_IRQ_AUXADC_DCOMP3:
+		return wm8350_set_bits(wm8350, WM8350_INTERRUPT_STATUS_2_MASK,
+			WM8350_IM_AUXADC_DCOMP3_EINT);
+	case WM8350_IRQ_AUXADC_DCOMP2:
+		return wm8350_set_bits(wm8350, WM8350_INTERRUPT_STATUS_2_MASK,
+			WM8350_IM_AUXADC_DCOMP2_EINT);
+	case WM8350_IRQ_AUXADC_DCOMP1:
+		return wm8350_set_bits(wm8350, WM8350_INTERRUPT_STATUS_2_MASK,
+			WM8350_IM_AUXADC_DCOMP1_EINT);
+	case WM8350_IRQ_SYS_HYST_COMP_FAIL:
+		return wm8350_set_bits(wm8350, WM8350_INTERRUPT_STATUS_2_MASK,
+			WM8350_IM_SYS_HYST_COMP_FAIL_EINT);
+	case WM8350_IRQ_SYS_CHIP_GT115:
+		return wm8350_set_bits(wm8350, WM8350_INTERRUPT_STATUS_2_MASK,
+			WM8350_IM_SYS_CHIP_GT115_EINT);
+	case WM8350_IRQ_SYS_CHIP_GT140:
+		return wm8350_set_bits(wm8350, WM8350_INTERRUPT_STATUS_2_MASK,
+			WM8350_IM_SYS_CHIP_GT140_EINT);
+	case WM8350_IRQ_SYS_WDOG_TO:
+		return wm8350_set_bits(wm8350, WM8350_INTERRUPT_STATUS_2_MASK,
+			WM8350_IM_SYS_WDOG_TO_EINT);
+	case WM8350_IRQ_UV_LDO4:
+		return wm8350_set_bits(wm8350, WM8350_UNDER_VOLTAGE_INTERRUPT_STATUS_MASK,
+			WM8350_IM_UV_LDO4_EINT);
+	case WM8350_IRQ_UV_LDO3:
+		return wm8350_set_bits(wm8350, WM8350_UNDER_VOLTAGE_INTERRUPT_STATUS_MASK,
+			WM8350_IM_UV_LDO3_EINT);
+	case WM8350_IRQ_UV_LDO2:
+		return wm8350_set_bits(wm8350, WM8350_UNDER_VOLTAGE_INTERRUPT_STATUS_MASK,
+			WM8350_IM_UV_LDO2_EINT);
+	case WM8350_IRQ_UV_LDO1:
+		return wm8350_set_bits(wm8350, WM8350_UNDER_VOLTAGE_INTERRUPT_STATUS_MASK,
+			WM8350_IM_UV_LDO1_EINT);
+	case WM8350_IRQ_UV_DC6:
+		return wm8350_set_bits(wm8350, WM8350_UNDER_VOLTAGE_INTERRUPT_STATUS_MASK,
+			WM8350_IM_UV_DC6_EINT);
+	case WM8350_IRQ_UV_DC5:
+		return wm8350_set_bits(wm8350, WM8350_UNDER_VOLTAGE_INTERRUPT_STATUS_MASK,
+			WM8350_IM_UV_DC5_EINT);
+	case WM8350_IRQ_UV_DC4:
+		return wm8350_set_bits(wm8350, WM8350_UNDER_VOLTAGE_INTERRUPT_STATUS_MASK,
+			WM8350_IM_UV_DC4_EINT);
+	case WM8350_IRQ_UV_DC3:
+		return wm8350_set_bits(wm8350, WM8350_UNDER_VOLTAGE_INTERRUPT_STATUS_MASK,
+			WM8350_IM_UV_DC3_EINT);
+	case WM8350_IRQ_UV_DC2:
+		return wm8350_set_bits(wm8350, WM8350_UNDER_VOLTAGE_INTERRUPT_STATUS_MASK,
+			WM8350_IM_UV_DC2_EINT);
+	case WM8350_IRQ_UV_DC1:
+		return wm8350_set_bits(wm8350, WM8350_UNDER_VOLTAGE_INTERRUPT_STATUS_MASK,
+			WM8350_IM_UV_DC1_EINT);
+	case WM8350_IRQ_OC_LS:
+		return wm8350_set_bits(wm8350, WM8350_OVER_CURRENT_INTERRUPT_STATUS_MASK,
+			WM8350_IM_OC_LS_EINT);
+	case WM8350_IRQ_EXT_USB_FB:
+		return wm8350_set_bits(wm8350, WM8350_COMPARATOR_INTERRUPT_STATUS_MASK,
+			WM8350_IM_EXT_USB_FB_EINT);
+	case WM8350_IRQ_EXT_WALL_FB:
+		return wm8350_set_bits(wm8350, WM8350_COMPARATOR_INTERRUPT_STATUS_MASK,
+			WM8350_IM_EXT_WALL_FB_EINT);
+	case WM8350_IRQ_EXT_BAT_FB:
+		return wm8350_set_bits(wm8350, WM8350_COMPARATOR_INTERRUPT_STATUS_MASK,
+			WM8350_IM_EXT_BAT_FB_EINT);
+	case WM8350_IRQ_CODEC_JCK_DET_L:
+		return wm8350_set_bits(wm8350, WM8350_COMPARATOR_INTERRUPT_STATUS_MASK,
+			WM8350_IM_CODEC_JCK_DET_L_EINT);
+	case WM8350_IRQ_CODEC_JCK_DET_R:
+		return wm8350_set_bits(wm8350, WM8350_COMPARATOR_INTERRUPT_STATUS_MASK,
+			WM8350_IM_CODEC_JCK_DET_R_EINT);
+	case WM8350_IRQ_CODEC_MICSCD:
+		return wm8350_set_bits(wm8350, WM8350_COMPARATOR_INTERRUPT_STATUS_MASK,
+			WM8350_IM_CODEC_MICSCD_EINT);
+	case WM8350_IRQ_CODEC_MICD:
+		return wm8350_set_bits(wm8350, WM8350_COMPARATOR_INTERRUPT_STATUS_MASK,
+			WM8350_IM_CODEC_MICD_EINT);
+	case WM8350_IRQ_WKUP_OFF_STATE:
+		return wm8350_set_bits(wm8350, WM8350_COMPARATOR_INTERRUPT_STATUS_MASK,
+			WM8350_IM_WKUP_OFF_STATE_EINT);
+	case WM8350_IRQ_WKUP_HIB_STATE:
+		return wm8350_set_bits(wm8350, WM8350_COMPARATOR_INTERRUPT_STATUS_MASK,
+			WM8350_IM_WKUP_HIB_STATE_EINT);
+	case WM8350_IRQ_WKUP_CONV_FAULT:
+		return wm8350_set_bits(wm8350, WM8350_COMPARATOR_INTERRUPT_STATUS_MASK,
+			WM8350_IM_WKUP_CONV_FAULT_EINT);
+	case WM8350_IRQ_WKUP_WDOG_RST:
+		return wm8350_set_bits(wm8350, WM8350_COMPARATOR_INTERRUPT_STATUS_MASK,
+			WM8350_IM_WKUP_OFF_STATE_EINT);
+	case WM8350_IRQ_WKUP_GP_PWR_ON:
+		return wm8350_set_bits(wm8350, WM8350_COMPARATOR_INTERRUPT_STATUS_MASK,
+			WM8350_IM_WKUP_GP_PWR_ON_EINT);
+	case WM8350_IRQ_WKUP_ONKEY:
+		return wm8350_set_bits(wm8350, WM8350_COMPARATOR_INTERRUPT_STATUS_MASK,
+			WM8350_IM_WKUP_ONKEY_EINT);
+	case WM8350_IRQ_WKUP_GP_WAKEUP:
+		return wm8350_set_bits(wm8350, WM8350_COMPARATOR_INTERRUPT_STATUS_MASK,
+			WM8350_IM_WKUP_GP_WAKEUP_EINT);
+	case WM8350_IRQ_GPIO(0):
+		return wm8350_set_bits(wm8350, WM8350_GPIO_INTERRUPT_STATUS_MASK,
+			WM8350_IM_GP0_EINT);
+	case WM8350_IRQ_GPIO(1):
+		return wm8350_set_bits(wm8350, WM8350_GPIO_INTERRUPT_STATUS_MASK,
+			WM8350_IM_GP1_EINT);
+	case WM8350_IRQ_GPIO(2):
+		return wm8350_set_bits(wm8350, WM8350_GPIO_INTERRUPT_STATUS_MASK,
+			WM8350_IM_GP2_EINT);
+	case WM8350_IRQ_GPIO(3):
+		return wm8350_set_bits(wm8350, WM8350_GPIO_INTERRUPT_STATUS_MASK,
+			WM8350_IM_GP3_EINT);
+	case WM8350_IRQ_GPIO(4):
+		return wm8350_set_bits(wm8350, WM8350_GPIO_INTERRUPT_STATUS_MASK,
+			WM8350_IM_GP4_EINT);
+	case WM8350_IRQ_GPIO(5):
+		return wm8350_set_bits(wm8350, WM8350_GPIO_INTERRUPT_STATUS_MASK,
+			WM8350_IM_GP5_EINT);
+	case WM8350_IRQ_GPIO(6):
+		return wm8350_set_bits(wm8350, WM8350_GPIO_INTERRUPT_STATUS_MASK,
+			WM8350_IM_GP6_EINT);
+	case WM8350_IRQ_GPIO(7):
+		return wm8350_set_bits(wm8350, WM8350_GPIO_INTERRUPT_STATUS_MASK,
+			WM8350_IM_GP7_EINT);
+	case WM8350_IRQ_GPIO(8):
+		return wm8350_set_bits(wm8350, WM8350_GPIO_INTERRUPT_STATUS_MASK,
+			WM8350_IM_GP8_EINT);
+	case WM8350_IRQ_GPIO(9):
+		return wm8350_set_bits(wm8350, WM8350_GPIO_INTERRUPT_STATUS_MASK,
+			WM8350_IM_GP9_EINT);
+	case WM8350_IRQ_GPIO(10):
+		return wm8350_set_bits(wm8350, WM8350_GPIO_INTERRUPT_STATUS_MASK,
+			WM8350_IM_GP10_EINT);
+	case WM8350_IRQ_GPIO(11):
+		return wm8350_set_bits(wm8350, WM8350_GPIO_INTERRUPT_STATUS_MASK,
+			WM8350_IM_GP11_EINT);
+	case WM8350_IRQ_GPIO(12):
+		return wm8350_set_bits(wm8350, WM8350_GPIO_INTERRUPT_STATUS_MASK,
+			WM8350_IM_GP12_EINT);
+	default:
+		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_INTERRUPT_STATUS_1_MASK,
+			WM8350_IM_CHG_BAT_HOT_EINT);
+	case WM8350_IRQ_CHG_BAT_COLD:
+		return wm8350_clear_bits(wm8350, WM8350_INTERRUPT_STATUS_1_MASK,
+			WM8350_IM_CHG_BAT_COLD_EINT);
+	case WM8350_IRQ_CHG_BAT_FAIL:
+		return wm8350_clear_bits(wm8350, WM8350_INTERRUPT_STATUS_1_MASK,
+			WM8350_IM_CHG_BAT_FAIL_EINT);
+	case WM8350_IRQ_CHG_TO:
+		return wm8350_clear_bits(wm8350, WM8350_INTERRUPT_STATUS_1_MASK,
+			WM8350_IM_CHG_TO_EINT);
+	case WM8350_IRQ_CHG_END:
+		return wm8350_clear_bits(wm8350, WM8350_INTERRUPT_STATUS_1_MASK,
+			WM8350_IM_CHG_END_EINT);
+	case WM8350_IRQ_CHG_START:
+		return wm8350_clear_bits(wm8350, WM8350_INTERRUPT_STATUS_1_MASK,
+			WM8350_IM_CHG_START_EINT);
+	case WM8350_IRQ_CHG_FAST_RDY:
+		return wm8350_clear_bits(wm8350, WM8350_INTERRUPT_STATUS_1_MASK,
+			WM8350_IM_CHG_FAST_RDY_EINT);
+	case WM8350_IRQ_RTC_PER:
+		return wm8350_clear_bits(wm8350, WM8350_INTERRUPT_STATUS_1_MASK,
+			WM8350_IM_RTC_PER_EINT);
+	case WM8350_IRQ_RTC_SEC:
+		return wm8350_clear_bits(wm8350, WM8350_INTERRUPT_STATUS_1_MASK,
+			WM8350_IM_RTC_SEC_EINT);
+	case WM8350_IRQ_RTC_ALM:
+		return wm8350_clear_bits(wm8350, WM8350_INTERRUPT_STATUS_1_MASK,
+			WM8350_IM_RTC_ALM_EINT);
+	case WM8350_IRQ_CHG_VBATT_LT_3P9:
+		return wm8350_clear_bits(wm8350, WM8350_INTERRUPT_STATUS_1_MASK,
+			WM8350_IM_CHG_VBATT_LT_3P9_EINT);
+	case WM8350_IRQ_CHG_VBATT_LT_3P1:
+		return wm8350_clear_bits(wm8350, WM8350_INTERRUPT_STATUS_1_MASK,
+			WM8350_IM_CHG_VBATT_LT_3P1_EINT);
+	case WM8350_IRQ_CHG_VBATT_LT_2P85:
+		return wm8350_clear_bits(wm8350, WM8350_INTERRUPT_STATUS_1_MASK,
+			WM8350_IM_CHG_VBATT_LT_2P85_EINT);
+	case WM8350_IRQ_CS1:
+		return wm8350_clear_bits(wm8350, WM8350_INTERRUPT_STATUS_2_MASK,
+			WM8350_IM_CS1_EINT);
+	case WM8350_IRQ_CS2:
+		return wm8350_clear_bits(wm8350, WM8350_INTERRUPT_STATUS_2_MASK,
+			WM8350_IM_CS2_EINT);
+	case WM8350_IRQ_USB_LIMIT:
+		return wm8350_clear_bits(wm8350, WM8350_INTERRUPT_STATUS_2_MASK,
+			WM8350_IM_USB_LIMIT_EINT);
+	case WM8350_IRQ_AUXADC_DATARDY:
+		return wm8350_clear_bits(wm8350, WM8350_INTERRUPT_STATUS_2_MASK,
+			WM8350_IM_AUXADC_DATARDY_EINT);
+	case WM8350_IRQ_AUXADC_DCOMP4:
+		return wm8350_clear_bits(wm8350, WM8350_INTERRUPT_STATUS_2_MASK,
+			WM8350_IM_AUXADC_DCOMP4_EINT);
+	case WM8350_IRQ_AUXADC_DCOMP3:
+		return wm8350_clear_bits(wm8350, WM8350_INTERRUPT_STATUS_2_MASK,
+			WM8350_IM_AUXADC_DCOMP3_EINT);
+	case WM8350_IRQ_AUXADC_DCOMP2:
+		return wm8350_clear_bits(wm8350, WM8350_INTERRUPT_STATUS_2_MASK,
+			WM8350_IM_AUXADC_DCOMP2_EINT);
+	case WM8350_IRQ_AUXADC_DCOMP1:
+		return wm8350_clear_bits(wm8350, WM8350_INTERRUPT_STATUS_2_MASK,
+			WM8350_IM_AUXADC_DCOMP1_EINT);
+	case WM8350_IRQ_SYS_HYST_COMP_FAIL:
+		return wm8350_clear_bits(wm8350, WM8350_INTERRUPT_STATUS_2_MASK,
+			WM8350_IM_SYS_HYST_COMP_FAIL_EINT);
+	case WM8350_IRQ_SYS_CHIP_GT115:
+		return wm8350_clear_bits(wm8350, WM8350_INTERRUPT_STATUS_2_MASK,
+			WM8350_IM_SYS_CHIP_GT115_EINT);
+	case WM8350_IRQ_SYS_CHIP_GT140:
+		return wm8350_clear_bits(wm8350, WM8350_INTERRUPT_STATUS_2_MASK,
+			WM8350_IM_SYS_CHIP_GT140_EINT);
+	case WM8350_IRQ_SYS_WDOG_TO:
+		return wm8350_clear_bits(wm8350, WM8350_INTERRUPT_STATUS_2_MASK,
+			WM8350_IM_SYS_WDOG_TO_EINT);
+	case WM8350_IRQ_UV_LDO4:
+		return wm8350_clear_bits(wm8350, WM8350_UNDER_VOLTAGE_INTERRUPT_STATUS_MASK,
+			WM8350_IM_UV_LDO4_EINT);
+	case WM8350_IRQ_UV_LDO3:
+		return wm8350_clear_bits(wm8350, WM8350_UNDER_VOLTAGE_INTERRUPT_STATUS_MASK,
+			WM8350_IM_UV_LDO3_EINT);
+	case WM8350_IRQ_UV_LDO2:
+		return wm8350_clear_bits(wm8350, WM8350_UNDER_VOLTAGE_INTERRUPT_STATUS_MASK,
+			WM8350_IM_UV_LDO2_EINT);
+	case WM8350_IRQ_UV_LDO1:
+		return wm8350_clear_bits(wm8350, WM8350_UNDER_VOLTAGE_INTERRUPT_STATUS_MASK,
+			WM8350_IM_UV_LDO1_EINT);
+	case WM8350_IRQ_UV_DC6:
+		return wm8350_clear_bits(wm8350, WM8350_UNDER_VOLTAGE_INTERRUPT_STATUS_MASK,
+			WM8350_IM_UV_DC6_EINT);
+	case WM8350_IRQ_UV_DC5:
+		return wm8350_clear_bits(wm8350, WM8350_UNDER_VOLTAGE_INTERRUPT_STATUS_MASK,
+			WM8350_IM_UV_DC5_EINT);
+	case WM8350_IRQ_UV_DC4:
+		return wm8350_clear_bits(wm8350, WM8350_UNDER_VOLTAGE_INTERRUPT_STATUS_MASK,
+			WM8350_IM_UV_DC4_EINT);
+	case WM8350_IRQ_UV_DC3:
+		return wm8350_clear_bits(wm8350, WM8350_UNDER_VOLTAGE_INTERRUPT_STATUS_MASK,
+			WM8350_IM_UV_DC3_EINT);
+	case WM8350_IRQ_UV_DC2:
+		return wm8350_clear_bits(wm8350, WM8350_UNDER_VOLTAGE_INTERRUPT_STATUS_MASK,
+			WM8350_IM_UV_DC2_EINT);
+	case WM8350_IRQ_UV_DC1:
+		return wm8350_clear_bits(wm8350, WM8350_UNDER_VOLTAGE_INTERRUPT_STATUS_MASK,
+			WM8350_IM_UV_DC1_EINT);
+	case WM8350_IRQ_OC_LS:
+		return wm8350_clear_bits(wm8350, WM8350_OVER_CURRENT_INTERRUPT_STATUS_MASK,
+			WM8350_IM_OC_LS_EINT);
+	case WM8350_IRQ_EXT_USB_FB:
+		return wm8350_clear_bits(wm8350, WM8350_COMPARATOR_INTERRUPT_STATUS_MASK,
+			WM8350_IM_EXT_USB_FB_EINT);
+	case WM8350_IRQ_EXT_WALL_FB:
+		return wm8350_clear_bits(wm8350, WM8350_COMPARATOR_INTERRUPT_STATUS_MASK,
+			WM8350_IM_EXT_WALL_FB_EINT);
+	case WM8350_IRQ_EXT_BAT_FB:
+		return wm8350_clear_bits(wm8350, WM8350_COMPARATOR_INTERRUPT_STATUS_MASK,
+			WM8350_IM_EXT_BAT_FB_EINT);
+	case WM8350_IRQ_CODEC_JCK_DET_L:
+		return wm8350_clear_bits(wm8350, WM8350_COMPARATOR_INTERRUPT_STATUS_MASK,
+			WM8350_IM_CODEC_JCK_DET_L_EINT);
+	case WM8350_IRQ_CODEC_JCK_DET_R:
+		return wm8350_clear_bits(wm8350, WM8350_COMPARATOR_INTERRUPT_STATUS_MASK,
+			WM8350_IM_CODEC_JCK_DET_R_EINT);
+	case WM8350_IRQ_CODEC_MICSCD:
+		return wm8350_clear_bits(wm8350, WM8350_COMPARATOR_INTERRUPT_STATUS_MASK,
+			WM8350_IM_CODEC_MICSCD_EINT);
+	case WM8350_IRQ_CODEC_MICD:
+		return wm8350_clear_bits(wm8350, WM8350_COMPARATOR_INTERRUPT_STATUS_MASK,
+			WM8350_IM_CODEC_MICD_EINT);
+	case WM8350_IRQ_WKUP_OFF_STATE:
+		return wm8350_clear_bits(wm8350, WM8350_COMPARATOR_INTERRUPT_STATUS_MASK,
+			WM8350_IM_WKUP_OFF_STATE_EINT);
+	case WM8350_IRQ_WKUP_HIB_STATE:
+		return wm8350_clear_bits(wm8350, WM8350_COMPARATOR_INTERRUPT_STATUS_MASK,
+			WM8350_IM_WKUP_HIB_STATE_EINT);
+	case WM8350_IRQ_WKUP_CONV_FAULT:
+		return wm8350_clear_bits(wm8350, WM8350_COMPARATOR_INTERRUPT_STATUS_MASK,
+			WM8350_IM_WKUP_CONV_FAULT_EINT);
+	case WM8350_IRQ_WKUP_WDOG_RST:
+		return wm8350_clear_bits(wm8350, WM8350_COMPARATOR_INTERRUPT_STATUS_MASK,
+			WM8350_IM_WKUP_OFF_STATE_EINT);
+	case WM8350_IRQ_WKUP_GP_PWR_ON:
+		return wm8350_clear_bits(wm8350, WM8350_COMPARATOR_INTERRUPT_STATUS_MASK,
+			WM8350_IM_WKUP_GP_PWR_ON_EINT);
+	case WM8350_IRQ_WKUP_ONKEY:
+		return wm8350_clear_bits(wm8350, WM8350_COMPARATOR_INTERRUPT_STATUS_MASK,
+			WM8350_IM_WKUP_ONKEY_EINT);
+	case WM8350_IRQ_WKUP_GP_WAKEUP:
+		return wm8350_clear_bits(wm8350, WM8350_COMPARATOR_INTERRUPT_STATUS_MASK,
+			WM8350_IM_WKUP_GP_WAKEUP_EINT);
+	case WM8350_IRQ_GPIO(0):
+		return wm8350_clear_bits(wm8350, WM8350_GPIO_INTERRUPT_STATUS_MASK,
+			WM8350_IM_GP0_EINT);
+	case WM8350_IRQ_GPIO(1):
+		return wm8350_clear_bits(wm8350, WM8350_GPIO_INTERRUPT_STATUS_MASK,
+			WM8350_IM_GP1_EINT);
+	case WM8350_IRQ_GPIO(2):
+		return wm8350_clear_bits(wm8350, WM8350_GPIO_INTERRUPT_STATUS_MASK,
+			WM8350_IM_GP2_EINT);
+	case WM8350_IRQ_GPIO(3):
+		return wm8350_clear_bits(wm8350, WM8350_GPIO_INTERRUPT_STATUS_MASK,
+			WM8350_IM_GP3_EINT);
+	case WM8350_IRQ_GPIO(4):
+		return wm8350_clear_bits(wm8350, WM8350_GPIO_INTERRUPT_STATUS_MASK,
+			WM8350_IM_GP4_EINT);
+	case WM8350_IRQ_GPIO(5):
+		return wm8350_clear_bits(wm8350, WM8350_GPIO_INTERRUPT_STATUS_MASK,
+			WM8350_IM_GP5_EINT);
+	case WM8350_IRQ_GPIO(6):
+		return wm8350_clear_bits(wm8350, WM8350_GPIO_INTERRUPT_STATUS_MASK,
+			WM8350_IM_GP6_EINT);
+	case WM8350_IRQ_GPIO(7):
+		return wm8350_clear_bits(wm8350, WM8350_GPIO_INTERRUPT_STATUS_MASK,
+			WM8350_IM_GP7_EINT);
+	case WM8350_IRQ_GPIO(8):
+		return wm8350_clear_bits(wm8350, WM8350_GPIO_INTERRUPT_STATUS_MASK,
+			WM8350_IM_GP8_EINT);
+	case WM8350_IRQ_GPIO(9):
+		return wm8350_clear_bits(wm8350, WM8350_GPIO_INTERRUPT_STATUS_MASK,
+			WM8350_IM_GP9_EINT);
+	case WM8350_IRQ_GPIO(10):
+		return wm8350_clear_bits(wm8350, WM8350_GPIO_INTERRUPT_STATUS_MASK,
+			WM8350_IM_GP10_EINT);
+	case WM8350_IRQ_GPIO(11):
+		return wm8350_clear_bits(wm8350, WM8350_GPIO_INTERRUPT_STATUS_MASK,
+			WM8350_IM_GP11_EINT);
+	case WM8350_IRQ_GPIO(12):
+		return wm8350_clear_bits(wm8350, WM8350_GPIO_INTERRUPT_STATUS_MASK,
+			WM8350_IM_GP12_EINT);
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+EXPORT_SYMBOL_GPL(wm8350_unmask_irq);
+
+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;
+}
+
+int wm8350_gpio_set_status(struct wm8350 *wm8350, int gpio, int status)
+{
+	if (status)
+		return wm8350_set_bits(wm8350,
+			WM8350_GPIO_PIN_STATUS, 1 << gpio);
+	else
+		return wm8350_clear_bits(wm8350,
+			WM8350_GPIO_PIN_STATUS, 1 << gpio);
+}
+EXPORT_SYMBOL_GPL(wm8350_gpio_set_status);
+
+int wm8350_gpio_get_status(struct wm8350 *wm8350, int gpio)
+{
+	return (wm8350_reg_read(wm8350, WM8350_GPIO_PIN_STATUS) &
+		(1 << gpio)) ? 1: 0;
+}
+EXPORT_SYMBOL_GPL(wm8350_gpio_get_status);
+
+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_INTERRUPT_MODE, 1 << gpio);
+	else
+		return wm8350_clear_bits(wm8350,
+			WM8350_GPIO_INTERRUPT_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);
+
+int wm8350_read_auxadc(struct wm8350 *wm8350, int channel, int scale, int vref)
+{
+	u16 reg, result = 0;
+	int tries = 5;
+
+	if (channel < WM8350_AUXADC_AUX1 || channel > WM8350_AUXADC_TEMP)
+		return -EINVAL;
+	if (channel >= WM8350_AUXADC_USB && channel <= WM8350_AUXADC_TEMP
+		&& (scale != 0 || vref != 0))
+		return -EINVAL;
+
+	mutex_lock(&auxadc_mutex);
+
+	/* Turn on the ADC */
+	reg = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_5);
+	wm8350_reg_write(wm8350, WM8350_POWER_MGMT_5, reg | WM8350_AUXADC_ENA);
+
+	if (scale || vref) {
+		reg = scale << 13;
+		reg |= vref << 12;
+		wm8350_reg_write(wm8350, WM8350_AUX1_READBACK + channel, reg);
+	}
+
+	reg = wm8350_reg_read(wm8350, WM8350_DIGITISER_CONTROL_1);
+	reg |= 1 << channel | WM8350_AUXADC_POLL;
+	wm8350_reg_write(wm8350, WM8350_DIGITISER_CONTROL_1, reg);
+
+	do {
+		schedule_timeout_interruptible(1);
+		reg = wm8350_reg_read(wm8350, WM8350_DIGITISER_CONTROL_1);
+	} while (tries-- && (reg & WM8350_AUXADC_POLL));
+
+	if (!tries)
+		printk (KERN_ERR "wm8350: adc chn %d read timeout\n", channel);
+	else
+		result = wm8350_reg_read(wm8350,
+			WM8350_AUX1_READBACK + channel);
+
+	/* Turn off the ADC */
+	reg=wm8350_reg_read(wm8350, WM8350_POWER_MGMT_5);
+	wm8350_reg_write(wm8350, WM8350_POWER_MGMT_5, reg & ~WM8350_AUXADC_ENA);
+
+	mutex_unlock(&auxadc_mutex);
+	return result & WM8350_AUXADC_DATA1_MASK;
+}
+EXPORT_SYMBOL_GPL(wm8350_read_auxadc);
+
+static void wm8350_rtc_dev_release(struct device *dev){}
+
+int wm8350_device_register_rtc(struct wm8350 *wm8350)
+{
+	int ret;
+
+	strcpy(wm8350->rtc.dev.bus_id, "wm8350-rtc");
+	wm8350->rtc.dev.bus = &wm8350_bus_type;
+	wm8350->rtc.dev.parent = &wm8350->i2c_client->dev;
+	wm8350->rtc.dev.release = wm8350_rtc_dev_release;
+
+	ret = device_register(&wm8350->rtc.dev);
+	if (ret < 0)
+		printk(KERN_ERR "failed to register WM8350 RTC device\n");
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wm8350_device_register_rtc);
+
+static void wm8350_wdg_dev_release(struct device *dev){}
+
+int wm8350_device_register_wdg(struct wm8350 *wm8350)
+{
+	int ret;
+
+	strcpy(wm8350->wdg.dev.bus_id, "wm8350-wdt");
+	wm8350->wdg.dev.bus = &wm8350_bus_type;
+	wm8350->wdg.dev.parent = &wm8350->i2c_client->dev;
+	wm8350->wdg.dev.release = wm8350_wdg_dev_release;
+
+	ret = device_register(&wm8350->wdg.dev);
+	if (ret < 0)
+		printk(KERN_ERR "failed to register WM8350 Watchdog device\n");
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wm8350_device_register_wdg);
+
+static void wm8350_power_dev_release(struct device *dev){}
+
+int wm8350_device_register_power(struct wm8350 *wm8350)
+{
+	int ret;
+
+	strcpy(wm8350->power.dev.bus_id, "wm8350-power");
+	wm8350->power.dev.bus = &wm8350_bus_type;
+	wm8350->power.dev.parent = &wm8350->i2c_client->dev;
+	wm8350->power.dev.release = wm8350_power_dev_release;
+
+	ret = device_register(&wm8350->power.dev);
+	if (ret < 0)
+		printk(KERN_ERR "failed to register WM8350 Power device\n");
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wm8350_device_register_power);
diff --git a/drivers/regulator/wm8350/reg-wm8350.c b/drivers/regulator/wm8350/reg-wm8350.c
new file mode 100644
index 0000000..bb25cea
--- /dev/null
+++ b/drivers/regulator/wm8350/reg-wm8350.c
@@ -0,0 +1,1140 @@
+/*
+ * wm8350_pmu.c  --  Power Management Driver for Wolfson WM8350 PMIC
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC.
+ *
+ * Author: Liam Girdwood
+ *         liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ *  Revision history
+ *    23rd Jan 2007   Initial version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/bitops.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/regulator/wm8350/bus.h>
+#include <linux/regulator/wm8350/pmic.h>
+#include <linux/regulator/regulator-drv.h>
+
+#define WM8350_CORE_VERSION	"0.7"
+
+/* debug */
+#define WM8350_DEBUG 0
+#if WM8350_DEBUG
+#define dbg(format, arg...) printk(format, ## arg)
+#else
+#define dbg(format, arg...)
+#endif
+
+/* hundredths of uA, 405 = 4.05 uA */
+static const int isink_cur[] = {
+	405, 482, 573, 681, 810, 963, 1146, 1362, 1620, 1927, 2291, 2725,
+	3240, 3853, 4582, 5449, 6480, 7706, 9164, 10898, 12960, 15412, 18328,
+	21796, 25920, 30824, 36656, 43592, 51840, 61648, 73313, 87184,
+	103680, 123297, 146626, 174368, 207360, 246594, 293251, 348737,
+	414720, 493188, 586503, 697473, 829440, 986376, 1173005, 1394946,
+	1658880, 1972752, 2346011, 2789892, 3317760, 3945504, 4692021,
+	5579785, 6635520, 7891008, 9384042, 11159570, 13271040, 15782015,
+	18768085, 22319140
+};
+
+static int get_isink_val(int uA)
+{
+	int i, huA = uA * 100;
+
+	for (i = ARRAY_SIZE(isink_cur) - 1; i >= 0 ; i--) {
+		if (huA > isink_cur[i])
+			return i;
+	}
+	return 0;
+}
+
+static int wm8350_isink_set_current(struct regulator_cdev *rcdev, int uA)
+{
+	struct wm8350_pmic *pmic = rcdev_get_drvdata(rcdev);
+	struct wm8350 *wm8350 = to_wm8350_from_pmic(pmic);
+	int isink = rcdev_get_id(rcdev);
+	u16 val;
+
+	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 |
+			get_isink_val(uA));
+		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 |
+			get_isink_val(uA));
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int wm8350_isink_get_current(struct regulator_cdev *rcdev)
+{
+	struct wm8350_pmic *pmic = rcdev_get_drvdata(rcdev);
+	struct wm8350 *wm8350 = to_wm8350_from_pmic(pmic);
+	int isink = rcdev_get_id(rcdev);
+	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 val * 1000;
+}
+
+/* turn on ISINK followed by DCDC */
+static int wm8350_isink_enable(struct regulator_cdev *rcdev)
+{
+	struct wm8350_pmic *pmic = rcdev_get_drvdata(rcdev);
+	struct wm8350 *wm8350 = to_wm8350_from_pmic(pmic);
+	int isink = rcdev_get_id(rcdev);
+
+	/* TODO: add ISINK's C,D,E */
+	switch (isink) {
+	case WM8350_ISINK_A:
+		switch (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 << (pmic->isink_A_dcdc - WM8350_DCDC_1));
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	case WM8350_ISINK_B:
+		switch (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 << (pmic->isink_B_dcdc - WM8350_DCDC_1));
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int wm8350_isink_disable(struct regulator_cdev *rcdev)
+{
+	struct wm8350_pmic *pmic = rcdev_get_drvdata(rcdev);
+	struct wm8350 *wm8350 = to_wm8350_from_pmic(pmic);
+	int isink = rcdev_get_id(rcdev);
+
+	/* TODO: add ISINK's C,D,E */
+	switch (isink) {
+	case WM8350_ISINK_A:
+		switch (pmic->isink_A_dcdc) {
+		case WM8350_DCDC_2:
+		case WM8350_DCDC_5:
+			wm8350_clear_bits(wm8350, WM8350_DCDC_LDO_REQUESTED,
+				1 << (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 (pmic->isink_B_dcdc) {
+		case WM8350_DCDC_2:
+		case WM8350_DCDC_5:
+			wm8350_clear_bits(wm8350, WM8350_DCDC_LDO_REQUESTED,
+				1 << (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_cdev *rcdev)
+{
+	struct wm8350_pmic *pmic = rcdev_get_drvdata(rcdev);
+	struct wm8350 *wm8350 = to_wm8350_from_pmic(pmic);
+	int isink = rcdev_get_id(rcdev);
+
+	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_pmic *pmic, int isink, u16 mode,
+	u16 trigger, u16 duration, u16 on_ramp, u16 off_ramp, u16 drive)
+{
+	struct wm8350 *wm8350 = to_wm8350_from_pmic(pmic);
+
+	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_cdev *rcdev, int uV)
+{
+	struct wm8350_pmic *pmic = rcdev_get_drvdata(rcdev);
+	struct wm8350 *wm8350 = to_wm8350_from_pmic(pmic);
+	int volt_reg, dcdc = rcdev_get_id(rcdev), mV = uV / 1000;
+	u16 val;
+
+	dbg("%s %d mV %d\n", __FUNCTION__, dcdc, mV);
+
+	if (mV < 850 || mV > 4025) {
+		printk(KERN_ERR "wm8350: dcdc %d voltage %d mV out of range\n",
+			dcdc, mV);
+		return -EINVAL;
+	}
+
+	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 DCDC's have same mV bits */
+	val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_DC1_VSEL_MASK;
+	wm8350_reg_write(wm8350, volt_reg, val | WM8350_DC1_VSEL(mV));
+	return 0;
+}
+
+static int wm8350_dcdc_get_voltage(struct regulator_cdev *rcdev)
+{
+	struct wm8350_pmic *pmic = rcdev_get_drvdata(rcdev);
+	struct wm8350 *wm8350 = to_wm8350_from_pmic(pmic);
+	int volt_reg, dcdc = rcdev_get_id(rcdev);
+	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 DCDC's have same mV bits */
+	val = wm8350_reg_read(wm8350, volt_reg) & WM8350_DC1_VSEL_MASK;
+	return WM8350_DC6_VSEL_V(val) * 1000;
+}
+
+int wm8350_dcdc_set_image_voltage(struct wm8350_pmic *pmic, int dcdc, int mV,
+	int mode, int signal)
+{
+	struct wm8350 *wm8350 = to_wm8350_from_pmic(pmic);
+	int volt_reg;
+
+	dbg("%s %d mV %d\n", __FUNCTION__, dcdc, mV);
+
+	if (mV && (mV < 850 || mV > 4025)) {
+		printk(KERN_ERR "wm8350: dcdc %d image 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 DCDC's have same mV bits */
+	wm8350_reg_write(wm8350, volt_reg, WM8350_DC1_VSEL(mV) | mode | signal);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(wm8350_dcdc_set_image_voltage);
+
+static int wm8350_ldo_set_voltage(struct regulator_cdev *rcdev, int uV)
+{
+	struct wm8350_pmic *pmic = rcdev_get_drvdata(rcdev);
+	struct wm8350 *wm8350 = to_wm8350_from_pmic(pmic);
+	int volt_reg, ldo = rcdev_get_id(rcdev), mV = uV / 1000;
+	u16 val;
+
+	dbg("%s %d mV %d\n", __FUNCTION__, ldo, mV);
+
+	if (mV < 900 || mV > 3300) {
+		printk(KERN_ERR "wm8350: ldo %d voltage %d mV out of range\n",
+			ldo, mV);
+		return -EINVAL;
+	}
+
+	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 LDO's have same mV bits */
+	val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_LDO1_VSEL_MASK;
+	wm8350_reg_write(wm8350, volt_reg, val | WM8350_LDO1_VSEL(mV));
+	return 0;
+}
+
+static int wm8350_ldo_get_voltage(struct regulator_cdev *rcdev)
+{
+	struct wm8350_pmic *pmic = rcdev_get_drvdata(rcdev);
+	struct wm8350 *wm8350 = to_wm8350_from_pmic(pmic);
+	int volt_reg, ldo = rcdev_get_id(rcdev);
+	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 LDO's have same mV bits */
+	val = wm8350_reg_read(wm8350, volt_reg) & WM8350_LDO1_VSEL_MASK;
+	return WM8350_LDO1_VSEL_V(val) * 1000;
+}
+
+int wm8350_ldo_set_image_voltage(struct wm8350_pmic *pmic, int ldo, int mV,
+	int mode, int signal)
+{
+	struct wm8350 *wm8350 = to_wm8350_from_pmic(pmic);
+	int volt_reg;
+
+	dbg("%s %d mV %d\n", __FUNCTION__, ldo, mV);
+
+	if (mV < 900 || mV > 3300) {
+		printk(KERN_ERR "wm8350: 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 LDO's have same mV bits */
+	wm8350_reg_write(wm8350, volt_reg, WM8350_LDO1_VSEL(mV) | mode | signal);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(wm8350_ldo_set_image_voltage);
+
+int wm8350_dcdc_set_slot(struct wm8350_pmic *pmic, int dcdc, u16 start,
+	u16 stop, u16 fault)
+{
+	struct wm8350 *wm8350 = to_wm8350_from_pmic(pmic);
+	int slot_reg;
+	u16 val;
+
+	dbg("%s %d start %d stop %d\n", __FUNCTION__, 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_pmic *pmic, int ldo, u16 start,
+	u16 stop)
+{
+	struct wm8350 *wm8350 = to_wm8350_from_pmic(pmic);
+	int slot_reg;
+	u16 val;
+
+	dbg("%s %d start %d stop %d\n", __FUNCTION__, 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_pmic *pmic, int dcdc, u16 mode,
+	u16 ilim, u16 ramp, u16 feedback)
+{
+	struct wm8350 *wm8350 = to_wm8350_from_pmic(pmic);
+	u16 val;
+
+	dbg("%s