diff --git a/arch/arm/mach-mx3/cpufreq.c b/arch/arm/mach-mx3/cpufreq.c
new file mode 100644
index 0000000..9fa465b
--- /dev/null
+++ b/arch/arm/mach-mx3/cpufreq.c
@@ -0,0 +1,227 @@
+/*
+ * cpufreq.c -- i.MX31 CPUfreq driver.
+ *
+ * Copyright (C) 2007 Wolfson Microelectronics PLC.
+ *
+ * Author: Liam Girdwood <lg@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ * 
+ * This driver uses the Freescale BSP CPU clock API for changing CPU dividers
+ * and will need updated when new Freescale API's hit mainline.
+ * 
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/cpufreq.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/proc_fs.h>
+#include <linux/pmic/pmic.h>
+#include <linux/pmic/wm8350.h>
+#include <asm/hardware.h>
+#include <asm/setup.h>
+#include <asm/io.h>
+#include <asm/arch/clock.h>
+#include <asm/cacheflush.h>
+
+static struct regulator_device *reg;
+
+/* does need to be in ascending order for calc_frequency() below */
+static struct cpufreq_frequency_table imx31_freq_table[] = {
+	{0x01,	133000},
+	{0x02,	266000},
+	{0x03,	532000},
+	{0,	CPUFREQ_TABLE_END},
+};
+
+
+static int mxc_verify_speed(struct cpufreq_policy *policy)
+{
+	if (policy->cpu != 0)
+		return -EINVAL;
+
+	return cpufreq_frequency_table_verify(policy, imx31_freq_table);
+}
+
+static unsigned int mxc_get_speed(unsigned int cpu)
+{
+	if (cpu)
+		return 0;
+	return mxc_get_clocks(CPU_CLK) / 1000;
+}
+
+static int calc_frequency(int target, unsigned int relation)
+{
+	int i;
+	
+	if (relation == CPUFREQ_RELATION_H) {
+		for (i = ARRAY_SIZE(imx31_freq_table) - 1; i > 0; i--) {
+			if (imx31_freq_table[i].frequency <= target)
+				return imx31_freq_table[i].frequency;
+		}
+	} else if (relation == CPUFREQ_RELATION_L) {
+		for (i = 0; i < ARRAY_SIZE(imx31_freq_table) - 1; i++) {
+			if (imx31_freq_table[i].frequency >= target)
+				return imx31_freq_table[i].frequency;
+		}
+	}
+	printk(KERN_ERR "error no valid cpufreq relation\n");
+	return 532000;
+}
+
+struct mxc_freq_volts {
+	int freq;
+	int mV;
+};
+
+static const struct mxc_freq_volts freq_mV[] = {
+	{133000 * 1000, 1425},
+	{266000 * 1000, 1500},
+	{532000 * 1000, 1600},
+};
+
+static int mxc_cpu_voltage(struct cpufreq_policy *policy, int freq)
+{
+	int i;
+	
+	for (i = 0; i < ARRAY_SIZE(freq_mV); i++) {
+		if (freq_mV[i].freq == freq)
+			return regulator_set_voltage(reg, freq_mV[i].mV);
+	}
+	return -EINVAL;
+}
+	
+
+static int mxc_set_target(struct cpufreq_policy *policy,
+			  unsigned int target_freq,
+			  unsigned int relation)
+{
+	struct cpufreq_freqs freqs;
+	long freq;
+	int ret = 0;
+
+	/*
+	 * Some governors do not respects CPU and policy lower limits
+	 * which leads to bad things (division by zero etc), ensure
+	 * that such things do not happen.
+	 */
+	if (target_freq < policy->cpuinfo.min_freq)
+		target_freq = policy->cpuinfo.min_freq;
+
+	if (target_freq < policy->min)
+		target_freq = policy->min;
+
+	freq = calc_frequency(target_freq, relation) * 1000;
+
+	//printk("i.MX31: requested frequency %ld Hz\n",freq);
+
+	freqs.old = mxc_get_clocks(CPU_CLK) / 1000;
+	freqs.new = (freq + 500) / 1000;
+	freqs.cpu = 0;
+	freqs.flags = 0;
+
+	if (freqs.old < freqs.new)
+		ret = mxc_cpu_voltage(policy, freq);
+	if (ret < 0) {
+		printk(KERN_ERR "cant raise voltage for CPU frequency %ld\n", 
+			freq);
+		return -EIO;
+	} 
+		
+	cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+	//printk("setting to %ld Hz\n", freq);
+	mxc_pm_intscale(freq, 133000000, 66500000);
+	
+	if (freqs.old > freqs.new)
+		ret = mxc_cpu_voltage(policy, freq);
+	if (ret < 0) {
+		printk(KERN_ERR "cant lower voltage for CPU frequency %ld\n", 
+			freq);
+	}
+	
+	cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+
+	return 0;
+}
+
+static int __init mxc_cpufreq_driver_init(struct cpufreq_policy *policy)
+{
+	int result;
+	
+	printk(KERN_INFO "i.MX3x CPU frequency driver\n");
+
+	if (policy->cpu != 0)
+		return -EINVAL;
+
+	policy->cur = policy->min = policy->max = mxc_get_clocks(CPU_CLK) / 1000;
+	policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
+	policy->cpuinfo.min_freq = 133000;
+	policy->cpuinfo.max_freq = 532000;
+	
+	 /* Manual states, that PLL stabilizes in two CLK32 periods */
+	policy->cpuinfo.transition_latency = 10; //lg
+	
+	result = cpufreq_frequency_table_cpuinfo(policy, imx31_freq_table);
+	if (result)
+		return (result);
+
+	cpufreq_frequency_table_get_attr(imx31_freq_table, policy->cpu);
+	return 0;
+}
+
+static int mxc_cpufreq_driver_exit(struct cpufreq_policy *policy)
+{
+	cpufreq_frequency_table_put_attr(policy->cpu);
+	return 0;
+}
+
+static struct cpufreq_driver mxc_driver = {
+	.flags		= CPUFREQ_STICKY,
+	.verify		= mxc_verify_speed,
+	.target		= mxc_set_target,
+	.get		= mxc_get_speed,
+	.init		= mxc_cpufreq_driver_init,
+	.exit		= mxc_cpufreq_driver_exit,
+	.name		= "imx31",
+};
+
+static int __init mxc_cpufreq_probe(struct device *dev)
+{
+	reg = to_regulator_device(dev);
+	return cpufreq_register_driver(&mxc_driver);
+}
+
+static int mxc_cpufreq_remove(struct device *dev)
+{
+	int ret;
+	
+	ret = cpufreq_unregister_driver(&mxc_driver);
+	reg = NULL;
+	return ret;
+}
+
+struct device_driver mxc_cpufreq_driver = {
+	.name = "DCDC1", /* have option to pass in Atlas DCDC */
+	.bus = &pmic_bus_type,
+	.owner = THIS_MODULE,
+	.probe = mxc_cpufreq_probe,
+	.remove = mxc_cpufreq_remove,
+};
+
+static int __devinit mxc_cpu_dvfs_init(void)
+{
+	return driver_register(&mxc_cpufreq_driver);
+}
+
+static void mxc_cpu_dvfs_exit(void)
+{
+ 	driver_unregister(&mxc_cpufreq_driver);
+}
+
+module_init(mxc_cpu_dvfs_init);
+module_exit(mxc_cpu_dvfs_exit);
diff --git a/drivers/Makefile b/drivers/Makefile
index a168eac..22fce79 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -89,3 +89,4 @@ obj-$(CONFIG_HID)		+= hid/
 obj-$(CONFIG_PPC_PS3)		+= ps3/
 obj-$(CONFIG_OF)		+= of/
 obj-$(CONFIG_SSB)		+= ssb/
+obj-$(CONFIG_PMIC)		+= pmic/
diff --git a/drivers/base/power/Makefile b/drivers/base/power/Makefile
index a803733..4f04f80 100644
--- a/drivers/base/power/Makefile
+++ b/drivers/base/power/Makefile
@@ -1,4 +1,4 @@
-obj-y			:= shutdown.o
+obj-y			:= shutdown.o pmic.o
 obj-$(CONFIG_PM_SLEEP)	+= main.o sysfs.o
 obj-$(CONFIG_PM_TRACE)	+= trace.o
 
diff --git a/drivers/base/power/pmic.c b/drivers/base/power/pmic.c
new file mode 100644
index 0000000..4b6c015
--- /dev/null
+++ b/drivers/base/power/pmic.c
@@ -0,0 +1,509 @@
+/*
+ * pmic.c -- Power Management IC for SoC support.
+ *
+ * Copyright (C) 2007 Wolfson Microelectronics PLC.
+ *
+ * Author: Liam Girdwood <lg@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/pmic.h>
+#include <linux/string.h>
+
+static DEFINE_MUTEX(list_mutex);
+static DEFINE_MUTEX(regulator_mutex);
+static LIST_HEAD(regulator_list);
+static LIST_HEAD(circuit_list);
+static LIST_HEAD(device_list);
+static LIST_HEAD(cpu_list);
+
+struct _pm_device {
+	struct device *dev;
+	enum pm_load load;
+	char *name;
+	struct list_head list;
+};
+
+struct _pm_cpu {
+	int cpu;
+	struct list_head list;
+}; 
+
+struct _pm_load_str {
+	enum pm_load load;
+	const char* str;
+};
+
+static const struct _pm_load_str sysfs_loads[] = {
+	{PM_LOAD_CPU, "cpu"},
+	{PM_LOAD_MEM, "memory"},
+	{PM_LOAD_SYS_IO, "io"},
+	{PM_LOAD_NAND, "nand"},
+	{PM_LOAD_NOR, "nor"},
+	{PM_LOAD_EEPROM, "eeprom"},
+	{PM_LOAD_LCD, "lcd"},
+	{PM_LOAD_GPU, "gpu"},
+	{PM_LOAD_TV, "tv"},
+	{PM_LOAD_BACKLIGHT, "backlight"},
+	{PM_LOAD_AUDIO, "audio"},
+	{PM_LOAD_DISK, "disk"},
+	{PM_LOAD_MMC, "mmc"},
+	{PM_LOAD_MSTICK, "memory_stick"},
+	{PM_LOAD_PCMCIA, "pcmcia"},
+	{PM_LOAD_SIM, "sim"},
+	{PM_LOAD_GSM, "gsm"},
+	{PM_LOAD_VIBRATOR, "vibrator"},
+	{PM_LOAD_ETH, "ethernet"},
+	{PM_LOAD_WIFI, "wifi"},
+	{PM_LOAD_BT, "bluetooth"},
+	{PM_LOAD_USB, "usb"},
+	{PM_LOAD_SERIAL, "serial"},
+	{PM_LOAD_IRDA, "irda"},
+	{PM_LOAD_BATTERY, "battery"},
+	{PM_LOAD_GPS, "gps"},
+	{PM_LOAD_CAMERA, "camera"},
+	{PM_LOAD_LED, "led"},
+	{PM_LOAD_TOUCH, "touchscreen"},
+};
+
+/* is the voltage valid for regulator & circuit */
+static inline int mV_valid(struct pm_regulator *r, int mV)
+{
+	struct pm_circuit *c = r->circuit;
+	
+	if (mV < c->mV_range.dmin || mV > c->mV_range.dmax)
+		return 0;
+	if (mV < r->mV_range.dmin || mV > r->mV_range.dmax)
+		return 0;
+	return 1;
+}
+
+/* is the current limit valid for regulator & circuit */
+static inline int mA_valid(struct pm_regulator *r, int mA)
+{
+	struct pm_circuit *c = r->circuit;
+	
+	if (mA < c->mA_range.dmin || mA > c->mA_range.dmax)
+		return 0;
+	if (mA < r->mA_range.dmin || mA > r->mA_range.dmax)
+		return 0;
+	return 1;
+}
+
+/* get a pm device from device */
+static struct _pm_device *get_pmd(struct device *dev)
+{
+	struct _pm_device *pmd;
+	
+	mutex_lock(&list_mutex);
+	list_for_each_entry(pmd, &device_list, list) {
+		if (dev == pmd->dev) {
+			mutex_unlock(&list_mutex);
+			return pmd;
+		}
+	}
+	mutex_unlock(&list_mutex);
+	return NULL;
+}
+
+/* find the circuit that this regulator supplies */
+static struct pm_circuit *find_regulator_circuit(struct pm_regulator *r)
+{
+	struct pm_circuit *c;
+	
+	list_for_each_entry(c, &circuit_list, list) {
+		if (c->regulator_id == r->id)
+			return c;
+	}
+	return NULL;
+}
+
+static struct pm_circuit *get_cpu_circuit(int cpu)
+{
+	struct _pm_cpu *pmc;
+	struct pm_circuit *c;
+	
+	/* is cpu valid ? */
+	list_for_each_entry(pmc, &cpu_list, list) {
+		if (cpu == pmc->cpu) {
+			list_for_each_entry(c, &circuit_list, list) {
+				if (c->load & PM_LOAD_CPU)
+					return c;
+			}
+		}
+	}
+	return NULL;
+}
+
+static inline int regulator_enable(struct pm_regulator *r, int enable)
+{
+	if (!r->ops->enable)
+		return -EINVAL;
+	return r->ops->enable(r, enable);
+}
+
+static inline int regulator_voltage(struct pm_regulator *r, int mV, int Vid)
+{
+	if (!mV_valid(r, mV))
+		return -EINVAL;
+	if (!r->ops->set_voltage)
+		return -EINVAL;
+	return r->ops->set_voltage(r, mV, Vid);
+}
+
+static inline int regulator_current(struct pm_regulator *r, int mA, int Aid)
+{
+	if (!mA_valid(r, mA))
+		return -EINVAL;
+	if (!r->ops->set_current)
+		return -EINVAL;
+	return r->ops->set_current(r, mA, Aid);
+}
+
+
+static int request_power(struct _pm_device *pmd)
+{
+	struct pm_regulator *r;
+	int ret = 0;
+	
+	list_for_each_entry(r, &regulator_list, list) {
+		if (r->circuit->load & pmd->load) {
+			mutex_lock(&regulator_mutex);
+			if (r->use_count == 0) 
+				ret = regulator_enable(r, 1);
+			if (ret < 0) {
+				mutex_unlock(&regulator_mutex);
+				break;
+			} else
+				r->use_count++;
+			mutex_unlock(&regulator_mutex);
+		}
+	}
+	return ret;
+}
+
+static int release_power(struct _pm_device *pmd)
+{
+	struct pm_regulator *r;
+	int ret = 0;
+	
+	list_for_each_entry(r, &regulator_list, list) {
+		if (r->circuit->load & pmd->load) {
+			mutex_lock(&regulator_mutex);
+			r->use_count--;
+			if (r->use_count == 0) 
+				ret = regulator_enable(r, 0);
+			if (ret < 0)
+				printk("%s : can't disable regulator %s\n",
+					__func__, r->name);
+			mutex_unlock(&regulator_mutex);
+		}
+	}
+	return ret;
+}
+
+static int request_voltage(struct _pm_device *pmd, int mV)
+{
+	struct pm_regulator *r;
+	int ret = 0;
+	
+	list_for_each_entry(r, &regulator_list, list) {
+		if (r->circuit->load & pmd->load) {
+			mutex_lock(&regulator_mutex);
+			ret = regulator_voltage(r, mV, 0);
+			mutex_unlock(&regulator_mutex);
+			if (ret < 0)
+				printk(KERN_ERR "%s : failed to set regulator"
+				 	"%d voltage to %d mV\n", __func__,
+				 	r->id, mV);
+		}
+	}
+	return ret;
+}
+
+static int request_current(struct _pm_device *pmd, int mA)
+{
+	struct pm_regulator *r;
+	int ret = 0;
+	
+	list_for_each_entry(r, &regulator_list, list) {
+		if (r->circuit->load & pmd->load) {
+			mutex_lock(&regulator_mutex);
+			ret = regulator_current(r, mA, 0);
+			mutex_unlock(&regulator_mutex);
+			if (ret < 0)
+				printk(KERN_ERR "%s : failed to set regulator"
+				 	"%d current limit to %d mA\n",
+				 	 __func__, r->id, mA);
+		}
+	}
+	return ret;
+}
+
+int pm_regulator_set_voltage(int id, int mV, int Vid)
+{
+	struct pm_regulator *r;
+	int ret = -EINVAL;
+	
+	mutex_lock(&list_mutex);
+	list_for_each_entry(r, &regulator_list, list) {
+		if (id == r->id) {
+			mutex_lock(&regulator_mutex);
+			ret = regulator_voltage(r, mV, Vid);
+			mutex_unlock(&regulator_mutex);
+			goto out;;
+		}
+	}
+out:	
+	mutex_unlock(&list_mutex);
+	return ret;	
+}
+EXPORT_SYMBOL_GPL(pm_regulator_set_voltage);
+
+/*
+ * PMIC driver API
+ */
+int pm_register_regulator(struct pm_regulator *reg, void *data)
+{
+	struct pm_circuit *c;
+	int ret = 0;
+
+	/* some ops are mandatory */
+	if (reg->ops == NULL)
+		return -EINVAL;
+	
+	mutex_lock(&list_mutex);
+
+	/* find the circuit we supply power to */
+	c = find_regulator_circuit(reg);
+	if (c != NULL) {
+		reg->circuit = c;
+		reg->private = data;
+		c->regulator = reg;
+			
+		/* are we already in use */
+		if (c->boot_time == PM_BOOT_POWER_ON)
+			reg->use_count = 1;
+			
+		printk(KERN_INFO "%s: added %s \n", __func__, reg->name);
+	} else
+		/* no circuit */
+		printk(KERN_INFO "%s: no circuit for %s\n", __func__, 
+			reg->name);
+	
+	/* add circuit to list */		
+	INIT_LIST_HEAD(&reg->list);
+	list_add(&reg->list, &regulator_list);
+	mutex_unlock(&list_mutex);
+	return ret;	
+}
+EXPORT_SYMBOL_GPL(pm_register_regulator);
+
+void pm_unregister_regulator(struct pm_regulator *reg)
+{
+	mutex_lock(&list_mutex);
+	list_del(&reg->list);
+	mutex_unlock(&list_mutex);
+}
+EXPORT_SYMBOL_GPL(pm_unregister_regulator);
+
+/*
+ * SoC Machine power API
+ */
+int pm_register_circuit(struct pm_circuit *circuit)
+{
+	if (circuit->load == 0)
+		return -EINVAL;
+		
+	mutex_lock(&list_mutex);
+	INIT_LIST_HEAD(&circuit->list);
+	list_add(&circuit->list, &circuit_list);
+	mutex_unlock(&list_mutex);
+	return 0;	
+}
+EXPORT_SYMBOL_GPL(pm_register_circuit);
+
+void pm_unregister_circuit(struct pm_circuit *circuit)
+{
+	mutex_lock(&list_mutex);
+	list_del(&circuit->list);
+	mutex_unlock(&list_mutex);	
+}
+EXPORT_SYMBOL_GPL(pm_unregister_circuit);
+
+/* 
+ * General device driver SoC pm API
+ */
+/* load registration */
+int pm_register_load(struct device *dev, enum pm_load load, char *id)
+{
+	struct _pm_device *pmd;
+
+	pmd = kzalloc(sizeof(struct _pm_device), GFP_KERNEL);
+	if (pmd == NULL)
+		return -ENOMEM;
+		
+	pmd->name = kstrdup(id, GFP_KERNEL);
+	if (pmd->name == NULL) {
+		kfree(pmd);
+		return -ENOMEM;
+	}
+	
+	pmd->load = load;
+	INIT_LIST_HEAD(&pmd->list);
+	mutex_lock(&list_mutex);
+	list_add(&pmd->list, &device_list);
+	mutex_unlock(&list_mutex);
+	return 0;	
+}
+EXPORT_SYMBOL_GPL(pm_register_load);
+
+void pm_unregister_load(struct device *dev)
+{
+	struct _pm_device *pmd;
+
+	pmd = get_pmd(dev);
+	if (pmd) {
+		mutex_lock(&list_mutex);
+		list_del(&pmd->list);
+		kfree(pmd->name);
+		kfree(pmd);
+		mutex_unlock(&list_mutex);
+	}	
+}
+EXPORT_SYMBOL_GPL(pm_unregister_load);
+
+
+/* power request/release for fixed voltage loads 
+ * (mV set by machine specific code in arch/cpu/platform/machine.c ) */
+int pm_request_power(struct device *dev)
+{
+	struct _pm_device *pmd;
+
+	pmd = get_pmd(dev);
+	if (pmd == NULL) {
+		printk(KERN_ERR "%s : device not registered\n", __func__);
+		return -EINVAL;
+	} else
+		return request_power(pmd);
+}
+EXPORT_SYMBOL_GPL(pm_request_power);
+
+int pm_release_power(struct device *dev)
+{
+	struct _pm_device *pmd;
+
+	pmd = get_pmd(dev);
+	if (pmd == NULL) {
+		printk(KERN_ERR "%s : device not registered\n", __func__);
+		return -EINVAL;
+	} else
+		return release_power(pmd);
+}
+EXPORT_SYMBOL_GPL(pm_release_power);
+
+/* power request for variable voltage loads */
+int pm_request_voltage(struct device *dev, int mV)
+{
+	struct _pm_device *pmd;
+
+	pmd = get_pmd(dev);
+	if (pmd == NULL) {
+		printk(KERN_ERR "%s : device not registered\n", __func__);
+		return -EINVAL;
+	} else
+		return request_voltage(pmd, mV);
+}
+EXPORT_SYMBOL_GPL(pm_request_voltage);
+
+/* power request for variable voltage loads */
+int pm_request_current(struct device *dev, int mA)
+{
+	struct _pm_device *pmd;
+
+	pmd = get_pmd(dev);
+	if (pmd == NULL) {
+		printk(KERN_ERR "%s : device not registered\n", __func__);
+		return -EINVAL;
+	} else
+		return request_current(pmd, mA);
+}
+EXPORT_SYMBOL_GPL(pm_request_current);
+
+/*
+ * CPUFREQ Client
+ * SoC PMIC registration and voltage scaling for CPUFREQ based drivers.
+ */ 
+int pm_register_cpu_load(int cpu)
+{
+	struct _pm_cpu *pmc;
+
+	pmc = kzalloc(sizeof(struct _pm_cpu), GFP_KERNEL);
+	if (pmc == NULL)
+		return -ENOMEM;
+	
+	INIT_LIST_HEAD(&pmc->list);
+	mutex_lock(&list_mutex);
+	list_add(&pmc->list, &cpu_list);
+	mutex_unlock(&list_mutex);
+	return 0;	
+}
+EXPORT_SYMBOL_GPL(pm_register_cpu_load);
+
+void pm_unregister_cpu_load(int cpu)
+{
+	struct _pm_cpu *pmc;
+
+	mutex_lock(&list_mutex);
+	list_for_each_entry(pmc, &cpu_list, list) {
+		if (cpu == pmc->cpu) {
+			kfree(pmc);
+			goto out;
+		}
+	}
+	
+out:
+	mutex_unlock(&list_mutex);	
+}
+EXPORT_SYMBOL_GPL(pm_unregister_cpu_load);
+
+int pm_request_cpu_voltage(int cpu, int mV)
+{
+	struct pm_circuit *c;
+	int ret = 0;
+	
+	mutex_lock(&list_mutex);
+	
+	c = get_cpu_circuit(cpu);
+	if (c == NULL) {
+		ret = -EINVAL;
+		goto out;
+	}
+		
+	if (c->regulator) {
+		struct pm_regulator *r = c->regulator;
+		
+		if (!mV_valid(r, mV)) {
+			ret = -EINVAL;
+			goto out;
+		}
+	
+		if (r->ops->set_voltage) {
+			ret = r->ops->set_voltage(r, mV, 0);
+			goto out;
+		}
+	}
+		 
+out:
+	mutex_unlock(&list_mutex);
+	return ret;	
+}
+EXPORT_SYMBOL_GPL(pm_request_cpu_voltage);
diff --git a/drivers/char/watchdog/Kconfig b/drivers/char/watchdog/Kconfig
index 37bddc1..da73a80 100644
--- a/drivers/char/watchdog/Kconfig
+++ b/drivers/char/watchdog/Kconfig
@@ -850,4 +850,13 @@ config USBPCWATCHDOG
 
 	  Most people will say N.
 
+#
+# PMIC based Watchdogs
+#
+config WM8350_WATCHDOG
+	tristate "WM8350 Watchdog"
+	depends on WATCHDOG
+	help
+	  Watchdog support for the watchdog module on wm8350 PMIC
+
 endif # WATCHDOG
diff --git a/drivers/char/watchdog/Makefile b/drivers/char/watchdog/Makefile
index 389f8b1..0841efd 100644
--- a/drivers/char/watchdog/Makefile
+++ b/drivers/char/watchdog/Makefile
@@ -118,3 +118,4 @@ obj-$(CONFIG_SH_WDT) += shwdt.o
 
 # Architecture Independant
 obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o
+obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o
diff --git a/drivers/char/watchdog/wm8350_wdt.c b/drivers/char/watchdog/wm8350_wdt.c
new file mode 100644
index 0000000..b15bdb1
--- /dev/null
+++ b/drivers/char/watchdog/wm8350_wdt.c
@@ -0,0 +1,213 @@
+/*
+ * Watchdog driver for the wm8350
+ *
+ * (c) Wolfson Microelectronics <linux@wolfsonmicro.com>
+ * Based on SoftDog driver by Alan Cox <alan@redhat.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
+ *
+ * 27/04/2007 Initial release
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/init.h>
+#include <linux/pmic/wm8350.h>
+
+#include <asm/hardware.h>
+#include <asm/bitops.h>
+#include <asm/uaccess.h>
+
+static unsigned long wm8350_wdt_users;
+static struct miscdevice wm8350_wdt_miscdev;
+
+static int wm8350_wdt_configure(struct wm8350 *wm8350, u16 value)
+{
+	int ret;
+
+	/* Unlock the Register */
+	wm8350_reg_unlock(wm8350);
+
+	/* Write to Watchdog Config Register */
+	ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, value);
+
+	/* Lock the Register */
+	wm8350_reg_lock(wm8350);
+
+	return ret;
+}
+
+/*
+ * Allow only one person to hold it open
+ */
+static int wm8350_wdt_open(struct inode *inode, struct file *file)
+{
+	nonseekable_open(inode, file);
+	if (test_and_set_bit(1, &wm8350_wdt_users))
+		return -EBUSY;
+
+	return 0;
+}
+
+static int wm8350_wdt_release(struct inode *inode, struct file *file)
+{
+	printk(KERN_CRIT "WATCHDOG: Device closed - timer will not stop\n");
+
+	clear_bit(1, &wm8350_wdt_users);
+
+	return 0;
+}
+
+static ssize_t wm8350_wdt_write(struct file *file,
+	const char __user *data, size_t len, loff_t *ppos)
+{
+	struct wm8350_wdg *wm_wdt = wm8350_wdt_miscdev.this_device->driver_data;
+	struct wm8350 *wm8350 = to_wm8350_from_wdg(wm_wdt);
+
+	if (len)
+
+	/* Write to Watchdog Config Register */
+	wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, 0);
+
+	return len;
+}
+
+static struct watchdog_info ident = {
+	.options	=  WDIOF_SETTIMEOUT |
+				WDIOF_KEEPALIVEPING,
+	.identity	= "WM8350 Watchdog",
+};
+
+static u16 wm8350_wdt_configs[9] = {
+	0x00, /* Watchdog disabled */
+	0x23, /* 1 second */
+	0x24, /* 2 second */
+	0x25, /* 4 second */
+	0x25, /* 4 second */
+	0x35, /* 8 second */ /* Use the interrupt then reset to get us 8 secs */
+	0x35, /* 8 second */
+	0x35, /* 8 second */
+	0x35, /* 8 second */
+};
+
+static int wm8350_wdt_ioctl(struct inode *inode, struct file *file,
+	unsigned int cmd, unsigned long arg)
+{
+	struct wm8350_wdg *wm_wdt = wm8350_wdt_miscdev.this_device->driver_data;
+	struct wm8350 *wm8350 = to_wm8350_from_wdg(wm_wdt);
+	int ret = -ENOTTY;
+	int time;
+	void __user *argp = (void __user *)arg;
+	int __user *p = argp;
+	int i;
+	u16 reg;
+
+	switch (cmd) {
+	case WDIOC_GETSUPPORT:
+		ret = copy_to_user(argp, &ident,
+				   sizeof(ident)) ? -EFAULT : 0;
+		break;
+
+	case WDIOC_GETSTATUS:
+		ret = put_user(0, p);
+		break;
+
+	case WDIOC_SETTIMEOUT:
+		ret = get_user(time, p);
+		if (ret)
+			break;
+
+		if (time < 0 || time > 8) {
+			ret = -EINVAL;
+			break;
+		}
+
+		ret = wm8350_wdt_configure(wm8350, wm8350_wdt_configs[time]);
+		break;
+	case WDIOC_GETTIMEOUT:
+		reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2);
+		for (i=0; i < 9; i++)
+			if (wm8350_wdt_configs[i] > reg) break;
+		ret = put_user(i-1, p);
+		break;
+
+	case WDIOC_KEEPALIVE:
+		/* Write to Watchdog Config Register */
+		ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, 0);
+		break;
+	}
+	return ret;
+}
+
+static const struct file_operations wm8350_wdt_fops =
+{
+	.owner		= THIS_MODULE,
+	.llseek		= no_llseek,
+	.write		= wm8350_wdt_write,
+	.ioctl		= wm8350_wdt_ioctl,
+	.open		= wm8350_wdt_open,
+	.release	= wm8350_wdt_release,
+};
+
+static struct miscdevice wm8350_wdt_miscdev =
+{
+	.minor		= WATCHDOG_MINOR,
+	.name		= "watchdog",
+	.fops		= &wm8350_wdt_fops,
+};
+
+static int wm8350_wdt_probe(struct device *dev)
+{
+	struct wm8350_wdg *wm_wdt = to_wm8350_wdg_device(dev);
+	struct wm8350 *wm8350 = to_wm8350_from_wdg(wm_wdt);
+	int ret = 0;
+
+	ret = misc_register(&wm8350_wdt_miscdev);
+	wm8350_wdt_miscdev.this_device->driver_data = wm_wdt;
+
+	/* Turn off Watchdog here just to make sure that it isnt
+	 * turned on until we can start kicking it.
+	 */
+	wm8350_wdt_configure(wm8350, 0x00);
+
+	return ret;
+}
+
+static int __exit wm8350_wdt_remove(struct device *dev)
+{
+	misc_deregister(&wm8350_wdt_miscdev);
+
+	return 0;
+}
+
+static struct device_driver wm8350_wdt_driver = {
+	.name = "wm8350-wdt",
+	.bus = &wm8350_bus_type,
+	.owner = THIS_MODULE,
+	.probe = wm8350_wdt_probe,
+	.remove = wm8350_wdt_remove,
+};
+
+static int __init wm8350_wdt_init(void)
+{
+	return driver_register(&wm8350_wdt_driver);
+}
+
+static void __exit wm8350_wdt_exit(void)
+{
+	driver_unregister(&wm8350_wdt_driver);
+}
+
+module_init(wm8350_wdt_init);
+module_exit(wm8350_wdt_exit);
+
+MODULE_AUTHOR("Graeme Gregory");
+MODULE_DESCRIPTION("WM8350 Watchdog");
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 257b440..792491d 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
+	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-imx32ads.c b/drivers/leds/leds-imx32ads.c
new file mode 100644
index 0000000..f5c93b4
--- /dev/null
+++ b/drivers/leds/leds-imx32ads.c
@@ -0,0 +1,137 @@
+/*
+ * LED driver for DCDC5 white leds on i.MX32ADS board
+ * 
+ * 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/pmic/wm8350.h>
+
+static void led_work(struct work_struct *work)
+{
+	struct wm8350_led *led = container_of(work, struct wm8350_led, work);
+	struct wm8350 *wm8350 = to_wm8350_from_led(led);
+	struct wm8350_pmic *pmic = &wm8350->pmic;
+	
+	mutex_lock(&led->mutex);
+	switch (led->value) {
+	case LED_OFF:
+		wm8350_dcdc_enable(pmic, WM8350_DCDC_5, 0);
+		wm8350_isink_enable(pmic, WM8350_ISINK_B, 0, 0);
+		break;
+	case LED_HALF:
+		wm8350_dcdc_enable(pmic, WM8350_DCDC_5, 1);
+		wm8350_isink_set_current(pmic, WM8350_ISINK_B, 11159570);
+		wm8350_isink_enable(pmic, WM8350_ISINK_B, 1, 0);
+		wm8350_dcdc25_set_mode(pmic, WM8350_DCDC_5, 
+			WM8350_ISINK_MODE_BOOST, WM8350_ISINK_ILIM_NORMAL,
+			WM8350_DC2_RMP_30V, WM8350_DC2_FBSRC_ISINKB);
+		break;
+	case LED_FULL:
+		wm8350_dcdc_enable(pmic, WM8350_DCDC_5, 1);
+		wm8350_isink_set_current(pmic, WM8350_ISINK_B, 18768085);
+		wm8350_isink_enable(pmic, WM8350_ISINK_B, 1, 0);
+		wm8350_dcdc25_set_mode(pmic, WM8350_DCDC_5, 
+			WM8350_ISINK_MODE_BOOST, WM8350_ISINK_ILIM_NORMAL,
+			WM8350_DC2_RMP_30V, WM8350_DC2_FBSRC_ISINKB);
+		break;
+	}
+	mutex_unlock(&led->mutex);
+}
+
+static void imx32ads_led_set(struct led_classdev *led_cdev, 
+	enum led_brightness value)
+{
+	struct device *dev = led_cdev->class_dev->dev;
+	struct wm8350_led *led = to_wm8350_led_device(dev);
+	
+	mutex_lock(&led->mutex);
+	led->value = value;
+	mutex_unlock(&led->mutex);
+	schedule_work(&led->work);
+}
+
+static struct led_classdev imx32ads_led = {
+	.name			= "imx32ads:white",
+	.default_trigger	= "heartbeat",
+	.brightness_set		= imx32ads_led_set,
+};
+
+#ifdef CONFIG_PM
+static int imx32ads_led_suspend(struct device *dev, pm_message_t state)
+{
+	led_classdev_suspend(&imx32ads_led);
+	return 0;
+}
+
+static int imx32ads_led_resume(struct device *dev)
+{
+	led_classdev_resume(&imx32ads_led);
+	return 0;
+}
+#endif
+
+static void imx32ads_led_shutdown (struct device *dev)
+{
+	struct wm8350_led *led = to_wm8350_led_device(dev);
+	struct wm8350 *wm8350 = to_wm8350_from_led(led);
+	struct wm8350_pmic *pmic = &wm8350->pmic;
+	
+	mutex_lock(&led->mutex);
+	led->value = LED_OFF;
+	wm8350_dcdc_enable(pmic, WM8350_DCDC_5, 0);
+	wm8350_isink_enable(pmic, WM8350_ISINK_B, 0, 0);
+	mutex_unlock(&led->mutex);
+}
+
+static int imx32ads_led_probe(struct device *dev)
+{
+	struct wm8350_led *led = to_wm8350_led_device(dev);
+	
+	mutex_init(&led->mutex);
+	INIT_WORK(&led->work, led_work);
+	led->value = LED_OFF;
+	return led_classdev_register(dev, &imx32ads_led);
+}
+
+static int imx32ads_led_remove(struct device *dev)
+{
+	led_classdev_unregister(&imx32ads_led);
+	return 0;
+}
+
+struct device_driver imx32ads_led_driver = {
+	.name = "wm8350-led",
+	.bus = &wm8350_bus_type,
+	.owner = THIS_MODULE,
+	.probe = imx32ads_led_probe,
+	.remove = imx32ads_led_remove,
+	.shutdown = imx32ads_led_shutdown,
+	.suspend = imx32ads_led_suspend,
+	.resume = imx32ads_led_resume,
+};
+
+static int __devinit imx32ads_led_init(void)
+{
+	return driver_register(&imx32ads_led_driver);
+}
+
+static void imx32ads_led_exit(void)
+{
+ 	driver_unregister(&imx32ads_led_driver);
+}
+
+module_init(imx32ads_led_init);
+module_exit(imx32ads_led_exit);
+
+MODULE_AUTHOR("Liam Girdwood <lg@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("i.MX32ADS LED driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pmic/Kconfig b/drivers/pmic/Kconfig
new file mode 100644
index 0000000..2c5bf91
--- /dev/null
+++ b/drivers/pmic/Kconfig
@@ -0,0 +1,56 @@
+menu "Power Management IC's"
+
+config PMIC
+	tristate "Power Managenment IC support"
+
+config PMIC_DEBUG
+	bool "PMIC debug support"
+	depends on PMIC
+	help
+	  Say yes here to enable debugging support in the PMIC framework
+	  and individual PMIC drivers.
+
+config PMIC_WM8350
+	tristate "WOLFSON WM8350 support"
+	help
+	  If you say yes here you get support for the
+	  Wolfon WM8300 PMIC chip.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called pmic-wm8300.
+
+menu "WM8350 Config Mode"
+depends on PMIC_WM8350
+
+choice
+	prompt "WM8350 Configuration Mode"
+	default PMIC_WM8350_MODE_0
+
+config PMIC_WM8350_MODE_0
+	bool "Mode 0"
+
+config PMIC_WM8350_MODE_1
+	bool "Mode 1"
+
+config PMIC_WM8350_MODE_2
+	bool "Mode 2"
+
+config PMIC_WM8350_MODE_3
+	bool "Mode 3"
+
+endchoice
+
+endmenu
+
+config IMX32ADS_WM8350_POWER
+	tristate "WOLFSON WM8350 IMX32ADS support"
+	depends on PMIC_WM8350
+	help
+	  If you say yes here you get support for the
+	  Wolfon WM8300 PMIC chip on the i.MX32ADS.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called imx32ads-wm8350.
+
+endmenu
+
diff --git a/drivers/pmic/Makefile b/drivers/pmic/Makefile
new file mode 100644
index 0000000..db690ab
--- /dev/null
+++ b/drivers/pmic/Makefile
@@ -0,0 +1,12 @@
+#
+# Makefile for PMIC drivers.
+#
+
+ifeq ($(CONFIG_PMIC_DEBUG),y)
+	EXTRA_CFLAGS		+= -DDEBUG
+endif
+
+obj-$(CONFIG_PMIC_WM8350) += \
+	pmic-wm8350.o pmic-wm8350-bus.o
+
+obj-$(CONFIG_IMX32ADS_WM8350_POWER) += pmic-wm8350-imx32ads.o
diff --git a/drivers/pmic/pmic-wm8350-bus.c b/drivers/pmic/pmic-wm8350-bus.c
new file mode 100644
index 0000000..29c2c11
--- /dev/null
+++ b/drivers/pmic/pmic-wm8350-bus.c
@@ -0,0 +1,1525 @@
+/*
+ * 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/pmic/wm8350.h>
+#include <linux/delay.h>
+
+#define WM8350_BUS_VERSION "0.4"
+#define WM8350_UNLOCK_KEY 0x0013
+#define WM8350_LOCK_KEY 0x0000
+
+/* 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);
+
+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);
+}
+
+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);
+}
+
+/* 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:
+		wm8350->read_dev = wm8350_read_i2c_device;
+		wm8350->write_dev = wm8350_write_i2c_device;
+		break;
+	case WM8350_IO_SPI:
+		wm8350->read_dev = wm8350_read_spi_device;
+		wm8350->write_dev = wm8350_write_spi_device;
+		break;
+	case WM8350_IO_CUSTOM:
+		wm8350->read_dev = read_dev;
+		wm8350->write_dev = write_dev;
+		break;
+	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++) {
+		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->handler[irq])
+		wm8350->handler[irq](wm8350, irq);
+	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))
+{
+	if (irq < 0 || irq > WM8350_NUM_IRQ || !handler)
+		return -EINVAL;
+
+	if (wm8350->handler[irq])
+		return -EBUSY;
+
+	mutex_lock(&wm8350->work_mutex);
+	wm8350->handler[irq] = handler;
+	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->handler[irq] = 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_pmic_dev_release(struct device *dev){}
+
+int wm8350_device_register_pmic(struct wm8350 *wm8350)
+{
+	int ret;
+
+	strcpy(wm8350->pmic.dev.bus_id, "wm8350-pmic");
+	wm8350->pmic.dev.bus = &wm8350_bus_type;
+	wm8350->pmic.dev.parent = &wm8350->i2c_client->dev;
+	wm8350->pmic.dev.release = wm8350_pmic_dev_release;
+
+	ret = device_register(&wm8350->pmic.dev);
+	if (ret < 0)
+		printk(KERN_ERR "failed to register WM8350 PMIC device\n");
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wm8350_device_register_pmic);
+
+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_led_dev_release(struct device *dev){}
+
+int wm8350_device_register_led(struct wm8350 *wm8350)
+{
+	int ret;
+
+	strcpy(wm8350->led.dev.bus_id, "wm8350-led");
+	wm8350->led.dev.bus = &wm8350_bus_type;
+	wm8350->led.dev.parent = &wm8350->i2c_client->dev;
+	wm8350->led.dev.release = wm8350_led_dev_release;
+
+	ret = device_register(&wm8350->led.dev);
+	if (ret < 0)
+		printk(KERN_ERR "failed to register WM8350 LED device\n");
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wm8350_device_register_led);
+
+static void wm8350_backlight_dev_release(struct device *dev){}
+
+int wm8350_device_register_backlight(struct wm8350 *wm8350)
+{
+	int ret;
+
+	strcpy(wm8350->backlight.dev.bus_id, "wm8350-bl");
+	wm8350->backlight.dev.bus = &wm8350_bus_type;
+	wm8350->backlight.dev.parent = &wm8350->i2c_client->dev;
+	wm8350->backlight.dev.release = wm8350_backlight_dev_release;
+
+	ret = device_register(&wm8350->backlight.dev);
+	if (ret < 0)
+		printk(KERN_ERR "failed to register WM8350 backlight device\n");
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wm8350_device_register_backlight);
+
+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);
+
+static int wm8350_bus_match(struct device *dev, struct device_driver *drv)
+{
+	if(!strcmp(dev->bus_id, drv->name))
+		return 1;
+	return 0;
+}
+
+static int wm8350_bus_suspend(struct device *dev, pm_message_t state)
+{
+	int ret = 0;
+
+	if (dev->driver && dev->driver->suspend)
+		ret = dev->driver->suspend(dev, state);
+
+	return ret;
+}
+
+static int wm8350_bus_resume(struct device *dev)
+{
+	int ret = 0;
+
+	if (dev->driver && dev->driver->resume)
+		ret = dev->driver->resume(dev);
+
+	return ret;
+}
+
+struct bus_type wm8350_bus_type = {
+	.name		= "wm8350",
+	.match		= wm8350_bus_match,
+	.suspend	= wm8350_bus_suspend,
+	.resume		= wm8350_bus_resume,
+};
+EXPORT_SYMBOL(wm8350_bus_type);
+
+static int __init wm8350_bus_init(void)
+{
+	printk("WM8350 Bus Manager %s\n", WM8350_BUS_VERSION);
+	return bus_register(&wm8350_bus_type);
+}
+subsys_initcall(wm8350_bus_init);
+
+static void __exit wm8350_bus_exit(void)
+{
+	bus_unregister(&wm8350_bus_type);
+}
+
+module_exit(wm8350_bus_exit);
+
+MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
+MODULE_DESCRIPTION("WM8350 PMIC Bus driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pmic/pmic-wm8350-imx32ads.c b/drivers/pmic/pmic-wm8350-imx32ads.c
new file mode 100644
index 0000000..cafa340
--- /dev/null
+++ b/drivers/pmic/pmic-wm8350-imx32ads.c
@@ -0,0 +1,805 @@
+/*
+ * pmic-wm8350-imx32ads.c  --  i.MX31ADS 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/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/pmic/wm8350.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/leds.h>
+#include <linux/fb.h>
+#include <linux/pmic.h>
+
+#include <asm/arch/dma.h>
+#include <asm/arch/spba.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/board-mx31ads.h>
+
+#include "../mxc/pm/dptc.h"
+#include "../mxc/spi/spi.h"
+
+#define WM8350_IMX32ADS_VERSION "0.2"
+
+/* 
+ * Set to 1 for MXC SPI driver (non standard SPI API)
+ */ 
+#define MXC_SPI 0
+/*
+ * Set to 1 when testing battery that is connected otherwise spuriuos debug
+ */
+#define BATTERY 0
+
+#define WM8350_I2C_ADDR         (0x34 >> 1)
+
+extern const char imx32ads_audio[32];
+extern struct led_trigger *imx32ads_led_trigger;
+
+/* program WM8350 so board will boot from WM8350 supplies */
+static int program = 0;
+module_param(program, int, 0);
+MODULE_PARM_DESC(program, "program initial DCDC & LDO values");
+
+/* set WM8350 into spi mode during program */
+static int spi = 0;
+module_param(spi, int, 0);
+MODULE_PARM_DESC(spi, "program spi 4 wire control");
+
+/*
+ * Program WM8350 with initial DCDC & LDO values.
+ */
+static int program_wm8350(struct wm8350* wm8350)
+{
+	struct wm8350_pmic *pmic = &wm8350->pmic;
+	u16 val;
+	
+	wm8350_reg_unlock(wm8350);
+	
+	/* DCDC1 @ 1.6 V  CPU*/
+	wm8350_dcdc_set_voltage(pmic, WM8350_DCDC_1, 1600);
+	wm8350_dcdc_set_slot(pmic, WM8350_DCDC_1, 1, 0, 
+		WM8350_DC1_ERRACT_SHUTDOWN_SYS);
+	wm8350_dcdc_enable(pmic, WM8350_DCDC_1, 1);
+	
+	/* DCDC3 @ 2.8 V  Peripherals & IO */
+	wm8350_dcdc_set_voltage(pmic, WM8350_DCDC_3, 2800);
+	wm8350_dcdc_set_slot(pmic, WM8350_DCDC_3, 1, 0, 
+		WM8350_DC1_ERRACT_NONE);
+	wm8350_dcdc_enable(pmic, WM8350_DCDC_3, 1);
+	
+	/* DCDC4 @ 1.8 V Peripherals & IO */
+	wm8350_dcdc_set_voltage(pmic, WM8350_DCDC_4, 1800);
+	wm8350_dcdc_set_slot(pmic, WM8350_DCDC_4, 1, 0, 
+		WM8350_DC1_ERRACT_NONE);
+	wm8350_dcdc_enable(pmic, WM8350_DCDC_4, 1);
+	
+	/* DCDC6 @ 1.8 V DDR Memory */
+	wm8350_dcdc_set_voltage(pmic, WM8350_DCDC_6, 1800);
+	wm8350_dcdc_set_slot(pmic, WM8350_DCDC_6, 1, 0, 
+		WM8350_DC1_ERRACT_NONE);
+	wm8350_dcdc_enable(pmic, WM8350_DCDC_6, 1);
+	
+	/* DCDC2 @  OFF - 5.5V SW3 output */
+	wm8350_dcdc_set_slot(pmic, WM8350_DCDC_2, 1, 0, 
+		WM8350_DC1_ERRACT_SHUTDOWN_CONV);
+	wm8350_dcdc_enable(pmic, WM8350_DCDC_2, 0);
+	
+	/* DCDC5 @  OFF  - White LEDS */
+	wm8350_dcdc_set_slot(pmic, WM8350_DCDC_5, 1, 1, 
+		WM8350_DC1_ERRACT_SHUTDOWN_CONV);
+	wm8350_dcdc_enable(pmic, WM8350_DCDC_5, 0);
+	
+	/* LDO1 @ 2.8 V Peripherals MMC & Camera */
+	wm8350_ldo_set_voltage(pmic, WM8350_LDO_1, 2800);
+	wm8350_ldo_set_slot(pmic, WM8350_LDO_1, 1, 0);
+	wm8350_ldo_enable(pmic, WM8350_LDO_1, 1);
+	
+	/* LDO2 @ 3.3 V */
+	wm8350_ldo_set_voltage(pmic, WM8350_LDO_2, 3300);
+	wm8350_ldo_set_slot(pmic, WM8350_LDO_2, 1, 0);
+	wm8350_ldo_enable(pmic, WM8350_LDO_2, 1);
+	
+	/* LDO3 @ 1.5 V VDIG, VGEN */
+	wm8350_ldo_set_voltage(pmic, WM8350_LDO_3, 1500);
+	wm8350_ldo_set_slot(pmic, WM8350_LDO_3, 1, 0);
+	wm8350_ldo_enable(pmic, WM8350_LDO_3, 1);
+	
+	/* LDO4 @ 2.6 V Transceivers */
+	wm8350_ldo_set_voltage(pmic, WM8350_LDO_4, 2600);
+	wm8350_ldo_set_slot(pmic, WM8350_LDO_4, 1, 0);
+	wm8350_ldo_enable(pmic, WM8350_LDO_4, 1);
+	
+	if (spi == 0) {
+		val = wm8350_reg_read(wm8350, WM8350_INTERFACE_CONTROL) & 
+			~(WM8350_RECONFIG_AT_ON | WM8350_USE_DEV_PINS | 
+			WM8350_SPI_3WIRE | WM8350_SPI_4WIRE);
+		wm8350_reg_write(wm8350, WM8350_INTERFACE_CONTROL, 
+			val | WM8350_CONFIG_DONE | WM8350_AUTOINC);
+	} else {
+		val = wm8350_reg_read(wm8350, WM8350_INTERFACE_CONTROL) & 
+			~(WM8350_RECONFIG_AT_ON | WM8350_USE_DEV_PINS);
+		wm8350_reg_write(wm8350, WM8350_INTERFACE_CONTROL, 
+			val | WM8350_CONFIG_DONE | WM8350_SPI_4WIRE | 
+			WM8350_SPI_3WIRE);
+	}
+	
+	/* dummy read required when changing Sec -> Pri I2C */
+	printk("dummy read %x\n", wm8350_reg_read(wm8350, 176));
+	printk("now press button and then swap jumpers if leds work\n");
+	return 0;
+}
+
+
+#if MXC_SPI
+static spi_config mxc_spi_config = {
+	.module_number		= 1,
+	.priority		= HIGH,
+	.master_mode		= 1,
+	.bit_rate		= 100000,// 4000000,
+	.bit_count		= 32,
+	.active_high_polarity	= 1,
+	.active_high_ss_polarity = 0,
+	.phase			= 0,
+	.ss_low_between_bursts 	= 1,
+	.ss_asserted		= 0,
+	.tx_delay		= 0,
+};
+static void *spi_id = &mxc_spi_config;
+
+int spi_send_frame_to_spi(int num_reg, unsigned char *reg_value)
+{
+	int res;
+
+	res = mxc_spi_is_active(mxc_spi_config.module_number);
+	if (res == 0)
+		return -EBUSY;
+
+	/* use this to launch SPI operation. */
+	return spi_send_frame(reg_value, (unsigned long)4, spi_id);
+};
+
+static int mx31_spi_read(struct wm8350 *wm8350, char reg, int size, char *dest)
+{
+	int ret;
+	u8 msg[4];
+
+	/* don't support incremental write with SPI */
+	if (size != 2)
+		return -EIO;
+
+	msg[0] = 0x80;
+	msg[1] = reg; 
+	msg[2] = 0;
+	msg[3] = 0;
+	ret = spi_send_frame_to_spi(reg, msg);
+	if (ret < 0) {
+		printk(KERN_ERR "%s: io failure %d\n", __func__, ret);
+		return 0;
+	}
+
+	*dest++ = msg[2]; *dest = msg[3];
+	return 0;
+}
+static int mx31_spi_write(struct wm8350 *wm8350, char reg, int size, char *src)
+{
+	u8 msg[4];
+
+	/* don't support incremental write with SPI */
+	if (size != 2)
+		return -EIO;
+
+	msg[0] = 0;
+	msg[1] = reg; 
+	msg[2] = *src++;
+	msg[3] = *src;
+	return spi_send_frame_to_spi(reg, msg);
+}
+#else
+static int wm8350_pmic_i2c_detect (struct i2c_adapter *adap, int addr, int kind);
+/*
+ * WM8350 2 wire address
+ */
+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;
+#endif
+
+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 void imx32ads_switch_handler(struct wm8350 *wm8350, int irq)
+{
+	printk("switch pressed %d\n", irq);
+}
+
+static struct platform_device *imx32ads_snd_device;
+
+/* i.MX31 CPU voltage scaling */
+struct pm_circuit mx31_cpu_circuit = {
+	.name = "mx31ads_cpu",
+	.regulator_id	= WM8350_DCDC_1,
+	.load	= PM_LOAD_CPU, 
+	.mV_range	= {
+		.dmin	= 1400,
+		.dmax	= 1600,
+	},
+};
+
+/* MX31ADS System IO */
+struct pm_circuit mx31_io_hi_circuit = {
+	.name = "mx31ads_io_hi",
+	.regulator_id	= WM8350_DCDC_3,
+	.load	= PM_LOAD_SYS_IO,
+	.mV_range	= {
+		.sval	= 2700,
+	},
+};
+
+/* MX31ADS System IO */
+struct pm_circuit mx31_io_lo_circuit = {
+	.name = "mx31ads_io_lo",
+	.regulator_id	= WM8350_DCDC_4,
+	.load	= PM_LOAD_SYS_IO, 
+	.mV_range	= {
+		.sval	= 1800,
+	},
+};
+
+/* MX31ADS System IO */
+struct pm_circuit mx31_io_gen_circuit = {
+	.name = "mx31ads_io_gen",
+	.regulator_id	= WM8350_LDO_3,
+	.load	= PM_LOAD_SYS_IO, 
+	.mV_range	= {
+		.sval	= 1500,
+	},
+};
+
+/* i.MX31 DDR RAM */
+struct pm_circuit mx31_mem_circuit = {
+	.name = "mx31ads_memory",
+	.regulator_id	= WM8350_DCDC_6,
+	.load	= PM_LOAD_MEM, 
+	.mV_range	= {
+		.sval	= 1800,
+	},
+};
+
+/* MX31ADS Camera & MMC & MSitck */
+struct pm_circuit mx31_vcam_circuit = {
+	.name = "mx31ads_vcam",
+	.regulator_id	= WM8350_LDO_1,
+	.load	= PM_LOAD_MMC | PM_LOAD_MSTICK | PM_LOAD_CAMERA, 
+	.mV_range	= {
+		.sval	= 2800,
+	},
+};
+
+/* MX31ADS SIM Card */
+struct pm_circuit mx31_vsim_circuit = {
+	.name = "mx31ads_vsim",
+	.regulator_id	= WM8350_LDO_2,
+	.load	= PM_LOAD_SIM | PM_LOAD_AUDIO, 
+	.mV_range	= {
+		.sval	= 3300,
+	},
+};
+
+/* MX31ADS Tranceivers and other peripherals */
+struct pm_circuit mx31_transceiver_circuit = {
+	.name = "mx31ads_transceiver",
+	.regulator_id	= WM8350_LDO_4,
+	.load	= PM_LOAD_SERIAL | PM_LOAD_USB | PM_LOAD_IRDA | PM_LOAD_LCD |
+		PM_LOAD_TV | PM_LOAD_DISK | PM_LOAD_MMC,
+	.mV_range	= {
+		.sval	= 2800,
+	},
+};
+
+static void wm8350_free(struct wm8350 *wm8350)
+{
+#if BATTERY
+	struct wm8350_power *power = &wm8350->power;
+#endif
+
+	pm_unregister_circuit(&mx31_cpu_circuit);
+	pm_unregister_circuit(&mx31_io_hi_circuit);
+	pm_unregister_circuit(&mx31_io_lo_circuit);
+	pm_unregister_circuit(&mx31_io_gen_circuit);
+	pm_unregister_circuit(&mx31_mem_circuit);
+	pm_unregister_circuit(&mx31_vcam_circuit);
+	pm_unregister_circuit(&mx31_vsim_circuit);
+	pm_unregister_circuit(&mx31_transceiver_circuit);
+	
+	/* make sure DCDC1 is back at 1.6 volts so we can safely reboot */
+	/* DCDC1 @ 1.6 V */
+	wm8350_dcdc_set_voltage(&wm8350->pmic, WM8350_DCDC_1, 1600);
+	wm8350_dcdc_set_slot(&wm8350->pmic, WM8350_DCDC_1, 1, 0, 
+		WM8350_DC1_ERRACT_SHUTDOWN_SYS);
+	wm8350_dcdc_enable(&wm8350->pmic, WM8350_DCDC_1, 1);
+
+	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();
+#ifdef MXC_DPTC	
+	mxc_pmic_unregister(&wm8350->pmic);
+#endif
+	if (wm8350->pmic.dev.is_registered)
+		device_unregister(&wm8350->pmic.dev);
+	if (wm8350->rtc.dev.is_registered)
+		device_unregister(&wm8350->rtc.dev);
+	if (wm8350->led.dev.is_registered)
+		device_unregister(&wm8350->led.dev);
+	if (wm8350->wdg.dev.is_registered)
+		device_unregister(&wm8350->wdg.dev);
+	if (wm8350->power.dev.is_registered)
+		device_unregister(&wm8350->power.dev);
+	if (wm8350->backlight.dev.is_registered)
+		device_unregister(&wm8350->backlight.dev);
+	platform_device_unregister(imx32ads_snd_device);
+}
+
+#if MXC_SPI
+static int wm8350_spi_detach(struct platform_device *pdev)
+{
+	struct wm8350 *wm8350 = dev_get_drvdata(&pdev->dev);
+	
+	wm8350_free(wm8350);
+	if (wm8350->reg_cache)
+		kfree(wm8350->reg_cache);
+	kfree(wm8350);
+	return 0;
+}
+
+#else
+
+static int wm8350_i2c_detach(struct i2c_client *client)
+{
+	struct wm8350 *wm8350 = i2c_get_clientdata(client);
+
+	wm8350_free(wm8350);
+	i2c_detach_client(client);
+	kfree(client);
+	if (wm8350->reg_cache)
+		kfree(wm8350->reg_cache);
+	kfree(wm8350);
+
+	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,
+};
+#endif
+
+#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(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
+
+static int config_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 -- needs level but GPIO5 is edge so we
+	 * will only wake with PWR_ON switch atm */
+	wm8350_gpio_config(wm8350, 5, WM8350_GPIO_DIR_IN, 
+		WM8350_GPIO5_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);
+#if MXC_SPI		
+	/* CSB */
+	wm8350_gpio_config(wm8350, 7, WM8350_GPIO_DIR_IN, 
+		WM8350_GPIO7_CSB_IN, WM8350_GPIO_ACTIVE_HIGH, 
+		WM8350_GPIO_PULL_NONE, WM8350_GPIO_INVERT_OFF, 
+		WM8350_GPIO_DEBOUNCE_OFF);
+#else
+	/* 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);
+	wm8350_unmask_irq(wm8350, WM8350_IRQ_GPIO(7));
+#endif
+
+	/* 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_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 */
+	wm8350_dcdc_set_image_voltage(pmic, WM8350_DCDC_1, 1400, /*1200,*/ /* 1.0v for mx32 */
+		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;
+}
+
+static int wm8350_init(struct wm8350* wm8350)
+{
+	int ret, i;
+	
+	printk("WM8350 i.MX32ads version %s\n", WM8350_IMX32ADS_VERSION);
+
+	ret = wm8350_create_cache(wm8350);
+	if (ret < 0) {
+		printk(KERN_ERR "wm8350: failed to create register cache\n");
+		goto err;
+	}
+
+	if (wm8350_reg_read(wm8350, 0) == 0x0)
+		printk("wm8350: found Rev C device\n");
+	else if (wm8350_reg_read(wm8350, 0) == 0x6143)
+		printk("wm8350: found Rev E device\n");
+	else {
+		printk(KERN_ERR "wm8350: device is not a WM8350\n");
+		ret = -ENODEV;
+		goto err;
+	}
+	
+	if (program) {
+		/* once only... 
+		 * or if VRTC batt discharges and there is no PIC */
+		program_wm8350(wm8350);
+		return -EBUSY;
+	}
+	
+	/* configure system power circuits */
+	pm_register_circuit(&mx31_cpu_circuit);
+	pm_register_circuit(&mx31_io_hi_circuit);
+	pm_register_circuit(&mx31_io_lo_circuit);
+	pm_register_circuit(&mx31_io_gen_circuit);
+	pm_register_circuit(&mx31_mem_circuit);
+	pm_register_circuit(&mx31_vcam_circuit);
+	pm_register_circuit(&mx31_vsim_circuit);
+	pm_register_circuit(&mx31_transceiver_circuit);
+	
+	/* backlight properties */
+	wm8350->backlight.props.max_brightness = 10;
+	wm8350->backlight.props.power = FB_BLANK_UNBLANK;
+	wm8350->backlight.props.brightness = 5;
+	wm8350->backlight.dcdc = WM8350_DCDC_2;
+	wm8350->backlight.isink = WM8350_ISINK_B;
+	wm8350->backlight.retries = 5;
+	
+	/* LED properties */
+	wm8350->led.dcdc = WM8350_DCDC_5;
+	wm8350->led.isink = WM8350_ISINK_A;
+	wm8350->led.retries = 5;
+	wm8350->led.half_value = 986376;
+	wm8350->led.full_value = 1972752;
+	
+	/* RTC periodic irq :- 
+	 * If we had a dedicated RTC PER IRQ pin we could add it here. */
+	wm8350->rtc.per_irq = 0;
+	
+	/* now register all i.MX32ADS WM8350 client devices */
+	wm8350_device_register_pmic(wm8350);
+	wm8350_device_register_rtc(wm8350);
+	wm8350_device_register_wdg(wm8350);
+	wm8350_device_register_led(wm8350);
+	wm8350_device_register_power(wm8350);
+	wm8350_device_register_backlight(wm8350);
+	
+	/* make sure DCDC1 is back at 1.6 volts so we can safely work */
+	/* DCDC1 @ 1.6 V */
+	wm8350_dcdc_set_voltage(&wm8350->pmic, WM8350_DCDC_1, 1600);
+	wm8350_dcdc_set_slot(&wm8350->pmic, WM8350_DCDC_1, 1, 0, 
+		WM8350_DC1_ERRACT_SHUTDOWN_SYS);
+	wm8350_dcdc_enable(&wm8350->pmic, WM8350_DCDC_1, 1);
+	
+	/* register sound */
+	imx32ads_snd_device = platform_device_alloc(imx32ads_audio, -1);
+	if (!imx32ads_snd_device) {
+		ret = -ENOMEM;
+		goto err;
+	}
+	platform_set_drvdata(imx32ads_snd_device, &wm8350->audio);
+	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(MXC_PMIC_INT_LINE, IRQT_RISING);
+	ret = request_irq(MXC_PMIC_INT_LINE, wm8350_irq_handler, 
+		IRQF_DISABLED, "wm8350-pmic", wm8350);
+	if (ret != 0) {
+		printk(KERN_ERR "wm8350: cant request irq %d\n", 
+			MXC_PMIC_INT_LINE);
+		goto err;
+	}
+	wm8350->nirq = MXC_PMIC_INT_LINE;
+	config_gpios(wm8350);
+	config_hibernate(wm8350);
+	
+	/* Sw1 --> PWR_ON */
+	wm8350_register_irq(wm8350, WM8350_IRQ_WKUP_ONKEY,
+			    imx32ads_switch_handler);
+	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);
+
+#ifdef MXC_DPTC	
+	/* DPTC reg */
+	mxc_pmic_register(&wm8350->pmic, wm8350_dcdc_set_voltage);
+#endif
+	
+#if BATTERY
+	/* not much use without a battery atm */
+	wm8350_init_battery(wm8350);
+#endif
+	return ret;
+snd_err:	
+	platform_device_put(imx32ads_snd_device);
+err:
+	kfree(wm8350->reg_cache);
+	return ret;
+}
+
+#if MXC_SPI
+static int wm8350_pmic_spi_detect(struct platform_device *pdev)
+{
+	struct wm8350 *wm8350;
+	int ret;
+
+	wm8350 = kzalloc(sizeof(struct wm8350), GFP_KERNEL);
+	if (wm8350 == NULL)
+		return -ENOMEM;
+	
+	dev_set_drvdata(&pdev->dev, wm8350);
+	mutex_init(&wm8350->work_mutex);
+	wm8350_set_io(wm8350, WM8350_IO_CUSTOM, mx31_spi_read, mx31_spi_write);
+	ret = wm8350_init(wm8350);
+	if (ret < 0)
+		goto err;
+	return ret;
+
+err:
+	kfree(wm8350);
+	return ret;
+}
+
+#else
+
+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;
+}
+#endif
+
+#ifdef CONFIG_PM
+static int imx32ads_wm8350_pmic_suspend(struct platform_device *dev, 
+	pm_message_t state)
+{
+	int ret = 0;
+	return ret;
+}
+
+static int imx32ads_wm8350_pmic_resume(struct platform_device *dev)
+{
+	int ret = 0;
+	return ret;
+}
+
+#else
+#define imx32ads_wm8350_pmic_suspend	NULL
+#define imx32ads_wm8350_pmic_resume	NULL
+#endif
+
+static int __devinit imx32ads_wm8350_pmic_probe(struct platform_device *dev)
+{
+#if MXC_SPI
+	return wm8350_pmic_spi_detect(dev);
+#else
+	return i2c_add_driver(&wm8350_i2c_driver);
+#endif
+}
+
+static int __devexit imx32ads_wm8350_pmic_remove(struct platform_device *dev)
+{
+#if MXC_SPI
+	wm8350_spi_detach(dev);
+#else
+	i2c_del_driver(&wm8350_i2c_driver);
+#endif
+	return 0;
+}
+
+static struct platform_driver imx32ads_wm8350_pmic_driver = {
+	.probe		= imx32ads_wm8350_pmic_probe,
+	.remove		= __devexit_p(imx32ads_wm8350_pmic_remove),
+	.suspend	= imx32ads_wm8350_pmic_suspend,
+	.resume		= imx32ads_wm8350_pmic_resume,
+	.driver		= {
+		.name	= "wm8350-imx32ads",
+	},
+};
+
+static int __init imx32ads_wm8350_pmic_init(void)
+{
+	return platform_driver_register(&imx32ads_wm8350_pmic_driver);
+}
+
+static void __exit imx32ads_wm8350_pmic_exit(void)
+{
+	platform_driver_unregister(&imx32ads_wm8350_pmic_driver);
+}
+
+module_init(imx32ads_wm8350_pmic_init);
+module_exit(imx32ads_wm8350_pmic_exit);
+
+MODULE_AUTHOR("Liam Girdwood");
+MODULE_DESCRIPTION("PMIC WM8350 Driver for i.MX32ADS");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pmic/pmic-wm8350.c b/drivers/pmic/pmic-wm8350.c
new file mode 100644
index 0000000..8e0a5f2
--- /dev/null
+++ b/drivers/pmic/pmic-wm8350.c
@@ -0,0 +1,813 @@
+/*
+ * wm8350_pmu.c  --  Power Managment 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/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/pmic/wm8350.h>
+#include <linux/pmic.h>
+
+#define WM8350_PMIC_VERSION	"0.3"
+
+/* debug */
+#define WM8350_DEBUG 0
+#if WM8350_DEBUG
+#define dbg(format, arg...) printk(format, ## arg)
+#else
+#define dbg(format, arg...)
+#endif
+
+static ssize_t pmic_reg_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct wm8350_pmic *pmic = to_wm8350_pmic_device(dev);
+	struct wm8350 *wm8350 = to_wm8350_from_pmic(pmic);
+	int i = 0, count = 0;
+
+	for (;i < WM8350_MAX_REGISTER + 1; i++)
+	//	count += sprintf(buf, "R%d = 0x%2.2x%2.2x\n", i >> 1, reg[i], reg[i+1]);
+		printk("R%d = 0x%4.4x\n", i,  wm8350_reg_read(wm8350,i)); // hack to fix
+
+	return count;
+}
+static DEVICE_ATTR(pmic_reg, 0444, pmic_reg_show, NULL);
+
+/* 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 huA)
+{
+	int i;
+	
+	for (i = ARRAY_SIZE(isink_cur); i >= 0 ; i--) {
+		if (huA > isink_cur[i])
+			return i;
+	}
+	return 0;
+}
+
+int wm8350_isink_set_current(struct wm8350_pmic *pmic, int isink, 
+	int huA)
+{
+	struct wm8350 *wm8350 = to_wm8350_from_pmic(pmic);
+	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(huA));
+		//printk("val %x\n", wm8350_reg_read(wm8350, WM8350_CURRENT_SINK_DRIVER_A));
+		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(huA));
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+EXPORT_SYMBOL_GPL(wm8350_isink_set_current);
+
+int wm8350_isink_enable(struct wm8350_pmic *pmic, int isink, int enable, 
+	int hibernate_enable)
+{
+	struct wm8350 *wm8350 = to_wm8350_from_pmic(pmic);
+	
+	switch (isink) {
+	case WM8350_ISINK_A:
+		if (enable)
+			wm8350_set_bits(wm8350, WM8350_POWER_MGMT_7, 
+				WM8350_CS1_ENA);
+		else {
+			wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_7, 
+				WM8350_CS1_ENA);
+		}	
+		if (hibernate_enable)
+			wm8350_set_bits(wm8350, WM8350_CURRENT_SINK_DRIVER_A, 
+				WM8350_CS1_HIB_MODE);
+		else
+			wm8350_clear_bits(wm8350, WM8350_CURRENT_SINK_DRIVER_A, 
+				WM8350_CS1_HIB_MODE);	
+		break;
+	case WM8350_ISINK_B:
+		if (enable)
+			wm8350_set_bits(wm8350, WM8350_POWER_MGMT_7, 
+				WM8350_CS2_ENA);
+		else {
+			wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_7, 
+				WM8350_CS2_ENA);
+		}	
+		if (hibernate_enable)
+			wm8350_set_bits(wm8350, WM8350_CURRENT_SINK_DRIVER_B, 
+				WM8350_CS2_HIB_MODE);
+		else
+			wm8350_clear_bits(wm8350, WM8350_CURRENT_SINK_DRIVER_B, 
+				WM8350_CS2_HIB_MODE);	
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+EXPORT_SYMBOL_GPL(wm8350_isink_enable);
+
+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);
+
+int wm8350_isink_trigger_flash(struct wm8350_pmic *pmic, int isink)
+{
+	struct wm8350 *wm8350 = to_wm8350_from_pmic(pmic);
+	u16 val;
+	
+	switch (isink) {
+	case WM8350_ISINK_A:
+		val = wm8350_reg_read(wm8350, WM8350_CSA_FLASH_CONTROL) & 
+			~WM8350_CS1_DRIVE;
+		wm8350_reg_write(wm8350, WM8350_CSA_FLASH_CONTROL, val | 
+			WM8350_CS1_DRIVE);
+		break;
+	case WM8350_ISINK_B:
+		val = wm8350_reg_read(wm8350, WM8350_CSA_FLASH_CONTROL) & 
+			~WM8350_CS2_DRIVE;
+		wm8350_reg_write(wm8350, WM8350_CSA_FLASH_CONTROL, val | 
+			WM8350_CS2_DRIVE);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+int wm8350_dcdc_set_voltage(struct wm8350_pmic *pmic, int dcdc, int mV)
+{
+	struct wm8350 *wm8350 = to_wm8350_from_pmic(pmic);
+	int volt_reg;
+	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;
+}
+EXPORT_SYMBOL_GPL(wm8350_dcdc_set_voltage);
+
+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);
+
+int wm8350_ldo_set_voltage(struct wm8350_pmic *pmic, int ldo, int mV)
+{
+	struct wm8350 *wm8350 = to_wm8350_from_pmic(pmic);
+	int volt_reg;
+	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;
+}
+EXPORT_SYMBOL_GPL(wm8350_ldo_set_voltage);
+
+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;
+	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_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_dcdc_set_mode(struct wm8350_pmic *pmic, int dcdc, u16 active, 
+	u16 sleep)
+{
+	struct wm8350 *wm8350 = to_wm8350_from_pmic(pmic);
+	u16 shift;
+	
+	if (dcdc == WM8350_DCDC_2 || dcdc == WM8350_DCDC_5)
+		return -EINVAL;
+	
+	dbg("%s %d mode: %s %s\n", __FUNCTION__, dcdc, active ? "on" : "off",
+		sleep ? "on" : "off");
+	
+	shift = dcdc - WM8350_DCDC_1;
+	
+	if (active)
+		wm8350_set_bits(wm8350, WM8350_DCDC_ACTIVE_OPTIONS, 1 << shift);
+	else
+		wm8350_clear_bits(wm8350, WM8350_DCDC_ACTIVE_OPTIONS, 1 << shift);
+	
+	if (sleep)
+		wm8350_set_bits(wm8350, WM8350_DCDC_SLEEP_OPTIONS, 1 << shift);
+	else
+		wm8350_clear_bits(wm8350, WM8350_DCDC_SLEEP_OPTIONS, 1 << shift);
+		
+	return 0;
+}
+EXPORT_SYMBOL_GPL(wm8350_dcdc_set_mode);
+
+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 %d mode: %s %s\n", __FUNCTION__, dcdc, mode ? "normal" : "boost",
+		ilim ? "low" : "normal");
+	
+	switch (dcdc) {
+	case WM8350_DCDC_2:
+		val = wm8350_reg_read(wm8350, WM8350_DCDC2_CONTROL)
+			 & ~(WM8350_DC2_MODE_MASK | WM8350_DC2_ILIM_MASK |
+			 WM8350_DC2_RMP_MASK | WM8350_DC2_FBSRC_MASK);
+		wm8350_reg_write(wm8350, WM8350_DCDC2_CONTROL, val | 
+			(mode << WM8350_DC2_MODE_SHIFT) | 
+			(ilim << WM8350_DC2_ILIM_SHIFT) |
+			(ramp << WM8350_DC2_RMP_SHIFT) |
+			(feedback << WM8350_DC2_FBSRC_SHIFT));
+		break;
+	case WM8350_DCDC_5:
+		val = wm8350_reg_read(wm8350, WM8350_DCDC5_CONTROL)
+			 & ~(WM8350_DC5_MODE_MASK | WM8350_DC5_ILIM_MASK |
+			 WM8350_DC5_RMP_MASK | WM8350_DC5_FBSRC_MASK);
+		wm8350_reg_write(wm8350, WM8350_DCDC5_CONTROL, val | 
+			(mode << WM8350_DC5_MODE_SHIFT) | 
+			(ilim << WM8350_DC5_ILIM_SHIFT) |
+			(ramp << WM8350_DC5_RMP_SHIFT) |
+			(feedback << WM8350_DC5_FBSRC_SHIFT));
+		break;
+	default:
+		return -EINVAL;
+	}
+		
+	return 0;
+}
+EXPORT_SYMBOL_GPL(wm8350_dcdc25_set_mode);
+
+int wm8350_dcdc_enable(struct wm8350_pmic *pmic, int dcdc, int enable)
+{
+	struct wm8350 *wm8350 = to_wm8350_from_pmic(pmic);
+	u16 shift;
+	
+	dbg("%s %d --> %s\n", __FUNCTION__, dcdc, enable ? "on" : "off");
+	
+	if (dcdc < WM8350_DCDC_1 || dcdc > WM8350_DCDC_6)
+		return -EINVAL;
+	
+	shift = dcdc - WM8350_DCDC_1;
+	
+	if (enable)
+		wm8350_set_bits(wm8350, WM8350_DCDC_LDO_REQUESTED, 1 << shift);
+	else
+		wm8350_clear_bits(wm8350, WM8350_DCDC_LDO_REQUESTED, 1 << shift);
+	
+	return 0;
+}
+EXPORT_SYMBOL_GPL(wm8350_dcdc_enable);
+
+int wm8350_ldo_enable(struct wm8350_pmic *pmic, int ldo, int enable)
+{
+	struct wm8350 *wm8350 = to_wm8350_from_pmic(pmic);
+	u16 shift;
+	
+	dbg("%s %d --> %s\n", __FUNCTION__, ldo, enable ? "on" : "off");
+	
+	if (ldo < WM8350_LDO_1 || ldo > WM8350_LDO_4)
+		return -EINVAL;
+	
+	shift = (ldo - WM8350_LDO_1) + 8;
+
+	if (enable)
+		wm8350_set_bits(wm8350, WM8350_DCDC_LDO_REQUESTED, 1 << shift);
+	else
+		wm8350_clear_bits(wm8350, WM8350_DCDC_LDO_REQUESTED, 1 << shift);
+	
+	return 0;
+}
+EXPORT_SYMBOL_GPL(wm8350_ldo_enable);
+
+static int set_voltage (struct pm_regulator *reg, int mV, int Vid)
+{
+	struct wm8350_pmic *pmic = (struct wm8350_pmic*)reg->private;
+	int ret = 0;
+	
+	switch (reg->id) {
+	case WM8350_DCDC_1:
+	case WM8350_DCDC_2:
+	case WM8350_DCDC_3:
+	case WM8350_DCDC_4:
+	case WM8350_DCDC_5:
+	case WM8350_DCDC_6:
+		ret = wm8350_dcdc_set_voltage(pmic, reg->id, mV);
+		break;
+	case WM8350_LDO_1:
+	case WM8350_LDO_2:
+	case WM8350_LDO_3:
+	case WM8350_LDO_4:
+		ret = wm8350_ldo_set_voltage(pmic, reg->id, mV);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
+static int enable (struct pm_regulator *reg, int enable)
+{
+	struct wm8350_pmic *pmic = (struct wm8350_pmic*)reg->private;
+	int ret = 0;
+	
+	switch (reg->id) {
+	case WM8350_DCDC_1:
+	case WM8350_DCDC_2:
+	case WM8350_DCDC_3:
+	case WM8350_DCDC_4:
+	case WM8350_DCDC_5:
+	case WM8350_DCDC_6:
+		ret = wm8350_dcdc_enable(pmic, reg->id, enable);
+		break;
+	case WM8350_LDO_1:
+	case WM8350_LDO_2:
+	case WM8350_LDO_3:
+	case WM8350_LDO_4:
+		ret = wm8350_ldo_enable(pmic, reg->id, enable);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
+struct pm_regulator_ops wm8350_pmic_ops = {
+	.set_voltage	= set_voltage,
+	.enable		= enable,
+};
+
+struct pm_regulator wm8350_dcdc1 = {
+	.name	= "DCDC1",
+	.id	= WM8350_DCDC_1,
+	.ops	= &wm8350_pmic_ops,
+	.mV_range	= {
+		.dmin	= 850,
+		.dmax	= 3400,
+	},
+	.mV_control	= PM_CONTROL_DYNAMIC,
+	.mA_control	= PM_CONTROL_NONE,
+};
+
+struct pm_regulator wm8350_dcdc6 = {
+	.name	= "DCDC6",
+	.id	= WM8350_DCDC_6,
+	.ops	= &wm8350_pmic_ops,
+	.mV_range	= {
+		.dmin	= 850,
+		.dmax	= 3400,
+	},
+	.mV_control	= PM_CONTROL_DYNAMIC,
+	.mA_control	= PM_CONTROL_NONE,
+};
+
+struct pm_regulator wm8350_dcdc3 = {
+	.name	= "DCDC3",
+	.id	= WM8350_DCDC_3,
+	.ops	= &wm8350_pmic_ops,
+	.mV_range	= {
+		.dmin	= 850,
+		.dmax	= 3400,
+	},
+	.mV_control	= PM_CONTROL_DYNAMIC,
+	.mA_control	= PM_CONTROL_NONE,
+};
+
+struct pm_regulator wm8350_dcdc4 = {
+	.name	= "DCDC4",
+	.id	= WM8350_DCDC_4,
+	.ops	= &wm8350_pmic_ops,
+	.mV_range	= {
+		.dmin	= 850,
+		.dmax	= 3400,
+	},
+	.mV_control	= PM_CONTROL_DYNAMIC,
+	.mA_control	= PM_CONTROL_NONE,
+};
+
+struct pm_regulator wm8350_dcdc2 = {
+	.name	= "DCDC2",
+	.id	= WM8350_DCDC_2,
+	.ops	= &wm8350_pmic_ops,
+	.mV_range	= {
+		.dmin	= 5000,
+		.dmax	= 20000,
+	},
+	.mV_control	= PM_CONTROL_NONE,
+	.mA_range	= {
+		.dmin	= 0,
+		.dmax	= 224,
+	},
+	.mA_control	= PM_CONTROL_DYNAMIC,
+};
+
+struct pm_regulator wm8350_dcdc5 = {
+	.name	= "DCDC5",
+	.id	= WM8350_DCDC_5,
+	.ops	= &wm8350_pmic_ops,
+	.mV_range	= {
+		.dmin	= 5000,
+		.dmax	= 20000,
+	},
+	.mV_control	= PM_CONTROL_NONE,
+	.mA_range	= {
+		.dmin	= 0,
+		.dmax	= 224,
+	},
+	.mA_control	= PM_CONTROL_DYNAMIC,
+};
+
+struct pm_regulator wm8350_ldo1 = {
+	.name	= "LDO1",
+	.id	= WM8350_LDO_1,
+	.ops	= &wm8350_pmic_ops,
+	.mV_range	= {
+		.dmin	= 900,
+		.dmax	= 3400,
+	},
+	.mV_control	= PM_CONTROL_DYNAMIC,
+	.mA_control	= PM_CONTROL_NONE,
+};
+
+struct pm_regulator wm8350_ldo2 = {
+	.name	= "LDO2",
+	.id	= WM8350_LDO_2,
+	.ops	= &wm8350_pmic_ops,
+	.mV_range	= {
+		.dmin	= 900,
+		.dmax	= 3400,
+	},
+	.mV_control	= PM_CONTROL_DYNAMIC,
+	.mA_control	= PM_CONTROL_NONE,
+};
+
+struct pm_regulator wm8350_ldo3 = {
+	.name	= "LDO3",
+	.id	= WM8350_LDO_3,
+	.ops	= &wm8350_pmic_ops,
+	.mV_range	= {
+		.dmin	= 900,
+		.dmax	= 3400,
+	},
+	.mV_control	= PM_CONTROL_DYNAMIC,
+	.mA_control	= PM_CONTROL_NONE,
+};
+
+struct pm_regulator wm8350_ldo4 = {
+	.name	= "LDO4",
+	.id	= WM8350_LDO_4,
+	.ops	= &wm8350_pmic_ops,
+	.mV_range	= {
+		.dmin	= 900,
+		.dmax	= 3400,
+	},
+	.mV_control	= PM_CONTROL_DYNAMIC,
+	.mA_control	= PM_CONTROL_NONE,
+};
+
+
+static int wm8350_probe(struct device *dev)
+{
+	int err;
+	
+	printk(KERN_INFO "WM8350 PMIC driver version %s\n", WM8350_PMIC_VERSION);
+	err = device_create_file(dev, &dev_attr_pmic_reg);
+	if (err < 0)
+		printk(KERN_WARNING "asoc: failed to add pmic sysfs entries\n");
+	pm_register_regulator(&wm8350_dcdc1, dev);
+	pm_register_regulator(&wm8350_dcdc2, dev);
+	pm_register_regulator(&wm8350_dcdc3, dev);
+	pm_register_regulator(&wm8350_dcdc4, dev);
+	pm_register_regulator(&wm8350_dcdc5, dev);
+	pm_register_regulator(&wm8350_dcdc6, dev);
+	pm_register_regulator(&wm8350_ldo1, dev);
+	pm_register_regulator(&wm8350_ldo2, dev);
+	pm_register_regulator(&wm8350_ldo3, dev);
+	pm_register_regulator(&wm8350_ldo4, dev);
+	return 0;
+}
+
+static int wm8350_remove (struct device *dev)
+{
+	pm_unregister_regulator(&wm8350_ldo4);
+	pm_unregister_regulator(&wm8350_ldo3);
+	pm_unregister_regulator(&wm8350_ldo2);
+	pm_unregister_regulator(&wm8350_ldo1);
+	pm_unregister_regulator(&wm8350_dcdc6);
+	pm_unregister_regulator(&wm8350_dcdc5);
+	pm_unregister_regulator(&wm8350_dcdc4);
+	pm_unregister_regulator(&wm8350_dcdc3);
+	pm_unregister_regulator(&wm8350_dcdc2);
+	pm_unregister_regulator(&wm8350_dcdc1);
+	device_remove_file(dev, &dev_attr_pmic_reg);
+	return 0;
+}
+
+static void wm8350_shutdown (struct device *dev)
+{
+}
+
+static int wm8350_suspend (struct device *dev, pm_message_t state)
+{
+	return 0;
+}
+
+static int wm8350_resume (struct device *dev)
+{
+	return 0;
+}
+
+struct device_driver wm8350_driver = {
+	.name = "wm8350-pmic",
+	.bus = &wm8350_bus_type,
+	.owner = THIS_MODULE,
+	.probe = wm8350_probe,
+	.remove = wm8350_remove,
+	.shutdown = wm8350_shutdown,
+	.suspend = wm8350_suspend,
+	.resume = wm8350_resume,
+};
+
+static int __devinit wm8350_pmu_init(void)
+{
+	return driver_register(&wm8350_driver);
+}
+
+static void wm8350_pmu_exit(void)
+{
+ 	driver_unregister(&wm8350_driver);
+}
+
+module_init(wm8350_pmu_init);
+module_exit(wm8350_pmu_exit);
+
+/* Module information */
+MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
+MODULE_DESCRIPTION("WM8350 PMIC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index ff9e35c..aaf9c69 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -344,6 +344,16 @@ config RTC_DRV_V3020
 	  This driver can also be built as a module. If so, the module
 	  will be called rtc-v3020.
 
+config RTC_DRV_WM8350
+	tristate "Wolfson Microelectronics WM8350"
+	depends on RTC_CLASS
+	help
+	  If you say yes here you will get support for the
+	  WM8300 RTC chip.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called rtc-wm8350.
+
 comment "on-CPU RTC drivers"
 
 config RTC_DRV_OMAP
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index d3a33aa..52bc15d 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -41,6 +41,7 @@ obj-$(CONFIG_RTC_DRV_RS5C313)	+= rtc-rs5c313.o
 obj-$(CONFIG_RTC_DRV_RS5C348)	+= rtc-rs5c348.o
 obj-$(CONFIG_RTC_DRV_RS5C372)	+= rtc-rs5c372.o
 obj-$(CONFIG_RTC_DRV_S3C)	+= rtc-s3c.o
+bj-$(CONFIG_RTC_DRV_WM8350)	+= rtc-wm8350.o
 obj-$(CONFIG_RTC_DRV_SA1100)	+= rtc-sa1100.o
 obj-$(CONFIG_RTC_DRV_SH)	+= rtc-sh.o
 obj-$(CONFIG_RTC_DRV_STK17TA8)	+= rtc-stk17ta8.o
diff --git a/drivers/rtc/rtc-wm8350.c b/drivers/rtc/rtc-wm8350.c
new file mode 100644
index 0000000..a647ea2
--- /dev/null
+++ b/drivers/rtc/rtc-wm8350.c
@@ -0,0 +1,448 @@
+/*
+ *	Real Time Clock driver for Wolfson Microelectronics WM8350
+ *
+ *	Copyright (C) 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
+ *    10th Apr 2007   Initial version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/time.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+#include <linux/interrupt.h>
+#include <linux/ioctl.h>
+#include <linux/completion.h>
+#include <linux/pmic/wm8350.h>
+#include <linux/delay.h>
+#include <asm/uaccess.h>
+#include <asm/rtc.h>
+#include <asm/mach/time.h>
+
+#define WM8350_RTC_VERSION	"0.2"
+#define WM8350_SET_ALM_RETRIES	5
+#define WM8350_SET_TIME_RETRIES	5
+#define WM8350_GET_TIME_RETRIES	5
+
+/*
+ * Debug
+ */
+#define WM8350_DEBUG 0
+
+#ifdef WM8350_DEBUG
+#define dbg(format, arg...) \
+	printk(KERN_DEBUG ": " format "\n" , ## arg)
+#else
+#define dbg(format, arg...) do {} while (0)
+#endif
+
+/*
+ * Read current time and date in RTC
+ */
+static int wm8350_rtc_readtime(struct device *dev, struct rtc_time *tm)
+{
+	struct wm8350_rtc *wm_rtc = to_wm8350_rtc_device(dev);
+	struct wm8350 *wm8350 = to_wm8350_from_rtc(wm_rtc);
+	u16 time1[4], time2[4];
+	int retries = WM8350_GET_TIME_RETRIES, ret;
+
+	/*
+	 * Read the time twice and compare.
+	 * If time1 == time2, then time is valid else retry.
+	 */
+	do {
+		ret = wm8350_block_read(wm8350, WM8350_RTC_SECONDS_MINUTES,
+					4, time1);
+		if (ret < 0)
+			return ret;
+		ret = wm8350_block_read(wm8350, WM8350_RTC_SECONDS_MINUTES,
+					4, time2);
+		if (ret < 0)
+			return ret;
+
+		if (!memcmp(time1,time2,8)) {
+			tm->tm_sec  = time1[0] & WM8350_RTC_SECS_MASK;
+
+			tm->tm_min  = (time1[0] & WM8350_RTC_MINS_MASK)
+					>> WM8350_RTC_MINS_SHIFT;
+
+			tm->tm_hour = time1[1] & WM8350_RTC_HRS_MASK;
+
+			tm->tm_wday = ((time1[1] >> WM8350_RTC_DAY_SHIFT)
+					& 0x7) - 1;
+
+			tm->tm_mon  = ((time1[2] & WM8350_RTC_MTH_MASK)
+					>> WM8350_RTC_MTH_SHIFT) - 1;
+
+			tm->tm_mday = (time1[2] & WM8350_RTC_DATE_MASK);
+
+			tm->tm_year  = ((time1[3] & WM8350_RTC_YHUNDREDS_MASK)
+					>> WM8350_RTC_YHUNDREDS_SHIFT) * 100;
+			tm->tm_year += time1[3] & WM8350_RTC_YUNITS_MASK;
+
+			tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon,
+							tm->tm_year);
+			tm->tm_year -= 1900;
+
+			return 0;
+		}
+	} while (retries--);
+
+	printk(KERN_ERR "wm8350-rtc: failed to read RTC time\n");
+	return -EIO;
+}
+
+/*
+ * Set current time and date in RTC
+ */
+static int wm8350_rtc_settime(struct device *dev, struct rtc_time *tm)
+{
+	struct wm8350_rtc *wm_rtc = to_wm8350_rtc_device(dev);
+	struct wm8350 *wm8350 = to_wm8350_from_rtc(wm_rtc);
+	u16 time1[4];
+	u16 rtc_ctrl;
+	int ret, retries = WM8350_SET_TIME_RETRIES;
+
+	dbg("%s tm->tm_wday %d, tm->tm_mon %d",
+		 __FUNCTION__, tm->tm_wday, tm->tm_mon);
+	time1[0] = tm->tm_sec;
+	time1[0] |= tm->tm_min << WM8350_RTC_MINS_SHIFT;
+	time1[1] = tm->tm_hour;
+	time1[1] |= (tm->tm_wday + 1) << WM8350_RTC_DAY_SHIFT;
+	time1[2] = tm->tm_mday;
+	time1[2] |= (tm->tm_mon + 1) << WM8350_RTC_MTH_SHIFT;
+	time1[3] = ((tm->tm_year + 1900) / 100) << WM8350_RTC_YHUNDREDS_SHIFT;
+	time1[3] |= (tm->tm_year + 1900) % 100;
+
+	/* Set RTC_SET to stop the clock */
+	ret = wm8350_set_bits(wm8350, WM8350_RTC_TIME_CONTROL, WM8350_RTC_SET);
+	if (ret < 0)
+		return ret;
+
+	/* Wait until confirmation of stopping */
+	do {
+		rtc_ctrl = wm8350_reg_read(wm8350, WM8350_RTC_TIME_CONTROL);
+		schedule_timeout_interruptible(msecs_to_jiffies(1));
+	} while (retries-- && !(rtc_ctrl & WM8350_RTC_STS));
+
+	if (!retries) {
+		printk(KERN_ERR "wm8350-rtc time out on set confirmation\n");
+		return -EIO;
+	}
+
+	/* Write time to RTC */
+	ret = wm8350_block_write(wm8350, WM8350_RTC_SECONDS_MINUTES,
+		4, time1);
+	if (ret < 0)
+		return ret;
+
+	/* Clear RTC_SET to start the clock */
+	ret = wm8350_clear_bits(wm8350, WM8350_RTC_TIME_CONTROL,
+				WM8350_RTC_SET);
+	return ret;
+}
+
+/*
+ * Read alarm time and date in RTC
+ */
+static int wm8350_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+	struct wm8350_rtc *wm_rtc = to_wm8350_rtc_device(dev);
+	struct wm8350 *wm8350 = to_wm8350_from_rtc(wm_rtc);
+	struct rtc_time *tm = &alrm->time;
+	u16 time1[3];
+	int ret;
+
+	ret = wm8350_block_read(wm8350, WM8350_ALARM_SECONDS_MINUTES,
+					3, time1);
+	if (ret <0)
+		return ret;
+
+	tm->tm_sec  = time1[0] & WM8350_RTC_ALMSECS_MASK;
+	tm->tm_min  = (time1[0] & WM8350_RTC_ALMMINS_MASK)
+			>> WM8350_RTC_ALMMINS_SHIFT;
+	tm->tm_hour = time1[1] & WM8350_RTC_ALMHRS_MASK;
+	tm->tm_wday = -1;
+	tm->tm_mday = -1;
+	tm->tm_mon  = -1;
+	tm->tm_year = -1;
+
+	return 0;
+}
+
+/*
+ * Set alarm time and date in RTC
+ */
+static int wm8350_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+	struct wm8350_rtc *wm_rtc = to_wm8350_rtc_device(dev);
+	struct wm8350 *wm8350 = to_wm8350_from_rtc(wm_rtc);
+	struct rtc_time *tm = &alrm->time;
+	u16 rtc_ctrl;
+	u16 time1[3];
+	int ret, retries = WM8350_SET_ALM_RETRIES;
+
+	time1[0] = tm->tm_sec;
+	time1[0] |= tm->tm_min << WM8350_RTC_ALMMINS_SHIFT;
+	time1[1] = tm->tm_hour;
+	time1[1] |= WM8350_RTC_ALMDAY_MASK;
+	time1[2] = WM8350_RTC_ALMDATE_MASK;
+	time1[2] |= WM8350_RTC_ALMMTH_MASK;
+
+	/* Set RTC_SET to stop the clock */
+	ret = wm8350_set_bits(wm8350, WM8350_RTC_TIME_CONTROL,
+				WM8350_RTC_ALM