[T106][ZXW-22]7520V3SCV2.01.01.02P42U09_VEC_V0.8_AP_VEC origin source commit

Change-Id: Ic6e05d89ecd62fc34f82b23dcf306c93764aec4b
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/88pm860x-core.c b/ap/os/linux/linux-3.4.x/drivers/mfd/88pm860x-core.c
new file mode 100644
index 0000000..87bd5ba
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/88pm860x-core.c
@@ -0,0 +1,910 @@
+/*
+ * Base driver for Marvell 88PM8607
+ *
+ * Copyright (C) 2009 Marvell International Ltd.
+ * 	Haojian Zhuang <haojian.zhuang@marvell.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/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/88pm860x.h>
+#include <linux/regulator/machine.h>
+
+#define INT_STATUS_NUM			3
+
+static struct resource bk_resources[] __devinitdata = {
+	{PM8606_BACKLIGHT1, PM8606_BACKLIGHT1, "backlight-0", IORESOURCE_IO,},
+	{PM8606_BACKLIGHT2, PM8606_BACKLIGHT2, "backlight-1", IORESOURCE_IO,},
+	{PM8606_BACKLIGHT3, PM8606_BACKLIGHT3, "backlight-2", IORESOURCE_IO,},
+};
+
+static struct resource led_resources[] __devinitdata = {
+	{PM8606_LED1_RED,   PM8606_LED1_RED,   "led0-red",   IORESOURCE_IO,},
+	{PM8606_LED1_GREEN, PM8606_LED1_GREEN, "led0-green", IORESOURCE_IO,},
+	{PM8606_LED1_BLUE,  PM8606_LED1_BLUE,  "led0-blue",  IORESOURCE_IO,},
+	{PM8606_LED2_RED,   PM8606_LED2_RED,   "led1-red",   IORESOURCE_IO,},
+	{PM8606_LED2_GREEN, PM8606_LED2_GREEN, "led1-green", IORESOURCE_IO,},
+	{PM8606_LED2_BLUE,  PM8606_LED2_BLUE,  "led1-blue",  IORESOURCE_IO,},
+};
+
+static struct resource regulator_resources[] __devinitdata = {
+	{PM8607_ID_BUCK1, PM8607_ID_BUCK1, "buck-1", IORESOURCE_IO,},
+	{PM8607_ID_BUCK2, PM8607_ID_BUCK2, "buck-2", IORESOURCE_IO,},
+	{PM8607_ID_BUCK3, PM8607_ID_BUCK3, "buck-3", IORESOURCE_IO,},
+	{PM8607_ID_LDO1,  PM8607_ID_LDO1,  "ldo-01", IORESOURCE_IO,},
+	{PM8607_ID_LDO2,  PM8607_ID_LDO2,  "ldo-02", IORESOURCE_IO,},
+	{PM8607_ID_LDO3,  PM8607_ID_LDO3,  "ldo-03", IORESOURCE_IO,},
+	{PM8607_ID_LDO4,  PM8607_ID_LDO4,  "ldo-04", IORESOURCE_IO,},
+	{PM8607_ID_LDO5,  PM8607_ID_LDO5,  "ldo-05", IORESOURCE_IO,},
+	{PM8607_ID_LDO6,  PM8607_ID_LDO6,  "ldo-06", IORESOURCE_IO,},
+	{PM8607_ID_LDO7,  PM8607_ID_LDO7,  "ldo-07", IORESOURCE_IO,},
+	{PM8607_ID_LDO8,  PM8607_ID_LDO8,  "ldo-08", IORESOURCE_IO,},
+	{PM8607_ID_LDO9,  PM8607_ID_LDO9,  "ldo-09", IORESOURCE_IO,},
+	{PM8607_ID_LDO10, PM8607_ID_LDO10, "ldo-10", IORESOURCE_IO,},
+	{PM8607_ID_LDO11, PM8607_ID_LDO11, "ldo-11", IORESOURCE_IO,},
+	{PM8607_ID_LDO12, PM8607_ID_LDO12, "ldo-12", IORESOURCE_IO,},
+	{PM8607_ID_LDO13, PM8607_ID_LDO13, "ldo-13", IORESOURCE_IO,},
+	{PM8607_ID_LDO14, PM8607_ID_LDO14, "ldo-14", IORESOURCE_IO,},
+	{PM8607_ID_LDO15, PM8607_ID_LDO15, "ldo-15", IORESOURCE_IO,},
+};
+
+static struct resource touch_resources[] __devinitdata = {
+	{PM8607_IRQ_PEN, PM8607_IRQ_PEN, "touch", IORESOURCE_IRQ,},
+};
+
+static struct resource onkey_resources[] __devinitdata = {
+	{PM8607_IRQ_ONKEY, PM8607_IRQ_ONKEY, "onkey", IORESOURCE_IRQ,},
+};
+
+static struct resource codec_resources[] __devinitdata = {
+	/* Headset microphone insertion or removal */
+	{PM8607_IRQ_MICIN,   PM8607_IRQ_MICIN,   "micin",   IORESOURCE_IRQ,},
+	/* Hook-switch press or release */
+	{PM8607_IRQ_HOOK,    PM8607_IRQ_HOOK,    "hook",    IORESOURCE_IRQ,},
+	/* Headset insertion or removal */
+	{PM8607_IRQ_HEADSET, PM8607_IRQ_HEADSET, "headset", IORESOURCE_IRQ,},
+	/* Audio short */
+	{PM8607_IRQ_AUDIO_SHORT, PM8607_IRQ_AUDIO_SHORT, "audio-short", IORESOURCE_IRQ,},
+};
+
+static struct resource battery_resources[] __devinitdata = {
+	{PM8607_IRQ_CC,  PM8607_IRQ_CC,  "columb counter", IORESOURCE_IRQ,},
+	{PM8607_IRQ_BAT, PM8607_IRQ_BAT, "battery",        IORESOURCE_IRQ,},
+};
+
+static struct resource charger_resources[] __devinitdata = {
+	{PM8607_IRQ_CHG,  PM8607_IRQ_CHG,  "charger detect",  IORESOURCE_IRQ,},
+	{PM8607_IRQ_CHG_DONE,  PM8607_IRQ_CHG_DONE,  "charging done",       IORESOURCE_IRQ,},
+	{PM8607_IRQ_CHG_FAULT, PM8607_IRQ_CHG_FAULT, "charging timeout",    IORESOURCE_IRQ,},
+	{PM8607_IRQ_GPADC1,    PM8607_IRQ_GPADC1,    "battery temperature", IORESOURCE_IRQ,},
+	{PM8607_IRQ_VBAT, PM8607_IRQ_VBAT, "battery voltage", IORESOURCE_IRQ,},
+	{PM8607_IRQ_VCHG, PM8607_IRQ_VCHG, "vchg voltage",    IORESOURCE_IRQ,},
+};
+
+static struct resource rtc_resources[] __devinitdata = {
+	{PM8607_IRQ_RTC, PM8607_IRQ_RTC, "rtc", IORESOURCE_IRQ,},
+};
+
+static struct mfd_cell bk_devs[] = {
+	{"88pm860x-backlight", 0,},
+	{"88pm860x-backlight", 1,},
+	{"88pm860x-backlight", 2,},
+};
+
+static struct mfd_cell led_devs[] = {
+	{"88pm860x-led", 0,},
+	{"88pm860x-led", 1,},
+	{"88pm860x-led", 2,},
+	{"88pm860x-led", 3,},
+	{"88pm860x-led", 4,},
+	{"88pm860x-led", 5,},
+};
+
+static struct mfd_cell regulator_devs[] = {
+	{"88pm860x-regulator", 0,},
+	{"88pm860x-regulator", 1,},
+	{"88pm860x-regulator", 2,},
+	{"88pm860x-regulator", 3,},
+	{"88pm860x-regulator", 4,},
+	{"88pm860x-regulator", 5,},
+	{"88pm860x-regulator", 6,},
+	{"88pm860x-regulator", 7,},
+	{"88pm860x-regulator", 8,},
+	{"88pm860x-regulator", 9,},
+	{"88pm860x-regulator", 10,},
+	{"88pm860x-regulator", 11,},
+	{"88pm860x-regulator", 12,},
+	{"88pm860x-regulator", 13,},
+	{"88pm860x-regulator", 14,},
+	{"88pm860x-regulator", 15,},
+	{"88pm860x-regulator", 16,},
+	{"88pm860x-regulator", 17,},
+};
+
+static struct mfd_cell touch_devs[] = {
+	{"88pm860x-touch", -1,},
+};
+
+static struct mfd_cell onkey_devs[] = {
+	{"88pm860x-onkey", -1,},
+};
+
+static struct mfd_cell codec_devs[] = {
+	{"88pm860x-codec", -1,},
+};
+
+static struct mfd_cell power_devs[] = {
+	{"88pm860x-battery", -1,},
+	{"88pm860x-charger", -1,},
+};
+
+static struct mfd_cell rtc_devs[] = {
+	{"88pm860x-rtc", -1,},
+};
+
+
+struct pm860x_irq_data {
+	int	reg;
+	int	mask_reg;
+	int	enable;		/* enable or not */
+	int	offs;		/* bit offset in mask register */
+};
+
+static struct pm860x_irq_data pm860x_irqs[] = {
+	[PM8607_IRQ_ONKEY] = {
+		.reg		= PM8607_INT_STATUS1,
+		.mask_reg	= PM8607_INT_MASK_1,
+		.offs		= 1 << 0,
+	},
+	[PM8607_IRQ_EXTON] = {
+		.reg		= PM8607_INT_STATUS1,
+		.mask_reg	= PM8607_INT_MASK_1,
+		.offs		= 1 << 1,
+	},
+	[PM8607_IRQ_CHG] = {
+		.reg		= PM8607_INT_STATUS1,
+		.mask_reg	= PM8607_INT_MASK_1,
+		.offs		= 1 << 2,
+	},
+	[PM8607_IRQ_BAT] = {
+		.reg		= PM8607_INT_STATUS1,
+		.mask_reg	= PM8607_INT_MASK_1,
+		.offs		= 1 << 3,
+	},
+	[PM8607_IRQ_RTC] = {
+		.reg		= PM8607_INT_STATUS1,
+		.mask_reg	= PM8607_INT_MASK_1,
+		.offs		= 1 << 4,
+	},
+	[PM8607_IRQ_CC] = {
+		.reg		= PM8607_INT_STATUS1,
+		.mask_reg	= PM8607_INT_MASK_1,
+		.offs		= 1 << 5,
+	},
+	[PM8607_IRQ_VBAT] = {
+		.reg		= PM8607_INT_STATUS2,
+		.mask_reg	= PM8607_INT_MASK_2,
+		.offs		= 1 << 0,
+	},
+	[PM8607_IRQ_VCHG] = {
+		.reg		= PM8607_INT_STATUS2,
+		.mask_reg	= PM8607_INT_MASK_2,
+		.offs		= 1 << 1,
+	},
+	[PM8607_IRQ_VSYS] = {
+		.reg		= PM8607_INT_STATUS2,
+		.mask_reg	= PM8607_INT_MASK_2,
+		.offs		= 1 << 2,
+	},
+	[PM8607_IRQ_TINT] = {
+		.reg		= PM8607_INT_STATUS2,
+		.mask_reg	= PM8607_INT_MASK_2,
+		.offs		= 1 << 3,
+	},
+	[PM8607_IRQ_GPADC0] = {
+		.reg		= PM8607_INT_STATUS2,
+		.mask_reg	= PM8607_INT_MASK_2,
+		.offs		= 1 << 4,
+	},
+	[PM8607_IRQ_GPADC1] = {
+		.reg		= PM8607_INT_STATUS2,
+		.mask_reg	= PM8607_INT_MASK_2,
+		.offs		= 1 << 5,
+	},
+	[PM8607_IRQ_GPADC2] = {
+		.reg		= PM8607_INT_STATUS2,
+		.mask_reg	= PM8607_INT_MASK_2,
+		.offs		= 1 << 6,
+	},
+	[PM8607_IRQ_GPADC3] = {
+		.reg		= PM8607_INT_STATUS2,
+		.mask_reg	= PM8607_INT_MASK_2,
+		.offs		= 1 << 7,
+	},
+	[PM8607_IRQ_AUDIO_SHORT] = {
+		.reg		= PM8607_INT_STATUS3,
+		.mask_reg	= PM8607_INT_MASK_3,
+		.offs		= 1 << 0,
+	},
+	[PM8607_IRQ_PEN] = {
+		.reg		= PM8607_INT_STATUS3,
+		.mask_reg	= PM8607_INT_MASK_3,
+		.offs		= 1 << 1,
+	},
+	[PM8607_IRQ_HEADSET] = {
+		.reg		= PM8607_INT_STATUS3,
+		.mask_reg	= PM8607_INT_MASK_3,
+		.offs		= 1 << 2,
+	},
+	[PM8607_IRQ_HOOK] = {
+		.reg		= PM8607_INT_STATUS3,
+		.mask_reg	= PM8607_INT_MASK_3,
+		.offs		= 1 << 3,
+	},
+	[PM8607_IRQ_MICIN] = {
+		.reg		= PM8607_INT_STATUS3,
+		.mask_reg	= PM8607_INT_MASK_3,
+		.offs		= 1 << 4,
+	},
+	[PM8607_IRQ_CHG_FAIL] = {
+		.reg		= PM8607_INT_STATUS3,
+		.mask_reg	= PM8607_INT_MASK_3,
+		.offs		= 1 << 5,
+	},
+	[PM8607_IRQ_CHG_DONE] = {
+		.reg		= PM8607_INT_STATUS3,
+		.mask_reg	= PM8607_INT_MASK_3,
+		.offs		= 1 << 6,
+	},
+	[PM8607_IRQ_CHG_FAULT] = {
+		.reg		= PM8607_INT_STATUS3,
+		.mask_reg	= PM8607_INT_MASK_3,
+		.offs		= 1 << 7,
+	},
+};
+
+static irqreturn_t pm860x_irq(int irq, void *data)
+{
+	struct pm860x_chip *chip = data;
+	struct pm860x_irq_data *irq_data;
+	struct i2c_client *i2c;
+	int read_reg = -1, value = 0;
+	int i;
+
+	i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
+	for (i = 0; i < ARRAY_SIZE(pm860x_irqs); i++) {
+		irq_data = &pm860x_irqs[i];
+		if (read_reg != irq_data->reg) {
+			read_reg = irq_data->reg;
+			value = pm860x_reg_read(i2c, irq_data->reg);
+		}
+		if (value & irq_data->enable)
+			handle_nested_irq(chip->irq_base + i);
+	}
+	return IRQ_HANDLED;
+}
+
+static void pm860x_irq_lock(struct irq_data *data)
+{
+	struct pm860x_chip *chip = irq_data_get_irq_chip_data(data);
+
+	mutex_lock(&chip->irq_lock);
+}
+
+static void pm860x_irq_sync_unlock(struct irq_data *data)
+{
+	struct pm860x_chip *chip = irq_data_get_irq_chip_data(data);
+	struct pm860x_irq_data *irq_data;
+	struct i2c_client *i2c;
+	static unsigned char cached[3] = {0x0, 0x0, 0x0};
+	unsigned char mask[3];
+	int i;
+
+	i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
+	/* Load cached value. In initial, all IRQs are masked */
+	for (i = 0; i < 3; i++)
+		mask[i] = cached[i];
+	for (i = 0; i < ARRAY_SIZE(pm860x_irqs); i++) {
+		irq_data = &pm860x_irqs[i];
+		switch (irq_data->mask_reg) {
+		case PM8607_INT_MASK_1:
+			mask[0] &= ~irq_data->offs;
+			mask[0] |= irq_data->enable;
+			break;
+		case PM8607_INT_MASK_2:
+			mask[1] &= ~irq_data->offs;
+			mask[1] |= irq_data->enable;
+			break;
+		case PM8607_INT_MASK_3:
+			mask[2] &= ~irq_data->offs;
+			mask[2] |= irq_data->enable;
+			break;
+		default:
+			dev_err(chip->dev, "wrong IRQ\n");
+			break;
+		}
+	}
+	/* update mask into registers */
+	for (i = 0; i < 3; i++) {
+		if (mask[i] != cached[i]) {
+			cached[i] = mask[i];
+			pm860x_reg_write(i2c, PM8607_INT_MASK_1 + i, mask[i]);
+		}
+	}
+
+	mutex_unlock(&chip->irq_lock);
+}
+
+static void pm860x_irq_enable(struct irq_data *data)
+{
+	struct pm860x_chip *chip = irq_data_get_irq_chip_data(data);
+	pm860x_irqs[data->irq - chip->irq_base].enable
+		= pm860x_irqs[data->irq - chip->irq_base].offs;
+}
+
+static void pm860x_irq_disable(struct irq_data *data)
+{
+	struct pm860x_chip *chip = irq_data_get_irq_chip_data(data);
+	pm860x_irqs[data->irq - chip->irq_base].enable = 0;
+}
+
+static struct irq_chip pm860x_irq_chip = {
+	.name		= "88pm860x",
+	.irq_bus_lock	= pm860x_irq_lock,
+	.irq_bus_sync_unlock = pm860x_irq_sync_unlock,
+	.irq_enable	= pm860x_irq_enable,
+	.irq_disable	= pm860x_irq_disable,
+};
+
+static int __devinit device_gpadc_init(struct pm860x_chip *chip,
+				       struct pm860x_platform_data *pdata)
+{
+	struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \
+				: chip->companion;
+	int data;
+	int ret;
+
+	/* initialize GPADC without activating it */
+
+	if (!pdata || !pdata->touch)
+		return -EINVAL;
+
+	/* set GPADC MISC1 register */
+	data = 0;
+	data |= (pdata->touch->gpadc_prebias << 1) & PM8607_GPADC_PREBIAS_MASK;
+	data |= (pdata->touch->slot_cycle << 3) & PM8607_GPADC_SLOT_CYCLE_MASK;
+	data |= (pdata->touch->off_scale << 5) & PM8607_GPADC_OFF_SCALE_MASK;
+	data |= (pdata->touch->sw_cal << 7) & PM8607_GPADC_SW_CAL_MASK;
+	if (data) {
+		ret = pm860x_reg_write(i2c, PM8607_GPADC_MISC1, data);
+		if (ret < 0)
+			goto out;
+	}
+	/* set tsi prebias time */
+	if (pdata->touch->tsi_prebias) {
+		data = pdata->touch->tsi_prebias;
+		ret = pm860x_reg_write(i2c, PM8607_TSI_PREBIAS, data);
+		if (ret < 0)
+			goto out;
+	}
+	/* set prebias & prechg time of pen detect */
+	data = 0;
+	data |= pdata->touch->pen_prebias & PM8607_PD_PREBIAS_MASK;
+	data |= (pdata->touch->pen_prechg << 5) & PM8607_PD_PRECHG_MASK;
+	if (data) {
+		ret = pm860x_reg_write(i2c, PM8607_PD_PREBIAS, data);
+		if (ret < 0)
+			goto out;
+	}
+
+	ret = pm860x_set_bits(i2c, PM8607_GPADC_MISC1,
+			      PM8607_GPADC_EN, PM8607_GPADC_EN);
+out:
+	return ret;
+}
+
+static int __devinit device_irq_init(struct pm860x_chip *chip,
+				     struct pm860x_platform_data *pdata)
+{
+	struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \
+				: chip->companion;
+	unsigned char status_buf[INT_STATUS_NUM];
+	unsigned long flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
+	int i, data, mask, ret = -EINVAL;
+	int __irq;
+
+	if (!pdata || !pdata->irq_base) {
+		dev_warn(chip->dev, "No interrupt support on IRQ base\n");
+		return -EINVAL;
+	}
+
+	mask = PM8607_B0_MISC1_INV_INT | PM8607_B0_MISC1_INT_CLEAR
+		| PM8607_B0_MISC1_INT_MASK;
+	data = 0;
+	chip->irq_mode = 0;
+	if (pdata && pdata->irq_mode) {
+		/*
+		 * irq_mode defines the way of clearing interrupt. If it's 1,
+		 * clear IRQ by write. Otherwise, clear it by read.
+		 * This control bit is valid from 88PM8607 B0 steping.
+		 */
+		data |= PM8607_B0_MISC1_INT_CLEAR;
+		chip->irq_mode = 1;
+	}
+	ret = pm860x_set_bits(i2c, PM8607_B0_MISC1, mask, data);
+	if (ret < 0)
+		goto out;
+
+	/* mask all IRQs */
+	memset(status_buf, 0, INT_STATUS_NUM);
+	ret = pm860x_bulk_write(i2c, PM8607_INT_MASK_1,
+				INT_STATUS_NUM, status_buf);
+	if (ret < 0)
+		goto out;
+
+	if (chip->irq_mode) {
+		/* clear interrupt status by write */
+		memset(status_buf, 0xFF, INT_STATUS_NUM);
+		ret = pm860x_bulk_write(i2c, PM8607_INT_STATUS1,
+					INT_STATUS_NUM, status_buf);
+	} else {
+		/* clear interrupt status by read */
+		ret = pm860x_bulk_read(i2c, PM8607_INT_STATUS1,
+					INT_STATUS_NUM, status_buf);
+	}
+	if (ret < 0)
+		goto out;
+
+	mutex_init(&chip->irq_lock);
+	chip->irq_base = pdata->irq_base;
+	chip->core_irq = i2c->irq;
+	if (!chip->core_irq)
+		goto out;
+
+	/* register IRQ by genirq */
+	for (i = 0; i < ARRAY_SIZE(pm860x_irqs); i++) {
+		__irq = i + chip->irq_base;
+		irq_set_chip_data(__irq, chip);
+		irq_set_chip_and_handler(__irq, &pm860x_irq_chip,
+					 handle_edge_irq);
+		irq_set_nested_thread(__irq, 1);
+#ifdef CONFIG_ARM
+		set_irq_flags(__irq, IRQF_VALID);
+#else
+		irq_set_noprobe(__irq);
+#endif
+	}
+
+	ret = request_threaded_irq(chip->core_irq, NULL, pm860x_irq, flags,
+				   "88pm860x", chip);
+	if (ret) {
+		dev_err(chip->dev, "Failed to request IRQ: %d\n", ret);
+		chip->core_irq = 0;
+	}
+
+	return 0;
+out:
+	chip->core_irq = 0;
+	return ret;
+}
+
+static void device_irq_exit(struct pm860x_chip *chip)
+{
+	if (chip->core_irq)
+		free_irq(chip->core_irq, chip);
+}
+
+int pm8606_osc_enable(struct pm860x_chip *chip, unsigned short client)
+{
+	int ret = -EIO;
+	struct i2c_client *i2c = (chip->id == CHIP_PM8606) ?
+		chip->client : chip->companion;
+
+	dev_dbg(chip->dev, "%s(B): client=0x%x\n", __func__, client);
+	dev_dbg(chip->dev, "%s(B): vote=0x%x status=%d\n",
+			__func__, chip->osc_vote,
+			chip->osc_status);
+
+	mutex_lock(&chip->osc_lock);
+	/* Update voting status */
+	chip->osc_vote |= client;
+	/* If reference group is off - turn on*/
+	if (chip->osc_status != PM8606_REF_GP_OSC_ON) {
+		chip->osc_status = PM8606_REF_GP_OSC_UNKNOWN;
+		/* Enable Reference group Vsys */
+		if (pm860x_set_bits(i2c, PM8606_VSYS,
+				PM8606_VSYS_EN, PM8606_VSYS_EN))
+			goto out;
+
+		/*Enable Internal Oscillator */
+		if (pm860x_set_bits(i2c, PM8606_MISC,
+				PM8606_MISC_OSC_EN, PM8606_MISC_OSC_EN))
+			goto out;
+		/* Update status (only if writes succeed) */
+		chip->osc_status = PM8606_REF_GP_OSC_ON;
+	}
+	mutex_unlock(&chip->osc_lock);
+
+	dev_dbg(chip->dev, "%s(A): vote=0x%x status=%d ret=%d\n",
+			__func__, chip->osc_vote,
+			chip->osc_status, ret);
+	return 0;
+out:
+	mutex_unlock(&chip->osc_lock);
+	return ret;
+}
+EXPORT_SYMBOL(pm8606_osc_enable);
+
+int pm8606_osc_disable(struct pm860x_chip *chip, unsigned short client)
+{
+	int ret = -EIO;
+	struct i2c_client *i2c = (chip->id == CHIP_PM8606) ?
+		chip->client : chip->companion;
+
+	dev_dbg(chip->dev, "%s(B): client=0x%x\n", __func__, client);
+	dev_dbg(chip->dev, "%s(B): vote=0x%x status=%d\n",
+			__func__, chip->osc_vote,
+			chip->osc_status);
+
+	mutex_lock(&chip->osc_lock);
+	/*Update voting status */
+	chip->osc_vote &= ~(client);
+	/* If reference group is off and this is the last client to release
+	 * - turn off */
+	if ((chip->osc_status != PM8606_REF_GP_OSC_OFF) &&
+			(chip->osc_vote == REF_GP_NO_CLIENTS)) {
+		chip->osc_status = PM8606_REF_GP_OSC_UNKNOWN;
+		/* Disable Reference group Vsys */
+		if (pm860x_set_bits(i2c, PM8606_VSYS, PM8606_VSYS_EN, 0))
+			goto out;
+		/* Disable Internal Oscillator */
+		if (pm860x_set_bits(i2c, PM8606_MISC, PM8606_MISC_OSC_EN, 0))
+			goto out;
+		chip->osc_status = PM8606_REF_GP_OSC_OFF;
+	}
+	mutex_unlock(&chip->osc_lock);
+
+	dev_dbg(chip->dev, "%s(A): vote=0x%x status=%d ret=%d\n",
+			__func__, chip->osc_vote,
+			chip->osc_status, ret);
+	return 0;
+out:
+	mutex_unlock(&chip->osc_lock);
+	return ret;
+}
+EXPORT_SYMBOL(pm8606_osc_disable);
+
+static void __devinit device_osc_init(struct i2c_client *i2c)
+{
+	struct pm860x_chip *chip = i2c_get_clientdata(i2c);
+
+	mutex_init(&chip->osc_lock);
+	/* init portofino reference group voting and status */
+	/* Disable Reference group Vsys */
+	pm860x_set_bits(i2c, PM8606_VSYS, PM8606_VSYS_EN, 0);
+	/* Disable Internal Oscillator */
+	pm860x_set_bits(i2c, PM8606_MISC, PM8606_MISC_OSC_EN, 0);
+
+	chip->osc_vote = REF_GP_NO_CLIENTS;
+	chip->osc_status = PM8606_REF_GP_OSC_OFF;
+}
+
+static void __devinit device_bk_init(struct pm860x_chip *chip,
+				     struct pm860x_platform_data *pdata)
+{
+	int ret;
+	int i, j, id;
+
+	if ((pdata == NULL) || (pdata->backlight == NULL))
+		return;
+
+	if (pdata->num_backlights > ARRAY_SIZE(bk_devs))
+		pdata->num_backlights = ARRAY_SIZE(bk_devs);
+
+	for (i = 0; i < pdata->num_backlights; i++) {
+		bk_devs[i].platform_data = &pdata->backlight[i];
+		bk_devs[i].pdata_size = sizeof(struct pm860x_backlight_pdata);
+
+		for (j = 0; j < ARRAY_SIZE(bk_devs); j++) {
+			id = bk_resources[j].start;
+			if (pdata->backlight[i].flags != id)
+				continue;
+
+			bk_devs[i].num_resources = 1;
+			bk_devs[i].resources = &bk_resources[j];
+			ret = mfd_add_devices(chip->dev, 0,
+					      &bk_devs[i], 1,
+					      &bk_resources[j], 0);
+			if (ret < 0) {
+				dev_err(chip->dev, "Failed to add "
+					"backlight subdev\n");
+				return;
+			}
+		}
+	}
+}
+
+static void __devinit device_led_init(struct pm860x_chip *chip,
+				      struct pm860x_platform_data *pdata)
+{
+	int ret;
+	int i, j, id;
+
+	if ((pdata == NULL) || (pdata->led == NULL))
+		return;
+
+	if (pdata->num_leds > ARRAY_SIZE(led_devs))
+		pdata->num_leds = ARRAY_SIZE(led_devs);
+
+	for (i = 0; i < pdata->num_leds; i++) {
+		led_devs[i].platform_data = &pdata->led[i];
+		led_devs[i].pdata_size = sizeof(struct pm860x_led_pdata);
+
+		for (j = 0; j < ARRAY_SIZE(led_devs); j++) {
+			id = led_resources[j].start;
+			if (pdata->led[i].flags != id)
+				continue;
+
+			led_devs[i].num_resources = 1;
+			led_devs[i].resources = &led_resources[j],
+			ret = mfd_add_devices(chip->dev, 0,
+					      &led_devs[i], 1,
+					      &led_resources[j], 0);
+			if (ret < 0) {
+				dev_err(chip->dev, "Failed to add "
+					"led subdev\n");
+				return;
+			}
+		}
+	}
+}
+
+static void __devinit device_regulator_init(struct pm860x_chip *chip,
+					    struct pm860x_platform_data *pdata)
+{
+	struct regulator_init_data *initdata;
+	int ret;
+	int i, seq;
+
+	if ((pdata == NULL) || (pdata->regulator == NULL))
+		return;
+
+	if (pdata->num_regulators > ARRAY_SIZE(regulator_devs))
+		pdata->num_regulators = ARRAY_SIZE(regulator_devs);
+
+	for (i = 0, seq = -1; i < pdata->num_regulators; i++) {
+		initdata = &pdata->regulator[i];
+		seq = *(unsigned int *)initdata->driver_data;
+		if ((seq < 0) || (seq > PM8607_ID_RG_MAX)) {
+			dev_err(chip->dev, "Wrong ID(%d) on regulator(%s)\n",
+				seq, initdata->constraints.name);
+			goto out;
+		}
+		regulator_devs[i].platform_data = &pdata->regulator[i];
+		regulator_devs[i].pdata_size = sizeof(struct regulator_init_data);
+		regulator_devs[i].num_resources = 1;
+		regulator_devs[i].resources = &regulator_resources[seq];
+
+		ret = mfd_add_devices(chip->dev, 0, &regulator_devs[i], 1,
+				      &regulator_resources[seq], 0);
+		if (ret < 0) {
+			dev_err(chip->dev, "Failed to add regulator subdev\n");
+			goto out;
+		}
+	}
+out:
+	return;
+}
+
+static void __devinit device_rtc_init(struct pm860x_chip *chip,
+				      struct pm860x_platform_data *pdata)
+{
+	int ret;
+
+	if ((pdata == NULL))
+		return;
+
+	rtc_devs[0].platform_data = pdata->rtc;
+	rtc_devs[0].pdata_size = sizeof(struct pm860x_rtc_pdata);
+	rtc_devs[0].num_resources = ARRAY_SIZE(rtc_resources);
+	rtc_devs[0].resources = &rtc_resources[0];
+	ret = mfd_add_devices(chip->dev, 0, &rtc_devs[0],
+			      ARRAY_SIZE(rtc_devs), &rtc_resources[0],
+			      chip->irq_base);
+	if (ret < 0)
+		dev_err(chip->dev, "Failed to add rtc subdev\n");
+}
+
+static void __devinit device_touch_init(struct pm860x_chip *chip,
+					struct pm860x_platform_data *pdata)
+{
+	int ret;
+
+	if (pdata == NULL)
+		return;
+
+	touch_devs[0].platform_data = pdata->touch;
+	touch_devs[0].pdata_size = sizeof(struct pm860x_touch_pdata);
+	touch_devs[0].num_resources = ARRAY_SIZE(touch_resources);
+	touch_devs[0].resources = &touch_resources[0];
+	ret = mfd_add_devices(chip->dev, 0, &touch_devs[0],
+			      ARRAY_SIZE(touch_devs), &touch_resources[0],
+			      chip->irq_base);
+	if (ret < 0)
+		dev_err(chip->dev, "Failed to add touch subdev\n");
+}
+
+static void __devinit device_power_init(struct pm860x_chip *chip,
+					struct pm860x_platform_data *pdata)
+{
+	int ret;
+
+	if (pdata == NULL)
+		return;
+
+	power_devs[0].platform_data = pdata->power;
+	power_devs[0].pdata_size = sizeof(struct pm860x_power_pdata);
+	power_devs[0].num_resources = ARRAY_SIZE(battery_resources);
+	power_devs[0].resources = &battery_resources[0],
+	ret = mfd_add_devices(chip->dev, 0, &power_devs[0], 1,
+			      &battery_resources[0], chip->irq_base);
+	if (ret < 0)
+		dev_err(chip->dev, "Failed to add battery subdev\n");
+
+	power_devs[1].platform_data = pdata->power;
+	power_devs[1].pdata_size = sizeof(struct pm860x_power_pdata);
+	power_devs[1].num_resources = ARRAY_SIZE(charger_resources);
+	power_devs[1].resources = &charger_resources[0],
+	ret = mfd_add_devices(chip->dev, 0, &power_devs[1], 1,
+			      &charger_resources[0], chip->irq_base);
+	if (ret < 0)
+		dev_err(chip->dev, "Failed to add charger subdev\n");
+}
+
+static void __devinit device_onkey_init(struct pm860x_chip *chip,
+					struct pm860x_platform_data *pdata)
+{
+	int ret;
+
+	onkey_devs[0].num_resources = ARRAY_SIZE(onkey_resources);
+	onkey_devs[0].resources = &onkey_resources[0],
+	ret = mfd_add_devices(chip->dev, 0, &onkey_devs[0],
+			      ARRAY_SIZE(onkey_devs), &onkey_resources[0],
+			      chip->irq_base);
+	if (ret < 0)
+		dev_err(chip->dev, "Failed to add onkey subdev\n");
+}
+
+static void __devinit device_codec_init(struct pm860x_chip *chip,
+					struct pm860x_platform_data *pdata)
+{
+	int ret;
+
+	codec_devs[0].num_resources = ARRAY_SIZE(codec_resources);
+	codec_devs[0].resources = &codec_resources[0],
+	ret = mfd_add_devices(chip->dev, 0, &codec_devs[0],
+			      ARRAY_SIZE(codec_devs), &codec_resources[0], 0);
+	if (ret < 0)
+		dev_err(chip->dev, "Failed to add codec subdev\n");
+}
+
+static void __devinit device_8607_init(struct pm860x_chip *chip,
+				       struct i2c_client *i2c,
+				       struct pm860x_platform_data *pdata)
+{
+	int data, ret;
+
+	ret = pm860x_reg_read(i2c, PM8607_CHIP_ID);
+	if (ret < 0) {
+		dev_err(chip->dev, "Failed to read CHIP ID: %d\n", ret);
+		goto out;
+	}
+	switch (ret & PM8607_VERSION_MASK) {
+	case 0x40:
+	case 0x50:
+		dev_info(chip->dev, "Marvell 88PM8607 (ID: %02x) detected\n",
+			 ret);
+		break;
+	default:
+		dev_err(chip->dev, "Failed to detect Marvell 88PM8607. "
+			"Chip ID: %02x\n", ret);
+		goto out;
+	}
+
+	ret = pm860x_reg_read(i2c, PM8607_BUCK3);
+	if (ret < 0) {
+		dev_err(chip->dev, "Failed to read BUCK3 register: %d\n", ret);
+		goto out;
+	}
+	if (ret & PM8607_BUCK3_DOUBLE)
+		chip->buck3_double = 1;
+
+	ret = pm860x_reg_read(i2c, PM8607_B0_MISC1);
+	if (ret < 0) {
+		dev_err(chip->dev, "Failed to read MISC1 register: %d\n", ret);
+		goto out;
+	}
+
+	if (pdata && (pdata->i2c_port == PI2C_PORT))
+		data = PM8607_B0_MISC1_PI2C;
+	else
+		data = 0;
+	ret = pm860x_set_bits(i2c, PM8607_B0_MISC1, PM8607_B0_MISC1_PI2C, data);
+	if (ret < 0) {
+		dev_err(chip->dev, "Failed to access MISC1:%d\n", ret);
+		goto out;
+	}
+
+	ret = device_gpadc_init(chip, pdata);
+	if (ret < 0)
+		goto out;
+
+	ret = device_irq_init(chip, pdata);
+	if (ret < 0)
+		goto out;
+
+	device_regulator_init(chip, pdata);
+	device_rtc_init(chip, pdata);
+	device_onkey_init(chip, pdata);
+	device_touch_init(chip, pdata);
+	device_power_init(chip, pdata);
+	device_codec_init(chip, pdata);
+out:
+	return;
+}
+
+static void __devinit device_8606_init(struct pm860x_chip *chip,
+				       struct i2c_client *i2c,
+				       struct pm860x_platform_data *pdata)
+{
+	device_osc_init(i2c);
+	device_bk_init(chip, pdata);
+	device_led_init(chip, pdata);
+}
+
+int __devinit pm860x_device_init(struct pm860x_chip *chip,
+		       struct pm860x_platform_data *pdata)
+{
+	chip->core_irq = 0;
+
+	switch (chip->id) {
+	case CHIP_PM8606:
+		device_8606_init(chip, chip->client, pdata);
+		break;
+	case CHIP_PM8607:
+		device_8607_init(chip, chip->client, pdata);
+		break;
+	}
+
+	if (chip->companion) {
+		switch (chip->id) {
+		case CHIP_PM8607:
+			device_8606_init(chip, chip->companion, pdata);
+			break;
+		case CHIP_PM8606:
+			device_8607_init(chip, chip->companion, pdata);
+			break;
+		}
+	}
+
+	return 0;
+}
+
+void __devexit pm860x_device_exit(struct pm860x_chip *chip)
+{
+	device_irq_exit(chip);
+	mfd_remove_devices(chip->dev);
+}
+
+MODULE_DESCRIPTION("PMIC Driver for Marvell 88PM860x");
+MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/88pm860x-i2c.c b/ap/os/linux/linux-3.4.x/drivers/mfd/88pm860x-i2c.c
new file mode 100644
index 0000000..b2cfdc4
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/88pm860x-i2c.c
@@ -0,0 +1,390 @@
+/*
+ * I2C driver for Marvell 88PM860x
+ *
+ * Copyright (C) 2009 Marvell International Ltd.
+ * 	Haojian Zhuang <haojian.zhuang@marvell.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/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/regmap.h>
+#include <linux/mfd/88pm860x.h>
+#include <linux/slab.h>
+
+int pm860x_reg_read(struct i2c_client *i2c, int reg)
+{
+	struct pm860x_chip *chip = i2c_get_clientdata(i2c);
+	struct regmap *map = (i2c == chip->client) ? chip->regmap
+				: chip->regmap_companion;
+	unsigned int data;
+	int ret;
+
+	ret = regmap_read(map, reg, &data);
+	if (ret < 0)
+		return ret;
+	else
+		return (int)data;
+}
+EXPORT_SYMBOL(pm860x_reg_read);
+
+int pm860x_reg_write(struct i2c_client *i2c, int reg,
+		     unsigned char data)
+{
+	struct pm860x_chip *chip = i2c_get_clientdata(i2c);
+	struct regmap *map = (i2c == chip->client) ? chip->regmap
+				: chip->regmap_companion;
+	int ret;
+
+	ret = regmap_write(map, reg, data);
+	return ret;
+}
+EXPORT_SYMBOL(pm860x_reg_write);
+
+int pm860x_bulk_read(struct i2c_client *i2c, int reg,
+		     int count, unsigned char *buf)
+{
+	struct pm860x_chip *chip = i2c_get_clientdata(i2c);
+	struct regmap *map = (i2c == chip->client) ? chip->regmap
+				: chip->regmap_companion;
+	int ret;
+
+	ret = regmap_raw_read(map, reg, buf, count);
+	return ret;
+}
+EXPORT_SYMBOL(pm860x_bulk_read);
+
+int pm860x_bulk_write(struct i2c_client *i2c, int reg,
+		      int count, unsigned char *buf)
+{
+	struct pm860x_chip *chip = i2c_get_clientdata(i2c);
+	struct regmap *map = (i2c == chip->client) ? chip->regmap
+				: chip->regmap_companion;
+	int ret;
+
+	ret = regmap_raw_write(map, reg, buf, count);
+	return ret;
+}
+EXPORT_SYMBOL(pm860x_bulk_write);
+
+int pm860x_set_bits(struct i2c_client *i2c, int reg,
+		    unsigned char mask, unsigned char data)
+{
+	struct pm860x_chip *chip = i2c_get_clientdata(i2c);
+	struct regmap *map = (i2c == chip->client) ? chip->regmap
+				: chip->regmap_companion;
+	int ret;
+
+	ret = regmap_update_bits(map, reg, mask, data);
+	return ret;
+}
+EXPORT_SYMBOL(pm860x_set_bits);
+
+static int read_device(struct i2c_client *i2c, int reg,
+		       int bytes, void *dest)
+{
+	unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX + 3];
+	unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX + 2];
+	struct i2c_adapter *adap = i2c->adapter;
+	struct i2c_msg msg[2] = {{i2c->addr, 0, 1, msgbuf0},
+				 {i2c->addr, I2C_M_RD, 0, msgbuf1},
+				};
+	int num = 1, ret = 0;
+
+	if (dest == NULL)
+		return -EINVAL;
+	msgbuf0[0] = (unsigned char)reg;	/* command */
+	msg[1].len = bytes;
+
+	/* if data needs to read back, num should be 2 */
+	if (bytes > 0)
+		num = 2;
+	ret = adap->algo->master_xfer(adap, msg, num);
+	memcpy(dest, msgbuf1, bytes);
+	if (ret < 0)
+		return ret;
+	return 0;
+}
+
+static int write_device(struct i2c_client *i2c, int reg,
+			int bytes, void *src)
+{
+	unsigned char buf[bytes + 1];
+	struct i2c_adapter *adap = i2c->adapter;
+	struct i2c_msg msg;
+	int ret;
+
+	buf[0] = (unsigned char)reg;
+	memcpy(&buf[1], src, bytes);
+	msg.addr = i2c->addr;
+	msg.flags = 0;
+	msg.len = bytes + 1;
+	msg.buf = buf;
+
+	ret = adap->algo->master_xfer(adap, &msg, 1);
+	if (ret < 0)
+		return ret;
+	return 0;
+}
+
+int pm860x_page_reg_read(struct i2c_client *i2c, int reg)
+{
+	unsigned char zero = 0;
+	unsigned char data;
+	int ret;
+
+	i2c_lock_adapter(i2c->adapter);
+	read_device(i2c, 0xFA, 0, &zero);
+	read_device(i2c, 0xFB, 0, &zero);
+	read_device(i2c, 0xFF, 0, &zero);
+	ret = read_device(i2c, reg, 1, &data);
+	if (ret >= 0)
+		ret = (int)data;
+	read_device(i2c, 0xFE, 0, &zero);
+	read_device(i2c, 0xFC, 0, &zero);
+	i2c_unlock_adapter(i2c->adapter);
+	return ret;
+}
+EXPORT_SYMBOL(pm860x_page_reg_read);
+
+int pm860x_page_reg_write(struct i2c_client *i2c, int reg,
+			  unsigned char data)
+{
+	unsigned char zero;
+	int ret;
+
+	i2c_lock_adapter(i2c->adapter);
+	read_device(i2c, 0xFA, 0, &zero);
+	read_device(i2c, 0xFB, 0, &zero);
+	read_device(i2c, 0xFF, 0, &zero);
+	ret = write_device(i2c, reg, 1, &data);
+	read_device(i2c, 0xFE, 0, &zero);
+	read_device(i2c, 0xFC, 0, &zero);
+	i2c_unlock_adapter(i2c->adapter);
+	return ret;
+}
+EXPORT_SYMBOL(pm860x_page_reg_write);
+
+int pm860x_page_bulk_read(struct i2c_client *i2c, int reg,
+			  int count, unsigned char *buf)
+{
+	unsigned char zero = 0;
+	int ret;
+
+	i2c_lock_adapter(i2c->adapter);
+	read_device(i2c, 0xfa, 0, &zero);
+	read_device(i2c, 0xfb, 0, &zero);
+	read_device(i2c, 0xff, 0, &zero);
+	ret = read_device(i2c, reg, count, buf);
+	read_device(i2c, 0xFE, 0, &zero);
+	read_device(i2c, 0xFC, 0, &zero);
+	i2c_unlock_adapter(i2c->adapter);
+	return ret;
+}
+EXPORT_SYMBOL(pm860x_page_bulk_read);
+
+int pm860x_page_bulk_write(struct i2c_client *i2c, int reg,
+			   int count, unsigned char *buf)
+{
+	unsigned char zero = 0;
+	int ret;
+
+	i2c_lock_adapter(i2c->adapter);
+	read_device(i2c, 0xFA, 0, &zero);
+	read_device(i2c, 0xFB, 0, &zero);
+	read_device(i2c, 0xFF, 0, &zero);
+	ret = write_device(i2c, reg, count, buf);
+	read_device(i2c, 0xFE, 0, &zero);
+	read_device(i2c, 0xFC, 0, &zero);
+	i2c_unlock_adapter(i2c->adapter);
+	i2c_unlock_adapter(i2c->adapter);
+	return ret;
+}
+EXPORT_SYMBOL(pm860x_page_bulk_write);
+
+int pm860x_page_set_bits(struct i2c_client *i2c, int reg,
+			 unsigned char mask, unsigned char data)
+{
+	unsigned char zero;
+	unsigned char value;
+	int ret;
+
+	i2c_lock_adapter(i2c->adapter);
+	read_device(i2c, 0xFA, 0, &zero);
+	read_device(i2c, 0xFB, 0, &zero);
+	read_device(i2c, 0xFF, 0, &zero);
+	ret = read_device(i2c, reg, 1, &value);
+	if (ret < 0)
+		goto out;
+	value &= ~mask;
+	value |= data;
+	ret = write_device(i2c, reg, 1, &value);
+out:
+	read_device(i2c, 0xFE, 0, &zero);
+	read_device(i2c, 0xFC, 0, &zero);
+	i2c_unlock_adapter(i2c->adapter);
+	return ret;
+}
+EXPORT_SYMBOL(pm860x_page_set_bits);
+
+static const struct i2c_device_id pm860x_id_table[] = {
+	{ "88PM860x", 0 },
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, pm860x_id_table);
+
+static int verify_addr(struct i2c_client *i2c)
+{
+	unsigned short addr_8607[] = {0x30, 0x34};
+	unsigned short addr_8606[] = {0x10, 0x11};
+	int size, i;
+
+	if (i2c == NULL)
+		return 0;
+	size = ARRAY_SIZE(addr_8606);
+	for (i = 0; i < size; i++) {
+		if (i2c->addr == *(addr_8606 + i))
+			return CHIP_PM8606;
+	}
+	size = ARRAY_SIZE(addr_8607);
+	for (i = 0; i < size; i++) {
+		if (i2c->addr == *(addr_8607 + i))
+			return CHIP_PM8607;
+	}
+	return 0;
+}
+
+static struct regmap_config pm860x_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+};
+
+static int __devinit pm860x_probe(struct i2c_client *client,
+				  const struct i2c_device_id *id)
+{
+	struct pm860x_platform_data *pdata = client->dev.platform_data;
+	struct pm860x_chip *chip;
+	int ret;
+
+	if (!pdata) {
+		pr_info("No platform data in %s!\n", __func__);
+		return -EINVAL;
+	}
+
+	chip = kzalloc(sizeof(struct pm860x_chip), GFP_KERNEL);
+	if (chip == NULL)
+		return -ENOMEM;
+
+	chip->id = verify_addr(client);
+	chip->regmap = regmap_init_i2c(client, &pm860x_regmap_config);
+	if (IS_ERR(chip->regmap)) {
+		ret = PTR_ERR(chip->regmap);
+		dev_err(&client->dev, "Failed to allocate register map: %d\n",
+				ret);
+		kfree(chip);
+		return ret;
+	}
+	chip->client = client;
+	i2c_set_clientdata(client, chip);
+	chip->dev = &client->dev;
+	dev_set_drvdata(chip->dev, chip);
+
+	/*
+	 * Both client and companion client shares same platform driver.
+	 * Driver distinguishes them by pdata->companion_addr.
+	 * pdata->companion_addr is only assigned if companion chip exists.
+	 * At the same time, the companion_addr shouldn't equal to client
+	 * address.
+	 */
+	if (pdata->companion_addr && (pdata->companion_addr != client->addr)) {
+		chip->companion_addr = pdata->companion_addr;
+		chip->companion = i2c_new_dummy(chip->client->adapter,
+						chip->companion_addr);
+		chip->regmap_companion = regmap_init_i2c(chip->companion,
+							&pm860x_regmap_config);
+		if (IS_ERR(chip->regmap_companion)) {
+			ret = PTR_ERR(chip->regmap_companion);
+			dev_err(&chip->companion->dev,
+				"Failed to allocate register map: %d\n", ret);
+			return ret;
+		}
+		i2c_set_clientdata(chip->companion, chip);
+	}
+
+	pm860x_device_init(chip, pdata);
+	return 0;
+}
+
+static int __devexit pm860x_remove(struct i2c_client *client)
+{
+	struct pm860x_chip *chip = i2c_get_clientdata(client);
+
+	pm860x_device_exit(chip);
+	if (chip->companion) {
+		regmap_exit(chip->regmap_companion);
+		i2c_unregister_device(chip->companion);
+	}
+	regmap_exit(chip->regmap);
+	kfree(chip);
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int pm860x_suspend(struct device *dev)
+{
+	struct i2c_client *client = container_of(dev, struct i2c_client, dev);
+	struct pm860x_chip *chip = i2c_get_clientdata(client);
+
+	if (device_may_wakeup(dev) && chip->wakeup_flag)
+		enable_irq_wake(chip->core_irq);
+	return 0;
+}
+
+static int pm860x_resume(struct device *dev)
+{
+	struct i2c_client *client = container_of(dev, struct i2c_client, dev);
+	struct pm860x_chip *chip = i2c_get_clientdata(client);
+
+	if (device_may_wakeup(dev) && chip->wakeup_flag)
+		disable_irq_wake(chip->core_irq);
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(pm860x_pm_ops, pm860x_suspend, pm860x_resume);
+
+static struct i2c_driver pm860x_driver = {
+	.driver	= {
+		.name	= "88PM860x",
+		.owner	= THIS_MODULE,
+		.pm     = &pm860x_pm_ops,
+	},
+	.probe		= pm860x_probe,
+	.remove		= __devexit_p(pm860x_remove),
+	.id_table	= pm860x_id_table,
+};
+
+static int __init pm860x_i2c_init(void)
+{
+	int ret;
+	ret = i2c_add_driver(&pm860x_driver);
+	if (ret != 0)
+		pr_err("Failed to register 88PM860x I2C driver: %d\n", ret);
+	return ret;
+}
+subsys_initcall(pm860x_i2c_init);
+
+static void __exit pm860x_i2c_exit(void)
+{
+	i2c_del_driver(&pm860x_driver);
+}
+module_exit(pm860x_i2c_exit);
+
+MODULE_DESCRIPTION("I2C Driver for Marvell 88PM860x");
+MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/Kconfig b/ap/os/linux/linux-3.4.x/drivers/mfd/Kconfig
new file mode 100644
index 0000000..2b32a47
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/Kconfig
@@ -0,0 +1,932 @@
+#
+# Multifunction miscellaneous devices
+#
+
+if HAS_IOMEM
+menu "Multifunction device drivers"
+
+config MFD_CORE
+	tristate
+	default n
+
+config MFD_88PM860X
+	bool "Support Marvell 88PM8606/88PM8607"
+	depends on I2C=y && GENERIC_HARDIRQS
+	select REGMAP_I2C
+	select MFD_CORE
+	help
+	  This supports for Marvell 88PM8606/88PM8607 Power Management IC.
+	  This includes the I2C driver and the core APIs _only_, you have to
+	  select individual components like voltage regulators, RTC and
+	  battery-charger under the corresponding menus.
+
+config MFD_SM501
+	tristate "Support for Silicon Motion SM501"
+	 ---help---
+	  This is the core driver for the Silicon Motion SM501 multimedia
+	  companion chip. This device is a multifunction device which may
+	  provide numerous interfaces including USB host controller, USB gadget,
+	  asynchronous serial ports, audio functions, and a dual display video
+	  interface. The device may be connected by PCI or local bus with
+	  varying functions enabled.
+
+config MFD_SM501_GPIO
+	bool "Export GPIO via GPIO layer"
+	depends on MFD_SM501 && GPIOLIB
+	 ---help---
+	 This option uses the gpio library layer to export the 64 GPIO
+	 lines on the SM501. The platform data is used to supply the
+	 base number for the first GPIO line to register.
+
+config MFD_ASIC3
+	bool "Support for Compaq ASIC3"
+	depends on GENERIC_HARDIRQS && GPIOLIB && ARM
+	select MFD_CORE
+	 ---help---
+	  This driver supports the ASIC3 multifunction chip found on many
+	  PDAs (mainly iPAQ and HTC based ones)
+
+config MFD_DAVINCI_VOICECODEC
+	tristate
+	select MFD_CORE
+
+config MFD_DM355EVM_MSP
+	bool "DaVinci DM355 EVM microcontroller"
+	depends on I2C=y && MACH_DAVINCI_DM355_EVM
+	help
+	  This driver supports the MSP430 microcontroller used on these
+	  boards.  MSP430 firmware manages resets and power sequencing,
+	  inputs from buttons and the IR remote, LEDs, an RTC, and more.
+
+config MFD_TI_SSP
+	tristate "TI Sequencer Serial Port support"
+	depends on ARCH_DAVINCI_TNETV107X
+	select MFD_CORE
+	---help---
+	  Say Y here if you want support for the Sequencer Serial Port
+	  in a Texas Instruments TNETV107X SoC.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ti-ssp.
+
+config HTC_EGPIO
+	bool "HTC EGPIO support"
+	depends on GENERIC_HARDIRQS && GPIOLIB && ARM
+	help
+	    This driver supports the CPLD egpio chip present on
+	    several HTC phones.  It provides basic support for input
+	    pins, output pins, and irqs.
+
+config HTC_PASIC3
+	tristate "HTC PASIC3 LED/DS1WM chip support"
+	select MFD_CORE
+	help
+	  This core driver provides register access for the LED/DS1WM
+	  chips labeled "AIC2" and "AIC3", found on HTC Blueangel and
+	  HTC Magician devices, respectively. Actual functionality is
+	  handled by the leds-pasic3 and ds1wm drivers.
+
+config HTC_I2CPLD
+	bool "HTC I2C PLD chip support"
+	depends on I2C=y && GPIOLIB
+	help
+	  If you say yes here you get support for the supposed CPLD
+	  found on omap850 HTC devices like the HTC Wizard and HTC Herald.
+	  This device provides input and output GPIOs through an I2C
+	  interface to one or more sub-chips.
+
+config UCB1400_CORE
+	tristate "Philips UCB1400 Core driver"
+	depends on AC97_BUS
+	depends on GPIOLIB
+	help
+	  This enables support for the Philips UCB1400 core functions.
+	  The UCB1400 is an AC97 audio codec.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ucb1400_core.
+
+config TPS6105X
+	tristate "TPS61050/61052 Boost Converters"
+	depends on I2C
+	select REGULATOR
+	select MFD_CORE
+	select REGULATOR_FIXED_VOLTAGE
+	help
+	  This option enables a driver for the TP61050/TPS61052
+	  high-power "white LED driver". This boost converter is
+	  sometimes used for other things than white LEDs, and
+	  also contains a GPIO pin.
+
+config TPS65010
+	tristate "TPS6501x Power Management chips"
+	depends on I2C && GPIOLIB
+	default y if MACH_OMAP_H2 || MACH_OMAP_H3 || MACH_OMAP_OSK
+	help
+	  If you say yes here you get support for the TPS6501x series of
+	  Power Management chips.  These include voltage regulators,
+	  lithium ion/polymer battery charging, and other features that
+	  are often used in portable devices like cell phones and cameras.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called tps65010.
+
+config TPS6507X
+	tristate "TPS6507x Power Management / Touch Screen chips"
+	select MFD_CORE
+	depends on I2C
+	help
+	  If you say yes here you get support for the TPS6507x series of
+	  Power Management / Touch Screen chips.  These include voltage
+	  regulators, lithium ion/polymer battery charging, touch screen
+	  and other features that are often used in portable devices.
+	  This driver can also be built as a module.  If so, the module
+	  will be called tps6507x.
+
+config MFD_TPS65217
+	tristate "TPS65217 Power Management / White LED chips"
+	depends on I2C
+	select MFD_CORE
+	select REGMAP_I2C
+	help
+	  If you say yes here you get support for the TPS65217 series of
+	  Power Management / White LED chips.
+	  These include voltage regulators, lithium ion/polymer battery
+	  charger, wled and other features that are often used in portable
+	  devices.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called tps65217.
+
+config MFD_TPS6586X
+	bool "TPS6586x Power Management chips"
+	depends on I2C=y && GPIOLIB && GENERIC_HARDIRQS
+	select MFD_CORE
+	help
+	  If you say yes here you get support for the TPS6586X series of
+	  Power Management chips.
+	  This driver provides common support for accessing the device,
+	  additional drivers must be enabled in order to use the
+	  functionality of the device.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called tps6586x.
+
+config MFD_TPS65910
+	bool "TPS65910 Power Management chip"
+	depends on I2C=y && GPIOLIB
+	select MFD_CORE
+	select GPIO_TPS65910
+	select REGMAP_I2C
+	help
+	  if you say yes here you get support for the TPS65910 series of
+	  Power Management chips.
+
+config MFD_TPS65912
+	bool
+	depends on GPIOLIB
+
+config MFD_TPS65912_I2C
+	bool "TPS65912 Power Management chip with I2C"
+	select MFD_CORE
+	select MFD_TPS65912
+	depends on I2C=y && GPIOLIB
+	help
+	  If you say yes here you get support for the TPS65912 series of
+	  PM chips with I2C interface.
+
+config MFD_TPS65912_SPI
+	bool "TPS65912 Power Management chip with SPI"
+	select MFD_CORE
+	select MFD_TPS65912
+	depends on SPI_MASTER && GPIOLIB
+	help
+	  If you say yes here you get support for the TPS65912 series of
+	  PM chips with SPI interface.
+
+config MFD_ZX234290
+	bool
+	depends on GPIOLIB
+
+config MFD_ZX234290_I2C
+	bool "ZX234290 Power Management chip with I2C"
+	select MFD_CORE
+	select MFD_ZX234290
+	depends on I2C=y && GPIOLIB
+	help
+	  If you say yes here you get support for the ZX234290 series of
+	  PM chips with I2C interface.
+config PMU_GM
+	bool "pmu gm"
+	depends on MFD_ZX234290
+	
+config MENELAUS
+	bool "Texas Instruments TWL92330/Menelaus PM chip"
+	depends on I2C=y && ARCH_OMAP2
+	help
+	  If you say yes here you get support for the Texas Instruments
+	  TWL92330/Menelaus Power Management chip. This include voltage
+	  regulators, Dual slot memory card transceivers, real-time clock
+	  and other features that are often used in portable devices like
+	  cell phones and PDAs.
+
+config TWL4030_CORE
+	bool "Texas Instruments TWL4030/TWL5030/TWL6030/TPS659x0 Support"
+	depends on I2C=y && GENERIC_HARDIRQS
+	select IRQ_DOMAIN
+	help
+	  Say yes here if you have TWL4030 / TWL6030 family chip on your board.
+	  This core driver provides register access and IRQ handling
+	  facilities, and registers devices for the various functions
+	  so that function-specific drivers can bind to them.
+
+	  These multi-function chips are found on many OMAP2 and OMAP3
+	  boards, providing power management, RTC, GPIO, keypad, a
+	  high speed USB OTG transceiver, an audio codec (on most
+	  versions) and many other features.
+
+config TWL4030_MADC
+	tristate "Texas Instruments TWL4030 MADC"
+	depends on TWL4030_CORE
+	help
+	This driver provides support for triton TWL4030-MADC. The
+	driver supports both RT and SW conversion methods.
+
+	This driver can be built as a module. If so it will be
+	named twl4030-madc
+
+config TWL4030_POWER
+	bool "Support power resources on TWL4030 family chips"
+	depends on TWL4030_CORE && ARM
+	help
+	  Say yes here if you want to use the power resources on the
+	  TWL4030 family chips.  Most of these resources are regulators,
+	  which have a separate driver; some are control signals, such
+	  as clock request handshaking.
+
+	  This driver uses board-specific data to initialize the resources
+	  and load scripts controlling which resources are switched off/on
+	  or reset when a sleep, wakeup or warm reset event occurs.
+
+config MFD_TWL4030_AUDIO
+	bool
+	depends on TWL4030_CORE
+	select MFD_CORE
+	default n
+
+config TWL6030_PWM
+	tristate "TWL6030 PWM (Pulse Width Modulator) Support"
+	depends on TWL4030_CORE
+	select HAVE_PWM
+	default n
+	help
+	  Say yes here if you want support for TWL6030 PWM.
+	  This is used to control charging LED brightness.
+
+config TWL6040_CORE
+	bool "Support for TWL6040 audio codec"
+	depends on I2C=y && GENERIC_HARDIRQS
+	select MFD_CORE
+	select REGMAP_I2C
+	default n
+	help
+	  Say yes here if you want support for Texas Instruments TWL6040 audio
+	  codec.
+	  This driver provides common support for accessing the device,
+	  additional drivers must be enabled in order to use the
+	  functionality of the device (audio, vibra).
+
+config MFD_STMPE
+	bool "Support STMicroelectronics STMPE"
+	depends on (I2C=y || SPI_MASTER=y) && GENERIC_HARDIRQS
+	select MFD_CORE
+	help
+	  Support for the STMPE family of I/O Expanders from
+	  STMicroelectronics.
+
+	  Currently supported devices are:
+
+		STMPE811: GPIO, Touchscreen
+		STMPE1601: GPIO, Keypad
+		STMPE2401: GPIO, Keypad
+		STMPE2403: GPIO, Keypad
+
+	  This driver provides common support for accessing the device,
+	  additional drivers must be enabled in order to use the functionality
+	  of the device.  Currently available sub drivers are:
+
+		GPIO: stmpe-gpio
+		Keypad: stmpe-keypad
+		Touchscreen: stmpe-ts
+
+menu "STMPE Interface Drivers"
+depends on MFD_STMPE
+
+config STMPE_I2C
+	bool "STMPE I2C Inteface"
+	depends on I2C=y
+	default y
+	help
+	  This is used to enable I2C interface of STMPE
+
+config STMPE_SPI
+	bool "STMPE SPI Inteface"
+	depends on SPI_MASTER
+	help
+	  This is used to enable SPI interface of STMPE
+endmenu
+
+config MFD_TC3589X
+	bool "Support Toshiba TC35892 and variants"
+	depends on I2C=y && GENERIC_HARDIRQS
+	select MFD_CORE
+	help
+	  Support for the Toshiba TC35892 and variants I/O Expander.
+
+	  This driver provides common support for accessing the device,
+	  additional drivers must be enabled in order to use the
+	  functionality of the device.
+
+config MFD_TMIO
+	bool
+	default n
+
+config MFD_T7L66XB
+	bool "Support Toshiba T7L66XB"
+	depends on ARM && HAVE_CLK
+	select MFD_CORE
+	select MFD_TMIO
+	help
+	  Support for Toshiba Mobile IO Controller T7L66XB
+
+config MFD_TC6387XB
+	bool "Support Toshiba TC6387XB"
+	depends on ARM && HAVE_CLK
+	select MFD_CORE
+	select MFD_TMIO
+	help
+	  Support for Toshiba Mobile IO Controller TC6387XB
+
+config MFD_TC6393XB
+	bool "Support Toshiba TC6393XB"
+	depends on GPIOLIB && ARM && HAVE_CLK
+	select MFD_CORE
+	select MFD_TMIO
+	help
+	  Support for Toshiba Mobile IO Controller TC6393XB
+
+config PMIC_DA903X
+	bool "Dialog Semiconductor DA9030/DA9034 PMIC Support"
+	depends on I2C=y
+	help
+	  Say yes here to support for Dialog Semiconductor DA9030 (a.k.a
+	  ARAVA) and DA9034 (a.k.a MICCO), these are Power Management IC
+	  usually found on PXA processors-based platforms. This includes
+	  the I2C driver and the core APIs _only_, you have to select
+	  individual components like LCD backlight, voltage regulators,
+	  LEDs and battery-charger under the corresponding menus.
+
+config PMIC_DA9052
+	bool
+	select MFD_CORE
+
+config MFD_DA9052_SPI
+	bool "Support Dialog Semiconductor DA9052/53 PMIC variants with SPI"
+	select REGMAP_SPI
+	select REGMAP_IRQ
+	select PMIC_DA9052
+	depends on SPI_MASTER=y
+	help
+	  Support for the Dialog Semiconductor DA9052 PMIC
+	  when controlled using SPI. This driver provides common support
+	  for accessing the device, additional drivers must be enabled in
+	  order to use the functionality of the device.
+
+config MFD_DA9052_I2C
+	bool "Support Dialog Semiconductor DA9052/53 PMIC variants with I2C"
+	select REGMAP_I2C
+	select REGMAP_IRQ
+	select PMIC_DA9052
+	depends on I2C=y
+	help
+	  Support for the Dialog Semiconductor DA9052 PMIC
+	  when controlled using I2C. This driver provides common support
+	  for accessing the device, additional drivers must be enabled in
+	  order to use the functionality of the device.
+
+config PMIC_ADP5520
+	bool "Analog Devices ADP5520/01 MFD PMIC Core Support"
+	depends on I2C=y
+	help
+	  Say yes here to add support for Analog Devices AD5520 and ADP5501,
+	  Multifunction Power Management IC. This includes
+	  the I2C driver and the core APIs _only_, you have to select
+	  individual components like LCD backlight, LEDs, GPIOs and Kepad
+	  under the corresponding menus.
+
+config MFD_MAX8925
+	bool "Maxim Semiconductor MAX8925 PMIC Support"
+	depends on I2C=y && GENERIC_HARDIRQS
+	select MFD_CORE
+	help
+	  Say yes here to support for Maxim Semiconductor MAX8925. This is
+	  a Power Management IC. This driver provies common support for
+	  accessing the device, additional drivers must be enabled in order
+	  to use the functionality of the device.
+
+config MFD_MAX8997
+	bool "Maxim Semiconductor MAX8997/8966 PMIC Support"
+	depends on I2C=y && GENERIC_HARDIRQS
+	select MFD_CORE
+	help
+	  Say yes here to support for Maxim Semiconductor MAX8997/8966.
+	  This is a Power Management IC with RTC, Flash, Fuel Gauge, Haptic,
+	  MUIC controls on chip.
+	  This driver provides common support for accessing the device;
+	  additional drivers must be enabled in order to use the functionality
+	  of the device.
+
+config MFD_MAX8998
+	bool "Maxim Semiconductor MAX8998/National LP3974 PMIC Support"
+	depends on I2C=y && GENERIC_HARDIRQS
+	select MFD_CORE
+	help
+	  Say yes here to support for Maxim Semiconductor MAX8998 and
+	  National Semiconductor LP3974. This is a Power Management IC.
+	  This driver provies common support for accessing the device,
+	  additional drivers must be enabled in order to use the functionality
+	  of the device.
+
+config MFD_S5M_CORE
+	bool "SAMSUNG S5M Series Support"
+	depends on I2C=y && GENERIC_HARDIRQS
+	select MFD_CORE
+	select REGMAP_I2C
+	help
+	 Support for the Samsung Electronics S5M MFD series.
+	 This driver provies common support for accessing the device,
+	 additional drivers must be enabled in order to use the functionality
+	 of the device
+
+config MFD_WM8400
+	tristate "Support Wolfson Microelectronics WM8400"
+	select MFD_CORE
+	depends on I2C
+	select REGMAP_I2C
+	help
+	  Support for the Wolfson Microelecronics WM8400 PMIC and audio
+	  CODEC.  This driver provides common support for accessing
+	  the device, additional drivers must be enabled in order to use
+	  the functionality of the device.
+
+config MFD_WM831X
+	bool
+	depends on GENERIC_HARDIRQS
+
+config MFD_WM831X_I2C
+	bool "Support Wolfson Microelectronics WM831x/2x PMICs with I2C"
+	select MFD_CORE
+	select MFD_WM831X
+	select REGMAP_I2C
+	depends on I2C=y && GENERIC_HARDIRQS
+	help
+	  Support for the Wolfson Microelecronics WM831x and WM832x PMICs
+	  when controlled using I2C.  This driver provides common support
+	  for accessing the device, additional drivers must be enabled in
+	  order to use the functionality of the device.
+
+config MFD_WM831X_SPI
+	bool "Support Wolfson Microelectronics WM831x/2x PMICs with SPI"
+	select MFD_CORE
+	select MFD_WM831X
+	select REGMAP_SPI
+	depends on SPI_MASTER && GENERIC_HARDIRQS
+	help
+	  Support for the Wolfson Microelecronics WM831x and WM832x PMICs
+	  when controlled using SPI.  This driver provides common support
+	  for accessing the device, additional drivers must be enabled in
+	  order to use the functionality of the device.
+
+config MFD_WM8350
+	bool
+	depends on GENERIC_HARDIRQS
+
+config MFD_WM8350_CONFIG_MODE_0
+	bool
+	depends on MFD_WM8350
+
+config MFD_WM8350_CONFIG_MODE_1
+	bool
+	depends on MFD_WM8350
+
+config MFD_WM8350_CONFIG_MODE_2
+	bool
+	depends on MFD_WM8350
+
+config MFD_WM8350_CONFIG_MODE_3
+	bool
+	depends on MFD_WM8350
+
+config MFD_WM8351_CONFIG_MODE_0
+	bool
+	depends on MFD_WM8350
+
+config MFD_WM8351_CONFIG_MODE_1
+	bool
+	depends on MFD_WM8350
+
+config MFD_WM8351_CONFIG_MODE_2
+	bool
+	depends on MFD_WM8350
+
+config MFD_WM8351_CONFIG_MODE_3
+	bool
+	depends on MFD_WM8350
+
+config MFD_WM8352_CONFIG_MODE_0
+	bool
+	depends on MFD_WM8350
+
+config MFD_WM8352_CONFIG_MODE_1
+	bool
+	depends on MFD_WM8350
+
+config MFD_WM8352_CONFIG_MODE_2
+	bool
+	depends on MFD_WM8350
+
+config MFD_WM8352_CONFIG_MODE_3
+	bool
+	depends on MFD_WM8350
+
+config MFD_WM8350_I2C
+	bool "Support Wolfson Microelectronics WM8350 with I2C"
+	select MFD_WM8350
+	depends on I2C=y && GENERIC_HARDIRQS
+	help
+	  The WM8350 is an integrated audio and power management
+	  subsystem with watchdog and RTC functionality for embedded
+	  systems.  This option enables core support for the WM8350 with
+	  I2C as the control interface.  Additional options must be
+	  selected to enable support for the functionality of the chip.
+
+config MFD_WM8994
+	bool "Support Wolfson Microelectronics WM8994"
+	select MFD_CORE
+	select REGMAP_I2C
+	select REGMAP_IRQ
+	depends on I2C=y && GENERIC_HARDIRQS
+	help
+	  The WM8994 is a highly integrated hi-fi CODEC designed for
+	  smartphone applicatiosn.  As well as audio functionality it
+	  has on board GPIO and regulator functionality which is
+	  supported via the relevant subsystems.  This driver provides
+	  core support for the WM8994, in order to use the actual
+	  functionaltiy of the device other drivers must be enabled.
+
+config MFD_PCF50633
+	tristate "Support for NXP PCF50633"
+	depends on I2C
+	select REGMAP_I2C
+	help
+	  Say yes here if you have NXP PCF50633 chip on your board.
+	  This core driver provides register access and IRQ handling
+	  facilities, and registers devices for the various functions
+	  so that function-specific drivers can bind to them.
+
+config PCF50633_ADC
+	tristate "Support for NXP PCF50633 ADC"
+	depends on MFD_PCF50633
+	help
+	 Say yes here if you want to include support for ADC in the
+	 NXP PCF50633 chip.
+
+config PCF50633_GPIO
+	tristate "Support for NXP PCF50633 GPIO"
+	depends on MFD_PCF50633
+	help
+	 Say yes here if you want to include support GPIO for pins on
+	 the PCF50633 chip.
+
+config MFD_MC13783
+	tristate
+
+config MFD_MC13XXX
+	tristate "Support Freescale MC13783 and MC13892"
+	depends on SPI_MASTER
+	select MFD_CORE
+	select MFD_MC13783
+	help
+	  Support for the Freescale (Atlas) PMIC and audio CODECs
+	  MC13783 and MC13892.
+	  This driver provides common support for accessing  the device,
+	  additional drivers must be enabled in order to use the
+	  functionality of the device.
+
+config ABX500_CORE
+	bool "ST-Ericsson ABX500 Mixed Signal Circuit register functions"
+	default y if ARCH_U300 || ARCH_U8500
+	help
+	  Say yes here if you have the ABX500 Mixed Signal IC family
+	  chips. This core driver expose register access functions.
+	  Functionality specific drivers using these functions can
+	  remain unchanged when IC changes. Binding of the functions to
+	  actual register access is done by the IC core driver.
+
+config AB3100_CORE
+	bool "ST-Ericsson AB3100 Mixed Signal Circuit core functions"
+	depends on I2C=y && ABX500_CORE
+	select MFD_CORE
+	default y if ARCH_U300
+	help
+	  Select this to enable the AB3100 Mixed Signal IC core
+	  functionality. This connects to a AB3100 on the I2C bus
+	  and expose a number of symbols needed for dependent devices
+	  to read and write registers and subscribe to events from
+	  this multi-functional IC. This is needed to use other features
+	  of the AB3100 such as battery-backed RTC, charging control,
+	  LEDs, vibrator, system power and temperature, power management
+	  and ALSA sound.
+
+config AB3100_OTP
+	tristate "ST-Ericsson AB3100 OTP functions"
+	depends on AB3100_CORE
+	default y if AB3100_CORE
+	help
+	  Select this to enable the AB3100 Mixed Signal IC OTP (one-time
+	  programmable memory) support. This exposes a sysfs file to read
+	  out OTP values.
+
+config EZX_PCAP
+	bool "PCAP Support"
+	depends on GENERIC_HARDIRQS && SPI_MASTER
+	help
+	  This enables the PCAP ASIC present on EZX Phones. This is
+	  needed for MMC, TouchScreen, Sound, USB, etc..
+
+config AB5500_CORE
+	bool "ST-Ericsson AB5500 Mixed Signal Power Management chip"
+	depends on ABX500_CORE && MFD_DB5500_PRCMU
+	select MFD_CORE
+	help
+	  Select this option to enable access to AB5500 power management
+	  chip. This connects to the db5500 chip via the I2C bus via PRCMU.
+	  This chip embeds various other multimedia funtionalities as well.
+
+config AB5500_DEBUG
+	bool "Enable debug info via debugfs"
+	depends on AB5500_CORE && DEBUG_FS
+	default y if DEBUG_FS
+	help
+	  Select this option if you want debug information from the AB5500
+	  using the debug filesystem, debugfs.
+
+config AB8500_CORE
+	bool "ST-Ericsson AB8500 Mixed Signal Power Management chip"
+	depends on GENERIC_HARDIRQS && ABX500_CORE
+	select MFD_CORE
+	help
+	  Select this option to enable access to AB8500 power management
+	  chip. This connects to U8500 either on the SSP/SPI bus (deprecated
+	  since hardware version v1.0) or the I2C bus via PRCMU. It also adds
+	  the irq_chip parts for handling the Mixed Signal chip events.
+	  This chip embeds various other multimedia funtionalities as well.
+
+config AB8500_I2C_CORE
+	bool "AB8500 register access via PRCMU I2C"
+	depends on AB8500_CORE && MFD_DB8500_PRCMU
+	default y
+	help
+	  This enables register access to the AB8500 chip via PRCMU I2C.
+	  The AB8500 chip can be accessed via SPI or I2C. On DB8500 hardware
+	  the I2C bus is connected to the Power Reset
+	  and Mangagement Unit, PRCMU.
+
+config AB8500_DEBUG
+       bool "Enable debug info via debugfs"
+       depends on AB8500_CORE && DEBUG_FS
+       default y if DEBUG_FS
+       help
+         Select this option if you want debug information using the debug
+         filesystem, debugfs.
+
+config AB8500_GPADC
+	bool "AB8500 GPADC driver"
+	depends on AB8500_CORE && REGULATOR_AB8500
+	default y
+	help
+	  AB8500 GPADC driver used to convert Acc and battery/ac/usb voltage
+
+config MFD_DB8500_PRCMU
+	bool "ST-Ericsson DB8500 Power Reset Control Management Unit"
+	depends on UX500_SOC_DB8500
+	select MFD_CORE
+	help
+	  Select this option to enable support for the DB8500 Power Reset
+	  and Control Management Unit. This is basically an autonomous
+	  system controller running an XP70 microprocessor, which is accessed
+	  through a register map.
+
+config MFD_DB5500_PRCMU
+	bool "ST-Ericsson DB5500 Power Reset Control Management Unit"
+	depends on UX500_SOC_DB5500
+	select MFD_CORE
+	help
+	  Select this option to enable support for the DB5500 Power Reset
+	  and Control Management Unit. This is basically an autonomous
+	  system controller running an XP70 microprocessor, which is accessed
+	  through a register map.
+
+config MFD_CS5535
+	tristate "Support for CS5535 and CS5536 southbridge core functions"
+	select MFD_CORE
+	depends on PCI && X86
+	---help---
+	  This is the core driver for CS5535/CS5536 MFD functions.  This is
+          necessary for using the board's GPIO and MFGPT functionality.
+
+config MFD_TIMBERDALE
+	tristate "Support for the Timberdale FPGA"
+	select MFD_CORE
+	depends on PCI && GPIOLIB
+	---help---
+	This is the core driver for the timberdale FPGA. This device is a
+	multifunction device which exposes numerous platform devices.
+
+	The timberdale FPGA can be found on the Intel Atom development board
+	for in-vehicle infontainment, called Russellville.
+
+config LPC_SCH
+	tristate "Intel SCH LPC"
+	depends on PCI
+	select MFD_CORE
+	help
+	  LPC bridge function of the Intel SCH provides support for
+	  System Management Bus and General Purpose I/O.
+
+config MFD_RDC321X
+	tristate "Support for RDC-R321x southbridge"
+	select MFD_CORE
+	depends on PCI
+	help
+	  Say yes here if you want to have support for the RDC R-321x SoC
+	  southbridge which provides access to GPIOs and Watchdog using the
+	  southbridge PCI device configuration space.
+
+config MFD_JANZ_CMODIO
+	tristate "Support for Janz CMOD-IO PCI MODULbus Carrier Board"
+	select MFD_CORE
+	depends on PCI
+	help
+	  This is the core driver for the Janz CMOD-IO PCI MODULbus
+	  carrier board. This device is a PCI to MODULbus bridge which may
+	  host many different types of MODULbus daughterboards, including
+	  CAN and GPIO controllers.
+
+config MFD_JZ4740_ADC
+	bool "Support for the JZ4740 SoC ADC core"
+	select MFD_CORE
+	select GENERIC_IRQ_CHIP
+	depends on MACH_JZ4740
+	help
+	  Say yes here if you want support for the ADC unit in the JZ4740 SoC.
+	  This driver is necessary for jz4740-battery and jz4740-hwmon driver.
+
+config MFD_VX855
+	tristate "Support for VIA VX855/VX875 integrated south bridge"
+	depends on PCI
+	select MFD_CORE
+	help
+	  Say yes here to enable support for various functions of the
+	  VIA VX855/VX875 south bridge. You will need to enable the vx855_spi
+	  and/or vx855_gpio drivers for this to do anything useful.
+
+config MFD_WL1273_CORE
+	tristate "Support for TI WL1273 FM radio."
+	depends on I2C
+	select MFD_CORE
+	default n
+	help
+	  This is the core driver for the TI WL1273 FM radio. This MFD
+	  driver connects the radio-wl1273 V4L2 module and the wl1273
+	  audio codec.
+
+config MFD_OMAP_USB_HOST
+	bool "Support OMAP USBHS core driver"
+	depends on USB_EHCI_HCD_OMAP || USB_OHCI_HCD_OMAP3
+	default y
+	help
+	  This is the core driver for the OAMP EHCI and OHCI drivers.
+	  This MFD driver does the required setup functionalities for
+	  OMAP USB Host drivers.
+
+config MFD_PM8XXX
+	tristate
+
+config MFD_PM8921_CORE
+	tristate "Qualcomm PM8921 PMIC chip"
+	depends on MSM_SSBI
+	select MFD_CORE
+	select MFD_PM8XXX
+	help
+	  If you say yes to this option, support will be included for the
+	  built-in PM8921 PMIC chip.
+
+	  This is required if your board has a PM8921 and uses its features,
+	  such as: MPPs, GPIOs, regulators, interrupts, and PWM.
+
+	  Say M here if you want to include support for PM8921 chip as a module.
+	  This will build a module called "pm8921-core".
+
+config MFD_PM8XXX_IRQ
+	bool "Support for Qualcomm PM8xxx IRQ features"
+	depends on MFD_PM8XXX
+	default y if MFD_PM8XXX
+	help
+	  This is the IRQ driver for Qualcomm PM 8xxx PMIC chips.
+
+	  This is required to use certain other PM 8xxx features, such as GPIO
+	  and MPP.
+
+config TPS65911_COMPARATOR
+	tristate
+
+config MFD_TPS65090
+	bool "TPS65090 Power Management chips"
+	depends on I2C=y && GENERIC_HARDIRQS
+	select MFD_CORE
+	select REGMAP_I2C
+	help
+	  If you say yes here you get support for the TPS65090 series of
+	  Power Management chips.
+	  This driver provides common support for accessing the device,
+	  additional drivers must be enabled in order to use the
+	  functionality of the device.
+
+config MFD_AAT2870_CORE
+	bool "Support for the AnalogicTech AAT2870"
+	select MFD_CORE
+	depends on I2C=y && GPIOLIB
+	help
+	  If you say yes here you get support for the AAT2870.
+	  This driver provides common support for accessing the device,
+	  additional drivers must be enabled in order to use the
+	  functionality of the device.
+
+config MFD_INTEL_MSIC
+	bool "Support for Intel MSIC"
+	depends on INTEL_SCU_IPC
+	select MFD_CORE
+	help
+	  Select this option to enable access to Intel MSIC (Avatele
+	  Passage) chip. This chip embeds audio, battery, GPIO, etc.
+	  devices used in Intel Medfield platforms.
+
+config MFD_RC5T583
+	bool "Ricoh RC5T583 Power Management system device"
+	depends on I2C=y && GENERIC_HARDIRQS
+	select MFD_CORE
+	select REGMAP_I2C
+	help
+	  Select this option to get support for the RICOH583 Power
+	  Management system device.
+	  This driver provides common support for accessing the device
+	  through i2c interface. The device supports multiple sub-devices
+	  like GPIO, interrupts, RTC, LDO and DCDC regulators, onkey.
+	  Additional drivers must be enabled in order to use the
+	  different functionality of the device.
+
+config MFD_ANATOP
+	bool "Support for Freescale i.MX on-chip ANATOP controller"
+	depends on SOC_IMX6Q
+	help
+	  Select this option to enable Freescale i.MX on-chip ANATOP
+	  MFD controller. This controller embeds regulator and
+	  thermal devices for Freescale i.MX platforms.
+
+endmenu
+endif
+
+menu "Multimedia Capabilities Port drivers"
+	depends on ARCH_SA1100
+
+config MCP
+	tristate
+
+# Interface drivers
+config MCP_SA11X0
+	tristate "Support SA11x0 MCP interface"
+	depends on ARCH_SA1100
+	select MCP
+
+# Chip drivers
+config MCP_UCB1200
+	bool "Support for UCB1200 / UCB1300"
+	depends on MCP_SA11X0
+	select MCP
+
+config MCP_UCB1200_TS
+	tristate "Touchscreen interface support"
+	depends on MCP_UCB1200 && INPUT
+
+endmenu
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/Makefile b/ap/os/linux/linux-3.4.x/drivers/mfd/Makefile
new file mode 100644
index 0000000..3849475
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/Makefile
@@ -0,0 +1,126 @@
+#
+# Makefile for multifunction miscellaneous devices
+#
+
+88pm860x-objs			:= 88pm860x-core.o 88pm860x-i2c.o
+obj-$(CONFIG_MFD_88PM860X)	+= 88pm860x.o
+obj-$(CONFIG_MFD_SM501)		+= sm501.o
+obj-$(CONFIG_MFD_ASIC3)		+= asic3.o tmio_core.o
+
+obj-$(CONFIG_HTC_EGPIO)		+= htc-egpio.o
+obj-$(CONFIG_HTC_PASIC3)	+= htc-pasic3.o
+obj-$(CONFIG_HTC_I2CPLD)	+= htc-i2cpld.o
+
+obj-$(CONFIG_MFD_DAVINCI_VOICECODEC)	+= davinci_voicecodec.o
+obj-$(CONFIG_MFD_DM355EVM_MSP)	+= dm355evm_msp.o
+obj-$(CONFIG_MFD_TI_SSP)	+= ti-ssp.o
+
+obj-$(CONFIG_MFD_STMPE)		+= stmpe.o
+obj-$(CONFIG_STMPE_I2C)		+= stmpe-i2c.o
+obj-$(CONFIG_STMPE_SPI)		+= stmpe-spi.o
+obj-$(CONFIG_MFD_TC3589X)	+= tc3589x.o
+obj-$(CONFIG_MFD_T7L66XB)	+= t7l66xb.o tmio_core.o
+obj-$(CONFIG_MFD_TC6387XB)	+= tc6387xb.o tmio_core.o
+obj-$(CONFIG_MFD_TC6393XB)	+= tc6393xb.o tmio_core.o
+
+obj-$(CONFIG_MFD_WM8400)	+= wm8400-core.o
+wm831x-objs			:= wm831x-core.o wm831x-irq.o wm831x-otp.o
+wm831x-objs			+= wm831x-auxadc.o
+obj-$(CONFIG_MFD_WM831X)	+= wm831x.o
+obj-$(CONFIG_MFD_WM831X_I2C)	+= wm831x-i2c.o
+obj-$(CONFIG_MFD_WM831X_SPI)	+= wm831x-spi.o
+wm8350-objs			:= wm8350-core.o wm8350-regmap.o wm8350-gpio.o
+wm8350-objs			+= wm8350-irq.o
+obj-$(CONFIG_MFD_WM8350)	+= wm8350.o
+obj-$(CONFIG_MFD_WM8350_I2C)	+= wm8350-i2c.o
+obj-$(CONFIG_MFD_WM8994)	+= wm8994-core.o wm8994-irq.o wm8994-regmap.o
+
+obj-$(CONFIG_TPS6105X)		+= tps6105x.o
+obj-$(CONFIG_TPS65010)		+= tps65010.o
+obj-$(CONFIG_TPS6507X)		+= tps6507x.o
+obj-$(CONFIG_MFD_TPS65217)	+= tps65217.o
+obj-$(CONFIG_MFD_TPS65910)	+= tps65910.o tps65910-irq.o
+tps65912-objs                   := tps65912-core.o tps65912-irq.o
+obj-$(CONFIG_MFD_TPS65912)	+= tps65912.o
+obj-$(CONFIG_MFD_TPS65912_I2C)	+= tps65912-i2c.o
+obj-$(CONFIG_MFD_TPS65912_SPI)  += tps65912-spi.o
+obj-$(CONFIG_MENELAUS)		+= menelaus.o
+
+zx234290-objs			:= zx234290-core.o zx234290-irq.o
+obj-$(CONFIG_MFD_ZX234290)	+= zx234290.o
+obj-$(CONFIG_MFD_ZX234290)	+= zx234290-i2c.o
+obj-$(CONFIG_MFD_ZX234290)	+= zx234290-regulator.o
+obj-$(CONFIG_MFD_ZX234290)	+= zx234290-regulator-wrapper.o
+obj-$(CONFIG_MFD_ZX234290)	+= zx234290-adc.o
+
+obj-$(CONFIG_TWL4030_CORE)	+= twl-core.o twl4030-irq.o twl6030-irq.o
+obj-$(CONFIG_TWL4030_MADC)      += twl4030-madc.o
+obj-$(CONFIG_TWL4030_POWER)    += twl4030-power.o
+obj-$(CONFIG_MFD_TWL4030_AUDIO)	+= twl4030-audio.o
+obj-$(CONFIG_TWL6030_PWM)	+= twl6030-pwm.o
+obj-$(CONFIG_TWL6040_CORE)	+= twl6040-core.o twl6040-irq.o
+
+obj-$(CONFIG_MFD_MC13XXX)	+= mc13xxx-core.o
+
+obj-$(CONFIG_MFD_CORE)		+= mfd-core.o
+
+obj-$(CONFIG_EZX_PCAP)		+= ezx-pcap.o
+
+obj-$(CONFIG_MCP)		+= mcp-core.o
+obj-$(CONFIG_MCP_SA11X0)	+= mcp-sa11x0.o
+obj-$(CONFIG_MCP_UCB1200)	+= ucb1x00-core.o
+obj-$(CONFIG_MCP_UCB1200_TS)	+= ucb1x00-ts.o
+
+ifeq ($(CONFIG_SA1100_ASSABET),y)
+obj-$(CONFIG_MCP_UCB1200)	+= ucb1x00-assabet.o
+endif
+obj-$(CONFIG_UCB1400_CORE)	+= ucb1400_core.o
+
+obj-$(CONFIG_PMIC_DA903X)	+= da903x.o
+
+obj-$(CONFIG_PMIC_DA9052)	+= da9052-core.o
+obj-$(CONFIG_MFD_DA9052_SPI)	+= da9052-spi.o
+obj-$(CONFIG_MFD_DA9052_I2C)	+= da9052-i2c.o
+
+max8925-objs			:= max8925-core.o max8925-i2c.o
+obj-$(CONFIG_MFD_MAX8925)	+= max8925.o
+obj-$(CONFIG_MFD_MAX8997)	+= max8997.o max8997-irq.o
+obj-$(CONFIG_MFD_MAX8998)	+= max8998.o max8998-irq.o
+
+pcf50633-objs			:= pcf50633-core.o pcf50633-irq.o
+obj-$(CONFIG_MFD_PCF50633)	+= pcf50633.o
+obj-$(CONFIG_PCF50633_ADC)	+= pcf50633-adc.o
+obj-$(CONFIG_PCF50633_GPIO)	+= pcf50633-gpio.o
+obj-$(CONFIG_ABX500_CORE)	+= abx500-core.o
+obj-$(CONFIG_AB3100_CORE)	+= ab3100-core.o
+obj-$(CONFIG_AB3100_OTP)	+= ab3100-otp.o
+obj-$(CONFIG_AB5500_CORE)	+= ab5500-core.o
+obj-$(CONFIG_AB5500_DEBUG)	+= ab5500-debugfs.o
+obj-$(CONFIG_AB8500_CORE)	+= ab8500-core.o ab8500-sysctrl.o
+obj-$(CONFIG_AB8500_DEBUG)	+= ab8500-debugfs.o
+obj-$(CONFIG_AB8500_GPADC)	+= ab8500-gpadc.o
+obj-$(CONFIG_MFD_DB8500_PRCMU)	+= db8500-prcmu.o
+# ab8500-i2c need to come after db8500-prcmu (which provides the channel)
+obj-$(CONFIG_AB8500_I2C_CORE)	+= ab8500-i2c.o
+obj-$(CONFIG_MFD_DB5500_PRCMU)	+= db5500-prcmu.o
+obj-$(CONFIG_MFD_TIMBERDALE)    += timberdale.o
+obj-$(CONFIG_PMIC_ADP5520)	+= adp5520.o
+obj-$(CONFIG_LPC_SCH)		+= lpc_sch.o
+obj-$(CONFIG_MFD_RDC321X)	+= rdc321x-southbridge.o
+obj-$(CONFIG_MFD_JANZ_CMODIO)	+= janz-cmodio.o
+obj-$(CONFIG_MFD_JZ4740_ADC)	+= jz4740-adc.o
+obj-$(CONFIG_MFD_TPS6586X)	+= tps6586x.o
+obj-$(CONFIG_MFD_VX855)		+= vx855.o
+obj-$(CONFIG_MFD_WL1273_CORE)	+= wl1273-core.o
+obj-$(CONFIG_MFD_CS5535)	+= cs5535-mfd.o
+obj-$(CONFIG_MFD_OMAP_USB_HOST)	+= omap-usb-host.o
+obj-$(CONFIG_MFD_PM8921_CORE) 	+= pm8921-core.o
+obj-$(CONFIG_MFD_PM8XXX_IRQ) 	+= pm8xxx-irq.o
+obj-$(CONFIG_TPS65911_COMPARATOR)	+= tps65911-comparator.o
+obj-$(CONFIG_MFD_TPS65090)	+= tps65090.o
+obj-$(CONFIG_MFD_AAT2870_CORE)	+= aat2870-core.o
+obj-$(CONFIG_MFD_INTEL_MSIC)	+= intel_msic.o
+obj-$(CONFIG_MFD_RC5T583)	+= rc5t583.o rc5t583-irq.o
+obj-$(CONFIG_MFD_S5M_CORE)	+= s5m-core.o s5m-irq.o
+obj-$(CONFIG_MFD_ANATOP)	+= anatop-mfd.o
+obj-$(CONFIG_ZX234290_ADC)	+= zx234290-adc.o
\ No newline at end of file
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/aat2870-core.c b/ap/os/linux/linux-3.4.x/drivers/mfd/aat2870-core.c
new file mode 100644
index 0000000..44a3fdb
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/aat2870-core.c
@@ -0,0 +1,529 @@
+/*
+ * linux/drivers/mfd/aat2870-core.c
+ *
+ * Copyright (c) 2011, NVIDIA Corporation.
+ * Author: Jin Park <jinyoungp@nvidia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/aat2870.h>
+#include <linux/regulator/machine.h>
+
+static struct aat2870_register aat2870_regs[AAT2870_REG_NUM] = {
+	/* readable, writeable, value */
+	{ 0, 1, 0x00 },	/* 0x00 AAT2870_BL_CH_EN */
+	{ 0, 1, 0x16 },	/* 0x01 AAT2870_BLM */
+	{ 0, 1, 0x16 },	/* 0x02 AAT2870_BLS */
+	{ 0, 1, 0x56 },	/* 0x03 AAT2870_BL1 */
+	{ 0, 1, 0x56 },	/* 0x04 AAT2870_BL2 */
+	{ 0, 1, 0x56 },	/* 0x05 AAT2870_BL3 */
+	{ 0, 1, 0x56 },	/* 0x06 AAT2870_BL4 */
+	{ 0, 1, 0x56 },	/* 0x07 AAT2870_BL5 */
+	{ 0, 1, 0x56 },	/* 0x08 AAT2870_BL6 */
+	{ 0, 1, 0x56 },	/* 0x09 AAT2870_BL7 */
+	{ 0, 1, 0x56 },	/* 0x0A AAT2870_BL8 */
+	{ 0, 1, 0x00 },	/* 0x0B AAT2870_FLR */
+	{ 0, 1, 0x03 },	/* 0x0C AAT2870_FM */
+	{ 0, 1, 0x03 },	/* 0x0D AAT2870_FS */
+	{ 0, 1, 0x10 },	/* 0x0E AAT2870_ALS_CFG0 */
+	{ 0, 1, 0x06 },	/* 0x0F AAT2870_ALS_CFG1 */
+	{ 0, 1, 0x00 },	/* 0x10 AAT2870_ALS_CFG2 */
+	{ 1, 0, 0x00 },	/* 0x11 AAT2870_AMB */
+	{ 0, 1, 0x00 },	/* 0x12 AAT2870_ALS0 */
+	{ 0, 1, 0x00 },	/* 0x13 AAT2870_ALS1 */
+	{ 0, 1, 0x00 },	/* 0x14 AAT2870_ALS2 */
+	{ 0, 1, 0x00 },	/* 0x15 AAT2870_ALS3 */
+	{ 0, 1, 0x00 },	/* 0x16 AAT2870_ALS4 */
+	{ 0, 1, 0x00 },	/* 0x17 AAT2870_ALS5 */
+	{ 0, 1, 0x00 },	/* 0x18 AAT2870_ALS6 */
+	{ 0, 1, 0x00 },	/* 0x19 AAT2870_ALS7 */
+	{ 0, 1, 0x00 },	/* 0x1A AAT2870_ALS8 */
+	{ 0, 1, 0x00 },	/* 0x1B AAT2870_ALS9 */
+	{ 0, 1, 0x00 },	/* 0x1C AAT2870_ALSA */
+	{ 0, 1, 0x00 },	/* 0x1D AAT2870_ALSB */
+	{ 0, 1, 0x00 },	/* 0x1E AAT2870_ALSC */
+	{ 0, 1, 0x00 },	/* 0x1F AAT2870_ALSD */
+	{ 0, 1, 0x00 },	/* 0x20 AAT2870_ALSE */
+	{ 0, 1, 0x00 },	/* 0x21 AAT2870_ALSF */
+	{ 0, 1, 0x00 },	/* 0x22 AAT2870_SUB_SET */
+	{ 0, 1, 0x00 },	/* 0x23 AAT2870_SUB_CTRL */
+	{ 0, 1, 0x00 },	/* 0x24 AAT2870_LDO_AB */
+	{ 0, 1, 0x00 },	/* 0x25 AAT2870_LDO_CD */
+	{ 0, 1, 0x00 },	/* 0x26 AAT2870_LDO_EN */
+};
+
+static struct mfd_cell aat2870_devs[] = {
+	{
+		.name = "aat2870-backlight",
+		.id = AAT2870_ID_BL,
+		.pdata_size = sizeof(struct aat2870_bl_platform_data),
+	},
+	{
+		.name = "aat2870-regulator",
+		.id = AAT2870_ID_LDOA,
+		.pdata_size = sizeof(struct regulator_init_data),
+	},
+	{
+		.name = "aat2870-regulator",
+		.id = AAT2870_ID_LDOB,
+		.pdata_size = sizeof(struct regulator_init_data),
+	},
+	{
+		.name = "aat2870-regulator",
+		.id = AAT2870_ID_LDOC,
+		.pdata_size = sizeof(struct regulator_init_data),
+	},
+	{
+		.name = "aat2870-regulator",
+		.id = AAT2870_ID_LDOD,
+		.pdata_size = sizeof(struct regulator_init_data),
+	},
+};
+
+static int __aat2870_read(struct aat2870_data *aat2870, u8 addr, u8 *val)
+{
+	int ret;
+
+	if (addr >= AAT2870_REG_NUM) {
+		dev_err(aat2870->dev, "Invalid address, 0x%02x\n", addr);
+		return -EINVAL;
+	}
+
+	if (!aat2870->reg_cache[addr].readable) {
+		*val = aat2870->reg_cache[addr].value;
+		goto out;
+	}
+
+	ret = i2c_master_send(aat2870->client, &addr, 1);
+	if (ret < 0)
+		return ret;
+	if (ret != 1)
+		return -EIO;
+
+	ret = i2c_master_recv(aat2870->client, val, 1);
+	if (ret < 0)
+		return ret;
+	if (ret != 1)
+		return -EIO;
+
+out:
+	dev_dbg(aat2870->dev, "read: addr=0x%02x, val=0x%02x\n", addr, *val);
+	return 0;
+}
+
+static int __aat2870_write(struct aat2870_data *aat2870, u8 addr, u8 val)
+{
+	u8 msg[2];
+	int ret;
+
+	if (addr >= AAT2870_REG_NUM) {
+		dev_err(aat2870->dev, "Invalid address, 0x%02x\n", addr);
+		return -EINVAL;
+	}
+
+	if (!aat2870->reg_cache[addr].writeable) {
+		dev_err(aat2870->dev, "Address 0x%02x is not writeable\n",
+			addr);
+		return -EINVAL;
+	}
+
+	msg[0] = addr;
+	msg[1] = val;
+	ret = i2c_master_send(aat2870->client, msg, 2);
+	if (ret < 0)
+		return ret;
+	if (ret != 2)
+		return -EIO;
+
+	aat2870->reg_cache[addr].value = val;
+
+	dev_dbg(aat2870->dev, "write: addr=0x%02x, val=0x%02x\n", addr, val);
+	return 0;
+}
+
+static int aat2870_read(struct aat2870_data *aat2870, u8 addr, u8 *val)
+{
+	int ret;
+
+	mutex_lock(&aat2870->io_lock);
+	ret = __aat2870_read(aat2870, addr, val);
+	mutex_unlock(&aat2870->io_lock);
+
+	return ret;
+}
+
+static int aat2870_write(struct aat2870_data *aat2870, u8 addr, u8 val)
+{
+	int ret;
+
+	mutex_lock(&aat2870->io_lock);
+	ret = __aat2870_write(aat2870, addr, val);
+	mutex_unlock(&aat2870->io_lock);
+
+	return ret;
+}
+
+static int aat2870_update(struct aat2870_data *aat2870, u8 addr, u8 mask,
+			  u8 val)
+{
+	int change;
+	u8 old_val, new_val;
+	int ret;
+
+	mutex_lock(&aat2870->io_lock);
+
+	ret = __aat2870_read(aat2870, addr, &old_val);
+	if (ret)
+		goto out_unlock;
+
+	new_val = (old_val & ~mask) | (val & mask);
+	change = old_val != new_val;
+	if (change)
+		ret = __aat2870_write(aat2870, addr, new_val);
+
+out_unlock:
+	mutex_unlock(&aat2870->io_lock);
+
+	return ret;
+}
+
+static inline void aat2870_enable(struct aat2870_data *aat2870)
+{
+	if (aat2870->en_pin >= 0)
+		gpio_set_value(aat2870->en_pin, 1);
+
+	aat2870->is_enable = 1;
+}
+
+static inline void aat2870_disable(struct aat2870_data *aat2870)
+{
+	if (aat2870->en_pin >= 0)
+		gpio_set_value(aat2870->en_pin, 0);
+
+	aat2870->is_enable = 0;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static ssize_t aat2870_dump_reg(struct aat2870_data *aat2870, char *buf)
+{
+	u8 addr, val;
+	ssize_t count = 0;
+	int ret;
+
+	count += sprintf(buf, "aat2870 registers\n");
+	for (addr = 0; addr < AAT2870_REG_NUM; addr++) {
+		count += sprintf(buf + count, "0x%02x: ", addr);
+		if (count >= PAGE_SIZE - 1)
+			break;
+
+		ret = aat2870->read(aat2870, addr, &val);
+		if (ret == 0)
+			count += snprintf(buf + count, PAGE_SIZE - count,
+					  "0x%02x", val);
+		else
+			count += snprintf(buf + count, PAGE_SIZE - count,
+					  "<read fail: %d>", ret);
+
+		if (count >= PAGE_SIZE - 1)
+			break;
+
+		count += snprintf(buf + count, PAGE_SIZE - count, "\n");
+		if (count >= PAGE_SIZE - 1)
+			break;
+	}
+
+	/* Truncate count; min() would cause a warning */
+	if (count >= PAGE_SIZE)
+		count = PAGE_SIZE - 1;
+
+	return count;
+}
+
+static ssize_t aat2870_reg_read_file(struct file *file, char __user *user_buf,
+				     size_t count, loff_t *ppos)
+{
+	struct aat2870_data *aat2870 = file->private_data;
+	char *buf;
+	ssize_t ret;
+
+	buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	ret = aat2870_dump_reg(aat2870, buf);
+	if (ret >= 0)
+		ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+
+	kfree(buf);
+
+	return ret;
+}
+
+static ssize_t aat2870_reg_write_file(struct file *file,
+				      const char __user *user_buf, size_t count,
+				      loff_t *ppos)
+{
+	struct aat2870_data *aat2870 = file->private_data;
+	char buf[32];
+	ssize_t buf_size;
+	char *start = buf;
+	unsigned long addr, val;
+	int ret;
+
+	buf_size = min(count, (sizeof(buf)-1));
+	if (copy_from_user(buf, user_buf, buf_size)) {
+		dev_err(aat2870->dev, "Failed to copy from user\n");
+		return -EFAULT;
+	}
+	buf[buf_size] = 0;
+
+	while (*start == ' ')
+		start++;
+
+	addr = simple_strtoul(start, &start, 16);
+	if (addr >= AAT2870_REG_NUM) {
+		dev_err(aat2870->dev, "Invalid address, 0x%lx\n", addr);
+		return -EINVAL;
+	}
+
+	while (*start == ' ')
+		start++;
+
+	if (strict_strtoul(start, 16, &val))
+		return -EINVAL;
+
+	ret = aat2870->write(aat2870, (u8)addr, (u8)val);
+	if (ret)
+		return ret;
+
+	return buf_size;
+}
+
+static const struct file_operations aat2870_reg_fops = {
+	.open = simple_open,
+	.read = aat2870_reg_read_file,
+	.write = aat2870_reg_write_file,
+};
+
+static void aat2870_init_debugfs(struct aat2870_data *aat2870)
+{
+	aat2870->dentry_root = debugfs_create_dir("aat2870", NULL);
+	if (!aat2870->dentry_root) {
+		dev_warn(aat2870->dev,
+			 "Failed to create debugfs root directory\n");
+		return;
+	}
+
+	aat2870->dentry_reg = debugfs_create_file("regs", 0644,
+						  aat2870->dentry_root,
+						  aat2870, &aat2870_reg_fops);
+	if (!aat2870->dentry_reg)
+		dev_warn(aat2870->dev,
+			 "Failed to create debugfs register file\n");
+}
+
+static void aat2870_uninit_debugfs(struct aat2870_data *aat2870)
+{
+	debugfs_remove_recursive(aat2870->dentry_root);
+}
+#else
+static inline void aat2870_init_debugfs(struct aat2870_data *aat2870)
+{
+}
+
+static inline void aat2870_uninit_debugfs(struct aat2870_data *aat2870)
+{
+}
+#endif /* CONFIG_DEBUG_FS */
+
+static int aat2870_i2c_probe(struct i2c_client *client,
+			     const struct i2c_device_id *id)
+{
+	struct aat2870_platform_data *pdata = client->dev.platform_data;
+	struct aat2870_data *aat2870;
+	int i, j;
+	int ret = 0;
+
+	aat2870 = kzalloc(sizeof(struct aat2870_data), GFP_KERNEL);
+	if (!aat2870) {
+		dev_err(&client->dev,
+			"Failed to allocate memory for aat2870\n");
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	aat2870->dev = &client->dev;
+	dev_set_drvdata(aat2870->dev, aat2870);
+
+	aat2870->client = client;
+	i2c_set_clientdata(client, aat2870);
+
+	aat2870->reg_cache = aat2870_regs;
+
+	if (pdata->en_pin < 0)
+		aat2870->en_pin = -1;
+	else
+		aat2870->en_pin = pdata->en_pin;
+
+	aat2870->init = pdata->init;
+	aat2870->uninit = pdata->uninit;
+	aat2870->read = aat2870_read;
+	aat2870->write = aat2870_write;
+	aat2870->update = aat2870_update;
+
+	mutex_init(&aat2870->io_lock);
+
+	if (aat2870->init)
+		aat2870->init(aat2870);
+
+	if (aat2870->en_pin >= 0) {
+		ret = gpio_request_one(aat2870->en_pin, GPIOF_OUT_INIT_HIGH,
+				       "aat2870-en");
+		if (ret < 0) {
+			dev_err(&client->dev,
+				"Failed to request GPIO %d\n", aat2870->en_pin);
+			goto out_kfree;
+		}
+	}
+
+	aat2870_enable(aat2870);
+
+	for (i = 0; i < pdata->num_subdevs; i++) {
+		for (j = 0; j < ARRAY_SIZE(aat2870_devs); j++) {
+			if ((pdata->subdevs[i].id == aat2870_devs[j].id) &&
+					!strcmp(pdata->subdevs[i].name,
+						aat2870_devs[j].name)) {
+				aat2870_devs[j].platform_data =
+					pdata->subdevs[i].platform_data;
+				break;
+			}
+		}
+	}
+
+	ret = mfd_add_devices(aat2870->dev, 0, aat2870_devs,
+			      ARRAY_SIZE(aat2870_devs), NULL, 0);
+	if (ret != 0) {
+		dev_err(aat2870->dev, "Failed to add subdev: %d\n", ret);
+		goto out_disable;
+	}
+
+	aat2870_init_debugfs(aat2870);
+
+	return 0;
+
+out_disable:
+	aat2870_disable(aat2870);
+	if (aat2870->en_pin >= 0)
+		gpio_free(aat2870->en_pin);
+out_kfree:
+	kfree(aat2870);
+out:
+	return ret;
+}
+
+static int aat2870_i2c_remove(struct i2c_client *client)
+{
+	struct aat2870_data *aat2870 = i2c_get_clientdata(client);
+
+	aat2870_uninit_debugfs(aat2870);
+
+	mfd_remove_devices(aat2870->dev);
+	aat2870_disable(aat2870);
+	if (aat2870->en_pin >= 0)
+		gpio_free(aat2870->en_pin);
+	if (aat2870->uninit)
+		aat2870->uninit(aat2870);
+	kfree(aat2870);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int aat2870_i2c_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct aat2870_data *aat2870 = i2c_get_clientdata(client);
+
+	aat2870_disable(aat2870);
+
+	return 0;
+}
+
+static int aat2870_i2c_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct aat2870_data *aat2870 = i2c_get_clientdata(client);
+	struct aat2870_register *reg = NULL;
+	int i;
+
+	aat2870_enable(aat2870);
+
+	/* restore registers */
+	for (i = 0; i < AAT2870_REG_NUM; i++) {
+		reg = &aat2870->reg_cache[i];
+		if (reg->writeable)
+			aat2870->write(aat2870, i, reg->value);
+	}
+
+	return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static SIMPLE_DEV_PM_OPS(aat2870_pm_ops, aat2870_i2c_suspend,
+			 aat2870_i2c_resume);
+
+static const struct i2c_device_id aat2870_i2c_id_table[] = {
+	{ "aat2870", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, aat2870_i2c_id_table);
+
+static struct i2c_driver aat2870_i2c_driver = {
+	.driver = {
+		.name	= "aat2870",
+		.owner	= THIS_MODULE,
+		.pm	= &aat2870_pm_ops,
+	},
+	.probe		= aat2870_i2c_probe,
+	.remove		= aat2870_i2c_remove,
+	.id_table	= aat2870_i2c_id_table,
+};
+
+static int __init aat2870_init(void)
+{
+	return i2c_add_driver(&aat2870_i2c_driver);
+}
+subsys_initcall(aat2870_init);
+
+static void __exit aat2870_exit(void)
+{
+	i2c_del_driver(&aat2870_i2c_driver);
+}
+module_exit(aat2870_exit);
+
+MODULE_DESCRIPTION("Core support for the AnalogicTech AAT2870");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jin Park <jinyoungp@nvidia.com>");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/ab3100-core.c b/ap/os/linux/linux-3.4.x/drivers/mfd/ab3100-core.c
new file mode 100644
index 0000000..1287645
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/ab3100-core.c
@@ -0,0 +1,1014 @@
+/*
+ * Copyright (C) 2007-2010 ST-Ericsson
+ * License terms: GNU General Public License (GPL) version 2
+ * Low-level core for exclusive access to the AB3100 IC on the I2C bus
+ * and some basic chip-configuration.
+ * Author: Linus Walleij <linus.walleij@stericsson.com>
+ */
+
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/notifier.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/random.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/abx500.h>
+
+/* These are the only registers inside AB3100 used in this main file */
+
+/* Interrupt event registers */
+#define AB3100_EVENTA1		0x21
+#define AB3100_EVENTA2		0x22
+#define AB3100_EVENTA3		0x23
+
+/* AB3100 DAC converter registers */
+#define AB3100_DIS		0x00
+#define AB3100_D0C		0x01
+#define AB3100_D1C		0x02
+#define AB3100_D2C		0x03
+#define AB3100_D3C		0x04
+
+/* Chip ID register */
+#define AB3100_CID		0x20
+
+/* AB3100 interrupt registers */
+#define AB3100_IMRA1		0x24
+#define AB3100_IMRA2		0x25
+#define AB3100_IMRA3		0x26
+#define AB3100_IMRB1		0x2B
+#define AB3100_IMRB2		0x2C
+#define AB3100_IMRB3		0x2D
+
+/* System Power Monitoring and control registers */
+#define AB3100_MCA		0x2E
+#define AB3100_MCB		0x2F
+
+/* SIM power up */
+#define AB3100_SUP		0x50
+
+/*
+ * I2C communication
+ *
+ * The AB3100 is usually assigned address 0x48 (7-bit)
+ * The chip is defined in the platform i2c_board_data section.
+ */
+static int ab3100_get_chip_id(struct device *dev)
+{
+	struct ab3100 *ab3100 = dev_get_drvdata(dev->parent);
+
+	return (int)ab3100->chip_id;
+}
+
+static int ab3100_set_register_interruptible(struct ab3100 *ab3100,
+	u8 reg, u8 regval)
+{
+	u8 regandval[2] = {reg, regval};
+	int err;
+
+	err = mutex_lock_interruptible(&ab3100->access_mutex);
+	if (err)
+		return err;
+
+	/*
+	 * A two-byte write message with the first byte containing the register
+	 * number and the second byte containing the value to be written
+	 * effectively sets a register in the AB3100.
+	 */
+	err = i2c_master_send(ab3100->i2c_client, regandval, 2);
+	if (err < 0) {
+		dev_err(ab3100->dev,
+			"write error (write register): %d\n",
+			err);
+	} else if (err != 2) {
+		dev_err(ab3100->dev,
+			"write error (write register) "
+			"%d bytes transferred (expected 2)\n",
+			err);
+		err = -EIO;
+	} else {
+		/* All is well */
+		err = 0;
+	}
+	mutex_unlock(&ab3100->access_mutex);
+	return err;
+}
+
+static int set_register_interruptible(struct device *dev,
+	u8 bank, u8 reg, u8 value)
+{
+	struct ab3100 *ab3100 = dev_get_drvdata(dev->parent);
+
+	return ab3100_set_register_interruptible(ab3100, reg, value);
+}
+
+/*
+ * The test registers exist at an I2C bus address up one
+ * from the ordinary base. They are not supposed to be used
+ * in production code, but sometimes you have to do that
+ * anyway. It's currently only used from this file so declare
+ * it static and do not export.
+ */
+static int ab3100_set_test_register_interruptible(struct ab3100 *ab3100,
+				    u8 reg, u8 regval)
+{
+	u8 regandval[2] = {reg, regval};
+	int err;
+
+	err = mutex_lock_interruptible(&ab3100->access_mutex);
+	if (err)
+		return err;
+
+	err = i2c_master_send(ab3100->testreg_client, regandval, 2);
+	if (err < 0) {
+		dev_err(ab3100->dev,
+			"write error (write test register): %d\n",
+			err);
+	} else if (err != 2) {
+		dev_err(ab3100->dev,
+			"write error (write test register) "
+			"%d bytes transferred (expected 2)\n",
+			err);
+		err = -EIO;
+	} else {
+		/* All is well */
+		err = 0;
+	}
+	mutex_unlock(&ab3100->access_mutex);
+
+	return err;
+}
+
+static int ab3100_get_register_interruptible(struct ab3100 *ab3100,
+					     u8 reg, u8 *regval)
+{
+	int err;
+
+	err = mutex_lock_interruptible(&ab3100->access_mutex);
+	if (err)
+		return err;
+
+	/*
+	 * AB3100 require an I2C "stop" command between each message, else
+	 * it will not work. The only way of achieveing this with the
+	 * message transport layer is to send the read and write messages
+	 * separately.
+	 */
+	err = i2c_master_send(ab3100->i2c_client, &reg, 1);
+	if (err < 0) {
+		dev_err(ab3100->dev,
+			"write error (send register address): %d\n",
+			err);
+		goto get_reg_out_unlock;
+	} else if (err != 1) {
+		dev_err(ab3100->dev,
+			"write error (send register address) "
+			"%d bytes transferred (expected 1)\n",
+			err);
+		err = -EIO;
+		goto get_reg_out_unlock;
+	} else {
+		/* All is well */
+		err = 0;
+	}
+
+	err = i2c_master_recv(ab3100->i2c_client, regval, 1);
+	if (err < 0) {
+		dev_err(ab3100->dev,
+			"write error (read register): %d\n",
+			err);
+		goto get_reg_out_unlock;
+	} else if (err != 1) {
+		dev_err(ab3100->dev,
+			"write error (read register) "
+			"%d bytes transferred (expected 1)\n",
+			err);
+		err = -EIO;
+		goto get_reg_out_unlock;
+	} else {
+		/* All is well */
+		err = 0;
+	}
+
+ get_reg_out_unlock:
+	mutex_unlock(&ab3100->access_mutex);
+	return err;
+}
+
+static int get_register_interruptible(struct device *dev, u8 bank, u8 reg,
+				      u8 *value)
+{
+	struct ab3100 *ab3100 = dev_get_drvdata(dev->parent);
+
+	return ab3100_get_register_interruptible(ab3100, reg, value);
+}
+
+static int ab3100_get_register_page_interruptible(struct ab3100 *ab3100,
+			     u8 first_reg, u8 *regvals, u8 numregs)
+{
+	int err;
+
+	if (ab3100->chip_id == 0xa0 ||
+	    ab3100->chip_id == 0xa1)
+		/* These don't support paged reads */
+		return -EIO;
+
+	err = mutex_lock_interruptible(&ab3100->access_mutex);
+	if (err)
+		return err;
+
+	/*
+	 * Paged read also require an I2C "stop" command.
+	 */
+	err = i2c_master_send(ab3100->i2c_client, &first_reg, 1);
+	if (err < 0) {
+		dev_err(ab3100->dev,
+			"write error (send first register address): %d\n",
+			err);
+		goto get_reg_page_out_unlock;
+	} else if (err != 1) {
+		dev_err(ab3100->dev,
+			"write error (send first register address) "
+			"%d bytes transferred (expected 1)\n",
+			err);
+		err = -EIO;
+		goto get_reg_page_out_unlock;
+	}
+
+	err = i2c_master_recv(ab3100->i2c_client, regvals, numregs);
+	if (err < 0) {
+		dev_err(ab3100->dev,
+			"write error (read register page): %d\n",
+			err);
+		goto get_reg_page_out_unlock;
+	} else if (err != numregs) {
+		dev_err(ab3100->dev,
+			"write error (read register page) "
+			"%d bytes transferred (expected %d)\n",
+			err, numregs);
+		err = -EIO;
+		goto get_reg_page_out_unlock;
+	}
+
+	/* All is well */
+	err = 0;
+
+ get_reg_page_out_unlock:
+	mutex_unlock(&ab3100->access_mutex);
+	return err;
+}
+
+static int get_register_page_interruptible(struct device *dev, u8 bank,
+	u8 first_reg, u8 *regvals, u8 numregs)
+{
+	struct ab3100 *ab3100 = dev_get_drvdata(dev->parent);
+
+	return ab3100_get_register_page_interruptible(ab3100,
+			first_reg, regvals, numregs);
+}
+
+static int ab3100_mask_and_set_register_interruptible(struct ab3100 *ab3100,
+				 u8 reg, u8 andmask, u8 ormask)
+{
+	u8 regandval[2] = {reg, 0};
+	int err;
+
+	err = mutex_lock_interruptible(&ab3100->access_mutex);
+	if (err)
+		return err;
+
+	/* First read out the target register */
+	err = i2c_master_send(ab3100->i2c_client, &reg, 1);
+	if (err < 0) {
+		dev_err(ab3100->dev,
+			"write error (maskset send address): %d\n",
+			err);
+		goto get_maskset_unlock;
+	} else if (err != 1) {
+		dev_err(ab3100->dev,
+			"write error (maskset send address) "
+			"%d bytes transferred (expected 1)\n",
+			err);
+		err = -EIO;
+		goto get_maskset_unlock;
+	}
+
+	err = i2c_master_recv(ab3100->i2c_client, &regandval[1], 1);
+	if (err < 0) {
+		dev_err(ab3100->dev,
+			"write error (maskset read register): %d\n",
+			err);
+		goto get_maskset_unlock;
+	} else if (err != 1) {
+		dev_err(ab3100->dev,
+			"write error (maskset read register) "
+			"%d bytes transferred (expected 1)\n",
+			err);
+		err = -EIO;
+		goto get_maskset_unlock;
+	}
+
+	/* Modify the register */
+	regandval[1] &= andmask;
+	regandval[1] |= ormask;
+
+	/* Write the register */
+	err = i2c_master_send(ab3100->i2c_client, regandval, 2);
+	if (err < 0) {
+		dev_err(ab3100->dev,
+			"write error (write register): %d\n",
+			err);
+		goto get_maskset_unlock;
+	} else if (err != 2) {
+		dev_err(ab3100->dev,
+			"write error (write register) "
+			"%d bytes transferred (expected 2)\n",
+			err);
+		err = -EIO;
+		goto get_maskset_unlock;
+	}
+
+	/* All is well */
+	err = 0;
+
+ get_maskset_unlock:
+	mutex_unlock(&ab3100->access_mutex);
+	return err;
+}
+
+static int mask_and_set_register_interruptible(struct device *dev, u8 bank,
+	u8 reg, u8 bitmask, u8 bitvalues)
+{
+	struct ab3100 *ab3100 = dev_get_drvdata(dev->parent);
+
+	return ab3100_mask_and_set_register_interruptible(ab3100,
+			reg, bitmask, (bitmask & bitvalues));
+}
+
+/*
+ * Register a simple callback for handling any AB3100 events.
+ */
+int ab3100_event_register(struct ab3100 *ab3100,
+			  struct notifier_block *nb)
+{
+	return blocking_notifier_chain_register(&ab3100->event_subscribers,
+					       nb);
+}
+EXPORT_SYMBOL(ab3100_event_register);
+
+/*
+ * Remove a previously registered callback.
+ */
+int ab3100_event_unregister(struct ab3100 *ab3100,
+			    struct notifier_block *nb)
+{
+  return blocking_notifier_chain_unregister(&ab3100->event_subscribers,
+					    nb);
+}
+EXPORT_SYMBOL(ab3100_event_unregister);
+
+
+static int ab3100_event_registers_startup_state_get(struct device *dev,
+					     u8 *event)
+{
+	struct ab3100 *ab3100 = dev_get_drvdata(dev->parent);
+	if (!ab3100->startup_events_read)
+		return -EAGAIN; /* Try again later */
+	memcpy(event, ab3100->startup_events, 3);
+	return 0;
+}
+
+static struct abx500_ops ab3100_ops = {
+	.get_chip_id = ab3100_get_chip_id,
+	.set_register = set_register_interruptible,
+	.get_register = get_register_interruptible,
+	.get_register_page = get_register_page_interruptible,
+	.set_register_page = NULL,
+	.mask_and_set_register = mask_and_set_register_interruptible,
+	.event_registers_startup_state_get =
+		ab3100_event_registers_startup_state_get,
+	.startup_irq_enabled = NULL,
+};
+
+/*
+ * This is a threaded interrupt handler so we can make some
+ * I2C calls etc.
+ */
+static irqreturn_t ab3100_irq_handler(int irq, void *data)
+{
+	struct ab3100 *ab3100 = data;
+	u8 event_regs[3];
+	u32 fatevent;
+	int err;
+
+	err = ab3100_get_register_page_interruptible(ab3100, AB3100_EVENTA1,
+				       event_regs, 3);
+	if (err)
+		goto err_event;
+
+	fatevent = (event_regs[0] << 16) |
+		(event_regs[1] << 8) |
+		event_regs[2];
+
+	if (!ab3100->startup_events_read) {
+		ab3100->startup_events[0] = event_regs[0];
+		ab3100->startup_events[1] = event_regs[1];
+		ab3100->startup_events[2] = event_regs[2];
+		ab3100->startup_events_read = true;
+	}
+	/*
+	 * The notified parties will have to mask out the events
+	 * they're interested in and react to them. They will be
+	 * notified on all events, then they use the fatevent value
+	 * to determine if they're interested.
+	 */
+	blocking_notifier_call_chain(&ab3100->event_subscribers,
+				     fatevent, NULL);
+
+	dev_dbg(ab3100->dev,
+		"IRQ Event: 0x%08x\n", fatevent);
+
+	return IRQ_HANDLED;
+
+ err_event:
+	dev_dbg(ab3100->dev,
+		"error reading event status\n");
+	return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_DEBUG_FS
+/*
+ * Some debugfs entries only exposed if we're using debug
+ */
+static int ab3100_registers_print(struct seq_file *s, void *p)
+{
+	struct ab3100 *ab3100 = s->private;
+	u8 value;
+	u8 reg;
+
+	seq_printf(s, "AB3100 registers:\n");
+
+	for (reg = 0; reg < 0xff; reg++) {
+		ab3100_get_register_interruptible(ab3100, reg, &value);
+		seq_printf(s, "[0x%x]:  0x%x\n", reg, value);
+	}
+	return 0;
+}
+
+static int ab3100_registers_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, ab3100_registers_print, inode->i_private);
+}
+
+static const struct file_operations ab3100_registers_fops = {
+	.open = ab3100_registers_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+	.owner = THIS_MODULE,
+};
+
+struct ab3100_get_set_reg_priv {
+	struct ab3100 *ab3100;
+	bool mode;
+};
+
+static ssize_t ab3100_get_set_reg(struct file *file,
+				  const char __user *user_buf,
+				  size_t count, loff_t *ppos)
+{
+	struct ab3100_get_set_reg_priv *priv = file->private_data;
+	struct ab3100 *ab3100 = priv->ab3100;
+	char buf[32];
+	ssize_t buf_size;
+	int regp;
+	unsigned long user_reg;
+	int err;
+	int i = 0;
+
+	/* Get userspace string and assure termination */
+	buf_size = min(count, (sizeof(buf)-1));
+	if (copy_from_user(buf, user_buf, buf_size))
+		return -EFAULT;
+	buf[buf_size] = 0;
+
+	/*
+	 * The idea is here to parse a string which is either
+	 * "0xnn" for reading a register, or "0xaa 0xbb" for
+	 * writing 0xbb to the register 0xaa. First move past
+	 * whitespace and then begin to parse the register.
+	 */
+	while ((i < buf_size) && (buf[i] == ' '))
+		i++;
+	regp = i;
+
+	/*
+	 * Advance pointer to end of string then terminate
+	 * the register string. This is needed to satisfy
+	 * the strict_strtoul() function.
+	 */
+	while ((i < buf_size) && (buf[i] != ' '))
+		i++;
+	buf[i] = '\0';
+
+	err = strict_strtoul(&buf[regp], 16, &user_reg);
+	if (err)
+		return err;
+	if (user_reg > 0xff)
+		return -EINVAL;
+
+	/* Either we read or we write a register here */
+	if (!priv->mode) {
+		/* Reading */
+		u8 reg = (u8) user_reg;
+		u8 regvalue;
+
+		ab3100_get_register_interruptible(ab3100, reg, &regvalue);
+
+		dev_info(ab3100->dev,
+			 "debug read AB3100 reg[0x%02x]: 0x%02x\n",
+			 reg, regvalue);
+	} else {
+		int valp;
+		unsigned long user_value;
+		u8 reg = (u8) user_reg;
+		u8 value;
+		u8 regvalue;
+
+		/*
+		 * Writing, we need some value to write to
+		 * the register so keep parsing the string
+		 * from userspace.
+		 */
+		i++;
+		while ((i < buf_size) && (buf[i] == ' '))
+			i++;
+		valp = i;
+		while ((i < buf_size) && (buf[i] != ' '))
+			i++;
+		buf[i] = '\0';
+
+		err = strict_strtoul(&buf[valp], 16, &user_value);
+		if (err)
+			return err;
+		if (user_reg > 0xff)
+			return -EINVAL;
+
+		value = (u8) user_value;
+		ab3100_set_register_interruptible(ab3100, reg, value);
+		ab3100_get_register_interruptible(ab3100, reg, &regvalue);
+
+		dev_info(ab3100->dev,
+			 "debug write reg[0x%02x] with 0x%02x, "
+			 "after readback: 0x%02x\n",
+			 reg, value, regvalue);
+	}
+	return buf_size;
+}
+
+static const struct file_operations ab3100_get_set_reg_fops = {
+	.open = simple_open,
+	.write = ab3100_get_set_reg,
+	.llseek = noop_llseek,
+};
+
+static struct dentry *ab3100_dir;
+static struct dentry *ab3100_reg_file;
+static struct ab3100_get_set_reg_priv ab3100_get_priv;
+static struct dentry *ab3100_get_reg_file;
+static struct ab3100_get_set_reg_priv ab3100_set_priv;
+static struct dentry *ab3100_set_reg_file;
+
+static void ab3100_setup_debugfs(struct ab3100 *ab3100)
+{
+	int err;
+
+	ab3100_dir = debugfs_create_dir("ab3100", NULL);
+	if (!ab3100_dir)
+		goto exit_no_debugfs;
+
+	ab3100_reg_file = debugfs_create_file("registers",
+				S_IRUGO, ab3100_dir, ab3100,
+				&ab3100_registers_fops);
+	if (!ab3100_reg_file) {
+		err = -ENOMEM;
+		goto exit_destroy_dir;
+	}
+
+	ab3100_get_priv.ab3100 = ab3100;
+	ab3100_get_priv.mode = false;
+	ab3100_get_reg_file = debugfs_create_file("get_reg",
+				S_IWUSR, ab3100_dir, &ab3100_get_priv,
+				&ab3100_get_set_reg_fops);
+	if (!ab3100_get_reg_file) {
+		err = -ENOMEM;
+		goto exit_destroy_reg;
+	}
+
+	ab3100_set_priv.ab3100 = ab3100;
+	ab3100_set_priv.mode = true;
+	ab3100_set_reg_file = debugfs_create_file("set_reg",
+				S_IWUSR, ab3100_dir, &ab3100_set_priv,
+				&ab3100_get_set_reg_fops);
+	if (!ab3100_set_reg_file) {
+		err = -ENOMEM;
+		goto exit_destroy_get_reg;
+	}
+	return;
+
+ exit_destroy_get_reg:
+	debugfs_remove(ab3100_get_reg_file);
+ exit_destroy_reg:
+	debugfs_remove(ab3100_reg_file);
+ exit_destroy_dir:
+	debugfs_remove(ab3100_dir);
+ exit_no_debugfs:
+	return;
+}
+static inline void ab3100_remove_debugfs(void)
+{
+	debugfs_remove(ab3100_set_reg_file);
+	debugfs_remove(ab3100_get_reg_file);
+	debugfs_remove(ab3100_reg_file);
+	debugfs_remove(ab3100_dir);
+}
+#else
+static inline void ab3100_setup_debugfs(struct ab3100 *ab3100)
+{
+}
+static inline void ab3100_remove_debugfs(void)
+{
+}
+#endif
+
+/*
+ * Basic set-up, datastructure creation/destruction and I2C interface.
+ * This sets up a default config in the AB3100 chip so that it
+ * will work as expected.
+ */
+
+struct ab3100_init_setting {
+	u8 abreg;
+	u8 setting;
+};
+
+static const struct ab3100_init_setting __devinitconst
+ab3100_init_settings[] = {
+	{
+		.abreg = AB3100_MCA,
+		.setting = 0x01
+	}, {
+		.abreg = AB3100_MCB,
+		.setting = 0x30
+	}, {
+		.abreg = AB3100_IMRA1,
+		.setting = 0x00
+	}, {
+		.abreg = AB3100_IMRA2,
+		.setting = 0xFF
+	}, {
+		.abreg = AB3100_IMRA3,
+		.setting = 0x01
+	}, {
+		.abreg = AB3100_IMRB1,
+		.setting = 0xBF
+	}, {
+		.abreg = AB3100_IMRB2,
+		.setting = 0xFF
+	}, {
+		.abreg = AB3100_IMRB3,
+		.setting = 0xFF
+	}, {
+		.abreg = AB3100_SUP,
+		.setting = 0x00
+	}, {
+		.abreg = AB3100_DIS,
+		.setting = 0xF0
+	}, {
+		.abreg = AB3100_D0C,
+		.setting = 0x00
+	}, {
+		.abreg = AB3100_D1C,
+		.setting = 0x00
+	}, {
+		.abreg = AB3100_D2C,
+		.setting = 0x00
+	}, {
+		.abreg = AB3100_D3C,
+		.setting = 0x00
+	},
+};
+
+static int __devinit ab3100_setup(struct ab3100 *ab3100)
+{
+	int err = 0;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(ab3100_init_settings); i++) {
+		err = ab3100_set_register_interruptible(ab3100,
+					  ab3100_init_settings[i].abreg,
+					  ab3100_init_settings[i].setting);
+		if (err)
+			goto exit_no_setup;
+	}
+
+	/*
+	 * Special trick to make the AB3100 use the 32kHz clock (RTC)
+	 * bit 3 in test register 0x02 is a special, undocumented test
+	 * register bit that only exist in AB3100 P1E
+	 */
+	if (ab3100->chip_id == 0xc4) {
+		dev_warn(ab3100->dev,
+			 "AB3100 P1E variant detected, "
+			 "forcing chip to 32KHz\n");
+		err = ab3100_set_test_register_interruptible(ab3100,
+			0x02, 0x08);
+	}
+
+ exit_no_setup:
+	return err;
+}
+
+/* The subdevices of the AB3100 */
+static struct mfd_cell ab3100_devs[] = {
+	{
+		.name = "ab3100-dac",
+		.id = -1,
+	},
+	{
+		.name = "ab3100-leds",
+		.id = -1,
+	},
+	{
+		.name = "ab3100-power",
+		.id = -1,
+	},
+	{
+		.name = "ab3100-regulators",
+		.id = -1,
+	},
+	{
+		.name = "ab3100-sim",
+		.id = -1,
+	},
+	{
+		.name = "ab3100-uart",
+		.id = -1,
+	},
+	{
+		.name = "ab3100-rtc",
+		.id = -1,
+	},
+	{
+		.name = "ab3100-charger",
+		.id = -1,
+	},
+	{
+		.name = "ab3100-boost",
+		.id = -1,
+	},
+	{
+		.name = "ab3100-adc",
+		.id = -1,
+	},
+	{
+		.name = "ab3100-fuelgauge",
+		.id = -1,
+	},
+	{
+		.name = "ab3100-vibrator",
+		.id = -1,
+	},
+	{
+		.name = "ab3100-otp",
+		.id = -1,
+	},
+	{
+		.name = "ab3100-codec",
+		.id = -1,
+	},
+};
+
+struct ab_family_id {
+	u8	id;
+	char	*name;
+};
+
+static const struct ab_family_id ids[] __devinitconst = {
+	/* AB3100 */
+	{
+		.id = 0xc0,
+		.name = "P1A"
+	}, {
+		.id = 0xc1,
+		.name = "P1B"
+	}, {
+		.id = 0xc2,
+		.name = "P1C"
+	}, {
+		.id = 0xc3,
+		.name = "P1D"
+	}, {
+		.id = 0xc4,
+		.name = "P1E"
+	}, {
+		.id = 0xc5,
+		.name = "P1F/R1A"
+	}, {
+		.id = 0xc6,
+		.name = "P1G/R1A"
+	}, {
+		.id = 0xc7,
+		.name = "P2A/R2A"
+	}, {
+		.id = 0xc8,
+		.name = "P2B/R2B"
+	},
+	/* AB3000 variants, not supported */
+	{
+		.id = 0xa0
+	}, {
+		.id = 0xa1
+	}, {
+		.id = 0xa2
+	}, {
+		.id = 0xa3
+	}, {
+		.id = 0xa4
+	}, {
+		.id = 0xa5
+	}, {
+		.id = 0xa6
+	}, {
+		.id = 0xa7
+	},
+	/* Terminator */
+	{
+		.id = 0x00,
+	},
+};
+
+static int __devinit ab3100_probe(struct i2c_client *client,
+				  const struct i2c_device_id *id)
+{
+	struct ab3100 *ab3100;
+	struct ab3100_platform_data *ab3100_plf_data =
+		client->dev.platform_data;
+	int err;
+	int i;
+
+	ab3100 = kzalloc(sizeof(struct ab3100), GFP_KERNEL);
+	if (!ab3100) {
+		dev_err(&client->dev, "could not allocate AB3100 device\n");
+		return -ENOMEM;
+	}
+
+	/* Initialize data structure */
+	mutex_init(&ab3100->access_mutex);
+	BLOCKING_INIT_NOTIFIER_HEAD(&ab3100->event_subscribers);
+
+	ab3100->i2c_client = client;
+	ab3100->dev = &ab3100->i2c_client->dev;
+
+	i2c_set_clientdata(client, ab3100);
+
+	/* Read chip ID register */
+	err = ab3100_get_register_interruptible(ab3100, AB3100_CID,
+						&ab3100->chip_id);
+	if (err) {
+		dev_err(&client->dev,
+			"could not communicate with the AB3100 analog "
+			"baseband chip\n");
+		goto exit_no_detect;
+	}
+
+	for (i = 0; ids[i].id != 0x0; i++) {
+		if (ids[i].id == ab3100->chip_id) {
+			if (ids[i].name != NULL) {
+				snprintf(&ab3100->chip_name[0],
+					 sizeof(ab3100->chip_name) - 1,
+					 "AB3100 %s",
+					 ids[i].name);
+				break;
+			} else {
+				dev_err(&client->dev,
+					"AB3000 is not supported\n");
+				goto exit_no_detect;
+			}
+		}
+	}
+
+	if (ids[i].id == 0x0) {
+		dev_err(&client->dev, "unknown analog baseband chip id: 0x%x\n",
+			ab3100->chip_id);
+		dev_err(&client->dev, "accepting it anyway. Please update "
+			"the driver.\n");
+		goto exit_no_detect;
+	}
+
+	dev_info(&client->dev, "Detected chip: %s\n",
+		 &ab3100->chip_name[0]);
+
+	/* Attach a second dummy i2c_client to the test register address */
+	ab3100->testreg_client = i2c_new_dummy(client->adapter,
+						     client->addr + 1);
+	if (!ab3100->testreg_client) {
+		err = -ENOMEM;
+		goto exit_no_testreg_client;
+	}
+
+	err = ab3100_setup(ab3100);
+	if (err)
+		goto exit_no_setup;
+
+	err = request_threaded_irq(client->irq, NULL, ab3100_irq_handler,
+				IRQF_ONESHOT, "ab3100-core", ab3100);
+	if (err)
+		goto exit_no_irq;
+
+	err = abx500_register_ops(&client->dev, &ab3100_ops);
+	if (err)
+		goto exit_no_ops;
+
+	/* Set up and register the platform devices. */
+	for (i = 0; i < ARRAY_SIZE(ab3100_devs); i++) {
+		ab3100_devs[i].platform_data = ab3100_plf_data;
+		ab3100_devs[i].pdata_size = sizeof(struct ab3100_platform_data);
+	}
+
+	err = mfd_add_devices(&client->dev, 0, ab3100_devs,
+		ARRAY_SIZE(ab3100_devs), NULL, 0);
+
+	ab3100_setup_debugfs(ab3100);
+
+	return 0;
+
+ exit_no_ops:
+ exit_no_irq:
+ exit_no_setup:
+	i2c_unregister_device(ab3100->testreg_client);
+ exit_no_testreg_client:
+ exit_no_detect:
+	kfree(ab3100);
+	return err;
+}
+
+static int __devexit ab3100_remove(struct i2c_client *client)
+{
+	struct ab3100 *ab3100 = i2c_get_clientdata(client);
+
+	/* Unregister subdevices */
+	mfd_remove_devices(&client->dev);
+
+	ab3100_remove_debugfs();
+	i2c_unregister_device(ab3100->testreg_client);
+
+	/*
+	 * At this point, all subscribers should have unregistered
+	 * their notifiers so deactivate IRQ
+	 */
+	free_irq(client->irq, ab3100);
+	kfree(ab3100);
+	return 0;
+}
+
+static const struct i2c_device_id ab3100_id[] = {
+	{ "ab3100", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ab3100_id);
+
+static struct i2c_driver ab3100_driver = {
+	.driver = {
+		.name	= "ab3100",
+		.owner	= THIS_MODULE,
+	},
+	.id_table	= ab3100_id,
+	.probe		= ab3100_probe,
+	.remove		= __devexit_p(ab3100_remove),
+};
+
+static int __init ab3100_i2c_init(void)
+{
+	return i2c_add_driver(&ab3100_driver);
+}
+
+static void __exit ab3100_i2c_exit(void)
+{
+	i2c_del_driver(&ab3100_driver);
+}
+
+subsys_initcall(ab3100_i2c_init);
+module_exit(ab3100_i2c_exit);
+
+MODULE_AUTHOR("Linus Walleij <linus.walleij@stericsson.com>");
+MODULE_DESCRIPTION("AB3100 core driver");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/ab3100-otp.c b/ap/os/linux/linux-3.4.x/drivers/mfd/ab3100-otp.c
new file mode 100644
index 0000000..8440010
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/ab3100-otp.c
@@ -0,0 +1,267 @@
+/*
+ * drivers/mfd/ab3100_otp.c
+ *
+ * Copyright (C) 2007-2009 ST-Ericsson AB
+ * License terms: GNU General Public License (GPL) version 2
+ * Driver to read out OTP from the AB3100 Mixed-signal circuit
+ * Author: Linus Walleij <linus.walleij@stericsson.com>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/abx500.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+/* The OTP registers */
+#define AB3100_OTP0		0xb0
+#define AB3100_OTP1		0xb1
+#define AB3100_OTP2		0xb2
+#define AB3100_OTP3		0xb3
+#define AB3100_OTP4		0xb4
+#define AB3100_OTP5		0xb5
+#define AB3100_OTP6		0xb6
+#define AB3100_OTP7		0xb7
+#define AB3100_OTPP		0xbf
+
+/**
+ * struct ab3100_otp
+ * @dev containing device
+ * @locked whether the OTP is locked, after locking, no more bits
+ *       can be changed but before locking it is still possible
+ *       to change bits from 1->0.
+ * @freq clocking frequency for the OTP, this frequency is either
+ *       32768Hz or 1MHz/30
+ * @paf product activation flag, indicates whether this is a real
+ *       product (paf true) or a lab board etc (paf false)
+ * @imeich if this is set it is possible to override the
+ *       IMEI number found in the tac, fac and svn fields with
+ *       (secured) software
+ * @cid customer ID
+ * @tac type allocation code of the IMEI
+ * @fac final assembly code of the IMEI
+ * @svn software version number of the IMEI
+ * @debugfs a debugfs file used when dumping to file
+ */
+struct ab3100_otp {
+	struct device *dev;
+	bool locked;
+	u32 freq;
+	bool paf;
+	bool imeich;
+	u16 cid:14;
+	u32 tac:20;
+	u8 fac;
+	u32 svn:20;
+	struct dentry *debugfs;
+};
+
+static int __init ab3100_otp_read(struct ab3100_otp *otp)
+{
+	u8 otpval[8];
+	u8 otpp;
+	int err;
+
+	err = abx500_get_register_interruptible(otp->dev, 0,
+		AB3100_OTPP, &otpp);
+	if (err) {
+		dev_err(otp->dev, "unable to read OTPP register\n");
+		return err;
+	}
+
+	err = abx500_get_register_page_interruptible(otp->dev, 0,
+		AB3100_OTP0, otpval, 8);
+	if (err) {
+		dev_err(otp->dev, "unable to read OTP register page\n");
+		return err;
+	}
+
+	/* Cache OTP properties, they never change by nature */
+	otp->locked = (otpp & 0x80);
+	otp->freq = (otpp & 0x40) ? 32768 : 34100;
+	otp->paf = (otpval[1] & 0x80);
+	otp->imeich = (otpval[1] & 0x40);
+	otp->cid = ((otpval[1] << 8) | otpval[0]) & 0x3fff;
+	otp->tac = ((otpval[4] & 0x0f) << 16) | (otpval[3] << 8) | otpval[2];
+	otp->fac = ((otpval[5] & 0x0f) << 4) | (otpval[4] >> 4);
+	otp->svn = (otpval[7] << 12) | (otpval[6] << 4) | (otpval[5] >> 4);
+	return 0;
+}
+
+/*
+ * This is a simple debugfs human-readable file that dumps out
+ * the contents of the OTP.
+ */
+#ifdef CONFIG_DEBUG_FS
+static int ab3100_show_otp(struct seq_file *s, void *v)
+{
+	struct ab3100_otp *otp = s->private;
+
+	seq_printf(s, "OTP is %s\n", otp->locked ? "LOCKED" : "UNLOCKED");
+	seq_printf(s, "OTP clock switch startup is %uHz\n", otp->freq);
+	seq_printf(s, "PAF is %s\n", otp->paf ? "SET" : "NOT SET");
+	seq_printf(s, "IMEI is %s\n", otp->imeich ?
+		   "CHANGEABLE" : "NOT CHANGEABLE");
+	seq_printf(s, "CID: 0x%04x (decimal: %d)\n", otp->cid, otp->cid);
+	seq_printf(s, "IMEI: %u-%u-%u\n", otp->tac, otp->fac, otp->svn);
+	return 0;
+}
+
+static int ab3100_otp_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, ab3100_show_otp, inode->i_private);
+}
+
+static const struct file_operations ab3100_otp_operations = {
+	.open		= ab3100_otp_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int __init ab3100_otp_init_debugfs(struct device *dev,
+					  struct ab3100_otp *otp)
+{
+	otp->debugfs = debugfs_create_file("ab3100_otp", S_IFREG | S_IRUGO,
+					   NULL, otp,
+					   &ab3100_otp_operations);
+	if (!otp->debugfs) {
+		dev_err(dev, "AB3100 debugfs OTP file registration failed!\n");
+		return -ENOENT;
+	}
+	return 0;
+}
+
+static void __exit ab3100_otp_exit_debugfs(struct ab3100_otp *otp)
+{
+	debugfs_remove(otp->debugfs);
+}
+#else
+/* Compile this out if debugfs not selected */
+static inline int __init ab3100_otp_init_debugfs(struct device *dev,
+						 struct ab3100_otp *otp)
+{
+	return 0;
+}
+
+static inline void __exit ab3100_otp_exit_debugfs(struct ab3100_otp *otp)
+{
+}
+#endif
+
+#define SHOW_AB3100_ATTR(name) \
+static ssize_t ab3100_otp_##name##_show(struct device *dev, \
+			       struct device_attribute *attr, \
+			       char *buf) \
+{\
+	struct ab3100_otp *otp = dev_get_drvdata(dev); \
+	return sprintf(buf, "%u\n", otp->name); \
+}
+
+SHOW_AB3100_ATTR(locked)
+SHOW_AB3100_ATTR(freq)
+SHOW_AB3100_ATTR(paf)
+SHOW_AB3100_ATTR(imeich)
+SHOW_AB3100_ATTR(cid)
+SHOW_AB3100_ATTR(fac)
+SHOW_AB3100_ATTR(tac)
+SHOW_AB3100_ATTR(svn)
+
+static struct device_attribute ab3100_otp_attrs[] = {
+	__ATTR(locked, S_IRUGO, ab3100_otp_locked_show, NULL),
+	__ATTR(freq, S_IRUGO, ab3100_otp_freq_show, NULL),
+	__ATTR(paf, S_IRUGO, ab3100_otp_paf_show, NULL),
+	__ATTR(imeich, S_IRUGO, ab3100_otp_imeich_show, NULL),
+	__ATTR(cid, S_IRUGO, ab3100_otp_cid_show, NULL),
+	__ATTR(fac, S_IRUGO, ab3100_otp_fac_show, NULL),
+	__ATTR(tac, S_IRUGO, ab3100_otp_tac_show, NULL),
+	__ATTR(svn, S_IRUGO, ab3100_otp_svn_show, NULL),
+};
+
+static int __init ab3100_otp_probe(struct platform_device *pdev)
+{
+	struct ab3100_otp *otp;
+	int err = 0;
+	int i;
+
+	otp = kzalloc(sizeof(struct ab3100_otp), GFP_KERNEL);
+	if (!otp) {
+		dev_err(&pdev->dev, "could not allocate AB3100 OTP device\n");
+		return -ENOMEM;
+	}
+	otp->dev = &pdev->dev;
+
+	/* Replace platform data coming in with a local struct */
+	platform_set_drvdata(pdev, otp);
+
+	err = ab3100_otp_read(otp);
+	if (err)
+		goto err_otp_read;
+
+	dev_info(&pdev->dev, "AB3100 OTP readout registered\n");
+
+	/* sysfs entries */
+	for (i = 0; i < ARRAY_SIZE(ab3100_otp_attrs); i++) {
+		err = device_create_file(&pdev->dev,
+					 &ab3100_otp_attrs[i]);
+		if (err)
+			goto err_create_file;
+	}
+
+	/* debugfs entries */
+	err = ab3100_otp_init_debugfs(&pdev->dev, otp);
+	if (err)
+		goto err_init_debugfs;
+
+	return 0;
+
+err_init_debugfs:
+err_create_file:
+	while (--i >= 0)
+		device_remove_file(&pdev->dev, &ab3100_otp_attrs[i]);
+err_otp_read:
+	kfree(otp);
+	return err;
+}
+
+static int __exit ab3100_otp_remove(struct platform_device *pdev)
+{
+	struct ab3100_otp *otp = platform_get_drvdata(pdev);
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(ab3100_otp_attrs); i++)
+		device_remove_file(&pdev->dev,
+				   &ab3100_otp_attrs[i]);
+	ab3100_otp_exit_debugfs(otp);
+	kfree(otp);
+	return 0;
+}
+
+static struct platform_driver ab3100_otp_driver = {
+	.driver = {
+		.name = "ab3100-otp",
+		.owner = THIS_MODULE,
+	},
+	.remove	 = __exit_p(ab3100_otp_remove),
+};
+
+static int __init ab3100_otp_init(void)
+{
+	return platform_driver_probe(&ab3100_otp_driver,
+				     ab3100_otp_probe);
+}
+
+static void __exit ab3100_otp_exit(void)
+{
+	platform_driver_unregister(&ab3100_otp_driver);
+}
+
+module_init(ab3100_otp_init);
+module_exit(ab3100_otp_exit);
+
+MODULE_AUTHOR("Linus Walleij <linus.walleij@stericsson.com>");
+MODULE_DESCRIPTION("AB3100 OTP Readout Driver");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/ab5500-core.c b/ap/os/linux/linux-3.4.x/drivers/mfd/ab5500-core.c
new file mode 100644
index 0000000..54d0fe4
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/ab5500-core.c
@@ -0,0 +1,1439 @@
+/*
+ * Copyright (C) 2007-2011 ST-Ericsson
+ * License terms: GNU General Public License (GPL) version 2
+ * Low-level core for exclusive access to the AB5500 IC on the I2C bus
+ * and some basic chip-configuration.
+ * Author: Bengt Jonsson <bengt.g.jonsson@stericsson.com>
+ * Author: Mattias Nilsson <mattias.i.nilsson@stericsson.com>
+ * Author: Mattias Wallin <mattias.wallin@stericsson.com>
+ * Author: Rickard Andersson <rickard.andersson@stericsson.com>
+ * Author: Karl Komierowski  <karl.komierowski@stericsson.com>
+ * Author: Bibek Basu <bibek.basu@stericsson.com>
+ *
+ * TODO: Event handling with irq_chip. Waiting for PRCMU fw support.
+ */
+
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/random.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab5500.h>
+#include <linux/list.h>
+#include <linux/bitops.h>
+#include <linux/spinlock.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/db5500-prcmu.h>
+
+#include "ab5500-core.h"
+#include "ab5500-debugfs.h"
+
+#define AB5500_NUM_EVENT_REG 23
+#define AB5500_IT_LATCH0_REG 0x40
+#define AB5500_IT_MASK0_REG 0x60
+
+/*
+ * Permissible register ranges for reading and writing per device and bank.
+ *
+ * The ranges must be listed in increasing address order, and no overlaps are
+ * allowed. It is assumed that write permission implies read permission
+ * (i.e. only RO and RW permissions should be used).  Ranges with write
+ * permission must not be split up.
+ */
+
+#define NO_RANGE {.count = 0, .range = NULL,}
+static struct ab5500_i2c_banks ab5500_bank_ranges[AB5500_NUM_DEVICES] = {
+	[AB5500_DEVID_USB] =  {
+		.nbanks = 1,
+		.bank = (struct ab5500_i2c_ranges []) {
+			{
+				.bankid = AB5500_BANK_USB,
+				.nranges = 12,
+				.range = (struct ab5500_reg_range[]) {
+					{
+						.first = 0x01,
+						.last = 0x01,
+						.perm = AB5500_PERM_RW,
+					},
+					{
+						.first = 0x80,
+						.last = 0x83,
+						.perm = AB5500_PERM_RW,
+					},
+					{
+						.first = 0x87,
+						.last = 0x8A,
+						.perm = AB5500_PERM_RW,
+					},
+					{
+						.first = 0x8B,
+						.last = 0x8B,
+						.perm = AB5500_PERM_RO,
+					},
+					{
+						.first = 0x91,
+						.last = 0x92,
+						.perm = AB5500_PERM_RO,
+					},
+					{
+						.first = 0x93,
+						.last = 0x93,
+						.perm = AB5500_PERM_RW,
+					},
+					{
+						.first = 0x94,
+						.last = 0x94,
+						.perm = AB5500_PERM_RO,
+					},
+					{
+						.first = 0xA8,
+						.last = 0xB0,
+						.perm = AB5500_PERM_RO,
+					},
+					{
+						.first = 0xB2,
+						.last = 0xB2,
+						.perm = AB5500_PERM_RO,
+					},
+					{
+						.first = 0xB4,
+						.last = 0xBC,
+						.perm = AB5500_PERM_RO,
+					},
+					{
+						.first = 0xBF,
+						.last = 0xBF,
+						.perm = AB5500_PERM_RO,
+					},
+					{
+						.first = 0xC1,
+						.last = 0xC5,
+						.perm = AB5500_PERM_RO,
+					},
+				},
+			},
+		},
+	},
+	[AB5500_DEVID_ADC] =  {
+		.nbanks = 1,
+		.bank = (struct ab5500_i2c_ranges []) {
+			{
+				.bankid = AB5500_BANK_ADC,
+				.nranges = 6,
+				.range = (struct ab5500_reg_range[]) {
+					{
+						.first = 0x1F,
+						.last = 0x22,
+						.perm = AB5500_PERM_RO,
+					},
+					{
+						.first = 0x23,
+						.last = 0x24,
+						.perm = AB5500_PERM_RW,
+					},
+					{
+						.first = 0x26,
+						.last = 0x2D,
+						.perm = AB5500_PERM_RO,
+					},
+					{
+						.first = 0x2F,
+						.last = 0x34,
+						.perm = AB5500_PERM_RW,
+					},
+					{
+						.first = 0x37,
+						.last = 0x57,
+						.perm = AB5500_PERM_RW,
+					},
+					{
+						.first = 0x58,
+						.last = 0x58,
+						.perm = AB5500_PERM_RO,
+					},
+				},
+			},
+		},
+	},
+	[AB5500_DEVID_LEDS] =  {
+		.nbanks = 1,
+		.bank = (struct ab5500_i2c_ranges []) {
+			{
+				.bankid = AB5500_BANK_LED,
+				.nranges = 1,
+				.range = (struct ab5500_reg_range[]) {
+					{
+						.first = 0x00,
+						.last = 0x0C,
+						.perm = AB5500_PERM_RW,
+					},
+				},
+			},
+		},
+	},
+	[AB5500_DEVID_VIDEO] =   {
+		.nbanks = 1,
+		.bank = (struct ab5500_i2c_ranges []) {
+			{
+				.bankid = AB5500_BANK_VDENC,
+				.nranges = 12,
+				.range = (struct ab5500_reg_range[]) {
+					{
+						.first = 0x00,
+						.last = 0x08,
+						.perm = AB5500_PERM_RW,
+					},
+					{
+						.first = 0x09,
+						.last = 0x09,
+						.perm = AB5500_PERM_RO,
+					},
+					{
+						.first = 0x0A,
+						.last = 0x12,
+						.perm = AB5500_PERM_RW,
+					},
+					{
+						.first = 0x15,
+						.last = 0x19,
+						.perm = AB5500_PERM_RW,
+					},
+					{
+						.first = 0x1B,
+						.last = 0x21,
+						.perm = AB5500_PERM_RW,
+					},
+					{
+						.first = 0x27,
+						.last = 0x2C,
+						.perm = AB5500_PERM_RW,
+					},
+					{
+						.first = 0x41,
+						.last = 0x41,
+						.perm = AB5500_PERM_RW,
+					},
+					{
+						.first = 0x45,
+						.last = 0x5B,
+						.perm = AB5500_PERM_RW,
+					},
+					{
+						.first = 0x5D,
+						.last = 0x5D,
+						.perm = AB5500_PERM_RW,
+					},
+					{
+						.first = 0x69,
+						.last = 0x69,
+						.perm = AB5500_PERM_RW,
+					},
+					{
+						.first = 0x6C,
+						.last = 0x6D,
+						.perm = AB5500_PERM_RW,
+					},
+					{
+						.first = 0x80,
+						.last = 0x81,
+						.perm = AB5500_PERM_RW,
+					},
+				},
+			},
+		},
+	},
+	[AB5500_DEVID_REGULATORS] =   {
+		.nbanks = 2,
+		.bank =  (struct ab5500_i2c_ranges []) {
+			{
+				.bankid = AB5500_BANK_STARTUP,
+				.nranges = 12,
+				.range = (struct ab5500_reg_range[]) {
+					{
+						.first = 0x00,
+						.last = 0x01,
+						.perm = AB5500_PERM_RW,
+					},
+					{
+						.first = 0x1F,
+						.last = 0x1F,
+						.perm = AB5500_PERM_RW,
+					},
+					{
+						.first = 0x2E,
+						.last = 0x2E,
+						.perm = AB5500_PERM_RO,
+					},
+					{
+						.first = 0x2F,
+						.last = 0x30,
+						.perm = AB5500_PERM_RW,
+					},
+					{
+						.first = 0x50,
+						.last = 0x51,
+						.perm = AB5500_PERM_RW,
+					},
+					{
+						.first = 0x60,
+						.last = 0x61,
+						.perm = AB5500_PERM_RW,
+					},
+					{
+						.first = 0x66,
+						.last = 0x8A,
+						.perm = AB5500_PERM_RW,
+					},
+					{
+						.first = 0x8C,
+						.last = 0x96,
+						.perm = AB5500_PERM_RW,
+					},
+					{
+						.first = 0xAA,
+						.last = 0xB4,
+						.perm = AB5500_PERM_RW,
+					},
+					{
+						.first = 0xB7,
+						.last = 0xBF,
+						.perm = AB5500_PERM_RW,
+					},
+					{
+						.first = 0xC1,
+						.last = 0xCA,
+						.perm = AB5500_PERM_RW,
+					},
+					{
+						.first = 0xD3,
+						.last = 0xE0,
+						.perm = AB5500_PERM_RW,
+					},
+				},
+			},
+			{
+				.bankid = AB5500_BANK_SIM_USBSIM,
+				.nranges = 1,
+				.range = (struct ab5500_reg_range[]) {
+					{
+						.first = 0x13,
+						.last = 0x19,
+						.perm = AB5500_PERM_RW,
+					},
+				},
+			},
+		},
+	},
+	[AB5500_DEVID_SIM] =   {
+		.nbanks = 1,
+		.bank = (struct ab5500_i2c_ranges []) {
+			{
+				.bankid = AB5500_BANK_SIM_USBSIM,
+				.nranges = 1,
+				.range = (struct ab5500_reg_range[]) {
+					{
+						.first = 0x13,
+						.last = 0x19,
+						.perm = AB5500_PERM_RW,
+					},
+				},
+			},
+		},
+	},
+	[AB5500_DEVID_RTC] =   {
+		.nbanks = 1,
+		.bank = (struct ab5500_i2c_ranges []) {
+			{
+				.bankid = AB5500_BANK_RTC,
+				.nranges = 2,
+				.range = (struct ab5500_reg_range[]) {
+					{
+						.first = 0x00,
+						.last = 0x04,
+						.perm = AB5500_PERM_RW,
+					},
+					{
+						.first = 0x06,
+						.last = 0x0C,
+						.perm = AB5500_PERM_RW,
+					},
+				},
+			},
+		},
+	},
+	[AB5500_DEVID_CHARGER] =   {
+		.nbanks = 1,
+		.bank = (struct ab5500_i2c_ranges []) {
+			{
+				.bankid = AB5500_BANK_CHG,
+				.nranges = 2,
+				.range = (struct ab5500_reg_range[]) {
+					{
+						.first = 0x11,
+						.last = 0x11,
+						.perm = AB5500_PERM_RO,
+					},
+					{
+						.first = 0x12,
+						.last = 0x1B,
+						.perm = AB5500_PERM_RW,
+					},
+				},
+			},
+		},
+	},
+	[AB5500_DEVID_FUELGAUGE] =   {
+		.nbanks = 1,
+		.bank = (struct ab5500_i2c_ranges []) {
+			{
+				.bankid = AB5500_BANK_FG_BATTCOM_ACC,
+				.nranges = 2,
+				.range = (struct ab5500_reg_range[]) {
+					{
+						.first = 0x00,
+						.last = 0x0B,
+						.perm = AB5500_PERM_RO,
+					},
+					{
+						.first = 0x0C,
+						.last = 0x10,
+						.perm = AB5500_PERM_RW,
+					},
+				},
+			},
+		},
+	},
+	[AB5500_DEVID_VIBRATOR] =   {
+		.nbanks = 1,
+		.bank = (struct ab5500_i2c_ranges []) {
+			{
+				.bankid = AB5500_BANK_VIBRA,
+				.nranges = 2,
+				.range = (struct ab5500_reg_range[]) {
+					{
+						.first = 0x10,
+						.last = 0x13,
+						.perm = AB5500_PERM_RW,
+					},
+					{
+						.first = 0xFE,
+						.last = 0xFE,
+						.perm = AB5500_PERM_RW,
+					},
+				},
+			},
+		},
+	},
+	[AB5500_DEVID_CODEC] =   {
+		.nbanks = 1,
+		.bank = (struct ab5500_i2c_ranges []) {
+			{
+				.bankid = AB5500_BANK_AUDIO_HEADSETUSB,
+				.nranges = 2,
+				.range = (struct ab5500_reg_range[]) {
+					{
+						.first = 0x00,
+						.last = 0x48,
+						.perm = AB5500_PERM_RW,
+					},
+					{
+						.first = 0xEB,
+						.last = 0xFB,
+						.perm = AB5500_PERM_RW,
+					},
+				},
+			},
+		},
+	},
+	[AB5500_DEVID_POWER] = {
+		.nbanks	= 2,
+		.bank	= (struct ab5500_i2c_ranges []) {
+			{
+				.bankid = AB5500_BANK_STARTUP,
+				.nranges = 1,
+				.range = (struct ab5500_reg_range[]) {
+					{
+						.first = 0x30,
+						.last = 0x30,
+						.perm = AB5500_PERM_RW,
+					},
+				},
+			},
+			{
+				.bankid = AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP,
+				.nranges = 1,
+				.range = (struct ab5500_reg_range[]) {
+					{
+						.first = 0x01,
+						.last = 0x01,
+						.perm = AB5500_PERM_RW,
+					},
+				},
+			},
+		},
+	},
+};
+
+#define AB5500_IRQ(bank, bit)	((bank) * 8 + (bit))
+
+/* I appologize for the resource names beeing a mix of upper case
+ * and lower case but I want them to be exact as the documentation */
+static struct mfd_cell ab5500_devs[AB5500_NUM_DEVICES] = {
+	[AB5500_DEVID_LEDS] = {
+		.name = "ab5500-leds",
+		.id = AB5500_DEVID_LEDS,
+	},
+	[AB5500_DEVID_POWER] = {
+		.name = "ab5500-power",
+		.id = AB5500_DEVID_POWER,
+	},
+	[AB5500_DEVID_REGULATORS] = {
+		.name = "ab5500-regulator",
+		.id = AB5500_DEVID_REGULATORS,
+	},
+	[AB5500_DEVID_SIM] = {
+		.name = "ab5500-sim",
+		.id = AB5500_DEVID_SIM,
+		.num_resources = 1,
+		.resources = (struct resource[]) {
+			{
+				.name = "SIMOFF",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(2, 0), /*rising*/
+				.end = AB5500_IRQ(2, 1), /*falling*/
+			},
+		},
+	},
+	[AB5500_DEVID_RTC] = {
+		.name = "ab5500-rtc",
+		.id = AB5500_DEVID_RTC,
+		.num_resources = 1,
+		.resources = (struct resource[]) {
+			{
+				.name	= "RTC_Alarm",
+				.flags	= IORESOURCE_IRQ,
+				.start	= AB5500_IRQ(1, 7),
+				.end	= AB5500_IRQ(1, 7),
+			}
+		},
+	},
+	[AB5500_DEVID_CHARGER] = {
+		.name = "ab5500-charger",
+		.id = AB5500_DEVID_CHARGER,
+	},
+	[AB5500_DEVID_ADC] = {
+		.name = "ab5500-adc",
+		.id = AB5500_DEVID_ADC,
+		.num_resources = 10,
+		.resources = (struct resource[]) {
+			{
+				.name = "TRIGGER-0",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(0, 0),
+				.end = AB5500_IRQ(0, 0),
+			},
+			{
+				.name = "TRIGGER-1",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(0, 1),
+				.end = AB5500_IRQ(0, 1),
+			},
+			{
+				.name = "TRIGGER-2",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(0, 2),
+				.end = AB5500_IRQ(0, 2),
+			},
+			{
+				.name = "TRIGGER-3",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(0, 3),
+				.end = AB5500_IRQ(0, 3),
+			},
+			{
+				.name = "TRIGGER-4",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(0, 4),
+				.end = AB5500_IRQ(0, 4),
+			},
+			{
+				.name = "TRIGGER-5",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(0, 5),
+				.end = AB5500_IRQ(0, 5),
+			},
+			{
+				.name = "TRIGGER-6",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(0, 6),
+				.end = AB5500_IRQ(0, 6),
+			},
+			{
+				.name = "TRIGGER-7",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(0, 7),
+				.end = AB5500_IRQ(0, 7),
+			},
+			{
+				.name = "TRIGGER-VBAT",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(0, 8),
+				.end = AB5500_IRQ(0, 8),
+			},
+			{
+				.name = "TRIGGER-VBAT-TXON",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(0, 9),
+				.end = AB5500_IRQ(0, 9),
+			},
+		},
+	},
+	[AB5500_DEVID_FUELGAUGE] = {
+		.name = "ab5500-fuelgauge",
+		.id = AB5500_DEVID_FUELGAUGE,
+		.num_resources = 6,
+		.resources = (struct resource[]) {
+			{
+				.name = "Batt_attach",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(7, 5),
+				.end = AB5500_IRQ(7, 5),
+			},
+			{
+				.name = "Batt_removal",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(7, 6),
+				.end = AB5500_IRQ(7, 6),
+			},
+			{
+				.name = "UART_framing",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(7, 7),
+				.end = AB5500_IRQ(7, 7),
+			},
+			{
+				.name = "UART_overrun",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(8, 0),
+				.end = AB5500_IRQ(8, 0),
+			},
+			{
+				.name = "UART_Rdy_RX",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(8, 1),
+				.end = AB5500_IRQ(8, 1),
+			},
+			{
+				.name = "UART_Rdy_TX",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(8, 2),
+				.end = AB5500_IRQ(8, 2),
+			},
+		},
+	},
+	[AB5500_DEVID_VIBRATOR] = {
+		.name = "ab5500-vibrator",
+		.id = AB5500_DEVID_VIBRATOR,
+	},
+	[AB5500_DEVID_CODEC] = {
+		.name = "ab5500-codec",
+		.id = AB5500_DEVID_CODEC,
+		.num_resources = 3,
+		.resources = (struct resource[]) {
+			{
+				.name = "audio_spkr1_ovc",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(9, 5),
+				.end = AB5500_IRQ(9, 5),
+			},
+			{
+				.name = "audio_plllocked",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(9, 6),
+				.end = AB5500_IRQ(9, 6),
+			},
+			{
+				.name = "audio_spkr2_ovc",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(17, 4),
+				.end = AB5500_IRQ(17, 4),
+			},
+		},
+	},
+	[AB5500_DEVID_USB] = {
+		.name = "ab5500-usb",
+		.id = AB5500_DEVID_USB,
+		.num_resources = 36,
+		.resources = (struct resource[]) {
+			{
+				.name = "Link_Update",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(22, 1),
+				.end = AB5500_IRQ(22, 1),
+			},
+			{
+				.name = "DCIO",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(8, 3),
+				.end = AB5500_IRQ(8, 4),
+			},
+			{
+				.name = "VBUS_R",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(8, 5),
+				.end = AB5500_IRQ(8, 5),
+			},
+			{
+				.name = "VBUS_F",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(8, 6),
+				.end = AB5500_IRQ(8, 6),
+			},
+			{
+				.name = "CHGstate_10_PCVBUSchg",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(8, 7),
+				.end = AB5500_IRQ(8, 7),
+			},
+			{
+				.name = "DCIOreverse_ovc",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(9, 0),
+				.end = AB5500_IRQ(9, 0),
+			},
+			{
+				.name = "USBCharDetDone",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(9, 1),
+				.end = AB5500_IRQ(9, 1),
+			},
+			{
+				.name = "DCIO_no_limit",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(9, 2),
+				.end = AB5500_IRQ(9, 2),
+			},
+			{
+				.name = "USB_suspend",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(9, 3),
+				.end = AB5500_IRQ(9, 3),
+			},
+			{
+				.name = "DCIOreverse_fwdcurrent",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(9, 4),
+				.end = AB5500_IRQ(9, 4),
+			},
+			{
+				.name = "Vbus_Imeasmax_change",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(9, 5),
+				.end = AB5500_IRQ(9, 6),
+			},
+			{
+				.name = "OVV",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(14, 5),
+				.end = AB5500_IRQ(14, 5),
+			},
+			{
+				.name = "USBcharging_NOTok",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(15, 3),
+				.end = AB5500_IRQ(15, 3),
+			},
+			{
+				.name = "usb_adp_sensoroff",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(15, 6),
+				.end = AB5500_IRQ(15, 6),
+			},
+			{
+				.name = "usb_adp_probeplug",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(15, 7),
+				.end = AB5500_IRQ(15, 7),
+			},
+			{
+				.name = "usb_adp_sinkerror",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(16, 0),
+				.end = AB5500_IRQ(16, 6),
+			},
+			{
+				.name = "usb_adp_sourceerror",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(16, 1),
+				.end = AB5500_IRQ(16, 1),
+			},
+			{
+				.name = "usb_idgnd_r",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(16, 2),
+				.end = AB5500_IRQ(16, 2),
+			},
+			{
+				.name = "usb_idgnd_f",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(16, 3),
+				.end = AB5500_IRQ(16, 3),
+			},
+			{
+				.name = "usb_iddetR1",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(16, 4),
+				.end = AB5500_IRQ(16, 5),
+			},
+			{
+				.name = "usb_iddetR2",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(16, 6),
+				.end = AB5500_IRQ(16, 7),
+			},
+			{
+				.name = "usb_iddetR3",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(17, 0),
+				.end = AB5500_IRQ(17, 1),
+			},
+			{
+				.name = "usb_iddetR4",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(17, 2),
+				.end = AB5500_IRQ(17, 3),
+			},
+			{
+				.name = "CharTempWindowOk",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(17, 7),
+				.end = AB5500_IRQ(18, 0),
+			},
+			{
+				.name = "USB_SprDetect",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(18, 1),
+				.end = AB5500_IRQ(18, 1),
+			},
+			{
+				.name = "usb_adp_probe_unplug",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(18, 2),
+				.end = AB5500_IRQ(18, 2),
+			},
+			{
+				.name = "VBUSChDrop",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(18, 3),
+				.end = AB5500_IRQ(18, 4),
+			},
+			{
+				.name = "dcio_char_rec_done",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(18, 5),
+				.end = AB5500_IRQ(18, 5),
+			},
+			{
+				.name = "Charging_stopped_by_temp",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(18, 6),
+				.end = AB5500_IRQ(18, 6),
+			},
+			{
+				.name = "CHGstate_11_SafeModeVBUS",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(21, 1),
+				.end = AB5500_IRQ(21, 2),
+			},
+			{
+				.name = "CHGstate_12_comletedVBUS",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(21, 2),
+				.end = AB5500_IRQ(21, 2),
+			},
+			{
+				.name = "CHGstate_13_completedVBUS",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(21, 3),
+				.end = AB5500_IRQ(21, 3),
+			},
+			{
+				.name = "CHGstate_14_FullChgDCIO",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(21, 4),
+				.end = AB5500_IRQ(21, 4),
+			},
+			{
+				.name = "CHGstate_15_SafeModeDCIO",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(21, 5),
+				.end = AB5500_IRQ(21, 5),
+			},
+			{
+				.name = "CHGstate_16_OFFsuspendDCIO",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(21, 6),
+				.end = AB5500_IRQ(21, 6),
+			},
+			{
+				.name = "CHGstate_17_completedDCIO",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(21, 7),
+				.end = AB5500_IRQ(21, 7),
+			},
+		},
+	},
+	[AB5500_DEVID_OTP] = {
+		.name = "ab5500-otp",
+		.id = AB5500_DEVID_OTP,
+	},
+	[AB5500_DEVID_VIDEO] = {
+		.name = "ab5500-video",
+		.id = AB5500_DEVID_VIDEO,
+		.num_resources = 1,
+		.resources = (struct resource[]) {
+			{
+				.name = "plugTVdet",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(22, 2),
+				.end = AB5500_IRQ(22, 2),
+			},
+		},
+	},
+	[AB5500_DEVID_DBIECI] = {
+		.name = "ab5500-dbieci",
+		.id = AB5500_DEVID_DBIECI,
+		.num_resources = 10,
+		.resources = (struct resource[]) {
+			{
+				.name = "COLL",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(14, 0),
+				.end = AB5500_IRQ(14, 0),
+			},
+			{
+				.name = "RESERR",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(14, 1),
+				.end = AB5500_IRQ(14, 1),
+			},
+			{
+				.name = "FRAERR",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(14, 2),
+				.end = AB5500_IRQ(14, 2),
+			},
+			{
+				.name = "COMERR",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(14, 3),
+				.end = AB5500_IRQ(14, 3),
+			},
+			{
+				.name = "BSI_indicator",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(14, 4),
+				.end = AB5500_IRQ(14, 4),
+			},
+			{
+				.name = "SPDSET",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(14, 6),
+				.end = AB5500_IRQ(14, 6),
+			},
+			{
+				.name = "DSENT",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(14, 7),
+				.end = AB5500_IRQ(14, 7),
+			},
+			{
+				.name = "DREC",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(15, 0),
+				.end = AB5500_IRQ(15, 0),
+			},
+			{
+				.name = "ACCINT",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(15, 1),
+				.end = AB5500_IRQ(15, 1),
+			},
+			{
+				.name = "NOPINT",
+				.flags = IORESOURCE_IRQ,
+				.start = AB5500_IRQ(15, 2),
+				.end = AB5500_IRQ(15, 2),
+			},
+		},
+	},
+	[AB5500_DEVID_ONSWA] = {
+		.name = "ab5500-onswa",
+		.id = AB5500_DEVID_ONSWA,
+		.num_resources = 2,
+		.resources = (struct resource[]) {
+			{
+				.name	= "ONSWAn_rising",
+				.flags	= IORESOURCE_IRQ,
+				.start	= AB5500_IRQ(1, 3),
+				.end	= AB5500_IRQ(1, 3),
+			},
+			{
+				.name	= "ONSWAn_falling",
+				.flags	= IORESOURCE_IRQ,
+				.start	= AB5500_IRQ(1, 4),
+				.end	= AB5500_IRQ(1, 4),
+			},
+		},
+	},
+};
+
+/*
+ * Functionality for getting/setting register values.
+ */
+int ab5500_get_register_interruptible_raw(struct ab5500 *ab,
+					  u8 bank, u8 reg,
+					  u8 *value)
+{
+	int err;
+
+	if (bank >= AB5500_NUM_BANKS)
+		return -EINVAL;
+
+	err = mutex_lock_interruptible(&ab->access_mutex);
+	if (err)
+		return err;
+	err = db5500_prcmu_abb_read(bankinfo[bank].slave_addr, reg, value, 1);
+
+	mutex_unlock(&ab->access_mutex);
+	return err;
+}
+
+static int get_register_page_interruptible(struct ab5500 *ab, u8 bank,
+	u8 first_reg, u8 *regvals, u8 numregs)
+{
+	int err;
+
+	if (bank >= AB5500_NUM_BANKS)
+		return -EINVAL;
+
+	err = mutex_lock_interruptible(&ab->access_mutex);
+	if (err)
+		return err;
+
+	while (numregs) {
+		/* The hardware limit for get page is 4 */
+		u8 curnum = min_t(u8, numregs, 4u);
+
+		err = db5500_prcmu_abb_read(bankinfo[bank].slave_addr,
+					    first_reg, regvals, curnum);
+		if (err)
+			goto out;
+
+		numregs -= curnum;
+		first_reg += curnum;
+		regvals += curnum;
+	}
+
+out:
+	mutex_unlock(&ab->access_mutex);
+	return err;
+}
+
+int ab5500_mask_and_set_register_interruptible_raw(struct ab5500 *ab, u8 bank,
+	u8 reg, u8 bitmask, u8 bitvalues)
+{
+	int err = 0;
+
+	if (bank >= AB5500_NUM_BANKS)
+		return -EINVAL;
+
+	if (bitmask) {
+		u8 buf;
+
+		err = mutex_lock_interruptible(&ab->access_mutex);
+		if (err)
+			return err;
+
+		if (bitmask == 0xFF) /* No need to read in this case. */
+			buf = bitvalues;
+		else { /* Read and modify the register value. */
+			err = db5500_prcmu_abb_read(bankinfo[bank].slave_addr,
+				reg, &buf, 1);
+			if (err)
+				return err;
+
+			buf = ((~bitmask & buf) | (bitmask & bitvalues));
+		}
+		/* Write the new value. */
+		err = db5500_prcmu_abb_write(bankinfo[bank].slave_addr, reg,
+					     &buf, 1);
+
+		mutex_unlock(&ab->access_mutex);
+	}
+	return err;
+}
+
+static int
+set_register_interruptible(struct ab5500 *ab, u8 bank, u8 reg, u8 value)
+{
+	return ab5500_mask_and_set_register_interruptible_raw(ab, bank, reg,
+							      0xff, value);
+}
+
+/*
+ * Read/write permission checking functions.
+ */
+static const struct ab5500_i2c_ranges *get_bankref(u8 devid, u8 bank)
+{
+	u8 i;
+
+	if (devid < AB5500_NUM_DEVICES) {
+		for (i = 0; i < ab5500_bank_ranges[devid].nbanks; i++) {
+			if (ab5500_bank_ranges[devid].bank[i].bankid == bank)
+				return &ab5500_bank_ranges[devid].bank[i];
+		}
+	}
+	return NULL;
+}
+
+static bool page_write_allowed(u8 devid, u8 bank, u8 first_reg, u8 last_reg)
+{
+	u8 i; /* range loop index */
+	const struct ab5500_i2c_ranges *bankref;
+
+	bankref = get_bankref(devid, bank);
+	if (bankref == NULL || last_reg < first_reg)
+		return false;
+
+	for (i = 0; i < bankref->nranges; i++) {
+		if (first_reg < bankref->range[i].first)
+			break;
+		if ((last_reg <= bankref->range[i].last) &&
+			(bankref->range[i].perm & AB5500_PERM_WR))
+			return true;
+	}
+	return false;
+}
+
+static bool reg_write_allowed(u8 devid, u8 bank, u8 reg)
+{
+	return page_write_allowed(devid, bank, reg, reg);
+}
+
+static bool page_read_allowed(u8 devid, u8 bank, u8 first_reg, u8 last_reg)
+{
+	u8 i;
+	const struct ab5500_i2c_ranges *bankref;
+
+	bankref = get_bankref(devid, bank);
+	if (bankref == NULL || last_reg < first_reg)
+		return false;
+
+
+	/* Find the range (if it exists in the list) that includes first_reg. */
+	for (i = 0; i < bankref->nranges; i++) {
+		if (first_reg < bankref->range[i].first)
+			return false;
+		if (first_reg <= bankref->range[i].last)
+			break;
+	}
+	/* Make sure that the entire range up to and including last_reg is
+	 * readable. This may span several of the ranges in the list.
+	 */
+	while ((i < bankref->nranges) &&
+		(bankref->range[i].perm & AB5500_PERM_RD)) {
+		if (last_reg <= bankref->range[i].last)
+			return true;
+		if ((++i >= bankref->nranges) ||
+			(bankref->range[i].first !=
+				(bankref->range[i - 1].last + 1))) {
+			break;
+		}
+	}
+	return false;
+}
+
+static bool reg_read_allowed(u8 devid, u8 bank, u8 reg)
+{
+	return page_read_allowed(devid, bank, reg, reg);
+}
+
+
+/*
+ * The exported register access functionality.
+ */
+static int ab5500_get_chip_id(struct device *dev)
+{
+	struct ab5500 *ab = dev_get_drvdata(dev->parent);
+
+	return (int)ab->chip_id;
+}
+
+static int ab5500_mask_and_set_register_interruptible(struct device *dev,
+		u8 bank, u8 reg, u8 bitmask, u8 bitvalues)
+{
+	struct ab5500 *ab;
+	struct platform_device *pdev = to_platform_device(dev);
+
+	if ((AB5500_NUM_BANKS <= bank) ||
+		!reg_write_allowed(pdev->id, bank, reg))
+		return -EINVAL;
+
+	ab = dev_get_drvdata(dev->parent);
+	return ab5500_mask_and_set_register_interruptible_raw(ab, bank, reg,
+		bitmask, bitvalues);
+}
+
+static int ab5500_set_register_interruptible(struct device *dev, u8 bank,
+	u8 reg, u8 value)
+{
+	return ab5500_mask_and_set_register_interruptible(dev, bank, reg, 0xFF,
+		value);
+}
+
+static int ab5500_get_register_interruptible(struct device *dev, u8 bank,
+		u8 reg, u8 *value)
+{
+	struct ab5500 *ab;
+	struct platform_device *pdev = to_platform_device(dev);
+
+	if ((AB5500_NUM_BANKS <= bank) ||
+		!reg_read_allowed(pdev->id, bank, reg))
+		return -EINVAL;
+
+	ab = dev_get_drvdata(dev->parent);
+	return ab5500_get_register_interruptible_raw(ab, bank, reg, value);
+}
+
+static int ab5500_get_register_page_interruptible(struct device *dev, u8 bank,
+		u8 first_reg, u8 *regvals, u8 numregs)
+{
+	struct ab5500 *ab;
+	struct platform_device *pdev = to_platform_device(dev);
+
+	if ((AB5500_NUM_BANKS <= bank) ||
+		!page_read_allowed(pdev->id, bank,
+			first_reg, (first_reg + numregs - 1)))
+		return -EINVAL;
+
+	ab = dev_get_drvdata(dev->parent);
+	return get_register_page_interruptible(ab, bank, first_reg, regvals,
+		numregs);
+}
+
+static int
+ab5500_event_registers_startup_state_get(struct device *dev, u8 *event)
+{
+	struct ab5500 *ab;
+
+	ab = dev_get_drvdata(dev->parent);
+	if (!ab->startup_events_read)
+		return -EAGAIN; /* Try again later */
+
+	memcpy(event, ab->startup_events, AB5500_NUM_EVENT_REG);
+	return 0;
+}
+
+static struct abx500_ops ab5500_ops = {
+	.get_chip_id = ab5500_get_chip_id,
+	.get_register = ab5500_get_register_interruptible,
+	.set_register = ab5500_set_register_interruptible,
+	.get_register_page = ab5500_get_register_page_interruptible,
+	.set_register_page = NULL,
+	.mask_and_set_register = ab5500_mask_and_set_register_interruptible,
+	.event_registers_startup_state_get =
+		ab5500_event_registers_startup_state_get,
+	.startup_irq_enabled = NULL,
+};
+
+/*
+ * ab5500_setup : Basic set-up, datastructure creation/destruction
+ *		  and I2C interface.This sets up a default config
+ *		  in the AB5500 chip so that it will work as expected.
+ * @ab :	  Pointer to ab5500 structure
+ * @settings :    Pointer to struct abx500_init_settings
+ * @size :        Size of init data
+ */
+static int __init ab5500_setup(struct ab5500 *ab,
+	struct abx500_init_settings *settings, unsigned int size)
+{
+	int err = 0;
+	int i;
+
+	for (i = 0; i < size; i++) {
+		err = ab5500_mask_and_set_register_interruptible_raw(ab,
+			settings[i].bank,
+			settings[i].reg,
+			0xFF, settings[i].setting);
+		if (err)
+			goto exit_no_setup;
+
+		/* If event mask register update the event mask in ab5500 */
+		if ((settings[i].bank == AB5500_BANK_IT) &&
+			(AB5500_MASK_BASE <= settings[i].reg) &&
+			(settings[i].reg <= AB5500_MASK_END)) {
+			ab->mask[settings[i].reg - AB5500_MASK_BASE] =
+				settings[i].setting;
+		}
+	}
+exit_no_setup:
+	return err;
+}
+
+struct ab_family_id {
+	u8	id;
+	char	*name;
+};
+
+static const struct ab_family_id ids[] __initdata = {
+	/* AB5500 */
+	{
+		.id = AB5500_1_0,
+		.name = "1.0"
+	},
+	{
+		.id = AB5500_1_1,
+		.name = "1.1"
+	},
+	/* Terminator */
+	{
+		.id = 0x00,
+	}
+};
+
+static int __init ab5500_probe(struct platform_device *pdev)
+{
+	struct ab5500 *ab;
+	struct ab5500_platform_data *ab5500_plf_data =
+		pdev->dev.platform_data;
+	int err;
+	int i;
+
+	ab = kzalloc(sizeof(struct ab5500), GFP_KERNEL);
+	if (!ab) {
+		dev_err(&pdev->dev,
+			"could not allocate ab5500 device\n");
+		return -ENOMEM;
+	}
+
+	/* Initialize data structure */
+	mutex_init(&ab->access_mutex);
+	mutex_init(&ab->irq_lock);
+	ab->dev = &pdev->dev;
+
+	platform_set_drvdata(pdev, ab);
+
+	/* Read chip ID register */
+	err = ab5500_get_register_interruptible_raw(ab,
+					AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP,
+					AB5500_CHIP_ID, &ab->chip_id);
+	if (err) {
+		dev_err(&pdev->dev, "could not communicate with the analog "
+			"baseband chip\n");
+		goto exit_no_detect;
+	}
+
+	for (i = 0; ids[i].id != 0x0; i++) {
+		if (ids[i].id == ab->chip_id) {
+			snprintf(&ab->chip_name[0], sizeof(ab->chip_name) - 1,
+				"AB5500 %s", ids[i].name);
+			break;
+		}
+	}
+	if (ids[i].id == 0x0) {
+		dev_err(&pdev->dev, "unknown analog baseband chip id: 0x%x\n",
+			ab->chip_id);
+		dev_err(&pdev->dev, "driver not started!\n");
+		goto exit_no_detect;
+	}
+
+	/* Clear and mask all interrupts */
+	for (i = 0; i < AB5500_NUM_IRQ_REGS; i++) {
+		u8 latchreg = AB5500_IT_LATCH0_REG + i;
+		u8 maskreg = AB5500_IT_MASK0_REG + i;
+		u8 val;
+
+		ab5500_get_register_interruptible_raw(ab, AB5500_BANK_IT,
+						      latchreg, &val);
+		set_register_interruptible(ab, AB5500_BANK_IT, maskreg, 0xff);
+		ab->mask[i] = ab->oldmask[i] = 0xff;
+	}
+
+	err = abx500_register_ops(&pdev->dev, &ab5500_ops);
+	if (err) {
+		dev_err(&pdev->dev, "ab5500_register ops error\n");
+		goto exit_no_detect;
+	}
+
+	/* Set up and register the platform devices. */
+	for (i = 0; i < AB5500_NUM_DEVICES; i++) {
+		ab5500_devs[i].platform_data = ab5500_plf_data->dev_data[i];
+		ab5500_devs[i].pdata_size =
+			sizeof(ab5500_plf_data->dev_data[i]);
+	}
+
+	err = mfd_add_devices(&pdev->dev, 0, ab5500_devs,
+		ARRAY_SIZE(ab5500_devs), NULL,
+		ab5500_plf_data->irq.base);
+	if (err) {
+		dev_err(&pdev->dev, "ab5500_mfd_add_device error\n");
+		goto exit_no_detect;
+	}
+
+	err = ab5500_setup(ab, ab5500_plf_data->init_settings,
+		ab5500_plf_data->init_settings_sz);
+	if (err) {
+		dev_err(&pdev->dev, "ab5500_setup error\n");
+		goto exit_no_detect;
+	}
+
+	ab5500_setup_debugfs(ab);
+
+	dev_info(&pdev->dev, "detected AB chip: %s\n", &ab->chip_name[0]);
+	return 0;
+
+exit_no_detect:
+	kfree(ab);
+	return err;
+}
+
+static int __exit ab5500_remove(struct platform_device *pdev)
+{
+	struct ab5500 *ab = platform_get_drvdata(pdev);
+
+	ab5500_remove_debugfs();
+	mfd_remove_devices(&pdev->dev);
+	kfree(ab);
+	return 0;
+}
+
+static struct platform_driver ab5500_driver = {
+	.driver = {
+		.name = "ab5500-core",
+		.owner = THIS_MODULE,
+	},
+	.remove  = __exit_p(ab5500_remove),
+};
+
+static int __init ab5500_core_init(void)
+{
+	return platform_driver_probe(&ab5500_driver, ab5500_probe);
+}
+
+static void __exit ab5500_core_exit(void)
+{
+	platform_driver_unregister(&ab5500_driver);
+}
+
+subsys_initcall(ab5500_core_init);
+module_exit(ab5500_core_exit);
+
+MODULE_AUTHOR("Mattias Wallin <mattias.wallin@stericsson.com>");
+MODULE_DESCRIPTION("AB5500 core driver");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/ab5500-core.h b/ap/os/linux/linux-3.4.x/drivers/mfd/ab5500-core.h
new file mode 100644
index 0000000..63b30b1
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/ab5500-core.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2011 ST-Ericsson
+ * License terms: GNU General Public License (GPL) version 2
+ * Shared definitions and data structures for the AB5500 MFD driver
+ */
+
+/* Read/write operation values. */
+#define AB5500_PERM_RD (0x01)
+#define AB5500_PERM_WR (0x02)
+
+/* Read/write permissions. */
+#define AB5500_PERM_RO (AB5500_PERM_RD)
+#define AB5500_PERM_RW (AB5500_PERM_RD | AB5500_PERM_WR)
+
+#define AB5500_MASK_BASE (0x60)
+#define AB5500_MASK_END (0x79)
+#define AB5500_CHIP_ID (0x20)
+
+/**
+ * struct ab5500_reg_range
+ * @first: the first address of the range
+ * @last: the last address of the range
+ * @perm: access permissions for the range
+ */
+struct ab5500_reg_range {
+	u8 first;
+	u8 last;
+	u8 perm;
+};
+
+/**
+ * struct ab5500_i2c_ranges
+ * @count: the number of ranges in the list
+ * @range: the list of register ranges
+ */
+struct ab5500_i2c_ranges {
+	u8 nranges;
+	u8 bankid;
+	const struct ab5500_reg_range *range;
+};
+
+/**
+ * struct ab5500_i2c_banks
+ * @count: the number of ranges in the list
+ * @range: the list of register ranges
+ */
+struct ab5500_i2c_banks {
+	u8 nbanks;
+	const struct ab5500_i2c_ranges *bank;
+};
+
+/**
+ * struct ab5500_bank
+ * @slave_addr: I2C slave_addr found in AB5500 specification
+ * @name: Documentation name of the bank. For reference
+ */
+struct ab5500_bank {
+	u8 slave_addr;
+	const char *name;
+};
+
+static const struct ab5500_bank bankinfo[AB5500_NUM_BANKS] = {
+	[AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP] = {
+		AB5500_ADDR_VIT_IO_I2C_CLK_TST_OTP, "VIT_IO_I2C_CLK_TST_OTP"},
+	[AB5500_BANK_VDDDIG_IO_I2C_CLK_TST] = {
+		AB5500_ADDR_VDDDIG_IO_I2C_CLK_TST, "VDDDIG_IO_I2C_CLK_TST"},
+	[AB5500_BANK_VDENC] = {AB5500_ADDR_VDENC, "VDENC"},
+	[AB5500_BANK_SIM_USBSIM] = {AB5500_ADDR_SIM_USBSIM, "SIM_USBSIM"},
+	[AB5500_BANK_LED] = {AB5500_ADDR_LED, "LED"},
+	[AB5500_BANK_ADC] = {AB5500_ADDR_ADC, "ADC"},
+	[AB5500_BANK_RTC] = {AB5500_ADDR_RTC, "RTC"},
+	[AB5500_BANK_STARTUP] = {AB5500_ADDR_STARTUP, "STARTUP"},
+	[AB5500_BANK_DBI_ECI] = {AB5500_ADDR_DBI_ECI, "DBI-ECI"},
+	[AB5500_BANK_CHG] = {AB5500_ADDR_CHG, "CHG"},
+	[AB5500_BANK_FG_BATTCOM_ACC] = {
+		AB5500_ADDR_FG_BATTCOM_ACC, "FG_BATCOM_ACC"},
+	[AB5500_BANK_USB] = {AB5500_ADDR_USB, "USB"},
+	[AB5500_BANK_IT] = {AB5500_ADDR_IT, "IT"},
+	[AB5500_BANK_VIBRA] = {AB5500_ADDR_VIBRA, "VIBRA"},
+	[AB5500_BANK_AUDIO_HEADSETUSB] = {
+		AB5500_ADDR_AUDIO_HEADSETUSB, "AUDIO_HEADSETUSB"},
+};
+
+int ab5500_get_register_interruptible_raw(struct ab5500 *ab, u8 bank, u8 reg,
+	u8 *value);
+int ab5500_mask_and_set_register_interruptible_raw(struct ab5500 *ab, u8 bank,
+	u8 reg, u8 bitmask, u8 bitvalues);
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/ab5500-debugfs.c b/ap/os/linux/linux-3.4.x/drivers/mfd/ab5500-debugfs.c
new file mode 100644
index 0000000..7200694
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/ab5500-debugfs.c
@@ -0,0 +1,807 @@
+/*
+ * Copyright (C) 2011 ST-Ericsson
+ * License terms: GNU General Public License (GPL) version 2
+ * Debugfs support for the AB5500 MFD driver
+ */
+
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab5500.h>
+#include <linux/uaccess.h>
+
+#include "ab5500-core.h"
+#include "ab5500-debugfs.h"
+
+static struct ab5500_i2c_ranges ab5500_reg_ranges[AB5500_NUM_BANKS] = {
+	[AB5500_BANK_LED] = {
+		.bankid = AB5500_BANK_LED,
+		.nranges = 1,
+		.range = (struct ab5500_reg_range[]) {
+			{
+				.first = 0x00,
+				.last = 0x0C,
+				.perm = AB5500_PERM_RW,
+			},
+		},
+	},
+	[AB5500_BANK_ADC] = {
+		.bankid = AB5500_BANK_ADC,
+		.nranges = 6,
+		.range = (struct ab5500_reg_range[]) {
+			{
+				.first = 0x1F,
+				.last = 0x22,
+				.perm = AB5500_PERM_RO,
+			},
+			{
+				.first = 0x23,
+				.last = 0x24,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0x26,
+				.last = 0x2D,
+				.perm = AB5500_PERM_RO,
+			},
+			{
+				.first = 0x2F,
+				.last = 0x34,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0x37,
+				.last = 0x57,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0x58,
+				.last = 0x58,
+				.perm = AB5500_PERM_RO,
+			},
+		},
+	},
+	[AB5500_BANK_RTC] = {
+		.bankid = AB5500_BANK_RTC,
+		.nranges = 2,
+		.range = (struct ab5500_reg_range[]) {
+			{
+				.first = 0x00,
+				.last = 0x04,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0x06,
+				.last = 0x0C,
+				.perm = AB5500_PERM_RW,
+			},
+		},
+	},
+	[AB5500_BANK_STARTUP] = {
+		.bankid = AB5500_BANK_STARTUP,
+		.nranges = 12,
+		.range = (struct ab5500_reg_range[]) {
+			{
+				.first = 0x00,
+				.last = 0x01,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0x1F,
+				.last = 0x1F,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0x2E,
+				.last = 0x2E,
+				.perm = AB5500_PERM_RO,
+			},
+			{
+				.first = 0x2F,
+				.last = 0x30,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0x50,
+				.last = 0x51,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0x60,
+				.last = 0x61,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0x66,
+				.last = 0x8A,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0x8C,
+				.last = 0x96,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0xAA,
+				.last = 0xB4,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0xB7,
+				.last = 0xBF,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0xC1,
+				.last = 0xCA,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0xD3,
+				.last = 0xE0,
+				.perm = AB5500_PERM_RW,
+			},
+		},
+	},
+	[AB5500_BANK_DBI_ECI] = {
+		.bankid = AB5500_BANK_DBI_ECI,
+		.nranges = 3,
+		.range = (struct ab5500_reg_range[]) {
+			{
+				.first = 0x00,
+				.last = 0x07,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0x10,
+				.last = 0x10,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0x13,
+				.last = 0x13,
+				.perm = AB5500_PERM_RW,
+			},
+		},
+	},
+	[AB5500_BANK_CHG] = {
+		.bankid = AB5500_BANK_CHG,
+		.nranges = 2,
+		.range = (struct ab5500_reg_range[]) {
+			{
+				.first = 0x11,
+				.last = 0x11,
+				.perm = AB5500_PERM_RO,
+			},
+			{
+				.first = 0x12,
+				.last = 0x1B,
+				.perm = AB5500_PERM_RW,
+			},
+		},
+	},
+	[AB5500_BANK_FG_BATTCOM_ACC] = {
+		.bankid = AB5500_BANK_FG_BATTCOM_ACC,
+		.nranges = 2,
+		.range = (struct ab5500_reg_range[]) {
+			{
+				.first = 0x00,
+				.last = 0x0B,
+				.perm = AB5500_PERM_RO,
+			},
+			{
+				.first = 0x0C,
+				.last = 0x10,
+				.perm = AB5500_PERM_RW,
+			},
+		},
+	},
+	[AB5500_BANK_USB] = {
+		.bankid = AB5500_BANK_USB,
+		.nranges = 12,
+		.range = (struct ab5500_reg_range[]) {
+			{
+				.first = 0x01,
+				.last = 0x01,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0x80,
+				.last = 0x83,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0x87,
+				.last = 0x8A,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0x8B,
+				.last = 0x8B,
+				.perm = AB5500_PERM_RO,
+			},
+			{
+				.first = 0x91,
+				.last = 0x92,
+				.perm = AB5500_PERM_RO,
+			},
+			{
+				.first = 0x93,
+				.last = 0x93,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0x94,
+				.last = 0x94,
+				.perm = AB5500_PERM_RO,
+			},
+			{
+				.first = 0xA8,
+				.last = 0xB0,
+				.perm = AB5500_PERM_RO,
+			},
+			{
+				.first = 0xB2,
+				.last = 0xB2,
+				.perm = AB5500_PERM_RO,
+			},
+			{
+				.first = 0xB4,
+				.last = 0xBC,
+				.perm = AB5500_PERM_RO,
+			},
+			{
+				.first = 0xBF,
+				.last = 0xBF,
+				.perm = AB5500_PERM_RO,
+			},
+			{
+				.first = 0xC1,
+				.last = 0xC5,
+				.perm = AB5500_PERM_RO,
+			},
+		},
+	},
+	[AB5500_BANK_IT] = {
+		.bankid = AB5500_BANK_IT,
+		.nranges = 4,
+		.range = (struct ab5500_reg_range[]) {
+			{
+				.first = 0x00,
+				.last = 0x02,
+				.perm = AB5500_PERM_RO,
+			},
+			{
+				.first = 0x20,
+				.last = 0x36,
+				.perm = AB5500_PERM_RO,
+			},
+			{
+				.first = 0x40,
+				.last = 0x56,
+				.perm = AB5500_PERM_RO,
+			},
+			{
+				.first = 0x60,
+				.last = 0x76,
+				.perm = AB5500_PERM_RO,
+			},
+		},
+	},
+	[AB5500_BANK_VDDDIG_IO_I2C_CLK_TST] = {
+		.bankid = AB5500_BANK_VDDDIG_IO_I2C_CLK_TST,
+		.nranges = 7,
+		.range = (struct ab5500_reg_range[]) {
+			{
+				.first = 0x02,
+				.last = 0x02,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0x12,
+				.last = 0x12,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0x30,
+				.last = 0x34,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0x40,
+				.last = 0x44,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0x50,
+				.last = 0x54,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0x60,
+				.last = 0x64,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0x70,
+				.last = 0x74,
+				.perm = AB5500_PERM_RW,
+			},
+		},
+	},
+	[AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP] = {
+		.bankid = AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP,
+		.nranges = 13,
+		.range = (struct ab5500_reg_range[]) {
+			{
+				.first = 0x01,
+				.last = 0x01,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0x02,
+				.last = 0x02,
+				.perm = AB5500_PERM_RO,
+			},
+			{
+				.first = 0x0D,
+				.last = 0x0F,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0x1C,
+				.last = 0x1C,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0x1E,
+				.last = 0x1E,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0x20,
+				.last = 0x21,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0x25,
+				.last = 0x25,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0x28,
+				.last = 0x2A,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0x30,
+				.last = 0x33,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0x40,
+				.last = 0x43,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0x50,
+				.last = 0x53,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0x60,
+				.last = 0x63,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0x70,
+				.last = 0x73,
+				.perm = AB5500_PERM_RW,
+			},
+		},
+	},
+	[AB5500_BANK_VIBRA] = {
+		.bankid = AB5500_BANK_VIBRA,
+		.nranges = 2,
+		.range = (struct ab5500_reg_range[]) {
+			{
+				.first = 0x10,
+				.last = 0x13,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0xFE,
+				.last = 0xFE,
+				.perm = AB5500_PERM_RW,
+			},
+		},
+	},
+	[AB5500_BANK_AUDIO_HEADSETUSB] = {
+		.bankid = AB5500_BANK_AUDIO_HEADSETUSB,
+		.nranges = 2,
+		.range = (struct ab5500_reg_range[]) {
+			{
+				.first = 0x00,
+				.last = 0x48,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0xEB,
+				.last = 0xFB,
+				.perm = AB5500_PERM_RW,
+			},
+		},
+	},
+	[AB5500_BANK_SIM_USBSIM] = {
+		.bankid = AB5500_BANK_SIM_USBSIM,
+		.nranges = 1,
+		.range = (struct ab5500_reg_range[]) {
+			{
+				.first = 0x13,
+				.last = 0x19,
+				.perm = AB5500_PERM_RW,
+			},
+		},
+	},
+	[AB5500_BANK_VDENC] = {
+		.bankid = AB5500_BANK_VDENC,
+		.nranges = 12,
+		.range = (struct ab5500_reg_range[]) {
+			{
+				.first = 0x00,
+				.last = 0x08,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0x09,
+				.last = 0x09,
+				.perm = AB5500_PERM_RO,
+			},
+			{
+				.first = 0x0A,
+				.last = 0x12,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0x15,
+				.last = 0x19,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0x1B,
+				.last = 0x21,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0x27,
+				.last = 0x2C,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0x41,
+				.last = 0x41,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0x45,
+				.last = 0x5B,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0x5D,
+				.last = 0x5D,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0x69,
+				.last = 0x69,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0x6C,
+				.last = 0x6D,
+				.perm = AB5500_PERM_RW,
+			},
+			{
+				.first = 0x80,
+				.last = 0x81,
+				.perm = AB5500_PERM_RW,
+			},
+		},
+	},
+};
+
+static int ab5500_registers_print(struct seq_file *s, void *p)
+{
+	struct ab5500 *ab = s->private;
+	unsigned int i;
+	u8 bank = (u8)ab->debug_bank;
+
+	seq_printf(s, "ab5500 register values:\n");
+	for (bank = 0; bank < AB5500_NUM_BANKS; bank++) {
+		seq_printf(s, " bank %u, %s (0x%x):\n", bank,
+				bankinfo[bank].name,
+				bankinfo[bank].slave_addr);
+		for (i = 0; i < ab5500_reg_ranges[bank].nranges; i++) {
+			u8 reg;
+			int err;
+
+			for (reg = ab5500_reg_ranges[bank].range[i].first;
+				reg <= ab5500_reg_ranges[bank].range[i].last;
+				reg++) {
+				u8 value;
+
+				err = ab5500_get_register_interruptible_raw(ab,
+								bank, reg,
+								&value);
+				if (err < 0) {
+					dev_err(ab->dev, "get_reg failed %d"
+						"bank 0x%x reg 0x%x\n",
+						err, bank, reg);
+					return err;
+				}
+
+				err = seq_printf(s, "[%d/0x%02X]: 0x%02X\n",
+						bank, reg, value);
+				if (err < 0) {
+					dev_err(ab->dev,
+						"seq_printf overflow\n");
+					/*
+					 * Error is not returned here since
+					 * the output is wanted in any case
+					 */
+					return 0;
+				}
+			}
+		}
+	}
+	return 0;
+}
+
+static int ab5500_registers_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, ab5500_registers_print, inode->i_private);
+}
+
+static const struct file_operations ab5500_registers_fops = {
+	.open = ab5500_registers_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+	.owner = THIS_MODULE,
+};
+
+static int ab5500_bank_print(struct seq_file *s, void *p)
+{
+	struct ab5500 *ab = s->private;
+
+	seq_printf(s, "%d\n", ab->debug_bank);
+	return 0;
+}
+
+static int ab5500_bank_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, ab5500_bank_print, inode->i_private);
+}
+
+static ssize_t ab5500_bank_write(struct file *file,
+	const char __user *user_buf,
+	size_t count, loff_t *ppos)
+{
+	struct ab5500 *ab = ((struct seq_file *)(file->private_data))->private;
+	char buf[32];
+	int buf_size;
+	unsigned long user_bank;
+	int err;
+
+	/* Get userspace string and assure termination */
+	buf_size = min(count, (sizeof(buf) - 1));
+	if (copy_from_user(buf, user_buf, buf_size))
+		return -EFAULT;
+	buf[buf_size] = 0;
+
+	err = strict_strtoul(buf, 0, &user_bank);
+	if (err)
+		return -EINVAL;
+
+	if (user_bank >= AB5500_NUM_BANKS) {
+		dev_err(ab->dev,
+			"debugfs error input > number of banks\n");
+		return -EINVAL;
+	}
+
+	ab->debug_bank = user_bank;
+
+	return buf_size;
+}
+
+static int ab5500_address_print(struct seq_file *s, void *p)
+{
+	struct ab5500 *ab = s->private;
+
+	seq_printf(s, "0x%02X\n", ab->debug_address);
+	return 0;
+}
+
+static int ab5500_address_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, ab5500_address_print, inode->i_private);
+}
+
+static ssize_t ab5500_address_write(struct file *file,
+	const char __user *user_buf,
+	size_t count, loff_t *ppos)
+{
+	struct ab5500 *ab = ((struct seq_file *)(file->private_data))->private;
+	char buf[32];
+	int buf_size;
+	unsigned long user_address;
+	int err;
+
+	/* Get userspace string and assure termination */
+	buf_size = min(count, (sizeof(buf) - 1));
+	if (copy_from_user(buf, user_buf, buf_size))
+		return -EFAULT;
+	buf[buf_size] = 0;
+
+	err = strict_strtoul(buf, 0, &user_address);
+	if (err)
+		return -EINVAL;
+	if (user_address > 0xff) {
+		dev_err(ab->dev,
+			"debugfs error input > 0xff\n");
+		return -EINVAL;
+	}
+	ab->debug_address = user_address;
+	return buf_size;
+}
+
+static int ab5500_val_print(struct seq_file *s, void *p)
+{
+	struct ab5500 *ab = s->private;
+	int err;
+	u8 regvalue;
+
+	err = ab5500_get_register_interruptible_raw(ab, (u8)ab->debug_bank,
+		(u8)ab->debug_address, &regvalue);
+	if (err) {
+		dev_err(ab->dev, "get_reg failed %d, bank 0x%x"
+			", reg 0x%x\n", err, ab->debug_bank,
+			ab->debug_address);
+		return -EINVAL;
+	}
+	seq_printf(s, "0x%02X\n", regvalue);
+
+	return 0;
+}
+
+static int ab5500_val_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, ab5500_val_print, inode->i_private);
+}
+
+static ssize_t ab5500_val_write(struct file *file,
+	const char __user *user_buf,
+	size_t count, loff_t *ppos)
+{
+	struct ab5500 *ab = ((struct seq_file *)(file->private_data))->private;
+	char buf[32];
+	int buf_size;
+	unsigned long user_val;
+	int err;
+	u8 regvalue;
+
+	/* Get userspace string and assure termination */
+	buf_size = min(count, (sizeof(buf)-1));
+	if (copy_from_user(buf, user_buf, buf_size))
+		return -EFAULT;
+	buf[buf_size] = 0;
+
+	err = strict_strtoul(buf, 0, &user_val);
+	if (err)
+		return -EINVAL;
+	if (user_val > 0xff) {
+		dev_err(ab->dev,
+			"debugfs error input > 0xff\n");
+		return -EINVAL;
+	}
+	err = ab5500_mask_and_set_register_interruptible_raw(
+		ab, (u8)ab->debug_bank,
+		(u8)ab->debug_address, 0xFF, (u8)user_val);
+	if (err)
+		return -EINVAL;
+
+	ab5500_get_register_interruptible_raw(ab, (u8)ab->debug_bank,
+		(u8)ab->debug_address, &regvalue);
+	if (err)
+		return -EINVAL;
+
+	return buf_size;
+}
+
+static const struct file_operations ab5500_bank_fops = {
+	.open = ab5500_bank_open,
+	.write = ab5500_bank_write,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+	.owner = THIS_MODULE,
+};
+
+static const struct file_operations ab5500_address_fops = {
+	.open = ab5500_address_open,
+	.write = ab5500_address_write,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+	.owner = THIS_MODULE,
+};
+
+static const struct file_operations ab5500_val_fops = {
+	.open = ab5500_val_open,
+	.write = ab5500_val_write,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+	.owner = THIS_MODULE,
+};
+
+static struct dentry *ab5500_dir;
+static struct dentry *ab5500_reg_file;
+static struct dentry *ab5500_bank_file;
+static struct dentry *ab5500_address_file;
+static struct dentry *ab5500_val_file;
+
+void __init ab5500_setup_debugfs(struct ab5500 *ab)
+{
+	ab->debug_bank = AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP;
+	ab->debug_address = AB5500_CHIP_ID;
+
+	ab5500_dir = debugfs_create_dir("ab5500", NULL);
+	if (!ab5500_dir)
+		goto exit_no_debugfs;
+
+	ab5500_reg_file = debugfs_create_file("all-bank-registers",
+		S_IRUGO, ab5500_dir, ab, &ab5500_registers_fops);
+	if (!ab5500_reg_file)
+		goto exit_destroy_dir;
+
+	ab5500_bank_file = debugfs_create_file("register-bank",
+		(S_IRUGO | S_IWUGO), ab5500_dir, ab, &ab5500_bank_fops);
+	if (!ab5500_bank_file)
+		goto exit_destroy_reg;
+
+	ab5500_address_file = debugfs_create_file("register-address",
+		(S_IRUGO | S_IWUGO), ab5500_dir, ab, &ab5500_address_fops);
+	if (!ab5500_address_file)
+		goto exit_destroy_bank;
+
+	ab5500_val_file = debugfs_create_file("register-value",
+		(S_IRUGO | S_IWUGO), ab5500_dir, ab, &ab5500_val_fops);
+	if (!ab5500_val_file)
+		goto exit_destroy_address;
+
+	return;
+
+exit_destroy_address:
+	debugfs_remove(ab5500_address_file);
+exit_destroy_bank:
+	debugfs_remove(ab5500_bank_file);
+exit_destroy_reg:
+	debugfs_remove(ab5500_reg_file);
+exit_destroy_dir:
+	debugfs_remove(ab5500_dir);
+exit_no_debugfs:
+	dev_err(ab->dev, "failed to create debugfs entries.\n");
+	return;
+}
+
+void __exit ab5500_remove_debugfs(void)
+{
+	debugfs_remove(ab5500_val_file);
+	debugfs_remove(ab5500_address_file);
+	debugfs_remove(ab5500_bank_file);
+	debugfs_remove(ab5500_reg_file);
+	debugfs_remove(ab5500_dir);
+}
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/ab5500-debugfs.h b/ap/os/linux/linux-3.4.x/drivers/mfd/ab5500-debugfs.h
new file mode 100644
index 0000000..7330a9b
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/ab5500-debugfs.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2011 ST-Ericsson
+ * License terms: GNU General Public License (GPL) version 2
+ * Debugfs interface to the AB5500 core driver
+ */
+
+#ifdef CONFIG_DEBUG_FS
+
+void ab5500_setup_debugfs(struct ab5500 *ab);
+void ab5500_remove_debugfs(void);
+
+#else /* !CONFIG_DEBUG_FS */
+
+static inline void ab5500_setup_debugfs(struct ab5500 *ab)
+{
+}
+
+static inline void ab5500_remove_debugfs(void)
+{
+}
+
+#endif
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/ab8500-core.c b/ap/os/linux/linux-3.4.x/drivers/mfd/ab8500-core.c
new file mode 100644
index 0000000..1f08704
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/ab8500-core.c
@@ -0,0 +1,1177 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Srinidhi Kasagar <srinidhi.kasagar@stericsson.com>
+ * Author: Rabin Vincent <rabin.vincent@stericsson.com>
+ * Author: Mattias Wallin <mattias.wallin@stericsson.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab8500.h>
+#include <linux/regulator/ab8500.h>
+
+/*
+ * Interrupt register offsets
+ * Bank : 0x0E
+ */
+#define AB8500_IT_SOURCE1_REG		0x00
+#define AB8500_IT_SOURCE2_REG		0x01
+#define AB8500_IT_SOURCE3_REG		0x02
+#define AB8500_IT_SOURCE4_REG		0x03
+#define AB8500_IT_SOURCE5_REG		0x04
+#define AB8500_IT_SOURCE6_REG		0x05
+#define AB8500_IT_SOURCE7_REG		0x06
+#define AB8500_IT_SOURCE8_REG		0x07
+#define AB9540_IT_SOURCE13_REG		0x0C
+#define AB8500_IT_SOURCE19_REG		0x12
+#define AB8500_IT_SOURCE20_REG		0x13
+#define AB8500_IT_SOURCE21_REG		0x14
+#define AB8500_IT_SOURCE22_REG		0x15
+#define AB8500_IT_SOURCE23_REG		0x16
+#define AB8500_IT_SOURCE24_REG		0x17
+
+/*
+ * latch registers
+ */
+#define AB8500_IT_LATCH1_REG		0x20
+#define AB8500_IT_LATCH2_REG		0x21
+#define AB8500_IT_LATCH3_REG		0x22
+#define AB8500_IT_LATCH4_REG		0x23
+#define AB8500_IT_LATCH5_REG		0x24
+#define AB8500_IT_LATCH6_REG		0x25
+#define AB8500_IT_LATCH7_REG		0x26
+#define AB8500_IT_LATCH8_REG		0x27
+#define AB8500_IT_LATCH9_REG		0x28
+#define AB8500_IT_LATCH10_REG		0x29
+#define AB8500_IT_LATCH12_REG		0x2B
+#define AB9540_IT_LATCH13_REG		0x2C
+#define AB8500_IT_LATCH19_REG		0x32
+#define AB8500_IT_LATCH20_REG		0x33
+#define AB8500_IT_LATCH21_REG		0x34
+#define AB8500_IT_LATCH22_REG		0x35
+#define AB8500_IT_LATCH23_REG		0x36
+#define AB8500_IT_LATCH24_REG		0x37
+
+/*
+ * mask registers
+ */
+
+#define AB8500_IT_MASK1_REG		0x40
+#define AB8500_IT_MASK2_REG		0x41
+#define AB8500_IT_MASK3_REG		0x42
+#define AB8500_IT_MASK4_REG		0x43
+#define AB8500_IT_MASK5_REG		0x44
+#define AB8500_IT_MASK6_REG		0x45
+#define AB8500_IT_MASK7_REG		0x46
+#define AB8500_IT_MASK8_REG		0x47
+#define AB8500_IT_MASK9_REG		0x48
+#define AB8500_IT_MASK10_REG		0x49
+#define AB8500_IT_MASK11_REG		0x4A
+#define AB8500_IT_MASK12_REG		0x4B
+#define AB8500_IT_MASK13_REG		0x4C
+#define AB8500_IT_MASK14_REG		0x4D
+#define AB8500_IT_MASK15_REG		0x4E
+#define AB8500_IT_MASK16_REG		0x4F
+#define AB8500_IT_MASK17_REG		0x50
+#define AB8500_IT_MASK18_REG		0x51
+#define AB8500_IT_MASK19_REG		0x52
+#define AB8500_IT_MASK20_REG		0x53
+#define AB8500_IT_MASK21_REG		0x54
+#define AB8500_IT_MASK22_REG		0x55
+#define AB8500_IT_MASK23_REG		0x56
+#define AB8500_IT_MASK24_REG		0x57
+
+#define AB8500_REV_REG			0x80
+#define AB8500_IC_NAME_REG		0x82
+#define AB8500_SWITCH_OFF_STATUS	0x00
+
+#define AB8500_TURN_ON_STATUS		0x00
+
+#define AB9540_MODEM_CTRL2_REG			0x23
+#define AB9540_MODEM_CTRL2_SWDBBRSTN_BIT	BIT(2)
+
+/*
+ * Map interrupt numbers to the LATCH and MASK register offsets, Interrupt
+ * numbers are indexed into this array with (num / 8). The interupts are
+ * defined in linux/mfd/ab8500.h
+ *
+ * This is one off from the register names, i.e. AB8500_IT_MASK1_REG is at
+ * offset 0.
+ */
+/* AB8500 support */
+static const int ab8500_irq_regoffset[AB8500_NUM_IRQ_REGS] = {
+	0, 1, 2, 3, 4, 6, 7, 8, 9, 11, 18, 19, 20, 21,
+};
+
+/* AB9540 support */
+static const int ab9540_irq_regoffset[AB9540_NUM_IRQ_REGS] = {
+	0, 1, 2, 3, 4, 6, 7, 8, 9, 11, 18, 19, 20, 21, 12, 13, 24,
+};
+
+static const char ab8500_version_str[][7] = {
+	[AB8500_VERSION_AB8500] = "AB8500",
+	[AB8500_VERSION_AB8505] = "AB8505",
+	[AB8500_VERSION_AB9540] = "AB9540",
+	[AB8500_VERSION_AB8540] = "AB8540",
+};
+
+static int ab8500_get_chip_id(struct device *dev)
+{
+	struct ab8500 *ab8500;
+
+	if (!dev)
+		return -EINVAL;
+	ab8500 = dev_get_drvdata(dev->parent);
+	return ab8500 ? (int)ab8500->chip_id : -EINVAL;
+}
+
+static int set_register_interruptible(struct ab8500 *ab8500, u8 bank,
+	u8 reg, u8 data)
+{
+	int ret;
+	/*
+	 * Put the u8 bank and u8 register together into a an u16.
+	 * The bank on higher 8 bits and register in lower 8 bits.
+	 * */
+	u16 addr = ((u16)bank) << 8 | reg;
+
+	dev_vdbg(ab8500->dev, "wr: addr %#x <= %#x\n", addr, data);
+
+	mutex_lock(&ab8500->lock);
+
+	ret = ab8500->write(ab8500, addr, data);
+	if (ret < 0)
+		dev_err(ab8500->dev, "failed to write reg %#x: %d\n",
+			addr, ret);
+	mutex_unlock(&ab8500->lock);
+
+	return ret;
+}
+
+static int ab8500_set_register(struct device *dev, u8 bank,
+	u8 reg, u8 value)
+{
+	struct ab8500 *ab8500 = dev_get_drvdata(dev->parent);
+
+	return set_register_interruptible(ab8500, bank, reg, value);
+}
+
+static int get_register_interruptible(struct ab8500 *ab8500, u8 bank,
+	u8 reg, u8 *value)
+{
+	int ret;
+	/* put the u8 bank and u8 reg together into a an u16.
+	 * bank on higher 8 bits and reg in lower */
+	u16 addr = ((u16)bank) << 8 | reg;
+
+	mutex_lock(&ab8500->lock);
+
+	ret = ab8500->read(ab8500, addr);
+	if (ret < 0)
+		dev_err(ab8500->dev, "failed to read reg %#x: %d\n",
+			addr, ret);
+	else
+		*value = ret;
+
+	mutex_unlock(&ab8500->lock);
+	dev_vdbg(ab8500->dev, "rd: addr %#x => data %#x\n", addr, ret);
+
+	return ret;
+}
+
+static int ab8500_get_register(struct device *dev, u8 bank,
+	u8 reg, u8 *value)
+{
+	struct ab8500 *ab8500 = dev_get_drvdata(dev->parent);
+
+	return get_register_interruptible(ab8500, bank, reg, value);
+}
+
+static int mask_and_set_register_interruptible(struct ab8500 *ab8500, u8 bank,
+	u8 reg, u8 bitmask, u8 bitvalues)
+{
+	int ret;
+	/* put the u8 bank and u8 reg together into a an u16.
+	 * bank on higher 8 bits and reg in lower */
+	u16 addr = ((u16)bank) << 8 | reg;
+
+	mutex_lock(&ab8500->lock);
+
+	if (ab8500->write_masked == NULL) {
+		u8 data;
+
+		ret = ab8500->read(ab8500, addr);
+		if (ret < 0) {
+			dev_err(ab8500->dev, "failed to read reg %#x: %d\n",
+				addr, ret);
+			goto out;
+		}
+
+		data = (u8)ret;
+		data = (~bitmask & data) | (bitmask & bitvalues);
+
+		ret = ab8500->write(ab8500, addr, data);
+		if (ret < 0)
+			dev_err(ab8500->dev, "failed to write reg %#x: %d\n",
+				addr, ret);
+
+		dev_vdbg(ab8500->dev, "mask: addr %#x => data %#x\n", addr,
+			data);
+		goto out;
+	}
+	ret = ab8500->write_masked(ab8500, addr, bitmask, bitvalues);
+	if (ret < 0)
+		dev_err(ab8500->dev, "failed to modify reg %#x: %d\n", addr,
+			ret);
+out:
+	mutex_unlock(&ab8500->lock);
+	return ret;
+}
+
+static int ab8500_mask_and_set_register(struct device *dev,
+	u8 bank, u8 reg, u8 bitmask, u8 bitvalues)
+{
+	struct ab8500 *ab8500 = dev_get_drvdata(dev->parent);
+
+	return mask_and_set_register_interruptible(ab8500, bank, reg,
+		bitmask, bitvalues);
+
+}
+
+static struct abx500_ops ab8500_ops = {
+	.get_chip_id = ab8500_get_chip_id,
+	.get_register = ab8500_get_register,
+	.set_register = ab8500_set_register,
+	.get_register_page = NULL,
+	.set_register_page = NULL,
+	.mask_and_set_register = ab8500_mask_and_set_register,
+	.event_registers_startup_state_get = NULL,
+	.startup_irq_enabled = NULL,
+};
+
+static void ab8500_irq_lock(struct irq_data *data)
+{
+	struct ab8500 *ab8500 = irq_data_get_irq_chip_data(data);
+
+	mutex_lock(&ab8500->irq_lock);
+}
+
+static void ab8500_irq_sync_unlock(struct irq_data *data)
+{
+	struct ab8500 *ab8500 = irq_data_get_irq_chip_data(data);
+	int i;
+
+	for (i = 0; i < ab8500->mask_size; i++) {
+		u8 old = ab8500->oldmask[i];
+		u8 new = ab8500->mask[i];
+		int reg;
+
+		if (new == old)
+			continue;
+
+		/*
+		 * Interrupt register 12 doesn't exist prior to AB8500 version
+		 * 2.0
+		 */
+		if (ab8500->irq_reg_offset[i] == 11 &&
+			is_ab8500_1p1_or_earlier(ab8500))
+			continue;
+
+		ab8500->oldmask[i] = new;
+
+		reg = AB8500_IT_MASK1_REG + ab8500->irq_reg_offset[i];
+		set_register_interruptible(ab8500, AB8500_INTERRUPT, reg, new);
+	}
+
+	mutex_unlock(&ab8500->irq_lock);
+}
+
+static void ab8500_irq_mask(struct irq_data *data)
+{
+	struct ab8500 *ab8500 = irq_data_get_irq_chip_data(data);
+	int offset = data->irq - ab8500->irq_base;
+	int index = offset / 8;
+	int mask = 1 << (offset % 8);
+
+	ab8500->mask[index] |= mask;
+}
+
+static void ab8500_irq_unmask(struct irq_data *data)
+{
+	struct ab8500 *ab8500 = irq_data_get_irq_chip_data(data);
+	int offset = data->irq - ab8500->irq_base;
+	int index = offset / 8;
+	int mask = 1 << (offset % 8);
+
+	ab8500->mask[index] &= ~mask;
+}
+
+static struct irq_chip ab8500_irq_chip = {
+	.name			= "ab8500",
+	.irq_bus_lock		= ab8500_irq_lock,
+	.irq_bus_sync_unlock	= ab8500_irq_sync_unlock,
+	.irq_mask		= ab8500_irq_mask,
+	.irq_disable		= ab8500_irq_mask,
+	.irq_unmask		= ab8500_irq_unmask,
+};
+
+static irqreturn_t ab8500_irq(int irq, void *dev)
+{
+	struct ab8500 *ab8500 = dev;
+	int i;
+
+	dev_vdbg(ab8500->dev, "interrupt\n");
+
+	for (i = 0; i < ab8500->mask_size; i++) {
+		int regoffset = ab8500->irq_reg_offset[i];
+		int status;
+		u8 value;
+
+		/*
+		 * Interrupt register 12 doesn't exist prior to AB8500 version
+		 * 2.0
+		 */
+		if (regoffset == 11 && is_ab8500_1p1_or_earlier(ab8500))
+			continue;
+
+		status = get_register_interruptible(ab8500, AB8500_INTERRUPT,
+			AB8500_IT_LATCH1_REG + regoffset, &value);
+		if (status < 0 || value == 0)
+			continue;
+
+		do {
+			int bit = __ffs(value);
+			int line = i * 8 + bit;
+
+			handle_nested_irq(ab8500->irq_base + line);
+			value &= ~(1 << bit);
+		} while (value);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int ab8500_irq_init(struct ab8500 *ab8500)
+{
+	int base = ab8500->irq_base;
+	int irq;
+	int num_irqs;
+
+	if (is_ab9540(ab8500))
+		num_irqs = AB9540_NR_IRQS;
+	else if (is_ab8505(ab8500))
+		num_irqs = AB8505_NR_IRQS;
+	else
+		num_irqs = AB8500_NR_IRQS;
+
+	for (irq = base; irq < base + num_irqs; irq++) {
+		irq_set_chip_data(irq, ab8500);
+		irq_set_chip_and_handler(irq, &ab8500_irq_chip,
+					 handle_simple_irq);
+		irq_set_nested_thread(irq, 1);
+#ifdef CONFIG_ARM
+		set_irq_flags(irq, IRQF_VALID);
+#else
+		irq_set_noprobe(irq);
+#endif
+	}
+
+	return 0;
+}
+
+static void ab8500_irq_remove(struct ab8500 *ab8500)
+{
+	int base = ab8500->irq_base;
+	int irq;
+	int num_irqs;
+
+	if (is_ab9540(ab8500))
+		num_irqs = AB9540_NR_IRQS;
+	else if (is_ab8505(ab8500))
+		num_irqs = AB8505_NR_IRQS;
+	else
+		num_irqs = AB8500_NR_IRQS;
+
+	for (irq = base; irq < base + num_irqs; irq++) {
+#ifdef CONFIG_ARM
+		set_irq_flags(irq, 0);
+#endif
+		irq_set_chip_and_handler(irq, NULL, NULL);
+		irq_set_chip_data(irq, NULL);
+	}
+}
+
+/* AB8500 GPIO Resources */
+static struct resource __devinitdata ab8500_gpio_resources[] = {
+	{
+		.name	= "GPIO_INT6",
+		.start	= AB8500_INT_GPIO6R,
+		.end	= AB8500_INT_GPIO41F,
+		.flags	= IORESOURCE_IRQ,
+	}
+};
+
+/* AB9540 GPIO Resources */
+static struct resource __devinitdata ab9540_gpio_resources[] = {
+	{
+		.name	= "GPIO_INT6",
+		.start	= AB8500_INT_GPIO6R,
+		.end	= AB8500_INT_GPIO41F,
+		.flags	= IORESOURCE_IRQ,
+	},
+	{
+		.name	= "GPIO_INT14",
+		.start	= AB9540_INT_GPIO50R,
+		.end	= AB9540_INT_GPIO54R,
+		.flags	= IORESOURCE_IRQ,
+	},
+	{
+		.name	= "GPIO_INT15",
+		.start	= AB9540_INT_GPIO50F,
+		.end	= AB9540_INT_GPIO54F,
+		.flags	= IORESOURCE_IRQ,
+	}
+};
+
+static struct resource __devinitdata ab8500_gpadc_resources[] = {
+	{
+		.name	= "HW_CONV_END",
+		.start	= AB8500_INT_GP_HW_ADC_CONV_END,
+		.end	= AB8500_INT_GP_HW_ADC_CONV_END,
+		.flags	= IORESOURCE_IRQ,
+	},
+	{
+		.name	= "SW_CONV_END",
+		.start	= AB8500_INT_GP_SW_ADC_CONV_END,
+		.end	= AB8500_INT_GP_SW_ADC_CONV_END,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource __devinitdata ab8500_rtc_resources[] = {
+	{
+		.name	= "60S",
+		.start	= AB8500_INT_RTC_60S,
+		.end	= AB8500_INT_RTC_60S,
+		.flags	= IORESOURCE_IRQ,
+	},
+	{
+		.name	= "ALARM",
+		.start	= AB8500_INT_RTC_ALARM,
+		.end	= AB8500_INT_RTC_ALARM,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource __devinitdata ab8500_poweronkey_db_resources[] = {
+	{
+		.name	= "ONKEY_DBF",
+		.start	= AB8500_INT_PON_KEY1DB_F,
+		.end	= AB8500_INT_PON_KEY1DB_F,
+		.flags	= IORESOURCE_IRQ,
+	},
+	{
+		.name	= "ONKEY_DBR",
+		.start	= AB8500_INT_PON_KEY1DB_R,
+		.end	= AB8500_INT_PON_KEY1DB_R,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource __devinitdata ab8500_av_acc_detect_resources[] = {
+	{
+	       .name = "ACC_DETECT_1DB_F",
+	       .start = AB8500_INT_ACC_DETECT_1DB_F,
+	       .end = AB8500_INT_ACC_DETECT_1DB_F,
+	       .flags = IORESOURCE_IRQ,
+	},
+	{
+	       .name = "ACC_DETECT_1DB_R",
+	       .start = AB8500_INT_ACC_DETECT_1DB_R,
+	       .end = AB8500_INT_ACC_DETECT_1DB_R,
+	       .flags = IORESOURCE_IRQ,
+	},
+	{
+	       .name = "ACC_DETECT_21DB_F",
+	       .start = AB8500_INT_ACC_DETECT_21DB_F,
+	       .end = AB8500_INT_ACC_DETECT_21DB_F,
+	       .flags = IORESOURCE_IRQ,
+	},
+	{
+	       .name = "ACC_DETECT_21DB_R",
+	       .start = AB8500_INT_ACC_DETECT_21DB_R,
+	       .end = AB8500_INT_ACC_DETECT_21DB_R,
+	       .flags = IORESOURCE_IRQ,
+	},
+	{
+	       .name = "ACC_DETECT_22DB_F",
+	       .start = AB8500_INT_ACC_DETECT_22DB_F,
+	       .end = AB8500_INT_ACC_DETECT_22DB_F,
+	       .flags = IORESOURCE_IRQ,
+	},
+	{
+	       .name = "ACC_DETECT_22DB_R",
+	       .start = AB8500_INT_ACC_DETECT_22DB_R,
+	       .end = AB8500_INT_ACC_DETECT_22DB_R,
+	       .flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct resource __devinitdata ab8500_charger_resources[] = {
+	{
+		.name = "MAIN_CH_UNPLUG_DET",
+		.start = AB8500_INT_MAIN_CH_UNPLUG_DET,
+		.end = AB8500_INT_MAIN_CH_UNPLUG_DET,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.name = "MAIN_CHARGE_PLUG_DET",
+		.start = AB8500_INT_MAIN_CH_PLUG_DET,
+		.end = AB8500_INT_MAIN_CH_PLUG_DET,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.name = "VBUS_DET_R",
+		.start = AB8500_INT_VBUS_DET_R,
+		.end = AB8500_INT_VBUS_DET_R,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.name = "VBUS_DET_F",
+		.start = AB8500_INT_VBUS_DET_F,
+		.end = AB8500_INT_VBUS_DET_F,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.name = "USB_LINK_STATUS",
+		.start = AB8500_INT_USB_LINK_STATUS,
+		.end = AB8500_INT_USB_LINK_STATUS,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.name = "VBUS_OVV",
+		.start = AB8500_INT_VBUS_OVV,
+		.end = AB8500_INT_VBUS_OVV,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.name = "USB_CH_TH_PROT_R",
+		.start = AB8500_INT_USB_CH_TH_PROT_R,
+		.end = AB8500_INT_USB_CH_TH_PROT_R,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.name = "USB_CH_TH_PROT_F",
+		.start = AB8500_INT_USB_CH_TH_PROT_F,
+		.end = AB8500_INT_USB_CH_TH_PROT_F,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.name = "MAIN_EXT_CH_NOT_OK",
+		.start = AB8500_INT_MAIN_EXT_CH_NOT_OK,
+		.end = AB8500_INT_MAIN_EXT_CH_NOT_OK,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.name = "MAIN_CH_TH_PROT_R",
+		.start = AB8500_INT_MAIN_CH_TH_PROT_R,
+		.end = AB8500_INT_MAIN_CH_TH_PROT_R,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.name = "MAIN_CH_TH_PROT_F",
+		.start = AB8500_INT_MAIN_CH_TH_PROT_F,
+		.end = AB8500_INT_MAIN_CH_TH_PROT_F,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.name = "USB_CHARGER_NOT_OKR",
+		.start = AB8500_INT_USB_CHARGER_NOT_OKR,
+		.end = AB8500_INT_USB_CHARGER_NOT_OKR,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.name = "CH_WD_EXP",
+		.start = AB8500_INT_CH_WD_EXP,
+		.end = AB8500_INT_CH_WD_EXP,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct resource __devinitdata ab8500_btemp_resources[] = {
+	{
+		.name = "BAT_CTRL_INDB",
+		.start = AB8500_INT_BAT_CTRL_INDB,
+		.end = AB8500_INT_BAT_CTRL_INDB,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.name = "BTEMP_LOW",
+		.start = AB8500_INT_BTEMP_LOW,
+		.end = AB8500_INT_BTEMP_LOW,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.name = "BTEMP_HIGH",
+		.start = AB8500_INT_BTEMP_HIGH,
+		.end = AB8500_INT_BTEMP_HIGH,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.name = "BTEMP_LOW_MEDIUM",
+		.start = AB8500_INT_BTEMP_LOW_MEDIUM,
+		.end = AB8500_INT_BTEMP_LOW_MEDIUM,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.name = "BTEMP_MEDIUM_HIGH",
+		.start = AB8500_INT_BTEMP_MEDIUM_HIGH,
+		.end = AB8500_INT_BTEMP_MEDIUM_HIGH,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct resource __devinitdata ab8500_fg_resources[] = {
+	{
+		.name = "NCONV_ACCU",
+		.start = AB8500_INT_CCN_CONV_ACC,
+		.end = AB8500_INT_CCN_CONV_ACC,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.name = "BATT_OVV",
+		.start = AB8500_INT_BATT_OVV,
+		.end = AB8500_INT_BATT_OVV,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.name = "LOW_BAT_F",
+		.start = AB8500_INT_LOW_BAT_F,
+		.end = AB8500_INT_LOW_BAT_F,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.name = "LOW_BAT_R",
+		.start = AB8500_INT_LOW_BAT_R,
+		.end = AB8500_INT_LOW_BAT_R,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.name = "CC_INT_CALIB",
+		.start = AB8500_INT_CC_INT_CALIB,
+		.end = AB8500_INT_CC_INT_CALIB,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.name = "CCEOC",
+		.start = AB8500_INT_CCEOC,
+		.end = AB8500_INT_CCEOC,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct resource __devinitdata ab8500_chargalg_resources[] = {};
+
+#ifdef CONFIG_DEBUG_FS
+static struct resource __devinitdata ab8500_debug_resources[] = {
+	{
+		.name	= "IRQ_FIRST",
+		.start	= AB8500_INT_MAIN_EXT_CH_NOT_OK,
+		.end	= AB8500_INT_MAIN_EXT_CH_NOT_OK,
+		.flags	= IORESOURCE_IRQ,
+	},
+	{
+		.name	= "IRQ_LAST",
+		.start	= AB8500_INT_XTAL32K_KO,
+		.end	= AB8500_INT_XTAL32K_KO,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+#endif
+
+static struct resource __devinitdata ab8500_usb_resources[] = {
+	{
+		.name = "ID_WAKEUP_R",
+		.start = AB8500_INT_ID_WAKEUP_R,
+		.end = AB8500_INT_ID_WAKEUP_R,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.name = "ID_WAKEUP_F",
+		.start = AB8500_INT_ID_WAKEUP_F,
+		.end = AB8500_INT_ID_WAKEUP_F,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.name = "VBUS_DET_F",
+		.start = AB8500_INT_VBUS_DET_F,
+		.end = AB8500_INT_VBUS_DET_F,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.name = "VBUS_DET_R",
+		.start = AB8500_INT_VBUS_DET_R,
+		.end = AB8500_INT_VBUS_DET_R,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.name = "USB_LINK_STATUS",
+		.start = AB8500_INT_USB_LINK_STATUS,
+		.end = AB8500_INT_USB_LINK_STATUS,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.name = "USB_ADP_PROBE_PLUG",
+		.start = AB8500_INT_ADP_PROBE_PLUG,
+		.end = AB8500_INT_ADP_PROBE_PLUG,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.name = "USB_ADP_PROBE_UNPLUG",
+		.start = AB8500_INT_ADP_PROBE_UNPLUG,
+		.end = AB8500_INT_ADP_PROBE_UNPLUG,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct resource __devinitdata ab8500_temp_resources[] = {
+	{
+		.name  = "AB8500_TEMP_WARM",
+		.start = AB8500_INT_TEMP_WARM,
+		.end   = AB8500_INT_TEMP_WARM,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct mfd_cell __devinitdata abx500_common_devs[] = {
+#ifdef CONFIG_DEBUG_FS
+	{
+		.name = "ab8500-debug",
+		.num_resources = ARRAY_SIZE(ab8500_debug_resources),
+		.resources = ab8500_debug_resources,
+	},
+#endif
+	{
+		.name = "ab8500-sysctrl",
+	},
+	{
+		.name = "ab8500-regulator",
+	},
+	{
+		.name = "ab8500-gpadc",
+		.num_resources = ARRAY_SIZE(ab8500_gpadc_resources),
+		.resources = ab8500_gpadc_resources,
+	},
+	{
+		.name = "ab8500-rtc",
+		.num_resources = ARRAY_SIZE(ab8500_rtc_resources),
+		.resources = ab8500_rtc_resources,
+	},
+	{
+		.name = "ab8500-charger",
+		.num_resources = ARRAY_SIZE(ab8500_charger_resources),
+		.resources = ab8500_charger_resources,
+	},
+	{
+		.name = "ab8500-btemp",
+		.num_resources = ARRAY_SIZE(ab8500_btemp_resources),
+		.resources = ab8500_btemp_resources,
+	},
+	{
+		.name = "ab8500-fg",
+		.num_resources = ARRAY_SIZE(ab8500_fg_resources),
+		.resources = ab8500_fg_resources,
+	},
+	{
+		.name = "ab8500-chargalg",
+		.num_resources = ARRAY_SIZE(ab8500_chargalg_resources),
+		.resources = ab8500_chargalg_resources,
+	},
+	{
+		.name = "ab8500-acc-det",
+		.num_resources = ARRAY_SIZE(ab8500_av_acc_detect_resources),
+		.resources = ab8500_av_acc_detect_resources,
+	},
+	{
+		.name = "ab8500-codec",
+	},
+
+	{
+		.name = "ab8500-poweron-key",
+		.num_resources = ARRAY_SIZE(ab8500_poweronkey_db_resources),
+		.resources = ab8500_poweronkey_db_resources,
+	},
+	{
+		.name = "ab8500-pwm",
+		.id = 1,
+	},
+	{
+		.name = "ab8500-pwm",
+		.id = 2,
+	},
+	{
+		.name = "ab8500-pwm",
+		.id = 3,
+	},
+	{ .name = "ab8500-leds", },
+	{
+		.name = "ab8500-denc",
+	},
+	{
+		.name = "ab8500-temp",
+		.num_resources = ARRAY_SIZE(ab8500_temp_resources),
+		.resources = ab8500_temp_resources,
+	},
+};
+
+static struct mfd_cell __devinitdata ab8500_devs[] = {
+	{
+		.name = "ab8500-gpio",
+		.num_resources = ARRAY_SIZE(ab8500_gpio_resources),
+		.resources = ab8500_gpio_resources,
+	},
+	{
+		.name = "ab8500-usb",
+		.num_resources = ARRAY_SIZE(ab8500_usb_resources),
+		.resources = ab8500_usb_resources,
+	},
+};
+
+static struct mfd_cell __devinitdata ab9540_devs[] = {
+	{
+		.name = "ab8500-gpio",
+		.num_resources = ARRAY_SIZE(ab9540_gpio_resources),
+		.resources = ab9540_gpio_resources,
+	},
+	{
+		.name = "ab9540-usb",
+		.num_resources = ARRAY_SIZE(ab8500_usb_resources),
+		.resources = ab8500_usb_resources,
+	},
+};
+
+static ssize_t show_chip_id(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct ab8500 *ab8500;
+
+	ab8500 = dev_get_drvdata(dev);
+	return sprintf(buf, "%#x\n", ab8500 ? ab8500->chip_id : -EINVAL);
+}
+
+/*
+ * ab8500 has switched off due to (SWITCH_OFF_STATUS):
+ * 0x01 Swoff bit programming
+ * 0x02 Thermal protection activation
+ * 0x04 Vbat lower then BattOk falling threshold
+ * 0x08 Watchdog expired
+ * 0x10 Non presence of 32kHz clock
+ * 0x20 Battery level lower than power on reset threshold
+ * 0x40 Power on key 1 pressed longer than 10 seconds
+ * 0x80 DB8500 thermal shutdown
+ */
+static ssize_t show_switch_off_status(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	int ret;
+	u8 value;
+	struct ab8500 *ab8500;
+
+	ab8500 = dev_get_drvdata(dev);
+	ret = get_register_interruptible(ab8500, AB8500_RTC,
+		AB8500_SWITCH_OFF_STATUS, &value);
+	if (ret < 0)
+		return ret;
+	return sprintf(buf, "%#x\n", value);
+}
+
+/*
+ * ab8500 has turned on due to (TURN_ON_STATUS):
+ * 0x01 PORnVbat
+ * 0x02 PonKey1dbF
+ * 0x04 PonKey2dbF
+ * 0x08 RTCAlarm
+ * 0x10 MainChDet
+ * 0x20 VbusDet
+ * 0x40 UsbIDDetect
+ * 0x80 Reserved
+ */
+static ssize_t show_turn_on_status(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	int ret;
+	u8 value;
+	struct ab8500 *ab8500;
+
+	ab8500 = dev_get_drvdata(dev);
+	ret = get_register_interruptible(ab8500, AB8500_SYS_CTRL1_BLOCK,
+		AB8500_TURN_ON_STATUS, &value);
+	if (ret < 0)
+		return ret;
+	return sprintf(buf, "%#x\n", value);
+}
+
+static ssize_t show_ab9540_dbbrstn(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct ab8500 *ab8500;
+	int ret;
+	u8 value;
+
+	ab8500 = dev_get_drvdata(dev);
+
+	ret = get_register_interruptible(ab8500, AB8500_REGU_CTRL2,
+		AB9540_MODEM_CTRL2_REG, &value);
+	if (ret < 0)
+		return ret;
+
+	return sprintf(buf, "%d\n",
+			(value & AB9540_MODEM_CTRL2_SWDBBRSTN_BIT) ? 1 : 0);
+}
+
+static ssize_t store_ab9540_dbbrstn(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct ab8500 *ab8500;
+	int ret = count;
+	int err;
+	u8 bitvalues;
+
+	ab8500 = dev_get_drvdata(dev);
+
+	if (count > 0) {
+		switch (buf[0]) {
+		case '0':
+			bitvalues = 0;
+			break;
+		case '1':
+			bitvalues = AB9540_MODEM_CTRL2_SWDBBRSTN_BIT;
+			break;
+		default:
+			goto exit;
+		}
+
+		err = mask_and_set_register_interruptible(ab8500,
+			AB8500_REGU_CTRL2, AB9540_MODEM_CTRL2_REG,
+			AB9540_MODEM_CTRL2_SWDBBRSTN_BIT, bitvalues);
+		if (err)
+			dev_info(ab8500->dev,
+				"Failed to set DBBRSTN %c, err %#x\n",
+				buf[0], err);
+	}
+
+exit:
+	return ret;
+}
+
+static DEVICE_ATTR(chip_id, S_IRUGO, show_chip_id, NULL);
+static DEVICE_ATTR(switch_off_status, S_IRUGO, show_switch_off_status, NULL);
+static DEVICE_ATTR(turn_on_status, S_IRUGO, show_turn_on_status, NULL);
+static DEVICE_ATTR(dbbrstn, S_IRUGO | S_IWUSR,
+			show_ab9540_dbbrstn, store_ab9540_dbbrstn);
+
+static struct attribute *ab8500_sysfs_entries[] = {
+	&dev_attr_chip_id.attr,
+	&dev_attr_switch_off_status.attr,
+	&dev_attr_turn_on_status.attr,
+	NULL,
+};
+
+static struct attribute *ab9540_sysfs_entries[] = {
+	&dev_attr_chip_id.attr,
+	&dev_attr_switch_off_status.attr,
+	&dev_attr_turn_on_status.attr,
+	&dev_attr_dbbrstn.attr,
+	NULL,
+};
+
+static struct attribute_group ab8500_attr_group = {
+	.attrs	= ab8500_sysfs_entries,
+};
+
+static struct attribute_group ab9540_attr_group = {
+	.attrs	= ab9540_sysfs_entries,
+};
+
+int __devinit ab8500_init(struct ab8500 *ab8500, enum ab8500_version version)
+{
+	struct ab8500_platform_data *plat = dev_get_platdata(ab8500->dev);
+	int ret;
+	int i;
+	u8 value;
+
+	if (plat)
+		ab8500->irq_base = plat->irq_base;
+
+	mutex_init(&ab8500->lock);
+	mutex_init(&ab8500->irq_lock);
+
+	if (version != AB8500_VERSION_UNDEFINED)
+		ab8500->version = version;
+	else {
+		ret = get_register_interruptible(ab8500, AB8500_MISC,
+			AB8500_IC_NAME_REG, &value);
+		if (ret < 0)
+			return ret;
+
+		ab8500->version = value;
+	}
+
+	ret = get_register_interruptible(ab8500, AB8500_MISC,
+		AB8500_REV_REG, &value);
+	if (ret < 0)
+		return ret;
+
+	ab8500->chip_id = value;
+
+	dev_info(ab8500->dev, "detected chip, %s rev. %1x.%1x\n",
+			ab8500_version_str[ab8500->version],
+			ab8500->chip_id >> 4,
+			ab8500->chip_id & 0x0F);
+
+	/* Configure AB8500 or AB9540 IRQ */
+	if (is_ab9540(ab8500) || is_ab8505(ab8500)) {
+		ab8500->mask_size = AB9540_NUM_IRQ_REGS;
+		ab8500->irq_reg_offset = ab9540_irq_regoffset;
+	} else {
+		ab8500->mask_size = AB8500_NUM_IRQ_REGS;
+		ab8500->irq_reg_offset = ab8500_irq_regoffset;
+	}
+	ab8500->mask = kzalloc(ab8500->mask_size, GFP_KERNEL);
+	if (!ab8500->mask)
+		return -ENOMEM;
+	ab8500->oldmask = kzalloc(ab8500->mask_size, GFP_KERNEL);
+	if (!ab8500->oldmask) {
+		ret = -ENOMEM;
+		goto out_freemask;
+	}
+	/*
+	 * ab8500 has switched off due to (SWITCH_OFF_STATUS):
+	 * 0x01 Swoff bit programming
+	 * 0x02 Thermal protection activation
+	 * 0x04 Vbat lower then BattOk falling threshold
+	 * 0x08 Watchdog expired
+	 * 0x10 Non presence of 32kHz clock
+	 * 0x20 Battery level lower than power on reset threshold
+	 * 0x40 Power on key 1 pressed longer than 10 seconds
+	 * 0x80 DB8500 thermal shutdown
+	 */
+
+	ret = get_register_interruptible(ab8500, AB8500_RTC,
+		AB8500_SWITCH_OFF_STATUS, &value);
+	if (ret < 0)
+		return ret;
+	dev_info(ab8500->dev, "switch off status: %#x", value);
+
+	if (plat && plat->init)
+		plat->init(ab8500);
+
+	/* Clear and mask all interrupts */
+	for (i = 0; i < ab8500->mask_size; i++) {
+		/*
+		 * Interrupt register 12 doesn't exist prior to AB8500 version
+		 * 2.0
+		 */
+		if (ab8500->irq_reg_offset[i] == 11 &&
+				is_ab8500_1p1_or_earlier(ab8500))
+			continue;
+
+		get_register_interruptible(ab8500, AB8500_INTERRUPT,
+			AB8500_IT_LATCH1_REG + ab8500->irq_reg_offset[i],
+			&value);
+		set_register_interruptible(ab8500, AB8500_INTERRUPT,
+			AB8500_IT_MASK1_REG + ab8500->irq_reg_offset[i], 0xff);
+	}
+
+	ret = abx500_register_ops(ab8500->dev, &ab8500_ops);
+	if (ret)
+		goto out_freeoldmask;
+
+	for (i = 0; i < ab8500->mask_size; i++)
+		ab8500->mask[i] = ab8500->oldmask[i] = 0xff;
+
+	if (ab8500->irq_base) {
+		ret = ab8500_irq_init(ab8500);
+		if (ret)
+			goto out_freeoldmask;
+
+		ret = request_threaded_irq(ab8500->irq, NULL, ab8500_irq,
+					   IRQF_ONESHOT | IRQF_NO_SUSPEND,
+					   "ab8500", ab8500);
+		if (ret)
+			goto out_removeirq;
+	}
+
+	ret = mfd_add_devices(ab8500->dev, 0, abx500_common_devs,
+			      ARRAY_SIZE(abx500_common_devs), NULL,
+			      ab8500->irq_base);
+
+	if (ret)
+		goto out_freeirq;
+
+	if (is_ab9540(ab8500))
+		ret = mfd_add_devices(ab8500->dev, 0, ab9540_devs,
+			      ARRAY_SIZE(ab9540_devs), NULL,
+			      ab8500->irq_base);
+	else
+		ret = mfd_add_devices(ab8500->dev, 0, ab8500_devs,
+			      ARRAY_SIZE(ab9540_devs), NULL,
+			      ab8500->irq_base);
+	if (ret)
+		goto out_freeirq;
+
+	if (is_ab9540(ab8500))
+		ret = sysfs_create_group(&ab8500->dev->kobj,
+					&ab9540_attr_group);
+	else
+		ret = sysfs_create_group(&ab8500->dev->kobj,
+					&ab8500_attr_group);
+	if (ret)
+		dev_err(ab8500->dev, "error creating sysfs entries\n");
+	else
+		return ret;
+
+out_freeirq:
+	if (ab8500->irq_base)
+		free_irq(ab8500->irq, ab8500);
+out_removeirq:
+	if (ab8500->irq_base)
+		ab8500_irq_remove(ab8500);
+out_freeoldmask:
+	kfree(ab8500->oldmask);
+out_freemask:
+	kfree(ab8500->mask);
+
+	return ret;
+}
+
+int __devexit ab8500_exit(struct ab8500 *ab8500)
+{
+	if (is_ab9540(ab8500))
+		sysfs_remove_group(&ab8500->dev->kobj, &ab9540_attr_group);
+	else
+		sysfs_remove_group(&ab8500->dev->kobj, &ab8500_attr_group);
+	mfd_remove_devices(ab8500->dev);
+	if (ab8500->irq_base) {
+		free_irq(ab8500->irq, ab8500);
+		ab8500_irq_remove(ab8500);
+	}
+	kfree(ab8500->oldmask);
+	kfree(ab8500->mask);
+
+	return 0;
+}
+
+MODULE_AUTHOR("Mattias Wallin, Srinidhi Kasagar, Rabin Vincent");
+MODULE_DESCRIPTION("AB8500 MFD core");
+MODULE_LICENSE("GPL v2");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/ab8500-debugfs.c b/ap/os/linux/linux-3.4.x/drivers/mfd/ab8500-debugfs.c
new file mode 100644
index 0000000..9a0211a
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/ab8500-debugfs.c
@@ -0,0 +1,634 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Mattias Wallin <mattias.wallin@stericsson.com> for ST-Ericsson.
+ * License Terms: GNU General Public License v2
+ */
+
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/platform_device.h>
+
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab8500.h>
+
+static u32 debug_bank;
+static u32 debug_address;
+
+/**
+ * struct ab8500_reg_range
+ * @first: the first address of the range
+ * @last: the last address of the range
+ * @perm: access permissions for the range
+ */
+struct ab8500_reg_range {
+	u8 first;
+	u8 last;
+	u8 perm;
+};
+
+/**
+ * struct ab8500_i2c_ranges
+ * @num_ranges: the number of ranges in the list
+ * @bankid: bank identifier
+ * @range: the list of register ranges
+ */
+struct ab8500_i2c_ranges {
+	u8 num_ranges;
+	u8 bankid;
+	const struct ab8500_reg_range *range;
+};
+
+#define AB8500_NAME_STRING "ab8500"
+#define AB8500_NUM_BANKS 22
+
+#define AB8500_REV_REG 0x80
+
+static struct ab8500_i2c_ranges debug_ranges[AB8500_NUM_BANKS] = {
+	[0x0] = {
+		.num_ranges = 0,
+		.range = 0,
+	},
+	[AB8500_SYS_CTRL1_BLOCK] = {
+		.num_ranges = 3,
+		.range = (struct ab8500_reg_range[]) {
+			{
+				.first = 0x00,
+				.last = 0x02,
+			},
+			{
+				.first = 0x42,
+				.last = 0x42,
+			},
+			{
+				.first = 0x80,
+				.last = 0x81,
+			},
+		},
+	},
+	[AB8500_SYS_CTRL2_BLOCK] = {
+		.num_ranges = 4,
+		.range = (struct ab8500_reg_range[]) {
+			{
+				.first = 0x00,
+				.last = 0x0D,
+			},
+			{
+				.first = 0x0F,
+				.last = 0x17,
+			},
+			{
+				.first = 0x30,
+				.last = 0x30,
+			},
+			{
+				.first = 0x32,
+				.last = 0x33,
+			},
+		},
+	},
+	[AB8500_REGU_CTRL1] = {
+		.num_ranges = 3,
+		.range = (struct ab8500_reg_range[]) {
+			{
+				.first = 0x00,
+				.last = 0x00,
+			},
+			{
+				.first = 0x03,
+				.last = 0x10,
+			},
+			{
+				.first = 0x80,
+				.last = 0x84,
+			},
+		},
+	},
+	[AB8500_REGU_CTRL2] = {
+		.num_ranges = 5,
+		.range = (struct ab8500_reg_range[]) {
+			{
+				.first = 0x00,
+				.last = 0x15,
+			},
+			{
+				.first = 0x17,
+				.last = 0x19,
+			},
+			{
+				.first = 0x1B,
+				.last = 0x1D,
+			},
+			{
+				.first = 0x1F,
+				.last = 0x22,
+			},
+			{
+				.first = 0x40,
+				.last = 0x44,
+			},
+			/* 0x80-0x8B is SIM registers and should
+			 * not be accessed from here */
+		},
+	},
+	[AB8500_USB] = {
+		.num_ranges = 2,
+		.range = (struct ab8500_reg_range[]) {
+			{
+				.first = 0x80,
+				.last = 0x83,
+			},
+			{
+				.first = 0x87,
+				.last = 0x8A,
+			},
+		},
+	},
+	[AB8500_TVOUT] = {
+		.num_ranges = 9,
+		.range = (struct ab8500_reg_range[]) {
+			{
+				.first = 0x00,
+				.last = 0x12,
+			},
+			{
+				.first = 0x15,
+				.last = 0x17,
+			},
+			{
+				.first = 0x19,
+				.last = 0x21,
+			},
+			{
+				.first = 0x27,
+				.last = 0x2C,
+			},
+			{
+				.first = 0x41,
+				.last = 0x41,
+			},
+			{
+				.first = 0x45,
+				.last = 0x5B,
+			},
+			{
+				.first = 0x5D,
+				.last = 0x5D,
+			},
+			{
+				.first = 0x69,
+				.last = 0x69,
+			},
+			{
+				.first = 0x80,
+				.last = 0x81,
+			},
+		},
+	},
+	[AB8500_DBI] = {
+		.num_ranges = 0,
+		.range = NULL,
+	},
+	[AB8500_ECI_AV_ACC] = {
+		.num_ranges = 1,
+		.range = (struct ab8500_reg_range[]) {
+			{
+				.first = 0x80,
+				.last = 0x82,
+			},
+		},
+	},
+	[0x9] = {
+		.num_ranges = 0,
+		.range = NULL,
+	},
+	[AB8500_GPADC] = {
+		.num_ranges = 1,
+		.range = (struct ab8500_reg_range[]) {
+			{
+				.first = 0x00,
+				.last = 0x08,
+			},
+		},
+	},
+	[AB8500_CHARGER] = {
+		.num_ranges = 8,
+		.range = (struct ab8500_reg_range[]) {
+			{
+				.first = 0x00,
+				.last = 0x03,
+			},
+			{
+				.first = 0x05,
+				.last = 0x05,
+			},
+			{
+				.first = 0x40,
+				.last = 0x40,
+			},
+			{
+				.first = 0x42,
+				.last = 0x42,
+			},
+			{
+				.first = 0x44,
+				.last = 0x44,
+			},
+			{
+				.first = 0x50,
+				.last = 0x55,
+			},
+			{
+				.first = 0x80,
+				.last = 0x82,
+			},
+			{
+				.first = 0xC0,
+				.last = 0xC2,
+			},
+		},
+	},
+	[AB8500_GAS_GAUGE] = {
+		.num_ranges = 3,
+		.range = (struct ab8500_reg_range[]) {
+			{
+				.first = 0x00,
+				.last = 0x00,
+			},
+			{
+				.first = 0x07,
+				.last = 0x0A,
+			},
+			{
+				.first = 0x10,
+				.last = 0x14,
+			},
+		},
+	},
+	[AB8500_AUDIO] = {
+		.num_ranges = 1,
+		.range = (struct ab8500_reg_range[]) {
+			{
+				.first = 0x00,
+				.last = 0x6F,
+			},
+		},
+	},
+	[AB8500_INTERRUPT] = {
+		.num_ranges = 0,
+		.range = NULL,
+	},
+	[AB8500_RTC] = {
+		.num_ranges = 1,
+		.range = (struct ab8500_reg_range[]) {
+			{
+				.first = 0x00,
+				.last = 0x0F,
+			},
+		},
+	},
+	[AB8500_MISC] = {
+		.num_ranges = 8,
+		.range = (struct ab8500_reg_range[]) {
+			{
+				.first = 0x00,
+				.last = 0x05,
+			},
+			{
+				.first = 0x10,
+				.last = 0x15,
+			},
+			{
+				.first = 0x20,
+				.last = 0x25,
+			},
+			{
+				.first = 0x30,
+				.last = 0x35,
+			},
+			{
+				.first = 0x40,
+				.last = 0x45,
+			},
+			{
+				.first = 0x50,
+				.last = 0x50,
+			},
+			{
+				.first = 0x60,
+				.last = 0x67,
+			},
+			{
+				.first = 0x80,
+				.last = 0x80,
+			},
+		},
+	},
+	[0x11] = {
+		.num_ranges = 0,
+		.range = NULL,
+	},
+	[0x12] = {
+		.num_ranges = 0,
+		.range = NULL,
+	},
+	[0x13] = {
+		.num_ranges = 0,
+		.range = NULL,
+	},
+	[0x14] = {
+		.num_ranges = 0,
+		.range = NULL,
+	},
+	[AB8500_OTP_EMUL] = {
+		.num_ranges = 1,
+		.range = (struct ab8500_reg_range[]) {
+			{
+				.first = 0x01,
+				.last = 0x0F,
+			},
+		},
+	},
+};
+
+static int ab8500_registers_print(struct seq_file *s, void *p)
+{
+	struct device *dev = s->private;
+	unsigned int i;
+	u32 bank = debug_bank;
+
+	seq_printf(s, AB8500_NAME_STRING " register values:\n");
+
+	seq_printf(s, " bank %u:\n", bank);
+	for (i = 0; i < debug_ranges[bank].num_ranges; i++) {
+		u32 reg;
+
+		for (reg = debug_ranges[bank].range[i].first;
+			reg <= debug_ranges[bank].range[i].last;
+			reg++) {
+			u8 value;
+			int err;
+
+			err = abx500_get_register_interruptible(dev,
+				(u8)bank, (u8)reg, &value);
+			if (err < 0) {
+				dev_err(dev, "ab->read fail %d\n", err);
+				return err;
+			}
+
+			err = seq_printf(s, "  [%u/0x%02X]: 0x%02X\n", bank,
+				reg, value);
+			if (err < 0) {
+				dev_err(dev, "seq_printf overflow\n");
+				/* Error is not returned here since
+				 * the output is wanted in any case */
+				return 0;
+			}
+		}
+	}
+	return 0;
+}
+
+static int ab8500_registers_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, ab8500_registers_print, inode->i_private);
+}
+
+static const struct file_operations ab8500_registers_fops = {
+	.open = ab8500_registers_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+	.owner = THIS_MODULE,
+};
+
+static int ab8500_bank_print(struct seq_file *s, void *p)
+{
+	return seq_printf(s, "%d\n", debug_bank);
+}
+
+static int ab8500_bank_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, ab8500_bank_print, inode->i_private);
+}
+
+static ssize_t ab8500_bank_write(struct file *file,
+	const char __user *user_buf,
+	size_t count, loff_t *ppos)
+{
+	struct device *dev = ((struct seq_file *)(file->private_data))->private;
+	unsigned long user_bank;
+	int err;
+
+	/* Get userspace string and assure termination */
+	err = kstrtoul_from_user(user_buf, count, 0, &user_bank);
+	if (err)
+		return err;
+
+	if (user_bank >= AB8500_NUM_BANKS) {
+		dev_err(dev, "debugfs error input > number of banks\n");
+		return -EINVAL;
+	}
+
+	debug_bank = user_bank;
+
+	return count;
+}
+
+static int ab8500_address_print(struct seq_file *s, void *p)
+{
+	return seq_printf(s, "0x%02X\n", debug_address);
+}
+
+static int ab8500_address_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, ab8500_address_print, inode->i_private);
+}
+
+static ssize_t ab8500_address_write(struct file *file,
+	const char __user *user_buf,
+	size_t count, loff_t *ppos)
+{
+	struct device *dev = ((struct seq_file *)(file->private_data))->private;
+	unsigned long user_address;
+	int err;
+
+	/* Get userspace string and assure termination */
+	err = kstrtoul_from_user(user_buf, count, 0, &user_address);
+	if (err)
+		return err;
+
+	if (user_address > 0xff) {
+		dev_err(dev, "debugfs error input > 0xff\n");
+		return -EINVAL;
+	}
+	debug_address = user_address;
+	return count;
+}
+
+static int ab8500_val_print(struct seq_file *s, void *p)
+{
+	struct device *dev = s->private;
+	int ret;
+	u8 regvalue;
+
+	ret = abx500_get_register_interruptible(dev,
+		(u8)debug_bank, (u8)debug_address, &regvalue);
+	if (ret < 0) {
+		dev_err(dev, "abx500_get_reg fail %d, %d\n",
+			ret, __LINE__);
+		return -EINVAL;
+	}
+	seq_printf(s, "0x%02X\n", regvalue);
+
+	return 0;
+}
+
+static int ab8500_val_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, ab8500_val_print, inode->i_private);
+}
+
+static ssize_t ab8500_val_write(struct file *file,
+	const char __user *user_buf,
+	size_t count, loff_t *ppos)
+{
+	struct device *dev = ((struct seq_file *)(file->private_data))->private;
+	unsigned long user_val;
+	int err;
+
+	/* Get userspace string and assure termination */
+	err = kstrtoul_from_user(user_buf, count, 0, &user_val);
+	if (err)
+		return err;
+
+	if (user_val > 0xff) {
+		dev_err(dev, "debugfs error input > 0xff\n");
+		return -EINVAL;
+	}
+	err = abx500_set_register_interruptible(dev,
+		(u8)debug_bank, debug_address, (u8)user_val);
+	if (err < 0) {
+		printk(KERN_ERR "abx500_set_reg failed %d, %d", err, __LINE__);
+		return -EINVAL;
+	}
+
+	return count;
+}
+
+static const struct file_operations ab8500_bank_fops = {
+	.open = ab8500_bank_open,
+	.write = ab8500_bank_write,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+	.owner = THIS_MODULE,
+};
+
+static const struct file_operations ab8500_address_fops = {
+	.open = ab8500_address_open,
+	.write = ab8500_address_write,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+	.owner = THIS_MODULE,
+};
+
+static const struct file_operations ab8500_val_fops = {
+	.open = ab8500_val_open,
+	.write = ab8500_val_write,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+	.owner = THIS_MODULE,
+};
+
+static struct dentry *ab8500_dir;
+static struct dentry *ab8500_reg_file;
+static struct dentry *ab8500_bank_file;
+static struct dentry *ab8500_address_file;
+static struct dentry *ab8500_val_file;
+
+static int __devinit ab8500_debug_probe(struct platform_device *plf)
+{
+	debug_bank = AB8500_MISC;
+	debug_address = AB8500_REV_REG & 0x00FF;
+
+	ab8500_dir = debugfs_create_dir(AB8500_NAME_STRING, NULL);
+	if (!ab8500_dir)
+		goto exit_no_debugfs;
+
+	ab8500_reg_file = debugfs_create_file("all-bank-registers",
+		S_IRUGO, ab8500_dir, &plf->dev, &ab8500_registers_fops);
+	if (!ab8500_reg_file)
+		goto exit_destroy_dir;
+
+	ab8500_bank_file = debugfs_create_file("register-bank",
+		(S_IRUGO | S_IWUSR), ab8500_dir, &plf->dev, &ab8500_bank_fops);
+	if (!ab8500_bank_file)
+		goto exit_destroy_reg;
+
+	ab8500_address_file = debugfs_create_file("register-address",
+		(S_IRUGO | S_IWUSR), ab8500_dir, &plf->dev,
+		&ab8500_address_fops);
+	if (!ab8500_address_file)
+		goto exit_destroy_bank;
+
+	ab8500_val_file = debugfs_create_file("register-value",
+		(S_IRUGO | S_IWUSR), ab8500_dir, &plf->dev, &ab8500_val_fops);
+	if (!ab8500_val_file)
+		goto exit_destroy_address;
+
+	return 0;
+
+exit_destroy_address:
+	debugfs_remove(ab8500_address_file);
+exit_destroy_bank:
+	debugfs_remove(ab8500_bank_file);
+exit_destroy_reg:
+	debugfs_remove(ab8500_reg_file);
+exit_destroy_dir:
+	debugfs_remove(ab8500_dir);
+exit_no_debugfs:
+	dev_err(&plf->dev, "failed to create debugfs entries.\n");
+	return -ENOMEM;
+}
+
+static int __devexit ab8500_debug_remove(struct platform_device *plf)
+{
+	debugfs_remove(ab8500_val_file);
+	debugfs_remove(ab8500_address_file);
+	debugfs_remove(ab8500_bank_file);
+	debugfs_remove(ab8500_reg_file);
+	debugfs_remove(ab8500_dir);
+
+	return 0;
+}
+
+static struct platform_driver ab8500_debug_driver = {
+	.driver = {
+		.name = "ab8500-debug",
+		.owner = THIS_MODULE,
+	},
+	.probe  = ab8500_debug_probe,
+	.remove = __devexit_p(ab8500_debug_remove)
+};
+
+static int __init ab8500_debug_init(void)
+{
+	return platform_driver_register(&ab8500_debug_driver);
+}
+
+static void __exit ab8500_debug_exit(void)
+{
+	platform_driver_unregister(&ab8500_debug_driver);
+}
+subsys_initcall(ab8500_debug_init);
+module_exit(ab8500_debug_exit);
+
+MODULE_AUTHOR("Mattias WALLIN <mattias.wallin@stericsson.com");
+MODULE_DESCRIPTION("AB8500 DEBUG");
+MODULE_LICENSE("GPL v2");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/ab8500-gpadc.c b/ap/os/linux/linux-3.4.x/drivers/mfd/ab8500-gpadc.c
new file mode 100644
index 0000000..c39fc71
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/ab8500-gpadc.c
@@ -0,0 +1,676 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Arun R Murthy <arun.murthy@stericsson.com>
+ * Author: Daniel Willerud <daniel.willerud@stericsson.com>
+ * Author: Johan Palsson <johan.palsson@stericsson.com>
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/completion.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab8500.h>
+#include <linux/mfd/abx500/ab8500-gpadc.h>
+
+/*
+ * GPADC register offsets
+ * Bank : 0x0A
+ */
+#define AB8500_GPADC_CTRL1_REG		0x00
+#define AB8500_GPADC_CTRL2_REG		0x01
+#define AB8500_GPADC_CTRL3_REG		0x02
+#define AB8500_GPADC_AUTO_TIMER_REG	0x03
+#define AB8500_GPADC_STAT_REG		0x04
+#define AB8500_GPADC_MANDATAL_REG	0x05
+#define AB8500_GPADC_MANDATAH_REG	0x06
+#define AB8500_GPADC_AUTODATAL_REG	0x07
+#define AB8500_GPADC_AUTODATAH_REG	0x08
+#define AB8500_GPADC_MUX_CTRL_REG	0x09
+
+/*
+ * OTP register offsets
+ * Bank : 0x15
+ */
+#define AB8500_GPADC_CAL_1		0x0F
+#define AB8500_GPADC_CAL_2		0x10
+#define AB8500_GPADC_CAL_3		0x11
+#define AB8500_GPADC_CAL_4		0x12
+#define AB8500_GPADC_CAL_5		0x13
+#define AB8500_GPADC_CAL_6		0x14
+#define AB8500_GPADC_CAL_7		0x15
+
+/* gpadc constants */
+#define EN_VINTCORE12			0x04
+#define EN_VTVOUT			0x02
+#define EN_GPADC			0x01
+#define DIS_GPADC			0x00
+#define SW_AVG_16			0x60
+#define ADC_SW_CONV			0x04
+#define EN_ICHAR			0x80
+#define BTEMP_PULL_UP			0x08
+#define EN_BUF				0x40
+#define DIS_ZERO			0x00
+#define GPADC_BUSY			0x01
+
+/* GPADC constants from AB8500 spec, UM0836 */
+#define ADC_RESOLUTION			1024
+#define ADC_CH_BTEMP_MIN		0
+#define ADC_CH_BTEMP_MAX		1350
+#define ADC_CH_DIETEMP_MIN		0
+#define ADC_CH_DIETEMP_MAX		1350
+#define ADC_CH_CHG_V_MIN		0
+#define ADC_CH_CHG_V_MAX		20030
+#define ADC_CH_ACCDET2_MIN		0
+#define ADC_CH_ACCDET2_MAX		2500
+#define ADC_CH_VBAT_MIN			2300
+#define ADC_CH_VBAT_MAX			4800
+#define ADC_CH_CHG_I_MIN		0
+#define ADC_CH_CHG_I_MAX		1500
+#define ADC_CH_BKBAT_MIN		0
+#define ADC_CH_BKBAT_MAX		3200
+
+/* This is used to not lose precision when dividing to get gain and offset */
+#define CALIB_SCALE			1000
+
+enum cal_channels {
+	ADC_INPUT_VMAIN = 0,
+	ADC_INPUT_BTEMP,
+	ADC_INPUT_VBAT,
+	NBR_CAL_INPUTS,
+};
+
+/**
+ * struct adc_cal_data - Table for storing gain and offset for the calibrated
+ * ADC channels
+ * @gain:		Gain of the ADC channel
+ * @offset:		Offset of the ADC channel
+ */
+struct adc_cal_data {
+	u64 gain;
+	u64 offset;
+};
+
+/**
+ * struct ab8500_gpadc - AB8500 GPADC device information
+ * @chip_id			ABB chip id
+ * @dev:			pointer to the struct device
+ * @node:			a list of AB8500 GPADCs, hence prepared for
+				reentrance
+ * @ab8500_gpadc_complete:	pointer to the struct completion, to indicate
+ *				the completion of gpadc conversion
+ * @ab8500_gpadc_lock:		structure of type mutex
+ * @regu:			pointer to the struct regulator
+ * @irq:			interrupt number that is used by gpadc
+ * @cal_data			array of ADC calibration data structs
+ */
+struct ab8500_gpadc {
+	u8 chip_id;
+	struct device *dev;
+	struct list_head node;
+	struct completion ab8500_gpadc_complete;
+	struct mutex ab8500_gpadc_lock;
+	struct regulator *regu;
+	int irq;
+	struct adc_cal_data cal_data[NBR_CAL_INPUTS];
+};
+
+static LIST_HEAD(ab8500_gpadc_list);
+
+/**
+ * ab8500_gpadc_get() - returns a reference to the primary AB8500 GPADC
+ * (i.e. the first GPADC in the instance list)
+ */
+struct ab8500_gpadc *ab8500_gpadc_get(char *name)
+{
+	struct ab8500_gpadc *gpadc;
+
+	list_for_each_entry(gpadc, &ab8500_gpadc_list, node) {
+		if (!strcmp(name, dev_name(gpadc->dev)))
+		    return gpadc;
+	}
+
+	return ERR_PTR(-ENOENT);
+}
+EXPORT_SYMBOL(ab8500_gpadc_get);
+
+/**
+ * ab8500_gpadc_ad_to_voltage() - Convert a raw ADC value to a voltage
+ */
+int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 channel,
+	int ad_value)
+{
+	int res;
+
+	switch (channel) {
+	case MAIN_CHARGER_V:
+		/* For some reason we don't have calibrated data */
+		if (!gpadc->cal_data[ADC_INPUT_VMAIN].gain) {
+			res = ADC_CH_CHG_V_MIN + (ADC_CH_CHG_V_MAX -
+				ADC_CH_CHG_V_MIN) * ad_value /
+				ADC_RESOLUTION;
+			break;
+		}
+		/* Here we can use the calibrated data */
+		res = (int) (ad_value * gpadc->cal_data[ADC_INPUT_VMAIN].gain +
+			gpadc->cal_data[ADC_INPUT_VMAIN].offset) / CALIB_SCALE;
+		break;
+
+	case BAT_CTRL:
+	case BTEMP_BALL:
+	case ACC_DETECT1:
+	case ADC_AUX1:
+	case ADC_AUX2:
+		/* For some reason we don't have calibrated data */
+		if (!gpadc->cal_data[ADC_INPUT_BTEMP].gain) {
+			res = ADC_CH_BTEMP_MIN + (ADC_CH_BTEMP_MAX -
+				ADC_CH_BTEMP_MIN) * ad_value /
+				ADC_RESOLUTION;
+			break;
+		}
+		/* Here we can use the calibrated data */
+		res = (int) (ad_value * gpadc->cal_data[ADC_INPUT_BTEMP].gain +
+			gpadc->cal_data[ADC_INPUT_BTEMP].offset) / CALIB_SCALE;
+		break;
+
+	case MAIN_BAT_V:
+		/* For some reason we don't have calibrated data */
+		if (!gpadc->cal_data[ADC_INPUT_VBAT].gain) {
+			res = ADC_CH_VBAT_MIN + (ADC_CH_VBAT_MAX -
+				ADC_CH_VBAT_MIN) * ad_value /
+				ADC_RESOLUTION;
+			break;
+		}
+		/* Here we can use the calibrated data */
+		res = (int) (ad_value * gpadc->cal_data[ADC_INPUT_VBAT].gain +
+			gpadc->cal_data[ADC_INPUT_VBAT].offset) / CALIB_SCALE;
+		break;
+
+	case DIE_TEMP:
+		res = ADC_CH_DIETEMP_MIN +
+			(ADC_CH_DIETEMP_MAX - ADC_CH_DIETEMP_MIN) * ad_value /
+			ADC_RESOLUTION;
+		break;
+
+	case ACC_DETECT2:
+		res = ADC_CH_ACCDET2_MIN +
+			(ADC_CH_ACCDET2_MAX - ADC_CH_ACCDET2_MIN) * ad_value /
+			ADC_RESOLUTION;
+		break;
+
+	case VBUS_V:
+		res = ADC_CH_CHG_V_MIN +
+			(ADC_CH_CHG_V_MAX - ADC_CH_CHG_V_MIN) * ad_value /
+			ADC_RESOLUTION;
+		break;
+
+	case MAIN_CHARGER_C:
+	case USB_CHARGER_C:
+		res = ADC_CH_CHG_I_MIN +
+			(ADC_CH_CHG_I_MAX - ADC_CH_CHG_I_MIN) * ad_value /
+			ADC_RESOLUTION;
+		break;
+
+	case BK_BAT_V:
+		res = ADC_CH_BKBAT_MIN +
+			(ADC_CH_BKBAT_MAX - ADC_CH_BKBAT_MIN) * ad_value /
+			ADC_RESOLUTION;
+		break;
+
+	default:
+		dev_err(gpadc->dev,
+			"unknown channel, not possible to convert\n");
+		res = -EINVAL;
+		break;
+
+	}
+	return res;
+}
+EXPORT_SYMBOL(ab8500_gpadc_ad_to_voltage);
+
+/**
+ * ab8500_gpadc_convert() - gpadc conversion
+ * @channel:	analog channel to be converted to digital data
+ *
+ * This function converts the selected analog i/p to digital
+ * data.
+ */
+int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 channel)
+{
+	int ad_value;
+	int voltage;
+
+	ad_value = ab8500_gpadc_read_raw(gpadc, channel);
+	if (ad_value < 0) {
+		dev_err(gpadc->dev, "GPADC raw value failed ch: %d\n", channel);
+		return ad_value;
+	}
+
+	voltage = ab8500_gpadc_ad_to_voltage(gpadc, channel, ad_value);
+
+	if (voltage < 0)
+		dev_err(gpadc->dev, "GPADC to voltage conversion failed ch:"
+			" %d AD: 0x%x\n", channel, ad_value);
+
+	return voltage;
+}
+EXPORT_SYMBOL(ab8500_gpadc_convert);
+
+/**
+ * ab8500_gpadc_read_raw() - gpadc read
+ * @channel:	analog channel to be read
+ *
+ * This function obtains the raw ADC value, this then needs
+ * to be converted by calling ab8500_gpadc_ad_to_voltage()
+ */
+int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
+{
+	int ret;
+	int looplimit = 0;
+	u8 val, low_data, high_data;
+
+	if (!gpadc)
+		return -ENODEV;
+
+	mutex_lock(&gpadc->ab8500_gpadc_lock);
+	/* Enable VTVout LDO this is required for GPADC */
+	regulator_enable(gpadc->regu);
+
+	/* Check if ADC is not busy, lock and proceed */
+	do {
+		ret = abx500_get_register_interruptible(gpadc->dev,
+			AB8500_GPADC, AB8500_GPADC_STAT_REG, &val);
+		if (ret < 0)
+			goto out;
+		if (!(val & GPADC_BUSY))
+			break;
+		msleep(10);
+	} while (++looplimit < 10);
+	if (looplimit >= 10 && (val & GPADC_BUSY)) {
+		dev_err(gpadc->dev, "gpadc_conversion: GPADC busy");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	/* Enable GPADC */
+	ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
+		AB8500_GPADC, AB8500_GPADC_CTRL1_REG, EN_GPADC, EN_GPADC);
+	if (ret < 0) {
+		dev_err(gpadc->dev, "gpadc_conversion: enable gpadc failed\n");
+		goto out;
+	}
+
+	/* Select the channel source and set average samples to 16 */
+	ret = abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC,
+		AB8500_GPADC_CTRL2_REG, (channel | SW_AVG_16));
+	if (ret < 0) {
+		dev_err(gpadc->dev,
+			"gpadc_conversion: set avg samples failed\n");
+		goto out;
+	}
+
+	/*
+	 * Enable ADC, buffering, select rising edge and enable ADC path
+	 * charging current sense if it needed, ABB 3.0 needs some special
+	 * treatment too.
+	 */
+	switch (channel) {
+	case MAIN_CHARGER_C:
+	case USB_CHARGER_C:
+		ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
+			AB8500_GPADC, AB8500_GPADC_CTRL1_REG,
+			EN_BUF | EN_ICHAR,
+			EN_BUF | EN_ICHAR);
+		break;
+	case BTEMP_BALL:
+		if (gpadc->chip_id >= AB8500_CUT3P0) {
+			/* Turn on btemp pull-up on ABB 3.0 */
+			ret = abx500_mask_and_set_register_interruptible(
+				gpadc->dev,
+				AB8500_GPADC, AB8500_GPADC_CTRL1_REG,
+				EN_BUF | BTEMP_PULL_UP,
+				EN_BUF | BTEMP_PULL_UP);
+
+		 /*
+		  * Delay might be needed for ABB8500 cut 3.0, if not, remove
+		  * when hardware will be availible
+		  */
+			msleep(1);
+			break;
+		}
+		/* Intentional fallthrough */
+	default:
+		ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
+			AB8500_GPADC, AB8500_GPADC_CTRL1_REG, EN_BUF, EN_BUF);
+		break;
+	}
+	if (ret < 0) {
+		dev_err(gpadc->dev,
+			"gpadc_conversion: select falling edge failed\n");
+		goto out;
+	}
+
+	ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
+		AB8500_GPADC, AB8500_GPADC_CTRL1_REG, ADC_SW_CONV, ADC_SW_CONV);
+	if (ret < 0) {
+		dev_err(gpadc->dev,
+			"gpadc_conversion: start s/w conversion failed\n");
+		goto out;
+	}
+	/* wait for completion of conversion */
+	if (!wait_for_completion_timeout(&gpadc->ab8500_gpadc_complete, 2*HZ)) {
+		dev_err(gpadc->dev,
+			"timeout: didn't receive GPADC conversion interrupt\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	/* Read the converted RAW data */
+	ret = abx500_get_register_interruptible(gpadc->dev, AB8500_GPADC,
+		AB8500_GPADC_MANDATAL_REG, &low_data);
+	if (ret < 0) {
+		dev_err(gpadc->dev, "gpadc_conversion: read low data failed\n");
+		goto out;
+	}
+
+	ret = abx500_get_register_interruptible(gpadc->dev, AB8500_GPADC,
+		AB8500_GPADC_MANDATAH_REG, &high_data);
+	if (ret < 0) {
+		dev_err(gpadc->dev,
+			"gpadc_conversion: read high data failed\n");
+		goto out;
+	}
+
+	/* Disable GPADC */
+	ret = abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC,
+		AB8500_GPADC_CTRL1_REG, DIS_GPADC);
+	if (ret < 0) {
+		dev_err(gpadc->dev, "gpadc_conversion: disable gpadc failed\n");
+		goto out;
+	}
+	/* Disable VTVout LDO this is required for GPADC */
+	regulator_disable(gpadc->regu);
+	mutex_unlock(&gpadc->ab8500_gpadc_lock);
+
+	return (high_data << 8) | low_data;
+
+out:
+	/*
+	 * It has shown to be needed to turn off the GPADC if an error occurs,
+	 * otherwise we might have problem when waiting for the busy bit in the
+	 * GPADC status register to go low. In V1.1 there wait_for_completion
+	 * seems to timeout when waiting for an interrupt.. Not seen in V2.0
+	 */
+	(void) abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC,
+		AB8500_GPADC_CTRL1_REG, DIS_GPADC);
+	regulator_disable(gpadc->regu);
+	mutex_unlock(&gpadc->ab8500_gpadc_lock);
+	dev_err(gpadc->dev,
+		"gpadc_conversion: Failed to AD convert channel %d\n", channel);
+	return ret;
+}
+EXPORT_SYMBOL(ab8500_gpadc_read_raw);
+
+/**
+ * ab8500_bm_gpswadcconvend_handler() - isr for s/w gpadc conversion completion
+ * @irq:	irq number
+ * @data:	pointer to the data passed during request irq
+ *
+ * This is a interrupt service routine for s/w gpadc conversion completion.
+ * Notifies the gpadc completion is completed and the converted raw value
+ * can be read from the registers.
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_bm_gpswadcconvend_handler(int irq, void *_gpadc)
+{
+	struct ab8500_gpadc *gpadc = _gpadc;
+
+	complete(&gpadc->ab8500_gpadc_complete);
+
+	return IRQ_HANDLED;
+}
+
+static int otp_cal_regs[] = {
+	AB8500_GPADC_CAL_1,
+	AB8500_GPADC_CAL_2,
+	AB8500_GPADC_CAL_3,
+	AB8500_GPADC_CAL_4,
+	AB8500_GPADC_CAL_5,
+	AB8500_GPADC_CAL_6,
+	AB8500_GPADC_CAL_7,
+};
+
+static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc)
+{
+	int i;
+	int ret[ARRAY_SIZE(otp_cal_regs)];
+	u8 gpadc_cal[ARRAY_SIZE(otp_cal_regs)];
+
+	int vmain_high, vmain_low;
+	int btemp_high, btemp_low;
+	int vbat_high, vbat_low;
+
+	/* First we read all OTP registers and store the error code */
+	for (i = 0; i < ARRAY_SIZE(otp_cal_regs); i++) {
+		ret[i] = abx500_get_register_interruptible(gpadc->dev,
+			AB8500_OTP_EMUL, otp_cal_regs[i],  &gpadc_cal[i]);
+		if (ret[i] < 0)
+			dev_err(gpadc->dev, "%s: read otp reg 0x%02x failed\n",
+				__func__, otp_cal_regs[i]);
+	}
+
+	/*
+	 * The ADC calibration data is stored in OTP registers.
+	 * The layout of the calibration data is outlined below and a more
+	 * detailed description can be found in UM0836
+	 *
+	 * vm_h/l = vmain_high/low
+	 * bt_h/l = btemp_high/low
+	 * vb_h/l = vbat_high/low
+	 *
+	 * Data bits:
+	 * | 7	   | 6	   | 5	   | 4	   | 3	   | 2	   | 1	   | 0
+	 * |.......|.......|.......|.......|.......|.......|.......|.......
+	 * |						   | vm_h9 | vm_h8
+	 * |.......|.......|.......|.......|.......|.......|.......|.......
+	 * |		   | vm_h7 | vm_h6 | vm_h5 | vm_h4 | vm_h3 | vm_h2
+	 * |.......|.......|.......|.......|.......|.......|.......|.......
+	 * | vm_h1 | vm_h0 | vm_l4 | vm_l3 | vm_l2 | vm_l1 | vm_l0 | bt_h9
+	 * |.......|.......|.......|.......|.......|.......|.......|.......
+	 * | bt_h8 | bt_h7 | bt_h6 | bt_h5 | bt_h4 | bt_h3 | bt_h2 | bt_h1
+	 * |.......|.......|.......|.......|.......|.......|.......|.......
+	 * | bt_h0 | bt_l4 | bt_l3 | bt_l2 | bt_l1 | bt_l0 | vb_h9 | vb_h8
+	 * |.......|.......|.......|.......|.......|.......|.......|.......
+	 * | vb_h7 | vb_h6 | vb_h5 | vb_h4 | vb_h3 | vb_h2 | vb_h1 | vb_h0
+	 * |.......|.......|.......|.......|.......|.......|.......|.......
+	 * | vb_l5 | vb_l4 | vb_l3 | vb_l2 | vb_l1 | vb_l0 |
+	 * |.......|.......|.......|.......|.......|.......|.......|.......
+	 *
+	 *
+	 * Ideal output ADC codes corresponding to injected input voltages
+	 * during manufacturing is:
+	 *
+	 * vmain_high: Vin = 19500mV / ADC ideal code = 997
+	 * vmain_low:  Vin = 315mV   / ADC ideal code = 16
+	 * btemp_high: Vin = 1300mV  / ADC ideal code = 985
+	 * btemp_low:  Vin = 21mV    / ADC ideal code = 16
+	 * vbat_high:  Vin = 4700mV  / ADC ideal code = 982
+	 * vbat_low:   Vin = 2380mV  / ADC ideal code = 33
+	 */
+
+	/* Calculate gain and offset for VMAIN if all reads succeeded */
+	if (!(ret[0] < 0 || ret[1] < 0 || ret[2] < 0)) {
+		vmain_high = (((gpadc_cal[0] & 0x03) << 8) |
+			((gpadc_cal[1] & 0x3F) << 2) |
+			((gpadc_cal[2] & 0xC0) >> 6));
+
+		vmain_low = ((gpadc_cal[2] & 0x3E) >> 1);
+
+		gpadc->cal_data[ADC_INPUT_VMAIN].gain = CALIB_SCALE *
+			(19500 - 315) /	(vmain_high - vmain_low);
+
+		gpadc->cal_data[ADC_INPUT_VMAIN].offset = CALIB_SCALE * 19500 -
+			(CALIB_SCALE * (19500 - 315) /
+			 (vmain_high - vmain_low)) * vmain_high;
+	} else {
+		gpadc->cal_data[ADC_INPUT_VMAIN].gain = 0;
+	}
+
+	/* Calculate gain and offset for BTEMP if all reads succeeded */
+	if (!(ret[2] < 0 || ret[3] < 0 || ret[4] < 0)) {
+		btemp_high = (((gpadc_cal[2] & 0x01) << 9) |
+			(gpadc_cal[3] << 1) |
+			((gpadc_cal[4] & 0x80) >> 7));
+
+		btemp_low = ((gpadc_cal[4] & 0x7C) >> 2);
+
+		gpadc->cal_data[ADC_INPUT_BTEMP].gain =
+			CALIB_SCALE * (1300 - 21) / (btemp_high - btemp_low);
+
+		gpadc->cal_data[ADC_INPUT_BTEMP].offset = CALIB_SCALE * 1300 -
+			(CALIB_SCALE * (1300 - 21) /
+			(btemp_high - btemp_low)) * btemp_high;
+	} else {
+		gpadc->cal_data[ADC_INPUT_BTEMP].gain = 0;
+	}
+
+	/* Calculate gain and offset for VBAT if all reads succeeded */
+	if (!(ret[4] < 0 || ret[5] < 0 || ret[6] < 0)) {
+		vbat_high = (((gpadc_cal[4] & 0x03) << 8) | gpadc_cal[5]);
+		vbat_low = ((gpadc_cal[6] & 0xFC) >> 2);
+
+		gpadc->cal_data[ADC_INPUT_VBAT].gain = CALIB_SCALE *
+			(4700 - 2380) /	(vbat_high - vbat_low);
+
+		gpadc->cal_data[ADC_INPUT_VBAT].offset = CALIB_SCALE * 4700 -
+			(CALIB_SCALE * (4700 - 2380) /
+			(vbat_high - vbat_low)) * vbat_high;
+	} else {
+		gpadc->cal_data[ADC_INPUT_VBAT].gain = 0;
+	}
+
+	dev_dbg(gpadc->dev, "VMAIN gain %llu offset %llu\n",
+		gpadc->cal_data[ADC_INPUT_VMAIN].gain,
+		gpadc->cal_data[ADC_INPUT_VMAIN].offset);
+
+	dev_dbg(gpadc->dev, "BTEMP gain %llu offset %llu\n",
+		gpadc->cal_data[ADC_INPUT_BTEMP].gain,
+		gpadc->cal_data[ADC_INPUT_BTEMP].offset);
+
+	dev_dbg(gpadc->dev, "VBAT gain %llu offset %llu\n",
+		gpadc->cal_data[ADC_INPUT_VBAT].gain,
+		gpadc->cal_data[ADC_INPUT_VBAT].offset);
+}
+
+static int __devinit ab8500_gpadc_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	struct ab8500_gpadc *gpadc;
+
+	gpadc = kzalloc(sizeof(struct ab8500_gpadc), GFP_KERNEL);
+	if (!gpadc) {
+		dev_err(&pdev->dev, "Error: No memory\n");
+		return -ENOMEM;
+	}
+
+	gpadc->irq = platform_get_irq_byname(pdev, "SW_CONV_END");
+	if (gpadc->irq < 0) {
+		dev_err(gpadc->dev, "failed to get platform irq-%d\n",
+			gpadc->irq);
+		ret = gpadc->irq;
+		goto fail;
+	}
+
+	gpadc->dev = &pdev->dev;
+	mutex_init(&gpadc->ab8500_gpadc_lock);
+
+	/* Initialize completion used to notify completion of conversion */
+	init_completion(&gpadc->ab8500_gpadc_complete);
+
+	/* Register interrupt  - SwAdcComplete */
+	ret = request_threaded_irq(gpadc->irq, NULL,
+		ab8500_bm_gpswadcconvend_handler,
+		IRQF_NO_SUSPEND | IRQF_SHARED, "ab8500-gpadc", gpadc);
+	if (ret < 0) {
+		dev_err(gpadc->dev, "Failed to register interrupt, irq: %d\n",
+			gpadc->irq);
+		goto fail;
+	}
+
+	/* Get Chip ID of the ABB ASIC  */
+	ret = abx500_get_chip_id(gpadc->dev);
+	if (ret < 0) {
+		dev_err(gpadc->dev, "failed to get chip ID\n");
+		goto fail_irq;
+	}
+	gpadc->chip_id = (u8) ret;
+
+	/* VTVout LDO used to power up ab8500-GPADC */
+	gpadc->regu = regulator_get(&pdev->dev, "vddadc");
+	if (IS_ERR(gpadc->regu)) {
+		ret = PTR_ERR(gpadc->regu);
+		dev_err(gpadc->dev, "failed to get vtvout LDO\n");
+		goto fail_irq;
+	}
+	ab8500_gpadc_read_calibration_data(gpadc);
+	list_add_tail(&gpadc->node, &ab8500_gpadc_list);
+	dev_dbg(gpadc->dev, "probe success\n");
+	return 0;
+fail_irq:
+	free_irq(gpadc->irq, gpadc);
+fail:
+	kfree(gpadc);
+	gpadc = NULL;
+	return ret;
+}
+
+static int __devexit ab8500_gpadc_remove(struct platform_device *pdev)
+{
+	struct ab8500_gpadc *gpadc = platform_get_drvdata(pdev);
+
+	/* remove this gpadc entry from the list */
+	list_del(&gpadc->node);
+	/* remove interrupt  - completion of Sw ADC conversion */
+	free_irq(gpadc->irq, gpadc);
+	/* disable VTVout LDO that is being used by GPADC */
+	regulator_put(gpadc->regu);
+	kfree(gpadc);
+	gpadc = NULL;
+	return 0;
+}
+
+static struct platform_driver ab8500_gpadc_driver = {
+	.probe = ab8500_gpadc_probe,
+	.remove = __devexit_p(ab8500_gpadc_remove),
+	.driver = {
+		.name = "ab8500-gpadc",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init ab8500_gpadc_init(void)
+{
+	return platform_driver_register(&ab8500_gpadc_driver);
+}
+
+static void __exit ab8500_gpadc_exit(void)
+{
+	platform_driver_unregister(&ab8500_gpadc_driver);
+}
+
+subsys_initcall_sync(ab8500_gpadc_init);
+module_exit(ab8500_gpadc_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Arun R Murthy, Daniel Willerud, Johan Palsson");
+MODULE_ALIAS("platform:ab8500_gpadc");
+MODULE_DESCRIPTION("AB8500 GPADC driver");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/ab8500-i2c.c b/ap/os/linux/linux-3.4.x/drivers/mfd/ab8500-i2c.c
new file mode 100644
index 0000000..b83045f
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/ab8500-i2c.c
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Mattias Wallin <mattias.wallin@stericsson.com> for ST-Ericsson.
+ * License Terms: GNU General Public License v2
+ * This file was based on drivers/mfd/ab8500-spi.c
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/abx500/ab8500.h>
+#include <linux/mfd/dbx500-prcmu.h>
+
+static int ab8500_i2c_write(struct ab8500 *ab8500, u16 addr, u8 data)
+{
+	int ret;
+
+	ret = prcmu_abb_write((u8)(addr >> 8), (u8)(addr & 0xFF), &data, 1);
+	if (ret < 0)
+		dev_err(ab8500->dev, "prcmu i2c error %d\n", ret);
+	return ret;
+}
+
+static int ab8500_i2c_write_masked(struct ab8500 *ab8500, u16 addr, u8 mask,
+	u8 data)
+{
+	int ret;
+
+	ret = prcmu_abb_write_masked((u8)(addr >> 8), (u8)(addr & 0xFF), &data,
+		&mask, 1);
+	if (ret < 0)
+		dev_err(ab8500->dev, "prcmu i2c error %d\n", ret);
+	return ret;
+}
+
+static int ab8500_i2c_read(struct ab8500 *ab8500, u16 addr)
+{
+	int ret;
+	u8 data;
+
+	ret = prcmu_abb_read((u8)(addr >> 8), (u8)(addr & 0xFF), &data, 1);
+	if (ret < 0) {
+		dev_err(ab8500->dev, "prcmu i2c error %d\n", ret);
+		return ret;
+	}
+	return (int)data;
+}
+
+static int __devinit ab8500_i2c_probe(struct platform_device *plf)
+{
+	const struct platform_device_id *platid = platform_get_device_id(plf);
+	struct ab8500 *ab8500;
+	struct resource *resource;
+	int ret;
+
+	ab8500 = kzalloc(sizeof *ab8500, GFP_KERNEL);
+	if (!ab8500)
+		return -ENOMEM;
+
+	ab8500->dev = &plf->dev;
+
+	resource = platform_get_resource(plf, IORESOURCE_IRQ, 0);
+	if (!resource) {
+		kfree(ab8500);
+		return -ENODEV;
+	}
+
+	ab8500->irq = resource->start;
+
+	ab8500->read = ab8500_i2c_read;
+	ab8500->write = ab8500_i2c_write;
+	ab8500->write_masked = ab8500_i2c_write_masked;
+
+	platform_set_drvdata(plf, ab8500);
+
+	ret = ab8500_init(ab8500, platid->driver_data);
+	if (ret)
+		kfree(ab8500);
+
+
+	return ret;
+}
+
+static int __devexit ab8500_i2c_remove(struct platform_device *plf)
+{
+	struct ab8500 *ab8500 = platform_get_drvdata(plf);
+
+	ab8500_exit(ab8500);
+	kfree(ab8500);
+
+	return 0;
+}
+
+static const struct platform_device_id ab8500_id[] = {
+	{ "ab8500-i2c", AB8500_VERSION_AB8500 },
+	{ "ab8505-i2c", AB8500_VERSION_AB8505 },
+	{ "ab9540-i2c", AB8500_VERSION_AB9540 },
+	{ "ab8540-i2c", AB8500_VERSION_AB8540 },
+	{ }
+};
+
+static struct platform_driver ab8500_i2c_driver = {
+	.driver = {
+		.name = "ab8500-i2c",
+		.owner = THIS_MODULE,
+	},
+	.probe	= ab8500_i2c_probe,
+	.remove	= __devexit_p(ab8500_i2c_remove),
+	.id_table = ab8500_id,
+};
+
+static int __init ab8500_i2c_init(void)
+{
+	return platform_driver_register(&ab8500_i2c_driver);
+}
+
+static void __exit ab8500_i2c_exit(void)
+{
+	platform_driver_unregister(&ab8500_i2c_driver);
+}
+arch_initcall(ab8500_i2c_init);
+module_exit(ab8500_i2c_exit);
+
+MODULE_AUTHOR("Mattias WALLIN <mattias.wallin@stericsson.com");
+MODULE_DESCRIPTION("AB8500 Core access via PRCMU I2C");
+MODULE_LICENSE("GPL v2");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/ab8500-sysctrl.c b/ap/os/linux/linux-3.4.x/drivers/mfd/ab8500-sysctrl.c
new file mode 100644
index 0000000..c28d4eb1
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/ab8500-sysctrl.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Mattias Nilsson <mattias.i.nilsson@stericsson.com> for ST Ericsson.
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab8500.h>
+#include <linux/mfd/abx500/ab8500-sysctrl.h>
+
+static struct device *sysctrl_dev;
+
+static inline bool valid_bank(u8 bank)
+{
+	return ((bank == AB8500_SYS_CTRL1_BLOCK) ||
+		(bank == AB8500_SYS_CTRL2_BLOCK));
+}
+
+int ab8500_sysctrl_read(u16 reg, u8 *value)
+{
+	u8 bank;
+
+	if (sysctrl_dev == NULL)
+		return -EAGAIN;
+
+	bank = (reg >> 8);
+	if (!valid_bank(bank))
+		return -EINVAL;
+
+	return abx500_get_register_interruptible(sysctrl_dev, bank,
+		(u8)(reg & 0xFF), value);
+}
+
+int ab8500_sysctrl_write(u16 reg, u8 mask, u8 value)
+{
+	u8 bank;
+
+	if (sysctrl_dev == NULL)
+		return -EAGAIN;
+
+	bank = (reg >> 8);
+	if (!valid_bank(bank))
+		return -EINVAL;
+
+	return abx500_mask_and_set_register_interruptible(sysctrl_dev, bank,
+		(u8)(reg & 0xFF), mask, value);
+}
+
+static int __devinit ab8500_sysctrl_probe(struct platform_device *pdev)
+{
+	sysctrl_dev = &pdev->dev;
+	return 0;
+}
+
+static int __devexit ab8500_sysctrl_remove(struct platform_device *pdev)
+{
+	sysctrl_dev = NULL;
+	return 0;
+}
+
+static struct platform_driver ab8500_sysctrl_driver = {
+	.driver = {
+		.name = "ab8500-sysctrl",
+		.owner = THIS_MODULE,
+	},
+	.probe = ab8500_sysctrl_probe,
+	.remove = __devexit_p(ab8500_sysctrl_remove),
+};
+
+static int __init ab8500_sysctrl_init(void)
+{
+	return platform_driver_register(&ab8500_sysctrl_driver);
+}
+subsys_initcall(ab8500_sysctrl_init);
+
+MODULE_AUTHOR("Mattias Nilsson <mattias.i.nilsson@stericsson.com");
+MODULE_DESCRIPTION("AB8500 system control driver");
+MODULE_LICENSE("GPL v2");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/abx500-core.c b/ap/os/linux/linux-3.4.x/drivers/mfd/abx500-core.c
new file mode 100644
index 0000000..7ce65f4
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/abx500-core.c
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2007-2010 ST-Ericsson
+ * License terms: GNU General Public License (GPL) version 2
+ * Register access functions for the ABX500 Mixed Signal IC family.
+ * Author: Mattias Wallin <mattias.wallin@stericsson.com>
+ */
+
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/mfd/abx500.h>
+
+static LIST_HEAD(abx500_list);
+
+struct abx500_device_entry {
+	struct list_head list;
+	struct abx500_ops ops;
+	struct device *dev;
+};
+
+static void lookup_ops(struct device *dev, struct abx500_ops **ops)
+{
+	struct abx500_device_entry *dev_entry;
+
+	*ops = NULL;
+	list_for_each_entry(dev_entry, &abx500_list, list) {
+		if (dev_entry->dev == dev) {
+			*ops = &dev_entry->ops;
+			return;
+		}
+	}
+}
+
+int abx500_register_ops(struct device *dev, struct abx500_ops *ops)
+{
+	struct abx500_device_entry *dev_entry;
+
+	dev_entry = kzalloc(sizeof(struct abx500_device_entry), GFP_KERNEL);
+	if (!dev_entry) {
+		dev_err(dev, "register_ops kzalloc failed");
+		return -ENOMEM;
+	}
+	dev_entry->dev = dev;
+	memcpy(&dev_entry->ops, ops, sizeof(struct abx500_ops));
+
+	list_add_tail(&dev_entry->list, &abx500_list);
+	return 0;
+}
+EXPORT_SYMBOL(abx500_register_ops);
+
+void abx500_remove_ops(struct device *dev)
+{
+	struct abx500_device_entry *dev_entry, *tmp;
+
+	list_for_each_entry_safe(dev_entry, tmp, &abx500_list, list)
+	{
+		if (dev_entry->dev == dev) {
+			list_del(&dev_entry->list);
+			kfree(dev_entry);
+		}
+	}
+}
+EXPORT_SYMBOL(abx500_remove_ops);
+
+int abx500_set_register_interruptible(struct device *dev, u8 bank, u8 reg,
+	u8 value)
+{
+	struct abx500_ops *ops;
+
+	lookup_ops(dev->parent, &ops);
+	if ((ops != NULL) && (ops->set_register != NULL))
+		return ops->set_register(dev, bank, reg, value);
+	else
+		return -ENOTSUPP;
+}
+EXPORT_SYMBOL(abx500_set_register_interruptible);
+
+int abx500_get_register_interruptible(struct device *dev, u8 bank, u8 reg,
+	u8 *value)
+{
+	struct abx500_ops *ops;
+
+	lookup_ops(dev->parent, &ops);
+	if ((ops != NULL) && (ops->get_register != NULL))
+		return ops->get_register(dev, bank, reg, value);
+	else
+		return -ENOTSUPP;
+}
+EXPORT_SYMBOL(abx500_get_register_interruptible);
+
+int abx500_get_register_page_interruptible(struct device *dev, u8 bank,
+	u8 first_reg, u8 *regvals, u8 numregs)
+{
+	struct abx500_ops *ops;
+
+	lookup_ops(dev->parent, &ops);
+	if ((ops != NULL) && (ops->get_register_page != NULL))
+		return ops->get_register_page(dev, bank,
+			first_reg, regvals, numregs);
+	else
+		return -ENOTSUPP;
+}
+EXPORT_SYMBOL(abx500_get_register_page_interruptible);
+
+int abx500_mask_and_set_register_interruptible(struct device *dev, u8 bank,
+	u8 reg, u8 bitmask, u8 bitvalues)
+{
+	struct abx500_ops *ops;
+
+	lookup_ops(dev->parent, &ops);
+	if ((ops != NULL) && (ops->mask_and_set_register != NULL))
+		return ops->mask_and_set_register(dev, bank,
+			reg, bitmask, bitvalues);
+	else
+		return -ENOTSUPP;
+}
+EXPORT_SYMBOL(abx500_mask_and_set_register_interruptible);
+
+int abx500_get_chip_id(struct device *dev)
+{
+	struct abx500_ops *ops;
+
+	lookup_ops(dev->parent, &ops);
+	if ((ops != NULL) && (ops->get_chip_id != NULL))
+		return ops->get_chip_id(dev);
+	else
+		return -ENOTSUPP;
+}
+EXPORT_SYMBOL(abx500_get_chip_id);
+
+int abx500_event_registers_startup_state_get(struct device *dev, u8 *event)
+{
+	struct abx500_ops *ops;
+
+	lookup_ops(dev->parent, &ops);
+	if ((ops != NULL) && (ops->event_registers_startup_state_get != NULL))
+		return ops->event_registers_startup_state_get(dev, event);
+	else
+		return -ENOTSUPP;
+}
+EXPORT_SYMBOL(abx500_event_registers_startup_state_get);
+
+int abx500_startup_irq_enabled(struct device *dev, unsigned int irq)
+{
+	struct abx500_ops *ops;
+
+	lookup_ops(dev->parent, &ops);
+	if ((ops != NULL) && (ops->startup_irq_enabled != NULL))
+		return ops->startup_irq_enabled(dev, irq);
+	else
+		return -ENOTSUPP;
+}
+EXPORT_SYMBOL(abx500_startup_irq_enabled);
+
+MODULE_AUTHOR("Mattias Wallin <mattias.wallin@stericsson.com>");
+MODULE_DESCRIPTION("ABX500 core driver");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/adp5520.c b/ap/os/linux/linux-3.4.x/drivers/mfd/adp5520.c
new file mode 100644
index 0000000..105f820
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/adp5520.c
@@ -0,0 +1,381 @@
+/*
+ * Base driver for Analog Devices ADP5520/ADP5501 MFD PMICs
+ * LCD Backlight: drivers/video/backlight/adp5520_bl
+ * LEDs		: drivers/led/leds-adp5520
+ * GPIO		: drivers/gpio/adp5520-gpio (ADP5520 only)
+ * Keys		: drivers/input/keyboard/adp5520-keys (ADP5520 only)
+ *
+ * Copyright 2009 Analog Devices Inc.
+ *
+ * Derived from da903x:
+ * Copyright (C) 2008 Compulab, Ltd.
+ * 	Mike Rapoport <mike@compulab.co.il>
+ *
+ * Copyright (C) 2006-2008 Marvell International Ltd.
+ * 	Eric Miao <eric.miao@marvell.com>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+
+#include <linux/mfd/adp5520.h>
+
+struct adp5520_chip {
+	struct i2c_client *client;
+	struct device *dev;
+	struct mutex lock;
+	struct blocking_notifier_head notifier_list;
+	int irq;
+	unsigned long id;
+	uint8_t mode;
+};
+
+static int __adp5520_read(struct i2c_client *client,
+				int reg, uint8_t *val)
+{
+	int ret;
+
+	ret = i2c_smbus_read_byte_data(client, reg);
+	if (ret < 0) {
+		dev_err(&client->dev, "failed reading at 0x%02x\n", reg);
+		return ret;
+	}
+
+	*val = (uint8_t)ret;
+	return 0;
+}
+
+static int __adp5520_write(struct i2c_client *client,
+				 int reg, uint8_t val)
+{
+	int ret;
+
+	ret = i2c_smbus_write_byte_data(client, reg, val);
+	if (ret < 0) {
+		dev_err(&client->dev, "failed writing 0x%02x to 0x%02x\n",
+				val, reg);
+		return ret;
+	}
+	return 0;
+}
+
+static int __adp5520_ack_bits(struct i2c_client *client, int reg,
+			      uint8_t bit_mask)
+{
+	struct adp5520_chip *chip = i2c_get_clientdata(client);
+	uint8_t reg_val;
+	int ret;
+
+	mutex_lock(&chip->lock);
+
+	ret = __adp5520_read(client, reg, &reg_val);
+
+	if (!ret) {
+		reg_val |= bit_mask;
+		ret = __adp5520_write(client, reg, reg_val);
+	}
+
+	mutex_unlock(&chip->lock);
+	return ret;
+}
+
+int adp5520_write(struct device *dev, int reg, uint8_t val)
+{
+	return __adp5520_write(to_i2c_client(dev), reg, val);
+}
+EXPORT_SYMBOL_GPL(adp5520_write);
+
+int adp5520_read(struct device *dev, int reg, uint8_t *val)
+{
+	return __adp5520_read(to_i2c_client(dev), reg, val);
+}
+EXPORT_SYMBOL_GPL(adp5520_read);
+
+int adp5520_set_bits(struct device *dev, int reg, uint8_t bit_mask)
+{
+	struct adp5520_chip *chip = dev_get_drvdata(dev);
+	uint8_t reg_val;
+	int ret;
+
+	mutex_lock(&chip->lock);
+
+	ret = __adp5520_read(chip->client, reg, &reg_val);
+
+	if (!ret && ((reg_val & bit_mask) != bit_mask)) {
+		reg_val |= bit_mask;
+		ret = __adp5520_write(chip->client, reg, reg_val);
+	}
+
+	mutex_unlock(&chip->lock);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(adp5520_set_bits);
+
+int adp5520_clr_bits(struct device *dev, int reg, uint8_t bit_mask)
+{
+	struct adp5520_chip *chip = dev_get_drvdata(dev);
+	uint8_t reg_val;
+	int ret;
+
+	mutex_lock(&chip->lock);
+
+	ret = __adp5520_read(chip->client, reg, &reg_val);
+
+	if (!ret && (reg_val & bit_mask)) {
+		reg_val &= ~bit_mask;
+		ret = __adp5520_write(chip->client, reg, reg_val);
+	}
+
+	mutex_unlock(&chip->lock);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(adp5520_clr_bits);
+
+int adp5520_register_notifier(struct device *dev, struct notifier_block *nb,
+				unsigned int events)
+{
+	struct adp5520_chip *chip = dev_get_drvdata(dev);
+
+	if (chip->irq) {
+		adp5520_set_bits(chip->dev, ADP5520_INTERRUPT_ENABLE,
+			events & (ADP5520_KP_IEN | ADP5520_KR_IEN |
+			ADP5520_OVP_IEN | ADP5520_CMPR_IEN));
+
+		return blocking_notifier_chain_register(&chip->notifier_list,
+			 nb);
+	}
+
+	return -ENODEV;
+}
+EXPORT_SYMBOL_GPL(adp5520_register_notifier);
+
+int adp5520_unregister_notifier(struct device *dev, struct notifier_block *nb,
+				unsigned int events)
+{
+	struct adp5520_chip *chip = dev_get_drvdata(dev);
+
+	adp5520_clr_bits(chip->dev, ADP5520_INTERRUPT_ENABLE,
+		events & (ADP5520_KP_IEN | ADP5520_KR_IEN |
+		ADP5520_OVP_IEN | ADP5520_CMPR_IEN));
+
+	return blocking_notifier_chain_unregister(&chip->notifier_list, nb);
+}
+EXPORT_SYMBOL_GPL(adp5520_unregister_notifier);
+
+static irqreturn_t adp5520_irq_thread(int irq, void *data)
+{
+	struct adp5520_chip *chip = data;
+	unsigned int events;
+	uint8_t reg_val;
+	int ret;
+
+	ret = __adp5520_read(chip->client, ADP5520_MODE_STATUS, &reg_val);
+	if (ret)
+		goto out;
+
+	events =  reg_val & (ADP5520_OVP_INT | ADP5520_CMPR_INT |
+		ADP5520_GPI_INT | ADP5520_KR_INT | ADP5520_KP_INT);
+
+	blocking_notifier_call_chain(&chip->notifier_list, events, NULL);
+	/* ACK, Sticky bits are W1C */
+	__adp5520_ack_bits(chip->client, ADP5520_MODE_STATUS, events);
+
+out:
+	return IRQ_HANDLED;
+}
+
+static int __remove_subdev(struct device *dev, void *unused)
+{
+	platform_device_unregister(to_platform_device(dev));
+	return 0;
+}
+
+static int adp5520_remove_subdevs(struct adp5520_chip *chip)
+{
+	return device_for_each_child(chip->dev, NULL, __remove_subdev);
+}
+
+static int __devinit adp5520_probe(struct i2c_client *client,
+					const struct i2c_device_id *id)
+{
+	struct adp5520_platform_data *pdata = client->dev.platform_data;
+	struct platform_device *pdev;
+	struct adp5520_chip *chip;
+	int ret;
+
+	if (!i2c_check_functionality(client->adapter,
+					I2C_FUNC_SMBUS_BYTE_DATA)) {
+		dev_err(&client->dev, "SMBUS Word Data not Supported\n");
+		return -EIO;
+	}
+
+	if (pdata == NULL) {
+		dev_err(&client->dev, "missing platform data\n");
+		return -ENODEV;
+	}
+
+	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+	if (!chip)
+		return -ENOMEM;
+
+	i2c_set_clientdata(client, chip);
+	chip->client = client;
+
+	chip->dev = &client->dev;
+	chip->irq = client->irq;
+	chip->id = id->driver_data;
+	mutex_init(&chip->lock);
+
+	if (chip->irq) {
+		BLOCKING_INIT_NOTIFIER_HEAD(&chip->notifier_list);
+
+		ret = request_threaded_irq(chip->irq, NULL, adp5520_irq_thread,
+				IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+				"adp5520", chip);
+		if (ret) {
+			dev_err(&client->dev, "failed to request irq %d\n",
+					chip->irq);
+			goto out_free_chip;
+		}
+	}
+
+	ret = adp5520_write(chip->dev, ADP5520_MODE_STATUS, ADP5520_nSTNBY);
+	if (ret) {
+		dev_err(&client->dev, "failed to write\n");
+		goto out_free_irq;
+	}
+
+	if (pdata->keys) {
+		pdev = platform_device_register_data(chip->dev, "adp5520-keys",
+				chip->id, pdata->keys, sizeof(*pdata->keys));
+		if (IS_ERR(pdev)) {
+			ret = PTR_ERR(pdev);
+			goto out_remove_subdevs;
+		}
+	}
+
+	if (pdata->gpio) {
+		pdev = platform_device_register_data(chip->dev, "adp5520-gpio",
+				chip->id, pdata->gpio, sizeof(*pdata->gpio));
+		if (IS_ERR(pdev)) {
+			ret = PTR_ERR(pdev);
+			goto out_remove_subdevs;
+		}
+	}
+
+	if (pdata->leds) {
+		pdev = platform_device_register_data(chip->dev, "adp5520-led",
+				chip->id, pdata->leds, sizeof(*pdata->leds));
+		if (IS_ERR(pdev)) {
+			ret = PTR_ERR(pdev);
+			goto out_remove_subdevs;
+		}
+	}
+
+	if (pdata->backlight) {
+		pdev = platform_device_register_data(chip->dev,
+						"adp5520-backlight",
+						chip->id,
+						pdata->backlight,
+						sizeof(*pdata->backlight));
+		if (IS_ERR(pdev)) {
+			ret = PTR_ERR(pdev);
+			goto out_remove_subdevs;
+		}
+	}
+
+	return 0;
+
+out_remove_subdevs:
+	adp5520_remove_subdevs(chip);
+
+out_free_irq:
+	if (chip->irq)
+		free_irq(chip->irq, chip);
+
+out_free_chip:
+	kfree(chip);
+
+	return ret;
+}
+
+static int __devexit adp5520_remove(struct i2c_client *client)
+{
+	struct adp5520_chip *chip = dev_get_drvdata(&client->dev);
+
+	if (chip->irq)
+		free_irq(chip->irq, chip);
+
+	adp5520_remove_subdevs(chip);
+	adp5520_write(chip->dev, ADP5520_MODE_STATUS, 0);
+	kfree(chip);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int adp5520_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct adp5520_chip *chip = dev_get_drvdata(&client->dev);
+
+	adp5520_read(chip->dev, ADP5520_MODE_STATUS, &chip->mode);
+	/* All other bits are W1C */
+	chip->mode &= ADP5520_BL_EN | ADP5520_DIM_EN | ADP5520_nSTNBY;
+	adp5520_write(chip->dev, ADP5520_MODE_STATUS, 0);
+	return 0;
+}
+
+static int adp5520_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct adp5520_chip *chip = dev_get_drvdata(&client->dev);
+
+	adp5520_write(chip->dev, ADP5520_MODE_STATUS, chip->mode);
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(adp5520_pm, adp5520_suspend, adp5520_resume);
+
+static const struct i2c_device_id adp5520_id[] = {
+	{ "pmic-adp5520", ID_ADP5520 },
+	{ "pmic-adp5501", ID_ADP5501 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, adp5520_id);
+
+static struct i2c_driver adp5520_driver = {
+	.driver = {
+		.name	= "adp5520",
+		.owner	= THIS_MODULE,
+		.pm	= &adp5520_pm,
+	},
+	.probe		= adp5520_probe,
+	.remove		= __devexit_p(adp5520_remove),
+	.id_table 	= adp5520_id,
+};
+
+static int __init adp5520_init(void)
+{
+	return i2c_add_driver(&adp5520_driver);
+}
+module_init(adp5520_init);
+
+static void __exit adp5520_exit(void)
+{
+	i2c_del_driver(&adp5520_driver);
+}
+module_exit(adp5520_exit);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("ADP5520(01) PMIC-MFD Driver");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/anatop-mfd.c b/ap/os/linux/linux-3.4.x/drivers/mfd/anatop-mfd.c
new file mode 100644
index 0000000..2af4248
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/anatop-mfd.c
@@ -0,0 +1,137 @@
+/*
+ * Anatop MFD driver
+ *
+ * Copyright (C) 2012 Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org>
+ * Copyright (C) 2012 Linaro
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/mfd/anatop.h>
+
+u32 anatop_get_bits(struct anatop *adata, u32 addr, int bit_shift,
+		    int bit_width)
+{
+	u32 val, mask;
+
+	if (bit_width == 32)
+		mask = ~0;
+	else
+		mask = (1 << bit_width) - 1;
+
+	val = readl(adata->ioreg + addr);
+	val = (val >> bit_shift) & mask;
+
+	return val;
+}
+EXPORT_SYMBOL_GPL(anatop_get_bits);
+
+void anatop_set_bits(struct anatop *adata, u32 addr, int bit_shift,
+		     int bit_width, u32 data)
+{
+	u32 val, mask;
+
+	if (bit_width == 32)
+		mask = ~0;
+	else
+		mask = (1 << bit_width) - 1;
+
+	spin_lock(&adata->reglock);
+	val = readl(adata->ioreg + addr) & ~(mask << bit_shift);
+	writel((data << bit_shift) | val, adata->ioreg + addr);
+	spin_unlock(&adata->reglock);
+}
+EXPORT_SYMBOL_GPL(anatop_set_bits);
+
+static const struct of_device_id of_anatop_match[] = {
+	{ .compatible = "fsl,imx6q-anatop", },
+	{ },
+};
+
+static int __devinit of_anatop_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	void *ioreg;
+	struct anatop *drvdata;
+
+	ioreg = of_iomap(np, 0);
+	if (!ioreg)
+		return -EADDRNOTAVAIL;
+	drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
+	if (!drvdata)
+		return -ENOMEM;
+	drvdata->ioreg = ioreg;
+	spin_lock_init(&drvdata->reglock);
+	platform_set_drvdata(pdev, drvdata);
+	of_platform_populate(np, of_anatop_match, NULL, dev);
+
+	return 0;
+}
+
+static int __devexit of_anatop_remove(struct platform_device *pdev)
+{
+	struct anatop *drvdata;
+	drvdata = platform_get_drvdata(pdev);
+	iounmap(drvdata->ioreg);
+
+	return 0;
+}
+
+static struct platform_driver anatop_of_driver = {
+	.driver = {
+		.name = "anatop-mfd",
+		.owner = THIS_MODULE,
+		.of_match_table = of_anatop_match,
+	},
+	.probe		= of_anatop_probe,
+	.remove		= of_anatop_remove,
+};
+
+static int __init anatop_init(void)
+{
+	return platform_driver_register(&anatop_of_driver);
+}
+postcore_initcall(anatop_init);
+
+static void __exit anatop_exit(void)
+{
+	platform_driver_unregister(&anatop_of_driver);
+}
+module_exit(anatop_exit);
+
+MODULE_AUTHOR("Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org>");
+MODULE_DESCRIPTION("ANATOP MFD driver");
+MODULE_LICENSE("GPL v2");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/asic3.c b/ap/os/linux/linux-3.4.x/drivers/mfd/asic3.c
new file mode 100644
index 0000000..1582c3d
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/asic3.c
@@ -0,0 +1,1059 @@
+/*
+ * driver/mfd/asic3.c
+ *
+ * Compaq ASIC3 support.
+ *
+ * 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.
+ *
+ * Copyright 2001 Compaq Computer Corporation.
+ * Copyright 2004-2005 Phil Blundell
+ * Copyright 2007-2008 OpenedHand Ltd.
+ *
+ * Authors: Phil Blundell <pb@handhelds.org>,
+ *	    Samuel Ortiz <sameo@openedhand.com>
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/export.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+
+#include <linux/mfd/asic3.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/ds1wm.h>
+#include <linux/mfd/tmio.h>
+
+enum {
+	ASIC3_CLOCK_SPI,
+	ASIC3_CLOCK_OWM,
+	ASIC3_CLOCK_PWM0,
+	ASIC3_CLOCK_PWM1,
+	ASIC3_CLOCK_LED0,
+	ASIC3_CLOCK_LED1,
+	ASIC3_CLOCK_LED2,
+	ASIC3_CLOCK_SD_HOST,
+	ASIC3_CLOCK_SD_BUS,
+	ASIC3_CLOCK_SMBUS,
+	ASIC3_CLOCK_EX0,
+	ASIC3_CLOCK_EX1,
+};
+
+struct asic3_clk {
+	int enabled;
+	unsigned int cdex;
+	unsigned long rate;
+};
+
+#define INIT_CDEX(_name, _rate)	\
+	[ASIC3_CLOCK_##_name] = {		\
+		.cdex = CLOCK_CDEX_##_name,	\
+		.rate = _rate,			\
+	}
+
+static struct asic3_clk asic3_clk_init[] __initdata = {
+	INIT_CDEX(SPI, 0),
+	INIT_CDEX(OWM, 5000000),
+	INIT_CDEX(PWM0, 0),
+	INIT_CDEX(PWM1, 0),
+	INIT_CDEX(LED0, 0),
+	INIT_CDEX(LED1, 0),
+	INIT_CDEX(LED2, 0),
+	INIT_CDEX(SD_HOST, 24576000),
+	INIT_CDEX(SD_BUS, 12288000),
+	INIT_CDEX(SMBUS, 0),
+	INIT_CDEX(EX0, 32768),
+	INIT_CDEX(EX1, 24576000),
+};
+
+struct asic3 {
+	void __iomem *mapping;
+	unsigned int bus_shift;
+	unsigned int irq_nr;
+	unsigned int irq_base;
+	spinlock_t lock;
+	u16 irq_bothedge[4];
+	struct gpio_chip gpio;
+	struct device *dev;
+	void __iomem *tmio_cnf;
+
+	struct asic3_clk clocks[ARRAY_SIZE(asic3_clk_init)];
+};
+
+static int asic3_gpio_get(struct gpio_chip *chip, unsigned offset);
+
+void asic3_write_register(struct asic3 *asic, unsigned int reg, u32 value)
+{
+	iowrite16(value, asic->mapping +
+		  (reg >> asic->bus_shift));
+}
+EXPORT_SYMBOL_GPL(asic3_write_register);
+
+u32 asic3_read_register(struct asic3 *asic, unsigned int reg)
+{
+	return ioread16(asic->mapping +
+			(reg >> asic->bus_shift));
+}
+EXPORT_SYMBOL_GPL(asic3_read_register);
+
+static void asic3_set_register(struct asic3 *asic, u32 reg, u32 bits, bool set)
+{
+	unsigned long flags;
+	u32 val;
+
+	spin_lock_irqsave(&asic->lock, flags);
+	val = asic3_read_register(asic, reg);
+	if (set)
+		val |= bits;
+	else
+		val &= ~bits;
+	asic3_write_register(asic, reg, val);
+	spin_unlock_irqrestore(&asic->lock, flags);
+}
+
+/* IRQs */
+#define MAX_ASIC_ISR_LOOPS    20
+#define ASIC3_GPIO_BASE_INCR \
+	(ASIC3_GPIO_B_BASE - ASIC3_GPIO_A_BASE)
+
+static void asic3_irq_flip_edge(struct asic3 *asic,
+				u32 base, int bit)
+{
+	u16 edge;
+	unsigned long flags;
+
+	spin_lock_irqsave(&asic->lock, flags);
+	edge = asic3_read_register(asic,
+				   base + ASIC3_GPIO_EDGE_TRIGGER);
+	edge ^= bit;
+	asic3_write_register(asic,
+			     base + ASIC3_GPIO_EDGE_TRIGGER, edge);
+	spin_unlock_irqrestore(&asic->lock, flags);
+}
+
+static void asic3_irq_demux(unsigned int irq, struct irq_desc *desc)
+{
+	struct asic3 *asic = irq_desc_get_handler_data(desc);
+	struct irq_data *data = irq_desc_get_irq_data(desc);
+	int iter, i;
+	unsigned long flags;
+
+	data->chip->irq_ack(data);
+
+	for (iter = 0 ; iter < MAX_ASIC_ISR_LOOPS; iter++) {
+		u32 status;
+		int bank;
+
+		spin_lock_irqsave(&asic->lock, flags);
+		status = asic3_read_register(asic,
+					     ASIC3_OFFSET(INTR, P_INT_STAT));
+		spin_unlock_irqrestore(&asic->lock, flags);
+
+		/* Check all ten register bits */
+		if ((status & 0x3ff) == 0)
+			break;
+
+		/* Handle GPIO IRQs */
+		for (bank = 0; bank < ASIC3_NUM_GPIO_BANKS; bank++) {
+			if (status & (1 << bank)) {
+				unsigned long base, istat;
+
+				base = ASIC3_GPIO_A_BASE
+				       + bank * ASIC3_GPIO_BASE_INCR;
+
+				spin_lock_irqsave(&asic->lock, flags);
+				istat = asic3_read_register(asic,
+							    base +
+							    ASIC3_GPIO_INT_STATUS);
+				/* Clearing IntStatus */
+				asic3_write_register(asic,
+						     base +
+						     ASIC3_GPIO_INT_STATUS, 0);
+				spin_unlock_irqrestore(&asic->lock, flags);
+
+				for (i = 0; i < ASIC3_GPIOS_PER_BANK; i++) {
+					int bit = (1 << i);
+					unsigned int irqnr;
+
+					if (!(istat & bit))
+						continue;
+
+					irqnr = asic->irq_base +
+						(ASIC3_GPIOS_PER_BANK * bank)
+						+ i;
+					generic_handle_irq(irqnr);
+					if (asic->irq_bothedge[bank] & bit)
+						asic3_irq_flip_edge(asic, base,
+								    bit);
+				}
+			}
+		}
+
+		/* Handle remaining IRQs in the status register */
+		for (i = ASIC3_NUM_GPIOS; i < ASIC3_NR_IRQS; i++) {
+			/* They start at bit 4 and go up */
+			if (status & (1 << (i - ASIC3_NUM_GPIOS + 4)))
+				generic_handle_irq(asic->irq_base + i);
+		}
+	}
+
+	if (iter >= MAX_ASIC_ISR_LOOPS)
+		dev_err(asic->dev, "interrupt processing overrun\n");
+}
+
+static inline int asic3_irq_to_bank(struct asic3 *asic, int irq)
+{
+	int n;
+
+	n = (irq - asic->irq_base) >> 4;
+
+	return (n * (ASIC3_GPIO_B_BASE - ASIC3_GPIO_A_BASE));
+}
+
+static inline int asic3_irq_to_index(struct asic3 *asic, int irq)
+{
+	return (irq - asic->irq_base) & 0xf;
+}
+
+static void asic3_mask_gpio_irq(struct irq_data *data)
+{
+	struct asic3 *asic = irq_data_get_irq_chip_data(data);
+	u32 val, bank, index;
+	unsigned long flags;
+
+	bank = asic3_irq_to_bank(asic, data->irq);
+	index = asic3_irq_to_index(asic, data->irq);
+
+	spin_lock_irqsave(&asic->lock, flags);
+	val = asic3_read_register(asic, bank + ASIC3_GPIO_MASK);
+	val |= 1 << index;
+	asic3_write_register(asic, bank + ASIC3_GPIO_MASK, val);
+	spin_unlock_irqrestore(&asic->lock, flags);
+}
+
+static void asic3_mask_irq(struct irq_data *data)
+{
+	struct asic3 *asic = irq_data_get_irq_chip_data(data);
+	int regval;
+	unsigned long flags;
+
+	spin_lock_irqsave(&asic->lock, flags);
+	regval = asic3_read_register(asic,
+				     ASIC3_INTR_BASE +
+				     ASIC3_INTR_INT_MASK);
+
+	regval &= ~(ASIC3_INTMASK_MASK0 <<
+		    (data->irq - (asic->irq_base + ASIC3_NUM_GPIOS)));
+
+	asic3_write_register(asic,
+			     ASIC3_INTR_BASE +
+			     ASIC3_INTR_INT_MASK,
+			     regval);
+	spin_unlock_irqrestore(&asic->lock, flags);
+}
+
+static void asic3_unmask_gpio_irq(struct irq_data *data)
+{
+	struct asic3 *asic = irq_data_get_irq_chip_data(data);
+	u32 val, bank, index;
+	unsigned long flags;
+
+	bank = asic3_irq_to_bank(asic, data->irq);
+	index = asic3_irq_to_index(asic, data->irq);
+
+	spin_lock_irqsave(&asic->lock, flags);
+	val = asic3_read_register(asic, bank + ASIC3_GPIO_MASK);
+	val &= ~(1 << index);
+	asic3_write_register(asic, bank + ASIC3_GPIO_MASK, val);
+	spin_unlock_irqrestore(&asic->lock, flags);
+}
+
+static void asic3_unmask_irq(struct irq_data *data)
+{
+	struct asic3 *asic = irq_data_get_irq_chip_data(data);
+	int regval;
+	unsigned long flags;
+
+	spin_lock_irqsave(&asic->lock, flags);
+	regval = asic3_read_register(asic,
+				     ASIC3_INTR_BASE +
+				     ASIC3_INTR_INT_MASK);
+
+	regval |= (ASIC3_INTMASK_MASK0 <<
+		   (data->irq - (asic->irq_base + ASIC3_NUM_GPIOS)));
+
+	asic3_write_register(asic,
+			     ASIC3_INTR_BASE +
+			     ASIC3_INTR_INT_MASK,
+			     regval);
+	spin_unlock_irqrestore(&asic->lock, flags);
+}
+
+static int asic3_gpio_irq_type(struct irq_data *data, unsigned int type)
+{
+	struct asic3 *asic = irq_data_get_irq_chip_data(data);
+	u32 bank, index;
+	u16 trigger, level, edge, bit;
+	unsigned long flags;
+
+	bank = asic3_irq_to_bank(asic, data->irq);
+	index = asic3_irq_to_index(asic, data->irq);
+	bit = 1<<index;
+
+	spin_lock_irqsave(&asic->lock, flags);
+	level = asic3_read_register(asic,
+				    bank + ASIC3_GPIO_LEVEL_TRIGGER);
+	edge = asic3_read_register(asic,
+				   bank + ASIC3_GPIO_EDGE_TRIGGER);
+	trigger = asic3_read_register(asic,
+				      bank + ASIC3_GPIO_TRIGGER_TYPE);
+	asic->irq_bothedge[(data->irq - asic->irq_base) >> 4] &= ~bit;
+
+	if (type == IRQ_TYPE_EDGE_RISING) {
+		trigger |= bit;
+		edge |= bit;
+	} else if (type == IRQ_TYPE_EDGE_FALLING) {
+		trigger |= bit;
+		edge &= ~bit;
+	} else if (type == IRQ_TYPE_EDGE_BOTH) {
+		trigger |= bit;
+		if (asic3_gpio_get(&asic->gpio, data->irq - asic->irq_base))
+			edge &= ~bit;
+		else
+			edge |= bit;
+		asic->irq_bothedge[(data->irq - asic->irq_base) >> 4] |= bit;
+	} else if (type == IRQ_TYPE_LEVEL_LOW) {
+		trigger &= ~bit;
+		level &= ~bit;
+	} else if (type == IRQ_TYPE_LEVEL_HIGH) {
+		trigger &= ~bit;
+		level |= bit;
+	} else {
+		/*
+		 * if type == IRQ_TYPE_NONE, we should mask interrupts, but
+		 * be careful to not unmask them if mask was also called.
+		 * Probably need internal state for mask.
+		 */
+		dev_notice(asic->dev, "irq type not changed\n");
+	}
+	asic3_write_register(asic, bank + ASIC3_GPIO_LEVEL_TRIGGER,
+			     level);
+	asic3_write_register(asic, bank + ASIC3_GPIO_EDGE_TRIGGER,
+			     edge);
+	asic3_write_register(asic, bank + ASIC3_GPIO_TRIGGER_TYPE,
+			     trigger);
+	spin_unlock_irqrestore(&asic->lock, flags);
+	return 0;
+}
+
+static struct irq_chip asic3_gpio_irq_chip = {
+	.name		= "ASIC3-GPIO",
+	.irq_ack	= asic3_mask_gpio_irq,
+	.irq_mask	= asic3_mask_gpio_irq,
+	.irq_unmask	= asic3_unmask_gpio_irq,
+	.irq_set_type	= asic3_gpio_irq_type,
+};
+
+static struct irq_chip asic3_irq_chip = {
+	.name		= "ASIC3",
+	.irq_ack	= asic3_mask_irq,
+	.irq_mask	= asic3_mask_irq,
+	.irq_unmask	= asic3_unmask_irq,
+};
+
+static int __init asic3_irq_probe(struct platform_device *pdev)
+{
+	struct asic3 *asic = platform_get_drvdata(pdev);
+	unsigned long clksel = 0;
+	unsigned int irq, irq_base;
+	int ret;
+
+	ret = platform_get_irq(pdev, 0);
+	if (ret < 0)
+		return ret;
+	asic->irq_nr = ret;
+
+	/* turn on clock to IRQ controller */
+	clksel |= CLOCK_SEL_CX;
+	asic3_write_register(asic, ASIC3_OFFSET(CLOCK, SEL),
+			     clksel);
+
+	irq_base = asic->irq_base;
+
+	for (irq = irq_base; irq < irq_base + ASIC3_NR_IRQS; irq++) {
+		if (irq < asic->irq_base + ASIC3_NUM_GPIOS)
+			irq_set_chip(irq, &asic3_gpio_irq_chip);
+		else
+			irq_set_chip(irq, &asic3_irq_chip);
+
+		irq_set_chip_data(irq, asic);
+		irq_set_handler(irq, handle_level_irq);
+		set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+	}
+
+	asic3_write_register(asic, ASIC3_OFFSET(INTR, INT_MASK),
+			     ASIC3_INTMASK_GINTMASK);
+
+	irq_set_chained_handler(asic->irq_nr, asic3_irq_demux);
+	irq_set_irq_type(asic->irq_nr, IRQ_TYPE_EDGE_RISING);
+	irq_set_handler_data(asic->irq_nr, asic);
+
+	return 0;
+}
+
+static void asic3_irq_remove(struct platform_device *pdev)
+{
+	struct asic3 *asic = platform_get_drvdata(pdev);
+	unsigned int irq, irq_base;
+
+	irq_base = asic->irq_base;
+
+	for (irq = irq_base; irq < irq_base + ASIC3_NR_IRQS; irq++) {
+		set_irq_flags(irq, 0);
+		irq_set_chip_and_handler(irq, NULL, NULL);
+		irq_set_chip_data(irq, NULL);
+	}
+	irq_set_chained_handler(asic->irq_nr, NULL);
+}
+
+/* GPIOs */
+static int asic3_gpio_direction(struct gpio_chip *chip,
+				unsigned offset, int out)
+{
+	u32 mask = ASIC3_GPIO_TO_MASK(offset), out_reg;
+	unsigned int gpio_base;
+	unsigned long flags;
+	struct asic3 *asic;
+
+	asic = container_of(chip, struct asic3, gpio);
+	gpio_base = ASIC3_GPIO_TO_BASE(offset);
+
+	if (gpio_base > ASIC3_GPIO_D_BASE) {
+		dev_err(asic->dev, "Invalid base (0x%x) for gpio %d\n",
+			gpio_base, offset);
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&asic->lock, flags);
+
+	out_reg = asic3_read_register(asic, gpio_base + ASIC3_GPIO_DIRECTION);
+
+	/* Input is 0, Output is 1 */
+	if (out)
+		out_reg |= mask;
+	else
+		out_reg &= ~mask;
+
+	asic3_write_register(asic, gpio_base + ASIC3_GPIO_DIRECTION, out_reg);
+
+	spin_unlock_irqrestore(&asic->lock, flags);
+
+	return 0;
+
+}
+
+static int asic3_gpio_direction_input(struct gpio_chip *chip,
+				      unsigned offset)
+{
+	return asic3_gpio_direction(chip, offset, 0);
+}
+
+static int asic3_gpio_direction_output(struct gpio_chip *chip,
+				       unsigned offset, int value)
+{
+	return asic3_gpio_direction(chip, offset, 1);
+}
+
+static int asic3_gpio_get(struct gpio_chip *chip,
+			  unsigned offset)
+{
+	unsigned int gpio_base;
+	u32 mask = ASIC3_GPIO_TO_MASK(offset);
+	struct asic3 *asic;
+
+	asic = container_of(chip, struct asic3, gpio);
+	gpio_base = ASIC3_GPIO_TO_BASE(offset);
+
+	if (gpio_base > ASIC3_GPIO_D_BASE) {
+		dev_err(asic->dev, "Invalid base (0x%x) for gpio %d\n",
+			gpio_base, offset);
+		return -EINVAL;
+	}
+
+	return asic3_read_register(asic, gpio_base + ASIC3_GPIO_STATUS) & mask;
+}
+
+static void asic3_gpio_set(struct gpio_chip *chip,
+			   unsigned offset, int value)
+{
+	u32 mask, out_reg;
+	unsigned int gpio_base;
+	unsigned long flags;
+	struct asic3 *asic;
+
+	asic = container_of(chip, struct asic3, gpio);
+	gpio_base = ASIC3_GPIO_TO_BASE(offset);
+
+	if (gpio_base > ASIC3_GPIO_D_BASE) {
+		dev_err(asic->dev, "Invalid base (0x%x) for gpio %d\n",
+			gpio_base, offset);
+		return;
+	}
+
+	mask = ASIC3_GPIO_TO_MASK(offset);
+
+	spin_lock_irqsave(&asic->lock, flags);
+
+	out_reg = asic3_read_register(asic, gpio_base + ASIC3_GPIO_OUT);
+
+	if (value)
+		out_reg |= mask;
+	else
+		out_reg &= ~mask;
+
+	asic3_write_register(asic, gpio_base + ASIC3_GPIO_OUT, out_reg);
+
+	spin_unlock_irqrestore(&asic->lock, flags);
+
+	return;
+}
+
+static int asic3_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+	struct asic3 *asic = container_of(chip, struct asic3, gpio);
+
+	return (offset < ASIC3_NUM_GPIOS) ? asic->irq_base + offset : -ENXIO;
+}
+
+static __init int asic3_gpio_probe(struct platform_device *pdev,
+				   u16 *gpio_config, int num)
+{
+	struct asic3 *asic = platform_get_drvdata(pdev);
+	u16 alt_reg[ASIC3_NUM_GPIO_BANKS];
+	u16 out_reg[ASIC3_NUM_GPIO_BANKS];
+	u16 dir_reg[ASIC3_NUM_GPIO_BANKS];
+	int i;
+
+	memset(alt_reg, 0, ASIC3_NUM_GPIO_BANKS * sizeof(u16));
+	memset(out_reg, 0, ASIC3_NUM_GPIO_BANKS * sizeof(u16));
+	memset(dir_reg, 0, ASIC3_NUM_GPIO_BANKS * sizeof(u16));
+
+	/* Enable all GPIOs */
+	asic3_write_register(asic, ASIC3_GPIO_OFFSET(A, MASK), 0xffff);
+	asic3_write_register(asic, ASIC3_GPIO_OFFSET(B, MASK), 0xffff);
+	asic3_write_register(asic, ASIC3_GPIO_OFFSET(C, MASK), 0xffff);
+	asic3_write_register(asic, ASIC3_GPIO_OFFSET(D, MASK), 0xffff);
+
+	for (i = 0; i < num; i++) {
+		u8 alt, pin, dir, init, bank_num, bit_num;
+		u16 config = gpio_config[i];
+
+		pin = ASIC3_CONFIG_GPIO_PIN(config);
+		alt = ASIC3_CONFIG_GPIO_ALT(config);
+		dir = ASIC3_CONFIG_GPIO_DIR(config);
+		init = ASIC3_CONFIG_GPIO_INIT(config);
+
+		bank_num = ASIC3_GPIO_TO_BANK(pin);
+		bit_num = ASIC3_GPIO_TO_BIT(pin);
+
+		alt_reg[bank_num] |= (alt << bit_num);
+		out_reg[bank_num] |= (init << bit_num);
+		dir_reg[bank_num] |= (dir << bit_num);
+	}
+
+	for (i = 0; i < ASIC3_NUM_GPIO_BANKS; i++) {
+		asic3_write_register(asic,
+				     ASIC3_BANK_TO_BASE(i) +
+				     ASIC3_GPIO_DIRECTION,
+				     dir_reg[i]);
+		asic3_write_register(asic,
+				     ASIC3_BANK_TO_BASE(i) + ASIC3_GPIO_OUT,
+				     out_reg[i]);
+		asic3_write_register(asic,
+				     ASIC3_BANK_TO_BASE(i) +
+				     ASIC3_GPIO_ALT_FUNCTION,
+				     alt_reg[i]);
+	}
+
+	return gpiochip_add(&asic->gpio);
+}
+
+static int asic3_gpio_remove(struct platform_device *pdev)
+{
+	struct asic3 *asic = platform_get_drvdata(pdev);
+
+	return gpiochip_remove(&asic->gpio);
+}
+
+static void asic3_clk_enable(struct asic3 *asic, struct asic3_clk *clk)
+{
+	unsigned long flags;
+	u32 cdex;
+
+	spin_lock_irqsave(&asic->lock, flags);
+	if (clk->enabled++ == 0) {
+		cdex = asic3_read_register(asic, ASIC3_OFFSET(CLOCK, CDEX));
+		cdex |= clk->cdex;
+		asic3_write_register(asic, ASIC3_OFFSET(CLOCK, CDEX), cdex);
+	}
+	spin_unlock_irqrestore(&asic->lock, flags);
+}
+
+static void asic3_clk_disable(struct asic3 *asic, struct asic3_clk *clk)
+{
+	unsigned long flags;
+	u32 cdex;
+
+	WARN_ON(clk->enabled == 0);
+
+	spin_lock_irqsave(&asic->lock, flags);
+	if (--clk->enabled == 0) {
+		cdex = asic3_read_register(asic, ASIC3_OFFSET(CLOCK, CDEX));
+		cdex &= ~clk->cdex;
+		asic3_write_register(asic, ASIC3_OFFSET(CLOCK, CDEX), cdex);
+	}
+	spin_unlock_irqrestore(&asic->lock, flags);
+}
+
+/* MFD cells (SPI, PWM, LED, DS1WM, MMC) */
+static struct ds1wm_driver_data ds1wm_pdata = {
+	.active_high = 1,
+	.reset_recover_delay = 1,
+};
+
+static struct resource ds1wm_resources[] = {
+	{
+		.start = ASIC3_OWM_BASE,
+		.end   = ASIC3_OWM_BASE + 0x13,
+		.flags = IORESOURCE_MEM,
+	},
+	{
+		.start = ASIC3_IRQ_OWM,
+		.end   = ASIC3_IRQ_OWM,
+		.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE,
+	},
+};
+
+static int ds1wm_enable(struct platform_device *pdev)
+{
+	struct asic3 *asic = dev_get_drvdata(pdev->dev.parent);
+
+	/* Turn on external clocks and the OWM clock */
+	asic3_clk_enable(asic, &asic->clocks[ASIC3_CLOCK_EX0]);
+	asic3_clk_enable(asic, &asic->clocks[ASIC3_CLOCK_EX1]);
+	asic3_clk_enable(asic, &asic->clocks[ASIC3_CLOCK_OWM]);
+	msleep(1);
+
+	/* Reset and enable DS1WM */
+	asic3_set_register(asic, ASIC3_OFFSET(EXTCF, RESET),
+			   ASIC3_EXTCF_OWM_RESET, 1);
+	msleep(1);
+	asic3_set_register(asic, ASIC3_OFFSET(EXTCF, RESET),
+			   ASIC3_EXTCF_OWM_RESET, 0);
+	msleep(1);
+	asic3_set_register(asic, ASIC3_OFFSET(EXTCF, SELECT),
+			   ASIC3_EXTCF_OWM_EN, 1);
+	msleep(1);
+
+	return 0;
+}
+
+static int ds1wm_disable(struct platform_device *pdev)
+{
+	struct asic3 *asic = dev_get_drvdata(pdev->dev.parent);
+
+	asic3_set_register(asic, ASIC3_OFFSET(EXTCF, SELECT),
+			   ASIC3_EXTCF_OWM_EN, 0);
+
+	asic3_clk_disable(asic, &asic->clocks[ASIC3_CLOCK_OWM]);
+	asic3_clk_disable(asic, &asic->clocks[ASIC3_CLOCK_EX0]);
+	asic3_clk_disable(asic, &asic->clocks[ASIC3_CLOCK_EX1]);
+
+	return 0;
+}
+
+static struct mfd_cell asic3_cell_ds1wm = {
+	.name          = "ds1wm",
+	.enable        = ds1wm_enable,
+	.disable       = ds1wm_disable,
+	.platform_data = &ds1wm_pdata,
+	.pdata_size    = sizeof(ds1wm_pdata),
+	.num_resources = ARRAY_SIZE(ds1wm_resources),
+	.resources     = ds1wm_resources,
+};
+
+static void asic3_mmc_pwr(struct platform_device *pdev, int state)
+{
+	struct asic3 *asic = dev_get_drvdata(pdev->dev.parent);
+
+	tmio_core_mmc_pwr(asic->tmio_cnf, 1 - asic->bus_shift, state);
+}
+
+static void asic3_mmc_clk_div(struct platform_device *pdev, int state)
+{
+	struct asic3 *asic = dev_get_drvdata(pdev->dev.parent);
+
+	tmio_core_mmc_clk_div(asic->tmio_cnf, 1 - asic->bus_shift, state);
+}
+
+static struct tmio_mmc_data asic3_mmc_data = {
+	.hclk           = 24576000,
+	.set_pwr        = asic3_mmc_pwr,
+	.set_clk_div    = asic3_mmc_clk_div,
+};
+
+static struct resource asic3_mmc_resources[] = {
+	{
+		.start = ASIC3_SD_CTRL_BASE,
+		.end   = ASIC3_SD_CTRL_BASE + 0x3ff,
+		.flags = IORESOURCE_MEM,
+	},
+	{
+		.start = 0,
+		.end   = 0,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static int asic3_mmc_enable(struct platform_device *pdev)
+{
+	struct asic3 *asic = dev_get_drvdata(pdev->dev.parent);
+
+	/* Not sure if it must be done bit by bit, but leaving as-is */
+	asic3_set_register(asic, ASIC3_OFFSET(SDHWCTRL, SDCONF),
+			   ASIC3_SDHWCTRL_LEVCD, 1);
+	asic3_set_register(asic, ASIC3_OFFSET(SDHWCTRL, SDCONF),
+			   ASIC3_SDHWCTRL_LEVWP, 1);
+	asic3_set_register(asic, ASIC3_OFFSET(SDHWCTRL, SDCONF),
+			   ASIC3_SDHWCTRL_SUSPEND, 0);
+	asic3_set_register(asic, ASIC3_OFFSET(SDHWCTRL, SDCONF),
+			   ASIC3_SDHWCTRL_PCLR, 0);
+
+	asic3_clk_enable(asic, &asic->clocks[ASIC3_CLOCK_EX0]);
+	/* CLK32 used for card detection and for interruption detection
+	 * when HCLK is stopped.
+	 */
+	asic3_clk_enable(asic, &asic->clocks[ASIC3_CLOCK_EX1]);
+	msleep(1);
+
+	/* HCLK 24.576 MHz, BCLK 12.288 MHz: */
+	asic3_write_register(asic, ASIC3_OFFSET(CLOCK, SEL),
+		CLOCK_SEL_CX | CLOCK_SEL_SD_HCLK_SEL);
+
+	asic3_clk_enable(asic, &asic->clocks[ASIC3_CLOCK_SD_HOST]);
+	asic3_clk_enable(asic, &asic->clocks[ASIC3_CLOCK_SD_BUS]);
+	msleep(1);
+
+	asic3_set_register(asic, ASIC3_OFFSET(EXTCF, SELECT),
+			   ASIC3_EXTCF_SD_MEM_ENABLE, 1);
+
+	/* Enable SD card slot 3.3V power supply */
+	asic3_set_register(asic, ASIC3_OFFSET(SDHWCTRL, SDCONF),
+			   ASIC3_SDHWCTRL_SDPWR, 1);
+
+	/* ASIC3_SD_CTRL_BASE assumes 32-bit addressing, TMIO is 16-bit */
+	tmio_core_mmc_enable(asic->tmio_cnf, 1 - asic->bus_shift,
+			     ASIC3_SD_CTRL_BASE >> 1);
+
+	return 0;
+}
+
+static int asic3_mmc_disable(struct platform_device *pdev)
+{
+	struct asic3 *asic = dev_get_drvdata(pdev->dev.parent);
+
+	/* Put in suspend mode */
+	asic3_set_register(asic, ASIC3_OFFSET(SDHWCTRL, SDCONF),
+			   ASIC3_SDHWCTRL_SUSPEND, 1);
+
+	/* Disable clocks */
+	asic3_clk_disable(asic, &asic->clocks[ASIC3_CLOCK_SD_HOST]);
+	asic3_clk_disable(asic, &asic->clocks[ASIC3_CLOCK_SD_BUS]);
+	asic3_clk_disable(asic, &asic->clocks[ASIC3_CLOCK_EX0]);
+	asic3_clk_disable(asic, &asic->clocks[ASIC3_CLOCK_EX1]);
+	return 0;
+}
+
+static struct mfd_cell asic3_cell_mmc = {
+	.name          = "tmio-mmc",
+	.enable        = asic3_mmc_enable,
+	.disable       = asic3_mmc_disable,
+	.suspend       = asic3_mmc_disable,
+	.resume        = asic3_mmc_enable,
+	.platform_data = &asic3_mmc_data,
+	.pdata_size    = sizeof(asic3_mmc_data),
+	.num_resources = ARRAY_SIZE(asic3_mmc_resources),
+	.resources     = asic3_mmc_resources,
+};
+
+static const int clock_ledn[ASIC3_NUM_LEDS] = {
+	[0] = ASIC3_CLOCK_LED0,
+	[1] = ASIC3_CLOCK_LED1,
+	[2] = ASIC3_CLOCK_LED2,
+};
+
+static int asic3_leds_enable(struct platform_device *pdev)
+{
+	const struct mfd_cell *cell = mfd_get_cell(pdev);
+	struct asic3 *asic = dev_get_drvdata(pdev->dev.parent);
+
+	asic3_clk_enable(asic, &asic->clocks[clock_ledn[cell->id]]);
+
+	return 0;
+}
+
+static int asic3_leds_disable(struct platform_device *pdev)
+{
+	const struct mfd_cell *cell = mfd_get_cell(pdev);
+	struct asic3 *asic = dev_get_drvdata(pdev->dev.parent);
+
+	asic3_clk_disable(asic, &asic->clocks[clock_ledn[cell->id]]);
+
+	return 0;
+}
+
+static int asic3_leds_suspend(struct platform_device *pdev)
+{
+	const struct mfd_cell *cell = mfd_get_cell(pdev);
+	struct asic3 *asic = dev_get_drvdata(pdev->dev.parent);
+
+	while (asic3_gpio_get(&asic->gpio, ASIC3_GPIO(C, cell->id)) != 0)
+		msleep(1);
+
+	asic3_clk_disable(asic, &asic->clocks[clock_ledn[cell->id]]);
+
+	return 0;
+}
+
+static struct mfd_cell asic3_cell_leds[ASIC3_NUM_LEDS] = {
+	[0] = {
+		.name          = "leds-asic3",
+		.id            = 0,
+		.enable        = asic3_leds_enable,
+		.disable       = asic3_leds_disable,
+		.suspend       = asic3_leds_suspend,
+		.resume        = asic3_leds_enable,
+	},
+	[1] = {
+		.name          = "leds-asic3",
+		.id            = 1,
+		.enable        = asic3_leds_enable,
+		.disable       = asic3_leds_disable,
+		.suspend       = asic3_leds_suspend,
+		.resume        = asic3_leds_enable,
+	},
+	[2] = {
+		.name          = "leds-asic3",
+		.id            = 2,
+		.enable        = asic3_leds_enable,
+		.disable       = asic3_leds_disable,
+		.suspend       = asic3_leds_suspend,
+		.resume        = asic3_leds_enable,
+	},
+};
+
+static int __init asic3_mfd_probe(struct platform_device *pdev,
+				  struct asic3_platform_data *pdata,
+				  struct resource *mem)
+{
+	struct asic3 *asic = platform_get_drvdata(pdev);
+	struct resource *mem_sdio;
+	int irq, ret;
+
+	mem_sdio = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (!mem_sdio)
+		dev_dbg(asic->dev, "no SDIO MEM resource\n");
+
+	irq = platform_get_irq(pdev, 1);
+	if (irq < 0)
+		dev_dbg(asic->dev, "no SDIO IRQ resource\n");
+
+	/* DS1WM */
+	asic3_set_register(asic, ASIC3_OFFSET(EXTCF, SELECT),
+			   ASIC3_EXTCF_OWM_SMB, 0);
+
+	ds1wm_resources[0].start >>= asic->bus_shift;
+	ds1wm_resources[0].end   >>= asic->bus_shift;
+
+	/* MMC */
+	asic->tmio_cnf = ioremap((ASIC3_SD_CONFIG_BASE >> asic->bus_shift) +
+				 mem_sdio->start,
+				 ASIC3_SD_CONFIG_SIZE >> asic->bus_shift);
+	if (!asic->tmio_cnf) {
+		ret = -ENOMEM;
+		dev_dbg(asic->dev, "Couldn't ioremap SD_CONFIG\n");
+		goto out;
+	}
+	asic3_mmc_resources[0].start >>= asic->bus_shift;
+	asic3_mmc_resources[0].end   >>= asic->bus_shift;
+
+	ret = mfd_add_devices(&pdev->dev, pdev->id,
+			&asic3_cell_ds1wm, 1, mem, asic->irq_base);
+	if (ret < 0)
+		goto out;
+
+	if (mem_sdio && (irq >= 0)) {
+		ret = mfd_add_devices(&pdev->dev, pdev->id,
+			&asic3_cell_mmc, 1, mem_sdio, irq);
+		if (ret < 0)
+			goto out;
+	}
+
+	if (pdata->leds) {
+		int i;
+
+		for (i = 0; i < ASIC3_NUM_LEDS; ++i) {
+			asic3_cell_leds[i].platform_data = &pdata->leds[i];
+			asic3_cell_leds[i].pdata_size = sizeof(pdata->leds[i]);
+		}
+		ret = mfd_add_devices(&pdev->dev, 0,
+			asic3_cell_leds, ASIC3_NUM_LEDS, NULL, 0);
+	}
+
+ out:
+	return ret;
+}
+
+static void asic3_mfd_remove(struct platform_device *pdev)
+{
+	struct asic3 *asic = platform_get_drvdata(pdev);
+
+	mfd_remove_devices(&pdev->dev);
+	iounmap(asic->tmio_cnf);
+}
+
+/* Core */
+static int __init asic3_probe(struct platform_device *pdev)
+{
+	struct asic3_platform_data *pdata = pdev->dev.platform_data;
+	struct asic3 *asic;
+	struct resource *mem;
+	unsigned long clksel;
+	int ret = 0;
+
+	asic = kzalloc(sizeof(struct asic3), GFP_KERNEL);
+	if (asic == NULL) {
+		printk(KERN_ERR "kzalloc failed\n");
+		return -ENOMEM;
+	}
+
+	spin_lock_init(&asic->lock);
+	platform_set_drvdata(pdev, asic);
+	asic->dev = &pdev->dev;
+
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!mem) {
+		ret = -ENOMEM;
+		dev_err(asic->dev, "no MEM resource\n");
+		goto out_free;
+	}
+
+	asic->mapping = ioremap(mem->start, resource_size(mem));
+	if (!asic->mapping) {
+		ret = -ENOMEM;
+		dev_err(asic->dev, "Couldn't ioremap\n");
+		goto out_free;
+	}
+
+	asic->irq_base = pdata->irq_base;
+
+	/* calculate bus shift from mem resource */
+	asic->bus_shift = 2 - (resource_size(mem) >> 12);
+
+	clksel = 0;
+	asic3_write_register(asic, ASIC3_OFFSET(CLOCK, SEL), clksel);
+
+	ret = asic3_irq_probe(pdev);
+	if (ret < 0) {
+		dev_err(asic->dev, "Couldn't probe IRQs\n");
+		goto out_unmap;
+	}
+
+	asic->gpio.label = "asic3";
+	asic->gpio.base = pdata->gpio_base;
+	asic->gpio.ngpio = ASIC3_NUM_GPIOS;
+	asic->gpio.get = asic3_gpio_get;
+	asic->gpio.set = asic3_gpio_set;
+	asic->gpio.direction_input = asic3_gpio_direction_input;
+	asic->gpio.direction_output = asic3_gpio_direction_output;
+	asic->gpio.to_irq = asic3_gpio_to_irq;
+
+	ret = asic3_gpio_probe(pdev,
+			       pdata->gpio_config,
+			       pdata->gpio_config_num);
+	if (ret < 0) {
+		dev_err(asic->dev, "GPIO probe failed\n");
+		goto out_irq;
+	}
+
+	/* Making a per-device copy is only needed for the
+	 * theoretical case of multiple ASIC3s on one board:
+	 */
+	memcpy(asic->clocks, asic3_clk_init, sizeof(asic3_clk_init));
+
+	asic3_mfd_probe(pdev, pdata, mem);
+
+	dev_info(asic->dev, "ASIC3 Core driver\n");
+
+	return 0;
+
+ out_irq:
+	asic3_irq_remove(pdev);
+
+ out_unmap:
+	iounmap(asic->mapping);
+
+ out_free:
+	kfree(asic);
+
+	return ret;
+}
+
+static int __devexit asic3_remove(struct platform_device *pdev)
+{
+	int ret;
+	struct asic3 *asic = platform_get_drvdata(pdev);
+
+	asic3_mfd_remove(pdev);
+
+	ret = asic3_gpio_remove(pdev);
+	if (ret < 0)
+		return ret;
+	asic3_irq_remove(pdev);
+
+	asic3_write_register(asic, ASIC3_OFFSET(CLOCK, SEL), 0);
+
+	iounmap(asic->mapping);
+
+	kfree(asic);
+
+	return 0;
+}
+
+static void asic3_shutdown(struct platform_device *pdev)
+{
+}
+
+static struct platform_driver asic3_device_driver = {
+	.driver		= {
+		.name	= "asic3",
+	},
+	.remove		= __devexit_p(asic3_remove),
+	.shutdown	= asic3_shutdown,
+};
+
+static int __init asic3_init(void)
+{
+	int retval = 0;
+	retval = platform_driver_probe(&asic3_device_driver, asic3_probe);
+	return retval;
+}
+
+subsys_initcall(asic3_init);
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/cs5535-mfd.c b/ap/os/linux/linux-3.4.x/drivers/mfd/cs5535-mfd.c
new file mode 100644
index 0000000..315fef5
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/cs5535-mfd.c
@@ -0,0 +1,204 @@
+/*
+ * cs5535-mfd.c - core MFD driver for CS5535/CS5536 southbridges
+ *
+ * The CS5535 and CS5536 has an ISA bridge on the PCI bus that is
+ * used for accessing GPIOs, MFGPTs, ACPI, etc.  Each subdevice has
+ * an IO range that's specified in a single BAR.  The BAR order is
+ * hardcoded in the CS553x specifications.
+ *
+ * Copyright (c) 2010  Andres Salomon <dilinger@queued.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <asm/olpc.h>
+
+#define DRV_NAME "cs5535-mfd"
+
+enum cs5535_mfd_bars {
+	SMB_BAR = 0,
+	GPIO_BAR = 1,
+	MFGPT_BAR = 2,
+	PMS_BAR = 4,
+	ACPI_BAR = 5,
+	NR_BARS,
+};
+
+static int cs5535_mfd_res_enable(struct platform_device *pdev)
+{
+	struct resource *res;
+
+	res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "can't fetch device resource info\n");
+		return -EIO;
+	}
+
+	if (!request_region(res->start, resource_size(res), DRV_NAME)) {
+		dev_err(&pdev->dev, "can't request region\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int cs5535_mfd_res_disable(struct platform_device *pdev)
+{
+	struct resource *res;
+	res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "can't fetch device resource info\n");
+		return -EIO;
+	}
+
+	release_region(res->start, resource_size(res));
+	return 0;
+}
+
+static __devinitdata struct resource cs5535_mfd_resources[NR_BARS];
+
+static __devinitdata struct mfd_cell cs5535_mfd_cells[] = {
+	{
+		.id = SMB_BAR,
+		.name = "cs5535-smb",
+		.num_resources = 1,
+		.resources = &cs5535_mfd_resources[SMB_BAR],
+	},
+	{
+		.id = GPIO_BAR,
+		.name = "cs5535-gpio",
+		.num_resources = 1,
+		.resources = &cs5535_mfd_resources[GPIO_BAR],
+	},
+	{
+		.id = MFGPT_BAR,
+		.name = "cs5535-mfgpt",
+		.num_resources = 1,
+		.resources = &cs5535_mfd_resources[MFGPT_BAR],
+	},
+	{
+		.id = PMS_BAR,
+		.name = "cs5535-pms",
+		.num_resources = 1,
+		.resources = &cs5535_mfd_resources[PMS_BAR],
+
+		.enable = cs5535_mfd_res_enable,
+		.disable = cs5535_mfd_res_disable,
+	},
+	{
+		.id = ACPI_BAR,
+		.name = "cs5535-acpi",
+		.num_resources = 1,
+		.resources = &cs5535_mfd_resources[ACPI_BAR],
+
+		.enable = cs5535_mfd_res_enable,
+		.disable = cs5535_mfd_res_disable,
+	},
+};
+
+#ifdef CONFIG_OLPC
+static void __devinit cs5535_clone_olpc_cells(void)
+{
+	const char *acpi_clones[] = { "olpc-xo1-pm-acpi", "olpc-xo1-sci-acpi" };
+
+	if (!machine_is_olpc())
+		return;
+
+	mfd_clone_cell("cs5535-acpi", acpi_clones, ARRAY_SIZE(acpi_clones));
+}
+#else
+static void cs5535_clone_olpc_cells(void) { }
+#endif
+
+static int __devinit cs5535_mfd_probe(struct pci_dev *pdev,
+		const struct pci_device_id *id)
+{
+	int err, i;
+
+	err = pci_enable_device(pdev);
+	if (err)
+		return err;
+
+	/* fill in IO range for each cell; subdrivers handle the region */
+	for (i = 0; i < ARRAY_SIZE(cs5535_mfd_cells); i++) {
+		int bar = cs5535_mfd_cells[i].id;
+		struct resource *r = &cs5535_mfd_resources[bar];
+
+		r->flags = IORESOURCE_IO;
+		r->start = pci_resource_start(pdev, bar);
+		r->end = pci_resource_end(pdev, bar);
+
+		/* id is used for temporarily storing BAR; unset it now */
+		cs5535_mfd_cells[i].id = 0;
+	}
+
+	err = mfd_add_devices(&pdev->dev, -1, cs5535_mfd_cells,
+			ARRAY_SIZE(cs5535_mfd_cells), NULL, 0);
+	if (err) {
+		dev_err(&pdev->dev, "MFD add devices failed: %d\n", err);
+		goto err_disable;
+	}
+	cs5535_clone_olpc_cells();
+
+	dev_info(&pdev->dev, "%zu devices registered.\n",
+			ARRAY_SIZE(cs5535_mfd_cells));
+
+	return 0;
+
+err_disable:
+	pci_disable_device(pdev);
+	return err;
+}
+
+static void __devexit cs5535_mfd_remove(struct pci_dev *pdev)
+{
+	mfd_remove_devices(&pdev->dev);
+	pci_disable_device(pdev);
+}
+
+static DEFINE_PCI_DEVICE_TABLE(cs5535_mfd_pci_tbl) = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_ISA) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA) },
+	{ 0, }
+};
+MODULE_DEVICE_TABLE(pci, cs5535_mfd_pci_tbl);
+
+static struct pci_driver cs5535_mfd_driver = {
+	.name = DRV_NAME,
+	.id_table = cs5535_mfd_pci_tbl,
+	.probe = cs5535_mfd_probe,
+	.remove = __devexit_p(cs5535_mfd_remove),
+};
+
+static int __init cs5535_mfd_init(void)
+{
+	return pci_register_driver(&cs5535_mfd_driver);
+}
+
+static void __exit cs5535_mfd_exit(void)
+{
+	pci_unregister_driver(&cs5535_mfd_driver);
+}
+
+module_init(cs5535_mfd_init);
+module_exit(cs5535_mfd_exit);
+
+MODULE_AUTHOR("Andres Salomon <dilinger@queued.net>");
+MODULE_DESCRIPTION("MFD driver for CS5535/CS5536 southbridge's ISA PCI device");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/da903x.c b/ap/os/linux/linux-3.4.x/drivers/mfd/da903x.c
new file mode 100644
index 0000000..1924b85
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/da903x.c
@@ -0,0 +1,582 @@
+/*
+ * Base driver for Dialog Semiconductor DA9030/DA9034
+ *
+ * Copyright (C) 2008 Compulab, Ltd.
+ * 	Mike Rapoport <mike@compulab.co.il>
+ *
+ * Copyright (C) 2006-2008 Marvell International Ltd.
+ * 	Eric Miao <eric.miao@marvell.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/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/mfd/da903x.h>
+#include <linux/slab.h>
+
+#define DA9030_CHIP_ID		0x00
+#define DA9030_EVENT_A		0x01
+#define DA9030_EVENT_B		0x02
+#define DA9030_EVENT_C		0x03
+#define DA9030_STATUS		0x04
+#define DA9030_IRQ_MASK_A	0x05
+#define DA9030_IRQ_MASK_B	0x06
+#define DA9030_IRQ_MASK_C	0x07
+#define DA9030_SYS_CTRL_A	0x08
+#define DA9030_SYS_CTRL_B	0x09
+#define DA9030_FAULT_LOG	0x0a
+
+#define DA9034_CHIP_ID		0x00
+#define DA9034_EVENT_A		0x01
+#define DA9034_EVENT_B		0x02
+#define DA9034_EVENT_C		0x03
+#define DA9034_EVENT_D		0x04
+#define DA9034_STATUS_A		0x05
+#define DA9034_STATUS_B		0x06
+#define DA9034_IRQ_MASK_A	0x07
+#define DA9034_IRQ_MASK_B	0x08
+#define DA9034_IRQ_MASK_C	0x09
+#define DA9034_IRQ_MASK_D	0x0a
+#define DA9034_SYS_CTRL_A	0x0b
+#define DA9034_SYS_CTRL_B	0x0c
+#define DA9034_FAULT_LOG	0x0d
+
+struct da903x_chip;
+
+struct da903x_chip_ops {
+	int	(*init_chip)(struct da903x_chip *);
+	int	(*unmask_events)(struct da903x_chip *, unsigned int events);
+	int	(*mask_events)(struct da903x_chip *, unsigned int events);
+	int	(*read_events)(struct da903x_chip *, unsigned int *events);
+	int	(*read_status)(struct da903x_chip *, unsigned int *status);
+};
+
+struct da903x_chip {
+	struct i2c_client	*client;
+	struct device		*dev;
+	struct da903x_chip_ops	*ops;
+
+	int			type;
+	uint32_t		events_mask;
+
+	struct mutex		lock;
+	struct work_struct	irq_work;
+
+	struct blocking_notifier_head notifier_list;
+};
+
+static inline int __da903x_read(struct i2c_client *client,
+				int reg, uint8_t *val)
+{
+	int ret;
+
+	ret = i2c_smbus_read_byte_data(client, reg);
+	if (ret < 0) {
+		dev_err(&client->dev, "failed reading at 0x%02x\n", reg);
+		return ret;
+	}
+
+	*val = (uint8_t)ret;
+	return 0;
+}
+
+static inline int __da903x_reads(struct i2c_client *client, int reg,
+				 int len, uint8_t *val)
+{
+	int ret;
+
+	ret = i2c_smbus_read_i2c_block_data(client, reg, len, val);
+	if (ret < 0) {
+		dev_err(&client->dev, "failed reading from 0x%02x\n", reg);
+		return ret;
+	}
+	return 0;
+}
+
+static inline int __da903x_write(struct i2c_client *client,
+				 int reg, uint8_t val)
+{
+	int ret;
+
+	ret = i2c_smbus_write_byte_data(client, reg, val);
+	if (ret < 0) {
+		dev_err(&client->dev, "failed writing 0x%02x to 0x%02x\n",
+				val, reg);
+		return ret;
+	}
+	return 0;
+}
+
+static inline int __da903x_writes(struct i2c_client *client, int reg,
+				  int len, uint8_t *val)
+{
+	int ret;
+
+	ret = i2c_smbus_write_i2c_block_data(client, reg, len, val);
+	if (ret < 0) {
+		dev_err(&client->dev, "failed writings to 0x%02x\n", reg);
+		return ret;
+	}
+	return 0;
+}
+
+int da903x_register_notifier(struct device *dev, struct notifier_block *nb,
+				unsigned int events)
+{
+	struct da903x_chip *chip = dev_get_drvdata(dev);
+
+	chip->ops->unmask_events(chip, events);
+	return blocking_notifier_chain_register(&chip->notifier_list, nb);
+}
+EXPORT_SYMBOL_GPL(da903x_register_notifier);
+
+int da903x_unregister_notifier(struct device *dev, struct notifier_block *nb,
+				unsigned int events)
+{
+	struct da903x_chip *chip = dev_get_drvdata(dev);
+
+	chip->ops->mask_events(chip, events);
+	return blocking_notifier_chain_unregister(&chip->notifier_list, nb);
+}
+EXPORT_SYMBOL_GPL(da903x_unregister_notifier);
+
+int da903x_write(struct device *dev, int reg, uint8_t val)
+{
+	return __da903x_write(to_i2c_client(dev), reg, val);
+}
+EXPORT_SYMBOL_GPL(da903x_write);
+
+int da903x_writes(struct device *dev, int reg, int len, uint8_t *val)
+{
+	return __da903x_writes(to_i2c_client(dev), reg, len, val);
+}
+EXPORT_SYMBOL_GPL(da903x_writes);
+
+int da903x_read(struct device *dev, int reg, uint8_t *val)
+{
+	return __da903x_read(to_i2c_client(dev), reg, val);
+}
+EXPORT_SYMBOL_GPL(da903x_read);
+
+int da903x_reads(struct device *dev, int reg, int len, uint8_t *val)
+{
+	return __da903x_reads(to_i2c_client(dev), reg, len, val);
+}
+EXPORT_SYMBOL_GPL(da903x_reads);
+
+int da903x_set_bits(struct device *dev, int reg, uint8_t bit_mask)
+{
+	struct da903x_chip *chip = dev_get_drvdata(dev);
+	uint8_t reg_val;
+	int ret = 0;
+
+	mutex_lock(&chip->lock);
+
+	ret = __da903x_read(chip->client, reg, &reg_val);
+	if (ret)
+		goto out;
+
+	if ((reg_val & bit_mask) != bit_mask) {
+		reg_val |= bit_mask;
+		ret = __da903x_write(chip->client, reg, reg_val);
+	}
+out:
+	mutex_unlock(&chip->lock);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(da903x_set_bits);
+
+int da903x_clr_bits(struct device *dev, int reg, uint8_t bit_mask)
+{
+	struct da903x_chip *chip = dev_get_drvdata(dev);
+	uint8_t reg_val;
+	int ret = 0;
+
+	mutex_lock(&chip->lock);
+
+	ret = __da903x_read(chip->client, reg, &reg_val);
+	if (ret)
+		goto out;
+
+	if (reg_val & bit_mask) {
+		reg_val &= ~bit_mask;
+		ret = __da903x_write(chip->client, reg, reg_val);
+	}
+out:
+	mutex_unlock(&chip->lock);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(da903x_clr_bits);
+
+int da903x_update(struct device *dev, int reg, uint8_t val, uint8_t mask)
+{
+	struct da903x_chip *chip = dev_get_drvdata(dev);
+	uint8_t reg_val;
+	int ret = 0;
+
+	mutex_lock(&chip->lock);
+
+	ret = __da903x_read(chip->client, reg, &reg_val);
+	if (ret)
+		goto out;
+
+	if ((reg_val & mask) != val) {
+		reg_val = (reg_val & ~mask) | val;
+		ret = __da903x_write(chip->client, reg, reg_val);
+	}
+out:
+	mutex_unlock(&chip->lock);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(da903x_update);
+
+int da903x_query_status(struct device *dev, unsigned int sbits)
+{
+	struct da903x_chip *chip = dev_get_drvdata(dev);
+	unsigned int status = 0;
+
+	chip->ops->read_status(chip, &status);
+	return ((status & sbits) == sbits);
+}
+EXPORT_SYMBOL(da903x_query_status);
+
+static int __devinit da9030_init_chip(struct da903x_chip *chip)
+{
+	uint8_t chip_id;
+	int err;
+
+	err = __da903x_read(chip->client, DA9030_CHIP_ID, &chip_id);
+	if (err)
+		return err;
+
+	err = __da903x_write(chip->client, DA9030_SYS_CTRL_A, 0xE8);
+	if (err)
+		return err;
+
+	dev_info(chip->dev, "DA9030 (CHIP ID: 0x%02x) detected\n", chip_id);
+	return 0;
+}
+
+static int da9030_unmask_events(struct da903x_chip *chip, unsigned int events)
+{
+	uint8_t v[3];
+
+	chip->events_mask &= ~events;
+
+	v[0] = (chip->events_mask & 0xff);
+	v[1] = (chip->events_mask >> 8) & 0xff;
+	v[2] = (chip->events_mask >> 16) & 0xff;
+
+	return __da903x_writes(chip->client, DA9030_IRQ_MASK_A, 3, v);
+}
+
+static int da9030_mask_events(struct da903x_chip *chip, unsigned int events)
+{
+	uint8_t v[3];
+
+	chip->events_mask |= events;
+
+	v[0] = (chip->events_mask & 0xff);
+	v[1] = (chip->events_mask >> 8) & 0xff;
+	v[2] = (chip->events_mask >> 16) & 0xff;
+
+	return __da903x_writes(chip->client, DA9030_IRQ_MASK_A, 3, v);
+}
+
+static int da9030_read_events(struct da903x_chip *chip, unsigned int *events)
+{
+	uint8_t v[3] = {0, 0, 0};
+	int ret;
+
+	ret = __da903x_reads(chip->client, DA9030_EVENT_A, 3, v);
+	if (ret < 0)
+		return ret;
+
+	*events = (v[2] << 16) | (v[1] << 8) | v[0];
+	return 0;
+}
+
+static int da9030_read_status(struct da903x_chip *chip, unsigned int *status)
+{
+	return __da903x_read(chip->client, DA9030_STATUS, (uint8_t *)status);
+}
+
+static int da9034_init_chip(struct da903x_chip *chip)
+{
+	uint8_t chip_id;
+	int err;
+
+	err = __da903x_read(chip->client, DA9034_CHIP_ID, &chip_id);
+	if (err)
+		return err;
+
+	err = __da903x_write(chip->client, DA9034_SYS_CTRL_A, 0xE8);
+	if (err)
+		return err;
+
+	/* avoid SRAM power off during sleep*/
+	__da903x_write(chip->client, 0x10, 0x07);
+	__da903x_write(chip->client, 0x11, 0xff);
+	__da903x_write(chip->client, 0x12, 0xff);
+
+	/* Enable the ONKEY power down functionality */
+	__da903x_write(chip->client, DA9034_SYS_CTRL_B, 0x20);
+	__da903x_write(chip->client, DA9034_SYS_CTRL_A, 0x60);
+
+	/* workaround to make LEDs work */
+	__da903x_write(chip->client, 0x90, 0x01);
+	__da903x_write(chip->client, 0xB0, 0x08);
+
+	/* make ADTV1 and SDTV1 effective */
+	__da903x_write(chip->client, 0x20, 0x00);
+
+	dev_info(chip->dev, "DA9034 (CHIP ID: 0x%02x) detected\n", chip_id);
+	return 0;
+}
+
+static int da9034_unmask_events(struct da903x_chip *chip, unsigned int events)
+{
+	uint8_t v[4];
+
+	chip->events_mask &= ~events;
+
+	v[0] = (chip->events_mask & 0xff);
+	v[1] = (chip->events_mask >> 8) & 0xff;
+	v[2] = (chip->events_mask >> 16) & 0xff;
+	v[3] = (chip->events_mask >> 24) & 0xff;
+
+	return __da903x_writes(chip->client, DA9034_IRQ_MASK_A, 4, v);
+}
+
+static int da9034_mask_events(struct da903x_chip *chip, unsigned int events)
+{
+	uint8_t v[4];
+
+	chip->events_mask |= events;
+
+	v[0] = (chip->events_mask & 0xff);
+	v[1] = (chip->events_mask >> 8) & 0xff;
+	v[2] = (chip->events_mask >> 16) & 0xff;
+	v[3] = (chip->events_mask >> 24) & 0xff;
+
+	return __da903x_writes(chip->client, DA9034_IRQ_MASK_A, 4, v);
+}
+
+static int da9034_read_events(struct da903x_chip *chip, unsigned int *events)
+{
+	uint8_t v[4] = {0, 0, 0, 0};
+	int ret;
+
+	ret = __da903x_reads(chip->client, DA9034_EVENT_A, 4, v);
+	if (ret < 0)
+		return ret;
+
+	*events = (v[3] << 24) | (v[2] << 16) | (v[1] << 8) | v[0];
+	return 0;
+}
+
+static int da9034_read_status(struct da903x_chip *chip, unsigned int *status)
+{
+	uint8_t v[2] = {0, 0};
+	int ret = 0;
+
+	ret = __da903x_reads(chip->client, DA9034_STATUS_A, 2, v);
+	if (ret)
+		return ret;
+
+	*status = (v[1] << 8) | v[0];
+	return 0;
+}
+
+static void da903x_irq_work(struct work_struct *work)
+{
+	struct da903x_chip *chip =
+		container_of(work, struct da903x_chip, irq_work);
+	unsigned int events = 0;
+
+	while (1) {
+		if (chip->ops->read_events(chip, &events))
+			break;
+
+		events &= ~chip->events_mask;
+		if (events == 0)
+			break;
+
+		blocking_notifier_call_chain(
+				&chip->notifier_list, events, NULL);
+	}
+	enable_irq(chip->client->irq);
+}
+
+static irqreturn_t da903x_irq_handler(int irq, void *data)
+{
+	struct da903x_chip *chip = data;
+
+	disable_irq_nosync(irq);
+	(void)schedule_work(&chip->irq_work);
+
+	return IRQ_HANDLED;
+}
+
+static struct da903x_chip_ops da903x_ops[] = {
+	[0] = {
+		.init_chip	= da9030_init_chip,
+		.unmask_events	= da9030_unmask_events,
+		.mask_events	= da9030_mask_events,
+		.read_events	= da9030_read_events,
+		.read_status	= da9030_read_status,
+	},
+	[1] = {
+		.init_chip	= da9034_init_chip,
+		.unmask_events	= da9034_unmask_events,
+		.mask_events	= da9034_mask_events,
+		.read_events	= da9034_read_events,
+		.read_status	= da9034_read_status,
+	}
+};
+
+static const struct i2c_device_id da903x_id_table[] = {
+	{ "da9030", 0 },
+	{ "da9034", 1 },
+	{ },
+};
+MODULE_DEVICE_TABLE(i2c, da903x_id_table);
+
+static int __remove_subdev(struct device *dev, void *unused)
+{
+	platform_device_unregister(to_platform_device(dev));
+	return 0;
+}
+
+static int da903x_remove_subdevs(struct da903x_chip *chip)
+{
+	return device_for_each_child(chip->dev, NULL, __remove_subdev);
+}
+
+static int __devinit da903x_add_subdevs(struct da903x_chip *chip,
+					struct da903x_platform_data *pdata)
+{
+	struct da903x_subdev_info *subdev;
+	struct platform_device *pdev;
+	int i, ret = 0;
+
+	for (i = 0; i < pdata->num_subdevs; i++) {
+		subdev = &pdata->subdevs[i];
+
+		pdev = platform_device_alloc(subdev->name, subdev->id);
+		if (!pdev) {
+			ret = -ENOMEM;
+			goto failed;
+		}
+
+		pdev->dev.parent = chip->dev;
+		pdev->dev.platform_data = subdev->platform_data;
+
+		ret = platform_device_add(pdev);
+		if (ret) {
+			platform_device_put(pdev);
+			goto failed;
+		}
+	}
+	return 0;
+
+failed:
+	da903x_remove_subdevs(chip);
+	return ret;
+}
+
+static int __devinit da903x_probe(struct i2c_client *client,
+				  const struct i2c_device_id *id)
+{
+	struct da903x_platform_data *pdata = client->dev.platform_data;
+	struct da903x_chip *chip;
+	unsigned int tmp;
+	int ret;
+
+	chip = kzalloc(sizeof(struct da903x_chip), GFP_KERNEL);
+	if (chip == NULL)
+		return -ENOMEM;
+
+	chip->client = client;
+	chip->dev = &client->dev;
+	chip->ops = &da903x_ops[id->driver_data];
+
+	mutex_init(&chip->lock);
+	INIT_WORK(&chip->irq_work, da903x_irq_work);
+	BLOCKING_INIT_NOTIFIER_HEAD(&chip->notifier_list);
+
+	i2c_set_clientdata(client, chip);
+
+	ret = chip->ops->init_chip(chip);
+	if (ret)
+		goto out_free_chip;
+
+	/* mask and clear all IRQs */
+	chip->events_mask = 0xffffffff;
+	chip->ops->mask_events(chip, chip->events_mask);
+	chip->ops->read_events(chip, &tmp);
+
+	ret = request_irq(client->irq, da903x_irq_handler,
+			IRQF_TRIGGER_FALLING,
+			"da903x", chip);
+	if (ret) {
+		dev_err(&client->dev, "failed to request irq %d\n",
+				client->irq);
+		goto out_free_chip;
+	}
+
+	ret = da903x_add_subdevs(chip, pdata);
+	if (ret)
+		goto out_free_irq;
+
+	return 0;
+
+out_free_irq:
+	free_irq(client->irq, chip);
+out_free_chip:
+	kfree(chip);
+	return ret;
+}
+
+static int __devexit da903x_remove(struct i2c_client *client)
+{
+	struct da903x_chip *chip = i2c_get_clientdata(client);
+
+	da903x_remove_subdevs(chip);
+	free_irq(client->irq, chip);
+	kfree(chip);
+	return 0;
+}
+
+static struct i2c_driver da903x_driver = {
+	.driver	= {
+		.name	= "da903x",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= da903x_probe,
+	.remove		= __devexit_p(da903x_remove),
+	.id_table	= da903x_id_table,
+};
+
+static int __init da903x_init(void)
+{
+	return i2c_add_driver(&da903x_driver);
+}
+subsys_initcall(da903x_init);
+
+static void __exit da903x_exit(void)
+{
+	i2c_del_driver(&da903x_driver);
+}
+module_exit(da903x_exit);
+
+MODULE_DESCRIPTION("PMIC Driver for Dialog Semiconductor DA9034");
+MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>"
+	      "Mike Rapoport <mike@compulab.co.il>");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/da9052-core.c b/ap/os/linux/linux-3.4.x/drivers/mfd/da9052-core.c
new file mode 100644
index 0000000..7ff313f
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/da9052-core.c
@@ -0,0 +1,691 @@
+/*
+ * Device access for Dialog DA9052 PMICs.
+ *
+ * Copyright(c) 2011 Dialog Semiconductor Ltd.
+ *
+ * Author: David Dajun Chen <dchen@diasemi.com>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/mfd/core.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+
+#include <linux/mfd/da9052/da9052.h>
+#include <linux/mfd/da9052/pdata.h>
+#include <linux/mfd/da9052/reg.h>
+
+#define DA9052_NUM_IRQ_REGS		4
+#define DA9052_IRQ_MASK_POS_1		0x01
+#define DA9052_IRQ_MASK_POS_2		0x02
+#define DA9052_IRQ_MASK_POS_3		0x04
+#define DA9052_IRQ_MASK_POS_4		0x08
+#define DA9052_IRQ_MASK_POS_5		0x10
+#define DA9052_IRQ_MASK_POS_6		0x20
+#define DA9052_IRQ_MASK_POS_7		0x40
+#define DA9052_IRQ_MASK_POS_8		0x80
+
+static bool da9052_reg_readable(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case DA9052_PAGE0_CON_REG:
+	case DA9052_STATUS_A_REG:
+	case DA9052_STATUS_B_REG:
+	case DA9052_STATUS_C_REG:
+	case DA9052_STATUS_D_REG:
+	case DA9052_EVENT_A_REG:
+	case DA9052_EVENT_B_REG:
+	case DA9052_EVENT_C_REG:
+	case DA9052_EVENT_D_REG:
+	case DA9052_FAULTLOG_REG:
+	case DA9052_IRQ_MASK_A_REG:
+	case DA9052_IRQ_MASK_B_REG:
+	case DA9052_IRQ_MASK_C_REG:
+	case DA9052_IRQ_MASK_D_REG:
+	case DA9052_CONTROL_A_REG:
+	case DA9052_CONTROL_B_REG:
+	case DA9052_CONTROL_C_REG:
+	case DA9052_CONTROL_D_REG:
+	case DA9052_PDDIS_REG:
+	case DA9052_INTERFACE_REG:
+	case DA9052_RESET_REG:
+	case DA9052_GPIO_0_1_REG:
+	case DA9052_GPIO_2_3_REG:
+	case DA9052_GPIO_4_5_REG:
+	case DA9052_GPIO_6_7_REG:
+	case DA9052_GPIO_14_15_REG:
+	case DA9052_ID_0_1_REG:
+	case DA9052_ID_2_3_REG:
+	case DA9052_ID_4_5_REG:
+	case DA9052_ID_6_7_REG:
+	case DA9052_ID_8_9_REG:
+	case DA9052_ID_10_11_REG:
+	case DA9052_ID_12_13_REG:
+	case DA9052_ID_14_15_REG:
+	case DA9052_ID_16_17_REG:
+	case DA9052_ID_18_19_REG:
+	case DA9052_ID_20_21_REG:
+	case DA9052_SEQ_STATUS_REG:
+	case DA9052_SEQ_A_REG:
+	case DA9052_SEQ_B_REG:
+	case DA9052_SEQ_TIMER_REG:
+	case DA9052_BUCKA_REG:
+	case DA9052_BUCKB_REG:
+	case DA9052_BUCKCORE_REG:
+	case DA9052_BUCKPRO_REG:
+	case DA9052_BUCKMEM_REG:
+	case DA9052_BUCKPERI_REG:
+	case DA9052_LDO1_REG:
+	case DA9052_LDO2_REG:
+	case DA9052_LDO3_REG:
+	case DA9052_LDO4_REG:
+	case DA9052_LDO5_REG:
+	case DA9052_LDO6_REG:
+	case DA9052_LDO7_REG:
+	case DA9052_LDO8_REG:
+	case DA9052_LDO9_REG:
+	case DA9052_LDO10_REG:
+	case DA9052_SUPPLY_REG:
+	case DA9052_PULLDOWN_REG:
+	case DA9052_CHGBUCK_REG:
+	case DA9052_WAITCONT_REG:
+	case DA9052_ISET_REG:
+	case DA9052_BATCHG_REG:
+	case DA9052_CHG_CONT_REG:
+	case DA9052_INPUT_CONT_REG:
+	case DA9052_CHG_TIME_REG:
+	case DA9052_BBAT_CONT_REG:
+	case DA9052_BOOST_REG:
+	case DA9052_LED_CONT_REG:
+	case DA9052_LEDMIN123_REG:
+	case DA9052_LED1_CONF_REG:
+	case DA9052_LED2_CONF_REG:
+	case DA9052_LED3_CONF_REG:
+	case DA9052_LED1CONT_REG:
+	case DA9052_LED2CONT_REG:
+	case DA9052_LED3CONT_REG:
+	case DA9052_LED_CONT_4_REG:
+	case DA9052_LED_CONT_5_REG:
+	case DA9052_ADC_MAN_REG:
+	case DA9052_ADC_CONT_REG:
+	case DA9052_ADC_RES_L_REG:
+	case DA9052_ADC_RES_H_REG:
+	case DA9052_VDD_RES_REG:
+	case DA9052_VDD_MON_REG:
+	case DA9052_ICHG_AV_REG:
+	case DA9052_ICHG_THD_REG:
+	case DA9052_ICHG_END_REG:
+	case DA9052_TBAT_RES_REG:
+	case DA9052_TBAT_HIGHP_REG:
+	case DA9052_TBAT_HIGHN_REG:
+	case DA9052_TBAT_LOW_REG:
+	case DA9052_T_OFFSET_REG:
+	case DA9052_ADCIN4_RES_REG:
+	case DA9052_AUTO4_HIGH_REG:
+	case DA9052_AUTO4_LOW_REG:
+	case DA9052_ADCIN5_RES_REG:
+	case DA9052_AUTO5_HIGH_REG:
+	case DA9052_AUTO5_LOW_REG:
+	case DA9052_ADCIN6_RES_REG:
+	case DA9052_AUTO6_HIGH_REG:
+	case DA9052_AUTO6_LOW_REG:
+	case DA9052_TJUNC_RES_REG:
+	case DA9052_TSI_CONT_A_REG:
+	case DA9052_TSI_CONT_B_REG:
+	case DA9052_TSI_X_MSB_REG:
+	case DA9052_TSI_Y_MSB_REG:
+	case DA9052_TSI_LSB_REG:
+	case DA9052_TSI_Z_MSB_REG:
+	case DA9052_COUNT_S_REG:
+	case DA9052_COUNT_MI_REG:
+	case DA9052_COUNT_H_REG:
+	case DA9052_COUNT_D_REG:
+	case DA9052_COUNT_MO_REG:
+	case DA9052_COUNT_Y_REG:
+	case DA9052_ALARM_MI_REG:
+	case DA9052_ALARM_H_REG:
+	case DA9052_ALARM_D_REG:
+	case DA9052_ALARM_MO_REG:
+	case DA9052_ALARM_Y_REG:
+	case DA9052_SECOND_A_REG:
+	case DA9052_SECOND_B_REG:
+	case DA9052_SECOND_C_REG:
+	case DA9052_SECOND_D_REG:
+	case DA9052_PAGE1_CON_REG:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool da9052_reg_writeable(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case DA9052_PAGE0_CON_REG:
+	case DA9052_EVENT_A_REG:
+	case DA9052_EVENT_B_REG:
+	case DA9052_EVENT_C_REG:
+	case DA9052_EVENT_D_REG:
+	case DA9052_IRQ_MASK_A_REG:
+	case DA9052_IRQ_MASK_B_REG:
+	case DA9052_IRQ_MASK_C_REG:
+	case DA9052_IRQ_MASK_D_REG:
+	case DA9052_CONTROL_A_REG:
+	case DA9052_CONTROL_B_REG:
+	case DA9052_CONTROL_C_REG:
+	case DA9052_CONTROL_D_REG:
+	case DA9052_PDDIS_REG:
+	case DA9052_RESET_REG:
+	case DA9052_GPIO_0_1_REG:
+	case DA9052_GPIO_2_3_REG:
+	case DA9052_GPIO_4_5_REG:
+	case DA9052_GPIO_6_7_REG:
+	case DA9052_GPIO_14_15_REG:
+	case DA9052_ID_0_1_REG:
+	case DA9052_ID_2_3_REG:
+	case DA9052_ID_4_5_REG:
+	case DA9052_ID_6_7_REG:
+	case DA9052_ID_8_9_REG:
+	case DA9052_ID_10_11_REG:
+	case DA9052_ID_12_13_REG:
+	case DA9052_ID_14_15_REG:
+	case DA9052_ID_16_17_REG:
+	case DA9052_ID_18_19_REG:
+	case DA9052_ID_20_21_REG:
+	case DA9052_SEQ_STATUS_REG:
+	case DA9052_SEQ_A_REG:
+	case DA9052_SEQ_B_REG:
+	case DA9052_SEQ_TIMER_REG:
+	case DA9052_BUCKA_REG:
+	case DA9052_BUCKB_REG:
+	case DA9052_BUCKCORE_REG:
+	case DA9052_BUCKPRO_REG:
+	case DA9052_BUCKMEM_REG:
+	case DA9052_BUCKPERI_REG:
+	case DA9052_LDO1_REG:
+	case DA9052_LDO2_REG:
+	case DA9052_LDO3_REG:
+	case DA9052_LDO4_REG:
+	case DA9052_LDO5_REG:
+	case DA9052_LDO6_REG:
+	case DA9052_LDO7_REG:
+	case DA9052_LDO8_REG:
+	case DA9052_LDO9_REG:
+	case DA9052_LDO10_REG:
+	case DA9052_SUPPLY_REG:
+	case DA9052_PULLDOWN_REG:
+	case DA9052_CHGBUCK_REG:
+	case DA9052_WAITCONT_REG:
+	case DA9052_ISET_REG:
+	case DA9052_BATCHG_REG:
+	case DA9052_CHG_CONT_REG:
+	case DA9052_INPUT_CONT_REG:
+	case DA9052_BBAT_CONT_REG:
+	case DA9052_BOOST_REG:
+	case DA9052_LED_CONT_REG:
+	case DA9052_LEDMIN123_REG:
+	case DA9052_LED1_CONF_REG:
+	case DA9052_LED2_CONF_REG:
+	case DA9052_LED3_CONF_REG:
+	case DA9052_LED1CONT_REG:
+	case DA9052_LED2CONT_REG:
+	case DA9052_LED3CONT_REG:
+	case DA9052_LED_CONT_4_REG:
+	case DA9052_LED_CONT_5_REG:
+	case DA9052_ADC_MAN_REG:
+	case DA9052_ADC_CONT_REG:
+	case DA9052_ADC_RES_L_REG:
+	case DA9052_ADC_RES_H_REG:
+	case DA9052_VDD_RES_REG:
+	case DA9052_VDD_MON_REG:
+	case DA9052_ICHG_THD_REG:
+	case DA9052_ICHG_END_REG:
+	case DA9052_TBAT_HIGHP_REG:
+	case DA9052_TBAT_HIGHN_REG:
+	case DA9052_TBAT_LOW_REG:
+	case DA9052_T_OFFSET_REG:
+	case DA9052_AUTO4_HIGH_REG:
+	case DA9052_AUTO4_LOW_REG:
+	case DA9052_AUTO5_HIGH_REG:
+	case DA9052_AUTO5_LOW_REG:
+	case DA9052_AUTO6_HIGH_REG:
+	case DA9052_AUTO6_LOW_REG:
+	case DA9052_TSI_CONT_A_REG:
+	case DA9052_TSI_CONT_B_REG:
+	case DA9052_COUNT_S_REG:
+	case DA9052_COUNT_MI_REG:
+	case DA9052_COUNT_H_REG:
+	case DA9052_COUNT_D_REG:
+	case DA9052_COUNT_MO_REG:
+	case DA9052_COUNT_Y_REG:
+	case DA9052_ALARM_MI_REG:
+	case DA9052_ALARM_H_REG:
+	case DA9052_ALARM_D_REG:
+	case DA9052_ALARM_MO_REG:
+	case DA9052_ALARM_Y_REG:
+	case DA9052_PAGE1_CON_REG:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool da9052_reg_volatile(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case DA9052_STATUS_A_REG:
+	case DA9052_STATUS_B_REG:
+	case DA9052_STATUS_C_REG:
+	case DA9052_STATUS_D_REG:
+	case DA9052_EVENT_A_REG:
+	case DA9052_EVENT_B_REG:
+	case DA9052_EVENT_C_REG:
+	case DA9052_EVENT_D_REG:
+	case DA9052_FAULTLOG_REG:
+	case DA9052_CHG_TIME_REG:
+	case DA9052_ADC_RES_L_REG:
+	case DA9052_ADC_RES_H_REG:
+	case DA9052_VDD_RES_REG:
+	case DA9052_ICHG_AV_REG:
+	case DA9052_TBAT_RES_REG:
+	case DA9052_ADCIN4_RES_REG:
+	case DA9052_ADCIN5_RES_REG:
+	case DA9052_ADCIN6_RES_REG:
+	case DA9052_TJUNC_RES_REG:
+	case DA9052_TSI_X_MSB_REG:
+	case DA9052_TSI_Y_MSB_REG:
+	case DA9052_TSI_LSB_REG:
+	case DA9052_TSI_Z_MSB_REG:
+	case DA9052_COUNT_S_REG:
+	case DA9052_COUNT_MI_REG:
+	case DA9052_COUNT_H_REG:
+	case DA9052_COUNT_D_REG:
+	case DA9052_COUNT_MO_REG:
+	case DA9052_COUNT_Y_REG:
+	case DA9052_ALARM_MI_REG:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static struct resource da9052_rtc_resource = {
+	.name = "ALM",
+	.start = DA9052_IRQ_ALARM,
+	.end   = DA9052_IRQ_ALARM,
+	.flags = IORESOURCE_IRQ,
+};
+
+static struct resource da9052_onkey_resource = {
+	.name = "ONKEY",
+	.start = DA9052_IRQ_NONKEY,
+	.end   = DA9052_IRQ_NONKEY,
+	.flags = IORESOURCE_IRQ,
+};
+
+static struct resource da9052_bat_resources[] = {
+	{
+		.name = "BATT TEMP",
+		.start = DA9052_IRQ_TBAT,
+		.end   = DA9052_IRQ_TBAT,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.name = "DCIN DET",
+		.start = DA9052_IRQ_DCIN,
+		.end   = DA9052_IRQ_DCIN,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.name = "DCIN REM",
+		.start = DA9052_IRQ_DCINREM,
+		.end   = DA9052_IRQ_DCINREM,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.name = "VBUS DET",
+		.start = DA9052_IRQ_VBUS,
+		.end   = DA9052_IRQ_VBUS,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.name = "VBUS REM",
+		.start = DA9052_IRQ_VBUSREM,
+		.end   = DA9052_IRQ_VBUSREM,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.name = "CHG END",
+		.start = DA9052_IRQ_CHGEND,
+		.end   = DA9052_IRQ_CHGEND,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct resource da9052_tsi_resources[] = {
+	{
+		.name = "PENDWN",
+		.start = DA9052_IRQ_PENDOWN,
+		.end   = DA9052_IRQ_PENDOWN,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.name = "TSIRDY",
+		.start = DA9052_IRQ_TSIREADY,
+		.end   = DA9052_IRQ_TSIREADY,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct mfd_cell __devinitdata da9052_subdev_info[] = {
+	{
+		.name = "da9052-regulator",
+		.id = 1,
+	},
+	{
+		.name = "da9052-regulator",
+		.id = 2,
+	},
+	{
+		.name = "da9052-regulator",
+		.id = 3,
+	},
+	{
+		.name = "da9052-regulator",
+		.id = 4,
+	},
+	{
+		.name = "da9052-regulator",
+		.id = 5,
+	},
+	{
+		.name = "da9052-regulator",
+		.id = 6,
+	},
+	{
+		.name = "da9052-regulator",
+		.id = 7,
+	},
+	{
+		.name = "da9052-regulator",
+		.id = 8,
+	},
+	{
+		.name = "da9052-regulator",
+		.id = 9,
+	},
+	{
+		.name = "da9052-regulator",
+		.id = 10,
+	},
+	{
+		.name = "da9052-regulator",
+		.id = 11,
+	},
+	{
+		.name = "da9052-regulator",
+		.id = 12,
+	},
+	{
+		.name = "da9052-regulator",
+		.id = 13,
+	},
+	{
+		.name = "da9052-regulator",
+		.id = 14,
+	},
+	{
+		.name = "da9052-onkey",
+		.resources = &da9052_onkey_resource,
+		.num_resources = 1,
+	},
+	{
+		.name = "da9052-rtc",
+		.resources = &da9052_rtc_resource,
+		.num_resources = 1,
+	},
+	{
+		.name = "da9052-gpio",
+	},
+	{
+		.name = "da9052-hwmon",
+	},
+	{
+		.name = "da9052-leds",
+	},
+	{
+		.name = "da9052-wled1",
+	},
+	{
+		.name = "da9052-wled2",
+	},
+	{
+		.name = "da9052-wled3",
+	},
+	{
+		.name = "da9052-tsi",
+		.resources = da9052_tsi_resources,
+		.num_resources = ARRAY_SIZE(da9052_tsi_resources),
+	},
+	{
+		.name = "da9052-bat",
+		.resources = da9052_bat_resources,
+		.num_resources = ARRAY_SIZE(da9052_bat_resources),
+	},
+	{
+		.name = "da9052-watchdog",
+	},
+};
+
+static struct regmap_irq da9052_irqs[] = {
+	[DA9052_IRQ_DCIN] = {
+		.reg_offset = 0,
+		.mask = DA9052_IRQ_MASK_POS_1,
+	},
+	[DA9052_IRQ_VBUS] = {
+		.reg_offset = 0,
+		.mask = DA9052_IRQ_MASK_POS_2,
+	},
+	[DA9052_IRQ_DCINREM] = {
+		.reg_offset = 0,
+		.mask = DA9052_IRQ_MASK_POS_3,
+	},
+	[DA9052_IRQ_VBUSREM] = {
+		.reg_offset = 0,
+		.mask = DA9052_IRQ_MASK_POS_4,
+	},
+	[DA9052_IRQ_VDDLOW] = {
+		.reg_offset = 0,
+		.mask = DA9052_IRQ_MASK_POS_5,
+	},
+	[DA9052_IRQ_ALARM] = {
+		.reg_offset = 0,
+		.mask = DA9052_IRQ_MASK_POS_6,
+	},
+	[DA9052_IRQ_SEQRDY] = {
+		.reg_offset = 0,
+		.mask = DA9052_IRQ_MASK_POS_7,
+	},
+	[DA9052_IRQ_COMP1V2] = {
+		.reg_offset = 0,
+		.mask = DA9052_IRQ_MASK_POS_8,
+	},
+	[DA9052_IRQ_NONKEY] = {
+		.reg_offset = 1,
+		.mask = DA9052_IRQ_MASK_POS_1,
+	},
+	[DA9052_IRQ_IDFLOAT] = {
+		.reg_offset = 1,
+		.mask = DA9052_IRQ_MASK_POS_2,
+	},
+	[DA9052_IRQ_IDGND] = {
+		.reg_offset = 1,
+		.mask = DA9052_IRQ_MASK_POS_3,
+	},
+	[DA9052_IRQ_CHGEND] = {
+		.reg_offset = 1,
+		.mask = DA9052_IRQ_MASK_POS_4,
+	},
+	[DA9052_IRQ_TBAT] = {
+		.reg_offset = 1,
+		.mask = DA9052_IRQ_MASK_POS_5,
+	},
+	[DA9052_IRQ_ADC_EOM] = {
+		.reg_offset = 1,
+		.mask = DA9052_IRQ_MASK_POS_6,
+	},
+	[DA9052_IRQ_PENDOWN] = {
+		.reg_offset = 1,
+		.mask = DA9052_IRQ_MASK_POS_7,
+	},
+	[DA9052_IRQ_TSIREADY] = {
+		.reg_offset = 1,
+		.mask = DA9052_IRQ_MASK_POS_8,
+	},
+	[DA9052_IRQ_GPI0] = {
+		.reg_offset = 2,
+		.mask = DA9052_IRQ_MASK_POS_1,
+	},
+	[DA9052_IRQ_GPI1] = {
+		.reg_offset = 2,
+		.mask = DA9052_IRQ_MASK_POS_2,
+	},
+	[DA9052_IRQ_GPI2] = {
+		.reg_offset = 2,
+		.mask = DA9052_IRQ_MASK_POS_3,
+	},
+	[DA9052_IRQ_GPI3] = {
+		.reg_offset = 2,
+		.mask = DA9052_IRQ_MASK_POS_4,
+	},
+	[DA9052_IRQ_GPI4] = {
+		.reg_offset = 2,
+		.mask = DA9052_IRQ_MASK_POS_5,
+	},
+	[DA9052_IRQ_GPI5] = {
+		.reg_offset = 2,
+		.mask = DA9052_IRQ_MASK_POS_6,
+	},
+	[DA9052_IRQ_GPI6] = {
+		.reg_offset = 2,
+		.mask = DA9052_IRQ_MASK_POS_7,
+	},
+	[DA9052_IRQ_GPI7] = {
+		.reg_offset = 2,
+		.mask = DA9052_IRQ_MASK_POS_8,
+	},
+	[DA9052_IRQ_GPI8] = {
+		.reg_offset = 3,
+		.mask = DA9052_IRQ_MASK_POS_1,
+	},
+	[DA9052_IRQ_GPI9] = {
+		.reg_offset = 3,
+		.mask = DA9052_IRQ_MASK_POS_2,
+	},
+	[DA9052_IRQ_GPI10] = {
+		.reg_offset = 3,
+		.mask = DA9052_IRQ_MASK_POS_3,
+	},
+	[DA9052_IRQ_GPI11] = {
+		.reg_offset = 3,
+		.mask = DA9052_IRQ_MASK_POS_4,
+	},
+	[DA9052_IRQ_GPI12] = {
+		.reg_offset = 3,
+		.mask = DA9052_IRQ_MASK_POS_5,
+	},
+	[DA9052_IRQ_GPI13] = {
+		.reg_offset = 3,
+		.mask = DA9052_IRQ_MASK_POS_6,
+	},
+	[DA9052_IRQ_GPI14] = {
+		.reg_offset = 3,
+		.mask = DA9052_IRQ_MASK_POS_7,
+	},
+	[DA9052_IRQ_GPI15] = {
+		.reg_offset = 3,
+		.mask = DA9052_IRQ_MASK_POS_8,
+	},
+};
+
+static struct regmap_irq_chip da9052_regmap_irq_chip = {
+	.name = "da9052_irq",
+	.status_base = DA9052_EVENT_A_REG,
+	.mask_base = DA9052_IRQ_MASK_A_REG,
+	.ack_base = DA9052_EVENT_A_REG,
+	.num_regs = DA9052_NUM_IRQ_REGS,
+	.irqs = da9052_irqs,
+	.num_irqs = ARRAY_SIZE(da9052_irqs),
+};
+
+struct regmap_config da9052_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.cache_type = REGCACHE_RBTREE,
+
+	.max_register = DA9052_PAGE1_CON_REG,
+	.readable_reg = da9052_reg_readable,
+	.writeable_reg = da9052_reg_writeable,
+	.volatile_reg = da9052_reg_volatile,
+};
+EXPORT_SYMBOL_GPL(da9052_regmap_config);
+
+int __devinit da9052_device_init(struct da9052 *da9052, u8 chip_id)
+{
+	struct da9052_pdata *pdata = da9052->dev->platform_data;
+	struct irq_desc *desc;
+	int ret;
+
+	if (pdata && pdata->init != NULL)
+		pdata->init(da9052);
+
+	da9052->chip_id = chip_id;
+
+	if (!pdata || !pdata->irq_base)
+		da9052->irq_base = -1;
+	else
+		da9052->irq_base = pdata->irq_base;
+
+	ret = regmap_add_irq_chip(da9052->regmap, da9052->chip_irq,
+				  IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+				  da9052->irq_base, &da9052_regmap_irq_chip,
+				  NULL);
+	if (ret < 0)
+		goto regmap_err;
+
+	desc = irq_to_desc(da9052->chip_irq);
+	da9052->irq_base = regmap_irq_chip_get_base(desc->action->dev_id);
+
+	ret = mfd_add_devices(da9052->dev, -1, da9052_subdev_info,
+			      ARRAY_SIZE(da9052_subdev_info), NULL, 0);
+	if (ret)
+		goto err;
+
+	return 0;
+
+err:
+	mfd_remove_devices(da9052->dev);
+regmap_err:
+	return ret;
+}
+
+void da9052_device_exit(struct da9052 *da9052)
+{
+	regmap_del_irq_chip(da9052->chip_irq,
+			    irq_get_irq_data(da9052->irq_base)->chip_data);
+	mfd_remove_devices(da9052->dev);
+}
+
+MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
+MODULE_DESCRIPTION("DA9052 MFD Core");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/da9052-i2c.c b/ap/os/linux/linux-3.4.x/drivers/mfd/da9052-i2c.c
new file mode 100644
index 0000000..36b88e3
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/da9052-i2c.c
@@ -0,0 +1,143 @@
+/*
+ * I2C access for DA9052 PMICs.
+ *
+ * Copyright(c) 2011 Dialog Semiconductor Ltd.
+ *
+ * Author: David Dajun Chen <dchen@diasemi.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/mfd/core.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+
+#include <linux/mfd/da9052/da9052.h>
+#include <linux/mfd/da9052/reg.h>
+
+static int da9052_i2c_enable_multiwrite(struct da9052 *da9052)
+{
+	int reg_val, ret;
+
+	ret = regmap_read(da9052->regmap, DA9052_CONTROL_B_REG, &reg_val);
+	if (ret < 0)
+		return ret;
+
+	if (reg_val & DA9052_CONTROL_B_WRITEMODE) {
+		reg_val &= ~DA9052_CONTROL_B_WRITEMODE;
+		ret = regmap_write(da9052->regmap, DA9052_CONTROL_B_REG,
+				   reg_val);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int __devinit da9052_i2c_probe(struct i2c_client *client,
+				       const struct i2c_device_id *id)
+{
+	struct da9052 *da9052;
+	int ret;
+
+	da9052 = kzalloc(sizeof(struct da9052), GFP_KERNEL);
+	if (!da9052)
+		return -ENOMEM;
+
+	if (!i2c_check_functionality(client->adapter,
+				     I2C_FUNC_SMBUS_BYTE_DATA)) {
+		dev_info(&client->dev, "Error in %s:i2c_check_functionality\n",
+			 __func__);
+		ret = -ENODEV;
+		goto err;
+	}
+
+	da9052->dev = &client->dev;
+	da9052->chip_irq = client->irq;
+
+	i2c_set_clientdata(client, da9052);
+
+	da9052->regmap = regmap_init_i2c(client, &da9052_regmap_config);
+	if (IS_ERR(da9052->regmap)) {
+		ret = PTR_ERR(da9052->regmap);
+		dev_err(&client->dev, "Failed to allocate register map: %d\n",
+			ret);
+		goto err;
+	}
+
+	ret = da9052_i2c_enable_multiwrite(da9052);
+	if (ret < 0)
+		goto err_regmap;
+
+	ret = da9052_device_init(da9052, id->driver_data);
+	if (ret != 0)
+		goto err_regmap;
+
+	return 0;
+
+err_regmap:
+	regmap_exit(da9052->regmap);
+err:
+	kfree(da9052);
+	return ret;
+}
+
+static int __devexit da9052_i2c_remove(struct i2c_client *client)
+{
+	struct da9052 *da9052 = i2c_get_clientdata(client);
+
+	da9052_device_exit(da9052);
+	regmap_exit(da9052->regmap);
+	kfree(da9052);
+
+	return 0;
+}
+
+static struct i2c_device_id da9052_i2c_id[] = {
+	{"da9052", DA9052},
+	{"da9053-aa", DA9053_AA},
+	{"da9053-ba", DA9053_BA},
+	{"da9053-bb", DA9053_BB},
+	{}
+};
+
+static struct i2c_driver da9052_i2c_driver = {
+	.probe = da9052_i2c_probe,
+	.remove = __devexit_p(da9052_i2c_remove),
+	.id_table = da9052_i2c_id,
+	.driver = {
+		.name = "da9052",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init da9052_i2c_init(void)
+{
+	int ret;
+
+	ret = i2c_add_driver(&da9052_i2c_driver);
+	if (ret != 0) {
+		pr_err("DA9052 I2C registration failed %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+subsys_initcall(da9052_i2c_init);
+
+static void __exit da9052_i2c_exit(void)
+{
+	i2c_del_driver(&da9052_i2c_driver);
+}
+module_exit(da9052_i2c_exit);
+
+MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
+MODULE_DESCRIPTION("I2C driver for Dialog DA9052 PMIC");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/da9052-spi.c b/ap/os/linux/linux-3.4.x/drivers/mfd/da9052-spi.c
new file mode 100644
index 0000000..6faf149
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/da9052-spi.c
@@ -0,0 +1,118 @@
+/*
+ * SPI access for Dialog DA9052 PMICs.
+ *
+ * Copyright(c) 2011 Dialog Semiconductor Ltd.
+ *
+ * Author: David Dajun Chen <dchen@diasemi.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/mfd/core.h>
+#include <linux/spi/spi.h>
+#include <linux/err.h>
+
+#include <linux/mfd/da9052/da9052.h>
+
+static int __devinit da9052_spi_probe(struct spi_device *spi)
+{
+	int ret;
+	const struct spi_device_id *id = spi_get_device_id(spi);
+	struct da9052 *da9052 = kzalloc(sizeof(struct da9052), GFP_KERNEL);
+
+	if (!da9052)
+		return -ENOMEM;
+
+	spi->mode = SPI_MODE_0 | SPI_CPOL;
+	spi->bits_per_word = 8;
+	spi_setup(spi);
+
+	da9052->dev = &spi->dev;
+	da9052->chip_irq = spi->irq;
+
+	dev_set_drvdata(&spi->dev, da9052);
+
+	da9052_regmap_config.read_flag_mask = 1;
+	da9052_regmap_config.write_flag_mask = 0;
+
+	da9052->regmap = regmap_init_spi(spi, &da9052_regmap_config);
+	if (IS_ERR(da9052->regmap)) {
+		ret = PTR_ERR(da9052->regmap);
+		dev_err(&spi->dev, "Failed to allocate register map: %d\n",
+			ret);
+		goto err;
+	}
+
+	ret = da9052_device_init(da9052, id->driver_data);
+	if (ret != 0)
+		goto err_regmap;
+
+	return 0;
+
+err_regmap:
+	regmap_exit(da9052->regmap);
+err:
+	kfree(da9052);
+	return ret;
+}
+
+static int __devexit da9052_spi_remove(struct spi_device *spi)
+{
+	struct da9052 *da9052 = dev_get_drvdata(&spi->dev);
+
+	da9052_device_exit(da9052);
+	regmap_exit(da9052->regmap);
+	kfree(da9052);
+
+	return 0;
+}
+
+static struct spi_device_id da9052_spi_id[] = {
+	{"da9052", DA9052},
+	{"da9053-aa", DA9053_AA},
+	{"da9053-ba", DA9053_BA},
+	{"da9053-bb", DA9053_BB},
+	{}
+};
+
+static struct spi_driver da9052_spi_driver = {
+	.probe = da9052_spi_probe,
+	.remove = __devexit_p(da9052_spi_remove),
+	.id_table = da9052_spi_id,
+	.driver = {
+		.name = "da9052",
+		.bus = &spi_bus_type,
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init da9052_spi_init(void)
+{
+	int ret;
+
+	ret = spi_register_driver(&da9052_spi_driver);
+	if (ret != 0) {
+		pr_err("Failed to register DA9052 SPI driver, %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+subsys_initcall(da9052_spi_init);
+
+static void __exit da9052_spi_exit(void)
+{
+	spi_unregister_driver(&da9052_spi_driver);
+}
+module_exit(da9052_spi_exit);
+
+MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
+MODULE_DESCRIPTION("SPI driver for Dialog DA9052 PMIC");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/davinci_voicecodec.c b/ap/os/linux/linux-3.4.x/drivers/mfd/davinci_voicecodec.c
new file mode 100644
index 0000000..4e2af2c
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/davinci_voicecodec.c
@@ -0,0 +1,194 @@
+/*
+ * DaVinci Voice Codec Core Interface for TI platforms
+ *
+ * Copyright (C) 2010 Texas Instruments, Inc
+ *
+ * Author: Miguel Aguilar <miguel.aguilar@ridgerun.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+
+#include <sound/pcm.h>
+
+#include <linux/mfd/davinci_voicecodec.h>
+
+u32 davinci_vc_read(struct davinci_vc *davinci_vc, int reg)
+{
+	return __raw_readl(davinci_vc->base + reg);
+}
+
+void davinci_vc_write(struct davinci_vc *davinci_vc,
+					   int reg, u32 val)
+{
+	__raw_writel(val, davinci_vc->base + reg);
+}
+
+static int __init davinci_vc_probe(struct platform_device *pdev)
+{
+	struct davinci_vc *davinci_vc;
+	struct resource *res, *mem;
+	struct mfd_cell *cell = NULL;
+	int ret;
+
+	davinci_vc = kzalloc(sizeof(struct davinci_vc), GFP_KERNEL);
+	if (!davinci_vc) {
+		dev_dbg(&pdev->dev,
+			    "could not allocate memory for private data\n");
+		return -ENOMEM;
+	}
+
+	davinci_vc->clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(davinci_vc->clk)) {
+		dev_dbg(&pdev->dev,
+			    "could not get the clock for voice codec\n");
+		ret = -ENODEV;
+		goto fail1;
+	}
+	clk_enable(davinci_vc->clk);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "no mem resource\n");
+		ret = -ENODEV;
+		goto fail2;
+	}
+
+	davinci_vc->pbase = res->start;
+	davinci_vc->base_size = resource_size(res);
+
+	mem = request_mem_region(davinci_vc->pbase, davinci_vc->base_size,
+				 pdev->name);
+	if (!mem) {
+		dev_err(&pdev->dev, "VCIF region already claimed\n");
+		ret = -EBUSY;
+		goto fail2;
+	}
+
+	davinci_vc->base = ioremap(davinci_vc->pbase, davinci_vc->base_size);
+	if (!davinci_vc->base) {
+		dev_err(&pdev->dev, "can't ioremap mem resource.\n");
+		ret = -ENOMEM;
+		goto fail3;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "no DMA resource\n");
+		ret = -ENXIO;
+		goto fail4;
+	}
+
+	davinci_vc->davinci_vcif.dma_tx_channel = res->start;
+	davinci_vc->davinci_vcif.dma_tx_addr =
+		(dma_addr_t)(io_v2p(davinci_vc->base) + DAVINCI_VC_WFIFO);
+
+	res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+	if (!res) {
+		dev_err(&pdev->dev, "no DMA resource\n");
+		ret = -ENXIO;
+		goto fail4;
+	}
+
+	davinci_vc->davinci_vcif.dma_rx_channel = res->start;
+	davinci_vc->davinci_vcif.dma_rx_addr =
+		(dma_addr_t)(io_v2p(davinci_vc->base) + DAVINCI_VC_RFIFO);
+
+	davinci_vc->dev = &pdev->dev;
+	davinci_vc->pdev = pdev;
+
+	/* Voice codec interface client */
+	cell = &davinci_vc->cells[DAVINCI_VC_VCIF_CELL];
+	cell->name = "davinci-vcif";
+	cell->platform_data = davinci_vc;
+	cell->pdata_size = sizeof(*davinci_vc);
+
+	/* Voice codec CQ93VC client */
+	cell = &davinci_vc->cells[DAVINCI_VC_CQ93VC_CELL];
+	cell->name = "cq93vc-codec";
+	cell->platform_data = davinci_vc;
+	cell->pdata_size = sizeof(*davinci_vc);
+
+	ret = mfd_add_devices(&pdev->dev, pdev->id, davinci_vc->cells,
+			      DAVINCI_VC_CELLS, NULL, 0);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "fail to register client devices\n");
+		goto fail4;
+	}
+
+	return 0;
+
+fail4:
+	iounmap(davinci_vc->base);
+fail3:
+	release_mem_region(davinci_vc->pbase, davinci_vc->base_size);
+fail2:
+	clk_disable(davinci_vc->clk);
+	clk_put(davinci_vc->clk);
+	davinci_vc->clk = NULL;
+fail1:
+	kfree(davinci_vc);
+
+	return ret;
+}
+
+static int __devexit davinci_vc_remove(struct platform_device *pdev)
+{
+	struct davinci_vc *davinci_vc = platform_get_drvdata(pdev);
+
+	mfd_remove_devices(&pdev->dev);
+
+	iounmap(davinci_vc->base);
+	release_mem_region(davinci_vc->pbase, davinci_vc->base_size);
+
+	clk_disable(davinci_vc->clk);
+	clk_put(davinci_vc->clk);
+	davinci_vc->clk = NULL;
+
+	kfree(davinci_vc);
+
+	return 0;
+}
+
+static struct platform_driver davinci_vc_driver = {
+	.driver	= {
+		.name = "davinci_voicecodec",
+		.owner = THIS_MODULE,
+	},
+	.remove	= __devexit_p(davinci_vc_remove),
+};
+
+static int __init davinci_vc_init(void)
+{
+	return platform_driver_probe(&davinci_vc_driver, davinci_vc_probe);
+}
+module_init(davinci_vc_init);
+
+static void __exit davinci_vc_exit(void)
+{
+	platform_driver_unregister(&davinci_vc_driver);
+}
+module_exit(davinci_vc_exit);
+
+MODULE_AUTHOR("Miguel Aguilar");
+MODULE_DESCRIPTION("Texas Instruments DaVinci Voice Codec Core Interface");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/db5500-prcmu.c b/ap/os/linux/linux-3.4.x/drivers/mfd/db5500-prcmu.c
new file mode 100644
index 0000000..bb115b2
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/db5500-prcmu.c
@@ -0,0 +1,451 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Mattias Nilsson <mattias.i.nilsson@stericsson.com>
+ *
+ * U5500 PRCM Unit interface driver
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/completion.h>
+#include <linux/irq.h>
+#include <linux/jiffies.h>
+#include <linux/bitops.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/dbx500-prcmu.h>
+#include <mach/hardware.h>
+#include <mach/irqs.h>
+#include <mach/db5500-regs.h>
+#include "dbx500-prcmu-regs.h"
+
+#define _PRCM_MB_HEADER (tcdm_base + 0xFE8)
+#define PRCM_REQ_MB0_HEADER (_PRCM_MB_HEADER + 0x0)
+#define PRCM_REQ_MB1_HEADER (_PRCM_MB_HEADER + 0x1)
+#define PRCM_REQ_MB2_HEADER (_PRCM_MB_HEADER + 0x2)
+#define PRCM_REQ_MB3_HEADER (_PRCM_MB_HEADER + 0x3)
+#define PRCM_REQ_MB4_HEADER (_PRCM_MB_HEADER + 0x4)
+#define PRCM_REQ_MB5_HEADER (_PRCM_MB_HEADER + 0x5)
+#define PRCM_REQ_MB6_HEADER (_PRCM_MB_HEADER + 0x6)
+#define PRCM_REQ_MB7_HEADER (_PRCM_MB_HEADER + 0x7)
+#define PRCM_ACK_MB0_HEADER (_PRCM_MB_HEADER + 0x8)
+#define PRCM_ACK_MB1_HEADER (_PRCM_MB_HEADER + 0x9)
+#define PRCM_ACK_MB2_HEADER (_PRCM_MB_HEADER + 0xa)
+#define PRCM_ACK_MB3_HEADER (_PRCM_MB_HEADER + 0xb)
+#define PRCM_ACK_MB4_HEADER (_PRCM_MB_HEADER + 0xc)
+#define PRCM_ACK_MB5_HEADER (_PRCM_MB_HEADER + 0xd)
+#define PRCM_ACK_MB6_HEADER (_PRCM_MB_HEADER + 0xe)
+#define PRCM_ACK_MB7_HEADER (_PRCM_MB_HEADER + 0xf)
+
+/* Req Mailboxes */
+#define PRCM_REQ_MB0 (tcdm_base + 0xFD8)
+#define PRCM_REQ_MB1 (tcdm_base + 0xFCC)
+#define PRCM_REQ_MB2 (tcdm_base + 0xFC4)
+#define PRCM_REQ_MB3 (tcdm_base + 0xFC0)
+#define PRCM_REQ_MB4 (tcdm_base + 0xF98)
+#define PRCM_REQ_MB5 (tcdm_base + 0xF90)
+#define PRCM_REQ_MB6 (tcdm_base + 0xF8C)
+#define PRCM_REQ_MB7 (tcdm_base + 0xF84)
+
+/* Ack Mailboxes */
+#define PRCM_ACK_MB0 (tcdm_base + 0xF38)
+#define PRCM_ACK_MB1 (tcdm_base + 0xF30)
+#define PRCM_ACK_MB2 (tcdm_base + 0xF24)
+#define PRCM_ACK_MB3 (tcdm_base + 0xF20)
+#define PRCM_ACK_MB4 (tcdm_base + 0xF1C)
+#define PRCM_ACK_MB5 (tcdm_base + 0xF14)
+#define PRCM_ACK_MB6 (tcdm_base + 0xF0C)
+#define PRCM_ACK_MB7 (tcdm_base + 0xF08)
+
+enum mb_return_code {
+	RC_SUCCESS,
+	RC_FAIL,
+};
+
+/* Mailbox 0 headers. */
+enum mb0_header {
+	/* request */
+	RMB0H_PWR_STATE_TRANS = 1,
+	RMB0H_WAKE_UP_CFG,
+	RMB0H_RD_WAKE_UP_ACK,
+	/* acknowledge */
+	AMB0H_WAKE_UP = 1,
+};
+
+/* Mailbox 5 headers. */
+enum mb5_header {
+	MB5H_I2C_WRITE = 1,
+	MB5H_I2C_READ,
+};
+
+/* Request mailbox 5 fields. */
+#define PRCM_REQ_MB5_I2C_SLAVE (PRCM_REQ_MB5 + 0)
+#define PRCM_REQ_MB5_I2C_REG (PRCM_REQ_MB5 + 1)
+#define PRCM_REQ_MB5_I2C_SIZE (PRCM_REQ_MB5 + 2)
+#define PRCM_REQ_MB5_I2C_DATA (PRCM_REQ_MB5 + 4)
+
+/* Acknowledge mailbox 5 fields. */
+#define PRCM_ACK_MB5_RETURN_CODE (PRCM_ACK_MB5 + 0)
+#define PRCM_ACK_MB5_I2C_DATA (PRCM_ACK_MB5 + 4)
+
+#define NUM_MB 8
+#define MBOX_BIT BIT
+#define ALL_MBOX_BITS (MBOX_BIT(NUM_MB) - 1)
+
+/*
+* Used by MCDE to setup all necessary PRCMU registers
+*/
+#define PRCMU_RESET_DSIPLL			0x00004000
+#define PRCMU_UNCLAMP_DSIPLL			0x00400800
+
+/* HDMI CLK MGT PLLSW=001 (PLLSOC0), PLLDIV=0x8, = 50 Mhz*/
+#define PRCMU_DSI_CLOCK_SETTING			0x00000128
+/* TVCLK_MGT PLLSW=001 (PLLSOC0) PLLDIV=0x13, = 19.05 MHZ */
+#define PRCMU_DSI_LP_CLOCK_SETTING		0x00000135
+#define PRCMU_PLLDSI_FREQ_SETTING		0x00020121
+#define PRCMU_DSI_PLLOUT_SEL_SETTING		0x00000002
+#define PRCMU_ENABLE_ESCAPE_CLOCK_DIV		0x03000201
+#define PRCMU_DISABLE_ESCAPE_CLOCK_DIV		0x00000101
+
+#define PRCMU_ENABLE_PLLDSI			0x00000001
+#define PRCMU_DISABLE_PLLDSI			0x00000000
+
+#define PRCMU_DSI_RESET_SW			0x00000003
+#define PRCMU_RESOUTN0_PIN			0x00000001
+#define PRCMU_RESOUTN1_PIN			0x00000002
+#define PRCMU_RESOUTN2_PIN			0x00000004
+
+#define PRCMU_PLLDSI_LOCKP_LOCKED		0x3
+
+/*
+ * mb0_transfer - state needed for mailbox 0 communication.
+ * @lock:		The transaction lock.
+ */
+static struct {
+	spinlock_t lock;
+} mb0_transfer;
+
+/*
+ * mb5_transfer - state needed for mailbox 5 communication.
+ * @lock:	The transaction lock.
+ * @work:	The transaction completion structure.
+ * @ack:	Reply ("acknowledge") data.
+ */
+static struct {
+	struct mutex lock;
+	struct completion work;
+	struct {
+		u8 header;
+		u8 status;
+		u8 value[4];
+	} ack;
+} mb5_transfer;
+
+/* PRCMU TCDM base IO address. */
+static __iomem void *tcdm_base;
+
+/**
+ * db5500_prcmu_abb_read() - Read register value(s) from the ABB.
+ * @slave:	The I2C slave address.
+ * @reg:	The (start) register address.
+ * @value:	The read out value(s).
+ * @size:	The number of registers to read.
+ *
+ * Reads register value(s) from the ABB.
+ * @size has to be <= 4.
+ */
+int db5500_prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size)
+{
+	int r;
+
+	if ((size < 1) || (4 < size))
+		return -EINVAL;
+
+	mutex_lock(&mb5_transfer.lock);
+
+	while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(5))
+		cpu_relax();
+	writeb(slave, PRCM_REQ_MB5_I2C_SLAVE);
+	writeb(reg, PRCM_REQ_MB5_I2C_REG);
+	writeb(size, PRCM_REQ_MB5_I2C_SIZE);
+	writeb(MB5H_I2C_READ, PRCM_REQ_MB5_HEADER);
+
+	writel(MBOX_BIT(5), PRCM_MBOX_CPU_SET);
+	wait_for_completion(&mb5_transfer.work);
+
+	r = 0;
+	if ((mb5_transfer.ack.header == MB5H_I2C_READ) &&
+		(mb5_transfer.ack.status == RC_SUCCESS))
+		memcpy(value, mb5_transfer.ack.value, (size_t)size);
+	else
+		r = -EIO;
+
+	mutex_unlock(&mb5_transfer.lock);
+
+	return r;
+}
+
+/**
+ * db5500_prcmu_abb_write() - Write register value(s) to the ABB.
+ * @slave:	The I2C slave address.
+ * @reg:	The (start) register address.
+ * @value:	The value(s) to write.
+ * @size:	The number of registers to write.
+ *
+ * Writes register value(s) to the ABB.
+ * @size has to be <= 4.
+ */
+int db5500_prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size)
+{
+	int r;
+
+	if ((size < 1) || (4 < size))
+		return -EINVAL;
+
+	mutex_lock(&mb5_transfer.lock);
+
+	while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(5))
+		cpu_relax();
+	writeb(slave, PRCM_REQ_MB5_I2C_SLAVE);
+	writeb(reg, PRCM_REQ_MB5_I2C_REG);
+	writeb(size, PRCM_REQ_MB5_I2C_SIZE);
+	memcpy_toio(PRCM_REQ_MB5_I2C_DATA, value, size);
+	writeb(MB5H_I2C_WRITE, PRCM_REQ_MB5_HEADER);
+
+	writel(MBOX_BIT(5), PRCM_MBOX_CPU_SET);
+	wait_for_completion(&mb5_transfer.work);
+
+	if ((mb5_transfer.ack.header == MB5H_I2C_WRITE) &&
+		(mb5_transfer.ack.status == RC_SUCCESS))
+		r = 0;
+	else
+		r = -EIO;
+
+	mutex_unlock(&mb5_transfer.lock);
+
+	return r;
+}
+
+int db5500_prcmu_enable_dsipll(void)
+{
+	int i;
+
+	/* Enable DSIPLL_RESETN resets */
+	writel(PRCMU_RESET_DSIPLL, PRCM_APE_RESETN_CLR);
+	/* Unclamp DSIPLL in/out */
+	writel(PRCMU_UNCLAMP_DSIPLL, PRCM_MMIP_LS_CLAMP_CLR);
+	/* Set DSI PLL FREQ */
+	writel(PRCMU_PLLDSI_FREQ_SETTING, PRCM_PLLDSI_FREQ);
+	writel(PRCMU_DSI_PLLOUT_SEL_SETTING,
+		PRCM_DSI_PLLOUT_SEL);
+	/* Enable Escape clocks */
+	writel(PRCMU_ENABLE_ESCAPE_CLOCK_DIV, PRCM_DSITVCLK_DIV);
+
+	/* Start DSI PLL */
+	writel(PRCMU_ENABLE_PLLDSI, PRCM_PLLDSI_ENABLE);
+	/* Reset DSI PLL */
+	writel(PRCMU_DSI_RESET_SW, PRCM_DSI_SW_RESET);
+	for (i = 0; i < 10; i++) {
+		if ((readl(PRCM_PLLDSI_LOCKP) &
+			PRCMU_PLLDSI_LOCKP_LOCKED) == PRCMU_PLLDSI_LOCKP_LOCKED)
+			break;
+		udelay(100);
+	}
+	/* Release DSIPLL_RESETN */
+	writel(PRCMU_RESET_DSIPLL, PRCM_APE_RESETN_SET);
+	return 0;
+}
+
+int db5500_prcmu_disable_dsipll(void)
+{
+	/* Disable dsi pll */
+	writel(PRCMU_DISABLE_PLLDSI, PRCM_PLLDSI_ENABLE);
+	/* Disable  escapeclock */
+	writel(PRCMU_DISABLE_ESCAPE_CLOCK_DIV, PRCM_DSITVCLK_DIV);
+	return 0;
+}
+
+int db5500_prcmu_set_display_clocks(void)
+{
+	/* HDMI and TVCLK Should be handled somewhere else */
+	/* PLLDIV=8, PLLSW=2, CLKEN=1 */
+	writel(PRCMU_DSI_CLOCK_SETTING, PRCM_HDMICLK_MGT);
+	/* PLLDIV=14, PLLSW=2, CLKEN=1 */
+	writel(PRCMU_DSI_LP_CLOCK_SETTING, PRCM_TVCLK_MGT);
+	return 0;
+}
+
+static void ack_dbb_wakeup(void)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&mb0_transfer.lock, flags);
+
+	while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(0))
+		cpu_relax();
+
+	writeb(RMB0H_RD_WAKE_UP_ACK, PRCM_REQ_MB0_HEADER);
+	writel(MBOX_BIT(0), PRCM_MBOX_CPU_SET);
+
+	spin_unlock_irqrestore(&mb0_transfer.lock, flags);
+}
+
+static inline void print_unknown_header_warning(u8 n, u8 header)
+{
+	pr_warning("prcmu: Unknown message header (%d) in mailbox %d.\n",
+		header, n);
+}
+
+static bool read_mailbox_0(void)
+{
+	bool r;
+	u8 header;
+
+	header = readb(PRCM_ACK_MB0_HEADER);
+	switch (header) {
+	case AMB0H_WAKE_UP:
+		r = true;
+		break;
+	default:
+		print_unknown_header_warning(0, header);
+		r = false;
+		break;
+	}
+	writel(MBOX_BIT(0), PRCM_ARM_IT1_CLR);
+	return r;
+}
+
+static bool read_mailbox_1(void)
+{
+	writel(MBOX_BIT(1), PRCM_ARM_IT1_CLR);
+	return false;
+}
+
+static bool read_mailbox_2(void)
+{
+	writel(MBOX_BIT(2), PRCM_ARM_IT1_CLR);
+	return false;
+}
+
+static bool read_mailbox_3(void)
+{
+	writel(MBOX_BIT(3), PRCM_ARM_IT1_CLR);
+	return false;
+}
+
+static bool read_mailbox_4(void)
+{
+	writel(MBOX_BIT(4), PRCM_ARM_IT1_CLR);
+	return false;
+}
+
+static bool read_mailbox_5(void)
+{
+	u8 header;
+
+	header = readb(PRCM_ACK_MB5_HEADER);
+	switch (header) {
+	case MB5H_I2C_READ:
+		memcpy_fromio(mb5_transfer.ack.value, PRCM_ACK_MB5_I2C_DATA, 4);
+	case MB5H_I2C_WRITE:
+		mb5_transfer.ack.header = header;
+		mb5_transfer.ack.status = readb(PRCM_ACK_MB5_RETURN_CODE);
+		complete(&mb5_transfer.work);
+		break;
+	default:
+		print_unknown_header_warning(5, header);
+		break;
+	}
+	writel(MBOX_BIT(5), PRCM_ARM_IT1_CLR);
+	return false;
+}
+
+static bool read_mailbox_6(void)
+{
+	writel(MBOX_BIT(6), PRCM_ARM_IT1_CLR);
+	return false;
+}
+
+static bool read_mailbox_7(void)
+{
+	writel(MBOX_BIT(7), PRCM_ARM_IT1_CLR);
+	return false;
+}
+
+static bool (* const read_mailbox[NUM_MB])(void) = {
+	read_mailbox_0,
+	read_mailbox_1,
+	read_mailbox_2,
+	read_mailbox_3,
+	read_mailbox_4,
+	read_mailbox_5,
+	read_mailbox_6,
+	read_mailbox_7
+};
+
+static irqreturn_t prcmu_irq_handler(int irq, void *data)
+{
+	u32 bits;
+	u8 n;
+	irqreturn_t r;
+
+	bits = (readl(PRCM_ARM_IT1_VAL) & ALL_MBOX_BITS);
+	if (unlikely(!bits))
+		return IRQ_NONE;
+
+	r = IRQ_HANDLED;
+	for (n = 0; bits; n++) {
+		if (bits & MBOX_BIT(n)) {
+			bits -= MBOX_BIT(n);
+			if (read_mailbox[n]())
+				r = IRQ_WAKE_THREAD;
+		}
+	}
+	return r;
+}
+
+static irqreturn_t prcmu_irq_thread_fn(int irq, void *data)
+{
+	ack_dbb_wakeup();
+	return IRQ_HANDLED;
+}
+
+void __init db5500_prcmu_early_init(void)
+{
+	tcdm_base = __io_address(U5500_PRCMU_TCDM_BASE);
+	spin_lock_init(&mb0_transfer.lock);
+	mutex_init(&mb5_transfer.lock);
+	init_completion(&mb5_transfer.work);
+}
+
+/**
+ * prcmu_fw_init - arch init call for the Linux PRCMU fw init logic
+ *
+ */
+int __init db5500_prcmu_init(void)
+{
+	int r = 0;
+
+	if (ux500_is_svp() || !cpu_is_u5500())
+		return -ENODEV;
+
+	/* Clean up the mailbox interrupts after pre-kernel code. */
+	writel(ALL_MBOX_BITS, PRCM_ARM_IT1_CLR);
+
+	r = request_threaded_irq(IRQ_DB5500_PRCMU1, prcmu_irq_handler,
+		prcmu_irq_thread_fn, 0, "prcmu", NULL);
+	if (r < 0) {
+		pr_err("prcmu: Failed to allocate IRQ_DB5500_PRCMU1.\n");
+		return -EBUSY;
+	}
+	return 0;
+}
+
+arch_initcall(db5500_prcmu_init);
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/db8500-prcmu.c b/ap/os/linux/linux-3.4.x/drivers/mfd/db8500-prcmu.c
new file mode 100644
index 0000000..5be3248
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/db8500-prcmu.c
@@ -0,0 +1,3013 @@
+/*
+ * Copyright (C) STMicroelectronics 2009
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Kumar Sanghvi <kumar.sanghvi@stericsson.com>
+ * Author: Sundar Iyer <sundar.iyer@stericsson.com>
+ * Author: Mattias Nilsson <mattias.i.nilsson@stericsson.com>
+ *
+ * U8500 PRCM Unit interface driver
+ *
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/completion.h>
+#include <linux/irq.h>
+#include <linux/jiffies.h>
+#include <linux/bitops.h>
+#include <linux/fs.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/dbx500-prcmu.h>
+#include <linux/regulator/db8500-prcmu.h>
+#include <linux/regulator/machine.h>
+#include <asm/hardware/gic.h>
+#include <mach/hardware.h>
+#include <mach/irqs.h>
+#include <mach/db8500-regs.h>
+#include <mach/id.h>
+#include "dbx500-prcmu-regs.h"
+
+/* Offset for the firmware version within the TCPM */
+#define PRCMU_FW_VERSION_OFFSET 0xA4
+
+/* Index of different voltages to be used when accessing AVSData */
+#define PRCM_AVS_BASE		0x2FC
+#define PRCM_AVS_VBB_RET	(PRCM_AVS_BASE + 0x0)
+#define PRCM_AVS_VBB_MAX_OPP	(PRCM_AVS_BASE + 0x1)
+#define PRCM_AVS_VBB_100_OPP	(PRCM_AVS_BASE + 0x2)
+#define PRCM_AVS_VBB_50_OPP	(PRCM_AVS_BASE + 0x3)
+#define PRCM_AVS_VARM_MAX_OPP	(PRCM_AVS_BASE + 0x4)
+#define PRCM_AVS_VARM_100_OPP	(PRCM_AVS_BASE + 0x5)
+#define PRCM_AVS_VARM_50_OPP	(PRCM_AVS_BASE + 0x6)
+#define PRCM_AVS_VARM_RET	(PRCM_AVS_BASE + 0x7)
+#define PRCM_AVS_VAPE_100_OPP	(PRCM_AVS_BASE + 0x8)
+#define PRCM_AVS_VAPE_50_OPP	(PRCM_AVS_BASE + 0x9)
+#define PRCM_AVS_VMOD_100_OPP	(PRCM_AVS_BASE + 0xA)
+#define PRCM_AVS_VMOD_50_OPP	(PRCM_AVS_BASE + 0xB)
+#define PRCM_AVS_VSAFE		(PRCM_AVS_BASE + 0xC)
+
+#define PRCM_AVS_VOLTAGE		0
+#define PRCM_AVS_VOLTAGE_MASK		0x3f
+#define PRCM_AVS_ISSLOWSTARTUP		6
+#define PRCM_AVS_ISSLOWSTARTUP_MASK	(1 << PRCM_AVS_ISSLOWSTARTUP)
+#define PRCM_AVS_ISMODEENABLE		7
+#define PRCM_AVS_ISMODEENABLE_MASK	(1 << PRCM_AVS_ISMODEENABLE)
+
+#define PRCM_BOOT_STATUS	0xFFF
+#define PRCM_ROMCODE_A2P	0xFFE
+#define PRCM_ROMCODE_P2A	0xFFD
+#define PRCM_XP70_CUR_PWR_STATE 0xFFC      /* 4 BYTES */
+
+#define PRCM_SW_RST_REASON 0xFF8 /* 2 bytes */
+
+#define _PRCM_MBOX_HEADER		0xFE8 /* 16 bytes */
+#define PRCM_MBOX_HEADER_REQ_MB0	(_PRCM_MBOX_HEADER + 0x0)
+#define PRCM_MBOX_HEADER_REQ_MB1	(_PRCM_MBOX_HEADER + 0x1)
+#define PRCM_MBOX_HEADER_REQ_MB2	(_PRCM_MBOX_HEADER + 0x2)
+#define PRCM_MBOX_HEADER_REQ_MB3	(_PRCM_MBOX_HEADER + 0x3)
+#define PRCM_MBOX_HEADER_REQ_MB4	(_PRCM_MBOX_HEADER + 0x4)
+#define PRCM_MBOX_HEADER_REQ_MB5	(_PRCM_MBOX_HEADER + 0x5)
+#define PRCM_MBOX_HEADER_ACK_MB0	(_PRCM_MBOX_HEADER + 0x8)
+
+/* Req Mailboxes */
+#define PRCM_REQ_MB0 0xFDC /* 12 bytes  */
+#define PRCM_REQ_MB1 0xFD0 /* 12 bytes  */
+#define PRCM_REQ_MB2 0xFC0 /* 16 bytes  */
+#define PRCM_REQ_MB3 0xE4C /* 372 bytes  */
+#define PRCM_REQ_MB4 0xE48 /* 4 bytes  */
+#define PRCM_REQ_MB5 0xE44 /* 4 bytes  */
+
+/* Ack Mailboxes */
+#define PRCM_ACK_MB0 0xE08 /* 52 bytes  */
+#define PRCM_ACK_MB1 0xE04 /* 4 bytes */
+#define PRCM_ACK_MB2 0xE00 /* 4 bytes */
+#define PRCM_ACK_MB3 0xDFC /* 4 bytes */
+#define PRCM_ACK_MB4 0xDF8 /* 4 bytes */
+#define PRCM_ACK_MB5 0xDF4 /* 4 bytes */
+
+/* Mailbox 0 headers */
+#define MB0H_POWER_STATE_TRANS		0
+#define MB0H_CONFIG_WAKEUPS_EXE		1
+#define MB0H_READ_WAKEUP_ACK		3
+#define MB0H_CONFIG_WAKEUPS_SLEEP	4
+
+#define MB0H_WAKEUP_EXE 2
+#define MB0H_WAKEUP_SLEEP 5
+
+/* Mailbox 0 REQs */
+#define PRCM_REQ_MB0_AP_POWER_STATE	(PRCM_REQ_MB0 + 0x0)
+#define PRCM_REQ_MB0_AP_PLL_STATE	(PRCM_REQ_MB0 + 0x1)
+#define PRCM_REQ_MB0_ULP_CLOCK_STATE	(PRCM_REQ_MB0 + 0x2)
+#define PRCM_REQ_MB0_DO_NOT_WFI		(PRCM_REQ_MB0 + 0x3)
+#define PRCM_REQ_MB0_WAKEUP_8500	(PRCM_REQ_MB0 + 0x4)
+#define PRCM_REQ_MB0_WAKEUP_4500	(PRCM_REQ_MB0 + 0x8)
+
+/* Mailbox 0 ACKs */
+#define PRCM_ACK_MB0_AP_PWRSTTR_STATUS	(PRCM_ACK_MB0 + 0x0)
+#define PRCM_ACK_MB0_READ_POINTER	(PRCM_ACK_MB0 + 0x1)
+#define PRCM_ACK_MB0_WAKEUP_0_8500	(PRCM_ACK_MB0 + 0x4)
+#define PRCM_ACK_MB0_WAKEUP_0_4500	(PRCM_ACK_MB0 + 0x8)
+#define PRCM_ACK_MB0_WAKEUP_1_8500	(PRCM_ACK_MB0 + 0x1C)
+#define PRCM_ACK_MB0_WAKEUP_1_4500	(PRCM_ACK_MB0 + 0x20)
+#define PRCM_ACK_MB0_EVENT_4500_NUMBERS	20
+
+/* Mailbox 1 headers */
+#define MB1H_ARM_APE_OPP 0x0
+#define MB1H_RESET_MODEM 0x2
+#define MB1H_REQUEST_APE_OPP_100_VOLT 0x3
+#define MB1H_RELEASE_APE_OPP_100_VOLT 0x4
+#define MB1H_RELEASE_USB_WAKEUP 0x5
+#define MB1H_PLL_ON_OFF 0x6
+
+/* Mailbox 1 Requests */
+#define PRCM_REQ_MB1_ARM_OPP			(PRCM_REQ_MB1 + 0x0)
+#define PRCM_REQ_MB1_APE_OPP			(PRCM_REQ_MB1 + 0x1)
+#define PRCM_REQ_MB1_PLL_ON_OFF			(PRCM_REQ_MB1 + 0x4)
+#define PLL_SOC0_OFF	0x1
+#define PLL_SOC0_ON	0x2
+#define PLL_SOC1_OFF	0x4
+#define PLL_SOC1_ON	0x8
+
+/* Mailbox 1 ACKs */
+#define PRCM_ACK_MB1_CURRENT_ARM_OPP	(PRCM_ACK_MB1 + 0x0)
+#define PRCM_ACK_MB1_CURRENT_APE_OPP	(PRCM_ACK_MB1 + 0x1)
+#define PRCM_ACK_MB1_APE_VOLTAGE_STATUS	(PRCM_ACK_MB1 + 0x2)
+#define PRCM_ACK_MB1_DVFS_STATUS	(PRCM_ACK_MB1 + 0x3)
+
+/* Mailbox 2 headers */
+#define MB2H_DPS	0x0
+#define MB2H_AUTO_PWR	0x1
+
+/* Mailbox 2 REQs */
+#define PRCM_REQ_MB2_SVA_MMDSP		(PRCM_REQ_MB2 + 0x0)
+#define PRCM_REQ_MB2_SVA_PIPE		(PRCM_REQ_MB2 + 0x1)
+#define PRCM_REQ_MB2_SIA_MMDSP		(PRCM_REQ_MB2 + 0x2)
+#define PRCM_REQ_MB2_SIA_PIPE		(PRCM_REQ_MB2 + 0x3)
+#define PRCM_REQ_MB2_SGA		(PRCM_REQ_MB2 + 0x4)
+#define PRCM_REQ_MB2_B2R2_MCDE		(PRCM_REQ_MB2 + 0x5)
+#define PRCM_REQ_MB2_ESRAM12		(PRCM_REQ_MB2 + 0x6)
+#define PRCM_REQ_MB2_ESRAM34		(PRCM_REQ_MB2 + 0x7)
+#define PRCM_REQ_MB2_AUTO_PM_SLEEP	(PRCM_REQ_MB2 + 0x8)
+#define PRCM_REQ_MB2_AUTO_PM_IDLE	(PRCM_REQ_MB2 + 0xC)
+
+/* Mailbox 2 ACKs */
+#define PRCM_ACK_MB2_DPS_STATUS (PRCM_ACK_MB2 + 0x0)
+#define HWACC_PWR_ST_OK 0xFE
+
+/* Mailbox 3 headers */
+#define MB3H_ANC	0x0
+#define MB3H_SIDETONE	0x1
+#define MB3H_SYSCLK	0xE
+
+/* Mailbox 3 Requests */
+#define PRCM_REQ_MB3_ANC_FIR_COEFF	(PRCM_REQ_MB3 + 0x0)
+#define PRCM_REQ_MB3_ANC_IIR_COEFF	(PRCM_REQ_MB3 + 0x20)
+#define PRCM_REQ_MB3_ANC_SHIFTER	(PRCM_REQ_MB3 + 0x60)
+#define PRCM_REQ_MB3_ANC_WARP		(PRCM_REQ_MB3 + 0x64)
+#define PRCM_REQ_MB3_SIDETONE_FIR_GAIN	(PRCM_REQ_MB3 + 0x68)
+#define PRCM_REQ_MB3_SIDETONE_FIR_COEFF	(PRCM_REQ_MB3 + 0x6C)
+#define PRCM_REQ_MB3_SYSCLK_MGT		(PRCM_REQ_MB3 + 0x16C)
+
+/* Mailbox 4 headers */
+#define MB4H_DDR_INIT	0x0
+#define MB4H_MEM_ST	0x1
+#define MB4H_HOTDOG	0x12
+#define MB4H_HOTMON	0x13
+#define MB4H_HOT_PERIOD	0x14
+#define MB4H_A9WDOG_CONF 0x16
+#define MB4H_A9WDOG_EN   0x17
+#define MB4H_A9WDOG_DIS  0x18
+#define MB4H_A9WDOG_LOAD 0x19
+#define MB4H_A9WDOG_KICK 0x20
+
+/* Mailbox 4 Requests */
+#define PRCM_REQ_MB4_DDR_ST_AP_SLEEP_IDLE	(PRCM_REQ_MB4 + 0x0)
+#define PRCM_REQ_MB4_DDR_ST_AP_DEEP_IDLE	(PRCM_REQ_MB4 + 0x1)
+#define PRCM_REQ_MB4_ESRAM0_ST			(PRCM_REQ_MB4 + 0x3)
+#define PRCM_REQ_MB4_HOTDOG_THRESHOLD		(PRCM_REQ_MB4 + 0x0)
+#define PRCM_REQ_MB4_HOTMON_LOW			(PRCM_REQ_MB4 + 0x0)
+#define PRCM_REQ_MB4_HOTMON_HIGH		(PRCM_REQ_MB4 + 0x1)
+#define PRCM_REQ_MB4_HOTMON_CONFIG		(PRCM_REQ_MB4 + 0x2)
+#define PRCM_REQ_MB4_HOT_PERIOD			(PRCM_REQ_MB4 + 0x0)
+#define HOTMON_CONFIG_LOW			BIT(0)
+#define HOTMON_CONFIG_HIGH			BIT(1)
+#define PRCM_REQ_MB4_A9WDOG_0			(PRCM_REQ_MB4 + 0x0)
+#define PRCM_REQ_MB4_A9WDOG_1			(PRCM_REQ_MB4 + 0x1)
+#define PRCM_REQ_MB4_A9WDOG_2			(PRCM_REQ_MB4 + 0x2)
+#define PRCM_REQ_MB4_A9WDOG_3			(PRCM_REQ_MB4 + 0x3)
+#define A9WDOG_AUTO_OFF_EN			BIT(7)
+#define A9WDOG_AUTO_OFF_DIS			0
+#define A9WDOG_ID_MASK				0xf
+
+/* Mailbox 5 Requests */
+#define PRCM_REQ_MB5_I2C_SLAVE_OP	(PRCM_REQ_MB5 + 0x0)
+#define PRCM_REQ_MB5_I2C_HW_BITS	(PRCM_REQ_MB5 + 0x1)
+#define PRCM_REQ_MB5_I2C_REG		(PRCM_REQ_MB5 + 0x2)
+#define PRCM_REQ_MB5_I2C_VAL		(PRCM_REQ_MB5 + 0x3)
+#define PRCMU_I2C_WRITE(slave) \
+	(((slave) << 1) | (cpu_is_u8500v2() ? BIT(6) : 0))
+#define PRCMU_I2C_READ(slave) \
+	(((slave) << 1) | BIT(0) | (cpu_is_u8500v2() ? BIT(6) : 0))
+#define PRCMU_I2C_STOP_EN		BIT(3)
+
+/* Mailbox 5 ACKs */
+#define PRCM_ACK_MB5_I2C_STATUS	(PRCM_ACK_MB5 + 0x1)
+#define PRCM_ACK_MB5_I2C_VAL	(PRCM_ACK_MB5 + 0x3)
+#define I2C_WR_OK 0x1
+#define I2C_RD_OK 0x2
+
+#define NUM_MB 8
+#define MBOX_BIT BIT
+#define ALL_MBOX_BITS (MBOX_BIT(NUM_MB) - 1)
+
+/*
+ * Wakeups/IRQs
+ */
+
+#define WAKEUP_BIT_RTC BIT(0)
+#define WAKEUP_BIT_RTT0 BIT(1)
+#define WAKEUP_BIT_RTT1 BIT(2)
+#define WAKEUP_BIT_HSI0 BIT(3)
+#define WAKEUP_BIT_HSI1 BIT(4)
+#define WAKEUP_BIT_CA_WAKE BIT(5)
+#define WAKEUP_BIT_USB BIT(6)
+#define WAKEUP_BIT_ABB BIT(7)
+#define WAKEUP_BIT_ABB_FIFO BIT(8)
+#define WAKEUP_BIT_SYSCLK_OK BIT(9)
+#define WAKEUP_BIT_CA_SLEEP BIT(10)
+#define WAKEUP_BIT_AC_WAKE_ACK BIT(11)
+#define WAKEUP_BIT_SIDE_TONE_OK BIT(12)
+#define WAKEUP_BIT_ANC_OK BIT(13)
+#define WAKEUP_BIT_SW_ERROR BIT(14)
+#define WAKEUP_BIT_AC_SLEEP_ACK BIT(15)
+#define WAKEUP_BIT_ARM BIT(17)
+#define WAKEUP_BIT_HOTMON_LOW BIT(18)
+#define WAKEUP_BIT_HOTMON_HIGH BIT(19)
+#define WAKEUP_BIT_MODEM_SW_RESET_REQ BIT(20)
+#define WAKEUP_BIT_GPIO0 BIT(23)
+#define WAKEUP_BIT_GPIO1 BIT(24)
+#define WAKEUP_BIT_GPIO2 BIT(25)
+#define WAKEUP_BIT_GPIO3 BIT(26)
+#define WAKEUP_BIT_GPIO4 BIT(27)
+#define WAKEUP_BIT_GPIO5 BIT(28)
+#define WAKEUP_BIT_GPIO6 BIT(29)
+#define WAKEUP_BIT_GPIO7 BIT(30)
+#define WAKEUP_BIT_GPIO8 BIT(31)
+
+static struct {
+	bool valid;
+	struct prcmu_fw_version version;
+} fw_info;
+
+/*
+ * This vector maps irq numbers to the bits in the bit field used in
+ * communication with the PRCMU firmware.
+ *
+ * The reason for having this is to keep the irq numbers contiguous even though
+ * the bits in the bit field are not. (The bits also have a tendency to move
+ * around, to further complicate matters.)
+ */
+#define IRQ_INDEX(_name) ((IRQ_PRCMU_##_name) - IRQ_PRCMU_BASE)
+#define IRQ_ENTRY(_name)[IRQ_INDEX(_name)] = (WAKEUP_BIT_##_name)
+static u32 prcmu_irq_bit[NUM_PRCMU_WAKEUPS] = {
+	IRQ_ENTRY(RTC),
+	IRQ_ENTRY(RTT0),
+	IRQ_ENTRY(RTT1),
+	IRQ_ENTRY(HSI0),
+	IRQ_ENTRY(HSI1),
+	IRQ_ENTRY(CA_WAKE),
+	IRQ_ENTRY(USB),
+	IRQ_ENTRY(ABB),
+	IRQ_ENTRY(ABB_FIFO),
+	IRQ_ENTRY(CA_SLEEP),
+	IRQ_ENTRY(ARM),
+	IRQ_ENTRY(HOTMON_LOW),
+	IRQ_ENTRY(HOTMON_HIGH),
+	IRQ_ENTRY(MODEM_SW_RESET_REQ),
+	IRQ_ENTRY(GPIO0),
+	IRQ_ENTRY(GPIO1),
+	IRQ_ENTRY(GPIO2),
+	IRQ_ENTRY(GPIO3),
+	IRQ_ENTRY(GPIO4),
+	IRQ_ENTRY(GPIO5),
+	IRQ_ENTRY(GPIO6),
+	IRQ_ENTRY(GPIO7),
+	IRQ_ENTRY(GPIO8)
+};
+
+#define VALID_WAKEUPS (BIT(NUM_PRCMU_WAKEUP_INDICES) - 1)
+#define WAKEUP_ENTRY(_name)[PRCMU_WAKEUP_INDEX_##_name] = (WAKEUP_BIT_##_name)
+static u32 prcmu_wakeup_bit[NUM_PRCMU_WAKEUP_INDICES] = {
+	WAKEUP_ENTRY(RTC),
+	WAKEUP_ENTRY(RTT0),
+	WAKEUP_ENTRY(RTT1),
+	WAKEUP_ENTRY(HSI0),
+	WAKEUP_ENTRY(HSI1),
+	WAKEUP_ENTRY(USB),
+	WAKEUP_ENTRY(ABB),
+	WAKEUP_ENTRY(ABB_FIFO),
+	WAKEUP_ENTRY(ARM)
+};
+
+/*
+ * mb0_transfer - state needed for mailbox 0 communication.
+ * @lock:		The transaction lock.
+ * @dbb_events_lock:	A lock used to handle concurrent access to (parts of)
+ *			the request data.
+ * @mask_work:		Work structure used for (un)masking wakeup interrupts.
+ * @req:		Request data that need to persist between requests.
+ */
+static struct {
+	spinlock_t lock;
+	spinlock_t dbb_irqs_lock;
+	struct work_struct mask_work;
+	struct mutex ac_wake_lock;
+	struct completion ac_wake_work;
+	struct {
+		u32 dbb_irqs;
+		u32 dbb_wakeups;
+		u32 abb_events;
+	} req;
+} mb0_transfer;
+
+/*
+ * mb1_transfer - state needed for mailbox 1 communication.
+ * @lock:	The transaction lock.
+ * @work:	The transaction completion structure.
+ * @ape_opp:	The current APE OPP.
+ * @ack:	Reply ("acknowledge") data.
+ */
+static struct {
+	struct mutex lock;
+	struct completion work;
+	u8 ape_opp;
+	struct {
+		u8 header;
+		u8 arm_opp;
+		u8 ape_opp;
+		u8 ape_voltage_status;
+	} ack;
+} mb1_transfer;
+
+/*
+ * mb2_transfer - state needed for mailbox 2 communication.
+ * @lock:            The transaction lock.
+ * @work:            The transaction completion structure.
+ * @auto_pm_lock:    The autonomous power management configuration lock.
+ * @auto_pm_enabled: A flag indicating whether autonomous PM is enabled.
+ * @req:             Request data that need to persist between requests.
+ * @ack:             Reply ("acknowledge") data.
+ */
+static struct {
+	struct mutex lock;
+	struct completion work;
+	spinlock_t auto_pm_lock;
+	bool auto_pm_enabled;
+	struct {
+		u8 status;
+	} ack;
+} mb2_transfer;
+
+/*
+ * mb3_transfer - state needed for mailbox 3 communication.
+ * @lock:		The request lock.
+ * @sysclk_lock:	A lock used to handle concurrent sysclk requests.
+ * @sysclk_work:	Work structure used for sysclk requests.
+ */
+static struct {
+	spinlock_t lock;
+	struct mutex sysclk_lock;
+	struct completion sysclk_work;
+} mb3_transfer;
+
+/*
+ * mb4_transfer - state needed for mailbox 4 communication.
+ * @lock:	The transaction lock.
+ * @work:	The transaction completion structure.
+ */
+static struct {
+	struct mutex lock;
+	struct completion work;
+} mb4_transfer;
+
+/*
+ * mb5_transfer - state needed for mailbox 5 communication.
+ * @lock:	The transaction lock.
+ * @work:	The transaction completion structure.
+ * @ack:	Reply ("acknowledge") data.
+ */
+static struct {
+	struct mutex lock;
+	struct completion work;
+	struct {
+		u8 status;
+		u8 value;
+	} ack;
+} mb5_transfer;
+
+static atomic_t ac_wake_req_state = ATOMIC_INIT(0);
+
+/* Spinlocks */
+static DEFINE_SPINLOCK(prcmu_lock);
+static DEFINE_SPINLOCK(clkout_lock);
+
+/* Global var to runtime determine TCDM base for v2 or v1 */
+static __iomem void *tcdm_base;
+
+struct clk_mgt {
+	void __iomem *reg;
+	u32 pllsw;
+	int branch;
+	bool clk38div;
+};
+
+enum {
+	PLL_RAW,
+	PLL_FIX,
+	PLL_DIV
+};
+
+static DEFINE_SPINLOCK(clk_mgt_lock);
+
+#define CLK_MGT_ENTRY(_name, _branch, _clk38div)[PRCMU_##_name] = \
+	{ (PRCM_##_name##_MGT), 0 , _branch, _clk38div}
+struct clk_mgt clk_mgt[PRCMU_NUM_REG_CLOCKS] = {
+	CLK_MGT_ENTRY(SGACLK, PLL_DIV, false),
+	CLK_MGT_ENTRY(UARTCLK, PLL_FIX, true),
+	CLK_MGT_ENTRY(MSP02CLK, PLL_FIX, true),
+	CLK_MGT_ENTRY(MSP1CLK, PLL_FIX, true),
+	CLK_MGT_ENTRY(I2CCLK, PLL_FIX, true),
+	CLK_MGT_ENTRY(SDMMCCLK, PLL_DIV, true),
+	CLK_MGT_ENTRY(SLIMCLK, PLL_FIX, true),
+	CLK_MGT_ENTRY(PER1CLK, PLL_DIV, true),
+	CLK_MGT_ENTRY(PER2CLK, PLL_DIV, true),
+	CLK_MGT_ENTRY(PER3CLK, PLL_DIV, true),
+	CLK_MGT_ENTRY(PER5CLK, PLL_DIV, true),
+	CLK_MGT_ENTRY(PER6CLK, PLL_DIV, true),
+	CLK_MGT_ENTRY(PER7CLK, PLL_DIV, true),
+	CLK_MGT_ENTRY(LCDCLK, PLL_FIX, true),
+	CLK_MGT_ENTRY(BMLCLK, PLL_DIV, true),
+	CLK_MGT_ENTRY(HSITXCLK, PLL_DIV, true),
+	CLK_MGT_ENTRY(HSIRXCLK, PLL_DIV, true),
+	CLK_MGT_ENTRY(HDMICLK, PLL_FIX, false),
+	CLK_MGT_ENTRY(APEATCLK, PLL_DIV, true),
+	CLK_MGT_ENTRY(APETRACECLK, PLL_DIV, true),
+	CLK_MGT_ENTRY(MCDECLK, PLL_DIV, true),
+	CLK_MGT_ENTRY(IPI2CCLK, PLL_FIX, true),
+	CLK_MGT_ENTRY(DSIALTCLK, PLL_FIX, false),
+	CLK_MGT_ENTRY(DMACLK, PLL_DIV, true),
+	CLK_MGT_ENTRY(B2R2CLK, PLL_DIV, true),
+	CLK_MGT_ENTRY(TVCLK, PLL_FIX, true),
+	CLK_MGT_ENTRY(SSPCLK, PLL_FIX, true),
+	CLK_MGT_ENTRY(RNGCLK, PLL_FIX, true),
+	CLK_MGT_ENTRY(UICCCLK, PLL_FIX, false),
+};
+
+struct dsiclk {
+	u32 divsel_mask;
+	u32 divsel_shift;
+	u32 divsel;
+};
+
+static struct dsiclk dsiclk[2] = {
+	{
+		.divsel_mask = PRCM_DSI_PLLOUT_SEL_DSI0_PLLOUT_DIVSEL_MASK,
+		.divsel_shift = PRCM_DSI_PLLOUT_SEL_DSI0_PLLOUT_DIVSEL_SHIFT,
+		.divsel = PRCM_DSI_PLLOUT_SEL_PHI,
+	},
+	{
+		.divsel_mask = PRCM_DSI_PLLOUT_SEL_DSI1_PLLOUT_DIVSEL_MASK,
+		.divsel_shift = PRCM_DSI_PLLOUT_SEL_DSI1_PLLOUT_DIVSEL_SHIFT,
+		.divsel = PRCM_DSI_PLLOUT_SEL_PHI,
+	}
+};
+
+struct dsiescclk {
+	u32 en;
+	u32 div_mask;
+	u32 div_shift;
+};
+
+static struct dsiescclk dsiescclk[3] = {
+	{
+		.en = PRCM_DSITVCLK_DIV_DSI0_ESC_CLK_EN,
+		.div_mask = PRCM_DSITVCLK_DIV_DSI0_ESC_CLK_DIV_MASK,
+		.div_shift = PRCM_DSITVCLK_DIV_DSI0_ESC_CLK_DIV_SHIFT,
+	},
+	{
+		.en = PRCM_DSITVCLK_DIV_DSI1_ESC_CLK_EN,
+		.div_mask = PRCM_DSITVCLK_DIV_DSI1_ESC_CLK_DIV_MASK,
+		.div_shift = PRCM_DSITVCLK_DIV_DSI1_ESC_CLK_DIV_SHIFT,
+	},
+	{
+		.en = PRCM_DSITVCLK_DIV_DSI2_ESC_CLK_EN,
+		.div_mask = PRCM_DSITVCLK_DIV_DSI2_ESC_CLK_DIV_MASK,
+		.div_shift = PRCM_DSITVCLK_DIV_DSI2_ESC_CLK_DIV_SHIFT,
+	}
+};
+
+/*
+* Used by MCDE to setup all necessary PRCMU registers
+*/
+#define PRCMU_RESET_DSIPLL		0x00004000
+#define PRCMU_UNCLAMP_DSIPLL		0x00400800
+
+#define PRCMU_CLK_PLL_DIV_SHIFT		0
+#define PRCMU_CLK_PLL_SW_SHIFT		5
+#define PRCMU_CLK_38			(1 << 9)
+#define PRCMU_CLK_38_SRC		(1 << 10)
+#define PRCMU_CLK_38_DIV		(1 << 11)
+
+/* PLLDIV=12, PLLSW=4 (PLLDDR) */
+#define PRCMU_DSI_CLOCK_SETTING		0x0000008C
+
+/* DPI 50000000 Hz */
+#define PRCMU_DPI_CLOCK_SETTING		((1 << PRCMU_CLK_PLL_SW_SHIFT) | \
+					  (16 << PRCMU_CLK_PLL_DIV_SHIFT))
+#define PRCMU_DSI_LP_CLOCK_SETTING	0x00000E00
+
+/* D=101, N=1, R=4, SELDIV2=0 */
+#define PRCMU_PLLDSI_FREQ_SETTING	0x00040165
+
+#define PRCMU_ENABLE_PLLDSI		0x00000001
+#define PRCMU_DISABLE_PLLDSI		0x00000000
+#define PRCMU_RELEASE_RESET_DSS		0x0000400C
+#define PRCMU_DSI_PLLOUT_SEL_SETTING	0x00000202
+/* ESC clk, div0=1, div1=1, div2=3 */
+#define PRCMU_ENABLE_ESCAPE_CLOCK_DIV	0x07030101
+#define PRCMU_DISABLE_ESCAPE_CLOCK_DIV	0x00030101
+#define PRCMU_DSI_RESET_SW		0x00000007
+
+#define PRCMU_PLLDSI_LOCKP_LOCKED	0x3
+
+int db8500_prcmu_enable_dsipll(void)
+{
+	int i;
+
+	/* Clear DSIPLL_RESETN */
+	writel(PRCMU_RESET_DSIPLL, PRCM_APE_RESETN_CLR);
+	/* Unclamp DSIPLL in/out */
+	writel(PRCMU_UNCLAMP_DSIPLL, PRCM_MMIP_LS_CLAMP_CLR);
+
+	/* Set DSI PLL FREQ */
+	writel(PRCMU_PLLDSI_FREQ_SETTING, PRCM_PLLDSI_FREQ);
+	writel(PRCMU_DSI_PLLOUT_SEL_SETTING, PRCM_DSI_PLLOUT_SEL);
+	/* Enable Escape clocks */
+	writel(PRCMU_ENABLE_ESCAPE_CLOCK_DIV, PRCM_DSITVCLK_DIV);
+
+	/* Start DSI PLL */
+	writel(PRCMU_ENABLE_PLLDSI, PRCM_PLLDSI_ENABLE);
+	/* Reset DSI PLL */
+	writel(PRCMU_DSI_RESET_SW, PRCM_DSI_SW_RESET);
+	for (i = 0; i < 10; i++) {
+		if ((readl(PRCM_PLLDSI_LOCKP) & PRCMU_PLLDSI_LOCKP_LOCKED)
+					== PRCMU_PLLDSI_LOCKP_LOCKED)
+			break;
+		udelay(100);
+	}
+	/* Set DSIPLL_RESETN */
+	writel(PRCMU_RESET_DSIPLL, PRCM_APE_RESETN_SET);
+	return 0;
+}
+
+int db8500_prcmu_disable_dsipll(void)
+{
+	/* Disable dsi pll */
+	writel(PRCMU_DISABLE_PLLDSI, PRCM_PLLDSI_ENABLE);
+	/* Disable  escapeclock */
+	writel(PRCMU_DISABLE_ESCAPE_CLOCK_DIV, PRCM_DSITVCLK_DIV);
+	return 0;
+}
+
+int db8500_prcmu_set_display_clocks(void)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&clk_mgt_lock, flags);
+
+	/* Grab the HW semaphore. */
+	while ((readl(PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0)
+		cpu_relax();
+
+	writel(PRCMU_DSI_CLOCK_SETTING, PRCM_HDMICLK_MGT);
+	writel(PRCMU_DSI_LP_CLOCK_SETTING, PRCM_TVCLK_MGT);
+	writel(PRCMU_DPI_CLOCK_SETTING, PRCM_LCDCLK_MGT);
+
+	/* Release the HW semaphore. */
+	writel(0, PRCM_SEM);
+
+	spin_unlock_irqrestore(&clk_mgt_lock, flags);
+
+	return 0;
+}
+
+u32 db8500_prcmu_read(unsigned int reg)
+{
+	return readl(_PRCMU_BASE + reg);
+}
+
+void db8500_prcmu_write(unsigned int reg, u32 value)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&prcmu_lock, flags);
+	writel(value, (_PRCMU_BASE + reg));
+	spin_unlock_irqrestore(&prcmu_lock, flags);
+}
+
+void db8500_prcmu_write_masked(unsigned int reg, u32 mask, u32 value)
+{
+	u32 val;
+	unsigned long flags;
+
+	spin_lock_irqsave(&prcmu_lock, flags);
+	val = readl(_PRCMU_BASE + reg);
+	val = ((val & ~mask) | (value & mask));
+	writel(val, (_PRCMU_BASE + reg));
+	spin_unlock_irqrestore(&prcmu_lock, flags);
+}
+
+struct prcmu_fw_version *prcmu_get_fw_version(void)
+{
+	return fw_info.valid ? &fw_info.version : NULL;
+}
+
+bool prcmu_has_arm_maxopp(void)
+{
+	return (readb(tcdm_base + PRCM_AVS_VARM_MAX_OPP) &
+		PRCM_AVS_ISMODEENABLE_MASK) == PRCM_AVS_ISMODEENABLE_MASK;
+}
+
+/**
+ * prcmu_get_boot_status - PRCMU boot status checking
+ * Returns: the current PRCMU boot status
+ */
+int prcmu_get_boot_status(void)
+{
+	return readb(tcdm_base + PRCM_BOOT_STATUS);
+}
+
+/**
+ * prcmu_set_rc_a2p - This function is used to run few power state sequences
+ * @val: Value to be set, i.e. transition requested
+ * Returns: 0 on success, -EINVAL on invalid argument
+ *
+ * This function is used to run the following power state sequences -
+ * any state to ApReset,  ApDeepSleep to ApExecute, ApExecute to ApDeepSleep
+ */
+int prcmu_set_rc_a2p(enum romcode_write val)
+{
+	if (val < RDY_2_DS || val > RDY_2_XP70_RST)
+		return -EINVAL;
+	writeb(val, (tcdm_base + PRCM_ROMCODE_A2P));
+	return 0;
+}
+
+/**
+ * prcmu_get_rc_p2a - This function is used to get power state sequences
+ * Returns: the power transition that has last happened
+ *
+ * This function can return the following transitions-
+ * any state to ApReset,  ApDeepSleep to ApExecute, ApExecute to ApDeepSleep
+ */
+enum romcode_read prcmu_get_rc_p2a(void)
+{
+	return readb(tcdm_base + PRCM_ROMCODE_P2A);
+}
+
+/**
+ * prcmu_get_current_mode - Return the current XP70 power mode
+ * Returns: Returns the current AP(ARM) power mode: init,
+ * apBoot, apExecute, apDeepSleep, apSleep, apIdle, apReset
+ */
+enum ap_pwrst prcmu_get_xp70_current_state(void)
+{
+	return readb(tcdm_base + PRCM_XP70_CUR_PWR_STATE);
+}
+
+/**
+ * prcmu_config_clkout - Configure one of the programmable clock outputs.
+ * @clkout:	The CLKOUT number (0 or 1).
+ * @source:	The clock to be used (one of the PRCMU_CLKSRC_*).
+ * @div:	The divider to be applied.
+ *
+ * Configures one of the programmable clock outputs (CLKOUTs).
+ * @div should be in the range [1,63] to request a configuration, or 0 to
+ * inform that the configuration is no longer requested.
+ */
+int prcmu_config_clkout(u8 clkout, u8 source, u8 div)
+{
+	static int requests[2];
+	int r = 0;
+	unsigned long flags;
+	u32 val;
+	u32 bits;
+	u32 mask;
+	u32 div_mask;
+
+	BUG_ON(clkout > 1);
+	BUG_ON(div > 63);
+	BUG_ON((clkout == 0) && (source > PRCMU_CLKSRC_CLK009));
+
+	if (!div && !requests[clkout])
+		return -EINVAL;
+
+	switch (clkout) {
+	case 0:
+		div_mask = PRCM_CLKOCR_CLKODIV0_MASK;
+		mask = (PRCM_CLKOCR_CLKODIV0_MASK | PRCM_CLKOCR_CLKOSEL0_MASK);
+		bits = ((source << PRCM_CLKOCR_CLKOSEL0_SHIFT) |
+			(div << PRCM_CLKOCR_CLKODIV0_SHIFT));
+		break;
+	case 1:
+		div_mask = PRCM_CLKOCR_CLKODIV1_MASK;
+		mask = (PRCM_CLKOCR_CLKODIV1_MASK | PRCM_CLKOCR_CLKOSEL1_MASK |
+			PRCM_CLKOCR_CLK1TYPE);
+		bits = ((source << PRCM_CLKOCR_CLKOSEL1_SHIFT) |
+			(div << PRCM_CLKOCR_CLKODIV1_SHIFT));
+		break;
+	}
+	bits &= mask;
+
+	spin_lock_irqsave(&clkout_lock, flags);
+
+	val = readl(PRCM_CLKOCR);
+	if (val & div_mask) {
+		if (div) {
+			if ((val & mask) != bits) {
+				r = -EBUSY;
+				goto unlock_and_return;
+			}
+		} else {
+			if ((val & mask & ~div_mask) != bits) {
+				r = -EINVAL;
+				goto unlock_and_return;
+			}
+		}
+	}
+	writel((bits | (val & ~mask)), PRCM_CLKOCR);
+	requests[clkout] += (div ? 1 : -1);
+
+unlock_and_return:
+	spin_unlock_irqrestore(&clkout_lock, flags);
+
+	return r;
+}
+
+int db8500_prcmu_set_power_state(u8 state, bool keep_ulp_clk, bool keep_ap_pll)
+{
+	unsigned long flags;
+
+	BUG_ON((state < PRCMU_AP_SLEEP) || (PRCMU_AP_DEEP_IDLE < state));
+
+	spin_lock_irqsave(&mb0_transfer.lock, flags);
+
+	while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(0))
+		cpu_relax();
+
+	writeb(MB0H_POWER_STATE_TRANS, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB0));
+	writeb(state, (tcdm_base + PRCM_REQ_MB0_AP_POWER_STATE));
+	writeb((keep_ap_pll ? 1 : 0), (tcdm_base + PRCM_REQ_MB0_AP_PLL_STATE));
+	writeb((keep_ulp_clk ? 1 : 0),
+		(tcdm_base + PRCM_REQ_MB0_ULP_CLOCK_STATE));
+	writeb(0, (tcdm_base + PRCM_REQ_MB0_DO_NOT_WFI));
+	writel(MBOX_BIT(0), PRCM_MBOX_CPU_SET);
+
+	spin_unlock_irqrestore(&mb0_transfer.lock, flags);
+
+	return 0;
+}
+
+u8 db8500_prcmu_get_power_state_result(void)
+{
+	return readb(tcdm_base + PRCM_ACK_MB0_AP_PWRSTTR_STATUS);
+}
+
+/* This function decouple the gic from the prcmu */
+int db8500_prcmu_gic_decouple(void)
+{
+	u32 val = readl(PRCM_A9_MASK_REQ);
+
+	/* Set bit 0 register value to 1 */
+	writel(val | PRCM_A9_MASK_REQ_PRCM_A9_MASK_REQ,
+	       PRCM_A9_MASK_REQ);
+
+	/* Make sure the register is updated */
+	readl(PRCM_A9_MASK_REQ);
+
+	/* Wait a few cycles for the gic mask completion */
+	udelay(1);
+
+	return 0;
+}
+
+/* This function recouple the gic with the prcmu */
+int db8500_prcmu_gic_recouple(void)
+{
+	u32 val = readl(PRCM_A9_MASK_REQ);
+
+	/* Set bit 0 register value to 0 */
+	writel(val & ~PRCM_A9_MASK_REQ_PRCM_A9_MASK_REQ, PRCM_A9_MASK_REQ);
+
+	return 0;
+}
+
+#define PRCMU_GIC_NUMBER_REGS 5
+
+/*
+ * This function checks if there are pending irq on the gic. It only
+ * makes sense if the gic has been decoupled before with the
+ * db8500_prcmu_gic_decouple function. Disabling an interrupt only
+ * disables the forwarding of the interrupt to any CPU interface. It
+ * does not prevent the interrupt from changing state, for example
+ * becoming pending, or active and pending if it is already
+ * active. Hence, we have to check the interrupt is pending *and* is
+ * active.
+ */
+bool db8500_prcmu_gic_pending_irq(void)
+{
+	u32 pr; /* Pending register */
+	u32 er; /* Enable register */
+	void __iomem *dist_base = __io_address(U8500_GIC_DIST_BASE);
+	int i;
+
+        /* 5 registers. STI & PPI not skipped */
+	for (i = 0; i < PRCMU_GIC_NUMBER_REGS; i++) {
+
+		pr = readl_relaxed(dist_base + GIC_DIST_PENDING_SET + i * 4);
+		er = readl_relaxed(dist_base + GIC_DIST_ENABLE_SET + i * 4);
+
+		if (pr & er)
+			return true; /* There is a pending interrupt */
+	}
+
+	return false;
+}
+
+/*
+ * This function checks if there are pending interrupt on the
+ * prcmu which has been delegated to monitor the irqs with the
+ * db8500_prcmu_copy_gic_settings function.
+ */
+bool db8500_prcmu_pending_irq(void)
+{
+	u32 it, im;
+	int i;
+
+	for (i = 0; i < PRCMU_GIC_NUMBER_REGS - 1; i++) {
+		it = readl(PRCM_ARMITVAL31TO0 + i * 4);
+		im = readl(PRCM_ARMITMSK31TO0 + i * 4);
+		if (it & im)
+			return true; /* There is a pending interrupt */
+	}
+
+	return false;
+}
+
+/*
+ * This function checks if the specified cpu is in in WFI. It's usage
+ * makes sense only if the gic is decoupled with the db8500_prcmu_gic_decouple
+ * function. Of course passing smp_processor_id() to this function will
+ * always return false...
+ */
+bool db8500_prcmu_is_cpu_in_wfi(int cpu)
+{
+	return readl(PRCM_ARM_WFI_STANDBY) & cpu ? PRCM_ARM_WFI_STANDBY_WFI1 :
+		     PRCM_ARM_WFI_STANDBY_WFI0;
+}
+
+/*
+ * This function copies the gic SPI settings to the prcmu in order to
+ * monitor them and abort/finish the retention/off sequence or state.
+ */
+int db8500_prcmu_copy_gic_settings(void)
+{
+	u32 er; /* Enable register */
+	void __iomem *dist_base = __io_address(U8500_GIC_DIST_BASE);
+	int i;
+
+        /* We skip the STI and PPI */
+	for (i = 0; i < PRCMU_GIC_NUMBER_REGS - 1; i++) {
+		er = readl_relaxed(dist_base +
+				   GIC_DIST_ENABLE_SET + (i + 1) * 4);
+		writel(er, PRCM_ARMITMSK31TO0 + i * 4);
+	}
+
+	return 0;
+}
+
+/* This function should only be called while mb0_transfer.lock is held. */
+static void config_wakeups(void)
+{
+	const u8 header[2] = {
+		MB0H_CONFIG_WAKEUPS_EXE,
+		MB0H_CONFIG_WAKEUPS_SLEEP
+	};
+	static u32 last_dbb_events;
+	static u32 last_abb_events;
+	u32 dbb_events;
+	u32 abb_events;
+	unsigned int i;
+
+	dbb_events = mb0_transfer.req.dbb_irqs | mb0_transfer.req.dbb_wakeups;
+	dbb_events |= (WAKEUP_BIT_AC_WAKE_ACK | WAKEUP_BIT_AC_SLEEP_ACK);
+
+	abb_events = mb0_transfer.req.abb_events;
+
+	if ((dbb_events == last_dbb_events) && (abb_events == last_abb_events))
+		return;
+
+	for (i = 0; i < 2; i++) {
+		while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(0))
+			cpu_relax();
+		writel(dbb_events, (tcdm_base + PRCM_REQ_MB0_WAKEUP_8500));
+		writel(abb_events, (tcdm_base + PRCM_REQ_MB0_WAKEUP_4500));
+		writeb(header[i], (tcdm_base + PRCM_MBOX_HEADER_REQ_MB0));
+		writel(MBOX_BIT(0), PRCM_MBOX_CPU_SET);
+	}
+	last_dbb_events = dbb_events;
+	last_abb_events = abb_events;
+}
+
+void db8500_prcmu_enable_wakeups(u32 wakeups)
+{
+	unsigned long flags;
+	u32 bits;
+	int i;
+
+	BUG_ON(wakeups != (wakeups & VALID_WAKEUPS));
+
+	for (i = 0, bits = 0; i < NUM_PRCMU_WAKEUP_INDICES; i++) {
+		if (wakeups & BIT(i))
+			bits |= prcmu_wakeup_bit[i];
+	}
+
+	spin_lock_irqsave(&mb0_transfer.lock, flags);
+
+	mb0_transfer.req.dbb_wakeups = bits;
+	config_wakeups();
+
+	spin_unlock_irqrestore(&mb0_transfer.lock, flags);
+}
+
+void db8500_prcmu_config_abb_event_readout(u32 abb_events)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&mb0_transfer.lock, flags);
+
+	mb0_transfer.req.abb_events = abb_events;
+	config_wakeups();
+
+	spin_unlock_irqrestore(&mb0_transfer.lock, flags);
+}
+
+void db8500_prcmu_get_abb_event_buffer(void __iomem **buf)
+{
+	if (readb(tcdm_base + PRCM_ACK_MB0_READ_POINTER) & 1)
+		*buf = (tcdm_base + PRCM_ACK_MB0_WAKEUP_1_4500);
+	else
+		*buf = (tcdm_base + PRCM_ACK_MB0_WAKEUP_0_4500);
+}
+
+/**
+ * db8500_prcmu_set_arm_opp - set the appropriate ARM OPP
+ * @opp: The new ARM operating point to which transition is to be made
+ * Returns: 0 on success, non-zero on failure
+ *
+ * This function sets the the operating point of the ARM.
+ */
+int db8500_prcmu_set_arm_opp(u8 opp)
+{
+	int r;
+
+	if (opp < ARM_NO_CHANGE || opp > ARM_EXTCLK)
+		return -EINVAL;
+
+	r = 0;
+
+	mutex_lock(&mb1_transfer.lock);
+
+	while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
+		cpu_relax();
+
+	writeb(MB1H_ARM_APE_OPP, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB1));
+	writeb(opp, (tcdm_base + PRCM_REQ_MB1_ARM_OPP));
+	writeb(APE_NO_CHANGE, (tcdm_base + PRCM_REQ_MB1_APE_OPP));
+
+	writel(MBOX_BIT(1), PRCM_MBOX_CPU_SET);
+	wait_for_completion(&mb1_transfer.work);
+
+	if ((mb1_transfer.ack.header != MB1H_ARM_APE_OPP) ||
+		(mb1_transfer.ack.arm_opp != opp))
+		r = -EIO;
+
+	mutex_unlock(&mb1_transfer.lock);
+
+	return r;
+}
+
+/**
+ * db8500_prcmu_get_arm_opp - get the current ARM OPP
+ *
+ * Returns: the current ARM OPP
+ */
+int db8500_prcmu_get_arm_opp(void)
+{
+	return readb(tcdm_base + PRCM_ACK_MB1_CURRENT_ARM_OPP);
+}
+
+/**
+ * db8500_prcmu_get_ddr_opp - get the current DDR OPP
+ *
+ * Returns: the current DDR OPP
+ */
+int db8500_prcmu_get_ddr_opp(void)
+{
+	return readb(PRCM_DDR_SUBSYS_APE_MINBW);
+}
+
+/**
+ * db8500_set_ddr_opp - set the appropriate DDR OPP
+ * @opp: The new DDR operating point to which transition is to be made
+ * Returns: 0 on success, non-zero on failure
+ *
+ * This function sets the operating point of the DDR.
+ */
+int db8500_prcmu_set_ddr_opp(u8 opp)
+{
+	if (opp < DDR_100_OPP || opp > DDR_25_OPP)
+		return -EINVAL;
+	/* Changing the DDR OPP can hang the hardware pre-v21 */
+	if (cpu_is_u8500v20_or_later() && !cpu_is_u8500v20())
+		writeb(opp, PRCM_DDR_SUBSYS_APE_MINBW);
+
+	return 0;
+}
+
+/* Divide the frequency of certain clocks by 2 for APE_50_PARTLY_25_OPP. */
+static void request_even_slower_clocks(bool enable)
+{
+	void __iomem *clock_reg[] = {
+		PRCM_ACLK_MGT,
+		PRCM_DMACLK_MGT
+	};
+	unsigned long flags;
+	unsigned int i;
+
+	spin_lock_irqsave(&clk_mgt_lock, flags);
+
+	/* Grab the HW semaphore. */
+	while ((readl(PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0)
+		cpu_relax();
+
+	for (i = 0; i < ARRAY_SIZE(clock_reg); i++) {
+		u32 val;
+		u32 div;
+
+		val = readl(clock_reg[i]);
+		div = (val & PRCM_CLK_MGT_CLKPLLDIV_MASK);
+		if (enable) {
+			if ((div <= 1) || (div > 15)) {
+				pr_err("prcmu: Bad clock divider %d in %s\n",
+					div, __func__);
+				goto unlock_and_return;
+			}
+			div <<= 1;
+		} else {
+			if (div <= 2)
+				goto unlock_and_return;
+			div >>= 1;
+		}
+		val = ((val & ~PRCM_CLK_MGT_CLKPLLDIV_MASK) |
+			(div & PRCM_CLK_MGT_CLKPLLDIV_MASK));
+		writel(val, clock_reg[i]);
+	}
+
+unlock_and_return:
+	/* Release the HW semaphore. */
+	writel(0, PRCM_SEM);
+
+	spin_unlock_irqrestore(&clk_mgt_lock, flags);
+}
+
+/**
+ * db8500_set_ape_opp - set the appropriate APE OPP
+ * @opp: The new APE operating point to which transition is to be made
+ * Returns: 0 on success, non-zero on failure
+ *
+ * This function sets the operating point of the APE.
+ */
+int db8500_prcmu_set_ape_opp(u8 opp)
+{
+	int r = 0;
+
+	if (opp == mb1_transfer.ape_opp)
+		return 0;
+
+	mutex_lock(&mb1_transfer.lock);
+
+	if (mb1_transfer.ape_opp == APE_50_PARTLY_25_OPP)
+		request_even_slower_clocks(false);
+
+	if ((opp != APE_100_OPP) && (mb1_transfer.ape_opp != APE_100_OPP))
+		goto skip_message;
+
+	while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
+		cpu_relax();
+
+	writeb(MB1H_ARM_APE_OPP, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB1));
+	writeb(ARM_NO_CHANGE, (tcdm_base + PRCM_REQ_MB1_ARM_OPP));
+	writeb(((opp == APE_50_PARTLY_25_OPP) ? APE_50_OPP : opp),
+		(tcdm_base + PRCM_REQ_MB1_APE_OPP));
+
+	writel(MBOX_BIT(1), PRCM_MBOX_CPU_SET);
+	wait_for_completion(&mb1_transfer.work);
+
+	if ((mb1_transfer.ack.header != MB1H_ARM_APE_OPP) ||
+		(mb1_transfer.ack.ape_opp != opp))
+		r = -EIO;
+
+skip_message:
+	if ((!r && (opp == APE_50_PARTLY_25_OPP)) ||
+		(r && (mb1_transfer.ape_opp == APE_50_PARTLY_25_OPP)))
+		request_even_slower_clocks(true);
+	if (!r)
+		mb1_transfer.ape_opp = opp;
+
+	mutex_unlock(&mb1_transfer.lock);
+
+	return r;
+}
+
+/**
+ * db8500_prcmu_get_ape_opp - get the current APE OPP
+ *
+ * Returns: the current APE OPP
+ */
+int db8500_prcmu_get_ape_opp(void)
+{
+	return readb(tcdm_base + PRCM_ACK_MB1_CURRENT_APE_OPP);
+}
+
+/**
+ * prcmu_request_ape_opp_100_voltage - Request APE OPP 100% voltage
+ * @enable: true to request the higher voltage, false to drop a request.
+ *
+ * Calls to this function to enable and disable requests must be balanced.
+ */
+int prcmu_request_ape_opp_100_voltage(bool enable)
+{
+	int r = 0;
+	u8 header;
+	static unsigned int requests;
+
+	mutex_lock(&mb1_transfer.lock);
+
+	if (enable) {
+		if (0 != requests++)
+			goto unlock_and_return;
+		header = MB1H_REQUEST_APE_OPP_100_VOLT;
+	} else {
+		if (requests == 0) {
+			r = -EIO;
+			goto unlock_and_return;
+		} else if (1 != requests--) {
+			goto unlock_and_return;
+		}
+		header = MB1H_RELEASE_APE_OPP_100_VOLT;
+	}
+
+	while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
+		cpu_relax();
+
+	writeb(header, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB1));
+
+	writel(MBOX_BIT(1), PRCM_MBOX_CPU_SET);
+	wait_for_completion(&mb1_transfer.work);
+
+	if ((mb1_transfer.ack.header != header) ||
+		((mb1_transfer.ack.ape_voltage_status & BIT(0)) != 0))
+		r = -EIO;
+
+unlock_and_return:
+	mutex_unlock(&mb1_transfer.lock);
+
+	return r;
+}
+
+/**
+ * prcmu_release_usb_wakeup_state - release the state required by a USB wakeup
+ *
+ * This function releases the power state requirements of a USB wakeup.
+ */
+int prcmu_release_usb_wakeup_state(void)
+{
+	int r = 0;
+
+	mutex_lock(&mb1_transfer.lock);
+
+	while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
+		cpu_relax();
+
+	writeb(MB1H_RELEASE_USB_WAKEUP,
+		(tcdm_base + PRCM_MBOX_HEADER_REQ_MB1));
+
+	writel(MBOX_BIT(1), PRCM_MBOX_CPU_SET);
+	wait_for_completion(&mb1_transfer.work);
+
+	if ((mb1_transfer.ack.header != MB1H_RELEASE_USB_WAKEUP) ||
+		((mb1_transfer.ack.ape_voltage_status & BIT(0)) != 0))
+		r = -EIO;
+
+	mutex_unlock(&mb1_transfer.lock);
+
+	return r;
+}
+
+static int request_pll(u8 clock, bool enable)
+{
+	int r = 0;
+
+	if (clock == PRCMU_PLLSOC0)
+		clock = (enable ? PLL_SOC0_ON : PLL_SOC0_OFF);
+	else if (clock == PRCMU_PLLSOC1)
+		clock = (enable ? PLL_SOC1_ON : PLL_SOC1_OFF);
+	else
+		return -EINVAL;
+
+	mutex_lock(&mb1_transfer.lock);
+
+	while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
+		cpu_relax();
+
+	writeb(MB1H_PLL_ON_OFF, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB1));
+	writeb(clock, (tcdm_base + PRCM_REQ_MB1_PLL_ON_OFF));
+
+	writel(MBOX_BIT(1), PRCM_MBOX_CPU_SET);
+	wait_for_completion(&mb1_transfer.work);
+
+	if (mb1_transfer.ack.header != MB1H_PLL_ON_OFF)
+		r = -EIO;
+
+	mutex_unlock(&mb1_transfer.lock);
+
+	return r;
+}
+
+/**
+ * db8500_prcmu_set_epod - set the state of a EPOD (power domain)
+ * @epod_id: The EPOD to set
+ * @epod_state: The new EPOD state
+ *
+ * This function sets the state of a EPOD (power domain). It may not be called
+ * from interrupt context.
+ */
+int db8500_prcmu_set_epod(u16 epod_id, u8 epod_state)
+{
+	int r = 0;
+	bool ram_retention = false;
+	int i;
+
+	/* check argument */
+	BUG_ON(epod_id >= NUM_EPOD_ID);
+
+	/* set flag if retention is possible */
+	switch (epod_id) {
+	case EPOD_ID_SVAMMDSP:
+	case EPOD_ID_SIAMMDSP:
+	case EPOD_ID_ESRAM12:
+	case EPOD_ID_ESRAM34:
+		ram_retention = true;
+		break;
+	}
+
+	/* check argument */
+	BUG_ON(epod_state > EPOD_STATE_ON);
+	BUG_ON(epod_state == EPOD_STATE_RAMRET && !ram_retention);
+
+	/* get lock */
+	mutex_lock(&mb2_transfer.lock);
+
+	/* wait for mailbox */
+	while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(2))
+		cpu_relax();
+
+	/* fill in mailbox */
+	for (i = 0; i < NUM_EPOD_ID; i++)
+		writeb(EPOD_STATE_NO_CHANGE, (tcdm_base + PRCM_REQ_MB2 + i));
+	writeb(epod_state, (tcdm_base + PRCM_REQ_MB2 + epod_id));
+
+	writeb(MB2H_DPS, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB2));
+
+	writel(MBOX_BIT(2), PRCM_MBOX_CPU_SET);
+
+	/*
+	 * The current firmware version does not handle errors correctly,
+	 * and we cannot recover if there is an error.
+	 * This is expected to change when the firmware is updated.
+	 */
+	if (!wait_for_completion_timeout(&mb2_transfer.work,
+			msecs_to_jiffies(20000))) {
+		pr_err("prcmu: %s timed out (20 s) waiting for a reply.\n",
+			__func__);
+		r = -EIO;
+		goto unlock_and_return;
+	}
+
+	if (mb2_transfer.ack.status != HWACC_PWR_ST_OK)
+		r = -EIO;
+
+unlock_and_return:
+	mutex_unlock(&mb2_transfer.lock);
+	return r;
+}
+
+/**
+ * prcmu_configure_auto_pm - Configure autonomous power management.
+ * @sleep: Configuration for ApSleep.
+ * @idle:  Configuration for ApIdle.
+ */
+void prcmu_configure_auto_pm(struct prcmu_auto_pm_config *sleep,
+	struct prcmu_auto_pm_config *idle)
+{
+	u32 sleep_cfg;
+	u32 idle_cfg;
+	unsigned long flags;
+
+	BUG_ON((sleep == NULL) || (idle == NULL));
+
+	sleep_cfg = (sleep->sva_auto_pm_enable & 0xF);
+	sleep_cfg = ((sleep_cfg << 4) | (sleep->sia_auto_pm_enable & 0xF));
+	sleep_cfg = ((sleep_cfg << 8) | (sleep->sva_power_on & 0xFF));
+	sleep_cfg = ((sleep_cfg << 8) | (sleep->sia_power_on & 0xFF));
+	sleep_cfg = ((sleep_cfg << 4) | (sleep->sva_policy & 0xF));
+	sleep_cfg = ((sleep_cfg << 4) | (sleep->sia_policy & 0xF));
+
+	idle_cfg = (idle->sva_auto_pm_enable & 0xF);
+	idle_cfg = ((idle_cfg << 4) | (idle->sia_auto_pm_enable & 0xF));
+	idle_cfg = ((idle_cfg << 8) | (idle->sva_power_on & 0xFF));
+	idle_cfg = ((idle_cfg << 8) | (idle->sia_power_on & 0xFF));
+	idle_cfg = ((idle_cfg << 4) | (idle->sva_policy & 0xF));
+	idle_cfg = ((idle_cfg << 4) | (idle->sia_policy & 0xF));
+
+	spin_lock_irqsave(&mb2_transfer.auto_pm_lock, flags);
+
+	/*
+	 * The autonomous power management configuration is done through
+	 * fields in mailbox 2, but these fields are only used as shared
+	 * variables - i.e. there is no need to send a message.
+	 */
+	writel(sleep_cfg, (tcdm_base + PRCM_REQ_MB2_AUTO_PM_SLEEP));
+	writel(idle_cfg, (tcdm_base + PRCM_REQ_MB2_AUTO_PM_IDLE));
+
+	mb2_transfer.auto_pm_enabled =
+		((sleep->sva_auto_pm_enable == PRCMU_AUTO_PM_ON) ||
+		 (sleep->sia_auto_pm_enable == PRCMU_AUTO_PM_ON) ||
+		 (idle->sva_auto_pm_enable == PRCMU_AUTO_PM_ON) ||
+		 (idle->sia_auto_pm_enable == PRCMU_AUTO_PM_ON));
+
+	spin_unlock_irqrestore(&mb2_transfer.auto_pm_lock, flags);
+}
+EXPORT_SYMBOL(prcmu_configure_auto_pm);
+
+bool prcmu_is_auto_pm_enabled(void)
+{
+	return mb2_transfer.auto_pm_enabled;
+}
+
+static int request_sysclk(bool enable)
+{
+	int r;
+	unsigned long flags;
+
+	r = 0;
+
+	mutex_lock(&mb3_transfer.sysclk_lock);
+
+	spin_lock_irqsave(&mb3_transfer.lock, flags);
+
+	while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(3))
+		cpu_relax();
+
+	writeb((enable ? ON : OFF), (tcdm_base + PRCM_REQ_MB3_SYSCLK_MGT));
+
+	writeb(MB3H_SYSCLK, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB3));
+	writel(MBOX_BIT(3), PRCM_MBOX_CPU_SET);
+
+	spin_unlock_irqrestore(&mb3_transfer.lock, flags);
+
+	/*
+	 * The firmware only sends an ACK if we want to enable the
+	 * SysClk, and it succeeds.
+	 */
+	if (enable && !wait_for_completion_timeout(&mb3_transfer.sysclk_work,
+			msecs_to_jiffies(20000))) {
+		pr_err("prcmu: %s timed out (20 s) waiting for a reply.\n",
+			__func__);
+		r = -EIO;
+	}
+
+	mutex_unlock(&mb3_transfer.sysclk_lock);
+
+	return r;
+}
+
+static int request_timclk(bool enable)
+{
+	u32 val = (PRCM_TCR_DOZE_MODE | PRCM_TCR_TENSEL_MASK);
+
+	if (!enable)
+		val |= PRCM_TCR_STOP_TIMERS;
+	writel(val, PRCM_TCR);
+
+	return 0;
+}
+
+static int request_clock(u8 clock, bool enable)
+{
+	u32 val;
+	unsigned long flags;
+
+	spin_lock_irqsave(&clk_mgt_lock, flags);
+
+	/* Grab the HW semaphore. */
+	while ((readl(PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0)
+		cpu_relax();
+
+	val = readl(clk_mgt[clock].reg);
+	if (enable) {
+		val |= (PRCM_CLK_MGT_CLKEN | clk_mgt[clock].pllsw);
+	} else {
+		clk_mgt[clock].pllsw = (val & PRCM_CLK_MGT_CLKPLLSW_MASK);
+		val &= ~(PRCM_CLK_MGT_CLKEN | PRCM_CLK_MGT_CLKPLLSW_MASK);
+	}
+	writel(val, clk_mgt[clock].reg);
+
+	/* Release the HW semaphore. */
+	writel(0, PRCM_SEM);
+
+	spin_unlock_irqrestore(&clk_mgt_lock, flags);
+
+	return 0;
+}
+
+static int request_sga_clock(u8 clock, bool enable)
+{
+	u32 val;
+	int ret;
+
+	if (enable) {
+		val = readl(PRCM_CGATING_BYPASS);
+		writel(val | PRCM_CGATING_BYPASS_ICN2, PRCM_CGATING_BYPASS);
+	}
+
+	ret = request_clock(clock, enable);
+
+	if (!ret && !enable) {
+		val = readl(PRCM_CGATING_BYPASS);
+		writel(val & ~PRCM_CGATING_BYPASS_ICN2, PRCM_CGATING_BYPASS);
+	}
+
+	return ret;
+}
+
+static inline bool plldsi_locked(void)
+{
+	return (readl(PRCM_PLLDSI_LOCKP) &
+		(PRCM_PLLDSI_LOCKP_PRCM_PLLDSI_LOCKP10 |
+		 PRCM_PLLDSI_LOCKP_PRCM_PLLDSI_LOCKP3)) ==
+		(PRCM_PLLDSI_LOCKP_PRCM_PLLDSI_LOCKP10 |
+		 PRCM_PLLDSI_LOCKP_PRCM_PLLDSI_LOCKP3);
+}
+
+static int request_plldsi(bool enable)
+{
+	int r = 0;
+	u32 val;
+
+	writel((PRCM_MMIP_LS_CLAMP_DSIPLL_CLAMP |
+		PRCM_MMIP_LS_CLAMP_DSIPLL_CLAMPI), (enable ?
+		PRCM_MMIP_LS_CLAMP_CLR : PRCM_MMIP_LS_CLAMP_SET));
+
+	val = readl(PRCM_PLLDSI_ENABLE);
+	if (enable)
+		val |= PRCM_PLLDSI_ENABLE_PRCM_PLLDSI_ENABLE;
+	else
+		val &= ~PRCM_PLLDSI_ENABLE_PRCM_PLLDSI_ENABLE;
+	writel(val, PRCM_PLLDSI_ENABLE);
+
+	if (enable) {
+		unsigned int i;
+		bool locked = plldsi_locked();
+
+		for (i = 10; !locked && (i > 0); --i) {
+			udelay(100);
+			locked = plldsi_locked();
+		}
+		if (locked) {
+			writel(PRCM_APE_RESETN_DSIPLL_RESETN,
+				PRCM_APE_RESETN_SET);
+		} else {
+			writel((PRCM_MMIP_LS_CLAMP_DSIPLL_CLAMP |
+				PRCM_MMIP_LS_CLAMP_DSIPLL_CLAMPI),
+				PRCM_MMIP_LS_CLAMP_SET);
+			val &= ~PRCM_PLLDSI_ENABLE_PRCM_PLLDSI_ENABLE;
+			writel(val, PRCM_PLLDSI_ENABLE);
+			r = -EAGAIN;
+		}
+	} else {
+		writel(PRCM_APE_RESETN_DSIPLL_RESETN, PRCM_APE_RESETN_CLR);
+	}
+	return r;
+}
+
+static int request_dsiclk(u8 n, bool enable)
+{
+	u32 val;
+
+	val = readl(PRCM_DSI_PLLOUT_SEL);
+	val &= ~dsiclk[n].divsel_mask;
+	val |= ((enable ? dsiclk[n].divsel : PRCM_DSI_PLLOUT_SEL_OFF) <<
+		dsiclk[n].divsel_shift);
+	writel(val, PRCM_DSI_PLLOUT_SEL);
+	return 0;
+}
+
+static int request_dsiescclk(u8 n, bool enable)
+{
+	u32 val;
+
+	val = readl(PRCM_DSITVCLK_DIV);
+	enable ? (val |= dsiescclk[n].en) : (val &= ~dsiescclk[n].en);
+	writel(val, PRCM_DSITVCLK_DIV);
+	return 0;
+}
+
+/**
+ * db8500_prcmu_request_clock() - Request for a clock to be enabled or disabled.
+ * @clock:      The clock for which the request is made.
+ * @enable:     Whether the clock should be enabled (true) or disabled (false).
+ *
+ * This function should only be used by the clock implementation.
+ * Do not use it from any other place!
+ */
+int db8500_prcmu_request_clock(u8 clock, bool enable)
+{
+	if (clock == PRCMU_SGACLK)
+		return request_sga_clock(clock, enable);
+	else if (clock < PRCMU_NUM_REG_CLOCKS)
+		return request_clock(clock, enable);
+	else if (clock == PRCMU_TIMCLK)
+		return request_timclk(enable);
+	else if ((clock == PRCMU_DSI0CLK) || (clock == PRCMU_DSI1CLK))
+		return request_dsiclk((clock - PRCMU_DSI0CLK), enable);
+	else if ((PRCMU_DSI0ESCCLK <= clock) && (clock <= PRCMU_DSI2ESCCLK))
+		return request_dsiescclk((clock - PRCMU_DSI0ESCCLK), enable);
+	else if (clock == PRCMU_PLLDSI)
+		return request_plldsi(enable);
+	else if (clock == PRCMU_SYSCLK)
+		return request_sysclk(enable);
+	else if ((clock == PRCMU_PLLSOC0) || (clock == PRCMU_PLLSOC1))
+		return request_pll(clock, enable);
+	else
+		return -EINVAL;
+}
+
+static unsigned long pll_rate(void __iomem *reg, unsigned long src_rate,
+	int branch)
+{
+	u64 rate;
+	u32 val;
+	u32 d;
+	u32 div = 1;
+
+	val = readl(reg);
+
+	rate = src_rate;
+	rate *= ((val & PRCM_PLL_FREQ_D_MASK) >> PRCM_PLL_FREQ_D_SHIFT);
+
+	d = ((val & PRCM_PLL_FREQ_N_MASK) >> PRCM_PLL_FREQ_N_SHIFT);
+	if (d > 1)
+		div *= d;
+
+	d = ((val & PRCM_PLL_FREQ_R_MASK) >> PRCM_PLL_FREQ_R_SHIFT);
+	if (d > 1)
+		div *= d;
+
+	if (val & PRCM_PLL_FREQ_SELDIV2)
+		div *= 2;
+
+	if ((branch == PLL_FIX) || ((branch == PLL_DIV) &&
+		(val & PRCM_PLL_FREQ_DIV2EN) &&
+		((reg == PRCM_PLLSOC0_FREQ) ||
+		 (reg == PRCM_PLLDDR_FREQ))))
+		div *= 2;
+
+	(void)do_div(rate, div);
+
+	return (unsigned long)rate;
+}
+
+#define ROOT_CLOCK_RATE 38400000
+
+static unsigned long clock_rate(u8 clock)
+{
+	u32 val;
+	u32 pllsw;
+	unsigned long rate = ROOT_CLOCK_RATE;
+
+	val = readl(clk_mgt[clock].reg);
+
+	if (val & PRCM_CLK_MGT_CLK38) {
+		if (clk_mgt[clock].clk38div && (val & PRCM_CLK_MGT_CLK38DIV))
+			rate /= 2;
+		return rate;
+	}
+
+	val |= clk_mgt[clock].pllsw;
+	pllsw = (val & PRCM_CLK_MGT_CLKPLLSW_MASK);
+
+	if (pllsw == PRCM_CLK_MGT_CLKPLLSW_SOC0)
+		rate = pll_rate(PRCM_PLLSOC0_FREQ, rate, clk_mgt[clock].branch);
+	else if (pllsw == PRCM_CLK_MGT_CLKPLLSW_SOC1)
+		rate = pll_rate(PRCM_PLLSOC1_FREQ, rate, clk_mgt[clock].branch);
+	else if (pllsw == PRCM_CLK_MGT_CLKPLLSW_DDR)
+		rate = pll_rate(PRCM_PLLDDR_FREQ, rate, clk_mgt[clock].branch);
+	else
+		return 0;
+
+	if ((clock == PRCMU_SGACLK) &&
+		(val & PRCM_SGACLK_MGT_SGACLKDIV_BY_2_5_EN)) {
+		u64 r = (rate * 10);
+
+		(void)do_div(r, 25);
+		return (unsigned long)r;
+	}
+	val &= PRCM_CLK_MGT_CLKPLLDIV_MASK;
+	if (val)
+		return rate / val;
+	else
+		return 0;
+}
+
+static unsigned long dsiclk_rate(u8 n)
+{
+	u32 divsel;
+	u32 div = 1;
+
+	divsel = readl(PRCM_DSI_PLLOUT_SEL);
+	divsel = ((divsel & dsiclk[n].divsel_mask) >> dsiclk[n].divsel_shift);
+
+	if (divsel == PRCM_DSI_PLLOUT_SEL_OFF)
+		divsel = dsiclk[n].divsel;
+
+	switch (divsel) {
+	case PRCM_DSI_PLLOUT_SEL_PHI_4:
+		div *= 2;
+	case PRCM_DSI_PLLOUT_SEL_PHI_2:
+		div *= 2;
+	case PRCM_DSI_PLLOUT_SEL_PHI:
+		return pll_rate(PRCM_PLLDSI_FREQ, clock_rate(PRCMU_HDMICLK),
+			PLL_RAW) / div;
+	default:
+		return 0;
+	}
+}
+
+static unsigned long dsiescclk_rate(u8 n)
+{
+	u32 div;
+
+	div = readl(PRCM_DSITVCLK_DIV);
+	div = ((div & dsiescclk[n].div_mask) >> (dsiescclk[n].div_shift));
+	return clock_rate(PRCMU_TVCLK) / max((u32)1, div);
+}
+
+unsigned long prcmu_clock_rate(u8 clock)
+{
+	if (clock < PRCMU_NUM_REG_CLOCKS)
+		return clock_rate(clock);
+	else if (clock == PRCMU_TIMCLK)
+		return ROOT_CLOCK_RATE / 16;
+	else if (clock == PRCMU_SYSCLK)
+		return ROOT_CLOCK_RATE;
+	else if (clock == PRCMU_PLLSOC0)
+		return pll_rate(PRCM_PLLSOC0_FREQ, ROOT_CLOCK_RATE, PLL_RAW);
+	else if (clock == PRCMU_PLLSOC1)
+		return pll_rate(PRCM_PLLSOC1_FREQ, ROOT_CLOCK_RATE, PLL_RAW);
+	else if (clock == PRCMU_PLLDDR)
+		return pll_rate(PRCM_PLLDDR_FREQ, ROOT_CLOCK_RATE, PLL_RAW);
+	else if (clock == PRCMU_PLLDSI)
+		return pll_rate(PRCM_PLLDSI_FREQ, clock_rate(PRCMU_HDMICLK),
+			PLL_RAW);
+	else if ((clock == PRCMU_DSI0CLK) || (clock == PRCMU_DSI1CLK))
+		return dsiclk_rate(clock - PRCMU_DSI0CLK);
+	else if ((PRCMU_DSI0ESCCLK <= clock) && (clock <= PRCMU_DSI2ESCCLK))
+		return dsiescclk_rate(clock - PRCMU_DSI0ESCCLK);
+	else
+		return 0;
+}
+
+static unsigned long clock_source_rate(u32 clk_mgt_val, int branch)
+{
+	if (clk_mgt_val & PRCM_CLK_MGT_CLK38)
+		return ROOT_CLOCK_RATE;
+	clk_mgt_val &= PRCM_CLK_MGT_CLKPLLSW_MASK;
+	if (clk_mgt_val == PRCM_CLK_MGT_CLKPLLSW_SOC0)
+		return pll_rate(PRCM_PLLSOC0_FREQ, ROOT_CLOCK_RATE, branch);
+	else if (clk_mgt_val == PRCM_CLK_MGT_CLKPLLSW_SOC1)
+		return pll_rate(PRCM_PLLSOC1_FREQ, ROOT_CLOCK_RATE, branch);
+	else if (clk_mgt_val == PRCM_CLK_MGT_CLKPLLSW_DDR)
+		return pll_rate(PRCM_PLLDDR_FREQ, ROOT_CLOCK_RATE, branch);
+	else
+		return 0;
+}
+
+static u32 clock_divider(unsigned long src_rate, unsigned long rate)
+{
+	u32 div;
+
+	div = (src_rate / rate);
+	if (div == 0)
+		return 1;
+	if (rate < (src_rate / div))
+		div++;
+	return div;
+}
+
+static long round_clock_rate(u8 clock, unsigned long rate)
+{
+	u32 val;
+	u32 div;
+	unsigned long src_rate;
+	long rounded_rate;
+
+	val = readl(clk_mgt[clock].reg);
+	src_rate = clock_source_rate((val | clk_mgt[clock].pllsw),
+		clk_mgt[clock].branch);
+	div = clock_divider(src_rate, rate);
+	if (val & PRCM_CLK_MGT_CLK38) {
+		if (clk_mgt[clock].clk38div) {
+			if (div > 2)
+				div = 2;
+		} else {
+			div = 1;
+		}
+	} else if ((clock == PRCMU_SGACLK) && (div == 3)) {
+		u64 r = (src_rate * 10);
+
+		(void)do_div(r, 25);
+		if (r <= rate)
+			return (unsigned long)r;
+	}
+	rounded_rate = (src_rate / min(div, (u32)31));
+
+	return rounded_rate;
+}
+
+#define MIN_PLL_VCO_RATE 600000000ULL
+#define MAX_PLL_VCO_RATE 1680640000ULL
+
+static long round_plldsi_rate(unsigned long rate)
+{
+	long rounded_rate = 0;
+	unsigned long src_rate;
+	unsigned long rem;
+	u32 r;
+
+	src_rate = clock_rate(PRCMU_HDMICLK);
+	rem = rate;
+
+	for (r = 7; (rem > 0) && (r > 0); r--) {
+		u64 d;
+
+		d = (r * rate);
+		(void)do_div(d, src_rate);
+		if (d < 6)
+			d = 6;
+		else if (d > 255)
+			d = 255;
+		d *= src_rate;
+		if (((2 * d) < (r * MIN_PLL_VCO_RATE)) ||
+			((r * MAX_PLL_VCO_RATE) < (2 * d)))
+			continue;
+		(void)do_div(d, r);
+		if (rate < d) {
+			if (rounded_rate == 0)
+				rounded_rate = (long)d;
+			break;
+		}
+		if ((rate - d) < rem) {
+			rem = (rate - d);
+			rounded_rate = (long)d;
+		}
+	}
+	return rounded_rate;
+}
+
+static long round_dsiclk_rate(unsigned long rate)
+{
+	u32 div;
+	unsigned long src_rate;
+	long rounded_rate;
+
+	src_rate = pll_rate(PRCM_PLLDSI_FREQ, clock_rate(PRCMU_HDMICLK),
+		PLL_RAW);
+	div = clock_divider(src_rate, rate);
+	rounded_rate = (src_rate / ((div > 2) ? 4 : div));
+
+	return rounded_rate;
+}
+
+static long round_dsiescclk_rate(unsigned long rate)
+{
+	u32 div;
+	unsigned long src_rate;
+	long rounded_rate;
+
+	src_rate = clock_rate(PRCMU_TVCLK);
+	div = clock_divider(src_rate, rate);
+	rounded_rate = (src_rate / min(div, (u32)255));
+
+	return rounded_rate;
+}
+
+long prcmu_round_clock_rate(u8 clock, unsigned long rate)
+{
+	if (clock < PRCMU_NUM_REG_CLOCKS)
+		return round_clock_rate(clock, rate);
+	else if (clock == PRCMU_PLLDSI)
+		return round_plldsi_rate(rate);
+	else if ((clock == PRCMU_DSI0CLK) || (clock == PRCMU_DSI1CLK))
+		return round_dsiclk_rate(rate);
+	else if ((PRCMU_DSI0ESCCLK <= clock) && (clock <= PRCMU_DSI2ESCCLK))
+		return round_dsiescclk_rate(rate);
+	else
+		return (long)prcmu_clock_rate(clock);
+}
+
+static void set_clock_rate(u8 clock, unsigned long rate)
+{
+	u32 val;
+	u32 div;
+	unsigned long src_rate;
+	unsigned long flags;
+
+	spin_lock_irqsave(&clk_mgt_lock, flags);
+
+	/* Grab the HW semaphore. */
+	while ((readl(PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0)
+		cpu_relax();
+
+	val = readl(clk_mgt[clock].reg);
+	src_rate = clock_source_rate((val | clk_mgt[clock].pllsw),
+		clk_mgt[clock].branch);
+	div = clock_divider(src_rate, rate);
+	if (val & PRCM_CLK_MGT_CLK38) {
+		if (clk_mgt[clock].clk38div) {
+			if (div > 1)
+				val |= PRCM_CLK_MGT_CLK38DIV;
+			else
+				val &= ~PRCM_CLK_MGT_CLK38DIV;
+		}
+	} else if (clock == PRCMU_SGACLK) {
+		val &= ~(PRCM_CLK_MGT_CLKPLLDIV_MASK |
+			PRCM_SGACLK_MGT_SGACLKDIV_BY_2_5_EN);
+		if (div == 3) {
+			u64 r = (src_rate * 10);
+
+			(void)do_div(r, 25);
+			if (r <= rate) {
+				val |= PRCM_SGACLK_MGT_SGACLKDIV_BY_2_5_EN;
+				div = 0;
+			}
+		}
+		val |= min(div, (u32)31);
+	} else {
+		val &= ~PRCM_CLK_MGT_CLKPLLDIV_MASK;
+		val |= min(div, (u32)31);
+	}
+	writel(val, clk_mgt[clock].reg);
+
+	/* Release the HW semaphore. */
+	writel(0, PRCM_SEM);
+
+	spin_unlock_irqrestore(&clk_mgt_lock, flags);
+}
+
+static int set_plldsi_rate(unsigned long rate)
+{
+	unsigned long src_rate;
+	unsigned long rem;
+	u32 pll_freq = 0;
+	u32 r;
+
+	src_rate = clock_rate(PRCMU_HDMICLK);
+	rem = rate;
+
+	for (r = 7; (rem > 0) && (r > 0); r--) {
+		u64 d;
+		u64 hwrate;
+
+		d = (r * rate);
+		(void)do_div(d, src_rate);
+		if (d < 6)
+			d = 6;
+		else if (d > 255)
+			d = 255;
+		hwrate = (d * src_rate);
+		if (((2 * hwrate) < (r * MIN_PLL_VCO_RATE)) ||
+			((r * MAX_PLL_VCO_RATE) < (2 * hwrate)))
+			continue;
+		(void)do_div(hwrate, r);
+		if (rate < hwrate) {
+			if (pll_freq == 0)
+				pll_freq = (((u32)d << PRCM_PLL_FREQ_D_SHIFT) |
+					(r << PRCM_PLL_FREQ_R_SHIFT));
+			break;
+		}
+		if ((rate - hwrate) < rem) {
+			rem = (rate - hwrate);
+			pll_freq = (((u32)d << PRCM_PLL_FREQ_D_SHIFT) |
+				(r << PRCM_PLL_FREQ_R_SHIFT));
+		}
+	}
+	if (pll_freq == 0)
+		return -EINVAL;
+
+	pll_freq |= (1 << PRCM_PLL_FREQ_N_SHIFT);
+	writel(pll_freq, PRCM_PLLDSI_FREQ);
+
+	return 0;
+}
+
+static void set_dsiclk_rate(u8 n, unsigned long rate)
+{
+	u32 val;
+	u32 div;
+
+	div = clock_divider(pll_rate(PRCM_PLLDSI_FREQ,
+			clock_rate(PRCMU_HDMICLK), PLL_RAW), rate);
+
+	dsiclk[n].divsel = (div == 1) ? PRCM_DSI_PLLOUT_SEL_PHI :
+			   (div == 2) ? PRCM_DSI_PLLOUT_SEL_PHI_2 :
+			   /* else */	PRCM_DSI_PLLOUT_SEL_PHI_4;
+
+	val = readl(PRCM_DSI_PLLOUT_SEL);
+	val &= ~dsiclk[n].divsel_mask;
+	val |= (dsiclk[n].divsel << dsiclk[n].divsel_shift);
+	writel(val, PRCM_DSI_PLLOUT_SEL);
+}
+
+static void set_dsiescclk_rate(u8 n, unsigned long rate)
+{
+	u32 val;
+	u32 div;
+
+	div = clock_divider(clock_rate(PRCMU_TVCLK), rate);
+	val = readl(PRCM_DSITVCLK_DIV);
+	val &= ~dsiescclk[n].div_mask;
+	val |= (min(div, (u32)255) << dsiescclk[n].div_shift);
+	writel(val, PRCM_DSITVCLK_DIV);
+}
+
+int prcmu_set_clock_rate(u8 clock, unsigned long rate)
+{
+	if (clock < PRCMU_NUM_REG_CLOCKS)
+		set_clock_rate(clock, rate);
+	else if (clock == PRCMU_PLLDSI)
+		return set_plldsi_rate(rate);
+	else if ((clock == PRCMU_DSI0CLK) || (clock == PRCMU_DSI1CLK))
+		set_dsiclk_rate((clock - PRCMU_DSI0CLK), rate);
+	else if ((PRCMU_DSI0ESCCLK <= clock) && (clock <= PRCMU_DSI2ESCCLK))
+		set_dsiescclk_rate((clock - PRCMU_DSI0ESCCLK), rate);
+	return 0;
+}
+
+int db8500_prcmu_config_esram0_deep_sleep(u8 state)
+{
+	if ((state > ESRAM0_DEEP_SLEEP_STATE_RET) ||
+	    (state < ESRAM0_DEEP_SLEEP_STATE_OFF))
+		return -EINVAL;
+
+	mutex_lock(&mb4_transfer.lock);
+
+	while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(4))
+		cpu_relax();
+
+	writeb(MB4H_MEM_ST, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB4));
+	writeb(((DDR_PWR_STATE_OFFHIGHLAT << 4) | DDR_PWR_STATE_ON),
+	       (tcdm_base + PRCM_REQ_MB4_DDR_ST_AP_SLEEP_IDLE));
+	writeb(DDR_PWR_STATE_ON,
+	       (tcdm_base + PRCM_REQ_MB4_DDR_ST_AP_DEEP_IDLE));
+	writeb(state, (tcdm_base + PRCM_REQ_MB4_ESRAM0_ST));
+
+	writel(MBOX_BIT(4), PRCM_MBOX_CPU_SET);
+	wait_for_completion(&mb4_transfer.work);
+
+	mutex_unlock(&mb4_transfer.lock);
+
+	return 0;
+}
+
+int db8500_prcmu_config_hotdog(u8 threshold)
+{
+	mutex_lock(&mb4_transfer.lock);
+
+	while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(4))
+		cpu_relax();
+
+	writeb(threshold, (tcdm_base + PRCM_REQ_MB4_HOTDOG_THRESHOLD));
+	writeb(MB4H_HOTDOG, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB4));
+
+	writel(MBOX_BIT(4), PRCM_MBOX_CPU_SET);
+	wait_for_completion(&mb4_transfer.work);
+
+	mutex_unlock(&mb4_transfer.lock);
+
+	return 0;
+}
+
+int db8500_prcmu_config_hotmon(u8 low, u8 high)
+{
+	mutex_lock(&mb4_transfer.lock);
+
+	while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(4))
+		cpu_relax();
+
+	writeb(low, (tcdm_base + PRCM_REQ_MB4_HOTMON_LOW));
+	writeb(high, (tcdm_base + PRCM_REQ_MB4_HOTMON_HIGH));
+	writeb((HOTMON_CONFIG_LOW | HOTMON_CONFIG_HIGH),
+		(tcdm_base + PRCM_REQ_MB4_HOTMON_CONFIG));
+	writeb(MB4H_HOTMON, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB4));
+
+	writel(MBOX_BIT(4), PRCM_MBOX_CPU_SET);
+	wait_for_completion(&mb4_transfer.work);
+
+	mutex_unlock(&mb4_transfer.lock);
+
+	return 0;
+}
+
+static int config_hot_period(u16 val)
+{
+	mutex_lock(&mb4_transfer.lock);
+
+	while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(4))
+		cpu_relax();
+
+	writew(val, (tcdm_base + PRCM_REQ_MB4_HOT_PERIOD));
+	writeb(MB4H_HOT_PERIOD, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB4));
+
+	writel(MBOX_BIT(4), PRCM_MBOX_CPU_SET);
+	wait_for_completion(&mb4_transfer.work);
+
+	mutex_unlock(&mb4_transfer.lock);
+
+	return 0;
+}
+
+int db8500_prcmu_start_temp_sense(u16 cycles32k)
+{
+	if (cycles32k == 0xFFFF)
+		return -EINVAL;
+
+	return config_hot_period(cycles32k);
+}
+
+int db8500_prcmu_stop_temp_sense(void)
+{
+	return config_hot_period(0xFFFF);
+}
+
+static int prcmu_a9wdog(u8 cmd, u8 d0, u8 d1, u8 d2, u8 d3)
+{
+
+	mutex_lock(&mb4_transfer.lock);
+
+	while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(4))
+		cpu_relax();
+
+	writeb(d0, (tcdm_base + PRCM_REQ_MB4_A9WDOG_0));
+	writeb(d1, (tcdm_base + PRCM_REQ_MB4_A9WDOG_1));
+	writeb(d2, (tcdm_base + PRCM_REQ_MB4_A9WDOG_2));
+	writeb(d3, (tcdm_base + PRCM_REQ_MB4_A9WDOG_3));
+
+	writeb(cmd, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB4));
+
+	writel(MBOX_BIT(4), PRCM_MBOX_CPU_SET);
+	wait_for_completion(&mb4_transfer.work);
+
+	mutex_unlock(&mb4_transfer.lock);
+
+	return 0;
+
+}
+
+int db8500_prcmu_config_a9wdog(u8 num, bool sleep_auto_off)
+{
+	BUG_ON(num == 0 || num > 0xf);
+	return prcmu_a9wdog(MB4H_A9WDOG_CONF, num, 0, 0,
+			    sleep_auto_off ? A9WDOG_AUTO_OFF_EN :
+			    A9WDOG_AUTO_OFF_DIS);
+}
+
+int db8500_prcmu_enable_a9wdog(u8 id)
+{
+	return prcmu_a9wdog(MB4H_A9WDOG_EN, id, 0, 0, 0);
+}
+
+int db8500_prcmu_disable_a9wdog(u8 id)
+{
+	return prcmu_a9wdog(MB4H_A9WDOG_DIS, id, 0, 0, 0);
+}
+
+int db8500_prcmu_kick_a9wdog(u8 id)
+{
+	return prcmu_a9wdog(MB4H_A9WDOG_KICK, id, 0, 0, 0);
+}
+
+/*
+ * timeout is 28 bit, in ms.
+ */
+int db8500_prcmu_load_a9wdog(u8 id, u32 timeout)
+{
+	return prcmu_a9wdog(MB4H_A9WDOG_LOAD,
+			    (id & A9WDOG_ID_MASK) |
+			    /*
+			     * Put the lowest 28 bits of timeout at
+			     * offset 4. Four first bits are used for id.
+			     */
+			    (u8)((timeout << 4) & 0xf0),
+			    (u8)((timeout >> 4) & 0xff),
+			    (u8)((timeout >> 12) & 0xff),
+			    (u8)((timeout >> 20) & 0xff));
+}
+
+/**
+ * prcmu_abb_read() - Read register value(s) from the ABB.
+ * @slave:	The I2C slave address.
+ * @reg:	The (start) register address.
+ * @value:	The read out value(s).
+ * @size:	The number of registers to read.
+ *
+ * Reads register value(s) from the ABB.
+ * @size has to be 1 for the current firmware version.
+ */
+int prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size)
+{
+	int r;
+
+	if (size != 1)
+		return -EINVAL;
+
+	mutex_lock(&mb5_transfer.lock);
+
+	while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(5))
+		cpu_relax();
+
+	writeb(0, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB5));
+	writeb(PRCMU_I2C_READ(slave), (tcdm_base + PRCM_REQ_MB5_I2C_SLAVE_OP));
+	writeb(PRCMU_I2C_STOP_EN, (tcdm_base + PRCM_REQ_MB5_I2C_HW_BITS));
+	writeb(reg, (tcdm_base + PRCM_REQ_MB5_I2C_REG));
+	writeb(0, (tcdm_base + PRCM_REQ_MB5_I2C_VAL));
+
+	writel(MBOX_BIT(5), PRCM_MBOX_CPU_SET);
+
+	if (!wait_for_completion_timeout(&mb5_transfer.work,
+				msecs_to_jiffies(20000))) {
+		pr_err("prcmu: %s timed out (20 s) waiting for a reply.\n",
+			__func__);
+		r = -EIO;
+	} else {
+		r = ((mb5_transfer.ack.status == I2C_RD_OK) ? 0 : -EIO);
+	}
+
+	if (!r)
+		*value = mb5_transfer.ack.value;
+
+	mutex_unlock(&mb5_transfer.lock);
+
+	return r;
+}
+
+/**
+ * prcmu_abb_write_masked() - Write masked register value(s) to the ABB.
+ * @slave:	The I2C slave address.
+ * @reg:	The (start) register address.
+ * @value:	The value(s) to write.
+ * @mask:	The mask(s) to use.
+ * @size:	The number of registers to write.
+ *
+ * Writes masked register value(s) to the ABB.
+ * For each @value, only the bits set to 1 in the corresponding @mask
+ * will be written. The other bits are not changed.
+ * @size has to be 1 for the current firmware version.
+ */
+int prcmu_abb_write_masked(u8 slave, u8 reg, u8 *value, u8 *mask, u8 size)
+{
+	int r;
+
+	if (size != 1)
+		return -EINVAL;
+
+	mutex_lock(&mb5_transfer.lock);
+
+	while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(5))
+		cpu_relax();
+
+	writeb(~*mask, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB5));
+	writeb(PRCMU_I2C_WRITE(slave), (tcdm_base + PRCM_REQ_MB5_I2C_SLAVE_OP));
+	writeb(PRCMU_I2C_STOP_EN, (tcdm_base + PRCM_REQ_MB5_I2C_HW_BITS));
+	writeb(reg, (tcdm_base + PRCM_REQ_MB5_I2C_REG));
+	writeb(*value, (tcdm_base + PRCM_REQ_MB5_I2C_VAL));
+
+	writel(MBOX_BIT(5), PRCM_MBOX_CPU_SET);
+
+	if (!wait_for_completion_timeout(&mb5_transfer.work,
+				msecs_to_jiffies(20000))) {
+		pr_err("prcmu: %s timed out (20 s) waiting for a reply.\n",
+			__func__);
+		r = -EIO;
+	} else {
+		r = ((mb5_transfer.ack.status == I2C_WR_OK) ? 0 : -EIO);
+	}
+
+	mutex_unlock(&mb5_transfer.lock);
+
+	return r;
+}
+
+/**
+ * prcmu_abb_write() - Write register value(s) to the ABB.
+ * @slave:	The I2C slave address.
+ * @reg:	The (start) register address.
+ * @value:	The value(s) to write.
+ * @size:	The number of registers to write.
+ *
+ * Writes register value(s) to the ABB.
+ * @size has to be 1 for the current firmware version.
+ */
+int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size)
+{
+	u8 mask = ~0;
+
+	return prcmu_abb_write_masked(slave, reg, value, &mask, size);
+}
+
+/**
+ * prcmu_ac_wake_req - should be called whenever ARM wants to wakeup Modem
+ */
+void prcmu_ac_wake_req(void)
+{
+	u32 val;
+	u32 status;
+
+	mutex_lock(&mb0_transfer.ac_wake_lock);
+
+	val = readl(PRCM_HOSTACCESS_REQ);
+	if (val & PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ)
+		goto unlock_and_return;
+
+	atomic_set(&ac_wake_req_state, 1);
+
+retry:
+	writel((val | PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ), PRCM_HOSTACCESS_REQ);
+
+	if (!wait_for_completion_timeout(&mb0_transfer.ac_wake_work,
+			msecs_to_jiffies(5000))) {
+		pr_crit("prcmu: %s timed out (5 s) waiting for a reply.\n",
+			__func__);
+		goto unlock_and_return;
+	}
+
+	/*
+	 * The modem can generate an AC_WAKE_ACK, and then still go to sleep.
+	 * As a workaround, we wait, and then check that the modem is indeed
+	 * awake (in terms of the value of the PRCM_MOD_AWAKE_STATUS
+	 * register, which may not be the whole truth).
+	 */
+	udelay(400);
+	status = (readl(PRCM_MOD_AWAKE_STATUS) & BITS(0, 2));
+	if (status != (PRCM_MOD_AWAKE_STATUS_PRCM_MOD_AAPD_AWAKE |
+			PRCM_MOD_AWAKE_STATUS_PRCM_MOD_COREPD_AWAKE)) {
+		pr_err("prcmu: %s received ack, but modem not awake (0x%X).\n",
+			__func__, status);
+		udelay(1200);
+		writel(val, PRCM_HOSTACCESS_REQ);
+		if (wait_for_completion_timeout(&mb0_transfer.ac_wake_work,
+				msecs_to_jiffies(5000)))
+			goto retry;
+		pr_crit("prcmu: %s timed out (5 s) waiting for AC_SLEEP_ACK.\n",
+			__func__);
+	}
+
+unlock_and_return:
+	mutex_unlock(&mb0_transfer.ac_wake_lock);
+}
+
+/**
+ * prcmu_ac_sleep_req - called when ARM no longer needs to talk to modem
+ */
+void prcmu_ac_sleep_req()
+{
+	u32 val;
+
+	mutex_lock(&mb0_transfer.ac_wake_lock);
+
+	val = readl(PRCM_HOSTACCESS_REQ);
+	if (!(val & PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ))
+		goto unlock_and_return;
+
+	writel((val & ~PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ),
+		PRCM_HOSTACCESS_REQ);
+
+	if (!wait_for_completion_timeout(&mb0_transfer.ac_wake_work,
+			msecs_to_jiffies(5000))) {
+		pr_crit("prcmu: %s timed out (5 s) waiting for a reply.\n",
+			__func__);
+	}
+
+	atomic_set(&ac_wake_req_state, 0);
+
+unlock_and_return:
+	mutex_unlock(&mb0_transfer.ac_wake_lock);
+}
+
+bool db8500_prcmu_is_ac_wake_requested(void)
+{
+	return (atomic_read(&ac_wake_req_state) != 0);
+}
+
+/**
+ * db8500_prcmu_system_reset - System reset
+ *
+ * Saves the reset reason code and then sets the APE_SOFTRST register which
+ * fires interrupt to fw
+ */
+void db8500_prcmu_system_reset(u16 reset_code)
+{
+	writew(reset_code, (tcdm_base + PRCM_SW_RST_REASON));
+	writel(1, PRCM_APE_SOFTRST);
+}
+
+/**
+ * db8500_prcmu_get_reset_code - Retrieve SW reset reason code
+ *
+ * Retrieves the reset reason code stored by prcmu_system_reset() before
+ * last restart.
+ */
+u16 db8500_prcmu_get_reset_code(void)
+{
+	return readw(tcdm_base + PRCM_SW_RST_REASON);
+}
+
+/**
+ * db8500_prcmu_reset_modem - ask the PRCMU to reset modem
+ */
+void db8500_prcmu_modem_reset(void)
+{
+	mutex_lock(&mb1_transfer.lock);
+
+	while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
+		cpu_relax();
+
+	writeb(MB1H_RESET_MODEM, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB1));
+	writel(MBOX_BIT(1), PRCM_MBOX_CPU_SET);
+	wait_for_completion(&mb1_transfer.work);
+
+	/*
+	 * No need to check return from PRCMU as modem should go in reset state
+	 * This state is already managed by upper layer
+	 */
+
+	mutex_unlock(&mb1_transfer.lock);
+}
+
+static void ack_dbb_wakeup(void)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&mb0_transfer.lock, flags);
+
+	while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(0))
+		cpu_relax();
+
+	writeb(MB0H_READ_WAKEUP_ACK, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB0));
+	writel(MBOX_BIT(0), PRCM_MBOX_CPU_SET);
+
+	spin_unlock_irqrestore(&mb0_transfer.lock, flags);
+}
+
+static inline void print_unknown_header_warning(u8 n, u8 header)
+{
+	pr_warning("prcmu: Unknown message header (%d) in mailbox %d.\n",
+		header, n);
+}
+
+static bool read_mailbox_0(void)
+{
+	bool r;
+	u32 ev;
+	unsigned int n;
+	u8 header;
+
+	header = readb(tcdm_base + PRCM_MBOX_HEADER_ACK_MB0);
+	switch (header) {
+	case MB0H_WAKEUP_EXE:
+	case MB0H_WAKEUP_SLEEP:
+		if (readb(tcdm_base + PRCM_ACK_MB0_READ_POINTER) & 1)
+			ev = readl(tcdm_base + PRCM_ACK_MB0_WAKEUP_1_8500);
+		else
+			ev = readl(tcdm_base + PRCM_ACK_MB0_WAKEUP_0_8500);
+
+		if (ev & (WAKEUP_BIT_AC_WAKE_ACK | WAKEUP_BIT_AC_SLEEP_ACK))
+			complete(&mb0_transfer.ac_wake_work);
+		if (ev & WAKEUP_BIT_SYSCLK_OK)
+			complete(&mb3_transfer.sysclk_work);
+
+		ev &= mb0_transfer.req.dbb_irqs;
+
+		for (n = 0; n < NUM_PRCMU_WAKEUPS; n++) {
+			if (ev & prcmu_irq_bit[n])
+				generic_handle_irq(IRQ_PRCMU_BASE + n);
+		}
+		r = true;
+		break;
+	default:
+		print_unknown_header_warning(0, header);
+		r = false;
+		break;
+	}
+	writel(MBOX_BIT(0), PRCM_ARM_IT1_CLR);
+	return r;
+}
+
+static bool read_mailbox_1(void)
+{
+	mb1_transfer.ack.header = readb(tcdm_base + PRCM_MBOX_HEADER_REQ_MB1);
+	mb1_transfer.ack.arm_opp = readb(tcdm_base +
+		PRCM_ACK_MB1_CURRENT_ARM_OPP);
+	mb1_transfer.ack.ape_opp = readb(tcdm_base +
+		PRCM_ACK_MB1_CURRENT_APE_OPP);
+	mb1_transfer.ack.ape_voltage_status = readb(tcdm_base +
+		PRCM_ACK_MB1_APE_VOLTAGE_STATUS);
+	writel(MBOX_BIT(1), PRCM_ARM_IT1_CLR);
+	complete(&mb1_transfer.work);
+	return false;
+}
+
+static bool read_mailbox_2(void)
+{
+	mb2_transfer.ack.status = readb(tcdm_base + PRCM_ACK_MB2_DPS_STATUS);
+	writel(MBOX_BIT(2), PRCM_ARM_IT1_CLR);
+	complete(&mb2_transfer.work);
+	return false;
+}
+
+static bool read_mailbox_3(void)
+{
+	writel(MBOX_BIT(3), PRCM_ARM_IT1_CLR);
+	return false;
+}
+
+static bool read_mailbox_4(void)
+{
+	u8 header;
+	bool do_complete = true;
+
+	header = readb(tcdm_base + PRCM_MBOX_HEADER_REQ_MB4);
+	switch (header) {
+	case MB4H_MEM_ST:
+	case MB4H_HOTDOG:
+	case MB4H_HOTMON:
+	case MB4H_HOT_PERIOD:
+	case MB4H_A9WDOG_CONF:
+	case MB4H_A9WDOG_EN:
+	case MB4H_A9WDOG_DIS:
+	case MB4H_A9WDOG_LOAD:
+	case MB4H_A9WDOG_KICK:
+		break;
+	default:
+		print_unknown_header_warning(4, header);
+		do_complete = false;
+		break;
+	}
+
+	writel(MBOX_BIT(4), PRCM_ARM_IT1_CLR);
+
+	if (do_complete)
+		complete(&mb4_transfer.work);
+
+	return false;
+}
+
+static bool read_mailbox_5(void)
+{
+	mb5_transfer.ack.status = readb(tcdm_base + PRCM_ACK_MB5_I2C_STATUS);
+	mb5_transfer.ack.value = readb(tcdm_base + PRCM_ACK_MB5_I2C_VAL);
+	writel(MBOX_BIT(5), PRCM_ARM_IT1_CLR);
+	complete(&mb5_transfer.work);
+	return false;
+}
+
+static bool read_mailbox_6(void)
+{
+	writel(MBOX_BIT(6), PRCM_ARM_IT1_CLR);
+	return false;
+}
+
+static bool read_mailbox_7(void)
+{
+	writel(MBOX_BIT(7), PRCM_ARM_IT1_CLR);
+	return false;
+}
+
+static bool (* const read_mailbox[NUM_MB])(void) = {
+	read_mailbox_0,
+	read_mailbox_1,
+	read_mailbox_2,
+	read_mailbox_3,
+	read_mailbox_4,
+	read_mailbox_5,
+	read_mailbox_6,
+	read_mailbox_7
+};
+
+static irqreturn_t prcmu_irq_handler(int irq, void *data)
+{
+	u32 bits;
+	u8 n;
+	irqreturn_t r;
+
+	bits = (readl(PRCM_ARM_IT1_VAL) & ALL_MBOX_BITS);
+	if (unlikely(!bits))
+		return IRQ_NONE;
+
+	r = IRQ_HANDLED;
+	for (n = 0; bits; n++) {
+		if (bits & MBOX_BIT(n)) {
+			bits -= MBOX_BIT(n);
+			if (read_mailbox[n]())
+				r = IRQ_WAKE_THREAD;
+		}
+	}
+	return r;
+}
+
+static irqreturn_t prcmu_irq_thread_fn(int irq, void *data)
+{
+	ack_dbb_wakeup();
+	return IRQ_HANDLED;
+}
+
+static void prcmu_mask_work(struct work_struct *work)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&mb0_transfer.lock, flags);
+
+	config_wakeups();
+
+	spin_unlock_irqrestore(&mb0_transfer.lock, flags);
+}
+
+static void prcmu_irq_mask(struct irq_data *d)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&mb0_transfer.dbb_irqs_lock, flags);
+
+	mb0_transfer.req.dbb_irqs &= ~prcmu_irq_bit[d->irq - IRQ_PRCMU_BASE];
+
+	spin_unlock_irqrestore(&mb0_transfer.dbb_irqs_lock, flags);
+
+	if (d->irq != IRQ_PRCMU_CA_SLEEP)
+		schedule_work(&mb0_transfer.mask_work);
+}
+
+static void prcmu_irq_unmask(struct irq_data *d)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&mb0_transfer.dbb_irqs_lock, flags);
+
+	mb0_transfer.req.dbb_irqs |= prcmu_irq_bit[d->irq - IRQ_PRCMU_BASE];
+
+	spin_unlock_irqrestore(&mb0_transfer.dbb_irqs_lock, flags);
+
+	if (d->irq != IRQ_PRCMU_CA_SLEEP)
+		schedule_work(&mb0_transfer.mask_work);
+}
+
+static void noop(struct irq_data *d)
+{
+}
+
+static struct irq_chip prcmu_irq_chip = {
+	.name		= "prcmu",
+	.irq_disable	= prcmu_irq_mask,
+	.irq_ack	= noop,
+	.irq_mask	= prcmu_irq_mask,
+	.irq_unmask	= prcmu_irq_unmask,
+};
+
+static char *fw_project_name(u8 project)
+{
+	switch (project) {
+	case PRCMU_FW_PROJECT_U8500:
+		return "U8500";
+	case PRCMU_FW_PROJECT_U8500_C2:
+		return "U8500 C2";
+	case PRCMU_FW_PROJECT_U9500:
+		return "U9500";
+	case PRCMU_FW_PROJECT_U9500_C2:
+		return "U9500 C2";
+	case PRCMU_FW_PROJECT_U8520:
+		return "U8520";
+	case PRCMU_FW_PROJECT_U8420:
+		return "U8420";
+	default:
+		return "Unknown";
+	}
+}
+
+void __init db8500_prcmu_early_init(void)
+{
+	unsigned int i;
+	if (cpu_is_u8500v2()) {
+		void *tcpm_base = ioremap_nocache(U8500_PRCMU_TCPM_BASE, SZ_4K);
+
+		if (tcpm_base != NULL) {
+			u32 version;
+			version = readl(tcpm_base + PRCMU_FW_VERSION_OFFSET);
+			fw_info.version.project = version & 0xFF;
+			fw_info.version.api_version = (version >> 8) & 0xFF;
+			fw_info.version.func_version = (version >> 16) & 0xFF;
+			fw_info.version.errata = (version >> 24) & 0xFF;
+			fw_info.valid = true;
+			pr_info("PRCMU firmware: %s, version %d.%d.%d\n",
+				fw_project_name(fw_info.version.project),
+				(version >> 8) & 0xFF, (version >> 16) & 0xFF,
+				(version >> 24) & 0xFF);
+			iounmap(tcpm_base);
+		}
+
+		tcdm_base = __io_address(U8500_PRCMU_TCDM_BASE);
+	} else {
+		pr_err("prcmu: Unsupported chip version\n");
+		BUG();
+	}
+
+	spin_lock_init(&mb0_transfer.lock);
+	spin_lock_init(&mb0_transfer.dbb_irqs_lock);
+	mutex_init(&mb0_transfer.ac_wake_lock);
+	init_completion(&mb0_transfer.ac_wake_work);
+	mutex_init(&mb1_transfer.lock);
+	init_completion(&mb1_transfer.work);
+	mb1_transfer.ape_opp = APE_NO_CHANGE;
+	mutex_init(&mb2_transfer.lock);
+	init_completion(&mb2_transfer.work);
+	spin_lock_init(&mb2_transfer.auto_pm_lock);
+	spin_lock_init(&mb3_transfer.lock);
+	mutex_init(&mb3_transfer.sysclk_lock);
+	init_completion(&mb3_transfer.sysclk_work);
+	mutex_init(&mb4_transfer.lock);
+	init_completion(&mb4_transfer.work);
+	mutex_init(&mb5_transfer.lock);
+	init_completion(&mb5_transfer.work);
+
+	INIT_WORK(&mb0_transfer.mask_work, prcmu_mask_work);
+
+	/* Initalize irqs. */
+	for (i = 0; i < NUM_PRCMU_WAKEUPS; i++) {
+		unsigned int irq;
+
+		irq = IRQ_PRCMU_BASE + i;
+		irq_set_chip_and_handler(irq, &prcmu_irq_chip,
+					 handle_simple_irq);
+		set_irq_flags(irq, IRQF_VALID);
+	}
+}
+
+static void __init init_prcm_registers(void)
+{
+	u32 val;
+
+	val = readl(PRCM_A9PL_FORCE_CLKEN);
+	val &= ~(PRCM_A9PL_FORCE_CLKEN_PRCM_A9PL_FORCE_CLKEN |
+		PRCM_A9PL_FORCE_CLKEN_PRCM_A9AXI_FORCE_CLKEN);
+	writel(val, (PRCM_A9PL_FORCE_CLKEN));
+}
+
+/*
+ * Power domain switches (ePODs) modeled as regulators for the DB8500 SoC
+ */
+static struct regulator_consumer_supply db8500_vape_consumers[] = {
+	REGULATOR_SUPPLY("v-ape", NULL),
+	REGULATOR_SUPPLY("v-i2c", "nmk-i2c.0"),
+	REGULATOR_SUPPLY("v-i2c", "nmk-i2c.1"),
+	REGULATOR_SUPPLY("v-i2c", "nmk-i2c.2"),
+	REGULATOR_SUPPLY("v-i2c", "nmk-i2c.3"),
+	/* "v-mmc" changed to "vcore" in the mainline kernel */
+	REGULATOR_SUPPLY("vcore", "sdi0"),
+	REGULATOR_SUPPLY("vcore", "sdi1"),
+	REGULATOR_SUPPLY("vcore", "sdi2"),
+	REGULATOR_SUPPLY("vcore", "sdi3"),
+	REGULATOR_SUPPLY("vcore", "sdi4"),
+	REGULATOR_SUPPLY("v-dma", "dma40.0"),
+	REGULATOR_SUPPLY("v-ape", "ab8500-usb.0"),
+	/* "v-uart" changed to "vcore" in the mainline kernel */
+	REGULATOR_SUPPLY("vcore", "uart0"),
+	REGULATOR_SUPPLY("vcore", "uart1"),
+	REGULATOR_SUPPLY("vcore", "uart2"),
+	REGULATOR_SUPPLY("v-ape", "nmk-ske-keypad.0"),
+	REGULATOR_SUPPLY("v-hsi", "ste_hsi.0"),
+};
+
+static struct regulator_consumer_supply db8500_vsmps2_consumers[] = {
+	REGULATOR_SUPPLY("musb_1v8", "ab8500-usb.0"),
+	/* AV8100 regulator */
+	REGULATOR_SUPPLY("hdmi_1v8", "0-0070"),
+};
+
+static struct regulator_consumer_supply db8500_b2r2_mcde_consumers[] = {
+	REGULATOR_SUPPLY("vsupply", "b2r2_bus"),
+	REGULATOR_SUPPLY("vsupply", "mcde"),
+};
+
+/* SVA MMDSP regulator switch */
+static struct regulator_consumer_supply db8500_svammdsp_consumers[] = {
+	REGULATOR_SUPPLY("sva-mmdsp", "cm_control"),
+};
+
+/* SVA pipe regulator switch */
+static struct regulator_consumer_supply db8500_svapipe_consumers[] = {
+	REGULATOR_SUPPLY("sva-pipe", "cm_control"),
+};
+
+/* SIA MMDSP regulator switch */
+static struct regulator_consumer_supply db8500_siammdsp_consumers[] = {
+	REGULATOR_SUPPLY("sia-mmdsp", "cm_control"),
+};
+
+/* SIA pipe regulator switch */
+static struct regulator_consumer_supply db8500_siapipe_consumers[] = {
+	REGULATOR_SUPPLY("sia-pipe", "cm_control"),
+};
+
+static struct regulator_consumer_supply db8500_sga_consumers[] = {
+	REGULATOR_SUPPLY("v-mali", NULL),
+};
+
+/* ESRAM1 and 2 regulator switch */
+static struct regulator_consumer_supply db8500_esram12_consumers[] = {
+	REGULATOR_SUPPLY("esram12", "cm_control"),
+};
+
+/* ESRAM3 and 4 regulator switch */
+static struct regulator_consumer_supply db8500_esram34_consumers[] = {
+	REGULATOR_SUPPLY("v-esram34", "mcde"),
+	REGULATOR_SUPPLY("esram34", "cm_control"),
+	REGULATOR_SUPPLY("lcla_esram", "dma40.0"),
+};
+
+static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = {
+	[DB8500_REGULATOR_VAPE] = {
+		.constraints = {
+			.name = "db8500-vape",
+			.valid_ops_mask = REGULATOR_CHANGE_STATUS,
+			.always_on = true,
+		},
+		.consumer_supplies = db8500_vape_consumers,
+		.num_consumer_supplies = ARRAY_SIZE(db8500_vape_consumers),
+	},
+	[DB8500_REGULATOR_VARM] = {
+		.constraints = {
+			.name = "db8500-varm",
+			.valid_ops_mask = REGULATOR_CHANGE_STATUS,
+		},
+	},
+	[DB8500_REGULATOR_VMODEM] = {
+		.constraints = {
+			.name = "db8500-vmodem",
+			.valid_ops_mask = REGULATOR_CHANGE_STATUS,
+		},
+	},
+	[DB8500_REGULATOR_VPLL] = {
+		.constraints = {
+			.name = "db8500-vpll",
+			.valid_ops_mask = REGULATOR_CHANGE_STATUS,
+		},
+	},
+	[DB8500_REGULATOR_VSMPS1] = {
+		.constraints = {
+			.name = "db8500-vsmps1",
+			.valid_ops_mask = REGULATOR_CHANGE_STATUS,
+		},
+	},
+	[DB8500_REGULATOR_VSMPS2] = {
+		.constraints = {
+			.name = "db8500-vsmps2",
+			.valid_ops_mask = REGULATOR_CHANGE_STATUS,
+		},
+		.consumer_supplies = db8500_vsmps2_consumers,
+		.num_consumer_supplies = ARRAY_SIZE(db8500_vsmps2_consumers),
+	},
+	[DB8500_REGULATOR_VSMPS3] = {
+		.constraints = {
+			.name = "db8500-vsmps3",
+			.valid_ops_mask = REGULATOR_CHANGE_STATUS,
+		},
+	},
+	[DB8500_REGULATOR_VRF1] = {
+		.constraints = {
+			.name = "db8500-vrf1",
+			.valid_ops_mask = REGULATOR_CHANGE_STATUS,
+		},
+	},
+	[DB8500_REGULATOR_SWITCH_SVAMMDSP] = {
+		/* dependency to u8500-vape is handled outside regulator framework */
+		.constraints = {
+			.name = "db8500-sva-mmdsp",
+			.valid_ops_mask = REGULATOR_CHANGE_STATUS,
+		},
+		.consumer_supplies = db8500_svammdsp_consumers,
+		.num_consumer_supplies = ARRAY_SIZE(db8500_svammdsp_consumers),
+	},
+	[DB8500_REGULATOR_SWITCH_SVAMMDSPRET] = {
+		.constraints = {
+			/* "ret" means "retention" */
+			.name = "db8500-sva-mmdsp-ret",
+			.valid_ops_mask = REGULATOR_CHANGE_STATUS,
+		},
+	},
+	[DB8500_REGULATOR_SWITCH_SVAPIPE] = {
+		/* dependency to u8500-vape is handled outside regulator framework */
+		.constraints = {
+			.name = "db8500-sva-pipe",
+			.valid_ops_mask = REGULATOR_CHANGE_STATUS,
+		},
+		.consumer_supplies = db8500_svapipe_consumers,
+		.num_consumer_supplies = ARRAY_SIZE(db8500_svapipe_consumers),
+	},
+	[DB8500_REGULATOR_SWITCH_SIAMMDSP] = {
+		/* dependency to u8500-vape is handled outside regulator framework */
+		.constraints = {
+			.name = "db8500-sia-mmdsp",
+			.valid_ops_mask = REGULATOR_CHANGE_STATUS,
+		},
+		.consumer_supplies = db8500_siammdsp_consumers,
+		.num_consumer_supplies = ARRAY_SIZE(db8500_siammdsp_consumers),
+	},
+	[DB8500_REGULATOR_SWITCH_SIAMMDSPRET] = {
+		.constraints = {
+			.name = "db8500-sia-mmdsp-ret",
+			.valid_ops_mask = REGULATOR_CHANGE_STATUS,
+		},
+	},
+	[DB8500_REGULATOR_SWITCH_SIAPIPE] = {
+		/* dependency to u8500-vape is handled outside regulator framework */
+		.constraints = {
+			.name = "db8500-sia-pipe",
+			.valid_ops_mask = REGULATOR_CHANGE_STATUS,
+		},
+		.consumer_supplies = db8500_siapipe_consumers,
+		.num_consumer_supplies = ARRAY_SIZE(db8500_siapipe_consumers),
+	},
+	[DB8500_REGULATOR_SWITCH_SGA] = {
+		.supply_regulator = "db8500-vape",
+		.constraints = {
+			.name = "db8500-sga",
+			.valid_ops_mask = REGULATOR_CHANGE_STATUS,
+		},
+		.consumer_supplies = db8500_sga_consumers,
+		.num_consumer_supplies = ARRAY_SIZE(db8500_sga_consumers),
+
+	},
+	[DB8500_REGULATOR_SWITCH_B2R2_MCDE] = {
+		.supply_regulator = "db8500-vape",
+		.constraints = {
+			.name = "db8500-b2r2-mcde",
+			.valid_ops_mask = REGULATOR_CHANGE_STATUS,
+		},
+		.consumer_supplies = db8500_b2r2_mcde_consumers,
+		.num_consumer_supplies = ARRAY_SIZE(db8500_b2r2_mcde_consumers),
+	},
+	[DB8500_REGULATOR_SWITCH_ESRAM12] = {
+		/*
+		 * esram12 is set in retention and supplied by Vsafe when Vape is off,
+		 * no need to hold Vape
+		 */
+		.constraints = {
+			.name = "db8500-esram12",
+			.valid_ops_mask = REGULATOR_CHANGE_STATUS,
+		},
+		.consumer_supplies = db8500_esram12_consumers,
+		.num_consumer_supplies = ARRAY_SIZE(db8500_esram12_consumers),
+	},
+	[DB8500_REGULATOR_SWITCH_ESRAM12RET] = {
+		.constraints = {
+			.name = "db8500-esram12-ret",
+			.valid_ops_mask = REGULATOR_CHANGE_STATUS,
+		},
+	},
+	[DB8500_REGULATOR_SWITCH_ESRAM34] = {
+		/*
+		 * esram34 is set in retention and supplied by Vsafe when Vape is off,
+		 * no need to hold Vape
+		 */
+		.constraints = {
+			.name = "db8500-esram34",
+			.valid_ops_mask = REGULATOR_CHANGE_STATUS,
+		},
+		.consumer_supplies = db8500_esram34_consumers,
+		.num_consumer_supplies = ARRAY_SIZE(db8500_esram34_consumers),
+	},
+	[DB8500_REGULATOR_SWITCH_ESRAM34RET] = {
+		.constraints = {
+			.name = "db8500-esram34-ret",
+			.valid_ops_mask = REGULATOR_CHANGE_STATUS,
+		},
+	},
+};
+
+static struct mfd_cell db8500_prcmu_devs[] = {
+	{
+		.name = "db8500-prcmu-regulators",
+		.platform_data = &db8500_regulators,
+		.pdata_size = sizeof(db8500_regulators),
+	},
+	{
+		.name = "cpufreq-u8500",
+	},
+};
+
+/**
+ * prcmu_fw_init - arch init call for the Linux PRCMU fw init logic
+ *
+ */
+static int __init db8500_prcmu_probe(struct platform_device *pdev)
+{
+	int err = 0;
+
+	if (ux500_is_svp())
+		return -ENODEV;
+
+	init_prcm_registers();
+
+	/* Clean up the mailbox interrupts after pre-kernel code. */
+	writel(ALL_MBOX_BITS, PRCM_ARM_IT1_CLR);
+
+	err = request_threaded_irq(IRQ_DB8500_PRCMU1, prcmu_irq_handler,
+		prcmu_irq_thread_fn, IRQF_NO_SUSPEND, "prcmu", NULL);
+	if (err < 0) {
+		pr_err("prcmu: Failed to allocate IRQ_DB8500_PRCMU1.\n");
+		err = -EBUSY;
+		goto no_irq_return;
+	}
+
+	if (cpu_is_u8500v20_or_later())
+		prcmu_config_esram0_deep_sleep(ESRAM0_DEEP_SLEEP_STATE_RET);
+
+	err = mfd_add_devices(&pdev->dev, 0, db8500_prcmu_devs,
+			      ARRAY_SIZE(db8500_prcmu_devs), NULL,
+			      0);
+
+	if (err)
+		pr_err("prcmu: Failed to add subdevices\n");
+	else
+		pr_info("DB8500 PRCMU initialized\n");
+
+no_irq_return:
+	return err;
+}
+
+static struct platform_driver db8500_prcmu_driver = {
+	.driver = {
+		.name = "db8500-prcmu",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init db8500_prcmu_init(void)
+{
+	return platform_driver_probe(&db8500_prcmu_driver, db8500_prcmu_probe);
+}
+
+arch_initcall(db8500_prcmu_init);
+
+MODULE_AUTHOR("Mattias Nilsson <mattias.i.nilsson@stericsson.com>");
+MODULE_DESCRIPTION("DB8500 PRCM Unit driver");
+MODULE_LICENSE("GPL v2");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/dbx500-prcmu-regs.h b/ap/os/linux/linux-3.4.x/drivers/mfd/dbx500-prcmu-regs.h
new file mode 100644
index 0000000..3a0bf91
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/dbx500-prcmu-regs.h
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) STMicroelectronics 2009
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Kumar Sanghvi <kumar.sanghvi@stericsson.com>
+ * Author: Sundar Iyer <sundar.iyer@stericsson.com>
+ *
+ * License Terms: GNU General Public License v2
+ *
+ * PRCM Unit registers
+ */
+
+#ifndef __DB8500_PRCMU_REGS_H
+#define __DB8500_PRCMU_REGS_H
+
+#include <mach/hardware.h>
+
+#define BITS(_start, _end) ((BIT(_end) - BIT(_start)) + BIT(_end))
+
+#define PRCM_CLK_MGT(_offset) (void __iomem *)(IO_ADDRESS(U8500_PRCMU_BASE) \
+	+ _offset)
+#define PRCM_ACLK_MGT		PRCM_CLK_MGT(0x004)
+#define PRCM_SVACLK_MGT		PRCM_CLK_MGT(0x008)
+#define PRCM_SIACLK_MGT		PRCM_CLK_MGT(0x00C)
+#define PRCM_SGACLK_MGT		PRCM_CLK_MGT(0x014)
+#define PRCM_UARTCLK_MGT	PRCM_CLK_MGT(0x018)
+#define PRCM_MSP02CLK_MGT	PRCM_CLK_MGT(0x01C)
+#define PRCM_I2CCLK_MGT		PRCM_CLK_MGT(0x020)
+#define PRCM_SDMMCCLK_MGT	PRCM_CLK_MGT(0x024)
+#define PRCM_SLIMCLK_MGT	PRCM_CLK_MGT(0x028)
+#define PRCM_PER1CLK_MGT	PRCM_CLK_MGT(0x02C)
+#define PRCM_PER2CLK_MGT	PRCM_CLK_MGT(0x030)
+#define PRCM_PER3CLK_MGT	PRCM_CLK_MGT(0x034)
+#define PRCM_PER5CLK_MGT	PRCM_CLK_MGT(0x038)
+#define PRCM_PER6CLK_MGT	PRCM_CLK_MGT(0x03C)
+#define PRCM_PER7CLK_MGT	PRCM_CLK_MGT(0x040)
+#define PRCM_LCDCLK_MGT		PRCM_CLK_MGT(0x044)
+#define PRCM_BMLCLK_MGT		PRCM_CLK_MGT(0x04C)
+#define PRCM_HSITXCLK_MGT	PRCM_CLK_MGT(0x050)
+#define PRCM_HSIRXCLK_MGT	PRCM_CLK_MGT(0x054)
+#define PRCM_HDMICLK_MGT	PRCM_CLK_MGT(0x058)
+#define PRCM_APEATCLK_MGT	PRCM_CLK_MGT(0x05C)
+#define PRCM_APETRACECLK_MGT	PRCM_CLK_MGT(0x060)
+#define PRCM_MCDECLK_MGT	PRCM_CLK_MGT(0x064)
+#define PRCM_IPI2CCLK_MGT	PRCM_CLK_MGT(0x068)
+#define PRCM_DSIALTCLK_MGT	PRCM_CLK_MGT(0x06C)
+#define PRCM_DMACLK_MGT		PRCM_CLK_MGT(0x074)
+#define PRCM_B2R2CLK_MGT	PRCM_CLK_MGT(0x078)
+#define PRCM_TVCLK_MGT		PRCM_CLK_MGT(0x07C)
+#define PRCM_UNIPROCLK_MGT	PRCM_CLK_MGT(0x278)
+#define PRCM_SSPCLK_MGT		PRCM_CLK_MGT(0x280)
+#define PRCM_RNGCLK_MGT		PRCM_CLK_MGT(0x284)
+#define PRCM_UICCCLK_MGT	PRCM_CLK_MGT(0x27C)
+#define PRCM_MSP1CLK_MGT	PRCM_CLK_MGT(0x288)
+
+#define PRCM_ARM_PLLDIVPS	(_PRCMU_BASE + 0x118)
+#define PRCM_ARM_PLLDIVPS_ARM_BRM_RATE		0x3f
+#define PRCM_ARM_PLLDIVPS_MAX_MASK		0xf
+
+#define PRCM_PLLARM_LOCKP       (_PRCMU_BASE + 0x0a8)
+#define PRCM_PLLARM_LOCKP_PRCM_PLLARM_LOCKP3	0x2
+
+#define PRCM_ARM_CHGCLKREQ	(_PRCMU_BASE + 0x114)
+#define PRCM_ARM_CHGCLKREQ_PRCM_ARM_CHGCLKREQ	0x1
+
+#define PRCM_PLLARM_ENABLE	(_PRCMU_BASE + 0x98)
+#define PRCM_PLLARM_ENABLE_PRCM_PLLARM_ENABLE	0x1
+#define PRCM_PLLARM_ENABLE_PRCM_PLLARM_COUNTON	0x100
+
+#define PRCM_ARMCLKFIX_MGT	(_PRCMU_BASE + 0x0)
+#define PRCM_A9PL_FORCE_CLKEN	(_PRCMU_BASE + 0x19C)
+#define PRCM_A9_RESETN_CLR	(_PRCMU_BASE + 0x1f4)
+#define PRCM_A9_RESETN_SET	(_PRCMU_BASE + 0x1f0)
+#define PRCM_ARM_LS_CLAMP	(_PRCMU_BASE + 0x30c)
+#define PRCM_SRAM_A9		(_PRCMU_BASE + 0x308)
+
+#define PRCM_A9PL_FORCE_CLKEN_PRCM_A9PL_FORCE_CLKEN BIT(0)
+#define PRCM_A9PL_FORCE_CLKEN_PRCM_A9AXI_FORCE_CLKEN BIT(1)
+
+/* ARM WFI Standby signal register */
+#define PRCM_ARM_WFI_STANDBY    (_PRCMU_BASE + 0x130)
+#define PRCM_ARM_WFI_STANDBY_WFI0               0x08
+#define PRCM_ARM_WFI_STANDBY_WFI1               0x10
+#define PRCM_IOCR		(_PRCMU_BASE + 0x310)
+#define PRCM_IOCR_IOFORCE			0x1
+
+/* CPU mailbox registers */
+#define PRCM_MBOX_CPU_VAL	(_PRCMU_BASE + 0x0fc)
+#define PRCM_MBOX_CPU_SET	(_PRCMU_BASE + 0x100)
+#define PRCM_MBOX_CPU_CLR	(_PRCMU_BASE + 0x104)
+
+/* Dual A9 core interrupt management unit registers */
+#define PRCM_A9_MASK_REQ	(_PRCMU_BASE + 0x328)
+#define PRCM_A9_MASK_REQ_PRCM_A9_MASK_REQ	0x1
+
+#define PRCM_A9_MASK_ACK	(_PRCMU_BASE + 0x32c)
+#define PRCM_ARMITMSK31TO0	(_PRCMU_BASE + 0x11c)
+#define PRCM_ARMITMSK63TO32	(_PRCMU_BASE + 0x120)
+#define PRCM_ARMITMSK95TO64	(_PRCMU_BASE + 0x124)
+#define PRCM_ARMITMSK127TO96	(_PRCMU_BASE + 0x128)
+#define PRCM_POWER_STATE_VAL	(_PRCMU_BASE + 0x25C)
+#define PRCM_ARMITVAL31TO0	(_PRCMU_BASE + 0x260)
+#define PRCM_ARMITVAL63TO32	(_PRCMU_BASE + 0x264)
+#define PRCM_ARMITVAL95TO64	(_PRCMU_BASE + 0x268)
+#define PRCM_ARMITVAL127TO96	(_PRCMU_BASE + 0x26C)
+
+#define PRCM_HOSTACCESS_REQ	(_PRCMU_BASE + 0x334)
+#define PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ 0x1
+#define ARM_WAKEUP_MODEM	0x1
+
+#define PRCM_ARM_IT1_CLR	(_PRCMU_BASE + 0x48C)
+#define PRCM_ARM_IT1_VAL	(_PRCMU_BASE + 0x494)
+#define PRCM_HOLD_EVT		(_PRCMU_BASE + 0x174)
+
+#define PRCM_MOD_AWAKE_STATUS	(_PRCMU_BASE + 0x4A0)
+#define PRCM_MOD_AWAKE_STATUS_PRCM_MOD_COREPD_AWAKE	BIT(0)
+#define PRCM_MOD_AWAKE_STATUS_PRCM_MOD_AAPD_AWAKE	BIT(1)
+#define PRCM_MOD_AWAKE_STATUS_PRCM_MOD_VMODEM_OFF_ISO	BIT(2)
+
+#define PRCM_ITSTATUS0		(_PRCMU_BASE + 0x148)
+#define PRCM_ITSTATUS1		(_PRCMU_BASE + 0x150)
+#define PRCM_ITSTATUS2		(_PRCMU_BASE + 0x158)
+#define PRCM_ITSTATUS3		(_PRCMU_BASE + 0x160)
+#define PRCM_ITSTATUS4		(_PRCMU_BASE + 0x168)
+#define PRCM_ITSTATUS5		(_PRCMU_BASE + 0x484)
+#define PRCM_ITCLEAR5		(_PRCMU_BASE + 0x488)
+#define PRCM_ARMIT_MASKXP70_IT	(_PRCMU_BASE + 0x1018)
+
+/* System reset register */
+#define PRCM_APE_SOFTRST	(_PRCMU_BASE + 0x228)
+
+/* Level shifter and clamp control registers */
+#define PRCM_MMIP_LS_CLAMP_SET     (_PRCMU_BASE + 0x420)
+#define PRCM_MMIP_LS_CLAMP_CLR     (_PRCMU_BASE + 0x424)
+
+#define PRCM_MMIP_LS_CLAMP_DSIPLL_CLAMP		BIT(11)
+#define PRCM_MMIP_LS_CLAMP_DSIPLL_CLAMPI	BIT(22)
+
+/* PRCMU clock/PLL/reset registers */
+#define PRCM_PLLSOC0_FREQ	   (_PRCMU_BASE + 0x080)
+#define PRCM_PLLSOC1_FREQ	   (_PRCMU_BASE + 0x084)
+#define PRCM_PLLDDR_FREQ	   (_PRCMU_BASE + 0x08C)
+#define PRCM_PLL_FREQ_D_SHIFT	0
+#define PRCM_PLL_FREQ_D_MASK	BITS(0, 7)
+#define PRCM_PLL_FREQ_N_SHIFT	8
+#define PRCM_PLL_FREQ_N_MASK	BITS(8, 13)
+#define PRCM_PLL_FREQ_R_SHIFT	16
+#define PRCM_PLL_FREQ_R_MASK	BITS(16, 18)
+#define PRCM_PLL_FREQ_SELDIV2	BIT(24)
+#define PRCM_PLL_FREQ_DIV2EN	BIT(25)
+
+#define PRCM_PLLDSI_FREQ           (_PRCMU_BASE + 0x500)
+#define PRCM_PLLDSI_ENABLE         (_PRCMU_BASE + 0x504)
+#define PRCM_PLLDSI_LOCKP          (_PRCMU_BASE + 0x508)
+#define PRCM_DSI_PLLOUT_SEL        (_PRCMU_BASE + 0x530)
+#define PRCM_DSITVCLK_DIV          (_PRCMU_BASE + 0x52C)
+#define PRCM_PLLDSI_LOCKP          (_PRCMU_BASE + 0x508)
+#define PRCM_APE_RESETN_SET        (_PRCMU_BASE + 0x1E4)
+#define PRCM_APE_RESETN_CLR        (_PRCMU_BASE + 0x1E8)
+
+#define PRCM_PLLDSI_ENABLE_PRCM_PLLDSI_ENABLE BIT(0)
+
+#define PRCM_PLLDSI_LOCKP_PRCM_PLLDSI_LOCKP10	BIT(0)
+#define PRCM_PLLDSI_LOCKP_PRCM_PLLDSI_LOCKP3	BIT(1)
+
+#define PRCM_DSI_PLLOUT_SEL_DSI0_PLLOUT_DIVSEL_SHIFT	0
+#define PRCM_DSI_PLLOUT_SEL_DSI0_PLLOUT_DIVSEL_MASK	BITS(0, 2)
+#define PRCM_DSI_PLLOUT_SEL_DSI1_PLLOUT_DIVSEL_SHIFT	8
+#define PRCM_DSI_PLLOUT_SEL_DSI1_PLLOUT_DIVSEL_MASK	BITS(8, 10)
+
+#define PRCM_DSI_PLLOUT_SEL_OFF		0
+#define PRCM_DSI_PLLOUT_SEL_PHI		1
+#define PRCM_DSI_PLLOUT_SEL_PHI_2	2
+#define PRCM_DSI_PLLOUT_SEL_PHI_4	3
+
+#define PRCM_DSITVCLK_DIV_DSI0_ESC_CLK_DIV_SHIFT	0
+#define PRCM_DSITVCLK_DIV_DSI0_ESC_CLK_DIV_MASK		BITS(0, 7)
+#define PRCM_DSITVCLK_DIV_DSI1_ESC_CLK_DIV_SHIFT	8
+#define PRCM_DSITVCLK_DIV_DSI1_ESC_CLK_DIV_MASK		BITS(8, 15)
+#define PRCM_DSITVCLK_DIV_DSI2_ESC_CLK_DIV_SHIFT	16
+#define PRCM_DSITVCLK_DIV_DSI2_ESC_CLK_DIV_MASK		BITS(16, 23)
+#define PRCM_DSITVCLK_DIV_DSI0_ESC_CLK_EN		BIT(24)
+#define PRCM_DSITVCLK_DIV_DSI1_ESC_CLK_EN		BIT(25)
+#define PRCM_DSITVCLK_DIV_DSI2_ESC_CLK_EN		BIT(26)
+
+#define PRCM_APE_RESETN_DSIPLL_RESETN BIT(14)
+
+#define PRCM_CLKOCR		   (_PRCMU_BASE + 0x1CC)
+#define PRCM_CLKOCR_CLKOUT0_REF_CLK	(1 << 0)
+#define PRCM_CLKOCR_CLKOUT0_MASK	BITS(0, 13)
+#define PRCM_CLKOCR_CLKOUT1_REF_CLK	(1 << 16)
+#define PRCM_CLKOCR_CLKOUT1_MASK	BITS(16, 29)
+
+/* ePOD and memory power signal control registers */
+#define PRCM_EPOD_C_SET            (_PRCMU_BASE + 0x410)
+#define PRCM_SRAM_LS_SLEEP         (_PRCMU_BASE + 0x304)
+
+/* Debug power control unit registers */
+#define PRCM_POWER_STATE_SET       (_PRCMU_BASE + 0x254)
+
+/* Miscellaneous unit registers */
+#define PRCM_DSI_SW_RESET          (_PRCMU_BASE + 0x324)
+#define PRCM_GPIOCR                (_PRCMU_BASE + 0x138)
+#define PRCM_GPIOCR_DBG_STM_MOD_CMD1            0x800
+#define PRCM_GPIOCR_DBG_UARTMOD_CMD0            0x1
+
+/* PRCMU HW semaphore */
+#define PRCM_SEM                   (_PRCMU_BASE + 0x400)
+#define PRCM_SEM_PRCM_SEM BIT(0)
+
+#define PRCM_TCR                   (_PRCMU_BASE + 0x1C8)
+#define PRCM_TCR_TENSEL_MASK       BITS(0, 7)
+#define PRCM_TCR_STOP_TIMERS       BIT(16)
+#define PRCM_TCR_DOZE_MODE         BIT(17)
+
+#define PRCM_CLKOCR_CLKODIV0_SHIFT	0
+#define PRCM_CLKOCR_CLKODIV0_MASK	BITS(0, 5)
+#define PRCM_CLKOCR_CLKOSEL0_SHIFT	6
+#define PRCM_CLKOCR_CLKOSEL0_MASK	BITS(6, 8)
+#define PRCM_CLKOCR_CLKODIV1_SHIFT	16
+#define PRCM_CLKOCR_CLKODIV1_MASK	BITS(16, 21)
+#define PRCM_CLKOCR_CLKOSEL1_SHIFT	22
+#define PRCM_CLKOCR_CLKOSEL1_MASK	BITS(22, 24)
+#define PRCM_CLKOCR_CLK1TYPE		BIT(28)
+
+#define PRCM_CLK_MGT_CLKPLLDIV_MASK		BITS(0, 4)
+#define PRCM_CLK_MGT_CLKPLLSW_SOC0		BIT(5)
+#define PRCM_CLK_MGT_CLKPLLSW_SOC1		BIT(6)
+#define PRCM_CLK_MGT_CLKPLLSW_DDR		BIT(7)
+#define PRCM_CLK_MGT_CLKPLLSW_MASK		BITS(5, 7)
+#define PRCM_CLK_MGT_CLKEN			BIT(8)
+#define PRCM_CLK_MGT_CLK38			BIT(9)
+#define PRCM_CLK_MGT_CLK38DIV			BIT(11)
+#define PRCM_SGACLK_MGT_SGACLKDIV_BY_2_5_EN	BIT(12)
+
+/* GPIOCR register */
+#define PRCM_GPIOCR_SPI2_SELECT BIT(23)
+
+#define PRCM_DDR_SUBSYS_APE_MINBW	(_PRCMU_BASE + 0x438)
+#define PRCM_CGATING_BYPASS		(_PRCMU_BASE + 0x134)
+#define PRCM_CGATING_BYPASS_ICN2	BIT(6)
+
+/* Miscellaneous unit registers */
+#define PRCM_RESOUTN_SET		(_PRCMU_BASE + 0x214)
+#define PRCM_RESOUTN_CLR		(_PRCMU_BASE + 0x218)
+
+/* System reset register */
+#define PRCM_APE_SOFTRST		(_PRCMU_BASE + 0x228)
+
+#endif /* __DB8500_PRCMU_REGS_H */
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/dm355evm_msp.c b/ap/os/linux/linux-3.4.x/drivers/mfd/dm355evm_msp.c
new file mode 100644
index 0000000..7710227
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/dm355evm_msp.c
@@ -0,0 +1,437 @@
+/*
+ * dm355evm_msp.c - driver for MSP430 firmware on DM355EVM board
+ *
+ * Copyright (C) 2008 David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/init.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/leds.h>
+#include <linux/i2c.h>
+#include <linux/i2c/dm355evm_msp.h>
+
+
+/*
+ * The DM355 is a DaVinci chip with video support but no C64+ DSP.  Its
+ * EVM board has an MSP430 programmed with firmware for various board
+ * support functions.  This driver exposes some of them directly, and
+ * supports other drivers (e.g. RTC, input) for more complex access.
+ *
+ * Because this firmware is entirely board-specific, this file embeds
+ * knowledge that would be passed as platform_data in a generic driver.
+ *
+ * This driver was tested with firmware revision A4.
+ */
+
+#if defined(CONFIG_INPUT_DM355EVM) || defined(CONFIG_INPUT_DM355EVM_MODULE)
+#define msp_has_keyboard()	true
+#else
+#define msp_has_keyboard()	false
+#endif
+
+#if defined(CONFIG_LEDS_GPIO) || defined(CONFIG_LEDS_GPIO_MODULE)
+#define msp_has_leds()		true
+#else
+#define msp_has_leds()		false
+#endif
+
+#if defined(CONFIG_RTC_DRV_DM355EVM) || defined(CONFIG_RTC_DRV_DM355EVM_MODULE)
+#define msp_has_rtc()		true
+#else
+#define msp_has_rtc()		false
+#endif
+
+#if defined(CONFIG_VIDEO_TVP514X) || defined(CONFIG_VIDEO_TVP514X_MODULE)
+#define msp_has_tvp()		true
+#else
+#define msp_has_tvp()		false
+#endif
+
+
+/*----------------------------------------------------------------------*/
+
+/* REVISIT for paranoia's sake, retry reads/writes on error */
+
+static struct i2c_client *msp430;
+
+/**
+ * dm355evm_msp_write - Writes a register in dm355evm_msp
+ * @value: the value to be written
+ * @reg: register address
+ *
+ * Returns result of operation - 0 is success, else negative errno
+ */
+int dm355evm_msp_write(u8 value, u8 reg)
+{
+	return i2c_smbus_write_byte_data(msp430, reg, value);
+}
+EXPORT_SYMBOL(dm355evm_msp_write);
+
+/**
+ * dm355evm_msp_read - Reads a register from dm355evm_msp
+ * @reg: register address
+ *
+ * Returns result of operation - value, or negative errno
+ */
+int dm355evm_msp_read(u8 reg)
+{
+	return i2c_smbus_read_byte_data(msp430, reg);
+}
+EXPORT_SYMBOL(dm355evm_msp_read);
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * Many of the msp430 pins are just used as fixed-direction GPIOs.
+ * We could export a few more of them this way, if we wanted.
+ */
+#define MSP_GPIO(bit,reg)	((DM355EVM_MSP_ ## reg) << 3 | (bit))
+
+static const u8 msp_gpios[] = {
+	/* eight leds */
+	MSP_GPIO(0, LED), MSP_GPIO(1, LED),
+	MSP_GPIO(2, LED), MSP_GPIO(3, LED),
+	MSP_GPIO(4, LED), MSP_GPIO(5, LED),
+	MSP_GPIO(6, LED), MSP_GPIO(7, LED),
+	/* SW6 and the NTSC/nPAL jumper */
+	MSP_GPIO(0, SWITCH1), MSP_GPIO(1, SWITCH1),
+	MSP_GPIO(2, SWITCH1), MSP_GPIO(3, SWITCH1),
+	MSP_GPIO(4, SWITCH1),
+	/* switches on MMC/SD sockets */
+	/*
+	 * Note: EVMDM355_ECP_VA4.pdf suggests that Bit 2 and 4 should be
+	 * checked for card detection. However on the EVM bit 1 and 3 gives
+	 * this status, for 0 and 1 instance respectively. The pdf also
+	 * suggests that Bit 1 and 3 should be checked for write protection.
+	 * However on the EVM bit 2 and 4 gives this status,for 0 and 1
+	 * instance respectively.
+	 */
+	MSP_GPIO(2, SDMMC), MSP_GPIO(1, SDMMC),	/* mmc0 WP, nCD */
+	MSP_GPIO(4, SDMMC), MSP_GPIO(3, SDMMC),	/* mmc1 WP, nCD */
+};
+
+#define MSP_GPIO_REG(offset)	(msp_gpios[(offset)] >> 3)
+#define MSP_GPIO_MASK(offset)	BIT(msp_gpios[(offset)] & 0x07)
+
+static int msp_gpio_in(struct gpio_chip *chip, unsigned offset)
+{
+	switch (MSP_GPIO_REG(offset)) {
+	case DM355EVM_MSP_SWITCH1:
+	case DM355EVM_MSP_SWITCH2:
+	case DM355EVM_MSP_SDMMC:
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
+static u8 msp_led_cache;
+
+static int msp_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+	int reg, status;
+
+	reg = MSP_GPIO_REG(offset);
+	status = dm355evm_msp_read(reg);
+	if (status < 0)
+		return status;
+	if (reg == DM355EVM_MSP_LED)
+		msp_led_cache = status;
+	return status & MSP_GPIO_MASK(offset);
+}
+
+static int msp_gpio_out(struct gpio_chip *chip, unsigned offset, int value)
+{
+	int mask, bits;
+
+	/* NOTE:  there are some other signals that could be
+	 * packaged as output GPIOs, but they aren't as useful
+	 * as the LEDs ... so for now we don't.
+	 */
+	if (MSP_GPIO_REG(offset) != DM355EVM_MSP_LED)
+		return -EINVAL;
+
+	mask = MSP_GPIO_MASK(offset);
+	bits = msp_led_cache;
+
+	bits &= ~mask;
+	if (value)
+		bits |= mask;
+	msp_led_cache = bits;
+
+	return dm355evm_msp_write(bits, DM355EVM_MSP_LED);
+}
+
+static void msp_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+	msp_gpio_out(chip, offset, value);
+}
+
+static struct gpio_chip dm355evm_msp_gpio = {
+	.label			= "dm355evm_msp",
+	.owner			= THIS_MODULE,
+	.direction_input	= msp_gpio_in,
+	.get			= msp_gpio_get,
+	.direction_output	= msp_gpio_out,
+	.set			= msp_gpio_set,
+	.base			= -EINVAL,		/* dynamic assignment */
+	.ngpio			= ARRAY_SIZE(msp_gpios),
+	.can_sleep		= true,
+};
+
+/*----------------------------------------------------------------------*/
+
+static struct device *add_child(struct i2c_client *client, const char *name,
+		void *pdata, unsigned pdata_len,
+		bool can_wakeup, int irq)
+{
+	struct platform_device	*pdev;
+	int			status;
+
+	pdev = platform_device_alloc(name, -1);
+	if (!pdev) {
+		dev_dbg(&client->dev, "can't alloc dev\n");
+		status = -ENOMEM;
+		goto err;
+	}
+
+	device_init_wakeup(&pdev->dev, can_wakeup);
+	pdev->dev.parent = &client->dev;
+
+	if (pdata) {
+		status = platform_device_add_data(pdev, pdata, pdata_len);
+		if (status < 0) {
+			dev_dbg(&pdev->dev, "can't add platform_data\n");
+			goto err;
+		}
+	}
+
+	if (irq) {
+		struct resource r = {
+			.start = irq,
+			.flags = IORESOURCE_IRQ,
+		};
+
+		status = platform_device_add_resources(pdev, &r, 1);
+		if (status < 0) {
+			dev_dbg(&pdev->dev, "can't add irq\n");
+			goto err;
+		}
+	}
+
+	status = platform_device_add(pdev);
+
+err:
+	if (status < 0) {
+		platform_device_put(pdev);
+		dev_err(&client->dev, "can't add %s dev\n", name);
+		return ERR_PTR(status);
+	}
+	return &pdev->dev;
+}
+
+static int add_children(struct i2c_client *client)
+{
+	static const struct {
+		int offset;
+		char *label;
+	} config_inputs[] = {
+		/* 8 == right after the LEDs */
+		{ 8 + 0, "sw6_1", },
+		{ 8 + 1, "sw6_2", },
+		{ 8 + 2, "sw6_3", },
+		{ 8 + 3, "sw6_4", },
+		{ 8 + 4, "NTSC/nPAL", },
+	};
+
+	struct device	*child;
+	int		status;
+	int		i;
+
+	/* GPIO-ish stuff */
+	dm355evm_msp_gpio.dev = &client->dev;
+	status = gpiochip_add(&dm355evm_msp_gpio);
+	if (status < 0)
+		return status;
+
+	/* LED output */
+	if (msp_has_leds()) {
+#define GPIO_LED(l)	.name = l, .active_low = true
+		static struct gpio_led evm_leds[] = {
+			{ GPIO_LED("dm355evm::ds14"),
+				.default_trigger = "heartbeat", },
+			{ GPIO_LED("dm355evm::ds15"),
+				.default_trigger = "mmc0", },
+			{ GPIO_LED("dm355evm::ds16"),
+				/* could also be a CE-ATA drive */
+				.default_trigger = "mmc1", },
+			{ GPIO_LED("dm355evm::ds17"),
+				.default_trigger = "nand-disk", },
+			{ GPIO_LED("dm355evm::ds18"), },
+			{ GPIO_LED("dm355evm::ds19"), },
+			{ GPIO_LED("dm355evm::ds20"), },
+			{ GPIO_LED("dm355evm::ds21"), },
+		};
+#undef GPIO_LED
+
+		struct gpio_led_platform_data evm_led_data = {
+			.num_leds	= ARRAY_SIZE(evm_leds),
+			.leds		= evm_leds,
+		};
+
+		for (i = 0; i < ARRAY_SIZE(evm_leds); i++)
+			evm_leds[i].gpio = i + dm355evm_msp_gpio.base;
+
+		/* NOTE:  these are the only fully programmable LEDs
+		 * on the board, since GPIO-61/ds22 (and many signals
+		 * going to DC7) must be used for AEMIF address lines
+		 * unless the top 1 GB of NAND is unused...
+		 */
+		child = add_child(client, "leds-gpio",
+				&evm_led_data, sizeof(evm_led_data),
+				false, 0);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+	}
+
+	/* configuration inputs */
+	for (i = 0; i < ARRAY_SIZE(config_inputs); i++) {
+		int gpio = dm355evm_msp_gpio.base + config_inputs[i].offset;
+
+		gpio_request_one(gpio, GPIOF_IN, config_inputs[i].label);
+
+		/* make it easy for userspace to see these */
+		gpio_export(gpio, false);
+	}
+
+	/* MMC/SD inputs -- right after the last config input */
+	if (client->dev.platform_data) {
+		void (*mmcsd_setup)(unsigned) = client->dev.platform_data;
+
+		mmcsd_setup(dm355evm_msp_gpio.base + 8 + 5);
+	}
+
+	/* RTC is a 32 bit counter, no alarm */
+	if (msp_has_rtc()) {
+		child = add_child(client, "rtc-dm355evm",
+				NULL, 0, false, 0);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+	}
+
+	/* input from buttons and IR remote (uses the IRQ) */
+	if (msp_has_keyboard()) {
+		child = add_child(client, "dm355evm_keys",
+				NULL, 0, true, client->irq);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+	}
+
+	return 0;
+}
+
+/*----------------------------------------------------------------------*/
+
+static void dm355evm_command(unsigned command)
+{
+	int status;
+
+	status = dm355evm_msp_write(command, DM355EVM_MSP_COMMAND);
+	if (status < 0)
+		dev_err(&msp430->dev, "command %d failure %d\n",
+				command, status);
+}
+
+static void dm355evm_power_off(void)
+{
+	dm355evm_command(MSP_COMMAND_POWEROFF);
+}
+
+static int dm355evm_msp_remove(struct i2c_client *client)
+{
+	pm_power_off = NULL;
+	msp430 = NULL;
+	return 0;
+}
+
+static int
+dm355evm_msp_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+	int		status;
+	const char	*video = msp_has_tvp() ? "TVP5146" : "imager";
+
+	if (msp430)
+		return -EBUSY;
+	msp430 = client;
+
+	/* display revision status; doubles as sanity check */
+	status = dm355evm_msp_read(DM355EVM_MSP_FIRMREV);
+	if (status < 0)
+		goto fail;
+	dev_info(&client->dev, "firmware v.%02X, %s as video-in\n",
+			status, video);
+
+	/* mux video input:  either tvp5146 or some external imager */
+	status = dm355evm_msp_write(msp_has_tvp() ? 0 : MSP_VIDEO_IMAGER,
+			DM355EVM_MSP_VIDEO_IN);
+	if (status < 0)
+		dev_warn(&client->dev, "error %d muxing %s as video-in\n",
+			status, video);
+
+	/* init LED cache, and turn off the LEDs */
+	msp_led_cache = 0xff;
+	dm355evm_msp_write(msp_led_cache, DM355EVM_MSP_LED);
+
+	/* export capabilities we support */
+	status = add_children(client);
+	if (status < 0)
+		goto fail;
+
+	/* PM hookup */
+	pm_power_off = dm355evm_power_off;
+
+	return 0;
+
+fail:
+	/* FIXME remove children ... */
+	dm355evm_msp_remove(client);
+	return status;
+}
+
+static const struct i2c_device_id dm355evm_msp_ids[] = {
+	{ "dm355evm_msp", 0 },
+	{ /* end of list */ },
+};
+MODULE_DEVICE_TABLE(i2c, dm355evm_msp_ids);
+
+static struct i2c_driver dm355evm_msp_driver = {
+	.driver.name	= "dm355evm_msp",
+	.id_table	= dm355evm_msp_ids,
+	.probe		= dm355evm_msp_probe,
+	.remove		= dm355evm_msp_remove,
+};
+
+static int __init dm355evm_msp_init(void)
+{
+	return i2c_add_driver(&dm355evm_msp_driver);
+}
+subsys_initcall(dm355evm_msp_init);
+
+static void __exit dm355evm_msp_exit(void)
+{
+	i2c_del_driver(&dm355evm_msp_driver);
+}
+module_exit(dm355evm_msp_exit);
+
+MODULE_DESCRIPTION("Interface to MSP430 firmware on DM355EVM");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/ezx-pcap.c b/ap/os/linux/linux-3.4.x/drivers/mfd/ezx-pcap.c
new file mode 100644
index 0000000..db662e2
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/ezx-pcap.c
@@ -0,0 +1,551 @@
+/*
+ * Driver for Motorola PCAP2 as present in EZX phones
+ *
+ * Copyright (C) 2006 Harald Welte <laforge@openezx.org>
+ * Copyright (C) 2009 Daniel Ribeiro <drwyrm@gmail.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/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/mfd/ezx-pcap.h>
+#include <linux/spi/spi.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+
+#define PCAP_ADC_MAXQ		8
+struct pcap_adc_request {
+	u8 bank;
+	u8 ch[2];
+	u32 flags;
+	void (*callback)(void *, u16[]);
+	void *data;
+};
+
+struct pcap_adc_sync_request {
+	u16 res[2];
+	struct completion completion;
+};
+
+struct pcap_chip {
+	struct spi_device *spi;
+
+	/* IO */
+	u32 buf;
+	struct mutex io_mutex;
+
+	/* IRQ */
+	unsigned int irq_base;
+	u32 msr;
+	struct work_struct isr_work;
+	struct work_struct msr_work;
+	struct workqueue_struct *workqueue;
+
+	/* ADC */
+	struct pcap_adc_request *adc_queue[PCAP_ADC_MAXQ];
+	u8 adc_head;
+	u8 adc_tail;
+	struct mutex adc_mutex;
+};
+
+/* IO */
+static int ezx_pcap_putget(struct pcap_chip *pcap, u32 *data)
+{
+	struct spi_transfer t;
+	struct spi_message m;
+	int status;
+
+	memset(&t, 0, sizeof t);
+	spi_message_init(&m);
+	t.len = sizeof(u32);
+	spi_message_add_tail(&t, &m);
+
+	pcap->buf = *data;
+	t.tx_buf = (u8 *) &pcap->buf;
+	t.rx_buf = (u8 *) &pcap->buf;
+	status = spi_sync(pcap->spi, &m);
+
+	if (status == 0)
+		*data = pcap->buf;
+
+	return status;
+}
+
+int ezx_pcap_write(struct pcap_chip *pcap, u8 reg_num, u32 value)
+{
+	int ret;
+
+	mutex_lock(&pcap->io_mutex);
+	value &= PCAP_REGISTER_VALUE_MASK;
+	value |= PCAP_REGISTER_WRITE_OP_BIT
+		| (reg_num << PCAP_REGISTER_ADDRESS_SHIFT);
+	ret = ezx_pcap_putget(pcap, &value);
+	mutex_unlock(&pcap->io_mutex);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(ezx_pcap_write);
+
+int ezx_pcap_read(struct pcap_chip *pcap, u8 reg_num, u32 *value)
+{
+	int ret;
+
+	mutex_lock(&pcap->io_mutex);
+	*value = PCAP_REGISTER_READ_OP_BIT
+		| (reg_num << PCAP_REGISTER_ADDRESS_SHIFT);
+
+	ret = ezx_pcap_putget(pcap, value);
+	mutex_unlock(&pcap->io_mutex);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(ezx_pcap_read);
+
+int ezx_pcap_set_bits(struct pcap_chip *pcap, u8 reg_num, u32 mask, u32 val)
+{
+	int ret;
+	u32 tmp = PCAP_REGISTER_READ_OP_BIT |
+		(reg_num << PCAP_REGISTER_ADDRESS_SHIFT);
+
+	mutex_lock(&pcap->io_mutex);
+	ret = ezx_pcap_putget(pcap, &tmp);
+	if (ret)
+		goto out_unlock;
+
+	tmp &= (PCAP_REGISTER_VALUE_MASK & ~mask);
+	tmp |= (val & mask) | PCAP_REGISTER_WRITE_OP_BIT |
+		(reg_num << PCAP_REGISTER_ADDRESS_SHIFT);
+
+	ret = ezx_pcap_putget(pcap, &tmp);
+out_unlock:
+	mutex_unlock(&pcap->io_mutex);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(ezx_pcap_set_bits);
+
+/* IRQ */
+int irq_to_pcap(struct pcap_chip *pcap, int irq)
+{
+	return irq - pcap->irq_base;
+}
+EXPORT_SYMBOL_GPL(irq_to_pcap);
+
+int pcap_to_irq(struct pcap_chip *pcap, int irq)
+{
+	return pcap->irq_base + irq;
+}
+EXPORT_SYMBOL_GPL(pcap_to_irq);
+
+static void pcap_mask_irq(struct irq_data *d)
+{
+	struct pcap_chip *pcap = irq_data_get_irq_chip_data(d);
+
+	pcap->msr |= 1 << irq_to_pcap(pcap, d->irq);
+	queue_work(pcap->workqueue, &pcap->msr_work);
+}
+
+static void pcap_unmask_irq(struct irq_data *d)
+{
+	struct pcap_chip *pcap = irq_data_get_irq_chip_data(d);
+
+	pcap->msr &= ~(1 << irq_to_pcap(pcap, d->irq));
+	queue_work(pcap->workqueue, &pcap->msr_work);
+}
+
+static struct irq_chip pcap_irq_chip = {
+	.name		= "pcap",
+	.irq_disable	= pcap_mask_irq,
+	.irq_mask	= pcap_mask_irq,
+	.irq_unmask	= pcap_unmask_irq,
+};
+
+static void pcap_msr_work(struct work_struct *work)
+{
+	struct pcap_chip *pcap = container_of(work, struct pcap_chip, msr_work);
+
+	ezx_pcap_write(pcap, PCAP_REG_MSR, pcap->msr);
+}
+
+static void pcap_isr_work(struct work_struct *work)
+{
+	struct pcap_chip *pcap = container_of(work, struct pcap_chip, isr_work);
+	struct pcap_platform_data *pdata = pcap->spi->dev.platform_data;
+	u32 msr, isr, int_sel, service;
+	int irq;
+
+	do {
+		ezx_pcap_read(pcap, PCAP_REG_MSR, &msr);
+		ezx_pcap_read(pcap, PCAP_REG_ISR, &isr);
+
+		/* We can't service/ack irqs that are assigned to port 2 */
+		if (!(pdata->config & PCAP_SECOND_PORT)) {
+			ezx_pcap_read(pcap, PCAP_REG_INT_SEL, &int_sel);
+			isr &= ~int_sel;
+		}
+
+		ezx_pcap_write(pcap, PCAP_REG_MSR, isr | msr);
+		ezx_pcap_write(pcap, PCAP_REG_ISR, isr);
+
+		local_irq_disable();
+		service = isr & ~msr;
+		for (irq = pcap->irq_base; service; service >>= 1, irq++) {
+			if (service & 1)
+				generic_handle_irq(irq);
+		}
+		local_irq_enable();
+		ezx_pcap_write(pcap, PCAP_REG_MSR, pcap->msr);
+	} while (gpio_get_value(pdata->gpio));
+}
+
+static void pcap_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+	struct pcap_chip *pcap = irq_get_handler_data(irq);
+
+	desc->irq_data.chip->irq_ack(&desc->irq_data);
+	queue_work(pcap->workqueue, &pcap->isr_work);
+	return;
+}
+
+/* ADC */
+void pcap_set_ts_bits(struct pcap_chip *pcap, u32 bits)
+{
+	u32 tmp;
+
+	mutex_lock(&pcap->adc_mutex);
+	ezx_pcap_read(pcap, PCAP_REG_ADC, &tmp);
+	tmp &= ~(PCAP_ADC_TS_M_MASK | PCAP_ADC_TS_REF_LOWPWR);
+	tmp |= bits & (PCAP_ADC_TS_M_MASK | PCAP_ADC_TS_REF_LOWPWR);
+	ezx_pcap_write(pcap, PCAP_REG_ADC, tmp);
+	mutex_unlock(&pcap->adc_mutex);
+}
+EXPORT_SYMBOL_GPL(pcap_set_ts_bits);
+
+static void pcap_disable_adc(struct pcap_chip *pcap)
+{
+	u32 tmp;
+
+	ezx_pcap_read(pcap, PCAP_REG_ADC, &tmp);
+	tmp &= ~(PCAP_ADC_ADEN|PCAP_ADC_BATT_I_ADC|PCAP_ADC_BATT_I_POLARITY);
+	ezx_pcap_write(pcap, PCAP_REG_ADC, tmp);
+}
+
+static void pcap_adc_trigger(struct pcap_chip *pcap)
+{
+	u32 tmp;
+	u8 head;
+
+	mutex_lock(&pcap->adc_mutex);
+	head = pcap->adc_head;
+	if (!pcap->adc_queue[head]) {
+		/* queue is empty, save power */
+		pcap_disable_adc(pcap);
+		mutex_unlock(&pcap->adc_mutex);
+		return;
+	}
+	/* start conversion on requested bank, save TS_M bits */
+	ezx_pcap_read(pcap, PCAP_REG_ADC, &tmp);
+	tmp &= (PCAP_ADC_TS_M_MASK | PCAP_ADC_TS_REF_LOWPWR);
+	tmp |= pcap->adc_queue[head]->flags | PCAP_ADC_ADEN;
+
+	if (pcap->adc_queue[head]->bank == PCAP_ADC_BANK_1)
+		tmp |= PCAP_ADC_AD_SEL1;
+
+	ezx_pcap_write(pcap, PCAP_REG_ADC, tmp);
+	mutex_unlock(&pcap->adc_mutex);
+	ezx_pcap_write(pcap, PCAP_REG_ADR, PCAP_ADR_ASC);
+}
+
+static irqreturn_t pcap_adc_irq(int irq, void *_pcap)
+{
+	struct pcap_chip *pcap = _pcap;
+	struct pcap_adc_request *req;
+	u16 res[2];
+	u32 tmp;
+
+	mutex_lock(&pcap->adc_mutex);
+	req = pcap->adc_queue[pcap->adc_head];
+
+	if (WARN(!req, "adc irq without pending request\n")) {
+		mutex_unlock(&pcap->adc_mutex);
+		return IRQ_HANDLED;
+	}
+
+	/* read requested channels results */
+	ezx_pcap_read(pcap, PCAP_REG_ADC, &tmp);
+	tmp &= ~(PCAP_ADC_ADA1_MASK | PCAP_ADC_ADA2_MASK);
+	tmp |= (req->ch[0] << PCAP_ADC_ADA1_SHIFT);
+	tmp |= (req->ch[1] << PCAP_ADC_ADA2_SHIFT);
+	ezx_pcap_write(pcap, PCAP_REG_ADC, tmp);
+	ezx_pcap_read(pcap, PCAP_REG_ADR, &tmp);
+	res[0] = (tmp & PCAP_ADR_ADD1_MASK) >> PCAP_ADR_ADD1_SHIFT;
+	res[1] = (tmp & PCAP_ADR_ADD2_MASK) >> PCAP_ADR_ADD2_SHIFT;
+
+	pcap->adc_queue[pcap->adc_head] = NULL;
+	pcap->adc_head = (pcap->adc_head + 1) & (PCAP_ADC_MAXQ - 1);
+	mutex_unlock(&pcap->adc_mutex);
+
+	/* pass the results and release memory */
+	req->callback(req->data, res);
+	kfree(req);
+
+	/* trigger next conversion (if any) on queue */
+	pcap_adc_trigger(pcap);
+
+	return IRQ_HANDLED;
+}
+
+int pcap_adc_async(struct pcap_chip *pcap, u8 bank, u32 flags, u8 ch[],
+						void *callback, void *data)
+{
+	struct pcap_adc_request *req;
+
+	/* This will be freed after we have a result */
+	req = kmalloc(sizeof(struct pcap_adc_request), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	req->bank = bank;
+	req->flags = flags;
+	req->ch[0] = ch[0];
+	req->ch[1] = ch[1];
+	req->callback = callback;
+	req->data = data;
+
+	mutex_lock(&pcap->adc_mutex);
+	if (pcap->adc_queue[pcap->adc_tail]) {
+		mutex_unlock(&pcap->adc_mutex);
+		kfree(req);
+		return -EBUSY;
+	}
+	pcap->adc_queue[pcap->adc_tail] = req;
+	pcap->adc_tail = (pcap->adc_tail + 1) & (PCAP_ADC_MAXQ - 1);
+	mutex_unlock(&pcap->adc_mutex);
+
+	/* start conversion */
+	pcap_adc_trigger(pcap);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pcap_adc_async);
+
+static void pcap_adc_sync_cb(void *param, u16 res[])
+{
+	struct pcap_adc_sync_request *req = param;
+
+	req->res[0] = res[0];
+	req->res[1] = res[1];
+	complete(&req->completion);
+}
+
+int pcap_adc_sync(struct pcap_chip *pcap, u8 bank, u32 flags, u8 ch[],
+								u16 res[])
+{
+	struct pcap_adc_sync_request sync_data;
+	int ret;
+
+	init_completion(&sync_data.completion);
+	ret = pcap_adc_async(pcap, bank, flags, ch, pcap_adc_sync_cb,
+								&sync_data);
+	if (ret)
+		return ret;
+	wait_for_completion(&sync_data.completion);
+	res[0] = sync_data.res[0];
+	res[1] = sync_data.res[1];
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pcap_adc_sync);
+
+/* subdevs */
+static int pcap_remove_subdev(struct device *dev, void *unused)
+{
+	platform_device_unregister(to_platform_device(dev));
+	return 0;
+}
+
+static int __devinit pcap_add_subdev(struct pcap_chip *pcap,
+						struct pcap_subdev *subdev)
+{
+	struct platform_device *pdev;
+	int ret;
+
+	pdev = platform_device_alloc(subdev->name, subdev->id);
+	if (!pdev)
+		return -ENOMEM;
+
+	pdev->dev.parent = &pcap->spi->dev;
+	pdev->dev.platform_data = subdev->platform_data;
+
+	ret = platform_device_add(pdev);
+	if (ret)
+		platform_device_put(pdev);
+
+	return ret;
+}
+
+static int __devexit ezx_pcap_remove(struct spi_device *spi)
+{
+	struct pcap_chip *pcap = dev_get_drvdata(&spi->dev);
+	struct pcap_platform_data *pdata = spi->dev.platform_data;
+	int i, adc_irq;
+
+	/* remove all registered subdevs */
+	device_for_each_child(&spi->dev, NULL, pcap_remove_subdev);
+
+	/* cleanup ADC */
+	adc_irq = pcap_to_irq(pcap, (pdata->config & PCAP_SECOND_PORT) ?
+				PCAP_IRQ_ADCDONE2 : PCAP_IRQ_ADCDONE);
+	free_irq(adc_irq, pcap);
+	mutex_lock(&pcap->adc_mutex);
+	for (i = 0; i < PCAP_ADC_MAXQ; i++)
+		kfree(pcap->adc_queue[i]);
+	mutex_unlock(&pcap->adc_mutex);
+
+	/* cleanup irqchip */
+	for (i = pcap->irq_base; i < (pcap->irq_base + PCAP_NIRQS); i++)
+		irq_set_chip_and_handler(i, NULL, NULL);
+
+	destroy_workqueue(pcap->workqueue);
+
+	kfree(pcap);
+
+	return 0;
+}
+
+static int __devinit ezx_pcap_probe(struct spi_device *spi)
+{
+	struct pcap_platform_data *pdata = spi->dev.platform_data;
+	struct pcap_chip *pcap;
+	int i, adc_irq;
+	int ret = -ENODEV;
+
+	/* platform data is required */
+	if (!pdata)
+		goto ret;
+
+	pcap = kzalloc(sizeof(*pcap), GFP_KERNEL);
+	if (!pcap) {
+		ret = -ENOMEM;
+		goto ret;
+	}
+
+	mutex_init(&pcap->io_mutex);
+	mutex_init(&pcap->adc_mutex);
+	INIT_WORK(&pcap->isr_work, pcap_isr_work);
+	INIT_WORK(&pcap->msr_work, pcap_msr_work);
+	dev_set_drvdata(&spi->dev, pcap);
+
+	/* setup spi */
+	spi->bits_per_word = 32;
+	spi->mode = SPI_MODE_0 | (pdata->config & PCAP_CS_AH ? SPI_CS_HIGH : 0);
+	ret = spi_setup(spi);
+	if (ret)
+		goto free_pcap;
+
+	pcap->spi = spi;
+
+	/* setup irq */
+	pcap->irq_base = pdata->irq_base;
+	pcap->workqueue = create_singlethread_workqueue("pcapd");
+	if (!pcap->workqueue) {
+		ret = -ENOMEM;
+		dev_err(&spi->dev, "can't create pcap thread\n");
+		goto free_pcap;
+	}
+
+	/* redirect interrupts to AP, except adcdone2 */
+	if (!(pdata->config & PCAP_SECOND_PORT))
+		ezx_pcap_write(pcap, PCAP_REG_INT_SEL,
+					(1 << PCAP_IRQ_ADCDONE2));
+
+	/* setup irq chip */
+	for (i = pcap->irq_base; i < (pcap->irq_base + PCAP_NIRQS); i++) {
+		irq_set_chip_and_handler(i, &pcap_irq_chip, handle_simple_irq);
+		irq_set_chip_data(i, pcap);
+#ifdef CONFIG_ARM
+		set_irq_flags(i, IRQF_VALID);
+#else
+		irq_set_noprobe(i);
+#endif
+	}
+
+	/* mask/ack all PCAP interrupts */
+	ezx_pcap_write(pcap, PCAP_REG_MSR, PCAP_MASK_ALL_INTERRUPT);
+	ezx_pcap_write(pcap, PCAP_REG_ISR, PCAP_CLEAR_INTERRUPT_REGISTER);
+	pcap->msr = PCAP_MASK_ALL_INTERRUPT;
+
+	irq_set_irq_type(spi->irq, IRQ_TYPE_EDGE_RISING);
+	irq_set_handler_data(spi->irq, pcap);
+	irq_set_chained_handler(spi->irq, pcap_irq_handler);
+	irq_set_irq_wake(spi->irq, 1);
+
+	/* ADC */
+	adc_irq = pcap_to_irq(pcap, (pdata->config & PCAP_SECOND_PORT) ?
+					PCAP_IRQ_ADCDONE2 : PCAP_IRQ_ADCDONE);
+
+	ret = request_irq(adc_irq, pcap_adc_irq, 0, "ADC", pcap);
+	if (ret)
+		goto free_irqchip;
+
+	/* setup subdevs */
+	for (i = 0; i < pdata->num_subdevs; i++) {
+		ret = pcap_add_subdev(pcap, &pdata->subdevs[i]);
+		if (ret)
+			goto remove_subdevs;
+	}
+
+	/* board specific quirks */
+	if (pdata->init)
+		pdata->init(pcap);
+
+	return 0;
+
+remove_subdevs:
+	device_for_each_child(&spi->dev, NULL, pcap_remove_subdev);
+/* free_adc: */
+	free_irq(adc_irq, pcap);
+free_irqchip:
+	for (i = pcap->irq_base; i < (pcap->irq_base + PCAP_NIRQS); i++)
+		irq_set_chip_and_handler(i, NULL, NULL);
+/* destroy_workqueue: */
+	destroy_workqueue(pcap->workqueue);
+free_pcap:
+	kfree(pcap);
+ret:
+	return ret;
+}
+
+static struct spi_driver ezxpcap_driver = {
+	.probe	= ezx_pcap_probe,
+	.remove = __devexit_p(ezx_pcap_remove),
+	.driver = {
+		.name	= "ezx-pcap",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init ezx_pcap_init(void)
+{
+	return spi_register_driver(&ezxpcap_driver);
+}
+
+static void __exit ezx_pcap_exit(void)
+{
+	spi_unregister_driver(&ezxpcap_driver);
+}
+
+subsys_initcall(ezx_pcap_init);
+module_exit(ezx_pcap_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Daniel Ribeiro / Harald Welte");
+MODULE_DESCRIPTION("Motorola PCAP2 ASIC Driver");
+MODULE_ALIAS("spi:ezx-pcap");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/htc-egpio.c b/ap/os/linux/linux-3.4.x/drivers/mfd/htc-egpio.c
new file mode 100644
index 0000000..bbaec0c
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/htc-egpio.c
@@ -0,0 +1,441 @@
+/*
+ * Support for the GPIO/IRQ expander chips present on several HTC phones.
+ * These are implemented in CPLD chips present on the board.
+ *
+ * Copyright (c) 2007 Kevin O'Connor <kevin@koconnor.net>
+ * Copyright (c) 2007 Philipp Zabel <philipp.zabel@gmail.com>
+ *
+ * This file may be distributed under the terms of the GNU GPL license.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/mfd/htc-egpio.h>
+
+struct egpio_chip {
+	int              reg_start;
+	int              cached_values;
+	unsigned long    is_out;
+	struct device    *dev;
+	struct gpio_chip chip;
+};
+
+struct egpio_info {
+	spinlock_t        lock;
+
+	/* iomem info */
+	void __iomem      *base_addr;
+	int               bus_shift;	/* byte shift */
+	int               reg_shift;	/* bit shift */
+	int               reg_mask;
+
+	/* irq info */
+	int               ack_register;
+	int               ack_write;
+	u16               irqs_enabled;
+	uint              irq_start;
+	int               nirqs;
+	uint              chained_irq;
+
+	/* egpio info */
+	struct egpio_chip *chip;
+	int               nchips;
+};
+
+static inline void egpio_writew(u16 value, struct egpio_info *ei, int reg)
+{
+	writew(value, ei->base_addr + (reg << ei->bus_shift));
+}
+
+static inline u16 egpio_readw(struct egpio_info *ei, int reg)
+{
+	return readw(ei->base_addr + (reg << ei->bus_shift));
+}
+
+/*
+ * IRQs
+ */
+
+static inline void ack_irqs(struct egpio_info *ei)
+{
+	egpio_writew(ei->ack_write, ei, ei->ack_register);
+	pr_debug("EGPIO ack - write %x to base+%x\n",
+			ei->ack_write, ei->ack_register << ei->bus_shift);
+}
+
+static void egpio_ack(struct irq_data *data)
+{
+}
+
+/* There does not appear to be a way to proactively mask interrupts
+ * on the egpio chip itself.  So, we simply ignore interrupts that
+ * aren't desired. */
+static void egpio_mask(struct irq_data *data)
+{
+	struct egpio_info *ei = irq_data_get_irq_chip_data(data);
+	ei->irqs_enabled &= ~(1 << (data->irq - ei->irq_start));
+	pr_debug("EGPIO mask %d %04x\n", data->irq, ei->irqs_enabled);
+}
+
+static void egpio_unmask(struct irq_data *data)
+{
+	struct egpio_info *ei = irq_data_get_irq_chip_data(data);
+	ei->irqs_enabled |= 1 << (data->irq - ei->irq_start);
+	pr_debug("EGPIO unmask %d %04x\n", data->irq, ei->irqs_enabled);
+}
+
+static struct irq_chip egpio_muxed_chip = {
+	.name		= "htc-egpio",
+	.irq_ack	= egpio_ack,
+	.irq_mask	= egpio_mask,
+	.irq_unmask	= egpio_unmask,
+};
+
+static void egpio_handler(unsigned int irq, struct irq_desc *desc)
+{
+	struct egpio_info *ei = irq_desc_get_handler_data(desc);
+	int irqpin;
+
+	/* Read current pins. */
+	unsigned long readval = egpio_readw(ei, ei->ack_register);
+	pr_debug("IRQ reg: %x\n", (unsigned int)readval);
+	/* Ack/unmask interrupts. */
+	ack_irqs(ei);
+	/* Process all set pins. */
+	readval &= ei->irqs_enabled;
+	for_each_set_bit(irqpin, &readval, ei->nirqs) {
+		/* Run irq handler */
+		pr_debug("got IRQ %d\n", irqpin);
+		generic_handle_irq(ei->irq_start + irqpin);
+	}
+}
+
+int htc_egpio_get_wakeup_irq(struct device *dev)
+{
+	struct egpio_info *ei = dev_get_drvdata(dev);
+
+	/* Read current pins. */
+	u16 readval = egpio_readw(ei, ei->ack_register);
+	/* Ack/unmask interrupts. */
+	ack_irqs(ei);
+	/* Return first set pin. */
+	readval &= ei->irqs_enabled;
+	return ei->irq_start + ffs(readval) - 1;
+}
+EXPORT_SYMBOL(htc_egpio_get_wakeup_irq);
+
+static inline int egpio_pos(struct egpio_info *ei, int bit)
+{
+	return bit >> ei->reg_shift;
+}
+
+static inline int egpio_bit(struct egpio_info *ei, int bit)
+{
+	return 1 << (bit & ((1 << ei->reg_shift)-1));
+}
+
+/*
+ * Input pins
+ */
+
+static int egpio_get(struct gpio_chip *chip, unsigned offset)
+{
+	struct egpio_chip *egpio;
+	struct egpio_info *ei;
+	unsigned           bit;
+	int                reg;
+	int                value;
+
+	pr_debug("egpio_get_value(%d)\n", chip->base + offset);
+
+	egpio = container_of(chip, struct egpio_chip, chip);
+	ei    = dev_get_drvdata(egpio->dev);
+	bit   = egpio_bit(ei, offset);
+	reg   = egpio->reg_start + egpio_pos(ei, offset);
+
+	value = egpio_readw(ei, reg);
+	pr_debug("readw(%p + %x) = %x\n",
+			ei->base_addr, reg << ei->bus_shift, value);
+	return value & bit;
+}
+
+static int egpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+	struct egpio_chip *egpio;
+
+	egpio = container_of(chip, struct egpio_chip, chip);
+	return test_bit(offset, &egpio->is_out) ? -EINVAL : 0;
+}
+
+
+/*
+ * Output pins
+ */
+
+static void egpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+	unsigned long     flag;
+	struct egpio_chip *egpio;
+	struct egpio_info *ei;
+	unsigned          bit;
+	int               pos;
+	int               reg;
+	int               shift;
+
+	pr_debug("egpio_set(%s, %d(%d), %d)\n",
+			chip->label, offset, offset+chip->base, value);
+
+	egpio = container_of(chip, struct egpio_chip, chip);
+	ei    = dev_get_drvdata(egpio->dev);
+	bit   = egpio_bit(ei, offset);
+	pos   = egpio_pos(ei, offset);
+	reg   = egpio->reg_start + pos;
+	shift = pos << ei->reg_shift;
+
+	pr_debug("egpio %s: reg %d = 0x%04x\n", value ? "set" : "clear",
+			reg, (egpio->cached_values >> shift) & ei->reg_mask);
+
+	spin_lock_irqsave(&ei->lock, flag);
+	if (value)
+		egpio->cached_values |= (1 << offset);
+	else
+		egpio->cached_values &= ~(1 << offset);
+	egpio_writew((egpio->cached_values >> shift) & ei->reg_mask, ei, reg);
+	spin_unlock_irqrestore(&ei->lock, flag);
+}
+
+static int egpio_direction_output(struct gpio_chip *chip,
+					unsigned offset, int value)
+{
+	struct egpio_chip *egpio;
+
+	egpio = container_of(chip, struct egpio_chip, chip);
+	if (test_bit(offset, &egpio->is_out)) {
+		egpio_set(chip, offset, value);
+		return 0;
+	} else {
+		return -EINVAL;
+	}
+}
+
+static void egpio_write_cache(struct egpio_info *ei)
+{
+	int               i;
+	struct egpio_chip *egpio;
+	int               shift;
+
+	for (i = 0; i < ei->nchips; i++) {
+		egpio = &(ei->chip[i]);
+		if (!egpio->is_out)
+			continue;
+
+		for (shift = 0; shift < egpio->chip.ngpio;
+				shift += (1<<ei->reg_shift)) {
+
+			int reg = egpio->reg_start + egpio_pos(ei, shift);
+
+			if (!((egpio->is_out >> shift) & ei->reg_mask))
+				continue;
+
+			pr_debug("EGPIO: setting %x to %x, was %x\n", reg,
+				(egpio->cached_values >> shift) & ei->reg_mask,
+				egpio_readw(ei, reg));
+
+			egpio_writew((egpio->cached_values >> shift)
+					& ei->reg_mask, ei, reg);
+		}
+	}
+}
+
+
+/*
+ * Setup
+ */
+
+static int __init egpio_probe(struct platform_device *pdev)
+{
+	struct htc_egpio_platform_data *pdata = pdev->dev.platform_data;
+	struct resource   *res;
+	struct egpio_info *ei;
+	struct gpio_chip  *chip;
+	unsigned int      irq, irq_end;
+	int               i;
+	int               ret;
+
+	/* Initialize ei data structure. */
+	ei = kzalloc(sizeof(*ei), GFP_KERNEL);
+	if (!ei)
+		return -ENOMEM;
+
+	spin_lock_init(&ei->lock);
+
+	/* Find chained irq */
+	ret = -EINVAL;
+	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (res)
+		ei->chained_irq = res->start;
+
+	/* Map egpio chip into virtual address space. */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		goto fail;
+	ei->base_addr = ioremap_nocache(res->start, resource_size(res));
+	if (!ei->base_addr)
+		goto fail;
+	pr_debug("EGPIO phys=%08x virt=%p\n", (u32)res->start, ei->base_addr);
+
+	if ((pdata->bus_width != 16) && (pdata->bus_width != 32))
+		goto fail;
+	ei->bus_shift = fls(pdata->bus_width - 1) - 3;
+	pr_debug("bus_shift = %d\n", ei->bus_shift);
+
+	if ((pdata->reg_width != 8) && (pdata->reg_width != 16))
+		goto fail;
+	ei->reg_shift = fls(pdata->reg_width - 1);
+	pr_debug("reg_shift = %d\n", ei->reg_shift);
+
+	ei->reg_mask = (1 << pdata->reg_width) - 1;
+
+	platform_set_drvdata(pdev, ei);
+
+	ei->nchips = pdata->num_chips;
+	ei->chip = kzalloc(sizeof(struct egpio_chip) * ei->nchips, GFP_KERNEL);
+	if (!ei->chip) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+	for (i = 0; i < ei->nchips; i++) {
+		ei->chip[i].reg_start = pdata->chip[i].reg_start;
+		ei->chip[i].cached_values = pdata->chip[i].initial_values;
+		ei->chip[i].is_out = pdata->chip[i].direction;
+		ei->chip[i].dev = &(pdev->dev);
+		chip = &(ei->chip[i].chip);
+		chip->label           = "htc-egpio";
+		chip->dev             = &pdev->dev;
+		chip->owner           = THIS_MODULE;
+		chip->get             = egpio_get;
+		chip->set             = egpio_set;
+		chip->direction_input = egpio_direction_input;
+		chip->direction_output = egpio_direction_output;
+		chip->base            = pdata->chip[i].gpio_base;
+		chip->ngpio           = pdata->chip[i].num_gpios;
+
+		gpiochip_add(chip);
+	}
+
+	/* Set initial pin values */
+	egpio_write_cache(ei);
+
+	ei->irq_start = pdata->irq_base;
+	ei->nirqs = pdata->num_irqs;
+	ei->ack_register = pdata->ack_register;
+
+	if (ei->chained_irq) {
+		/* Setup irq handlers */
+		ei->ack_write = 0xFFFF;
+		if (pdata->invert_acks)
+			ei->ack_write = 0;
+		irq_end = ei->irq_start + ei->nirqs;
+		for (irq = ei->irq_start; irq < irq_end; irq++) {
+			irq_set_chip_and_handler(irq, &egpio_muxed_chip,
+						 handle_simple_irq);
+			irq_set_chip_data(irq, ei);
+			set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+		}
+		irq_set_irq_type(ei->chained_irq, IRQ_TYPE_EDGE_RISING);
+		irq_set_handler_data(ei->chained_irq, ei);
+		irq_set_chained_handler(ei->chained_irq, egpio_handler);
+		ack_irqs(ei);
+
+		device_init_wakeup(&pdev->dev, 1);
+	}
+
+	return 0;
+
+fail:
+	printk(KERN_ERR "EGPIO failed to setup\n");
+	kfree(ei);
+	return ret;
+}
+
+static int __exit egpio_remove(struct platform_device *pdev)
+{
+	struct egpio_info *ei = platform_get_drvdata(pdev);
+	unsigned int      irq, irq_end;
+
+	if (ei->chained_irq) {
+		irq_end = ei->irq_start + ei->nirqs;
+		for (irq = ei->irq_start; irq < irq_end; irq++) {
+			irq_set_chip_and_handler(irq, NULL, NULL);
+			set_irq_flags(irq, 0);
+		}
+		irq_set_chained_handler(ei->chained_irq, NULL);
+		device_init_wakeup(&pdev->dev, 0);
+	}
+	iounmap(ei->base_addr);
+	kfree(ei->chip);
+	kfree(ei);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int egpio_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct egpio_info *ei = platform_get_drvdata(pdev);
+
+	if (ei->chained_irq && device_may_wakeup(&pdev->dev))
+		enable_irq_wake(ei->chained_irq);
+	return 0;
+}
+
+static int egpio_resume(struct platform_device *pdev)
+{
+	struct egpio_info *ei = platform_get_drvdata(pdev);
+
+	if (ei->chained_irq && device_may_wakeup(&pdev->dev))
+		disable_irq_wake(ei->chained_irq);
+
+	/* Update registers from the cache, in case
+	   the CPLD was powered off during suspend */
+	egpio_write_cache(ei);
+	return 0;
+}
+#else
+#define egpio_suspend NULL
+#define egpio_resume NULL
+#endif
+
+
+static struct platform_driver egpio_driver = {
+	.driver = {
+		.name = "htc-egpio",
+	},
+	.remove       = __exit_p(egpio_remove),
+	.suspend      = egpio_suspend,
+	.resume       = egpio_resume,
+};
+
+static int __init egpio_init(void)
+{
+	return platform_driver_probe(&egpio_driver, egpio_probe);
+}
+
+static void __exit egpio_exit(void)
+{
+	platform_driver_unregister(&egpio_driver);
+}
+
+/* start early for dependencies */
+subsys_initcall(egpio_init);
+module_exit(egpio_exit)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Kevin O'Connor <kevin@koconnor.net>");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/htc-i2cpld.c b/ap/os/linux/linux-3.4.x/drivers/mfd/htc-i2cpld.c
new file mode 100644
index 0000000..d55065c
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/htc-i2cpld.c
@@ -0,0 +1,700 @@
+/*
+ *  htc-i2cpld.c
+ *  Chip driver for an unknown CPLD chip found on omap850 HTC devices like
+ *  the HTC Wizard and HTC Herald.
+ *  The cpld is located on the i2c bus and acts as an input/output GPIO
+ *  extender.
+ *
+ *  Copyright (C) 2009 Cory Maccarrone <darkstar6262@gmail.com>
+ *
+ *  Based on work done in the linwizard project
+ *  Copyright (C) 2008-2009 Angelo Arrifano <miknix@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/spinlock.h>
+#include <linux/htcpld.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+
+struct htcpld_chip {
+	spinlock_t              lock;
+
+	/* chip info */
+	u8                      reset;
+	u8                      addr;
+	struct device           *dev;
+	struct i2c_client	*client;
+
+	/* Output details */
+	u8                      cache_out;
+	struct gpio_chip        chip_out;
+
+	/* Input details */
+	u8                      cache_in;
+	struct gpio_chip        chip_in;
+
+	u16                     irqs_enabled;
+	uint                    irq_start;
+	int                     nirqs;
+
+	unsigned int		flow_type;
+	/*
+	 * Work structure to allow for setting values outside of any
+	 * possible interrupt context
+	 */
+	struct work_struct set_val_work;
+};
+
+struct htcpld_data {
+	/* irq info */
+	u16                irqs_enabled;
+	uint               irq_start;
+	int                nirqs;
+	uint               chained_irq;
+	unsigned int       int_reset_gpio_hi;
+	unsigned int       int_reset_gpio_lo;
+
+	/* htcpld info */
+	struct htcpld_chip *chip;
+	unsigned int       nchips;
+};
+
+/* There does not appear to be a way to proactively mask interrupts
+ * on the htcpld chip itself.  So, we simply ignore interrupts that
+ * aren't desired. */
+static void htcpld_mask(struct irq_data *data)
+{
+	struct htcpld_chip *chip = irq_data_get_irq_chip_data(data);
+	chip->irqs_enabled &= ~(1 << (data->irq - chip->irq_start));
+	pr_debug("HTCPLD mask %d %04x\n", data->irq, chip->irqs_enabled);
+}
+static void htcpld_unmask(struct irq_data *data)
+{
+	struct htcpld_chip *chip = irq_data_get_irq_chip_data(data);
+	chip->irqs_enabled |= 1 << (data->irq - chip->irq_start);
+	pr_debug("HTCPLD unmask %d %04x\n", data->irq, chip->irqs_enabled);
+}
+
+static int htcpld_set_type(struct irq_data *data, unsigned int flags)
+{
+	struct htcpld_chip *chip = irq_data_get_irq_chip_data(data);
+
+	if (flags & ~IRQ_TYPE_SENSE_MASK)
+		return -EINVAL;
+
+	/* We only allow edge triggering */
+	if (flags & (IRQ_TYPE_LEVEL_LOW|IRQ_TYPE_LEVEL_HIGH))
+		return -EINVAL;
+
+	chip->flow_type = flags;
+	return 0;
+}
+
+static struct irq_chip htcpld_muxed_chip = {
+	.name         = "htcpld",
+	.irq_mask     = htcpld_mask,
+	.irq_unmask   = htcpld_unmask,
+	.irq_set_type = htcpld_set_type,
+};
+
+/* To properly dispatch IRQ events, we need to read from the
+ * chip.  This is an I2C action that could possibly sleep
+ * (which is bad in interrupt context) -- so we use a threaded
+ * interrupt handler to get around that.
+ */
+static irqreturn_t htcpld_handler(int irq, void *dev)
+{
+	struct htcpld_data *htcpld = dev;
+	unsigned int i;
+	unsigned long flags;
+	int irqpin;
+
+	if (!htcpld) {
+		pr_debug("htcpld is null in ISR\n");
+		return IRQ_HANDLED;
+	}
+
+	/*
+	 * For each chip, do a read of the chip and trigger any interrupts
+	 * desired.  The interrupts will be triggered from LSB to MSB (i.e.
+	 * bit 0 first, then bit 1, etc.)
+	 *
+	 * For chips that have no interrupt range specified, just skip 'em.
+	 */
+	for (i = 0; i < htcpld->nchips; i++) {
+		struct htcpld_chip *chip = &htcpld->chip[i];
+		struct i2c_client *client;
+		int val;
+		unsigned long uval, old_val;
+
+		if (!chip) {
+			pr_debug("chip %d is null in ISR\n", i);
+			continue;
+		}
+
+		if (chip->nirqs == 0)
+			continue;
+
+		client = chip->client;
+		if (!client) {
+			pr_debug("client %d is null in ISR\n", i);
+			continue;
+		}
+
+		/* Scan the chip */
+		val = i2c_smbus_read_byte_data(client, chip->cache_out);
+		if (val < 0) {
+			/* Throw a warning and skip this chip */
+			dev_warn(chip->dev, "Unable to read from chip: %d\n",
+				 val);
+			continue;
+		}
+
+		uval = (unsigned long)val;
+
+		spin_lock_irqsave(&chip->lock, flags);
+
+		/* Save away the old value so we can compare it */
+		old_val = chip->cache_in;
+
+		/* Write the new value */
+		chip->cache_in = uval;
+
+		spin_unlock_irqrestore(&chip->lock, flags);
+
+		/*
+		 * For each bit in the data (starting at bit 0), trigger
+		 * associated interrupts.
+		 */
+		for (irqpin = 0; irqpin < chip->nirqs; irqpin++) {
+			unsigned oldb, newb, type = chip->flow_type;
+
+			irq = chip->irq_start + irqpin;
+
+			/* Run the IRQ handler, but only if the bit value
+			 * changed, and the proper flags are set */
+			oldb = (old_val >> irqpin) & 1;
+			newb = (uval >> irqpin) & 1;
+
+			if ((!oldb && newb && (type & IRQ_TYPE_EDGE_RISING)) ||
+			    (oldb && !newb && (type & IRQ_TYPE_EDGE_FALLING))) {
+				pr_debug("fire IRQ %d\n", irqpin);
+				generic_handle_irq(irq);
+			}
+		}
+	}
+
+	/*
+	 * In order to continue receiving interrupts, the int_reset_gpio must
+	 * be asserted.
+	 */
+	if (htcpld->int_reset_gpio_hi)
+		gpio_set_value(htcpld->int_reset_gpio_hi, 1);
+	if (htcpld->int_reset_gpio_lo)
+		gpio_set_value(htcpld->int_reset_gpio_lo, 0);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * The GPIO set routines can be called from interrupt context, especially if,
+ * for example they're attached to the led-gpio framework and a trigger is
+ * enabled.  As such, we declared work above in the htcpld_chip structure,
+ * and that work is scheduled in the set routine.  The kernel can then run
+ * the I2C functions, which will sleep, in process context.
+ */
+static void htcpld_chip_set(struct gpio_chip *chip, unsigned offset, int val)
+{
+	struct i2c_client *client;
+	struct htcpld_chip *chip_data;
+	unsigned long flags;
+
+	chip_data = container_of(chip, struct htcpld_chip, chip_out);
+	if (!chip_data)
+		return;
+
+	client = chip_data->client;
+	if (client == NULL)
+		return;
+
+	spin_lock_irqsave(&chip_data->lock, flags);
+	if (val)
+		chip_data->cache_out |= (1 << offset);
+	else
+		chip_data->cache_out &= ~(1 << offset);
+	spin_unlock_irqrestore(&chip_data->lock, flags);
+
+	schedule_work(&(chip_data->set_val_work));
+}
+
+static void htcpld_chip_set_ni(struct work_struct *work)
+{
+	struct htcpld_chip *chip_data;
+	struct i2c_client *client;
+
+	chip_data = container_of(work, struct htcpld_chip, set_val_work);
+	client = chip_data->client;
+	i2c_smbus_read_byte_data(client, chip_data->cache_out);
+}
+
+static int htcpld_chip_get(struct gpio_chip *chip, unsigned offset)
+{
+	struct htcpld_chip *chip_data;
+	int val = 0;
+	int is_input = 0;
+
+	/* Try out first */
+	chip_data = container_of(chip, struct htcpld_chip, chip_out);
+	if (!chip_data) {
+		/* Try in */
+		is_input = 1;
+		chip_data = container_of(chip, struct htcpld_chip, chip_in);
+		if (!chip_data)
+			return -EINVAL;
+	}
+
+	/* Determine if this is an input or output GPIO */
+	if (!is_input)
+		/* Use the output cache */
+		val = (chip_data->cache_out >> offset) & 1;
+	else
+		/* Use the input cache */
+		val = (chip_data->cache_in >> offset) & 1;
+
+	if (val)
+		return 1;
+	else
+		return 0;
+}
+
+static int htcpld_direction_output(struct gpio_chip *chip,
+					unsigned offset, int value)
+{
+	htcpld_chip_set(chip, offset, value);
+	return 0;
+}
+
+static int htcpld_direction_input(struct gpio_chip *chip,
+					unsigned offset)
+{
+	/*
+	 * No-op: this function can only be called on the input chip.
+	 * We do however make sure the offset is within range.
+	 */
+	return (offset < chip->ngpio) ? 0 : -EINVAL;
+}
+
+static int htcpld_chip_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+	struct htcpld_chip *chip_data;
+
+	chip_data = container_of(chip, struct htcpld_chip, chip_in);
+
+	if (offset < chip_data->nirqs)
+		return chip_data->irq_start + offset;
+	else
+		return -EINVAL;
+}
+
+static void htcpld_chip_reset(struct i2c_client *client)
+{
+	struct htcpld_chip *chip_data = i2c_get_clientdata(client);
+	if (!chip_data)
+		return;
+
+	i2c_smbus_read_byte_data(
+		client, (chip_data->cache_out = chip_data->reset));
+}
+
+static int __devinit htcpld_setup_chip_irq(
+		struct platform_device *pdev,
+		int chip_index)
+{
+	struct htcpld_data *htcpld;
+	struct device *dev = &pdev->dev;
+	struct htcpld_core_platform_data *pdata;
+	struct htcpld_chip *chip;
+	struct htcpld_chip_platform_data *plat_chip_data;
+	unsigned int irq, irq_end;
+	int ret = 0;
+
+	/* Get the platform and driver data */
+	pdata = dev->platform_data;
+	htcpld = platform_get_drvdata(pdev);
+	chip = &htcpld->chip[chip_index];
+	plat_chip_data = &pdata->chip[chip_index];
+
+	/* Setup irq handlers */
+	irq_end = chip->irq_start + chip->nirqs;
+	for (irq = chip->irq_start; irq < irq_end; irq++) {
+		irq_set_chip_and_handler(irq, &htcpld_muxed_chip,
+					 handle_simple_irq);
+		irq_set_chip_data(irq, chip);
+#ifdef CONFIG_ARM
+		set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+#else
+		irq_set_probe(irq);
+#endif
+	}
+
+	return ret;
+}
+
+static int __devinit htcpld_register_chip_i2c(
+		struct platform_device *pdev,
+		int chip_index)
+{
+	struct htcpld_data *htcpld;
+	struct device *dev = &pdev->dev;
+	struct htcpld_core_platform_data *pdata;
+	struct htcpld_chip *chip;
+	struct htcpld_chip_platform_data *plat_chip_data;
+	struct i2c_adapter *adapter;
+	struct i2c_client *client;
+	struct i2c_board_info info;
+
+	/* Get the platform and driver data */
+	pdata = dev->platform_data;
+	htcpld = platform_get_drvdata(pdev);
+	chip = &htcpld->chip[chip_index];
+	plat_chip_data = &pdata->chip[chip_index];
+
+	adapter = i2c_get_adapter(pdata->i2c_adapter_id);
+	if (adapter == NULL) {
+		/* Eek, no such I2C adapter!  Bail out. */
+		dev_warn(dev, "Chip at i2c address 0x%x: Invalid i2c adapter %d\n",
+			 plat_chip_data->addr, pdata->i2c_adapter_id);
+		return -ENODEV;
+	}
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA)) {
+		dev_warn(dev, "i2c adapter %d non-functional\n",
+			 pdata->i2c_adapter_id);
+		return -EINVAL;
+	}
+
+	memset(&info, 0, sizeof(struct i2c_board_info));
+	info.addr = plat_chip_data->addr;
+	strlcpy(info.type, "htcpld-chip", I2C_NAME_SIZE);
+	info.platform_data = chip;
+
+	/* Add the I2C device.  This calls the probe() function. */
+	client = i2c_new_device(adapter, &info);
+	if (!client) {
+		/* I2C device registration failed, contineu with the next */
+		dev_warn(dev, "Unable to add I2C device for 0x%x\n",
+			 plat_chip_data->addr);
+		return -ENODEV;
+	}
+
+	i2c_set_clientdata(client, chip);
+	snprintf(client->name, I2C_NAME_SIZE, "Chip_0x%d", client->addr);
+	chip->client = client;
+
+	/* Reset the chip */
+	htcpld_chip_reset(client);
+	chip->cache_in = i2c_smbus_read_byte_data(client, chip->cache_out);
+
+	return 0;
+}
+
+static void __devinit htcpld_unregister_chip_i2c(
+		struct platform_device *pdev,
+		int chip_index)
+{
+	struct htcpld_data *htcpld;
+	struct htcpld_chip *chip;
+
+	/* Get the platform and driver data */
+	htcpld = platform_get_drvdata(pdev);
+	chip = &htcpld->chip[chip_index];
+
+	if (chip->client)
+		i2c_unregister_device(chip->client);
+}
+
+static int __devinit htcpld_register_chip_gpio(
+		struct platform_device *pdev,
+		int chip_index)
+{
+	struct htcpld_data *htcpld;
+	struct device *dev = &pdev->dev;
+	struct htcpld_core_platform_data *pdata;
+	struct htcpld_chip *chip;
+	struct htcpld_chip_platform_data *plat_chip_data;
+	struct gpio_chip *gpio_chip;
+	int ret = 0;
+
+	/* Get the platform and driver data */
+	pdata = dev->platform_data;
+	htcpld = platform_get_drvdata(pdev);
+	chip = &htcpld->chip[chip_index];
+	plat_chip_data = &pdata->chip[chip_index];
+
+	/* Setup the GPIO chips */
+	gpio_chip = &(chip->chip_out);
+	gpio_chip->label           = "htcpld-out";
+	gpio_chip->dev             = dev;
+	gpio_chip->owner           = THIS_MODULE;
+	gpio_chip->get             = htcpld_chip_get;
+	gpio_chip->set             = htcpld_chip_set;
+	gpio_chip->direction_input = NULL;
+	gpio_chip->direction_output = htcpld_direction_output;
+	gpio_chip->base            = plat_chip_data->gpio_out_base;
+	gpio_chip->ngpio           = plat_chip_data->num_gpios;
+
+	gpio_chip = &(chip->chip_in);
+	gpio_chip->label           = "htcpld-in";
+	gpio_chip->dev             = dev;
+	gpio_chip->owner           = THIS_MODULE;
+	gpio_chip->get             = htcpld_chip_get;
+	gpio_chip->set             = NULL;
+	gpio_chip->direction_input = htcpld_direction_input;
+	gpio_chip->direction_output = NULL;
+	gpio_chip->to_irq          = htcpld_chip_to_irq;
+	gpio_chip->base            = plat_chip_data->gpio_in_base;
+	gpio_chip->ngpio           = plat_chip_data->num_gpios;
+
+	/* Add the GPIO chips */
+	ret = gpiochip_add(&(chip->chip_out));
+	if (ret) {
+		dev_warn(dev, "Unable to register output GPIOs for 0x%x: %d\n",
+			 plat_chip_data->addr, ret);
+		return ret;
+	}
+
+	ret = gpiochip_add(&(chip->chip_in));
+	if (ret) {
+		int error;
+
+		dev_warn(dev, "Unable to register input GPIOs for 0x%x: %d\n",
+			 plat_chip_data->addr, ret);
+
+		error = gpiochip_remove(&(chip->chip_out));
+		if (error)
+			dev_warn(dev, "Error while trying to unregister gpio chip: %d\n", error);
+
+		return ret;
+	}
+
+	return 0;
+}
+
+static int __devinit htcpld_setup_chips(struct platform_device *pdev)
+{
+	struct htcpld_data *htcpld;
+	struct device *dev = &pdev->dev;
+	struct htcpld_core_platform_data *pdata;
+	int i;
+
+	/* Get the platform and driver data */
+	pdata = dev->platform_data;
+	htcpld = platform_get_drvdata(pdev);
+
+	/* Setup each chip's output GPIOs */
+	htcpld->nchips = pdata->num_chip;
+	htcpld->chip = kzalloc(sizeof(struct htcpld_chip) * htcpld->nchips,
+			       GFP_KERNEL);
+	if (!htcpld->chip) {
+		dev_warn(dev, "Unable to allocate memory for chips\n");
+		return -ENOMEM;
+	}
+
+	/* Add the chips as best we can */
+	for (i = 0; i < htcpld->nchips; i++) {
+		int ret;
+
+		/* Setup the HTCPLD chips */
+		htcpld->chip[i].reset = pdata->chip[i].reset;
+		htcpld->chip[i].cache_out = pdata->chip[i].reset;
+		htcpld->chip[i].cache_in = 0;
+		htcpld->chip[i].dev = dev;
+		htcpld->chip[i].irq_start = pdata->chip[i].irq_base;
+		htcpld->chip[i].nirqs = pdata->chip[i].num_irqs;
+
+		INIT_WORK(&(htcpld->chip[i].set_val_work), &htcpld_chip_set_ni);
+		spin_lock_init(&(htcpld->chip[i].lock));
+
+		/* Setup the interrupts for the chip */
+		if (htcpld->chained_irq) {
+			ret = htcpld_setup_chip_irq(pdev, i);
+			if (ret)
+				continue;
+		}
+
+		/* Register the chip with I2C */
+		ret = htcpld_register_chip_i2c(pdev, i);
+		if (ret)
+			continue;
+
+
+		/* Register the chips with the GPIO subsystem */
+		ret = htcpld_register_chip_gpio(pdev, i);
+		if (ret) {
+			/* Unregister the chip from i2c and continue */
+			htcpld_unregister_chip_i2c(pdev, i);
+			continue;
+		}
+
+		dev_info(dev, "Registered chip at 0x%x\n", pdata->chip[i].addr);
+	}
+
+	return 0;
+}
+
+static int __devinit htcpld_core_probe(struct platform_device *pdev)
+{
+	struct htcpld_data *htcpld;
+	struct device *dev = &pdev->dev;
+	struct htcpld_core_platform_data *pdata;
+	struct resource *res;
+	int ret = 0;
+
+	if (!dev)
+		return -ENODEV;
+
+	pdata = dev->platform_data;
+	if (!pdata) {
+		dev_warn(dev, "Platform data not found for htcpld core!\n");
+		return -ENXIO;
+	}
+
+	htcpld = kzalloc(sizeof(struct htcpld_data), GFP_KERNEL);
+	if (!htcpld)
+		return -ENOMEM;
+
+	/* Find chained irq */
+	ret = -EINVAL;
+	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (res) {
+		int flags;
+		htcpld->chained_irq = res->start;
+
+		/* Setup the chained interrupt handler */
+		flags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING;
+		ret = request_threaded_irq(htcpld->chained_irq,
+					   NULL, htcpld_handler,
+					   flags, pdev->name, htcpld);
+		if (ret) {
+			dev_warn(dev, "Unable to setup chained irq handler: %d\n", ret);
+			goto fail;
+		} else
+			device_init_wakeup(dev, 0);
+	}
+
+	/* Set the driver data */
+	platform_set_drvdata(pdev, htcpld);
+
+	/* Setup the htcpld chips */
+	ret = htcpld_setup_chips(pdev);
+	if (ret)
+		goto fail;
+
+	/* Request the GPIO(s) for the int reset and set them up */
+	if (pdata->int_reset_gpio_hi) {
+		ret = gpio_request(pdata->int_reset_gpio_hi, "htcpld-core");
+		if (ret) {
+			/*
+			 * If it failed, that sucks, but we can probably
+			 * continue on without it.
+			 */
+			dev_warn(dev, "Unable to request int_reset_gpio_hi -- interrupts may not work\n");
+			htcpld->int_reset_gpio_hi = 0;
+		} else {
+			htcpld->int_reset_gpio_hi = pdata->int_reset_gpio_hi;
+			gpio_set_value(htcpld->int_reset_gpio_hi, 1);
+		}
+	}
+
+	if (pdata->int_reset_gpio_lo) {
+		ret = gpio_request(pdata->int_reset_gpio_lo, "htcpld-core");
+		if (ret) {
+			/*
+			 * If it failed, that sucks, but we can probably
+			 * continue on without it.
+			 */
+			dev_warn(dev, "Unable to request int_reset_gpio_lo -- interrupts may not work\n");
+			htcpld->int_reset_gpio_lo = 0;
+		} else {
+			htcpld->int_reset_gpio_lo = pdata->int_reset_gpio_lo;
+			gpio_set_value(htcpld->int_reset_gpio_lo, 0);
+		}
+	}
+
+	dev_info(dev, "Initialized successfully\n");
+	return 0;
+
+fail:
+	kfree(htcpld);
+	return ret;
+}
+
+/* The I2C Driver -- used internally */
+static const struct i2c_device_id htcpld_chip_id[] = {
+	{ "htcpld-chip", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, htcpld_chip_id);
+
+
+static struct i2c_driver htcpld_chip_driver = {
+	.driver = {
+		.name	= "htcpld-chip",
+	},
+	.id_table = htcpld_chip_id,
+};
+
+/* The Core Driver */
+static struct platform_driver htcpld_core_driver = {
+	.driver = {
+		.name = "i2c-htcpld",
+	},
+};
+
+static int __init htcpld_core_init(void)
+{
+	int ret;
+
+	/* Register the I2C Chip driver */
+	ret = i2c_add_driver(&htcpld_chip_driver);
+	if (ret)
+		return ret;
+
+	/* Probe for our chips */
+	return platform_driver_probe(&htcpld_core_driver, htcpld_core_probe);
+}
+
+static void __exit htcpld_core_exit(void)
+{
+	i2c_del_driver(&htcpld_chip_driver);
+	platform_driver_unregister(&htcpld_core_driver);
+}
+
+module_init(htcpld_core_init);
+module_exit(htcpld_core_exit);
+
+MODULE_AUTHOR("Cory Maccarrone <darkstar6262@gmail.com>");
+MODULE_DESCRIPTION("I2C HTC PLD Driver");
+MODULE_LICENSE("GPL");
+
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/htc-pasic3.c b/ap/os/linux/linux-3.4.x/drivers/mfd/htc-pasic3.c
new file mode 100644
index 0000000..04c7093
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/htc-pasic3.c
@@ -0,0 +1,225 @@
+/*
+ * Core driver for HTC PASIC3 LED/DS1WM chip.
+ *
+ * Copyright (C) 2006 Philipp Zabel <philipp.zabel@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/ds1wm.h>
+#include <linux/mfd/htc-pasic3.h>
+#include <linux/slab.h>
+
+struct pasic3_data {
+	void __iomem *mapping;
+	unsigned int bus_shift;
+};
+
+#define REG_ADDR  5
+#define REG_DATA  6
+
+#define READ_MODE 0x80
+
+/*
+ * write to a secondary register on the PASIC3
+ */
+void pasic3_write_register(struct device *dev, u32 reg, u8 val)
+{
+	struct pasic3_data *asic = dev_get_drvdata(dev);
+	int bus_shift = asic->bus_shift;
+	void __iomem *addr = asic->mapping + (REG_ADDR << bus_shift);
+	void __iomem *data = asic->mapping + (REG_DATA << bus_shift);
+
+	__raw_writeb(~READ_MODE & reg, addr);
+	__raw_writeb(val, data);
+}
+EXPORT_SYMBOL(pasic3_write_register); /* for leds-pasic3 */
+
+/*
+ * read from a secondary register on the PASIC3
+ */
+u8 pasic3_read_register(struct device *dev, u32 reg)
+{
+	struct pasic3_data *asic = dev_get_drvdata(dev);
+	int bus_shift = asic->bus_shift;
+	void __iomem *addr = asic->mapping + (REG_ADDR << bus_shift);
+	void __iomem *data = asic->mapping + (REG_DATA << bus_shift);
+
+	__raw_writeb(READ_MODE | reg, addr);
+	return __raw_readb(data);
+}
+EXPORT_SYMBOL(pasic3_read_register); /* for leds-pasic3 */
+
+/*
+ * LEDs
+ */
+
+static struct mfd_cell led_cell __initdata = {
+	.name = "leds-pasic3",
+};
+
+/*
+ * DS1WM
+ */
+
+static int ds1wm_enable(struct platform_device *pdev)
+{
+	struct device *dev = pdev->dev.parent;
+	int c;
+
+	c = pasic3_read_register(dev, 0x28);
+	pasic3_write_register(dev, 0x28, c & 0x7f);
+
+	dev_dbg(dev, "DS1WM OWM_EN low (active) %02x\n", c & 0x7f);
+	return 0;
+}
+
+static int ds1wm_disable(struct platform_device *pdev)
+{
+	struct device *dev = pdev->dev.parent;
+	int c;
+
+	c = pasic3_read_register(dev, 0x28);
+	pasic3_write_register(dev, 0x28, c | 0x80);
+
+	dev_dbg(dev, "DS1WM OWM_EN high (inactive) %02x\n", c | 0x80);
+	return 0;
+}
+
+static struct ds1wm_driver_data ds1wm_pdata = {
+	.active_high = 0,
+	.reset_recover_delay = 1,
+};
+
+static struct resource ds1wm_resources[] __initdata = {
+	[0] = {
+		.start  = 0,
+		.flags  = IORESOURCE_MEM,
+	},
+	[1] = {
+		.start  = 0,
+		.end    = 0,
+		.flags  = IORESOURCE_IRQ,
+	},
+};
+
+static struct mfd_cell ds1wm_cell __initdata = {
+	.name          = "ds1wm",
+	.enable        = ds1wm_enable,
+	.disable       = ds1wm_disable,
+	.platform_data = &ds1wm_pdata,
+	.pdata_size    = sizeof(ds1wm_pdata),
+	.num_resources = 2,
+	.resources     = ds1wm_resources,
+};
+
+static int __init pasic3_probe(struct platform_device *pdev)
+{
+	struct pasic3_platform_data *pdata = pdev->dev.platform_data;
+	struct device *dev = &pdev->dev;
+	struct pasic3_data *asic;
+	struct resource *r;
+	int ret;
+	int irq = 0;
+
+	r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (r) {
+		ds1wm_resources[1].flags = IORESOURCE_IRQ | (r->flags &
+			(IORESOURCE_IRQ_HIGHEDGE | IORESOURCE_IRQ_LOWEDGE));
+		irq = r->start;
+	}
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!r)
+		return -ENXIO;
+
+	if (!request_mem_region(r->start, resource_size(r), "pasic3"))
+		return -EBUSY;
+
+	asic = kzalloc(sizeof(struct pasic3_data), GFP_KERNEL);
+	if (!asic)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, asic);
+
+	asic->mapping = ioremap(r->start, resource_size(r));
+	if (!asic->mapping) {
+		dev_err(dev, "couldn't ioremap PASIC3\n");
+		kfree(asic);
+		return -ENOMEM;
+	}
+
+	/* calculate bus shift from mem resource */
+	asic->bus_shift = (resource_size(r) - 5) >> 3;
+
+	if (pdata && pdata->clock_rate) {
+		ds1wm_pdata.clock_rate = pdata->clock_rate;
+		/* the first 5 PASIC3 registers control the DS1WM */
+		ds1wm_resources[0].end = (5 << asic->bus_shift) - 1;
+		ret = mfd_add_devices(&pdev->dev, pdev->id,
+				&ds1wm_cell, 1, r, irq);
+		if (ret < 0)
+			dev_warn(dev, "failed to register DS1WM\n");
+	}
+
+	if (pdata && pdata->led_pdata) {
+		led_cell.platform_data = pdata->led_pdata;
+		led_cell.pdata_size = sizeof(struct pasic3_leds_machinfo);
+		ret = mfd_add_devices(&pdev->dev, pdev->id, &led_cell, 1, r, 0);
+		if (ret < 0)
+			dev_warn(dev, "failed to register LED device\n");
+	}
+
+	return 0;
+}
+
+static int pasic3_remove(struct platform_device *pdev)
+{
+	struct pasic3_data *asic = platform_get_drvdata(pdev);
+	struct resource *r;
+
+	mfd_remove_devices(&pdev->dev);
+
+	iounmap(asic->mapping);
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	release_mem_region(r->start, resource_size(r));
+	kfree(asic);
+	return 0;
+}
+
+MODULE_ALIAS("platform:pasic3");
+
+static struct platform_driver pasic3_driver = {
+	.driver		= {
+		.name	= "pasic3",
+	},
+	.remove		= pasic3_remove,
+};
+
+static int __init pasic3_base_init(void)
+{
+	return platform_driver_probe(&pasic3_driver, pasic3_probe);
+}
+
+static void __exit pasic3_base_exit(void)
+{
+	platform_driver_unregister(&pasic3_driver);
+}
+
+module_init(pasic3_base_init);
+module_exit(pasic3_base_exit);
+
+MODULE_AUTHOR("Philipp Zabel <philipp.zabel@gmail.com>");
+MODULE_DESCRIPTION("Core driver for HTC PASIC3");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/intel_msic.c b/ap/os/linux/linux-3.4.x/drivers/mfd/intel_msic.c
new file mode 100644
index 0000000..b76657e
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/intel_msic.c
@@ -0,0 +1,492 @@
+/*
+ * Driver for Intel MSIC
+ *
+ * Copyright (C) 2011, Intel Corporation
+ * Author: Mika Westerberg <mika.westerberg@linux.intel.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/gpio.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/intel_msic.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <asm/intel_scu_ipc.h>
+
+#define MSIC_VENDOR(id)		((id >> 6) & 3)
+#define MSIC_VERSION(id)	(id & 0x3f)
+#define MSIC_MAJOR(id)		('A' + ((id >> 3) & 7))
+#define MSIC_MINOR(id)		(id & 7)
+
+/*
+ * MSIC interrupt tree is readable from SRAM at INTEL_MSIC_IRQ_PHYS_BASE.
+ * Since IRQ block starts from address 0x002 we need to substract that from
+ * the actual IRQ status register address.
+ */
+#define MSIC_IRQ_STATUS(x)	(INTEL_MSIC_IRQ_PHYS_BASE + ((x) - 2))
+#define MSIC_IRQ_STATUS_ACCDET	MSIC_IRQ_STATUS(INTEL_MSIC_ACCDET)
+
+/*
+ * The SCU hardware has limitation of 16 bytes per read/write buffer on
+ * Medfield.
+ */
+#define SCU_IPC_RWBUF_LIMIT	16
+
+/**
+ * struct intel_msic - an MSIC MFD instance
+ * @pdev: pointer to the platform device
+ * @vendor: vendor ID
+ * @version: chip version
+ * @irq_base: base address of the mapped MSIC SRAM interrupt tree
+ */
+struct intel_msic {
+	struct platform_device		*pdev;
+	unsigned			vendor;
+	unsigned			version;
+	void __iomem			*irq_base;
+};
+
+static struct resource msic_touch_resources[] = {
+	{
+		.flags		= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource msic_adc_resources[] = {
+	{
+		.flags		= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource msic_battery_resources[] = {
+	{
+		.flags		= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource msic_gpio_resources[] = {
+	{
+		.flags		= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource msic_audio_resources[] = {
+	{
+		.name		= "IRQ",
+		.flags		= IORESOURCE_IRQ,
+	},
+	/*
+	 * We will pass IRQ_BASE to the driver now but this can be removed
+	 * when/if the driver starts to use intel_msic_irq_read().
+	 */
+	{
+		.name		= "IRQ_BASE",
+		.flags		= IORESOURCE_MEM,
+		.start		= MSIC_IRQ_STATUS_ACCDET,
+		.end		= MSIC_IRQ_STATUS_ACCDET,
+	},
+};
+
+static struct resource msic_hdmi_resources[] = {
+	{
+		.flags		= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource msic_thermal_resources[] = {
+	{
+		.flags		= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource msic_power_btn_resources[] = {
+	{
+		.flags		= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource msic_ocd_resources[] = {
+	{
+		.flags		= IORESOURCE_IRQ,
+	},
+};
+
+/*
+ * Devices that are part of the MSIC and are available via firmware
+ * populated SFI DEVS table.
+ */
+static struct mfd_cell msic_devs[] = {
+	[INTEL_MSIC_BLOCK_TOUCH]	= {
+		.name			= "msic_touch",
+		.num_resources		= ARRAY_SIZE(msic_touch_resources),
+		.resources		= msic_touch_resources,
+	},
+	[INTEL_MSIC_BLOCK_ADC]		= {
+		.name			= "msic_adc",
+		.num_resources		= ARRAY_SIZE(msic_adc_resources),
+		.resources		= msic_adc_resources,
+	},
+	[INTEL_MSIC_BLOCK_BATTERY]	= {
+		.name			= "msic_battery",
+		.num_resources		= ARRAY_SIZE(msic_battery_resources),
+		.resources		= msic_battery_resources,
+	},
+	[INTEL_MSIC_BLOCK_GPIO]		= {
+		.name			= "msic_gpio",
+		.num_resources		= ARRAY_SIZE(msic_gpio_resources),
+		.resources		= msic_gpio_resources,
+	},
+	[INTEL_MSIC_BLOCK_AUDIO]	= {
+		.name			= "msic_audio",
+		.num_resources		= ARRAY_SIZE(msic_audio_resources),
+		.resources		= msic_audio_resources,
+	},
+	[INTEL_MSIC_BLOCK_HDMI]		= {
+		.name			= "msic_hdmi",
+		.num_resources		= ARRAY_SIZE(msic_hdmi_resources),
+		.resources		= msic_hdmi_resources,
+	},
+	[INTEL_MSIC_BLOCK_THERMAL]	= {
+		.name			= "msic_thermal",
+		.num_resources		= ARRAY_SIZE(msic_thermal_resources),
+		.resources		= msic_thermal_resources,
+	},
+	[INTEL_MSIC_BLOCK_POWER_BTN]	= {
+		.name			= "msic_power_btn",
+		.num_resources		= ARRAY_SIZE(msic_power_btn_resources),
+		.resources		= msic_power_btn_resources,
+	},
+	[INTEL_MSIC_BLOCK_OCD]		= {
+		.name			= "msic_ocd",
+		.num_resources		= ARRAY_SIZE(msic_ocd_resources),
+		.resources		= msic_ocd_resources,
+	},
+};
+
+/*
+ * Other MSIC related devices which are not directly available via SFI DEVS
+ * table. These can be pseudo devices, regulators etc. which are needed for
+ * different purposes.
+ *
+ * These devices appear only after the MSIC driver itself is initialized so
+ * we can guarantee that the SCU IPC interface is ready.
+ */
+static struct mfd_cell msic_other_devs[] = {
+	/* Audio codec in the MSIC */
+	{
+		.id			= -1,
+		.name			= "sn95031",
+	},
+};
+
+/**
+ * intel_msic_reg_read - read a single MSIC register
+ * @reg: register to read
+ * @val: register value is placed here
+ *
+ * Read a single register from MSIC. Returns %0 on success and negative
+ * errno in case of failure.
+ *
+ * Function may sleep.
+ */
+int intel_msic_reg_read(unsigned short reg, u8 *val)
+{
+	return intel_scu_ipc_ioread8(reg, val);
+}
+EXPORT_SYMBOL_GPL(intel_msic_reg_read);
+
+/**
+ * intel_msic_reg_write - write a single MSIC register
+ * @reg: register to write
+ * @val: value to write to that register
+ *
+ * Write a single MSIC register. Returns 0 on success and negative
+ * errno in case of failure.
+ *
+ * Function may sleep.
+ */
+int intel_msic_reg_write(unsigned short reg, u8 val)
+{
+	return intel_scu_ipc_iowrite8(reg, val);
+}
+EXPORT_SYMBOL_GPL(intel_msic_reg_write);
+
+/**
+ * intel_msic_reg_update - update a single MSIC register
+ * @reg: register to update
+ * @val: value to write to the register
+ * @mask: specifies which of the bits are updated (%0 = don't update,
+ *        %1 = update)
+ *
+ * Perform an update to a register @reg. @mask is used to specify which
+ * bits are updated. Returns %0 in case of success and negative errno in
+ * case of failure.
+ *
+ * Function may sleep.
+ */
+int intel_msic_reg_update(unsigned short reg, u8 val, u8 mask)
+{
+	return intel_scu_ipc_update_register(reg, val, mask);
+}
+EXPORT_SYMBOL_GPL(intel_msic_reg_update);
+
+/**
+ * intel_msic_bulk_read - read an array of registers
+ * @reg: array of register addresses to read
+ * @buf: array where the read values are placed
+ * @count: number of registers to read
+ *
+ * Function reads @count registers from the MSIC using addresses passed in
+ * @reg. Read values are placed in @buf. Reads are performed atomically
+ * wrt. MSIC.
+ *
+ * Returns %0 in case of success and negative errno in case of failure.
+ *
+ * Function may sleep.
+ */
+int intel_msic_bulk_read(unsigned short *reg, u8 *buf, size_t count)
+{
+	if (WARN_ON(count > SCU_IPC_RWBUF_LIMIT))
+		return -EINVAL;
+
+	return intel_scu_ipc_readv(reg, buf, count);
+}
+EXPORT_SYMBOL_GPL(intel_msic_bulk_read);
+
+/**
+ * intel_msic_bulk_write - write an array of values to the MSIC registers
+ * @reg: array of registers to write
+ * @buf: values to write to each register
+ * @count: number of registers to write
+ *
+ * Function writes @count registers in @buf to MSIC. Writes are performed
+ * atomically wrt MSIC. Returns %0 in case of success and negative errno in
+ * case of failure.
+ *
+ * Function may sleep.
+ */
+int intel_msic_bulk_write(unsigned short *reg, u8 *buf, size_t count)
+{
+	if (WARN_ON(count > SCU_IPC_RWBUF_LIMIT))
+		return -EINVAL;
+
+	return intel_scu_ipc_writev(reg, buf, count);
+}
+EXPORT_SYMBOL_GPL(intel_msic_bulk_write);
+
+/**
+ * intel_msic_irq_read - read a register from an MSIC interrupt tree
+ * @msic: MSIC instance
+ * @reg: interrupt register (between %INTEL_MSIC_IRQLVL1 and
+ *	 %INTEL_MSIC_RESETIRQ2)
+ * @val: value of the register is placed here
+ *
+ * This function can be used by an MSIC subdevice interrupt handler to read
+ * a register value from the MSIC interrupt tree. In this way subdevice
+ * drivers don't have to map in the interrupt tree themselves but can just
+ * call this function instead.
+ *
+ * Function doesn't sleep and is callable from interrupt context.
+ *
+ * Returns %-EINVAL if @reg is outside of the allowed register region.
+ */
+int intel_msic_irq_read(struct intel_msic *msic, unsigned short reg, u8 *val)
+{
+	if (WARN_ON(reg < INTEL_MSIC_IRQLVL1 || reg > INTEL_MSIC_RESETIRQ2))
+		return -EINVAL;
+
+	*val = readb(msic->irq_base + (reg - INTEL_MSIC_IRQLVL1));
+	return 0;
+}
+EXPORT_SYMBOL_GPL(intel_msic_irq_read);
+
+static int __devinit intel_msic_init_devices(struct intel_msic *msic)
+{
+	struct platform_device *pdev = msic->pdev;
+	struct intel_msic_platform_data *pdata = pdev->dev.platform_data;
+	int ret, i;
+
+	if (pdata->gpio) {
+		struct mfd_cell *cell = &msic_devs[INTEL_MSIC_BLOCK_GPIO];
+
+		cell->platform_data = pdata->gpio;
+		cell->pdata_size = sizeof(*pdata->gpio);
+	}
+
+	if (pdata->ocd) {
+		unsigned gpio = pdata->ocd->gpio;
+
+		ret = gpio_request_one(gpio, GPIOF_IN, "ocd_gpio");
+		if (ret) {
+			dev_err(&pdev->dev, "failed to register OCD GPIO\n");
+			return ret;
+		}
+
+		ret = gpio_to_irq(gpio);
+		if (ret < 0) {
+			dev_err(&pdev->dev, "no IRQ number for OCD GPIO\n");
+			gpio_free(gpio);
+			return ret;
+		}
+
+		/* Update the IRQ number for the OCD */
+		pdata->irq[INTEL_MSIC_BLOCK_OCD] = ret;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(msic_devs); i++) {
+		if (!pdata->irq[i])
+			continue;
+
+		ret = mfd_add_devices(&pdev->dev, -1, &msic_devs[i], 1, NULL,
+				      pdata->irq[i]);
+		if (ret)
+			goto fail;
+	}
+
+	ret = mfd_add_devices(&pdev->dev, 0, msic_other_devs,
+			      ARRAY_SIZE(msic_other_devs), NULL, 0);
+	if (ret)
+		goto fail;
+
+	return 0;
+
+fail:
+	mfd_remove_devices(&pdev->dev);
+	if (pdata->ocd)
+		gpio_free(pdata->ocd->gpio);
+
+	return ret;
+}
+
+static void __devexit intel_msic_remove_devices(struct intel_msic *msic)
+{
+	struct platform_device *pdev = msic->pdev;
+	struct intel_msic_platform_data *pdata = pdev->dev.platform_data;
+
+	mfd_remove_devices(&pdev->dev);
+
+	if (pdata->ocd)
+		gpio_free(pdata->ocd->gpio);
+}
+
+static int __devinit intel_msic_probe(struct platform_device *pdev)
+{
+	struct intel_msic_platform_data *pdata = pdev->dev.platform_data;
+	struct intel_msic *msic;
+	struct resource *res;
+	u8 id0, id1;
+	int ret;
+
+	if (!pdata) {
+		dev_err(&pdev->dev, "no platform data passed\n");
+		return -EINVAL;
+	}
+
+	/* First validate that we have an MSIC in place */
+	ret = intel_scu_ipc_ioread8(INTEL_MSIC_ID0, &id0);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to identify the MSIC chip (ID0)\n");
+		return -ENXIO;
+	}
+
+	ret = intel_scu_ipc_ioread8(INTEL_MSIC_ID1, &id1);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to identify the MSIC chip (ID1)\n");
+		return -ENXIO;
+	}
+
+	if (MSIC_VENDOR(id0) != MSIC_VENDOR(id1)) {
+		dev_err(&pdev->dev, "invalid vendor ID: %x, %x\n", id0, id1);
+		return -ENXIO;
+	}
+
+	msic = kzalloc(sizeof(*msic), GFP_KERNEL);
+	if (!msic)
+		return -ENOMEM;
+
+	msic->vendor = MSIC_VENDOR(id0);
+	msic->version = MSIC_VERSION(id0);
+	msic->pdev = pdev;
+
+	/*
+	 * Map in the MSIC interrupt tree area in SRAM. This is exposed to
+	 * the clients via intel_msic_irq_read().
+	 */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "failed to get SRAM iomem resource\n");
+		ret = -ENODEV;
+		goto fail_free_msic;
+	}
+
+	res = request_mem_region(res->start, resource_size(res), pdev->name);
+	if (!res) {
+		ret = -EBUSY;
+		goto fail_free_msic;
+	}
+
+	msic->irq_base = ioremap_nocache(res->start, resource_size(res));
+	if (!msic->irq_base) {
+		dev_err(&pdev->dev, "failed to map SRAM memory\n");
+		ret = -ENOMEM;
+		goto fail_release_region;
+	}
+
+	platform_set_drvdata(pdev, msic);
+
+	ret = intel_msic_init_devices(msic);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to initialize MSIC devices\n");
+		goto fail_unmap_mem;
+	}
+
+	dev_info(&pdev->dev, "Intel MSIC version %c%d (vendor %#x)\n",
+		 MSIC_MAJOR(msic->version), MSIC_MINOR(msic->version),
+		 msic->vendor);
+
+	return 0;
+
+fail_unmap_mem:
+	iounmap(msic->irq_base);
+fail_release_region:
+	release_mem_region(res->start, resource_size(res));
+fail_free_msic:
+	kfree(msic);
+
+	return ret;
+}
+
+static int __devexit intel_msic_remove(struct platform_device *pdev)
+{
+	struct intel_msic *msic = platform_get_drvdata(pdev);
+	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+	intel_msic_remove_devices(msic);
+	platform_set_drvdata(pdev, NULL);
+	iounmap(msic->irq_base);
+	release_mem_region(res->start, resource_size(res));
+	kfree(msic);
+
+	return 0;
+}
+
+static struct platform_driver intel_msic_driver = {
+	.probe		= intel_msic_probe,
+	.remove		= __devexit_p(intel_msic_remove),
+	.driver		= {
+		.name	= "intel_msic",
+		.owner	= THIS_MODULE,
+	},
+};
+
+module_platform_driver(intel_msic_driver);
+
+MODULE_DESCRIPTION("Driver for Intel MSIC");
+MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/janz-cmodio.c b/ap/os/linux/linux-3.4.x/drivers/mfd/janz-cmodio.c
new file mode 100644
index 0000000..a9223ed
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/janz-cmodio.c
@@ -0,0 +1,305 @@
+/*
+ * Janz CMOD-IO MODULbus Carrier Board PCI Driver
+ *
+ * Copyright (c) 2010 Ira W. Snyder <iws@ovro.caltech.edu>
+ *
+ * Lots of inspiration and code was copied from drivers/mfd/sm501.c
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/mfd/core.h>
+
+#include <linux/mfd/janz.h>
+
+#define DRV_NAME "janz-cmodio"
+
+/* Size of each MODULbus module in PCI BAR4 */
+#define CMODIO_MODULBUS_SIZE	0x200
+
+/* Maximum number of MODULbus modules on a CMOD-IO carrier board */
+#define CMODIO_MAX_MODULES	4
+
+/* Module Parameters */
+static unsigned int num_modules = CMODIO_MAX_MODULES;
+static char *modules[CMODIO_MAX_MODULES] = {
+	"empty", "empty", "empty", "empty",
+};
+
+module_param_array(modules, charp, &num_modules, S_IRUGO);
+MODULE_PARM_DESC(modules, "MODULbus modules attached to the carrier board");
+
+/* Unique Device Id */
+static unsigned int cmodio_id;
+
+struct cmodio_device {
+	/* Parent PCI device */
+	struct pci_dev *pdev;
+
+	/* PLX control registers */
+	struct janz_cmodio_onboard_regs __iomem *ctrl;
+
+	/* hex switch position */
+	u8 hex;
+
+	/* mfd-core API */
+	struct mfd_cell cells[CMODIO_MAX_MODULES];
+	struct resource resources[3 * CMODIO_MAX_MODULES];
+	struct janz_platform_data pdata[CMODIO_MAX_MODULES];
+};
+
+/*
+ * Subdevices using the mfd-core API
+ */
+
+static int __devinit cmodio_setup_subdevice(struct cmodio_device *priv,
+					    char *name, unsigned int devno,
+					    unsigned int modno)
+{
+	struct janz_platform_data *pdata;
+	struct mfd_cell *cell;
+	struct resource *res;
+	struct pci_dev *pci;
+
+	pci = priv->pdev;
+	cell = &priv->cells[devno];
+	res = &priv->resources[devno * 3];
+	pdata = &priv->pdata[devno];
+
+	cell->name = name;
+	cell->resources = res;
+	cell->num_resources = 3;
+
+	/* Setup the subdevice ID -- must be unique */
+	cell->id = cmodio_id++;
+
+	/* Add platform data */
+	pdata->modno = modno;
+	cell->platform_data = pdata;
+	cell->pdata_size = sizeof(*pdata);
+
+	/* MODULbus registers -- PCI BAR3 is big-endian MODULbus access */
+	res->flags = IORESOURCE_MEM;
+	res->parent = &pci->resource[3];
+	res->start = pci->resource[3].start + (CMODIO_MODULBUS_SIZE * modno);
+	res->end = res->start + CMODIO_MODULBUS_SIZE - 1;
+	res++;
+
+	/* PLX Control Registers -- PCI BAR4 is interrupt and other registers */
+	res->flags = IORESOURCE_MEM;
+	res->parent = &pci->resource[4];
+	res->start = pci->resource[4].start;
+	res->end = pci->resource[4].end;
+	res++;
+
+	/*
+	 * IRQ
+	 *
+	 * The start and end fields are used as an offset to the irq_base
+	 * parameter passed into the mfd_add_devices() function call. All
+	 * devices share the same IRQ.
+	 */
+	res->flags = IORESOURCE_IRQ;
+	res->parent = NULL;
+	res->start = 0;
+	res->end = 0;
+	res++;
+
+	return 0;
+}
+
+/* Probe each submodule using kernel parameters */
+static int __devinit cmodio_probe_submodules(struct cmodio_device *priv)
+{
+	struct pci_dev *pdev = priv->pdev;
+	unsigned int num_probed = 0;
+	char *name;
+	int i;
+
+	for (i = 0; i < num_modules; i++) {
+		name = modules[i];
+		if (!strcmp(name, "") || !strcmp(name, "empty"))
+			continue;
+
+		dev_dbg(&priv->pdev->dev, "MODULbus %d: name %s\n", i, name);
+		cmodio_setup_subdevice(priv, name, num_probed, i);
+		num_probed++;
+	}
+
+	/* print an error message if no modules were probed */
+	if (num_probed == 0) {
+		dev_err(&priv->pdev->dev, "no MODULbus modules specified, "
+					  "please set the ``modules'' kernel "
+					  "parameter according to your "
+					  "hardware configuration\n");
+		return -ENODEV;
+	}
+
+	return mfd_add_devices(&pdev->dev, 0, priv->cells,
+			       num_probed, NULL, pdev->irq);
+}
+
+/*
+ * SYSFS Attributes
+ */
+
+static ssize_t mbus_show(struct device *dev, struct device_attribute *attr,
+			 char *buf)
+{
+	struct cmodio_device *priv = dev_get_drvdata(dev);
+
+	return snprintf(buf, PAGE_SIZE, "%x\n", priv->hex);
+}
+
+static DEVICE_ATTR(modulbus_number, S_IRUGO, mbus_show, NULL);
+
+static struct attribute *cmodio_sysfs_attrs[] = {
+	&dev_attr_modulbus_number.attr,
+	NULL,
+};
+
+static const struct attribute_group cmodio_sysfs_attr_group = {
+	.attrs = cmodio_sysfs_attrs,
+};
+
+/*
+ * PCI Driver
+ */
+
+static int __devinit cmodio_pci_probe(struct pci_dev *dev,
+				      const struct pci_device_id *id)
+{
+	struct cmodio_device *priv;
+	int ret;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv) {
+		dev_err(&dev->dev, "unable to allocate private data\n");
+		ret = -ENOMEM;
+		goto out_return;
+	}
+
+	pci_set_drvdata(dev, priv);
+	priv->pdev = dev;
+
+	/* Hardware Initialization */
+	ret = pci_enable_device(dev);
+	if (ret) {
+		dev_err(&dev->dev, "unable to enable device\n");
+		goto out_free_priv;
+	}
+
+	pci_set_master(dev);
+	ret = pci_request_regions(dev, DRV_NAME);
+	if (ret) {
+		dev_err(&dev->dev, "unable to request regions\n");
+		goto out_pci_disable_device;
+	}
+
+	/* Onboard configuration registers */
+	priv->ctrl = pci_ioremap_bar(dev, 4);
+	if (!priv->ctrl) {
+		dev_err(&dev->dev, "unable to remap onboard regs\n");
+		ret = -ENOMEM;
+		goto out_pci_release_regions;
+	}
+
+	/* Read the hex switch on the carrier board */
+	priv->hex = ioread8(&priv->ctrl->int_enable);
+
+	/* Add the MODULbus number (hex switch value) to the device's sysfs */
+	ret = sysfs_create_group(&dev->dev.kobj, &cmodio_sysfs_attr_group);
+	if (ret) {
+		dev_err(&dev->dev, "unable to create sysfs attributes\n");
+		goto out_unmap_ctrl;
+	}
+
+	/*
+	 * Disable all interrupt lines, each submodule will enable its
+	 * own interrupt line if needed
+	 */
+	iowrite8(0xf, &priv->ctrl->int_disable);
+
+	/* Register drivers for all submodules */
+	ret = cmodio_probe_submodules(priv);
+	if (ret) {
+		dev_err(&dev->dev, "unable to probe submodules\n");
+		goto out_sysfs_remove_group;
+	}
+
+	return 0;
+
+out_sysfs_remove_group:
+	sysfs_remove_group(&dev->dev.kobj, &cmodio_sysfs_attr_group);
+out_unmap_ctrl:
+	iounmap(priv->ctrl);
+out_pci_release_regions:
+	pci_release_regions(dev);
+out_pci_disable_device:
+	pci_disable_device(dev);
+out_free_priv:
+	kfree(priv);
+out_return:
+	return ret;
+}
+
+static void __devexit cmodio_pci_remove(struct pci_dev *dev)
+{
+	struct cmodio_device *priv = pci_get_drvdata(dev);
+
+	mfd_remove_devices(&dev->dev);
+	sysfs_remove_group(&dev->dev.kobj, &cmodio_sysfs_attr_group);
+	iounmap(priv->ctrl);
+	pci_release_regions(dev);
+	pci_disable_device(dev);
+	kfree(priv);
+}
+
+#define PCI_VENDOR_ID_JANZ		0x13c3
+
+/* The list of devices that this module will support */
+static DEFINE_PCI_DEVICE_TABLE(cmodio_pci_ids) = {
+	{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, PCI_VENDOR_ID_JANZ, 0x0101 },
+	{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_VENDOR_ID_JANZ, 0x0100 },
+	{ 0, }
+};
+MODULE_DEVICE_TABLE(pci, cmodio_pci_ids);
+
+static struct pci_driver cmodio_pci_driver = {
+	.name     = DRV_NAME,
+	.id_table = cmodio_pci_ids,
+	.probe    = cmodio_pci_probe,
+	.remove   = __devexit_p(cmodio_pci_remove),
+};
+
+/*
+ * Module Init / Exit
+ */
+
+static int __init cmodio_init(void)
+{
+	return pci_register_driver(&cmodio_pci_driver);
+}
+
+static void __exit cmodio_exit(void)
+{
+	pci_unregister_driver(&cmodio_pci_driver);
+}
+
+MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>");
+MODULE_DESCRIPTION("Janz CMOD-IO PCI MODULbus Carrier Board Driver");
+MODULE_LICENSE("GPL");
+
+module_init(cmodio_init);
+module_exit(cmodio_exit);
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/jz4740-adc.c b/ap/os/linux/linux-3.4.x/drivers/mfd/jz4740-adc.c
new file mode 100644
index 0000000..87662a1
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/jz4740-adc.c
@@ -0,0 +1,346 @@
+/*
+ * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de>
+ * JZ4740 SoC ADC driver
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under  the terms of the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * This driver synchronizes access to the JZ4740 ADC core between the
+ * JZ4740 battery and hwmon drivers.
+ */
+
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include <linux/clk.h>
+#include <linux/mfd/core.h>
+
+#include <linux/jz4740-adc.h>
+
+
+#define JZ_REG_ADC_ENABLE	0x00
+#define JZ_REG_ADC_CFG		0x04
+#define JZ_REG_ADC_CTRL		0x08
+#define JZ_REG_ADC_STATUS	0x0c
+
+#define JZ_REG_ADC_TOUCHSCREEN_BASE	0x10
+#define JZ_REG_ADC_BATTERY_BASE	0x1c
+#define JZ_REG_ADC_HWMON_BASE	0x20
+
+#define JZ_ADC_ENABLE_TOUCH	BIT(2)
+#define JZ_ADC_ENABLE_BATTERY	BIT(1)
+#define JZ_ADC_ENABLE_ADCIN	BIT(0)
+
+enum {
+	JZ_ADC_IRQ_ADCIN = 0,
+	JZ_ADC_IRQ_BATTERY,
+	JZ_ADC_IRQ_TOUCH,
+	JZ_ADC_IRQ_PENUP,
+	JZ_ADC_IRQ_PENDOWN,
+};
+
+struct jz4740_adc {
+	struct resource *mem;
+	void __iomem *base;
+
+	int irq;
+	struct irq_chip_generic *gc;
+
+	struct clk *clk;
+	atomic_t clk_ref;
+
+	spinlock_t lock;
+};
+
+static void jz4740_adc_irq_demux(unsigned int irq, struct irq_desc *desc)
+{
+	struct irq_chip_generic *gc = irq_desc_get_handler_data(desc);
+	uint8_t status;
+	unsigned int i;
+
+	status = readb(gc->reg_base + JZ_REG_ADC_STATUS);
+
+	for (i = 0; i < 5; ++i) {
+		if (status & BIT(i))
+			generic_handle_irq(gc->irq_base + i);
+	}
+}
+
+
+/* Refcounting for the ADC clock is done in here instead of in the clock
+ * framework, because it is the only clock which is shared between multiple
+ * devices and thus is the only clock which needs refcounting */
+static inline void jz4740_adc_clk_enable(struct jz4740_adc *adc)
+{
+	if (atomic_inc_return(&adc->clk_ref) == 1)
+		clk_enable(adc->clk);
+}
+
+static inline void jz4740_adc_clk_disable(struct jz4740_adc *adc)
+{
+	if (atomic_dec_return(&adc->clk_ref) == 0)
+		clk_disable(adc->clk);
+}
+
+static inline void jz4740_adc_set_enabled(struct jz4740_adc *adc, int engine,
+	bool enabled)
+{
+	unsigned long flags;
+	uint8_t val;
+
+	spin_lock_irqsave(&adc->lock, flags);
+
+	val = readb(adc->base + JZ_REG_ADC_ENABLE);
+	if (enabled)
+		val |= BIT(engine);
+	else
+		val &= ~BIT(engine);
+	writeb(val, adc->base + JZ_REG_ADC_ENABLE);
+
+	spin_unlock_irqrestore(&adc->lock, flags);
+}
+
+static int jz4740_adc_cell_enable(struct platform_device *pdev)
+{
+	struct jz4740_adc *adc = dev_get_drvdata(pdev->dev.parent);
+
+	jz4740_adc_clk_enable(adc);
+	jz4740_adc_set_enabled(adc, pdev->id, true);
+
+	return 0;
+}
+
+static int jz4740_adc_cell_disable(struct platform_device *pdev)
+{
+	struct jz4740_adc *adc = dev_get_drvdata(pdev->dev.parent);
+
+	jz4740_adc_set_enabled(adc, pdev->id, false);
+	jz4740_adc_clk_disable(adc);
+
+	return 0;
+}
+
+int jz4740_adc_set_config(struct device *dev, uint32_t mask, uint32_t val)
+{
+	struct jz4740_adc *adc = dev_get_drvdata(dev);
+	unsigned long flags;
+	uint32_t cfg;
+
+	if (!adc)
+		return -ENODEV;
+
+	spin_lock_irqsave(&adc->lock, flags);
+
+	cfg = readl(adc->base + JZ_REG_ADC_CFG);
+
+	cfg &= ~mask;
+	cfg |= val;
+
+	writel(cfg, adc->base + JZ_REG_ADC_CFG);
+
+	spin_unlock_irqrestore(&adc->lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(jz4740_adc_set_config);
+
+static struct resource jz4740_hwmon_resources[] = {
+	{
+		.start = JZ_ADC_IRQ_ADCIN,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.start	= JZ_REG_ADC_HWMON_BASE,
+		.end	= JZ_REG_ADC_HWMON_BASE + 3,
+		.flags	= IORESOURCE_MEM,
+	},
+};
+
+static struct resource jz4740_battery_resources[] = {
+	{
+		.start = JZ_ADC_IRQ_BATTERY,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.start	= JZ_REG_ADC_BATTERY_BASE,
+		.end	= JZ_REG_ADC_BATTERY_BASE + 3,
+		.flags	= IORESOURCE_MEM,
+	},
+};
+
+static struct mfd_cell jz4740_adc_cells[] = {
+	{
+		.id = 0,
+		.name = "jz4740-hwmon",
+		.num_resources = ARRAY_SIZE(jz4740_hwmon_resources),
+		.resources = jz4740_hwmon_resources,
+
+		.enable = jz4740_adc_cell_enable,
+		.disable = jz4740_adc_cell_disable,
+	},
+	{
+		.id = 1,
+		.name = "jz4740-battery",
+		.num_resources = ARRAY_SIZE(jz4740_battery_resources),
+		.resources = jz4740_battery_resources,
+
+		.enable = jz4740_adc_cell_enable,
+		.disable = jz4740_adc_cell_disable,
+	},
+};
+
+static int __devinit jz4740_adc_probe(struct platform_device *pdev)
+{
+	struct irq_chip_generic *gc;
+	struct irq_chip_type *ct;
+	struct jz4740_adc *adc;
+	struct resource *mem_base;
+	int ret;
+	int irq_base;
+
+	adc = kmalloc(sizeof(*adc), GFP_KERNEL);
+	if (!adc) {
+		dev_err(&pdev->dev, "Failed to allocate driver structure\n");
+		return -ENOMEM;
+	}
+
+	adc->irq = platform_get_irq(pdev, 0);
+	if (adc->irq < 0) {
+		ret = adc->irq;
+		dev_err(&pdev->dev, "Failed to get platform irq: %d\n", ret);
+		goto err_free;
+	}
+
+	irq_base = platform_get_irq(pdev, 1);
+	if (irq_base < 0) {
+		ret = irq_base;
+		dev_err(&pdev->dev, "Failed to get irq base: %d\n", ret);
+		goto err_free;
+	}
+
+	mem_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!mem_base) {
+		ret = -ENOENT;
+		dev_err(&pdev->dev, "Failed to get platform mmio resource\n");
+		goto err_free;
+	}
+
+	/* Only request the shared registers for the MFD driver */
+	adc->mem = request_mem_region(mem_base->start, JZ_REG_ADC_STATUS,
+					pdev->name);
+	if (!adc->mem) {
+		ret = -EBUSY;
+		dev_err(&pdev->dev, "Failed to request mmio memory region\n");
+		goto err_free;
+	}
+
+	adc->base = ioremap_nocache(adc->mem->start, resource_size(adc->mem));
+	if (!adc->base) {
+		ret = -EBUSY;
+		dev_err(&pdev->dev, "Failed to ioremap mmio memory\n");
+		goto err_release_mem_region;
+	}
+
+	adc->clk = clk_get(&pdev->dev, "adc");
+	if (IS_ERR(adc->clk)) {
+		ret = PTR_ERR(adc->clk);
+		dev_err(&pdev->dev, "Failed to get clock: %d\n", ret);
+		goto err_iounmap;
+	}
+
+	spin_lock_init(&adc->lock);
+	atomic_set(&adc->clk_ref, 0);
+
+	platform_set_drvdata(pdev, adc);
+
+	gc = irq_alloc_generic_chip("INTC", 1, irq_base, adc->base,
+		handle_level_irq);
+
+	ct = gc->chip_types;
+	ct->regs.mask = JZ_REG_ADC_CTRL;
+	ct->regs.ack = JZ_REG_ADC_STATUS;
+	ct->chip.irq_mask = irq_gc_mask_set_bit;
+	ct->chip.irq_unmask = irq_gc_mask_clr_bit;
+	ct->chip.irq_ack = irq_gc_ack_set_bit;
+
+	irq_setup_generic_chip(gc, IRQ_MSK(5), 0, 0, IRQ_NOPROBE | IRQ_LEVEL);
+
+	adc->gc = gc;
+
+	irq_set_handler_data(adc->irq, gc);
+	irq_set_chained_handler(adc->irq, jz4740_adc_irq_demux);
+
+	writeb(0x00, adc->base + JZ_REG_ADC_ENABLE);
+	writeb(0xff, adc->base + JZ_REG_ADC_CTRL);
+
+	ret = mfd_add_devices(&pdev->dev, 0, jz4740_adc_cells,
+		ARRAY_SIZE(jz4740_adc_cells), mem_base, irq_base);
+	if (ret < 0)
+		goto err_clk_put;
+
+	return 0;
+
+err_clk_put:
+	clk_put(adc->clk);
+err_iounmap:
+	platform_set_drvdata(pdev, NULL);
+	iounmap(adc->base);
+err_release_mem_region:
+	release_mem_region(adc->mem->start, resource_size(adc->mem));
+err_free:
+	kfree(adc);
+
+	return ret;
+}
+
+static int __devexit jz4740_adc_remove(struct platform_device *pdev)
+{
+	struct jz4740_adc *adc = platform_get_drvdata(pdev);
+
+	mfd_remove_devices(&pdev->dev);
+
+	irq_remove_generic_chip(adc->gc, IRQ_MSK(5), IRQ_NOPROBE | IRQ_LEVEL, 0);
+	kfree(adc->gc);
+	irq_set_handler_data(adc->irq, NULL);
+	irq_set_chained_handler(adc->irq, NULL);
+
+	iounmap(adc->base);
+	release_mem_region(adc->mem->start, resource_size(adc->mem));
+
+	clk_put(adc->clk);
+
+	platform_set_drvdata(pdev, NULL);
+
+	kfree(adc);
+
+	return 0;
+}
+
+static struct platform_driver jz4740_adc_driver = {
+	.probe	= jz4740_adc_probe,
+	.remove = __devexit_p(jz4740_adc_remove),
+	.driver = {
+		.name = "jz4740-adc",
+		.owner = THIS_MODULE,
+	},
+};
+
+module_platform_driver(jz4740_adc_driver);
+
+MODULE_DESCRIPTION("JZ4740 SoC ADC driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:jz4740-adc");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/lpc_sch.c b/ap/os/linux/linux-3.4.x/drivers/mfd/lpc_sch.c
new file mode 100644
index 0000000..abc4213
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/lpc_sch.c
@@ -0,0 +1,185 @@
+/*
+ *  lpc_sch.c - LPC interface for Intel Poulsbo SCH
+ *
+ *  LPC bridge function of the Intel SCH contains many other
+ *  functional units, such as Interrupt controllers, Timers,
+ *  Power Management, System Management, GPIO, RTC, and LPC
+ *  Configuration Registers.
+ *
+ *  Copyright (c) 2010 CompuLab Ltd
+ *  Author: Denis Turischev <denis@compulab.co.il>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License 2 as published
+ *  by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/acpi.h>
+#include <linux/pci.h>
+#include <linux/mfd/core.h>
+
+#define SMBASE		0x40
+#define SMBUS_IO_SIZE	64
+
+#define GPIOBASE	0x44
+#define GPIO_IO_SIZE	64
+
+#define WDTBASE		0x84
+#define WDT_IO_SIZE	64
+
+static struct resource smbus_sch_resource = {
+		.flags = IORESOURCE_IO,
+};
+
+
+static struct resource gpio_sch_resource = {
+		.flags = IORESOURCE_IO,
+};
+
+static struct mfd_cell lpc_sch_cells[] = {
+	{
+		.name = "isch_smbus",
+		.num_resources = 1,
+		.resources = &smbus_sch_resource,
+	},
+	{
+		.name = "sch_gpio",
+		.num_resources = 1,
+		.resources = &gpio_sch_resource,
+	},
+};
+
+static struct resource wdt_sch_resource = {
+		.flags = IORESOURCE_IO,
+};
+
+static struct mfd_cell tunnelcreek_cells[] = {
+	{
+		.name = "tunnelcreek_wdt",
+		.num_resources = 1,
+		.resources = &wdt_sch_resource,
+	},
+};
+
+static DEFINE_PCI_DEVICE_TABLE(lpc_sch_ids) = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SCH_LPC) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ITC_LPC) },
+	{ 0, }
+};
+MODULE_DEVICE_TABLE(pci, lpc_sch_ids);
+
+static int __devinit lpc_sch_probe(struct pci_dev *dev,
+				const struct pci_device_id *id)
+{
+	unsigned int base_addr_cfg;
+	unsigned short base_addr;
+	int i;
+	int ret;
+
+	pci_read_config_dword(dev, SMBASE, &base_addr_cfg);
+	if (!(base_addr_cfg & (1 << 31))) {
+		dev_err(&dev->dev, "Decode of the SMBus I/O range disabled\n");
+		return -ENODEV;
+	}
+	base_addr = (unsigned short)base_addr_cfg;
+	if (base_addr == 0) {
+		dev_err(&dev->dev, "I/O space for SMBus uninitialized\n");
+		return -ENODEV;
+	}
+
+	smbus_sch_resource.start = base_addr;
+	smbus_sch_resource.end = base_addr + SMBUS_IO_SIZE - 1;
+
+	pci_read_config_dword(dev, GPIOBASE, &base_addr_cfg);
+	if (!(base_addr_cfg & (1 << 31))) {
+		dev_err(&dev->dev, "Decode of the GPIO I/O range disabled\n");
+		return -ENODEV;
+	}
+	base_addr = (unsigned short)base_addr_cfg;
+	if (base_addr == 0) {
+		dev_err(&dev->dev, "I/O space for GPIO uninitialized\n");
+		return -ENODEV;
+	}
+
+	gpio_sch_resource.start = base_addr;
+	gpio_sch_resource.end = base_addr + GPIO_IO_SIZE - 1;
+
+	for (i=0; i < ARRAY_SIZE(lpc_sch_cells); i++)
+		lpc_sch_cells[i].id = id->device;
+
+	ret = mfd_add_devices(&dev->dev, 0,
+			lpc_sch_cells, ARRAY_SIZE(lpc_sch_cells), NULL, 0);
+	if (ret)
+		goto out_dev;
+
+	if (id->device == PCI_DEVICE_ID_INTEL_ITC_LPC) {
+		pci_read_config_dword(dev, WDTBASE, &base_addr_cfg);
+		if (!(base_addr_cfg & (1 << 31))) {
+			dev_err(&dev->dev, "Decode of the WDT I/O range disabled\n");
+			ret = -ENODEV;
+			goto out_dev;
+		}
+		base_addr = (unsigned short)base_addr_cfg;
+		if (base_addr == 0) {
+			dev_err(&dev->dev, "I/O space for WDT uninitialized\n");
+			ret = -ENODEV;
+			goto out_dev;
+		}
+
+		wdt_sch_resource.start = base_addr;
+		wdt_sch_resource.end = base_addr + WDT_IO_SIZE - 1;
+
+		for (i = 0; i < ARRAY_SIZE(tunnelcreek_cells); i++)
+			tunnelcreek_cells[i].id = id->device;
+
+		ret = mfd_add_devices(&dev->dev, 0, tunnelcreek_cells,
+			ARRAY_SIZE(tunnelcreek_cells), NULL, 0);
+	}
+
+	return ret;
+out_dev:
+	mfd_remove_devices(&dev->dev);
+	return ret;
+}
+
+static void __devexit lpc_sch_remove(struct pci_dev *dev)
+{
+	mfd_remove_devices(&dev->dev);
+}
+
+static struct pci_driver lpc_sch_driver = {
+	.name		= "lpc_sch",
+	.id_table	= lpc_sch_ids,
+	.probe		= lpc_sch_probe,
+	.remove		= __devexit_p(lpc_sch_remove),
+};
+
+static int __init lpc_sch_init(void)
+{
+	return pci_register_driver(&lpc_sch_driver);
+}
+
+static void __exit lpc_sch_exit(void)
+{
+	pci_unregister_driver(&lpc_sch_driver);
+}
+
+module_init(lpc_sch_init);
+module_exit(lpc_sch_exit);
+
+MODULE_AUTHOR("Denis Turischev <denis@compulab.co.il>");
+MODULE_DESCRIPTION("LPC interface for Intel Poulsbo SCH");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/max8925-core.c b/ap/os/linux/linux-3.4.x/drivers/mfd/max8925-core.c
new file mode 100644
index 0000000..746a59c
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/max8925-core.c
@@ -0,0 +1,685 @@
+/*
+ * Base driver for Maxim MAX8925
+ *
+ * Copyright (C) 2009-2010 Marvell International Ltd.
+ *	Haojian Zhuang <haojian.zhuang@marvell.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/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/max8925.h>
+
+static struct resource io_parent = {
+	.start = 0,
+	.end   = 0xffffffff,
+	.flags = IORESOURCE_IO,
+};
+
+static struct resource backlight_resources[] = {
+	{
+		.name	= "max8925-backlight",
+		.start	= MAX8925_WLED_MODE_CNTL,
+		.end	= MAX8925_WLED_CNTL,
+		.flags	= IORESOURCE_IO,
+		.parent = &io_parent,
+	},
+};
+
+static struct mfd_cell backlight_devs[] = {
+	{
+		.name		= "max8925-backlight",
+		.num_resources	= 1,
+		.resources	= &backlight_resources[0],
+		.id		= -1,
+	},
+};
+
+static struct resource touch_resources[] = {
+	{
+		.name	= "max8925-tsc",
+		.start	= MAX8925_TSC_IRQ,
+		.end	= MAX8925_ADC_RES_END,
+		.flags	= IORESOURCE_IO,
+		.parent = &io_parent,
+	},
+};
+
+static struct mfd_cell touch_devs[] = {
+	{
+		.name		= "max8925-touch",
+		.num_resources	= 1,
+		.resources	= &touch_resources[0],
+		.id		= -1,
+	},
+};
+
+static struct resource power_supply_resources[] = {
+	{
+		.name	= "max8925-power",
+		.start	= MAX8925_CHG_IRQ1,
+		.end	= MAX8925_CHG_IRQ1_MASK,
+		.flags	= IORESOURCE_IO,
+		.parent = &io_parent,
+	},
+};
+
+static struct mfd_cell power_devs[] = {
+	{
+		.name		= "max8925-power",
+		.num_resources	= 1,
+		.resources	= &power_supply_resources[0],
+		.id		= -1,
+	},
+};
+
+static struct resource rtc_resources[] = {
+	{
+		.name	= "max8925-rtc",
+		.start	= MAX8925_RTC_IRQ,
+		.end	= MAX8925_RTC_IRQ_MASK,
+		.flags	= IORESOURCE_IO,
+	},
+};
+
+static struct mfd_cell rtc_devs[] = {
+	{
+		.name		= "max8925-rtc",
+		.num_resources	= 1,
+		.resources	= &rtc_resources[0],
+		.id		= -1,
+	},
+};
+
+static struct resource onkey_resources[] = {
+	{
+		.name	= "max8925-onkey",
+		.start	= MAX8925_IRQ_GPM_SW_R,
+		.end	= MAX8925_IRQ_GPM_SW_R,
+		.flags	= IORESOURCE_IRQ,
+	}, {
+		.name	= "max8925-onkey",
+		.start	= MAX8925_IRQ_GPM_SW_F,
+		.end	= MAX8925_IRQ_GPM_SW_F,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct mfd_cell onkey_devs[] = {
+	{
+		.name		= "max8925-onkey",
+		.num_resources	= 2,
+		.resources	= &onkey_resources[0],
+		.id		= -1,
+	},
+};
+
+#define MAX8925_REG_RESOURCE(_start, _end)	\
+{						\
+	.start	= MAX8925_##_start,		\
+	.end	= MAX8925_##_end,		\
+	.flags	= IORESOURCE_IO,		\
+	.parent = &io_parent,			\
+}
+
+static struct resource regulator_resources[] = {
+	MAX8925_REG_RESOURCE(SDCTL1, SDCTL1),
+	MAX8925_REG_RESOURCE(SDCTL2, SDCTL2),
+	MAX8925_REG_RESOURCE(SDCTL3, SDCTL3),
+	MAX8925_REG_RESOURCE(LDOCTL1, LDOCTL1),
+	MAX8925_REG_RESOURCE(LDOCTL2, LDOCTL2),
+	MAX8925_REG_RESOURCE(LDOCTL3, LDOCTL3),
+	MAX8925_REG_RESOURCE(LDOCTL4, LDOCTL4),
+	MAX8925_REG_RESOURCE(LDOCTL5, LDOCTL5),
+	MAX8925_REG_RESOURCE(LDOCTL6, LDOCTL6),
+	MAX8925_REG_RESOURCE(LDOCTL7, LDOCTL7),
+	MAX8925_REG_RESOURCE(LDOCTL8, LDOCTL8),
+	MAX8925_REG_RESOURCE(LDOCTL9, LDOCTL9),
+	MAX8925_REG_RESOURCE(LDOCTL10, LDOCTL10),
+	MAX8925_REG_RESOURCE(LDOCTL11, LDOCTL11),
+	MAX8925_REG_RESOURCE(LDOCTL12, LDOCTL12),
+	MAX8925_REG_RESOURCE(LDOCTL13, LDOCTL13),
+	MAX8925_REG_RESOURCE(LDOCTL14, LDOCTL14),
+	MAX8925_REG_RESOURCE(LDOCTL15, LDOCTL15),
+	MAX8925_REG_RESOURCE(LDOCTL16, LDOCTL16),
+	MAX8925_REG_RESOURCE(LDOCTL17, LDOCTL17),
+	MAX8925_REG_RESOURCE(LDOCTL18, LDOCTL18),
+	MAX8925_REG_RESOURCE(LDOCTL19, LDOCTL19),
+	MAX8925_REG_RESOURCE(LDOCTL20, LDOCTL20),
+};
+
+#define MAX8925_REG_DEVS(_id)						\
+{									\
+	.name		= "max8925-regulator",				\
+	.num_resources	= 1,						\
+	.resources	= &regulator_resources[MAX8925_ID_##_id],	\
+	.id		= MAX8925_ID_##_id,				\
+}
+
+static struct mfd_cell regulator_devs[] = {
+	MAX8925_REG_DEVS(SD1),
+	MAX8925_REG_DEVS(SD2),
+	MAX8925_REG_DEVS(SD3),
+	MAX8925_REG_DEVS(LDO1),
+	MAX8925_REG_DEVS(LDO2),
+	MAX8925_REG_DEVS(LDO3),
+	MAX8925_REG_DEVS(LDO4),
+	MAX8925_REG_DEVS(LDO5),
+	MAX8925_REG_DEVS(LDO6),
+	MAX8925_REG_DEVS(LDO7),
+	MAX8925_REG_DEVS(LDO8),
+	MAX8925_REG_DEVS(LDO9),
+	MAX8925_REG_DEVS(LDO10),
+	MAX8925_REG_DEVS(LDO11),
+	MAX8925_REG_DEVS(LDO12),
+	MAX8925_REG_DEVS(LDO13),
+	MAX8925_REG_DEVS(LDO14),
+	MAX8925_REG_DEVS(LDO15),
+	MAX8925_REG_DEVS(LDO16),
+	MAX8925_REG_DEVS(LDO17),
+	MAX8925_REG_DEVS(LDO18),
+	MAX8925_REG_DEVS(LDO19),
+	MAX8925_REG_DEVS(LDO20),
+};
+
+enum {
+	FLAGS_ADC = 1,	/* register in ADC component */
+	FLAGS_RTC,	/* register in RTC component */
+};
+
+struct max8925_irq_data {
+	int	reg;
+	int	mask_reg;
+	int	enable;		/* enable or not */
+	int	offs;		/* bit offset in mask register */
+	int	flags;
+	int	tsc_irq;
+};
+
+static struct max8925_irq_data max8925_irqs[] = {
+	[MAX8925_IRQ_VCHG_DC_OVP] = {
+		.reg		= MAX8925_CHG_IRQ1,
+		.mask_reg	= MAX8925_CHG_IRQ1_MASK,
+		.offs		= 1 << 0,
+	},
+	[MAX8925_IRQ_VCHG_DC_F] = {
+		.reg		= MAX8925_CHG_IRQ1,
+		.mask_reg	= MAX8925_CHG_IRQ1_MASK,
+		.offs		= 1 << 1,
+	},
+	[MAX8925_IRQ_VCHG_DC_R] = {
+		.reg		= MAX8925_CHG_IRQ1,
+		.mask_reg	= MAX8925_CHG_IRQ1_MASK,
+		.offs		= 1 << 2,
+	},
+	[MAX8925_IRQ_VCHG_THM_OK_R] = {
+		.reg		= MAX8925_CHG_IRQ2,
+		.mask_reg	= MAX8925_CHG_IRQ2_MASK,
+		.offs		= 1 << 0,
+	},
+	[MAX8925_IRQ_VCHG_THM_OK_F] = {
+		.reg		= MAX8925_CHG_IRQ2,
+		.mask_reg	= MAX8925_CHG_IRQ2_MASK,
+		.offs		= 1 << 1,
+	},
+	[MAX8925_IRQ_VCHG_SYSLOW_F] = {
+		.reg		= MAX8925_CHG_IRQ2,
+		.mask_reg	= MAX8925_CHG_IRQ2_MASK,
+		.offs		= 1 << 2,
+	},
+	[MAX8925_IRQ_VCHG_SYSLOW_R] = {
+		.reg		= MAX8925_CHG_IRQ2,
+		.mask_reg	= MAX8925_CHG_IRQ2_MASK,
+		.offs		= 1 << 3,
+	},
+	[MAX8925_IRQ_VCHG_RST] = {
+		.reg		= MAX8925_CHG_IRQ2,
+		.mask_reg	= MAX8925_CHG_IRQ2_MASK,
+		.offs		= 1 << 4,
+	},
+	[MAX8925_IRQ_VCHG_DONE] = {
+		.reg		= MAX8925_CHG_IRQ2,
+		.mask_reg	= MAX8925_CHG_IRQ2_MASK,
+		.offs		= 1 << 5,
+	},
+	[MAX8925_IRQ_VCHG_TOPOFF] = {
+		.reg		= MAX8925_CHG_IRQ2,
+		.mask_reg	= MAX8925_CHG_IRQ2_MASK,
+		.offs		= 1 << 6,
+	},
+	[MAX8925_IRQ_VCHG_TMR_FAULT] = {
+		.reg		= MAX8925_CHG_IRQ2,
+		.mask_reg	= MAX8925_CHG_IRQ2_MASK,
+		.offs		= 1 << 7,
+	},
+	[MAX8925_IRQ_GPM_RSTIN] = {
+		.reg		= MAX8925_ON_OFF_IRQ1,
+		.mask_reg	= MAX8925_ON_OFF_IRQ1_MASK,
+		.offs		= 1 << 0,
+	},
+	[MAX8925_IRQ_GPM_MPL] = {
+		.reg		= MAX8925_ON_OFF_IRQ1,
+		.mask_reg	= MAX8925_ON_OFF_IRQ1_MASK,
+		.offs		= 1 << 1,
+	},
+	[MAX8925_IRQ_GPM_SW_3SEC] = {
+		.reg		= MAX8925_ON_OFF_IRQ1,
+		.mask_reg	= MAX8925_ON_OFF_IRQ1_MASK,
+		.offs		= 1 << 2,
+	},
+	[MAX8925_IRQ_GPM_EXTON_F] = {
+		.reg		= MAX8925_ON_OFF_IRQ1,
+		.mask_reg	= MAX8925_ON_OFF_IRQ1_MASK,
+		.offs		= 1 << 3,
+	},
+	[MAX8925_IRQ_GPM_EXTON_R] = {
+		.reg		= MAX8925_ON_OFF_IRQ1,
+		.mask_reg	= MAX8925_ON_OFF_IRQ1_MASK,
+		.offs		= 1 << 4,
+	},
+	[MAX8925_IRQ_GPM_SW_1SEC] = {
+		.reg		= MAX8925_ON_OFF_IRQ1,
+		.mask_reg	= MAX8925_ON_OFF_IRQ1_MASK,
+		.offs		= 1 << 5,
+	},
+	[MAX8925_IRQ_GPM_SW_F] = {
+		.reg		= MAX8925_ON_OFF_IRQ1,
+		.mask_reg	= MAX8925_ON_OFF_IRQ1_MASK,
+		.offs		= 1 << 6,
+	},
+	[MAX8925_IRQ_GPM_SW_R] = {
+		.reg		= MAX8925_ON_OFF_IRQ1,
+		.mask_reg	= MAX8925_ON_OFF_IRQ1_MASK,
+		.offs		= 1 << 7,
+	},
+	[MAX8925_IRQ_GPM_SYSCKEN_F] = {
+		.reg		= MAX8925_ON_OFF_IRQ2,
+		.mask_reg	= MAX8925_ON_OFF_IRQ2_MASK,
+		.offs		= 1 << 0,
+	},
+	[MAX8925_IRQ_GPM_SYSCKEN_R] = {
+		.reg		= MAX8925_ON_OFF_IRQ2,
+		.mask_reg	= MAX8925_ON_OFF_IRQ2_MASK,
+		.offs		= 1 << 1,
+	},
+	[MAX8925_IRQ_RTC_ALARM1] = {
+		.reg		= MAX8925_RTC_IRQ,
+		.mask_reg	= MAX8925_RTC_IRQ_MASK,
+		.offs		= 1 << 2,
+		.flags		= FLAGS_RTC,
+	},
+	[MAX8925_IRQ_RTC_ALARM0] = {
+		.reg		= MAX8925_RTC_IRQ,
+		.mask_reg	= MAX8925_RTC_IRQ_MASK,
+		.offs		= 1 << 3,
+		.flags		= FLAGS_RTC,
+	},
+	[MAX8925_IRQ_TSC_STICK] = {
+		.reg		= MAX8925_TSC_IRQ,
+		.mask_reg	= MAX8925_TSC_IRQ_MASK,
+		.offs		= 1 << 0,
+		.flags		= FLAGS_ADC,
+		.tsc_irq	= 1,
+	},
+	[MAX8925_IRQ_TSC_NSTICK] = {
+		.reg		= MAX8925_TSC_IRQ,
+		.mask_reg	= MAX8925_TSC_IRQ_MASK,
+		.offs		= 1 << 1,
+		.flags		= FLAGS_ADC,
+		.tsc_irq	= 1,
+	},
+};
+
+static inline struct max8925_irq_data *irq_to_max8925(struct max8925_chip *chip,
+						      int irq)
+{
+	return &max8925_irqs[irq - chip->irq_base];
+}
+
+static irqreturn_t max8925_irq(int irq, void *data)
+{
+	struct max8925_chip *chip = data;
+	struct max8925_irq_data *irq_data;
+	struct i2c_client *i2c;
+	int read_reg = -1, value = 0;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(max8925_irqs); i++) {
+		irq_data = &max8925_irqs[i];
+		/* TSC IRQ should be serviced in max8925_tsc_irq() */
+		if (irq_data->tsc_irq)
+			continue;
+		if (irq_data->flags == FLAGS_RTC)
+			i2c = chip->rtc;
+		else if (irq_data->flags == FLAGS_ADC)
+			i2c = chip->adc;
+		else
+			i2c = chip->i2c;
+		if (read_reg != irq_data->reg) {
+			read_reg = irq_data->reg;
+			value = max8925_reg_read(i2c, irq_data->reg);
+		}
+		if (value & irq_data->enable)
+			handle_nested_irq(chip->irq_base + i);
+	}
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t max8925_tsc_irq(int irq, void *data)
+{
+	struct max8925_chip *chip = data;
+	struct max8925_irq_data *irq_data;
+	struct i2c_client *i2c;
+	int read_reg = -1, value = 0;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(max8925_irqs); i++) {
+		irq_data = &max8925_irqs[i];
+		/* non TSC IRQ should be serviced in max8925_irq() */
+		if (!irq_data->tsc_irq)
+			continue;
+		if (irq_data->flags == FLAGS_RTC)
+			i2c = chip->rtc;
+		else if (irq_data->flags == FLAGS_ADC)
+			i2c = chip->adc;
+		else
+			i2c = chip->i2c;
+		if (read_reg != irq_data->reg) {
+			read_reg = irq_data->reg;
+			value = max8925_reg_read(i2c, irq_data->reg);
+		}
+		if (value & irq_data->enable)
+			handle_nested_irq(chip->irq_base + i);
+	}
+	return IRQ_HANDLED;
+}
+
+static void max8925_irq_lock(struct irq_data *data)
+{
+	struct max8925_chip *chip = irq_data_get_irq_chip_data(data);
+
+	mutex_lock(&chip->irq_lock);
+}
+
+static void max8925_irq_sync_unlock(struct irq_data *data)
+{
+	struct max8925_chip *chip = irq_data_get_irq_chip_data(data);
+	struct max8925_irq_data *irq_data;
+	static unsigned char cache_chg[2] = {0xff, 0xff};
+	static unsigned char cache_on[2] = {0xff, 0xff};
+	static unsigned char cache_rtc = 0xff, cache_tsc = 0xff;
+	unsigned char irq_chg[2], irq_on[2];
+	unsigned char irq_rtc, irq_tsc;
+	int i;
+
+	/* Load cached value. In initial, all IRQs are masked */
+	irq_chg[0] = cache_chg[0];
+	irq_chg[1] = cache_chg[1];
+	irq_on[0] = cache_on[0];
+	irq_on[1] = cache_on[1];
+	irq_rtc = cache_rtc;
+	irq_tsc = cache_tsc;
+	for (i = 0; i < ARRAY_SIZE(max8925_irqs); i++) {
+		irq_data = &max8925_irqs[i];
+		/* 1 -- disable, 0 -- enable */
+		switch (irq_data->mask_reg) {
+		case MAX8925_CHG_IRQ1_MASK:
+			irq_chg[0] &= ~irq_data->enable;
+			break;
+		case MAX8925_CHG_IRQ2_MASK:
+			irq_chg[1] &= ~irq_data->enable;
+			break;
+		case MAX8925_ON_OFF_IRQ1_MASK:
+			irq_on[0] &= ~irq_data->enable;
+			break;
+		case MAX8925_ON_OFF_IRQ2_MASK:
+			irq_on[1] &= ~irq_data->enable;
+			break;
+		case MAX8925_RTC_IRQ_MASK:
+			irq_rtc &= ~irq_data->enable;
+			break;
+		case MAX8925_TSC_IRQ_MASK:
+			irq_tsc &= ~irq_data->enable;
+			break;
+		default:
+			dev_err(chip->dev, "wrong IRQ\n");
+			break;
+		}
+	}
+	/* update mask into registers */
+	if (cache_chg[0] != irq_chg[0]) {
+		cache_chg[0] = irq_chg[0];
+		max8925_reg_write(chip->i2c, MAX8925_CHG_IRQ1_MASK,
+			irq_chg[0]);
+	}
+	if (cache_chg[1] != irq_chg[1]) {
+		cache_chg[1] = irq_chg[1];
+		max8925_reg_write(chip->i2c, MAX8925_CHG_IRQ2_MASK,
+			irq_chg[1]);
+	}
+	if (cache_on[0] != irq_on[0]) {
+		cache_on[0] = irq_on[0];
+		max8925_reg_write(chip->i2c, MAX8925_ON_OFF_IRQ1_MASK,
+				irq_on[0]);
+	}
+	if (cache_on[1] != irq_on[1]) {
+		cache_on[1] = irq_on[1];
+		max8925_reg_write(chip->i2c, MAX8925_ON_OFF_IRQ2_MASK,
+				irq_on[1]);
+	}
+	if (cache_rtc != irq_rtc) {
+		cache_rtc = irq_rtc;
+		max8925_reg_write(chip->rtc, MAX8925_RTC_IRQ_MASK, irq_rtc);
+	}
+	if (cache_tsc != irq_tsc) {
+		cache_tsc = irq_tsc;
+		max8925_reg_write(chip->adc, MAX8925_TSC_IRQ_MASK, irq_tsc);
+	}
+
+	mutex_unlock(&chip->irq_lock);
+}
+
+static void max8925_irq_enable(struct irq_data *data)
+{
+	struct max8925_chip *chip = irq_data_get_irq_chip_data(data);
+	max8925_irqs[data->irq - chip->irq_base].enable
+		= max8925_irqs[data->irq - chip->irq_base].offs;
+}
+
+static void max8925_irq_disable(struct irq_data *data)
+{
+	struct max8925_chip *chip = irq_data_get_irq_chip_data(data);
+	max8925_irqs[data->irq - chip->irq_base].enable = 0;
+}
+
+static struct irq_chip max8925_irq_chip = {
+	.name		= "max8925",
+	.irq_bus_lock	= max8925_irq_lock,
+	.irq_bus_sync_unlock = max8925_irq_sync_unlock,
+	.irq_enable	= max8925_irq_enable,
+	.irq_disable	= max8925_irq_disable,
+};
+
+static int max8925_irq_init(struct max8925_chip *chip, int irq,
+			    struct max8925_platform_data *pdata)
+{
+	unsigned long flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
+	int i, ret;
+	int __irq;
+
+	if (!pdata || !pdata->irq_base) {
+		dev_warn(chip->dev, "No interrupt support on IRQ base\n");
+		return -EINVAL;
+	}
+	/* clear all interrupts */
+	max8925_reg_read(chip->i2c, MAX8925_CHG_IRQ1);
+	max8925_reg_read(chip->i2c, MAX8925_CHG_IRQ2);
+	max8925_reg_read(chip->i2c, MAX8925_ON_OFF_IRQ1);
+	max8925_reg_read(chip->i2c, MAX8925_ON_OFF_IRQ2);
+	max8925_reg_read(chip->rtc, MAX8925_RTC_IRQ);
+	max8925_reg_read(chip->adc, MAX8925_TSC_IRQ);
+	/* mask all interrupts except for TSC */
+	max8925_reg_write(chip->rtc, MAX8925_ALARM0_CNTL, 0);
+	max8925_reg_write(chip->rtc, MAX8925_ALARM1_CNTL, 0);
+	max8925_reg_write(chip->i2c, MAX8925_CHG_IRQ1_MASK, 0xff);
+	max8925_reg_write(chip->i2c, MAX8925_CHG_IRQ2_MASK, 0xff);
+	max8925_reg_write(chip->i2c, MAX8925_ON_OFF_IRQ1_MASK, 0xff);
+	max8925_reg_write(chip->i2c, MAX8925_ON_OFF_IRQ2_MASK, 0xff);
+	max8925_reg_write(chip->rtc, MAX8925_RTC_IRQ_MASK, 0xff);
+
+	mutex_init(&chip->irq_lock);
+	chip->core_irq = irq;
+	chip->irq_base = pdata->irq_base;
+
+	/* register with genirq */
+	for (i = 0; i < ARRAY_SIZE(max8925_irqs); i++) {
+		__irq = i + chip->irq_base;
+		irq_set_chip_data(__irq, chip);
+		irq_set_chip_and_handler(__irq, &max8925_irq_chip,
+					 handle_edge_irq);
+		irq_set_nested_thread(__irq, 1);
+#ifdef CONFIG_ARM
+		set_irq_flags(__irq, IRQF_VALID);
+#else
+		irq_set_noprobe(__irq);
+#endif
+	}
+	if (!irq) {
+		dev_warn(chip->dev, "No interrupt support on core IRQ\n");
+		goto tsc_irq;
+	}
+
+	ret = request_threaded_irq(irq, NULL, max8925_irq, flags,
+				   "max8925", chip);
+	if (ret) {
+		dev_err(chip->dev, "Failed to request core IRQ: %d\n", ret);
+		chip->core_irq = 0;
+	}
+
+tsc_irq:
+	/* mask TSC interrupt */
+	max8925_reg_write(chip->adc, MAX8925_TSC_IRQ_MASK, 0x0f);
+
+	if (!pdata->tsc_irq) {
+		dev_warn(chip->dev, "No interrupt support on TSC IRQ\n");
+		return 0;
+	}
+	chip->tsc_irq = pdata->tsc_irq;
+
+	ret = request_threaded_irq(chip->tsc_irq, NULL, max8925_tsc_irq,
+				   flags, "max8925-tsc", chip);
+	if (ret) {
+		dev_err(chip->dev, "Failed to request TSC IRQ: %d\n", ret);
+		chip->tsc_irq = 0;
+	}
+	return 0;
+}
+
+int __devinit max8925_device_init(struct max8925_chip *chip,
+				  struct max8925_platform_data *pdata)
+{
+	int ret;
+
+	max8925_irq_init(chip, chip->i2c->irq, pdata);
+
+	if (pdata && (pdata->power || pdata->touch)) {
+		/* enable ADC to control internal reference */
+		max8925_set_bits(chip->i2c, MAX8925_RESET_CNFG, 1, 1);
+		/* enable internal reference for ADC */
+		max8925_set_bits(chip->adc, MAX8925_TSC_CNFG1, 3, 2);
+		/* check for internal reference IRQ */
+		do {
+			ret = max8925_reg_read(chip->adc, MAX8925_TSC_IRQ);
+		} while (ret & MAX8925_NREF_OK);
+		/* enaable ADC scheduler, interval is 1 second */
+		max8925_set_bits(chip->adc, MAX8925_ADC_SCHED, 3, 2);
+	}
+
+	/* enable Momentary Power Loss */
+	max8925_set_bits(chip->rtc, MAX8925_MPL_CNTL, 1 << 4, 1 << 4);
+
+	ret = mfd_add_devices(chip->dev, 0, &rtc_devs[0],
+			      ARRAY_SIZE(rtc_devs),
+			      &rtc_resources[0], 0);
+	if (ret < 0) {
+		dev_err(chip->dev, "Failed to add rtc subdev\n");
+		goto out;
+	}
+
+	ret = mfd_add_devices(chip->dev, 0, &onkey_devs[0],
+			      ARRAY_SIZE(onkey_devs),
+			      &onkey_resources[0], 0);
+	if (ret < 0) {
+		dev_err(chip->dev, "Failed to add onkey subdev\n");
+		goto out_dev;
+	}
+
+	if (pdata) {
+		ret = mfd_add_devices(chip->dev, 0, &regulator_devs[0],
+				      ARRAY_SIZE(regulator_devs),
+				      &regulator_resources[0], 0);
+		if (ret < 0) {
+			dev_err(chip->dev, "Failed to add regulator subdev\n");
+			goto out_dev;
+		}
+	}
+
+	if (pdata && pdata->backlight) {
+		ret = mfd_add_devices(chip->dev, 0, &backlight_devs[0],
+				      ARRAY_SIZE(backlight_devs),
+				      &backlight_resources[0], 0);
+		if (ret < 0) {
+			dev_err(chip->dev, "Failed to add backlight subdev\n");
+			goto out_dev;
+		}
+	}
+
+	if (pdata && pdata->power) {
+		ret = mfd_add_devices(chip->dev, 0, &power_devs[0],
+					ARRAY_SIZE(power_devs),
+					&power_supply_resources[0], 0);
+		if (ret < 0) {
+			dev_err(chip->dev, "Failed to add power supply "
+				"subdev\n");
+			goto out_dev;
+		}
+	}
+
+	if (pdata && pdata->touch) {
+		ret = mfd_add_devices(chip->dev, 0, &touch_devs[0],
+				      ARRAY_SIZE(touch_devs),
+				      &touch_resources[0], 0);
+		if (ret < 0) {
+			dev_err(chip->dev, "Failed to add touch subdev\n");
+			goto out_dev;
+		}
+	}
+
+	return 0;
+out_dev:
+	mfd_remove_devices(chip->dev);
+out:
+	return ret;
+}
+
+void __devexit max8925_device_exit(struct max8925_chip *chip)
+{
+	if (chip->core_irq)
+		free_irq(chip->core_irq, chip);
+	if (chip->tsc_irq)
+		free_irq(chip->tsc_irq, chip);
+	mfd_remove_devices(chip->dev);
+}
+
+
+MODULE_DESCRIPTION("PMIC Driver for Maxim MAX8925");
+MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/max8925-i2c.c b/ap/os/linux/linux-3.4.x/drivers/mfd/max8925-i2c.c
new file mode 100644
index 0000000..6d71067
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/max8925-i2c.c
@@ -0,0 +1,245 @@
+/*
+ * I2C driver for Maxim MAX8925
+ *
+ * Copyright (C) 2009 Marvell International Ltd.
+ *	Haojian Zhuang <haojian.zhuang@marvell.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/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/mfd/max8925.h>
+#include <linux/slab.h>
+
+#define RTC_I2C_ADDR		0x68
+#define ADC_I2C_ADDR		0x47
+
+static inline int max8925_read_device(struct i2c_client *i2c,
+				      int reg, int bytes, void *dest)
+{
+	int ret;
+
+	if (bytes > 1)
+		ret = i2c_smbus_read_i2c_block_data(i2c, reg, bytes, dest);
+	else {
+		ret = i2c_smbus_read_byte_data(i2c, reg);
+		if (ret < 0)
+			return ret;
+		*(unsigned char *)dest = (unsigned char)ret;
+	}
+	return ret;
+}
+
+static inline int max8925_write_device(struct i2c_client *i2c,
+				       int reg, int bytes, void *src)
+{
+	unsigned char buf[bytes + 1];
+	int ret;
+
+	buf[0] = (unsigned char)reg;
+	memcpy(&buf[1], src, bytes);
+
+	ret = i2c_master_send(i2c, buf, bytes + 1);
+	if (ret < 0)
+		return ret;
+	return 0;
+}
+
+int max8925_reg_read(struct i2c_client *i2c, int reg)
+{
+	struct max8925_chip *chip = i2c_get_clientdata(i2c);
+	unsigned char data = 0;
+	int ret;
+
+	mutex_lock(&chip->io_lock);
+	ret = max8925_read_device(i2c, reg, 1, &data);
+	mutex_unlock(&chip->io_lock);
+
+	if (ret < 0)
+		return ret;
+	else
+		return (int)data;
+}
+EXPORT_SYMBOL(max8925_reg_read);
+
+int max8925_reg_write(struct i2c_client *i2c, int reg,
+		unsigned char data)
+{
+	struct max8925_chip *chip = i2c_get_clientdata(i2c);
+	int ret;
+
+	mutex_lock(&chip->io_lock);
+	ret = max8925_write_device(i2c, reg, 1, &data);
+	mutex_unlock(&chip->io_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(max8925_reg_write);
+
+int max8925_bulk_read(struct i2c_client *i2c, int reg,
+		int count, unsigned char *buf)
+{
+	struct max8925_chip *chip = i2c_get_clientdata(i2c);
+	int ret;
+
+	mutex_lock(&chip->io_lock);
+	ret = max8925_read_device(i2c, reg, count, buf);
+	mutex_unlock(&chip->io_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(max8925_bulk_read);
+
+int max8925_bulk_write(struct i2c_client *i2c, int reg,
+		int count, unsigned char *buf)
+{
+	struct max8925_chip *chip = i2c_get_clientdata(i2c);
+	int ret;
+
+	mutex_lock(&chip->io_lock);
+	ret = max8925_write_device(i2c, reg, count, buf);
+	mutex_unlock(&chip->io_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(max8925_bulk_write);
+
+int max8925_set_bits(struct i2c_client *i2c, int reg,
+		unsigned char mask, unsigned char data)
+{
+	struct max8925_chip *chip = i2c_get_clientdata(i2c);
+	unsigned char value;
+	int ret;
+
+	mutex_lock(&chip->io_lock);
+	ret = max8925_read_device(i2c, reg, 1, &value);
+	if (ret < 0)
+		goto out;
+	value &= ~mask;
+	value |= data;
+	ret = max8925_write_device(i2c, reg, 1, &value);
+out:
+	mutex_unlock(&chip->io_lock);
+	return ret;
+}
+EXPORT_SYMBOL(max8925_set_bits);
+
+
+static const struct i2c_device_id max8925_id_table[] = {
+	{ "max8925", 0 },
+	{ },
+};
+MODULE_DEVICE_TABLE(i2c, max8925_id_table);
+
+static int __devinit max8925_probe(struct i2c_client *client,
+				   const struct i2c_device_id *id)
+{
+	struct max8925_platform_data *pdata = client->dev.platform_data;
+	static struct max8925_chip *chip;
+
+	if (!pdata) {
+		pr_info("%s: platform data is missing\n", __func__);
+		return -EINVAL;
+	}
+
+	chip = kzalloc(sizeof(struct max8925_chip), GFP_KERNEL);
+	if (chip == NULL)
+		return -ENOMEM;
+	chip->i2c = client;
+	chip->dev = &client->dev;
+	i2c_set_clientdata(client, chip);
+	dev_set_drvdata(chip->dev, chip);
+	mutex_init(&chip->io_lock);
+
+	chip->rtc = i2c_new_dummy(chip->i2c->adapter, RTC_I2C_ADDR);
+	if (!chip->rtc) {
+		dev_err(chip->dev, "Failed to allocate I2C device for RTC\n");
+		return -ENODEV;
+	}
+	i2c_set_clientdata(chip->rtc, chip);
+
+	chip->adc = i2c_new_dummy(chip->i2c->adapter, ADC_I2C_ADDR);
+	if (!chip->adc) {
+		dev_err(chip->dev, "Failed to allocate I2C device for ADC\n");
+		i2c_unregister_device(chip->rtc);
+		return -ENODEV;
+	}
+	i2c_set_clientdata(chip->adc, chip);
+
+	device_init_wakeup(&client->dev, 1);
+
+	max8925_device_init(chip, pdata);
+
+	return 0;
+}
+
+static int __devexit max8925_remove(struct i2c_client *client)
+{
+	struct max8925_chip *chip = i2c_get_clientdata(client);
+
+	max8925_device_exit(chip);
+	i2c_unregister_device(chip->adc);
+	i2c_unregister_device(chip->rtc);
+	kfree(chip);
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int max8925_suspend(struct device *dev)
+{
+	struct i2c_client *client = container_of(dev, struct i2c_client, dev);
+	struct max8925_chip *chip = i2c_get_clientdata(client);
+
+	if (device_may_wakeup(dev) && chip->wakeup_flag)
+		enable_irq_wake(chip->core_irq);
+	return 0;
+}
+
+static int max8925_resume(struct device *dev)
+{
+	struct i2c_client *client = container_of(dev, struct i2c_client, dev);
+	struct max8925_chip *chip = i2c_get_clientdata(client);
+
+	if (device_may_wakeup(dev) && chip->wakeup_flag)
+		disable_irq_wake(chip->core_irq);
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(max8925_pm_ops, max8925_suspend, max8925_resume);
+
+static struct i2c_driver max8925_driver = {
+	.driver	= {
+		.name	= "max8925",
+		.owner	= THIS_MODULE,
+		.pm     = &max8925_pm_ops,
+	},
+	.probe		= max8925_probe,
+	.remove		= __devexit_p(max8925_remove),
+	.id_table	= max8925_id_table,
+};
+
+static int __init max8925_i2c_init(void)
+{
+	int ret;
+
+	ret = i2c_add_driver(&max8925_driver);
+	if (ret != 0)
+		pr_err("Failed to register MAX8925 I2C driver: %d\n", ret);
+	return ret;
+}
+subsys_initcall(max8925_i2c_init);
+
+static void __exit max8925_i2c_exit(void)
+{
+	i2c_del_driver(&max8925_driver);
+}
+module_exit(max8925_i2c_exit);
+
+MODULE_DESCRIPTION("I2C Driver for Maxim 8925");
+MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/max8997-irq.c b/ap/os/linux/linux-3.4.x/drivers/mfd/max8997-irq.c
new file mode 100644
index 0000000..09274cf
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/max8997-irq.c
@@ -0,0 +1,375 @@
+/*
+ * max8997-irq.c - Interrupt controller support for MAX8997
+ *
+ * Copyright (C) 2011 Samsung Electronics Co.Ltd
+ * MyungJoo Ham <myungjoo.ham@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * This driver is based on max8998-irq.c
+ */
+
+#include <linux/err.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/max8997.h>
+#include <linux/mfd/max8997-private.h>
+
+static const u8 max8997_mask_reg[] = {
+	[PMIC_INT1] = MAX8997_REG_INT1MSK,
+	[PMIC_INT2] = MAX8997_REG_INT2MSK,
+	[PMIC_INT3] = MAX8997_REG_INT3MSK,
+	[PMIC_INT4] = MAX8997_REG_INT4MSK,
+	[FUEL_GAUGE] = MAX8997_REG_INVALID,
+	[MUIC_INT1] = MAX8997_MUIC_REG_INTMASK1,
+	[MUIC_INT2] = MAX8997_MUIC_REG_INTMASK2,
+	[MUIC_INT3] = MAX8997_MUIC_REG_INTMASK3,
+	[GPIO_LOW] = MAX8997_REG_INVALID,
+	[GPIO_HI] = MAX8997_REG_INVALID,
+	[FLASH_STATUS] = MAX8997_REG_INVALID,
+};
+
+static struct i2c_client *get_i2c(struct max8997_dev *max8997,
+				enum max8997_irq_source src)
+{
+	switch (src) {
+	case PMIC_INT1 ... PMIC_INT4:
+		return max8997->i2c;
+	case FUEL_GAUGE:
+		return NULL;
+	case MUIC_INT1 ... MUIC_INT3:
+		return max8997->muic;
+	case GPIO_LOW ... GPIO_HI:
+		return max8997->i2c;
+	case FLASH_STATUS:
+		return max8997->i2c;
+	default:
+		return ERR_PTR(-EINVAL);
+	}
+}
+
+struct max8997_irq_data {
+	int mask;
+	enum max8997_irq_source group;
+};
+
+#define DECLARE_IRQ(idx, _group, _mask)		\
+	[(idx)] = { .group = (_group), .mask = (_mask) }
+static const struct max8997_irq_data max8997_irqs[] = {
+	DECLARE_IRQ(MAX8997_PMICIRQ_PWRONR,	PMIC_INT1, 1 << 0),
+	DECLARE_IRQ(MAX8997_PMICIRQ_PWRONF,	PMIC_INT1, 1 << 1),
+	DECLARE_IRQ(MAX8997_PMICIRQ_PWRON1SEC,	PMIC_INT1, 1 << 3),
+	DECLARE_IRQ(MAX8997_PMICIRQ_JIGONR,	PMIC_INT1, 1 << 4),
+	DECLARE_IRQ(MAX8997_PMICIRQ_JIGONF,	PMIC_INT1, 1 << 5),
+	DECLARE_IRQ(MAX8997_PMICIRQ_LOWBAT2,	PMIC_INT1, 1 << 6),
+	DECLARE_IRQ(MAX8997_PMICIRQ_LOWBAT1,	PMIC_INT1, 1 << 7),
+
+	DECLARE_IRQ(MAX8997_PMICIRQ_JIGR,	PMIC_INT2, 1 << 0),
+	DECLARE_IRQ(MAX8997_PMICIRQ_JIGF,	PMIC_INT2, 1 << 1),
+	DECLARE_IRQ(MAX8997_PMICIRQ_MR,		PMIC_INT2, 1 << 2),
+	DECLARE_IRQ(MAX8997_PMICIRQ_DVS1OK,	PMIC_INT2, 1 << 3),
+	DECLARE_IRQ(MAX8997_PMICIRQ_DVS2OK,	PMIC_INT2, 1 << 4),
+	DECLARE_IRQ(MAX8997_PMICIRQ_DVS3OK,	PMIC_INT2, 1 << 5),
+	DECLARE_IRQ(MAX8997_PMICIRQ_DVS4OK,	PMIC_INT2, 1 << 6),
+
+	DECLARE_IRQ(MAX8997_PMICIRQ_CHGINS,	PMIC_INT3, 1 << 0),
+	DECLARE_IRQ(MAX8997_PMICIRQ_CHGRM,	PMIC_INT3, 1 << 1),
+	DECLARE_IRQ(MAX8997_PMICIRQ_DCINOVP,	PMIC_INT3, 1 << 2),
+	DECLARE_IRQ(MAX8997_PMICIRQ_TOPOFFR,	PMIC_INT3, 1 << 3),
+	DECLARE_IRQ(MAX8997_PMICIRQ_CHGRSTF,	PMIC_INT3, 1 << 5),
+	DECLARE_IRQ(MAX8997_PMICIRQ_MBCHGTMEXPD,	PMIC_INT3, 1 << 7),
+
+	DECLARE_IRQ(MAX8997_PMICIRQ_RTC60S,	PMIC_INT4, 1 << 0),
+	DECLARE_IRQ(MAX8997_PMICIRQ_RTCA1,	PMIC_INT4, 1 << 1),
+	DECLARE_IRQ(MAX8997_PMICIRQ_RTCA2,	PMIC_INT4, 1 << 2),
+	DECLARE_IRQ(MAX8997_PMICIRQ_SMPL_INT,	PMIC_INT4, 1 << 3),
+	DECLARE_IRQ(MAX8997_PMICIRQ_RTC1S,	PMIC_INT4, 1 << 4),
+	DECLARE_IRQ(MAX8997_PMICIRQ_WTSR,	PMIC_INT4, 1 << 5),
+
+	DECLARE_IRQ(MAX8997_MUICIRQ_ADCError,	MUIC_INT1, 1 << 2),
+	DECLARE_IRQ(MAX8997_MUICIRQ_ADCLow,	MUIC_INT1, 1 << 1),
+	DECLARE_IRQ(MAX8997_MUICIRQ_ADC,	MUIC_INT1, 1 << 0),
+
+	DECLARE_IRQ(MAX8997_MUICIRQ_VBVolt,	MUIC_INT2, 1 << 4),
+	DECLARE_IRQ(MAX8997_MUICIRQ_DBChg,	MUIC_INT2, 1 << 3),
+	DECLARE_IRQ(MAX8997_MUICIRQ_DCDTmr,	MUIC_INT2, 1 << 2),
+	DECLARE_IRQ(MAX8997_MUICIRQ_ChgDetRun,	MUIC_INT2, 1 << 1),
+	DECLARE_IRQ(MAX8997_MUICIRQ_ChgTyp,	MUIC_INT2, 1 << 0),
+
+	DECLARE_IRQ(MAX8997_MUICIRQ_OVP,	MUIC_INT3, 1 << 2),
+};
+
+static void max8997_irq_lock(struct irq_data *data)
+{
+	struct max8997_dev *max8997 = irq_get_chip_data(data->irq);
+
+	mutex_lock(&max8997->irqlock);
+}
+
+static void max8997_irq_sync_unlock(struct irq_data *data)
+{
+	struct max8997_dev *max8997 = irq_get_chip_data(data->irq);
+	int i;
+
+	for (i = 0; i < MAX8997_IRQ_GROUP_NR; i++) {
+		u8 mask_reg = max8997_mask_reg[i];
+		struct i2c_client *i2c = get_i2c(max8997, i);
+
+		if (mask_reg == MAX8997_REG_INVALID ||
+				IS_ERR_OR_NULL(i2c))
+			continue;
+		max8997->irq_masks_cache[i] = max8997->irq_masks_cur[i];
+
+		max8997_write_reg(i2c, max8997_mask_reg[i],
+				max8997->irq_masks_cur[i]);
+	}
+
+	mutex_unlock(&max8997->irqlock);
+}
+
+static const inline struct max8997_irq_data *
+irq_to_max8997_irq(struct max8997_dev *max8997, int irq)
+{
+	return &max8997_irqs[irq - max8997->irq_base];
+}
+
+static void max8997_irq_mask(struct irq_data *data)
+{
+	struct max8997_dev *max8997 = irq_get_chip_data(data->irq);
+	const struct max8997_irq_data *irq_data = irq_to_max8997_irq(max8997,
+								data->irq);
+
+	max8997->irq_masks_cur[irq_data->group] |= irq_data->mask;
+}
+
+static void max8997_irq_unmask(struct irq_data *data)
+{
+	struct max8997_dev *max8997 = irq_get_chip_data(data->irq);
+	const struct max8997_irq_data *irq_data = irq_to_max8997_irq(max8997,
+								data->irq);
+
+	max8997->irq_masks_cur[irq_data->group] &= ~irq_data->mask;
+}
+
+static struct irq_chip max8997_irq_chip = {
+	.name			= "max8997",
+	.irq_bus_lock		= max8997_irq_lock,
+	.irq_bus_sync_unlock	= max8997_irq_sync_unlock,
+	.irq_mask		= max8997_irq_mask,
+	.irq_unmask		= max8997_irq_unmask,
+};
+
+#define MAX8997_IRQSRC_PMIC		(1 << 1)
+#define MAX8997_IRQSRC_FUELGAUGE	(1 << 2)
+#define MAX8997_IRQSRC_MUIC		(1 << 3)
+#define MAX8997_IRQSRC_GPIO		(1 << 4)
+#define MAX8997_IRQSRC_FLASH		(1 << 5)
+static irqreturn_t max8997_irq_thread(int irq, void *data)
+{
+	struct max8997_dev *max8997 = data;
+	u8 irq_reg[MAX8997_IRQ_GROUP_NR] = {};
+	u8 irq_src;
+	int ret;
+	int i;
+
+	ret = max8997_read_reg(max8997->i2c, MAX8997_REG_INTSRC, &irq_src);
+	if (ret < 0) {
+		dev_err(max8997->dev, "Failed to read interrupt source: %d\n",
+				ret);
+		return IRQ_NONE;
+	}
+
+	if (irq_src & MAX8997_IRQSRC_PMIC) {
+		/* PMIC INT1 ~ INT4 */
+		max8997_bulk_read(max8997->i2c, MAX8997_REG_INT1, 4,
+				&irq_reg[PMIC_INT1]);
+	}
+	if (irq_src & MAX8997_IRQSRC_FUELGAUGE) {
+		/*
+		 * TODO: FUEL GAUGE
+		 *
+		 * This is to be supported by Max17042 driver. When
+		 * an interrupt incurs here, it should be relayed to a
+		 * Max17042 device that is connected (probably by
+		 * platform-data). However, we do not have interrupt
+		 * handling in Max17042 driver currently. The Max17042 IRQ
+		 * driver should be ready to be used as a stand-alone device and
+		 * a Max8997-dependent device. Because it is not ready in
+		 * Max17042-side and it is not too critical in operating
+		 * Max8997, we do not implement this in initial releases.
+		 */
+		irq_reg[FUEL_GAUGE] = 0;
+	}
+	if (irq_src & MAX8997_IRQSRC_MUIC) {
+		/* MUIC INT1 ~ INT3 */
+		max8997_bulk_read(max8997->muic, MAX8997_MUIC_REG_INT1, 3,
+				&irq_reg[MUIC_INT1]);
+	}
+	if (irq_src & MAX8997_IRQSRC_GPIO) {
+		/* GPIO Interrupt */
+		u8 gpio_info[MAX8997_NUM_GPIO];
+
+		irq_reg[GPIO_LOW] = 0;
+		irq_reg[GPIO_HI] = 0;
+
+		max8997_bulk_read(max8997->i2c, MAX8997_REG_GPIOCNTL1,
+				MAX8997_NUM_GPIO, gpio_info);
+		for (i = 0; i < MAX8997_NUM_GPIO; i++) {
+			bool interrupt = false;
+
+			switch (gpio_info[i] & MAX8997_GPIO_INT_MASK) {
+			case MAX8997_GPIO_INT_BOTH:
+				if (max8997->gpio_status[i] != gpio_info[i])
+					interrupt = true;
+				break;
+			case MAX8997_GPIO_INT_RISE:
+				if ((max8997->gpio_status[i] != gpio_info[i]) &&
+				    (gpio_info[i] & MAX8997_GPIO_DATA_MASK))
+					interrupt = true;
+				break;
+			case MAX8997_GPIO_INT_FALL:
+				if ((max8997->gpio_status[i] != gpio_info[i]) &&
+				    !(gpio_info[i] & MAX8997_GPIO_DATA_MASK))
+					interrupt = true;
+				break;
+			default:
+				break;
+			}
+
+			if (interrupt) {
+				if (i < 8)
+					irq_reg[GPIO_LOW] |= (1 << i);
+				else
+					irq_reg[GPIO_HI] |= (1 << (i - 8));
+			}
+
+		}
+	}
+	if (irq_src & MAX8997_IRQSRC_FLASH) {
+		/* Flash Status Interrupt */
+		ret = max8997_read_reg(max8997->i2c, MAX8997_REG_FLASHSTATUS,
+				&irq_reg[FLASH_STATUS]);
+	}
+
+	/* Apply masking */
+	for (i = 0; i < MAX8997_IRQ_GROUP_NR; i++)
+		irq_reg[i] &= ~max8997->irq_masks_cur[i];
+
+	/* Report */
+	for (i = 0; i < MAX8997_IRQ_NR; i++) {
+		if (irq_reg[max8997_irqs[i].group] & max8997_irqs[i].mask)
+			handle_nested_irq(max8997->irq_base + i);
+	}
+
+	return IRQ_HANDLED;
+}
+
+int max8997_irq_resume(struct max8997_dev *max8997)
+{
+	if (max8997->irq && max8997->irq_base)
+		max8997_irq_thread(max8997->irq_base, max8997);
+	return 0;
+}
+
+int max8997_irq_init(struct max8997_dev *max8997)
+{
+	int i;
+	int cur_irq;
+	int ret;
+	u8 val;
+
+	if (!max8997->irq) {
+		dev_warn(max8997->dev, "No interrupt specified.\n");
+		max8997->irq_base = 0;
+		return 0;
+	}
+
+	if (!max8997->irq_base) {
+		dev_err(max8997->dev, "No interrupt base specified.\n");
+		return 0;
+	}
+
+	mutex_init(&max8997->irqlock);
+
+	/* Mask individual interrupt sources */
+	for (i = 0; i < MAX8997_IRQ_GROUP_NR; i++) {
+		struct i2c_client *i2c;
+
+		max8997->irq_masks_cur[i] = 0xff;
+		max8997->irq_masks_cache[i] = 0xff;
+		i2c = get_i2c(max8997, i);
+
+		if (IS_ERR_OR_NULL(i2c))
+			continue;
+		if (max8997_mask_reg[i] == MAX8997_REG_INVALID)
+			continue;
+
+		max8997_write_reg(i2c, max8997_mask_reg[i], 0xff);
+	}
+
+	for (i = 0; i < MAX8997_NUM_GPIO; i++) {
+		max8997->gpio_status[i] = (max8997_read_reg(max8997->i2c,
+						MAX8997_REG_GPIOCNTL1 + i,
+						&val)
+					& MAX8997_GPIO_DATA_MASK) ?
+					true : false;
+	}
+
+	/* Register with genirq */
+	for (i = 0; i < MAX8997_IRQ_NR; i++) {
+		cur_irq = i + max8997->irq_base;
+		irq_set_chip_data(cur_irq, max8997);
+		irq_set_chip_and_handler(cur_irq, &max8997_irq_chip,
+				handle_edge_irq);
+		irq_set_nested_thread(cur_irq, 1);
+#ifdef CONFIG_ARM
+		set_irq_flags(cur_irq, IRQF_VALID);
+#else
+		irq_set_noprobe(cur_irq);
+#endif
+	}
+
+	ret = request_threaded_irq(max8997->irq, NULL, max8997_irq_thread,
+			IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+			"max8997-irq", max8997);
+
+	if (ret) {
+		dev_err(max8997->dev, "Failed to request IRQ %d: %d\n",
+				max8997->irq, ret);
+		return ret;
+	}
+
+	if (!max8997->ono)
+		return 0;
+
+	ret = request_threaded_irq(max8997->ono, NULL, max8997_irq_thread,
+			IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING |
+			IRQF_ONESHOT, "max8997-ono", max8997);
+
+	if (ret)
+		dev_err(max8997->dev, "Failed to request ono-IRQ %d: %d\n",
+				max8997->ono, ret);
+
+	return 0;
+}
+
+void max8997_irq_exit(struct max8997_dev *max8997)
+{
+	if (max8997->ono)
+		free_irq(max8997->ono, max8997);
+
+	if (max8997->irq)
+		free_irq(max8997->irq, max8997);
+}
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/max8997.c b/ap/os/linux/linux-3.4.x/drivers/mfd/max8997.c
new file mode 100644
index 0000000..b4d48af
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/max8997.c
@@ -0,0 +1,477 @@
+/*
+ * max8997.c - mfd core driver for the Maxim 8966 and 8997
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ * MyungJoo Ham <myungjoo.ham@smasung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * This driver is based on max8998.c
+ */
+
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/pm_runtime.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/max8997.h>
+#include <linux/mfd/max8997-private.h>
+
+#define I2C_ADDR_PMIC	(0xCC >> 1)
+#define I2C_ADDR_MUIC	(0x4A >> 1)
+#define I2C_ADDR_BATTERY	(0x6C >> 1)
+#define I2C_ADDR_RTC	(0x0C >> 1)
+#define I2C_ADDR_HAPTIC	(0x90 >> 1)
+
+static struct mfd_cell max8997_devs[] = {
+	{ .name = "max8997-pmic", },
+	{ .name = "max8997-rtc", },
+	{ .name = "max8997-battery", },
+	{ .name = "max8997-haptic", },
+	{ .name = "max8997-muic", },
+	{ .name = "max8997-led", .id = 1 },
+	{ .name = "max8997-led", .id = 2 },
+};
+
+int max8997_read_reg(struct i2c_client *i2c, u8 reg, u8 *dest)
+{
+	struct max8997_dev *max8997 = i2c_get_clientdata(i2c);
+	int ret;
+
+	mutex_lock(&max8997->iolock);
+	ret = i2c_smbus_read_byte_data(i2c, reg);
+	mutex_unlock(&max8997->iolock);
+	if (ret < 0)
+		return ret;
+
+	ret &= 0xff;
+	*dest = ret;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(max8997_read_reg);
+
+int max8997_bulk_read(struct i2c_client *i2c, u8 reg, int count, u8 *buf)
+{
+	struct max8997_dev *max8997 = i2c_get_clientdata(i2c);
+	int ret;
+
+	mutex_lock(&max8997->iolock);
+	ret = i2c_smbus_read_i2c_block_data(i2c, reg, count, buf);
+	mutex_unlock(&max8997->iolock);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(max8997_bulk_read);
+
+int max8997_write_reg(struct i2c_client *i2c, u8 reg, u8 value)
+{
+	struct max8997_dev *max8997 = i2c_get_clientdata(i2c);
+	int ret;
+
+	mutex_lock(&max8997->iolock);
+	ret = i2c_smbus_write_byte_data(i2c, reg, value);
+	mutex_unlock(&max8997->iolock);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(max8997_write_reg);
+
+int max8997_bulk_write(struct i2c_client *i2c, u8 reg, int count, u8 *buf)
+{
+	struct max8997_dev *max8997 = i2c_get_clientdata(i2c);
+	int ret;
+
+	mutex_lock(&max8997->iolock);
+	ret = i2c_smbus_write_i2c_block_data(i2c, reg, count, buf);
+	mutex_unlock(&max8997->iolock);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(max8997_bulk_write);
+
+int max8997_update_reg(struct i2c_client *i2c, u8 reg, u8 val, u8 mask)
+{
+	struct max8997_dev *max8997 = i2c_get_clientdata(i2c);
+	int ret;
+
+	mutex_lock(&max8997->iolock);
+	ret = i2c_smbus_read_byte_data(i2c, reg);
+	if (ret >= 0) {
+		u8 old_val = ret & 0xff;
+		u8 new_val = (val & mask) | (old_val & (~mask));
+		ret = i2c_smbus_write_byte_data(i2c, reg, new_val);
+	}
+	mutex_unlock(&max8997->iolock);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(max8997_update_reg);
+
+static int max8997_i2c_probe(struct i2c_client *i2c,
+			    const struct i2c_device_id *id)
+{
+	struct max8997_dev *max8997;
+	struct max8997_platform_data *pdata = i2c->dev.platform_data;
+	int ret = 0;
+
+	max8997 = kzalloc(sizeof(struct max8997_dev), GFP_KERNEL);
+	if (max8997 == NULL)
+		return -ENOMEM;
+
+	i2c_set_clientdata(i2c, max8997);
+	max8997->dev = &i2c->dev;
+	max8997->i2c = i2c;
+	max8997->type = id->driver_data;
+	max8997->irq = i2c->irq;
+
+	if (!pdata)
+		goto err;
+
+	max8997->irq_base = pdata->irq_base;
+	max8997->ono = pdata->ono;
+
+	mutex_init(&max8997->iolock);
+
+	max8997->rtc = i2c_new_dummy(i2c->adapter, I2C_ADDR_RTC);
+	if (!max8997->rtc) {
+		dev_err(max8997->dev, "Failed to allocate I2C device for RTC\n");
+		return -ENODEV;
+	}
+	i2c_set_clientdata(max8997->rtc, max8997);
+
+	max8997->haptic = i2c_new_dummy(i2c->adapter, I2C_ADDR_HAPTIC);
+	if (!max8997->haptic) {
+		dev_err(max8997->dev, "Failed to allocate I2C device for Haptic\n");
+		ret = -ENODEV;
+		goto err_i2c_haptic;
+	}
+	i2c_set_clientdata(max8997->haptic, max8997);
+
+	max8997->muic = i2c_new_dummy(i2c->adapter, I2C_ADDR_MUIC);
+	if (!max8997->muic) {
+		dev_err(max8997->dev, "Failed to allocate I2C device for MUIC\n");
+		ret = -ENODEV;
+		goto err_i2c_muic;
+	}
+	i2c_set_clientdata(max8997->muic, max8997);
+
+	pm_runtime_set_active(max8997->dev);
+
+	max8997_irq_init(max8997);
+
+	mfd_add_devices(max8997->dev, -1, max8997_devs,
+			ARRAY_SIZE(max8997_devs),
+			NULL, 0);
+
+	/*
+	 * TODO: enable others (flash, muic, rtc, battery, ...) and
+	 * check the return value
+	 */
+
+	if (ret < 0)
+		goto err_mfd;
+
+	/* MAX8997 has a power button input. */
+	device_init_wakeup(max8997->dev, pdata->wakeup);
+
+	return ret;
+
+err_mfd:
+	mfd_remove_devices(max8997->dev);
+	i2c_unregister_device(max8997->muic);
+err_i2c_muic:
+	i2c_unregister_device(max8997->haptic);
+err_i2c_haptic:
+	i2c_unregister_device(max8997->rtc);
+err:
+	kfree(max8997);
+	return ret;
+}
+
+static int max8997_i2c_remove(struct i2c_client *i2c)
+{
+	struct max8997_dev *max8997 = i2c_get_clientdata(i2c);
+
+	mfd_remove_devices(max8997->dev);
+	i2c_unregister_device(max8997->muic);
+	i2c_unregister_device(max8997->haptic);
+	i2c_unregister_device(max8997->rtc);
+	kfree(max8997);
+
+	return 0;
+}
+
+static const struct i2c_device_id max8997_i2c_id[] = {
+	{ "max8997", TYPE_MAX8997 },
+	{ "max8966", TYPE_MAX8966 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, max8998_i2c_id);
+
+u8 max8997_dumpaddr_pmic[] = {
+	MAX8997_REG_INT1MSK,
+	MAX8997_REG_INT2MSK,
+	MAX8997_REG_INT3MSK,
+	MAX8997_REG_INT4MSK,
+	MAX8997_REG_MAINCON1,
+	MAX8997_REG_MAINCON2,
+	MAX8997_REG_BUCKRAMP,
+	MAX8997_REG_BUCK1CTRL,
+	MAX8997_REG_BUCK1DVS1,
+	MAX8997_REG_BUCK1DVS2,
+	MAX8997_REG_BUCK1DVS3,
+	MAX8997_REG_BUCK1DVS4,
+	MAX8997_REG_BUCK1DVS5,
+	MAX8997_REG_BUCK1DVS6,
+	MAX8997_REG_BUCK1DVS7,
+	MAX8997_REG_BUCK1DVS8,
+	MAX8997_REG_BUCK2CTRL,
+	MAX8997_REG_BUCK2DVS1,
+	MAX8997_REG_BUCK2DVS2,
+	MAX8997_REG_BUCK2DVS3,
+	MAX8997_REG_BUCK2DVS4,
+	MAX8997_REG_BUCK2DVS5,
+	MAX8997_REG_BUCK2DVS6,
+	MAX8997_REG_BUCK2DVS7,
+	MAX8997_REG_BUCK2DVS8,
+	MAX8997_REG_BUCK3CTRL,
+	MAX8997_REG_BUCK3DVS,
+	MAX8997_REG_BUCK4CTRL,
+	MAX8997_REG_BUCK4DVS,
+	MAX8997_REG_BUCK5CTRL,
+	MAX8997_REG_BUCK5DVS1,
+	MAX8997_REG_BUCK5DVS2,
+	MAX8997_REG_BUCK5DVS3,
+	MAX8997_REG_BUCK5DVS4,
+	MAX8997_REG_BUCK5DVS5,
+	MAX8997_REG_BUCK5DVS6,
+	MAX8997_REG_BUCK5DVS7,
+	MAX8997_REG_BUCK5DVS8,
+	MAX8997_REG_BUCK6CTRL,
+	MAX8997_REG_BUCK6BPSKIPCTRL,
+	MAX8997_REG_BUCK7CTRL,
+	MAX8997_REG_BUCK7DVS,
+	MAX8997_REG_LDO1CTRL,
+	MAX8997_REG_LDO2CTRL,
+	MAX8997_REG_LDO3CTRL,
+	MAX8997_REG_LDO4CTRL,
+	MAX8997_REG_LDO5CTRL,
+	MAX8997_REG_LDO6CTRL,
+	MAX8997_REG_LDO7CTRL,
+	MAX8997_REG_LDO8CTRL,
+	MAX8997_REG_LDO9CTRL,
+	MAX8997_REG_LDO10CTRL,
+	MAX8997_REG_LDO11CTRL,
+	MAX8997_REG_LDO12CTRL,
+	MAX8997_REG_LDO13CTRL,
+	MAX8997_REG_LDO14CTRL,
+	MAX8997_REG_LDO15CTRL,
+	MAX8997_REG_LDO16CTRL,
+	MAX8997_REG_LDO17CTRL,
+	MAX8997_REG_LDO18CTRL,
+	MAX8997_REG_LDO21CTRL,
+	MAX8997_REG_MBCCTRL1,
+	MAX8997_REG_MBCCTRL2,
+	MAX8997_REG_MBCCTRL3,
+	MAX8997_REG_MBCCTRL4,
+	MAX8997_REG_MBCCTRL5,
+	MAX8997_REG_MBCCTRL6,
+	MAX8997_REG_OTPCGHCVS,
+	MAX8997_REG_SAFEOUTCTRL,
+	MAX8997_REG_LBCNFG1,
+	MAX8997_REG_LBCNFG2,
+	MAX8997_REG_BBCCTRL,
+
+	MAX8997_REG_FLASH1_CUR,
+	MAX8997_REG_FLASH2_CUR,
+	MAX8997_REG_MOVIE_CUR,
+	MAX8997_REG_GSMB_CUR,
+	MAX8997_REG_BOOST_CNTL,
+	MAX8997_REG_LEN_CNTL,
+	MAX8997_REG_FLASH_CNTL,
+	MAX8997_REG_WDT_CNTL,
+	MAX8997_REG_MAXFLASH1,
+	MAX8997_REG_MAXFLASH2,
+	MAX8997_REG_FLASHSTATUSMASK,
+
+	MAX8997_REG_GPIOCNTL1,
+	MAX8997_REG_GPIOCNTL2,
+	MAX8997_REG_GPIOCNTL3,
+	MAX8997_REG_GPIOCNTL4,
+	MAX8997_REG_GPIOCNTL5,
+	MAX8997_REG_GPIOCNTL6,
+	MAX8997_REG_GPIOCNTL7,
+	MAX8997_REG_GPIOCNTL8,
+	MAX8997_REG_GPIOCNTL9,
+	MAX8997_REG_GPIOCNTL10,
+	MAX8997_REG_GPIOCNTL11,
+	MAX8997_REG_GPIOCNTL12,
+
+	MAX8997_REG_LDO1CONFIG,
+	MAX8997_REG_LDO2CONFIG,
+	MAX8997_REG_LDO3CONFIG,
+	MAX8997_REG_LDO4CONFIG,
+	MAX8997_REG_LDO5CONFIG,
+	MAX8997_REG_LDO6CONFIG,
+	MAX8997_REG_LDO7CONFIG,
+	MAX8997_REG_LDO8CONFIG,
+	MAX8997_REG_LDO9CONFIG,
+	MAX8997_REG_LDO10CONFIG,
+	MAX8997_REG_LDO11CONFIG,
+	MAX8997_REG_LDO12CONFIG,
+	MAX8997_REG_LDO13CONFIG,
+	MAX8997_REG_LDO14CONFIG,
+	MAX8997_REG_LDO15CONFIG,
+	MAX8997_REG_LDO16CONFIG,
+	MAX8997_REG_LDO17CONFIG,
+	MAX8997_REG_LDO18CONFIG,
+	MAX8997_REG_LDO21CONFIG,
+
+	MAX8997_REG_DVSOKTIMER1,
+	MAX8997_REG_DVSOKTIMER2,
+	MAX8997_REG_DVSOKTIMER4,
+	MAX8997_REG_DVSOKTIMER5,
+};
+
+u8 max8997_dumpaddr_muic[] = {
+	MAX8997_MUIC_REG_INTMASK1,
+	MAX8997_MUIC_REG_INTMASK2,
+	MAX8997_MUIC_REG_INTMASK3,
+	MAX8997_MUIC_REG_CDETCTRL,
+	MAX8997_MUIC_REG_CONTROL1,
+	MAX8997_MUIC_REG_CONTROL2,
+	MAX8997_MUIC_REG_CONTROL3,
+};
+
+u8 max8997_dumpaddr_haptic[] = {
+	MAX8997_HAPTIC_REG_CONF1,
+	MAX8997_HAPTIC_REG_CONF2,
+	MAX8997_HAPTIC_REG_DRVCONF,
+	MAX8997_HAPTIC_REG_CYCLECONF1,
+	MAX8997_HAPTIC_REG_CYCLECONF2,
+	MAX8997_HAPTIC_REG_SIGCONF1,
+	MAX8997_HAPTIC_REG_SIGCONF2,
+	MAX8997_HAPTIC_REG_SIGCONF3,
+	MAX8997_HAPTIC_REG_SIGCONF4,
+	MAX8997_HAPTIC_REG_SIGDC1,
+	MAX8997_HAPTIC_REG_SIGDC2,
+	MAX8997_HAPTIC_REG_SIGPWMDC1,
+	MAX8997_HAPTIC_REG_SIGPWMDC2,
+	MAX8997_HAPTIC_REG_SIGPWMDC3,
+	MAX8997_HAPTIC_REG_SIGPWMDC4,
+};
+
+static int max8997_freeze(struct device *dev)
+{
+	struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+	struct max8997_dev *max8997 = i2c_get_clientdata(i2c);
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(max8997_dumpaddr_pmic); i++)
+		max8997_read_reg(i2c, max8997_dumpaddr_pmic[i],
+				&max8997->reg_dump[i]);
+
+	for (i = 0; i < ARRAY_SIZE(max8997_dumpaddr_muic); i++)
+		max8997_read_reg(i2c, max8997_dumpaddr_muic[i],
+				&max8997->reg_dump[i + MAX8997_REG_PMIC_END]);
+
+	for (i = 0; i < ARRAY_SIZE(max8997_dumpaddr_haptic); i++)
+		max8997_read_reg(i2c, max8997_dumpaddr_haptic[i],
+				&max8997->reg_dump[i + MAX8997_REG_PMIC_END +
+				MAX8997_MUIC_REG_END]);
+
+	return 0;
+}
+
+static int max8997_restore(struct device *dev)
+{
+	struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+	struct max8997_dev *max8997 = i2c_get_clientdata(i2c);
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(max8997_dumpaddr_pmic); i++)
+		max8997_write_reg(i2c, max8997_dumpaddr_pmic[i],
+				max8997->reg_dump[i]);
+
+	for (i = 0; i < ARRAY_SIZE(max8997_dumpaddr_muic); i++)
+		max8997_write_reg(i2c, max8997_dumpaddr_muic[i],
+				max8997->reg_dump[i + MAX8997_REG_PMIC_END]);
+
+	for (i = 0; i < ARRAY_SIZE(max8997_dumpaddr_haptic); i++)
+		max8997_write_reg(i2c, max8997_dumpaddr_haptic[i],
+				max8997->reg_dump[i + MAX8997_REG_PMIC_END +
+				MAX8997_MUIC_REG_END]);
+
+	return 0;
+}
+
+static int max8997_suspend(struct device *dev)
+{
+	struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+	struct max8997_dev *max8997 = i2c_get_clientdata(i2c);
+
+	if (device_may_wakeup(dev))
+		irq_set_irq_wake(max8997->irq, 1);
+	return 0;
+}
+
+static int max8997_resume(struct device *dev)
+{
+	struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+	struct max8997_dev *max8997 = i2c_get_clientdata(i2c);
+
+	if (device_may_wakeup(dev))
+		irq_set_irq_wake(max8997->irq, 0);
+	return max8997_irq_resume(max8997);
+}
+
+const struct dev_pm_ops max8997_pm = {
+	.suspend = max8997_suspend,
+	.resume = max8997_resume,
+	.freeze = max8997_freeze,
+	.restore = max8997_restore,
+};
+
+static struct i2c_driver max8997_i2c_driver = {
+	.driver = {
+		   .name = "max8997",
+		   .owner = THIS_MODULE,
+		   .pm = &max8997_pm,
+	},
+	.probe = max8997_i2c_probe,
+	.remove = max8997_i2c_remove,
+	.id_table = max8997_i2c_id,
+};
+
+static int __init max8997_i2c_init(void)
+{
+	return i2c_add_driver(&max8997_i2c_driver);
+}
+/* init early so consumer devices can complete system boot */
+subsys_initcall(max8997_i2c_init);
+
+static void __exit max8997_i2c_exit(void)
+{
+	i2c_del_driver(&max8997_i2c_driver);
+}
+module_exit(max8997_i2c_exit);
+
+MODULE_DESCRIPTION("MAXIM 8997 multi-function core driver");
+MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/max8998-irq.c b/ap/os/linux/linux-3.4.x/drivers/mfd/max8998-irq.c
new file mode 100644
index 0000000..5919710
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/max8998-irq.c
@@ -0,0 +1,267 @@
+/*
+ * Interrupt controller support for MAX8998
+ *
+ * Copyright (C) 2010 Samsung Electronics Co.Ltd
+ * Author: Joonyoung Shim <jy0922.shim@samsung.com>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/mfd/max8998-private.h>
+
+struct max8998_irq_data {
+	int reg;
+	int mask;
+};
+
+static struct max8998_irq_data max8998_irqs[] = {
+	[MAX8998_IRQ_DCINF] = {
+		.reg = 1,
+		.mask = MAX8998_IRQ_DCINF_MASK,
+	},
+	[MAX8998_IRQ_DCINR] = {
+		.reg = 1,
+		.mask = MAX8998_IRQ_DCINR_MASK,
+	},
+	[MAX8998_IRQ_JIGF] = {
+		.reg = 1,
+		.mask = MAX8998_IRQ_JIGF_MASK,
+	},
+	[MAX8998_IRQ_JIGR] = {
+		.reg = 1,
+		.mask = MAX8998_IRQ_JIGR_MASK,
+	},
+	[MAX8998_IRQ_PWRONF] = {
+		.reg = 1,
+		.mask = MAX8998_IRQ_PWRONF_MASK,
+	},
+	[MAX8998_IRQ_PWRONR] = {
+		.reg = 1,
+		.mask = MAX8998_IRQ_PWRONR_MASK,
+	},
+	[MAX8998_IRQ_WTSREVNT] = {
+		.reg = 2,
+		.mask = MAX8998_IRQ_WTSREVNT_MASK,
+	},
+	[MAX8998_IRQ_SMPLEVNT] = {
+		.reg = 2,
+		.mask = MAX8998_IRQ_SMPLEVNT_MASK,
+	},
+	[MAX8998_IRQ_ALARM1] = {
+		.reg = 2,
+		.mask = MAX8998_IRQ_ALARM1_MASK,
+	},
+	[MAX8998_IRQ_ALARM0] = {
+		.reg = 2,
+		.mask = MAX8998_IRQ_ALARM0_MASK,
+	},
+	[MAX8998_IRQ_ONKEY1S] = {
+		.reg = 3,
+		.mask = MAX8998_IRQ_ONKEY1S_MASK,
+	},
+	[MAX8998_IRQ_TOPOFFR] = {
+		.reg = 3,
+		.mask = MAX8998_IRQ_TOPOFFR_MASK,
+	},
+	[MAX8998_IRQ_DCINOVPR] = {
+		.reg = 3,
+		.mask = MAX8998_IRQ_DCINOVPR_MASK,
+	},
+	[MAX8998_IRQ_CHGRSTF] = {
+		.reg = 3,
+		.mask = MAX8998_IRQ_CHGRSTF_MASK,
+	},
+	[MAX8998_IRQ_DONER] = {
+		.reg = 3,
+		.mask = MAX8998_IRQ_DONER_MASK,
+	},
+	[MAX8998_IRQ_CHGFAULT] = {
+		.reg = 3,
+		.mask = MAX8998_IRQ_CHGFAULT_MASK,
+	},
+	[MAX8998_IRQ_LOBAT1] = {
+		.reg = 4,
+		.mask = MAX8998_IRQ_LOBAT1_MASK,
+	},
+	[MAX8998_IRQ_LOBAT2] = {
+		.reg = 4,
+		.mask = MAX8998_IRQ_LOBAT2_MASK,
+	},
+};
+
+static inline struct max8998_irq_data *
+irq_to_max8998_irq(struct max8998_dev *max8998, int irq)
+{
+	return &max8998_irqs[irq - max8998->irq_base];
+}
+
+static void max8998_irq_lock(struct irq_data *data)
+{
+	struct max8998_dev *max8998 = irq_data_get_irq_chip_data(data);
+
+	mutex_lock(&max8998->irqlock);
+}
+
+static void max8998_irq_sync_unlock(struct irq_data *data)
+{
+	struct max8998_dev *max8998 = irq_data_get_irq_chip_data(data);
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(max8998->irq_masks_cur); i++) {
+		/*
+		 * If there's been a change in the mask write it back
+		 * to the hardware.
+		 */
+		if (max8998->irq_masks_cur[i] != max8998->irq_masks_cache[i]) {
+			max8998->irq_masks_cache[i] = max8998->irq_masks_cur[i];
+			max8998_write_reg(max8998->i2c, MAX8998_REG_IRQM1 + i,
+					max8998->irq_masks_cur[i]);
+		}
+	}
+
+	mutex_unlock(&max8998->irqlock);
+}
+
+static void max8998_irq_unmask(struct irq_data *data)
+{
+	struct max8998_dev *max8998 = irq_data_get_irq_chip_data(data);
+	struct max8998_irq_data *irq_data = irq_to_max8998_irq(max8998,
+							       data->irq);
+
+	max8998->irq_masks_cur[irq_data->reg - 1] &= ~irq_data->mask;
+}
+
+static void max8998_irq_mask(struct irq_data *data)
+{
+	struct max8998_dev *max8998 = irq_data_get_irq_chip_data(data);
+	struct max8998_irq_data *irq_data = irq_to_max8998_irq(max8998,
+							       data->irq);
+
+	max8998->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask;
+}
+
+static struct irq_chip max8998_irq_chip = {
+	.name = "max8998",
+	.irq_bus_lock = max8998_irq_lock,
+	.irq_bus_sync_unlock = max8998_irq_sync_unlock,
+	.irq_mask = max8998_irq_mask,
+	.irq_unmask = max8998_irq_unmask,
+};
+
+static irqreturn_t max8998_irq_thread(int irq, void *data)
+{
+	struct max8998_dev *max8998 = data;
+	u8 irq_reg[MAX8998_NUM_IRQ_REGS];
+	int ret;
+	int i;
+
+	ret = max8998_bulk_read(max8998->i2c, MAX8998_REG_IRQ1,
+			MAX8998_NUM_IRQ_REGS, irq_reg);
+	if (ret < 0) {
+		dev_err(max8998->dev, "Failed to read interrupt register: %d\n",
+				ret);
+		return IRQ_NONE;
+	}
+
+	/* Apply masking */
+	for (i = 0; i < MAX8998_NUM_IRQ_REGS; i++)
+		irq_reg[i] &= ~max8998->irq_masks_cur[i];
+
+	/* Report */
+	for (i = 0; i < MAX8998_IRQ_NR; i++) {
+		if (irq_reg[max8998_irqs[i].reg - 1] & max8998_irqs[i].mask)
+			handle_nested_irq(max8998->irq_base + i);
+	}
+
+	return IRQ_HANDLED;
+}
+
+int max8998_irq_resume(struct max8998_dev *max8998)
+{
+	if (max8998->irq && max8998->irq_base)
+		max8998_irq_thread(max8998->irq_base, max8998);
+	return 0;
+}
+
+int max8998_irq_init(struct max8998_dev *max8998)
+{
+	int i;
+	int cur_irq;
+	int ret;
+
+	if (!max8998->irq) {
+		dev_warn(max8998->dev,
+			 "No interrupt specified, no interrupts\n");
+		max8998->irq_base = 0;
+		return 0;
+	}
+
+	if (!max8998->irq_base) {
+		dev_err(max8998->dev,
+			"No interrupt base specified, no interrupts\n");
+		return 0;
+	}
+
+	mutex_init(&max8998->irqlock);
+
+	/* Mask the individual interrupt sources */
+	for (i = 0; i < MAX8998_NUM_IRQ_REGS; i++) {
+		max8998->irq_masks_cur[i] = 0xff;
+		max8998->irq_masks_cache[i] = 0xff;
+		max8998_write_reg(max8998->i2c, MAX8998_REG_IRQM1 + i, 0xff);
+	}
+
+	max8998_write_reg(max8998->i2c, MAX8998_REG_STATUSM1, 0xff);
+	max8998_write_reg(max8998->i2c, MAX8998_REG_STATUSM2, 0xff);
+
+	/* register with genirq */
+	for (i = 0; i < MAX8998_IRQ_NR; i++) {
+		cur_irq = i + max8998->irq_base;
+		irq_set_chip_data(cur_irq, max8998);
+		irq_set_chip_and_handler(cur_irq, &max8998_irq_chip,
+					 handle_edge_irq);
+		irq_set_nested_thread(cur_irq, 1);
+#ifdef CONFIG_ARM
+		set_irq_flags(cur_irq, IRQF_VALID);
+#else
+		irq_set_noprobe(cur_irq);
+#endif
+	}
+
+	ret = request_threaded_irq(max8998->irq, NULL, max8998_irq_thread,
+				   IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+				   "max8998-irq", max8998);
+	if (ret) {
+		dev_err(max8998->dev, "Failed to request IRQ %d: %d\n",
+			max8998->irq, ret);
+		return ret;
+	}
+
+	if (!max8998->ono)
+		return 0;
+
+	ret = request_threaded_irq(max8998->ono, NULL, max8998_irq_thread,
+				   IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING |
+				   IRQF_ONESHOT, "max8998-ono", max8998);
+	if (ret)
+		dev_err(max8998->dev, "Failed to request IRQ %d: %d\n",
+			max8998->ono, ret);
+
+	return 0;
+}
+
+void max8998_irq_exit(struct max8998_dev *max8998)
+{
+	if (max8998->ono)
+		free_irq(max8998->ono, max8998);
+
+	if (max8998->irq)
+		free_irq(max8998->irq, max8998);
+}
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/max8998.c b/ap/os/linux/linux-3.4.x/drivers/mfd/max8998.c
new file mode 100644
index 0000000..0a44816
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/max8998.c
@@ -0,0 +1,342 @@
+/*
+ * max8998.c - mfd core driver for the Maxim 8998
+ *
+ *  Copyright (C) 2009-2010 Samsung Electronics
+ *  Kyungmin Park <kyungmin.park@samsung.com>
+ *  Marek Szyprowski <m.szyprowski@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/pm_runtime.h>
+#include <linux/mutex.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/max8998.h>
+#include <linux/mfd/max8998-private.h>
+
+#define RTC_I2C_ADDR		(0x0c >> 1)
+
+static struct mfd_cell max8998_devs[] = {
+	{
+		.name = "max8998-pmic",
+	}, {
+		.name = "max8998-rtc",
+	}, {
+		.name = "max8998-battery",
+	},
+};
+
+static struct mfd_cell lp3974_devs[] = {
+	{
+		.name = "lp3974-pmic",
+	}, {
+		.name = "lp3974-rtc",
+	},
+};
+
+int max8998_read_reg(struct i2c_client *i2c, u8 reg, u8 *dest)
+{
+	struct max8998_dev *max8998 = i2c_get_clientdata(i2c);
+	int ret;
+
+	mutex_lock(&max8998->iolock);
+	ret = i2c_smbus_read_byte_data(i2c, reg);
+	mutex_unlock(&max8998->iolock);
+	if (ret < 0)
+		return ret;
+
+	ret &= 0xff;
+	*dest = ret;
+	return 0;
+}
+EXPORT_SYMBOL(max8998_read_reg);
+
+int max8998_bulk_read(struct i2c_client *i2c, u8 reg, int count, u8 *buf)
+{
+	struct max8998_dev *max8998 = i2c_get_clientdata(i2c);
+	int ret;
+
+	mutex_lock(&max8998->iolock);
+	ret = i2c_smbus_read_i2c_block_data(i2c, reg, count, buf);
+	mutex_unlock(&max8998->iolock);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+EXPORT_SYMBOL(max8998_bulk_read);
+
+int max8998_write_reg(struct i2c_client *i2c, u8 reg, u8 value)
+{
+	struct max8998_dev *max8998 = i2c_get_clientdata(i2c);
+	int ret;
+
+	mutex_lock(&max8998->iolock);
+	ret = i2c_smbus_write_byte_data(i2c, reg, value);
+	mutex_unlock(&max8998->iolock);
+	return ret;
+}
+EXPORT_SYMBOL(max8998_write_reg);
+
+int max8998_bulk_write(struct i2c_client *i2c, u8 reg, int count, u8 *buf)
+{
+	struct max8998_dev *max8998 = i2c_get_clientdata(i2c);
+	int ret;
+
+	mutex_lock(&max8998->iolock);
+	ret = i2c_smbus_write_i2c_block_data(i2c, reg, count, buf);
+	mutex_unlock(&max8998->iolock);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+EXPORT_SYMBOL(max8998_bulk_write);
+
+int max8998_update_reg(struct i2c_client *i2c, u8 reg, u8 val, u8 mask)
+{
+	struct max8998_dev *max8998 = i2c_get_clientdata(i2c);
+	int ret;
+
+	mutex_lock(&max8998->iolock);
+	ret = i2c_smbus_read_byte_data(i2c, reg);
+	if (ret >= 0) {
+		u8 old_val = ret & 0xff;
+		u8 new_val = (val & mask) | (old_val & (~mask));
+		ret = i2c_smbus_write_byte_data(i2c, reg, new_val);
+	}
+	mutex_unlock(&max8998->iolock);
+	return ret;
+}
+EXPORT_SYMBOL(max8998_update_reg);
+
+static int max8998_i2c_probe(struct i2c_client *i2c,
+			    const struct i2c_device_id *id)
+{
+	struct max8998_platform_data *pdata = i2c->dev.platform_data;
+	struct max8998_dev *max8998;
+	int ret = 0;
+
+	max8998 = kzalloc(sizeof(struct max8998_dev), GFP_KERNEL);
+	if (max8998 == NULL)
+		return -ENOMEM;
+
+	i2c_set_clientdata(i2c, max8998);
+	max8998->dev = &i2c->dev;
+	max8998->i2c = i2c;
+	max8998->irq = i2c->irq;
+	max8998->type = id->driver_data;
+	if (pdata) {
+		max8998->ono = pdata->ono;
+		max8998->irq_base = pdata->irq_base;
+		max8998->wakeup = pdata->wakeup;
+	}
+	mutex_init(&max8998->iolock);
+
+	max8998->rtc = i2c_new_dummy(i2c->adapter, RTC_I2C_ADDR);
+	if (!max8998->rtc) {
+		dev_err(&i2c->dev, "Failed to allocate I2C device for RTC\n");
+		return -ENODEV;
+	}
+	i2c_set_clientdata(max8998->rtc, max8998);
+
+	max8998_irq_init(max8998);
+
+	pm_runtime_set_active(max8998->dev);
+
+	switch (id->driver_data) {
+	case TYPE_LP3974:
+		ret = mfd_add_devices(max8998->dev, -1,
+				lp3974_devs, ARRAY_SIZE(lp3974_devs),
+				NULL, 0);
+		break;
+	case TYPE_MAX8998:
+		ret = mfd_add_devices(max8998->dev, -1,
+				max8998_devs, ARRAY_SIZE(max8998_devs),
+				NULL, 0);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	if (ret < 0)
+		goto err;
+
+	device_init_wakeup(max8998->dev, max8998->wakeup);
+
+	return ret;
+
+err:
+	mfd_remove_devices(max8998->dev);
+	max8998_irq_exit(max8998);
+	i2c_unregister_device(max8998->rtc);
+	kfree(max8998);
+	return ret;
+}
+
+static int max8998_i2c_remove(struct i2c_client *i2c)
+{
+	struct max8998_dev *max8998 = i2c_get_clientdata(i2c);
+
+	mfd_remove_devices(max8998->dev);
+	max8998_irq_exit(max8998);
+	i2c_unregister_device(max8998->rtc);
+	kfree(max8998);
+
+	return 0;
+}
+
+static const struct i2c_device_id max8998_i2c_id[] = {
+	{ "max8998", TYPE_MAX8998 },
+	{ "lp3974", TYPE_LP3974},
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, max8998_i2c_id);
+
+static int max8998_suspend(struct device *dev)
+{
+	struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+	struct max8998_dev *max8998 = i2c_get_clientdata(i2c);
+
+	if (device_may_wakeup(dev))
+		irq_set_irq_wake(max8998->irq, 1);
+	return 0;
+}
+
+static int max8998_resume(struct device *dev)
+{
+	struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+	struct max8998_dev *max8998 = i2c_get_clientdata(i2c);
+
+	if (device_may_wakeup(dev))
+		irq_set_irq_wake(max8998->irq, 0);
+	/*
+	 * In LP3974, if IRQ registers are not "read & clear"
+	 * when it's set during sleep, the interrupt becomes
+	 * disabled.
+	 */
+	return max8998_irq_resume(i2c_get_clientdata(i2c));
+}
+
+struct max8998_reg_dump {
+	u8	addr;
+	u8	val;
+};
+#define SAVE_ITEM(x)	{ .addr = (x), .val = 0x0, }
+static struct max8998_reg_dump max8998_dump[] = {
+	SAVE_ITEM(MAX8998_REG_IRQM1),
+	SAVE_ITEM(MAX8998_REG_IRQM2),
+	SAVE_ITEM(MAX8998_REG_IRQM3),
+	SAVE_ITEM(MAX8998_REG_IRQM4),
+	SAVE_ITEM(MAX8998_REG_STATUSM1),
+	SAVE_ITEM(MAX8998_REG_STATUSM2),
+	SAVE_ITEM(MAX8998_REG_CHGR1),
+	SAVE_ITEM(MAX8998_REG_CHGR2),
+	SAVE_ITEM(MAX8998_REG_LDO_ACTIVE_DISCHARGE1),
+	SAVE_ITEM(MAX8998_REG_LDO_ACTIVE_DISCHARGE1),
+	SAVE_ITEM(MAX8998_REG_BUCK_ACTIVE_DISCHARGE3),
+	SAVE_ITEM(MAX8998_REG_ONOFF1),
+	SAVE_ITEM(MAX8998_REG_ONOFF2),
+	SAVE_ITEM(MAX8998_REG_ONOFF3),
+	SAVE_ITEM(MAX8998_REG_ONOFF4),
+	SAVE_ITEM(MAX8998_REG_BUCK1_VOLTAGE1),
+	SAVE_ITEM(MAX8998_REG_BUCK1_VOLTAGE2),
+	SAVE_ITEM(MAX8998_REG_BUCK1_VOLTAGE3),
+	SAVE_ITEM(MAX8998_REG_BUCK1_VOLTAGE4),
+	SAVE_ITEM(MAX8998_REG_BUCK2_VOLTAGE1),
+	SAVE_ITEM(MAX8998_REG_BUCK2_VOLTAGE2),
+	SAVE_ITEM(MAX8998_REG_LDO2_LDO3),
+	SAVE_ITEM(MAX8998_REG_LDO4),
+	SAVE_ITEM(MAX8998_REG_LDO5),
+	SAVE_ITEM(MAX8998_REG_LDO6),
+	SAVE_ITEM(MAX8998_REG_LDO7),
+	SAVE_ITEM(MAX8998_REG_LDO8_LDO9),
+	SAVE_ITEM(MAX8998_REG_LDO10_LDO11),
+	SAVE_ITEM(MAX8998_REG_LDO12),
+	SAVE_ITEM(MAX8998_REG_LDO13),
+	SAVE_ITEM(MAX8998_REG_LDO14),
+	SAVE_ITEM(MAX8998_REG_LDO15),
+	SAVE_ITEM(MAX8998_REG_LDO16),
+	SAVE_ITEM(MAX8998_REG_LDO17),
+	SAVE_ITEM(MAX8998_REG_BKCHR),
+	SAVE_ITEM(MAX8998_REG_LBCNFG1),
+	SAVE_ITEM(MAX8998_REG_LBCNFG2),
+};
+/* Save registers before hibernation */
+static int max8998_freeze(struct device *dev)
+{
+	struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(max8998_dump); i++)
+		max8998_read_reg(i2c, max8998_dump[i].addr,
+				&max8998_dump[i].val);
+
+	return 0;
+}
+
+/* Restore registers after hibernation */
+static int max8998_restore(struct device *dev)
+{
+	struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(max8998_dump); i++)
+		max8998_write_reg(i2c, max8998_dump[i].addr,
+				max8998_dump[i].val);
+
+	return 0;
+}
+
+static const struct dev_pm_ops max8998_pm = {
+	.suspend = max8998_suspend,
+	.resume = max8998_resume,
+	.freeze = max8998_freeze,
+	.restore = max8998_restore,
+};
+
+static struct i2c_driver max8998_i2c_driver = {
+	.driver = {
+		   .name = "max8998",
+		   .owner = THIS_MODULE,
+		   .pm = &max8998_pm,
+	},
+	.probe = max8998_i2c_probe,
+	.remove = max8998_i2c_remove,
+	.id_table = max8998_i2c_id,
+};
+
+static int __init max8998_i2c_init(void)
+{
+	return i2c_add_driver(&max8998_i2c_driver);
+}
+/* init early so consumer devices can complete system boot */
+subsys_initcall(max8998_i2c_init);
+
+static void __exit max8998_i2c_exit(void)
+{
+	i2c_del_driver(&max8998_i2c_driver);
+}
+module_exit(max8998_i2c_exit);
+
+MODULE_DESCRIPTION("MAXIM 8998 multi-function core driver");
+MODULE_AUTHOR("Kyungmin Park <kyungmin.park@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/mc13xxx-core.c b/ap/os/linux/linux-3.4.x/drivers/mfd/mc13xxx-core.c
new file mode 100644
index 0000000..9fd4f63
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/mc13xxx-core.c
@@ -0,0 +1,879 @@
+/*
+ * Copyright 2009-2010 Pengutronix
+ * Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>
+ *
+ * loosely based on an earlier driver that has
+ * Copyright 2009 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/interrupt.h>
+#include <linux/spi/spi.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/mc13xxx.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+
+struct mc13xxx {
+	struct spi_device *spidev;
+	struct mutex lock;
+	int irq;
+	int flags;
+
+	irq_handler_t irqhandler[MC13XXX_NUM_IRQ];
+	void *irqdata[MC13XXX_NUM_IRQ];
+
+	int adcflags;
+};
+
+#define MC13XXX_IRQSTAT0	0
+#define MC13XXX_IRQSTAT0_ADCDONEI	(1 << 0)
+#define MC13XXX_IRQSTAT0_ADCBISDONEI	(1 << 1)
+#define MC13XXX_IRQSTAT0_TSI		(1 << 2)
+#define MC13783_IRQSTAT0_WHIGHI		(1 << 3)
+#define MC13783_IRQSTAT0_WLOWI		(1 << 4)
+#define MC13XXX_IRQSTAT0_CHGDETI	(1 << 6)
+#define MC13783_IRQSTAT0_CHGOVI		(1 << 7)
+#define MC13XXX_IRQSTAT0_CHGREVI	(1 << 8)
+#define MC13XXX_IRQSTAT0_CHGSHORTI	(1 << 9)
+#define MC13XXX_IRQSTAT0_CCCVI		(1 << 10)
+#define MC13XXX_IRQSTAT0_CHGCURRI	(1 << 11)
+#define MC13XXX_IRQSTAT0_BPONI		(1 << 12)
+#define MC13XXX_IRQSTAT0_LOBATLI	(1 << 13)
+#define MC13XXX_IRQSTAT0_LOBATHI	(1 << 14)
+#define MC13783_IRQSTAT0_UDPI		(1 << 15)
+#define MC13783_IRQSTAT0_USBI		(1 << 16)
+#define MC13783_IRQSTAT0_IDI		(1 << 19)
+#define MC13783_IRQSTAT0_SE1I		(1 << 21)
+#define MC13783_IRQSTAT0_CKDETI		(1 << 22)
+#define MC13783_IRQSTAT0_UDMI		(1 << 23)
+
+#define MC13XXX_IRQMASK0	1
+#define MC13XXX_IRQMASK0_ADCDONEM	MC13XXX_IRQSTAT0_ADCDONEI
+#define MC13XXX_IRQMASK0_ADCBISDONEM	MC13XXX_IRQSTAT0_ADCBISDONEI
+#define MC13XXX_IRQMASK0_TSM		MC13XXX_IRQSTAT0_TSI
+#define MC13783_IRQMASK0_WHIGHM		MC13783_IRQSTAT0_WHIGHI
+#define MC13783_IRQMASK0_WLOWM		MC13783_IRQSTAT0_WLOWI
+#define MC13XXX_IRQMASK0_CHGDETM	MC13XXX_IRQSTAT0_CHGDETI
+#define MC13783_IRQMASK0_CHGOVM		MC13783_IRQSTAT0_CHGOVI
+#define MC13XXX_IRQMASK0_CHGREVM	MC13XXX_IRQSTAT0_CHGREVI
+#define MC13XXX_IRQMASK0_CHGSHORTM	MC13XXX_IRQSTAT0_CHGSHORTI
+#define MC13XXX_IRQMASK0_CCCVM		MC13XXX_IRQSTAT0_CCCVI
+#define MC13XXX_IRQMASK0_CHGCURRM	MC13XXX_IRQSTAT0_CHGCURRI
+#define MC13XXX_IRQMASK0_BPONM		MC13XXX_IRQSTAT0_BPONI
+#define MC13XXX_IRQMASK0_LOBATLM	MC13XXX_IRQSTAT0_LOBATLI
+#define MC13XXX_IRQMASK0_LOBATHM	MC13XXX_IRQSTAT0_LOBATHI
+#define MC13783_IRQMASK0_UDPM		MC13783_IRQSTAT0_UDPI
+#define MC13783_IRQMASK0_USBM		MC13783_IRQSTAT0_USBI
+#define MC13783_IRQMASK0_IDM		MC13783_IRQSTAT0_IDI
+#define MC13783_IRQMASK0_SE1M		MC13783_IRQSTAT0_SE1I
+#define MC13783_IRQMASK0_CKDETM		MC13783_IRQSTAT0_CKDETI
+#define MC13783_IRQMASK0_UDMM		MC13783_IRQSTAT0_UDMI
+
+#define MC13XXX_IRQSTAT1	3
+#define MC13XXX_IRQSTAT1_1HZI		(1 << 0)
+#define MC13XXX_IRQSTAT1_TODAI		(1 << 1)
+#define MC13783_IRQSTAT1_ONOFD1I	(1 << 3)
+#define MC13783_IRQSTAT1_ONOFD2I	(1 << 4)
+#define MC13783_IRQSTAT1_ONOFD3I	(1 << 5)
+#define MC13XXX_IRQSTAT1_SYSRSTI	(1 << 6)
+#define MC13XXX_IRQSTAT1_RTCRSTI	(1 << 7)
+#define MC13XXX_IRQSTAT1_PCI		(1 << 8)
+#define MC13XXX_IRQSTAT1_WARMI		(1 << 9)
+#define MC13XXX_IRQSTAT1_MEMHLDI	(1 << 10)
+#define MC13783_IRQSTAT1_PWRRDYI	(1 << 11)
+#define MC13XXX_IRQSTAT1_THWARNLI	(1 << 12)
+#define MC13XXX_IRQSTAT1_THWARNHI	(1 << 13)
+#define MC13XXX_IRQSTAT1_CLKI		(1 << 14)
+#define MC13783_IRQSTAT1_SEMAFI		(1 << 15)
+#define MC13783_IRQSTAT1_MC2BI		(1 << 17)
+#define MC13783_IRQSTAT1_HSDETI		(1 << 18)
+#define MC13783_IRQSTAT1_HSLI		(1 << 19)
+#define MC13783_IRQSTAT1_ALSPTHI	(1 << 20)
+#define MC13783_IRQSTAT1_AHSSHORTI	(1 << 21)
+
+#define MC13XXX_IRQMASK1	4
+#define MC13XXX_IRQMASK1_1HZM		MC13XXX_IRQSTAT1_1HZI
+#define MC13XXX_IRQMASK1_TODAM		MC13XXX_IRQSTAT1_TODAI
+#define MC13783_IRQMASK1_ONOFD1M	MC13783_IRQSTAT1_ONOFD1I
+#define MC13783_IRQMASK1_ONOFD2M	MC13783_IRQSTAT1_ONOFD2I
+#define MC13783_IRQMASK1_ONOFD3M	MC13783_IRQSTAT1_ONOFD3I
+#define MC13XXX_IRQMASK1_SYSRSTM	MC13XXX_IRQSTAT1_SYSRSTI
+#define MC13XXX_IRQMASK1_RTCRSTM	MC13XXX_IRQSTAT1_RTCRSTI
+#define MC13XXX_IRQMASK1_PCM		MC13XXX_IRQSTAT1_PCI
+#define MC13XXX_IRQMASK1_WARMM		MC13XXX_IRQSTAT1_WARMI
+#define MC13XXX_IRQMASK1_MEMHLDM	MC13XXX_IRQSTAT1_MEMHLDI
+#define MC13783_IRQMASK1_PWRRDYM	MC13783_IRQSTAT1_PWRRDYI
+#define MC13XXX_IRQMASK1_THWARNLM	MC13XXX_IRQSTAT1_THWARNLI
+#define MC13XXX_IRQMASK1_THWARNHM	MC13XXX_IRQSTAT1_THWARNHI
+#define MC13XXX_IRQMASK1_CLKM		MC13XXX_IRQSTAT1_CLKI
+#define MC13783_IRQMASK1_SEMAFM		MC13783_IRQSTAT1_SEMAFI
+#define MC13783_IRQMASK1_MC2BM		MC13783_IRQSTAT1_MC2BI
+#define MC13783_IRQMASK1_HSDETM		MC13783_IRQSTAT1_HSDETI
+#define MC13783_IRQMASK1_HSLM		MC13783_IRQSTAT1_HSLI
+#define MC13783_IRQMASK1_ALSPTHM	MC13783_IRQSTAT1_ALSPTHI
+#define MC13783_IRQMASK1_AHSSHORTM	MC13783_IRQSTAT1_AHSSHORTI
+
+#define MC13XXX_REVISION	7
+#define MC13XXX_REVISION_REVMETAL	(0x07 <<  0)
+#define MC13XXX_REVISION_REVFULL	(0x03 <<  3)
+#define MC13XXX_REVISION_ICID		(0x07 <<  6)
+#define MC13XXX_REVISION_FIN		(0x03 <<  9)
+#define MC13XXX_REVISION_FAB		(0x03 << 11)
+#define MC13XXX_REVISION_ICIDCODE	(0x3f << 13)
+
+#define MC13XXX_ADC1		44
+#define MC13XXX_ADC1_ADEN		(1 << 0)
+#define MC13XXX_ADC1_RAND		(1 << 1)
+#define MC13XXX_ADC1_ADSEL		(1 << 3)
+#define MC13XXX_ADC1_ASC		(1 << 20)
+#define MC13XXX_ADC1_ADTRIGIGN		(1 << 21)
+
+#define MC13XXX_ADC2		45
+
+#define MC13XXX_NUMREGS 0x3f
+
+void mc13xxx_lock(struct mc13xxx *mc13xxx)
+{
+	if (!mutex_trylock(&mc13xxx->lock)) {
+		dev_dbg(&mc13xxx->spidev->dev, "wait for %s from %pf\n",
+				__func__, __builtin_return_address(0));
+
+		mutex_lock(&mc13xxx->lock);
+	}
+	dev_dbg(&mc13xxx->spidev->dev, "%s from %pf\n",
+			__func__, __builtin_return_address(0));
+}
+EXPORT_SYMBOL(mc13xxx_lock);
+
+void mc13xxx_unlock(struct mc13xxx *mc13xxx)
+{
+	dev_dbg(&mc13xxx->spidev->dev, "%s from %pf\n",
+			__func__, __builtin_return_address(0));
+	mutex_unlock(&mc13xxx->lock);
+}
+EXPORT_SYMBOL(mc13xxx_unlock);
+
+#define MC13XXX_REGOFFSET_SHIFT 25
+int mc13xxx_reg_read(struct mc13xxx *mc13xxx, unsigned int offset, u32 *val)
+{
+	struct spi_transfer t;
+	struct spi_message m;
+	int ret;
+
+	BUG_ON(!mutex_is_locked(&mc13xxx->lock));
+
+	if (offset > MC13XXX_NUMREGS)
+		return -EINVAL;
+
+	*val = offset << MC13XXX_REGOFFSET_SHIFT;
+
+	memset(&t, 0, sizeof(t));
+
+	t.tx_buf = val;
+	t.rx_buf = val;
+	t.len = sizeof(u32);
+
+	spi_message_init(&m);
+	spi_message_add_tail(&t, &m);
+
+	ret = spi_sync(mc13xxx->spidev, &m);
+
+	/* error in message.status implies error return from spi_sync */
+	BUG_ON(!ret && m.status);
+
+	if (ret)
+		return ret;
+
+	*val &= 0xffffff;
+
+	dev_vdbg(&mc13xxx->spidev->dev, "[0x%02x] -> 0x%06x\n", offset, *val);
+
+	return 0;
+}
+EXPORT_SYMBOL(mc13xxx_reg_read);
+
+int mc13xxx_reg_write(struct mc13xxx *mc13xxx, unsigned int offset, u32 val)
+{
+	u32 buf;
+	struct spi_transfer t;
+	struct spi_message m;
+	int ret;
+
+	BUG_ON(!mutex_is_locked(&mc13xxx->lock));
+
+	dev_vdbg(&mc13xxx->spidev->dev, "[0x%02x] <- 0x%06x\n", offset, val);
+
+	if (offset > MC13XXX_NUMREGS || val > 0xffffff)
+		return -EINVAL;
+
+	buf = 1 << 31 | offset << MC13XXX_REGOFFSET_SHIFT | val;
+
+	memset(&t, 0, sizeof(t));
+
+	t.tx_buf = &buf;
+	t.rx_buf = &buf;
+	t.len = sizeof(u32);
+
+	spi_message_init(&m);
+	spi_message_add_tail(&t, &m);
+
+	ret = spi_sync(mc13xxx->spidev, &m);
+
+	BUG_ON(!ret && m.status);
+
+	if (ret)
+		return ret;
+
+	return 0;
+}
+EXPORT_SYMBOL(mc13xxx_reg_write);
+
+int mc13xxx_reg_rmw(struct mc13xxx *mc13xxx, unsigned int offset,
+		u32 mask, u32 val)
+{
+	int ret;
+	u32 valread;
+
+	BUG_ON(val & ~mask);
+
+	ret = mc13xxx_reg_read(mc13xxx, offset, &valread);
+	if (ret)
+		return ret;
+
+	valread = (valread & ~mask) | val;
+
+	return mc13xxx_reg_write(mc13xxx, offset, valread);
+}
+EXPORT_SYMBOL(mc13xxx_reg_rmw);
+
+int mc13xxx_irq_mask(struct mc13xxx *mc13xxx, int irq)
+{
+	int ret;
+	unsigned int offmask = irq < 24 ? MC13XXX_IRQMASK0 : MC13XXX_IRQMASK1;
+	u32 irqbit = 1 << (irq < 24 ? irq : irq - 24);
+	u32 mask;
+
+	if (irq < 0 || irq >= MC13XXX_NUM_IRQ)
+		return -EINVAL;
+
+	ret = mc13xxx_reg_read(mc13xxx, offmask, &mask);
+	if (ret)
+		return ret;
+
+	if (mask & irqbit)
+		/* already masked */
+		return 0;
+
+	return mc13xxx_reg_write(mc13xxx, offmask, mask | irqbit);
+}
+EXPORT_SYMBOL(mc13xxx_irq_mask);
+
+int mc13xxx_irq_unmask(struct mc13xxx *mc13xxx, int irq)
+{
+	int ret;
+	unsigned int offmask = irq < 24 ? MC13XXX_IRQMASK0 : MC13XXX_IRQMASK1;
+	u32 irqbit = 1 << (irq < 24 ? irq : irq - 24);
+	u32 mask;
+
+	if (irq < 0 || irq >= MC13XXX_NUM_IRQ)
+		return -EINVAL;
+
+	ret = mc13xxx_reg_read(mc13xxx, offmask, &mask);
+	if (ret)
+		return ret;
+
+	if (!(mask & irqbit))
+		/* already unmasked */
+		return 0;
+
+	return mc13xxx_reg_write(mc13xxx, offmask, mask & ~irqbit);
+}
+EXPORT_SYMBOL(mc13xxx_irq_unmask);
+
+int mc13xxx_irq_status(struct mc13xxx *mc13xxx, int irq,
+		int *enabled, int *pending)
+{
+	int ret;
+	unsigned int offmask = irq < 24 ? MC13XXX_IRQMASK0 : MC13XXX_IRQMASK1;
+	unsigned int offstat = irq < 24 ? MC13XXX_IRQSTAT0 : MC13XXX_IRQSTAT1;
+	u32 irqbit = 1 << (irq < 24 ? irq : irq - 24);
+
+	if (irq < 0 || irq >= MC13XXX_NUM_IRQ)
+		return -EINVAL;
+
+	if (enabled) {
+		u32 mask;
+
+		ret = mc13xxx_reg_read(mc13xxx, offmask, &mask);
+		if (ret)
+			return ret;
+
+		*enabled = mask & irqbit;
+	}
+
+	if (pending) {
+		u32 stat;
+
+		ret = mc13xxx_reg_read(mc13xxx, offstat, &stat);
+		if (ret)
+			return ret;
+
+		*pending = stat & irqbit;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(mc13xxx_irq_status);
+
+int mc13xxx_irq_ack(struct mc13xxx *mc13xxx, int irq)
+{
+	unsigned int offstat = irq < 24 ? MC13XXX_IRQSTAT0 : MC13XXX_IRQSTAT1;
+	unsigned int val = 1 << (irq < 24 ? irq : irq - 24);
+
+	BUG_ON(irq < 0 || irq >= MC13XXX_NUM_IRQ);
+
+	return mc13xxx_reg_write(mc13xxx, offstat, val);
+}
+EXPORT_SYMBOL(mc13xxx_irq_ack);
+
+int mc13xxx_irq_request_nounmask(struct mc13xxx *mc13xxx, int irq,
+		irq_handler_t handler, const char *name, void *dev)
+{
+	BUG_ON(!mutex_is_locked(&mc13xxx->lock));
+	BUG_ON(!handler);
+
+	if (irq < 0 || irq >= MC13XXX_NUM_IRQ)
+		return -EINVAL;
+
+	if (mc13xxx->irqhandler[irq])
+		return -EBUSY;
+
+	mc13xxx->irqhandler[irq] = handler;
+	mc13xxx->irqdata[irq] = dev;
+
+	return 0;
+}
+EXPORT_SYMBOL(mc13xxx_irq_request_nounmask);
+
+int mc13xxx_irq_request(struct mc13xxx *mc13xxx, int irq,
+		irq_handler_t handler, const char *name, void *dev)
+{
+	int ret;
+
+	ret = mc13xxx_irq_request_nounmask(mc13xxx, irq, handler, name, dev);
+	if (ret)
+		return ret;
+
+	ret = mc13xxx_irq_unmask(mc13xxx, irq);
+	if (ret) {
+		mc13xxx->irqhandler[irq] = NULL;
+		mc13xxx->irqdata[irq] = NULL;
+		return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(mc13xxx_irq_request);
+
+int mc13xxx_irq_free(struct mc13xxx *mc13xxx, int irq, void *dev)
+{
+	int ret;
+	BUG_ON(!mutex_is_locked(&mc13xxx->lock));
+
+	if (irq < 0 || irq >= MC13XXX_NUM_IRQ || !mc13xxx->irqhandler[irq] ||
+			mc13xxx->irqdata[irq] != dev)
+		return -EINVAL;
+
+	ret = mc13xxx_irq_mask(mc13xxx, irq);
+	if (ret)
+		return ret;
+
+	mc13xxx->irqhandler[irq] = NULL;
+	mc13xxx->irqdata[irq] = NULL;
+
+	return 0;
+}
+EXPORT_SYMBOL(mc13xxx_irq_free);
+
+static inline irqreturn_t mc13xxx_irqhandler(struct mc13xxx *mc13xxx, int irq)
+{
+	return mc13xxx->irqhandler[irq](irq, mc13xxx->irqdata[irq]);
+}
+
+/*
+ * returns: number of handled irqs or negative error
+ * locking: holds mc13xxx->lock
+ */
+static int mc13xxx_irq_handle(struct mc13xxx *mc13xxx,
+		unsigned int offstat, unsigned int offmask, int baseirq)
+{
+	u32 stat, mask;
+	int ret = mc13xxx_reg_read(mc13xxx, offstat, &stat);
+	int num_handled = 0;
+
+	if (ret)
+		return ret;
+
+	ret = mc13xxx_reg_read(mc13xxx, offmask, &mask);
+	if (ret)
+		return ret;
+
+	while (stat & ~mask) {
+		int irq = __ffs(stat & ~mask);
+
+		stat &= ~(1 << irq);
+
+		if (likely(mc13xxx->irqhandler[baseirq + irq])) {
+			irqreturn_t handled;
+
+			handled = mc13xxx_irqhandler(mc13xxx, baseirq + irq);
+			if (handled == IRQ_HANDLED)
+				num_handled++;
+		} else {
+			dev_err(&mc13xxx->spidev->dev,
+					"BUG: irq %u but no handler\n",
+					baseirq + irq);
+
+			mask |= 1 << irq;
+
+			ret = mc13xxx_reg_write(mc13xxx, offmask, mask);
+		}
+	}
+
+	return num_handled;
+}
+
+static irqreturn_t mc13xxx_irq_thread(int irq, void *data)
+{
+	struct mc13xxx *mc13xxx = data;
+	irqreturn_t ret;
+	int handled = 0;
+
+	mc13xxx_lock(mc13xxx);
+
+	ret = mc13xxx_irq_handle(mc13xxx, MC13XXX_IRQSTAT0,
+			MC13XXX_IRQMASK0, 0);
+	if (ret > 0)
+		handled = 1;
+
+	ret = mc13xxx_irq_handle(mc13xxx, MC13XXX_IRQSTAT1,
+			MC13XXX_IRQMASK1, 24);
+	if (ret > 0)
+		handled = 1;
+
+	mc13xxx_unlock(mc13xxx);
+
+	return IRQ_RETVAL(handled);
+}
+
+enum mc13xxx_id {
+	MC13XXX_ID_MC13783,
+	MC13XXX_ID_MC13892,
+	MC13XXX_ID_INVALID,
+};
+
+static const char *mc13xxx_chipname[] = {
+	[MC13XXX_ID_MC13783] = "mc13783",
+	[MC13XXX_ID_MC13892] = "mc13892",
+};
+
+#define maskval(reg, mask)	(((reg) & (mask)) >> __ffs(mask))
+static int mc13xxx_identify(struct mc13xxx *mc13xxx, enum mc13xxx_id *id)
+{
+	u32 icid;
+	u32 revision;
+	const char *name;
+	int ret;
+
+	ret = mc13xxx_reg_read(mc13xxx, 46, &icid);
+	if (ret)
+		return ret;
+
+	icid = (icid >> 6) & 0x7;
+
+	switch (icid) {
+	case 2:
+		*id = MC13XXX_ID_MC13783;
+		name = "mc13783";
+		break;
+	case 7:
+		*id = MC13XXX_ID_MC13892;
+		name = "mc13892";
+		break;
+	default:
+		*id = MC13XXX_ID_INVALID;
+		break;
+	}
+
+	if (*id == MC13XXX_ID_MC13783 || *id == MC13XXX_ID_MC13892) {
+		ret = mc13xxx_reg_read(mc13xxx, MC13XXX_REVISION, &revision);
+		if (ret)
+			return ret;
+
+		dev_info(&mc13xxx->spidev->dev, "%s: rev: %d.%d, "
+				"fin: %d, fab: %d, icid: %d/%d\n",
+				mc13xxx_chipname[*id],
+				maskval(revision, MC13XXX_REVISION_REVFULL),
+				maskval(revision, MC13XXX_REVISION_REVMETAL),
+				maskval(revision, MC13XXX_REVISION_FIN),
+				maskval(revision, MC13XXX_REVISION_FAB),
+				maskval(revision, MC13XXX_REVISION_ICID),
+				maskval(revision, MC13XXX_REVISION_ICIDCODE));
+	}
+
+	if (*id != MC13XXX_ID_INVALID) {
+		const struct spi_device_id *devid =
+			spi_get_device_id(mc13xxx->spidev);
+		if (!devid || devid->driver_data != *id)
+			dev_warn(&mc13xxx->spidev->dev, "device id doesn't "
+					"match auto detection!\n");
+	}
+
+	return 0;
+}
+
+static const char *mc13xxx_get_chipname(struct mc13xxx *mc13xxx)
+{
+	const struct spi_device_id *devid =
+		spi_get_device_id(mc13xxx->spidev);
+
+	if (!devid)
+		return NULL;
+
+	return mc13xxx_chipname[devid->driver_data];
+}
+
+int mc13xxx_get_flags(struct mc13xxx *mc13xxx)
+{
+	return mc13xxx->flags;
+}
+EXPORT_SYMBOL(mc13xxx_get_flags);
+
+#define MC13XXX_ADC1_CHAN0_SHIFT	5
+#define MC13XXX_ADC1_CHAN1_SHIFT	8
+#define MC13783_ADC1_ATO_SHIFT		11
+#define MC13783_ADC1_ATOX		(1 << 19)
+
+struct mc13xxx_adcdone_data {
+	struct mc13xxx *mc13xxx;
+	struct completion done;
+};
+
+static irqreturn_t mc13xxx_handler_adcdone(int irq, void *data)
+{
+	struct mc13xxx_adcdone_data *adcdone_data = data;
+
+	mc13xxx_irq_ack(adcdone_data->mc13xxx, irq);
+
+	complete_all(&adcdone_data->done);
+
+	return IRQ_HANDLED;
+}
+
+#define MC13XXX_ADC_WORKING (1 << 0)
+
+int mc13xxx_adc_do_conversion(struct mc13xxx *mc13xxx, unsigned int mode,
+		unsigned int channel, u8 ato, bool atox,
+		unsigned int *sample)
+{
+	u32 adc0, adc1, old_adc0;
+	int i, ret;
+	struct mc13xxx_adcdone_data adcdone_data = {
+		.mc13xxx = mc13xxx,
+	};
+	init_completion(&adcdone_data.done);
+
+	dev_dbg(&mc13xxx->spidev->dev, "%s\n", __func__);
+
+	mc13xxx_lock(mc13xxx);
+
+	if (mc13xxx->adcflags & MC13XXX_ADC_WORKING) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	mc13xxx->adcflags |= MC13XXX_ADC_WORKING;
+
+	mc13xxx_reg_read(mc13xxx, MC13XXX_ADC0, &old_adc0);
+
+	adc0 = MC13XXX_ADC0_ADINC1 | MC13XXX_ADC0_ADINC2;
+	adc1 = MC13XXX_ADC1_ADEN | MC13XXX_ADC1_ADTRIGIGN | MC13XXX_ADC1_ASC;
+
+	if (channel > 7)
+		adc1 |= MC13XXX_ADC1_ADSEL;
+
+	switch (mode) {
+	case MC13XXX_ADC_MODE_TS:
+		adc0 |= MC13XXX_ADC0_ADREFEN | MC13XXX_ADC0_TSMOD0 |
+			MC13XXX_ADC0_TSMOD1;
+		adc1 |= 4 << MC13XXX_ADC1_CHAN1_SHIFT;
+		break;
+
+	case MC13XXX_ADC_MODE_SINGLE_CHAN:
+		adc0 |= old_adc0 & MC13XXX_ADC0_CONFIG_MASK;
+		adc1 |= (channel & 0x7) << MC13XXX_ADC1_CHAN0_SHIFT;
+		adc1 |= MC13XXX_ADC1_RAND;
+		break;
+
+	case MC13XXX_ADC_MODE_MULT_CHAN:
+		adc0 |= old_adc0 & MC13XXX_ADC0_CONFIG_MASK;
+		adc1 |= 4 << MC13XXX_ADC1_CHAN1_SHIFT;
+		break;
+
+	default:
+		mc13xxx_unlock(mc13xxx);
+		return -EINVAL;
+	}
+
+	adc1 |= ato << MC13783_ADC1_ATO_SHIFT;
+	if (atox)
+		adc1 |= MC13783_ADC1_ATOX;
+	dev_dbg(&mc13xxx->spidev->dev, "%s: request irq\n", __func__);
+	mc13xxx_irq_request(mc13xxx, MC13XXX_IRQ_ADCDONE,
+			mc13xxx_handler_adcdone, __func__, &adcdone_data);
+	mc13xxx_irq_ack(mc13xxx, MC13XXX_IRQ_ADCDONE);
+
+	mc13xxx_reg_write(mc13xxx, MC13XXX_ADC0, adc0);
+	mc13xxx_reg_write(mc13xxx, MC13XXX_ADC1, adc1);
+
+	mc13xxx_unlock(mc13xxx);
+
+	ret = wait_for_completion_interruptible_timeout(&adcdone_data.done, HZ);
+
+	if (!ret)
+		ret = -ETIMEDOUT;
+
+	mc13xxx_lock(mc13xxx);
+
+	mc13xxx_irq_free(mc13xxx, MC13XXX_IRQ_ADCDONE, &adcdone_data);
+
+	if (ret > 0)
+		for (i = 0; i < 4; ++i) {
+			ret = mc13xxx_reg_read(mc13xxx,
+					MC13XXX_ADC2, &sample[i]);
+			if (ret)
+				break;
+		}
+
+	if (mode == MC13XXX_ADC_MODE_TS)
+		/* restore TSMOD */
+		mc13xxx_reg_write(mc13xxx, MC13XXX_ADC0, old_adc0);
+
+	mc13xxx->adcflags &= ~MC13XXX_ADC_WORKING;
+out:
+	mc13xxx_unlock(mc13xxx);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(mc13xxx_adc_do_conversion);
+
+static int mc13xxx_add_subdevice_pdata(struct mc13xxx *mc13xxx,
+		const char *format, void *pdata, size_t pdata_size)
+{
+	char buf[30];
+	const char *name = mc13xxx_get_chipname(mc13xxx);
+
+	struct mfd_cell cell = {
+		.platform_data = pdata,
+		.pdata_size = pdata_size,
+	};
+
+	/* there is no asnprintf in the kernel :-( */
+	if (snprintf(buf, sizeof(buf), format, name) > sizeof(buf))
+		return -E2BIG;
+
+	cell.name = kmemdup(buf, strlen(buf) + 1, GFP_KERNEL);
+	if (!cell.name)
+		return -ENOMEM;
+
+	return mfd_add_devices(&mc13xxx->spidev->dev, -1, &cell, 1, NULL, 0);
+}
+
+static int mc13xxx_add_subdevice(struct mc13xxx *mc13xxx, const char *format)
+{
+	return mc13xxx_add_subdevice_pdata(mc13xxx, format, NULL, 0);
+}
+
+#ifdef CONFIG_OF
+static int mc13xxx_probe_flags_dt(struct mc13xxx *mc13xxx)
+{
+	struct device_node *np = mc13xxx->spidev->dev.of_node;
+
+	if (!np)
+		return -ENODEV;
+
+	if (of_get_property(np, "fsl,mc13xxx-uses-adc", NULL))
+		mc13xxx->flags |= MC13XXX_USE_ADC;
+
+	if (of_get_property(np, "fsl,mc13xxx-uses-codec", NULL))
+		mc13xxx->flags |= MC13XXX_USE_CODEC;
+
+	if (of_get_property(np, "fsl,mc13xxx-uses-rtc", NULL))
+		mc13xxx->flags |= MC13XXX_USE_RTC;
+
+	if (of_get_property(np, "fsl,mc13xxx-uses-touch", NULL))
+		mc13xxx->flags |= MC13XXX_USE_TOUCHSCREEN;
+
+	return 0;
+}
+#else
+static inline int mc13xxx_probe_flags_dt(struct mc13xxx *mc13xxx)
+{
+	return -ENODEV;
+}
+#endif
+
+static const struct spi_device_id mc13xxx_device_id[] = {
+	{
+		.name = "mc13783",
+		.driver_data = MC13XXX_ID_MC13783,
+	}, {
+		.name = "mc13892",
+		.driver_data = MC13XXX_ID_MC13892,
+	}, {
+		/* sentinel */
+	}
+};
+MODULE_DEVICE_TABLE(spi, mc13xxx_device_id);
+
+static const struct of_device_id mc13xxx_dt_ids[] = {
+	{ .compatible = "fsl,mc13783", .data = (void *) MC13XXX_ID_MC13783, },
+	{ .compatible = "fsl,mc13892", .data = (void *) MC13XXX_ID_MC13892, },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mc13xxx_dt_ids);
+
+static int mc13xxx_probe(struct spi_device *spi)
+{
+	const struct of_device_id *of_id;
+	struct spi_driver *sdrv = to_spi_driver(spi->dev.driver);
+	struct mc13xxx *mc13xxx;
+	struct mc13xxx_platform_data *pdata = dev_get_platdata(&spi->dev);
+	enum mc13xxx_id id;
+	int ret;
+
+	of_id = of_match_device(mc13xxx_dt_ids, &spi->dev);
+	if (of_id)
+		sdrv->id_table = &mc13xxx_device_id[(enum mc13xxx_id) of_id->data];
+
+	mc13xxx = kzalloc(sizeof(*mc13xxx), GFP_KERNEL);
+	if (!mc13xxx)
+		return -ENOMEM;
+
+	dev_set_drvdata(&spi->dev, mc13xxx);
+	spi->mode = SPI_MODE_0 | SPI_CS_HIGH;
+	spi->bits_per_word = 32;
+	spi_setup(spi);
+
+	mc13xxx->spidev = spi;
+
+	mutex_init(&mc13xxx->lock);
+	mc13xxx_lock(mc13xxx);
+
+	ret = mc13xxx_identify(mc13xxx, &id);
+	if (ret || id == MC13XXX_ID_INVALID)
+		goto err_revision;
+
+	/* mask all irqs */
+	ret = mc13xxx_reg_write(mc13xxx, MC13XXX_IRQMASK0, 0x00ffffff);
+	if (ret)
+		goto err_mask;
+
+	ret = mc13xxx_reg_write(mc13xxx, MC13XXX_IRQMASK1, 0x00ffffff);
+	if (ret)
+		goto err_mask;
+
+	ret = request_threaded_irq(spi->irq, NULL, mc13xxx_irq_thread,
+			IRQF_ONESHOT | IRQF_TRIGGER_HIGH, "mc13xxx", mc13xxx);
+
+	if (ret) {
+err_mask:
+err_revision:
+		mc13xxx_unlock(mc13xxx);
+		dev_set_drvdata(&spi->dev, NULL);
+		kfree(mc13xxx);
+		return ret;
+	}
+
+	mc13xxx_unlock(mc13xxx);
+
+	if (mc13xxx_probe_flags_dt(mc13xxx) < 0 && pdata)
+		mc13xxx->flags = pdata->flags;
+
+	if (mc13xxx->flags & MC13XXX_USE_ADC)
+		mc13xxx_add_subdevice(mc13xxx, "%s-adc");
+
+	if (mc13xxx->flags & MC13XXX_USE_CODEC)
+		mc13xxx_add_subdevice(mc13xxx, "%s-codec");
+
+	if (mc13xxx->flags & MC13XXX_USE_RTC)
+		mc13xxx_add_subdevice(mc13xxx, "%s-rtc");
+
+	if (mc13xxx->flags & MC13XXX_USE_TOUCHSCREEN)
+		mc13xxx_add_subdevice_pdata(mc13xxx, "%s-ts",
+				&pdata->touch, sizeof(pdata->touch));
+
+	if (pdata) {
+		mc13xxx_add_subdevice_pdata(mc13xxx, "%s-regulator",
+			&pdata->regulators, sizeof(pdata->regulators));
+		mc13xxx_add_subdevice_pdata(mc13xxx, "%s-led",
+				pdata->leds, sizeof(*pdata->leds));
+		mc13xxx_add_subdevice_pdata(mc13xxx, "%s-pwrbutton",
+				pdata->buttons, sizeof(*pdata->buttons));
+	} else {
+		mc13xxx_add_subdevice(mc13xxx, "%s-regulator");
+		mc13xxx_add_subdevice(mc13xxx, "%s-led");
+		mc13xxx_add_subdevice(mc13xxx, "%s-pwrbutton");
+	}
+
+	return 0;
+}
+
+static int __devexit mc13xxx_remove(struct spi_device *spi)
+{
+	struct mc13xxx *mc13xxx = dev_get_drvdata(&spi->dev);
+
+	free_irq(mc13xxx->spidev->irq, mc13xxx);
+
+	mfd_remove_devices(&spi->dev);
+
+	kfree(mc13xxx);
+
+	return 0;
+}
+
+static struct spi_driver mc13xxx_driver = {
+	.id_table = mc13xxx_device_id,
+	.driver = {
+		.name = "mc13xxx",
+		.owner = THIS_MODULE,
+		.of_match_table = mc13xxx_dt_ids,
+	},
+	.probe = mc13xxx_probe,
+	.remove = __devexit_p(mc13xxx_remove),
+};
+
+static int __init mc13xxx_init(void)
+{
+	return spi_register_driver(&mc13xxx_driver);
+}
+subsys_initcall(mc13xxx_init);
+
+static void __exit mc13xxx_exit(void)
+{
+	spi_unregister_driver(&mc13xxx_driver);
+}
+module_exit(mc13xxx_exit);
+
+MODULE_DESCRIPTION("Core driver for Freescale MC13XXX PMIC");
+MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/mcp-core.c b/ap/os/linux/linux-3.4.x/drivers/mfd/mcp-core.c
new file mode 100644
index 0000000..62e5e36
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/mcp-core.c
@@ -0,0 +1,238 @@
+/*
+ *  linux/drivers/mfd/mcp-core.c
+ *
+ *  Copyright (C) 2001 Russell King
+ *
+ * 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.
+ *
+ *  Generic MCP (Multimedia Communications Port) layer.  All MCP locking
+ *  is solely held within this file.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/smp.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/mfd/mcp.h>
+
+
+#define to_mcp(d)		container_of(d, struct mcp, attached_device)
+#define to_mcp_driver(d)	container_of(d, struct mcp_driver, drv)
+
+static int mcp_bus_match(struct device *dev, struct device_driver *drv)
+{
+	return 1;
+}
+
+static int mcp_bus_probe(struct device *dev)
+{
+	struct mcp *mcp = to_mcp(dev);
+	struct mcp_driver *drv = to_mcp_driver(dev->driver);
+
+	return drv->probe(mcp);
+}
+
+static int mcp_bus_remove(struct device *dev)
+{
+	struct mcp *mcp = to_mcp(dev);
+	struct mcp_driver *drv = to_mcp_driver(dev->driver);
+
+	drv->remove(mcp);
+	return 0;
+}
+
+static struct bus_type mcp_bus_type = {
+	.name		= "mcp",
+	.match		= mcp_bus_match,
+	.probe		= mcp_bus_probe,
+	.remove		= mcp_bus_remove,
+};
+
+/**
+ *	mcp_set_telecom_divisor - set the telecom divisor
+ *	@mcp: MCP interface structure
+ *	@div: SIB clock divisor
+ *
+ *	Set the telecom divisor on the MCP interface.  The resulting
+ *	sample rate is SIBCLOCK/div.
+ */
+void mcp_set_telecom_divisor(struct mcp *mcp, unsigned int div)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&mcp->lock, flags);
+	mcp->ops->set_telecom_divisor(mcp, div);
+	spin_unlock_irqrestore(&mcp->lock, flags);
+}
+EXPORT_SYMBOL(mcp_set_telecom_divisor);
+
+/**
+ *	mcp_set_audio_divisor - set the audio divisor
+ *	@mcp: MCP interface structure
+ *	@div: SIB clock divisor
+ *
+ *	Set the audio divisor on the MCP interface.
+ */
+void mcp_set_audio_divisor(struct mcp *mcp, unsigned int div)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&mcp->lock, flags);
+	mcp->ops->set_audio_divisor(mcp, div);
+	spin_unlock_irqrestore(&mcp->lock, flags);
+}
+EXPORT_SYMBOL(mcp_set_audio_divisor);
+
+/**
+ *	mcp_reg_write - write a device register
+ *	@mcp: MCP interface structure
+ *	@reg: 4-bit register index
+ *	@val: 16-bit data value
+ *
+ *	Write a device register.  The MCP interface must be enabled
+ *	to prevent this function hanging.
+ */
+void mcp_reg_write(struct mcp *mcp, unsigned int reg, unsigned int val)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&mcp->lock, flags);
+	mcp->ops->reg_write(mcp, reg, val);
+	spin_unlock_irqrestore(&mcp->lock, flags);
+}
+EXPORT_SYMBOL(mcp_reg_write);
+
+/**
+ *	mcp_reg_read - read a device register
+ *	@mcp: MCP interface structure
+ *	@reg: 4-bit register index
+ *
+ *	Read a device register and return its value.  The MCP interface
+ *	must be enabled to prevent this function hanging.
+ */
+unsigned int mcp_reg_read(struct mcp *mcp, unsigned int reg)
+{
+	unsigned long flags;
+	unsigned int val;
+
+	spin_lock_irqsave(&mcp->lock, flags);
+	val = mcp->ops->reg_read(mcp, reg);
+	spin_unlock_irqrestore(&mcp->lock, flags);
+
+	return val;
+}
+EXPORT_SYMBOL(mcp_reg_read);
+
+/**
+ *	mcp_enable - enable the MCP interface
+ *	@mcp: MCP interface to enable
+ *
+ *	Enable the MCP interface.  Each call to mcp_enable will need
+ *	a corresponding call to mcp_disable to disable the interface.
+ */
+void mcp_enable(struct mcp *mcp)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&mcp->lock, flags);
+	if (mcp->use_count++ == 0)
+		mcp->ops->enable(mcp);
+	spin_unlock_irqrestore(&mcp->lock, flags);
+}
+EXPORT_SYMBOL(mcp_enable);
+
+/**
+ *	mcp_disable - disable the MCP interface
+ *	@mcp: MCP interface to disable
+ *
+ *	Disable the MCP interface.  The MCP interface will only be
+ *	disabled once the number of calls to mcp_enable matches the
+ *	number of calls to mcp_disable.
+ */
+void mcp_disable(struct mcp *mcp)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&mcp->lock, flags);
+	if (--mcp->use_count == 0)
+		mcp->ops->disable(mcp);
+	spin_unlock_irqrestore(&mcp->lock, flags);
+}
+EXPORT_SYMBOL(mcp_disable);
+
+static void mcp_release(struct device *dev)
+{
+	struct mcp *mcp = container_of(dev, struct mcp, attached_device);
+
+	kfree(mcp);
+}
+
+struct mcp *mcp_host_alloc(struct device *parent, size_t size)
+{
+	struct mcp *mcp;
+
+	mcp = kzalloc(sizeof(struct mcp) + size, GFP_KERNEL);
+	if (mcp) {
+		spin_lock_init(&mcp->lock);
+		device_initialize(&mcp->attached_device);
+		mcp->attached_device.parent = parent;
+		mcp->attached_device.bus = &mcp_bus_type;
+		mcp->attached_device.dma_mask = parent->dma_mask;
+		mcp->attached_device.release = mcp_release;
+	}
+	return mcp;
+}
+EXPORT_SYMBOL(mcp_host_alloc);
+
+int mcp_host_add(struct mcp *mcp, void *pdata)
+{
+	mcp->attached_device.platform_data = pdata;
+	dev_set_name(&mcp->attached_device, "mcp0");
+	return device_add(&mcp->attached_device);
+}
+EXPORT_SYMBOL(mcp_host_add);
+
+void mcp_host_del(struct mcp *mcp)
+{
+	device_del(&mcp->attached_device);
+}
+EXPORT_SYMBOL(mcp_host_del);
+
+void mcp_host_free(struct mcp *mcp)
+{
+	put_device(&mcp->attached_device);
+}
+EXPORT_SYMBOL(mcp_host_free);
+
+int mcp_driver_register(struct mcp_driver *mcpdrv)
+{
+	mcpdrv->drv.bus = &mcp_bus_type;
+	return driver_register(&mcpdrv->drv);
+}
+EXPORT_SYMBOL(mcp_driver_register);
+
+void mcp_driver_unregister(struct mcp_driver *mcpdrv)
+{
+	driver_unregister(&mcpdrv->drv);
+}
+EXPORT_SYMBOL(mcp_driver_unregister);
+
+static int __init mcp_init(void)
+{
+	return bus_register(&mcp_bus_type);
+}
+
+static void __exit mcp_exit(void)
+{
+	bus_unregister(&mcp_bus_type);
+}
+
+module_init(mcp_init);
+module_exit(mcp_exit);
+
+MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
+MODULE_DESCRIPTION("Core multimedia communications port driver");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/mcp-sa11x0.c b/ap/os/linux/linux-3.4.x/drivers/mfd/mcp-sa11x0.c
new file mode 100644
index 0000000..c54e244
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/mcp-sa11x0.c
@@ -0,0 +1,319 @@
+/*
+ *  linux/drivers/mfd/mcp-sa11x0.c
+ *
+ *  Copyright (C) 2001-2005 Russell King
+ *
+ * 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.
+ *
+ *  SA11x0 MCP (Multimedia Communications Port) driver.
+ *
+ *  MCP read/write timeouts from Jordi Colomer, rehacked by rmk.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/mfd/mcp.h>
+
+#include <mach/hardware.h>
+#include <asm/mach-types.h>
+#include <mach/mcp.h>
+
+#define DRIVER_NAME "sa11x0-mcp"
+
+struct mcp_sa11x0 {
+	void __iomem	*base0;
+	void __iomem	*base1;
+	u32		mccr0;
+	u32		mccr1;
+};
+
+/* Register offsets */
+#define MCCR0(m)	((m)->base0 + 0x00)
+#define MCDR0(m)	((m)->base0 + 0x08)
+#define MCDR1(m)	((m)->base0 + 0x0c)
+#define MCDR2(m)	((m)->base0 + 0x10)
+#define MCSR(m)		((m)->base0 + 0x18)
+#define MCCR1(m)	((m)->base1 + 0x00)
+
+#define priv(mcp)	((struct mcp_sa11x0 *)mcp_priv(mcp))
+
+static void
+mcp_sa11x0_set_telecom_divisor(struct mcp *mcp, unsigned int divisor)
+{
+	struct mcp_sa11x0 *m = priv(mcp);
+
+	divisor /= 32;
+
+	m->mccr0 &= ~0x00007f00;
+	m->mccr0 |= divisor << 8;
+	writel_relaxed(m->mccr0, MCCR0(m));
+}
+
+static void
+mcp_sa11x0_set_audio_divisor(struct mcp *mcp, unsigned int divisor)
+{
+	struct mcp_sa11x0 *m = priv(mcp);
+
+	divisor /= 32;
+
+	m->mccr0 &= ~0x0000007f;
+	m->mccr0 |= divisor;
+	writel_relaxed(m->mccr0, MCCR0(m));
+}
+
+/*
+ * Write data to the device.  The bit should be set after 3 subframe
+ * times (each frame is 64 clocks).  We wait a maximum of 6 subframes.
+ * We really should try doing something more productive while we
+ * wait.
+ */
+static void
+mcp_sa11x0_write(struct mcp *mcp, unsigned int reg, unsigned int val)
+{
+	struct mcp_sa11x0 *m = priv(mcp);
+	int ret = -ETIME;
+	int i;
+
+	writel_relaxed(reg << 17 | MCDR2_Wr | (val & 0xffff), MCDR2(m));
+
+	for (i = 0; i < 2; i++) {
+		udelay(mcp->rw_timeout);
+		if (readl_relaxed(MCSR(m)) & MCSR_CWC) {
+			ret = 0;
+			break;
+		}
+	}
+
+	if (ret < 0)
+		printk(KERN_WARNING "mcp: write timed out\n");
+}
+
+/*
+ * Read data from the device.  The bit should be set after 3 subframe
+ * times (each frame is 64 clocks).  We wait a maximum of 6 subframes.
+ * We really should try doing something more productive while we
+ * wait.
+ */
+static unsigned int
+mcp_sa11x0_read(struct mcp *mcp, unsigned int reg)
+{
+	struct mcp_sa11x0 *m = priv(mcp);
+	int ret = -ETIME;
+	int i;
+
+	writel_relaxed(reg << 17 | MCDR2_Rd, MCDR2(m));
+
+	for (i = 0; i < 2; i++) {
+		udelay(mcp->rw_timeout);
+		if (readl_relaxed(MCSR(m)) & MCSR_CRC) {
+			ret = readl_relaxed(MCDR2(m)) & 0xffff;
+			break;
+		}
+	}
+
+	if (ret < 0)
+		printk(KERN_WARNING "mcp: read timed out\n");
+
+	return ret;
+}
+
+static void mcp_sa11x0_enable(struct mcp *mcp)
+{
+	struct mcp_sa11x0 *m = priv(mcp);
+
+	writel(-1, MCSR(m));
+	m->mccr0 |= MCCR0_MCE;
+	writel_relaxed(m->mccr0, MCCR0(m));
+}
+
+static void mcp_sa11x0_disable(struct mcp *mcp)
+{
+	struct mcp_sa11x0 *m = priv(mcp);
+
+	m->mccr0 &= ~MCCR0_MCE;
+	writel_relaxed(m->mccr0, MCCR0(m));
+}
+
+/*
+ * Our methods.
+ */
+static struct mcp_ops mcp_sa11x0 = {
+	.set_telecom_divisor	= mcp_sa11x0_set_telecom_divisor,
+	.set_audio_divisor	= mcp_sa11x0_set_audio_divisor,
+	.reg_write		= mcp_sa11x0_write,
+	.reg_read		= mcp_sa11x0_read,
+	.enable			= mcp_sa11x0_enable,
+	.disable		= mcp_sa11x0_disable,
+};
+
+static int mcp_sa11x0_probe(struct platform_device *dev)
+{
+	struct mcp_plat_data *data = dev->dev.platform_data;
+	struct resource *mem0, *mem1;
+	struct mcp_sa11x0 *m;
+	struct mcp *mcp;
+	int ret;
+
+	if (!data)
+		return -ENODEV;
+
+	mem0 = platform_get_resource(dev, IORESOURCE_MEM, 0);
+	mem1 = platform_get_resource(dev, IORESOURCE_MEM, 1);
+	if (!mem0 || !mem1)
+		return -ENXIO;
+
+	if (!request_mem_region(mem0->start, resource_size(mem0),
+				DRIVER_NAME)) {
+		ret = -EBUSY;
+		goto err_mem0;
+	}
+
+	if (!request_mem_region(mem1->start, resource_size(mem1),
+				DRIVER_NAME)) {
+		ret = -EBUSY;
+		goto err_mem1;
+	}
+
+	mcp = mcp_host_alloc(&dev->dev, sizeof(struct mcp_sa11x0));
+	if (!mcp) {
+		ret = -ENOMEM;
+		goto err_alloc;
+	}
+
+	mcp->owner		= THIS_MODULE;
+	mcp->ops		= &mcp_sa11x0;
+	mcp->sclk_rate		= data->sclk_rate;
+
+	m = priv(mcp);
+	m->mccr0 = data->mccr0 | 0x7f7f;
+	m->mccr1 = data->mccr1;
+
+	m->base0 = ioremap(mem0->start, resource_size(mem0));
+	m->base1 = ioremap(mem1->start, resource_size(mem1));
+	if (!m->base0 || !m->base1) {
+		ret = -ENOMEM;
+		goto err_ioremap;
+	}
+
+	platform_set_drvdata(dev, mcp);
+
+	/*
+	 * Initialise device.  Note that we initially
+	 * set the sampling rate to minimum.
+	 */
+	writel_relaxed(-1, MCSR(m));
+	writel_relaxed(m->mccr1, MCCR1(m));
+	writel_relaxed(m->mccr0, MCCR0(m));
+
+	/*
+	 * Calculate the read/write timeout (us) from the bit clock
+	 * rate.  This is the period for 3 64-bit frames.  Always
+	 * round this time up.
+	 */
+	mcp->rw_timeout = (64 * 3 * 1000000 + mcp->sclk_rate - 1) /
+			  mcp->sclk_rate;
+
+	ret = mcp_host_add(mcp, data->codec_pdata);
+	if (ret == 0)
+		return 0;
+
+	platform_set_drvdata(dev, NULL);
+
+ err_ioremap:
+	iounmap(m->base1);
+	iounmap(m->base0);
+	mcp_host_free(mcp);
+ err_alloc:
+	release_mem_region(mem1->start, resource_size(mem1));
+ err_mem1:
+	release_mem_region(mem0->start, resource_size(mem0));
+ err_mem0:
+	return ret;
+}
+
+static int mcp_sa11x0_remove(struct platform_device *dev)
+{
+	struct mcp *mcp = platform_get_drvdata(dev);
+	struct mcp_sa11x0 *m = priv(mcp);
+	struct resource *mem0, *mem1;
+
+	if (m->mccr0 & MCCR0_MCE)
+		dev_warn(&dev->dev,
+			 "device left active (missing disable call?)\n");
+
+	mem0 = platform_get_resource(dev, IORESOURCE_MEM, 0);
+	mem1 = platform_get_resource(dev, IORESOURCE_MEM, 1);
+
+	platform_set_drvdata(dev, NULL);
+	mcp_host_del(mcp);
+	iounmap(m->base1);
+	iounmap(m->base0);
+	mcp_host_free(mcp);
+	release_mem_region(mem1->start, resource_size(mem1));
+	release_mem_region(mem0->start, resource_size(mem0));
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int mcp_sa11x0_suspend(struct device *dev)
+{
+	struct mcp_sa11x0 *m = priv(dev_get_drvdata(dev));
+
+	if (m->mccr0 & MCCR0_MCE)
+		dev_warn(dev, "device left active (missing disable call?)\n");
+
+	writel(m->mccr0 & ~MCCR0_MCE, MCCR0(m));
+
+	return 0;
+}
+
+static int mcp_sa11x0_resume(struct device *dev)
+{
+	struct mcp_sa11x0 *m = priv(dev_get_drvdata(dev));
+
+	writel_relaxed(m->mccr1, MCCR1(m));
+	writel_relaxed(m->mccr0, MCCR0(m));
+
+	return 0;
+}
+#endif
+
+static const struct dev_pm_ops mcp_sa11x0_pm_ops = {
+#ifdef CONFIG_PM_SLEEP
+	.suspend = mcp_sa11x0_suspend,
+	.freeze = mcp_sa11x0_suspend,
+	.poweroff = mcp_sa11x0_suspend,
+	.resume_noirq = mcp_sa11x0_resume,
+	.thaw_noirq = mcp_sa11x0_resume,
+	.restore_noirq = mcp_sa11x0_resume,
+#endif
+};
+
+static struct platform_driver mcp_sa11x0_driver = {
+	.probe		= mcp_sa11x0_probe,
+	.remove		= mcp_sa11x0_remove,
+	.driver		= {
+		.name	= DRIVER_NAME,
+		.owner	= THIS_MODULE,
+		.pm	= &mcp_sa11x0_pm_ops,
+	},
+};
+
+/*
+ * This needs re-working
+ */
+module_platform_driver(mcp_sa11x0_driver);
+
+MODULE_ALIAS("platform:" DRIVER_NAME);
+MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
+MODULE_DESCRIPTION("SA11x0 multimedia communications port driver");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/menelaus.c b/ap/os/linux/linux-3.4.x/drivers/mfd/menelaus.c
new file mode 100644
index 0000000..cb4910a
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/menelaus.c
@@ -0,0 +1,1319 @@
+/*
+ * Copyright (C) 2004 Texas Instruments, Inc.
+ *
+ * Some parts based tps65010.c:
+ * Copyright (C) 2004 Texas Instruments and
+ * Copyright (C) 2004-2005 David Brownell
+ *
+ * Some parts based on tlv320aic24.c:
+ * Copyright (C) by Kai Svahn <kai.svahn@nokia.com>
+ *
+ * Changes for interrupt handling and clean-up by
+ * Tony Lindgren <tony@atomide.com> and Imre Deak <imre.deak@nokia.com>
+ * Cleanup and generalized support for voltage setting by
+ * Juha Yrjola
+ * Added support for controlling VCORE and regulator sleep states,
+ * Amit Kucheria <amit.kucheria@nokia.com>
+ * Copyright (C) 2005, 2006 Nokia Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+#include <linux/slab.h>
+
+#include <asm/mach/irq.h>
+
+#include <asm/gpio.h>
+#include <plat/menelaus.h>
+
+#define DRIVER_NAME			"menelaus"
+
+#define MENELAUS_I2C_ADDRESS		0x72
+
+#define MENELAUS_REV			0x01
+#define MENELAUS_VCORE_CTRL1		0x02
+#define MENELAUS_VCORE_CTRL2		0x03
+#define MENELAUS_VCORE_CTRL3		0x04
+#define MENELAUS_VCORE_CTRL4		0x05
+#define MENELAUS_VCORE_CTRL5		0x06
+#define MENELAUS_DCDC_CTRL1		0x07
+#define MENELAUS_DCDC_CTRL2		0x08
+#define MENELAUS_DCDC_CTRL3		0x09
+#define MENELAUS_LDO_CTRL1		0x0A
+#define MENELAUS_LDO_CTRL2		0x0B
+#define MENELAUS_LDO_CTRL3		0x0C
+#define MENELAUS_LDO_CTRL4		0x0D
+#define MENELAUS_LDO_CTRL5		0x0E
+#define MENELAUS_LDO_CTRL6		0x0F
+#define MENELAUS_LDO_CTRL7		0x10
+#define MENELAUS_LDO_CTRL8		0x11
+#define MENELAUS_SLEEP_CTRL1		0x12
+#define MENELAUS_SLEEP_CTRL2		0x13
+#define MENELAUS_DEVICE_OFF		0x14
+#define MENELAUS_OSC_CTRL		0x15
+#define MENELAUS_DETECT_CTRL		0x16
+#define MENELAUS_INT_MASK1		0x17
+#define MENELAUS_INT_MASK2		0x18
+#define MENELAUS_INT_STATUS1		0x19
+#define MENELAUS_INT_STATUS2		0x1A
+#define MENELAUS_INT_ACK1		0x1B
+#define MENELAUS_INT_ACK2		0x1C
+#define MENELAUS_GPIO_CTRL		0x1D
+#define MENELAUS_GPIO_IN		0x1E
+#define MENELAUS_GPIO_OUT		0x1F
+#define MENELAUS_BBSMS			0x20
+#define MENELAUS_RTC_CTRL		0x21
+#define MENELAUS_RTC_UPDATE		0x22
+#define MENELAUS_RTC_SEC		0x23
+#define MENELAUS_RTC_MIN		0x24
+#define MENELAUS_RTC_HR			0x25
+#define MENELAUS_RTC_DAY		0x26
+#define MENELAUS_RTC_MON		0x27
+#define MENELAUS_RTC_YR			0x28
+#define MENELAUS_RTC_WKDAY		0x29
+#define MENELAUS_RTC_AL_SEC		0x2A
+#define MENELAUS_RTC_AL_MIN		0x2B
+#define MENELAUS_RTC_AL_HR		0x2C
+#define MENELAUS_RTC_AL_DAY		0x2D
+#define MENELAUS_RTC_AL_MON		0x2E
+#define MENELAUS_RTC_AL_YR		0x2F
+#define MENELAUS_RTC_COMP_MSB		0x30
+#define MENELAUS_RTC_COMP_LSB		0x31
+#define MENELAUS_S1_PULL_EN		0x32
+#define MENELAUS_S1_PULL_DIR		0x33
+#define MENELAUS_S2_PULL_EN		0x34
+#define MENELAUS_S2_PULL_DIR		0x35
+#define MENELAUS_MCT_CTRL1		0x36
+#define MENELAUS_MCT_CTRL2		0x37
+#define MENELAUS_MCT_CTRL3		0x38
+#define MENELAUS_MCT_PIN_ST		0x39
+#define MENELAUS_DEBOUNCE1		0x3A
+
+#define IH_MENELAUS_IRQS		12
+#define MENELAUS_MMC_S1CD_IRQ		0	/* MMC slot 1 card change */
+#define MENELAUS_MMC_S2CD_IRQ		1	/* MMC slot 2 card change */
+#define MENELAUS_MMC_S1D1_IRQ		2	/* MMC DAT1 low in slot 1 */
+#define MENELAUS_MMC_S2D1_IRQ		3	/* MMC DAT1 low in slot 2 */
+#define MENELAUS_LOWBAT_IRQ		4	/* Low battery */
+#define MENELAUS_HOTDIE_IRQ		5	/* Hot die detect */
+#define MENELAUS_UVLO_IRQ		6	/* UVLO detect */
+#define MENELAUS_TSHUT_IRQ		7	/* Thermal shutdown */
+#define MENELAUS_RTCTMR_IRQ		8	/* RTC timer */
+#define MENELAUS_RTCALM_IRQ		9	/* RTC alarm */
+#define MENELAUS_RTCERR_IRQ		10	/* RTC error */
+#define MENELAUS_PSHBTN_IRQ		11	/* Push button */
+#define MENELAUS_RESERVED12_IRQ		12	/* Reserved */
+#define MENELAUS_RESERVED13_IRQ		13	/* Reserved */
+#define MENELAUS_RESERVED14_IRQ		14	/* Reserved */
+#define MENELAUS_RESERVED15_IRQ		15	/* Reserved */
+
+/* VCORE_CTRL1 register */
+#define VCORE_CTRL1_BYP_COMP		(1 << 5)
+#define VCORE_CTRL1_HW_NSW		(1 << 7)
+
+/* GPIO_CTRL register */
+#define GPIO_CTRL_SLOTSELEN		(1 << 5)
+#define GPIO_CTRL_SLPCTLEN		(1 << 6)
+#define GPIO1_DIR_INPUT			(1 << 0)
+#define GPIO2_DIR_INPUT			(1 << 1)
+#define GPIO3_DIR_INPUT			(1 << 2)
+
+/* MCT_CTRL1 register */
+#define MCT_CTRL1_S1_CMD_OD		(1 << 2)
+#define MCT_CTRL1_S2_CMD_OD		(1 << 3)
+
+/* MCT_CTRL2 register */
+#define MCT_CTRL2_VS2_SEL_D0		(1 << 0)
+#define MCT_CTRL2_VS2_SEL_D1		(1 << 1)
+#define MCT_CTRL2_S1CD_BUFEN		(1 << 4)
+#define MCT_CTRL2_S2CD_BUFEN		(1 << 5)
+#define MCT_CTRL2_S1CD_DBEN		(1 << 6)
+#define MCT_CTRL2_S2CD_BEN		(1 << 7)
+
+/* MCT_CTRL3 register */
+#define MCT_CTRL3_SLOT1_EN		(1 << 0)
+#define MCT_CTRL3_SLOT2_EN		(1 << 1)
+#define MCT_CTRL3_S1_AUTO_EN		(1 << 2)
+#define MCT_CTRL3_S2_AUTO_EN		(1 << 3)
+
+/* MCT_PIN_ST register */
+#define MCT_PIN_ST_S1_CD_ST		(1 << 0)
+#define MCT_PIN_ST_S2_CD_ST		(1 << 1)
+
+static void menelaus_work(struct work_struct *_menelaus);
+
+struct menelaus_chip {
+	struct mutex		lock;
+	struct i2c_client	*client;
+	struct work_struct	work;
+#ifdef CONFIG_RTC_DRV_TWL92330
+	struct rtc_device	*rtc;
+	u8			rtc_control;
+	unsigned		uie:1;
+#endif
+	unsigned		vcore_hw_mode:1;
+	u8			mask1, mask2;
+	void			(*handlers[16])(struct menelaus_chip *);
+	void			(*mmc_callback)(void *data, u8 mask);
+	void			*mmc_callback_data;
+};
+
+static struct menelaus_chip *the_menelaus;
+
+static int menelaus_write_reg(int reg, u8 value)
+{
+	int val = i2c_smbus_write_byte_data(the_menelaus->client, reg, value);
+
+	if (val < 0) {
+		pr_err(DRIVER_NAME ": write error");
+		return val;
+	}
+
+	return 0;
+}
+
+static int menelaus_read_reg(int reg)
+{
+	int val = i2c_smbus_read_byte_data(the_menelaus->client, reg);
+
+	if (val < 0)
+		pr_err(DRIVER_NAME ": read error");
+
+	return val;
+}
+
+static int menelaus_enable_irq(int irq)
+{
+	if (irq > 7) {
+		irq -= 8;
+		the_menelaus->mask2 &= ~(1 << irq);
+		return menelaus_write_reg(MENELAUS_INT_MASK2,
+				the_menelaus->mask2);
+	} else {
+		the_menelaus->mask1 &= ~(1 << irq);
+		return menelaus_write_reg(MENELAUS_INT_MASK1,
+				the_menelaus->mask1);
+	}
+}
+
+static int menelaus_disable_irq(int irq)
+{
+	if (irq > 7) {
+		irq -= 8;
+		the_menelaus->mask2 |= (1 << irq);
+		return menelaus_write_reg(MENELAUS_INT_MASK2,
+				the_menelaus->mask2);
+	} else {
+		the_menelaus->mask1 |= (1 << irq);
+		return menelaus_write_reg(MENELAUS_INT_MASK1,
+				the_menelaus->mask1);
+	}
+}
+
+static int menelaus_ack_irq(int irq)
+{
+	if (irq > 7)
+		return menelaus_write_reg(MENELAUS_INT_ACK2, 1 << (irq - 8));
+	else
+		return menelaus_write_reg(MENELAUS_INT_ACK1, 1 << irq);
+}
+
+/* Adds a handler for an interrupt. Does not run in interrupt context */
+static int menelaus_add_irq_work(int irq,
+		void (*handler)(struct menelaus_chip *))
+{
+	int ret = 0;
+
+	mutex_lock(&the_menelaus->lock);
+	the_menelaus->handlers[irq] = handler;
+	ret = menelaus_enable_irq(irq);
+	mutex_unlock(&the_menelaus->lock);
+
+	return ret;
+}
+
+/* Removes handler for an interrupt */
+static int menelaus_remove_irq_work(int irq)
+{
+	int ret = 0;
+
+	mutex_lock(&the_menelaus->lock);
+	ret = menelaus_disable_irq(irq);
+	the_menelaus->handlers[irq] = NULL;
+	mutex_unlock(&the_menelaus->lock);
+
+	return ret;
+}
+
+/*
+ * Gets scheduled when a card detect interrupt happens. Note that in some cases
+ * this line is wired to card cover switch rather than the card detect switch
+ * in each slot. In this case the cards are not seen by menelaus.
+ * FIXME: Add handling for D1 too
+ */
+static void menelaus_mmc_cd_work(struct menelaus_chip *menelaus_hw)
+{
+	int reg;
+	unsigned char card_mask = 0;
+
+	reg = menelaus_read_reg(MENELAUS_MCT_PIN_ST);
+	if (reg < 0)
+		return;
+
+	if (!(reg & 0x1))
+		card_mask |= MCT_PIN_ST_S1_CD_ST;
+
+	if (!(reg & 0x2))
+		card_mask |= MCT_PIN_ST_S2_CD_ST;
+
+	if (menelaus_hw->mmc_callback)
+		menelaus_hw->mmc_callback(menelaus_hw->mmc_callback_data,
+					  card_mask);
+}
+
+/*
+ * Toggles the MMC slots between open-drain and push-pull mode.
+ */
+int menelaus_set_mmc_opendrain(int slot, int enable)
+{
+	int ret, val;
+
+	if (slot != 1 && slot != 2)
+		return -EINVAL;
+	mutex_lock(&the_menelaus->lock);
+	ret = menelaus_read_reg(MENELAUS_MCT_CTRL1);
+	if (ret < 0) {
+		mutex_unlock(&the_menelaus->lock);
+		return ret;
+	}
+	val = ret;
+	if (slot == 1) {
+		if (enable)
+			val |= MCT_CTRL1_S1_CMD_OD;
+		else
+			val &= ~MCT_CTRL1_S1_CMD_OD;
+	} else {
+		if (enable)
+			val |= MCT_CTRL1_S2_CMD_OD;
+		else
+			val &= ~MCT_CTRL1_S2_CMD_OD;
+	}
+	ret = menelaus_write_reg(MENELAUS_MCT_CTRL1, val);
+	mutex_unlock(&the_menelaus->lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(menelaus_set_mmc_opendrain);
+
+int menelaus_set_slot_sel(int enable)
+{
+	int ret;
+
+	mutex_lock(&the_menelaus->lock);
+	ret = menelaus_read_reg(MENELAUS_GPIO_CTRL);
+	if (ret < 0)
+		goto out;
+	ret |= GPIO2_DIR_INPUT;
+	if (enable)
+		ret |= GPIO_CTRL_SLOTSELEN;
+	else
+		ret &= ~GPIO_CTRL_SLOTSELEN;
+	ret = menelaus_write_reg(MENELAUS_GPIO_CTRL, ret);
+out:
+	mutex_unlock(&the_menelaus->lock);
+	return ret;
+}
+EXPORT_SYMBOL(menelaus_set_slot_sel);
+
+int menelaus_set_mmc_slot(int slot, int enable, int power, int cd_en)
+{
+	int ret, val;
+
+	if (slot != 1 && slot != 2)
+		return -EINVAL;
+	if (power >= 3)
+		return -EINVAL;
+
+	mutex_lock(&the_menelaus->lock);
+
+	ret = menelaus_read_reg(MENELAUS_MCT_CTRL2);
+	if (ret < 0)
+		goto out;
+	val = ret;
+	if (slot == 1) {
+		if (cd_en)
+			val |= MCT_CTRL2_S1CD_BUFEN | MCT_CTRL2_S1CD_DBEN;
+		else
+			val &= ~(MCT_CTRL2_S1CD_BUFEN | MCT_CTRL2_S1CD_DBEN);
+	} else {
+		if (cd_en)
+			val |= MCT_CTRL2_S2CD_BUFEN | MCT_CTRL2_S2CD_BEN;
+		else
+			val &= ~(MCT_CTRL2_S2CD_BUFEN | MCT_CTRL2_S2CD_BEN);
+	}
+	ret = menelaus_write_reg(MENELAUS_MCT_CTRL2, val);
+	if (ret < 0)
+		goto out;
+
+	ret = menelaus_read_reg(MENELAUS_MCT_CTRL3);
+	if (ret < 0)
+		goto out;
+	val = ret;
+	if (slot == 1) {
+		if (enable)
+			val |= MCT_CTRL3_SLOT1_EN;
+		else
+			val &= ~MCT_CTRL3_SLOT1_EN;
+	} else {
+		int b;
+
+		if (enable)
+			val |= MCT_CTRL3_SLOT2_EN;
+		else
+			val &= ~MCT_CTRL3_SLOT2_EN;
+		b = menelaus_read_reg(MENELAUS_MCT_CTRL2);
+		b &= ~(MCT_CTRL2_VS2_SEL_D0 | MCT_CTRL2_VS2_SEL_D1);
+		b |= power;
+		ret = menelaus_write_reg(MENELAUS_MCT_CTRL2, b);
+		if (ret < 0)
+			goto out;
+	}
+	/* Disable autonomous shutdown */
+	val &= ~(MCT_CTRL3_S1_AUTO_EN | MCT_CTRL3_S2_AUTO_EN);
+	ret = menelaus_write_reg(MENELAUS_MCT_CTRL3, val);
+out:
+	mutex_unlock(&the_menelaus->lock);
+	return ret;
+}
+EXPORT_SYMBOL(menelaus_set_mmc_slot);
+
+int menelaus_register_mmc_callback(void (*callback)(void *data, u8 card_mask),
+				   void *data)
+{
+	int ret = 0;
+
+	the_menelaus->mmc_callback_data = data;
+	the_menelaus->mmc_callback = callback;
+	ret = menelaus_add_irq_work(MENELAUS_MMC_S1CD_IRQ,
+				    menelaus_mmc_cd_work);
+	if (ret < 0)
+		return ret;
+	ret = menelaus_add_irq_work(MENELAUS_MMC_S2CD_IRQ,
+				    menelaus_mmc_cd_work);
+	if (ret < 0)
+		return ret;
+	ret = menelaus_add_irq_work(MENELAUS_MMC_S1D1_IRQ,
+				    menelaus_mmc_cd_work);
+	if (ret < 0)
+		return ret;
+	ret = menelaus_add_irq_work(MENELAUS_MMC_S2D1_IRQ,
+				    menelaus_mmc_cd_work);
+
+	return ret;
+}
+EXPORT_SYMBOL(menelaus_register_mmc_callback);
+
+void menelaus_unregister_mmc_callback(void)
+{
+	menelaus_remove_irq_work(MENELAUS_MMC_S1CD_IRQ);
+	menelaus_remove_irq_work(MENELAUS_MMC_S2CD_IRQ);
+	menelaus_remove_irq_work(MENELAUS_MMC_S1D1_IRQ);
+	menelaus_remove_irq_work(MENELAUS_MMC_S2D1_IRQ);
+
+	the_menelaus->mmc_callback = NULL;
+	the_menelaus->mmc_callback_data = 0;
+}
+EXPORT_SYMBOL(menelaus_unregister_mmc_callback);
+
+struct menelaus_vtg {
+	const char *name;
+	u8 vtg_reg;
+	u8 vtg_shift;
+	u8 vtg_bits;
+	u8 mode_reg;
+};
+
+struct menelaus_vtg_value {
+	u16 vtg;
+	u16 val;
+};
+
+static int menelaus_set_voltage(const struct menelaus_vtg *vtg, int mV,
+				int vtg_val, int mode)
+{
+	int val, ret;
+	struct i2c_client *c = the_menelaus->client;
+
+	mutex_lock(&the_menelaus->lock);
+	if (vtg == 0)
+		goto set_voltage;
+
+	ret = menelaus_read_reg(vtg->vtg_reg);
+	if (ret < 0)
+		goto out;
+	val = ret & ~(((1 << vtg->vtg_bits) - 1) << vtg->vtg_shift);
+	val |= vtg_val << vtg->vtg_shift;
+
+	dev_dbg(&c->dev, "Setting voltage '%s'"
+			 "to %d mV (reg 0x%02x, val 0x%02x)\n",
+			vtg->name, mV, vtg->vtg_reg, val);
+
+	ret = menelaus_write_reg(vtg->vtg_reg, val);
+	if (ret < 0)
+		goto out;
+set_voltage:
+	ret = menelaus_write_reg(vtg->mode_reg, mode);
+out:
+	mutex_unlock(&the_menelaus->lock);
+	if (ret == 0) {
+		/* Wait for voltage to stabilize */
+		msleep(1);
+	}
+	return ret;
+}
+
+static int menelaus_get_vtg_value(int vtg, const struct menelaus_vtg_value *tbl,
+				  int n)
+{
+	int i;
+
+	for (i = 0; i < n; i++, tbl++)
+		if (tbl->vtg == vtg)
+			return tbl->val;
+	return -EINVAL;
+}
+
+/*
+ * Vcore can be programmed in two ways:
+ * SW-controlled: Required voltage is programmed into VCORE_CTRL1
+ * HW-controlled: Required range (roof-floor) is programmed into VCORE_CTRL3
+ * and VCORE_CTRL4
+ *
+ * Call correct 'set' function accordingly
+ */
+
+static const struct menelaus_vtg_value vcore_values[] = {
+	{ 1000, 0 },
+	{ 1025, 1 },
+	{ 1050, 2 },
+	{ 1075, 3 },
+	{ 1100, 4 },
+	{ 1125, 5 },
+	{ 1150, 6 },
+	{ 1175, 7 },
+	{ 1200, 8 },
+	{ 1225, 9 },
+	{ 1250, 10 },
+	{ 1275, 11 },
+	{ 1300, 12 },
+	{ 1325, 13 },
+	{ 1350, 14 },
+	{ 1375, 15 },
+	{ 1400, 16 },
+	{ 1425, 17 },
+	{ 1450, 18 },
+};
+
+int menelaus_set_vcore_sw(unsigned int mV)
+{
+	int val, ret;
+	struct i2c_client *c = the_menelaus->client;
+
+	val = menelaus_get_vtg_value(mV, vcore_values,
+				     ARRAY_SIZE(vcore_values));
+	if (val < 0)
+		return -EINVAL;
+
+	dev_dbg(&c->dev, "Setting VCORE to %d mV (val 0x%02x)\n", mV, val);
+
+	/* Set SW mode and the voltage in one go. */
+	mutex_lock(&the_menelaus->lock);
+	ret = menelaus_write_reg(MENELAUS_VCORE_CTRL1, val);
+	if (ret == 0)
+		the_menelaus->vcore_hw_mode = 0;
+	mutex_unlock(&the_menelaus->lock);
+	msleep(1);
+
+	return ret;
+}
+
+int menelaus_set_vcore_hw(unsigned int roof_mV, unsigned int floor_mV)
+{
+	int fval, rval, val, ret;
+	struct i2c_client *c = the_menelaus->client;
+
+	rval = menelaus_get_vtg_value(roof_mV, vcore_values,
+				      ARRAY_SIZE(vcore_values));
+	if (rval < 0)
+		return -EINVAL;
+	fval = menelaus_get_vtg_value(floor_mV, vcore_values,
+				      ARRAY_SIZE(vcore_values));
+	if (fval < 0)
+		return -EINVAL;
+
+	dev_dbg(&c->dev, "Setting VCORE FLOOR to %d mV and ROOF to %d mV\n",
+	       floor_mV, roof_mV);
+
+	mutex_lock(&the_menelaus->lock);
+	ret = menelaus_write_reg(MENELAUS_VCORE_CTRL3, fval);
+	if (ret < 0)
+		goto out;
+	ret = menelaus_write_reg(MENELAUS_VCORE_CTRL4, rval);
+	if (ret < 0)
+		goto out;
+	if (!the_menelaus->vcore_hw_mode) {
+		val = menelaus_read_reg(MENELAUS_VCORE_CTRL1);
+		/* HW mode, turn OFF byte comparator */
+		val |= (VCORE_CTRL1_HW_NSW | VCORE_CTRL1_BYP_COMP);
+		ret = menelaus_write_reg(MENELAUS_VCORE_CTRL1, val);
+		the_menelaus->vcore_hw_mode = 1;
+	}
+	msleep(1);
+out:
+	mutex_unlock(&the_menelaus->lock);
+	return ret;
+}
+
+static const struct menelaus_vtg vmem_vtg = {
+	.name = "VMEM",
+	.vtg_reg = MENELAUS_LDO_CTRL1,
+	.vtg_shift = 0,
+	.vtg_bits = 2,
+	.mode_reg = MENELAUS_LDO_CTRL3,
+};
+
+static const struct menelaus_vtg_value vmem_values[] = {
+	{ 1500, 0 },
+	{ 1800, 1 },
+	{ 1900, 2 },
+	{ 2500, 3 },
+};
+
+int menelaus_set_vmem(unsigned int mV)
+{
+	int val;
+
+	if (mV == 0)
+		return menelaus_set_voltage(&vmem_vtg, 0, 0, 0);
+
+	val = menelaus_get_vtg_value(mV, vmem_values, ARRAY_SIZE(vmem_values));
+	if (val < 0)
+		return -EINVAL;
+	return menelaus_set_voltage(&vmem_vtg, mV, val, 0x02);
+}
+EXPORT_SYMBOL(menelaus_set_vmem);
+
+static const struct menelaus_vtg vio_vtg = {
+	.name = "VIO",
+	.vtg_reg = MENELAUS_LDO_CTRL1,
+	.vtg_shift = 2,
+	.vtg_bits = 2,
+	.mode_reg = MENELAUS_LDO_CTRL4,
+};
+
+static const struct menelaus_vtg_value vio_values[] = {
+	{ 1500, 0 },
+	{ 1800, 1 },
+	{ 2500, 2 },
+	{ 2800, 3 },
+};
+
+int menelaus_set_vio(unsigned int mV)
+{
+	int val;
+
+	if (mV == 0)
+		return menelaus_set_voltage(&vio_vtg, 0, 0, 0);
+
+	val = menelaus_get_vtg_value(mV, vio_values, ARRAY_SIZE(vio_values));
+	if (val < 0)
+		return -EINVAL;
+	return menelaus_set_voltage(&vio_vtg, mV, val, 0x02);
+}
+EXPORT_SYMBOL(menelaus_set_vio);
+
+static const struct menelaus_vtg_value vdcdc_values[] = {
+	{ 1500, 0 },
+	{ 1800, 1 },
+	{ 2000, 2 },
+	{ 2200, 3 },
+	{ 2400, 4 },
+	{ 2800, 5 },
+	{ 3000, 6 },
+	{ 3300, 7 },
+};
+
+static const struct menelaus_vtg vdcdc2_vtg = {
+	.name = "VDCDC2",
+	.vtg_reg = MENELAUS_DCDC_CTRL1,
+	.vtg_shift = 0,
+	.vtg_bits = 3,
+	.mode_reg = MENELAUS_DCDC_CTRL2,
+};
+
+static const struct menelaus_vtg vdcdc3_vtg = {
+	.name = "VDCDC3",
+	.vtg_reg = MENELAUS_DCDC_CTRL1,
+	.vtg_shift = 3,
+	.vtg_bits = 3,
+	.mode_reg = MENELAUS_DCDC_CTRL3,
+};
+
+int menelaus_set_vdcdc(int dcdc, unsigned int mV)
+{
+	const struct menelaus_vtg *vtg;
+	int val;
+
+	if (dcdc != 2 && dcdc != 3)
+		return -EINVAL;
+	if (dcdc == 2)
+		vtg = &vdcdc2_vtg;
+	else
+		vtg = &vdcdc3_vtg;
+
+	if (mV == 0)
+		return menelaus_set_voltage(vtg, 0, 0, 0);
+
+	val = menelaus_get_vtg_value(mV, vdcdc_values,
+				     ARRAY_SIZE(vdcdc_values));
+	if (val < 0)
+		return -EINVAL;
+	return menelaus_set_voltage(vtg, mV, val, 0x03);
+}
+
+static const struct menelaus_vtg_value vmmc_values[] = {
+	{ 1850, 0 },
+	{ 2800, 1 },
+	{ 3000, 2 },
+	{ 3100, 3 },
+};
+
+static const struct menelaus_vtg vmmc_vtg = {
+	.name = "VMMC",
+	.vtg_reg = MENELAUS_LDO_CTRL1,
+	.vtg_shift = 6,
+	.vtg_bits = 2,
+	.mode_reg = MENELAUS_LDO_CTRL7,
+};
+
+int menelaus_set_vmmc(unsigned int mV)
+{
+	int val;
+
+	if (mV == 0)
+		return menelaus_set_voltage(&vmmc_vtg, 0, 0, 0);
+
+	val = menelaus_get_vtg_value(mV, vmmc_values, ARRAY_SIZE(vmmc_values));
+	if (val < 0)
+		return -EINVAL;
+	return menelaus_set_voltage(&vmmc_vtg, mV, val, 0x02);
+}
+EXPORT_SYMBOL(menelaus_set_vmmc);
+
+
+static const struct menelaus_vtg_value vaux_values[] = {
+	{ 1500, 0 },
+	{ 1800, 1 },
+	{ 2500, 2 },
+	{ 2800, 3 },
+};
+
+static const struct menelaus_vtg vaux_vtg = {
+	.name = "VAUX",
+	.vtg_reg = MENELAUS_LDO_CTRL1,
+	.vtg_shift = 4,
+	.vtg_bits = 2,
+	.mode_reg = MENELAUS_LDO_CTRL6,
+};
+
+int menelaus_set_vaux(unsigned int mV)
+{
+	int val;
+
+	if (mV == 0)
+		return menelaus_set_voltage(&vaux_vtg, 0, 0, 0);
+
+	val = menelaus_get_vtg_value(mV, vaux_values, ARRAY_SIZE(vaux_values));
+	if (val < 0)
+		return -EINVAL;
+	return menelaus_set_voltage(&vaux_vtg, mV, val, 0x02);
+}
+EXPORT_SYMBOL(menelaus_set_vaux);
+
+int menelaus_get_slot_pin_states(void)
+{
+	return menelaus_read_reg(MENELAUS_MCT_PIN_ST);
+}
+EXPORT_SYMBOL(menelaus_get_slot_pin_states);
+
+int menelaus_set_regulator_sleep(int enable, u32 val)
+{
+	int t, ret;
+	struct i2c_client *c = the_menelaus->client;
+
+	mutex_lock(&the_menelaus->lock);
+	ret = menelaus_write_reg(MENELAUS_SLEEP_CTRL2, val);
+	if (ret < 0)
+		goto out;
+
+	dev_dbg(&c->dev, "regulator sleep configuration: %02x\n", val);
+
+	ret = menelaus_read_reg(MENELAUS_GPIO_CTRL);
+	if (ret < 0)
+		goto out;
+	t = (GPIO_CTRL_SLPCTLEN | GPIO3_DIR_INPUT);
+	if (enable)
+		ret |= t;
+	else
+		ret &= ~t;
+	ret = menelaus_write_reg(MENELAUS_GPIO_CTRL, ret);
+out:
+	mutex_unlock(&the_menelaus->lock);
+	return ret;
+}
+
+/*-----------------------------------------------------------------------*/
+
+/* Handles Menelaus interrupts. Does not run in interrupt context */
+static void menelaus_work(struct work_struct *_menelaus)
+{
+	struct menelaus_chip *menelaus =
+			container_of(_menelaus, struct menelaus_chip, work);
+	void (*handler)(struct menelaus_chip *menelaus);
+
+	while (1) {
+		unsigned isr;
+
+		isr = (menelaus_read_reg(MENELAUS_INT_STATUS2)
+				& ~menelaus->mask2) << 8;
+		isr |= menelaus_read_reg(MENELAUS_INT_STATUS1)
+				& ~menelaus->mask1;
+		if (!isr)
+			break;
+
+		while (isr) {
+			int irq = fls(isr) - 1;
+			isr &= ~(1 << irq);
+
+			mutex_lock(&menelaus->lock);
+			menelaus_disable_irq(irq);
+			menelaus_ack_irq(irq);
+			handler = menelaus->handlers[irq];
+			if (handler)
+				handler(menelaus);
+			menelaus_enable_irq(irq);
+			mutex_unlock(&menelaus->lock);
+		}
+	}
+	enable_irq(menelaus->client->irq);
+}
+
+/*
+ * We cannot use I2C in interrupt context, so we just schedule work.
+ */
+static irqreturn_t menelaus_irq(int irq, void *_menelaus)
+{
+	struct menelaus_chip *menelaus = _menelaus;
+
+	disable_irq_nosync(irq);
+	(void)schedule_work(&menelaus->work);
+
+	return IRQ_HANDLED;
+}
+
+/*-----------------------------------------------------------------------*/
+
+/*
+ * The RTC needs to be set once, then it runs on backup battery power.
+ * It supports alarms, including system wake alarms (from some modes);
+ * and 1/second IRQs if requested.
+ */
+#ifdef CONFIG_RTC_DRV_TWL92330
+
+#define RTC_CTRL_RTC_EN		(1 << 0)
+#define RTC_CTRL_AL_EN		(1 << 1)
+#define RTC_CTRL_MODE12		(1 << 2)
+#define RTC_CTRL_EVERY_MASK	(3 << 3)
+#define RTC_CTRL_EVERY_SEC	(0 << 3)
+#define RTC_CTRL_EVERY_MIN	(1 << 3)
+#define RTC_CTRL_EVERY_HR	(2 << 3)
+#define RTC_CTRL_EVERY_DAY	(3 << 3)
+
+#define RTC_UPDATE_EVERY	0x08
+
+#define RTC_HR_PM		(1 << 7)
+
+static void menelaus_to_time(char *regs, struct rtc_time *t)
+{
+	t->tm_sec = bcd2bin(regs[0]);
+	t->tm_min = bcd2bin(regs[1]);
+	if (the_menelaus->rtc_control & RTC_CTRL_MODE12) {
+		t->tm_hour = bcd2bin(regs[2] & 0x1f) - 1;
+		if (regs[2] & RTC_HR_PM)
+			t->tm_hour += 12;
+	} else
+		t->tm_hour = bcd2bin(regs[2] & 0x3f);
+	t->tm_mday = bcd2bin(regs[3]);
+	t->tm_mon = bcd2bin(regs[4]) - 1;
+	t->tm_year = bcd2bin(regs[5]) + 100;
+}
+
+static int time_to_menelaus(struct rtc_time *t, int regnum)
+{
+	int	hour, status;
+
+	status = menelaus_write_reg(regnum++, bin2bcd(t->tm_sec));
+	if (status < 0)
+		goto fail;
+
+	status = menelaus_write_reg(regnum++, bin2bcd(t->tm_min));
+	if (status < 0)
+		goto fail;
+
+	if (the_menelaus->rtc_control & RTC_CTRL_MODE12) {
+		hour = t->tm_hour + 1;
+		if (hour > 12)
+			hour = RTC_HR_PM | bin2bcd(hour - 12);
+		else
+			hour = bin2bcd(hour);
+	} else
+		hour = bin2bcd(t->tm_hour);
+	status = menelaus_write_reg(regnum++, hour);
+	if (status < 0)
+		goto fail;
+
+	status = menelaus_write_reg(regnum++, bin2bcd(t->tm_mday));
+	if (status < 0)
+		goto fail;
+
+	status = menelaus_write_reg(regnum++, bin2bcd(t->tm_mon + 1));
+	if (status < 0)
+		goto fail;
+
+	status = menelaus_write_reg(regnum++, bin2bcd(t->tm_year - 100));
+	if (status < 0)
+		goto fail;
+
+	return 0;
+fail:
+	dev_err(&the_menelaus->client->dev, "rtc write reg %02x, err %d\n",
+			--regnum, status);
+	return status;
+}
+
+static int menelaus_read_time(struct device *dev, struct rtc_time *t)
+{
+	struct i2c_msg	msg[2];
+	char		regs[7];
+	int		status;
+
+	/* block read date and time registers */
+	regs[0] = MENELAUS_RTC_SEC;
+
+	msg[0].addr = MENELAUS_I2C_ADDRESS;
+	msg[0].flags = 0;
+	msg[0].len = 1;
+	msg[0].buf = regs;
+
+	msg[1].addr = MENELAUS_I2C_ADDRESS;
+	msg[1].flags = I2C_M_RD;
+	msg[1].len = sizeof(regs);
+	msg[1].buf = regs;
+
+	status = i2c_transfer(the_menelaus->client->adapter, msg, 2);
+	if (status != 2) {
+		dev_err(dev, "%s error %d\n", "read", status);
+		return -EIO;
+	}
+
+	menelaus_to_time(regs, t);
+	t->tm_wday = bcd2bin(regs[6]);
+
+	return 0;
+}
+
+static int menelaus_set_time(struct device *dev, struct rtc_time *t)
+{
+	int		status;
+
+	/* write date and time registers */
+	status = time_to_menelaus(t, MENELAUS_RTC_SEC);
+	if (status < 0)
+		return status;
+	status = menelaus_write_reg(MENELAUS_RTC_WKDAY, bin2bcd(t->tm_wday));
+	if (status < 0) {
+		dev_err(&the_menelaus->client->dev, "rtc write reg %02x "
+				"err %d\n", MENELAUS_RTC_WKDAY, status);
+		return status;
+	}
+
+	/* now commit the write */
+	status = menelaus_write_reg(MENELAUS_RTC_UPDATE, RTC_UPDATE_EVERY);
+	if (status < 0)
+		dev_err(&the_menelaus->client->dev, "rtc commit time, err %d\n",
+				status);
+
+	return 0;
+}
+
+static int menelaus_read_alarm(struct device *dev, struct rtc_wkalrm *w)
+{
+	struct i2c_msg	msg[2];
+	char		regs[6];
+	int		status;
+
+	/* block read alarm registers */
+	regs[0] = MENELAUS_RTC_AL_SEC;
+
+	msg[0].addr = MENELAUS_I2C_ADDRESS;
+	msg[0].flags = 0;
+	msg[0].len = 1;
+	msg[0].buf = regs;
+
+	msg[1].addr = MENELAUS_I2C_ADDRESS;
+	msg[1].flags = I2C_M_RD;
+	msg[1].len = sizeof(regs);
+	msg[1].buf = regs;
+
+	status = i2c_transfer(the_menelaus->client->adapter, msg, 2);
+	if (status != 2) {
+		dev_err(dev, "%s error %d\n", "alarm read", status);
+		return -EIO;
+	}
+
+	menelaus_to_time(regs, &w->time);
+
+	w->enabled = !!(the_menelaus->rtc_control & RTC_CTRL_AL_EN);
+
+	/* NOTE we *could* check if actually pending... */
+	w->pending = 0;
+
+	return 0;
+}
+
+static int menelaus_set_alarm(struct device *dev, struct rtc_wkalrm *w)
+{
+	int		status;
+
+	if (the_menelaus->client->irq <= 0 && w->enabled)
+		return -ENODEV;
+
+	/* clear previous alarm enable */
+	if (the_menelaus->rtc_control & RTC_CTRL_AL_EN) {
+		the_menelaus->rtc_control &= ~RTC_CTRL_AL_EN;
+		status = menelaus_write_reg(MENELAUS_RTC_CTRL,
+				the_menelaus->rtc_control);
+		if (status < 0)
+			return status;
+	}
+
+	/* write alarm registers */
+	status = time_to_menelaus(&w->time, MENELAUS_RTC_AL_SEC);
+	if (status < 0)
+		return status;
+
+	/* enable alarm if requested */
+	if (w->enabled) {
+		the_menelaus->rtc_control |= RTC_CTRL_AL_EN;
+		status = menelaus_write_reg(MENELAUS_RTC_CTRL,
+				the_menelaus->rtc_control);
+	}
+
+	return status;
+}
+
+#ifdef CONFIG_RTC_INTF_DEV
+
+static void menelaus_rtc_update_work(struct menelaus_chip *m)
+{
+	/* report 1/sec update */
+	local_irq_disable();
+	rtc_update_irq(m->rtc, 1, RTC_IRQF | RTC_UF);
+	local_irq_enable();
+}
+
+static int menelaus_ioctl(struct device *dev, unsigned cmd, unsigned long arg)
+{
+	int	status;
+
+	if (the_menelaus->client->irq <= 0)
+		return -ENOIOCTLCMD;
+
+	switch (cmd) {
+	/* alarm IRQ */
+	case RTC_AIE_ON:
+		if (the_menelaus->rtc_control & RTC_CTRL_AL_EN)
+			return 0;
+		the_menelaus->rtc_control |= RTC_CTRL_AL_EN;
+		break;
+	case RTC_AIE_OFF:
+		if (!(the_menelaus->rtc_control & RTC_CTRL_AL_EN))
+			return 0;
+		the_menelaus->rtc_control &= ~RTC_CTRL_AL_EN;
+		break;
+	/* 1/second "update" IRQ */
+	case RTC_UIE_ON:
+		if (the_menelaus->uie)
+			return 0;
+		status = menelaus_remove_irq_work(MENELAUS_RTCTMR_IRQ);
+		status = menelaus_add_irq_work(MENELAUS_RTCTMR_IRQ,
+				menelaus_rtc_update_work);
+		if (status == 0)
+			the_menelaus->uie = 1;
+		return status;
+	case RTC_UIE_OFF:
+		if (!the_menelaus->uie)
+			return 0;
+		status = menelaus_remove_irq_work(MENELAUS_RTCTMR_IRQ);
+		if (status == 0)
+			the_menelaus->uie = 0;
+		return status;
+	default:
+		return -ENOIOCTLCMD;
+	}
+	return menelaus_write_reg(MENELAUS_RTC_CTRL, the_menelaus->rtc_control);
+}
+
+#else
+#define menelaus_ioctl	NULL
+#endif
+
+/* REVISIT no compensation register support ... */
+
+static const struct rtc_class_ops menelaus_rtc_ops = {
+	.ioctl			= menelaus_ioctl,
+	.read_time		= menelaus_read_time,
+	.set_time		= menelaus_set_time,
+	.read_alarm		= menelaus_read_alarm,
+	.set_alarm		= menelaus_set_alarm,
+};
+
+static void menelaus_rtc_alarm_work(struct menelaus_chip *m)
+{
+	/* report alarm */
+	local_irq_disable();
+	rtc_update_irq(m->rtc, 1, RTC_IRQF | RTC_AF);
+	local_irq_enable();
+
+	/* then disable it; alarms are oneshot */
+	the_menelaus->rtc_control &= ~RTC_CTRL_AL_EN;
+	menelaus_write_reg(MENELAUS_RTC_CTRL, the_menelaus->rtc_control);
+}
+
+static inline void menelaus_rtc_init(struct menelaus_chip *m)
+{
+	int	alarm = (m->client->irq > 0);
+
+	/* assume 32KDETEN pin is pulled high */
+	if (!(menelaus_read_reg(MENELAUS_OSC_CTRL) & 0x80)) {
+		dev_dbg(&m->client->dev, "no 32k oscillator\n");
+		return;
+	}
+
+	/* support RTC alarm; it can issue wakeups */
+	if (alarm) {
+		if (menelaus_add_irq_work(MENELAUS_RTCALM_IRQ,
+				menelaus_rtc_alarm_work) < 0) {
+			dev_err(&m->client->dev, "can't handle RTC alarm\n");
+			return;
+		}
+		device_init_wakeup(&m->client->dev, 1);
+	}
+
+	/* be sure RTC is enabled; allow 1/sec irqs; leave 12hr mode alone */
+	m->rtc_control = menelaus_read_reg(MENELAUS_RTC_CTRL);
+	if (!(m->rtc_control & RTC_CTRL_RTC_EN)
+			|| (m->rtc_control & RTC_CTRL_AL_EN)
+			|| (m->rtc_control & RTC_CTRL_EVERY_MASK)) {
+		if (!(m->rtc_control & RTC_CTRL_RTC_EN)) {
+			dev_warn(&m->client->dev, "rtc clock needs setting\n");
+			m->rtc_control |= RTC_CTRL_RTC_EN;
+		}
+		m->rtc_control &= ~RTC_CTRL_EVERY_MASK;
+		m->rtc_control &= ~RTC_CTRL_AL_EN;
+		menelaus_write_reg(MENELAUS_RTC_CTRL, m->rtc_control);
+	}
+
+	m->rtc = rtc_device_register(DRIVER_NAME,
+			&m->client->dev,
+			&menelaus_rtc_ops, THIS_MODULE);
+	if (IS_ERR(m->rtc)) {
+		if (alarm) {
+			menelaus_remove_irq_work(MENELAUS_RTCALM_IRQ);
+			device_init_wakeup(&m->client->dev, 0);
+		}
+		dev_err(&m->client->dev, "can't register RTC: %d\n",
+				(int) PTR_ERR(m->rtc));
+		the_menelaus->rtc = NULL;
+	}
+}
+
+#else
+
+static inline void menelaus_rtc_init(struct menelaus_chip *m)
+{
+	/* nothing */
+}
+
+#endif
+
+/*-----------------------------------------------------------------------*/
+
+static struct i2c_driver menelaus_i2c_driver;
+
+static int menelaus_probe(struct i2c_client *client,
+			  const struct i2c_device_id *id)
+{
+	struct menelaus_chip	*menelaus;
+	int			rev = 0, val;
+	int			err = 0;
+	struct menelaus_platform_data *menelaus_pdata =
+					client->dev.platform_data;
+
+	if (the_menelaus) {
+		dev_dbg(&client->dev, "only one %s for now\n",
+				DRIVER_NAME);
+		return -ENODEV;
+	}
+
+	menelaus = kzalloc(sizeof *menelaus, GFP_KERNEL);
+	if (!menelaus)
+		return -ENOMEM;
+
+	i2c_set_clientdata(client, menelaus);
+
+	the_menelaus = menelaus;
+	menelaus->client = client;
+
+	/* If a true probe check the device */
+	rev = menelaus_read_reg(MENELAUS_REV);
+	if (rev < 0) {
+		pr_err(DRIVER_NAME ": device not found");
+		err = -ENODEV;
+		goto fail1;
+	}
+
+	/* Ack and disable all Menelaus interrupts */
+	menelaus_write_reg(MENELAUS_INT_ACK1, 0xff);
+	menelaus_write_reg(MENELAUS_INT_ACK2, 0xff);
+	menelaus_write_reg(MENELAUS_INT_MASK1, 0xff);
+	menelaus_write_reg(MENELAUS_INT_MASK2, 0xff);
+	menelaus->mask1 = 0xff;
+	menelaus->mask2 = 0xff;
+
+	/* Set output buffer strengths */
+	menelaus_write_reg(MENELAUS_MCT_CTRL1, 0x73);
+
+	if (client->irq > 0) {
+		err = request_irq(client->irq, menelaus_irq, 0,
+				  DRIVER_NAME, menelaus);
+		if (err) {
+			dev_dbg(&client->dev,  "can't get IRQ %d, err %d\n",
+					client->irq, err);
+			goto fail1;
+		}
+	}
+
+	mutex_init(&menelaus->lock);
+	INIT_WORK(&menelaus->work, menelaus_work);
+
+	pr_info("Menelaus rev %d.%d\n", rev >> 4, rev & 0x0f);
+
+	val = menelaus_read_reg(MENELAUS_VCORE_CTRL1);
+	if (val < 0)
+		goto fail2;
+	if (val & (1 << 7))
+		menelaus->vcore_hw_mode = 1;
+	else
+		menelaus->vcore_hw_mode = 0;
+
+	if (menelaus_pdata != NULL && menelaus_pdata->late_init != NULL) {
+		err = menelaus_pdata->late_init(&client->dev);
+		if (err < 0)
+			goto fail2;
+	}
+
+	menelaus_rtc_init(menelaus);
+
+	return 0;
+fail2:
+	free_irq(client->irq, menelaus);
+	flush_work_sync(&menelaus->work);
+fail1:
+	kfree(menelaus);
+	return err;
+}
+
+static int __exit menelaus_remove(struct i2c_client *client)
+{
+	struct menelaus_chip	*menelaus = i2c_get_clientdata(client);
+
+	free_irq(client->irq, menelaus);
+	flush_work_sync(&menelaus->work);
+	kfree(menelaus);
+	the_menelaus = NULL;
+	return 0;
+}
+
+static const struct i2c_device_id menelaus_id[] = {
+	{ "menelaus", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, menelaus_id);
+
+static struct i2c_driver menelaus_i2c_driver = {
+	.driver = {
+		.name		= DRIVER_NAME,
+	},
+	.probe		= menelaus_probe,
+	.remove		= __exit_p(menelaus_remove),
+	.id_table	= menelaus_id,
+};
+
+static int __init menelaus_init(void)
+{
+	int res;
+
+	res = i2c_add_driver(&menelaus_i2c_driver);
+	if (res < 0) {
+		pr_err(DRIVER_NAME ": driver registration failed\n");
+		return res;
+	}
+
+	return 0;
+}
+
+static void __exit menelaus_exit(void)
+{
+	i2c_del_driver(&menelaus_i2c_driver);
+
+	/* FIXME: Shutdown menelaus parts that can be shut down */
+}
+
+MODULE_AUTHOR("Texas Instruments, Inc. (and others)");
+MODULE_DESCRIPTION("I2C interface for Menelaus.");
+MODULE_LICENSE("GPL");
+
+module_init(menelaus_init);
+module_exit(menelaus_exit);
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/mfd-core.c b/ap/os/linux/linux-3.4.x/drivers/mfd/mfd-core.c
new file mode 100644
index 0000000..4d0022b
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/mfd-core.c
@@ -0,0 +1,252 @@
+/*
+ * drivers/mfd/mfd-core.c
+ *
+ * core MFD support
+ * Copyright (c) 2006 Ian Molton
+ * Copyright (c) 2007,2008 Dmitry Baryshkov
+ *
+ * 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/platform_device.h>
+#include <linux/acpi.h>
+#include <linux/mfd/core.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+
+static struct device_type mfd_dev_type = {
+	.name	= "mfd_device",
+};
+
+int mfd_cell_enable(struct platform_device *pdev)
+{
+	const struct mfd_cell *cell = mfd_get_cell(pdev);
+	int err = 0;
+
+	/* only call enable hook if the cell wasn't previously enabled */
+	if (atomic_inc_return(cell->usage_count) == 1)
+		err = cell->enable(pdev);
+
+	/* if the enable hook failed, decrement counter to allow retries */
+	if (err)
+		atomic_dec(cell->usage_count);
+
+	return err;
+}
+EXPORT_SYMBOL(mfd_cell_enable);
+
+int mfd_cell_disable(struct platform_device *pdev)
+{
+	const struct mfd_cell *cell = mfd_get_cell(pdev);
+	int err = 0;
+
+	/* only disable if no other clients are using it */
+	if (atomic_dec_return(cell->usage_count) == 0)
+		err = cell->disable(pdev);
+
+	/* if the disable hook failed, increment to allow retries */
+	if (err)
+		atomic_inc(cell->usage_count);
+
+	/* sanity check; did someone call disable too many times? */
+	WARN_ON(atomic_read(cell->usage_count) < 0);
+
+	return err;
+}
+EXPORT_SYMBOL(mfd_cell_disable);
+
+static int mfd_platform_add_cell(struct platform_device *pdev,
+				 const struct mfd_cell *cell)
+{
+	if (!cell)
+		return 0;
+
+	pdev->mfd_cell = kmemdup(cell, sizeof(*cell), GFP_KERNEL);
+	if (!pdev->mfd_cell)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static int mfd_add_device(struct device *parent, int id,
+			  const struct mfd_cell *cell,
+			  struct resource *mem_base,
+			  int irq_base)
+{
+	struct resource *res;
+	struct platform_device *pdev;
+	int ret = -ENOMEM;
+	int r;
+
+	pdev = platform_device_alloc(cell->name, id + cell->id);
+	if (!pdev)
+		goto fail_alloc;
+
+	res = kzalloc(sizeof(*res) * cell->num_resources, GFP_KERNEL);
+	if (!res)
+		goto fail_device;
+
+	pdev->dev.parent = parent;
+	pdev->dev.type = &mfd_dev_type;
+
+	if (cell->pdata_size) {
+		ret = platform_device_add_data(pdev,
+					cell->platform_data, cell->pdata_size);
+		if (ret)
+			goto fail_res;
+	}
+
+	ret = mfd_platform_add_cell(pdev, cell);
+	if (ret)
+		goto fail_res;
+
+	for (r = 0; r < cell->num_resources; r++) {
+		res[r].name = cell->resources[r].name;
+		res[r].flags = cell->resources[r].flags;
+
+		/* Find out base to use */
+		if ((cell->resources[r].flags & IORESOURCE_MEM) && mem_base) {
+			res[r].parent = mem_base;
+			res[r].start = mem_base->start +
+				cell->resources[r].start;
+			res[r].end = mem_base->start +
+				cell->resources[r].end;
+		} else if (cell->resources[r].flags & IORESOURCE_IRQ) {
+			res[r].start = irq_base +
+				cell->resources[r].start;
+			res[r].end   = irq_base +
+				cell->resources[r].end;
+		} else {
+			res[r].parent = cell->resources[r].parent;
+			res[r].start = cell->resources[r].start;
+			res[r].end   = cell->resources[r].end;
+		}
+
+		if (!cell->ignore_resource_conflicts) {
+			ret = acpi_check_resource_conflict(&res[r]);
+			if (ret)
+				goto fail_res;
+		}
+	}
+
+	ret = platform_device_add_resources(pdev, res, cell->num_resources);
+	if (ret)
+		goto fail_res;
+
+	ret = platform_device_add(pdev);
+	if (ret)
+		goto fail_res;
+
+	if (cell->pm_runtime_no_callbacks)
+		pm_runtime_no_callbacks(&pdev->dev);
+
+	kfree(res);
+
+	return 0;
+
+fail_res:
+	kfree(res);
+fail_device:
+	platform_device_put(pdev);
+fail_alloc:
+	return ret;
+}
+
+int mfd_add_devices(struct device *parent, int id,
+		    struct mfd_cell *cells, int n_devs,
+		    struct resource *mem_base,
+		    int irq_base)
+{
+	int i;
+	int ret = 0;
+	atomic_t *cnts;
+
+	if (n_devs <= 0)
+		return -EINVAL;
+
+	/* initialize reference counting for all cells */
+	cnts = kcalloc(n_devs, sizeof(*cnts), GFP_KERNEL);
+	if (!cnts)
+		return -ENOMEM;
+
+	for (i = 0; i < n_devs; i++) {
+		atomic_set(&cnts[i], 0);
+		cells[i].usage_count = &cnts[i];
+		ret = mfd_add_device(parent, id, cells + i, mem_base, irq_base);
+		if (ret)
+			break;
+	}
+
+	if (ret)
+		mfd_remove_devices(parent);
+
+	return ret;
+}
+EXPORT_SYMBOL(mfd_add_devices);
+
+static int mfd_remove_devices_fn(struct device *dev, void *c)
+{
+	struct platform_device *pdev;
+	const struct mfd_cell *cell;
+	atomic_t **usage_count = c;
+
+	if (dev->type != &mfd_dev_type)
+		return 0;
+
+	pdev = to_platform_device(dev);
+	cell = mfd_get_cell(pdev);
+
+	/* find the base address of usage_count pointers (for freeing) */
+	if (!*usage_count || (cell->usage_count < *usage_count))
+		*usage_count = cell->usage_count;
+
+	platform_device_unregister(pdev);
+	return 0;
+}
+
+void mfd_remove_devices(struct device *parent)
+{
+	atomic_t *cnts = NULL;
+
+	device_for_each_child(parent, &cnts, mfd_remove_devices_fn);
+	kfree(cnts);
+}
+EXPORT_SYMBOL(mfd_remove_devices);
+
+int mfd_clone_cell(const char *cell, const char **clones, size_t n_clones)
+{
+	struct mfd_cell cell_entry;
+	struct device *dev;
+	struct platform_device *pdev;
+	int i;
+
+	/* fetch the parent cell's device (should already be registered!) */
+	dev = bus_find_device_by_name(&platform_bus_type, NULL, cell);
+	if (!dev) {
+		printk(KERN_ERR "failed to find device for cell %s\n", cell);
+		return -ENODEV;
+	}
+	pdev = to_platform_device(dev);
+	memcpy(&cell_entry, mfd_get_cell(pdev), sizeof(cell_entry));
+
+	WARN_ON(!cell_entry.enable);
+
+	for (i = 0; i < n_clones; i++) {
+		cell_entry.name = clones[i];
+		/* don't give up if a single call fails; just report error */
+		if (mfd_add_device(pdev->dev.parent, -1, &cell_entry, NULL, 0))
+			dev_err(dev, "failed to create platform device '%s'\n",
+					clones[i]);
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(mfd_clone_cell);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ian Molton, Dmitry Baryshkov");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/omap-usb-host.c b/ap/os/linux/linux-3.4.x/drivers/mfd/omap-usb-host.c
new file mode 100644
index 0000000..7e96bb2
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/omap-usb-host.c
@@ -0,0 +1,874 @@
+/**
+ * omap-usb-host.c - The USBHS core driver for OMAP EHCI & OHCI
+ *
+ * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com
+ * Author: Keshava Munegowda <keshava_mgowda@ti.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2  of
+ * the License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/spinlock.h>
+#include <plat/cpu.h>
+#include <plat/usb.h>
+#include <linux/pm_runtime.h>
+
+#define USBHS_DRIVER_NAME	"usbhs_omap"
+#define OMAP_EHCI_DEVICE	"ehci-omap"
+#define OMAP_OHCI_DEVICE	"ohci-omap3"
+
+/* OMAP USBHOST Register addresses  */
+
+/* TLL Register Set */
+#define	OMAP_USBTLL_REVISION				(0x00)
+#define	OMAP_USBTLL_SYSCONFIG				(0x10)
+#define	OMAP_USBTLL_SYSCONFIG_CACTIVITY			(1 << 8)
+#define	OMAP_USBTLL_SYSCONFIG_SIDLEMODE			(1 << 3)
+#define	OMAP_USBTLL_SYSCONFIG_ENAWAKEUP			(1 << 2)
+#define	OMAP_USBTLL_SYSCONFIG_SOFTRESET			(1 << 1)
+#define	OMAP_USBTLL_SYSCONFIG_AUTOIDLE			(1 << 0)
+
+#define	OMAP_USBTLL_SYSSTATUS				(0x14)
+#define	OMAP_USBTLL_SYSSTATUS_RESETDONE			(1 << 0)
+
+#define	OMAP_USBTLL_IRQSTATUS				(0x18)
+#define	OMAP_USBTLL_IRQENABLE				(0x1C)
+
+#define	OMAP_TLL_SHARED_CONF				(0x30)
+#define	OMAP_TLL_SHARED_CONF_USB_90D_DDR_EN		(1 << 6)
+#define	OMAP_TLL_SHARED_CONF_USB_180D_SDR_EN		(1 << 5)
+#define	OMAP_TLL_SHARED_CONF_USB_DIVRATION		(1 << 2)
+#define	OMAP_TLL_SHARED_CONF_FCLK_REQ			(1 << 1)
+#define	OMAP_TLL_SHARED_CONF_FCLK_IS_ON			(1 << 0)
+
+#define	OMAP_TLL_CHANNEL_CONF(num)			(0x040 + 0x004 * num)
+#define OMAP_TLL_CHANNEL_CONF_FSLSMODE_SHIFT		24
+#define	OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF		(1 << 11)
+#define	OMAP_TLL_CHANNEL_CONF_ULPI_ULPIAUTOIDLE		(1 << 10)
+#define	OMAP_TLL_CHANNEL_CONF_UTMIAUTOIDLE		(1 << 9)
+#define	OMAP_TLL_CHANNEL_CONF_ULPIDDRMODE		(1 << 8)
+#define OMAP_TLL_CHANNEL_CONF_CHANMODE_FSLS		(1 << 1)
+#define	OMAP_TLL_CHANNEL_CONF_CHANEN			(1 << 0)
+
+#define OMAP_TLL_FSLSMODE_6PIN_PHY_DAT_SE0		0x0
+#define OMAP_TLL_FSLSMODE_6PIN_PHY_DP_DM		0x1
+#define OMAP_TLL_FSLSMODE_3PIN_PHY			0x2
+#define OMAP_TLL_FSLSMODE_4PIN_PHY			0x3
+#define OMAP_TLL_FSLSMODE_6PIN_TLL_DAT_SE0		0x4
+#define OMAP_TLL_FSLSMODE_6PIN_TLL_DP_DM		0x5
+#define OMAP_TLL_FSLSMODE_3PIN_TLL			0x6
+#define OMAP_TLL_FSLSMODE_4PIN_TLL			0x7
+#define OMAP_TLL_FSLSMODE_2PIN_TLL_DAT_SE0		0xA
+#define OMAP_TLL_FSLSMODE_2PIN_DAT_DP_DM		0xB
+
+#define	OMAP_TLL_ULPI_FUNCTION_CTRL(num)		(0x804 + 0x100 * num)
+#define	OMAP_TLL_ULPI_INTERFACE_CTRL(num)		(0x807 + 0x100 * num)
+#define	OMAP_TLL_ULPI_OTG_CTRL(num)			(0x80A + 0x100 * num)
+#define	OMAP_TLL_ULPI_INT_EN_RISE(num)			(0x80D + 0x100 * num)
+#define	OMAP_TLL_ULPI_INT_EN_FALL(num)			(0x810 + 0x100 * num)
+#define	OMAP_TLL_ULPI_INT_STATUS(num)			(0x813 + 0x100 * num)
+#define	OMAP_TLL_ULPI_INT_LATCH(num)			(0x814 + 0x100 * num)
+#define	OMAP_TLL_ULPI_DEBUG(num)			(0x815 + 0x100 * num)
+#define	OMAP_TLL_ULPI_SCRATCH_REGISTER(num)		(0x816 + 0x100 * num)
+
+#define OMAP_TLL_CHANNEL_COUNT				3
+#define OMAP_TLL_CHANNEL_1_EN_MASK			(1 << 0)
+#define OMAP_TLL_CHANNEL_2_EN_MASK			(1 << 1)
+#define OMAP_TLL_CHANNEL_3_EN_MASK			(1 << 2)
+
+/* UHH Register Set */
+#define	OMAP_UHH_REVISION				(0x00)
+#define	OMAP_UHH_SYSCONFIG				(0x10)
+#define	OMAP_UHH_SYSCONFIG_MIDLEMODE			(1 << 12)
+#define	OMAP_UHH_SYSCONFIG_CACTIVITY			(1 << 8)
+#define	OMAP_UHH_SYSCONFIG_SIDLEMODE			(1 << 3)
+#define	OMAP_UHH_SYSCONFIG_ENAWAKEUP			(1 << 2)
+#define	OMAP_UHH_SYSCONFIG_SOFTRESET			(1 << 1)
+#define	OMAP_UHH_SYSCONFIG_AUTOIDLE			(1 << 0)
+
+#define	OMAP_UHH_SYSSTATUS				(0x14)
+#define	OMAP_UHH_HOSTCONFIG				(0x40)
+#define	OMAP_UHH_HOSTCONFIG_ULPI_BYPASS			(1 << 0)
+#define	OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS		(1 << 0)
+#define	OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS		(1 << 11)
+#define	OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS		(1 << 12)
+#define OMAP_UHH_HOSTCONFIG_INCR4_BURST_EN		(1 << 2)
+#define OMAP_UHH_HOSTCONFIG_INCR8_BURST_EN		(1 << 3)
+#define OMAP_UHH_HOSTCONFIG_INCR16_BURST_EN		(1 << 4)
+#define OMAP_UHH_HOSTCONFIG_INCRX_ALIGN_EN		(1 << 5)
+#define OMAP_UHH_HOSTCONFIG_P1_CONNECT_STATUS		(1 << 8)
+#define OMAP_UHH_HOSTCONFIG_P2_CONNECT_STATUS		(1 << 9)
+#define OMAP_UHH_HOSTCONFIG_P3_CONNECT_STATUS		(1 << 10)
+#define OMAP4_UHH_HOSTCONFIG_APP_START_CLK		(1 << 31)
+
+/* OMAP4-specific defines */
+#define OMAP4_UHH_SYSCONFIG_IDLEMODE_CLEAR		(3 << 2)
+#define OMAP4_UHH_SYSCONFIG_NOIDLE			(1 << 2)
+#define OMAP4_UHH_SYSCONFIG_STDBYMODE_CLEAR		(3 << 4)
+#define OMAP4_UHH_SYSCONFIG_NOSTDBY			(1 << 4)
+#define OMAP4_UHH_SYSCONFIG_SOFTRESET			(1 << 0)
+
+#define OMAP4_P1_MODE_CLEAR				(3 << 16)
+#define OMAP4_P1_MODE_TLL				(1 << 16)
+#define OMAP4_P1_MODE_HSIC				(3 << 16)
+#define OMAP4_P2_MODE_CLEAR				(3 << 18)
+#define OMAP4_P2_MODE_TLL				(1 << 18)
+#define OMAP4_P2_MODE_HSIC				(3 << 18)
+
+#define OMAP_REV2_TLL_CHANNEL_COUNT			2
+
+#define	OMAP_UHH_DEBUG_CSR				(0x44)
+
+/* Values of UHH_REVISION - Note: these are not given in the TRM */
+#define OMAP_USBHS_REV1		0x00000010	/* OMAP3 */
+#define OMAP_USBHS_REV2		0x50700100	/* OMAP4 */
+
+#define is_omap_usbhs_rev1(x)	(x->usbhs_rev == OMAP_USBHS_REV1)
+#define is_omap_usbhs_rev2(x)	(x->usbhs_rev == OMAP_USBHS_REV2)
+
+#define is_ehci_phy_mode(x)	(x == OMAP_EHCI_PORT_MODE_PHY)
+#define is_ehci_tll_mode(x)	(x == OMAP_EHCI_PORT_MODE_TLL)
+#define is_ehci_hsic_mode(x)	(x == OMAP_EHCI_PORT_MODE_HSIC)
+
+
+struct usbhs_hcd_omap {
+	struct clk			*xclk60mhsp1_ck;
+	struct clk			*xclk60mhsp2_ck;
+	struct clk			*utmi_p1_fck;
+	struct clk			*usbhost_p1_fck;
+	struct clk			*usbtll_p1_fck;
+	struct clk			*utmi_p2_fck;
+	struct clk			*usbhost_p2_fck;
+	struct clk			*usbtll_p2_fck;
+	struct clk			*init_60m_fclk;
+	struct clk			*ehci_logic_fck;
+
+	void __iomem			*uhh_base;
+	void __iomem			*tll_base;
+
+	struct usbhs_omap_platform_data	platdata;
+
+	u32				usbhs_rev;
+	spinlock_t			lock;
+};
+/*-------------------------------------------------------------------------*/
+
+const char usbhs_driver_name[] = USBHS_DRIVER_NAME;
+static u64 usbhs_dmamask = DMA_BIT_MASK(32);
+
+/*-------------------------------------------------------------------------*/
+
+static inline void usbhs_write(void __iomem *base, u32 reg, u32 val)
+{
+	__raw_writel(val, base + reg);
+}
+
+static inline u32 usbhs_read(void __iomem *base, u32 reg)
+{
+	return __raw_readl(base + reg);
+}
+
+static inline void usbhs_writeb(void __iomem *base, u8 reg, u8 val)
+{
+	__raw_writeb(val, base + reg);
+}
+
+static inline u8 usbhs_readb(void __iomem *base, u8 reg)
+{
+	return __raw_readb(base + reg);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct platform_device *omap_usbhs_alloc_child(const char *name,
+			struct resource	*res, int num_resources, void *pdata,
+			size_t pdata_size, struct device *dev)
+{
+	struct platform_device	*child;
+	int			ret;
+
+	child = platform_device_alloc(name, 0);
+
+	if (!child) {
+		dev_err(dev, "platform_device_alloc %s failed\n", name);
+		goto err_end;
+	}
+
+	ret = platform_device_add_resources(child, res, num_resources);
+	if (ret) {
+		dev_err(dev, "platform_device_add_resources failed\n");
+		goto err_alloc;
+	}
+
+	ret = platform_device_add_data(child, pdata, pdata_size);
+	if (ret) {
+		dev_err(dev, "platform_device_add_data failed\n");
+		goto err_alloc;
+	}
+
+	child->dev.dma_mask		= &usbhs_dmamask;
+	dma_set_coherent_mask(&child->dev, DMA_BIT_MASK(32));
+	child->dev.parent		= dev;
+
+	ret = platform_device_add(child);
+	if (ret) {
+		dev_err(dev, "platform_device_add failed\n");
+		goto err_alloc;
+	}
+
+	return child;
+
+err_alloc:
+	platform_device_put(child);
+
+err_end:
+	return NULL;
+}
+
+static int omap_usbhs_alloc_children(struct platform_device *pdev)
+{
+	struct device				*dev = &pdev->dev;
+	struct usbhs_hcd_omap			*omap;
+	struct ehci_hcd_omap_platform_data	*ehci_data;
+	struct ohci_hcd_omap_platform_data	*ohci_data;
+	struct platform_device			*ehci;
+	struct platform_device			*ohci;
+	struct resource				*res;
+	struct resource				resources[2];
+	int					ret;
+
+	omap = platform_get_drvdata(pdev);
+	ehci_data = omap->platdata.ehci_data;
+	ohci_data = omap->platdata.ohci_data;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ehci");
+	if (!res) {
+		dev_err(dev, "EHCI get resource IORESOURCE_MEM failed\n");
+		ret = -ENODEV;
+		goto err_end;
+	}
+	resources[0] = *res;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "ehci-irq");
+	if (!res) {
+		dev_err(dev, " EHCI get resource IORESOURCE_IRQ failed\n");
+		ret = -ENODEV;
+		goto err_end;
+	}
+	resources[1] = *res;
+
+	ehci = omap_usbhs_alloc_child(OMAP_EHCI_DEVICE, resources, 2, ehci_data,
+		sizeof(*ehci_data), dev);
+
+	if (!ehci) {
+		dev_err(dev, "omap_usbhs_alloc_child failed\n");
+		ret = -ENOMEM;
+		goto err_end;
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ohci");
+	if (!res) {
+		dev_err(dev, "OHCI get resource IORESOURCE_MEM failed\n");
+		ret = -ENODEV;
+		goto err_ehci;
+	}
+	resources[0] = *res;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "ohci-irq");
+	if (!res) {
+		dev_err(dev, "OHCI get resource IORESOURCE_IRQ failed\n");
+		ret = -ENODEV;
+		goto err_ehci;
+	}
+	resources[1] = *res;
+
+	ohci = omap_usbhs_alloc_child(OMAP_OHCI_DEVICE, resources, 2, ohci_data,
+		sizeof(*ohci_data), dev);
+	if (!ohci) {
+		dev_err(dev, "omap_usbhs_alloc_child failed\n");
+		ret = -ENOMEM;
+		goto err_ehci;
+	}
+
+	return 0;
+
+err_ehci:
+	platform_device_unregister(ehci);
+
+err_end:
+	return ret;
+}
+
+static bool is_ohci_port(enum usbhs_omap_port_mode pmode)
+{
+	switch (pmode) {
+	case OMAP_OHCI_PORT_MODE_PHY_6PIN_DATSE0:
+	case OMAP_OHCI_PORT_MODE_PHY_6PIN_DPDM:
+	case OMAP_OHCI_PORT_MODE_PHY_3PIN_DATSE0:
+	case OMAP_OHCI_PORT_MODE_PHY_4PIN_DPDM:
+	case OMAP_OHCI_PORT_MODE_TLL_6PIN_DATSE0:
+	case OMAP_OHCI_PORT_MODE_TLL_6PIN_DPDM:
+	case OMAP_OHCI_PORT_MODE_TLL_3PIN_DATSE0:
+	case OMAP_OHCI_PORT_MODE_TLL_4PIN_DPDM:
+	case OMAP_OHCI_PORT_MODE_TLL_2PIN_DATSE0:
+	case OMAP_OHCI_PORT_MODE_TLL_2PIN_DPDM:
+		return true;
+
+	default:
+		return false;
+	}
+}
+
+/*
+ * convert the port-mode enum to a value we can use in the FSLSMODE
+ * field of USBTLL_CHANNEL_CONF
+ */
+static unsigned ohci_omap3_fslsmode(enum usbhs_omap_port_mode mode)
+{
+	switch (mode) {
+	case OMAP_USBHS_PORT_MODE_UNUSED:
+	case OMAP_OHCI_PORT_MODE_PHY_6PIN_DATSE0:
+		return OMAP_TLL_FSLSMODE_6PIN_PHY_DAT_SE0;
+
+	case OMAP_OHCI_PORT_MODE_PHY_6PIN_DPDM:
+		return OMAP_TLL_FSLSMODE_6PIN_PHY_DP_DM;
+
+	case OMAP_OHCI_PORT_MODE_PHY_3PIN_DATSE0:
+		return OMAP_TLL_FSLSMODE_3PIN_PHY;
+
+	case OMAP_OHCI_PORT_MODE_PHY_4PIN_DPDM:
+		return OMAP_TLL_FSLSMODE_4PIN_PHY;
+
+	case OMAP_OHCI_PORT_MODE_TLL_6PIN_DATSE0:
+		return OMAP_TLL_FSLSMODE_6PIN_TLL_DAT_SE0;
+
+	case OMAP_OHCI_PORT_MODE_TLL_6PIN_DPDM:
+		return OMAP_TLL_FSLSMODE_6PIN_TLL_DP_DM;
+
+	case OMAP_OHCI_PORT_MODE_TLL_3PIN_DATSE0:
+		return OMAP_TLL_FSLSMODE_3PIN_TLL;
+
+	case OMAP_OHCI_PORT_MODE_TLL_4PIN_DPDM:
+		return OMAP_TLL_FSLSMODE_4PIN_TLL;
+
+	case OMAP_OHCI_PORT_MODE_TLL_2PIN_DATSE0:
+		return OMAP_TLL_FSLSMODE_2PIN_TLL_DAT_SE0;
+
+	case OMAP_OHCI_PORT_MODE_TLL_2PIN_DPDM:
+		return OMAP_TLL_FSLSMODE_2PIN_DAT_DP_DM;
+	default:
+		pr_warning("Invalid port mode, using default\n");
+		return OMAP_TLL_FSLSMODE_6PIN_PHY_DAT_SE0;
+	}
+}
+
+static void usbhs_omap_tll_init(struct device *dev, u8 tll_channel_count)
+{
+	struct usbhs_hcd_omap		*omap = dev_get_drvdata(dev);
+	struct usbhs_omap_platform_data	*pdata = dev->platform_data;
+	unsigned			reg;
+	int				i;
+
+	/* Program Common TLL register */
+	reg = usbhs_read(omap->tll_base, OMAP_TLL_SHARED_CONF);
+	reg |= (OMAP_TLL_SHARED_CONF_FCLK_IS_ON
+		| OMAP_TLL_SHARED_CONF_USB_DIVRATION);
+	reg &= ~OMAP_TLL_SHARED_CONF_USB_90D_DDR_EN;
+	reg &= ~OMAP_TLL_SHARED_CONF_USB_180D_SDR_EN;
+
+	usbhs_write(omap->tll_base, OMAP_TLL_SHARED_CONF, reg);
+
+	/* Enable channels now */
+	for (i = 0; i < tll_channel_count; i++) {
+		reg = usbhs_read(omap->tll_base,
+				OMAP_TLL_CHANNEL_CONF(i));
+
+		if (is_ohci_port(pdata->port_mode[i])) {
+			reg |= ohci_omap3_fslsmode(pdata->port_mode[i])
+				<< OMAP_TLL_CHANNEL_CONF_FSLSMODE_SHIFT;
+			reg |= OMAP_TLL_CHANNEL_CONF_CHANMODE_FSLS;
+		} else if (pdata->port_mode[i] == OMAP_EHCI_PORT_MODE_TLL) {
+
+			/* Disable AutoIdle, BitStuffing and use SDR Mode */
+			reg &= ~(OMAP_TLL_CHANNEL_CONF_UTMIAUTOIDLE
+				| OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF
+				| OMAP_TLL_CHANNEL_CONF_ULPIDDRMODE);
+
+		} else
+			continue;
+
+		reg |= OMAP_TLL_CHANNEL_CONF_CHANEN;
+		usbhs_write(omap->tll_base,
+				OMAP_TLL_CHANNEL_CONF(i), reg);
+
+		usbhs_writeb(omap->tll_base,
+				OMAP_TLL_ULPI_SCRATCH_REGISTER(i), 0xbe);
+	}
+}
+
+static int usbhs_runtime_resume(struct device *dev)
+{
+	struct usbhs_hcd_omap		*omap = dev_get_drvdata(dev);
+	struct usbhs_omap_platform_data	*pdata = &omap->platdata;
+	unsigned long			flags;
+
+	dev_dbg(dev, "usbhs_runtime_resume\n");
+
+	if (!pdata) {
+		dev_dbg(dev, "missing platform_data\n");
+		return  -ENODEV;
+	}
+
+	spin_lock_irqsave(&omap->lock, flags);
+
+	if (omap->ehci_logic_fck && !IS_ERR(omap->ehci_logic_fck))
+		clk_enable(omap->ehci_logic_fck);
+
+	if (is_ehci_tll_mode(pdata->port_mode[0])) {
+		clk_enable(omap->usbhost_p1_fck);
+		clk_enable(omap->usbtll_p1_fck);
+	}
+	if (is_ehci_tll_mode(pdata->port_mode[1])) {
+		clk_enable(omap->usbhost_p2_fck);
+		clk_enable(omap->usbtll_p2_fck);
+	}
+	clk_enable(omap->utmi_p1_fck);
+	clk_enable(omap->utmi_p2_fck);
+
+	spin_unlock_irqrestore(&omap->lock, flags);
+
+	return 0;
+}
+
+static int usbhs_runtime_suspend(struct device *dev)
+{
+	struct usbhs_hcd_omap		*omap = dev_get_drvdata(dev);
+	struct usbhs_omap_platform_data	*pdata = &omap->platdata;
+	unsigned long			flags;
+
+	dev_dbg(dev, "usbhs_runtime_suspend\n");
+
+	if (!pdata) {
+		dev_dbg(dev, "missing platform_data\n");
+		return  -ENODEV;
+	}
+
+	spin_lock_irqsave(&omap->lock, flags);
+
+	if (is_ehci_tll_mode(pdata->port_mode[0])) {
+		clk_disable(omap->usbhost_p1_fck);
+		clk_disable(omap->usbtll_p1_fck);
+	}
+	if (is_ehci_tll_mode(pdata->port_mode[1])) {
+		clk_disable(omap->usbhost_p2_fck);
+		clk_disable(omap->usbtll_p2_fck);
+	}
+	clk_disable(omap->utmi_p2_fck);
+	clk_disable(omap->utmi_p1_fck);
+
+	if (omap->ehci_logic_fck && !IS_ERR(omap->ehci_logic_fck))
+		clk_disable(omap->ehci_logic_fck);
+
+	spin_unlock_irqrestore(&omap->lock, flags);
+
+	return 0;
+}
+
+static void omap_usbhs_init(struct device *dev)
+{
+	struct usbhs_hcd_omap		*omap = dev_get_drvdata(dev);
+	struct usbhs_omap_platform_data	*pdata = &omap->platdata;
+	unsigned long			flags;
+	unsigned			reg;
+
+	dev_dbg(dev, "starting TI HSUSB Controller\n");
+
+	pm_runtime_get_sync(dev);
+	spin_lock_irqsave(&omap->lock, flags);
+
+	omap->usbhs_rev = usbhs_read(omap->uhh_base, OMAP_UHH_REVISION);
+	dev_dbg(dev, "OMAP UHH_REVISION 0x%x\n", omap->usbhs_rev);
+
+	reg = usbhs_read(omap->uhh_base, OMAP_UHH_HOSTCONFIG);
+	/* setup ULPI bypass and burst configurations */
+	reg |= (OMAP_UHH_HOSTCONFIG_INCR4_BURST_EN
+			| OMAP_UHH_HOSTCONFIG_INCR8_BURST_EN
+			| OMAP_UHH_HOSTCONFIG_INCR16_BURST_EN);
+	reg |= OMAP4_UHH_HOSTCONFIG_APP_START_CLK;
+	reg &= ~OMAP_UHH_HOSTCONFIG_INCRX_ALIGN_EN;
+
+	if (is_omap_usbhs_rev1(omap)) {
+		if (pdata->port_mode[0] == OMAP_USBHS_PORT_MODE_UNUSED)
+			reg &= ~OMAP_UHH_HOSTCONFIG_P1_CONNECT_STATUS;
+		if (pdata->port_mode[1] == OMAP_USBHS_PORT_MODE_UNUSED)
+			reg &= ~OMAP_UHH_HOSTCONFIG_P2_CONNECT_STATUS;
+		if (pdata->port_mode[2] == OMAP_USBHS_PORT_MODE_UNUSED)
+			reg &= ~OMAP_UHH_HOSTCONFIG_P3_CONNECT_STATUS;
+
+		/* Bypass the TLL module for PHY mode operation */
+		if (cpu_is_omap3430() && (omap_rev() <= OMAP3430_REV_ES2_1)) {
+			dev_dbg(dev, "OMAP3 ES version <= ES2.1\n");
+			if (is_ehci_phy_mode(pdata->port_mode[0]) ||
+				is_ehci_phy_mode(pdata->port_mode[1]) ||
+					is_ehci_phy_mode(pdata->port_mode[2]))
+				reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_BYPASS;
+			else
+				reg |= OMAP_UHH_HOSTCONFIG_ULPI_BYPASS;
+		} else {
+			dev_dbg(dev, "OMAP3 ES version > ES2.1\n");
+			if (is_ehci_phy_mode(pdata->port_mode[0]))
+				reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS;
+			else
+				reg |= OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS;
+			if (is_ehci_phy_mode(pdata->port_mode[1]))
+				reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS;
+			else
+				reg |= OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS;
+			if (is_ehci_phy_mode(pdata->port_mode[2]))
+				reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS;
+			else
+				reg |= OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS;
+		}
+	} else if (is_omap_usbhs_rev2(omap)) {
+		/* Clear port mode fields for PHY mode*/
+		reg &= ~OMAP4_P1_MODE_CLEAR;
+		reg &= ~OMAP4_P2_MODE_CLEAR;
+
+		if (is_ehci_tll_mode(pdata->port_mode[0]) ||
+			(is_ohci_port(pdata->port_mode[0])))
+			reg |= OMAP4_P1_MODE_TLL;
+		else if (is_ehci_hsic_mode(pdata->port_mode[0]))
+			reg |= OMAP4_P1_MODE_HSIC;
+
+		if (is_ehci_tll_mode(pdata->port_mode[1]) ||
+			(is_ohci_port(pdata->port_mode[1])))
+			reg |= OMAP4_P2_MODE_TLL;
+		else if (is_ehci_hsic_mode(pdata->port_mode[1]))
+			reg |= OMAP4_P2_MODE_HSIC;
+	}
+
+	usbhs_write(omap->uhh_base, OMAP_UHH_HOSTCONFIG, reg);
+	dev_dbg(dev, "UHH setup done, uhh_hostconfig=%x\n", reg);
+
+	if (is_ehci_tll_mode(pdata->port_mode[0]) ||
+		is_ehci_tll_mode(pdata->port_mode[1]) ||
+		is_ehci_tll_mode(pdata->port_mode[2]) ||
+		(is_ohci_port(pdata->port_mode[0])) ||
+		(is_ohci_port(pdata->port_mode[1])) ||
+		(is_ohci_port(pdata->port_mode[2]))) {
+
+		/* Enable UTMI mode for required TLL channels */
+		if (is_omap_usbhs_rev2(omap))
+			usbhs_omap_tll_init(dev, OMAP_REV2_TLL_CHANNEL_COUNT);
+		else
+			usbhs_omap_tll_init(dev, OMAP_TLL_CHANNEL_COUNT);
+	}
+
+	spin_unlock_irqrestore(&omap->lock, flags);
+	pm_runtime_put_sync(dev);
+}
+
+
+/**
+ * usbhs_omap_probe - initialize TI-based HCDs
+ *
+ * Allocates basic resources for this USB host controller.
+ */
+static int __devinit usbhs_omap_probe(struct platform_device *pdev)
+{
+	struct device			*dev =  &pdev->dev;
+	struct usbhs_omap_platform_data	*pdata = dev->platform_data;
+	struct usbhs_hcd_omap		*omap;
+	struct resource			*res;
+	int				ret = 0;
+	int				i;
+
+	if (!pdata) {
+		dev_err(dev, "Missing platform data\n");
+		ret = -ENOMEM;
+		goto end_probe;
+	}
+
+	omap = kzalloc(sizeof(*omap), GFP_KERNEL);
+	if (!omap) {
+		dev_err(dev, "Memory allocation failed\n");
+		ret = -ENOMEM;
+		goto end_probe;
+	}
+
+	spin_lock_init(&omap->lock);
+
+	for (i = 0; i < OMAP3_HS_USB_PORTS; i++)
+		omap->platdata.port_mode[i] = pdata->port_mode[i];
+
+	omap->platdata.ehci_data = pdata->ehci_data;
+	omap->platdata.ohci_data = pdata->ohci_data;
+
+	pm_runtime_enable(dev);
+
+
+	for (i = 0; i < OMAP3_HS_USB_PORTS; i++)
+		if (is_ehci_phy_mode(i) || is_ehci_tll_mode(i) ||
+			is_ehci_hsic_mode(i)) {
+			omap->ehci_logic_fck = clk_get(dev, "ehci_logic_fck");
+			if (IS_ERR(omap->ehci_logic_fck)) {
+				ret = PTR_ERR(omap->ehci_logic_fck);
+				dev_warn(dev, "ehci_logic_fck failed:%d\n",
+					 ret);
+			}
+			break;
+		}
+
+	omap->utmi_p1_fck = clk_get(dev, "utmi_p1_gfclk");
+	if (IS_ERR(omap->utmi_p1_fck)) {
+		ret = PTR_ERR(omap->utmi_p1_fck);
+		dev_err(dev, "utmi_p1_gfclk failed error:%d\n",	ret);
+		goto err_end;
+	}
+
+	omap->xclk60mhsp1_ck = clk_get(dev, "xclk60mhsp1_ck");
+	if (IS_ERR(omap->xclk60mhsp1_ck)) {
+		ret = PTR_ERR(omap->xclk60mhsp1_ck);
+		dev_err(dev, "xclk60mhsp1_ck failed error:%d\n", ret);
+		goto err_utmi_p1_fck;
+	}
+
+	omap->utmi_p2_fck = clk_get(dev, "utmi_p2_gfclk");
+	if (IS_ERR(omap->utmi_p2_fck)) {
+		ret = PTR_ERR(omap->utmi_p2_fck);
+		dev_err(dev, "utmi_p2_gfclk failed error:%d\n", ret);
+		goto err_xclk60mhsp1_ck;
+	}
+
+	omap->xclk60mhsp2_ck = clk_get(dev, "xclk60mhsp2_ck");
+	if (IS_ERR(omap->xclk60mhsp2_ck)) {
+		ret = PTR_ERR(omap->xclk60mhsp2_ck);
+		dev_err(dev, "xclk60mhsp2_ck failed error:%d\n", ret);
+		goto err_utmi_p2_fck;
+	}
+
+	omap->usbhost_p1_fck = clk_get(dev, "usb_host_hs_utmi_p1_clk");
+	if (IS_ERR(omap->usbhost_p1_fck)) {
+		ret = PTR_ERR(omap->usbhost_p1_fck);
+		dev_err(dev, "usbhost_p1_fck failed error:%d\n", ret);
+		goto err_xclk60mhsp2_ck;
+	}
+
+	omap->usbtll_p1_fck = clk_get(dev, "usb_tll_hs_usb_ch0_clk");
+	if (IS_ERR(omap->usbtll_p1_fck)) {
+		ret = PTR_ERR(omap->usbtll_p1_fck);
+		dev_err(dev, "usbtll_p1_fck failed error:%d\n", ret);
+		goto err_usbhost_p1_fck;
+	}
+
+	omap->usbhost_p2_fck = clk_get(dev, "usb_host_hs_utmi_p2_clk");
+	if (IS_ERR(omap->usbhost_p2_fck)) {
+		ret = PTR_ERR(omap->usbhost_p2_fck);
+		dev_err(dev, "usbhost_p2_fck failed error:%d\n", ret);
+		goto err_usbtll_p1_fck;
+	}
+
+	omap->usbtll_p2_fck = clk_get(dev, "usb_tll_hs_usb_ch1_clk");
+	if (IS_ERR(omap->usbtll_p2_fck)) {
+		ret = PTR_ERR(omap->usbtll_p2_fck);
+		dev_err(dev, "usbtll_p2_fck failed error:%d\n", ret);
+		goto err_usbhost_p2_fck;
+	}
+
+	omap->init_60m_fclk = clk_get(dev, "init_60m_fclk");
+	if (IS_ERR(omap->init_60m_fclk)) {
+		ret = PTR_ERR(omap->init_60m_fclk);
+		dev_err(dev, "init_60m_fclk failed error:%d\n", ret);
+		goto err_usbtll_p2_fck;
+	}
+
+	if (is_ehci_phy_mode(pdata->port_mode[0])) {
+		/* for OMAP3 , the clk set paretn fails */
+		ret = clk_set_parent(omap->utmi_p1_fck,
+					omap->xclk60mhsp1_ck);
+		if (ret != 0)
+			dev_err(dev, "xclk60mhsp1_ck set parent"
+				"failed error:%d\n", ret);
+	} else if (is_ehci_tll_mode(pdata->port_mode[0])) {
+		ret = clk_set_parent(omap->utmi_p1_fck,
+					omap->init_60m_fclk);
+		if (ret != 0)
+			dev_err(dev, "init_60m_fclk set parent"
+				"failed error:%d\n", ret);
+	}
+
+	if (is_ehci_phy_mode(pdata->port_mode[1])) {
+		ret = clk_set_parent(omap->utmi_p2_fck,
+					omap->xclk60mhsp2_ck);
+		if (ret != 0)
+			dev_err(dev, "xclk60mhsp2_ck set parent"
+					"failed error:%d\n", ret);
+	} else if (is_ehci_tll_mode(pdata->port_mode[1])) {
+		ret = clk_set_parent(omap->utmi_p2_fck,
+						omap->init_60m_fclk);
+		if (ret != 0)
+			dev_err(dev, "init_60m_fclk set parent"
+				"failed error:%d\n", ret);
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "uhh");
+	if (!res) {
+		dev_err(dev, "UHH EHCI get resource failed\n");
+		ret = -ENODEV;
+		goto err_init_60m_fclk;
+	}
+
+	omap->uhh_base = ioremap(res->start, resource_size(res));
+	if (!omap->uhh_base) {
+		dev_err(dev, "UHH ioremap failed\n");
+		ret = -ENOMEM;
+		goto err_init_60m_fclk;
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tll");
+	if (!res) {
+		dev_err(dev, "UHH EHCI get resource failed\n");
+		ret = -ENODEV;
+		goto err_tll;
+	}
+
+	omap->tll_base = ioremap(res->start, resource_size(res));
+	if (!omap->tll_base) {
+		dev_err(dev, "TLL ioremap failed\n");
+		ret = -ENOMEM;
+		goto err_tll;
+	}
+
+	platform_set_drvdata(pdev, omap);
+
+	omap_usbhs_init(dev);
+	ret = omap_usbhs_alloc_children(pdev);
+	if (ret) {
+		dev_err(dev, "omap_usbhs_alloc_children failed\n");
+		goto err_alloc;
+	}
+
+	goto end_probe;
+
+err_alloc:
+	iounmap(omap->tll_base);
+
+err_tll:
+	iounmap(omap->uhh_base);
+
+err_init_60m_fclk:
+	clk_put(omap->init_60m_fclk);
+
+err_usbtll_p2_fck:
+	clk_put(omap->usbtll_p2_fck);
+
+err_usbhost_p2_fck:
+	clk_put(omap->usbhost_p2_fck);
+
+err_usbtll_p1_fck:
+	clk_put(omap->usbtll_p1_fck);
+
+err_usbhost_p1_fck:
+	clk_put(omap->usbhost_p1_fck);
+
+err_xclk60mhsp2_ck:
+	clk_put(omap->xclk60mhsp2_ck);
+
+err_utmi_p2_fck:
+	clk_put(omap->utmi_p2_fck);
+
+err_xclk60mhsp1_ck:
+	clk_put(omap->xclk60mhsp1_ck);
+
+err_utmi_p1_fck:
+	clk_put(omap->utmi_p1_fck);
+
+err_end:
+	clk_put(omap->ehci_logic_fck);
+	pm_runtime_disable(dev);
+	kfree(omap);
+
+end_probe:
+	return ret;
+}
+
+/**
+ * usbhs_omap_remove - shutdown processing for UHH & TLL HCDs
+ * @pdev: USB Host Controller being removed
+ *
+ * Reverses the effect of usbhs_omap_probe().
+ */
+static int __devexit usbhs_omap_remove(struct platform_device *pdev)
+{
+	struct usbhs_hcd_omap *omap = platform_get_drvdata(pdev);
+
+	iounmap(omap->tll_base);
+	iounmap(omap->uhh_base);
+	clk_put(omap->init_60m_fclk);
+	clk_put(omap->usbtll_p2_fck);
+	clk_put(omap->usbhost_p2_fck);
+	clk_put(omap->usbtll_p1_fck);
+	clk_put(omap->usbhost_p1_fck);
+	clk_put(omap->xclk60mhsp2_ck);
+	clk_put(omap->utmi_p2_fck);
+	clk_put(omap->xclk60mhsp1_ck);
+	clk_put(omap->utmi_p1_fck);
+	clk_put(omap->ehci_logic_fck);
+	pm_runtime_disable(&pdev->dev);
+	kfree(omap);
+
+	return 0;
+}
+
+static const struct dev_pm_ops usbhsomap_dev_pm_ops = {
+	.runtime_suspend	= usbhs_runtime_suspend,
+	.runtime_resume		= usbhs_runtime_resume,
+};
+
+static struct platform_driver usbhs_omap_driver = {
+	.driver = {
+		.name		= (char *)usbhs_driver_name,
+		.owner		= THIS_MODULE,
+		.pm		= &usbhsomap_dev_pm_ops,
+	},
+	.remove		= __exit_p(usbhs_omap_remove),
+};
+
+MODULE_AUTHOR("Keshava Munegowda <keshava_mgowda@ti.com>");
+MODULE_ALIAS("platform:" USBHS_DRIVER_NAME);
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("usb host common core driver for omap EHCI and OHCI");
+
+static int __init omap_usbhs_drvinit(void)
+{
+	return platform_driver_probe(&usbhs_omap_driver, usbhs_omap_probe);
+}
+
+/*
+ * init before ehci and ohci drivers;
+ * The usbhs core driver should be initialized much before
+ * the omap ehci and ohci probe functions are called.
+ */
+fs_initcall(omap_usbhs_drvinit);
+
+static void __exit omap_usbhs_drvexit(void)
+{
+	platform_driver_unregister(&usbhs_omap_driver);
+}
+module_exit(omap_usbhs_drvexit);
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/pcf50633-adc.c b/ap/os/linux/linux-3.4.x/drivers/mfd/pcf50633-adc.c
new file mode 100644
index 0000000..3927c17
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/pcf50633-adc.c
@@ -0,0 +1,258 @@
+/* NXP PCF50633 ADC Driver
+ *
+ * (C) 2006-2008 by Openmoko, Inc.
+ * Author: Balaji Rao <balajirrao@openmoko.org>
+ * All rights reserved.
+ *
+ * Broken down from monstrous PCF50633 driver mainly by
+ * Harald Welte, Andy Green and Werner Almesberger
+ *
+ *  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.
+ *
+ *  NOTE: This driver does not yet support subtractive ADC mode, which means
+ *  you can do only one measurement per read request.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/completion.h>
+
+#include <linux/mfd/pcf50633/core.h>
+#include <linux/mfd/pcf50633/adc.h>
+
+struct pcf50633_adc_request {
+	int mux;
+	int avg;
+	void (*callback)(struct pcf50633 *, void *, int);
+	void *callback_param;
+};
+
+struct pcf50633_adc_sync_request {
+	int result;
+	struct completion completion;
+};
+
+#define PCF50633_MAX_ADC_FIFO_DEPTH 8
+
+struct pcf50633_adc {
+	struct pcf50633 *pcf;
+
+	/* Private stuff */
+	struct pcf50633_adc_request *queue[PCF50633_MAX_ADC_FIFO_DEPTH];
+	int queue_head;
+	int queue_tail;
+	struct mutex queue_mutex;
+};
+
+static inline struct pcf50633_adc *__to_adc(struct pcf50633 *pcf)
+{
+	return platform_get_drvdata(pcf->adc_pdev);
+}
+
+static void adc_setup(struct pcf50633 *pcf, int channel, int avg)
+{
+	channel &= PCF50633_ADCC1_ADCMUX_MASK;
+
+	/* kill ratiometric, but enable ACCSW biasing */
+	pcf50633_reg_write(pcf, PCF50633_REG_ADCC2, 0x00);
+	pcf50633_reg_write(pcf, PCF50633_REG_ADCC3, 0x01);
+
+	/* start ADC conversion on selected channel */
+	pcf50633_reg_write(pcf, PCF50633_REG_ADCC1, channel | avg |
+		    PCF50633_ADCC1_ADCSTART | PCF50633_ADCC1_RES_10BIT);
+}
+
+static void trigger_next_adc_job_if_any(struct pcf50633 *pcf)
+{
+	struct pcf50633_adc *adc = __to_adc(pcf);
+	int head;
+
+	head = adc->queue_head;
+
+	if (!adc->queue[head])
+		return;
+
+	adc_setup(pcf, adc->queue[head]->mux, adc->queue[head]->avg);
+}
+
+static int
+adc_enqueue_request(struct pcf50633 *pcf, struct pcf50633_adc_request *req)
+{
+	struct pcf50633_adc *adc = __to_adc(pcf);
+	int head, tail;
+
+	mutex_lock(&adc->queue_mutex);
+
+	head = adc->queue_head;
+	tail = adc->queue_tail;
+
+	if (adc->queue[tail]) {
+		mutex_unlock(&adc->queue_mutex);
+		dev_err(pcf->dev, "ADC queue is full, dropping request\n");
+		return -EBUSY;
+	}
+
+	adc->queue[tail] = req;
+	if (head == tail)
+		trigger_next_adc_job_if_any(pcf);
+	adc->queue_tail = (tail + 1) & (PCF50633_MAX_ADC_FIFO_DEPTH - 1);
+
+	mutex_unlock(&adc->queue_mutex);
+
+	return 0;
+}
+
+static void pcf50633_adc_sync_read_callback(struct pcf50633 *pcf, void *param,
+	int result)
+{
+	struct pcf50633_adc_sync_request *req = param;
+
+	req->result = result;
+	complete(&req->completion);
+}
+
+int pcf50633_adc_sync_read(struct pcf50633 *pcf, int mux, int avg)
+{
+	struct pcf50633_adc_sync_request req;
+	int ret;
+
+	init_completion(&req.completion);
+
+	ret = pcf50633_adc_async_read(pcf, mux, avg,
+		pcf50633_adc_sync_read_callback, &req);
+	if (ret)
+		return ret;
+
+	wait_for_completion(&req.completion);
+
+	return req.result;
+}
+EXPORT_SYMBOL_GPL(pcf50633_adc_sync_read);
+
+int pcf50633_adc_async_read(struct pcf50633 *pcf, int mux, int avg,
+			     void (*callback)(struct pcf50633 *, void *, int),
+			     void *callback_param)
+{
+	struct pcf50633_adc_request *req;
+
+	/* req is freed when the result is ready, in interrupt handler */
+	req = kmalloc(sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	req->mux = mux;
+	req->avg = avg;
+	req->callback = callback;
+	req->callback_param = callback_param;
+
+	return adc_enqueue_request(pcf, req);
+}
+EXPORT_SYMBOL_GPL(pcf50633_adc_async_read);
+
+static int adc_result(struct pcf50633 *pcf)
+{
+	u8 adcs1, adcs3;
+	u16 result;
+
+	adcs1 = pcf50633_reg_read(pcf, PCF50633_REG_ADCS1);
+	adcs3 = pcf50633_reg_read(pcf, PCF50633_REG_ADCS3);
+	result = (adcs1 << 2) | (adcs3 & PCF50633_ADCS3_ADCDAT1L_MASK);
+
+	dev_dbg(pcf->dev, "adc result = %d\n", result);
+
+	return result;
+}
+
+static void pcf50633_adc_irq(int irq, void *data)
+{
+	struct pcf50633_adc *adc = data;
+	struct pcf50633 *pcf = adc->pcf;
+	struct pcf50633_adc_request *req;
+	int head, res;
+
+	mutex_lock(&adc->queue_mutex);
+	head = adc->queue_head;
+
+	req = adc->queue[head];
+	if (WARN_ON(!req)) {
+		dev_err(pcf->dev, "pcf50633-adc irq: ADC queue empty!\n");
+		mutex_unlock(&adc->queue_mutex);
+		return;
+	}
+	adc->queue[head] = NULL;
+	adc->queue_head = (head + 1) &
+				      (PCF50633_MAX_ADC_FIFO_DEPTH - 1);
+
+	res = adc_result(pcf);
+	trigger_next_adc_job_if_any(pcf);
+
+	mutex_unlock(&adc->queue_mutex);
+
+	req->callback(pcf, req->callback_param, res);
+	kfree(req);
+}
+
+static int __devinit pcf50633_adc_probe(struct platform_device *pdev)
+{
+	struct pcf50633_adc *adc;
+
+	adc = kzalloc(sizeof(*adc), GFP_KERNEL);
+	if (!adc)
+		return -ENOMEM;
+
+	adc->pcf = dev_to_pcf50633(pdev->dev.parent);
+	platform_set_drvdata(pdev, adc);
+
+	pcf50633_register_irq(adc->pcf, PCF50633_IRQ_ADCRDY,
+					pcf50633_adc_irq, adc);
+
+	mutex_init(&adc->queue_mutex);
+
+	return 0;
+}
+
+static int __devexit pcf50633_adc_remove(struct platform_device *pdev)
+{
+	struct pcf50633_adc *adc = platform_get_drvdata(pdev);
+	int i, head;
+
+	pcf50633_free_irq(adc->pcf, PCF50633_IRQ_ADCRDY);
+
+	mutex_lock(&adc->queue_mutex);
+	head = adc->queue_head;
+
+	if (WARN_ON(adc->queue[head]))
+		dev_err(adc->pcf->dev,
+			"adc driver removed with request pending\n");
+
+	for (i = 0; i < PCF50633_MAX_ADC_FIFO_DEPTH; i++)
+		kfree(adc->queue[i]);
+
+	mutex_unlock(&adc->queue_mutex);
+	kfree(adc);
+
+	return 0;
+}
+
+static struct platform_driver pcf50633_adc_driver = {
+	.driver = {
+		.name = "pcf50633-adc",
+	},
+	.probe = pcf50633_adc_probe,
+	.remove = __devexit_p(pcf50633_adc_remove),
+};
+
+module_platform_driver(pcf50633_adc_driver);
+
+MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>");
+MODULE_DESCRIPTION("PCF50633 adc driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pcf50633-adc");
+
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/pcf50633-core.c b/ap/os/linux/linux-3.4.x/drivers/mfd/pcf50633-core.c
new file mode 100644
index 0000000..189c2f0
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/pcf50633-core.c
@@ -0,0 +1,340 @@
+/* NXP PCF50633 Power Management Unit (PMU) driver
+ *
+ * (C) 2006-2008 by Openmoko, Inc.
+ * Author: Harald Welte <laforge@openmoko.org>
+ * 	   Balaji Rao <balajirrao@openmoko.org>
+ * All rights reserved.
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/sysfs.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
+#include <linux/err.h>
+
+#include <linux/mfd/pcf50633/core.h>
+
+/* Read a block of up to 32 regs  */
+int pcf50633_read_block(struct pcf50633 *pcf, u8 reg,
+					int nr_regs, u8 *data)
+{
+	int ret;
+
+	ret = regmap_raw_read(pcf->regmap, reg, data, nr_regs);
+	if (ret != 0)
+		return ret;
+
+	return nr_regs;
+}
+EXPORT_SYMBOL_GPL(pcf50633_read_block);
+
+/* Write a block of up to 32 regs  */
+int pcf50633_write_block(struct pcf50633 *pcf , u8 reg,
+					int nr_regs, u8 *data)
+{
+	return regmap_raw_write(pcf->regmap, reg, data, nr_regs);
+}
+EXPORT_SYMBOL_GPL(pcf50633_write_block);
+
+u8 pcf50633_reg_read(struct pcf50633 *pcf, u8 reg)
+{
+	unsigned int val;
+	int ret;
+
+	ret = regmap_read(pcf->regmap, reg, &val);
+	if (ret < 0)
+		return -1;
+
+	return val;
+}
+EXPORT_SYMBOL_GPL(pcf50633_reg_read);
+
+int pcf50633_reg_write(struct pcf50633 *pcf, u8 reg, u8 val)
+{
+	return regmap_write(pcf->regmap, reg, val);
+}
+EXPORT_SYMBOL_GPL(pcf50633_reg_write);
+
+int pcf50633_reg_set_bit_mask(struct pcf50633 *pcf, u8 reg, u8 mask, u8 val)
+{
+	return regmap_update_bits(pcf->regmap, reg, mask, val);
+}
+EXPORT_SYMBOL_GPL(pcf50633_reg_set_bit_mask);
+
+int pcf50633_reg_clear_bits(struct pcf50633 *pcf, u8 reg, u8 val)
+{
+	return regmap_update_bits(pcf->regmap, reg, val, 0);
+}
+EXPORT_SYMBOL_GPL(pcf50633_reg_clear_bits);
+
+/* sysfs attributes */
+static ssize_t show_dump_regs(struct device *dev, struct device_attribute *attr,
+			    char *buf)
+{
+	struct pcf50633 *pcf = dev_get_drvdata(dev);
+	u8 dump[16];
+	int n, n1, idx = 0;
+	char *buf1 = buf;
+	static u8 address_no_read[] = { /* must be ascending */
+		PCF50633_REG_INT1,
+		PCF50633_REG_INT2,
+		PCF50633_REG_INT3,
+		PCF50633_REG_INT4,
+		PCF50633_REG_INT5,
+		0 /* terminator */
+	};
+
+	for (n = 0; n < 256; n += sizeof(dump)) {
+		for (n1 = 0; n1 < sizeof(dump); n1++)
+			if (n == address_no_read[idx]) {
+				idx++;
+				dump[n1] = 0x00;
+			} else
+				dump[n1] = pcf50633_reg_read(pcf, n + n1);
+
+		hex_dump_to_buffer(dump, sizeof(dump), 16, 1, buf1, 128, 0);
+		buf1 += strlen(buf1);
+		*buf1++ = '\n';
+		*buf1 = '\0';
+	}
+
+	return buf1 - buf;
+}
+static DEVICE_ATTR(dump_regs, 0400, show_dump_regs, NULL);
+
+static ssize_t show_resume_reason(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct pcf50633 *pcf = dev_get_drvdata(dev);
+	int n;
+
+	n = sprintf(buf, "%02x%02x%02x%02x%02x\n",
+				pcf->resume_reason[0],
+				pcf->resume_reason[1],
+				pcf->resume_reason[2],
+				pcf->resume_reason[3],
+				pcf->resume_reason[4]);
+
+	return n;
+}
+static DEVICE_ATTR(resume_reason, 0400, show_resume_reason, NULL);
+
+static struct attribute *pcf_sysfs_entries[] = {
+	&dev_attr_dump_regs.attr,
+	&dev_attr_resume_reason.attr,
+	NULL,
+};
+
+static struct attribute_group pcf_attr_group = {
+	.name	= NULL,			/* put in device directory */
+	.attrs	= pcf_sysfs_entries,
+};
+
+static void
+pcf50633_client_dev_register(struct pcf50633 *pcf, const char *name,
+						struct platform_device **pdev)
+{
+	int ret;
+
+	*pdev = platform_device_alloc(name, -1);
+	if (!*pdev) {
+		dev_err(pcf->dev, "Falied to allocate %s\n", name);
+		return;
+	}
+
+	(*pdev)->dev.parent = pcf->dev;
+
+	ret = platform_device_add(*pdev);
+	if (ret) {
+		dev_err(pcf->dev, "Failed to register %s: %d\n", name, ret);
+		platform_device_put(*pdev);
+		*pdev = NULL;
+	}
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int pcf50633_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct pcf50633 *pcf = i2c_get_clientdata(client);
+
+	return pcf50633_irq_suspend(pcf);
+}
+
+static int pcf50633_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct pcf50633 *pcf = i2c_get_clientdata(client);
+
+	return pcf50633_irq_resume(pcf);
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(pcf50633_pm, pcf50633_suspend, pcf50633_resume);
+
+static struct regmap_config pcf50633_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+};
+
+static int __devinit pcf50633_probe(struct i2c_client *client,
+				const struct i2c_device_id *ids)
+{
+	struct pcf50633 *pcf;
+	struct pcf50633_platform_data *pdata = client->dev.platform_data;
+	int i, ret;
+	int version, variant;
+
+	if (!client->irq) {
+		dev_err(&client->dev, "Missing IRQ\n");
+		return -ENOENT;
+	}
+
+	pcf = kzalloc(sizeof(*pcf), GFP_KERNEL);
+	if (!pcf)
+		return -ENOMEM;
+
+	pcf->pdata = pdata;
+
+	mutex_init(&pcf->lock);
+
+	pcf->regmap = regmap_init_i2c(client, &pcf50633_regmap_config);
+	if (IS_ERR(pcf->regmap)) {
+		ret = PTR_ERR(pcf->regmap);
+		dev_err(pcf->dev, "Failed to allocate register map: %d\n",
+			ret);
+		goto err_free;
+	}
+
+	i2c_set_clientdata(client, pcf);
+	pcf->dev = &client->dev;
+
+	version = pcf50633_reg_read(pcf, 0);
+	variant = pcf50633_reg_read(pcf, 1);
+	if (version < 0 || variant < 0) {
+		dev_err(pcf->dev, "Unable to probe pcf50633\n");
+		ret = -ENODEV;
+		goto err_regmap;
+	}
+
+	dev_info(pcf->dev, "Probed device version %d variant %d\n",
+							version, variant);
+
+	pcf50633_irq_init(pcf, client->irq);
+
+	/* Create sub devices */
+	pcf50633_client_dev_register(pcf, "pcf50633-input",
+						&pcf->input_pdev);
+	pcf50633_client_dev_register(pcf, "pcf50633-rtc",
+						&pcf->rtc_pdev);
+	pcf50633_client_dev_register(pcf, "pcf50633-mbc",
+						&pcf->mbc_pdev);
+	pcf50633_client_dev_register(pcf, "pcf50633-adc",
+						&pcf->adc_pdev);
+	pcf50633_client_dev_register(pcf, "pcf50633-backlight",
+						&pcf->bl_pdev);
+
+
+	for (i = 0; i < PCF50633_NUM_REGULATORS; i++) {
+		struct platform_device *pdev;
+
+		pdev = platform_device_alloc("pcf50633-regltr", i);
+		if (!pdev) {
+			dev_err(pcf->dev, "Cannot create regulator %d\n", i);
+			continue;
+		}
+
+		pdev->dev.parent = pcf->dev;
+		platform_device_add_data(pdev, &pdata->reg_init_data[i],
+					sizeof(pdata->reg_init_data[i]));
+		pcf->regulator_pdev[i] = pdev;
+
+		platform_device_add(pdev);
+	}
+
+	ret = sysfs_create_group(&client->dev.kobj, &pcf_attr_group);
+	if (ret)
+		dev_err(pcf->dev, "error creating sysfs entries\n");
+
+	if (pdata->probe_done)
+		pdata->probe_done(pcf);
+
+	return 0;
+
+err_regmap:
+	regmap_exit(pcf->regmap);
+err_free:
+	kfree(pcf);
+
+	return ret;
+}
+
+static int __devexit pcf50633_remove(struct i2c_client *client)
+{
+	struct pcf50633 *pcf = i2c_get_clientdata(client);
+	int i;
+
+	sysfs_remove_group(&client->dev.kobj, &pcf_attr_group);
+	pcf50633_irq_free(pcf);
+
+	platform_device_unregister(pcf->input_pdev);
+	platform_device_unregister(pcf->rtc_pdev);
+	platform_device_unregister(pcf->mbc_pdev);
+	platform_device_unregister(pcf->adc_pdev);
+	platform_device_unregister(pcf->bl_pdev);
+
+	for (i = 0; i < PCF50633_NUM_REGULATORS; i++)
+		platform_device_unregister(pcf->regulator_pdev[i]);
+
+	regmap_exit(pcf->regmap);
+	kfree(pcf);
+
+	return 0;
+}
+
+static const struct i2c_device_id pcf50633_id_table[] = {
+	{"pcf50633", 0x73},
+	{/* end of list */}
+};
+MODULE_DEVICE_TABLE(i2c, pcf50633_id_table);
+
+static struct i2c_driver pcf50633_driver = {
+	.driver = {
+		.name	= "pcf50633",
+		.pm	= &pcf50633_pm,
+	},
+	.id_table = pcf50633_id_table,
+	.probe = pcf50633_probe,
+	.remove = __devexit_p(pcf50633_remove),
+};
+
+static int __init pcf50633_init(void)
+{
+	return i2c_add_driver(&pcf50633_driver);
+}
+
+static void __exit pcf50633_exit(void)
+{
+	i2c_del_driver(&pcf50633_driver);
+}
+
+MODULE_DESCRIPTION("I2C chip driver for NXP PCF50633 PMU");
+MODULE_AUTHOR("Harald Welte <laforge@openmoko.org>");
+MODULE_LICENSE("GPL");
+
+subsys_initcall(pcf50633_init);
+module_exit(pcf50633_exit);
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/pcf50633-gpio.c b/ap/os/linux/linux-3.4.x/drivers/mfd/pcf50633-gpio.c
new file mode 100644
index 0000000..d02ddf2
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/pcf50633-gpio.c
@@ -0,0 +1,96 @@
+/* NXP PCF50633 GPIO Driver
+ *
+ * (C) 2006-2008 by Openmoko, Inc.
+ * Author: Balaji Rao <balajirrao@openmoko.org>
+ * All rights reserved.
+ *
+ * Broken down from monstrous PCF50633 driver mainly by
+ * Harald Welte, Andy Green and Werner Almesberger
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include <linux/mfd/pcf50633/core.h>
+#include <linux/mfd/pcf50633/gpio.h>
+#include <linux/mfd/pcf50633/pmic.h>
+
+static const u8 pcf50633_regulator_registers[PCF50633_NUM_REGULATORS] = {
+	[PCF50633_REGULATOR_AUTO]	= PCF50633_REG_AUTOOUT,
+	[PCF50633_REGULATOR_DOWN1]	= PCF50633_REG_DOWN1OUT,
+	[PCF50633_REGULATOR_DOWN2]	= PCF50633_REG_DOWN2OUT,
+	[PCF50633_REGULATOR_MEMLDO]	= PCF50633_REG_MEMLDOOUT,
+	[PCF50633_REGULATOR_LDO1]	= PCF50633_REG_LDO1OUT,
+	[PCF50633_REGULATOR_LDO2]	= PCF50633_REG_LDO2OUT,
+	[PCF50633_REGULATOR_LDO3]	= PCF50633_REG_LDO3OUT,
+	[PCF50633_REGULATOR_LDO4]	= PCF50633_REG_LDO4OUT,
+	[PCF50633_REGULATOR_LDO5]	= PCF50633_REG_LDO5OUT,
+	[PCF50633_REGULATOR_LDO6]	= PCF50633_REG_LDO6OUT,
+	[PCF50633_REGULATOR_HCLDO]	= PCF50633_REG_HCLDOOUT,
+};
+
+int pcf50633_gpio_set(struct pcf50633 *pcf, int gpio, u8 val)
+{
+	u8 reg;
+
+	reg = gpio - PCF50633_GPIO1 + PCF50633_REG_GPIO1CFG;
+
+	return pcf50633_reg_set_bit_mask(pcf, reg, 0x07, val);
+}
+EXPORT_SYMBOL_GPL(pcf50633_gpio_set);
+
+u8 pcf50633_gpio_get(struct pcf50633 *pcf, int gpio)
+{
+	u8 reg, val;
+
+	reg = gpio - PCF50633_GPIO1 + PCF50633_REG_GPIO1CFG;
+	val = pcf50633_reg_read(pcf, reg) & 0x07;
+
+	return val;
+}
+EXPORT_SYMBOL_GPL(pcf50633_gpio_get);
+
+int pcf50633_gpio_invert_set(struct pcf50633 *pcf, int gpio, int invert)
+{
+	u8 val, reg;
+
+	reg = gpio - PCF50633_GPIO1 + PCF50633_REG_GPIO1CFG;
+	val = !!invert << 3;
+
+	return pcf50633_reg_set_bit_mask(pcf, reg, 1 << 3, val);
+}
+EXPORT_SYMBOL_GPL(pcf50633_gpio_invert_set);
+
+int pcf50633_gpio_invert_get(struct pcf50633 *pcf, int gpio)
+{
+	u8 reg, val;
+
+	reg = gpio - PCF50633_GPIO1 + PCF50633_REG_GPIO1CFG;
+	val = pcf50633_reg_read(pcf, reg);
+
+	return val & (1 << 3);
+}
+EXPORT_SYMBOL_GPL(pcf50633_gpio_invert_get);
+
+int pcf50633_gpio_power_supply_set(struct pcf50633 *pcf,
+					int gpio, int regulator, int on)
+{
+	u8 reg, val, mask;
+
+	/* the *ENA register is always one after the *OUT register */
+	reg = pcf50633_regulator_registers[regulator] + 1;
+
+	val = !!on << (gpio - PCF50633_GPIO1);
+	mask = 1 << (gpio - PCF50633_GPIO1);
+
+	return pcf50633_reg_set_bit_mask(pcf, reg, mask, val);
+}
+EXPORT_SYMBOL_GPL(pcf50633_gpio_power_supply_set);
+
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/pcf50633-irq.c b/ap/os/linux/linux-3.4.x/drivers/mfd/pcf50633-irq.c
new file mode 100644
index 0000000..498286c
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/pcf50633-irq.c
@@ -0,0 +1,314 @@
+/* NXP PCF50633 Power Management Unit (PMU) driver
+ *
+ * (C) 2006-2008 by Openmoko, Inc.
+ * Author: Harald Welte <laforge@openmoko.org>
+ * 	   Balaji Rao <balajirrao@openmoko.org>
+ * All rights reserved.
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+
+#include <linux/mfd/pcf50633/core.h>
+#include <linux/mfd/pcf50633/mbc.h>
+
+int pcf50633_register_irq(struct pcf50633 *pcf, int irq,
+			void (*handler) (int, void *), void *data)
+{
+	if (irq < 0 || irq >= PCF50633_NUM_IRQ || !handler)
+		return -EINVAL;
+
+	if (WARN_ON(pcf->irq_handler[irq].handler))
+		return -EBUSY;
+
+	mutex_lock(&pcf->lock);
+	pcf->irq_handler[irq].handler = handler;
+	pcf->irq_handler[irq].data = data;
+	mutex_unlock(&pcf->lock);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pcf50633_register_irq);
+
+int pcf50633_free_irq(struct pcf50633 *pcf, int irq)
+{
+	if (irq < 0 || irq >= PCF50633_NUM_IRQ)
+		return -EINVAL;
+
+	mutex_lock(&pcf->lock);
+	pcf->irq_handler[irq].handler = NULL;
+	mutex_unlock(&pcf->lock);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pcf50633_free_irq);
+
+static int __pcf50633_irq_mask_set(struct pcf50633 *pcf, int irq, u8 mask)
+{
+	u8 reg, bit;
+	int ret = 0, idx;
+
+	idx = irq >> 3;
+	reg = PCF50633_REG_INT1M + idx;
+	bit = 1 << (irq & 0x07);
+
+	pcf50633_reg_set_bit_mask(pcf, reg, bit, mask ? bit : 0);
+
+	mutex_lock(&pcf->lock);
+
+	if (mask)
+		pcf->mask_regs[idx] |= bit;
+	else
+		pcf->mask_regs[idx] &= ~bit;
+
+	mutex_unlock(&pcf->lock);
+
+	return ret;
+}
+
+int pcf50633_irq_mask(struct pcf50633 *pcf, int irq)
+{
+	dev_dbg(pcf->dev, "Masking IRQ %d\n", irq);
+
+	return __pcf50633_irq_mask_set(pcf, irq, 1);
+}
+EXPORT_SYMBOL_GPL(pcf50633_irq_mask);
+
+int pcf50633_irq_unmask(struct pcf50633 *pcf, int irq)
+{
+	dev_dbg(pcf->dev, "Unmasking IRQ %d\n", irq);
+
+	return __pcf50633_irq_mask_set(pcf, irq, 0);
+}
+EXPORT_SYMBOL_GPL(pcf50633_irq_unmask);
+
+int pcf50633_irq_mask_get(struct pcf50633 *pcf, int irq)
+{
+	u8 reg, bits;
+
+	reg =  irq >> 3;
+	bits = 1 << (irq & 0x07);
+
+	return pcf->mask_regs[reg] & bits;
+}
+EXPORT_SYMBOL_GPL(pcf50633_irq_mask_get);
+
+static void pcf50633_irq_call_handler(struct pcf50633 *pcf, int irq)
+{
+	if (pcf->irq_handler[irq].handler)
+		pcf->irq_handler[irq].handler(irq, pcf->irq_handler[irq].data);
+}
+
+/* Maximum amount of time ONKEY is held before emergency action is taken */
+#define PCF50633_ONKEY1S_TIMEOUT 8
+
+static irqreturn_t pcf50633_irq(int irq, void *data)
+{
+	struct pcf50633 *pcf = data;
+	int ret, i, j;
+	u8 pcf_int[5], chgstat;
+
+	/* Read the 5 INT regs in one transaction */
+	ret = pcf50633_read_block(pcf, PCF50633_REG_INT1,
+						ARRAY_SIZE(pcf_int), pcf_int);
+	if (ret != ARRAY_SIZE(pcf_int)) {
+		dev_err(pcf->dev, "Error reading INT registers\n");
+
+		/*
+		 * If this doesn't ACK the interrupt to the chip, we'll be
+		 * called once again as we're level triggered.
+		 */
+		goto out;
+	}
+
+	/* defeat 8s death from lowsys on A5 */
+	pcf50633_reg_write(pcf, PCF50633_REG_OOCSHDWN,  0x04);
+
+	/* We immediately read the usb and adapter status. We thus make sure
+	 * only of USBINS/USBREM IRQ handlers are called */
+	if (pcf_int[0] & (PCF50633_INT1_USBINS | PCF50633_INT1_USBREM)) {
+		chgstat = pcf50633_reg_read(pcf, PCF50633_REG_MBCS2);
+		if (chgstat & (0x3 << 4))
+			pcf_int[0] &= ~PCF50633_INT1_USBREM;
+		else
+			pcf_int[0] &= ~PCF50633_INT1_USBINS;
+	}
+
+	/* Make sure only one of ADPINS or ADPREM is set */
+	if (pcf_int[0] & (PCF50633_INT1_ADPINS | PCF50633_INT1_ADPREM)) {
+		chgstat = pcf50633_reg_read(pcf, PCF50633_REG_MBCS2);
+		if (chgstat & (0x3 << 4))
+			pcf_int[0] &= ~PCF50633_INT1_ADPREM;
+		else
+			pcf_int[0] &= ~PCF50633_INT1_ADPINS;
+	}
+
+	dev_dbg(pcf->dev, "INT1=0x%02x INT2=0x%02x INT3=0x%02x "
+			"INT4=0x%02x INT5=0x%02x\n", pcf_int[0],
+			pcf_int[1], pcf_int[2], pcf_int[3], pcf_int[4]);
+
+	/* Some revisions of the chip don't have a 8s standby mode on
+	 * ONKEY1S press. We try to manually do it in such cases. */
+	if ((pcf_int[0] & PCF50633_INT1_SECOND) && pcf->onkey1s_held) {
+		dev_info(pcf->dev, "ONKEY1S held for %d secs\n",
+							pcf->onkey1s_held);
+		if (pcf->onkey1s_held++ == PCF50633_ONKEY1S_TIMEOUT)
+			if (pcf->pdata->force_shutdown)
+				pcf->pdata->force_shutdown(pcf);
+	}
+
+	if (pcf_int[2] & PCF50633_INT3_ONKEY1S) {
+		dev_info(pcf->dev, "ONKEY1S held\n");
+		pcf->onkey1s_held = 1 ;
+
+		/* Unmask IRQ_SECOND */
+		pcf50633_reg_clear_bits(pcf, PCF50633_REG_INT1M,
+						PCF50633_INT1_SECOND);
+
+		/* Unmask IRQ_ONKEYR */
+		pcf50633_reg_clear_bits(pcf, PCF50633_REG_INT2M,
+						PCF50633_INT2_ONKEYR);
+	}
+
+	if ((pcf_int[1] & PCF50633_INT2_ONKEYR) && pcf->onkey1s_held) {
+		pcf->onkey1s_held = 0;
+
+		/* Mask SECOND and ONKEYR interrupts */
+		if (pcf->mask_regs[0] & PCF50633_INT1_SECOND)
+			pcf50633_reg_set_bit_mask(pcf,
+					PCF50633_REG_INT1M,
+					PCF50633_INT1_SECOND,
+					PCF50633_INT1_SECOND);
+
+		if (pcf->mask_regs[1] & PCF50633_INT2_ONKEYR)
+			pcf50633_reg_set_bit_mask(pcf,
+					PCF50633_REG_INT2M,
+					PCF50633_INT2_ONKEYR,
+					PCF50633_INT2_ONKEYR);
+	}
+
+	/* Have we just resumed ? */
+	if (pcf->is_suspended) {
+		pcf->is_suspended = 0;
+
+		/* Set the resume reason filtering out non resumers */
+		for (i = 0; i < ARRAY_SIZE(pcf_int); i++)
+			pcf->resume_reason[i] = pcf_int[i] &
+						pcf->pdata->resumers[i];
+
+		/* Make sure we don't pass on any ONKEY events to
+		 * userspace now */
+		pcf_int[1] &= ~(PCF50633_INT2_ONKEYR | PCF50633_INT2_ONKEYF);
+	}
+
+	for (i = 0; i < ARRAY_SIZE(pcf_int); i++) {
+		/* Unset masked interrupts */
+		pcf_int[i] &= ~pcf->mask_regs[i];
+
+		for (j = 0; j < 8 ; j++)
+			if (pcf_int[i] & (1 << j))
+				pcf50633_irq_call_handler(pcf, (i * 8) + j);
+	}
+
+out:
+	return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_PM
+
+int pcf50633_irq_suspend(struct pcf50633 *pcf)
+{
+	int ret;
+	int i;
+	u8 res[5];
+
+
+	/* Make sure our interrupt handlers are not called
+	 * henceforth */
+	disable_irq(pcf->irq);
+
+	/* Save the masks */
+	ret = pcf50633_read_block(pcf, PCF50633_REG_INT1M,
+				ARRAY_SIZE(pcf->suspend_irq_masks),
+					pcf->suspend_irq_masks);
+	if (ret < 0) {
+		dev_err(pcf->dev, "error saving irq masks\n");
+		goto out;
+	}
+
+	/* Write wakeup irq masks */
+	for (i = 0; i < ARRAY_SIZE(res); i++)
+		res[i] = ~pcf->pdata->resumers[i];
+
+	ret = pcf50633_write_block(pcf, PCF50633_REG_INT1M,
+					ARRAY_SIZE(res), &res[0]);
+	if (ret < 0) {
+		dev_err(pcf->dev, "error writing wakeup irq masks\n");
+		goto out;
+	}
+
+	pcf->is_suspended = 1;
+
+out:
+	return ret;
+}
+
+int pcf50633_irq_resume(struct pcf50633 *pcf)
+{
+	int ret;
+
+	/* Write the saved mask registers */
+	ret = pcf50633_write_block(pcf, PCF50633_REG_INT1M,
+				ARRAY_SIZE(pcf->suspend_irq_masks),
+					pcf->suspend_irq_masks);
+	if (ret < 0)
+		dev_err(pcf->dev, "Error restoring saved suspend masks\n");
+
+	enable_irq(pcf->irq);
+
+	return ret;
+}
+
+#endif
+
+int pcf50633_irq_init(struct pcf50633 *pcf, int irq)
+{
+	int ret;
+
+	pcf->irq = irq;
+
+	/* Enable all interrupts except RTC SECOND */
+	pcf->mask_regs[0] = 0x80;
+	pcf50633_reg_write(pcf, PCF50633_REG_INT1M, pcf->mask_regs[0]);
+	pcf50633_reg_write(pcf, PCF50633_REG_INT2M, 0x00);
+	pcf50633_reg_write(pcf, PCF50633_REG_INT3M, 0x00);
+	pcf50633_reg_write(pcf, PCF50633_REG_INT4M, 0x00);
+	pcf50633_reg_write(pcf, PCF50633_REG_INT5M, 0x00);
+
+	ret = request_threaded_irq(irq, NULL, pcf50633_irq,
+					IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+					"pcf50633", pcf);
+
+	if (ret)
+		dev_err(pcf->dev, "Failed to request IRQ %d\n", ret);
+
+	if (enable_irq_wake(irq) < 0)
+		dev_err(pcf->dev, "IRQ %u cannot be enabled as wake-up source"
+			"in this hardware revision", irq);
+
+	return ret;
+}
+
+void pcf50633_irq_free(struct pcf50633 *pcf)
+{
+	free_irq(pcf->irq, pcf);
+}
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/pm8921-core.c b/ap/os/linux/linux-3.4.x/drivers/mfd/pm8921-core.c
new file mode 100644
index 0000000..e873b15
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/pm8921-core.c
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/msm_ssbi.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/pm8xxx/pm8921.h>
+#include <linux/mfd/pm8xxx/core.h>
+
+#define REG_HWREV		0x002  /* PMIC4 revision */
+#define REG_HWREV_2		0x0E8  /* PMIC4 revision 2 */
+
+struct pm8921 {
+	struct device			*dev;
+	struct pm_irq_chip		*irq_chip;
+};
+
+static int pm8921_readb(const struct device *dev, u16 addr, u8 *val)
+{
+	const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev);
+	const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data;
+
+	return msm_ssbi_read(pmic->dev->parent, addr, val, 1);
+}
+
+static int pm8921_writeb(const struct device *dev, u16 addr, u8 val)
+{
+	const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev);
+	const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data;
+
+	return msm_ssbi_write(pmic->dev->parent, addr, &val, 1);
+}
+
+static int pm8921_read_buf(const struct device *dev, u16 addr, u8 *buf,
+									int cnt)
+{
+	const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev);
+	const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data;
+
+	return msm_ssbi_read(pmic->dev->parent, addr, buf, cnt);
+}
+
+static int pm8921_write_buf(const struct device *dev, u16 addr, u8 *buf,
+									int cnt)
+{
+	const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev);
+	const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data;
+
+	return msm_ssbi_write(pmic->dev->parent, addr, buf, cnt);
+}
+
+static int pm8921_read_irq_stat(const struct device *dev, int irq)
+{
+	const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev);
+	const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data;
+
+	return pm8xxx_get_irq_stat(pmic->irq_chip, irq);
+}
+
+static struct pm8xxx_drvdata pm8921_drvdata = {
+	.pmic_readb		= pm8921_readb,
+	.pmic_writeb		= pm8921_writeb,
+	.pmic_read_buf		= pm8921_read_buf,
+	.pmic_write_buf		= pm8921_write_buf,
+	.pmic_read_irq_stat	= pm8921_read_irq_stat,
+};
+
+static int __devinit pm8921_add_subdevices(const struct pm8921_platform_data
+					   *pdata,
+					   struct pm8921 *pmic,
+					   u32 rev)
+{
+	int ret = 0, irq_base = 0;
+	struct pm_irq_chip *irq_chip;
+
+	if (pdata->irq_pdata) {
+		pdata->irq_pdata->irq_cdata.nirqs = PM8921_NR_IRQS;
+		pdata->irq_pdata->irq_cdata.rev = rev;
+		irq_base = pdata->irq_pdata->irq_base;
+		irq_chip = pm8xxx_irq_init(pmic->dev, pdata->irq_pdata);
+
+		if (IS_ERR(irq_chip)) {
+			pr_err("Failed to init interrupts ret=%ld\n",
+					PTR_ERR(irq_chip));
+			return PTR_ERR(irq_chip);
+		}
+		pmic->irq_chip = irq_chip;
+	}
+	return ret;
+}
+
+static int __devinit pm8921_probe(struct platform_device *pdev)
+{
+	const struct pm8921_platform_data *pdata = pdev->dev.platform_data;
+	struct pm8921 *pmic;
+	int rc;
+	u8 val;
+	u32 rev;
+
+	if (!pdata) {
+		pr_err("missing platform data\n");
+		return -EINVAL;
+	}
+
+	pmic = kzalloc(sizeof(struct pm8921), GFP_KERNEL);
+	if (!pmic) {
+		pr_err("Cannot alloc pm8921 struct\n");
+		return -ENOMEM;
+	}
+
+	/* Read PMIC chip revision */
+	rc = msm_ssbi_read(pdev->dev.parent, REG_HWREV, &val, sizeof(val));
+	if (rc) {
+		pr_err("Failed to read hw rev reg %d:rc=%d\n", REG_HWREV, rc);
+		goto err_read_rev;
+	}
+	pr_info("PMIC revision 1: %02X\n", val);
+	rev = val;
+
+	/* Read PMIC chip revision 2 */
+	rc = msm_ssbi_read(pdev->dev.parent, REG_HWREV_2, &val, sizeof(val));
+	if (rc) {
+		pr_err("Failed to read hw rev 2 reg %d:rc=%d\n",
+			REG_HWREV_2, rc);
+		goto err_read_rev;
+	}
+	pr_info("PMIC revision 2: %02X\n", val);
+	rev |= val << BITS_PER_BYTE;
+
+	pmic->dev = &pdev->dev;
+	pm8921_drvdata.pm_chip_data = pmic;
+	platform_set_drvdata(pdev, &pm8921_drvdata);
+
+	rc = pm8921_add_subdevices(pdata, pmic, rev);
+	if (rc) {
+		pr_err("Cannot add subdevices rc=%d\n", rc);
+		goto err;
+	}
+
+	/* gpio might not work if no irq device is found */
+	WARN_ON(pmic->irq_chip == NULL);
+
+	return 0;
+
+err:
+	mfd_remove_devices(pmic->dev);
+	platform_set_drvdata(pdev, NULL);
+err_read_rev:
+	kfree(pmic);
+	return rc;
+}
+
+static int __devexit pm8921_remove(struct platform_device *pdev)
+{
+	struct pm8xxx_drvdata *drvdata;
+	struct pm8921 *pmic = NULL;
+
+	drvdata = platform_get_drvdata(pdev);
+	if (drvdata)
+		pmic = drvdata->pm_chip_data;
+	if (pmic)
+		mfd_remove_devices(pmic->dev);
+	if (pmic->irq_chip) {
+		pm8xxx_irq_exit(pmic->irq_chip);
+		pmic->irq_chip = NULL;
+	}
+	platform_set_drvdata(pdev, NULL);
+	kfree(pmic);
+
+	return 0;
+}
+
+static struct platform_driver pm8921_driver = {
+	.probe		= pm8921_probe,
+	.remove		= __devexit_p(pm8921_remove),
+	.driver		= {
+		.name	= "pm8921-core",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init pm8921_init(void)
+{
+	return platform_driver_register(&pm8921_driver);
+}
+subsys_initcall(pm8921_init);
+
+static void __exit pm8921_exit(void)
+{
+	platform_driver_unregister(&pm8921_driver);
+}
+module_exit(pm8921_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC 8921 core driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:pm8921-core");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/pm8xxx-irq.c b/ap/os/linux/linux-3.4.x/drivers/mfd/pm8xxx-irq.c
new file mode 100644
index 0000000..d452dd0
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/pm8xxx-irq.c
@@ -0,0 +1,371 @@
+/*
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt)	"%s: " fmt, __func__
+
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/mfd/pm8xxx/irq.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+/* PMIC8xxx IRQ */
+
+#define	SSBI_REG_ADDR_IRQ_BASE		0x1BB
+
+#define	SSBI_REG_ADDR_IRQ_ROOT		(SSBI_REG_ADDR_IRQ_BASE + 0)
+#define	SSBI_REG_ADDR_IRQ_M_STATUS1	(SSBI_REG_ADDR_IRQ_BASE + 1)
+#define	SSBI_REG_ADDR_IRQ_M_STATUS2	(SSBI_REG_ADDR_IRQ_BASE + 2)
+#define	SSBI_REG_ADDR_IRQ_M_STATUS3	(SSBI_REG_ADDR_IRQ_BASE + 3)
+#define	SSBI_REG_ADDR_IRQ_M_STATUS4	(SSBI_REG_ADDR_IRQ_BASE + 4)
+#define	SSBI_REG_ADDR_IRQ_BLK_SEL	(SSBI_REG_ADDR_IRQ_BASE + 5)
+#define	SSBI_REG_ADDR_IRQ_IT_STATUS	(SSBI_REG_ADDR_IRQ_BASE + 6)
+#define	SSBI_REG_ADDR_IRQ_CONFIG	(SSBI_REG_ADDR_IRQ_BASE + 7)
+#define	SSBI_REG_ADDR_IRQ_RT_STATUS	(SSBI_REG_ADDR_IRQ_BASE + 8)
+
+#define	PM_IRQF_LVL_SEL			0x01	/* level select */
+#define	PM_IRQF_MASK_FE			0x02	/* mask falling edge */
+#define	PM_IRQF_MASK_RE			0x04	/* mask rising edge */
+#define	PM_IRQF_CLR			0x08	/* clear interrupt */
+#define	PM_IRQF_BITS_MASK		0x70
+#define	PM_IRQF_BITS_SHIFT		4
+#define	PM_IRQF_WRITE			0x80
+
+#define	PM_IRQF_MASK_ALL		(PM_IRQF_MASK_FE | \
+					PM_IRQF_MASK_RE)
+
+struct pm_irq_chip {
+	struct device		*dev;
+	spinlock_t		pm_irq_lock;
+	unsigned int		devirq;
+	unsigned int		irq_base;
+	unsigned int		num_irqs;
+	unsigned int		num_blocks;
+	unsigned int		num_masters;
+	u8			config[0];
+};
+
+static int pm8xxx_read_root_irq(const struct pm_irq_chip *chip, u8 *rp)
+{
+	return pm8xxx_readb(chip->dev, SSBI_REG_ADDR_IRQ_ROOT, rp);
+}
+
+static int pm8xxx_read_master_irq(const struct pm_irq_chip *chip, u8 m, u8 *bp)
+{
+	return pm8xxx_readb(chip->dev,
+			SSBI_REG_ADDR_IRQ_M_STATUS1 + m, bp);
+}
+
+static int pm8xxx_read_block_irq(struct pm_irq_chip *chip, u8 bp, u8 *ip)
+{
+	int	rc;
+
+	spin_lock(&chip->pm_irq_lock);
+	rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, bp);
+	if (rc) {
+		pr_err("Failed Selecting Block %d rc=%d\n", bp, rc);
+		goto bail;
+	}
+
+	rc = pm8xxx_readb(chip->dev, SSBI_REG_ADDR_IRQ_IT_STATUS, ip);
+	if (rc)
+		pr_err("Failed Reading Status rc=%d\n", rc);
+bail:
+	spin_unlock(&chip->pm_irq_lock);
+	return rc;
+}
+
+static int pm8xxx_config_irq(struct pm_irq_chip *chip, u8 bp, u8 cp)
+{
+	int	rc;
+
+	spin_lock(&chip->pm_irq_lock);
+	rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, bp);
+	if (rc) {
+		pr_err("Failed Selecting Block %d rc=%d\n", bp, rc);
+		goto bail;
+	}
+
+	cp |= PM_IRQF_WRITE;
+	rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_CONFIG, cp);
+	if (rc)
+		pr_err("Failed Configuring IRQ rc=%d\n", rc);
+bail:
+	spin_unlock(&chip->pm_irq_lock);
+	return rc;
+}
+
+static int pm8xxx_irq_block_handler(struct pm_irq_chip *chip, int block)
+{
+	int pmirq, irq, i, ret = 0;
+	u8 bits;
+
+	ret = pm8xxx_read_block_irq(chip, block, &bits);
+	if (ret) {
+		pr_err("Failed reading %d block ret=%d", block, ret);
+		return ret;
+	}
+	if (!bits) {
+		pr_err("block bit set in master but no irqs: %d", block);
+		return 0;
+	}
+
+	/* Check IRQ bits */
+	for (i = 0; i < 8; i++) {
+		if (bits & (1 << i)) {
+			pmirq = block * 8 + i;
+			irq = pmirq + chip->irq_base;
+			generic_handle_irq(irq);
+		}
+	}
+	return 0;
+}
+
+static int pm8xxx_irq_master_handler(struct pm_irq_chip *chip, int master)
+{
+	u8 blockbits;
+	int block_number, i, ret = 0;
+
+	ret = pm8xxx_read_master_irq(chip, master, &blockbits);
+	if (ret) {
+		pr_err("Failed to read master %d ret=%d\n", master, ret);
+		return ret;
+	}
+	if (!blockbits) {
+		pr_err("master bit set in root but no blocks: %d", master);
+		return 0;
+	}
+
+	for (i = 0; i < 8; i++)
+		if (blockbits & (1 << i)) {
+			block_number = master * 8 + i;	/* block # */
+			ret |= pm8xxx_irq_block_handler(chip, block_number);
+		}
+	return ret;
+}
+
+static void pm8xxx_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+	struct pm_irq_chip *chip = irq_desc_get_handler_data(desc);
+	struct irq_chip *irq_chip = irq_desc_get_chip(desc);
+	u8	root;
+	int	i, ret, masters = 0;
+
+	ret = pm8xxx_read_root_irq(chip, &root);
+	if (ret) {
+		pr_err("Can't read root status ret=%d\n", ret);
+		return;
+	}
+
+	/* on pm8xxx series masters start from bit 1 of the root */
+	masters = root >> 1;
+
+	/* Read allowed masters for blocks. */
+	for (i = 0; i < chip->num_masters; i++)
+		if (masters & (1 << i))
+			pm8xxx_irq_master_handler(chip, i);
+
+	irq_chip->irq_ack(&desc->irq_data);
+}
+
+static void pm8xxx_irq_mask_ack(struct irq_data *d)
+{
+	struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
+	unsigned int pmirq = d->irq - chip->irq_base;
+	int	master, irq_bit;
+	u8	block, config;
+
+	block = pmirq / 8;
+	master = block / 8;
+	irq_bit = pmirq % 8;
+
+	config = chip->config[pmirq] | PM_IRQF_MASK_ALL | PM_IRQF_CLR;
+	pm8xxx_config_irq(chip, block, config);
+}
+
+static void pm8xxx_irq_unmask(struct irq_data *d)
+{
+	struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
+	unsigned int pmirq = d->irq - chip->irq_base;
+	int	master, irq_bit;
+	u8	block, config;
+
+	block = pmirq / 8;
+	master = block / 8;
+	irq_bit = pmirq % 8;
+
+	config = chip->config[pmirq];
+	pm8xxx_config_irq(chip, block, config);
+}
+
+static int pm8xxx_irq_set_type(struct irq_data *d, unsigned int flow_type)
+{
+	struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
+	unsigned int pmirq = d->irq - chip->irq_base;
+	int master, irq_bit;
+	u8 block, config;
+
+	block = pmirq / 8;
+	master = block / 8;
+	irq_bit  = pmirq % 8;
+
+	chip->config[pmirq] = (irq_bit << PM_IRQF_BITS_SHIFT)
+							| PM_IRQF_MASK_ALL;
+	if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
+		if (flow_type & IRQF_TRIGGER_RISING)
+			chip->config[pmirq] &= ~PM_IRQF_MASK_RE;
+		if (flow_type & IRQF_TRIGGER_FALLING)
+			chip->config[pmirq] &= ~PM_IRQF_MASK_FE;
+	} else {
+		chip->config[pmirq] |= PM_IRQF_LVL_SEL;
+
+		if (flow_type & IRQF_TRIGGER_HIGH)
+			chip->config[pmirq] &= ~PM_IRQF_MASK_RE;
+		else
+			chip->config[pmirq] &= ~PM_IRQF_MASK_FE;
+	}
+
+	config = chip->config[pmirq] | PM_IRQF_CLR;
+	return pm8xxx_config_irq(chip, block, config);
+}
+
+static int pm8xxx_irq_set_wake(struct irq_data *d, unsigned int on)
+{
+	return 0;
+}
+
+static struct irq_chip pm8xxx_irq_chip = {
+	.name		= "pm8xxx",
+	.irq_mask_ack	= pm8xxx_irq_mask_ack,
+	.irq_unmask	= pm8xxx_irq_unmask,
+	.irq_set_type	= pm8xxx_irq_set_type,
+	.irq_set_wake	= pm8xxx_irq_set_wake,
+	.flags		= IRQCHIP_MASK_ON_SUSPEND,
+};
+
+/**
+ * pm8xxx_get_irq_stat - get the status of the irq line
+ * @chip: pointer to identify a pmic irq controller
+ * @irq: the irq number
+ *
+ * The pm8xxx gpio and mpp rely on the interrupt block to read
+ * the values on their pins. This function is to facilitate reading
+ * the status of a gpio or an mpp line. The caller has to convert the
+ * gpio number to irq number.
+ *
+ * RETURNS:
+ * an int indicating the value read on that line
+ */
+int pm8xxx_get_irq_stat(struct pm_irq_chip *chip, int irq)
+{
+	int pmirq, rc;
+	u8  block, bits, bit;
+	unsigned long flags;
+
+	if (chip == NULL || irq < chip->irq_base ||
+			irq >= chip->irq_base + chip->num_irqs)
+		return -EINVAL;
+
+	pmirq = irq - chip->irq_base;
+
+	block = pmirq / 8;
+	bit = pmirq % 8;
+
+	spin_lock_irqsave(&chip->pm_irq_lock, flags);
+
+	rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, block);
+	if (rc) {
+		pr_err("Failed Selecting block irq=%d pmirq=%d blk=%d rc=%d\n",
+			irq, pmirq, block, rc);
+		goto bail_out;
+	}
+
+	rc = pm8xxx_readb(chip->dev, SSBI_REG_ADDR_IRQ_RT_STATUS, &bits);
+	if (rc) {
+		pr_err("Failed Configuring irq=%d pmirq=%d blk=%d rc=%d\n",
+			irq, pmirq, block, rc);
+		goto bail_out;
+	}
+
+	rc = (bits & (1 << bit)) ? 1 : 0;
+
+bail_out:
+	spin_unlock_irqrestore(&chip->pm_irq_lock, flags);
+
+	return rc;
+}
+EXPORT_SYMBOL_GPL(pm8xxx_get_irq_stat);
+
+struct pm_irq_chip *  __devinit pm8xxx_irq_init(struct device *dev,
+				const struct pm8xxx_irq_platform_data *pdata)
+{
+	struct pm_irq_chip  *chip;
+	int devirq, rc;
+	unsigned int pmirq;
+
+	if (!pdata) {
+		pr_err("No platform data\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	devirq = pdata->devirq;
+	if (devirq < 0) {
+		pr_err("missing devirq\n");
+		rc = devirq;
+		return ERR_PTR(-EINVAL);
+	}
+
+	chip = kzalloc(sizeof(struct pm_irq_chip)
+			+ sizeof(u8) * pdata->irq_cdata.nirqs, GFP_KERNEL);
+	if (!chip) {
+		pr_err("Cannot alloc pm_irq_chip struct\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	chip->dev = dev;
+	chip->devirq = devirq;
+	chip->irq_base = pdata->irq_base;
+	chip->num_irqs = pdata->irq_cdata.nirqs;
+	chip->num_blocks = DIV_ROUND_UP(chip->num_irqs, 8);
+	chip->num_masters = DIV_ROUND_UP(chip->num_blocks, 8);
+	spin_lock_init(&chip->pm_irq_lock);
+
+	for (pmirq = 0; pmirq < chip->num_irqs; pmirq++) {
+		irq_set_chip_and_handler(chip->irq_base + pmirq,
+				&pm8xxx_irq_chip,
+				handle_level_irq);
+		irq_set_chip_data(chip->irq_base + pmirq, chip);
+#ifdef CONFIG_ARM
+		set_irq_flags(chip->irq_base + pmirq, IRQF_VALID);
+#else
+		irq_set_noprobe(chip->irq_base + pmirq);
+#endif
+	}
+
+	irq_set_irq_type(devirq, pdata->irq_trigger_flag);
+	irq_set_handler_data(devirq, chip);
+	irq_set_chained_handler(devirq, pm8xxx_irq_handler);
+	set_irq_wake(devirq, 1);
+
+	return chip;
+}
+
+int __devexit pm8xxx_irq_exit(struct pm_irq_chip *chip)
+{
+	irq_set_chained_handler(chip->devirq, NULL);
+	kfree(chip);
+	return 0;
+}
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/rc5t583-irq.c b/ap/os/linux/linux-3.4.x/drivers/mfd/rc5t583-irq.c
new file mode 100644
index 0000000..fa6f80f
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/rc5t583-irq.c
@@ -0,0 +1,408 @@
+/*
+ * Interrupt driver for RICOH583 power management chip.
+ *
+ * Copyright (c) 2011-2012, NVIDIA CORPORATION.  All rights reserved.
+ * Author: Laxman dewangan <ldewangan@nvidia.com>
+ *
+ * based on code
+ *      Copyright (C) 2011 RICOH COMPANY,LTD
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/mfd/rc5t583.h>
+
+enum int_type {
+	SYS_INT  = 0x1,
+	DCDC_INT = 0x2,
+	RTC_INT  = 0x4,
+	ADC_INT  = 0x8,
+	GPIO_INT = 0x10,
+};
+
+static int gpedge_add[] = {
+	RC5T583_GPIO_GPEDGE2,
+	RC5T583_GPIO_GPEDGE2
+};
+
+static int irq_en_add[] = {
+	RC5T583_INT_EN_SYS1,
+	RC5T583_INT_EN_SYS2,
+	RC5T583_INT_EN_DCDC,
+	RC5T583_INT_EN_RTC,
+	RC5T583_INT_EN_ADC1,
+	RC5T583_INT_EN_ADC2,
+	RC5T583_INT_EN_ADC3,
+	RC5T583_GPIO_EN_INT
+};
+
+static int irq_mon_add[] = {
+	RC5T583_INT_MON_SYS1,
+	RC5T583_INT_MON_SYS2,
+	RC5T583_INT_MON_DCDC,
+	RC5T583_INT_MON_RTC,
+	RC5T583_INT_IR_ADCL,
+	RC5T583_INT_IR_ADCH,
+	RC5T583_INT_IR_ADCEND,
+	RC5T583_INT_IR_GPIOF,
+	RC5T583_INT_IR_GPIOR
+};
+
+static int irq_clr_add[] = {
+	RC5T583_INT_IR_SYS1,
+	RC5T583_INT_IR_SYS2,
+	RC5T583_INT_IR_DCDC,
+	RC5T583_INT_IR_RTC,
+	RC5T583_INT_IR_ADCL,
+	RC5T583_INT_IR_ADCH,
+	RC5T583_INT_IR_ADCEND,
+	RC5T583_INT_IR_GPIOF,
+	RC5T583_INT_IR_GPIOR
+};
+
+static int main_int_type[] = {
+	SYS_INT,
+	SYS_INT,
+	DCDC_INT,
+	RTC_INT,
+	ADC_INT,
+	ADC_INT,
+	ADC_INT,
+	GPIO_INT,
+	GPIO_INT,
+};
+
+struct rc5t583_irq_data {
+	u8	int_type;
+	u8	master_bit;
+	u8	int_en_bit;
+	u8	mask_reg_index;
+	int	grp_index;
+};
+
+#define RC5T583_IRQ(_int_type, _master_bit, _grp_index, \
+			_int_bit, _mask_ind)		\
+	{						\
+		.int_type	= _int_type,		\
+		.master_bit	= _master_bit,		\
+		.grp_index	= _grp_index,		\
+		.int_en_bit	= _int_bit,		\
+		.mask_reg_index	= _mask_ind,		\
+	}
+
+static const struct rc5t583_irq_data rc5t583_irqs[RC5T583_MAX_IRQS] = {
+	[RC5T583_IRQ_ONKEY]		= RC5T583_IRQ(SYS_INT,  0, 0, 0, 0),
+	[RC5T583_IRQ_ACOK]		= RC5T583_IRQ(SYS_INT,  0, 1, 1, 0),
+	[RC5T583_IRQ_LIDOPEN]		= RC5T583_IRQ(SYS_INT,  0, 2, 2, 0),
+	[RC5T583_IRQ_PREOT]		= RC5T583_IRQ(SYS_INT,  0, 3, 3, 0),
+	[RC5T583_IRQ_CLKSTP]		= RC5T583_IRQ(SYS_INT,  0, 4, 4, 0),
+	[RC5T583_IRQ_ONKEY_OFF]		= RC5T583_IRQ(SYS_INT,  0, 5, 5, 0),
+	[RC5T583_IRQ_WD]		= RC5T583_IRQ(SYS_INT,  0, 7, 7, 0),
+	[RC5T583_IRQ_EN_PWRREQ1]	= RC5T583_IRQ(SYS_INT,  0, 8, 0, 1),
+	[RC5T583_IRQ_EN_PWRREQ2]	= RC5T583_IRQ(SYS_INT,  0, 9, 1, 1),
+	[RC5T583_IRQ_PRE_VINDET]	= RC5T583_IRQ(SYS_INT,  0, 10, 2, 1),
+
+	[RC5T583_IRQ_DC0LIM]		= RC5T583_IRQ(DCDC_INT, 1, 0, 0, 2),
+	[RC5T583_IRQ_DC1LIM]		= RC5T583_IRQ(DCDC_INT, 1, 1, 1, 2),
+	[RC5T583_IRQ_DC2LIM]		= RC5T583_IRQ(DCDC_INT, 1, 2, 2, 2),
+	[RC5T583_IRQ_DC3LIM]		= RC5T583_IRQ(DCDC_INT, 1, 3, 3, 2),
+
+	[RC5T583_IRQ_CTC]		= RC5T583_IRQ(RTC_INT,  2, 0, 0, 3),
+	[RC5T583_IRQ_YALE]		= RC5T583_IRQ(RTC_INT,  2, 5, 5, 3),
+	[RC5T583_IRQ_DALE]		= RC5T583_IRQ(RTC_INT,  2, 6, 6, 3),
+	[RC5T583_IRQ_WALE]		= RC5T583_IRQ(RTC_INT,  2, 7, 7, 3),
+
+	[RC5T583_IRQ_AIN1L]		= RC5T583_IRQ(ADC_INT,  3, 0, 0, 4),
+	[RC5T583_IRQ_AIN2L]		= RC5T583_IRQ(ADC_INT,  3, 1, 1, 4),
+	[RC5T583_IRQ_AIN3L]		= RC5T583_IRQ(ADC_INT,  3, 2, 2, 4),
+	[RC5T583_IRQ_VBATL]		= RC5T583_IRQ(ADC_INT,  3, 3, 3, 4),
+	[RC5T583_IRQ_VIN3L]		= RC5T583_IRQ(ADC_INT,  3, 4, 4, 4),
+	[RC5T583_IRQ_VIN8L]		= RC5T583_IRQ(ADC_INT,  3, 5, 5, 4),
+	[RC5T583_IRQ_AIN1H]		= RC5T583_IRQ(ADC_INT,  3, 6, 0, 5),
+	[RC5T583_IRQ_AIN2H]		= RC5T583_IRQ(ADC_INT,  3, 7, 1, 5),
+	[RC5T583_IRQ_AIN3H]		= RC5T583_IRQ(ADC_INT,  3, 8, 2, 5),
+	[RC5T583_IRQ_VBATH]		= RC5T583_IRQ(ADC_INT,  3, 9, 3, 5),
+	[RC5T583_IRQ_VIN3H]		= RC5T583_IRQ(ADC_INT,  3, 10, 4, 5),
+	[RC5T583_IRQ_VIN8H]		= RC5T583_IRQ(ADC_INT,  3, 11, 5, 5),
+	[RC5T583_IRQ_ADCEND]		= RC5T583_IRQ(ADC_INT,  3, 12, 0, 6),
+
+	[RC5T583_IRQ_GPIO0]		= RC5T583_IRQ(GPIO_INT, 4, 0, 0, 7),
+	[RC5T583_IRQ_GPIO1]		= RC5T583_IRQ(GPIO_INT, 4, 1, 1, 7),
+	[RC5T583_IRQ_GPIO2]		= RC5T583_IRQ(GPIO_INT, 4, 2, 2, 7),
+	[RC5T583_IRQ_GPIO3]		= RC5T583_IRQ(GPIO_INT, 4, 3, 3, 7),
+	[RC5T583_IRQ_GPIO4]		= RC5T583_IRQ(GPIO_INT, 4, 4, 4, 7),
+	[RC5T583_IRQ_GPIO5]		= RC5T583_IRQ(GPIO_INT, 4, 5, 5, 7),
+	[RC5T583_IRQ_GPIO6]		= RC5T583_IRQ(GPIO_INT, 4, 6, 6, 7),
+	[RC5T583_IRQ_GPIO7]		= RC5T583_IRQ(GPIO_INT, 4, 7, 7, 7),
+};
+
+static void rc5t583_irq_lock(struct irq_data *irq_data)
+{
+	struct rc5t583 *rc5t583 = irq_data_get_irq_chip_data(irq_data);
+	mutex_lock(&rc5t583->irq_lock);
+}
+
+static void rc5t583_irq_unmask(struct irq_data *irq_data)
+{
+	struct rc5t583 *rc5t583 = irq_data_get_irq_chip_data(irq_data);
+	unsigned int __irq = irq_data->irq - rc5t583->irq_base;
+	const struct rc5t583_irq_data *data = &rc5t583_irqs[__irq];
+
+	rc5t583->group_irq_en[data->grp_index] |= 1 << data->grp_index;
+	rc5t583->intc_inten_reg |= 1 << data->master_bit;
+	rc5t583->irq_en_reg[data->mask_reg_index] |= 1 << data->int_en_bit;
+}
+
+static void rc5t583_irq_mask(struct irq_data *irq_data)
+{
+	struct rc5t583 *rc5t583 = irq_data_get_irq_chip_data(irq_data);
+	unsigned int __irq = irq_data->irq - rc5t583->irq_base;
+	const struct rc5t583_irq_data *data = &rc5t583_irqs[__irq];
+
+	rc5t583->group_irq_en[data->grp_index] &= ~(1 << data->grp_index);
+	if (!rc5t583->group_irq_en[data->grp_index])
+		rc5t583->intc_inten_reg &= ~(1 << data->master_bit);
+
+	rc5t583->irq_en_reg[data->mask_reg_index] &= ~(1 << data->int_en_bit);
+}
+
+static int rc5t583_irq_set_type(struct irq_data *irq_data, unsigned int type)
+{
+	struct rc5t583 *rc5t583 = irq_data_get_irq_chip_data(irq_data);
+	unsigned int __irq = irq_data->irq - rc5t583->irq_base;
+	const struct rc5t583_irq_data *data = &rc5t583_irqs[__irq];
+	int val = 0;
+	int gpedge_index;
+	int gpedge_bit_pos;
+
+	/* Supporting only trigger level inetrrupt */
+	if ((data->int_type & GPIO_INT) && (type & IRQ_TYPE_EDGE_BOTH)) {
+		gpedge_index = data->int_en_bit / 4;
+		gpedge_bit_pos = data->int_en_bit % 4;
+
+		if (type & IRQ_TYPE_EDGE_FALLING)
+			val |= 0x2;
+
+		if (type & IRQ_TYPE_EDGE_RISING)
+			val |= 0x1;
+
+		rc5t583->gpedge_reg[gpedge_index] &= ~(3 << gpedge_bit_pos);
+		rc5t583->gpedge_reg[gpedge_index] |= (val << gpedge_bit_pos);
+		rc5t583_irq_unmask(irq_data);
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static void rc5t583_irq_sync_unlock(struct irq_data *irq_data)
+{
+	struct rc5t583 *rc5t583 = irq_data_get_irq_chip_data(irq_data);
+	int i;
+	int ret;
+
+	for (i = 0; i < ARRAY_SIZE(rc5t583->gpedge_reg); i++) {
+		ret = rc5t583_write(rc5t583->dev, gpedge_add[i],
+				rc5t583->gpedge_reg[i]);
+		if (ret < 0)
+			dev_warn(rc5t583->dev,
+				"Error in writing reg 0x%02x error: %d\n",
+				gpedge_add[i], ret);
+	}
+
+	for (i = 0; i < ARRAY_SIZE(rc5t583->irq_en_reg); i++) {
+		ret = rc5t583_write(rc5t583->dev, irq_en_add[i],
+					rc5t583->irq_en_reg[i]);
+		if (ret < 0)
+			dev_warn(rc5t583->dev,
+				"Error in writing reg 0x%02x error: %d\n",
+				irq_en_add[i], ret);
+	}
+
+	ret = rc5t583_write(rc5t583->dev, RC5T583_INTC_INTEN,
+				rc5t583->intc_inten_reg);
+	if (ret < 0)
+		dev_warn(rc5t583->dev,
+			"Error in writing reg 0x%02x error: %d\n",
+			RC5T583_INTC_INTEN, ret);
+
+	mutex_unlock(&rc5t583->irq_lock);
+}
+#ifdef CONFIG_PM_SLEEP
+static int rc5t583_irq_set_wake(struct irq_data *irq_data, unsigned int on)
+{
+	struct rc5t583 *rc5t583 = irq_data_get_irq_chip_data(irq_data);
+	return irq_set_irq_wake(rc5t583->chip_irq, on);
+}
+#else
+#define rc5t583_irq_set_wake NULL
+#endif
+
+static irqreturn_t rc5t583_irq(int irq, void *data)
+{
+	struct rc5t583 *rc5t583 = data;
+	uint8_t int_sts[RC5T583_MAX_INTERRUPT_MASK_REGS];
+	uint8_t master_int;
+	int i;
+	int ret;
+	unsigned int rtc_int_sts = 0;
+
+	/* Clear the status */
+	for (i = 0; i < RC5T583_MAX_INTERRUPT_MASK_REGS; i++)
+		int_sts[i] = 0;
+
+	ret  = rc5t583_read(rc5t583->dev, RC5T583_INTC_INTMON, &master_int);
+	if (ret < 0) {
+		dev_err(rc5t583->dev,
+			"Error in reading reg 0x%02x error: %d\n",
+			RC5T583_INTC_INTMON, ret);
+		return IRQ_HANDLED;
+	}
+
+	for (i = 0; i < RC5T583_MAX_INTERRUPT_MASK_REGS; ++i) {
+		if (!(master_int & main_int_type[i]))
+			continue;
+
+		ret = rc5t583_read(rc5t583->dev, irq_mon_add[i], &int_sts[i]);
+		if (ret < 0) {
+			dev_warn(rc5t583->dev,
+				"Error in reading reg 0x%02x error: %d\n",
+				irq_mon_add[i], ret);
+			int_sts[i] = 0;
+			continue;
+		}
+
+		if (main_int_type[i] & RTC_INT) {
+			rtc_int_sts = 0;
+			if (int_sts[i] & 0x1)
+				rtc_int_sts |= BIT(6);
+			if (int_sts[i] & 0x2)
+				rtc_int_sts |= BIT(7);
+			if (int_sts[i] & 0x4)
+				rtc_int_sts |= BIT(0);
+			if (int_sts[i] & 0x8)
+				rtc_int_sts |= BIT(5);
+		}
+
+		ret = rc5t583_write(rc5t583->dev, irq_clr_add[i],
+				~int_sts[i]);
+		if (ret < 0)
+			dev_warn(rc5t583->dev,
+				"Error in reading reg 0x%02x error: %d\n",
+				irq_clr_add[i], ret);
+
+		if (main_int_type[i] & RTC_INT)
+			int_sts[i] = rtc_int_sts;
+	}
+
+	/* Merge gpio interrupts for rising and falling case*/
+	int_sts[7] |= int_sts[8];
+
+	/* Call interrupt handler if enabled */
+	for (i = 0; i < RC5T583_MAX_IRQS; ++i) {
+		const struct rc5t583_irq_data *data = &rc5t583_irqs[i];
+		if ((int_sts[data->mask_reg_index] & (1 << data->int_en_bit)) &&
+			(rc5t583->group_irq_en[data->master_bit] &
+					(1 << data->grp_index)))
+			handle_nested_irq(rc5t583->irq_base + i);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static struct irq_chip rc5t583_irq_chip = {
+	.name = "rc5t583-irq",
+	.irq_mask = rc5t583_irq_mask,
+	.irq_unmask = rc5t583_irq_unmask,
+	.irq_bus_lock = rc5t583_irq_lock,
+	.irq_bus_sync_unlock = rc5t583_irq_sync_unlock,
+	.irq_set_type = rc5t583_irq_set_type,
+	.irq_set_wake = rc5t583_irq_set_wake,
+};
+
+int rc5t583_irq_init(struct rc5t583 *rc5t583, int irq, int irq_base)
+{
+	int i, ret;
+
+	if (!irq_base) {
+		dev_warn(rc5t583->dev, "No interrupt support on IRQ base\n");
+		return -EINVAL;
+	}
+
+	mutex_init(&rc5t583->irq_lock);
+
+	/* Initailize all int register to 0 */
+	for (i = 0; i < RC5T583_MAX_INTERRUPT_MASK_REGS; i++)  {
+		ret = rc5t583_write(rc5t583->dev, irq_en_add[i],
+				rc5t583->irq_en_reg[i]);
+		if (ret < 0)
+			dev_warn(rc5t583->dev,
+				"Error in writing reg 0x%02x error: %d\n",
+				irq_en_add[i], ret);
+	}
+
+	for (i = 0; i < RC5T583_MAX_GPEDGE_REG; i++)  {
+		ret = rc5t583_write(rc5t583->dev, gpedge_add[i],
+				rc5t583->gpedge_reg[i]);
+		if (ret < 0)
+			dev_warn(rc5t583->dev,
+				"Error in writing reg 0x%02x error: %d\n",
+				gpedge_add[i], ret);
+	}
+
+	ret = rc5t583_write(rc5t583->dev, RC5T583_INTC_INTEN, 0x0);
+	if (ret < 0)
+		dev_warn(rc5t583->dev,
+			"Error in writing reg 0x%02x error: %d\n",
+			RC5T583_INTC_INTEN, ret);
+
+	/* Clear all interrupts in case they woke up active. */
+	for (i = 0; i < RC5T583_MAX_INTERRUPT_MASK_REGS; i++)  {
+		ret = rc5t583_write(rc5t583->dev, irq_clr_add[i], 0);
+		if (ret < 0)
+			dev_warn(rc5t583->dev,
+				"Error in writing reg 0x%02x error: %d\n",
+				irq_clr_add[i], ret);
+	}
+
+	rc5t583->irq_base = irq_base;
+	rc5t583->chip_irq = irq;
+
+	for (i = 0; i < RC5T583_MAX_IRQS; i++) {
+		int __irq = i + rc5t583->irq_base;
+		irq_set_chip_data(__irq, rc5t583);
+		irq_set_chip_and_handler(__irq, &rc5t583_irq_chip,
+					 handle_simple_irq);
+		irq_set_nested_thread(__irq, 1);
+#ifdef CONFIG_ARM
+		set_irq_flags(__irq, IRQF_VALID);
+#endif
+	}
+
+	ret = request_threaded_irq(irq, NULL, rc5t583_irq, IRQF_ONESHOT,
+				"rc5t583", rc5t583);
+	if (ret < 0)
+		dev_err(rc5t583->dev,
+			"Error in registering interrupt error: %d\n", ret);
+	return ret;
+}
+
+int rc5t583_irq_exit(struct rc5t583 *rc5t583)
+{
+	if (rc5t583->chip_irq)
+		free_irq(rc5t583->chip_irq, rc5t583);
+	return 0;
+}
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/rc5t583.c b/ap/os/linux/linux-3.4.x/drivers/mfd/rc5t583.c
new file mode 100644
index 0000000..44afae0
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/rc5t583.c
@@ -0,0 +1,349 @@
+/*
+ * Core driver access RC5T583 power management chip.
+ *
+ * Copyright (c) 2011-2012, NVIDIA CORPORATION.  All rights reserved.
+ * Author: Laxman dewangan <ldewangan@nvidia.com>
+ *
+ * Based on code
+ *	Copyright (C) 2011 RICOH COMPANY,LTD
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/rc5t583.h>
+#include <linux/regmap.h>
+
+#define RICOH_ONOFFSEL_REG	0x10
+#define RICOH_SWCTL_REG		0x5E
+
+struct deepsleep_control_data {
+	u8 reg_add;
+	u8 ds_pos_bit;
+};
+
+#define DEEPSLEEP_INIT(_id, _reg, _pos)		\
+	{					\
+		.reg_add = RC5T583_##_reg,	\
+		.ds_pos_bit = _pos,		\
+	}
+
+static struct deepsleep_control_data deepsleep_data[] = {
+	DEEPSLEEP_INIT(DC0, SLPSEQ1, 0),
+	DEEPSLEEP_INIT(DC1, SLPSEQ1, 4),
+	DEEPSLEEP_INIT(DC2, SLPSEQ2, 0),
+	DEEPSLEEP_INIT(DC3, SLPSEQ2, 4),
+	DEEPSLEEP_INIT(LDO0, SLPSEQ3, 0),
+	DEEPSLEEP_INIT(LDO1, SLPSEQ3, 4),
+	DEEPSLEEP_INIT(LDO2, SLPSEQ4, 0),
+	DEEPSLEEP_INIT(LDO3, SLPSEQ4, 4),
+	DEEPSLEEP_INIT(LDO4, SLPSEQ5, 0),
+	DEEPSLEEP_INIT(LDO5, SLPSEQ5, 4),
+	DEEPSLEEP_INIT(LDO6, SLPSEQ6, 0),
+	DEEPSLEEP_INIT(LDO7, SLPSEQ6, 4),
+	DEEPSLEEP_INIT(LDO8, SLPSEQ7, 0),
+	DEEPSLEEP_INIT(LDO9, SLPSEQ7, 4),
+	DEEPSLEEP_INIT(PSO0, SLPSEQ8, 0),
+	DEEPSLEEP_INIT(PSO1, SLPSEQ8, 4),
+	DEEPSLEEP_INIT(PSO2, SLPSEQ9, 0),
+	DEEPSLEEP_INIT(PSO3, SLPSEQ9, 4),
+	DEEPSLEEP_INIT(PSO4, SLPSEQ10, 0),
+	DEEPSLEEP_INIT(PSO5, SLPSEQ10, 4),
+	DEEPSLEEP_INIT(PSO6, SLPSEQ11, 0),
+	DEEPSLEEP_INIT(PSO7, SLPSEQ11, 4),
+};
+
+#define EXT_PWR_REQ		\
+	(RC5T583_EXT_PWRREQ1_CONTROL | RC5T583_EXT_PWRREQ2_CONTROL)
+
+static struct mfd_cell rc5t583_subdevs[] = {
+	{.name = "rc5t583-regulator",},
+	{.name = "rc5t583-rtc",      },
+	{.name = "rc5t583-key",      }
+};
+
+static int __rc5t583_set_ext_pwrreq1_control(struct device *dev,
+	int id, int ext_pwr, int slots)
+{
+	int ret;
+	uint8_t sleepseq_val;
+	unsigned int en_bit;
+	unsigned int slot_bit;
+
+	if (id == RC5T583_DS_DC0) {
+		dev_err(dev, "PWRREQ1 is invalid control for rail %d\n", id);
+		return -EINVAL;
+	}
+
+	en_bit = deepsleep_data[id].ds_pos_bit;
+	slot_bit = en_bit + 1;
+	ret = rc5t583_read(dev, deepsleep_data[id].reg_add, &sleepseq_val);
+	if (ret < 0) {
+		dev_err(dev, "Error in reading reg 0x%x\n",
+				deepsleep_data[id].reg_add);
+		return ret;
+	}
+
+	sleepseq_val &= ~(0xF << en_bit);
+	sleepseq_val |= BIT(en_bit);
+	sleepseq_val |= ((slots & 0x7) << slot_bit);
+	ret = rc5t583_set_bits(dev, RICOH_ONOFFSEL_REG, BIT(1));
+	if (ret < 0) {
+		dev_err(dev, "Error in updating the 0x%02x register\n",
+				RICOH_ONOFFSEL_REG);
+		return ret;
+	}
+
+	ret = rc5t583_write(dev, deepsleep_data[id].reg_add, sleepseq_val);
+	if (ret < 0) {
+		dev_err(dev, "Error in writing reg 0x%x\n",
+				deepsleep_data[id].reg_add);
+		return ret;
+	}
+
+	if (id == RC5T583_DS_LDO4) {
+		ret = rc5t583_write(dev, RICOH_SWCTL_REG, 0x1);
+		if (ret < 0)
+			dev_err(dev, "Error in writing reg 0x%x\n",
+				RICOH_SWCTL_REG);
+	}
+	return ret;
+}
+
+static int __rc5t583_set_ext_pwrreq2_control(struct device *dev,
+	int id, int ext_pwr)
+{
+	int ret;
+
+	if (id != RC5T583_DS_DC0) {
+		dev_err(dev, "PWRREQ2 is invalid control for rail %d\n", id);
+		return -EINVAL;
+	}
+
+	ret = rc5t583_set_bits(dev, RICOH_ONOFFSEL_REG, BIT(2));
+	if (ret < 0)
+		dev_err(dev, "Error in updating the ONOFFSEL 0x10 register\n");
+	return ret;
+}
+
+int rc5t583_ext_power_req_config(struct device *dev, int ds_id,
+	int ext_pwr_req, int deepsleep_slot_nr)
+{
+	if ((ext_pwr_req & EXT_PWR_REQ) == EXT_PWR_REQ)
+		return -EINVAL;
+
+	if (ext_pwr_req & RC5T583_EXT_PWRREQ1_CONTROL)
+		return __rc5t583_set_ext_pwrreq1_control(dev, ds_id,
+				ext_pwr_req, deepsleep_slot_nr);
+
+	if (ext_pwr_req & RC5T583_EXT_PWRREQ2_CONTROL)
+		return __rc5t583_set_ext_pwrreq2_control(dev,
+			ds_id, ext_pwr_req);
+	return 0;
+}
+EXPORT_SYMBOL(rc5t583_ext_power_req_config);
+
+static int rc5t583_clear_ext_power_req(struct rc5t583 *rc5t583,
+	struct rc5t583_platform_data *pdata)
+{
+	int ret;
+	int i;
+	uint8_t on_off_val = 0;
+
+	/*  Clear ONOFFSEL register */
+	if (pdata->enable_shutdown)
+		on_off_val = 0x1;
+
+	ret = rc5t583_write(rc5t583->dev, RICOH_ONOFFSEL_REG, on_off_val);
+	if (ret < 0)
+		dev_warn(rc5t583->dev, "Error in writing reg %d error: %d\n",
+					RICOH_ONOFFSEL_REG, ret);
+
+	ret = rc5t583_write(rc5t583->dev, RICOH_SWCTL_REG, 0x0);
+	if (ret < 0)
+		dev_warn(rc5t583->dev, "Error in writing reg %d error: %d\n",
+					RICOH_SWCTL_REG, ret);
+
+	/* Clear sleep sequence register */
+	for (i = RC5T583_SLPSEQ1; i <= RC5T583_SLPSEQ11; ++i) {
+		ret = rc5t583_write(rc5t583->dev, i, 0x0);
+		if (ret < 0)
+			dev_warn(rc5t583->dev,
+				"Error in writing reg 0x%02x error: %d\n",
+				i, ret);
+	}
+	return 0;
+}
+
+static bool volatile_reg(struct device *dev, unsigned int reg)
+{
+	/* Enable caching in interrupt registers */
+	switch (reg) {
+	case RC5T583_INT_EN_SYS1:
+	case RC5T583_INT_EN_SYS2:
+	case RC5T583_INT_EN_DCDC:
+	case RC5T583_INT_EN_RTC:
+	case RC5T583_INT_EN_ADC1:
+	case RC5T583_INT_EN_ADC2:
+	case RC5T583_INT_EN_ADC3:
+	case RC5T583_GPIO_GPEDGE1:
+	case RC5T583_GPIO_GPEDGE2:
+	case RC5T583_GPIO_EN_INT:
+		return false;
+
+	case RC5T583_GPIO_MON_IOIN:
+		/* This is gpio input register */
+		return true;
+
+	default:
+		/* Enable caching in gpio registers */
+		if ((reg >= RC5T583_GPIO_IOSEL) &&
+				(reg <= RC5T583_GPIO_GPOFUNC))
+			return false;
+
+		/* Enable caching in sleep seq registers */
+		if ((reg >= RC5T583_SLPSEQ1) && (reg <= RC5T583_SLPSEQ11))
+			return false;
+
+		/* Enable caching of regulator registers */
+		if ((reg >= RC5T583_REG_DC0CTL) && (reg <= RC5T583_REG_SR3CTL))
+			return false;
+		if ((reg >= RC5T583_REG_LDOEN1) &&
+					(reg <= RC5T583_REG_LDO9DAC_DS))
+			return false;
+
+		break;
+	}
+
+	return true;
+}
+
+static const struct regmap_config rc5t583_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.volatile_reg = volatile_reg,
+	.max_register = RC5T583_MAX_REGS,
+	.num_reg_defaults_raw = RC5T583_MAX_REGS,
+	.cache_type = REGCACHE_RBTREE,
+};
+
+static int __devinit rc5t583_i2c_probe(struct i2c_client *i2c,
+			      const struct i2c_device_id *id)
+{
+	struct rc5t583 *rc5t583;
+	struct rc5t583_platform_data *pdata = i2c->dev.platform_data;
+	int ret;
+	bool irq_init_success = false;
+
+	if (!pdata) {
+		dev_err(&i2c->dev, "Err: Platform data not found\n");
+		return -EINVAL;
+	}
+
+	rc5t583 = devm_kzalloc(&i2c->dev, sizeof(struct rc5t583), GFP_KERNEL);
+	if (!rc5t583) {
+		dev_err(&i2c->dev, "Memory allocation failed\n");
+		return -ENOMEM;
+	}
+
+	rc5t583->dev = &i2c->dev;
+	i2c_set_clientdata(i2c, rc5t583);
+
+	rc5t583->regmap = regmap_init_i2c(i2c, &rc5t583_regmap_config);
+	if (IS_ERR(rc5t583->regmap)) {
+		ret = PTR_ERR(rc5t583->regmap);
+		dev_err(&i2c->dev, "regmap initialization failed: %d\n", ret);
+		return ret;
+	}
+
+	ret = rc5t583_clear_ext_power_req(rc5t583, pdata);
+	if (ret < 0)
+		goto err_irq_init;
+
+	if (i2c->irq) {
+		ret = rc5t583_irq_init(rc5t583, i2c->irq, pdata->irq_base);
+		/* Still continue with waring if irq init fails */
+		if (ret)
+			dev_warn(&i2c->dev, "IRQ init failed: %d\n", ret);
+		else
+			irq_init_success = true;
+	}
+
+	ret = mfd_add_devices(rc5t583->dev, -1, rc5t583_subdevs,
+			ARRAY_SIZE(rc5t583_subdevs), NULL, 0);
+	if (ret) {
+		dev_err(&i2c->dev, "add mfd devices failed: %d\n", ret);
+		goto err_add_devs;
+	}
+
+	return 0;
+
+err_add_devs:
+	if (irq_init_success)
+		rc5t583_irq_exit(rc5t583);
+err_irq_init:
+	regmap_exit(rc5t583->regmap);
+	return ret;
+}
+
+static int  __devexit rc5t583_i2c_remove(struct i2c_client *i2c)
+{
+	struct rc5t583 *rc5t583 = i2c_get_clientdata(i2c);
+
+	mfd_remove_devices(rc5t583->dev);
+	rc5t583_irq_exit(rc5t583);
+	regmap_exit(rc5t583->regmap);
+	return 0;
+}
+
+static const struct i2c_device_id rc5t583_i2c_id[] = {
+	{.name = "rc5t583", .driver_data = 0},
+	{}
+};
+
+MODULE_DEVICE_TABLE(i2c, rc5t583_i2c_id);
+
+static struct i2c_driver rc5t583_i2c_driver = {
+	.driver = {
+		   .name = "rc5t583",
+		   .owner = THIS_MODULE,
+		   },
+	.probe = rc5t583_i2c_probe,
+	.remove = __devexit_p(rc5t583_i2c_remove),
+	.id_table = rc5t583_i2c_id,
+};
+
+static int __init rc5t583_i2c_init(void)
+{
+	return i2c_add_driver(&rc5t583_i2c_driver);
+}
+subsys_initcall(rc5t583_i2c_init);
+
+static void __exit rc5t583_i2c_exit(void)
+{
+	i2c_del_driver(&rc5t583_i2c_driver);
+}
+
+module_exit(rc5t583_i2c_exit);
+
+MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
+MODULE_DESCRIPTION("RICOH RC5T583 power management system device driver");
+MODULE_LICENSE("GPL v2");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/rdc321x-southbridge.c b/ap/os/linux/linux-3.4.x/drivers/mfd/rdc321x-southbridge.c
new file mode 100644
index 0000000..809bd4a
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/rdc321x-southbridge.c
@@ -0,0 +1,126 @@
+/*
+ * RDC321x MFD southbrige driver
+ *
+ * Copyright (C) 2007-2010 Florian Fainelli <florian@openwrt.org>
+ * Copyright (C) 2010 Bernhard Loos <bernhardloos@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/pci.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/rdc321x.h>
+
+static struct rdc321x_wdt_pdata rdc321x_wdt_pdata;
+
+static struct resource rdc321x_wdt_resource[] = {
+	{
+		.name	= "wdt-reg",
+		.start	= RDC321X_WDT_CTRL,
+		.end	= RDC321X_WDT_CTRL + 0x3,
+		.flags	= IORESOURCE_IO,
+	}
+};
+
+static struct rdc321x_gpio_pdata rdc321x_gpio_pdata = {
+	.max_gpios	= RDC321X_MAX_GPIO,
+};
+
+static struct resource rdc321x_gpio_resources[] = {
+	{
+		.name	= "gpio-reg1",
+		.start	= RDC321X_GPIO_CTRL_REG1,
+		.end	= RDC321X_GPIO_CTRL_REG1 + 0x7,
+		.flags	= IORESOURCE_IO,
+	}, {
+		.name	= "gpio-reg2",
+		.start	= RDC321X_GPIO_CTRL_REG2,
+		.end	= RDC321X_GPIO_CTRL_REG2 + 0x7,
+		.flags	= IORESOURCE_IO,
+	}
+};
+
+static struct mfd_cell rdc321x_sb_cells[] = {
+	{
+		.name		= "rdc321x-wdt",
+		.resources	= rdc321x_wdt_resource,
+		.num_resources	= ARRAY_SIZE(rdc321x_wdt_resource),
+		.platform_data	= &rdc321x_wdt_pdata,
+		.pdata_size	= sizeof(rdc321x_wdt_pdata),
+	}, {
+		.name		= "rdc321x-gpio",
+		.resources	= rdc321x_gpio_resources,
+		.num_resources	= ARRAY_SIZE(rdc321x_gpio_resources),
+		.platform_data	= &rdc321x_gpio_pdata,
+		.pdata_size	= sizeof(rdc321x_gpio_pdata),
+	},
+};
+
+static int __devinit rdc321x_sb_probe(struct pci_dev *pdev,
+					const struct pci_device_id *ent)
+{
+	int err;
+
+	err = pci_enable_device(pdev);
+	if (err) {
+		dev_err(&pdev->dev, "failed to enable device\n");
+		return err;
+	}
+
+	rdc321x_gpio_pdata.sb_pdev = pdev;
+	rdc321x_wdt_pdata.sb_pdev = pdev;
+
+	return mfd_add_devices(&pdev->dev, -1,
+		rdc321x_sb_cells, ARRAY_SIZE(rdc321x_sb_cells), NULL, 0);
+}
+
+static void __devexit rdc321x_sb_remove(struct pci_dev *pdev)
+{
+	mfd_remove_devices(&pdev->dev);
+}
+
+static DEFINE_PCI_DEVICE_TABLE(rdc321x_sb_table) = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_RDC, PCI_DEVICE_ID_RDC_R6030) },
+	{}
+};
+MODULE_DEVICE_TABLE(pci, rdc321x_sb_table);
+
+static struct pci_driver rdc321x_sb_driver = {
+	.name		= "RDC321x Southbridge",
+	.id_table	= rdc321x_sb_table,
+	.probe		= rdc321x_sb_probe,
+	.remove		= __devexit_p(rdc321x_sb_remove),
+};
+
+static int __init rdc321x_sb_init(void)
+{
+	return pci_register_driver(&rdc321x_sb_driver);
+}
+
+static void __exit rdc321x_sb_exit(void)
+{
+	pci_unregister_driver(&rdc321x_sb_driver);
+}
+
+module_init(rdc321x_sb_init);
+module_exit(rdc321x_sb_exit);
+
+MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("RDC R-321x MFD southbridge driver");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/s5m-core.c b/ap/os/linux/linux-3.4.x/drivers/mfd/s5m-core.c
new file mode 100644
index 0000000..48949d9
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/s5m-core.c
@@ -0,0 +1,208 @@
+/*
+ * s5m87xx.c
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd
+ *              http://www.samsung.com
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/pm_runtime.h>
+#include <linux/mutex.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/s5m87xx/s5m-core.h>
+#include <linux/mfd/s5m87xx/s5m-pmic.h>
+#include <linux/mfd/s5m87xx/s5m-rtc.h>
+#include <linux/regmap.h>
+
+static struct mfd_cell s5m8751_devs[] = {
+	{
+		.name = "s5m8751-pmic",
+	}, {
+		.name = "s5m-charger",
+	}, {
+		.name = "s5m8751-codec",
+	},
+};
+
+static struct mfd_cell s5m8763_devs[] = {
+	{
+		.name = "s5m8763-pmic",
+	}, {
+		.name = "s5m-rtc",
+	}, {
+		.name = "s5m-charger",
+	},
+};
+
+static struct mfd_cell s5m8767_devs[] = {
+	{
+		.name = "s5m8767-pmic",
+	}, {
+		.name = "s5m-rtc",
+	},
+};
+
+int s5m_reg_read(struct s5m87xx_dev *s5m87xx, u8 reg, void *dest)
+{
+	return regmap_read(s5m87xx->regmap, reg, dest);
+}
+EXPORT_SYMBOL_GPL(s5m_reg_read);
+
+int s5m_bulk_read(struct s5m87xx_dev *s5m87xx, u8 reg, int count, u8 *buf)
+{
+	return regmap_bulk_read(s5m87xx->regmap, reg, buf, count);
+}
+EXPORT_SYMBOL_GPL(s5m_bulk_read);
+
+int s5m_reg_write(struct s5m87xx_dev *s5m87xx, u8 reg, u8 value)
+{
+	return regmap_write(s5m87xx->regmap, reg, value);
+}
+EXPORT_SYMBOL_GPL(s5m_reg_write);
+
+int s5m_bulk_write(struct s5m87xx_dev *s5m87xx, u8 reg, int count, u8 *buf)
+{
+	return regmap_raw_write(s5m87xx->regmap, reg, buf, count);
+}
+EXPORT_SYMBOL_GPL(s5m_bulk_write);
+
+int s5m_reg_update(struct s5m87xx_dev *s5m87xx, u8 reg, u8 val, u8 mask)
+{
+	return regmap_update_bits(s5m87xx->regmap, reg, mask, val);
+}
+EXPORT_SYMBOL_GPL(s5m_reg_update);
+
+static struct regmap_config s5m_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+};
+
+static int s5m87xx_i2c_probe(struct i2c_client *i2c,
+			    const struct i2c_device_id *id)
+{
+	struct s5m_platform_data *pdata = i2c->dev.platform_data;
+	struct s5m87xx_dev *s5m87xx;
+	int ret;
+
+	s5m87xx = devm_kzalloc(&i2c->dev, sizeof(struct s5m87xx_dev),
+				GFP_KERNEL);
+	if (s5m87xx == NULL)
+		return -ENOMEM;
+
+	i2c_set_clientdata(i2c, s5m87xx);
+	s5m87xx->dev = &i2c->dev;
+	s5m87xx->i2c = i2c;
+	s5m87xx->irq = i2c->irq;
+	s5m87xx->type = id->driver_data;
+
+	if (pdata) {
+		s5m87xx->device_type = pdata->device_type;
+		s5m87xx->ono = pdata->ono;
+		s5m87xx->irq_base = pdata->irq_base;
+		s5m87xx->wakeup = pdata->wakeup;
+	}
+
+	s5m87xx->regmap = regmap_init_i2c(i2c, &s5m_regmap_config);
+	if (IS_ERR(s5m87xx->regmap)) {
+		ret = PTR_ERR(s5m87xx->regmap);
+		dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+			ret);
+		goto err;
+	}
+
+	s5m87xx->rtc = i2c_new_dummy(i2c->adapter, RTC_I2C_ADDR);
+	i2c_set_clientdata(s5m87xx->rtc, s5m87xx);
+
+	if (pdata && pdata->cfg_pmic_irq)
+		pdata->cfg_pmic_irq();
+
+	s5m_irq_init(s5m87xx);
+
+	pm_runtime_set_active(s5m87xx->dev);
+
+	switch (s5m87xx->device_type) {
+	case S5M8751X:
+		ret = mfd_add_devices(s5m87xx->dev, -1, s5m8751_devs,
+					ARRAY_SIZE(s5m8751_devs), NULL, 0);
+		break;
+	case S5M8763X:
+		ret = mfd_add_devices(s5m87xx->dev, -1, s5m8763_devs,
+					ARRAY_SIZE(s5m8763_devs), NULL, 0);
+		break;
+	case S5M8767X:
+		ret = mfd_add_devices(s5m87xx->dev, -1, s5m8767_devs,
+					ARRAY_SIZE(s5m8767_devs), NULL, 0);
+		break;
+	default:
+		/* If this happens the probe function is problem */
+		BUG();
+	}
+
+	if (ret < 0)
+		goto err;
+
+	return ret;
+
+err:
+	mfd_remove_devices(s5m87xx->dev);
+	s5m_irq_exit(s5m87xx);
+	i2c_unregister_device(s5m87xx->rtc);
+	regmap_exit(s5m87xx->regmap);
+	return ret;
+}
+
+static int s5m87xx_i2c_remove(struct i2c_client *i2c)
+{
+	struct s5m87xx_dev *s5m87xx = i2c_get_clientdata(i2c);
+
+	mfd_remove_devices(s5m87xx->dev);
+	s5m_irq_exit(s5m87xx);
+	i2c_unregister_device(s5m87xx->rtc);
+	regmap_exit(s5m87xx->regmap);
+	return 0;
+}
+
+static const struct i2c_device_id s5m87xx_i2c_id[] = {
+	{ "s5m87xx", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, s5m87xx_i2c_id);
+
+static struct i2c_driver s5m87xx_i2c_driver = {
+	.driver = {
+		   .name = "s5m87xx",
+		   .owner = THIS_MODULE,
+	},
+	.probe = s5m87xx_i2c_probe,
+	.remove = s5m87xx_i2c_remove,
+	.id_table = s5m87xx_i2c_id,
+};
+
+static int __init s5m87xx_i2c_init(void)
+{
+	return i2c_add_driver(&s5m87xx_i2c_driver);
+}
+
+subsys_initcall(s5m87xx_i2c_init);
+
+static void __exit s5m87xx_i2c_exit(void)
+{
+	i2c_del_driver(&s5m87xx_i2c_driver);
+}
+module_exit(s5m87xx_i2c_exit);
+
+MODULE_AUTHOR("Sangbeom Kim <sbkim73@samsung.com>");
+MODULE_DESCRIPTION("Core support for the S5M MFD");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/s5m-irq.c b/ap/os/linux/linux-3.4.x/drivers/mfd/s5m-irq.c
new file mode 100644
index 0000000..0236676
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/s5m-irq.c
@@ -0,0 +1,495 @@
+/*
+ * s5m-irq.c
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd
+ *              http://www.samsung.com
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/mfd/s5m87xx/s5m-core.h>
+
+struct s5m_irq_data {
+	int reg;
+	int mask;
+};
+
+static struct s5m_irq_data s5m8767_irqs[] = {
+	[S5M8767_IRQ_PWRR] = {
+		.reg = 1,
+		.mask = S5M8767_IRQ_PWRR_MASK,
+	},
+	[S5M8767_IRQ_PWRF] = {
+		.reg = 1,
+		.mask = S5M8767_IRQ_PWRF_MASK,
+	},
+	[S5M8767_IRQ_PWR1S] = {
+		.reg = 1,
+		.mask = S5M8767_IRQ_PWR1S_MASK,
+	},
+	[S5M8767_IRQ_JIGR] = {
+		.reg = 1,
+		.mask = S5M8767_IRQ_JIGR_MASK,
+	},
+	[S5M8767_IRQ_JIGF] = {
+		.reg = 1,
+		.mask = S5M8767_IRQ_JIGF_MASK,
+	},
+	[S5M8767_IRQ_LOWBAT2] = {
+		.reg = 1,
+		.mask = S5M8767_IRQ_LOWBAT2_MASK,
+	},
+	[S5M8767_IRQ_LOWBAT1] = {
+		.reg = 1,
+		.mask = S5M8767_IRQ_LOWBAT1_MASK,
+	},
+	[S5M8767_IRQ_MRB] = {
+		.reg = 2,
+		.mask = S5M8767_IRQ_MRB_MASK,
+	},
+	[S5M8767_IRQ_DVSOK2] = {
+		.reg = 2,
+		.mask = S5M8767_IRQ_DVSOK2_MASK,
+	},
+	[S5M8767_IRQ_DVSOK3] = {
+		.reg = 2,
+		.mask = S5M8767_IRQ_DVSOK3_MASK,
+	},
+	[S5M8767_IRQ_DVSOK4] = {
+		.reg = 2,
+		.mask = S5M8767_IRQ_DVSOK4_MASK,
+	},
+	[S5M8767_IRQ_RTC60S] = {
+		.reg = 3,
+		.mask = S5M8767_IRQ_RTC60S_MASK,
+	},
+	[S5M8767_IRQ_RTCA1] = {
+		.reg = 3,
+		.mask = S5M8767_IRQ_RTCA1_MASK,
+	},
+	[S5M8767_IRQ_RTCA2] = {
+		.reg = 3,
+		.mask = S5M8767_IRQ_RTCA2_MASK,
+	},
+	[S5M8767_IRQ_SMPL] = {
+		.reg = 3,
+		.mask = S5M8767_IRQ_SMPL_MASK,
+	},
+	[S5M8767_IRQ_RTC1S] = {
+		.reg = 3,
+		.mask = S5M8767_IRQ_RTC1S_MASK,
+	},
+	[S5M8767_IRQ_WTSR] = {
+		.reg = 3,
+		.mask = S5M8767_IRQ_WTSR_MASK,
+	},
+};
+
+static struct s5m_irq_data s5m8763_irqs[] = {
+	[S5M8763_IRQ_DCINF] = {
+		.reg = 1,
+		.mask = S5M8763_IRQ_DCINF_MASK,
+	},
+	[S5M8763_IRQ_DCINR] = {
+		.reg = 1,
+		.mask = S5M8763_IRQ_DCINR_MASK,
+	},
+	[S5M8763_IRQ_JIGF] = {
+		.reg = 1,
+		.mask = S5M8763_IRQ_JIGF_MASK,
+	},
+	[S5M8763_IRQ_JIGR] = {
+		.reg = 1,
+		.mask = S5M8763_IRQ_JIGR_MASK,
+	},
+	[S5M8763_IRQ_PWRONF] = {
+		.reg = 1,
+		.mask = S5M8763_IRQ_PWRONF_MASK,
+	},
+	[S5M8763_IRQ_PWRONR] = {
+		.reg = 1,
+		.mask = S5M8763_IRQ_PWRONR_MASK,
+	},
+	[S5M8763_IRQ_WTSREVNT] = {
+		.reg = 2,
+		.mask = S5M8763_IRQ_WTSREVNT_MASK,
+	},
+	[S5M8763_IRQ_SMPLEVNT] = {
+		.reg = 2,
+		.mask = S5M8763_IRQ_SMPLEVNT_MASK,
+	},
+	[S5M8763_IRQ_ALARM1] = {
+		.reg = 2,
+		.mask = S5M8763_IRQ_ALARM1_MASK,
+	},
+	[S5M8763_IRQ_ALARM0] = {
+		.reg = 2,
+		.mask = S5M8763_IRQ_ALARM0_MASK,
+	},
+	[S5M8763_IRQ_ONKEY1S] = {
+		.reg = 3,
+		.mask = S5M8763_IRQ_ONKEY1S_MASK,
+	},
+	[S5M8763_IRQ_TOPOFFR] = {
+		.reg = 3,
+		.mask = S5M8763_IRQ_TOPOFFR_MASK,
+	},
+	[S5M8763_IRQ_DCINOVPR] = {
+		.reg = 3,
+		.mask = S5M8763_IRQ_DCINOVPR_MASK,
+	},
+	[S5M8763_IRQ_CHGRSTF] = {
+		.reg = 3,
+		.mask = S5M8763_IRQ_CHGRSTF_MASK,
+	},
+	[S5M8763_IRQ_DONER] = {
+		.reg = 3,
+		.mask = S5M8763_IRQ_DONER_MASK,
+	},
+	[S5M8763_IRQ_CHGFAULT] = {
+		.reg = 3,
+		.mask = S5M8763_IRQ_CHGFAULT_MASK,
+	},
+	[S5M8763_IRQ_LOBAT1] = {
+		.reg = 4,
+		.mask = S5M8763_IRQ_LOBAT1_MASK,
+	},
+	[S5M8763_IRQ_LOBAT2] = {
+		.reg = 4,
+		.mask = S5M8763_IRQ_LOBAT2_MASK,
+	},
+};
+
+static inline struct s5m_irq_data *
+irq_to_s5m8767_irq(struct s5m87xx_dev *s5m87xx, int irq)
+{
+	return &s5m8767_irqs[irq - s5m87xx->irq_base];
+}
+
+static void s5m8767_irq_lock(struct irq_data *data)
+{
+	struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data);
+
+	mutex_lock(&s5m87xx->irqlock);
+}
+
+static void s5m8767_irq_sync_unlock(struct irq_data *data)
+{
+	struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data);
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(s5m87xx->irq_masks_cur); i++) {
+		if (s5m87xx->irq_masks_cur[i] != s5m87xx->irq_masks_cache[i]) {
+			s5m87xx->irq_masks_cache[i] = s5m87xx->irq_masks_cur[i];
+			s5m_reg_write(s5m87xx, S5M8767_REG_INT1M + i,
+					s5m87xx->irq_masks_cur[i]);
+		}
+	}
+
+	mutex_unlock(&s5m87xx->irqlock);
+}
+
+static void s5m8767_irq_unmask(struct irq_data *data)
+{
+	struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data);
+	struct s5m_irq_data *irq_data = irq_to_s5m8767_irq(s5m87xx,
+							       data->irq);
+
+	s5m87xx->irq_masks_cur[irq_data->reg - 1] &= ~irq_data->mask;
+}
+
+static void s5m8767_irq_mask(struct irq_data *data)
+{
+	struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data);
+	struct s5m_irq_data *irq_data = irq_to_s5m8767_irq(s5m87xx,
+							       data->irq);
+
+	s5m87xx->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask;
+}
+
+static struct irq_chip s5m8767_irq_chip = {
+	.name = "s5m8767",
+	.irq_bus_lock = s5m8767_irq_lock,
+	.irq_bus_sync_unlock = s5m8767_irq_sync_unlock,
+	.irq_mask = s5m8767_irq_mask,
+	.irq_unmask = s5m8767_irq_unmask,
+};
+
+static inline struct s5m_irq_data *
+irq_to_s5m8763_irq(struct s5m87xx_dev *s5m87xx, int irq)
+{
+	return &s5m8763_irqs[irq - s5m87xx->irq_base];
+}
+
+static void s5m8763_irq_lock(struct irq_data *data)
+{
+	struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data);
+
+	mutex_lock(&s5m87xx->irqlock);
+}
+
+static void s5m8763_irq_sync_unlock(struct irq_data *data)
+{
+	struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data);
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(s5m87xx->irq_masks_cur); i++) {
+		if (s5m87xx->irq_masks_cur[i] != s5m87xx->irq_masks_cache[i]) {
+			s5m87xx->irq_masks_cache[i] = s5m87xx->irq_masks_cur[i];
+			s5m_reg_write(s5m87xx, S5M8763_REG_IRQM1 + i,
+					s5m87xx->irq_masks_cur[i]);
+		}
+	}
+
+	mutex_unlock(&s5m87xx->irqlock);
+}
+
+static void s5m8763_irq_unmask(struct irq_data *data)
+{
+	struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data);
+	struct s5m_irq_data *irq_data = irq_to_s5m8763_irq(s5m87xx,
+							       data->irq);
+
+	s5m87xx->irq_masks_cur[irq_data->reg - 1] &= ~irq_data->mask;
+}
+
+static void s5m8763_irq_mask(struct irq_data *data)
+{
+	struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data);
+	struct s5m_irq_data *irq_data = irq_to_s5m8763_irq(s5m87xx,
+							       data->irq);
+
+	s5m87xx->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask;
+}
+
+static struct irq_chip s5m8763_irq_chip = {
+	.name = "s5m8763",
+	.irq_bus_lock = s5m8763_irq_lock,
+	.irq_bus_sync_unlock = s5m8763_irq_sync_unlock,
+	.irq_mask = s5m8763_irq_mask,
+	.irq_unmask = s5m8763_irq_unmask,
+};
+
+
+static irqreturn_t s5m8767_irq_thread(int irq, void *data)
+{
+	struct s5m87xx_dev *s5m87xx = data;
+	u8 irq_reg[NUM_IRQ_REGS-1];
+	int ret;
+	int i;
+
+
+	ret = s5m_bulk_read(s5m87xx, S5M8767_REG_INT1,
+				NUM_IRQ_REGS - 1, irq_reg);
+	if (ret < 0) {
+		dev_err(s5m87xx->dev, "Failed to read interrupt register: %d\n",
+				ret);
+		return IRQ_NONE;
+	}
+
+	for (i = 0; i < NUM_IRQ_REGS - 1; i++)
+		irq_reg[i] &= ~s5m87xx->irq_masks_cur[i];
+
+	for (i = 0; i < S5M8767_IRQ_NR; i++) {
+		if (irq_reg[s5m8767_irqs[i].reg - 1] & s5m8767_irqs[i].mask)
+			handle_nested_irq(s5m87xx->irq_base + i);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t s5m8763_irq_thread(int irq, void *data)
+{
+	struct s5m87xx_dev *s5m87xx = data;
+	u8 irq_reg[NUM_IRQ_REGS];
+	int ret;
+	int i;
+
+	ret = s5m_bulk_read(s5m87xx, S5M8763_REG_IRQ1,
+				NUM_IRQ_REGS, irq_reg);
+	if (ret < 0) {
+		dev_err(s5m87xx->dev, "Failed to read interrupt register: %d\n",
+				ret);
+		return IRQ_NONE;
+	}
+
+	for (i = 0; i < NUM_IRQ_REGS; i++)
+		irq_reg[i] &= ~s5m87xx->irq_masks_cur[i];
+
+	for (i = 0; i < S5M8763_IRQ_NR; i++) {
+		if (irq_reg[s5m8763_irqs[i].reg - 1] & s5m8763_irqs[i].mask)
+			handle_nested_irq(s5m87xx->irq_base + i);
+	}
+
+	return IRQ_HANDLED;
+}
+
+int s5m_irq_resume(struct s5m87xx_dev *s5m87xx)
+{
+	if (s5m87xx->irq && s5m87xx->irq_base){
+		switch (s5m87xx->device_type) {
+		case S5M8763X:
+			s5m8763_irq_thread(s5m87xx->irq_base, s5m87xx);
+			break;
+		case S5M8767X:
+			s5m8767_irq_thread(s5m87xx->irq_base, s5m87xx);
+			break;
+		default:
+			dev_err(s5m87xx->dev,
+				"Unknown device type %d\n",
+				s5m87xx->device_type);
+			return -EINVAL;
+
+		}
+	}
+	return 0;
+}
+
+int s5m_irq_init(struct s5m87xx_dev *s5m87xx)
+{
+	int i;
+	int cur_irq;
+	int ret = 0;
+	int type = s5m87xx->device_type;
+
+	if (!s5m87xx->irq) {
+		dev_warn(s5m87xx->dev,
+			 "No interrupt specified, no interrupts\n");
+		s5m87xx->irq_base = 0;
+		return 0;
+	}
+
+	if (!s5m87xx->irq_base) {
+		dev_err(s5m87xx->dev,
+			"No interrupt base specified, no interrupts\n");
+		return 0;
+	}
+
+	mutex_init(&s5m87xx->irqlock);
+
+	switch (type) {
+	case S5M8763X:
+		for (i = 0; i < NUM_IRQ_REGS; i++) {
+			s5m87xx->irq_masks_cur[i] = 0xff;
+			s5m87xx->irq_masks_cache[i] = 0xff;
+			s5m_reg_write(s5m87xx, S5M8763_REG_IRQM1 + i,
+						0xff);
+		}
+
+		s5m_reg_write(s5m87xx, S5M8763_REG_STATUSM1, 0xff);
+		s5m_reg_write(s5m87xx, S5M8763_REG_STATUSM2, 0xff);
+
+		for (i = 0; i < S5M8763_IRQ_NR; i++) {
+			cur_irq = i + s5m87xx->irq_base;
+			irq_set_chip_data(cur_irq, s5m87xx);
+			irq_set_chip_and_handler(cur_irq, &s5m8763_irq_chip,
+						 handle_edge_irq);
+			irq_set_nested_thread(cur_irq, 1);
+#ifdef CONFIG_ARM
+			set_irq_flags(cur_irq, IRQF_VALID);
+#else
+			irq_set_noprobe(cur_irq);
+#endif
+		}
+
+		ret = request_threaded_irq(s5m87xx->irq, NULL,
+					s5m8763_irq_thread,
+					IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+					"s5m87xx-irq", s5m87xx);
+		if (ret) {
+			dev_err(s5m87xx->dev, "Failed to request IRQ %d: %d\n",
+				s5m87xx->irq, ret);
+			return ret;
+		}
+		break;
+	case S5M8767X:
+		for (i = 0; i < NUM_IRQ_REGS - 1; i++) {
+			s5m87xx->irq_masks_cur[i] = 0xff;
+			s5m87xx->irq_masks_cache[i] = 0xff;
+			s5m_reg_write(s5m87xx, S5M8767_REG_INT1M + i,
+						0xff);
+		}
+		for (i = 0; i < S5M8767_IRQ_NR; i++) {
+			cur_irq = i + s5m87xx->irq_base;
+			irq_set_chip_data(cur_irq, s5m87xx);
+			if (ret) {
+				dev_err(s5m87xx->dev,
+					"Failed to irq_set_chip_data %d: %d\n",
+					s5m87xx->irq, ret);
+				return ret;
+			}
+
+			irq_set_chip_and_handler(cur_irq, &s5m8767_irq_chip,
+						 handle_edge_irq);
+			irq_set_nested_thread(cur_irq, 1);
+#ifdef CONFIG_ARM
+			set_irq_flags(cur_irq, IRQF_VALID);
+#else
+			irq_set_noprobe(cur_irq);
+#endif
+		}
+
+		ret = request_threaded_irq(s5m87xx->irq, NULL,
+					   s5m8767_irq_thread,
+					   IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+					   "s5m87xx-irq", s5m87xx);
+		if (ret) {
+			dev_err(s5m87xx->dev, "Failed to request IRQ %d: %d\n",
+				s5m87xx->irq, ret);
+			return ret;
+		}
+		break;
+	default:
+		dev_err(s5m87xx->dev,
+			"Unknown device type %d\n", s5m87xx->device_type);
+		return -EINVAL;
+	}
+
+	if (!s5m87xx->ono)
+		return 0;
+
+	switch (type) {
+	case S5M8763X:
+		ret = request_threaded_irq(s5m87xx->ono, NULL,
+						s5m8763_irq_thread,
+						IRQF_TRIGGER_FALLING |
+						IRQF_TRIGGER_RISING |
+						IRQF_ONESHOT, "s5m87xx-ono",
+						s5m87xx);
+		break;
+	case S5M8767X:
+		ret = request_threaded_irq(s5m87xx->ono, NULL,
+					s5m8767_irq_thread,
+					IRQF_TRIGGER_FALLING |
+					IRQF_TRIGGER_RISING |
+					IRQF_ONESHOT, "s5m87xx-ono", s5m87xx);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	if (ret) {
+		dev_err(s5m87xx->dev, "Failed to request IRQ %d: %d\n",
+			s5m87xx->ono, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+void s5m_irq_exit(struct s5m87xx_dev *s5m87xx)
+{
+	if (s5m87xx->ono)
+		free_irq(s5m87xx->ono, s5m87xx);
+
+	if (s5m87xx->irq)
+		free_irq(s5m87xx->irq, s5m87xx);
+}
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/sm501.c b/ap/os/linux/linux-3.4.x/drivers/mfd/sm501.c
new file mode 100644
index 0000000..d927dd4
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/sm501.c
@@ -0,0 +1,1765 @@
+/* linux/drivers/mfd/sm501.c
+ *
+ * Copyright (C) 2006 Simtec Electronics
+ *	Ben Dooks <ben@simtec.co.uk>
+ *	Vincent Sanders <vince@simtec.co.uk>
+ *
+ * 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.
+ *
+ * SM501 MFD driver
+*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/pci.h>
+#include <linux/i2c-gpio.h>
+#include <linux/slab.h>
+
+#include <linux/sm501.h>
+#include <linux/sm501-regs.h>
+#include <linux/serial_8250.h>
+
+#include <linux/io.h>
+
+struct sm501_device {
+	struct list_head		list;
+	struct platform_device		pdev;
+};
+
+struct sm501_gpio;
+
+#ifdef CONFIG_MFD_SM501_GPIO
+#include <linux/gpio.h>
+
+struct sm501_gpio_chip {
+	struct gpio_chip	gpio;
+	struct sm501_gpio	*ourgpio;	/* to get back to parent. */
+	void __iomem		*regbase;
+	void __iomem		*control;	/* address of control reg. */
+};
+
+struct sm501_gpio {
+	struct sm501_gpio_chip	low;
+	struct sm501_gpio_chip	high;
+	spinlock_t		lock;
+
+	unsigned int		 registered : 1;
+	void __iomem		*regs;
+	struct resource		*regs_res;
+};
+#else
+struct sm501_gpio {
+	/* no gpio support, empty definition for sm501_devdata. */
+};
+#endif
+
+struct sm501_devdata {
+	spinlock_t			 reg_lock;
+	struct mutex			 clock_lock;
+	struct list_head		 devices;
+	struct sm501_gpio		 gpio;
+
+	struct device			*dev;
+	struct resource			*io_res;
+	struct resource			*mem_res;
+	struct resource			*regs_claim;
+	struct sm501_platdata		*platdata;
+
+
+	unsigned int			 in_suspend;
+	unsigned long			 pm_misc;
+
+	int				 unit_power[20];
+	unsigned int			 pdev_id;
+	unsigned int			 irq;
+	void __iomem			*regs;
+	unsigned int			 rev;
+};
+
+
+#define MHZ (1000 * 1000)
+
+#ifdef DEBUG
+static const unsigned int div_tab[] = {
+	[0]		= 1,
+	[1]		= 2,
+	[2]		= 4,
+	[3]		= 8,
+	[4]		= 16,
+	[5]		= 32,
+	[6]		= 64,
+	[7]		= 128,
+	[8]		= 3,
+	[9]		= 6,
+	[10]	        = 12,
+	[11]		= 24,
+	[12]		= 48,
+	[13]		= 96,
+	[14]		= 192,
+	[15]		= 384,
+	[16]		= 5,
+	[17]		= 10,
+	[18]		= 20,
+	[19]		= 40,
+	[20]		= 80,
+	[21]		= 160,
+	[22]		= 320,
+	[23]		= 604,
+};
+
+static unsigned long decode_div(unsigned long pll2, unsigned long val,
+				unsigned int lshft, unsigned int selbit,
+				unsigned long mask)
+{
+	if (val & selbit)
+		pll2 = 288 * MHZ;
+
+	return pll2 / div_tab[(val >> lshft) & mask];
+}
+
+#define fmt_freq(x) ((x) / MHZ), ((x) % MHZ), (x)
+
+/* sm501_dump_clk
+ *
+ * Print out the current clock configuration for the device
+*/
+
+static void sm501_dump_clk(struct sm501_devdata *sm)
+{
+	unsigned long misct = smc501_readl(sm->regs + SM501_MISC_TIMING);
+	unsigned long pm0 = smc501_readl(sm->regs + SM501_POWER_MODE_0_CLOCK);
+	unsigned long pm1 = smc501_readl(sm->regs + SM501_POWER_MODE_1_CLOCK);
+	unsigned long pmc = smc501_readl(sm->regs + SM501_POWER_MODE_CONTROL);
+	unsigned long sdclk0, sdclk1;
+	unsigned long pll2 = 0;
+
+	switch (misct & 0x30) {
+	case 0x00:
+		pll2 = 336 * MHZ;
+		break;
+	case 0x10:
+		pll2 = 288 * MHZ;
+		break;
+	case 0x20:
+		pll2 = 240 * MHZ;
+		break;
+	case 0x30:
+		pll2 = 192 * MHZ;
+		break;
+	}
+
+	sdclk0 = (misct & (1<<12)) ? pll2 : 288 * MHZ;
+	sdclk0 /= div_tab[((misct >> 8) & 0xf)];
+
+	sdclk1 = (misct & (1<<20)) ? pll2 : 288 * MHZ;
+	sdclk1 /= div_tab[((misct >> 16) & 0xf)];
+
+	dev_dbg(sm->dev, "MISCT=%08lx, PM0=%08lx, PM1=%08lx\n",
+		misct, pm0, pm1);
+
+	dev_dbg(sm->dev, "PLL2 = %ld.%ld MHz (%ld), SDCLK0=%08lx, SDCLK1=%08lx\n",
+		fmt_freq(pll2), sdclk0, sdclk1);
+
+	dev_dbg(sm->dev, "SDRAM: PM0=%ld, PM1=%ld\n", sdclk0, sdclk1);
+
+	dev_dbg(sm->dev, "PM0[%c]: "
+		 "P2 %ld.%ld MHz (%ld), V2 %ld.%ld (%ld), "
+		 "M %ld.%ld (%ld), MX1 %ld.%ld (%ld)\n",
+		 (pmc & 3 ) == 0 ? '*' : '-',
+		 fmt_freq(decode_div(pll2, pm0, 24, 1<<29, 31)),
+		 fmt_freq(decode_div(pll2, pm0, 16, 1<<20, 15)),
+		 fmt_freq(decode_div(pll2, pm0, 8,  1<<12, 15)),
+		 fmt_freq(decode_div(pll2, pm0, 0,  1<<4,  15)));
+
+	dev_dbg(sm->dev, "PM1[%c]: "
+		"P2 %ld.%ld MHz (%ld), V2 %ld.%ld (%ld), "
+		"M %ld.%ld (%ld), MX1 %ld.%ld (%ld)\n",
+		(pmc & 3 ) == 1 ? '*' : '-',
+		fmt_freq(decode_div(pll2, pm1, 24, 1<<29, 31)),
+		fmt_freq(decode_div(pll2, pm1, 16, 1<<20, 15)),
+		fmt_freq(decode_div(pll2, pm1, 8,  1<<12, 15)),
+		fmt_freq(decode_div(pll2, pm1, 0,  1<<4,  15)));
+}
+
+static void sm501_dump_regs(struct sm501_devdata *sm)
+{
+	void __iomem *regs = sm->regs;
+
+	dev_info(sm->dev, "System Control   %08x\n",
+			smc501_readl(regs + SM501_SYSTEM_CONTROL));
+	dev_info(sm->dev, "Misc Control     %08x\n",
+			smc501_readl(regs + SM501_MISC_CONTROL));
+	dev_info(sm->dev, "GPIO Control Low %08x\n",
+			smc501_readl(regs + SM501_GPIO31_0_CONTROL));
+	dev_info(sm->dev, "GPIO Control Hi  %08x\n",
+			smc501_readl(regs + SM501_GPIO63_32_CONTROL));
+	dev_info(sm->dev, "DRAM Control     %08x\n",
+			smc501_readl(regs + SM501_DRAM_CONTROL));
+	dev_info(sm->dev, "Arbitration Ctrl %08x\n",
+			smc501_readl(regs + SM501_ARBTRTN_CONTROL));
+	dev_info(sm->dev, "Misc Timing      %08x\n",
+			smc501_readl(regs + SM501_MISC_TIMING));
+}
+
+static void sm501_dump_gate(struct sm501_devdata *sm)
+{
+	dev_info(sm->dev, "CurrentGate      %08x\n",
+			smc501_readl(sm->regs + SM501_CURRENT_GATE));
+	dev_info(sm->dev, "CurrentClock     %08x\n",
+			smc501_readl(sm->regs + SM501_CURRENT_CLOCK));
+	dev_info(sm->dev, "PowerModeControl %08x\n",
+			smc501_readl(sm->regs + SM501_POWER_MODE_CONTROL));
+}
+
+#else
+static inline void sm501_dump_gate(struct sm501_devdata *sm) { }
+static inline void sm501_dump_regs(struct sm501_devdata *sm) { }
+static inline void sm501_dump_clk(struct sm501_devdata *sm) { }
+#endif
+
+/* sm501_sync_regs
+ *
+ * ensure the
+*/
+
+static void sm501_sync_regs(struct sm501_devdata *sm)
+{
+	smc501_readl(sm->regs);
+}
+
+static inline void sm501_mdelay(struct sm501_devdata *sm, unsigned int delay)
+{
+	/* during suspend/resume, we are currently not allowed to sleep,
+	 * so change to using mdelay() instead of msleep() if we
+	 * are in one of these paths */
+
+	if (sm->in_suspend)
+		mdelay(delay);
+	else
+		msleep(delay);
+}
+
+/* sm501_misc_control
+ *
+ * alters the miscellaneous control parameters
+*/
+
+int sm501_misc_control(struct device *dev,
+		       unsigned long set, unsigned long clear)
+{
+	struct sm501_devdata *sm = dev_get_drvdata(dev);
+	unsigned long misc;
+	unsigned long save;
+	unsigned long to;
+
+	spin_lock_irqsave(&sm->reg_lock, save);
+
+	misc = smc501_readl(sm->regs + SM501_MISC_CONTROL);
+	to = (misc & ~clear) | set;
+
+	if (to != misc) {
+		smc501_writel(to, sm->regs + SM501_MISC_CONTROL);
+		sm501_sync_regs(sm);
+
+		dev_dbg(sm->dev, "MISC_CONTROL %08lx\n", misc);
+	}
+
+	spin_unlock_irqrestore(&sm->reg_lock, save);
+	return to;
+}
+
+EXPORT_SYMBOL_GPL(sm501_misc_control);
+
+/* sm501_modify_reg
+ *
+ * Modify a register in the SM501 which may be shared with other
+ * drivers.
+*/
+
+unsigned long sm501_modify_reg(struct device *dev,
+			       unsigned long reg,
+			       unsigned long set,
+			       unsigned long clear)
+{
+	struct sm501_devdata *sm = dev_get_drvdata(dev);
+	unsigned long data;
+	unsigned long save;
+
+	spin_lock_irqsave(&sm->reg_lock, save);
+
+	data = smc501_readl(sm->regs + reg);
+	data |= set;
+	data &= ~clear;
+
+	smc501_writel(data, sm->regs + reg);
+	sm501_sync_regs(sm);
+
+	spin_unlock_irqrestore(&sm->reg_lock, save);
+
+	return data;
+}
+
+EXPORT_SYMBOL_GPL(sm501_modify_reg);
+
+/* sm501_unit_power
+ *
+ * alters the power active gate to set specific units on or off
+ */
+
+int sm501_unit_power(struct device *dev, unsigned int unit, unsigned int to)
+{
+	struct sm501_devdata *sm = dev_get_drvdata(dev);
+	unsigned long mode;
+	unsigned long gate;
+	unsigned long clock;
+
+	mutex_lock(&sm->clock_lock);
+
+	mode = smc501_readl(sm->regs + SM501_POWER_MODE_CONTROL);
+	gate = smc501_readl(sm->regs + SM501_CURRENT_GATE);
+	clock = smc501_readl(sm->regs + SM501_CURRENT_CLOCK);
+
+	mode &= 3;		/* get current power mode */
+
+	if (unit >= ARRAY_SIZE(sm->unit_power)) {
+		dev_err(dev, "%s: bad unit %d\n", __func__, unit);
+		goto already;
+	}
+
+	dev_dbg(sm->dev, "%s: unit %d, cur %d, to %d\n", __func__, unit,
+		sm->unit_power[unit], to);
+
+	if (to == 0 && sm->unit_power[unit] == 0) {
+		dev_err(sm->dev, "unit %d is already shutdown\n", unit);
+		goto already;
+	}
+
+	sm->unit_power[unit] += to ? 1 : -1;
+	to = sm->unit_power[unit] ? 1 : 0;
+
+	if (to) {
+		if (gate & (1 << unit))
+			goto already;
+		gate |= (1 << unit);
+	} else {
+		if (!(gate & (1 << unit)))
+			goto already;
+		gate &= ~(1 << unit);
+	}
+
+	switch (mode) {
+	case 1:
+		smc501_writel(gate, sm->regs + SM501_POWER_MODE_0_GATE);
+		smc501_writel(clock, sm->regs + SM501_POWER_MODE_0_CLOCK);
+		mode = 0;
+		break;
+	case 2:
+	case 0:
+		smc501_writel(gate, sm->regs + SM501_POWER_MODE_1_GATE);
+		smc501_writel(clock, sm->regs + SM501_POWER_MODE_1_CLOCK);
+		mode = 1;
+		break;
+
+	default:
+		gate = -1;
+		goto already;
+	}
+
+	smc501_writel(mode, sm->regs + SM501_POWER_MODE_CONTROL);
+	sm501_sync_regs(sm);
+
+	dev_dbg(sm->dev, "gate %08lx, clock %08lx, mode %08lx\n",
+		gate, clock, mode);
+
+	sm501_mdelay(sm, 16);
+
+ already:
+	mutex_unlock(&sm->clock_lock);
+	return gate;
+}
+
+EXPORT_SYMBOL_GPL(sm501_unit_power);
+
+/* clock value structure. */
+struct sm501_clock {
+	unsigned long mclk;
+	int divider;
+	int shift;
+	unsigned int m, n, k;
+};
+
+/* sm501_calc_clock
+ *
+ * Calculates the nearest discrete clock frequency that
+ * can be achieved with the specified input clock.
+ *   the maximum divisor is 3 or 5
+ */
+
+static int sm501_calc_clock(unsigned long freq,
+			    struct sm501_clock *clock,
+			    int max_div,
+			    unsigned long mclk,
+			    long *best_diff)
+{
+	int ret = 0;
+	int divider;
+	int shift;
+	long diff;
+
+	/* try dividers 1 and 3 for CRT and for panel,
+	   try divider 5 for panel only.*/
+
+	for (divider = 1; divider <= max_div; divider += 2) {
+		/* try all 8 shift values.*/
+		for (shift = 0; shift < 8; shift++) {
+			/* Calculate difference to requested clock */
+			diff = DIV_ROUND_CLOSEST(mclk, divider << shift) - freq;
+			if (diff < 0)
+				diff = -diff;
+
+			/* If it is less than the current, use it */
+			if (diff < *best_diff) {
+				*best_diff = diff;
+
+				clock->mclk = mclk;
+				clock->divider = divider;
+				clock->shift = shift;
+				ret = 1;
+			}
+		}
+	}
+
+	return ret;
+}
+
+/* sm501_calc_pll
+ *
+ * Calculates the nearest discrete clock frequency that can be
+ * achieved using the programmable PLL.
+ *   the maximum divisor is 3 or 5
+ */
+
+static unsigned long sm501_calc_pll(unsigned long freq,
+					struct sm501_clock *clock,
+					int max_div)
+{
+	unsigned long mclk;
+	unsigned int m, n, k;
+	long best_diff = 999999999;
+
+	/*
+	 * The SM502 datasheet doesn't specify the min/max values for M and N.
+	 * N = 1 at least doesn't work in practice.
+	 */
+	for (m = 2; m <= 255; m++) {
+		for (n = 2; n <= 127; n++) {
+			for (k = 0; k <= 1; k++) {
+				mclk = (24000000UL * m / n) >> k;
+
+				if (sm501_calc_clock(freq, clock, max_div,
+						     mclk, &best_diff)) {
+					clock->m = m;
+					clock->n = n;
+					clock->k = k;
+				}
+			}
+		}
+	}
+
+	/* Return best clock. */
+	return clock->mclk / (clock->divider << clock->shift);
+}
+
+/* sm501_select_clock
+ *
+ * Calculates the nearest discrete clock frequency that can be
+ * achieved using the 288MHz and 336MHz PLLs.
+ *   the maximum divisor is 3 or 5
+ */
+
+static unsigned long sm501_select_clock(unsigned long freq,
+					struct sm501_clock *clock,
+					int max_div)
+{
+	unsigned long mclk;
+	long best_diff = 999999999;
+
+	/* Try 288MHz and 336MHz clocks. */
+	for (mclk = 288000000; mclk <= 336000000; mclk += 48000000) {
+		sm501_calc_clock(freq, clock, max_div, mclk, &best_diff);
+	}
+
+	/* Return best clock. */
+	return clock->mclk / (clock->divider << clock->shift);
+}
+
+/* sm501_set_clock
+ *
+ * set one of the four clock sources to the closest available frequency to
+ *  the one specified
+*/
+
+unsigned long sm501_set_clock(struct device *dev,
+			      int clksrc,
+			      unsigned long req_freq)
+{
+	struct sm501_devdata *sm = dev_get_drvdata(dev);
+	unsigned long mode = smc501_readl(sm->regs + SM501_POWER_MODE_CONTROL);
+	unsigned long gate = smc501_readl(sm->regs + SM501_CURRENT_GATE);
+	unsigned long clock = smc501_readl(sm->regs + SM501_CURRENT_CLOCK);
+	unsigned char reg;
+	unsigned int pll_reg = 0;
+	unsigned long sm501_freq; /* the actual frequency achieved */
+
+	struct sm501_clock to;
+
+	/* find achivable discrete frequency and setup register value
+	 * accordingly, V2XCLK, MCLK and M1XCLK are the same P2XCLK
+	 * has an extra bit for the divider */
+
+	switch (clksrc) {
+	case SM501_CLOCK_P2XCLK:
+		/* This clock is divided in half so to achieve the
+		 * requested frequency the value must be multiplied by
+		 * 2. This clock also has an additional pre divisor */
+
+		if (sm->rev >= 0xC0) {
+			/* SM502 -> use the programmable PLL */
+			sm501_freq = (sm501_calc_pll(2 * req_freq,
+						     &to, 5) / 2);
+			reg = to.shift & 0x07;/* bottom 3 bits are shift */
+			if (to.divider == 3)
+				reg |= 0x08; /* /3 divider required */
+			else if (to.divider == 5)
+				reg |= 0x10; /* /5 divider required */
+			reg |= 0x40; /* select the programmable PLL */
+			pll_reg = 0x20000 | (to.k << 15) | (to.n << 8) | to.m;
+		} else {
+			sm501_freq = (sm501_select_clock(2 * req_freq,
+							 &to, 5) / 2);
+			reg = to.shift & 0x07;/* bottom 3 bits are shift */
+			if (to.divider == 3)
+				reg |= 0x08; /* /3 divider required */
+			else if (to.divider == 5)
+				reg |= 0x10; /* /5 divider required */
+			if (to.mclk != 288000000)
+				reg |= 0x20; /* which mclk pll is source */
+		}
+		break;
+
+	case SM501_CLOCK_V2XCLK:
+		/* This clock is divided in half so to achieve the
+		 * requested frequency the value must be multiplied by 2. */
+
+		sm501_freq = (sm501_select_clock(2 * req_freq, &to, 3) / 2);
+		reg=to.shift & 0x07;	/* bottom 3 bits are shift */
+		if (to.divider == 3)
+			reg |= 0x08;	/* /3 divider required */
+		if (to.mclk != 288000000)
+			reg |= 0x10;	/* which mclk pll is source */
+		break;
+
+	case SM501_CLOCK_MCLK:
+	case SM501_CLOCK_M1XCLK:
+		/* These clocks are the same and not further divided */
+
+		sm501_freq = sm501_select_clock( req_freq, &to, 3);
+		reg=to.shift & 0x07;	/* bottom 3 bits are shift */
+		if (to.divider == 3)
+			reg |= 0x08;	/* /3 divider required */
+		if (to.mclk != 288000000)
+			reg |= 0x10;	/* which mclk pll is source */
+		break;
+
+	default:
+		return 0; /* this is bad */
+	}
+
+	mutex_lock(&sm->clock_lock);
+
+	mode = smc501_readl(sm->regs + SM501_POWER_MODE_CONTROL);
+	gate = smc501_readl(sm->regs + SM501_CURRENT_GATE);
+	clock = smc501_readl(sm->regs + SM501_CURRENT_CLOCK);
+
+	clock = clock & ~(0xFF << clksrc);
+	clock |= reg<<clksrc;
+
+	mode &= 3;	/* find current mode */
+
+	switch (mode) {
+	case 1:
+		smc501_writel(gate, sm->regs + SM501_POWER_MODE_0_GATE);
+		smc501_writel(clock, sm->regs + SM501_POWER_MODE_0_CLOCK);
+		mode = 0;
+		break;
+	case 2:
+	case 0:
+		smc501_writel(gate, sm->regs + SM501_POWER_MODE_1_GATE);
+		smc501_writel(clock, sm->regs + SM501_POWER_MODE_1_CLOCK);
+		mode = 1;
+		break;
+
+	default:
+		mutex_unlock(&sm->clock_lock);
+		return -1;
+	}
+
+	smc501_writel(mode, sm->regs + SM501_POWER_MODE_CONTROL);
+
+	if (pll_reg)
+		smc501_writel(pll_reg,
+				sm->regs + SM501_PROGRAMMABLE_PLL_CONTROL);
+
+	sm501_sync_regs(sm);
+
+	dev_dbg(sm->dev, "gate %08lx, clock %08lx, mode %08lx\n",
+		gate, clock, mode);
+
+	sm501_mdelay(sm, 16);
+	mutex_unlock(&sm->clock_lock);
+
+	sm501_dump_clk(sm);
+
+	return sm501_freq;
+}
+
+EXPORT_SYMBOL_GPL(sm501_set_clock);
+
+/* sm501_find_clock
+ *
+ * finds the closest available frequency for a given clock
+*/
+
+unsigned long sm501_find_clock(struct device *dev,
+			       int clksrc,
+			       unsigned long req_freq)
+{
+	struct sm501_devdata *sm = dev_get_drvdata(dev);
+	unsigned long sm501_freq; /* the frequency achieveable by the 501 */
+	struct sm501_clock to;
+
+	switch (clksrc) {
+	case SM501_CLOCK_P2XCLK:
+		if (sm->rev >= 0xC0) {
+			/* SM502 -> use the programmable PLL */
+			sm501_freq = (sm501_calc_pll(2 * req_freq,
+						     &to, 5) / 2);
+		} else {
+			sm501_freq = (sm501_select_clock(2 * req_freq,
+							 &to, 5) / 2);
+		}
+		break;
+
+	case SM501_CLOCK_V2XCLK:
+		sm501_freq = (sm501_select_clock(2 * req_freq, &to, 3) / 2);
+		break;
+
+	case SM501_CLOCK_MCLK:
+	case SM501_CLOCK_M1XCLK:
+		sm501_freq = sm501_select_clock(req_freq, &to, 3);
+		break;
+
+	default:
+		sm501_freq = 0;		/* error */
+	}
+
+	return sm501_freq;
+}
+
+EXPORT_SYMBOL_GPL(sm501_find_clock);
+
+static struct sm501_device *to_sm_device(struct platform_device *pdev)
+{
+	return container_of(pdev, struct sm501_device, pdev);
+}
+
+/* sm501_device_release
+ *
+ * A release function for the platform devices we create to allow us to
+ * free any items we allocated
+*/
+
+static void sm501_device_release(struct device *dev)
+{
+	kfree(to_sm_device(to_platform_device(dev)));
+}
+
+/* sm501_create_subdev
+ *
+ * Create a skeleton platform device with resources for passing to a
+ * sub-driver
+*/
+
+static struct platform_device *
+sm501_create_subdev(struct sm501_devdata *sm, char *name,
+		    unsigned int res_count, unsigned int platform_data_size)
+{
+	struct sm501_device *smdev;
+
+	smdev = kzalloc(sizeof(struct sm501_device) +
+			(sizeof(struct resource) * res_count) +
+			platform_data_size, GFP_KERNEL);
+	if (!smdev)
+		return NULL;
+
+	smdev->pdev.dev.release = sm501_device_release;
+
+	smdev->pdev.name = name;
+	smdev->pdev.id = sm->pdev_id;
+	smdev->pdev.dev.parent = sm->dev;
+
+	if (res_count) {
+		smdev->pdev.resource = (struct resource *)(smdev+1);
+		smdev->pdev.num_resources = res_count;
+	}
+	if (platform_data_size)
+		smdev->pdev.dev.platform_data = (void *)(smdev+1);
+
+	return &smdev->pdev;
+}
+
+/* sm501_register_device
+ *
+ * Register a platform device created with sm501_create_subdev()
+*/
+
+static int sm501_register_device(struct sm501_devdata *sm,
+				 struct platform_device *pdev)
+{
+	struct sm501_device *smdev = to_sm_device(pdev);
+	int ptr;
+	int ret;
+
+	for (ptr = 0; ptr < pdev->num_resources; ptr++) {
+		printk(KERN_DEBUG "%s[%d] %pR\n",
+		       pdev->name, ptr, &pdev->resource[ptr]);
+	}
+
+	ret = platform_device_register(pdev);
+
+	if (ret >= 0) {
+		dev_dbg(sm->dev, "registered %s\n", pdev->name);
+		list_add_tail(&smdev->list, &sm->devices);
+	} else
+		dev_err(sm->dev, "error registering %s (%d)\n",
+			pdev->name, ret);
+
+	return ret;
+}
+
+/* sm501_create_subio
+ *
+ * Fill in an IO resource for a sub device
+*/
+
+static void sm501_create_subio(struct sm501_devdata *sm,
+			       struct resource *res,
+			       resource_size_t offs,
+			       resource_size_t size)
+{
+	res->flags = IORESOURCE_MEM;
+	res->parent = sm->io_res;
+	res->start = sm->io_res->start + offs;
+	res->end = res->start + size - 1;
+}
+
+/* sm501_create_mem
+ *
+ * Fill in an MEM resource for a sub device
+*/
+
+static void sm501_create_mem(struct sm501_devdata *sm,
+			     struct resource *res,
+			     resource_size_t *offs,
+			     resource_size_t size)
+{
+	*offs -= size;		/* adjust memory size */
+
+	res->flags = IORESOURCE_MEM;
+	res->parent = sm->mem_res;
+	res->start = sm->mem_res->start + *offs;
+	res->end = res->start + size - 1;
+}
+
+/* sm501_create_irq
+ *
+ * Fill in an IRQ resource for a sub device
+*/
+
+static void sm501_create_irq(struct sm501_devdata *sm,
+			     struct resource *res)
+{
+	res->flags = IORESOURCE_IRQ;
+	res->parent = NULL;
+	res->start = res->end = sm->irq;
+}
+
+static int sm501_register_usbhost(struct sm501_devdata *sm,
+				  resource_size_t *mem_avail)
+{
+	struct platform_device *pdev;
+
+	pdev = sm501_create_subdev(sm, "sm501-usb", 3, 0);
+	if (!pdev)
+		return -ENOMEM;
+
+	sm501_create_subio(sm, &pdev->resource[0], 0x40000, 0x20000);
+	sm501_create_mem(sm, &pdev->resource[1], mem_avail, 256*1024);
+	sm501_create_irq(sm, &pdev->resource[2]);
+
+	return sm501_register_device(sm, pdev);
+}
+
+static void sm501_setup_uart_data(struct sm501_devdata *sm,
+				  struct plat_serial8250_port *uart_data,
+				  unsigned int offset)
+{
+	uart_data->membase = sm->regs + offset;
+	uart_data->mapbase = sm->io_res->start + offset;
+	uart_data->iotype = UPIO_MEM;
+	uart_data->irq = sm->irq;
+	uart_data->flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_SHARE_IRQ;
+	uart_data->regshift = 2;
+	uart_data->uartclk = (9600 * 16);
+}
+
+static int sm501_register_uart(struct sm501_devdata *sm, int devices)
+{
+	struct platform_device *pdev;
+	struct plat_serial8250_port *uart_data;
+
+	pdev = sm501_create_subdev(sm, "serial8250", 0,
+				   sizeof(struct plat_serial8250_port) * 3);
+	if (!pdev)
+		return -ENOMEM;
+
+	uart_data = pdev->dev.platform_data;
+
+	if (devices & SM501_USE_UART0) {
+		sm501_setup_uart_data(sm, uart_data++, 0x30000);
+		sm501_unit_power(sm->dev, SM501_GATE_UART0, 1);
+		sm501_modify_reg(sm->dev, SM501_IRQ_MASK, 1 << 12, 0);
+		sm501_modify_reg(sm->dev, SM501_GPIO63_32_CONTROL, 0x01e0, 0);
+	}
+	if (devices & SM501_USE_UART1) {
+		sm501_setup_uart_data(sm, uart_data++, 0x30020);
+		sm501_unit_power(sm->dev, SM501_GATE_UART1, 1);
+		sm501_modify_reg(sm->dev, SM501_IRQ_MASK, 1 << 13, 0);
+		sm501_modify_reg(sm->dev, SM501_GPIO63_32_CONTROL, 0x1e00, 0);
+	}
+
+	pdev->id = PLAT8250_DEV_SM501;
+
+	return sm501_register_device(sm, pdev);
+}
+
+static int sm501_register_display(struct sm501_devdata *sm,
+				  resource_size_t *mem_avail)
+{
+	struct platform_device *pdev;
+
+	pdev = sm501_create_subdev(sm, "sm501-fb", 4, 0);
+	if (!pdev)
+		return -ENOMEM;
+
+	sm501_create_subio(sm, &pdev->resource[0], 0x80000, 0x10000);
+	sm501_create_subio(sm, &pdev->resource[1], 0x100000, 0x50000);
+	sm501_create_mem(sm, &pdev->resource[2], mem_avail, *mem_avail);
+	sm501_create_irq(sm, &pdev->resource[3]);
+
+	return sm501_register_device(sm, pdev);
+}
+
+#ifdef CONFIG_MFD_SM501_GPIO
+
+static inline struct sm501_gpio_chip *to_sm501_gpio(struct gpio_chip *gc)
+{
+	return container_of(gc, struct sm501_gpio_chip, gpio);
+}
+
+static inline struct sm501_devdata *sm501_gpio_to_dev(struct sm501_gpio *gpio)
+{
+	return container_of(gpio, struct sm501_devdata, gpio);
+}
+
+static int sm501_gpio_get(struct gpio_chip *chip, unsigned offset)
+
+{
+	struct sm501_gpio_chip *smgpio = to_sm501_gpio(chip);
+	unsigned long result;
+
+	result = smc501_readl(smgpio->regbase + SM501_GPIO_DATA_LOW);
+	result >>= offset;
+
+	return result & 1UL;
+}
+
+static void sm501_gpio_ensure_gpio(struct sm501_gpio_chip *smchip,
+				   unsigned long bit)
+{
+	unsigned long ctrl;
+
+	/* check and modify if this pin is not set as gpio. */
+
+	if (smc501_readl(smchip->control) & bit) {
+		dev_info(sm501_gpio_to_dev(smchip->ourgpio)->dev,
+			 "changing mode of gpio, bit %08lx\n", bit);
+
+		ctrl = smc501_readl(smchip->control);
+		ctrl &= ~bit;
+		smc501_writel(ctrl, smchip->control);
+
+		sm501_sync_regs(sm501_gpio_to_dev(smchip->ourgpio));
+	}
+}
+
+static void sm501_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+
+{
+	struct sm501_gpio_chip *smchip = to_sm501_gpio(chip);
+	struct sm501_gpio *smgpio = smchip->ourgpio;
+	unsigned long bit = 1 << offset;
+	void __iomem *regs = smchip->regbase;
+	unsigned long save;
+	unsigned long val;
+
+	dev_dbg(sm501_gpio_to_dev(smgpio)->dev, "%s(%p,%d)\n",
+		__func__, chip, offset);
+
+	spin_lock_irqsave(&smgpio->lock, save);
+
+	val = smc501_readl(regs + SM501_GPIO_DATA_LOW) & ~bit;
+	if (value)
+		val |= bit;
+	smc501_writel(val, regs);
+
+	sm501_sync_regs(sm501_gpio_to_dev(smgpio));
+	sm501_gpio_ensure_gpio(smchip, bit);
+
+	spin_unlock_irqrestore(&smgpio->lock, save);
+}
+
+static int sm501_gpio_input(struct gpio_chip *chip, unsigned offset)
+{
+	struct sm501_gpio_chip *smchip = to_sm501_gpio(chip);
+	struct sm501_gpio *smgpio = smchip->ourgpio;
+	void __iomem *regs = smchip->regbase;
+	unsigned long bit = 1 << offset;
+	unsigned long save;
+	unsigned long ddr;
+
+	dev_dbg(sm501_gpio_to_dev(smgpio)->dev, "%s(%p,%d)\n",
+		__func__, chip, offset);
+
+	spin_lock_irqsave(&smgpio->lock, save);
+
+	ddr = smc501_readl(regs + SM501_GPIO_DDR_LOW);
+	smc501_writel(ddr & ~bit, regs + SM501_GPIO_DDR_LOW);
+
+	sm501_sync_regs(sm501_gpio_to_dev(smgpio));
+	sm501_gpio_ensure_gpio(smchip, bit);
+
+	spin_unlock_irqrestore(&smgpio->lock, save);
+
+	return 0;
+}
+
+static int sm501_gpio_output(struct gpio_chip *chip,
+			     unsigned offset, int value)
+{
+	struct sm501_gpio_chip *smchip = to_sm501_gpio(chip);
+	struct sm501_gpio *smgpio = smchip->ourgpio;
+	unsigned long bit = 1 << offset;
+	void __iomem *regs = smchip->regbase;
+	unsigned long save;
+	unsigned long val;
+	unsigned long ddr;
+
+	dev_dbg(sm501_gpio_to_dev(smgpio)->dev, "%s(%p,%d,%d)\n",
+		__func__, chip, offset, value);
+
+	spin_lock_irqsave(&smgpio->lock, save);
+
+	val = smc501_readl(regs + SM501_GPIO_DATA_LOW);
+	if (value)
+		val |= bit;
+	else
+		val &= ~bit;
+	smc501_writel(val, regs);
+
+	ddr = smc501_readl(regs + SM501_GPIO_DDR_LOW);
+	smc501_writel(ddr | bit, regs + SM501_GPIO_DDR_LOW);
+
+	sm501_sync_regs(sm501_gpio_to_dev(smgpio));
+	smc501_writel(val, regs + SM501_GPIO_DATA_LOW);
+
+	sm501_sync_regs(sm501_gpio_to_dev(smgpio));
+	spin_unlock_irqrestore(&smgpio->lock, save);
+
+	return 0;
+}
+
+static struct gpio_chip gpio_chip_template = {
+	.ngpio			= 32,
+	.direction_input	= sm501_gpio_input,
+	.direction_output	= sm501_gpio_output,
+	.set			= sm501_gpio_set,
+	.get			= sm501_gpio_get,
+};
+
+static int __devinit sm501_gpio_register_chip(struct sm501_devdata *sm,
+					      struct sm501_gpio *gpio,
+					      struct sm501_gpio_chip *chip)
+{
+	struct sm501_platdata *pdata = sm->platdata;
+	struct gpio_chip *gchip = &chip->gpio;
+	int base = pdata->gpio_base;
+
+	chip->gpio = gpio_chip_template;
+
+	if (chip == &gpio->high) {
+		if (base > 0)
+			base += 32;
+		chip->regbase = gpio->regs + SM501_GPIO_DATA_HIGH;
+		chip->control = sm->regs + SM501_GPIO63_32_CONTROL;
+		gchip->label  = "SM501-HIGH";
+	} else {
+		chip->regbase = gpio->regs + SM501_GPIO_DATA_LOW;
+		chip->control = sm->regs + SM501_GPIO31_0_CONTROL;
+		gchip->label  = "SM501-LOW";
+	}
+
+	gchip->base   = base;
+	chip->ourgpio = gpio;
+
+	return gpiochip_add(gchip);
+}
+
+static int __devinit sm501_register_gpio(struct sm501_devdata *sm)
+{
+	struct sm501_gpio *gpio = &sm->gpio;
+	resource_size_t iobase = sm->io_res->start + SM501_GPIO;
+	int ret;
+	int tmp;
+
+	dev_dbg(sm->dev, "registering gpio block %08llx\n",
+		(unsigned long long)iobase);
+
+	spin_lock_init(&gpio->lock);
+
+	gpio->regs_res = request_mem_region(iobase, 0x20, "sm501-gpio");
+	if (gpio->regs_res == NULL) {
+		dev_err(sm->dev, "gpio: failed to request region\n");
+		return -ENXIO;
+	}
+
+	gpio->regs = ioremap(iobase, 0x20);
+	if (gpio->regs == NULL) {
+		dev_err(sm->dev, "gpio: failed to remap registers\n");
+		ret = -ENXIO;
+		goto err_claimed;
+	}
+
+	/* Register both our chips. */
+
+	ret = sm501_gpio_register_chip(sm, gpio, &gpio->low);
+	if (ret) {
+		dev_err(sm->dev, "failed to add low chip\n");
+		goto err_mapped;
+	}
+
+	ret = sm501_gpio_register_chip(sm, gpio, &gpio->high);
+	if (ret) {
+		dev_err(sm->dev, "failed to add high chip\n");
+		goto err_low_chip;
+	}
+
+	gpio->registered = 1;
+
+	return 0;
+
+ err_low_chip:
+	tmp = gpiochip_remove(&gpio->low.gpio);
+	if (tmp) {
+		dev_err(sm->dev, "cannot remove low chip, cannot tidy up\n");
+		return ret;
+	}
+
+ err_mapped:
+	iounmap(gpio->regs);
+
+ err_claimed:
+	release_resource(gpio->regs_res);
+	kfree(gpio->regs_res);
+
+	return ret;
+}
+
+static void sm501_gpio_remove(struct sm501_devdata *sm)
+{
+	struct sm501_gpio *gpio = &sm->gpio;
+	int ret;
+
+	if (!sm->gpio.registered)
+		return;
+
+	ret = gpiochip_remove(&gpio->low.gpio);
+	if (ret)
+		dev_err(sm->dev, "cannot remove low chip, cannot tidy up\n");
+
+	ret = gpiochip_remove(&gpio->high.gpio);
+	if (ret)
+		dev_err(sm->dev, "cannot remove high chip, cannot tidy up\n");
+
+	iounmap(gpio->regs);
+	release_resource(gpio->regs_res);
+	kfree(gpio->regs_res);
+}
+
+static inline int sm501_gpio_pin2nr(struct sm501_devdata *sm, unsigned int pin)
+{
+	struct sm501_gpio *gpio = &sm->gpio;
+	int base = (pin < 32) ? gpio->low.gpio.base : gpio->high.gpio.base;
+
+	return (pin % 32) + base;
+}
+
+static inline int sm501_gpio_isregistered(struct sm501_devdata *sm)
+{
+	return sm->gpio.registered;
+}
+#else
+static inline int sm501_register_gpio(struct sm501_devdata *sm)
+{
+	return 0;
+}
+
+static inline void sm501_gpio_remove(struct sm501_devdata *sm)
+{
+}
+
+static inline int sm501_gpio_pin2nr(struct sm501_devdata *sm, unsigned int pin)
+{
+	return -1;
+}
+
+static inline int sm501_gpio_isregistered(struct sm501_devdata *sm)
+{
+	return 0;
+}
+#endif
+
+static int sm501_register_gpio_i2c_instance(struct sm501_devdata *sm,
+					    struct sm501_platdata_gpio_i2c *iic)
+{
+	struct i2c_gpio_platform_data *icd;
+	struct platform_device *pdev;
+
+	pdev = sm501_create_subdev(sm, "i2c-gpio", 0,
+				   sizeof(struct i2c_gpio_platform_data));
+	if (!pdev)
+		return -ENOMEM;
+
+	icd = pdev->dev.platform_data;
+
+	/* We keep the pin_sda and pin_scl fields relative in case the
+	 * same platform data is passed to >1 SM501.
+	 */
+
+	icd->sda_pin = sm501_gpio_pin2nr(sm, iic->pin_sda);
+	icd->scl_pin = sm501_gpio_pin2nr(sm, iic->pin_scl);
+	icd->timeout = iic->timeout;
+	icd->udelay = iic->udelay;
+
+	/* note, we can't use either of the pin numbers, as the i2c-gpio
+	 * driver uses the platform.id field to generate the bus number
+	 * to register with the i2c core; The i2c core doesn't have enough
+	 * entries to deal with anything we currently use.
+	*/
+
+	pdev->id = iic->bus_num;
+
+	dev_info(sm->dev, "registering i2c-%d: sda=%d (%d), scl=%d (%d)\n",
+		 iic->bus_num,
+		 icd->sda_pin, iic->pin_sda, icd->scl_pin, iic->pin_scl);
+
+	return sm501_register_device(sm, pdev);
+}
+
+static int sm501_register_gpio_i2c(struct sm501_devdata *sm,
+				   struct sm501_platdata *pdata)
+{
+	struct sm501_platdata_gpio_i2c *iic = pdata->gpio_i2c;
+	int index;
+	int ret;
+
+	for (index = 0; index < pdata->gpio_i2c_nr; index++, iic++) {
+		ret = sm501_register_gpio_i2c_instance(sm, iic);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+/* sm501_dbg_regs
+ *
+ * Debug attribute to attach to parent device to show core registers
+*/
+
+static ssize_t sm501_dbg_regs(struct device *dev,
+			      struct device_attribute *attr, char *buff)
+{
+	struct sm501_devdata *sm = dev_get_drvdata(dev)	;
+	unsigned int reg;
+	char *ptr = buff;
+	int ret;
+
+	for (reg = 0x00; reg < 0x70; reg += 4) {
+		ret = sprintf(ptr, "%08x = %08x\n",
+			      reg, smc501_readl(sm->regs + reg));
+		ptr += ret;
+	}
+
+	return ptr - buff;
+}
+
+
+static DEVICE_ATTR(dbg_regs, 0666, sm501_dbg_regs, NULL);
+
+/* sm501_init_reg
+ *
+ * Helper function for the init code to setup a register
+ *
+ * clear the bits which are set in r->mask, and then set
+ * the bits set in r->set.
+*/
+
+static inline void sm501_init_reg(struct sm501_devdata *sm,
+				  unsigned long reg,
+				  struct sm501_reg_init *r)
+{
+	unsigned long tmp;
+
+	tmp = smc501_readl(sm->regs + reg);
+	tmp &= ~r->mask;
+	tmp |= r->set;
+	smc501_writel(tmp, sm->regs + reg);
+}
+
+/* sm501_init_regs
+ *
+ * Setup core register values
+*/
+
+static void sm501_init_regs(struct sm501_devdata *sm,
+			    struct sm501_initdata *init)
+{
+	sm501_misc_control(sm->dev,
+			   init->misc_control.set,
+			   init->misc_control.mask);
+
+	sm501_init_reg(sm, SM501_MISC_TIMING, &init->misc_timing);
+	sm501_init_reg(sm, SM501_GPIO31_0_CONTROL, &init->gpio_low);
+	sm501_init_reg(sm, SM501_GPIO63_32_CONTROL, &init->gpio_high);
+
+	if (init->m1xclk) {
+		dev_info(sm->dev, "setting M1XCLK to %ld\n", init->m1xclk);
+		sm501_set_clock(sm->dev, SM501_CLOCK_M1XCLK, init->m1xclk);
+	}
+
+	if (init->mclk) {
+		dev_info(sm->dev, "setting MCLK to %ld\n", init->mclk);
+		sm501_set_clock(sm->dev, SM501_CLOCK_MCLK, init->mclk);
+	}
+
+}
+
+/* Check the PLL sources for the M1CLK and M1XCLK
+ *
+ * If the M1CLK and M1XCLKs are not sourced from the same PLL, then
+ * there is a risk (see errata AB-5) that the SM501 will cease proper
+ * function. If this happens, then it is likely the SM501 will
+ * hang the system.
+*/
+
+static int sm501_check_clocks(struct sm501_devdata *sm)
+{
+	unsigned long pwrmode = smc501_readl(sm->regs + SM501_CURRENT_CLOCK);
+	unsigned long msrc = (pwrmode & SM501_POWERMODE_M_SRC);
+	unsigned long m1src = (pwrmode & SM501_POWERMODE_M1_SRC);
+
+	return ((msrc == 0 && m1src != 0) || (msrc != 0 && m1src == 0));
+}
+
+static unsigned int sm501_mem_local[] = {
+	[0]	= 4*1024*1024,
+	[1]	= 8*1024*1024,
+	[2]	= 16*1024*1024,
+	[3]	= 32*1024*1024,
+	[4]	= 64*1024*1024,
+	[5]	= 2*1024*1024,
+};
+
+/* sm501_init_dev
+ *
+ * Common init code for an SM501
+*/
+
+static int __devinit sm501_init_dev(struct sm501_devdata *sm)
+{
+	struct sm501_initdata *idata;
+	struct sm501_platdata *pdata;
+	resource_size_t mem_avail;
+	unsigned long dramctrl;
+	unsigned long devid;
+	int ret;
+
+	mutex_init(&sm->clock_lock);
+	spin_lock_init(&sm->reg_lock);
+
+	INIT_LIST_HEAD(&sm->devices);
+
+	devid = smc501_readl(sm->regs + SM501_DEVICEID);
+
+	if ((devid & SM501_DEVICEID_IDMASK) != SM501_DEVICEID_SM501) {
+		dev_err(sm->dev, "incorrect device id %08lx\n", devid);
+		return -EINVAL;
+	}
+
+	/* disable irqs */
+	smc501_writel(0, sm->regs + SM501_IRQ_MASK);
+
+	dramctrl = smc501_readl(sm->regs + SM501_DRAM_CONTROL);
+	mem_avail = sm501_mem_local[(dramctrl >> 13) & 0x7];
+
+	dev_info(sm->dev, "SM501 At %p: Version %08lx, %ld Mb, IRQ %d\n",
+		 sm->regs, devid, (unsigned long)mem_avail >> 20, sm->irq);
+
+	sm->rev = devid & SM501_DEVICEID_REVMASK;
+
+	sm501_dump_gate(sm);
+
+	ret = device_create_file(sm->dev, &dev_attr_dbg_regs);
+	if (ret)
+		dev_err(sm->dev, "failed to create debug regs file\n");
+
+	sm501_dump_clk(sm);
+
+	/* check to see if we have some device initialisation */
+
+	pdata = sm->platdata;
+	idata = pdata ? pdata->init : NULL;
+
+	if (idata) {
+		sm501_init_regs(sm, idata);
+
+		if (idata->devices & SM501_USE_USB_HOST)
+			sm501_register_usbhost(sm, &mem_avail);
+		if (idata->devices & (SM501_USE_UART0 | SM501_USE_UART1))
+			sm501_register_uart(sm, idata->devices);
+		if (idata->devices & SM501_USE_GPIO)
+			sm501_register_gpio(sm);
+	}
+
+	if (pdata && pdata->gpio_i2c != NULL && pdata->gpio_i2c_nr > 0) {
+		if (!sm501_gpio_isregistered(sm))
+			dev_err(sm->dev, "no gpio available for i2c gpio.\n");
+		else
+			sm501_register_gpio_i2c(sm, pdata);
+	}
+
+	ret = sm501_check_clocks(sm);
+	if (ret) {
+		dev_err(sm->dev, "M1X and M clocks sourced from different "
+					"PLLs\n");
+		return -EINVAL;
+	}
+
+	/* always create a framebuffer */
+	sm501_register_display(sm, &mem_avail);
+
+	return 0;
+}
+
+static int __devinit sm501_plat_probe(struct platform_device *dev)
+{
+	struct sm501_devdata *sm;
+	int ret;
+
+	sm = kzalloc(sizeof(struct sm501_devdata), GFP_KERNEL);
+	if (sm == NULL) {
+		dev_err(&dev->dev, "no memory for device data\n");
+		ret = -ENOMEM;
+		goto err1;
+	}
+
+	sm->dev = &dev->dev;
+	sm->pdev_id = dev->id;
+	sm->platdata = dev->dev.platform_data;
+
+	ret = platform_get_irq(dev, 0);
+	if (ret < 0) {
+		dev_err(&dev->dev, "failed to get irq resource\n");
+		goto err_res;
+	}
+	sm->irq = ret;
+
+	sm->io_res = platform_get_resource(dev, IORESOURCE_MEM, 1);
+	sm->mem_res = platform_get_resource(dev, IORESOURCE_MEM, 0);
+
+	if (sm->io_res == NULL || sm->mem_res == NULL) {
+		dev_err(&dev->dev, "failed to get IO resource\n");
+		ret = -ENOENT;
+		goto err_res;
+	}
+
+	sm->regs_claim = request_mem_region(sm->io_res->start,
+					    0x100, "sm501");
+
+	if (sm->regs_claim == NULL) {
+		dev_err(&dev->dev, "cannot claim registers\n");
+		ret = -EBUSY;
+		goto err_res;
+	}
+
+	platform_set_drvdata(dev, sm);
+
+	sm->regs = ioremap(sm->io_res->start, resource_size(sm->io_res));
+
+	if (sm->regs == NULL) {
+		dev_err(&dev->dev, "cannot remap registers\n");
+		ret = -EIO;
+		goto err_claim;
+	}
+
+	return sm501_init_dev(sm);
+
+ err_claim:
+	release_resource(sm->regs_claim);
+	kfree(sm->regs_claim);
+ err_res:
+	kfree(sm);
+ err1:
+	return ret;
+
+}
+
+#ifdef CONFIG_PM
+
+/* power management support */
+
+static void sm501_set_power(struct sm501_devdata *sm, int on)
+{
+	struct sm501_platdata *pd = sm->platdata;
+
+	if (pd == NULL)
+		return;
+
+	if (pd->get_power) {
+		if (pd->get_power(sm->dev) == on) {
+			dev_dbg(sm->dev, "is already %d\n", on);
+			return;
+		}
+	}
+
+	if (pd->set_power) {
+		dev_dbg(sm->dev, "setting power to %d\n", on);
+
+		pd->set_power(sm->dev, on);
+		sm501_mdelay(sm, 10);
+	}
+}
+
+static int sm501_plat_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct sm501_devdata *sm = platform_get_drvdata(pdev);
+
+	sm->in_suspend = 1;
+	sm->pm_misc = smc501_readl(sm->regs + SM501_MISC_CONTROL);
+
+	sm501_dump_regs(sm);
+
+	if (sm->platdata) {
+		if (sm->platdata->flags & SM501_FLAG_SUSPEND_OFF)
+			sm501_set_power(sm, 0);
+	}
+
+	return 0;
+}
+
+static int sm501_plat_resume(struct platform_device *pdev)
+{
+	struct sm501_devdata *sm = platform_get_drvdata(pdev);
+
+	sm501_set_power(sm, 1);
+
+	sm501_dump_regs(sm);
+	sm501_dump_gate(sm);
+	sm501_dump_clk(sm);
+
+	/* check to see if we are in the same state as when suspended */
+
+	if (smc501_readl(sm->regs + SM501_MISC_CONTROL) != sm->pm_misc) {
+		dev_info(sm->dev, "SM501_MISC_CONTROL changed over sleep\n");
+		smc501_writel(sm->pm_misc, sm->regs + SM501_MISC_CONTROL);
+
+		/* our suspend causes the controller state to change,
+		 * either by something attempting setup, power loss,
+		 * or an external reset event on power change */
+
+		if (sm->platdata && sm->platdata->init) {
+			sm501_init_regs(sm, sm->platdata->init);
+		}
+	}
+
+	/* dump our state from resume */
+
+	sm501_dump_regs(sm);
+	sm501_dump_clk(sm);
+
+	sm->in_suspend = 0;
+
+	return 0;
+}
+#else
+#define sm501_plat_suspend NULL
+#define sm501_plat_resume NULL
+#endif
+
+/* Initialisation data for PCI devices */
+
+static struct sm501_initdata sm501_pci_initdata = {
+	.gpio_high	= {
+		.set	= 0x3F000000,		/* 24bit panel */
+		.mask	= 0x0,
+	},
+	.misc_timing	= {
+		.set	= 0x010100,		/* SDRAM timing */
+		.mask	= 0x1F1F00,
+	},
+	.misc_control	= {
+		.set	= SM501_MISC_PNL_24BIT,
+		.mask	= 0,
+	},
+
+	.devices	= SM501_USE_ALL,
+
+	/* Errata AB-3 says that 72MHz is the fastest available
+	 * for 33MHZ PCI with proper bus-mastering operation */
+
+	.mclk		= 72 * MHZ,
+	.m1xclk		= 144 * MHZ,
+};
+
+static struct sm501_platdata_fbsub sm501_pdata_fbsub = {
+	.flags		= (SM501FB_FLAG_USE_INIT_MODE |
+			   SM501FB_FLAG_USE_HWCURSOR |
+			   SM501FB_FLAG_USE_HWACCEL |
+			   SM501FB_FLAG_DISABLE_AT_EXIT),
+};
+
+static struct sm501_platdata_fb sm501_fb_pdata = {
+	.fb_route	= SM501_FB_OWN,
+	.fb_crt		= &sm501_pdata_fbsub,
+	.fb_pnl		= &sm501_pdata_fbsub,
+};
+
+static struct sm501_platdata sm501_pci_platdata = {
+	.init		= &sm501_pci_initdata,
+	.fb		= &sm501_fb_pdata,
+	.gpio_base	= -1,
+};
+
+static int __devinit sm501_pci_probe(struct pci_dev *dev,
+				     const struct pci_device_id *id)
+{
+	struct sm501_devdata *sm;
+	int err;
+
+	sm = kzalloc(sizeof(struct sm501_devdata), GFP_KERNEL);
+	if (sm == NULL) {
+		dev_err(&dev->dev, "no memory for device data\n");
+		err = -ENOMEM;
+		goto err1;
+	}
+
+	/* set a default set of platform data */
+	dev->dev.platform_data = sm->platdata = &sm501_pci_platdata;
+
+	/* set a hopefully unique id for our child platform devices */
+	sm->pdev_id = 32 + dev->devfn;
+
+	pci_set_drvdata(dev, sm);
+
+	err = pci_enable_device(dev);
+	if (err) {
+		dev_err(&dev->dev, "cannot enable device\n");
+		goto err2;
+	}
+
+	sm->dev = &dev->dev;
+	sm->irq = dev->irq;
+
+#ifdef __BIG_ENDIAN
+	/* if the system is big-endian, we most probably have a
+	 * translation in the IO layer making the PCI bus little endian
+	 * so make the framebuffer swapped pixels */
+
+	sm501_fb_pdata.flags |= SM501_FBPD_SWAP_FB_ENDIAN;
+#endif
+
+	/* check our resources */
+
+	if (!(pci_resource_flags(dev, 0) & IORESOURCE_MEM)) {
+		dev_err(&dev->dev, "region #0 is not memory?\n");
+		err = -EINVAL;
+		goto err3;
+	}
+
+	if (!(pci_resource_flags(dev, 1) & IORESOURCE_MEM)) {
+		dev_err(&dev->dev, "region #1 is not memory?\n");
+		err = -EINVAL;
+		goto err3;
+	}
+
+	/* make our resources ready for sharing */
+
+	sm->io_res = &dev->resource[1];
+	sm->mem_res = &dev->resource[0];
+
+	sm->regs_claim = request_mem_region(sm->io_res->start,
+					    0x100, "sm501");
+	if (sm->regs_claim == NULL) {
+		dev_err(&dev->dev, "cannot claim registers\n");
+		err= -EBUSY;
+		goto err3;
+	}
+
+	sm->regs = pci_ioremap_bar(dev, 1);
+
+	if (sm->regs == NULL) {
+		dev_err(&dev->dev, "cannot remap registers\n");
+		err = -EIO;
+		goto err4;
+	}
+
+	sm501_init_dev(sm);
+	return 0;
+
+ err4:
+	release_resource(sm->regs_claim);
+	kfree(sm->regs_claim);
+ err3:
+	pci_disable_device(dev);
+ err2:
+	pci_set_drvdata(dev, NULL);
+	kfree(sm);
+ err1:
+	return err;
+}
+
+static void sm501_remove_sub(struct sm501_devdata *sm,
+			     struct sm501_device *smdev)
+{
+	list_del(&smdev->list);
+	platform_device_unregister(&smdev->pdev);
+}
+
+static void sm501_dev_remove(struct sm501_devdata *sm)
+{
+	struct sm501_device *smdev, *tmp;
+
+	list_for_each_entry_safe(smdev, tmp, &sm->devices, list)
+		sm501_remove_sub(sm, smdev);
+
+	device_remove_file(sm->dev, &dev_attr_dbg_regs);
+
+	sm501_gpio_remove(sm);
+}
+
+static void __devexit sm501_pci_remove(struct pci_dev *dev)
+{
+	struct sm501_devdata *sm = pci_get_drvdata(dev);
+
+	sm501_dev_remove(sm);
+	iounmap(sm->regs);
+
+	release_resource(sm->regs_claim);
+	kfree(sm->regs_claim);
+
+	pci_set_drvdata(dev, NULL);
+	pci_disable_device(dev);
+}
+
+static int sm501_plat_remove(struct platform_device *dev)
+{
+	struct sm501_devdata *sm = platform_get_drvdata(dev);
+
+	sm501_dev_remove(sm);
+	iounmap(sm->regs);
+
+	release_resource(sm->regs_claim);
+	kfree(sm->regs_claim);
+
+	return 0;
+}
+
+static DEFINE_PCI_DEVICE_TABLE(sm501_pci_tbl) = {
+	{ 0x126f, 0x0501, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+	{ 0, },
+};
+
+MODULE_DEVICE_TABLE(pci, sm501_pci_tbl);
+
+static struct pci_driver sm501_pci_driver = {
+	.name		= "sm501",
+	.id_table	= sm501_pci_tbl,
+	.probe		= sm501_pci_probe,
+	.remove		= __devexit_p(sm501_pci_remove),
+};
+
+MODULE_ALIAS("platform:sm501");
+
+static struct of_device_id __devinitdata of_sm501_match_tbl[] = {
+	{ .compatible = "smi,sm501", },
+	{ /* end */ }
+};
+
+static struct platform_driver sm501_plat_driver = {
+	.driver		= {
+		.name	= "sm501",
+		.owner	= THIS_MODULE,
+		.of_match_table = of_sm501_match_tbl,
+	},
+	.probe		= sm501_plat_probe,
+	.remove		= sm501_plat_remove,
+	.suspend	= sm501_plat_suspend,
+	.resume		= sm501_plat_resume,
+};
+
+static int __init sm501_base_init(void)
+{
+	platform_driver_register(&sm501_plat_driver);
+	return pci_register_driver(&sm501_pci_driver);
+}
+
+static void __exit sm501_base_exit(void)
+{
+	platform_driver_unregister(&sm501_plat_driver);
+	pci_unregister_driver(&sm501_pci_driver);
+}
+
+module_init(sm501_base_init);
+module_exit(sm501_base_exit);
+
+MODULE_DESCRIPTION("SM501 Core Driver");
+MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>, Vincent Sanders");
+MODULE_LICENSE("GPL v2");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/stmpe-i2c.c b/ap/os/linux/linux-3.4.x/drivers/mfd/stmpe-i2c.c
new file mode 100644
index 0000000..373f423
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/stmpe-i2c.c
@@ -0,0 +1,109 @@
+/*
+ * ST Microelectronics MFD: stmpe's i2c client specific driver
+ *
+ * Copyright (C) ST-Ericsson SA 2010
+ * Copyright (C) ST Microelectronics SA 2011
+ *
+ * License Terms: GNU General Public License, version 2
+ * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
+ * Author: Viresh Kumar <viresh.kumar@st.com> for ST Microelectronics
+ */
+
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include "stmpe.h"
+
+static int i2c_reg_read(struct stmpe *stmpe, u8 reg)
+{
+	struct i2c_client *i2c = stmpe->client;
+
+	return i2c_smbus_read_byte_data(i2c, reg);
+}
+
+static int i2c_reg_write(struct stmpe *stmpe, u8 reg, u8 val)
+{
+	struct i2c_client *i2c = stmpe->client;
+
+	return i2c_smbus_write_byte_data(i2c, reg, val);
+}
+
+static int i2c_block_read(struct stmpe *stmpe, u8 reg, u8 length, u8 *values)
+{
+	struct i2c_client *i2c = stmpe->client;
+
+	return i2c_smbus_read_i2c_block_data(i2c, reg, length, values);
+}
+
+static int i2c_block_write(struct stmpe *stmpe, u8 reg, u8 length,
+		const u8 *values)
+{
+	struct i2c_client *i2c = stmpe->client;
+
+	return i2c_smbus_write_i2c_block_data(i2c, reg, length, values);
+}
+
+static struct stmpe_client_info i2c_ci = {
+	.read_byte = i2c_reg_read,
+	.write_byte = i2c_reg_write,
+	.read_block = i2c_block_read,
+	.write_block = i2c_block_write,
+};
+
+static int __devinit
+stmpe_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
+{
+	i2c_ci.data = (void *)id;
+	i2c_ci.irq = i2c->irq;
+	i2c_ci.client = i2c;
+	i2c_ci.dev = &i2c->dev;
+
+	return stmpe_probe(&i2c_ci, id->driver_data);
+}
+
+static int __devexit stmpe_i2c_remove(struct i2c_client *i2c)
+{
+	struct stmpe *stmpe = dev_get_drvdata(&i2c->dev);
+
+	return stmpe_remove(stmpe);
+}
+
+static const struct i2c_device_id stmpe_i2c_id[] = {
+	{ "stmpe610", STMPE610 },
+	{ "stmpe801", STMPE801 },
+	{ "stmpe811", STMPE811 },
+	{ "stmpe1601", STMPE1601 },
+	{ "stmpe2401", STMPE2401 },
+	{ "stmpe2403", STMPE2403 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, stmpe_id);
+
+static struct i2c_driver stmpe_i2c_driver = {
+	.driver.name	= "stmpe-i2c",
+	.driver.owner	= THIS_MODULE,
+#ifdef CONFIG_PM
+	.driver.pm	= &stmpe_dev_pm_ops,
+#endif
+	.probe		= stmpe_i2c_probe,
+	.remove		= __devexit_p(stmpe_i2c_remove),
+	.id_table	= stmpe_i2c_id,
+};
+
+static int __init stmpe_init(void)
+{
+	return i2c_add_driver(&stmpe_i2c_driver);
+}
+subsys_initcall(stmpe_init);
+
+static void __exit stmpe_exit(void)
+{
+	i2c_del_driver(&stmpe_i2c_driver);
+}
+module_exit(stmpe_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("STMPE MFD I2C Interface Driver");
+MODULE_AUTHOR("Rabin Vincent <rabin.vincent@stericsson.com>");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/stmpe-spi.c b/ap/os/linux/linux-3.4.x/drivers/mfd/stmpe-spi.c
new file mode 100644
index 0000000..b58c43c
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/stmpe-spi.c
@@ -0,0 +1,150 @@
+/*
+ * ST Microelectronics MFD: stmpe's spi client specific driver
+ *
+ * Copyright (C) ST Microelectronics SA 2011
+ *
+ * License Terms: GNU General Public License, version 2
+ * Author: Viresh Kumar <viresh.kumar@st.com> for ST Microelectronics
+ */
+
+#include <linux/spi/spi.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include "stmpe.h"
+
+#define READ_CMD	(1 << 7)
+
+static int spi_reg_read(struct stmpe *stmpe, u8 reg)
+{
+	struct spi_device *spi = stmpe->client;
+	int status = spi_w8r16(spi, reg | READ_CMD);
+
+	return (status < 0) ? status : status >> 8;
+}
+
+static int spi_reg_write(struct stmpe *stmpe, u8 reg, u8 val)
+{
+	struct spi_device *spi = stmpe->client;
+	u16 cmd = (val << 8) | reg;
+
+	return spi_write(spi, (const u8 *)&cmd, 2);
+}
+
+static int spi_block_read(struct stmpe *stmpe, u8 reg, u8 length, u8 *values)
+{
+	int ret, i;
+
+	for (i = 0; i < length; i++) {
+		ret = spi_reg_read(stmpe, reg + i);
+		if (ret < 0)
+			return ret;
+		*(values + i) = ret;
+	}
+
+	return 0;
+}
+
+static int spi_block_write(struct stmpe *stmpe, u8 reg, u8 length,
+		const u8 *values)
+{
+	int ret = 0, i;
+
+	for (i = length; i > 0; i--, reg++) {
+		ret = spi_reg_write(stmpe, reg, *(values + i - 1));
+		if (ret < 0)
+			return ret;
+	}
+
+	return ret;
+}
+
+static void spi_init(struct stmpe *stmpe)
+{
+	struct spi_device *spi = stmpe->client;
+
+	spi->bits_per_word = 8;
+
+	/* This register is only present for stmpe811 */
+	if (stmpe->variant->id_val == 0x0811)
+		spi_reg_write(stmpe, STMPE811_REG_SPI_CFG, spi->mode);
+
+	if (spi_setup(spi) < 0)
+		dev_dbg(&spi->dev, "spi_setup failed\n");
+}
+
+static struct stmpe_client_info spi_ci = {
+	.read_byte = spi_reg_read,
+	.write_byte = spi_reg_write,
+	.read_block = spi_block_read,
+	.write_block = spi_block_write,
+	.init = spi_init,
+};
+
+static int __devinit
+stmpe_spi_probe(struct spi_device *spi)
+{
+	const struct spi_device_id *id = spi_get_device_id(spi);
+
+	/* don't exceed max specified rate - 1MHz - Limitation of STMPE */
+	if (spi->max_speed_hz > 1000000) {
+		dev_dbg(&spi->dev, "f(sample) %d KHz?\n",
+				(spi->max_speed_hz/1000));
+		return -EINVAL;
+	}
+
+	spi_ci.irq = spi->irq;
+	spi_ci.client = spi;
+	spi_ci.dev = &spi->dev;
+
+	return stmpe_probe(&spi_ci, id->driver_data);
+}
+
+static int __devexit stmpe_spi_remove(struct spi_device *spi)
+{
+	struct stmpe *stmpe = dev_get_drvdata(&spi->dev);
+
+	return stmpe_remove(stmpe);
+}
+
+static const struct spi_device_id stmpe_spi_id[] = {
+	{ "stmpe610", STMPE610 },
+	{ "stmpe801", STMPE801 },
+	{ "stmpe811", STMPE811 },
+	{ "stmpe1601", STMPE1601 },
+	{ "stmpe2401", STMPE2401 },
+	{ "stmpe2403", STMPE2403 },
+	{ }
+};
+MODULE_DEVICE_TABLE(spi, stmpe_id);
+
+static struct spi_driver stmpe_spi_driver = {
+	.driver = {
+		.name	= "stmpe-spi",
+		.bus	= &spi_bus_type,
+		.owner	= THIS_MODULE,
+#ifdef CONFIG_PM
+		.pm	= &stmpe_dev_pm_ops,
+#endif
+	},
+	.probe		= stmpe_spi_probe,
+	.remove		= __devexit_p(stmpe_spi_remove),
+	.id_table	= stmpe_spi_id,
+};
+
+static int __init stmpe_init(void)
+{
+	return spi_register_driver(&stmpe_spi_driver);
+}
+subsys_initcall(stmpe_init);
+
+static void __exit stmpe_exit(void)
+{
+	spi_unregister_driver(&stmpe_spi_driver);
+}
+module_exit(stmpe_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("STMPE MFD SPI Interface Driver");
+MODULE_AUTHOR("Viresh Kumar <viresh.kumar@st.com>");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/stmpe.c b/ap/os/linux/linux-3.4.x/drivers/mfd/stmpe.c
new file mode 100644
index 0000000..2dd8d49
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/stmpe.c
@@ -0,0 +1,1138 @@
+/*
+ * ST Microelectronics MFD: stmpe's driver
+ *
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License, version 2
+ * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
+ */
+
+#include <linux/gpio.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <linux/mfd/core.h>
+#include "stmpe.h"
+
+static int __stmpe_enable(struct stmpe *stmpe, unsigned int blocks)
+{
+	return stmpe->variant->enable(stmpe, blocks, true);
+}
+
+static int __stmpe_disable(struct stmpe *stmpe, unsigned int blocks)
+{
+	return stmpe->variant->enable(stmpe, blocks, false);
+}
+
+static int __stmpe_reg_read(struct stmpe *stmpe, u8 reg)
+{
+	int ret;
+
+	ret = stmpe->ci->read_byte(stmpe, reg);
+	if (ret < 0)
+		dev_err(stmpe->dev, "failed to read reg %#x: %d\n", reg, ret);
+
+	dev_vdbg(stmpe->dev, "rd: reg %#x => data %#x\n", reg, ret);
+
+	return ret;
+}
+
+static int __stmpe_reg_write(struct stmpe *stmpe, u8 reg, u8 val)
+{
+	int ret;
+
+	dev_vdbg(stmpe->dev, "wr: reg %#x <= %#x\n", reg, val);
+
+	ret = stmpe->ci->write_byte(stmpe, reg, val);
+	if (ret < 0)
+		dev_err(stmpe->dev, "failed to write reg %#x: %d\n", reg, ret);
+
+	return ret;
+}
+
+static int __stmpe_set_bits(struct stmpe *stmpe, u8 reg, u8 mask, u8 val)
+{
+	int ret;
+
+	ret = __stmpe_reg_read(stmpe, reg);
+	if (ret < 0)
+		return ret;
+
+	ret &= ~mask;
+	ret |= val;
+
+	return __stmpe_reg_write(stmpe, reg, ret);
+}
+
+static int __stmpe_block_read(struct stmpe *stmpe, u8 reg, u8 length,
+			      u8 *values)
+{
+	int ret;
+
+	ret = stmpe->ci->read_block(stmpe, reg, length, values);
+	if (ret < 0)
+		dev_err(stmpe->dev, "failed to read regs %#x: %d\n", reg, ret);
+
+	dev_vdbg(stmpe->dev, "rd: reg %#x (%d) => ret %#x\n", reg, length, ret);
+	stmpe_dump_bytes("stmpe rd: ", values, length);
+
+	return ret;
+}
+
+static int __stmpe_block_write(struct stmpe *stmpe, u8 reg, u8 length,
+			const u8 *values)
+{
+	int ret;
+
+	dev_vdbg(stmpe->dev, "wr: regs %#x (%d)\n", reg, length);
+	stmpe_dump_bytes("stmpe wr: ", values, length);
+
+	ret = stmpe->ci->write_block(stmpe, reg, length, values);
+	if (ret < 0)
+		dev_err(stmpe->dev, "failed to write regs %#x: %d\n", reg, ret);
+
+	return ret;
+}
+
+/**
+ * stmpe_enable - enable blocks on an STMPE device
+ * @stmpe:	Device to work on
+ * @blocks:	Mask of blocks (enum stmpe_block values) to enable
+ */
+int stmpe_enable(struct stmpe *stmpe, unsigned int blocks)
+{
+	int ret;
+
+	mutex_lock(&stmpe->lock);
+	ret = __stmpe_enable(stmpe, blocks);
+	mutex_unlock(&stmpe->lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(stmpe_enable);
+
+/**
+ * stmpe_disable - disable blocks on an STMPE device
+ * @stmpe:	Device to work on
+ * @blocks:	Mask of blocks (enum stmpe_block values) to enable
+ */
+int stmpe_disable(struct stmpe *stmpe, unsigned int blocks)
+{
+	int ret;
+
+	mutex_lock(&stmpe->lock);
+	ret = __stmpe_disable(stmpe, blocks);
+	mutex_unlock(&stmpe->lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(stmpe_disable);
+
+/**
+ * stmpe_reg_read() - read a single STMPE register
+ * @stmpe:	Device to read from
+ * @reg:	Register to read
+ */
+int stmpe_reg_read(struct stmpe *stmpe, u8 reg)
+{
+	int ret;
+
+	mutex_lock(&stmpe->lock);
+	ret = __stmpe_reg_read(stmpe, reg);
+	mutex_unlock(&stmpe->lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(stmpe_reg_read);
+
+/**
+ * stmpe_reg_write() - write a single STMPE register
+ * @stmpe:	Device to write to
+ * @reg:	Register to write
+ * @val:	Value to write
+ */
+int stmpe_reg_write(struct stmpe *stmpe, u8 reg, u8 val)
+{
+	int ret;
+
+	mutex_lock(&stmpe->lock);
+	ret = __stmpe_reg_write(stmpe, reg, val);
+	mutex_unlock(&stmpe->lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(stmpe_reg_write);
+
+/**
+ * stmpe_set_bits() - set the value of a bitfield in a STMPE register
+ * @stmpe:	Device to write to
+ * @reg:	Register to write
+ * @mask:	Mask of bits to set
+ * @val:	Value to set
+ */
+int stmpe_set_bits(struct stmpe *stmpe, u8 reg, u8 mask, u8 val)
+{
+	int ret;
+
+	mutex_lock(&stmpe->lock);
+	ret = __stmpe_set_bits(stmpe, reg, mask, val);
+	mutex_unlock(&stmpe->lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(stmpe_set_bits);
+
+/**
+ * stmpe_block_read() - read multiple STMPE registers
+ * @stmpe:	Device to read from
+ * @reg:	First register
+ * @length:	Number of registers
+ * @values:	Buffer to write to
+ */
+int stmpe_block_read(struct stmpe *stmpe, u8 reg, u8 length, u8 *values)
+{
+	int ret;
+
+	mutex_lock(&stmpe->lock);
+	ret = __stmpe_block_read(stmpe, reg, length, values);
+	mutex_unlock(&stmpe->lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(stmpe_block_read);
+
+/**
+ * stmpe_block_write() - write multiple STMPE registers
+ * @stmpe:	Device to write to
+ * @reg:	First register
+ * @length:	Number of registers
+ * @values:	Values to write
+ */
+int stmpe_block_write(struct stmpe *stmpe, u8 reg, u8 length,
+		      const u8 *values)
+{
+	int ret;
+
+	mutex_lock(&stmpe->lock);
+	ret = __stmpe_block_write(stmpe, reg, length, values);
+	mutex_unlock(&stmpe->lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(stmpe_block_write);
+
+/**
+ * stmpe_set_altfunc()- set the alternate function for STMPE pins
+ * @stmpe:	Device to configure
+ * @pins:	Bitmask of pins to affect
+ * @block:	block to enable alternate functions for
+ *
+ * @pins is assumed to have a bit set for each of the bits whose alternate
+ * function is to be changed, numbered according to the GPIOXY numbers.
+ *
+ * If the GPIO module is not enabled, this function automatically enables it in
+ * order to perform the change.
+ */
+int stmpe_set_altfunc(struct stmpe *stmpe, u32 pins, enum stmpe_block block)
+{
+	struct stmpe_variant_info *variant = stmpe->variant;
+	u8 regaddr = stmpe->regs[STMPE_IDX_GPAFR_U_MSB];
+	int af_bits = variant->af_bits;
+	int numregs = DIV_ROUND_UP(stmpe->num_gpios * af_bits, 8);
+	int mask = (1 << af_bits) - 1;
+	u8 regs[numregs];
+	int af, afperreg, ret;
+
+	if (!variant->get_altfunc)
+		return 0;
+
+	afperreg = 8 / af_bits;
+	mutex_lock(&stmpe->lock);
+
+	ret = __stmpe_enable(stmpe, STMPE_BLOCK_GPIO);
+	if (ret < 0)
+		goto out;
+
+	ret = __stmpe_block_read(stmpe, regaddr, numregs, regs);
+	if (ret < 0)
+		goto out;
+
+	af = variant->get_altfunc(stmpe, block);
+
+	while (pins) {
+		int pin = __ffs(pins);
+		int regoffset = numregs - (pin / afperreg) - 1;
+		int pos = (pin % afperreg) * (8 / afperreg);
+
+		regs[regoffset] &= ~(mask << pos);
+		regs[regoffset] |= af << pos;
+
+		pins &= ~(1 << pin);
+	}
+
+	ret = __stmpe_block_write(stmpe, regaddr, numregs, regs);
+
+out:
+	mutex_unlock(&stmpe->lock);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(stmpe_set_altfunc);
+
+/*
+ * GPIO (all variants)
+ */
+
+static struct resource stmpe_gpio_resources[] = {
+	/* Start and end filled dynamically */
+	{
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct mfd_cell stmpe_gpio_cell = {
+	.name		= "stmpe-gpio",
+	.resources	= stmpe_gpio_resources,
+	.num_resources	= ARRAY_SIZE(stmpe_gpio_resources),
+};
+
+static struct mfd_cell stmpe_gpio_cell_noirq = {
+	.name		= "stmpe-gpio",
+	/* gpio cell resources consist of an irq only so no resources here */
+};
+
+/*
+ * Keypad (1601, 2401, 2403)
+ */
+
+static struct resource stmpe_keypad_resources[] = {
+	{
+		.name	= "KEYPAD",
+		.start	= 0,
+		.end	= 0,
+		.flags	= IORESOURCE_IRQ,
+	},
+	{
+		.name	= "KEYPAD_OVER",
+		.start	= 1,
+		.end	= 1,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct mfd_cell stmpe_keypad_cell = {
+	.name		= "stmpe-keypad",
+	.resources	= stmpe_keypad_resources,
+	.num_resources	= ARRAY_SIZE(stmpe_keypad_resources),
+};
+
+/*
+ * STMPE801
+ */
+static const u8 stmpe801_regs[] = {
+	[STMPE_IDX_CHIP_ID]	= STMPE801_REG_CHIP_ID,
+	[STMPE_IDX_ICR_LSB]	= STMPE801_REG_SYS_CTRL,
+	[STMPE_IDX_GPMR_LSB]	= STMPE801_REG_GPIO_MP_STA,
+	[STMPE_IDX_GPSR_LSB]	= STMPE801_REG_GPIO_SET_PIN,
+	[STMPE_IDX_GPCR_LSB]	= STMPE801_REG_GPIO_SET_PIN,
+	[STMPE_IDX_GPDR_LSB]	= STMPE801_REG_GPIO_DIR,
+	[STMPE_IDX_IEGPIOR_LSB] = STMPE801_REG_GPIO_INT_EN,
+	[STMPE_IDX_ISGPIOR_MSB] = STMPE801_REG_GPIO_INT_STA,
+
+};
+
+static struct stmpe_variant_block stmpe801_blocks[] = {
+	{
+		.cell	= &stmpe_gpio_cell,
+		.irq	= 0,
+		.block	= STMPE_BLOCK_GPIO,
+	},
+};
+
+static struct stmpe_variant_block stmpe801_blocks_noirq[] = {
+	{
+		.cell	= &stmpe_gpio_cell_noirq,
+		.block	= STMPE_BLOCK_GPIO,
+	},
+};
+
+static int stmpe801_enable(struct stmpe *stmpe, unsigned int blocks,
+			   bool enable)
+{
+	if (blocks & STMPE_BLOCK_GPIO)
+		return 0;
+	else
+		return -EINVAL;
+}
+
+static struct stmpe_variant_info stmpe801 = {
+	.name		= "stmpe801",
+	.id_val		= STMPE801_ID,
+	.id_mask	= 0xffff,
+	.num_gpios	= 8,
+	.regs		= stmpe801_regs,
+	.blocks		= stmpe801_blocks,
+	.num_blocks	= ARRAY_SIZE(stmpe801_blocks),
+	.num_irqs	= STMPE801_NR_INTERNAL_IRQS,
+	.enable		= stmpe801_enable,
+};
+
+static struct stmpe_variant_info stmpe801_noirq = {
+	.name		= "stmpe801",
+	.id_val		= STMPE801_ID,
+	.id_mask	= 0xffff,
+	.num_gpios	= 8,
+	.regs		= stmpe801_regs,
+	.blocks		= stmpe801_blocks_noirq,
+	.num_blocks	= ARRAY_SIZE(stmpe801_blocks_noirq),
+	.enable		= stmpe801_enable,
+};
+
+/*
+ * Touchscreen (STMPE811 or STMPE610)
+ */
+
+static struct resource stmpe_ts_resources[] = {
+	{
+		.name	= "TOUCH_DET",
+		.start	= 0,
+		.end	= 0,
+		.flags	= IORESOURCE_IRQ,
+	},
+	{
+		.name	= "FIFO_TH",
+		.start	= 1,
+		.end	= 1,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct mfd_cell stmpe_ts_cell = {
+	.name		= "stmpe-ts",
+	.resources	= stmpe_ts_resources,
+	.num_resources	= ARRAY_SIZE(stmpe_ts_resources),
+};
+
+/*
+ * STMPE811 or STMPE610
+ */
+
+static const u8 stmpe811_regs[] = {
+	[STMPE_IDX_CHIP_ID]	= STMPE811_REG_CHIP_ID,
+	[STMPE_IDX_ICR_LSB]	= STMPE811_REG_INT_CTRL,
+	[STMPE_IDX_IER_LSB]	= STMPE811_REG_INT_EN,
+	[STMPE_IDX_ISR_MSB]	= STMPE811_REG_INT_STA,
+	[STMPE_IDX_GPMR_LSB]	= STMPE811_REG_GPIO_MP_STA,
+	[STMPE_IDX_GPSR_LSB]	= STMPE811_REG_GPIO_SET_PIN,
+	[STMPE_IDX_GPCR_LSB]	= STMPE811_REG_GPIO_CLR_PIN,
+	[STMPE_IDX_GPDR_LSB]	= STMPE811_REG_GPIO_DIR,
+	[STMPE_IDX_GPRER_LSB]	= STMPE811_REG_GPIO_RE,
+	[STMPE_IDX_GPFER_LSB]	= STMPE811_REG_GPIO_FE,
+	[STMPE_IDX_GPAFR_U_MSB]	= STMPE811_REG_GPIO_AF,
+	[STMPE_IDX_IEGPIOR_LSB]	= STMPE811_REG_GPIO_INT_EN,
+	[STMPE_IDX_ISGPIOR_MSB]	= STMPE811_REG_GPIO_INT_STA,
+	[STMPE_IDX_GPEDR_MSB]	= STMPE811_REG_GPIO_ED,
+};
+
+static struct stmpe_variant_block stmpe811_blocks[] = {
+	{
+		.cell	= &stmpe_gpio_cell,
+		.irq	= STMPE811_IRQ_GPIOC,
+		.block	= STMPE_BLOCK_GPIO,
+	},
+	{
+		.cell	= &stmpe_ts_cell,
+		.irq	= STMPE811_IRQ_TOUCH_DET,
+		.block	= STMPE_BLOCK_TOUCHSCREEN,
+	},
+};
+
+static int stmpe811_enable(struct stmpe *stmpe, unsigned int blocks,
+			   bool enable)
+{
+	unsigned int mask = 0;
+
+	if (blocks & STMPE_BLOCK_GPIO)
+		mask |= STMPE811_SYS_CTRL2_GPIO_OFF;
+
+	if (blocks & STMPE_BLOCK_ADC)
+		mask |= STMPE811_SYS_CTRL2_ADC_OFF;
+
+	if (blocks & STMPE_BLOCK_TOUCHSCREEN)
+		mask |= STMPE811_SYS_CTRL2_TSC_OFF;
+
+	return __stmpe_set_bits(stmpe, STMPE811_REG_SYS_CTRL2, mask,
+				enable ? 0 : mask);
+}
+
+static int stmpe811_get_altfunc(struct stmpe *stmpe, enum stmpe_block block)
+{
+	/* 0 for touchscreen, 1 for GPIO */
+	return block != STMPE_BLOCK_TOUCHSCREEN;
+}
+
+static struct stmpe_variant_info stmpe811 = {
+	.name		= "stmpe811",
+	.id_val		= 0x0811,
+	.id_mask	= 0xffff,
+	.num_gpios	= 8,
+	.af_bits	= 1,
+	.regs		= stmpe811_regs,
+	.blocks		= stmpe811_blocks,
+	.num_blocks	= ARRAY_SIZE(stmpe811_blocks),
+	.num_irqs	= STMPE811_NR_INTERNAL_IRQS,
+	.enable		= stmpe811_enable,
+	.get_altfunc	= stmpe811_get_altfunc,
+};
+
+/* Similar to 811, except number of gpios */
+static struct stmpe_variant_info stmpe610 = {
+	.name		= "stmpe610",
+	.id_val		= 0x0811,
+	.id_mask	= 0xffff,
+	.num_gpios	= 6,
+	.af_bits	= 1,
+	.regs		= stmpe811_regs,
+	.blocks		= stmpe811_blocks,
+	.num_blocks	= ARRAY_SIZE(stmpe811_blocks),
+	.num_irqs	= STMPE811_NR_INTERNAL_IRQS,
+	.enable		= stmpe811_enable,
+	.get_altfunc	= stmpe811_get_altfunc,
+};
+
+/*
+ * STMPE1601
+ */
+
+static const u8 stmpe1601_regs[] = {
+	[STMPE_IDX_CHIP_ID]	= STMPE1601_REG_CHIP_ID,
+	[STMPE_IDX_ICR_LSB]	= STMPE1601_REG_ICR_LSB,
+	[STMPE_IDX_IER_LSB]	= STMPE1601_REG_IER_LSB,
+	[STMPE_IDX_ISR_MSB]	= STMPE1601_REG_ISR_MSB,
+	[STMPE_IDX_GPMR_LSB]	= STMPE1601_REG_GPIO_MP_LSB,
+	[STMPE_IDX_GPSR_LSB]	= STMPE1601_REG_GPIO_SET_LSB,
+	[STMPE_IDX_GPCR_LSB]	= STMPE1601_REG_GPIO_CLR_LSB,
+	[STMPE_IDX_GPDR_LSB]	= STMPE1601_REG_GPIO_SET_DIR_LSB,
+	[STMPE_IDX_GPRER_LSB]	= STMPE1601_REG_GPIO_RE_LSB,
+	[STMPE_IDX_GPFER_LSB]	= STMPE1601_REG_GPIO_FE_LSB,
+	[STMPE_IDX_GPAFR_U_MSB]	= STMPE1601_REG_GPIO_AF_U_MSB,
+	[STMPE_IDX_IEGPIOR_LSB]	= STMPE1601_REG_INT_EN_GPIO_MASK_LSB,
+	[STMPE_IDX_ISGPIOR_MSB]	= STMPE1601_REG_INT_STA_GPIO_MSB,
+	[STMPE_IDX_GPEDR_MSB]	= STMPE1601_REG_GPIO_ED_MSB,
+};
+
+static struct stmpe_variant_block stmpe1601_blocks[] = {
+	{
+		.cell	= &stmpe_gpio_cell,
+		.irq	= STMPE24XX_IRQ_GPIOC,
+		.block	= STMPE_BLOCK_GPIO,
+	},
+	{
+		.cell	= &stmpe_keypad_cell,
+		.irq	= STMPE24XX_IRQ_KEYPAD,
+		.block	= STMPE_BLOCK_KEYPAD,
+	},
+};
+
+/* supported autosleep timeout delay (in msecs) */
+static const int stmpe_autosleep_delay[] = {
+	4, 16, 32, 64, 128, 256, 512, 1024,
+};
+
+static int stmpe_round_timeout(int timeout)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(stmpe_autosleep_delay); i++) {
+		if (stmpe_autosleep_delay[i] >= timeout)
+			return i;
+	}
+
+	/*
+	 * requests for delays longer than supported should not return the
+	 * longest supported delay
+	 */
+	return -EINVAL;
+}
+
+static int stmpe_autosleep(struct stmpe *stmpe, int autosleep_timeout)
+{
+	int ret;
+
+	if (!stmpe->variant->enable_autosleep)
+		return -ENOSYS;
+
+	mutex_lock(&stmpe->lock);
+	ret = stmpe->variant->enable_autosleep(stmpe, autosleep_timeout);
+	mutex_unlock(&stmpe->lock);
+
+	return ret;
+}
+
+/*
+ * Both stmpe 1601/2403 support same layout for autosleep
+ */
+static int stmpe1601_autosleep(struct stmpe *stmpe,
+		int autosleep_timeout)
+{
+	int ret, timeout;
+
+	/* choose the best available timeout */
+	timeout = stmpe_round_timeout(autosleep_timeout);
+	if (timeout < 0) {
+		dev_err(stmpe->dev, "invalid timeout\n");
+		return timeout;
+	}
+
+	ret = __stmpe_set_bits(stmpe, STMPE1601_REG_SYS_CTRL2,
+			STMPE1601_AUTOSLEEP_TIMEOUT_MASK,
+			timeout);
+	if (ret < 0)
+		return ret;
+
+	return __stmpe_set_bits(stmpe, STMPE1601_REG_SYS_CTRL2,
+			STPME1601_AUTOSLEEP_ENABLE,
+			STPME1601_AUTOSLEEP_ENABLE);
+}
+
+static int stmpe1601_enable(struct stmpe *stmpe, unsigned int blocks,
+			    bool enable)
+{
+	unsigned int mask = 0;
+
+	if (blocks & STMPE_BLOCK_GPIO)
+		mask |= STMPE1601_SYS_CTRL_ENABLE_GPIO;
+
+	if (blocks & STMPE_BLOCK_KEYPAD)
+		mask |= STMPE1601_SYS_CTRL_ENABLE_KPC;
+
+	return __stmpe_set_bits(stmpe, STMPE1601_REG_SYS_CTRL, mask,
+				enable ? mask : 0);
+}
+
+static int stmpe1601_get_altfunc(struct stmpe *stmpe, enum stmpe_block block)
+{
+	switch (block) {
+	case STMPE_BLOCK_PWM:
+		return 2;
+
+	case STMPE_BLOCK_KEYPAD:
+		return 1;
+
+	case STMPE_BLOCK_GPIO:
+	default:
+		return 0;
+	}
+}
+
+static struct stmpe_variant_info stmpe1601 = {
+	.name		= "stmpe1601",
+	.id_val		= 0x0210,
+	.id_mask	= 0xfff0,	/* at least 0x0210 and 0x0212 */
+	.num_gpios	= 16,
+	.af_bits	= 2,
+	.regs		= stmpe1601_regs,
+	.blocks		= stmpe1601_blocks,
+	.num_blocks	= ARRAY_SIZE(stmpe1601_blocks),
+	.num_irqs	= STMPE1601_NR_INTERNAL_IRQS,
+	.enable		= stmpe1601_enable,
+	.get_altfunc	= stmpe1601_get_altfunc,
+	.enable_autosleep	= stmpe1601_autosleep,
+};
+
+/*
+ * STMPE24XX
+ */
+
+static const u8 stmpe24xx_regs[] = {
+	[STMPE_IDX_CHIP_ID]	= STMPE24XX_REG_CHIP_ID,
+	[STMPE_IDX_ICR_LSB]	= STMPE24XX_REG_ICR_LSB,
+	[STMPE_IDX_IER_LSB]	= STMPE24XX_REG_IER_LSB,
+	[STMPE_IDX_ISR_MSB]	= STMPE24XX_REG_ISR_MSB,
+	[STMPE_IDX_GPMR_LSB]	= STMPE24XX_REG_GPMR_LSB,
+	[STMPE_IDX_GPSR_LSB]	= STMPE24XX_REG_GPSR_LSB,
+	[STMPE_IDX_GPCR_LSB]	= STMPE24XX_REG_GPCR_LSB,
+	[STMPE_IDX_GPDR_LSB]	= STMPE24XX_REG_GPDR_LSB,
+	[STMPE_IDX_GPRER_LSB]	= STMPE24XX_REG_GPRER_LSB,
+	[STMPE_IDX_GPFER_LSB]	= STMPE24XX_REG_GPFER_LSB,
+	[STMPE_IDX_GPAFR_U_MSB]	= STMPE24XX_REG_GPAFR_U_MSB,
+	[STMPE_IDX_IEGPIOR_LSB]	= STMPE24XX_REG_IEGPIOR_LSB,
+	[STMPE_IDX_ISGPIOR_MSB]	= STMPE24XX_REG_ISGPIOR_MSB,
+	[STMPE_IDX_GPEDR_MSB]	= STMPE24XX_REG_GPEDR_MSB,
+};
+
+static struct stmpe_variant_block stmpe24xx_blocks[] = {
+	{
+		.cell	= &stmpe_gpio_cell,
+		.irq	= STMPE24XX_IRQ_GPIOC,
+		.block	= STMPE_BLOCK_GPIO,
+	},
+	{
+		.cell	= &stmpe_keypad_cell,
+		.irq	= STMPE24XX_IRQ_KEYPAD,
+		.block	= STMPE_BLOCK_KEYPAD,
+	},
+};
+
+static int stmpe24xx_enable(struct stmpe *stmpe, unsigned int blocks,
+			    bool enable)
+{
+	unsigned int mask = 0;
+
+	if (blocks & STMPE_BLOCK_GPIO)
+		mask |= STMPE24XX_SYS_CTRL_ENABLE_GPIO;
+
+	if (blocks & STMPE_BLOCK_KEYPAD)
+		mask |= STMPE24XX_SYS_CTRL_ENABLE_KPC;
+
+	return __stmpe_set_bits(stmpe, STMPE24XX_REG_SYS_CTRL, mask,
+				enable ? mask : 0);
+}
+
+static int stmpe24xx_get_altfunc(struct stmpe *stmpe, enum stmpe_block block)
+{
+	switch (block) {
+	case STMPE_BLOCK_ROTATOR:
+		return 2;
+
+	case STMPE_BLOCK_KEYPAD:
+		return 1;
+
+	case STMPE_BLOCK_GPIO:
+	default:
+		return 0;
+	}
+}
+
+static struct stmpe_variant_info stmpe2401 = {
+	.name		= "stmpe2401",
+	.id_val		= 0x0101,
+	.id_mask	= 0xffff,
+	.num_gpios	= 24,
+	.af_bits	= 2,
+	.regs		= stmpe24xx_regs,
+	.blocks		= stmpe24xx_blocks,
+	.num_blocks	= ARRAY_SIZE(stmpe24xx_blocks),
+	.num_irqs	= STMPE24XX_NR_INTERNAL_IRQS,
+	.enable		= stmpe24xx_enable,
+	.get_altfunc	= stmpe24xx_get_altfunc,
+};
+
+static struct stmpe_variant_info stmpe2403 = {
+	.name		= "stmpe2403",
+	.id_val		= 0x0120,
+	.id_mask	= 0xffff,
+	.num_gpios	= 24,
+	.af_bits	= 2,
+	.regs		= stmpe24xx_regs,
+	.blocks		= stmpe24xx_blocks,
+	.num_blocks	= ARRAY_SIZE(stmpe24xx_blocks),
+	.num_irqs	= STMPE24XX_NR_INTERNAL_IRQS,
+	.enable		= stmpe24xx_enable,
+	.get_altfunc	= stmpe24xx_get_altfunc,
+	.enable_autosleep	= stmpe1601_autosleep, /* same as stmpe1601 */
+};
+
+static struct stmpe_variant_info *stmpe_variant_info[STMPE_NBR_PARTS] = {
+	[STMPE610]	= &stmpe610,
+	[STMPE801]	= &stmpe801,
+	[STMPE811]	= &stmpe811,
+	[STMPE1601]	= &stmpe1601,
+	[STMPE2401]	= &stmpe2401,
+	[STMPE2403]	= &stmpe2403,
+};
+
+/*
+ * These devices can be connected in a 'no-irq' configuration - the irq pin
+ * is not used and the device cannot interrupt the CPU. Here we only list
+ * devices which support this configuration - the driver will fail probing
+ * for any devices not listed here which are configured in this way.
+ */
+static struct stmpe_variant_info *stmpe_noirq_variant_info[STMPE_NBR_PARTS] = {
+	[STMPE801]	= &stmpe801_noirq,
+};
+
+static irqreturn_t stmpe_irq(int irq, void *data)
+{
+	struct stmpe *stmpe = data;
+	struct stmpe_variant_info *variant = stmpe->variant;
+	int num = DIV_ROUND_UP(variant->num_irqs, 8);
+	u8 israddr = stmpe->regs[STMPE_IDX_ISR_MSB];
+	u8 isr[num];
+	int ret;
+	int i;
+
+	if (variant->id_val == STMPE801_ID) {
+		handle_nested_irq(stmpe->irq_base);
+		return IRQ_HANDLED;
+	}
+
+	ret = stmpe_block_read(stmpe, israddr, num, isr);
+	if (ret < 0)
+		return IRQ_NONE;
+
+	for (i = 0; i < num; i++) {
+		int bank = num - i - 1;
+		u8 status = isr[i];
+		u8 clear;
+
+		status &= stmpe->ier[bank];
+		if (!status)
+			continue;
+
+		clear = status;
+		while (status) {
+			int bit = __ffs(status);
+			int line = bank * 8 + bit;
+
+			handle_nested_irq(stmpe->irq_base + line);
+			status &= ~(1 << bit);
+		}
+
+		stmpe_reg_write(stmpe, israddr + i, clear);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void stmpe_irq_lock(struct irq_data *data)
+{
+	struct stmpe *stmpe = irq_data_get_irq_chip_data(data);
+
+	mutex_lock(&stmpe->irq_lock);
+}
+
+static void stmpe_irq_sync_unlock(struct irq_data *data)
+{
+	struct stmpe *stmpe = irq_data_get_irq_chip_data(data);
+	struct stmpe_variant_info *variant = stmpe->variant;
+	int num = DIV_ROUND_UP(variant->num_irqs, 8);
+	int i;
+
+	for (i = 0; i < num; i++) {
+		u8 new = stmpe->ier[i];
+		u8 old = stmpe->oldier[i];
+
+		if (new == old)
+			continue;
+
+		stmpe->oldier[i] = new;
+		stmpe_reg_write(stmpe, stmpe->regs[STMPE_IDX_IER_LSB] - i, new);
+	}
+
+	mutex_unlock(&stmpe->irq_lock);
+}
+
+static void stmpe_irq_mask(struct irq_data *data)
+{
+	struct stmpe *stmpe = irq_data_get_irq_chip_data(data);
+	int offset = data->irq - stmpe->irq_base;
+	int regoffset = offset / 8;
+	int mask = 1 << (offset % 8);
+
+	stmpe->ier[regoffset] &= ~mask;
+}
+
+static void stmpe_irq_unmask(struct irq_data *data)
+{
+	struct stmpe *stmpe = irq_data_get_irq_chip_data(data);
+	int offset = data->irq - stmpe->irq_base;
+	int regoffset = offset / 8;
+	int mask = 1 << (offset % 8);
+
+	stmpe->ier[regoffset] |= mask;
+}
+
+static struct irq_chip stmpe_irq_chip = {
+	.name			= "stmpe",
+	.irq_bus_lock		= stmpe_irq_lock,
+	.irq_bus_sync_unlock	= stmpe_irq_sync_unlock,
+	.irq_mask		= stmpe_irq_mask,
+	.irq_unmask		= stmpe_irq_unmask,
+};
+
+static int __devinit stmpe_irq_init(struct stmpe *stmpe)
+{
+	struct irq_chip *chip = NULL;
+	int num_irqs = stmpe->variant->num_irqs;
+	int base = stmpe->irq_base;
+	int irq;
+
+	if (stmpe->variant->id_val != STMPE801_ID)
+		chip = &stmpe_irq_chip;
+
+	for (irq = base; irq < base + num_irqs; irq++) {
+		irq_set_chip_data(irq, stmpe);
+		irq_set_chip_and_handler(irq, chip, handle_edge_irq);
+		irq_set_nested_thread(irq, 1);
+#ifdef CONFIG_ARM
+		set_irq_flags(irq, IRQF_VALID);
+#else
+		irq_set_noprobe(irq);
+#endif
+	}
+
+	return 0;
+}
+
+static void stmpe_irq_remove(struct stmpe *stmpe)
+{
+	int num_irqs = stmpe->variant->num_irqs;
+	int base = stmpe->irq_base;
+	int irq;
+
+	for (irq = base; irq < base + num_irqs; irq++) {
+#ifdef CONFIG_ARM
+		set_irq_flags(irq, 0);
+#endif
+		irq_set_chip_and_handler(irq, NULL, NULL);
+		irq_set_chip_data(irq, NULL);
+	}
+}
+
+static int __devinit stmpe_chip_init(struct stmpe *stmpe)
+{
+	unsigned int irq_trigger = stmpe->pdata->irq_trigger;
+	int autosleep_timeout = stmpe->pdata->autosleep_timeout;
+	struct stmpe_variant_info *variant = stmpe->variant;
+	u8 icr = 0;
+	unsigned int id;
+	u8 data[2];
+	int ret;
+
+	ret = stmpe_block_read(stmpe, stmpe->regs[STMPE_IDX_CHIP_ID],
+			       ARRAY_SIZE(data), data);
+	if (ret < 0)
+		return ret;
+
+	id = (data[0] << 8) | data[1];
+	if ((id & variant->id_mask) != variant->id_val) {
+		dev_err(stmpe->dev, "unknown chip id: %#x\n", id);
+		return -EINVAL;
+	}
+
+	dev_info(stmpe->dev, "%s detected, chip id: %#x\n", variant->name, id);
+
+	/* Disable all modules -- subdrivers should enable what they need. */
+	ret = stmpe_disable(stmpe, ~0);
+	if (ret)
+		return ret;
+
+	if (stmpe->irq >= 0) {
+		if (id == STMPE801_ID)
+			icr = STMPE801_REG_SYS_CTRL_INT_EN;
+		else
+			icr = STMPE_ICR_LSB_GIM;
+
+		/* STMPE801 doesn't support Edge interrupts */
+		if (id != STMPE801_ID) {
+			if (irq_trigger == IRQF_TRIGGER_FALLING ||
+					irq_trigger == IRQF_TRIGGER_RISING)
+				icr |= STMPE_ICR_LSB_EDGE;
+		}
+
+		if (irq_trigger == IRQF_TRIGGER_RISING ||
+				irq_trigger == IRQF_TRIGGER_HIGH) {
+			if (id == STMPE801_ID)
+				icr |= STMPE801_REG_SYS_CTRL_INT_HI;
+			else
+				icr |= STMPE_ICR_LSB_HIGH;
+		}
+
+		if (stmpe->pdata->irq_invert_polarity) {
+			if (id == STMPE801_ID)
+				icr ^= STMPE801_REG_SYS_CTRL_INT_HI;
+			else
+				icr ^= STMPE_ICR_LSB_HIGH;
+		}
+	}
+
+	if (stmpe->pdata->autosleep) {
+		ret = stmpe_autosleep(stmpe, autosleep_timeout);
+		if (ret)
+			return ret;
+	}
+
+	return stmpe_reg_write(stmpe, stmpe->regs[STMPE_IDX_ICR_LSB], icr);
+}
+
+static int __devinit stmpe_add_device(struct stmpe *stmpe,
+				      struct mfd_cell *cell, int irq)
+{
+	return mfd_add_devices(stmpe->dev, stmpe->pdata->id, cell, 1,
+			       NULL, stmpe->irq_base + irq);
+}
+
+static int __devinit stmpe_devices_init(struct stmpe *stmpe)
+{
+	struct stmpe_variant_info *variant = stmpe->variant;
+	unsigned int platform_blocks = stmpe->pdata->blocks;
+	int ret = -EINVAL;
+	int i;
+
+	for (i = 0; i < variant->num_blocks; i++) {
+		struct stmpe_variant_block *block = &variant->blocks[i];
+
+		if (!(platform_blocks & block->block))
+			continue;
+
+		platform_blocks &= ~block->block;
+		ret = stmpe_add_device(stmpe, block->cell, block->irq);
+		if (ret)
+			return ret;
+	}
+
+	if (platform_blocks)
+		dev_warn(stmpe->dev,
+			 "platform wants blocks (%#x) not present on variant",
+			 platform_blocks);
+
+	return ret;
+}
+
+/* Called from client specific probe routines */
+int __devinit stmpe_probe(struct stmpe_client_info *ci, int partnum)
+{
+	struct stmpe_platform_data *pdata = dev_get_platdata(ci->dev);
+	struct stmpe *stmpe;
+	int ret;
+
+	if (!pdata)
+		return -EINVAL;
+
+	stmpe = kzalloc(sizeof(struct stmpe), GFP_KERNEL);
+	if (!stmpe)
+		return -ENOMEM;
+
+	mutex_init(&stmpe->irq_lock);
+	mutex_init(&stmpe->lock);
+
+	stmpe->dev = ci->dev;
+	stmpe->client = ci->client;
+	stmpe->pdata = pdata;
+	stmpe->irq_base = pdata->irq_base;
+	stmpe->ci = ci;
+	stmpe->partnum = partnum;
+	stmpe->variant = stmpe_variant_info[partnum];
+	stmpe->regs = stmpe->variant->regs;
+	stmpe->num_gpios = stmpe->variant->num_gpios;
+	dev_set_drvdata(stmpe->dev, stmpe);
+
+	if (ci->init)
+		ci->init(stmpe);
+
+	if (pdata->irq_over_gpio) {
+		ret = gpio_request_one(pdata->irq_gpio, GPIOF_DIR_IN, "stmpe");
+		if (ret) {
+			dev_err(stmpe->dev, "failed to request IRQ GPIO: %d\n",
+					ret);
+			goto out_free;
+		}
+
+		stmpe->irq = gpio_to_irq(pdata->irq_gpio);
+	} else {
+		stmpe->irq = ci->irq;
+	}
+
+	if (stmpe->irq < 0) {
+		/* use alternate variant info for no-irq mode, if supported */
+		dev_info(stmpe->dev,
+			"%s configured in no-irq mode by platform data\n",
+			stmpe->variant->name);
+		if (!stmpe_noirq_variant_info[stmpe->partnum]) {
+			dev_err(stmpe->dev,
+				"%s does not support no-irq mode!\n",
+				stmpe->variant->name);
+			ret = -ENODEV;
+			goto free_gpio;
+		}
+		stmpe->variant = stmpe_noirq_variant_info[stmpe->partnum];
+	}
+
+	ret = stmpe_chip_init(stmpe);
+	if (ret)
+		goto free_gpio;
+
+	if (stmpe->irq >= 0) {
+		ret = stmpe_irq_init(stmpe);
+		if (ret)
+			goto free_gpio;
+
+		ret = request_threaded_irq(stmpe->irq, NULL, stmpe_irq,
+				pdata->irq_trigger | IRQF_ONESHOT,
+				"stmpe", stmpe);
+		if (ret) {
+			dev_err(stmpe->dev, "failed to request IRQ: %d\n",
+					ret);
+			goto out_removeirq;
+		}
+	}
+
+	ret = stmpe_devices_init(stmpe);
+	if (ret) {
+		dev_err(stmpe->dev, "failed to add children\n");
+		goto out_removedevs;
+	}
+
+	return 0;
+
+out_removedevs:
+	mfd_remove_devices(stmpe->dev);
+	if (stmpe->irq >= 0)
+		free_irq(stmpe->irq, stmpe);
+out_removeirq:
+	if (stmpe->irq >= 0)
+		stmpe_irq_remove(stmpe);
+free_gpio:
+	if (pdata->irq_over_gpio)
+		gpio_free(pdata->irq_gpio);
+out_free:
+	kfree(stmpe);
+	return ret;
+}
+
+int stmpe_remove(struct stmpe *stmpe)
+{
+	mfd_remove_devices(stmpe->dev);
+
+	if (stmpe->irq >= 0) {
+		free_irq(stmpe->irq, stmpe);
+		stmpe_irq_remove(stmpe);
+	}
+
+	if (stmpe->pdata->irq_over_gpio)
+		gpio_free(stmpe->pdata->irq_gpio);
+
+	kfree(stmpe);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int stmpe_suspend(struct device *dev)
+{
+	struct stmpe *stmpe = dev_get_drvdata(dev);
+
+	if (stmpe->irq >= 0 && device_may_wakeup(dev))
+		enable_irq_wake(stmpe->irq);
+
+	return 0;
+}
+
+static int stmpe_resume(struct device *dev)
+{
+	struct stmpe *stmpe = dev_get_drvdata(dev);
+
+	if (stmpe->irq >= 0 && device_may_wakeup(dev))
+		disable_irq_wake(stmpe->irq);
+
+	return 0;
+}
+
+const struct dev_pm_ops stmpe_dev_pm_ops = {
+	.suspend	= stmpe_suspend,
+	.resume		= stmpe_resume,
+};
+#endif
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/stmpe.h b/ap/os/linux/linux-3.4.x/drivers/mfd/stmpe.h
new file mode 100644
index 0000000..7b8e13f
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/stmpe.h
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License, version 2
+ * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
+ */
+
+#ifndef __STMPE_H
+#define __STMPE_H
+
+#include <linux/device.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/stmpe.h>
+#include <linux/printk.h>
+#include <linux/types.h>
+
+extern const struct dev_pm_ops stmpe_dev_pm_ops;
+
+#ifdef STMPE_DUMP_BYTES
+static inline void stmpe_dump_bytes(const char *str, const void *buf,
+				    size_t len)
+{
+	print_hex_dump_bytes(str, DUMP_PREFIX_OFFSET, buf, len);
+}
+#else
+static inline void stmpe_dump_bytes(const char *str, const void *buf,
+				    size_t len)
+{
+}
+#endif
+
+/**
+ * struct stmpe_variant_block - information about block
+ * @cell:	base mfd cell
+ * @irq:	interrupt number to be added to each IORESOURCE_IRQ
+ *		in the cell
+ * @block:	block id; used for identification with platform data and for
+ *		enable and altfunc callbacks
+ */
+struct stmpe_variant_block {
+	struct mfd_cell		*cell;
+	int			irq;
+	enum stmpe_block	block;
+};
+
+/**
+ * struct stmpe_variant_info - variant-specific information
+ * @name:	part name
+ * @id_val:	content of CHIPID register
+ * @id_mask:	bits valid in CHIPID register for comparison with id_val
+ * @num_gpios:	number of GPIOS
+ * @af_bits:	number of bits used to specify the alternate function
+ * @regs: variant specific registers.
+ * @blocks:	list of blocks present on this device
+ * @num_blocks:	number of blocks present on this device
+ * @num_irqs:	number of internal IRQs available on this device
+ * @enable:	callback to enable the specified blocks.
+ *		Called with the I/O lock held.
+ * @get_altfunc: callback to get the alternate function number for the
+ *		 specific block
+ * @enable_autosleep: callback to configure autosleep with specified timeout
+ */
+struct stmpe_variant_info {
+	const char *name;
+	u16 id_val;
+	u16 id_mask;
+	int num_gpios;
+	int af_bits;
+	const u8 *regs;
+	struct stmpe_variant_block *blocks;
+	int num_blocks;
+	int num_irqs;
+	int (*enable)(struct stmpe *stmpe, unsigned int blocks, bool enable);
+	int (*get_altfunc)(struct stmpe *stmpe, enum stmpe_block block);
+	int (*enable_autosleep)(struct stmpe *stmpe, int autosleep_timeout);
+};
+
+/**
+ * struct stmpe_client_info - i2c or spi specific routines/info
+ * @data: client specific data
+ * @read_byte: read single byte
+ * @write_byte: write single byte
+ * @read_block: read block or multiple bytes
+ * @write_block: write block or multiple bytes
+ * @init: client init routine, called during probe
+ */
+struct stmpe_client_info {
+	void *data;
+	int irq;
+	void *client;
+	struct device *dev;
+	int (*read_byte)(struct stmpe *stmpe, u8 reg);
+	int (*write_byte)(struct stmpe *stmpe, u8 reg, u8 val);
+	int (*read_block)(struct stmpe *stmpe, u8 reg, u8 len, u8 *values);
+	int (*write_block)(struct stmpe *stmpe, u8 reg, u8 len,
+			const u8 *values);
+	void (*init)(struct stmpe *stmpe);
+};
+
+int stmpe_probe(struct stmpe_client_info *ci, int partnum);
+int stmpe_remove(struct stmpe *stmpe);
+
+#define STMPE_ICR_LSB_HIGH	(1 << 2)
+#define STMPE_ICR_LSB_EDGE	(1 << 1)
+#define STMPE_ICR_LSB_GIM	(1 << 0)
+
+/*
+ * STMPE801
+ */
+#define STMPE801_ID			0x0108
+#define STMPE801_NR_INTERNAL_IRQS	1
+
+#define STMPE801_REG_CHIP_ID		0x00
+#define STMPE801_REG_VERSION_ID		0x02
+#define STMPE801_REG_SYS_CTRL		0x04
+#define STMPE801_REG_GPIO_INT_EN	0x08
+#define STMPE801_REG_GPIO_INT_STA	0x09
+#define STMPE801_REG_GPIO_MP_STA	0x10
+#define STMPE801_REG_GPIO_SET_PIN	0x11
+#define STMPE801_REG_GPIO_DIR		0x12
+
+#define STMPE801_REG_SYS_CTRL_RESET	(1 << 7)
+#define STMPE801_REG_SYS_CTRL_INT_EN	(1 << 2)
+#define STMPE801_REG_SYS_CTRL_INT_HI	(1 << 0)
+
+/*
+ * STMPE811
+ */
+
+#define STMPE811_IRQ_TOUCH_DET		0
+#define STMPE811_IRQ_FIFO_TH		1
+#define STMPE811_IRQ_FIFO_OFLOW		2
+#define STMPE811_IRQ_FIFO_FULL		3
+#define STMPE811_IRQ_FIFO_EMPTY		4
+#define STMPE811_IRQ_TEMP_SENS		5
+#define STMPE811_IRQ_ADC		6
+#define STMPE811_IRQ_GPIOC		7
+#define STMPE811_NR_INTERNAL_IRQS	8
+
+#define STMPE811_REG_CHIP_ID		0x00
+#define STMPE811_REG_SYS_CTRL2		0x04
+#define STMPE811_REG_SPI_CFG		0x08
+#define STMPE811_REG_INT_CTRL		0x09
+#define STMPE811_REG_INT_EN		0x0A
+#define STMPE811_REG_INT_STA		0x0B
+#define STMPE811_REG_GPIO_INT_EN	0x0C
+#define STMPE811_REG_GPIO_INT_STA	0x0D
+#define STMPE811_REG_GPIO_SET_PIN	0x10
+#define STMPE811_REG_GPIO_CLR_PIN	0x11
+#define STMPE811_REG_GPIO_MP_STA	0x12
+#define STMPE811_REG_GPIO_DIR		0x13
+#define STMPE811_REG_GPIO_ED		0x14
+#define STMPE811_REG_GPIO_RE		0x15
+#define STMPE811_REG_GPIO_FE		0x16
+#define STMPE811_REG_GPIO_AF		0x17
+
+#define STMPE811_SYS_CTRL2_ADC_OFF	(1 << 0)
+#define STMPE811_SYS_CTRL2_TSC_OFF	(1 << 1)
+#define STMPE811_SYS_CTRL2_GPIO_OFF	(1 << 2)
+#define STMPE811_SYS_CTRL2_TS_OFF	(1 << 3)
+
+/*
+ * STMPE1601
+ */
+
+#define STMPE1601_IRQ_GPIOC		8
+#define STMPE1601_IRQ_PWM3		7
+#define STMPE1601_IRQ_PWM2		6
+#define STMPE1601_IRQ_PWM1		5
+#define STMPE1601_IRQ_PWM0		4
+#define STMPE1601_IRQ_KEYPAD_OVER	2
+#define STMPE1601_IRQ_KEYPAD		1
+#define STMPE1601_IRQ_WAKEUP		0
+#define STMPE1601_NR_INTERNAL_IRQS	9
+
+#define STMPE1601_REG_SYS_CTRL			0x02
+#define STMPE1601_REG_SYS_CTRL2			0x03
+#define STMPE1601_REG_ICR_LSB			0x11
+#define STMPE1601_REG_IER_LSB			0x13
+#define STMPE1601_REG_ISR_MSB			0x14
+#define STMPE1601_REG_CHIP_ID			0x80
+#define STMPE1601_REG_INT_EN_GPIO_MASK_LSB	0x17
+#define STMPE1601_REG_INT_STA_GPIO_MSB		0x18
+#define STMPE1601_REG_GPIO_MP_LSB		0x87
+#define STMPE1601_REG_GPIO_SET_LSB		0x83
+#define STMPE1601_REG_GPIO_CLR_LSB		0x85
+#define STMPE1601_REG_GPIO_SET_DIR_LSB		0x89
+#define STMPE1601_REG_GPIO_ED_MSB		0x8A
+#define STMPE1601_REG_GPIO_RE_LSB		0x8D
+#define STMPE1601_REG_GPIO_FE_LSB		0x8F
+#define STMPE1601_REG_GPIO_AF_U_MSB		0x92
+
+#define STMPE1601_SYS_CTRL_ENABLE_GPIO		(1 << 3)
+#define STMPE1601_SYS_CTRL_ENABLE_KPC		(1 << 1)
+#define STMPE1601_SYSCON_ENABLE_SPWM		(1 << 0)
+
+/* The 1601/2403 share the same masks */
+#define STMPE1601_AUTOSLEEP_TIMEOUT_MASK	(0x7)
+#define STPME1601_AUTOSLEEP_ENABLE		(1 << 3)
+
+/*
+ * STMPE24xx
+ */
+
+#define STMPE24XX_IRQ_GPIOC		8
+#define STMPE24XX_IRQ_PWM2		7
+#define STMPE24XX_IRQ_PWM1		6
+#define STMPE24XX_IRQ_PWM0		5
+#define STMPE24XX_IRQ_ROT_OVER		4
+#define STMPE24XX_IRQ_ROT		3
+#define STMPE24XX_IRQ_KEYPAD_OVER	2
+#define STMPE24XX_IRQ_KEYPAD		1
+#define STMPE24XX_IRQ_WAKEUP		0
+#define STMPE24XX_NR_INTERNAL_IRQS	9
+
+#define STMPE24XX_REG_SYS_CTRL		0x02
+#define STMPE24XX_REG_ICR_LSB		0x11
+#define STMPE24XX_REG_IER_LSB		0x13
+#define STMPE24XX_REG_ISR_MSB		0x14
+#define STMPE24XX_REG_CHIP_ID		0x80
+#define STMPE24XX_REG_IEGPIOR_LSB	0x18
+#define STMPE24XX_REG_ISGPIOR_MSB	0x19
+#define STMPE24XX_REG_GPMR_LSB		0xA5
+#define STMPE24XX_REG_GPSR_LSB		0x85
+#define STMPE24XX_REG_GPCR_LSB		0x88
+#define STMPE24XX_REG_GPDR_LSB		0x8B
+#define STMPE24XX_REG_GPEDR_MSB		0x8C
+#define STMPE24XX_REG_GPRER_LSB		0x91
+#define STMPE24XX_REG_GPFER_LSB		0x94
+#define STMPE24XX_REG_GPAFR_U_MSB	0x9B
+
+#define STMPE24XX_SYS_CTRL_ENABLE_GPIO		(1 << 3)
+#define STMPE24XX_SYSCON_ENABLE_PWM		(1 << 2)
+#define STMPE24XX_SYS_CTRL_ENABLE_KPC		(1 << 1)
+#define STMPE24XX_SYSCON_ENABLE_ROT		(1 << 0)
+
+#endif
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/t7l66xb.c b/ap/os/linux/linux-3.4.x/drivers/mfd/t7l66xb.c
new file mode 100644
index 0000000..2d9e879
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/t7l66xb.c
@@ -0,0 +1,450 @@
+/*
+ *
+ * Toshiba T7L66XB core mfd support
+ *
+ * Copyright (c) 2005, 2007, 2008 Ian Molton
+ * Copyright (c) 2008 Dmitry Baryshkov
+ *
+ * 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.
+ *
+ * T7L66 features:
+ *
+ * Supported in this driver:
+ * SD/MMC
+ * SM/NAND flash controller
+ *
+ * As yet not supported
+ * GPIO interface (on NAND pins)
+ * Serial interface
+ * TFT 'interface converter'
+ * PCMCIA interface logic
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/tmio.h>
+#include <linux/mfd/t7l66xb.h>
+
+enum {
+	T7L66XB_CELL_NAND,
+	T7L66XB_CELL_MMC,
+};
+
+static const struct resource t7l66xb_mmc_resources[] = {
+	{
+		.start = 0x800,
+		.end	= 0x9ff,
+		.flags = IORESOURCE_MEM,
+	},
+	{
+		.start = IRQ_T7L66XB_MMC,
+		.end	= IRQ_T7L66XB_MMC,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+#define SCR_REVID	0x08		/* b Revision ID	*/
+#define SCR_IMR		0x42		/* b Interrupt Mask	*/
+#define SCR_DEV_CTL	0xe0		/* b Device control	*/
+#define SCR_ISR		0xe1		/* b Interrupt Status	*/
+#define SCR_GPO_OC	0xf0		/* b GPO output control	*/
+#define SCR_GPO_OS	0xf1		/* b GPO output enable	*/
+#define SCR_GPI_S	0xf2		/* w GPI status		*/
+#define SCR_APDC	0xf8		/* b Active pullup down ctrl */
+
+#define SCR_DEV_CTL_USB		BIT(0)	/* USB enable		*/
+#define SCR_DEV_CTL_MMC		BIT(1)	/* MMC enable		*/
+
+/*--------------------------------------------------------------------------*/
+
+struct t7l66xb {
+	void __iomem		*scr;
+	/* Lock to protect registers requiring read/modify/write ops. */
+	spinlock_t		lock;
+
+	struct resource		rscr;
+	struct clk		*clk48m;
+	struct clk		*clk32k;
+	int			irq;
+	int			irq_base;
+};
+
+/*--------------------------------------------------------------------------*/
+
+static int t7l66xb_mmc_enable(struct platform_device *mmc)
+{
+	struct platform_device *dev = to_platform_device(mmc->dev.parent);
+	struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
+	unsigned long flags;
+	u8 dev_ctl;
+
+	clk_enable(t7l66xb->clk32k);
+
+	spin_lock_irqsave(&t7l66xb->lock, flags);
+
+	dev_ctl = tmio_ioread8(t7l66xb->scr + SCR_DEV_CTL);
+	dev_ctl |= SCR_DEV_CTL_MMC;
+	tmio_iowrite8(dev_ctl, t7l66xb->scr + SCR_DEV_CTL);
+
+	spin_unlock_irqrestore(&t7l66xb->lock, flags);
+
+	tmio_core_mmc_enable(t7l66xb->scr + 0x200, 0,
+		t7l66xb_mmc_resources[0].start & 0xfffe);
+
+	return 0;
+}
+
+static int t7l66xb_mmc_disable(struct platform_device *mmc)
+{
+	struct platform_device *dev = to_platform_device(mmc->dev.parent);
+	struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
+	unsigned long flags;
+	u8 dev_ctl;
+
+	spin_lock_irqsave(&t7l66xb->lock, flags);
+
+	dev_ctl = tmio_ioread8(t7l66xb->scr + SCR_DEV_CTL);
+	dev_ctl &= ~SCR_DEV_CTL_MMC;
+	tmio_iowrite8(dev_ctl, t7l66xb->scr + SCR_DEV_CTL);
+
+	spin_unlock_irqrestore(&t7l66xb->lock, flags);
+
+	clk_disable(t7l66xb->clk32k);
+
+	return 0;
+}
+
+static void t7l66xb_mmc_pwr(struct platform_device *mmc, int state)
+{
+	struct platform_device *dev = to_platform_device(mmc->dev.parent);
+	struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
+
+	tmio_core_mmc_pwr(t7l66xb->scr + 0x200, 0, state);
+}
+
+static void t7l66xb_mmc_clk_div(struct platform_device *mmc, int state)
+{
+	struct platform_device *dev = to_platform_device(mmc->dev.parent);
+	struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
+
+	tmio_core_mmc_clk_div(t7l66xb->scr + 0x200, 0, state);
+}
+
+/*--------------------------------------------------------------------------*/
+
+static struct tmio_mmc_data t7166xb_mmc_data = {
+	.hclk = 24000000,
+	.set_pwr = t7l66xb_mmc_pwr,
+	.set_clk_div = t7l66xb_mmc_clk_div,
+};
+
+static const struct resource t7l66xb_nand_resources[] = {
+	{
+		.start	= 0xc00,
+		.end	= 0xc07,
+		.flags	= IORESOURCE_MEM,
+	},
+	{
+		.start	= 0x0100,
+		.end	= 0x01ff,
+		.flags	= IORESOURCE_MEM,
+	},
+	{
+		.start	= IRQ_T7L66XB_NAND,
+		.end	= IRQ_T7L66XB_NAND,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct mfd_cell t7l66xb_cells[] = {
+	[T7L66XB_CELL_MMC] = {
+		.name = "tmio-mmc",
+		.enable = t7l66xb_mmc_enable,
+		.disable = t7l66xb_mmc_disable,
+		.platform_data = &t7166xb_mmc_data,
+		.pdata_size    = sizeof(t7166xb_mmc_data),
+		.num_resources = ARRAY_SIZE(t7l66xb_mmc_resources),
+		.resources = t7l66xb_mmc_resources,
+	},
+	[T7L66XB_CELL_NAND] = {
+		.name = "tmio-nand",
+		.num_resources = ARRAY_SIZE(t7l66xb_nand_resources),
+		.resources = t7l66xb_nand_resources,
+	},
+};
+
+/*--------------------------------------------------------------------------*/
+
+/* Handle the T7L66XB interrupt mux */
+static void t7l66xb_irq(unsigned int irq, struct irq_desc *desc)
+{
+	struct t7l66xb *t7l66xb = irq_get_handler_data(irq);
+	unsigned int isr;
+	unsigned int i, irq_base;
+
+	irq_base = t7l66xb->irq_base;
+
+	while ((isr = tmio_ioread8(t7l66xb->scr + SCR_ISR) &
+				~tmio_ioread8(t7l66xb->scr + SCR_IMR)))
+		for (i = 0; i < T7L66XB_NR_IRQS; i++)
+			if (isr & (1 << i))
+				generic_handle_irq(irq_base + i);
+}
+
+static void t7l66xb_irq_mask(struct irq_data *data)
+{
+	struct t7l66xb *t7l66xb = irq_data_get_irq_chip_data(data);
+	unsigned long			flags;
+	u8 imr;
+
+	spin_lock_irqsave(&t7l66xb->lock, flags);
+	imr = tmio_ioread8(t7l66xb->scr + SCR_IMR);
+	imr |= 1 << (data->irq - t7l66xb->irq_base);
+	tmio_iowrite8(imr, t7l66xb->scr + SCR_IMR);
+	spin_unlock_irqrestore(&t7l66xb->lock, flags);
+}
+
+static void t7l66xb_irq_unmask(struct irq_data *data)
+{
+	struct t7l66xb *t7l66xb = irq_data_get_irq_chip_data(data);
+	unsigned long flags;
+	u8 imr;
+
+	spin_lock_irqsave(&t7l66xb->lock, flags);
+	imr = tmio_ioread8(t7l66xb->scr + SCR_IMR);
+	imr &= ~(1 << (data->irq - t7l66xb->irq_base));
+	tmio_iowrite8(imr, t7l66xb->scr + SCR_IMR);
+	spin_unlock_irqrestore(&t7l66xb->lock, flags);
+}
+
+static struct irq_chip t7l66xb_chip = {
+	.name		= "t7l66xb",
+	.irq_ack	= t7l66xb_irq_mask,
+	.irq_mask	= t7l66xb_irq_mask,
+	.irq_unmask	= t7l66xb_irq_unmask,
+};
+
+/*--------------------------------------------------------------------------*/
+
+/* Install the IRQ handler */
+static void t7l66xb_attach_irq(struct platform_device *dev)
+{
+	struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
+	unsigned int irq, irq_base;
+
+	irq_base = t7l66xb->irq_base;
+
+	for (irq = irq_base; irq < irq_base + T7L66XB_NR_IRQS; irq++) {
+		irq_set_chip_and_handler(irq, &t7l66xb_chip, handle_level_irq);
+		irq_set_chip_data(irq, t7l66xb);
+#ifdef CONFIG_ARM
+		set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+#endif
+	}
+
+	irq_set_irq_type(t7l66xb->irq, IRQ_TYPE_EDGE_FALLING);
+	irq_set_handler_data(t7l66xb->irq, t7l66xb);
+	irq_set_chained_handler(t7l66xb->irq, t7l66xb_irq);
+}
+
+static void t7l66xb_detach_irq(struct platform_device *dev)
+{
+	struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
+	unsigned int irq, irq_base;
+
+	irq_base = t7l66xb->irq_base;
+
+	irq_set_chained_handler(t7l66xb->irq, NULL);
+	irq_set_handler_data(t7l66xb->irq, NULL);
+
+	for (irq = irq_base; irq < irq_base + T7L66XB_NR_IRQS; irq++) {
+#ifdef CONFIG_ARM
+		set_irq_flags(irq, 0);
+#endif
+		irq_set_chip(irq, NULL);
+		irq_set_chip_data(irq, NULL);
+	}
+}
+
+/*--------------------------------------------------------------------------*/
+
+#ifdef CONFIG_PM
+static int t7l66xb_suspend(struct platform_device *dev, pm_message_t state)
+{
+	struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
+	struct t7l66xb_platform_data *pdata = dev->dev.platform_data;
+
+	if (pdata && pdata->suspend)
+		pdata->suspend(dev);
+	clk_disable(t7l66xb->clk48m);
+
+	return 0;
+}
+
+static int t7l66xb_resume(struct platform_device *dev)
+{
+	struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
+	struct t7l66xb_platform_data *pdata = dev->dev.platform_data;
+
+	clk_enable(t7l66xb->clk48m);
+	if (pdata && pdata->resume)
+		pdata->resume(dev);
+
+	tmio_core_mmc_enable(t7l66xb->scr + 0x200, 0,
+		t7l66xb_mmc_resources[0].start & 0xfffe);
+
+	return 0;
+}
+#else
+#define t7l66xb_suspend NULL
+#define t7l66xb_resume	NULL
+#endif
+
+/*--------------------------------------------------------------------------*/
+
+static int t7l66xb_probe(struct platform_device *dev)
+{
+	struct t7l66xb_platform_data *pdata = dev->dev.platform_data;
+	struct t7l66xb *t7l66xb;
+	struct resource *iomem, *rscr;
+	int ret;
+
+	if (pdata == NULL)
+		return -EINVAL;
+
+	iomem = platform_get_resource(dev, IORESOURCE_MEM, 0);
+	if (!iomem)
+		return -EINVAL;
+
+	t7l66xb = kzalloc(sizeof *t7l66xb, GFP_KERNEL);
+	if (!t7l66xb)
+		return -ENOMEM;
+
+	spin_lock_init(&t7l66xb->lock);
+
+	platform_set_drvdata(dev, t7l66xb);
+
+	ret = platform_get_irq(dev, 0);
+	if (ret >= 0)
+		t7l66xb->irq = ret;
+	else
+		goto err_noirq;
+
+	t7l66xb->irq_base = pdata->irq_base;
+
+	t7l66xb->clk32k = clk_get(&dev->dev, "CLK_CK32K");
+	if (IS_ERR(t7l66xb->clk32k)) {
+		ret = PTR_ERR(t7l66xb->clk32k);
+		goto err_clk32k_get;
+	}
+
+	t7l66xb->clk48m = clk_get(&dev->dev, "CLK_CK48M");
+	if (IS_ERR(t7l66xb->clk48m)) {
+		ret = PTR_ERR(t7l66xb->clk48m);
+		goto err_clk48m_get;
+	}
+
+	rscr = &t7l66xb->rscr;
+	rscr->name = "t7l66xb-core";
+	rscr->start = iomem->start;
+	rscr->end = iomem->start + 0xff;
+	rscr->flags = IORESOURCE_MEM;
+
+	ret = request_resource(iomem, rscr);
+	if (ret)
+		goto err_request_scr;
+
+	t7l66xb->scr = ioremap(rscr->start, resource_size(rscr));
+	if (!t7l66xb->scr) {
+		ret = -ENOMEM;
+		goto err_ioremap;
+	}
+
+	clk_enable(t7l66xb->clk48m);
+
+	if (pdata && pdata->enable)
+		pdata->enable(dev);
+
+	/* Mask all interrupts */
+	tmio_iowrite8(0xbf, t7l66xb->scr + SCR_IMR);
+
+	printk(KERN_INFO "%s rev %d @ 0x%08lx, irq %d\n",
+		dev->name, tmio_ioread8(t7l66xb->scr + SCR_REVID),
+		(unsigned long)iomem->start, t7l66xb->irq);
+
+	t7l66xb_attach_irq(dev);
+
+	t7l66xb_cells[T7L66XB_CELL_NAND].platform_data = pdata->nand_data;
+	t7l66xb_cells[T7L66XB_CELL_NAND].pdata_size = sizeof(*pdata->nand_data);
+
+	ret = mfd_add_devices(&dev->dev, dev->id,
+			      t7l66xb_cells, ARRAY_SIZE(t7l66xb_cells),
+			      iomem, t7l66xb->irq_base);
+
+	if (!ret)
+		return 0;
+
+	t7l66xb_detach_irq(dev);
+	iounmap(t7l66xb->scr);
+err_ioremap:
+	release_resource(&t7l66xb->rscr);
+err_request_scr:
+	clk_put(t7l66xb->clk48m);
+err_clk48m_get:
+	clk_put(t7l66xb->clk32k);
+err_clk32k_get:
+err_noirq:
+	kfree(t7l66xb);
+	return ret;
+}
+
+static int t7l66xb_remove(struct platform_device *dev)
+{
+	struct t7l66xb_platform_data *pdata = dev->dev.platform_data;
+	struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
+	int ret;
+
+	ret = pdata->disable(dev);
+	clk_disable(t7l66xb->clk48m);
+	clk_put(t7l66xb->clk48m);
+	clk_disable(t7l66xb->clk32k);
+	clk_put(t7l66xb->clk32k);
+	t7l66xb_detach_irq(dev);
+	iounmap(t7l66xb->scr);
+	release_resource(&t7l66xb->rscr);
+	mfd_remove_devices(&dev->dev);
+	platform_set_drvdata(dev, NULL);
+	kfree(t7l66xb);
+
+	return ret;
+
+}
+
+static struct platform_driver t7l66xb_platform_driver = {
+	.driver = {
+		.name	= "t7l66xb",
+		.owner	= THIS_MODULE,
+	},
+	.suspend	= t7l66xb_suspend,
+	.resume		= t7l66xb_resume,
+	.probe		= t7l66xb_probe,
+	.remove		= t7l66xb_remove,
+};
+
+/*--------------------------------------------------------------------------*/
+
+module_platform_driver(t7l66xb_platform_driver);
+
+MODULE_DESCRIPTION("Toshiba T7L66XB core driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Ian Molton");
+MODULE_ALIAS("platform:t7l66xb");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/tc3589x.c b/ap/os/linux/linux-3.4.x/drivers/mfd/tc3589x.c
new file mode 100644
index 0000000..de97974
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/tc3589x.c
@@ -0,0 +1,424 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License, version 2
+ * Author: Hanumath Prasad <hanumath.prasad@stericsson.com> for ST-Ericsson
+ * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/tc3589x.h>
+
+#define TC3589x_CLKMODE_MODCTL_SLEEP		0x0
+#define TC3589x_CLKMODE_MODCTL_OPERATION	(1 << 0)
+
+/**
+ * tc3589x_reg_read() - read a single TC3589x register
+ * @tc3589x:	Device to read from
+ * @reg:	Register to read
+ */
+int tc3589x_reg_read(struct tc3589x *tc3589x, u8 reg)
+{
+	int ret;
+
+	ret = i2c_smbus_read_byte_data(tc3589x->i2c, reg);
+	if (ret < 0)
+		dev_err(tc3589x->dev, "failed to read reg %#x: %d\n",
+			reg, ret);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(tc3589x_reg_read);
+
+/**
+ * tc3589x_reg_read() - write a single TC3589x register
+ * @tc3589x:	Device to write to
+ * @reg:	Register to read
+ * @data:	Value to write
+ */
+int tc3589x_reg_write(struct tc3589x *tc3589x, u8 reg, u8 data)
+{
+	int ret;
+
+	ret = i2c_smbus_write_byte_data(tc3589x->i2c, reg, data);
+	if (ret < 0)
+		dev_err(tc3589x->dev, "failed to write reg %#x: %d\n",
+			reg, ret);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(tc3589x_reg_write);
+
+/**
+ * tc3589x_block_read() - read multiple TC3589x registers
+ * @tc3589x:	Device to read from
+ * @reg:	First register
+ * @length:	Number of registers
+ * @values:	Buffer to write to
+ */
+int tc3589x_block_read(struct tc3589x *tc3589x, u8 reg, u8 length, u8 *values)
+{
+	int ret;
+
+	ret = i2c_smbus_read_i2c_block_data(tc3589x->i2c, reg, length, values);
+	if (ret < 0)
+		dev_err(tc3589x->dev, "failed to read regs %#x: %d\n",
+			reg, ret);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(tc3589x_block_read);
+
+/**
+ * tc3589x_block_write() - write multiple TC3589x registers
+ * @tc3589x:	Device to write to
+ * @reg:	First register
+ * @length:	Number of registers
+ * @values:	Values to write
+ */
+int tc3589x_block_write(struct tc3589x *tc3589x, u8 reg, u8 length,
+			const u8 *values)
+{
+	int ret;
+
+	ret = i2c_smbus_write_i2c_block_data(tc3589x->i2c, reg, length,
+					     values);
+	if (ret < 0)
+		dev_err(tc3589x->dev, "failed to write regs %#x: %d\n",
+			reg, ret);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(tc3589x_block_write);
+
+/**
+ * tc3589x_set_bits() - set the value of a bitfield in a TC3589x register
+ * @tc3589x:	Device to write to
+ * @reg:	Register to write
+ * @mask:	Mask of bits to set
+ * @values:	Value to set
+ */
+int tc3589x_set_bits(struct tc3589x *tc3589x, u8 reg, u8 mask, u8 val)
+{
+	int ret;
+
+	mutex_lock(&tc3589x->lock);
+
+	ret = tc3589x_reg_read(tc3589x, reg);
+	if (ret < 0)
+		goto out;
+
+	ret &= ~mask;
+	ret |= val;
+
+	ret = tc3589x_reg_write(tc3589x, reg, ret);
+
+out:
+	mutex_unlock(&tc3589x->lock);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(tc3589x_set_bits);
+
+static struct resource gpio_resources[] = {
+	{
+		.start	= TC3589x_INT_GPIIRQ,
+		.end	= TC3589x_INT_GPIIRQ,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource keypad_resources[] = {
+	{
+		.start  = TC3589x_INT_KBDIRQ,
+		.end    = TC3589x_INT_KBDIRQ,
+		.flags  = IORESOURCE_IRQ,
+	},
+};
+
+static struct mfd_cell tc3589x_dev_gpio[] = {
+	{
+		.name		= "tc3589x-gpio",
+		.num_resources	= ARRAY_SIZE(gpio_resources),
+		.resources	= &gpio_resources[0],
+	},
+};
+
+static struct mfd_cell tc3589x_dev_keypad[] = {
+	{
+		.name           = "tc3589x-keypad",
+		.num_resources  = ARRAY_SIZE(keypad_resources),
+		.resources      = &keypad_resources[0],
+	},
+};
+
+static irqreturn_t tc3589x_irq(int irq, void *data)
+{
+	struct tc3589x *tc3589x = data;
+	int status;
+
+again:
+	status = tc3589x_reg_read(tc3589x, TC3589x_IRQST);
+	if (status < 0)
+		return IRQ_NONE;
+
+	while (status) {
+		int bit = __ffs(status);
+
+		handle_nested_irq(tc3589x->irq_base + bit);
+		status &= ~(1 << bit);
+	}
+
+	/*
+	 * A dummy read or write (to any register) appears to be necessary to
+	 * have the last interrupt clear (for example, GPIO IC write) take
+	 * effect. In such a case, recheck for any interrupt which is still
+	 * pending.
+	 */
+	status = tc3589x_reg_read(tc3589x, TC3589x_IRQST);
+	if (status)
+		goto again;
+
+	return IRQ_HANDLED;
+}
+
+static int tc3589x_irq_init(struct tc3589x *tc3589x)
+{
+	int base = tc3589x->irq_base;
+	int irq;
+
+	for (irq = base; irq < base + TC3589x_NR_INTERNAL_IRQS; irq++) {
+		irq_set_chip_data(irq, tc3589x);
+		irq_set_chip_and_handler(irq, &dummy_irq_chip,
+					 handle_edge_irq);
+		irq_set_nested_thread(irq, 1);
+#ifdef CONFIG_ARM
+		set_irq_flags(irq, IRQF_VALID);
+#else
+		irq_set_noprobe(irq);
+#endif
+	}
+
+	return 0;
+}
+
+static void tc3589x_irq_remove(struct tc3589x *tc3589x)
+{
+	int base = tc3589x->irq_base;
+	int irq;
+
+	for (irq = base; irq < base + TC3589x_NR_INTERNAL_IRQS; irq++) {
+#ifdef CONFIG_ARM
+		set_irq_flags(irq, 0);
+#endif
+		irq_set_chip_and_handler(irq, NULL, NULL);
+		irq_set_chip_data(irq, NULL);
+	}
+}
+
+static int tc3589x_chip_init(struct tc3589x *tc3589x)
+{
+	int manf, ver, ret;
+
+	manf = tc3589x_reg_read(tc3589x, TC3589x_MANFCODE);
+	if (manf < 0)
+		return manf;
+
+	ver = tc3589x_reg_read(tc3589x, TC3589x_VERSION);
+	if (ver < 0)
+		return ver;
+
+	if (manf != TC3589x_MANFCODE_MAGIC) {
+		dev_err(tc3589x->dev, "unknown manufacturer: %#x\n", manf);
+		return -EINVAL;
+	}
+
+	dev_info(tc3589x->dev, "manufacturer: %#x, version: %#x\n", manf, ver);
+
+	/*
+	 * Put everything except the IRQ module into reset;
+	 * also spare the GPIO module for any pin initialization
+	 * done during pre-kernel boot
+	 */
+	ret = tc3589x_reg_write(tc3589x, TC3589x_RSTCTRL,
+				TC3589x_RSTCTRL_TIMRST
+				| TC3589x_RSTCTRL_ROTRST
+				| TC3589x_RSTCTRL_KBDRST);
+	if (ret < 0)
+		return ret;
+
+	/* Clear the reset interrupt. */
+	return tc3589x_reg_write(tc3589x, TC3589x_RSTINTCLR, 0x1);
+}
+
+static int __devinit tc3589x_device_init(struct tc3589x *tc3589x)
+{
+	int ret = 0;
+	unsigned int blocks = tc3589x->pdata->block;
+
+	if (blocks & TC3589x_BLOCK_GPIO) {
+		ret = mfd_add_devices(tc3589x->dev, -1, tc3589x_dev_gpio,
+				ARRAY_SIZE(tc3589x_dev_gpio), NULL,
+				tc3589x->irq_base);
+		if (ret) {
+			dev_err(tc3589x->dev, "failed to add gpio child\n");
+			return ret;
+		}
+		dev_info(tc3589x->dev, "added gpio block\n");
+	}
+
+	if (blocks & TC3589x_BLOCK_KEYPAD) {
+		ret = mfd_add_devices(tc3589x->dev, -1, tc3589x_dev_keypad,
+				ARRAY_SIZE(tc3589x_dev_keypad), NULL,
+				tc3589x->irq_base);
+		if (ret) {
+			dev_err(tc3589x->dev, "failed to keypad child\n");
+			return ret;
+		}
+		dev_info(tc3589x->dev, "added keypad block\n");
+	}
+
+	return ret;
+}
+
+static int __devinit tc3589x_probe(struct i2c_client *i2c,
+				   const struct i2c_device_id *id)
+{
+	struct tc3589x_platform_data *pdata = i2c->dev.platform_data;
+	struct tc3589x *tc3589x;
+	int ret;
+
+	if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA
+				     | I2C_FUNC_SMBUS_I2C_BLOCK))
+		return -EIO;
+
+	tc3589x = kzalloc(sizeof(struct tc3589x), GFP_KERNEL);
+	if (!tc3589x)
+		return -ENOMEM;
+
+	mutex_init(&tc3589x->lock);
+
+	tc3589x->dev = &i2c->dev;
+	tc3589x->i2c = i2c;
+	tc3589x->pdata = pdata;
+	tc3589x->irq_base = pdata->irq_base;
+	tc3589x->num_gpio = id->driver_data;
+
+	i2c_set_clientdata(i2c, tc3589x);
+
+	ret = tc3589x_chip_init(tc3589x);
+	if (ret)
+		goto out_free;
+
+	ret = tc3589x_irq_init(tc3589x);
+	if (ret)
+		goto out_free;
+
+	ret = request_threaded_irq(tc3589x->i2c->irq, NULL, tc3589x_irq,
+				   IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+				   "tc3589x", tc3589x);
+	if (ret) {
+		dev_err(tc3589x->dev, "failed to request IRQ: %d\n", ret);
+		goto out_removeirq;
+	}
+
+	ret = tc3589x_device_init(tc3589x);
+	if (ret) {
+		dev_err(tc3589x->dev, "failed to add child devices\n");
+		goto out_freeirq;
+	}
+
+	return 0;
+
+out_freeirq:
+	free_irq(tc3589x->i2c->irq, tc3589x);
+out_removeirq:
+	tc3589x_irq_remove(tc3589x);
+out_free:
+	kfree(tc3589x);
+	return ret;
+}
+
+static int __devexit tc3589x_remove(struct i2c_client *client)
+{
+	struct tc3589x *tc3589x = i2c_get_clientdata(client);
+
+	mfd_remove_devices(tc3589x->dev);
+
+	free_irq(tc3589x->i2c->irq, tc3589x);
+	tc3589x_irq_remove(tc3589x);
+
+	kfree(tc3589x);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int tc3589x_suspend(struct device *dev)
+{
+	struct tc3589x *tc3589x = dev_get_drvdata(dev);
+	struct i2c_client *client = tc3589x->i2c;
+	int ret = 0;
+
+	/* put the system to sleep mode */
+	if (!device_may_wakeup(&client->dev))
+		ret = tc3589x_reg_write(tc3589x, TC3589x_CLKMODE,
+				TC3589x_CLKMODE_MODCTL_SLEEP);
+
+	return ret;
+}
+
+static int tc3589x_resume(struct device *dev)
+{
+	struct tc3589x *tc3589x = dev_get_drvdata(dev);
+	struct i2c_client *client = tc3589x->i2c;
+	int ret = 0;
+
+	/* enable the system into operation */
+	if (!device_may_wakeup(&client->dev))
+		ret = tc3589x_reg_write(tc3589x, TC3589x_CLKMODE,
+				TC3589x_CLKMODE_MODCTL_OPERATION);
+
+	return ret;
+}
+
+static const SIMPLE_DEV_PM_OPS(tc3589x_dev_pm_ops, tc3589x_suspend,
+						tc3589x_resume);
+#endif
+
+static const struct i2c_device_id tc3589x_id[] = {
+	{ "tc3589x", 24 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, tc3589x_id);
+
+static struct i2c_driver tc3589x_driver = {
+	.driver.name	= "tc3589x",
+	.driver.owner	= THIS_MODULE,
+#ifdef CONFIG_PM
+	.driver.pm	= &tc3589x_dev_pm_ops,
+#endif
+	.probe		= tc3589x_probe,
+	.remove		= __devexit_p(tc3589x_remove),
+	.id_table	= tc3589x_id,
+};
+
+static int __init tc3589x_init(void)
+{
+	return i2c_add_driver(&tc3589x_driver);
+}
+subsys_initcall(tc3589x_init);
+
+static void __exit tc3589x_exit(void)
+{
+	i2c_del_driver(&tc3589x_driver);
+}
+module_exit(tc3589x_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("TC3589x MFD core driver");
+MODULE_AUTHOR("Hanumath Prasad, Rabin Vincent");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/tc6387xb.c b/ap/os/linux/linux-3.4.x/drivers/mfd/tc6387xb.c
new file mode 100644
index 0000000..d20a284
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/tc6387xb.c
@@ -0,0 +1,243 @@
+/*
+ * Toshiba TC6387XB support
+ * Copyright (c) 2005 Ian Molton
+ *
+ * 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 file contains TC6387XB base support.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/tmio.h>
+#include <linux/mfd/tc6387xb.h>
+#include <linux/slab.h>
+
+enum {
+	TC6387XB_CELL_MMC,
+};
+
+struct tc6387xb {
+	void __iomem *scr;
+	struct clk *clk32k;
+	struct resource rscr;
+};
+
+static struct resource tc6387xb_mmc_resources[] = {
+	{
+		.start = 0x800,
+		.end   = 0x9ff,
+		.flags = IORESOURCE_MEM,
+	},
+	{
+		.start = 0,
+		.end   = 0,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+/*--------------------------------------------------------------------------*/
+
+#ifdef CONFIG_PM
+static int tc6387xb_suspend(struct platform_device *dev, pm_message_t state)
+{
+	struct tc6387xb *tc6387xb = platform_get_drvdata(dev);
+	struct tc6387xb_platform_data *pdata = dev->dev.platform_data;
+
+	if (pdata && pdata->suspend)
+		pdata->suspend(dev);
+	clk_disable(tc6387xb->clk32k);
+
+	return 0;
+}
+
+static int tc6387xb_resume(struct platform_device *dev)
+{
+	struct tc6387xb *tc6387xb = platform_get_drvdata(dev);
+	struct tc6387xb_platform_data *pdata = dev->dev.platform_data;
+
+	clk_enable(tc6387xb->clk32k);
+	if (pdata && pdata->resume)
+		pdata->resume(dev);
+
+	tmio_core_mmc_resume(tc6387xb->scr + 0x200, 0,
+		tc6387xb_mmc_resources[0].start & 0xfffe);
+
+	return 0;
+}
+#else
+#define tc6387xb_suspend  NULL
+#define tc6387xb_resume   NULL
+#endif
+
+/*--------------------------------------------------------------------------*/
+
+static void tc6387xb_mmc_pwr(struct platform_device *mmc, int state)
+{
+	struct platform_device *dev = to_platform_device(mmc->dev.parent);
+	struct tc6387xb *tc6387xb = platform_get_drvdata(dev);
+
+	tmio_core_mmc_pwr(tc6387xb->scr + 0x200, 0, state);
+}
+
+static void tc6387xb_mmc_clk_div(struct platform_device *mmc, int state)
+{
+	struct platform_device *dev = to_platform_device(mmc->dev.parent);
+	struct tc6387xb *tc6387xb = platform_get_drvdata(dev);
+
+	tmio_core_mmc_clk_div(tc6387xb->scr + 0x200, 0, state);
+}
+
+
+static int tc6387xb_mmc_enable(struct platform_device *mmc)
+{
+	struct platform_device *dev      = to_platform_device(mmc->dev.parent);
+	struct tc6387xb *tc6387xb = platform_get_drvdata(dev);
+
+	clk_enable(tc6387xb->clk32k);
+
+	tmio_core_mmc_enable(tc6387xb->scr + 0x200, 0,
+		tc6387xb_mmc_resources[0].start & 0xfffe);
+
+	return 0;
+}
+
+static int tc6387xb_mmc_disable(struct platform_device *mmc)
+{
+	struct platform_device *dev      = to_platform_device(mmc->dev.parent);
+	struct tc6387xb *tc6387xb = platform_get_drvdata(dev);
+
+	clk_disable(tc6387xb->clk32k);
+
+	return 0;
+}
+
+static struct tmio_mmc_data tc6387xb_mmc_data = {
+	.hclk = 24000000,
+	.set_pwr = tc6387xb_mmc_pwr,
+	.set_clk_div = tc6387xb_mmc_clk_div,
+};
+
+/*--------------------------------------------------------------------------*/
+
+static struct mfd_cell tc6387xb_cells[] = {
+	[TC6387XB_CELL_MMC] = {
+		.name = "tmio-mmc",
+		.enable = tc6387xb_mmc_enable,
+		.disable = tc6387xb_mmc_disable,
+		.platform_data = &tc6387xb_mmc_data,
+		.pdata_size    = sizeof(tc6387xb_mmc_data),
+		.num_resources = ARRAY_SIZE(tc6387xb_mmc_resources),
+		.resources = tc6387xb_mmc_resources,
+	},
+};
+
+static int __devinit tc6387xb_probe(struct platform_device *dev)
+{
+	struct tc6387xb_platform_data *pdata = dev->dev.platform_data;
+	struct resource *iomem, *rscr;
+	struct clk *clk32k;
+	struct tc6387xb *tc6387xb;
+	int irq, ret;
+
+	iomem = platform_get_resource(dev, IORESOURCE_MEM, 0);
+	if (!iomem) {
+		return -EINVAL;
+	}
+
+	tc6387xb = kzalloc(sizeof *tc6387xb, GFP_KERNEL);
+	if (!tc6387xb)
+		return -ENOMEM;
+
+	ret  = platform_get_irq(dev, 0);
+	if (ret >= 0)
+		irq = ret;
+	else
+		goto err_no_irq;
+
+	clk32k = clk_get(&dev->dev, "CLK_CK32K");
+	if (IS_ERR(clk32k)) {
+		ret = PTR_ERR(clk32k);
+		goto err_no_clk;
+	}
+
+	rscr = &tc6387xb->rscr;
+	rscr->name = "tc6387xb-core";
+	rscr->start = iomem->start;
+	rscr->end = iomem->start + 0xff;
+	rscr->flags = IORESOURCE_MEM;
+
+	ret = request_resource(iomem, rscr);
+	if (ret)
+		goto err_resource;
+
+	tc6387xb->scr = ioremap(rscr->start, resource_size(rscr));
+	if (!tc6387xb->scr) {
+		ret = -ENOMEM;
+		goto err_ioremap;
+	}
+
+	tc6387xb->clk32k = clk32k;
+	platform_set_drvdata(dev, tc6387xb);
+
+	if (pdata && pdata->enable)
+		pdata->enable(dev);
+
+	printk(KERN_INFO "Toshiba tc6387xb initialised\n");
+
+	ret = mfd_add_devices(&dev->dev, dev->id, tc6387xb_cells,
+			      ARRAY_SIZE(tc6387xb_cells), iomem, irq);
+
+	if (!ret)
+		return 0;
+
+	iounmap(tc6387xb->scr);
+err_ioremap:
+	release_resource(&tc6387xb->rscr);
+err_resource:
+	clk_put(clk32k);
+err_no_clk:
+err_no_irq:
+	kfree(tc6387xb);
+	return ret;
+}
+
+static int __devexit tc6387xb_remove(struct platform_device *dev)
+{
+	struct tc6387xb *tc6387xb = platform_get_drvdata(dev);
+
+	mfd_remove_devices(&dev->dev);
+	iounmap(tc6387xb->scr);
+	release_resource(&tc6387xb->rscr);
+	clk_disable(tc6387xb->clk32k);
+	clk_put(tc6387xb->clk32k);
+	platform_set_drvdata(dev, NULL);
+	kfree(tc6387xb);
+
+	return 0;
+}
+
+
+static struct platform_driver tc6387xb_platform_driver = {
+	.driver = {
+		.name		= "tc6387xb",
+	},
+	.probe		= tc6387xb_probe,
+	.remove		= __devexit_p(tc6387xb_remove),
+	.suspend        = tc6387xb_suspend,
+	.resume         = tc6387xb_resume,
+};
+
+module_platform_driver(tc6387xb_platform_driver);
+
+MODULE_DESCRIPTION("Toshiba TC6387XB core driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Ian Molton");
+MODULE_ALIAS("platform:tc6387xb");
+
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/tc6393xb.c b/ap/os/linux/linux-3.4.x/drivers/mfd/tc6393xb.c
new file mode 100644
index 0000000..b69d91b
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/tc6393xb.c
@@ -0,0 +1,872 @@
+/*
+ * Toshiba TC6393XB SoC support
+ *
+ * Copyright(c) 2005-2006 Chris Humbert
+ * Copyright(c) 2005 Dirk Opfer
+ * Copyright(c) 2005 Ian Molton <spyro@f2s.com>
+ * Copyright(c) 2007 Dmitry Baryshkov
+ *
+ * Based on code written by Sharp/Lineo for 2.4 kernels
+ * Based on locomo.c
+ *
+ * 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/module.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/tmio.h>
+#include <linux/mfd/tc6393xb.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+
+#define SCR_REVID	0x08		/* b Revision ID	*/
+#define SCR_ISR		0x50		/* b Interrupt Status	*/
+#define SCR_IMR		0x52		/* b Interrupt Mask	*/
+#define SCR_IRR		0x54		/* b Interrupt Routing	*/
+#define SCR_GPER	0x60		/* w GP Enable		*/
+#define SCR_GPI_SR(i)	(0x64 + (i))	/* b3 GPI Status	*/
+#define SCR_GPI_IMR(i)	(0x68 + (i))	/* b3 GPI INT Mask	*/
+#define SCR_GPI_EDER(i)	(0x6c + (i))	/* b3 GPI Edge Detect Enable */
+#define SCR_GPI_LIR(i)	(0x70 + (i))	/* b3 GPI Level Invert	*/
+#define SCR_GPO_DSR(i)	(0x78 + (i))	/* b3 GPO Data Set	*/
+#define SCR_GPO_DOECR(i) (0x7c + (i))	/* b3 GPO Data OE Control */
+#define SCR_GP_IARCR(i)	(0x80 + (i))	/* b3 GP Internal Active Register Control */
+#define SCR_GP_IARLCR(i) (0x84 + (i))	/* b3 GP INTERNAL Active Register Level Control */
+#define SCR_GPI_BCR(i)	(0x88 + (i))	/* b3 GPI Buffer Control */
+#define SCR_GPA_IARCR	0x8c		/* w GPa Internal Active Register Control */
+#define SCR_GPA_IARLCR	0x90		/* w GPa Internal Active Register Level Control */
+#define SCR_GPA_BCR	0x94		/* w GPa Buffer Control */
+#define SCR_CCR		0x98		/* w Clock Control	*/
+#define SCR_PLL2CR	0x9a		/* w PLL2 Control	*/
+#define SCR_PLL1CR	0x9c		/* l PLL1 Control	*/
+#define SCR_DIARCR	0xa0		/* b Device Internal Active Register Control */
+#define SCR_DBOCR	0xa1		/* b Device Buffer Off Control */
+#define SCR_FER		0xe0		/* b Function Enable	*/
+#define SCR_MCR		0xe4		/* w Mode Control	*/
+#define SCR_CONFIG	0xfc		/* b Configuration Control */
+#define SCR_DEBUG	0xff		/* b Debug		*/
+
+#define SCR_CCR_CK32K	BIT(0)
+#define SCR_CCR_USBCK	BIT(1)
+#define SCR_CCR_UNK1	BIT(4)
+#define SCR_CCR_MCLK_MASK	(7 << 8)
+#define SCR_CCR_MCLK_OFF	(0 << 8)
+#define SCR_CCR_MCLK_12	(1 << 8)
+#define SCR_CCR_MCLK_24	(2 << 8)
+#define SCR_CCR_MCLK_48	(3 << 8)
+#define SCR_CCR_HCLK_MASK	(3 << 12)
+#define SCR_CCR_HCLK_24	(0 << 12)
+#define SCR_CCR_HCLK_48	(1 << 12)
+
+#define SCR_FER_USBEN		BIT(0)	/* USB host enable */
+#define SCR_FER_LCDCVEN		BIT(1)	/* polysilicon TFT enable */
+#define SCR_FER_SLCDEN		BIT(2)	/* SLCD enable */
+
+#define SCR_MCR_RDY_MASK		(3 << 0)
+#define SCR_MCR_RDY_OPENDRAIN	(0 << 0)
+#define SCR_MCR_RDY_TRISTATE	(1 << 0)
+#define SCR_MCR_RDY_PUSHPULL	(2 << 0)
+#define SCR_MCR_RDY_UNK		BIT(2)
+#define SCR_MCR_RDY_EN		BIT(3)
+#define SCR_MCR_INT_MASK		(3 << 4)
+#define SCR_MCR_INT_OPENDRAIN	(0 << 4)
+#define SCR_MCR_INT_TRISTATE	(1 << 4)
+#define SCR_MCR_INT_PUSHPULL	(2 << 4)
+#define SCR_MCR_INT_UNK		BIT(6)
+#define SCR_MCR_INT_EN		BIT(7)
+/* bits 8 - 16 are unknown */
+
+#define TC_GPIO_BIT(i)		(1 << (i & 0x7))
+
+/*--------------------------------------------------------------------------*/
+
+struct tc6393xb {
+	void __iomem		*scr;
+
+	struct gpio_chip	gpio;
+
+	struct clk		*clk; /* 3,6 Mhz */
+
+	spinlock_t		lock; /* protects RMW cycles */
+
+	struct {
+		u8		fer;
+		u16		ccr;
+		u8		gpi_bcr[3];
+		u8		gpo_dsr[3];
+		u8		gpo_doecr[3];
+	} suspend_state;
+
+	struct resource		rscr;
+	struct resource		*iomem;
+	int			irq;
+	int			irq_base;
+};
+
+enum {
+	TC6393XB_CELL_NAND,
+	TC6393XB_CELL_MMC,
+	TC6393XB_CELL_OHCI,
+	TC6393XB_CELL_FB,
+};
+
+/*--------------------------------------------------------------------------*/
+
+static int tc6393xb_nand_enable(struct platform_device *nand)
+{
+	struct platform_device *dev = to_platform_device(nand->dev.parent);
+	struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
+	unsigned long flags;
+
+	spin_lock_irqsave(&tc6393xb->lock, flags);
+
+	/* SMD buffer on */
+	dev_dbg(&dev->dev, "SMD buffer on\n");
+	tmio_iowrite8(0xff, tc6393xb->scr + SCR_GPI_BCR(1));
+
+	spin_unlock_irqrestore(&tc6393xb->lock, flags);
+
+	return 0;
+}
+
+static struct resource __devinitdata tc6393xb_nand_resources[] = {
+	{
+		.start	= 0x1000,
+		.end	= 0x1007,
+		.flags	= IORESOURCE_MEM,
+	},
+	{
+		.start	= 0x0100,
+		.end	= 0x01ff,
+		.flags	= IORESOURCE_MEM,
+	},
+	{
+		.start	= IRQ_TC6393_NAND,
+		.end	= IRQ_TC6393_NAND,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource tc6393xb_mmc_resources[] = {
+	{
+		.start	= 0x800,
+		.end	= 0x9ff,
+		.flags	= IORESOURCE_MEM,
+	},
+	{
+		.start	= IRQ_TC6393_MMC,
+		.end	= IRQ_TC6393_MMC,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static const struct resource tc6393xb_ohci_resources[] = {
+	{
+		.start	= 0x3000,
+		.end	= 0x31ff,
+		.flags	= IORESOURCE_MEM,
+	},
+	{
+		.start	= 0x0300,
+		.end	= 0x03ff,
+		.flags	= IORESOURCE_MEM,
+	},
+	{
+		.start	= 0x010000,
+		.end	= 0x017fff,
+		.flags	= IORESOURCE_MEM,
+	},
+	{
+		.start	= 0x018000,
+		.end	= 0x01ffff,
+		.flags	= IORESOURCE_MEM,
+	},
+	{
+		.start	= IRQ_TC6393_OHCI,
+		.end	= IRQ_TC6393_OHCI,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct resource __devinitdata tc6393xb_fb_resources[] = {
+	{
+		.start	= 0x5000,
+		.end	= 0x51ff,
+		.flags	= IORESOURCE_MEM,
+	},
+	{
+		.start	= 0x0500,
+		.end	= 0x05ff,
+		.flags	= IORESOURCE_MEM,
+	},
+	{
+		.start	= 0x100000,
+		.end	= 0x1fffff,
+		.flags	= IORESOURCE_MEM,
+	},
+	{
+		.start	= IRQ_TC6393_FB,
+		.end	= IRQ_TC6393_FB,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static int tc6393xb_ohci_enable(struct platform_device *dev)
+{
+	struct tc6393xb *tc6393xb = dev_get_drvdata(dev->dev.parent);
+	unsigned long flags;
+	u16 ccr;
+	u8 fer;
+
+	spin_lock_irqsave(&tc6393xb->lock, flags);
+
+	ccr = tmio_ioread16(tc6393xb->scr + SCR_CCR);
+	ccr |= SCR_CCR_USBCK;
+	tmio_iowrite16(ccr, tc6393xb->scr + SCR_CCR);
+
+	fer = tmio_ioread8(tc6393xb->scr + SCR_FER);
+	fer |= SCR_FER_USBEN;
+	tmio_iowrite8(fer, tc6393xb->scr + SCR_FER);
+
+	spin_unlock_irqrestore(&tc6393xb->lock, flags);
+
+	return 0;
+}
+
+static int tc6393xb_ohci_disable(struct platform_device *dev)
+{
+	struct tc6393xb *tc6393xb = dev_get_drvdata(dev->dev.parent);
+	unsigned long flags;
+	u16 ccr;
+	u8 fer;
+
+	spin_lock_irqsave(&tc6393xb->lock, flags);
+
+	fer = tmio_ioread8(tc6393xb->scr + SCR_FER);
+	fer &= ~SCR_FER_USBEN;
+	tmio_iowrite8(fer, tc6393xb->scr + SCR_FER);
+
+	ccr = tmio_ioread16(tc6393xb->scr + SCR_CCR);
+	ccr &= ~SCR_CCR_USBCK;
+	tmio_iowrite16(ccr, tc6393xb->scr + SCR_CCR);
+
+	spin_unlock_irqrestore(&tc6393xb->lock, flags);
+
+	return 0;
+}
+
+static int tc6393xb_ohci_suspend(struct platform_device *dev)
+{
+	struct tc6393xb_platform_data *tcpd = dev_get_platdata(dev->dev.parent);
+
+	/* We can't properly store/restore OHCI state, so fail here */
+	if (tcpd->resume_restore)
+		return -EBUSY;
+
+	return tc6393xb_ohci_disable(dev);
+}
+
+static int tc6393xb_fb_enable(struct platform_device *dev)
+{
+	struct tc6393xb *tc6393xb = dev_get_drvdata(dev->dev.parent);
+	unsigned long flags;
+	u16 ccr;
+
+	spin_lock_irqsave(&tc6393xb->lock, flags);
+
+	ccr = tmio_ioread16(tc6393xb->scr + SCR_CCR);
+	ccr &= ~SCR_CCR_MCLK_MASK;
+	ccr |= SCR_CCR_MCLK_48;
+	tmio_iowrite16(ccr, tc6393xb->scr + SCR_CCR);
+
+	spin_unlock_irqrestore(&tc6393xb->lock, flags);
+
+	return 0;
+}
+
+static int tc6393xb_fb_disable(struct platform_device *dev)
+{
+	struct tc6393xb *tc6393xb = dev_get_drvdata(dev->dev.parent);
+	unsigned long flags;
+	u16 ccr;
+
+	spin_lock_irqsave(&tc6393xb->lock, flags);
+
+	ccr = tmio_ioread16(tc6393xb->scr + SCR_CCR);
+	ccr &= ~SCR_CCR_MCLK_MASK;
+	ccr |= SCR_CCR_MCLK_OFF;
+	tmio_iowrite16(ccr, tc6393xb->scr + SCR_CCR);
+
+	spin_unlock_irqrestore(&tc6393xb->lock, flags);
+
+	return 0;
+}
+
+int tc6393xb_lcd_set_power(struct platform_device *fb, bool on)
+{
+	struct platform_device *dev = to_platform_device(fb->dev.parent);
+	struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
+	u8 fer;
+	unsigned long flags;
+
+	spin_lock_irqsave(&tc6393xb->lock, flags);
+
+	fer = ioread8(tc6393xb->scr + SCR_FER);
+	if (on)
+		fer |= SCR_FER_SLCDEN;
+	else
+		fer &= ~SCR_FER_SLCDEN;
+	iowrite8(fer, tc6393xb->scr + SCR_FER);
+
+	spin_unlock_irqrestore(&tc6393xb->lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL(tc6393xb_lcd_set_power);
+
+int tc6393xb_lcd_mode(struct platform_device *fb,
+					const struct fb_videomode *mode) {
+	struct platform_device *dev = to_platform_device(fb->dev.parent);
+	struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
+	unsigned long flags;
+
+	spin_lock_irqsave(&tc6393xb->lock, flags);
+
+	iowrite16(mode->pixclock, tc6393xb->scr + SCR_PLL1CR + 0);
+	iowrite16(mode->pixclock >> 16, tc6393xb->scr + SCR_PLL1CR + 2);
+
+	spin_unlock_irqrestore(&tc6393xb->lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL(tc6393xb_lcd_mode);
+
+static int tc6393xb_mmc_enable(struct platform_device *mmc)
+{
+	struct platform_device *dev = to_platform_device(mmc->dev.parent);
+	struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
+
+	tmio_core_mmc_enable(tc6393xb->scr + 0x200, 0,
+		tc6393xb_mmc_resources[0].start & 0xfffe);
+
+	return 0;
+}
+
+static int tc6393xb_mmc_resume(struct platform_device *mmc)
+{
+	struct platform_device *dev = to_platform_device(mmc->dev.parent);
+	struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
+
+	tmio_core_mmc_resume(tc6393xb->scr + 0x200, 0,
+		tc6393xb_mmc_resources[0].start & 0xfffe);
+
+	return 0;
+}
+
+static void tc6393xb_mmc_pwr(struct platform_device *mmc, int state)
+{
+	struct platform_device *dev = to_platform_device(mmc->dev.parent);
+	struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
+
+	tmio_core_mmc_pwr(tc6393xb->scr + 0x200, 0, state);
+}
+
+static void tc6393xb_mmc_clk_div(struct platform_device *mmc, int state)
+{
+	struct platform_device *dev = to_platform_device(mmc->dev.parent);
+	struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
+
+	tmio_core_mmc_clk_div(tc6393xb->scr + 0x200, 0, state);
+}
+
+static struct tmio_mmc_data tc6393xb_mmc_data = {
+	.hclk = 24000000,
+	.set_pwr = tc6393xb_mmc_pwr,
+	.set_clk_div = tc6393xb_mmc_clk_div,
+};
+
+static struct mfd_cell __devinitdata tc6393xb_cells[] = {
+	[TC6393XB_CELL_NAND] = {
+		.name = "tmio-nand",
+		.enable = tc6393xb_nand_enable,
+		.num_resources = ARRAY_SIZE(tc6393xb_nand_resources),
+		.resources = tc6393xb_nand_resources,
+	},
+	[TC6393XB_CELL_MMC] = {
+		.name = "tmio-mmc",
+		.enable = tc6393xb_mmc_enable,
+		.resume = tc6393xb_mmc_resume,
+		.platform_data = &tc6393xb_mmc_data,
+		.pdata_size    = sizeof(tc6393xb_mmc_data),
+		.num_resources = ARRAY_SIZE(tc6393xb_mmc_resources),
+		.resources = tc6393xb_mmc_resources,
+	},
+	[TC6393XB_CELL_OHCI] = {
+		.name = "tmio-ohci",
+		.num_resources = ARRAY_SIZE(tc6393xb_ohci_resources),
+		.resources = tc6393xb_ohci_resources,
+		.enable = tc6393xb_ohci_enable,
+		.suspend = tc6393xb_ohci_suspend,
+		.resume = tc6393xb_ohci_enable,
+		.disable = tc6393xb_ohci_disable,
+	},
+	[TC6393XB_CELL_FB] = {
+		.name = "tmio-fb",
+		.num_resources = ARRAY_SIZE(tc6393xb_fb_resources),
+		.resources = tc6393xb_fb_resources,
+		.enable = tc6393xb_fb_enable,
+		.suspend = tc6393xb_fb_disable,
+		.resume = tc6393xb_fb_enable,
+		.disable = tc6393xb_fb_disable,
+	},
+};
+
+/*--------------------------------------------------------------------------*/
+
+static int tc6393xb_gpio_get(struct gpio_chip *chip,
+		unsigned offset)
+{
+	struct tc6393xb *tc6393xb = container_of(chip, struct tc6393xb, gpio);
+
+	/* XXX: does dsr also represent inputs? */
+	return tmio_ioread8(tc6393xb->scr + SCR_GPO_DSR(offset / 8))
+		& TC_GPIO_BIT(offset);
+}
+
+static void __tc6393xb_gpio_set(struct gpio_chip *chip,
+		unsigned offset, int value)
+{
+	struct tc6393xb *tc6393xb = container_of(chip, struct tc6393xb, gpio);
+	u8  dsr;
+
+	dsr = tmio_ioread8(tc6393xb->scr + SCR_GPO_DSR(offset / 8));
+	if (value)
+		dsr |= TC_GPIO_BIT(offset);
+	else
+		dsr &= ~TC_GPIO_BIT(offset);
+
+	tmio_iowrite8(dsr, tc6393xb->scr + SCR_GPO_DSR(offset / 8));
+}
+
+static void tc6393xb_gpio_set(struct gpio_chip *chip,
+		unsigned offset, int value)
+{
+	struct tc6393xb *tc6393xb = container_of(chip, struct tc6393xb, gpio);
+	unsigned long flags;
+
+	spin_lock_irqsave(&tc6393xb->lock, flags);
+
+	__tc6393xb_gpio_set(chip, offset, value);
+
+	spin_unlock_irqrestore(&tc6393xb->lock, flags);
+}
+
+static int tc6393xb_gpio_direction_input(struct gpio_chip *chip,
+			unsigned offset)
+{
+	struct tc6393xb *tc6393xb = container_of(chip, struct tc6393xb, gpio);
+	unsigned long flags;
+	u8 doecr;
+
+	spin_lock_irqsave(&tc6393xb->lock, flags);
+
+	doecr = tmio_ioread8(tc6393xb->scr + SCR_GPO_DOECR(offset / 8));
+	doecr &= ~TC_GPIO_BIT(offset);
+	tmio_iowrite8(doecr, tc6393xb->scr + SCR_GPO_DOECR(offset / 8));
+
+	spin_unlock_irqrestore(&tc6393xb->lock, flags);
+
+	return 0;
+}
+
+static int tc6393xb_gpio_direction_output(struct gpio_chip *chip,
+			unsigned offset, int value)
+{
+	struct tc6393xb *tc6393xb = container_of(chip, struct tc6393xb, gpio);
+	unsigned long flags;
+	u8 doecr;
+
+	spin_lock_irqsave(&tc6393xb->lock, flags);
+
+	__tc6393xb_gpio_set(chip, offset, value);
+
+	doecr = tmio_ioread8(tc6393xb->scr + SCR_GPO_DOECR(offset / 8));
+	doecr |= TC_GPIO_BIT(offset);
+	tmio_iowrite8(doecr, tc6393xb->scr + SCR_GPO_DOECR(offset / 8));
+
+	spin_unlock_irqrestore(&tc6393xb->lock, flags);
+
+	return 0;
+}
+
+static int tc6393xb_register_gpio(struct tc6393xb *tc6393xb, int gpio_base)
+{
+	tc6393xb->gpio.label = "tc6393xb";
+	tc6393xb->gpio.base = gpio_base;
+	tc6393xb->gpio.ngpio = 16;
+	tc6393xb->gpio.set = tc6393xb_gpio_set;
+	tc6393xb->gpio.get = tc6393xb_gpio_get;
+	tc6393xb->gpio.direction_input = tc6393xb_gpio_direction_input;
+	tc6393xb->gpio.direction_output = tc6393xb_gpio_direction_output;
+
+	return gpiochip_add(&tc6393xb->gpio);
+}
+
+/*--------------------------------------------------------------------------*/
+
+static void
+tc6393xb_irq(unsigned int irq, struct irq_desc *desc)
+{
+	struct tc6393xb *tc6393xb = irq_get_handler_data(irq);
+	unsigned int isr;
+	unsigned int i, irq_base;
+
+	irq_base = tc6393xb->irq_base;
+
+	while ((isr = tmio_ioread8(tc6393xb->scr + SCR_ISR) &
+				~tmio_ioread8(tc6393xb->scr + SCR_IMR)))
+		for (i = 0; i < TC6393XB_NR_IRQS; i++) {
+			if (isr & (1 << i))
+				generic_handle_irq(irq_base + i);
+		}
+}
+
+static void tc6393xb_irq_ack(struct irq_data *data)
+{
+}
+
+static void tc6393xb_irq_mask(struct irq_data *data)
+{
+	struct tc6393xb *tc6393xb = irq_data_get_irq_chip_data(data);
+	unsigned long flags;
+	u8 imr;
+
+	spin_lock_irqsave(&tc6393xb->lock, flags);
+	imr = tmio_ioread8(tc6393xb->scr + SCR_IMR);
+	imr |= 1 << (data->irq - tc6393xb->irq_base);
+	tmio_iowrite8(imr, tc6393xb->scr + SCR_IMR);
+	spin_unlock_irqrestore(&tc6393xb->lock, flags);
+}
+
+static void tc6393xb_irq_unmask(struct irq_data *data)
+{
+	struct tc6393xb *tc6393xb = irq_data_get_irq_chip_data(data);
+	unsigned long flags;
+	u8 imr;
+
+	spin_lock_irqsave(&tc6393xb->lock, flags);
+	imr = tmio_ioread8(tc6393xb->scr + SCR_IMR);
+	imr &= ~(1 << (data->irq - tc6393xb->irq_base));
+	tmio_iowrite8(imr, tc6393xb->scr + SCR_IMR);
+	spin_unlock_irqrestore(&tc6393xb->lock, flags);
+}
+
+static struct irq_chip tc6393xb_chip = {
+	.name		= "tc6393xb",
+	.irq_ack	= tc6393xb_irq_ack,
+	.irq_mask	= tc6393xb_irq_mask,
+	.irq_unmask	= tc6393xb_irq_unmask,
+};
+
+static void tc6393xb_attach_irq(struct platform_device *dev)
+{
+	struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
+	unsigned int irq, irq_base;
+
+	irq_base = tc6393xb->irq_base;
+
+	for (irq = irq_base; irq < irq_base + TC6393XB_NR_IRQS; irq++) {
+		irq_set_chip_and_handler(irq, &tc6393xb_chip, handle_edge_irq);
+		irq_set_chip_data(irq, tc6393xb);
+		set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+	}
+
+	irq_set_irq_type(tc6393xb->irq, IRQ_TYPE_EDGE_FALLING);
+	irq_set_handler_data(tc6393xb->irq, tc6393xb);
+	irq_set_chained_handler(tc6393xb->irq, tc6393xb_irq);
+}
+
+static void tc6393xb_detach_irq(struct platform_device *dev)
+{
+	struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
+	unsigned int irq, irq_base;
+
+	irq_set_chained_handler(tc6393xb->irq, NULL);
+	irq_set_handler_data(tc6393xb->irq, NULL);
+
+	irq_base = tc6393xb->irq_base;
+
+	for (irq = irq_base; irq < irq_base + TC6393XB_NR_IRQS; irq++) {
+		set_irq_flags(irq, 0);
+		irq_set_chip(irq, NULL);
+		irq_set_chip_data(irq, NULL);
+	}
+}
+
+/*--------------------------------------------------------------------------*/
+
+static int __devinit tc6393xb_probe(struct platform_device *dev)
+{
+	struct tc6393xb_platform_data *tcpd = dev->dev.platform_data;
+	struct tc6393xb *tc6393xb;
+	struct resource *iomem, *rscr;
+	int ret, temp;
+
+	iomem = platform_get_resource(dev, IORESOURCE_MEM, 0);
+	if (!iomem)
+		return -EINVAL;
+
+	tc6393xb = kzalloc(sizeof *tc6393xb, GFP_KERNEL);
+	if (!tc6393xb) {
+		ret = -ENOMEM;
+		goto err_kzalloc;
+	}
+
+	spin_lock_init(&tc6393xb->lock);
+
+	platform_set_drvdata(dev, tc6393xb);
+
+	ret = platform_get_irq(dev, 0);
+	if (ret >= 0)
+		tc6393xb->irq = ret;
+	else
+		goto err_noirq;
+
+	tc6393xb->iomem = iomem;
+	tc6393xb->irq_base = tcpd->irq_base;
+
+	tc6393xb->clk = clk_get(&dev->dev, "CLK_CK3P6MI");
+	if (IS_ERR(tc6393xb->clk)) {
+		ret = PTR_ERR(tc6393xb->clk);
+		goto err_clk_get;
+	}
+
+	rscr = &tc6393xb->rscr;
+	rscr->name = "tc6393xb-core";
+	rscr->start = iomem->start;
+	rscr->end = iomem->start + 0xff;
+	rscr->flags = IORESOURCE_MEM;
+
+	ret = request_resource(iomem, rscr);
+	if (ret)
+		goto err_request_scr;
+
+	tc6393xb->scr = ioremap(rscr->start, resource_size(rscr));
+	if (!tc6393xb->scr) {
+		ret = -ENOMEM;
+		goto err_ioremap;
+	}
+
+	ret = clk_enable(tc6393xb->clk);
+	if (ret)
+		goto err_clk_enable;
+
+	ret = tcpd->enable(dev);
+	if (ret)
+		goto err_enable;
+
+	iowrite8(0,				tc6393xb->scr + SCR_FER);
+	iowrite16(tcpd->scr_pll2cr,		tc6393xb->scr + SCR_PLL2CR);
+	iowrite16(SCR_CCR_UNK1 | SCR_CCR_HCLK_48,
+						tc6393xb->scr + SCR_CCR);
+	iowrite16(SCR_MCR_RDY_OPENDRAIN | SCR_MCR_RDY_UNK | SCR_MCR_RDY_EN |
+		  SCR_MCR_INT_OPENDRAIN | SCR_MCR_INT_UNK | SCR_MCR_INT_EN |
+		  BIT(15),			tc6393xb->scr + SCR_MCR);
+	iowrite16(tcpd->scr_gper,		tc6393xb->scr + SCR_GPER);
+	iowrite8(0,				tc6393xb->scr + SCR_IRR);
+	iowrite8(0xbf,				tc6393xb->scr + SCR_IMR);
+
+	printk(KERN_INFO "Toshiba tc6393xb revision %d at 0x%08lx, irq %d\n",
+			tmio_ioread8(tc6393xb->scr + SCR_REVID),
+			(unsigned long) iomem->start, tc6393xb->irq);
+
+	tc6393xb->gpio.base = -1;
+
+	if (tcpd->gpio_base >= 0) {
+		ret = tc6393xb_register_gpio(tc6393xb, tcpd->gpio_base);
+		if (ret)
+			goto err_gpio_add;
+	}
+
+	tc6393xb_attach_irq(dev);
+
+	if (tcpd->setup) {
+		ret = tcpd->setup(dev);
+		if (ret)
+			goto err_setup;
+	}
+
+	tc6393xb_cells[TC6393XB_CELL_NAND].platform_data = tcpd->nand_data;
+	tc6393xb_cells[TC6393XB_CELL_NAND].pdata_size =
+						sizeof(*tcpd->nand_data);
+	tc6393xb_cells[TC6393XB_CELL_FB].platform_data = tcpd->fb_data;
+	tc6393xb_cells[TC6393XB_CELL_FB].pdata_size = sizeof(*tcpd->fb_data);
+
+	ret = mfd_add_devices(&dev->dev, dev->id,
+			tc6393xb_cells, ARRAY_SIZE(tc6393xb_cells),
+			iomem, tcpd->irq_base);
+
+	if (!ret)
+		return 0;
+
+	if (tcpd->teardown)
+		tcpd->teardown(dev);
+
+err_setup:
+	tc6393xb_detach_irq(dev);
+
+err_gpio_add:
+	if (tc6393xb->gpio.base != -1)
+		temp = gpiochip_remove(&tc6393xb->gpio);
+	tcpd->disable(dev);
+err_enable:
+	clk_disable(tc6393xb->clk);
+err_clk_enable:
+	iounmap(tc6393xb->scr);
+err_ioremap:
+	release_resource(&tc6393xb->rscr);
+err_request_scr:
+	clk_put(tc6393xb->clk);
+err_noirq:
+err_clk_get:
+	kfree(tc6393xb);
+err_kzalloc:
+	return ret;
+}
+
+static int __devexit tc6393xb_remove(struct platform_device *dev)
+{
+	struct tc6393xb_platform_data *tcpd = dev->dev.platform_data;
+	struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
+	int ret;
+
+	mfd_remove_devices(&dev->dev);
+
+	if (tcpd->teardown)
+		tcpd->teardown(dev);
+
+	tc6393xb_detach_irq(dev);
+
+	if (tc6393xb->gpio.base != -1) {
+		ret = gpiochip_remove(&tc6393xb->gpio);
+		if (ret) {
+			dev_err(&dev->dev, "Can't remove gpio chip: %d\n", ret);
+			return ret;
+		}
+	}
+
+	ret = tcpd->disable(dev);
+	clk_disable(tc6393xb->clk);
+	iounmap(tc6393xb->scr);
+	release_resource(&tc6393xb->rscr);
+	platform_set_drvdata(dev, NULL);
+	clk_put(tc6393xb->clk);
+	kfree(tc6393xb);
+
+	return ret;
+}
+
+#ifdef CONFIG_PM
+static int tc6393xb_suspend(struct platform_device *dev, pm_message_t state)
+{
+	struct tc6393xb_platform_data *tcpd = dev->dev.platform_data;
+	struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
+	int i, ret;
+
+	tc6393xb->suspend_state.ccr = ioread16(tc6393xb->scr + SCR_CCR);
+	tc6393xb->suspend_state.fer = ioread8(tc6393xb->scr + SCR_FER);
+
+	for (i = 0; i < 3; i++) {
+		tc6393xb->suspend_state.gpo_dsr[i] =
+			ioread8(tc6393xb->scr + SCR_GPO_DSR(i));
+		tc6393xb->suspend_state.gpo_doecr[i] =
+			ioread8(tc6393xb->scr + SCR_GPO_DOECR(i));
+		tc6393xb->suspend_state.gpi_bcr[i] =
+			ioread8(tc6393xb->scr + SCR_GPI_BCR(i));
+	}
+	ret = tcpd->suspend(dev);
+	clk_disable(tc6393xb->clk);
+
+	return ret;
+}
+
+static int tc6393xb_resume(struct platform_device *dev)
+{
+	struct tc6393xb_platform_data *tcpd = dev->dev.platform_data;
+	struct tc6393xb *tc6393xb = platform_get_drvdata(dev);
+	int ret;
+	int i;
+
+	clk_enable(tc6393xb->clk);
+
+	ret = tcpd->resume(dev);
+	if (ret)
+		return ret;
+
+	if (!tcpd->resume_restore)
+		return 0;
+
+	iowrite8(tc6393xb->suspend_state.fer,	tc6393xb->scr + SCR_FER);
+	iowrite16(tcpd->scr_pll2cr,		tc6393xb->scr + SCR_PLL2CR);
+	iowrite16(tc6393xb->suspend_state.ccr,	tc6393xb->scr + SCR_CCR);
+	iowrite16(SCR_MCR_RDY_OPENDRAIN | SCR_MCR_RDY_UNK | SCR_MCR_RDY_EN |
+		  SCR_MCR_INT_OPENDRAIN | SCR_MCR_INT_UNK | SCR_MCR_INT_EN |
+		  BIT(15),			tc6393xb->scr + SCR_MCR);
+	iowrite16(tcpd->scr_gper,		tc6393xb->scr + SCR_GPER);
+	iowrite8(0,				tc6393xb->scr + SCR_IRR);
+	iowrite8(0xbf,				tc6393xb->scr + SCR_IMR);
+
+	for (i = 0; i < 3; i++) {
+		iowrite8(tc6393xb->suspend_state.gpo_dsr[i],
+					tc6393xb->scr + SCR_GPO_DSR(i));
+		iowrite8(tc6393xb->suspend_state.gpo_doecr[i],
+					tc6393xb->scr + SCR_GPO_DOECR(i));
+		iowrite8(tc6393xb->suspend_state.gpi_bcr[i],
+					tc6393xb->scr + SCR_GPI_BCR(i));
+	}
+
+	return 0;
+}
+#else
+#define tc6393xb_suspend NULL
+#define tc6393xb_resume NULL
+#endif
+
+static struct platform_driver tc6393xb_driver = {
+	.probe = tc6393xb_probe,
+	.remove = __devexit_p(tc6393xb_remove),
+	.suspend = tc6393xb_suspend,
+	.resume = tc6393xb_resume,
+
+	.driver = {
+		.name = "tc6393xb",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init tc6393xb_init(void)
+{
+	return platform_driver_register(&tc6393xb_driver);
+}
+
+static void __exit tc6393xb_exit(void)
+{
+	platform_driver_unregister(&tc6393xb_driver);
+}
+
+subsys_initcall(tc6393xb_init);
+module_exit(tc6393xb_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Ian Molton, Dmitry Baryshkov and Dirk Opfer");
+MODULE_DESCRIPTION("tc6393xb Toshiba Mobile IO Controller");
+MODULE_ALIAS("platform:tc6393xb");
+
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/ti-ssp.c b/ap/os/linux/linux-3.4.x/drivers/mfd/ti-ssp.c
new file mode 100644
index 0000000..4fb0e6c
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/ti-ssp.c
@@ -0,0 +1,466 @@
+/*
+ * Sequencer Serial Port (SSP) driver for Texas Instruments' SoCs
+ *
+ * Copyright (C) 2010 Texas Instruments Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/wait.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/ti_ssp.h>
+
+/* Register Offsets */
+#define REG_REV		0x00
+#define REG_IOSEL_1	0x04
+#define REG_IOSEL_2	0x08
+#define REG_PREDIV	0x0c
+#define REG_INTR_ST	0x10
+#define REG_INTR_EN	0x14
+#define REG_TEST_CTRL	0x18
+
+/* Per port registers */
+#define PORT_CFG_2	0x00
+#define PORT_ADDR	0x04
+#define PORT_DATA	0x08
+#define PORT_CFG_1	0x0c
+#define PORT_STATE	0x10
+
+#define SSP_PORT_CONFIG_MASK	(SSP_EARLY_DIN | SSP_DELAY_DOUT)
+#define SSP_PORT_CLKRATE_MASK	0x0f
+
+#define SSP_SEQRAM_WR_EN	BIT(4)
+#define SSP_SEQRAM_RD_EN	BIT(5)
+#define SSP_START		BIT(15)
+#define SSP_BUSY		BIT(10)
+#define SSP_PORT_ASL		BIT(7)
+#define SSP_PORT_CFO1		BIT(6)
+
+#define SSP_PORT_SEQRAM_SIZE	32
+
+static const int ssp_port_base[]   = {0x040, 0x080};
+static const int ssp_port_seqram[] = {0x100, 0x180};
+
+struct ti_ssp {
+	struct resource		*res;
+	struct device		*dev;
+	void __iomem		*regs;
+	spinlock_t		lock;
+	struct clk		*clk;
+	int			irq;
+	wait_queue_head_t	wqh;
+
+	/*
+	 * Some of the iosel2 register bits always read-back as 0, we need to
+	 * remember these values so that we don't clobber previously set
+	 * values.
+	 */
+	u32			iosel2;
+};
+
+static inline struct ti_ssp *dev_to_ssp(struct device *dev)
+{
+	return dev_get_drvdata(dev->parent);
+}
+
+static inline int dev_to_port(struct device *dev)
+{
+	return to_platform_device(dev)->id;
+}
+
+/* Register Access Helpers, rmw() functions need to run locked */
+static inline u32 ssp_read(struct ti_ssp *ssp, int reg)
+{
+	return __raw_readl(ssp->regs + reg);
+}
+
+static inline void ssp_write(struct ti_ssp *ssp, int reg, u32 val)
+{
+	__raw_writel(val, ssp->regs + reg);
+}
+
+static inline void ssp_rmw(struct ti_ssp *ssp, int reg, u32 mask, u32 bits)
+{
+	ssp_write(ssp, reg, (ssp_read(ssp, reg) & ~mask) | bits);
+}
+
+static inline u32 ssp_port_read(struct ti_ssp *ssp, int port, int reg)
+{
+	return ssp_read(ssp, ssp_port_base[port] + reg);
+}
+
+static inline void ssp_port_write(struct ti_ssp *ssp, int port, int reg,
+				  u32 val)
+{
+	ssp_write(ssp, ssp_port_base[port] + reg, val);
+}
+
+static inline void ssp_port_rmw(struct ti_ssp *ssp, int port, int reg,
+				u32 mask, u32 bits)
+{
+	ssp_rmw(ssp, ssp_port_base[port] + reg, mask, bits);
+}
+
+static inline void ssp_port_clr_bits(struct ti_ssp *ssp, int port, int reg,
+				     u32 bits)
+{
+	ssp_port_rmw(ssp, port, reg, bits, 0);
+}
+
+static inline void ssp_port_set_bits(struct ti_ssp *ssp, int port, int reg,
+				     u32 bits)
+{
+	ssp_port_rmw(ssp, port, reg, 0, bits);
+}
+
+/* Called to setup port clock mode, caller must hold ssp->lock */
+static int __set_mode(struct ti_ssp *ssp, int port, int mode)
+{
+	mode &= SSP_PORT_CONFIG_MASK;
+	ssp_port_rmw(ssp, port, PORT_CFG_1, SSP_PORT_CONFIG_MASK, mode);
+
+	return 0;
+}
+
+int ti_ssp_set_mode(struct device *dev, int mode)
+{
+	struct ti_ssp *ssp = dev_to_ssp(dev);
+	int port = dev_to_port(dev);
+	int ret;
+
+	spin_lock(&ssp->lock);
+	ret = __set_mode(ssp, port, mode);
+	spin_unlock(&ssp->lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(ti_ssp_set_mode);
+
+/* Called to setup iosel2, caller must hold ssp->lock */
+static void __set_iosel2(struct ti_ssp *ssp, u32 mask, u32 val)
+{
+	ssp->iosel2 = (ssp->iosel2 & ~mask) | val;
+	ssp_write(ssp, REG_IOSEL_2, ssp->iosel2);
+}
+
+/* Called to setup port iosel, caller must hold ssp->lock */
+static void __set_iosel(struct ti_ssp *ssp, int port, u32 iosel)
+{
+	unsigned val, shift = port ? 16 : 0;
+
+	/* IOSEL1 gets the least significant 16 bits */
+	val = ssp_read(ssp, REG_IOSEL_1);
+	val &= 0xffff << (port ? 0 : 16);
+	val |= (iosel & 0xffff) << (port ? 16 : 0);
+	ssp_write(ssp, REG_IOSEL_1, val);
+
+	/* IOSEL2 gets the most significant 16 bits */
+	val = (iosel >> 16) & 0x7;
+	__set_iosel2(ssp, 0x7 << shift, val << shift);
+}
+
+int ti_ssp_set_iosel(struct device *dev, u32 iosel)
+{
+	struct ti_ssp *ssp = dev_to_ssp(dev);
+	int port = dev_to_port(dev);
+
+	spin_lock(&ssp->lock);
+	__set_iosel(ssp, port, iosel);
+	spin_unlock(&ssp->lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(ti_ssp_set_iosel);
+
+int ti_ssp_load(struct device *dev, int offs, u32* prog, int len)
+{
+	struct ti_ssp *ssp = dev_to_ssp(dev);
+	int port = dev_to_port(dev);
+	int i;
+
+	if (len > SSP_PORT_SEQRAM_SIZE)
+		return -ENOSPC;
+
+	spin_lock(&ssp->lock);
+
+	/* Enable SeqRAM access */
+	ssp_port_set_bits(ssp, port, PORT_CFG_2, SSP_SEQRAM_WR_EN);
+
+	/* Copy code */
+	for (i = 0; i < len; i++) {
+		__raw_writel(prog[i], ssp->regs + offs + 4*i +
+			     ssp_port_seqram[port]);
+	}
+
+	/* Disable SeqRAM access */
+	ssp_port_clr_bits(ssp, port, PORT_CFG_2, SSP_SEQRAM_WR_EN);
+
+	spin_unlock(&ssp->lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(ti_ssp_load);
+
+int ti_ssp_raw_read(struct device *dev)
+{
+	struct ti_ssp *ssp = dev_to_ssp(dev);
+	int port = dev_to_port(dev);
+	int shift = port ? 27 : 11;
+
+	return (ssp_read(ssp, REG_IOSEL_2) >> shift) & 0xf;
+}
+EXPORT_SYMBOL(ti_ssp_raw_read);
+
+int ti_ssp_raw_write(struct device *dev, u32 val)
+{
+	struct ti_ssp *ssp = dev_to_ssp(dev);
+	int port = dev_to_port(dev), shift;
+
+	spin_lock(&ssp->lock);
+
+	shift = port ? 22 : 6;
+	val &= 0xf;
+	__set_iosel2(ssp, 0xf << shift, val << shift);
+
+	spin_unlock(&ssp->lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(ti_ssp_raw_write);
+
+static inline int __xfer_done(struct ti_ssp *ssp, int port)
+{
+	return !(ssp_port_read(ssp, port, PORT_CFG_1) & SSP_BUSY);
+}
+
+int ti_ssp_run(struct device *dev, u32 pc, u32 input, u32 *output)
+{
+	struct ti_ssp *ssp = dev_to_ssp(dev);
+	int port = dev_to_port(dev);
+	int ret;
+
+	if (pc & ~(0x3f))
+		return -EINVAL;
+
+	/* Grab ssp->lock to serialize rmw on ssp registers */
+	spin_lock(&ssp->lock);
+
+	ssp_port_write(ssp, port, PORT_ADDR, input >> 16);
+	ssp_port_write(ssp, port, PORT_DATA, input & 0xffff);
+	ssp_port_rmw(ssp, port, PORT_CFG_1, 0x3f, pc);
+
+	/* grab wait queue head lock to avoid race with the isr */
+	spin_lock_irq(&ssp->wqh.lock);
+
+	/* kick off sequence execution in hardware */
+	ssp_port_set_bits(ssp, port, PORT_CFG_1, SSP_START);
+
+	/* drop ssp lock; no register writes beyond this */
+	spin_unlock(&ssp->lock);
+
+	ret = wait_event_interruptible_locked_irq(ssp->wqh,
+						  __xfer_done(ssp, port));
+	spin_unlock_irq(&ssp->wqh.lock);
+
+	if (ret < 0)
+		return ret;
+
+	if (output) {
+		*output = (ssp_port_read(ssp, port, PORT_ADDR) << 16) |
+			  (ssp_port_read(ssp, port, PORT_DATA) &  0xffff);
+	}
+
+	ret = ssp_port_read(ssp, port, PORT_STATE) & 0x3f; /* stop address */
+
+	return ret;
+}
+EXPORT_SYMBOL(ti_ssp_run);
+
+static irqreturn_t ti_ssp_interrupt(int irq, void *dev_data)
+{
+	struct ti_ssp *ssp = dev_data;
+
+	spin_lock(&ssp->wqh.lock);
+
+	ssp_write(ssp, REG_INTR_ST, 0x3);
+	wake_up_locked(&ssp->wqh);
+
+	spin_unlock(&ssp->wqh.lock);
+
+	return IRQ_HANDLED;
+}
+
+static int __devinit ti_ssp_probe(struct platform_device *pdev)
+{
+	static struct ti_ssp *ssp;
+	const struct ti_ssp_data *pdata = pdev->dev.platform_data;
+	int error = 0, prediv = 0xff, id;
+	unsigned long sysclk;
+	struct device *dev = &pdev->dev;
+	struct mfd_cell cells[2];
+
+	ssp = kzalloc(sizeof(*ssp), GFP_KERNEL);
+	if (!ssp) {
+		dev_err(dev, "cannot allocate device info\n");
+		return -ENOMEM;
+	}
+
+	ssp->dev = dev;
+	dev_set_drvdata(dev, ssp);
+
+	ssp->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!ssp->res) {
+		error = -ENODEV;
+		dev_err(dev, "cannot determine register area\n");
+		goto error_res;
+	}
+
+	if (!request_mem_region(ssp->res->start, resource_size(ssp->res),
+				pdev->name)) {
+		error = -ENOMEM;
+		dev_err(dev, "cannot claim register memory\n");
+		goto error_res;
+	}
+
+	ssp->regs = ioremap(ssp->res->start, resource_size(ssp->res));
+	if (!ssp->regs) {
+		error = -ENOMEM;
+		dev_err(dev, "cannot map register memory\n");
+		goto error_map;
+	}
+
+	ssp->clk = clk_get(dev, NULL);
+	if (IS_ERR(ssp->clk)) {
+		error = PTR_ERR(ssp->clk);
+		dev_err(dev, "cannot claim device clock\n");
+		goto error_clk;
+	}
+
+	ssp->irq = platform_get_irq(pdev, 0);
+	if (ssp->irq < 0) {
+		error = -ENODEV;
+		dev_err(dev, "unknown irq\n");
+		goto error_irq;
+	}
+
+	error = request_threaded_irq(ssp->irq, NULL, ti_ssp_interrupt, 0,
+				     dev_name(dev), ssp);
+	if (error < 0) {
+		dev_err(dev, "cannot acquire irq\n");
+		goto error_irq;
+	}
+
+	spin_lock_init(&ssp->lock);
+	init_waitqueue_head(&ssp->wqh);
+
+	/* Power on and initialize SSP */
+	error = clk_enable(ssp->clk);
+	if (error) {
+		dev_err(dev, "cannot enable device clock\n");
+		goto error_enable;
+	}
+
+	/* Reset registers to a sensible known state */
+	ssp_write(ssp, REG_IOSEL_1, 0);
+	ssp_write(ssp, REG_IOSEL_2, 0);
+	ssp_write(ssp, REG_INTR_EN, 0x3);
+	ssp_write(ssp, REG_INTR_ST, 0x3);
+	ssp_write(ssp, REG_TEST_CTRL, 0);
+	ssp_port_write(ssp, 0, PORT_CFG_1, SSP_PORT_ASL);
+	ssp_port_write(ssp, 1, PORT_CFG_1, SSP_PORT_ASL);
+	ssp_port_write(ssp, 0, PORT_CFG_2, SSP_PORT_CFO1);
+	ssp_port_write(ssp, 1, PORT_CFG_2, SSP_PORT_CFO1);
+
+	sysclk = clk_get_rate(ssp->clk);
+	if (pdata && pdata->out_clock)
+		prediv = (sysclk / pdata->out_clock) - 1;
+	prediv = clamp(prediv, 0, 0xff);
+	ssp_rmw(ssp, REG_PREDIV, 0xff, prediv);
+
+	memset(cells, 0, sizeof(cells));
+	for (id = 0; id < 2; id++) {
+		const struct ti_ssp_dev_data *data = &pdata->dev_data[id];
+
+		cells[id].id		= id;
+		cells[id].name		= data->dev_name;
+		cells[id].platform_data	= data->pdata;
+		cells[id].data_size	= data->pdata_size;
+	}
+
+	error = mfd_add_devices(dev, 0, cells, 2, NULL, 0);
+	if (error < 0) {
+		dev_err(dev, "cannot add mfd cells\n");
+		goto error_enable;
+	}
+
+	return 0;
+
+error_enable:
+	free_irq(ssp->irq, ssp);
+error_irq:
+	clk_put(ssp->clk);
+error_clk:
+	iounmap(ssp->regs);
+error_map:
+	release_mem_region(ssp->res->start, resource_size(ssp->res));
+error_res:
+	kfree(ssp);
+	return error;
+}
+
+static int __devexit ti_ssp_remove(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct ti_ssp *ssp = dev_get_drvdata(dev);
+
+	mfd_remove_devices(dev);
+	clk_disable(ssp->clk);
+	free_irq(ssp->irq, ssp);
+	clk_put(ssp->clk);
+	iounmap(ssp->regs);
+	release_mem_region(ssp->res->start, resource_size(ssp->res));
+	kfree(ssp);
+	dev_set_drvdata(dev, NULL);
+	return 0;
+}
+
+static struct platform_driver ti_ssp_driver = {
+	.probe		= ti_ssp_probe,
+	.remove		= __devexit_p(ti_ssp_remove),
+	.driver		= {
+		.name	= "ti-ssp",
+		.owner	= THIS_MODULE,
+	}
+};
+
+module_platform_driver(ti_ssp_driver);
+
+MODULE_DESCRIPTION("Sequencer Serial Port (SSP) Driver");
+MODULE_AUTHOR("Cyril Chemparathy");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:ti-ssp");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/timberdale.c b/ap/os/linux/linux-3.4.x/drivers/mfd/timberdale.c
new file mode 100644
index 0000000..0ba26fb
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/timberdale.c
@@ -0,0 +1,904 @@
+/*
+ * timberdale.c timberdale FPGA MFD driver
+ * Copyright (c) 2009 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Supports:
+ * Timberdale FPGA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/msi.h>
+#include <linux/mfd/core.h>
+#include <linux/slab.h>
+
+#include <linux/timb_gpio.h>
+
+#include <linux/i2c.h>
+#include <linux/i2c-ocores.h>
+#include <linux/i2c-xiic.h>
+#include <linux/i2c/tsc2007.h>
+
+#include <linux/spi/spi.h>
+#include <linux/spi/xilinx_spi.h>
+#include <linux/spi/max7301.h>
+#include <linux/spi/mc33880.h>
+
+#include <media/timb_radio.h>
+#include <media/timb_video.h>
+
+#include <linux/timb_dma.h>
+
+#include <linux/ks8842.h>
+
+#include "timberdale.h"
+
+#define DRIVER_NAME "timberdale"
+
+struct timberdale_device {
+	resource_size_t		ctl_mapbase;
+	unsigned char __iomem   *ctl_membase;
+	struct {
+		u32 major;
+		u32 minor;
+		u32 config;
+	} fw;
+};
+
+/*--------------------------------------------------------------------------*/
+
+static struct tsc2007_platform_data timberdale_tsc2007_platform_data = {
+	.model = 2003,
+	.x_plate_ohms = 100
+};
+
+static struct i2c_board_info timberdale_i2c_board_info[] = {
+	{
+		I2C_BOARD_INFO("tsc2007", 0x48),
+		.platform_data = &timberdale_tsc2007_platform_data,
+		.irq = IRQ_TIMBERDALE_TSC_INT
+	},
+};
+
+static __devinitdata struct xiic_i2c_platform_data
+timberdale_xiic_platform_data = {
+	.devices = timberdale_i2c_board_info,
+	.num_devices = ARRAY_SIZE(timberdale_i2c_board_info)
+};
+
+static __devinitdata struct ocores_i2c_platform_data
+timberdale_ocores_platform_data = {
+	.regstep = 4,
+	.clock_khz = 62500,
+	.devices = timberdale_i2c_board_info,
+	.num_devices = ARRAY_SIZE(timberdale_i2c_board_info)
+};
+
+static const __devinitconst struct resource timberdale_xiic_resources[] = {
+	{
+		.start	= XIICOFFSET,
+		.end	= XIICEND,
+		.flags	= IORESOURCE_MEM,
+	},
+	{
+		.start	= IRQ_TIMBERDALE_I2C,
+		.end	= IRQ_TIMBERDALE_I2C,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static const __devinitconst struct resource timberdale_ocores_resources[] = {
+	{
+		.start	= OCORESOFFSET,
+		.end	= OCORESEND,
+		.flags	= IORESOURCE_MEM,
+	},
+	{
+		.start 	= IRQ_TIMBERDALE_I2C,
+		.end	= IRQ_TIMBERDALE_I2C,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+const struct max7301_platform_data timberdale_max7301_platform_data = {
+	.base = 200
+};
+
+const struct mc33880_platform_data timberdale_mc33880_platform_data = {
+	.base = 100
+};
+
+static struct spi_board_info timberdale_spi_16bit_board_info[] = {
+	{
+		.modalias = "max7301",
+		.max_speed_hz = 26000,
+		.chip_select = 2,
+		.mode = SPI_MODE_0,
+		.platform_data = &timberdale_max7301_platform_data
+	},
+};
+
+static struct spi_board_info timberdale_spi_8bit_board_info[] = {
+	{
+		.modalias = "mc33880",
+		.max_speed_hz = 4000,
+		.chip_select = 1,
+		.mode = SPI_MODE_1,
+		.platform_data = &timberdale_mc33880_platform_data
+	},
+};
+
+static __devinitdata struct xspi_platform_data timberdale_xspi_platform_data = {
+	.num_chipselect = 3,
+	.little_endian = true,
+	/* bits per word and devices will be filled in runtime depending
+	 * on the HW config
+	 */
+};
+
+static const __devinitconst struct resource timberdale_spi_resources[] = {
+	{
+		.start 	= SPIOFFSET,
+		.end	= SPIEND,
+		.flags	= IORESOURCE_MEM,
+	},
+	{
+		.start	= IRQ_TIMBERDALE_SPI,
+		.end	= IRQ_TIMBERDALE_SPI,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static __devinitdata struct ks8842_platform_data
+	timberdale_ks8842_platform_data = {
+	.rx_dma_channel = DMA_ETH_RX,
+	.tx_dma_channel = DMA_ETH_TX
+};
+
+static const __devinitconst struct resource timberdale_eth_resources[] = {
+	{
+		.start	= ETHOFFSET,
+		.end	= ETHEND,
+		.flags	= IORESOURCE_MEM,
+	},
+	{
+		.start	= IRQ_TIMBERDALE_ETHSW_IF,
+		.end	= IRQ_TIMBERDALE_ETHSW_IF,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static __devinitdata struct timbgpio_platform_data
+	timberdale_gpio_platform_data = {
+	.gpio_base = 0,
+	.nr_pins = GPIO_NR_PINS,
+	.irq_base = 200,
+};
+
+static const __devinitconst struct resource timberdale_gpio_resources[] = {
+	{
+		.start	= GPIOOFFSET,
+		.end	= GPIOEND,
+		.flags	= IORESOURCE_MEM,
+	},
+	{
+		.start	= IRQ_TIMBERDALE_GPIO,
+		.end	= IRQ_TIMBERDALE_GPIO,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static const __devinitconst struct resource timberdale_mlogicore_resources[] = {
+	{
+		.start	= MLCOREOFFSET,
+		.end	= MLCOREEND,
+		.flags	= IORESOURCE_MEM,
+	},
+	{
+		.start	= IRQ_TIMBERDALE_MLCORE,
+		.end	= IRQ_TIMBERDALE_MLCORE,
+		.flags	= IORESOURCE_IRQ,
+	},
+	{
+		.start	= IRQ_TIMBERDALE_MLCORE_BUF,
+		.end	= IRQ_TIMBERDALE_MLCORE_BUF,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static const __devinitconst struct resource timberdale_uart_resources[] = {
+	{
+		.start	= UARTOFFSET,
+		.end	= UARTEND,
+		.flags	= IORESOURCE_MEM,
+	},
+	{
+		.start	= IRQ_TIMBERDALE_UART,
+		.end	= IRQ_TIMBERDALE_UART,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static const __devinitconst struct resource timberdale_uartlite_resources[] = {
+	{
+		.start	= UARTLITEOFFSET,
+		.end	= UARTLITEEND,
+		.flags	= IORESOURCE_MEM,
+	},
+	{
+		.start	= IRQ_TIMBERDALE_UARTLITE,
+		.end	= IRQ_TIMBERDALE_UARTLITE,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static __devinitdata struct i2c_board_info timberdale_adv7180_i2c_board_info = {
+	/* Requires jumper JP9 to be off */
+	I2C_BOARD_INFO("adv7180", 0x42 >> 1),
+	.irq = IRQ_TIMBERDALE_ADV7180
+};
+
+static __devinitdata struct timb_video_platform_data
+	timberdale_video_platform_data = {
+	.dma_channel = DMA_VIDEO_RX,
+	.i2c_adapter = 0,
+	.encoder = {
+		.info = &timberdale_adv7180_i2c_board_info
+	}
+};
+
+static const __devinitconst struct resource
+timberdale_radio_resources[] = {
+	{
+		.start	= RDSOFFSET,
+		.end	= RDSEND,
+		.flags	= IORESOURCE_MEM,
+	},
+	{
+		.start	= IRQ_TIMBERDALE_RDS,
+		.end	= IRQ_TIMBERDALE_RDS,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static __devinitdata struct i2c_board_info timberdale_tef6868_i2c_board_info = {
+	I2C_BOARD_INFO("tef6862", 0x60)
+};
+
+static __devinitdata struct i2c_board_info timberdale_saa7706_i2c_board_info = {
+	I2C_BOARD_INFO("saa7706h", 0x1C)
+};
+
+static __devinitdata struct timb_radio_platform_data
+	timberdale_radio_platform_data = {
+	.i2c_adapter = 0,
+	.tuner = &timberdale_tef6868_i2c_board_info,
+	.dsp = &timberdale_saa7706_i2c_board_info
+};
+
+static const __devinitconst struct resource timberdale_video_resources[] = {
+	{
+		.start	= LOGIWOFFSET,
+		.end	= LOGIWEND,
+		.flags	= IORESOURCE_MEM,
+	},
+	/*
+	note that the "frame buffer" is located in DMA area
+	starting at 0x1200000
+	*/
+};
+
+static __devinitdata struct timb_dma_platform_data timb_dma_platform_data = {
+	.nr_channels = 10,
+	.channels = {
+		{
+			/* UART RX */
+			.rx = true,
+			.descriptors = 2,
+			.descriptor_elements = 1
+		},
+		{
+			/* UART TX */
+			.rx = false,
+			.descriptors = 2,
+			.descriptor_elements = 1
+		},
+		{
+			/* MLB RX */
+			.rx = true,
+			.descriptors = 2,
+			.descriptor_elements = 1
+		},
+		{
+			/* MLB TX */
+			.rx = false,
+			.descriptors = 2,
+			.descriptor_elements = 1
+		},
+		{
+			/* Video RX */
+			.rx = true,
+			.bytes_per_line = 1440,
+			.descriptors = 2,
+			.descriptor_elements = 16
+		},
+		{
+			/* Video framedrop */
+		},
+		{
+			/* SDHCI RX */
+			.rx = true,
+		},
+		{
+			/* SDHCI TX */
+		},
+		{
+			/* ETH RX */
+			.rx = true,
+			.descriptors = 2,
+			.descriptor_elements = 1
+		},
+		{
+			/* ETH TX */
+			.rx = false,
+			.descriptors = 2,
+			.descriptor_elements = 1
+		},
+	}
+};
+
+static const __devinitconst struct resource timberdale_dma_resources[] = {
+	{
+		.start	= DMAOFFSET,
+		.end	= DMAEND,
+		.flags	= IORESOURCE_MEM,
+	},
+	{
+		.start	= IRQ_TIMBERDALE_DMA,
+		.end	= IRQ_TIMBERDALE_DMA,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg0[] = {
+	{
+		.name = "timb-dma",
+		.num_resources = ARRAY_SIZE(timberdale_dma_resources),
+		.resources = timberdale_dma_resources,
+		.platform_data = &timb_dma_platform_data,
+		.pdata_size = sizeof(timb_dma_platform_data),
+	},
+	{
+		.name = "timb-uart",
+		.num_resources = ARRAY_SIZE(timberdale_uart_resources),
+		.resources = timberdale_uart_resources,
+	},
+	{
+		.name = "xiic-i2c",
+		.num_resources = ARRAY_SIZE(timberdale_xiic_resources),
+		.resources = timberdale_xiic_resources,
+		.platform_data = &timberdale_xiic_platform_data,
+		.pdata_size = sizeof(timberdale_xiic_platform_data),
+	},
+	{
+		.name = "timb-gpio",
+		.num_resources = ARRAY_SIZE(timberdale_gpio_resources),
+		.resources = timberdale_gpio_resources,
+		.platform_data = &timberdale_gpio_platform_data,
+		.pdata_size = sizeof(timberdale_gpio_platform_data),
+	},
+	{
+		.name = "timb-video",
+		.num_resources = ARRAY_SIZE(timberdale_video_resources),
+		.resources = timberdale_video_resources,
+		.platform_data = &timberdale_video_platform_data,
+		.pdata_size = sizeof(timberdale_video_platform_data),
+	},
+	{
+		.name = "timb-radio",
+		.num_resources = ARRAY_SIZE(timberdale_radio_resources),
+		.resources = timberdale_radio_resources,
+		.platform_data = &timberdale_radio_platform_data,
+		.pdata_size = sizeof(timberdale_radio_platform_data),
+	},
+	{
+		.name = "xilinx_spi",
+		.num_resources = ARRAY_SIZE(timberdale_spi_resources),
+		.resources = timberdale_spi_resources,
+		.platform_data = &timberdale_xspi_platform_data,
+		.pdata_size = sizeof(timberdale_xspi_platform_data),
+	},
+	{
+		.name = "ks8842",
+		.num_resources = ARRAY_SIZE(timberdale_eth_resources),
+		.resources = timberdale_eth_resources,
+		.platform_data = &timberdale_ks8842_platform_data,
+		.pdata_size = sizeof(timberdale_ks8842_platform_data),
+	},
+};
+
+static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg1[] = {
+	{
+		.name = "timb-dma",
+		.num_resources = ARRAY_SIZE(timberdale_dma_resources),
+		.resources = timberdale_dma_resources,
+		.platform_data = &timb_dma_platform_data,
+		.pdata_size = sizeof(timb_dma_platform_data),
+	},
+	{
+		.name = "timb-uart",
+		.num_resources = ARRAY_SIZE(timberdale_uart_resources),
+		.resources = timberdale_uart_resources,
+	},
+	{
+		.name = "uartlite",
+		.num_resources = ARRAY_SIZE(timberdale_uartlite_resources),
+		.resources = timberdale_uartlite_resources,
+	},
+	{
+		.name = "xiic-i2c",
+		.num_resources = ARRAY_SIZE(timberdale_xiic_resources),
+		.resources = timberdale_xiic_resources,
+		.platform_data = &timberdale_xiic_platform_data,
+		.pdata_size = sizeof(timberdale_xiic_platform_data),
+	},
+	{
+		.name = "timb-gpio",
+		.num_resources = ARRAY_SIZE(timberdale_gpio_resources),
+		.resources = timberdale_gpio_resources,
+		.platform_data = &timberdale_gpio_platform_data,
+		.pdata_size = sizeof(timberdale_gpio_platform_data),
+	},
+	{
+		.name = "timb-mlogicore",
+		.num_resources = ARRAY_SIZE(timberdale_mlogicore_resources),
+		.resources = timberdale_mlogicore_resources,
+	},
+	{
+		.name = "timb-video",
+		.num_resources = ARRAY_SIZE(timberdale_video_resources),
+		.resources = timberdale_video_resources,
+		.platform_data = &timberdale_video_platform_data,
+		.pdata_size = sizeof(timberdale_video_platform_data),
+	},
+	{
+		.name = "timb-radio",
+		.num_resources = ARRAY_SIZE(timberdale_radio_resources),
+		.resources = timberdale_radio_resources,
+		.platform_data = &timberdale_radio_platform_data,
+		.pdata_size = sizeof(timberdale_radio_platform_data),
+	},
+	{
+		.name = "xilinx_spi",
+		.num_resources = ARRAY_SIZE(timberdale_spi_resources),
+		.resources = timberdale_spi_resources,
+		.platform_data = &timberdale_xspi_platform_data,
+		.pdata_size = sizeof(timberdale_xspi_platform_data),
+	},
+	{
+		.name = "ks8842",
+		.num_resources = ARRAY_SIZE(timberdale_eth_resources),
+		.resources = timberdale_eth_resources,
+		.platform_data = &timberdale_ks8842_platform_data,
+		.pdata_size = sizeof(timberdale_ks8842_platform_data),
+	},
+};
+
+static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg2[] = {
+	{
+		.name = "timb-dma",
+		.num_resources = ARRAY_SIZE(timberdale_dma_resources),
+		.resources = timberdale_dma_resources,
+		.platform_data = &timb_dma_platform_data,
+		.pdata_size = sizeof(timb_dma_platform_data),
+	},
+	{
+		.name = "timb-uart",
+		.num_resources = ARRAY_SIZE(timberdale_uart_resources),
+		.resources = timberdale_uart_resources,
+	},
+	{
+		.name = "xiic-i2c",
+		.num_resources = ARRAY_SIZE(timberdale_xiic_resources),
+		.resources = timberdale_xiic_resources,
+		.platform_data = &timberdale_xiic_platform_data,
+		.pdata_size = sizeof(timberdale_xiic_platform_data),
+	},
+	{
+		.name = "timb-gpio",
+		.num_resources = ARRAY_SIZE(timberdale_gpio_resources),
+		.resources = timberdale_gpio_resources,
+		.platform_data = &timberdale_gpio_platform_data,
+		.pdata_size = sizeof(timberdale_gpio_platform_data),
+	},
+	{
+		.name = "timb-video",
+		.num_resources = ARRAY_SIZE(timberdale_video_resources),
+		.resources = timberdale_video_resources,
+		.platform_data = &timberdale_video_platform_data,
+		.pdata_size = sizeof(timberdale_video_platform_data),
+	},
+	{
+		.name = "timb-radio",
+		.num_resources = ARRAY_SIZE(timberdale_radio_resources),
+		.resources = timberdale_radio_resources,
+		.platform_data = &timberdale_radio_platform_data,
+		.pdata_size = sizeof(timberdale_radio_platform_data),
+	},
+	{
+		.name = "xilinx_spi",
+		.num_resources = ARRAY_SIZE(timberdale_spi_resources),
+		.resources = timberdale_spi_resources,
+		.platform_data = &timberdale_xspi_platform_data,
+		.pdata_size = sizeof(timberdale_xspi_platform_data),
+	},
+};
+
+static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg3[] = {
+	{
+		.name = "timb-dma",
+		.num_resources = ARRAY_SIZE(timberdale_dma_resources),
+		.resources = timberdale_dma_resources,
+		.platform_data = &timb_dma_platform_data,
+		.pdata_size = sizeof(timb_dma_platform_data),
+	},
+	{
+		.name = "timb-uart",
+		.num_resources = ARRAY_SIZE(timberdale_uart_resources),
+		.resources = timberdale_uart_resources,
+	},
+	{
+		.name = "ocores-i2c",
+		.num_resources = ARRAY_SIZE(timberdale_ocores_resources),
+		.resources = timberdale_ocores_resources,
+		.platform_data = &timberdale_ocores_platform_data,
+		.pdata_size = sizeof(timberdale_ocores_platform_data),
+	},
+	{
+		.name = "timb-gpio",
+		.num_resources = ARRAY_SIZE(timberdale_gpio_resources),
+		.resources = timberdale_gpio_resources,
+		.platform_data = &timberdale_gpio_platform_data,
+		.pdata_size = sizeof(timberdale_gpio_platform_data),
+	},
+	{
+		.name = "timb-video",
+		.num_resources = ARRAY_SIZE(timberdale_video_resources),
+		.resources = timberdale_video_resources,
+		.platform_data = &timberdale_video_platform_data,
+		.pdata_size = sizeof(timberdale_video_platform_data),
+	},
+	{
+		.name = "timb-radio",
+		.num_resources = ARRAY_SIZE(timberdale_radio_resources),
+		.resources = timberdale_radio_resources,
+		.platform_data = &timberdale_radio_platform_data,
+		.pdata_size = sizeof(timberdale_radio_platform_data),
+	},
+	{
+		.name = "xilinx_spi",
+		.num_resources = ARRAY_SIZE(timberdale_spi_resources),
+		.resources = timberdale_spi_resources,
+		.platform_data = &timberdale_xspi_platform_data,
+		.pdata_size = sizeof(timberdale_xspi_platform_data),
+	},
+	{
+		.name = "ks8842",
+		.num_resources = ARRAY_SIZE(timberdale_eth_resources),
+		.resources = timberdale_eth_resources,
+		.platform_data = &timberdale_ks8842_platform_data,
+		.pdata_size = sizeof(timberdale_ks8842_platform_data),
+	},
+};
+
+static const __devinitconst struct resource timberdale_sdhc_resources[] = {
+	/* located in bar 1 and bar 2 */
+	{
+		.start	= SDHC0OFFSET,
+		.end	= SDHC0END,
+		.flags	= IORESOURCE_MEM,
+	},
+	{
+		.start	= IRQ_TIMBERDALE_SDHC,
+		.end	= IRQ_TIMBERDALE_SDHC,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static __devinitdata struct mfd_cell timberdale_cells_bar1[] = {
+	{
+		.name = "sdhci",
+		.num_resources = ARRAY_SIZE(timberdale_sdhc_resources),
+		.resources = timberdale_sdhc_resources,
+	},
+};
+
+static __devinitdata struct mfd_cell timberdale_cells_bar2[] = {
+	{
+		.name = "sdhci",
+		.num_resources = ARRAY_SIZE(timberdale_sdhc_resources),
+		.resources = timberdale_sdhc_resources,
+	},
+};
+
+static ssize_t show_fw_ver(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+	struct timberdale_device *priv = pci_get_drvdata(pdev);
+
+	return sprintf(buf, "%d.%d.%d\n", priv->fw.major, priv->fw.minor,
+		priv->fw.config);
+}
+
+static DEVICE_ATTR(fw_ver, S_IRUGO, show_fw_ver, NULL);
+
+/*--------------------------------------------------------------------------*/
+
+static int __devinit timb_probe(struct pci_dev *dev,
+	const struct pci_device_id *id)
+{
+	struct timberdale_device *priv;
+	int err, i;
+	resource_size_t mapbase;
+	struct msix_entry *msix_entries = NULL;
+	u8 ip_setup;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	pci_set_drvdata(dev, priv);
+
+	err = pci_enable_device(dev);
+	if (err)
+		goto err_enable;
+
+	mapbase = pci_resource_start(dev, 0);
+	if (!mapbase) {
+		dev_err(&dev->dev, "No resource\n");
+		goto err_start;
+	}
+
+	/* create a resource for the PCI master register */
+	priv->ctl_mapbase = mapbase + CHIPCTLOFFSET;
+	if (!request_mem_region(priv->ctl_mapbase, CHIPCTLSIZE, "timb-ctl")) {
+		dev_err(&dev->dev, "Failed to request ctl mem\n");
+		goto err_request;
+	}
+
+	priv->ctl_membase = ioremap(priv->ctl_mapbase, CHIPCTLSIZE);
+	if (!priv->ctl_membase) {
+		dev_err(&dev->dev, "ioremap failed for ctl mem\n");
+		goto err_ioremap;
+	}
+
+	/* read the HW config */
+	priv->fw.major = ioread32(priv->ctl_membase + TIMB_REV_MAJOR);
+	priv->fw.minor = ioread32(priv->ctl_membase + TIMB_REV_MINOR);
+	priv->fw.config = ioread32(priv->ctl_membase + TIMB_HW_CONFIG);
+
+	if (priv->fw.major > TIMB_SUPPORTED_MAJOR) {
+		dev_err(&dev->dev, "The driver supports an older "
+			"version of the FPGA, please update the driver to "
+			"support %d.%d\n", priv->fw.major, priv->fw.minor);
+		goto err_config;
+	}
+	if (priv->fw.major < TIMB_SUPPORTED_MAJOR ||
+		priv->fw.minor < TIMB_REQUIRED_MINOR) {
+		dev_err(&dev->dev, "The FPGA image is too old (%d.%d), "
+			"please upgrade the FPGA to at least: %d.%d\n",
+			priv->fw.major, priv->fw.minor,
+			TIMB_SUPPORTED_MAJOR, TIMB_REQUIRED_MINOR);
+		goto err_config;
+	}
+
+	msix_entries = kzalloc(TIMBERDALE_NR_IRQS * sizeof(*msix_entries),
+		GFP_KERNEL);
+	if (!msix_entries)
+		goto err_config;
+
+	for (i = 0; i < TIMBERDALE_NR_IRQS; i++)
+		msix_entries[i].entry = i;
+
+	err = pci_enable_msix(dev, msix_entries, TIMBERDALE_NR_IRQS);
+	if (err) {
+		dev_err(&dev->dev,
+			"MSI-X init failed: %d, expected entries: %d\n",
+			err, TIMBERDALE_NR_IRQS);
+		goto err_msix;
+	}
+
+	err = device_create_file(&dev->dev, &dev_attr_fw_ver);
+	if (err)
+		goto err_create_file;
+
+	/* Reset all FPGA PLB peripherals */
+	iowrite32(0x1, priv->ctl_membase + TIMB_SW_RST);
+
+	/* update IRQ offsets in I2C board info */
+	for (i = 0; i < ARRAY_SIZE(timberdale_i2c_board_info); i++)
+		timberdale_i2c_board_info[i].irq =
+			msix_entries[timberdale_i2c_board_info[i].irq].vector;
+
+	/* Update the SPI configuration depending on the HW (8 or 16 bit) */
+	if (priv->fw.config & TIMB_HW_CONFIG_SPI_8BIT) {
+		timberdale_xspi_platform_data.bits_per_word = 8;
+		timberdale_xspi_platform_data.devices =
+			timberdale_spi_8bit_board_info;
+		timberdale_xspi_platform_data.num_devices =
+			ARRAY_SIZE(timberdale_spi_8bit_board_info);
+	} else {
+		timberdale_xspi_platform_data.bits_per_word = 16;
+		timberdale_xspi_platform_data.devices =
+			timberdale_spi_16bit_board_info;
+		timberdale_xspi_platform_data.num_devices =
+			ARRAY_SIZE(timberdale_spi_16bit_board_info);
+	}
+
+	ip_setup = priv->fw.config & TIMB_HW_VER_MASK;
+	switch (ip_setup) {
+	case TIMB_HW_VER0:
+		err = mfd_add_devices(&dev->dev, -1,
+			timberdale_cells_bar0_cfg0,
+			ARRAY_SIZE(timberdale_cells_bar0_cfg0),
+			&dev->resource[0], msix_entries[0].vector);
+		break;
+	case TIMB_HW_VER1:
+		err = mfd_add_devices(&dev->dev, -1,
+			timberdale_cells_bar0_cfg1,
+			ARRAY_SIZE(timberdale_cells_bar0_cfg1),
+			&dev->resource[0], msix_entries[0].vector);
+		break;
+	case TIMB_HW_VER2:
+		err = mfd_add_devices(&dev->dev, -1,
+			timberdale_cells_bar0_cfg2,
+			ARRAY_SIZE(timberdale_cells_bar0_cfg2),
+			&dev->resource[0], msix_entries[0].vector);
+		break;
+	case TIMB_HW_VER3:
+		err = mfd_add_devices(&dev->dev, -1,
+			timberdale_cells_bar0_cfg3,
+			ARRAY_SIZE(timberdale_cells_bar0_cfg3),
+			&dev->resource[0], msix_entries[0].vector);
+		break;
+	default:
+		dev_err(&dev->dev, "Uknown IP setup: %d.%d.%d\n",
+			priv->fw.major, priv->fw.minor, ip_setup);
+		err = -ENODEV;
+		goto err_mfd;
+		break;
+	}
+
+	if (err) {
+		dev_err(&dev->dev, "mfd_add_devices failed: %d\n", err);
+		goto err_mfd;
+	}
+
+	err = mfd_add_devices(&dev->dev, 0,
+		timberdale_cells_bar1, ARRAY_SIZE(timberdale_cells_bar1),
+		&dev->resource[1], msix_entries[0].vector);
+	if (err) {
+		dev_err(&dev->dev, "mfd_add_devices failed: %d\n", err);
+		goto err_mfd2;
+	}
+
+	/* only version 0 and 3 have the iNand routed to SDHCI */
+	if (((priv->fw.config & TIMB_HW_VER_MASK) == TIMB_HW_VER0) ||
+		((priv->fw.config & TIMB_HW_VER_MASK) == TIMB_HW_VER3)) {
+		err = mfd_add_devices(&dev->dev, 1, timberdale_cells_bar2,
+			ARRAY_SIZE(timberdale_cells_bar2),
+			&dev->resource[2], msix_entries[0].vector);
+		if (err) {
+			dev_err(&dev->dev, "mfd_add_devices failed: %d\n", err);
+			goto err_mfd2;
+		}
+	}
+
+	kfree(msix_entries);
+
+	dev_info(&dev->dev,
+		"Found Timberdale Card. Rev: %d.%d, HW config: 0x%02x\n",
+		priv->fw.major, priv->fw.minor, priv->fw.config);
+
+	return 0;
+
+err_mfd2:
+	mfd_remove_devices(&dev->dev);
+err_mfd:
+	device_remove_file(&dev->dev, &dev_attr_fw_ver);
+err_create_file:
+	pci_disable_msix(dev);
+err_msix:
+	kfree(msix_entries);
+err_config:
+	iounmap(priv->ctl_membase);
+err_ioremap:
+	release_mem_region(priv->ctl_mapbase, CHIPCTLSIZE);
+err_request:
+	pci_set_drvdata(dev, NULL);
+err_start:
+	pci_disable_device(dev);
+err_enable:
+	kfree(priv);
+	pci_set_drvdata(dev, NULL);
+	return -ENODEV;
+}
+
+static void __devexit timb_remove(struct pci_dev *dev)
+{
+	struct timberdale_device *priv = pci_get_drvdata(dev);
+
+	mfd_remove_devices(&dev->dev);
+
+	device_remove_file(&dev->dev, &dev_attr_fw_ver);
+
+	iounmap(priv->ctl_membase);
+	release_mem_region(priv->ctl_mapbase, CHIPCTLSIZE);
+
+	pci_disable_msix(dev);
+	pci_disable_device(dev);
+	pci_set_drvdata(dev, NULL);
+	kfree(priv);
+}
+
+static DEFINE_PCI_DEVICE_TABLE(timberdale_pci_tbl) = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_TIMB, PCI_DEVICE_ID_TIMB) },
+	{ 0 }
+};
+MODULE_DEVICE_TABLE(pci, timberdale_pci_tbl);
+
+static struct pci_driver timberdale_pci_driver = {
+	.name = DRIVER_NAME,
+	.id_table = timberdale_pci_tbl,
+	.probe = timb_probe,
+	.remove = __devexit_p(timb_remove),
+};
+
+static int __init timberdale_init(void)
+{
+	int err;
+
+	err = pci_register_driver(&timberdale_pci_driver);
+	if (err < 0) {
+		printk(KERN_ERR
+			"Failed to register PCI driver for %s device.\n",
+			timberdale_pci_driver.name);
+		return -ENODEV;
+	}
+
+	printk(KERN_INFO "Driver for %s has been successfully registered.\n",
+		timberdale_pci_driver.name);
+
+	return 0;
+}
+
+static void __exit timberdale_exit(void)
+{
+	pci_unregister_driver(&timberdale_pci_driver);
+
+	printk(KERN_INFO "Driver for %s has been successfully unregistered.\n",
+		timberdale_pci_driver.name);
+}
+
+module_init(timberdale_init);
+module_exit(timberdale_exit);
+
+MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>");
+MODULE_VERSION(DRV_VERSION);
+MODULE_LICENSE("GPL v2");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/timberdale.h b/ap/os/linux/linux-3.4.x/drivers/mfd/timberdale.h
new file mode 100644
index 0000000..4412acd
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/timberdale.h
@@ -0,0 +1,142 @@
+/*
+ * timberdale.h timberdale FPGA MFD driver defines
+ * Copyright (c) 2009 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Supports:
+ * Timberdale FPGA
+ */
+
+#ifndef MFD_TIMBERDALE_H
+#define MFD_TIMBERDALE_H
+
+#define DRV_VERSION		"0.3"
+
+/* This driver only support versions >= 3.8 and < 4.0  */
+#define TIMB_SUPPORTED_MAJOR	3
+
+/* This driver only support minor >= 8 */
+#define TIMB_REQUIRED_MINOR	8
+
+/* Registers of the control area */
+#define TIMB_REV_MAJOR	0x00
+#define TIMB_REV_MINOR	0x04
+#define TIMB_HW_CONFIG	0x08
+#define TIMB_SW_RST	0x40
+
+/* bits in the TIMB_HW_CONFIG register */
+#define TIMB_HW_CONFIG_SPI_8BIT	0x80
+
+#define TIMB_HW_VER_MASK	0x0f
+#define TIMB_HW_VER0		0x00
+#define TIMB_HW_VER1		0x01
+#define TIMB_HW_VER2		0x02
+#define TIMB_HW_VER3		0x03
+
+#define OCORESOFFSET	0x0
+#define OCORESEND	0x1f
+
+#define SPIOFFSET	0x80
+#define SPIEND		0xff
+
+#define UARTLITEOFFSET	0x100
+#define UARTLITEEND	0x10f
+
+#define RDSOFFSET	0x180
+#define RDSEND		0x183
+
+#define ETHOFFSET	0x300
+#define ETHEND		0x3ff
+
+#define GPIOOFFSET	0x400
+#define GPIOEND		0x7ff
+
+#define CHIPCTLOFFSET	0x800
+#define CHIPCTLEND	0x8ff
+#define CHIPCTLSIZE	(CHIPCTLEND - CHIPCTLOFFSET + 1)
+
+#define INTCOFFSET	0xc00
+#define INTCEND		0xfff
+#define INTCSIZE	(INTCEND - INTCOFFSET)
+
+#define MOSTOFFSET	0x1000
+#define MOSTEND		0x13ff
+
+#define UARTOFFSET	0x1400
+#define UARTEND		0x17ff
+
+#define XIICOFFSET	0x1800
+#define XIICEND		0x19ff
+
+#define I2SOFFSET	0x1C00
+#define I2SEND		0x1fff
+
+#define LOGIWOFFSET	0x30000
+#define LOGIWEND	0x37fff
+
+#define MLCOREOFFSET	0x40000
+#define MLCOREEND	0x43fff
+
+#define DMAOFFSET	0x01000000
+#define DMAEND		0x013fffff
+
+/* SDHC0 is placed in PCI bar 1 */
+#define SDHC0OFFSET	0x00
+#define SDHC0END	0xff
+
+/* SDHC1 is placed in PCI bar 2 */
+#define SDHC1OFFSET	0x00
+#define SDHC1END	0xff
+
+#define PCI_VENDOR_ID_TIMB	0x10ee
+#define PCI_DEVICE_ID_TIMB	0xa123
+
+#define IRQ_TIMBERDALE_INIC		0
+#define IRQ_TIMBERDALE_MLB		1
+#define IRQ_TIMBERDALE_GPIO		2
+#define IRQ_TIMBERDALE_I2C		3
+#define IRQ_TIMBERDALE_UART		4
+#define IRQ_TIMBERDALE_DMA		5
+#define IRQ_TIMBERDALE_I2S		6
+#define IRQ_TIMBERDALE_TSC_INT		7
+#define IRQ_TIMBERDALE_SDHC		8
+#define IRQ_TIMBERDALE_ADV7180		9
+#define IRQ_TIMBERDALE_ETHSW_IF		10
+#define IRQ_TIMBERDALE_SPI		11
+#define IRQ_TIMBERDALE_UARTLITE		12
+#define IRQ_TIMBERDALE_MLCORE		13
+#define IRQ_TIMBERDALE_MLCORE_BUF	14
+#define IRQ_TIMBERDALE_RDS		15
+#define TIMBERDALE_NR_IRQS		16
+
+#define GPIO_PIN_ASCB		8
+#define GPIO_PIN_INIC_RST	14
+#define GPIO_PIN_BT_RST		15
+#define GPIO_NR_PINS		16
+
+/* DMA Channels */
+#define DMA_UART_RX         0
+#define DMA_UART_TX         1
+#define DMA_MLB_RX          2
+#define DMA_MLB_TX          3
+#define DMA_VIDEO_RX        4
+#define DMA_VIDEO_DROP      5
+#define DMA_SDHCI_RX        6
+#define DMA_SDHCI_TX        7
+#define DMA_ETH_RX          8
+#define DMA_ETH_TX          9
+
+#endif
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/tmio_core.c b/ap/os/linux/linux-3.4.x/drivers/mfd/tmio_core.c
new file mode 100644
index 0000000..83af78c
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/tmio_core.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright(c) 2009 Ian Molton <spyro@f2s.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/export.h>
+#include <linux/mfd/tmio.h>
+
+int tmio_core_mmc_enable(void __iomem *cnf, int shift, unsigned long base)
+{
+	/* Enable the MMC/SD Control registers */
+	sd_config_write16(cnf, shift, CNF_CMD, SDCREN);
+	sd_config_write32(cnf, shift, CNF_CTL_BASE, base & 0xfffe);
+
+	/* Disable SD power during suspend */
+	sd_config_write8(cnf, shift, CNF_PWR_CTL_3, 0x01);
+
+	/* The below is required but why? FIXME */
+	sd_config_write8(cnf, shift, CNF_STOP_CLK_CTL, 0x1f);
+
+	/* Power down SD bus */
+	sd_config_write8(cnf, shift, CNF_PWR_CTL_2, 0x00);
+
+	return 0;
+}
+EXPORT_SYMBOL(tmio_core_mmc_enable);
+
+int tmio_core_mmc_resume(void __iomem *cnf, int shift, unsigned long base)
+{
+
+	/* Enable the MMC/SD Control registers */
+	sd_config_write16(cnf, shift, CNF_CMD, SDCREN);
+	sd_config_write32(cnf, shift, CNF_CTL_BASE, base & 0xfffe);
+
+	return 0;
+}
+EXPORT_SYMBOL(tmio_core_mmc_resume);
+
+void tmio_core_mmc_pwr(void __iomem *cnf, int shift, int state)
+{
+	sd_config_write8(cnf, shift, CNF_PWR_CTL_2, state ? 0x02 : 0x00);
+}
+EXPORT_SYMBOL(tmio_core_mmc_pwr);
+
+void tmio_core_mmc_clk_div(void __iomem *cnf, int shift, int state)
+{
+	sd_config_write8(cnf, shift, CNF_SD_CLK_MODE, state ? 1 : 0);
+}
+EXPORT_SYMBOL(tmio_core_mmc_clk_div);
+
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/tps6105x.c b/ap/os/linux/linux-3.4.x/drivers/mfd/tps6105x.c
new file mode 100644
index 0000000..a293b97
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/tps6105x.c
@@ -0,0 +1,247 @@
+/*
+ * Core driver for TPS61050/61052 boost converters, used for while LED
+ * driving, audio power amplification, white LED flash, and generic
+ * boost conversion. Additionally it provides a 1-bit GPIO pin (out or in)
+ * and a flash synchronization pin to synchronize flash events when used as
+ * flashgun.
+ *
+ * Copyright (C) 2011 ST-Ericsson SA
+ * Written on behalf of Linaro for ST-Ericsson
+ *
+ * Author: Linus Walleij <linus.walleij@linaro.org>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/gpio.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/regulator/driver.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/tps6105x.h>
+
+int tps6105x_set(struct tps6105x *tps6105x, u8 reg, u8 value)
+{
+	int ret;
+
+	ret = mutex_lock_interruptible(&tps6105x->lock);
+	if (ret)
+		return ret;
+	ret = i2c_smbus_write_byte_data(tps6105x->client, reg, value);
+	mutex_unlock(&tps6105x->lock);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+EXPORT_SYMBOL(tps6105x_set);
+
+int tps6105x_get(struct tps6105x *tps6105x, u8 reg, u8 *buf)
+{
+	int ret;
+
+	ret = mutex_lock_interruptible(&tps6105x->lock);
+	if (ret)
+		return ret;
+	ret = i2c_smbus_read_byte_data(tps6105x->client, reg);
+	mutex_unlock(&tps6105x->lock);
+	if (ret < 0)
+		return ret;
+
+	*buf = ret;
+	return 0;
+}
+EXPORT_SYMBOL(tps6105x_get);
+
+/*
+ * Masks off the bits in the mask and sets the bits in the bitvalues
+ * parameter in one atomic operation
+ */
+int tps6105x_mask_and_set(struct tps6105x *tps6105x, u8 reg,
+			  u8 bitmask, u8 bitvalues)
+{
+	int ret;
+	u8 regval;
+
+	ret = mutex_lock_interruptible(&tps6105x->lock);
+	if (ret)
+		return ret;
+	ret = i2c_smbus_read_byte_data(tps6105x->client, reg);
+	if (ret < 0)
+		goto fail;
+	regval = ret;
+	regval = (~bitmask & regval) | (bitmask & bitvalues);
+	ret = i2c_smbus_write_byte_data(tps6105x->client, reg, regval);
+fail:
+	mutex_unlock(&tps6105x->lock);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+EXPORT_SYMBOL(tps6105x_mask_and_set);
+
+static int __devinit tps6105x_startup(struct tps6105x *tps6105x)
+{
+	int ret;
+	u8 regval;
+
+	ret = tps6105x_get(tps6105x, TPS6105X_REG_0, &regval);
+	if (ret)
+		return ret;
+	switch (regval >> TPS6105X_REG0_MODE_SHIFT) {
+	case TPS6105X_REG0_MODE_SHUTDOWN:
+		dev_info(&tps6105x->client->dev,
+			 "TPS6105x found in SHUTDOWN mode\n");
+		break;
+	case TPS6105X_REG0_MODE_TORCH:
+		dev_info(&tps6105x->client->dev,
+			 "TPS6105x found in TORCH mode\n");
+		break;
+	case TPS6105X_REG0_MODE_TORCH_FLASH:
+		dev_info(&tps6105x->client->dev,
+			 "TPS6105x found in FLASH mode\n");
+		break;
+	case TPS6105X_REG0_MODE_VOLTAGE:
+		dev_info(&tps6105x->client->dev,
+			 "TPS6105x found in VOLTAGE mode\n");
+		break;
+	default:
+		break;
+	}
+
+	return ret;
+}
+
+/*
+ * MFD cells - we have one cell which is selected operation
+ * mode, and we always have a GPIO cell.
+ */
+static struct mfd_cell tps6105x_cells[] = {
+	{
+		/* name will be runtime assigned */
+		.id = -1,
+	},
+	{
+		.name = "tps6105x-gpio",
+		.id = -1,
+	},
+};
+
+static int __devinit tps6105x_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct tps6105x			*tps6105x;
+	struct tps6105x_platform_data	*pdata;
+	int ret;
+	int i;
+
+	tps6105x = kmalloc(sizeof(*tps6105x), GFP_KERNEL);
+	if (!tps6105x)
+		return -ENOMEM;
+
+	i2c_set_clientdata(client, tps6105x);
+	tps6105x->client = client;
+	pdata = client->dev.platform_data;
+	tps6105x->pdata = pdata;
+	mutex_init(&tps6105x->lock);
+
+	ret = tps6105x_startup(tps6105x);
+	if (ret) {
+		dev_err(&client->dev, "chip initialization failed\n");
+		goto fail;
+	}
+
+	/* Remove warning texts when you implement new cell drivers */
+	switch (pdata->mode) {
+	case TPS6105X_MODE_SHUTDOWN:
+		dev_info(&client->dev,
+			 "present, not used for anything, only GPIO\n");
+		break;
+	case TPS6105X_MODE_TORCH:
+		tps6105x_cells[0].name = "tps6105x-leds";
+		dev_warn(&client->dev,
+			 "torch mode is unsupported\n");
+		break;
+	case TPS6105X_MODE_TORCH_FLASH:
+		tps6105x_cells[0].name = "tps6105x-flash";
+		dev_warn(&client->dev,
+			 "flash mode is unsupported\n");
+		break;
+	case TPS6105X_MODE_VOLTAGE:
+		tps6105x_cells[0].name ="tps6105x-regulator";
+		break;
+	default:
+		break;
+	}
+
+	/* Set up and register the platform devices. */
+	for (i = 0; i < ARRAY_SIZE(tps6105x_cells); i++) {
+		/* One state holder for all drivers, this is simple */
+		tps6105x_cells[i].platform_data = tps6105x;
+		tps6105x_cells[i].pdata_size = sizeof(*tps6105x);
+	}
+
+	ret = mfd_add_devices(&client->dev, 0, tps6105x_cells,
+		ARRAY_SIZE(tps6105x_cells), NULL, 0);
+	if (ret)
+		goto fail;
+
+	return 0;
+
+fail:
+	kfree(tps6105x);
+	return ret;
+}
+
+static int __devexit tps6105x_remove(struct i2c_client *client)
+{
+	struct tps6105x *tps6105x = i2c_get_clientdata(client);
+
+	mfd_remove_devices(&client->dev);
+
+	/* Put chip in shutdown mode */
+	tps6105x_mask_and_set(tps6105x, TPS6105X_REG_0,
+		TPS6105X_REG0_MODE_MASK,
+		TPS6105X_MODE_SHUTDOWN << TPS6105X_REG0_MODE_SHIFT);
+
+	kfree(tps6105x);
+	return 0;
+}
+
+static const struct i2c_device_id tps6105x_id[] = {
+	{ "tps61050", 0 },
+	{ "tps61052", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, tps6105x_id);
+
+static struct i2c_driver tps6105x_driver = {
+	.driver = {
+		.name	= "tps6105x",
+	},
+	.probe		= tps6105x_probe,
+	.remove		= __devexit_p(tps6105x_remove),
+	.id_table	= tps6105x_id,
+};
+
+static int __init tps6105x_init(void)
+{
+	return i2c_add_driver(&tps6105x_driver);
+}
+subsys_initcall(tps6105x_init);
+
+static void __exit tps6105x_exit(void)
+{
+	i2c_del_driver(&tps6105x_driver);
+}
+module_exit(tps6105x_exit);
+
+MODULE_AUTHOR("Linus Walleij");
+MODULE_DESCRIPTION("TPS6105x White LED Boost Converter Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/tps65010.c b/ap/os/linux/linux-3.4.x/drivers/mfd/tps65010.c
new file mode 100644
index 0000000..93d5fdf
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/tps65010.c
@@ -0,0 +1,1098 @@
+/*
+ * tps65010 - driver for tps6501x power management chips
+ *
+ * Copyright (C) 2004 Texas Instruments
+ * Copyright (C) 2004-2005 David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+
+#include <linux/i2c/tps65010.h>
+
+#include <linux/gpio.h>
+
+
+/*-------------------------------------------------------------------------*/
+
+#define	DRIVER_VERSION	"2 May 2005"
+#define	DRIVER_NAME	(tps65010_driver.driver.name)
+
+MODULE_DESCRIPTION("TPS6501x Power Management Driver");
+MODULE_LICENSE("GPL");
+
+static struct i2c_driver tps65010_driver;
+
+/*-------------------------------------------------------------------------*/
+
+/* This driver handles a family of multipurpose chips, which incorporate
+ * voltage regulators, lithium ion/polymer battery charging, GPIOs, LEDs,
+ * and other features often needed in portable devices like cell phones
+ * or digital cameras.
+ *
+ * The tps65011 and tps65013 have different voltage settings compared
+ * to tps65010 and tps65012.  The tps65013 has a NO_CHG status/irq.
+ * All except tps65010 have "wait" mode, possibly defaulted so that
+ * battery-insert != device-on.
+ *
+ * We could distinguish between some models by checking VDCDC1.UVLO or
+ * other registers, unless they've been changed already after powerup
+ * as part of board setup by a bootloader.
+ */
+enum tps_model {
+	TPS65010,
+	TPS65011,
+	TPS65012,
+	TPS65013,
+};
+
+struct tps65010 {
+	struct i2c_client	*client;
+	struct mutex		lock;
+	struct delayed_work	work;
+	struct dentry		*file;
+	unsigned		charging:1;
+	unsigned		por:1;
+	unsigned		model:8;
+	u16			vbus;
+	unsigned long		flags;
+#define	FLAG_VBUS_CHANGED	0
+#define	FLAG_IRQ_ENABLE		1
+
+	/* copies of last register state */
+	u8			chgstatus, regstatus, chgconf;
+	u8			nmask1, nmask2;
+
+	u8			outmask;
+	struct gpio_chip	chip;
+	struct platform_device	*leds;
+};
+
+#define	POWER_POLL_DELAY	msecs_to_jiffies(5000)
+
+/*-------------------------------------------------------------------------*/
+
+#if	defined(DEBUG) || defined(CONFIG_DEBUG_FS)
+
+static void dbg_chgstat(char *buf, size_t len, u8 chgstatus)
+{
+	snprintf(buf, len, "%02x%s%s%s%s%s%s%s%s\n",
+		chgstatus,
+		(chgstatus & TPS_CHG_USB) ? " USB" : "",
+		(chgstatus & TPS_CHG_AC) ? " AC" : "",
+		(chgstatus & TPS_CHG_THERM) ? " therm" : "",
+		(chgstatus & TPS_CHG_TERM) ? " done" :
+			((chgstatus & (TPS_CHG_USB|TPS_CHG_AC))
+				? " (charging)" : ""),
+		(chgstatus & TPS_CHG_TAPER_TMO) ? " taper_tmo" : "",
+		(chgstatus & TPS_CHG_CHG_TMO) ? " charge_tmo" : "",
+		(chgstatus & TPS_CHG_PRECHG_TMO) ? " prechg_tmo" : "",
+		(chgstatus & TPS_CHG_TEMP_ERR) ? " temp_err" : "");
+}
+
+static void dbg_regstat(char *buf, size_t len, u8 regstatus)
+{
+	snprintf(buf, len, "%02x %s%s%s%s%s%s%s%s\n",
+		regstatus,
+		(regstatus & TPS_REG_ONOFF) ? "off" : "(on)",
+		(regstatus & TPS_REG_COVER) ? " uncover" : "",
+		(regstatus & TPS_REG_UVLO) ? " UVLO" : "",
+		(regstatus & TPS_REG_NO_CHG) ? " NO_CHG" : "",
+		(regstatus & TPS_REG_PG_LD02) ? " ld02_bad" : "",
+		(regstatus & TPS_REG_PG_LD01) ? " ld01_bad" : "",
+		(regstatus & TPS_REG_PG_MAIN) ? " main_bad" : "",
+		(regstatus & TPS_REG_PG_CORE) ? " core_bad" : "");
+}
+
+static void dbg_chgconf(int por, char *buf, size_t len, u8 chgconfig)
+{
+	const char *hibit;
+
+	if (por)
+		hibit = (chgconfig & TPS_CHARGE_POR)
+				? "POR=69ms" : "POR=1sec";
+	else
+		hibit = (chgconfig & TPS65013_AUA) ? "AUA" : "";
+
+	snprintf(buf, len, "%02x %s%s%s AC=%d%% USB=%dmA %sCharge\n",
+		chgconfig, hibit,
+		(chgconfig & TPS_CHARGE_RESET) ? " reset" : "",
+		(chgconfig & TPS_CHARGE_FAST) ? " fast" : "",
+		({int p; switch ((chgconfig >> 3) & 3) {
+		case 3:		p = 100; break;
+		case 2:		p = 75; break;
+		case 1:		p = 50; break;
+		default:	p = 25; break;
+		}; p; }),
+		(chgconfig & TPS_VBUS_CHARGING)
+			? ((chgconfig & TPS_VBUS_500MA) ? 500 : 100)
+			: 0,
+		(chgconfig & TPS_CHARGE_ENABLE) ? "" : "No");
+}
+
+#endif
+
+#ifdef	DEBUG
+
+static void show_chgstatus(const char *label, u8 chgstatus)
+{
+	char buf [100];
+
+	dbg_chgstat(buf, sizeof buf, chgstatus);
+	pr_debug("%s: %s %s", DRIVER_NAME, label, buf);
+}
+
+static void show_regstatus(const char *label, u8 regstatus)
+{
+	char buf [100];
+
+	dbg_regstat(buf, sizeof buf, regstatus);
+	pr_debug("%s: %s %s", DRIVER_NAME, label, buf);
+}
+
+static void show_chgconfig(int por, const char *label, u8 chgconfig)
+{
+	char buf [100];
+
+	dbg_chgconf(por, buf, sizeof buf, chgconfig);
+	pr_debug("%s: %s %s", DRIVER_NAME, label, buf);
+}
+
+#else
+
+static inline void show_chgstatus(const char *label, u8 chgstatus) { }
+static inline void show_regstatus(const char *label, u8 chgstatus) { }
+static inline void show_chgconfig(int por, const char *label, u8 chgconfig) { }
+
+#endif
+
+#ifdef	CONFIG_DEBUG_FS
+
+static int dbg_show(struct seq_file *s, void *_)
+{
+	struct tps65010	*tps = s->private;
+	u8		value, v2;
+	unsigned	i;
+	char		buf[100];
+	const char	*chip;
+
+	switch (tps->model) {
+	case TPS65010:	chip = "tps65010"; break;
+	case TPS65011:	chip = "tps65011"; break;
+	case TPS65012:	chip = "tps65012"; break;
+	case TPS65013:	chip = "tps65013"; break;
+	default:	chip = NULL; break;
+	}
+	seq_printf(s, "driver  %s\nversion %s\nchip    %s\n\n",
+			DRIVER_NAME, DRIVER_VERSION, chip);
+
+	mutex_lock(&tps->lock);
+
+	/* FIXME how can we tell whether a battery is present?
+	 * likely involves a charge gauging chip (like BQ26501).
+	 */
+
+	seq_printf(s, "%scharging\n\n", tps->charging ? "" : "(not) ");
+
+
+	/* registers for monitoring battery charging and status; note
+	 * that reading chgstat and regstat may ack IRQs...
+	 */
+	value = i2c_smbus_read_byte_data(tps->client, TPS_CHGCONFIG);
+	dbg_chgconf(tps->por, buf, sizeof buf, value);
+	seq_printf(s, "chgconfig %s", buf);
+
+	value = i2c_smbus_read_byte_data(tps->client, TPS_CHGSTATUS);
+	dbg_chgstat(buf, sizeof buf, value);
+	seq_printf(s, "chgstat   %s", buf);
+	value = i2c_smbus_read_byte_data(tps->client, TPS_MASK1);
+	dbg_chgstat(buf, sizeof buf, value);
+	seq_printf(s, "mask1     %s", buf);
+	/* ignore ackint1 */
+
+	value = i2c_smbus_read_byte_data(tps->client, TPS_REGSTATUS);
+	dbg_regstat(buf, sizeof buf, value);
+	seq_printf(s, "regstat   %s", buf);
+	value = i2c_smbus_read_byte_data(tps->client, TPS_MASK2);
+	dbg_regstat(buf, sizeof buf, value);
+	seq_printf(s, "mask2     %s\n", buf);
+	/* ignore ackint2 */
+
+	schedule_delayed_work(&tps->work, POWER_POLL_DELAY);
+
+
+	/* VMAIN voltage, enable lowpower, etc */
+	value = i2c_smbus_read_byte_data(tps->client, TPS_VDCDC1);
+	seq_printf(s, "vdcdc1    %02x\n", value);
+
+	/* VCORE voltage, vibrator on/off */
+	value = i2c_smbus_read_byte_data(tps->client, TPS_VDCDC2);
+	seq_printf(s, "vdcdc2    %02x\n", value);
+
+	/* both LD0s, and their lowpower behavior */
+	value = i2c_smbus_read_byte_data(tps->client, TPS_VREGS1);
+	seq_printf(s, "vregs1    %02x\n\n", value);
+
+
+	/* LEDs and GPIOs */
+	value = i2c_smbus_read_byte_data(tps->client, TPS_LED1_ON);
+	v2 = i2c_smbus_read_byte_data(tps->client, TPS_LED1_PER);
+	seq_printf(s, "led1 %s, on=%02x, per=%02x, %d/%d msec\n",
+		(value & 0x80)
+			? ((v2 & 0x80) ? "on" : "off")
+			: ((v2 & 0x80) ? "blink" : "(nPG)"),
+		value, v2,
+		(value & 0x7f) * 10, (v2 & 0x7f) * 100);
+
+	value = i2c_smbus_read_byte_data(tps->client, TPS_LED2_ON);
+	v2 = i2c_smbus_read_byte_data(tps->client, TPS_LED2_PER);
+	seq_printf(s, "led2 %s, on=%02x, per=%02x, %d/%d msec\n",
+		(value & 0x80)
+			? ((v2 & 0x80) ? "on" : "off")
+			: ((v2 & 0x80) ? "blink" : "off"),
+		value, v2,
+		(value & 0x7f) * 10, (v2 & 0x7f) * 100);
+
+	value = i2c_smbus_read_byte_data(tps->client, TPS_DEFGPIO);
+	v2 = i2c_smbus_read_byte_data(tps->client, TPS_MASK3);
+	seq_printf(s, "defgpio %02x mask3 %02x\n", value, v2);
+
+	for (i = 0; i < 4; i++) {
+		if (value & (1 << (4 + i)))
+			seq_printf(s, "  gpio%d-out %s\n", i + 1,
+				(value & (1 << i)) ? "low" : "hi ");
+		else
+			seq_printf(s, "  gpio%d-in  %s %s %s\n", i + 1,
+				(value & (1 << i)) ? "hi " : "low",
+				(v2 & (1 << i)) ? "no-irq" : "irq",
+				(v2 & (1 << (4 + i))) ? "rising" : "falling");
+	}
+
+	mutex_unlock(&tps->lock);
+	return 0;
+}
+
+static int dbg_tps_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, dbg_show, inode->i_private);
+}
+
+static const struct file_operations debug_fops = {
+	.open		= dbg_tps_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+#define	DEBUG_FOPS	&debug_fops
+
+#else
+#define	DEBUG_FOPS	NULL
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+/* handle IRQS in a task context, so we can use I2C calls */
+static void tps65010_interrupt(struct tps65010 *tps)
+{
+	u8 tmp = 0, mask, poll;
+
+	/* IRQs won't trigger for certain events, but we can get
+	 * others by polling (normally, with external power applied).
+	 */
+	poll = 0;
+
+	/* regstatus irqs */
+	if (tps->nmask2) {
+		tmp = i2c_smbus_read_byte_data(tps->client, TPS_REGSTATUS);
+		mask = tmp ^ tps->regstatus;
+		tps->regstatus = tmp;
+		mask &= tps->nmask2;
+	} else
+		mask = 0;
+	if (mask) {
+		tps->regstatus =  tmp;
+		/* may need to shut something down ... */
+
+		/* "off" usually means deep sleep */
+		if (tmp & TPS_REG_ONOFF) {
+			pr_info("%s: power off button\n", DRIVER_NAME);
+#if 0
+			/* REVISIT:  this might need its own workqueue
+			 * plus tweaks including deadlock avoidance ...
+			 * also needs to get error handling and probably
+			 * an #ifdef CONFIG_HIBERNATION
+			 */
+			hibernate();
+#endif
+			poll = 1;
+		}
+	}
+
+	/* chgstatus irqs */
+	if (tps->nmask1) {
+		tmp = i2c_smbus_read_byte_data(tps->client, TPS_CHGSTATUS);
+		mask = tmp ^ tps->chgstatus;
+		tps->chgstatus = tmp;
+		mask &= tps->nmask1;
+	} else
+		mask = 0;
+	if (mask) {
+		unsigned	charging = 0;
+
+		show_chgstatus("chg/irq", tmp);
+		if (tmp & (TPS_CHG_USB|TPS_CHG_AC))
+			show_chgconfig(tps->por, "conf", tps->chgconf);
+
+		/* Unless it was turned off or disabled, we charge any
+		 * battery whenever there's power available for it
+		 * and the charger hasn't been disabled.
+		 */
+		if (!(tps->chgstatus & ~(TPS_CHG_USB|TPS_CHG_AC))
+				&& (tps->chgstatus & (TPS_CHG_USB|TPS_CHG_AC))
+				&& (tps->chgconf & TPS_CHARGE_ENABLE)
+				) {
+			if (tps->chgstatus & TPS_CHG_USB) {
+				/* VBUS options are readonly until reconnect */
+				if (mask & TPS_CHG_USB)
+					set_bit(FLAG_VBUS_CHANGED, &tps->flags);
+				charging = 1;
+			} else if (tps->chgstatus & TPS_CHG_AC)
+				charging = 1;
+		}
+		if (charging != tps->charging) {
+			tps->charging = charging;
+			pr_info("%s: battery %scharging\n",
+				DRIVER_NAME, charging ? "" :
+				((tps->chgstatus & (TPS_CHG_USB|TPS_CHG_AC))
+					? "NOT " : "dis"));
+		}
+	}
+
+	/* always poll to detect (a) power removal, without tps65013
+	 * NO_CHG IRQ; or (b) restart of charging after stop.
+	 */
+	if ((tps->model != TPS65013 || !tps->charging)
+			&& (tps->chgstatus & (TPS_CHG_USB|TPS_CHG_AC)))
+		poll = 1;
+	if (poll)
+		schedule_delayed_work(&tps->work, POWER_POLL_DELAY);
+
+	/* also potentially gpio-in rise or fall */
+}
+
+/* handle IRQs and polling using keventd for now */
+static void tps65010_work(struct work_struct *work)
+{
+	struct tps65010		*tps;
+
+	tps = container_of(to_delayed_work(work), struct tps65010, work);
+	mutex_lock(&tps->lock);
+
+	tps65010_interrupt(tps);
+
+	if (test_and_clear_bit(FLAG_VBUS_CHANGED, &tps->flags)) {
+		int	status;
+		u8	chgconfig, tmp;
+
+		chgconfig = i2c_smbus_read_byte_data(tps->client,
+					TPS_CHGCONFIG);
+		chgconfig &= ~(TPS_VBUS_500MA | TPS_VBUS_CHARGING);
+		if (tps->vbus == 500)
+			chgconfig |= TPS_VBUS_500MA | TPS_VBUS_CHARGING;
+		else if (tps->vbus >= 100)
+			chgconfig |= TPS_VBUS_CHARGING;
+
+		status = i2c_smbus_write_byte_data(tps->client,
+				TPS_CHGCONFIG, chgconfig);
+
+		/* vbus update fails unless VBUS is connected! */
+		tmp = i2c_smbus_read_byte_data(tps->client, TPS_CHGCONFIG);
+		tps->chgconf = tmp;
+		show_chgconfig(tps->por, "update vbus", tmp);
+	}
+
+	if (test_and_clear_bit(FLAG_IRQ_ENABLE, &tps->flags))
+		enable_irq(tps->client->irq);
+
+	mutex_unlock(&tps->lock);
+}
+
+static irqreturn_t tps65010_irq(int irq, void *_tps)
+{
+	struct tps65010		*tps = _tps;
+
+	disable_irq_nosync(irq);
+	set_bit(FLAG_IRQ_ENABLE, &tps->flags);
+	schedule_delayed_work(&tps->work, 0);
+	return IRQ_HANDLED;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* offsets 0..3 == GPIO1..GPIO4
+ * offsets 4..5 == LED1/nPG, LED2 (we set one of the non-BLINK modes)
+ * offset 6 == vibrator motor driver
+ */
+static void
+tps65010_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+	if (offset < 4)
+		tps65010_set_gpio_out_value(offset + 1, value);
+	else if (offset < 6)
+		tps65010_set_led(offset - 3, value ? ON : OFF);
+	else
+		tps65010_set_vib(value);
+}
+
+static int
+tps65010_output(struct gpio_chip *chip, unsigned offset, int value)
+{
+	/* GPIOs may be input-only */
+	if (offset < 4) {
+		struct tps65010		*tps;
+
+		tps = container_of(chip, struct tps65010, chip);
+		if (!(tps->outmask & (1 << offset)))
+			return -EINVAL;
+		tps65010_set_gpio_out_value(offset + 1, value);
+	} else if (offset < 6)
+		tps65010_set_led(offset - 3, value ? ON : OFF);
+	else
+		tps65010_set_vib(value);
+
+	return 0;
+}
+
+static int tps65010_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+	int			value;
+	struct tps65010		*tps;
+
+	tps = container_of(chip, struct tps65010, chip);
+
+	if (offset < 4) {
+		value = i2c_smbus_read_byte_data(tps->client, TPS_DEFGPIO);
+		if (value < 0)
+			return 0;
+		if (value & (1 << (offset + 4)))	/* output */
+			return !(value & (1 << offset));
+		else					/* input */
+			return (value & (1 << offset));
+	}
+
+	/* REVISIT we *could* report LED1/nPG and LED2 state ... */
+	return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static struct tps65010 *the_tps;
+
+static int __exit tps65010_remove(struct i2c_client *client)
+{
+	struct tps65010		*tps = i2c_get_clientdata(client);
+	struct tps65010_board	*board = client->dev.platform_data;
+
+	if (board && board->teardown) {
+		int status = board->teardown(client, board->context);
+		if (status < 0)
+			dev_dbg(&client->dev, "board %s %s err %d\n",
+				"teardown", client->name, status);
+	}
+	if (client->irq > 0)
+		free_irq(client->irq, tps);
+	cancel_delayed_work_sync(&tps->work);
+	debugfs_remove(tps->file);
+	kfree(tps);
+	the_tps = NULL;
+	return 0;
+}
+
+static int tps65010_probe(struct i2c_client *client,
+			  const struct i2c_device_id *id)
+{
+	struct tps65010		*tps;
+	int			status;
+	struct tps65010_board	*board = client->dev.platform_data;
+
+	if (the_tps) {
+		dev_dbg(&client->dev, "only one tps6501x chip allowed\n");
+		return -ENODEV;
+	}
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+		return -EINVAL;
+
+	tps = kzalloc(sizeof *tps, GFP_KERNEL);
+	if (!tps)
+		return -ENOMEM;
+
+	mutex_init(&tps->lock);
+	INIT_DELAYED_WORK(&tps->work, tps65010_work);
+	tps->client = client;
+	tps->model = id->driver_data;
+
+	/* the IRQ is active low, but many gpio lines can't support that
+	 * so this driver uses falling-edge triggers instead.
+	 */
+	if (client->irq > 0) {
+		status = request_irq(client->irq, tps65010_irq,
+			IRQF_SAMPLE_RANDOM | IRQF_TRIGGER_FALLING,
+			DRIVER_NAME, tps);
+		if (status < 0) {
+			dev_dbg(&client->dev, "can't get IRQ %d, err %d\n",
+					client->irq, status);
+			goto fail1;
+		}
+		/* annoying race here, ideally we'd have an option
+		 * to claim the irq now and enable it later.
+		 * FIXME genirq IRQF_NOAUTOEN now solves that ...
+		 */
+		disable_irq(client->irq);
+		set_bit(FLAG_IRQ_ENABLE, &tps->flags);
+	} else
+		dev_warn(&client->dev, "IRQ not configured!\n");
+
+
+	switch (tps->model) {
+	case TPS65010:
+	case TPS65012:
+		tps->por = 1;
+		break;
+	/* else CHGCONFIG.POR is replaced by AUA, enabling a WAIT mode */
+	}
+	tps->chgconf = i2c_smbus_read_byte_data(client, TPS_CHGCONFIG);
+	show_chgconfig(tps->por, "conf/init", tps->chgconf);
+
+	show_chgstatus("chg/init",
+		i2c_smbus_read_byte_data(client, TPS_CHGSTATUS));
+	show_regstatus("reg/init",
+		i2c_smbus_read_byte_data(client, TPS_REGSTATUS));
+
+	pr_debug("%s: vdcdc1 0x%02x, vdcdc2 %02x, vregs1 %02x\n", DRIVER_NAME,
+		i2c_smbus_read_byte_data(client, TPS_VDCDC1),
+		i2c_smbus_read_byte_data(client, TPS_VDCDC2),
+		i2c_smbus_read_byte_data(client, TPS_VREGS1));
+	pr_debug("%s: defgpio 0x%02x, mask3 0x%02x\n", DRIVER_NAME,
+		i2c_smbus_read_byte_data(client, TPS_DEFGPIO),
+		i2c_smbus_read_byte_data(client, TPS_MASK3));
+
+	i2c_set_clientdata(client, tps);
+	the_tps = tps;
+
+#if	defined(CONFIG_USB_GADGET) && !defined(CONFIG_USB_OTG)
+	/* USB hosts can't draw VBUS.  OTG devices could, later
+	 * when OTG infrastructure enables it.  USB peripherals
+	 * could be relying on VBUS while booting, though.
+	 */
+	tps->vbus = 100;
+#endif
+
+	/* unmask the "interesting" irqs, then poll once to
+	 * kickstart monitoring, initialize shadowed status
+	 * registers, and maybe disable VBUS draw.
+	 */
+	tps->nmask1 = ~0;
+	(void) i2c_smbus_write_byte_data(client, TPS_MASK1, ~tps->nmask1);
+
+	tps->nmask2 = TPS_REG_ONOFF;
+	if (tps->model == TPS65013)
+		tps->nmask2 |= TPS_REG_NO_CHG;
+	(void) i2c_smbus_write_byte_data(client, TPS_MASK2, ~tps->nmask2);
+
+	(void) i2c_smbus_write_byte_data(client, TPS_MASK3, 0x0f
+		| i2c_smbus_read_byte_data(client, TPS_MASK3));
+
+	tps65010_work(&tps->work.work);
+
+	tps->file = debugfs_create_file(DRIVER_NAME, S_IRUGO, NULL,
+				tps, DEBUG_FOPS);
+
+	/* optionally register GPIOs */
+	if (board && board->base != 0) {
+		tps->outmask = board->outmask;
+
+		tps->chip.label = client->name;
+		tps->chip.dev = &client->dev;
+		tps->chip.owner = THIS_MODULE;
+
+		tps->chip.set = tps65010_gpio_set;
+		tps->chip.direction_output = tps65010_output;
+
+		/* NOTE:  only partial support for inputs; nyet IRQs */
+		tps->chip.get = tps65010_gpio_get;
+
+		tps->chip.base = board->base;
+		tps->chip.ngpio = 7;
+		tps->chip.can_sleep = 1;
+
+		status = gpiochip_add(&tps->chip);
+		if (status < 0)
+			dev_err(&client->dev, "can't add gpiochip, err %d\n",
+					status);
+		else if (board->setup) {
+			status = board->setup(client, board->context);
+			if (status < 0) {
+				dev_dbg(&client->dev,
+					"board %s %s err %d\n",
+					"setup", client->name, status);
+				status = 0;
+			}
+		}
+	}
+
+	return 0;
+fail1:
+	kfree(tps);
+	return status;
+}
+
+static const struct i2c_device_id tps65010_id[] = {
+	{ "tps65010", TPS65010 },
+	{ "tps65011", TPS65011 },
+	{ "tps65012", TPS65012 },
+	{ "tps65013", TPS65013 },
+	{ "tps65014", TPS65011 },	/* tps65011 charging at 6.5V max */
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, tps65010_id);
+
+static struct i2c_driver tps65010_driver = {
+	.driver = {
+		.name	= "tps65010",
+	},
+	.probe	= tps65010_probe,
+	.remove	= __exit_p(tps65010_remove),
+	.id_table = tps65010_id,
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* Draw from VBUS:
+ *   0 mA -- DON'T DRAW (might supply power instead)
+ * 100 mA -- usb unit load (slowest charge rate)
+ * 500 mA -- usb high power (fast battery charge)
+ */
+int tps65010_set_vbus_draw(unsigned mA)
+{
+	unsigned long	flags;
+
+	if (!the_tps)
+		return -ENODEV;
+
+	/* assumes non-SMP */
+	local_irq_save(flags);
+	if (mA >= 500)
+		mA = 500;
+	else if (mA >= 100)
+		mA = 100;
+	else
+		mA = 0;
+	the_tps->vbus = mA;
+	if ((the_tps->chgstatus & TPS_CHG_USB)
+			&& test_and_set_bit(
+				FLAG_VBUS_CHANGED, &the_tps->flags)) {
+		/* gadget drivers call this in_irq() */
+		schedule_delayed_work(&the_tps->work, 0);
+	}
+	local_irq_restore(flags);
+
+	return 0;
+}
+EXPORT_SYMBOL(tps65010_set_vbus_draw);
+
+/*-------------------------------------------------------------------------*/
+/* tps65010_set_gpio_out_value parameter:
+ * gpio:  GPIO1, GPIO2, GPIO3 or GPIO4
+ * value: LOW or HIGH
+ */
+int tps65010_set_gpio_out_value(unsigned gpio, unsigned value)
+{
+	int	 status;
+	unsigned defgpio;
+
+	if (!the_tps)
+		return -ENODEV;
+	if ((gpio < GPIO1) || (gpio > GPIO4))
+		return -EINVAL;
+
+	mutex_lock(&the_tps->lock);
+
+	defgpio = i2c_smbus_read_byte_data(the_tps->client, TPS_DEFGPIO);
+
+	/* Configure GPIO for output */
+	defgpio |= 1 << (gpio + 3);
+
+	/* Writing 1 forces a logic 0 on that GPIO and vice versa */
+	switch (value) {
+	case LOW:
+		defgpio |= 1 << (gpio - 1);    /* set GPIO low by writing 1 */
+		break;
+	/* case HIGH: */
+	default:
+		defgpio &= ~(1 << (gpio - 1)); /* set GPIO high by writing 0 */
+		break;
+	}
+
+	status = i2c_smbus_write_byte_data(the_tps->client,
+		TPS_DEFGPIO, defgpio);
+
+	pr_debug("%s: gpio%dout = %s, defgpio 0x%02x\n", DRIVER_NAME,
+		gpio, value ? "high" : "low",
+		i2c_smbus_read_byte_data(the_tps->client, TPS_DEFGPIO));
+
+	mutex_unlock(&the_tps->lock);
+	return status;
+}
+EXPORT_SYMBOL(tps65010_set_gpio_out_value);
+
+/*-------------------------------------------------------------------------*/
+/* tps65010_set_led parameter:
+ * led:  LED1 or LED2
+ * mode: ON, OFF or BLINK
+ */
+int tps65010_set_led(unsigned led, unsigned mode)
+{
+	int	 status;
+	unsigned led_on, led_per, offs;
+
+	if (!the_tps)
+		return -ENODEV;
+
+	if (led == LED1)
+		offs = 0;
+	else {
+		offs = 2;
+		led = LED2;
+	}
+
+	mutex_lock(&the_tps->lock);
+
+	pr_debug("%s: led%i_on   0x%02x\n", DRIVER_NAME, led,
+		i2c_smbus_read_byte_data(the_tps->client,
+				TPS_LED1_ON + offs));
+
+	pr_debug("%s: led%i_per  0x%02x\n", DRIVER_NAME, led,
+		i2c_smbus_read_byte_data(the_tps->client,
+				TPS_LED1_PER + offs));
+
+	switch (mode) {
+	case OFF:
+		led_on  = 1 << 7;
+		led_per = 0 << 7;
+		break;
+	case ON:
+		led_on  = 1 << 7;
+		led_per = 1 << 7;
+		break;
+	case BLINK:
+		led_on  = 0x30 | (0 << 7);
+		led_per = 0x08 | (1 << 7);
+		break;
+	default:
+		printk(KERN_ERR "%s: Wrong mode parameter for set_led()\n",
+		       DRIVER_NAME);
+		mutex_unlock(&the_tps->lock);
+		return -EINVAL;
+	}
+
+	status = i2c_smbus_write_byte_data(the_tps->client,
+			TPS_LED1_ON + offs, led_on);
+
+	if (status != 0) {
+		printk(KERN_ERR "%s: Failed to write led%i_on register\n",
+		       DRIVER_NAME, led);
+		mutex_unlock(&the_tps->lock);
+		return status;
+	}
+
+	pr_debug("%s: led%i_on   0x%02x\n", DRIVER_NAME, led,
+		i2c_smbus_read_byte_data(the_tps->client, TPS_LED1_ON + offs));
+
+	status = i2c_smbus_write_byte_data(the_tps->client,
+			TPS_LED1_PER + offs, led_per);
+
+	if (status != 0) {
+		printk(KERN_ERR "%s: Failed to write led%i_per register\n",
+		       DRIVER_NAME, led);
+		mutex_unlock(&the_tps->lock);
+		return status;
+	}
+
+	pr_debug("%s: led%i_per  0x%02x\n", DRIVER_NAME, led,
+		i2c_smbus_read_byte_data(the_tps->client,
+				TPS_LED1_PER + offs));
+
+	mutex_unlock(&the_tps->lock);
+
+	return status;
+}
+EXPORT_SYMBOL(tps65010_set_led);
+
+/*-------------------------------------------------------------------------*/
+/* tps65010_set_vib parameter:
+ * value: ON or OFF
+ */
+int tps65010_set_vib(unsigned value)
+{
+	int	 status;
+	unsigned vdcdc2;
+
+	if (!the_tps)
+		return -ENODEV;
+
+	mutex_lock(&the_tps->lock);
+
+	vdcdc2 = i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC2);
+	vdcdc2 &= ~(1 << 1);
+	if (value)
+		vdcdc2 |= (1 << 1);
+	status = i2c_smbus_write_byte_data(the_tps->client,
+		TPS_VDCDC2, vdcdc2);
+
+	pr_debug("%s: vibrator %s\n", DRIVER_NAME, value ? "on" : "off");
+
+	mutex_unlock(&the_tps->lock);
+	return status;
+}
+EXPORT_SYMBOL(tps65010_set_vib);
+
+/*-------------------------------------------------------------------------*/
+/* tps65010_set_low_pwr parameter:
+ * mode: ON or OFF
+ */
+int tps65010_set_low_pwr(unsigned mode)
+{
+	int	 status;
+	unsigned vdcdc1;
+
+	if (!the_tps)
+		return -ENODEV;
+
+	mutex_lock(&the_tps->lock);
+
+	pr_debug("%s: %s low_pwr, vdcdc1 0x%02x\n", DRIVER_NAME,
+		mode ? "enable" : "disable",
+		i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC1));
+
+	vdcdc1 = i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC1);
+
+	switch (mode) {
+	case OFF:
+		vdcdc1 &= ~TPS_ENABLE_LP; /* disable ENABLE_LP bit */
+		break;
+	/* case ON: */
+	default:
+		vdcdc1 |= TPS_ENABLE_LP;  /* enable ENABLE_LP bit */
+		break;
+	}
+
+	status = i2c_smbus_write_byte_data(the_tps->client,
+			TPS_VDCDC1, vdcdc1);
+
+	if (status != 0)
+		printk(KERN_ERR "%s: Failed to write vdcdc1 register\n",
+			DRIVER_NAME);
+	else
+		pr_debug("%s: vdcdc1 0x%02x\n", DRIVER_NAME,
+			i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC1));
+
+	mutex_unlock(&the_tps->lock);
+
+	return status;
+}
+EXPORT_SYMBOL(tps65010_set_low_pwr);
+
+/*-------------------------------------------------------------------------*/
+/* tps65010_config_vregs1 parameter:
+ * value to be written to VREGS1 register
+ * Note: The complete register is written, set all bits you need
+ */
+int tps65010_config_vregs1(unsigned value)
+{
+	int	 status;
+
+	if (!the_tps)
+		return -ENODEV;
+
+	mutex_lock(&the_tps->lock);
+
+	pr_debug("%s: vregs1 0x%02x\n", DRIVER_NAME,
+			i2c_smbus_read_byte_data(the_tps->client, TPS_VREGS1));
+
+	status = i2c_smbus_write_byte_data(the_tps->client,
+			TPS_VREGS1, value);
+
+	if (status != 0)
+		printk(KERN_ERR "%s: Failed to write vregs1 register\n",
+			DRIVER_NAME);
+	else
+		pr_debug("%s: vregs1 0x%02x\n", DRIVER_NAME,
+			i2c_smbus_read_byte_data(the_tps->client, TPS_VREGS1));
+
+	mutex_unlock(&the_tps->lock);
+
+	return status;
+}
+EXPORT_SYMBOL(tps65010_config_vregs1);
+
+int tps65010_config_vdcdc2(unsigned value)
+{
+	struct i2c_client *c;
+	int	 status;
+
+	if (!the_tps)
+		return -ENODEV;
+
+	c = the_tps->client;
+	mutex_lock(&the_tps->lock);
+
+	pr_debug("%s: vdcdc2 0x%02x\n", DRIVER_NAME,
+		 i2c_smbus_read_byte_data(c, TPS_VDCDC2));
+
+	status = i2c_smbus_write_byte_data(c, TPS_VDCDC2, value);
+
+	if (status != 0)
+		printk(KERN_ERR "%s: Failed to write vdcdc2 register\n",
+			DRIVER_NAME);
+	else
+		pr_debug("%s: vregs1 0x%02x\n", DRIVER_NAME,
+			 i2c_smbus_read_byte_data(c, TPS_VDCDC2));
+
+	mutex_unlock(&the_tps->lock);
+	return status;
+}
+EXPORT_SYMBOL(tps65010_config_vdcdc2);
+
+/*-------------------------------------------------------------------------*/
+/* tps65013_set_low_pwr parameter:
+ * mode: ON or OFF
+ */
+
+/* FIXME: Assumes AC or USB power is present. Setting AUA bit is not
+	required if power supply is through a battery */
+
+int tps65013_set_low_pwr(unsigned mode)
+{
+	int	 status;
+	unsigned vdcdc1, chgconfig;
+
+	if (!the_tps || the_tps->por)
+		return -ENODEV;
+
+	mutex_lock(&the_tps->lock);
+
+	pr_debug("%s: %s low_pwr, chgconfig 0x%02x vdcdc1 0x%02x\n",
+		DRIVER_NAME,
+		mode ? "enable" : "disable",
+		i2c_smbus_read_byte_data(the_tps->client, TPS_CHGCONFIG),
+		i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC1));
+
+	chgconfig = i2c_smbus_read_byte_data(the_tps->client, TPS_CHGCONFIG);
+	vdcdc1 = i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC1);
+
+	switch (mode) {
+	case OFF:
+		chgconfig &= ~TPS65013_AUA; /* disable AUA bit */
+		vdcdc1 &= ~TPS_ENABLE_LP; /* disable ENABLE_LP bit */
+		break;
+	/* case ON: */
+	default:
+		chgconfig |= TPS65013_AUA;  /* enable AUA bit */
+		vdcdc1 |= TPS_ENABLE_LP;  /* enable ENABLE_LP bit */
+		break;
+	}
+
+	status = i2c_smbus_write_byte_data(the_tps->client,
+			TPS_CHGCONFIG, chgconfig);
+	if (status != 0) {
+		printk(KERN_ERR "%s: Failed to write chconfig register\n",
+	 DRIVER_NAME);
+		mutex_unlock(&the_tps->lock);
+		return status;
+	}
+
+	chgconfig = i2c_smbus_read_byte_data(the_tps->client, TPS_CHGCONFIG);
+	the_tps->chgconf = chgconfig;
+	show_chgconfig(0, "chgconf", chgconfig);
+
+	status = i2c_smbus_write_byte_data(the_tps->client,
+			TPS_VDCDC1, vdcdc1);
+
+	if (status != 0)
+		printk(KERN_ERR "%s: Failed to write vdcdc1 register\n",
+	 DRIVER_NAME);
+	else
+		pr_debug("%s: vdcdc1 0x%02x\n", DRIVER_NAME,
+			i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC1));
+
+	mutex_unlock(&the_tps->lock);
+
+	return status;
+}
+EXPORT_SYMBOL(tps65013_set_low_pwr);
+
+/*-------------------------------------------------------------------------*/
+
+static int __init tps_init(void)
+{
+	u32	tries = 3;
+	int	status = -ENODEV;
+
+	printk(KERN_INFO "%s: version %s\n", DRIVER_NAME, DRIVER_VERSION);
+
+	/* some boards have startup glitches */
+	while (tries--) {
+		status = i2c_add_driver(&tps65010_driver);
+		if (the_tps)
+			break;
+		i2c_del_driver(&tps65010_driver);
+		if (!tries) {
+			printk(KERN_ERR "%s: no chip?\n", DRIVER_NAME);
+			return -ENODEV;
+		}
+		pr_debug("%s: re-probe ...\n", DRIVER_NAME);
+		msleep(10);
+	}
+
+	return status;
+}
+/* NOTE:  this MUST be initialized before the other parts of the system
+ * that rely on it ... but after the i2c bus on which this relies.
+ * That is, much earlier than on PC-type systems, which don't often use
+ * I2C as a core system bus.
+ */
+subsys_initcall(tps_init);
+
+static void __exit tps_exit(void)
+{
+	i2c_del_driver(&tps65010_driver);
+}
+module_exit(tps_exit);
+
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/tps6507x.c b/ap/os/linux/linux-3.4.x/drivers/mfd/tps6507x.c
new file mode 100644
index 0000000..33ba772
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/tps6507x.c
@@ -0,0 +1,157 @@
+/*
+ * tps6507x.c  --  TPS6507x chip family multi-function driver
+ *
+ *  Copyright (c) 2010 RidgeRun (todd.fischer@ridgerun.com)
+ *
+ * Author: Todd Fischer
+ *         todd.fischer@ridgerun.com
+ *
+ * Credits:
+ *
+ *    Using code from wm831x-*.c, wm8400-core, Wolfson Microelectronics PLC.
+ *
+ * For licencing details see kernel-base/COPYING
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/tps6507x.h>
+
+static struct mfd_cell tps6507x_devs[] = {
+	{
+		.name = "tps6507x-pmic",
+	},
+	{
+		.name = "tps6507x-ts",
+	},
+};
+
+
+static int tps6507x_i2c_read_device(struct tps6507x_dev *tps6507x, char reg,
+				  int bytes, void *dest)
+{
+	struct i2c_client *i2c = tps6507x->i2c_client;
+	struct i2c_msg xfer[2];
+	int ret;
+
+	/* Write register */
+	xfer[0].addr = i2c->addr;
+	xfer[0].flags = 0;
+	xfer[0].len = 1;
+	xfer[0].buf = &reg;
+
+	/* Read data */
+	xfer[1].addr = i2c->addr;
+	xfer[1].flags = I2C_M_RD;
+	xfer[1].len = bytes;
+	xfer[1].buf = dest;
+
+	ret = i2c_transfer(i2c->adapter, xfer, 2);
+	if (ret == 2)
+		ret = 0;
+	else if (ret >= 0)
+		ret = -EIO;
+
+	return ret;
+}
+
+static int tps6507x_i2c_write_device(struct tps6507x_dev *tps6507x, char reg,
+				   int bytes, void *src)
+{
+	struct i2c_client *i2c = tps6507x->i2c_client;
+	/* we add 1 byte for device register */
+	u8 msg[TPS6507X_MAX_REGISTER + 1];
+	int ret;
+
+	if (bytes > TPS6507X_MAX_REGISTER)
+		return -EINVAL;
+
+	msg[0] = reg;
+	memcpy(&msg[1], src, bytes);
+
+	ret = i2c_master_send(i2c, msg, bytes + 1);
+	if (ret < 0)
+		return ret;
+	if (ret != bytes + 1)
+		return -EIO;
+	return 0;
+}
+
+static int tps6507x_i2c_probe(struct i2c_client *i2c,
+			    const struct i2c_device_id *id)
+{
+	struct tps6507x_dev *tps6507x;
+	int ret = 0;
+
+	tps6507x = kzalloc(sizeof(struct tps6507x_dev), GFP_KERNEL);
+	if (tps6507x == NULL)
+		return -ENOMEM;
+
+	i2c_set_clientdata(i2c, tps6507x);
+	tps6507x->dev = &i2c->dev;
+	tps6507x->i2c_client = i2c;
+	tps6507x->read_dev = tps6507x_i2c_read_device;
+	tps6507x->write_dev = tps6507x_i2c_write_device;
+
+	ret = mfd_add_devices(tps6507x->dev, -1,
+			      tps6507x_devs, ARRAY_SIZE(tps6507x_devs),
+			      NULL, 0);
+
+	if (ret < 0)
+		goto err;
+
+	return ret;
+
+err:
+	mfd_remove_devices(tps6507x->dev);
+	kfree(tps6507x);
+	return ret;
+}
+
+static int tps6507x_i2c_remove(struct i2c_client *i2c)
+{
+	struct tps6507x_dev *tps6507x = i2c_get_clientdata(i2c);
+
+	mfd_remove_devices(tps6507x->dev);
+	kfree(tps6507x);
+
+	return 0;
+}
+
+static const struct i2c_device_id tps6507x_i2c_id[] = {
+       { "tps6507x", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, tps6507x_i2c_id);
+
+
+static struct i2c_driver tps6507x_i2c_driver = {
+	.driver = {
+		   .name = "tps6507x",
+		   .owner = THIS_MODULE,
+	},
+	.probe = tps6507x_i2c_probe,
+	.remove = tps6507x_i2c_remove,
+	.id_table = tps6507x_i2c_id,
+};
+
+static int __init tps6507x_i2c_init(void)
+{
+	return i2c_add_driver(&tps6507x_i2c_driver);
+}
+/* init early so consumer devices can complete system boot */
+subsys_initcall(tps6507x_i2c_init);
+
+static void __exit tps6507x_i2c_exit(void)
+{
+	i2c_del_driver(&tps6507x_i2c_driver);
+}
+module_exit(tps6507x_i2c_exit);
+
+MODULE_DESCRIPTION("TPS6507x chip family multi-function driver");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/tps65090.c b/ap/os/linux/linux-3.4.x/drivers/mfd/tps65090.c
new file mode 100644
index 0000000..a66d4df
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/tps65090.c
@@ -0,0 +1,387 @@
+/*
+ * Core driver for TI TPS65090 PMIC family
+ *
+ * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.
+
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/tps65090.h>
+#include <linux/regmap.h>
+#include <linux/err.h>
+
+#define NUM_INT_REG 2
+#define TOTAL_NUM_REG 0x18
+
+/* interrupt status registers */
+#define TPS65090_INT_STS	0x0
+#define TPS65090_INT_STS2	0x1
+
+/* interrupt mask registers */
+#define TPS65090_INT_MSK	0x2
+#define TPS65090_INT_MSK2	0x3
+
+struct tps65090_irq_data {
+	u8		mask_reg;
+	u8		mask_pos;
+};
+
+#define TPS65090_IRQ(_reg, _mask_pos)		\
+	{					\
+		.mask_reg	= (_reg),	\
+		.mask_pos	= (_mask_pos),	\
+	}
+
+static const struct tps65090_irq_data tps65090_irqs[] = {
+	[0]		= TPS65090_IRQ(0, 0),
+	[1]		= TPS65090_IRQ(0, 1),
+	[2]		= TPS65090_IRQ(0, 2),
+	[3]		= TPS65090_IRQ(0, 3),
+	[4]		= TPS65090_IRQ(0, 4),
+	[5]		= TPS65090_IRQ(0, 5),
+	[6]		= TPS65090_IRQ(0, 6),
+	[7]		= TPS65090_IRQ(0, 7),
+	[8]		= TPS65090_IRQ(1, 0),
+	[9]		= TPS65090_IRQ(1, 1),
+	[10]		= TPS65090_IRQ(1, 2),
+	[11]		= TPS65090_IRQ(1, 3),
+	[12]		= TPS65090_IRQ(1, 4),
+	[13]		= TPS65090_IRQ(1, 5),
+	[14]		= TPS65090_IRQ(1, 6),
+	[15]		= TPS65090_IRQ(1, 7),
+};
+
+static struct mfd_cell tps65090s[] = {
+	{
+		.name = "tps65910-pmic",
+	},
+	{
+		.name = "tps65910-regulator",
+	},
+};
+
+struct tps65090 {
+	struct mutex		lock;
+	struct device		*dev;
+	struct i2c_client	*client;
+	struct regmap		*rmap;
+	struct irq_chip		irq_chip;
+	struct mutex		irq_lock;
+	int			irq_base;
+	unsigned int		id;
+};
+
+int tps65090_write(struct device *dev, int reg, uint8_t val)
+{
+	struct tps65090 *tps = dev_get_drvdata(dev);
+	return regmap_write(tps->rmap, reg, val);
+}
+EXPORT_SYMBOL_GPL(tps65090_write);
+
+int tps65090_read(struct device *dev, int reg, uint8_t *val)
+{
+	struct tps65090 *tps = dev_get_drvdata(dev);
+	unsigned int temp_val;
+	int ret;
+	ret = regmap_read(tps->rmap, reg, &temp_val);
+	if (!ret)
+		*val = temp_val;
+	return ret;
+}
+EXPORT_SYMBOL_GPL(tps65090_read);
+
+int tps65090_set_bits(struct device *dev, int reg, uint8_t bit_num)
+{
+	struct tps65090 *tps = dev_get_drvdata(dev);
+	return regmap_update_bits(tps->rmap, reg, BIT(bit_num), ~0u);
+}
+EXPORT_SYMBOL_GPL(tps65090_set_bits);
+
+int tps65090_clr_bits(struct device *dev, int reg, uint8_t bit_num)
+{
+	struct tps65090 *tps = dev_get_drvdata(dev);
+	return regmap_update_bits(tps->rmap, reg, BIT(bit_num), 0u);
+}
+EXPORT_SYMBOL_GPL(tps65090_clr_bits);
+
+static void tps65090_irq_lock(struct irq_data *data)
+{
+	struct tps65090 *tps65090 = irq_data_get_irq_chip_data(data);
+
+	mutex_lock(&tps65090->irq_lock);
+}
+
+static void tps65090_irq_mask(struct irq_data *irq_data)
+{
+	struct tps65090 *tps65090 = irq_data_get_irq_chip_data(irq_data);
+	unsigned int __irq = irq_data->hwirq;
+	const struct tps65090_irq_data *data = &tps65090_irqs[__irq];
+
+	tps65090_set_bits(tps65090->dev, (TPS65090_INT_MSK + data->mask_reg),
+		data->mask_pos);
+}
+
+static void tps65090_irq_unmask(struct irq_data *irq_data)
+{
+	struct tps65090 *tps65090 = irq_data_get_irq_chip_data(irq_data);
+	unsigned int __irq = irq_data->irq - tps65090->irq_base;
+	const struct tps65090_irq_data *data = &tps65090_irqs[__irq];
+
+	tps65090_clr_bits(tps65090->dev, (TPS65090_INT_MSK + data->mask_reg),
+		data->mask_pos);
+}
+
+static void tps65090_irq_sync_unlock(struct irq_data *data)
+{
+	struct tps65090 *tps65090 = irq_data_get_irq_chip_data(data);
+
+	mutex_unlock(&tps65090->irq_lock);
+}
+
+static irqreturn_t tps65090_irq(int irq, void *data)
+{
+	struct tps65090 *tps65090 = data;
+	int ret = 0;
+	u8 status, mask;
+	unsigned long int acks = 0;
+	int i;
+
+	for (i = 0; i < NUM_INT_REG; i++) {
+		ret = tps65090_read(tps65090->dev, TPS65090_INT_MSK + i, &mask);
+		if (ret < 0) {
+			dev_err(tps65090->dev,
+				"failed to read mask reg [addr:%d]\n",
+				TPS65090_INT_MSK + i);
+			return IRQ_NONE;
+		}
+		ret = tps65090_read(tps65090->dev, TPS65090_INT_STS + i,
+			&status);
+		if (ret < 0) {
+			dev_err(tps65090->dev,
+				"failed to read status reg [addr:%d]\n",
+				 TPS65090_INT_STS + i);
+			return IRQ_NONE;
+		}
+		if (status) {
+			/* Ack only those interrupts which are not masked */
+			status &= (~mask);
+			ret = tps65090_write(tps65090->dev,
+					TPS65090_INT_STS + i, status);
+			if (ret < 0) {
+				dev_err(tps65090->dev,
+					"failed to write interrupt status\n");
+				return IRQ_NONE;
+			}
+			acks |= (status << (i * 8));
+		}
+	}
+
+	for_each_set_bit(i, &acks, ARRAY_SIZE(tps65090_irqs))
+		handle_nested_irq(tps65090->irq_base + i);
+	return acks ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static int __devinit tps65090_irq_init(struct tps65090 *tps65090, int irq,
+	int irq_base)
+{
+	int i, ret;
+
+	if (!irq_base) {
+		dev_err(tps65090->dev, "IRQ base not set\n");
+		return -EINVAL;
+	}
+
+	mutex_init(&tps65090->irq_lock);
+
+	for (i = 0; i < NUM_INT_REG; i++)
+		tps65090_write(tps65090->dev, TPS65090_INT_MSK + i, 0xFF);
+
+	for (i = 0; i < NUM_INT_REG; i++)
+		tps65090_write(tps65090->dev, TPS65090_INT_STS + i, 0xff);
+
+	tps65090->irq_base = irq_base;
+	tps65090->irq_chip.name = "tps65090";
+	tps65090->irq_chip.irq_mask = tps65090_irq_mask;
+	tps65090->irq_chip.irq_unmask = tps65090_irq_unmask;
+	tps65090->irq_chip.irq_bus_lock = tps65090_irq_lock;
+	tps65090->irq_chip.irq_bus_sync_unlock = tps65090_irq_sync_unlock;
+
+	for (i = 0; i < ARRAY_SIZE(tps65090_irqs); i++) {
+		int __irq = i + tps65090->irq_base;
+		irq_set_chip_data(__irq, tps65090);
+		irq_set_chip_and_handler(__irq, &tps65090->irq_chip,
+					 handle_simple_irq);
+		irq_set_nested_thread(__irq, 1);
+#ifdef CONFIG_ARM
+		set_irq_flags(__irq, IRQF_VALID);
+#endif
+	}
+
+	ret = request_threaded_irq(irq, NULL, tps65090_irq, IRQF_ONESHOT,
+				"tps65090", tps65090);
+	if (!ret) {
+		device_init_wakeup(tps65090->dev, 1);
+		enable_irq_wake(irq);
+	}
+
+	return ret;
+}
+
+static bool is_volatile_reg(struct device *dev, unsigned int reg)
+{
+	if ((reg == TPS65090_INT_STS) || (reg == TPS65090_INT_STS))
+		return true;
+	else
+		return false;
+}
+
+static const struct regmap_config tps65090_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.max_register = TOTAL_NUM_REG,
+	.num_reg_defaults_raw = TOTAL_NUM_REG,
+	.cache_type = REGCACHE_RBTREE,
+	.volatile_reg = is_volatile_reg,
+};
+
+static int __devinit tps65090_i2c_probe(struct i2c_client *client,
+					const struct i2c_device_id *id)
+{
+	struct tps65090_platform_data *pdata = client->dev.platform_data;
+	struct tps65090 *tps65090;
+	int ret;
+
+	if (!pdata) {
+		dev_err(&client->dev, "tps65090 requires platform data\n");
+		return -EINVAL;
+	}
+
+	tps65090 = devm_kzalloc(&client->dev, sizeof(struct tps65090),
+		GFP_KERNEL);
+	if (tps65090 == NULL)
+		return -ENOMEM;
+
+	tps65090->client = client;
+	tps65090->dev = &client->dev;
+	i2c_set_clientdata(client, tps65090);
+
+	mutex_init(&tps65090->lock);
+
+	if (client->irq) {
+		ret = tps65090_irq_init(tps65090, client->irq, pdata->irq_base);
+		if (ret) {
+			dev_err(&client->dev, "IRQ init failed with err: %d\n",
+				ret);
+			goto err_exit;
+		}
+	}
+
+	tps65090->rmap = regmap_init_i2c(tps65090->client,
+		&tps65090_regmap_config);
+	if (IS_ERR(tps65090->rmap)) {
+		dev_err(&client->dev, "regmap_init failed with err: %ld\n",
+			PTR_ERR(tps65090->rmap));
+		goto err_irq_exit;
+	};
+
+	ret = mfd_add_devices(tps65090->dev, -1, tps65090s,
+		ARRAY_SIZE(tps65090s), NULL, 0);
+	if (ret) {
+		dev_err(&client->dev, "add mfd devices failed with err: %d\n",
+			ret);
+		goto err_regmap_exit;
+	}
+
+	return 0;
+
+err_regmap_exit:
+	regmap_exit(tps65090->rmap);
+
+err_irq_exit:
+	if (client->irq)
+		free_irq(client->irq, tps65090);
+err_exit:
+	return ret;
+}
+
+static int __devexit tps65090_i2c_remove(struct i2c_client *client)
+{
+	struct tps65090 *tps65090 = i2c_get_clientdata(client);
+
+	mfd_remove_devices(tps65090->dev);
+	regmap_exit(tps65090->rmap);
+	if (client->irq)
+		free_irq(client->irq, tps65090);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int tps65090_i2c_suspend(struct i2c_client *client, pm_message_t state)
+{
+	if (client->irq)
+		disable_irq(client->irq);
+	return 0;
+}
+
+static int tps65090_i2c_resume(struct i2c_client *client)
+{
+	if (client->irq)
+		enable_irq(client->irq);
+	return 0;
+}
+#endif
+
+static const struct i2c_device_id tps65090_id_table[] = {
+	{ "tps65090", 0 },
+	{ },
+};
+MODULE_DEVICE_TABLE(i2c, tps65090_id_table);
+
+static struct i2c_driver tps65090_driver = {
+	.driver	= {
+		.name	= "tps65090",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= tps65090_i2c_probe,
+	.remove		= __devexit_p(tps65090_i2c_remove),
+#ifdef CONFIG_PM
+	.suspend	= tps65090_i2c_suspend,
+	.resume		= tps65090_i2c_resume,
+#endif
+	.id_table	= tps65090_id_table,
+};
+
+static int __init tps65090_init(void)
+{
+	return i2c_add_driver(&tps65090_driver);
+}
+subsys_initcall(tps65090_init);
+
+static void __exit tps65090_exit(void)
+{
+	i2c_del_driver(&tps65090_driver);
+}
+module_exit(tps65090_exit);
+
+MODULE_DESCRIPTION("TPS65090 core driver");
+MODULE_AUTHOR("Venu Byravarasu <vbyravarasu@nvidia.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/tps65217.c b/ap/os/linux/linux-3.4.x/drivers/mfd/tps65217.c
new file mode 100644
index 0000000..f7d854e
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/tps65217.c
@@ -0,0 +1,242 @@
+/*
+ * tps65217.c
+ *
+ * TPS65217 chip family multi-function driver
+ *
+ * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
+#include <linux/err.h>
+
+#include <linux/mfd/core.h>
+#include <linux/mfd/tps65217.h>
+
+/**
+ * tps65217_reg_read: Read a single tps65217 register.
+ *
+ * @tps: Device to read from.
+ * @reg: Register to read.
+ * @val: Contians the value
+ */
+int tps65217_reg_read(struct tps65217 *tps, unsigned int reg,
+			unsigned int *val)
+{
+	return regmap_read(tps->regmap, reg, val);
+}
+EXPORT_SYMBOL_GPL(tps65217_reg_read);
+
+/**
+ * tps65217_reg_write: Write a single tps65217 register.
+ *
+ * @tps65217: Device to write to.
+ * @reg: Register to write to.
+ * @val: Value to write.
+ * @level: Password protected level
+ */
+int tps65217_reg_write(struct tps65217 *tps, unsigned int reg,
+			unsigned int val, unsigned int level)
+{
+	int ret;
+	unsigned int xor_reg_val;
+
+	switch (level) {
+	case TPS65217_PROTECT_NONE:
+		return regmap_write(tps->regmap, reg, val);
+	case TPS65217_PROTECT_L1:
+		xor_reg_val = reg ^ TPS65217_PASSWORD_REGS_UNLOCK;
+		ret = regmap_write(tps->regmap, TPS65217_REG_PASSWORD,
+							xor_reg_val);
+		if (ret < 0)
+			return ret;
+
+		return regmap_write(tps->regmap, reg, val);
+	case TPS65217_PROTECT_L2:
+		xor_reg_val = reg ^ TPS65217_PASSWORD_REGS_UNLOCK;
+		ret = regmap_write(tps->regmap, TPS65217_REG_PASSWORD,
+							xor_reg_val);
+		if (ret < 0)
+			return ret;
+		ret = regmap_write(tps->regmap, reg, val);
+		if (ret < 0)
+			return ret;
+		ret = regmap_write(tps->regmap, TPS65217_REG_PASSWORD,
+							xor_reg_val);
+		if (ret < 0)
+			return ret;
+		return regmap_write(tps->regmap, reg, val);
+	default:
+		return -EINVAL;
+	}
+}
+EXPORT_SYMBOL_GPL(tps65217_reg_write);
+
+/**
+ * tps65217_update_bits: Modify bits w.r.t mask, val and level.
+ *
+ * @tps65217: Device to write to.
+ * @reg: Register to read-write to.
+ * @mask: Mask.
+ * @val: Value to write.
+ * @level: Password protected level
+ */
+int tps65217_update_bits(struct tps65217 *tps, unsigned int reg,
+		unsigned int mask, unsigned int val, unsigned int level)
+{
+	int ret;
+	unsigned int data;
+
+	ret = tps65217_reg_read(tps, reg, &data);
+	if (ret) {
+		dev_err(tps->dev, "Read from reg 0x%x failed\n", reg);
+		return ret;
+	}
+
+	data &= ~mask;
+	data |= val & mask;
+
+	ret = tps65217_reg_write(tps, reg, data, level);
+	if (ret)
+		dev_err(tps->dev, "Write for reg 0x%x failed\n", reg);
+
+	return ret;
+}
+
+int tps65217_set_bits(struct tps65217 *tps, unsigned int reg,
+		unsigned int mask, unsigned int val, unsigned int level)
+{
+	return tps65217_update_bits(tps, reg, mask, val, level);
+}
+EXPORT_SYMBOL_GPL(tps65217_set_bits);
+
+int tps65217_clear_bits(struct tps65217 *tps, unsigned int reg,
+		unsigned int mask, unsigned int level)
+{
+	return tps65217_update_bits(tps, reg, mask, 0, level);
+}
+EXPORT_SYMBOL_GPL(tps65217_clear_bits);
+
+static struct regmap_config tps65217_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+};
+
+static int __devinit tps65217_probe(struct i2c_client *client,
+				const struct i2c_device_id *ids)
+{
+	struct tps65217 *tps;
+	struct tps65217_board *pdata = client->dev.platform_data;
+	int i, ret;
+	unsigned int version;
+
+	tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL);
+	if (!tps)
+		return -ENOMEM;
+
+	tps->pdata = pdata;
+	tps->regmap = regmap_init_i2c(client, &tps65217_regmap_config);
+	if (IS_ERR(tps->regmap)) {
+		ret = PTR_ERR(tps->regmap);
+		dev_err(tps->dev, "Failed to allocate register map: %d\n",
+			ret);
+		return ret;
+	}
+
+	i2c_set_clientdata(client, tps);
+	tps->dev = &client->dev;
+
+	ret = tps65217_reg_read(tps, TPS65217_REG_CHIPID, &version);
+	if (ret < 0) {
+		dev_err(tps->dev, "Failed to read revision"
+					" register: %d\n", ret);
+		goto err_regmap;
+	}
+
+	dev_info(tps->dev, "TPS65217 ID %#x version 1.%d\n",
+			(version & TPS65217_CHIPID_CHIP_MASK) >> 4,
+			version & TPS65217_CHIPID_REV_MASK);
+
+	for (i = 0; i < TPS65217_NUM_REGULATOR; i++) {
+		struct platform_device *pdev;
+
+		pdev = platform_device_alloc("tps65217-pmic", i);
+		if (!pdev) {
+			dev_err(tps->dev, "Cannot create regulator %d\n", i);
+			continue;
+		}
+
+		pdev->dev.parent = tps->dev;
+		platform_device_add_data(pdev, &pdata->tps65217_init_data[i],
+					sizeof(pdata->tps65217_init_data[i]));
+		tps->regulator_pdev[i] = pdev;
+
+		platform_device_add(pdev);
+	}
+
+	return 0;
+
+err_regmap:
+	regmap_exit(tps->regmap);
+
+	return ret;
+}
+
+static int __devexit tps65217_remove(struct i2c_client *client)
+{
+	struct tps65217 *tps = i2c_get_clientdata(client);
+	int i;
+
+	for (i = 0; i < TPS65217_NUM_REGULATOR; i++)
+		platform_device_unregister(tps->regulator_pdev[i]);
+
+	regmap_exit(tps->regmap);
+
+	return 0;
+}
+
+static const struct i2c_device_id tps65217_id_table[] = {
+	{"tps65217", 0xF0},
+	{/* end of list */}
+};
+MODULE_DEVICE_TABLE(i2c, tps65217_id_table);
+
+static struct i2c_driver tps65217_driver = {
+	.driver		= {
+		.name	= "tps65217",
+	},
+	.id_table	= tps65217_id_table,
+	.probe		= tps65217_probe,
+	.remove		= __devexit_p(tps65217_remove),
+};
+
+static int __init tps65217_init(void)
+{
+	return i2c_add_driver(&tps65217_driver);
+}
+subsys_initcall(tps65217_init);
+
+static void __exit tps65217_exit(void)
+{
+	i2c_del_driver(&tps65217_driver);
+}
+module_exit(tps65217_exit);
+
+MODULE_AUTHOR("AnilKumar Ch <anilkumar@ti.com>");
+MODULE_DESCRIPTION("TPS65217 chip family multi-function driver");
+MODULE_LICENSE("GPL v2");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/tps6586x.c b/ap/os/linux/linux-3.4.x/drivers/mfd/tps6586x.c
new file mode 100644
index 0000000..a5ddf31
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/tps6586x.c
@@ -0,0 +1,596 @@
+/*
+ * Core driver for TI TPS6586x PMIC family
+ *
+ * Copyright (c) 2010 CompuLab Ltd.
+ * Mike Rapoport <mike@compulab.co.il>
+ *
+ * Based on da903x.c.
+ * Copyright (C) 2008 Compulab, Ltd.
+ * Mike Rapoport <mike@compulab.co.il>
+ * Copyright (C) 2006-2008 Marvell International Ltd.
+ * Eric Miao <eric.miao@marvell.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/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+
+#include <linux/mfd/core.h>
+#include <linux/mfd/tps6586x.h>
+
+/* GPIO control registers */
+#define TPS6586X_GPIOSET1	0x5d
+#define TPS6586X_GPIOSET2	0x5e
+
+/* interrupt control registers */
+#define TPS6586X_INT_ACK1	0xb5
+#define TPS6586X_INT_ACK2	0xb6
+#define TPS6586X_INT_ACK3	0xb7
+#define TPS6586X_INT_ACK4	0xb8
+
+/* interrupt mask registers */
+#define TPS6586X_INT_MASK1	0xb0
+#define TPS6586X_INT_MASK2	0xb1
+#define TPS6586X_INT_MASK3	0xb2
+#define TPS6586X_INT_MASK4	0xb3
+#define TPS6586X_INT_MASK5	0xb4
+
+/* device id */
+#define TPS6586X_VERSIONCRC	0xcd
+
+struct tps6586x_irq_data {
+	u8	mask_reg;
+	u8	mask_mask;
+};
+
+#define TPS6586X_IRQ(_reg, _mask)				\
+	{							\
+		.mask_reg = (_reg) - TPS6586X_INT_MASK1,	\
+		.mask_mask = (_mask),				\
+	}
+
+static const struct tps6586x_irq_data tps6586x_irqs[] = {
+	[TPS6586X_INT_PLDO_0]	= TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 0),
+	[TPS6586X_INT_PLDO_1]	= TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 1),
+	[TPS6586X_INT_PLDO_2]	= TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 2),
+	[TPS6586X_INT_PLDO_3]	= TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 3),
+	[TPS6586X_INT_PLDO_4]	= TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 4),
+	[TPS6586X_INT_PLDO_5]	= TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 5),
+	[TPS6586X_INT_PLDO_6]	= TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 6),
+	[TPS6586X_INT_PLDO_7]	= TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 7),
+	[TPS6586X_INT_COMP_DET]	= TPS6586X_IRQ(TPS6586X_INT_MASK4, 1 << 0),
+	[TPS6586X_INT_ADC]	= TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 1),
+	[TPS6586X_INT_PLDO_8]	= TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 2),
+	[TPS6586X_INT_PLDO_9]	= TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 3),
+	[TPS6586X_INT_PSM_0]	= TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 4),
+	[TPS6586X_INT_PSM_1]	= TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 5),
+	[TPS6586X_INT_PSM_2]	= TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 6),
+	[TPS6586X_INT_PSM_3]	= TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 7),
+	[TPS6586X_INT_RTC_ALM1]	= TPS6586X_IRQ(TPS6586X_INT_MASK5, 1 << 4),
+	[TPS6586X_INT_ACUSB_OVP] = TPS6586X_IRQ(TPS6586X_INT_MASK5, 0x03),
+	[TPS6586X_INT_USB_DET]	= TPS6586X_IRQ(TPS6586X_INT_MASK5, 1 << 2),
+	[TPS6586X_INT_AC_DET]	= TPS6586X_IRQ(TPS6586X_INT_MASK5, 1 << 3),
+	[TPS6586X_INT_BAT_DET]	= TPS6586X_IRQ(TPS6586X_INT_MASK3, 1 << 0),
+	[TPS6586X_INT_CHG_STAT]	= TPS6586X_IRQ(TPS6586X_INT_MASK4, 0xfc),
+	[TPS6586X_INT_CHG_TEMP]	= TPS6586X_IRQ(TPS6586X_INT_MASK3, 0x06),
+	[TPS6586X_INT_PP]	= TPS6586X_IRQ(TPS6586X_INT_MASK3, 0xf0),
+	[TPS6586X_INT_RESUME]	= TPS6586X_IRQ(TPS6586X_INT_MASK5, 1 << 5),
+	[TPS6586X_INT_LOW_SYS]	= TPS6586X_IRQ(TPS6586X_INT_MASK5, 1 << 6),
+	[TPS6586X_INT_RTC_ALM2] = TPS6586X_IRQ(TPS6586X_INT_MASK4, 1 << 1),
+};
+
+struct tps6586x {
+	struct mutex		lock;
+	struct device		*dev;
+	struct i2c_client	*client;
+
+	struct gpio_chip	gpio;
+	struct irq_chip		irq_chip;
+	struct mutex		irq_lock;
+	int			irq_base;
+	u32			irq_en;
+	u8			mask_cache[5];
+	u8			mask_reg[5];
+};
+
+static inline int __tps6586x_read(struct i2c_client *client,
+				  int reg, uint8_t *val)
+{
+	int ret;
+
+	ret = i2c_smbus_read_byte_data(client, reg);
+	if (ret < 0) {
+		dev_err(&client->dev, "failed reading at 0x%02x\n", reg);
+		return ret;
+	}
+
+	*val = (uint8_t)ret;
+
+	return 0;
+}
+
+static inline int __tps6586x_reads(struct i2c_client *client, int reg,
+				   int len, uint8_t *val)
+{
+	int ret;
+
+	ret = i2c_smbus_read_i2c_block_data(client, reg, len, val);
+	if (ret < 0) {
+		dev_err(&client->dev, "failed reading from 0x%02x\n", reg);
+		return ret;
+	}
+
+	return 0;
+}
+
+static inline int __tps6586x_write(struct i2c_client *client,
+				 int reg, uint8_t val)
+{
+	int ret;
+
+	ret = i2c_smbus_write_byte_data(client, reg, val);
+	if (ret < 0) {
+		dev_err(&client->dev, "failed writing 0x%02x to 0x%02x\n",
+				val, reg);
+		return ret;
+	}
+
+	return 0;
+}
+
+static inline int __tps6586x_writes(struct i2c_client *client, int reg,
+				  int len, uint8_t *val)
+{
+	int ret, i;
+
+	for (i = 0; i < len; i++) {
+		ret = __tps6586x_write(client, reg + i, *(val + i));
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+int tps6586x_write(struct device *dev, int reg, uint8_t val)
+{
+	return __tps6586x_write(to_i2c_client(dev), reg, val);
+}
+EXPORT_SYMBOL_GPL(tps6586x_write);
+
+int tps6586x_writes(struct device *dev, int reg, int len, uint8_t *val)
+{
+	return __tps6586x_writes(to_i2c_client(dev), reg, len, val);
+}
+EXPORT_SYMBOL_GPL(tps6586x_writes);
+
+int tps6586x_read(struct device *dev, int reg, uint8_t *val)
+{
+	return __tps6586x_read(to_i2c_client(dev), reg, val);
+}
+EXPORT_SYMBOL_GPL(tps6586x_read);
+
+int tps6586x_reads(struct device *dev, int reg, int len, uint8_t *val)
+{
+	return __tps6586x_reads(to_i2c_client(dev), reg, len, val);
+}
+EXPORT_SYMBOL_GPL(tps6586x_reads);
+
+int tps6586x_set_bits(struct device *dev, int reg, uint8_t bit_mask)
+{
+	struct tps6586x *tps6586x = dev_get_drvdata(dev);
+	uint8_t reg_val;
+	int ret = 0;
+
+	mutex_lock(&tps6586x->lock);
+
+	ret = __tps6586x_read(to_i2c_client(dev), reg, &reg_val);
+	if (ret)
+		goto out;
+
+	if ((reg_val & bit_mask) != bit_mask) {
+		reg_val |= bit_mask;
+		ret = __tps6586x_write(to_i2c_client(dev), reg, reg_val);
+	}
+out:
+	mutex_unlock(&tps6586x->lock);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(tps6586x_set_bits);
+
+int tps6586x_clr_bits(struct device *dev, int reg, uint8_t bit_mask)
+{
+	struct tps6586x *tps6586x = dev_get_drvdata(dev);
+	uint8_t reg_val;
+	int ret = 0;
+
+	mutex_lock(&tps6586x->lock);
+
+	ret = __tps6586x_read(to_i2c_client(dev), reg, &reg_val);
+	if (ret)
+		goto out;
+
+	if (reg_val & bit_mask) {
+		reg_val &= ~bit_mask;
+		ret = __tps6586x_write(to_i2c_client(dev), reg, reg_val);
+	}
+out:
+	mutex_unlock(&tps6586x->lock);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(tps6586x_clr_bits);
+
+int tps6586x_update(struct device *dev, int reg, uint8_t val, uint8_t mask)
+{
+	struct tps6586x *tps6586x = dev_get_drvdata(dev);
+	uint8_t reg_val;
+	int ret = 0;
+
+	mutex_lock(&tps6586x->lock);
+
+	ret = __tps6586x_read(tps6586x->client, reg, &reg_val);
+	if (ret)
+		goto out;
+
+	if ((reg_val & mask) != val) {
+		reg_val = (reg_val & ~mask) | val;
+		ret = __tps6586x_write(tps6586x->client, reg, reg_val);
+	}
+out:
+	mutex_unlock(&tps6586x->lock);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(tps6586x_update);
+
+static int tps6586x_gpio_get(struct gpio_chip *gc, unsigned offset)
+{
+	struct tps6586x *tps6586x = container_of(gc, struct tps6586x, gpio);
+	uint8_t val;
+	int ret;
+
+	ret = __tps6586x_read(tps6586x->client, TPS6586X_GPIOSET2, &val);
+	if (ret)
+		return ret;
+
+	return !!(val & (1 << offset));
+}
+
+
+static void tps6586x_gpio_set(struct gpio_chip *chip, unsigned offset,
+			      int value)
+{
+	struct tps6586x *tps6586x = container_of(chip, struct tps6586x, gpio);
+
+	tps6586x_update(tps6586x->dev, TPS6586X_GPIOSET2,
+			value << offset, 1 << offset);
+}
+
+static int tps6586x_gpio_output(struct gpio_chip *gc, unsigned offset,
+				int value)
+{
+	struct tps6586x *tps6586x = container_of(gc, struct tps6586x, gpio);
+	uint8_t val, mask;
+
+	tps6586x_gpio_set(gc, offset, value);
+
+	val = 0x1 << (offset * 2);
+	mask = 0x3 << (offset * 2);
+
+	return tps6586x_update(tps6586x->dev, TPS6586X_GPIOSET1, val, mask);
+}
+
+static int tps6586x_gpio_init(struct tps6586x *tps6586x, int gpio_base)
+{
+	if (!gpio_base)
+		return 0;
+
+	tps6586x->gpio.owner		= THIS_MODULE;
+	tps6586x->gpio.label		= tps6586x->client->name;
+	tps6586x->gpio.dev		= tps6586x->dev;
+	tps6586x->gpio.base		= gpio_base;
+	tps6586x->gpio.ngpio		= 4;
+	tps6586x->gpio.can_sleep	= 1;
+
+	/* FIXME: add handling of GPIOs as dedicated inputs */
+	tps6586x->gpio.direction_output	= tps6586x_gpio_output;
+	tps6586x->gpio.set		= tps6586x_gpio_set;
+	tps6586x->gpio.get		= tps6586x_gpio_get;
+
+	return gpiochip_add(&tps6586x->gpio);
+}
+
+static int __remove_subdev(struct device *dev, void *unused)
+{
+	platform_device_unregister(to_platform_device(dev));
+	return 0;
+}
+
+static int tps6586x_remove_subdevs(struct tps6586x *tps6586x)
+{
+	return device_for_each_child(tps6586x->dev, NULL, __remove_subdev);
+}
+
+static void tps6586x_irq_lock(struct irq_data *data)
+{
+	struct tps6586x *tps6586x = irq_data_get_irq_chip_data(data);
+
+	mutex_lock(&tps6586x->irq_lock);
+}
+
+static void tps6586x_irq_enable(struct irq_data *irq_data)
+{
+	struct tps6586x *tps6586x = irq_data_get_irq_chip_data(irq_data);
+	unsigned int __irq = irq_data->irq - tps6586x->irq_base;
+	const struct tps6586x_irq_data *data = &tps6586x_irqs[__irq];
+
+	tps6586x->mask_reg[data->mask_reg] &= ~data->mask_mask;
+	tps6586x->irq_en |= (1 << __irq);
+}
+
+static void tps6586x_irq_disable(struct irq_data *irq_data)
+{
+	struct tps6586x *tps6586x = irq_data_get_irq_chip_data(irq_data);
+
+	unsigned int __irq = irq_data->irq - tps6586x->irq_base;
+	const struct tps6586x_irq_data *data = &tps6586x_irqs[__irq];
+
+	tps6586x->mask_reg[data->mask_reg] |= data->mask_mask;
+	tps6586x->irq_en &= ~(1 << __irq);
+}
+
+static void tps6586x_irq_sync_unlock(struct irq_data *data)
+{
+	struct tps6586x *tps6586x = irq_data_get_irq_chip_data(data);
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(tps6586x->mask_reg); i++) {
+		if (tps6586x->mask_reg[i] != tps6586x->mask_cache[i]) {
+			if (!WARN_ON(tps6586x_write(tps6586x->dev,
+						    TPS6586X_INT_MASK1 + i,
+						    tps6586x->mask_reg[i])))
+				tps6586x->mask_cache[i] = tps6586x->mask_reg[i];
+		}
+	}
+
+	mutex_unlock(&tps6586x->irq_lock);
+}
+
+static irqreturn_t tps6586x_irq(int irq, void *data)
+{
+	struct tps6586x *tps6586x = data;
+	u32 acks;
+	int ret = 0;
+
+	ret = tps6586x_reads(tps6586x->dev, TPS6586X_INT_ACK1,
+			     sizeof(acks), (uint8_t *)&acks);
+
+	if (ret < 0) {
+		dev_err(tps6586x->dev, "failed to read interrupt status\n");
+		return IRQ_NONE;
+	}
+
+	acks = le32_to_cpu(acks);
+
+	while (acks) {
+		int i = __ffs(acks);
+
+		if (tps6586x->irq_en & (1 << i))
+			handle_nested_irq(tps6586x->irq_base + i);
+
+		acks &= ~(1 << i);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int __devinit tps6586x_irq_init(struct tps6586x *tps6586x, int irq,
+				       int irq_base)
+{
+	int i, ret;
+	u8 tmp[4];
+
+	if (!irq_base) {
+		dev_warn(tps6586x->dev, "No interrupt support on IRQ base\n");
+		return -EINVAL;
+	}
+
+	mutex_init(&tps6586x->irq_lock);
+	for (i = 0; i < 5; i++) {
+		tps6586x->mask_cache[i] = 0xff;
+		tps6586x->mask_reg[i] = 0xff;
+		tps6586x_write(tps6586x->dev, TPS6586X_INT_MASK1 + i, 0xff);
+	}
+
+	tps6586x_reads(tps6586x->dev, TPS6586X_INT_ACK1, sizeof(tmp), tmp);
+
+	tps6586x->irq_base = irq_base;
+
+	tps6586x->irq_chip.name = "tps6586x";
+	tps6586x->irq_chip.irq_enable = tps6586x_irq_enable;
+	tps6586x->irq_chip.irq_disable = tps6586x_irq_disable;
+	tps6586x->irq_chip.irq_bus_lock = tps6586x_irq_lock;
+	tps6586x->irq_chip.irq_bus_sync_unlock = tps6586x_irq_sync_unlock;
+
+	for (i = 0; i < ARRAY_SIZE(tps6586x_irqs); i++) {
+		int __irq = i + tps6586x->irq_base;
+		irq_set_chip_data(__irq, tps6586x);
+		irq_set_chip_and_handler(__irq, &tps6586x->irq_chip,
+					 handle_simple_irq);
+		irq_set_nested_thread(__irq, 1);
+#ifdef CONFIG_ARM
+		set_irq_flags(__irq, IRQF_VALID);
+#endif
+	}
+
+	ret = request_threaded_irq(irq, NULL, tps6586x_irq, IRQF_ONESHOT,
+				   "tps6586x", tps6586x);
+
+	if (!ret) {
+		device_init_wakeup(tps6586x->dev, 1);
+		enable_irq_wake(irq);
+	}
+
+	return ret;
+}
+
+static int __devinit tps6586x_add_subdevs(struct tps6586x *tps6586x,
+					  struct tps6586x_platform_data *pdata)
+{
+	struct tps6586x_subdev_info *subdev;
+	struct platform_device *pdev;
+	int i, ret = 0;
+
+	for (i = 0; i < pdata->num_subdevs; i++) {
+		subdev = &pdata->subdevs[i];
+
+		pdev = platform_device_alloc(subdev->name, subdev->id);
+		if (!pdev) {
+			ret = -ENOMEM;
+			goto failed;
+		}
+
+		pdev->dev.parent = tps6586x->dev;
+		pdev->dev.platform_data = subdev->platform_data;
+
+		ret = platform_device_add(pdev);
+		if (ret) {
+			platform_device_put(pdev);
+			goto failed;
+		}
+	}
+	return 0;
+
+failed:
+	tps6586x_remove_subdevs(tps6586x);
+	return ret;
+}
+
+static int __devinit tps6586x_i2c_probe(struct i2c_client *client,
+					const struct i2c_device_id *id)
+{
+	struct tps6586x_platform_data *pdata = client->dev.platform_data;
+	struct tps6586x *tps6586x;
+	int ret;
+
+	if (!pdata) {
+		dev_err(&client->dev, "tps6586x requires platform data\n");
+		return -ENOTSUPP;
+	}
+
+	ret = i2c_smbus_read_byte_data(client, TPS6586X_VERSIONCRC);
+	if (ret < 0) {
+		dev_err(&client->dev, "Chip ID read failed: %d\n", ret);
+		return -EIO;
+	}
+
+	dev_info(&client->dev, "VERSIONCRC is %02x\n", ret);
+
+	tps6586x = kzalloc(sizeof(struct tps6586x), GFP_KERNEL);
+	if (tps6586x == NULL)
+		return -ENOMEM;
+
+	tps6586x->client = client;
+	tps6586x->dev = &client->dev;
+	i2c_set_clientdata(client, tps6586x);
+
+	mutex_init(&tps6586x->lock);
+
+	if (client->irq) {
+		ret = tps6586x_irq_init(tps6586x, client->irq,
+					pdata->irq_base);
+		if (ret) {
+			dev_err(&client->dev, "IRQ init failed: %d\n", ret);
+			goto err_irq_init;
+		}
+	}
+
+	ret = tps6586x_gpio_init(tps6586x, pdata->gpio_base);
+	if (ret) {
+		dev_err(&client->dev, "GPIO registration failed: %d\n", ret);
+		goto err_gpio_init;
+	}
+
+	ret = tps6586x_add_subdevs(tps6586x, pdata);
+	if (ret) {
+		dev_err(&client->dev, "add devices failed: %d\n", ret);
+		goto err_add_devs;
+	}
+
+	return 0;
+
+err_add_devs:
+	if (pdata->gpio_base) {
+		ret = gpiochip_remove(&tps6586x->gpio);
+		if (ret)
+			dev_err(&client->dev, "Can't remove gpio chip: %d\n",
+				ret);
+	}
+err_gpio_init:
+	if (client->irq)
+		free_irq(client->irq, tps6586x);
+err_irq_init:
+	kfree(tps6586x);
+	return ret;
+}
+
+static int __devexit tps6586x_i2c_remove(struct i2c_client *client)
+{
+	struct tps6586x *tps6586x = i2c_get_clientdata(client);
+	struct tps6586x_platform_data *pdata = client->dev.platform_data;
+	int ret;
+
+	if (client->irq)
+		free_irq(client->irq, tps6586x);
+
+	if (pdata->gpio_base) {
+		ret = gpiochip_remove(&tps6586x->gpio);
+		if (ret)
+			dev_err(&client->dev, "Can't remove gpio chip: %d\n",
+				ret);
+	}
+
+	tps6586x_remove_subdevs(tps6586x);
+	kfree(tps6586x);
+	return 0;
+}
+
+static const struct i2c_device_id tps6586x_id_table[] = {
+	{ "tps6586x", 0 },
+	{ },
+};
+MODULE_DEVICE_TABLE(i2c, tps6586x_id_table);
+
+static struct i2c_driver tps6586x_driver = {
+	.driver	= {
+		.name	= "tps6586x",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= tps6586x_i2c_probe,
+	.remove		= __devexit_p(tps6586x_i2c_remove),
+	.id_table	= tps6586x_id_table,
+};
+
+static int __init tps6586x_init(void)
+{
+	return i2c_add_driver(&tps6586x_driver);
+}
+subsys_initcall(tps6586x_init);
+
+static void __exit tps6586x_exit(void)
+{
+	i2c_del_driver(&tps6586x_driver);
+}
+module_exit(tps6586x_exit);
+
+MODULE_DESCRIPTION("TPS6586X core driver");
+MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/tps65910-irq.c b/ap/os/linux/linux-3.4.x/drivers/mfd/tps65910-irq.c
new file mode 100644
index 0000000..c9ed5c0
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/tps65910-irq.c
@@ -0,0 +1,232 @@
+/*
+ * tps65910-irq.c  --  TI TPS6591x
+ *
+ * Copyright 2010 Texas Instruments Inc.
+ *
+ * Author: Graeme Gregory <gg@slimlogic.co.uk>
+ * Author: Jorge Eduardo Candelaria <jedu@slimlogic.co.uk>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under  the terms of the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/bug.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/mfd/tps65910.h>
+
+static inline int irq_to_tps65910_irq(struct tps65910 *tps65910,
+							int irq)
+{
+	return (irq - tps65910->irq_base);
+}
+
+/*
+ * This is a threaded IRQ handler so can access I2C/SPI.  Since all
+ * interrupts are clear on read the IRQ line will be reasserted and
+ * the physical IRQ will be handled again if another interrupt is
+ * asserted while we run - in the normal course of events this is a
+ * rare occurrence so we save I2C/SPI reads.  We're also assuming that
+ * it's rare to get lots of interrupts firing simultaneously so try to
+ * minimise I/O.
+ */
+static irqreturn_t tps65910_irq(int irq, void *irq_data)
+{
+	struct tps65910 *tps65910 = irq_data;
+	u32 irq_sts;
+	u32 irq_mask;
+	u8 reg;
+	int i;
+
+	tps65910->read(tps65910, TPS65910_INT_STS, 1, &reg);
+	irq_sts = reg;
+	tps65910->read(tps65910, TPS65910_INT_STS2, 1, &reg);
+	irq_sts |= reg << 8;
+	switch (tps65910_chip_id(tps65910)) {
+	case TPS65911:
+		tps65910->read(tps65910, TPS65910_INT_STS3, 1, &reg);
+		irq_sts |= reg << 16;
+	}
+
+	tps65910->read(tps65910, TPS65910_INT_MSK, 1, &reg);
+	irq_mask = reg;
+	tps65910->read(tps65910, TPS65910_INT_MSK2, 1, &reg);
+	irq_mask |= reg << 8;
+	switch (tps65910_chip_id(tps65910)) {
+	case TPS65911:
+		tps65910->read(tps65910, TPS65910_INT_MSK3, 1, &reg);
+		irq_mask |= reg << 16;
+	}
+
+	irq_sts &= ~irq_mask;
+
+	if (!irq_sts)
+		return IRQ_NONE;
+
+	for (i = 0; i < tps65910->irq_num; i++) {
+
+		if (!(irq_sts & (1 << i)))
+			continue;
+
+		handle_nested_irq(tps65910->irq_base + i);
+	}
+
+	/* Write the STS register back to clear IRQs we handled */
+	reg = irq_sts & 0xFF;
+	irq_sts >>= 8;
+	tps65910->write(tps65910, TPS65910_INT_STS, 1, &reg);
+	reg = irq_sts & 0xFF;
+	tps65910->write(tps65910, TPS65910_INT_STS2, 1, &reg);
+	switch (tps65910_chip_id(tps65910)) {
+	case TPS65911:
+		reg = irq_sts >> 8;
+		tps65910->write(tps65910, TPS65910_INT_STS3, 1, &reg);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void tps65910_irq_lock(struct irq_data *data)
+{
+	struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data);
+
+	mutex_lock(&tps65910->irq_lock);
+}
+
+static void tps65910_irq_sync_unlock(struct irq_data *data)
+{
+	struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data);
+	u32 reg_mask;
+	u8 reg;
+
+	tps65910->read(tps65910, TPS65910_INT_MSK, 1, &reg);
+	reg_mask = reg;
+	tps65910->read(tps65910, TPS65910_INT_MSK2, 1, &reg);
+	reg_mask |= reg << 8;
+	switch (tps65910_chip_id(tps65910)) {
+	case TPS65911:
+		tps65910->read(tps65910, TPS65910_INT_MSK3, 1, &reg);
+		reg_mask |= reg << 16;
+	}
+
+	if (tps65910->irq_mask != reg_mask) {
+		reg = tps65910->irq_mask & 0xFF;
+		tps65910->write(tps65910, TPS65910_INT_MSK, 1, &reg);
+		reg = tps65910->irq_mask >> 8 & 0xFF;
+		tps65910->write(tps65910, TPS65910_INT_MSK2, 1, &reg);
+		switch (tps65910_chip_id(tps65910)) {
+		case TPS65911:
+			reg = tps65910->irq_mask >> 16;
+			tps65910->write(tps65910, TPS65910_INT_MSK3, 1, &reg);
+		}
+	}
+	mutex_unlock(&tps65910->irq_lock);
+}
+
+static void tps65910_irq_enable(struct irq_data *data)
+{
+	struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data);
+
+	tps65910->irq_mask &= ~( 1 << irq_to_tps65910_irq(tps65910, data->irq));
+}
+
+static void tps65910_irq_disable(struct irq_data *data)
+{
+	struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data);
+
+	tps65910->irq_mask |= ( 1 << irq_to_tps65910_irq(tps65910, data->irq));
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int tps65910_irq_set_wake(struct irq_data *data, unsigned int enable)
+{
+	struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data);
+	return irq_set_irq_wake(tps65910->chip_irq, enable);
+}
+#else
+#define tps65910_irq_set_wake NULL
+#endif
+
+static struct irq_chip tps65910_irq_chip = {
+	.name = "tps65910",
+	.irq_bus_lock = tps65910_irq_lock,
+	.irq_bus_sync_unlock = tps65910_irq_sync_unlock,
+	.irq_disable = tps65910_irq_disable,
+	.irq_enable = tps65910_irq_enable,
+	.irq_set_wake = tps65910_irq_set_wake,
+};
+
+int tps65910_irq_init(struct tps65910 *tps65910, int irq,
+		    struct tps65910_platform_data *pdata)
+{
+	int ret, cur_irq;
+	int flags = IRQF_ONESHOT;
+
+	if (!irq) {
+		dev_warn(tps65910->dev, "No interrupt support, no core IRQ\n");
+		return -EINVAL;
+	}
+
+	if (!pdata || !pdata->irq_base) {
+		dev_warn(tps65910->dev, "No interrupt support, no IRQ base\n");
+		return -EINVAL;
+	}
+
+	tps65910->irq_mask = 0xFFFFFF;
+
+	mutex_init(&tps65910->irq_lock);
+	tps65910->chip_irq = irq;
+	tps65910->irq_base = pdata->irq_base;
+
+	switch (tps65910_chip_id(tps65910)) {
+	case TPS65910:
+		tps65910->irq_num = TPS65910_NUM_IRQ;
+		break;
+	case TPS65911:
+		tps65910->irq_num = TPS65911_NUM_IRQ;
+		break;
+	}
+
+	/* Register with genirq */
+	for (cur_irq = tps65910->irq_base;
+	     cur_irq < tps65910->irq_num + tps65910->irq_base;
+	     cur_irq++) {
+		irq_set_chip_data(cur_irq, tps65910);
+		irq_set_chip_and_handler(cur_irq, &tps65910_irq_chip,
+					 handle_edge_irq);
+		irq_set_nested_thread(cur_irq, 1);
+
+		/* ARM needs us to explicitly flag the IRQ as valid
+		 * and will set them noprobe when we do so. */
+#ifdef CONFIG_ARM
+		set_irq_flags(cur_irq, IRQF_VALID);
+#else
+		irq_set_noprobe(cur_irq);
+#endif
+	}
+
+	ret = request_threaded_irq(irq, NULL, tps65910_irq, flags,
+				   "tps65910", tps65910);
+
+	irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW);
+
+	if (ret != 0)
+		dev_err(tps65910->dev, "Failed to request IRQ: %d\n", ret);
+
+	return ret;
+}
+
+int tps65910_irq_exit(struct tps65910 *tps65910)
+{
+	if (tps65910->chip_irq)
+		free_irq(tps65910->chip_irq, tps65910);
+	return 0;
+}
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/tps65910.c b/ap/os/linux/linux-3.4.x/drivers/mfd/tps65910.c
new file mode 100644
index 0000000..bf2b25e
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/tps65910.c
@@ -0,0 +1,200 @@
+/*
+ * tps65910.c  --  TI TPS6591x
+ *
+ * Copyright 2010 Texas Instruments Inc.
+ *
+ * Author: Graeme Gregory <gg@slimlogic.co.uk>
+ * Author: Jorge Eduardo Candelaria <jedu@slimlogic.co.uk>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under  the terms of the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/mfd/core.h>
+#include <linux/regmap.h>
+#include <linux/mfd/tps65910.h>
+
+static struct mfd_cell tps65910s[] = {
+	{
+		.name = "tps65910-pmic",
+	},
+	{
+		.name = "tps65910-rtc",
+	},
+	{
+		.name = "tps65910-power",
+	},
+};
+
+
+static int tps65910_i2c_read(struct tps65910 *tps65910, u8 reg,
+				  int bytes, void *dest)
+{
+	return regmap_bulk_read(tps65910->regmap, reg, dest, bytes);
+}
+
+static int tps65910_i2c_write(struct tps65910 *tps65910, u8 reg,
+				  int bytes, void *src)
+{
+	return regmap_bulk_write(tps65910->regmap, reg, src, bytes);
+}
+
+int tps65910_set_bits(struct tps65910 *tps65910, u8 reg, u8 mask)
+{
+	return regmap_update_bits(tps65910->regmap, reg, mask, mask);
+}
+EXPORT_SYMBOL_GPL(tps65910_set_bits);
+
+int tps65910_clear_bits(struct tps65910 *tps65910, u8 reg, u8 mask)
+{
+	return regmap_update_bits(tps65910->regmap, reg, mask, 0);
+}
+EXPORT_SYMBOL_GPL(tps65910_clear_bits);
+
+static bool is_volatile_reg(struct device *dev, unsigned int reg)
+{
+	struct tps65910 *tps65910 = dev_get_drvdata(dev);
+
+	/*
+	 * Caching all regulator registers.
+	 * All regualator register address range is same for
+	 * TPS65910 and TPS65911
+	 */
+	if ((reg >= TPS65910_VIO) && (reg <= TPS65910_VDAC)) {
+		/* Check for non-existing register */
+		if (tps65910_chip_id(tps65910) == TPS65910)
+			if ((reg == TPS65911_VDDCTRL_OP) ||
+				(reg == TPS65911_VDDCTRL_SR))
+				return true;
+		return false;
+	}
+	return true;
+}
+
+static const struct regmap_config tps65910_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.volatile_reg = is_volatile_reg,
+	.max_register = TPS65910_MAX_REGISTER,
+	.num_reg_defaults_raw = TPS65910_MAX_REGISTER,
+	.cache_type = REGCACHE_RBTREE,
+};
+
+static int tps65910_i2c_probe(struct i2c_client *i2c,
+			    const struct i2c_device_id *id)
+{
+	struct tps65910 *tps65910;
+	struct tps65910_board *pmic_plat_data;
+	struct tps65910_platform_data *init_data;
+	int ret = 0;
+
+	pmic_plat_data = dev_get_platdata(&i2c->dev);
+	if (!pmic_plat_data)
+		return -EINVAL;
+
+	init_data = kzalloc(sizeof(struct tps65910_platform_data), GFP_KERNEL);
+	if (init_data == NULL)
+		return -ENOMEM;
+
+	tps65910 = kzalloc(sizeof(struct tps65910), GFP_KERNEL);
+	if (tps65910 == NULL) {
+		kfree(init_data);
+		return -ENOMEM;
+	}
+
+	i2c_set_clientdata(i2c, tps65910);
+	tps65910->dev = &i2c->dev;
+	tps65910->i2c_client = i2c;
+	tps65910->id = id->driver_data;
+	tps65910->read = tps65910_i2c_read;
+	tps65910->write = tps65910_i2c_write;
+	mutex_init(&tps65910->io_mutex);
+
+	tps65910->regmap = regmap_init_i2c(i2c, &tps65910_regmap_config);
+	if (IS_ERR(tps65910->regmap)) {
+		ret = PTR_ERR(tps65910->regmap);
+		dev_err(&i2c->dev, "regmap initialization failed: %d\n", ret);
+		goto regmap_err;
+	}
+
+	ret = mfd_add_devices(tps65910->dev, -1,
+			      tps65910s, ARRAY_SIZE(tps65910s),
+			      NULL, 0);
+	if (ret < 0)
+		goto err;
+
+	init_data->irq = pmic_plat_data->irq;
+	init_data->irq_base = pmic_plat_data->irq_base;
+
+	tps65910_gpio_init(tps65910, pmic_plat_data->gpio_base);
+
+	tps65910_irq_init(tps65910, init_data->irq, init_data);
+
+	kfree(init_data);
+	return ret;
+
+err:
+	regmap_exit(tps65910->regmap);
+regmap_err:
+	kfree(tps65910);
+	kfree(init_data);
+	return ret;
+}
+
+static int tps65910_i2c_remove(struct i2c_client *i2c)
+{
+	struct tps65910 *tps65910 = i2c_get_clientdata(i2c);
+
+	tps65910_irq_exit(tps65910);
+	mfd_remove_devices(tps65910->dev);
+	regmap_exit(tps65910->regmap);
+	kfree(tps65910);
+
+	return 0;
+}
+
+static const struct i2c_device_id tps65910_i2c_id[] = {
+       { "tps65910", TPS65910 },
+       { "tps65911", TPS65911 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, tps65910_i2c_id);
+
+
+static struct i2c_driver tps65910_i2c_driver = {
+	.driver = {
+		   .name = "tps65910",
+		   .owner = THIS_MODULE,
+	},
+	.probe = tps65910_i2c_probe,
+	.remove = tps65910_i2c_remove,
+	.id_table = tps65910_i2c_id,
+};
+
+static int __init tps65910_i2c_init(void)
+{
+	return i2c_add_driver(&tps65910_i2c_driver);
+}
+/* init early so consumer devices can complete system boot */
+subsys_initcall(tps65910_i2c_init);
+
+static void __exit tps65910_i2c_exit(void)
+{
+	i2c_del_driver(&tps65910_i2c_driver);
+}
+module_exit(tps65910_i2c_exit);
+
+MODULE_AUTHOR("Graeme Gregory <gg@slimlogic.co.uk>");
+MODULE_AUTHOR("Jorge Eduardo Candelaria <jedu@slimlogic.co.uk>");
+MODULE_DESCRIPTION("TPS6591x chip family multi-function driver");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/tps65911-comparator.c b/ap/os/linux/linux-3.4.x/drivers/mfd/tps65911-comparator.c
new file mode 100644
index 0000000..e7ff783
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/tps65911-comparator.c
@@ -0,0 +1,190 @@
+/*
+ * tps65910.c  --  TI TPS6591x
+ *
+ * Copyright 2010 Texas Instruments Inc.
+ *
+ * Author: Jorge Eduardo Candelaria <jedu@slimlogic.co.uk>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under  the terms of the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/debugfs.h>
+#include <linux/gpio.h>
+#include <linux/mfd/tps65910.h>
+
+#define COMP					0
+#define COMP1					1
+#define COMP2					2
+
+/* Comparator 1 voltage selection table in milivolts */
+static const u16 COMP_VSEL_TABLE[] = {
+	0, 2500, 2500, 2500, 2500, 2550, 2600, 2650,
+	2700, 2750, 2800, 2850, 2900, 2950, 3000, 3050,
+	3100, 3150, 3200, 3250, 3300, 3350, 3400, 3450,
+	3500,
+};
+
+struct comparator {
+	const char *name;
+	int reg;
+	int uV_max;
+	const u16 *vsel_table;
+};
+
+static struct comparator tps_comparators[] = {
+	{
+		.name = "COMP1",
+		.reg = TPS65911_VMBCH,
+		.uV_max = 3500,
+		.vsel_table = COMP_VSEL_TABLE,
+	},
+	{
+		.name = "COMP2",
+		.reg = TPS65911_VMBCH2,
+		.uV_max = 3500,
+		.vsel_table = COMP_VSEL_TABLE,
+	},
+};
+
+static int comp_threshold_set(struct tps65910 *tps65910, int id, int voltage)
+{
+	struct comparator tps_comp = tps_comparators[id];
+	int curr_voltage = 0;
+	int ret;
+	u8 index = 0, val;
+
+	if (id == COMP)
+		return 0;
+
+	while (curr_voltage < tps_comp.uV_max) {
+		curr_voltage = tps_comp.vsel_table[index];
+		if (curr_voltage >= voltage)
+			break;
+		else if (curr_voltage < voltage)
+			index ++;
+	}
+
+	if (curr_voltage > tps_comp.uV_max)
+		return -EINVAL;
+
+	val = index << 1;
+	ret = tps65910->write(tps65910, tps_comp.reg, 1, &val);
+
+	return ret;
+}
+
+static int comp_threshold_get(struct tps65910 *tps65910, int id)
+{
+	struct comparator tps_comp = tps_comparators[id];
+	int ret;
+	u8 val;
+
+	if (id == COMP)
+		return 0;
+
+	ret = tps65910->read(tps65910, tps_comp.reg, 1, &val);
+	if (ret < 0)
+		return ret;
+
+	val >>= 1;
+	return tps_comp.vsel_table[val];
+}
+
+static ssize_t comp_threshold_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct tps65910 *tps65910 = dev_get_drvdata(dev->parent);
+	struct attribute comp_attr = attr->attr;
+	int id, uVolt;
+
+	if (!strcmp(comp_attr.name, "comp1_threshold"))
+		id = COMP1;
+	else if (!strcmp(comp_attr.name, "comp2_threshold"))
+		id = COMP2;
+	else
+		return -EINVAL;
+
+	uVolt = comp_threshold_get(tps65910, id);
+
+	return sprintf(buf, "%d\n", uVolt);
+}
+
+static DEVICE_ATTR(comp1_threshold, S_IRUGO, comp_threshold_show, NULL);
+static DEVICE_ATTR(comp2_threshold, S_IRUGO, comp_threshold_show, NULL);
+
+static __devinit int tps65911_comparator_probe(struct platform_device *pdev)
+{
+	struct tps65910 *tps65910 = dev_get_drvdata(pdev->dev.parent);
+	struct tps65910_board *pdata = dev_get_platdata(tps65910->dev);
+	int ret;
+
+	ret = comp_threshold_set(tps65910, COMP1,  pdata->vmbch_threshold);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "cannot set COMP1 threshold\n");
+		return ret;
+	}
+
+	ret = comp_threshold_set(tps65910, COMP2, pdata->vmbch2_threshold);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "cannot set COMP2 theshold\n");
+		return ret;
+	}
+
+	/* Create sysfs entry */
+	ret = device_create_file(&pdev->dev, &dev_attr_comp1_threshold);
+	if (ret < 0)
+		dev_err(&pdev->dev, "failed to add COMP1 sysfs file\n");
+
+	ret = device_create_file(&pdev->dev, &dev_attr_comp2_threshold);
+	if (ret < 0)
+		dev_err(&pdev->dev, "failed to add COMP2 sysfs file\n");
+
+	return ret;
+}
+
+static __devexit int tps65911_comparator_remove(struct platform_device *pdev)
+{
+	struct tps65910 *tps65910;
+
+	tps65910 = dev_get_drvdata(pdev->dev.parent);
+	device_remove_file(&pdev->dev, &dev_attr_comp2_threshold);
+	device_remove_file(&pdev->dev, &dev_attr_comp1_threshold);
+
+	return 0;
+}
+
+static struct platform_driver tps65911_comparator_driver = {
+	.driver = {
+		.name = "tps65911-comparator",
+		.owner = THIS_MODULE,
+	},
+	.probe = tps65911_comparator_probe,
+	.remove = __devexit_p(tps65911_comparator_remove),
+};
+
+static int __init tps65911_comparator_init(void)
+{
+	return platform_driver_register(&tps65911_comparator_driver);
+}
+subsys_initcall(tps65911_comparator_init);
+
+static void __exit tps65911_comparator_exit(void)
+{
+	platform_driver_unregister(&tps65911_comparator_driver);
+}
+module_exit(tps65911_comparator_exit);
+
+MODULE_AUTHOR("Jorge Eduardo Candelaria <jedu@slimlogic.co.uk>");
+MODULE_DESCRIPTION("TPS65911 comparator driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:tps65911-comparator");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/tps65912-core.c b/ap/os/linux/linux-3.4.x/drivers/mfd/tps65912-core.c
new file mode 100644
index 0000000..74fd8cb
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/tps65912-core.c
@@ -0,0 +1,177 @@
+/*
+ * tps65912-core.c  --  TI TPS65912x
+ *
+ * Copyright 2011 Texas Instruments Inc.
+ *
+ * Author: Margarita Olaya Cabrera <magi@slimlogic.co.uk>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under  the terms of the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the License, or (at your
+ *  option) any later version.
+ *
+ *  This driver is based on wm8350 implementation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/tps65912.h>
+
+static struct mfd_cell tps65912s[] = {
+	{
+		.name = "tps65912-pmic",
+	},
+};
+
+int tps65912_set_bits(struct tps65912 *tps65912, u8 reg, u8 mask)
+{
+	u8 data;
+	int err;
+
+	mutex_lock(&tps65912->io_mutex);
+
+	err = tps65912->read(tps65912, reg, 1, &data);
+	if (err) {
+		dev_err(tps65912->dev, "Read from reg 0x%x failed\n", reg);
+		goto out;
+	}
+
+	data |= mask;
+	err = tps65912->write(tps65912, reg, 1, &data);
+	if (err)
+		dev_err(tps65912->dev, "Write to reg 0x%x failed\n", reg);
+
+out:
+	mutex_unlock(&tps65912->io_mutex);
+	return err;
+}
+EXPORT_SYMBOL_GPL(tps65912_set_bits);
+
+int tps65912_clear_bits(struct tps65912 *tps65912, u8 reg, u8 mask)
+{
+	u8 data;
+	int err;
+
+	mutex_lock(&tps65912->io_mutex);
+	err = tps65912->read(tps65912, reg, 1, &data);
+	if (err) {
+		dev_err(tps65912->dev, "Read from reg 0x%x failed\n", reg);
+		goto out;
+	}
+
+	data &= ~mask;
+	err = tps65912->write(tps65912, reg, 1, &data);
+	if (err)
+		dev_err(tps65912->dev, "Write to reg 0x%x failed\n", reg);
+
+out:
+	mutex_unlock(&tps65912->io_mutex);
+	return err;
+}
+EXPORT_SYMBOL_GPL(tps65912_clear_bits);
+
+static inline int tps65912_read(struct tps65912 *tps65912, u8 reg)
+{
+	u8 val;
+	int err;
+
+	err = tps65912->read(tps65912, reg, 1, &val);
+	if (err < 0)
+		return err;
+
+	return val;
+}
+
+static inline int tps65912_write(struct tps65912 *tps65912, u8 reg, u8 val)
+{
+	return tps65912->write(tps65912, reg, 1, &val);
+}
+
+int tps65912_reg_read(struct tps65912 *tps65912, u8 reg)
+{
+	int data;
+
+	mutex_lock(&tps65912->io_mutex);
+
+	data = tps65912_read(tps65912, reg);
+	if (data < 0)
+		dev_err(tps65912->dev, "Read from reg 0x%x failed\n", reg);
+
+	mutex_unlock(&tps65912->io_mutex);
+	return data;
+}
+EXPORT_SYMBOL_GPL(tps65912_reg_read);
+
+int tps65912_reg_write(struct tps65912 *tps65912, u8 reg, u8 val)
+{
+	int err;
+
+	mutex_lock(&tps65912->io_mutex);
+
+	err = tps65912_write(tps65912, reg, val);
+	if (err < 0)
+		dev_err(tps65912->dev, "Write for reg 0x%x failed\n", reg);
+
+	mutex_unlock(&tps65912->io_mutex);
+	return err;
+}
+EXPORT_SYMBOL_GPL(tps65912_reg_write);
+
+int tps65912_device_init(struct tps65912 *tps65912)
+{
+	struct tps65912_board *pmic_plat_data = tps65912->dev->platform_data;
+	struct tps65912_platform_data *init_data;
+	int ret, dcdc_avs, value;
+
+	init_data = kzalloc(sizeof(struct tps65912_platform_data), GFP_KERNEL);
+	if (init_data == NULL)
+		return -ENOMEM;
+
+	mutex_init(&tps65912->io_mutex);
+	dev_set_drvdata(tps65912->dev, tps65912);
+
+	dcdc_avs = (pmic_plat_data->is_dcdc1_avs << 0 |
+			pmic_plat_data->is_dcdc2_avs  << 1 |
+				pmic_plat_data->is_dcdc3_avs << 2 |
+					pmic_plat_data->is_dcdc4_avs << 3);
+	if (dcdc_avs) {
+		tps65912->read(tps65912, TPS65912_I2C_SPI_CFG, 1, &value);
+		dcdc_avs |= value;
+		tps65912->write(tps65912, TPS65912_I2C_SPI_CFG, 1, &dcdc_avs);
+	}
+
+	ret = mfd_add_devices(tps65912->dev, -1,
+			      tps65912s, ARRAY_SIZE(tps65912s),
+			      NULL, 0);
+	if (ret < 0)
+		goto err;
+
+	init_data->irq = pmic_plat_data->irq;
+	init_data->irq_base = pmic_plat_data->irq_base;
+	ret = tps65912_irq_init(tps65912, init_data->irq, init_data);
+	if (ret < 0)
+		goto err;
+
+	kfree(init_data);
+	return ret;
+
+err:
+	kfree(init_data);
+	mfd_remove_devices(tps65912->dev);
+	kfree(tps65912);
+	return ret;
+}
+
+void tps65912_device_exit(struct tps65912 *tps65912)
+{
+	mfd_remove_devices(tps65912->dev);
+	kfree(tps65912);
+}
+
+MODULE_AUTHOR("Margarita Olaya	<magi@slimlogic.co.uk>");
+MODULE_DESCRIPTION("TPS65912x chip family multi-function driver");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/tps65912-i2c.c b/ap/os/linux/linux-3.4.x/drivers/mfd/tps65912-i2c.c
new file mode 100644
index 0000000..c041f2c
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/tps65912-i2c.c
@@ -0,0 +1,139 @@
+/*
+ * tps65912-i2c.c  --  I2C access for TI TPS65912x PMIC
+ *
+ * Copyright 2011 Texas Instruments Inc.
+ *
+ * Author: Margarita Olaya Cabrera <magi@slimlogic.co.uk>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under  the terms of the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the License, or (at your
+ *  option) any later version.
+ *
+ *  This driver is based on wm8350 implementation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/tps65912.h>
+
+static int tps65912_i2c_read(struct tps65912 *tps65912, u8 reg,
+				  int bytes, void *dest)
+{
+	struct i2c_client *i2c = tps65912->control_data;
+	struct i2c_msg xfer[2];
+	int ret;
+
+	/* Write register */
+	xfer[0].addr = i2c->addr;
+	xfer[0].flags = 0;
+	xfer[0].len = 1;
+	xfer[0].buf = &reg;
+
+	/* Read data */
+	xfer[1].addr = i2c->addr;
+	xfer[1].flags = I2C_M_RD;
+	xfer[1].len = bytes;
+	xfer[1].buf = dest;
+
+	ret = i2c_transfer(i2c->adapter, xfer, 2);
+	if (ret == 2)
+		ret = 0;
+	else if (ret >= 0)
+		ret = -EIO;
+	return ret;
+}
+
+static int tps65912_i2c_write(struct tps65912 *tps65912, u8 reg,
+				   int bytes, void *src)
+{
+	struct i2c_client *i2c = tps65912->control_data;
+	/* we add 1 byte for device register */
+	u8 msg[TPS6591X_MAX_REGISTER + 1];
+	int ret;
+
+	if (bytes > TPS6591X_MAX_REGISTER)
+		return -EINVAL;
+
+	msg[0] = reg;
+	memcpy(&msg[1], src, bytes);
+
+	ret = i2c_master_send(i2c, msg, bytes + 1);
+	if (ret < 0)
+		return ret;
+	if (ret != bytes + 1)
+		return -EIO;
+
+	return 0;
+}
+
+static int tps65912_i2c_probe(struct i2c_client *i2c,
+			    const struct i2c_device_id *id)
+{
+	struct tps65912 *tps65912;
+
+	tps65912 = kzalloc(sizeof(struct tps65912), GFP_KERNEL);
+	if (tps65912 == NULL)
+		return -ENOMEM;
+
+	i2c_set_clientdata(i2c, tps65912);
+	tps65912->dev = &i2c->dev;
+	tps65912->control_data = i2c;
+	tps65912->read = tps65912_i2c_read;
+	tps65912->write = tps65912_i2c_write;
+
+	return tps65912_device_init(tps65912);
+}
+
+static int tps65912_i2c_remove(struct i2c_client *i2c)
+{
+	struct tps65912 *tps65912 = i2c_get_clientdata(i2c);
+
+	tps65912_device_exit(tps65912);
+
+	return 0;
+}
+
+static const struct i2c_device_id tps65912_i2c_id[] = {
+	{"tps65912", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, tps65912_i2c_id);
+
+static struct i2c_driver tps65912_i2c_driver = {
+	.driver = {
+		   .name = "tps65912",
+		   .owner = THIS_MODULE,
+	},
+	.probe = tps65912_i2c_probe,
+	.remove = tps65912_i2c_remove,
+	.id_table = tps65912_i2c_id,
+};
+
+static int __init tps65912_i2c_init(void)
+{
+	int ret;
+
+	ret = i2c_add_driver(&tps65912_i2c_driver);
+	if (ret != 0)
+		pr_err("Failed to register TPS65912 I2C driver: %d\n", ret);
+
+	return ret;
+}
+/* init early so consumer devices can complete system boot */
+subsys_initcall(tps65912_i2c_init);
+
+static void __exit tps65912_i2c_exit(void)
+{
+	i2c_del_driver(&tps65912_i2c_driver);
+}
+module_exit(tps65912_i2c_exit);
+
+MODULE_AUTHOR("Margarita Olaya	<magi@slimlogic.co.uk>");
+MODULE_DESCRIPTION("TPS6591x chip family multi-function driver");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/tps65912-irq.c b/ap/os/linux/linux-3.4.x/drivers/mfd/tps65912-irq.c
new file mode 100644
index 0000000..d360a83
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/tps65912-irq.c
@@ -0,0 +1,224 @@
+/*
+ * tps65912-irq.c  --  TI TPS6591x
+ *
+ * Copyright 2011 Texas Instruments Inc.
+ *
+ * Author: Margarita Olaya <magi@slimlogic.co.uk>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under  the terms of the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the License, or (at your
+ *  option) any later version.
+ *
+ * This driver is based on wm8350 implementation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/bug.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/mfd/tps65912.h>
+
+static inline int irq_to_tps65912_irq(struct tps65912 *tps65912,
+							int irq)
+{
+	return irq - tps65912->irq_base;
+}
+
+/*
+ * This is a threaded IRQ handler so can access I2C/SPI.  Since the
+ * IRQ handler explicitly clears the IRQ it handles the IRQ line
+ * will be reasserted and the physical IRQ will be handled again if
+ * another interrupt is asserted while we run - in the normal course
+ * of events this is a rare occurrence so we save I2C/SPI reads. We're
+ * also assuming that it's rare to get lots of interrupts firing
+ * simultaneously so try to minimise I/O.
+ */
+static irqreturn_t tps65912_irq(int irq, void *irq_data)
+{
+	struct tps65912 *tps65912 = irq_data;
+	u32 irq_sts;
+	u32 irq_mask;
+	u8 reg;
+	int i;
+
+
+	tps65912->read(tps65912, TPS65912_INT_STS, 1, &reg);
+	irq_sts = reg;
+	tps65912->read(tps65912, TPS65912_INT_STS2, 1, &reg);
+	irq_sts |= reg << 8;
+	tps65912->read(tps65912, TPS65912_INT_STS3, 1, &reg);
+	irq_sts |= reg << 16;
+	tps65912->read(tps65912, TPS65912_INT_STS4, 1, &reg);
+	irq_sts |= reg << 24;
+
+	tps65912->read(tps65912, TPS65912_INT_MSK, 1, &reg);
+	irq_mask = reg;
+	tps65912->read(tps65912, TPS65912_INT_MSK2, 1, &reg);
+	irq_mask |= reg << 8;
+	tps65912->read(tps65912, TPS65912_INT_MSK3, 1, &reg);
+	irq_mask |= reg << 16;
+	tps65912->read(tps65912, TPS65912_INT_MSK4, 1, &reg);
+	irq_mask |= reg << 24;
+
+	irq_sts &= ~irq_mask;
+	if (!irq_sts)
+		return IRQ_NONE;
+
+	for (i = 0; i < tps65912->irq_num; i++) {
+		if (!(irq_sts & (1 << i)))
+			continue;
+
+		handle_nested_irq(tps65912->irq_base + i);
+	}
+
+	/* Write the STS register back to clear IRQs we handled */
+	reg = irq_sts & 0xFF;
+	irq_sts >>= 8;
+	if (reg)
+		tps65912->write(tps65912, TPS65912_INT_STS, 1, &reg);
+	reg = irq_sts & 0xFF;
+	irq_sts >>= 8;
+	if (reg)
+		tps65912->write(tps65912, TPS65912_INT_STS2, 1, &reg);
+	reg = irq_sts & 0xFF;
+	irq_sts >>= 8;
+	if (reg)
+		tps65912->write(tps65912, TPS65912_INT_STS3, 1, &reg);
+	reg = irq_sts & 0xFF;
+	if (reg)
+		tps65912->write(tps65912, TPS65912_INT_STS4, 1, &reg);
+
+	return IRQ_HANDLED;
+}
+
+static void tps65912_irq_lock(struct irq_data *data)
+{
+	struct tps65912 *tps65912 = irq_data_get_irq_chip_data(data);
+
+	mutex_lock(&tps65912->irq_lock);
+}
+
+static void tps65912_irq_sync_unlock(struct irq_data *data)
+{
+	struct tps65912 *tps65912 = irq_data_get_irq_chip_data(data);
+	u32 reg_mask;
+	u8 reg;
+
+	tps65912->read(tps65912, TPS65912_INT_MSK, 1, &reg);
+	reg_mask = reg;
+	tps65912->read(tps65912, TPS65912_INT_MSK2, 1, &reg);
+	reg_mask |= reg << 8;
+	tps65912->read(tps65912, TPS65912_INT_MSK3, 1, &reg);
+	reg_mask |= reg << 16;
+	tps65912->read(tps65912, TPS65912_INT_MSK4, 1, &reg);
+	reg_mask |= reg << 24;
+
+	if (tps65912->irq_mask != reg_mask) {
+		reg = tps65912->irq_mask & 0xFF;
+		tps65912->write(tps65912, TPS65912_INT_MSK, 1, &reg);
+		reg = tps65912->irq_mask >> 8 & 0xFF;
+		tps65912->write(tps65912, TPS65912_INT_MSK2, 1, &reg);
+		reg = tps65912->irq_mask >> 16 & 0xFF;
+		tps65912->write(tps65912, TPS65912_INT_MSK3, 1, &reg);
+		reg = tps65912->irq_mask >> 24 & 0xFF;
+		tps65912->write(tps65912, TPS65912_INT_MSK4, 1, &reg);
+	}
+
+	mutex_unlock(&tps65912->irq_lock);
+}
+
+static void tps65912_irq_enable(struct irq_data *data)
+{
+	struct tps65912 *tps65912 = irq_data_get_irq_chip_data(data);
+
+	tps65912->irq_mask &= ~(1 << irq_to_tps65912_irq(tps65912, data->irq));
+}
+
+static void tps65912_irq_disable(struct irq_data *data)
+{
+	struct tps65912 *tps65912 = irq_data_get_irq_chip_data(data);
+
+	tps65912->irq_mask |= (1 << irq_to_tps65912_irq(tps65912, data->irq));
+}
+
+static struct irq_chip tps65912_irq_chip = {
+	.name = "tps65912",
+	.irq_bus_lock = tps65912_irq_lock,
+	.irq_bus_sync_unlock = tps65912_irq_sync_unlock,
+	.irq_disable = tps65912_irq_disable,
+	.irq_enable = tps65912_irq_enable,
+};
+
+int tps65912_irq_init(struct tps65912 *tps65912, int irq,
+			    struct tps65912_platform_data *pdata)
+{
+	int ret, cur_irq;
+	int flags = IRQF_ONESHOT;
+	u8 reg;
+
+	if (!irq) {
+		dev_warn(tps65912->dev, "No interrupt support, no core IRQ\n");
+		return 0;
+	}
+
+	if (!pdata || !pdata->irq_base) {
+		dev_warn(tps65912->dev, "No interrupt support, no IRQ base\n");
+		return 0;
+	}
+
+	/* Clear unattended interrupts */
+	tps65912->read(tps65912, TPS65912_INT_STS, 1, &reg);
+	tps65912->write(tps65912, TPS65912_INT_STS, 1, &reg);
+	tps65912->read(tps65912, TPS65912_INT_STS2, 1, &reg);
+	tps65912->write(tps65912, TPS65912_INT_STS2, 1, &reg);
+	tps65912->read(tps65912, TPS65912_INT_STS3, 1, &reg);
+	tps65912->write(tps65912, TPS65912_INT_STS3, 1, &reg);
+	tps65912->read(tps65912, TPS65912_INT_STS4, 1, &reg);
+	tps65912->write(tps65912, TPS65912_INT_STS4, 1, &reg);
+
+	/* Mask top level interrupts */
+	tps65912->irq_mask = 0xFFFFFFFF;
+
+	mutex_init(&tps65912->irq_lock);
+	tps65912->chip_irq = irq;
+	tps65912->irq_base = pdata->irq_base;
+
+	tps65912->irq_num = TPS65912_NUM_IRQ;
+
+	/* Register with genirq */
+	for (cur_irq = tps65912->irq_base;
+	     cur_irq < tps65912->irq_num + tps65912->irq_base;
+	     cur_irq++) {
+		irq_set_chip_data(cur_irq, tps65912);
+		irq_set_chip_and_handler(cur_irq, &tps65912_irq_chip,
+					 handle_edge_irq);
+		irq_set_nested_thread(cur_irq, 1);
+		/* ARM needs us to explicitly flag the IRQ as valid
+		 * and will set them noprobe when we do so. */
+#ifdef CONFIG_ARM
+		set_irq_flags(cur_irq, IRQF_VALID);
+#else
+		irq_set_noprobe(cur_irq);
+#endif
+	}
+
+	ret = request_threaded_irq(irq, NULL, tps65912_irq, flags,
+				   "tps65912", tps65912);
+
+	irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW);
+	if (ret != 0)
+		dev_err(tps65912->dev, "Failed to request IRQ: %d\n", ret);
+
+	return ret;
+}
+
+int tps65912_irq_exit(struct tps65912 *tps65912)
+{
+	free_irq(tps65912->chip_irq, tps65912);
+	return 0;
+}
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/tps65912-spi.c b/ap/os/linux/linux-3.4.x/drivers/mfd/tps65912-spi.c
new file mode 100644
index 0000000..27d3302
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/tps65912-spi.c
@@ -0,0 +1,141 @@
+/*
+ * tps65912-spi.c  --  SPI access for TI TPS65912x PMIC
+ *
+ * Copyright 2011 Texas Instruments Inc.
+ *
+ * Author: Margarita Olaya Cabrera <magi@slimlogic.co.uk>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under  the terms of the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the License, or (at your
+ *  option) any later version.
+ *
+ *  This driver is based on wm8350 implementation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/tps65912.h>
+
+static int tps65912_spi_write(struct tps65912 *tps65912, u8 addr,
+							int bytes, void *src)
+{
+	struct spi_device *spi = tps65912->control_data;
+	u8 *data = (u8 *) src;
+	int ret;
+	/* bit 23 is the read/write bit */
+	unsigned long spi_data = 1 << 23 | addr << 15 | *data;
+	struct spi_transfer xfer;
+	struct spi_message msg;
+	u32 tx_buf, rx_buf;
+
+	tx_buf = spi_data;
+	rx_buf = 0;
+
+	xfer.tx_buf	= &tx_buf;
+	xfer.rx_buf	= NULL;
+	xfer.len	= sizeof(unsigned long);
+	xfer.bits_per_word = 24;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+
+	ret = spi_sync(spi, &msg);
+	return ret;
+}
+
+static int tps65912_spi_read(struct tps65912 *tps65912, u8 addr,
+							int bytes, void *dest)
+{
+	struct spi_device *spi = tps65912->control_data;
+	/* bit 23 is the read/write bit */
+	unsigned long spi_data = 0 << 23 | addr << 15;
+	struct spi_transfer xfer;
+	struct spi_message msg;
+	int ret;
+	u8 *data = (u8 *) dest;
+	u32 tx_buf, rx_buf;
+
+	tx_buf = spi_data;
+	rx_buf = 0;
+
+	xfer.tx_buf	= &tx_buf;
+	xfer.rx_buf	= &rx_buf;
+	xfer.len	= sizeof(unsigned long);
+	xfer.bits_per_word = 24;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+
+	if (spi == NULL)
+		return 0;
+
+	ret = spi_sync(spi, &msg);
+	if (ret == 0)
+		*data = (u8) (rx_buf & 0xFF);
+	return ret;
+}
+
+static int __devinit tps65912_spi_probe(struct spi_device *spi)
+{
+	struct tps65912 *tps65912;
+
+	tps65912 = kzalloc(sizeof(struct tps65912), GFP_KERNEL);
+	if (tps65912 == NULL)
+		return -ENOMEM;
+
+	tps65912->dev = &spi->dev;
+	tps65912->control_data = spi;
+	tps65912->read = tps65912_spi_read;
+	tps65912->write = tps65912_spi_write;
+
+	spi_set_drvdata(spi, tps65912);
+
+	return tps65912_device_init(tps65912);
+}
+
+static int __devexit tps65912_spi_remove(struct spi_device *spi)
+{
+	struct tps65912 *tps65912 = spi_get_drvdata(spi);
+
+	tps65912_device_exit(tps65912);
+
+	return 0;
+}
+
+static struct spi_driver tps65912_spi_driver = {
+	.driver = {
+		.name = "tps65912",
+		.owner = THIS_MODULE,
+	},
+	.probe	= tps65912_spi_probe,
+	.remove = __devexit_p(tps65912_spi_remove),
+};
+
+static int __init tps65912_spi_init(void)
+{
+	int ret;
+
+	ret = spi_register_driver(&tps65912_spi_driver);
+	if (ret != 0)
+		pr_err("Failed to register TPS65912 SPI driver: %d\n", ret);
+
+	return 0;
+}
+/* init early so consumer devices can complete system boot */
+subsys_initcall(tps65912_spi_init);
+
+static void __exit tps65912_spi_exit(void)
+{
+	spi_unregister_driver(&tps65912_spi_driver);
+}
+module_exit(tps65912_spi_exit);
+
+MODULE_AUTHOR("Margarita Olaya	<magi@slimlogic.co.uk>");
+MODULE_DESCRIPTION("SPI support for TPS65912 chip family mfd");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/twl-core.c b/ap/os/linux/linux-3.4.x/drivers/mfd/twl-core.c
new file mode 100644
index 0000000..7c2267e
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/twl-core.c
@@ -0,0 +1,1382 @@
+/*
+ * twl_core.c - driver for TWL4030/TWL5030/TWL60X0/TPS659x0 PM
+ * and audio CODEC devices
+ *
+ * Copyright (C) 2005-2006 Texas Instruments, Inc.
+ *
+ * Modifications to defer interrupt handling to a kernel thread:
+ * Copyright (C) 2006 MontaVista Software, Inc.
+ *
+ * Based on tlv320aic23.c:
+ * Copyright (c) by Kai Svahn <kai.svahn@nokia.com>
+ *
+ * Code cleanup and modifications to IRQ handler.
+ * by syed khasim <x0khasim@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+
+#include <linux/regulator/machine.h>
+
+#include <linux/i2c.h>
+#include <linux/i2c/twl.h>
+
+#include "twl-core.h"
+
+/*
+ * The TWL4030 "Triton 2" is one of a family of a multi-function "Power
+ * Management and System Companion Device" chips originally designed for
+ * use in OMAP2 and OMAP 3 based systems.  Its control interfaces use I2C,
+ * often at around 3 Mbit/sec, including for interrupt handling.
+ *
+ * This driver core provides genirq support for the interrupts emitted,
+ * by the various modules, and exports register access primitives.
+ *
+ * FIXME this driver currently requires use of the first interrupt line
+ * (and associated registers).
+ */
+
+#define DRIVER_NAME			"twl"
+
+#if defined(CONFIG_KEYBOARD_TWL4030) || defined(CONFIG_KEYBOARD_TWL4030_MODULE)
+#define twl_has_keypad()	true
+#else
+#define twl_has_keypad()	false
+#endif
+
+#if defined(CONFIG_GPIO_TWL4030) || defined(CONFIG_GPIO_TWL4030_MODULE)
+#define twl_has_gpio()	true
+#else
+#define twl_has_gpio()	false
+#endif
+
+#if defined(CONFIG_REGULATOR_TWL4030) \
+	|| defined(CONFIG_REGULATOR_TWL4030_MODULE)
+#define twl_has_regulator()	true
+#else
+#define twl_has_regulator()	false
+#endif
+
+#if defined(CONFIG_TWL4030_MADC) || defined(CONFIG_TWL4030_MADC_MODULE)
+#define twl_has_madc()	true
+#else
+#define twl_has_madc()	false
+#endif
+
+#ifdef CONFIG_TWL4030_POWER
+#define twl_has_power()        true
+#else
+#define twl_has_power()        false
+#endif
+
+#if defined(CONFIG_RTC_DRV_TWL4030) || defined(CONFIG_RTC_DRV_TWL4030_MODULE)
+#define twl_has_rtc()	true
+#else
+#define twl_has_rtc()	false
+#endif
+
+#if defined(CONFIG_TWL4030_USB) || defined(CONFIG_TWL4030_USB_MODULE) ||\
+	defined(CONFIG_TWL6030_USB) || defined(CONFIG_TWL6030_USB_MODULE)
+#define twl_has_usb()	true
+#else
+#define twl_has_usb()	false
+#endif
+
+#if defined(CONFIG_TWL4030_WATCHDOG) || \
+	defined(CONFIG_TWL4030_WATCHDOG_MODULE)
+#define twl_has_watchdog()        true
+#else
+#define twl_has_watchdog()        false
+#endif
+
+#if defined(CONFIG_MFD_TWL4030_AUDIO) || \
+	defined(CONFIG_MFD_TWL4030_AUDIO_MODULE)
+#define twl_has_codec()	true
+#else
+#define twl_has_codec()	false
+#endif
+
+#if defined(CONFIG_CHARGER_TWL4030) || defined(CONFIG_CHARGER_TWL4030_MODULE)
+#define twl_has_bci()	true
+#else
+#define twl_has_bci()	false
+#endif
+
+/* Triton Core internal information (BEGIN) */
+
+/* Last - for index max*/
+#define TWL4030_MODULE_LAST		TWL4030_MODULE_SECURED_REG
+
+#define TWL_NUM_SLAVES		4
+
+#if defined(CONFIG_INPUT_TWL4030_PWRBUTTON) \
+	|| defined(CONFIG_INPUT_TWL4030_PWRBUTTON_MODULE)
+#define twl_has_pwrbutton()	true
+#else
+#define twl_has_pwrbutton()	false
+#endif
+
+#define SUB_CHIP_ID0 0
+#define SUB_CHIP_ID1 1
+#define SUB_CHIP_ID2 2
+#define SUB_CHIP_ID3 3
+#define SUB_CHIP_ID_INVAL 0xff
+
+#define TWL_MODULE_LAST TWL4030_MODULE_LAST
+
+/* Base Address defns for twl4030_map[] */
+
+/* subchip/slave 0 - USB ID */
+#define TWL4030_BASEADD_USB		0x0000
+
+/* subchip/slave 1 - AUD ID */
+#define TWL4030_BASEADD_AUDIO_VOICE	0x0000
+#define TWL4030_BASEADD_GPIO		0x0098
+#define TWL4030_BASEADD_INTBR		0x0085
+#define TWL4030_BASEADD_PIH		0x0080
+#define TWL4030_BASEADD_TEST		0x004C
+
+/* subchip/slave 2 - AUX ID */
+#define TWL4030_BASEADD_INTERRUPTS	0x00B9
+#define TWL4030_BASEADD_LED		0x00EE
+#define TWL4030_BASEADD_MADC		0x0000
+#define TWL4030_BASEADD_MAIN_CHARGE	0x0074
+#define TWL4030_BASEADD_PRECHARGE	0x00AA
+#define TWL4030_BASEADD_PWM0		0x00F8
+#define TWL4030_BASEADD_PWM1		0x00FB
+#define TWL4030_BASEADD_PWMA		0x00EF
+#define TWL4030_BASEADD_PWMB		0x00F1
+#define TWL4030_BASEADD_KEYPAD		0x00D2
+
+#define TWL5031_BASEADD_ACCESSORY	0x0074 /* Replaces Main Charge */
+#define TWL5031_BASEADD_INTERRUPTS	0x00B9 /* Different than TWL4030's
+						  one */
+
+/* subchip/slave 3 - POWER ID */
+#define TWL4030_BASEADD_BACKUP		0x0014
+#define TWL4030_BASEADD_INT		0x002E
+#define TWL4030_BASEADD_PM_MASTER	0x0036
+#define TWL4030_BASEADD_PM_RECEIVER	0x005B
+#define TWL4030_BASEADD_RTC		0x001C
+#define TWL4030_BASEADD_SECURED_REG	0x0000
+
+/* Triton Core internal information (END) */
+
+
+/* subchip/slave 0 0x48 - POWER */
+#define TWL6030_BASEADD_RTC		0x0000
+#define TWL6030_BASEADD_MEM		0x0017
+#define TWL6030_BASEADD_PM_MASTER	0x001F
+#define TWL6030_BASEADD_PM_SLAVE_MISC	0x0030 /* PM_RECEIVER */
+#define TWL6030_BASEADD_PM_MISC		0x00E2
+#define TWL6030_BASEADD_PM_PUPD		0x00F0
+
+/* subchip/slave 1 0x49 - FEATURE */
+#define TWL6030_BASEADD_USB		0x0000
+#define TWL6030_BASEADD_GPADC_CTRL	0x002E
+#define TWL6030_BASEADD_AUX		0x0090
+#define TWL6030_BASEADD_PWM		0x00BA
+#define TWL6030_BASEADD_GASGAUGE	0x00C0
+#define TWL6030_BASEADD_PIH		0x00D0
+#define TWL6030_BASEADD_CHARGER		0x00E0
+#define TWL6025_BASEADD_CHARGER		0x00DA
+
+/* subchip/slave 2 0x4A - DFT */
+#define TWL6030_BASEADD_DIEID		0x00C0
+
+/* subchip/slave 3 0x4B - AUDIO */
+#define TWL6030_BASEADD_AUDIO		0x0000
+#define TWL6030_BASEADD_RSV		0x0000
+#define TWL6030_BASEADD_ZERO		0x0000
+
+/* Few power values */
+#define R_CFG_BOOT			0x05
+
+/* some fields in R_CFG_BOOT */
+#define HFCLK_FREQ_19p2_MHZ		(1 << 0)
+#define HFCLK_FREQ_26_MHZ		(2 << 0)
+#define HFCLK_FREQ_38p4_MHZ		(3 << 0)
+#define HIGH_PERF_SQ			(1 << 3)
+#define CK32K_LOWPWR_EN			(1 << 7)
+
+
+/* chip-specific feature flags, for i2c_device_id.driver_data */
+#define TWL4030_VAUX2		BIT(0)	/* pre-5030 voltage ranges */
+#define TPS_SUBSET		BIT(1)	/* tps659[23]0 have fewer LDOs */
+#define TWL5031			BIT(2)  /* twl5031 has different registers */
+#define TWL6030_CLASS		BIT(3)	/* TWL6030 class */
+
+/*----------------------------------------------------------------------*/
+
+/* is driver active, bound to a chip? */
+static bool inuse;
+
+/* TWL IDCODE Register value */
+static u32 twl_idcode;
+
+static unsigned int twl_id;
+unsigned int twl_rev(void)
+{
+	return twl_id;
+}
+EXPORT_SYMBOL(twl_rev);
+
+/* Structure for each TWL4030/TWL6030 Slave */
+struct twl_client {
+	struct i2c_client *client;
+	u8 address;
+
+	/* max numb of i2c_msg required is for read =2 */
+	struct i2c_msg xfer_msg[2];
+
+	/* To lock access to xfer_msg */
+	struct mutex xfer_lock;
+};
+
+static struct twl_client twl_modules[TWL_NUM_SLAVES];
+
+/* mapping the module id to slave id and base address */
+struct twl_mapping {
+	unsigned char sid;	/* Slave ID */
+	unsigned char base;	/* base address */
+};
+static struct twl_mapping *twl_map;
+
+static struct twl_mapping twl4030_map[TWL4030_MODULE_LAST + 1] = {
+	/*
+	 * NOTE:  don't change this table without updating the
+	 * <linux/i2c/twl.h> defines for TWL4030_MODULE_*
+	 * so they continue to match the order in this table.
+	 */
+
+	{ 0, TWL4030_BASEADD_USB },
+
+	{ 1, TWL4030_BASEADD_AUDIO_VOICE },
+	{ 1, TWL4030_BASEADD_GPIO },
+	{ 1, TWL4030_BASEADD_INTBR },
+	{ 1, TWL4030_BASEADD_PIH },
+	{ 1, TWL4030_BASEADD_TEST },
+
+	{ 2, TWL4030_BASEADD_KEYPAD },
+	{ 2, TWL4030_BASEADD_MADC },
+	{ 2, TWL4030_BASEADD_INTERRUPTS },
+	{ 2, TWL4030_BASEADD_LED },
+	{ 2, TWL4030_BASEADD_MAIN_CHARGE },
+	{ 2, TWL4030_BASEADD_PRECHARGE },
+	{ 2, TWL4030_BASEADD_PWM0 },
+	{ 2, TWL4030_BASEADD_PWM1 },
+	{ 2, TWL4030_BASEADD_PWMA },
+	{ 2, TWL4030_BASEADD_PWMB },
+	{ 2, TWL5031_BASEADD_ACCESSORY },
+	{ 2, TWL5031_BASEADD_INTERRUPTS },
+
+	{ 3, TWL4030_BASEADD_BACKUP },
+	{ 3, TWL4030_BASEADD_INT },
+	{ 3, TWL4030_BASEADD_PM_MASTER },
+	{ 3, TWL4030_BASEADD_PM_RECEIVER },
+	{ 3, TWL4030_BASEADD_RTC },
+	{ 3, TWL4030_BASEADD_SECURED_REG },
+};
+
+static struct twl_mapping twl6030_map[] = {
+	/*
+	 * NOTE:  don't change this table without updating the
+	 * <linux/i2c/twl.h> defines for TWL4030_MODULE_*
+	 * so they continue to match the order in this table.
+	 */
+	{ SUB_CHIP_ID1, TWL6030_BASEADD_USB },
+	{ SUB_CHIP_ID_INVAL, TWL6030_BASEADD_AUDIO },
+	{ SUB_CHIP_ID2, TWL6030_BASEADD_DIEID },
+	{ SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
+	{ SUB_CHIP_ID1, TWL6030_BASEADD_PIH },
+
+	{ SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
+	{ SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
+	{ SUB_CHIP_ID1, TWL6030_BASEADD_GPADC_CTRL },
+	{ SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
+	{ SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
+
+	{ SUB_CHIP_ID1, TWL6030_BASEADD_CHARGER },
+	{ SUB_CHIP_ID1, TWL6030_BASEADD_GASGAUGE },
+	{ SUB_CHIP_ID1, TWL6030_BASEADD_PWM },
+	{ SUB_CHIP_ID0, TWL6030_BASEADD_ZERO },
+	{ SUB_CHIP_ID1, TWL6030_BASEADD_ZERO },
+
+	{ SUB_CHIP_ID2, TWL6030_BASEADD_ZERO },
+	{ SUB_CHIP_ID2, TWL6030_BASEADD_ZERO },
+	{ SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
+	{ SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
+	{ SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
+	{ SUB_CHIP_ID0, TWL6030_BASEADD_PM_MASTER },
+	{ SUB_CHIP_ID0, TWL6030_BASEADD_PM_SLAVE_MISC },
+
+	{ SUB_CHIP_ID0, TWL6030_BASEADD_RTC },
+	{ SUB_CHIP_ID0, TWL6030_BASEADD_MEM },
+	{ SUB_CHIP_ID1, TWL6025_BASEADD_CHARGER },
+};
+
+/*----------------------------------------------------------------------*/
+
+/* Exported Functions */
+
+/**
+ * twl_i2c_write - Writes a n bit register in TWL4030/TWL5030/TWL60X0
+ * @mod_no: module number
+ * @value: an array of num_bytes+1 containing data to write
+ * @reg: register address (just offset will do)
+ * @num_bytes: number of bytes to transfer
+ *
+ * IMPORTANT: for 'value' parameter: Allocate value num_bytes+1 and
+ * valid data starts at Offset 1.
+ *
+ * Returns the result of operation - 0 is success
+ */
+int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
+{
+	int ret;
+	int sid;
+	struct twl_client *twl;
+	struct i2c_msg *msg;
+
+	if (unlikely(mod_no > TWL_MODULE_LAST)) {
+		pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no);
+		return -EPERM;
+	}
+	if (unlikely(!inuse)) {
+		pr_err("%s: not initialized\n", DRIVER_NAME);
+		return -EPERM;
+	}
+	sid = twl_map[mod_no].sid;
+	if (unlikely(sid == SUB_CHIP_ID_INVAL)) {
+		pr_err("%s: module %d is not part of the pmic\n",
+		       DRIVER_NAME, mod_no);
+		return -EINVAL;
+	}
+	twl = &twl_modules[sid];
+
+	mutex_lock(&twl->xfer_lock);
+	/*
+	 * [MSG1]: fill the register address data
+	 * fill the data Tx buffer
+	 */
+	msg = &twl->xfer_msg[0];
+	msg->addr = twl->address;
+	msg->len = num_bytes + 1;
+	msg->flags = 0;
+	msg->buf = value;
+	/* over write the first byte of buffer with the register address */
+	*value = twl_map[mod_no].base + reg;
+	ret = i2c_transfer(twl->client->adapter, twl->xfer_msg, 1);
+	mutex_unlock(&twl->xfer_lock);
+
+	/* i2c_transfer returns number of messages transferred */
+	if (ret != 1) {
+		pr_err("%s: i2c_write failed to transfer all messages\n",
+			DRIVER_NAME);
+		if (ret < 0)
+			return ret;
+		else
+			return -EIO;
+	} else {
+		return 0;
+	}
+}
+EXPORT_SYMBOL(twl_i2c_write);
+
+/**
+ * twl_i2c_read - Reads a n bit register in TWL4030/TWL5030/TWL60X0
+ * @mod_no: module number
+ * @value: an array of num_bytes containing data to be read
+ * @reg: register address (just offset will do)
+ * @num_bytes: number of bytes to transfer
+ *
+ * Returns result of operation - num_bytes is success else failure.
+ */
+int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
+{
+	int ret;
+	u8 val;
+	int sid;
+	struct twl_client *twl;
+	struct i2c_msg *msg;
+
+	if (unlikely(mod_no > TWL_MODULE_LAST)) {
+		pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no);
+		return -EPERM;
+	}
+	if (unlikely(!inuse)) {
+		pr_err("%s: not initialized\n", DRIVER_NAME);
+		return -EPERM;
+	}
+	sid = twl_map[mod_no].sid;
+	if (unlikely(sid == SUB_CHIP_ID_INVAL)) {
+		pr_err("%s: module %d is not part of the pmic\n",
+		       DRIVER_NAME, mod_no);
+		return -EINVAL;
+	}
+	twl = &twl_modules[sid];
+
+	mutex_lock(&twl->xfer_lock);
+	/* [MSG1] fill the register address data */
+	msg = &twl->xfer_msg[0];
+	msg->addr = twl->address;
+	msg->len = 1;
+	msg->flags = 0;	/* Read the register value */
+	val = twl_map[mod_no].base + reg;
+	msg->buf = &val;
+	/* [MSG2] fill the data rx buffer */
+	msg = &twl->xfer_msg[1];
+	msg->addr = twl->address;
+	msg->flags = I2C_M_RD;	/* Read the register value */
+	msg->len = num_bytes;	/* only n bytes */
+	msg->buf = value;
+	ret = i2c_transfer(twl->client->adapter, twl->xfer_msg, 2);
+	mutex_unlock(&twl->xfer_lock);
+
+	/* i2c_transfer returns number of messages transferred */
+	if (ret != 2) {
+		pr_err("%s: i2c_read failed to transfer all messages\n",
+			DRIVER_NAME);
+		if (ret < 0)
+			return ret;
+		else
+			return -EIO;
+	} else {
+		return 0;
+	}
+}
+EXPORT_SYMBOL(twl_i2c_read);
+
+/**
+ * twl_i2c_write_u8 - Writes a 8 bit register in TWL4030/TWL5030/TWL60X0
+ * @mod_no: module number
+ * @value: the value to be written 8 bit
+ * @reg: register address (just offset will do)
+ *
+ * Returns result of operation - 0 is success
+ */
+int twl_i2c_write_u8(u8 mod_no, u8 value, u8 reg)
+{
+
+	/* 2 bytes offset 1 contains the data offset 0 is used by i2c_write */
+	u8 temp_buffer[2] = { 0 };
+	/* offset 1 contains the data */
+	temp_buffer[1] = value;
+	return twl_i2c_write(mod_no, temp_buffer, reg, 1);
+}
+EXPORT_SYMBOL(twl_i2c_write_u8);
+
+/**
+ * twl_i2c_read_u8 - Reads a 8 bit register from TWL4030/TWL5030/TWL60X0
+ * @mod_no: module number
+ * @value: the value read 8 bit
+ * @reg: register address (just offset will do)
+ *
+ * Returns result of operation - 0 is success
+ */
+int twl_i2c_read_u8(u8 mod_no, u8 *value, u8 reg)
+{
+	return twl_i2c_read(mod_no, value, reg, 1);
+}
+EXPORT_SYMBOL(twl_i2c_read_u8);
+
+/*----------------------------------------------------------------------*/
+
+/**
+ * twl_read_idcode_register - API to read the IDCODE register.
+ *
+ * Unlocks the IDCODE register and read the 32 bit value.
+ */
+static int twl_read_idcode_register(void)
+{
+	int err;
+
+	err = twl_i2c_write_u8(TWL4030_MODULE_INTBR, TWL_EEPROM_R_UNLOCK,
+						REG_UNLOCK_TEST_REG);
+	if (err) {
+		pr_err("TWL4030 Unable to unlock IDCODE registers -%d\n", err);
+		goto fail;
+	}
+
+	err = twl_i2c_read(TWL4030_MODULE_INTBR, (u8 *)(&twl_idcode),
+						REG_IDCODE_7_0, 4);
+	if (err) {
+		pr_err("TWL4030: unable to read IDCODE -%d\n", err);
+		goto fail;
+	}
+
+	err = twl_i2c_write_u8(TWL4030_MODULE_INTBR, 0x0, REG_UNLOCK_TEST_REG);
+	if (err)
+		pr_err("TWL4030 Unable to relock IDCODE registers -%d\n", err);
+fail:
+	return err;
+}
+
+/**
+ * twl_get_type - API to get TWL Si type.
+ *
+ * Api to get the TWL Si type from IDCODE value.
+ */
+int twl_get_type(void)
+{
+	return TWL_SIL_TYPE(twl_idcode);
+}
+EXPORT_SYMBOL_GPL(twl_get_type);
+
+/**
+ * twl_get_version - API to get TWL Si version.
+ *
+ * Api to get the TWL Si version from IDCODE value.
+ */
+int twl_get_version(void)
+{
+	return TWL_SIL_REV(twl_idcode);
+}
+EXPORT_SYMBOL_GPL(twl_get_version);
+
+static struct device *
+add_numbered_child(unsigned chip, const char *name, int num,
+		void *pdata, unsigned pdata_len,
+		bool can_wakeup, int irq0, int irq1)
+{
+	struct platform_device	*pdev;
+	struct twl_client	*twl = &twl_modules[chip];
+	int			status;
+
+	pdev = platform_device_alloc(name, num);
+	if (!pdev) {
+		dev_dbg(&twl->client->dev, "can't alloc dev\n");
+		status = -ENOMEM;
+		goto err;
+	}
+
+	device_init_wakeup(&pdev->dev, can_wakeup);
+	pdev->dev.parent = &twl->client->dev;
+
+	if (pdata) {
+		status = platform_device_add_data(pdev, pdata, pdata_len);
+		if (status < 0) {
+			dev_dbg(&pdev->dev, "can't add platform_data\n");
+			goto err;
+		}
+	}
+
+	if (irq0) {
+		struct resource r[2] = {
+			{ .start = irq0, .flags = IORESOURCE_IRQ, },
+			{ .start = irq1, .flags = IORESOURCE_IRQ, },
+		};
+
+		status = platform_device_add_resources(pdev, r, irq1 ? 2 : 1);
+		if (status < 0) {
+			dev_dbg(&pdev->dev, "can't add irqs\n");
+			goto err;
+		}
+	}
+
+	status = platform_device_add(pdev);
+
+err:
+	if (status < 0) {
+		platform_device_put(pdev);
+		dev_err(&twl->client->dev, "can't add %s dev\n", name);
+		return ERR_PTR(status);
+	}
+	return &pdev->dev;
+}
+
+static inline struct device *add_child(unsigned chip, const char *name,
+		void *pdata, unsigned pdata_len,
+		bool can_wakeup, int irq0, int irq1)
+{
+	return add_numbered_child(chip, name, -1, pdata, pdata_len,
+		can_wakeup, irq0, irq1);
+}
+
+static struct device *
+add_regulator_linked(int num, struct regulator_init_data *pdata,
+		struct regulator_consumer_supply *consumers,
+		unsigned num_consumers, unsigned long features)
+{
+	unsigned sub_chip_id;
+	struct twl_regulator_driver_data drv_data;
+
+	/* regulator framework demands init_data ... */
+	if (!pdata)
+		return NULL;
+
+	if (consumers) {
+		pdata->consumer_supplies = consumers;
+		pdata->num_consumer_supplies = num_consumers;
+	}
+
+	if (pdata->driver_data) {
+		/* If we have existing drv_data, just add the flags */
+		struct twl_regulator_driver_data *tmp;
+		tmp = pdata->driver_data;
+		tmp->features |= features;
+	} else {
+		/* add new driver data struct, used only during init */
+		drv_data.features = features;
+		drv_data.set_voltage = NULL;
+		drv_data.get_voltage = NULL;
+		drv_data.data = NULL;
+		pdata->driver_data = &drv_data;
+	}
+
+	/* NOTE:  we currently ignore regulator IRQs, e.g. for short circuits */
+	sub_chip_id = twl_map[TWL_MODULE_PM_MASTER].sid;
+	return add_numbered_child(sub_chip_id, "twl_reg", num,
+		pdata, sizeof(*pdata), false, 0, 0);
+}
+
+static struct device *
+add_regulator(int num, struct regulator_init_data *pdata,
+		unsigned long features)
+{
+	return add_regulator_linked(num, pdata, NULL, 0, features);
+}
+
+/*
+ * NOTE:  We know the first 8 IRQs after pdata->base_irq are
+ * for the PIH, and the next are for the PWR_INT SIH, since
+ * that's how twl_init_irq() sets things up.
+ */
+
+static int
+add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
+		unsigned long features)
+{
+	struct device	*child;
+	unsigned sub_chip_id;
+
+	if (twl_has_gpio() && pdata->gpio) {
+		child = add_child(SUB_CHIP_ID1, "twl4030_gpio",
+				pdata->gpio, sizeof(*pdata->gpio),
+				false, irq_base + GPIO_INTR_OFFSET, 0);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+	}
+
+	if (twl_has_keypad() && pdata->keypad) {
+		child = add_child(SUB_CHIP_ID2, "twl4030_keypad",
+				pdata->keypad, sizeof(*pdata->keypad),
+				true, irq_base + KEYPAD_INTR_OFFSET, 0);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+	}
+
+	if (twl_has_madc() && pdata->madc) {
+		child = add_child(2, "twl4030_madc",
+				pdata->madc, sizeof(*pdata->madc),
+				true, irq_base + MADC_INTR_OFFSET, 0);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+	}
+
+	if (twl_has_rtc()) {
+		/*
+		 * REVISIT platform_data here currently might expose the
+		 * "msecure" line ... but for now we just expect board
+		 * setup to tell the chip "it's always ok to SET_TIME".
+		 * Eventually, Linux might become more aware of such
+		 * HW security concerns, and "least privilege".
+		 */
+		sub_chip_id = twl_map[TWL_MODULE_RTC].sid;
+		child = add_child(sub_chip_id, "twl_rtc",
+				NULL, 0,
+				true, irq_base + RTC_INTR_OFFSET, 0);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+	}
+
+	if (twl_has_usb() && pdata->usb && twl_class_is_4030()) {
+
+		static struct regulator_consumer_supply usb1v5 = {
+			.supply =	"usb1v5",
+		};
+		static struct regulator_consumer_supply usb1v8 = {
+			.supply =	"usb1v8",
+		};
+		static struct regulator_consumer_supply usb3v1 = {
+			.supply =	"usb3v1",
+		};
+
+	/* First add the regulators so that they can be used by transceiver */
+		if (twl_has_regulator()) {
+			/* this is a template that gets copied */
+			struct regulator_init_data usb_fixed = {
+				.constraints.valid_modes_mask =
+					REGULATOR_MODE_NORMAL
+					| REGULATOR_MODE_STANDBY,
+				.constraints.valid_ops_mask =
+					REGULATOR_CHANGE_MODE
+					| REGULATOR_CHANGE_STATUS,
+			};
+
+			child = add_regulator_linked(TWL4030_REG_VUSB1V5,
+						      &usb_fixed, &usb1v5, 1,
+						      features);
+			if (IS_ERR(child))
+				return PTR_ERR(child);
+
+			child = add_regulator_linked(TWL4030_REG_VUSB1V8,
+						      &usb_fixed, &usb1v8, 1,
+						      features);
+			if (IS_ERR(child))
+				return PTR_ERR(child);
+
+			child = add_regulator_linked(TWL4030_REG_VUSB3V1,
+						      &usb_fixed, &usb3v1, 1,
+						      features);
+			if (IS_ERR(child))
+				return PTR_ERR(child);
+
+		}
+
+		child = add_child(0, "twl4030_usb",
+				pdata->usb, sizeof(*pdata->usb),
+				true,
+				/* irq0 = USB_PRES, irq1 = USB */
+				irq_base + USB_PRES_INTR_OFFSET,
+				irq_base + USB_INTR_OFFSET);
+
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+
+		/* we need to connect regulators to this transceiver */
+		if (twl_has_regulator() && child) {
+			usb1v5.dev_name = dev_name(child);
+			usb1v8.dev_name = dev_name(child);
+			usb3v1.dev_name = dev_name(child);
+		}
+	}
+	if (twl_has_usb() && pdata->usb && twl_class_is_6030()) {
+
+		static struct regulator_consumer_supply usb3v3;
+		int regulator;
+
+		if (twl_has_regulator()) {
+			/* this is a template that gets copied */
+			struct regulator_init_data usb_fixed = {
+				.constraints.valid_modes_mask =
+					REGULATOR_MODE_NORMAL
+					| REGULATOR_MODE_STANDBY,
+				.constraints.valid_ops_mask =
+					REGULATOR_CHANGE_MODE
+					| REGULATOR_CHANGE_STATUS,
+			};
+
+			if (features & TWL6025_SUBCLASS) {
+				usb3v3.supply =	"ldousb";
+				regulator = TWL6025_REG_LDOUSB;
+			} else {
+				usb3v3.supply = "vusb";
+				regulator = TWL6030_REG_VUSB;
+			}
+			child = add_regulator_linked(regulator, &usb_fixed,
+							&usb3v3, 1,
+							features);
+			if (IS_ERR(child))
+				return PTR_ERR(child);
+		}
+
+		pdata->usb->features = features;
+
+		child = add_child(0, "twl6030_usb",
+			pdata->usb, sizeof(*pdata->usb),
+			true,
+			/* irq1 = VBUS_PRES, irq0 = USB ID */
+			irq_base + USBOTG_INTR_OFFSET,
+			irq_base + USB_PRES_INTR_OFFSET);
+
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+		/* we need to connect regulators to this transceiver */
+		if (twl_has_regulator() && child)
+			usb3v3.dev_name = dev_name(child);
+	} else if (twl_has_regulator() && twl_class_is_6030()) {
+		if (features & TWL6025_SUBCLASS)
+			child = add_regulator(TWL6025_REG_LDOUSB,
+						pdata->ldousb, features);
+		else
+			child = add_regulator(TWL6030_REG_VUSB,
+						pdata->vusb, features);
+
+			if (IS_ERR(child))
+					return PTR_ERR(child);
+	}
+
+	if (twl_has_watchdog() && twl_class_is_4030()) {
+		child = add_child(0, "twl4030_wdt", NULL, 0, false, 0, 0);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+	}
+
+	if (twl_has_pwrbutton() && twl_class_is_4030()) {
+		child = add_child(1, "twl4030_pwrbutton",
+				NULL, 0, true, irq_base + 8 + 0, 0);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+	}
+
+	if (twl_has_codec() && pdata->audio && twl_class_is_4030()) {
+		sub_chip_id = twl_map[TWL_MODULE_AUDIO_VOICE].sid;
+		child = add_child(sub_chip_id, "twl4030-audio",
+				pdata->audio, sizeof(*pdata->audio),
+				false, 0, 0);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+	}
+
+	/* twl4030 regulators */
+	if (twl_has_regulator() && twl_class_is_4030()) {
+		child = add_regulator(TWL4030_REG_VPLL1, pdata->vpll1,
+					features);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+
+		child = add_regulator(TWL4030_REG_VIO, pdata->vio,
+					features);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+
+		child = add_regulator(TWL4030_REG_VDD1, pdata->vdd1,
+					features);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+
+		child = add_regulator(TWL4030_REG_VDD2, pdata->vdd2,
+					features);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+
+		child = add_regulator(TWL4030_REG_VMMC1, pdata->vmmc1,
+					features);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+
+		child = add_regulator(TWL4030_REG_VDAC, pdata->vdac,
+					features);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+
+		child = add_regulator((features & TWL4030_VAUX2)
+					? TWL4030_REG_VAUX2_4030
+					: TWL4030_REG_VAUX2,
+				pdata->vaux2, features);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+
+		child = add_regulator(TWL4030_REG_VINTANA1, pdata->vintana1,
+					features);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+
+		child = add_regulator(TWL4030_REG_VINTANA2, pdata->vintana2,
+					features);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+
+		child = add_regulator(TWL4030_REG_VINTDIG, pdata->vintdig,
+					features);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+	}
+
+	/* maybe add LDOs that are omitted on cost-reduced parts */
+	if (twl_has_regulator() && !(features & TPS_SUBSET)
+	  && twl_class_is_4030()) {
+		child = add_regulator(TWL4030_REG_VPLL2, pdata->vpll2,
+					features);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+
+		child = add_regulator(TWL4030_REG_VMMC2, pdata->vmmc2,
+					features);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+
+		child = add_regulator(TWL4030_REG_VSIM, pdata->vsim,
+					features);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+
+		child = add_regulator(TWL4030_REG_VAUX1, pdata->vaux1,
+					features);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+
+		child = add_regulator(TWL4030_REG_VAUX3, pdata->vaux3,
+					features);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+
+		child = add_regulator(TWL4030_REG_VAUX4, pdata->vaux4,
+					features);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+	}
+
+	/* twl6030 regulators */
+	if (twl_has_regulator() && twl_class_is_6030() &&
+			!(features & TWL6025_SUBCLASS)) {
+		child = add_regulator(TWL6030_REG_VDD1, pdata->vdd1,
+					features);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+
+		child = add_regulator(TWL6030_REG_VDD2, pdata->vdd2,
+					features);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+
+		child = add_regulator(TWL6030_REG_VDD3, pdata->vdd3,
+					features);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+
+		child = add_regulator(TWL6030_REG_V1V8, pdata->v1v8,
+					features);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+
+		child = add_regulator(TWL6030_REG_V2V1, pdata->v2v1,
+					features);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+
+		child = add_regulator(TWL6030_REG_VMMC, pdata->vmmc,
+					features);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+
+		child = add_regulator(TWL6030_REG_VPP, pdata->vpp,
+					features);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+
+		child = add_regulator(TWL6030_REG_VUSIM, pdata->vusim,
+					features);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+
+		child = add_regulator(TWL6030_REG_VCXIO, pdata->vcxio,
+					features);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+
+		child = add_regulator(TWL6030_REG_VDAC, pdata->vdac,
+					features);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+
+		child = add_regulator(TWL6030_REG_VAUX1_6030, pdata->vaux1,
+					features);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+
+		child = add_regulator(TWL6030_REG_VAUX2_6030, pdata->vaux2,
+					features);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+
+		child = add_regulator(TWL6030_REG_VAUX3_6030, pdata->vaux3,
+					features);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+
+		child = add_regulator(TWL6030_REG_CLK32KG, pdata->clk32kg,
+					features);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+	}
+
+	/* 6030 and 6025 share this regulator */
+	if (twl_has_regulator() && twl_class_is_6030()) {
+		child = add_regulator(TWL6030_REG_VANA, pdata->vana,
+					features);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+	}
+
+	/* twl6025 regulators */
+	if (twl_has_regulator() && twl_class_is_6030() &&
+			(features & TWL6025_SUBCLASS)) {
+		child = add_regulator(TWL6025_REG_LDO5, pdata->ldo5,
+					features);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+
+		child = add_regulator(TWL6025_REG_LDO1, pdata->ldo1,
+					features);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+
+		child = add_regulator(TWL6025_REG_LDO7, pdata->ldo7,
+					features);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+
+		child = add_regulator(TWL6025_REG_LDO6, pdata->ldo6,
+					features);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+
+		child = add_regulator(TWL6025_REG_LDOLN, pdata->ldoln,
+					features);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+
+		child = add_regulator(TWL6025_REG_LDO2, pdata->ldo2,
+					features);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+
+		child = add_regulator(TWL6025_REG_LDO4, pdata->ldo4,
+					features);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+
+		child = add_regulator(TWL6025_REG_LDO3, pdata->ldo3,
+					features);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+
+		child = add_regulator(TWL6025_REG_SMPS3, pdata->smps3,
+					features);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+
+		child = add_regulator(TWL6025_REG_SMPS4, pdata->smps4,
+					features);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+
+		child = add_regulator(TWL6025_REG_VIO, pdata->vio6025,
+					features);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+
+	}
+
+	if (twl_has_bci() && pdata->bci &&
+			!(features & (TPS_SUBSET | TWL5031))) {
+		child = add_child(3, "twl4030_bci",
+				pdata->bci, sizeof(*pdata->bci), false,
+				/* irq0 = CHG_PRES, irq1 = BCI */
+				irq_base + BCI_PRES_INTR_OFFSET,
+				irq_base + BCI_INTR_OFFSET);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+	}
+
+	return 0;
+}
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * These three functions initialize the on-chip clock framework,
+ * letting it generate the right frequencies for USB, MADC, and
+ * other purposes.
+ */
+static inline int __init protect_pm_master(void)
+{
+	int e = 0;
+
+	e = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0,
+			TWL4030_PM_MASTER_PROTECT_KEY);
+	return e;
+}
+
+static inline int __init unprotect_pm_master(void)
+{
+	int e = 0;
+
+	e |= twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER,
+			TWL4030_PM_MASTER_KEY_CFG1,
+			TWL4030_PM_MASTER_PROTECT_KEY);
+	e |= twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER,
+			TWL4030_PM_MASTER_KEY_CFG2,
+			TWL4030_PM_MASTER_PROTECT_KEY);
+
+	return e;
+}
+
+static void clocks_init(struct device *dev,
+			struct twl4030_clock_init_data *clock)
+{
+	int e = 0;
+	struct clk *osc;
+	u32 rate;
+	u8 ctrl = HFCLK_FREQ_26_MHZ;
+
+#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3)
+	if (cpu_is_omap2430())
+		osc = clk_get(dev, "osc_ck");
+	else
+		osc = clk_get(dev, "osc_sys_ck");
+
+	if (IS_ERR(osc)) {
+		printk(KERN_WARNING "Skipping twl internal clock init and "
+				"using bootloader value (unknown osc rate)\n");
+		return;
+	}
+
+	rate = clk_get_rate(osc);
+	clk_put(osc);
+
+#else
+	/* REVISIT for non-OMAP systems, pass the clock rate from
+	 * board init code, using platform_data.
+	 */
+	osc = ERR_PTR(-EIO);
+
+	printk(KERN_WARNING "Skipping twl internal clock init and "
+	       "using bootloader value (unknown osc rate)\n");
+
+	return;
+#endif
+
+	switch (rate) {
+	case 19200000:
+		ctrl = HFCLK_FREQ_19p2_MHZ;
+		break;
+	case 26000000:
+		ctrl = HFCLK_FREQ_26_MHZ;
+		break;
+	case 38400000:
+		ctrl = HFCLK_FREQ_38p4_MHZ;
+		break;
+	}
+
+	ctrl |= HIGH_PERF_SQ;
+	if (clock && clock->ck32k_lowpwr_enable)
+		ctrl |= CK32K_LOWPWR_EN;
+
+	e |= unprotect_pm_master();
+	/* effect->MADC+USB ck en */
+	e |= twl_i2c_write_u8(TWL_MODULE_PM_MASTER, ctrl, R_CFG_BOOT);
+	e |= protect_pm_master();
+
+	if (e < 0)
+		pr_err("%s: clock init err [%d]\n", DRIVER_NAME, e);
+}
+
+/*----------------------------------------------------------------------*/
+
+
+static int twl_remove(struct i2c_client *client)
+{
+	unsigned i, num_slaves;
+	int status;
+
+	if (twl_class_is_4030()) {
+		status = twl4030_exit_irq();
+		num_slaves = TWL_NUM_SLAVES;
+	} else {
+		status = twl6030_exit_irq();
+		num_slaves = TWL_NUM_SLAVES - 1;
+	}
+
+	if (status < 0)
+		return status;
+
+	for (i = 0; i < num_slaves; i++) {
+		struct twl_client	*twl = &twl_modules[i];
+
+		if (twl->client && twl->client != client)
+			i2c_unregister_device(twl->client);
+		twl_modules[i].client = NULL;
+	}
+	inuse = false;
+	return 0;
+}
+
+/* NOTE: This driver only handles a single twl4030/tps659x0 chip */
+static int __devinit
+twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+	struct twl4030_platform_data	*pdata = client->dev.platform_data;
+	struct device_node		*node = client->dev.of_node;
+	int				irq_base = 0;
+	int				status;
+	unsigned			i, num_slaves;
+
+	if (node && !pdata) {
+		/*
+		 * XXX: Temporary pdata until the information is correctly
+		 * retrieved by every TWL modules from DT.
+		 */
+		pdata = devm_kzalloc(&client->dev,
+				     sizeof(struct twl4030_platform_data),
+				     GFP_KERNEL);
+		if (!pdata)
+			return -ENOMEM;
+	}
+
+	if (!pdata) {
+		dev_dbg(&client->dev, "no platform data?\n");
+		return -EINVAL;
+	}
+
+	if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) == 0) {
+		dev_dbg(&client->dev, "can't talk I2C?\n");
+		return -EIO;
+	}
+
+	if (inuse) {
+		dev_dbg(&client->dev, "driver is already in use\n");
+		return -EBUSY;
+	}
+
+	if ((id->driver_data) & TWL6030_CLASS) {
+		twl_id = TWL6030_CLASS_ID;
+		twl_map = &twl6030_map[0];
+		num_slaves = TWL_NUM_SLAVES - 1;
+	} else {
+		twl_id = TWL4030_CLASS_ID;
+		twl_map = &twl4030_map[0];
+		num_slaves = TWL_NUM_SLAVES;
+	}
+
+	for (i = 0; i < num_slaves; i++) {
+		struct twl_client *twl = &twl_modules[i];
+
+		twl->address = client->addr + i;
+		if (i == 0) {
+			twl->client = client;
+		} else {
+			twl->client = i2c_new_dummy(client->adapter,
+					twl->address);
+			if (!twl->client) {
+				dev_err(&client->dev,
+					"can't attach client %d\n", i);
+				status = -ENOMEM;
+				goto fail;
+			}
+		}
+		mutex_init(&twl->xfer_lock);
+	}
+
+	inuse = true;
+
+	/* setup clock framework */
+	clocks_init(&client->dev, pdata->clock);
+
+	/* read TWL IDCODE Register */
+	if (twl_id == TWL4030_CLASS_ID) {
+		status = twl_read_idcode_register();
+		WARN(status < 0, "Error: reading twl_idcode register value\n");
+	}
+
+	/* load power event scripts */
+	if (twl_has_power() && pdata->power)
+		twl4030_power_init(pdata->power);
+
+	/* Maybe init the T2 Interrupt subsystem */
+	if (client->irq) {
+		if (twl_class_is_4030()) {
+			twl4030_init_chip_irq(id->name);
+			irq_base = twl4030_init_irq(&client->dev, client->irq);
+		} else {
+			irq_base = twl6030_init_irq(&client->dev, client->irq);
+		}
+
+		if (irq_base < 0) {
+			status = irq_base;
+			goto fail;
+		}
+	}
+
+	/*
+	 * Disable TWL4030/TWL5030 I2C Pull-up on I2C1 and I2C4(SR) interface.
+	 * Program I2C_SCL_CTRL_PU(bit 0)=0, I2C_SDA_CTRL_PU (bit 2)=0,
+	 * SR_I2C_SCL_CTRL_PU(bit 4)=0 and SR_I2C_SDA_CTRL_PU(bit 6)=0.
+	 */
+	if (twl_class_is_4030()) {
+		u8 temp;
+
+		twl_i2c_read_u8(TWL4030_MODULE_INTBR, &temp, REG_GPPUPDCTR1);
+		temp &= ~(SR_I2C_SDA_CTRL_PU | SR_I2C_SCL_CTRL_PU | \
+			I2C_SDA_CTRL_PU | I2C_SCL_CTRL_PU);
+		twl_i2c_write_u8(TWL4030_MODULE_INTBR, temp, REG_GPPUPDCTR1);
+	}
+
+	status = -ENODEV;
+	if (node)
+		status = of_platform_populate(node, NULL, NULL, &client->dev);
+	if (status)
+		status = add_children(pdata, irq_base, id->driver_data);
+
+fail:
+	if (status < 0)
+		twl_remove(client);
+
+	return status;
+}
+
+static const struct i2c_device_id twl_ids[] = {
+	{ "twl4030", TWL4030_VAUX2 },	/* "Triton 2" */
+	{ "twl5030", 0 },		/* T2 updated */
+	{ "twl5031", TWL5031 },		/* TWL5030 updated */
+	{ "tps65950", 0 },		/* catalog version of twl5030 */
+	{ "tps65930", TPS_SUBSET },	/* fewer LDOs and DACs; no charger */
+	{ "tps65920", TPS_SUBSET },	/* fewer LDOs; no codec or charger */
+	{ "tps65921", TPS_SUBSET },	/* fewer LDOs; no codec, no LED
+					   and vibrator. Charger in USB module*/
+	{ "twl6030", TWL6030_CLASS },	/* "Phoenix power chip" */
+	{ "twl6025", TWL6030_CLASS | TWL6025_SUBCLASS }, /* "Phoenix lite" */
+	{ /* end of list */ },
+};
+MODULE_DEVICE_TABLE(i2c, twl_ids);
+
+/* One Client Driver , 4 Clients */
+static struct i2c_driver twl_driver = {
+	.driver.name	= DRIVER_NAME,
+	.id_table	= twl_ids,
+	.probe		= twl_probe,
+	.remove		= twl_remove,
+};
+
+static int __init twl_init(void)
+{
+	return i2c_add_driver(&twl_driver);
+}
+subsys_initcall(twl_init);
+
+static void __exit twl_exit(void)
+{
+	i2c_del_driver(&twl_driver);
+}
+module_exit(twl_exit);
+
+MODULE_AUTHOR("Texas Instruments, Inc.");
+MODULE_DESCRIPTION("I2C Core interface for TWL");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/twl-core.h b/ap/os/linux/linux-3.4.x/drivers/mfd/twl-core.h
new file mode 100644
index 0000000..6ff99dc
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/twl-core.h
@@ -0,0 +1,10 @@
+#ifndef __TWL_CORE_H__
+#define __TWL_CORE_H__
+
+extern int twl6030_init_irq(struct device *dev, int irq_num);
+extern int twl6030_exit_irq(void);
+extern int twl4030_init_irq(struct device *dev, int irq_num);
+extern int twl4030_exit_irq(void);
+extern int twl4030_init_chip_irq(const char *chip);
+
+#endif /*  __TWL_CORE_H__ */
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/twl4030-audio.c b/ap/os/linux/linux-3.4.x/drivers/mfd/twl4030-audio.c
new file mode 100644
index 0000000..838ce4e
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/twl4030-audio.c
@@ -0,0 +1,267 @@
+/*
+ * MFD driver for twl4030 audio submodule, which contains an audio codec, and
+ * the vibra control.
+ *
+ * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
+ *
+ * Copyright:   (C) 2009 Nokia Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/platform_device.h>
+#include <linux/i2c/twl.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/twl4030-audio.h>
+
+#define TWL4030_AUDIO_CELLS	2
+
+static struct platform_device *twl4030_audio_dev;
+
+struct twl4030_audio_resource {
+	int request_count;
+	u8 reg;
+	u8 mask;
+};
+
+struct twl4030_audio {
+	unsigned int audio_mclk;
+	struct mutex mutex;
+	struct twl4030_audio_resource resource[TWL4030_AUDIO_RES_MAX];
+	struct mfd_cell cells[TWL4030_AUDIO_CELLS];
+};
+
+/*
+ * Modify the resource, the function returns the content of the register
+ * after the modification.
+ */
+static int twl4030_audio_set_resource(enum twl4030_audio_res id, int enable)
+{
+	struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev);
+	u8 val;
+
+	twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val,
+			audio->resource[id].reg);
+
+	if (enable)
+		val |= audio->resource[id].mask;
+	else
+		val &= ~audio->resource[id].mask;
+
+	twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
+					val, audio->resource[id].reg);
+
+	return val;
+}
+
+static inline int twl4030_audio_get_resource(enum twl4030_audio_res id)
+{
+	struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev);
+	u8 val;
+
+	twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val,
+			audio->resource[id].reg);
+
+	return val;
+}
+
+/*
+ * Enable the resource.
+ * The function returns with error or the content of the register
+ */
+int twl4030_audio_enable_resource(enum twl4030_audio_res id)
+{
+	struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev);
+	int val;
+
+	if (id >= TWL4030_AUDIO_RES_MAX) {
+		dev_err(&twl4030_audio_dev->dev,
+				"Invalid resource ID (%u)\n", id);
+		return -EINVAL;
+	}
+
+	mutex_lock(&audio->mutex);
+	if (!audio->resource[id].request_count)
+		/* Resource was disabled, enable it */
+		val = twl4030_audio_set_resource(id, 1);
+	else
+		val = twl4030_audio_get_resource(id);
+
+	audio->resource[id].request_count++;
+	mutex_unlock(&audio->mutex);
+
+	return val;
+}
+EXPORT_SYMBOL_GPL(twl4030_audio_enable_resource);
+
+/*
+ * Disable the resource.
+ * The function returns with error or the content of the register
+ */
+int twl4030_audio_disable_resource(unsigned id)
+{
+	struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev);
+	int val;
+
+	if (id >= TWL4030_AUDIO_RES_MAX) {
+		dev_err(&twl4030_audio_dev->dev,
+				"Invalid resource ID (%u)\n", id);
+		return -EINVAL;
+	}
+
+	mutex_lock(&audio->mutex);
+	if (!audio->resource[id].request_count) {
+		dev_err(&twl4030_audio_dev->dev,
+			"Resource has been disabled already (%u)\n", id);
+		mutex_unlock(&audio->mutex);
+		return -EPERM;
+	}
+	audio->resource[id].request_count--;
+
+	if (!audio->resource[id].request_count)
+		/* Resource can be disabled now */
+		val = twl4030_audio_set_resource(id, 0);
+	else
+		val = twl4030_audio_get_resource(id);
+
+	mutex_unlock(&audio->mutex);
+
+	return val;
+}
+EXPORT_SYMBOL_GPL(twl4030_audio_disable_resource);
+
+unsigned int twl4030_audio_get_mclk(void)
+{
+	struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev);
+
+	return audio->audio_mclk;
+}
+EXPORT_SYMBOL_GPL(twl4030_audio_get_mclk);
+
+static int __devinit twl4030_audio_probe(struct platform_device *pdev)
+{
+	struct twl4030_audio *audio;
+	struct twl4030_audio_data *pdata = pdev->dev.platform_data;
+	struct mfd_cell *cell = NULL;
+	int ret, childs = 0;
+	u8 val;
+
+	if (!pdata) {
+		dev_err(&pdev->dev, "Platform data is missing\n");
+		return -EINVAL;
+	}
+
+	/* Configure APLL_INFREQ and disable APLL if enabled */
+	val = 0;
+	switch (pdata->audio_mclk) {
+	case 19200000:
+		val |= TWL4030_APLL_INFREQ_19200KHZ;
+		break;
+	case 26000000:
+		val |= TWL4030_APLL_INFREQ_26000KHZ;
+		break;
+	case 38400000:
+		val |= TWL4030_APLL_INFREQ_38400KHZ;
+		break;
+	default:
+		dev_err(&pdev->dev, "Invalid audio_mclk\n");
+		return -EINVAL;
+	}
+	twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
+					val, TWL4030_REG_APLL_CTL);
+
+	audio = kzalloc(sizeof(struct twl4030_audio), GFP_KERNEL);
+	if (!audio)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, audio);
+
+	twl4030_audio_dev = pdev;
+	mutex_init(&audio->mutex);
+	audio->audio_mclk = pdata->audio_mclk;
+
+	/* Codec power */
+	audio->resource[TWL4030_AUDIO_RES_POWER].reg = TWL4030_REG_CODEC_MODE;
+	audio->resource[TWL4030_AUDIO_RES_POWER].mask = TWL4030_CODECPDZ;
+
+	/* PLL */
+	audio->resource[TWL4030_AUDIO_RES_APLL].reg = TWL4030_REG_APLL_CTL;
+	audio->resource[TWL4030_AUDIO_RES_APLL].mask = TWL4030_APLL_EN;
+
+	if (pdata->codec) {
+		cell = &audio->cells[childs];
+		cell->name = "twl4030-codec";
+		cell->platform_data = pdata->codec;
+		cell->pdata_size = sizeof(*pdata->codec);
+		childs++;
+	}
+	if (pdata->vibra) {
+		cell = &audio->cells[childs];
+		cell->name = "twl4030-vibra";
+		cell->platform_data = pdata->vibra;
+		cell->pdata_size = sizeof(*pdata->vibra);
+		childs++;
+	}
+
+	if (childs)
+		ret = mfd_add_devices(&pdev->dev, pdev->id, audio->cells,
+				      childs, NULL, 0);
+	else {
+		dev_err(&pdev->dev, "No platform data found for childs\n");
+		ret = -ENODEV;
+	}
+
+	if (!ret)
+		return 0;
+
+	platform_set_drvdata(pdev, NULL);
+	kfree(audio);
+	twl4030_audio_dev = NULL;
+	return ret;
+}
+
+static int __devexit twl4030_audio_remove(struct platform_device *pdev)
+{
+	struct twl4030_audio *audio = platform_get_drvdata(pdev);
+
+	mfd_remove_devices(&pdev->dev);
+	platform_set_drvdata(pdev, NULL);
+	kfree(audio);
+	twl4030_audio_dev = NULL;
+
+	return 0;
+}
+
+MODULE_ALIAS("platform:twl4030-audio");
+
+static struct platform_driver twl4030_audio_driver = {
+	.probe		= twl4030_audio_probe,
+	.remove		= __devexit_p(twl4030_audio_remove),
+	.driver		= {
+		.owner	= THIS_MODULE,
+		.name	= "twl4030-audio",
+	},
+};
+
+module_platform_driver(twl4030_audio_driver);
+
+MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/twl4030-irq.c b/ap/os/linux/linux-3.4.x/drivers/mfd/twl4030-irq.c
new file mode 100644
index 0000000..5d656e8
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/twl4030-irq.c
@@ -0,0 +1,794 @@
+/*
+ * twl4030-irq.c - TWL4030/TPS659x0 irq support
+ *
+ * Copyright (C) 2005-2006 Texas Instruments, Inc.
+ *
+ * Modifications to defer interrupt handling to a kernel thread:
+ * Copyright (C) 2006 MontaVista Software, Inc.
+ *
+ * Based on tlv320aic23.c:
+ * Copyright (c) by Kai Svahn <kai.svahn@nokia.com>
+ *
+ * Code cleanup and modifications to IRQ handler.
+ * by syed khasim <x0khasim@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/export.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/irqdomain.h>
+#include <linux/i2c/twl.h>
+
+#include "twl-core.h"
+
+/*
+ * TWL4030 IRQ handling has two stages in hardware, and thus in software.
+ * The Primary Interrupt Handler (PIH) stage exposes status bits saying
+ * which Secondary Interrupt Handler (SIH) stage is raising an interrupt.
+ * SIH modules are more traditional IRQ components, which support per-IRQ
+ * enable/disable and trigger controls; they do most of the work.
+ *
+ * These chips are designed to support IRQ handling from two different
+ * I2C masters.  Each has a dedicated IRQ line, and dedicated IRQ status
+ * and mask registers in the PIH and SIH modules.
+ *
+ * We set up IRQs starting at a platform-specified base, always starting
+ * with PIH and the SIH for PWR_INT and then usually adding GPIO:
+ *	base + 0  .. base + 7	PIH
+ *	base + 8  .. base + 15	SIH for PWR_INT
+ *	base + 16 .. base + 33	SIH for GPIO
+ */
+#define TWL4030_CORE_NR_IRQS	8
+#define TWL4030_PWR_NR_IRQS	8
+
+/* PIH register offsets */
+#define REG_PIH_ISR_P1			0x01
+#define REG_PIH_ISR_P2			0x02
+#define REG_PIH_SIR			0x03	/* for testing */
+
+/* Linux could (eventually) use either IRQ line */
+static int irq_line;
+
+struct sih {
+	char	name[8];
+	u8	module;			/* module id */
+	u8	control_offset;		/* for SIH_CTRL */
+	bool	set_cor;
+
+	u8	bits;			/* valid in isr/imr */
+	u8	bytes_ixr;		/* bytelen of ISR/IMR/SIR */
+
+	u8	edr_offset;
+	u8	bytes_edr;		/* bytelen of EDR */
+
+	u8	irq_lines;		/* number of supported irq lines */
+
+	/* SIR ignored -- set interrupt, for testing only */
+	struct sih_irq_data {
+		u8	isr_offset;
+		u8	imr_offset;
+	} mask[2];
+	/* + 2 bytes padding */
+};
+
+static const struct sih *sih_modules;
+static int nr_sih_modules;
+
+#define SIH_INITIALIZER(modname, nbits) \
+	.module		= TWL4030_MODULE_ ## modname, \
+	.control_offset = TWL4030_ ## modname ## _SIH_CTRL, \
+	.bits		= nbits, \
+	.bytes_ixr	= DIV_ROUND_UP(nbits, 8), \
+	.edr_offset	= TWL4030_ ## modname ## _EDR, \
+	.bytes_edr	= DIV_ROUND_UP((2*(nbits)), 8), \
+	.irq_lines	= 2, \
+	.mask = { { \
+		.isr_offset	= TWL4030_ ## modname ## _ISR1, \
+		.imr_offset	= TWL4030_ ## modname ## _IMR1, \
+	}, \
+	{ \
+		.isr_offset	= TWL4030_ ## modname ## _ISR2, \
+		.imr_offset	= TWL4030_ ## modname ## _IMR2, \
+	}, },
+
+/* register naming policies are inconsistent ... */
+#define TWL4030_INT_PWR_EDR		TWL4030_INT_PWR_EDR1
+#define TWL4030_MODULE_KEYPAD_KEYP	TWL4030_MODULE_KEYPAD
+#define TWL4030_MODULE_INT_PWR		TWL4030_MODULE_INT
+
+
+/*
+ * Order in this table matches order in PIH_ISR.  That is,
+ * BIT(n) in PIH_ISR is sih_modules[n].
+ */
+/* sih_modules_twl4030 is used both in twl4030 and twl5030 */
+static const struct sih sih_modules_twl4030[6] = {
+	[0] = {
+		.name		= "gpio",
+		.module		= TWL4030_MODULE_GPIO,
+		.control_offset	= REG_GPIO_SIH_CTRL,
+		.set_cor	= true,
+		.bits		= TWL4030_GPIO_MAX,
+		.bytes_ixr	= 3,
+		/* Note: *all* of these IRQs default to no-trigger */
+		.edr_offset	= REG_GPIO_EDR1,
+		.bytes_edr	= 5,
+		.irq_lines	= 2,
+		.mask = { {
+			.isr_offset	= REG_GPIO_ISR1A,
+			.imr_offset	= REG_GPIO_IMR1A,
+		}, {
+			.isr_offset	= REG_GPIO_ISR1B,
+			.imr_offset	= REG_GPIO_IMR1B,
+		}, },
+	},
+	[1] = {
+		.name		= "keypad",
+		.set_cor	= true,
+		SIH_INITIALIZER(KEYPAD_KEYP, 4)
+	},
+	[2] = {
+		.name		= "bci",
+		.module		= TWL4030_MODULE_INTERRUPTS,
+		.control_offset	= TWL4030_INTERRUPTS_BCISIHCTRL,
+		.set_cor	= true,
+		.bits		= 12,
+		.bytes_ixr	= 2,
+		.edr_offset	= TWL4030_INTERRUPTS_BCIEDR1,
+		/* Note: most of these IRQs default to no-trigger */
+		.bytes_edr	= 3,
+		.irq_lines	= 2,
+		.mask = { {
+			.isr_offset	= TWL4030_INTERRUPTS_BCIISR1A,
+			.imr_offset	= TWL4030_INTERRUPTS_BCIIMR1A,
+		}, {
+			.isr_offset	= TWL4030_INTERRUPTS_BCIISR1B,
+			.imr_offset	= TWL4030_INTERRUPTS_BCIIMR1B,
+		}, },
+	},
+	[3] = {
+		.name		= "madc",
+		SIH_INITIALIZER(MADC, 4)
+	},
+	[4] = {
+		/* USB doesn't use the same SIH organization */
+		.name		= "usb",
+	},
+	[5] = {
+		.name		= "power",
+		.set_cor	= true,
+		SIH_INITIALIZER(INT_PWR, 8)
+	},
+		/* there are no SIH modules #6 or #7 ... */
+};
+
+static const struct sih sih_modules_twl5031[8] = {
+	[0] = {
+		.name		= "gpio",
+		.module		= TWL4030_MODULE_GPIO,
+		.control_offset	= REG_GPIO_SIH_CTRL,
+		.set_cor	= true,
+		.bits		= TWL4030_GPIO_MAX,
+		.bytes_ixr	= 3,
+		/* Note: *all* of these IRQs default to no-trigger */
+		.edr_offset	= REG_GPIO_EDR1,
+		.bytes_edr	= 5,
+		.irq_lines	= 2,
+		.mask = { {
+			.isr_offset	= REG_GPIO_ISR1A,
+			.imr_offset	= REG_GPIO_IMR1A,
+		}, {
+			.isr_offset	= REG_GPIO_ISR1B,
+			.imr_offset	= REG_GPIO_IMR1B,
+		}, },
+	},
+	[1] = {
+		.name		= "keypad",
+		.set_cor	= true,
+		SIH_INITIALIZER(KEYPAD_KEYP, 4)
+	},
+	[2] = {
+		.name		= "bci",
+		.module		= TWL5031_MODULE_INTERRUPTS,
+		.control_offset	= TWL5031_INTERRUPTS_BCISIHCTRL,
+		.bits		= 7,
+		.bytes_ixr	= 1,
+		.edr_offset	= TWL5031_INTERRUPTS_BCIEDR1,
+		/* Note: most of these IRQs default to no-trigger */
+		.bytes_edr	= 2,
+		.irq_lines	= 2,
+		.mask = { {
+			.isr_offset	= TWL5031_INTERRUPTS_BCIISR1,
+			.imr_offset	= TWL5031_INTERRUPTS_BCIIMR1,
+		}, {
+			.isr_offset	= TWL5031_INTERRUPTS_BCIISR2,
+			.imr_offset	= TWL5031_INTERRUPTS_BCIIMR2,
+		}, },
+	},
+	[3] = {
+		.name		= "madc",
+		SIH_INITIALIZER(MADC, 4)
+	},
+	[4] = {
+		/* USB doesn't use the same SIH organization */
+		.name		= "usb",
+	},
+	[5] = {
+		.name		= "power",
+		.set_cor	= true,
+		SIH_INITIALIZER(INT_PWR, 8)
+	},
+	[6] = {
+		/*
+		 * ECI/DBI doesn't use the same SIH organization.
+		 * For example, it supports only one interrupt output line.
+		 * That is, the interrupts are seen on both INT1 and INT2 lines.
+		 */
+		.name		= "eci_dbi",
+		.module		= TWL5031_MODULE_ACCESSORY,
+		.bits		= 9,
+		.bytes_ixr	= 2,
+		.irq_lines	= 1,
+		.mask = { {
+			.isr_offset	= TWL5031_ACIIDR_LSB,
+			.imr_offset	= TWL5031_ACIIMR_LSB,
+		}, },
+
+	},
+	[7] = {
+		/* Audio accessory */
+		.name		= "audio",
+		.module		= TWL5031_MODULE_ACCESSORY,
+		.control_offset	= TWL5031_ACCSIHCTRL,
+		.bits		= 2,
+		.bytes_ixr	= 1,
+		.edr_offset	= TWL5031_ACCEDR1,
+		/* Note: most of these IRQs default to no-trigger */
+		.bytes_edr	= 1,
+		.irq_lines	= 2,
+		.mask = { {
+			.isr_offset	= TWL5031_ACCISR1,
+			.imr_offset	= TWL5031_ACCIMR1,
+		}, {
+			.isr_offset	= TWL5031_ACCISR2,
+			.imr_offset	= TWL5031_ACCIMR2,
+		}, },
+	},
+};
+
+#undef TWL4030_MODULE_KEYPAD_KEYP
+#undef TWL4030_MODULE_INT_PWR
+#undef TWL4030_INT_PWR_EDR
+
+/*----------------------------------------------------------------------*/
+
+static unsigned twl4030_irq_base;
+
+/*
+ * handle_twl4030_pih() is the desc->handle method for the twl4030 interrupt.
+ * This is a chained interrupt, so there is no desc->action method for it.
+ * Now we need to query the interrupt controller in the twl4030 to determine
+ * which module is generating the interrupt request.  However, we can't do i2c
+ * transactions in interrupt context, so we must defer that work to a kernel
+ * thread.  All we do here is acknowledge and mask the interrupt and wakeup
+ * the kernel thread.
+ */
+static irqreturn_t handle_twl4030_pih(int irq, void *devid)
+{
+	irqreturn_t	ret;
+	u8		pih_isr;
+
+	ret = twl_i2c_read_u8(TWL4030_MODULE_PIH, &pih_isr,
+			REG_PIH_ISR_P1);
+	if (ret) {
+		pr_warning("twl4030: I2C error %d reading PIH ISR\n", ret);
+		return IRQ_NONE;
+	}
+
+	while (pih_isr) {
+		unsigned long	pending = __ffs(pih_isr);
+		unsigned int	irq;
+
+		pih_isr &= ~BIT(pending);
+		irq = pending + twl4030_irq_base;
+		handle_nested_irq(irq);
+	}
+
+	return IRQ_HANDLED;
+}
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * twl4030_init_sih_modules() ... start from a known state where no
+ * IRQs will be coming in, and where we can quickly enable them then
+ * handle them as they arrive.  Mask all IRQs: maybe init SIH_CTRL.
+ *
+ * NOTE:  we don't touch EDR registers here; they stay with hardware
+ * defaults or whatever the last value was.  Note that when both EDR
+ * bits for an IRQ are clear, that's as if its IMR bit is set...
+ */
+static int twl4030_init_sih_modules(unsigned line)
+{
+	const struct sih *sih;
+	u8 buf[4];
+	int i;
+	int status;
+
+	/* line 0 == int1_n signal; line 1 == int2_n signal */
+	if (line > 1)
+		return -EINVAL;
+
+	irq_line = line;
+
+	/* disable all interrupts on our line */
+	memset(buf, 0xff, sizeof buf);
+	sih = sih_modules;
+	for (i = 0; i < nr_sih_modules; i++, sih++) {
+		/* skip USB -- it's funky */
+		if (!sih->bytes_ixr)
+			continue;
+
+		/* Not all the SIH modules support multiple interrupt lines */
+		if (sih->irq_lines <= line)
+			continue;
+
+		status = twl_i2c_write(sih->module, buf,
+				sih->mask[line].imr_offset, sih->bytes_ixr);
+		if (status < 0)
+			pr_err("twl4030: err %d initializing %s %s\n",
+					status, sih->name, "IMR");
+
+		/*
+		 * Maybe disable "exclusive" mode; buffer second pending irq;
+		 * set Clear-On-Read (COR) bit.
+		 *
+		 * NOTE that sometimes COR polarity is documented as being
+		 * inverted:  for MADC, COR=1 means "clear on write".
+		 * And for PWR_INT it's not documented...
+		 */
+		if (sih->set_cor) {
+			status = twl_i2c_write_u8(sih->module,
+					TWL4030_SIH_CTRL_COR_MASK,
+					sih->control_offset);
+			if (status < 0)
+				pr_err("twl4030: err %d initializing %s %s\n",
+						status, sih->name, "SIH_CTRL");
+		}
+	}
+
+	sih = sih_modules;
+	for (i = 0; i < nr_sih_modules; i++, sih++) {
+		u8 rxbuf[4];
+		int j;
+
+		/* skip USB */
+		if (!sih->bytes_ixr)
+			continue;
+
+		/* Not all the SIH modules support multiple interrupt lines */
+		if (sih->irq_lines <= line)
+			continue;
+
+		/*
+		 * Clear pending interrupt status.  Either the read was
+		 * enough, or we need to write those bits.  Repeat, in
+		 * case an IRQ is pending (PENDDIS=0) ... that's not
+		 * uncommon with PWR_INT.PWRON.
+		 */
+		for (j = 0; j < 2; j++) {
+			status = twl_i2c_read(sih->module, rxbuf,
+				sih->mask[line].isr_offset, sih->bytes_ixr);
+			if (status < 0)
+				pr_err("twl4030: err %d initializing %s %s\n",
+					status, sih->name, "ISR");
+
+			if (!sih->set_cor)
+				status = twl_i2c_write(sih->module, buf,
+					sih->mask[line].isr_offset,
+					sih->bytes_ixr);
+			/*
+			 * else COR=1 means read sufficed.
+			 * (for most SIH modules...)
+			 */
+		}
+	}
+
+	return 0;
+}
+
+static inline void activate_irq(int irq)
+{
+#ifdef CONFIG_ARM
+	/*
+	 * ARM requires an extra step to clear IRQ_NOREQUEST, which it
+	 * sets on behalf of every irq_chip.  Also sets IRQ_NOPROBE.
+	 */
+	set_irq_flags(irq, IRQF_VALID);
+#else
+	/* same effect on other architectures */
+	irq_set_noprobe(irq);
+#endif
+}
+
+/*----------------------------------------------------------------------*/
+
+struct sih_agent {
+	int			irq_base;
+	const struct sih	*sih;
+
+	u32			imr;
+	bool			imr_change_pending;
+
+	u32			edge_change;
+
+	struct mutex		irq_lock;
+	char			*irq_name;
+};
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * All irq_chip methods get issued from code holding irq_desc[irq].lock,
+ * which can't perform the underlying I2C operations (because they sleep).
+ * So we must hand them off to a thread (workqueue) and cope with asynch
+ * completion, potentially including some re-ordering, of these requests.
+ */
+
+static void twl4030_sih_mask(struct irq_data *data)
+{
+	struct sih_agent *agent = irq_data_get_irq_chip_data(data);
+
+	agent->imr |= BIT(data->irq - agent->irq_base);
+	agent->imr_change_pending = true;
+}
+
+static void twl4030_sih_unmask(struct irq_data *data)
+{
+	struct sih_agent *agent = irq_data_get_irq_chip_data(data);
+
+	agent->imr &= ~BIT(data->irq - agent->irq_base);
+	agent->imr_change_pending = true;
+}
+
+static int twl4030_sih_set_type(struct irq_data *data, unsigned trigger)
+{
+	struct sih_agent *agent = irq_data_get_irq_chip_data(data);
+
+	if (trigger & ~(IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
+		return -EINVAL;
+
+	if (irqd_get_trigger_type(data) != trigger)
+		agent->edge_change |= BIT(data->irq - agent->irq_base);
+
+	return 0;
+}
+
+static void twl4030_sih_bus_lock(struct irq_data *data)
+{
+	struct sih_agent	*agent = irq_data_get_irq_chip_data(data);
+
+	mutex_lock(&agent->irq_lock);
+}
+
+static void twl4030_sih_bus_sync_unlock(struct irq_data *data)
+{
+	struct sih_agent	*agent = irq_data_get_irq_chip_data(data);
+	const struct sih	*sih = agent->sih;
+	int			status;
+
+	if (agent->imr_change_pending) {
+		union {
+			u32	word;
+			u8	bytes[4];
+		} imr;
+
+		/* byte[0] gets overwritten as we write ... */
+		imr.word = cpu_to_le32(agent->imr << 8);
+		agent->imr_change_pending = false;
+
+		/* write the whole mask ... simpler than subsetting it */
+		status = twl_i2c_write(sih->module, imr.bytes,
+				sih->mask[irq_line].imr_offset,
+				sih->bytes_ixr);
+		if (status)
+			pr_err("twl4030: %s, %s --> %d\n", __func__,
+					"write", status);
+	}
+
+	if (agent->edge_change) {
+		u32		edge_change;
+		u8		bytes[6];
+
+		edge_change = agent->edge_change;
+		agent->edge_change = 0;
+
+		/*
+		 * Read, reserving first byte for write scratch.  Yes, this
+		 * could be cached for some speedup ... but be careful about
+		 * any processor on the other IRQ line, EDR registers are
+		 * shared.
+		 */
+		status = twl_i2c_read(sih->module, bytes + 1,
+				sih->edr_offset, sih->bytes_edr);
+		if (status) {
+			pr_err("twl4030: %s, %s --> %d\n", __func__,
+					"read", status);
+			return;
+		}
+
+		/* Modify only the bits we know must change */
+		while (edge_change) {
+			int		i = fls(edge_change) - 1;
+			struct irq_data	*idata;
+			int		byte = 1 + (i >> 2);
+			int		off = (i & 0x3) * 2;
+			unsigned int	type;
+
+			idata = irq_get_irq_data(i + agent->irq_base);
+
+			bytes[byte] &= ~(0x03 << off);
+
+			type = irqd_get_trigger_type(idata);
+			if (type & IRQ_TYPE_EDGE_RISING)
+				bytes[byte] |= BIT(off + 1);
+			if (type & IRQ_TYPE_EDGE_FALLING)
+				bytes[byte] |= BIT(off + 0);
+
+			edge_change &= ~BIT(i);
+		}
+
+		/* Write */
+		status = twl_i2c_write(sih->module, bytes,
+				sih->edr_offset, sih->bytes_edr);
+		if (status)
+			pr_err("twl4030: %s, %s --> %d\n", __func__,
+					"write", status);
+	}
+
+	mutex_unlock(&agent->irq_lock);
+}
+
+static struct irq_chip twl4030_sih_irq_chip = {
+	.name		= "twl4030",
+	.irq_mask	= twl4030_sih_mask,
+	.irq_unmask	= twl4030_sih_unmask,
+	.irq_set_type	= twl4030_sih_set_type,
+	.irq_bus_lock	= twl4030_sih_bus_lock,
+	.irq_bus_sync_unlock = twl4030_sih_bus_sync_unlock,
+};
+
+/*----------------------------------------------------------------------*/
+
+static inline int sih_read_isr(const struct sih *sih)
+{
+	int status;
+	union {
+		u8 bytes[4];
+		u32 word;
+	} isr;
+
+	/* FIXME need retry-on-error ... */
+
+	isr.word = 0;
+	status = twl_i2c_read(sih->module, isr.bytes,
+			sih->mask[irq_line].isr_offset, sih->bytes_ixr);
+
+	return (status < 0) ? status : le32_to_cpu(isr.word);
+}
+
+/*
+ * Generic handler for SIH interrupts ... we "know" this is called
+ * in task context, with IRQs enabled.
+ */
+static irqreturn_t handle_twl4030_sih(int irq, void *data)
+{
+	struct sih_agent *agent = irq_get_handler_data(irq);
+	const struct sih *sih = agent->sih;
+	int isr;
+
+	/* reading ISR acks the IRQs, using clear-on-read mode */
+	isr = sih_read_isr(sih);
+
+	if (isr < 0) {
+		pr_err("twl4030: %s SIH, read ISR error %d\n",
+			sih->name, isr);
+		/* REVISIT:  recover; eventually mask it all, etc */
+		return IRQ_HANDLED;
+	}
+
+	while (isr) {
+		irq = fls(isr);
+		irq--;
+		isr &= ~BIT(irq);
+
+		if (irq < sih->bits)
+			handle_nested_irq(agent->irq_base + irq);
+		else
+			pr_err("twl4030: %s SIH, invalid ISR bit %d\n",
+				sih->name, irq);
+	}
+	return IRQ_HANDLED;
+}
+
+/* returns the first IRQ used by this SIH bank, or negative errno */
+int twl4030_sih_setup(struct device *dev, int module, int irq_base)
+{
+	int			sih_mod;
+	const struct sih	*sih = NULL;
+	struct sih_agent	*agent;
+	int			i, irq;
+	int			status = -EINVAL;
+
+	/* only support modules with standard clear-on-read for now */
+	for (sih_mod = 0, sih = sih_modules; sih_mod < nr_sih_modules;
+			sih_mod++, sih++) {
+		if (sih->module == module && sih->set_cor) {
+			status = 0;
+			break;
+		}
+	}
+
+	if (status < 0)
+		return status;
+
+	agent = kzalloc(sizeof *agent, GFP_KERNEL);
+	if (!agent)
+		return -ENOMEM;
+
+	agent->irq_base = irq_base;
+	agent->sih = sih;
+	agent->imr = ~0;
+	mutex_init(&agent->irq_lock);
+
+	for (i = 0; i < sih->bits; i++) {
+		irq = irq_base + i;
+
+		irq_set_chip_data(irq, agent);
+		irq_set_chip_and_handler(irq, &twl4030_sih_irq_chip,
+					 handle_edge_irq);
+		irq_set_nested_thread(irq, 1);
+		activate_irq(irq);
+	}
+
+	/* replace generic PIH handler (handle_simple_irq) */
+	irq = sih_mod + twl4030_irq_base;
+	irq_set_handler_data(irq, agent);
+	agent->irq_name = kasprintf(GFP_KERNEL, "twl4030_%s", sih->name);
+	status = request_threaded_irq(irq, NULL, handle_twl4030_sih, 0,
+				      agent->irq_name ?: sih->name, NULL);
+
+	dev_info(dev, "%s (irq %d) chaining IRQs %d..%d\n", sih->name,
+			irq, irq_base, irq_base + i - 1);
+
+	return status < 0 ? status : irq_base;
+}
+
+/* FIXME need a call to reverse twl4030_sih_setup() ... */
+
+/*----------------------------------------------------------------------*/
+
+/* FIXME pass in which interrupt line we'll use ... */
+#define twl_irq_line	0
+
+int twl4030_init_irq(struct device *dev, int irq_num)
+{
+	static struct irq_chip	twl4030_irq_chip;
+	int			status, i;
+	int			irq_base, irq_end, nr_irqs;
+	struct			device_node *node = dev->of_node;
+
+	/*
+	 * TWL core and pwr interrupts must be contiguous because
+	 * the hwirqs numbers are defined contiguously from 1 to 15.
+	 * Create only one domain for both.
+	 */
+	nr_irqs = TWL4030_PWR_NR_IRQS + TWL4030_CORE_NR_IRQS;
+
+	irq_base = irq_alloc_descs(-1, 0, nr_irqs, 0);
+	if (IS_ERR_VALUE(irq_base)) {
+		dev_err(dev, "Fail to allocate IRQ descs\n");
+		return irq_base;
+	}
+
+	irq_domain_add_legacy(node, nr_irqs, irq_base, 0,
+			      &irq_domain_simple_ops, NULL);
+
+	irq_end = irq_base + TWL4030_CORE_NR_IRQS;
+
+	/*
+	 * Mask and clear all TWL4030 interrupts since initially we do
+	 * not have any TWL4030 module interrupt handlers present
+	 */
+	status = twl4030_init_sih_modules(twl_irq_line);
+	if (status < 0)
+		return status;
+
+	twl4030_irq_base = irq_base;
+
+	/*
+	 * Install an irq handler for each of the SIH modules;
+	 * clone dummy irq_chip since PIH can't *do* anything
+	 */
+	twl4030_irq_chip = dummy_irq_chip;
+	twl4030_irq_chip.name = "twl4030";
+
+	twl4030_sih_irq_chip.irq_ack = dummy_irq_chip.irq_ack;
+
+	for (i = irq_base; i < irq_end; i++) {
+		irq_set_chip_and_handler(i, &twl4030_irq_chip,
+					 handle_simple_irq);
+		irq_set_nested_thread(i, 1);
+		activate_irq(i);
+	}
+
+	dev_info(dev, "%s (irq %d) chaining IRQs %d..%d\n", "PIH",
+			irq_num, irq_base, irq_end);
+
+	/* ... and the PWR_INT module ... */
+	status = twl4030_sih_setup(dev, TWL4030_MODULE_INT, irq_end);
+	if (status < 0) {
+		dev_err(dev, "sih_setup PWR INT --> %d\n", status);
+		goto fail;
+	}
+
+	/* install an irq handler to demultiplex the TWL4030 interrupt */
+	status = request_threaded_irq(irq_num, NULL, handle_twl4030_pih,
+				      IRQF_ONESHOT,
+				      "TWL4030-PIH", NULL);
+	if (status < 0) {
+		dev_err(dev, "could not claim irq%d: %d\n", irq_num, status);
+		goto fail_rqirq;
+	}
+
+	return irq_base;
+fail_rqirq:
+	/* clean up twl4030_sih_setup */
+fail:
+	for (i = irq_base; i < irq_end; i++) {
+		irq_set_nested_thread(i, 0);
+		irq_set_chip_and_handler(i, NULL, NULL);
+	}
+
+	return status;
+}
+
+int twl4030_exit_irq(void)
+{
+	/* FIXME undo twl_init_irq() */
+	if (twl4030_irq_base) {
+		pr_err("twl4030: can't yet clean up IRQs?\n");
+		return -ENOSYS;
+	}
+	return 0;
+}
+
+int twl4030_init_chip_irq(const char *chip)
+{
+	if (!strcmp(chip, "twl5031")) {
+		sih_modules = sih_modules_twl5031;
+		nr_sih_modules = ARRAY_SIZE(sih_modules_twl5031);
+	} else {
+		sih_modules = sih_modules_twl4030;
+		nr_sih_modules = ARRAY_SIZE(sih_modules_twl4030);
+	}
+
+	return 0;
+}
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/twl4030-madc.c b/ap/os/linux/linux-3.4.x/drivers/mfd/twl4030-madc.c
new file mode 100644
index 0000000..456ecb5
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/twl4030-madc.c
@@ -0,0 +1,815 @@
+/*
+ *
+ * TWL4030 MADC module driver-This driver monitors the real time
+ * conversion of analog signals like battery temperature,
+ * battery type, battery level etc.
+ *
+ * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
+ * J Keerthy <j-keerthy@ti.com>
+ *
+ * Based on twl4030-madc.c
+ * Copyright (C) 2008 Nokia Corporation
+ * Mikko Ylinen <mikko.k.ylinen@nokia.com>
+ *
+ * Amit Kucheria <amit.kucheria@canonical.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/i2c/twl.h>
+#include <linux/i2c/twl4030-madc.h>
+#include <linux/module.h>
+#include <linux/stddef.h>
+#include <linux/mutex.h>
+#include <linux/bitops.h>
+#include <linux/jiffies.h>
+#include <linux/types.h>
+#include <linux/gfp.h>
+#include <linux/err.h>
+
+/*
+ * struct twl4030_madc_data - a container for madc info
+ * @dev - pointer to device structure for madc
+ * @lock - mutex protecting this data structure
+ * @requests - Array of request struct corresponding to SW1, SW2 and RT
+ * @imr - Interrupt mask register of MADC
+ * @isr - Interrupt status register of MADC
+ */
+struct twl4030_madc_data {
+	struct device *dev;
+	struct mutex lock;	/* mutex protecting this data structure */
+	struct twl4030_madc_request requests[TWL4030_MADC_NUM_METHODS];
+	int imr;
+	int isr;
+};
+
+static struct twl4030_madc_data *twl4030_madc;
+
+struct twl4030_prescale_divider_ratios {
+	s16 numerator;
+	s16 denominator;
+};
+
+static const struct twl4030_prescale_divider_ratios
+twl4030_divider_ratios[16] = {
+	{1, 1},		/* CHANNEL 0 No Prescaler */
+	{1, 1},		/* CHANNEL 1 No Prescaler */
+	{6, 10},	/* CHANNEL 2 */
+	{6, 10},	/* CHANNEL 3 */
+	{6, 10},	/* CHANNEL 4 */
+	{6, 10},	/* CHANNEL 5 */
+	{6, 10},	/* CHANNEL 6 */
+	{6, 10},	/* CHANNEL 7 */
+	{3, 14},	/* CHANNEL 8 */
+	{1, 3},		/* CHANNEL 9 */
+	{1, 1},		/* CHANNEL 10 No Prescaler */
+	{15, 100},	/* CHANNEL 11 */
+	{1, 4},		/* CHANNEL 12 */
+	{1, 1},		/* CHANNEL 13 Reserved channels */
+	{1, 1},		/* CHANNEL 14 Reseved channels */
+	{5, 11},	/* CHANNEL 15 */
+};
+
+
+/*
+ * Conversion table from -3 to 55 degree Celcius
+ */
+static int therm_tbl[] = {
+30800,	29500,	28300,	27100,
+26000,	24900,	23900,	22900,	22000,	21100,	20300,	19400,	18700,	17900,
+17200,	16500,	15900,	15300,	14700,	14100,	13600,	13100,	12600,	12100,
+11600,	11200,	10800,	10400,	10000,	9630,	9280,	8950,	8620,	8310,
+8020,	7730,	7460,	7200,	6950,	6710,	6470,	6250,	6040,	5830,
+5640,	5450,	5260,	5090,	4920,	4760,	4600,	4450,	4310,	4170,
+4040,	3910,	3790,	3670,	3550
+};
+
+/*
+ * Structure containing the registers
+ * of different conversion methods supported by MADC.
+ * Hardware or RT real time conversion request initiated by external host
+ * processor for RT Signal conversions.
+ * External host processors can also request for non RT conversions
+ * SW1 and SW2 software conversions also called asynchronous or GPC request.
+ */
+static
+const struct twl4030_madc_conversion_method twl4030_conversion_methods[] = {
+	[TWL4030_MADC_RT] = {
+			     .sel = TWL4030_MADC_RTSELECT_LSB,
+			     .avg = TWL4030_MADC_RTAVERAGE_LSB,
+			     .rbase = TWL4030_MADC_RTCH0_LSB,
+			     },
+	[TWL4030_MADC_SW1] = {
+			      .sel = TWL4030_MADC_SW1SELECT_LSB,
+			      .avg = TWL4030_MADC_SW1AVERAGE_LSB,
+			      .rbase = TWL4030_MADC_GPCH0_LSB,
+			      .ctrl = TWL4030_MADC_CTRL_SW1,
+			      },
+	[TWL4030_MADC_SW2] = {
+			      .sel = TWL4030_MADC_SW2SELECT_LSB,
+			      .avg = TWL4030_MADC_SW2AVERAGE_LSB,
+			      .rbase = TWL4030_MADC_GPCH0_LSB,
+			      .ctrl = TWL4030_MADC_CTRL_SW2,
+			      },
+};
+
+/*
+ * Function to read a particular channel value.
+ * @madc - pointer to struct twl4030_madc_data
+ * @reg - lsb of ADC Channel
+ * If the i2c read fails it returns an error else returns 0.
+ */
+static int twl4030_madc_channel_raw_read(struct twl4030_madc_data *madc, u8 reg)
+{
+	u8 msb, lsb;
+	int ret;
+	/*
+	 * For each ADC channel, we have MSB and LSB register pair. MSB address
+	 * is always LSB address+1. reg parameter is the address of LSB register
+	 */
+	ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &msb, reg + 1);
+	if (ret) {
+		dev_err(madc->dev, "unable to read MSB register 0x%X\n",
+			reg + 1);
+		return ret;
+	}
+	ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &lsb, reg);
+	if (ret) {
+		dev_err(madc->dev, "unable to read LSB register 0x%X\n", reg);
+		return ret;
+	}
+
+	return (int)(((msb << 8) | lsb) >> 6);
+}
+
+/*
+ * Return battery temperature
+ * Or < 0 on failure.
+ */
+static int twl4030battery_temperature(int raw_volt)
+{
+	u8 val;
+	int temp, curr, volt, res, ret;
+
+	volt = (raw_volt * TEMP_STEP_SIZE) / TEMP_PSR_R;
+	/* Getting and calculating the supply current in micro ampers */
+	ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &val,
+		REG_BCICTL2);
+	if (ret < 0)
+		return ret;
+	curr = ((val & TWL4030_BCI_ITHEN) + 1) * 10;
+	/* Getting and calculating the thermistor resistance in ohms */
+	res = volt * 1000 / curr;
+	/* calculating temperature */
+	for (temp = 58; temp >= 0; temp--) {
+		int actual = therm_tbl[temp];
+
+		if ((actual - res) >= 0)
+			break;
+	}
+
+	return temp + 1;
+}
+
+static int twl4030battery_current(int raw_volt)
+{
+	int ret;
+	u8 val;
+
+	ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &val,
+		TWL4030_BCI_BCICTL1);
+	if (ret)
+		return ret;
+	if (val & TWL4030_BCI_CGAIN) /* slope of 0.44 mV/mA */
+		return (raw_volt * CURR_STEP_SIZE) / CURR_PSR_R1;
+	else /* slope of 0.88 mV/mA */
+		return (raw_volt * CURR_STEP_SIZE) / CURR_PSR_R2;
+}
+/*
+ * Function to read channel values
+ * @madc - pointer to twl4030_madc_data struct
+ * @reg_base - Base address of the first channel
+ * @Channels - 16 bit bitmap. If the bit is set, channel value is read
+ * @buf - The channel values are stored here. if read fails error
+ * value is stored
+ * Returns the number of successfully read channels.
+ */
+static int twl4030_madc_read_channels(struct twl4030_madc_data *madc,
+				      u8 reg_base, unsigned
+						long channels, int *buf)
+{
+	int count = 0, count_req = 0, i;
+	u8 reg;
+
+	for_each_set_bit(i, &channels, TWL4030_MADC_MAX_CHANNELS) {
+		reg = reg_base + 2 * i;
+		buf[i] = twl4030_madc_channel_raw_read(madc, reg);
+		if (buf[i] < 0) {
+			dev_err(madc->dev,
+				"Unable to read register 0x%X\n", reg);
+			count_req++;
+			continue;
+		}
+		switch (i) {
+		case 10:
+			buf[i] = twl4030battery_current(buf[i]);
+			if (buf[i] < 0) {
+				dev_err(madc->dev, "err reading current\n");
+				count_req++;
+			} else {
+				count++;
+				buf[i] = buf[i] - 750;
+			}
+			break;
+		case 1:
+			buf[i] = twl4030battery_temperature(buf[i]);
+			if (buf[i] < 0) {
+				dev_err(madc->dev, "err reading temperature\n");
+				count_req++;
+			} else {
+				buf[i] -= 3;
+				count++;
+			}
+			break;
+		default:
+			count++;
+			/* Analog Input (V) = conv_result * step_size / R
+			 * conv_result = decimal value of 10-bit conversion
+			 *		 result
+			 * step size = 1.5 / (2 ^ 10 -1)
+			 * R = Prescaler ratio for input channels.
+			 * Result given in mV hence multiplied by 1000.
+			 */
+			buf[i] = (buf[i] * 3 * 1000 *
+				 twl4030_divider_ratios[i].denominator)
+				/ (2 * 1023 *
+				twl4030_divider_ratios[i].numerator);
+		}
+	}
+	if (count_req)
+		dev_err(madc->dev, "%d channel conversion failed\n", count_req);
+
+	return count;
+}
+
+/*
+ * Enables irq.
+ * @madc - pointer to twl4030_madc_data struct
+ * @id - irq number to be enabled
+ * can take one of TWL4030_MADC_RT, TWL4030_MADC_SW1, TWL4030_MADC_SW2
+ * corresponding to RT, SW1, SW2 conversion requests.
+ * If the i2c read fails it returns an error else returns 0.
+ */
+static int twl4030_madc_enable_irq(struct twl4030_madc_data *madc, u8 id)
+{
+	u8 val;
+	int ret;
+
+	ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &val, madc->imr);
+	if (ret) {
+		dev_err(madc->dev, "unable to read imr register 0x%X\n",
+			madc->imr);
+		return ret;
+	}
+	val &= ~(1 << id);
+	ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, val, madc->imr);
+	if (ret) {
+		dev_err(madc->dev,
+			"unable to write imr register 0x%X\n", madc->imr);
+		return ret;
+
+	}
+
+	return 0;
+}
+
+/*
+ * Disables irq.
+ * @madc - pointer to twl4030_madc_data struct
+ * @id - irq number to be disabled
+ * can take one of TWL4030_MADC_RT, TWL4030_MADC_SW1, TWL4030_MADC_SW2
+ * corresponding to RT, SW1, SW2 conversion requests.
+ * Returns error if i2c read/write fails.
+ */
+static int twl4030_madc_disable_irq(struct twl4030_madc_data *madc, u8 id)
+{
+	u8 val;
+	int ret;
+
+	ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &val, madc->imr);
+	if (ret) {
+		dev_err(madc->dev, "unable to read imr register 0x%X\n",
+			madc->imr);
+		return ret;
+	}
+	val |= (1 << id);
+	ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, val, madc->imr);
+	if (ret) {
+		dev_err(madc->dev,
+			"unable to write imr register 0x%X\n", madc->imr);
+		return ret;
+	}
+
+	return 0;
+}
+
+static irqreturn_t twl4030_madc_threaded_irq_handler(int irq, void *_madc)
+{
+	struct twl4030_madc_data *madc = _madc;
+	const struct twl4030_madc_conversion_method *method;
+	u8 isr_val, imr_val;
+	int i, len, ret;
+	struct twl4030_madc_request *r;
+
+	mutex_lock(&madc->lock);
+	ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &isr_val, madc->isr);
+	if (ret) {
+		dev_err(madc->dev, "unable to read isr register 0x%X\n",
+			madc->isr);
+		goto err_i2c;
+	}
+	ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &imr_val, madc->imr);
+	if (ret) {
+		dev_err(madc->dev, "unable to read imr register 0x%X\n",
+			madc->imr);
+		goto err_i2c;
+	}
+	isr_val &= ~imr_val;
+	for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) {
+		if (!(isr_val & (1 << i)))
+			continue;
+		ret = twl4030_madc_disable_irq(madc, i);
+		if (ret < 0)
+			dev_dbg(madc->dev, "Disable interrupt failed%d\n", i);
+		madc->requests[i].result_pending = 1;
+	}
+	for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) {
+		r = &madc->requests[i];
+		/* No pending results for this method, move to next one */
+		if (!r->result_pending)
+			continue;
+		method = &twl4030_conversion_methods[r->method];
+		/* Read results */
+		len = twl4030_madc_read_channels(madc, method->rbase,
+						 r->channels, r->rbuf);
+		/* Return results to caller */
+		if (r->func_cb != NULL) {
+			r->func_cb(len, r->channels, r->rbuf);
+			r->func_cb = NULL;
+		}
+		/* Free request */
+		r->result_pending = 0;
+		r->active = 0;
+	}
+	mutex_unlock(&madc->lock);
+
+	return IRQ_HANDLED;
+
+err_i2c:
+	/*
+	 * In case of error check whichever request is active
+	 * and service the same.
+	 */
+	for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) {
+		r = &madc->requests[i];
+		if (r->active == 0)
+			continue;
+		method = &twl4030_conversion_methods[r->method];
+		/* Read results */
+		len = twl4030_madc_read_channels(madc, method->rbase,
+						 r->channels, r->rbuf);
+		/* Return results to caller */
+		if (r->func_cb != NULL) {
+			r->func_cb(len, r->channels, r->rbuf);
+			r->func_cb = NULL;
+		}
+		/* Free request */
+		r->result_pending = 0;
+		r->active = 0;
+	}
+	mutex_unlock(&madc->lock);
+
+	return IRQ_HANDLED;
+}
+
+static int twl4030_madc_set_irq(struct twl4030_madc_data *madc,
+				struct twl4030_madc_request *req)
+{
+	struct twl4030_madc_request *p;
+	int ret;
+
+	p = &madc->requests[req->method];
+	memcpy(p, req, sizeof(*req));
+	ret = twl4030_madc_enable_irq(madc, req->method);
+	if (ret < 0) {
+		dev_err(madc->dev, "enable irq failed!!\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+/*
+ * Function which enables the madc conversion
+ * by writing to the control register.
+ * @madc - pointer to twl4030_madc_data struct
+ * @conv_method - can be TWL4030_MADC_RT, TWL4030_MADC_SW2, TWL4030_MADC_SW1
+ * corresponding to RT SW1 or SW2 conversion methods.
+ * Returns 0 if succeeds else a negative error value
+ */
+static int twl4030_madc_start_conversion(struct twl4030_madc_data *madc,
+					 int conv_method)
+{
+	const struct twl4030_madc_conversion_method *method;
+	int ret = 0;
+	method = &twl4030_conversion_methods[conv_method];
+	switch (conv_method) {
+	case TWL4030_MADC_SW1:
+	case TWL4030_MADC_SW2:
+		ret = twl_i2c_write_u8(TWL4030_MODULE_MADC,
+				       TWL4030_MADC_SW_START, method->ctrl);
+		if (ret) {
+			dev_err(madc->dev,
+				"unable to write ctrl register 0x%X\n",
+				method->ctrl);
+			return ret;
+		}
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+/*
+ * Function that waits for conversion to be ready
+ * @madc - pointer to twl4030_madc_data struct
+ * @timeout_ms - timeout value in milliseconds
+ * @status_reg - ctrl register
+ * returns 0 if succeeds else a negative error value
+ */
+static int twl4030_madc_wait_conversion_ready(struct twl4030_madc_data *madc,
+					      unsigned int timeout_ms,
+					      u8 status_reg)
+{
+	unsigned long timeout;
+	int ret;
+
+	timeout = jiffies + msecs_to_jiffies(timeout_ms);
+	do {
+		u8 reg;
+
+		ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &reg, status_reg);
+		if (ret) {
+			dev_err(madc->dev,
+				"unable to read status register 0x%X\n",
+				status_reg);
+			return ret;
+		}
+		if (!(reg & TWL4030_MADC_BUSY) && (reg & TWL4030_MADC_EOC_SW))
+			return 0;
+		usleep_range(500, 2000);
+	} while (!time_after(jiffies, timeout));
+	dev_err(madc->dev, "conversion timeout!\n");
+
+	return -EAGAIN;
+}
+
+/*
+ * An exported function which can be called from other kernel drivers.
+ * @req twl4030_madc_request structure
+ * req->rbuf will be filled with read values of channels based on the
+ * channel index. If a particular channel reading fails there will
+ * be a negative error value in the corresponding array element.
+ * returns 0 if succeeds else error value
+ */
+int twl4030_madc_conversion(struct twl4030_madc_request *req)
+{
+	const struct twl4030_madc_conversion_method *method;
+	u8 ch_msb, ch_lsb;
+	int ret;
+
+	if (!req || !twl4030_madc)
+		return -EINVAL;
+
+	mutex_lock(&twl4030_madc->lock);
+	if (req->method < TWL4030_MADC_RT || req->method > TWL4030_MADC_SW2) {
+		ret = -EINVAL;
+		goto out;
+	}
+	/* Do we have a conversion request ongoing */
+	if (twl4030_madc->requests[req->method].active) {
+		ret = -EBUSY;
+		goto out;
+	}
+	ch_msb = (req->channels >> 8) & 0xff;
+	ch_lsb = req->channels & 0xff;
+	method = &twl4030_conversion_methods[req->method];
+	/* Select channels to be converted */
+	ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, ch_msb, method->sel + 1);
+	if (ret) {
+		dev_err(twl4030_madc->dev,
+			"unable to write sel register 0x%X\n", method->sel + 1);
+		goto out;
+	}
+	ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, ch_lsb, method->sel);
+	if (ret) {
+		dev_err(twl4030_madc->dev,
+			"unable to write sel register 0x%X\n", method->sel + 1);
+		goto out;
+	}
+	/* Select averaging for all channels if do_avg is set */
+	if (req->do_avg) {
+		ret = twl_i2c_write_u8(TWL4030_MODULE_MADC,
+				       ch_msb, method->avg + 1);
+		if (ret) {
+			dev_err(twl4030_madc->dev,
+				"unable to write avg register 0x%X\n",
+				method->avg + 1);
+			goto out;
+		}
+		ret = twl_i2c_write_u8(TWL4030_MODULE_MADC,
+				       ch_lsb, method->avg);
+		if (ret) {
+			dev_err(twl4030_madc->dev,
+				"unable to write sel reg 0x%X\n",
+				method->sel + 1);
+			goto out;
+		}
+	}
+	if (req->type == TWL4030_MADC_IRQ_ONESHOT && req->func_cb != NULL) {
+		ret = twl4030_madc_set_irq(twl4030_madc, req);
+		if (ret < 0)
+			goto out;
+		ret = twl4030_madc_start_conversion(twl4030_madc, req->method);
+		if (ret < 0)
+			goto out;
+		twl4030_madc->requests[req->method].active = 1;
+		ret = 0;
+		goto out;
+	}
+	/* With RT method we should not be here anymore */
+	if (req->method == TWL4030_MADC_RT) {
+		ret = -EINVAL;
+		goto out;
+	}
+	ret = twl4030_madc_start_conversion(twl4030_madc, req->method);
+	if (ret < 0)
+		goto out;
+	twl4030_madc->requests[req->method].active = 1;
+	/* Wait until conversion is ready (ctrl register returns EOC) */
+	ret = twl4030_madc_wait_conversion_ready(twl4030_madc, 5, method->ctrl);
+	if (ret) {
+		twl4030_madc->requests[req->method].active = 0;
+		goto out;
+	}
+	ret = twl4030_madc_read_channels(twl4030_madc, method->rbase,
+					 req->channels, req->rbuf);
+	twl4030_madc->requests[req->method].active = 0;
+
+out:
+	mutex_unlock(&twl4030_madc->lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(twl4030_madc_conversion);
+
+/*
+ * Return channel value
+ * Or < 0 on failure.
+ */
+int twl4030_get_madc_conversion(int channel_no)
+{
+	struct twl4030_madc_request req;
+	int temp = 0;
+	int ret;
+
+	req.channels = (1 << channel_no);
+	req.method = TWL4030_MADC_SW2;
+	req.active = 0;
+	req.func_cb = NULL;
+	ret = twl4030_madc_conversion(&req);
+	if (ret < 0)
+		return ret;
+	if (req.rbuf[channel_no] > 0)
+		temp = req.rbuf[channel_no];
+
+	return temp;
+}
+EXPORT_SYMBOL_GPL(twl4030_get_madc_conversion);
+
+/*
+ * Function to enable or disable bias current for
+ * main battery type reading or temperature sensing
+ * @madc - pointer to twl4030_madc_data struct
+ * @chan - can be one of the two values
+ * TWL4030_BCI_ITHEN - Enables bias current for main battery type reading
+ * TWL4030_BCI_TYPEN - Enables bias current for main battery temperature
+ * sensing
+ * @on - enable or disable chan.
+ */
+static int twl4030_madc_set_current_generator(struct twl4030_madc_data *madc,
+					      int chan, int on)
+{
+	int ret;
+	u8 regval;
+
+	ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE,
+			      &regval, TWL4030_BCI_BCICTL1);
+	if (ret) {
+		dev_err(madc->dev, "unable to read BCICTL1 reg 0x%X",
+			TWL4030_BCI_BCICTL1);
+		return ret;
+	}
+	if (on)
+		regval |= chan ? TWL4030_BCI_ITHEN : TWL4030_BCI_TYPEN;
+	else
+		regval &= chan ? ~TWL4030_BCI_ITHEN : ~TWL4030_BCI_TYPEN;
+	ret = twl_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE,
+			       regval, TWL4030_BCI_BCICTL1);
+	if (ret) {
+		dev_err(madc->dev, "unable to write BCICTL1 reg 0x%X\n",
+			TWL4030_BCI_BCICTL1);
+		return ret;
+	}
+
+	return 0;
+}
+
+/*
+ * Function that sets MADC software power on bit to enable MADC
+ * @madc - pointer to twl4030_madc_data struct
+ * @on - Enable or disable MADC software powen on bit.
+ * returns error if i2c read/write fails else 0
+ */
+static int twl4030_madc_set_power(struct twl4030_madc_data *madc, int on)
+{
+	u8 regval;
+	int ret;
+
+	ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE,
+			      &regval, TWL4030_MADC_CTRL1);
+	if (ret) {
+		dev_err(madc->dev, "unable to read madc ctrl1 reg 0x%X\n",
+			TWL4030_MADC_CTRL1);
+		return ret;
+	}
+	if (on)
+		regval |= TWL4030_MADC_MADCON;
+	else
+		regval &= ~TWL4030_MADC_MADCON;
+	ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, regval, TWL4030_MADC_CTRL1);
+	if (ret) {
+		dev_err(madc->dev, "unable to write madc ctrl1 reg 0x%X\n",
+			TWL4030_MADC_CTRL1);
+		return ret;
+	}
+
+	return 0;
+}
+
+/*
+ * Initialize MADC and request for threaded irq
+ */
+static int __devinit twl4030_madc_probe(struct platform_device *pdev)
+{
+	struct twl4030_madc_data *madc;
+	struct twl4030_madc_platform_data *pdata = pdev->dev.platform_data;
+	int ret;
+	u8 regval;
+
+	if (!pdata) {
+		dev_err(&pdev->dev, "platform_data not available\n");
+		return -EINVAL;
+	}
+	madc = kzalloc(sizeof(*madc), GFP_KERNEL);
+	if (!madc)
+		return -ENOMEM;
+
+	madc->dev = &pdev->dev;
+
+	/*
+	 * Phoenix provides 2 interrupt lines. The first one is connected to
+	 * the OMAP. The other one can be connected to the other processor such
+	 * as modem. Hence two separate ISR and IMR registers.
+	 */
+	madc->imr = (pdata->irq_line == 1) ?
+	    TWL4030_MADC_IMR1 : TWL4030_MADC_IMR2;
+	madc->isr = (pdata->irq_line == 1) ?
+	    TWL4030_MADC_ISR1 : TWL4030_MADC_ISR2;
+	ret = twl4030_madc_set_power(madc, 1);
+	if (ret < 0)
+		goto err_power;
+	ret = twl4030_madc_set_current_generator(madc, 0, 1);
+	if (ret < 0)
+		goto err_current_generator;
+
+	ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE,
+			      &regval, TWL4030_BCI_BCICTL1);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to read reg BCI CTL1 0x%X\n",
+			TWL4030_BCI_BCICTL1);
+		goto err_i2c;
+	}
+	regval |= TWL4030_BCI_MESBAT;
+	ret = twl_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE,
+			       regval, TWL4030_BCI_BCICTL1);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to write reg BCI Ctl1 0x%X\n",
+			TWL4030_BCI_BCICTL1);
+		goto err_i2c;
+	}
+
+	/* Check that MADC clock is on */
+	ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, &regval, TWL4030_REG_GPBR1);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to read reg GPBR1 0x%X\n",
+				TWL4030_REG_GPBR1);
+		goto err_i2c;
+	}
+
+	/* If MADC clk is not on, turn it on */
+	if (!(regval & TWL4030_GPBR1_MADC_HFCLK_EN)) {
+		dev_info(&pdev->dev, "clk disabled, enabling\n");
+		regval |= TWL4030_GPBR1_MADC_HFCLK_EN;
+		ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, regval,
+				       TWL4030_REG_GPBR1);
+		if (ret) {
+			dev_err(&pdev->dev, "unable to write reg GPBR1 0x%X\n",
+					TWL4030_REG_GPBR1);
+			goto err_i2c;
+		}
+	}
+
+	platform_set_drvdata(pdev, madc);
+	mutex_init(&madc->lock);
+	ret = request_threaded_irq(platform_get_irq(pdev, 0), NULL,
+				   twl4030_madc_threaded_irq_handler,
+				   IRQF_TRIGGER_RISING, "twl4030_madc", madc);
+	if (ret) {
+		dev_dbg(&pdev->dev, "could not request irq\n");
+		goto err_irq;
+	}
+	twl4030_madc = madc;
+	return 0;
+err_irq:
+	platform_set_drvdata(pdev, NULL);
+err_i2c:
+	twl4030_madc_set_current_generator(madc, 0, 0);
+err_current_generator:
+	twl4030_madc_set_power(madc, 0);
+err_power:
+	kfree(madc);
+
+	return ret;
+}
+
+static int __devexit twl4030_madc_remove(struct platform_device *pdev)
+{
+	struct twl4030_madc_data *madc = platform_get_drvdata(pdev);
+
+	free_irq(platform_get_irq(pdev, 0), madc);
+	platform_set_drvdata(pdev, NULL);
+	twl4030_madc_set_current_generator(madc, 0, 0);
+	twl4030_madc_set_power(madc, 0);
+	kfree(madc);
+
+	return 0;
+}
+
+static struct platform_driver twl4030_madc_driver = {
+	.probe = twl4030_madc_probe,
+	.remove = __exit_p(twl4030_madc_remove),
+	.driver = {
+		   .name = "twl4030_madc",
+		   .owner = THIS_MODULE,
+		   },
+};
+
+module_platform_driver(twl4030_madc_driver);
+
+MODULE_DESCRIPTION("TWL4030 ADC driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("J Keerthy");
+MODULE_ALIAS("platform:twl4030_madc");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/twl4030-power.c b/ap/os/linux/linux-3.4.x/drivers/mfd/twl4030-power.c
new file mode 100644
index 0000000..79ca33d
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/twl4030-power.c
@@ -0,0 +1,607 @@
+/*
+ * linux/drivers/i2c/chips/twl4030-power.c
+ *
+ * Handle TWL4030 Power initialization
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Copyright (C) 2006 Texas Instruments, Inc
+ *
+ * Written by 	Kalle Jokiniemi
+ *		Peter De Schrijver <peter.de-schrijver@nokia.com>
+ * Several fixes by Amit Kucheria <amit.kucheria@verdurent.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/i2c/twl.h>
+#include <linux/platform_device.h>
+
+#include <asm/mach-types.h>
+
+static u8 twl4030_start_script_address = 0x2b;
+
+#define PWR_P1_SW_EVENTS	0x10
+#define PWR_DEVOFF		(1 << 0)
+#define SEQ_OFFSYNC		(1 << 0)
+
+#define PHY_TO_OFF_PM_MASTER(p)		(p - 0x36)
+#define PHY_TO_OFF_PM_RECEIVER(p)	(p - 0x5b)
+
+/* resource - hfclk */
+#define R_HFCLKOUT_DEV_GRP 	PHY_TO_OFF_PM_RECEIVER(0xe6)
+
+/* PM events */
+#define R_P1_SW_EVENTS		PHY_TO_OFF_PM_MASTER(0x46)
+#define R_P2_SW_EVENTS		PHY_TO_OFF_PM_MASTER(0x47)
+#define R_P3_SW_EVENTS		PHY_TO_OFF_PM_MASTER(0x48)
+#define R_CFG_P1_TRANSITION	PHY_TO_OFF_PM_MASTER(0x36)
+#define R_CFG_P2_TRANSITION	PHY_TO_OFF_PM_MASTER(0x37)
+#define R_CFG_P3_TRANSITION	PHY_TO_OFF_PM_MASTER(0x38)
+
+#define LVL_WAKEUP	0x08
+
+#define ENABLE_WARMRESET (1<<4)
+
+#define END_OF_SCRIPT		0x3f
+
+#define R_SEQ_ADD_A2S		PHY_TO_OFF_PM_MASTER(0x55)
+#define R_SEQ_ADD_S2A12		PHY_TO_OFF_PM_MASTER(0x56)
+#define	R_SEQ_ADD_S2A3		PHY_TO_OFF_PM_MASTER(0x57)
+#define	R_SEQ_ADD_WARM		PHY_TO_OFF_PM_MASTER(0x58)
+#define R_MEMORY_ADDRESS	PHY_TO_OFF_PM_MASTER(0x59)
+#define R_MEMORY_DATA		PHY_TO_OFF_PM_MASTER(0x5a)
+
+/* resource configuration registers
+   <RESOURCE>_DEV_GRP   at address 'n+0'
+   <RESOURCE>_TYPE      at address 'n+1'
+   <RESOURCE>_REMAP     at address 'n+2'
+   <RESOURCE>_DEDICATED at address 'n+3'
+*/
+#define DEV_GRP_OFFSET		0
+#define TYPE_OFFSET		1
+#define REMAP_OFFSET		2
+#define DEDICATED_OFFSET	3
+
+/* Bit positions in the registers */
+
+/* <RESOURCE>_DEV_GRP */
+#define DEV_GRP_SHIFT		5
+#define DEV_GRP_MASK		(7 << DEV_GRP_SHIFT)
+
+/* <RESOURCE>_TYPE */
+#define TYPE_SHIFT		0
+#define TYPE_MASK		(7 << TYPE_SHIFT)
+#define TYPE2_SHIFT		3
+#define TYPE2_MASK		(3 << TYPE2_SHIFT)
+
+/* <RESOURCE>_REMAP */
+#define SLEEP_STATE_SHIFT	0
+#define SLEEP_STATE_MASK	(0xf << SLEEP_STATE_SHIFT)
+#define OFF_STATE_SHIFT		4
+#define OFF_STATE_MASK		(0xf << OFF_STATE_SHIFT)
+
+static u8 res_config_addrs[] = {
+	[RES_VAUX1]	= 0x17,
+	[RES_VAUX2]	= 0x1b,
+	[RES_VAUX3]	= 0x1f,
+	[RES_VAUX4]	= 0x23,
+	[RES_VMMC1]	= 0x27,
+	[RES_VMMC2]	= 0x2b,
+	[RES_VPLL1]	= 0x2f,
+	[RES_VPLL2]	= 0x33,
+	[RES_VSIM]	= 0x37,
+	[RES_VDAC]	= 0x3b,
+	[RES_VINTANA1]	= 0x3f,
+	[RES_VINTANA2]	= 0x43,
+	[RES_VINTDIG]	= 0x47,
+	[RES_VIO]	= 0x4b,
+	[RES_VDD1]	= 0x55,
+	[RES_VDD2]	= 0x63,
+	[RES_VUSB_1V5]	= 0x71,
+	[RES_VUSB_1V8]	= 0x74,
+	[RES_VUSB_3V1]	= 0x77,
+	[RES_VUSBCP]	= 0x7a,
+	[RES_REGEN]	= 0x7f,
+	[RES_NRES_PWRON] = 0x82,
+	[RES_CLKEN]	= 0x85,
+	[RES_SYSEN]	= 0x88,
+	[RES_HFCLKOUT]	= 0x8b,
+	[RES_32KCLKOUT]	= 0x8e,
+	[RES_RESET]	= 0x91,
+	[RES_MAIN_REF]	= 0x94,
+};
+
+static int __devinit twl4030_write_script_byte(u8 address, u8 byte)
+{
+	int err;
+
+	err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address,
+				R_MEMORY_ADDRESS);
+	if (err)
+		goto out;
+	err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, byte,
+				R_MEMORY_DATA);
+out:
+	return err;
+}
+
+static int __devinit twl4030_write_script_ins(u8 address, u16 pmb_message,
+					   u8 delay, u8 next)
+{
+	int err;
+
+	address *= 4;
+	err = twl4030_write_script_byte(address++, pmb_message >> 8);
+	if (err)
+		goto out;
+	err = twl4030_write_script_byte(address++, pmb_message & 0xff);
+	if (err)
+		goto out;
+	err = twl4030_write_script_byte(address++, delay);
+	if (err)
+		goto out;
+	err = twl4030_write_script_byte(address++, next);
+out:
+	return err;
+}
+
+static int __devinit twl4030_write_script(u8 address, struct twl4030_ins *script,
+				       int len)
+{
+	int err;
+
+	for (; len; len--, address++, script++) {
+		if (len == 1) {
+			err = twl4030_write_script_ins(address,
+						script->pmb_message,
+						script->delay,
+						END_OF_SCRIPT);
+			if (err)
+				break;
+		} else {
+			err = twl4030_write_script_ins(address,
+						script->pmb_message,
+						script->delay,
+						address + 1);
+			if (err)
+				break;
+		}
+	}
+	return err;
+}
+
+static int __devinit twl4030_config_wakeup3_sequence(u8 address)
+{
+	int err;
+	u8 data;
+
+	/* Set SLEEP to ACTIVE SEQ address for P3 */
+	err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address,
+				R_SEQ_ADD_S2A3);
+	if (err)
+		goto out;
+
+	/* P3 LVL_WAKEUP should be on LEVEL */
+	err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &data,
+				R_P3_SW_EVENTS);
+	if (err)
+		goto out;
+	data |= LVL_WAKEUP;
+	err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, data,
+				R_P3_SW_EVENTS);
+out:
+	if (err)
+		pr_err("TWL4030 wakeup sequence for P3 config error\n");
+	return err;
+}
+
+static int __devinit twl4030_config_wakeup12_sequence(u8 address)
+{
+	int err = 0;
+	u8 data;
+
+	/* Set SLEEP to ACTIVE SEQ address for P1 and P2 */
+	err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address,
+				R_SEQ_ADD_S2A12);
+	if (err)
+		goto out;
+
+	/* P1/P2 LVL_WAKEUP should be on LEVEL */
+	err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &data,
+				R_P1_SW_EVENTS);
+	if (err)
+		goto out;
+
+	data |= LVL_WAKEUP;
+	err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, data,
+				R_P1_SW_EVENTS);
+	if (err)
+		goto out;
+
+	err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &data,
+				R_P2_SW_EVENTS);
+	if (err)
+		goto out;
+
+	data |= LVL_WAKEUP;
+	err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, data,
+				R_P2_SW_EVENTS);
+	if (err)
+		goto out;
+
+	if (machine_is_omap_3430sdp() || machine_is_omap_ldp()) {
+		/* Disabling AC charger effect on sleep-active transitions */
+		err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &data,
+					R_CFG_P1_TRANSITION);
+		if (err)
+			goto out;
+		data &= ~(1<<1);
+		err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, data ,
+					R_CFG_P1_TRANSITION);
+		if (err)
+			goto out;
+	}
+
+out:
+	if (err)
+		pr_err("TWL4030 wakeup sequence for P1 and P2" \
+			"config error\n");
+	return err;
+}
+
+static int __devinit twl4030_config_sleep_sequence(u8 address)
+{
+	int err;
+
+	/* Set ACTIVE to SLEEP SEQ address in T2 memory*/
+	err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address,
+				R_SEQ_ADD_A2S);
+
+	if (err)
+		pr_err("TWL4030 sleep sequence config error\n");
+
+	return err;
+}
+
+static int __devinit twl4030_config_warmreset_sequence(u8 address)
+{
+	int err;
+	u8 rd_data;
+
+	/* Set WARM RESET SEQ address for P1 */
+	err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address,
+				R_SEQ_ADD_WARM);
+	if (err)
+		goto out;
+
+	/* P1/P2/P3 enable WARMRESET */
+	err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &rd_data,
+				R_P1_SW_EVENTS);
+	if (err)
+		goto out;
+
+	rd_data |= ENABLE_WARMRESET;
+	err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, rd_data,
+				R_P1_SW_EVENTS);
+	if (err)
+		goto out;
+
+	err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &rd_data,
+				R_P2_SW_EVENTS);
+	if (err)
+		goto out;
+
+	rd_data |= ENABLE_WARMRESET;
+	err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, rd_data,
+				R_P2_SW_EVENTS);
+	if (err)
+		goto out;
+
+	err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &rd_data,
+				R_P3_SW_EVENTS);
+	if (err)
+		goto out;
+
+	rd_data |= ENABLE_WARMRESET;
+	err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, rd_data,
+				R_P3_SW_EVENTS);
+out:
+	if (err)
+		pr_err("TWL4030 warmreset seq config error\n");
+	return err;
+}
+
+static int __devinit twl4030_configure_resource(struct twl4030_resconfig *rconfig)
+{
+	int rconfig_addr;
+	int err;
+	u8 type;
+	u8 grp;
+	u8 remap;
+
+	if (rconfig->resource > TOTAL_RESOURCES) {
+		pr_err("TWL4030 Resource %d does not exist\n",
+			rconfig->resource);
+		return -EINVAL;
+	}
+
+	rconfig_addr = res_config_addrs[rconfig->resource];
+
+	/* Set resource group */
+	err = twl_i2c_read_u8(TWL4030_MODULE_PM_RECEIVER, &grp,
+			      rconfig_addr + DEV_GRP_OFFSET);
+	if (err) {
+		pr_err("TWL4030 Resource %d group could not be read\n",
+			rconfig->resource);
+		return err;
+	}
+
+	if (rconfig->devgroup != TWL4030_RESCONFIG_UNDEF) {
+		grp &= ~DEV_GRP_MASK;
+		grp |= rconfig->devgroup << DEV_GRP_SHIFT;
+		err = twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
+				       grp, rconfig_addr + DEV_GRP_OFFSET);
+		if (err < 0) {
+			pr_err("TWL4030 failed to program devgroup\n");
+			return err;
+		}
+	}
+
+	/* Set resource types */
+	err = twl_i2c_read_u8(TWL4030_MODULE_PM_RECEIVER, &type,
+				rconfig_addr + TYPE_OFFSET);
+	if (err < 0) {
+		pr_err("TWL4030 Resource %d type could not be read\n",
+			rconfig->resource);
+		return err;
+	}
+
+	if (rconfig->type != TWL4030_RESCONFIG_UNDEF) {
+		type &= ~TYPE_MASK;
+		type |= rconfig->type << TYPE_SHIFT;
+	}
+
+	if (rconfig->type2 != TWL4030_RESCONFIG_UNDEF) {
+		type &= ~TYPE2_MASK;
+		type |= rconfig->type2 << TYPE2_SHIFT;
+	}
+
+	err = twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
+				type, rconfig_addr + TYPE_OFFSET);
+	if (err < 0) {
+		pr_err("TWL4030 failed to program resource type\n");
+		return err;
+	}
+
+	/* Set remap states */
+	err = twl_i2c_read_u8(TWL4030_MODULE_PM_RECEIVER, &remap,
+			      rconfig_addr + REMAP_OFFSET);
+	if (err < 0) {
+		pr_err("TWL4030 Resource %d remap could not be read\n",
+			rconfig->resource);
+		return err;
+	}
+
+	if (rconfig->remap_off != TWL4030_RESCONFIG_UNDEF) {
+		remap &= ~OFF_STATE_MASK;
+		remap |= rconfig->remap_off << OFF_STATE_SHIFT;
+	}
+
+	if (rconfig->remap_sleep != TWL4030_RESCONFIG_UNDEF) {
+		remap &= ~SLEEP_STATE_MASK;
+		remap |= rconfig->remap_sleep << SLEEP_STATE_SHIFT;
+	}
+
+	err = twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
+			       remap,
+			       rconfig_addr + REMAP_OFFSET);
+	if (err < 0) {
+		pr_err("TWL4030 failed to program remap\n");
+		return err;
+	}
+
+	return 0;
+}
+
+static int __devinit load_twl4030_script(struct twl4030_script *tscript,
+	       u8 address)
+{
+	int err;
+	static int order;
+
+	/* Make sure the script isn't going beyond last valid address (0x3f) */
+	if ((address + tscript->size) > END_OF_SCRIPT) {
+		pr_err("TWL4030 scripts too big error\n");
+		return -EINVAL;
+	}
+
+	err = twl4030_write_script(address, tscript->script, tscript->size);
+	if (err)
+		goto out;
+
+	if (tscript->flags & TWL4030_WRST_SCRIPT) {
+		err = twl4030_config_warmreset_sequence(address);
+		if (err)
+			goto out;
+	}
+	if (tscript->flags & TWL4030_WAKEUP12_SCRIPT) {
+		err = twl4030_config_wakeup12_sequence(address);
+		if (err)
+			goto out;
+		order = 1;
+	}
+	if (tscript->flags & TWL4030_WAKEUP3_SCRIPT) {
+		err = twl4030_config_wakeup3_sequence(address);
+		if (err)
+			goto out;
+	}
+	if (tscript->flags & TWL4030_SLEEP_SCRIPT) {
+		if (!order)
+			pr_warning("TWL4030: Bad order of scripts (sleep "\
+					"script before wakeup) Leads to boot"\
+					"failure on some boards\n");
+		err = twl4030_config_sleep_sequence(address);
+	}
+out:
+	return err;
+}
+
+int twl4030_remove_script(u8 flags)
+{
+	int err = 0;
+
+	err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER,
+			TWL4030_PM_MASTER_KEY_CFG1,
+			TWL4030_PM_MASTER_PROTECT_KEY);
+	if (err) {
+		pr_err("twl4030: unable to unlock PROTECT_KEY\n");
+		return err;
+	}
+
+	err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER,
+			TWL4030_PM_MASTER_KEY_CFG2,
+			TWL4030_PM_MASTER_PROTECT_KEY);
+	if (err) {
+		pr_err("twl4030: unable to unlock PROTECT_KEY\n");
+		return err;
+	}
+
+	if (flags & TWL4030_WRST_SCRIPT) {
+		err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, END_OF_SCRIPT,
+				R_SEQ_ADD_WARM);
+		if (err)
+			return err;
+	}
+	if (flags & TWL4030_WAKEUP12_SCRIPT) {
+		err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, END_OF_SCRIPT,
+				R_SEQ_ADD_S2A12);
+		if (err)
+			return err;
+	}
+	if (flags & TWL4030_WAKEUP3_SCRIPT) {
+		err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, END_OF_SCRIPT,
+				R_SEQ_ADD_S2A3);
+		if (err)
+			return err;
+	}
+	if (flags & TWL4030_SLEEP_SCRIPT) {
+		err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, END_OF_SCRIPT,
+				R_SEQ_ADD_A2S);
+		if (err)
+			return err;
+	}
+
+	err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0,
+			TWL4030_PM_MASTER_PROTECT_KEY);
+	if (err)
+		pr_err("TWL4030 Unable to relock registers\n");
+
+	return err;
+}
+
+/*
+ * In master mode, start the power off sequence.
+ * After a successful execution, TWL shuts down the power to the SoC
+ * and all peripherals connected to it.
+ */
+void twl4030_power_off(void)
+{
+	int err;
+
+	err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, PWR_DEVOFF,
+			       TWL4030_PM_MASTER_P1_SW_EVENTS);
+	if (err)
+		pr_err("TWL4030 Unable to power off\n");
+}
+
+void __devinit twl4030_power_init(struct twl4030_power_data *twl4030_scripts)
+{
+	int err = 0;
+	int i;
+	struct twl4030_resconfig *resconfig;
+	u8 val, address = twl4030_start_script_address;
+
+	err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER,
+			TWL4030_PM_MASTER_KEY_CFG1,
+			TWL4030_PM_MASTER_PROTECT_KEY);
+	if (err)
+		goto unlock;
+
+	err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER,
+			TWL4030_PM_MASTER_KEY_CFG2,
+			TWL4030_PM_MASTER_PROTECT_KEY);
+	if (err)
+		goto unlock;
+
+	for (i = 0; i < twl4030_scripts->num; i++) {
+		err = load_twl4030_script(twl4030_scripts->scripts[i], address);
+		if (err)
+			goto load;
+		address += twl4030_scripts->scripts[i]->size;
+	}
+
+	resconfig = twl4030_scripts->resource_config;
+	if (resconfig) {
+		while (resconfig->resource) {
+			err = twl4030_configure_resource(resconfig);
+			if (err)
+				goto resource;
+			resconfig++;
+
+		}
+	}
+
+	/* Board has to be wired properly to use this feature */
+	if (twl4030_scripts->use_poweroff && !pm_power_off) {
+		/* Default for SEQ_OFFSYNC is set, lets ensure this */
+		err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &val,
+				      TWL4030_PM_MASTER_CFG_P123_TRANSITION);
+		if (err) {
+			pr_warning("TWL4030 Unable to read registers\n");
+
+		} else if (!(val & SEQ_OFFSYNC)) {
+			val |= SEQ_OFFSYNC;
+			err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, val,
+					TWL4030_PM_MASTER_CFG_P123_TRANSITION);
+			if (err) {
+				pr_err("TWL4030 Unable to setup SEQ_OFFSYNC\n");
+				goto relock;
+			}
+		}
+
+		pm_power_off = twl4030_power_off;
+	}
+
+relock:
+	err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0,
+			TWL4030_PM_MASTER_PROTECT_KEY);
+	if (err)
+		pr_err("TWL4030 Unable to relock registers\n");
+	return;
+
+unlock:
+	if (err)
+		pr_err("TWL4030 Unable to unlock registers\n");
+	return;
+load:
+	if (err)
+		pr_err("TWL4030 failed to load scripts\n");
+	return;
+resource:
+	if (err)
+		pr_err("TWL4030 failed to configure resource\n");
+	return;
+}
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/twl6030-irq.c b/ap/os/linux/linux-3.4.x/drivers/mfd/twl6030-irq.c
new file mode 100644
index 0000000..b76902f
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/twl6030-irq.c
@@ -0,0 +1,446 @@
+/*
+ * twl6030-irq.c - TWL6030 irq support
+ *
+ * Copyright (C) 2005-2009 Texas Instruments, Inc.
+ *
+ * Modifications to defer interrupt handling to a kernel thread:
+ * Copyright (C) 2006 MontaVista Software, Inc.
+ *
+ * Based on tlv320aic23.c:
+ * Copyright (c) by Kai Svahn <kai.svahn@nokia.com>
+ *
+ * Code cleanup and modifications to IRQ handler.
+ * by syed khasim <x0khasim@ti.com>
+ *
+ * TWL6030 specific code and IRQ handling changes by
+ * Jagadeesh Bhaskar Pakaravoor <j-pakaravoor@ti.com>
+ * Balaji T K <balajitk@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/export.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kthread.h>
+#include <linux/i2c/twl.h>
+#include <linux/platform_device.h>
+#include <linux/suspend.h>
+#include <linux/of.h>
+#include <linux/irqdomain.h>
+
+#include "twl-core.h"
+
+/*
+ * TWL6030 (unlike its predecessors, which had two level interrupt handling)
+ * three interrupt registers INT_STS_A, INT_STS_B and INT_STS_C.
+ * It exposes status bits saying who has raised an interrupt. There are
+ * three mask registers that corresponds to these status registers, that
+ * enables/disables these interrupts.
+ *
+ * We set up IRQs starting at a platform-specified base. An interrupt map table,
+ * specifies mapping between interrupt number and the associated module.
+ */
+#define TWL6030_NR_IRQS    20
+
+static int twl6030_interrupt_mapping[24] = {
+	PWR_INTR_OFFSET,	/* Bit 0	PWRON			*/
+	PWR_INTR_OFFSET,	/* Bit 1	RPWRON			*/
+	PWR_INTR_OFFSET,	/* Bit 2	BAT_VLOW		*/
+	RTC_INTR_OFFSET,	/* Bit 3	RTC_ALARM		*/
+	RTC_INTR_OFFSET,	/* Bit 4	RTC_PERIOD		*/
+	HOTDIE_INTR_OFFSET,	/* Bit 5	HOT_DIE			*/
+	SMPSLDO_INTR_OFFSET,	/* Bit 6	VXXX_SHORT		*/
+	SMPSLDO_INTR_OFFSET,	/* Bit 7	VMMC_SHORT		*/
+
+	SMPSLDO_INTR_OFFSET,	/* Bit 8	VUSIM_SHORT		*/
+	BATDETECT_INTR_OFFSET,	/* Bit 9	BAT			*/
+	SIMDETECT_INTR_OFFSET,	/* Bit 10	SIM			*/
+	MMCDETECT_INTR_OFFSET,	/* Bit 11	MMC			*/
+	RSV_INTR_OFFSET,  	/* Bit 12	Reserved		*/
+	MADC_INTR_OFFSET,	/* Bit 13	GPADC_RT_EOC		*/
+	MADC_INTR_OFFSET,	/* Bit 14	GPADC_SW_EOC		*/
+	GASGAUGE_INTR_OFFSET,	/* Bit 15	CC_AUTOCAL		*/
+
+	USBOTG_INTR_OFFSET,	/* Bit 16	ID_WKUP			*/
+	USBOTG_INTR_OFFSET,	/* Bit 17	VBUS_WKUP		*/
+	USBOTG_INTR_OFFSET,	/* Bit 18	ID			*/
+	USB_PRES_INTR_OFFSET,	/* Bit 19	VBUS			*/
+	CHARGER_INTR_OFFSET,	/* Bit 20	CHRG_CTRL		*/
+	CHARGERFAULT_INTR_OFFSET,	/* Bit 21	EXT_CHRG	*/
+	CHARGERFAULT_INTR_OFFSET,	/* Bit 22	INT_CHRG	*/
+	RSV_INTR_OFFSET,	/* Bit 23	Reserved		*/
+};
+/*----------------------------------------------------------------------*/
+
+static unsigned twl6030_irq_base;
+static int twl_irq;
+static bool twl_irq_wake_enabled;
+
+static struct completion irq_event;
+static atomic_t twl6030_wakeirqs = ATOMIC_INIT(0);
+
+static int twl6030_irq_pm_notifier(struct notifier_block *notifier,
+				   unsigned long pm_event, void *unused)
+{
+	int chained_wakeups;
+
+	switch (pm_event) {
+	case PM_SUSPEND_PREPARE:
+		chained_wakeups = atomic_read(&twl6030_wakeirqs);
+
+		if (chained_wakeups && !twl_irq_wake_enabled) {
+			if (enable_irq_wake(twl_irq))
+				pr_err("twl6030 IRQ wake enable failed\n");
+			else
+				twl_irq_wake_enabled = true;
+		} else if (!chained_wakeups && twl_irq_wake_enabled) {
+			disable_irq_wake(twl_irq);
+			twl_irq_wake_enabled = false;
+		}
+
+		disable_irq(twl_irq);
+		break;
+
+	case PM_POST_SUSPEND:
+		enable_irq(twl_irq);
+		break;
+
+	default:
+		break;
+	}
+
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block twl6030_irq_pm_notifier_block = {
+	.notifier_call = twl6030_irq_pm_notifier,
+};
+
+/*
+ * This thread processes interrupts reported by the Primary Interrupt Handler.
+ */
+static int twl6030_irq_thread(void *data)
+{
+	long irq = (long)data;
+	static unsigned i2c_errors;
+	static const unsigned max_i2c_errors = 100;
+	int ret;
+
+	while (!kthread_should_stop()) {
+		int i;
+		union {
+		u8 bytes[4];
+		u32 int_sts;
+		} sts;
+
+		/* Wait for IRQ, then read PIH irq status (also blocking) */
+		wait_for_completion_interruptible(&irq_event);
+
+		/* read INT_STS_A, B and C in one shot using a burst read */
+		ret = twl_i2c_read(TWL_MODULE_PIH, sts.bytes,
+				REG_INT_STS_A, 3);
+		if (ret) {
+			pr_warning("twl6030: I2C error %d reading PIH ISR\n",
+					ret);
+			if (++i2c_errors >= max_i2c_errors) {
+				printk(KERN_ERR "Maximum I2C error count"
+						" exceeded.  Terminating %s.\n",
+						__func__);
+				break;
+			}
+			complete(&irq_event);
+			continue;
+		}
+
+
+
+		sts.bytes[3] = 0; /* Only 24 bits are valid*/
+
+		/*
+		 * Since VBUS status bit is not reliable for VBUS disconnect
+		 * use CHARGER VBUS detection status bit instead.
+		 */
+		if (sts.bytes[2] & 0x10)
+			sts.bytes[2] |= 0x08;
+
+		for (i = 0; sts.int_sts; sts.int_sts >>= 1, i++) {
+			local_irq_disable();
+			if (sts.int_sts & 0x1) {
+				int module_irq = twl6030_irq_base +
+					twl6030_interrupt_mapping[i];
+				generic_handle_irq(module_irq);
+
+			}
+		local_irq_enable();
+		}
+
+		/*
+		 * NOTE:
+		 * Simulation confirms that documentation is wrong w.r.t the
+		 * interrupt status clear operation. A single *byte* write to
+		 * any one of STS_A to STS_C register results in all three
+		 * STS registers being reset. Since it does not matter which
+		 * value is written, all three registers are cleared on a
+		 * single byte write, so we just use 0x0 to clear.
+		 */
+		ret = twl_i2c_write_u8(TWL_MODULE_PIH, 0x00, REG_INT_STS_A);
+		if (ret)
+			pr_warning("twl6030: I2C error in clearing PIH ISR\n");
+
+		enable_irq(irq);
+	}
+
+	return 0;
+}
+
+/*
+ * handle_twl6030_int() is the desc->handle method for the twl6030 interrupt.
+ * This is a chained interrupt, so there is no desc->action method for it.
+ * Now we need to query the interrupt controller in the twl6030 to determine
+ * which module is generating the interrupt request.  However, we can't do i2c
+ * transactions in interrupt context, so we must defer that work to a kernel
+ * thread.  All we do here is acknowledge and mask the interrupt and wakeup
+ * the kernel thread.
+ */
+static irqreturn_t handle_twl6030_pih(int irq, void *devid)
+{
+	disable_irq_nosync(irq);
+	complete(devid);
+	return IRQ_HANDLED;
+}
+
+/*----------------------------------------------------------------------*/
+
+static inline void activate_irq(int irq)
+{
+#ifdef CONFIG_ARM
+	/* ARM requires an extra step to clear IRQ_NOREQUEST, which it
+	 * sets on behalf of every irq_chip.  Also sets IRQ_NOPROBE.
+	 */
+	set_irq_flags(irq, IRQF_VALID);
+#else
+	/* same effect on other architectures */
+	irq_set_noprobe(irq);
+#endif
+}
+
+static int twl6030_irq_set_wake(struct irq_data *d, unsigned int on)
+{
+	if (on)
+		atomic_inc(&twl6030_wakeirqs);
+	else
+		atomic_dec(&twl6030_wakeirqs);
+
+	return 0;
+}
+
+int twl6030_interrupt_unmask(u8 bit_mask, u8 offset)
+{
+	int ret;
+	u8 unmask_value;
+	ret = twl_i2c_read_u8(TWL_MODULE_PIH, &unmask_value,
+			REG_INT_STS_A + offset);
+	unmask_value &= (~(bit_mask));
+	ret |= twl_i2c_write_u8(TWL_MODULE_PIH, unmask_value,
+			REG_INT_STS_A + offset); /* unmask INT_MSK_A/B/C */
+	return ret;
+}
+EXPORT_SYMBOL(twl6030_interrupt_unmask);
+
+int twl6030_interrupt_mask(u8 bit_mask, u8 offset)
+{
+	int ret;
+	u8 mask_value;
+	ret = twl_i2c_read_u8(TWL_MODULE_PIH, &mask_value,
+			REG_INT_STS_A + offset);
+	mask_value |= (bit_mask);
+	ret |= twl_i2c_write_u8(TWL_MODULE_PIH, mask_value,
+			REG_INT_STS_A + offset); /* mask INT_MSK_A/B/C */
+	return ret;
+}
+EXPORT_SYMBOL(twl6030_interrupt_mask);
+
+int twl6030_mmc_card_detect_config(void)
+{
+	int ret;
+	u8 reg_val = 0;
+
+	/* Unmasking the Card detect Interrupt line for MMC1 from Phoenix */
+	twl6030_interrupt_unmask(TWL6030_MMCDETECT_INT_MASK,
+						REG_INT_MSK_LINE_B);
+	twl6030_interrupt_unmask(TWL6030_MMCDETECT_INT_MASK,
+						REG_INT_MSK_STS_B);
+	/*
+	 * Initially Configuring MMC_CTRL for receiving interrupts &
+	 * Card status on TWL6030 for MMC1
+	 */
+	ret = twl_i2c_read_u8(TWL6030_MODULE_ID0, &reg_val, TWL6030_MMCCTRL);
+	if (ret < 0) {
+		pr_err("twl6030: Failed to read MMCCTRL, error %d\n", ret);
+		return ret;
+	}
+	reg_val &= ~VMMC_AUTO_OFF;
+	reg_val |= SW_FC;
+	ret = twl_i2c_write_u8(TWL6030_MODULE_ID0, reg_val, TWL6030_MMCCTRL);
+	if (ret < 0) {
+		pr_err("twl6030: Failed to write MMCCTRL, error %d\n", ret);
+		return ret;
+	}
+
+	/* Configuring PullUp-PullDown register */
+	ret = twl_i2c_read_u8(TWL6030_MODULE_ID0, &reg_val,
+						TWL6030_CFG_INPUT_PUPD3);
+	if (ret < 0) {
+		pr_err("twl6030: Failed to read CFG_INPUT_PUPD3, error %d\n",
+									ret);
+		return ret;
+	}
+	reg_val &= ~(MMC_PU | MMC_PD);
+	ret = twl_i2c_write_u8(TWL6030_MODULE_ID0, reg_val,
+						TWL6030_CFG_INPUT_PUPD3);
+	if (ret < 0) {
+		pr_err("twl6030: Failed to write CFG_INPUT_PUPD3, error %d\n",
+									ret);
+		return ret;
+	}
+
+	return twl6030_irq_base + MMCDETECT_INTR_OFFSET;
+}
+EXPORT_SYMBOL(twl6030_mmc_card_detect_config);
+
+int twl6030_mmc_card_detect(struct device *dev, int slot)
+{
+	int ret = -EIO;
+	u8 read_reg = 0;
+	struct platform_device *pdev = to_platform_device(dev);
+
+	if (pdev->id) {
+		/* TWL6030 provide's Card detect support for
+		 * only MMC1 controller.
+		 */
+		pr_err("Unknown MMC controller %d in %s\n", pdev->id, __func__);
+		return ret;
+	}
+	/*
+	 * BIT0 of MMC_CTRL on TWL6030 provides card status for MMC1
+	 * 0 - Card not present ,1 - Card present
+	 */
+	ret = twl_i2c_read_u8(TWL6030_MODULE_ID0, &read_reg,
+						TWL6030_MMCCTRL);
+	if (ret >= 0)
+		ret = read_reg & STS_MMC;
+	return ret;
+}
+EXPORT_SYMBOL(twl6030_mmc_card_detect);
+
+int twl6030_init_irq(struct device *dev, int irq_num)
+{
+	struct			device_node *node = dev->of_node;
+	int			nr_irqs, irq_base, irq_end;
+	struct task_struct	*task;
+	static struct irq_chip  twl6030_irq_chip;
+	int			status = 0;
+	int			i;
+	u8			mask[4];
+
+	nr_irqs = TWL6030_NR_IRQS;
+
+	irq_base = irq_alloc_descs(-1, 0, nr_irqs, 0);
+	if (IS_ERR_VALUE(irq_base)) {
+		dev_err(dev, "Fail to allocate IRQ descs\n");
+		return irq_base;
+	}
+
+	irq_domain_add_legacy(node, nr_irqs, irq_base, 0,
+			      &irq_domain_simple_ops, NULL);
+
+	irq_end = irq_base + nr_irqs;
+
+	mask[1] = 0xFF;
+	mask[2] = 0xFF;
+	mask[3] = 0xFF;
+
+	/* mask all int lines */
+	twl_i2c_write(TWL_MODULE_PIH, &mask[0], REG_INT_MSK_LINE_A, 3);
+	/* mask all int sts */
+	twl_i2c_write(TWL_MODULE_PIH, &mask[0], REG_INT_MSK_STS_A, 3);
+	/* clear INT_STS_A,B,C */
+	twl_i2c_write(TWL_MODULE_PIH, &mask[0], REG_INT_STS_A, 3);
+
+	twl6030_irq_base = irq_base;
+
+	/*
+	 * install an irq handler for each of the modules;
+	 * clone dummy irq_chip since PIH can't *do* anything
+	 */
+	twl6030_irq_chip = dummy_irq_chip;
+	twl6030_irq_chip.name = "twl6030";
+	twl6030_irq_chip.irq_set_type = NULL;
+	twl6030_irq_chip.irq_set_wake = twl6030_irq_set_wake;
+
+	for (i = irq_base; i < irq_end; i++) {
+		irq_set_chip_and_handler(i, &twl6030_irq_chip,
+					 handle_simple_irq);
+		irq_set_chip_data(i, (void *)irq_num);
+		activate_irq(i);
+	}
+
+	dev_info(dev, "PIH (irq %d) chaining IRQs %d..%d\n",
+			irq_num, irq_base, irq_end);
+
+	/* install an irq handler to demultiplex the TWL6030 interrupt */
+	init_completion(&irq_event);
+
+	status = request_irq(irq_num, handle_twl6030_pih, 0, "TWL6030-PIH",
+			     &irq_event);
+	if (status < 0) {
+		dev_err(dev, "could not claim irq %d: %d\n", irq_num, status);
+		goto fail_irq;
+	}
+
+	task = kthread_run(twl6030_irq_thread, (void *)irq_num, "twl6030-irq");
+	if (IS_ERR(task)) {
+		dev_err(dev, "could not create irq %d thread!\n", irq_num);
+		status = PTR_ERR(task);
+		goto fail_kthread;
+	}
+
+	twl_irq = irq_num;
+	register_pm_notifier(&twl6030_irq_pm_notifier_block);
+	return irq_base;
+
+fail_kthread:
+	free_irq(irq_num, &irq_event);
+
+fail_irq:
+	for (i = irq_base; i < irq_end; i++)
+		irq_set_chip_and_handler(i, NULL, NULL);
+
+	return status;
+}
+
+int twl6030_exit_irq(void)
+{
+	unregister_pm_notifier(&twl6030_irq_pm_notifier_block);
+
+	if (twl6030_irq_base) {
+		pr_err("twl6030: can't yet clean up IRQs?\n");
+		return -ENOSYS;
+	}
+	return 0;
+}
+
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/twl6030-pwm.c b/ap/os/linux/linux-3.4.x/drivers/mfd/twl6030-pwm.c
new file mode 100644
index 0000000..e8fee14
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/twl6030-pwm.c
@@ -0,0 +1,165 @@
+/*
+ * twl6030_pwm.c
+ * Driver for PHOENIX (TWL6030) Pulse Width Modulator
+ *
+ * Copyright (C) 2010 Texas Instruments
+ * Author: Hemanth V <hemanthv@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/i2c/twl.h>
+#include <linux/slab.h>
+
+#define LED_PWM_CTRL1	0xF4
+#define LED_PWM_CTRL2	0xF5
+
+/* Max value for CTRL1 register */
+#define PWM_CTRL1_MAX	255
+
+/* Pull down disable */
+#define PWM_CTRL2_DIS_PD	(1 << 6)
+
+/* Current control 2.5 milli Amps */
+#define PWM_CTRL2_CURR_02	(2 << 4)
+
+/* LED supply source */
+#define PWM_CTRL2_SRC_VAC	(1 << 2)
+
+/* LED modes */
+#define PWM_CTRL2_MODE_HW	(0 << 0)
+#define PWM_CTRL2_MODE_SW	(1 << 0)
+#define PWM_CTRL2_MODE_DIS	(2 << 0)
+
+#define PWM_CTRL2_MODE_MASK	0x3
+
+struct pwm_device {
+	const char *label;
+	unsigned int pwm_id;
+};
+
+int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+{
+	u8 duty_cycle;
+	int ret;
+
+	if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
+		return -EINVAL;
+
+	duty_cycle = (duty_ns * PWM_CTRL1_MAX) / period_ns;
+
+	ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, duty_cycle, LED_PWM_CTRL1);
+
+	if (ret < 0) {
+		pr_err("%s: Failed to configure PWM, Error %d\n",
+			pwm->label, ret);
+		return ret;
+	}
+	return 0;
+}
+EXPORT_SYMBOL(pwm_config);
+
+int pwm_enable(struct pwm_device *pwm)
+{
+	u8 val;
+	int ret;
+
+	ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, LED_PWM_CTRL2);
+	if (ret < 0) {
+		pr_err("%s: Failed to enable PWM, Error %d\n", pwm->label, ret);
+		return ret;
+	}
+
+	/* Change mode to software control */
+	val &= ~PWM_CTRL2_MODE_MASK;
+	val |= PWM_CTRL2_MODE_SW;
+
+	ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, LED_PWM_CTRL2);
+	if (ret < 0) {
+		pr_err("%s: Failed to enable PWM, Error %d\n", pwm->label, ret);
+		return ret;
+	}
+
+	twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, LED_PWM_CTRL2);
+	return 0;
+}
+EXPORT_SYMBOL(pwm_enable);
+
+void pwm_disable(struct pwm_device *pwm)
+{
+	u8 val;
+	int ret;
+
+	ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, LED_PWM_CTRL2);
+	if (ret < 0) {
+		pr_err("%s: Failed to disable PWM, Error %d\n",
+			pwm->label, ret);
+		return;
+	}
+
+	val &= ~PWM_CTRL2_MODE_MASK;
+	val |= PWM_CTRL2_MODE_HW;
+
+	ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, LED_PWM_CTRL2);
+	if (ret < 0) {
+		pr_err("%s: Failed to disable PWM, Error %d\n",
+			pwm->label, ret);
+		return;
+	}
+	return;
+}
+EXPORT_SYMBOL(pwm_disable);
+
+struct pwm_device *pwm_request(int pwm_id, const char *label)
+{
+	u8 val;
+	int ret;
+	struct pwm_device *pwm;
+
+	pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL);
+	if (pwm == NULL) {
+		pr_err("%s: failed to allocate memory\n", label);
+		return NULL;
+	}
+
+	pwm->label = label;
+	pwm->pwm_id = pwm_id;
+
+	/* Configure PWM */
+	val = PWM_CTRL2_DIS_PD | PWM_CTRL2_CURR_02 | PWM_CTRL2_SRC_VAC |
+		PWM_CTRL2_MODE_HW;
+
+	ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, LED_PWM_CTRL2);
+
+	if (ret < 0) {
+		pr_err("%s: Failed to configure PWM, Error %d\n",
+			 pwm->label, ret);
+
+		kfree(pwm);
+		return NULL;
+	}
+
+	return pwm;
+}
+EXPORT_SYMBOL(pwm_request);
+
+void pwm_free(struct pwm_device *pwm)
+{
+	pwm_disable(pwm);
+	kfree(pwm);
+}
+EXPORT_SYMBOL(pwm_free);
+
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/twl6040-core.c b/ap/os/linux/linux-3.4.x/drivers/mfd/twl6040-core.c
new file mode 100644
index 0000000..2d6beda
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/twl6040-core.c
@@ -0,0 +1,673 @@
+/*
+ * MFD driver for TWL6040 audio device
+ *
+ * Authors:	Misael Lopez Cruz <misael.lopez@ti.com>
+ *		Jorge Eduardo Candelaria <jorge.candelaria@ti.com>
+ *		Peter Ujfalusi <peter.ujfalusi@ti.com>
+ *
+ * Copyright:	(C) 2011 Texas Instruments, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/err.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/twl6040.h>
+
+#define VIBRACTRL_MEMBER(reg) ((reg == TWL6040_REG_VIBCTLL) ? 0 : 1)
+
+int twl6040_reg_read(struct twl6040 *twl6040, unsigned int reg)
+{
+	int ret;
+	unsigned int val;
+
+	mutex_lock(&twl6040->io_mutex);
+	/* Vibra control registers from cache */
+	if (unlikely(reg == TWL6040_REG_VIBCTLL ||
+		     reg == TWL6040_REG_VIBCTLR)) {
+		val = twl6040->vibra_ctrl_cache[VIBRACTRL_MEMBER(reg)];
+	} else {
+		ret = regmap_read(twl6040->regmap, reg, &val);
+		if (ret < 0) {
+			mutex_unlock(&twl6040->io_mutex);
+			return ret;
+		}
+	}
+	mutex_unlock(&twl6040->io_mutex);
+
+	return val;
+}
+EXPORT_SYMBOL(twl6040_reg_read);
+
+int twl6040_reg_write(struct twl6040 *twl6040, unsigned int reg, u8 val)
+{
+	int ret;
+
+	mutex_lock(&twl6040->io_mutex);
+	ret = regmap_write(twl6040->regmap, reg, val);
+	/* Cache the vibra control registers */
+	if (reg == TWL6040_REG_VIBCTLL || reg == TWL6040_REG_VIBCTLR)
+		twl6040->vibra_ctrl_cache[VIBRACTRL_MEMBER(reg)] = val;
+	mutex_unlock(&twl6040->io_mutex);
+
+	return ret;
+}
+EXPORT_SYMBOL(twl6040_reg_write);
+
+int twl6040_set_bits(struct twl6040 *twl6040, unsigned int reg, u8 mask)
+{
+	int ret;
+
+	mutex_lock(&twl6040->io_mutex);
+	ret = regmap_update_bits(twl6040->regmap, reg, mask, mask);
+	mutex_unlock(&twl6040->io_mutex);
+	return ret;
+}
+EXPORT_SYMBOL(twl6040_set_bits);
+
+int twl6040_clear_bits(struct twl6040 *twl6040, unsigned int reg, u8 mask)
+{
+	int ret;
+
+	mutex_lock(&twl6040->io_mutex);
+	ret = regmap_update_bits(twl6040->regmap, reg, mask, 0);
+	mutex_unlock(&twl6040->io_mutex);
+	return ret;
+}
+EXPORT_SYMBOL(twl6040_clear_bits);
+
+/* twl6040 codec manual power-up sequence */
+static int twl6040_power_up(struct twl6040 *twl6040)
+{
+	u8 ldoctl, ncpctl, lppllctl;
+	int ret;
+
+	/* enable high-side LDO, reference system and internal oscillator */
+	ldoctl = TWL6040_HSLDOENA | TWL6040_REFENA | TWL6040_OSCENA;
+	ret = twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
+	if (ret)
+		return ret;
+	usleep_range(10000, 10500);
+
+	/* enable negative charge pump */
+	ncpctl = TWL6040_NCPENA;
+	ret = twl6040_reg_write(twl6040, TWL6040_REG_NCPCTL, ncpctl);
+	if (ret)
+		goto ncp_err;
+	usleep_range(1000, 1500);
+
+	/* enable low-side LDO */
+	ldoctl |= TWL6040_LSLDOENA;
+	ret = twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
+	if (ret)
+		goto lsldo_err;
+	usleep_range(1000, 1500);
+
+	/* enable low-power PLL */
+	lppllctl = TWL6040_LPLLENA;
+	ret = twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl);
+	if (ret)
+		goto lppll_err;
+	usleep_range(5000, 5500);
+
+	/* disable internal oscillator */
+	ldoctl &= ~TWL6040_OSCENA;
+	ret = twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
+	if (ret)
+		goto osc_err;
+
+	return 0;
+
+osc_err:
+	lppllctl &= ~TWL6040_LPLLENA;
+	twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl);
+lppll_err:
+	ldoctl &= ~TWL6040_LSLDOENA;
+	twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
+lsldo_err:
+	ncpctl &= ~TWL6040_NCPENA;
+	twl6040_reg_write(twl6040, TWL6040_REG_NCPCTL, ncpctl);
+ncp_err:
+	ldoctl &= ~(TWL6040_HSLDOENA | TWL6040_REFENA | TWL6040_OSCENA);
+	twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
+
+	return ret;
+}
+
+/* twl6040 manual power-down sequence */
+static void twl6040_power_down(struct twl6040 *twl6040)
+{
+	u8 ncpctl, ldoctl, lppllctl;
+
+	ncpctl = twl6040_reg_read(twl6040, TWL6040_REG_NCPCTL);
+	ldoctl = twl6040_reg_read(twl6040, TWL6040_REG_LDOCTL);
+	lppllctl = twl6040_reg_read(twl6040, TWL6040_REG_LPPLLCTL);
+
+	/* enable internal oscillator */
+	ldoctl |= TWL6040_OSCENA;
+	twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
+	usleep_range(1000, 1500);
+
+	/* disable low-power PLL */
+	lppllctl &= ~TWL6040_LPLLENA;
+	twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl);
+
+	/* disable low-side LDO */
+	ldoctl &= ~TWL6040_LSLDOENA;
+	twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
+
+	/* disable negative charge pump */
+	ncpctl &= ~TWL6040_NCPENA;
+	twl6040_reg_write(twl6040, TWL6040_REG_NCPCTL, ncpctl);
+
+	/* disable high-side LDO, reference system and internal oscillator */
+	ldoctl &= ~(TWL6040_HSLDOENA | TWL6040_REFENA | TWL6040_OSCENA);
+	twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl);
+}
+
+static irqreturn_t twl6040_naudint_handler(int irq, void *data)
+{
+	struct twl6040 *twl6040 = data;
+	u8 intid, status;
+
+	intid = twl6040_reg_read(twl6040, TWL6040_REG_INTID);
+
+	if (intid & TWL6040_READYINT)
+		complete(&twl6040->ready);
+
+	if (intid & TWL6040_THINT) {
+		status = twl6040_reg_read(twl6040, TWL6040_REG_STATUS);
+		if (status & TWL6040_TSHUTDET) {
+			dev_warn(twl6040->dev,
+				 "Thermal shutdown, powering-off");
+			twl6040_power(twl6040, 0);
+		} else {
+			dev_warn(twl6040->dev,
+				 "Leaving thermal shutdown, powering-on");
+			twl6040_power(twl6040, 1);
+		}
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int twl6040_power_up_completion(struct twl6040 *twl6040,
+				       int naudint)
+{
+	int time_left;
+	u8 intid;
+
+	time_left = wait_for_completion_timeout(&twl6040->ready,
+						msecs_to_jiffies(144));
+	if (!time_left) {
+		intid = twl6040_reg_read(twl6040, TWL6040_REG_INTID);
+		if (!(intid & TWL6040_READYINT)) {
+			dev_err(twl6040->dev,
+				"timeout waiting for READYINT\n");
+			return -ETIMEDOUT;
+		}
+	}
+
+	return 0;
+}
+
+int twl6040_power(struct twl6040 *twl6040, int on)
+{
+	int audpwron = twl6040->audpwron;
+	int naudint = twl6040->irq;
+	int ret = 0;
+
+	mutex_lock(&twl6040->mutex);
+
+	if (on) {
+		/* already powered-up */
+		if (twl6040->power_count++)
+			goto out;
+
+		if (gpio_is_valid(audpwron)) {
+			/* use AUDPWRON line */
+			gpio_set_value(audpwron, 1);
+			/* wait for power-up completion */
+			ret = twl6040_power_up_completion(twl6040, naudint);
+			if (ret) {
+				dev_err(twl6040->dev,
+					"automatic power-down failed\n");
+				twl6040->power_count = 0;
+				goto out;
+			}
+		} else {
+			/* use manual power-up sequence */
+			ret = twl6040_power_up(twl6040);
+			if (ret) {
+				dev_err(twl6040->dev,
+					"manual power-up failed\n");
+				twl6040->power_count = 0;
+				goto out;
+			}
+		}
+		/* Default PLL configuration after power up */
+		twl6040->pll = TWL6040_SYSCLK_SEL_LPPLL;
+		twl6040->sysclk = 19200000;
+		twl6040->mclk = 32768;
+	} else {
+		/* already powered-down */
+		if (!twl6040->power_count) {
+			dev_err(twl6040->dev,
+				"device is already powered-off\n");
+			ret = -EPERM;
+			goto out;
+		}
+
+		if (--twl6040->power_count)
+			goto out;
+
+		if (gpio_is_valid(audpwron)) {
+			/* use AUDPWRON line */
+			gpio_set_value(audpwron, 0);
+
+			/* power-down sequence latency */
+			usleep_range(500, 700);
+		} else {
+			/* use manual power-down sequence */
+			twl6040_power_down(twl6040);
+		}
+		twl6040->sysclk = 0;
+		twl6040->mclk = 0;
+	}
+
+out:
+	mutex_unlock(&twl6040->mutex);
+	return ret;
+}
+EXPORT_SYMBOL(twl6040_power);
+
+int twl6040_set_pll(struct twl6040 *twl6040, int pll_id,
+		    unsigned int freq_in, unsigned int freq_out)
+{
+	u8 hppllctl, lppllctl;
+	int ret = 0;
+
+	mutex_lock(&twl6040->mutex);
+
+	hppllctl = twl6040_reg_read(twl6040, TWL6040_REG_HPPLLCTL);
+	lppllctl = twl6040_reg_read(twl6040, TWL6040_REG_LPPLLCTL);
+
+	/* Force full reconfiguration when switching between PLL */
+	if (pll_id != twl6040->pll) {
+		twl6040->sysclk = 0;
+		twl6040->mclk = 0;
+	}
+
+	switch (pll_id) {
+	case TWL6040_SYSCLK_SEL_LPPLL:
+		/* low-power PLL divider */
+		/* Change the sysclk configuration only if it has been canged */
+		if (twl6040->sysclk != freq_out) {
+			switch (freq_out) {
+			case 17640000:
+				lppllctl |= TWL6040_LPLLFIN;
+				break;
+			case 19200000:
+				lppllctl &= ~TWL6040_LPLLFIN;
+				break;
+			default:
+				dev_err(twl6040->dev,
+					"freq_out %d not supported\n",
+					freq_out);
+				ret = -EINVAL;
+				goto pll_out;
+			}
+			twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL,
+					  lppllctl);
+		}
+
+		/* The PLL in use has not been change, we can exit */
+		if (twl6040->pll == pll_id)
+			break;
+
+		switch (freq_in) {
+		case 32768:
+			lppllctl |= TWL6040_LPLLENA;
+			twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL,
+					  lppllctl);
+			mdelay(5);
+			lppllctl &= ~TWL6040_HPLLSEL;
+			twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL,
+					  lppllctl);
+			hppllctl &= ~TWL6040_HPLLENA;
+			twl6040_reg_write(twl6040, TWL6040_REG_HPPLLCTL,
+					  hppllctl);
+			break;
+		default:
+			dev_err(twl6040->dev,
+				"freq_in %d not supported\n", freq_in);
+			ret = -EINVAL;
+			goto pll_out;
+		}
+		break;
+	case TWL6040_SYSCLK_SEL_HPPLL:
+		/* high-performance PLL can provide only 19.2 MHz */
+		if (freq_out != 19200000) {
+			dev_err(twl6040->dev,
+				"freq_out %d not supported\n", freq_out);
+			ret = -EINVAL;
+			goto pll_out;
+		}
+
+		if (twl6040->mclk != freq_in) {
+			hppllctl &= ~TWL6040_MCLK_MSK;
+
+			switch (freq_in) {
+			case 12000000:
+				/* PLL enabled, active mode */
+				hppllctl |= TWL6040_MCLK_12000KHZ |
+					    TWL6040_HPLLENA;
+				break;
+			case 19200000:
+				/*
+				* PLL disabled
+				* (enable PLL if MCLK jitter quality
+				*  doesn't meet specification)
+				*/
+				hppllctl |= TWL6040_MCLK_19200KHZ;
+				break;
+			case 26000000:
+				/* PLL enabled, active mode */
+				hppllctl |= TWL6040_MCLK_26000KHZ |
+					    TWL6040_HPLLENA;
+				break;
+			case 38400000:
+				/* PLL enabled, active mode */
+				hppllctl |= TWL6040_MCLK_38400KHZ |
+					    TWL6040_HPLLENA;
+				break;
+			default:
+				dev_err(twl6040->dev,
+					"freq_in %d not supported\n", freq_in);
+				ret = -EINVAL;
+				goto pll_out;
+			}
+
+			/*
+			 * enable clock slicer to ensure input waveform is
+			 * square
+			 */
+			hppllctl |= TWL6040_HPLLSQRENA;
+
+			twl6040_reg_write(twl6040, TWL6040_REG_HPPLLCTL,
+					  hppllctl);
+			usleep_range(500, 700);
+			lppllctl |= TWL6040_HPLLSEL;
+			twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL,
+					  lppllctl);
+			lppllctl &= ~TWL6040_LPLLENA;
+			twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL,
+					  lppllctl);
+		}
+		break;
+	default:
+		dev_err(twl6040->dev, "unknown pll id %d\n", pll_id);
+		ret = -EINVAL;
+		goto pll_out;
+	}
+
+	twl6040->sysclk = freq_out;
+	twl6040->mclk = freq_in;
+	twl6040->pll = pll_id;
+
+pll_out:
+	mutex_unlock(&twl6040->mutex);
+	return ret;
+}
+EXPORT_SYMBOL(twl6040_set_pll);
+
+int twl6040_get_pll(struct twl6040 *twl6040)
+{
+	if (twl6040->power_count)
+		return twl6040->pll;
+	else
+		return -ENODEV;
+}
+EXPORT_SYMBOL(twl6040_get_pll);
+
+unsigned int twl6040_get_sysclk(struct twl6040 *twl6040)
+{
+	return twl6040->sysclk;
+}
+EXPORT_SYMBOL(twl6040_get_sysclk);
+
+/* Get the combined status of the vibra control register */
+int twl6040_get_vibralr_status(struct twl6040 *twl6040)
+{
+	u8 status;
+
+	status = twl6040->vibra_ctrl_cache[0] | twl6040->vibra_ctrl_cache[1];
+	status &= (TWL6040_VIBENA | TWL6040_VIBSEL);
+
+	return status;
+}
+EXPORT_SYMBOL(twl6040_get_vibralr_status);
+
+static struct resource twl6040_vibra_rsrc[] = {
+	{
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct resource twl6040_codec_rsrc[] = {
+	{
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static bool twl6040_readable_reg(struct device *dev, unsigned int reg)
+{
+	/* Register 0 is not readable */
+	if (!reg)
+		return false;
+	return true;
+}
+
+static struct regmap_config twl6040_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.max_register = TWL6040_REG_STATUS, /* 0x2e */
+
+	.readable_reg = twl6040_readable_reg,
+};
+
+static int __devinit twl6040_probe(struct i2c_client *client,
+				     const struct i2c_device_id *id)
+{
+	struct twl6040_platform_data *pdata = client->dev.platform_data;
+	struct twl6040 *twl6040;
+	struct mfd_cell *cell = NULL;
+	int ret, children = 0;
+
+	if (!pdata) {
+		dev_err(&client->dev, "Platform data is missing\n");
+		return -EINVAL;
+	}
+
+	/* In order to operate correctly we need valid interrupt config */
+	if (!client->irq || !pdata->irq_base) {
+		dev_err(&client->dev, "Invalid IRQ configuration\n");
+		return -EINVAL;
+	}
+
+	twl6040 = devm_kzalloc(&client->dev, sizeof(struct twl6040),
+			       GFP_KERNEL);
+	if (!twl6040) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	twl6040->regmap = regmap_init_i2c(client, &twl6040_regmap_config);
+	if (IS_ERR(twl6040->regmap)) {
+		ret = PTR_ERR(twl6040->regmap);
+		goto err;
+	}
+
+	i2c_set_clientdata(client, twl6040);
+
+	twl6040->dev = &client->dev;
+	twl6040->irq = client->irq;
+	twl6040->irq_base = pdata->irq_base;
+
+	mutex_init(&twl6040->mutex);
+	mutex_init(&twl6040->io_mutex);
+	init_completion(&twl6040->ready);
+
+	twl6040->rev = twl6040_reg_read(twl6040, TWL6040_REG_ASICREV);
+
+	/* ERRATA: Automatic power-up is not possible in ES1.0 */
+	if (twl6040_get_revid(twl6040) > TWL6040_REV_ES1_0)
+		twl6040->audpwron = pdata->audpwron_gpio;
+	else
+		twl6040->audpwron = -EINVAL;
+
+	if (gpio_is_valid(twl6040->audpwron)) {
+		ret = gpio_request_one(twl6040->audpwron, GPIOF_OUT_INIT_LOW,
+				       "audpwron");
+		if (ret)
+			goto gpio1_err;
+	}
+
+	/* codec interrupt */
+	ret = twl6040_irq_init(twl6040);
+	if (ret)
+		goto gpio2_err;
+
+	ret = request_threaded_irq(twl6040->irq_base + TWL6040_IRQ_READY,
+				   NULL, twl6040_naudint_handler, 0,
+				   "twl6040_irq_ready", twl6040);
+	if (ret) {
+		dev_err(twl6040->dev, "READY IRQ request failed: %d\n",
+			ret);
+		goto irq_err;
+	}
+
+	/* dual-access registers controlled by I2C only */
+	twl6040_set_bits(twl6040, TWL6040_REG_ACCCTL, TWL6040_I2CSEL);
+
+	if (pdata->codec) {
+		int irq = twl6040->irq_base + TWL6040_IRQ_PLUG;
+
+		cell = &twl6040->cells[children];
+		cell->name = "twl6040-codec";
+		twl6040_codec_rsrc[0].start = irq;
+		twl6040_codec_rsrc[0].end = irq;
+		cell->resources = twl6040_codec_rsrc;
+		cell->num_resources = ARRAY_SIZE(twl6040_codec_rsrc);
+		cell->platform_data = pdata->codec;
+		cell->pdata_size = sizeof(*pdata->codec);
+		children++;
+	}
+
+	if (pdata->vibra) {
+		int irq = twl6040->irq_base + TWL6040_IRQ_VIB;
+
+		cell = &twl6040->cells[children];
+		cell->name = "twl6040-vibra";
+		twl6040_vibra_rsrc[0].start = irq;
+		twl6040_vibra_rsrc[0].end = irq;
+		cell->resources = twl6040_vibra_rsrc;
+		cell->num_resources = ARRAY_SIZE(twl6040_vibra_rsrc);
+
+		cell->platform_data = pdata->vibra;
+		cell->pdata_size = sizeof(*pdata->vibra);
+		children++;
+	}
+
+	if (children) {
+		ret = mfd_add_devices(&client->dev, -1, twl6040->cells,
+				      children, NULL, 0);
+		if (ret)
+			goto mfd_err;
+	} else {
+		dev_err(&client->dev, "No platform data found for children\n");
+		ret = -ENODEV;
+		goto mfd_err;
+	}
+
+	return 0;
+
+mfd_err:
+	free_irq(twl6040->irq_base + TWL6040_IRQ_READY, twl6040);
+irq_err:
+	twl6040_irq_exit(twl6040);
+gpio2_err:
+	if (gpio_is_valid(twl6040->audpwron))
+		gpio_free(twl6040->audpwron);
+gpio1_err:
+	i2c_set_clientdata(client, NULL);
+	regmap_exit(twl6040->regmap);
+err:
+	return ret;
+}
+
+static int __devexit twl6040_remove(struct i2c_client *client)
+{
+	struct twl6040 *twl6040 = i2c_get_clientdata(client);
+
+	if (twl6040->power_count)
+		twl6040_power(twl6040, 0);
+
+	if (gpio_is_valid(twl6040->audpwron))
+		gpio_free(twl6040->audpwron);
+
+	free_irq(twl6040->irq_base + TWL6040_IRQ_READY, twl6040);
+	twl6040_irq_exit(twl6040);
+
+	mfd_remove_devices(&client->dev);
+	i2c_set_clientdata(client, NULL);
+	regmap_exit(twl6040->regmap);
+
+	return 0;
+}
+
+static const struct i2c_device_id twl6040_i2c_id[] = {
+	{ "twl6040", 0, },
+	{ },
+};
+MODULE_DEVICE_TABLE(i2c, twl6040_i2c_id);
+
+static struct i2c_driver twl6040_driver = {
+	.driver = {
+		.name = "twl6040",
+		.owner = THIS_MODULE,
+	},
+	.probe		= twl6040_probe,
+	.remove		= __devexit_p(twl6040_remove),
+	.id_table	= twl6040_i2c_id,
+};
+
+module_i2c_driver(twl6040_driver);
+
+MODULE_DESCRIPTION("TWL6040 MFD");
+MODULE_AUTHOR("Misael Lopez Cruz <misael.lopez@ti.com>");
+MODULE_AUTHOR("Jorge Eduardo Candelaria <jorge.candelaria@ti.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:twl6040");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/twl6040-irq.c b/ap/os/linux/linux-3.4.x/drivers/mfd/twl6040-irq.c
new file mode 100644
index 0000000..b3f8dda
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/twl6040-irq.c
@@ -0,0 +1,191 @@
+/*
+ * Interrupt controller support for TWL6040
+ *
+ * Author:     Misael Lopez Cruz <misael.lopez@ti.com>
+ *
+ * Copyright:   (C) 2011 Texas Instruments, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/twl6040.h>
+
+struct twl6040_irq_data {
+	int mask;
+	int status;
+};
+
+static struct twl6040_irq_data twl6040_irqs[] = {
+	{
+		.mask = TWL6040_THMSK,
+		.status = TWL6040_THINT,
+	},
+	{
+		.mask = TWL6040_PLUGMSK,
+		.status = TWL6040_PLUGINT | TWL6040_UNPLUGINT,
+	},
+	{
+		.mask = TWL6040_HOOKMSK,
+		.status = TWL6040_HOOKINT,
+	},
+	{
+		.mask = TWL6040_HFMSK,
+		.status = TWL6040_HFINT,
+	},
+	{
+		.mask = TWL6040_VIBMSK,
+		.status = TWL6040_VIBINT,
+	},
+	{
+		.mask = TWL6040_READYMSK,
+		.status = TWL6040_READYINT,
+	},
+};
+
+static inline
+struct twl6040_irq_data *irq_to_twl6040_irq(struct twl6040 *twl6040,
+					    int irq)
+{
+	return &twl6040_irqs[irq - twl6040->irq_base];
+}
+
+static void twl6040_irq_lock(struct irq_data *data)
+{
+	struct twl6040 *twl6040 = irq_data_get_irq_chip_data(data);
+
+	mutex_lock(&twl6040->irq_mutex);
+}
+
+static void twl6040_irq_sync_unlock(struct irq_data *data)
+{
+	struct twl6040 *twl6040 = irq_data_get_irq_chip_data(data);
+
+	/* write back to hardware any change in irq mask */
+	if (twl6040->irq_masks_cur != twl6040->irq_masks_cache) {
+		twl6040->irq_masks_cache = twl6040->irq_masks_cur;
+		twl6040_reg_write(twl6040, TWL6040_REG_INTMR,
+				  twl6040->irq_masks_cur);
+	}
+
+	mutex_unlock(&twl6040->irq_mutex);
+}
+
+static void twl6040_irq_enable(struct irq_data *data)
+{
+	struct twl6040 *twl6040 = irq_data_get_irq_chip_data(data);
+	struct twl6040_irq_data *irq_data = irq_to_twl6040_irq(twl6040,
+							       data->irq);
+
+	twl6040->irq_masks_cur &= ~irq_data->mask;
+}
+
+static void twl6040_irq_disable(struct irq_data *data)
+{
+	struct twl6040 *twl6040 = irq_data_get_irq_chip_data(data);
+	struct twl6040_irq_data *irq_data = irq_to_twl6040_irq(twl6040,
+							       data->irq);
+
+	twl6040->irq_masks_cur |= irq_data->mask;
+}
+
+static struct irq_chip twl6040_irq_chip = {
+	.name			= "twl6040",
+	.irq_bus_lock		= twl6040_irq_lock,
+	.irq_bus_sync_unlock	= twl6040_irq_sync_unlock,
+	.irq_enable		= twl6040_irq_enable,
+	.irq_disable		= twl6040_irq_disable,
+};
+
+static irqreturn_t twl6040_irq_thread(int irq, void *data)
+{
+	struct twl6040 *twl6040 = data;
+	u8 intid;
+	int i;
+
+	intid = twl6040_reg_read(twl6040, TWL6040_REG_INTID);
+
+	/* apply masking and report (backwards to handle READYINT first) */
+	for (i = ARRAY_SIZE(twl6040_irqs) - 1; i >= 0; i--) {
+		if (twl6040->irq_masks_cur & twl6040_irqs[i].mask)
+			intid &= ~twl6040_irqs[i].status;
+		if (intid & twl6040_irqs[i].status)
+			handle_nested_irq(twl6040->irq_base + i);
+	}
+
+	/* ack unmasked irqs */
+	twl6040_reg_write(twl6040, TWL6040_REG_INTID, intid);
+
+	return IRQ_HANDLED;
+}
+
+int twl6040_irq_init(struct twl6040 *twl6040)
+{
+	int cur_irq, ret;
+	u8 val;
+
+	mutex_init(&twl6040->irq_mutex);
+
+	/* mask the individual interrupt sources */
+	twl6040->irq_masks_cur = TWL6040_ALLINT_MSK;
+	twl6040->irq_masks_cache = TWL6040_ALLINT_MSK;
+	twl6040_reg_write(twl6040, TWL6040_REG_INTMR, TWL6040_ALLINT_MSK);
+
+	/* Register them with genirq */
+	for (cur_irq = twl6040->irq_base;
+	     cur_irq < twl6040->irq_base + ARRAY_SIZE(twl6040_irqs);
+	     cur_irq++) {
+		irq_set_chip_data(cur_irq, twl6040);
+		irq_set_chip_and_handler(cur_irq, &twl6040_irq_chip,
+					 handle_level_irq);
+		irq_set_nested_thread(cur_irq, 1);
+
+		/* ARM needs us to explicitly flag the IRQ as valid
+		 * and will set them noprobe when we do so. */
+#ifdef CONFIG_ARM
+		set_irq_flags(cur_irq, IRQF_VALID);
+#else
+		irq_set_noprobe(cur_irq);
+#endif
+	}
+
+	ret = request_threaded_irq(twl6040->irq, NULL, twl6040_irq_thread,
+				   IRQF_ONESHOT, "twl6040", twl6040);
+	if (ret) {
+		dev_err(twl6040->dev, "failed to request IRQ %d: %d\n",
+			twl6040->irq, ret);
+		return ret;
+	}
+
+	/* reset interrupts */
+	val = twl6040_reg_read(twl6040, TWL6040_REG_INTID);
+
+	/* interrupts cleared on write */
+	twl6040_clear_bits(twl6040, TWL6040_REG_ACCCTL, TWL6040_INTCLRMODE);
+
+	return 0;
+}
+EXPORT_SYMBOL(twl6040_irq_init);
+
+void twl6040_irq_exit(struct twl6040 *twl6040)
+{
+	free_irq(twl6040->irq, twl6040);
+}
+EXPORT_SYMBOL(twl6040_irq_exit);
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/ucb1400_core.c b/ap/os/linux/linux-3.4.x/drivers/mfd/ucb1400_core.c
new file mode 100644
index 0000000..daf6952
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/ucb1400_core.c
@@ -0,0 +1,158 @@
+/*
+ * Core functions for:
+ *  Philips UCB1400 multifunction chip
+ *
+ * Based on ucb1400_ts.c:
+ *  Author:	Nicolas Pitre
+ *  Created:	September 25, 2006
+ *  Copyright:	MontaVista Software, Inc.
+ *
+ * Spliting done by: Marek Vasut <marek.vasut@gmail.com>
+ * If something doesn't work and it worked before spliting, e-mail me,
+ * dont bother Nicolas please ;-)
+ *
+ * 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 code is heavily based on ucb1x00-*.c copyrighted by Russell King
+ * covering the UCB1100, UCB1200 and UCB1300..  Support for the UCB1400 has
+ * been made separate from ucb1x00-core/ucb1x00-ts on Russell's request.
+ */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/ucb1400.h>
+
+unsigned int ucb1400_adc_read(struct snd_ac97 *ac97, u16 adc_channel,
+		int adcsync)
+{
+	unsigned int val;
+
+	if (adcsync)
+		adc_channel |= UCB_ADC_SYNC_ENA;
+
+	ucb1400_reg_write(ac97, UCB_ADC_CR, UCB_ADC_ENA | adc_channel);
+	ucb1400_reg_write(ac97, UCB_ADC_CR, UCB_ADC_ENA | adc_channel |
+			UCB_ADC_START);
+
+	while (!((val = ucb1400_reg_read(ac97, UCB_ADC_DATA))
+				& UCB_ADC_DAT_VALID))
+		schedule_timeout_uninterruptible(1);
+
+	return val & UCB_ADC_DAT_MASK;
+}
+EXPORT_SYMBOL_GPL(ucb1400_adc_read);
+
+static int ucb1400_core_probe(struct device *dev)
+{
+	int err;
+	struct ucb1400 *ucb;
+	struct ucb1400_ts ucb_ts;
+	struct ucb1400_gpio ucb_gpio;
+	struct snd_ac97 *ac97;
+	struct ucb1400_pdata *pdata = dev->platform_data;
+
+	memset(&ucb_ts, 0, sizeof(ucb_ts));
+	memset(&ucb_gpio, 0, sizeof(ucb_gpio));
+
+	ucb = kzalloc(sizeof(struct ucb1400), GFP_KERNEL);
+	if (!ucb) {
+		err = -ENOMEM;
+		goto err;
+	}
+
+	dev_set_drvdata(dev, ucb);
+
+	ac97 = to_ac97_t(dev);
+
+	ucb_ts.id = ucb1400_reg_read(ac97, UCB_ID);
+	if (ucb_ts.id != UCB_ID_1400) {
+		err = -ENODEV;
+		goto err0;
+	}
+
+	/* GPIO */
+	ucb_gpio.ac97 = ac97;
+	ucb->ucb1400_gpio = platform_device_alloc("ucb1400_gpio", -1);
+	if (!ucb->ucb1400_gpio) {
+		err = -ENOMEM;
+		goto err0;
+	}
+	err = platform_device_add_data(ucb->ucb1400_gpio, &ucb_gpio,
+					sizeof(ucb_gpio));
+	if (err)
+		goto err1;
+	err = platform_device_add(ucb->ucb1400_gpio);
+	if (err)
+		goto err1;
+
+	/* TOUCHSCREEN */
+	ucb_ts.ac97 = ac97;
+
+	if (pdata != NULL && pdata->irq >= 0)
+		ucb_ts.irq = pdata->irq;
+	else
+		ucb_ts.irq = -1;
+
+	ucb->ucb1400_ts = platform_device_alloc("ucb1400_ts", -1);
+	if (!ucb->ucb1400_ts) {
+		err = -ENOMEM;
+		goto err2;
+	}
+	err = platform_device_add_data(ucb->ucb1400_ts, &ucb_ts,
+					sizeof(ucb_ts));
+	if (err)
+		goto err3;
+	err = platform_device_add(ucb->ucb1400_ts);
+	if (err)
+		goto err3;
+
+	return 0;
+
+err3:
+	platform_device_put(ucb->ucb1400_ts);
+err2:
+	platform_device_del(ucb->ucb1400_gpio);
+err1:
+	platform_device_put(ucb->ucb1400_gpio);
+err0:
+	kfree(ucb);
+err:
+	return err;
+}
+
+static int ucb1400_core_remove(struct device *dev)
+{
+	struct ucb1400 *ucb = dev_get_drvdata(dev);
+
+	platform_device_unregister(ucb->ucb1400_ts);
+	platform_device_unregister(ucb->ucb1400_gpio);
+
+	kfree(ucb);
+	return 0;
+}
+
+static struct device_driver ucb1400_core_driver = {
+	.name	= "ucb1400_core",
+	.bus	= &ac97_bus_type,
+	.probe	= ucb1400_core_probe,
+	.remove	= ucb1400_core_remove,
+};
+
+static int __init ucb1400_core_init(void)
+{
+	return driver_register(&ucb1400_core_driver);
+}
+
+static void __exit ucb1400_core_exit(void)
+{
+	driver_unregister(&ucb1400_core_driver);
+}
+
+module_init(ucb1400_core_init);
+module_exit(ucb1400_core_exit);
+
+MODULE_DESCRIPTION("Philips UCB1400 driver");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/ucb1x00-assabet.c b/ap/os/linux/linux-3.4.x/drivers/mfd/ucb1x00-assabet.c
new file mode 100644
index 0000000..b63c075
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/ucb1x00-assabet.c
@@ -0,0 +1,106 @@
+/*
+ *  linux/drivers/mfd/ucb1x00-assabet.c
+ *
+ *  Copyright (C) 2001-2003 Russell King, All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License.
+ *
+ *  We handle the machine-specific bits of the UCB1x00 driver here.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/fs.h>
+#include <linux/gpio_keys.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <linux/proc_fs.h>
+#include <linux/mfd/ucb1x00.h>
+
+#define UCB1X00_ATTR(name,input)\
+static ssize_t name##_show(struct device *dev, struct device_attribute *attr, \
+			   char *buf)	\
+{								\
+	struct ucb1x00 *ucb = classdev_to_ucb1x00(dev);		\
+	int val;						\
+	ucb1x00_adc_enable(ucb);				\
+	val = ucb1x00_adc_read(ucb, input, UCB_NOSYNC);		\
+	ucb1x00_adc_disable(ucb);				\
+	return sprintf(buf, "%d\n", val);			\
+}								\
+static DEVICE_ATTR(name,0444,name##_show,NULL)
+
+UCB1X00_ATTR(vbatt, UCB_ADC_INP_AD1);
+UCB1X00_ATTR(vcharger, UCB_ADC_INP_AD0);
+UCB1X00_ATTR(batt_temp, UCB_ADC_INP_AD2);
+
+static int ucb1x00_assabet_add(struct ucb1x00_dev *dev)
+{
+	struct ucb1x00 *ucb = dev->ucb;
+	struct platform_device *pdev;
+	struct gpio_keys_platform_data keys;
+	static struct gpio_keys_button buttons[6];
+	unsigned i;
+
+	memset(buttons, 0, sizeof(buttons));
+	memset(&keys, 0, sizeof(keys));
+
+	for (i = 0; i < ARRAY_SIZE(buttons); i++) {
+		buttons[i].code = BTN_0 + i;
+		buttons[i].gpio = ucb->gpio.base + i;
+		buttons[i].type = EV_KEY;
+		buttons[i].can_disable = true;
+	}
+
+	keys.buttons = buttons;
+	keys.nbuttons = ARRAY_SIZE(buttons);
+	keys.poll_interval = 50;
+	keys.name = "ucb1x00";
+
+	pdev = platform_device_register_data(&ucb->dev, "gpio-keys", -1,
+		&keys, sizeof(keys));
+
+	device_create_file(&ucb->dev, &dev_attr_vbatt);
+	device_create_file(&ucb->dev, &dev_attr_vcharger);
+	device_create_file(&ucb->dev, &dev_attr_batt_temp);
+
+	dev->priv = pdev;
+	return 0;
+}
+
+static void ucb1x00_assabet_remove(struct ucb1x00_dev *dev)
+{
+	struct platform_device *pdev = dev->priv;
+
+	if (!IS_ERR(pdev))
+		platform_device_unregister(pdev);
+
+	device_remove_file(&dev->ucb->dev, &dev_attr_batt_temp);
+	device_remove_file(&dev->ucb->dev, &dev_attr_vcharger);
+	device_remove_file(&dev->ucb->dev, &dev_attr_vbatt);
+}
+
+static struct ucb1x00_driver ucb1x00_assabet_driver = {
+	.add	= ucb1x00_assabet_add,
+	.remove	= ucb1x00_assabet_remove,
+};
+
+static int __init ucb1x00_assabet_init(void)
+{
+	return ucb1x00_register_driver(&ucb1x00_assabet_driver);
+}
+
+static void __exit ucb1x00_assabet_exit(void)
+{
+	ucb1x00_unregister_driver(&ucb1x00_assabet_driver);
+}
+
+module_init(ucb1x00_assabet_init);
+module_exit(ucb1x00_assabet_exit);
+
+MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
+MODULE_DESCRIPTION("Assabet noddy testing only example ADC driver");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/ucb1x00-core.c b/ap/os/linux/linux-3.4.x/drivers/mfd/ucb1x00-core.c
new file mode 100644
index 0000000..70f02da
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/ucb1x00-core.c
@@ -0,0 +1,788 @@
+/*
+ *  linux/drivers/mfd/ucb1x00-core.c
+ *
+ *  Copyright (C) 2001 Russell King, All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License.
+ *
+ *  The UCB1x00 core driver provides basic services for handling IO,
+ *  the ADC, interrupts, and accessing registers.  It is designed
+ *  such that everything goes through this layer, thereby providing
+ *  a consistent locking methodology, as well as allowing the drivers
+ *  to be used on other non-MCP-enabled hardware platforms.
+ *
+ *  Note that all locks are private to this file.  Nothing else may
+ *  touch them.
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/mfd/ucb1x00.h>
+#include <linux/pm.h>
+#include <linux/gpio.h>
+
+static DEFINE_MUTEX(ucb1x00_mutex);
+static LIST_HEAD(ucb1x00_drivers);
+static LIST_HEAD(ucb1x00_devices);
+
+/**
+ *	ucb1x00_io_set_dir - set IO direction
+ *	@ucb: UCB1x00 structure describing chip
+ *	@in:  bitfield of IO pins to be set as inputs
+ *	@out: bitfield of IO pins to be set as outputs
+ *
+ *	Set the IO direction of the ten general purpose IO pins on
+ *	the UCB1x00 chip.  The @in bitfield has priority over the
+ *	@out bitfield, in that if you specify a pin as both input
+ *	and output, it will end up as an input.
+ *
+ *	ucb1x00_enable must have been called to enable the comms
+ *	before using this function.
+ *
+ *	This function takes a spinlock, disabling interrupts.
+ */
+void ucb1x00_io_set_dir(struct ucb1x00 *ucb, unsigned int in, unsigned int out)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&ucb->io_lock, flags);
+	ucb->io_dir |= out;
+	ucb->io_dir &= ~in;
+
+	ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir);
+	spin_unlock_irqrestore(&ucb->io_lock, flags);
+}
+
+/**
+ *	ucb1x00_io_write - set or clear IO outputs
+ *	@ucb:   UCB1x00 structure describing chip
+ *	@set:   bitfield of IO pins to set to logic '1'
+ *	@clear: bitfield of IO pins to set to logic '0'
+ *
+ *	Set the IO output state of the specified IO pins.  The value
+ *	is retained if the pins are subsequently configured as inputs.
+ *	The @clear bitfield has priority over the @set bitfield -
+ *	outputs will be cleared.
+ *
+ *	ucb1x00_enable must have been called to enable the comms
+ *	before using this function.
+ *
+ *	This function takes a spinlock, disabling interrupts.
+ */
+void ucb1x00_io_write(struct ucb1x00 *ucb, unsigned int set, unsigned int clear)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&ucb->io_lock, flags);
+	ucb->io_out |= set;
+	ucb->io_out &= ~clear;
+
+	ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out);
+	spin_unlock_irqrestore(&ucb->io_lock, flags);
+}
+
+/**
+ *	ucb1x00_io_read - read the current state of the IO pins
+ *	@ucb: UCB1x00 structure describing chip
+ *
+ *	Return a bitfield describing the logic state of the ten
+ *	general purpose IO pins.
+ *
+ *	ucb1x00_enable must have been called to enable the comms
+ *	before using this function.
+ *
+ *	This function does not take any mutexes or spinlocks.
+ */
+unsigned int ucb1x00_io_read(struct ucb1x00 *ucb)
+{
+	return ucb1x00_reg_read(ucb, UCB_IO_DATA);
+}
+
+static void ucb1x00_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+	struct ucb1x00 *ucb = container_of(chip, struct ucb1x00, gpio);
+	unsigned long flags;
+
+	spin_lock_irqsave(&ucb->io_lock, flags);
+	if (value)
+		ucb->io_out |= 1 << offset;
+	else
+		ucb->io_out &= ~(1 << offset);
+
+	ucb1x00_enable(ucb);
+	ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out);
+	ucb1x00_disable(ucb);
+	spin_unlock_irqrestore(&ucb->io_lock, flags);
+}
+
+static int ucb1x00_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+	struct ucb1x00 *ucb = container_of(chip, struct ucb1x00, gpio);
+	unsigned val;
+
+	ucb1x00_enable(ucb);
+	val = ucb1x00_reg_read(ucb, UCB_IO_DATA);
+	ucb1x00_disable(ucb);
+
+	return val & (1 << offset);
+}
+
+static int ucb1x00_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+	struct ucb1x00 *ucb = container_of(chip, struct ucb1x00, gpio);
+	unsigned long flags;
+
+	spin_lock_irqsave(&ucb->io_lock, flags);
+	ucb->io_dir &= ~(1 << offset);
+	ucb1x00_enable(ucb);
+	ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir);
+	ucb1x00_disable(ucb);
+	spin_unlock_irqrestore(&ucb->io_lock, flags);
+
+	return 0;
+}
+
+static int ucb1x00_gpio_direction_output(struct gpio_chip *chip, unsigned offset
+		, int value)
+{
+	struct ucb1x00 *ucb = container_of(chip, struct ucb1x00, gpio);
+	unsigned long flags;
+	unsigned old, mask = 1 << offset;
+
+	spin_lock_irqsave(&ucb->io_lock, flags);
+	old = ucb->io_out;
+	if (value)
+		ucb->io_out |= mask;
+	else
+		ucb->io_out &= ~mask;
+
+	ucb1x00_enable(ucb);
+	if (old != ucb->io_out)
+		ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out);
+
+	if (!(ucb->io_dir & mask)) {
+		ucb->io_dir |= mask;
+		ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir);
+	}
+	ucb1x00_disable(ucb);
+	spin_unlock_irqrestore(&ucb->io_lock, flags);
+
+	return 0;
+}
+
+static int ucb1x00_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+	struct ucb1x00 *ucb = container_of(chip, struct ucb1x00, gpio);
+
+	return ucb->irq_base > 0 ? ucb->irq_base + offset : -ENXIO;
+}
+
+/*
+ * UCB1300 data sheet says we must:
+ *  1. enable ADC	=> 5us (including reference startup time)
+ *  2. select input	=> 51*tsibclk  => 4.3us
+ *  3. start conversion	=> 102*tsibclk => 8.5us
+ * (tsibclk = 1/11981000)
+ * Period between SIB 128-bit frames = 10.7us
+ */
+
+/**
+ *	ucb1x00_adc_enable - enable the ADC converter
+ *	@ucb: UCB1x00 structure describing chip
+ *
+ *	Enable the ucb1x00 and ADC converter on the UCB1x00 for use.
+ *	Any code wishing to use the ADC converter must call this
+ *	function prior to using it.
+ *
+ *	This function takes the ADC mutex to prevent two or more
+ *	concurrent uses, and therefore may sleep.  As a result, it
+ *	can only be called from process context, not interrupt
+ *	context.
+ *
+ *	You should release the ADC as soon as possible using
+ *	ucb1x00_adc_disable.
+ */
+void ucb1x00_adc_enable(struct ucb1x00 *ucb)
+{
+	mutex_lock(&ucb->adc_mutex);
+
+	ucb->adc_cr |= UCB_ADC_ENA;
+
+	ucb1x00_enable(ucb);
+	ucb1x00_reg_write(ucb, UCB_ADC_CR, ucb->adc_cr);
+}
+
+/**
+ *	ucb1x00_adc_read - read the specified ADC channel
+ *	@ucb: UCB1x00 structure describing chip
+ *	@adc_channel: ADC channel mask
+ *	@sync: wait for syncronisation pulse.
+ *
+ *	Start an ADC conversion and wait for the result.  Note that
+ *	synchronised ADC conversions (via the ADCSYNC pin) must wait
+ *	until the trigger is asserted and the conversion is finished.
+ *
+ *	This function currently spins waiting for the conversion to
+ *	complete (2 frames max without sync).
+ *
+ *	If called for a synchronised ADC conversion, it may sleep
+ *	with the ADC mutex held.
+ */
+unsigned int ucb1x00_adc_read(struct ucb1x00 *ucb, int adc_channel, int sync)
+{
+	unsigned int val;
+
+	if (sync)
+		adc_channel |= UCB_ADC_SYNC_ENA;
+
+	ucb1x00_reg_write(ucb, UCB_ADC_CR, ucb->adc_cr | adc_channel);
+	ucb1x00_reg_write(ucb, UCB_ADC_CR, ucb->adc_cr | adc_channel | UCB_ADC_START);
+
+	for (;;) {
+		val = ucb1x00_reg_read(ucb, UCB_ADC_DATA);
+		if (val & UCB_ADC_DAT_VAL)
+			break;
+		/* yield to other processes */
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(1);
+	}
+
+	return UCB_ADC_DAT(val);
+}
+
+/**
+ *	ucb1x00_adc_disable - disable the ADC converter
+ *	@ucb: UCB1x00 structure describing chip
+ *
+ *	Disable the ADC converter and release the ADC mutex.
+ */
+void ucb1x00_adc_disable(struct ucb1x00 *ucb)
+{
+	ucb->adc_cr &= ~UCB_ADC_ENA;
+	ucb1x00_reg_write(ucb, UCB_ADC_CR, ucb->adc_cr);
+	ucb1x00_disable(ucb);
+
+	mutex_unlock(&ucb->adc_mutex);
+}
+
+/*
+ * UCB1x00 Interrupt handling.
+ *
+ * The UCB1x00 can generate interrupts when the SIBCLK is stopped.
+ * Since we need to read an internal register, we must re-enable
+ * SIBCLK to talk to the chip.  We leave the clock running until
+ * we have finished processing all interrupts from the chip.
+ */
+static void ucb1x00_irq(unsigned int irq, struct irq_desc *desc)
+{
+	struct ucb1x00 *ucb = irq_desc_get_handler_data(desc);
+	unsigned int isr, i;
+
+	ucb1x00_enable(ucb);
+	isr = ucb1x00_reg_read(ucb, UCB_IE_STATUS);
+	ucb1x00_reg_write(ucb, UCB_IE_CLEAR, isr);
+	ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0);
+
+	for (i = 0; i < 16 && isr; i++, isr >>= 1, irq++)
+		if (isr & 1)
+			generic_handle_irq(ucb->irq_base + i);
+	ucb1x00_disable(ucb);
+}
+
+static void ucb1x00_irq_update(struct ucb1x00 *ucb, unsigned mask)
+{
+	ucb1x00_enable(ucb);
+	if (ucb->irq_ris_enbl & mask)
+		ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl &
+				  ucb->irq_mask);
+	if (ucb->irq_fal_enbl & mask)
+		ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl &
+				  ucb->irq_mask);
+	ucb1x00_disable(ucb);
+}
+
+static void ucb1x00_irq_noop(struct irq_data *data)
+{
+}
+
+static void ucb1x00_irq_mask(struct irq_data *data)
+{
+	struct ucb1x00 *ucb = irq_data_get_irq_chip_data(data);
+	unsigned mask = 1 << (data->irq - ucb->irq_base);
+
+	raw_spin_lock(&ucb->irq_lock);
+	ucb->irq_mask &= ~mask;
+	ucb1x00_irq_update(ucb, mask);
+	raw_spin_unlock(&ucb->irq_lock);
+}
+
+static void ucb1x00_irq_unmask(struct irq_data *data)
+{
+	struct ucb1x00 *ucb = irq_data_get_irq_chip_data(data);
+	unsigned mask = 1 << (data->irq - ucb->irq_base);
+
+	raw_spin_lock(&ucb->irq_lock);
+	ucb->irq_mask |= mask;
+	ucb1x00_irq_update(ucb, mask);
+	raw_spin_unlock(&ucb->irq_lock);
+}
+
+static int ucb1x00_irq_set_type(struct irq_data *data, unsigned int type)
+{
+	struct ucb1x00 *ucb = irq_data_get_irq_chip_data(data);
+	unsigned mask = 1 << (data->irq - ucb->irq_base);
+
+	raw_spin_lock(&ucb->irq_lock);
+	if (type & IRQ_TYPE_EDGE_RISING)
+		ucb->irq_ris_enbl |= mask;
+	else
+		ucb->irq_ris_enbl &= ~mask;
+
+	if (type & IRQ_TYPE_EDGE_FALLING)
+		ucb->irq_fal_enbl |= mask;
+	else
+		ucb->irq_fal_enbl &= ~mask;
+	if (ucb->irq_mask & mask) {
+		ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl &
+				  ucb->irq_mask);
+		ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl &
+				  ucb->irq_mask);
+	}
+	raw_spin_unlock(&ucb->irq_lock);
+
+	return 0;
+}
+
+static int ucb1x00_irq_set_wake(struct irq_data *data, unsigned int on)
+{
+	struct ucb1x00 *ucb = irq_data_get_irq_chip_data(data);
+	struct ucb1x00_plat_data *pdata = ucb->mcp->attached_device.platform_data;
+	unsigned mask = 1 << (data->irq - ucb->irq_base);
+
+	if (!pdata || !pdata->can_wakeup)
+		return -EINVAL;
+
+	raw_spin_lock(&ucb->irq_lock);
+	if (on)
+		ucb->irq_wake |= mask;
+	else
+		ucb->irq_wake &= ~mask;
+	raw_spin_unlock(&ucb->irq_lock);
+
+	return 0;
+}
+
+static struct irq_chip ucb1x00_irqchip = {
+	.name = "ucb1x00",
+	.irq_ack = ucb1x00_irq_noop,
+	.irq_mask = ucb1x00_irq_mask,
+	.irq_unmask = ucb1x00_irq_unmask,
+	.irq_set_type = ucb1x00_irq_set_type,
+	.irq_set_wake = ucb1x00_irq_set_wake,
+};
+
+static int ucb1x00_add_dev(struct ucb1x00 *ucb, struct ucb1x00_driver *drv)
+{
+	struct ucb1x00_dev *dev;
+	int ret = -ENOMEM;
+
+	dev = kmalloc(sizeof(struct ucb1x00_dev), GFP_KERNEL);
+	if (dev) {
+		dev->ucb = ucb;
+		dev->drv = drv;
+
+		ret = drv->add(dev);
+
+		if (ret == 0) {
+			list_add_tail(&dev->dev_node, &ucb->devs);
+			list_add_tail(&dev->drv_node, &drv->devs);
+		} else {
+			kfree(dev);
+		}
+	}
+	return ret;
+}
+
+static void ucb1x00_remove_dev(struct ucb1x00_dev *dev)
+{
+	dev->drv->remove(dev);
+	list_del(&dev->dev_node);
+	list_del(&dev->drv_node);
+	kfree(dev);
+}
+
+/*
+ * Try to probe our interrupt, rather than relying on lots of
+ * hard-coded machine dependencies.  For reference, the expected
+ * IRQ mappings are:
+ *
+ *  	Machine		Default IRQ
+ *	adsbitsy	IRQ_GPCIN4
+ *	cerf		IRQ_GPIO_UCB1200_IRQ
+ *	flexanet	IRQ_GPIO_GUI
+ *	freebird	IRQ_GPIO_FREEBIRD_UCB1300_IRQ
+ *	graphicsclient	ADS_EXT_IRQ(8)
+ *	graphicsmaster	ADS_EXT_IRQ(8)
+ *	lart		LART_IRQ_UCB1200
+ *	omnimeter	IRQ_GPIO23
+ *	pfs168		IRQ_GPIO_UCB1300_IRQ
+ *	simpad		IRQ_GPIO_UCB1300_IRQ
+ *	shannon		SHANNON_IRQ_GPIO_IRQ_CODEC
+ *	yopy		IRQ_GPIO_UCB1200_IRQ
+ */
+static int ucb1x00_detect_irq(struct ucb1x00 *ucb)
+{
+	unsigned long mask;
+
+	mask = probe_irq_on();
+	if (!mask) {
+		probe_irq_off(mask);
+		return NO_IRQ;
+	}
+
+	/*
+	 * Enable the ADC interrupt.
+	 */
+	ucb1x00_reg_write(ucb, UCB_IE_RIS, UCB_IE_ADC);
+	ucb1x00_reg_write(ucb, UCB_IE_FAL, UCB_IE_ADC);
+	ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0xffff);
+	ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0);
+
+	/*
+	 * Cause an ADC interrupt.
+	 */
+	ucb1x00_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA);
+	ucb1x00_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA | UCB_ADC_START);
+
+	/*
+	 * Wait for the conversion to complete.
+	 */
+	while ((ucb1x00_reg_read(ucb, UCB_ADC_DATA) & UCB_ADC_DAT_VAL) == 0);
+	ucb1x00_reg_write(ucb, UCB_ADC_CR, 0);
+
+	/*
+	 * Disable and clear interrupt.
+	 */
+	ucb1x00_reg_write(ucb, UCB_IE_RIS, 0);
+	ucb1x00_reg_write(ucb, UCB_IE_FAL, 0);
+	ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0xffff);
+	ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0);
+
+	/*
+	 * Read triggered interrupt.
+	 */
+	return probe_irq_off(mask);
+}
+
+static void ucb1x00_release(struct device *dev)
+{
+	struct ucb1x00 *ucb = classdev_to_ucb1x00(dev);
+	kfree(ucb);
+}
+
+static struct class ucb1x00_class = {
+	.name		= "ucb1x00",
+	.dev_release	= ucb1x00_release,
+};
+
+static int ucb1x00_probe(struct mcp *mcp)
+{
+	struct ucb1x00_plat_data *pdata = mcp->attached_device.platform_data;
+	struct ucb1x00_driver *drv;
+	struct ucb1x00 *ucb;
+	unsigned id, i, irq_base;
+	int ret = -ENODEV;
+
+	/* Tell the platform to deassert the UCB1x00 reset */
+	if (pdata && pdata->reset)
+		pdata->reset(UCB_RST_PROBE);
+
+	mcp_enable(mcp);
+	id = mcp_reg_read(mcp, UCB_ID);
+	mcp_disable(mcp);
+
+	if (id != UCB_ID_1200 && id != UCB_ID_1300 && id != UCB_ID_TC35143) {
+		printk(KERN_WARNING "UCB1x00 ID not found: %04x\n", id);
+		goto out;
+	}
+
+	ucb = kzalloc(sizeof(struct ucb1x00), GFP_KERNEL);
+	ret = -ENOMEM;
+	if (!ucb)
+		goto out;
+
+	device_initialize(&ucb->dev);
+	ucb->dev.class = &ucb1x00_class;
+	ucb->dev.parent = &mcp->attached_device;
+	dev_set_name(&ucb->dev, "ucb1x00");
+
+	raw_spin_lock_init(&ucb->irq_lock);
+	spin_lock_init(&ucb->io_lock);
+	mutex_init(&ucb->adc_mutex);
+
+	ucb->id  = id;
+	ucb->mcp = mcp;
+
+	ret = device_add(&ucb->dev);
+	if (ret)
+		goto err_dev_add;
+
+	ucb1x00_enable(ucb);
+	ucb->irq = ucb1x00_detect_irq(ucb);
+	ucb1x00_disable(ucb);
+	if (ucb->irq == NO_IRQ) {
+		dev_err(&ucb->dev, "IRQ probe failed\n");
+		ret = -ENODEV;
+		goto err_no_irq;
+	}
+
+	ucb->gpio.base = -1;
+	irq_base = pdata ? pdata->irq_base : 0;
+	ucb->irq_base = irq_alloc_descs(-1, irq_base, 16, -1);
+	if (ucb->irq_base < 0) {
+		dev_err(&ucb->dev, "unable to allocate 16 irqs: %d\n",
+			ucb->irq_base);
+		goto err_irq_alloc;
+	}
+
+	for (i = 0; i < 16; i++) {
+		unsigned irq = ucb->irq_base + i;
+
+		irq_set_chip_and_handler(irq, &ucb1x00_irqchip, handle_edge_irq);
+		irq_set_chip_data(irq, ucb);
+		set_irq_flags(irq, IRQF_VALID | IRQ_NOREQUEST);
+	}
+
+	irq_set_irq_type(ucb->irq, IRQ_TYPE_EDGE_RISING);
+	irq_set_handler_data(ucb->irq, ucb);
+	irq_set_chained_handler(ucb->irq, ucb1x00_irq);
+
+	if (pdata && pdata->gpio_base) {
+		ucb->gpio.label = dev_name(&ucb->dev);
+		ucb->gpio.dev = &ucb->dev;
+		ucb->gpio.owner = THIS_MODULE;
+		ucb->gpio.base = pdata->gpio_base;
+		ucb->gpio.ngpio = 10;
+		ucb->gpio.set = ucb1x00_gpio_set;
+		ucb->gpio.get = ucb1x00_gpio_get;
+		ucb->gpio.direction_input = ucb1x00_gpio_direction_input;
+		ucb->gpio.direction_output = ucb1x00_gpio_direction_output;
+		ucb->gpio.to_irq = ucb1x00_to_irq;
+		ret = gpiochip_add(&ucb->gpio);
+		if (ret)
+			goto err_gpio_add;
+	} else
+		dev_info(&ucb->dev, "gpio_base not set so no gpiolib support");
+
+	mcp_set_drvdata(mcp, ucb);
+
+	if (pdata)
+		device_set_wakeup_capable(&ucb->dev, pdata->can_wakeup);
+
+	INIT_LIST_HEAD(&ucb->devs);
+	mutex_lock(&ucb1x00_mutex);
+	list_add_tail(&ucb->node, &ucb1x00_devices);
+	list_for_each_entry(drv, &ucb1x00_drivers, node) {
+		ucb1x00_add_dev(ucb, drv);
+	}
+	mutex_unlock(&ucb1x00_mutex);
+
+	return ret;
+
+ err_gpio_add:
+	irq_set_chained_handler(ucb->irq, NULL);
+ err_irq_alloc:
+	if (ucb->irq_base > 0)
+		irq_free_descs(ucb->irq_base, 16);
+ err_no_irq:
+	device_del(&ucb->dev);
+ err_dev_add:
+	put_device(&ucb->dev);
+ out:
+	if (pdata && pdata->reset)
+		pdata->reset(UCB_RST_PROBE_FAIL);
+	return ret;
+}
+
+static void ucb1x00_remove(struct mcp *mcp)
+{
+	struct ucb1x00_plat_data *pdata = mcp->attached_device.platform_data;
+	struct ucb1x00 *ucb = mcp_get_drvdata(mcp);
+	struct list_head *l, *n;
+	int ret;
+
+	mutex_lock(&ucb1x00_mutex);
+	list_del(&ucb->node);
+	list_for_each_safe(l, n, &ucb->devs) {
+		struct ucb1x00_dev *dev = list_entry(l, struct ucb1x00_dev, dev_node);
+		ucb1x00_remove_dev(dev);
+	}
+	mutex_unlock(&ucb1x00_mutex);
+
+	if (ucb->gpio.base != -1) {
+		ret = gpiochip_remove(&ucb->gpio);
+		if (ret)
+			dev_err(&ucb->dev, "Can't remove gpio chip: %d\n", ret);
+	}
+
+	irq_set_chained_handler(ucb->irq, NULL);
+	irq_free_descs(ucb->irq_base, 16);
+	device_unregister(&ucb->dev);
+
+	if (pdata && pdata->reset)
+		pdata->reset(UCB_RST_REMOVE);
+}
+
+int ucb1x00_register_driver(struct ucb1x00_driver *drv)
+{
+	struct ucb1x00 *ucb;
+
+	INIT_LIST_HEAD(&drv->devs);
+	mutex_lock(&ucb1x00_mutex);
+	list_add_tail(&drv->node, &ucb1x00_drivers);
+	list_for_each_entry(ucb, &ucb1x00_devices, node) {
+		ucb1x00_add_dev(ucb, drv);
+	}
+	mutex_unlock(&ucb1x00_mutex);
+	return 0;
+}
+
+void ucb1x00_unregister_driver(struct ucb1x00_driver *drv)
+{
+	struct list_head *n, *l;
+
+	mutex_lock(&ucb1x00_mutex);
+	list_del(&drv->node);
+	list_for_each_safe(l, n, &drv->devs) {
+		struct ucb1x00_dev *dev = list_entry(l, struct ucb1x00_dev, drv_node);
+		ucb1x00_remove_dev(dev);
+	}
+	mutex_unlock(&ucb1x00_mutex);
+}
+
+static int ucb1x00_suspend(struct device *dev)
+{
+	struct ucb1x00_plat_data *pdata = dev->platform_data;
+	struct ucb1x00 *ucb = dev_get_drvdata(dev);
+	struct ucb1x00_dev *udev;
+
+	mutex_lock(&ucb1x00_mutex);
+	list_for_each_entry(udev, &ucb->devs, dev_node) {
+		if (udev->drv->suspend)
+			udev->drv->suspend(udev);
+	}
+	mutex_unlock(&ucb1x00_mutex);
+
+	if (ucb->irq_wake) {
+		unsigned long flags;
+
+		raw_spin_lock_irqsave(&ucb->irq_lock, flags);
+		ucb1x00_enable(ucb);
+		ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl &
+				  ucb->irq_wake);
+		ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl &
+				  ucb->irq_wake);
+		ucb1x00_disable(ucb);
+		raw_spin_unlock_irqrestore(&ucb->irq_lock, flags);
+
+		enable_irq_wake(ucb->irq);
+	} else if (pdata && pdata->reset)
+		pdata->reset(UCB_RST_SUSPEND);
+
+	return 0;
+}
+
+static int ucb1x00_resume(struct device *dev)
+{
+	struct ucb1x00_plat_data *pdata = dev->platform_data;
+	struct ucb1x00 *ucb = dev_get_drvdata(dev);
+	struct ucb1x00_dev *udev;
+
+	if (!ucb->irq_wake && pdata && pdata->reset)
+		pdata->reset(UCB_RST_RESUME);
+
+	ucb1x00_enable(ucb);
+	ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out);
+	ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir);
+
+	if (ucb->irq_wake) {
+		unsigned long flags;
+
+		raw_spin_lock_irqsave(&ucb->irq_lock, flags);
+		ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl &
+				  ucb->irq_mask);
+		ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl &
+				  ucb->irq_mask);
+		raw_spin_unlock_irqrestore(&ucb->irq_lock, flags);
+
+		disable_irq_wake(ucb->irq);
+	}
+	ucb1x00_disable(ucb);
+
+	mutex_lock(&ucb1x00_mutex);
+	list_for_each_entry(udev, &ucb->devs, dev_node) {
+		if (udev->drv->resume)
+			udev->drv->resume(udev);
+	}
+	mutex_unlock(&ucb1x00_mutex);
+	return 0;
+}
+
+static const struct dev_pm_ops ucb1x00_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(ucb1x00_suspend, ucb1x00_resume)
+};
+
+static struct mcp_driver ucb1x00_driver = {
+	.drv		= {
+		.name	= "ucb1x00",
+		.owner	= THIS_MODULE,
+		.pm	= &ucb1x00_pm_ops,
+	},
+	.probe		= ucb1x00_probe,
+	.remove		= ucb1x00_remove,
+};
+
+static int __init ucb1x00_init(void)
+{
+	int ret = class_register(&ucb1x00_class);
+	if (ret == 0) {
+		ret = mcp_driver_register(&ucb1x00_driver);
+		if (ret)
+			class_unregister(&ucb1x00_class);
+	}
+	return ret;
+}
+
+static void __exit ucb1x00_exit(void)
+{
+	mcp_driver_unregister(&ucb1x00_driver);
+	class_unregister(&ucb1x00_class);
+}
+
+module_init(ucb1x00_init);
+module_exit(ucb1x00_exit);
+
+EXPORT_SYMBOL(ucb1x00_io_set_dir);
+EXPORT_SYMBOL(ucb1x00_io_write);
+EXPORT_SYMBOL(ucb1x00_io_read);
+
+EXPORT_SYMBOL(ucb1x00_adc_enable);
+EXPORT_SYMBOL(ucb1x00_adc_read);
+EXPORT_SYMBOL(ucb1x00_adc_disable);
+
+EXPORT_SYMBOL(ucb1x00_register_driver);
+EXPORT_SYMBOL(ucb1x00_unregister_driver);
+
+MODULE_ALIAS("mcp:ucb1x00");
+MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
+MODULE_DESCRIPTION("UCB1x00 core driver");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/ucb1x00-ts.c b/ap/os/linux/linux-3.4.x/drivers/mfd/ucb1x00-ts.c
new file mode 100644
index 0000000..1e0e20c
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/ucb1x00-ts.c
@@ -0,0 +1,448 @@
+/*
+ *  Touchscreen driver for UCB1x00-based touchscreens
+ *
+ *  Copyright (C) 2001 Russell King, All Rights Reserved.
+ *  Copyright (C) 2005 Pavel Machek
+ *
+ * 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.
+ *
+ * 21-Jan-2002 <jco@ict.es> :
+ *
+ * Added support for synchronous A/D mode. This mode is useful to
+ * avoid noise induced in the touchpanel by the LCD, provided that
+ * the UCB1x00 has a valid LCD sync signal routed to its ADCSYNC pin.
+ * It is important to note that the signal connected to the ADCSYNC
+ * pin should provide pulses even when the LCD is blanked, otherwise
+ * a pen touch needed to unblank the LCD will never be read.
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/input.h>
+#include <linux/device.h>
+#include <linux/freezer.h>
+#include <linux/slab.h>
+#include <linux/kthread.h>
+#include <linux/mfd/ucb1x00.h>
+
+#include <mach/collie.h>
+#include <asm/mach-types.h>
+
+
+
+struct ucb1x00_ts {
+	struct input_dev	*idev;
+	struct ucb1x00		*ucb;
+
+	spinlock_t		irq_lock;
+	unsigned		irq_disabled;
+	wait_queue_head_t	irq_wait;
+	struct task_struct	*rtask;
+	u16			x_res;
+	u16			y_res;
+
+	unsigned int		adcsync:1;
+};
+
+static int adcsync;
+
+static inline void ucb1x00_ts_evt_add(struct ucb1x00_ts *ts, u16 pressure, u16 x, u16 y)
+{
+	struct input_dev *idev = ts->idev;
+
+	input_report_abs(idev, ABS_X, x);
+	input_report_abs(idev, ABS_Y, y);
+	input_report_abs(idev, ABS_PRESSURE, pressure);
+	input_report_key(idev, BTN_TOUCH, 1);
+	input_sync(idev);
+}
+
+static inline void ucb1x00_ts_event_release(struct ucb1x00_ts *ts)
+{
+	struct input_dev *idev = ts->idev;
+
+	input_report_abs(idev, ABS_PRESSURE, 0);
+	input_report_key(idev, BTN_TOUCH, 0);
+	input_sync(idev);
+}
+
+/*
+ * Switch to interrupt mode.
+ */
+static inline void ucb1x00_ts_mode_int(struct ucb1x00_ts *ts)
+{
+	ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
+			UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW |
+			UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND |
+			UCB_TS_CR_MODE_INT);
+}
+
+/*
+ * Switch to pressure mode, and read pressure.  We don't need to wait
+ * here, since both plates are being driven.
+ */
+static inline unsigned int ucb1x00_ts_read_pressure(struct ucb1x00_ts *ts)
+{
+	if (machine_is_collie()) {
+		ucb1x00_io_write(ts->ucb, COLLIE_TC35143_GPIO_TBL_CHK, 0);
+		ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
+				  UCB_TS_CR_TSPX_POW | UCB_TS_CR_TSMX_POW |
+				  UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
+
+		udelay(55);
+
+		return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_AD2, ts->adcsync);
+	} else {
+		ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
+				  UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW |
+				  UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND |
+				  UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
+
+		return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync);
+	}
+}
+
+/*
+ * Switch to X position mode and measure Y plate.  We switch the plate
+ * configuration in pressure mode, then switch to position mode.  This
+ * gives a faster response time.  Even so, we need to wait about 55us
+ * for things to stabilise.
+ */
+static inline unsigned int ucb1x00_ts_read_xpos(struct ucb1x00_ts *ts)
+{
+	if (machine_is_collie())
+		ucb1x00_io_write(ts->ucb, 0, COLLIE_TC35143_GPIO_TBL_CHK);
+	else {
+		ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
+				  UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
+				  UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
+		ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
+				  UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
+				  UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
+	}
+	ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
+			UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
+			UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
+
+	udelay(55);
+
+	return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync);
+}
+
+/*
+ * Switch to Y position mode and measure X plate.  We switch the plate
+ * configuration in pressure mode, then switch to position mode.  This
+ * gives a faster response time.  Even so, we need to wait about 55us
+ * for things to stabilise.
+ */
+static inline unsigned int ucb1x00_ts_read_ypos(struct ucb1x00_ts *ts)
+{
+	if (machine_is_collie())
+		ucb1x00_io_write(ts->ucb, 0, COLLIE_TC35143_GPIO_TBL_CHK);
+	else {
+		ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
+				  UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
+				  UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
+		ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
+				  UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
+				  UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
+	}
+
+	ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
+			UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
+			UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
+
+	udelay(55);
+
+	return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPX, ts->adcsync);
+}
+
+/*
+ * Switch to X plate resistance mode.  Set MX to ground, PX to
+ * supply.  Measure current.
+ */
+static inline unsigned int ucb1x00_ts_read_xres(struct ucb1x00_ts *ts)
+{
+	ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
+			UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
+			UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
+	return ucb1x00_adc_read(ts->ucb, 0, ts->adcsync);
+}
+
+/*
+ * Switch to Y plate resistance mode.  Set MY to ground, PY to
+ * supply.  Measure current.
+ */
+static inline unsigned int ucb1x00_ts_read_yres(struct ucb1x00_ts *ts)
+{
+	ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
+			UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
+			UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
+	return ucb1x00_adc_read(ts->ucb, 0, ts->adcsync);
+}
+
+static inline int ucb1x00_ts_pen_down(struct ucb1x00_ts *ts)
+{
+	unsigned int val = ucb1x00_reg_read(ts->ucb, UCB_TS_CR);
+
+	if (machine_is_collie())
+		return (!(val & (UCB_TS_CR_TSPX_LOW)));
+	else
+		return (val & (UCB_TS_CR_TSPX_LOW | UCB_TS_CR_TSMX_LOW));
+}
+
+/*
+ * This is a RT kernel thread that handles the ADC accesses
+ * (mainly so we can use semaphores in the UCB1200 core code
+ * to serialise accesses to the ADC).
+ */
+static int ucb1x00_thread(void *_ts)
+{
+	struct ucb1x00_ts *ts = _ts;
+	DECLARE_WAITQUEUE(wait, current);
+	bool frozen, ignore = false;
+	int valid = 0;
+
+	set_freezable();
+	add_wait_queue(&ts->irq_wait, &wait);
+	while (!kthread_freezable_should_stop(&frozen)) {
+		unsigned int x, y, p;
+		signed long timeout;
+
+		if (frozen)
+			ignore = true;
+
+		ucb1x00_adc_enable(ts->ucb);
+
+		x = ucb1x00_ts_read_xpos(ts);
+		y = ucb1x00_ts_read_ypos(ts);
+		p = ucb1x00_ts_read_pressure(ts);
+
+		/*
+		 * Switch back to interrupt mode.
+		 */
+		ucb1x00_ts_mode_int(ts);
+		ucb1x00_adc_disable(ts->ucb);
+
+		msleep(10);
+
+		ucb1x00_enable(ts->ucb);
+
+
+		if (ucb1x00_ts_pen_down(ts)) {
+			set_current_state(TASK_INTERRUPTIBLE);
+
+			spin_lock_irq(&ts->irq_lock);
+			if (ts->irq_disabled) {
+				ts->irq_disabled = 0;
+				enable_irq(ts->ucb->irq_base + UCB_IRQ_TSPX);
+			}
+			spin_unlock_irq(&ts->irq_lock);
+			ucb1x00_disable(ts->ucb);
+
+			/*
+			 * If we spat out a valid sample set last time,
+			 * spit out a "pen off" sample here.
+			 */
+			if (valid) {
+				ucb1x00_ts_event_release(ts);
+				valid = 0;
+			}
+
+			timeout = MAX_SCHEDULE_TIMEOUT;
+		} else {
+			ucb1x00_disable(ts->ucb);
+
+			/*
+			 * Filtering is policy.  Policy belongs in user
+			 * space.  We therefore leave it to user space
+			 * to do any filtering they please.
+			 */
+			if (!ignore) {
+				ucb1x00_ts_evt_add(ts, p, x, y);
+				valid = 1;
+			}
+
+			set_current_state(TASK_INTERRUPTIBLE);
+			timeout = HZ / 100;
+		}
+
+		schedule_timeout(timeout);
+	}
+
+	remove_wait_queue(&ts->irq_wait, &wait);
+
+	ts->rtask = NULL;
+	return 0;
+}
+
+/*
+ * We only detect touch screen _touches_ with this interrupt
+ * handler, and even then we just schedule our task.
+ */
+static irqreturn_t ucb1x00_ts_irq(int irq, void *id)
+{
+	struct ucb1x00_ts *ts = id;
+
+	spin_lock(&ts->irq_lock);
+	ts->irq_disabled = 1;
+	disable_irq_nosync(ts->ucb->irq_base + UCB_IRQ_TSPX);
+	spin_unlock(&ts->irq_lock);
+	wake_up(&ts->irq_wait);
+
+	return IRQ_HANDLED;
+}
+
+static int ucb1x00_ts_open(struct input_dev *idev)
+{
+	struct ucb1x00_ts *ts = input_get_drvdata(idev);
+	unsigned long flags = 0;
+	int ret = 0;
+
+	BUG_ON(ts->rtask);
+
+	if (machine_is_collie())
+		flags = IRQF_TRIGGER_RISING;
+	else
+		flags = IRQF_TRIGGER_FALLING;
+
+	ts->irq_disabled = 0;
+
+	init_waitqueue_head(&ts->irq_wait);
+	ret = request_irq(ts->ucb->irq_base + UCB_IRQ_TSPX, ucb1x00_ts_irq,
+			  flags, "ucb1x00-ts", ts);
+	if (ret < 0)
+		goto out;
+
+	/*
+	 * If we do this at all, we should allow the user to
+	 * measure and read the X and Y resistance at any time.
+	 */
+	ucb1x00_adc_enable(ts->ucb);
+	ts->x_res = ucb1x00_ts_read_xres(ts);
+	ts->y_res = ucb1x00_ts_read_yres(ts);
+	ucb1x00_adc_disable(ts->ucb);
+
+	ts->rtask = kthread_run(ucb1x00_thread, ts, "ktsd");
+	if (!IS_ERR(ts->rtask)) {
+		ret = 0;
+	} else {
+		free_irq(ts->ucb->irq_base + UCB_IRQ_TSPX, ts);
+		ts->rtask = NULL;
+		ret = -EFAULT;
+	}
+
+ out:
+	return ret;
+}
+
+/*
+ * Release touchscreen resources.  Disable IRQs.
+ */
+static void ucb1x00_ts_close(struct input_dev *idev)
+{
+	struct ucb1x00_ts *ts = input_get_drvdata(idev);
+
+	if (ts->rtask)
+		kthread_stop(ts->rtask);
+
+	ucb1x00_enable(ts->ucb);
+	free_irq(ts->ucb->irq_base + UCB_IRQ_TSPX, ts);
+	ucb1x00_reg_write(ts->ucb, UCB_TS_CR, 0);
+	ucb1x00_disable(ts->ucb);
+}
+
+
+/*
+ * Initialisation.
+ */
+static int ucb1x00_ts_add(struct ucb1x00_dev *dev)
+{
+	struct ucb1x00_ts *ts;
+	struct input_dev *idev;
+	int err;
+
+	ts = kzalloc(sizeof(struct ucb1x00_ts), GFP_KERNEL);
+	idev = input_allocate_device();
+	if (!ts || !idev) {
+		err = -ENOMEM;
+		goto fail;
+	}
+
+	ts->ucb = dev->ucb;
+	ts->idev = idev;
+	ts->adcsync = adcsync ? UCB_SYNC : UCB_NOSYNC;
+	spin_lock_init(&ts->irq_lock);
+
+	idev->name       = "Touchscreen panel";
+	idev->id.product = ts->ucb->id;
+	idev->open       = ucb1x00_ts_open;
+	idev->close      = ucb1x00_ts_close;
+	idev->dev.parent = &ts->ucb->dev;
+
+	idev->evbit[0]   = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY);
+	idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+	input_set_drvdata(idev, ts);
+
+	ucb1x00_adc_enable(ts->ucb);
+	ts->x_res = ucb1x00_ts_read_xres(ts);
+	ts->y_res = ucb1x00_ts_read_yres(ts);
+	ucb1x00_adc_disable(ts->ucb);
+
+	input_set_abs_params(idev, ABS_X, 0, ts->x_res, 0, 0);
+	input_set_abs_params(idev, ABS_Y, 0, ts->y_res, 0, 0);
+	input_set_abs_params(idev, ABS_PRESSURE, 0, 0, 0, 0);
+
+	err = input_register_device(idev);
+	if (err)
+		goto fail;
+
+	dev->priv = ts;
+
+	return 0;
+
+ fail:
+	input_free_device(idev);
+	kfree(ts);
+	return err;
+}
+
+static void ucb1x00_ts_remove(struct ucb1x00_dev *dev)
+{
+	struct ucb1x00_ts *ts = dev->priv;
+
+	input_unregister_device(ts->idev);
+	kfree(ts);
+}
+
+static struct ucb1x00_driver ucb1x00_ts_driver = {
+	.add		= ucb1x00_ts_add,
+	.remove		= ucb1x00_ts_remove,
+};
+
+static int __init ucb1x00_ts_init(void)
+{
+	return ucb1x00_register_driver(&ucb1x00_ts_driver);
+}
+
+static void __exit ucb1x00_ts_exit(void)
+{
+	ucb1x00_unregister_driver(&ucb1x00_ts_driver);
+}
+
+module_param(adcsync, int, 0444);
+module_init(ucb1x00_ts_init);
+module_exit(ucb1x00_ts_exit);
+
+MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
+MODULE_DESCRIPTION("UCB1x00 touchscreen driver");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/vx855.c b/ap/os/linux/linux-3.4.x/drivers/mfd/vx855.c
new file mode 100644
index 0000000..b73cc15
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/vx855.c
@@ -0,0 +1,148 @@
+/*
+ * Linux multi-function-device driver (MFD) for the integrated peripherals
+ * of the VIA VX855 chipset
+ *
+ * Copyright (C) 2009 VIA Technologies, Inc.
+ * Copyright (C) 2010 One Laptop per Child
+ * Author: Harald Welte <HaraldWelte@viatech.com>
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/pci.h>
+#include <linux/mfd/core.h>
+
+/* offset into pci config space indicating the 16bit register containing
+ * the power management IO space base */
+#define VX855_CFG_PMIO_OFFSET	0x88
+
+/* ACPI I/O Space registers */
+#define VX855_PMIO_ACPI		0x00
+#define VX855_PMIO_ACPI_LEN	0x0b
+
+/* Processor Power Management */
+#define VX855_PMIO_PPM		0x10
+#define VX855_PMIO_PPM_LEN	0x08
+
+/* General Purpose Power Management */
+#define VX855_PMIO_GPPM		0x20
+#define VX855_PMIO_R_GPI	0x48
+#define VX855_PMIO_R_GPO	0x4c
+#define VX855_PMIO_GPPM_LEN	0x33
+
+#define VSPIC_MMIO_SIZE	0x1000
+
+static struct resource vx855_gpio_resources[] = {
+	{
+		.flags = IORESOURCE_IO,
+	},
+	{
+		.flags = IORESOURCE_IO,
+	},
+};
+
+static struct mfd_cell vx855_cells[] = {
+	{
+		.name = "vx855_gpio",
+		.num_resources = ARRAY_SIZE(vx855_gpio_resources),
+		.resources = vx855_gpio_resources,
+
+		/* we must ignore resource conflicts, for reasons outlined in
+		 * the vx855_gpio driver */
+		.ignore_resource_conflicts = true,
+	},
+};
+
+static __devinit int vx855_probe(struct pci_dev *pdev,
+				 const struct pci_device_id *id)
+{
+	int ret;
+	u16 gpio_io_offset;
+
+	ret = pci_enable_device(pdev);
+	if (ret)
+		return -ENODEV;
+
+	pci_read_config_word(pdev, VX855_CFG_PMIO_OFFSET, &gpio_io_offset);
+	if (!gpio_io_offset) {
+		dev_warn(&pdev->dev,
+			"BIOS did not assign PMIO base offset?!?\n");
+		ret = -ENODEV;
+		goto out;
+	}
+
+	/* mask out the lowest seven bits, as they are always zero, but
+	 * hardware returns them as 0x01 */
+	gpio_io_offset &= 0xff80;
+
+	/* As the region identified here includes many non-GPIO things, we
+	 * only work with the specific registers that concern us. */
+	vx855_gpio_resources[0].start = gpio_io_offset + VX855_PMIO_R_GPI;
+	vx855_gpio_resources[0].end = vx855_gpio_resources[0].start + 3;
+	vx855_gpio_resources[1].start = gpio_io_offset + VX855_PMIO_R_GPO;
+	vx855_gpio_resources[1].end = vx855_gpio_resources[1].start + 3;
+
+	ret = mfd_add_devices(&pdev->dev, -1, vx855_cells, ARRAY_SIZE(vx855_cells),
+			NULL, 0);
+
+	/* we always return -ENODEV here in order to enable other
+	 * drivers like old, not-yet-platform_device ported i2c-viapro */
+	return -ENODEV;
+out:
+	pci_disable_device(pdev);
+	return ret;
+}
+
+static void __devexit vx855_remove(struct pci_dev *pdev)
+{
+	mfd_remove_devices(&pdev->dev);
+	pci_disable_device(pdev);
+}
+
+static DEFINE_PCI_DEVICE_TABLE(vx855_pci_tbl) = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VX855) },
+	{ 0, }
+};
+MODULE_DEVICE_TABLE(pci, vx855_pci_tbl);
+
+static struct pci_driver vx855_pci_driver = {
+	.name		= "vx855",
+	.id_table	= vx855_pci_tbl,
+	.probe		= vx855_probe,
+	.remove		= __devexit_p(vx855_remove),
+};
+
+static int vx855_init(void)
+{
+	return pci_register_driver(&vx855_pci_driver);
+}
+module_init(vx855_init);
+
+static void vx855_exit(void)
+{
+	pci_unregister_driver(&vx855_pci_driver);
+}
+module_exit(vx855_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Harald Welte <HaraldWelte@viatech.com>");
+MODULE_DESCRIPTION("Driver for the VIA VX855 chipset");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/wl1273-core.c b/ap/os/linux/linux-3.4.x/drivers/mfd/wl1273-core.c
new file mode 100644
index 0000000..f39b756
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/wl1273-core.c
@@ -0,0 +1,291 @@
+/*
+ * MFD driver for wl1273 FM radio and audio codec submodules.
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ * Author: Matti Aaltonen <matti.j.aaltonen@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/mfd/wl1273-core.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+
+#define DRIVER_DESC "WL1273 FM Radio Core"
+
+static const struct i2c_device_id wl1273_driver_id_table[] = {
+	{ WL1273_FM_DRIVER_NAME, 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, wl1273_driver_id_table);
+
+static int wl1273_fm_read_reg(struct wl1273_core *core, u8 reg, u16 *value)
+{
+	struct i2c_client *client = core->client;
+	u8 b[2];
+	int r;
+
+	r = i2c_smbus_read_i2c_block_data(client, reg, sizeof(b), b);
+	if (r != 2) {
+		dev_err(&client->dev, "%s: Read: %d fails.\n", __func__, reg);
+		return -EREMOTEIO;
+	}
+
+	*value = (u16)b[0] << 8 | b[1];
+
+	return 0;
+}
+
+static int wl1273_fm_write_cmd(struct wl1273_core *core, u8 cmd, u16 param)
+{
+	struct i2c_client *client = core->client;
+	u8 buf[] = { (param >> 8) & 0xff, param & 0xff };
+	int r;
+
+	r = i2c_smbus_write_i2c_block_data(client, cmd, sizeof(buf), buf);
+	if (r) {
+		dev_err(&client->dev, "%s: Cmd: %d fails.\n", __func__, cmd);
+		return r;
+	}
+
+	return 0;
+}
+
+static int wl1273_fm_write_data(struct wl1273_core *core, u8 *data, u16 len)
+{
+	struct i2c_client *client = core->client;
+	struct i2c_msg msg;
+	int r;
+
+	msg.addr = client->addr;
+	msg.flags = 0;
+	msg.buf = data;
+	msg.len = len;
+
+	r = i2c_transfer(client->adapter, &msg, 1);
+	if (r != 1) {
+		dev_err(&client->dev, "%s: write error.\n", __func__);
+		return -EREMOTEIO;
+	}
+
+	return 0;
+}
+
+/**
+ * wl1273_fm_set_audio() -	Set audio mode.
+ * @core:			A pointer to the device struct.
+ * @new_mode:			The new audio mode.
+ *
+ * Audio modes are WL1273_AUDIO_DIGITAL and WL1273_AUDIO_ANALOG.
+ */
+static int wl1273_fm_set_audio(struct wl1273_core *core, unsigned int new_mode)
+{
+	int r = 0;
+
+	if (core->mode == WL1273_MODE_OFF ||
+	    core->mode == WL1273_MODE_SUSPENDED)
+		return -EPERM;
+
+	if (core->mode == WL1273_MODE_RX && new_mode == WL1273_AUDIO_DIGITAL) {
+		r = wl1273_fm_write_cmd(core, WL1273_PCM_MODE_SET,
+					WL1273_PCM_DEF_MODE);
+		if (r)
+			goto out;
+
+		r = wl1273_fm_write_cmd(core, WL1273_I2S_MODE_CONFIG_SET,
+					core->i2s_mode);
+		if (r)
+			goto out;
+
+		r = wl1273_fm_write_cmd(core, WL1273_AUDIO_ENABLE,
+					WL1273_AUDIO_ENABLE_I2S);
+		if (r)
+			goto out;
+
+	} else if (core->mode == WL1273_MODE_RX &&
+		   new_mode == WL1273_AUDIO_ANALOG) {
+		r = wl1273_fm_write_cmd(core, WL1273_AUDIO_ENABLE,
+					WL1273_AUDIO_ENABLE_ANALOG);
+		if (r)
+			goto out;
+
+	} else if (core->mode == WL1273_MODE_TX &&
+		   new_mode == WL1273_AUDIO_DIGITAL) {
+		r = wl1273_fm_write_cmd(core, WL1273_I2S_MODE_CONFIG_SET,
+					core->i2s_mode);
+		if (r)
+			goto out;
+
+		r = wl1273_fm_write_cmd(core, WL1273_AUDIO_IO_SET,
+					WL1273_AUDIO_IO_SET_I2S);
+		if (r)
+			goto out;
+
+	} else if (core->mode == WL1273_MODE_TX &&
+		   new_mode == WL1273_AUDIO_ANALOG) {
+		r = wl1273_fm_write_cmd(core, WL1273_AUDIO_IO_SET,
+					WL1273_AUDIO_IO_SET_ANALOG);
+		if (r)
+			goto out;
+	}
+
+	core->audio_mode = new_mode;
+out:
+	return r;
+}
+
+/**
+ * wl1273_fm_set_volume() -	Set volume.
+ * @core:			A pointer to the device struct.
+ * @volume:			The new volume value.
+ */
+static int wl1273_fm_set_volume(struct wl1273_core *core, unsigned int volume)
+{
+	int r;
+
+	if (volume > WL1273_MAX_VOLUME)
+		return -EINVAL;
+
+	if (core->volume == volume)
+		return 0;
+
+	r = wl1273_fm_write_cmd(core, WL1273_VOLUME_SET, volume);
+	if (r)
+		return r;
+
+	core->volume = volume;
+	return 0;
+}
+
+static int wl1273_core_remove(struct i2c_client *client)
+{
+	struct wl1273_core *core = i2c_get_clientdata(client);
+
+	dev_dbg(&client->dev, "%s\n", __func__);
+
+	mfd_remove_devices(&client->dev);
+	kfree(core);
+
+	return 0;
+}
+
+static int __devinit wl1273_core_probe(struct i2c_client *client,
+				       const struct i2c_device_id *id)
+{
+	struct wl1273_fm_platform_data *pdata = client->dev.platform_data;
+	struct wl1273_core *core;
+	struct mfd_cell *cell;
+	int children = 0;
+	int r = 0;
+
+	dev_dbg(&client->dev, "%s\n", __func__);
+
+	if (!pdata) {
+		dev_err(&client->dev, "No platform data.\n");
+		return -EINVAL;
+	}
+
+	if (!(pdata->children & WL1273_RADIO_CHILD)) {
+		dev_err(&client->dev, "Cannot function without radio child.\n");
+		return -EINVAL;
+	}
+
+	core = kzalloc(sizeof(*core), GFP_KERNEL);
+	if (!core)
+		return -ENOMEM;
+
+	core->pdata = pdata;
+	core->client = client;
+	mutex_init(&core->lock);
+
+	i2c_set_clientdata(client, core);
+
+	dev_dbg(&client->dev, "%s: Have V4L2.\n", __func__);
+
+	cell = &core->cells[children];
+	cell->name = "wl1273_fm_radio";
+	cell->platform_data = &core;
+	cell->pdata_size = sizeof(core);
+	children++;
+
+	core->read = wl1273_fm_read_reg;
+	core->write = wl1273_fm_write_cmd;
+	core->write_data = wl1273_fm_write_data;
+	core->set_audio = wl1273_fm_set_audio;
+	core->set_volume = wl1273_fm_set_volume;
+
+	if (pdata->children & WL1273_CODEC_CHILD) {
+		cell = &core->cells[children];
+
+		dev_dbg(&client->dev, "%s: Have codec.\n", __func__);
+		cell->name = "wl1273-codec";
+		cell->platform_data = &core;
+		cell->pdata_size = sizeof(core);
+		children++;
+	}
+
+	dev_dbg(&client->dev, "%s: number of children: %d.\n",
+		__func__, children);
+
+	r = mfd_add_devices(&client->dev, -1, core->cells,
+			    children, NULL, 0);
+	if (r)
+		goto err;
+
+	return 0;
+
+err:
+	pdata->free_resources();
+	kfree(core);
+
+	dev_dbg(&client->dev, "%s\n", __func__);
+
+	return r;
+}
+
+static struct i2c_driver wl1273_core_driver = {
+	.driver = {
+		.name = WL1273_FM_DRIVER_NAME,
+	},
+	.probe = wl1273_core_probe,
+	.id_table = wl1273_driver_id_table,
+	.remove = __devexit_p(wl1273_core_remove),
+};
+
+static int __init wl1273_core_init(void)
+{
+	int r;
+
+	r = i2c_add_driver(&wl1273_core_driver);
+	if (r) {
+		pr_err(WL1273_FM_DRIVER_NAME
+		       ": driver registration failed\n");
+		return r;
+	}
+
+	return r;
+}
+
+static void __exit wl1273_core_exit(void)
+{
+	i2c_del_driver(&wl1273_core_driver);
+}
+late_initcall(wl1273_core_init);
+module_exit(wl1273_core_exit);
+
+MODULE_AUTHOR("Matti Aaltonen <matti.j.aaltonen@nokia.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/wm831x-auxadc.c b/ap/os/linux/linux-3.4.x/drivers/mfd/wm831x-auxadc.c
new file mode 100644
index 0000000..8721095
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/wm831x-auxadc.c
@@ -0,0 +1,299 @@
+/*
+ * wm831x-auxadc.c  --  AUXADC for Wolfson WM831x PMICs
+ *
+ * Copyright 2009-2011 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/mfd/core.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+
+#include <linux/mfd/wm831x/core.h>
+#include <linux/mfd/wm831x/pdata.h>
+#include <linux/mfd/wm831x/irq.h>
+#include <linux/mfd/wm831x/auxadc.h>
+#include <linux/mfd/wm831x/otp.h>
+#include <linux/mfd/wm831x/regulator.h>
+
+struct wm831x_auxadc_req {
+	struct list_head list;
+	enum wm831x_auxadc input;
+	int val;
+	struct completion done;
+};
+
+static int wm831x_auxadc_read_irq(struct wm831x *wm831x,
+				  enum wm831x_auxadc input)
+{
+	struct wm831x_auxadc_req *req;
+	int ret;
+	bool ena = false;
+
+	req = kzalloc(sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	init_completion(&req->done);
+	req->input = input;
+	req->val = -ETIMEDOUT;
+
+	mutex_lock(&wm831x->auxadc_lock);
+
+	/* Enqueue the request */
+	list_add(&req->list, &wm831x->auxadc_pending);
+
+	ena = !wm831x->auxadc_active;
+
+	if (ena) {
+		ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL,
+				      WM831X_AUX_ENA, WM831X_AUX_ENA);
+		if (ret != 0) {
+			dev_err(wm831x->dev, "Failed to enable AUXADC: %d\n",
+				ret);
+			goto out;
+		}
+	}
+
+	/* Enable the conversion if not already running */
+	if (!(wm831x->auxadc_active & (1 << input))) {
+		ret = wm831x_set_bits(wm831x, WM831X_AUXADC_SOURCE,
+				      1 << input, 1 << input);
+		if (ret != 0) {
+			dev_err(wm831x->dev,
+				"Failed to set AUXADC source: %d\n", ret);
+			goto out;
+		}
+
+		wm831x->auxadc_active |= 1 << input;
+	}
+
+	/* We convert at the fastest rate possible */
+	if (ena) {
+		ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL,
+				      WM831X_AUX_CVT_ENA |
+				      WM831X_AUX_RATE_MASK,
+				      WM831X_AUX_CVT_ENA |
+				      WM831X_AUX_RATE_MASK);
+		if (ret != 0) {
+			dev_err(wm831x->dev, "Failed to start AUXADC: %d\n",
+				ret);
+			goto out;
+		}
+	}
+
+	mutex_unlock(&wm831x->auxadc_lock);
+
+	/* Wait for an interrupt */
+	wait_for_completion_timeout(&req->done, msecs_to_jiffies(500));
+
+	mutex_lock(&wm831x->auxadc_lock);
+
+	list_del(&req->list);
+	ret = req->val;
+
+out:
+	mutex_unlock(&wm831x->auxadc_lock);
+
+	kfree(req);
+
+	return ret;
+}
+
+static irqreturn_t wm831x_auxadc_irq(int irq, void *irq_data)
+{
+	struct wm831x *wm831x = irq_data;
+	struct wm831x_auxadc_req *req;
+	int ret, input, val;
+
+	ret = wm831x_reg_read(wm831x, WM831X_AUXADC_DATA);
+	if (ret < 0) {
+		dev_err(wm831x->dev,
+			"Failed to read AUXADC data: %d\n", ret);
+		return IRQ_NONE;
+	}
+
+	input = ((ret & WM831X_AUX_DATA_SRC_MASK)
+		 >> WM831X_AUX_DATA_SRC_SHIFT) - 1;
+
+	if (input == 14)
+		input = WM831X_AUX_CAL;
+
+	val = ret & WM831X_AUX_DATA_MASK;
+
+	mutex_lock(&wm831x->auxadc_lock);
+
+	/* Disable this conversion, we're about to complete all users */
+	wm831x_set_bits(wm831x, WM831X_AUXADC_SOURCE,
+			1 << input, 0);
+	wm831x->auxadc_active &= ~(1 << input);
+
+	/* Turn off the entire convertor if idle */
+	if (!wm831x->auxadc_active)
+		wm831x_reg_write(wm831x, WM831X_AUXADC_CONTROL, 0);
+
+	/* Wake up any threads waiting for this request */
+	list_for_each_entry(req, &wm831x->auxadc_pending, list) {
+		if (req->input == input) {
+			req->val = val;
+			complete(&req->done);
+		}
+	}
+
+	mutex_unlock(&wm831x->auxadc_lock);
+
+	return IRQ_HANDLED;
+}
+
+static int wm831x_auxadc_read_polled(struct wm831x *wm831x,
+				     enum wm831x_auxadc input)
+{
+	int ret, src, timeout;
+
+	mutex_lock(&wm831x->auxadc_lock);
+
+	ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL,
+			      WM831X_AUX_ENA, WM831X_AUX_ENA);
+	if (ret < 0) {
+		dev_err(wm831x->dev, "Failed to enable AUXADC: %d\n", ret);
+		goto out;
+	}
+
+	/* We force a single source at present */
+	src = input;
+	ret = wm831x_reg_write(wm831x, WM831X_AUXADC_SOURCE,
+			       1 << src);
+	if (ret < 0) {
+		dev_err(wm831x->dev, "Failed to set AUXADC source: %d\n", ret);
+		goto out;
+	}
+
+	ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL,
+			      WM831X_AUX_CVT_ENA, WM831X_AUX_CVT_ENA);
+	if (ret < 0) {
+		dev_err(wm831x->dev, "Failed to start AUXADC: %d\n", ret);
+		goto disable;
+	}
+
+	/* If we're not using interrupts then poll the
+	 * interrupt status register */
+	timeout = 5;
+	while (timeout) {
+		msleep(1);
+
+		ret = wm831x_reg_read(wm831x,
+				      WM831X_INTERRUPT_STATUS_1);
+		if (ret < 0) {
+			dev_err(wm831x->dev,
+				"ISR 1 read failed: %d\n", ret);
+			goto disable;
+		}
+
+		/* Did it complete? */
+		if (ret & WM831X_AUXADC_DATA_EINT) {
+			wm831x_reg_write(wm831x,
+					 WM831X_INTERRUPT_STATUS_1,
+					 WM831X_AUXADC_DATA_EINT);
+			break;
+		} else {
+			dev_err(wm831x->dev,
+				"AUXADC conversion timeout\n");
+			ret = -EBUSY;
+			goto disable;
+		}
+	}
+
+	ret = wm831x_reg_read(wm831x, WM831X_AUXADC_DATA);
+	if (ret < 0) {
+		dev_err(wm831x->dev,
+			"Failed to read AUXADC data: %d\n", ret);
+		goto disable;
+	}
+
+	src = ((ret & WM831X_AUX_DATA_SRC_MASK)
+	       >> WM831X_AUX_DATA_SRC_SHIFT) - 1;
+
+	if (src == 14)
+		src = WM831X_AUX_CAL;
+
+	if (src != input) {
+		dev_err(wm831x->dev, "Data from source %d not %d\n",
+			src, input);
+		ret = -EINVAL;
+	} else {
+		ret &= WM831X_AUX_DATA_MASK;
+	}
+
+disable:
+	wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL, WM831X_AUX_ENA, 0);
+out:
+	mutex_unlock(&wm831x->auxadc_lock);
+	return ret;
+}
+
+/**
+ * wm831x_auxadc_read: Read a value from the WM831x AUXADC
+ *
+ * @wm831x: Device to read from.
+ * @input: AUXADC input to read.
+ */
+int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input)
+{
+	return wm831x->auxadc_read(wm831x, input);
+}
+EXPORT_SYMBOL_GPL(wm831x_auxadc_read);
+
+/**
+ * wm831x_auxadc_read_uv: Read a voltage from the WM831x AUXADC
+ *
+ * @wm831x: Device to read from.
+ * @input: AUXADC input to read.
+ */
+int wm831x_auxadc_read_uv(struct wm831x *wm831x, enum wm831x_auxadc input)
+{
+	int ret;
+
+	ret = wm831x_auxadc_read(wm831x, input);
+	if (ret < 0)
+		return ret;
+
+	ret *= 1465;
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wm831x_auxadc_read_uv);
+
+void wm831x_auxadc_init(struct wm831x *wm831x)
+{
+	int ret;
+
+	mutex_init(&wm831x->auxadc_lock);
+	INIT_LIST_HEAD(&wm831x->auxadc_pending);
+
+	if (wm831x->irq && wm831x->irq_base) {
+		wm831x->auxadc_read = wm831x_auxadc_read_irq;
+
+		ret = request_threaded_irq(wm831x->irq_base +
+					   WM831X_IRQ_AUXADC_DATA,
+					   NULL, wm831x_auxadc_irq, 0,
+					   "auxadc", wm831x);
+		if (ret < 0) {
+			dev_err(wm831x->dev, "AUXADC IRQ request failed: %d\n",
+				ret);
+			wm831x->auxadc_read = NULL;
+		}
+	}
+
+	if (!wm831x->auxadc_read)
+		wm831x->auxadc_read = wm831x_auxadc_read_polled;
+}
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/wm831x-core.c b/ap/os/linux/linux-3.4.x/drivers/mfd/wm831x-core.c
new file mode 100644
index 0000000..838056c
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/wm831x-core.c
@@ -0,0 +1,1938 @@
+/*
+ * wm831x-core.c  --  Device access for Wolfson WM831x PMICs
+ *
+ * Copyright 2009 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/bcd.h>
+#include <linux/delay.h>
+#include <linux/mfd/core.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+
+#include <linux/mfd/wm831x/core.h>
+#include <linux/mfd/wm831x/pdata.h>
+#include <linux/mfd/wm831x/irq.h>
+#include <linux/mfd/wm831x/auxadc.h>
+#include <linux/mfd/wm831x/otp.h>
+#include <linux/mfd/wm831x/pmu.h>
+#include <linux/mfd/wm831x/regulator.h>
+
+/* Current settings - values are 2*2^(reg_val/4) microamps.  These are
+ * exported since they are used by multiple drivers.
+ */
+int wm831x_isinkv_values[WM831X_ISINK_MAX_ISEL + 1] = {
+	2,
+	2,
+	3,
+	3,
+	4,
+	5,
+	6,
+	7,
+	8,
+	10,
+	11,
+	13,
+	16,
+	19,
+	23,
+	27,
+	32,
+	38,
+	45,
+	54,
+	64,
+	76,
+	91,
+	108,
+	128,
+	152,
+	181,
+	215,
+	256,
+	304,
+	362,
+	431,
+	512,
+	609,
+	724,
+	861,
+	1024,
+	1218,
+	1448,
+	1722,
+	2048,
+	2435,
+	2896,
+	3444,
+	4096,
+	4871,
+	5793,
+	6889,
+	8192,
+	9742,
+	11585,
+	13777,
+	16384,
+	19484,
+	23170,
+	27554,
+};
+EXPORT_SYMBOL_GPL(wm831x_isinkv_values);
+
+static int wm831x_reg_locked(struct wm831x *wm831x, unsigned short reg)
+{
+	if (!wm831x->locked)
+		return 0;
+
+	switch (reg) {
+	case WM831X_WATCHDOG:
+	case WM831X_DC4_CONTROL:
+	case WM831X_ON_PIN_CONTROL:
+	case WM831X_BACKUP_CHARGER_CONTROL:
+	case WM831X_CHARGER_CONTROL_1:
+	case WM831X_CHARGER_CONTROL_2:
+		return 1;
+
+	default:
+		return 0;
+	}
+}
+
+/**
+ * wm831x_reg_unlock: Unlock user keyed registers
+ *
+ * The WM831x has a user key preventing writes to particularly
+ * critical registers.  This function locks those registers,
+ * allowing writes to them.
+ */
+void wm831x_reg_lock(struct wm831x *wm831x)
+{
+	int ret;
+
+	ret = wm831x_reg_write(wm831x, WM831X_SECURITY_KEY, 0);
+	if (ret == 0) {
+		dev_vdbg(wm831x->dev, "Registers locked\n");
+
+		mutex_lock(&wm831x->io_lock);
+		WARN_ON(wm831x->locked);
+		wm831x->locked = 1;
+		mutex_unlock(&wm831x->io_lock);
+	} else {
+		dev_err(wm831x->dev, "Failed to lock registers: %d\n", ret);
+	}
+
+}
+EXPORT_SYMBOL_GPL(wm831x_reg_lock);
+
+/**
+ * wm831x_reg_unlock: Unlock user keyed registers
+ *
+ * The WM831x has a user key preventing writes to particularly
+ * critical registers.  This function locks those registers,
+ * preventing spurious writes.
+ */
+int wm831x_reg_unlock(struct wm831x *wm831x)
+{
+	int ret;
+
+	/* 0x9716 is the value required to unlock the registers */
+	ret = wm831x_reg_write(wm831x, WM831X_SECURITY_KEY, 0x9716);
+	if (ret == 0) {
+		dev_vdbg(wm831x->dev, "Registers unlocked\n");
+
+		mutex_lock(&wm831x->io_lock);
+		WARN_ON(!wm831x->locked);
+		wm831x->locked = 0;
+		mutex_unlock(&wm831x->io_lock);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wm831x_reg_unlock);
+
+static bool wm831x_reg_readable(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case WM831X_RESET_ID:
+	case WM831X_REVISION:
+	case WM831X_PARENT_ID:
+	case WM831X_SYSVDD_CONTROL:
+	case WM831X_THERMAL_MONITORING:
+	case WM831X_POWER_STATE:
+	case WM831X_WATCHDOG:
+	case WM831X_ON_PIN_CONTROL:
+	case WM831X_RESET_CONTROL:
+	case WM831X_CONTROL_INTERFACE:
+	case WM831X_SECURITY_KEY:
+	case WM831X_SOFTWARE_SCRATCH:
+	case WM831X_OTP_CONTROL:
+	case WM831X_GPIO_LEVEL:
+	case WM831X_SYSTEM_STATUS:
+	case WM831X_ON_SOURCE:
+	case WM831X_OFF_SOURCE:
+	case WM831X_SYSTEM_INTERRUPTS:
+	case WM831X_INTERRUPT_STATUS_1:
+	case WM831X_INTERRUPT_STATUS_2:
+	case WM831X_INTERRUPT_STATUS_3:
+	case WM831X_INTERRUPT_STATUS_4:
+	case WM831X_INTERRUPT_STATUS_5:
+	case WM831X_IRQ_CONFIG:
+	case WM831X_SYSTEM_INTERRUPTS_MASK:
+	case WM831X_INTERRUPT_STATUS_1_MASK:
+	case WM831X_INTERRUPT_STATUS_2_MASK:
+	case WM831X_INTERRUPT_STATUS_3_MASK:
+	case WM831X_INTERRUPT_STATUS_4_MASK:
+	case WM831X_INTERRUPT_STATUS_5_MASK:
+	case WM831X_RTC_WRITE_COUNTER:
+	case WM831X_RTC_TIME_1:
+	case WM831X_RTC_TIME_2:
+	case WM831X_RTC_ALARM_1:
+	case WM831X_RTC_ALARM_2:
+	case WM831X_RTC_CONTROL:
+	case WM831X_RTC_TRIM:
+	case WM831X_TOUCH_CONTROL_1:
+	case WM831X_TOUCH_CONTROL_2:
+	case WM831X_TOUCH_DATA_X:
+	case WM831X_TOUCH_DATA_Y:
+	case WM831X_TOUCH_DATA_Z:
+	case WM831X_AUXADC_DATA:
+	case WM831X_AUXADC_CONTROL:
+	case WM831X_AUXADC_SOURCE:
+	case WM831X_COMPARATOR_CONTROL:
+	case WM831X_COMPARATOR_1:
+	case WM831X_COMPARATOR_2:
+	case WM831X_COMPARATOR_3:
+	case WM831X_COMPARATOR_4:
+	case WM831X_GPIO1_CONTROL:
+	case WM831X_GPIO2_CONTROL:
+	case WM831X_GPIO3_CONTROL:
+	case WM831X_GPIO4_CONTROL:
+	case WM831X_GPIO5_CONTROL:
+	case WM831X_GPIO6_CONTROL:
+	case WM831X_GPIO7_CONTROL:
+	case WM831X_GPIO8_CONTROL:
+	case WM831X_GPIO9_CONTROL:
+	case WM831X_GPIO10_CONTROL:
+	case WM831X_GPIO11_CONTROL:
+	case WM831X_GPIO12_CONTROL:
+	case WM831X_GPIO13_CONTROL:
+	case WM831X_GPIO14_CONTROL:
+	case WM831X_GPIO15_CONTROL:
+	case WM831X_GPIO16_CONTROL:
+	case WM831X_CHARGER_CONTROL_1:
+	case WM831X_CHARGER_CONTROL_2:
+	case WM831X_CHARGER_STATUS:
+	case WM831X_BACKUP_CHARGER_CONTROL:
+	case WM831X_STATUS_LED_1:
+	case WM831X_STATUS_LED_2:
+	case WM831X_CURRENT_SINK_1:
+	case WM831X_CURRENT_SINK_2:
+	case WM831X_DCDC_ENABLE:
+	case WM831X_LDO_ENABLE:
+	case WM831X_DCDC_STATUS:
+	case WM831X_LDO_STATUS:
+	case WM831X_DCDC_UV_STATUS:
+	case WM831X_LDO_UV_STATUS:
+	case WM831X_DC1_CONTROL_1:
+	case WM831X_DC1_CONTROL_2:
+	case WM831X_DC1_ON_CONFIG:
+	case WM831X_DC1_SLEEP_CONTROL:
+	case WM831X_DC1_DVS_CONTROL:
+	case WM831X_DC2_CONTROL_1:
+	case WM831X_DC2_CONTROL_2:
+	case WM831X_DC2_ON_CONFIG:
+	case WM831X_DC2_SLEEP_CONTROL:
+	case WM831X_DC2_DVS_CONTROL:
+	case WM831X_DC3_CONTROL_1:
+	case WM831X_DC3_CONTROL_2:
+	case WM831X_DC3_ON_CONFIG:
+	case WM831X_DC3_SLEEP_CONTROL:
+	case WM831X_DC4_CONTROL:
+	case WM831X_DC4_SLEEP_CONTROL:
+	case WM831X_EPE1_CONTROL:
+	case WM831X_EPE2_CONTROL:
+	case WM831X_LDO1_CONTROL:
+	case WM831X_LDO1_ON_CONTROL:
+	case WM831X_LDO1_SLEEP_CONTROL:
+	case WM831X_LDO2_CONTROL:
+	case WM831X_LDO2_ON_CONTROL:
+	case WM831X_LDO2_SLEEP_CONTROL:
+	case WM831X_LDO3_CONTROL:
+	case WM831X_LDO3_ON_CONTROL:
+	case WM831X_LDO3_SLEEP_CONTROL:
+	case WM831X_LDO4_CONTROL:
+	case WM831X_LDO4_ON_CONTROL:
+	case WM831X_LDO4_SLEEP_CONTROL:
+	case WM831X_LDO5_CONTROL:
+	case WM831X_LDO5_ON_CONTROL:
+	case WM831X_LDO5_SLEEP_CONTROL:
+	case WM831X_LDO6_CONTROL:
+	case WM831X_LDO6_ON_CONTROL:
+	case WM831X_LDO6_SLEEP_CONTROL:
+	case WM831X_LDO7_CONTROL:
+	case WM831X_LDO7_ON_CONTROL:
+	case WM831X_LDO7_SLEEP_CONTROL:
+	case WM831X_LDO8_CONTROL:
+	case WM831X_LDO8_ON_CONTROL:
+	case WM831X_LDO8_SLEEP_CONTROL:
+	case WM831X_LDO9_CONTROL:
+	case WM831X_LDO9_ON_CONTROL:
+	case WM831X_LDO9_SLEEP_CONTROL:
+	case WM831X_LDO10_CONTROL:
+	case WM831X_LDO10_ON_CONTROL:
+	case WM831X_LDO10_SLEEP_CONTROL:
+	case WM831X_LDO11_ON_CONTROL:
+	case WM831X_LDO11_SLEEP_CONTROL:
+	case WM831X_POWER_GOOD_SOURCE_1:
+	case WM831X_POWER_GOOD_SOURCE_2:
+	case WM831X_CLOCK_CONTROL_1:
+	case WM831X_CLOCK_CONTROL_2:
+	case WM831X_FLL_CONTROL_1:
+	case WM831X_FLL_CONTROL_2:
+	case WM831X_FLL_CONTROL_3:
+	case WM831X_FLL_CONTROL_4:
+	case WM831X_FLL_CONTROL_5:
+	case WM831X_UNIQUE_ID_1:
+	case WM831X_UNIQUE_ID_2:
+	case WM831X_UNIQUE_ID_3:
+	case WM831X_UNIQUE_ID_4:
+	case WM831X_UNIQUE_ID_5:
+	case WM831X_UNIQUE_ID_6:
+	case WM831X_UNIQUE_ID_7:
+	case WM831X_UNIQUE_ID_8:
+	case WM831X_FACTORY_OTP_ID:
+	case WM831X_FACTORY_OTP_1:
+	case WM831X_FACTORY_OTP_2:
+	case WM831X_FACTORY_OTP_3:
+	case WM831X_FACTORY_OTP_4:
+	case WM831X_FACTORY_OTP_5:
+	case WM831X_CUSTOMER_OTP_ID:
+	case WM831X_DC1_OTP_CONTROL:
+	case WM831X_DC2_OTP_CONTROL:
+	case WM831X_DC3_OTP_CONTROL:
+	case WM831X_LDO1_2_OTP_CONTROL:
+	case WM831X_LDO3_4_OTP_CONTROL:
+	case WM831X_LDO5_6_OTP_CONTROL:
+	case WM831X_LDO7_8_OTP_CONTROL:
+	case WM831X_LDO9_10_OTP_CONTROL:
+	case WM831X_LDO11_EPE_CONTROL:
+	case WM831X_GPIO1_OTP_CONTROL:
+	case WM831X_GPIO2_OTP_CONTROL:
+	case WM831X_GPIO3_OTP_CONTROL:
+	case WM831X_GPIO4_OTP_CONTROL:
+	case WM831X_GPIO5_OTP_CONTROL:
+	case WM831X_GPIO6_OTP_CONTROL:
+	case WM831X_DBE_CHECK_DATA:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool wm831x_reg_writeable(struct device *dev, unsigned int reg)
+{
+	struct wm831x *wm831x = dev_get_drvdata(dev);
+
+	if (wm831x_reg_locked(wm831x, reg))
+		return false;
+
+	switch (reg) {
+	case WM831X_SYSVDD_CONTROL:
+	case WM831X_THERMAL_MONITORING:
+	case WM831X_POWER_STATE:
+	case WM831X_WATCHDOG:
+	case WM831X_ON_PIN_CONTROL:
+	case WM831X_RESET_CONTROL:
+	case WM831X_CONTROL_INTERFACE:
+	case WM831X_SECURITY_KEY:
+	case WM831X_SOFTWARE_SCRATCH:
+	case WM831X_OTP_CONTROL:
+	case WM831X_GPIO_LEVEL:
+	case WM831X_INTERRUPT_STATUS_1:
+	case WM831X_INTERRUPT_STATUS_2:
+	case WM831X_INTERRUPT_STATUS_3:
+	case WM831X_INTERRUPT_STATUS_4:
+	case WM831X_INTERRUPT_STATUS_5:
+	case WM831X_IRQ_CONFIG:
+	case WM831X_SYSTEM_INTERRUPTS_MASK:
+	case WM831X_INTERRUPT_STATUS_1_MASK:
+	case WM831X_INTERRUPT_STATUS_2_MASK:
+	case WM831X_INTERRUPT_STATUS_3_MASK:
+	case WM831X_INTERRUPT_STATUS_4_MASK:
+	case WM831X_INTERRUPT_STATUS_5_MASK:
+	case WM831X_RTC_TIME_1:
+	case WM831X_RTC_TIME_2:
+	case WM831X_RTC_ALARM_1:
+	case WM831X_RTC_ALARM_2:
+	case WM831X_RTC_CONTROL:
+	case WM831X_RTC_TRIM:
+	case WM831X_TOUCH_CONTROL_1:
+	case WM831X_TOUCH_CONTROL_2:
+	case WM831X_AUXADC_CONTROL:
+	case WM831X_AUXADC_SOURCE:
+	case WM831X_COMPARATOR_CONTROL:
+	case WM831X_COMPARATOR_1:
+	case WM831X_COMPARATOR_2:
+	case WM831X_COMPARATOR_3:
+	case WM831X_COMPARATOR_4:
+	case WM831X_GPIO1_CONTROL:
+	case WM831X_GPIO2_CONTROL:
+	case WM831X_GPIO3_CONTROL:
+	case WM831X_GPIO4_CONTROL:
+	case WM831X_GPIO5_CONTROL:
+	case WM831X_GPIO6_CONTROL:
+	case WM831X_GPIO7_CONTROL:
+	case WM831X_GPIO8_CONTROL:
+	case WM831X_GPIO9_CONTROL:
+	case WM831X_GPIO10_CONTROL:
+	case WM831X_GPIO11_CONTROL:
+	case WM831X_GPIO12_CONTROL:
+	case WM831X_GPIO13_CONTROL:
+	case WM831X_GPIO14_CONTROL:
+	case WM831X_GPIO15_CONTROL:
+	case WM831X_GPIO16_CONTROL:
+	case WM831X_CHARGER_CONTROL_1:
+	case WM831X_CHARGER_CONTROL_2:
+	case WM831X_CHARGER_STATUS:
+	case WM831X_BACKUP_CHARGER_CONTROL:
+	case WM831X_STATUS_LED_1:
+	case WM831X_STATUS_LED_2:
+	case WM831X_CURRENT_SINK_1:
+	case WM831X_CURRENT_SINK_2:
+	case WM831X_DCDC_ENABLE:
+	case WM831X_LDO_ENABLE:
+	case WM831X_DC1_CONTROL_1:
+	case WM831X_DC1_CONTROL_2:
+	case WM831X_DC1_ON_CONFIG:
+	case WM831X_DC1_SLEEP_CONTROL:
+	case WM831X_DC1_DVS_CONTROL:
+	case WM831X_DC2_CONTROL_1:
+	case WM831X_DC2_CONTROL_2:
+	case WM831X_DC2_ON_CONFIG:
+	case WM831X_DC2_SLEEP_CONTROL:
+	case WM831X_DC2_DVS_CONTROL:
+	case WM831X_DC3_CONTROL_1:
+	case WM831X_DC3_CONTROL_2:
+	case WM831X_DC3_ON_CONFIG:
+	case WM831X_DC3_SLEEP_CONTROL:
+	case WM831X_DC4_CONTROL:
+	case WM831X_DC4_SLEEP_CONTROL:
+	case WM831X_EPE1_CONTROL:
+	case WM831X_EPE2_CONTROL:
+	case WM831X_LDO1_CONTROL:
+	case WM831X_LDO1_ON_CONTROL:
+	case WM831X_LDO1_SLEEP_CONTROL:
+	case WM831X_LDO2_CONTROL:
+	case WM831X_LDO2_ON_CONTROL:
+	case WM831X_LDO2_SLEEP_CONTROL:
+	case WM831X_LDO3_CONTROL:
+	case WM831X_LDO3_ON_CONTROL:
+	case WM831X_LDO3_SLEEP_CONTROL:
+	case WM831X_LDO4_CONTROL:
+	case WM831X_LDO4_ON_CONTROL:
+	case WM831X_LDO4_SLEEP_CONTROL:
+	case WM831X_LDO5_CONTROL:
+	case WM831X_LDO5_ON_CONTROL:
+	case WM831X_LDO5_SLEEP_CONTROL:
+	case WM831X_LDO6_CONTROL:
+	case WM831X_LDO6_ON_CONTROL:
+	case WM831X_LDO6_SLEEP_CONTROL:
+	case WM831X_LDO7_CONTROL:
+	case WM831X_LDO7_ON_CONTROL:
+	case WM831X_LDO7_SLEEP_CONTROL:
+	case WM831X_LDO8_CONTROL:
+	case WM831X_LDO8_ON_CONTROL:
+	case WM831X_LDO8_SLEEP_CONTROL:
+	case WM831X_LDO9_CONTROL:
+	case WM831X_LDO9_ON_CONTROL:
+	case WM831X_LDO9_SLEEP_CONTROL:
+	case WM831X_LDO10_CONTROL:
+	case WM831X_LDO10_ON_CONTROL:
+	case WM831X_LDO10_SLEEP_CONTROL:
+	case WM831X_LDO11_ON_CONTROL:
+	case WM831X_LDO11_SLEEP_CONTROL:
+	case WM831X_POWER_GOOD_SOURCE_1:
+	case WM831X_POWER_GOOD_SOURCE_2:
+	case WM831X_CLOCK_CONTROL_1:
+	case WM831X_CLOCK_CONTROL_2:
+	case WM831X_FLL_CONTROL_1:
+	case WM831X_FLL_CONTROL_2:
+	case WM831X_FLL_CONTROL_3:
+	case WM831X_FLL_CONTROL_4:
+	case WM831X_FLL_CONTROL_5:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool wm831x_reg_volatile(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case WM831X_SYSTEM_STATUS:
+	case WM831X_ON_SOURCE:
+	case WM831X_OFF_SOURCE:
+	case WM831X_GPIO_LEVEL:
+	case WM831X_SYSTEM_INTERRUPTS:
+	case WM831X_INTERRUPT_STATUS_1:
+	case WM831X_INTERRUPT_STATUS_2:
+	case WM831X_INTERRUPT_STATUS_3:
+	case WM831X_INTERRUPT_STATUS_4:
+	case WM831X_INTERRUPT_STATUS_5:
+	case WM831X_RTC_TIME_1:
+	case WM831X_RTC_TIME_2:
+	case WM831X_TOUCH_DATA_X:
+	case WM831X_TOUCH_DATA_Y:
+	case WM831X_TOUCH_DATA_Z:
+	case WM831X_AUXADC_DATA:
+	case WM831X_CHARGER_STATUS:
+	case WM831X_DCDC_STATUS:
+	case WM831X_LDO_STATUS:
+	case WM831X_DCDC_UV_STATUS:
+	case WM831X_LDO_UV_STATUS:
+		return true;
+	default:
+		return false;
+	}
+}
+
+/**
+ * wm831x_reg_read: Read a single WM831x register.
+ *
+ * @wm831x: Device to read from.
+ * @reg: Register to read.
+ */
+int wm831x_reg_read(struct wm831x *wm831x, unsigned short reg)
+{
+	unsigned int val;
+	int ret;
+
+	ret = regmap_read(wm831x->regmap, reg, &val);
+
+	if (ret < 0)
+		return ret;
+	else
+		return val;
+}
+EXPORT_SYMBOL_GPL(wm831x_reg_read);
+
+/**
+ * wm831x_bulk_read: Read multiple WM831x registers
+ *
+ * @wm831x: Device to read from
+ * @reg: First register
+ * @count: Number of registers
+ * @buf: Buffer to fill.
+ */
+int wm831x_bulk_read(struct wm831x *wm831x, unsigned short reg,
+		     int count, u16 *buf)
+{
+	return regmap_bulk_read(wm831x->regmap, reg, buf, count);
+}
+EXPORT_SYMBOL_GPL(wm831x_bulk_read);
+
+static int wm831x_write(struct wm831x *wm831x, unsigned short reg,
+			int bytes, void *src)
+{
+	u16 *buf = src;
+	int i, ret;
+
+	BUG_ON(bytes % 2);
+	BUG_ON(bytes <= 0);
+
+	for (i = 0; i < bytes / 2; i++) {
+		if (wm831x_reg_locked(wm831x, reg))
+			return -EPERM;
+
+		dev_vdbg(wm831x->dev, "Write %04x to R%d(0x%x)\n",
+			 buf[i], reg + i, reg + i);
+		ret = regmap_write(wm831x->regmap, reg + i, buf[i]);
+		if (ret != 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * wm831x_reg_write: Write a single WM831x register.
+ *
+ * @wm831x: Device to write to.
+ * @reg: Register to write to.
+ * @val: Value to write.
+ */
+int wm831x_reg_write(struct wm831x *wm831x, unsigned short reg,
+		     unsigned short val)
+{
+	int ret;
+
+	mutex_lock(&wm831x->io_lock);
+
+	ret = wm831x_write(wm831x, reg, 2, &val);
+
+	mutex_unlock(&wm831x->io_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wm831x_reg_write);
+
+/**
+ * wm831x_set_bits: Set the value of a bitfield in a WM831x register
+ *
+ * @wm831x: Device to write to.
+ * @reg: Register to write to.
+ * @mask: Mask of bits to set.
+ * @val: Value to set (unshifted)
+ */
+int wm831x_set_bits(struct wm831x *wm831x, unsigned short reg,
+		    unsigned short mask, unsigned short val)
+{
+	int ret;
+
+	mutex_lock(&wm831x->io_lock);
+
+	if (!wm831x_reg_locked(wm831x, reg))
+		ret = regmap_update_bits(wm831x->regmap, reg, mask, val);
+	else
+		ret = -EPERM;
+
+	mutex_unlock(&wm831x->io_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wm831x_set_bits);
+
+static struct resource wm831x_dcdc1_resources[] = {
+	{
+		.start = WM831X_DC1_CONTROL_1,
+		.end   = WM831X_DC1_DVS_CONTROL,
+		.flags = IORESOURCE_IO,
+	},
+	{
+		.name  = "UV",
+		.start = WM831X_IRQ_UV_DC1,
+		.end   = WM831X_IRQ_UV_DC1,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.name  = "HC",
+		.start = WM831X_IRQ_HC_DC1,
+		.end   = WM831X_IRQ_HC_DC1,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+
+static struct resource wm831x_dcdc2_resources[] = {
+	{
+		.start = WM831X_DC2_CONTROL_1,
+		.end   = WM831X_DC2_DVS_CONTROL,
+		.flags = IORESOURCE_IO,
+	},
+	{
+		.name  = "UV",
+		.start = WM831X_IRQ_UV_DC2,
+		.end   = WM831X_IRQ_UV_DC2,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.name  = "HC",
+		.start = WM831X_IRQ_HC_DC2,
+		.end   = WM831X_IRQ_HC_DC2,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct resource wm831x_dcdc3_resources[] = {
+	{
+		.start = WM831X_DC3_CONTROL_1,
+		.end   = WM831X_DC3_SLEEP_CONTROL,
+		.flags = IORESOURCE_IO,
+	},
+	{
+		.name  = "UV",
+		.start = WM831X_IRQ_UV_DC3,
+		.end   = WM831X_IRQ_UV_DC3,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct resource wm831x_dcdc4_resources[] = {
+	{
+		.start = WM831X_DC4_CONTROL,
+		.end   = WM831X_DC4_SLEEP_CONTROL,
+		.flags = IORESOURCE_IO,
+	},
+	{
+		.name  = "UV",
+		.start = WM831X_IRQ_UV_DC4,
+		.end   = WM831X_IRQ_UV_DC4,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct resource wm8320_dcdc4_buck_resources[] = {
+	{
+		.start = WM831X_DC4_CONTROL,
+		.end   = WM832X_DC4_SLEEP_CONTROL,
+		.flags = IORESOURCE_IO,
+	},
+	{
+		.name  = "UV",
+		.start = WM831X_IRQ_UV_DC4,
+		.end   = WM831X_IRQ_UV_DC4,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct resource wm831x_gpio_resources[] = {
+	{
+		.start = WM831X_IRQ_GPIO_1,
+		.end   = WM831X_IRQ_GPIO_16,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct resource wm831x_isink1_resources[] = {
+	{
+		.start = WM831X_CURRENT_SINK_1,
+		.end   = WM831X_CURRENT_SINK_1,
+		.flags = IORESOURCE_IO,
+	},
+	{
+		.start = WM831X_IRQ_CS1,
+		.end   = WM831X_IRQ_CS1,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct resource wm831x_isink2_resources[] = {
+	{
+		.start = WM831X_CURRENT_SINK_2,
+		.end   = WM831X_CURRENT_SINK_2,
+		.flags = IORESOURCE_IO,
+	},
+	{
+		.start = WM831X_IRQ_CS2,
+		.end   = WM831X_IRQ_CS2,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct resource wm831x_ldo1_resources[] = {
+	{
+		.start = WM831X_LDO1_CONTROL,
+		.end   = WM831X_LDO1_SLEEP_CONTROL,
+		.flags = IORESOURCE_IO,
+	},
+	{
+		.name  = "UV",
+		.start = WM831X_IRQ_UV_LDO1,
+		.end   = WM831X_IRQ_UV_LDO1,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct resource wm831x_ldo2_resources[] = {
+	{
+		.start = WM831X_LDO2_CONTROL,
+		.end   = WM831X_LDO2_SLEEP_CONTROL,
+		.flags = IORESOURCE_IO,
+	},
+	{
+		.name  = "UV",
+		.start = WM831X_IRQ_UV_LDO2,
+		.end   = WM831X_IRQ_UV_LDO2,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct resource wm831x_ldo3_resources[] = {
+	{
+		.start = WM831X_LDO3_CONTROL,
+		.end   = WM831X_LDO3_SLEEP_CONTROL,
+		.flags = IORESOURCE_IO,
+	},
+	{
+		.name  = "UV",
+		.start = WM831X_IRQ_UV_LDO3,
+		.end   = WM831X_IRQ_UV_LDO3,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct resource wm831x_ldo4_resources[] = {
+	{
+		.start = WM831X_LDO4_CONTROL,
+		.end   = WM831X_LDO4_SLEEP_CONTROL,
+		.flags = IORESOURCE_IO,
+	},
+	{
+		.name  = "UV",
+		.start = WM831X_IRQ_UV_LDO4,
+		.end   = WM831X_IRQ_UV_LDO4,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct resource wm831x_ldo5_resources[] = {
+	{
+		.start = WM831X_LDO5_CONTROL,
+		.end   = WM831X_LDO5_SLEEP_CONTROL,
+		.flags = IORESOURCE_IO,
+	},
+	{
+		.name  = "UV",
+		.start = WM831X_IRQ_UV_LDO5,
+		.end   = WM831X_IRQ_UV_LDO5,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct resource wm831x_ldo6_resources[] = {
+	{
+		.start = WM831X_LDO6_CONTROL,
+		.end   = WM831X_LDO6_SLEEP_CONTROL,
+		.flags = IORESOURCE_IO,
+	},
+	{
+		.name  = "UV",
+		.start = WM831X_IRQ_UV_LDO6,
+		.end   = WM831X_IRQ_UV_LDO6,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct resource wm831x_ldo7_resources[] = {
+	{
+		.start = WM831X_LDO7_CONTROL,
+		.end   = WM831X_LDO7_SLEEP_CONTROL,
+		.flags = IORESOURCE_IO,
+	},
+	{
+		.name  = "UV",
+		.start = WM831X_IRQ_UV_LDO7,
+		.end   = WM831X_IRQ_UV_LDO7,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct resource wm831x_ldo8_resources[] = {
+	{
+		.start = WM831X_LDO8_CONTROL,
+		.end   = WM831X_LDO8_SLEEP_CONTROL,
+		.flags = IORESOURCE_IO,
+	},
+	{
+		.name  = "UV",
+		.start = WM831X_IRQ_UV_LDO8,
+		.end   = WM831X_IRQ_UV_LDO8,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct resource wm831x_ldo9_resources[] = {
+	{
+		.start = WM831X_LDO9_CONTROL,
+		.end   = WM831X_LDO9_SLEEP_CONTROL,
+		.flags = IORESOURCE_IO,
+	},
+	{
+		.name  = "UV",
+		.start = WM831X_IRQ_UV_LDO9,
+		.end   = WM831X_IRQ_UV_LDO9,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct resource wm831x_ldo10_resources[] = {
+	{
+		.start = WM831X_LDO10_CONTROL,
+		.end   = WM831X_LDO10_SLEEP_CONTROL,
+		.flags = IORESOURCE_IO,
+	},
+	{
+		.name  = "UV",
+		.start = WM831X_IRQ_UV_LDO10,
+		.end   = WM831X_IRQ_UV_LDO10,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct resource wm831x_ldo11_resources[] = {
+	{
+		.start = WM831X_LDO11_ON_CONTROL,
+		.end   = WM831X_LDO11_SLEEP_CONTROL,
+		.flags = IORESOURCE_IO,
+	},
+};
+
+static struct resource wm831x_on_resources[] = {
+	{
+		.start = WM831X_IRQ_ON,
+		.end   = WM831X_IRQ_ON,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+
+static struct resource wm831x_power_resources[] = {
+	{
+		.name = "SYSLO",
+		.start = WM831X_IRQ_PPM_SYSLO,
+		.end   = WM831X_IRQ_PPM_SYSLO,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.name = "PWR SRC",
+		.start = WM831X_IRQ_PPM_PWR_SRC,
+		.end   = WM831X_IRQ_PPM_PWR_SRC,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.name = "USB CURR",
+		.start = WM831X_IRQ_PPM_USB_CURR,
+		.end   = WM831X_IRQ_PPM_USB_CURR,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.name = "BATT HOT",
+		.start = WM831X_IRQ_CHG_BATT_HOT,
+		.end   = WM831X_IRQ_CHG_BATT_HOT,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.name = "BATT COLD",
+		.start = WM831X_IRQ_CHG_BATT_COLD,
+		.end   = WM831X_IRQ_CHG_BATT_COLD,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.name = "BATT FAIL",
+		.start = WM831X_IRQ_CHG_BATT_FAIL,
+		.end   = WM831X_IRQ_CHG_BATT_FAIL,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.name = "OV",
+		.start = WM831X_IRQ_CHG_OV,
+		.end   = WM831X_IRQ_CHG_OV,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.name = "END",
+		.start = WM831X_IRQ_CHG_END,
+		.end   = WM831X_IRQ_CHG_END,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.name = "TO",
+		.start = WM831X_IRQ_CHG_TO,
+		.end   = WM831X_IRQ_CHG_TO,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.name = "MODE",
+		.start = WM831X_IRQ_CHG_MODE,
+		.end   = WM831X_IRQ_CHG_MODE,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.name = "START",
+		.start = WM831X_IRQ_CHG_START,
+		.end   = WM831X_IRQ_CHG_START,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct resource wm831x_rtc_resources[] = {
+	{
+		.name = "PER",
+		.start = WM831X_IRQ_RTC_PER,
+		.end   = WM831X_IRQ_RTC_PER,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.name = "ALM",
+		.start = WM831X_IRQ_RTC_ALM,
+		.end   = WM831X_IRQ_RTC_ALM,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct resource wm831x_status1_resources[] = {
+	{
+		.start = WM831X_STATUS_LED_1,
+		.end   = WM831X_STATUS_LED_1,
+		.flags = IORESOURCE_IO,
+	},
+};
+
+static struct resource wm831x_status2_resources[] = {
+	{
+		.start = WM831X_STATUS_LED_2,
+		.end   = WM831X_STATUS_LED_2,
+		.flags = IORESOURCE_IO,
+	},
+};
+
+static struct resource wm831x_touch_resources[] = {
+	{
+		.name = "TCHPD",
+		.start = WM831X_IRQ_TCHPD,
+		.end   = WM831X_IRQ_TCHPD,
+		.flags = IORESOURCE_IRQ,
+	},
+	{
+		.name = "TCHDATA",
+		.start = WM831X_IRQ_TCHDATA,
+		.end   = WM831X_IRQ_TCHDATA,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct resource wm831x_wdt_resources[] = {
+	{
+		.start = WM831X_IRQ_WDOG_TO,
+		.end   = WM831X_IRQ_WDOG_TO,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct mfd_cell wm8310_devs[] = {
+	{
+		.name = "wm831x-backup",
+	},
+	{
+		.name = "wm831x-buckv",
+		.id = 1,
+		.num_resources = ARRAY_SIZE(wm831x_dcdc1_resources),
+		.resources = wm831x_dcdc1_resources,
+	},
+	{
+		.name = "wm831x-buckv",
+		.id = 2,
+		.num_resources = ARRAY_SIZE(wm831x_dcdc2_resources),
+		.resources = wm831x_dcdc2_resources,
+	},
+	{
+		.name = "wm831x-buckp",
+		.id = 3,
+		.num_resources = ARRAY_SIZE(wm831x_dcdc3_resources),
+		.resources = wm831x_dcdc3_resources,
+	},
+	{
+		.name = "wm831x-boostp",
+		.id = 4,
+		.num_resources = ARRAY_SIZE(wm831x_dcdc4_resources),
+		.resources = wm831x_dcdc4_resources,
+	},
+	{
+		.name = "wm831x-clk",
+	},
+	{
+		.name = "wm831x-epe",
+		.id = 1,
+	},
+	{
+		.name = "wm831x-epe",
+		.id = 2,
+	},
+	{
+		.name = "wm831x-gpio",
+		.num_resources = ARRAY_SIZE(wm831x_gpio_resources),
+		.resources = wm831x_gpio_resources,
+	},
+	{
+		.name = "wm831x-hwmon",
+	},
+	{
+		.name = "wm831x-isink",
+		.id = 1,
+		.num_resources = ARRAY_SIZE(wm831x_isink1_resources),
+		.resources = wm831x_isink1_resources,
+	},
+	{
+		.name = "wm831x-isink",
+		.id = 2,
+		.num_resources = ARRAY_SIZE(wm831x_isink2_resources),
+		.resources = wm831x_isink2_resources,
+	},
+	{
+		.name = "wm831x-ldo",
+		.id = 1,
+		.num_resources = ARRAY_SIZE(wm831x_ldo1_resources),
+		.resources = wm831x_ldo1_resources,
+	},
+	{
+		.name = "wm831x-ldo",
+		.id = 2,
+		.num_resources = ARRAY_SIZE(wm831x_ldo2_resources),
+		.resources = wm831x_ldo2_resources,
+	},
+	{
+		.name = "wm831x-ldo",
+		.id = 3,
+		.num_resources = ARRAY_SIZE(wm831x_ldo3_resources),
+		.resources = wm831x_ldo3_resources,
+	},
+	{
+		.name = "wm831x-ldo",
+		.id = 4,
+		.num_resources = ARRAY_SIZE(wm831x_ldo4_resources),
+		.resources = wm831x_ldo4_resources,
+	},
+	{
+		.name = "wm831x-ldo",
+		.id = 5,
+		.num_resources = ARRAY_SIZE(wm831x_ldo5_resources),
+		.resources = wm831x_ldo5_resources,
+	},
+	{
+		.name = "wm831x-ldo",
+		.id = 6,
+		.num_resources = ARRAY_SIZE(wm831x_ldo6_resources),
+		.resources = wm831x_ldo6_resources,
+	},
+	{
+		.name = "wm831x-aldo",
+		.id = 7,
+		.num_resources = ARRAY_SIZE(wm831x_ldo7_resources),
+		.resources = wm831x_ldo7_resources,
+	},
+	{
+		.name = "wm831x-aldo",
+		.id = 8,
+		.num_resources = ARRAY_SIZE(wm831x_ldo8_resources),
+		.resources = wm831x_ldo8_resources,
+	},
+	{
+		.name = "wm831x-aldo",
+		.id = 9,
+		.num_resources = ARRAY_SIZE(wm831x_ldo9_resources),
+		.resources = wm831x_ldo9_resources,
+	},
+	{
+		.name = "wm831x-aldo",
+		.id = 10,
+		.num_resources = ARRAY_SIZE(wm831x_ldo10_resources),
+		.resources = wm831x_ldo10_resources,
+	},
+	{
+		.name = "wm831x-alive-ldo",
+		.id = 11,
+		.num_resources = ARRAY_SIZE(wm831x_ldo11_resources),
+		.resources = wm831x_ldo11_resources,
+	},
+	{
+		.name = "wm831x-on",
+		.num_resources = ARRAY_SIZE(wm831x_on_resources),
+		.resources = wm831x_on_resources,
+	},
+	{
+		.name = "wm831x-power",
+		.num_resources = ARRAY_SIZE(wm831x_power_resources),
+		.resources = wm831x_power_resources,
+	},
+	{
+		.name = "wm831x-status",
+		.id = 1,
+		.num_resources = ARRAY_SIZE(wm831x_status1_resources),
+		.resources = wm831x_status1_resources,
+	},
+	{
+		.name = "wm831x-status",
+		.id = 2,
+		.num_resources = ARRAY_SIZE(wm831x_status2_resources),
+		.resources = wm831x_status2_resources,
+	},
+	{
+		.name = "wm831x-watchdog",
+		.num_resources = ARRAY_SIZE(wm831x_wdt_resources),
+		.resources = wm831x_wdt_resources,
+	},
+};
+
+static struct mfd_cell wm8311_devs[] = {
+	{
+		.name = "wm831x-backup",
+	},
+	{
+		.name = "wm831x-buckv",
+		.id = 1,
+		.num_resources = ARRAY_SIZE(wm831x_dcdc1_resources),
+		.resources = wm831x_dcdc1_resources,
+	},
+	{
+		.name = "wm831x-buckv",
+		.id = 2,
+		.num_resources = ARRAY_SIZE(wm831x_dcdc2_resources),
+		.resources = wm831x_dcdc2_resources,
+	},
+	{
+		.name = "wm831x-buckp",
+		.id = 3,
+		.num_resources = ARRAY_SIZE(wm831x_dcdc3_resources),
+		.resources = wm831x_dcdc3_resources,
+	},
+	{
+		.name = "wm831x-boostp",
+		.id = 4,
+		.num_resources = ARRAY_SIZE(wm831x_dcdc4_resources),
+		.resources = wm831x_dcdc4_resources,
+	},
+	{
+		.name = "wm831x-clk",
+	},
+	{
+		.name = "wm831x-epe",
+		.id = 1,
+	},
+	{
+		.name = "wm831x-epe",
+		.id = 2,
+	},
+	{
+		.name = "wm831x-gpio",
+		.num_resources = ARRAY_SIZE(wm831x_gpio_resources),
+		.resources = wm831x_gpio_resources,
+	},
+	{
+		.name = "wm831x-hwmon",
+	},
+	{
+		.name = "wm831x-isink",
+		.id = 1,
+		.num_resources = ARRAY_SIZE(wm831x_isink1_resources),
+		.resources = wm831x_isink1_resources,
+	},
+	{
+		.name = "wm831x-isink",
+		.id = 2,
+		.num_resources = ARRAY_SIZE(wm831x_isink2_resources),
+		.resources = wm831x_isink2_resources,
+	},
+	{
+		.name = "wm831x-ldo",
+		.id = 1,
+		.num_resources = ARRAY_SIZE(wm831x_ldo1_resources),
+		.resources = wm831x_ldo1_resources,
+	},
+	{
+		.name = "wm831x-ldo",
+		.id = 2,
+		.num_resources = ARRAY_SIZE(wm831x_ldo2_resources),
+		.resources = wm831x_ldo2_resources,
+	},
+	{
+		.name = "wm831x-ldo",
+		.id = 3,
+		.num_resources = ARRAY_SIZE(wm831x_ldo3_resources),
+		.resources = wm831x_ldo3_resources,
+	},
+	{
+		.name = "wm831x-ldo",
+		.id = 4,
+		.num_resources = ARRAY_SIZE(wm831x_ldo4_resources),
+		.resources = wm831x_ldo4_resources,
+	},
+	{
+		.name = "wm831x-ldo",
+		.id = 5,
+		.num_resources = ARRAY_SIZE(wm831x_ldo5_resources),
+		.resources = wm831x_ldo5_resources,
+	},
+	{
+		.name = "wm831x-aldo",
+		.id = 7,
+		.num_resources = ARRAY_SIZE(wm831x_ldo7_resources),
+		.resources = wm831x_ldo7_resources,
+	},
+	{
+		.name = "wm831x-alive-ldo",
+		.id = 11,
+		.num_resources = ARRAY_SIZE(wm831x_ldo11_resources),
+		.resources = wm831x_ldo11_resources,
+	},
+	{
+		.name = "wm831x-on",
+		.num_resources = ARRAY_SIZE(wm831x_on_resources),
+		.resources = wm831x_on_resources,
+	},
+	{
+		.name = "wm831x-power",
+		.num_resources = ARRAY_SIZE(wm831x_power_resources),
+		.resources = wm831x_power_resources,
+	},
+	{
+		.name = "wm831x-status",
+		.id = 1,
+		.num_resources = ARRAY_SIZE(wm831x_status1_resources),
+		.resources = wm831x_status1_resources,
+	},
+	{
+		.name = "wm831x-status",
+		.id = 2,
+		.num_resources = ARRAY_SIZE(wm831x_status2_resources),
+		.resources = wm831x_status2_resources,
+	},
+	{
+		.name = "wm831x-watchdog",
+		.num_resources = ARRAY_SIZE(wm831x_wdt_resources),
+		.resources = wm831x_wdt_resources,
+	},
+};
+
+static struct mfd_cell wm8312_devs[] = {
+	{
+		.name = "wm831x-backup",
+	},
+	{
+		.name = "wm831x-buckv",
+		.id = 1,
+		.num_resources = ARRAY_SIZE(wm831x_dcdc1_resources),
+		.resources = wm831x_dcdc1_resources,
+	},
+	{
+		.name = "wm831x-buckv",
+		.id = 2,
+		.num_resources = ARRAY_SIZE(wm831x_dcdc2_resources),
+		.resources = wm831x_dcdc2_resources,
+	},
+	{
+		.name = "wm831x-buckp",
+		.id = 3,
+		.num_resources = ARRAY_SIZE(wm831x_dcdc3_resources),
+		.resources = wm831x_dcdc3_resources,
+	},
+	{
+		.name = "wm831x-boostp",
+		.id = 4,
+		.num_resources = ARRAY_SIZE(wm831x_dcdc4_resources),
+		.resources = wm831x_dcdc4_resources,
+	},
+	{
+		.name = "wm831x-clk",
+	},
+	{
+		.name = "wm831x-epe",
+		.id = 1,
+	},
+	{
+		.name = "wm831x-epe",
+		.id = 2,
+	},
+	{
+		.name = "wm831x-gpio",
+		.num_resources = ARRAY_SIZE(wm831x_gpio_resources),
+		.resources = wm831x_gpio_resources,
+	},
+	{
+		.name = "wm831x-hwmon",
+	},
+	{
+		.name = "wm831x-isink",
+		.id = 1,
+		.num_resources = ARRAY_SIZE(wm831x_isink1_resources),
+		.resources = wm831x_isink1_resources,
+	},
+	{
+		.name = "wm831x-isink",
+		.id = 2,
+		.num_resources = ARRAY_SIZE(wm831x_isink2_resources),
+		.resources = wm831x_isink2_resources,
+	},
+	{
+		.name = "wm831x-ldo",
+		.id = 1,
+		.num_resources = ARRAY_SIZE(wm831x_ldo1_resources),
+		.resources = wm831x_ldo1_resources,
+	},
+	{
+		.name = "wm831x-ldo",
+		.id = 2,
+		.num_resources = ARRAY_SIZE(wm831x_ldo2_resources),
+		.resources = wm831x_ldo2_resources,
+	},
+	{
+		.name = "wm831x-ldo",
+		.id = 3,
+		.num_resources = ARRAY_SIZE(wm831x_ldo3_resources),
+		.resources = wm831x_ldo3_resources,
+	},
+	{
+		.name = "wm831x-ldo",
+		.id = 4,
+		.num_resources = ARRAY_SIZE(wm831x_ldo4_resources),
+		.resources = wm831x_ldo4_resources,
+	},
+	{
+		.name = "wm831x-ldo",
+		.id = 5,
+		.num_resources = ARRAY_SIZE(wm831x_ldo5_resources),
+		.resources = wm831x_ldo5_resources,
+	},
+	{
+		.name = "wm831x-ldo",
+		.id = 6,
+		.num_resources = ARRAY_SIZE(wm831x_ldo6_resources),
+		.resources = wm831x_ldo6_resources,
+	},
+	{
+		.name = "wm831x-aldo",
+		.id = 7,
+		.num_resources = ARRAY_SIZE(wm831x_ldo7_resources),
+		.resources = wm831x_ldo7_resources,
+	},
+	{
+		.name = "wm831x-aldo",
+		.id = 8,
+		.num_resources = ARRAY_SIZE(wm831x_ldo8_resources),
+		.resources = wm831x_ldo8_resources,
+	},
+	{
+		.name = "wm831x-aldo",
+		.id = 9,
+		.num_resources = ARRAY_SIZE(wm831x_ldo9_resources),
+		.resources = wm831x_ldo9_resources,
+	},
+	{
+		.name = "wm831x-aldo",
+		.id = 10,
+		.num_resources = ARRAY_SIZE(wm831x_ldo10_resources),
+		.resources = wm831x_ldo10_resources,
+	},
+	{
+		.name = "wm831x-alive-ldo",
+		.id = 11,
+		.num_resources = ARRAY_SIZE(wm831x_ldo11_resources),
+		.resources = wm831x_ldo11_resources,
+	},
+	{
+		.name = "wm831x-on",
+		.num_resources = ARRAY_SIZE(wm831x_on_resources),
+		.resources = wm831x_on_resources,
+	},
+	{
+		.name = "wm831x-power",
+		.num_resources = ARRAY_SIZE(wm831x_power_resources),
+		.resources = wm831x_power_resources,
+	},
+	{
+		.name = "wm831x-status",
+		.id = 1,
+		.num_resources = ARRAY_SIZE(wm831x_status1_resources),
+		.resources = wm831x_status1_resources,
+	},
+	{
+		.name = "wm831x-status",
+		.id = 2,
+		.num_resources = ARRAY_SIZE(wm831x_status2_resources),
+		.resources = wm831x_status2_resources,
+	},
+	{
+		.name = "wm831x-watchdog",
+		.num_resources = ARRAY_SIZE(wm831x_wdt_resources),
+		.resources = wm831x_wdt_resources,
+	},
+};
+
+static struct mfd_cell wm8320_devs[] = {
+	{
+		.name = "wm831x-backup",
+	},
+	{
+		.name = "wm831x-buckv",
+		.id = 1,
+		.num_resources = ARRAY_SIZE(wm831x_dcdc1_resources),
+		.resources = wm831x_dcdc1_resources,
+	},
+	{
+		.name = "wm831x-buckv",
+		.id = 2,
+		.num_resources = ARRAY_SIZE(wm831x_dcdc2_resources),
+		.resources = wm831x_dcdc2_resources,
+	},
+	{
+		.name = "wm831x-buckp",
+		.id = 3,
+		.num_resources = ARRAY_SIZE(wm831x_dcdc3_resources),
+		.resources = wm831x_dcdc3_resources,
+	},
+	{
+		.name = "wm831x-buckp",
+		.id = 4,
+		.num_resources = ARRAY_SIZE(wm8320_dcdc4_buck_resources),
+		.resources = wm8320_dcdc4_buck_resources,
+	},
+	{
+		.name = "wm831x-clk",
+	},
+	{
+		.name = "wm831x-gpio",
+		.num_resources = ARRAY_SIZE(wm831x_gpio_resources),
+		.resources = wm831x_gpio_resources,
+	},
+	{
+		.name = "wm831x-hwmon",
+	},
+	{
+		.name = "wm831x-ldo",
+		.id = 1,
+		.num_resources = ARRAY_SIZE(wm831x_ldo1_resources),
+		.resources = wm831x_ldo1_resources,
+	},
+	{
+		.name = "wm831x-ldo",
+		.id = 2,
+		.num_resources = ARRAY_SIZE(wm831x_ldo2_resources),
+		.resources = wm831x_ldo2_resources,
+	},
+	{
+		.name = "wm831x-ldo",
+		.id = 3,
+		.num_resources = ARRAY_SIZE(wm831x_ldo3_resources),
+		.resources = wm831x_ldo3_resources,
+	},
+	{
+		.name = "wm831x-ldo",
+		.id = 4,
+		.num_resources = ARRAY_SIZE(wm831x_ldo4_resources),
+		.resources = wm831x_ldo4_resources,
+	},
+	{
+		.name = "wm831x-ldo",
+		.id = 5,
+		.num_resources = ARRAY_SIZE(wm831x_ldo5_resources),
+		.resources = wm831x_ldo5_resources,
+	},
+	{
+		.name = "wm831x-ldo",
+		.id = 6,
+		.num_resources = ARRAY_SIZE(wm831x_ldo6_resources),
+		.resources = wm831x_ldo6_resources,
+	},
+	{
+		.name = "wm831x-aldo",
+		.id = 7,
+		.num_resources = ARRAY_SIZE(wm831x_ldo7_resources),
+		.resources = wm831x_ldo7_resources,
+	},
+	{
+		.name = "wm831x-aldo",
+		.id = 8,
+		.num_resources = ARRAY_SIZE(wm831x_ldo8_resources),
+		.resources = wm831x_ldo8_resources,
+	},
+	{
+		.name = "wm831x-aldo",
+		.id = 9,
+		.num_resources = ARRAY_SIZE(wm831x_ldo9_resources),
+		.resources = wm831x_ldo9_resources,
+	},
+	{
+		.name = "wm831x-aldo",
+		.id = 10,
+		.num_resources = ARRAY_SIZE(wm831x_ldo10_resources),
+		.resources = wm831x_ldo10_resources,
+	},
+	{
+		.name = "wm831x-alive-ldo",
+		.id = 11,
+		.num_resources = ARRAY_SIZE(wm831x_ldo11_resources),
+		.resources = wm831x_ldo11_resources,
+	},
+	{
+		.name = "wm831x-on",
+		.num_resources = ARRAY_SIZE(wm831x_on_resources),
+		.resources = wm831x_on_resources,
+	},
+	{
+		.name = "wm831x-status",
+		.id = 1,
+		.num_resources = ARRAY_SIZE(wm831x_status1_resources),
+		.resources = wm831x_status1_resources,
+	},
+	{
+		.name = "wm831x-status",
+		.id = 2,
+		.num_resources = ARRAY_SIZE(wm831x_status2_resources),
+		.resources = wm831x_status2_resources,
+	},
+	{
+		.name = "wm831x-watchdog",
+		.num_resources = ARRAY_SIZE(wm831x_wdt_resources),
+		.resources = wm831x_wdt_resources,
+	},
+};
+
+static struct mfd_cell touch_devs[] = {
+	{
+		.name = "wm831x-touch",
+		.num_resources = ARRAY_SIZE(wm831x_touch_resources),
+		.resources = wm831x_touch_resources,
+	},
+};
+
+static struct mfd_cell rtc_devs[] = {
+	{
+		.name = "wm831x-rtc",
+		.num_resources = ARRAY_SIZE(wm831x_rtc_resources),
+		.resources = wm831x_rtc_resources,
+	},
+};
+
+static struct mfd_cell backlight_devs[] = {
+	{
+		.name = "wm831x-backlight",
+	},
+};
+
+struct regmap_config wm831x_regmap_config = {
+	.reg_bits = 16,
+	.val_bits = 16,
+
+	.cache_type = REGCACHE_RBTREE,
+
+	.max_register = WM831X_DBE_CHECK_DATA,
+	.readable_reg = wm831x_reg_readable,
+	.writeable_reg = wm831x_reg_writeable,
+	.volatile_reg = wm831x_reg_volatile,
+};
+EXPORT_SYMBOL_GPL(wm831x_regmap_config);
+
+/*
+ * Instantiate the generic non-control parts of the device.
+ */
+int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
+{
+	struct wm831x_pdata *pdata = wm831x->dev->platform_data;
+	int rev, wm831x_num;
+	enum wm831x_parent parent;
+	int ret, i;
+
+	mutex_init(&wm831x->io_lock);
+	mutex_init(&wm831x->key_lock);
+	dev_set_drvdata(wm831x->dev, wm831x);
+	wm831x->soft_shutdown = pdata->soft_shutdown;
+
+	ret = wm831x_reg_read(wm831x, WM831X_PARENT_ID);
+	if (ret < 0) {
+		dev_err(wm831x->dev, "Failed to read parent ID: %d\n", ret);
+		goto err;
+	}
+	switch (ret) {
+	case 0x6204:
+	case 0x6246:
+		break;
+	default:
+		dev_err(wm831x->dev, "Device is not a WM831x: ID %x\n", ret);
+		ret = -EINVAL;
+		goto err;
+	}
+
+	ret = wm831x_reg_read(wm831x, WM831X_REVISION);
+	if (ret < 0) {
+		dev_err(wm831x->dev, "Failed to read revision: %d\n", ret);
+		goto err;
+	}
+	rev = (ret & WM831X_PARENT_REV_MASK) >> WM831X_PARENT_REV_SHIFT;
+
+	ret = wm831x_reg_read(wm831x, WM831X_RESET_ID);
+	if (ret < 0) {
+		dev_err(wm831x->dev, "Failed to read device ID: %d\n", ret);
+		goto err;
+	}
+
+	/* Some engineering samples do not have the ID set, rely on
+	 * the device being registered correctly.
+	 */
+	if (ret == 0) {
+		dev_info(wm831x->dev, "Device is an engineering sample\n");
+		ret = id;
+	}
+
+	switch (ret) {
+	case WM8310:
+		parent = WM8310;
+		wm831x->num_gpio = 16;
+		wm831x->charger_irq_wake = 1;
+		if (rev > 0) {
+			wm831x->has_gpio_ena = 1;
+			wm831x->has_cs_sts = 1;
+		}
+
+		dev_info(wm831x->dev, "WM8310 revision %c\n", 'A' + rev);
+		break;
+
+	case WM8311:
+		parent = WM8311;
+		wm831x->num_gpio = 16;
+		wm831x->charger_irq_wake = 1;
+		if (rev > 0) {
+			wm831x->has_gpio_ena = 1;
+			wm831x->has_cs_sts = 1;
+		}
+
+		dev_info(wm831x->dev, "WM8311 revision %c\n", 'A' + rev);
+		break;
+
+	case WM8312:
+		parent = WM8312;
+		wm831x->num_gpio = 16;
+		wm831x->charger_irq_wake = 1;
+		if (rev > 0) {
+			wm831x->has_gpio_ena = 1;
+			wm831x->has_cs_sts = 1;
+		}
+
+		dev_info(wm831x->dev, "WM8312 revision %c\n", 'A' + rev);
+		break;
+
+	case WM8320:
+		parent = WM8320;
+		wm831x->num_gpio = 12;
+		dev_info(wm831x->dev, "WM8320 revision %c\n", 'A' + rev);
+		break;
+
+	case WM8321:
+		parent = WM8321;
+		wm831x->num_gpio = 12;
+		dev_info(wm831x->dev, "WM8321 revision %c\n", 'A' + rev);
+		break;
+
+	case WM8325:
+		parent = WM8325;
+		wm831x->num_gpio = 12;
+		dev_info(wm831x->dev, "WM8325 revision %c\n", 'A' + rev);
+		break;
+
+	case WM8326:
+		parent = WM8326;
+		wm831x->num_gpio = 12;
+		dev_info(wm831x->dev, "WM8326 revision %c\n", 'A' + rev);
+		break;
+
+	default:
+		dev_err(wm831x->dev, "Unknown WM831x device %04x\n", ret);
+		ret = -EINVAL;
+		goto err;
+	}
+
+	/* This will need revisiting in future but is OK for all
+	 * current parts.
+	 */
+	if (parent != id)
+		dev_warn(wm831x->dev, "Device was registered as a WM%lx\n",
+			 id);
+
+	/* Bootstrap the user key */
+	ret = wm831x_reg_read(wm831x, WM831X_SECURITY_KEY);
+	if (ret < 0) {
+		dev_err(wm831x->dev, "Failed to read security key: %d\n", ret);
+		goto err;
+	}
+	if (ret != 0) {
+		dev_warn(wm831x->dev, "Security key had non-zero value %x\n",
+			 ret);
+		wm831x_reg_write(wm831x, WM831X_SECURITY_KEY, 0);
+	}
+	wm831x->locked = 1;
+
+	if (pdata && pdata->pre_init) {
+		ret = pdata->pre_init(wm831x);
+		if (ret != 0) {
+			dev_err(wm831x->dev, "pre_init() failed: %d\n", ret);
+			goto err;
+		}
+	}
+
+	if (pdata) {
+		for (i = 0; i < ARRAY_SIZE(pdata->gpio_defaults); i++) {
+			if (!pdata->gpio_defaults[i])
+				continue;
+
+			wm831x_reg_write(wm831x,
+					 WM831X_GPIO1_CONTROL + i,
+					 pdata->gpio_defaults[i] & 0xffff);
+		}
+	}
+
+	/* Multiply by 10 as we have many subdevices of the same type */
+	if (pdata && pdata->wm831x_num)
+		wm831x_num = pdata->wm831x_num * 10;
+	else
+		wm831x_num = -1;
+
+	ret = wm831x_irq_init(wm831x, irq);
+	if (ret != 0)
+		goto err;
+
+	wm831x_auxadc_init(wm831x);
+
+	/* The core device is up, instantiate the subdevices. */
+	switch (parent) {
+	case WM8310:
+		ret = mfd_add_devices(wm831x->dev, wm831x_num,
+				      wm8310_devs, ARRAY_SIZE(wm8310_devs),
+				      NULL, wm831x->irq_base);
+		break;
+
+	case WM8311:
+		ret = mfd_add_devices(wm831x->dev, wm831x_num,
+				      wm8311_devs, ARRAY_SIZE(wm8311_devs),
+				      NULL, wm831x->irq_base);
+		if (!pdata || !pdata->disable_touch)
+			mfd_add_devices(wm831x->dev, wm831x_num,
+					touch_devs, ARRAY_SIZE(touch_devs),
+					NULL, wm831x->irq_base);
+		break;
+
+	case WM8312:
+		ret = mfd_add_devices(wm831x->dev, wm831x_num,
+				      wm8312_devs, ARRAY_SIZE(wm8312_devs),
+				      NULL, wm831x->irq_base);
+		if (!pdata || !pdata->disable_touch)
+			mfd_add_devices(wm831x->dev, wm831x_num,
+					touch_devs, ARRAY_SIZE(touch_devs),
+					NULL, wm831x->irq_base);
+		break;
+
+	case WM8320:
+	case WM8321:
+	case WM8325:
+	case WM8326:
+		ret = mfd_add_devices(wm831x->dev, wm831x_num,
+				      wm8320_devs, ARRAY_SIZE(wm8320_devs),
+				      NULL, wm831x->irq_base);
+		break;
+
+	default:
+		/* If this happens the bus probe function is buggy */
+		BUG();
+	}
+
+	if (ret != 0) {
+		dev_err(wm831x->dev, "Failed to add children\n");
+		goto err_irq;
+	}
+
+	/* The RTC can only be used if the 32.768kHz crystal is
+	 * enabled; this can't be controlled by software at runtime.
+	 */
+	ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_2);
+	if (ret < 0) {
+		dev_err(wm831x->dev, "Failed to read clock status: %d\n", ret);
+		goto err_irq;
+	}
+
+	if (ret & WM831X_XTAL_ENA) {
+		ret = mfd_add_devices(wm831x->dev, wm831x_num,
+				      rtc_devs, ARRAY_SIZE(rtc_devs),
+				      NULL, wm831x->irq_base);
+		if (ret != 0) {
+			dev_err(wm831x->dev, "Failed to add RTC: %d\n", ret);
+			goto err_irq;
+		}
+	} else {
+		dev_info(wm831x->dev, "32.768kHz clock disabled, no RTC\n");
+	}
+
+	if (pdata && pdata->backlight) {
+		/* Treat errors as non-critical */
+		ret = mfd_add_devices(wm831x->dev, wm831x_num, backlight_devs,
+				      ARRAY_SIZE(backlight_devs), NULL,
+				      wm831x->irq_base);
+		if (ret < 0)
+			dev_err(wm831x->dev, "Failed to add backlight: %d\n",
+				ret);
+	}
+
+	wm831x_otp_init(wm831x);
+
+	if (pdata && pdata->post_init) {
+		ret = pdata->post_init(wm831x);
+		if (ret != 0) {
+			dev_err(wm831x->dev, "post_init() failed: %d\n", ret);
+			goto err_irq;
+		}
+	}
+
+	return 0;
+
+err_irq:
+	wm831x_irq_exit(wm831x);
+err:
+	mfd_remove_devices(wm831x->dev);
+	return ret;
+}
+
+void wm831x_device_exit(struct wm831x *wm831x)
+{
+	wm831x_otp_exit(wm831x);
+	mfd_remove_devices(wm831x->dev);
+	if (wm831x->irq_base)
+		free_irq(wm831x->irq_base + WM831X_IRQ_AUXADC_DATA, wm831x);
+	wm831x_irq_exit(wm831x);
+}
+
+int wm831x_device_suspend(struct wm831x *wm831x)
+{
+	int reg, mask;
+
+	/* If the charger IRQs are a wake source then make sure we ack
+	 * them even if they're not actively being used (eg, no power
+	 * driver or no IRQ line wired up) then acknowledge the
+	 * interrupts otherwise suspend won't last very long.
+	 */
+	if (wm831x->charger_irq_wake) {
+		reg = wm831x_reg_read(wm831x, WM831X_INTERRUPT_STATUS_2_MASK);
+
+		mask = WM831X_CHG_BATT_HOT_EINT |
+			WM831X_CHG_BATT_COLD_EINT |
+			WM831X_CHG_BATT_FAIL_EINT |
+			WM831X_CHG_OV_EINT | WM831X_CHG_END_EINT |
+			WM831X_CHG_TO_EINT | WM831X_CHG_MODE_EINT |
+			WM831X_CHG_START_EINT;
+
+		/* If any of the interrupts are masked read the statuses */
+		if (reg & mask)
+			reg = wm831x_reg_read(wm831x,
+					      WM831X_INTERRUPT_STATUS_2);
+
+		if (reg & mask) {
+			dev_info(wm831x->dev,
+				 "Acknowledging masked charger IRQs: %x\n",
+				 reg & mask);
+			wm831x_reg_write(wm831x, WM831X_INTERRUPT_STATUS_2,
+					 reg & mask);
+		}
+	}
+
+	return 0;
+}
+
+void wm831x_device_shutdown(struct wm831x *wm831x)
+{
+	if (wm831x->soft_shutdown) {
+		dev_info(wm831x->dev, "Initiating shutdown...\n");
+		wm831x_set_bits(wm831x, WM831X_POWER_STATE, WM831X_CHIP_ON, 0);
+	}
+}
+EXPORT_SYMBOL_GPL(wm831x_device_shutdown);
+
+MODULE_DESCRIPTION("Core support for the WM831X AudioPlus PMIC");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mark Brown");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/wm831x-i2c.c b/ap/os/linux/linux-3.4.x/drivers/mfd/wm831x-i2c.c
new file mode 100644
index 0000000..2b29cae
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/wm831x-i2c.c
@@ -0,0 +1,118 @@
+/*
+ * wm831x-i2c.c  --  I2C access for Wolfson WM831x PMICs
+ *
+ * Copyright 2009,2010 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/mfd/core.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/regmap.h>
+
+#include <linux/mfd/wm831x/core.h>
+#include <linux/mfd/wm831x/pdata.h>
+
+static int wm831x_i2c_probe(struct i2c_client *i2c,
+			    const struct i2c_device_id *id)
+{
+	struct wm831x *wm831x;
+	int ret;
+
+	wm831x = devm_kzalloc(&i2c->dev, sizeof(struct wm831x), GFP_KERNEL);
+	if (wm831x == NULL)
+		return -ENOMEM;
+
+	i2c_set_clientdata(i2c, wm831x);
+	wm831x->dev = &i2c->dev;
+
+	wm831x->regmap = devm_regmap_init_i2c(i2c, &wm831x_regmap_config);
+	if (IS_ERR(wm831x->regmap)) {
+		ret = PTR_ERR(wm831x->regmap);
+		dev_err(wm831x->dev, "Failed to allocate register map: %d\n",
+			ret);
+		return ret;
+	}
+
+	return wm831x_device_init(wm831x, id->driver_data, i2c->irq);
+}
+
+static int wm831x_i2c_remove(struct i2c_client *i2c)
+{
+	struct wm831x *wm831x = i2c_get_clientdata(i2c);
+
+	wm831x_device_exit(wm831x);
+
+	return 0;
+}
+
+static int wm831x_i2c_suspend(struct device *dev)
+{
+	struct wm831x *wm831x = dev_get_drvdata(dev);
+
+	return wm831x_device_suspend(wm831x);
+}
+
+static void wm831x_i2c_shutdown(struct i2c_client *i2c)
+{
+	struct wm831x *wm831x = i2c_get_clientdata(i2c);
+
+	wm831x_device_shutdown(wm831x);
+}
+
+static const struct i2c_device_id wm831x_i2c_id[] = {
+	{ "wm8310", WM8310 },
+	{ "wm8311", WM8311 },
+	{ "wm8312", WM8312 },
+	{ "wm8320", WM8320 },
+	{ "wm8321", WM8321 },
+	{ "wm8325", WM8325 },
+	{ "wm8326", WM8326 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, wm831x_i2c_id);
+
+static const struct dev_pm_ops wm831x_pm_ops = {
+	.suspend = wm831x_i2c_suspend,
+};
+
+static struct i2c_driver wm831x_i2c_driver = {
+	.driver = {
+		.name = "wm831x",
+		.owner = THIS_MODULE,
+		.pm = &wm831x_pm_ops,
+	},
+	.probe = wm831x_i2c_probe,
+	.remove = wm831x_i2c_remove,
+	.shutdown = wm831x_i2c_shutdown,
+	.id_table = wm831x_i2c_id,
+};
+
+static int __init wm831x_i2c_init(void)
+{
+	int ret;
+
+	ret = i2c_add_driver(&wm831x_i2c_driver);
+	if (ret != 0)
+		pr_err("Failed to register wm831x I2C driver: %d\n", ret);
+
+	return ret;
+}
+subsys_initcall(wm831x_i2c_init);
+
+static void __exit wm831x_i2c_exit(void)
+{
+	i2c_del_driver(&wm831x_i2c_driver);
+}
+module_exit(wm831x_i2c_exit);
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/wm831x-irq.c b/ap/os/linux/linux-3.4.x/drivers/mfd/wm831x-irq.c
new file mode 100644
index 0000000..bec4d05
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/wm831x-irq.c
@@ -0,0 +1,623 @@
+/*
+ * wm831x-irq.c  --  Interrupt controller support for Wolfson WM831x PMICs
+ *
+ * Copyright 2009 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/mfd/core.h>
+#include <linux/interrupt.h>
+
+#include <linux/mfd/wm831x/core.h>
+#include <linux/mfd/wm831x/pdata.h>
+#include <linux/mfd/wm831x/gpio.h>
+#include <linux/mfd/wm831x/irq.h>
+
+#include <linux/delay.h>
+
+struct wm831x_irq_data {
+	int primary;
+	int reg;
+	int mask;
+};
+
+static struct wm831x_irq_data wm831x_irqs[] = {
+	[WM831X_IRQ_TEMP_THW] = {
+		.primary = WM831X_TEMP_INT,
+		.reg = 1,
+		.mask = WM831X_TEMP_THW_EINT,
+	},
+	[WM831X_IRQ_GPIO_1] = {
+		.primary = WM831X_GP_INT,
+		.reg = 5,
+		.mask = WM831X_GP1_EINT,
+	},
+	[WM831X_IRQ_GPIO_2] = {
+		.primary = WM831X_GP_INT,
+		.reg = 5,
+		.mask = WM831X_GP2_EINT,
+	},
+	[WM831X_IRQ_GPIO_3] = {
+		.primary = WM831X_GP_INT,
+		.reg = 5,
+		.mask = WM831X_GP3_EINT,
+	},
+	[WM831X_IRQ_GPIO_4] = {
+		.primary = WM831X_GP_INT,
+		.reg = 5,
+		.mask = WM831X_GP4_EINT,
+	},
+	[WM831X_IRQ_GPIO_5] = {
+		.primary = WM831X_GP_INT,
+		.reg = 5,
+		.mask = WM831X_GP5_EINT,
+	},
+	[WM831X_IRQ_GPIO_6] = {
+		.primary = WM831X_GP_INT,
+		.reg = 5,
+		.mask = WM831X_GP6_EINT,
+	},
+	[WM831X_IRQ_GPIO_7] = {
+		.primary = WM831X_GP_INT,
+		.reg = 5,
+		.mask = WM831X_GP7_EINT,
+	},
+	[WM831X_IRQ_GPIO_8] = {
+		.primary = WM831X_GP_INT,
+		.reg = 5,
+		.mask = WM831X_GP8_EINT,
+	},
+	[WM831X_IRQ_GPIO_9] = {
+		.primary = WM831X_GP_INT,
+		.reg = 5,
+		.mask = WM831X_GP9_EINT,
+	},
+	[WM831X_IRQ_GPIO_10] = {
+		.primary = WM831X_GP_INT,
+		.reg = 5,
+		.mask = WM831X_GP10_EINT,
+	},
+	[WM831X_IRQ_GPIO_11] = {
+		.primary = WM831X_GP_INT,
+		.reg = 5,
+		.mask = WM831X_GP11_EINT,
+	},
+	[WM831X_IRQ_GPIO_12] = {
+		.primary = WM831X_GP_INT,
+		.reg = 5,
+		.mask = WM831X_GP12_EINT,
+	},
+	[WM831X_IRQ_GPIO_13] = {
+		.primary = WM831X_GP_INT,
+		.reg = 5,
+		.mask = WM831X_GP13_EINT,
+	},
+	[WM831X_IRQ_GPIO_14] = {
+		.primary = WM831X_GP_INT,
+		.reg = 5,
+		.mask = WM831X_GP14_EINT,
+	},
+	[WM831X_IRQ_GPIO_15] = {
+		.primary = WM831X_GP_INT,
+		.reg = 5,
+		.mask = WM831X_GP15_EINT,
+	},
+	[WM831X_IRQ_GPIO_16] = {
+		.primary = WM831X_GP_INT,
+		.reg = 5,
+		.mask = WM831X_GP16_EINT,
+	},
+	[WM831X_IRQ_ON] = {
+		.primary = WM831X_ON_PIN_INT,
+		.reg = 1,
+		.mask = WM831X_ON_PIN_EINT,
+	},
+	[WM831X_IRQ_PPM_SYSLO] = {
+		.primary = WM831X_PPM_INT,
+		.reg = 1,
+		.mask = WM831X_PPM_SYSLO_EINT,
+	},
+	[WM831X_IRQ_PPM_PWR_SRC] = {
+		.primary = WM831X_PPM_INT,
+		.reg = 1,
+		.mask = WM831X_PPM_PWR_SRC_EINT,
+	},
+	[WM831X_IRQ_PPM_USB_CURR] = {
+		.primary = WM831X_PPM_INT,
+		.reg = 1,
+		.mask = WM831X_PPM_USB_CURR_EINT,
+	},
+	[WM831X_IRQ_WDOG_TO] = {
+		.primary = WM831X_WDOG_INT,
+		.reg = 1,
+		.mask = WM831X_WDOG_TO_EINT,
+	},
+	[WM831X_IRQ_RTC_PER] = {
+		.primary = WM831X_RTC_INT,
+		.reg = 1,
+		.mask = WM831X_RTC_PER_EINT,
+	},
+	[WM831X_IRQ_RTC_ALM] = {
+		.primary = WM831X_RTC_INT,
+		.reg = 1,
+		.mask = WM831X_RTC_ALM_EINT,
+	},
+	[WM831X_IRQ_CHG_BATT_HOT] = {
+		.primary = WM831X_CHG_INT,
+		.reg = 2,
+		.mask = WM831X_CHG_BATT_HOT_EINT,
+	},
+	[WM831X_IRQ_CHG_BATT_COLD] = {
+		.primary = WM831X_CHG_INT,
+		.reg = 2,
+		.mask = WM831X_CHG_BATT_COLD_EINT,
+	},
+	[WM831X_IRQ_CHG_BATT_FAIL] = {
+		.primary = WM831X_CHG_INT,
+		.reg = 2,
+		.mask = WM831X_CHG_BATT_FAIL_EINT,
+	},
+	[WM831X_IRQ_CHG_OV] = {
+		.primary = WM831X_CHG_INT,
+		.reg = 2,
+		.mask = WM831X_CHG_OV_EINT,
+	},
+	[WM831X_IRQ_CHG_END] = {
+		.primary = WM831X_CHG_INT,
+		.reg = 2,
+		.mask = WM831X_CHG_END_EINT,
+	},
+	[WM831X_IRQ_CHG_TO] = {
+		.primary = WM831X_CHG_INT,
+		.reg = 2,
+		.mask = WM831X_CHG_TO_EINT,
+	},
+	[WM831X_IRQ_CHG_MODE] = {
+		.primary = WM831X_CHG_INT,
+		.reg = 2,
+		.mask = WM831X_CHG_MODE_EINT,
+	},
+	[WM831X_IRQ_CHG_START] = {
+		.primary = WM831X_CHG_INT,
+		.reg = 2,
+		.mask = WM831X_CHG_START_EINT,
+	},
+	[WM831X_IRQ_TCHDATA] = {
+		.primary = WM831X_TCHDATA_INT,
+		.reg = 1,
+		.mask = WM831X_TCHDATA_EINT,
+	},
+	[WM831X_IRQ_TCHPD] = {
+		.primary = WM831X_TCHPD_INT,
+		.reg = 1,
+		.mask = WM831X_TCHPD_EINT,
+	},
+	[WM831X_IRQ_AUXADC_DATA] = {
+		.primary = WM831X_AUXADC_INT,
+		.reg = 1,
+		.mask = WM831X_AUXADC_DATA_EINT,
+	},
+	[WM831X_IRQ_AUXADC_DCOMP1] = {
+		.primary = WM831X_AUXADC_INT,
+		.reg = 1,
+		.mask = WM831X_AUXADC_DCOMP1_EINT,
+	},
+	[WM831X_IRQ_AUXADC_DCOMP2] = {
+		.primary = WM831X_AUXADC_INT,
+		.reg = 1,
+		.mask = WM831X_AUXADC_DCOMP2_EINT,
+	},
+	[WM831X_IRQ_AUXADC_DCOMP3] = {
+		.primary = WM831X_AUXADC_INT,
+		.reg = 1,
+		.mask = WM831X_AUXADC_DCOMP3_EINT,
+	},
+	[WM831X_IRQ_AUXADC_DCOMP4] = {
+		.primary = WM831X_AUXADC_INT,
+		.reg = 1,
+		.mask = WM831X_AUXADC_DCOMP4_EINT,
+	},
+	[WM831X_IRQ_CS1] = {
+		.primary = WM831X_CS_INT,
+		.reg = 2,
+		.mask = WM831X_CS1_EINT,
+	},
+	[WM831X_IRQ_CS2] = {
+		.primary = WM831X_CS_INT,
+		.reg = 2,
+		.mask = WM831X_CS2_EINT,
+	},
+	[WM831X_IRQ_HC_DC1] = {
+		.primary = WM831X_HC_INT,
+		.reg = 4,
+		.mask = WM831X_HC_DC1_EINT,
+	},
+	[WM831X_IRQ_HC_DC2] = {
+		.primary = WM831X_HC_INT,
+		.reg = 4,
+		.mask = WM831X_HC_DC2_EINT,
+	},
+	[WM831X_IRQ_UV_LDO1] = {
+		.primary = WM831X_UV_INT,
+		.reg = 3,
+		.mask = WM831X_UV_LDO1_EINT,
+	},
+	[WM831X_IRQ_UV_LDO2] = {
+		.primary = WM831X_UV_INT,
+		.reg = 3,
+		.mask = WM831X_UV_LDO2_EINT,
+	},
+	[WM831X_IRQ_UV_LDO3] = {
+		.primary = WM831X_UV_INT,
+		.reg = 3,
+		.mask = WM831X_UV_LDO3_EINT,
+	},
+	[WM831X_IRQ_UV_LDO4] = {
+		.primary = WM831X_UV_INT,
+		.reg = 3,
+		.mask = WM831X_UV_LDO4_EINT,
+	},
+	[WM831X_IRQ_UV_LDO5] = {
+		.primary = WM831X_UV_INT,
+		.reg = 3,
+		.mask = WM831X_UV_LDO5_EINT,
+	},
+	[WM831X_IRQ_UV_LDO6] = {
+		.primary = WM831X_UV_INT,
+		.reg = 3,
+		.mask = WM831X_UV_LDO6_EINT,
+	},
+	[WM831X_IRQ_UV_LDO7] = {
+		.primary = WM831X_UV_INT,
+		.reg = 3,
+		.mask = WM831X_UV_LDO7_EINT,
+	},
+	[WM831X_IRQ_UV_LDO8] = {
+		.primary = WM831X_UV_INT,
+		.reg = 3,
+		.mask = WM831X_UV_LDO8_EINT,
+	},
+	[WM831X_IRQ_UV_LDO9] = {
+		.primary = WM831X_UV_INT,
+		.reg = 3,
+		.mask = WM831X_UV_LDO9_EINT,
+	},
+	[WM831X_IRQ_UV_LDO10] = {
+		.primary = WM831X_UV_INT,
+		.reg = 3,
+		.mask = WM831X_UV_LDO10_EINT,
+	},
+	[WM831X_IRQ_UV_DC1] = {
+		.primary = WM831X_UV_INT,
+		.reg = 4,
+		.mask = WM831X_UV_DC1_EINT,
+	},
+	[WM831X_IRQ_UV_DC2] = {
+		.primary = WM831X_UV_INT,
+		.reg = 4,
+		.mask = WM831X_UV_DC2_EINT,
+	},
+	[WM831X_IRQ_UV_DC3] = {
+		.primary = WM831X_UV_INT,
+		.reg = 4,
+		.mask = WM831X_UV_DC3_EINT,
+	},
+	[WM831X_IRQ_UV_DC4] = {
+		.primary = WM831X_UV_INT,
+		.reg = 4,
+		.mask = WM831X_UV_DC4_EINT,
+	},
+};
+
+static inline int irq_data_to_status_reg(struct wm831x_irq_data *irq_data)
+{
+	return WM831X_INTERRUPT_STATUS_1 - 1 + irq_data->reg;
+}
+
+static inline struct wm831x_irq_data *irq_to_wm831x_irq(struct wm831x *wm831x,
+							int irq)
+{
+	return &wm831x_irqs[irq - wm831x->irq_base];
+}
+
+static void wm831x_irq_lock(struct irq_data *data)
+{
+	struct wm831x *wm831x = irq_data_get_irq_chip_data(data);
+
+	mutex_lock(&wm831x->irq_lock);
+}
+
+static void wm831x_irq_sync_unlock(struct irq_data *data)
+{
+	struct wm831x *wm831x = irq_data_get_irq_chip_data(data);
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(wm831x->gpio_update); i++) {
+		if (wm831x->gpio_update[i]) {
+			wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + i,
+					WM831X_GPN_INT_MODE | WM831X_GPN_POL,
+					wm831x->gpio_update[i]);
+			wm831x->gpio_update[i] = 0;
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(wm831x->irq_masks_cur); i++) {
+		/* If there's been a change in the mask write it back
+		 * to the hardware. */
+		if (wm831x->irq_masks_cur[i] != wm831x->irq_masks_cache[i]) {
+			dev_dbg(wm831x->dev, "IRQ mask sync: %x = %x\n",
+				WM831X_INTERRUPT_STATUS_1_MASK + i,
+				wm831x->irq_masks_cur[i]);
+
+			wm831x->irq_masks_cache[i] = wm831x->irq_masks_cur[i];
+			wm831x_reg_write(wm831x,
+					 WM831X_INTERRUPT_STATUS_1_MASK + i,
+					 wm831x->irq_masks_cur[i]);
+		}
+	}
+
+	mutex_unlock(&wm831x->irq_lock);
+}
+
+static void wm831x_irq_enable(struct irq_data *data)
+{
+	struct wm831x *wm831x = irq_data_get_irq_chip_data(data);
+	struct wm831x_irq_data *irq_data = irq_to_wm831x_irq(wm831x,
+							     data->irq);
+
+	wm831x->irq_masks_cur[irq_data->reg - 1] &= ~irq_data->mask;
+}
+
+static void wm831x_irq_disable(struct irq_data *data)
+{
+	struct wm831x *wm831x = irq_data_get_irq_chip_data(data);
+	struct wm831x_irq_data *irq_data = irq_to_wm831x_irq(wm831x,
+							     data->irq);
+
+	wm831x->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask;
+}
+
+static int wm831x_irq_set_type(struct irq_data *data, unsigned int type)
+{
+	struct wm831x *wm831x = irq_data_get_irq_chip_data(data);
+	int irq;
+
+	irq = data->irq - wm831x->irq_base;
+
+	if (irq < WM831X_IRQ_GPIO_1 || irq > WM831X_IRQ_GPIO_11) {
+		/* Ignore internal-only IRQs */
+		if (irq >= 0 && irq < WM831X_NUM_IRQS)
+			return 0;
+		else
+			return -EINVAL;
+	}
+
+	/* Rebase the IRQ into the GPIO range so we've got a sensible array
+	 * index.
+	 */
+	irq -= WM831X_IRQ_GPIO_1;
+
+	/* We set the high bit to flag that we need an update; don't
+	 * do the update here as we can be called with the bus lock
+	 * held.
+	 */
+	switch (type) {
+	case IRQ_TYPE_EDGE_BOTH:
+		wm831x->gpio_update[irq] = 0x10000 | WM831X_GPN_INT_MODE;
+		wm831x->gpio_level[irq] = false;
+		break;
+	case IRQ_TYPE_EDGE_RISING:
+		wm831x->gpio_update[irq] = 0x10000 | WM831X_GPN_POL;
+		wm831x->gpio_level[irq] = false;
+		break;
+	case IRQ_TYPE_EDGE_FALLING:
+		wm831x->gpio_update[irq] = 0x10000;
+		wm831x->gpio_level[irq] = false;
+		break;
+	case IRQ_TYPE_LEVEL_HIGH:
+		wm831x->gpio_update[irq] = 0x10000 | WM831X_GPN_POL;
+		wm831x->gpio_level[irq] = true;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static struct irq_chip wm831x_irq_chip = {
+	.name			= "wm831x",
+	.irq_bus_lock		= wm831x_irq_lock,
+	.irq_bus_sync_unlock	= wm831x_irq_sync_unlock,
+	.irq_disable		= wm831x_irq_disable,
+	.irq_enable		= wm831x_irq_enable,
+	.irq_set_type		= wm831x_irq_set_type,
+};
+
+/* The processing of the primary interrupt occurs in a thread so that
+ * we can interact with the device over I2C or SPI. */
+static irqreturn_t wm831x_irq_thread(int irq, void *data)
+{
+	struct wm831x *wm831x = data;
+	unsigned int i;
+	int primary, status_addr, ret;
+	int status_regs[WM831X_NUM_IRQ_REGS] = { 0 };
+	int read[WM831X_NUM_IRQ_REGS] = { 0 };
+	int *status;
+
+	primary = wm831x_reg_read(wm831x, WM831X_SYSTEM_INTERRUPTS);
+	if (primary < 0) {
+		dev_err(wm831x->dev, "Failed to read system interrupt: %d\n",
+			primary);
+		goto out;
+	}
+
+	/* The touch interrupts are visible in the primary register as
+	 * an optimisation; open code this to avoid complicating the
+	 * main handling loop and so we can also skip iterating the
+	 * descriptors.
+	 */
+	if (primary & WM831X_TCHPD_INT)
+		handle_nested_irq(wm831x->irq_base + WM831X_IRQ_TCHPD);
+	if (primary & WM831X_TCHDATA_INT)
+		handle_nested_irq(wm831x->irq_base + WM831X_IRQ_TCHDATA);
+	primary &= ~(WM831X_TCHDATA_EINT | WM831X_TCHPD_EINT);
+
+	for (i = 0; i < ARRAY_SIZE(wm831x_irqs); i++) {
+		int offset = wm831x_irqs[i].reg - 1;
+
+		if (!(primary & wm831x_irqs[i].primary))
+			continue;
+
+		status = &status_regs[offset];
+
+		/* Hopefully there should only be one register to read
+		 * each time otherwise we ought to do a block read. */
+		if (!read[offset]) {
+			status_addr = irq_data_to_status_reg(&wm831x_irqs[i]);
+
+			*status = wm831x_reg_read(wm831x, status_addr);
+			if (*status < 0) {
+				dev_err(wm831x->dev,
+					"Failed to read IRQ status: %d\n",
+					*status);
+				goto out;
+			}
+
+			read[offset] = 1;
+
+			/* Ignore any bits that we don't think are masked */
+			*status &= ~wm831x->irq_masks_cur[offset];
+
+			/* Acknowledge now so we don't miss
+			 * notifications while we handle.
+			 */
+			wm831x_reg_write(wm831x, status_addr, *status);
+		}
+
+		if (*status & wm831x_irqs[i].mask)
+			handle_nested_irq(wm831x->irq_base + i);
+
+		/* Simulate an edge triggered IRQ by polling the input
+		 * status.  This is sucky but improves interoperability.
+		 */
+		if (primary == WM831X_GP_INT &&
+		    wm831x->gpio_level[i - WM831X_IRQ_GPIO_1]) {
+			ret = wm831x_reg_read(wm831x, WM831X_GPIO_LEVEL);
+			while (ret & 1 << (i - WM831X_IRQ_GPIO_1)) {
+				handle_nested_irq(wm831x->irq_base + i);
+				ret = wm831x_reg_read(wm831x,
+						      WM831X_GPIO_LEVEL);
+			}
+		}
+	}
+
+out:
+	return IRQ_HANDLED;
+}
+
+int wm831x_irq_init(struct wm831x *wm831x, int irq)
+{
+	struct wm831x_pdata *pdata = wm831x->dev->platform_data;
+	int i, cur_irq, ret;
+
+	mutex_init(&wm831x->irq_lock);
+
+	/* Mask the individual interrupt sources */
+	for (i = 0; i < ARRAY_SIZE(wm831x->irq_masks_cur); i++) {
+		wm831x->irq_masks_cur[i] = 0xffff;
+		wm831x->irq_masks_cache[i] = 0xffff;
+		wm831x_reg_write(wm831x, WM831X_INTERRUPT_STATUS_1_MASK + i,
+				 0xffff);
+	}
+
+	/* Try to dynamically allocate IRQs if no base is specified */
+	if (!pdata || !pdata->irq_base)
+		wm831x->irq_base = -1;
+	else
+		wm831x->irq_base = pdata->irq_base;
+
+	wm831x->irq_base = irq_alloc_descs(wm831x->irq_base, 0,
+					   WM831X_NUM_IRQS, 0);
+	if (wm831x->irq_base < 0) {
+		dev_warn(wm831x->dev, "Failed to allocate IRQs: %d\n",
+			 wm831x->irq_base);
+		wm831x->irq_base = 0;
+		return 0;
+	}
+
+	if (pdata && pdata->irq_cmos)
+		i = 0;
+	else
+		i = WM831X_IRQ_OD;
+
+	wm831x_set_bits(wm831x, WM831X_IRQ_CONFIG,
+			WM831X_IRQ_OD, i);
+
+	/* Try to flag /IRQ as a wake source; there are a number of
+	 * unconditional wake sources in the PMIC so this isn't
+	 * conditional but we don't actually care *too* much if it
+	 * fails.
+	 */
+	ret = enable_irq_wake(irq);
+	if (ret != 0) {
+		dev_warn(wm831x->dev, "Can't enable IRQ as wake source: %d\n",
+			 ret);
+	}
+
+	wm831x->irq = irq;
+
+	/* Register them with genirq */
+	for (cur_irq = wm831x->irq_base;
+	     cur_irq < ARRAY_SIZE(wm831x_irqs) + wm831x->irq_base;
+	     cur_irq++) {
+		irq_set_chip_data(cur_irq, wm831x);
+		irq_set_chip_and_handler(cur_irq, &wm831x_irq_chip,
+					 handle_edge_irq);
+		irq_set_nested_thread(cur_irq, 1);
+
+		/* ARM needs us to explicitly flag the IRQ as valid
+		 * and will set them noprobe when we do so. */
+#ifdef CONFIG_ARM
+		set_irq_flags(cur_irq, IRQF_VALID);
+#else
+		irq_set_noprobe(cur_irq);
+#endif
+	}
+
+	if (irq) {
+		ret = request_threaded_irq(irq, NULL, wm831x_irq_thread,
+					   IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+					   "wm831x", wm831x);
+		if (ret != 0) {
+			dev_err(wm831x->dev, "Failed to request IRQ %d: %d\n",
+				irq, ret);
+			return ret;
+		}
+	} else {
+		dev_warn(wm831x->dev,
+			 "No interrupt specified - functionality limited\n");
+	}
+
+	/* Enable top level interrupts, we mask at secondary level */
+	wm831x_reg_write(wm831x, WM831X_SYSTEM_INTERRUPTS_MASK, 0);
+
+	return 0;
+}
+
+void wm831x_irq_exit(struct wm831x *wm831x)
+{
+	if (wm831x->irq)
+		free_irq(wm831x->irq, wm831x);
+}
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/wm831x-otp.c b/ap/os/linux/linux-3.4.x/drivers/mfd/wm831x-otp.c
new file mode 100644
index 0000000..b90f3e0
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/wm831x-otp.c
@@ -0,0 +1,91 @@
+/*
+ * wm831x-otp.c  --  OTP for Wolfson WM831x PMICs
+ *
+ * Copyright 2009 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/bcd.h>
+#include <linux/delay.h>
+#include <linux/mfd/core.h>
+#include <linux/random.h>
+
+#include <linux/mfd/wm831x/core.h>
+#include <linux/mfd/wm831x/otp.h>
+
+/* In bytes */
+#define WM831X_UNIQUE_ID_LEN 16
+
+/* Read the unique ID from the chip into id */
+static int wm831x_unique_id_read(struct wm831x *wm831x, char *id)
+{
+	int i, val;
+
+	for (i = 0; i < WM831X_UNIQUE_ID_LEN / 2; i++) {
+		val = wm831x_reg_read(wm831x, WM831X_UNIQUE_ID_1 + i);
+		if (val < 0)
+			return val;
+
+		id[i * 2]       = (val >> 8) & 0xff;
+		id[(i * 2) + 1] = val & 0xff;
+	}
+
+	return 0;
+}
+
+static ssize_t wm831x_unique_id_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	struct wm831x *wm831x = dev_get_drvdata(dev);
+	int i, rval;
+	char id[WM831X_UNIQUE_ID_LEN];
+	ssize_t ret = 0;
+
+	rval = wm831x_unique_id_read(wm831x, id);
+	if (rval < 0)
+		return 0;
+
+	for (i = 0; i < WM831X_UNIQUE_ID_LEN; i++)
+		ret += sprintf(&buf[ret], "%02x", buf[i]);
+
+	ret += sprintf(&buf[ret], "\n");
+
+	return ret;
+}
+
+static DEVICE_ATTR(unique_id, 0444, wm831x_unique_id_show, NULL);
+
+int wm831x_otp_init(struct wm831x *wm831x)
+{
+	char uuid[WM831X_UNIQUE_ID_LEN];
+	int ret;
+
+	ret = device_create_file(wm831x->dev, &dev_attr_unique_id);
+	if (ret != 0)
+		dev_err(wm831x->dev, "Unique ID attribute not created: %d\n",
+			ret);
+
+	ret = wm831x_unique_id_read(wm831x, uuid);
+	if (ret == 0)
+		add_device_randomness(uuid, sizeof(uuid));
+	else
+		dev_err(wm831x->dev, "Failed to read UUID: %d\n", ret);
+
+	return ret;
+}
+
+void wm831x_otp_exit(struct wm831x *wm831x)
+{
+	device_remove_file(wm831x->dev, &dev_attr_unique_id);
+}
+
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/wm831x-spi.c b/ap/os/linux/linux-3.4.x/drivers/mfd/wm831x-spi.c
new file mode 100644
index 0000000..4bceee9
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/wm831x-spi.c
@@ -0,0 +1,126 @@
+/*
+ * wm831x-spi.c  --  SPI access for Wolfson WM831x PMICs
+ *
+ * Copyright 2009,2010 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/spi/spi.h>
+#include <linux/regmap.h>
+#include <linux/err.h>
+
+#include <linux/mfd/wm831x/core.h>
+
+static int __devinit wm831x_spi_probe(struct spi_device *spi)
+{
+	const struct spi_device_id *id = spi_get_device_id(spi);
+	struct wm831x *wm831x;
+	enum wm831x_parent type;
+	int ret;
+
+	type = (enum wm831x_parent)id->driver_data;
+
+	wm831x = devm_kzalloc(&spi->dev, sizeof(struct wm831x), GFP_KERNEL);
+	if (wm831x == NULL)
+		return -ENOMEM;
+
+	spi->bits_per_word = 16;
+	spi->mode = SPI_MODE_0;
+
+	dev_set_drvdata(&spi->dev, wm831x);
+	wm831x->dev = &spi->dev;
+
+	wm831x->regmap = devm_regmap_init_spi(spi, &wm831x_regmap_config);
+	if (IS_ERR(wm831x->regmap)) {
+		ret = PTR_ERR(wm831x->regmap);
+		dev_err(wm831x->dev, "Failed to allocate register map: %d\n",
+			ret);
+		return ret;
+	}
+
+	return wm831x_device_init(wm831x, type, spi->irq);
+}
+
+static int __devexit wm831x_spi_remove(struct spi_device *spi)
+{
+	struct wm831x *wm831x = dev_get_drvdata(&spi->dev);
+
+	wm831x_device_exit(wm831x);
+
+	return 0;
+}
+
+static int wm831x_spi_suspend(struct device *dev)
+{
+	struct wm831x *wm831x = dev_get_drvdata(dev);
+
+	return wm831x_device_suspend(wm831x);
+}
+
+static void wm831x_spi_shutdown(struct spi_device *spi)
+{
+	struct wm831x *wm831x = dev_get_drvdata(&spi->dev);
+
+	wm831x_device_shutdown(wm831x);
+}
+
+static const struct dev_pm_ops wm831x_spi_pm = {
+	.freeze = wm831x_spi_suspend,
+	.suspend = wm831x_spi_suspend,
+};
+
+static const struct spi_device_id wm831x_spi_ids[] = {
+	{ "wm8310", WM8310 },
+	{ "wm8311", WM8311 },
+	{ "wm8312", WM8312 },
+	{ "wm8320", WM8320 },
+	{ "wm8321", WM8321 },
+	{ "wm8325", WM8325 },
+	{ "wm8326", WM8326 },
+	{ },
+};
+MODULE_DEVICE_TABLE(spi, wm831x_spi_ids);
+
+static struct spi_driver wm831x_spi_driver = {
+	.driver = {
+		.name	= "wm831x",
+		.owner	= THIS_MODULE,
+		.pm	= &wm831x_spi_pm,
+	},
+	.id_table	= wm831x_spi_ids,
+	.probe		= wm831x_spi_probe,
+	.remove		= __devexit_p(wm831x_spi_remove),
+	.shutdown	= wm831x_spi_shutdown,
+};
+
+static int __init wm831x_spi_init(void)
+{
+	int ret;
+
+	ret = spi_register_driver(&wm831x_spi_driver);
+	if (ret != 0)
+		pr_err("Failed to register WM831x SPI driver: %d\n", ret);
+
+	return 0;
+}
+subsys_initcall(wm831x_spi_init);
+
+static void __exit wm831x_spi_exit(void)
+{
+	spi_unregister_driver(&wm831x_spi_driver);
+}
+module_exit(wm831x_spi_exit);
+
+MODULE_DESCRIPTION("SPI support for WM831x/2x AudioPlus PMICs");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mark Brown");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/wm8350-core.c b/ap/os/linux/linux-3.4.x/drivers/mfd/wm8350-core.c
new file mode 100644
index 0000000..dd1caaa
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/wm8350-core.c
@@ -0,0 +1,778 @@
+/*
+ * wm8350-core.c  --  Device access for Wolfson WM8350
+ *
+ * Copyright 2007, 2008 Wolfson Microelectronics PLC.
+ *
+ * Author: Liam Girdwood, Mark Brown
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/bug.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+
+#include <linux/mfd/wm8350/core.h>
+#include <linux/mfd/wm8350/audio.h>
+#include <linux/mfd/wm8350/comparator.h>
+#include <linux/mfd/wm8350/gpio.h>
+#include <linux/mfd/wm8350/pmic.h>
+#include <linux/mfd/wm8350/rtc.h>
+#include <linux/mfd/wm8350/supply.h>
+#include <linux/mfd/wm8350/wdt.h>
+
+#define WM8350_UNLOCK_KEY		0x0013
+#define WM8350_LOCK_KEY			0x0000
+
+#define WM8350_CLOCK_CONTROL_1		0x28
+#define WM8350_AIF_TEST			0x74
+
+/* debug */
+#define WM8350_BUS_DEBUG 0
+#if WM8350_BUS_DEBUG
+#define dump(regs, src) do { \
+	int i_; \
+	u16 *src_ = src; \
+	printk(KERN_DEBUG); \
+	for (i_ = 0; i_ < regs; i_++) \
+		printk(" 0x%4.4x", *src_++); \
+	printk("\n"); \
+} while (0);
+#else
+#define dump(bytes, src)
+#endif
+
+#define WM8350_LOCK_DEBUG 0
+#if WM8350_LOCK_DEBUG
+#define ldbg(format, arg...) printk(format, ## arg)
+#else
+#define ldbg(format, arg...)
+#endif
+
+/*
+ * WM8350 Device IO
+ */
+static DEFINE_MUTEX(io_mutex);
+static DEFINE_MUTEX(reg_lock_mutex);
+
+/* Perform a physical read from the device.
+ */
+static int wm8350_phys_read(struct wm8350 *wm8350, u8 reg, int num_regs,
+			    u16 *dest)
+{
+	int i, ret;
+	int bytes = num_regs * 2;
+
+	dev_dbg(wm8350->dev, "volatile read\n");
+	ret = wm8350->read_dev(wm8350, reg, bytes, (char *)dest);
+
+	for (i = reg; i < reg + num_regs; i++) {
+		/* Cache is CPU endian */
+		dest[i - reg] = be16_to_cpu(dest[i - reg]);
+
+		/* Mask out non-readable bits */
+		dest[i - reg] &= wm8350_reg_io_map[i].readable;
+	}
+
+	dump(num_regs, dest);
+
+	return ret;
+}
+
+static int wm8350_read(struct wm8350 *wm8350, u8 reg, int num_regs, u16 *dest)
+{
+	int i;
+	int end = reg + num_regs;
+	int ret = 0;
+	int bytes = num_regs * 2;
+
+	if (wm8350->read_dev == NULL)
+		return -ENODEV;
+
+	if ((reg + num_regs - 1) > WM8350_MAX_REGISTER) {
+		dev_err(wm8350->dev, "invalid reg %x\n",
+			reg + num_regs - 1);
+		return -EINVAL;
+	}
+
+	dev_dbg(wm8350->dev,
+		"%s R%d(0x%2.2x) %d regs\n", __func__, reg, reg, num_regs);
+
+#if WM8350_BUS_DEBUG
+	/* we can _safely_ read any register, but warn if read not supported */
+	for (i = reg; i < end; i++) {
+		if (!wm8350_reg_io_map[i].readable)
+			dev_warn(wm8350->dev,
+				"reg R%d is not readable\n", i);
+	}
+#endif
+
+	/* if any volatile registers are required, then read back all */
+	for (i = reg; i < end; i++)
+		if (wm8350_reg_io_map[i].vol)
+			return wm8350_phys_read(wm8350, reg, num_regs, dest);
+
+	/* no volatiles, then cache is good */
+	dev_dbg(wm8350->dev, "cache read\n");
+	memcpy(dest, &wm8350->reg_cache[reg], bytes);
+	dump(num_regs, dest);
+	return ret;
+}
+
+static inline int is_reg_locked(struct wm8350 *wm8350, u8 reg)
+{
+	if (reg == WM8350_SECURITY ||
+	    wm8350->reg_cache[WM8350_SECURITY] == WM8350_UNLOCK_KEY)
+		return 0;
+
+	if ((reg >= WM8350_GPIO_FUNCTION_SELECT_1 &&
+	     reg <= WM8350_GPIO_FUNCTION_SELECT_4) ||
+	    (reg >= WM8350_BATTERY_CHARGER_CONTROL_1 &&
+	     reg <= WM8350_BATTERY_CHARGER_CONTROL_3))
+		return 1;
+	return 0;
+}
+
+static int wm8350_write(struct wm8350 *wm8350, u8 reg, int num_regs, u16 *src)
+{
+	int i;
+	int end = reg + num_regs;
+	int bytes = num_regs * 2;
+
+	if (wm8350->write_dev == NULL)
+		return -ENODEV;
+
+	if ((reg + num_regs - 1) > WM8350_MAX_REGISTER) {
+		dev_err(wm8350->dev, "invalid reg %x\n",
+			reg + num_regs - 1);
+		return -EINVAL;
+	}
+
+	/* it's generally not a good idea to write to RO or locked registers */
+	for (i = reg; i < end; i++) {
+		if (!wm8350_reg_io_map[i].writable) {
+			dev_err(wm8350->dev,
+				"attempted write to read only reg R%d\n", i);
+			return -EINVAL;
+		}
+
+		if (is_reg_locked(wm8350, i)) {
+			dev_err(wm8350->dev,
+			       "attempted write to locked reg R%d\n", i);
+			return -EINVAL;
+		}
+
+		src[i - reg] &= wm8350_reg_io_map[i].writable;
+
+		wm8350->reg_cache[i] =
+			(wm8350->reg_cache[i] & ~wm8350_reg_io_map[i].writable)
+			| src[i - reg];
+
+		src[i - reg] = cpu_to_be16(src[i - reg]);
+	}
+
+	/* Actually write it out */
+	return wm8350->write_dev(wm8350, reg, bytes, (char *)src);
+}
+
+/*
+ * Safe read, modify, write methods
+ */
+int wm8350_clear_bits(struct wm8350 *wm8350, u16 reg, u16 mask)
+{
+	u16 data;
+	int err;
+
+	mutex_lock(&io_mutex);
+	err = wm8350_read(wm8350, reg, 1, &data);
+	if (err) {
+		dev_err(wm8350->dev, "read from reg R%d failed\n", reg);
+		goto out;
+	}
+
+	data &= ~mask;
+	err = wm8350_write(wm8350, reg, 1, &data);
+	if (err)
+		dev_err(wm8350->dev, "write to reg R%d failed\n", reg);
+out:
+	mutex_unlock(&io_mutex);
+	return err;
+}
+EXPORT_SYMBOL_GPL(wm8350_clear_bits);
+
+int wm8350_set_bits(struct wm8350 *wm8350, u16 reg, u16 mask)
+{
+	u16 data;
+	int err;
+
+	mutex_lock(&io_mutex);
+	err = wm8350_read(wm8350, reg, 1, &data);
+	if (err) {
+		dev_err(wm8350->dev, "read from reg R%d failed\n", reg);
+		goto out;
+	}
+
+	data |= mask;
+	err = wm8350_write(wm8350, reg, 1, &data);
+	if (err)
+		dev_err(wm8350->dev, "write to reg R%d failed\n", reg);
+out:
+	mutex_unlock(&io_mutex);
+	return err;
+}
+EXPORT_SYMBOL_GPL(wm8350_set_bits);
+
+u16 wm8350_reg_read(struct wm8350 *wm8350, int reg)
+{
+	u16 data;
+	int err;
+
+	mutex_lock(&io_mutex);
+	err = wm8350_read(wm8350, reg, 1, &data);
+	if (err)
+		dev_err(wm8350->dev, "read from reg R%d failed\n", reg);
+
+	mutex_unlock(&io_mutex);
+	return data;
+}
+EXPORT_SYMBOL_GPL(wm8350_reg_read);
+
+int wm8350_reg_write(struct wm8350 *wm8350, int reg, u16 val)
+{
+	int ret;
+	u16 data = val;
+
+	mutex_lock(&io_mutex);
+	ret = wm8350_write(wm8350, reg, 1, &data);
+	if (ret)
+		dev_err(wm8350->dev, "write to reg R%d failed\n", reg);
+	mutex_unlock(&io_mutex);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wm8350_reg_write);
+
+int wm8350_block_read(struct wm8350 *wm8350, int start_reg, int regs,
+		      u16 *dest)
+{
+	int err = 0;
+
+	mutex_lock(&io_mutex);
+	err = wm8350_read(wm8350, start_reg, regs, dest);
+	if (err)
+		dev_err(wm8350->dev, "block read starting from R%d failed\n",
+			start_reg);
+	mutex_unlock(&io_mutex);
+	return err;
+}
+EXPORT_SYMBOL_GPL(wm8350_block_read);
+
+int wm8350_block_write(struct wm8350 *wm8350, int start_reg, int regs,
+		       u16 *src)
+{
+	int ret = 0;
+
+	mutex_lock(&io_mutex);
+	ret = wm8350_write(wm8350, start_reg, regs, src);
+	if (ret)
+		dev_err(wm8350->dev, "block write starting at R%d failed\n",
+			start_reg);
+	mutex_unlock(&io_mutex);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wm8350_block_write);
+
+/**
+ * wm8350_reg_lock()
+ *
+ * The WM8350 has a hardware lock which can be used to prevent writes to
+ * some registers (generally those which can cause particularly serious
+ * problems if misused).  This function enables that lock.
+ */
+int wm8350_reg_lock(struct wm8350 *wm8350)
+{
+	u16 key = WM8350_LOCK_KEY;
+	int ret;
+
+	ldbg(__func__);
+	mutex_lock(&io_mutex);
+	ret = wm8350_write(wm8350, WM8350_SECURITY, 1, &key);
+	if (ret)
+		dev_err(wm8350->dev, "lock failed\n");
+	mutex_unlock(&io_mutex);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wm8350_reg_lock);
+
+/**
+ * wm8350_reg_unlock()
+ *
+ * The WM8350 has a hardware lock which can be used to prevent writes to
+ * some registers (generally those which can cause particularly serious
+ * problems if misused).  This function disables that lock so updates
+ * can be performed.  For maximum safety this should be done only when
+ * required.
+ */
+int wm8350_reg_unlock(struct wm8350 *wm8350)
+{
+	u16 key = WM8350_UNLOCK_KEY;
+	int ret;
+
+	ldbg(__func__);
+	mutex_lock(&io_mutex);
+	ret = wm8350_write(wm8350, WM8350_SECURITY, 1, &key);
+	if (ret)
+		dev_err(wm8350->dev, "unlock failed\n");
+	mutex_unlock(&io_mutex);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wm8350_reg_unlock);
+
+int wm8350_read_auxadc(struct wm8350 *wm8350, int channel, int scale, int vref)
+{
+	u16 reg, result = 0;
+
+	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(&wm8350->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);
+
+	/* If a late IRQ left the completion signalled then consume
+	 * the completion. */
+	try_wait_for_completion(&wm8350->auxadc_done);
+
+	/* We ignore the result of the completion and just check for a
+	 * conversion result, allowing us to soldier on if the IRQ
+	 * infrastructure is not set up for the chip. */
+	wait_for_completion_timeout(&wm8350->auxadc_done, msecs_to_jiffies(5));
+
+	reg = wm8350_reg_read(wm8350, WM8350_DIGITISER_CONTROL_1);
+	if (reg & WM8350_AUXADC_POLL)
+		dev_err(wm8350->dev, "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(&wm8350->auxadc_mutex);
+
+	return result & WM8350_AUXADC_DATA1_MASK;
+}
+EXPORT_SYMBOL_GPL(wm8350_read_auxadc);
+
+static irqreturn_t wm8350_auxadc_irq(int irq, void *irq_data)
+{
+	struct wm8350 *wm8350 = irq_data;
+
+	complete(&wm8350->auxadc_done);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * Cache is always host endian.
+ */
+static int wm8350_create_cache(struct wm8350 *wm8350, int type, int mode)
+{
+	int i, ret = 0;
+	u16 value;
+	const u16 *reg_map;
+
+	switch (type) {
+	case 0:
+		switch (mode) {
+#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_0
+		case 0:
+			reg_map = wm8350_mode0_defaults;
+			break;
+#endif
+#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_1
+		case 1:
+			reg_map = wm8350_mode1_defaults;
+			break;
+#endif
+#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_2
+		case 2:
+			reg_map = wm8350_mode2_defaults;
+			break;
+#endif
+#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_3
+		case 3:
+			reg_map = wm8350_mode3_defaults;
+			break;
+#endif
+		default:
+			dev_err(wm8350->dev,
+				"WM8350 configuration mode %d not supported\n",
+				mode);
+			return -EINVAL;
+		}
+		break;
+
+	case 1:
+		switch (mode) {
+#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_0
+		case 0:
+			reg_map = wm8351_mode0_defaults;
+			break;
+#endif
+#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_1
+		case 1:
+			reg_map = wm8351_mode1_defaults;
+			break;
+#endif
+#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_2
+		case 2:
+			reg_map = wm8351_mode2_defaults;
+			break;
+#endif
+#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_3
+		case 3:
+			reg_map = wm8351_mode3_defaults;
+			break;
+#endif
+		default:
+			dev_err(wm8350->dev,
+				"WM8351 configuration mode %d not supported\n",
+				mode);
+			return -EINVAL;
+		}
+		break;
+
+	case 2:
+		switch (mode) {
+#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_0
+		case 0:
+			reg_map = wm8352_mode0_defaults;
+			break;
+#endif
+#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_1
+		case 1:
+			reg_map = wm8352_mode1_defaults;
+			break;
+#endif
+#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_2
+		case 2:
+			reg_map = wm8352_mode2_defaults;
+			break;
+#endif
+#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_3
+		case 3:
+			reg_map = wm8352_mode3_defaults;
+			break;
+#endif
+		default:
+			dev_err(wm8350->dev,
+				"WM8352 configuration mode %d not supported\n",
+				mode);
+			return -EINVAL;
+		}
+		break;
+
+	default:
+		dev_err(wm8350->dev,
+			"WM835x configuration mode %d not supported\n",
+			mode);
+		return -EINVAL;
+	}
+
+	wm8350->reg_cache =
+		kmalloc(sizeof(u16) * (WM8350_MAX_REGISTER + 1), GFP_KERNEL);
+	if (wm8350->reg_cache == NULL)
+		return -ENOMEM;
+
+	/* Read the initial cache state back from the device - this is
+	 * a PMIC so the device many not be in a virgin state and we
+	 * can't rely on the silicon values.
+	 */
+	ret = wm8350->read_dev(wm8350, 0,
+			       sizeof(u16) * (WM8350_MAX_REGISTER + 1),
+			       wm8350->reg_cache);
+	if (ret < 0) {
+		dev_err(wm8350->dev,
+			"failed to read initial cache values\n");
+		goto out;
+	}
+
+	/* Mask out uncacheable/unreadable bits and the audio. */
+	for (i = 0; i < WM8350_MAX_REGISTER; i++) {
+		if (wm8350_reg_io_map[i].readable &&
+		    (i < WM8350_CLOCK_CONTROL_1 || i > WM8350_AIF_TEST)) {
+			value = be16_to_cpu(wm8350->reg_cache[i]);
+			value &= wm8350_reg_io_map[i].readable;
+			wm8350->reg_cache[i] = value;
+		} else
+			wm8350->reg_cache[i] = reg_map[i];
+	}
+
+out:
+	kfree(wm8350->reg_cache);
+	return ret;
+}
+
+/*
+ * Register a client device.  This is non-fatal since there is no need to
+ * fail the entire device init due to a single platform device failing.
+ */
+static void wm8350_client_dev_register(struct wm8350 *wm8350,
+				       const char *name,
+				       struct platform_device **pdev)
+{
+	int ret;
+
+	*pdev = platform_device_alloc(name, -1);
+	if (*pdev == NULL) {
+		dev_err(wm8350->dev, "Failed to allocate %s\n", name);
+		return;
+	}
+
+	(*pdev)->dev.parent = wm8350->dev;
+	platform_set_drvdata(*pdev, wm8350);
+	ret = platform_device_add(*pdev);
+	if (ret != 0) {
+		dev_err(wm8350->dev, "Failed to register %s: %d\n", name, ret);
+		platform_device_put(*pdev);
+		*pdev = NULL;
+	}
+}
+
+int wm8350_device_init(struct wm8350 *wm8350, int irq,
+		       struct wm8350_platform_data *pdata)
+{
+	int ret;
+	u16 id1, id2, mask_rev;
+	u16 cust_id, mode, chip_rev;
+
+	dev_set_drvdata(wm8350->dev, wm8350);
+
+	/* get WM8350 revision and config mode */
+	ret = wm8350->read_dev(wm8350, WM8350_RESET_ID, sizeof(id1), &id1);
+	if (ret != 0) {
+		dev_err(wm8350->dev, "Failed to read ID: %d\n", ret);
+		goto err;
+	}
+
+	ret = wm8350->read_dev(wm8350, WM8350_ID, sizeof(id2), &id2);
+	if (ret != 0) {
+		dev_err(wm8350->dev, "Failed to read ID: %d\n", ret);
+		goto err;
+	}
+
+	ret = wm8350->read_dev(wm8350, WM8350_REVISION, sizeof(mask_rev),
+			       &mask_rev);
+	if (ret != 0) {
+		dev_err(wm8350->dev, "Failed to read revision: %d\n", ret);
+		goto err;
+	}
+
+	id1 = be16_to_cpu(id1);
+	id2 = be16_to_cpu(id2);
+	mask_rev = be16_to_cpu(mask_rev);
+
+	if (id1 != 0x6143) {
+		dev_err(wm8350->dev,
+			"Device with ID %x is not a WM8350\n", id1);
+		ret = -ENODEV;
+		goto err;
+	}
+
+	mode = id2 & WM8350_CONF_STS_MASK >> 10;
+	cust_id = id2 & WM8350_CUST_ID_MASK;
+	chip_rev = (id2 & WM8350_CHIP_REV_MASK) >> 12;
+	dev_info(wm8350->dev,
+		 "CONF_STS %d, CUST_ID %d, MASK_REV %d, CHIP_REV %d\n",
+		 mode, cust_id, mask_rev, chip_rev);
+
+	if (cust_id != 0) {
+		dev_err(wm8350->dev, "Unsupported CUST_ID\n");
+		ret = -ENODEV;
+		goto err;
+	}
+
+	switch (mask_rev) {
+	case 0:
+		wm8350->pmic.max_dcdc = WM8350_DCDC_6;
+		wm8350->pmic.max_isink = WM8350_ISINK_B;
+
+		switch (chip_rev) {
+		case WM8350_REV_E:
+			dev_info(wm8350->dev, "WM8350 Rev E\n");
+			break;
+		case WM8350_REV_F:
+			dev_info(wm8350->dev, "WM8350 Rev F\n");
+			break;
+		case WM8350_REV_G:
+			dev_info(wm8350->dev, "WM8350 Rev G\n");
+			wm8350->power.rev_g_coeff = 1;
+			break;
+		case WM8350_REV_H:
+			dev_info(wm8350->dev, "WM8350 Rev H\n");
+			wm8350->power.rev_g_coeff = 1;
+			break;
+		default:
+			/* For safety we refuse to run on unknown hardware */
+			dev_err(wm8350->dev, "Unknown WM8350 CHIP_REV\n");
+			ret = -ENODEV;
+			goto err;
+		}
+		break;
+
+	case 1:
+		wm8350->pmic.max_dcdc = WM8350_DCDC_4;
+		wm8350->pmic.max_isink = WM8350_ISINK_A;
+
+		switch (chip_rev) {
+		case 0:
+			dev_info(wm8350->dev, "WM8351 Rev A\n");
+			wm8350->power.rev_g_coeff = 1;
+			break;
+
+		case 1:
+			dev_info(wm8350->dev, "WM8351 Rev B\n");
+			wm8350->power.rev_g_coeff = 1;
+			break;
+
+		default:
+			dev_err(wm8350->dev, "Unknown WM8351 CHIP_REV\n");
+			ret = -ENODEV;
+			goto err;
+		}
+		break;
+
+	case 2:
+		wm8350->pmic.max_dcdc = WM8350_DCDC_6;
+		wm8350->pmic.max_isink = WM8350_ISINK_B;
+
+		switch (chip_rev) {
+		case 0:
+			dev_info(wm8350->dev, "WM8352 Rev A\n");
+			wm8350->power.rev_g_coeff = 1;
+			break;
+
+		default:
+			dev_err(wm8350->dev, "Unknown WM8352 CHIP_REV\n");
+			ret = -ENODEV;
+			goto err;
+		}
+		break;
+
+	default:
+		dev_err(wm8350->dev, "Unknown MASK_REV\n");
+		ret = -ENODEV;
+		goto err;
+	}
+
+	ret = wm8350_create_cache(wm8350, mask_rev, mode);
+	if (ret < 0) {
+		dev_err(wm8350->dev, "Failed to create register cache\n");
+		return ret;
+	}
+
+	mutex_init(&wm8350->auxadc_mutex);
+	init_completion(&wm8350->auxadc_done);
+
+	ret = wm8350_irq_init(wm8350, irq, pdata);
+	if (ret < 0)
+		goto err_free;
+
+	if (wm8350->irq_base) {
+		ret = request_threaded_irq(wm8350->irq_base +
+					   WM8350_IRQ_AUXADC_DATARDY,
+					   NULL, wm8350_auxadc_irq, 0,
+					   "auxadc", wm8350);
+		if (ret < 0)
+			dev_warn(wm8350->dev,
+				 "Failed to request AUXADC IRQ: %d\n", ret);
+	}
+
+	if (pdata && pdata->init) {
+		ret = pdata->init(wm8350);
+		if (ret != 0) {
+			dev_err(wm8350->dev, "Platform init() failed: %d\n",
+				ret);
+			goto err_irq;
+		}
+	}
+
+	wm8350_reg_write(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK, 0x0);
+
+	wm8350_client_dev_register(wm8350, "wm8350-codec",
+				   &(wm8350->codec.pdev));
+	wm8350_client_dev_register(wm8350, "wm8350-gpio",
+				   &(wm8350->gpio.pdev));
+	wm8350_client_dev_register(wm8350, "wm8350-hwmon",
+				   &(wm8350->hwmon.pdev));
+	wm8350_client_dev_register(wm8350, "wm8350-power",
+				   &(wm8350->power.pdev));
+	wm8350_client_dev_register(wm8350, "wm8350-rtc", &(wm8350->rtc.pdev));
+	wm8350_client_dev_register(wm8350, "wm8350-wdt", &(wm8350->wdt.pdev));
+
+	return 0;
+
+err_irq:
+	wm8350_irq_exit(wm8350);
+err_free:
+	kfree(wm8350->reg_cache);
+err:
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wm8350_device_init);
+
+void wm8350_device_exit(struct wm8350 *wm8350)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(wm8350->pmic.led); i++)
+		platform_device_unregister(wm8350->pmic.led[i].pdev);
+
+	for (i = 0; i < ARRAY_SIZE(wm8350->pmic.pdev); i++)
+		platform_device_unregister(wm8350->pmic.pdev[i]);
+
+	platform_device_unregister(wm8350->wdt.pdev);
+	platform_device_unregister(wm8350->rtc.pdev);
+	platform_device_unregister(wm8350->power.pdev);
+	platform_device_unregister(wm8350->hwmon.pdev);
+	platform_device_unregister(wm8350->gpio.pdev);
+	platform_device_unregister(wm8350->codec.pdev);
+
+	if (wm8350->irq_base)
+		free_irq(wm8350->irq_base + WM8350_IRQ_AUXADC_DATARDY, wm8350);
+
+	wm8350_irq_exit(wm8350);
+
+	kfree(wm8350->reg_cache);
+}
+EXPORT_SYMBOL_GPL(wm8350_device_exit);
+
+MODULE_DESCRIPTION("WM8350 AudioPlus PMIC core driver");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/wm8350-gpio.c b/ap/os/linux/linux-3.4.x/drivers/mfd/wm8350-gpio.c
new file mode 100644
index 0000000..d584f6b
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/wm8350-gpio.c
@@ -0,0 +1,222 @@
+/*
+ * wm8350-core.c  --  Device access for Wolfson WM8350
+ *
+ * Copyright 2007, 2008 Wolfson Microelectronics PLC.
+ *
+ * Author: Liam Girdwood
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+
+#include <linux/mfd/wm8350/core.h>
+#include <linux/mfd/wm8350/gpio.h>
+#include <linux/mfd/wm8350/pmic.h>
+
+static int gpio_set_dir(struct wm8350 *wm8350, int gpio, int dir)
+{
+	int ret;
+
+	wm8350_reg_unlock(wm8350);
+	if (dir == WM8350_GPIO_DIR_OUT)
+		ret = wm8350_clear_bits(wm8350,
+					WM8350_GPIO_CONFIGURATION_I_O,
+					1 << gpio);
+	else
+		ret = wm8350_set_bits(wm8350,
+				      WM8350_GPIO_CONFIGURATION_I_O,
+				      1 << gpio);
+	wm8350_reg_lock(wm8350);
+	return ret;
+}
+
+static int wm8350_gpio_set_debounce(struct wm8350 *wm8350, int gpio, int db)
+{
+	if (db == WM8350_GPIO_DEBOUNCE_ON)
+		return wm8350_set_bits(wm8350, WM8350_GPIO_DEBOUNCE,
+				       1 << gpio);
+	else
+		return wm8350_clear_bits(wm8350,
+					 WM8350_GPIO_DEBOUNCE, 1 << gpio);
+}
+
+static int gpio_set_func(struct wm8350 *wm8350, int gpio, int func)
+{
+	u16 reg;
+
+	wm8350_reg_unlock(wm8350);
+	switch (gpio) {
+	case 0:
+		reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_1)
+		    & ~WM8350_GP0_FN_MASK;
+		wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_1,
+				 reg | ((func & 0xf) << 0));
+		break;
+	case 1:
+		reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_1)
+		    & ~WM8350_GP1_FN_MASK;
+		wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_1,
+				 reg | ((func & 0xf) << 4));
+		break;
+	case 2:
+		reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_1)
+		    & ~WM8350_GP2_FN_MASK;
+		wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_1,
+				 reg | ((func & 0xf) << 8));
+		break;
+	case 3:
+		reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_1)
+		    & ~WM8350_GP3_FN_MASK;
+		wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_1,
+				 reg | ((func & 0xf) << 12));
+		break;
+	case 4:
+		reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_2)
+		    & ~WM8350_GP4_FN_MASK;
+		wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_2,
+				 reg | ((func & 0xf) << 0));
+		break;
+	case 5:
+		reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_2)
+		    & ~WM8350_GP5_FN_MASK;
+		wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_2,
+				 reg | ((func & 0xf) << 4));
+		break;
+	case 6:
+		reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_2)
+		    & ~WM8350_GP6_FN_MASK;
+		wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_2,
+				 reg | ((func & 0xf) << 8));
+		break;
+	case 7:
+		reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_2)
+		    & ~WM8350_GP7_FN_MASK;
+		wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_2,
+				 reg | ((func & 0xf) << 12));
+		break;
+	case 8:
+		reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_3)
+		    & ~WM8350_GP8_FN_MASK;
+		wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_3,
+				 reg | ((func & 0xf) << 0));
+		break;
+	case 9:
+		reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_3)
+		    & ~WM8350_GP9_FN_MASK;
+		wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_3,
+				 reg | ((func & 0xf) << 4));
+		break;
+	case 10:
+		reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_3)
+		    & ~WM8350_GP10_FN_MASK;
+		wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_3,
+				 reg | ((func & 0xf) << 8));
+		break;
+	case 11:
+		reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_3)
+		    & ~WM8350_GP11_FN_MASK;
+		wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_3,
+				 reg | ((func & 0xf) << 12));
+		break;
+	case 12:
+		reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_4)
+		    & ~WM8350_GP12_FN_MASK;
+		wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_4,
+				 reg | ((func & 0xf) << 0));
+		break;
+	default:
+		wm8350_reg_lock(wm8350);
+		return -EINVAL;
+	}
+
+	wm8350_reg_lock(wm8350);
+	return 0;
+}
+
+static int gpio_set_pull_up(struct wm8350 *wm8350, int gpio, int up)
+{
+	if (up)
+		return wm8350_set_bits(wm8350,
+				       WM8350_GPIO_PIN_PULL_UP_CONTROL,
+				       1 << gpio);
+	else
+		return wm8350_clear_bits(wm8350,
+					 WM8350_GPIO_PIN_PULL_UP_CONTROL,
+					 1 << gpio);
+}
+
+static int gpio_set_pull_down(struct wm8350 *wm8350, int gpio, int down)
+{
+	if (down)
+		return wm8350_set_bits(wm8350,
+				       WM8350_GPIO_PULL_DOWN_CONTROL,
+				       1 << gpio);
+	else
+		return wm8350_clear_bits(wm8350,
+					 WM8350_GPIO_PULL_DOWN_CONTROL,
+					 1 << gpio);
+}
+
+static int gpio_set_polarity(struct wm8350 *wm8350, int gpio, int pol)
+{
+	if (pol == WM8350_GPIO_ACTIVE_HIGH)
+		return wm8350_set_bits(wm8350,
+				       WM8350_GPIO_PIN_POLARITY_TYPE,
+				       1 << gpio);
+	else
+		return wm8350_clear_bits(wm8350,
+					 WM8350_GPIO_PIN_POLARITY_TYPE,
+					 1 << gpio);
+}
+
+static int gpio_set_invert(struct wm8350 *wm8350, int gpio, int invert)
+{
+	if (invert == WM8350_GPIO_INVERT_ON)
+		return wm8350_set_bits(wm8350, WM8350_GPIO_INT_MODE, 1 << gpio);
+	else
+		return wm8350_clear_bits(wm8350,
+					 WM8350_GPIO_INT_MODE, 1 << gpio);
+}
+
+int wm8350_gpio_config(struct wm8350 *wm8350, int gpio, int dir, int func,
+		       int pol, int pull, int invert, int debounce)
+{
+	/* make sure we never pull up and down at the same time */
+	if (pull == WM8350_GPIO_PULL_NONE) {
+		if (gpio_set_pull_up(wm8350, gpio, 0))
+			goto err;
+		if (gpio_set_pull_down(wm8350, gpio, 0))
+			goto err;
+	} else if (pull == WM8350_GPIO_PULL_UP) {
+		if (gpio_set_pull_down(wm8350, gpio, 0))
+			goto err;
+		if (gpio_set_pull_up(wm8350, gpio, 1))
+			goto err;
+	} else if (pull == WM8350_GPIO_PULL_DOWN) {
+		if (gpio_set_pull_up(wm8350, gpio, 0))
+			goto err;
+		if (gpio_set_pull_down(wm8350, gpio, 1))
+			goto err;
+	}
+
+	if (gpio_set_invert(wm8350, gpio, invert))
+		goto err;
+	if (gpio_set_polarity(wm8350, gpio, pol))
+		goto err;
+	if (wm8350_gpio_set_debounce(wm8350, gpio, debounce))
+		goto err;
+	if (gpio_set_dir(wm8350, gpio, dir))
+		goto err;
+	return gpio_set_func(wm8350, gpio, func);
+
+err:
+	return -EIO;
+}
+EXPORT_SYMBOL_GPL(wm8350_gpio_config);
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/wm8350-i2c.c b/ap/os/linux/linux-3.4.x/drivers/mfd/wm8350-i2c.c
new file mode 100644
index 0000000..d955faa
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/wm8350-i2c.c
@@ -0,0 +1,128 @@
+/*
+ * wm8350-i2c.c  --  Generic I2C driver for Wolfson WM8350 PMIC
+ *
+ * Copyright 2007, 2008 Wolfson Microelectronics PLC.
+ *
+ * Author: Liam Girdwood
+ *         linux@wolfsonmicro.com
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/wm8350/core.h>
+#include <linux/slab.h>
+
+static int wm8350_i2c_read_device(struct wm8350 *wm8350, char reg,
+				  int bytes, void *dest)
+{
+	int ret;
+
+	ret = i2c_master_send(wm8350->i2c_client, &reg, 1);
+	if (ret < 0)
+		return ret;
+	ret = i2c_master_recv(wm8350->i2c_client, dest, bytes);
+	if (ret < 0)
+		return ret;
+	if (ret != bytes)
+		return -EIO;
+	return 0;
+}
+
+static int wm8350_i2c_write_device(struct wm8350 *wm8350, char reg,
+				   int bytes, void *src)
+{
+	/* we add 1 byte for device register */
+	u8 msg[(WM8350_MAX_REGISTER << 1) + 1];
+	int ret;
+
+	if (bytes > ((WM8350_MAX_REGISTER << 1) + 1))
+		return -EINVAL;
+
+	msg[0] = reg;
+	memcpy(&msg[1], src, bytes);
+	ret = i2c_master_send(wm8350->i2c_client, msg, bytes + 1);
+	if (ret < 0)
+		return ret;
+	if (ret != bytes + 1)
+		return -EIO;
+	return 0;
+}
+
+static int wm8350_i2c_probe(struct i2c_client *i2c,
+			    const struct i2c_device_id *id)
+{
+	struct wm8350 *wm8350;
+	int ret = 0;
+
+	wm8350 = devm_kzalloc(&i2c->dev, sizeof(struct wm8350), GFP_KERNEL);
+	if (wm8350 == NULL)
+		return -ENOMEM;
+
+	i2c_set_clientdata(i2c, wm8350);
+	wm8350->dev = &i2c->dev;
+	wm8350->i2c_client = i2c;
+	wm8350->read_dev = wm8350_i2c_read_device;
+	wm8350->write_dev = wm8350_i2c_write_device;
+
+	ret = wm8350_device_init(wm8350, i2c->irq, i2c->dev.platform_data);
+	if (ret < 0)
+		goto err;
+
+	return ret;
+
+err:
+	return ret;
+}
+
+static int wm8350_i2c_remove(struct i2c_client *i2c)
+{
+	struct wm8350 *wm8350 = i2c_get_clientdata(i2c);
+
+	wm8350_device_exit(wm8350);
+
+	return 0;
+}
+
+static const struct i2c_device_id wm8350_i2c_id[] = {
+       { "wm8350", 0 },
+       { "wm8351", 0 },
+       { "wm8352", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8350_i2c_id);
+
+
+static struct i2c_driver wm8350_i2c_driver = {
+	.driver = {
+		   .name = "wm8350",
+		   .owner = THIS_MODULE,
+	},
+	.probe = wm8350_i2c_probe,
+	.remove = wm8350_i2c_remove,
+	.id_table = wm8350_i2c_id,
+};
+
+static int __init wm8350_i2c_init(void)
+{
+	return i2c_add_driver(&wm8350_i2c_driver);
+}
+/* init early so consumer devices can complete system boot */
+subsys_initcall(wm8350_i2c_init);
+
+static void __exit wm8350_i2c_exit(void)
+{
+	i2c_del_driver(&wm8350_i2c_driver);
+}
+module_exit(wm8350_i2c_exit);
+
+MODULE_DESCRIPTION("I2C support for the WM8350 AudioPlus PMIC");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/wm8350-irq.c b/ap/os/linux/linux-3.4.x/drivers/mfd/wm8350-irq.c
new file mode 100644
index 0000000..9fd01bf
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/wm8350-irq.c
@@ -0,0 +1,555 @@
+/*
+ * wm8350-irq.c  --  IRQ support for Wolfson WM8350
+ *
+ * Copyright 2007, 2008, 2009 Wolfson Microelectronics PLC.
+ *
+ * Author: Liam Girdwood, Mark Brown
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/bug.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+
+#include <linux/mfd/wm8350/core.h>
+#include <linux/mfd/wm8350/audio.h>
+#include <linux/mfd/wm8350/comparator.h>
+#include <linux/mfd/wm8350/gpio.h>
+#include <linux/mfd/wm8350/pmic.h>
+#include <linux/mfd/wm8350/rtc.h>
+#include <linux/mfd/wm8350/supply.h>
+#include <linux/mfd/wm8350/wdt.h>
+
+#define WM8350_INT_OFFSET_1                     0
+#define WM8350_INT_OFFSET_2                     1
+#define WM8350_POWER_UP_INT_OFFSET              2
+#define WM8350_UNDER_VOLTAGE_INT_OFFSET         3
+#define WM8350_OVER_CURRENT_INT_OFFSET          4
+#define WM8350_GPIO_INT_OFFSET                  5
+#define WM8350_COMPARATOR_INT_OFFSET            6
+
+struct wm8350_irq_data {
+	int primary;
+	int reg;
+	int mask;
+	int primary_only;
+};
+
+static struct wm8350_irq_data wm8350_irqs[] = {
+	[WM8350_IRQ_OC_LS] = {
+		.primary = WM8350_OC_INT,
+		.reg = WM8350_OVER_CURRENT_INT_OFFSET,
+		.mask = WM8350_OC_LS_EINT,
+		.primary_only = 1,
+	},
+	[WM8350_IRQ_UV_DC1] = {
+		.primary = WM8350_UV_INT,
+		.reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
+		.mask = WM8350_UV_DC1_EINT,
+	},
+	[WM8350_IRQ_UV_DC2] = {
+		.primary = WM8350_UV_INT,
+		.reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
+		.mask = WM8350_UV_DC2_EINT,
+	},
+	[WM8350_IRQ_UV_DC3] = {
+		.primary = WM8350_UV_INT,
+		.reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
+		.mask = WM8350_UV_DC3_EINT,
+	},
+	[WM8350_IRQ_UV_DC4] = {
+		.primary = WM8350_UV_INT,
+		.reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
+		.mask = WM8350_UV_DC4_EINT,
+	},
+	[WM8350_IRQ_UV_DC5] = {
+		.primary = WM8350_UV_INT,
+		.reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
+		.mask = WM8350_UV_DC5_EINT,
+	},
+	[WM8350_IRQ_UV_DC6] = {
+		.primary = WM8350_UV_INT,
+		.reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
+		.mask = WM8350_UV_DC6_EINT,
+	},
+	[WM8350_IRQ_UV_LDO1] = {
+		.primary = WM8350_UV_INT,
+		.reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
+		.mask = WM8350_UV_LDO1_EINT,
+	},
+	[WM8350_IRQ_UV_LDO2] = {
+		.primary = WM8350_UV_INT,
+		.reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
+		.mask = WM8350_UV_LDO2_EINT,
+	},
+	[WM8350_IRQ_UV_LDO3] = {
+		.primary = WM8350_UV_INT,
+		.reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
+		.mask = WM8350_UV_LDO3_EINT,
+	},
+	[WM8350_IRQ_UV_LDO4] = {
+		.primary = WM8350_UV_INT,
+		.reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
+		.mask = WM8350_UV_LDO4_EINT,
+	},
+	[WM8350_IRQ_CHG_BAT_HOT] = {
+		.primary = WM8350_CHG_INT,
+		.reg = WM8350_INT_OFFSET_1,
+		.mask = WM8350_CHG_BAT_HOT_EINT,
+	},
+	[WM8350_IRQ_CHG_BAT_COLD] = {
+		.primary = WM8350_CHG_INT,
+		.reg = WM8350_INT_OFFSET_1,
+		.mask = WM8350_CHG_BAT_COLD_EINT,
+	},
+	[WM8350_IRQ_CHG_BAT_FAIL] = {
+		.primary = WM8350_CHG_INT,
+		.reg = WM8350_INT_OFFSET_1,
+		.mask = WM8350_CHG_BAT_FAIL_EINT,
+	},
+	[WM8350_IRQ_CHG_TO] = {
+		.primary = WM8350_CHG_INT,
+		.reg = WM8350_INT_OFFSET_1,
+		.mask = WM8350_CHG_TO_EINT,
+	},
+	[WM8350_IRQ_CHG_END] = {
+		.primary = WM8350_CHG_INT,
+		.reg = WM8350_INT_OFFSET_1,
+		.mask = WM8350_CHG_END_EINT,
+	},
+	[WM8350_IRQ_CHG_START] = {
+		.primary = WM8350_CHG_INT,
+		.reg = WM8350_INT_OFFSET_1,
+		.mask = WM8350_CHG_START_EINT,
+	},
+	[WM8350_IRQ_CHG_FAST_RDY] = {
+		.primary = WM8350_CHG_INT,
+		.reg = WM8350_INT_OFFSET_1,
+		.mask = WM8350_CHG_FAST_RDY_EINT,
+	},
+	[WM8350_IRQ_CHG_VBATT_LT_3P9] = {
+		.primary = WM8350_CHG_INT,
+		.reg = WM8350_INT_OFFSET_1,
+		.mask = WM8350_CHG_VBATT_LT_3P9_EINT,
+	},
+	[WM8350_IRQ_CHG_VBATT_LT_3P1] = {
+		.primary = WM8350_CHG_INT,
+		.reg = WM8350_INT_OFFSET_1,
+		.mask = WM8350_CHG_VBATT_LT_3P1_EINT,
+	},
+	[WM8350_IRQ_CHG_VBATT_LT_2P85] = {
+		.primary = WM8350_CHG_INT,
+		.reg = WM8350_INT_OFFSET_1,
+		.mask = WM8350_CHG_VBATT_LT_2P85_EINT,
+	},
+	[WM8350_IRQ_RTC_ALM] = {
+		.primary = WM8350_RTC_INT,
+		.reg = WM8350_INT_OFFSET_1,
+		.mask = WM8350_RTC_ALM_EINT,
+	},
+	[WM8350_IRQ_RTC_SEC] = {
+		.primary = WM8350_RTC_INT,
+		.reg = WM8350_INT_OFFSET_1,
+		.mask = WM8350_RTC_SEC_EINT,
+	},
+	[WM8350_IRQ_RTC_PER] = {
+		.primary = WM8350_RTC_INT,
+		.reg = WM8350_INT_OFFSET_1,
+		.mask = WM8350_RTC_PER_EINT,
+	},
+	[WM8350_IRQ_CS1] = {
+		.primary = WM8350_CS_INT,
+		.reg = WM8350_INT_OFFSET_2,
+		.mask = WM8350_CS1_EINT,
+	},
+	[WM8350_IRQ_CS2] = {
+		.primary = WM8350_CS_INT,
+		.reg = WM8350_INT_OFFSET_2,
+		.mask = WM8350_CS2_EINT,
+	},
+	[WM8350_IRQ_SYS_HYST_COMP_FAIL] = {
+		.primary = WM8350_SYS_INT,
+		.reg = WM8350_INT_OFFSET_2,
+		.mask = WM8350_SYS_HYST_COMP_FAIL_EINT,
+	},
+	[WM8350_IRQ_SYS_CHIP_GT115] = {
+		.primary = WM8350_SYS_INT,
+		.reg = WM8350_INT_OFFSET_2,
+		.mask = WM8350_SYS_CHIP_GT115_EINT,
+	},
+	[WM8350_IRQ_SYS_CHIP_GT140] = {
+		.primary = WM8350_SYS_INT,
+		.reg = WM8350_INT_OFFSET_2,
+		.mask = WM8350_SYS_CHIP_GT140_EINT,
+	},
+	[WM8350_IRQ_SYS_WDOG_TO] = {
+		.primary = WM8350_SYS_INT,
+		.reg = WM8350_INT_OFFSET_2,
+		.mask = WM8350_SYS_WDOG_TO_EINT,
+	},
+	[WM8350_IRQ_AUXADC_DATARDY] = {
+		.primary = WM8350_AUXADC_INT,
+		.reg = WM8350_INT_OFFSET_2,
+		.mask = WM8350_AUXADC_DATARDY_EINT,
+	},
+	[WM8350_IRQ_AUXADC_DCOMP4] = {
+		.primary = WM8350_AUXADC_INT,
+		.reg = WM8350_INT_OFFSET_2,
+		.mask = WM8350_AUXADC_DCOMP4_EINT,
+	},
+	[WM8350_IRQ_AUXADC_DCOMP3] = {
+		.primary = WM8350_AUXADC_INT,
+		.reg = WM8350_INT_OFFSET_2,
+		.mask = WM8350_AUXADC_DCOMP3_EINT,
+	},
+	[WM8350_IRQ_AUXADC_DCOMP2] = {
+		.primary = WM8350_AUXADC_INT,
+		.reg = WM8350_INT_OFFSET_2,
+		.mask = WM8350_AUXADC_DCOMP2_EINT,
+	},
+	[WM8350_IRQ_AUXADC_DCOMP1] = {
+		.primary = WM8350_AUXADC_INT,
+		.reg = WM8350_INT_OFFSET_2,
+		.mask = WM8350_AUXADC_DCOMP1_EINT,
+	},
+	[WM8350_IRQ_USB_LIMIT] = {
+		.primary = WM8350_USB_INT,
+		.reg = WM8350_INT_OFFSET_2,
+		.mask = WM8350_USB_LIMIT_EINT,
+		.primary_only = 1,
+	},
+	[WM8350_IRQ_WKUP_OFF_STATE] = {
+		.primary = WM8350_WKUP_INT,
+		.reg = WM8350_COMPARATOR_INT_OFFSET,
+		.mask = WM8350_WKUP_OFF_STATE_EINT,
+	},
+	[WM8350_IRQ_WKUP_HIB_STATE] = {
+		.primary = WM8350_WKUP_INT,
+		.reg = WM8350_COMPARATOR_INT_OFFSET,
+		.mask = WM8350_WKUP_HIB_STATE_EINT,
+	},
+	[WM8350_IRQ_WKUP_CONV_FAULT] = {
+		.primary = WM8350_WKUP_INT,
+		.reg = WM8350_COMPARATOR_INT_OFFSET,
+		.mask = WM8350_WKUP_CONV_FAULT_EINT,
+	},
+	[WM8350_IRQ_WKUP_WDOG_RST] = {
+		.primary = WM8350_WKUP_INT,
+		.reg = WM8350_COMPARATOR_INT_OFFSET,
+		.mask = WM8350_WKUP_WDOG_RST_EINT,
+	},
+	[WM8350_IRQ_WKUP_GP_PWR_ON] = {
+		.primary = WM8350_WKUP_INT,
+		.reg = WM8350_COMPARATOR_INT_OFFSET,
+		.mask = WM8350_WKUP_GP_PWR_ON_EINT,
+	},
+	[WM8350_IRQ_WKUP_ONKEY] = {
+		.primary = WM8350_WKUP_INT,
+		.reg = WM8350_COMPARATOR_INT_OFFSET,
+		.mask = WM8350_WKUP_ONKEY_EINT,
+	},
+	[WM8350_IRQ_WKUP_GP_WAKEUP] = {
+		.primary = WM8350_WKUP_INT,
+		.reg = WM8350_COMPARATOR_INT_OFFSET,
+		.mask = WM8350_WKUP_GP_WAKEUP_EINT,
+	},
+	[WM8350_IRQ_CODEC_JCK_DET_L] = {
+		.primary = WM8350_CODEC_INT,
+		.reg = WM8350_COMPARATOR_INT_OFFSET,
+		.mask = WM8350_CODEC_JCK_DET_L_EINT,
+	},
+	[WM8350_IRQ_CODEC_JCK_DET_R] = {
+		.primary = WM8350_CODEC_INT,
+		.reg = WM8350_COMPARATOR_INT_OFFSET,
+		.mask = WM8350_CODEC_JCK_DET_R_EINT,
+	},
+	[WM8350_IRQ_CODEC_MICSCD] = {
+		.primary = WM8350_CODEC_INT,
+		.reg = WM8350_COMPARATOR_INT_OFFSET,
+		.mask = WM8350_CODEC_MICSCD_EINT,
+	},
+	[WM8350_IRQ_CODEC_MICD] = {
+		.primary = WM8350_CODEC_INT,
+		.reg = WM8350_COMPARATOR_INT_OFFSET,
+		.mask = WM8350_CODEC_MICD_EINT,
+	},
+	[WM8350_IRQ_EXT_USB_FB] = {
+		.primary = WM8350_EXT_INT,
+		.reg = WM8350_COMPARATOR_INT_OFFSET,
+		.mask = WM8350_EXT_USB_FB_EINT,
+	},
+	[WM8350_IRQ_EXT_WALL_FB] = {
+		.primary = WM8350_EXT_INT,
+		.reg = WM8350_COMPARATOR_INT_OFFSET,
+		.mask = WM8350_EXT_WALL_FB_EINT,
+	},
+	[WM8350_IRQ_EXT_BAT_FB] = {
+		.primary = WM8350_EXT_INT,
+		.reg = WM8350_COMPARATOR_INT_OFFSET,
+		.mask = WM8350_EXT_BAT_FB_EINT,
+	},
+	[WM8350_IRQ_GPIO(0)] = {
+		.primary = WM8350_GP_INT,
+		.reg = WM8350_GPIO_INT_OFFSET,
+		.mask = WM8350_GP0_EINT,
+	},
+	[WM8350_IRQ_GPIO(1)] = {
+		.primary = WM8350_GP_INT,
+		.reg = WM8350_GPIO_INT_OFFSET,
+		.mask = WM8350_GP1_EINT,
+	},
+	[WM8350_IRQ_GPIO(2)] = {
+		.primary = WM8350_GP_INT,
+		.reg = WM8350_GPIO_INT_OFFSET,
+		.mask = WM8350_GP2_EINT,
+	},
+	[WM8350_IRQ_GPIO(3)] = {
+		.primary = WM8350_GP_INT,
+		.reg = WM8350_GPIO_INT_OFFSET,
+		.mask = WM8350_GP3_EINT,
+	},
+	[WM8350_IRQ_GPIO(4)] = {
+		.primary = WM8350_GP_INT,
+		.reg = WM8350_GPIO_INT_OFFSET,
+		.mask = WM8350_GP4_EINT,
+	},
+	[WM8350_IRQ_GPIO(5)] = {
+		.primary = WM8350_GP_INT,
+		.reg = WM8350_GPIO_INT_OFFSET,
+		.mask = WM8350_GP5_EINT,
+	},
+	[WM8350_IRQ_GPIO(6)] = {
+		.primary = WM8350_GP_INT,
+		.reg = WM8350_GPIO_INT_OFFSET,
+		.mask = WM8350_GP6_EINT,
+	},
+	[WM8350_IRQ_GPIO(7)] = {
+		.primary = WM8350_GP_INT,
+		.reg = WM8350_GPIO_INT_OFFSET,
+		.mask = WM8350_GP7_EINT,
+	},
+	[WM8350_IRQ_GPIO(8)] = {
+		.primary = WM8350_GP_INT,
+		.reg = WM8350_GPIO_INT_OFFSET,
+		.mask = WM8350_GP8_EINT,
+	},
+	[WM8350_IRQ_GPIO(9)] = {
+		.primary = WM8350_GP_INT,
+		.reg = WM8350_GPIO_INT_OFFSET,
+		.mask = WM8350_GP9_EINT,
+	},
+	[WM8350_IRQ_GPIO(10)] = {
+		.primary = WM8350_GP_INT,
+		.reg = WM8350_GPIO_INT_OFFSET,
+		.mask = WM8350_GP10_EINT,
+	},
+	[WM8350_IRQ_GPIO(11)] = {
+		.primary = WM8350_GP_INT,
+		.reg = WM8350_GPIO_INT_OFFSET,
+		.mask = WM8350_GP11_EINT,
+	},
+	[WM8350_IRQ_GPIO(12)] = {
+		.primary = WM8350_GP_INT,
+		.reg = WM8350_GPIO_INT_OFFSET,
+		.mask = WM8350_GP12_EINT,
+	},
+};
+
+static inline struct wm8350_irq_data *irq_to_wm8350_irq(struct wm8350 *wm8350,
+							int irq)
+{
+	return &wm8350_irqs[irq - wm8350->irq_base];
+}
+
+/*
+ * This is a threaded IRQ handler so can access I2C/SPI.  Since all
+ * interrupts are clear on read the IRQ line will be reasserted and
+ * the physical IRQ will be handled again if another interrupt is
+ * asserted while we run - in the normal course of events this is a
+ * rare occurrence so we save I2C/SPI reads.  We're also assuming that
+ * it's rare to get lots of interrupts firing simultaneously so try to
+ * minimise I/O.
+ */
+static irqreturn_t wm8350_irq(int irq, void *irq_data)
+{
+	struct wm8350 *wm8350 = irq_data;
+	u16 level_one;
+	u16 sub_reg[WM8350_NUM_IRQ_REGS];
+	int read_done[WM8350_NUM_IRQ_REGS];
+	struct wm8350_irq_data *data;
+	int i;
+
+	level_one = wm8350_reg_read(wm8350, WM8350_SYSTEM_INTERRUPTS)
+		& ~wm8350_reg_read(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK);
+
+	if (!level_one)
+		return IRQ_NONE;
+
+	memset(&read_done, 0, sizeof(read_done));
+
+	for (i = 0; i < ARRAY_SIZE(wm8350_irqs); i++) {
+		data = &wm8350_irqs[i];
+
+		if (!(level_one & data->primary))
+			continue;
+
+		if (!read_done[data->reg]) {
+			sub_reg[data->reg] =
+				wm8350_reg_read(wm8350, WM8350_INT_STATUS_1 +
+						data->reg);
+			sub_reg[data->reg] &= ~wm8350->irq_masks[data->reg];
+			read_done[data->reg] = 1;
+		}
+
+		if (sub_reg[data->reg] & data->mask)
+			handle_nested_irq(wm8350->irq_base + i);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void wm8350_irq_lock(struct irq_data *data)
+{
+	struct wm8350 *wm8350 = irq_data_get_irq_chip_data(data);
+
+	mutex_lock(&wm8350->irq_lock);
+}
+
+static void wm8350_irq_sync_unlock(struct irq_data *data)
+{
+	struct wm8350 *wm8350 = irq_data_get_irq_chip_data(data);
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(wm8350->irq_masks); i++) {
+		/* If there's been a change in the mask write it back
+		 * to the hardware. */
+		if (wm8350->irq_masks[i] !=
+		    wm8350->reg_cache[WM8350_INT_STATUS_1_MASK + i])
+			WARN_ON(wm8350_reg_write(wm8350,
+					 WM8350_INT_STATUS_1_MASK + i,
+						 wm8350->irq_masks[i]));
+	}
+
+	mutex_unlock(&wm8350->irq_lock);
+}
+
+static void wm8350_irq_enable(struct irq_data *data)
+{
+	struct wm8350 *wm8350 = irq_data_get_irq_chip_data(data);
+	struct wm8350_irq_data *irq_data = irq_to_wm8350_irq(wm8350,
+							     data->irq);
+
+	wm8350->irq_masks[irq_data->reg] &= ~irq_data->mask;
+}
+
+static void wm8350_irq_disable(struct irq_data *data)
+{
+	struct wm8350 *wm8350 = irq_data_get_irq_chip_data(data);
+	struct wm8350_irq_data *irq_data = irq_to_wm8350_irq(wm8350,
+							     data->irq);
+
+	wm8350->irq_masks[irq_data->reg] |= irq_data->mask;
+}
+
+static struct irq_chip wm8350_irq_chip = {
+	.name			= "wm8350",
+	.irq_bus_lock		= wm8350_irq_lock,
+	.irq_bus_sync_unlock	= wm8350_irq_sync_unlock,
+	.irq_disable		= wm8350_irq_disable,
+	.irq_enable		= wm8350_irq_enable,
+};
+
+int wm8350_irq_init(struct wm8350 *wm8350, int irq,
+		    struct wm8350_platform_data *pdata)
+{
+	int ret, cur_irq, i;
+	int flags = IRQF_ONESHOT;
+	int irq_base = -1;
+
+	if (!irq) {
+		dev_warn(wm8350->dev, "No interrupt support, no core IRQ\n");
+		return 0;
+	}
+
+	/* Mask top level interrupts */
+	wm8350_reg_write(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK, 0xFFFF);
+
+	/* Mask all individual interrupts by default and cache the
+	 * masks.  We read the masks back since there are unwritable
+	 * bits in the mask registers. */
+	for (i = 0; i < ARRAY_SIZE(wm8350->irq_masks); i++) {
+		wm8350_reg_write(wm8350, WM8350_INT_STATUS_1_MASK + i,
+				 0xFFFF);
+		wm8350->irq_masks[i] =
+			wm8350_reg_read(wm8350,
+					WM8350_INT_STATUS_1_MASK + i);
+	}
+
+	mutex_init(&wm8350->irq_lock);
+	wm8350->chip_irq = irq;
+
+	if (pdata && pdata->irq_base > 0)
+		irq_base = pdata->irq_base;
+
+	wm8350->irq_base = irq_alloc_descs(irq_base, 0, ARRAY_SIZE(wm8350_irqs), 0);
+	if (wm8350->irq_base < 0) {
+		dev_warn(wm8350->dev, "Allocating irqs failed with %d\n",
+			wm8350->irq_base);
+		return 0;
+	}
+
+	if (pdata && pdata->irq_high) {
+		flags |= IRQF_TRIGGER_HIGH;
+
+		wm8350_set_bits(wm8350, WM8350_SYSTEM_CONTROL_1,
+				WM8350_IRQ_POL);
+	} else {
+		flags |= IRQF_TRIGGER_LOW;
+
+		wm8350_clear_bits(wm8350, WM8350_SYSTEM_CONTROL_1,
+				  WM8350_IRQ_POL);
+	}
+
+	/* Register with genirq */
+	for (cur_irq = wm8350->irq_base;
+	     cur_irq < ARRAY_SIZE(wm8350_irqs) + wm8350->irq_base;
+	     cur_irq++) {
+		irq_set_chip_data(cur_irq, wm8350);
+		irq_set_chip_and_handler(cur_irq, &wm8350_irq_chip,
+					 handle_edge_irq);
+		irq_set_nested_thread(cur_irq, 1);
+
+		/* ARM needs us to explicitly flag the IRQ as valid
+		 * and will set them noprobe when we do so. */
+#ifdef CONFIG_ARM
+		set_irq_flags(cur_irq, IRQF_VALID);
+#else
+		irq_set_noprobe(cur_irq);
+#endif
+	}
+
+	ret = request_threaded_irq(irq, NULL, wm8350_irq, flags,
+				   "wm8350", wm8350);
+	if (ret != 0)
+		dev_err(wm8350->dev, "Failed to request IRQ: %d\n", ret);
+
+	/* Allow interrupts to fire */
+	wm8350_reg_write(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK, 0);
+
+	return ret;
+}
+
+int wm8350_irq_exit(struct wm8350 *wm8350)
+{
+	free_irq(wm8350->chip_irq, wm8350);
+	return 0;
+}
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/wm8350-regmap.c b/ap/os/linux/linux-3.4.x/drivers/mfd/wm8350-regmap.c
new file mode 100644
index 0000000..e965139
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/wm8350-regmap.c
@@ -0,0 +1,3435 @@
+/*
+ * wm8350-regmap.c  --  Wolfson Microelectronics WM8350 register map
+ *
+ * This file splits out the tables describing the defaults and access
+ * status of the WM8350 registers since they are rather large.
+ *
+ * Copyright 2007, 2008 Wolfson Microelectronics PLC.
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#include <linux/mfd/wm8350/core.h>
+
+#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_0
+
+#undef WM8350_HAVE_CONFIG_MODE
+#define WM8350_HAVE_CONFIG_MODE
+
+const u16 wm8350_mode0_defaults[] = {
+	0x17FF,     /* R0   - Reset/ID */
+	0x1000,     /* R1   - ID */
+	0x0000,     /* R2 */
+	0x1002,     /* R3   - System Control 1 */
+	0x0004,     /* R4   - System Control 2 */
+	0x0000,     /* R5   - System Hibernate */
+	0x8A00,     /* R6   - Interface Control */
+	0x0000,     /* R7 */
+	0x8000,     /* R8   - Power mgmt (1) */
+	0x0000,     /* R9   - Power mgmt (2) */
+	0x0000,     /* R10  - Power mgmt (3) */
+	0x2000,     /* R11  - Power mgmt (4) */
+	0x0E00,     /* R12  - Power mgmt (5) */
+	0x0000,     /* R13  - Power mgmt (6) */
+	0x0000,     /* R14  - Power mgmt (7) */
+	0x0000,     /* R15 */
+	0x0000,     /* R16  - RTC Seconds/Minutes */
+	0x0100,     /* R17  - RTC Hours/Day */
+	0x0101,     /* R18  - RTC Date/Month */
+	0x1400,     /* R19  - RTC Year */
+	0x0000,     /* R20  - Alarm Seconds/Minutes */
+	0x0000,     /* R21  - Alarm Hours/Day */
+	0x0000,     /* R22  - Alarm Date/Month */
+	0x0320,     /* R23  - RTC Time Control */
+	0x0000,     /* R24  - System Interrupts */
+	0x0000,     /* R25  - Interrupt Status 1 */
+	0x0000,     /* R26  - Interrupt Status 2 */
+	0x0000,     /* R27  - Power Up Interrupt Status */
+	0x0000,     /* R28  - Under Voltage Interrupt status */
+	0x0000,     /* R29  - Over Current Interrupt status */
+	0x0000,     /* R30  - GPIO Interrupt Status */
+	0x0000,     /* R31  - Comparator Interrupt Status */
+	0x3FFF,     /* R32  - System Interrupts Mask */
+	0x0000,     /* R33  - Interrupt Status 1 Mask */
+	0x0000,     /* R34  - Interrupt Status 2 Mask */
+	0x0000,     /* R35  - Power Up Interrupt Status Mask */
+	0x0000,     /* R36  - Under Voltage Interrupt status Mask */
+	0x0000,     /* R37  - Over Current Interrupt status Mask */
+	0x0000,     /* R38  - GPIO Interrupt Status Mask */
+	0x0000,     /* R39  - Comparator Interrupt Status Mask */
+	0x0040,     /* R40  - Clock Control 1 */
+	0x0000,     /* R41  - Clock Control 2 */
+	0x3B00,     /* R42  - FLL Control 1 */
+	0x7086,     /* R43  - FLL Control 2 */
+	0xC226,     /* R44  - FLL Control 3 */
+	0x0000,     /* R45  - FLL Control 4 */
+	0x0000,     /* R46 */
+	0x0000,     /* R47 */
+	0x0000,     /* R48  - DAC Control */
+	0x0000,     /* R49 */
+	0x00C0,     /* R50  - DAC Digital Volume L */
+	0x00C0,     /* R51  - DAC Digital Volume R */
+	0x0000,     /* R52 */
+	0x0040,     /* R53  - DAC LR Rate */
+	0x0000,     /* R54  - DAC Clock Control */
+	0x0000,     /* R55 */
+	0x0000,     /* R56 */
+	0x0000,     /* R57 */
+	0x4000,     /* R58  - DAC Mute */
+	0x0000,     /* R59  - DAC Mute Volume */
+	0x0000,     /* R60  - DAC Side */
+	0x0000,     /* R61 */
+	0x0000,     /* R62 */
+	0x0000,     /* R63 */
+	0x8000,     /* R64  - ADC Control */
+	0x0000,     /* R65 */
+	0x00C0,     /* R66  - ADC Digital Volume L */
+	0x00C0,     /* R67  - ADC Digital Volume R */
+	0x0000,     /* R68  - ADC Divider */
+	0x0000,     /* R69 */
+	0x0040,     /* R70  - ADC LR Rate */
+	0x0000,     /* R71 */
+	0x0303,     /* R72  - Input Control */
+	0x0000,     /* R73  - IN3 Input Control */
+	0x0000,     /* R74  - Mic Bias Control */
+	0x0000,     /* R75 */
+	0x0000,     /* R76  - Output Control */
+	0x0000,     /* R77  - Jack Detect */
+	0x0000,     /* R78  - Anti Pop Control */
+	0x0000,     /* R79 */
+	0x0040,     /* R80  - Left Input Volume */
+	0x0040,     /* R81  - Right Input Volume */
+	0x0000,     /* R82 */
+	0x0000,     /* R83 */
+	0x0000,     /* R84 */
+	0x0000,     /* R85 */
+	0x0000,     /* R86 */
+	0x0000,     /* R87 */
+	0x0800,     /* R88  - Left Mixer Control */
+	0x1000,     /* R89  - Right Mixer Control */
+	0x0000,     /* R90 */
+	0x0000,     /* R91 */
+	0x0000,     /* R92  - OUT3 Mixer Control */
+	0x0000,     /* R93  - OUT4 Mixer Control */
+	0x0000,     /* R94 */
+	0x0000,     /* R95 */
+	0x0000,     /* R96  - Output Left Mixer Volume */
+	0x0000,     /* R97  - Output Right Mixer Volume */
+	0x0000,     /* R98  - Input Mixer Volume L */
+	0x0000,     /* R99  - Input Mixer Volume R */
+	0x0000,     /* R100 - Input Mixer Volume */
+	0x0000,     /* R101 */
+	0x0000,     /* R102 */
+	0x0000,     /* R103 */
+	0x00E4,     /* R104 - LOUT1 Volume */
+	0x00E4,     /* R105 - ROUT1 Volume */
+	0x00E4,     /* R106 - LOUT2 Volume */
+	0x02E4,     /* R107 - ROUT2 Volume */
+	0x0000,     /* R108 */
+	0x0000,     /* R109 */
+	0x0000,     /* R110 */
+	0x0000,     /* R111 - BEEP Volume */
+	0x0A00,     /* R112 - AI Formating */
+	0x0000,     /* R113 - ADC DAC COMP */
+	0x0020,     /* R114 - AI ADC Control */
+	0x0020,     /* R115 - AI DAC Control */
+	0x0000,     /* R116 - AIF Test */
+	0x0000,     /* R117 */
+	0x0000,     /* R118 */
+	0x0000,     /* R119 */
+	0x0000,     /* R120 */
+	0x0000,     /* R121 */
+	0x0000,     /* R122 */
+	0x0000,     /* R123 */
+	0x0000,     /* R124 */
+	0x0000,     /* R125 */
+	0x0000,     /* R126 */
+	0x0000,     /* R127 */
+	0x1FFF,     /* R128 - GPIO Debounce */
+	0x0000,     /* R129 - GPIO Pin pull up Control */
+	0x03FC,     /* R130 - GPIO Pull down Control */
+	0x0000,     /* R131 - GPIO Interrupt Mode */
+	0x0000,     /* R132 */
+	0x0000,     /* R133 - GPIO Control */
+	0x0FFC,     /* R134 - GPIO Configuration (i/o) */
+	0x0FFC,     /* R135 - GPIO Pin Polarity / Type */
+	0x0000,     /* R136 */
+	0x0000,     /* R137 */
+	0x0000,     /* R138 */
+	0x0000,     /* R139 */
+	0x0013,     /* R140 - GPIO Function Select 1 */
+	0x0000,     /* R141 - GPIO Function Select 2 */
+	0x0000,     /* R142 - GPIO Function Select 3 */
+	0x0003,     /* R143 - GPIO Function Select 4 */
+	0x0000,     /* R144 - Digitiser Control (1) */
+	0x0002,     /* R145 - Digitiser Control (2) */
+	0x0000,     /* R146 */
+	0x0000,     /* R147 */
+	0x0000,     /* R148 */
+	0x0000,     /* R149 */
+	0x0000,     /* R150 */
+	0x0000,     /* R151 */
+	0x7000,     /* R152 - AUX1 Readback */
+	0x7000,     /* R153 - AUX2 Readback */
+	0x7000,     /* R154 - AUX3 Readback */
+	0x7000,     /* R155 - AUX4 Readback */
+	0x0000,     /* R156 - USB Voltage Readback */
+	0x0000,     /* R157 - LINE Voltage Readback */
+	0x0000,     /* R158 - BATT Voltage Readback */
+	0x0000,     /* R159 - Chip Temp Readback */
+	0x0000,     /* R160 */
+	0x0000,     /* R161 */
+	0x0000,     /* R162 */
+	0x0000,     /* R163 - Generic Comparator Control */
+	0x0000,     /* R164 - Generic comparator 1 */
+	0x0000,     /* R165 - Generic comparator 2 */
+	0x0000,     /* R166 - Generic comparator 3 */
+	0x0000,     /* R167 - Generic comparator 4 */
+	0xA00F,     /* R168 - Battery Charger Control 1 */
+	0x0B06,     /* R169 - Battery Charger Control 2 */
+	0x0000,     /* R170 - Battery Charger Control 3 */
+	0x0000,     /* R171 */
+	0x0000,     /* R172 - Current Sink Driver A */
+	0x0000,     /* R173 - CSA Flash control */
+	0x0000,     /* R174 - Current Sink Driver B */
+	0x0000,     /* R175 - CSB Flash control */
+	0x0000,     /* R176 - DCDC/LDO requested */
+	0x002D,     /* R177 - DCDC Active options */
+	0x0000,     /* R178 - DCDC Sleep options */
+	0x0025,     /* R179 - Power-check comparator */
+	0x000E,     /* R180 - DCDC1 Control */
+	0x0000,     /* R181 - DCDC1 Timeouts */
+	0x1006,     /* R182 - DCDC1 Low Power */
+	0x0018,     /* R183 - DCDC2 Control */
+	0x0000,     /* R184 - DCDC2 Timeouts */
+	0x0000,     /* R185 */
+	0x0000,     /* R186 - DCDC3 Control */
+	0x0000,     /* R187 - DCDC3 Timeouts */
+	0x0006,     /* R188 - DCDC3 Low Power */
+	0x0000,     /* R189 - DCDC4 Control */
+	0x0000,     /* R190 - DCDC4 Timeouts */
+	0x0006,     /* R191 - DCDC4 Low Power */
+	0x0008,     /* R192 - DCDC5 Control */
+	0x0000,     /* R193 - DCDC5 Timeouts */
+	0x0000,     /* R194 */
+	0x0000,     /* R195 - DCDC6 Control */
+	0x0000,     /* R196 - DCDC6 Timeouts */
+	0x0006,     /* R197 - DCDC6 Low Power */
+	0x0000,     /* R198 */
+	0x0003,     /* R199 - Limit Switch Control */
+	0x001C,     /* R200 - LDO1 Control */
+	0x0000,     /* R201 - LDO1 Timeouts */
+	0x001C,     /* R202 - LDO1 Low Power */
+	0x001B,     /* R203 - LDO2 Control */
+	0x0000,     /* R204 - LDO2 Timeouts */
+	0x001C,     /* R205 - LDO2 Low Power */
+	0x001B,     /* R206 - LDO3 Control */
+	0x0000,     /* R207 - LDO3 Timeouts */
+	0x001C,     /* R208 - LDO3 Low Power */
+	0x001B,     /* R209 - LDO4 Control */
+	0x0000,     /* R210 - LDO4 Timeouts */
+	0x001C,     /* R211 - LDO4 Low Power */
+	0x0000,     /* R212 */
+	0x0000,     /* R213 */
+	0x0000,     /* R214 */
+	0x0000,     /* R215 - VCC_FAULT Masks */
+	0x001F,     /* R216 - Main Bandgap Control */
+	0x0000,     /* R217 - OSC Control */
+	0x9000,     /* R218 - RTC Tick Control */
+	0x0000,     /* R219 */
+	0x4000,     /* R220 - RAM BIST 1 */
+	0x0000,     /* R221 */
+	0x0000,     /* R222 */
+	0x0000,     /* R223 */
+	0x0000,     /* R224 */
+	0x0000,     /* R225 - DCDC/LDO status */
+	0x0000,     /* R226 */
+	0x0000,     /* R227 */
+	0x0000,     /* R228 */
+	0x0000,     /* R229 */
+	0xE000,     /* R230 - GPIO Pin Status */
+	0x0000,     /* R231 */
+	0x0000,     /* R232 */
+	0x0000,     /* R233 */
+	0x0000,     /* R234 */
+	0x0000,     /* R235 */
+	0x0000,     /* R236 */
+	0x0000,     /* R237 */
+	0x0000,     /* R238 */
+	0x0000,     /* R239 */
+	0x0000,     /* R240 */
+	0x0000,     /* R241 */
+	0x0000,     /* R242 */
+	0x0000,     /* R243 */
+	0x0000,     /* R244 */
+	0x0000,     /* R245 */
+	0x0000,     /* R246 */
+	0x0000,     /* R247 */
+	0x0000,     /* R248 */
+	0x0000,     /* R249 */
+	0x0000,     /* R250 */
+	0x0000,     /* R251 */
+	0x0000,     /* R252 */
+	0x0000,     /* R253 */
+	0x0000,     /* R254 */
+	0x0000,     /* R255 */
+};
+#endif
+
+#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_1
+
+#undef WM8350_HAVE_CONFIG_MODE
+#define WM8350_HAVE_CONFIG_MODE
+
+const u16 wm8350_mode1_defaults[] = {
+	0x17FF,     /* R0   - Reset/ID */
+	0x1000,     /* R1   - ID */
+	0x0000,     /* R2 */
+	0x1002,     /* R3   - System Control 1 */
+	0x0014,     /* R4   - System Control 2 */
+	0x0000,     /* R5   - System Hibernate */
+	0x8A00,     /* R6   - Interface Control */
+	0x0000,     /* R7 */
+	0x8000,     /* R8   - Power mgmt (1) */
+	0x0000,     /* R9   - Power mgmt (2) */
+	0x0000,     /* R10  - Power mgmt (3) */
+	0x2000,     /* R11  - Power mgmt (4) */
+	0x0E00,     /* R12  - Power mgmt (5) */
+	0x0000,     /* R13  - Power mgmt (6) */
+	0x0000,     /* R14  - Power mgmt (7) */
+	0x0000,     /* R15 */
+	0x0000,     /* R16  - RTC Seconds/Minutes */
+	0x0100,     /* R17  - RTC Hours/Day */
+	0x0101,     /* R18  - RTC Date/Month */
+	0x1400,     /* R19  - RTC Year */
+	0x0000,     /* R20  - Alarm Seconds/Minutes */
+	0x0000,     /* R21  - Alarm Hours/Day */
+	0x0000,     /* R22  - Alarm Date/Month */
+	0x0320,     /* R23  - RTC Time Control */
+	0x0000,     /* R24  - System Interrupts */
+	0x0000,     /* R25  - Interrupt Status 1 */
+	0x0000,     /* R26  - Interrupt Status 2 */
+	0x0000,     /* R27  - Power Up Interrupt Status */
+	0x0000,     /* R28  - Under Voltage Interrupt status */
+	0x0000,     /* R29  - Over Current Interrupt status */
+	0x0000,     /* R30  - GPIO Interrupt Status */
+	0x0000,     /* R31  - Comparator Interrupt Status */
+	0x3FFF,     /* R32  - System Interrupts Mask */
+	0x0000,     /* R33  - Interrupt Status 1 Mask */
+	0x0000,     /* R34  - Interrupt Status 2 Mask */
+	0x0000,     /* R35  - Power Up Interrupt Status Mask */
+	0x0000,     /* R36  - Under Voltage Interrupt status Mask */
+	0x0000,     /* R37  - Over Current Interrupt status Mask */
+	0x0000,     /* R38  - GPIO Interrupt Status Mask */
+	0x0000,     /* R39  - Comparator Interrupt Status Mask */
+	0x0040,     /* R40  - Clock Control 1 */
+	0x0000,     /* R41  - Clock Control 2 */
+	0x3B00,     /* R42  - FLL Control 1 */
+	0x7086,     /* R43  - FLL Control 2 */
+	0xC226,     /* R44  - FLL Control 3 */
+	0x0000,     /* R45  - FLL Control 4 */
+	0x0000,     /* R46 */
+	0x0000,     /* R47 */
+	0x0000,     /* R48  - DAC Control */
+	0x0000,     /* R49 */
+	0x00C0,     /* R50  - DAC Digital Volume L */
+	0x00C0,     /* R51  - DAC Digital Volume R */
+	0x0000,     /* R52 */
+	0x0040,     /* R53  - DAC LR Rate */
+	0x0000,     /* R54  - DAC Clock Control */
+	0x0000,     /* R55 */
+	0x0000,     /* R56 */
+	0x0000,     /* R57 */
+	0x4000,     /* R58  - DAC Mute */
+	0x0000,     /* R59  - DAC Mute Volume */
+	0x0000,     /* R60  - DAC Side */
+	0x0000,     /* R61 */
+	0x0000,     /* R62 */
+	0x0000,     /* R63 */
+	0x8000,     /* R64  - ADC Control */
+	0x0000,     /* R65 */
+	0x00C0,     /* R66  - ADC Digital Volume L */
+	0x00C0,     /* R67  - ADC Digital Volume R */
+	0x0000,     /* R68  - ADC Divider */
+	0x0000,     /* R69 */
+	0x0040,     /* R70  - ADC LR Rate */
+	0x0000,     /* R71 */
+	0x0303,     /* R72  - Input Control */
+	0x0000,     /* R73  - IN3 Input Control */
+	0x0000,     /* R74  - Mic Bias Control */
+	0x0000,     /* R75 */
+	0x0000,     /* R76  - Output Control */
+	0x0000,     /* R77  - Jack Detect */
+	0x0000,     /* R78  - Anti Pop Control */
+	0x0000,     /* R79 */
+	0x0040,     /* R80  - Left Input Volume */
+	0x0040,     /* R81  - Right Input Volume */
+	0x0000,     /* R82 */
+	0x0000,     /* R83 */
+	0x0000,     /* R84 */
+	0x0000,     /* R85 */
+	0x0000,     /* R86 */
+	0x0000,     /* R87 */
+	0x0800,     /* R88  - Left Mixer Control */
+	0x1000,     /* R89  - Right Mixer Control */
+	0x0000,     /* R90 */
+	0x0000,     /* R91 */
+	0x0000,     /* R92  - OUT3 Mixer Control */
+	0x0000,     /* R93  - OUT4 Mixer Control */
+	0x0000,     /* R94 */
+	0x0000,     /* R95 */
+	0x0000,     /* R96  - Output Left Mixer Volume */
+	0x0000,     /* R97  - Output Right Mixer Volume */
+	0x0000,     /* R98  - Input Mixer Volume L */
+	0x0000,     /* R99  - Input Mixer Volume R */
+	0x0000,     /* R100 - Input Mixer Volume */
+	0x0000,     /* R101 */
+	0x0000,     /* R102 */
+	0x0000,     /* R103 */
+	0x00E4,     /* R104 - LOUT1 Volume */
+	0x00E4,     /* R105 - ROUT1 Volume */
+	0x00E4,     /* R106 - LOUT2 Volume */
+	0x02E4,     /* R107 - ROUT2 Volume */
+	0x0000,     /* R108 */
+	0x0000,     /* R109 */
+	0x0000,     /* R110 */
+	0x0000,     /* R111 - BEEP Volume */
+	0x0A00,     /* R112 - AI Formating */
+	0x0000,     /* R113 - ADC DAC COMP */
+	0x0020,     /* R114 - AI ADC Control */
+	0x0020,     /* R115 - AI DAC Control */
+	0x0000,     /* R116 - AIF Test */
+	0x0000,     /* R117 */
+	0x0000,     /* R118 */
+	0x0000,     /* R119 */
+	0x0000,     /* R120 */
+	0x0000,     /* R121 */
+	0x0000,     /* R122 */
+	0x0000,     /* R123 */
+	0x0000,     /* R124 */
+	0x0000,     /* R125 */
+	0x0000,     /* R126 */
+	0x0000,     /* R127 */
+	0x1FFF,     /* R128 - GPIO Debounce */
+	0x0000,     /* R129 - GPIO Pin pull up Control */
+	0x03FC,     /* R130 - GPIO Pull down Control */
+	0x0000,     /* R131 - GPIO Interrupt Mode */
+	0x0000,     /* R132 */
+	0x0000,     /* R133 - GPIO Control */
+	0x00FB,     /* R134 - GPIO Configuration (i/o) */
+	0x04FE,     /* R135 - GPIO Pin Polarity / Type */
+	0x0000,     /* R136 */
+	0x0000,     /* R137 */
+	0x0000,     /* R138 */
+	0x0000,     /* R139 */
+	0x0312,     /* R140 - GPIO Function Select 1 */
+	0x1003,     /* R141 - GPIO Function Select 2 */
+	0x1331,     /* R142 - GPIO Function Select 3 */
+	0x0003,     /* R143 - GPIO Function Select 4 */
+	0x0000,     /* R144 - Digitiser Control (1) */
+	0x0002,     /* R145 - Digitiser Control (2) */
+	0x0000,     /* R146 */
+	0x0000,     /* R147 */
+	0x0000,     /* R148 */
+	0x0000,     /* R149 */
+	0x0000,     /* R150 */
+	0x0000,     /* R151 */
+	0x7000,     /* R152 - AUX1 Readback */
+	0x7000,     /* R153 - AUX2 Readback */
+	0x7000,     /* R154 - AUX3 Readback */
+	0x7000,     /* R155 - AUX4 Readback */
+	0x0000,     /* R156 - USB Voltage Readback */
+	0x0000,     /* R157 - LINE Voltage Readback */
+	0x0000,     /* R158 - BATT Voltage Readback */
+	0x0000,     /* R159 - Chip Temp Readback */
+	0x0000,     /* R160 */
+	0x0000,     /* R161 */
+	0x0000,     /* R162 */
+	0x0000,     /* R163 - Generic Comparator Control */
+	0x0000,     /* R164 - Generic comparator 1 */
+	0x0000,     /* R165 - Generic comparator 2 */
+	0x0000,     /* R166 - Generic comparator 3 */
+	0x0000,     /* R167 - Generic comparator 4 */
+	0xA00F,     /* R168 - Battery Charger Control 1 */
+	0x0B06,     /* R169 - Battery Charger Control 2 */
+	0x0000,     /* R170 - Battery Charger Control 3 */
+	0x0000,     /* R171 */
+	0x0000,     /* R172 - Current Sink Driver A */
+	0x0000,     /* R173 - CSA Flash control */
+	0x0000,     /* R174 - Current Sink Driver B */
+	0x0000,     /* R175 - CSB Flash control */
+	0x0000,     /* R176 - DCDC/LDO requested */
+	0x002D,     /* R177 - DCDC Active options */
+	0x0000,     /* R178 - DCDC Sleep options */
+	0x0025,     /* R179 - Power-check comparator */
+	0x0062,     /* R180 - DCDC1 Control */
+	0x0400,     /* R181 - DCDC1 Timeouts */
+	0x1006,     /* R182 - DCDC1 Low Power */
+	0x0018,     /* R183 - DCDC2 Control */
+	0x0000,     /* R184 - DCDC2 Timeouts */
+	0x0000,     /* R185 */
+	0x0026,     /* R186 - DCDC3 Control */
+	0x0400,     /* R187 - DCDC3 Timeouts */
+	0x0006,     /* R188 - DCDC3 Low Power */
+	0x0062,     /* R189 - DCDC4 Control */
+	0x0400,     /* R190 - DCDC4 Timeouts */
+	0x0006,     /* R191 - DCDC4 Low Power */
+	0x0008,     /* R192 - DCDC5 Control */
+	0x0000,     /* R193 - DCDC5 Timeouts */
+	0x0000,     /* R194 */
+	0x0026,     /* R195 - DCDC6 Control */
+	0x0800,     /* R196 - DCDC6 Timeouts */
+	0x0006,     /* R197 - DCDC6 Low Power */
+	0x0000,     /* R198 */
+	0x0003,     /* R199 - Limit Switch Control */
+	0x0006,     /* R200 - LDO1 Control */
+	0x0400,     /* R201 - LDO1 Timeouts */
+	0x001C,     /* R202 - LDO1 Low Power */
+	0x0006,     /* R203 - LDO2 Control */
+	0x0400,     /* R204 - LDO2 Timeouts */
+	0x001C,     /* R205 - LDO2 Low Power */
+	0x001B,     /* R206 - LDO3 Control */
+	0x0000,     /* R207 - LDO3 Timeouts */
+	0x001C,     /* R208 - LDO3 Low Power */
+	0x001B,     /* R209 - LDO4 Control */
+	0x0000,     /* R210 - LDO4 Timeouts */
+	0x001C,     /* R211 - LDO4 Low Power */
+	0x0000,     /* R212 */
+	0x0000,     /* R213 */
+	0x0000,     /* R214 */
+	0x0000,     /* R215 - VCC_FAULT Masks */
+	0x001F,     /* R216 - Main Bandgap Control */
+	0x0000,     /* R217 - OSC Control */
+	0x9000,     /* R218 - RTC Tick Control */
+	0x0000,     /* R219 */
+	0x4000,     /* R220 - RAM BIST 1 */
+	0x0000,     /* R221 */
+	0x0000,     /* R222 */
+	0x0000,     /* R223 */
+	0x0000,     /* R224 */
+	0x0000,     /* R225 - DCDC/LDO status */
+	0x0000,     /* R226 */
+	0x0000,     /* R227 */
+	0x0000,     /* R228 */
+	0x0000,     /* R229 */
+	0xE000,     /* R230 - GPIO Pin Status */
+	0x0000,     /* R231 */
+	0x0000,     /* R232 */
+	0x0000,     /* R233 */
+	0x0000,     /* R234 */
+	0x0000,     /* R235 */
+	0x0000,     /* R236 */
+	0x0000,     /* R237 */
+	0x0000,     /* R238 */
+	0x0000,     /* R239 */
+	0x0000,     /* R240 */
+	0x0000,     /* R241 */
+	0x0000,     /* R242 */
+	0x0000,     /* R243 */
+	0x0000,     /* R244 */
+	0x0000,     /* R245 */
+	0x0000,     /* R246 */
+	0x0000,     /* R247 */
+	0x0000,     /* R248 */
+	0x0000,     /* R249 */
+	0x0000,     /* R250 */
+	0x0000,     /* R251 */
+	0x0000,     /* R252 */
+	0x0000,     /* R253 */
+	0x0000,     /* R254 */
+	0x0000,     /* R255 */
+};
+#endif
+
+#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_2
+
+#undef WM8350_HAVE_CONFIG_MODE
+#define WM8350_HAVE_CONFIG_MODE
+
+const u16 wm8350_mode2_defaults[] = {
+	0x17FF,     /* R0   - Reset/ID */
+	0x1000,     /* R1   - ID */
+	0x0000,     /* R2 */
+	0x1002,     /* R3   - System Control 1 */
+	0x0014,     /* R4   - System Control 2 */
+	0x0000,     /* R5   - System Hibernate */
+	0x8A00,     /* R6   - Interface Control */
+	0x0000,     /* R7 */
+	0x8000,     /* R8   - Power mgmt (1) */
+	0x0000,     /* R9   - Power mgmt (2) */
+	0x0000,     /* R10  - Power mgmt (3) */
+	0x2000,     /* R11  - Power mgmt (4) */
+	0x0E00,     /* R12  - Power mgmt (5) */
+	0x0000,     /* R13  - Power mgmt (6) */
+	0x0000,     /* R14  - Power mgmt (7) */
+	0x0000,     /* R15 */
+	0x0000,     /* R16  - RTC Seconds/Minutes */
+	0x0100,     /* R17  - RTC Hours/Day */
+	0x0101,     /* R18  - RTC Date/Month */
+	0x1400,     /* R19  - RTC Year */
+	0x0000,     /* R20  - Alarm Seconds/Minutes */
+	0x0000,     /* R21  - Alarm Hours/Day */
+	0x0000,     /* R22  - Alarm Date/Month */
+	0x0320,     /* R23  - RTC Time Control */
+	0x0000,     /* R24  - System Interrupts */
+	0x0000,     /* R25  - Interrupt Status 1 */
+	0x0000,     /* R26  - Interrupt Status 2 */
+	0x0000,     /* R27  - Power Up Interrupt Status */
+	0x0000,     /* R28  - Under Voltage Interrupt status */
+	0x0000,     /* R29  - Over Current Interrupt status */
+	0x0000,     /* R30  - GPIO Interrupt Status */
+	0x0000,     /* R31  - Comparator Interrupt Status */
+	0x3FFF,     /* R32  - System Interrupts Mask */
+	0x0000,     /* R33  - Interrupt Status 1 Mask */
+	0x0000,     /* R34  - Interrupt Status 2 Mask */
+	0x0000,     /* R35  - Power Up Interrupt Status Mask */
+	0x0000,     /* R36  - Under Voltage Interrupt status Mask */
+	0x0000,     /* R37  - Over Current Interrupt status Mask */
+	0x0000,     /* R38  - GPIO Interrupt Status Mask */
+	0x0000,     /* R39  - Comparator Interrupt Status Mask */
+	0x0040,     /* R40  - Clock Control 1 */
+	0x0000,     /* R41  - Clock Control 2 */
+	0x3B00,     /* R42  - FLL Control 1 */
+	0x7086,     /* R43  - FLL Control 2 */
+	0xC226,     /* R44  - FLL Control 3 */
+	0x0000,     /* R45  - FLL Control 4 */
+	0x0000,     /* R46 */
+	0x0000,     /* R47 */
+	0x0000,     /* R48  - DAC Control */
+	0x0000,     /* R49 */
+	0x00C0,     /* R50  - DAC Digital Volume L */
+	0x00C0,     /* R51  - DAC Digital Volume R */
+	0x0000,     /* R52 */
+	0x0040,     /* R53  - DAC LR Rate */
+	0x0000,     /* R54  - DAC Clock Control */
+	0x0000,     /* R55 */
+	0x0000,     /* R56 */
+	0x0000,     /* R57 */
+	0x4000,     /* R58  - DAC Mute */
+	0x0000,     /* R59  - DAC Mute Volume */
+	0x0000,     /* R60  - DAC Side */
+	0x0000,     /* R61 */
+	0x0000,     /* R62 */
+	0x0000,     /* R63 */
+	0x8000,     /* R64  - ADC Control */
+	0x0000,     /* R65 */
+	0x00C0,     /* R66  - ADC Digital Volume L */
+	0x00C0,     /* R67  - ADC Digital Volume R */
+	0x0000,     /* R68  - ADC Divider */
+	0x0000,     /* R69 */
+	0x0040,     /* R70  - ADC LR Rate */
+	0x0000,     /* R71 */
+	0x0303,     /* R72  - Input Control */
+	0x0000,     /* R73  - IN3 Input Control */
+	0x0000,     /* R74  - Mic Bias Control */
+	0x0000,     /* R75 */
+	0x0000,     /* R76  - Output Control */
+	0x0000,     /* R77  - Jack Detect */
+	0x0000,     /* R78  - Anti Pop Control */
+	0x0000,     /* R79 */
+	0x0040,     /* R80  - Left Input Volume */
+	0x0040,     /* R81  - Right Input Volume */
+	0x0000,     /* R82 */
+	0x0000,     /* R83 */
+	0x0000,     /* R84 */
+	0x0000,     /* R85 */
+	0x0000,     /* R86 */
+	0x0000,     /* R87 */
+	0x0800,     /* R88  - Left Mixer Control */
+	0x1000,     /* R89  - Right Mixer Control */
+	0x0000,     /* R90 */
+	0x0000,     /* R91 */
+	0x0000,     /* R92  - OUT3 Mixer Control */
+	0x0000,     /* R93  - OUT4 Mixer Control */
+	0x0000,     /* R94 */
+	0x0000,     /* R95 */
+	0x0000,     /* R96  - Output Left Mixer Volume */
+	0x0000,     /* R97  - Output Right Mixer Volume */
+	0x0000,     /* R98  - Input Mixer Volume L */
+	0x0000,     /* R99  - Input Mixer Volume R */
+	0x0000,     /* R100 - Input Mixer Volume */
+	0x0000,     /* R101 */
+	0x0000,     /* R102 */
+	0x0000,     /* R103 */
+	0x00E4,     /* R104 - LOUT1 Volume */
+	0x00E4,     /* R105 - ROUT1 Volume */
+	0x00E4,     /* R106 - LOUT2 Volume */
+	0x02E4,     /* R107 - ROUT2 Volume */
+	0x0000,     /* R108 */
+	0x0000,     /* R109 */
+	0x0000,     /* R110 */
+	0x0000,     /* R111 - BEEP Volume */
+	0x0A00,     /* R112 - AI Formating */
+	0x0000,     /* R113 - ADC DAC COMP */
+	0x0020,     /* R114 - AI ADC Control */
+	0x0020,     /* R115 - AI DAC Control */
+	0x0000,     /* R116 - AIF Test */
+	0x0000,     /* R117 */
+	0x0000,     /* R118 */
+	0x0000,     /* R119 */
+	0x0000,     /* R120 */
+	0x0000,     /* R121 */
+	0x0000,     /* R122 */
+	0x0000,     /* R123 */
+	0x0000,     /* R124 */
+	0x0000,     /* R125 */
+	0x0000,     /* R126 */
+	0x0000,     /* R127 */
+	0x1FFF,     /* R128 - GPIO Debounce */
+	0x0000,     /* R129 - GPIO Pin pull up Control */
+	0x03FC,     /* R130 - GPIO Pull down Control */
+	0x0000,     /* R131 - GPIO Interrupt Mode */
+	0x0000,     /* R132 */
+	0x0000,     /* R133 - GPIO Control */
+	0x08FB,     /* R134 - GPIO Configuration (i/o) */
+	0x0CFE,     /* R135 - GPIO Pin Polarity / Type */
+	0x0000,     /* R136 */
+	0x0000,     /* R137 */
+	0x0000,     /* R138 */
+	0x0000,     /* R139 */
+	0x0312,     /* R140 - GPIO Function Select 1 */
+	0x0003,     /* R141 - GPIO Function Select 2 */
+	0x2331,     /* R142 - GPIO Function Select 3 */
+	0x0003,     /* R143 - GPIO Function Select 4 */
+	0x0000,     /* R144 - Digitiser Control (1) */
+	0x0002,     /* R145 - Digitiser Control (2) */
+	0x0000,     /* R146 */
+	0x0000,     /* R147 */
+	0x0000,     /* R148 */
+	0x0000,     /* R149 */
+	0x0000,     /* R150 */
+	0x0000,     /* R151 */
+	0x7000,     /* R152 - AUX1 Readback */
+	0x7000,     /* R153 - AUX2 Readback */
+	0x7000,     /* R154 - AUX3 Readback */
+	0x7000,     /* R155 - AUX4 Readback */
+	0x0000,     /* R156 - USB Voltage Readback */
+	0x0000,     /* R157 - LINE Voltage Readback */
+	0x0000,     /* R158 - BATT Voltage Readback */
+	0x0000,     /* R159 - Chip Temp Readback */
+	0x0000,     /* R160 */
+	0x0000,     /* R161 */
+	0x0000,     /* R162 */
+	0x0000,     /* R163 - Generic Comparator Control */
+	0x0000,     /* R164 - Generic comparator 1 */
+	0x0000,     /* R165 - Generic comparator 2 */
+	0x0000,     /* R166 - Generic comparator 3 */
+	0x0000,     /* R167 - Generic comparator 4 */
+	0xA00F,     /* R168 - Battery Charger Control 1 */
+	0x0B06,     /* R169 - Battery Charger Control 2 */
+	0x0000,     /* R170 - Battery Charger Control 3 */
+	0x0000,     /* R171 */
+	0x0000,     /* R172 - Current Sink Driver A */
+	0x0000,     /* R173 - CSA Flash control */
+	0x0000,     /* R174 - Current Sink Driver B */
+	0x0000,     /* R175 - CSB Flash control */
+	0x0000,     /* R176 - DCDC/LDO requested */
+	0x002D,     /* R177 - DCDC Active options */
+	0x0000,     /* R178 - DCDC Sleep options */
+	0x0025,     /* R179 - Power-check comparator */
+	0x000E,     /* R180 - DCDC1 Control */
+	0x0400,     /* R181 - DCDC1 Timeouts */
+	0x1006,     /* R182 - DCDC1 Low Power */
+	0x0018,     /* R183 - DCDC2 Control */
+	0x0000,     /* R184 - DCDC2 Timeouts */
+	0x0000,     /* R185 */
+	0x002E,     /* R186 - DCDC3 Control */
+	0x0800,     /* R187 - DCDC3 Timeouts */
+	0x0006,     /* R188 - DCDC3 Low Power */
+	0x000E,     /* R189 - DCDC4 Control */
+	0x0800,     /* R190 - DCDC4 Timeouts */
+	0x0006,     /* R191 - DCDC4 Low Power */
+	0x0008,     /* R192 - DCDC5 Control */
+	0x0000,     /* R193 - DCDC5 Timeouts */
+	0x0000,     /* R194 */
+	0x0026,     /* R195 - DCDC6 Control */
+	0x0C00,     /* R196 - DCDC6 Timeouts */
+	0x0006,     /* R197 - DCDC6 Low Power */
+	0x0000,     /* R198 */
+	0x0003,     /* R199 - Limit Switch Control */
+	0x001A,     /* R200 - LDO1 Control */
+	0x0800,     /* R201 - LDO1 Timeouts */
+	0x001C,     /* R202 - LDO1 Low Power */
+	0x0010,     /* R203 - LDO2 Control */
+	0x0800,     /* R204 - LDO2 Timeouts */
+	0x001C,     /* R205 - LDO2 Low Power */
+	0x000A,     /* R206 - LDO3 Control */
+	0x0C00,     /* R207 - LDO3 Timeouts */
+	0x001C,     /* R208 - LDO3 Low Power */
+	0x001A,     /* R209 - LDO4 Control */
+	0x0800,     /* R210 - LDO4 Timeouts */
+	0x001C,     /* R211 - LDO4 Low Power */
+	0x0000,     /* R212 */
+	0x0000,     /* R213 */
+	0x0000,     /* R214 */
+	0x0000,     /* R215 - VCC_FAULT Masks */
+	0x001F,     /* R216 - Main Bandgap Control */
+	0x0000,     /* R217 - OSC Control */
+	0x9000,     /* R218 - RTC Tick Control */
+	0x0000,     /* R219 */
+	0x4000,     /* R220 - RAM BIST 1 */
+	0x0000,     /* R221 */
+	0x0000,     /* R222 */
+	0x0000,     /* R223 */
+	0x0000,     /* R224 */
+	0x0000,     /* R225 - DCDC/LDO status */
+	0x0000,     /* R226 */
+	0x0000,     /* R227 */
+	0x0000,     /* R228 */
+	0x0000,     /* R229 */
+	0xE000,     /* R230 - GPIO Pin Status */
+	0x0000,     /* R231 */
+	0x0000,     /* R232 */
+	0x0000,     /* R233 */
+	0x0000,     /* R234 */
+	0x0000,     /* R235 */
+	0x0000,     /* R236 */
+	0x0000,     /* R237 */
+	0x0000,     /* R238 */
+	0x0000,     /* R239 */
+	0x0000,     /* R240 */
+	0x0000,     /* R241 */
+	0x0000,     /* R242 */
+	0x0000,     /* R243 */
+	0x0000,     /* R244 */
+	0x0000,     /* R245 */
+	0x0000,     /* R246 */
+	0x0000,     /* R247 */
+	0x0000,     /* R248 */
+	0x0000,     /* R249 */
+	0x0000,     /* R250 */
+	0x0000,     /* R251 */
+	0x0000,     /* R252 */
+	0x0000,     /* R253 */
+	0x0000,     /* R254 */
+	0x0000,     /* R255 */
+};
+#endif
+
+#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_3
+
+#undef WM8350_HAVE_CONFIG_MODE
+#define WM8350_HAVE_CONFIG_MODE
+
+const u16 wm8350_mode3_defaults[] = {
+	0x17FF,     /* R0   - Reset/ID */
+	0x1000,     /* R1   - ID */
+	0x0000,     /* R2 */
+	0x1000,     /* R3   - System Control 1 */
+	0x0004,     /* R4   - System Control 2 */
+	0x0000,     /* R5   - System Hibernate */
+	0x8A00,     /* R6   - Interface Control */
+	0x0000,     /* R7 */
+	0x8000,     /* R8   - Power mgmt (1) */
+	0x0000,     /* R9   - Power mgmt (2) */
+	0x0000,     /* R10  - Power mgmt (3) */
+	0x2000,     /* R11  - Power mgmt (4) */
+	0x0E00,     /* R12  - Power mgmt (5) */
+	0x0000,     /* R13  - Power mgmt (6) */
+	0x0000,     /* R14  - Power mgmt (7) */
+	0x0000,     /* R15 */
+	0x0000,     /* R16  - RTC Seconds/Minutes */
+	0x0100,     /* R17  - RTC Hours/Day */
+	0x0101,     /* R18  - RTC Date/Month */
+	0x1400,     /* R19  - RTC Year */
+	0x0000,     /* R20  - Alarm Seconds/Minutes */
+	0x0000,     /* R21  - Alarm Hours/Day */
+	0x0000,     /* R22  - Alarm Date/Month */
+	0x0320,     /* R23  - RTC Time Control */
+	0x0000,     /* R24  - System Interrupts */
+	0x0000,     /* R25  - Interrupt Status 1 */
+	0x0000,     /* R26  - Interrupt Status 2 */
+	0x0000,     /* R27  - Power Up Interrupt Status */
+	0x0000,     /* R28  - Under Voltage Interrupt status */
+	0x0000,     /* R29  - Over Current Interrupt status */
+	0x0000,     /* R30  - GPIO Interrupt Status */
+	0x0000,     /* R31  - Comparator Interrupt Status */
+	0x3FFF,     /* R32  - System Interrupts Mask */
+	0x0000,     /* R33  - Interrupt Status 1 Mask */
+	0x0000,     /* R34  - Interrupt Status 2 Mask */
+	0x0000,     /* R35  - Power Up Interrupt Status Mask */
+	0x0000,     /* R36  - Under Voltage Interrupt status Mask */
+	0x0000,     /* R37  - Over Current Interrupt status Mask */
+	0x0000,     /* R38  - GPIO Interrupt Status Mask */
+	0x0000,     /* R39  - Comparator Interrupt Status Mask */
+	0x0040,     /* R40  - Clock Control 1 */
+	0x0000,     /* R41  - Clock Control 2 */
+	0x3B00,     /* R42  - FLL Control 1 */
+	0x7086,     /* R43  - FLL Control 2 */
+	0xC226,     /* R44  - FLL Control 3 */
+	0x0000,     /* R45  - FLL Control 4 */
+	0x0000,     /* R46 */
+	0x0000,     /* R47 */
+	0x0000,     /* R48  - DAC Control */
+	0x0000,     /* R49 */
+	0x00C0,     /* R50  - DAC Digital Volume L */
+	0x00C0,     /* R51  - DAC Digital Volume R */
+	0x0000,     /* R52 */
+	0x0040,     /* R53  - DAC LR Rate */
+	0x0000,     /* R54  - DAC Clock Control */
+	0x0000,     /* R55 */
+	0x0000,     /* R56 */
+	0x0000,     /* R57 */
+	0x4000,     /* R58  - DAC Mute */
+	0x0000,     /* R59  - DAC Mute Volume */
+	0x0000,     /* R60  - DAC Side */
+	0x0000,     /* R61 */
+	0x0000,     /* R62 */
+	0x0000,     /* R63 */
+	0x8000,     /* R64  - ADC Control */
+	0x0000,     /* R65 */
+	0x00C0,     /* R66  - ADC Digital Volume L */
+	0x00C0,     /* R67  - ADC Digital Volume R */
+	0x0000,     /* R68  - ADC Divider */
+	0x0000,     /* R69 */
+	0x0040,     /* R70  - ADC LR Rate */
+	0x0000,     /* R71 */
+	0x0303,     /* R72  - Input Control */
+	0x0000,     /* R73  - IN3 Input Control */
+	0x0000,     /* R74  - Mic Bias Control */
+	0x0000,     /* R75 */
+	0x0000,     /* R76  - Output Control */
+	0x0000,     /* R77  - Jack Detect */
+	0x0000,     /* R78  - Anti Pop Control */
+	0x0000,     /* R79 */
+	0x0040,     /* R80  - Left Input Volume */
+	0x0040,     /* R81  - Right Input Volume */
+	0x0000,     /* R82 */
+	0x0000,     /* R83 */
+	0x0000,     /* R84 */
+	0x0000,     /* R85 */
+	0x0000,     /* R86 */
+	0x0000,     /* R87 */
+	0x0800,     /* R88  - Left Mixer Control */
+	0x1000,     /* R89  - Right Mixer Control */
+	0x0000,     /* R90 */
+	0x0000,     /* R91 */
+	0x0000,     /* R92  - OUT3 Mixer Control */
+	0x0000,     /* R93  - OUT4 Mixer Control */
+	0x0000,     /* R94 */
+	0x0000,     /* R95 */
+	0x0000,     /* R96  - Output Left Mixer Volume */
+	0x0000,     /* R97  - Output Right Mixer Volume */
+	0x0000,     /* R98  - Input Mixer Volume L */
+	0x0000,     /* R99  - Input Mixer Volume R */
+	0x0000,     /* R100 - Input Mixer Volume */
+	0x0000,     /* R101 */
+	0x0000,     /* R102 */
+	0x0000,     /* R103 */
+	0x00E4,     /* R104 - LOUT1 Volume */
+	0x00E4,     /* R105 - ROUT1 Volume */
+	0x00E4,     /* R106 - LOUT2 Volume */
+	0x02E4,     /* R107 - ROUT2 Volume */
+	0x0000,     /* R108 */
+	0x0000,     /* R109 */
+	0x0000,     /* R110 */
+	0x0000,     /* R111 - BEEP Volume */
+	0x0A00,     /* R112 - AI Formating */
+	0x0000,     /* R113 - ADC DAC COMP */
+	0x0020,     /* R114 - AI ADC Control */
+	0x0020,     /* R115 - AI DAC Control */
+	0x0000,     /* R116 - AIF Test */
+	0x0000,     /* R117 */
+	0x0000,     /* R118 */
+	0x0000,     /* R119 */
+	0x0000,     /* R120 */
+	0x0000,     /* R121 */
+	0x0000,     /* R122 */
+	0x0000,     /* R123 */
+	0x0000,     /* R124 */
+	0x0000,     /* R125 */
+	0x0000,     /* R126 */
+	0x0000,     /* R127 */
+	0x1FFF,     /* R128 - GPIO Debounce */
+	0x0000,     /* R129 - GPIO Pin pull up Control */
+	0x03FC,     /* R130 - GPIO Pull down Control */
+	0x0000,     /* R131 - GPIO Interrupt Mode */
+	0x0000,     /* R132 */
+	0x0000,     /* R133 - GPIO Control */
+	0x0A7B,     /* R134 - GPIO Configuration (i/o) */
+	0x06FE,     /* R135 - GPIO Pin Polarity / Type */
+	0x0000,     /* R136 */
+	0x0000,     /* R137 */
+	0x0000,     /* R138 */
+	0x0000,     /* R139 */
+	0x1312,     /* R140 - GPIO Function Select 1 */
+	0x1030,     /* R141 - GPIO Function Select 2 */
+	0x2231,     /* R142 - GPIO Function Select 3 */
+	0x0003,     /* R143 - GPIO Function Select 4 */
+	0x0000,     /* R144 - Digitiser Control (1) */
+	0x0002,     /* R145 - Digitiser Control (2) */
+	0x0000,     /* R146 */
+	0x0000,     /* R147 */
+	0x0000,     /* R148 */
+	0x0000,     /* R149 */
+	0x0000,     /* R150 */
+	0x0000,     /* R151 */
+	0x7000,     /* R152 - AUX1 Readback */
+	0x7000,     /* R153 - AUX2 Readback */
+	0x7000,     /* R154 - AUX3 Readback */
+	0x7000,     /* R155 - AUX4 Readback */
+	0x0000,     /* R156 - USB Voltage Readback */
+	0x0000,     /* R157 - LINE Voltage Readback */
+	0x0000,     /* R158 - BATT Voltage Readback */
+	0x0000,     /* R159 - Chip Temp Readback */
+	0x0000,     /* R160 */
+	0x0000,     /* R161 */
+	0x0000,     /* R162 */
+	0x0000,     /* R163 - Generic Comparator Control */
+	0x0000,     /* R164 - Generic comparator 1 */
+	0x0000,     /* R165 - Generic comparator 2 */
+	0x0000,     /* R166 - Generic comparator 3 */
+	0x0000,     /* R167 - Generic comparator 4 */
+	0xA00F,     /* R168 - Battery Charger Control 1 */
+	0x0B06,     /* R169 - Battery Charger Control 2 */
+	0x0000,     /* R170 - Battery Charger Control 3 */
+	0x0000,     /* R171 */
+	0x0000,     /* R172 - Current Sink Driver A */
+	0x0000,     /* R173 - CSA Flash control */
+	0x0000,     /* R174 - Current Sink Driver B */
+	0x0000,     /* R175 - CSB Flash control */
+	0x0000,     /* R176 - DCDC/LDO requested */
+	0x002D,     /* R177 - DCDC Active options */
+	0x0000,     /* R178 - DCDC Sleep options */
+	0x0025,     /* R179 - Power-check comparator */
+	0x000E,     /* R180 - DCDC1 Control */
+	0x0400,     /* R181 - DCDC1 Timeouts */
+	0x1006,     /* R182 - DCDC1 Low Power */
+	0x0018,     /* R183 - DCDC2 Control */
+	0x0000,     /* R184 - DCDC2 Timeouts */
+	0x0000,     /* R185 */
+	0x000E,     /* R186 - DCDC3 Control */
+	0x0400,     /* R187 - DCDC3 Timeouts */
+	0x0006,     /* R188 - DCDC3 Low Power */
+	0x0026,     /* R189 - DCDC4 Control */
+	0x0400,     /* R190 - DCDC4 Timeouts */
+	0x0006,     /* R191 - DCDC4 Low Power */
+	0x0008,     /* R192 - DCDC5 Control */
+	0x0000,     /* R193 - DCDC5 Timeouts */
+	0x0000,     /* R194 */
+	0x0026,     /* R195 - DCDC6 Control */
+	0x0400,     /* R196 - DCDC6 Timeouts */
+	0x0006,     /* R197 - DCDC6 Low Power */
+	0x0000,     /* R198 */
+	0x0003,     /* R199 - Limit Switch Control */
+	0x001C,     /* R200 - LDO1 Control */
+	0x0000,     /* R201 - LDO1 Timeouts */
+	0x001C,     /* R202 - LDO1 Low Power */
+	0x001C,     /* R203 - LDO2 Control */
+	0x0400,     /* R204 - LDO2 Timeouts */
+	0x001C,     /* R205 - LDO2 Low Power */
+	0x001C,     /* R206 - LDO3 Control */
+	0x0400,     /* R207 - LDO3 Timeouts */
+	0x001C,     /* R208 - LDO3 Low Power */
+	0x001F,     /* R209 - LDO4 Control */
+	0x0400,     /* R210 - LDO4 Timeouts */
+	0x001C,     /* R211 - LDO4 Low Power */
+	0x0000,     /* R212 */
+	0x0000,     /* R213 */
+	0x0000,     /* R214 */
+	0x0000,     /* R215 - VCC_FAULT Masks */
+	0x001F,     /* R216 - Main Bandgap Control */
+	0x0000,     /* R217 - OSC Control */
+	0x9000,     /* R218 - RTC Tick Control */
+	0x0000,     /* R219 */
+	0x4000,     /* R220 - RAM BIST 1 */
+	0x0000,     /* R221 */
+	0x0000,     /* R222 */
+	0x0000,     /* R223 */
+	0x0000,     /* R224 */
+	0x0000,     /* R225 - DCDC/LDO status */
+	0x0000,     /* R226 */
+	0x0000,     /* R227 */
+	0x0000,     /* R228 */
+	0x0000,     /* R229 */
+	0xE000,     /* R230 - GPIO Pin Status */
+	0x0000,     /* R231 */
+	0x0000,     /* R232 */
+	0x0000,     /* R233 */
+	0x0000,     /* R234 */
+	0x0000,     /* R235 */
+	0x0000,     /* R236 */
+	0x0000,     /* R237 */
+	0x0000,     /* R238 */
+	0x0000,     /* R239 */
+	0x0000,     /* R240 */
+	0x0000,     /* R241 */
+	0x0000,     /* R242 */
+	0x0000,     /* R243 */
+	0x0000,     /* R244 */
+	0x0000,     /* R245 */
+	0x0000,     /* R246 */
+	0x0000,     /* R247 */
+	0x0000,     /* R248 */
+	0x0000,     /* R249 */
+	0x0000,     /* R250 */
+	0x0000,     /* R251 */
+	0x0000,     /* R252 */
+	0x0000,     /* R253 */
+	0x0000,     /* R254 */
+	0x0000,     /* R255 */
+};
+#endif
+
+#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_0
+
+#undef WM8350_HAVE_CONFIG_MODE
+#define WM8350_HAVE_CONFIG_MODE
+
+const u16 wm8351_mode0_defaults[] = {
+	0x6143,     /* R0   - Reset/ID */
+	0x0000,     /* R1   - ID */
+	0x0001,     /* R2   - Revision */
+	0x1C02,     /* R3   - System Control 1 */
+	0x0004,     /* R4   - System Control 2 */
+	0x0000,     /* R5   - System Hibernate */
+	0x8A00,     /* R6   - Interface Control */
+	0x0000,     /* R7 */
+	0x8000,     /* R8   - Power mgmt (1) */
+	0x0000,     /* R9   - Power mgmt (2) */
+	0x0000,     /* R10  - Power mgmt (3) */
+	0x2000,     /* R11  - Power mgmt (4) */
+	0x0E00,     /* R12  - Power mgmt (5) */
+	0x0000,     /* R13  - Power mgmt (6) */
+	0x0000,     /* R14  - Power mgmt (7) */
+	0x0000,     /* R15 */
+	0x0000,     /* R16  - RTC Seconds/Minutes */
+	0x0100,     /* R17  - RTC Hours/Day */
+	0x0101,     /* R18  - RTC Date/Month */
+	0x1400,     /* R19  - RTC Year */
+	0x0000,     /* R20  - Alarm Seconds/Minutes */
+	0x0000,     /* R21  - Alarm Hours/Day */
+	0x0000,     /* R22  - Alarm Date/Month */
+	0x0320,     /* R23  - RTC Time Control */
+	0x0000,     /* R24  - System Interrupts */
+	0x0000,     /* R25  - Interrupt Status 1 */
+	0x0000,     /* R26  - Interrupt Status 2 */
+	0x0000,     /* R27 */
+	0x0000,     /* R28  - Under Voltage Interrupt status */
+	0x0000,     /* R29  - Over Current Interrupt status */
+	0x0000,     /* R30  - GPIO Interrupt Status */
+	0x0000,     /* R31  - Comparator Interrupt Status */
+	0x3FFF,     /* R32  - System Interrupts Mask */
+	0x0000,     /* R33  - Interrupt Status 1 Mask */
+	0x0000,     /* R34  - Interrupt Status 2 Mask */
+	0x0000,     /* R35 */
+	0x0000,     /* R36  - Under Voltage Interrupt status Mask */
+	0x0000,     /* R37  - Over Current Interrupt status Mask */
+	0x0000,     /* R38  - GPIO Interrupt Status Mask */
+	0x0000,     /* R39  - Comparator Interrupt Status Mask */
+	0x0040,     /* R40  - Clock Control 1 */
+	0x0000,     /* R41  - Clock Control 2 */
+	0x3A00,     /* R42  - FLL Control 1 */
+	0x7086,     /* R43  - FLL Control 2 */
+	0xC226,     /* R44  - FLL Control 3 */
+	0x0000,     /* R45  - FLL Control 4 */
+	0x0000,     /* R46 */
+	0x0000,     /* R47 */
+	0x0000,     /* R48  - DAC Control */
+	0x0000,     /* R49 */
+	0x00C0,     /* R50  - DAC Digital Volume L */
+	0x00C0,     /* R51  - DAC Digital Volume R */
+	0x0000,     /* R52 */
+	0x0040,     /* R53  - DAC LR Rate */
+	0x0000,     /* R54  - DAC Clock Control */
+	0x0000,     /* R55 */
+	0x0000,     /* R56 */
+	0x0000,     /* R57 */
+	0x4000,     /* R58  - DAC Mute */
+	0x0000,     /* R59  - DAC Mute Volume */
+	0x0000,     /* R60  - DAC Side */
+	0x0000,     /* R61 */
+	0x0000,     /* R62 */
+	0x0000,     /* R63 */
+	0x8000,     /* R64  - ADC Control */
+	0x0000,     /* R65 */
+	0x00C0,     /* R66  - ADC Digital Volume L */
+	0x00C0,     /* R67  - ADC Digital Volume R */
+	0x0000,     /* R68  - ADC Divider */
+	0x0000,     /* R69 */
+	0x0040,     /* R70  - ADC LR Rate */
+	0x0000,     /* R71 */
+	0x0303,     /* R72  - Input Control */
+	0x0000,     /* R73  - IN3 Input Control */
+	0x0000,     /* R74  - Mic Bias Control */
+	0x0000,     /* R75 */
+	0x0000,     /* R76  - Output Control */
+	0x0000,     /* R77  - Jack Detect */
+	0x0000,     /* R78  - Anti Pop Control */
+	0x0000,     /* R79 */
+	0x0040,     /* R80  - Left Input Volume */
+	0x0040,     /* R81  - Right Input Volume */
+	0x0000,     /* R82 */
+	0x0000,     /* R83 */
+	0x0000,     /* R84 */
+	0x0000,     /* R85 */
+	0x0000,     /* R86 */
+	0x0000,     /* R87 */
+	0x0800,     /* R88  - Left Mixer Control */
+	0x1000,     /* R89  - Right Mixer Control */
+	0x0000,     /* R90 */
+	0x0000,     /* R91 */
+	0x0000,     /* R92  - OUT3 Mixer Control */
+	0x0000,     /* R93  - OUT4 Mixer Control */
+	0x0000,     /* R94 */
+	0x0000,     /* R95 */
+	0x0000,     /* R96  - Output Left Mixer Volume */
+	0x0000,     /* R97  - Output Right Mixer Volume */
+	0x0000,     /* R98  - Input Mixer Volume L */
+	0x0000,     /* R99  - Input Mixer Volume R */
+	0x0000,     /* R100 - Input Mixer Volume */
+	0x0000,     /* R101 */
+	0x0000,     /* R102 */
+	0x0000,     /* R103 */
+	0x00E4,     /* R104 - OUT1L Volume */
+	0x00E4,     /* R105 - OUT1R Volume */
+	0x00E4,     /* R106 - OUT2L Volume */
+	0x02E4,     /* R107 - OUT2R Volume */
+	0x0000,     /* R108 */
+	0x0000,     /* R109 */
+	0x0000,     /* R110 */
+	0x0000,     /* R111 - BEEP Volume */
+	0x0A00,     /* R112 - AI Formating */
+	0x0000,     /* R113 - ADC DAC COMP */
+	0x0020,     /* R114 - AI ADC Control */
+	0x0020,     /* R115 - AI DAC Control */
+	0x0000,     /* R116 */
+	0x0000,     /* R117 */
+	0x0000,     /* R118 */
+	0x0000,     /* R119 */
+	0x0000,     /* R120 */
+	0x0000,     /* R121 */
+	0x0000,     /* R122 */
+	0x0000,     /* R123 */
+	0x0000,     /* R124 */
+	0x0000,     /* R125 */
+	0x0000,     /* R126 */
+	0x0000,     /* R127 */
+	0x1FFF,     /* R128 - GPIO Debounce */
+	0x0000,     /* R129 - GPIO Pin pull up Control */
+	0x0000,     /* R130 - GPIO Pull down Control */
+	0x0000,     /* R131 - GPIO Interrupt Mode */
+	0x0000,     /* R132 */
+	0x0000,     /* R133 - GPIO Control */
+	0x0FFC,     /* R134 - GPIO Configuration (i/o) */
+	0x0FFC,     /* R135 - GPIO Pin Polarity / Type */
+	0x0000,     /* R136 */
+	0x0000,     /* R137 */
+	0x0000,     /* R138 */
+	0x0000,     /* R139 */
+	0x0013,     /* R140 - GPIO Function Select 1 */
+	0x0000,     /* R141 - GPIO Function Select 2 */
+	0x0000,     /* R142 - GPIO Function Select 3 */
+	0x0003,     /* R143 - GPIO Function Select 4 */
+	0x0000,     /* R144 - Digitiser Control (1) */
+	0x0002,     /* R145 - Digitiser Control (2) */
+	0x0000,     /* R146 */
+	0x0000,     /* R147 */
+	0x0000,     /* R148 */
+	0x0000,     /* R149 */
+	0x0000,     /* R150 */
+	0x0000,     /* R151 */
+	0x7000,     /* R152 - AUX1 Readback */
+	0x7000,     /* R153 - AUX2 Readback */
+	0x7000,     /* R154 - AUX3 Readback */
+	0x7000,     /* R155 - AUX4 Readback */
+	0x0000,     /* R156 - USB Voltage Readback */
+	0x0000,     /* R157 - LINE Voltage Readback */
+	0x0000,     /* R158 - BATT Voltage Readback */
+	0x0000,     /* R159 - Chip Temp Readback */
+	0x0000,     /* R160 */
+	0x0000,     /* R161 */
+	0x0000,     /* R162 */
+	0x0000,     /* R163 - Generic Comparator Control */
+	0x0000,     /* R164 - Generic comparator 1 */
+	0x0000,     /* R165 - Generic comparator 2 */
+	0x0000,     /* R166 - Generic comparator 3 */
+	0x0000,     /* R167 - Generic comparator 4 */
+	0xA00F,     /* R168 - Battery Charger Control 1 */
+	0x0B06,     /* R169 - Battery Charger Control 2 */
+	0x0000,     /* R170 - Battery Charger Control 3 */
+	0x0000,     /* R171 */
+	0x0000,     /* R172 - Current Sink Driver A */
+	0x0000,     /* R173 - CSA Flash control */
+	0x0000,     /* R174 */
+	0x0000,     /* R175 */
+	0x0000,     /* R176 - DCDC/LDO requested */
+	0x032D,     /* R177 - DCDC Active options */
+	0x0000,     /* R178 - DCDC Sleep options */
+	0x0025,     /* R179 - Power-check comparator */
+	0x000E,     /* R180 - DCDC1 Control */
+	0x0000,     /* R181 - DCDC1 Timeouts */
+	0x1006,     /* R182 - DCDC1 Low Power */
+	0x0018,     /* R183 - DCDC2 Control */
+	0x0000,     /* R184 - DCDC2 Timeouts */
+	0x0000,     /* R185 */
+	0x0000,     /* R186 - DCDC3 Control */
+	0x0000,     /* R187 - DCDC3 Timeouts */
+	0x0006,     /* R188 - DCDC3 Low Power */
+	0x0000,     /* R189 - DCDC4 Control */
+	0x0000,     /* R190 - DCDC4 Timeouts */
+	0x0006,     /* R191 - DCDC4 Low Power */
+	0x0008,     /* R192 */
+	0x0000,     /* R193 */
+	0x0000,     /* R194 */
+	0x0000,     /* R195 */
+	0x0000,     /* R196 */
+	0x0006,     /* R197 */
+	0x0000,     /* R198 */
+	0x0003,     /* R199 - Limit Switch Control */
+	0x001C,     /* R200 - LDO1 Control */
+	0x0000,     /* R201 - LDO1 Timeouts */
+	0x001C,     /* R202 - LDO1 Low Power */
+	0x001B,     /* R203 - LDO2 Control */
+	0x0000,     /* R204 - LDO2 Timeouts */
+	0x001C,     /* R205 - LDO2 Low Power */
+	0x001B,     /* R206 - LDO3 Control */
+	0x0000,     /* R207 - LDO3 Timeouts */
+	0x001C,     /* R208 - LDO3 Low Power */
+	0x001B,     /* R209 - LDO4 Control */
+	0x0000,     /* R210 - LDO4 Timeouts */
+	0x001C,     /* R211 - LDO4 Low Power */
+	0x0000,     /* R212 */
+	0x0000,     /* R213 */
+	0x0000,     /* R214 */
+	0x0000,     /* R215 - VCC_FAULT Masks */
+	0x001F,     /* R216 - Main Bandgap Control */
+	0x0000,     /* R217 - OSC Control */
+	0x9000,     /* R218 - RTC Tick Control */
+	0x0000,     /* R219 - Security1 */
+	0x4000,     /* R220 */
+	0x0000,     /* R221 */
+	0x0000,     /* R222 */
+	0x0000,     /* R223 */
+	0x0000,     /* R224 - Signal overrides */
+	0x0000,     /* R225 - DCDC/LDO status */
+	0x0000,     /* R226 - Charger Overides/status */
+	0x0000,     /* R227 - misc overrides */
+	0x0000,     /* R228 - Supply overrides/status 1 */
+	0x0000,     /* R229 - Supply overrides/status 2 */
+	0xE000,     /* R230 - GPIO Pin Status */
+	0x0000,     /* R231 - comparotor overrides */
+	0x0000,     /* R232 */
+	0x0000,     /* R233 - State Machine status */
+	0x1200,     /* R234 - FLL Test 1 */
+	0x0000,     /* R235 */
+	0x8000,     /* R236 */
+	0x0000,     /* R237 */
+	0x0000,     /* R238 */
+	0x0000,     /* R239 */
+	0x0003,     /* R240 */
+	0x0000,     /* R241 */
+	0x0000,     /* R242 */
+	0x0004,     /* R243 */
+	0x0300,     /* R244 */
+	0x0000,     /* R245 */
+	0x0200,     /* R246 */
+	0x0000,     /* R247 */
+	0x1000,     /* R248 - DCDC1 Test Controls */
+	0x1000,     /* R249 */
+	0x1000,     /* R250 - DCDC3 Test Controls */
+	0x1000,     /* R251 - DCDC4 Test Controls */
+};
+#endif
+
+#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_1
+
+#undef WM8350_HAVE_CONFIG_MODE
+#define WM8350_HAVE_CONFIG_MODE
+
+const u16 wm8351_mode1_defaults[] = {
+	0x6143,     /* R0   - Reset/ID */
+	0x0000,     /* R1   - ID */
+	0x0001,     /* R2   - Revision */
+	0x1C02,     /* R3   - System Control 1 */
+	0x0204,     /* R4   - System Control 2 */
+	0x0000,     /* R5   - System Hibernate */
+	0x8A00,     /* R6   - Interface Control */
+	0x0000,     /* R7 */
+	0x8000,     /* R8   - Power mgmt (1) */
+	0x0000,     /* R9   - Power mgmt (2) */
+	0x0000,     /* R10  - Power mgmt (3) */
+	0x2000,     /* R11  - Power mgmt (4) */
+	0x0E00,     /* R12  - Power mgmt (5) */
+	0x0000,     /* R13  - Power mgmt (6) */
+	0x0000,     /* R14  - Power mgmt (7) */
+	0x0000,     /* R15 */
+	0x0000,     /* R16  - RTC Seconds/Minutes */
+	0x0100,     /* R17  - RTC Hours/Day */
+	0x0101,     /* R18  - RTC Date/Month */
+	0x1400,     /* R19  - RTC Year */
+	0x0000,     /* R20  - Alarm Seconds/Minutes */
+	0x0000,     /* R21  - Alarm Hours/Day */
+	0x0000,     /* R22  - Alarm Date/Month */
+	0x0320,     /* R23  - RTC Time Control */
+	0x0000,     /* R24  - System Interrupts */
+	0x0000,     /* R25  - Interrupt Status 1 */
+	0x0000,     /* R26  - Interrupt Status 2 */
+	0x0000,     /* R27 */
+	0x0000,     /* R28  - Under Voltage Interrupt status */
+	0x0000,     /* R29  - Over Current Interrupt status */
+	0x0000,     /* R30  - GPIO Interrupt Status */
+	0x0000,     /* R31  - Comparator Interrupt Status */
+	0x3FFF,     /* R32  - System Interrupts Mask */
+	0x0000,     /* R33  - Interrupt Status 1 Mask */
+	0x0000,     /* R34  - Interrupt Status 2 Mask */
+	0x0000,     /* R35 */
+	0x0000,     /* R36  - Under Voltage Interrupt status Mask */
+	0x0000,     /* R37  - Over Current Interrupt status Mask */
+	0x0000,     /* R38  - GPIO Interrupt Status Mask */
+	0x0000,     /* R39  - Comparator Interrupt Status Mask */
+	0x0040,     /* R40  - Clock Control 1 */
+	0x0000,     /* R41  - Clock Control 2 */
+	0x3A00,     /* R42  - FLL Control 1 */
+	0x7086,     /* R43  - FLL Control 2 */
+	0xC226,     /* R44  - FLL Control 3 */
+	0x0000,     /* R45  - FLL Control 4 */
+	0x0000,     /* R46 */
+	0x0000,     /* R47 */
+	0x0000,     /* R48  - DAC Control */
+	0x0000,     /* R49 */
+	0x00C0,     /* R50  - DAC Digital Volume L */
+	0x00C0,     /* R51  - DAC Digital Volume R */
+	0x0000,     /* R52 */
+	0x0040,     /* R53  - DAC LR Rate */
+	0x0000,     /* R54  - DAC Clock Control */
+	0x0000,     /* R55 */
+	0x0000,     /* R56 */
+	0x0000,     /* R57 */
+	0x4000,     /* R58  - DAC Mute */
+	0x0000,     /* R59  - DAC Mute Volume */
+	0x0000,     /* R60  - DAC Side */
+	0x0000,     /* R61 */
+	0x0000,     /* R62 */
+	0x0000,     /* R63 */
+	0x8000,     /* R64  - ADC Control */
+	0x0000,     /* R65 */
+	0x00C0,     /* R66  - ADC Digital Volume L */
+	0x00C0,     /* R67  - ADC Digital Volume R */
+	0x0000,     /* R68  - ADC Divider */
+	0x0000,     /* R69 */
+	0x0040,     /* R70  - ADC LR Rate */
+	0x0000,     /* R71 */
+	0x0303,     /* R72  - Input Control */
+	0x0000,     /* R73  - IN3 Input Control */
+	0x0000,     /* R74  - Mic Bias Control */
+	0x0000,     /* R75 */
+	0x0000,     /* R76  - Output Control */
+	0x0000,     /* R77  - Jack Detect */
+	0x0000,     /* R78  - Anti Pop Control */
+	0x0000,     /* R79 */
+	0x0040,     /* R80  - Left Input Volume */
+	0x0040,     /* R81  - Right Input Volume */
+	0x0000,     /* R82 */
+	0x0000,     /* R83 */
+	0x0000,     /* R84 */
+	0x0000,     /* R85 */
+	0x0000,     /* R86 */
+	0x0000,     /* R87 */
+	0x0800,     /* R88  - Left Mixer Control */
+	0x1000,     /* R89  - Right Mixer Control */
+	0x0000,     /* R90 */
+	0x0000,     /* R91 */
+	0x0000,     /* R92  - OUT3 Mixer Control */
+	0x0000,     /* R93  - OUT4 Mixer Control */
+	0x0000,     /* R94 */
+	0x0000,     /* R95 */
+	0x0000,     /* R96  - Output Left Mixer Volume */
+	0x0000,     /* R97  - Output Right Mixer Volume */
+	0x0000,     /* R98  - Input Mixer Volume L */
+	0x0000,     /* R99  - Input Mixer Volume R */
+	0x0000,     /* R100 - Input Mixer Volume */
+	0x0000,     /* R101 */
+	0x0000,     /* R102 */
+	0x0000,     /* R103 */
+	0x00E4,     /* R104 - OUT1L Volume */
+	0x00E4,     /* R105 - OUT1R Volume */
+	0x00E4,     /* R106 - OUT2L Volume */
+	0x02E4,     /* R107 - OUT2R Volume */
+	0x0000,     /* R108 */
+	0x0000,     /* R109 */
+	0x0000,     /* R110 */
+	0x0000,     /* R111 - BEEP Volume */
+	0x0A00,     /* R112 - AI Formating */
+	0x0000,     /* R113 - ADC DAC COMP */
+	0x0020,     /* R114 - AI ADC Control */
+	0x0020,     /* R115 - AI DAC Control */
+	0x0000,     /* R116 */
+	0x0000,     /* R117 */
+	0x0000,     /* R118 */
+	0x0000,     /* R119 */
+	0x0000,     /* R120 */
+	0x0000,     /* R121 */
+	0x0000,     /* R122 */
+	0x0000,     /* R123 */
+	0x0000,     /* R124 */
+	0x0000,     /* R125 */
+	0x0000,     /* R126 */
+	0x0000,     /* R127 */
+	0x1FFF,     /* R128 - GPIO Debounce */
+	0x0000,     /* R129 - GPIO Pin pull up Control */
+	0x0000,     /* R130 - GPIO Pull down Control */
+	0x0000,     /* R131 - GPIO Interrupt Mode */
+	0x0000,     /* R132 */
+	0x0000,     /* R133 - GPIO Control */
+	0x0CFB,     /* R134 - GPIO Configuration (i/o) */
+	0x0C1F,     /* R135 - GPIO Pin Polarity / Type */
+	0x0000,     /* R136 */
+	0x0000,     /* R137 */
+	0x0000,     /* R138 */
+	0x0000,     /* R139 */
+	0x0300,     /* R140 - GPIO Function Select 1 */
+	0x1110,     /* R141 - GPIO Function Select 2 */
+	0x0013,     /* R142 - GPIO Function Select 3 */
+	0x0003,     /* R143 - GPIO Function Select 4 */
+	0x0000,     /* R144 - Digitiser Control (1) */
+	0x0002,     /* R145 - Digitiser Control (2) */
+	0x0000,     /* R146 */
+	0x0000,     /* R147 */
+	0x0000,     /* R148 */
+	0x0000,     /* R149 */
+	0x0000,     /* R150 */
+	0x0000,     /* R151 */
+	0x7000,     /* R152 - AUX1 Readback */
+	0x7000,     /* R153 - AUX2 Readback */
+	0x7000,     /* R154 - AUX3 Readback */
+	0x7000,     /* R155 - AUX4 Readback */
+	0x0000,     /* R156 - USB Voltage Readback */
+	0x0000,     /* R157 - LINE Voltage Readback */
+	0x0000,     /* R158 - BATT Voltage Readback */
+	0x0000,     /* R159 - Chip Temp Readback */
+	0x0000,     /* R160 */
+	0x0000,     /* R161 */
+	0x0000,     /* R162 */
+	0x0000,     /* R163 - Generic Comparator Control */
+	0x0000,     /* R164 - Generic comparator 1 */
+	0x0000,     /* R165 - Generic comparator 2 */
+	0x0000,     /* R166 - Generic comparator 3 */
+	0x0000,     /* R167 - Generic comparator 4 */
+	0xA00F,     /* R168 - Battery Charger Control 1 */
+	0x0B06,     /* R169 - Battery Charger Control 2 */
+	0x0000,     /* R170 - Battery Charger Control 3 */
+	0x0000,     /* R171 */
+	0x0000,     /* R172 - Current Sink Driver A */
+	0x0000,     /* R173 - CSA Flash control */
+	0x0000,     /* R174 */
+	0x0000,     /* R175 */
+	0x0000,     /* R176 - DCDC/LDO requested */
+	0x032D,     /* R177 - DCDC Active options */
+	0x0000,     /* R178 - DCDC Sleep options */
+	0x0025,     /* R179 - Power-check comparator */
+	0x000E,     /* R180 - DCDC1 Control */
+	0x0C00,     /* R181 - DCDC1 Timeouts */
+	0x1006,     /* R182 - DCDC1 Low Power */
+	0x0018,     /* R183 - DCDC2 Control */
+	0x0000,     /* R184 - DCDC2 Timeouts */
+	0x0000,     /* R185 */
+	0x0026,     /* R186 - DCDC3 Control */
+	0x0400,     /* R187 - DCDC3 Timeouts */
+	0x0006,     /* R188 - DCDC3 Low Power */
+	0x0062,     /* R189 - DCDC4 Control */
+	0x0800,     /* R190 - DCDC4 Timeouts */
+	0x0006,     /* R191 - DCDC4 Low Power */
+	0x0008,     /* R192 */
+	0x0000,     /* R193 */
+	0x0000,     /* R194 */
+	0x000A,     /* R195 */
+	0x1000,     /* R196 */
+	0x0006,     /* R197 */
+	0x0000,     /* R198 */
+	0x0003,     /* R199 - Limit Switch Control */
+	0x0006,     /* R200 - LDO1 Control */
+	0x0000,     /* R201 - LDO1 Timeouts */
+	0x001C,     /* R202 - LDO1 Low Power */
+	0x0010,     /* R203 - LDO2 Control */
+	0x0C00,     /* R204 - LDO2 Timeouts */
+	0x001C,     /* R205 - LDO2 Low Power */
+	0x001F,     /* R206 - LDO3 Control */
+	0x0800,     /* R207 - LDO3 Timeouts */
+	0x001C,     /* R208 - LDO3 Low Power */
+	0x000A,     /* R209 - LDO4 Control */
+	0x0800,     /* R210 - LDO4 Timeouts */
+	0x001C,     /* R211 - LDO4 Low Power */
+	0x0000,     /* R212 */
+	0x0000,     /* R213 */
+	0x0000,     /* R214 */
+	0x0000,     /* R215 - VCC_FAULT Masks */
+	0x001F,     /* R216 - Main Bandgap Control */
+	0x0000,     /* R217 - OSC Control */
+	0x9000,     /* R218 - RTC Tick Control */
+	0x0000,     /* R219 - Security1 */
+	0x4000,     /* R220 */
+	0x0000,     /* R221 */
+	0x0000,     /* R222 */
+	0x0000,     /* R223 */
+	0x0000,     /* R224 - Signal overrides */
+	0x0000,     /* R225 - DCDC/LDO status */
+	0x0000,     /* R226 - Charger Overides/status */
+	0x0000,     /* R227 - misc overrides */
+	0x0000,     /* R228 - Supply overrides/status 1 */
+	0x0000,     /* R229 - Supply overrides/status 2 */
+	0xE000,     /* R230 - GPIO Pin Status */
+	0x0000,     /* R231 - comparotor overrides */
+	0x0000,     /* R232 */
+	0x0000,     /* R233 - State Machine status */
+	0x1200,     /* R234 - FLL Test 1 */
+	0x0000,     /* R235 */
+	0x8000,     /* R236 */
+	0x0000,     /* R237 */
+	0x0000,     /* R238 */
+	0x0000,     /* R239 */
+	0x0003,     /* R240 */
+	0x0000,     /* R241 */
+	0x0000,     /* R242 */
+	0x0004,     /* R243 */
+	0x0300,     /* R244 */
+	0x0000,     /* R245 */
+	0x0200,     /* R246 */
+	0x1000,     /* R247 */
+	0x1000,     /* R248 - DCDC1 Test Controls */
+	0x1000,     /* R249 */
+	0x1000,     /* R250 - DCDC3 Test Controls */
+	0x1000,     /* R251 - DCDC4 Test Controls */
+};
+#endif
+
+#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_2
+
+#undef WM8350_HAVE_CONFIG_MODE
+#define WM8350_HAVE_CONFIG_MODE
+
+const u16 wm8351_mode2_defaults[] = {
+	0x6143,     /* R0   - Reset/ID */
+	0x0000,     /* R1   - ID */
+	0x0001,     /* R2   - Revision */
+	0x1C02,     /* R3   - System Control 1 */
+	0x0214,     /* R4   - System Control 2 */
+	0x0000,     /* R5   - System Hibernate */
+	0x8A00,     /* R6   - Interface Control */
+	0x0000,     /* R7 */
+	0x8000,     /* R8   - Power mgmt (1) */
+	0x0000,     /* R9   - Power mgmt (2) */
+	0x0000,     /* R10  - Power mgmt (3) */
+	0x2000,     /* R11  - Power mgmt (4) */
+	0x0E00,     /* R12  - Power mgmt (5) */
+	0x0000,     /* R13  - Power mgmt (6) */
+	0x0000,     /* R14  - Power mgmt (7) */
+	0x0000,     /* R15 */
+	0x0000,     /* R16  - RTC Seconds/Minutes */
+	0x0100,     /* R17  - RTC Hours/Day */
+	0x0101,     /* R18  - RTC Date/Month */
+	0x1400,     /* R19  - RTC Year */
+	0x0000,     /* R20  - Alarm Seconds/Minutes */
+	0x0000,     /* R21  - Alarm Hours/Day */
+	0x0000,     /* R22  - Alarm Date/Month */
+	0x0320,     /* R23  - RTC Time Control */
+	0x0000,     /* R24  - System Interrupts */
+	0x0000,     /* R25  - Interrupt Status 1 */
+	0x0000,     /* R26  - Interrupt Status 2 */
+	0x0000,     /* R27 */
+	0x0000,     /* R28  - Under Voltage Interrupt status */
+	0x0000,     /* R29  - Over Current Interrupt status */
+	0x0000,     /* R30  - GPIO Interrupt Status */
+	0x0000,     /* R31  - Comparator Interrupt Status */
+	0x3FFF,     /* R32  - System Interrupts Mask */
+	0x0000,     /* R33  - Interrupt Status 1 Mask */
+	0x0000,     /* R34  - Interrupt Status 2 Mask */
+	0x0000,     /* R35 */
+	0x0000,     /* R36  - Under Voltage Interrupt status Mask */
+	0x0000,     /* R37  - Over Current Interrupt status Mask */
+	0x0000,     /* R38  - GPIO Interrupt Status Mask */
+	0x0000,     /* R39  - Comparator Interrupt Status Mask */
+	0x0040,     /* R40  - Clock Control 1 */
+	0x0000,     /* R41  - Clock Control 2 */
+	0x3A00,     /* R42  - FLL Control 1 */
+	0x7086,     /* R43  - FLL Control 2 */
+	0xC226,     /* R44  - FLL Control 3 */
+	0x0000,     /* R45  - FLL Control 4 */
+	0x0000,     /* R46 */
+	0x0000,     /* R47 */
+	0x0000,     /* R48  - DAC Control */
+	0x0000,     /* R49 */
+	0x00C0,     /* R50  - DAC Digital Volume L */
+	0x00C0,     /* R51  - DAC Digital Volume R */
+	0x0000,     /* R52 */
+	0x0040,     /* R53  - DAC LR Rate */
+	0x0000,     /* R54  - DAC Clock Control */
+	0x0000,     /* R55 */
+	0x0000,     /* R56 */
+	0x0000,     /* R57 */
+	0x4000,     /* R58  - DAC Mute */
+	0x0000,     /* R59  - DAC Mute Volume */
+	0x0000,     /* R60  - DAC Side */
+	0x0000,     /* R61 */
+	0x0000,     /* R62 */
+	0x0000,     /* R63 */
+	0x8000,     /* R64  - ADC Control */
+	0x0000,     /* R65 */
+	0x00C0,     /* R66  - ADC Digital Volume L */
+	0x00C0,     /* R67  - ADC Digital Volume R */
+	0x0000,     /* R68  - ADC Divider */
+	0x0000,     /* R69 */
+	0x0040,     /* R70  - ADC LR Rate */
+	0x0000,     /* R71 */
+	0x0303,     /* R72  - Input Control */
+	0x0000,     /* R73  - IN3 Input Control */
+	0x0000,     /* R74  - Mic Bias Control */
+	0x0000,     /* R75 */
+	0x0000,     /* R76  - Output Control */
+	0x0000,     /* R77  - Jack Detect */
+	0x0000,     /* R78  - Anti Pop Control */
+	0x0000,     /* R79 */
+	0x0040,     /* R80  - Left Input Volume */
+	0x0040,     /* R81  - Right Input Volume */
+	0x0000,     /* R82 */
+	0x0000,     /* R83 */
+	0x0000,     /* R84 */
+	0x0000,     /* R85 */
+	0x0000,     /* R86 */
+	0x0000,     /* R87 */
+	0x0800,     /* R88  - Left Mixer Control */
+	0x1000,     /* R89  - Right Mixer Control */
+	0x0000,     /* R90 */
+	0x0000,     /* R91 */
+	0x0000,     /* R92  - OUT3 Mixer Control */
+	0x0000,     /* R93  - OUT4 Mixer Control */
+	0x0000,     /* R94 */
+	0x0000,     /* R95 */
+	0x0000,     /* R96  - Output Left Mixer Volume */
+	0x0000,     /* R97  - Output Right Mixer Volume */
+	0x0000,     /* R98  - Input Mixer Volume L */
+	0x0000,     /* R99  - Input Mixer Volume R */
+	0x0000,     /* R100 - Input Mixer Volume */
+	0x0000,     /* R101 */
+	0x0000,     /* R102 */
+	0x0000,     /* R103 */
+	0x00E4,     /* R104 - OUT1L Volume */
+	0x00E4,     /* R105 - OUT1R Volume */
+	0x00E4,     /* R106 - OUT2L Volume */
+	0x02E4,     /* R107 - OUT2R Volume */
+	0x0000,     /* R108 */
+	0x0000,     /* R109 */
+	0x0000,     /* R110 */
+	0x0000,     /* R111 - BEEP Volume */
+	0x0A00,     /* R112 - AI Formating */
+	0x0000,     /* R113 - ADC DAC COMP */
+	0x0020,     /* R114 - AI ADC Control */
+	0x0020,     /* R115 - AI DAC Control */
+	0x0000,     /* R116 */
+	0x0000,     /* R117 */
+	0x0000,     /* R118 */
+	0x0000,     /* R119 */
+	0x0000,     /* R120 */
+	0x0000,     /* R121 */
+	0x0000,     /* R122 */
+	0x0000,     /* R123 */
+	0x0000,     /* R124 */
+	0x0000,     /* R125 */
+	0x0000,     /* R126 */
+	0x0000,     /* R127 */
+	0x1FFF,     /* R128 - GPIO Debounce */
+	0x0000,     /* R129 - GPIO Pin pull up Control */
+	0x0110,     /* R130 - GPIO Pull down Control */
+	0x0000,     /* R131 - GPIO Interrupt Mode */
+	0x0000,     /* R132 */
+	0x0000,     /* R133 - GPIO Control */
+	0x09FA,     /* R134 - GPIO Configuration (i/o) */
+	0x0DF6,     /* R135 - GPIO Pin Polarity / Type */
+	0x0000,     /* R136 */
+	0x0000,     /* R137 */
+	0x0000,     /* R138 */
+	0x0000,     /* R139 */
+	0x1310,     /* R140 - GPIO Function Select 1 */
+	0x0003,     /* R141 - GPIO Function Select 2 */
+	0x2000,     /* R142 - GPIO Function Select 3 */
+	0x0000,     /* R143 - GPIO Function Select 4 */
+	0x0000,     /* R144 - Digitiser Control (1) */
+	0x0002,     /* R145 - Digitiser Control (2) */
+	0x0000,     /* R146 */
+	0x0000,     /* R147 */
+	0x0000,     /* R148 */
+	0x0000,     /* R149 */
+	0x0000,     /* R150 */
+	0x0000,     /* R151 */
+	0x7000,     /* R152 - AUX1 Readback */
+	0x7000,     /* R153 - AUX2 Readback */
+	0x7000,     /* R154 - AUX3 Readback */
+	0x7000,     /* R155 - AUX4 Readback */
+	0x0000,     /* R156 - USB Voltage Readback */
+	0x0000,     /* R157 - LINE Voltage Readback */
+	0x0000,     /* R158 - BATT Voltage Readback */
+	0x0000,     /* R159 - Chip Temp Readback */
+	0x0000,     /* R160 */
+	0x0000,     /* R161 */
+	0x0000,     /* R162 */
+	0x0000,     /* R163 - Generic Comparator Control */
+	0x0000,     /* R164 - Generic comparator 1 */
+	0x0000,     /* R165 - Generic comparator 2 */
+	0x0000,     /* R166 - Generic comparator 3 */
+	0x0000,     /* R167 - Generic comparator 4 */
+	0xA00F,     /* R168 - Battery Charger Control 1 */
+	0x0B06,     /* R169 - Battery Charger Control 2 */
+	0x0000,     /* R170 - Battery Charger Control 3 */
+	0x0000,     /* R171 */
+	0x0000,     /* R172 - Current Sink Driver A */
+	0x0000,     /* R173 - CSA Flash control */
+	0x0000,     /* R174 */
+	0x0000,     /* R175 */
+	0x0000,     /* R176 - DCDC/LDO requested */
+	0x032D,     /* R177 - DCDC Active options */
+	0x0000,     /* R178 - DCDC Sleep options */
+	0x0025,     /* R179 - Power-check comparator */
+	0x001A,     /* R180 - DCDC1 Control */
+	0x0800,     /* R181 - DCDC1 Timeouts */
+	0x1006,     /* R182 - DCDC1 Low Power */
+	0x0018,     /* R183 - DCDC2 Control */
+	0x0000,     /* R184 - DCDC2 Timeouts */
+	0x0000,     /* R185 */
+	0x0056,     /* R186 - DCDC3 Control */
+	0x0400,     /* R187 - DCDC3 Timeouts */
+	0x0006,     /* R188 - DCDC3 Low Power */
+	0x0026,     /* R189 - DCDC4 Control */
+	0x0C00,     /* R190 - DCDC4 Timeouts */
+	0x0006,     /* R191 - DCDC4 Low Power */
+	0x0008,     /* R192 */
+	0x0000,     /* R193 */
+	0x0000,     /* R194 */
+	0x0026,     /* R195 */
+	0x0C00,     /* R196 */
+	0x0006,     /* R197 */
+	0x0000,     /* R198 */
+	0x0003,     /* R199 - Limit Switch Control */
+	0x001C,     /* R200 - LDO1 Control */
+	0x0400,     /* R201 - LDO1 Timeouts */
+	0x001C,     /* R202 - LDO1 Low Power */
+	0x0010,     /* R203 - LDO2 Control */
+	0x0C00,     /* R204 - LDO2 Timeouts */
+	0x001C,     /* R205 - LDO2 Low Power */
+	0x0015,     /* R206 - LDO3 Control */
+	0x0000,     /* R207 - LDO3 Timeouts */
+	0x001C,     /* R208 - LDO3 Low Power */
+	0x001A,     /* R209 - LDO4 Control */
+	0x0000,     /* R210 - LDO4 Timeouts */
+	0x001C,     /* R211 - LDO4 Low Power */
+	0x0000,     /* R212 */
+	0x0000,     /* R213 */
+	0x0000,     /* R214 */
+	0x0000,     /* R215 - VCC_FAULT Masks */
+	0x001F,     /* R216 - Main Bandgap Control */
+	0x0000,     /* R217 - OSC Control */
+	0x9000,     /* R218 - RTC Tick Control */
+	0x0000,     /* R219 - Security1 */
+	0x4000,     /* R220 */
+	0x0000,     /* R221 */
+	0x0000,     /* R222 */
+	0x0000,     /* R223 */
+	0x0000,     /* R224 - Signal overrides */
+	0x0000,     /* R225 - DCDC/LDO status */
+	0x0000,     /* R226 - Charger Overides/status */
+	0x0000,     /* R227 - misc overrides */
+	0x0000,     /* R228 - Supply overrides/status 1 */
+	0x0000,     /* R229 - Supply overrides/status 2 */
+	0xE000,     /* R230 - GPIO Pin Status */
+	0x0000,     /* R231 - comparotor overrides */
+	0x0000,     /* R232 */
+	0x0000,     /* R233 - State Machine status */
+	0x1200,     /* R234 - FLL Test 1 */
+	0x0000,     /* R235 */
+	0x8000,     /* R236 */
+	0x0000,     /* R237 */
+	0x0000,     /* R238 */
+	0x0000,     /* R239 */
+	0x0003,     /* R240 */
+	0x0000,     /* R241 */
+	0x0000,     /* R242 */
+	0x0004,     /* R243 */
+	0x0300,     /* R244 */
+	0x0000,     /* R245 */
+	0x0200,     /* R246 */
+	0x0000,     /* R247 */
+	0x1000,     /* R248 - DCDC1 Test Controls */
+	0x1000,     /* R249 */
+	0x1000,     /* R250 - DCDC3 Test Controls */
+	0x1000,     /* R251 - DCDC4 Test Controls */
+};
+#endif
+
+#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_3
+
+#undef WM8350_HAVE_CONFIG_MODE
+#define WM8350_HAVE_CONFIG_MODE
+
+const u16 wm8351_mode3_defaults[] = {
+	0x6143,     /* R0   - Reset/ID */
+	0x0000,     /* R1   - ID */
+	0x0001,     /* R2   - Revision */
+	0x1C02,     /* R3   - System Control 1 */
+	0x0204,     /* R4   - System Control 2 */
+	0x0000,     /* R5   - System Hibernate */
+	0x8A00,     /* R6   - Interface Control */
+	0x0000,     /* R7 */
+	0x8000,     /* R8   - Power mgmt (1) */
+	0x0000,     /* R9   - Power mgmt (2) */
+	0x0000,     /* R10  - Power mgmt (3) */
+	0x2000,     /* R11  - Power mgmt (4) */
+	0x0E00,     /* R12  - Power mgmt (5) */
+	0x0000,     /* R13  - Power mgmt (6) */
+	0x0000,     /* R14  - Power mgmt (7) */
+	0x0000,     /* R15 */
+	0x0000,     /* R16  - RTC Seconds/Minutes */
+	0x0100,     /* R17  - RTC Hours/Day */
+	0x0101,     /* R18  - RTC Date/Month */
+	0x1400,     /* R19  - RTC Year */
+	0x0000,     /* R20  - Alarm Seconds/Minutes */
+	0x0000,     /* R21  - Alarm Hours/Day */
+	0x0000,     /* R22  - Alarm Date/Month */
+	0x0320,     /* R23  - RTC Time Control */
+	0x0000,     /* R24  - System Interrupts */
+	0x0000,     /* R25  - Interrupt Status 1 */
+	0x0000,     /* R26  - Interrupt Status 2 */
+	0x0000,     /* R27 */
+	0x0000,     /* R28  - Under Voltage Interrupt status */
+	0x0000,     /* R29  - Over Current Interrupt status */
+	0x0000,     /* R30  - GPIO Interrupt Status */
+	0x0000,     /* R31  - Comparator Interrupt Status */
+	0x3FFF,     /* R32  - System Interrupts Mask */
+	0x0000,     /* R33  - Interrupt Status 1 Mask */
+	0x0000,     /* R34  - Interrupt Status 2 Mask */
+	0x0000,     /* R35 */
+	0x0000,     /* R36  - Under Voltage Interrupt status Mask */
+	0x0000,     /* R37  - Over Current Interrupt status Mask */
+	0x0000,     /* R38  - GPIO Interrupt Status Mask */
+	0x0000,     /* R39  - Comparator Interrupt Status Mask */
+	0x0040,     /* R40  - Clock Control 1 */
+	0x0000,     /* R41  - Clock Control 2 */
+	0x3A00,     /* R42  - FLL Control 1 */
+	0x7086,     /* R43  - FLL Control 2 */
+	0xC226,     /* R44  - FLL Control 3 */
+	0x0000,     /* R45  - FLL Control 4 */
+	0x0000,     /* R46 */
+	0x0000,     /* R47 */
+	0x0000,     /* R48  - DAC Control */
+	0x0000,     /* R49 */
+	0x00C0,     /* R50  - DAC Digital Volume L */
+	0x00C0,     /* R51  - DAC Digital Volume R */
+	0x0000,     /* R52 */
+	0x0040,     /* R53  - DAC LR Rate */
+	0x0000,     /* R54  - DAC Clock Control */
+	0x0000,     /* R55 */
+	0x0000,     /* R56 */
+	0x0000,     /* R57 */
+	0x4000,     /* R58  - DAC Mute */
+	0x0000,     /* R59  - DAC Mute Volume */
+	0x0000,     /* R60  - DAC Side */
+	0x0000,     /* R61 */
+	0x0000,     /* R62 */
+	0x0000,     /* R63 */
+	0x8000,     /* R64  - ADC Control */
+	0x0000,     /* R65 */
+	0x00C0,     /* R66  - ADC Digital Volume L */
+	0x00C0,     /* R67  - ADC Digital Volume R */
+	0x0000,     /* R68  - ADC Divider */
+	0x0000,     /* R69 */
+	0x0040,     /* R70  - ADC LR Rate */
+	0x0000,     /* R71 */
+	0x0303,     /* R72  - Input Control */
+	0x0000,     /* R73  - IN3 Input Control */
+	0x0000,     /* R74  - Mic Bias Control */
+	0x0000,     /* R75 */
+	0x0000,     /* R76  - Output Control */
+	0x0000,     /* R77  - Jack Detect */
+	0x0000,     /* R78  - Anti Pop Control */
+	0x0000,     /* R79 */
+	0x0040,     /* R80  - Left Input Volume */
+	0x0040,     /* R81  - Right Input Volume */
+	0x0000,     /* R82 */
+	0x0000,     /* R83 */
+	0x0000,     /* R84 */
+	0x0000,     /* R85 */
+	0x0000,     /* R86 */
+	0x0000,     /* R87 */
+	0x0800,     /* R88  - Left Mixer Control */
+	0x1000,     /* R89  - Right Mixer Control */
+	0x0000,     /* R90 */
+	0x0000,     /* R91 */
+	0x0000,     /* R92  - OUT3 Mixer Control */
+	0x0000,     /* R93  - OUT4 Mixer Control */
+	0x0000,     /* R94 */
+	0x0000,     /* R95 */
+	0x0000,     /* R96  - Output Left Mixer Volume */
+	0x0000,     /* R97  - Output Right Mixer Volume */
+	0x0000,     /* R98  - Input Mixer Volume L */
+	0x0000,     /* R99  - Input Mixer Volume R */
+	0x0000,     /* R100 - Input Mixer Volume */
+	0x0000,     /* R101 */
+	0x0000,     /* R102 */
+	0x0000,     /* R103 */
+	0x00E4,     /* R104 - OUT1L Volume */
+	0x00E4,     /* R105 - OUT1R Volume */
+	0x00E4,     /* R106 - OUT2L Volume */
+	0x02E4,     /* R107 - OUT2R Volume */
+	0x0000,     /* R108 */
+	0x0000,     /* R109 */
+	0x0000,     /* R110 */
+	0x0000,     /* R111 - BEEP Volume */
+	0x0A00,     /* R112 - AI Formating */
+	0x0000,     /* R113 - ADC DAC COMP */
+	0x0020,     /* R114 - AI ADC Control */
+	0x0020,     /* R115 - AI DAC Control */
+	0x0000,     /* R116 */
+	0x0000,     /* R117 */
+	0x0000,     /* R118 */
+	0x0000,     /* R119 */
+	0x0000,     /* R120 */
+	0x0000,     /* R121 */
+	0x0000,     /* R122 */
+	0x0000,     /* R123 */
+	0x0000,     /* R124 */
+	0x0000,     /* R125 */
+	0x0000,     /* R126 */
+	0x0000,     /* R127 */
+	0x1FFF,     /* R128 - GPIO Debounce */
+	0x0010,     /* R129 - GPIO Pin pull up Control */
+	0x0000,     /* R130 - GPIO Pull down Control */
+	0x0000,     /* R131 - GPIO Interrupt Mode */
+	0x0000,     /* R132 */
+	0x0000,     /* R133 - GPIO Control */
+	0x0BFB,     /* R134 - GPIO Configuration (i/o) */
+	0x0FFD,     /* R135 - GPIO Pin Polarity / Type */
+	0x0000,     /* R136 */
+	0x0000,     /* R137 */
+	0x0000,     /* R138 */
+	0x0000,     /* R139 */
+	0x0310,     /* R140 - GPIO Function Select 1 */
+	0x0001,     /* R141 - GPIO Function Select 2 */
+	0x2300,     /* R142 - GPIO Function Select 3 */
+	0x0003,     /* R143 - GPIO Function Select 4 */
+	0x0000,     /* R144 - Digitiser Control (1) */
+	0x0002,     /* R145 - Digitiser Control (2) */
+	0x0000,     /* R146 */
+	0x0000,     /* R147 */
+	0x0000,     /* R148 */
+	0x0000,     /* R149 */
+	0x0000,     /* R150 */
+	0x0000,     /* R151 */
+	0x7000,     /* R152 - AUX1 Readback */
+	0x7000,     /* R153 - AUX2 Readback */
+	0x7000,     /* R154 - AUX3 Readback */
+	0x7000,     /* R155 - AUX4 Readback */
+	0x0000,     /* R156 - USB Voltage Readback */
+	0x0000,     /* R157 - LINE Voltage Readback */
+	0x0000,     /* R158 - BATT Voltage Readback */
+	0x0000,     /* R159 - Chip Temp Readback */
+	0x0000,     /* R160 */
+	0x0000,     /* R161 */
+	0x0000,     /* R162 */
+	0x0000,     /* R163 - Generic Comparator Control */
+	0x0000,     /* R164 - Generic comparator 1 */
+	0x0000,     /* R165 - Generic comparator 2 */
+	0x0000,     /* R166 - Generic comparator 3 */
+	0x0000,     /* R167 - Generic comparator 4 */
+	0xA00F,     /* R168 - Battery Charger Control 1 */
+	0x0B06,     /* R169 - Battery Charger Control 2 */
+	0x0000,     /* R170 - Battery Charger Control 3 */
+	0x0000,     /* R171 */
+	0x0000,     /* R172 - Current Sink Driver A */
+	0x0000,     /* R173 - CSA Flash control */
+	0x0000,     /* R174 */
+	0x0000,     /* R175 */
+	0x0000,     /* R176 - DCDC/LDO requested */
+	0x032D,     /* R177 - DCDC Active options */
+	0x0000,     /* R178 - DCDC Sleep options */
+	0x0025,     /* R179 - Power-check comparator */
+	0x000E,     /* R180 - DCDC1 Control */
+	0x0400,     /* R181 - DCDC1 Timeouts */
+	0x1006,     /* R182 - DCDC1 Low Power */
+	0x0018,     /* R183 - DCDC2 Control */
+	0x0000,     /* R184 - DCDC2 Timeouts */
+	0x0000,     /* R185 */
+	0x0026,     /* R186 - DCDC3 Control */
+	0x0800,     /* R187 - DCDC3 Timeouts */
+	0x0006,     /* R188 - DCDC3 Low Power */
+	0x0062,     /* R189 - DCDC4 Control */
+	0x1400,     /* R190 - DCDC4 Timeouts */
+	0x0006,     /* R191 - DCDC4 Low Power */
+	0x0008,     /* R192 */
+	0x0000,     /* R193 */
+	0x0000,     /* R194 */
+	0x0026,     /* R195 */
+	0x0400,     /* R196 */
+	0x0006,     /* R197 */
+	0x0000,     /* R198 */
+	0x0003,     /* R199 - Limit Switch Control */
+	0x0006,     /* R200 - LDO1 Control */
+	0x0C00,     /* R201 - LDO1 Timeouts */
+	0x001C,     /* R202 - LDO1 Low Power */
+	0x0016,     /* R203 - LDO2 Control */
+	0x0000,     /* R204 - LDO2 Timeouts */
+	0x001C,     /* R205 - LDO2 Low Power */
+	0x0019,     /* R206 - LDO3 Control */
+	0x0000,     /* R207 - LDO3 Timeouts */
+	0x001C,     /* R208 - LDO3 Low Power */
+	0x001A,     /* R209 - LDO4 Control */
+	0x1000,     /* R210 - LDO4 Timeouts */
+	0x001C,     /* R211 - LDO4 Low Power */
+	0x0000,     /* R212 */
+	0x0000,     /* R213 */
+	0x0000,     /* R214 */
+	0x0000,     /* R215 - VCC_FAULT Masks */
+	0x001F,     /* R216 - Main Bandgap Control */
+	0x0000,     /* R217 - OSC Control */
+	0x9000,     /* R218 - RTC Tick Control */
+	0x0000,     /* R219 - Security1 */
+	0x4000,     /* R220 */
+	0x0000,     /* R221 */
+	0x0000,     /* R222 */
+	0x0000,     /* R223 */
+	0x0000,     /* R224 - Signal overrides */
+	0x0000,     /* R225 - DCDC/LDO status */
+	0x0000,     /* R226 - Charger Overides/status */
+	0x0000,     /* R227 - misc overrides */
+	0x0000,     /* R228 - Supply overrides/status 1 */
+	0x0000,     /* R229 - Supply overrides/status 2 */
+	0xE000,     /* R230 - GPIO Pin Status */
+	0x0000,     /* R231 - comparotor overrides */
+	0x0000,     /* R232 */
+	0x0000,     /* R233 - State Machine status */
+	0x1200,     /* R234 - FLL Test 1 */
+	0x0000,     /* R235 */
+	0x8000,     /* R236 */
+	0x0000,     /* R237 */
+	0x0000,     /* R238 */
+	0x0000,     /* R239 */
+	0x0003,     /* R240 */
+	0x0000,     /* R241 */
+	0x0000,     /* R242 */
+	0x0004,     /* R243 */
+	0x0300,     /* R244 */
+	0x0000,     /* R245 */
+	0x0200,     /* R246 */
+	0x0000,     /* R247 */
+	0x1000,     /* R248 - DCDC1 Test Controls */
+	0x1000,     /* R249 */
+	0x1000,     /* R250 - DCDC3 Test Controls */
+	0x1000,     /* R251 - DCDC4 Test Controls */
+};
+#endif
+
+#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_0
+
+#undef WM8350_HAVE_CONFIG_MODE
+#define WM8350_HAVE_CONFIG_MODE
+
+const u16 wm8352_mode0_defaults[] = {
+	0x6143,     /* R0   - Reset/ID */
+	0x0000,     /* R1   - ID */
+	0x0002,     /* R2   - Revision */
+	0x1C02,     /* R3   - System Control 1 */
+	0x0004,     /* R4   - System Control 2 */
+	0x0000,     /* R5   - System Hibernate */
+	0x8A00,     /* R6   - Interface Control */
+	0x0000,     /* R7 */
+	0x8000,     /* R8   - Power mgmt (1) */
+	0x0000,     /* R9   - Power mgmt (2) */
+	0x0000,     /* R10  - Power mgmt (3) */
+	0x2000,     /* R11  - Power mgmt (4) */
+	0x0E00,     /* R12  - Power mgmt (5) */
+	0x0000,     /* R13  - Power mgmt (6) */
+	0x0000,     /* R14  - Power mgmt (7) */
+	0x0000,     /* R15 */
+	0x0000,     /* R16  - RTC Seconds/Minutes */
+	0x0100,     /* R17  - RTC Hours/Day */
+	0x0101,     /* R18  - RTC Date/Month */
+	0x1400,     /* R19  - RTC Year */
+	0x0000,     /* R20  - Alarm Seconds/Minutes */
+	0x0000,     /* R21  - Alarm Hours/Day */
+	0x0000,     /* R22  - Alarm Date/Month */
+	0x0320,     /* R23  - RTC Time Control */
+	0x0000,     /* R24  - System Interrupts */
+	0x0000,     /* R25  - Interrupt Status 1 */
+	0x0000,     /* R26  - Interrupt Status 2 */
+	0x0000,     /* R27 */
+	0x0000,     /* R28  - Under Voltage Interrupt status */
+	0x0000,     /* R29  - Over Current Interrupt status */
+	0x0000,     /* R30  - GPIO Interrupt Status */
+	0x0000,     /* R31  - Comparator Interrupt Status */
+	0x3FFF,     /* R32  - System Interrupts Mask */
+	0x0000,     /* R33  - Interrupt Status 1 Mask */
+	0x0000,     /* R34  - Interrupt Status 2 Mask */
+	0x0000,     /* R35 */
+	0x0000,     /* R36  - Under Voltage Interrupt status Mask */
+	0x0000,     /* R37  - Over Current Interrupt status Mask */
+	0x0000,     /* R38  - GPIO Interrupt Status Mask */
+	0x0000,     /* R39  - Comparator Interrupt Status Mask */
+	0x0040,     /* R40  - Clock Control 1 */
+	0x0000,     /* R41  - Clock Control 2 */
+	0x3A00,     /* R42  - FLL Control 1 */
+	0x7086,     /* R43  - FLL Control 2 */
+	0xC226,     /* R44  - FLL Control 3 */
+	0x0000,     /* R45  - FLL Control 4 */
+	0x0000,     /* R46 */
+	0x0000,     /* R47 */
+	0x0000,     /* R48  - DAC Control */
+	0x0000,     /* R49 */
+	0x00C0,     /* R50  - DAC Digital Volume L */
+	0x00C0,     /* R51  - DAC Digital Volume R */
+	0x0000,     /* R52 */
+	0x0040,     /* R53  - DAC LR Rate */
+	0x0000,     /* R54  - DAC Clock Control */
+	0x0000,     /* R55 */
+	0x0000,     /* R56 */
+	0x0000,     /* R57 */
+	0x4000,     /* R58  - DAC Mute */
+	0x0000,     /* R59  - DAC Mute Volume */
+	0x0000,     /* R60  - DAC Side */
+	0x0000,     /* R61 */
+	0x0000,     /* R62 */
+	0x0000,     /* R63 */
+	0x8000,     /* R64  - ADC Control */
+	0x0000,     /* R65 */
+	0x00C0,     /* R66  - ADC Digital Volume L */
+	0x00C0,     /* R67  - ADC Digital Volume R */
+	0x0000,     /* R68  - ADC Divider */
+	0x0000,     /* R69 */
+	0x0040,     /* R70  - ADC LR Rate */
+	0x0000,     /* R71 */
+	0x0303,     /* R72  - Input Control */
+	0x0000,     /* R73  - IN3 Input Control */
+	0x0000,     /* R74  - Mic Bias Control */
+	0x0000,     /* R75 */
+	0x0000,     /* R76  - Output Control */
+	0x0000,     /* R77  - Jack Detect */
+	0x0000,     /* R78  - Anti Pop Control */
+	0x0000,     /* R79 */
+	0x0040,     /* R80  - Left Input Volume */
+	0x0040,     /* R81  - Right Input Volume */
+	0x0000,     /* R82 */
+	0x0000,     /* R83 */
+	0x0000,     /* R84 */
+	0x0000,     /* R85 */
+	0x0000,     /* R86 */
+	0x0000,     /* R87 */
+	0x0800,     /* R88  - Left Mixer Control */
+	0x1000,     /* R89  - Right Mixer Control */
+	0x0000,     /* R90 */
+	0x0000,     /* R91 */
+	0x0000,     /* R92  - OUT3 Mixer Control */
+	0x0000,     /* R93  - OUT4 Mixer Control */
+	0x0000,     /* R94 */
+	0x0000,     /* R95 */
+	0x0000,     /* R96  - Output Left Mixer Volume */
+	0x0000,     /* R97  - Output Right Mixer Volume */
+	0x0000,     /* R98  - Input Mixer Volume L */
+	0x0000,     /* R99  - Input Mixer Volume R */
+	0x0000,     /* R100 - Input Mixer Volume */
+	0x0000,     /* R101 */
+	0x0000,     /* R102 */
+	0x0000,     /* R103 */
+	0x00E4,     /* R104 - OUT1L Volume */
+	0x00E4,     /* R105 - OUT1R Volume */
+	0x00E4,     /* R106 - OUT2L Volume */
+	0x02E4,     /* R107 - OUT2R Volume */
+	0x0000,     /* R108 */
+	0x0000,     /* R109 */
+	0x0000,     /* R110 */
+	0x0000,     /* R111 - BEEP Volume */
+	0x0A00,     /* R112 - AI Formating */
+	0x0000,     /* R113 - ADC DAC COMP */
+	0x0020,     /* R114 - AI ADC Control */
+	0x0020,     /* R115 - AI DAC Control */
+	0x0000,     /* R116 */
+	0x0000,     /* R117 */
+	0x0000,     /* R118 */
+	0x0000,     /* R119 */
+	0x0000,     /* R120 */
+	0x0000,     /* R121 */
+	0x0000,     /* R122 */
+	0x0000,     /* R123 */
+	0x0000,     /* R124 */
+	0x0000,     /* R125 */
+	0x0000,     /* R126 */
+	0x0000,     /* R127 */
+	0x1FFF,     /* R128 - GPIO Debounce */
+	0x0000,     /* R129 - GPIO Pin pull up Control */
+	0x0000,     /* R130 - GPIO Pull down Control */
+	0x0000,     /* R131 - GPIO Interrupt Mode */
+	0x0000,     /* R132 */
+	0x0000,     /* R133 - GPIO Control */
+	0x0FFC,     /* R134 - GPIO Configuration (i/o) */
+	0x0FFC,     /* R135 - GPIO Pin Polarity / Type */
+	0x0000,     /* R136 */
+	0x0000,     /* R137 */
+	0x0000,     /* R138 */
+	0x0000,     /* R139 */
+	0x0013,     /* R140 - GPIO Function Select 1 */
+	0x0000,     /* R141 - GPIO Function Select 2 */
+	0x0000,     /* R142 - GPIO Function Select 3 */
+	0x0003,     /* R143 - GPIO Function Select 4 */
+	0x0000,     /* R144 - Digitiser Control (1) */
+	0x0002,     /* R145 - Digitiser Control (2) */
+	0x0000,     /* R146 */
+	0x0000,     /* R147 */
+	0x0000,     /* R148 */
+	0x0000,     /* R149 */
+	0x0000,     /* R150 */
+	0x0000,     /* R151 */
+	0x7000,     /* R152 - AUX1 Readback */
+	0x7000,     /* R153 - AUX2 Readback */
+	0x7000,     /* R154 - AUX3 Readback */
+	0x7000,     /* R155 - AUX4 Readback */
+	0x0000,     /* R156 - USB Voltage Readback */
+	0x0000,     /* R157 - LINE Voltage Readback */
+	0x0000,     /* R158 - BATT Voltage Readback */
+	0x0000,     /* R159 - Chip Temp Readback */
+	0x0000,     /* R160 */
+	0x0000,     /* R161 */
+	0x0000,     /* R162 */
+	0x0000,     /* R163 - Generic Comparator Control */
+	0x0000,     /* R164 - Generic comparator 1 */
+	0x0000,     /* R165 - Generic comparator 2 */
+	0x0000,     /* R166 - Generic comparator 3 */
+	0x0000,     /* R167 - Generic comparator 4 */
+	0xA00F,     /* R168 - Battery Charger Control 1 */
+	0x0B06,     /* R169 - Battery Charger Control 2 */
+	0x0000,     /* R170 - Battery Charger Control 3 */
+	0x0000,     /* R171 */
+	0x0000,     /* R172 - Current Sink Driver A */
+	0x0000,     /* R173 - CSA Flash control */
+	0x0000,     /* R174 - Current Sink Driver B */
+	0x0000,     /* R175 - CSB Flash control */
+	0x0000,     /* R176 - DCDC/LDO requested */
+	0x032D,     /* R177 - DCDC Active options */
+	0x0000,     /* R178 - DCDC Sleep options */
+	0x0025,     /* R179 - Power-check comparator */
+	0x000E,     /* R180 - DCDC1 Control */
+	0x0000,     /* R181 - DCDC1 Timeouts */
+	0x1006,     /* R182 - DCDC1 Low Power */
+	0x0018,     /* R183 - DCDC2 Control */
+	0x0000,     /* R184 - DCDC2 Timeouts */
+	0x0000,     /* R185 */
+	0x0000,     /* R186 - DCDC3 Control */
+	0x0000,     /* R187 - DCDC3 Timeouts */
+	0x0006,     /* R188 - DCDC3 Low Power */
+	0x0000,     /* R189 - DCDC4 Control */
+	0x0000,     /* R190 - DCDC4 Timeouts */
+	0x0006,     /* R191 - DCDC4 Low Power */
+	0x0008,     /* R192 - DCDC5 Control */
+	0x0000,     /* R193 - DCDC5 Timeouts */
+	0x0000,     /* R194 */
+	0x0000,     /* R195 - DCDC6 Control */
+	0x0000,     /* R196 - DCDC6 Timeouts */
+	0x0006,     /* R197 - DCDC6 Low Power */
+	0x0000,     /* R198 */
+	0x0003,     /* R199 - Limit Switch Control */
+	0x001C,     /* R200 - LDO1 Control */
+	0x0000,     /* R201 - LDO1 Timeouts */
+	0x001C,     /* R202 - LDO1 Low Power */
+	0x001B,     /* R203 - LDO2 Control */
+	0x0000,     /* R204 - LDO2 Timeouts */
+	0x001C,     /* R205 - LDO2 Low Power */
+	0x001B,     /* R206 - LDO3 Control */
+	0x0000,     /* R207 - LDO3 Timeouts */
+	0x001C,     /* R208 - LDO3 Low Power */
+	0x001B,     /* R209 - LDO4 Control */
+	0x0000,     /* R210 - LDO4 Timeouts */
+	0x001C,     /* R211 - LDO4 Low Power */
+	0x0000,     /* R212 */
+	0x0000,     /* R213 */
+	0x0000,     /* R214 */
+	0x0000,     /* R215 - VCC_FAULT Masks */
+	0x001F,     /* R216 - Main Bandgap Control */
+	0x0000,     /* R217 - OSC Control */
+	0x9000,     /* R218 - RTC Tick Control */
+	0x0000,     /* R219 - Security1 */
+	0x4000,     /* R220 */
+	0x0000,     /* R221 */
+	0x0000,     /* R222 */
+	0x0000,     /* R223 */
+	0x0000,     /* R224 - Signal overrides */
+	0x0000,     /* R225 - DCDC/LDO status */
+	0x0000,     /* R226 - Charger Overides/status */
+	0x0000,     /* R227 - misc overrides */
+	0x0000,     /* R228 - Supply overrides/status 1 */
+	0x0000,     /* R229 - Supply overrides/status 2 */
+	0xE000,     /* R230 - GPIO Pin Status */
+	0x0000,     /* R231 - comparotor overrides */
+	0x0000,     /* R232 */
+	0x0000,     /* R233 - State Machine status */
+	0x1200,     /* R234 */
+	0x0000,     /* R235 */
+	0x8000,     /* R236 */
+	0x0000,     /* R237 */
+	0x0000,     /* R238 */
+	0x0000,     /* R239 */
+	0x0003,     /* R240 */
+	0x0000,     /* R241 */
+	0x0000,     /* R242 */
+	0x0004,     /* R243 */
+	0x0300,     /* R244 */
+	0x0000,     /* R245 */
+	0x0200,     /* R246 */
+	0x0000,     /* R247 */
+	0x1000,     /* R248 - DCDC1 Test Controls */
+	0x5000,     /* R249 */
+	0x1000,     /* R250 - DCDC3 Test Controls */
+	0x1000,     /* R251 - DCDC4 Test Controls */
+	0x5100,     /* R252 */
+	0x1000,     /* R253 - DCDC6 Test Controls */
+};
+#endif
+
+#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_1
+
+#undef WM8350_HAVE_CONFIG_MODE
+#define WM8350_HAVE_CONFIG_MODE
+
+const u16 wm8352_mode1_defaults[] = {
+	0x6143,     /* R0   - Reset/ID */
+	0x0000,     /* R1   - ID */
+	0x0002,     /* R2   - Revision */
+	0x1C02,     /* R3   - System Control 1 */
+	0x0204,     /* R4   - System Control 2 */
+	0x0000,     /* R5   - System Hibernate */
+	0x8A00,     /* R6   - Interface Control */
+	0x0000,     /* R7 */
+	0x8000,     /* R8   - Power mgmt (1) */
+	0x0000,     /* R9   - Power mgmt (2) */
+	0x0000,     /* R10  - Power mgmt (3) */
+	0x2000,     /* R11  - Power mgmt (4) */
+	0x0E00,     /* R12  - Power mgmt (5) */
+	0x0000,     /* R13  - Power mgmt (6) */
+	0x0000,     /* R14  - Power mgmt (7) */
+	0x0000,     /* R15 */
+	0x0000,     /* R16  - RTC Seconds/Minutes */
+	0x0100,     /* R17  - RTC Hours/Day */
+	0x0101,     /* R18  - RTC Date/Month */
+	0x1400,     /* R19  - RTC Year */
+	0x0000,     /* R20  - Alarm Seconds/Minutes */
+	0x0000,     /* R21  - Alarm Hours/Day */
+	0x0000,     /* R22  - Alarm Date/Month */
+	0x0320,     /* R23  - RTC Time Control */
+	0x0000,     /* R24  - System Interrupts */
+	0x0000,     /* R25  - Interrupt Status 1 */
+	0x0000,     /* R26  - Interrupt Status 2 */
+	0x0000,     /* R27 */
+	0x0000,     /* R28  - Under Voltage Interrupt status */
+	0x0000,     /* R29  - Over Current Interrupt status */
+	0x0000,     /* R30  - GPIO Interrupt Status */
+	0x0000,     /* R31  - Comparator Interrupt Status */
+	0x3FFF,     /* R32  - System Interrupts Mask */
+	0x0000,     /* R33  - Interrupt Status 1 Mask */
+	0x0000,     /* R34  - Interrupt Status 2 Mask */
+	0x0000,     /* R35 */
+	0x0000,     /* R36  - Under Voltage Interrupt status Mask */
+	0x0000,     /* R37  - Over Current Interrupt status Mask */
+	0x0000,     /* R38  - GPIO Interrupt Status Mask */
+	0x0000,     /* R39  - Comparator Interrupt Status Mask */
+	0x0040,     /* R40  - Clock Control 1 */
+	0x0000,     /* R41  - Clock Control 2 */
+	0x3A00,     /* R42  - FLL Control 1 */
+	0x7086,     /* R43  - FLL Control 2 */
+	0xC226,     /* R44  - FLL Control 3 */
+	0x0000,     /* R45  - FLL Control 4 */
+	0x0000,     /* R46 */
+	0x0000,     /* R47 */
+	0x0000,     /* R48  - DAC Control */
+	0x0000,     /* R49 */
+	0x00C0,     /* R50  - DAC Digital Volume L */
+	0x00C0,     /* R51  - DAC Digital Volume R */
+	0x0000,     /* R52 */
+	0x0040,     /* R53  - DAC LR Rate */
+	0x0000,     /* R54  - DAC Clock Control */
+	0x0000,     /* R55 */
+	0x0000,     /* R56 */
+	0x0000,     /* R57 */
+	0x4000,     /* R58  - DAC Mute */
+	0x0000,     /* R59  - DAC Mute Volume */
+	0x0000,     /* R60  - DAC Side */
+	0x0000,     /* R61 */
+	0x0000,     /* R62 */
+	0x0000,     /* R63 */
+	0x8000,     /* R64  - ADC Control */
+	0x0000,     /* R65 */
+	0x00C0,     /* R66  - ADC Digital Volume L */
+	0x00C0,     /* R67  - ADC Digital Volume R */
+	0x0000,     /* R68  - ADC Divider */
+	0x0000,     /* R69 */
+	0x0040,     /* R70  - ADC LR Rate */
+	0x0000,     /* R71 */
+	0x0303,     /* R72  - Input Control */
+	0x0000,     /* R73  - IN3 Input Control */
+	0x0000,     /* R74  - Mic Bias Control */
+	0x0000,     /* R75 */
+	0x0000,     /* R76  - Output Control */
+	0x0000,     /* R77  - Jack Detect */
+	0x0000,     /* R78  - Anti Pop Control */
+	0x0000,     /* R79 */
+	0x0040,     /* R80  - Left Input Volume */
+	0x0040,     /* R81  - Right Input Volume */
+	0x0000,     /* R82 */
+	0x0000,     /* R83 */
+	0x0000,     /* R84 */
+	0x0000,     /* R85 */
+	0x0000,     /* R86 */
+	0x0000,     /* R87 */
+	0x0800,     /* R88  - Left Mixer Control */
+	0x1000,     /* R89  - Right Mixer Control */
+	0x0000,     /* R90 */
+	0x0000,     /* R91 */
+	0x0000,     /* R92  - OUT3 Mixer Control */
+	0x0000,     /* R93  - OUT4 Mixer Control */
+	0x0000,     /* R94 */
+	0x0000,     /* R95 */
+	0x0000,     /* R96  - Output Left Mixer Volume */
+	0x0000,     /* R97  - Output Right Mixer Volume */
+	0x0000,     /* R98  - Input Mixer Volume L */
+	0x0000,     /* R99  - Input Mixer Volume R */
+	0x0000,     /* R100 - Input Mixer Volume */
+	0x0000,     /* R101 */
+	0x0000,     /* R102 */
+	0x0000,     /* R103 */
+	0x00E4,     /* R104 - OUT1L Volume */
+	0x00E4,     /* R105 - OUT1R Volume */
+	0x00E4,     /* R106 - OUT2L Volume */
+	0x02E4,     /* R107 - OUT2R Volume */
+	0x0000,     /* R108 */
+	0x0000,     /* R109 */
+	0x0000,     /* R110 */
+	0x0000,     /* R111 - BEEP Volume */
+	0x0A00,     /* R112 - AI Formating */
+	0x0000,     /* R113 - ADC DAC COMP */
+	0x0020,     /* R114 - AI ADC Control */
+	0x0020,     /* R115 - AI DAC Control */
+	0x0000,     /* R116 */
+	0x0000,     /* R117 */
+	0x0000,     /* R118 */
+	0x0000,     /* R119 */
+	0x0000,     /* R120 */
+	0x0000,     /* R121 */
+	0x0000,     /* R122 */
+	0x0000,     /* R123 */
+	0x0000,     /* R124 */
+	0x0000,     /* R125 */
+	0x0000,     /* R126 */
+	0x0000,     /* R127 */
+	0x1FFF,     /* R128 - GPIO Debounce */
+	0x0000,     /* R129 - GPIO Pin pull up Control */
+	0x0000,     /* R130 - GPIO Pull down Control */
+	0x0000,     /* R131 - GPIO Interrupt Mode */
+	0x0000,     /* R132 */
+	0x0000,     /* R133 - GPIO Control */
+	0x0BFB,     /* R134 - GPIO Configuration (i/o) */
+	0x0FFF,     /* R135 - GPIO Pin Polarity / Type */
+	0x0000,     /* R136 */
+	0x0000,     /* R137 */
+	0x0000,     /* R138 */
+	0x0000,     /* R139 */
+	0x0300,     /* R140 - GPIO Function Select 1 */
+	0x0000,     /* R141 - GPIO Function Select 2 */
+	0x2300,     /* R142 - GPIO Function Select 3 */
+	0x0003,     /* R143 - GPIO Function Select 4 */
+	0x0000,     /* R144 - Digitiser Control (1) */
+	0x0002,     /* R145 - Digitiser Control (2) */
+	0x0000,     /* R146 */
+	0x0000,     /* R147 */
+	0x0000,     /* R148 */
+	0x0000,     /* R149 */
+	0x0000,     /* R150 */
+	0x0000,     /* R151 */
+	0x7000,     /* R152 - AUX1 Readback */
+	0x7000,     /* R153 - AUX2 Readback */
+	0x7000,     /* R154 - AUX3 Readback */
+	0x7000,     /* R155 - AUX4 Readback */
+	0x0000,     /* R156 - USB Voltage Readback */
+	0x0000,     /* R157 - LINE Voltage Readback */
+	0x0000,     /* R158 - BATT Voltage Readback */
+	0x0000,     /* R159 - Chip Temp Readback */
+	0x0000,     /* R160 */
+	0x0000,     /* R161 */
+	0x0000,     /* R162 */
+	0x0000,     /* R163 - Generic Comparator Control */
+	0x0000,     /* R164 - Generic comparator 1 */
+	0x0000,     /* R165 - Generic comparator 2 */
+	0x0000,     /* R166 - Generic comparator 3 */
+	0x0000,     /* R167 - Generic comparator 4 */
+	0xA00F,     /* R168 - Battery Charger Control 1 */
+	0x0B06,     /* R169 - Battery Charger Control 2 */
+	0x0000,     /* R170 - Battery Charger Control 3 */
+	0x0000,     /* R171 */
+	0x0000,     /* R172 - Current Sink Driver A */
+	0x0000,     /* R173 - CSA Flash control */
+	0x0000,     /* R174 - Current Sink Driver B */
+	0x0000,     /* R175 - CSB Flash control */
+	0x0000,     /* R176 - DCDC/LDO requested */
+	0x032D,     /* R177 - DCDC Active options */
+	0x0000,     /* R178 - DCDC Sleep options */
+	0x0025,     /* R179 - Power-check comparator */
+	0x0062,     /* R180 - DCDC1 Control */
+	0x0400,     /* R181 - DCDC1 Timeouts */
+	0x1006,     /* R182 - DCDC1 Low Power */
+	0x0018,     /* R183 - DCDC2 Control */
+	0x0000,     /* R184 - DCDC2 Timeouts */
+	0x0000,     /* R185 */
+	0x0006,     /* R186 - DCDC3 Control */
+	0x0800,     /* R187 - DCDC3 Timeouts */
+	0x0006,     /* R188 - DCDC3 Low Power */
+	0x0006,     /* R189 - DCDC4 Control */
+	0x0C00,     /* R190 - DCDC4 Timeouts */
+	0x0006,     /* R191 - DCDC4 Low Power */
+	0x0008,     /* R192 - DCDC5 Control */
+	0x0000,     /* R193 - DCDC5 Timeouts */
+	0x0000,     /* R194 */
+	0x0026,     /* R195 - DCDC6 Control */
+	0x1000,     /* R196 - DCDC6 Timeouts */
+	0x0006,     /* R197 - DCDC6 Low Power */
+	0x0000,     /* R198 */
+	0x0003,     /* R199 - Limit Switch Control */
+	0x0002,     /* R200 - LDO1 Control */
+	0x0000,     /* R201 - LDO1 Timeouts */
+	0x001C,     /* R202 - LDO1 Low Power */
+	0x001A,     /* R203 - LDO2 Control */
+	0x0000,     /* R204 - LDO2 Timeouts */
+	0x001C,     /* R205 - LDO2 Low Power */
+	0x001F,     /* R206 - LDO3 Control */
+	0x0000,     /* R207 - LDO3 Timeouts */
+	0x001C,     /* R208 - LDO3 Low Power */
+	0x001F,     /* R209 - LDO4 Control */
+	0x0000,     /* R210 - LDO4 Timeouts */
+	0x001C,     /* R211 - LDO4 Low Power */
+	0x0000,     /* R212 */
+	0x0000,     /* R213 */
+	0x0000,     /* R214 */
+	0x0000,     /* R215 - VCC_FAULT Masks */
+	0x001F,     /* R216 - Main Bandgap Control */
+	0x0000,     /* R217 - OSC Control */
+	0x9000,     /* R218 - RTC Tick Control */
+	0x0000,     /* R219 - Security1 */
+	0x4000,     /* R220 */
+	0x0000,     /* R221 */
+	0x0000,     /* R222 */
+	0x0000,     /* R223 */
+	0x0000,     /* R224 - Signal overrides */
+	0x0000,     /* R225 - DCDC/LDO status */
+	0x0000,     /* R226 - Charger Overides/status */
+	0x0000,     /* R227 - misc overrides */
+	0x0000,     /* R228 - Supply overrides/status 1 */
+	0x0000,     /* R229 - Supply overrides/status 2 */
+	0xE000,     /* R230 - GPIO Pin Status */
+	0x0000,     /* R231 - comparotor overrides */
+	0x0000,     /* R232 */
+	0x0000,     /* R233 - State Machine status */
+	0x1200,     /* R234 */
+	0x0000,     /* R235 */
+	0x8000,     /* R236 */
+	0x0000,     /* R237 */
+	0x0000,     /* R238 */
+	0x0000,     /* R239 */
+	0x0003,     /* R240 */
+	0x0000,     /* R241 */
+	0x0000,     /* R242 */
+	0x0004,     /* R243 */
+	0x0300,     /* R244 */
+	0x0000,     /* R245 */
+	0x0200,     /* R246 */
+	0x0000,     /* R247 */
+	0x1000,     /* R248 - DCDC1 Test Controls */
+	0x5000,     /* R249 */
+	0x1000,     /* R250 - DCDC3 Test Controls */
+	0x1000,     /* R251 - DCDC4 Test Controls */
+	0x5100,     /* R252 */
+	0x1000,     /* R253 - DCDC6 Test Controls */
+};
+#endif
+
+#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_2
+
+#undef WM8350_HAVE_CONFIG_MODE
+#define WM8350_HAVE_CONFIG_MODE
+
+const u16 wm8352_mode2_defaults[] = {
+	0x6143,     /* R0   - Reset/ID */
+	0x0000,     /* R1   - ID */
+	0x0002,     /* R2   - Revision */
+	0x1C02,     /* R3   - System Control 1 */
+	0x0204,     /* R4   - System Control 2 */
+	0x0000,     /* R5   - System Hibernate */
+	0x8A00,     /* R6   - Interface Control */
+	0x0000,     /* R7 */
+	0x8000,     /* R8   - Power mgmt (1) */
+	0x0000,     /* R9   - Power mgmt (2) */
+	0x0000,     /* R10  - Power mgmt (3) */
+	0x2000,     /* R11  - Power mgmt (4) */
+	0x0E00,     /* R12  - Power mgmt (5) */
+	0x0000,     /* R13  - Power mgmt (6) */
+	0x0000,     /* R14  - Power mgmt (7) */
+	0x0000,     /* R15 */
+	0x0000,     /* R16  - RTC Seconds/Minutes */
+	0x0100,     /* R17  - RTC Hours/Day */
+	0x0101,     /* R18  - RTC Date/Month */
+	0x1400,     /* R19  - RTC Year */
+	0x0000,     /* R20  - Alarm Seconds/Minutes */
+	0x0000,     /* R21  - Alarm Hours/Day */
+	0x0000,     /* R22  - Alarm Date/Month */
+	0x0320,     /* R23  - RTC Time Control */
+	0x0000,     /* R24  - System Interrupts */
+	0x0000,     /* R25  - Interrupt Status 1 */
+	0x0000,     /* R26  - Interrupt Status 2 */
+	0x0000,     /* R27 */
+	0x0000,     /* R28  - Under Voltage Interrupt status */
+	0x0000,     /* R29  - Over Current Interrupt status */
+	0x0000,     /* R30  - GPIO Interrupt Status */
+	0x0000,     /* R31  - Comparator Interrupt Status */
+	0x3FFF,     /* R32  - System Interrupts Mask */
+	0x0000,     /* R33  - Interrupt Status 1 Mask */
+	0x0000,     /* R34  - Interrupt Status 2 Mask */
+	0x0000,     /* R35 */
+	0x0000,     /* R36  - Under Voltage Interrupt status Mask */
+	0x0000,     /* R37  - Over Current Interrupt status Mask */
+	0x0000,     /* R38  - GPIO Interrupt Status Mask */
+	0x0000,     /* R39  - Comparator Interrupt Status Mask */
+	0x0040,     /* R40  - Clock Control 1 */
+	0x0000,     /* R41  - Clock Control 2 */
+	0x3A00,     /* R42  - FLL Control 1 */
+	0x7086,     /* R43  - FLL Control 2 */
+	0xC226,     /* R44  - FLL Control 3 */
+	0x0000,     /* R45  - FLL Control 4 */
+	0x0000,     /* R46 */
+	0x0000,     /* R47 */
+	0x0000,     /* R48  - DAC Control */
+	0x0000,     /* R49 */
+	0x00C0,     /* R50  - DAC Digital Volume L */
+	0x00C0,     /* R51  - DAC Digital Volume R */
+	0x0000,     /* R52 */
+	0x0040,     /* R53  - DAC LR Rate */
+	0x0000,     /* R54  - DAC Clock Control */
+	0x0000,     /* R55 */
+	0x0000,     /* R56 */
+	0x0000,     /* R57 */
+	0x4000,     /* R58  - DAC Mute */
+	0x0000,     /* R59  - DAC Mute Volume */
+	0x0000,     /* R60  - DAC Side */
+	0x0000,     /* R61 */
+	0x0000,     /* R62 */
+	0x0000,     /* R63 */
+	0x8000,     /* R64  - ADC Control */
+	0x0000,     /* R65 */
+	0x00C0,     /* R66  - ADC Digital Volume L */
+	0x00C0,     /* R67  - ADC Digital Volume R */
+	0x0000,     /* R68  - ADC Divider */
+	0x0000,     /* R69 */
+	0x0040,     /* R70  - ADC LR Rate */
+	0x0000,     /* R71 */
+	0x0303,     /* R72  - Input Control */
+	0x0000,     /* R73  - IN3 Input Control */
+	0x0000,     /* R74  - Mic Bias Control */
+	0x0000,     /* R75 */
+	0x0000,     /* R76  - Output Control */
+	0x0000,     /* R77  - Jack Detect */
+	0x0000,     /* R78  - Anti Pop Control */
+	0x0000,     /* R79 */
+	0x0040,     /* R80  - Left Input Volume */
+	0x0040,     /* R81  - Right Input Volume */
+	0x0000,     /* R82 */
+	0x0000,     /* R83 */
+	0x0000,     /* R84 */
+	0x0000,     /* R85 */
+	0x0000,     /* R86 */
+	0x0000,     /* R87 */
+	0x0800,     /* R88  - Left Mixer Control */
+	0x1000,     /* R89  - Right Mixer Control */
+	0x0000,     /* R90 */
+	0x0000,     /* R91 */
+	0x0000,     /* R92  - OUT3 Mixer Control */
+	0x0000,     /* R93  - OUT4 Mixer Control */
+	0x0000,     /* R94 */
+	0x0000,     /* R95 */
+	0x0000,     /* R96  - Output Left Mixer Volume */
+	0x0000,     /* R97  - Output Right Mixer Volume */
+	0x0000,     /* R98  - Input Mixer Volume L */
+	0x0000,     /* R99  - Input Mixer Volume R */
+	0x0000,     /* R100 - Input Mixer Volume */
+	0x0000,     /* R101 */
+	0x0000,     /* R102 */
+	0x0000,     /* R103 */
+	0x00E4,     /* R104 - OUT1L Volume */
+	0x00E4,     /* R105 - OUT1R Volume */
+	0x00E4,     /* R106 - OUT2L Volume */
+	0x02E4,     /* R107 - OUT2R Volume */
+	0x0000,     /* R108 */
+	0x0000,     /* R109 */
+	0x0000,     /* R110 */
+	0x0000,     /* R111 - BEEP Volume */
+	0x0A00,     /* R112 - AI Formating */
+	0x0000,     /* R113 - ADC DAC COMP */
+	0x0020,     /* R114 - AI ADC Control */
+	0x0020,     /* R115 - AI DAC Control */
+	0x0000,     /* R116 */
+	0x0000,     /* R117 */
+	0x0000,     /* R118 */
+	0x0000,     /* R119 */
+	0x0000,     /* R120 */
+	0x0000,     /* R121 */
+	0x0000,     /* R122 */
+	0x0000,     /* R123 */
+	0x0000,     /* R124 */
+	0x0000,     /* R125 */
+	0x0000,     /* R126 */
+	0x0000,     /* R127 */
+	0x1FFF,     /* R128 - GPIO Debounce */
+	0x0000,     /* R129 - GPIO Pin pull up Control */
+	0x0110,     /* R130 - GPIO Pull down Control */
+	0x0000,     /* R131 - GPIO Interrupt Mode */
+	0x0000,     /* R132 */
+	0x0000,     /* R133 - GPIO Control */
+	0x09DA,     /* R134 - GPIO Configuration (i/o) */
+	0x0DD6,     /* R135 - GPIO Pin Polarity / Type */
+	0x0000,     /* R136 */
+	0x0000,     /* R137 */
+	0x0000,     /* R138 */
+	0x0000,     /* R139 */
+	0x1310,     /* R140 - GPIO Function Select 1 */
+	0x0033,     /* R141 - GPIO Function Select 2 */
+	0x2000,     /* R142 - GPIO Function Select 3 */
+	0x0000,     /* R143 - GPIO Function Select 4 */
+	0x0000,     /* R144 - Digitiser Control (1) */
+	0x0002,     /* R145 - Digitiser Control (2) */
+	0x0000,     /* R146 */
+	0x0000,     /* R147 */
+	0x0000,     /* R148 */
+	0x0000,     /* R149 */
+	0x0000,     /* R150 */
+	0x0000,     /* R151 */
+	0x7000,     /* R152 - AUX1 Readback */
+	0x7000,     /* R153 - AUX2 Readback */
+	0x7000,     /* R154 - AUX3 Readback */
+	0x7000,     /* R155 - AUX4 Readback */
+	0x0000,     /* R156 - USB Voltage Readback */
+	0x0000,     /* R157 - LINE Voltage Readback */
+	0x0000,     /* R158 - BATT Voltage Readback */
+	0x0000,     /* R159 - Chip Temp Readback */
+	0x0000,     /* R160 */
+	0x0000,     /* R161 */
+	0x0000,     /* R162 */
+	0x0000,     /* R163 - Generic Comparator Control */
+	0x0000,     /* R164 - Generic comparator 1 */
+	0x0000,     /* R165 - Generic comparator 2 */
+	0x0000,     /* R166 - Generic comparator 3 */
+	0x0000,     /* R167 - Generic comparator 4 */
+	0xA00F,     /* R168 - Battery Charger Control 1 */
+	0x0B06,     /* R169 - Battery Charger Control 2 */
+	0x0000,     /* R170 - Battery Charger Control 3 */
+	0x0000,     /* R171 */
+	0x0000,     /* R172 - Current Sink Driver A */
+	0x0000,     /* R173 - CSA Flash control */
+	0x0000,     /* R174 - Current Sink Driver B */
+	0x0000,     /* R175 - CSB Flash control */
+	0x0000,     /* R176 - DCDC/LDO requested */
+	0x032D,     /* R177 - DCDC Active options */
+	0x0000,     /* R178 - DCDC Sleep options */
+	0x0025,     /* R179 - Power-check comparator */
+	0x000E,     /* R180 - DCDC1 Control */
+	0x0800,     /* R181 - DCDC1 Timeouts */
+	0x1006,     /* R182 - DCDC1 Low Power */
+	0x0018,     /* R183 - DCDC2 Control */
+	0x0000,     /* R184 - DCDC2 Timeouts */
+	0x0000,     /* R185 */
+	0x0056,     /* R186 - DCDC3 Control */
+	0x1800,     /* R187 - DCDC3 Timeouts */
+	0x0006,     /* R188 - DCDC3 Low Power */
+	0x000E,     /* R189 - DCDC4 Control */
+	0x1000,     /* R190 - DCDC4 Timeouts */
+	0x0006,     /* R191 - DCDC4 Low Power */
+	0x0008,     /* R192 - DCDC5 Control */
+	0x0000,     /* R193 - DCDC5 Timeouts */
+	0x0000,     /* R194 */
+	0x0026,     /* R195 - DCDC6 Control */
+	0x0C00,     /* R196 - DCDC6 Timeouts */
+	0x0006,     /* R197 - DCDC6 Low Power */
+	0x0000,     /* R198 */
+	0x0003,     /* R199 - Limit Switch Control */
+	0x001C,     /* R200 - LDO1 Control */
+	0x0000,     /* R201 - LDO1 Timeouts */
+	0x001C,     /* R202 - LDO1 Low Power */
+	0x0006,     /* R203 - LDO2 Control */
+	0x0400,     /* R204 - LDO2 Timeouts */
+	0x001C,     /* R205 - LDO2 Low Power */
+	0x001C,     /* R206 - LDO3 Control */
+	0x1400,     /* R207 - LDO3 Timeouts */
+	0x001C,     /* R208 - LDO3 Low Power */
+	0x001A,     /* R209 - LDO4 Control */
+	0x0000,     /* R210 - LDO4 Timeouts */
+	0x001C,     /* R211 - LDO4 Low Power */
+	0x0000,     /* R212 */
+	0x0000,     /* R213 */
+	0x0000,     /* R214 */
+	0x0000,     /* R215 - VCC_FAULT Masks */
+	0x001F,     /* R216 - Main Bandgap Control */
+	0x0000,     /* R217 - OSC Control */
+	0x9000,     /* R218 - RTC Tick Control */
+	0x0000,     /* R219 - Security1 */
+	0x4000,     /* R220 */
+	0x0000,     /* R221 */
+	0x0000,     /* R222 */
+	0x0000,     /* R223 */
+	0x0000,     /* R224 - Signal overrides */
+	0x0000,     /* R225 - DCDC/LDO status */
+	0x0000,     /* R226 - Charger Overides/status */
+	0x0000,     /* R227 - misc overrides */
+	0x0000,     /* R228 - Supply overrides/status 1 */
+	0x0000,     /* R229 - Supply overrides/status 2 */
+	0xE000,     /* R230 - GPIO Pin Status */
+	0x0000,     /* R231 - comparotor overrides */
+	0x0000,     /* R232 */
+	0x0000,     /* R233 - State Machine status */
+	0x1200,     /* R234 */
+	0x0000,     /* R235 */
+	0x8000,     /* R236 */
+	0x0000,     /* R237 */
+	0x0000,     /* R238 */
+	0x0000,     /* R239 */
+	0x0003,     /* R240 */
+	0x0000,     /* R241 */
+	0x0000,     /* R242 */
+	0x0004,     /* R243 */
+	0x0300,     /* R244 */
+	0x0000,     /* R245 */
+	0x0200,     /* R246 */
+	0x0000,     /* R247 */
+	0x1000,     /* R248 - DCDC1 Test Controls */
+	0x5000,     /* R249 */
+	0x1000,     /* R250 - DCDC3 Test Controls */
+	0x1000,     /* R251 - DCDC4 Test Controls */
+	0x5100,     /* R252 */
+	0x1000,     /* R253 - DCDC6 Test Controls */
+};
+#endif
+
+#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_3
+
+#undef WM8350_HAVE_CONFIG_MODE
+#define WM8350_HAVE_CONFIG_MODE
+
+const u16 wm8352_mode3_defaults[] = {
+	0x6143,     /* R0   - Reset/ID */
+	0x0000,     /* R1   - ID */
+	0x0002,     /* R2   - Revision */
+	0x1C02,     /* R3   - System Control 1 */
+	0x0204,     /* R4   - System Control 2 */
+	0x0000,     /* R5   - System Hibernate */
+	0x8A00,     /* R6   - Interface Control */
+	0x0000,     /* R7 */
+	0x8000,     /* R8   - Power mgmt (1) */
+	0x0000,     /* R9   - Power mgmt (2) */
+	0x0000,     /* R10  - Power mgmt (3) */
+	0x2000,     /* R11  - Power mgmt (4) */
+	0x0E00,     /* R12  - Power mgmt (5) */
+	0x0000,     /* R13  - Power mgmt (6) */
+	0x0000,     /* R14  - Power mgmt (7) */
+	0x0000,     /* R15 */
+	0x0000,     /* R16  - RTC Seconds/Minutes */
+	0x0100,     /* R17  - RTC Hours/Day */
+	0x0101,     /* R18  - RTC Date/Month */
+	0x1400,     /* R19  - RTC Year */
+	0x0000,     /* R20  - Alarm Seconds/Minutes */
+	0x0000,     /* R21  - Alarm Hours/Day */
+	0x0000,     /* R22  - Alarm Date/Month */
+	0x0320,     /* R23  - RTC Time Control */
+	0x0000,     /* R24  - System Interrupts */
+	0x0000,     /* R25  - Interrupt Status 1 */
+	0x0000,     /* R26  - Interrupt Status 2 */
+	0x0000,     /* R27 */
+	0x0000,     /* R28  - Under Voltage Interrupt status */
+	0x0000,     /* R29  - Over Current Interrupt status */
+	0x0000,     /* R30  - GPIO Interrupt Status */
+	0x0000,     /* R31  - Comparator Interrupt Status */
+	0x3FFF,     /* R32  - System Interrupts Mask */
+	0x0000,     /* R33  - Interrupt Status 1 Mask */
+	0x0000,     /* R34  - Interrupt Status 2 Mask */
+	0x0000,     /* R35 */
+	0x0000,     /* R36  - Under Voltage Interrupt status Mask */
+	0x0000,     /* R37  - Over Current Interrupt status Mask */
+	0x0000,     /* R38  - GPIO Interrupt Status Mask */
+	0x0000,     /* R39  - Comparator Interrupt Status Mask */
+	0x0040,     /* R40  - Clock Control 1 */
+	0x0000,     /* R41  - Clock Control 2 */
+	0x3A00,     /* R42  - FLL Control 1 */
+	0x7086,     /* R43  - FLL Control 2 */
+	0xC226,     /* R44  - FLL Control 3 */
+	0x0000,     /* R45  - FLL Control 4 */
+	0x0000,     /* R46 */
+	0x0000,     /* R47 */
+	0x0000,     /* R48  - DAC Control */
+	0x0000,     /* R49 */
+	0x00C0,     /* R50  - DAC Digital Volume L */
+	0x00C0,     /* R51  - DAC Digital Volume R */
+	0x0000,     /* R52 */
+	0x0040,     /* R53  - DAC LR Rate */
+	0x0000,     /* R54  - DAC Clock Control */
+	0x0000,     /* R55 */
+	0x0000,     /* R56 */
+	0x0000,     /* R57 */
+	0x4000,     /* R58  - DAC Mute */
+	0x0000,     /* R59  - DAC Mute Volume */
+	0x0000,     /* R60  - DAC Side */
+	0x0000,     /* R61 */
+	0x0000,     /* R62 */
+	0x0000,     /* R63 */
+	0x8000,     /* R64  - ADC Control */
+	0x0000,     /* R65 */
+	0x00C0,     /* R66  - ADC Digital Volume L */
+	0x00C0,     /* R67  - ADC Digital Volume R */
+	0x0000,     /* R68  - ADC Divider */
+	0x0000,     /* R69 */
+	0x0040,     /* R70  - ADC LR Rate */
+	0x0000,     /* R71 */
+	0x0303,     /* R72  - Input Control */
+	0x0000,     /* R73  - IN3 Input Control */
+	0x0000,     /* R74  - Mic Bias Control */
+	0x0000,     /* R75 */
+	0x0000,     /* R76  - Output Control */
+	0x0000,     /* R77  - Jack Detect */
+	0x0000,     /* R78  - Anti Pop Control */
+	0x0000,     /* R79 */
+	0x0040,     /* R80  - Left Input Volume */
+	0x0040,     /* R81  - Right Input Volume */
+	0x0000,     /* R82 */
+	0x0000,     /* R83 */
+	0x0000,     /* R84 */
+	0x0000,     /* R85 */
+	0x0000,     /* R86 */
+	0x0000,     /* R87 */
+	0x0800,     /* R88  - Left Mixer Control */
+	0x1000,     /* R89  - Right Mixer Control */
+	0x0000,     /* R90 */
+	0x0000,     /* R91 */
+	0x0000,     /* R92  - OUT3 Mixer Control */
+	0x0000,     /* R93  - OUT4 Mixer Control */
+	0x0000,     /* R94 */
+	0x0000,     /* R95 */
+	0x0000,     /* R96  - Output Left Mixer Volume */
+	0x0000,     /* R97  - Output Right Mixer Volume */
+	0x0000,     /* R98  - Input Mixer Volume L */
+	0x0000,     /* R99  - Input Mixer Volume R */
+	0x0000,     /* R100 - Input Mixer Volume */
+	0x0000,     /* R101 */
+	0x0000,     /* R102 */
+	0x0000,     /* R103 */
+	0x00E4,     /* R104 - OUT1L Volume */
+	0x00E4,     /* R105 - OUT1R Volume */
+	0x00E4,     /* R106 - OUT2L Volume */
+	0x02E4,     /* R107 - OUT2R Volume */
+	0x0000,     /* R108 */
+	0x0000,     /* R109 */
+	0x0000,     /* R110 */
+	0x0000,     /* R111 - BEEP Volume */
+	0x0A00,     /* R112 - AI Formating */
+	0x0000,     /* R113 - ADC DAC COMP */
+	0x0020,     /* R114 - AI ADC Control */
+	0x0020,     /* R115 - AI DAC Control */
+	0x0000,     /* R116 */
+	0x0000,     /* R117 */
+	0x0000,     /* R118 */
+	0x0000,     /* R119 */
+	0x0000,     /* R120 */
+	0x0000,     /* R121 */
+	0x0000,     /* R122 */
+	0x0000,     /* R123 */
+	0x0000,     /* R124 */
+	0x0000,     /* R125 */
+	0x0000,     /* R126 */
+	0x0000,     /* R127 */
+	0x1FFF,     /* R128 - GPIO Debounce */
+	0x0010,     /* R129 - GPIO Pin pull up Control */
+	0x0000,     /* R130 - GPIO Pull down Control */
+	0x0000,     /* R131 - GPIO Interrupt Mode */
+	0x0000,     /* R132 */
+	0x0000,     /* R133 - GPIO Control */
+	0x0BFB,     /* R134 - GPIO Configuration (i/o) */
+	0x0FFD,     /* R135 - GPIO Pin Polarity / Type */
+	0x0000,     /* R136 */
+	0x0000,     /* R137 */
+	0x0000,     /* R138 */
+	0x0000,     /* R139 */
+	0x0310,     /* R140 - GPIO Function Select 1 */
+	0x0001,     /* R141 - GPIO Function Select 2 */
+	0x2300,     /* R142 - GPIO Function Select 3 */
+	0x0003,     /* R143 - GPIO Function Select 4 */
+	0x0000,     /* R144 - Digitiser Control (1) */
+	0x0002,     /* R145 - Digitiser Control (2) */
+	0x0000,     /* R146 */
+	0x0000,     /* R147 */
+	0x0000,     /* R148 */
+	0x0000,     /* R149 */
+	0x0000,     /* R150 */
+	0x0000,     /* R151 */
+	0x7000,     /* R152 - AUX1 Readback */
+	0x7000,     /* R153 - AUX2 Readback */
+	0x7000,     /* R154 - AUX3 Readback */
+	0x7000,     /* R155 - AUX4 Readback */
+	0x0000,     /* R156 - USB Voltage Readback */
+	0x0000,     /* R157 - LINE Voltage Readback */
+	0x0000,     /* R158 - BATT Voltage Readback */
+	0x0000,     /* R159 - Chip Temp Readback */
+	0x0000,     /* R160 */
+	0x0000,     /* R161 */
+	0x0000,     /* R162 */
+	0x0000,     /* R163 - Generic Comparator Control */
+	0x0000,     /* R164 - Generic comparator 1 */
+	0x0000,     /* R165 - Generic comparator 2 */
+	0x0000,     /* R166 - Generic comparator 3 */
+	0x0000,     /* R167 - Generic comparator 4 */
+	0xA00F,     /* R168 - Battery Charger Control 1 */
+	0x0B06,     /* R169 - Battery Charger Control 2 */
+	0x0000,     /* R170 - Battery Charger Control 3 */
+	0x0000,     /* R171 */
+	0x0000,     /* R172 - Current Sink Driver A */
+	0x0000,     /* R173 - CSA Flash control */
+	0x0000,     /* R174 - Current Sink Driver B */
+	0x0000,     /* R175 - CSB Flash control */
+	0x0000,     /* R176 - DCDC/LDO requested */
+	0x032D,     /* R177 - DCDC Active options */
+	0x0000,     /* R178 - DCDC Sleep options */
+	0x0025,     /* R179 - Power-check comparator */
+	0x0006,     /* R180 - DCDC1 Control */
+	0x0400,     /* R181 - DCDC1 Timeouts */
+	0x1006,     /* R182 - DCDC1 Low Power */
+	0x0018,     /* R183 - DCDC2 Control */
+	0x0000,     /* R184 - DCDC2 Timeouts */
+	0x0000,     /* R185 */
+	0x0050,     /* R186 - DCDC3 Control */
+	0x0C00,     /* R187 - DCDC3 Timeouts */
+	0x0006,     /* R188 - DCDC3 Low Power */
+	0x000E,     /* R189 - DCDC4 Control */
+	0x0400,     /* R190 - DCDC4 Timeouts */
+	0x0006,     /* R191 - DCDC4 Low Power */
+	0x0008,     /* R192 - DCDC5 Control */
+	0x0000,     /* R193 - DCDC5 Timeouts */
+	0x0000,     /* R194 */
+	0x0029,     /* R195 - DCDC6 Control */
+	0x0800,     /* R196 - DCDC6 Timeouts */
+	0x0006,     /* R197 - DCDC6 Low Power */
+	0x0000,     /* R198 */
+	0x0003,     /* R199 - Limit Switch Control */
+	0x001D,     /* R200 - LDO1 Control */
+	0x1000,     /* R201 - LDO1 Timeouts */
+	0x001C,     /* R202 - LDO1 Low Power */
+	0x0017,     /* R203 - LDO2 Control */
+	0x1000,     /* R204 - LDO2 Timeouts */
+	0x001C,     /* R205 - LDO2 Low Power */
+	0x0006,     /* R206 - LDO3 Control */
+	0x1000,     /* R207 - LDO3 Timeouts */
+	0x001C,     /* R208 - LDO3 Low Power */
+	0x0010,     /* R209 - LDO4 Control */
+	0x1000,     /* R210 - LDO4 Timeouts */
+	0x001C,     /* R211 - LDO4 Low Power */
+	0x0000,     /* R212 */
+	0x0000,     /* R213 */
+	0x0000,     /* R214 */
+	0x0000,     /* R215 - VCC_FAULT Masks */
+	0x001F,     /* R216 - Main Bandgap Control */
+	0x0000,     /* R217 - OSC Control */
+	0x9000,     /* R218 - RTC Tick Control */
+	0x0000,     /* R219 - Security1 */
+	0x4000,     /* R220 */
+	0x0000,     /* R221 */
+	0x0000,     /* R222 */
+	0x0000,     /* R223 */
+	0x0000,     /* R224 - Signal overrides */
+	0x0000,     /* R225 - DCDC/LDO status */
+	0x0000,     /* R226 - Charger Overides/status */
+	0x0000,     /* R227 - misc overrides */
+	0x0000,     /* R228 - Supply overrides/status 1 */
+	0x0000,     /* R229 - Supply overrides/status 2 */
+	0xE000,     /* R230 - GPIO Pin Status */
+	0x0000,     /* R231 - comparotor overrides */
+	0x0000,     /* R232 */
+	0x0000,     /* R233 - State Machine status */
+	0x1200,     /* R234 */
+	0x0000,     /* R235 */
+	0x8000,     /* R236 */
+	0x0000,     /* R237 */
+	0x0000,     /* R238 */
+	0x0000,     /* R239 */
+	0x0003,     /* R240 */
+	0x0000,     /* R241 */
+	0x0000,     /* R242 */
+	0x0004,     /* R243 */
+	0x0300,     /* R244 */
+	0x0000,     /* R245 */
+	0x0200,     /* R246 */
+	0x0000,     /* R247 */
+	0x1000,     /* R248 - DCDC1 Test Controls */
+	0x5000,     /* R249 */
+	0x1000,     /* R250 - DCDC3 Test Controls */
+	0x1000,     /* R251 - DCDC4 Test Controls */
+	0x5100,     /* R252 */
+	0x1000,     /* R253 - DCDC6 Test Controls */
+};
+#endif
+
+/*
+ * Access masks.
+ */
+
+const struct wm8350_reg_access wm8350_reg_io_map[] = {
+	/*  read    write volatile */
+	{ 0xFFFF, 0xFFFF, 0xFFFF }, /* R0   - Reset/ID */
+	{ 0x7CFF, 0x0C00, 0x7FFF }, /* R1   - ID */
+	{ 0x007F, 0x0000, 0x0000 }, /* R2   - ROM Mask ID */
+	{ 0xBE3B, 0xBE3B, 0x8000 }, /* R3   - System Control 1 */
+	{ 0xFEF7, 0xFEF7, 0xF800 }, /* R4   - System Control 2 */
+	{ 0x80FF, 0x80FF, 0x8000 }, /* R5   - System Hibernate */
+	{ 0xFB0E, 0xFB0E, 0x0000 }, /* R6   - Interface Control */
+	{ 0x0000, 0x0000, 0x0000 }, /* R7 */
+	{ 0xE537, 0xE537, 0xFFFF }, /* R8   - Power mgmt (1) */
+	{ 0x0FF3, 0x0FF3, 0xFFFF }, /* R9   - Power mgmt (2) */
+	{ 0x008F, 0x008F, 0xFFFF }, /* R10  - Power mgmt (3) */
+	{ 0x6D3C, 0x6D3C, 0xFFFF }, /* R11  - Power mgmt (4) */
+	{ 0x1F8F, 0x1F8F, 0xFFFF }, /* R12  - Power mgmt (5) */
+	{ 0x8F3F, 0x8F3F, 0xFFFF }, /* R13  - Power mgmt (6) */
+	{ 0x0003, 0x0003, 0xFFFF }, /* R14  - Power mgmt (7) */
+	{ 0x0000, 0x0000, 0x0000 }, /* R15 */
+	{ 0x7F7F, 0x7F7F, 0xFFFF }, /* R16  - RTC Seconds/Minutes */
+	{ 0x073F, 0x073F, 0xFFFF }, /* R17  - RTC Hours/Day */
+	{ 0x1F3F, 0x1F3F, 0xFFFF }, /* R18  - RTC Date/Month */
+	{ 0x3FFF, 0x00FF, 0xFFFF }, /* R19  - RTC Year */
+	{ 0x7F7F, 0x7F7F, 0x0000 }, /* R20  - Alarm Seconds/Minutes */
+	{ 0x0F3F, 0x0F3F, 0x0000 }, /* R21  - Alarm Hours/Day */
+	{ 0x1F3F, 0x1F3F, 0x0000 }, /* R22  - Alarm Date/Month */
+	{ 0xEF7F, 0xEA7F, 0xFFFF }, /* R23  - RTC Time Control */
+	{ 0x3BFF, 0x0000, 0xFFFF }, /* R24  - System Interrupts */
+	{ 0xFEE7, 0x0000, 0xFFFF }, /* R25  - Interrupt Status 1 */
+	{ 0x35FF, 0x0000, 0xFFFF }, /* R26  - Interrupt Status 2 */
+	{ 0x0F3F, 0x0000, 0xFFFF }, /* R27  - Power Up Interrupt Status */
+	{ 0x0F3F, 0x0000, 0xFFFF }, /* R28  - Under Voltage Interrupt status */
+	{ 0x8000, 0x0000, 0xFFFF }, /* R29  - Over Current Interrupt status */
+	{ 0x1FFF, 0x0000, 0xFFFF }, /* R30  - GPIO Interrupt Status */
+	{ 0xEF7F, 0x0000, 0xFFFF }, /* R31  - Comparator Interrupt Status */
+	{ 0x3FFF, 0x3FFF, 0x0000 }, /* R32  - System Interrupts Mask */
+	{ 0xFEE7, 0xFEE7, 0x0000 }, /* R33  - Interrupt Status 1 Mask */
+	{ 0xF5FF, 0xF5FF, 0x0000 }, /* R34  - Interrupt Status 2 Mask */
+	{ 0x0F3F, 0x0F3F, 0x0000 }, /* R35  - Power Up Interrupt Status Mask */
+	{ 0x0F3F, 0x0F3F, 0x0000 }, /* R36  - Under Voltage Int status Mask */
+	{ 0x8000, 0x8000, 0x0000 }, /* R37  - Over Current Int status Mask */
+	{ 0x1FFF, 0x1FFF, 0x0000 }, /* R38  - GPIO Interrupt Status Mask */
+	{ 0xEF7F, 0xEF7F, 0x0000 }, /* R39  - Comparator IntStatus Mask */
+	{ 0xC9F7, 0xC9F7, 0xFFFF }, /* R40  - Clock Control 1 */
+	{ 0x8001, 0x8001, 0x0000 }, /* R41  - Clock Control 2 */
+	{ 0xFFF7, 0xFFF7, 0xFFFF }, /* R42  - FLL Control 1 */
+	{ 0xFBFF, 0xFBFF, 0x0000 }, /* R43  - FLL Control 2 */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R44  - FLL Control 3 */
+	{ 0x0033, 0x0033, 0x0000 }, /* R45  - FLL Control 4 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R46 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R47 */
+	{ 0x3033, 0x3033, 0x0000 }, /* R48  - DAC Control */
+	{ 0x0000, 0x0000, 0x0000 }, /* R49 */
+	{ 0x81FF, 0x81FF, 0xFFFF }, /* R50  - DAC Digital Volume L */
+	{ 0x81FF, 0x81FF, 0xFFFF }, /* R51  - DAC Digital Volume R */
+	{ 0x0000, 0x0000, 0x0000 }, /* R52 */
+	{ 0x0FFF, 0x0FFF, 0xFFFF }, /* R53  - DAC LR Rate */
+	{ 0x0017, 0x0017, 0x0000 }, /* R54  - DAC Clock Control */
+	{ 0x0000, 0x0000, 0x0000 }, /* R55 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R56 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R57 */
+	{ 0x4000, 0x4000, 0x0000 }, /* R58  - DAC Mute */
+	{ 0x7000, 0x7000, 0x0000 }, /* R59  - DAC Mute Volume */
+	{ 0x3C00, 0x3C00, 0x0000 }, /* R60  - DAC Side */
+	{ 0x0000, 0x0000, 0x0000 }, /* R61 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R62 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R63 */
+	{ 0x8303, 0x8303, 0xFFFF }, /* R64  - ADC Control */
+	{ 0x0000, 0x0000, 0x0000 }, /* R65 */
+	{ 0x81FF, 0x81FF, 0xFFFF }, /* R66  - ADC Digital Volume L */
+	{ 0x81FF, 0x81FF, 0xFFFF }, /* R67  - ADC Digital Volume R */
+	{ 0x0FFF, 0x0FFF, 0x0000 }, /* R68  - ADC Divider */
+	{ 0x0000, 0x0000, 0x0000 }, /* R69 */
+	{ 0x0FFF, 0x0FFF, 0xFFFF }, /* R70  - ADC LR Rate */
+	{ 0x0000, 0x0000, 0x0000 }, /* R71 */
+	{ 0x0707, 0x0707, 0xFFFF }, /* R72  - Input Control */
+	{ 0xC0C0, 0xC0C0, 0xFFFF }, /* R73  - IN3 Input Control */
+	{ 0xC09F, 0xC09F, 0xFFFF }, /* R74  - Mic Bias Control */
+	{ 0x0000, 0x0000, 0x0000 }, /* R75 */
+	{ 0x0F15, 0x0F15, 0xFFFF }, /* R76  - Output Control */
+	{ 0xC000, 0xC000, 0xFFFF }, /* R77  - Jack Detect */
+	{ 0x03FF, 0x03FF, 0x0000 }, /* R78  - Anti Pop Control */
+	{ 0x0000, 0x0000, 0x0000 }, /* R79 */
+	{ 0xE1FC, 0xE1FC, 0x8000 }, /* R80  - Left Input Volume */
+	{ 0xE1FC, 0xE1FC, 0x8000 }, /* R81  - Right Input Volume */
+	{ 0x0000, 0x0000, 0x0000 }, /* R82 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R83 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R84 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R85 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R86 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R87 */
+	{ 0x9807, 0x9807, 0xFFFF }, /* R88  - Left Mixer Control */
+	{ 0x980B, 0x980B, 0xFFFF }, /* R89  - Right Mixer Control */
+	{ 0x0000, 0x0000, 0x0000 }, /* R90 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R91 */
+	{ 0x8909, 0x8909, 0xFFFF }, /* R92  - OUT3 Mixer Control */
+	{ 0x9E07, 0x9E07, 0xFFFF }, /* R93  - OUT4 Mixer Control */
+	{ 0x0000, 0x0000, 0x0000 }, /* R94 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R95 */
+	{ 0x0EEE, 0x0EEE, 0x0000 }, /* R96  - Output Left Mixer Volume */
+	{ 0xE0EE, 0xE0EE, 0x0000 }, /* R97  - Output Right Mixer Volume */
+	{ 0x0E0F, 0x0E0F, 0x0000 }, /* R98  - Input Mixer Volume L */
+	{ 0xE0E1, 0xE0E1, 0x0000 }, /* R99  - Input Mixer Volume R */
+	{ 0x800E, 0x800E, 0x0000 }, /* R100 - Input Mixer Volume */
+	{ 0x0000, 0x0000, 0x0000 }, /* R101 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R102 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R103 */
+	{ 0xE1FC, 0xE1FC, 0xFFFF }, /* R104 - LOUT1 Volume */
+	{ 0xE1FC, 0xE1FC, 0xFFFF }, /* R105 - ROUT1 Volume */
+	{ 0xE1FC, 0xE1FC, 0xFFFF }, /* R106 - LOUT2 Volume */
+	{ 0xE7FC, 0xE7FC, 0xFFFF }, /* R107 - ROUT2 Volume */
+	{ 0x0000, 0x0000, 0x0000 }, /* R108 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R109 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R110 */
+	{ 0x80E0, 0x80E0, 0xFFFF }, /* R111 - BEEP Volume */
+	{ 0xBF00, 0xBF00, 0x0000 }, /* R112 - AI Formating */
+	{ 0x00F1, 0x00F1, 0x0000 }, /* R113 - ADC DAC COMP */
+	{ 0x00F8, 0x00F8, 0x0000 }, /* R114 - AI ADC Control */
+	{ 0x40FB, 0x40FB, 0x0000 }, /* R115 - AI DAC Control */
+	{ 0x7C30, 0x7C30, 0x0000 }, /* R116 - AIF Test */
+	{ 0x0000, 0x0000, 0x0000 }, /* R117 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R118 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R119 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R120 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R121 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R122 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R123 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R124 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R125 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R126 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R127 */
+	{ 0x1FFF, 0x1FFF, 0x0000 }, /* R128 - GPIO Debounce */
+	{ 0x1FFF, 0x1FFF, 0x0000 }, /* R129 - GPIO Pin pull up Control */
+	{ 0x1FFF, 0x1FFF, 0x0000 }, /* R130 - GPIO Pull down Control */
+	{ 0x1FFF, 0x1FFF, 0x0000 }, /* R131 - GPIO Interrupt Mode */
+	{ 0x0000, 0x0000, 0x0000 }, /* R132 */
+	{ 0x00C0, 0x00C0, 0x0000 }, /* R133 - GPIO Control */
+	{ 0x1FFF, 0x1FFF, 0x0000 }, /* R134 - GPIO Configuration (i/o) */
+	{ 0x1FFF, 0x1FFF, 0x0000 }, /* R135 - GPIO Pin Polarity / Type */
+	{ 0x0000, 0x0000, 0x0000 }, /* R136 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R137 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R138 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R139 */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R140 - GPIO Function Select 1 */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R141 - GPIO Function Select 2 */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R142 - GPIO Function Select 3 */
+	{ 0x000F, 0x000F, 0x0000 }, /* R143 - GPIO Function Select 4 */
+	{ 0xF0FF, 0xF0FF, 0xA000 }, /* R144 - Digitiser Control (1) */
+	{ 0x3707, 0x3707, 0x0000 }, /* R145 - Digitiser Control (2) */
+	{ 0x0000, 0x0000, 0x0000 }, /* R146 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R147 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R148 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R149 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R150 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R151 */
+	{ 0x7FFF, 0x7000, 0xFFFF }, /* R152 - AUX1 Readback */
+	{ 0x7FFF, 0x7000, 0xFFFF }, /* R153 - AUX2 Readback */
+	{ 0x7FFF, 0x7000, 0xFFFF }, /* R154 - AUX3 Readback */
+	{ 0x7FFF, 0x7000, 0xFFFF }, /* R155 - AUX4 Readback */
+	{ 0x0FFF, 0x0000, 0xFFFF }, /* R156 - USB Voltage Readback */
+	{ 0x0FFF, 0x0000, 0xFFFF }, /* R157 - LINE Voltage Readback */
+	{ 0x0FFF, 0x0000, 0xFFFF }, /* R158 - BATT Voltage Readback */
+	{ 0x0FFF, 0x0000, 0xFFFF }, /* R159 - Chip Temp Readback */
+	{ 0x0000, 0x0000, 0x0000 }, /* R160 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R161 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R162 */
+	{ 0x000F, 0x000F, 0x0000 }, /* R163 - Generic Comparator Control */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R164 - Generic comparator 1 */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R165 - Generic comparator 2 */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R166 - Generic comparator 3 */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R167 - Generic comparator 4 */
+	{ 0xBFFF, 0xBFFF, 0x8000 }, /* R168 - Battery Charger Control 1 */
+	{ 0xFFFF, 0x4FFF, 0xB000 }, /* R169 - Battery Charger Control 2 */
+	{ 0x007F, 0x007F, 0x0000 }, /* R170 - Battery Charger Control 3 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R171 */
+	{ 0x903F, 0x903F, 0xFFFF }, /* R172 - Current Sink Driver A */
+	{ 0xE333, 0xE333, 0xFFFF }, /* R173 - CSA Flash control */
+	{ 0x903F, 0x903F, 0xFFFF }, /* R174 - Current Sink Driver B */
+	{ 0xE333, 0xE333, 0xFFFF }, /* R175 - CSB Flash control */
+	{ 0x8F3F, 0x8F3F, 0xFFFF }, /* R176 - DCDC/LDO requested */
+	{ 0x332D, 0x332D, 0x0000 }, /* R177 - DCDC Active options */
+	{ 0x002D, 0x002D, 0x0000 }, /* R178 - DCDC Sleep options */
+	{ 0x5177, 0x5177, 0x8000 }, /* R179 - Power-check comparator */
+	{ 0x047F, 0x047F, 0x0000 }, /* R180 - DCDC1 Control */
+	{ 0xFFC0, 0xFFC0, 0x0000 }, /* R181 - DCDC1 Timeouts */
+	{ 0x737F, 0x737F, 0x0000 }, /* R182 - DCDC1 Low Power */
+	{ 0x535B, 0x535B, 0x0000 }, /* R183 - DCDC2 Control */
+	{ 0xFFC0, 0xFFC0, 0x0000 }, /* R184 - DCDC2 Timeouts */
+	{ 0x0000, 0x0000, 0x0000 }, /* R185 */
+	{ 0x047F, 0x047F, 0x0000 }, /* R186 - DCDC3 Control */
+	{ 0xFFC0, 0xFFC0, 0x0000 }, /* R187 - DCDC3 Timeouts */
+	{ 0x737F, 0x737F, 0x0000 }, /* R188 - DCDC3 Low Power */
+	{ 0x047F, 0x047F, 0x0000 }, /* R189 - DCDC4 Control */
+	{ 0xFFC0, 0xFFC0, 0x0000 }, /* R190 - DCDC4 Timeouts */
+	{ 0x737F, 0x737F, 0x0000 }, /* R191 - DCDC4 Low Power */
+	{ 0x535B, 0x535B, 0x0000 }, /* R192 - DCDC5 Control */
+	{ 0xFFC0, 0xFFC0, 0x0000 }, /* R193 - DCDC5 Timeouts */
+	{ 0x0000, 0x0000, 0x0000 }, /* R194 */
+	{ 0x047F, 0x047F, 0x0000 }, /* R195 - DCDC6 Control */
+	{ 0xFFC0, 0xFFC0, 0x0000 }, /* R196 - DCDC6 Timeouts */
+	{ 0x737F, 0x737F, 0x0000 }, /* R197 - DCDC6 Low Power */
+	{ 0x0000, 0x0000, 0x0000 }, /* R198 */
+	{ 0xFFD3, 0xFFD3, 0x0000 }, /* R199 - Limit Switch Control */
+	{ 0x441F, 0x441F, 0x0000 }, /* R200 - LDO1 Control */
+	{ 0xFFC0, 0xFFC0, 0x0000 }, /* R201 - LDO1 Timeouts */
+	{ 0x331F, 0x331F, 0x0000 }, /* R202 - LDO1 Low Power */
+	{ 0x441F, 0x441F, 0x0000 }, /* R203 - LDO2 Control */
+	{ 0xFFC0, 0xFFC0, 0x0000 }, /* R204 - LDO2 Timeouts */
+	{ 0x331F, 0x331F, 0x0000 }, /* R205 - LDO2 Low Power */
+	{ 0x441F, 0x441F, 0x0000 }, /* R206 - LDO3 Control */
+	{ 0xFFC0, 0xFFC0, 0x0000 }, /* R207 - LDO3 Timeouts */
+	{ 0x331F, 0x331F, 0x0000 }, /* R208 - LDO3 Low Power */
+	{ 0x441F, 0x441F, 0x0000 }, /* R209 - LDO4 Control */
+	{ 0xFFC0, 0xFFC0, 0x0000 }, /* R210 - LDO4 Timeouts */
+	{ 0x331F, 0x331F, 0x0000 }, /* R211 - LDO4 Low Power */
+	{ 0x0000, 0x0000, 0x0000 }, /* R212 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R213 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R214 */
+	{ 0x8F3F, 0x8F3F, 0x0000 }, /* R215 - VCC_FAULT Masks */
+	{ 0xFF3F, 0xE03F, 0x0000 }, /* R216 - Main Bandgap Control */
+	{ 0xEF2F, 0xE02F, 0x0000 }, /* R217 - OSC Control */
+	{ 0xF3FF, 0xB3FF, 0xc000 }, /* R218 - RTC Tick Control */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R219 - Security */
+	{ 0x09FF, 0x01FF, 0x0000 }, /* R220 - RAM BIST 1 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R221 */
+	{ 0xFFFF, 0xFFFF, 0xFFFF }, /* R222 */
+	{ 0xFFFF, 0xFFFF, 0xFFFF }, /* R223 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R224 */
+	{ 0x8F3F, 0x0000, 0xFFFF }, /* R225 - DCDC/LDO status */
+	{ 0x0000, 0x0000, 0xFFFF }, /* R226 - Charger status */
+	{ 0x34FE, 0x0000, 0xFFFF }, /* R227 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R228 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R229 */
+	{ 0xFFFF, 0x1FFF, 0xFFFF }, /* R230 - GPIO Pin Status */
+	{ 0xFFFF, 0x1FFF, 0xFFFF }, /* R231 */
+	{ 0xFFFF, 0x1FFF, 0xFFFF }, /* R232 */
+	{ 0xFFFF, 0x1FFF, 0xFFFF }, /* R233 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R234 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R235 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R236 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R237 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R238 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R239 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R240 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R241 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R242 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R243 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R244 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R245 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R246 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R247 */
+	{ 0xFFFF, 0x0010, 0xFFFF }, /* R248 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R249 */
+	{ 0xFFFF, 0x0010, 0xFFFF }, /* R250 */
+	{ 0xFFFF, 0x0010, 0xFFFF }, /* R251 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R252 */
+	{ 0xFFFF, 0x0010, 0xFFFF }, /* R253 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R254 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R255 */
+};
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/wm8400-core.c b/ap/os/linux/linux-3.4.x/drivers/mfd/wm8400-core.c
new file mode 100644
index 0000000..1189a17
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/wm8400-core.c
@@ -0,0 +1,420 @@
+/*
+ * Core driver for WM8400.
+ *
+ * Copyright 2008 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/bug.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/wm8400-private.h>
+#include <linux/mfd/wm8400-audio.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+static struct {
+	u16  readable;    /* Mask of readable bits */
+	u16  writable;    /* Mask of writable bits */
+	u16  vol;         /* Mask of volatile bits */
+	int  is_codec;    /* Register controlled by codec reset */
+	u16  default_val; /* Value on reset */
+} reg_data[] = {
+	{ 0xFFFF, 0xFFFF, 0x0000, 0, 0x6172 }, /* R0 */
+	{ 0x7000, 0x0000, 0x8000, 0, 0x0000 }, /* R1 */
+	{ 0xFF17, 0xFF17, 0x0000, 0, 0x0000 }, /* R2 */
+	{ 0xEBF3, 0xEBF3, 0x0000, 1, 0x6000 }, /* R3 */
+	{ 0x3CF3, 0x3CF3, 0x0000, 1, 0x0000 }, /* R4  */
+	{ 0xF1F8, 0xF1F8, 0x0000, 1, 0x4050 }, /* R5  */
+	{ 0xFC1F, 0xFC1F, 0x0000, 1, 0x4000 }, /* R6  */
+	{ 0xDFDE, 0xDFDE, 0x0000, 1, 0x01C8 }, /* R7  */
+	{ 0xFCFC, 0xFCFC, 0x0000, 1, 0x0000 }, /* R8  */
+	{ 0xEFFF, 0xEFFF, 0x0000, 1, 0x0040 }, /* R9  */
+	{ 0xEFFF, 0xEFFF, 0x0000, 1, 0x0040 }, /* R10 */
+	{ 0x27F7, 0x27F7, 0x0000, 1, 0x0004 }, /* R11 */
+	{ 0x01FF, 0x01FF, 0x0000, 1, 0x00C0 }, /* R12 */
+	{ 0x01FF, 0x01FF, 0x0000, 1, 0x00C0 }, /* R13 */
+	{ 0x1FEF, 0x1FEF, 0x0000, 1, 0x0000 }, /* R14 */
+	{ 0x0163, 0x0163, 0x0000, 1, 0x0100 }, /* R15 */
+	{ 0x01FF, 0x01FF, 0x0000, 1, 0x00C0 }, /* R16 */
+	{ 0x01FF, 0x01FF, 0x0000, 1, 0x00C0 }, /* R17 */
+	{ 0x1FFF, 0x0FFF, 0x0000, 1, 0x0000 }, /* R18 */
+	{ 0xFFFF, 0xFFFF, 0x0000, 1, 0x1000 }, /* R19 */
+	{ 0xFFFF, 0xFFFF, 0x0000, 1, 0x1010 }, /* R20 */
+	{ 0xFFFF, 0xFFFF, 0x0000, 1, 0x1010 }, /* R21 */
+	{ 0x0FDD, 0x0FDD, 0x0000, 1, 0x8000 }, /* R22 */
+	{ 0x1FFF, 0x1FFF, 0x0000, 1, 0x0800 }, /* R23 */
+	{ 0x0000, 0x01DF, 0x0000, 1, 0x008B }, /* R24 */
+	{ 0x0000, 0x01DF, 0x0000, 1, 0x008B }, /* R25 */
+	{ 0x0000, 0x01DF, 0x0000, 1, 0x008B }, /* R26 */
+	{ 0x0000, 0x01DF, 0x0000, 1, 0x008B }, /* R27 */
+	{ 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R28 */
+	{ 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R29 */
+	{ 0x0000, 0x0077, 0x0000, 1, 0x0066 }, /* R30 */
+	{ 0x0000, 0x0033, 0x0000, 1, 0x0022 }, /* R31 */
+	{ 0x0000, 0x01FF, 0x0000, 1, 0x0079 }, /* R32 */
+	{ 0x0000, 0x01FF, 0x0000, 1, 0x0079 }, /* R33 */
+	{ 0x0000, 0x0003, 0x0000, 1, 0x0003 }, /* R34 */
+	{ 0x0000, 0x01FF, 0x0000, 1, 0x0003 }, /* R35 */
+	{ 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R36 */
+	{ 0x0000, 0x003F, 0x0000, 1, 0x0100 }, /* R37 */
+	{ 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R38 */
+	{ 0x0000, 0x000F, 0x0000, 0, 0x0000 }, /* R39 */
+	{ 0x0000, 0x00FF, 0x0000, 1, 0x0000 }, /* R40 */
+	{ 0x0000, 0x01B7, 0x0000, 1, 0x0000 }, /* R41 */
+	{ 0x0000, 0x01B7, 0x0000, 1, 0x0000 }, /* R42 */
+	{ 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R43 */
+	{ 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R44 */
+	{ 0x0000, 0x00FD, 0x0000, 1, 0x0000 }, /* R45 */
+	{ 0x0000, 0x00FD, 0x0000, 1, 0x0000 }, /* R46 */
+	{ 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R47 */
+	{ 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R48 */
+	{ 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R49 */
+	{ 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R50 */
+	{ 0x0000, 0x01B3, 0x0000, 1, 0x0180 }, /* R51 */
+	{ 0x0000, 0x0077, 0x0000, 1, 0x0000 }, /* R52 */
+	{ 0x0000, 0x0077, 0x0000, 1, 0x0000 }, /* R53 */
+	{ 0x0000, 0x00FF, 0x0000, 1, 0x0000 }, /* R54 */
+	{ 0x0000, 0x0001, 0x0000, 1, 0x0000 }, /* R55 */
+	{ 0x0000, 0x003F, 0x0000, 1, 0x0000 }, /* R56 */
+	{ 0x0000, 0x004F, 0x0000, 1, 0x0000 }, /* R57 */
+	{ 0x0000, 0x00FD, 0x0000, 1, 0x0000 }, /* R58 */
+	{ 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R59 */
+	{ 0x1FFF, 0x1FFF, 0x0000, 1, 0x0000 }, /* R60 */
+	{ 0xFFFF, 0xFFFF, 0x0000, 1, 0x0000 }, /* R61 */
+	{ 0x03FF, 0x03FF, 0x0000, 1, 0x0000 }, /* R62 */
+	{ 0x007F, 0x007F, 0x0000, 1, 0x0000 }, /* R63 */
+	{ 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R64 */
+	{ 0xDFFF, 0xDFFF, 0x0000, 0, 0x0000 }, /* R65 */
+	{ 0xDFFF, 0xDFFF, 0x0000, 0, 0x0000 }, /* R66 */
+	{ 0xDFFF, 0xDFFF, 0x0000, 0, 0x0000 }, /* R67 */
+	{ 0xDFFF, 0xDFFF, 0x0000, 0, 0x0000 }, /* R68 */
+	{ 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R69 */
+	{ 0xFFFF, 0xFFFF, 0x0000, 0, 0x4400 }, /* R70 */
+	{ 0x23FF, 0x23FF, 0x0000, 0, 0x0000 }, /* R71 */
+	{ 0xFFFF, 0xFFFF, 0x0000, 0, 0x4400 }, /* R72 */
+	{ 0x23FF, 0x23FF, 0x0000, 0, 0x0000 }, /* R73 */
+	{ 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R74 */
+	{ 0x000E, 0x000E, 0x0000, 0, 0x0008 }, /* R75 */
+	{ 0xE00F, 0xE00F, 0x0000, 0, 0x0000 }, /* R76 */
+	{ 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R77 */
+	{ 0x03C0, 0x03C0, 0x0000, 0, 0x02C0 }, /* R78 */
+	{ 0xFFFF, 0x0000, 0xffff, 0, 0x0000 }, /* R79 */
+	{ 0xFFFF, 0xFFFF, 0x0000, 0, 0x0000 }, /* R80 */
+	{ 0xFFFF, 0x0000, 0xffff, 0, 0x0000 }, /* R81 */
+	{ 0x2BFF, 0x0000, 0xffff, 0, 0x0000 }, /* R82 */
+	{ 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R83 */
+	{ 0x80FF, 0x80FF, 0x0000, 0, 0x00ff }, /* R84 */
+};
+
+static int wm8400_read(struct wm8400 *wm8400, u8 reg, int num_regs, u16 *dest)
+{
+	int i, ret = 0;
+
+	BUG_ON(reg + num_regs > ARRAY_SIZE(wm8400->reg_cache));
+
+	/* If there are any volatile reads then read back the entire block */
+	for (i = reg; i < reg + num_regs; i++)
+		if (reg_data[i].vol) {
+			ret = regmap_bulk_read(wm8400->regmap, reg, dest,
+					       num_regs);
+			return ret;
+		}
+
+	/* Otherwise use the cache */
+	memcpy(dest, &wm8400->reg_cache[reg], num_regs * sizeof(u16));
+
+	return 0;
+}
+
+static int wm8400_write(struct wm8400 *wm8400, u8 reg, int num_regs,
+			u16 *src)
+{
+	int ret, i;
+
+	BUG_ON(reg + num_regs > ARRAY_SIZE(wm8400->reg_cache));
+
+	for (i = 0; i < num_regs; i++) {
+		BUG_ON(!reg_data[reg + i].writable);
+		wm8400->reg_cache[reg + i] = src[i];
+		ret = regmap_write(wm8400->regmap, reg, src[i]);
+		if (ret != 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * wm8400_reg_read - Single register read
+ *
+ * @wm8400: Pointer to wm8400 control structure
+ * @reg:    Register to read
+ *
+ * @return  Read value
+ */
+u16 wm8400_reg_read(struct wm8400 *wm8400, u8 reg)
+{
+	u16 val;
+
+	mutex_lock(&wm8400->io_lock);
+
+	wm8400_read(wm8400, reg, 1, &val);
+
+	mutex_unlock(&wm8400->io_lock);
+
+	return val;
+}
+EXPORT_SYMBOL_GPL(wm8400_reg_read);
+
+int wm8400_block_read(struct wm8400 *wm8400, u8 reg, int count, u16 *data)
+{
+	int ret;
+
+	mutex_lock(&wm8400->io_lock);
+
+	ret = wm8400_read(wm8400, reg, count, data);
+
+	mutex_unlock(&wm8400->io_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wm8400_block_read);
+
+/**
+ * wm8400_set_bits - Bitmask write
+ *
+ * @wm8400: Pointer to wm8400 control structure
+ * @reg:    Register to access
+ * @mask:   Mask of bits to change
+ * @val:    Value to set for masked bits
+ */
+int wm8400_set_bits(struct wm8400 *wm8400, u8 reg, u16 mask, u16 val)
+{
+	u16 tmp;
+	int ret;
+
+	mutex_lock(&wm8400->io_lock);
+
+	ret = wm8400_read(wm8400, reg, 1, &tmp);
+	tmp = (tmp & ~mask) | val;
+	if (ret == 0)
+		ret = wm8400_write(wm8400, reg, 1, &tmp);
+
+	mutex_unlock(&wm8400->io_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wm8400_set_bits);
+
+/**
+ * wm8400_reset_codec_reg_cache - Reset cached codec registers to
+ * their default values.
+ */
+void wm8400_reset_codec_reg_cache(struct wm8400 *wm8400)
+{
+	int i;
+
+	mutex_lock(&wm8400->io_lock);
+
+	/* Reset all codec registers to their initial value */
+	for (i = 0; i < ARRAY_SIZE(wm8400->reg_cache); i++)
+		if (reg_data[i].is_codec)
+			wm8400->reg_cache[i] = reg_data[i].default_val;
+
+	mutex_unlock(&wm8400->io_lock);
+}
+EXPORT_SYMBOL_GPL(wm8400_reset_codec_reg_cache);
+
+static int wm8400_register_codec(struct wm8400 *wm8400)
+{
+	struct mfd_cell cell = {
+		.name = "wm8400-codec",
+		.platform_data = wm8400,
+		.pdata_size = sizeof(*wm8400),
+	};
+
+	return mfd_add_devices(wm8400->dev, -1, &cell, 1, NULL, 0);
+}
+
+/*
+ * wm8400_init - Generic initialisation
+ *
+ * The WM8400 can be configured as either an I2C or SPI device.  Probe
+ * functions for each bus set up the accessors then call into this to
+ * set up the device itself.
+ */
+static int wm8400_init(struct wm8400 *wm8400,
+		       struct wm8400_platform_data *pdata)
+{
+	u16 reg;
+	int ret, i;
+
+	mutex_init(&wm8400->io_lock);
+
+	dev_set_drvdata(wm8400->dev, wm8400);
+
+	/* Check that this is actually a WM8400 */
+	ret = regmap_read(wm8400->regmap, WM8400_RESET_ID, &i);
+	if (ret != 0) {
+		dev_err(wm8400->dev, "Chip ID register read failed\n");
+		return -EIO;
+	}
+	if (i != reg_data[WM8400_RESET_ID].default_val) {
+		dev_err(wm8400->dev, "Device is not a WM8400, ID is %x\n", i);
+		return -ENODEV;
+	}
+
+	/* We don't know what state the hardware is in and since this
+	 * is a PMIC we can't reset it safely so initialise the register
+	 * cache from the hardware.
+	 */
+	ret = regmap_raw_read(wm8400->regmap, 0, wm8400->reg_cache,
+			      ARRAY_SIZE(wm8400->reg_cache));
+	if (ret != 0) {
+		dev_err(wm8400->dev, "Register cache read failed\n");
+		return -EIO;
+	}
+	for (i = 0; i < ARRAY_SIZE(wm8400->reg_cache); i++)
+		wm8400->reg_cache[i] = be16_to_cpu(wm8400->reg_cache[i]);
+
+	/* If the codec is in reset use hard coded values */
+	if (!(wm8400->reg_cache[WM8400_POWER_MANAGEMENT_1] & WM8400_CODEC_ENA))
+		for (i = 0; i < ARRAY_SIZE(wm8400->reg_cache); i++)
+			if (reg_data[i].is_codec)
+				wm8400->reg_cache[i] = reg_data[i].default_val;
+
+	ret = wm8400_read(wm8400, WM8400_ID, 1, &reg);
+	if (ret != 0) {
+		dev_err(wm8400->dev, "ID register read failed: %d\n", ret);
+		return ret;
+	}
+	reg = (reg & WM8400_CHIP_REV_MASK) >> WM8400_CHIP_REV_SHIFT;
+	dev_info(wm8400->dev, "WM8400 revision %x\n", reg);
+
+	ret = wm8400_register_codec(wm8400);
+	if (ret != 0) {
+		dev_err(wm8400->dev, "Failed to register codec\n");
+		goto err_children;
+	}
+
+	if (pdata && pdata->platform_init) {
+		ret = pdata->platform_init(wm8400->dev);
+		if (ret != 0) {
+			dev_err(wm8400->dev, "Platform init failed: %d\n",
+				ret);
+			goto err_children;
+		}
+	} else
+		dev_warn(wm8400->dev, "No platform initialisation supplied\n");
+
+	return 0;
+
+err_children:
+	mfd_remove_devices(wm8400->dev);
+	return ret;
+}
+
+static void wm8400_release(struct wm8400 *wm8400)
+{
+	mfd_remove_devices(wm8400->dev);
+}
+
+static const struct regmap_config wm8400_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 16,
+	.max_register = WM8400_REGISTER_COUNT - 1,
+};
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+static int wm8400_i2c_probe(struct i2c_client *i2c,
+			    const struct i2c_device_id *id)
+{
+	struct wm8400 *wm8400;
+	int ret;
+
+	wm8400 = devm_kzalloc(&i2c->dev, sizeof(struct wm8400), GFP_KERNEL);
+	if (wm8400 == NULL) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	wm8400->regmap = devm_regmap_init_i2c(i2c, &wm8400_regmap_config);
+	if (IS_ERR(wm8400->regmap)) {
+		ret = PTR_ERR(wm8400->regmap);
+		goto err;
+	}
+
+	wm8400->dev = &i2c->dev;
+	i2c_set_clientdata(i2c, wm8400);
+
+	ret = wm8400_init(wm8400, i2c->dev.platform_data);
+	if (ret != 0)
+		goto err;
+
+	return 0;
+
+err:
+	return ret;
+}
+
+static int wm8400_i2c_remove(struct i2c_client *i2c)
+{
+	struct wm8400 *wm8400 = i2c_get_clientdata(i2c);
+
+	wm8400_release(wm8400);
+
+	return 0;
+}
+
+static const struct i2c_device_id wm8400_i2c_id[] = {
+       { "wm8400", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8400_i2c_id);
+
+static struct i2c_driver wm8400_i2c_driver = {
+	.driver = {
+		.name = "WM8400",
+		.owner = THIS_MODULE,
+	},
+	.probe    = wm8400_i2c_probe,
+	.remove   = wm8400_i2c_remove,
+	.id_table = wm8400_i2c_id,
+};
+#endif
+
+static int __init wm8400_module_init(void)
+{
+	int ret = -ENODEV;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	ret = i2c_add_driver(&wm8400_i2c_driver);
+	if (ret != 0)
+		pr_err("Failed to register I2C driver: %d\n", ret);
+#endif
+
+	return ret;
+}
+subsys_initcall(wm8400_module_init);
+
+static void __exit wm8400_module_exit(void)
+{
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	i2c_del_driver(&wm8400_i2c_driver);
+#endif
+}
+module_exit(wm8400_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/wm8994-core.c b/ap/os/linux/linux-3.4.x/drivers/mfd/wm8994-core.c
new file mode 100644
index 0000000..8d46e68
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/wm8994-core.c
@@ -0,0 +1,756 @@
+/*
+ * wm8994-core.c  --  Device access for Wolfson WM8994
+ *
+ * Copyright 2009 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/mfd/core.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/machine.h>
+
+#include <linux/mfd/wm8994/core.h>
+#include <linux/mfd/wm8994/pdata.h>
+#include <linux/mfd/wm8994/registers.h>
+
+#include "wm8994.h"
+
+/**
+ * wm8994_reg_read: Read a single WM8994 register.
+ *
+ * @wm8994: Device to read from.
+ * @reg: Register to read.
+ */
+int wm8994_reg_read(struct wm8994 *wm8994, unsigned short reg)
+{
+	unsigned int val;
+	int ret;
+
+	ret = regmap_read(wm8994->regmap, reg, &val);
+
+	if (ret < 0)
+		return ret;
+	else
+		return val;
+}
+EXPORT_SYMBOL_GPL(wm8994_reg_read);
+
+/**
+ * wm8994_bulk_read: Read multiple WM8994 registers
+ *
+ * @wm8994: Device to read from
+ * @reg: First register
+ * @count: Number of registers
+ * @buf: Buffer to fill.  The data will be returned big endian.
+ */
+int wm8994_bulk_read(struct wm8994 *wm8994, unsigned short reg,
+		     int count, u16 *buf)
+{
+	return regmap_bulk_read(wm8994->regmap, reg, buf, count);
+}
+
+/**
+ * wm8994_reg_write: Write a single WM8994 register.
+ *
+ * @wm8994: Device to write to.
+ * @reg: Register to write to.
+ * @val: Value to write.
+ */
+int wm8994_reg_write(struct wm8994 *wm8994, unsigned short reg,
+		     unsigned short val)
+{
+	return regmap_write(wm8994->regmap, reg, val);
+}
+EXPORT_SYMBOL_GPL(wm8994_reg_write);
+
+/**
+ * wm8994_bulk_write: Write multiple WM8994 registers
+ *
+ * @wm8994: Device to write to
+ * @reg: First register
+ * @count: Number of registers
+ * @buf: Buffer to write from.  Data must be big-endian formatted.
+ */
+int wm8994_bulk_write(struct wm8994 *wm8994, unsigned short reg,
+		      int count, const u16 *buf)
+{
+	return regmap_raw_write(wm8994->regmap, reg, buf, count * sizeof(u16));
+}
+EXPORT_SYMBOL_GPL(wm8994_bulk_write);
+
+/**
+ * wm8994_set_bits: Set the value of a bitfield in a WM8994 register
+ *
+ * @wm8994: Device to write to.
+ * @reg: Register to write to.
+ * @mask: Mask of bits to set.
+ * @val: Value to set (unshifted)
+ */
+int wm8994_set_bits(struct wm8994 *wm8994, unsigned short reg,
+		    unsigned short mask, unsigned short val)
+{
+	return regmap_update_bits(wm8994->regmap, reg, mask, val);
+}
+EXPORT_SYMBOL_GPL(wm8994_set_bits);
+
+static struct mfd_cell wm8994_regulator_devs[] = {
+	{
+		.name = "wm8994-ldo",
+		.id = 1,
+		.pm_runtime_no_callbacks = true,
+	},
+	{
+		.name = "wm8994-ldo",
+		.id = 2,
+		.pm_runtime_no_callbacks = true,
+	},
+};
+
+static struct resource wm8994_codec_resources[] = {
+	{
+		.start = WM8994_IRQ_TEMP_SHUT,
+		.end   = WM8994_IRQ_TEMP_WARN,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct resource wm8994_gpio_resources[] = {
+	{
+		.start = WM8994_IRQ_GPIO(1),
+		.end   = WM8994_IRQ_GPIO(11),
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct mfd_cell wm8994_devs[] = {
+	{
+		.name = "wm8994-codec",
+		.num_resources = ARRAY_SIZE(wm8994_codec_resources),
+		.resources = wm8994_codec_resources,
+	},
+
+	{
+		.name = "wm8994-gpio",
+		.num_resources = ARRAY_SIZE(wm8994_gpio_resources),
+		.resources = wm8994_gpio_resources,
+		.pm_runtime_no_callbacks = true,
+	},
+};
+
+/*
+ * Supplies for the main bulk of CODEC; the LDO supplies are ignored
+ * and should be handled via the standard regulator API supply
+ * management.
+ */
+static const char *wm1811_main_supplies[] = {
+	"DBVDD1",
+	"DBVDD2",
+	"DBVDD3",
+	"DCVDD",
+	"AVDD1",
+	"AVDD2",
+	"CPVDD",
+	"SPKVDD1",
+	"SPKVDD2",
+};
+
+static const char *wm8994_main_supplies[] = {
+	"DBVDD",
+	"DCVDD",
+	"AVDD1",
+	"AVDD2",
+	"CPVDD",
+	"SPKVDD1",
+	"SPKVDD2",
+};
+
+static const char *wm8958_main_supplies[] = {
+	"DBVDD1",
+	"DBVDD2",
+	"DBVDD3",
+	"DCVDD",
+	"AVDD1",
+	"AVDD2",
+	"CPVDD",
+	"SPKVDD1",
+	"SPKVDD2",
+};
+
+#ifdef CONFIG_PM
+static int wm8994_suspend(struct device *dev)
+{
+	struct wm8994 *wm8994 = dev_get_drvdata(dev);
+	int ret;
+
+	/* Don't actually go through with the suspend if the CODEC is
+	 * still active (eg, for audio passthrough from CP. */
+	ret = wm8994_reg_read(wm8994, WM8994_POWER_MANAGEMENT_1);
+	if (ret < 0) {
+		dev_err(dev, "Failed to read power status: %d\n", ret);
+	} else if (ret & WM8994_VMID_SEL_MASK) {
+		dev_dbg(dev, "CODEC still active, ignoring suspend\n");
+		return 0;
+	}
+
+	ret = wm8994_reg_read(wm8994, WM8994_POWER_MANAGEMENT_4);
+	if (ret < 0) {
+		dev_err(dev, "Failed to read power status: %d\n", ret);
+	} else if (ret & (WM8994_AIF2ADCL_ENA | WM8994_AIF2ADCR_ENA |
+			  WM8994_AIF1ADC2L_ENA | WM8994_AIF1ADC2R_ENA |
+			  WM8994_AIF1ADC1L_ENA | WM8994_AIF1ADC1R_ENA)) {
+		dev_dbg(dev, "CODEC still active, ignoring suspend\n");
+		return 0;
+	}
+
+	ret = wm8994_reg_read(wm8994, WM8994_POWER_MANAGEMENT_5);
+	if (ret < 0) {
+		dev_err(dev, "Failed to read power status: %d\n", ret);
+	} else if (ret & (WM8994_AIF2DACL_ENA | WM8994_AIF2DACR_ENA |
+			  WM8994_AIF1DAC2L_ENA | WM8994_AIF1DAC2R_ENA |
+			  WM8994_AIF1DAC1L_ENA | WM8994_AIF1DAC1R_ENA)) {
+		dev_dbg(dev, "CODEC still active, ignoring suspend\n");
+		return 0;
+	}
+
+	switch (wm8994->type) {
+	case WM8958:
+	case WM1811:
+		ret = wm8994_reg_read(wm8994, WM8958_MIC_DETECT_1);
+		if (ret < 0) {
+			dev_err(dev, "Failed to read power status: %d\n", ret);
+		} else if (ret & WM8958_MICD_ENA) {
+			dev_dbg(dev, "CODEC still active, ignoring suspend\n");
+			return 0;
+		}
+		break;
+	default:
+		break;
+	}
+
+	switch (wm8994->type) {
+	case WM1811:
+		ret = wm8994_reg_read(wm8994, WM8994_ANTIPOP_2);
+		if (ret < 0) {
+			dev_err(dev, "Failed to read jackdet: %d\n", ret);
+		} else if (ret & WM1811_JACKDET_MODE_MASK) {
+			dev_dbg(dev, "CODEC still active, ignoring suspend\n");
+			return 0;
+		}
+		break;
+	default:
+		break;
+	}
+
+	switch (wm8994->type) {
+	case WM1811:
+		ret = wm8994_reg_read(wm8994, WM8994_ANTIPOP_2);
+		if (ret < 0) {
+			dev_err(dev, "Failed to read jackdet: %d\n", ret);
+		} else if (ret & WM1811_JACKDET_MODE_MASK) {
+			dev_dbg(dev, "CODEC still active, ignoring suspend\n");
+			return 0;
+		}
+		break;
+	default:
+		break;
+	}
+
+	/* Disable LDO pulldowns while the device is suspended if we
+	 * don't know that something will be driving them. */
+	if (!wm8994->ldo_ena_always_driven)
+		wm8994_set_bits(wm8994, WM8994_PULL_CONTROL_2,
+				WM8994_LDO1ENA_PD | WM8994_LDO2ENA_PD,
+				WM8994_LDO1ENA_PD | WM8994_LDO2ENA_PD);
+
+	/* Explicitly put the device into reset in case regulators
+	 * don't get disabled in order to ensure consistent restart.
+	 */
+	wm8994_reg_write(wm8994, WM8994_SOFTWARE_RESET,
+			 wm8994_reg_read(wm8994, WM8994_SOFTWARE_RESET));
+
+	regcache_cache_only(wm8994->regmap, true);
+	regcache_mark_dirty(wm8994->regmap);
+
+	wm8994->suspended = true;
+
+	ret = regulator_bulk_disable(wm8994->num_supplies,
+				     wm8994->supplies);
+	if (ret != 0) {
+		dev_err(dev, "Failed to disable supplies: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int wm8994_resume(struct device *dev)
+{
+	struct wm8994 *wm8994 = dev_get_drvdata(dev);
+	int ret;
+
+	/* We may have lied to the PM core about suspending */
+	if (!wm8994->suspended)
+		return 0;
+
+	ret = regulator_bulk_enable(wm8994->num_supplies,
+				    wm8994->supplies);
+	if (ret != 0) {
+		dev_err(dev, "Failed to enable supplies: %d\n", ret);
+		return ret;
+	}
+
+	regcache_cache_only(wm8994->regmap, false);
+	ret = regcache_sync(wm8994->regmap);
+	if (ret != 0) {
+		dev_err(dev, "Failed to restore register map: %d\n", ret);
+		goto err_enable;
+	}
+
+	/* Disable LDO pulldowns while the device is active */
+	wm8994_set_bits(wm8994, WM8994_PULL_CONTROL_2,
+			WM8994_LDO1ENA_PD | WM8994_LDO2ENA_PD,
+			0);
+
+	wm8994->suspended = false;
+
+	return 0;
+
+err_enable:
+	regulator_bulk_disable(wm8994->num_supplies, wm8994->supplies);
+
+	return ret;
+}
+#endif
+
+#ifdef CONFIG_REGULATOR
+static int wm8994_ldo_in_use(struct wm8994_pdata *pdata, int ldo)
+{
+	struct wm8994_ldo_pdata *ldo_pdata;
+
+	if (!pdata)
+		return 0;
+
+	ldo_pdata = &pdata->ldo[ldo];
+
+	if (!ldo_pdata->init_data)
+		return 0;
+
+	return ldo_pdata->init_data->num_consumer_supplies != 0;
+}
+#else
+static int wm8994_ldo_in_use(struct wm8994_pdata *pdata, int ldo)
+{
+	return 0;
+}
+#endif
+
+static const __devinitdata struct reg_default wm8994_revc_patch[] = {
+	{ 0x102, 0x3 },
+	{ 0x56, 0x3 },
+	{ 0x817, 0x0 },
+	{ 0x102, 0x0 },
+};
+
+static const __devinitdata struct reg_default wm8958_reva_patch[] = {
+	{ 0x102, 0x3 },
+	{ 0xcb, 0x81 },
+	{ 0x817, 0x0 },
+	{ 0x102, 0x0 },
+};
+
+static const __devinitdata struct reg_default wm1811_reva_patch[] = {
+	{ 0x102, 0x3 },
+	{ 0x56, 0x7 },
+	{ 0x5d, 0x7e },
+	{ 0x5e, 0x0 },
+	{ 0x102, 0x0 },
+};
+
+/*
+ * Instantiate the generic non-control parts of the device.
+ */
+static __devinit int wm8994_device_init(struct wm8994 *wm8994, int irq)
+{
+	struct wm8994_pdata *pdata = wm8994->dev->platform_data;
+	struct regmap_config *regmap_config;
+	const struct reg_default *regmap_patch = NULL;
+	const char *devname;
+	int ret, i, patch_regs;
+	int pulls = 0;
+
+	dev_set_drvdata(wm8994->dev, wm8994);
+
+	/* Add the on-chip regulators first for bootstrapping */
+	ret = mfd_add_devices(wm8994->dev, -1,
+			      wm8994_regulator_devs,
+			      ARRAY_SIZE(wm8994_regulator_devs),
+			      NULL, 0);
+	if (ret != 0) {
+		dev_err(wm8994->dev, "Failed to add children: %d\n", ret);
+		goto err;
+	}
+
+	switch (wm8994->type) {
+	case WM1811:
+		wm8994->num_supplies = ARRAY_SIZE(wm1811_main_supplies);
+		break;
+	case WM8994:
+		wm8994->num_supplies = ARRAY_SIZE(wm8994_main_supplies);
+		break;
+	case WM8958:
+		wm8994->num_supplies = ARRAY_SIZE(wm8958_main_supplies);
+		break;
+	default:
+		BUG();
+		goto err;
+	}
+
+	wm8994->supplies = devm_kzalloc(wm8994->dev,
+					sizeof(struct regulator_bulk_data) *
+					wm8994->num_supplies, GFP_KERNEL);
+	if (!wm8994->supplies) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	switch (wm8994->type) {
+	case WM1811:
+		for (i = 0; i < ARRAY_SIZE(wm1811_main_supplies); i++)
+			wm8994->supplies[i].supply = wm1811_main_supplies[i];
+		break;
+	case WM8994:
+		for (i = 0; i < ARRAY_SIZE(wm8994_main_supplies); i++)
+			wm8994->supplies[i].supply = wm8994_main_supplies[i];
+		break;
+	case WM8958:
+		for (i = 0; i < ARRAY_SIZE(wm8958_main_supplies); i++)
+			wm8994->supplies[i].supply = wm8958_main_supplies[i];
+		break;
+	default:
+		BUG();
+		goto err;
+	}
+		
+	ret = regulator_bulk_get(wm8994->dev, wm8994->num_supplies,
+				 wm8994->supplies);
+	if (ret != 0) {
+		dev_err(wm8994->dev, "Failed to get supplies: %d\n", ret);
+		goto err;
+	}
+
+	ret = regulator_bulk_enable(wm8994->num_supplies,
+				    wm8994->supplies);
+	if (ret != 0) {
+		dev_err(wm8994->dev, "Failed to enable supplies: %d\n", ret);
+		goto err_get;
+	}
+
+	ret = wm8994_reg_read(wm8994, WM8994_SOFTWARE_RESET);
+	if (ret < 0) {
+		dev_err(wm8994->dev, "Failed to read ID register\n");
+		goto err_enable;
+	}
+	switch (ret) {
+	case 0x1811:
+		devname = "WM1811";
+		if (wm8994->type != WM1811)
+			dev_warn(wm8994->dev, "Device registered as type %d\n",
+				 wm8994->type);
+		wm8994->type = WM1811;
+		break;
+	case 0x8994:
+		devname = "WM8994";
+		if (wm8994->type != WM8994)
+			dev_warn(wm8994->dev, "Device registered as type %d\n",
+				 wm8994->type);
+		wm8994->type = WM8994;
+		break;
+	case 0x8958:
+		devname = "WM8958";
+		if (wm8994->type != WM8958)
+			dev_warn(wm8994->dev, "Device registered as type %d\n",
+				 wm8994->type);
+		wm8994->type = WM8958;
+		break;
+	default:
+		dev_err(wm8994->dev, "Device is not a WM8994, ID is %x\n",
+			ret);
+		ret = -EINVAL;
+		goto err_enable;
+	}
+
+	ret = wm8994_reg_read(wm8994, WM8994_CHIP_REVISION);
+	if (ret < 0) {
+		dev_err(wm8994->dev, "Failed to read revision register: %d\n",
+			ret);
+		goto err_enable;
+	}
+	wm8994->revision = ret;
+
+	switch (wm8994->type) {
+	case WM8994:
+		switch (wm8994->revision) {
+		case 0:
+		case 1:
+			dev_warn(wm8994->dev,
+				 "revision %c not fully supported\n",
+				 'A' + wm8994->revision);
+			break;
+		case 2:
+		case 3:
+			regmap_patch = wm8994_revc_patch;
+			patch_regs = ARRAY_SIZE(wm8994_revc_patch);
+			break;
+		default:
+			break;
+		}
+		break;
+
+	case WM8958:
+		switch (wm8994->revision) {
+		case 0:
+			regmap_patch = wm8958_reva_patch;
+			patch_regs = ARRAY_SIZE(wm8958_reva_patch);
+			break;
+		default:
+			break;
+		}
+		break;
+
+	case WM1811:
+		/* Revision C did not change the relevant layer */
+		if (wm8994->revision > 1)
+			wm8994->revision++;
+		switch (wm8994->revision) {
+		case 0:
+		case 1:
+		case 2:
+		case 3:
+		case 4:
+			regmap_patch = wm1811_reva_patch;
+			patch_regs = ARRAY_SIZE(wm1811_reva_patch);
+			break;
+		default:
+			break;
+		}
+		break;
+
+	default:
+		break;
+	}
+
+	dev_info(wm8994->dev, "%s revision %c\n", devname,
+		 'A' + wm8994->revision);
+
+	switch (wm8994->type) {
+	case WM1811:
+		regmap_config = &wm1811_regmap_config;
+		break;
+	case WM8994:
+		regmap_config = &wm8994_regmap_config;
+		break;
+	case WM8958:
+		regmap_config = &wm8958_regmap_config;
+		break;
+	default:
+		dev_err(wm8994->dev, "Unknown device type %d\n", wm8994->type);
+		return -EINVAL;
+	}
+
+	ret = regmap_reinit_cache(wm8994->regmap, regmap_config);
+	if (ret != 0) {
+		dev_err(wm8994->dev, "Failed to reinit register cache: %d\n",
+			ret);
+		return ret;
+	}
+
+	if (regmap_patch) {
+		ret = regmap_register_patch(wm8994->regmap, regmap_patch,
+					    patch_regs);
+		if (ret != 0) {
+			dev_err(wm8994->dev, "Failed to register patch: %d\n",
+				ret);
+			goto err;
+		}
+	}
+
+	if (pdata) {
+		wm8994->irq_base = pdata->irq_base;
+		wm8994->gpio_base = pdata->gpio_base;
+
+		/* GPIO configuration is only applied if it's non-zero */
+		for (i = 0; i < ARRAY_SIZE(pdata->gpio_defaults); i++) {
+			if (pdata->gpio_defaults[i]) {
+				wm8994_set_bits(wm8994, WM8994_GPIO_1 + i,
+						0xffff,
+						pdata->gpio_defaults[i]);
+			}
+		}
+
+		wm8994->ldo_ena_always_driven = pdata->ldo_ena_always_driven;
+
+		if (pdata->spkmode_pu)
+			pulls |= WM8994_SPKMODE_PU;
+	}
+
+	/* Disable unneeded pulls */
+	wm8994_set_bits(wm8994, WM8994_PULL_CONTROL_2,
+			WM8994_LDO1ENA_PD | WM8994_LDO2ENA_PD |
+			WM8994_SPKMODE_PU | WM8994_CSNADDR_PD,
+			pulls);
+
+	/* In some system designs where the regulators are not in use,
+	 * we can achieve a small reduction in leakage currents by
+	 * floating LDO outputs.  This bit makes no difference if the
+	 * LDOs are enabled, it only affects cases where the LDOs were
+	 * in operation and are then disabled.
+	 */
+	for (i = 0; i < WM8994_NUM_LDO_REGS; i++) {
+		if (wm8994_ldo_in_use(pdata, i))
+			wm8994_set_bits(wm8994, WM8994_LDO_1 + i,
+					WM8994_LDO1_DISCH, WM8994_LDO1_DISCH);
+		else
+			wm8994_set_bits(wm8994, WM8994_LDO_1 + i,
+					WM8994_LDO1_DISCH, 0);
+	}
+
+	wm8994_irq_init(wm8994);
+
+	ret = mfd_add_devices(wm8994->dev, -1,
+			      wm8994_devs, ARRAY_SIZE(wm8994_devs),
+			      NULL, 0);
+	if (ret != 0) {
+		dev_err(wm8994->dev, "Failed to add children: %d\n", ret);
+		goto err_irq;
+	}
+
+	pm_runtime_enable(wm8994->dev);
+	pm_runtime_idle(wm8994->dev);
+
+	return 0;
+
+err_irq:
+	wm8994_irq_exit(wm8994);
+err_enable:
+	regulator_bulk_disable(wm8994->num_supplies,
+			       wm8994->supplies);
+err_get:
+	regulator_bulk_free(wm8994->num_supplies, wm8994->supplies);
+err:
+	mfd_remove_devices(wm8994->dev);
+	return ret;
+}
+
+static __devexit void wm8994_device_exit(struct wm8994 *wm8994)
+{
+	pm_runtime_disable(wm8994->dev);
+	mfd_remove_devices(wm8994->dev);
+	wm8994_irq_exit(wm8994);
+	regulator_bulk_disable(wm8994->num_supplies,
+			       wm8994->supplies);
+	regulator_bulk_free(wm8994->num_supplies, wm8994->supplies);
+}
+
+static const struct of_device_id wm8994_of_match[] = {
+	{ .compatible = "wlf,wm1811", },
+	{ .compatible = "wlf,wm8994", },
+	{ .compatible = "wlf,wm8958", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, wm8994_of_match);
+
+static __devinit int wm8994_i2c_probe(struct i2c_client *i2c,
+				      const struct i2c_device_id *id)
+{
+	struct wm8994 *wm8994;
+	int ret;
+
+	wm8994 = devm_kzalloc(&i2c->dev, sizeof(struct wm8994), GFP_KERNEL);
+	if (wm8994 == NULL)
+		return -ENOMEM;
+
+	i2c_set_clientdata(i2c, wm8994);
+	wm8994->dev = &i2c->dev;
+	wm8994->irq = i2c->irq;
+	wm8994->type = id->driver_data;
+
+	wm8994->regmap = devm_regmap_init_i2c(i2c, &wm8994_base_regmap_config);
+	if (IS_ERR(wm8994->regmap)) {
+		ret = PTR_ERR(wm8994->regmap);
+		dev_err(wm8994->dev, "Failed to allocate register map: %d\n",
+			ret);
+		return ret;
+	}
+
+	return wm8994_device_init(wm8994, i2c->irq);
+}
+
+static __devexit int wm8994_i2c_remove(struct i2c_client *i2c)
+{
+	struct wm8994 *wm8994 = i2c_get_clientdata(i2c);
+
+	wm8994_device_exit(wm8994);
+
+	return 0;
+}
+
+static const struct i2c_device_id wm8994_i2c_id[] = {
+	{ "wm1811", WM1811 },
+	{ "wm1811a", WM1811 },
+	{ "wm8994", WM8994 },
+	{ "wm8958", WM8958 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, wm8994_i2c_id);
+
+static UNIVERSAL_DEV_PM_OPS(wm8994_pm_ops, wm8994_suspend, wm8994_resume,
+			    NULL);
+
+static struct i2c_driver wm8994_i2c_driver = {
+	.driver = {
+		.name = "wm8994",
+		.owner = THIS_MODULE,
+		.pm = &wm8994_pm_ops,
+		.of_match_table = wm8994_of_match,
+	},
+	.probe = wm8994_i2c_probe,
+	.remove = __devexit_p(wm8994_i2c_remove),
+	.id_table = wm8994_i2c_id,
+};
+
+static int __init wm8994_i2c_init(void)
+{
+	int ret;
+
+	ret = i2c_add_driver(&wm8994_i2c_driver);
+	if (ret != 0)
+		pr_err("Failed to register wm8994 I2C driver: %d\n", ret);
+
+	return ret;
+}
+module_init(wm8994_i2c_init);
+
+static void __exit wm8994_i2c_exit(void)
+{
+	i2c_del_driver(&wm8994_i2c_driver);
+}
+module_exit(wm8994_i2c_exit);
+
+MODULE_DESCRIPTION("Core support for the WM8994 audio CODEC");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/wm8994-irq.c b/ap/os/linux/linux-3.4.x/drivers/mfd/wm8994-irq.c
new file mode 100644
index 0000000..46b20c4
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/wm8994-irq.c
@@ -0,0 +1,174 @@
+/*
+ * wm8994-irq.c  --  Interrupt controller support for Wolfson WM8994
+ *
+ * Copyright 2010 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/mfd/core.h>
+#include <linux/interrupt.h>
+#include <linux/regmap.h>
+
+#include <linux/mfd/wm8994/core.h>
+#include <linux/mfd/wm8994/registers.h>
+
+#include <linux/delay.h>
+
+static struct regmap_irq wm8994_irqs[] = {
+	[WM8994_IRQ_TEMP_SHUT] = {
+		.reg_offset = 1,
+		.mask = WM8994_TEMP_SHUT_EINT,
+	},
+	[WM8994_IRQ_MIC1_DET] = {
+		.reg_offset = 1,
+		.mask = WM8994_MIC1_DET_EINT,
+	},
+	[WM8994_IRQ_MIC1_SHRT] = {
+		.reg_offset = 1,
+		.mask = WM8994_MIC1_SHRT_EINT,
+	},
+	[WM8994_IRQ_MIC2_DET] = {
+		.reg_offset = 1,
+		.mask = WM8994_MIC2_DET_EINT,
+	},
+	[WM8994_IRQ_MIC2_SHRT] = {
+		.reg_offset = 1,
+		.mask = WM8994_MIC2_SHRT_EINT,
+	},
+	[WM8994_IRQ_FLL1_LOCK] = {
+		.reg_offset = 1,
+		.mask = WM8994_FLL1_LOCK_EINT,
+	},
+	[WM8994_IRQ_FLL2_LOCK] = {
+		.reg_offset = 1,
+		.mask = WM8994_FLL2_LOCK_EINT,
+	},
+	[WM8994_IRQ_SRC1_LOCK] = {
+		.reg_offset = 1,
+		.mask = WM8994_SRC1_LOCK_EINT,
+	},
+	[WM8994_IRQ_SRC2_LOCK] = {
+		.reg_offset = 1,
+		.mask = WM8994_SRC2_LOCK_EINT,
+	},
+	[WM8994_IRQ_AIF1DRC1_SIG_DET] = {
+		.reg_offset = 1,
+		.mask = WM8994_AIF1DRC1_SIG_DET,
+	},
+	[WM8994_IRQ_AIF1DRC2_SIG_DET] = {
+		.reg_offset = 1,
+		.mask = WM8994_AIF1DRC2_SIG_DET_EINT,
+	},
+	[WM8994_IRQ_AIF2DRC_SIG_DET] = {
+		.reg_offset = 1,
+		.mask = WM8994_AIF2DRC_SIG_DET_EINT,
+	},
+	[WM8994_IRQ_FIFOS_ERR] = {
+		.reg_offset = 1,
+		.mask = WM8994_FIFOS_ERR_EINT,
+	},
+	[WM8994_IRQ_WSEQ_DONE] = {
+		.reg_offset = 1,
+		.mask = WM8994_WSEQ_DONE_EINT,
+	},
+	[WM8994_IRQ_DCS_DONE] = {
+		.reg_offset = 1,
+		.mask = WM8994_DCS_DONE_EINT,
+	},
+	[WM8994_IRQ_TEMP_WARN] = {
+		.reg_offset = 1,
+		.mask = WM8994_TEMP_WARN_EINT,
+	},
+	[WM8994_IRQ_GPIO(1)] = {
+		.mask = WM8994_GP1_EINT,
+	},
+	[WM8994_IRQ_GPIO(2)] = {
+		.mask = WM8994_GP2_EINT,
+	},
+	[WM8994_IRQ_GPIO(3)] = {
+		.mask = WM8994_GP3_EINT,
+	},
+	[WM8994_IRQ_GPIO(4)] = {
+		.mask = WM8994_GP4_EINT,
+	},
+	[WM8994_IRQ_GPIO(5)] = {
+		.mask = WM8994_GP5_EINT,
+	},
+	[WM8994_IRQ_GPIO(6)] = {
+		.mask = WM8994_GP6_EINT,
+	},
+	[WM8994_IRQ_GPIO(7)] = {
+		.mask = WM8994_GP7_EINT,
+	},
+	[WM8994_IRQ_GPIO(8)] = {
+		.mask = WM8994_GP8_EINT,
+	},
+	[WM8994_IRQ_GPIO(9)] = {
+		.mask = WM8994_GP8_EINT,
+	},
+	[WM8994_IRQ_GPIO(10)] = {
+		.mask = WM8994_GP10_EINT,
+	},
+	[WM8994_IRQ_GPIO(11)] = {
+		.mask = WM8994_GP11_EINT,
+	},
+};
+
+static struct regmap_irq_chip wm8994_irq_chip = {
+	.name = "wm8994",
+	.irqs = wm8994_irqs,
+	.num_irqs = ARRAY_SIZE(wm8994_irqs),
+
+	.num_regs = 2,
+	.status_base = WM8994_INTERRUPT_STATUS_1,
+	.mask_base = WM8994_INTERRUPT_STATUS_1_MASK,
+	.ack_base = WM8994_INTERRUPT_STATUS_1,
+};
+
+int wm8994_irq_init(struct wm8994 *wm8994)
+{
+	int ret;
+
+	if (!wm8994->irq) {
+		dev_warn(wm8994->dev,
+			 "No interrupt specified, no interrupts\n");
+		wm8994->irq_base = 0;
+		return 0;
+	}
+
+	if (!wm8994->irq_base) {
+		dev_err(wm8994->dev,
+			"No interrupt base specified, no interrupts\n");
+		return 0;
+	}
+
+	ret = regmap_add_irq_chip(wm8994->regmap, wm8994->irq,
+				  IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+				  wm8994->irq_base, &wm8994_irq_chip,
+				  &wm8994->irq_data);
+	if (ret != 0) {
+		dev_err(wm8994->dev, "Failed to register IRQ chip: %d\n", ret);
+		return ret;
+	}
+
+	/* Enable top level interrupt if it was masked */
+	wm8994_reg_write(wm8994, WM8994_INTERRUPT_CONTROL, 0);
+
+	return 0;
+}
+
+void wm8994_irq_exit(struct wm8994 *wm8994)
+{
+	regmap_del_irq_chip(wm8994->irq, wm8994->irq_data);
+}
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/wm8994-regmap.c b/ap/os/linux/linux-3.4.x/drivers/mfd/wm8994-regmap.c
new file mode 100644
index 0000000..bfd25af
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/wm8994-regmap.c
@@ -0,0 +1,1224 @@
+/*
+ * wm8994-regmap.c  --  Register map data for WM8994 series devices
+ *
+ * Copyright 2011 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/mfd/wm8994/core.h>
+#include <linux/mfd/wm8994/registers.h>
+#include <linux/regmap.h>
+#include <linux/device.h>
+
+#include "wm8994.h"
+
+static struct reg_default wm1811_defaults[] = {
+	{ 0x0001, 0x0000 },    /* R1    - Power Management (1) */
+	{ 0x0002, 0x6000 },    /* R2    - Power Management (2) */
+	{ 0x0003, 0x0000 },    /* R3    - Power Management (3) */
+	{ 0x0004, 0x0000 },    /* R4    - Power Management (4) */
+	{ 0x0005, 0x0000 },    /* R5    - Power Management (5) */
+	{ 0x0006, 0x0000 },    /* R6    - Power Management (6) */
+	{ 0x0015, 0x0000 },    /* R21   - Input Mixer (1) */
+	{ 0x0018, 0x008B },    /* R24   - Left Line Input 1&2 Volume */
+	{ 0x0019, 0x008B },    /* R25   - Left Line Input 3&4 Volume */
+	{ 0x001A, 0x008B },    /* R26   - Right Line Input 1&2 Volume */
+	{ 0x001B, 0x008B },    /* R27   - Right Line Input 3&4 Volume */
+	{ 0x001C, 0x006D },    /* R28   - Left Output Volume */
+	{ 0x001D, 0x006D },    /* R29   - Right Output Volume */
+	{ 0x001E, 0x0066 },    /* R30   - Line Outputs Volume */
+	{ 0x001F, 0x0020 },    /* R31   - HPOUT2 Volume */
+	{ 0x0020, 0x0079 },    /* R32   - Left OPGA Volume */
+	{ 0x0021, 0x0079 },    /* R33   - Right OPGA Volume */
+	{ 0x0022, 0x0003 },    /* R34   - SPKMIXL Attenuation */
+	{ 0x0023, 0x0003 },    /* R35   - SPKMIXR Attenuation */
+	{ 0x0024, 0x0011 },    /* R36   - SPKOUT Mixers */
+	{ 0x0025, 0x0140 },    /* R37   - ClassD */
+	{ 0x0026, 0x0079 },    /* R38   - Speaker Volume Left */
+	{ 0x0027, 0x0079 },    /* R39   - Speaker Volume Right */
+	{ 0x0028, 0x0000 },    /* R40   - Input Mixer (2) */
+	{ 0x0029, 0x0000 },    /* R41   - Input Mixer (3) */
+	{ 0x002A, 0x0000 },    /* R42   - Input Mixer (4) */
+	{ 0x002B, 0x0000 },    /* R43   - Input Mixer (5) */
+	{ 0x002C, 0x0000 },    /* R44   - Input Mixer (6) */
+	{ 0x002D, 0x0000 },    /* R45   - Output Mixer (1) */
+	{ 0x002E, 0x0000 },    /* R46   - Output Mixer (2) */
+	{ 0x002F, 0x0000 },    /* R47   - Output Mixer (3) */
+	{ 0x0030, 0x0000 },    /* R48   - Output Mixer (4) */
+	{ 0x0031, 0x0000 },    /* R49   - Output Mixer (5) */
+	{ 0x0032, 0x0000 },    /* R50   - Output Mixer (6) */
+	{ 0x0033, 0x0000 },    /* R51   - HPOUT2 Mixer */
+	{ 0x0034, 0x0000 },    /* R52   - Line Mixer (1) */
+	{ 0x0035, 0x0000 },    /* R53   - Line Mixer (2) */
+	{ 0x0036, 0x0000 },    /* R54   - Speaker Mixer */
+	{ 0x0037, 0x0000 },    /* R55   - Additional Control */
+	{ 0x0038, 0x0000 },    /* R56   - AntiPOP (1) */
+	{ 0x0039, 0x0000 },    /* R57   - AntiPOP (2) */
+	{ 0x003B, 0x000D },    /* R59   - LDO 1 */
+	{ 0x003C, 0x0003 },    /* R60   - LDO 2 */
+	{ 0x003D, 0x0039 },    /* R61   - MICBIAS1 */
+	{ 0x003E, 0x0039 },    /* R62   - MICBIAS2 */
+	{ 0x004C, 0x1F25 },    /* R76   - Charge Pump (1) */
+	{ 0x004D, 0xAB19 },    /* R77   - Charge Pump (2) */
+	{ 0x0051, 0x0004 },    /* R81   - Class W (1) */
+	{ 0x0055, 0x054A },    /* R85   - DC Servo (2) */
+	{ 0x0059, 0x0000 },    /* R89   - DC Servo (4) */
+	{ 0x0060, 0x0000 },    /* R96   - Analogue HP (1) */
+	{ 0x00C5, 0x0000 },    /* R197  - Class D Test (5) */
+	{ 0x00D0, 0x7600 },    /* R208  - Mic Detect 1 */
+	{ 0x00D1, 0x007F },    /* R209  - Mic Detect 2 */
+	{ 0x0101, 0x8004 },    /* R257  - Control Interface */
+	{ 0x0200, 0x0000 },    /* R512  - AIF1 Clocking (1) */
+	{ 0x0201, 0x0000 },    /* R513  - AIF1 Clocking (2) */
+	{ 0x0204, 0x0000 },    /* R516  - AIF2 Clocking (1) */
+	{ 0x0205, 0x0000 },    /* R517  - AIF2 Clocking (2) */
+	{ 0x0208, 0x0000 },    /* R520  - Clocking (1) */
+	{ 0x0209, 0x0000 },    /* R521  - Clocking (2) */
+	{ 0x0210, 0x0083 },    /* R528  - AIF1 Rate */
+	{ 0x0211, 0x0083 },    /* R529  - AIF2 Rate */
+	{ 0x0220, 0x0000 },    /* R544  - FLL1 Control (1) */
+	{ 0x0221, 0x0000 },    /* R545  - FLL1 Control (2) */
+	{ 0x0222, 0x0000 },    /* R546  - FLL1 Control (3) */
+	{ 0x0223, 0x0000 },    /* R547  - FLL1 Control (4) */
+	{ 0x0224, 0x0C80 },    /* R548  - FLL1 Control (5) */
+	{ 0x0226, 0x0000 },    /* R550  - FLL1 EFS 1 */
+	{ 0x0227, 0x0006 },    /* R551  - FLL1 EFS 2 */
+	{ 0x0240, 0x0000 },    /* R576  - FLL2Control (1) */
+	{ 0x0241, 0x0000 },    /* R577  - FLL2Control (2) */
+	{ 0x0242, 0x0000 },    /* R578  - FLL2Control (3) */
+	{ 0x0243, 0x0000 },    /* R579  - FLL2 Control (4) */
+	{ 0x0244, 0x0C80 },    /* R580  - FLL2Control (5) */
+	{ 0x0246, 0x0000 },    /* R582  - FLL2 EFS 1 */
+	{ 0x0247, 0x0006 },    /* R583  - FLL2 EFS 2 */
+	{ 0x0300, 0x4050 },    /* R768  - AIF1 Control (1) */
+	{ 0x0301, 0x4000 },    /* R769  - AIF1 Control (2) */
+	{ 0x0302, 0x0000 },    /* R770  - AIF1 Master/Slave */
+	{ 0x0303, 0x0040 },    /* R771  - AIF1 BCLK */
+	{ 0x0304, 0x0040 },    /* R772  - AIF1ADC LRCLK */
+	{ 0x0305, 0x0040 },    /* R773  - AIF1DAC LRCLK */
+	{ 0x0306, 0x0004 },    /* R774  - AIF1DAC Data */
+	{ 0x0307, 0x0100 },    /* R775  - AIF1ADC Data */
+	{ 0x0310, 0x4050 },    /* R784  - AIF2 Control (1) */
+	{ 0x0311, 0x4000 },    /* R785  - AIF2 Control (2) */
+	{ 0x0312, 0x0000 },    /* R786  - AIF2 Master/Slave */
+	{ 0x0313, 0x0040 },    /* R787  - AIF2 BCLK */
+	{ 0x0314, 0x0040 },    /* R788  - AIF2ADC LRCLK */
+	{ 0x0315, 0x0040 },    /* R789  - AIF2DAC LRCLK */
+	{ 0x0316, 0x0000 },    /* R790  - AIF2DAC Data */
+	{ 0x0317, 0x0000 },    /* R791  - AIF2ADC Data */
+	{ 0x0318, 0x0003 },    /* R792  - AIF2TX Control */
+	{ 0x0320, 0x0040 },    /* R800  - AIF3 Control (1) */
+	{ 0x0321, 0x0000 },    /* R801  - AIF3 Control (2) */
+	{ 0x0322, 0x0000 },    /* R802  - AIF3DAC Data */
+	{ 0x0323, 0x0000 },    /* R803  - AIF3ADC Data */
+	{ 0x0400, 0x00C0 },    /* R1024 - AIF1 ADC1 Left Volume */
+	{ 0x0401, 0x00C0 },    /* R1025 - AIF1 ADC1 Right Volume */
+	{ 0x0402, 0x00C0 },    /* R1026 - AIF1 DAC1 Left Volume */
+	{ 0x0403, 0x00C0 },    /* R1027 - AIF1 DAC1 Right Volume */
+	{ 0x0410, 0x0000 },    /* R1040 - AIF1 ADC1 Filters */
+	{ 0x0420, 0x0200 },    /* R1056 - AIF1 DAC1 Filters (1) */
+	{ 0x0421, 0x0010 },    /* R1057 - AIF1 DAC1 Filters (2) */
+	{ 0x0430, 0x0068 },    /* R1072 - AIF1 DAC1 Noise Gate */
+	{ 0x0440, 0x0098 },    /* R1088 - AIF1 DRC1 (1) */
+	{ 0x0441, 0x0845 },    /* R1089 - AIF1 DRC1 (2) */
+	{ 0x0442, 0x0000 },    /* R1090 - AIF1 DRC1 (3) */
+	{ 0x0443, 0x0000 },    /* R1091 - AIF1 DRC1 (4) */
+	{ 0x0444, 0x0000 },    /* R1092 - AIF1 DRC1 (5) */
+	{ 0x0480, 0x6318 },    /* R1152 - AIF1 DAC1 EQ Gains (1) */
+	{ 0x0481, 0x6300 },    /* R1153 - AIF1 DAC1 EQ Gains (2) */
+	{ 0x0482, 0x0FCA },    /* R1154 - AIF1 DAC1 EQ Band 1 A */
+	{ 0x0483, 0x0400 },    /* R1155 - AIF1 DAC1 EQ Band 1 B */
+	{ 0x0484, 0x00D8 },    /* R1156 - AIF1 DAC1 EQ Band 1 PG */
+	{ 0x0485, 0x1EB5 },    /* R1157 - AIF1 DAC1 EQ Band 2 A */
+	{ 0x0486, 0xF145 },    /* R1158 - AIF1 DAC1 EQ Band 2 B */
+	{ 0x0487, 0x0B75 },    /* R1159 - AIF1 DAC1 EQ Band 2 C */
+	{ 0x0488, 0x01C5 },    /* R1160 - AIF1 DAC1 EQ Band 2 PG */
+	{ 0x0489, 0x1C58 },    /* R1161 - AIF1 DAC1 EQ Band 3 A */
+	{ 0x048A, 0xF373 },    /* R1162 - AIF1 DAC1 EQ Band 3 B */
+	{ 0x048B, 0x0A54 },    /* R1163 - AIF1 DAC1 EQ Band 3 C */
+	{ 0x048C, 0x0558 },    /* R1164 - AIF1 DAC1 EQ Band 3 PG */
+	{ 0x048D, 0x168E },    /* R1165 - AIF1 DAC1 EQ Band 4 A */
+	{ 0x048E, 0xF829 },    /* R1166 - AIF1 DAC1 EQ Band 4 B */
+	{ 0x048F, 0x07AD },    /* R1167 - AIF1 DAC1 EQ Band 4 C */
+	{ 0x0490, 0x1103 },    /* R1168 - AIF1 DAC1 EQ Band 4 PG */
+	{ 0x0491, 0x0564 },    /* R1169 - AIF1 DAC1 EQ Band 5 A */
+	{ 0x0492, 0x0559 },    /* R1170 - AIF1 DAC1 EQ Band 5 B */
+	{ 0x0493, 0x4000 },    /* R1171 - AIF1 DAC1 EQ Band 5 PG */
+	{ 0x0494, 0x0000 },    /* R1172 - AIF1 DAC1 EQ Band 1 C */
+	{ 0x0500, 0x00C0 },    /* R1280 - AIF2 ADC Left Volume */
+	{ 0x0501, 0x00C0 },    /* R1281 - AIF2 ADC Right Volume */
+	{ 0x0502, 0x00C0 },    /* R1282 - AIF2 DAC Left Volume */
+	{ 0x0503, 0x00C0 },    /* R1283 - AIF2 DAC Right Volume */
+	{ 0x0510, 0x0000 },    /* R1296 - AIF2 ADC Filters */
+	{ 0x0520, 0x0200 },    /* R1312 - AIF2 DAC Filters (1) */
+	{ 0x0521, 0x0010 },    /* R1313 - AIF2 DAC Filters (2) */
+	{ 0x0530, 0x0068 },    /* R1328 - AIF2 DAC Noise Gate */
+	{ 0x0540, 0x0098 },    /* R1344 - AIF2 DRC (1) */
+	{ 0x0541, 0x0845 },    /* R1345 - AIF2 DRC (2) */
+	{ 0x0542, 0x0000 },    /* R1346 - AIF2 DRC (3) */
+	{ 0x0543, 0x0000 },    /* R1347 - AIF2 DRC (4) */
+	{ 0x0544, 0x0000 },    /* R1348 - AIF2 DRC (5) */
+	{ 0x0580, 0x6318 },    /* R1408 - AIF2 EQ Gains (1) */
+	{ 0x0581, 0x6300 },    /* R1409 - AIF2 EQ Gains (2) */
+	{ 0x0582, 0x0FCA },    /* R1410 - AIF2 EQ Band 1 A */
+	{ 0x0583, 0x0400 },    /* R1411 - AIF2 EQ Band 1 B */
+	{ 0x0584, 0x00D8 },    /* R1412 - AIF2 EQ Band 1 PG */
+	{ 0x0585, 0x1EB5 },    /* R1413 - AIF2 EQ Band 2 A */
+	{ 0x0586, 0xF145 },    /* R1414 - AIF2 EQ Band 2 B */
+	{ 0x0587, 0x0B75 },    /* R1415 - AIF2 EQ Band 2 C */
+	{ 0x0588, 0x01C5 },    /* R1416 - AIF2 EQ Band 2 PG */
+	{ 0x0589, 0x1C58 },    /* R1417 - AIF2 EQ Band 3 A */
+	{ 0x058A, 0xF373 },    /* R1418 - AIF2 EQ Band 3 B */
+	{ 0x058B, 0x0A54 },    /* R1419 - AIF2 EQ Band 3 C */
+	{ 0x058C, 0x0558 },    /* R1420 - AIF2 EQ Band 3 PG */
+	{ 0x058D, 0x168E },    /* R1421 - AIF2 EQ Band 4 A */
+	{ 0x058E, 0xF829 },    /* R1422 - AIF2 EQ Band 4 B */
+	{ 0x058F, 0x07AD },    /* R1423 - AIF2 EQ Band 4 C */
+	{ 0x0590, 0x1103 },    /* R1424 - AIF2 EQ Band 4 PG */
+	{ 0x0591, 0x0564 },    /* R1425 - AIF2 EQ Band 5 A */
+	{ 0x0592, 0x0559 },    /* R1426 - AIF2 EQ Band 5 B */
+	{ 0x0593, 0x4000 },    /* R1427 - AIF2 EQ Band 5 PG */
+	{ 0x0594, 0x0000 },    /* R1428 - AIF2 EQ Band 1 C */
+	{ 0x0600, 0x0000 },    /* R1536 - DAC1 Mixer Volumes */
+	{ 0x0601, 0x0000 },    /* R1537 - DAC1 Left Mixer Routing */
+	{ 0x0602, 0x0000 },    /* R1538 - DAC1 Right Mixer Routing */
+	{ 0x0603, 0x0000 },    /* R1539 - AIF2ADC Mixer Volumes */
+	{ 0x0604, 0x0000 },    /* R1540 - AIF2ADC Left Mixer Routing */
+	{ 0x0605, 0x0000 },    /* R1541 - AIF2ADC Right Mixer Routing */
+	{ 0x0606, 0x0000 },    /* R1542 - AIF1 ADC1 Left Mixer Routing */
+	{ 0x0607, 0x0000 },    /* R1543 - AIF1 ADC1 Right Mixer Routing */
+	{ 0x0610, 0x02C0 },    /* R1552 - DAC1 Left Volume */
+	{ 0x0611, 0x02C0 },    /* R1553 - DAC1 Right Volume */
+	{ 0x0612, 0x02C0 },    /* R1554 - AIF2TX Left Volume */
+	{ 0x0613, 0x02C0 },    /* R1555 - AIF2TX Right Volume */
+	{ 0x0614, 0x0000 },    /* R1556 - DAC Softmute */
+	{ 0x0620, 0x0002 },    /* R1568 - Oversampling */
+	{ 0x0621, 0x0000 },    /* R1569 - Sidetone */
+	{ 0x0700, 0x8100 },    /* R1792 - GPIO 1 */
+	{ 0x0701, 0xA101 },    /* R1793 - Pull Control (MCLK2) */
+	{ 0x0702, 0xA101 },    /* R1794 - Pull Control (BCLK2) */
+	{ 0x0703, 0xA101 },    /* R1795 - Pull Control (DACLRCLK2) */
+	{ 0x0704, 0xA101 },    /* R1796 - Pull Control (DACDAT2) */
+	{ 0x0707, 0xA101 },    /* R1799 - GPIO 8 */
+	{ 0x0708, 0xA101 },    /* R1800 - GPIO 9 */
+	{ 0x0709, 0xA101 },    /* R1801 - GPIO 10 */
+	{ 0x070A, 0xA101 },    /* R1802 - GPIO 11 */
+	{ 0x0720, 0x0000 },    /* R1824 - Pull Control (1) */
+	{ 0x0721, 0x0156 },    /* R1825 - Pull Control (2) */
+	{ 0x0732, 0x0000 },    /* R1842 - Interrupt Raw Status 2 */
+	{ 0x0738, 0x07FF },    /* R1848 - Interrupt Status 1 Mask */
+	{ 0x0739, 0xDFEF },    /* R1849 - Interrupt Status 2 Mask */
+	{ 0x0740, 0x0000 },    /* R1856 - Interrupt Control */
+	{ 0x0748, 0x003F },    /* R1864 - IRQ Debounce */
+};
+
+static struct reg_default wm8994_defaults[] = {
+	{ 0x0001, 0x0000 },    /* R1     - Power Management (1) */ 
+	{ 0x0002, 0x6000 },    /* R2     - Power Management (2) */ 
+	{ 0x0003, 0x0000 },    /* R3     - Power Management (3) */ 
+	{ 0x0004, 0x0000 },    /* R4     - Power Management (4) */ 
+	{ 0x0005, 0x0000 },    /* R5     - Power Management (5) */ 
+	{ 0x0006, 0x0000 },    /* R6     - Power Management (6) */ 
+	{ 0x0015, 0x0000 },    /* R21    - Input Mixer (1) */ 
+	{ 0x0018, 0x008B },    /* R24    - Left Line Input 1&2 Volume */ 
+	{ 0x0019, 0x008B },    /* R25    - Left Line Input 3&4 Volume */ 
+	{ 0x001A, 0x008B },    /* R26    - Right Line Input 1&2 Volume */ 
+	{ 0x001B, 0x008B },    /* R27    - Right Line Input 3&4 Volume */ 
+	{ 0x001C, 0x006D },    /* R28    - Left Output Volume */ 
+	{ 0x001D, 0x006D },    /* R29    - Right Output Volume */ 
+	{ 0x001E, 0x0066 },    /* R30    - Line Outputs Volume */ 
+	{ 0x001F, 0x0020 },    /* R31    - HPOUT2 Volume */ 
+	{ 0x0020, 0x0079 },    /* R32    - Left OPGA Volume */ 
+	{ 0x0021, 0x0079 },    /* R33    - Right OPGA Volume */ 
+	{ 0x0022, 0x0003 },    /* R34    - SPKMIXL Attenuation */ 
+	{ 0x0023, 0x0003 },    /* R35    - SPKMIXR Attenuation */ 
+	{ 0x0024, 0x0011 },    /* R36    - SPKOUT Mixers */ 
+	{ 0x0025, 0x0140 },    /* R37    - ClassD */ 
+	{ 0x0026, 0x0079 },    /* R38    - Speaker Volume Left */ 
+	{ 0x0027, 0x0079 },    /* R39    - Speaker Volume Right */ 
+	{ 0x0028, 0x0000 },    /* R40    - Input Mixer (2) */ 
+	{ 0x0029, 0x0000 },    /* R41    - Input Mixer (3) */ 
+	{ 0x002A, 0x0000 },    /* R42    - Input Mixer (4) */ 
+	{ 0x002B, 0x0000 },    /* R43    - Input Mixer (5) */ 
+	{ 0x002C, 0x0000 },    /* R44    - Input Mixer (6) */ 
+	{ 0x002D, 0x0000 },    /* R45    - Output Mixer (1) */ 
+	{ 0x002E, 0x0000 },    /* R46    - Output Mixer (2) */ 
+	{ 0x002F, 0x0000 },    /* R47    - Output Mixer (3) */ 
+	{ 0x0030, 0x0000 },    /* R48    - Output Mixer (4) */ 
+	{ 0x0031, 0x0000 },    /* R49    - Output Mixer (5) */ 
+	{ 0x0032, 0x0000 },    /* R50    - Output Mixer (6) */ 
+	{ 0x0033, 0x0000 },    /* R51    - HPOUT2 Mixer */ 
+	{ 0x0034, 0x0000 },    /* R52    - Line Mixer (1) */ 
+	{ 0x0035, 0x0000 },    /* R53    - Line Mixer (2) */ 
+	{ 0x0036, 0x0000 },    /* R54    - Speaker Mixer */ 
+	{ 0x0037, 0x0000 },    /* R55    - Additional Control */ 
+	{ 0x0038, 0x0000 },    /* R56    - AntiPOP (1) */ 
+	{ 0x0039, 0x0000 },    /* R57    - AntiPOP (2) */ 
+	{ 0x003A, 0x0000 },    /* R58    - MICBIAS */ 
+	{ 0x003B, 0x000D },    /* R59    - LDO 1 */ 
+	{ 0x003C, 0x0003 },    /* R60    - LDO 2 */ 
+	{ 0x004C, 0x1F25 },    /* R76    - Charge Pump (1) */ 
+	{ 0x0051, 0x0004 },    /* R81    - Class W (1) */ 
+	{ 0x0055, 0x054A },    /* R85    - DC Servo (2) */ 
+	{ 0x0057, 0x0000 },    /* R87    - DC Servo (4) */ 
+	{ 0x0060, 0x0000 },    /* R96    - Analogue HP (1) */ 
+	{ 0x0101, 0x8004 },    /* R257   - Control Interface */ 
+	{ 0x0110, 0x0000 },    /* R272   - Write Sequencer Ctrl (1) */ 
+	{ 0x0111, 0x0000 },    /* R273   - Write Sequencer Ctrl (2) */ 
+	{ 0x0200, 0x0000 },    /* R512   - AIF1 Clocking (1) */ 
+	{ 0x0201, 0x0000 },    /* R513   - AIF1 Clocking (2) */ 
+	{ 0x0204, 0x0000 },    /* R516   - AIF2 Clocking (1) */ 
+	{ 0x0205, 0x0000 },    /* R517   - AIF2 Clocking (2) */ 
+	{ 0x0208, 0x0000 },    /* R520   - Clocking (1) */ 
+	{ 0x0209, 0x0000 },    /* R521   - Clocking (2) */ 
+	{ 0x0210, 0x0083 },    /* R528   - AIF1 Rate */ 
+	{ 0x0211, 0x0083 },    /* R529   - AIF2 Rate */ 
+	{ 0x0220, 0x0000 },    /* R544   - FLL1 Control (1) */ 
+	{ 0x0221, 0x0000 },    /* R545   - FLL1 Control (2) */ 
+	{ 0x0222, 0x0000 },    /* R546   - FLL1 Control (3) */ 
+	{ 0x0223, 0x0000 },    /* R547   - FLL1 Control (4) */ 
+	{ 0x0224, 0x0C80 },    /* R548   - FLL1 Control (5) */ 
+	{ 0x0240, 0x0000 },    /* R576   - FLL2 Control (1) */ 
+	{ 0x0241, 0x0000 },    /* R577   - FLL2 Control (2) */ 
+	{ 0x0242, 0x0000 },    /* R578   - FLL2 Control (3) */ 
+	{ 0x0243, 0x0000 },    /* R579   - FLL2 Control (4) */ 
+	{ 0x0244, 0x0C80 },    /* R580   - FLL2 Control (5) */ 
+	{ 0x0300, 0x4050 },    /* R768   - AIF1 Control (1) */ 
+	{ 0x0301, 0x4000 },    /* R769   - AIF1 Control (2) */ 
+	{ 0x0302, 0x0000 },    /* R770   - AIF1 Master/Slave */ 
+	{ 0x0303, 0x0040 },    /* R771   - AIF1 BCLK */ 
+	{ 0x0304, 0x0040 },    /* R772   - AIF1ADC LRCLK */ 
+	{ 0x0305, 0x0040 },    /* R773   - AIF1DAC LRCLK */ 
+	{ 0x0306, 0x0004 },    /* R774   - AIF1DAC Data */ 
+	{ 0x0307, 0x0100 },    /* R775   - AIF1ADC Data */ 
+	{ 0x0310, 0x4050 },    /* R784   - AIF2 Control (1) */ 
+	{ 0x0311, 0x4000 },    /* R785   - AIF2 Control (2) */ 
+	{ 0x0312, 0x0000 },    /* R786   - AIF2 Master/Slave */ 
+	{ 0x0313, 0x0040 },    /* R787   - AIF2 BCLK */ 
+	{ 0x0314, 0x0040 },    /* R788   - AIF2ADC LRCLK */ 
+	{ 0x0315, 0x0040 },    /* R789   - AIF2DAC LRCLK */ 
+	{ 0x0316, 0x0000 },    /* R790   - AIF2DAC Data */ 
+	{ 0x0317, 0x0000 },    /* R791   - AIF2ADC Data */ 
+	{ 0x0400, 0x00C0 },    /* R1024  - AIF1 ADC1 Left Volume */ 
+	{ 0x0401, 0x00C0 },    /* R1025  - AIF1 ADC1 Right Volume */ 
+	{ 0x0402, 0x00C0 },    /* R1026  - AIF1 DAC1 Left Volume */ 
+	{ 0x0403, 0x00C0 },    /* R1027  - AIF1 DAC1 Right Volume */ 
+	{ 0x0404, 0x00C0 },    /* R1028  - AIF1 ADC2 Left Volume */ 
+	{ 0x0405, 0x00C0 },    /* R1029  - AIF1 ADC2 Right Volume */ 
+	{ 0x0406, 0x00C0 },    /* R1030  - AIF1 DAC2 Left Volume */ 
+	{ 0x0407, 0x00C0 },    /* R1031  - AIF1 DAC2 Right Volume */ 
+	{ 0x0410, 0x0000 },    /* R1040  - AIF1 ADC1 Filters */ 
+	{ 0x0411, 0x0000 },    /* R1041  - AIF1 ADC2 Filters */ 
+	{ 0x0420, 0x0200 },    /* R1056  - AIF1 DAC1 Filters (1) */ 
+	{ 0x0421, 0x0010 },    /* R1057  - AIF1 DAC1 Filters (2) */ 
+	{ 0x0422, 0x0200 },    /* R1058  - AIF1 DAC2 Filters (1) */ 
+	{ 0x0423, 0x0010 },    /* R1059  - AIF1 DAC2 Filters (2) */ 
+	{ 0x0440, 0x0098 },    /* R1088  - AIF1 DRC1 (1) */ 
+	{ 0x0441, 0x0845 },    /* R1089  - AIF1 DRC1 (2) */ 
+	{ 0x0442, 0x0000 },    /* R1090  - AIF1 DRC1 (3) */ 
+	{ 0x0443, 0x0000 },    /* R1091  - AIF1 DRC1 (4) */ 
+	{ 0x0444, 0x0000 },    /* R1092  - AIF1 DRC1 (5) */ 
+	{ 0x0450, 0x0098 },    /* R1104  - AIF1 DRC2 (1) */ 
+	{ 0x0451, 0x0845 },    /* R1105  - AIF1 DRC2 (2) */ 
+	{ 0x0452, 0x0000 },    /* R1106  - AIF1 DRC2 (3) */ 
+	{ 0x0453, 0x0000 },    /* R1107  - AIF1 DRC2 (4) */ 
+	{ 0x0454, 0x0000 },    /* R1108  - AIF1 DRC2 (5) */ 
+	{ 0x0480, 0x6318 },    /* R1152  - AIF1 DAC1 EQ Gains (1) */ 
+	{ 0x0481, 0x6300 },    /* R1153  - AIF1 DAC1 EQ Gains (2) */ 
+	{ 0x0482, 0x0FCA },    /* R1154  - AIF1 DAC1 EQ Band 1 A */ 
+	{ 0x0483, 0x0400 },    /* R1155  - AIF1 DAC1 EQ Band 1 B */ 
+	{ 0x0484, 0x00D8 },    /* R1156  - AIF1 DAC1 EQ Band 1 PG */ 
+	{ 0x0485, 0x1EB5 },    /* R1157  - AIF1 DAC1 EQ Band 2 A */ 
+	{ 0x0486, 0xF145 },    /* R1158  - AIF1 DAC1 EQ Band 2 B */ 
+	{ 0x0487, 0x0B75 },    /* R1159  - AIF1 DAC1 EQ Band 2 C */ 
+	{ 0x0488, 0x01C5 },    /* R1160  - AIF1 DAC1 EQ Band 2 PG */ 
+	{ 0x0489, 0x1C58 },    /* R1161  - AIF1 DAC1 EQ Band 3 A */ 
+	{ 0x048A, 0xF373 },    /* R1162  - AIF1 DAC1 EQ Band 3 B */ 
+	{ 0x048B, 0x0A54 },    /* R1163  - AIF1 DAC1 EQ Band 3 C */ 
+	{ 0x048C, 0x0558 },    /* R1164  - AIF1 DAC1 EQ Band 3 PG */ 
+	{ 0x048D, 0x168E },    /* R1165  - AIF1 DAC1 EQ Band 4 A */ 
+	{ 0x048E, 0xF829 },    /* R1166  - AIF1 DAC1 EQ Band 4 B */ 
+	{ 0x048F, 0x07AD },    /* R1167  - AIF1 DAC1 EQ Band 4 C */ 
+	{ 0x0490, 0x1103 },    /* R1168  - AIF1 DAC1 EQ Band 4 PG */ 
+	{ 0x0491, 0x0564 },    /* R1169  - AIF1 DAC1 EQ Band 5 A */ 
+	{ 0x0492, 0x0559 },    /* R1170  - AIF1 DAC1 EQ Band 5 B */ 
+	{ 0x0493, 0x4000 },    /* R1171  - AIF1 DAC1 EQ Band 5 PG */ 
+	{ 0x04A0, 0x6318 },    /* R1184  - AIF1 DAC2 EQ Gains (1) */ 
+	{ 0x04A1, 0x6300 },    /* R1185  - AIF1 DAC2 EQ Gains (2) */ 
+	{ 0x04A2, 0x0FCA },    /* R1186  - AIF1 DAC2 EQ Band 1 A */ 
+	{ 0x04A3, 0x0400 },    /* R1187  - AIF1 DAC2 EQ Band 1 B */ 
+	{ 0x04A4, 0x00D8 },    /* R1188  - AIF1 DAC2 EQ Band 1 PG */ 
+	{ 0x04A5, 0x1EB5 },    /* R1189  - AIF1 DAC2 EQ Band 2 A */ 
+	{ 0x04A6, 0xF145 },    /* R1190  - AIF1 DAC2 EQ Band 2 B */ 
+	{ 0x04A7, 0x0B75 },    /* R1191  - AIF1 DAC2 EQ Band 2 C */ 
+	{ 0x04A8, 0x01C5 },    /* R1192  - AIF1 DAC2 EQ Band 2 PG */ 
+	{ 0x04A9, 0x1C58 },    /* R1193  - AIF1 DAC2 EQ Band 3 A */ 
+	{ 0x04AA, 0xF373 },    /* R1194  - AIF1 DAC2 EQ Band 3 B */ 
+	{ 0x04AB, 0x0A54 },    /* R1195  - AIF1 DAC2 EQ Band 3 C */ 
+	{ 0x04AC, 0x0558 },    /* R1196  - AIF1 DAC2 EQ Band 3 PG */ 
+	{ 0x04AD, 0x168E },    /* R1197  - AIF1 DAC2 EQ Band 4 A */ 
+	{ 0x04AE, 0xF829 },    /* R1198  - AIF1 DAC2 EQ Band 4 B */ 
+	{ 0x04AF, 0x07AD },    /* R1199  - AIF1 DAC2 EQ Band 4 C */ 
+	{ 0x04B0, 0x1103 },    /* R1200  - AIF1 DAC2 EQ Band 4 PG */ 
+	{ 0x04B1, 0x0564 },    /* R1201  - AIF1 DAC2 EQ Band 5 A */ 
+	{ 0x04B2, 0x0559 },    /* R1202  - AIF1 DAC2 EQ Band 5 B */ 
+	{ 0x04B3, 0x4000 },    /* R1203  - AIF1 DAC2 EQ Band 5 PG */ 
+	{ 0x0500, 0x00C0 },    /* R1280  - AIF2 ADC Left Volume */ 
+	{ 0x0501, 0x00C0 },    /* R1281  - AIF2 ADC Right Volume */ 
+	{ 0x0502, 0x00C0 },    /* R1282  - AIF2 DAC Left Volume */ 
+	{ 0x0503, 0x00C0 },    /* R1283  - AIF2 DAC Right Volume */ 
+	{ 0x0510, 0x0000 },    /* R1296  - AIF2 ADC Filters */ 
+	{ 0x0520, 0x0200 },    /* R1312  - AIF2 DAC Filters (1) */ 
+	{ 0x0521, 0x0010 },    /* R1313  - AIF2 DAC Filters (2) */ 
+	{ 0x0540, 0x0098 },    /* R1344  - AIF2 DRC (1) */ 
+	{ 0x0541, 0x0845 },    /* R1345  - AIF2 DRC (2) */ 
+	{ 0x0542, 0x0000 },    /* R1346  - AIF2 DRC (3) */ 
+	{ 0x0543, 0x0000 },    /* R1347  - AIF2 DRC (4) */ 
+	{ 0x0544, 0x0000 },    /* R1348  - AIF2 DRC (5) */ 
+	{ 0x0580, 0x6318 },    /* R1408  - AIF2 EQ Gains (1) */ 
+	{ 0x0581, 0x6300 },    /* R1409  - AIF2 EQ Gains (2) */ 
+	{ 0x0582, 0x0FCA },    /* R1410  - AIF2 EQ Band 1 A */ 
+	{ 0x0583, 0x0400 },    /* R1411  - AIF2 EQ Band 1 B */ 
+	{ 0x0584, 0x00D8 },    /* R1412  - AIF2 EQ Band 1 PG */ 
+	{ 0x0585, 0x1EB5 },    /* R1413  - AIF2 EQ Band 2 A */ 
+	{ 0x0586, 0xF145 },    /* R1414  - AIF2 EQ Band 2 B */ 
+	{ 0x0587, 0x0B75 },    /* R1415  - AIF2 EQ Band 2 C */ 
+	{ 0x0588, 0x01C5 },    /* R1416  - AIF2 EQ Band 2 PG */ 
+	{ 0x0589, 0x1C58 },    /* R1417  - AIF2 EQ Band 3 A */ 
+	{ 0x058A, 0xF373 },    /* R1418  - AIF2 EQ Band 3 B */ 
+	{ 0x058B, 0x0A54 },    /* R1419  - AIF2 EQ Band 3 C */ 
+	{ 0x058C, 0x0558 },    /* R1420  - AIF2 EQ Band 3 PG */ 
+	{ 0x058D, 0x168E },    /* R1421  - AIF2 EQ Band 4 A */ 
+	{ 0x058E, 0xF829 },    /* R1422  - AIF2 EQ Band 4 B */ 
+	{ 0x058F, 0x07AD },    /* R1423  - AIF2 EQ Band 4 C */ 
+	{ 0x0590, 0x1103 },    /* R1424  - AIF2 EQ Band 4 PG */ 
+	{ 0x0591, 0x0564 },    /* R1425  - AIF2 EQ Band 5 A */ 
+	{ 0x0592, 0x0559 },    /* R1426  - AIF2 EQ Band 5 B */ 
+	{ 0x0593, 0x4000 },    /* R1427  - AIF2 EQ Band 5 PG */ 
+	{ 0x0600, 0x0000 },    /* R1536  - DAC1 Mixer Volumes */ 
+	{ 0x0601, 0x0000 },    /* R1537  - DAC1 Left Mixer Routing */ 
+	{ 0x0602, 0x0000 },    /* R1538  - DAC1 Right Mixer Routing */ 
+	{ 0x0603, 0x0000 },    /* R1539  - DAC2 Mixer Volumes */ 
+	{ 0x0604, 0x0000 },    /* R1540  - DAC2 Left Mixer Routing */ 
+	{ 0x0605, 0x0000 },    /* R1541  - DAC2 Right Mixer Routing */ 
+	{ 0x0606, 0x0000 },    /* R1542  - AIF1 ADC1 Left Mixer Routing */ 
+	{ 0x0607, 0x0000 },    /* R1543  - AIF1 ADC1 Right Mixer Routing */ 
+	{ 0x0608, 0x0000 },    /* R1544  - AIF1 ADC2 Left Mixer Routing */ 
+	{ 0x0609, 0x0000 },    /* R1545  - AIF1 ADC2 Right mixer Routing */ 
+	{ 0x0610, 0x02C0 },    /* R1552  - DAC1 Left Volume */ 
+	{ 0x0611, 0x02C0 },    /* R1553  - DAC1 Right Volume */ 
+	{ 0x0612, 0x02C0 },    /* R1554  - DAC2 Left Volume */ 
+	{ 0x0613, 0x02C0 },    /* R1555  - DAC2 Right Volume */ 
+	{ 0x0614, 0x0000 },    /* R1556  - DAC Softmute */ 
+	{ 0x0620, 0x0002 },    /* R1568  - Oversampling */ 
+	{ 0x0621, 0x0000 },    /* R1569  - Sidetone */ 
+	{ 0x0700, 0x8100 },    /* R1792  - GPIO 1 */ 
+	{ 0x0701, 0xA101 },    /* R1793  - GPIO 2 */ 
+	{ 0x0702, 0xA101 },    /* R1794  - GPIO 3 */ 
+	{ 0x0703, 0xA101 },    /* R1795  - GPIO 4 */ 
+	{ 0x0704, 0xA101 },    /* R1796  - GPIO 5 */ 
+	{ 0x0705, 0xA101 },    /* R1797  - GPIO 6 */ 
+	{ 0x0706, 0xA101 },    /* R1798  - GPIO 7 */ 
+	{ 0x0707, 0xA101 },    /* R1799  - GPIO 8 */ 
+	{ 0x0708, 0xA101 },    /* R1800  - GPIO 9 */ 
+	{ 0x0709, 0xA101 },    /* R1801  - GPIO 10 */ 
+	{ 0x070A, 0xA101 },    /* R1802  - GPIO 11 */ 
+	{ 0x0720, 0x0000 },    /* R1824  - Pull Control (1) */ 
+	{ 0x0721, 0x0156 },    /* R1825  - Pull Control (2) */ 
+	{ 0x0738, 0x07FF },    /* R1848  - Interrupt Status 1 Mask */ 
+	{ 0x0739, 0xFFFF },    /* R1849  - Interrupt Status 2 Mask */ 
+	{ 0x0740, 0x0000 },    /* R1856  - Interrupt Control */ 
+	{ 0x0748, 0x003F },    /* R1864  - IRQ Debounce */ 
+};
+
+static struct reg_default wm8958_defaults[] = {
+	{ 0x0001, 0x0000 },    /* R1     - Power Management (1) */
+	{ 0x0002, 0x6000 },    /* R2     - Power Management (2) */
+	{ 0x0003, 0x0000 },    /* R3     - Power Management (3) */
+	{ 0x0004, 0x0000 },    /* R4     - Power Management (4) */
+	{ 0x0005, 0x0000 },    /* R5     - Power Management (5) */
+	{ 0x0006, 0x0000 },    /* R6     - Power Management (6) */
+	{ 0x0015, 0x0000 },    /* R21    - Input Mixer (1) */
+	{ 0x0018, 0x008B },    /* R24    - Left Line Input 1&2 Volume */
+	{ 0x0019, 0x008B },    /* R25    - Left Line Input 3&4 Volume */
+	{ 0x001A, 0x008B },    /* R26    - Right Line Input 1&2 Volume */
+	{ 0x001B, 0x008B },    /* R27    - Right Line Input 3&4 Volume */
+	{ 0x001C, 0x006D },    /* R28    - Left Output Volume */
+	{ 0x001D, 0x006D },    /* R29    - Right Output Volume */
+	{ 0x001E, 0x0066 },    /* R30    - Line Outputs Volume */
+	{ 0x001F, 0x0020 },    /* R31    - HPOUT2 Volume */
+	{ 0x0020, 0x0079 },    /* R32    - Left OPGA Volume */
+	{ 0x0021, 0x0079 },    /* R33    - Right OPGA Volume */
+	{ 0x0022, 0x0003 },    /* R34    - SPKMIXL Attenuation */
+	{ 0x0023, 0x0003 },    /* R35    - SPKMIXR Attenuation */
+	{ 0x0024, 0x0011 },    /* R36    - SPKOUT Mixers */
+	{ 0x0025, 0x0140 },    /* R37    - ClassD */
+	{ 0x0026, 0x0079 },    /* R38    - Speaker Volume Left */
+	{ 0x0027, 0x0079 },    /* R39    - Speaker Volume Right */
+	{ 0x0028, 0x0000 },    /* R40    - Input Mixer (2) */
+	{ 0x0029, 0x0000 },    /* R41    - Input Mixer (3) */
+	{ 0x002A, 0x0000 },    /* R42    - Input Mixer (4) */
+	{ 0x002B, 0x0000 },    /* R43    - Input Mixer (5) */
+	{ 0x002C, 0x0000 },    /* R44    - Input Mixer (6) */
+	{ 0x002D, 0x0000 },    /* R45    - Output Mixer (1) */
+	{ 0x002E, 0x0000 },    /* R46    - Output Mixer (2) */
+	{ 0x002F, 0x0000 },    /* R47    - Output Mixer (3) */
+	{ 0x0030, 0x0000 },    /* R48    - Output Mixer (4) */
+	{ 0x0031, 0x0000 },    /* R49    - Output Mixer (5) */
+	{ 0x0032, 0x0000 },    /* R50    - Output Mixer (6) */
+	{ 0x0033, 0x0000 },    /* R51    - HPOUT2 Mixer */
+	{ 0x0034, 0x0000 },    /* R52    - Line Mixer (1) */
+	{ 0x0035, 0x0000 },    /* R53    - Line Mixer (2) */
+	{ 0x0036, 0x0000 },    /* R54    - Speaker Mixer */
+	{ 0x0037, 0x0000 },    /* R55    - Additional Control */
+	{ 0x0038, 0x0000 },    /* R56    - AntiPOP (1) */
+	{ 0x0039, 0x0180 },    /* R57    - AntiPOP (2) */
+	{ 0x003B, 0x000D },    /* R59    - LDO 1 */
+	{ 0x003C, 0x0005 },    /* R60    - LDO 2 */
+	{ 0x003D, 0x0039 },    /* R61    - MICBIAS1 */
+	{ 0x003E, 0x0039 },    /* R62    - MICBIAS2 */
+	{ 0x004C, 0x1F25 },    /* R76    - Charge Pump (1) */
+	{ 0x004D, 0xAB19 },    /* R77    - Charge Pump (2) */
+	{ 0x0051, 0x0004 },    /* R81    - Class W (1) */
+	{ 0x0055, 0x054A },    /* R85    - DC Servo (2) */
+	{ 0x0057, 0x0000 },    /* R87    - DC Servo (4) */
+	{ 0x0060, 0x0000 },    /* R96    - Analogue HP (1) */
+	{ 0x00C5, 0x0000 },    /* R197   - Class D Test (5) */
+	{ 0x00D0, 0x5600 },    /* R208   - Mic Detect 1 */
+	{ 0x00D1, 0x007F },    /* R209   - Mic Detect 2 */
+	{ 0x0101, 0x8004 },    /* R257   - Control Interface */
+	{ 0x0110, 0x0000 },    /* R272   - Write Sequencer Ctrl (1) */
+	{ 0x0111, 0x0000 },    /* R273   - Write Sequencer Ctrl (2) */
+	{ 0x0200, 0x0000 },    /* R512   - AIF1 Clocking (1) */
+	{ 0x0201, 0x0000 },    /* R513   - AIF1 Clocking (2) */
+	{ 0x0204, 0x0000 },    /* R516   - AIF2 Clocking (1) */
+	{ 0x0205, 0x0000 },    /* R517   - AIF2 Clocking (2) */
+	{ 0x0208, 0x0000 },    /* R520   - Clocking (1) */
+	{ 0x0209, 0x0000 },    /* R521   - Clocking (2) */
+	{ 0x0210, 0x0083 },    /* R528   - AIF1 Rate */
+	{ 0x0211, 0x0083 },    /* R529   - AIF2 Rate */
+	{ 0x0220, 0x0000 },    /* R544   - FLL1 Control (1) */
+	{ 0x0221, 0x0000 },    /* R545   - FLL1 Control (2) */
+	{ 0x0222, 0x0000 },    /* R546   - FLL1 Control (3) */
+	{ 0x0223, 0x0000 },    /* R547   - FLL1 Control (4) */
+	{ 0x0224, 0x0C80 },    /* R548   - FLL1 Control (5) */
+	{ 0x0226, 0x0000 },    /* R550   - FLL1 EFS 1 */
+	{ 0x0227, 0x0006 },    /* R551   - FLL1 EFS 2 */
+	{ 0x0240, 0x0000 },    /* R576   - FLL2Control (1) */
+	{ 0x0241, 0x0000 },    /* R577   - FLL2Control (2) */
+	{ 0x0242, 0x0000 },    /* R578   - FLL2Control (3) */
+	{ 0x0243, 0x0000 },    /* R579   - FLL2 Control (4) */
+	{ 0x0244, 0x0C80 },    /* R580   - FLL2Control (5) */
+	{ 0x0246, 0x0000 },    /* R582   - FLL2 EFS 1 */
+	{ 0x0247, 0x0006 },    /* R583   - FLL2 EFS 2 */
+	{ 0x0300, 0x4050 },    /* R768   - AIF1 Control (1) */
+	{ 0x0301, 0x4000 },    /* R769   - AIF1 Control (2) */
+	{ 0x0302, 0x0000 },    /* R770   - AIF1 Master/Slave */
+	{ 0x0303, 0x0040 },    /* R771   - AIF1 BCLK */
+	{ 0x0304, 0x0040 },    /* R772   - AIF1ADC LRCLK */
+	{ 0x0305, 0x0040 },    /* R773   - AIF1DAC LRCLK */
+	{ 0x0306, 0x0004 },    /* R774   - AIF1DAC Data */
+	{ 0x0307, 0x0100 },    /* R775   - AIF1ADC Data */
+	{ 0x0310, 0x4053 },    /* R784   - AIF2 Control (1) */
+	{ 0x0311, 0x4000 },    /* R785   - AIF2 Control (2) */
+	{ 0x0312, 0x0000 },    /* R786   - AIF2 Master/Slave */
+	{ 0x0313, 0x0040 },    /* R787   - AIF2 BCLK */
+	{ 0x0314, 0x0040 },    /* R788   - AIF2ADC LRCLK */
+	{ 0x0315, 0x0040 },    /* R789   - AIF2DAC LRCLK */
+	{ 0x0316, 0x0000 },    /* R790   - AIF2DAC Data */
+	{ 0x0317, 0x0000 },    /* R791   - AIF2ADC Data */
+	{ 0x0320, 0x0040 },    /* R800   - AIF3 Control (1) */
+	{ 0x0321, 0x0000 },    /* R801   - AIF3 Control (2) */
+	{ 0x0322, 0x0000 },    /* R802   - AIF3DAC Data */
+	{ 0x0323, 0x0000 },    /* R803   - AIF3ADC Data */
+	{ 0x0400, 0x00C0 },    /* R1024  - AIF1 ADC1 Left Volume */
+	{ 0x0401, 0x00C0 },    /* R1025  - AIF1 ADC1 Right Volume */
+	{ 0x0402, 0x00C0 },    /* R1026  - AIF1 DAC1 Left Volume */
+	{ 0x0403, 0x00C0 },    /* R1027  - AIF1 DAC1 Right Volume */
+	{ 0x0404, 0x00C0 },    /* R1028  - AIF1 ADC2 Left Volume */
+	{ 0x0405, 0x00C0 },    /* R1029  - AIF1 ADC2 Right Volume */
+	{ 0x0406, 0x00C0 },    /* R1030  - AIF1 DAC2 Left Volume */
+	{ 0x0407, 0x00C0 },    /* R1031  - AIF1 DAC2 Right Volume */
+	{ 0x0410, 0x0000 },    /* R1040  - AIF1 ADC1 Filters */
+	{ 0x0411, 0x0000 },    /* R1041  - AIF1 ADC2 Filters */
+	{ 0x0420, 0x0200 },    /* R1056  - AIF1 DAC1 Filters (1) */
+	{ 0x0421, 0x0010 },    /* R1057  - AIF1 DAC1 Filters (2) */
+	{ 0x0422, 0x0200 },    /* R1058  - AIF1 DAC2 Filters (1) */
+	{ 0x0423, 0x0010 },    /* R1059  - AIF1 DAC2 Filters (2) */
+	{ 0x0430, 0x0068 },    /* R1072  - AIF1 DAC1 Noise Gate */
+	{ 0x0431, 0x0068 },    /* R1073  - AIF1 DAC2 Noise Gate */
+	{ 0x0440, 0x0098 },    /* R1088  - AIF1 DRC1 (1) */
+	{ 0x0441, 0x0845 },    /* R1089  - AIF1 DRC1 (2) */
+	{ 0x0442, 0x0000 },    /* R1090  - AIF1 DRC1 (3) */
+	{ 0x0443, 0x0000 },    /* R1091  - AIF1 DRC1 (4) */
+	{ 0x0444, 0x0000 },    /* R1092  - AIF1 DRC1 (5) */
+	{ 0x0450, 0x0098 },    /* R1104  - AIF1 DRC2 (1) */
+	{ 0x0451, 0x0845 },    /* R1105  - AIF1 DRC2 (2) */
+	{ 0x0452, 0x0000 },    /* R1106  - AIF1 DRC2 (3) */
+	{ 0x0453, 0x0000 },    /* R1107  - AIF1 DRC2 (4) */
+	{ 0x0454, 0x0000 },    /* R1108  - AIF1 DRC2 (5) */
+	{ 0x0480, 0x6318 },    /* R1152  - AIF1 DAC1 EQ Gains (1) */
+	{ 0x0481, 0x6300 },    /* R1153  - AIF1 DAC1 EQ Gains (2) */
+	{ 0x0482, 0x0FCA },    /* R1154  - AIF1 DAC1 EQ Band 1 A */
+	{ 0x0483, 0x0400 },    /* R1155  - AIF1 DAC1 EQ Band 1 B */
+	{ 0x0484, 0x00D8 },    /* R1156  - AIF1 DAC1 EQ Band 1 PG */
+	{ 0x0485, 0x1EB5 },    /* R1157  - AIF1 DAC1 EQ Band 2 A */
+	{ 0x0486, 0xF145 },    /* R1158  - AIF1 DAC1 EQ Band 2 B */
+	{ 0x0487, 0x0B75 },    /* R1159  - AIF1 DAC1 EQ Band 2 C */
+	{ 0x0488, 0x01C5 },    /* R1160  - AIF1 DAC1 EQ Band 2 PG */
+	{ 0x0489, 0x1C58 },    /* R1161  - AIF1 DAC1 EQ Band 3 A */
+	{ 0x048A, 0xF373 },    /* R1162  - AIF1 DAC1 EQ Band 3 B */
+	{ 0x048B, 0x0A54 },    /* R1163  - AIF1 DAC1 EQ Band 3 C */
+	{ 0x048C, 0x0558 },    /* R1164  - AIF1 DAC1 EQ Band 3 PG */
+	{ 0x048D, 0x168E },    /* R1165  - AIF1 DAC1 EQ Band 4 A */
+	{ 0x048E, 0xF829 },    /* R1166  - AIF1 DAC1 EQ Band 4 B */
+	{ 0x048F, 0x07AD },    /* R1167  - AIF1 DAC1 EQ Band 4 C */
+	{ 0x0490, 0x1103 },    /* R1168  - AIF1 DAC1 EQ Band 4 PG */
+	{ 0x0491, 0x0564 },    /* R1169  - AIF1 DAC1 EQ Band 5 A */
+	{ 0x0492, 0x0559 },    /* R1170  - AIF1 DAC1 EQ Band 5 B */
+	{ 0x0493, 0x4000 },    /* R1171  - AIF1 DAC1 EQ Band 5 PG */
+	{ 0x0494, 0x0000 },    /* R1172  - AIF1 DAC1 EQ Band 1 C */
+	{ 0x04A0, 0x6318 },    /* R1184  - AIF1 DAC2 EQ Gains (1) */
+	{ 0x04A1, 0x6300 },    /* R1185  - AIF1 DAC2 EQ Gains (2) */
+	{ 0x04A2, 0x0FCA },    /* R1186  - AIF1 DAC2 EQ Band 1 A */
+	{ 0x04A3, 0x0400 },    /* R1187  - AIF1 DAC2 EQ Band 1 B */
+	{ 0x04A4, 0x00D8 },    /* R1188  - AIF1 DAC2 EQ Band 1 PG */
+	{ 0x04A5, 0x1EB5 },    /* R1189  - AIF1 DAC2 EQ Band 2 A */
+	{ 0x04A6, 0xF145 },    /* R1190  - AIF1 DAC2 EQ Band 2 B */
+	{ 0x04A7, 0x0B75 },    /* R1191  - AIF1 DAC2 EQ Band 2 C */
+	{ 0x04A8, 0x01C5 },    /* R1192  - AIF1 DAC2 EQ Band 2 PG */
+	{ 0x04A9, 0x1C58 },    /* R1193  - AIF1 DAC2 EQ Band 3 A */
+	{ 0x04AA, 0xF373 },    /* R1194  - AIF1 DAC2 EQ Band 3 B */
+	{ 0x04AB, 0x0A54 },    /* R1195  - AIF1 DAC2 EQ Band 3 C */
+	{ 0x04AC, 0x0558 },    /* R1196  - AIF1 DAC2 EQ Band 3 PG */
+	{ 0x04AD, 0x168E },    /* R1197  - AIF1 DAC2 EQ Band 4 A */
+	{ 0x04AE, 0xF829 },    /* R1198  - AIF1 DAC2 EQ Band 4 B */
+	{ 0x04AF, 0x07AD },    /* R1199  - AIF1 DAC2 EQ Band 4 C */
+	{ 0x04B0, 0x1103 },    /* R1200  - AIF1 DAC2 EQ Band 4 PG */
+	{ 0x04B1, 0x0564 },    /* R1201  - AIF1 DAC2 EQ Band 5 A */
+	{ 0x04B2, 0x0559 },    /* R1202  - AIF1 DAC2 EQ Band 5 B */
+	{ 0x04B3, 0x4000 },    /* R1203  - AIF1 DAC2 EQ Band 5 PG */
+	{ 0x04B4, 0x0000 },    /* R1204  - AIF1 DAC2EQ Band 1 C */
+	{ 0x0500, 0x00C0 },    /* R1280  - AIF2 ADC Left Volume */
+	{ 0x0501, 0x00C0 },    /* R1281  - AIF2 ADC Right Volume */
+	{ 0x0502, 0x00C0 },    /* R1282  - AIF2 DAC Left Volume */
+	{ 0x0503, 0x00C0 },    /* R1283  - AIF2 DAC Right Volume */
+	{ 0x0510, 0x0000 },    /* R1296  - AIF2 ADC Filters */
+	{ 0x0520, 0x0200 },    /* R1312  - AIF2 DAC Filters (1) */
+	{ 0x0521, 0x0010 },    /* R1313  - AIF2 DAC Filters (2) */
+	{ 0x0530, 0x0068 },    /* R1328  - AIF2 DAC Noise Gate */
+	{ 0x0540, 0x0098 },    /* R1344  - AIF2 DRC (1) */
+	{ 0x0541, 0x0845 },    /* R1345  - AIF2 DRC (2) */
+	{ 0x0542, 0x0000 },    /* R1346  - AIF2 DRC (3) */
+	{ 0x0543, 0x0000 },    /* R1347  - AIF2 DRC (4) */
+	{ 0x0544, 0x0000 },    /* R1348  - AIF2 DRC (5) */
+	{ 0x0580, 0x6318 },    /* R1408  - AIF2 EQ Gains (1) */
+	{ 0x0581, 0x6300 },    /* R1409  - AIF2 EQ Gains (2) */
+	{ 0x0582, 0x0FCA },    /* R1410  - AIF2 EQ Band 1 A */
+	{ 0x0583, 0x0400 },    /* R1411  - AIF2 EQ Band 1 B */
+	{ 0x0584, 0x00D8 },    /* R1412  - AIF2 EQ Band 1 PG */
+	{ 0x0585, 0x1EB5 },    /* R1413  - AIF2 EQ Band 2 A */
+	{ 0x0586, 0xF145 },    /* R1414  - AIF2 EQ Band 2 B */
+	{ 0x0587, 0x0B75 },    /* R1415  - AIF2 EQ Band 2 C */
+	{ 0x0588, 0x01C5 },    /* R1416  - AIF2 EQ Band 2 PG */
+	{ 0x0589, 0x1C58 },    /* R1417  - AIF2 EQ Band 3 A */
+	{ 0x058A, 0xF373 },    /* R1418  - AIF2 EQ Band 3 B */
+	{ 0x058B, 0x0A54 },    /* R1419  - AIF2 EQ Band 3 C */
+	{ 0x058C, 0x0558 },    /* R1420  - AIF2 EQ Band 3 PG */
+	{ 0x058D, 0x168E },    /* R1421  - AIF2 EQ Band 4 A */
+	{ 0x058E, 0xF829 },    /* R1422  - AIF2 EQ Band 4 B */
+	{ 0x058F, 0x07AD },    /* R1423  - AIF2 EQ Band 4 C */
+	{ 0x0590, 0x1103 },    /* R1424  - AIF2 EQ Band 4 PG */
+	{ 0x0591, 0x0564 },    /* R1425  - AIF2 EQ Band 5 A */
+	{ 0x0592, 0x0559 },    /* R1426  - AIF2 EQ Band 5 B */
+	{ 0x0593, 0x4000 },    /* R1427  - AIF2 EQ Band 5 PG */
+	{ 0x0594, 0x0000 },    /* R1428  - AIF2 EQ Band 1 C */
+	{ 0x0600, 0x0000 },    /* R1536  - DAC1 Mixer Volumes */
+	{ 0x0601, 0x0000 },    /* R1537  - DAC1 Left Mixer Routing */
+	{ 0x0602, 0x0000 },    /* R1538  - DAC1 Right Mixer Routing */
+	{ 0x0603, 0x0000 },    /* R1539  - DAC2 Mixer Volumes */
+	{ 0x0604, 0x0000 },    /* R1540  - DAC2 Left Mixer Routing */
+	{ 0x0605, 0x0000 },    /* R1541  - DAC2 Right Mixer Routing */
+	{ 0x0606, 0x0000 },    /* R1542  - AIF1 ADC1 Left Mixer Routing */
+	{ 0x0607, 0x0000 },    /* R1543  - AIF1 ADC1 Right Mixer Routing */
+	{ 0x0608, 0x0000 },    /* R1544  - AIF1 ADC2 Left Mixer Routing */
+	{ 0x0609, 0x0000 },    /* R1545  - AIF1 ADC2 Right mixer Routing */
+	{ 0x0610, 0x02C0 },    /* R1552  - DAC1 Left Volume */
+	{ 0x0611, 0x02C0 },    /* R1553  - DAC1 Right Volume */
+	{ 0x0612, 0x02C0 },    /* R1554  - DAC2 Left Volume */
+	{ 0x0613, 0x02C0 },    /* R1555  - DAC2 Right Volume */
+	{ 0x0614, 0x0000 },    /* R1556  - DAC Softmute */
+	{ 0x0620, 0x0002 },    /* R1568  - Oversampling */
+	{ 0x0621, 0x0000 },    /* R1569  - Sidetone */
+	{ 0x0700, 0x8100 },    /* R1792  - GPIO 1 */
+	{ 0x0701, 0xA101 },    /* R1793  - Pull Control (MCLK2) */
+	{ 0x0702, 0xA101 },    /* R1794  - Pull Control (BCLK2) */
+	{ 0x0703, 0xA101 },    /* R1795  - Pull Control (DACLRCLK2) */
+	{ 0x0704, 0xA101 },    /* R1796  - Pull Control (DACDAT2) */
+	{ 0x0705, 0xA101 },    /* R1797  - GPIO 6 */
+	{ 0x0707, 0xA101 },    /* R1799  - GPIO 8 */
+	{ 0x0708, 0xA101 },    /* R1800  - GPIO 9 */
+	{ 0x0709, 0xA101 },    /* R1801  - GPIO 10 */
+	{ 0x070A, 0xA101 },    /* R1802  - GPIO 11 */
+	{ 0x0720, 0x0000 },    /* R1824  - Pull Control (1) */
+	{ 0x0721, 0x0156 },    /* R1825  - Pull Control (2) */
+	{ 0x0738, 0x07FF },    /* R1848  - Interrupt Status 1 Mask */
+	{ 0x0739, 0xFFEF },    /* R1849  - Interrupt Status 2 Mask */
+	{ 0x0740, 0x0000 },    /* R1856  - Interrupt Control */
+	{ 0x0748, 0x003F },    /* R1864  - IRQ Debounce */
+	{ 0x0900, 0x1C00 },    /* R2304  - DSP2_Program */
+	{ 0x0901, 0x0000 },    /* R2305  - DSP2_Config */
+	{ 0x0A0D, 0x0000 },    /* R2573  - DSP2_ExecControl */
+	{ 0x2400, 0x003F },    /* R9216  - MBC Band 1 K (1) */
+	{ 0x2401, 0x8BD8 },    /* R9217  - MBC Band 1 K (2) */
+	{ 0x2402, 0x0032 },    /* R9218  - MBC Band 1 N1 (1) */
+	{ 0x2403, 0xF52D },    /* R9219  - MBC Band 1 N1 (2) */
+	{ 0x2404, 0x0065 },    /* R9220  - MBC Band 1 N2 (1) */
+	{ 0x2405, 0xAC8C },    /* R9221  - MBC Band 1 N2 (2) */
+	{ 0x2406, 0x006B },    /* R9222  - MBC Band 1 N3 (1) */
+	{ 0x2407, 0xE087 },    /* R9223  - MBC Band 1 N3 (2) */
+	{ 0x2408, 0x0072 },    /* R9224  - MBC Band 1 N4 (1) */
+	{ 0x2409, 0x1483 },    /* R9225  - MBC Band 1 N4 (2) */
+	{ 0x240A, 0x0072 },    /* R9226  - MBC Band 1 N5 (1) */
+	{ 0x240B, 0x1483 },    /* R9227  - MBC Band 1 N5 (2) */
+	{ 0x240C, 0x0043 },    /* R9228  - MBC Band 1 X1 (1) */
+	{ 0x240D, 0x3525 },    /* R9229  - MBC Band 1 X1 (2) */
+	{ 0x240E, 0x0006 },    /* R9230  - MBC Band 1 X2 (1) */
+	{ 0x240F, 0x6A4A },    /* R9231  - MBC Band 1 X2 (2) */
+	{ 0x2410, 0x0043 },    /* R9232  - MBC Band 1 X3 (1) */
+	{ 0x2411, 0x6079 },    /* R9233  - MBC Band 1 X3 (2) */
+	{ 0x2412, 0x000C },    /* R9234  - MBC Band 1 Attack (1) */
+	{ 0x2413, 0xCCCD },    /* R9235  - MBC Band 1 Attack (2) */
+	{ 0x2414, 0x0000 },    /* R9236  - MBC Band 1 Decay (1) */
+	{ 0x2415, 0x0800 },    /* R9237  - MBC Band 1 Decay (2) */
+	{ 0x2416, 0x003F },    /* R9238  - MBC Band 2 K (1) */
+	{ 0x2417, 0x8BD8 },    /* R9239  - MBC Band 2 K (2) */
+	{ 0x2418, 0x0032 },    /* R9240  - MBC Band 2 N1 (1) */
+	{ 0x2419, 0xF52D },    /* R9241  - MBC Band 2 N1 (2) */
+	{ 0x241A, 0x0065 },    /* R9242  - MBC Band 2 N2 (1) */
+	{ 0x241B, 0xAC8C },    /* R9243  - MBC Band 2 N2 (2) */
+	{ 0x241C, 0x006B },    /* R9244  - MBC Band 2 N3 (1) */
+	{ 0x241D, 0xE087 },    /* R9245  - MBC Band 2 N3 (2) */
+	{ 0x241E, 0x0072 },    /* R9246  - MBC Band 2 N4 (1) */
+	{ 0x241F, 0x1483 },    /* R9247  - MBC Band 2 N4 (2) */
+	{ 0x2420, 0x0072 },    /* R9248  - MBC Band 2 N5 (1) */
+	{ 0x2421, 0x1483 },    /* R9249  - MBC Band 2 N5 (2) */
+	{ 0x2422, 0x0043 },    /* R9250  - MBC Band 2 X1 (1) */
+	{ 0x2423, 0x3525 },    /* R9251  - MBC Band 2 X1 (2) */
+	{ 0x2424, 0x0006 },    /* R9252  - MBC Band 2 X2 (1) */
+	{ 0x2425, 0x6A4A },    /* R9253  - MBC Band 2 X2 (2) */
+	{ 0x2426, 0x0043 },    /* R9254  - MBC Band 2 X3 (1) */
+	{ 0x2427, 0x6079 },    /* R9255  - MBC Band 2 X3 (2) */
+	{ 0x2428, 0x000C },    /* R9256  - MBC Band 2 Attack (1) */
+	{ 0x2429, 0xCCCD },    /* R9257  - MBC Band 2 Attack (2) */
+	{ 0x242A, 0x0000 },    /* R9258  - MBC Band 2 Decay (1) */
+	{ 0x242B, 0x0800 },    /* R9259  - MBC Band 2 Decay (2) */
+	{ 0x242C, 0x005A },    /* R9260  - MBC_B2_PG2 (1) */
+	{ 0x242D, 0x7EFA },    /* R9261  - MBC_B2_PG2 (2) */
+	{ 0x242E, 0x005A },    /* R9262  - MBC_B1_PG2 (1) */
+	{ 0x242F, 0x7EFA },    /* R9263  - MBC_B1_PG2 (2) */
+	{ 0x2600, 0x00A7 },    /* R9728  - MBC Crossover (1) */
+	{ 0x2601, 0x0D1C },    /* R9729  - MBC Crossover (2) */
+	{ 0x2602, 0x0083 },    /* R9730  - MBC HPF (1) */
+	{ 0x2603, 0x98AD },    /* R9731  - MBC HPF (2) */
+	{ 0x2606, 0x0008 },    /* R9734  - MBC LPF (1) */
+	{ 0x2607, 0xE7A2 },    /* R9735  - MBC LPF (2) */
+	{ 0x260A, 0x0055 },    /* R9738  - MBC RMS Limit (1) */
+	{ 0x260B, 0x8C4B },    /* R9739  - MBC RMS Limit (2) */
+};
+
+static bool wm1811_readable_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case WM8994_SOFTWARE_RESET:
+	case WM8994_POWER_MANAGEMENT_1:
+	case WM8994_POWER_MANAGEMENT_2:
+	case WM8994_POWER_MANAGEMENT_3:
+	case WM8994_POWER_MANAGEMENT_4:
+	case WM8994_POWER_MANAGEMENT_5:
+	case WM8994_POWER_MANAGEMENT_6:
+	case WM8994_INPUT_MIXER_1:
+	case WM8994_LEFT_LINE_INPUT_1_2_VOLUME:
+	case WM8994_LEFT_LINE_INPUT_3_4_VOLUME:
+	case WM8994_RIGHT_LINE_INPUT_1_2_VOLUME:
+	case WM8994_RIGHT_LINE_INPUT_3_4_VOLUME:
+	case WM8994_LEFT_OUTPUT_VOLUME:
+	case WM8994_RIGHT_OUTPUT_VOLUME:
+	case WM8994_LINE_OUTPUTS_VOLUME:
+	case WM8994_HPOUT2_VOLUME:
+	case WM8994_LEFT_OPGA_VOLUME:
+	case WM8994_RIGHT_OPGA_VOLUME:
+	case WM8994_SPKMIXL_ATTENUATION:
+	case WM8994_SPKMIXR_ATTENUATION:
+	case WM8994_SPKOUT_MIXERS:
+	case WM8994_CLASSD:
+	case WM8994_SPEAKER_VOLUME_LEFT:
+	case WM8994_SPEAKER_VOLUME_RIGHT:
+	case WM8994_INPUT_MIXER_2:
+	case WM8994_INPUT_MIXER_3:
+	case WM8994_INPUT_MIXER_4:
+	case WM8994_INPUT_MIXER_5:
+	case WM8994_INPUT_MIXER_6:
+	case WM8994_OUTPUT_MIXER_1:
+	case WM8994_OUTPUT_MIXER_2:
+	case WM8994_OUTPUT_MIXER_3:
+	case WM8994_OUTPUT_MIXER_4:
+	case WM8994_OUTPUT_MIXER_5:
+	case WM8994_OUTPUT_MIXER_6:
+	case WM8994_HPOUT2_MIXER:
+	case WM8994_LINE_MIXER_1:
+	case WM8994_LINE_MIXER_2:
+	case WM8994_SPEAKER_MIXER:
+	case WM8994_ADDITIONAL_CONTROL:
+	case WM8994_ANTIPOP_1:
+	case WM8994_ANTIPOP_2:
+	case WM8994_LDO_1:
+	case WM8994_LDO_2:
+	case WM8958_MICBIAS1:
+	case WM8958_MICBIAS2:
+	case WM8994_CHARGE_PUMP_1:
+	case WM8958_CHARGE_PUMP_2:
+	case WM8994_CLASS_W_1:
+	case WM8994_DC_SERVO_1:
+	case WM8994_DC_SERVO_2:
+	case WM8994_DC_SERVO_READBACK:
+	case WM8994_DC_SERVO_4:
+	case WM8994_DC_SERVO_4E:
+	case WM8994_ANALOGUE_HP_1:
+	case WM8958_MIC_DETECT_1:
+	case WM8958_MIC_DETECT_2:
+	case WM8958_MIC_DETECT_3:
+	case WM8994_CHIP_REVISION:
+	case WM8994_CONTROL_INTERFACE:
+	case WM8994_AIF1_CLOCKING_1:
+	case WM8994_AIF1_CLOCKING_2:
+	case WM8994_AIF2_CLOCKING_1:
+	case WM8994_AIF2_CLOCKING_2:
+	case WM8994_CLOCKING_1:
+	case WM8994_CLOCKING_2:
+	case WM8994_AIF1_RATE:
+	case WM8994_AIF2_RATE:
+	case WM8994_RATE_STATUS:
+	case WM8994_FLL1_CONTROL_1:
+	case WM8994_FLL1_CONTROL_2:
+	case WM8994_FLL1_CONTROL_3:
+	case WM8994_FLL1_CONTROL_4:
+	case WM8994_FLL1_CONTROL_5:
+	case WM8958_FLL1_EFS_1:
+	case WM8958_FLL1_EFS_2:
+	case WM8994_FLL2_CONTROL_1:
+	case WM8994_FLL2_CONTROL_2:
+	case WM8994_FLL2_CONTROL_3:
+	case WM8994_FLL2_CONTROL_4:
+	case WM8994_FLL2_CONTROL_5:
+	case WM8958_FLL2_EFS_1:
+	case WM8958_FLL2_EFS_2:
+	case WM8994_AIF1_CONTROL_1:
+	case WM8994_AIF1_CONTROL_2:
+	case WM8994_AIF1_MASTER_SLAVE:
+	case WM8994_AIF1_BCLK:
+	case WM8994_AIF1ADC_LRCLK:
+	case WM8994_AIF1DAC_LRCLK:
+	case WM8994_AIF1DAC_DATA:
+	case WM8994_AIF1ADC_DATA:
+	case WM8994_AIF2_CONTROL_1:
+	case WM8994_AIF2_CONTROL_2:
+	case WM8994_AIF2_MASTER_SLAVE:
+	case WM8994_AIF2_BCLK:
+	case WM8994_AIF2ADC_LRCLK:
+	case WM8994_AIF2DAC_LRCLK:
+	case WM8994_AIF2DAC_DATA:
+	case WM8994_AIF2ADC_DATA:
+	case WM1811_AIF2TX_CONTROL:
+	case WM8958_AIF3_CONTROL_1:
+	case WM8958_AIF3_CONTROL_2:
+	case WM8958_AIF3DAC_DATA:
+	case WM8958_AIF3ADC_DATA:
+	case WM8994_AIF1_ADC1_LEFT_VOLUME:
+	case WM8994_AIF1_ADC1_RIGHT_VOLUME:
+	case WM8994_AIF1_DAC1_LEFT_VOLUME:
+	case WM8994_AIF1_DAC1_RIGHT_VOLUME:
+	case WM8994_AIF1_ADC1_FILTERS:
+	case WM8994_AIF1_DAC1_FILTERS_1:
+	case WM8994_AIF1_DAC1_FILTERS_2:
+	case WM8958_AIF1_DAC1_NOISE_GATE:
+	case WM8994_AIF1_DRC1_1:
+	case WM8994_AIF1_DRC1_2:
+	case WM8994_AIF1_DRC1_3:
+	case WM8994_AIF1_DRC1_4:
+	case WM8994_AIF1_DRC1_5:
+	case WM8994_AIF1_DAC1_EQ_GAINS_1:
+	case WM8994_AIF1_DAC1_EQ_GAINS_2:
+	case WM8994_AIF1_DAC1_EQ_BAND_1_A:
+	case WM8994_AIF1_DAC1_EQ_BAND_1_B:
+	case WM8994_AIF1_DAC1_EQ_BAND_1_PG:
+	case WM8994_AIF1_DAC1_EQ_BAND_2_A:
+	case WM8994_AIF1_DAC1_EQ_BAND_2_B:
+	case WM8994_AIF1_DAC1_EQ_BAND_2_C:
+	case WM8994_AIF1_DAC1_EQ_BAND_2_PG:
+	case WM8994_AIF1_DAC1_EQ_BAND_3_A:
+	case WM8994_AIF1_DAC1_EQ_BAND_3_B:
+	case WM8994_AIF1_DAC1_EQ_BAND_3_C:
+	case WM8994_AIF1_DAC1_EQ_BAND_3_PG:
+	case WM8994_AIF1_DAC1_EQ_BAND_4_A:
+	case WM8994_AIF1_DAC1_EQ_BAND_4_B:
+	case WM8994_AIF1_DAC1_EQ_BAND_4_C:
+	case WM8994_AIF1_DAC1_EQ_BAND_4_PG:
+	case WM8994_AIF1_DAC1_EQ_BAND_5_A:
+	case WM8994_AIF1_DAC1_EQ_BAND_5_B:
+	case WM8994_AIF1_DAC1_EQ_BAND_5_PG:
+	case WM8994_AIF1_DAC1_EQ_BAND_1_C:
+	case WM8994_AIF2_ADC_LEFT_VOLUME:
+	case WM8994_AIF2_ADC_RIGHT_VOLUME:
+	case WM8994_AIF2_DAC_LEFT_VOLUME:
+	case WM8994_AIF2_DAC_RIGHT_VOLUME:
+	case WM8994_AIF2_ADC_FILTERS:
+	case WM8994_AIF2_DAC_FILTERS_1:
+	case WM8994_AIF2_DAC_FILTERS_2:
+	case WM8958_AIF2_DAC_NOISE_GATE:
+	case WM8994_AIF2_DRC_1:
+	case WM8994_AIF2_DRC_2:
+	case WM8994_AIF2_DRC_3:
+	case WM8994_AIF2_DRC_4:
+	case WM8994_AIF2_DRC_5:
+	case WM8994_AIF2_EQ_GAINS_1:
+	case WM8994_AIF2_EQ_GAINS_2:
+	case WM8994_AIF2_EQ_BAND_1_A:
+	case WM8994_AIF2_EQ_BAND_1_B:
+	case WM8994_AIF2_EQ_BAND_1_PG:
+	case WM8994_AIF2_EQ_BAND_2_A:
+	case WM8994_AIF2_EQ_BAND_2_B:
+	case WM8994_AIF2_EQ_BAND_2_C:
+	case WM8994_AIF2_EQ_BAND_2_PG:
+	case WM8994_AIF2_EQ_BAND_3_A:
+	case WM8994_AIF2_EQ_BAND_3_B:
+	case WM8994_AIF2_EQ_BAND_3_C:
+	case WM8994_AIF2_EQ_BAND_3_PG:
+	case WM8994_AIF2_EQ_BAND_4_A:
+	case WM8994_AIF2_EQ_BAND_4_B:
+	case WM8994_AIF2_EQ_BAND_4_C:
+	case WM8994_AIF2_EQ_BAND_4_PG:
+	case WM8994_AIF2_EQ_BAND_5_A:
+	case WM8994_AIF2_EQ_BAND_5_B:
+	case WM8994_AIF2_EQ_BAND_5_PG:
+	case WM8994_AIF2_EQ_BAND_1_C:
+	case WM8994_DAC1_MIXER_VOLUMES:
+	case WM8994_DAC1_LEFT_MIXER_ROUTING:
+	case WM8994_DAC1_RIGHT_MIXER_ROUTING:
+	case WM8994_DAC2_MIXER_VOLUMES:
+	case WM8994_DAC2_LEFT_MIXER_ROUTING:
+	case WM8994_DAC2_RIGHT_MIXER_ROUTING:
+	case WM8994_AIF1_ADC1_LEFT_MIXER_ROUTING:
+	case WM8994_AIF1_ADC1_RIGHT_MIXER_ROUTING:
+	case WM8994_DAC1_LEFT_VOLUME:
+	case WM8994_DAC1_RIGHT_VOLUME:
+	case WM8994_DAC2_LEFT_VOLUME:
+	case WM8994_DAC2_RIGHT_VOLUME:
+	case WM8994_DAC_SOFTMUTE:
+	case WM8994_OVERSAMPLING:
+	case WM8994_SIDETONE:
+	case WM8994_GPIO_1:
+	case WM8994_GPIO_2:
+	case WM8994_GPIO_3:
+	case WM8994_GPIO_4:
+	case WM8994_GPIO_5:
+	case WM8994_GPIO_6:
+	case WM8994_GPIO_8:
+	case WM8994_GPIO_9:
+	case WM8994_GPIO_10:
+	case WM8994_GPIO_11:
+	case WM8994_PULL_CONTROL_1:
+	case WM8994_PULL_CONTROL_2:
+	case WM8994_INTERRUPT_STATUS_1:
+	case WM8994_INTERRUPT_STATUS_2:
+	case WM8994_INTERRUPT_RAW_STATUS_2:
+	case WM8994_INTERRUPT_STATUS_1_MASK:
+	case WM8994_INTERRUPT_STATUS_2_MASK:
+	case WM8994_INTERRUPT_CONTROL:
+	case WM8994_IRQ_DEBOUNCE:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool wm8994_readable_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case WM8994_DC_SERVO_READBACK:
+	case WM8994_MICBIAS:
+	case WM8994_WRITE_SEQUENCER_CTRL_1:
+	case WM8994_WRITE_SEQUENCER_CTRL_2:
+	case WM8994_AIF1_ADC2_LEFT_VOLUME:
+	case WM8994_AIF1_ADC2_RIGHT_VOLUME:
+	case WM8994_AIF1_DAC2_LEFT_VOLUME:
+	case WM8994_AIF1_DAC2_RIGHT_VOLUME:
+	case WM8994_AIF1_ADC2_FILTERS:
+	case WM8994_AIF1_DAC2_FILTERS_1:
+	case WM8994_AIF1_DAC2_FILTERS_2:
+	case WM8958_AIF1_DAC2_NOISE_GATE:
+	case WM8994_AIF1_DRC2_1:
+	case WM8994_AIF1_DRC2_2:
+	case WM8994_AIF1_DRC2_3:
+	case WM8994_AIF1_DRC2_4:
+	case WM8994_AIF1_DRC2_5:
+	case WM8994_AIF1_DAC2_EQ_GAINS_1:
+	case WM8994_AIF1_DAC2_EQ_GAINS_2:
+	case WM8994_AIF1_DAC2_EQ_BAND_1_A:
+	case WM8994_AIF1_DAC2_EQ_BAND_1_B:
+	case WM8994_AIF1_DAC2_EQ_BAND_1_PG:
+	case WM8994_AIF1_DAC2_EQ_BAND_2_A:
+	case WM8994_AIF1_DAC2_EQ_BAND_2_B:
+	case WM8994_AIF1_DAC2_EQ_BAND_2_C:
+	case WM8994_AIF1_DAC2_EQ_BAND_2_PG:
+	case WM8994_AIF1_DAC2_EQ_BAND_3_A:
+	case WM8994_AIF1_DAC2_EQ_BAND_3_B:
+	case WM8994_AIF1_DAC2_EQ_BAND_3_C:
+	case WM8994_AIF1_DAC2_EQ_BAND_3_PG:
+	case WM8994_AIF1_DAC2_EQ_BAND_4_A:
+	case WM8994_AIF1_DAC2_EQ_BAND_4_B:
+	case WM8994_AIF1_DAC2_EQ_BAND_4_C:
+	case WM8994_AIF1_DAC2_EQ_BAND_4_PG:
+	case WM8994_AIF1_DAC2_EQ_BAND_5_A:
+	case WM8994_AIF1_DAC2_EQ_BAND_5_B:
+	case WM8994_AIF1_DAC2_EQ_BAND_5_PG:
+	case WM8994_AIF1_DAC2_EQ_BAND_1_C:
+	case WM8994_DAC2_MIXER_VOLUMES:
+	case WM8994_DAC2_LEFT_MIXER_ROUTING:
+	case WM8994_DAC2_RIGHT_MIXER_ROUTING:
+	case WM8994_AIF1_ADC2_LEFT_MIXER_ROUTING:
+	case WM8994_AIF1_ADC2_RIGHT_MIXER_ROUTING:
+	case WM8994_DAC2_LEFT_VOLUME:
+	case WM8994_DAC2_RIGHT_VOLUME:
+		return true;
+	default:
+		return wm1811_readable_register(dev, reg);
+	}
+}
+
+static bool wm8958_readable_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case WM8958_DSP2_PROGRAM:
+	case WM8958_DSP2_CONFIG:
+	case WM8958_DSP2_MAGICNUM:
+	case WM8958_DSP2_RELEASEYEAR:
+	case WM8958_DSP2_RELEASEMONTHDAY:
+	case WM8958_DSP2_RELEASETIME:
+	case WM8958_DSP2_VERMAJMIN:
+	case WM8958_DSP2_VERBUILD:
+	case WM8958_DSP2_TESTREG:
+	case WM8958_DSP2_XORREG:
+	case WM8958_DSP2_SHIFTMAXX:
+	case WM8958_DSP2_SHIFTMAXY:
+	case WM8958_DSP2_SHIFTMAXZ:
+	case WM8958_DSP2_SHIFTMAXEXTLO:
+	case WM8958_DSP2_AESSELECT:
+	case WM8958_DSP2_EXECCONTROL:
+	case WM8958_DSP2_SAMPLEBREAK:
+	case WM8958_DSP2_COUNTBREAK:
+	case WM8958_DSP2_INTSTATUS:
+	case WM8958_DSP2_EVENTSTATUS:
+	case WM8958_DSP2_INTMASK:
+	case WM8958_DSP2_CONFIGDWIDTH:
+	case WM8958_DSP2_CONFIGINSTR:
+	case WM8958_DSP2_CONFIGDMEM:
+	case WM8958_DSP2_CONFIGDELAYS:
+	case WM8958_DSP2_CONFIGNUMIO:
+	case WM8958_DSP2_CONFIGEXTDEPTH:
+	case WM8958_DSP2_CONFIGMULTIPLIER:
+	case WM8958_DSP2_CONFIGCTRLDWIDTH:
+	case WM8958_DSP2_CONFIGPIPELINE:
+	case WM8958_DSP2_SHIFTMAXEXTHI:
+	case WM8958_DSP2_SWVERSIONREG:
+	case WM8958_DSP2_CONFIGXMEM:
+	case WM8958_DSP2_CONFIGYMEM:
+	case WM8958_DSP2_CONFIGZMEM:
+	case WM8958_FW_BUILD_1:
+	case WM8958_FW_BUILD_0:
+	case WM8958_FW_ID_1:
+	case WM8958_FW_ID_0:
+	case WM8958_FW_MAJOR_1:
+	case WM8958_FW_MAJOR_0:
+	case WM8958_FW_MINOR_1:
+	case WM8958_FW_MINOR_0:
+	case WM8958_FW_PATCH_1:
+	case WM8958_FW_PATCH_0:
+	case WM8958_MBC_BAND_1_K_1:
+	case WM8958_MBC_BAND_1_K_2:
+	case WM8958_MBC_BAND_1_N1_1:
+	case WM8958_MBC_BAND_1_N1_2:
+	case WM8958_MBC_BAND_1_N2_1:
+	case WM8958_MBC_BAND_1_N2_2:
+	case WM8958_MBC_BAND_1_N3_1:
+	case WM8958_MBC_BAND_1_N3_2:
+	case WM8958_MBC_BAND_1_N4_1:
+	case WM8958_MBC_BAND_1_N4_2:
+	case WM8958_MBC_BAND_1_N5_1:
+	case WM8958_MBC_BAND_1_N5_2:
+	case WM8958_MBC_BAND_1_X1_1:
+	case WM8958_MBC_BAND_1_X1_2:
+	case WM8958_MBC_BAND_1_X2_1:
+	case WM8958_MBC_BAND_1_X2_2:
+	case WM8958_MBC_BAND_1_X3_1:
+	case WM8958_MBC_BAND_1_X3_2:
+	case WM8958_MBC_BAND_1_ATTACK_1:
+	case WM8958_MBC_BAND_1_ATTACK_2:
+	case WM8958_MBC_BAND_1_DECAY_1:
+	case WM8958_MBC_BAND_1_DECAY_2:
+	case WM8958_MBC_BAND_2_K_1:
+	case WM8958_MBC_BAND_2_K_2:
+	case WM8958_MBC_BAND_2_N1_1:
+	case WM8958_MBC_BAND_2_N1_2:
+	case WM8958_MBC_BAND_2_N2_1:
+	case WM8958_MBC_BAND_2_N2_2:
+	case WM8958_MBC_BAND_2_N3_1:
+	case WM8958_MBC_BAND_2_N3_2:
+	case WM8958_MBC_BAND_2_N4_1:
+	case WM8958_MBC_BAND_2_N4_2:
+	case WM8958_MBC_BAND_2_N5_1:
+	case WM8958_MBC_BAND_2_N5_2:
+	case WM8958_MBC_BAND_2_X1_1:
+	case WM8958_MBC_BAND_2_X1_2:
+	case WM8958_MBC_BAND_2_X2_1:
+	case WM8958_MBC_BAND_2_X2_2:
+	case WM8958_MBC_BAND_2_X3_1:
+	case WM8958_MBC_BAND_2_X3_2:
+	case WM8958_MBC_BAND_2_ATTACK_1:
+	case WM8958_MBC_BAND_2_ATTACK_2:
+	case WM8958_MBC_BAND_2_DECAY_1:
+	case WM8958_MBC_BAND_2_DECAY_2:
+	case WM8958_MBC_B2_PG2_1:
+	case WM8958_MBC_B2_PG2_2:
+	case WM8958_MBC_B1_PG2_1:
+	case WM8958_MBC_B1_PG2_2:
+	case WM8958_MBC_CROSSOVER_1:
+	case WM8958_MBC_CROSSOVER_2:
+	case WM8958_MBC_HPF_1:
+	case WM8958_MBC_HPF_2:
+	case WM8958_MBC_LPF_1:
+	case WM8958_MBC_LPF_2:
+	case WM8958_MBC_RMS_LIMIT_1:
+	case WM8958_MBC_RMS_LIMIT_2:
+		return true;
+	default:
+		return wm8994_readable_register(dev, reg);
+	}
+}
+
+static bool wm8994_volatile_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case WM8994_SOFTWARE_RESET:
+	case WM8994_DC_SERVO_1:
+	case WM8994_DC_SERVO_READBACK:
+	case WM8994_RATE_STATUS:
+	case WM8958_MIC_DETECT_3:
+	case WM8994_DC_SERVO_4E:
+	case WM8994_CHIP_REVISION:
+	case WM8994_INTERRUPT_STATUS_1:
+	case WM8994_INTERRUPT_STATUS_2:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool wm1811_volatile_register(struct device *dev, unsigned int reg)
+{
+	struct wm8994 *wm8994 = dev_get_drvdata(dev);
+
+	switch (reg) {
+	case WM8994_GPIO_6:
+		if (wm8994->revision > 1)
+			return true;
+		else
+			return false;
+	default:
+		return wm8994_volatile_register(dev, reg);
+	}
+}
+
+static bool wm8958_volatile_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case WM8958_DSP2_MAGICNUM:
+	case WM8958_DSP2_RELEASEYEAR:
+	case WM8958_DSP2_RELEASEMONTHDAY:
+	case WM8958_DSP2_RELEASETIME:
+	case WM8958_DSP2_VERMAJMIN:
+	case WM8958_DSP2_VERBUILD:
+	case WM8958_DSP2_EXECCONTROL:
+	case WM8958_DSP2_SWVERSIONREG:
+	case WM8958_DSP2_CONFIGXMEM:
+	case WM8958_DSP2_CONFIGYMEM:
+	case WM8958_DSP2_CONFIGZMEM:
+	case WM8958_FW_BUILD_1:
+	case WM8958_FW_BUILD_0:
+	case WM8958_FW_ID_1:
+	case WM8958_FW_ID_0:
+	case WM8958_FW_MAJOR_1:
+	case WM8958_FW_MAJOR_0:
+	case WM8958_FW_MINOR_1:
+	case WM8958_FW_MINOR_0:
+	case WM8958_FW_PATCH_1:
+	case WM8958_FW_PATCH_0:
+		return true;
+	default:
+		return wm8994_volatile_register(dev, reg);
+	}
+}
+
+struct regmap_config wm1811_regmap_config = {
+	.reg_bits = 16,
+	.val_bits = 16,
+
+	.cache_type = REGCACHE_RBTREE,
+
+	.reg_defaults = wm1811_defaults,
+	.num_reg_defaults = ARRAY_SIZE(wm1811_defaults),
+
+	.max_register = WM8994_MAX_REGISTER,
+	.volatile_reg = wm1811_volatile_register,
+	.readable_reg = wm1811_readable_register,
+};
+
+struct regmap_config wm8994_regmap_config = {
+	.reg_bits = 16,
+	.val_bits = 16,
+
+	.cache_type = REGCACHE_RBTREE,
+
+	.reg_defaults = wm8994_defaults,
+	.num_reg_defaults = ARRAY_SIZE(wm8994_defaults),
+
+	.max_register = WM8994_MAX_REGISTER,
+	.volatile_reg = wm8994_volatile_register,
+	.readable_reg = wm8994_readable_register,
+};
+
+struct regmap_config wm8958_regmap_config = {
+	.reg_bits = 16,
+	.val_bits = 16,
+
+	.cache_type = REGCACHE_RBTREE,
+
+	.reg_defaults = wm8958_defaults,
+	.num_reg_defaults = ARRAY_SIZE(wm8958_defaults),
+
+	.max_register = WM8994_MAX_REGISTER,
+	.volatile_reg = wm8958_volatile_register,
+	.readable_reg = wm8958_readable_register,
+};
+
+struct regmap_config wm8994_base_regmap_config = {
+	.reg_bits = 16,
+	.val_bits = 16,
+};
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/wm8994.h b/ap/os/linux/linux-3.4.x/drivers/mfd/wm8994.h
new file mode 100644
index 0000000..6f39a84
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/wm8994.h
@@ -0,0 +1,25 @@
+/*
+ * wm8994.h -- WM8994 MFD internals
+ *
+ * Copyright 2011 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#ifndef __MFD_WM8994_H__
+#define __MFD_WM8994_H__
+
+#include <linux/regmap.h>
+
+extern struct regmap_config wm1811_regmap_config;
+extern struct regmap_config wm8994_regmap_config;
+extern struct regmap_config wm8958_regmap_config;
+extern struct regmap_config wm8994_base_regmap_config;
+
+#endif
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/zx234290-adc.c b/ap/os/linux/linux-3.4.x/drivers/mfd/zx234290-adc.c
new file mode 100644
index 0000000..453cbb8
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/zx234290-adc.c
@@ -0,0 +1,426 @@
+#include <linux/init.h>        /* For init/exit macros */

+#include <linux/module.h>      /* For MODULE_ marcros  */

+#include <linux/interrupt.h>

+#include <linux/spinlock.h>

+#include <linux/platform_device.h>

+

+#include <linux/device.h>

+#include <linux/fs.h>

+#include <linux/cdev.h>

+#include <linux/delay.h>

+#include <linux/mutex.h>

+#include <linux/kthread.h>

+#include <linux/rtc.h>

+#include <linux/mfd/zx234290.h>

+#include <mach/spinlock.h>

+#include <linux/wakelock.h>

+

+#define ZX234290_VBAT_ADC_COMPENDATE_VALUE  50

+typedef enum adc_channel

+{

+    ADC_CHANNEL_VBAT_ADC = 0,

+    ADC_CHANNEL_VADC2 = 1,	/* 01 */

+    ADC_CHANNEL_VADC1 = 2,	/* 10 */

+    MAX_ADC_CHANNEL

+}zx234290_adc_channel;

+

+/*******************************************************************************

+ * Function:zx234290_adc_correspond

+ * Description:adjust battery voltage  according to compensate value

+ * Parameters:

+ *	 Input:

+ *        channel: adc channel

+ *        value:battery voltage  needed be compensated

+ *	 Output:

+ *

+ * Returns:

+ *    0:  success

+ *     -1:failed

+ *

+ * Others:

+ ********************************************************************************/

+static int zx234290_adc_correspond(zx234290_adc_channel channel, int *value)

+{

+	int nTemp;

+

+	switch (channel)

+	{

+		case ADC_CHANNEL_VBAT_ADC:

+			{

+				nTemp = (int)((uint64_t)(5000-0)*(*value)/4096);

+				*value = nTemp + 0;//ZX234290_VBAT_ADC_COMPENDATE_VALUE; // compensate the battery voltage value

+				break;

+			}

+		case ADC_CHANNEL_VADC1:

+			{

+				nTemp = (int)((uint64_t)(5000-0)*(*value)/4096);

+				*value = nTemp + 0;

+				break;

+			}

+

+		case ADC_CHANNEL_VADC2:

+			{

+				nTemp = (int)((uint64_t)(5000-0)*(*value)/4096);

+				*value = nTemp + 0;

+				break;

+			}

+		default:

+			{

+				return -1;

+			}

+	}

+

+

+	return 0;

+

+}

+

+/*******************************************************************************

+ * Function:zx234290_adc_read_vbat

+ * Description:read vbat adc value

+ * Parameters:

+ *	 Input:

+ *

+ *	 Output:

+ *

+ * Returns:

+ *    nTempAdc:the value of vbat adc

+ * Others:

+ ********************************************************************************/

+static int zx234290_adc_read_vbat(void)

+{

+	int nRet;

+	int nTempAdc = 0;

+	unsigned char msb=0, lsb=0;

+

+	/* read msb */

+	//nRet = zx234290_i2c_read_simple(ZX234290_REG_ADDR_ADC_VBATMSB, &msb);

+	nRet = zx234290_i2c_read_simple(ZX234290_REG_ADDR_VBATADC_MSB, &msb);

+	if (nRet != 0)

+	{

+		return nRet;

+	}

+

+	/* read lsb */

+	//nRet = zx234290_i2c_read_simple(ZX234290_REG_ADDR_ADC_VBATLSB, &lsb);

+	nRet = zx234290_i2c_read_simple(ZX234290_REG_ADDR_VBATADC_LSB, &lsb);

+	if (nRet != 0)

+	{

+		return nRet;

+	}

+	nTempAdc = (int)msb;

+	nTempAdc = ((nTempAdc<<4)|lsb>>4);

+

+	return nTempAdc;

+}

+

+/*******************************************************************************

+ * Function:zx234290_adc_read_adc1

+ * Description:read the channel of  adc1 value

+ * Parameters:

+ *	 Input:

+ *

+ *	 Output:

+ *

+ * Returns:

+ *    nTempAdc:the value of  adc1 channel

+ * Others:

+ ********************************************************************************/

+static int zx234290_adc_read_adc1(void)

+{

+	int nRet;

+	int nTempAdc = 0;

+	unsigned char msb=0, lsb=0;

+

+    /* read msb */

+	//nRet = zx234290_i2c_read_simple(ZX234290_REG_ADDR_ADC1MSB, &msb);

+	nRet = zx234290_i2c_read_simple(ZX234290_REG_ADDR_ADC1_MSB, &msb);

+	if (nRet != 0)

+	{

+		return nRet;

+	}

+

+	/* read lsb */

+	//nRet = zx234290_i2c_read_simple(ZX234290_REG_ADDR_ADC1LSB, &lsb);

+	nRet = zx234290_i2c_read_simple(ZX234290_REG_ADDR_ADC1_LSB, &lsb);

+	if (nRet != 0)

+	{

+		return nRet;

+	}

+

+	nTempAdc = (int)msb;

+	nTempAdc = ((nTempAdc<<4)|lsb>>4);

+

+	return nTempAdc;

+}

+

+/*******************************************************************************

+ * Function:zx234290_adc_read_adc2

+ * Description:read the channel of  adc2value

+ * Parameters:

+ *	 Input:

+ *

+ *	 Output:

+ *

+ * Returns:

+ *    nTempAdc:the value of  adc2 channel

+ * Others:

+ ********************************************************************************/

+static int zx234290_adc_read_adc2(void)

+{

+	int nRet;

+	int nTempAdc = 0;

+	unsigned char msb=0, lsb=0;

+

+	 /* read msb */

+	//nRet = zx234290_i2c_read_simple(ZX234290_REG_ADDR_ADC2MSB, &msb);

+	nRet = zx234290_i2c_read_simple(ZX234290_REG_ADDR_ADC2_MSB, &msb);

+	if (nRet != 0)

+	{

+		return nRet;

+	}

+

+	/* read lsb */

+	//nRet = zx234290_i2c_read_simple(ZX234290_REG_ADDR_ADC2LSB, &lsb);

+	nRet = zx234290_i2c_read_simple(ZX234290_REG_ADDR_ADC2_LSB, &lsb);

+	if (nRet != 0)

+	{

+		return nRet;

+	}

+

+	nTempAdc = (int)msb;

+	nTempAdc = ((nTempAdc<<4)|lsb>>4);

+

+	return nTempAdc;

+}

+

+

+/**************************************************************************

+* Function: 	zx234290_adc_read

+* Description:	read ADC value according to adc channel

+* Parameters:

+*	Input:

+*       channel:which channel want to be read

+*

+*	Output:

+*       value:the adc value correspond to channel

+* Returns:

+*				0 if success, not 0 if faile

+* Others: None

+**************************************************************************/

+extern struct wake_lock adc_wake_lock;

+static int zx234290_adc_read(zx234290_adc_channel channel, int *value)

+{

+	int nRet = 0;

+	int nTempAdc = 0;

+	int  reg_val=0, mask=0;

+	int num=1;

+	unsigned char status_a=0;

+    unsigned char content =0;

+	if(channel >= MAX_ADC_CHANNEL)

+	{

+		return -1;

+	}

+

+	soft_spin_lock(ADC_SFLOCK);

+	wake_lock(&adc_wake_lock);

+	if (channel != ADC_CHANNEL_VBAT_ADC)

+	{

+		/* select channel */

+		reg_val = channel << 3;	/* ×óÒÆ3λ£¬Î»¿í 2λ	*/

+		mask = 0x18;

+		nRet = zx234290_i2c_read_simple(ZX234290_REG_ADDR_SYS_CTRL, &content);

+	        if (nRet != 0)

+	        {

+	        	wake_unlock(&adc_wake_lock);

+	        	soft_spin_unlock(ADC_SFLOCK);

+			return nRet;

+	        }

+		content &= ~mask;

+		content |= reg_val;

+		nRet = zx234290_i2c_write_simple(ZX234290_REG_ADDR_SYS_CTRL, &content);

+		if (nRet != 0)

+		{

+			wake_unlock(&adc_wake_lock);

+	        	soft_spin_unlock(ADC_SFLOCK);

+			return nRet;

+		}

+	}

+#if 0

+	/* set start bit */

+	reg_val = 1 << 5; /* ¸ÃλÖà 1	*/

+	mask = 0x20;

+	nRet = zx234290_i2c_read_simple(ZX234290_REG_ADDR_SYS_CTRL, &content);

+    if (nRet != 0)

+    {

+        return nRet;

+    }

+    content &= ~mask;

+    content |= reg_val;

+	nRet = zx234290_i2c_write_simple(ZX234290_REG_ADDR_SYS_CTRL, &content);

+	if (nRet != 0)

+	{

+		return nRet;

+	}

+

+	msleep(30);        /* ÑÓʱ30ms */

+#else

+	reg_val = 1 << 5; /* ¸ÃλÖà 1	*/

+	mask = 0x20;

+	for(num=1; num <= 50; num++)

+	{

+		/* set start bit */

+		nRet = zx234290_i2c_read_simple(ZX234290_REG_ADDR_SYS_CTRL, &content);

+		if (nRet != 0)

+		{

+			wake_unlock(&adc_wake_lock);

+			soft_spin_unlock(ADC_SFLOCK);

+			return nRet;

+		}

+		content &= ~mask;

+		content |= reg_val;

+		nRet = zx234290_i2c_write_simple(ZX234290_REG_ADDR_SYS_CTRL, &content);

+		if (nRet != 0)

+		{

+			wake_unlock(&adc_wake_lock);

+			soft_spin_unlock(ADC_SFLOCK);

+			return nRet;

+		}

+		udelay(500);

+		/*read status_A*/

+		nRet = zx234290_i2c_read_simple(ZX234290_REG_ADDR_STSA, &status_a);

+		if (nRet != 0)

+		{

+			wake_unlock(&adc_wake_lock);

+			soft_spin_unlock(ADC_SFLOCK);

+			return nRet;

+	    }

+		if(status_a & 0x04)

+		{

+//			printk( "get adc,break num =%d ...\n", num);

+			break;

+		}

+	}

+#endif

+	/* ÕâÀïÒªµÈÖжÏÀ´ ²ÅÄܶÁ	*/

+	switch (channel) {

+	case ADC_CHANNEL_VBAT_ADC:

+		nTempAdc = zx234290_adc_read_vbat();

+		break;

+	case ADC_CHANNEL_VADC1:

+		nTempAdc = zx234290_adc_read_adc1();

+		break;

+	case ADC_CHANNEL_VADC2:

+		nTempAdc = zx234290_adc_read_adc2();

+		break;

+	default:

+		break;

+	}

+

+	if (nTempAdc < 0)

+	{

+		wake_unlock(&adc_wake_lock);

+		soft_spin_unlock(ADC_SFLOCK);

+		return 0;

+	}

+

+	/* ת»» */

+	zx234290_adc_correspond(channel, &nTempAdc);

+	*value = (int)nTempAdc;

+	wake_unlock(&adc_wake_lock);

+	soft_spin_unlock(ADC_SFLOCK);

+	//pmic_AdcUnlock();

+	return nRet;

+}

+

+

+

+/**************************************************************************

+* Function: 	get_battery_voltage

+* Description:	get battery voltage

+* Parameters:

+*	Input:

+*

+*

+*	Output:

+*

+* Returns:

+*	avgValue:battery voltage

+* Others: None

+**************************************************************************/

+uint get_battery_voltage(void)

+{

+	int counter = 3;

+	int index=0;

+	int tmpValue=0,totalValue=0;

+	uint avgValue;

+	for(index = 0; index < counter; index++)

+	{

+		zx234290_adc_read(ADC_CHANNEL_VBAT_ADC, &tmpValue);

+		totalValue += tmpValue;

+	}

+	avgValue = (uint)(totalValue / counter);

+	return avgValue;

+}

+EXPORT_SYMBOL(get_battery_voltage);

+

+/**************************************************************************

+* Function: 	get_adc1_voltage

+* Description:	get adc1 voltage

+* Parameters:

+*	Input:

+*

+*

+*	Output:

+*

+* Returns:

+*	avgValue:adc voltage

+* Others: None

+**************************************************************************/

+uint get_adc1_voltage(void)

+{

+	int counter = 3;

+	int index=0;

+	int tmpValue=0,totalValue=0;

+	uint avgValue;

+	for(index = 0; index < counter; index++)

+	{

+		zx234290_adc_read(ADC_CHANNEL_VADC1, &tmpValue);

+		totalValue += tmpValue;

+	}

+	avgValue = (uint)(totalValue / counter);

+	return avgValue;

+}

+EXPORT_SYMBOL(get_adc1_voltage);

+

+/**************************************************************************

+* Function: 	get_adc2_voltage

+* Description:	get adc2 voltage

+* Parameters:

+*	Input:

+*

+*

+*	Output:

+*

+* Returns:

+*	avgValue:adc2 voltage

+* Others: None

+**************************************************************************/

+//extern struct wake_lock adc_wake_lock;

+uint get_adc2_voltage(void)

+{

+	int counter = 3;

+	int index=0;

+	int tmpValue=0,totalValue=0;

+	uint avgValue;

+	//wake_lock(&adc_wake_lock);

+	for(index = 0; index < counter; index++)

+	{

+		zx234290_adc_read(ADC_CHANNEL_VADC2, &tmpValue);

+		totalValue += tmpValue;

+	}

+	//wake_unlock(&adc_wake_lock);

+	avgValue = (uint)(totalValue / counter);

+	return avgValue;

+}

+EXPORT_SYMBOL(get_adc2_voltage);

diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/zx234290-core.c b/ap/os/linux/linux-3.4.x/drivers/mfd/zx234290-core.c
new file mode 100644
index 0000000..7fc94b3
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/zx234290-core.c
@@ -0,0 +1,617 @@
+/*
+ * zx234290-core.c  --  Device access for ZX234290 PMICs
+ *
+ * Copyright 2016 ZTE Inc.
+ *
+ * Author: yuxiang<yu.xiang5@zte.com.cn>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/zx234290.h>
+
+#include <linux/debugfs.h>
+#include <asm/uaccess.h>
+
+#include <mach/gpio-names.h>
+#include <linux/gpio_keys.h>
+
+#include <mach/iomap.h>
+#include <linux/delay.h>
+
+#include <mach/peri_cfg.h>
+
+/*the power on info, boot_reason */
+typedef enum
+{
+	POWER_ON_NORMAL = 0,
+	POWER_ON_FOTA,
+	POWER_ON_CHARGING,
+	POWER_ON_RTC,
+	POWER_ON_RESET,
+	POWER_ON_HDT_TEST,
+	POWER_ON_EXCEPTRESET,
+	POWER_ON_LOCALUPDATE,
+	POWER_ON_BOOST_IN,
+	POWER_ON_AMT,
+	POWER_ON_PRODUCTION,
+	POWER_ON_INVALID,
+}T_ZDrvSys_PowerOn_Type;
+
+#ifdef CONFIG_KEYBOARD_ZX234290_UFI
+/* --------------------------------------------------------------------
+ *  Keypad (power_on, ufi, ufi_reset) for ufi
+ * -------------------------------------------------------------------- */
+static struct gpio_keys_button zx297510_keypad[] = {
+	[0] = {
+		.gpio		= AO_GPIO_13,		/* UFI_RESET */
+		.desc		= "reset",
+		.active_low	= 1,     //ÊÇ·ñµÍµçƽÓÐЧ¡£1: °´ÏÂΪµÍµçƽ  0: °´ÏÂΪ¸ßµçƽ
+		},
+	[1] = {
+		.gpio		= AO_GPIO_14,		/* POWER - gpioΪÎÞЧ²ÎÊý */
+		.desc		= "power",
+		.active_low	= 0,
+		},
+	[2] = {
+		.gpio		= AO_GPIO_12,		/* WIFI */
+		.desc		= "wifi",
+		.active_low	= 1,
+		},
+};
+
+static struct gpio_keys_platform_data zx297510_keypad_data = {
+	.buttons	= zx297510_keypad,
+	.nbuttons	= ARRAY_SIZE(zx297510_keypad),
+};
+#endif
+
+static struct mfd_cell zx234290_cell[] = {
+	{
+		.name = "zx234290-regulators",
+	},
+	{
+		.name = "zx234290-rtc",
+	},
+	{
+		.name = "zx234290-gpadc",
+	},
+	#ifdef CONFIG_KEYBOARD_ZX234290_UFI
+	{
+		.name = "zx234290_keypad",
+		.id 	=	-1,
+		.platform_data = &zx297510_keypad_data,
+		.pdata_size    = sizeof(zx297510_keypad_data),
+	},
+	#endif
+};
+
+unsigned int boot_reason = POWER_ON_NORMAL;
+
+unsigned int * get_boot_reason_addr(void)
+{
+	return (unsigned int *)POWERON_TYPE_BASE;
+}
+EXPORT_SYMBOL(get_boot_reason_addr);
+
+static void get_boot_reason(void)
+{
+#ifdef POWERON_TYPE_BASE
+	boot_reason = *(unsigned int *)POWERON_TYPE_BASE;
+
+//cl:È¥³ý²»±ØÒªµÄÄں˴òÓ¡
+//	printk(KERN_INFO "[PMU] get boot_reason = %d from 0x%p.\n",
+//								boot_reason, (void *)POWERON_TYPE_BASE);
+#else
+	printk(KERN_INFO "[PMU] boot_reason is unknown.\n");
+#endif
+}
+
+#if 1
+int zx234290_set_bits(struct zx234290 *zx234290, u8 reg, u8 mask)
+{
+	u8 data;
+	int err;
+
+	mutex_lock(&zx234290->io_mutex);
+
+	err = zx234290->read(zx234290, reg, 1, &data);
+	if (err) {
+		dev_err(zx234290->dev, "Read from reg 0x%x failed\n", reg);
+		goto out;
+	}
+
+	data |= mask;
+	err = zx234290->write(zx234290, reg, 1, &data);
+	if (err)
+		dev_err(zx234290->dev, "Write to reg 0x%x failed\n", reg);
+
+out:
+	mutex_unlock(&zx234290->io_mutex);
+	return err;
+}
+EXPORT_SYMBOL_GPL(zx234290_set_bits);
+
+int zx234290_clear_bits(struct zx234290 *zx234290, u8 reg, u8 mask)
+{
+	u8 data;
+	int err;
+
+	mutex_lock(&zx234290->io_mutex);
+	err = zx234290->read(zx234290, reg, 1, &data);
+	if (err) {
+		dev_err(zx234290->dev, "Read from reg 0x%x failed\n", reg);
+		goto out;
+	}
+
+	data &= ~mask;
+	err = zx234290->write(zx234290, reg, 1, &data);
+	if (err)
+		dev_err(zx234290->dev, "Write to reg 0x%x failed\n", reg);
+
+out:
+	mutex_unlock(&zx234290->io_mutex);
+	return err;
+}
+EXPORT_SYMBOL_GPL(zx234290_clear_bits);
+#endif
+
+static inline int zx234290_read(struct zx234290 *zx234290, u8 reg)
+{
+	u8 val;
+	int err;
+
+	err = zx234290->read(zx234290, reg, 1, &val);
+	if (err < 0)
+		return err;
+
+	return val;
+}
+
+static inline int zx234290_write(struct zx234290 *zx234290, u8 reg, u8 val)
+{
+	return zx234290->write(zx234290, reg, 1, &val);
+}
+
+#if 1
+int zx234290_reg_read(struct zx234290 *zx234290, u8 reg)
+{
+	int data;
+
+	mutex_lock(&zx234290->io_mutex);
+
+	data = zx234290_read(zx234290, reg);
+	if (data < 0)
+		dev_err(zx234290->dev, "Read from reg 0x%x failed\n", reg);
+
+	mutex_unlock(&zx234290->io_mutex);
+	return data;
+}
+EXPORT_SYMBOL_GPL(zx234290_reg_read);
+
+int zx234290_reg_write(struct zx234290 *zx234290, u8 reg, u8 val)
+{
+	int err;
+
+	mutex_lock(&zx234290->io_mutex);
+
+	err = zx234290_write(zx234290, reg, val);
+	if (err < 0)
+		dev_err(zx234290->dev, "Write for reg 0x%x failed\n", reg);
+
+	mutex_unlock(&zx234290->io_mutex);
+	return err;
+}
+EXPORT_SYMBOL_GPL(zx234290_reg_write);
+#endif
+
+extern int zx234290_i2c_write_simple(u8 reg, void *src);
+extern int zx234290_i2c_read_simple(u8 reg, void *dest);
+int Zx234290_SetVldo6Onoff(void)
+{
+    int ret = 0;
+    u8 reg_addr=0, reg_val=0;
+    reg_addr = 0x21;
+    ret = zx234290_i2c_read_simple(reg_addr,&reg_val);
+    if (ret) {
+        return -EIO;
+    }
+    reg_val = reg_val&(~(1<<5));
+    ret = zx234290_i2c_write_simple(reg_addr, &reg_val);
+    if (ret) {
+        return -EIO;
+    }
+    return 0;
+}
+EXPORT_SYMBOL(Zx234290_SetVldo6Onoff);
+#if 0
+int zx297510_write_pmu_flag_charging(void)
+{
+	int ret = 0;
+	unsigned char reg = 0;
+	ret = zx234290_i2c_read_simple(0xf, &reg);
+	reg = reg|0xff;
+	ret += zx234290_i2c_write_simple(0xf, &reg);
+	ret = zx234290_i2c_read_simple(0xe, &reg);
+	reg = reg|0x3;
+	ret += zx234290_i2c_write_simple(0xe, &reg);
+	return ret;
+}
+#endif
+//static void __iomem* PMU_ADDR_VIR;
+//#define GPIO_PMU_PSHOLD ZX29_GPIO_51
+unsigned int gpio_num_pshold;
+gpio_func_id gpio_func_pshold;
+int pshold_gpio_flag=0xff;
+void zx234290_pshold_pull_down(void)
+{
+	//PMU_ADDR_VIR = ioremap(0x10d6c0,4);
+	//__raw_writel(0x0,PMU_ADDR_VIR);
+	if(pshold_gpio_flag)
+		pshold_gpio_flag = gpio_request(gpio_num_pshold, "pshold1");
+	zx29_gpio_config(gpio_num_pshold, gpio_func_pshold);
+	//zx29_gpio_set_direction(gpio_num_pshold, GPIO_IN); //set output;v3 gpio24(pshold) direction is reverse
+	zx29_gpio_set_direction(gpio_num_pshold, GPIO_OUT);
+	zx29_gpio_output_data(gpio_num_pshold, 0);
+}
+
+//extern int zx234290_rtc_disable_timer_alarm();
+EXPORT_SYMBOL(zx234290_pshold_pull_down);
+
+/***********yuwei added at 20170523**************/
+void zx234290_pshold_pull_up(void)
+{
+    //PMU_ADDR_VIR = ioremap(0x10d6c0,4);
+    //__raw_writel(0x0,PMU_ADDR_VIR);
+	if(pshold_gpio_flag)
+		pshold_gpio_flag = gpio_request(gpio_num_pshold, "pshold1");
+
+	zx29_gpio_config(gpio_num_pshold, gpio_func_pshold);
+	//zx29_gpio_set_direction(gpio_num_pshold, GPIO_IN); //set output;v3 gpio24(pshold) direction is reverse
+	zx29_gpio_set_direction(gpio_num_pshold, GPIO_OUT);
+	zx29_gpio_output_data(gpio_num_pshold, 1);
+
+//	printk("[yuwei] %s, pshold_gpio =%d, for charging in poweroff.\n",__FUNCTION__,pshold_gpio_flag);
+}
+/**************/
+
+static int zx234290_set_softon(int on)
+{
+	u8 reg = 0;
+    int ret;
+
+    ret = zx234290_i2c_read_simple(ZX234290_REG_ADDR_SYS_CTRL, &reg);
+    if (ret) {
+        return -EIO;
+    }
+
+    if ((reg >> ZX234290_SOFTON_LSH) != on) {
+        reg ^= (0x01 << ZX234290_SOFTON_LSH);
+        ret = zx234290_i2c_write_simple(ZX234290_REG_ADDR_SYS_CTRL, &reg);
+        if (ret) {
+            return -EIO;
+        }
+    }
+
+    return 0;
+}
+
+static bool debug_stop_poweroff = false;
+module_param(debug_stop_poweroff, bool, 0644);
+extern void zx29_restart(char str,const char * cmd);
+static void zx234290_power_off(void)
+{
+	//void __iomem *reset_charging_reg;
+	//reset_charging_reg = ZX29_TOP_VA;
+	//zx234290_rtc_disable_timer_alarm();
+	//Zx234290_SetVldo6Onoff();
+	u8 reg_poweron = 0;
+    int ret;
+
+	if(debug_stop_poweroff )
+	{
+		printk(KERN_INFO"debug_stop_poweroff= 0x%x, for debug, bug_on!!!!\n", debug_stop_poweroff);
+		panic("poweroff");
+	}
+	zx234290_set_softon(0);
+	zx234290_pshold_pull_down();
+#if 1
+	while(1){
+		ret = zx234290_i2c_read_simple(ZX234290_REG_ADDR_STSA, &reg_poweron);	
+		if (ret) {
+			printk(KERN_INFO"power off pmu i2c read err\n");
+			break;
+		}
+		if((reg_poweron&(1<<ZX234290_STATUSA_POWERON_LSH))== 0)
+			break;
+	}
+	msleep(50);
+	/*reset to charging*/
+	zx29_restart(NULL,"drv_key reboot");
+#endif
+}
+
+
+#if defined(CONFIG_DEBUG_FS)
+static ssize_t debugfs_regs_write(struct file *file, const char __user *buf,size_t nbytes, loff_t *ppos)
+{
+	struct zx234290 *zx234290 = file->private_data;
+
+	unsigned int val1, val2;
+	u8 reg, value;
+	int ret;
+	char *kern_buf;
+
+	kern_buf = kzalloc(nbytes, GFP_KERNEL);
+
+	if (!kern_buf) {
+		printk(KERN_INFO "zx234290-core: Failed to allocate buffer\n");
+		return -ENOMEM;
+	}
+
+	if (copy_from_user(kern_buf, (void  __user *)buf, nbytes)) {
+		kfree(kern_buf);
+		return -ENOMEM;
+	}
+	printk(KERN_INFO "%s input str=%s,nbytes=%d \n", __func__, kern_buf,nbytes);
+
+	ret = sscanf(kern_buf, "%x:%x", &val1, &val2);
+	if (ret < 2 || val1 > ZX234290_MAX_REGISTER ) {
+		printk(KERN_INFO "zx234290-core: failed to read user buf, ret=%d, input 0x%x:0x%x\n",
+				ret, val1, val2);
+		kfree(kern_buf);
+		return -EINVAL;
+	}
+	kfree(kern_buf);
+
+	reg = val1 & 0xff;
+	value = val2 & 0xff;
+	printk(KERN_INFO "%s input %x,%x; reg=%x,value=%x\n", __func__, val1, val2, reg, value);
+	ret = zx234290_i2c_write_simple(reg, &value);
+
+	return ret ? ret : nbytes;
+}
+
+static int debugfs_regs_show(struct seq_file *s, void *v)
+{
+	int i;
+	u8 value[ZX234290_MAX_REGISTER];
+	int ret=0;
+	u8 reg_rtc_ctrl2 = 0;
+
+	printk(KERN_INFO "%s\n", __func__);
+	memset(value, 0, sizeof(value));
+	for (i = 0; i < ZX234290_MAX_REGISTER; i++){
+		ret = zx234290_i2c_read_simple(i, &(value[i]));
+		if(ret){
+			printk(KERN_INFO "%s err=%d, break\n", __func__, ret);
+			seq_printf(s, "%s err=%d, break", __func__, ret);
+			return ret;
+		}
+	}
+
+	for (i = 0; i < ZX234290_MAX_REGISTER; i++) {
+		if((i+1)%9 == 0)
+			seq_printf(s, "\n");
+
+		seq_printf(s, "[0x%x]%02x ", i, value[i]);
+	}
+
+	reg_rtc_ctrl2 = value[ZX234290_REG_ADDR_RTC_CTRL2];
+	seq_printf(s, "\nAF=%d,TF=%d,Alarm %s,Timer %s\n",(reg_rtc_ctrl2&0x8),(reg_rtc_ctrl2&0x4),
+		  			(reg_rtc_ctrl2&0x2)? "enable":"disable",(reg_rtc_ctrl2&0x1)? "enable":"disable");
+	if(value[ZX234290_REG_ADDR_BUCK_FAULT_STATUS]||value[ZX234290_REG_ADDR_LDO_FAULT_STATUS])
+		seq_printf(s, "ldo or bulk fault!!!!!\n ");
+	else
+		seq_printf(s, "no ldo or bulk fault\n ");
+	if(value[ZX234290_REG_ADDR_TIMER_CTRL]&0x80)
+		seq_printf(s, "timer enable\n ");
+	else
+		seq_printf(s, "timer disable\n ");
+
+	return ret;
+}
+
+#define DEBUGFS_FILE_ENTRY(name) \
+static int debugfs_##name##_open(struct inode *inode, struct file *file) \
+{\
+return single_open(file, debugfs_##name##_show, inode->i_private); \
+}\
+\
+static const struct file_operations debugfs_##name##_fops = { \
+.owner= THIS_MODULE, \
+.open= debugfs_##name##_open, \
+.write=debugfs_##name##_write, \
+.read= seq_read, \
+.llseek= seq_lseek, \
+.release= single_release, \
+}
+
+
+DEBUGFS_FILE_ENTRY(regs);
+
+static int debugfs_adc_get(void *data, u64 *val)
+{
+	switch ((int)data) {
+	case 0:
+		*val = get_battery_voltage();
+		break;
+	case 1:
+		*val = get_adc1_voltage();
+		break;
+	case 2:
+		*val = get_adc2_voltage();
+		break;
+	default:
+		*val = -1;
+		break;
+	}
+
+    return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(fops_adc_ro, debugfs_adc_get, NULL, "%llumV\n");
+
+static struct dentry *g_pmu_root;
+
+extern unsigned long int_irq_times;
+extern unsigned long  int_thread_times;
+
+static void debugfs_pmu_init(struct zx234290 *zx234290)
+{
+	struct dentry *root;
+	struct dentry *node;
+	int i;
+
+	if(!zx234290)
+		return;
+
+	//create root
+	root = debugfs_create_dir("pmu_zx29", NULL);
+	if (!root)	{
+		dev_err(&zx234290->dev, "debugfs_create_dir err=%d\n", IS_ERR(root));
+		goto err;
+	}
+
+	//print regs;
+	node = debugfs_create_file("regs", S_IRUGO | S_IWUGO, root, zx234290,  &debugfs_regs_fops);
+	if (!node){
+		dev_err(&zx234290->dev, "debugfs_create_dir err=%d\n", IS_ERR(node));
+		goto err;
+	}
+
+	//print adc0;
+	node = debugfs_create_file("adc0", S_IRUGO, root, 0,  &fops_adc_ro);
+	if (!node){
+		dev_err(&zx234290->dev, "debugfs_create_dir err=%d\n", IS_ERR(node));
+		goto err;
+	}
+
+	//print adc1;
+	node = debugfs_create_file("adc1", S_IRUGO, root, 1,  &fops_adc_ro);
+	if (!node){
+		dev_err(&zx234290->dev, "debugfs_create_dir err=%d\n", IS_ERR(node));
+		goto err;
+	}
+
+	//print adc2;
+	node = debugfs_create_file("adc2", S_IRUGO, root, 2,  &fops_adc_ro);
+	if (!node){
+		dev_err(&zx234290->dev, "debugfs_create_dir err=%d\n", IS_ERR(node));
+		goto err;
+	}
+
+	//print u32
+	node = debugfs_create_u32("irq_cnt", S_IRUGO, root, &int_irq_times);
+	if (!node){
+		dev_err(&zx234290->dev, "debugfs_create_u32  irq_cnt err=%d\n", IS_ERR(node));
+		goto err;
+	}
+
+	//print u32
+	node = debugfs_create_u32("thread_cnt", S_IRUGO, root, &int_thread_times);
+	if (!node){
+		dev_err(&zx234290->dev, "debugfs_create_u32  thread_cnt err=%d\n", IS_ERR(node));
+		goto err;
+	}
+
+	g_pmu_root = (void *)root;
+	return;
+err:
+	dev_err(&zx234290->dev, "debugfs_pmu_init err\n");
+}
+
+#endif
+
+int zx234290_device_init(struct zx234290 *zx234290)
+{
+	struct zx234290_board *pmic_plat_data = zx234290->dev->platform_data;
+	//struct zx234290_platform_data *init_data;
+	int ret;
+	int irq;
+
+	get_boot_reason();
+    /*
+	init_data = kzalloc(sizeof(struct zx234290_platform_data), GFP_KERNEL);
+	if (init_data == NULL)
+		return -ENOMEM;
+    */
+	mutex_init(&zx234290->io_mutex);
+	dev_set_drvdata(zx234290->dev, zx234290);
+
+	ret = mfd_add_devices(zx234290->dev, -1,
+			      zx234290_cell, ARRAY_SIZE(zx234290_cell),
+			      NULL, 0);
+	if (ret < 0)
+		goto err;
+
+    gpio_num_pshold = pmic_plat_data->pshold_gpio_num;//by yuxiang
+    gpio_func_pshold= pmic_plat_data->pshold_gpio_func;//by yuxiang
+	if (!pm_power_off)
+		pm_power_off = zx234290_power_off;
+
+#ifdef PSHOLD_PULLUP_IN_POWEROFFCHARGING
+	/* CPE MDL don't control ps_hold pin. */
+	if (boot_reason == POWER_ON_CHARGING) {
+		zx234290_pshold_pull_up();
+	}
+#endif
+/***********PJT added **************/
+	zx234290_get_chip_version();
+	zDrvZx234290_LdoInit();
+    if (POWER_ON_CHARGING == boot_reason) {
+		 zDrvZx234290_ChgOnSleepConfig();
+  	} else {
+		zDrvZx234290_NormalSleepConfig();
+	}
+
+/*************************/
+#ifndef AP_NO_NEED_PMU_INT_FOR_PHONE
+	//init_data->irq = pmic_plat_data->irq;
+	//init_data->irq_base = pmic_plat_data->irq_base;
+	irq = gpio_to_irq(pmic_plat_data->irq_gpio_num);
+	ret = zx234290_irq_init(zx234290, irq, pmic_plat_data);
+	if (ret < 0)
+		goto err;
+#endif
+
+
+#if defined(CONFIG_DEBUG_FS)
+	debugfs_pmu_init(zx234290);
+#endif
+	//kfree(init_data);
+	return ret;
+
+err:
+	//kfree(init_data);
+	mfd_remove_devices(zx234290->dev);
+	kfree(zx234290);
+	return ret;
+}
+
+void zx234290_device_exit(struct zx234290 *zx234290)
+{
+#if defined(CONFIG_DEBUG_FS)
+	if(g_pmu_root){
+		printk(KERN_INFO "zx234290_device_exit:debugfs_remove_recursive \n");
+		debugfs_remove_recursive(g_pmu_root);
+	}
+#endif
+	mfd_remove_devices(zx234290->dev);
+	kfree(zx234290);
+}
+
+
+MODULE_AUTHOR("yuxiang");
+MODULE_DESCRIPTION("ZX234290 chip family multi-function driver");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/zx234290-i2c.c b/ap/os/linux/linux-3.4.x/drivers/mfd/zx234290-i2c.c
new file mode 100644
index 0000000..a4c5c2f
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/zx234290-i2c.c
@@ -0,0 +1,304 @@
+/*
+ * zx234290-i2c.c  --  I2C access for ZTE ZX234290 PMIC
+ *
+ * Copyright 2016 ZTE Inc.
+ *
+ * Author: yuxiang<yu.xiang5@zte.com.cn>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under  the terms of the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/zx234290.h>
+#include <linux/wakelock.h>
+
+static struct i2c_client *zx234290_i2c =0;
+static DEFINE_MUTEX(zx234290_i2c_lock);
+
+int zx29_i2c_xfer_PSM(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
+#if 1
+int zx234290_i2c_read_simple(u8 reg, void *dest)
+{
+	mutex_lock(&zx234290_i2c_lock);
+//	struct i2c_client *i2c = zx234290->control_data;
+	struct i2c_client *i2c = zx234290_i2c;
+	struct i2c_msg xfer[2];
+	int ret;
+
+	/* Write register */
+	xfer[0].addr = i2c->addr;
+	xfer[0].flags = 0;
+	xfer[0].len = 1;
+	xfer[0].buf = &reg;
+
+	/* Read data */
+	xfer[1].addr = i2c->addr;
+	xfer[1].flags = I2C_M_RD;
+	xfer[1].len = 1;
+	xfer[1].buf = dest;
+
+	ret = i2c_transfer(i2c->adapter, xfer, 2);
+	if (ret == 2)
+		ret = 0;
+	else if (ret >= 0)
+	{
+		ret = -EIO;
+
+		printk(KERN_INFO "[zx234290 i2c]read-1 error. reg:%x\n", reg);
+	}
+	mutex_unlock(&zx234290_i2c_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(zx234290_i2c_read_simple);
+
+int zx234290_i2c_read_simple_PSM(u8 reg, void *dest)
+{
+//	struct i2c_client *i2c = zx234290->control_data;
+	struct i2c_client *i2c = zx234290_i2c;
+	struct i2c_msg xfer[2];
+	int ret;
+
+	/* Write register */
+	xfer[0].addr = i2c->addr;
+	xfer[0].flags = 0;
+	xfer[0].len = 1;
+	xfer[0].buf = &reg;
+
+	/* Read data */
+	xfer[1].addr = i2c->addr;
+	xfer[1].flags = I2C_M_RD;
+	xfer[1].len = 1;
+	xfer[1].buf = dest;
+
+	ret = zx29_i2c_xfer_PSM(i2c->adapter, xfer, 2);
+	if (ret == 2)
+		ret = 0;
+	else
+	{
+		ret = -EIO;
+
+		printk(KERN_INFO "[zx234290 i2c]read-psm error. reg:%x\n", reg);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(zx234290_i2c_read_simple_PSM);
+
+int zx234290_i2c_write_simple(u8 reg, void *src)
+{
+	mutex_lock(&zx234290_i2c_lock);
+//	struct i2c_client *i2c = zx234290->control_data;
+	struct i2c_client *i2c = zx234290_i2c;
+	/* we add 1 byte for device register */
+	u8 msg[4];
+	int ret;
+
+	//if (reg > ZX234290_MAX_REGISTER)
+	//	return -EINVAL;
+
+	msg[0] = reg;
+	memcpy(&msg[1], src, 1);
+
+	ret = i2c_master_send(i2c, msg, 1 + 1);
+	if (ret < 0){
+		mutex_unlock(&zx234290_i2c_lock);
+
+		printk(KERN_INFO "[zx234290 i2c]write-3 error. reg:%x\n", reg);
+
+		return ret;
+	}
+	if (ret != 1 + 1){
+		mutex_unlock(&zx234290_i2c_lock);
+		printk(KERN_INFO "[zx234290 i2c]write-4 error. reg:%x\n", reg);
+		return -EIO;
+	}
+
+	mutex_unlock(&zx234290_i2c_lock);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(zx234290_i2c_write_simple);
+
+int zx234290_i2c_write_simple_PSM(u8 reg, void *src)
+{
+//	struct i2c_client *i2c = zx234290->control_data;
+	struct i2c_client *i2c = zx234290_i2c;
+	/* we add 1 byte for device register */
+	u8 buf[4];
+	int ret;
+	struct i2c_msg msg;
+
+	if (reg > ZX234290_MAX_REGISTER)
+		return -EINVAL;
+
+	buf[0] = reg;
+	memcpy(buf+1, src, 1);
+
+	msg.addr = i2c->addr;
+	msg.flags = 0;
+	msg.len = 2;
+	msg.buf = (char *)buf;
+
+	ret = zx29_i2c_xfer_PSM(i2c->adapter, &msg, 1);
+	if (ret != 1) {
+		printk(KERN_INFO "[zx234290 i2c]write-psm error. reg:%x\n", reg);
+
+		return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(zx234290_i2c_write_simple_PSM);
+#endif
+
+
+
+//static i2c_client *zx234290_i2c=0;
+#if 1
+static int zx234290_i2c_read(struct zx234290 *zx234290, u8 reg,
+				  int bytes, void *dest)
+{
+	struct i2c_client *i2c = zx234290->control_data;
+//	struct i2c_client *i2c = zx234290_i2c;
+	struct i2c_msg xfer[2];
+	int ret;
+
+	/* Write register */
+	xfer[0].addr = i2c->addr;
+	xfer[0].flags = 0;
+	xfer[0].len = 1;
+	xfer[0].buf = &reg;
+
+	/* Read data */
+	xfer[1].addr = i2c->addr;
+	xfer[1].flags = I2C_M_RD;
+	xfer[1].len = bytes;
+	xfer[1].buf = dest;
+
+	ret = i2c_transfer(i2c->adapter, xfer, 2);
+	if (ret == 2)
+		ret = 0;
+	else if (ret >= 0)
+	{
+		ret = -EIO;
+
+		printk(KERN_INFO "[zx234290 i2c]read error. reg:%x\n", reg);
+	}
+
+	return ret;
+}
+
+static int zx234290_i2c_write(struct zx234290 *zx234290, u8 reg,
+				   int bytes, void *src)
+{
+	struct i2c_client *i2c = zx234290->control_data;
+//	struct i2c_client *i2c = zx234290_i2c;
+	/* we add 1 byte for device register */
+	u8 msg[ZX234290_MAX_REGISTER + 1];
+	int ret;
+
+	if (bytes > ZX234290_MAX_REGISTER)
+		return -EINVAL;
+
+	msg[0] = reg;
+	memcpy(&msg[1], src, bytes);
+
+	ret = i2c_master_send(i2c, msg, bytes + 1);
+	if (ret < 0)
+	{
+		printk(KERN_INFO "[zx234290 i2c]write-1 error. reg:%x\n", reg);
+
+		return ret;
+	}
+	if (ret != bytes + 1)
+	{
+		printk(KERN_INFO "[zx234290 i2c]write-2 error. reg:%x\n", reg);
+
+		return -EIO;
+	}
+
+	return 0;
+}
+#endif
+
+static int zx234290_i2c_probe(struct i2c_client *i2c,
+			    const struct i2c_device_id *id)
+{
+	struct zx234290 *zx234290;
+
+	zx234290_i2c = i2c;
+
+	zx234290 = kzalloc(sizeof(struct zx234290), GFP_KERNEL);
+	if (zx234290 == NULL)
+		return -ENOMEM;
+
+	i2c_set_clientdata(i2c, zx234290);
+	zx234290->dev = &i2c->dev;
+	zx234290->control_data = i2c;
+	zx234290->read = zx234290_i2c_read;
+	zx234290->write = zx234290_i2c_write;
+
+	return zx234290_device_init(zx234290);
+}
+
+static int zx234290_i2c_remove(struct i2c_client *i2c)
+{
+	struct zx234290 *zx234290 = i2c_get_clientdata(i2c);
+
+	if(!zx234290)
+		return -EINVAL;
+	zx234290_device_exit(zx234290);
+
+	return 0;
+}
+
+static const struct i2c_device_id zx234290_i2c_id[] = {
+	{"zx234290", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, zx234290_i2c_id);
+
+#if 1
+static struct i2c_driver zx234290_i2c_driver = {
+	.driver = {
+		   .name = "zx234290",
+		   .owner = THIS_MODULE,
+	},
+	.probe = zx234290_i2c_probe,
+	.remove = zx234290_i2c_remove,
+	.id_table = zx234290_i2c_id,
+};
+#endif
+struct wake_lock adc_wake_lock;
+static int __init zx234290_i2c_init(void)
+{
+	int ret;
+	wake_lock_init(&adc_wake_lock, WAKE_LOCK_SUSPEND, "adc_lock");
+	ret = i2c_add_driver(&zx234290_i2c_driver);
+	if (ret != 0)
+		pr_err("Failed to register ZX234290 I2C driver: %d\n", ret);
+
+	return ret;
+}
+/* init early so consumer devices can complete system boot */
+subsys_initcall(zx234290_i2c_init);
+
+static void __exit zx234290_i2c_exit(void)
+{
+	i2c_del_driver(&zx234290_i2c_driver);
+}
+module_exit(zx234290_i2c_exit);
+
+MODULE_AUTHOR("Dongjian");
+MODULE_DESCRIPTION("ZX234290 chip family multi-function driver");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/zx234290-irq.c b/ap/os/linux/linux-3.4.x/drivers/mfd/zx234290-irq.c
new file mode 100644
index 0000000..14980d8
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/zx234290-irq.c
@@ -0,0 +1,356 @@
+/*
+ * zx234290-irq.c  --  Interrupt controller support for ZX234290 PMICs
+ *
+ * Copyright 2016 ZTE Inc.
+ *
+ * Author: yuxiang<yu.xiang5@zte.com.cn>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/bug.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/mfd/zx234290.h>
+#include <mach/pcu.h>
+#include <mach/gpio.h>
+#include <linux/sched.h>
+#include <linux/semaphore.h>
+#include <linux/kthread.h>
+
+#define	INT_LOW_LEVEL 	/* yuxiang */
+#define	ZX234290_INT_DEBUG
+#ifdef ZX234290_INT_DEBUG
+unsigned long int_irq_times = 0;
+unsigned long int_thread_times = 0;
+#endif
+struct semaphore zx234290_sem;
+
+#define ZX234290_WAKELOCK 1
+#ifdef ZX234290_WAKELOCK
+#include <linux/wakelock.h>
+static struct wake_lock zx234290_wake_lock;
+#endif
+
+#ifdef ZX234290_PWR_FAUL_PROCESS
+extern int zx234290_regulator_error_irq_request(struct zx234290 *zx234290);
+#endif
+
+static inline int irq_to_zx234290_irq(struct zx234290 *zx234290,
+							int irq)
+{
+	return irq - zx234290->irq_base;
+}
+
+/*
+ * This is a threaded IRQ handler so can access I2C/SPI.  Since the
+ * IRQ handler explicitly clears the IRQ it handles the IRQ line
+ * will be reasserted and the physical IRQ will be handled again if
+ * another interrupt is asserted while we run - in the normal course
+ * of events this is a rare occurrence so we save I2C/SPI reads. We're
+ * also assuming that it's rare to get lots of interrupts firing
+ * simultaneously so try to minimise I/O.
+ */
+static int zx234290_irq(void *irq_data)
+{
+	struct zx234290 *zx234290 = irq_data;
+	u32 irq_sts;
+	u32 irq_mask;
+	u8 buck_sts=0, buck_mask=0;
+	u8 ldo_sts=0, ldo_mask=0;
+	u8 reg;
+	int i;
+	int irq = zx234290->chip_irq;
+
+	const struct sched_param param = {
+		.sched_priority = 31,
+	};
+	sched_setscheduler(current, SCHED_FIFO, &param);
+
+	while (!kthread_should_stop()) {
+		down(&zx234290_sem);
+	#ifdef ZX234290_WAKELOCK
+    	wake_lock(&zx234290_wake_lock);
+	#endif
+
+#ifdef ZX234290_INT_DEBUG
+	int_thread_times ++;
+#endif
+    //printk(KERN_INFO "zx234290 irq  handler:irq=%d.\n", irq);
+
+	zx234290->read(zx234290, ZX234290_REG_ADDR_INTA, 1, &reg);
+	irq_sts = reg;
+	zx234290->read(zx234290, ZX234290_REG_ADDR_INTB, 1, &reg);
+	irq_sts |= reg << 8;
+
+ //   printk(KERN_INFO "zx234290 irq  handler:irq=%d, INTR_A=0x%02x, INTR_B=0x%02x\n", irq, irq_sts&0xFF, irq_sts>>8);
+
+	zx234290->read(zx234290, ZX234290_REG_ADDR_INTA_MASK, 1, &reg);
+	irq_mask = reg;
+	zx234290->read(zx234290, ZX234290_REG_ADDR_INTB_MASK, 1, &reg);
+	irq_mask |= reg << 8;
+
+	irq_sts &= ~irq_mask;	/* 	*/
+
+#ifdef ZX234290_PWR_FAUL_PROCESS
+//++
+	zx234290->read(zx234290, ZX234290_REG_ADDR_BUCK_FAULT_STATUS, 1, &buck_sts);
+	zx234290->read(zx234290, ZX234290_REG_ADDR_LDO_FAULT_STATUS, 1, &ldo_sts);
+	zx234290->read(zx234290, ZX234290_REG_ADDR_BUCK_INT_MASK, 1, &buck_mask);
+	zx234290->read(zx234290, ZX234290_REG_ADDR_LDO_INT_MASK, 1, &ldo_mask);
+
+	buck_sts &= ~buck_mask;
+	ldo_sts &= ~ldo_mask;
+
+	if(buck_sts )
+		irq_sts |= (1 << ZX234290_INT_BUCK_FAUL);
+	else
+		irq_sts &= ~(1 << ZX234290_INT_BUCK_FAUL);
+
+	if(ldo_sts )
+		irq_sts |= (1 << ZX234290_INT_LDO_FAUL);
+	else
+		irq_sts &= ~(1 << ZX234290_INT_LDO_FAUL);
+
+//	if(buck_sts ||  ldo_sts)
+//		printk(KERN_INFO "zx234290 irq  handler:buck_sts=0x%02x, ldo_sts=0x%02x, irq_sts=0x%x\n", buck_sts, ldo_sts, irq_sts);
+#endif
+
+	if (!irq_sts)
+	{
+
+		#ifdef ZX234290_WAKELOCK
+			wake_unlock(&zx234290_wake_lock);
+		#endif
+
+		#ifdef INT_LOW_LEVEL
+			pcu_clr_irq_pending(irq);
+			enable_irq(irq);//yx
+		#endif
+			//return IRQ_NONE;
+			continue;
+	}
+
+	for (i = 0; i < zx234290->irq_num; i++) {
+		if (!(irq_sts & (1 << i)))
+			continue;
+
+		handle_nested_irq(zx234290->irq_base + i);
+	}
+    /*write bit7 to be 0, clear pmu INT*/
+    //zx234290->read(zx234290, ZX234290_REG_ADDR_INTA, 1, &reg);
+    //reg &= ~(0x1<<7);
+    //zx234290->write(zx234290, ZX234290_REG_ADDR_INTA, 1, &reg);
+    #if 0
+	/* Write the STS register back to clear IRQs we handled */
+	reg = irq_sts & 0xFF;
+	irq_sts >>= 8;
+	if (reg)
+		zx234290->write(zx234290, ZX234290_REG_ADDR_INTA, 1, &reg);
+	reg = irq_sts & 0xFF;
+	irq_sts >>= 8;
+	if (reg)
+		zx234290->write(zx234290, ZX234290_REG_ADDR_INTB, 1, &reg);
+    #endif
+
+	#ifdef ZX234290_WAKELOCK
+		wake_unlock(&zx234290_wake_lock);
+	#endif
+
+
+#ifdef INT_LOW_LEVEL
+		pcu_clr_irq_pending(irq);
+		enable_irq(irq);//yx
+#endif
+	}
+	return 0;
+}
+
+static void zx234290_irq_lock(struct irq_data *data)
+{
+	struct zx234290 *zx234290 = irq_data_get_irq_chip_data(data);
+
+	mutex_lock(&zx234290->irq_lock);
+}
+
+static void zx234290_irq_sync_unlock(struct irq_data *data)
+{
+	struct zx234290 *zx234290 = irq_data_get_irq_chip_data(data);
+	u32 reg_mask;
+	u8 reg, reg2;
+
+	zx234290->read(zx234290, ZX234290_REG_ADDR_INTA_MASK, 1, &reg);
+	reg_mask = reg;
+	zx234290->read(zx234290, ZX234290_REG_ADDR_INTB_MASK, 1, &reg);
+	reg_mask |= reg << 8;
+	/* take ldo6 & ldo8 error as buck error */
+	zx234290->read(zx234290, ZX234290_REG_ADDR_BUCK_INT_MASK, 1, &reg);
+	if (reg)
+		reg_mask |= BIT(ZX234290_INT_BUCK_FAUL);
+	zx234290->read(zx234290, ZX234290_REG_ADDR_LDO_INT_MASK, 1, &reg);
+	if (reg)
+		reg_mask |= BIT(ZX234290_INT_LDO_FAUL);
+
+	if (zx234290->irq_mask != reg_mask) {
+		reg = zx234290->irq_mask & 0xFC;
+		zx234290->write(zx234290, ZX234290_REG_ADDR_INTA_MASK, 1, &reg);
+
+		reg = zx234290->irq_mask >> 8 & 0xFF;
+		zx234290->write(zx234290, ZX234290_REG_ADDR_INTB_MASK, 1, &reg);
+
+		reg = (zx234290->irq_mask & BIT(ZX234290_INT_BUCK_FAUL)) ? 0xFF : 0;
+		zx234290->write(zx234290, ZX234290_REG_ADDR_BUCK_INT_MASK, 1, &reg);
+
+		reg = (zx234290->irq_mask & BIT(ZX234290_INT_LDO_FAUL)) ? 0xFF : 0;
+		zx234290->write(zx234290, ZX234290_REG_ADDR_LDO_INT_MASK, 1, &reg);
+	}
+
+	mutex_unlock(&zx234290->irq_lock);
+}
+
+static void zx234290_irq_enable(struct irq_data *data)
+{
+	struct zx234290 *zx234290 = irq_data_get_irq_chip_data(data);
+
+	zx234290->irq_mask &= ~(1 << irq_to_zx234290_irq(zx234290, data->irq));
+}
+
+static void zx234290_irq_disable(struct irq_data *data)
+{
+	struct zx234290 *zx234290 = irq_data_get_irq_chip_data(data);
+
+	zx234290->irq_mask |= (1 << irq_to_zx234290_irq(zx234290, data->irq));
+}
+
+static struct irq_chip zx234290_irq_chip = {
+	.name = "zx234290",
+	.irq_bus_lock = zx234290_irq_lock,
+	.irq_bus_sync_unlock = zx234290_irq_sync_unlock,
+	.irq_disable = zx234290_irq_disable,
+	.irq_enable = zx234290_irq_enable,
+};
+static irqreturn_t irq_primary_handler(int irq, void *dev_id)
+{
+#ifdef INT_LOW_LEVEL
+    disable_irq_nosync(irq);//yx
+#endif
+	//pcu_int_clear(PCU_EX0_INT); //xzg
+	pcu_clr_irq_pending(irq);
+	up(&zx234290_sem);
+#ifdef ZX234290_INT_DEBUG
+	int_irq_times ++;
+#endif
+
+	//return IRQ_WAKE_THREAD;
+	return IRQ_HANDLED;
+}
+
+int zx234290_irq_init(struct zx234290 *zx234290, int irq,
+			    struct zx234290_board *pdata)
+{
+	int ret, cur_irq;
+/*	int flags = IRQF_ONESHOT; */
+	u8 reg;
+
+	#ifdef ZX234290_WAKELOCK
+    	wake_lock_init(&zx234290_wake_lock, WAKE_LOCK_SUSPEND, "zx234290");
+	#endif
+
+	if (!irq) {
+		dev_warn(zx234290->dev, "No interrupt support, no core IRQ\n");
+		return 0;
+	}
+
+	if (!pdata || !pdata->irq_base) {
+		dev_warn(zx234290->dev, "No interrupt support, no IRQ base\n");
+		return 0;
+	}
+
+	sema_init(&zx234290_sem, 0);
+	/* Clear unattended interrupts */
+	zx234290->read(zx234290, ZX234290_REG_ADDR_INTA, 1, &reg);
+	//zx234290->write(zx234290, ZX234290_REG_ADDR_INTA, 1, &reg);
+	zx234290->read(zx234290, ZX234290_REG_ADDR_INTB, 1, &reg);
+	//zx234290->write(zx234290, ZX234290_REG_ADDR_INTB, 1, &reg);
+
+	/*write bit7 to be 0, clear pmu INT*/
+	zx234290->read(zx234290, ZX234290_REG_ADDR_INTA, 1, &reg);
+	reg &= ~(0x1<<7);
+	zx234290->write(zx234290, ZX234290_REG_ADDR_INTA, 1, &reg);
+
+	/* Mask top level interrupts */
+	zx234290->irq_mask = 0xFFFF;
+
+	mutex_init(&zx234290->irq_lock);
+	zx234290->chip_irq = irq;				/* irq	*/
+	zx234290->irq_base = pdata->irq_base;	/* irq_base  32+49	*/
+
+	zx234290->irq_num = ZX234290_NUM_IRQ;
+
+	/* Register with genirq */
+	for (cur_irq = zx234290->irq_base;
+	     cur_irq < zx234290->irq_num + zx234290->irq_base;
+	     cur_irq++) {
+		irq_set_chip_data(cur_irq, zx234290);
+		irq_set_chip_and_handler(cur_irq, &zx234290_irq_chip,
+					 handle_edge_irq);
+		irq_set_nested_thread(cur_irq, 1);
+		/* ARM needs us to explicitly flag the IRQ as valid
+		 * and will set them noprobe when we do so. */
+#ifdef CONFIG_ARM
+		set_irq_flags(cur_irq, IRQF_VALID);
+#else
+		irq_set_noprobe(cur_irq);
+#endif
+	}
+
+#ifdef ZX234290_PWR_FAUL_PROCESS
+	zx234290_regulator_error_irq_request(zx234290);
+#endif
+    //unsigned int gpio_num = irq_to_gpio(irq);//by yuxiang
+	//#define GPIO_PMU_INT ZX29_GPIO_50
+	/* GPIO reuse*/
+	zx29_gpio_config(pdata->irq_gpio_num, pdata->irq_gpio_func);
+#ifdef INT_LOW_LEVEL
+	zx29_gpio_set_inttype(pdata->irq_gpio_num, IRQ_TYPE_LEVEL_LOW);
+#else
+	zx29_gpio_set_inttype(pdata->irq_gpio_num, IRQ_TYPE_EDGE_FALLING);
+#endif
+	zx29_gpio_pd_pu_set(pdata->irq_gpio_num, 0);
+	//pcu_int_clear(PCU_EX0_INT); //by yuxiang
+	pcu_clr_irq_pending(irq);
+	//irq_set_irq_type(irq, IRQ_TYPE_EDGE_FALLING);//by yuxiang
+	//ret = request_threaded_irq(irq, irq_primary_handler, zx234290_irq, IRQF_ONESHOT,
+	//			   "zx234290", zx234290);
+	//µÈµ½pmuËùÓÐÄÚ²¿ÖжÏ×¢²áºó£¬ÔÙʹÄÜpmuÖжÏ
+	/* irq_set_status_flags(irq, IRQ_NOAUTOEN); */
+	ret = request_irq(irq, irq_primary_handler, IRQF_NO_THREAD,
+				   "zx234290", zx234290);
+	if (ret != 0)
+		dev_err(zx234290->dev, "Failed to request IRQ: %d\n", ret);
+	#ifndef CONFIG_ARCH_ZX297520V3_CAP	
+	irq_set_irq_wake(irq, 1);
+	#endif
+	kthread_run(zx234290_irq, zx234290, "irq/%d-%s", irq, "zx234290");
+	return ret;
+}
+
+int zx234290_irq_exit(struct zx234290 *zx234290)
+{
+	free_irq(zx234290->chip_irq, zx234290);
+
+	#ifdef ZX234290_WAKELOCK
+		wake_lock_destroy(&zx234290_wake_lock);
+	#endif
+
+	return 0;
+}
diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/zx234290-regulator-wrapper.c b/ap/os/linux/linux-3.4.x/drivers/mfd/zx234290-regulator-wrapper.c
new file mode 100644
index 0000000..b20a3f1
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/zx234290-regulator-wrapper.c
@@ -0,0 +1,1307 @@
+/*

+ * zx234290-regulator-wrapper.c  --  regulator for ZX234290 PMICs

+ *

+ * Copyright 2016 ZTE Inc.

+ *

+ * Author: yuxiang<yu.xiang5@zte.com.cn>

+ *

+ *  This program is free software; you can redistribute  it and/or modify it

+ *  under  the terms of  the GNU General  Public License as published by the

+ *  Free Software Foundation;  either version 2 of the  License, or (at your

+ *  option) any later version.

+ *

+ */

+#include <linux/init.h>        /* For init/exit macros */

+#include <linux/module.h>      /* For MODULE_ marcros  */

+#include <linux/interrupt.h>

+#include <linux/spinlock.h>

+#include <linux/platform_device.h>

+

+#include <linux/device.h>

+#include <linux/delay.h>

+#include <linux/mutex.h>

+#include <linux/mfd/zx234290.h>

+

+static int zDrvZx234290_DischargerConfig(void)

+{

+    int nRet = 0;

+    int index = 0 ;

+

+    /*enable all discharger*/

+    for(index = DISCHARGER_LDO_9; index < DISCHARGER_MAX; index++)

+    {

+        if(index == DISCHARGER_BUCK_X || index == DISCHARGER_LDO_X)

+            continue;

+        nRet = zx234290_SetDischarger(index, DISCHARGER_ENABLE);

+    	if (0 != nRet)

+    	{

+    		return -EIO;

+    	}

+    }

+    /*config, buck0 discharger disable*/

+    /*

+    nRet = Zx234290_SetDischarger(DISCHARGER_BUCK_0, DISCHARGER_DISBALE);

+    if (DRV_SUCCESS != nRet)

+    {

+    	return DRV_ERROR;

+    }*/

+

+    return 0;

+}

+#if 1

+extern int pmu_chip_ver;

+

+int zDrvZx234290_NormalSleepConfig(void)

+{

+	int nRet = 0;

+

+	/* if chg_pwron, this bucks will be closed, we open them again when system reset... */

+    //zx234290_set_buck3_onoff(LDO_ENABLE_ON);        /*RF 1V6*/

+    zx234290_set_buck4_onoff(LDO_ENABLE_ON);        /*PA 3V6*/

+    //zx234290_set_ldo8_onoff(LDO_ENABLE_ON);         /*ZSP JTAG/SD 2V85*/

+	//zx234290_set_ldo6_onoff(LDO_ENABLE_ON);         /*SIM1 1V8ÕâÀï²»Óÿª£¬SIM¿¨×Ô¼º¿ª£¬·ñÔòÓ°ÏìʱÐò*/

+	if (pmu_chip_ver != 297)

+	zx234290_set_ldo10_onoff(LDO_ENABLE_ON);        /*adc0.9v/dac0.9v/avdd_pll0.9v/avdd_abb0.9v*/

+    zx234290_set_ldo1_onoff(LDO_ENABLE_ON);         /*USB 0V9*/

+    zx234290_set_ldo5_onoff(LDO_ENABLE_ON);         /*USB 3V3*/

+	

+	//nRet = Zx234290_SetBUCK1SleepMode(BUCK1_SLP_AUTO_ECO_SLP);

+	nRet = zx234290_set_buck1_sleep_mode(BUCK1_SLP_SHUTDOWN);

+	if (0 != nRet)

+	{

+		return -EIO;

+	}

+

+    nRet = zx234290_set_buck2_sleep_mode(BUCK234_SLP_ECO_WITH_ECO);

+	if (0 != nRet)

+	{

+		return -EIO;

+	}

+	//nRet = Zx234290_SetBUCK3SleepMode(BUCK234_SLP_ECO_WITH_ECO);

+	nRet = zx234290_set_buck3_sleep_mode(BUCK234_SLP_SHUTDOWN);

+	if (0 != nRet)

+	{

+		return -EIO;

+	}

+

+	nRet = zx234290_set_buck4_sleep_mode(BUCK234_SLP_SHUTDOWN);

+	if (0 != nRet)

+	{

+		return -EIO;

+	}

+	nRet = zx234290_set_ldo2_sleep_mode(LDOA_SLP_ECO_VOLT);

+	if (0 != nRet)

+	{

+		return -EIO;

+	}

+	nRet = zx234290_set_ldo3_sleep_mode(LDOA_SLP_ECO_VOLT);

+	if (0 != nRet)

+	{

+		return -EIO;

+	}

+

+	nRet = zx234290_set_ldo4_sleep_mode(LDOB_SLP_SHUTDOWN);

+	if (0 != nRet)

+	{

+		return -EIO;

+	}

+

+	nRet = zx234290_set_ldo6_sleep_mode(LDOB_SLP_ECO_VOLT);

+	if (0 != nRet)

+	{

+		return -EIO;

+	}

+

+	nRet = zx234290_set_ldo7_sleep_mode(LDOA_SLP_ECO_VOLT);

+	if (0 != nRet)

+	{

+		return -EIO;

+	}

+

+	nRet = zx234290_set_ldo8_sleep_mode(LDOA_SLP_ECO_VOLT);//×¢ÒâÐͺŻú²»ÄܹØ

+	if (0 != nRet)

+	{

+		return -EIO;

+	}

+

+	nRet = zx234290_set_ldo9_sleep_mode(LDOA_SLP_ECO_VOLT);

+	if (0 != nRet)

+	{

+		return -EIO;

+	}

+

+    nRet = zx234290_set_ldo10_sleep_mode(LDOA_SLP_ECO_VOLT);

+	if (0 != nRet)

+	{

+		return -EIO;

+	}

+

+	return 0;

+}

+/* for chaging power on */

+int zDrvZx234290_ChgOnSleepConfig(void)

+{

+    //zx234290_set_buck3_onoff(LDO_ENABLE_OFF);        /*RF 1V6*/

+    zx234290_set_buck4_onoff(LDO_ENABLE_OFF);        /*PA 3V6*/

+    //zx234290_set_ldo1_onoff(LDO_ENABLE_OFF);         /*USB 0V9*/

+    //zx234290_set_ldo5_onoff(LDO_ENABLE_OFF);         /*USB 3V3*/

+    zx234290_set_ldo6_onoff(LDO_ENABLE_OFF);         /*SIM1 1V8*/

+    //zx234290_set_ldo7_onoff(LDO_ENABLE_OFF);       /*RF 2V85*/

+    //zx234290_set_ldo8_onoff(LDO_ENABLE_ON);         /*ZSP JTAG/SD 2V85 ÐͺŻú²»ÄܹØ*/

+    //zx234290_set_ldo9_onoff(LDO_ENABLE_OFF);         /*PLL 0V9*/

+    zx234290_set_ldo10_onoff(LDO_ENABLE_OFF);        /*ABB DIG*/

+

+	zx234290_set_buck1_sleep_mode(BUCK1_SLP_SHUTDOWN);  /*vdcore*/

+    zx234290_set_buck2_sleep_mode(BUCK234_SLP_ECO_WITH_ECO);/*ddr1.2v/mcp1.2v/hsic1.2v*/

+    zx234290_set_buck3_sleep_mode(BUCK234_SLP_SHUTDOWN);   /*RF 1V6*/

+    //zx234290_set_buck4_sleep_mode(BUCK234_SLP_SHUTDOWN);   /*PA 3V6*/

+    //zx234290_set_ldo1_sleep_mode(LDOA_SLP_SHUTDOWN);       /*USB 0V9*/

+    //zx234290_set_ldo5_sleep_mode(LDOA_SLP_SHUTDOWN);       /*USB 3V3*/

+	zx234290_set_ldo2_sleep_mode(LDOA_SLP_ECO_VOLT_SLP);    /*io1.8v/mcp1.8v/sd01.8v*/

+	zx234290_set_ldo3_sleep_mode(LDOA_SLP_ECO_VOLT_SLP);	/*vp111.8v/vddr_pll1.8v/*/

+	zx234290_set_ldo4_sleep_mode(LDOA_SLP_SHUTDOWN);		/*vcxo*/

+    //zx234290_set_ldo6_sleep_mode(LDOA_SLP_SHUTDOWN);       /*SIM1 1V8*/

+    zx234290_set_ldo7_sleep_mode(LDOA_SLP_ECO_VOLT_SLP);  /*RF 2V85 -- 26M */

+    zx234290_set_ldo8_sleep_mode(LDOA_SLP_ECO_VOLT_SLP);       /*ZSP JTAG/SD 2V85/LCD*/

+    zx234290_set_ldo9_sleep_mode(LDOA_SLP_ECO_VOLT_SLP);       /*PLL 0V9*/

+    //zx234290_set_ldo10_sleep_mode(LDOA_SLP_SHUTDOWN);      /*ABB DIG*/

+

+	return 0;

+}

+

+

+#endif

+#ifdef CONFIG_PMU_GM

+extern int zx234296h_support_parameter(void);

+#endif

+int zDrvZx234290_LdoInit(void)

+{

+    int ret = 0;

+	

+#ifdef CONFIG_PMU_GM

+	zx234296h_support_parameter();

+#endif

+	//zDrvZx234290_Config();

+	ret = zDrvZx234290_LdoRstErr();

+    

+	ret += zx234290_set_buck1_active_mode(BUCK_NRM_FORCE_PWM);

+	

+	ret += zx234290_set_buck2_active_mode(BUCK_NRM_FORCE_PWM);

+	

+	ret += zx234290_set_buck3_active_mode(BUCK_NRM_FORCE_PWM);

+	

+	ret += zx234290_set_buck4_active_mode(BUCK_NRM_FORCE_PWM);

+

+    

+	ret += zDrvZx234290_DischargerConfig();

+

+	//zDrvZx234290_SleepConfig();

+

+    ret += zx234290_SetLlpEnable(LLP_ENABLE);

+    ret += zx234290_SetTllpToDo(LLP_SHUTDOWN);

+   // ret += zx234290_SetRestartDly(LLP_DLY_500MS);  //LLP_DLY_250MS 

+    if(ret != 0)

+	{

+		printk("zx234290 zDrvZx234290_LdoInit err\n");

+		return -1;

+	}

+

+	return 0;

+}

+#if 1	/* vdd_CORE0 -- 0.9V -- BUCK1 */

+/*******************************************************************************

+ * Function:

+ * Description:

+ * Parameters:

+ *	 Input:

+ *

+ *	 Output:

+ *

+ * Returns:

+ *

+ *

+ * Others:

+ ********************************************************************************/

+int zx234290_setcore_onoff(T_ZDrvPmic_Enable status)

+{

+	int ret = 0;

+

+	ret = zx234290_set_buck1_onoff((T_ZDrvZx234290_LDO_ENABLE)status);

+

+	return ret;

+}

+

+T_ZDrvPmic_Enable zx234290_getcore_onoff(void)

+{

+	int status;

+

+	status	= zx234290_get_buck1_onoff();

+

+	return (T_ZDrvPmic_Enable)status;

+}

+int zx234290_setcore_voltage(int vol)

+{

+	int ret = 0;

+	switch (vol)

+	{

+		case 900:

+		{

+			ret = zx234290_set_buck1_voltage(VBUCKA_0_900);

+			break;

+		}

+

+		case 1100:

+		{

+			ret = zx234290_set_buck1_voltage(VBUCKA_1_100);

+			break;

+		}

+		default:

+		{

+			ret = -EINVAL;

+		}

+	}

+

+	return ret;

+}

+

+int zx234290_getcore_voltage(void)

+{

+	int	vget;

+	T_ZDrvZx234290_VbuckA vol;

+

+	vol = zx234290_get_buck1_voltage();

+	switch (vol)

+	{

+		case VBUCKA_0_900:

+		{

+			vget = 900;

+			break;

+		}

+

+		case VBUCKA_1_100:

+		{

+			vget = 1100;

+			break;

+		}

+

+		default:

+		{

+			return -EINVAL;

+		}

+	}

+

+	return vget;

+}

+

+int zx234290_setcore_sleepmode(T_ZDrvPmic_SlpMode mode)

+{

+	int ret = 0;

+

+    switch (mode)

+	{

+		case PM_SLPMODE_AUTO_NORMAL:

+		case PM_SLPMODE_ECO_NRMV:

+		case PM_SLPMODE_ECO_SLPV:

+        case PM_SLPMODE_OFF:

+		{

+			ret = zx234290_set_buck1_sleep_mode((T_ZDrvZx234290_BUCK1_SLPMODE)mode);

+			break;

+		}

+		default:

+		{

+			ret = -EINVAL;

+		}

+	}

+

+	return ret;

+}

+

+T_ZDrvPmic_SlpMode zx234290_getcore_sleepmode(void)

+{

+	T_ZDrvPmic_SlpMode	getMode;

+	T_ZDrvZx234290_BUCK1_SLPMODE	mode;

+

+	mode = zx234290_get_buck1_sleep_mode();

+	switch (mode)

+	{

+		case BUCK1_SLP_AUTO_WITHOUT_ECO:

+		{

+			getMode = PM_SLPMODE_AUTO_NORMAL;

+			break;			

+		}

+		case BUCK1_SLP_AUTO_ECO:	

+		{

+			getMode = PM_SLPMODE_ECO_NRMV;

+			break;			

+		}

+		case BUCK1_SLP_AUTO_ECO_SLP:

+		{

+			getMode = PM_SLPMODE_ECO_SLPV;

+			break;			

+		}

+

+		case BUCK1_SLP_SHUTDOWN:

+		{

+			getMode = PM_SLPMODE_OFF;

+			break;

+		}

+		default:

+		{

+			return PM_SLPMODE_NOT_SUPPORT;

+		}

+	}

+

+	return getMode;

+}

+

+int zx234290_setcore_sleepvoltage(int vol) 	 /* buck1 sleep vol */

+{

+	int ret = 0;

+	switch (vol)

+	{

+        case 800:

+		{

+			ret = zx234290_set_buck1_sleep_voltage(VBUCKA_0_800);

+			break;

+		}

+		case 900:

+		{

+			ret = zx234290_set_buck1_sleep_voltage(VBUCKA_0_900);

+			break;

+		}

+

+		case 1100:

+		{

+			ret = zx234290_set_buck1_sleep_voltage(VBUCKA_1_100);

+			break;

+		}

+		default:

+		{

+			ret = -EINVAL;

+		}

+	}

+

+	return ret;

+}

+#endif

+

+

+#if 1	/*	USB 0.9V-- LDO1 */

+/*******************************************************************************

+ * Function:

+ * Description:

+ * Parameters:

+ *	 Input:

+ *

+ *	 Output:

+ *

+ * Returns:

+ *

+ *

+ * Others:

+ ********************************************************************************/

+int zx234290_setusb_v0v9_onoff(T_ZDrvPmic_Enable status)

+{

+	int ret = 0;

+

+	ret = zx234290_set_ldo1_onoff((T_ZDrvZx234290_LDO_ENABLE)status);

+

+	return ret;

+}

+

+int zx234290_setusb_v0v9_onoff_PSM(T_ZDrvPmic_Enable status)

+{

+	int ret = 0;

+

+	ret = zx234290_set_ldo1_onoff_PSM((T_ZDrvZx234290_LDO_ENABLE)status);

+

+	return ret;

+}

+

+T_ZDrvPmic_Enable zx234290_getusb_v0v9_onoff(void)

+{

+	int status;

+

+	status	= zx234290_get_ldo1_onoff();

+

+	return (T_ZDrvPmic_Enable)status;

+}

+

+int zx234290_setusb_v0v9_voltage(int vol)

+{

+	int ret = 0;

+	switch (vol)

+	{

+		case 900:

+		{

+			ret = zx234290_set_ldo1_voltage(VLDOA_0_900);

+			break;

+		}

+		default:

+		{

+			ret = -EINVAL;

+		}

+	}

+	return ret;

+}

+

+int zx234290_getusb_v0v9_voltage(void)

+{

+	int  vget;

+	T_ZDrvZx234290_VldoA   vol;

+

+	vol = zx234290_get_ldo1_voltage();

+	switch (vol)

+	{

+		case VLDOA_0_900:

+		{

+			vget = 900;

+			break;

+		}

+		default:

+		{

+			return -EINVAL;

+		}

+	}

+

+	return vget;

+}

+

+int zx234290_setusb_v0v9_sleepmode(T_ZDrvPmic_SlpMode mode)

+{

+	int ret = 0;

+

+    switch (mode)

+	{

+		case PM_SLPMODE_AUTO_NORMAL:

+		case PM_SLPMODE_ECO_NRMV:

+		case PM_SLPMODE_ECO_SLPV:

+        case PM_SLPMODE_OFF:

+		{

+			ret = zx234290_set_ldo1_sleep_mode((T_ZDrvZx234290_LDOA_SLPMODE)mode);

+			break;

+		}

+		default:

+		{

+			ret = -EINVAL;

+		}

+	}

+	return ret;

+

+}

+

+T_ZDrvPmic_SlpMode zx234290_getusb_v0v9_sleepmode(void)

+{

+	T_ZDrvPmic_SlpMode	getMode;

+	T_ZDrvZx234290_LDOA_SLPMODE	 mode;

+

+	mode = zx234290_get_ldo1_sleep_mode();

+	switch (mode)

+	{

+		case LDOA_SLP_NRM_MODE:

+		{

+			getMode = PM_SLPMODE_AUTO_NORMAL;

+			break;

+		}

+		case LDOA_SLP_ECO_VOLT:

+		{

+			getMode = PM_SLPMODE_ECO_NRMV;

+			break;

+		}

+        case LDOA_SLP_ECO_VOLT_SLP:

+		{

+			getMode = PM_SLPMODE_ECO_SLPV;

+			break;

+		}

+		case LDOA_SLP_SHUTDOWN:

+		{

+			getMode = PM_SLPMODE_OFF;

+			break;

+		}

+		default:

+		{

+			return PM_SLPMODE_NOT_SUPPORT;

+		}

+	}

+	return getMode;

+}

+

+#endif

+

+#if 1	/*	USB 3.3V-- LDO1 */

+/*******************************************************************************

+ * Function:

+ * Description:

+ * Parameters:

+ *	 Input:

+ *

+ *	 Output:

+ *

+ * Returns:

+ *

+ *

+ * Others:

+ ********************************************************************************/

+int zx234290_setusb_v3v3_onoff(T_ZDrvPmic_Enable status)

+{

+	int ret = 0;

+

+	ret = zx234290_set_ldo5_onoff((T_ZDrvZx234290_LDO_ENABLE)status);

+

+	return ret;

+}

+

+int zx234290_setusb_v3v3_onoff_PSM(T_ZDrvPmic_Enable status)

+{

+	int ret = 0;

+

+	ret = zx234290_set_ldo5_onoff_PSM((T_ZDrvZx234290_LDO_ENABLE)status);

+

+	return ret;

+}

+

+

+T_ZDrvPmic_Enable zx234290_getusb_v3v3_onoff(void)

+{

+	int status;

+

+	status	= zx234290_get_ldo5_onoff();

+

+	return (T_ZDrvPmic_Enable)status;

+}

+

+int zx234290_setusb_v3v3_voltage(int vol)

+{

+	int ret = 0;

+	switch (vol)

+	{

+		case 3300:

+		{

+			ret = zx234290_set_ldo5_voltage(VLDOC_3_300);

+			break;

+		}

+		default:

+		{

+			ret = -EINVAL;

+		}

+	}

+	return ret;

+}

+

+int zx234290_getusb_v3v3_voltage(void)

+{

+	int vget;

+	T_ZDrvZx234290_VldoB	vol;

+

+	vol = zx234290_get_ldo5_voltage();

+	switch (vol)

+	{

+		case VLDOC_3_300:

+		{

+			vget = 3300;

+			break;

+		}

+		default:

+		{

+			return -EINVAL;

+		}

+	}

+

+	return vget;

+}

+

+int zx234290_setusb_v3v3_sleepmode(T_ZDrvPmic_SlpMode mode)

+{

+	int ret = 0;

+

+    switch (mode)

+	{

+		case PM_SLPMODE_AUTO_NORMAL:

+		case PM_SLPMODE_ECO_NRMV:

+		case PM_SLPMODE_ECO_SLPV:

+        case PM_SLPMODE_OFF:

+		{

+			ret = zx234290_set_ldo5_sleep_mode((T_ZDrvZx234290_LDOB_SLPMODE)mode);

+			break;

+		}

+		default:

+		{

+			ret = -EINVAL;

+		}

+	}

+	return ret;

+

+}

+

+T_ZDrvPmic_SlpMode zx234290_getusb_v3v3_sleepmode(void)

+{

+	T_ZDrvPmic_SlpMode	getMode;

+	T_ZDrvZx234290_LDOB_SLPMODE	 mode;

+

+	mode = zx234290_get_ldo5_sleep_mode();

+	switch (mode)

+	{

+		case LDOA_SLP_NRM_MODE:

+		{

+			getMode = PM_SLPMODE_AUTO_NORMAL;

+			break;

+		}

+		case LDOA_SLP_ECO_VOLT:

+		{

+			getMode = PM_SLPMODE_ECO_NRMV;

+			break;

+		}

+        case LDOA_SLP_ECO_VOLT_SLP:

+		{

+			getMode = PM_SLPMODE_ECO_SLPV;

+			break;

+		}

+		case LDOA_SLP_SHUTDOWN:

+		{

+			getMode = PM_SLPMODE_OFF;

+			break;

+		}

+		default:

+		{

+			return PM_SLPMODE_NOT_SUPPORT;

+		}

+	}

+	return getMode;

+}

+#endif

+

+#if 1	/* SIM1 1.8V/3.0V -- LDO6 */

+/*******************************************************************************

+ * Function:

+ * Description:

+ * Parameters:

+ *	 Input:

+ *

+ *	 Output:

+ *

+ * Returns:

+ *

+ *

+ * Others:

+ ********************************************************************************/

+int zx234290_setsim1_onoff(T_ZDrvPmic_Enable status)

+{

+	int ret = 0;

+

+	ret = zx234290_set_ldo6_onoff((T_ZDrvZx234290_LDO_ENABLE)status);

+

+	return ret;

+}

+

+

+T_ZDrvPmic_Enable zx234290_getsim1_onoff(void)

+{

+	int status;

+

+	status	= zx234290_get_ldo6_onoff();

+

+	return (T_ZDrvPmic_Enable)status;

+}

+int zx234290_setsim1_voltage(int vol)

+{

+	int ret = 0;

+	switch (vol)

+	{

+		case 1800:

+		{

+			ret = zx234290_set_ldo6_voltage(VLDOD_1_800);

+			break;

+		}

+

+		case 3000:

+		{

+			ret = zx234290_set_ldo6_voltage(VLDOD_3_000);

+			break;

+		}

+

+		default:

+		{

+			ret = -EINVAL;

+		}

+	}

+

+	return ret;

+}

+int zx234290_getsim1_voltage(void)

+{

+	int vget;

+	T_ZDrvZx234290_VldoD vol;

+

+	vol = zx234290_get_ldo6_voltage();

+	switch (vol)

+	{

+		case VLDOD_1_800:

+		{

+			vget = 1800;

+			break;

+		}

+

+		case VLDOD_3_000:

+		{

+			vget = 3000;

+			break;

+		}

+

+		default:

+		{

+			return -EINVAL;

+		}

+	}

+

+	return vget;

+}

+

+int zx234290_setsim1_sleepmode(T_ZDrvPmic_SlpMode mode)

+{

+	int ret = 0;

+

+    switch (mode)

+	{

+		case PM_SLPMODE_AUTO_NORMAL:

+		case PM_SLPMODE_ECO_NRMV:

+        case PM_SLPMODE_OFF:

+		{

+			ret = zx234290_set_ldo6_sleep_mode((T_ZDrvZx234290_LDOB_SLPMODE)mode);

+			break;

+		}

+		default:

+		{

+			ret = -EINVAL;

+		}

+	}

+

+	return ret;

+}

+

+T_ZDrvPmic_SlpMode zx234290_getsim1_sleepmode(void)

+{

+	T_ZDrvPmic_SlpMode	getMode;

+	T_ZDrvZx234290_LDOB_SLPMODE	 mode;

+

+	mode = zx234290_get_ldo6_sleep_mode();

+	switch (mode)

+	{

+		case LDOB_SLP_NRM_MODE:

+		{

+			getMode = PM_SLPMODE_AUTO_NORMAL;

+			break;			

+		}

+		case LDOB_SLP_ECO_VOLT:

+		{

+			getMode = PM_SLPMODE_ECO_NRMV;

+			break;			

+		}

+		case LDOB_SLP_SHUTDOWN:

+		{

+			getMode = PM_SLPMODE_OFF;

+			break;			

+		}

+		default:

+		{

+			return PM_SLPMODE_NOT_SUPPORT;

+		}

+	}

+

+	return getMode;

+}

+#endif

+

+#if 1	/* SIM2 1.8V/3.0V -- LDO6 */

+/*******************************************************************************

+ * Function:

+ * Description:

+ * Parameters:

+ *	 Input:

+ *

+ *	 Output:

+ *

+ * Returns:

+ *

+ *

+ * Others:

+ ********************************************************************************/

+int zx234290_setsim2_onoff(T_ZDrvPmic_Enable status)

+{

+	int ret = 0;

+

+	ret = zx234290_set_ldo10_onoff((T_ZDrvZx234290_LDO_ENABLE)status);

+

+	return ret;

+}

+

+

+T_ZDrvPmic_Enable zx234290_getsim2_onoff(void)

+{

+	T_ZDrvZx234290_LDO_ENABLE status;

+

+	status	= zx234290_get_ldo10_onoff();

+

+	return (T_ZDrvPmic_Enable)status;

+}

+

+int zx234290_setsim2_voltage(int vol)

+{

+	int ret = 0;

+	switch (vol)

+	{

+		case 1800:

+		{

+			ret = zx234297_set_ldo10_voltageF(VLDOF_1_800);

+			break;

+		}

+

+		case 3000:

+		{

+			ret = zx234297_set_ldo10_voltageF(VLDOF_3_000);

+			break;

+		}

+

+		default:

+		{

+			ret = -EINVAL;

+		}

+	}

+

+	return ret;

+}

+int zx234290_getsim2_voltage(void)

+{

+	int vget;

+	T_ZDrvZx234297_VldoF vol;

+

+	vol = zx234290_get_ldo10_voltageF();

+	switch (vol)

+	{

+		case VLDOF_1_800:

+		{

+			vget = 1800;

+			break;

+		}

+

+		case VLDOF_3_000:

+		{

+			vget = 3000;

+			break;

+		}

+

+		default:

+		{

+			return -EINVAL;

+		}

+	}

+

+	return vget;

+}

+

+int zx234290_setsim2_sleepmode(T_ZDrvPmic_SlpMode mode)

+{

+	int ret = 0;

+

+    switch (mode)

+	{

+		case PM_SLPMODE_AUTO_NORMAL:

+		case PM_SLPMODE_ECO_NRMV:

+        case PM_SLPMODE_OFF:

+		{

+			ret = zx234290_set_ldo10_sleep_mode((T_ZDrvZx234290_LDOA_SLPMODE)mode);

+			break;

+		}

+		default:

+		{

+			ret = -EINVAL;

+		}

+	}

+

+	return ret;

+}

+

+T_ZDrvPmic_SlpMode zx234290_getsim2_sleepmode(void)

+{

+	T_ZDrvPmic_SlpMode	getMode;

+	T_ZDrvZx234290_LDOA_SLPMODE	 mode;

+

+	mode = zx234290_get_ldo10_sleep_mode();

+	switch (mode)

+	{

+		case LDOA_SLP_NRM_MODE:

+		{

+			getMode = PM_SLPMODE_AUTO_NORMAL;

+			break;			

+		}

+		case LDOA_SLP_ECO_VOLT:

+		{

+			getMode = PM_SLPMODE_ECO_NRMV;

+			break;			

+		}

+		case LDOA_SLP_ECO_VOLT_SLP:

+		{

+			getMode = PM_SLPMODE_ECO_SLPV;

+			break;			

+		}	

+		case LDOA_SLP_SHUTDOWN:

+		{

+			getMode = PM_SLPMODE_OFF;

+			break;			

+		}

+		default:

+		{

+			return PM_SLPMODE_NOT_SUPPORT;

+		}

+	}

+

+	return getMode;

+}

+#endif

+

+

+#if 1	/* SD1- 1.8V/2.8V --LDO8 */

+/*******************************************************************************

+ * Function:

+ * Description:

+ * Parameters:

+ *	 Input:

+ *

+ *	 Output:

+ *

+ * Returns:

+ *

+ *

+ * Others:

+ ********************************************************************************/

+int zx234290_setsd1_onoff(T_ZDrvPmic_Enable status)

+{

+	int ret = 0;

+

+	ret = zx234290_set_ldo8_onoff((T_ZDrvZx234290_LDO_ENABLE)status);

+

+	return ret;

+}

+

+T_ZDrvPmic_Enable zx234290_getsd1_onoff(void)

+{

+	int status;

+

+	status = zx234290_get_ldo8_onoff();

+

+	return (T_ZDrvPmic_Enable)status;

+}

+int zx234290_setsd1_voltage(int vol)

+{

+	int ret = 0;

+

+    switch (vol)

+	{

+        case 1800:

+		{

+			ret = zx234290_set_ldo8_voltage(VLDOD_1_800);

+			break;

+		}

+		case 2800:

+		{

+			ret = zx234290_set_ldo8_voltage(VLDOD_2_800);

+			break;

+		}

+		default:

+		{

+			ret = -EINVAL;

+		}

+	}

+

+	return ret;

+}

+

+int zx234290_getsd1_voltage(void)

+{

+	int vget;

+	T_ZDrvZx234290_VldoD vol;

+

+	vol = zx234290_get_ldo8_voltage();

+

+	switch (vol)

+	{

+        case VLDOD_1_800:

+		{

+			vget = 1800;

+			break;

+		}

+

+		case VLDOD_2_800:

+		{

+			vget = 2800;

+			break;

+		}

+

+		default:

+		{

+			return -EINVAL;

+		}

+	}

+

+	return vget;

+}

+

+int zx234290_setsd1_sleepmode(T_ZDrvPmic_SlpMode mode)

+{

+	int ret = 0;

+

+    switch (mode)

+	{

+		case PM_SLPMODE_AUTO_NORMAL:

+		case PM_SLPMODE_ECO_NRMV:

+        case PM_SLPMODE_ECO_SLPV:

+        case PM_SLPMODE_OFF:

+		{

+			ret = zx234290_set_ldo8_sleep_mode((T_ZDrvZx234290_LDOA_SLPMODE)mode);

+			break;

+		}

+		default:

+		{

+			ret = -EINVAL;

+		}

+	}

+

+	return ret;

+}

+

+T_ZDrvPmic_SlpMode zx234290_getsd1_sleepmode(void)

+{

+	T_ZDrvPmic_SlpMode	getMode;

+	T_ZDrvZx234290_LDOA_SLPMODE	 mode;

+

+    mode = zx234290_get_ldo8_sleep_mode();

+

+	switch (mode)

+	{

+		case LDOA_SLP_NRM_MODE:

+		{		

+			getMode = PM_SLPMODE_AUTO_NORMAL;

+			break;

+		}

+		case LDOA_SLP_ECO_VOLT:	

+		{		

+			getMode = PM_SLPMODE_ECO_NRMV;

+			break;

+		}

+        case LDOA_SLP_ECO_VOLT_SLP:	

+		{		

+			getMode = PM_SLPMODE_ECO_SLPV;

+			break;

+		}

+		case LDOA_SLP_SHUTDOWN:

+		{

+			getMode = PM_SLPMODE_OFF;

+			break;

+		}

+		default:

+		{

+			return PM_SLPMODE_NOT_SUPPORT;

+		}

+	}

+

+	return getMode;

+}

+

+#endif

+

+#if 1

+int zDrvPmic_SetNormal_Onoff(T_ZDrvPmic_Regulator regulator, T_ZDrvPmic_Enable enable)
+{
+    int ret = 0;
+
+    switch (regulator)
+    {
+        case VSD1:

+            ret = zx234290_setsd1_onoff(enable);
+            break;
+
+        case VSIM1:

+            ret = zx234290_setsim1_onoff(enable);
+            break;
+			
+        case VSIM2:

+            ret = zx234290_setsim2_onoff(enable);

+            break;

+			

+        case VUSB_0V9:
+            ret = zx234290_setusb_v0v9_onoff(enable);
+            break;
+
+        case VUSB_3V3:
+            ret = zx234290_setusb_v3v3_onoff(enable);
+            break;
+
+		default:
+			return -EINVAL;
+    }
+
+    return ret;
+}
+EXPORT_SYMBOL(zDrvPmic_SetNormal_Onoff);

+

+

+int zDrvPmic_SetNormal_Onoff_PSM(T_ZDrvPmic_Regulator regulator, T_ZDrvPmic_Enable enable)

+{

+    int ret = 0;

+

+    switch (regulator)

+    {

+        case VSD1:

+            ret = zx234290_setsd1_onoff(enable);

+            break;

+

+        case VSIM1:

+            ret = zx234290_setsim1_onoff(enable);

+            break;

+

+        case VUSB_0V9:

+            ret = zx234290_setusb_v0v9_onoff_PSM(enable);

+            break;

+

+        case VUSB_3V3:

+            ret = zx234290_setusb_v3v3_onoff_PSM(enable);

+            break;

+

+		default:

+			return -EINVAL;

+    }

+

+    return ret;

+}

+EXPORT_SYMBOL(zDrvPmic_SetNormal_Onoff_PSM);
+
+
+
+int zDrvPmic_SetNormal_Voltage(T_ZDrvPmic_Regulator regulator, int voltage)
+{
+    int ret = 0;
+
+    switch (regulator)
+    {
+        case VSD1:

+            ret = zx234290_setsd1_voltage(voltage);
+            break;
+
+        case VSIM1:

+            ret = zx234290_setsim1_voltage(voltage);
+            break;
+        case VSIM2:

+            ret = zx234290_setsim2_voltage(voltage);

+            break;
+        case VUSB_0V9:
+            ret = zx234290_setusb_v0v9_voltage(voltage);
+            break;
+
+        case VUSB_3V3:
+            ret = zx234290_setusb_v0v9_voltage(voltage);
+            break;
+
+		default:
+			return -EINVAL;
+    }
+
+    return ret;
+}
+EXPORT_SYMBOL(zDrvPmic_SetNormal_Voltage);
+
+
+
+int zDrvPmic_SetSleep_Voltage(T_ZDrvPmic_Regulator regulator, int voltage)
+{
+    int ret = 0;
+
+    switch (regulator)
+    {
+        case VCORE0:
+            break;
+
+        case VCORE1:
+            {
+            ret = zx234290_setcore_voltage(voltage);
+            if (ret != 0)
+            {
+                return ret;
+            }
+            break;
+        }
+
+		default:
+			return -EINVAL;
+    }
+
+    return ret;
+}
+
+EXPORT_SYMBOL(zDrvPmic_SetSleep_Voltage);
+
+
+int zDrvPmic_GetNormal_Onoff(T_ZDrvPmic_Regulator regulator, T_ZDrvPmic_Enable* enable)
+{
+    int ret = 0;
+
+    switch (regulator)
+    {
+

+        case VSD1:
+            *enable = zx234290_getsd1_onoff();
+            break;
+
+        case VSIM1:

+            *enable = zx234290_getsim1_onoff();
+            break;
+
+        case VUSB_0V9:
+            *enable = zx234290_getusb_v0v9_onoff();
+            break;
+
+        case VUSB_3V3:
+            *enable = zx234290_getusb_v3v3_onoff();
+            break;
+
+		default:
+			return -EINVAL;
+    }
+
+    return ret;
+}
+
+EXPORT_SYMBOL(zDrvPmic_GetNormal_Onoff);
+
+
+int zDrvPmic_GetNormal_Voltage(T_ZDrvPmic_Regulator regulator, int* voltage)
+{
+    int ret = 0;
+
+    switch (regulator)
+    {
+

+        case VSD1:
+            *voltage = zx234290_getsd1_voltage();
+            break;

+
+        case VSIM1:

+            *voltage = zx234290_getsim1_voltage();
+            break;
+
+        case VUSB_0V9:
+            *voltage = zx234290_getusb_v0v9_voltage();
+            break;
+
+        case VUSB_3V3:
+            *voltage = zx234290_getusb_v3v3_voltage();
+            break;
+
+		default:
+			return -EINVAL;
+    }
+
+    return ret;
+}
+
+EXPORT_SYMBOL(zDrvPmic_GetNormal_Voltage);

+#endif

+

diff --git a/ap/os/linux/linux-3.4.x/drivers/mfd/zx234290-regulator.c b/ap/os/linux/linux-3.4.x/drivers/mfd/zx234290-regulator.c
new file mode 100644
index 0000000..28b57f0
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mfd/zx234290-regulator.c
@@ -0,0 +1,2437 @@
+/*
+ * zx234290-regulator.c  --  regulator for ZX234290 PMICs
+ *
+ * Copyright 2016 ZTE Inc.
+ *
+ * Author: yuxiang<yu.xiang5@zte.com.cn>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+#include<linux/mutex.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/zx234290.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <mach/spinlock.h>
+
+/* 0 for zx234296, 297 for zx234297 */
+ int pmu_chip_ver = 0;
+
+static int zx234290_regulator_write_register(unsigned char addr, unsigned char data, unsigned char mask)
+{
+    int ret = 0;
+    unsigned int content =0;
+	
+    soft_spin_lock(PMIC_SFLOCK);
+
+    ret = zx234290_i2c_read_simple(addr, &content);
+    if (ret < 0)
+    {
+	    soft_spin_unlock(PMIC_SFLOCK);
+        return -EIO;
+    }
+    content &= ~mask;
+    content |= data & mask;
+    ret = zx234290_i2c_write_simple(addr, &content);
+    if (ret != 0)
+    {  
+	    soft_spin_unlock(PMIC_SFLOCK);
+        return ret;
+    }
+    soft_spin_unlock(PMIC_SFLOCK);
+
+    return ret;
+}
+
+int zx234290_regulator_write_register_PSM(unsigned char addr, unsigned char data, unsigned char mask)
+{
+    int ret = 0;
+    unsigned int content =0;
+//    printk("reg = %x, origin data = %x, mask = %x\n", addr, data, mask);
+    ret = zx234290_i2c_read_simple_PSM(addr, &content);
+    if (ret < 0)
+    {
+        return -EIO;
+    }
+//    printk("read content = %x\n", content);
+    content &= ~mask;
+    content |= data & mask;
+//    printk("infact data = %x\n", content);
+    ret = zx234290_i2c_write_simple_PSM(addr, &content);
+    if (ret != 0)
+    {
+        return ret;
+    }
+
+    content = 0;
+    zx234290_i2c_read_simple_PSM(addr, &content);
+//    printk("after write content = %x\n", content);
+
+    return ret;
+}
+
+static int zx234290_regulator_read_register(unsigned char reg, unsigned char *dest)
+{
+    int ret = 0;
+    unsigned int content =0;
+
+	ret = zx234290_i2c_read_simple(reg, &content);
+	if (!ret)
+		*dest = content;
+
+    return ret;
+}
+static int zx234290_regulator_read_register_PSM(unsigned char reg, unsigned char *dest)
+{
+    int ret = 0;
+    unsigned int content =0;
+
+	ret = zx234290_i2c_read_simple_PSM(reg, &content);
+	if (!ret)
+		*dest = content;
+
+    return ret;
+}
+
+int zx234290_get_chip_version(void)
+{
+	int ret;
+    unsigned char reg_addr=0, reg_val=0;
+
+    reg_addr = ZX234297_REG_ADDR_SINK_CONTROL;
+    ret = zx234290_regulator_read_register(reg_addr, &reg_val);
+	if (ret) {
+		return ret;
+	} else if (reg_val == 0xFF) {
+		pmu_chip_ver = 0;
+		return 296;
+	} else {
+		pr_info("recognize zx234297, [0x29] = %#X", reg_val);
+		pmu_chip_ver = 297;
+		return 297;
+	}
+}
+
+#if 1   /* buck1 */
+/*******************************************************************************
+ * Function:
+ * Description:
+ * Parameters:
+ *   Input:
+ *
+ *   Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:
+ ********************************************************************************/
+int zx234290_set_buck1_onoff(T_ZDrvZx234290_LDO_ENABLE status)
+{
+    int ret = 0;
+    unsigned char reg_addr=0, reg_val=0, mask=0;
+
+    if(status > LDO_AVTICE_MAX)
+    {
+        return -EINVAL;
+    }
+
+    reg_addr = ZX234290_REG_ADDR_LDO_EN2;
+
+    reg_val = ZX234290_BITFVAL(status, ZX234290_BUCK1_ON_LSH);
+    mask = ZX234290_BITFMASK(ZX234290_LDOS_ON_WID, ZX234290_BUCK1_ON_LSH);
+
+    ret = zx234290_regulator_write_register(reg_addr, reg_val, mask);
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+
+    return 0;
+}
+
+T_ZDrvZx234290_LDO_ENABLE zx234290_get_buck1_onoff(void)
+{
+    unsigned char reg_addr=0, reg_val=0;
+    T_ZDrvZx234290_LDO_ENABLE status;
+
+    reg_addr = ZX234290_REG_ADDR_LDO_EN2;
+    zx234290_regulator_read_register(reg_addr, &reg_val);
+
+    status = ZX234290_BITFEXT(reg_val, ZX234290_LDOS_ON_WID, ZX234290_BUCK1_ON_LSH);
+
+    return status;
+}
+
+int zx234290_set_buck1_active_mode(T_ZDrvZx234290_BUCK_NRMMODE status)
+{
+    int ret = 0;
+    unsigned char reg_addr=0, reg_val=0, mask=0;
+
+    if(status > BUCK_NRMMODE_MAX)
+    {
+        return -EINVAL;
+    }
+
+    reg_addr = ZX234290_REG_ADDR_BUCK1_MODE;
+
+    reg_val = ZX234290_BITFVAL(status, ZX234290_BUCK1_NRMMODE_LSH);
+    mask = ZX234290_BITFMASK(ZX234290_REGULATOR_MODE_WID, ZX234290_BUCK1_NRMMODE_LSH);
+
+    ret = zx234290_regulator_write_register(reg_addr, reg_val, mask);
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+
+    return 0;
+}
+
+T_ZDrvZx234290_BUCK_NRMMODE zx234290_get_buck1_active_mode(void)
+{
+    unsigned char reg_addr=0, reg_val=0;
+    T_ZDrvZx234290_BUCK_NRMMODE status;
+
+    reg_addr = ZX234290_REG_ADDR_BUCK1_MODE;
+    zx234290_regulator_read_register(reg_addr, &reg_val);
+
+    status = ZX234290_BITFEXT(reg_val, ZX234290_REGULATOR_MODE_WID, ZX234290_BUCK1_NRMMODE_LSH);
+
+    return status;
+}
+
+int zx234290_set_buck1_voltage(T_ZDrvZx234290_VbuckA vol)
+{
+    int ret = 0;
+    unsigned char reg_addr=0, reg_val=0, mask=0;
+
+    if(vol > VBUCKA_MAX)
+    {
+        return -EINVAL;
+    }
+
+    reg_addr = ZX234290_REG_ADDR_BUCK1_VOL;
+
+    reg_val = ZX234290_BITFVAL(vol, ZX234290_BUCK01_VSEL_LSH);
+    mask = ZX234290_BITFMASK(ZX234290_BUCK01_VSEL_WID, ZX234290_BUCK01_VSEL_LSH);
+
+    ret = zx234290_regulator_write_register(reg_addr, reg_val, mask);
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+
+    return 0;
+}
+
+
+T_ZDrvZx234290_VbuckA zx234290_get_buck1_voltage(void)
+{
+    unsigned char reg_addr=0, reg_val=0;
+    T_ZDrvZx234290_VbuckA vol;
+
+    reg_addr = ZX234290_REG_ADDR_BUCK1_VOL;
+    zx234290_regulator_read_register(reg_addr, &reg_val);
+
+    vol = ZX234290_BITFEXT(reg_val, ZX234290_BUCK01_VSEL_WID, ZX234290_BUCK01_VSEL_LSH);
+
+    return vol;
+}
+
+int zx234290_set_buck1_sleep_mode(T_ZDrvZx234290_BUCK1_SLPMODE status)
+{
+    int ret = 0;
+    unsigned char reg_addr=0, reg_val=0, mask=0;
+
+    if(status > BUCK1_SLPMODE_MAX)
+    {
+        return -EINVAL;
+    }
+
+    reg_addr = ZX234290_REG_ADDR_BUCK1_MODE;
+
+    reg_val = ZX234290_BITFVAL(status, ZX234290_BUCK1_SLPMODE_LSH);
+    mask = ZX234290_BITFMASK(ZX234290_REGULATOR_MODE_WID, ZX234290_BUCK1_SLPMODE_LSH);
+    ret = zx234290_regulator_write_register(reg_addr, reg_val, mask);
+
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+
+    return 0;
+}
+
+T_ZDrvZx234290_BUCK1_SLPMODE zx234290_get_buck1_sleep_mode(void)
+{
+    unsigned char reg_addr=0, reg_val=0;
+    T_ZDrvZx234290_BUCK1_SLPMODE status = 0;
+
+    reg_addr = ZX234290_REG_ADDR_BUCK1_MODE;
+
+    zx234290_regulator_read_register(reg_addr, &reg_val);
+    status = ZX234290_BITFEXT(reg_val, ZX234290_REGULATOR_MODE_WID, ZX234290_BUCK1_SLPMODE_LSH);
+
+    return status;
+}
+
+int zx234290_set_buck1_sleep_voltage(T_ZDrvZx234290_VbuckA vol)
+{
+    int ret = 0;
+    unsigned char reg_addr=0, reg_val=0, mask=0;
+
+    if(vol > VBUCKA_MAX)
+    {
+        return -EINVAL;
+    }
+
+    reg_addr = ZX234290_REG_ADDR_BUCK1_SLPVOL;
+
+    reg_val = ZX234290_BITFVAL(vol, ZX234290_BUCK01_SLEEP_VSEL_LSH);
+    mask = ZX234290_BITFMASK(ZX234290_BUCK01_SLEEP_VSEL_WID, ZX234290_BUCK01_SLEEP_VSEL_LSH);
+
+    ret = zx234290_regulator_write_register(reg_addr, reg_val, mask);
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+
+    return 0;
+}
+
+#endif
+#if 1   /* buck2 add*/
+int zx234290_set_buck2_onoff(T_ZDrvZx234290_LDO_ENABLE status)
+{
+    int ret = 0;
+    unsigned char reg_addr=0, reg_val=0, mask=0;
+
+    if(status > LDO_AVTICE_MAX)
+    {
+        return -EINVAL;
+    }
+
+    reg_addr = ZX234290_REG_ADDR_LDO_EN2;
+
+    reg_val = ZX234290_BITFVAL(status, ZX234290_BUCK2_ON_LSH);
+    mask = ZX234290_BITFMASK(ZX234290_LDOS_ON_WID, ZX234290_BUCK2_ON_LSH);
+
+    ret = zx234290_regulator_write_register(reg_addr, reg_val, mask);
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+
+    return 0;
+}
+
+T_ZDrvZx234290_LDO_ENABLE zx234290_get_buck2_onoff(void)
+{
+    unsigned char reg_addr=0, reg_val=0;
+    T_ZDrvZx234290_LDO_ENABLE status;
+
+    reg_addr = ZX234290_REG_ADDR_LDO_EN2;
+    zx234290_regulator_read_register(reg_addr, &reg_val);
+
+    status = ZX234290_BITFEXT(reg_val, ZX234290_LDOS_ON_WID, ZX234290_BUCK2_ON_LSH);
+
+    return status;
+}
+
+int zx234290_set_buck2_active_mode(T_ZDrvZx234290_BUCK_NRMMODE status)
+{
+    int ret = 0;
+	unsigned char  reg_addr=0, reg_val=0, mask=0;
+
+    if(status > BUCK_NRMMODE_MAX)
+    {
+        return -EINVAL;
+    }
+
+    reg_addr = ZX234290_REG_ADDR_BUCK23_MODE;
+
+    reg_val = ZX234290_BITFVAL(status, ZX234290_BUCK2_NRMMODE_LSH);
+    mask = ZX234290_BITFMASK(ZX234290_REGULATOR_MODE_WID, ZX234290_BUCK2_NRMMODE_LSH);
+
+    ret = zx234290_regulator_write_register(reg_addr, reg_val, mask);
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+
+    return 0;
+}
+
+T_ZDrvZx234290_BUCK_NRMMODE zx234290_get_buck2_active_mode(void)
+{
+    unsigned char reg_addr=0, reg_val=0;
+    T_ZDrvZx234290_BUCK_NRMMODE status;
+
+    reg_addr = ZX234290_REG_ADDR_BUCK23_MODE;
+    zx234290_regulator_read_register(reg_addr, &reg_val);
+
+    status = ZX234290_BITFEXT(reg_val, ZX234290_REGULATOR_MODE_WID, ZX234290_BUCK2_NRMMODE_LSH);
+
+    return status;
+}
+
+int zx234290_set_buck2_voltage(T_ZDrvZx234290_VbuckC vol)
+{
+    int ret = 0;
+    unsigned char reg_addr=0, reg_val=0, mask=0;
+
+ 	if(vol > VBUCKC_MAX)
+    {
+        return -EINVAL;
+    }
+    reg_addr = ZX234290_REG_ADDR_BUCK2_VOL;
+
+    reg_val = ZX234290_BITFVAL(vol, ZX234290_BUCK2_VSEL_LSH);
+
+    mask = ZX234290_BITFMASK(ZX234290_BUCK2_VSEL_WID, ZX234290_BUCK2_VSEL_LSH);
+
+    ret = zx234290_regulator_write_register(reg_addr, reg_val, mask);
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+
+    return 0;
+}
+
+T_ZDrvZx234290_VbuckC zx234290_get_buck2_voltage(void)
+{
+    unsigned char reg_addr=0, reg_val=0;
+    T_ZDrvZx234290_VbuckC vol;
+
+    reg_addr = ZX234290_REG_ADDR_BUCK2_VOL;
+    zx234290_regulator_read_register(reg_addr, &reg_val);
+
+    vol = ZX234290_BITFEXT(reg_val, ZX234290_BUCK2_VSEL_WID, ZX234290_BUCK2_VSEL_LSH);
+
+    return vol;
+}
+
+int zx234290_set_buck2_sleep_mode(T_ZDrvZx234290_BUCK234_SLPMODE status)
+{
+    int ret = 0;
+    unsigned char reg_addr=0, reg_val=0, mask=0;
+
+    if(status > BUCK234_SLPMODE_MAX)
+    {
+        return -EINVAL;
+    }
+
+    reg_addr = ZX234290_REG_ADDR_BUCK23_MODE;
+
+    reg_val = ZX234290_BITFVAL(status, ZX234290_BUCK2_SLPMODE_LSH);
+    mask = ZX234290_BITFMASK(ZX234290_REGULATOR_MODE_WID, ZX234290_BUCK2_SLPMODE_LSH);
+    ret = zx234290_regulator_write_register(reg_addr, reg_val, mask);
+
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+
+    return 0;
+}
+
+T_ZDrvZx234290_BUCK234_SLPMODE zx234290_get_buck2_sleep_mode(void)
+{
+    unsigned char reg_addr=0, reg_val=0;
+    T_ZDrvZx234290_BUCK234_SLPMODE status = 0;
+
+    reg_addr = ZX234290_REG_ADDR_BUCK23_MODE;
+
+    zx234290_regulator_read_register(reg_addr, &reg_val);
+    status = ZX234290_BITFEXT(reg_val, ZX234290_REGULATOR_MODE_WID, ZX234290_BUCK2_SLPMODE_LSH);
+
+    return status;
+}
+#endif
+#if 1   /* BUCK3/4µçѹ²»¿É¸Ä add*/
+int zx234290_set_buck3_onoff(T_ZDrvZx234290_LDO_ENABLE status)
+{
+    int ret = 0;
+    unsigned char reg_addr=0, reg_val=0, mask=0;
+
+    if(status > LDO_AVTICE_MAX)
+    {
+        return -EINVAL;
+    }
+
+    reg_addr = ZX234290_REG_ADDR_LDO_EN2;
+
+    reg_val = ZX234290_BITFVAL(status, ZX234290_BUCK3_ON_LSH);
+    mask = ZX234290_BITFMASK(ZX234290_LDOS_ON_WID, ZX234290_BUCK3_ON_LSH);
+
+    ret = zx234290_regulator_write_register(reg_addr, reg_val, mask);
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+    return 0;
+}
+
+int zx234290_set_buck3_active_mode(T_ZDrvZx234290_BUCK_NRMMODE status)
+{
+    int ret = 0;
+    unsigned char reg_addr=0, reg_val=0, mask=0;
+
+    if(status > BUCK_NRMMODE_MAX)
+    {
+        return -EINVAL;
+    }
+
+    reg_addr = ZX234290_REG_ADDR_BUCK23_MODE;
+
+    reg_val = ZX234290_BITFVAL(status, ZX234290_BUCK3_NRMMODE_LSH);
+    mask = ZX234290_BITFMASK(ZX234290_REGULATOR_MODE_WID, ZX234290_BUCK3_NRMMODE_LSH);
+
+    ret = zx234290_regulator_write_register(reg_addr, reg_val, mask);
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+
+    return 0;
+}
+
+
+int zx234290_set_buck3_sleep_mode(T_ZDrvZx234290_BUCK234_SLPMODE status)
+{
+    int ret = 0;
+    unsigned char reg_addr=0, reg_val=0, mask=0;
+
+    if(status > BUCK234_SLPMODE_MAX)
+    {
+        return -EINVAL;
+    }
+
+    reg_addr = ZX234290_REG_ADDR_BUCK23_MODE;
+
+    reg_val = ZX234290_BITFVAL(status, ZX234290_BUCK3_SLPMODE_LSH);
+    mask = ZX234290_BITFMASK(ZX234290_REGULATOR_MODE_WID, ZX234290_BUCK3_SLPMODE_LSH);
+    ret = zx234290_regulator_write_register(reg_addr, reg_val, mask);
+
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+
+    return 0;
+}
+
+#endif
+#if 1   /* buck4  add*/
+int zx234290_set_buck4_onoff(T_ZDrvZx234290_LDO_ENABLE status)
+{
+    int ret = 0;
+    unsigned char reg_addr=0, reg_val=0, mask=0;
+
+    if(status > LDO_AVTICE_MAX)
+    {
+        return -EINVAL;
+    }
+
+    reg_addr = ZX234290_REG_ADDR_LDO_EN2;
+
+    reg_val = ZX234290_BITFVAL(status, ZX234290_BUCK4_ON_LSH);
+    mask = ZX234290_BITFMASK(ZX234290_LDOS_ON_WID, ZX234290_BUCK4_ON_LSH);
+
+    ret = zx234290_regulator_write_register( reg_addr, reg_val, mask);
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+
+    return 0;
+}
+
+
+int zx234290_set_buck4_active_mode(T_ZDrvZx234290_BUCK_NRMMODE status)
+{
+    int ret = 0;
+    unsigned char reg_addr=0, reg_val=0, mask=0;
+
+    if(status > BUCK_NRMMODE_MAX)
+    {
+        return -EINVAL;
+    }
+
+    reg_addr = ZX234290_REG_ADDR_BUCK4_MODE;
+
+    reg_val = ZX234290_BITFVAL(status, ZX234290_BUCK4_NRMMODE_LSH);
+    mask = ZX234290_BITFMASK(ZX234290_REGULATOR_MODE_WID, ZX234290_BUCK4_NRMMODE_LSH);
+
+    ret = zx234290_regulator_write_register( reg_addr, reg_val, mask);
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+
+    return 0;
+}
+
+
+int zx234290_set_buck4_sleep_mode(T_ZDrvZx234290_BUCK234_SLPMODE status)
+{
+    int ret = 0;
+    unsigned char reg_addr=0, reg_val=0, mask=0;
+
+    if(status > BUCK234_SLPMODE_MAX)
+    {
+        return -EINVAL;
+    }
+
+    reg_addr = ZX234290_REG_ADDR_BUCK4_MODE;
+
+    reg_val = ZX234290_BITFVAL(status, ZX234290_BUCK4_SLPMODE_LSH);
+    mask = ZX234290_BITFMASK(ZX234290_REGULATOR_MODE_WID, ZX234290_BUCK4_SLPMODE_LSH);
+    ret = zx234290_regulator_write_register( reg_addr, reg_val, mask);
+
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+    return 0;
+}
+
+#endif
+
+
+#if 1
+int zx234290_set_ldo1_onoff(T_ZDrvZx234290_LDO_ENABLE status)
+{
+    int ret = 0;
+    unsigned char reg_addr=0, reg_val=0, mask=0;
+
+    if(status > LDO_AVTICE_MAX)
+    {
+        return -EINVAL;
+    }
+
+    reg_addr = ZX234290_REG_ADDR_LDO_EN1;
+
+    reg_val = ZX234290_BITFVAL(status, ZX234290_LDO1_ON_LSH);
+    mask = ZX234290_BITFMASK(ZX234290_LDOS_ON_WID, ZX234290_LDO1_ON_LSH);
+
+    ret = zx234290_regulator_write_register(reg_addr, reg_val, mask);
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+
+    return 0;
+}
+
+int zx234290_set_ldo1_onoff_PSM(T_ZDrvZx234290_LDO_ENABLE status)
+{
+    int ret = 0;
+    unsigned char reg_addr=0, reg_val=0, mask=0;
+
+    if(status > LDO_AVTICE_MAX)
+    {
+        return -EINVAL;
+    }
+
+    reg_addr = ZX234290_REG_ADDR_LDO_EN1;
+
+    reg_val = ZX234290_BITFVAL(status, ZX234290_LDO1_ON_LSH);
+    mask = ZX234290_BITFMASK(ZX234290_LDOS_ON_WID, ZX234290_LDO1_ON_LSH);
+
+    ret = zx234290_regulator_write_register_PSM(reg_addr, reg_val, mask);
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+
+    return 0;
+}
+
+
+T_ZDrvZx234290_LDO_ENABLE zx234290_get_ldo1_onoff(void)
+{
+    unsigned char reg_addr=0, reg_val=0;
+    T_ZDrvZx234290_LDO_ENABLE status;
+
+    reg_addr = ZX234290_REG_ADDR_LDO_EN1;
+    zx234290_regulator_read_register(reg_addr, &reg_val);
+
+    status = ZX234290_BITFEXT(reg_val, ZX234290_LDOS_ON_WID, ZX234290_LDO1_ON_LSH);
+
+    return status;
+}
+
+/* ×¢Òâ²ÎÊýµÄÀàÐÍ£¬ LDO1/9/10 - LDOA */
+int zx234290_set_ldo1_voltage(T_ZDrvZx234290_VldoA vol)
+{
+    int ret = 0;
+    unsigned char reg_addr=0, reg_val=0, mask=0;
+
+    if(vol > VLDOA_MAX)
+    {
+        return -EINVAL;
+    }
+
+    reg_addr = ZX234290_REG_ADDR_LDO12_VOL;
+
+    reg_val = ZX234290_BITFVAL(vol, ZX234290_LDO1_VSEL_LSH);
+    mask = ZX234290_BITFMASK(ZX234290_LDO_VSEL_WID, ZX234290_LDO1_VSEL_LSH);
+
+    ret = zx234290_regulator_write_register(reg_addr, reg_val, mask);
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+
+    return 0;
+}
+
+T_ZDrvZx234290_VldoA zx234290_get_ldo1_voltage(void)
+{
+    unsigned char reg_addr=0, reg_val=0;
+    T_ZDrvZx234290_VldoA vol;
+
+    reg_addr = ZX234290_REG_ADDR_LDO12_VOL;
+    zx234290_regulator_read_register(reg_addr, &reg_val);
+
+    vol = ZX234290_BITFEXT(reg_val, ZX234290_LDO_VSEL_WID, ZX234290_LDO1_VSEL_LSH);
+
+    return vol;
+}
+
+int zx234290_set_ldo1_sleep_mode(T_ZDrvZx234290_LDOA_SLPMODE status)
+{
+    int ret = 0;
+    unsigned char reg_addr=0, reg_val=0, mask=0;
+
+    if(status > LDOA_SLPMODE_MAX)
+    {
+        return -EINVAL;
+    }
+
+    reg_addr = ZX234290_REG_ADDR_LDO1234_MODE;
+
+    reg_val = ZX234290_BITFVAL(status, ZX234290_LDO1_SLPMODE_LSH);
+    mask = ZX234290_BITFMASK(ZX234290_REGULATOR_MODE_WID, ZX234290_LDO1_SLPMODE_LSH);
+    ret = zx234290_regulator_write_register(reg_addr, reg_val, mask);
+
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+
+    return 0;
+}
+
+
+T_ZDrvZx234290_LDOA_SLPMODE zx234290_get_ldo1_sleep_mode(void)
+{
+    unsigned char reg_addr=0, reg_val=0;
+    T_ZDrvZx234290_LDOA_SLPMODE status = 0;
+
+    reg_addr = ZX234290_REG_ADDR_LDO1234_MODE;
+
+    zx234290_regulator_read_register(reg_addr, &reg_val);
+    status = ZX234290_BITFEXT(reg_val, ZX234290_REGULATOR_MODE_WID, ZX234290_LDO1_SLPMODE_LSH);
+
+    return status;
+}
+#endif
+
+#if 1   /* LDO2  */
+
+int zx234290_set_ldo2_onoff(T_ZDrvZx234290_LDO_ENABLE status)
+{
+    int ret = 0;
+    unsigned char  reg_addr=0, reg_val=0, mask=0;
+
+    if(status > LDO_AVTICE_MAX)
+    {
+        return -EINVAL;
+    }
+
+    reg_addr = ZX234290_REG_ADDR_LDO_EN1;
+
+    reg_val = ZX234290_BITFVAL(status, ZX234290_LDO2_ON_LSH);
+    mask = ZX234290_BITFMASK(ZX234290_LDOS_ON_WID, ZX234290_LDO2_ON_LSH);
+
+    ret = zx234290_regulator_write_register(reg_addr, reg_val, mask);
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+
+    return 0;
+}
+
+T_ZDrvZx234290_LDO_ENABLE zx234290_get_ldo2_onoff(void)
+{
+    unsigned char reg_addr=0, reg_val=0;
+    T_ZDrvZx234290_LDO_ENABLE status;
+
+    reg_addr = ZX234290_REG_ADDR_LDO_EN1;
+    zx234290_regulator_read_register(reg_addr, &reg_val);
+
+    status = ZX234290_BITFEXT(reg_val, ZX234290_LDOS_ON_WID, ZX234290_LDO2_ON_LSH);  /*  */
+
+    return status;
+}
+
+/* ×¢Òâ²ÎÊýµÄÀàÐÍ£¬ LDO2/3/ - LDOC */
+int zx234290_set_ldo2_voltage(T_ZDrvZx234290_VldoC vol)
+{
+    int ret = 0;
+    unsigned char reg_addr=0, reg_val=0, mask=0;
+
+    reg_addr = ZX234290_REG_ADDR_LDO12_VOL;
+
+    reg_val = ZX234290_BITFVAL(vol, ZX234290_LDO2_VSEL_LSH);
+    mask = ZX234290_BITFMASK(ZX234290_LDO_VSEL_WID, ZX234290_LDO2_VSEL_LSH);
+
+    ret = zx234290_regulator_write_register(reg_addr, reg_val, mask);
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+
+    return 0;
+
+}
+
+T_ZDrvZx234290_VldoC zx234290_get_ldo2_voltage(void)
+{
+    unsigned char reg_addr=0, reg_val=0;
+    T_ZDrvZx234290_VldoC vol;
+
+    reg_addr = ZX234290_REG_ADDR_LDO12_VOL;
+    zx234290_regulator_read_register(reg_addr, &reg_val);
+
+    vol = ZX234290_BITFEXT(reg_val, ZX234290_LDO_VSEL_WID, ZX234290_LDO2_VSEL_LSH);
+
+    return vol;
+}
+
+int zx234290_set_ldo2_sleep_mode(T_ZDrvZx234290_LDOA_SLPMODE status)
+{
+    int ret = 0;
+    unsigned char reg_addr=0, reg_val=0, mask=0;
+
+    if(status > LDOA_SLPMODE_MAX)
+    {
+        return -EINVAL;
+    }
+
+    reg_addr = ZX234290_REG_ADDR_LDO1234_MODE;
+
+    reg_val = ZX234290_BITFVAL(status, ZX234290_LDO2_SLPMODE_LSH);
+    mask = ZX234290_BITFMASK(ZX234290_REGULATOR_MODE_WID, ZX234290_LDO2_SLPMODE_LSH);
+    ret = zx234290_regulator_write_register(reg_addr, reg_val, mask);
+
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+
+    return 0;
+}
+
+T_ZDrvZx234290_LDOA_SLPMODE zx234290_get_ldo2_sleep_mode(void)
+{
+    unsigned char reg_addr=0, reg_val=0;
+    T_ZDrvZx234290_LDOA_SLPMODE status = 0;
+
+    reg_addr = ZX234290_REG_ADDR_LDO1234_MODE;
+
+    zx234290_regulator_read_register(reg_addr, &reg_val);
+    status = ZX234290_BITFEXT(reg_val, ZX234290_REGULATOR_MODE_WID, ZX234290_LDO2_SLPMODE_LSH);
+
+    return status;
+}
+#endif
+#if 1   /* LDO3 add*/
+
+int zx234290_set_ldo3_onoff(T_ZDrvZx234290_LDO_ENABLE status)
+{
+    int ret = 0;
+    unsigned char reg_addr=0, reg_val=0, mask=0;
+
+    if(status > LDO_AVTICE_MAX)
+    {
+        return -EINVAL;
+    }
+
+    reg_addr = ZX234290_REG_ADDR_LDO_EN1;
+
+    reg_val = ZX234290_BITFVAL(status, ZX234290_LDO3_ON_LSH);
+    mask = ZX234290_BITFMASK(ZX234290_LDOS_ON_WID, ZX234290_LDO3_ON_LSH);
+
+    ret = zx234290_regulator_write_register( reg_addr, reg_val, mask);
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+    return 0;
+}
+
+T_ZDrvZx234290_LDO_ENABLE zx234290_get_ldo3_onoff(void)
+{
+    unsigned char reg_addr=0, reg_val=0;
+    T_ZDrvZx234290_LDO_ENABLE status;
+
+    reg_addr = ZX234290_REG_ADDR_LDO_EN1;
+    zx234290_regulator_read_register( reg_addr, &reg_val);
+
+    status = ZX234290_BITFEXT(reg_val, ZX234290_LDOS_ON_WID, ZX234290_LDO3_ON_LSH);  /*  */
+
+    return status;
+}
+
+/* ×¢Òâ²ÎÊýµÄÀàÐÍ£¬ LDO2/3/ - LDOC */
+int zx234290_set_ldo3_voltage(T_ZDrvZx234290_VldoC vol)
+{
+    int ret = 0;
+    unsigned char reg_addr=0, reg_val=0, mask=0;
+
+    reg_addr = ZX234290_REG_ADDR_LDO34_VOL;
+
+    reg_val = ZX234290_BITFVAL(vol, ZX234290_LDO3_VSEL_LSH);
+    mask = ZX234290_BITFMASK(ZX234290_LDO_VSEL_WID, ZX234290_LDO3_VSEL_LSH);
+
+    ret = zx234290_regulator_write_register( reg_addr, reg_val, mask);
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+    return 0;
+}
+
+T_ZDrvZx234290_VldoC zx234290_get_ldo3_voltage(void)
+{
+    unsigned char  reg_addr=0, reg_val=0;
+    T_ZDrvZx234290_VldoC vol;
+
+    reg_addr = ZX234290_REG_ADDR_LDO34_VOL;
+    zx234290_regulator_read_register( reg_addr, &reg_val);
+
+    vol = ZX234290_BITFEXT(reg_val, ZX234290_LDO_VSEL_WID, ZX234290_LDO3_VSEL_LSH);
+
+    return vol;
+}
+
+int zx234290_set_ldo3_sleep_mode(T_ZDrvZx234290_LDOA_SLPMODE status)
+{
+    int ret = 0;
+    unsigned char reg_addr=0, reg_val=0, mask=0;
+
+    if(status > LDOA_SLPMODE_MAX)
+    {
+        return -EINVAL;
+    }
+
+    reg_addr = ZX234290_REG_ADDR_LDO1234_MODE;
+
+    reg_val = ZX234290_BITFVAL(status, ZX234290_LDO3_SLPMODE_LSH);
+    mask = ZX234290_BITFMASK(ZX234290_REGULATOR_MODE_WID, ZX234290_LDO3_SLPMODE_LSH);
+    ret = zx234290_regulator_write_register( reg_addr, reg_val, mask);
+
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+    return 0;
+}
+
+T_ZDrvZx234290_LDOA_SLPMODE zx234290_get_ldo3_sleep_mode(void)
+{
+    unsigned char reg_addr=0, reg_val=0;
+    T_ZDrvZx234290_LDOA_SLPMODE status = 0;
+
+    reg_addr = ZX234290_REG_ADDR_LDO1234_MODE;
+
+    zx234290_regulator_read_register( reg_addr, &reg_val);
+    status = ZX234290_BITFEXT(reg_val, ZX234290_REGULATOR_MODE_WID, ZX234290_LDO3_SLPMODE_LSH);
+
+    return status;
+}
+#endif
+#if 1   /* LDO4 add*/
+
+int zx234290_set_ldo4_onoff(T_ZDrvZx234290_LDO_ENABLE status)
+{
+    int ret = 0;
+    unsigned char  reg_addr=0, reg_val=0, mask=0;
+
+    if(status > LDO_AVTICE_MAX)
+    {
+        return -EINVAL;
+    }
+
+    reg_addr = ZX234290_REG_ADDR_LDO_EN1;
+
+    reg_val = ZX234290_BITFVAL(status, ZX234290_LDO4_ON_LSH);
+    mask = ZX234290_BITFMASK(ZX234290_LDOS_ON_WID, ZX234290_LDO4_ON_LSH);
+
+    ret = zx234290_regulator_write_register( reg_addr, reg_val, mask);
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+
+    return 0;
+}
+
+T_ZDrvZx234290_LDO_ENABLE zx234290_get_ldo4_onoff(void)
+{
+    unsigned char  reg_addr=0, reg_val=0;
+    T_ZDrvZx234290_LDO_ENABLE status;
+
+    reg_addr = ZX234290_REG_ADDR_LDO_EN1;
+    zx234290_regulator_read_register( reg_addr, &reg_val);
+
+    status = ZX234290_BITFEXT(reg_val, ZX234290_LDOS_ON_WID, ZX234290_LDO4_ON_LSH);  /*  */
+
+    return status;
+}
+
+/* ×¢Òâ²ÎÊýµÄÀàÐÍ£¬ LDO4/6/7/8/ - LDOD */
+int zx234290_set_ldo4_voltage(T_ZDrvZx234290_VldoD vol)
+{
+    int ret = 0;
+    unsigned char  reg_addr=0, reg_val=0, mask=0;
+
+    reg_addr = ZX234290_REG_ADDR_LDO34_VOL;
+
+    reg_val = ZX234290_BITFVAL(vol, ZX234290_LDO4_VSEL_LSH);
+    mask = ZX234290_BITFMASK(ZX234290_LDO_VSEL_WID, ZX234290_LDO4_VSEL_LSH);
+
+    ret = zx234290_regulator_write_register( reg_addr, reg_val, mask);
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+    return 0;
+}
+
+T_ZDrvZx234290_VldoD zx234290_get_ldo4_voltage(void)
+{
+    unsigned char  reg_addr=0, reg_val=0;
+    T_ZDrvZx234290_VldoD vol;
+
+    reg_addr = ZX234290_REG_ADDR_LDO34_VOL;
+    zx234290_regulator_read_register( reg_addr, &reg_val);
+
+    vol = ZX234290_BITFEXT(reg_val, ZX234290_LDO_VSEL_WID, ZX234290_LDO4_VSEL_LSH);
+
+    return vol;
+}
+
+int zx234290_set_ldo4_sleep_mode(T_ZDrvZx234290_LDOB_SLPMODE status)
+{
+    int ret = 0;
+    unsigned char  reg_addr=0, reg_val=0, mask=0;
+
+    if(status > LDOB_SLPMODE_MAX)
+    {
+        return -EINVAL;
+    }
+
+    reg_addr = ZX234290_REG_ADDR_LDO1234_MODE;
+
+    reg_val = ZX234290_BITFVAL(status, ZX234290_LDO4_SLPMODE_LSH);
+    mask = ZX234290_BITFMASK(ZX234290_REGULATOR_MODE_WID, ZX234290_LDO4_SLPMODE_LSH);
+    ret = zx234290_regulator_write_register( reg_addr, reg_val, mask);
+
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+    return 0;
+}
+
+T_ZDrvZx234290_LDOB_SLPMODE zx234290_get_ldo4_sleep_mode(void)
+{
+    unsigned char  reg_addr=0, reg_val=0;
+    T_ZDrvZx234290_LDOB_SLPMODE status = 0;
+
+    reg_addr = ZX234290_REG_ADDR_LDO1234_MODE;
+
+    zx234290_regulator_read_register( reg_addr, &reg_val);
+    status = ZX234290_BITFEXT(reg_val, ZX234290_REGULATOR_MODE_WID, ZX234290_LDO4_SLPMODE_LSH);
+
+    return status;
+}
+
+#endif
+
+
+
+#if 1
+
+int zx234290_set_ldo5_onoff(T_ZDrvZx234290_LDO_ENABLE status)
+{
+    int ret = 0;
+    unsigned char reg_addr=0, reg_val=0, mask=0;
+
+    if(status > LDO_AVTICE_MAX)
+    {
+        return -EINVAL;
+    }
+
+    reg_addr = ZX234290_REG_ADDR_LDO_EN1;
+
+    reg_val = ZX234290_BITFVAL(status, ZX234290_LDO5_ON_LSH);
+    mask = ZX234290_BITFMASK(ZX234290_LDOS_ON_WID, ZX234290_LDO5_ON_LSH);
+
+    ret = zx234290_regulator_write_register(reg_addr, reg_val, mask);
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+
+    return 0;
+}
+
+int zx234290_set_ldo5_onoff_PSM(T_ZDrvZx234290_LDO_ENABLE status)
+{
+    int ret = 0;
+    unsigned char reg_addr=0, reg_val=0, mask=0;
+
+    if(status > LDO_AVTICE_MAX)
+    {
+        return -EINVAL;
+    }
+
+    reg_addr = ZX234290_REG_ADDR_LDO_EN1;
+
+    reg_val = ZX234290_BITFVAL(status, ZX234290_LDO5_ON_LSH);
+    mask = ZX234290_BITFMASK(ZX234290_LDOS_ON_WID, ZX234290_LDO5_ON_LSH);
+
+    ret = zx234290_regulator_write_register_PSM(reg_addr, reg_val, mask);
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+
+    return 0;
+}
+
+T_ZDrvZx234290_LDO_ENABLE zx234290_get_ldo5_onoff(void)
+{
+    unsigned char reg_addr=0, reg_val=0;
+    T_ZDrvZx234290_LDO_ENABLE status;
+
+    reg_addr = ZX234290_REG_ADDR_LDO_EN1;
+    zx234290_regulator_read_register(reg_addr, &reg_val);
+
+    status = ZX234290_BITFEXT(reg_val, ZX234290_LDOS_ON_WID, ZX234290_LDO5_ON_LSH);
+
+    return status;
+}
+
+/* ×¢Òâ²ÎÊýµÄÀàÐÍ£¬ LDO5 */
+int zx234290_set_ldo5_voltage(T_ZDrvZx234290_VldoB vol)
+{
+    int ret = 0;
+    unsigned char reg_addr=0, reg_val=0, mask=0;
+
+    if(vol > VLDOB_MAX)
+    {
+        return -EINVAL;
+    }
+
+    reg_addr = ZX234290_REG_ADDR_LDO56_VOL;
+
+    reg_val = ZX234290_BITFVAL(vol, ZX234290_LDO5_VSEL_LSH);
+    mask = ZX234290_BITFMASK(ZX234290_LDO5_VSEL_WID, ZX234290_LDO5_VSEL_LSH);
+
+    ret = zx234290_regulator_write_register(reg_addr, reg_val, mask);
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+
+    return 0;
+}
+
+T_ZDrvZx234290_VldoB zx234290_get_ldo5_voltage(void)
+{
+    unsigned char reg_addr=0, reg_val=0;
+    T_ZDrvZx234290_VldoB vol;
+
+    reg_addr = ZX234290_REG_ADDR_LDO56_VOL;
+    zx234290_regulator_read_register(reg_addr, &reg_val);
+
+    vol = ZX234290_BITFEXT(reg_val, ZX234290_LDO5_VSEL_WID, ZX234290_LDO5_VSEL_LSH);
+
+    return vol;
+}
+
+
+int zx234290_set_ldo5_sleep_mode(T_ZDrvZx234290_LDOB_SLPMODE status)
+{
+    int ret = 0;
+    unsigned char reg_addr=0, reg_val=0, mask=0;
+
+    if(status > LDOB_SLPMODE_MAX)
+    {
+        return -EINVAL;
+    }
+
+    reg_addr = ZX234290_REG_ADDR_LDO5678_MODE;
+
+    reg_val = ZX234290_BITFVAL(status, ZX234290_LDO5_SLPMODE_LSH);
+    mask = ZX234290_BITFMASK(ZX234290_REGULATOR_MODE_WID, ZX234290_LDO5_SLPMODE_LSH);
+    ret = zx234290_regulator_write_register(reg_addr, reg_val, mask);
+
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+
+    return 0;
+}
+
+
+
+T_ZDrvZx234290_LDOB_SLPMODE zx234290_get_ldo5_sleep_mode(void)
+{
+    unsigned char reg_addr=0, reg_val=0;
+    T_ZDrvZx234290_LDOB_SLPMODE status = 0;
+
+    reg_addr = ZX234290_REG_ADDR_LDO5678_MODE;
+
+    zx234290_regulator_read_register(reg_addr, &reg_val);
+    status = ZX234290_BITFEXT(reg_val, ZX234290_REGULATOR_MODE_WID, ZX234290_LDO1_SLPMODE_LSH);
+
+    return status;
+}
+#endif
+
+
+#if 1   /* LDO6 */
+
+int zx234290_set_ldo6_onoff(T_ZDrvZx234290_LDO_ENABLE status)
+{
+    int ret = 0;
+    unsigned char reg_addr=0, reg_val=0, mask=0;
+
+    if(status > LDO_AVTICE_MAX)
+    {
+        return -EINVAL;
+    }
+
+    reg_addr = ZX234290_REG_ADDR_LDO_EN1;
+
+    reg_val = ZX234290_BITFVAL(status, ZX234290_LDO6_ON_LSH);
+    mask = ZX234290_BITFMASK(ZX234290_LDOS_ON_WID, ZX234290_LDO6_ON_LSH);
+
+    ret = zx234290_regulator_write_register(reg_addr, reg_val, mask);
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+
+    return 0;
+}
+
+
+T_ZDrvZx234290_LDO_ENABLE zx234290_get_ldo6_onoff(void)
+{
+    unsigned char reg_addr=0, reg_val=0;
+    T_ZDrvZx234290_LDO_ENABLE status;
+
+    reg_addr = ZX234290_REG_ADDR_LDO_EN1;
+    zx234290_regulator_read_register(reg_addr, &reg_val);
+
+    status = ZX234290_BITFEXT(reg_val, ZX234290_LDOS_ON_WID, ZX234290_LDO6_ON_LSH);
+
+    return status;
+}
+
+int zx234290_set_ldo6_voltage(T_ZDrvZx234290_VldoD vol)
+{
+    int ret = 0;
+    unsigned char reg_addr=0, reg_val=0, mask=0;
+
+    if(vol > VLDOD_MAX)
+    {
+        return -EINVAL;
+    }
+
+    reg_addr = ZX234290_REG_ADDR_LDO56_VOL;
+
+    reg_val = ZX234290_BITFVAL(vol, ZX234290_LDO6_VSEL_LSH);
+    mask = ZX234290_BITFMASK(ZX234290_LDO_VSEL_WID, ZX234290_LDO6_VSEL_LSH);
+
+    ret = zx234290_regulator_write_register(reg_addr, reg_val, mask);
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+
+    return 0;
+}
+
+T_ZDrvZx234290_VldoD zx234290_get_ldo6_voltage(void)
+{
+    unsigned char reg_addr=0, reg_val=0;
+    T_ZDrvZx234290_VldoD vol;
+
+    reg_addr = ZX234290_REG_ADDR_LDO56_VOL;
+    zx234290_regulator_read_register(reg_addr, &reg_val);
+
+    vol = ZX234290_BITFEXT(reg_val, ZX234290_LDO_VSEL_WID, ZX234290_LDO6_VSEL_LSH);
+
+    return vol;
+}
+
+int zx234290_set_ldo6_sleep_mode(T_ZDrvZx234290_LDOB_SLPMODE status)
+{
+    int ret = 0;
+    unsigned char reg_addr=0, reg_val=0, mask=0;
+
+    if(status > LDOB_SLPMODE_MAX)
+    {
+        return -EINVAL;
+    }
+
+    reg_addr = ZX234290_REG_ADDR_LDO5678_MODE;
+
+    reg_val = ZX234290_BITFVAL(status, ZX234290_LDO6_SLPMODE_LSH);
+    mask = ZX234290_BITFMASK(ZX234290_REGULATOR_MODE_WID, ZX234290_LDO6_SLPMODE_LSH);
+    ret = zx234290_regulator_write_register(reg_addr, reg_val, mask);
+
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+
+    return 0;
+}
+
+T_ZDrvZx234290_LDOB_SLPMODE zx234290_get_ldo6_sleep_mode(void)
+{
+    unsigned char reg_addr=0, reg_val=0;
+    T_ZDrvZx234290_LDOB_SLPMODE status = 0;
+
+    reg_addr = ZX234290_REG_ADDR_LDO5678_MODE;
+
+    zx234290_regulator_read_register(reg_addr, &reg_val);
+    status = ZX234290_BITFEXT(reg_val, ZX234290_REGULATOR_MODE_WID, ZX234290_LDO6_SLPMODE_LSH);
+
+    return status;
+}
+#endif
+#if 1   /* LDO7 add*/
+
+int zx234290_set_ldo7_onoff(T_ZDrvZx234290_LDO_ENABLE status)
+{
+    int ret = 0;
+    unsigned char  reg_addr=0, reg_val=0, mask=0;
+
+    if(status > LDO_AVTICE_MAX)
+    {
+        return -EINVAL;
+    }
+
+    reg_addr = ZX234290_REG_ADDR_LDO_EN1;
+
+    reg_val = ZX234290_BITFVAL(status, ZX234290_LDO7_ON_LSH);
+    mask = ZX234290_BITFMASK(ZX234290_LDOS_ON_WID, ZX234290_LDO7_ON_LSH);
+
+    ret = zx234290_regulator_write_register( reg_addr, reg_val, mask);
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+    return 0;
+}
+
+T_ZDrvZx234290_LDO_ENABLE zx234290_get_ldo7_onoff(void)
+{
+    unsigned char  reg_addr=0, reg_val=0;
+    T_ZDrvZx234290_LDO_ENABLE status;
+
+    reg_addr = ZX234290_REG_ADDR_LDO_EN1;
+    zx234290_regulator_read_register( reg_addr, &reg_val);
+
+    status = ZX234290_BITFEXT(reg_val, ZX234290_LDOS_ON_WID, ZX234290_LDO7_ON_LSH);  /*  */
+
+    return status;
+}
+
+/* ×¢Òâ²ÎÊýµÄÀàÐÍ£¬µçѹ·¶Î§*/
+int zx234290_set_ldo7_voltage(T_ZDrvZx234290_VldoD vol)
+{
+    int ret = 0;
+    unsigned char  reg_addr=0, reg_val=0, mask=0;
+
+    if(vol > VLDOD_MAX)
+    {
+        return -EINVAL;
+    }
+    reg_addr = ZX234290_REG_ADDR_LDO78_VOL;
+
+    reg_val = ZX234290_BITFVAL(vol, ZX234290_LDO7_VSEL_LSH);
+    mask = ZX234290_BITFMASK(ZX234290_LDO_VSEL_WID, ZX234290_LDO7_VSEL_LSH);
+
+    ret = zx234290_regulator_write_register( reg_addr, reg_val, mask);
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+    return 0;
+}
+
+T_ZDrvZx234290_VldoD zx234290_get_ldo7_voltage(void)
+{
+    unsigned char  reg_addr=0, reg_val=0;
+    T_ZDrvZx234290_VldoD vol;
+
+    reg_addr = ZX234290_REG_ADDR_LDO78_VOL;
+    zx234290_regulator_read_register( reg_addr, &reg_val);
+
+    vol = ZX234290_BITFEXT(reg_val, ZX234290_LDO_VSEL_WID, ZX234290_LDO7_VSEL_LSH);
+
+    return vol;
+}
+
+int zx234290_set_ldo7_sleep_mode(T_ZDrvZx234290_LDOA_SLPMODE status)
+{
+    int ret = 0;
+    unsigned char  reg_addr=0, reg_val=0, mask=0;
+
+    if(status > LDOA_SLPMODE_MAX)
+    {
+        return -EINVAL;
+    }
+
+    reg_addr = ZX234290_REG_ADDR_LDO5678_MODE;
+
+    reg_val = ZX234290_BITFVAL(status, ZX234290_LDO7_SLPMODE_LSH);
+    mask = ZX234290_BITFMASK(ZX234290_REGULATOR_MODE_WID, ZX234290_LDO7_SLPMODE_LSH);
+    ret = zx234290_regulator_write_register( reg_addr, reg_val, mask);
+
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+    return 0;
+}
+
+T_ZDrvZx234290_LDOA_SLPMODE zx234290_get_ldo7_sleep_mode(void)
+{
+    unsigned char  reg_addr=0, reg_val=0;
+    T_ZDrvZx234290_LDOA_SLPMODE status = 0;
+
+    reg_addr = ZX234290_REG_ADDR_LDO5678_MODE;
+
+    zx234290_regulator_read_register( reg_addr, &reg_val);
+    status = ZX234290_BITFEXT(reg_val, ZX234290_REGULATOR_MODE_WID, ZX234290_LDO7_SLPMODE_LSH);
+
+    return status;
+}
+#endif
+
+
+#if 1   /* LDO8 */
+/*******************************************************************************
+ * Function:
+ * Description:
+ * Parameters:
+ *   Input:
+ *
+ *   Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:
+ ********************************************************************************/
+
+int zx234290_set_ldo8_onoff(T_ZDrvZx234290_LDO_ENABLE status)
+{
+    int ret = 0;
+    unsigned char reg_addr=0, reg_val=0, mask=0;
+
+    if(status > LDO_AVTICE_MAX)
+    {
+        return -EINVAL;
+    }
+
+    reg_addr = ZX234290_REG_ADDR_LDO_EN1;
+
+    reg_val = ZX234290_BITFVAL(status, ZX234290_LDO8_ON_LSH);
+    mask = ZX234290_BITFMASK(ZX234290_LDOS_ON_WID, ZX234290_LDO8_ON_LSH);
+
+    ret = zx234290_regulator_write_register(reg_addr, reg_val, mask);
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+
+    return 0;
+}
+
+T_ZDrvZx234290_LDO_ENABLE zx234290_get_ldo8_onoff(void)
+{
+    unsigned char reg_addr=0, reg_val=0;
+    T_ZDrvZx234290_LDO_ENABLE status;
+
+    reg_addr = ZX234290_REG_ADDR_LDO_EN1;
+    zx234290_regulator_read_register(reg_addr, &reg_val);
+
+    status = ZX234290_BITFEXT(reg_val, ZX234290_LDOS_ON_WID, ZX234290_LDO8_ON_LSH);  /*  */
+
+    return status;
+}
+
+int zx234290_set_ldo8_voltage(T_ZDrvZx234290_VldoD vol)
+{
+    int ret = 0;
+    unsigned char reg_addr=0, reg_val=0, mask=0;
+
+    if(vol > VLDOD_MAX)
+    {
+        return -EINVAL;
+    }
+
+    reg_addr = ZX234290_REG_ADDR_LDO78_VOL;
+
+    reg_val = ZX234290_BITFVAL(vol, ZX234290_LDO8_VSEL_LSH);
+    mask = ZX234290_BITFMASK(ZX234290_LDO_VSEL_WID, ZX234290_LDO8_VSEL_LSH);
+
+    ret = zx234290_regulator_write_register(reg_addr, reg_val, mask);
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+
+    return 0;
+}
+
+T_ZDrvZx234290_VldoD zx234290_get_ldo8_voltage(void)
+{
+    unsigned char reg_addr=0, reg_val=0;
+    T_ZDrvZx234290_VldoD vol;
+
+    reg_addr = ZX234290_REG_ADDR_LDO78_VOL;
+    zx234290_regulator_read_register(reg_addr, &reg_val);
+
+    vol = ZX234290_BITFEXT(reg_val, ZX234290_LDO_VSEL_WID, ZX234290_LDO8_VSEL_LSH);
+
+    return vol;
+}
+
+int zx234290_set_ldo8_sleep_mode(T_ZDrvZx234290_LDOA_SLPMODE status)
+{
+    int ret = 0;
+    unsigned char reg_addr=0, reg_val=0, mask=0;
+
+    if(status > LDOA_SLPMODE_MAX)
+    {
+        return -EINVAL;
+    }
+
+    reg_addr = ZX234290_REG_ADDR_LDO5678_MODE;
+
+    reg_val = ZX234290_BITFVAL(status, ZX234290_LDO8_SLPMODE_LSH);
+    mask = ZX234290_BITFMASK(ZX234290_REGULATOR_MODE_WID, ZX234290_LDO8_SLPMODE_LSH);
+    ret = zx234290_regulator_write_register(reg_addr, reg_val, mask);
+
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+
+    return 0;
+}
+
+
+T_ZDrvZx234290_LDOA_SLPMODE zx234290_get_ldo8_sleep_mode(void)
+{
+    unsigned char reg_addr=0, reg_val=0;
+    T_ZDrvZx234290_LDOA_SLPMODE status = 0;
+
+    reg_addr = ZX234290_REG_ADDR_LDO5678_MODE;
+
+    zx234290_regulator_read_register(reg_addr, &reg_val);
+    status = ZX234290_BITFEXT(reg_val, ZX234290_REGULATOR_MODE_WID, ZX234290_LDO8_SLPMODE_LSH);
+
+    return status;
+}
+#endif
+#if 1   /* LDO9 add*/
+
+int zx234290_set_ldo9_onoff(T_ZDrvZx234290_LDO_ENABLE status)
+{
+    int ret = 0;
+    unsigned char reg_addr=0, reg_val=0, mask=0;    /*  4 ¸ö²ÎÊý    */
+
+    if(status >= LDO_AVTICE_MAX)
+    {
+        return -EINVAL;
+    }
+
+    reg_addr = ZX234290_REG_ADDR_LDO_EN2;
+
+	if (pmu_chip_ver == 297) {
+	    reg_val = ZX234290_BITFVAL(status, ZX234297_LDO9_ON_LSH);
+	    mask = ZX234290_BITFMASK(ZX234290_LDOS_ON_WID, ZX234297_LDO9_ON_LSH);
+	} else {
+	    reg_val = ZX234290_BITFVAL(status, ZX234290_LDO9_ON_LSH);
+	    mask = ZX234290_BITFMASK(ZX234290_LDOS_ON_WID, ZX234290_LDO9_ON_LSH);
+	}
+
+    ret = zx234290_regulator_write_register( reg_addr, reg_val, mask);
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+    return 0;
+}
+
+T_ZDrvZx234290_LDO_ENABLE zx234290_get_ldo9_onoff(void)
+{
+    unsigned char  reg_addr=0, reg_val=0;
+    T_ZDrvZx234290_LDO_ENABLE status;
+
+    reg_addr = ZX234290_REG_ADDR_LDO_EN2;
+    zx234290_regulator_read_register( reg_addr, &reg_val);
+
+	if (pmu_chip_ver == 297) {
+    	status = ZX234290_BITFEXT(reg_val, ZX234290_LDOS_ON_WID, ZX234297_LDO9_ON_LSH);
+	} else {
+    	status = ZX234290_BITFEXT(reg_val, ZX234290_LDOS_ON_WID, ZX234290_LDO9_ON_LSH);
+	}
+
+    return status;
+}
+
+/* ×¢Òâ²ÎÊýµÄÀàÐÍ£¬ LDO1/9/10 - LDOA */
+int zx234290_set_ldo9_voltage(T_ZDrvZx234290_VldoA vol)
+{
+    int ret = 0;
+    unsigned char  reg_addr=0, reg_val=0, mask=0;
+
+    reg_addr = ZX234290_REG_ADDR_LDO9_VOL;
+
+    reg_val = ZX234290_BITFVAL(vol, ZX234290_LDO9_VSEL_LSH);
+    mask = ZX234290_BITFMASK(ZX234290_LDO_VSEL_WID, ZX234290_LDO9_VSEL_LSH);
+
+    ret = zx234290_regulator_write_register( reg_addr, reg_val, mask);
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+    return 0;
+}
+
+T_ZDrvZx234290_VldoA zx234290_get_ldo9_voltage(void)
+{
+    unsigned char  reg_addr=0, reg_val=0;
+    T_ZDrvZx234290_VldoA vol;
+
+    reg_addr = ZX234290_REG_ADDR_LDO9_VOL;
+    zx234290_regulator_read_register( reg_addr, &reg_val);
+
+    vol = ZX234290_BITFEXT(reg_val, ZX234290_LDO_VSEL_WID, ZX234290_LDO9_VSEL_LSH);
+
+    return vol;
+}
+
+int zx234290_set_ldo9_sleep_mode(T_ZDrvZx234290_LDOA_SLPMODE status)
+{
+    int ret = 0;
+    unsigned char  reg_addr=0, reg_val=0, mask=0;
+
+    if(status >= LDOA_SLPMODE_MAX)
+    {
+        return -EINVAL;
+    }
+
+    reg_addr = ZX234290_REG_ADDR_LDO910_MODE;
+
+    reg_val = ZX234290_BITFVAL(status, ZX234290_LDO9_SLPMODE_LSH);
+    mask = ZX234290_BITFMASK(ZX234290_REGULATOR_MODE_WID, ZX234290_LDO9_SLPMODE_LSH);
+    ret = zx234290_regulator_write_register( reg_addr, reg_val, mask);
+
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+    return 0;
+}
+
+T_ZDrvZx234290_LDOA_SLPMODE zx234290_get_ldo9_sleep_mode(void)
+{
+    unsigned char  reg_addr=0, reg_val=0;
+    T_ZDrvZx234290_LDOA_SLPMODE status = 0;
+
+    reg_addr = ZX234290_REG_ADDR_LDO910_MODE;
+
+    zx234290_regulator_read_register( reg_addr, &reg_val);
+    status = ZX234290_BITFEXT(reg_val, ZX234290_REGULATOR_MODE_WID, ZX234290_LDO9_SLPMODE_LSH);
+
+    return status;
+}
+#endif
+#if 1 /* LDO10 add*/
+int zx234290_set_ldo10_onoff(T_ZDrvZx234290_LDO_ENABLE status)
+{
+    int ret = 0;
+    unsigned char  reg_addr=0, reg_val=0, mask=0;
+
+    if(status >= LDO_AVTICE_MAX)
+    {
+        return -EINVAL;
+    }
+
+    reg_addr = ZX234290_REG_ADDR_LDO_EN2;
+	if (pmu_chip_ver == 297) {
+	    reg_val = ZX234290_BITFVAL(status, ZX234297_LDO10_ON_LSH);
+	    mask = ZX234290_BITFMASK(ZX234290_LDOS_ON_WID, ZX234297_LDO10_ON_LSH);
+	} else {
+	    reg_val = ZX234290_BITFVAL(status, ZX234290_LDO10_ON_LSH);
+	    mask = ZX234290_BITFMASK(ZX234290_LDOS_ON_WID, ZX234290_LDO10_ON_LSH);
+	}
+
+    ret = zx234290_regulator_write_register( reg_addr, reg_val, mask);
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+
+    return 0;
+}
+
+T_ZDrvZx234290_LDO_ENABLE zx234290_get_ldo10_onoff(void)
+{
+    unsigned char  reg_addr=0, reg_val=0;
+    T_ZDrvZx234290_LDO_ENABLE status;
+
+    reg_addr = ZX234290_REG_ADDR_LDO_EN2;
+    zx234290_regulator_read_register( reg_addr, &reg_val);
+
+	if (pmu_chip_ver == 297) {
+	    status = ZX234290_BITFEXT(reg_val, ZX234290_LDOS_ON_WID, ZX234297_LDO10_ON_LSH);
+	} else {
+	    status = ZX234290_BITFEXT(reg_val, ZX234290_LDOS_ON_WID, ZX234290_LDO10_ON_LSH);
+	}
+
+    return status;
+}
+
+static int zx234290_set_ldo10_voltage(unsigned char vol)
+{
+    int ret = 0;
+    unsigned char  reg_addr=0, reg_val=0, mask=0;
+
+    reg_addr = ZX234290_REG_ADDR_LDO10_RTCLDO_VOL;
+
+    reg_val = ZX234290_BITFVAL(vol, ZX234290_LDO10_VSEL_LSH);
+    mask = ZX234290_BITFMASK(ZX234290_LDO_VSEL_WID, ZX234290_LDO10_VSEL_LSH);
+
+    ret = zx234290_regulator_write_register( reg_addr, reg_val, mask);
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+    return 0;
+
+}
+
+/* ×¢Òâ²ÎÊýµÄÀàÐÍ£¬ zx234296 LDO1/9/10 - LDOA */
+int zx234290_set_ldo10_voltageA(T_ZDrvZx234290_VldoA vol)
+{
+    if (vol >= VLDOA_MAX)
+    {
+        return -EINVAL;
+    }
+
+	return zx234290_set_ldo10_voltage((unsigned char)vol);
+}
+
+/* ×¢Òâ²ÎÊýµÄÀàÐÍ£¬ zx234297 LDO10 - LDOF */
+int zx234297_set_ldo10_voltageF(T_ZDrvZx234297_VldoF vol)
+{
+    if (vol >= VLDOF_MAX)
+    {
+        return -EINVAL;
+    }
+
+	return zx234290_set_ldo10_voltage((unsigned char)vol);
+}
+
+static unsigned char zx234290_get_ldo10_voltage(void)
+{
+    unsigned char  reg_addr=0, reg_val=0;
+
+    reg_addr = ZX234290_REG_ADDR_LDO10_RTCLDO_VOL;
+    zx234290_regulator_read_register( reg_addr, &reg_val);
+
+    return ZX234290_BITFEXT(reg_val, ZX234290_LDO_VSEL_WID, ZX234290_LDO10_VSEL_LSH);
+}
+
+T_ZDrvZx234290_VldoA zx234290_get_ldo10_voltageA(void)
+{
+	unsigned char vol;
+
+	vol = zx234290_get_ldo10_voltage();
+
+	return (T_ZDrvZx234290_VldoA)vol;
+}
+
+T_ZDrvZx234297_VldoF zx234290_get_ldo10_voltageF(void)
+{
+	unsigned char vol;
+
+	vol = zx234290_get_ldo10_voltage();
+
+	return (T_ZDrvZx234297_VldoF)vol;
+}
+
+int zx234290_set_ldo10_sleep_mode(T_ZDrvZx234290_LDOA_SLPMODE status)
+{
+    int ret = 0;
+    unsigned char  reg_addr=0, reg_val=0, mask=0;
+
+    if(status >= LDOA_SLPMODE_MAX)
+    {
+        return -EINVAL;
+    }
+
+    reg_addr = ZX234290_REG_ADDR_LDO910_MODE;
+
+    reg_val = ZX234290_BITFVAL(status, ZX234290_LDO10_SLPMODE_LSH);
+    mask = ZX234290_BITFMASK(ZX234290_REGULATOR_MODE_WID, ZX234290_LDO10_SLPMODE_LSH);
+    ret = zx234290_regulator_write_register( reg_addr, reg_val, mask);
+
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+    return 0;
+}
+
+T_ZDrvZx234290_LDOA_SLPMODE zx234290_get_ldo10_sleep_mode(void)
+{
+    unsigned char  reg_addr=0, reg_val=0;
+    T_ZDrvZx234290_LDOA_SLPMODE status = 0;
+
+    reg_addr = ZX234290_REG_ADDR_LDO910_MODE;
+
+    zx234290_regulator_read_register( reg_addr, &reg_val);
+    status = ZX234290_BITFEXT(reg_val, ZX234290_REGULATOR_MODE_WID, ZX234290_LDO10_SLPMODE_LSH);
+
+    return status;
+}
+#endif
+
+#if 1   /* LDO COIN add*/
+int zx234290_setVortc(T_ZDrvZx234290_VldoE vol)
+{
+    int ret = 0;
+    unsigned char reg_addr=0, reg_val=0, mask=0;
+
+    reg_addr = ZX234290_REG_ADDR_LDO10_RTCLDO_VOL;
+
+    reg_val = ZX234290_BITFVAL(vol, ZX234290_VORTC_VSEL_LSH);
+    mask = ZX234290_BITFMASK(ZX234290_VORTC_VSEL_WID, ZX234290_VORTC_VSEL_LSH);
+
+    ret = zx234290_regulator_write_register( reg_addr, reg_val, mask);
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+    return 0;
+
+}
+
+T_ZDrvZx234290_VldoE zx234290_getVortc(void)
+{
+    unsigned char  reg_addr=0, reg_val=0;
+    T_ZDrvZx234290_VldoE vol;
+
+    reg_addr = ZX234290_REG_ADDR_LDO10_RTCLDO_VOL;
+    zx234290_regulator_read_register( reg_addr, &reg_val);
+
+    vol = ZX234290_BITFEXT(reg_val, ZX234290_VORTC_VSEL_WID, ZX234290_VORTC_VSEL_LSH);
+
+    return vol;
+}
+#endif
+
+#if 1   /* Sink Control */
+int zx234297_set_sink(T_ZDrvZx234297_SINK sink_num, int is_on, T_ZDrvZx234297_SINK_CURRENT sink_current)
+{
+    int ret = 0;
+	unsigned char lsh_on, lsh_current;
+    unsigned char reg_addr=0, reg_val=0, mask=0;
+
+	if (sink_num == ZX234297_SINK1) {
+		lsh_on = ZX234297_SINK1_ON_LSH;
+		lsh_current = ZX234297_SINK1_CURRENT_LSH;
+	} else if (sink_num == ZX234297_SINK2) {
+		lsh_on = ZX234297_SINK2_ON_LSH;
+		lsh_current = ZX234297_SINK2_CURRENT_LSH;
+	} else
+		return -EINVAL;
+
+	if (is_on) {
+		if (sink_current >= SINK_CURRENT_MAX)
+			sink_current = SINK_CURRENT_120MA;
+
+		reg_addr = ZX234297_REG_ADDR_SINK_CONTROL;
+	    reg_val = ZX234290_BITFVAL(sink_current, lsh_current);
+	    mask = ZX234290_BITFMASK(ZX234297_SINK_CURRENT_WID, lsh_current);
+	    ret = zx234290_regulator_write_register( reg_addr, reg_val, mask);
+	    if (ret != 0)
+	    {
+	        return -EIO;
+	    }
+	}
+
+	is_on = !!is_on;
+    reg_addr = ZX234290_REG_ADDR_LDO_EN2;
+    reg_val = ZX234290_BITFVAL(is_on, lsh_on);
+    mask = ZX234290_BITFMASK(ZX234297_SINK_ON_WID, lsh_on);
+    ret = zx234290_regulator_write_register( reg_addr, reg_val, mask);
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+
+    return 0;
+}
+
+int zx234297_set_sink_slpmode(T_ZDrvZx234297_SINK sink_num, T_ZDrvZx234297_SINK_SLPMODE mode)
+{
+    int ret = 0;
+	unsigned char lsh;
+    unsigned char reg_addr=0, reg_val=0, mask=0;
+
+	if (mode >= SLPMODE_MAX) {
+		return -EINVAL;
+	}
+	if (sink_num == ZX234297_SINK1) {
+		lsh = ZX234297_SINK1_SLP_MODE_LSH;
+	} else if (sink_num == ZX234297_SINK2) {
+		lsh = ZX234297_SINK2_SLP_MODE_LSH;
+	} else
+		return -EINVAL;
+
+    reg_addr = ZX234290_REG_ADDR_LDO910_MODE;
+    reg_val = ZX234290_BITFVAL(mode, lsh);
+    mask = ZX234290_BITFMASK(ZX234297_SINK_ON_WID, lsh);
+    ret = zx234290_regulator_write_register( reg_addr, reg_val, mask);
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+
+    return 0;
+}
+#endif
+
+#if 1
+/*******************************************************************************
+* Function:
+* Description:  0, do forced shutdown when TLLP time up ;
+                1, do re-start up procedure when TLLP time up. (default is 0)
+* Parameters:
+*    Input:
+*
+*    Output:
+*
+* Returns:
+*
+*
+* Others:
+********************************************************************************/
+int zx234290_SetTllpToDo(T_ZDrvZx234290_LLP_TODO enable)
+{
+    int ret = 0;
+    unsigned char  reg_addr=0, reg_val=0, mask=0;
+
+    if(enable > LLP_TODO_MAX)
+    {
+        return -EINVAL;
+    }
+
+    reg_addr = ZX234290_REG_ADDR_PWRKEY_CONTROL1;
+
+    reg_val = ZX234290_BITFVAL(enable, 3);
+    mask = ZX234290_BITFMASK(1, 3);
+
+    //ret = zDrvPmic_SetRegister(slv_addr, reg_addr, reg_val, mask);
+    ret = zx234290_regulator_write_register(reg_addr, reg_val, mask);
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+
+    return 0;
+}
+#ifdef CONFIG_PMU_GM
+int zx234296h_support_parameter(void)
+{
+    int ret = 0;
+    unsigned char reg_addr=0, reg_val=0, mask=0;
+
+#if 1//set gm debug bulk1&4
+	reg_addr = 0x70;
+	reg_val = 0xA1;
+	ret = zx234290_i2c_write_simple(reg_addr,&reg_val);	
+	if (ret != 0)
+	{
+		return -EIO;
+	}
+	
+	reg_addr = 0x8E;
+	reg_val = 0xD4;
+	ret = zx234290_i2c_write_simple(reg_addr,&reg_val);	
+	if (ret != 0)
+	{
+		return -EIO;
+	}
+	printk("gm set reg[0x8E]=0xd4,reg[0x70]=0xA1 ok!\n");
+	
+#endif
+
+    return 0;
+}
+#endif
+/*******************************************************************************
+* Function:
+* Description: Long Long Pressed Enable : 0 is disable, 1 is enable long long pressed interrupt.(default is 0)
+* Parameters:
+*    Input:
+*
+*    Output:
+*
+* Returns:
+*
+*
+* Others:
+********************************************************************************/
+int zx234290_SetLlpEnable(T_ZDrvZx234290_LLP_ENABLE enable)
+{
+    int ret = 0;
+    unsigned char  reg_addr=0, reg_val=0, mask=0;
+
+    if(enable > LLP_ENABLE_MAX)
+    {
+        return -EINVAL;
+    }
+
+    reg_addr = ZX234290_REG_ADDR_PWRKEY_CONTROL1;
+
+    reg_val = ZX234290_BITFVAL(enable, 2);
+    mask = ZX234290_BITFMASK(1, 2);
+
+    //ret = zDrvPmic_SetRegister(slv_addr, reg_addr, reg_val, mask);
+    ret = zx234290_regulator_write_register(reg_addr, reg_val, mask);
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+
+    return 0;
+}
+
+/*******************************************************************************
+* Function:
+* Description: Long Pressed Time, 00, 01, 10, 11: 1sec, 2sec, 3sec, 4sec (default is 2 sec)
+* Parameters:
+*    Input:
+*
+*    Output:
+*
+* Returns:
+*
+*
+* Others:
+********************************************************************************/
+int zx234290_SetLpTime(T_ZDrvZx234290_LP_TIME time)
+{
+    int ret = 0;
+    unsigned char  reg_addr=0, reg_val=0, mask=0;
+
+    if(time > LP_TIME_MAX)
+    {
+        return -EINVAL;
+    }
+
+    reg_addr = ZX234290_REG_ADDR_PWRKEY_CONTROL1;
+
+    reg_val = ZX234290_BITFVAL(time, 0);
+    mask = ZX234290_BITFMASK(2, 0);
+
+    //ret = zDrvPmic_SetRegister(slv_addr, reg_addr, reg_val, mask);
+    ret = zx234290_regulator_write_register(reg_addr, reg_val, mask);
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+
+    return 0;
+}
+
+/*******************************************************************************
+* Function:
+* Description: RESTART_DLY[1:0]=00, 01, 10, 11:    0.25sec, 0.5sec, 1sec, 2sec (default is 1 sec)
+* Parameters:
+*    Input:
+*
+*    Output:
+*
+* Returns:
+*
+*
+* Others:
+********************************************************************************/
+int zx234290_SetRestartDly(T_ZDrvZx234290_TIME_IT time)
+{
+    int ret = 0;
+    unsigned char  reg_addr=0, reg_val=0, mask=0;
+
+    if(time > LLP_DLY_MAX)
+    {
+        return -EINVAL;
+    }
+
+    reg_addr = ZX234290_REG_ADDR_PWRKEY_CONTROL2;
+
+    reg_val = ZX234290_BITFVAL(time, 0);
+    mask = ZX234290_BITFMASK(2, 0);
+
+    //ret = zDrvPmic_SetRegister(slv_addr, reg_addr, reg_val, mask);
+    ret = zx234290_regulator_write_register(reg_addr, reg_val, mask);
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+
+    return 0;
+}
+
+
+/*******************************************************************************
+* Function:
+* Description: TIME_LP[1:0]=00, 01, 10, 11:    6sec, 7sec, 8sec, 10sec
+* Parameters:
+*    Input:
+*
+*    Output:
+*
+* Returns:
+*
+*
+* Others:
+********************************************************************************/
+int zx234290_SetLlpTime(T_ZDrvZx234290_LLP_TIME time)
+{
+    int ret = 0;
+    unsigned char  reg_addr=0, reg_val=0, mask=0;
+
+    if(time > LLP_TIME_MAX)
+    {
+        return -EINVAL;
+    }
+
+    reg_addr = ZX234290_REG_ADDR_PWRKEY_CONTROL2;
+
+    reg_val = ZX234290_BITFVAL(time, 2);
+    mask = ZX234290_BITFMASK(2, 2);
+
+    //ret = zDrvPmic_SetRegister(slv_addr, reg_addr, reg_val, mask);
+    ret = zx234290_regulator_write_register(reg_addr, reg_val, mask);
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+
+    return 0;
+}
+
+/*******************************************************************************
+* Function:
+* Description:EN_VO_DISCH 0: Disable LDO output discharge resistance. The output becomes high impedance.
+              EN_VO_DISCH 1: Enable LDO output discharge resistance.
+* Parameters:
+*    Input:
+*
+*    Output:
+*
+* Returns:
+*
+*
+* Others:
+********************************************************************************/
+int zx234290_SetDischarger(T_ZDrvZx234290_LdoDischarger ldo, T_ZDrvZx234290_DISCHARGER_ENABLE enable)
+{
+    int ret = 0;
+    unsigned char  reg_addr=0, reg_val=0, mask=0;
+
+    if( ldo < 8 )
+    {
+        reg_addr = ZX234290_REG_ADDR_EN_DISCH1;
+    }
+    else
+    {
+        ldo -= 8;//
+        reg_addr = ZX234290_REG_ADDR_EN_DISCH2;
+    }
+
+    reg_val = ZX234290_BITFVAL(enable, ldo);
+    mask = ZX234290_BITFMASK(1, ldo);
+    //ret = zDrvPmic_SetRegister(slv_addr, reg_addr, reg_val, mask);
+    ret = zx234290_regulator_write_register(reg_addr, reg_val, mask);
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+
+    return 0;
+}
+
+int zDrvZx234290_LdoRstErr(void)
+{
+    int ret = 0;
+    unsigned char reg_val=0,mask=0;
+
+    reg_val = ZX234290_BITFVAL(1, ZX234290_LDO_RSTERR_LSH);
+    mask = ZX234290_BITFMASK(ZX234290_LDO_RSTERR_WID, ZX234290_LDO_RSTERR_LSH);
+    ret = zx234290_regulator_write_register(ZX234290_REG_ADDR_BUCK_FAULT_STATUS, reg_val, mask);
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+
+    return 0;
+}
+
+/*******************************************************************************
+* Function: Zx234290_SetUserReg_PSM
+* Description:
+* Parameters:
+*    Input:
+*
+*    Output:
+*
+* Returns:
+*
+*
+* Others:
+********************************************************************************/
+int Zx234290_SetUserReg_PSM(unsigned char data)
+{
+    int ret = 0;
+    unsigned char reg_addr=0, reg_val=0, mask=0;
+
+    reg_addr = ZX234290_REG_ADDR_USER_RESERVED;
+    mask     = 0xff;
+    reg_val  = data;
+    ret = zx234290_regulator_write_register_PSM(reg_addr, reg_val, mask);
+    if (ret != 0)
+    {
+        return -EIO;
+    }
+
+    return 0;
+}
+int zx234290_setSoftOn(bool SoftOn)
+{
+    int ret = 0;
+    unsigned char reg_addr=0;
+    unsigned char reg_val = 0;
+    unsigned char mask = 0;
+
+    reg_addr = ZX234290_REG_ADDR_SYS_CTRL;
+
+    mask     = 0x01 << 7;
+    if(SoftOn)
+    {
+        reg_val  = 0x01 << 7;
+    }
+
+    ret = zx234290_regulator_write_register_PSM(reg_addr, reg_val, mask);
+
+    return ret;
+}
+
+int zx234290_setSoftOn_PSM(bool SoftOn)
+{
+    int ret = 0;
+    unsigned char reg_addr=0;
+    unsigned char reg_val = 0;
+    unsigned char mask = 0;
+
+    reg_addr = ZX234290_REG_ADDR_SYS_CTRL;
+
+    mask     = 0x01 << 7;
+    if(SoftOn)
+    {
+        reg_val  = 0x01 << 7;
+    }
+
+    ret = zx234290_regulator_write_register_PSM(reg_addr, reg_val, mask);
+
+    return ret;
+}
+int zx234290_getPoweronStatus(void)
+{
+    int ret = 0;
+    unsigned char status = 0;
+    unsigned char reg_addr=0;
+
+    reg_addr = ZX234290_REG_ADDR_STSA;
+
+     ret = zx234290_regulator_read_register(reg_addr, &status);
+     if (ret != 0)
+     {
+         return -1;
+     }
+
+    return (status & (1<<5));
+}
+int zx234290_getPoweronStatus_PSM(void)
+{
+    int ret = 0;
+    unsigned char status = 0;
+    unsigned char reg_addr=0;
+
+    reg_addr = ZX234290_REG_ADDR_STSA;
+
+    ret = zx234290_regulator_read_register_PSM(reg_addr, &status);
+
+     if (ret != 0)
+     {
+         return -1;
+     }
+
+    return (status & (1<<5));
+}
+
+#endif
+
+#ifdef ZX234290_PWR_FAUL_PROCESS
+
+static BLOCKING_NOTIFIER_HEAD(zx234290_ldo_fail_notifier_list);
+
+int zx234290_register_client(struct notifier_block *nb)
+{
+	return blocking_notifier_chain_register(&zx234290_ldo_fail_notifier_list, nb);
+}
+EXPORT_SYMBOL(zx234290_register_client);
+
+int zx234290_unregister_client(struct notifier_block *nb)
+{
+	return blocking_notifier_chain_unregister(&zx234290_ldo_fail_notifier_list, nb);
+}
+EXPORT_SYMBOL(zx234290_unregister_client);
+
+int zx234290_notifier_call_chain(unsigned long val, void *v)
+{
+//	printk(KERN_INFO "zx234290_notifier_call_chain,val=%ld\n", val);
+	return blocking_notifier_call_chain(&zx234290_ldo_fail_notifier_list, val, v);
+}
+EXPORT_SYMBOL_GPL(zx234290_notifier_call_chain);
+
+
+static irqreturn_t zx234290_buck_faul_irq(int irq, void *id)
+{
+	u8 buck_sts=0, buck_mask=0;
+	//u8 val=0;
+	zx234290_regulator_read_register(ZX234290_REG_ADDR_BUCK_FAULT_STATUS,  &buck_sts);
+	zx234290_regulator_read_register(ZX234290_REG_ADDR_BUCK_INT_MASK,  &buck_mask);
+	zDrvZx234290_LdoRstErr();
+
+	buck_sts &= ~buck_mask;
+	if(buck_sts & 0x4 ){
+		printk(KERN_ERR "zx234290_buck_faul_irq!!  ldo8 error~,  buckFail_irq=0x%x\n", buck_sts);
+		//val =0x01; //clear faul flag;
+
+		zx234290_set_ldo8_onoff(LDO_ENABLE_OFF);
+		zx234290_set_ldo8_onoff(LDO_ENABLE_ON);
+
+		//zx234290_regulator_write_register(ZX234290_REG_ADDR_BUCK_FAULT_STATUS, 0x1, 0x1);
+
+		zx234290_notifier_call_chain(ZX234290_INT_BUCK_FAUL,NULL);
+		WARN_ON(1);
+	}
+	else if(buck_sts & 0x8 )//ldo6
+	{
+		printk(KERN_ERR "zx234290_buck_faul_irq!!  ldo6 error~,  buckFail_irq=0x%x, clear irq\n", buck_sts);
+		//zx234290_regulator_write_register(ZX234290_REG_ADDR_BUCK_FAULT_STATUS, 0x1, 0x1);
+		WARN_ON(1);
+	}
+	else if(buck_sts & 0x80 )//BUCK4
+	{
+		printk(KERN_ERR "zx234290_buck_faul_irq!!  BUCK4 error~,  buckFail_irq=0x%x, clear irq\n", buck_sts);
+		//zx234290_regulator_write_register(ZX234290_REG_ADDR_BUCK_FAULT_STATUS, 0x1, 0x1);
+		WARN_ON(1);
+	}
+	else
+	{
+		printk(KERN_ERR "zx234290_buck_faul_irq!! buckFail_irq=0x%x,BUG()!!\n", buck_sts);
+		BUG();
+	}
+	return 0;
+}
+
+static irqreturn_t zx234290_ldo_faul_irq(int irq, void *id)
+{
+	u8 ldo_sts=0, ldo_mask=0;
+	zx234290_regulator_read_register( ZX234290_REG_ADDR_LDO_FAULT_STATUS, &ldo_sts);
+	zx234290_regulator_read_register( ZX234290_REG_ADDR_LDO_INT_MASK, &ldo_mask);
+	zDrvZx234290_LdoRstErr();
+
+	ldo_sts &= ~ldo_mask;
+	if(ldo_sts){
+		printk(KERN_ERR "zx234290_ldo_faul_irq!! LDOFail_irq=0x%x,BUG()!!\n", ldo_sts);
+		BUG();
+	}
+	return 0;
+}
+
+int zx234290_regulator_error_irq_request(struct zx234290 *zx234290)
+{
+	int ret = -1;
+	//zx234290_rtc.zx234290->irq_base = PMIC_INT_START;
+	//printk(KERN_INFO"zx234290_PMU_regulator_init, BUCKFail_irq=%d,LDOFail_irq=%d\n", zx234290->irq_base + ZX234290_INT_BUCK_FAUL,zx234290->irq_base + ZX234290_INT_LDO_FAUL);
+	ret = request_threaded_irq(zx234290->irq_base + ZX234290_INT_BUCK_FAUL, NULL, zx234290_buck_faul_irq,	0, "zx234290-regulator buck faul", zx234290);
+	if (ret)	{
+		printk(KERN_ERR "buck faul IRQ%d error %d\n", zx234290->irq_base + ZX234290_INT_BUCK_FAUL, ret);
+		goto err_buck_irq;
+	}
+	ret = request_threaded_irq(zx234290->irq_base + ZX234290_INT_LDO_FAUL, NULL, zx234290_ldo_faul_irq,	0, "zx234290-regulator ldo faul", zx234290);
+	if (ret)	{
+		printk(KERN_ERR"ldo faul IRQ%d error %d\n", zx234290->irq_base + ZX234290_INT_LDO_FAUL, ret);
+		goto err_ldo_irq;
+	}
+
+	return 0;
+
+	err_ldo_irq:
+		free_irq(zx234290->irq_base + ZX234290_INT_LDO_FAUL, zx234290);
+	err_buck_irq:
+		free_irq(zx234290->irq_base + ZX234290_INT_BUCK_FAUL, zx234290);
+		return ret;
+}
+#endif
+