Merge commit 'AU_LINUX_ANDROID_ICS.04.00.04.00.126' into msm-3.4

AU_LINUX_ANDROID_ICS.04.00.04.00.126 from msm-3.0.
First parent is from google/android-3.4.

* commit 'AU_LINUX_ANDROID_ICS.04.00.04.00.126': (8712 commits)
  PRNG: Device tree entry for qrng device.
  vidc:1080p: Set video core timeout value for Thumbnail mode
  msm: sps: improve the debugging support in SPS driver
  board-8064 msm: Overlap secure and non secure video firmware heaps.
  msm: clock: Add handoff ops for 7x30 and copper XO clocks
  msm_fb: display: Wait for external vsync before DTV IOMMU unmap
  msm: Fix ciruclar dependency in debug UART settings
  msm: gdsc: Add GDSC regulator driver for msm-copper
  defconfig: Enable Mobicore Driver.
  mobicore: Add mobicore driver.
  mobicore: rename variable to lower case.
  mobicore: rename folder.
  mobicore: add makefiles
  mobicore: initial import of kernel driver
  ASoC: msm: Add SLIMBUS_2_RX CPU DAI
  board-8064-gpio: Update FUNC for EPM SPI CS
  msm_fb: display: Remove chicken bit config during video playback
  mmc: msm_sdcc: enable the sanitize capability
  msm-fb: display: lm2 writeback support on mpq platfroms
  msm_fb: display: Disable LVDS phy & pll during panel off
  ...

Signed-off-by: Steve Muckle <smuckle@codeaurora.org>
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 11e4438..7c61bf3 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -131,6 +131,38 @@
 	  This driver can also be built as a module.  If so, the module
 	  will be called tps65010.
 
+config TPS65023
+        tristate "TPS65023 Power Management chip"
+        depends on I2C && ARCH_MSM_SCORPION && !MSM_SMP
+        default y if I2C && ARCH_MSM_SCORPION && !MSM_SMP
+        help
+          Say yes here for Qualcomm QSD chips. The TI PMIC is used by the
+          QSD8x50 series of chips for power management.
+
+config PMIC8058
+	tristate "PMIC8058 Power Management chip"
+	depends on MSM_SSBI
+	select MFD_CORE
+	select MFD_PM8XXX
+	help
+	  Say yes here for Qualcomm PM8058 chip.
+
+config PMIC8901
+	tristate "PMIC8901 Power Management chip"
+	depends on MSM_SSBI
+	select MFD_CORE
+	select MFD_PM8XXX
+	help
+	  Say yes here for Qualcomm PM8901 chip.
+
+config MARIMBA_TSADC
+	tristate "Support for Marimba Touchscreen ADC"
+	depends on MARIMBA_CORE && ARCH_MSM7X30
+	default y if MARIMBA_CORE
+	help
+	  Say yes here if you want to include support for TSADC in the
+	  Qualcomm Marimba chip.
+
 config TPS6507X
 	tristate "TPS6507x Power Management / Touch Screen chips"
 	select MFD_CORE
@@ -214,6 +246,33 @@
 	  and other features that are often used in portable devices like
 	  cell phones and PDAs.
 
+config MARIMBA_CORE
+	tristate "Marimba Core"
+	depends on I2C && (ARCH_MSM7X30 || ARCH_MSM8X60 || ARCH_MSM7X27A)
+	default n
+	help
+	  Enables the Marimba Core driver. The core driver provides
+	  read/write capability to registers which are part of the
+	  marimba core.
+	  This driver dynamically detects the SoC and works for both
+	  Marimba and Bahama Chip.
+
+config MARIMBA_CODEC
+	tristate "Marimba Codec"
+	depends on MARIMBA_CORE
+	default n
+	help
+	 This driver programs Marimba Wideband Codec for input/output of
+	 audio signal.
+
+config TIMPANI_CODEC
+	tristate "Timpani Codec"
+	depends on MARIMBA_CORE
+	default n
+	help
+	 This driver programs Timpani Wideband Codec for input/output of
+	 audio signal.
+
 config TWL4030_CORE
 	bool "Texas Instruments TWL4030/TWL5030/TWL6030/TPS659x0 Support"
 	depends on I2C=y && GENERIC_HARDIRQS
@@ -822,6 +881,51 @@
 	  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_PM8821_CORE
+	tristate "Qualcomm PM8821 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 PM8821 PMIC chip.
+
+	  This is required if your board has a PM8821 and uses its features,
+	  such as: MPPs, and interrupts.
+
+	  Say M here if you want to include support for PM8821 chip as a module.
+	  This will build a module called "pm8821-core".
+
+config MFD_PM8018_CORE
+	tristate "Qualcomm PM8018 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 PM8018 PMIC chip.
+
+	  This is required if your board has a PM8018 and uses its features,
+	  such as: MPPs, GPIOs, regulators, interrupts, and PWM.
+
+	  Say M here if you want to include support for PM8018 chip as a module.
+	  This will build a module called "pm8018-core".
+
+config MFD_PM8038_CORE
+	tristate "Qualcomm PM8038 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 PM8038 PMIC chip.
+
+	  This is required if your board has a PM8038 and uses its features,
+	  such as: MPPs, GPIOs, regulators, interrupts, and PWM.
+
+	  Say M here if you want to include support for PM8038 chip as a module.
+	  This will build a module called "pm8038-core".
+
 config MFD_PM8XXX_IRQ
 	bool "Support for Qualcomm PM8xxx IRQ features"
 	depends on MFD_PM8XXX
@@ -866,6 +970,71 @@
 	  Passage) chip. This chip embeds audio, battery, GPIO, etc.
 	  devices used in Intel Medfield platforms.
 
+config MFD_PM8XXX_DEBUG
+	tristate "Qualcomm PM8xxx debugfs support"
+	depends on MFD_PM8XXX && DEBUG_FS
+	default y if MFD_PM8XXX
+	help
+	  This driver provides a debugfs interface to the SSBI registers on
+	  Qualcomm PM 8xxx PMIC chips.  It allows for reads and writes to
+	  arbitrary addresses.  Writes are blocking so values are guaranteed to
+	  be set into hardware registers upon return.
+
+config MFD_PM8XXX_PWM
+	tristate "Support for Qualcomm PM8xxx PWM feature"
+	depends on MFD_PM8XXX
+	default y if MFD_PM8XXX
+	help
+	  This is the Pulse Width Modulation (PWM) driver for Qualcomm
+	  PM 8xxx PMIC chips. It can drive 8 channels of PWM output, and
+	  has a lookup table with size of 64 to be shared by any of the
+	  8 channels.
+
+config MFD_PM8XXX_MISC
+	tristate "Support for Qualcomm PM8xxx miscellaneous APIs"
+	depends on MFD_PM8XXX
+	default y if MFD_PM8XXX
+	help
+	  This driver implements several miscellaneous APIs that may be needed
+	  in order to control the PM8XXX PMIC chip.
+
+config MFD_PM8XXX_SPK
+	tristate "Support for Qualcomm PM8xxx speaker APIs"
+	depends on MFD_PM8XXX
+	help
+	  This driver implements several external speaker amplifier APIs that
+	  may be needed in order to control the PM8XXX PMIC chip.
+
+config MFD_PM8XXX_BATT_ALARM
+	tristate "Support for Qualcomm PM8xxx battery voltage alarm"
+	depends on MFD_PM8XXX
+	help
+	  This driver provides a means monitor battery under and over-voltage
+	  conditions.  An upper and/or lower threshold can be specified for
+	  normal operation.  A wakeable interrupt is triggered when the battery
+	  voltage leaves the accepatable range which then calls a notifier call
+	  chain.
+
+config WCD9304_CODEC
+        tristate "WCD9304 Codec"
+        select SLIMBUS
+        select MFD_CORE
+        default n
+        help
+          Enables the WCD9304 core driver. The core driver provides
+          read/write capability to registers which are part of the
+          WCD9304 core and gives the ability to use the WCD9304 codec.
+
+config WCD9310_CODEC
+        tristate "WCD9310 Codec"
+        select SLIMBUS
+        select MFD_CORE
+        default n
+        help
+          Enables the WCD9310 core driver. The core driver provides
+          read/write capability to registers which are part of the
+          WCD9310 core and gives the ability to use the WCD9310 codec.
+
 config MFD_RC5T583
 	bool "Ricoh RC5T583 Power Management system device"
 	depends on I2C=y && GENERIC_HARDIRQS
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 05fa538..e81e4b1 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -44,12 +44,26 @@
 obj-$(CONFIG_MFD_TPS65912)	+= tps65912.o
 obj-$(CONFIG_MFD_TPS65912_I2C)	+= tps65912-i2c.o
 obj-$(CONFIG_MFD_TPS65912_SPI)  += tps65912-spi.o
+obj-$(CONFIG_MARIMBA_CODEC)     += marimba-codec.o
 obj-$(CONFIG_MENELAUS)		+= menelaus.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_MARIMBA_CORE)	+= marimba-core.o
+obj-$(CONFIG_MARIMBA_TSADC)	+= marimba-tsadc.o
+obj-$(CONFIG_TPS65023)		+= tps65023.o
+
+obj-$(CONFIG_TIMPANI_CODEC)	+= timpani-codec.o
+
+ifdef CONFIG_TIMPANI_CODEC
+obj-$(CONFIG_TIMPANI_CODEC) += msm-adie-codec.o
+else ifdef CONFIG_MARIMBA_CODEC
+obj-$(CONFIG_MARIMBA_CODEC) += msm-adie-codec.o
+endif
+
 obj-$(CONFIG_TWL6030_PWM)	+= twl6030-pwm.o
 obj-$(CONFIG_TWL6040_CORE)	+= twl6040-core.o twl6040-irq.o
 
@@ -64,6 +78,9 @@
 obj-$(CONFIG_MCP_UCB1200)	+= ucb1x00-core.o
 obj-$(CONFIG_MCP_UCB1200_TS)	+= ucb1x00-ts.o
 
+obj-$(CONFIG_WCD9310_CODEC)       += wcd9xxx-core.o wcd9xxx-irq.o wcd9xxx-slimslave.o
+obj-$(CONFIG_WCD9304_CODEC)       += wcd9xxx-core.o wcd9xxx-irq.o wcd9xxx-slimslave.o
+
 ifeq ($(CONFIG_SA1100_ASSABET),y)
 obj-$(CONFIG_MCP_UCB1200)	+= ucb1x00-assabet.o
 endif
@@ -107,12 +124,22 @@
 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_PMIC8058)		+= pmic8058.o
+obj-$(CONFIG_PMIC8901)		+= pmic8901.o
+obj-$(CONFIG_MFD_PM8921_CORE) 	+= pm8921-core.o
+obj-$(CONFIG_MFD_PM8821_CORE) 	+= pm8821-core.o
+obj-$(CONFIG_MFD_PM8018_CORE) 	+= pm8018-core.o
+obj-$(CONFIG_MFD_PM8038_CORE) 	+= pm8038-core.o
+obj-$(CONFIG_MFD_PM8XXX_IRQ) 	+= pm8xxx-irq.o
+obj-$(CONFIG_MFD_PM8XXX_DEBUG) 	+= pm8xxx-debug.o
+obj-$(CONFIG_MFD_PM8XXX_PWM) 	+= pm8xxx-pwm.o
+obj-$(CONFIG_MFD_PM8XXX_MISC) 	+= pm8xxx-misc.o
+obj-$(CONFIG_MFD_PM8XXX_SPK) 	+= pm8xxx-spk.o
+obj-$(CONFIG_MFD_PM8XXX_BATT_ALARM) 	+= pm8xxx-batt-alarm.o
 obj-$(CONFIG_MFD_ANATOP)	+= anatop-mfd.o
diff --git a/drivers/mfd/marimba-codec.c b/drivers/mfd/marimba-codec.c
new file mode 100644
index 0000000..6416e0a
--- /dev/null
+++ b/drivers/mfd/marimba-codec.c
@@ -0,0 +1,963 @@
+/* Copyright (c) 2009-2010, 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.
+ *
+ */
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/msm-adie-codec.h>
+#include <linux/mfd/marimba.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <linux/string.h>
+
+#define MARIMBA_CDC_RX_CTL 0x81
+#define MARIMBA_CDC_RX_CTL_ST_EN_MASK 0x20
+#define MARIMBA_CDC_RX_CTL_ST_EN_SHFT 0x5
+#define MARIMBA_CODEC_CDC_LRXG     0x84
+#define MARIMBA_CODEC_CDC_RRXG     0x85
+#define MARIMBA_CODEC_CDC_LTXG     0x86
+#define MARIMBA_CODEC_CDC_RTXG     0x87
+
+#define MAX_MDELAY_US 2000
+#define MIN_MDELAY_US 1000
+
+struct adie_codec_path {
+	struct adie_codec_dev_profile *profile;
+	struct adie_codec_register_image img;
+	u32 hwsetting_idx;
+	u32 stage_idx;
+	u32 curr_stage;
+};
+
+static struct adie_codec_register adie_codec_tx_regs[] = {
+	{ 0x04, 0xc0, 0x8C },
+	{ 0x0D, 0xFF, 0x00 },
+	{ 0x0E, 0xFF, 0x00 },
+	{ 0x0F, 0xFF, 0x00 },
+	{ 0x10, 0xF8, 0x68 },
+	{ 0x11, 0xFE, 0x00 },
+	{ 0x12, 0xFE, 0x00 },
+	{ 0x13, 0xFF, 0x58 },
+	{ 0x14, 0xFF, 0x00 },
+	{ 0x15, 0xFE, 0x00 },
+	{ 0x16, 0xFF, 0x00 },
+	{ 0x1A, 0xFF, 0x00 },
+	{ 0x80, 0x01, 0x00 },
+	{ 0x82, 0x7F, 0x18 },
+	{ 0x83, 0x1C, 0x00 },
+	{ 0x86, 0xFF, 0xAC },
+	{ 0x87, 0xFF, 0xAC },
+	{ 0x89, 0xFF, 0xFF },
+	{ 0x8A, 0xF0, 0x30 }
+};
+
+static struct adie_codec_register adie_codec_rx_regs[] = {
+	{ 0x23, 0xF8, 0x00 },
+	{ 0x24, 0x6F, 0x00 },
+	{ 0x25, 0x7F, 0x00 },
+	{ 0x26, 0xFC, 0x00 },
+	{ 0x28, 0xFE, 0x00 },
+	{ 0x29, 0xFE, 0x00 },
+	{ 0x33, 0xFF, 0x00 },
+	{ 0x34, 0xFF, 0x00 },
+	{ 0x35, 0xFC, 0x00 },
+	{ 0x36, 0xFE, 0x00 },
+	{ 0x37, 0xFE, 0x00 },
+	{ 0x38, 0xFE, 0x00 },
+	{ 0x39, 0xF0, 0x00 },
+	{ 0x3A, 0xFF, 0x0A },
+	{ 0x3B, 0xFC, 0xAC },
+	{ 0x3C, 0xFC, 0xAC },
+	{ 0x3D, 0xFF, 0x55 },
+	{ 0x3E, 0xFF, 0x55 },
+	{ 0x3F, 0xCF, 0x00 },
+	{ 0x40, 0x3F, 0x00 },
+	{ 0x41, 0x3F, 0x00 },
+	{ 0x42, 0xFF, 0x00 },
+	{ 0x43, 0xF7, 0x00 },
+	{ 0x43, 0xF7, 0x00 },
+	{ 0x43, 0xF7, 0x00 },
+	{ 0x43, 0xF7, 0x00 },
+	{ 0x44, 0xF7, 0x00 },
+	{ 0x45, 0xFF, 0x00 },
+	{ 0x46, 0xFF, 0x00 },
+	{ 0x47, 0xF7, 0x00 },
+	{ 0x48, 0xF7, 0x00 },
+	{ 0x49, 0xFF, 0x00 },
+	{ 0x4A, 0xFF, 0x00 },
+	{ 0x80, 0x02, 0x00 },
+	{ 0x81, 0xFF, 0x4C },
+	{ 0x83, 0x23, 0x00 },
+	{ 0x84, 0xFF, 0xAC },
+	{ 0x85, 0xFF, 0xAC },
+	{ 0x88, 0xFF, 0xFF },
+	{ 0x8A, 0x0F, 0x03 },
+	{ 0x8B, 0xFF, 0xAC },
+	{ 0x8C, 0x03, 0x01 },
+	{ 0x8D, 0xFF, 0x00 },
+	{ 0x8E, 0xFF, 0x00 }
+};
+
+static struct adie_codec_register adie_codec_lb_regs[] = {
+	{ 0x2B, 0x8F, 0x02 },
+	{ 0x2C, 0x8F, 0x02 }
+};
+
+struct adie_codec_state {
+	struct adie_codec_path path[ADIE_CODEC_MAX];
+	u32 ref_cnt;
+	struct marimba *pdrv_ptr;
+	struct marimba_codec_platform_data *codec_pdata;
+	struct mutex lock;
+};
+
+static struct adie_codec_state adie_codec;
+
+/* Array containing write details of Tx and RX Digital Volume
+   Tx and Rx and both the left and right channel use the same data
+*/
+u8 adie_codec_rx_tx_dig_vol_data[] = {
+	0x81, 0x82, 0x83, 0x84,
+	0x85, 0x86, 0x87, 0x88,
+	0x89, 0x8a, 0x8b, 0x8c,
+	0x8d, 0x8e, 0x8f, 0x90,
+	0x91, 0x92, 0x93, 0x94,
+	0x95, 0x96, 0x97, 0x98,
+	0x99, 0x9a, 0x9b, 0x9c,
+	0x9d, 0x9e, 0x9f, 0xa0,
+	0xa1, 0xa2, 0xa3, 0xa4,
+	0xa5, 0xa6, 0xa7, 0xa8,
+	0xa9, 0xaa, 0xab, 0xac,
+	0xad, 0xae, 0xaf, 0xb0,
+	0xb1, 0xb2, 0xb3, 0xb4,
+	0xb5, 0xb6, 0xb7, 0xb8,
+	0xb9, 0xba, 0xbb, 0xbc,
+	0xbd, 0xbe, 0xbf, 0xc0,
+	0xc1, 0xc2, 0xc3, 0xc4,
+	0xc5, 0xc6, 0xc7, 0xc8,
+	0xc9, 0xca, 0xcb, 0xcc,
+	0xcd, 0xce, 0xcf, 0xd0,
+	0xd1, 0xd2, 0xd3, 0xd4,
+	0xd5, 0xd6, 0xd7, 0xd8,
+	0xd9, 0xda, 0xdb, 0xdc,
+	0xdd, 0xde, 0xdf, 0xe0,
+	0xe1, 0xe2, 0xe3, 0xe4,
+	0xe5, 0xe6, 0xe7, 0xe8,
+	0xe9, 0xea, 0xeb, 0xec,
+	0xed, 0xee, 0xf0, 0xf1,
+	0xf2, 0xf3, 0xf4, 0xf5,
+	0xf6, 0xf7, 0xf8, 0xf9,
+	0xfa, 0xfb, 0xfc, 0xfd,
+	0xfe, 0xff, 0x00, 0x01,
+	0x02, 0x03, 0x04, 0x05,
+	0x06, 0x07, 0x08, 0x09,
+	0x0a, 0x0b, 0x0c, 0x0d,
+	0x0e, 0x0f, 0x10, 0x11,
+	0x12, 0x13, 0x14, 0x15,
+	0x16, 0x17, 0x18, 0x19,
+	0x1a, 0x1b, 0x1c, 0x1d,
+	0x1e, 0x1f, 0x20, 0x21,
+	0x22, 0x23, 0x24, 0x25,
+	0x26, 0x27, 0x28, 0x29,
+	0x2a, 0x2b, 0x2c, 0x2d,
+	0x2e, 0x2f, 0x30, 0x31,
+	0x32, 0x33, 0x34, 0x35,
+	0x36, 0x37, 0x38, 0x39,
+	0x3a, 0x3b, 0x3c, 0x3d,
+	0x3e, 0x3f, 0x40, 0x41,
+	0x42, 0x43, 0x44, 0x45,
+	0x46, 0x47, 0x48, 0x49,
+	0x4a, 0x4b, 0x4c, 0x4d,
+	0x4e, 0x4f, 0x50, 0x51,
+	0x52, 0x53, 0x54, 0x55,
+	0x56, 0x57, 0x58, 0x59,
+	0x5a, 0x5b, 0x5c, 0x5d,
+	0x5e, 0x5f, 0x60, 0x61,
+	0x62, 0x63, 0x64, 0x65,
+	0x66, 0x67, 0x68, 0x69,
+	0x6a, 0x6b, 0x6c, 0x6d,
+	0x6e, 0x6f, 0x70, 0x71,
+	0x72, 0x73, 0x74, 0x75,
+	0x76, 0x77, 0x78, 0x79,
+	0x7a, 0x7b, 0x7c, 0x7d,
+	0x7e, 0x7f
+};
+
+enum adie_vol_type {
+	ADIE_CODEC_RX_DIG_VOL,
+	ADIE_CODEC_TX_DIG_VOL,
+	ADIE_CODEC_VOL_TYPE_MAX
+};
+
+struct adie_codec_ch_vol_cntrl {
+	u8 codec_reg;
+	u8 codec_mask;
+	u8 *vol_cntrl_data;
+};
+
+struct adie_codec_vol_cntrl_data {
+
+	enum adie_vol_type vol_type;
+
+	/* Jump length used while doing writes in incremental fashion */
+	u32 jump_length;
+	s32 min_mb;		/* Min Db applicable to the vol control */
+	s32 max_mb;		/* Max Db applicable to the vol control */
+	u32 step_in_mb;
+	u32 steps;		/* No of steps allowed for this vol type */
+
+	struct adie_codec_ch_vol_cntrl *ch_vol_cntrl_info;
+};
+
+static struct adie_codec_ch_vol_cntrl adie_codec_rx_vol_cntrl[] = {
+	{MARIMBA_CODEC_CDC_LRXG, 0xff, adie_codec_rx_tx_dig_vol_data},
+	{MARIMBA_CODEC_CDC_RRXG, 0xff, adie_codec_rx_tx_dig_vol_data}
+};
+
+static struct adie_codec_ch_vol_cntrl adie_codec_tx_vol_cntrl[] = {
+	{MARIMBA_CODEC_CDC_LTXG, 0xff, adie_codec_rx_tx_dig_vol_data},
+	{MARIMBA_CODEC_CDC_RTXG, 0xff, adie_codec_rx_tx_dig_vol_data}
+};
+
+static struct adie_codec_vol_cntrl_data adie_codec_vol_cntrl[] = {
+	{ADIE_CODEC_RX_DIG_VOL, 5100, -12700, 12700, 100, 255,
+	 adie_codec_rx_vol_cntrl},
+
+	{ADIE_CODEC_TX_DIG_VOL, 5100, -12700, 12700, 100, 255,
+	 adie_codec_tx_vol_cntrl}
+};
+
+static int adie_codec_write(u8 reg, u8 mask, u8 val)
+{
+	int rc;
+
+	rc = marimba_write_bit_mask(adie_codec.pdrv_ptr, reg,  &val, 1, mask);
+	if (IS_ERR_VALUE(rc)) {
+		pr_err("%s: fail to write reg %x\n", __func__, reg);
+		return -EIO;
+	}
+
+	pr_debug("%s: write reg %x val %x\n", __func__, reg, val);
+
+	return 0;
+}
+
+static int adie_codec_read(u8 reg, u8 *val)
+{
+	return marimba_read(adie_codec.pdrv_ptr, reg, val, 1);
+}
+
+static int adie_codec_read_dig_vol(enum adie_vol_type vol_type, u32 chan_index,
+				   u32 *cur_index)
+{
+	u32 counter;
+	u32 size;
+	u8 reg, mask, cur_val;
+	int rc;
+
+	reg =
+	    adie_codec_vol_cntrl[vol_type].
+	    ch_vol_cntrl_info[chan_index].codec_reg;
+
+	mask =
+	    adie_codec_vol_cntrl[vol_type].
+	    ch_vol_cntrl_info[chan_index].codec_mask;
+
+	rc = marimba_read(adie_codec.pdrv_ptr, reg, &cur_val, 1);
+
+	if (IS_ERR_VALUE(rc)) {
+		pr_err("%s: fail to read reg %x\n", __func__, reg);
+		return -EIO;
+	}
+
+	cur_val = cur_val & mask;
+
+	pr_debug("%s: reg 0x%x  mask 0x%x  reg_value = 0x%x"
+		"vol_type = %d\n", __func__, reg, mask, cur_val, vol_type);
+
+	size = adie_codec_vol_cntrl[vol_type].steps;
+
+	for (counter = 0; counter <= size; counter++) {
+
+		if (adie_codec_vol_cntrl[vol_type].ch_vol_cntrl_info
+		    [chan_index].vol_cntrl_data[counter] == cur_val) {
+			*cur_index = counter;
+			return 0;
+		}
+	}
+
+	pr_err("%s: could not find 0x%x in reg 0x%x values array\n",
+			__func__, cur_val, reg);
+
+	return -EINVAL;;
+}
+
+static int adie_codec_set_dig_vol(enum adie_vol_type vol_type, u32 chan_index,
+				  u32 cur_index, u32 target_index)
+{
+	u32 count;
+	u8 reg, mask, val;
+	u32 i;
+	u32 index;
+	u32 index_jump;
+
+	int rc;
+
+	index_jump = adie_codec_vol_cntrl[vol_type].jump_length;
+
+	reg =
+	    adie_codec_vol_cntrl[vol_type].
+	    ch_vol_cntrl_info[chan_index].codec_reg;
+
+	mask =
+	    adie_codec_vol_cntrl[vol_type].
+	    ch_vol_cntrl_info[chan_index].codec_mask;
+
+	/* compare the target index with current index */
+	if (cur_index < target_index) {
+
+		/* Volume is being increased loop and increase it in 4-5 steps
+		 */
+		count = ((target_index - cur_index) * 100 / index_jump);
+		index = cur_index;
+
+		for (i = 1; i <= count; i++) {
+			index = index + (int)(index_jump / 100);
+
+			val =
+			    adie_codec_vol_cntrl[vol_type].ch_vol_cntrl_info
+			    [chan_index].vol_cntrl_data[index];
+
+			pr_debug("%s: write reg %x val 0x%x\n",
+					__func__, reg, val);
+
+			rc = adie_codec_write(reg, mask, val);
+			if (rc < 0) {
+				pr_err("%s: write reg %x val 0x%x failed\n",
+					__func__, reg, val);
+				return rc;
+			}
+		}
+
+		/*do one final write to take it to the target index level */
+		val =
+		    adie_codec_vol_cntrl[vol_type].ch_vol_cntrl_info
+		    [chan_index].vol_cntrl_data[target_index];
+
+		pr_debug("%s: write reg %x val 0x%x\n", __func__, reg, val);
+
+		rc = adie_codec_write(reg, mask, val);
+
+		if (rc < 0) {
+			pr_err("%s: write reg %x val 0x%x failed\n",
+					__func__, reg, val);
+			return rc;
+		}
+
+	} else {
+
+		/* Volume is being decreased from the current setting */
+		index = cur_index;
+		/* loop and decrease it in 4-5 steps */
+		count = ((cur_index - target_index) * 100 / index_jump);
+
+		for (i = 1; i <= count; i++) {
+			index = index - (int)(index_jump / 100);
+
+			val =
+			    adie_codec_vol_cntrl[vol_type].ch_vol_cntrl_info
+			    [chan_index].vol_cntrl_data[index];
+
+			pr_debug("%s: write reg %x val 0x%x\n",
+					__func__, reg, val);
+
+			rc = adie_codec_write(reg, mask, val);
+			if (rc < 0) {
+				pr_err("%s: write reg %x val 0x%x failed\n",
+					__func__, reg, val);
+				return rc;
+			}
+		}
+
+		/* do one final write to take it to the target index level */
+		val =
+		    adie_codec_vol_cntrl[vol_type].ch_vol_cntrl_info
+		    [chan_index].vol_cntrl_data[target_index];
+
+		pr_debug("%s: write reg %x val 0x%x\n", __func__, reg, val);
+
+		rc = adie_codec_write(reg, mask, val);
+
+		if (rc < 0) {
+			pr_err("%s: write reg %x val 0x%x failed\n",
+					__func__, reg, val);
+			return rc;
+		}
+	}
+	return 0;
+}
+
+static int marimba_adie_codec_set_device_digital_volume(
+		struct adie_codec_path *path_ptr,
+		u32 num_channels, u32 vol_percentage /* in percentage */)
+{
+	enum adie_vol_type vol_type;
+	s32 milli_bel;
+	u32 chan_index;
+	u32 step_index;
+	u32 cur_step_index = 0;
+
+	if (!path_ptr  || (path_ptr->curr_stage !=
+				ADIE_CODEC_DIGITAL_ANALOG_READY)) {
+		pr_info("%s: Marimba codec not ready for volume control\n",
+		       __func__);
+		return  -EPERM;
+	}
+
+	if (num_channels > 2) {
+		pr_err("%s: Marimba codec only supports max two channels\n",
+		       __func__);
+		return -EINVAL;
+	}
+
+	if (path_ptr->profile->path_type == ADIE_CODEC_RX)
+		vol_type = ADIE_CODEC_RX_DIG_VOL;
+	else if (path_ptr->profile->path_type == ADIE_CODEC_TX)
+		vol_type = ADIE_CODEC_TX_DIG_VOL;
+	else {
+		pr_err("%s: invalid device data neither RX nor TX\n",
+				__func__);
+		return -EINVAL;
+	}
+
+	milli_bel = ((adie_codec_vol_cntrl[vol_type].max_mb -
+			adie_codec_vol_cntrl[vol_type].min_mb) *
+			vol_percentage) / 100;
+
+	milli_bel = adie_codec_vol_cntrl[vol_type].min_mb + milli_bel;
+
+	pr_debug("%s: milli bell = %d vol_type = %d vol_percentage = %d"
+		 " num_cha =  %d \n",
+		 __func__, milli_bel, vol_type, vol_percentage, num_channels);
+
+
+	step_index = ((milli_bel
+		       - adie_codec_vol_cntrl[vol_type].min_mb
+		       + (adie_codec_vol_cntrl[vol_type].step_in_mb / 2))
+		      / adie_codec_vol_cntrl[vol_type].step_in_mb);
+
+
+	for (chan_index = 0; chan_index < num_channels; chan_index++) {
+		adie_codec_read_dig_vol(vol_type, chan_index, &cur_step_index);
+
+		pr_debug("%s: cur_step_index = %u  current vol = 0x%x\n",
+				__func__, cur_step_index,
+			adie_codec_vol_cntrl[vol_type].ch_vol_cntrl_info
+			[chan_index].vol_cntrl_data[cur_step_index]);
+
+		pr_debug("%s: step index = %u  new volume = 0x%x\n",
+		 __func__, step_index,
+		 adie_codec_vol_cntrl[vol_type].ch_vol_cntrl_info
+		 [chan_index].vol_cntrl_data[step_index]);
+
+		adie_codec_set_dig_vol(vol_type, chan_index, cur_step_index,
+				       step_index);
+
+	}
+	return 0;
+}
+
+static int marimba_adie_codec_setpath(struct adie_codec_path *path_ptr,
+					u32 freq_plan, u32 osr)
+{
+	int rc = 0;
+	u32 i, freq_idx = 0, freq = 0;
+
+	if ((path_ptr->curr_stage != ADIE_CODEC_DIGITAL_OFF) &&
+		(path_ptr->curr_stage != ADIE_CODEC_FLASH_IMAGE)) {
+		rc = -EBUSY;
+		goto error;
+	}
+
+	for (i = 0; i < path_ptr->profile->setting_sz; i++) {
+		if (path_ptr->profile->settings[i].osr == osr) {
+			if (path_ptr->profile->settings[i].freq_plan >=
+				freq_plan) {
+				if (freq == 0) {
+					freq = path_ptr->profile->settings[i].
+								freq_plan;
+					freq_idx = i;
+				} else if (path_ptr->profile->settings[i].
+					freq_plan < freq) {
+					freq = path_ptr->profile->settings[i].
+								freq_plan;
+					freq_idx = i;
+				}
+			}
+		}
+	}
+
+	if (freq_idx >= path_ptr->profile->setting_sz)
+		rc = -ENODEV;
+	else {
+		path_ptr->hwsetting_idx = freq_idx;
+		path_ptr->stage_idx = 0;
+	}
+
+error:
+	return rc;
+}
+
+static u32 marimba_adie_codec_freq_supported(
+				struct adie_codec_dev_profile *profile,
+				u32 requested_freq)
+{
+	u32 i, rc = -EINVAL;
+
+	for (i = 0; i < profile->setting_sz; i++) {
+		if (profile->settings[i].freq_plan >= requested_freq) {
+			rc = 0;
+			break;
+		}
+	}
+	return rc;
+}
+
+static int marimba_adie_codec_enable_sidetone(
+				struct adie_codec_path *rx_path_ptr,
+				u32 enable)
+{
+	int rc = 0;
+
+	pr_debug("%s()\n", __func__);
+
+	mutex_lock(&adie_codec.lock);
+
+	if (!rx_path_ptr || &adie_codec.path[ADIE_CODEC_RX] != rx_path_ptr) {
+		pr_err("%s: invalid path pointer\n", __func__);
+		rc = -EINVAL;
+		goto error;
+	} else if (rx_path_ptr->curr_stage !=
+		ADIE_CODEC_DIGITAL_ANALOG_READY) {
+		pr_err("%s: bad state\n", __func__);
+		rc = -EPERM;
+		goto error;
+	}
+
+	if (enable)
+		rc = adie_codec_write(MARIMBA_CDC_RX_CTL,
+		MARIMBA_CDC_RX_CTL_ST_EN_MASK,
+		(0x1 << MARIMBA_CDC_RX_CTL_ST_EN_SHFT));
+	else
+		rc = adie_codec_write(MARIMBA_CDC_RX_CTL,
+		MARIMBA_CDC_RX_CTL_ST_EN_MASK, 0);
+
+error:
+	mutex_unlock(&adie_codec.lock);
+	return rc;
+}
+
+static void adie_codec_reach_stage_action(struct adie_codec_path *path_ptr,
+						u32 stage)
+{
+	u32 iter;
+	struct adie_codec_register *reg_info;
+
+	if (stage == ADIE_CODEC_FLASH_IMAGE) {
+		/* perform reimage */
+		for (iter = 0; iter < path_ptr->img.img_sz; iter++) {
+			reg_info = &path_ptr->img.regs[iter];
+			adie_codec_write(reg_info->reg,
+			reg_info->mask, reg_info->val);
+		}
+	}
+}
+
+static int marimba_adie_codec_proceed_stage(struct adie_codec_path *path_ptr,
+						u32 state)
+{
+	int rc = 0, loop_exit = 0;
+	struct adie_codec_action_unit *curr_action;
+	struct adie_codec_hwsetting_entry *setting;
+	u8 reg, mask, val;
+
+	mutex_lock(&adie_codec.lock);
+	setting = &path_ptr->profile->settings[path_ptr->hwsetting_idx];
+	while (!loop_exit) {
+		curr_action = &setting->actions[path_ptr->stage_idx];
+		switch (curr_action->type) {
+		case ADIE_CODEC_ACTION_ENTRY:
+			ADIE_CODEC_UNPACK_ENTRY(curr_action->action,
+			reg, mask, val);
+			adie_codec_write(reg, mask, val);
+			break;
+		case ADIE_CODEC_ACTION_DELAY_WAIT:
+			if (curr_action->action > MAX_MDELAY_US)
+				msleep(curr_action->action/1000);
+			else if (curr_action->action < MIN_MDELAY_US)
+				udelay(curr_action->action);
+			else
+				mdelay(curr_action->action/1000);
+			break;
+		case ADIE_CODEC_ACTION_STAGE_REACHED:
+			adie_codec_reach_stage_action(path_ptr,
+				curr_action->action);
+			if (curr_action->action == state) {
+				path_ptr->curr_stage = state;
+				loop_exit = 1;
+			}
+			break;
+		default:
+			BUG();
+		}
+
+		path_ptr->stage_idx++;
+		if (path_ptr->stage_idx == setting->action_sz)
+			path_ptr->stage_idx = 0;
+	}
+	mutex_unlock(&adie_codec.lock);
+	return rc;
+}
+
+static void marimba_codec_bring_up(void)
+{
+	/* bring up sequence for Marimba codec core
+	 * ensure RESET_N = 0 and GDFS_CLAMP_EN=1 -
+	 * set GDFS_EN_FEW=1 then GDFS_EN_REST=1 then
+	 * GDFS_CLAMP_EN = 0 and finally RESET_N = 1
+	 * Marimba codec bring up should use the Marimba
+	 * slave address after which the codec slave
+	 * address can be used
+	 */
+
+	/* Bring up codec */
+	adie_codec_write(0xFF, 0xFF, 0x08);
+
+	/* set GDFS_EN_FEW=1 */
+	adie_codec_write(0xFF, 0xFF, 0x0a);
+
+	/* set GDFS_EN_REST=1 */
+	adie_codec_write(0xFF, 0xFF, 0x0e);
+
+	/* set RESET_N=1 */
+	adie_codec_write(0xFF, 0xFF, 0x07);
+
+	adie_codec_write(0xFF, 0xFF, 0x17);
+
+	/* enable band gap */
+	adie_codec_write(0x03, 0xFF, 0x04);
+
+	/* dither delay selected and dmic gain stage bypassed */
+	adie_codec_write(0x8F, 0xFF, 0x44);
+}
+
+static void marimba_codec_bring_down(void)
+{
+	adie_codec_write(0xFF, 0xFF, 0x07);
+	adie_codec_write(0xFF, 0xFF, 0x06);
+	adie_codec_write(0xFF, 0xFF, 0x0e);
+	adie_codec_write(0xFF, 0xFF, 0x08);
+	adie_codec_write(0x03, 0xFF, 0x00);
+}
+
+static int marimba_adie_codec_open(struct adie_codec_dev_profile *profile,
+	struct adie_codec_path **path_pptr)
+{
+	int rc = 0;
+
+	mutex_lock(&adie_codec.lock);
+
+	if (!profile || !path_pptr) {
+		rc = -EINVAL;
+		goto error;
+	}
+
+	if (adie_codec.path[profile->path_type].profile) {
+		rc = -EBUSY;
+		goto error;
+	}
+
+	if (!adie_codec.ref_cnt) {
+
+		if (adie_codec.codec_pdata &&
+				adie_codec.codec_pdata->marimba_codec_power) {
+
+			rc = adie_codec.codec_pdata->marimba_codec_power(1);
+			if (rc) {
+				pr_err("%s: could not power up marimba "
+						"codec\n", __func__);
+				goto error;
+			}
+		}
+		marimba_codec_bring_up();
+	}
+
+	adie_codec.path[profile->path_type].profile = profile;
+	*path_pptr = (void *) &adie_codec.path[profile->path_type];
+	adie_codec.ref_cnt++;
+	adie_codec.path[profile->path_type].hwsetting_idx = 0;
+	adie_codec.path[profile->path_type].curr_stage = ADIE_CODEC_FLASH_IMAGE;
+	adie_codec.path[profile->path_type].stage_idx = 0;
+
+
+error:
+
+	mutex_unlock(&adie_codec.lock);
+	return rc;
+}
+
+static int marimba_adie_codec_close(struct adie_codec_path *path_ptr)
+{
+	int rc = 0;
+
+	mutex_lock(&adie_codec.lock);
+
+	if (!path_ptr) {
+		rc = -EINVAL;
+		goto error;
+	}
+	if (path_ptr->curr_stage != ADIE_CODEC_DIGITAL_OFF)
+		adie_codec_proceed_stage(path_ptr, ADIE_CODEC_DIGITAL_OFF);
+
+	BUG_ON(!adie_codec.ref_cnt);
+
+	path_ptr->profile = NULL;
+	adie_codec.ref_cnt--;
+
+	if (!adie_codec.ref_cnt) {
+
+		marimba_codec_bring_down();
+
+		if (adie_codec.codec_pdata &&
+				adie_codec.codec_pdata->marimba_codec_power) {
+
+			rc = adie_codec.codec_pdata->marimba_codec_power(0);
+			if (rc) {
+				pr_err("%s: could not power down marimba "
+						"codec\n", __func__);
+				goto error;
+			}
+		}
+	}
+
+error:
+	mutex_unlock(&adie_codec.lock);
+	return rc;
+}
+
+static const struct adie_codec_operations marimba_adie_ops = {
+	.codec_id = MARIMBA_ID,
+	.codec_open = marimba_adie_codec_open,
+	.codec_close = marimba_adie_codec_close,
+	.codec_setpath = marimba_adie_codec_setpath,
+	.codec_proceed_stage = marimba_adie_codec_proceed_stage,
+	.codec_freq_supported = marimba_adie_codec_freq_supported,
+	.codec_enable_sidetone = marimba_adie_codec_enable_sidetone,
+	.codec_set_device_digital_volume =
+			marimba_adie_codec_set_device_digital_volume,
+};
+
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *debugfs_marimba_dent;
+static struct dentry *debugfs_peek;
+static struct dentry *debugfs_poke;
+static struct dentry *debugfs_power;
+
+static unsigned char read_data;
+
+static int codec_debug_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static int get_parameters(char *buf, long int *param1, int num_of_par)
+{
+	char *token;
+	int base, cnt;
+
+	token = strsep(&buf, " ");
+
+	for (cnt = 0; cnt < num_of_par; cnt++) {
+		if (token != NULL) {
+			if ((token[1] == 'x') || (token[1] == 'X'))
+				base = 16;
+			else
+				base = 10;
+
+			if (strict_strtoul(token, base, &param1[cnt]) != 0)
+				return -EINVAL;
+
+			token = strsep(&buf, " ");
+			}
+		else
+			return -EINVAL;
+	}
+	return 0;
+}
+
+static ssize_t codec_debug_read(struct file *file, char __user *ubuf,
+				size_t count, loff_t *ppos)
+{
+	char lbuf[8];
+
+	snprintf(lbuf, sizeof(lbuf), "0x%x\n", read_data);
+	return simple_read_from_buffer(ubuf, count, ppos, lbuf, strlen(lbuf));
+}
+
+static ssize_t codec_debug_write(struct file *filp,
+	const char __user *ubuf, size_t cnt, loff_t *ppos)
+{
+	char *access_str = filp->private_data;
+	char lbuf[32];
+	int rc;
+	long int param[5];
+
+	if (cnt > sizeof(lbuf) - 1)
+		return -EINVAL;
+
+	rc = copy_from_user(lbuf, ubuf, cnt);
+	if (rc)
+		return -EFAULT;
+
+	lbuf[cnt] = '\0';
+
+	if (!strcmp(access_str, "power")) {
+		if (get_parameters(lbuf, param, 1) == 0) {
+			switch (param[0]) {
+			case 1:
+				adie_codec.codec_pdata->marimba_codec_power(1);
+				marimba_codec_bring_up();
+				break;
+			case 0:
+				marimba_codec_bring_down();
+				adie_codec.codec_pdata->marimba_codec_power(0);
+				break;
+			default:
+				rc = -EINVAL;
+				break;
+			}
+		} else
+			rc = -EINVAL;
+	} else if (!strcmp(access_str, "poke")) {
+		/* write */
+		rc = get_parameters(lbuf, param, 2);
+		if ((param[0] <= 0xFF) && (param[1] <= 0xFF) &&
+			(rc == 0))
+			adie_codec_write(param[0], 0xFF, param[1]);
+		else
+			rc = -EINVAL;
+	} else if (!strcmp(access_str, "peek")) {
+		/* read */
+		rc = get_parameters(lbuf, param, 1);
+		if ((param[0] <= 0xFF) && (rc == 0))
+			adie_codec_read(param[0], &read_data);
+		else
+			rc = -EINVAL;
+	}
+
+	if (rc == 0)
+		rc = cnt;
+	else
+		pr_err("%s: rc = %d\n", __func__, rc);
+
+	return rc;
+}
+
+static const struct file_operations codec_debug_ops = {
+	.open = codec_debug_open,
+	.write = codec_debug_write,
+	.read = codec_debug_read
+};
+#endif
+
+static int marimba_codec_probe(struct platform_device *pdev)
+{
+	int rc;
+
+	adie_codec.pdrv_ptr = platform_get_drvdata(pdev);
+	adie_codec.codec_pdata = pdev->dev.platform_data;
+
+	if (adie_codec.codec_pdata->snddev_profile_init)
+		adie_codec.codec_pdata->snddev_profile_init();
+
+	/* Register the marimba ADIE operations */
+	rc = adie_codec_register_codec_operations(&marimba_adie_ops);
+
+#ifdef CONFIG_DEBUG_FS
+	debugfs_marimba_dent = debugfs_create_dir("msm_adie_codec", 0);
+	if (!IS_ERR(debugfs_marimba_dent)) {
+		debugfs_peek = debugfs_create_file("peek",
+		S_IFREG | S_IRUGO, debugfs_marimba_dent,
+		(void *) "peek", &codec_debug_ops);
+
+		debugfs_poke = debugfs_create_file("poke",
+		S_IFREG | S_IRUGO, debugfs_marimba_dent,
+		(void *) "poke", &codec_debug_ops);
+
+		debugfs_power = debugfs_create_file("power",
+		S_IFREG | S_IRUGO, debugfs_marimba_dent,
+		(void *) "power", &codec_debug_ops);
+	}
+#endif
+	return rc;
+}
+
+static struct platform_driver marimba_codec_driver = {
+	.probe = marimba_codec_probe,
+	.driver = {
+		.name = "marimba_codec",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init marimba_codec_init(void)
+{
+	s32 rc;
+
+	rc = platform_driver_register(&marimba_codec_driver);
+	if (IS_ERR_VALUE(rc))
+		goto error;
+
+	adie_codec.path[ADIE_CODEC_TX].img.regs = adie_codec_tx_regs;
+	adie_codec.path[ADIE_CODEC_TX].img.img_sz =
+	ARRAY_SIZE(adie_codec_tx_regs);
+	adie_codec.path[ADIE_CODEC_RX].img.regs = adie_codec_rx_regs;
+	adie_codec.path[ADIE_CODEC_RX].img.img_sz =
+	ARRAY_SIZE(adie_codec_rx_regs);
+	adie_codec.path[ADIE_CODEC_LB].img.regs = adie_codec_lb_regs;
+	adie_codec.path[ADIE_CODEC_LB].img.img_sz =
+	ARRAY_SIZE(adie_codec_lb_regs);
+	mutex_init(&adie_codec.lock);
+
+error:
+	return rc;
+}
+
+static void __exit marimba_codec_exit(void)
+{
+#ifdef CONFIG_DEBUG_FS
+	debugfs_remove(debugfs_peek);
+	debugfs_remove(debugfs_poke);
+	debugfs_remove(debugfs_power);
+	debugfs_remove(debugfs_marimba_dent);
+#endif
+	platform_driver_unregister(&marimba_codec_driver);
+}
+
+module_init(marimba_codec_init);
+module_exit(marimba_codec_exit);
+
+MODULE_DESCRIPTION("Marimba codec driver");
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/marimba-core.c b/drivers/mfd/marimba-core.c
new file mode 100644
index 0000000..70ec2ec
--- /dev/null
+++ b/drivers/mfd/marimba-core.c
@@ -0,0 +1,937 @@
+/* Copyright (c) 2009-2012, 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.
+ *
+ */
+/*
+ * Qualcomm Marimba Core Driver
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+
+#include <linux/i2c.h>
+#include <linux/mfd/marimba.h>
+#include <linux/slab.h>
+#include <linux/debugfs.h>
+#include <linux/module.h>
+
+#define MARIMBA_MODE				0x00
+
+#define ADIE_ARRY_SIZE  (CHIP_ID_MAX * MARIMBA_NUM_CHILD)
+
+static int marimba_shadow[ADIE_ARRY_SIZE][0xff];
+static int mutex_initialized;
+struct marimba marimba_modules[ADIE_ARRY_SIZE];
+
+#define MARIMBA_VERSION_REG		0x11
+#define MARIMBA_MODE_REG		0x00
+
+struct marimba_platform_data *marimba_pdata;
+
+static uint32_t marimba_gpio_count;
+static bool fm_status;
+static bool bt_status;
+
+#ifdef CONFIG_I2C_SSBI
+#define NUM_ADD	MARIMBA_NUM_CHILD
+#else
+#define NUM_ADD	(MARIMBA_NUM_CHILD - 1)
+#endif
+
+#if defined(CONFIG_DEBUG_FS)
+struct adie_dbg_device {
+	struct mutex		dbg_mutex;
+	struct dentry		*dent;
+	int			addr;
+	int			mod_id;
+};
+
+static struct adie_dbg_device *marimba_dbg_device;
+static struct adie_dbg_device *timpani_dbg_device;
+static struct adie_dbg_device *bahama_dbg_device;
+#endif
+
+
+/**
+ * marimba_read_bahama_ver - Reads Bahama version.
+ * @param marimba: marimba structure pointer passed by client
+ * @returns result of the operation.
+ */
+int marimba_read_bahama_ver(struct marimba *marimba)
+{
+	int rc;
+	u8 bahama_version;
+
+	rc = marimba_read_bit_mask(marimba, 0x00,  &bahama_version, 1, 0x1F);
+	if (rc < 0)
+		return rc;
+	switch (bahama_version) {
+	case 0x08: /* varient of bahama v1 */
+	case 0x10:
+	case 0x00:
+		return BAHAMA_VER_1_0;
+	case 0x09: /* variant of bahama v2 */
+		return BAHAMA_VER_2_0;
+	default:
+		return BAHAMA_VER_UNSUPPORTED;
+	}
+}
+EXPORT_SYMBOL(marimba_read_bahama_ver);
+/**
+ * marimba_ssbi_write - Writes a n bit TSADC register in Marimba
+ * @param marimba: marimba structure pointer passed by client
+ * @param reg: register address
+ * @param value: buffer to be written
+ * @param len: num of bytes
+ * @returns result of the operation.
+ */
+int marimba_ssbi_write(struct marimba *marimba, u16 reg , u8 *value, int len)
+{
+	struct i2c_msg *msg;
+	int ret;
+
+	marimba = &marimba_modules[marimba->mod_id];
+
+	mutex_lock(&marimba->xfer_lock);
+
+	msg = &marimba->xfer_msg[0];
+	msg->addr = reg;
+	msg->flags = 0x0;
+	msg->buf = value;
+	msg->len = len;
+
+	ret = i2c_transfer(marimba->client->adapter, marimba->xfer_msg, 1);
+
+	mutex_unlock(&marimba->xfer_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(marimba_ssbi_write);
+
+/**
+ * marimba_ssbi_read - Reads a n bit TSADC register in Marimba
+ * @param marimba: marimba structure pointer passed by client
+ * @param reg: register address
+ * @param value: ssbi read of the register to be stored
+ * @param len: num of bytes
+ *
+ * @returns result of the operation.
+*/
+int marimba_ssbi_read(struct marimba *marimba, u16 reg, u8 *value, int len)
+{
+	struct i2c_msg *msg;
+	int ret;
+
+	marimba = &marimba_modules[marimba->mod_id];
+
+	mutex_lock(&marimba->xfer_lock);
+
+	msg = &marimba->xfer_msg[0];
+	msg->addr = reg;
+	msg->flags = I2C_M_RD;
+	msg->buf = value;
+	msg->len = len;
+
+	ret = i2c_transfer(marimba->client->adapter, marimba->xfer_msg, 1);
+
+	mutex_unlock(&marimba->xfer_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(marimba_ssbi_read);
+
+/**
+ * marimba_write_bit_mask - Sets n bit register using bit mask
+ * @param marimba: marimba structure pointer passed by client
+ * @param reg: register address
+ * @param value: buffer to be written to the registers
+ * @param num_bytes: n bytes to write
+ * @param mask: bit mask corresponding to the registers
+ *
+ * @returns result of the operation.
+ */
+int marimba_write_bit_mask(struct marimba *marimba, u8 reg, u8 *value,
+						unsigned num_bytes, u8 mask)
+{
+	int ret, i;
+	struct i2c_msg *msg;
+	u8 data[num_bytes + 1];
+	u8 mask_value[num_bytes];
+
+	marimba = &marimba_modules[marimba->mod_id];
+	if (marimba == NULL) {
+		pr_err("%s: Unable to access Marimba core\n", __func__);
+		return -ENODEV;
+	}
+
+
+	mutex_lock(&marimba->xfer_lock);
+
+	for (i = 0; i < num_bytes; i++)
+		mask_value[i] = (marimba_shadow[marimba->mod_id][reg + i]
+					& ~mask) | (value[i] & mask);
+
+	msg = &marimba->xfer_msg[0];
+	if (marimba->client == NULL) {
+		pr_err("%s: Unable to access the Marimba slave device.\n",
+								__func__);
+		return -ENODEV;
+	}
+
+	msg->addr = marimba->client->addr;
+	msg->flags = 0;
+	msg->len = num_bytes + 1;
+	msg->buf = data;
+	data[0] = reg;
+	memcpy(data+1, mask_value, num_bytes);
+
+	ret = i2c_transfer(marimba->client->adapter, marimba->xfer_msg, 1);
+
+	/* Try again if the write fails */
+	if (ret != 1)
+		ret = i2c_transfer(marimba->client->adapter,
+						marimba->xfer_msg, 1);
+
+	if (ret == 1) {
+		for (i = 0; i < num_bytes; i++)
+			marimba_shadow[marimba->mod_id][reg + i]
+							= mask_value[i];
+	} else {
+		dev_err(&marimba->client->dev, "i2c write failed\n");
+		ret = -ENODEV;
+	}
+
+	mutex_unlock(&marimba->xfer_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(marimba_write_bit_mask);
+
+/**
+ * marimba_write - Sets n bit register in Marimba
+ * @param marimba: marimba structure pointer passed by client
+ * @param reg: register address
+ * @param value: buffer values to be written
+ * @param num_bytes: n bytes to write
+ *
+ * @returns result of the operation.
+ */
+int marimba_write(struct marimba *marimba, u8 reg, u8 *value,
+							unsigned num_bytes)
+{
+	return marimba_write_bit_mask(marimba, reg, value, num_bytes, 0xff);
+}
+EXPORT_SYMBOL(marimba_write);
+
+/**
+ * marimba_read_bit_mask - Reads a n bit register based on bit mask
+ * @param marimba: marimba structure pointer passed by client
+ * @param reg: register address
+ * @param value: i2c read of the register to be stored
+ * @param num_bytes: n bytes to be read.
+ * @param mask: bit mask concerning its register
+ *
+ * @returns result of the operation.
+*/
+int marimba_read_bit_mask(struct marimba *marimba, u8 reg, u8 *value,
+						unsigned num_bytes, u8 mask)
+{
+	int ret, i;
+
+	struct i2c_msg *msg;
+
+	marimba = &marimba_modules[marimba->mod_id];
+
+	mutex_lock(&marimba->xfer_lock);
+
+	msg = &marimba->xfer_msg[0];
+	msg->addr = marimba->client->addr;
+	msg->len = 1;
+	msg->flags = 0;
+	msg->buf = &reg;
+
+	msg = &marimba->xfer_msg[1];
+	msg->addr = marimba->client->addr;
+	msg->len = num_bytes;
+	msg->flags = I2C_M_RD;
+	msg->buf = value;
+
+	ret = i2c_transfer(marimba->client->adapter, marimba->xfer_msg, 2);
+
+	/* Try again if read fails first time */
+	if (ret != 2)
+		ret = i2c_transfer(marimba->client->adapter,
+						marimba->xfer_msg, 2);
+
+	if (ret == 2) {
+		for (i = 0; i < num_bytes; i++) {
+			marimba_shadow[marimba->mod_id][reg + i] = value[i];
+			value[i] &= mask;
+		}
+	} else {
+		dev_err(&marimba->client->dev, "i2c read failed\n");
+		ret = -ENODEV;
+	}
+
+	mutex_unlock(&marimba->xfer_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(marimba_read_bit_mask);
+
+/**
+ * marimba_read - Reads n bit registers in Marimba
+ * @param marimba: marimba structure pointer passed by client
+ * @param reg: register address
+ * @param value: i2c read of the register to be stored
+ * @param num_bytes: n bytes to read.
+ * @param mask: bit mask concerning its register
+ *
+ * @returns result of the operation.
+*/
+int marimba_read(struct marimba *marimba, u8 reg, u8 *value, unsigned num_bytes)
+{
+	return marimba_read_bit_mask(marimba, reg, value, num_bytes, 0xff);
+}
+EXPORT_SYMBOL(marimba_read);
+
+int timpani_read(struct marimba *marimba, u8 reg, u8 *value, unsigned num_bytes)
+{
+	return marimba_read_bit_mask(marimba, reg, value, num_bytes, 0xff);
+}
+EXPORT_SYMBOL(timpani_read);
+
+int timpani_write(struct marimba *marimba, u8 reg,
+					u8 *value, unsigned num_bytes)
+{
+	return marimba_write_bit_mask(marimba, reg, value, num_bytes, 0xff);
+}
+EXPORT_SYMBOL(timpani_write);
+
+static int cur_codec_type = -1, cur_adie_type = -1, cur_connv_type = -1;
+static int adie_arry_idx;
+
+int adie_get_detected_codec_type(void)
+{
+	return cur_codec_type;
+}
+EXPORT_SYMBOL(adie_get_detected_codec_type);
+
+int adie_get_detected_connectivity_type(void)
+{
+	return cur_connv_type;
+}
+EXPORT_SYMBOL(adie_get_detected_connectivity_type);
+
+static struct device *
+add_numbered_child(unsigned chip, const char *name, int num, u8 driver_data,
+					void *pdata, unsigned pdata_len)
+{
+	struct platform_device *pdev;
+	struct marimba  *marimba = &marimba_modules[chip + adie_arry_idx];
+	int status = 0;
+
+	pdev = platform_device_alloc(name, num);
+	if (!pdev) {
+		status = -ENOMEM;
+		return ERR_PTR(status);
+	}
+
+	pdev->dev.parent = &marimba->client->dev;
+
+	marimba->mod_id = chip + adie_arry_idx;
+
+	platform_set_drvdata(pdev, marimba);
+
+	if (pdata) {
+		status = platform_device_add_data(pdev, pdata, pdata_len);
+		if (status < 0)
+			goto err;
+	}
+
+	status = platform_device_add(pdev);
+	if (status < 0)
+		goto err;
+
+err:
+	if (status < 0) {
+		platform_set_drvdata(pdev, NULL);
+		platform_device_put(pdev);
+		dev_err(&marimba->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,
+		u8 driver_data, void *pdata, unsigned pdata_len)
+{
+	return add_numbered_child(chip, name, -1, driver_data, pdata,
+								pdata_len);
+}
+
+static int marimba_add_child(struct marimba_platform_data *pdata,
+					u8 driver_data)
+{
+	struct device	*child;
+
+	if (cur_adie_type == MARIMBA_ID) {
+		child = add_child(MARIMBA_SLAVE_ID_FM, "marimba_fm",
+			driver_data, pdata->fm, sizeof(*pdata->fm));
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+	} else if ((cur_adie_type == BAHAMA_ID) &&
+			(cur_connv_type == BAHAMA_ID)) {
+		child = add_child(BAHAMA_SLAVE_ID_FM_ID, "marimba_fm",
+			driver_data, pdata->fm, sizeof(*pdata->fm));
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+	}
+
+	/* Add Codec for Marimba and Timpani */
+	if (cur_adie_type == MARIMBA_ID) {
+		child = add_child(MARIMBA_SLAVE_ID_CDC, "marimba_codec",
+			driver_data, pdata->codec, sizeof(*pdata->codec));
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+	} else if (cur_adie_type == TIMPANI_ID) {
+		child = add_child(MARIMBA_SLAVE_ID_CDC, "timpani_codec",
+			driver_data, pdata->codec, sizeof(*pdata->codec));
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+	}
+
+#if defined(CONFIG_I2C_SSBI)
+	if ((pdata->tsadc != NULL) && (cur_adie_type != BAHAMA_ID)) {
+		child = add_child(MARIMBA_ID_TSADC, "marimba_tsadc",
+			driver_data, pdata->tsadc, sizeof(*pdata->tsadc));
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+	}
+#endif
+	return 0;
+}
+
+int marimba_gpio_config(int gpio_value)
+{
+	struct marimba *marimba = &marimba_modules[MARIMBA_SLAVE_ID_MARIMBA];
+	struct marimba_platform_data *pdata = marimba_pdata;
+	int rc = 0;
+
+	/* Clients BT/FM need to manage GPIO 34 on Fusion for its clocks */
+
+	mutex_lock(&marimba->xfer_lock);
+
+	if (gpio_value) {
+		marimba_gpio_count++;
+		if (marimba_gpio_count == 1)
+			rc = pdata->marimba_gpio_config(1);
+	} else {
+		marimba_gpio_count--;
+		if (marimba_gpio_count == 0)
+			rc = pdata->marimba_gpio_config(0);
+	}
+
+	mutex_unlock(&marimba->xfer_lock);
+
+	return rc;
+
+}
+EXPORT_SYMBOL(marimba_gpio_config);
+
+bool marimba_get_fm_status(struct marimba *marimba)
+{
+	bool ret;
+
+	marimba = &marimba_modules[marimba->mod_id];
+
+	mutex_lock(&marimba->xfer_lock);
+
+	ret = fm_status;
+
+	mutex_unlock(&marimba->xfer_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(marimba_get_fm_status);
+
+void marimba_set_fm_status(struct marimba *marimba, bool value)
+{
+	marimba = &marimba_modules[marimba->mod_id];
+
+	mutex_lock(&marimba->xfer_lock);
+
+	fm_status = value;
+
+	mutex_unlock(&marimba->xfer_lock);
+}
+EXPORT_SYMBOL(marimba_set_fm_status);
+
+bool marimba_get_bt_status(struct marimba *marimba)
+{
+	bool ret;
+
+	marimba = &marimba_modules[marimba->mod_id];
+
+	mutex_lock(&marimba->xfer_lock);
+
+	ret = bt_status;
+
+	mutex_unlock(&marimba->xfer_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(marimba_get_bt_status);
+
+void marimba_set_bt_status(struct marimba *marimba, bool value)
+{
+	marimba = &marimba_modules[marimba->mod_id];
+
+	mutex_lock(&marimba->xfer_lock);
+
+	bt_status = value;
+
+	mutex_unlock(&marimba->xfer_lock);
+}
+EXPORT_SYMBOL(marimba_set_bt_status);
+
+#if defined(CONFIG_DEBUG_FS)
+
+static int check_addr(int addr, const char *func_name)
+{
+	if (addr < 0 || addr > 0xFF) {
+		pr_err("%s: Marimba register address is invalid: %d\n",
+			func_name, addr);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int marimba_debugfs_set(void *data, u64 val)
+{
+	struct adie_dbg_device *dbgdev = data;
+	u8 reg = val;
+	int rc;
+	struct marimba marimba_id;
+
+	mutex_lock(&dbgdev->dbg_mutex);
+
+	rc = check_addr(dbgdev->addr, __func__);
+	if (rc)
+		goto done;
+
+	marimba_id.mod_id = dbgdev->mod_id;
+	rc = marimba_write(&marimba_id, dbgdev->addr, &reg, 1);
+	rc = (rc == 1) ? 0 : rc;
+
+	if (rc)
+		pr_err("%s: FAIL marimba_write(0x%03X)=0x%02X: rc=%d\n",
+			__func__, dbgdev->addr, reg, rc);
+done:
+	mutex_unlock(&dbgdev->dbg_mutex);
+	return rc;
+}
+
+static int marimba_debugfs_get(void *data, u64 *val)
+{
+	struct adie_dbg_device *dbgdev = data;
+	int rc;
+	u8 reg;
+	struct marimba marimba_id;
+
+	mutex_lock(&dbgdev->dbg_mutex);
+
+	rc = check_addr(dbgdev->addr, __func__);
+	if (rc)
+		goto done;
+
+	marimba_id.mod_id = dbgdev->mod_id;
+	rc = marimba_read(&marimba_id, dbgdev->addr, &reg, 1);
+	rc = (rc == 2) ? 0 : rc;
+
+	if (rc) {
+		pr_err("%s: FAIL marimba_read(0x%03X)=0x%02X: rc=%d\n",
+			__func__, dbgdev->addr, reg, rc);
+		goto done;
+	}
+
+	*val = reg;
+done:
+	mutex_unlock(&dbgdev->dbg_mutex);
+	return rc;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(dbg_marimba_fops, marimba_debugfs_get,
+		marimba_debugfs_set, "0x%02llX\n");
+
+static int addr_set(void *data, u64 val)
+{
+	struct adie_dbg_device *dbgdev = data;
+	int rc;
+
+	rc = check_addr(val, __func__);
+	if (rc)
+		return rc;
+
+	mutex_lock(&dbgdev->dbg_mutex);
+	dbgdev->addr = val;
+	mutex_unlock(&dbgdev->dbg_mutex);
+
+	return 0;
+}
+
+static int addr_get(void *data, u64 *val)
+{
+	struct adie_dbg_device *dbgdev = data;
+	int rc;
+
+	mutex_lock(&dbgdev->dbg_mutex);
+
+	rc = check_addr(dbgdev->addr, __func__);
+	if (rc) {
+		mutex_unlock(&dbgdev->dbg_mutex);
+		return rc;
+	}
+	*val = dbgdev->addr;
+
+	mutex_unlock(&dbgdev->dbg_mutex);
+
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(dbg_addr_fops, addr_get, addr_set, "0x%03llX\n");
+
+static int __devinit marimba_dbg_init(int adie_type)
+{
+	struct adie_dbg_device *dbgdev;
+	struct dentry *dent;
+	struct dentry *temp;
+
+	dbgdev = kzalloc(sizeof *dbgdev, GFP_KERNEL);
+	if (dbgdev == NULL) {
+		pr_err("%s: kzalloc() failed.\n", __func__);
+		return -ENOMEM;
+	}
+
+	mutex_init(&dbgdev->dbg_mutex);
+	dbgdev->addr = -1;
+
+	if (adie_type == MARIMBA_ID) {
+		marimba_dbg_device = dbgdev;
+		marimba_dbg_device->mod_id = MARIMBA_SLAVE_ID_MARIMBA;
+		dent = debugfs_create_dir("marimba-dbg", NULL);
+	} else if (adie_type == TIMPANI_ID) {
+		timpani_dbg_device = dbgdev;
+		timpani_dbg_device->mod_id = MARIMBA_SLAVE_ID_MARIMBA;
+		dent = debugfs_create_dir("timpani-dbg", NULL);
+	} else if (adie_type == BAHAMA_ID) {
+		bahama_dbg_device = dbgdev;
+		bahama_dbg_device->mod_id = SLAVE_ID_BAHAMA;
+		dent = debugfs_create_dir("bahama-dbg", NULL);
+	}
+	if (dent == NULL || IS_ERR(dent)) {
+		pr_err("%s: ERR debugfs_create_dir: dent=0x%X\n",
+					__func__, (unsigned)dent);
+		kfree(dbgdev);
+		return -ENOMEM;
+	}
+
+	temp = debugfs_create_file("addr", S_IRUSR | S_IWUSR, dent,
+					dbgdev, &dbg_addr_fops);
+	if (temp == NULL || IS_ERR(temp)) {
+		pr_err("%s: ERR debugfs_create_file: dent=0x%X\n",
+				__func__, (unsigned)temp);
+		goto debug_error;
+	}
+
+	temp = debugfs_create_file("data", S_IRUSR | S_IWUSR, dent,
+					dbgdev,	&dbg_marimba_fops);
+	if (temp == NULL || IS_ERR(temp)) {
+		pr_err("%s: ERR debugfs_create_file: dent=0x%X\n",
+				__func__, (unsigned)temp);
+		goto debug_error;
+	}
+	dbgdev->dent = dent;
+
+	return 0;
+
+debug_error:
+	kfree(dbgdev);
+	debugfs_remove_recursive(dent);
+	return -ENOMEM;
+}
+
+static int __devexit marimba_dbg_remove(void)
+{
+	if (marimba_dbg_device) {
+		debugfs_remove_recursive(marimba_dbg_device->dent);
+		kfree(marimba_dbg_device);
+	}
+	if (timpani_dbg_device) {
+		debugfs_remove_recursive(timpani_dbg_device->dent);
+		kfree(timpani_dbg_device);
+	}
+	if (bahama_dbg_device) {
+		debugfs_remove_recursive(bahama_dbg_device->dent);
+		kfree(bahama_dbg_device);
+	}
+	return 0;
+}
+
+#else
+
+static int __devinit marimba_dbg_init(int adie_type)
+{
+	return 0;
+}
+
+static int __devexit marimba_dbg_remove(void)
+{
+	return 0;
+}
+
+#endif
+
+static int get_adie_type(void)
+{
+	u8 rd_val;
+	int ret;
+
+	struct marimba *marimba = &marimba_modules[ADIE_ARRY_SIZE - 1];
+
+	marimba->mod_id = ADIE_ARRY_SIZE - 1;
+	/* Enable the Mode for Marimba/Timpani */
+	ret = marimba_read(marimba, MARIMBA_MODE_REG, &rd_val, 1);
+
+	if (ret >= 0) {
+		if (rd_val & 0x80) {
+			cur_adie_type = BAHAMA_ID;
+			return cur_adie_type;
+		} else {
+			ret = marimba_read(marimba,
+				MARIMBA_VERSION_REG, &rd_val, 1);
+			if ((ret >= 0) && (rd_val & 0x20)) {
+				cur_adie_type = TIMPANI_ID;
+				return cur_adie_type;
+			} else if (ret >= 0) {
+				cur_adie_type = MARIMBA_ID;
+				return cur_adie_type;
+			}
+		}
+	}
+
+	return ret;
+}
+
+static void marimba_init_reg(struct i2c_client *client, u8 driver_data)
+{
+	struct marimba_platform_data *pdata = client->dev.platform_data;
+	struct marimba *marimba =
+		&marimba_modules[MARIMBA_SLAVE_ID_MARIMBA + adie_arry_idx];
+
+	u8 buf[1];
+
+	buf[0] = 0x10;
+
+	if (cur_adie_type != BAHAMA_ID) {
+		marimba->mod_id = MARIMBA_SLAVE_ID_MARIMBA + adie_arry_idx;
+		/* Enable the Mode for Marimba/Timpani */
+		marimba_write(marimba, MARIMBA_MODE, buf, 1);
+	} else if ((cur_adie_type == BAHAMA_ID) &&
+				(cur_connv_type == BAHAMA_ID)) {
+		marimba->mod_id = MARIMBA_SLAVE_ID_MARIMBA + adie_arry_idx;
+		marimba_write(marimba, BAHAMA_SLAVE_ID_FM_ID,
+				&pdata->slave_id[SLAVE_ID_BAHAMA_FM], 1);
+		/* Configure Bahama core registers (AREG & DREG) */
+		/* with optimal values to eliminate power leakage */
+		if (pdata->bahama_core_config != NULL)
+			pdata->bahama_core_config(cur_adie_type);
+	}
+}
+
+static int __devinit marimba_probe(struct i2c_client *client,
+				const struct i2c_device_id *id)
+{
+	struct marimba_platform_data *pdata = client->dev.platform_data;
+	struct i2c_adapter *ssbi_adap;
+	struct marimba *marimba;
+	int i, status, rc, client_loop, adie_slave_idx_offset;
+	int rc_bahama = 0, rc_marimba = 0;
+
+	if (!pdata) {
+		dev_dbg(&client->dev, "no platform data?\n");
+		status = -EINVAL;
+		goto fail;
+	}
+
+	if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) == 0) {
+		dev_dbg(&client->dev, "can't talk I2C?\n");
+		status = -EIO;
+		goto fail;
+	}
+	if (!mutex_initialized) {
+		for (i = 0; i < ADIE_ARRY_SIZE; ++i) {
+			marimba = &marimba_modules[i];
+			mutex_init(&marimba->xfer_lock);
+		}
+		mutex_initialized = 1;
+	}
+	/* First, identify the codec type */
+	if (pdata->marimba_setup != NULL) {
+		rc_marimba = pdata->marimba_setup();
+		if (rc_marimba)
+			pdata->marimba_shutdown();
+	}
+	if (pdata->bahama_setup != NULL &&
+		cur_connv_type != BAHAMA_ID) {
+		rc_bahama = pdata->bahama_setup();
+		if (rc_bahama)
+			pdata->bahama_shutdown(cur_connv_type);
+	}
+	if (rc_marimba & rc_bahama) {
+		status = -EAGAIN;
+		goto fail;
+	}
+	marimba = &marimba_modules[ADIE_ARRY_SIZE - 1];
+	marimba->client = client;
+
+	rc = get_adie_type();
+
+	if (rc < 0) {
+		if (pdata->bahama_setup != NULL)
+			pdata->bahama_shutdown(cur_adie_type);
+		if (pdata->marimba_shutdown != NULL)
+			pdata->marimba_shutdown();
+		status = -ENODEV;
+		goto fail;
+	}
+
+	if (rc < 2) {
+		adie_arry_idx = 0;
+		adie_slave_idx_offset = 0;
+		client_loop = 0;
+		cur_codec_type = rc;
+		if (cur_connv_type < 0)
+			cur_connv_type = rc;
+		if (pdata->bahama_shutdown != NULL)
+			pdata->bahama_shutdown(cur_connv_type);
+	} else {
+		adie_arry_idx = 5;
+		adie_slave_idx_offset = 5;
+		client_loop = 1;
+		cur_connv_type = rc;
+	}
+
+	marimba = &marimba_modules[adie_arry_idx];
+	marimba->client = client;
+
+	for (i = 1; i <= (NUM_ADD - client_loop); i++) {
+		/* Skip adding BT/FM for Timpani */
+		if (i == 1 && rc >= 1)
+			i++;
+		marimba = &marimba_modules[i + adie_arry_idx];
+		if (i != MARIMBA_ID_TSADC)
+			marimba->client = i2c_new_dummy(client->adapter,
+				pdata->slave_id[i + adie_slave_idx_offset]);
+		else if (pdata->tsadc_ssbi_adap) {
+			ssbi_adap = i2c_get_adapter(pdata->tsadc_ssbi_adap);
+			marimba->client = i2c_new_dummy(ssbi_adap,
+						0x55);
+		} else
+			ssbi_adap = NULL;
+
+		if (!marimba->client) {
+			dev_err(&marimba->client->dev,
+				"can't attach client %d\n", i);
+			status = -ENOMEM;
+			goto fail;
+		}
+		strlcpy(marimba->client->name, id->name,
+			sizeof(marimba->client->name));
+
+	}
+
+	if (marimba_dbg_init(rc) != 0)
+		pr_debug("%s: marimba debugfs init failed\n", __func__);
+
+	marimba_init_reg(client, id->driver_data);
+
+	status = marimba_add_child(pdata, id->driver_data);
+
+	marimba_pdata = pdata;
+
+	return 0;
+
+fail:
+	return status;
+}
+
+static int __devexit marimba_remove(struct i2c_client *client)
+{
+	int i;
+	struct marimba_platform_data *pdata;
+
+	pdata = client->dev.platform_data;
+	for (i = 0; i < ADIE_ARRY_SIZE; i++) {
+		struct marimba *marimba = &marimba_modules[i];
+
+		if (marimba->client && marimba->client != client)
+			i2c_unregister_device(marimba->client);
+
+		marimba_modules[i].client = NULL;
+		if (mutex_initialized)
+			mutex_destroy(&marimba->xfer_lock);
+
+	}
+	marimba_dbg_remove();
+	mutex_initialized = 0;
+	if (pdata->marimba_shutdown != NULL)
+		pdata->marimba_shutdown();
+
+	return 0;
+}
+
+static struct i2c_device_id marimba_id_table[] = {
+	{"marimba", MARIMBA_ID},
+	{"timpani", TIMPANI_ID},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, marimba_id_table);
+
+static struct i2c_driver marimba_driver = {
+		.driver			= {
+			.owner		=	THIS_MODULE,
+			.name		=	"marimba-core",
+		},
+		.id_table		=	marimba_id_table,
+		.probe			=	marimba_probe,
+		.remove			=	__devexit_p(marimba_remove),
+};
+
+static int __init marimba_init(void)
+{
+	return i2c_add_driver(&marimba_driver);
+}
+module_init(marimba_init);
+
+static void __exit marimba_exit(void)
+{
+	i2c_del_driver(&marimba_driver);
+}
+module_exit(marimba_exit);
+
+MODULE_DESCRIPTION("Marimba Top level Driver");
+MODULE_ALIAS("platform:marimba-core");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.1");
diff --git a/drivers/mfd/marimba-tsadc.c b/drivers/mfd/marimba-tsadc.c
new file mode 100644
index 0000000..8a7b781
--- /dev/null
+++ b/drivers/mfd/marimba-tsadc.c
@@ -0,0 +1,696 @@
+/*
+ * Marimba TSADC driver.
+ *
+ * Copyright (c) 2009-2010, 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/mfd/marimba.h>
+#include <linux/mfd/marimba-tsadc.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+#include <linux/earlysuspend.h>
+#endif
+
+/* marimba configuration block: TS_CTL0 */
+#define TS_CTL0			0xFF
+#define TS_CTL0_RESET		BIT(0)
+#define TS_CTL0_CLK_EN		BIT(1)
+#define TS_CTL0_XO_EN		BIT(2)
+#define TS_CTL0_EOC_EN		BIT(3)
+#define TS_CTL0_PENIRQ_EN	BIT(4)
+
+/* TSADC registers */
+#define SSBI_PRESET		0x00
+#define TSHK_DIG_CONFIG		0x4F
+#define TSHK_INTF_CONFIG	0x50
+#define TSHK_SETUP		0x51
+	#define TSHK_SETUP_EN_ADC  BIT(0)
+	#define TSHK_SETUP_EN_PIRQ BIT(7)
+#define TSHK_PARAM		0x52
+#define TSHK_DATA_RD		0x53
+#define TSHK_STATUS		0x54
+#define TSHK_SETUP2		0x55
+#define TSHK_RSV1		0x56
+	#define TSHK_RSV1_PRECHARGE_EN	BIT(0)
+#define TSHK_COMMAND		0x57
+#define TSHK_PARAM2		0x58
+	#define TSHK_INPUT_CLK_MASK	0x3F
+	#define TSHK_SAMPLE_PRD_MASK	0xC7
+	#define TSHK_INPUT_CLK_SHIFT	0x6
+	#define TSHK_SAMPLE_PRD_SHIFT	0x3
+#define TSHK_PARAM3		0x59
+	#define TSHK_PARAM3_MODE_MASK	0xFC
+	#define TSHK_PARAM3_PRE_CHG_SHIFT (5)
+	#define TSHK_PARAM3_STABIZ_SHIFT (2)
+	#define TSHK_STABLE_TIME_MASK	0xE3
+	#define TSHK_PRECHG_TIME_MASK	0x1F
+#define TSHK_PARAM4		0x5A
+#define TSHK_RSV2		0x5B
+#define TSHK_RSV3		0x5C
+#define TSHK_RSV4		0x5D
+#define TSHK_RSV5		0x5E
+
+struct marimba_tsadc_client {
+	unsigned int is_ts;
+	struct platform_device *pdev;
+};
+
+struct marimba_tsadc {
+	struct marimba *marimba;
+	struct device *dev;
+	struct marimba_tsadc_platform_data *pdata;
+	struct clk	*codec_ssbi;
+	struct device *child_tssc;
+	bool clk_enabled;
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+	struct early_suspend		early_suspend;
+#endif
+};
+
+static struct marimba_tsadc *tsadc_dev;
+
+static int marimba_write_u8(struct marimba_tsadc *tsadc, u8 reg, u8 data)
+{
+	int rc;
+
+	tsadc->marimba->mod_id = MARIMBA_SLAVE_ID_MARIMBA;
+	rc = marimba_write(tsadc->marimba, reg, &data, 1);
+
+	if (!rc)
+		dev_warn(tsadc->dev, "Error writing marimba reg %X - ret %X\n",
+				reg, data);
+	return 0;
+}
+
+static int marimba_tsadc_write(struct marimba_tsadc *tsadc, u8 reg, u8 data)
+{
+	int rc;
+
+	tsadc->marimba->mod_id = MARIMBA_ID_TSADC;
+
+	rc = marimba_ssbi_write(tsadc->marimba, reg, &data, 1);
+	if (!rc)
+		dev_warn(tsadc->dev, "Error writing marimba reg %X - ret %X\n",
+				reg, data);
+	return rc;
+}
+
+static int marimba_tsadc_shutdown(struct marimba_tsadc *tsadc)
+{
+	u8 val;
+	int rc;
+
+	/* force reset */
+	val = TS_CTL0_XO_EN | TS_CTL0_EOC_EN | TS_CTL0_PENIRQ_EN |
+				TS_CTL0_CLK_EN;
+	rc = marimba_write_u8(tsadc, TS_CTL0, val);
+	if (rc < 0)
+		return rc;
+
+	/* disable xo, clock */
+	val = TS_CTL0_PENIRQ_EN | TS_CTL0_EOC_EN;
+	rc = marimba_write_u8(tsadc, TS_CTL0, val);
+	if (rc < 0)
+		return rc;
+
+	/* de-vote S2 1.3v */
+	if (tsadc->pdata->level_vote)
+		/* REVISIT: Ignore error for level_vote(0) for now*/
+		tsadc->pdata->level_vote(0);
+
+	return 0;
+}
+
+static int marimba_tsadc_startup(struct marimba_tsadc *tsadc)
+{
+	u8 val;
+	int rc = 0;
+
+	/* vote for S2 1.3v */
+	if (tsadc->pdata->level_vote) {
+		rc = tsadc->pdata->level_vote(1);
+		if (rc < 0)
+			return rc;
+	}
+
+	/* disable XO, clock and output enables */
+	rc = marimba_write_u8(tsadc, TS_CTL0, 0x00);
+	if (rc < 0)
+		goto fail_marimba_write;
+
+	/* Enable output enables */
+	val = TS_CTL0_XO_EN | TS_CTL0_EOC_EN | TS_CTL0_PENIRQ_EN;
+	rc = marimba_write_u8(tsadc, TS_CTL0, val);
+	if (rc < 0)
+		goto fail_marimba_write;
+
+	/* Enable clock */
+	val = val | TS_CTL0_CLK_EN;
+	rc = marimba_write_u8(tsadc, TS_CTL0, val);
+	if (rc < 0)
+		goto fail_marimba_write;
+
+	/* remove reset */
+	val = val | TS_CTL0_RESET;
+	rc = marimba_write_u8(tsadc, TS_CTL0, val);
+	if (rc < 0)
+		goto fail_marimba_write;
+
+	return 0;
+
+fail_marimba_write:
+	if (tsadc->pdata->level_vote)
+		/* REVISIT: Ignore error for level_vote(0) for now*/
+		tsadc->pdata->level_vote(0);
+	return rc;
+}
+
+
+static int marimba_tsadc_configure(struct marimba_tsadc *tsadc)
+{
+	u8 rsv1 = 0,  setup = 0, i, count = 0;
+	u8 param2 = 0,  param3 = 0;
+	unsigned long val;
+	int rc;
+
+	rc = marimba_tsadc_write(tsadc, SSBI_PRESET, 0x00);
+	if (rc < 0)
+		return rc;
+
+	if (!tsadc->pdata)
+		return -EINVAL;
+
+	/* Configure RSV1 register*/
+	if (tsadc->pdata->tsadc_prechg_en == true)
+		rsv1 |= TSHK_RSV1_PRECHARGE_EN;
+	else
+		rsv1 &= ~TSHK_RSV1_PRECHARGE_EN;
+
+	/*  Set RSV1 register*/
+	rc = marimba_tsadc_write(tsadc, TSHK_RSV1, rsv1);
+	if (rc < 0)
+		return rc;
+
+	/* Configure PARAM2 register */
+	/* Input clk */
+	val = tsadc->pdata->params2.input_clk_khz;
+	param2 &= TSHK_INPUT_CLK_MASK;
+	val /= 600;
+	if (val >= 1 && val <= 8 && !(val & (val - 1))) {
+		/* Input clk can be .6, 1.2, 2.4, 4.8Mhz */
+		if (val % 4 != 0)
+			param2 = (4 - (val % 4)) << TSHK_INPUT_CLK_SHIFT;
+		else
+			param2 = ((val / 4) - 1) << TSHK_INPUT_CLK_SHIFT;
+	} else /* Configure the default clk 2.4Mhz */
+		param2 = 0x00 << TSHK_INPUT_CLK_SHIFT;
+
+	/* Sample period */
+	param2 &= TSHK_SAMPLE_PRD_MASK;
+	param2 |=  tsadc->pdata->params2.sample_prd << TSHK_SAMPLE_PRD_SHIFT;
+
+	/* Write PARAM2 register */
+	rc = marimba_tsadc_write(tsadc, TSHK_PARAM2, param2);
+	if (rc < 0)
+		return rc;
+
+	/* REVISIT: If Precharge time, stabilization time  > 409.6us */
+	/* Configure PARAM3 register */
+	val = tsadc->pdata->params3.prechg_time_nsecs;
+	param3 &= TSHK_PRECHG_TIME_MASK;
+	val /= 6400;
+	if (val >= 1 && val <= 64  && !(val & (val - 1))) {
+		count = 0;
+		while ((val = val >> 1) != 0)
+			count++;
+		param3 |= count << TSHK_PARAM3_PRE_CHG_SHIFT;
+	} else	/* Set default value if the input is wrong */
+		param3 |= 0x00 << TSHK_PARAM3_PRE_CHG_SHIFT;
+
+	val = tsadc->pdata->params3.stable_time_nsecs;
+	param3 &= TSHK_STABLE_TIME_MASK;
+	val /= 6400;
+	if (val >= 1 && val <= 64 && !(val & (val - 1))) {
+		count = 0;
+		while ((val = val >> 1) != 0)
+			count++;
+		param3 |= count << TSHK_PARAM3_STABIZ_SHIFT;
+	} else /* Set default value if the input is wrong */
+		param3 |=  0x00 << TSHK_PARAM3_STABIZ_SHIFT;
+
+	/* Get TSADC mode */
+	val = tsadc->pdata->params3.tsadc_test_mode;
+	param3 &= TSHK_PARAM3_MODE_MASK;
+	if (val == 0)
+		param3 |= 0x00;
+	else
+		for (i = 0; i < 3 ; i++) {
+			if (((val + i) % 39322) == 0) {
+				param3 |= (i + 1);
+				break;
+			}
+		}
+	if (i == 3) /* Set to normal mode if input is wrong */
+		param3 |= 0x00;
+
+	rc = marimba_tsadc_write(tsadc, TSHK_PARAM3, param3);
+	if (rc < 0)
+		return rc;
+
+	/* Configure TSHK SETUP Register */
+	if (tsadc->pdata->setup.pen_irq_en == true)
+		setup |= TSHK_SETUP_EN_PIRQ;
+	else
+		setup &= ~TSHK_SETUP_EN_PIRQ;
+
+	if (tsadc->pdata->setup.tsadc_en == true)
+		setup |= TSHK_SETUP_EN_ADC;
+	else
+		setup &= ~TSHK_SETUP_EN_ADC;
+
+	/* Enable signals to ADC, pen irq assertion */
+	rc = marimba_tsadc_write(tsadc, TSHK_SETUP, setup);
+	if (rc < 0)
+		return rc;
+
+	return 0;
+}
+
+int marimba_tsadc_start(struct marimba_tsadc_client *client)
+{
+	int rc = 0;
+
+	if (!client) {
+		pr_err("%s: Not a valid client\n", __func__);
+		return -ENODEV;
+	}
+
+	if (!tsadc_dev) {
+		dev_err(&client->pdev->dev,
+			"%s: No tsadc device available\n", __func__);
+		return -ENODEV;
+	}
+
+	/* REVISIT - add locks */
+	if (client->is_ts) {
+		rc = marimba_tsadc_startup(tsadc_dev);
+		if (rc < 0)
+			goto fail_tsadc_startup;
+		rc = marimba_tsadc_configure(tsadc_dev);
+		if (rc < 0)
+			goto fail_tsadc_conf;
+	}
+
+	return 0;
+fail_tsadc_conf:
+	marimba_tsadc_shutdown(tsadc_dev);
+fail_tsadc_startup:
+	return rc;
+}
+EXPORT_SYMBOL(marimba_tsadc_start);
+
+struct marimba_tsadc_client *
+marimba_tsadc_register(struct platform_device *pdev, unsigned int is_ts)
+{
+	struct marimba_tsadc_client *client;
+
+	if (!pdev) {
+		pr_err("%s: valid platform device pointer please\n", __func__);
+		return ERR_PTR(-EINVAL);
+	}
+
+	if (!is_ts) {
+		dev_err(&pdev->dev, "%s: only TS right now\n", __func__);
+		return ERR_PTR(-EINVAL);
+	}
+
+	if (!tsadc_dev) {
+		dev_err(&pdev->dev,
+			"%s: No tsadc device available\n", __func__);
+		return ERR_PTR(-ENODEV);
+	}
+
+	client = kzalloc(sizeof *client, GFP_KERNEL);
+	if (!client)
+		return ERR_PTR(-ENOMEM);
+
+	client->pdev = pdev;
+	client->is_ts = is_ts;
+
+	return client;
+}
+EXPORT_SYMBOL(marimba_tsadc_register);
+
+void marimba_tsadc_unregister(struct marimba_tsadc_client *client)
+{
+	if (client->is_ts)
+		marimba_tsadc_shutdown(tsadc_dev);
+	kfree(client);
+}
+EXPORT_SYMBOL(marimba_tsadc_unregister);
+
+static struct resource resources_tssc[] = {
+	{
+		.start	= 0xAD300000,
+		.end	= 0xAD300000 + SZ_4K - 1,
+		.name	= "tssc",
+		.flags	= IORESOURCE_MEM,
+	},
+	{
+		.start	= 55,
+		.end	= 55,
+		.name	= "tssc1",
+		.flags	= IORESOURCE_IRQ | IRQF_TRIGGER_RISING,
+	},
+	{
+		.start	= 56,
+		.end	= 56,
+		.name	= "tssc2",
+		.flags	= IORESOURCE_IRQ | IRQF_TRIGGER_RISING,
+	},
+};
+
+static struct device *
+marimba_add_tssc_subdev(struct device *parent, const char *name, int num,
+			 struct resource *resources, int num_resources,
+			 void *pdata, int pdata_len)
+{
+	struct platform_device	*pdev;
+	int			status;
+
+	pdev = platform_device_alloc(name, num);
+	if (!pdev) {
+		dev_dbg(parent, "can't alloc dev\n");
+		status = -ENOMEM;
+		goto err;
+	}
+
+	pdev->dev.parent = parent;
+
+	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;
+		}
+	}
+
+	status = platform_device_add_resources(pdev, resources, num_resources);
+	if (status < 0) {
+		dev_dbg(&pdev->dev, "can't add resources\n");
+		goto err;
+	}
+
+	status = platform_device_add(pdev);
+
+err:
+	if (status < 0) {
+		platform_device_put(pdev);
+		dev_err(parent, "can't add %s dev\n", name);
+		return ERR_PTR(status);
+	}
+	return &pdev->dev;
+}
+
+#ifdef CONFIG_PM
+static int
+marimba_tsadc_suspend(struct device *dev)
+{
+	int rc = 0, ret = 0;
+	struct marimba_tsadc *tsadc = dev_get_drvdata(dev);
+
+	if (tsadc->clk_enabled == true) {
+		clk_disable(tsadc->codec_ssbi);
+		tsadc->clk_enabled = false;
+	}
+
+	if (!(device_may_wakeup(dev) &&
+			device_may_wakeup(tsadc->child_tssc))) {
+		rc = marimba_tsadc_shutdown(tsadc);
+		if (rc < 0) {
+			pr_err("%s: Unable to shutdown TSADC\n", __func__);
+			goto fail_shutdown;
+		}
+
+		if (tsadc->pdata->marimba_tsadc_power) {
+			rc = tsadc->pdata->marimba_tsadc_power(0);
+			if (rc < 0)
+				goto fail_tsadc_power;
+		}
+	}
+	return rc;
+
+fail_tsadc_power:
+	marimba_tsadc_startup(tsadc_dev);
+	marimba_tsadc_configure(tsadc_dev);
+fail_shutdown:
+	if (tsadc->clk_enabled == false) {
+		ret = clk_enable(tsadc->codec_ssbi);
+		if (ret == 0)
+			tsadc->clk_enabled = true;
+	}
+	return rc;
+}
+
+static int marimba_tsadc_resume(struct device *dev)
+{
+	int rc = 0;
+	struct marimba_tsadc *tsadc = dev_get_drvdata(dev);
+
+	if (tsadc->clk_enabled == false) {
+		rc = clk_enable(tsadc->codec_ssbi);
+		if (rc != 0) {
+			pr_err("%s: Clk enable failed\n", __func__);
+			return rc;
+		}
+		tsadc->clk_enabled = true;
+	}
+
+	if (!(device_may_wakeup(dev) &&
+			device_may_wakeup(tsadc->child_tssc))) {
+		if (tsadc->pdata->marimba_tsadc_power) {
+			rc = tsadc->pdata->marimba_tsadc_power(1);
+			if (rc) {
+				pr_err("%s: Unable to power on TSADC \n",
+						__func__);
+				goto fail_tsadc_power;
+			}
+		}
+
+		rc = marimba_tsadc_startup(tsadc_dev);
+		if (rc < 0) {
+			pr_err("%s: Unable to startup TSADC\n", __func__);
+			goto fail_tsadc_startup;
+		}
+
+		rc = marimba_tsadc_configure(tsadc_dev);
+		if (rc < 0) {
+			pr_err("%s: Unable to configure TSADC\n", __func__);
+			goto fail_tsadc_configure;
+		}
+	}
+	return rc;
+
+fail_tsadc_configure:
+	marimba_tsadc_shutdown(tsadc_dev);
+fail_tsadc_startup:
+	if (tsadc->pdata->marimba_tsadc_power)
+		tsadc->pdata->marimba_tsadc_power(0);
+fail_tsadc_power:
+	if (tsadc->clk_enabled == true) {
+		clk_disable(tsadc->codec_ssbi);
+		tsadc->clk_enabled = false;
+	}
+	return rc;
+}
+
+static struct dev_pm_ops tsadc_pm_ops = {
+#ifndef CONFIG_HAS_EARLYSUSPEND
+	.suspend = marimba_tsadc_suspend,
+	.resume = marimba_tsadc_resume,
+#endif
+};
+#endif
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void marimba_tsadc_early_suspend(struct early_suspend *h)
+{
+	struct marimba_tsadc *tsadc = container_of(h, struct marimba_tsadc,
+						 early_suspend);
+
+	marimba_tsadc_suspend(tsadc->dev);
+}
+
+static void marimba_tsadc_late_resume(struct early_suspend *h)
+{
+	struct marimba_tsadc *tsadc = container_of(h, struct marimba_tsadc,
+						 early_suspend);
+
+	marimba_tsadc_resume(tsadc->dev);
+}
+#endif
+
+static int __devinit marimba_tsadc_probe(struct platform_device *pdev)
+{
+	struct marimba *marimba = platform_get_drvdata(pdev);
+	struct marimba_tsadc *tsadc;
+	struct marimba_tsadc_platform_data *pdata = pdev->dev.platform_data;
+	int rc = 0;
+	struct device *child;
+
+	printk("%s\n", __func__);
+
+	if (!pdata) {
+		dev_dbg(&pdev->dev, "no tsadc platform data?\n");
+		return -EINVAL;
+	}
+
+	tsadc = kzalloc(sizeof *tsadc, GFP_KERNEL);
+	if (!tsadc)
+		return -ENOMEM;
+
+	tsadc->marimba	= marimba;
+	tsadc->dev	= &pdev->dev;
+	tsadc->pdata	= pdata;
+
+	platform_set_drvdata(pdev, tsadc);
+
+	if (tsadc->pdata->init) {
+		rc = tsadc->pdata->init();
+		if (rc < 0)
+			goto fail_tsadc_init;
+	}
+
+	if (tsadc->pdata->marimba_tsadc_power) {
+		rc = tsadc->pdata->marimba_tsadc_power(1);
+		if (rc) {
+			pr_err("%s: Unable to power up TSADC \n", __func__);
+			goto fail_tsadc_power;
+		}
+	}
+
+	tsadc->codec_ssbi = clk_get(NULL, "codec_ssbi_clk");
+	if (IS_ERR(tsadc->codec_ssbi)) {
+		rc = PTR_ERR(tsadc->codec_ssbi);
+		goto fail_clk_get;
+	}
+	rc = clk_enable(tsadc->codec_ssbi);
+	if (rc != 0)
+		goto fail_clk_enable;
+
+	tsadc->clk_enabled = true;
+
+	child = marimba_add_tssc_subdev(&pdev->dev, "msm_touchscreen", -1,
+			 resources_tssc, ARRAY_SIZE(resources_tssc),
+			 pdata->tssc_data, sizeof(*pdata->tssc_data));
+
+	if (IS_ERR(child)) {
+		rc = PTR_ERR(child);
+		goto fail_add_subdev;
+	}
+
+	tsadc->child_tssc = child;
+	platform_set_drvdata(pdev, tsadc);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	tsadc->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN +
+						 TSADC_SUSPEND_LEVEL;
+	tsadc->early_suspend.suspend = marimba_tsadc_early_suspend;
+	tsadc->early_suspend.resume = marimba_tsadc_late_resume;
+	register_early_suspend(&tsadc->early_suspend);
+#endif
+
+	tsadc_dev = tsadc;
+	device_init_wakeup(&pdev->dev, pdata->can_wakeup);
+
+	return rc;
+
+fail_add_subdev:
+	clk_disable(tsadc->codec_ssbi);
+
+fail_clk_enable:
+	clk_put(tsadc->codec_ssbi);
+
+fail_clk_get:
+	if (tsadc->pdata->marimba_tsadc_power)
+		rc = tsadc->pdata->marimba_tsadc_power(0);
+fail_tsadc_power:
+	if (tsadc->pdata->exit)
+		rc = tsadc->pdata->exit();
+fail_tsadc_init:
+	kfree(tsadc);
+	return rc;
+}
+
+static int __devexit marimba_tsadc_remove(struct platform_device *pdev)
+{
+	int rc = 0;
+	struct marimba_tsadc *tsadc = platform_get_drvdata(pdev);
+
+	device_init_wakeup(&pdev->dev, 0);
+
+	if (tsadc->clk_enabled == true)
+		clk_disable(tsadc->codec_ssbi);
+
+	clk_put(tsadc->codec_ssbi);
+
+	if (tsadc->pdata->exit)
+		rc = tsadc->pdata->exit();
+
+	if (tsadc->pdata->marimba_tsadc_power)
+		rc = tsadc->pdata->marimba_tsadc_power(0);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	unregister_early_suspend(&tsadc->early_suspend);
+#endif
+
+	platform_set_drvdata(pdev, NULL);
+	kfree(tsadc);
+	return rc;
+}
+
+static struct platform_driver tsadc_driver = {
+	.probe	= marimba_tsadc_probe,
+	.remove	= __devexit_p(marimba_tsadc_remove),
+	.driver	= {
+		.name = "marimba_tsadc",
+		.owner = THIS_MODULE,
+#ifdef CONFIG_PM
+		.pm = &tsadc_pm_ops,
+#endif
+	},
+};
+
+static int __init marimba_tsadc_init(void)
+{
+	return platform_driver_register(&tsadc_driver);
+}
+device_initcall(marimba_tsadc_init);
+
+static void __exit marimba_tsadc_exit(void)
+{
+	return platform_driver_unregister(&tsadc_driver);
+}
+module_exit(marimba_tsadc_exit);
+
+MODULE_DESCRIPTION("Marimba TSADC driver");
+MODULE_VERSION("0.1");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:marimba_tsadc");
diff --git a/drivers/mfd/msm-adie-codec.c b/drivers/mfd/msm-adie-codec.c
new file mode 100644
index 0000000..d9414ed
--- /dev/null
+++ b/drivers/mfd/msm-adie-codec.c
@@ -0,0 +1,196 @@
+/* Copyright (c) 2010, 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.
+ *
+ */
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/msm-adie-codec.h>
+#include <linux/mfd/marimba.h>
+
+static const struct adie_codec_operations *cur_adie_ops;
+
+int adie_codec_register_codec_operations(
+			const struct adie_codec_operations *adie_ops)
+{
+	if (adie_ops == NULL)
+		return -EINVAL;
+
+	if (adie_ops->codec_id != adie_get_detected_codec_type())
+		return -EINVAL;
+
+	cur_adie_ops = adie_ops;
+	pr_info("%s: codec type %d\n", __func__, adie_ops->codec_id);
+	return 0;
+}
+
+int adie_codec_open(struct adie_codec_dev_profile *profile,
+	struct adie_codec_path **path_pptr)
+{
+	int rc = -EPERM;
+
+	if (cur_adie_ops != NULL) {
+		if (cur_adie_ops->codec_open != NULL)
+			rc = cur_adie_ops->codec_open(profile, path_pptr);
+	} else
+		rc = -ENODEV;
+
+	return rc;
+}
+EXPORT_SYMBOL(adie_codec_open);
+
+int adie_codec_close(struct adie_codec_path *path_ptr)
+{
+	int rc = -EPERM;
+
+	if (cur_adie_ops != NULL) {
+		if (cur_adie_ops->codec_close != NULL)
+			rc = cur_adie_ops->codec_close(path_ptr);
+	} else
+		rc = -ENODEV;
+
+	return rc;
+}
+EXPORT_SYMBOL(adie_codec_close);
+
+int adie_codec_set_device_digital_volume(struct adie_codec_path *path_ptr,
+		u32 num_channels, u32 vol_percentage /* in percentage */)
+{
+	int rc = -EPERM;
+
+	if (cur_adie_ops != NULL) {
+		if (cur_adie_ops->codec_set_device_digital_volume != NULL) {
+			rc = cur_adie_ops->codec_set_device_digital_volume(
+							path_ptr,
+							num_channels,
+							vol_percentage);
+		}
+	} else
+		rc = -ENODEV;
+
+	return rc;
+}
+EXPORT_SYMBOL(adie_codec_set_device_digital_volume);
+
+int adie_codec_set_device_analog_volume(struct adie_codec_path *path_ptr,
+		u32 num_channels, u32 volume /* in percentage */)
+{
+	int rc = -EPERM;
+
+	if (cur_adie_ops != NULL) {
+		if (cur_adie_ops->codec_set_device_analog_volume != NULL) {
+			rc = cur_adie_ops->codec_set_device_analog_volume(
+							path_ptr,
+							num_channels,
+							volume);
+		}
+	} else
+		rc = -ENODEV;
+
+	return rc;
+}
+EXPORT_SYMBOL(adie_codec_set_device_analog_volume);
+
+int adie_codec_setpath(struct adie_codec_path *path_ptr, u32 freq_plan, u32 osr)
+{
+	int rc = -EPERM;
+
+	if (cur_adie_ops != NULL) {
+		if (cur_adie_ops->codec_setpath != NULL) {
+			rc = cur_adie_ops->codec_setpath(path_ptr,
+							freq_plan,
+							osr);
+		}
+	} else
+		rc = -ENODEV;
+
+	return rc;
+}
+EXPORT_SYMBOL(adie_codec_setpath);
+
+u32 adie_codec_freq_supported(struct adie_codec_dev_profile *profile,
+	u32 requested_freq)
+{
+	int rc = -EPERM;
+
+	if (cur_adie_ops != NULL) {
+		if (cur_adie_ops->codec_freq_supported != NULL)
+			rc = cur_adie_ops->codec_freq_supported(profile,
+							requested_freq);
+	} else
+		rc = -ENODEV;
+
+	return rc;
+}
+EXPORT_SYMBOL(adie_codec_freq_supported);
+
+int adie_codec_enable_sidetone(struct adie_codec_path *rx_path_ptr,
+	u32 enable)
+{
+	int rc = -EPERM;
+
+	if (cur_adie_ops != NULL) {
+		if (cur_adie_ops->codec_enable_sidetone != NULL)
+			rc = cur_adie_ops->codec_enable_sidetone(rx_path_ptr,
+								enable);
+	} else
+		rc = -ENODEV;
+
+	return rc;
+}
+EXPORT_SYMBOL(adie_codec_enable_sidetone);
+
+int adie_codec_enable_anc(struct adie_codec_path *rx_path_ptr,
+	u32 enable, struct adie_codec_anc_data *calibration_writes)
+{
+	int rc = -EPERM;
+
+	if (cur_adie_ops != NULL) {
+		if (cur_adie_ops->codec_enable_anc != NULL)
+			rc = cur_adie_ops->codec_enable_anc(rx_path_ptr,
+				enable, calibration_writes);
+	}
+
+	return rc;
+}
+EXPORT_SYMBOL(adie_codec_enable_anc);
+
+int adie_codec_proceed_stage(struct adie_codec_path *path_ptr, u32 state)
+{
+	int rc = -EPERM;
+
+	if (cur_adie_ops != NULL) {
+		if (cur_adie_ops->codec_proceed_stage != NULL)
+			rc = cur_adie_ops->codec_proceed_stage(path_ptr,
+								state);
+	} else
+		rc = -ENODEV;
+
+	return rc;
+}
+EXPORT_SYMBOL(adie_codec_proceed_stage);
+
+int adie_codec_set_master_mode(struct adie_codec_path *path_ptr, u8 master)
+{
+	int rc = -EPERM;
+
+	if (cur_adie_ops != NULL) {
+		if (cur_adie_ops->codec_set_master_mode != NULL)
+			rc = cur_adie_ops->codec_set_master_mode(path_ptr,
+					master);
+	} else
+		rc = -ENODEV;
+
+	return rc;
+}
+EXPORT_SYMBOL(adie_codec_set_master_mode);
+
+
diff --git a/drivers/mfd/pm8018-core.c b/drivers/mfd/pm8018-core.c
new file mode 100644
index 0000000..b1b64cb
--- /dev/null
+++ b/drivers/mfd/pm8018-core.c
@@ -0,0 +1,674 @@
+/*
+ * Copyright (c) 2011-2012, 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/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/err.h>
+#include <linux/msm_ssbi.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/pm8xxx/pm8018.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/mfd/pm8xxx/regulator.h>
+#include <linux/leds-pm8xxx.h>
+
+
+/* PMIC PM8018 SSBI Addresses */
+#define REG_HWREV		0x002  /* PMIC4 revision */
+#define REG_HWREV_2		0x0E8  /* PMIC4 revision 2 */
+
+#define REG_MPP_BASE		0x050
+#define REG_IRQ_BASE		0x1BB
+
+#define REG_RTC_BASE		0x11D
+
+#define REG_TEMP_ALARM_CTRL	0x01B
+#define REG_TEMP_ALARM_PWM	0x09B
+
+
+#define PM8018_VERSION_MASK	0xFFF0
+#define PM8018_VERSION_VALUE	0x08F0
+#define PM8018_REVISION_MASK	0x000F
+
+#define REG_PM8018_PON_CNTRL_3	0x01D
+#define PM8018_RESTART_REASON_MASK	0x07
+
+#define SINGLE_IRQ_RESOURCE(_name, _irq) \
+{ \
+	.name	= _name, \
+	.start	= _irq, \
+	.end	= _irq, \
+	.flags	= IORESOURCE_IRQ, \
+}
+
+struct pm8018 {
+	struct device					*dev;
+	struct pm_irq_chip				*irq_chip;
+	struct mfd_cell					*mfd_regulators;
+	struct pm8xxx_regulator_core_platform_data	*regulator_cdata;
+	u32						rev_registers;
+};
+
+static int pm8018_readb(const struct device *dev, u16 addr, u8 *val)
+{
+	const struct pm8xxx_drvdata *pm8018_drvdata = dev_get_drvdata(dev);
+	const struct pm8018 *pmic = pm8018_drvdata->pm_chip_data;
+
+	return msm_ssbi_read(pmic->dev->parent, addr, val, 1);
+}
+
+static int pm8018_writeb(const struct device *dev, u16 addr, u8 val)
+{
+	const struct pm8xxx_drvdata *pm8018_drvdata = dev_get_drvdata(dev);
+	const struct pm8018 *pmic = pm8018_drvdata->pm_chip_data;
+
+	return msm_ssbi_write(pmic->dev->parent, addr, &val, 1);
+}
+
+static int pm8018_read_buf(const struct device *dev, u16 addr, u8 *buf,
+									int cnt)
+{
+	const struct pm8xxx_drvdata *pm8018_drvdata = dev_get_drvdata(dev);
+	const struct pm8018 *pmic = pm8018_drvdata->pm_chip_data;
+
+	return msm_ssbi_read(pmic->dev->parent, addr, buf, cnt);
+}
+
+static int pm8018_write_buf(const struct device *dev, u16 addr, u8 *buf,
+									int cnt)
+{
+	const struct pm8xxx_drvdata *pm8018_drvdata = dev_get_drvdata(dev);
+	const struct pm8018 *pmic = pm8018_drvdata->pm_chip_data;
+
+	return msm_ssbi_write(pmic->dev->parent, addr, buf, cnt);
+}
+
+static int pm8018_read_irq_stat(const struct device *dev, int irq)
+{
+	const struct pm8xxx_drvdata *pm8018_drvdata = dev_get_drvdata(dev);
+	const struct pm8018 *pmic = pm8018_drvdata->pm_chip_data;
+
+	return pm8xxx_get_irq_stat(pmic->irq_chip, irq);
+}
+
+static enum pm8xxx_version pm8018_get_version(const struct device *dev)
+{
+	const struct pm8xxx_drvdata *pm8018_drvdata = dev_get_drvdata(dev);
+	const struct pm8018 *pmic = pm8018_drvdata->pm_chip_data;
+	enum pm8xxx_version version = -ENODEV;
+
+	if ((pmic->rev_registers & PM8018_VERSION_MASK) == PM8018_VERSION_VALUE)
+		version = PM8XXX_VERSION_8018;
+
+	return version;
+}
+
+static int pm8018_get_revision(const struct device *dev)
+{
+	const struct pm8xxx_drvdata *pm8018_drvdata = dev_get_drvdata(dev);
+	const struct pm8018 *pmic = pm8018_drvdata->pm_chip_data;
+
+	return pmic->rev_registers & PM8018_REVISION_MASK;
+}
+
+static struct pm8xxx_drvdata pm8018_drvdata = {
+	.pmic_readb		= pm8018_readb,
+	.pmic_writeb		= pm8018_writeb,
+	.pmic_read_buf		= pm8018_read_buf,
+	.pmic_write_buf		= pm8018_write_buf,
+	.pmic_read_irq_stat	= pm8018_read_irq_stat,
+	.pmic_get_version	= pm8018_get_version,
+	.pmic_get_revision	= pm8018_get_revision,
+};
+
+static const struct resource gpio_cell_resources[] __devinitconst = {
+	[0] = {
+		.start = PM8018_IRQ_BLOCK_BIT(PM8018_GPIO_BLOCK_START, 0),
+		.end   = PM8018_IRQ_BLOCK_BIT(PM8018_GPIO_BLOCK_START, 0)
+			+ PM8018_NR_GPIOS - 1,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct mfd_cell gpio_cell __devinitdata = {
+	.name		= PM8XXX_GPIO_DEV_NAME,
+	.id		= -1,
+	.resources	= gpio_cell_resources,
+	.num_resources	= ARRAY_SIZE(gpio_cell_resources),
+};
+
+static const struct resource adc_cell_resources[] __devinitconst = {
+	SINGLE_IRQ_RESOURCE(NULL, PM8018_ADC_EOC_USR_IRQ),
+	SINGLE_IRQ_RESOURCE(NULL, PM8018_ADC_BATT_TEMP_WARM_IRQ),
+	SINGLE_IRQ_RESOURCE(NULL, PM8018_ADC_BATT_TEMP_COLD_IRQ),
+};
+
+static struct mfd_cell adc_cell __devinitdata = {
+	.name		= PM8XXX_ADC_DEV_NAME,
+	.id		= -1,
+	.resources	= adc_cell_resources,
+	.num_resources	= ARRAY_SIZE(adc_cell_resources),
+};
+
+static const struct resource mpp_cell_resources[] __devinitconst = {
+	{
+		.start	= PM8018_IRQ_BLOCK_BIT(PM8018_MPP_BLOCK_START, 0),
+		.end	= PM8018_IRQ_BLOCK_BIT(PM8018_MPP_BLOCK_START, 0)
+			  + PM8018_NR_MPPS - 1,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct mfd_cell mpp_cell __devinitdata = {
+	.name		= PM8XXX_MPP_DEV_NAME,
+	.id		= -1,
+	.resources	= mpp_cell_resources,
+	.num_resources	= ARRAY_SIZE(mpp_cell_resources),
+};
+
+static const struct resource rtc_cell_resources[] __devinitconst = {
+	[0] = SINGLE_IRQ_RESOURCE(NULL, PM8018_RTC_ALARM_IRQ),
+	[1] = {
+		.name   = "pmic_rtc_base",
+		.start  = REG_RTC_BASE,
+		.end    = REG_RTC_BASE,
+		.flags  = IORESOURCE_IO,
+	},
+};
+
+static struct mfd_cell rtc_cell __devinitdata = {
+	.name           = PM8XXX_RTC_DEV_NAME,
+	.id             = -1,
+	.resources      = rtc_cell_resources,
+	.num_resources  = ARRAY_SIZE(rtc_cell_resources),
+};
+
+static const struct resource resources_pwrkey[] __devinitconst = {
+	SINGLE_IRQ_RESOURCE(NULL, PM8018_PWRKEY_REL_IRQ),
+	SINGLE_IRQ_RESOURCE(NULL, PM8018_PWRKEY_PRESS_IRQ),
+};
+
+static struct mfd_cell pwrkey_cell __devinitdata = {
+	.name		= PM8XXX_PWRKEY_DEV_NAME,
+	.id		= -1,
+	.num_resources	= ARRAY_SIZE(resources_pwrkey),
+	.resources	= resources_pwrkey,
+};
+
+static struct mfd_cell misc_cell __devinitdata = {
+	.name           = PM8XXX_MISC_DEV_NAME,
+	.id             = -1,
+};
+
+static struct mfd_cell debugfs_cell __devinitdata = {
+	.name		= "pm8xxx-debug",
+	.id		= -1,
+	.platform_data	= "pm8018-dbg",
+	.pdata_size	= sizeof("pm8018-dbg"),
+};
+
+static struct mfd_cell pwm_cell __devinitdata = {
+	.name           = PM8XXX_PWM_DEV_NAME,
+	.id             = -1,
+};
+
+static struct mfd_cell leds_cell __devinitdata = {
+	.name		= PM8XXX_LEDS_DEV_NAME,
+	.id		= -1,
+};
+
+static const struct resource thermal_alarm_cell_resources[] __devinitconst = {
+	SINGLE_IRQ_RESOURCE("pm8018_tempstat_irq", PM8018_TEMPSTAT_IRQ),
+	SINGLE_IRQ_RESOURCE("pm8018_overtemp_irq", PM8018_OVERTEMP_IRQ),
+};
+
+static struct pm8xxx_tm_core_data thermal_alarm_cdata = {
+	.adc_channel =			CHANNEL_DIE_TEMP,
+	.adc_type =			PM8XXX_TM_ADC_PM8XXX_ADC,
+	.reg_addr_temp_alarm_ctrl =	REG_TEMP_ALARM_CTRL,
+	.reg_addr_temp_alarm_pwm =	REG_TEMP_ALARM_PWM,
+	.tm_name =			"pm8018_tz",
+	.irq_name_temp_stat =		"pm8018_tempstat_irq",
+	.irq_name_over_temp =		"pm8018_overtemp_irq",
+};
+
+static struct mfd_cell thermal_alarm_cell __devinitdata = {
+	.name		= PM8XXX_TM_DEV_NAME,
+	.id		= -1,
+	.resources	= thermal_alarm_cell_resources,
+	.num_resources	= ARRAY_SIZE(thermal_alarm_cell_resources),
+	.platform_data	= &thermal_alarm_cdata,
+	.pdata_size	= sizeof(struct pm8xxx_tm_core_data),
+};
+
+static struct pm8xxx_vreg regulator_data[] = {
+	/*   name	     pc_name	    ctrl   test   hpm_min */
+	PLDO("8018_l2",      "8018_l2_pc",  0x0B0, 0x0B1, LDO_50),
+	PLDO("8018_l3",      "8018_l3_pc",  0x0B2, 0x0B3, LDO_50),
+	PLDO("8018_l4",      "8018_l4_pc",  0x0B4, 0x0B5, LDO_300),
+	PLDO("8018_l5",      "8018_l5_pc",  0x0B6, 0x0B7, LDO_150),
+	PLDO("8018_l6",      "8018_l6_pc",  0x0B8, 0x0B9, LDO_150),
+	PLDO("8018_l7",      "8018_l7_pc",  0x0BA, 0x0BB, LDO_300),
+	NLDO("8018_l8",      "8018_l8_pc",  0x0BC, 0x0BD, LDO_150),
+	NLDO1200("8018_l9",		    0x0BE, 0x0BF, LDO_1200),
+	NLDO1200("8018_l10",		    0x0C0, 0x0C1, LDO_1200),
+	NLDO1200("8018_l11",		    0x0C2, 0x0C3, LDO_1200),
+	NLDO1200("8018_l12",		    0x0C4, 0x0C5, LDO_1200),
+	PLDO("8018_l13",     "8018_l13_pc", 0x0C8, 0x0C9, LDO_50),
+	PLDO("8018_l14",     "8018_l14_pc", 0x0CA, 0x0CB, LDO_50),
+
+	/*   name	pc_name       ctrl   test2  clk    sleep  hpm_min */
+	SMPS("8018_s1", "8018_s1_pc", 0x1D0, 0x1D5, 0x009, 0x1D2, SMPS_1500),
+	SMPS("8018_s2", "8018_s2_pc", 0x1D8, 0x1DD, 0x00A, 0x1DA, SMPS_1500),
+	SMPS("8018_s3", "8018_s3_pc", 0x1E0, 0x1E5, 0x00B, 0x1E2, SMPS_1500),
+	SMPS("8018_s4", "8018_s4_pc", 0x1E8, 0x1ED, 0x00C, 0x1EA, SMPS_1500),
+	SMPS("8018_s5", "8018_s5_pc", 0x1F0, 0x1F5, 0x00D, 0x1F2, SMPS_1500),
+
+	/* name		     pc_name	     ctrl   test */
+	VS("8018_lvs1",      "8018_lvs1_pc", 0x060, 0x061),
+};
+
+#define MAX_NAME_COMPARISON_LEN 32
+
+static int __devinit match_regulator(
+	struct pm8xxx_regulator_core_platform_data *core_data, const char *name)
+{
+	int found = 0;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(regulator_data); i++) {
+		if (regulator_data[i].rdesc.name
+		    && strncmp(regulator_data[i].rdesc.name, name,
+				MAX_NAME_COMPARISON_LEN) == 0) {
+			core_data->is_pin_controlled = false;
+			core_data->vreg = &regulator_data[i];
+			found = 1;
+			break;
+		} else if (regulator_data[i].rdesc_pc.name
+			   && strncmp(regulator_data[i].rdesc_pc.name, name,
+				MAX_NAME_COMPARISON_LEN) == 0) {
+			core_data->is_pin_controlled = true;
+			core_data->vreg = &regulator_data[i];
+			found = 1;
+			break;
+		}
+	}
+
+	if (!found)
+		pr_err("could not find a match for regulator: %s\n", name);
+
+	return found;
+}
+
+static int __devinit
+pm8018_add_regulators(const struct pm8018_platform_data *pdata,
+		      struct pm8018 *pmic, int irq_base)
+{
+	int ret = 0;
+	struct mfd_cell *mfd_regulators;
+	struct pm8xxx_regulator_core_platform_data *cdata;
+	int i;
+
+	/* Add one device for each regulator used by the board. */
+	mfd_regulators = kzalloc(sizeof(struct mfd_cell)
+				 * (pdata->num_regulators), GFP_KERNEL);
+	if (!mfd_regulators) {
+		pr_err("Cannot allocate %d bytes for pm8018 regulator "
+			"mfd cells\n", sizeof(struct mfd_cell)
+					* (pdata->num_regulators));
+		return -ENOMEM;
+	}
+	cdata = kzalloc(sizeof(struct pm8xxx_regulator_core_platform_data)
+			* pdata->num_regulators, GFP_KERNEL);
+	if (!cdata) {
+		pr_err("Cannot allocate %d bytes for pm8018 regulator "
+			"core data\n", pdata->num_regulators
+			  * sizeof(struct pm8xxx_regulator_core_platform_data));
+		kfree(mfd_regulators);
+		return -ENOMEM;
+	}
+	for (i = 0; i < ARRAY_SIZE(regulator_data); i++)
+		mutex_init(&regulator_data[i].pc_lock);
+
+	for (i = 0; i < pdata->num_regulators; i++) {
+		if (!pdata->regulator_pdatas[i].init_data.constraints.name) {
+			pr_err("name missing for regulator %d\n", i);
+			ret = -EINVAL;
+			goto bail;
+		}
+		if (!match_regulator(&cdata[i],
+		      pdata->regulator_pdatas[i].init_data.constraints.name)) {
+			ret = -ENODEV;
+			goto bail;
+		}
+		cdata[i].pdata = &(pdata->regulator_pdatas[i]);
+		mfd_regulators[i].name = PM8XXX_REGULATOR_DEV_NAME;
+		mfd_regulators[i].id = cdata[i].pdata->id;
+		mfd_regulators[i].platform_data = &cdata[i];
+		mfd_regulators[i].pdata_size =
+			sizeof(struct pm8xxx_regulator_core_platform_data);
+	}
+	ret = mfd_add_devices(pmic->dev, 0, mfd_regulators,
+			pdata->num_regulators, NULL, irq_base);
+	if (ret)
+		goto bail;
+
+	pmic->mfd_regulators = mfd_regulators;
+	pmic->regulator_cdata = cdata;
+	return ret;
+
+bail:
+	for (i = 0; i < ARRAY_SIZE(regulator_data); i++)
+		mutex_destroy(&regulator_data[i].pc_lock);
+	kfree(mfd_regulators);
+	kfree(cdata);
+	return ret;
+}
+
+static int __devinit
+pm8018_add_subdevices(const struct pm8018_platform_data *pdata,
+		      struct pm8018 *pmic)
+{
+	int ret = 0, irq_base = 0;
+	struct pm_irq_chip *irq_chip;
+
+	if (pdata->irq_pdata) {
+		pdata->irq_pdata->irq_cdata.nirqs = PM8018_NR_IRQS;
+		pdata->irq_pdata->irq_cdata.base_addr = REG_IRQ_BASE;
+		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;
+	}
+
+	if (pdata->gpio_pdata) {
+		pdata->gpio_pdata->gpio_cdata.ngpios = PM8018_NR_GPIOS;
+		gpio_cell.platform_data = pdata->gpio_pdata;
+		gpio_cell.pdata_size = sizeof(struct pm8xxx_gpio_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &gpio_cell, 1,
+					NULL, irq_base);
+		if (ret) {
+			pr_err("Failed to add  gpio subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
+	if (pdata->mpp_pdata) {
+		pdata->mpp_pdata->core_data.nmpps = PM8018_NR_MPPS;
+		pdata->mpp_pdata->core_data.base_addr = REG_MPP_BASE;
+		mpp_cell.platform_data = pdata->mpp_pdata;
+		mpp_cell.pdata_size = sizeof(struct pm8xxx_mpp_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &mpp_cell, 1, NULL,
+					irq_base);
+		if (ret) {
+			pr_err("Failed to add mpp subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
+	if (pdata->rtc_pdata) {
+		rtc_cell.platform_data = pdata->rtc_pdata;
+		rtc_cell.pdata_size = sizeof(struct pm8xxx_rtc_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &rtc_cell, 1, NULL,
+				irq_base);
+		if (ret) {
+			pr_err("Failed to add rtc subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
+	if (pdata->pwrkey_pdata) {
+		pwrkey_cell.platform_data = pdata->pwrkey_pdata;
+		pwrkey_cell.pdata_size =
+			sizeof(struct pm8xxx_pwrkey_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &pwrkey_cell, 1, NULL,
+					irq_base);
+		if (ret) {
+			pr_err("Failed to add pwrkey subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
+	if (pdata->misc_pdata) {
+		misc_cell.platform_data = pdata->misc_pdata;
+		misc_cell.pdata_size = sizeof(struct pm8xxx_misc_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &misc_cell, 1, NULL,
+				      irq_base);
+		if (ret) {
+			pr_err("Failed to add  misc subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
+	if (pdata->adc_pdata) {
+		adc_cell.platform_data = pdata->adc_pdata;
+		adc_cell.pdata_size = sizeof(struct pm8xxx_adc_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &adc_cell, 1, NULL,
+				      irq_base);
+		if (ret) {
+			pr_err("Failed to add adc subdevice ret=%d\n", ret);
+		}
+	}
+
+	if (pdata->leds_pdata) {
+		leds_cell.platform_data = pdata->leds_pdata;
+		leds_cell.pdata_size = sizeof(struct pm8xxx_led_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &leds_cell, 1, NULL, 0);
+		if (ret) {
+			pr_err("Failed to add leds subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
+	ret = mfd_add_devices(pmic->dev, 0, &debugfs_cell, 1, NULL, irq_base);
+	if (ret) {
+		pr_err("Failed to add debugfs subdevice ret=%d\n", ret);
+		goto bail;
+	}
+
+	ret = mfd_add_devices(pmic->dev, 0, &pwm_cell, 1, NULL, 0);
+	if (ret) {
+		pr_err("Failed to add pwm subdevice ret=%d\n", ret);
+		goto bail;
+	}
+
+	if (pdata->num_regulators > 0 && pdata->regulator_pdatas) {
+		ret = pm8018_add_regulators(pdata, pmic, irq_base);
+		if (ret) {
+			pr_err("Failed to add regulator subdevices ret=%d\n",
+				ret);
+			goto bail;
+		}
+	}
+
+	ret = mfd_add_devices(pmic->dev, 0, &thermal_alarm_cell, 1, NULL,
+				irq_base);
+	if (ret) {
+		pr_err("Failed to add thermal alarm subdevice, ret=%d\n", ret);
+		goto bail;
+	}
+
+	return 0;
+bail:
+	if (pmic->irq_chip) {
+		pm8xxx_irq_exit(pmic->irq_chip);
+		pmic->irq_chip = NULL;
+	}
+	return ret;
+}
+
+static const char * const pm8018_restart_reason[] = {
+	[0] = "Unknown",
+	[1] = "Triggered from CBL (external charger)",
+	[2] = "Triggered from KPD (power key press)",
+	[3] = "Triggered from CHG (usb charger insertion)",
+	[4] = "Triggered from SMPL (sudden momentary power loss)",
+	[5] = "Triggered from RTC (real time clock)",
+	[6] = "Triggered by Hard Reset",
+	[7] = "Triggered by General Purpose Trigger",
+};
+
+static const char * const pm8018_rev_names[] = {
+	[PM8XXX_REVISION_8018_TEST]	= "test",
+	[PM8XXX_REVISION_8018_1p0]	= "1.0",
+	[PM8XXX_REVISION_8018_2p0]	= "2.0",
+	[PM8XXX_REVISION_8018_2p1]	= "2.1",
+};
+
+static int __devinit pm8018_probe(struct platform_device *pdev)
+{
+	const struct pm8018_platform_data *pdata = pdev->dev.platform_data;
+	const char *revision_name = "unknown";
+	struct pm8018 *pmic;
+	enum pm8xxx_version version;
+	int revision;
+	int rc;
+	u8 val;
+
+	if (!pdata) {
+		pr_err("missing platform data\n");
+		return -EINVAL;
+	}
+
+	pmic = kzalloc(sizeof(struct pm8018), GFP_KERNEL);
+	if (!pmic) {
+		pr_err("Cannot alloc pm8018 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 1 reg %d:rc=%d\n", REG_HWREV, rc);
+		goto err_read_rev;
+	}
+	pr_info("PMIC revision 1: %02X\n", val);
+	pmic->rev_registers = 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);
+	pmic->rev_registers |= val << BITS_PER_BYTE;
+
+	pmic->dev = &pdev->dev;
+	pm8018_drvdata.pm_chip_data = pmic;
+	platform_set_drvdata(pdev, &pm8018_drvdata);
+
+	/* Print out human readable version and revision names. */
+	version = pm8xxx_get_version(pmic->dev);
+	if (version == PM8XXX_VERSION_8018) {
+		revision = pm8xxx_get_revision(pmic->dev);
+		if (revision >= 0 && revision < ARRAY_SIZE(pm8018_rev_names))
+			revision_name = pm8018_rev_names[revision];
+		pr_info("PMIC version: PM8018 rev %s\n", revision_name);
+	} else {
+		WARN_ON(version != PM8XXX_VERSION_8018);
+	}
+	/* Log human readable restart reason */
+	rc = msm_ssbi_read(pdev->dev.parent, REG_PM8018_PON_CNTRL_3, &val, 1);
+	if (rc) {
+		pr_err("Cannot read restart reason rc=%d\n", rc);
+		goto err_read_rev;
+	}
+	val &= PM8018_RESTART_REASON_MASK;
+	pr_info("PMIC Restart Reason: %s\n", pm8018_restart_reason[val]);
+
+	rc = pm8018_add_subdevices(pdata, pmic);
+	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);
+	kfree(pmic->mfd_regulators);
+	kfree(pmic->regulator_cdata);
+err_read_rev:
+	kfree(pmic);
+	return rc;
+}
+
+static int __devexit pm8018_remove(struct platform_device *pdev)
+{
+	struct pm8xxx_drvdata *drvdata;
+	struct pm8018 *pmic = NULL;
+	int i;
+
+	drvdata = platform_get_drvdata(pdev);
+	if (drvdata)
+		pmic = drvdata->pm_chip_data;
+	if (pmic) {
+		if (pmic->dev)
+			mfd_remove_devices(pmic->dev);
+		if (pmic->irq_chip) {
+			pm8xxx_irq_exit(pmic->irq_chip);
+			pmic->irq_chip = NULL;
+		}
+		if (pmic->mfd_regulators) {
+			for (i = 0; i < ARRAY_SIZE(regulator_data); i++)
+				mutex_destroy(&regulator_data[i].pc_lock);
+		}
+		kfree(pmic->mfd_regulators);
+		kfree(pmic->regulator_cdata);
+		kfree(pmic);
+	}
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+static struct platform_driver pm8018_driver = {
+	.probe		= pm8018_probe,
+	.remove		= __devexit_p(pm8018_remove),
+	.driver		= {
+		.name	= PM8018_CORE_DEV_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init pm8018_init(void)
+{
+	return platform_driver_register(&pm8018_driver);
+}
+postcore_initcall(pm8018_init);
+
+static void __exit pm8018_exit(void)
+{
+	platform_driver_unregister(&pm8018_driver);
+}
+module_exit(pm8018_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC 8018 core driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:" PM8018_CORE_DEV_NAME);
diff --git a/drivers/mfd/pm8038-core.c b/drivers/mfd/pm8038-core.c
new file mode 100644
index 0000000..4271a2a
--- /dev/null
+++ b/drivers/mfd/pm8038-core.c
@@ -0,0 +1,775 @@
+/*
+ * Copyright (c) 2011-2012, 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/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/string.h>
+#include <linux/msm_ssbi.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/pm8xxx/pm8038.h>
+#include <linux/mfd/pm8xxx/pm8921.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/mfd/pm8xxx/regulator.h>
+
+#define REG_HWREV		0x002  /* PMIC4 revision */
+#define REG_HWREV_2		0x0E8  /* PMIC4 revision 2 */
+
+#define REG_MPP_BASE		0x050
+#define REG_RTC_BASE		0x11D
+#define REG_IRQ_BASE            0x1BB
+
+#define REG_SPK_BASE		0x253
+#define REG_SPK_REGISTERS	3
+
+#define PM8038_VERSION_MASK	0xFFF0
+#define PM8038_VERSION_VALUE	0x09F0
+#define PM8038_REVISION_MASK	0x000F
+
+#define REG_PM8038_PON_CNTRL_3	0x01D
+#define PM8038_RESTART_REASON_MASK	0x07
+
+#define SINGLE_IRQ_RESOURCE(_name, _irq) \
+{ \
+	.name	= _name, \
+	.start	= _irq, \
+	.end	= _irq, \
+	.flags	= IORESOURCE_IRQ, \
+}
+
+struct pm8038 {
+	struct device					*dev;
+	struct pm_irq_chip				*irq_chip;
+	struct mfd_cell					*mfd_regulators;
+	struct pm8xxx_regulator_core_platform_data	*regulator_cdata;
+	u32						rev_registers;
+};
+
+static int pm8038_readb(const struct device *dev, u16 addr, u8 *val)
+{
+	const struct pm8xxx_drvdata *pm8038_drvdata = dev_get_drvdata(dev);
+	const struct pm8038 *pmic = pm8038_drvdata->pm_chip_data;
+
+	return msm_ssbi_read(pmic->dev->parent, addr, val, 1);
+}
+
+static int pm8038_writeb(const struct device *dev, u16 addr, u8 val)
+{
+	const struct pm8xxx_drvdata *pm8038_drvdata = dev_get_drvdata(dev);
+	const struct pm8038 *pmic = pm8038_drvdata->pm_chip_data;
+
+	return msm_ssbi_write(pmic->dev->parent, addr, &val, 1);
+}
+
+static int pm8038_read_buf(const struct device *dev, u16 addr, u8 *buf,
+									int cnt)
+{
+	const struct pm8xxx_drvdata *pm8038_drvdata = dev_get_drvdata(dev);
+	const struct pm8038 *pmic = pm8038_drvdata->pm_chip_data;
+
+	return msm_ssbi_read(pmic->dev->parent, addr, buf, cnt);
+}
+
+static int pm8038_write_buf(const struct device *dev, u16 addr, u8 *buf,
+									int cnt)
+{
+	const struct pm8xxx_drvdata *pm8038_drvdata = dev_get_drvdata(dev);
+	const struct pm8038 *pmic = pm8038_drvdata->pm_chip_data;
+
+	return msm_ssbi_write(pmic->dev->parent, addr, buf, cnt);
+}
+
+static int pm8038_read_irq_stat(const struct device *dev, int irq)
+{
+	const struct pm8xxx_drvdata *pm8038_drvdata = dev_get_drvdata(dev);
+	const struct pm8038 *pmic = pm8038_drvdata->pm_chip_data;
+
+	return pm8xxx_get_irq_stat(pmic->irq_chip, irq);
+}
+
+static enum pm8xxx_version pm8038_get_version(const struct device *dev)
+{
+	const struct pm8xxx_drvdata *pm8038_drvdata = dev_get_drvdata(dev);
+	const struct pm8038 *pmic = pm8038_drvdata->pm_chip_data;
+	enum pm8xxx_version version = -ENODEV;
+
+	if ((pmic->rev_registers & PM8038_VERSION_MASK) == PM8038_VERSION_VALUE)
+		version = PM8XXX_VERSION_8038;
+
+	return version;
+}
+
+static int pm8038_get_revision(const struct device *dev)
+{
+	const struct pm8xxx_drvdata *pm8038_drvdata = dev_get_drvdata(dev);
+	const struct pm8038 *pmic = pm8038_drvdata->pm_chip_data;
+
+	return pmic->rev_registers & PM8038_REVISION_MASK;
+}
+
+static struct pm8xxx_drvdata pm8038_drvdata = {
+	.pmic_readb		= pm8038_readb,
+	.pmic_writeb		= pm8038_writeb,
+	.pmic_read_buf		= pm8038_read_buf,
+	.pmic_write_buf		= pm8038_write_buf,
+	.pmic_read_irq_stat	= pm8038_read_irq_stat,
+	.pmic_get_version	= pm8038_get_version,
+	.pmic_get_revision	= pm8038_get_revision,
+};
+
+static const struct resource gpio_cell_resources[] __devinitconst = {
+	[0] = {
+		.start = PM8038_IRQ_BLOCK_BIT(PM8038_GPIO_BLOCK_START, 0),
+		.end   = PM8038_IRQ_BLOCK_BIT(PM8038_GPIO_BLOCK_START, 0)
+			+ PM8038_NR_GPIOS - 1,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct mfd_cell gpio_cell __devinitdata = {
+	.name		= PM8XXX_GPIO_DEV_NAME,
+	.id		= -1,
+	.resources	= gpio_cell_resources,
+	.num_resources	= ARRAY_SIZE(gpio_cell_resources),
+};
+
+static const struct resource adc_cell_resources[] __devinitconst = {
+	SINGLE_IRQ_RESOURCE(NULL, PM8038_ADC_EOC_USR_IRQ),
+	SINGLE_IRQ_RESOURCE(NULL, PM8038_ADC_BATT_TEMP_WARM_IRQ),
+	SINGLE_IRQ_RESOURCE(NULL, PM8038_ADC_BATT_TEMP_COLD_IRQ),
+};
+
+static struct mfd_cell adc_cell __devinitdata = {
+	.name		= PM8XXX_ADC_DEV_NAME,
+	.id		= -1,
+	.resources	= adc_cell_resources,
+	.num_resources	= ARRAY_SIZE(adc_cell_resources),
+};
+
+static const struct resource charger_cell_resources[] __devinitconst = {
+	SINGLE_IRQ_RESOURCE("USBIN_VALID_IRQ", PM8921_USBIN_VALID_IRQ),
+	SINGLE_IRQ_RESOURCE("USBIN_OV_IRQ", PM8921_USBIN_OV_IRQ),
+	SINGLE_IRQ_RESOURCE("BATT_INSERTED_IRQ", PM8921_BATT_INSERTED_IRQ),
+	SINGLE_IRQ_RESOURCE("VBATDET_LOW_IRQ", PM8921_VBATDET_LOW_IRQ),
+	SINGLE_IRQ_RESOURCE("USBIN_UV_IRQ", PM8921_USBIN_UV_IRQ),
+	SINGLE_IRQ_RESOURCE("VBAT_OV_IRQ", PM8921_VBAT_OV_IRQ),
+	SINGLE_IRQ_RESOURCE("CHGWDOG_IRQ", PM8921_CHGWDOG_IRQ),
+	SINGLE_IRQ_RESOURCE("VCP_IRQ", PM8921_VCP_IRQ),
+	SINGLE_IRQ_RESOURCE("ATCDONE_IRQ", PM8921_ATCDONE_IRQ),
+	SINGLE_IRQ_RESOURCE("ATCFAIL_IRQ", PM8921_ATCFAIL_IRQ),
+	SINGLE_IRQ_RESOURCE("CHGDONE_IRQ", PM8921_CHGDONE_IRQ),
+	SINGLE_IRQ_RESOURCE("CHGFAIL_IRQ", PM8921_CHGFAIL_IRQ),
+	SINGLE_IRQ_RESOURCE("CHGSTATE_IRQ", PM8921_CHGSTATE_IRQ),
+	SINGLE_IRQ_RESOURCE("LOOP_CHANGE_IRQ", PM8921_LOOP_CHANGE_IRQ),
+	SINGLE_IRQ_RESOURCE("FASTCHG_IRQ", PM8921_FASTCHG_IRQ),
+	SINGLE_IRQ_RESOURCE("TRKLCHG_IRQ", PM8921_TRKLCHG_IRQ),
+	SINGLE_IRQ_RESOURCE("BATT_REMOVED_IRQ", PM8921_BATT_REMOVED_IRQ),
+	SINGLE_IRQ_RESOURCE("BATTTEMP_HOT_IRQ", PM8921_BATTTEMP_HOT_IRQ),
+	SINGLE_IRQ_RESOURCE("CHGHOT_IRQ", PM8921_CHGHOT_IRQ),
+	SINGLE_IRQ_RESOURCE("BATTTEMP_COLD_IRQ", PM8921_BATTTEMP_COLD_IRQ),
+	SINGLE_IRQ_RESOURCE("CHG_GONE_IRQ", PM8921_CHG_GONE_IRQ),
+	SINGLE_IRQ_RESOURCE("BAT_TEMP_OK_IRQ", PM8921_BAT_TEMP_OK_IRQ),
+	SINGLE_IRQ_RESOURCE("COARSE_DET_LOW_IRQ", PM8921_COARSE_DET_LOW_IRQ),
+	SINGLE_IRQ_RESOURCE("VDD_LOOP_IRQ", PM8921_VDD_LOOP_IRQ),
+	SINGLE_IRQ_RESOURCE("VREG_OV_IRQ", PM8921_VREG_OV_IRQ),
+	SINGLE_IRQ_RESOURCE("VBATDET_IRQ", PM8921_VBATDET_IRQ),
+	SINGLE_IRQ_RESOURCE("BATFET_IRQ", PM8921_BATFET_IRQ),
+	SINGLE_IRQ_RESOURCE("PSI_IRQ", PM8921_PSI_IRQ),
+	SINGLE_IRQ_RESOURCE("DCIN_VALID_IRQ", PM8921_DCIN_VALID_IRQ),
+	SINGLE_IRQ_RESOURCE("DCIN_OV_IRQ", PM8921_DCIN_OV_IRQ),
+	SINGLE_IRQ_RESOURCE("DCIN_UV_IRQ", PM8921_DCIN_UV_IRQ),
+};
+
+static const struct resource bms_cell_resources[] __devinitconst = {
+	SINGLE_IRQ_RESOURCE("PM8921_BMS_SBI_WRITE_OK", PM8921_BMS_SBI_WRITE_OK),
+	SINGLE_IRQ_RESOURCE("PM8921_BMS_CC_THR", PM8921_BMS_CC_THR),
+	SINGLE_IRQ_RESOURCE("PM8921_BMS_VSENSE_THR", PM8921_BMS_VSENSE_THR),
+	SINGLE_IRQ_RESOURCE("PM8921_BMS_VSENSE_FOR_R", PM8921_BMS_VSENSE_FOR_R),
+	SINGLE_IRQ_RESOURCE("PM8921_BMS_OCV_FOR_R", PM8921_BMS_OCV_FOR_R),
+	SINGLE_IRQ_RESOURCE("PM8921_BMS_GOOD_OCV", PM8921_BMS_GOOD_OCV),
+	SINGLE_IRQ_RESOURCE("PM8921_BMS_VSENSE_AVG", PM8921_BMS_VSENSE_AVG),
+};
+
+static struct mfd_cell charger_cell __devinitdata = {
+	.name		= PM8921_CHARGER_DEV_NAME,
+	.id		= -1,
+	.resources	= charger_cell_resources,
+	.num_resources	= ARRAY_SIZE(charger_cell_resources),
+};
+
+static struct mfd_cell bms_cell __devinitdata = {
+	.name		= PM8921_BMS_DEV_NAME,
+	.id		= -1,
+	.resources	= bms_cell_resources,
+	.num_resources	= ARRAY_SIZE(bms_cell_resources),
+};
+static const struct resource mpp_cell_resources[] __devinitconst = {
+	{
+		.start	= PM8038_IRQ_BLOCK_BIT(PM8038_MPP_BLOCK_START, 0),
+		.end	= PM8038_IRQ_BLOCK_BIT(PM8038_MPP_BLOCK_START, 0)
+			  + PM8038_NR_MPPS - 1,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct mfd_cell mpp_cell __devinitdata = {
+	.name		= PM8XXX_MPP_DEV_NAME,
+	.id		= 1,
+	.resources	= mpp_cell_resources,
+	.num_resources	= ARRAY_SIZE(mpp_cell_resources),
+};
+
+static const struct resource rtc_cell_resources[] __devinitconst = {
+	[0] = SINGLE_IRQ_RESOURCE(NULL, PM8038_RTC_ALARM_IRQ),
+	[1] = {
+		.name   = "pmic_rtc_base",
+		.start  = REG_RTC_BASE,
+		.end    = REG_RTC_BASE,
+		.flags  = IORESOURCE_IO,
+	},
+};
+
+static struct mfd_cell rtc_cell __devinitdata = {
+	.name           = PM8XXX_RTC_DEV_NAME,
+	.id             = -1,
+	.resources      = rtc_cell_resources,
+	.num_resources  = ARRAY_SIZE(rtc_cell_resources),
+};
+
+static const struct resource resources_pwrkey[] __devinitconst = {
+	SINGLE_IRQ_RESOURCE(NULL, PM8038_PWRKEY_REL_IRQ),
+	SINGLE_IRQ_RESOURCE(NULL, PM8038_PWRKEY_PRESS_IRQ),
+};
+
+static struct mfd_cell pwrkey_cell __devinitdata = {
+	.name		= PM8XXX_PWRKEY_DEV_NAME,
+	.id		= -1,
+	.num_resources	= ARRAY_SIZE(resources_pwrkey),
+	.resources	= resources_pwrkey,
+};
+
+static struct mfd_cell pwm_cell __devinitdata = {
+	.name           = PM8XXX_PWM_DEV_NAME,
+	.id             = -1,
+};
+
+static struct mfd_cell misc_cell __devinitdata = {
+	.name           = PM8XXX_MISC_DEV_NAME,
+	.id             = -1,
+};
+
+static struct mfd_cell leds_cell __devinitdata = {
+	.name		= PM8XXX_LEDS_DEV_NAME,
+	.id		= -1,
+};
+
+static const struct resource resources_spk[] __devinitconst = {
+	[0] = {
+		.name   = PM8XXX_SPK_DEV_NAME,
+		.start  = REG_SPK_BASE,
+		.end    = REG_SPK_BASE + REG_SPK_REGISTERS,
+		.flags  = IORESOURCE_IO,
+	},
+};
+
+static struct mfd_cell spk_cell __devinitdata = {
+	.name           = PM8XXX_SPK_DEV_NAME,
+	.id             = -1,
+	.num_resources	= ARRAY_SIZE(resources_spk),
+	.resources	= resources_spk,
+};
+
+static struct mfd_cell debugfs_cell __devinitdata = {
+	.name		= "pm8xxx-debug",
+	.id		= 0,
+	.platform_data	= "pm8038-dbg",
+	.pdata_size	= sizeof("pm8038-dbg"),
+};
+
+static struct pm8xxx_vreg regulator_data[] = {
+	/*   name	     pc_name	    ctrl   test   hpm_min */
+	NLDO1200("8038_l1",		    0x0AE, 0x0AF, LDO_1200),
+	NLDO("8038_l2",      "8038_l2_pc",  0x0B0, 0x0B1, LDO_150),
+	PLDO("8038_l3",      "8038_l3_pc",  0x0B2, 0x0B3, LDO_50),
+	PLDO("8038_l4",      "8038_l4_pc",  0x0B4, 0x0B5, LDO_50),
+	PLDO("8038_l5",      "8038_l5_pc",  0x0B6, 0x0B7, LDO_600),
+	PLDO("8038_l6",      "8038_l6_pc",  0x0B8, 0x0B9, LDO_600),
+	PLDO("8038_l7",      "8038_l7_pc",  0x0BA, 0x0BB, LDO_600),
+	PLDO("8038_l8",      "8038_l8_pc",  0x0BC, 0x0BD, LDO_300),
+	PLDO("8038_l9",      "8038_l9_pc",  0x0BE, 0x0BF, LDO_300),
+	PLDO("8038_l10",     "8038_l10_pc", 0x0C0, 0x0C1, LDO_600),
+	PLDO("8038_l11",     "8038_l11_pc", 0x0C2, 0x0C3, LDO_600),
+	NLDO("8038_l12",     "8038_l12_pc", 0x0C4, 0x0C5, LDO_300),
+	PLDO("8038_l14",     "8038_l14_pc", 0x0C8, 0x0C9, LDO_50),
+	PLDO("8038_l15",     "8038_l15_pc", 0x0CA, 0x0CB, LDO_150),
+	NLDO1200("8038_l16",		    0x0CC, 0x0CD, LDO_1200),
+	PLDO("8038_l17",     "8038_l17_pc", 0x0CE, 0x0CF, LDO_150),
+	PLDO("8038_l18",     "8038_l18_pc", 0x0D0, 0x0D1, LDO_50),
+	NLDO1200("8038_l19",		    0x0D2, 0x0D3, LDO_1200),
+	NLDO1200("8038_l20",		    0x0D4, 0x0D5, LDO_1200),
+	PLDO("8038_l21",     "8038_l21_pc", 0x0D6, 0x0D7, LDO_150),
+	PLDO("8038_l22",     "8038_l22_pc", 0x0D8, 0x0D9, LDO_50),
+	PLDO("8038_l23",     "8038_l23_pc", 0x0DA, 0x0DB, LDO_50),
+	NLDO1200("8038_l24",		    0x0DC, 0x0DD, LDO_1200),
+	NLDO("8038_l26",     "8038_l26_pc", 0x0E0, 0x0E1, LDO_150),
+	NLDO1200("8038_l27",		    0x0E2, 0x0E3, LDO_1200),
+
+	/*   name	pc_name       ctrl   test2  clk    sleep  hpm_min */
+	SMPS("8038_s1", "8038_s1_pc", 0x1E0, 0x1E5, 0x009, 0x1E2, SMPS_1500),
+	SMPS("8038_s2", "8038_s2_pc", 0x1D8, 0x1DD, 0x00A, 0x1DA, SMPS_1500),
+	SMPS("8038_s3", "8038_s3_pc", 0x1D0, 0x1D5, 0x00B, 0x1D2, SMPS_1500),
+	SMPS("8038_s4", "8038_s4_pc", 0x1E8, 0x1ED, 0x00C, 0x1EA, SMPS_1500),
+
+	/*     name	  ctrl fts_cnfg1 pfm  pwr_cnfg  hpm_min */
+	FTSMPS("8038_s5", 0x025, 0x02E, 0x026, 0x032, SMPS_2000),
+	FTSMPS("8038_s6", 0x036, 0x03F, 0x037, 0x043, SMPS_2000),
+
+	/* name		       pc_name	       ctrl   test */
+	VS("8038_lvs1",        "8038_lvs1_pc", 0x060, 0x061),
+	VS("8038_lvs2",        "8038_lvs2_pc", 0x062, 0x063),
+};
+
+#define MAX_NAME_COMPARISON_LEN 32
+
+static int __devinit match_regulator(
+	struct pm8xxx_regulator_core_platform_data *core_data, const char *name)
+{
+	int found = 0;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(regulator_data); i++) {
+		if (regulator_data[i].rdesc.name
+		    && strncmp(regulator_data[i].rdesc.name, name,
+				MAX_NAME_COMPARISON_LEN) == 0) {
+			core_data->is_pin_controlled = false;
+			core_data->vreg = &regulator_data[i];
+			found = 1;
+			break;
+		} else if (regulator_data[i].rdesc_pc.name
+			   && strncmp(regulator_data[i].rdesc_pc.name, name,
+				MAX_NAME_COMPARISON_LEN) == 0) {
+			core_data->is_pin_controlled = true;
+			core_data->vreg = &regulator_data[i];
+			found = 1;
+			break;
+		}
+	}
+
+	if (!found)
+		pr_err("could not find a match for regulator: %s\n", name);
+
+	return found;
+}
+
+static int __devinit
+pm8038_add_regulators(const struct pm8038_platform_data *pdata,
+		      struct pm8038 *pmic, int irq_base)
+{
+	int ret = 0;
+	struct mfd_cell *mfd_regulators;
+	struct pm8xxx_regulator_core_platform_data *cdata;
+	int i;
+
+	/* Add one device for each regulator used by the board. */
+	mfd_regulators = kzalloc(sizeof(struct mfd_cell)
+				 * (pdata->num_regulators), GFP_KERNEL);
+	if (!mfd_regulators) {
+		pr_err("Cannot allocate %d bytes for pm8038 regulator "
+			"mfd cells\n", sizeof(struct mfd_cell)
+					* (pdata->num_regulators));
+		return -ENOMEM;
+	}
+	cdata = kzalloc(sizeof(struct pm8xxx_regulator_core_platform_data)
+			* pdata->num_regulators, GFP_KERNEL);
+	if (!cdata) {
+		pr_err("Cannot allocate %d bytes for pm8038 regulator "
+			"core data\n", pdata->num_regulators
+			  * sizeof(struct pm8xxx_regulator_core_platform_data));
+		kfree(mfd_regulators);
+		return -ENOMEM;
+	}
+	for (i = 0; i < ARRAY_SIZE(regulator_data); i++)
+		mutex_init(&regulator_data[i].pc_lock);
+
+	for (i = 0; i < pdata->num_regulators; i++) {
+		if (!pdata->regulator_pdatas[i].init_data.constraints.name) {
+			pr_err("name missing for regulator %d\n", i);
+			ret = -EINVAL;
+			goto bail;
+		}
+		if (!match_regulator(&cdata[i],
+		      pdata->regulator_pdatas[i].init_data.constraints.name)) {
+			ret = -ENODEV;
+			goto bail;
+		}
+		cdata[i].pdata = &(pdata->regulator_pdatas[i]);
+		mfd_regulators[i].name = PM8XXX_REGULATOR_DEV_NAME;
+		mfd_regulators[i].id = cdata[i].pdata->id;
+		mfd_regulators[i].platform_data = &cdata[i];
+		mfd_regulators[i].pdata_size =
+			sizeof(struct pm8xxx_regulator_core_platform_data);
+	}
+	ret = mfd_add_devices(pmic->dev, 0, mfd_regulators,
+			pdata->num_regulators, NULL, irq_base);
+	if (ret)
+		goto bail;
+
+	pmic->mfd_regulators = mfd_regulators;
+	pmic->regulator_cdata = cdata;
+	return ret;
+
+bail:
+	for (i = 0; i < ARRAY_SIZE(regulator_data); i++)
+		mutex_destroy(&regulator_data[i].pc_lock);
+	kfree(mfd_regulators);
+	kfree(cdata);
+	return ret;
+}
+
+static int __devinit
+pm8038_add_subdevices(const struct pm8038_platform_data *pdata,
+		      struct pm8038 *pmic)
+{
+	int ret = 0, irq_base = 0;
+	struct pm_irq_chip *irq_chip;
+
+	if (pdata->irq_pdata) {
+		pdata->irq_pdata->irq_cdata.nirqs = PM8038_NR_IRQS;
+		pdata->irq_pdata->irq_cdata.base_addr = REG_IRQ_BASE;
+		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;
+	}
+
+	if (pdata->gpio_pdata) {
+		pdata->gpio_pdata->gpio_cdata.ngpios = PM8038_NR_GPIOS;
+		gpio_cell.platform_data = pdata->gpio_pdata;
+		gpio_cell.pdata_size = sizeof(struct pm8xxx_gpio_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &gpio_cell, 1,
+					NULL, irq_base);
+		if (ret) {
+			pr_err("Failed to add  gpio subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
+	if (pdata->mpp_pdata) {
+		pdata->mpp_pdata->core_data.nmpps = PM8038_NR_MPPS;
+		pdata->mpp_pdata->core_data.base_addr = REG_MPP_BASE;
+		mpp_cell.platform_data = pdata->mpp_pdata;
+		mpp_cell.pdata_size = sizeof(struct pm8xxx_mpp_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &mpp_cell, 1, NULL,
+					irq_base);
+		if (ret) {
+			pr_err("Failed to add mpp subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
+	if (pdata->rtc_pdata) {
+		rtc_cell.platform_data = pdata->rtc_pdata;
+		rtc_cell.pdata_size = sizeof(struct pm8xxx_rtc_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &rtc_cell, 1, NULL,
+				irq_base);
+		if (ret) {
+			pr_err("Failed to add rtc subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
+	if (pdata->pwrkey_pdata) {
+		pwrkey_cell.platform_data = pdata->pwrkey_pdata;
+		pwrkey_cell.pdata_size =
+			sizeof(struct pm8xxx_pwrkey_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &pwrkey_cell, 1, NULL,
+					irq_base);
+		if (ret) {
+			pr_err("Failed to add pwrkey subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
+	ret = mfd_add_devices(pmic->dev, 0, &pwm_cell, 1, NULL, 0);
+	if (ret) {
+		pr_err("Failed to add pwm subdevice ret=%d\n", ret);
+		goto bail;
+	}
+
+	if (pdata->misc_pdata) {
+		misc_cell.platform_data = pdata->misc_pdata;
+		misc_cell.pdata_size = sizeof(struct pm8xxx_misc_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &misc_cell, 1, NULL,
+				      irq_base);
+		if (ret) {
+			pr_err("Failed to add  misc subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
+	if (pdata->leds_pdata) {
+		leds_cell.platform_data = pdata->leds_pdata;
+		leds_cell.pdata_size = sizeof(struct pm8xxx_led_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &leds_cell, 1, NULL, 0);
+		if (ret) {
+			pr_err("Failed to add leds subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
+	if (pdata->spk_pdata) {
+		spk_cell.platform_data = pdata->spk_pdata;
+		spk_cell.pdata_size = sizeof(struct pm8xxx_spk_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &spk_cell, 1, NULL, 0);
+		if (ret) {
+			pr_err("Failed to add spk subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
+	if (pdata->num_regulators > 0 && pdata->regulator_pdatas) {
+		ret = pm8038_add_regulators(pdata, pmic, irq_base);
+		if (ret) {
+			pr_err("Failed to add regulator subdevices ret=%d\n",
+				ret);
+			goto bail;
+		}
+	}
+
+	ret = mfd_add_devices(pmic->dev, 0, &debugfs_cell, 1, NULL, irq_base);
+	if (ret) {
+		pr_err("Failed to add debugfs subdevice ret=%d\n", ret);
+		goto bail;
+	}
+
+	if (pdata->adc_pdata) {
+		adc_cell.platform_data = pdata->adc_pdata;
+		adc_cell.pdata_size =
+			sizeof(struct pm8xxx_adc_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &adc_cell, 1, NULL,
+					irq_base);
+		if (ret) {
+			pr_err("Failed to add adc subdevices ret=%d\n",
+					ret);
+		}
+	}
+
+	if (pdata->charger_pdata) {
+		pdata->charger_pdata->charger_cdata.vbat_channel = CHANNEL_VBAT;
+		pdata->charger_pdata->charger_cdata.batt_temp_channel
+						= CHANNEL_BATT_THERM;
+		pdata->charger_pdata->charger_cdata.batt_id_channel
+						= CHANNEL_BATT_ID;
+		charger_cell.platform_data = pdata->charger_pdata;
+		charger_cell.pdata_size =
+				sizeof(struct pm8921_charger_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &charger_cell, 1, NULL,
+					irq_base);
+		if (ret) {
+			pr_err("Failed to add charger subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
+	if (pdata->bms_pdata) {
+		pdata->bms_pdata->bms_cdata.batt_temp_channel
+						= CHANNEL_BATT_THERM;
+		pdata->bms_pdata->bms_cdata.vbat_channel = CHANNEL_VBAT;
+		pdata->bms_pdata->bms_cdata.ref625mv_channel = CHANNEL_625MV;
+		pdata->bms_pdata->bms_cdata.ref1p25v_channel = CHANNEL_125V;
+		pdata->bms_pdata->bms_cdata.batt_id_channel = CHANNEL_BATT_ID;
+		bms_cell.platform_data = pdata->bms_pdata;
+		bms_cell.pdata_size = sizeof(struct pm8921_bms_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &bms_cell, 1, NULL,
+					irq_base);
+		if (ret) {
+			pr_err("Failed to add bms subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+	return 0;
+bail:
+	if (pmic->irq_chip) {
+		pm8xxx_irq_exit(pmic->irq_chip);
+		pmic->irq_chip = NULL;
+	}
+	return ret;
+}
+
+static const char * const pm8038_restart_reason[] = {
+	[0] = "Unknown",
+	[1] = "Triggered from CBL (external charger)",
+	[2] = "Triggered from KPD (power key press)",
+	[3] = "Triggered from CHG (usb charger insertion)",
+	[4] = "Triggered from SMPL (sudden momentary power loss)",
+	[5] = "Triggered from RTC (real time clock)",
+	[6] = "Triggered by Hard Reset",
+	[7] = "Triggered by General Purpose Trigger",
+};
+
+static const char * const pm8038_rev_names[] = {
+	[PM8XXX_REVISION_8038_TEST]	= "test",
+	[PM8XXX_REVISION_8038_1p0]	= "1.0",
+	[PM8XXX_REVISION_8038_2p0]	= "2.0",
+	[PM8XXX_REVISION_8038_2p1]	= "2.1",
+};
+
+static int __devinit pm8038_probe(struct platform_device *pdev)
+{
+	const struct pm8038_platform_data *pdata = pdev->dev.platform_data;
+	const char *revision_name = "unknown";
+	struct pm8038 *pmic;
+	enum pm8xxx_version version;
+	int revision;
+	int rc;
+	u8 val;
+
+	if (!pdata) {
+		pr_err("missing platform data\n");
+		return -EINVAL;
+	}
+
+	pmic = kzalloc(sizeof(struct pm8038), GFP_KERNEL);
+	if (!pmic) {
+		pr_err("Cannot alloc pm8038 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: PM8038 rev %02X\n", val);
+	pmic->rev_registers = 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: PM8038 rev %02X\n", val);
+	pmic->rev_registers |= val << BITS_PER_BYTE;
+
+	pmic->dev = &pdev->dev;
+	pm8038_drvdata.pm_chip_data = pmic;
+	platform_set_drvdata(pdev, &pm8038_drvdata);
+
+	/* Print out human readable version and revision names. */
+	version = pm8xxx_get_version(pmic->dev);
+	if (version == PM8XXX_VERSION_8038) {
+		revision = pm8xxx_get_revision(pmic->dev);
+		if (revision >= 0 && revision < ARRAY_SIZE(pm8038_rev_names))
+			revision_name = pm8038_rev_names[revision];
+		pr_info("PMIC version: PM8038 ver %s\n", revision_name);
+	} else {
+		WARN_ON(version != PM8XXX_VERSION_8038);
+	}
+
+	/* Log human readable restart reason */
+	rc = msm_ssbi_read(pdev->dev.parent, REG_PM8038_PON_CNTRL_3, &val, 1);
+	if (rc) {
+		pr_err("Cannot read restart reason rc=%d\n", rc);
+		goto err_read_rev;
+	}
+	val &= PM8038_RESTART_REASON_MASK;
+	pr_info("PMIC Restart Reason: %s\n", pm8038_restart_reason[val]);
+
+	rc = pm8038_add_subdevices(pdata, pmic);
+	if (rc) {
+		pr_err("Cannot add subdevices rc=%d\n", rc);
+		goto err;
+	}
+
+	return 0;
+
+err:
+	mfd_remove_devices(pmic->dev);
+	platform_set_drvdata(pdev, NULL);
+	kfree(pmic->mfd_regulators);
+	kfree(pmic->regulator_cdata);
+err_read_rev:
+	kfree(pmic);
+	return rc;
+}
+
+static int __devexit pm8038_remove(struct platform_device *pdev)
+{
+	struct pm8xxx_drvdata *drvdata;
+	struct pm8038 *pmic = NULL;
+	int i;
+
+	drvdata = platform_get_drvdata(pdev);
+
+	if (drvdata)
+		pmic = drvdata->pm_chip_data;
+
+	if (pmic) {
+		if (pmic->dev)
+			mfd_remove_devices(pmic->dev);
+
+		if (pmic->irq_chip) {
+			pm8xxx_irq_exit(pmic->irq_chip);
+			pmic->irq_chip = NULL;
+		}
+		if (pmic->mfd_regulators) {
+			for (i = 0; i < ARRAY_SIZE(regulator_data); i++)
+				mutex_destroy(&regulator_data[i].pc_lock);
+		}
+		kfree(pmic->mfd_regulators);
+		kfree(pmic->regulator_cdata);
+		kfree(pmic);
+	}
+
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+static struct platform_driver pm8038_driver = {
+	.probe		= pm8038_probe,
+	.remove		= __devexit_p(pm8038_remove),
+	.driver		= {
+		.name	= PM8038_CORE_DEV_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init pm8038_init(void)
+{
+	return platform_driver_register(&pm8038_driver);
+}
+postcore_initcall(pm8038_init);
+
+static void __exit pm8038_exit(void)
+{
+	platform_driver_unregister(&pm8038_driver);
+}
+module_exit(pm8038_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC 8038 core driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:pm8038-core");
diff --git a/drivers/mfd/pm8821-core.c b/drivers/mfd/pm8821-core.c
new file mode 100644
index 0000000..df9d2e1
--- /dev/null
+++ b/drivers/mfd/pm8821-core.c
@@ -0,0 +1,317 @@
+/*
+ * 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/module.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/pm8821.h>
+#include <linux/mfd/pm8xxx/core.h>
+
+#define REG_HWREV		0x002  /* PMIC4 revision */
+#define REG_HWREV_2		0x0E8  /* PMIC4 revision 2 */
+
+#define REG_MPP_BASE		0x050
+#define REG_IRQ_BASE		0x1BB
+
+#define PM8821_VERSION_MASK	0xFFF0
+#define PM8821_VERSION_VALUE	0x0BF0
+#define PM8821_REVISION_MASK	0x000F
+
+#define SINGLE_IRQ_RESOURCE(_name, _irq) \
+{ \
+	.name	= _name, \
+	.start	= _irq, \
+	.end	= _irq, \
+	.flags	= IORESOURCE_IRQ, \
+}
+
+struct pm8821 {
+	struct device			*dev;
+	struct pm_irq_chip		*irq_chip;
+	u32				rev_registers;
+};
+
+static int pm8821_readb(const struct device *dev, u16 addr, u8 *val)
+{
+	const struct pm8xxx_drvdata *pm8821_drvdata = dev_get_drvdata(dev);
+	const struct pm8821 *pmic = pm8821_drvdata->pm_chip_data;
+
+	return msm_ssbi_read(pmic->dev->parent, addr, val, 1);
+}
+
+static int pm8821_writeb(const struct device *dev, u16 addr, u8 val)
+{
+	const struct pm8xxx_drvdata *pm8821_drvdata = dev_get_drvdata(dev);
+	const struct pm8821 *pmic = pm8821_drvdata->pm_chip_data;
+
+	return msm_ssbi_write(pmic->dev->parent, addr, &val, 1);
+}
+
+static int pm8821_read_buf(const struct device *dev, u16 addr, u8 *buf,
+									int cnt)
+{
+	const struct pm8xxx_drvdata *pm8821_drvdata = dev_get_drvdata(dev);
+	const struct pm8821 *pmic = pm8821_drvdata->pm_chip_data;
+
+	return msm_ssbi_read(pmic->dev->parent, addr, buf, cnt);
+}
+
+static int pm8821_write_buf(const struct device *dev, u16 addr, u8 *buf,
+									int cnt)
+{
+	const struct pm8xxx_drvdata *pm8821_drvdata = dev_get_drvdata(dev);
+	const struct pm8821 *pmic = pm8821_drvdata->pm_chip_data;
+
+	return msm_ssbi_write(pmic->dev->parent, addr, buf, cnt);
+}
+
+static int pm8821_read_irq_stat(const struct device *dev, int irq)
+{
+	const struct pm8xxx_drvdata *pm8821_drvdata = dev_get_drvdata(dev);
+	const struct pm8821 *pmic = pm8821_drvdata->pm_chip_data;
+
+	return pm8xxx_get_irq_stat(pmic->irq_chip, irq);
+}
+
+static enum pm8xxx_version pm8821_get_version(const struct device *dev)
+{
+	const struct pm8xxx_drvdata *pm8821_drvdata = dev_get_drvdata(dev);
+	const struct pm8821 *pmic = pm8821_drvdata->pm_chip_data;
+	enum pm8xxx_version version = -ENODEV;
+
+	if ((pmic->rev_registers & PM8821_VERSION_MASK) == PM8821_VERSION_VALUE)
+		version = PM8XXX_VERSION_8821;
+
+	return version;
+}
+
+static int pm8821_get_revision(const struct device *dev)
+{
+	const struct pm8xxx_drvdata *pm8821_drvdata = dev_get_drvdata(dev);
+	const struct pm8821 *pmic = pm8821_drvdata->pm_chip_data;
+
+	return pmic->rev_registers & PM8821_REVISION_MASK;
+}
+
+static struct pm8xxx_drvdata pm8821_drvdata = {
+	.pmic_readb		= pm8821_readb,
+	.pmic_writeb		= pm8821_writeb,
+	.pmic_read_buf		= pm8821_read_buf,
+	.pmic_write_buf		= pm8821_write_buf,
+	.pmic_read_irq_stat	= pm8821_read_irq_stat,
+	.pmic_get_version	= pm8821_get_version,
+	.pmic_get_revision	= pm8821_get_revision,
+};
+
+static const struct resource mpp_cell_resources[] __devinitconst = {
+	{
+		.start	= PM8821_IRQ_BLOCK_BIT(PM8821_MPP_BLOCK_START, 0),
+		.end	= PM8821_IRQ_BLOCK_BIT(PM8821_MPP_BLOCK_START, 0)
+			  + PM8821_NR_MPPS - 1,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct mfd_cell mpp_cell __devinitdata = {
+	.name		= PM8XXX_MPP_DEV_NAME,
+	.id		= 1,
+	.resources	= mpp_cell_resources,
+	.num_resources	= ARRAY_SIZE(mpp_cell_resources),
+};
+
+static struct mfd_cell debugfs_cell __devinitdata = {
+	.name		= "pm8xxx-debug",
+	.id		= 1,
+	.platform_data	= "pm8821-dbg",
+	.pdata_size	= sizeof("pm8821-dbg"),
+};
+
+
+static int __devinit
+pm8821_add_subdevices(const struct pm8821_platform_data *pdata,
+		      struct pm8821 *pmic)
+{
+	int ret = 0, irq_base = 0;
+	struct pm_irq_chip *irq_chip;
+
+	if (pdata->irq_pdata) {
+		pdata->irq_pdata->irq_cdata.nirqs = PM8821_NR_IRQS;
+		pdata->irq_pdata->irq_cdata.base_addr = REG_IRQ_BASE;
+		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;
+	}
+
+	if (pdata->mpp_pdata) {
+		pdata->mpp_pdata->core_data.nmpps = PM8821_NR_MPPS;
+		pdata->mpp_pdata->core_data.base_addr = REG_MPP_BASE;
+		mpp_cell.platform_data = pdata->mpp_pdata;
+		mpp_cell.pdata_size = sizeof(struct pm8xxx_mpp_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &mpp_cell, 1, NULL,
+					irq_base);
+		if (ret) {
+			pr_err("Failed to add mpp subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
+	ret = mfd_add_devices(pmic->dev, 0, &debugfs_cell, 1, NULL, irq_base);
+	if (ret) {
+		pr_err("Failed to add debugfs subdevice ret=%d\n", ret);
+		goto bail;
+	}
+
+	return 0;
+bail:
+	if (pmic->irq_chip) {
+		pm8xxx_irq_exit(pmic->irq_chip);
+		pmic->irq_chip = NULL;
+	}
+	return ret;
+}
+
+static const char * const pm8821_rev_names[] = {
+	[PM8XXX_REVISION_8821_TEST]	= "test",
+	[PM8XXX_REVISION_8821_1p0]	= "1.0",
+	[PM8XXX_REVISION_8821_2p0]	= "2.0",
+	[PM8XXX_REVISION_8821_2p1]	= "2.1",
+};
+
+static int __devinit pm8821_probe(struct platform_device *pdev)
+{
+	const struct pm8821_platform_data *pdata = pdev->dev.platform_data;
+	const char *revision_name = "unknown";
+	struct pm8821 *pmic;
+	enum pm8xxx_version version;
+	int revision;
+	int rc;
+	u8 val;
+
+	if (!pdata) {
+		pr_err("missing platform data\n");
+		return -EINVAL;
+	}
+
+	pmic = kzalloc(sizeof(struct pm8821), GFP_KERNEL);
+	if (!pmic) {
+		pr_err("Cannot alloc pm8821 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: PM8821 rev %02X\n", val);
+	pmic->rev_registers = 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: PM8821 rev %02X\n", val);
+	pmic->rev_registers |= val << BITS_PER_BYTE;
+
+	pmic->dev = &pdev->dev;
+	pm8821_drvdata.pm_chip_data = pmic;
+	platform_set_drvdata(pdev, &pm8821_drvdata);
+
+	/* Print out human readable version and revision names. */
+	version = pm8xxx_get_version(pmic->dev);
+	if (version == PM8XXX_VERSION_8821) {
+		revision = pm8xxx_get_revision(pmic->dev);
+		if (revision >= 0 && revision < ARRAY_SIZE(pm8821_rev_names))
+			revision_name = pm8821_rev_names[revision];
+		pr_info("PMIC version: PM8821 ver %s\n", revision_name);
+	} else {
+		WARN_ON(version != PM8XXX_VERSION_8821);
+	}
+
+	rc = pm8821_add_subdevices(pdata, pmic);
+	if (rc) {
+		pr_err("Cannot add subdevices rc=%d\n", rc);
+		goto err;
+	}
+
+	return 0;
+
+err:
+	mfd_remove_devices(pmic->dev);
+	platform_set_drvdata(pdev, NULL);
+err_read_rev:
+	kfree(pmic);
+	return rc;
+}
+
+static int __devexit pm8821_remove(struct platform_device *pdev)
+{
+	struct pm8xxx_drvdata *drvdata;
+	struct pm8821 *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 pm8821_driver = {
+	.probe		= pm8821_probe,
+	.remove		= __devexit_p(pm8821_remove),
+	.driver		= {
+		.name	= "pm8821-core",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init pm8821_init(void)
+{
+	return platform_driver_register(&pm8821_driver);
+}
+postcore_initcall(pm8821_init);
+
+static void __exit pm8821_exit(void)
+{
+	platform_driver_unregister(&pm8821_driver);
+}
+module_exit(pm8821_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC 8821 core driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:pm8821-core");
diff --git a/drivers/mfd/pm8921-core.c b/drivers/mfd/pm8921-core.c
index e873b15..f39a19f 100644
--- a/drivers/mfd/pm8921-core.c
+++ b/drivers/mfd/pm8921-core.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2011-2012, 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
@@ -14,20 +14,55 @@
 #define pr_fmt(fmt) "%s: " fmt, __func__
 
 #include <linux/kernel.h>
+#include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
+#include <linux/string.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>
+#include <linux/mfd/pm8xxx/regulator.h>
+#include <linux/leds-pm8xxx.h>
 
 #define REG_HWREV		0x002  /* PMIC4 revision */
 #define REG_HWREV_2		0x0E8  /* PMIC4 revision 2 */
 
+#define REG_MPP_BASE		0x050
+#define REG_IRQ_BASE		0x1BB
+
+#define REG_TEMP_ALARM_CTRL	0x1B
+#define REG_TEMP_ALARM_PWM	0x9B
+
+#define REG_BATT_ALARM_THRESH	0x023
+#define REG_BATT_ALARM_CTRL1	0x024
+#define REG_BATT_ALARM_CTRL2	0x021
+#define REG_BATT_ALARM_PWM_CTRL	0x020
+
+#define PM8921_VERSION_MASK	0xFFF0
+#define PM8921_VERSION_VALUE	0x06F0
+#define PM8922_VERSION_VALUE	0x0AF0
+#define PM8917_VERSION_VALUE	0x0CF0
+#define PM8921_REVISION_MASK	0x000F
+
+#define REG_PM8921_PON_CNTRL_3	0x01D
+#define PM8921_RESTART_REASON_MASK	0x07
+
+#define SINGLE_IRQ_RESOURCE(_name, _irq) \
+{ \
+	.name	= _name, \
+	.start	= _irq, \
+	.end	= _irq, \
+	.flags	= IORESOURCE_IRQ, \
+}
+
 struct pm8921 {
-	struct device			*dev;
-	struct pm_irq_chip		*irq_chip;
+	struct device					*dev;
+	struct pm_irq_chip				*irq_chip;
+	struct mfd_cell					*mfd_regulators;
+	struct pm8xxx_regulator_core_platform_data	*regulator_cdata;
+	u32						rev_registers;
 };
 
 static int pm8921_readb(const struct device *dev, u16 addr, u8 *val)
@@ -72,25 +107,481 @@
 	return pm8xxx_get_irq_stat(pmic->irq_chip, irq);
 }
 
+static enum pm8xxx_version pm8921_get_version(const struct device *dev)
+{
+	const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev);
+	const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data;
+	enum pm8xxx_version version = -ENODEV;
+
+	if ((pmic->rev_registers & PM8921_VERSION_MASK) == PM8921_VERSION_VALUE)
+		version = PM8XXX_VERSION_8921;
+	else if ((pmic->rev_registers & PM8921_VERSION_MASK)
+			== PM8922_VERSION_VALUE)
+		version = PM8XXX_VERSION_8922;
+	else if ((pmic->rev_registers & PM8921_VERSION_MASK)
+			== PM8917_VERSION_VALUE)
+		version = PM8XXX_VERSION_8917;
+
+	return version;
+}
+
+static int pm8921_get_revision(const struct device *dev)
+{
+	const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev);
+	const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data;
+
+	return pmic->rev_registers & PM8921_REVISION_MASK;
+}
+
 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,
+	.pmic_get_version	= pm8921_get_version,
+	.pmic_get_revision	= pm8921_get_revision,
 };
 
-static int __devinit pm8921_add_subdevices(const struct pm8921_platform_data
-					   *pdata,
-					   struct pm8921 *pmic,
-					   u32 rev)
+static struct resource gpio_cell_resources[] = {
+	[0] = {
+		.start = PM8921_IRQ_BLOCK_BIT(PM8921_GPIO_BLOCK_START, 0),
+		.end   = PM8921_IRQ_BLOCK_BIT(PM8921_GPIO_BLOCK_START, 0)
+			+ PM8921_NR_GPIOS - 1,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct mfd_cell gpio_cell __devinitdata = {
+	.name		= PM8XXX_GPIO_DEV_NAME,
+	.id		= -1,
+	.resources	= gpio_cell_resources,
+	.num_resources	= ARRAY_SIZE(gpio_cell_resources),
+};
+
+static const struct resource adc_cell_resources[] __devinitconst = {
+	SINGLE_IRQ_RESOURCE(NULL, PM8921_ADC_EOC_USR_IRQ),
+	SINGLE_IRQ_RESOURCE(NULL, PM8921_ADC_BATT_TEMP_WARM_IRQ),
+	SINGLE_IRQ_RESOURCE(NULL, PM8921_ADC_BATT_TEMP_COLD_IRQ),
+};
+
+static struct mfd_cell adc_cell __devinitdata = {
+	.name		= PM8XXX_ADC_DEV_NAME,
+	.id		= -1,
+	.resources	= adc_cell_resources,
+	.num_resources	= ARRAY_SIZE(adc_cell_resources),
+};
+
+static struct resource mpp_cell_resources[] = {
+	{
+		.start	= PM8921_IRQ_BLOCK_BIT(PM8921_MPP_BLOCK_START, 0),
+		.end	= PM8921_IRQ_BLOCK_BIT(PM8921_MPP_BLOCK_START, 0)
+			  + PM8921_NR_MPPS - 1,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct mfd_cell mpp_cell __devinitdata = {
+	.name		= PM8XXX_MPP_DEV_NAME,
+	.id		= 0,
+	.resources	= mpp_cell_resources,
+	.num_resources	= ARRAY_SIZE(mpp_cell_resources),
+};
+
+static const struct resource rtc_cell_resources[] __devinitconst = {
+	[0] = SINGLE_IRQ_RESOURCE(NULL, PM8921_RTC_ALARM_IRQ),
+	[1] = {
+		.name   = "pmic_rtc_base",
+		.start  = PM8921_RTC_BASE,
+		.end    = PM8921_RTC_BASE,
+		.flags  = IORESOURCE_IO,
+	},
+};
+
+static struct mfd_cell rtc_cell __devinitdata = {
+	.name           = PM8XXX_RTC_DEV_NAME,
+	.id             = -1,
+	.resources      = rtc_cell_resources,
+	.num_resources  = ARRAY_SIZE(rtc_cell_resources),
+};
+
+static const struct resource resources_pwrkey[] __devinitconst = {
+	SINGLE_IRQ_RESOURCE(NULL, PM8921_PWRKEY_REL_IRQ),
+	SINGLE_IRQ_RESOURCE(NULL, PM8921_PWRKEY_PRESS_IRQ),
+};
+
+static struct mfd_cell pwrkey_cell __devinitdata = {
+	.name		= PM8XXX_PWRKEY_DEV_NAME,
+	.id		= -1,
+	.num_resources	= ARRAY_SIZE(resources_pwrkey),
+	.resources	= resources_pwrkey,
+};
+
+static const struct resource resources_keypad[] = {
+	SINGLE_IRQ_RESOURCE(NULL, PM8921_KEYPAD_IRQ),
+	SINGLE_IRQ_RESOURCE(NULL, PM8921_KEYSTUCK_IRQ),
+};
+
+static struct mfd_cell keypad_cell __devinitdata = {
+	.name		= PM8XXX_KEYPAD_DEV_NAME,
+	.id		= -1,
+	.num_resources	= ARRAY_SIZE(resources_keypad),
+	.resources	= resources_keypad,
+};
+
+static struct mfd_cell debugfs_cell __devinitdata = {
+	.name		= "pm8xxx-debug",
+	.id		= 0,
+	.platform_data	= "pm8921-dbg",
+	.pdata_size	= sizeof("pm8921-dbg"),
+};
+
+static struct mfd_cell pwm_cell __devinitdata = {
+	.name           = PM8XXX_PWM_DEV_NAME,
+	.id             = -1,
+};
+
+static const struct resource charger_cell_resources[] __devinitconst = {
+	SINGLE_IRQ_RESOURCE("USBIN_VALID_IRQ", PM8921_USBIN_VALID_IRQ),
+	SINGLE_IRQ_RESOURCE("USBIN_OV_IRQ", PM8921_USBIN_OV_IRQ),
+	SINGLE_IRQ_RESOURCE("BATT_INSERTED_IRQ", PM8921_BATT_INSERTED_IRQ),
+	SINGLE_IRQ_RESOURCE("VBATDET_LOW_IRQ", PM8921_VBATDET_LOW_IRQ),
+	SINGLE_IRQ_RESOURCE("USBIN_UV_IRQ", PM8921_USBIN_UV_IRQ),
+	SINGLE_IRQ_RESOURCE("VBAT_OV_IRQ", PM8921_VBAT_OV_IRQ),
+	SINGLE_IRQ_RESOURCE("CHGWDOG_IRQ", PM8921_CHGWDOG_IRQ),
+	SINGLE_IRQ_RESOURCE("VCP_IRQ", PM8921_VCP_IRQ),
+	SINGLE_IRQ_RESOURCE("ATCDONE_IRQ", PM8921_ATCDONE_IRQ),
+	SINGLE_IRQ_RESOURCE("ATCFAIL_IRQ", PM8921_ATCFAIL_IRQ),
+	SINGLE_IRQ_RESOURCE("CHGDONE_IRQ", PM8921_CHGDONE_IRQ),
+	SINGLE_IRQ_RESOURCE("CHGFAIL_IRQ", PM8921_CHGFAIL_IRQ),
+	SINGLE_IRQ_RESOURCE("CHGSTATE_IRQ", PM8921_CHGSTATE_IRQ),
+	SINGLE_IRQ_RESOURCE("LOOP_CHANGE_IRQ", PM8921_LOOP_CHANGE_IRQ),
+	SINGLE_IRQ_RESOURCE("FASTCHG_IRQ", PM8921_FASTCHG_IRQ),
+	SINGLE_IRQ_RESOURCE("TRKLCHG_IRQ", PM8921_TRKLCHG_IRQ),
+	SINGLE_IRQ_RESOURCE("BATT_REMOVED_IRQ", PM8921_BATT_REMOVED_IRQ),
+	SINGLE_IRQ_RESOURCE("BATTTEMP_HOT_IRQ", PM8921_BATTTEMP_HOT_IRQ),
+	SINGLE_IRQ_RESOURCE("CHGHOT_IRQ", PM8921_CHGHOT_IRQ),
+	SINGLE_IRQ_RESOURCE("BATTTEMP_COLD_IRQ", PM8921_BATTTEMP_COLD_IRQ),
+	SINGLE_IRQ_RESOURCE("CHG_GONE_IRQ", PM8921_CHG_GONE_IRQ),
+	SINGLE_IRQ_RESOURCE("BAT_TEMP_OK_IRQ", PM8921_BAT_TEMP_OK_IRQ),
+	SINGLE_IRQ_RESOURCE("COARSE_DET_LOW_IRQ", PM8921_COARSE_DET_LOW_IRQ),
+	SINGLE_IRQ_RESOURCE("VDD_LOOP_IRQ", PM8921_VDD_LOOP_IRQ),
+	SINGLE_IRQ_RESOURCE("VREG_OV_IRQ", PM8921_VREG_OV_IRQ),
+	SINGLE_IRQ_RESOURCE("VBATDET_IRQ", PM8921_VBATDET_IRQ),
+	SINGLE_IRQ_RESOURCE("BATFET_IRQ", PM8921_BATFET_IRQ),
+	SINGLE_IRQ_RESOURCE("PSI_IRQ", PM8921_PSI_IRQ),
+	SINGLE_IRQ_RESOURCE("DCIN_VALID_IRQ", PM8921_DCIN_VALID_IRQ),
+	SINGLE_IRQ_RESOURCE("DCIN_OV_IRQ", PM8921_DCIN_OV_IRQ),
+	SINGLE_IRQ_RESOURCE("DCIN_UV_IRQ", PM8921_DCIN_UV_IRQ),
+};
+
+static const struct resource bms_cell_resources[] __devinitconst = {
+	SINGLE_IRQ_RESOURCE("PM8921_BMS_SBI_WRITE_OK", PM8921_BMS_SBI_WRITE_OK),
+	SINGLE_IRQ_RESOURCE("PM8921_BMS_CC_THR", PM8921_BMS_CC_THR),
+	SINGLE_IRQ_RESOURCE("PM8921_BMS_VSENSE_THR", PM8921_BMS_VSENSE_THR),
+	SINGLE_IRQ_RESOURCE("PM8921_BMS_VSENSE_FOR_R", PM8921_BMS_VSENSE_FOR_R),
+	SINGLE_IRQ_RESOURCE("PM8921_BMS_OCV_FOR_R", PM8921_BMS_OCV_FOR_R),
+	SINGLE_IRQ_RESOURCE("PM8921_BMS_GOOD_OCV", PM8921_BMS_GOOD_OCV),
+	SINGLE_IRQ_RESOURCE("PM8921_BMS_VSENSE_AVG", PM8921_BMS_VSENSE_AVG),
+};
+
+static struct mfd_cell charger_cell __devinitdata = {
+	.name		= PM8921_CHARGER_DEV_NAME,
+	.id		= -1,
+	.resources	= charger_cell_resources,
+	.num_resources	= ARRAY_SIZE(charger_cell_resources),
+};
+
+static struct mfd_cell bms_cell __devinitdata = {
+	.name		= PM8921_BMS_DEV_NAME,
+	.id		= -1,
+	.resources	= bms_cell_resources,
+	.num_resources	= ARRAY_SIZE(bms_cell_resources),
+};
+
+static struct mfd_cell misc_cell __devinitdata = {
+	.name           = PM8XXX_MISC_DEV_NAME,
+	.id             = -1,
+};
+
+static struct mfd_cell leds_cell __devinitdata = {
+	.name		= PM8XXX_LEDS_DEV_NAME,
+	.id		= -1,
+};
+
+static const struct resource thermal_alarm_cell_resources[] __devinitconst = {
+	SINGLE_IRQ_RESOURCE("pm8921_tempstat_irq", PM8921_TEMPSTAT_IRQ),
+	SINGLE_IRQ_RESOURCE("pm8921_overtemp_irq", PM8921_OVERTEMP_IRQ),
+};
+
+static struct pm8xxx_tm_core_data thermal_alarm_cdata = {
+	.adc_channel =			CHANNEL_DIE_TEMP,
+	.adc_type =			PM8XXX_TM_ADC_PM8XXX_ADC,
+	.reg_addr_temp_alarm_ctrl =	REG_TEMP_ALARM_CTRL,
+	.reg_addr_temp_alarm_pwm =	REG_TEMP_ALARM_PWM,
+	.tm_name =			"pm8921_tz",
+	.irq_name_temp_stat =		"pm8921_tempstat_irq",
+	.irq_name_over_temp =		"pm8921_overtemp_irq",
+};
+
+static struct mfd_cell thermal_alarm_cell __devinitdata = {
+	.name		= PM8XXX_TM_DEV_NAME,
+	.id		= -1,
+	.resources	= thermal_alarm_cell_resources,
+	.num_resources	= ARRAY_SIZE(thermal_alarm_cell_resources),
+	.platform_data	= &thermal_alarm_cdata,
+	.pdata_size	= sizeof(struct pm8xxx_tm_core_data),
+};
+
+static const struct resource batt_alarm_cell_resources[] __devinitconst = {
+	SINGLE_IRQ_RESOURCE("pm8921_batt_alarm_irq", PM8921_BATT_ALARM_IRQ),
+};
+
+static struct pm8xxx_batt_alarm_core_data batt_alarm_cdata = {
+	.irq_name		= "pm8921_batt_alarm_irq",
+	.reg_addr_threshold	= REG_BATT_ALARM_THRESH,
+	.reg_addr_ctrl1		= REG_BATT_ALARM_CTRL1,
+	.reg_addr_ctrl2		= REG_BATT_ALARM_CTRL2,
+	.reg_addr_pwm_ctrl	= REG_BATT_ALARM_PWM_CTRL,
+};
+
+static struct mfd_cell batt_alarm_cell __devinitdata = {
+	.name		= PM8XXX_BATT_ALARM_DEV_NAME,
+	.id		= -1,
+	.resources	= batt_alarm_cell_resources,
+	.num_resources	= ARRAY_SIZE(batt_alarm_cell_resources),
+	.platform_data	= &batt_alarm_cdata,
+	.pdata_size	= sizeof(struct pm8xxx_batt_alarm_core_data),
+};
+
+static const struct resource ccadc_cell_resources[] __devinitconst = {
+	SINGLE_IRQ_RESOURCE("PM8921_BMS_CCADC_EOC", PM8921_BMS_CCADC_EOC),
+};
+
+static struct mfd_cell ccadc_cell __devinitdata = {
+	.name		= PM8XXX_CCADC_DEV_NAME,
+	.id		= -1,
+	.resources	= ccadc_cell_resources,
+	.num_resources	= ARRAY_SIZE(ccadc_cell_resources),
+};
+
+static struct mfd_cell vibrator_cell __devinitdata = {
+	.name           = PM8XXX_VIBRATOR_DEV_NAME,
+	.id             = -1,
+};
+
+static struct pm8xxx_vreg regulator_data[] = {
+	/*   name	     pc_name	    ctrl   test   hpm_min */
+	NLDO("8921_l1",      "8921_l1_pc",  0x0AE, 0x0AF, LDO_150),
+	NLDO("8921_l2",      "8921_l2_pc",  0x0B0, 0x0B1, LDO_150),
+	PLDO("8921_l3",      "8921_l3_pc",  0x0B2, 0x0B3, LDO_150),
+	PLDO("8921_l4",      "8921_l4_pc",  0x0B4, 0x0B5, LDO_50),
+	PLDO("8921_l5",      "8921_l5_pc",  0x0B6, 0x0B7, LDO_300),
+	PLDO("8921_l6",      "8921_l6_pc",  0x0B8, 0x0B9, LDO_600),
+	PLDO("8921_l7",      "8921_l7_pc",  0x0BA, 0x0BB, LDO_150),
+	PLDO("8921_l8",      "8921_l8_pc",  0x0BC, 0x0BD, LDO_300),
+	PLDO("8921_l9",      "8921_l9_pc",  0x0BE, 0x0BF, LDO_300),
+	PLDO("8921_l10",     "8921_l10_pc", 0x0C0, 0x0C1, LDO_600),
+	PLDO("8921_l11",     "8921_l11_pc", 0x0C2, 0x0C3, LDO_150),
+	NLDO("8921_l12",     "8921_l12_pc", 0x0C4, 0x0C5, LDO_150),
+	PLDO("8921_l14",     "8921_l14_pc", 0x0C8, 0x0C9, LDO_50),
+	PLDO("8921_l15",     "8921_l15_pc", 0x0CA, 0x0CB, LDO_150),
+	PLDO("8921_l16",     "8921_l16_pc", 0x0CC, 0x0CD, LDO_300),
+	PLDO("8921_l17",     "8921_l17_pc", 0x0CE, 0x0CF, LDO_150),
+	NLDO("8921_l18",     "8921_l18_pc", 0x0D0, 0x0D1, LDO_150),
+	PLDO("8921_l21",     "8921_l21_pc", 0x0D6, 0x0D7, LDO_150),
+	PLDO("8921_l22",     "8921_l22_pc", 0x0D8, 0x0D9, LDO_150),
+	PLDO("8921_l23",     "8921_l23_pc", 0x0DA, 0x0DB, LDO_150),
+	NLDO1200("8921_l24",		    0x0DC, 0x0DD, LDO_1200),
+	NLDO1200("8921_l25",		    0x0DE, 0x0DF, LDO_1200),
+	NLDO1200("8921_l26",		    0x0E0, 0x0E1, LDO_1200),
+	NLDO1200("8921_l27",		    0x0E2, 0x0E3, LDO_1200),
+	NLDO1200("8921_l28",		    0x0E4, 0x0E5, LDO_1200),
+	PLDO("8921_l29",     "8921_l29_pc", 0x0E6, 0x0E7, LDO_150),
+
+	/*   name	pc_name       ctrl   test2  clk    sleep  hpm_min */
+	SMPS("8921_s1", "8921_s1_pc", 0x1D0, 0x1D5, 0x009, 0x1D2, SMPS_1500),
+	SMPS("8921_s2", "8921_s2_pc", 0x1D8, 0x1DD, 0x00A, 0x1DA, SMPS_1500),
+	SMPS("8921_s3", "8921_s3_pc", 0x1E0, 0x1E5, 0x00B, 0x1E2, SMPS_1500),
+	SMPS("8921_s4", "8921_s4_pc", 0x1E8, 0x1ED, 0x011, 0x1EA, SMPS_1500),
+
+	/*     name	  ctrl fts_cnfg1 pfm  pwr_cnfg  hpm_min */
+	FTSMPS("8921_s5", 0x025, 0x02E, 0x026, 0x032, SMPS_2000),
+	FTSMPS("8921_s6", 0x036, 0x03F, 0x037, 0x043, SMPS_2000),
+
+	/*   name	pc_name       ctrl   test2  clk    sleep  hpm_min */
+	SMPS("8921_s7", "8921_s7_pc", 0x1F0, 0x1F5, 0x012, 0x1F2, SMPS_1500),
+	SMPS("8921_s8", "8921_s8_pc", 0x1F8, 0x1FD, 0x013, 0x1FA, SMPS_1500),
+
+	/* name		       pc_name	       ctrl   test */
+	VS("8921_lvs1",        "8921_lvs1_pc", 0x060, 0x061),
+	VS300("8921_lvs2",		       0x062, 0x063),
+	VS("8921_lvs3",        "8921_lvs3_pc", 0x064, 0x065),
+	VS("8921_lvs4",        "8921_lvs4_pc", 0x066, 0x067),
+	VS("8921_lvs5",        "8921_lvs5_pc", 0x068, 0x069),
+	VS("8921_lvs6",        "8921_lvs6_pc", 0x06A, 0x06B),
+	VS("8921_lvs7",        "8921_lvs7_pc", 0x06C, 0x06D),
+	VS300("8921_usb_otg",		       0x06E, 0x06F),
+	VS300("8921_hdmi_mvs",		       0x070, 0x071),
+
+	/*  name	ctrl */
+	NCP("8921_ncp", 0x090),
+};
+
+/*
+ * PM8917 adds 6 LDOs and a boost regulator beyond those available on PM8921.
+ * It also replaces SMPS 3 with FTSMPS 3.  PM8917 does not have an NCP.
+ */
+static struct pm8xxx_vreg pm8917_regulator_data[] = {
+	/*   name	     pc_name	    ctrl   test   hpm_min */
+	PLDO("8917_l30",     "8917_l30_pc", 0x0A3, 0x0A4, LDO_150),
+	PLDO("8917_l31",     "8917_l31_pc", 0x0A5, 0x0A6, LDO_150),
+	PLDO("8917_l32",     "8917_l32_pc", 0x0A7, 0x0A8, LDO_150),
+	PLDO("8917_l33",     "8917_l33_pc", 0x0C6, 0x0C7, LDO_150),
+	PLDO("8917_l34",     "8917_l34_pc", 0x0D2, 0x0D3, LDO_150),
+	PLDO("8917_l35",     "8917_l35_pc", 0x0D4, 0x0D5, LDO_300),
+	PLDO("8917_l36",     "8917_l36_pc", 0x0A9, 0x0AA, LDO_50),
+
+	/*    name          ctrl */
+	BOOST("8917_boost", 0x04B),
+};
+
+#define MAX_NAME_COMPARISON_LEN 32
+
+static int __devinit match_regulator(enum pm8xxx_version version,
+	struct pm8xxx_regulator_core_platform_data *core_data, const char *name)
+{
+	int found = 0;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(regulator_data); i++) {
+		if (regulator_data[i].rdesc.name
+		    && strncmp(regulator_data[i].rdesc.name, name,
+				MAX_NAME_COMPARISON_LEN) == 0) {
+			core_data->is_pin_controlled = false;
+			core_data->vreg = &regulator_data[i];
+			found = 1;
+			break;
+		} else if (regulator_data[i].rdesc_pc.name
+			   && strncmp(regulator_data[i].rdesc_pc.name, name,
+				MAX_NAME_COMPARISON_LEN) == 0) {
+			core_data->is_pin_controlled = true;
+			core_data->vreg = &regulator_data[i];
+			found = 1;
+			break;
+		}
+	}
+	if (version == PM8XXX_VERSION_8917) {
+		for (i = 0; i < ARRAY_SIZE(pm8917_regulator_data); i++) {
+			if (pm8917_regulator_data[i].rdesc.name
+			    && strncmp(pm8917_regulator_data[i].rdesc.name,
+					name, MAX_NAME_COMPARISON_LEN) == 0) {
+				core_data->is_pin_controlled = false;
+				core_data->vreg = &pm8917_regulator_data[i];
+				found = 1;
+				break;
+			} else if (pm8917_regulator_data[i].rdesc_pc.name
+			      && strncmp(pm8917_regulator_data[i].rdesc_pc.name,
+					name, MAX_NAME_COMPARISON_LEN) == 0) {
+				core_data->is_pin_controlled = true;
+				core_data->vreg = &pm8917_regulator_data[i];
+				found = 1;
+				break;
+			}
+		}
+	}
+
+	if (!found)
+		pr_err("could not find a match for regulator: %s\n", name);
+
+	return found;
+}
+
+static int __devinit
+pm8921_add_regulators(const struct pm8921_platform_data *pdata,
+		      struct pm8921 *pmic, int irq_base)
+{
+	int ret = 0;
+	struct mfd_cell *mfd_regulators;
+	struct pm8xxx_regulator_core_platform_data *cdata;
+	enum pm8xxx_version version;
+	int i;
+
+	version = pm8xxx_get_version(pmic->dev);
+
+	/* Add one device for each regulator used by the board. */
+	mfd_regulators = kzalloc(sizeof(struct mfd_cell)
+				 * (pdata->num_regulators), GFP_KERNEL);
+	if (!mfd_regulators) {
+		pr_err("Cannot allocate %d bytes for pm8921 regulator "
+			"mfd cells\n", sizeof(struct mfd_cell)
+					* (pdata->num_regulators));
+		return -ENOMEM;
+	}
+	cdata = kzalloc(sizeof(struct pm8xxx_regulator_core_platform_data)
+			* pdata->num_regulators, GFP_KERNEL);
+	if (!cdata) {
+		pr_err("Cannot allocate %d bytes for pm8921 regulator "
+			"core data\n", pdata->num_regulators
+			  * sizeof(struct pm8xxx_regulator_core_platform_data));
+		kfree(mfd_regulators);
+		return -ENOMEM;
+	}
+	for (i = 0; i < ARRAY_SIZE(regulator_data); i++)
+		mutex_init(&regulator_data[i].pc_lock);
+	for (i = 0; i < ARRAY_SIZE(pm8917_regulator_data); i++)
+		mutex_init(&pm8917_regulator_data[i].pc_lock);
+
+	for (i = 0; i < pdata->num_regulators; i++) {
+		if (!pdata->regulator_pdatas[i].init_data.constraints.name) {
+			pr_err("name missing for regulator %d\n", i);
+			ret = -EINVAL;
+			goto bail;
+		}
+		if (!match_regulator(version, &cdata[i],
+		      pdata->regulator_pdatas[i].init_data.constraints.name)) {
+			ret = -ENODEV;
+			goto bail;
+		}
+		cdata[i].pdata = &(pdata->regulator_pdatas[i]);
+		mfd_regulators[i].name = PM8XXX_REGULATOR_DEV_NAME;
+		mfd_regulators[i].id = cdata[i].pdata->id;
+		mfd_regulators[i].platform_data = &cdata[i];
+		mfd_regulators[i].pdata_size =
+			sizeof(struct pm8xxx_regulator_core_platform_data);
+	}
+	ret = mfd_add_devices(pmic->dev, 0, mfd_regulators,
+			pdata->num_regulators, NULL, irq_base);
+	if (ret)
+		goto bail;
+
+	pmic->mfd_regulators = mfd_regulators;
+	pmic->regulator_cdata = cdata;
+	return ret;
+
+bail:
+	for (i = 0; i < ARRAY_SIZE(regulator_data); i++)
+		mutex_destroy(&regulator_data[i].pc_lock);
+	for (i = 0; i < ARRAY_SIZE(pm8917_regulator_data); i++)
+		mutex_destroy(&pm8917_regulator_data[i].pc_lock);
+	kfree(mfd_regulators);
+	kfree(cdata);
+	return ret;
+}
+
+static int __devinit
+pm8921_add_subdevices(const struct pm8921_platform_data *pdata,
+		      struct pm8921 *pmic)
 {
 	int ret = 0, irq_base = 0;
 	struct pm_irq_chip *irq_chip;
+	enum pm8xxx_version version;
+
+	version = pm8xxx_get_version(pmic->dev);
 
 	if (pdata->irq_pdata) {
 		pdata->irq_pdata->irq_cdata.nirqs = PM8921_NR_IRQS;
-		pdata->irq_pdata->irq_cdata.rev = rev;
+		pdata->irq_pdata->irq_cdata.base_addr = REG_IRQ_BASE;
 		irq_base = pdata->irq_pdata->irq_base;
 		irq_chip = pm8xxx_irq_init(pmic->dev, pdata->irq_pdata);
 
@@ -101,16 +592,270 @@
 		}
 		pmic->irq_chip = irq_chip;
 	}
+
+	if (pdata->gpio_pdata) {
+		if (version == PM8XXX_VERSION_8917) {
+			gpio_cell_resources[0].end = gpio_cell_resources[0].end
+							+ PM8917_NR_GPIOS
+							- PM8921_NR_GPIOS;
+			pdata->gpio_pdata->gpio_cdata.ngpios = PM8917_NR_GPIOS;
+		} else {
+			pdata->gpio_pdata->gpio_cdata.ngpios = PM8921_NR_GPIOS;
+		}
+		gpio_cell.platform_data = pdata->gpio_pdata;
+		gpio_cell.pdata_size = sizeof(struct pm8xxx_gpio_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &gpio_cell, 1,
+					NULL, irq_base);
+		if (ret) {
+			pr_err("Failed to add  gpio subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
+	if (pdata->mpp_pdata) {
+		if (version == PM8XXX_VERSION_8917) {
+			mpp_cell_resources[0].end = mpp_cell_resources[0].end
+							+ PM8917_NR_MPPS
+							- PM8921_NR_MPPS;
+			pdata->mpp_pdata->core_data.nmpps = PM8917_NR_MPPS;
+		} else {
+			pdata->mpp_pdata->core_data.nmpps = PM8921_NR_MPPS;
+		}
+		pdata->mpp_pdata->core_data.base_addr = REG_MPP_BASE;
+		mpp_cell.platform_data = pdata->mpp_pdata;
+		mpp_cell.pdata_size = sizeof(struct pm8xxx_mpp_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &mpp_cell, 1, NULL,
+					irq_base);
+		if (ret) {
+			pr_err("Failed to add mpp subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
+	if (pdata->rtc_pdata) {
+		rtc_cell.platform_data = pdata->rtc_pdata;
+		rtc_cell.pdata_size = sizeof(struct pm8xxx_rtc_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &rtc_cell, 1, NULL,
+				irq_base);
+		if (ret) {
+			pr_err("Failed to add rtc subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
+	if (pdata->pwrkey_pdata) {
+		pwrkey_cell.platform_data = pdata->pwrkey_pdata;
+		pwrkey_cell.pdata_size =
+			sizeof(struct pm8xxx_pwrkey_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &pwrkey_cell, 1, NULL,
+					irq_base);
+		if (ret) {
+			pr_err("Failed to add pwrkey subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
+	if (pdata->keypad_pdata) {
+		keypad_cell.platform_data = pdata->keypad_pdata;
+		keypad_cell.pdata_size =
+			sizeof(struct pm8xxx_keypad_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &keypad_cell, 1, NULL,
+					irq_base);
+		if (ret) {
+			pr_err("Failed to add keypad subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
+	if (pdata->charger_pdata) {
+		pdata->charger_pdata->charger_cdata.vbat_channel = CHANNEL_VBAT;
+		pdata->charger_pdata->charger_cdata.batt_temp_channel
+						= CHANNEL_BATT_THERM;
+		pdata->charger_pdata->charger_cdata.batt_id_channel
+						= CHANNEL_BATT_ID;
+		charger_cell.platform_data = pdata->charger_pdata;
+		charger_cell.pdata_size =
+				sizeof(struct pm8921_charger_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &charger_cell, 1, NULL,
+					irq_base);
+		if (ret) {
+			pr_err("Failed to add charger subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
+	if (pdata->adc_pdata) {
+		adc_cell.platform_data = pdata->adc_pdata;
+		adc_cell.pdata_size =
+			sizeof(struct pm8xxx_adc_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &adc_cell, 1, NULL,
+					irq_base);
+		if (ret) {
+			pr_err("Failed to add regulator subdevices ret=%d\n",
+					ret);
+		}
+	}
+
+	if (pdata->bms_pdata) {
+		pdata->bms_pdata->bms_cdata.batt_temp_channel
+						= CHANNEL_BATT_THERM;
+		pdata->bms_pdata->bms_cdata.vbat_channel = CHANNEL_VBAT;
+		pdata->bms_pdata->bms_cdata.ref625mv_channel = CHANNEL_625MV;
+		pdata->bms_pdata->bms_cdata.ref1p25v_channel = CHANNEL_125V;
+		pdata->bms_pdata->bms_cdata.batt_id_channel = CHANNEL_BATT_ID;
+		bms_cell.platform_data = pdata->bms_pdata;
+		bms_cell.pdata_size = sizeof(struct pm8921_bms_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &bms_cell, 1, NULL,
+					irq_base);
+		if (ret) {
+			pr_err("Failed to add bms subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
+	if (pdata->num_regulators > 0 && pdata->regulator_pdatas) {
+		ret = pm8921_add_regulators(pdata, pmic, irq_base);
+		if (ret) {
+			pr_err("Failed to add regulator subdevices ret=%d\n",
+				ret);
+			goto bail;
+		}
+	}
+
+	ret = mfd_add_devices(pmic->dev, 0, &debugfs_cell, 1, NULL, irq_base);
+	if (ret) {
+		pr_err("Failed to add debugfs subdevice ret=%d\n", ret);
+		goto bail;
+	}
+
+	if (pdata->misc_pdata) {
+		misc_cell.platform_data = pdata->misc_pdata;
+		misc_cell.pdata_size = sizeof(struct pm8xxx_misc_platform_data);
+		ret = mfd_add_devices(pmic->dev, 0, &misc_cell, 1, NULL,
+				      irq_base);
+		if (ret) {
+			pr_err("Failed to add  misc subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
+	ret = mfd_add_devices(pmic->dev, 0, &thermal_alarm_cell, 1, NULL,
+				irq_base);
+	if (ret) {
+		pr_err("Failed to add thermal alarm subdevice ret=%d\n",
+			ret);
+		goto bail;
+	}
+
+	ret = mfd_add_devices(pmic->dev, 0, &batt_alarm_cell, 1, NULL,
+				irq_base);
+	if (ret) {
+		pr_err("Failed to add battery alarm subdevice ret=%d\n",
+			ret);
+		goto bail;
+	}
+
+	if (version != PM8XXX_VERSION_8917) {
+		if (pdata->pwm_pdata) {
+			pwm_cell.platform_data = pdata->pwm_pdata;
+			pwm_cell.pdata_size =
+				sizeof(struct pm8xxx_pwm_platform_data);
+		}
+		ret = mfd_add_devices(pmic->dev, 0, &pwm_cell, 1, NULL, 0);
+		if (ret) {
+			pr_err("Failed to add pwm subdevice ret=%d\n", ret);
+			goto bail;
+		}
+
+		if (pdata->leds_pdata) {
+			leds_cell.platform_data = pdata->leds_pdata;
+			leds_cell.pdata_size =
+				sizeof(struct pm8xxx_led_platform_data);
+			ret = mfd_add_devices(pmic->dev, 0, &leds_cell,
+					      1, NULL, 0);
+			if (ret) {
+				pr_err("Failed to add leds subdevice ret=%d\n",
+						ret);
+				goto bail;
+			}
+		}
+
+		if (pdata->vibrator_pdata) {
+			vibrator_cell.platform_data = pdata->vibrator_pdata;
+			vibrator_cell.pdata_size =
+				sizeof(struct pm8xxx_vibrator_platform_data);
+			ret = mfd_add_devices(pmic->dev, 0, &vibrator_cell,
+					      1, NULL, 0);
+			if (ret) {
+				pr_err("Failed to add vibrator ret=%d\n", ret);
+				goto bail;
+			}
+		}
+	}
+
+	if (pdata->ccadc_pdata) {
+		ccadc_cell.platform_data = pdata->ccadc_pdata;
+		ccadc_cell.pdata_size =
+				sizeof(struct pm8xxx_ccadc_platform_data);
+
+		ret = mfd_add_devices(pmic->dev, 0, &ccadc_cell, 1, NULL,
+					irq_base);
+		if (ret) {
+			pr_err("Failed to add ccadc subdevice ret=%d\n", ret);
+			goto bail;
+		}
+	}
+
+	return 0;
+bail:
+	if (pmic->irq_chip) {
+		pm8xxx_irq_exit(pmic->irq_chip);
+		pmic->irq_chip = NULL;
+	}
 	return ret;
 }
 
+static const char * const pm8921_restart_reason[] = {
+	[0] = "Unknown",
+	[1] = "Triggered from CBL (external charger)",
+	[2] = "Triggered from KPD (power key press)",
+	[3] = "Triggered from CHG (usb charger insertion)",
+	[4] = "Triggered from SMPL (sudden momentary power loss)",
+	[5] = "Triggered from RTC (real time clock)",
+	[6] = "Triggered by Hard Reset",
+	[7] = "Triggered by General Purpose Trigger",
+};
+
+static const char * const pm8921_rev_names[] = {
+	[PM8XXX_REVISION_8921_TEST]	= "test",
+	[PM8XXX_REVISION_8921_1p0]	= "1.0",
+	[PM8XXX_REVISION_8921_1p1]	= "1.1",
+	[PM8XXX_REVISION_8921_2p0]	= "2.0",
+	[PM8XXX_REVISION_8921_3p0]	= "3.0",
+	[PM8XXX_REVISION_8921_3p1]	= "3.1",
+};
+
+static const char * const pm8922_rev_names[] = {
+	[PM8XXX_REVISION_8922_TEST]	= "test",
+	[PM8XXX_REVISION_8922_1p0]	= "1.0",
+	[PM8XXX_REVISION_8922_1p1]	= "1.1",
+	[PM8XXX_REVISION_8922_2p0]	= "2.0",
+};
+
+static const char * const pm8917_rev_names[] = {
+	[PM8XXX_REVISION_8917_TEST]	= "test",
+	[PM8XXX_REVISION_8917_1p0]	= "1.0",
+};
+
 static int __devinit pm8921_probe(struct platform_device *pdev)
 {
 	const struct pm8921_platform_data *pdata = pdev->dev.platform_data;
+	const char *revision_name = "unknown";
 	struct pm8921 *pmic;
+	enum pm8xxx_version version;
+	int revision;
 	int rc;
 	u8 val;
-	u32 rev;
 
 	if (!pdata) {
 		pr_err("missing platform data\n");
@@ -130,7 +875,7 @@
 		goto err_read_rev;
 	}
 	pr_info("PMIC revision 1: %02X\n", val);
-	rev = val;
+	pmic->rev_registers = val;
 
 	/* Read PMIC chip revision 2 */
 	rc = msm_ssbi_read(pdev->dev.parent, REG_HWREV_2, &val, sizeof(val));
@@ -140,13 +885,43 @@
 		goto err_read_rev;
 	}
 	pr_info("PMIC revision 2: %02X\n", val);
-	rev |= val << BITS_PER_BYTE;
+	pmic->rev_registers |= 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);
+	/* Print out human readable version and revision names. */
+	version = pm8xxx_get_version(pmic->dev);
+	revision = pm8xxx_get_revision(pmic->dev);
+	if (version == PM8XXX_VERSION_8921) {
+		if (revision >= 0 && revision < ARRAY_SIZE(pm8921_rev_names))
+			revision_name = pm8921_rev_names[revision];
+		pr_info("PMIC version: PM8921 rev %s\n", revision_name);
+	} else if (version == PM8XXX_VERSION_8922) {
+		if (revision >= 0 && revision < ARRAY_SIZE(pm8922_rev_names))
+			revision_name = pm8922_rev_names[revision];
+		pr_info("PMIC version: PM8922 rev %s\n", revision_name);
+	} else if (version == PM8XXX_VERSION_8917) {
+		if (revision >= 0 && revision < ARRAY_SIZE(pm8917_rev_names))
+			revision_name = pm8917_rev_names[revision];
+		pr_info("PMIC version: PM8917 rev %s\n", revision_name);
+	} else {
+		WARN_ON(version != PM8XXX_VERSION_8921
+			&& version != PM8XXX_VERSION_8922
+			&& version != PM8XXX_VERSION_8917);
+	}
+
+	/* Log human readable restart reason */
+	rc = msm_ssbi_read(pdev->dev.parent, REG_PM8921_PON_CNTRL_3, &val, 1);
+	if (rc) {
+		pr_err("Cannot read restart reason rc=%d\n", rc);
+		goto err_read_rev;
+	}
+	val &= PM8921_RESTART_REASON_MASK;
+	pr_info("PMIC Restart Reason: %s\n", pm8921_restart_reason[val]);
+
+	rc = pm8921_add_subdevices(pdata, pmic);
 	if (rc) {
 		pr_err("Cannot add subdevices rc=%d\n", rc);
 		goto err;
@@ -160,6 +935,8 @@
 err:
 	mfd_remove_devices(pmic->dev);
 	platform_set_drvdata(pdev, NULL);
+	kfree(pmic->mfd_regulators);
+	kfree(pmic->regulator_cdata);
 err_read_rev:
 	kfree(pmic);
 	return rc;
@@ -169,18 +946,28 @@
 {
 	struct pm8xxx_drvdata *drvdata;
 	struct pm8921 *pmic = NULL;
+	int i;
 
 	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;
+	if (pmic) {
+		if (pmic->dev)
+			mfd_remove_devices(pmic->dev);
+		if (pmic->irq_chip)
+			pm8xxx_irq_exit(pmic->irq_chip);
+		if (pmic->mfd_regulators) {
+			for (i = 0; i < ARRAY_SIZE(regulator_data); i++)
+				mutex_destroy(&regulator_data[i].pc_lock);
+			for (i = 0; i < ARRAY_SIZE(pm8917_regulator_data); i++)
+				mutex_destroy(
+					&pm8917_regulator_data[i].pc_lock);
+		}
+		kfree(pmic->mfd_regulators);
+		kfree(pmic->regulator_cdata);
+		kfree(pmic);
 	}
 	platform_set_drvdata(pdev, NULL);
-	kfree(pmic);
 
 	return 0;
 }
@@ -198,7 +985,7 @@
 {
 	return platform_driver_register(&pm8921_driver);
 }
-subsys_initcall(pm8921_init);
+postcore_initcall(pm8921_init);
 
 static void __exit pm8921_exit(void)
 {
diff --git a/drivers/mfd/pm8xxx-batt-alarm.c b/drivers/mfd/pm8xxx-batt-alarm.c
new file mode 100644
index 0000000..1d30db9
--- /dev/null
+++ b/drivers/mfd/pm8xxx-batt-alarm.c
@@ -0,0 +1,806 @@
+/* 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.
+ */
+/*
+ * Qualcomm PMIC PM8xxx Battery Alarm driver
+ *
+ */
+
+#define pr_fmt(fmt)	"%s: " fmt, __func__
+
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/mfd/pm8xxx/batt-alarm.h>
+
+/* Available voltage threshold values */
+#define THRESHOLD_MIN_MV		2500
+#define THRESHOLD_MAX_MV		5675
+#define THRESHOLD_STEP_MV		25
+
+/* Register bit definitions */
+
+/* Threshold register */
+#define THRESHOLD_UPPER_MASK		0xF0
+#define THRESHOLD_LOWER_MASK		0x0F
+#define THRESHOLD_UPPER_SHIFT		4
+#define THRESHOLD_LOWER_SHIFT		0
+
+/* CTRL 1 register */
+#define CTRL1_BATT_ALARM_ENABLE_MASK	0x80
+#define CTRL1_BATT_ALARM_ENABLE		0x80
+#define CTRL1_BATT_ALARM_DISABLE	0x00
+#define CTRL1_HOLD_TIME_MASK		0x70
+#define CTRL1_STATUS_UPPER_MASK		0x02
+#define CTRL1_STATUS_LOWER_MASK		0x01
+#define CTRL1_HOLD_TIME_SHIFT		4
+#define CTRL1_HOLD_TIME_MIN		0
+#define CTRL1_HOLD_TIME_MAX		7
+
+/* CTRL 2 register */
+#define CTRL2_COMP_UPPER_DISABLE_MASK	0x80
+#define CTRL2_COMP_UPPER_ENABLE		0x00
+#define CTRL2_COMP_UPPER_DISABLE	0x80
+#define CTRL2_COMP_LOWER_DISABLE_MASK	0x40
+#define CTRL2_COMP_LOWER_ENABLE		0x00
+#define CTRL2_COMP_LOWER_DISABLE	0x40
+#define CTRL2_FINE_STEP_UPPER_MASK	0x30
+#define CTRL2_RANGE_EXT_UPPER_MASK	0x08
+#define CTRL2_FINE_STEP_LOWER_MASK	0x06
+#define CTRL2_RANGE_EXT_LOWER_MASK	0x01
+#define CTRL2_FINE_STEP_UPPER_SHIFT	4
+#define CTRL2_FINE_STEP_LOWER_SHIFT	1
+
+/* PWM control register */
+#define PWM_CTRL_ALARM_EN_MASK		0xC0
+#define PWM_CTRL_ALARM_EN_NEVER		0x00
+#define PWM_CTRL_ALARM_EN_TCXO		0x40
+#define PWM_CTRL_ALARM_EN_PWM		0x80
+#define PWM_CTRL_ALARM_EN_ALWAYS	0xC0
+#define PWM_CTRL_PRE_MASK		0x38
+#define PWM_CTRL_DIV_MASK		0x07
+#define PWM_CTRL_PRE_SHIFT		3
+#define PWM_CTRL_DIV_SHIFT		0
+#define PWM_CTRL_PRE_MIN		0
+#define PWM_CTRL_PRE_MAX		7
+#define PWM_CTRL_DIV_MIN		1
+#define PWM_CTRL_DIV_MAX		7
+
+/* PWM control input range */
+#define PWM_CTRL_PRE_INPUT_MIN		2
+#define PWM_CTRL_PRE_INPUT_MAX		9
+#define PWM_CTRL_DIV_INPUT_MIN		2
+#define PWM_CTRL_DIV_INPUT_MAX		8
+
+/* Available voltage threshold values */
+#define THRESHOLD_BASIC_MIN_MV		2800
+#define THRESHOLD_EXT_MIN_MV		4400
+
+/*
+ * Default values used during initialization:
+ * Slowest PWM rate to ensure minimal status jittering when crossing thresholds.
+ * Largest hold time also helps reduce status value jittering.  Comparators
+ * are disabled by default and must be turned on by calling
+ * pm8xxx_batt_alarm_state_set.
+ */
+#define DEFAULT_THRESHOLD_LOWER		3200
+#define DEFAULT_THRESHOLD_UPPER		4300
+#define DEFAULT_HOLD_TIME		PM8XXX_BATT_ALARM_HOLD_TIME_16_MS
+#define DEFAULT_USE_PWM			1
+#define DEFAULT_PWM_SCALER		9
+#define DEFAULT_PWM_DIVIDER		8
+#define DEFAULT_LOWER_ENABLE		0
+#define DEFAULT_UPPER_ENABLE		0
+
+struct pm8xxx_batt_alarm_chip {
+	struct pm8xxx_batt_alarm_core_data	cdata;
+	struct srcu_notifier_head		irq_notifier_list;
+	struct work_struct			irq_work;
+	struct device				*dev;
+	struct mutex				lock;
+	unsigned int				irq;
+	int					notifier_count;
+	u8					reg_threshold;
+	u8					reg_ctrl1;
+	u8					reg_ctrl2;
+	u8					reg_pwm_ctrl;
+};
+static struct pm8xxx_batt_alarm_chip *the_battalarm;
+
+static int pm8xxx_reg_write(struct pm8xxx_batt_alarm_chip *chip, u16 addr,
+				u8 val, u8 mask, u8 *reg_save)
+{
+	int rc = 0;
+	u8 reg;
+
+	reg = (*reg_save & ~mask) | (val & mask);
+	if (reg != *reg_save)
+		rc = pm8xxx_writeb(chip->dev->parent, addr, reg);
+	if (rc)
+		pr_err("pm8xxx_writeb failed; addr=%03X, rc=%d\n", addr, rc);
+	else
+		*reg_save = reg;
+	return rc;
+}
+
+/**
+ * pm8xxx_batt_alarm_enable - enable one of the battery voltage threshold
+ *			      comparators
+ * @comparator:	selects which comparator to enable
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_batt_alarm_enable(enum pm8xxx_batt_alarm_comparator comparator)
+{
+	struct pm8xxx_batt_alarm_chip *chip = the_battalarm;
+	int rc;
+	u8 val_ctrl2 = 0, mask_ctrl2 = 0;
+
+	if (!chip) {
+		pr_err("no battery alarm device found.\n");
+		return -ENODEV;
+	}
+
+	if (comparator < 0 || comparator > PM8XXX_BATT_ALARM_UPPER_COMPARATOR) {
+		pr_err("invalid comparator ID number: %d\n", comparator);
+		return -EINVAL;
+	}
+
+	if (comparator == PM8XXX_BATT_ALARM_LOWER_COMPARATOR) {
+		val_ctrl2 = CTRL2_COMP_LOWER_ENABLE;
+		mask_ctrl2 = CTRL2_COMP_LOWER_DISABLE_MASK;
+	} else {
+		val_ctrl2 = CTRL2_COMP_UPPER_ENABLE;
+		mask_ctrl2 = CTRL2_COMP_UPPER_DISABLE_MASK;
+	}
+
+	mutex_lock(&chip->lock);
+
+	/* Enable the battery alarm block. */
+	rc = pm8xxx_reg_write(chip, chip->cdata.reg_addr_ctrl1,
+				CTRL1_BATT_ALARM_ENABLE,
+				CTRL1_BATT_ALARM_ENABLE_MASK, &chip->reg_ctrl1);
+	if (rc)
+		goto bail;
+
+	/* Enable the individual comparators. */
+	rc = pm8xxx_reg_write(chip, chip->cdata.reg_addr_ctrl2, val_ctrl2,
+				mask_ctrl2, &chip->reg_ctrl2);
+
+bail:
+	mutex_unlock(&chip->lock);
+	return rc;
+}
+EXPORT_SYMBOL(pm8xxx_batt_alarm_enable);
+
+/**
+ * pm8xxx_batt_alarm_disable - disable one of the battery voltage threshold
+ *			       comparators
+ * @comparator:	selects which comparator to disable
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_batt_alarm_disable(enum pm8xxx_batt_alarm_comparator comparator)
+{
+	struct pm8xxx_batt_alarm_chip *chip = the_battalarm;
+	int rc;
+	u8 val_ctrl1 = 0, val_ctrl2 = 0, mask_ctrl2 = 0;
+
+	if (!chip) {
+		pr_err("no battery alarm device found.\n");
+		return -ENODEV;
+	}
+
+	if (comparator < 0 || comparator > PM8XXX_BATT_ALARM_UPPER_COMPARATOR) {
+		pr_err("invalid comparator ID number: %d\n", comparator);
+		return -EINVAL;
+	}
+
+	if (comparator == PM8XXX_BATT_ALARM_LOWER_COMPARATOR) {
+		val_ctrl2 = CTRL2_COMP_LOWER_DISABLE;
+		mask_ctrl2 = CTRL2_COMP_LOWER_DISABLE_MASK;
+	} else {
+		val_ctrl2 = CTRL2_COMP_UPPER_DISABLE;
+		mask_ctrl2 = CTRL2_COMP_UPPER_DISABLE_MASK;
+	}
+
+	mutex_lock(&chip->lock);
+
+	/* Disable the specified comparator. */
+	rc = pm8xxx_reg_write(chip, chip->cdata.reg_addr_ctrl2, val_ctrl2,
+				mask_ctrl2, &chip->reg_ctrl2);
+	if (rc)
+		goto bail;
+
+	/* Disable the battery alarm block if both comparators are disabled. */
+	val_ctrl2 = chip->reg_ctrl2
+	      & (CTRL2_COMP_LOWER_DISABLE_MASK | CTRL2_COMP_UPPER_DISABLE_MASK);
+	if (val_ctrl2 == (CTRL2_COMP_LOWER_DISABLE | CTRL2_COMP_UPPER_DISABLE))
+		val_ctrl1 = CTRL1_BATT_ALARM_DISABLE;
+	else
+		val_ctrl1 = CTRL1_BATT_ALARM_ENABLE;
+
+	rc = pm8xxx_reg_write(chip, chip->cdata.reg_addr_ctrl1, val_ctrl1,
+				CTRL1_BATT_ALARM_ENABLE_MASK, &chip->reg_ctrl1);
+
+bail:
+	mutex_unlock(&chip->lock);
+	return rc;
+}
+EXPORT_SYMBOL(pm8xxx_batt_alarm_disable);
+
+/**
+ * pm8xxx_batt_alarm_threshold_set - set the lower and upper alarm thresholds
+ * @comparator:		selects which comparator to set the threshold of
+ * @threshold_mV:	battery voltage threshold in millivolts
+ *			set points = 2500-5675 mV in 25 mV steps
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_batt_alarm_threshold_set(
+	enum pm8xxx_batt_alarm_comparator comparator, int threshold_mV)
+{
+	struct pm8xxx_batt_alarm_chip *chip = the_battalarm;
+	int step, fine_step, rc;
+	u8 val_threshold = 0, val_ctrl2 = 0;
+	int threshold_mask, threshold_shift, range_ext_mask, fine_step_mask;
+	int fine_step_shift;
+
+	if (!chip) {
+		pr_err("no battery alarm device found.\n");
+		return -ENXIO;
+	}
+
+	if (comparator < 0 || comparator > PM8XXX_BATT_ALARM_UPPER_COMPARATOR) {
+		pr_err("invalid comparator ID number: %d\n", comparator);
+		return -EINVAL;
+	}
+
+	if (threshold_mV < THRESHOLD_MIN_MV
+	    || threshold_mV > THRESHOLD_MAX_MV) {
+		pr_err("threshold value, %d mV, is outside of allowable "
+			"range: [%d, %d] mV\n", threshold_mV,
+			THRESHOLD_MIN_MV, THRESHOLD_MAX_MV);
+		return -EINVAL;
+	}
+
+	if (comparator == PM8XXX_BATT_ALARM_LOWER_COMPARATOR) {
+		threshold_mask = THRESHOLD_LOWER_MASK;
+		threshold_shift = THRESHOLD_LOWER_SHIFT;
+		range_ext_mask = CTRL2_RANGE_EXT_LOWER_MASK;
+		fine_step_mask = CTRL2_FINE_STEP_LOWER_MASK;
+		fine_step_shift = CTRL2_FINE_STEP_LOWER_SHIFT;
+	} else {
+		threshold_mask = THRESHOLD_UPPER_MASK;
+		threshold_shift = THRESHOLD_UPPER_SHIFT;
+		range_ext_mask = CTRL2_RANGE_EXT_UPPER_MASK;
+		fine_step_mask = CTRL2_FINE_STEP_UPPER_MASK;
+		fine_step_shift = CTRL2_FINE_STEP_UPPER_SHIFT;
+	}
+
+	/* Determine register settings to achieve the threshold. */
+	if (threshold_mV < THRESHOLD_BASIC_MIN_MV) {
+		/* Extended low range */
+		val_ctrl2 |= range_ext_mask;
+
+		step = (threshold_mV - THRESHOLD_MIN_MV) / THRESHOLD_STEP_MV;
+
+		fine_step = step & 0x3;
+		/* Extended low range is for steps 0 to 2 */
+		step >>= 2;
+	} else if (threshold_mV >= THRESHOLD_EXT_MIN_MV) {
+		/* Extended high range */
+		val_ctrl2 |= range_ext_mask;
+
+		step = (threshold_mV - THRESHOLD_EXT_MIN_MV)
+			/ THRESHOLD_STEP_MV;
+
+		fine_step = step & 0x3;
+		/* Extended high range is for steps 3 to 15 */
+		step = (step >> 2) + 3;
+	} else {
+		/* Basic range */
+		step = (threshold_mV - THRESHOLD_BASIC_MIN_MV)
+			/ THRESHOLD_STEP_MV;
+
+		fine_step = step & 0x3;
+		step >>= 2;
+	}
+	val_threshold |= step << threshold_shift;
+	val_ctrl2 |= (fine_step << fine_step_shift) & fine_step_mask;
+
+	mutex_lock(&chip->lock);
+	rc = pm8xxx_reg_write(chip, chip->cdata.reg_addr_threshold,
+			val_threshold, threshold_mask, &chip->reg_threshold);
+	if (rc)
+		goto bail;
+
+	rc = pm8xxx_reg_write(chip, chip->cdata.reg_addr_ctrl2, val_ctrl2,
+			range_ext_mask | fine_step_mask, &chip->reg_ctrl2);
+
+bail:
+	mutex_unlock(&chip->lock);
+	return rc;
+}
+EXPORT_SYMBOL(pm8xxx_batt_alarm_threshold_set);
+
+/**
+ * pm8xxx_batt_alarm_status_read - get status of both threshold comparators
+ *
+ * RETURNS:	< 0	   = error
+ *		  0	   = battery voltage ok
+ *		BIT(0) set = battery voltage below lower threshold
+ *		BIT(1) set = battery voltage above upper threshold
+ */
+int pm8xxx_batt_alarm_status_read(void)
+{
+	struct pm8xxx_batt_alarm_chip *chip = the_battalarm;
+	int status, rc;
+
+	if (!chip) {
+		pr_err("no battery alarm device found.\n");
+		return -ENXIO;
+	}
+
+	mutex_lock(&chip->lock);
+	rc = pm8xxx_readb(chip->dev->parent, chip->cdata.reg_addr_ctrl1,
+			  &chip->reg_ctrl1);
+
+	status = ((chip->reg_ctrl1 & CTRL1_STATUS_LOWER_MASK)
+			? PM8XXX_BATT_ALARM_STATUS_BELOW_LOWER : 0)
+		| ((chip->reg_ctrl1 & CTRL1_STATUS_UPPER_MASK)
+			? PM8XXX_BATT_ALARM_STATUS_ABOVE_UPPER : 0);
+	mutex_unlock(&chip->lock);
+
+	if (rc) {
+		pr_err("pm8xxx_readb failed, rc=%d\n", rc);
+		return rc;
+	}
+
+	return status;
+}
+EXPORT_SYMBOL(pm8xxx_batt_alarm_status_read);
+
+/**
+ * pm8xxx_batt_alarm_hold_time_set - set hold time of interrupt output *
+ * @hold_time:	amount of time that battery voltage must remain outside of the
+ *		threshold range before the battery alarm interrupt triggers
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_batt_alarm_hold_time_set(enum pm8xxx_batt_alarm_hold_time hold_time)
+{
+	struct pm8xxx_batt_alarm_chip *chip = the_battalarm;
+	int rc;
+	u8 reg_ctrl1 = 0;
+
+	if (!chip) {
+		pr_err("no battery alarm device found.\n");
+		return -ENXIO;
+	}
+
+	if (hold_time < CTRL1_HOLD_TIME_MIN
+	    || hold_time > CTRL1_HOLD_TIME_MAX) {
+
+		pr_err("hold time, %d, is outside of allowable range: "
+			"[%d, %d]\n", hold_time, CTRL1_HOLD_TIME_MIN,
+			CTRL1_HOLD_TIME_MAX);
+		return -EINVAL;
+	}
+
+	reg_ctrl1 = hold_time << CTRL1_HOLD_TIME_SHIFT;
+
+	mutex_lock(&chip->lock);
+	rc = pm8xxx_reg_write(chip, chip->cdata.reg_addr_ctrl1, reg_ctrl1,
+			      CTRL1_HOLD_TIME_MASK, &chip->reg_ctrl1);
+	mutex_unlock(&chip->lock);
+
+	return rc;
+}
+EXPORT_SYMBOL(pm8xxx_batt_alarm_hold_time_set);
+
+/**
+ * pm8xxx_batt_alarm_pwm_rate_set - set battery alarm update rate *
+ * @use_pwm:		1 = use PWM update rate, 0 = comparators always active
+ * @clock_scaler:	PWM clock scaler = 2 to 9
+ * @clock_divider:	PWM clock divider = 2 to 8
+ *
+ * This function sets the rate at which the battery alarm module enables
+ * the threshold comparators.  The rate is determined by the following equation:
+ *
+ * f_update = (1024 Hz) / (clock_divider * (2 ^ clock_scaler))
+ *
+ * Thus, the update rate can range from 0.25 Hz to 128 Hz.
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_batt_alarm_pwm_rate_set(int use_pwm, int clock_scaler,
+				   int clock_divider)
+{
+	struct pm8xxx_batt_alarm_chip *chip = the_battalarm;
+	int rc;
+	u8 reg_pwm_ctrl = 0, mask = 0;
+
+	if (!chip) {
+		pr_err("no battery alarm device found.\n");
+		return -ENXIO;
+	}
+
+	if (use_pwm && (clock_scaler < PWM_CTRL_PRE_INPUT_MIN
+	    || clock_scaler > PWM_CTRL_PRE_INPUT_MAX)) {
+		pr_err("PWM clock scaler, %d, is outside of allowable range: "
+			"[%d, %d]\n", clock_scaler, PWM_CTRL_PRE_INPUT_MIN,
+			PWM_CTRL_PRE_INPUT_MAX);
+		return -EINVAL;
+	}
+
+	if (use_pwm && (clock_divider < PWM_CTRL_DIV_INPUT_MIN
+	    || clock_divider > PWM_CTRL_DIV_INPUT_MAX)) {
+		pr_err("PWM clock divider, %d, is outside of allowable range: "
+			"[%d, %d]\n", clock_divider, PWM_CTRL_DIV_INPUT_MIN,
+			PWM_CTRL_DIV_INPUT_MAX);
+		return -EINVAL;
+	}
+
+	if (!use_pwm) {
+		/* Turn off PWM control and always enable. */
+		reg_pwm_ctrl = PWM_CTRL_ALARM_EN_ALWAYS;
+		mask = PWM_CTRL_ALARM_EN_MASK;
+	} else {
+		/* Use PWM control. */
+		reg_pwm_ctrl = PWM_CTRL_ALARM_EN_PWM;
+		mask = PWM_CTRL_ALARM_EN_MASK | PWM_CTRL_PRE_MASK
+			| PWM_CTRL_DIV_MASK;
+
+		clock_scaler -= PWM_CTRL_PRE_INPUT_MIN - PWM_CTRL_PRE_MIN;
+		clock_divider -= PWM_CTRL_DIV_INPUT_MIN - PWM_CTRL_DIV_MIN;
+
+		reg_pwm_ctrl |= (clock_scaler << PWM_CTRL_PRE_SHIFT)
+				& PWM_CTRL_PRE_MASK;
+		reg_pwm_ctrl |= (clock_divider << PWM_CTRL_DIV_SHIFT)
+				& PWM_CTRL_DIV_MASK;
+	}
+
+	mutex_lock(&chip->lock);
+	rc = pm8xxx_reg_write(chip, chip->cdata.reg_addr_pwm_ctrl, reg_pwm_ctrl,
+			      mask, &chip->reg_pwm_ctrl);
+	mutex_unlock(&chip->lock);
+
+	return rc;
+}
+EXPORT_SYMBOL(pm8xxx_batt_alarm_pwm_rate_set);
+
+/*
+ * Handle the BATT_ALARM interrupt:
+ * Battery voltage is above or below threshold range.
+ */
+static irqreturn_t pm8xxx_batt_alarm_isr(int irq, void *data)
+{
+	struct pm8xxx_batt_alarm_chip *chip = data;
+
+	disable_irq_nosync(chip->irq);
+	schedule_work(&chip->irq_work);
+
+	return IRQ_HANDLED;
+}
+
+static void pm8xxx_batt_alarm_isr_work(struct work_struct *work)
+{
+	struct pm8xxx_batt_alarm_chip *chip
+		= container_of(work, struct pm8xxx_batt_alarm_chip, irq_work);
+	int status;
+
+	if (!chip)
+		return;
+
+	status = pm8xxx_batt_alarm_status_read();
+
+	if (status < 0)
+		pr_err("failed to read status, rc=%d\n", status);
+	else
+		srcu_notifier_call_chain(&chip->irq_notifier_list,
+						status, NULL);
+
+	enable_irq(chip->irq);
+}
+
+/**
+ * pm8xxx_batt_alarm_register_notifier - register a notifier to run when a
+ *	battery voltage change interrupt fires
+ * @nb:	notifier block containing callback function to register
+ *
+ * nb->notifier_call must point to a function of this form -
+ * int (*notifier_call)(struct notifier_block *nb, unsigned long status,
+ *			void *unused);
+ * "status" will receive the battery alarm status; "unused" will be NULL.
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_batt_alarm_register_notifier(struct notifier_block *nb)
+{
+	struct pm8xxx_batt_alarm_chip *chip = the_battalarm;
+	int rc;
+
+	if (!chip) {
+		pr_err("no battery alarm device found.\n");
+		return -ENXIO;
+	}
+
+	rc = srcu_notifier_chain_register(&chip->irq_notifier_list, nb);
+	mutex_lock(&chip->lock);
+	if (rc == 0) {
+		if (chip->notifier_count == 0) {
+			enable_irq(chip->irq);
+			rc = irq_set_irq_wake(chip->irq, 1);
+		}
+
+		chip->notifier_count++;
+	}
+
+	mutex_unlock(&chip->lock);
+	return rc;
+}
+EXPORT_SYMBOL(pm8xxx_batt_alarm_register_notifier);
+
+/**
+ * pm8xxx_batt_alarm_unregister_notifier - unregister a notifier that is run
+ *	when a battery voltage change interrupt fires
+ * @nb:	notifier block containing callback function to unregister
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_batt_alarm_unregister_notifier(struct notifier_block *nb)
+{
+	struct pm8xxx_batt_alarm_chip *chip = the_battalarm;
+	int rc;
+
+	if (!chip) {
+		pr_err("no battery alarm device found.\n");
+		return -ENXIO;
+	}
+
+	rc = srcu_notifier_chain_unregister(&chip->irq_notifier_list, nb);
+	if (rc == 0) {
+		mutex_lock(&chip->lock);
+
+		chip->notifier_count--;
+
+		if (chip->notifier_count == 0) {
+			rc = irq_set_irq_wake(chip->irq, 0);
+			disable_irq(chip->irq);
+		}
+
+		WARN_ON(chip->notifier_count < 0);
+
+		mutex_unlock(&chip->lock);
+	}
+
+	return rc;
+}
+EXPORT_SYMBOL(pm8xxx_batt_alarm_unregister_notifier);
+
+static int pm8xxx_batt_alarm_reg_init(struct pm8xxx_batt_alarm_chip *chip)
+{
+	int rc = 0;
+
+	/* save the current register states */
+	rc = pm8xxx_readb(chip->dev->parent, chip->cdata.reg_addr_threshold,
+			  &chip->reg_threshold);
+	if (rc)
+		goto bail;
+
+	rc = pm8xxx_readb(chip->dev->parent, chip->cdata.reg_addr_ctrl1,
+			  &chip->reg_ctrl1);
+	if (rc)
+		goto bail;
+
+	rc = pm8xxx_readb(chip->dev->parent, chip->cdata.reg_addr_ctrl2,
+			  &chip->reg_ctrl2);
+	if (rc)
+		goto bail;
+
+	rc = pm8xxx_readb(chip->dev->parent, chip->cdata.reg_addr_pwm_ctrl,
+			  &chip->reg_pwm_ctrl);
+	if (rc)
+		goto bail;
+
+bail:
+	if (rc)
+		pr_err("pm8xxx_readb failed; initial register states "
+			"unknown, rc=%d\n", rc);
+	return rc;
+}
+
+/* TODO: should this default setting function be removed? */
+static int pm8xxx_batt_alarm_config_defaults(void)
+{
+	int rc = 0;
+
+	/* Use default values when no platform data is provided. */
+	rc = pm8xxx_batt_alarm_threshold_set(PM8XXX_BATT_ALARM_LOWER_COMPARATOR,
+		DEFAULT_THRESHOLD_LOWER);
+	if (rc) {
+		pr_err("threshold_set failed, rc=%d\n", rc);
+		goto done;
+	}
+
+	rc = pm8xxx_batt_alarm_threshold_set(PM8XXX_BATT_ALARM_UPPER_COMPARATOR,
+		DEFAULT_THRESHOLD_UPPER);
+	if (rc) {
+		pr_err("threshold_set failed, rc=%d\n", rc);
+		goto done;
+	}
+
+	rc = pm8xxx_batt_alarm_hold_time_set(DEFAULT_HOLD_TIME);
+	if (rc) {
+		pr_err("hold_time_set failed, rc=%d\n", rc);
+		goto done;
+	}
+
+	rc = pm8xxx_batt_alarm_pwm_rate_set(DEFAULT_USE_PWM,
+			DEFAULT_PWM_SCALER, DEFAULT_PWM_DIVIDER);
+	if (rc) {
+		pr_err("pwm_rate_set failed, rc=%d\n", rc);
+		goto done;
+	}
+
+	rc = pm8xxx_batt_alarm_disable(PM8XXX_BATT_ALARM_LOWER_COMPARATOR);
+	if (rc) {
+		pr_err("disable lower failed, rc=%d\n", rc);
+		goto done;
+	}
+
+	rc = pm8xxx_batt_alarm_disable(PM8XXX_BATT_ALARM_UPPER_COMPARATOR);
+	if (rc) {
+		pr_err("disable upper failed, rc=%d\n", rc);
+		goto done;
+	}
+
+done:
+	return rc;
+}
+
+static int __devinit pm8xxx_batt_alarm_probe(struct platform_device *pdev)
+{
+	const struct pm8xxx_batt_alarm_core_data *cdata
+			= pdev->dev.platform_data;
+	struct pm8xxx_batt_alarm_chip *chip;
+	struct resource *res;
+	int rc;
+
+	if (the_battalarm) {
+		pr_err("A PMIC battery alarm device has already probed.\n");
+		return -ENODEV;
+	}
+
+	if (!cdata) {
+		pr_err("missing core data\n");
+		return -EINVAL;
+	}
+
+	if (!cdata->irq_name) {
+		pr_err("missing IRQ name\n");
+		return -EINVAL;
+	}
+
+	chip = kzalloc(sizeof(struct pm8xxx_batt_alarm_chip), GFP_KERNEL);
+	if (chip == NULL) {
+		pr_err("kzalloc() failed.\n");
+		return -ENOMEM;
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+		cdata->irq_name);
+	if (res) {
+		chip->irq = res->start;
+	} else {
+		pr_err("Battery alarm IRQ not specified\n");
+		rc = -EINVAL;
+		goto err_free_chip;
+	}
+
+	chip->dev = &pdev->dev;
+	memcpy(&(chip->cdata), cdata,
+		sizeof(struct pm8xxx_batt_alarm_core_data));
+
+	srcu_init_notifier_head(&chip->irq_notifier_list);
+
+	chip->notifier_count = 0;
+	mutex_init(&chip->lock);
+
+	the_battalarm = chip;
+
+	rc = pm8xxx_batt_alarm_reg_init(chip);
+	if (rc)
+		goto err_free_mutex;
+
+	rc = pm8xxx_batt_alarm_config_defaults();
+	if (rc)
+		goto err_free_mutex;
+
+	INIT_WORK(&chip->irq_work, pm8xxx_batt_alarm_isr_work);
+
+/* TODO: Is it best to trigger on both edges? Should this be configurable? */
+	rc = request_irq(chip->irq, pm8xxx_batt_alarm_isr,
+		IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, cdata->irq_name,
+		chip);
+	if (rc < 0) {
+		pr_err("request_irq(%d) failed, rc=%d\n", chip->irq, rc);
+		goto err_cancel_work;
+	}
+
+	/* Disable the IRQ until a notifier is registered. */
+	disable_irq(chip->irq);
+
+	platform_set_drvdata(pdev, chip);
+
+	return 0;
+
+err_cancel_work:
+	cancel_work_sync(&chip->irq_work);
+err_free_mutex:
+	mutex_destroy(&chip->lock);
+	srcu_cleanup_notifier_head(&chip->irq_notifier_list);
+err_free_chip:
+	kfree(chip);
+	the_battalarm = NULL;
+
+	return rc;
+}
+
+static int __devexit pm8xxx_batt_alarm_remove(struct platform_device *pdev)
+{
+	struct pm8xxx_batt_alarm_chip *chip = platform_get_drvdata(pdev);
+
+	if (chip) {
+		platform_set_drvdata(pdev, NULL);
+		irq_set_irq_wake(chip->irq, 0);
+		free_irq(chip->irq, chip);
+		cancel_work_sync(&chip->irq_work);
+		srcu_cleanup_notifier_head(&chip->irq_notifier_list);
+		mutex_destroy(&chip->lock);
+		kfree(chip);
+		the_battalarm = NULL;
+	}
+
+	return 0;
+}
+
+static struct platform_driver pm8xxx_batt_alarm_driver = {
+	.probe	= pm8xxx_batt_alarm_probe,
+	.remove	= __devexit_p(pm8xxx_batt_alarm_remove),
+	.driver	= {
+		.name = PM8XXX_BATT_ALARM_DEV_NAME,
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init pm8xxx_batt_alarm_init(void)
+{
+	return platform_driver_register(&pm8xxx_batt_alarm_driver);
+}
+
+static void __exit pm8xxx_batt_alarm_exit(void)
+{
+	platform_driver_unregister(&pm8xxx_batt_alarm_driver);
+}
+
+module_init(pm8xxx_batt_alarm_init);
+module_exit(pm8xxx_batt_alarm_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC PM8xxx Battery Alarm");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:" PM8XXX_BATT_ALARM_DEV_NAME);
diff --git a/drivers/mfd/pm8xxx-debug.c b/drivers/mfd/pm8xxx-debug.c
new file mode 100644
index 0000000..d450db3
--- /dev/null
+++ b/drivers/mfd/pm8xxx-debug.c
@@ -0,0 +1,216 @@
+/*
+ * 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/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/debugfs.h>
+
+#define PM8XXX_DEBUG_DEV_NAME "pm8xxx-debug"
+
+struct pm8xxx_debug_device {
+	struct mutex		debug_mutex;
+	struct device		*parent;
+	struct dentry		*dir;
+	int			addr;
+};
+
+static bool pm8xxx_debug_addr_is_valid(int addr)
+{
+	if (addr < 0 || addr > 0x3FF) {
+		pr_err("PMIC register address is invalid: %d\n", addr);
+		return false;
+	}
+	return true;
+}
+
+static int pm8xxx_debug_data_set(void *data, u64 val)
+{
+	struct pm8xxx_debug_device *debugdev = data;
+	u8 reg = val;
+	int rc;
+
+	mutex_lock(&debugdev->debug_mutex);
+
+	if (pm8xxx_debug_addr_is_valid(debugdev->addr)) {
+		rc = pm8xxx_writeb(debugdev->parent, debugdev->addr, reg);
+
+		if (rc)
+			pr_err("pm8xxx_writeb(0x%03X)=0x%02X failed: rc=%d\n",
+				debugdev->addr, reg, rc);
+	}
+
+	mutex_unlock(&debugdev->debug_mutex);
+	return 0;
+}
+
+static int pm8xxx_debug_data_get(void *data, u64 *val)
+{
+	struct pm8xxx_debug_device *debugdev = data;
+	int rc;
+	u8 reg;
+
+	mutex_lock(&debugdev->debug_mutex);
+
+	if (pm8xxx_debug_addr_is_valid(debugdev->addr)) {
+		rc = pm8xxx_readb(debugdev->parent, debugdev->addr, &reg);
+
+		if (rc)
+			pr_err("pm8xxx_readb(0x%03X) failed: rc=%d\n",
+				debugdev->addr, rc);
+		else
+			*val = reg;
+	}
+
+	mutex_unlock(&debugdev->debug_mutex);
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(debug_data_fops, pm8xxx_debug_data_get,
+			pm8xxx_debug_data_set, "0x%02llX\n");
+
+static int pm8xxx_debug_addr_set(void *data, u64 val)
+{
+	struct pm8xxx_debug_device *debugdev = data;
+
+	if (pm8xxx_debug_addr_is_valid(val)) {
+		mutex_lock(&debugdev->debug_mutex);
+		debugdev->addr = val;
+		mutex_unlock(&debugdev->debug_mutex);
+	}
+
+	return 0;
+}
+
+static int pm8xxx_debug_addr_get(void *data, u64 *val)
+{
+	struct pm8xxx_debug_device *debugdev = data;
+
+	mutex_lock(&debugdev->debug_mutex);
+
+	if (pm8xxx_debug_addr_is_valid(debugdev->addr))
+		*val = debugdev->addr;
+
+	mutex_unlock(&debugdev->debug_mutex);
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(debug_addr_fops, pm8xxx_debug_addr_get,
+			pm8xxx_debug_addr_set, "0x%03llX\n");
+
+static int __devinit pm8xxx_debug_probe(struct platform_device *pdev)
+{
+	char *name = pdev->dev.platform_data;
+	struct pm8xxx_debug_device *debugdev;
+	struct dentry *dir;
+	struct dentry *temp;
+	int rc;
+
+	if (name == NULL) {
+		pr_err("debugfs directory name must be specified in "
+			"platform_data pointer\n");
+		return -EINVAL;
+	}
+
+	debugdev = kzalloc(sizeof(struct pm8xxx_debug_device), GFP_KERNEL);
+	if (debugdev == NULL) {
+		pr_err("kzalloc failed\n");
+		return -ENOMEM;
+	}
+
+	debugdev->parent = pdev->dev.parent;
+	debugdev->addr = -1;
+
+	dir = debugfs_create_dir(name, NULL);
+	if (dir == NULL || IS_ERR(dir)) {
+		pr_err("debugfs_create_dir failed: rc=%ld\n", PTR_ERR(dir));
+		rc = PTR_ERR(dir);
+		goto dir_error;
+	}
+
+	temp = debugfs_create_file("addr", S_IRUSR | S_IWUSR, dir, debugdev,
+				   &debug_addr_fops);
+	if (temp == NULL || IS_ERR(temp)) {
+		pr_err("debugfs_create_file failed: rc=%ld\n", PTR_ERR(temp));
+		rc = PTR_ERR(temp);
+		goto file_error;
+	}
+
+	temp = debugfs_create_file("data", S_IRUSR | S_IWUSR, dir, debugdev,
+				   &debug_data_fops);
+	if (temp == NULL || IS_ERR(temp)) {
+		pr_err("debugfs_create_file failed: rc=%ld\n", PTR_ERR(temp));
+		rc = PTR_ERR(temp);
+		goto file_error;
+	}
+
+	mutex_init(&debugdev->debug_mutex);
+
+	debugdev->dir = dir;
+	platform_set_drvdata(pdev, debugdev);
+
+	return 0;
+
+file_error:
+	debugfs_remove_recursive(dir);
+dir_error:
+	kfree(debugdev);
+
+	return rc;
+}
+
+static int __devexit pm8xxx_debug_remove(struct platform_device *pdev)
+{
+	struct pm8xxx_debug_device *debugdev = platform_get_drvdata(pdev);
+
+	if (debugdev) {
+		debugfs_remove_recursive(debugdev->dir);
+		mutex_destroy(&debugdev->debug_mutex);
+		kfree(debugdev);
+	}
+
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+static struct platform_driver pm8xxx_debug_driver = {
+	.probe		= pm8xxx_debug_probe,
+	.remove		= __devexit_p(pm8xxx_debug_remove),
+	.driver		= {
+		.name	= PM8XXX_DEBUG_DEV_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init pm8xxx_debug_init(void)
+{
+	return platform_driver_register(&pm8xxx_debug_driver);
+}
+subsys_initcall(pm8xxx_debug_init);
+
+static void __exit pm8xxx_debug_exit(void)
+{
+	platform_driver_unregister(&pm8xxx_debug_driver);
+}
+module_exit(pm8xxx_debug_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PM8XXX Debug driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:" PM8XXX_DEBUG_DEV_NAME);
diff --git a/drivers/mfd/pm8xxx-irq.c b/drivers/mfd/pm8xxx-irq.c
index d452dd0..cc156b3 100644
--- a/drivers/mfd/pm8xxx-irq.c
+++ b/drivers/mfd/pm8xxx-irq.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2011-2012, 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
@@ -13,6 +13,7 @@
 
 #define pr_fmt(fmt)	"%s: " fmt, __func__
 
+#include <linux/export.h>
 #include <linux/err.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
@@ -24,17 +25,15 @@
 
 /* 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 SSBI_REG_ADDR_IRQ_ROOT(base)		(base + 0)
+#define SSBI_REG_ADDR_IRQ_M_STATUS1(base)	(base + 1)
+#define SSBI_REG_ADDR_IRQ_M_STATUS2(base)	(base + 2)
+#define SSBI_REG_ADDR_IRQ_M_STATUS3(base)	(base + 3)
+#define SSBI_REG_ADDR_IRQ_M_STATUS4(base)	(base + 4)
+#define SSBI_REG_ADDR_IRQ_BLK_SEL(base)		(base + 5)
+#define SSBI_REG_ADDR_IRQ_IT_STATUS(base)	(base + 6)
+#define SSBI_REG_ADDR_IRQ_CONFIG(base)		(base + 7)
+#define SSBI_REG_ADDR_IRQ_RT_STATUS(base)	(base + 8)
 
 #define	PM_IRQF_LVL_SEL			0x01	/* level select */
 #define	PM_IRQF_MASK_FE			0x02	/* mask falling edge */
@@ -50,6 +49,7 @@
 struct pm_irq_chip {
 	struct device		*dev;
 	spinlock_t		pm_irq_lock;
+	unsigned int		base_addr;
 	unsigned int		devirq;
 	unsigned int		irq_base;
 	unsigned int		num_irqs;
@@ -60,13 +60,14 @@
 
 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);
+	return pm8xxx_readb(chip->dev,
+			SSBI_REG_ADDR_IRQ_ROOT(chip->base_addr), 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);
+			SSBI_REG_ADDR_IRQ_M_STATUS1(chip->base_addr) + m, bp);
 }
 
 static int pm8xxx_read_block_irq(struct pm_irq_chip *chip, u8 bp, u8 *ip)
@@ -74,13 +75,15 @@
 	int	rc;
 
 	spin_lock(&chip->pm_irq_lock);
-	rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, bp);
+	rc = pm8xxx_writeb(chip->dev,
+			SSBI_REG_ADDR_IRQ_BLK_SEL(chip->base_addr), 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);
+	rc = pm8xxx_readb(chip->dev,
+			SSBI_REG_ADDR_IRQ_IT_STATUS(chip->base_addr), ip);
 	if (rc)
 		pr_err("Failed Reading Status rc=%d\n", rc);
 bail:
@@ -88,19 +91,51 @@
 	return rc;
 }
 
-static int pm8xxx_config_irq(struct pm_irq_chip *chip, u8 bp, u8 cp)
+static int pm8xxx_read_config_irq(struct pm_irq_chip *chip, u8 bp, u8 cp, u8 *r)
 {
 	int	rc;
 
 	spin_lock(&chip->pm_irq_lock);
-	rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, bp);
+	rc = pm8xxx_writeb(chip->dev,
+			SSBI_REG_ADDR_IRQ_BLK_SEL(chip->base_addr), 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(chip->base_addr), cp);
+	if (rc)
+		pr_err("Failed Configuring IRQ rc=%d\n", rc);
+
+	rc = pm8xxx_readb(chip->dev,
+			SSBI_REG_ADDR_IRQ_CONFIG(chip->base_addr), r);
+	if (rc)
+		pr_err("Failed reading IRQ rc=%d\n", rc);
+bail:
+	spin_unlock(&chip->pm_irq_lock);
+	return rc;
+}
+
+static int pm8xxx_write_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(chip->base_addr), bp);
+	if (rc) {
+		pr_err("Failed Selecting Block %d rc=%d\n", bp, rc);
+		goto bail;
+	}
+	/*
+	 * Set the write bit here as this could be a unrequested irq
+	 * whose PM_IRQF_WRITE bit is not set
+	 */
 	cp |= PM_IRQF_WRITE;
-	rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_CONFIG, cp);
+	rc = pm8xxx_writeb(chip->dev,
+			SSBI_REG_ADDR_IRQ_CONFIG(chip->base_addr), cp);
 	if (rc)
 		pr_err("Failed Configuring IRQ rc=%d\n", rc);
 bail:
@@ -157,17 +192,16 @@
 	return ret;
 }
 
-static void pm8xxx_irq_handler(unsigned int irq, struct irq_desc *desc)
+static irqreturn_t pm8xxx_irq_handler(int irq, void *data)
 {
-	struct pm_irq_chip *chip = irq_desc_get_handler_data(desc);
-	struct irq_chip *irq_chip = irq_desc_get_chip(desc);
+	struct pm_irq_chip *chip = data;
 	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;
+		return IRQ_HANDLED;
 	}
 
 	/* on pm8xxx series masters start from bit 1 of the root */
@@ -178,7 +212,27 @@
 		if (masters & (1 << i))
 			pm8xxx_irq_master_handler(chip, i);
 
-	irq_chip->irq_ack(&desc->irq_data);
+	return IRQ_HANDLED;
+}
+
+static void pm8xxx_irq_mask(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;
+
+	if (chip->config[pmirq] == 0) {
+		pr_warn("masking rogue irq=%d pmirq=%d\n", d->irq, pmirq);
+		chip->config[pmirq] = irq_bit << PM_IRQF_BITS_SHIFT;
+	}
+
+	config = chip->config[pmirq] | PM_IRQF_MASK_ALL;
+	pm8xxx_write_config_irq(chip, block, config);
 }
 
 static void pm8xxx_irq_mask_ack(struct irq_data *d)
@@ -192,8 +246,13 @@
 	master = block / 8;
 	irq_bit = pmirq % 8;
 
+	if (chip->config[pmirq] == 0) {
+		pr_warn("mask acking rogue irq=%d pmirq=%d\n", d->irq, pmirq);
+		chip->config[pmirq] = irq_bit << PM_IRQF_BITS_SHIFT;
+	}
+
 	config = chip->config[pmirq] | PM_IRQF_MASK_ALL | PM_IRQF_CLR;
-	pm8xxx_config_irq(chip, block, config);
+	pm8xxx_write_config_irq(chip, block, config);
 }
 
 static void pm8xxx_irq_unmask(struct irq_data *d)
@@ -201,14 +260,17 @@
 	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;
+	u8	block, config, hw_conf;
 
 	block = pmirq / 8;
 	master = block / 8;
 	irq_bit = pmirq % 8;
 
 	config = chip->config[pmirq];
-	pm8xxx_config_irq(chip, block, config);
+	pm8xxx_read_config_irq(chip, block, config, &hw_conf);
+	/* check if it is masked */
+	if ((hw_conf & PM_IRQF_MASK_ALL) == PM_IRQF_MASK_ALL)
+		pm8xxx_write_config_irq(chip, block, config);
 }
 
 static int pm8xxx_irq_set_type(struct irq_data *d, unsigned int flow_type)
@@ -238,8 +300,14 @@
 			chip->config[pmirq] &= ~PM_IRQF_MASK_FE;
 	}
 
+	/*
+	 * The PM_IRQF_WRITE flag serves as an indication that this interrupt
+	 * been requested
+	 */
+	chip->config[pmirq] |= PM_IRQF_WRITE;
+
 	config = chip->config[pmirq] | PM_IRQF_CLR;
-	return pm8xxx_config_irq(chip, block, config);
+	return pm8xxx_write_config_irq(chip, block, config);
 }
 
 static int pm8xxx_irq_set_wake(struct irq_data *d, unsigned int on)
@@ -247,12 +315,21 @@
 	return 0;
 }
 
+static int pm8xxx_irq_read_line(struct irq_data *d)
+{
+	struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
+
+	return pm8xxx_get_irq_stat(chip, d->irq);
+}
+
 static struct irq_chip pm8xxx_irq_chip = {
 	.name		= "pm8xxx",
+	.irq_mask	= pm8xxx_irq_mask,
 	.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,
+	.irq_read_line	= pm8xxx_irq_read_line,
 	.flags		= IRQCHIP_MASK_ON_SUSPEND,
 };
 
@@ -286,14 +363,16 @@
 
 	spin_lock_irqsave(&chip->pm_irq_lock, flags);
 
-	rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, block);
+	rc = pm8xxx_writeb(chip->dev,
+			SSBI_REG_ADDR_IRQ_BLK_SEL(chip->base_addr), 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);
+	rc = pm8xxx_readb(chip->dev,
+			SSBI_REG_ADDR_IRQ_RT_STATUS(chip->base_addr), &bits);
 	if (rc) {
 		pr_err("Failed Configuring irq=%d pmirq=%d blk=%d rc=%d\n",
 			irq, pmirq, block, rc);
@@ -339,6 +418,7 @@
 	chip->devirq = devirq;
 	chip->irq_base = pdata->irq_base;
 	chip->num_irqs = pdata->irq_cdata.nirqs;
+	chip->base_addr = pdata->irq_cdata.base_addr;
 	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);
@@ -355,15 +435,22 @@
 #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);
+	if (devirq != 0) {
+		rc = request_irq(devirq, pm8xxx_irq_handler,
+				pdata->irq_trigger_flag,
+				"pm8xxx_usr_irq", chip);
+		if (rc) {
+			pr_err("failed to request_irq for %d rc=%d\n",
+								devirq, rc);
+		} else {
+			irq_set_irq_wake(devirq, 1);
+		}
+	}
 
 	return chip;
 }
 
-int __devexit pm8xxx_irq_exit(struct pm_irq_chip *chip)
+int pm8xxx_irq_exit(struct pm_irq_chip *chip)
 {
 	irq_set_chained_handler(chip->devirq, NULL);
 	kfree(chip);
diff --git a/drivers/mfd/pm8xxx-misc.c b/drivers/mfd/pm8xxx-misc.c
new file mode 100644
index 0000000..0af013e
--- /dev/null
+++ b/drivers/mfd/pm8xxx-misc.c
@@ -0,0 +1,1256 @@
+/*
+ * Copyright (c) 2011-2012, 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/module.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/mfd/pm8xxx/misc.h>
+
+/* PON CTRL 1 register */
+#define REG_PM8XXX_PON_CTRL_1			0x01C
+
+#define PON_CTRL_1_PULL_UP_MASK			0xE0
+#define PON_CTRL_1_USB_PWR_EN			0x10
+
+#define PON_CTRL_1_WD_EN_MASK			0x08
+#define PON_CTRL_1_WD_EN_RESET			0x08
+#define PON_CTRL_1_WD_EN_PWR_OFF		0x00
+
+/* PON CNTL registers */
+#define REG_PM8058_PON_CNTL_4			0x098
+#define REG_PM8901_PON_CNTL_4			0x099
+#define REG_PM8018_PON_CNTL_4			0x01E
+#define REG_PM8921_PON_CNTL_4			0x01E
+#define REG_PM8058_PON_CNTL_5			0x07B
+#define REG_PM8901_PON_CNTL_5			0x09A
+#define REG_PM8018_PON_CNTL_5			0x01F
+#define REG_PM8921_PON_CNTL_5			0x01F
+
+#define PON_CTRL_4_RESET_EN_MASK		0x01
+#define PON_CTRL_4_SHUTDOWN_ON_RESET		0x0
+#define PON_CTRL_4_RESTART_ON_RESET		0x1
+#define PON_CTRL_5_HARD_RESET_EN_MASK		0x08
+#define PON_CTRL_5_HARD_RESET_EN		0x08
+#define PON_CTRL_5_HARD_RESET_DIS		0x00
+
+/* Regulator master enable addresses */
+#define REG_PM8058_VREG_EN_MSM			0x018
+#define REG_PM8058_VREG_EN_GRP_5_4		0x1C8
+
+/* Regulator control registers for shutdown/reset */
+#define REG_PM8058_S0_CTRL			0x004
+#define REG_PM8058_S1_CTRL			0x005
+#define REG_PM8058_S3_CTRL			0x111
+#define REG_PM8058_L21_CTRL			0x120
+#define REG_PM8058_L22_CTRL			0x121
+
+#define PM8058_REGULATOR_ENABLE_MASK		0x80
+#define PM8058_REGULATOR_ENABLE			0x80
+#define PM8058_REGULATOR_DISABLE		0x00
+#define PM8058_REGULATOR_PULL_DOWN_MASK		0x40
+#define PM8058_REGULATOR_PULL_DOWN_EN		0x40
+
+/* Buck CTRL register */
+#define PM8058_SMPS_LEGACY_VREF_SEL		0x20
+#define PM8058_SMPS_LEGACY_VPROG_MASK		0x1F
+#define PM8058_SMPS_ADVANCED_BAND_MASK		0xC0
+#define PM8058_SMPS_ADVANCED_BAND_SHIFT		6
+#define PM8058_SMPS_ADVANCED_VPROG_MASK		0x3F
+
+/* Buck TEST2 registers for shutdown/reset */
+#define REG_PM8058_S0_TEST2			0x084
+#define REG_PM8058_S1_TEST2			0x085
+#define REG_PM8058_S3_TEST2			0x11A
+
+#define PM8058_REGULATOR_BANK_WRITE		0x80
+#define PM8058_REGULATOR_BANK_MASK		0x70
+#define PM8058_REGULATOR_BANK_SHIFT		4
+#define PM8058_REGULATOR_BANK_SEL(n)	((n) << PM8058_REGULATOR_BANK_SHIFT)
+
+/* Buck TEST2 register bank 1 */
+#define PM8058_SMPS_LEGACY_VLOW_SEL		0x01
+
+/* Buck TEST2 register bank 7 */
+#define PM8058_SMPS_ADVANCED_MODE_MASK		0x02
+#define PM8058_SMPS_ADVANCED_MODE		0x02
+#define PM8058_SMPS_LEGACY_MODE			0x00
+
+/* SLEEP CTRL register */
+#define REG_PM8058_SLEEP_CTRL			0x02B
+#define REG_PM8921_SLEEP_CTRL			0x10A
+#define REG_PM8018_SLEEP_CTRL			0x10A
+
+#define SLEEP_CTRL_SMPL_EN_MASK			0x04
+#define SLEEP_CTRL_SMPL_EN_RESET		0x04
+#define SLEEP_CTRL_SMPL_EN_PWR_OFF		0x00
+
+#define SLEEP_CTRL_SMPL_SEL_MASK		0x03
+#define SLEEP_CTRL_SMPL_SEL_MIN			0
+#define SLEEP_CTRL_SMPL_SEL_MAX			3
+
+/* FTS regulator PMR registers */
+#define REG_PM8901_REGULATOR_S1_PMR		0xA7
+#define REG_PM8901_REGULATOR_S2_PMR		0xA8
+#define REG_PM8901_REGULATOR_S3_PMR		0xA9
+#define REG_PM8901_REGULATOR_S4_PMR		0xAA
+
+#define PM8901_REGULATOR_PMR_STATE_MASK		0x60
+#define PM8901_REGULATOR_PMR_STATE_OFF		0x20
+
+/* COINCELL CHG registers */
+#define REG_PM8058_COIN_CHG			0x02F
+#define REG_PM8921_COIN_CHG			0x09C
+#define REG_PM8018_COIN_CHG			0x09C
+
+#define COINCELL_RESISTOR_SHIFT			0x2
+
+/* GP TEST register */
+#define REG_PM8XXX_GP_TEST_1			0x07A
+
+/* Stay on configuration */
+#define PM8XXX_STAY_ON_CFG			0x92
+
+/* GPIO UART MUX CTRL registers */
+#define REG_PM8XXX_GPIO_MUX_CTRL		0x1CC
+
+#define UART_PATH_SEL_MASK			0x60
+#define UART_PATH_SEL_SHIFT			0x5
+
+#define USB_ID_PU_EN_MASK			0x10	/* PM8921 family only */
+#define USB_ID_PU_EN_SHIFT			4
+
+/* Shutdown/restart delays to allow for LDO 7/dVdd regulator load settling. */
+#define PM8901_DELAY_AFTER_REG_DISABLE_MS	4
+#define PM8901_DELAY_BEFORE_SHUTDOWN_MS		8
+
+#define REG_PM8XXX_XO_CNTRL_2	0x114
+#define MP3_1_MASK	0xE0
+#define MP3_2_MASK	0x1C
+#define MP3_1_SHIFT	5
+#define MP3_2_SHIFT	2
+
+#define REG_HSED_BIAS0_CNTL2		0xA1
+#define REG_HSED_BIAS1_CNTL2		0x135
+#define REG_HSED_BIAS2_CNTL2		0x138
+#define HSED_EN_MASK			0xC0
+
+struct pm8xxx_misc_chip {
+	struct list_head			link;
+	struct pm8xxx_misc_platform_data	pdata;
+	struct device				*dev;
+	enum pm8xxx_version			version;
+	u64					osc_halt_count;
+};
+
+static LIST_HEAD(pm8xxx_misc_chips);
+static DEFINE_SPINLOCK(pm8xxx_misc_chips_lock);
+
+static int pm8xxx_misc_masked_write(struct pm8xxx_misc_chip *chip, u16 addr,
+				    u8 mask, u8 val)
+{
+	int rc;
+	u8 reg;
+
+	rc = pm8xxx_readb(chip->dev->parent, addr, &reg);
+	if (rc) {
+		pr_err("pm8xxx_readb(0x%03X) failed, rc=%d\n", addr, rc);
+		return rc;
+	}
+	reg &= ~mask;
+	reg |= val & mask;
+	rc = pm8xxx_writeb(chip->dev->parent, addr, reg);
+	if (rc)
+		pr_err("pm8xxx_writeb(0x%03X)=0x%02X failed, rc=%d\n", addr,
+			reg, rc);
+	return rc;
+}
+
+/*
+ * Set an SMPS regulator to be disabled in its CTRL register, but enabled
+ * in the master enable register.  Also set it's pull down enable bit.
+ * Take care to make sure that the output voltage doesn't change if switching
+ * from advanced mode to legacy mode.
+ */
+static int
+__pm8058_disable_smps_locally_set_pull_down(struct pm8xxx_misc_chip *chip,
+	u16 ctrl_addr, u16 test2_addr, u16 master_enable_addr,
+	u8 master_enable_bit)
+{
+	int rc = 0;
+	u8 vref_sel, vlow_sel, band, vprog, bank, reg;
+
+	bank = PM8058_REGULATOR_BANK_SEL(7);
+	rc = pm8xxx_writeb(chip->dev->parent, test2_addr, bank);
+	if (rc) {
+		pr_err("%s: pm8xxx_writeb(0x%03X) failed: rc=%d\n", __func__,
+			test2_addr, rc);
+		goto done;
+	}
+
+	rc = pm8xxx_readb(chip->dev->parent, test2_addr, &reg);
+	if (rc) {
+		pr_err("%s: FAIL pm8xxx_readb(0x%03X): rc=%d\n",
+		       __func__, test2_addr, rc);
+		goto done;
+	}
+
+	/* Check if in advanced mode. */
+	if ((reg & PM8058_SMPS_ADVANCED_MODE_MASK) ==
+					PM8058_SMPS_ADVANCED_MODE) {
+		/* Determine current output voltage. */
+		rc = pm8xxx_readb(chip->dev->parent, ctrl_addr, &reg);
+		if (rc) {
+			pr_err("%s: FAIL pm8xxx_readb(0x%03X): rc=%d\n",
+			       __func__, ctrl_addr, rc);
+			goto done;
+		}
+
+		band = (reg & PM8058_SMPS_ADVANCED_BAND_MASK)
+			>> PM8058_SMPS_ADVANCED_BAND_SHIFT;
+		switch (band) {
+		case 3:
+			vref_sel = 0;
+			vlow_sel = 0;
+			break;
+		case 2:
+			vref_sel = PM8058_SMPS_LEGACY_VREF_SEL;
+			vlow_sel = 0;
+			break;
+		case 1:
+			vref_sel = PM8058_SMPS_LEGACY_VREF_SEL;
+			vlow_sel = PM8058_SMPS_LEGACY_VLOW_SEL;
+			break;
+		default:
+			pr_err("%s: regulator already disabled\n", __func__);
+			return -EPERM;
+		}
+		vprog = (reg & PM8058_SMPS_ADVANCED_VPROG_MASK);
+		/* Round up if fine step is in use. */
+		vprog = (vprog + 1) >> 1;
+		if (vprog > PM8058_SMPS_LEGACY_VPROG_MASK)
+			vprog = PM8058_SMPS_LEGACY_VPROG_MASK;
+
+		/* Set VLOW_SEL bit. */
+		bank = PM8058_REGULATOR_BANK_SEL(1);
+		rc = pm8xxx_writeb(chip->dev->parent, test2_addr, bank);
+		if (rc) {
+			pr_err("%s: FAIL pm8xxx_writeb(0x%03X): rc=%d\n",
+			       __func__, test2_addr, rc);
+			goto done;
+		}
+
+		rc = pm8xxx_misc_masked_write(chip, test2_addr,
+			PM8058_REGULATOR_BANK_WRITE | PM8058_REGULATOR_BANK_MASK
+				| PM8058_SMPS_LEGACY_VLOW_SEL,
+			PM8058_REGULATOR_BANK_WRITE |
+			PM8058_REGULATOR_BANK_SEL(1) | vlow_sel);
+		if (rc)
+			goto done;
+
+		/* Switch to legacy mode */
+		bank = PM8058_REGULATOR_BANK_SEL(7);
+		rc = pm8xxx_writeb(chip->dev->parent, test2_addr, bank);
+		if (rc) {
+			pr_err("%s: FAIL pm8xxx_writeb(0x%03X): rc=%d\n",
+					__func__, test2_addr, rc);
+			goto done;
+		}
+		rc = pm8xxx_misc_masked_write(chip, test2_addr,
+				PM8058_REGULATOR_BANK_WRITE |
+				PM8058_REGULATOR_BANK_MASK |
+				PM8058_SMPS_ADVANCED_MODE_MASK,
+				PM8058_REGULATOR_BANK_WRITE |
+				PM8058_REGULATOR_BANK_SEL(7) |
+				PM8058_SMPS_LEGACY_MODE);
+		if (rc)
+			goto done;
+
+		/* Enable locally, enable pull down, keep voltage the same. */
+		rc = pm8xxx_misc_masked_write(chip, ctrl_addr,
+			PM8058_REGULATOR_ENABLE_MASK |
+			PM8058_REGULATOR_PULL_DOWN_MASK |
+			PM8058_SMPS_LEGACY_VREF_SEL |
+			PM8058_SMPS_LEGACY_VPROG_MASK,
+			PM8058_REGULATOR_ENABLE | PM8058_REGULATOR_PULL_DOWN_EN
+				| vref_sel | vprog);
+		if (rc)
+			goto done;
+	}
+
+	/* Enable in master control register. */
+	rc = pm8xxx_misc_masked_write(chip, master_enable_addr,
+			master_enable_bit, master_enable_bit);
+	if (rc)
+		goto done;
+
+	/* Disable locally and enable pull down. */
+	rc = pm8xxx_misc_masked_write(chip, ctrl_addr,
+		PM8058_REGULATOR_ENABLE_MASK | PM8058_REGULATOR_PULL_DOWN_MASK,
+		PM8058_REGULATOR_DISABLE | PM8058_REGULATOR_PULL_DOWN_EN);
+
+done:
+	return rc;
+}
+
+static int
+__pm8058_disable_ldo_locally_set_pull_down(struct pm8xxx_misc_chip *chip,
+		u16 ctrl_addr, u16 master_enable_addr, u8 master_enable_bit)
+{
+	int rc;
+
+	/* Enable LDO in master control register. */
+	rc = pm8xxx_misc_masked_write(chip, master_enable_addr,
+			master_enable_bit, master_enable_bit);
+	if (rc)
+		goto done;
+
+	/* Disable LDO in CTRL register and set pull down */
+	rc = pm8xxx_misc_masked_write(chip, ctrl_addr,
+		PM8058_REGULATOR_ENABLE_MASK | PM8058_REGULATOR_PULL_DOWN_MASK,
+		PM8058_REGULATOR_DISABLE | PM8058_REGULATOR_PULL_DOWN_EN);
+
+done:
+	return rc;
+}
+
+static int __pm8018_reset_pwr_off(struct pm8xxx_misc_chip *chip, int reset)
+{
+	int rc;
+
+	/* Enable SMPL if resetting is desired. */
+	rc = pm8xxx_misc_masked_write(chip, REG_PM8018_SLEEP_CTRL,
+	       SLEEP_CTRL_SMPL_EN_MASK,
+	       (reset ? SLEEP_CTRL_SMPL_EN_RESET : SLEEP_CTRL_SMPL_EN_PWR_OFF));
+	if (rc) {
+		pr_err("pm8xxx_misc_masked_write failed, rc=%d\n", rc);
+		return rc;
+	}
+
+	/*
+	 * Select action to perform (reset or shutdown) when PS_HOLD goes low.
+	 * Also ensure that KPD, CBL0, and CBL1 pull ups are enabled and that
+	 * USB charging is enabled.
+	 */
+	rc = pm8xxx_misc_masked_write(chip, REG_PM8XXX_PON_CTRL_1,
+		PON_CTRL_1_PULL_UP_MASK | PON_CTRL_1_USB_PWR_EN
+		| PON_CTRL_1_WD_EN_MASK,
+		PON_CTRL_1_PULL_UP_MASK | PON_CTRL_1_USB_PWR_EN
+		| (reset ? PON_CTRL_1_WD_EN_RESET : PON_CTRL_1_WD_EN_PWR_OFF));
+	if (rc)
+		pr_err("pm8xxx_misc_masked_write failed, rc=%d\n", rc);
+
+	return rc;
+}
+
+static int __pm8058_reset_pwr_off(struct pm8xxx_misc_chip *chip, int reset)
+{
+	int rc;
+
+	/* When shutting down, enable active pulldowns on important rails. */
+	if (!reset) {
+		/* Disable SMPS's 0,1,3 locally and set pulldown enable bits. */
+		__pm8058_disable_smps_locally_set_pull_down(chip,
+			REG_PM8058_S0_CTRL, REG_PM8058_S0_TEST2,
+			REG_PM8058_VREG_EN_MSM, BIT(7));
+		__pm8058_disable_smps_locally_set_pull_down(chip,
+			REG_PM8058_S1_CTRL, REG_PM8058_S1_TEST2,
+			REG_PM8058_VREG_EN_MSM, BIT(6));
+		__pm8058_disable_smps_locally_set_pull_down(chip,
+			REG_PM8058_S3_CTRL, REG_PM8058_S3_TEST2,
+			REG_PM8058_VREG_EN_GRP_5_4, BIT(7) | BIT(4));
+		/* Disable LDO 21 locally and set pulldown enable bit. */
+		__pm8058_disable_ldo_locally_set_pull_down(chip,
+			REG_PM8058_L21_CTRL, REG_PM8058_VREG_EN_GRP_5_4,
+			BIT(1));
+	}
+
+	/*
+	 * Fix-up: Set regulator LDO22 to 1.225 V in high power mode. Leave its
+	 * pull-down state intact. This ensures a safe shutdown.
+	 */
+	rc = pm8xxx_misc_masked_write(chip, REG_PM8058_L22_CTRL, 0xBF, 0x93);
+	if (rc) {
+		pr_err("pm8xxx_misc_masked_write failed, rc=%d\n", rc);
+		goto read_write_err;
+	}
+
+	/* Enable SMPL if resetting is desired. */
+	rc = pm8xxx_misc_masked_write(chip, REG_PM8058_SLEEP_CTRL,
+	       SLEEP_CTRL_SMPL_EN_MASK,
+	       (reset ? SLEEP_CTRL_SMPL_EN_RESET : SLEEP_CTRL_SMPL_EN_PWR_OFF));
+	if (rc) {
+		pr_err("pm8xxx_misc_masked_write failed, rc=%d\n", rc);
+		goto read_write_err;
+	}
+
+	/*
+	 * Select action to perform (reset or shutdown) when PS_HOLD goes low.
+	 * Also ensure that KPD, CBL0, and CBL1 pull ups are enabled and that
+	 * USB charging is enabled.
+	 */
+	rc = pm8xxx_misc_masked_write(chip, REG_PM8XXX_PON_CTRL_1,
+		PON_CTRL_1_PULL_UP_MASK | PON_CTRL_1_USB_PWR_EN
+		| PON_CTRL_1_WD_EN_MASK,
+		PON_CTRL_1_PULL_UP_MASK | PON_CTRL_1_USB_PWR_EN
+		| (reset ? PON_CTRL_1_WD_EN_RESET : PON_CTRL_1_WD_EN_PWR_OFF));
+	if (rc) {
+		pr_err("pm8xxx_misc_masked_write failed, rc=%d\n", rc);
+		goto read_write_err;
+	}
+
+read_write_err:
+	return rc;
+}
+
+static int __pm8901_reset_pwr_off(struct pm8xxx_misc_chip *chip, int reset)
+{
+	int rc = 0, i;
+	u8 pmr_addr[4] = {
+		REG_PM8901_REGULATOR_S2_PMR,
+		REG_PM8901_REGULATOR_S3_PMR,
+		REG_PM8901_REGULATOR_S4_PMR,
+		REG_PM8901_REGULATOR_S1_PMR,
+	};
+
+	/* Fix-up: Turn off regulators S1, S2, S3, S4 when shutting down. */
+	if (!reset) {
+		for (i = 0; i < 4; i++) {
+			rc = pm8xxx_misc_masked_write(chip, pmr_addr[i],
+				PM8901_REGULATOR_PMR_STATE_MASK,
+				PM8901_REGULATOR_PMR_STATE_OFF);
+			if (rc) {
+				pr_err("pm8xxx_misc_masked_write failed, "
+					"rc=%d\n", rc);
+				goto read_write_err;
+			}
+			mdelay(PM8901_DELAY_AFTER_REG_DISABLE_MS);
+		}
+	}
+
+read_write_err:
+	mdelay(PM8901_DELAY_BEFORE_SHUTDOWN_MS);
+	return rc;
+}
+
+static int __pm8921_reset_pwr_off(struct pm8xxx_misc_chip *chip, int reset)
+{
+	int rc;
+
+	/* Enable SMPL if resetting is desired. */
+	rc = pm8xxx_misc_masked_write(chip, REG_PM8921_SLEEP_CTRL,
+	       SLEEP_CTRL_SMPL_EN_MASK,
+	       (reset ? SLEEP_CTRL_SMPL_EN_RESET : SLEEP_CTRL_SMPL_EN_PWR_OFF));
+	if (rc) {
+		pr_err("pm8xxx_misc_masked_write failed, rc=%d\n", rc);
+		goto read_write_err;
+	}
+
+	/*
+	 * Select action to perform (reset or shutdown) when PS_HOLD goes low.
+	 * Also ensure that KPD, CBL0, and CBL1 pull ups are enabled and that
+	 * USB charging is enabled.
+	 */
+	rc = pm8xxx_misc_masked_write(chip, REG_PM8XXX_PON_CTRL_1,
+		PON_CTRL_1_PULL_UP_MASK | PON_CTRL_1_USB_PWR_EN
+		| PON_CTRL_1_WD_EN_MASK,
+		PON_CTRL_1_PULL_UP_MASK | PON_CTRL_1_USB_PWR_EN
+		| (reset ? PON_CTRL_1_WD_EN_RESET : PON_CTRL_1_WD_EN_PWR_OFF));
+	if (rc) {
+		pr_err("pm8xxx_misc_masked_write failed, rc=%d\n", rc);
+		goto read_write_err;
+	}
+
+read_write_err:
+	return rc;
+}
+
+/**
+ * pm8xxx_reset_pwr_off - switch all PM8XXX PMIC chips attached to the system to
+ *			  either reset or shutdown when they are turned off
+ * @reset: 0 = shudown the PMICs, 1 = shutdown and then restart the PMICs
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_reset_pwr_off(int reset)
+{
+	struct pm8xxx_misc_chip *chip;
+	unsigned long flags;
+	int rc = 0;
+
+	spin_lock_irqsave(&pm8xxx_misc_chips_lock, flags);
+
+	/* Loop over all attached PMICs and call specific functions for them. */
+	list_for_each_entry(chip, &pm8xxx_misc_chips, link) {
+		switch (chip->version) {
+		case PM8XXX_VERSION_8018:
+			rc = __pm8018_reset_pwr_off(chip, reset);
+			break;
+		case PM8XXX_VERSION_8058:
+			rc = __pm8058_reset_pwr_off(chip, reset);
+			break;
+		case PM8XXX_VERSION_8901:
+			rc = __pm8901_reset_pwr_off(chip, reset);
+			break;
+		case PM8XXX_VERSION_8038:
+		case PM8XXX_VERSION_8917:
+		case PM8XXX_VERSION_8921:
+			rc = __pm8921_reset_pwr_off(chip, reset);
+			break;
+		default:
+			/* PMIC doesn't have reset_pwr_off; do nothing. */
+			break;
+		}
+		if (rc) {
+			pr_err("reset_pwr_off failed, rc=%d\n", rc);
+			break;
+		}
+	}
+
+	spin_unlock_irqrestore(&pm8xxx_misc_chips_lock, flags);
+
+	return rc;
+}
+EXPORT_SYMBOL_GPL(pm8xxx_reset_pwr_off);
+
+/**
+ * pm8xxx_smpl_control - enables/disables SMPL detection
+ * @enable: 0 = shutdown PMIC on power loss, 1 = reset PMIC on power loss
+ *
+ * This function enables or disables the Sudden Momentary Power Loss detection
+ * module.  If SMPL detection is enabled, then when a sufficiently long power
+ * loss event occurs, the PMIC will automatically reset itself.  If SMPL
+ * detection is disabled, then the PMIC will shutdown when power loss occurs.
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_smpl_control(int enable)
+{
+	struct pm8xxx_misc_chip *chip;
+	unsigned long flags;
+	int rc = 0;
+
+	spin_lock_irqsave(&pm8xxx_misc_chips_lock, flags);
+
+	/* Loop over all attached PMICs and call specific functions for them. */
+	list_for_each_entry(chip, &pm8xxx_misc_chips, link) {
+		switch (chip->version) {
+		case PM8XXX_VERSION_8018:
+			rc = pm8xxx_misc_masked_write(chip,
+				REG_PM8018_SLEEP_CTRL, SLEEP_CTRL_SMPL_EN_MASK,
+				(enable ? SLEEP_CTRL_SMPL_EN_RESET
+					   : SLEEP_CTRL_SMPL_EN_PWR_OFF));
+			break;
+		case PM8XXX_VERSION_8058:
+			rc = pm8xxx_misc_masked_write(chip,
+				REG_PM8058_SLEEP_CTRL, SLEEP_CTRL_SMPL_EN_MASK,
+				(enable ? SLEEP_CTRL_SMPL_EN_RESET
+					   : SLEEP_CTRL_SMPL_EN_PWR_OFF));
+			break;
+		case PM8XXX_VERSION_8921:
+			rc = pm8xxx_misc_masked_write(chip,
+				REG_PM8921_SLEEP_CTRL, SLEEP_CTRL_SMPL_EN_MASK,
+				(enable ? SLEEP_CTRL_SMPL_EN_RESET
+					   : SLEEP_CTRL_SMPL_EN_PWR_OFF));
+			break;
+		default:
+			/* PMIC doesn't have reset_pwr_off; do nothing. */
+			break;
+		}
+		if (rc) {
+			pr_err("setting smpl control failed, rc=%d\n", rc);
+			break;
+		}
+	}
+
+	spin_unlock_irqrestore(&pm8xxx_misc_chips_lock, flags);
+
+	return rc;
+}
+EXPORT_SYMBOL(pm8xxx_smpl_control);
+
+
+/**
+ * pm8xxx_smpl_set_delay - sets the SMPL detection time delay
+ * @delay: enum value corresponding to delay time
+ *
+ * This function sets the time delay of the SMPL detection module.  If power
+ * is reapplied within this interval, then the PMIC reset automatically.  The
+ * SMPL detection module must be enabled for this delay time to take effect.
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_smpl_set_delay(enum pm8xxx_smpl_delay delay)
+{
+	struct pm8xxx_misc_chip *chip;
+	unsigned long flags;
+	int rc = 0;
+
+	if (delay < SLEEP_CTRL_SMPL_SEL_MIN
+	    || delay > SLEEP_CTRL_SMPL_SEL_MAX) {
+		pr_err("%s: invalid delay specified: %d\n", __func__, delay);
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&pm8xxx_misc_chips_lock, flags);
+
+	/* Loop over all attached PMICs and call specific functions for them. */
+	list_for_each_entry(chip, &pm8xxx_misc_chips, link) {
+		switch (chip->version) {
+		case PM8XXX_VERSION_8018:
+			rc = pm8xxx_misc_masked_write(chip,
+				REG_PM8018_SLEEP_CTRL, SLEEP_CTRL_SMPL_SEL_MASK,
+				delay);
+			break;
+		case PM8XXX_VERSION_8058:
+			rc = pm8xxx_misc_masked_write(chip,
+				REG_PM8058_SLEEP_CTRL, SLEEP_CTRL_SMPL_SEL_MASK,
+				delay);
+			break;
+		case PM8XXX_VERSION_8921:
+			rc = pm8xxx_misc_masked_write(chip,
+				REG_PM8921_SLEEP_CTRL, SLEEP_CTRL_SMPL_SEL_MASK,
+				delay);
+			break;
+		default:
+			/* PMIC doesn't have reset_pwr_off; do nothing. */
+			break;
+		}
+		if (rc) {
+			pr_err("setting smpl delay failed, rc=%d\n", rc);
+			break;
+		}
+	}
+
+	spin_unlock_irqrestore(&pm8xxx_misc_chips_lock, flags);
+
+	return rc;
+}
+EXPORT_SYMBOL(pm8xxx_smpl_set_delay);
+
+/**
+ * pm8xxx_coincell_chg_config - Disables or enables the coincell charger, and
+ *				configures its voltage and resistor settings.
+ * @chg_config:			Holds both voltage and resistor values, and a
+ *				switch to change the state of charger.
+ *				If state is to disable the charger then
+ *				both voltage and resistor are disregarded.
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_coincell_chg_config(struct pm8xxx_coincell_chg *chg_config)
+{
+	struct pm8xxx_misc_chip *chip;
+	unsigned long flags;
+	u8 reg = 0, voltage, resistor;
+	int rc = 0;
+
+	if (chg_config == NULL) {
+		pr_err("chg_config is NULL\n");
+		return -EINVAL;
+	}
+
+	voltage = chg_config->voltage;
+	resistor = chg_config->resistor;
+
+	if (resistor < PM8XXX_COINCELL_RESISTOR_2100_OHMS ||
+			resistor > PM8XXX_COINCELL_RESISTOR_800_OHMS) {
+		pr_err("Invalid resistor value provided\n");
+		return -EINVAL;
+	}
+
+	if (voltage < PM8XXX_COINCELL_VOLTAGE_3p2V ||
+		(voltage > PM8XXX_COINCELL_VOLTAGE_3p0V &&
+			voltage != PM8XXX_COINCELL_VOLTAGE_2p5V)) {
+		pr_err("Invalid voltage value provided\n");
+		return -EINVAL;
+	}
+
+	if (chg_config->state == PM8XXX_COINCELL_CHG_DISABLE) {
+		reg = 0;
+	} else {
+		reg |= voltage;
+		reg |= (resistor << COINCELL_RESISTOR_SHIFT);
+	}
+
+	spin_lock_irqsave(&pm8xxx_misc_chips_lock, flags);
+
+	/* Loop over all attached PMICs and call specific functions for them. */
+	list_for_each_entry(chip, &pm8xxx_misc_chips, link) {
+		switch (chip->version) {
+		case PM8XXX_VERSION_8018:
+			rc = pm8xxx_writeb(chip->dev->parent,
+					REG_PM8018_COIN_CHG, reg);
+			break;
+		case PM8XXX_VERSION_8058:
+			rc = pm8xxx_writeb(chip->dev->parent,
+					REG_PM8058_COIN_CHG, reg);
+			break;
+		case PM8XXX_VERSION_8921:
+			rc = pm8xxx_writeb(chip->dev->parent,
+					REG_PM8921_COIN_CHG, reg);
+			break;
+		default:
+			/* PMIC doesn't have reset_pwr_off; do nothing. */
+			break;
+		}
+		if (rc) {
+			pr_err("coincell chg. config failed, rc=%d\n", rc);
+			break;
+		}
+	}
+
+	spin_unlock_irqrestore(&pm8xxx_misc_chips_lock, flags);
+
+	return rc;
+}
+EXPORT_SYMBOL(pm8xxx_coincell_chg_config);
+
+/**
+ * pm8xxx_watchdog_reset_control - enables/disables watchdog reset detection
+ * @enable: 0 = shutdown when PS_HOLD goes low, 1 = reset when PS_HOLD goes low
+ *
+ * This function enables or disables the PMIC watchdog reset detection feature.
+ * If watchdog reset detection is enabled, then the PMIC will reset itself
+ * when PS_HOLD goes low.  If it is not enabled, then the PMIC will shutdown
+ * when PS_HOLD goes low.
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_watchdog_reset_control(int enable)
+{
+	struct pm8xxx_misc_chip *chip;
+	unsigned long flags;
+	int rc = 0;
+
+	spin_lock_irqsave(&pm8xxx_misc_chips_lock, flags);
+
+	/* Loop over all attached PMICs and call specific functions for them. */
+	list_for_each_entry(chip, &pm8xxx_misc_chips, link) {
+		switch (chip->version) {
+		case PM8XXX_VERSION_8018:
+		case PM8XXX_VERSION_8058:
+		case PM8XXX_VERSION_8921:
+			rc = pm8xxx_misc_masked_write(chip,
+				REG_PM8XXX_PON_CTRL_1, PON_CTRL_1_WD_EN_MASK,
+				(enable ? PON_CTRL_1_WD_EN_RESET
+					   : PON_CTRL_1_WD_EN_PWR_OFF));
+			break;
+		default:
+			/* WD reset control not supported */
+			break;
+		}
+		if (rc) {
+			pr_err("setting WD reset control failed, rc=%d\n", rc);
+			break;
+		}
+	}
+
+	spin_unlock_irqrestore(&pm8xxx_misc_chips_lock, flags);
+
+	return rc;
+}
+EXPORT_SYMBOL(pm8xxx_watchdog_reset_control);
+
+/**
+ * pm8xxx_stay_on - enables stay_on feature
+ *
+ * PMIC stay-on feature allows PMIC to ignore MSM PS_HOLD=low
+ * signal so that some special functions like debugging could be
+ * performed.
+ *
+ * This feature should not be used in any product release.
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_stay_on(void)
+{
+	struct pm8xxx_misc_chip *chip;
+	unsigned long flags;
+	int rc = 0;
+
+	spin_lock_irqsave(&pm8xxx_misc_chips_lock, flags);
+
+	/* Loop over all attached PMICs and call specific functions for them. */
+	list_for_each_entry(chip, &pm8xxx_misc_chips, link) {
+		switch (chip->version) {
+		case PM8XXX_VERSION_8018:
+		case PM8XXX_VERSION_8058:
+		case PM8XXX_VERSION_8921:
+			rc = pm8xxx_writeb(chip->dev->parent,
+				REG_PM8XXX_GP_TEST_1, PM8XXX_STAY_ON_CFG);
+			break;
+		default:
+			/* stay on not supported */
+			break;
+		}
+		if (rc) {
+			pr_err("stay_on failed failed, rc=%d\n", rc);
+			break;
+		}
+	}
+
+	spin_unlock_irqrestore(&pm8xxx_misc_chips_lock, flags);
+
+	return rc;
+}
+EXPORT_SYMBOL(pm8xxx_stay_on);
+
+static int
+__pm8xxx_hard_reset_config(struct pm8xxx_misc_chip *chip,
+		enum pm8xxx_pon_config config, u16 pon4_addr, u16 pon5_addr)
+{
+	int rc = 0;
+
+	switch (config) {
+	case PM8XXX_DISABLE_HARD_RESET:
+		rc = pm8xxx_misc_masked_write(chip, pon5_addr,
+				PON_CTRL_5_HARD_RESET_EN_MASK,
+				PON_CTRL_5_HARD_RESET_DIS);
+		break;
+	case PM8XXX_SHUTDOWN_ON_HARD_RESET:
+		rc = pm8xxx_misc_masked_write(chip, pon5_addr,
+				PON_CTRL_5_HARD_RESET_EN_MASK,
+				PON_CTRL_5_HARD_RESET_EN);
+		if (!rc) {
+			rc = pm8xxx_misc_masked_write(chip, pon4_addr,
+					PON_CTRL_4_RESET_EN_MASK,
+					PON_CTRL_4_SHUTDOWN_ON_RESET);
+		}
+		break;
+	case PM8XXX_RESTART_ON_HARD_RESET:
+		rc = pm8xxx_misc_masked_write(chip, pon5_addr,
+				PON_CTRL_5_HARD_RESET_EN_MASK,
+				PON_CTRL_5_HARD_RESET_EN);
+		if (!rc) {
+			rc = pm8xxx_misc_masked_write(chip, pon4_addr,
+					PON_CTRL_4_RESET_EN_MASK,
+					PON_CTRL_4_RESTART_ON_RESET);
+		}
+		break;
+	default:
+		rc = -EINVAL;
+		break;
+	}
+	return rc;
+}
+
+/**
+ * pm8xxx_hard_reset_config - Allows different reset configurations
+ *
+ * config = PM8XXX_DISABLE_HARD_RESET to disable hard reset
+ *	  = PM8XXX_SHUTDOWN_ON_HARD_RESET to turn off the system on hard reset
+ *	  = PM8XXX_RESTART_ON_HARD_RESET to restart the system on hard reset
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_hard_reset_config(enum pm8xxx_pon_config config)
+{
+	struct pm8xxx_misc_chip *chip;
+	unsigned long flags;
+	int rc = 0;
+
+	spin_lock_irqsave(&pm8xxx_misc_chips_lock, flags);
+
+	/* Loop over all attached PMICs and call specific functions for them. */
+	list_for_each_entry(chip, &pm8xxx_misc_chips, link) {
+		switch (chip->version) {
+		case PM8XXX_VERSION_8018:
+			__pm8xxx_hard_reset_config(chip, config,
+				REG_PM8018_PON_CNTL_4, REG_PM8018_PON_CNTL_5);
+			break;
+		case PM8XXX_VERSION_8058:
+			__pm8xxx_hard_reset_config(chip, config,
+				REG_PM8058_PON_CNTL_4, REG_PM8058_PON_CNTL_5);
+			break;
+		case PM8XXX_VERSION_8901:
+			__pm8xxx_hard_reset_config(chip, config,
+				REG_PM8901_PON_CNTL_4, REG_PM8901_PON_CNTL_5);
+			break;
+		case PM8XXX_VERSION_8921:
+			__pm8xxx_hard_reset_config(chip, config,
+				REG_PM8921_PON_CNTL_4, REG_PM8921_PON_CNTL_5);
+			break;
+		default:
+			/* hard reset config. no supported */
+			break;
+		}
+		if (rc) {
+			pr_err("hard reset config. failed, rc=%d\n", rc);
+			break;
+		}
+	}
+
+	spin_unlock_irqrestore(&pm8xxx_misc_chips_lock, flags);
+
+	return rc;
+}
+EXPORT_SYMBOL(pm8xxx_hard_reset_config);
+
+/* Handle the OSC_HALT interrupt: 32 kHz XTAL oscillator has stopped. */
+static irqreturn_t pm8xxx_osc_halt_isr(int irq, void *data)
+{
+	struct pm8xxx_misc_chip *chip = data;
+	u64 count = 0;
+
+	if (chip) {
+		chip->osc_halt_count++;
+		count = chip->osc_halt_count;
+	}
+
+	pr_crit("%s: OSC_HALT interrupt has triggered, 32 kHz XTAL oscillator"
+				" has halted (%llu)!\n", __func__, count);
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * pm8xxx_uart_gpio_mux_ctrl - Mux configuration to select the UART
+ *
+ * @uart_path_sel: Input argument to select either UART1/2/3
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_uart_gpio_mux_ctrl(enum pm8xxx_uart_path_sel uart_path_sel)
+{
+	struct pm8xxx_misc_chip *chip;
+	unsigned long flags;
+	int rc = 0;
+
+	spin_lock_irqsave(&pm8xxx_misc_chips_lock, flags);
+
+	/* Loop over all attached PMICs and call specific functions for them. */
+	list_for_each_entry(chip, &pm8xxx_misc_chips, link) {
+		switch (chip->version) {
+		case PM8XXX_VERSION_8018:
+		case PM8XXX_VERSION_8058:
+		case PM8XXX_VERSION_8921:
+			rc = pm8xxx_misc_masked_write(chip,
+				REG_PM8XXX_GPIO_MUX_CTRL, UART_PATH_SEL_MASK,
+				uart_path_sel << UART_PATH_SEL_SHIFT);
+			break;
+		default:
+			/* Functionality not supported */
+			break;
+		}
+		if (rc) {
+			pr_err("uart_gpio_mux_ctrl failed, rc=%d\n", rc);
+			break;
+		}
+	}
+
+	spin_unlock_irqrestore(&pm8xxx_misc_chips_lock, flags);
+
+	return rc;
+}
+EXPORT_SYMBOL(pm8xxx_uart_gpio_mux_ctrl);
+
+/**
+ * pm8xxx_usb_id_pullup - Control a pullup for USB ID
+ *
+ * @enable: enable (1) or disable (0) the pullup
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_usb_id_pullup(int enable)
+{
+	struct pm8xxx_misc_chip *chip;
+	unsigned long flags;
+	int rc = -ENXIO;
+
+	spin_lock_irqsave(&pm8xxx_misc_chips_lock, flags);
+
+	/* Loop over all attached PMICs and call specific functions for them. */
+	list_for_each_entry(chip, &pm8xxx_misc_chips, link) {
+		switch (chip->version) {
+		case PM8XXX_VERSION_8921:
+		case PM8XXX_VERSION_8922:
+		case PM8XXX_VERSION_8917:
+		case PM8XXX_VERSION_8038:
+			rc = pm8xxx_misc_masked_write(chip,
+				REG_PM8XXX_GPIO_MUX_CTRL, USB_ID_PU_EN_MASK,
+				enable << USB_ID_PU_EN_SHIFT);
+
+			if (rc)
+				pr_err("Fail: reg=%x, rc=%d\n",
+				       REG_PM8XXX_GPIO_MUX_CTRL, rc);
+			break;
+		default:
+			/* Functionality not supported */
+			break;
+		}
+	}
+
+	spin_unlock_irqrestore(&pm8xxx_misc_chips_lock, flags);
+
+	return rc;
+}
+EXPORT_SYMBOL(pm8xxx_usb_id_pullup);
+
+static int __pm8901_preload_dVdd(struct pm8xxx_misc_chip *chip)
+{
+	int rc;
+
+	/* dVdd preloading is not needed for PMIC PM8901 rev 2.3 and beyond. */
+	if (pm8xxx_get_revision(chip->dev->parent) >= PM8XXX_REVISION_8901_2p3)
+		return 0;
+
+	rc = pm8xxx_writeb(chip->dev->parent, 0x0BD, 0x0F);
+	if (rc)
+		pr_err("pm8xxx_writeb failed for 0x0BD, rc=%d\n", rc);
+
+	rc = pm8xxx_writeb(chip->dev->parent, 0x001, 0xB4);
+	if (rc)
+		pr_err("pm8xxx_writeb failed for 0x001, rc=%d\n", rc);
+
+	pr_info("dVdd preloaded\n");
+
+	return rc;
+}
+
+/**
+ * pm8xxx_preload_dVdd - preload the dVdd regulator during off state.
+ *
+ * This can help to reduce fluctuations in the dVdd voltage during startup
+ * at the cost of additional off state current draw.
+ *
+ * This API should only be called if dVdd startup issues are suspected.
+ *
+ * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
+ */
+int pm8xxx_preload_dVdd(void)
+{
+	struct pm8xxx_misc_chip *chip;
+	unsigned long flags;
+	int rc = 0;
+
+	spin_lock_irqsave(&pm8xxx_misc_chips_lock, flags);
+
+	/* Loop over all attached PMICs and call specific functions for them. */
+	list_for_each_entry(chip, &pm8xxx_misc_chips, link) {
+		switch (chip->version) {
+		case PM8XXX_VERSION_8901:
+			rc = __pm8901_preload_dVdd(chip);
+			break;
+		default:
+			/* PMIC doesn't have preload_dVdd; do nothing. */
+			break;
+		}
+		if (rc) {
+			pr_err("preload_dVdd failed, rc=%d\n", rc);
+			break;
+		}
+	}
+
+	spin_unlock_irqrestore(&pm8xxx_misc_chips_lock, flags);
+
+	return rc;
+}
+EXPORT_SYMBOL_GPL(pm8xxx_preload_dVdd);
+
+int pm8xxx_aux_clk_control(enum pm8xxx_aux_clk_id clk_id,
+				enum pm8xxx_aux_clk_div divider, bool enable)
+{
+	struct pm8xxx_misc_chip *chip;
+	unsigned long flags;
+	u8 clk_mask = 0, value = 0;
+
+	if (clk_id == CLK_MP3_1) {
+		clk_mask = MP3_1_MASK;
+		value = divider << MP3_1_SHIFT;
+	} else if (clk_id == CLK_MP3_2) {
+		clk_mask = MP3_2_MASK;
+		value = divider << MP3_2_SHIFT;
+	} else {
+		pr_err("Invalid clock id of %d\n", clk_id);
+		return -EINVAL;
+	}
+	if (!enable)
+		value = 0;
+
+	spin_lock_irqsave(&pm8xxx_misc_chips_lock, flags);
+
+	/* Loop over all attached PMICs and call specific functions for them. */
+	list_for_each_entry(chip, &pm8xxx_misc_chips, link) {
+		switch (chip->version) {
+		case PM8XXX_VERSION_8038:
+		case PM8XXX_VERSION_8921:
+			pm8xxx_misc_masked_write(chip,
+					REG_PM8XXX_XO_CNTRL_2, clk_mask, value);
+			break;
+		default:
+			/* Functionality not supported */
+			break;
+		}
+	}
+
+	spin_unlock_irqrestore(&pm8xxx_misc_chips_lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pm8xxx_aux_clk_control);
+
+int pm8xxx_hsed_bias_control(enum pm8xxx_hsed_bias bias, bool enable)
+{
+	struct pm8xxx_misc_chip *chip;
+	unsigned long flags;
+	int rc = 0;
+	u16 addr;
+
+	switch (bias) {
+	case PM8XXX_HSED_BIAS0:
+		addr = REG_HSED_BIAS0_CNTL2;
+		break;
+	case PM8XXX_HSED_BIAS1:
+		addr = REG_HSED_BIAS1_CNTL2;
+		break;
+	case PM8XXX_HSED_BIAS2:
+		addr = REG_HSED_BIAS2_CNTL2;
+		break;
+	default:
+		pr_err("Invalid BIAS line\n");
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&pm8xxx_misc_chips_lock, flags);
+
+	/* Loop over all attached PMICs and call specific functions for them. */
+	list_for_each_entry(chip, &pm8xxx_misc_chips, link) {
+		switch (chip->version) {
+		case PM8XXX_VERSION_8058:
+		case PM8XXX_VERSION_8921:
+			rc = pm8xxx_misc_masked_write(chip, addr,
+				HSED_EN_MASK, enable ? HSED_EN_MASK : 0);
+			if (rc < 0)
+				pr_err("Enable HSED BIAS failed rc=%d\n", rc);
+			break;
+		default:
+			/* Functionality not supported */
+			break;
+		}
+	}
+
+	spin_unlock_irqrestore(&pm8xxx_misc_chips_lock, flags);
+
+	return rc;
+}
+EXPORT_SYMBOL(pm8xxx_hsed_bias_control);
+
+static int __devinit pm8xxx_misc_probe(struct platform_device *pdev)
+{
+	const struct pm8xxx_misc_platform_data *pdata = pdev->dev.platform_data;
+	struct pm8xxx_misc_chip *chip;
+	struct pm8xxx_misc_chip *sibling;
+	struct list_head *prev;
+	unsigned long flags;
+	int rc = 0, irq;
+
+	if (!pdata) {
+		pr_err("missing platform data\n");
+		return -EINVAL;
+	}
+
+	chip = kzalloc(sizeof(struct pm8xxx_misc_chip), GFP_KERNEL);
+	if (!chip) {
+		pr_err("Cannot allocate %d bytes\n",
+			sizeof(struct pm8xxx_misc_chip));
+		return -ENOMEM;
+	}
+
+	chip->dev = &pdev->dev;
+	chip->version = pm8xxx_get_version(chip->dev->parent);
+	memcpy(&(chip->pdata), pdata, sizeof(struct pm8xxx_misc_platform_data));
+
+	irq = platform_get_irq_byname(pdev, "pm8xxx_osc_halt_irq");
+	if (irq > 0) {
+		rc = request_any_context_irq(irq, pm8xxx_osc_halt_isr,
+				 IRQF_TRIGGER_RISING | IRQF_DISABLED,
+				 "pm8xxx_osc_halt_irq", chip);
+		if (rc < 0) {
+			pr_err("%s: request_any_context_irq(%d) FAIL: %d\n",
+							 __func__, irq, rc);
+			goto fail_irq;
+		}
+	}
+
+	/* Insert PMICs in priority order (lowest value first). */
+	spin_lock_irqsave(&pm8xxx_misc_chips_lock, flags);
+	prev = &pm8xxx_misc_chips;
+	list_for_each_entry(sibling, &pm8xxx_misc_chips, link) {
+		if (chip->pdata.priority < sibling->pdata.priority)
+			break;
+		else
+			prev = &sibling->link;
+	}
+	list_add(&chip->link, prev);
+	spin_unlock_irqrestore(&pm8xxx_misc_chips_lock, flags);
+
+	platform_set_drvdata(pdev, chip);
+
+	return rc;
+
+fail_irq:
+	platform_set_drvdata(pdev, NULL);
+	kfree(chip);
+	return rc;
+}
+
+static int __devexit pm8xxx_misc_remove(struct platform_device *pdev)
+{
+	struct pm8xxx_misc_chip *chip = platform_get_drvdata(pdev);
+	unsigned long flags;
+	int irq = platform_get_irq_byname(pdev, "pm8xxx_osc_halt_irq");
+	if (irq > 0)
+		free_irq(irq, chip);
+
+	spin_lock_irqsave(&pm8xxx_misc_chips_lock, flags);
+	list_del(&chip->link);
+	spin_unlock_irqrestore(&pm8xxx_misc_chips_lock, flags);
+
+	platform_set_drvdata(pdev, NULL);
+	kfree(chip);
+
+	return 0;
+}
+
+static struct platform_driver pm8xxx_misc_driver = {
+	.probe	= pm8xxx_misc_probe,
+	.remove	= __devexit_p(pm8xxx_misc_remove),
+	.driver	= {
+		.name	= PM8XXX_MISC_DEV_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init pm8xxx_misc_init(void)
+{
+	return platform_driver_register(&pm8xxx_misc_driver);
+}
+postcore_initcall(pm8xxx_misc_init);
+
+static void __exit pm8xxx_misc_exit(void)
+{
+	platform_driver_unregister(&pm8xxx_misc_driver);
+}
+module_exit(pm8xxx_misc_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC 8XXX misc driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:" PM8XXX_MISC_DEV_NAME);
diff --git a/drivers/mfd/pm8xxx-pwm.c b/drivers/mfd/pm8xxx-pwm.c
new file mode 100644
index 0000000..022cfb6
--- /dev/null
+++ b/drivers/mfd/pm8xxx-pwm.c
@@ -0,0 +1,1471 @@
+/* Copyright (c) 2011-2012, 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.
+ */
+/*
+ * Qualcomm PM8XXX Pulse Width Modulation (PWM) driver
+ *
+ * The HW module is also called LPG (Light Pulse Generator).
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/debugfs.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/mfd/pm8xxx/pwm.h>
+
+#define PM8XXX_PWM_CHANNELS		3
+
+/*
+ * For the lack of better term to distinguish functional
+ * differences, hereby, LPG version 0 (V0, v0) denotes
+ * PM8058/8921, and version 1 (V1, v1) denotes
+ * PM8922/8038.
+ */
+#define PM8XXX_LPG_V0_PWM_CHANNELS	8
+#define PM8XXX_LPG_V1_PWM_CHANNELS	6
+#define PM8XXX_LPG_CTL_REGS		7
+
+/* PM8XXX PWM */
+#define SSBI_REG_ADDR_PWM1_CTRL1	0x88
+#define SSBI_REG_ADDR_PWM1_CTRL2	0x89
+#define SSBI_REG_ADDR_PWM_CTL(id, base) (id == 0 ? base : (base + (id << 1)))
+#define SSBI_REG_ADDR_PWM_CTL1(id)	SSBI_REG_ADDR_PWM_CTL(id, \
+						SSBI_REG_ADDR_PWM1_CTRL1)
+#define SSBI_REG_ADDR_PWM_CTL2(id)	SSBI_REG_ADDR_PWM_CTL(id, \
+						SSBI_REG_ADDR_PWM1_CTRL2)
+
+#define PM8XXX_PWM_CLK_SEL_SHIFT	6
+#define PM8XXX_PWM_CLK_SEL_MASK		0xC0
+#define PM8XXX_PWM_PREDIVIDE_SHIFT	5
+#define PM8XXX_PWM_PREDIVIDE_MASK	0x20
+#define PM8XXX_PWM_M_SHIFT		2
+#define PM8XXX_PWM_M_MASK		0x1C
+#define PM8XXX_PWM_SIZE_SHIFT		1
+#define PM8XXX_PWM_SIZE_MASK		0x02
+#define PM8XXX_PWM_VALUE_BIT0		0x01
+#define PM8XXX_PWM_DISABLE		0x3F
+
+/* PM8XXX LPG PWM */
+#define SSBI_REG_ADDR_LPG_CTL_BASE	0x13C
+#define SSBI_REG_ADDR_LPG_CTL(n)	(SSBI_REG_ADDR_LPG_CTL_BASE + (n))
+#define SSBI_REG_ADDR_LPG_BANK_SEL	0x143
+#define SSBI_REG_ADDR_LPG_BANK_EN	0x144
+#define SSBI_REG_ADDR_LPG_LUT_CFG0	0x145
+#define SSBI_REG_ADDR_LPG_LUT_CFG1	0x146
+#define SSBI_REG_ADDR_LPG_TEST		0x147
+
+/* LPG Control 0 */
+#define PM8XXX_PWM_1KHZ_COUNT_MASK	0xF0
+#define PM8XXX_PWM_1KHZ_COUNT_SHIFT	4
+
+#define PM8XXX_PWM_1KHZ_COUNT_MAX	15
+
+#define PM8XXX_PWM_OUTPUT_EN		0x08
+#define PM8XXX_PWM_PWM_EN		0x04
+#define PM8XXX_PWM_RAMP_GEN_EN		0x02
+#define PM8XXX_PWM_RAMP_START		0x01
+
+#define PM8XXX_PWM_PWM_START		(PM8XXX_PWM_OUTPUT_EN \
+					| PM8XXX_PWM_PWM_EN)
+#define PM8XXX_PWM_RAMP_GEN_START	(PM8XXX_PWM_RAMP_GEN_EN \
+					| PM8XXX_PWM_RAMP_START)
+
+/* LPG Control 1 */
+#define PM8XXX_PWM_REVERSE_EN		0x80
+#define PM8XXX_PWM_BYPASS_LUT		0x40
+#define PM8XXX_PWM_HIGH_INDEX_MASK	0x3F
+
+/* LPG Control 2 */
+#define PM8XXX_PWM_LOOP_EN		0x80
+#define PM8XXX_PWM_RAMP_UP		0x40
+#define PM8XXX_PWM_LOW_INDEX_MASK	0x3F
+
+/* LPG Control 3 */
+#define PM8XXX_PWM_VALUE_BIT7_0		0xFF
+#define PM8XXX_PWM_VALUE_BIT5_0		0x3F
+
+/* LPG Control 4 */
+#define PM8XXX_PWM_VALUE_BIT8		0x80
+
+#define PM8XXX_LPG_PWM_CLK_SEL_MASK	0x60
+#define PM8XXX_LPG_PWM_CLK_SEL_SHIFT	5
+
+#define PM8XXX_PWM_CLK_SEL_NO		0
+#define PM8XXX_PWM_CLK_SEL_1KHZ		1
+#define PM8XXX_PWM_CLK_SEL_32KHZ	2
+#define PM8XXX_PWM_CLK_SEL_19P2MHZ	3
+
+#define PM8XXX_LPG_PWM_PREDIVIDE_MASK	0x18
+#define PM8XXX_LPG_PWM_PREDIVIDE_SHIFT	3
+
+#define PM8XXX_PWM_PREDIVIDE_2		0
+#define PM8XXX_PWM_PREDIVIDE_3		1
+#define PM8XXX_PWM_PREDIVIDE_5		2
+#define PM8XXX_PWM_PREDIVIDE_6		3
+
+#define PM8XXX_LPG_PWM_M_MASK		0x07
+#define PM8XXX_PWM_M_MIN		0
+#define PM8XXX_PWM_M_MAX		7
+
+/* LPG Control 5 */
+#define PM8XXX_PWM_PAUSE_COUNT_HI_MASK		0xFC
+#define PM8XXX_PWM_PAUSE_COUNT_HI_SHIFT		2
+
+#define PM8XXX_PWM_PAUSE_ENABLE_HIGH		0x02
+#define PM8XXX_PWM_SIZE_9_BIT			0x01
+
+/* LPG Control 6 */
+#define PM8XXX_PWM_PAUSE_COUNT_LO_MASK		0xFC
+#define PM8XXX_PWM_PAUSE_COUNT_LO_SHIFT		2
+
+#define PM8XXX_PWM_PAUSE_ENABLE_LOW		0x02
+#define PM8XXX_PWM_RESERVED			0x01
+
+#define PM8XXX_PWM_PAUSE_COUNT_MAX		56 /* < 2^6 = 64 */
+
+/* LPG LUT_CFG1 */
+#define PM8XXX_PWM_LUT_READ			0x40
+
+/* TEST */
+#define PM8XXX_PWM_DTEST_MASK		0x38
+#define PM8XXX_PWM_DTEST_SHIFT		3
+#define PM8XXX_PWM_DTEST_BANK_MASK	0x07
+
+/*
+ * PWM Frequency = Clock Frequency / (N * T)
+ *	or
+ * PWM Period = Clock Period * (N * T)
+ *	where
+ * N = 2^9 or 2^6 for 9-bit or 6-bit PWM size
+ * T = Pre-divide * 2^m, where m = 0..7 (exponent)
+ *
+ * This is the formula to figure out m for the best pre-divide and clock:
+ * (PWM Period / N) = (Pre-divide * Clock Period) * 2^m
+ */
+#define NUM_CLOCKS	3
+
+#define NSEC_1024HZ	(NSEC_PER_SEC / 1024)
+#define NSEC_32768HZ	(NSEC_PER_SEC / 32768)
+#define NSEC_19P2MHZ	(NSEC_PER_SEC / 19200000)
+
+#define NUM_LPG_PRE_DIVIDE	4
+#define NUM_PWM_PRE_DIVIDE	2
+
+#define PRE_DIVIDE_1		1	/* v1 */
+#define PRE_DIVIDE_2		2
+#define PRE_DIVIDE_3		3
+#define PRE_DIVIDE_5		5
+#define PRE_DIVIDE_6		6
+
+static unsigned int pt_t[NUM_LPG_PRE_DIVIDE][NUM_CLOCKS] = {
+	{	PRE_DIVIDE_2 * NSEC_1024HZ,
+		PRE_DIVIDE_2 * NSEC_32768HZ,
+		PRE_DIVIDE_2 * NSEC_19P2MHZ,
+	},
+	{	PRE_DIVIDE_3 * NSEC_1024HZ,
+		PRE_DIVIDE_3 * NSEC_32768HZ,
+		PRE_DIVIDE_3 * NSEC_19P2MHZ,
+	},
+	{	PRE_DIVIDE_5 * NSEC_1024HZ,
+		PRE_DIVIDE_5 * NSEC_32768HZ,
+		PRE_DIVIDE_5 * NSEC_19P2MHZ,
+	},
+	{	PRE_DIVIDE_6 * NSEC_1024HZ,
+		PRE_DIVIDE_6 * NSEC_32768HZ,
+		PRE_DIVIDE_6 * NSEC_19P2MHZ,
+	},
+};
+
+/* Private data */
+struct pm8xxx_pwm_chip;
+
+struct pwm_device {
+	int			pwm_id;		/* = bank/channel id */
+	int			in_use;
+	const char		*label;
+	struct pm8xxx_pwm_period	period;
+	int			pwm_value;
+	int			pwm_period;
+	int			pwm_duty;
+	u8			pwm_lpg_ctl[PM8XXX_LPG_CTL_REGS];
+	u8			pwm_ctl1;
+	u8			pwm_ctl2;
+	int			irq;
+	struct pm8xxx_pwm_chip	*chip;
+	int			bypass_lut;
+	int			dtest_mode_supported;
+};
+
+struct pm8xxx_pwm_chip {
+	struct pwm_device		*pwm_dev;
+	u8				pwm_channels;
+	u8				pwm_total_pre_divs;
+	u8				bank_mask;
+	struct mutex			pwm_mutex;
+	struct device			*dev;
+	bool				is_lpg_supported;
+};
+
+static struct pm8xxx_pwm_chip	*pwm_chip;
+
+struct pm8xxx_pwm_lut {
+	/* LUT parameters */
+	int	lut_duty_ms;
+	int	lut_lo_index;
+	int	lut_hi_index;
+	int	lut_pause_hi;
+	int	lut_pause_lo;
+	int	flags;
+};
+
+static const u16 duty_msec[PM8XXX_PWM_1KHZ_COUNT_MAX + 1] = {
+	0, 1, 2, 3, 4, 6, 8, 16, 18, 24, 32, 36, 64, 128, 256, 512
+};
+
+static const u16 pause_count[PM8XXX_PWM_PAUSE_COUNT_MAX + 1] = {
+	1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+	23, 28, 31, 42, 47, 56, 63, 83, 94, 111, 125, 167, 188, 222, 250, 333,
+	375, 500, 667, 750, 800, 900, 1000, 1100,
+	1200, 1300, 1400, 1500, 1600, 1800, 2000, 2500,
+	3000, 3500, 4000, 4500, 5000, 5500, 6000, 6500,
+	7000
+};
+
+/* Internal functions */
+static void pm8xxx_pwm_save(u8 *u8p, u8 mask, u8 val)
+{
+	*u8p &= ~mask;
+	*u8p |= val & mask;
+}
+
+static int pm8xxx_pwm_bank_enable(struct pwm_device *pwm, int enable)
+{
+	int	rc;
+	u8	reg;
+	struct pm8xxx_pwm_chip	*chip;
+
+	chip = pwm->chip;
+
+	if (enable)
+		reg = chip->bank_mask | (1 << pwm->pwm_id);
+	else
+		reg = chip->bank_mask & ~(1 << pwm->pwm_id);
+
+	rc = pm8xxx_writeb(chip->dev->parent, SSBI_REG_ADDR_LPG_BANK_EN, reg);
+	if (rc) {
+		pr_err("pm8xxx_writeb(): rc=%d (Enable LPG Bank)\n", rc);
+		return rc;
+	}
+	chip->bank_mask = reg;
+
+	return 0;
+}
+
+static int pm8xxx_pwm_bank_sel(struct pwm_device *pwm)
+{
+	int	rc;
+
+	rc = pm8xxx_writeb(pwm->chip->dev->parent, SSBI_REG_ADDR_LPG_BANK_SEL,
+			   pwm->pwm_id);
+	if (rc)
+		pr_err("pm8xxx_writeb(): rc=%d (Select PWM Bank)\n", rc);
+	return rc;
+}
+
+static int pm8xxx_pwm_start(struct pwm_device *pwm, int start, int ramp_start)
+{
+	int	rc;
+	u8	reg;
+
+	if (start) {
+		reg = pwm->pwm_lpg_ctl[0] | PM8XXX_PWM_PWM_START;
+		if (ramp_start)
+			reg |= PM8XXX_PWM_RAMP_GEN_START;
+		else
+			reg &= ~PM8XXX_PWM_RAMP_GEN_START;
+	} else {
+		reg = pwm->pwm_lpg_ctl[0] & ~PM8XXX_PWM_PWM_START;
+		reg &= ~PM8XXX_PWM_RAMP_GEN_START;
+	}
+
+	rc = pm8xxx_writeb(pwm->chip->dev->parent, SSBI_REG_ADDR_LPG_CTL(0),
+			   reg);
+	if (rc)
+		pr_err("pm8xxx_writeb(): rc=%d (Enable PWM Ctl 0)\n", rc);
+	else
+		pwm->pwm_lpg_ctl[0] = reg;
+	return rc;
+}
+
+static int pm8xxx_pwm_disable(struct pwm_device *pwm)
+{
+	int	rc;
+	u8	reg;
+
+	reg = pwm->pwm_ctl1 & PM8XXX_PWM_DISABLE;
+
+	rc = pm8xxx_writeb(pwm->chip->dev->parent,
+			SSBI_REG_ADDR_PWM_CTL1(pwm->pwm_id), reg);
+
+	if (rc)
+		pr_err("pm8xxx_writeb(): rc=%d (Disable PWM Ctl %d)\n", rc,
+								pwm->pwm_id);
+	return rc;
+}
+
+static int pm8xxx_pwm_enable(struct pwm_device *pwm)
+{
+	/**
+	 * A kind of best Effort: Just write the clock information that
+	 * we have in the register.
+	 */
+	int	rc;
+
+	rc = pm8xxx_writeb(pwm->chip->dev->parent,
+			SSBI_REG_ADDR_PWM_CTL1(pwm->pwm_id), pwm->pwm_ctl1);
+
+	if (rc)
+		pr_err("pm8xxx_writeb(): rc=%d (Enable PWM Ctl %d)\n", rc,
+								pwm->pwm_id);
+	return rc;
+}
+
+static void pm8xxx_pwm_calc_period(unsigned int period_us,
+				   struct pm8xxx_pwm_period *period)
+{
+	int	n, m, clk, div;
+	int	best_m, best_div, best_clk;
+	unsigned int	last_err, cur_err, min_err;
+	unsigned int	tmp_p, period_n;
+
+	/* PWM Period / N */
+	if (period_us < ((unsigned)(-1) / NSEC_PER_USEC)) {
+		period_n = (period_us * NSEC_PER_USEC) >> 6;
+		n = 6;
+	} else {
+		period_n = (period_us >> 9) * NSEC_PER_USEC;
+		n = 9;
+	}
+
+	min_err = last_err = (unsigned)(-1);
+	best_m = 0;
+	best_clk = 0;
+	best_div = 0;
+	for (clk = 0; clk < NUM_CLOCKS; clk++) {
+		for (div = 0; div < pwm_chip->pwm_total_pre_divs; div++) {
+			/* period_n = (PWM Period / N) */
+			/* tmp_p = (Pre-divide * Clock Period) * 2^m */
+			tmp_p = pt_t[div][clk];
+			for (m = 0; m <= PM8XXX_PWM_M_MAX; m++) {
+				if (period_n > tmp_p)
+					cur_err = period_n - tmp_p;
+				else
+					cur_err = tmp_p - period_n;
+
+				if (cur_err < min_err) {
+					min_err = cur_err;
+					best_m = m;
+					best_clk = clk;
+					best_div = div;
+				}
+
+				if (m && cur_err > last_err)
+					/* Break for bigger cur_err */
+					break;
+
+				last_err = cur_err;
+				tmp_p <<= 1;
+			}
+		}
+	}
+
+	/* Use higher resolution */
+	if (best_m >= 3 && n == 6) {
+		n += 3;
+		best_m -= 3;
+	}
+
+	period->pwm_size = n;
+	period->clk = best_clk;
+	period->pre_div = best_div;
+	period->pre_div_exp = best_m;
+}
+
+static void pm8xxx_pwm_calc_pwm_value(struct pwm_device *pwm,
+				      unsigned int period_us,
+				      unsigned int duty_us)
+{
+	unsigned int max_pwm_value, tmp;
+
+	/* Figure out pwm_value with overflow handling */
+	tmp = 1 << (sizeof(tmp) * 8 - pwm->period.pwm_size);
+	if (duty_us < tmp) {
+		tmp = duty_us << pwm->period.pwm_size;
+		pwm->pwm_value = tmp / period_us;
+	} else {
+		tmp = period_us >> pwm->period.pwm_size;
+		pwm->pwm_value = duty_us / tmp;
+	}
+	max_pwm_value = (1 << pwm->period.pwm_size) - 1;
+	if (pwm->pwm_value > max_pwm_value)
+		pwm->pwm_value = max_pwm_value;
+}
+
+static int pm8xxx_pwm_change_table(struct pwm_device *pwm, int duty_pct[],
+				   int start_idx, int len, int raw_value)
+{
+	unsigned int pwm_value, max_pwm_value;
+	u8	cfg0, cfg1;
+	int	i, pwm_size;
+	int	rc = 0;
+
+	pwm_size = (pwm->pwm_lpg_ctl[5] & PM8XXX_PWM_SIZE_9_BIT) ? 9 : 6;
+	max_pwm_value = (1 << pwm_size) - 1;
+	for (i = 0; i < len; i++) {
+		if (raw_value)
+			pwm_value = duty_pct[i];
+		else
+			pwm_value = (duty_pct[i] << pwm_size) / 100;
+
+		if (pwm_value > max_pwm_value)
+			pwm_value = max_pwm_value;
+		cfg0 = pwm_value;
+		cfg1 = (pwm_value >> 1) & 0x80;
+		cfg1 |= start_idx + i;
+
+		rc = pm8xxx_writeb(pwm->chip->dev->parent,
+				   SSBI_REG_ADDR_LPG_LUT_CFG0, cfg0);
+		if (rc)
+			break;
+
+		rc = pm8xxx_writeb(pwm->chip->dev->parent,
+				   SSBI_REG_ADDR_LPG_LUT_CFG1, cfg1);
+		if (rc)
+			break;
+	}
+	return rc;
+}
+
+static void pm8xxx_pwm_save_index(struct pwm_device *pwm,
+				   int low_idx, int high_idx, int flags)
+{
+	pwm->pwm_lpg_ctl[1] = high_idx & PM8XXX_PWM_HIGH_INDEX_MASK;
+	pwm->pwm_lpg_ctl[2] = low_idx & PM8XXX_PWM_LOW_INDEX_MASK;
+
+	if (flags & PM_PWM_LUT_REVERSE)
+		pwm->pwm_lpg_ctl[1] |= PM8XXX_PWM_REVERSE_EN;
+	if (flags & PM_PWM_LUT_RAMP_UP)
+		pwm->pwm_lpg_ctl[2] |= PM8XXX_PWM_RAMP_UP;
+	if (flags & PM_PWM_LUT_LOOP)
+		pwm->pwm_lpg_ctl[2] |= PM8XXX_PWM_LOOP_EN;
+}
+
+static void pm8xxx_pwm_save_period(struct pwm_device *pwm)
+{
+	u8	mask, val;
+
+	if (pwm_chip->is_lpg_supported) {
+		val = ((pwm->period.clk + 1) << PM8XXX_LPG_PWM_CLK_SEL_SHIFT)
+			& PM8XXX_LPG_PWM_CLK_SEL_MASK;
+		val |= (pwm->period.pre_div << PM8XXX_LPG_PWM_PREDIVIDE_SHIFT)
+			& PM8XXX_LPG_PWM_PREDIVIDE_MASK;
+		val |= pwm->period.pre_div_exp & PM8XXX_LPG_PWM_M_MASK;
+		mask = PM8XXX_LPG_PWM_CLK_SEL_MASK |
+			PM8XXX_LPG_PWM_PREDIVIDE_MASK | PM8XXX_LPG_PWM_M_MASK;
+		pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[4], mask, val);
+
+		val = (pwm->period.pwm_size > 6) ? PM8XXX_PWM_SIZE_9_BIT : 0;
+		mask = PM8XXX_PWM_SIZE_9_BIT;
+		pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[5], mask, val);
+	} else {
+		val = ((pwm->period.clk + 1) << PM8XXX_PWM_CLK_SEL_SHIFT)
+			& PM8XXX_PWM_CLK_SEL_MASK;
+		val |= (pwm->period.pre_div << PM8XXX_PWM_PREDIVIDE_SHIFT)
+			& PM8XXX_PWM_PREDIVIDE_MASK;
+		val |= (pwm->period.pre_div_exp << PM8XXX_PWM_M_SHIFT)
+				& PM8XXX_PWM_M_MASK;
+		val |= (((pwm->period.pwm_size > 6) ? PM8XXX_PWM_SIZE_9_BIT : 0)
+			<< PM8XXX_PWM_SIZE_SHIFT) & PM8XXX_PWM_SIZE_MASK;
+
+		mask = PM8XXX_PWM_CLK_SEL_MASK | PM8XXX_PWM_PREDIVIDE_MASK |
+			PM8XXX_PWM_M_MASK | PM8XXX_PWM_SIZE_MASK;
+		pm8xxx_pwm_save(&pwm->pwm_ctl1, mask, val);
+	}
+}
+
+static void pm8xxx_pwm_save_pwm_value(struct pwm_device *pwm)
+{
+	u8	mask, val;
+
+	if (pwm_chip->is_lpg_supported) {
+		val = (pwm->period.pwm_size > 6) ? (pwm->pwm_value >> 1) : 0;
+		pwm->pwm_lpg_ctl[3] = pwm->pwm_value;
+		mask = PM8XXX_PWM_VALUE_BIT8;
+		pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[4], mask, val);
+	} else {
+		val = (pwm->period.pwm_size > 6) ? (pwm->pwm_value >> 8) : 0;
+		pwm->pwm_ctl2 = pwm->pwm_value;
+		mask = PM8XXX_PWM_VALUE_BIT0;
+		pm8xxx_pwm_save(&pwm->pwm_ctl1, mask, val);
+	}
+}
+
+static void pm8xxx_pwm_save_duty_time(struct pwm_device *pwm,
+				      struct pm8xxx_pwm_lut *lut)
+{
+	int	i;
+	u8	mask, val;
+
+	/* Linear search for duty time */
+	for (i = 0; i < PM8XXX_PWM_1KHZ_COUNT_MAX; i++) {
+		if (duty_msec[i] >= lut->lut_duty_ms)
+			break;
+	}
+	val = i << PM8XXX_PWM_1KHZ_COUNT_SHIFT;
+
+	mask = PM8XXX_PWM_1KHZ_COUNT_MASK;
+	pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[0], mask, val);
+}
+
+static void pm8xxx_pwm_save_pause(struct pwm_device *pwm,
+				  struct pm8xxx_pwm_lut *lut)
+{
+	int	i, pause_cnt, time_cnt;
+	u8	mask, val;
+
+	time_cnt = (pwm->pwm_lpg_ctl[0] & PM8XXX_PWM_1KHZ_COUNT_MASK)
+				>> PM8XXX_PWM_1KHZ_COUNT_SHIFT;
+	if (lut->flags & PM_PWM_LUT_PAUSE_HI_EN) {
+		pause_cnt = (lut->lut_pause_hi + duty_msec[time_cnt] / 2)
+				/ duty_msec[time_cnt];
+		/* Linear search for pause time */
+		for (i = 0; i < PM8XXX_PWM_PAUSE_COUNT_MAX; i++) {
+			if (pause_count[i] >= pause_cnt)
+				break;
+		}
+		val = (i << PM8XXX_PWM_PAUSE_COUNT_HI_SHIFT) &
+			PM8XXX_PWM_PAUSE_COUNT_HI_MASK;
+		val |= PM8XXX_PWM_PAUSE_ENABLE_HIGH;
+	} else {
+		val = 0;
+	}
+
+	mask = PM8XXX_PWM_PAUSE_COUNT_HI_MASK | PM8XXX_PWM_PAUSE_ENABLE_HIGH;
+	pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[5], mask, val);
+
+	if (lut->flags & PM_PWM_LUT_PAUSE_LO_EN) {
+		/* Linear search for pause time */
+		pause_cnt = (lut->lut_pause_lo + duty_msec[time_cnt] / 2)
+				/ duty_msec[time_cnt];
+		for (i = 0; i < PM8XXX_PWM_PAUSE_COUNT_MAX; i++) {
+			if (pause_count[i] >= pause_cnt)
+				break;
+		}
+		val = (i << PM8XXX_PWM_PAUSE_COUNT_LO_SHIFT) &
+			PM8XXX_PWM_PAUSE_COUNT_LO_MASK;
+		val |= PM8XXX_PWM_PAUSE_ENABLE_LOW;
+	} else {
+		val = 0;
+	}
+
+	mask = PM8XXX_PWM_PAUSE_COUNT_LO_MASK | PM8XXX_PWM_PAUSE_ENABLE_LOW;
+	pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[6], mask, val);
+}
+
+static int pm8xxx_pwm_write(struct pwm_device *pwm)
+{
+	int rc = 0;
+
+	rc = pm8xxx_writeb(pwm->chip->dev->parent,
+			   SSBI_REG_ADDR_PWM_CTL1(pwm->pwm_id),
+			   pwm->pwm_ctl1);
+	if (rc) {
+		pr_err("pm8xxx_writeb() failed: rc=%d (PWM Ctl1[%d])\n",
+							rc, pwm->pwm_id);
+		return rc;
+	}
+
+	rc = pm8xxx_writeb(pwm->chip->dev->parent,
+			   SSBI_REG_ADDR_PWM_CTL2(pwm->pwm_id),
+			   pwm->pwm_ctl2);
+	if (rc) {
+		pr_err("pm8xxx_writeb() failed: rc=%d (PWM Ctl2[%d])\n",
+							rc, pwm->pwm_id);
+		return rc;
+	}
+
+	return rc;
+}
+
+static int pm8xxx_lpg_pwm_write(struct pwm_device *pwm, int start, int end)
+{
+	int	i, rc;
+
+	/* Write in reverse way so 0 would be the last */
+	for (i = end - 1; i >= start; i--) {
+		rc = pm8xxx_writeb(pwm->chip->dev->parent,
+				   SSBI_REG_ADDR_LPG_CTL(i),
+				   pwm->pwm_lpg_ctl[i]);
+		if (rc) {
+			pr_err("pm8xxx_writeb(): rc=%d (PWM Ctl[%d])\n", rc, i);
+			return rc;
+		}
+	}
+
+	return 0;
+}
+
+static int pm8xxx_pwm_change_lut(struct pwm_device *pwm,
+				 struct pm8xxx_pwm_lut *lut)
+{
+	int	rc;
+
+	pm8xxx_pwm_save_index(pwm, lut->lut_lo_index,
+			     lut->lut_hi_index, lut->flags);
+	pm8xxx_pwm_save_duty_time(pwm, lut);
+	pm8xxx_pwm_save_pause(pwm, lut);
+	pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[1], PM8XXX_PWM_BYPASS_LUT, 0);
+
+	pm8xxx_pwm_bank_sel(pwm);
+	rc = pm8xxx_lpg_pwm_write(pwm, 0, 7);
+
+	return rc;
+}
+
+static int pm8xxx_pwm_set_dtest(struct pwm_device *pwm, int enable)
+{
+	int	rc;
+	u8	reg;
+
+	reg = pwm->pwm_id & PM8XXX_PWM_DTEST_BANK_MASK;
+
+	if (enable) {
+		/* Observe LPG_OUT on DTEST1*/
+		reg |= (1 << PM8XXX_PWM_DTEST_SHIFT) &
+				PM8XXX_PWM_DTEST_MASK;
+	}
+
+	rc = pm8xxx_writeb(pwm->chip->dev->parent,
+			SSBI_REG_ADDR_LPG_TEST, reg);
+	if (rc)
+		pr_err("pm8xxx_write(DTEST=0x%x) failed: rc=%d\n",
+							reg, rc);
+
+	return rc;
+}
+
+/* APIs */
+/**
+ * pwm_request - request a PWM device
+ * @pwm_id: PWM id or channel
+ * @label: the label to identify the user
+ */
+struct pwm_device *pwm_request(int pwm_id, const char *label)
+{
+	struct pwm_device	*pwm;
+
+	if (pwm_chip == NULL) {
+		pr_err("No pwm_chip\n");
+		return ERR_PTR(-ENODEV);
+	}
+
+	if (pwm_id >= pwm_chip->pwm_channels || pwm_id < 0) {
+		pr_err("Invalid pwm_id: %d with %s\n",
+		       pwm_id, label ? label : ".");
+		return ERR_PTR(-EINVAL);
+	}
+
+	mutex_lock(&pwm_chip->pwm_mutex);
+	pwm = &pwm_chip->pwm_dev[pwm_id];
+	if (!pwm->in_use) {
+		pwm->in_use = 1;
+		pwm->label = label;
+	} else {
+		pwm = ERR_PTR(-EBUSY);
+	}
+	mutex_unlock(&pwm_chip->pwm_mutex);
+
+	return pwm;
+}
+EXPORT_SYMBOL_GPL(pwm_request);
+
+/**
+ * pwm_free - free a PWM device
+ * @pwm: the PWM device
+ */
+void pwm_free(struct pwm_device *pwm)
+{
+	if (pwm == NULL || IS_ERR(pwm) || pwm->chip == NULL) {
+		pr_err("Invalid pwm handle\n");
+		return;
+	}
+
+	mutex_lock(&pwm->chip->pwm_mutex);
+	if (pwm->in_use) {
+		if (pwm_chip->is_lpg_supported) {
+			pm8xxx_pwm_bank_sel(pwm);
+			pm8xxx_pwm_start(pwm, 0, 0);
+		} else {
+			pm8xxx_pwm_disable(pwm);
+		}
+		pwm->in_use = 0;
+		pwm->label = NULL;
+	}
+	if (pwm_chip->is_lpg_supported)
+		pm8xxx_pwm_bank_enable(pwm, 0);
+	mutex_unlock(&pwm->chip->pwm_mutex);
+}
+EXPORT_SYMBOL_GPL(pwm_free);
+
+/**
+ * pwm_config - change a PWM device configuration
+ * @pwm: the PWM device
+ * @period_us: period in microseconds
+ * @duty_us: duty cycle in microseconds
+ */
+int pwm_config(struct pwm_device *pwm, int duty_us, int period_us)
+{
+	struct pm8xxx_pwm_period *period;
+	int	rc = 0;
+
+	if (pwm == NULL || IS_ERR(pwm) ||
+		duty_us > period_us ||
+		(unsigned)period_us > PM8XXX_PWM_PERIOD_MAX ||
+		(unsigned)period_us < PM8XXX_PWM_PERIOD_MIN) {
+		pr_err("Invalid pwm handle or parameters\n");
+		return -EINVAL;
+	}
+	if (pwm->chip == NULL) {
+		pr_err("No pwm_chip\n");
+		return -ENODEV;
+	}
+
+	period = &pwm->period;
+
+	mutex_lock(&pwm->chip->pwm_mutex);
+
+	if (!pwm->in_use) {
+		rc = -EINVAL;
+		goto out_unlock;
+	}
+
+	if (pwm->pwm_period != period_us) {
+		pm8xxx_pwm_calc_period(period_us, period);
+		pm8xxx_pwm_save_period(pwm);
+		pwm->pwm_period = period_us;
+	}
+
+	pm8xxx_pwm_calc_pwm_value(pwm, period_us, duty_us);
+	pm8xxx_pwm_save_pwm_value(pwm);
+
+	if (pwm_chip->is_lpg_supported) {
+		pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[1],
+				PM8XXX_PWM_BYPASS_LUT, PM8XXX_PWM_BYPASS_LUT);
+
+		pm8xxx_pwm_bank_sel(pwm);
+		rc = pm8xxx_lpg_pwm_write(pwm, 1, 6);
+	} else {
+		rc = pm8xxx_pwm_write(pwm);
+	}
+
+	pr_debug("duty/period=%u/%u usec: pwm_value=%d (of %d)\n",
+		 (unsigned)duty_us, (unsigned)period_us,
+		 pwm->pwm_value, 1 << period->pwm_size);
+
+out_unlock:
+	mutex_unlock(&pwm->chip->pwm_mutex);
+	return rc;
+}
+EXPORT_SYMBOL_GPL(pwm_config);
+
+/**
+ * pwm_enable - start a PWM output toggling
+ * @pwm: the PWM device
+ */
+int pwm_enable(struct pwm_device *pwm)
+{
+	int	rc = 0;
+
+	if (pwm == NULL || IS_ERR(pwm)) {
+		pr_err("Invalid pwm handle\n");
+		return -EINVAL;
+	}
+	if (pwm->chip == NULL) {
+		pr_err("No pwm_chip\n");
+		return -ENODEV;
+	}
+
+	mutex_lock(&pwm->chip->pwm_mutex);
+	if (!pwm->in_use) {
+		pr_err("pwm_id: %d: stale handle?\n", pwm->pwm_id);
+		rc = -EINVAL;
+	} else {
+		if (pwm_chip->is_lpg_supported) {
+			if (pwm->dtest_mode_supported)
+				pm8xxx_pwm_set_dtest(pwm, 1);
+			rc = pm8xxx_pwm_bank_enable(pwm, 1);
+			pm8xxx_pwm_bank_sel(pwm);
+			pm8xxx_pwm_start(pwm, 1, 0);
+		} else {
+			pm8xxx_pwm_enable(pwm);
+		}
+	}
+	mutex_unlock(&pwm->chip->pwm_mutex);
+	return rc;
+}
+EXPORT_SYMBOL_GPL(pwm_enable);
+
+/**
+ * pwm_disable - stop a PWM output toggling
+ * @pwm: the PWM device
+ */
+void pwm_disable(struct pwm_device *pwm)
+{
+	if (pwm == NULL || IS_ERR(pwm) || pwm->chip == NULL) {
+		pr_err("Invalid pwm handle or no pwm_chip\n");
+		return;
+	}
+
+	mutex_lock(&pwm->chip->pwm_mutex);
+	if (pwm->in_use) {
+		if (pwm_chip->is_lpg_supported) {
+			if (pwm->dtest_mode_supported)
+				pm8xxx_pwm_set_dtest(pwm, 0);
+			pm8xxx_pwm_bank_sel(pwm);
+			pm8xxx_pwm_start(pwm, 0, 0);
+			pm8xxx_pwm_bank_enable(pwm, 0);
+		} else {
+			pm8xxx_pwm_disable(pwm);
+		}
+	}
+	mutex_unlock(&pwm->chip->pwm_mutex);
+}
+EXPORT_SYMBOL_GPL(pwm_disable);
+
+/**
+ * pm8xxx_pwm_config_period - change PWM period
+ *
+ * @pwm: the PWM device
+ * @pwm_p: period in struct pm8xxx_pwm_period
+ */
+int pm8xxx_pwm_config_period(struct pwm_device *pwm,
+			     struct pm8xxx_pwm_period *period)
+{
+	int			rc;
+
+	if (pwm == NULL || IS_ERR(pwm) || period == NULL)
+		return -EINVAL;
+	if (pwm->chip == NULL)
+		return -ENODEV;
+
+	mutex_lock(&pwm->chip->pwm_mutex);
+
+	if (!pwm->in_use) {
+		rc = -EINVAL;
+		goto out_unlock;
+	}
+
+	pwm->period.pwm_size = period->pwm_size;
+	pwm->period.clk = period->clk;
+	pwm->period.pre_div = period->pre_div;
+	pwm->period.pre_div_exp = period->pre_div_exp;
+
+	pm8xxx_pwm_save_period(pwm);
+
+	if (pwm_chip->is_lpg_supported) {
+		pm8xxx_pwm_bank_sel(pwm);
+		rc = pm8xxx_lpg_pwm_write(pwm, 4, 6);
+	} else {
+		rc = pm8xxx_pwm_write(pwm);
+	}
+
+
+out_unlock:
+	mutex_unlock(&pwm->chip->pwm_mutex);
+	return rc;
+}
+EXPORT_SYMBOL(pm8xxx_pwm_config_period);
+
+/**
+ * pm8xxx_pwm_config_pwm_value - change a PWM device configuration
+ * @pwm: the PWM device
+ * @pwm_value: the duty cycle in raw PWM value (< 2^pwm_size)
+ */
+int pm8xxx_pwm_config_pwm_value(struct pwm_device *pwm, int pwm_value)
+{
+	int	rc = 0;
+
+	if (pwm == NULL || IS_ERR(pwm))
+		return -EINVAL;
+	if (pwm->chip == NULL)
+		return -ENODEV;
+
+	mutex_lock(&pwm->chip->pwm_mutex);
+
+	if (!pwm->in_use || !pwm->pwm_period) {
+		rc = -EINVAL;
+		goto out_unlock;
+	}
+
+	if (pwm->pwm_value == pwm_value)
+		goto out_unlock;
+
+	pwm->pwm_value = pwm_value;
+
+	pm8xxx_pwm_save_pwm_value(pwm);
+
+	if (pwm_chip->is_lpg_supported) {
+		pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[1],
+				PM8XXX_PWM_BYPASS_LUT, PM8XXX_PWM_BYPASS_LUT);
+		pm8xxx_pwm_bank_sel(pwm);
+		rc = pm8xxx_lpg_pwm_write(pwm, 1, 6);
+	} else {
+		rc = pm8xxx_pwm_write(pwm);
+	}
+
+	if (rc)
+		pr_err("[%d]: pm8xxx_pwm_write: rc=%d\n", pwm->pwm_id, rc);
+
+out_unlock:
+	mutex_unlock(&pwm->chip->pwm_mutex);
+	return rc;
+}
+EXPORT_SYMBOL_GPL(pm8xxx_pwm_config_pwm_value);
+
+/**
+ * pm8xxx_pwm_lut_config - change a PWM device configuration to use LUT
+ * @pwm: the PWM device
+ * @period_us: period in microseconds
+ * @duty_pct: arrary of duty cycles in percent, like 20, 50.
+ * @duty_time_ms: time for each duty cycle in milliseconds
+ * @start_idx: start index in lookup table from 0 to MAX-1
+ * @idx_len: number of index
+ * @pause_lo: pause time in milliseconds at low index
+ * @pause_hi: pause time in milliseconds at high index
+ * @flags: control flags
+ */
+int pm8xxx_pwm_lut_config(struct pwm_device *pwm, int period_us,
+			  int duty_pct[], int duty_time_ms, int start_idx,
+			  int idx_len, int pause_lo, int pause_hi, int flags)
+{
+	struct pm8xxx_pwm_lut	lut;
+	struct pm8xxx_pwm_period *period;
+	int	len;
+	int	rc;
+
+	if (pwm == NULL || IS_ERR(pwm) || !idx_len) {
+		pr_err("Invalid pwm handle or idx_len=0\n");
+		return -EINVAL;
+	}
+	if (duty_pct == NULL && !(flags & PM_PWM_LUT_NO_TABLE)) {
+		pr_err("Invalid duty_pct with flag\n");
+		return -EINVAL;
+	}
+	if (pwm->chip == NULL) {
+		pr_err("No pwm_chip\n");
+		return -ENODEV;
+	}
+
+	if (pwm->chip->is_lpg_supported == 0) {
+		pr_err("LPG module isn't supported\n");
+		return -EINVAL;
+	}
+
+	if (idx_len >= PM_PWM_LUT_SIZE && start_idx) {
+		pr_err("Wrong LUT size or index\n");
+		return -EINVAL;
+	}
+	if ((start_idx + idx_len) > PM_PWM_LUT_SIZE) {
+		pr_err("Exceed LUT limit\n");
+		return -EINVAL;
+	}
+	if ((unsigned)period_us > PM8XXX_PWM_PERIOD_MAX ||
+		(unsigned)period_us < PM8XXX_PWM_PERIOD_MIN) {
+		pr_err("Period out of range\n");
+		return -EINVAL;
+	}
+
+	period = &pwm->period;
+	mutex_lock(&pwm->chip->pwm_mutex);
+
+	if (!pwm->in_use) {
+		pr_err("pwm_id: %d: stale handle?\n", pwm->pwm_id);
+		rc = -EINVAL;
+		goto out_unlock;
+	}
+
+	if (pwm->pwm_period != period_us) {
+		pm8xxx_pwm_calc_period(period_us, period);
+		pm8xxx_pwm_save_period(pwm);
+		pwm->pwm_period = period_us;
+	}
+
+	len = (idx_len > PM_PWM_LUT_SIZE) ? PM_PWM_LUT_SIZE : idx_len;
+
+	if (flags & PM_PWM_LUT_NO_TABLE)
+		goto after_table_write;
+
+	rc = pm8xxx_pwm_change_table(pwm, duty_pct, start_idx, len, 0);
+	if (rc) {
+		pr_err("pm8xxx_pwm_change_table: rc=%d\n", rc);
+		goto out_unlock;
+	}
+
+after_table_write:
+	lut.lut_duty_ms = duty_time_ms;
+	lut.lut_lo_index = start_idx;
+	lut.lut_hi_index = start_idx + len - 1;
+	lut.lut_pause_lo = pause_lo;
+	lut.lut_pause_hi = pause_hi;
+	lut.flags = flags;
+	pwm->bypass_lut = 0;
+
+	rc = pm8xxx_pwm_change_lut(pwm, &lut);
+
+out_unlock:
+	mutex_unlock(&pwm->chip->pwm_mutex);
+	return rc;
+}
+EXPORT_SYMBOL_GPL(pm8xxx_pwm_lut_config);
+
+/**
+ * pm8xxx_pwm_lut_enable - control a PWM device to start/stop LUT ramp
+ * @pwm: the PWM device
+ * @start: to start (1), or stop (0)
+ */
+int pm8xxx_pwm_lut_enable(struct pwm_device *pwm, int start)
+{
+	if (pwm == NULL || IS_ERR(pwm)) {
+		pr_err("Invalid pwm handle\n");
+		return -EINVAL;
+	}
+	if (pwm->chip == NULL) {
+		pr_err("No pwm_chip\n");
+		return -ENODEV;
+	}
+	if (pwm->chip->is_lpg_supported == 0) {
+		pr_err("LPG module isn't supported\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&pwm->chip->pwm_mutex);
+	if (start) {
+		if (pwm->dtest_mode_supported)
+			pm8xxx_pwm_set_dtest(pwm, 1);
+
+		pm8xxx_pwm_bank_enable(pwm, 1);
+
+		pm8xxx_pwm_bank_sel(pwm);
+		pm8xxx_pwm_start(pwm, 1, 1);
+	} else {
+		if (pwm->dtest_mode_supported)
+			pm8xxx_pwm_set_dtest(pwm, 0);
+
+		pm8xxx_pwm_bank_sel(pwm);
+		pm8xxx_pwm_start(pwm, 0, 0);
+
+		pm8xxx_pwm_bank_enable(pwm, 0);
+	}
+	mutex_unlock(&pwm->chip->pwm_mutex);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pm8xxx_pwm_lut_enable);
+
+#if defined(CONFIG_DEBUG_FS)
+
+struct pm8xxx_pwm_dbg_device;
+
+struct pm8xxx_pwm_user {
+	int				pwm_id;
+	struct pwm_device		*pwm;
+	int				period;
+	int				duty_cycle;
+	int				enable;
+	struct pm8xxx_pwm_dbg_device	*dbgdev;
+};
+
+struct pm8xxx_pwm_dbg_device {
+	struct mutex		dbg_mutex;
+	struct device		*dev;
+	struct dentry		*dent;
+
+	struct pm8xxx_pwm_user	*user;
+};
+
+static struct pm8xxx_pwm_dbg_device *pmic_dbg_device;
+
+static int dbg_pwm_check_period(int period)
+{
+	if (period < PM8XXX_PWM_PERIOD_MIN || period > PM8XXX_PWM_PERIOD_MAX) {
+		pr_err("period is invalid: %d\n", period);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int dbg_pwm_check_duty_cycle(int duty_cycle, const char *func_name)
+{
+	if (duty_cycle <= 0 || duty_cycle > 100) {
+		pr_err("%s: duty_cycle is invalid: %d\n",
+		      func_name, duty_cycle);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static void dbg_pwm_check_handle(struct pm8xxx_pwm_user *puser)
+{
+	struct pwm_device *tmp;
+
+	if (puser->pwm == NULL) {
+		tmp = pwm_request(puser->pwm_id, "pwm-dbg");
+		if (PTR_ERR(puser->pwm)) {
+			pr_err("pwm_request: err=%ld\n", PTR_ERR(puser->pwm));
+			puser->pwm = NULL;
+		} else {
+			pr_debug("[id=%d] pwm_request ok\n", puser->pwm_id);
+			puser->pwm = tmp;
+		}
+	}
+}
+
+static int dbg_pwm_enable_set(void *data, u64 val)
+{
+	struct pm8xxx_pwm_user	  *puser = data;
+	struct pm8xxx_pwm_dbg_device    *dbgdev = puser->dbgdev;
+	int     rc;
+
+	mutex_lock(&dbgdev->dbg_mutex);
+	rc = dbg_pwm_check_duty_cycle(puser->duty_cycle, __func__);
+	if (!rc) {
+		puser->enable = val;
+		dbg_pwm_check_handle(puser);
+		if (puser->pwm) {
+			if (puser->enable)
+				pwm_enable(puser->pwm);
+			else
+				pwm_disable(puser->pwm);
+		}
+	}
+	mutex_unlock(&dbgdev->dbg_mutex);
+	return 0;
+}
+
+static int dbg_pwm_enable_get(void *data, u64 *val)
+{
+	struct pm8xxx_pwm_user	  *puser = data;
+	struct pm8xxx_pwm_dbg_device    *dbgdev = puser->dbgdev;
+
+	mutex_lock(&dbgdev->dbg_mutex);
+	*val = puser->enable;
+	mutex_unlock(&dbgdev->dbg_mutex);
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(dbg_pwm_enable_fops,
+			dbg_pwm_enable_get, dbg_pwm_enable_set,
+			"%lld\n");
+
+static int dbg_pwm_duty_cycle_set(void *data, u64 val)
+{
+	struct pm8xxx_pwm_user	  *puser = data;
+	struct pm8xxx_pwm_dbg_device    *dbgdev = puser->dbgdev;
+	int     rc;
+
+	mutex_lock(&dbgdev->dbg_mutex);
+	rc = dbg_pwm_check_duty_cycle(val, __func__);
+	if (!rc) {
+		puser->duty_cycle = val;
+		dbg_pwm_check_handle(puser);
+		if (puser->pwm) {
+			int     duty_us;
+
+			duty_us = puser->duty_cycle * puser->period / 100;
+			pwm_config(puser->pwm, duty_us, puser->period);
+		}
+	}
+	mutex_unlock(&dbgdev->dbg_mutex);
+	return 0;
+}
+
+static int dbg_pwm_duty_cycle_get(void *data, u64 *val)
+{
+	struct pm8xxx_pwm_user	  *puser = data;
+	struct pm8xxx_pwm_dbg_device    *dbgdev = puser->dbgdev;
+
+	mutex_lock(&dbgdev->dbg_mutex);
+	*val = puser->duty_cycle;
+	mutex_unlock(&dbgdev->dbg_mutex);
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(dbg_pwm_duty_cycle_fops,
+			dbg_pwm_duty_cycle_get, dbg_pwm_duty_cycle_set,
+			"%lld\n");
+
+static int dbg_pwm_period_set(void *data, u64 val)
+{
+	struct pm8xxx_pwm_user	  *puser = data;
+	struct pm8xxx_pwm_dbg_device    *dbgdev = puser->dbgdev;
+	int     rc;
+
+	mutex_lock(&dbgdev->dbg_mutex);
+	rc = dbg_pwm_check_period(val);
+	if (!rc)
+		puser->period = val;
+	mutex_unlock(&dbgdev->dbg_mutex);
+	return 0;
+}
+
+static int dbg_pwm_period_get(void *data, u64 *val)
+{
+	struct pm8xxx_pwm_user	  *puser = data;
+	struct pm8xxx_pwm_dbg_device    *dbgdev = puser->dbgdev;
+
+	mutex_lock(&dbgdev->dbg_mutex);
+	*val = puser->period;
+	mutex_unlock(&dbgdev->dbg_mutex);
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(dbg_pwm_period_fops,
+			dbg_pwm_period_get, dbg_pwm_period_set, "%lld\n");
+
+static int __devinit pm8xxx_pwm_dbg_probe(struct device *dev)
+{
+	struct pm8xxx_pwm_dbg_device    *dbgdev;
+	struct dentry		   *dent;
+	struct dentry		   *temp;
+	struct pm8xxx_pwm_user	  *puser;
+	int			     i;
+	int rc = 0;
+
+	if (dev == NULL) {
+		pr_err("no parent data passed in.\n");
+		return -EINVAL;
+	}
+
+	dbgdev = kzalloc(sizeof *dbgdev, GFP_KERNEL);
+	if (dbgdev == NULL) {
+		pr_err("kzalloc() failed.\n");
+		return -ENOMEM;
+	}
+
+	dbgdev->user = kcalloc(pwm_chip->pwm_channels,
+				sizeof(struct pm8xxx_pwm_user), GFP_KERNEL);
+	if (dbgdev->user == NULL) {
+		pr_err("kcalloc() failed.\n");
+		rc = -ENOMEM;
+		goto user_error;
+	}
+
+	mutex_init(&dbgdev->dbg_mutex);
+
+	dbgdev->dev = dev;
+
+	dent = debugfs_create_dir("pm8xxx-pwm-dbg", NULL);
+	if (dent == NULL || IS_ERR(dent)) {
+		pr_err("ERR debugfs_create_dir: dent=%p\n", dent);
+		rc = -ENOMEM;
+		goto dir_error;
+	}
+
+	dbgdev->dent = dent;
+
+	for (i = 0; i < pwm_chip->pwm_channels; i++) {
+		char pwm_ch[] = "0";
+
+		pwm_ch[0] = '0' + i;
+		dent = debugfs_create_dir(pwm_ch, dbgdev->dent);
+		if (dent == NULL || IS_ERR(dent)) {
+			pr_err("ERR: pwm=%d: dir: dent=%p\n", i, dent);
+			rc = -ENOMEM;
+			goto debug_error;
+		}
+
+		puser = &dbgdev->user[i];
+		puser->dbgdev = dbgdev;
+		puser->pwm_id = i;
+		temp = debugfs_create_file("period", S_IRUGO | S_IWUSR,
+				dent, puser, &dbg_pwm_period_fops);
+		if (temp == NULL || IS_ERR(temp)) {
+			pr_err("ERR: pwm=%d: period: dent=%p\n", i, dent);
+			rc = -ENOMEM;
+			goto debug_error;
+		}
+
+		temp = debugfs_create_file("duty-cycle", S_IRUGO | S_IWUSR,
+				dent, puser, &dbg_pwm_duty_cycle_fops);
+		if (temp == NULL || IS_ERR(temp)) {
+			pr_err("ERR: pwm=%d: duty-cycle: dent=%p\n", i, dent);
+			rc = -ENOMEM;
+			goto debug_error;
+		}
+
+		temp = debugfs_create_file("enable", S_IRUGO | S_IWUSR,
+				dent, puser, &dbg_pwm_enable_fops);
+		if (temp == NULL || IS_ERR(temp)) {
+			pr_err("ERR: pwm=%d: enable: dent=%p\n", i, dent);
+			rc = -ENOMEM;
+			goto debug_error;
+		}
+	}
+
+	pmic_dbg_device = dbgdev;
+
+	return 0;
+
+debug_error:
+	debugfs_remove_recursive(dbgdev->dent);
+dir_error:
+	kfree(dbgdev->user);
+user_error:
+	kfree(dbgdev);
+	return rc;
+}
+
+static int __devexit pm8xxx_pwm_dbg_remove(void)
+{
+	if (pmic_dbg_device) {
+		kfree(pmic_dbg_device->user);
+		debugfs_remove_recursive(pmic_dbg_device->dent);
+		kfree(pmic_dbg_device);
+	}
+	return 0;
+}
+
+#else
+
+static int __devinit pm8xxx_pwm_dbg_probe(struct device *dev)
+{
+	return 0;
+}
+
+static int __devexit pm8xxx_pwm_dbg_remove(void)
+{
+	return 0;
+}
+
+#endif
+
+static int __devinit pm8xxx_pwm_probe(struct platform_device *pdev)
+{
+	const struct pm8xxx_pwm_platform_data *pdata = pdev->dev.platform_data;
+	struct pm8xxx_pwm_chip	*chip;
+	int	i, dtest_channel;
+	enum pm8xxx_version version;
+
+	chip = kzalloc(sizeof *chip, GFP_KERNEL);
+	if (chip == NULL) {
+		pr_err("kzalloc() failed.\n");
+		return -ENOMEM;
+	}
+
+	if (pdata != NULL)
+		dtest_channel = pdata->dtest_channel;
+	else
+		dtest_channel = -1;
+
+	mutex_init(&chip->pwm_mutex);
+
+	chip->dev = &pdev->dev;
+	pwm_chip = chip;
+
+	version = pm8xxx_get_version(chip->dev->parent);
+
+	if (version == PM8XXX_VERSION_8921 ||
+			version == PM8XXX_VERSION_8058 ||
+			version == PM8XXX_VERSION_8922 ||
+			version == PM8XXX_VERSION_8038) {
+		chip->is_lpg_supported = 1;
+	}
+	if (chip->is_lpg_supported) {
+		if (version == PM8XXX_VERSION_8922 ||
+				version == PM8XXX_VERSION_8038) {
+			for (i = 0; i < NUM_CLOCKS; i++)
+				pt_t[0][i] /= PRE_DIVIDE_2;
+			chip->pwm_channels = PM8XXX_LPG_V1_PWM_CHANNELS;
+		} else {
+			chip->pwm_channels = PM8XXX_LPG_V0_PWM_CHANNELS;
+		}
+		chip->pwm_total_pre_divs = NUM_LPG_PRE_DIVIDE;
+	} else {
+		chip->pwm_channels = PM8XXX_PWM_CHANNELS;
+		chip->pwm_total_pre_divs = NUM_PWM_PRE_DIVIDE;
+	}
+
+	chip->pwm_dev = kcalloc(chip->pwm_channels, sizeof(struct pwm_device),
+								GFP_KERNEL);
+	if (chip->pwm_dev == NULL) {
+		pr_err("kcalloc() failed.\n");
+		mutex_destroy(&chip->pwm_mutex);
+		kfree(chip);
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < chip->pwm_channels; i++) {
+		chip->pwm_dev[i].pwm_id = i;
+		chip->pwm_dev[i].chip = chip;
+		if (i == dtest_channel)
+			chip->pwm_dev[i].dtest_mode_supported = 1;
+	}
+
+	platform_set_drvdata(pdev, chip);
+
+	if (pm8xxx_pwm_dbg_probe(&pdev->dev) < 0)
+		pr_err("could not set up debugfs\n");
+
+	pr_notice("OK\n");
+	return 0;
+}
+
+static int __devexit pm8xxx_pwm_remove(struct platform_device *pdev)
+{
+	struct pm8xxx_pwm_chip	*chip = dev_get_drvdata(pdev->dev.parent);
+
+	pm8xxx_pwm_dbg_remove();
+	kfree(chip->pwm_dev);
+	mutex_destroy(&chip->pwm_mutex);
+	platform_set_drvdata(pdev, NULL);
+	kfree(chip);
+	return 0;
+}
+
+static struct platform_driver pm8xxx_pwm_driver = {
+	.probe		= pm8xxx_pwm_probe,
+	.remove		= __devexit_p(pm8xxx_pwm_remove),
+	.driver		= {
+		.name = PM8XXX_PWM_DEV_NAME,
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init pm8xxx_pwm_init(void)
+{
+	return platform_driver_register(&pm8xxx_pwm_driver);
+}
+
+static void __exit pm8xxx_pwm_exit(void)
+{
+	platform_driver_unregister(&pm8xxx_pwm_driver);
+}
+
+subsys_initcall(pm8xxx_pwm_init);
+module_exit(pm8xxx_pwm_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PM8XXX PWM driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:" PM8XXX_PWM_DEV_NAME);
diff --git a/drivers/mfd/pm8xxx-spk.c b/drivers/mfd/pm8xxx-spk.c
new file mode 100644
index 0000000..297ddfa
--- /dev/null
+++ b/drivers/mfd/pm8xxx-spk.c
@@ -0,0 +1,279 @@
+/* Copyright (c) 2012, 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/module.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/stddef.h>
+#include <linux/debugfs.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/mfd/pm8xxx/spk.h>
+
+#define PM8XXX_SPK_CTL1_REG_OFF		0
+#define PM8XXX_SPK_TEST_REG_1_OFF	1
+#define PM8XXX_SPK_TEST_REG_2_OFF	2
+
+#define PM8XXX_SPK_BANK_SEL		4
+#define PM8XXX_SPK_BANK_WRITE		0x80
+#define PM8XXX_SPK_BANK_VAL_MASK	0xF
+
+#define BOOST_6DB_GAIN_EN_MASK		0x8
+#define VSEL_LD0_1P1			0x0
+#define VSEL_LD0_1P2			0x2
+#define VSEL_LD0_1P0			0x4
+
+#define PWM_EN_MASK			0xF
+#define PM8XXX_SPK_TEST_REG_1_BANKS	8
+#define PM8XXX_SPK_TEST_REG_2_BANKS	2
+
+#define PM8XXX_SPK_GAIN			0x5
+#define PM8XXX_ADD_EN			0x1
+
+struct pm8xxx_spk_chip {
+	struct list_head                        link;
+	struct pm8xxx_spk_platform_data		pdata;
+	struct device                           *dev;
+	enum pm8xxx_version                     version;
+	struct mutex				spk_mutex;
+	u16					base;
+	u16					end;
+};
+
+static struct pm8xxx_spk_chip *the_spk_chip;
+
+static inline bool spk_defined(void)
+{
+	if (the_spk_chip == NULL || IS_ERR(the_spk_chip))
+		return false;
+	return true;
+}
+
+static int pm8xxx_spk_bank_write(u16 reg, u16 bank, u8 val)
+{
+	int rc = 0;
+	u8 bank_val = PM8XXX_SPK_BANK_WRITE | (bank << PM8XXX_SPK_BANK_SEL);
+
+	bank_val |= (val & PM8XXX_SPK_BANK_VAL_MASK);
+	mutex_lock(&the_spk_chip->spk_mutex);
+	rc = pm8xxx_writeb(the_spk_chip->dev->parent, reg, bank_val);
+	if (rc)
+		pr_err("pm8xxx_writeb(): rc=%d\n", rc);
+	mutex_unlock(&the_spk_chip->spk_mutex);
+	return rc;
+}
+
+
+static int pm8xxx_spk_read(u16 addr)
+{
+	int rc = 0;
+	u8 val = 0;
+
+	mutex_lock(&the_spk_chip->spk_mutex);
+	rc = pm8xxx_readb(the_spk_chip->dev->parent,
+			the_spk_chip->base + addr, &val);
+	if (rc) {
+		pr_err("pm8xxx_spk_readb() failed: rc=%d\n", rc);
+		val = rc;
+	}
+	mutex_unlock(&the_spk_chip->spk_mutex);
+
+	return val;
+}
+
+static int pm8xxx_spk_write(u16 addr, u8 val)
+{
+	int rc = 0;
+
+	mutex_lock(&the_spk_chip->spk_mutex);
+	rc = pm8xxx_writeb(the_spk_chip->dev->parent,
+			the_spk_chip->base + addr, val);
+	if (rc)
+		pr_err("pm8xxx_writeb() failed: rc=%d\n", rc);
+	mutex_unlock(&the_spk_chip->spk_mutex);
+	return rc;
+}
+
+int pm8xxx_spk_mute(bool mute)
+{
+	u8 val = 0;
+	int ret = 0;
+	if (spk_defined() == false) {
+		pr_err("Invalid spk handle or no spk_chip\n");
+		return -ENODEV;
+	}
+
+	val = pm8xxx_spk_read(PM8XXX_SPK_CTL1_REG_OFF);
+	if (val < 0)
+		return val;
+	val |= mute << 2;
+	ret = pm8xxx_spk_write(PM8XXX_SPK_CTL1_REG_OFF, val);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(pm8xxx_spk_mute);
+
+int pm8xxx_spk_gain(u8 gain)
+{
+	u8 val;
+	int ret = 0;
+
+	if (spk_defined() == false) {
+		pr_err("Invalid spk handle or no spk_chip\n");
+		return -ENODEV;
+	}
+
+	val = pm8xxx_spk_read(PM8XXX_SPK_CTL1_REG_OFF);
+	if (val < 0)
+		return val;
+	val |= (gain << 4);
+	ret = pm8xxx_spk_write(PM8XXX_SPK_CTL1_REG_OFF, val);
+	if (!ret) {
+		pm8xxx_spk_bank_write(the_spk_chip->base
+			+ PM8XXX_SPK_TEST_REG_1_OFF,
+			0, BOOST_6DB_GAIN_EN_MASK | VSEL_LD0_1P2);
+	}
+	return ret;
+}
+EXPORT_SYMBOL_GPL(pm8xxx_spk_gain);
+
+int pm8xxx_spk_enable(int enable)
+{
+	int val = 0;
+	u16 addr;
+	int ret = 0;
+
+	if (spk_defined() == false) {
+		pr_err("Invalid spk handle or no spk_chip\n");
+		return -ENODEV;
+	}
+
+	addr = the_spk_chip->base + PM8XXX_SPK_TEST_REG_1_OFF;
+	val = pm8xxx_spk_read(PM8XXX_SPK_CTL1_REG_OFF);
+	if (val < 0)
+		return val;
+	val |= (enable << 3);
+	ret = pm8xxx_spk_write(PM8XXX_SPK_CTL1_REG_OFF, val);
+	if (!ret)
+		ret = pm8xxx_spk_bank_write(addr, 6, PWM_EN_MASK);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(pm8xxx_spk_enable);
+
+static int pm8xxx_spk_config(void)
+{
+	u16 addr;
+	int ret = 0;
+
+	if (spk_defined() == false) {
+		pr_err("Invalid spk handle or no spk_chip\n");
+		return -ENODEV;
+	}
+
+	addr = the_spk_chip->base + PM8XXX_SPK_TEST_REG_1_OFF;
+	ret = pm8xxx_spk_bank_write(addr, 6, PWM_EN_MASK & 0);
+	if (!ret)
+		ret = pm8xxx_spk_gain(PM8XXX_SPK_GAIN);
+	return ret;
+}
+
+static int __devinit pm8xxx_spk_probe(struct platform_device *pdev)
+{
+	const struct pm8xxx_spk_platform_data *pdata = pdev->dev.platform_data;
+	int ret = 0;
+
+	if (!pdata) {
+		pr_err("missing platform data\n");
+		return -EINVAL;
+	}
+
+	the_spk_chip = kzalloc(sizeof(struct pm8xxx_spk_chip), GFP_KERNEL);
+	if (the_spk_chip == NULL) {
+		pr_err("kzalloc() failed.\n");
+		return -ENOMEM;
+	}
+
+	mutex_init(&the_spk_chip->spk_mutex);
+
+	the_spk_chip->dev = &pdev->dev;
+	the_spk_chip->version = pm8xxx_get_version(the_spk_chip->dev->parent);
+	switch (pm8xxx_get_version(the_spk_chip->dev->parent)) {
+	case PM8XXX_VERSION_8038:
+		break;
+	default:
+		ret = -ENODEV;
+		goto err_handle;
+	}
+
+	memcpy(&(the_spk_chip->pdata), pdata,
+			sizeof(struct pm8xxx_spk_platform_data));
+
+	the_spk_chip->base = pdev->resource[0].start;
+	the_spk_chip->end = pdev->resource[0].end;
+
+	if (the_spk_chip->pdata.spk_add_enable) {
+		int val;
+		val = pm8xxx_spk_read(PM8XXX_SPK_CTL1_REG_OFF);
+		if (val < 0) {
+			ret = val;
+			goto err_handle;
+		}
+		val |= (the_spk_chip->pdata.spk_add_enable & PM8XXX_ADD_EN);
+		ret = pm8xxx_spk_write(PM8XXX_SPK_CTL1_REG_OFF, val);
+		if (ret < 0)
+			goto err_handle;
+	}
+	return pm8xxx_spk_config();
+err_handle:
+	pr_err("pm8xxx_spk_probe failed."
+			"Audio unavailable on speaker.\n");
+	mutex_destroy(&the_spk_chip->spk_mutex);
+	kfree(the_spk_chip);
+	return ret;
+}
+
+static int __devexit pm8xxx_spk_remove(struct platform_device *pdev)
+{
+	if (spk_defined() == false) {
+		pr_err("Invalid spk handle or no spk_chip\n");
+		return -ENODEV;
+	}
+	mutex_destroy(&the_spk_chip->spk_mutex);
+	kfree(the_spk_chip);
+	return 0;
+}
+
+static struct platform_driver pm8xxx_spk_driver = {
+	.probe		= pm8xxx_spk_probe,
+	.remove		= __devexit_p(pm8xxx_spk_remove),
+	.driver		= {
+		.name = PM8XXX_SPK_DEV_NAME,
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init pm8xxx_spk_init(void)
+{
+	return platform_driver_register(&pm8xxx_spk_driver);
+}
+subsys_initcall(pm8xxx_spk_init);
+
+static void __exit pm8xxx_spk_exit(void)
+{
+	platform_driver_unregister(&pm8xxx_spk_driver);
+}
+module_exit(pm8xxx_spk_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PM8XXX SPK driver");
+MODULE_ALIAS("platform:" PM8XXX_SPK_DEV_NAME);
diff --git a/drivers/mfd/pmic8058.c b/drivers/mfd/pmic8058.c
new file mode 100644
index 0000000..8f7ab2d
--- /dev/null
+++ b/drivers/mfd/pmic8058.c
@@ -0,0 +1,792 @@
+/* Copyright (c) 2009-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.
+ *
+ */
+/*
+ * Qualcomm PMIC8058 driver
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/msm_ssbi.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/pmic8058.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/msm_adc.h>
+#include <linux/module.h>
+
+#define REG_MPP_BASE			0x50
+#define REG_IRQ_BASE			0x1BB
+
+/* PMIC8058 Revision */
+#define PM8058_REG_REV			0x002  /* PMIC4 revision */
+#define PM8058_VERSION_MASK		0xF0
+#define PM8058_REVISION_MASK		0x0F
+#define PM8058_VERSION_VALUE		0xE0
+
+/* PMIC 8058 Battery Alarm SSBI registers */
+#define REG_BATT_ALARM_THRESH		0x023
+#define REG_BATT_ALARM_CTRL1		0x024
+#define REG_BATT_ALARM_CTRL2		0x0AA
+#define REG_BATT_ALARM_PWM_CTRL		0x0A3
+
+#define REG_TEMP_ALRM_CTRL		0x1B
+#define REG_TEMP_ALRM_PWM		0x9B
+
+/* PON CNTL 4 register */
+#define SSBI_REG_ADDR_PON_CNTL_4 0x98
+#define PM8058_PON_RESET_EN_MASK 0x01
+
+/* PON CNTL 5 register */
+#define SSBI_REG_ADDR_PON_CNTL_5 0x7B
+#define PM8058_HARD_RESET_EN_MASK 0x08
+
+/* GP_TEST1 register */
+#define SSBI_REG_ADDR_GP_TEST_1		0x07A
+
+#define PM8058_RTC_BASE			0x1E8
+#define PM8058_OTHC_CNTR_BASE0		0xA0
+#define PM8058_OTHC_CNTR_BASE1		0x134
+#define PM8058_OTHC_CNTR_BASE2		0x137
+
+#define SINGLE_IRQ_RESOURCE(_name, _irq) \
+{ \
+	.name	= _name, \
+	.start	= _irq, \
+	.end	= _irq, \
+	.flags	= IORESOURCE_IRQ, \
+}
+
+struct pm8058_chip {
+	struct pm8058_platform_data	pdata;
+	struct device		*dev;
+	struct pm_irq_chip	*irq_chip;
+	struct mfd_cell         *mfd_regulators, *mfd_xo_buffers;
+
+	u8		revision;
+};
+
+static int pm8058_readb(const struct device *dev, u16 addr, u8 *val)
+{
+	const struct pm8xxx_drvdata *pm8058_drvdata = dev_get_drvdata(dev);
+	const struct pm8058_chip *pmic = pm8058_drvdata->pm_chip_data;
+
+	return msm_ssbi_read(pmic->dev->parent, addr, val, 1);
+}
+
+static int pm8058_writeb(const struct device *dev, u16 addr, u8 val)
+{
+	const struct pm8xxx_drvdata *pm8058_drvdata = dev_get_drvdata(dev);
+	const struct pm8058_chip *pmic = pm8058_drvdata->pm_chip_data;
+
+	return msm_ssbi_write(pmic->dev->parent, addr, &val, 1);
+}
+
+static int pm8058_read_buf(const struct device *dev, u16 addr, u8 *buf,
+								int cnt)
+{
+	const struct pm8xxx_drvdata *pm8058_drvdata = dev_get_drvdata(dev);
+	const struct pm8058_chip *pmic = pm8058_drvdata->pm_chip_data;
+
+	return msm_ssbi_read(pmic->dev->parent, addr, buf, cnt);
+}
+
+static int pm8058_write_buf(const struct device *dev, u16 addr, u8 *buf,
+								int cnt)
+{
+	const struct pm8xxx_drvdata *pm8058_drvdata = dev_get_drvdata(dev);
+	const struct pm8058_chip *pmic = pm8058_drvdata->pm_chip_data;
+
+	return msm_ssbi_write(pmic->dev->parent, addr, buf, cnt);
+}
+
+static int pm8058_read_irq_stat(const struct device *dev, int irq)
+{
+	const struct pm8xxx_drvdata *pm8058_drvdata = dev_get_drvdata(dev);
+	const struct pm8058_chip *pmic = pm8058_drvdata->pm_chip_data;
+
+	return pm8xxx_get_irq_stat(pmic->irq_chip, irq);
+
+	return 0;
+}
+
+static enum pm8xxx_version pm8058_get_version(const struct device *dev)
+{
+	const struct pm8xxx_drvdata *pm8058_drvdata = dev_get_drvdata(dev);
+	const struct pm8058_chip *pmic = pm8058_drvdata->pm_chip_data;
+	enum pm8xxx_version version = -ENODEV;
+
+	if ((pmic->revision & PM8058_VERSION_MASK) == PM8058_VERSION_VALUE)
+		version = PM8XXX_VERSION_8058;
+
+	return version;
+}
+
+static int pm8058_get_revision(const struct device *dev)
+{
+	const struct pm8xxx_drvdata *pm8058_drvdata = dev_get_drvdata(dev);
+	const struct pm8058_chip *pmic = pm8058_drvdata->pm_chip_data;
+
+	return pmic->revision & PM8058_REVISION_MASK;
+}
+
+static struct pm8xxx_drvdata pm8058_drvdata = {
+	.pmic_readb		= pm8058_readb,
+	.pmic_writeb		= pm8058_writeb,
+	.pmic_read_buf		= pm8058_read_buf,
+	.pmic_write_buf		= pm8058_write_buf,
+	.pmic_read_irq_stat	= pm8058_read_irq_stat,
+	.pmic_get_version	= pm8058_get_version,
+	.pmic_get_revision	= pm8058_get_revision,
+};
+
+static const struct resource pm8058_charger_resources[] __devinitconst = {
+	SINGLE_IRQ_RESOURCE("CHGVAL",		PM8058_CHGVAL_IRQ),
+	SINGLE_IRQ_RESOURCE("CHGINVAL",		PM8058_CHGINVAL_IRQ),
+	SINGLE_IRQ_RESOURCE("CHGILIM",		PM8058_CHGILIM_IRQ),
+	SINGLE_IRQ_RESOURCE("VCP",		PM8058_VCP_IRQ),
+	SINGLE_IRQ_RESOURCE("ATC_DONE",		PM8058_ATC_DONE_IRQ),
+	SINGLE_IRQ_RESOURCE("ATCFAIL",		PM8058_ATCFAIL_IRQ),
+	SINGLE_IRQ_RESOURCE("AUTO_CHGDONE",	PM8058_AUTO_CHGDONE_IRQ),
+	SINGLE_IRQ_RESOURCE("AUTO_CHGFAIL",	PM8058_AUTO_CHGFAIL_IRQ),
+	SINGLE_IRQ_RESOURCE("CHGSTATE",		PM8058_CHGSTATE_IRQ),
+	SINGLE_IRQ_RESOURCE("FASTCHG",		PM8058_FASTCHG_IRQ),
+	SINGLE_IRQ_RESOURCE("CHG_END",		PM8058_CHG_END_IRQ),
+	SINGLE_IRQ_RESOURCE("BATTTEMP",		PM8058_BATTTEMP_IRQ),
+	SINGLE_IRQ_RESOURCE("CHGHOT",		PM8058_CHGHOT_IRQ),
+	SINGLE_IRQ_RESOURCE("CHGTLIMIT",	PM8058_CHGTLIMIT_IRQ),
+	SINGLE_IRQ_RESOURCE("CHG_GONE",		PM8058_CHG_GONE_IRQ),
+	SINGLE_IRQ_RESOURCE("VCPMAJOR",		PM8058_VCPMAJOR_IRQ),
+	SINGLE_IRQ_RESOURCE("VBATDET",		PM8058_VBATDET_IRQ),
+	SINGLE_IRQ_RESOURCE("BATFET",		PM8058_BATFET_IRQ),
+	SINGLE_IRQ_RESOURCE("BATT_REPLACE",	PM8058_BATT_REPLACE_IRQ),
+	SINGLE_IRQ_RESOURCE("BATTCONNECT",	PM8058_BATTCONNECT_IRQ),
+	SINGLE_IRQ_RESOURCE("VBATDET_LOW",	PM8058_VBATDET_LOW_IRQ),
+};
+
+static struct mfd_cell pm8058_charger_cell __devinitdata = {
+	.name		= "pm8058-charger",
+	.id		= -1,
+	.resources	= pm8058_charger_resources,
+	.num_resources	= ARRAY_SIZE(pm8058_charger_resources),
+};
+
+static const struct resource misc_cell_resources[] __devinitconst = {
+	SINGLE_IRQ_RESOURCE("pm8xxx_osc_halt_irq", PM8058_OSCHALT_IRQ),
+};
+
+static struct mfd_cell misc_cell __devinitdata = {
+	.name		= PM8XXX_MISC_DEV_NAME,
+	.id		= -1,
+	.resources	= misc_cell_resources,
+	.num_resources	= ARRAY_SIZE(misc_cell_resources),
+};
+
+static struct mfd_cell pm8058_pwm_cell __devinitdata = {
+	.name		= "pm8058-pwm",
+	.id		= -1,
+};
+
+static struct resource xoadc_resources[] = {
+	SINGLE_IRQ_RESOURCE(NULL, PM8058_ADC_IRQ),
+};
+
+static struct mfd_cell xoadc_cell __devinitdata = {
+	.name		= "pm8058-xoadc",
+	.id		= -1,
+	.resources	= xoadc_resources,
+	.num_resources	= ARRAY_SIZE(xoadc_resources),
+};
+
+static const struct resource thermal_alarm_cell_resources[] __devinitconst = {
+	SINGLE_IRQ_RESOURCE("pm8058_tempstat_irq", PM8058_TEMPSTAT_IRQ),
+	SINGLE_IRQ_RESOURCE("pm8058_overtemp_irq", PM8058_OVERTEMP_IRQ),
+};
+
+static struct pm8xxx_tm_core_data thermal_alarm_cdata = {
+	.adc_channel			= CHANNEL_ADC_DIE_TEMP,
+	.adc_type			= PM8XXX_TM_ADC_PM8058_ADC,
+	.reg_addr_temp_alarm_ctrl	= REG_TEMP_ALRM_CTRL,
+	.reg_addr_temp_alarm_pwm	= REG_TEMP_ALRM_PWM,
+	.tm_name			= "pm8058_tz",
+	.irq_name_temp_stat		= "pm8058_tempstat_irq",
+	.irq_name_over_temp		= "pm8058_overtemp_irq",
+};
+
+static struct mfd_cell thermal_alarm_cell __devinitdata = {
+	.name		= PM8XXX_TM_DEV_NAME,
+	.id		= -1,
+	.resources	= thermal_alarm_cell_resources,
+	.num_resources	= ARRAY_SIZE(thermal_alarm_cell_resources),
+	.platform_data	= &thermal_alarm_cdata,
+	.pdata_size	= sizeof(struct pm8xxx_tm_core_data),
+};
+
+static struct mfd_cell debugfs_cell __devinitdata = {
+	.name		= "pm8xxx-debug",
+	.id		= -1,
+	.platform_data	= "pm8058-dbg",
+	.pdata_size	= sizeof("pm8058-dbg"),
+};
+
+static const struct resource othc0_cell_resources[] __devinitconst = {
+	{
+		.name	= "othc_base",
+		.start	= PM8058_OTHC_CNTR_BASE0,
+		.end	= PM8058_OTHC_CNTR_BASE0,
+		.flags	= IORESOURCE_IO,
+	},
+};
+
+static const struct resource othc1_cell_resources[] __devinitconst = {
+	SINGLE_IRQ_RESOURCE(NULL, PM8058_SW_1_IRQ),
+	SINGLE_IRQ_RESOURCE(NULL, PM8058_IR_1_IRQ),
+	{
+		.name	= "othc_base",
+		.start	= PM8058_OTHC_CNTR_BASE1,
+		.end	= PM8058_OTHC_CNTR_BASE1,
+		.flags	= IORESOURCE_IO,
+	},
+};
+
+static const struct resource othc2_cell_resources[] __devinitconst = {
+	{
+		.name	= "othc_base",
+		.start	= PM8058_OTHC_CNTR_BASE2,
+		.end	= PM8058_OTHC_CNTR_BASE2,
+		.flags	= IORESOURCE_IO,
+	},
+};
+
+static const struct resource batt_alarm_cell_resources[] __devinitconst = {
+	SINGLE_IRQ_RESOURCE("pm8058_batt_alarm_irq", PM8058_BATT_ALARM_IRQ),
+};
+
+static struct mfd_cell leds_cell __devinitdata = {
+	.name		= "pm8058-led",
+	.id		= -1,
+};
+
+static struct mfd_cell othc0_cell __devinitdata = {
+	.name		= "pm8058-othc",
+	.id		= 0,
+	.resources	= othc0_cell_resources,
+	.num_resources  = ARRAY_SIZE(othc0_cell_resources),
+};
+
+static struct mfd_cell othc1_cell __devinitdata = {
+	.name		= "pm8058-othc",
+	.id		= 1,
+	.resources	= othc1_cell_resources,
+	.num_resources  = ARRAY_SIZE(othc1_cell_resources),
+};
+
+static struct mfd_cell othc2_cell __devinitdata = {
+	.name		= "pm8058-othc",
+	.id		= 2,
+	.resources	= othc2_cell_resources,
+	.num_resources  = ARRAY_SIZE(othc2_cell_resources),
+};
+
+static struct pm8xxx_batt_alarm_core_data batt_alarm_cdata = {
+	.irq_name		= "pm8058_batt_alarm_irq",
+	.reg_addr_threshold	= REG_BATT_ALARM_THRESH,
+	.reg_addr_ctrl1		= REG_BATT_ALARM_CTRL1,
+	.reg_addr_ctrl2		= REG_BATT_ALARM_CTRL2,
+	.reg_addr_pwm_ctrl	= REG_BATT_ALARM_PWM_CTRL,
+};
+
+static struct mfd_cell batt_alarm_cell __devinitdata = {
+	.name		= PM8XXX_BATT_ALARM_DEV_NAME,
+	.id		= -1,
+	.resources	= batt_alarm_cell_resources,
+	.num_resources	= ARRAY_SIZE(batt_alarm_cell_resources),
+	.platform_data	= &batt_alarm_cdata,
+	.pdata_size	= sizeof(struct pm8xxx_batt_alarm_core_data),
+};
+
+static struct mfd_cell upl_cell __devinitdata = {
+	.name		= PM8XXX_UPL_DEV_NAME,
+	.id		= -1,
+};
+
+static struct mfd_cell nfc_cell __devinitdata = {
+	.name		= PM8XXX_NFC_DEV_NAME,
+	.id		= -1,
+};
+
+static const struct resource rtc_cell_resources[] __devinitconst = {
+	[0] = SINGLE_IRQ_RESOURCE(NULL, PM8058_RTC_ALARM_IRQ),
+	[1] = {
+		.name   = "pmic_rtc_base",
+		.start  = PM8058_RTC_BASE,
+		.end    = PM8058_RTC_BASE,
+		.flags  = IORESOURCE_IO,
+	},
+};
+
+static struct mfd_cell rtc_cell __devinitdata = {
+	.name		= PM8XXX_RTC_DEV_NAME,
+	.id		= -1,
+	.resources	= rtc_cell_resources,
+	.num_resources  = ARRAY_SIZE(rtc_cell_resources),
+};
+
+static const struct resource resources_pwrkey[] __devinitconst = {
+	SINGLE_IRQ_RESOURCE(NULL, PM8058_PWRKEY_REL_IRQ),
+	SINGLE_IRQ_RESOURCE(NULL, PM8058_PWRKEY_PRESS_IRQ),
+};
+
+static struct mfd_cell vibrator_cell __devinitdata = {
+	.name		= PM8XXX_VIBRATOR_DEV_NAME,
+	.id		= -1,
+};
+
+static struct mfd_cell pwrkey_cell __devinitdata = {
+	.name		= PM8XXX_PWRKEY_DEV_NAME,
+	.id		= -1,
+	.num_resources	= ARRAY_SIZE(resources_pwrkey),
+	.resources	= resources_pwrkey,
+};
+
+static const struct resource resources_keypad[] = {
+	SINGLE_IRQ_RESOURCE(NULL, PM8058_KEYPAD_IRQ),
+	SINGLE_IRQ_RESOURCE(NULL, PM8058_KEYSTUCK_IRQ),
+};
+
+static struct mfd_cell keypad_cell __devinitdata = {
+	.name		= PM8XXX_KEYPAD_DEV_NAME,
+	.id		= -1,
+	.num_resources  = ARRAY_SIZE(resources_keypad),
+	.resources	= resources_keypad,
+};
+
+static const struct resource mpp_cell_resources[] __devinitconst = {
+	{
+		.start	= PM8058_IRQ_BLOCK_BIT(PM8058_MPP_BLOCK_START, 0),
+		.end	= PM8058_IRQ_BLOCK_BIT(PM8058_MPP_BLOCK_START, 0)
+			  + PM8058_MPPS - 1,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct mfd_cell mpp_cell __devinitdata = {
+	.name		= PM8XXX_MPP_DEV_NAME,
+	.id		= 0,
+	.resources	= mpp_cell_resources,
+	.num_resources	= ARRAY_SIZE(mpp_cell_resources),
+};
+
+static const struct resource gpio_cell_resources[] __devinitconst = {
+	[0] = {
+		.start = PM8058_IRQ_BLOCK_BIT(PM8058_GPIO_BLOCK_START, 0),
+		.end   = PM8058_IRQ_BLOCK_BIT(PM8058_GPIO_BLOCK_START, 0)
+			+ PM8058_GPIOS - 1,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct mfd_cell gpio_cell __devinitdata = {
+	.name		= PM8XXX_GPIO_DEV_NAME,
+	.id		= -1,
+	.resources	= gpio_cell_resources,
+	.num_resources	= ARRAY_SIZE(gpio_cell_resources),
+};
+
+static int __devinit
+pm8058_add_subdevices(const struct pm8058_platform_data *pdata,
+				struct pm8058_chip *pmic)
+{
+	int rc = 0, irq_base = 0, i;
+	struct pm_irq_chip *irq_chip;
+	static struct mfd_cell *mfd_regulators, *mfd_xo_buffers;
+
+	if (pdata->irq_pdata) {
+		pdata->irq_pdata->irq_cdata.nirqs = PM8058_NR_IRQS;
+		pdata->irq_pdata->irq_cdata.base_addr = REG_IRQ_BASE;
+		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;
+	}
+
+	if (pdata->gpio_pdata) {
+		pdata->gpio_pdata->gpio_cdata.ngpios = PM8058_GPIOS;
+		gpio_cell.platform_data = pdata->gpio_pdata;
+		gpio_cell.pdata_size = sizeof(struct pm8xxx_gpio_platform_data);
+		rc = mfd_add_devices(pmic->dev, 0, &gpio_cell, 1,
+					NULL, irq_base);
+		if (rc) {
+			pr_err("Failed to add  gpio subdevice ret=%d\n", rc);
+			goto bail;
+		}
+	}
+
+	if (pdata->mpp_pdata) {
+		pdata->mpp_pdata->core_data.nmpps = PM8058_MPPS;
+		pdata->mpp_pdata->core_data.base_addr = REG_MPP_BASE;
+		mpp_cell.platform_data = pdata->mpp_pdata;
+		mpp_cell.pdata_size = sizeof(struct pm8xxx_mpp_platform_data);
+		rc = mfd_add_devices(pmic->dev, 0, &mpp_cell, 1, NULL,
+					irq_base);
+		if (rc) {
+			pr_err("Failed to add mpp subdevice ret=%d\n", rc);
+			goto bail;
+		}
+	}
+
+	if (pdata->num_regulators > 0 && pdata->regulator_pdatas) {
+		mfd_regulators = kzalloc(sizeof(struct mfd_cell)
+					 * (pdata->num_regulators), GFP_KERNEL);
+		if (!mfd_regulators) {
+			pr_err("Cannot allocate %d bytes for pm8058 regulator "
+				"mfd cells\n", sizeof(struct mfd_cell)
+						* (pdata->num_regulators));
+			rc = -ENOMEM;
+			goto bail;
+		}
+		for (i = 0; i < pdata->num_regulators; i++) {
+			mfd_regulators[i].name = "pm8058-regulator";
+			mfd_regulators[i].id = pdata->regulator_pdatas[i].id;
+			mfd_regulators[i].platform_data =
+				&(pdata->regulator_pdatas[i]);
+			mfd_regulators[i].pdata_size =
+					sizeof(struct pm8058_vreg_pdata);
+		}
+		rc = mfd_add_devices(pmic->dev, 0, mfd_regulators,
+				pdata->num_regulators, NULL, irq_base);
+		if (rc) {
+			pr_err("Failed to add regulator subdevices ret=%d\n",
+				rc);
+			kfree(mfd_regulators);
+			goto bail;
+		}
+		pmic->mfd_regulators = mfd_regulators;
+	}
+
+	if (pdata->num_xo_buffers > 0 && pdata->xo_buffer_pdata) {
+		mfd_xo_buffers = kzalloc(sizeof(struct mfd_cell)
+					 * (pdata->num_xo_buffers), GFP_KERNEL);
+		if (!mfd_xo_buffers) {
+			pr_err("Cannot allocate %d bytes for pm8058 XO buffer "
+				"mfd cells\n", sizeof(struct mfd_cell)
+						* (pdata->num_xo_buffers));
+			rc = -ENOMEM;
+			goto bail;
+		}
+		for (i = 0; i < pdata->num_xo_buffers; i++) {
+			mfd_xo_buffers[i].name = PM8058_XO_BUFFER_DEV_NAME;
+			mfd_xo_buffers[i].id = pdata->xo_buffer_pdata[i].id;
+			mfd_xo_buffers[i].platform_data =
+				&(pdata->xo_buffer_pdata[i]);
+			mfd_xo_buffers[i].pdata_size =
+					sizeof(struct pm8058_xo_pdata);
+		}
+		rc = mfd_add_devices(pmic->dev, 0, mfd_xo_buffers,
+				pdata->num_xo_buffers, NULL, irq_base);
+		if (rc) {
+			pr_err("Failed to add XO buffer subdevices ret=%d\n",
+				rc);
+			kfree(mfd_xo_buffers);
+			goto bail;
+		}
+		pmic->mfd_xo_buffers = mfd_xo_buffers;
+	}
+
+	if (pdata->keypad_pdata) {
+		keypad_cell.platform_data = pdata->keypad_pdata;
+		keypad_cell.pdata_size =
+			sizeof(struct pm8xxx_keypad_platform_data);
+		rc = mfd_add_devices(pmic->dev, 0, &keypad_cell, 1, NULL,
+					irq_base);
+		if (rc) {
+			pr_err("Failed to add keypad subdevice ret=%d\n", rc);
+			goto bail;
+		}
+	}
+
+	if (pdata->rtc_pdata) {
+		rtc_cell.platform_data = pdata->rtc_pdata;
+		rtc_cell.pdata_size = sizeof(struct pm8xxx_rtc_platform_data);
+		rc = mfd_add_devices(pmic->dev, 0, &rtc_cell, 1, NULL,
+						irq_base);
+		if (rc) {
+			pr_err("Failed to add rtc subdevice ret=%d\n", rc);
+			goto bail;
+		}
+	}
+
+	if (pdata->pwrkey_pdata) {
+		pwrkey_cell.platform_data = pdata->pwrkey_pdata;
+		pwrkey_cell.pdata_size =
+			sizeof(struct pm8xxx_pwrkey_platform_data);
+		rc = mfd_add_devices(pmic->dev, 0, &pwrkey_cell, 1, NULL,
+							irq_base);
+		if (rc) {
+			pr_err("Failed to add pwrkey subdevice ret=%d\n", rc);
+			goto bail;
+		}
+	}
+
+	if (pdata->vibrator_pdata) {
+		vibrator_cell.platform_data = pdata->vibrator_pdata;
+		vibrator_cell.pdata_size =
+				sizeof(struct pm8xxx_vibrator_platform_data);
+		rc = mfd_add_devices(pmic->dev, 0, &vibrator_cell, 1, NULL,
+								irq_base);
+		if (rc) {
+			pr_err("Failed to add vibrator subdevice ret=%d\n",
+									rc);
+			goto bail;
+		}
+	}
+
+	if (pdata->leds_pdata) {
+		leds_cell.platform_data = pdata->leds_pdata;
+		leds_cell.pdata_size =
+			sizeof(struct pmic8058_leds_platform_data);
+		rc = mfd_add_devices(pmic->dev, 0, &leds_cell, 1, NULL,
+								irq_base);
+		if (rc) {
+			pr_err("Failed to add leds subdevice ret=%d\n", rc);
+			goto bail;
+		}
+	}
+
+	if (pdata->xoadc_pdata) {
+		xoadc_cell.platform_data = pdata->xoadc_pdata;
+		xoadc_cell.pdata_size =
+			sizeof(struct xoadc_platform_data);
+		rc = mfd_add_devices(pmic->dev, 0, &xoadc_cell, 1, NULL,
+								irq_base);
+		if (rc) {
+			pr_err("Failed to add leds subdevice ret=%d\n", rc);
+			goto bail;
+		}
+	}
+
+	if (pdata->othc0_pdata) {
+		othc0_cell.platform_data = pdata->othc0_pdata;
+		othc0_cell.pdata_size =
+			sizeof(struct pmic8058_othc_config_pdata);
+		rc = mfd_add_devices(pmic->dev, 0, &othc0_cell, 1, NULL, 0);
+		if (rc) {
+			pr_err("Failed to add othc0 subdevice ret=%d\n", rc);
+			goto bail;
+		}
+	}
+
+	if (pdata->othc1_pdata) {
+		othc1_cell.platform_data = pdata->othc1_pdata;
+		othc1_cell.pdata_size =
+			sizeof(struct pmic8058_othc_config_pdata);
+		rc = mfd_add_devices(pmic->dev, 0, &othc1_cell, 1, NULL,
+								irq_base);
+		if (rc) {
+			pr_err("Failed to add othc1 subdevice ret=%d\n", rc);
+			goto bail;
+		}
+	}
+
+	if (pdata->othc2_pdata) {
+		othc2_cell.platform_data = pdata->othc2_pdata;
+		othc2_cell.pdata_size =
+			sizeof(struct pmic8058_othc_config_pdata);
+		rc = mfd_add_devices(pmic->dev, 0, &othc2_cell, 1, NULL, 0);
+		if (rc) {
+			pr_err("Failed to add othc2 subdevice ret=%d\n", rc);
+			goto bail;
+		}
+	}
+
+	if (pdata->pwm_pdata) {
+		pm8058_pwm_cell.platform_data = pdata->pwm_pdata;
+		pm8058_pwm_cell.pdata_size = sizeof(struct pm8058_pwm_pdata);
+		rc = mfd_add_devices(pmic->dev, 0, &pm8058_pwm_cell, 1, NULL,
+								irq_base);
+		if (rc) {
+			pr_err("Failed to add pwm subdevice ret=%d\n", rc);
+			goto bail;
+		}
+	}
+
+	if (pdata->misc_pdata) {
+		misc_cell.platform_data = pdata->misc_pdata;
+		misc_cell.pdata_size = sizeof(struct pm8xxx_misc_platform_data);
+		rc = mfd_add_devices(pmic->dev, 0, &misc_cell, 1, NULL,
+				      irq_base);
+		if (rc) {
+			pr_err("Failed to add  misc subdevice ret=%d\n", rc);
+			goto bail;
+		}
+	}
+
+	rc = mfd_add_devices(pmic->dev, 0, &thermal_alarm_cell, 1, NULL,
+				irq_base);
+	if (rc) {
+		pr_err("Failed to add thermal alarm subdevice ret=%d\n",
+			rc);
+		goto bail;
+	}
+
+	rc = mfd_add_devices(pmic->dev, 0, &batt_alarm_cell, 1, NULL,
+				irq_base);
+	if (rc) {
+		pr_err("Failed to add battery alarm subdevice ret=%d\n",
+			rc);
+		goto bail;
+	}
+
+	rc = mfd_add_devices(pmic->dev, 0, &upl_cell, 1, NULL, 0);
+	if (rc) {
+		pr_err("Failed to add upl subdevice ret=%d\n", rc);
+		goto bail;
+	}
+
+	rc = mfd_add_devices(pmic->dev, 0, &nfc_cell, 1, NULL, 0);
+	if (rc) {
+		pr_err("Failed to add upl subdevice ret=%d\n", rc);
+		goto bail;
+	}
+
+	if (pdata->charger_pdata) {
+		pm8058_charger_cell.platform_data = pdata->charger_pdata;
+		pm8058_charger_cell.pdata_size = sizeof(struct
+						pmic8058_charger_data);
+		rc = mfd_add_devices(pmic->dev, 0, &pm8058_charger_cell,
+						1, NULL, irq_base);
+		if (rc) {
+			pr_err("Failed to add charger subdevice ret=%d\n", rc);
+			goto bail;
+		}
+	}
+
+	rc = mfd_add_devices(pmic->dev, 0, &debugfs_cell, 1, NULL, irq_base);
+	if (rc) {
+		pr_err("Failed to add debugfs subdevice ret=%d\n", rc);
+		goto bail;
+	}
+
+	return rc;
+bail:
+	if (pmic->irq_chip) {
+		pm8xxx_irq_exit(pmic->irq_chip);
+		pmic->irq_chip = NULL;
+	}
+	return rc;
+}
+
+static int __devinit pm8058_probe(struct platform_device *pdev)
+{
+	int rc;
+	struct pm8058_platform_data *pdata = pdev->dev.platform_data;
+	struct pm8058_chip *pmic;
+
+	if (pdata == NULL) {
+		pr_err("%s: No platform_data or IRQ.\n", __func__);
+		return -ENODEV;
+	}
+
+	pmic = kzalloc(sizeof *pmic, GFP_KERNEL);
+	if (pmic == NULL) {
+		pr_err("%s: kzalloc() failed.\n", __func__);
+		return -ENOMEM;
+	}
+
+	pmic->dev = &pdev->dev;
+
+	pm8058_drvdata.pm_chip_data = pmic;
+	platform_set_drvdata(pdev, &pm8058_drvdata);
+
+	/* Read PMIC chip revision */
+	rc = pm8058_readb(pmic->dev, PM8058_REG_REV, &pmic->revision);
+	if (rc)
+		pr_err("%s: Failed on pm8058_readb for revision: rc=%d.\n",
+			__func__, rc);
+
+	pr_info("%s: PMIC revision: %X\n", __func__, pmic->revision);
+
+	(void) memcpy((void *)&pmic->pdata, (const void *)pdata,
+		      sizeof(pmic->pdata));
+
+	rc = pm8058_add_subdevices(pdata, pmic);
+	if (rc) {
+		pr_err("Cannot add subdevices rc=%d\n", rc);
+		goto err;
+	}
+
+	rc = pm8xxx_hard_reset_config(PM8XXX_SHUTDOWN_ON_HARD_RESET);
+	if (rc < 0)
+		pr_err("%s: failed to config shutdown on hard reset: %d\n",
+								__func__, rc);
+
+	return 0;
+
+err:
+	mfd_remove_devices(pmic->dev);
+	platform_set_drvdata(pdev, NULL);
+	kfree(pmic);
+	return rc;
+}
+
+static int __devexit pm8058_remove(struct platform_device *pdev)
+{
+	struct pm8xxx_drvdata *drvdata;
+	struct pm8058_chip *pmic = NULL;
+
+	drvdata = platform_get_drvdata(pdev);
+	if (drvdata)
+		pmic = drvdata->pm_chip_data;
+	if (pmic) {
+		if (pmic->dev)
+			mfd_remove_devices(pmic->dev);
+		if (pmic->irq_chip)
+			pm8xxx_irq_exit(pmic->irq_chip);
+		kfree(pmic->mfd_regulators);
+		kfree(pmic);
+	}
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+static struct platform_driver pm8058_driver = {
+	.probe		= pm8058_probe,
+	.remove		= __devexit_p(pm8058_remove),
+	.driver		= {
+		.name	= "pm8058-core",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init pm8058_init(void)
+{
+	return platform_driver_register(&pm8058_driver);
+}
+postcore_initcall(pm8058_init);
+
+static void __exit pm8058_exit(void)
+{
+	platform_driver_unregister(&pm8058_driver);
+}
+module_exit(pm8058_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC8058 core driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:pmic8058-core");
diff --git a/drivers/mfd/pmic8901.c b/drivers/mfd/pmic8901.c
new file mode 100644
index 0000000..955258c
--- /dev/null
+++ b/drivers/mfd/pmic8901.c
@@ -0,0 +1,382 @@
+/* Copyright (c) 2010-2012, 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.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/mfd/core.h>
+#include <linux/msm_ssbi.h>
+#include <linux/mfd/pmic8901.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/module.h>
+
+/* PMIC8901 Revision */
+#define PM8901_REG_REV			0x002
+#define PM8901_VERSION_MASK		0xF0
+#define PM8901_REVISION_MASK		0x0F
+#define PM8901_VERSION_VALUE		0xF0
+
+#define REG_IRQ_BASE			0xD5
+#define REG_MPP_BASE			0x27
+
+#define REG_TEMP_ALRM_CTRL		0x23
+#define REG_TEMP_ALRM_PWM		0x24
+
+#define SINGLE_IRQ_RESOURCE(_name, _irq) \
+{ \
+	.name	= _name, \
+	.start	= _irq, \
+	.end	= _irq, \
+	.flags	= IORESOURCE_IRQ, \
+}
+
+struct pm8901_chip {
+	struct pm8901_platform_data	pdata;
+	struct device			*dev;
+	struct pm_irq_chip		*irq_chip;
+	struct mfd_cell			*mfd_regulators;
+	u8				revision;
+};
+
+static int pm8901_readb(const struct device *dev, u16 addr, u8 *val)
+{
+	const struct pm8xxx_drvdata *pm8901_drvdata = dev_get_drvdata(dev);
+	const struct pm8901_chip *pmic = pm8901_drvdata->pm_chip_data;
+
+	return msm_ssbi_read(pmic->dev->parent, addr, val, 1);
+}
+
+static int pm8901_writeb(const struct device *dev, u16 addr, u8 val)
+{
+	const struct pm8xxx_drvdata *pm8901_drvdata = dev_get_drvdata(dev);
+	const struct pm8901_chip *pmic = pm8901_drvdata->pm_chip_data;
+
+	return msm_ssbi_write(pmic->dev->parent, addr, &val, 1);
+}
+
+static int pm8901_read_buf(const struct device *dev, u16 addr, u8 *buf,
+								int cnt)
+{
+	const struct pm8xxx_drvdata *pm8901_drvdata = dev_get_drvdata(dev);
+	const struct pm8901_chip *pmic = pm8901_drvdata->pm_chip_data;
+
+	return msm_ssbi_read(pmic->dev->parent, addr, buf, cnt);
+}
+
+static int pm8901_write_buf(const struct device *dev, u16 addr, u8 *buf,
+								int cnt)
+{
+	const struct pm8xxx_drvdata *pm8901_drvdata = dev_get_drvdata(dev);
+	const struct pm8901_chip *pmic = pm8901_drvdata->pm_chip_data;
+
+	return msm_ssbi_write(pmic->dev->parent, addr, buf, cnt);
+}
+
+static int pm8901_read_irq_stat(const struct device *dev, int irq)
+{
+	const struct pm8xxx_drvdata *pm8901_drvdata = dev_get_drvdata(dev);
+	const struct pm8901_chip *pmic = pm8901_drvdata->pm_chip_data;
+
+	return pm8xxx_get_irq_stat(pmic->irq_chip, irq);
+
+	return 0;
+}
+
+static enum pm8xxx_version pm8901_get_version(const struct device *dev)
+{
+	const struct pm8xxx_drvdata *pm8901_drvdata = dev_get_drvdata(dev);
+	const struct pm8901_chip *pmic = pm8901_drvdata->pm_chip_data;
+	enum pm8xxx_version version = -ENODEV;
+
+	if ((pmic->revision & PM8901_VERSION_MASK) == PM8901_VERSION_VALUE)
+		version = PM8XXX_VERSION_8901;
+
+	return version;
+}
+
+static int pm8901_get_revision(const struct device *dev)
+{
+	const struct pm8xxx_drvdata *pm8901_drvdata = dev_get_drvdata(dev);
+	const struct pm8901_chip *pmic = pm8901_drvdata->pm_chip_data;
+
+	return pmic->revision & PM8901_REVISION_MASK;
+}
+
+static struct pm8xxx_drvdata pm8901_drvdata = {
+	.pmic_readb		= pm8901_readb,
+	.pmic_writeb		= pm8901_writeb,
+	.pmic_read_buf		= pm8901_read_buf,
+	.pmic_write_buf		= pm8901_write_buf,
+	.pmic_read_irq_stat	= pm8901_read_irq_stat,
+	.pmic_get_version	= pm8901_get_version,
+	.pmic_get_revision	= pm8901_get_revision,
+};
+
+static struct mfd_cell misc_cell = {
+	.name		= PM8XXX_MISC_DEV_NAME,
+	.id		= 1,
+};
+
+static struct mfd_cell debugfs_cell = {
+	.name		= "pm8xxx-debug",
+	.id		= 1,
+	.platform_data	= "pm8901-dbg",
+	.pdata_size	= sizeof("pm8901-dbg"),
+};
+
+static const struct resource thermal_alarm_cell_resources[] = {
+	SINGLE_IRQ_RESOURCE("pm8901_tempstat_irq", PM8901_TEMPSTAT_IRQ),
+	SINGLE_IRQ_RESOURCE("pm8901_overtemp_irq", PM8901_OVERTEMP_IRQ),
+};
+
+static struct pm8xxx_tm_core_data thermal_alarm_cdata = {
+	.adc_type			= PM8XXX_TM_ADC_NONE,
+	.reg_addr_temp_alarm_ctrl	= REG_TEMP_ALRM_CTRL,
+	.reg_addr_temp_alarm_pwm	= REG_TEMP_ALRM_PWM,
+	.tm_name			= "pm8901_tz",
+	.irq_name_temp_stat		= "pm8901_tempstat_irq",
+	.irq_name_over_temp		= "pm8901_overtemp_irq",
+};
+
+static struct mfd_cell thermal_alarm_cell = {
+	.name		= PM8XXX_TM_DEV_NAME,
+	.id		= 1,
+	.resources	= thermal_alarm_cell_resources,
+	.num_resources	= ARRAY_SIZE(thermal_alarm_cell_resources),
+	.platform_data	= &thermal_alarm_cdata,
+	.pdata_size	= sizeof(struct pm8xxx_tm_core_data),
+};
+
+static const struct resource mpp_cell_resources[] = {
+	{
+		.start	= PM8901_IRQ_BLOCK_BIT(PM8901_MPP_BLOCK_START, 0),
+		.end	= PM8901_IRQ_BLOCK_BIT(PM8901_MPP_BLOCK_START, 0)
+			  + PM8901_MPPS - 1,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct mfd_cell mpp_cell = {
+	.name		= PM8XXX_MPP_DEV_NAME,
+	.id		= 1,
+	.resources	= mpp_cell_resources,
+	.num_resources	= ARRAY_SIZE(mpp_cell_resources),
+};
+
+static int __devinit
+pm8901_add_subdevices(const struct pm8901_platform_data *pdata,
+				struct pm8901_chip *pmic)
+{
+	int rc = 0, irq_base = 0, i;
+	struct pm_irq_chip *irq_chip;
+	static struct mfd_cell *mfd_regulators;
+
+	if (pdata->irq_pdata) {
+		pdata->irq_pdata->irq_cdata.nirqs = PM8901_NR_IRQS;
+		pdata->irq_pdata->irq_cdata.base_addr = REG_IRQ_BASE;
+		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;
+	}
+
+	if (pdata->mpp_pdata) {
+		pdata->mpp_pdata->core_data.nmpps = PM8901_MPPS;
+		pdata->mpp_pdata->core_data.base_addr = REG_MPP_BASE;
+		mpp_cell.platform_data = pdata->mpp_pdata;
+		mpp_cell.pdata_size = sizeof(struct pm8xxx_mpp_platform_data);
+		rc = mfd_add_devices(pmic->dev, 0, &mpp_cell, 1, NULL,
+					irq_base);
+		if (rc) {
+			pr_err("Failed to add mpp subdevice ret=%d\n", rc);
+			goto bail;
+		}
+	}
+
+	if (pdata->num_regulators > 0 && pdata->regulator_pdatas) {
+		mfd_regulators = kzalloc(sizeof(struct mfd_cell)
+					 * (pdata->num_regulators), GFP_KERNEL);
+		if (!mfd_regulators) {
+			pr_err("Cannot allocate %d bytes for pm8901 regulator "
+				"mfd cells\n", sizeof(struct mfd_cell)
+						* (pdata->num_regulators));
+			rc = -ENOMEM;
+			goto bail;
+		}
+		for (i = 0; i < pdata->num_regulators; i++) {
+			mfd_regulators[i].name = "pm8901-regulator";
+			mfd_regulators[i].id = pdata->regulator_pdatas[i].id;
+			mfd_regulators[i].platform_data =
+				&(pdata->regulator_pdatas[i]);
+			mfd_regulators[i].pdata_size =
+					sizeof(struct pm8901_vreg_pdata);
+		}
+		rc = mfd_add_devices(pmic->dev, 0, mfd_regulators,
+				pdata->num_regulators, NULL, irq_base);
+		if (rc) {
+			pr_err("Failed to add regulator subdevices ret=%d\n",
+				rc);
+			kfree(mfd_regulators);
+			goto bail;
+		}
+		pmic->mfd_regulators = mfd_regulators;
+	}
+
+	rc = mfd_add_devices(pmic->dev, 0, &thermal_alarm_cell, 1, NULL,
+							irq_base);
+	if (rc) {
+		pr_err("Failed to add thermal alarm subdevice ret=%d\n",
+			rc);
+		goto bail;
+	}
+
+	rc = mfd_add_devices(pmic->dev, 0, &debugfs_cell, 1, NULL, irq_base);
+	if (rc) {
+		pr_err("Failed to add debugfs subdevice ret=%d\n", rc);
+		goto bail;
+	}
+
+	if (pdata->misc_pdata) {
+		misc_cell.platform_data = pdata->misc_pdata;
+		misc_cell.pdata_size = sizeof(struct pm8xxx_misc_platform_data);
+		rc = mfd_add_devices(pmic->dev, 0, &misc_cell, 1, NULL,
+				      irq_base);
+		if (rc) {
+			pr_err("Failed to add  misc subdevice ret=%d\n", rc);
+			goto bail;
+		}
+	}
+
+	return rc;
+
+bail:
+	if (pmic->irq_chip) {
+		pm8xxx_irq_exit(pmic->irq_chip);
+		pmic->irq_chip = NULL;
+	}
+	return rc;
+}
+
+static const char * const pm8901_rev_names[] = {
+	[PM8XXX_REVISION_8901_TEST]	= "test",
+	[PM8XXX_REVISION_8901_1p0]	= "1.0",
+	[PM8XXX_REVISION_8901_1p1]	= "1.1",
+	[PM8XXX_REVISION_8901_2p0]	= "2.0",
+	[PM8XXX_REVISION_8901_2p1]	= "2.1",
+	[PM8XXX_REVISION_8901_2p2]	= "2.2",
+	[PM8XXX_REVISION_8901_2p3]	= "2.3",
+};
+
+static int __devinit pm8901_probe(struct platform_device *pdev)
+{
+	int rc;
+	struct pm8901_platform_data *pdata = pdev->dev.platform_data;
+	const char *revision_name = "unknown";
+	struct pm8901_chip *pmic;
+	int revision;
+
+	if (pdata == NULL) {
+		pr_err("%s: No platform_data or IRQ.\n", __func__);
+		return -ENODEV;
+	}
+
+	pmic = kzalloc(sizeof *pmic, GFP_KERNEL);
+	if (pmic == NULL) {
+		pr_err("%s: kzalloc() failed.\n", __func__);
+		return -ENOMEM;
+	}
+
+	pmic->dev = &pdev->dev;
+
+	pm8901_drvdata.pm_chip_data = pmic;
+	platform_set_drvdata(pdev, &pm8901_drvdata);
+
+	/* Read PMIC chip revision */
+	rc = pm8901_readb(pmic->dev, PM8901_REG_REV, &pmic->revision);
+	if (rc)
+		pr_err("%s: Failed reading version register rc=%d.\n",
+			__func__, rc);
+
+	pr_info("%s: PMIC revision reg: %02X\n", __func__, pmic->revision);
+	revision =  pm8xxx_get_revision(pmic->dev);
+	if (revision >= 0 && revision < ARRAY_SIZE(pm8901_rev_names))
+		revision_name = pm8901_rev_names[revision];
+	pr_info("%s: PMIC version: PM8901 rev %s\n", __func__, revision_name);
+
+	(void) memcpy((void *)&pmic->pdata, (const void *)pdata,
+		      sizeof(pmic->pdata));
+
+	rc = pm8901_add_subdevices(pdata, pmic);
+	if (rc) {
+		pr_err("Cannot add subdevices rc=%d\n", rc);
+		goto err;
+	}
+
+	return 0;
+
+err:
+	platform_set_drvdata(pdev, NULL);
+	kfree(pmic);
+	return rc;
+}
+
+static int __devexit pm8901_remove(struct platform_device *pdev)
+{
+	struct pm8xxx_drvdata *drvdata;
+	struct pm8901_chip *pmic = NULL;
+
+	drvdata = platform_get_drvdata(pdev);
+	if (drvdata)
+		pmic = drvdata->pm_chip_data;
+	if (pmic) {
+		if (pmic->dev)
+			mfd_remove_devices(pmic->dev);
+		if (pmic->irq_chip)
+			pm8xxx_irq_exit(pmic->irq_chip);
+		kfree(pmic->mfd_regulators);
+		kfree(pmic);
+	}
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+static struct platform_driver pm8901_driver = {
+	.probe		= pm8901_probe,
+	.remove		= __devexit_p(pm8901_remove),
+	.driver		= {
+		.name	= "pm8901-core",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init pm8901_init(void)
+{
+	return  platform_driver_register(&pm8901_driver);
+}
+postcore_initcall(pm8901_init);
+
+static void __exit pm8901_exit(void)
+{
+	platform_driver_unregister(&pm8901_driver);
+}
+module_exit(pm8901_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC8901 core driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:pmic8901-core");
diff --git a/drivers/mfd/timpani-codec.c b/drivers/mfd/timpani-codec.c
new file mode 100644
index 0000000..1e0a839
--- /dev/null
+++ b/drivers/mfd/timpani-codec.c
@@ -0,0 +1,3661 @@
+/* Copyright (c) 2010-2012, 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.
+ *
+ */
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/msm-adie-codec.h>
+#include <linux/mfd/marimba.h>
+#include <linux/mfd/timpani-audio.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <linux/string.h>
+
+/* Timpani codec driver is activated through Marimba core driver */
+
+#define MAX_MDELAY_US 20000
+
+#define TIMPANI_PATH_MASK(x) (1 << (x))
+
+#define TIMPANI_CODEC_AUXPGA_GAIN_RANGE (0x0F)
+
+#define TIMPANI_RX1_ST_MASK (TIMPANI_CDC_RX1_CTL_SIDETONE_EN1_L_M |\
+		TIMPANI_CDC_RX1_CTL_SIDETONE_EN1_R_M)
+#define TIMPANI_RX1_ST_ENABLE ((1 << TIMPANI_CDC_RX1_CTL_SIDETONE_EN1_L_S) |\
+		(1 << TIMPANI_CDC_RX1_CTL_SIDETONE_EN1_R_S))
+#define TIMPANI_CDC_ST_MIXING_TX1_MASK (TIMPANI_CDC_ST_MIXING_TX1_L_M |\
+		TIMPANI_CDC_ST_MIXING_TX1_R_M)
+#define TIMPANI_CDC_ST_MIXING_TX1_ENABLE ((1 << TIMPANI_CDC_ST_MIXING_TX1_L_S)\
+		| (1 << TIMPANI_CDC_ST_MIXING_TX1_R_S))
+#define TIMPANI_CDC_ST_MIXING_TX2_MASK (TIMPANI_CDC_ST_MIXING_TX2_L_M |\
+		TIMPANI_CDC_ST_MIXING_TX2_R_M)
+#define TIMPANI_CDC_ST_MIXING_TX2_ENABLE ((1 << TIMPANI_CDC_ST_MIXING_TX2_L_S)\
+		| (1 << TIMPANI_CDC_ST_MIXING_TX2_R_S))
+
+enum refcnt {
+	DEC = 0,
+	INC = 1,
+	IGNORE = 2,
+};
+#define TIMPANI_ARRAY_SIZE	(TIMPANI_A_CDC_COMP_HALT + 1)
+#define MAX_SHADOW_RIGISTERS	TIMPANI_A_CDC_COMP_HALT
+
+static u8 timpani_shadow[TIMPANI_ARRAY_SIZE];
+
+struct adie_codec_path {
+	struct adie_codec_dev_profile *profile;
+	struct adie_codec_register_image img;
+	u32 hwsetting_idx;
+	u32 stage_idx;
+	u32 curr_stage;
+	u32 reg_owner;
+};
+
+enum /* regaccess blk id */
+{
+	RA_BLOCK_RX1 = 0,
+	RA_BLOCK_RX2,
+	RA_BLOCK_TX1,
+	RA_BLOCK_TX2,
+	RA_BLOCK_LB,
+	RA_BLOCK_SHARED_RX_LB,
+	RA_BLOCK_SHARED_TX,
+	RA_BLOCK_TXFE1,
+	RA_BLOCK_TXFE2,
+	RA_BLOCK_PA_COMMON,
+	RA_BLOCK_PA_EAR,
+	RA_BLOCK_PA_HPH,
+	RA_BLOCK_PA_LINE,
+	RA_BLOCK_PA_AUX,
+	RA_BLOCK_ADC,
+	RA_BLOCK_DMIC,
+	RA_BLOCK_TX_I2S,
+	RA_BLOCK_DRV,
+	RA_BLOCK_TEST,
+	RA_BLOCK_RESERVED,
+	RA_BLOCK_NUM,
+};
+
+enum /* regaccess onwer ID */
+{
+	RA_OWNER_NONE = 0,
+	RA_OWNER_PATH_RX1,
+	RA_OWNER_PATH_RX2,
+	RA_OWNER_PATH_TX1,
+	RA_OWNER_PATH_TX2,
+	RA_OWNER_PATH_LB,
+	RA_OWNER_DRV,
+	RA_OWNER_NUM,
+};
+
+struct reg_acc_blk_cfg {
+	u8 valid_owners[RA_OWNER_NUM];
+};
+
+struct reg_ref_cnt {
+	u8 mask;
+	u8 path_mask;
+};
+
+#define TIMPANI_MAX_FIELDS	5
+
+struct timpani_regaccess {
+	u8 reg_addr;
+	u8 blk_mask[RA_BLOCK_NUM];
+	u8 reg_mask;
+	u8 reg_default;
+	struct reg_ref_cnt fld_ref_cnt[TIMPANI_MAX_FIELDS];
+};
+
+struct timpani_regaccess timpani_regset[] = {
+	{
+		TIMPANI_A_MREF,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xFC, 0x0, 0x3},
+		TIMPANI_MREF_M,
+		TIMPANI_MREF_POR,
+		{
+			{ .mask = 0xFC, .path_mask = 0},
+			{ .mask = 0x03, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDAC_IDAC_REF_CUR,
+		{0xFC, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDAC_IDAC_REF_CUR_M,
+		TIMPANI_CDAC_IDAC_REF_CUR_POR,
+		{
+			{ .mask = 0xFC, .path_mask = 0},
+			{ .mask = 0x03, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_TXADC12_REF_CURR,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF},
+		TIMPANI_TXADC12_REF_CURR_M,
+		TIMPANI_TXADC12_REF_CURR_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_TXADC3_EN,
+		{ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFE, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1},
+		TIMPANI_TXADC3_EN_M,
+		TIMPANI_TXADC3_EN_POR,
+		{
+			{ .mask = 0xFE, .path_mask = 0},
+			{ .mask = 0x01, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_TXADC4_EN,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFE, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1},
+		TIMPANI_TXADC4_EN_M,
+		TIMPANI_TXADC4_EN_POR,
+		{
+			{ .mask = 0xFE, .path_mask = 0},
+			{ .mask = 0x01, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CODEC_TXADC_STATUS_REGISTER_1,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC0, 0x30, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF},
+		TIMPANI_CODEC_TXADC_STATUS_REGISTER_1_M,
+		TIMPANI_CODEC_TXADC_STATUS_REGISTER_1_POR,
+		{
+			{ .mask = 0xC0, .path_mask = 0},
+			{ .mask = 0x30, .path_mask = 0},
+			{ .mask = 0x0F, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_TXFE1,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_TXFE1_M,
+		TIMPANI_TXFE1_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_TXFE2,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_TXFE2_M,
+		TIMPANI_TXFE2_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_TXFE12_ATEST,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_TXFE12_ATEST_M,
+		TIMPANI_TXFE12_ATEST_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_TXFE_CLT,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF8, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7},
+		TIMPANI_TXFE_CLT_M,
+		TIMPANI_TXFE_CLT_POR,
+		{
+			{ .mask = 0xF8, .path_mask = 0},
+			{ .mask = 0x07, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_TXADC1_EN,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFE, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1},
+		TIMPANI_TXADC1_EN_M,
+		TIMPANI_TXADC1_EN_POR,
+		{
+			{ .mask = 0xFE, .path_mask = 0},
+			{ .mask = 0x01, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_TXADC2_EN,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFE, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1},
+		TIMPANI_TXADC2_EN_M,
+		TIMPANI_TXADC2_EN_POR,
+		{
+			{ .mask = 0xFE, .path_mask = 0},
+			{ .mask = 0x01, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_TXADC_CTL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_TXADC_CTL_M,
+		TIMPANI_TXADC_CTL_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_TXADC_CTL2,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_TXADC_CTL2_M,
+		TIMPANI_TXADC_CTL2_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_TXADC_CTL3,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0xFE, 0x0, 0x0, 0x0, 0x0, 0x1},
+		TIMPANI_TXADC_CTL3_M,
+		TIMPANI_TXADC_CTL3_POR,
+		{
+			{ .mask = 0xFE, .path_mask = 0},
+			{ .mask = 0x01, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_TXADC_CHOP_CTL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0xFC, 0x0, 0x0, 0x0, 0x0, 0x3},
+		TIMPANI_TXADC_CHOP_CTL_M,
+		TIMPANI_TXADC_CHOP_CTL_POR,
+		{
+			{ .mask = 0xFC, .path_mask = 0},
+			{ .mask = 0x03, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_TXFE3,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xE2, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1D},
+		TIMPANI_TXFE3_M,
+		TIMPANI_TXFE3_POR,
+		{
+			{ .mask = 0xE2, .path_mask = 0},
+			{ .mask = 0x1D, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_TXFE4,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xE2, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1D},
+		TIMPANI_TXFE4_M,
+		TIMPANI_TXFE4_POR,
+		{
+			{ .mask = 0xE2, .path_mask = 0},
+			{ .mask = 0x1D, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_TXFE3_ATEST,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_TXFE3_ATEST_M,
+		TIMPANI_TXFE3_ATEST_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_TXFE_DIFF_SE,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0xC, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF0},
+		TIMPANI_TXFE_DIFF_SE_M,
+		TIMPANI_TXFE_DIFF_SE_POR,
+		{
+			{ .mask = 0x03, .path_mask = 0},
+			{ .mask = 0x0C, .path_mask = 0},
+			{ .mask = 0xF0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDAC_RX_CLK_CTL,
+		{0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDAC_RX_CLK_CTL_M,
+		TIMPANI_CDAC_RX_CLK_CTL_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDAC_BUFF_CTL,
+		{0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDAC_BUFF_CTL_M,
+		TIMPANI_CDAC_BUFF_CTL_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDAC_REF_CTL1,
+		{0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDAC_REF_CTL1_M,
+		TIMPANI_CDAC_REF_CTL1_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_IDAC_DWA_FIR_CTL,
+		{0xF8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7},
+		TIMPANI_IDAC_DWA_FIR_CTL_M,
+		TIMPANI_IDAC_DWA_FIR_CTL_POR,
+		{
+			{ .mask = 0xF8, .path_mask = 0},
+			{ .mask = 0x07, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDAC_REF_CTL2,
+		{0x6F, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x90},
+		TIMPANI_CDAC_REF_CTL2_M,
+		TIMPANI_CDAC_REF_CTL2_POR,
+		{
+			{ .mask = 0x6F, .path_mask = 0},
+			{ .mask = 0x90, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDAC_CTL1,
+		{0x7F, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80},
+		TIMPANI_CDAC_CTL1_M,
+		TIMPANI_CDAC_CTL1_POR,
+		{
+			{ .mask = 0x7F, .path_mask = 0},
+			{ .mask = 0x80, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDAC_CTL2,
+		{0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDAC_CTL2_M,
+		TIMPANI_CDAC_CTL2_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_IDAC_L_CTL,
+		{0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_IDAC_L_CTL_M,
+		TIMPANI_IDAC_L_CTL_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_IDAC_R_CTL,
+		{0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_IDAC_R_CTL_M,
+		TIMPANI_IDAC_R_CTL_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_MASTER_BIAS,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1F,
+		0xE0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_PA_MASTER_BIAS_M,
+		TIMPANI_PA_MASTER_BIAS_POR,
+		{
+			{ .mask = 0x1F, .path_mask = 0},
+			{ .mask = 0xE0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_CLASSD_BIAS,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_PA_CLASSD_BIAS_M,
+		TIMPANI_PA_CLASSD_BIAS_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_AUXPGA_CUR,
+		{0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_AUXPGA_CUR_M,
+		TIMPANI_AUXPGA_CUR_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_AUXPGA_CM,
+		{0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_AUXPGA_CM_M,
+		TIMPANI_AUXPGA_CM_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_HPH_EARPA_MSTB_EN,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0xFC,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_PA_HPH_EARPA_MSTB_EN_M,
+		TIMPANI_PA_HPH_EARPA_MSTB_EN_POR,
+		{
+			{ .mask = 0x01, .path_mask = 0},
+			{ .mask = 0x02, .path_mask = 0},
+			{ .mask = 0xFC, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_LINE_AUXO_EN,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0xF8, 0x7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_PA_LINE_AUXO_EN_M,
+		TIMPANI_PA_LINE_AUXO_EN_POR,
+		{
+			{ .mask = 0xF8, .path_mask = 0},
+			{ .mask = 0x07, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_CLASSD_AUXPGA_EN,
+		{0x0, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC0},
+		TIMPANI_PA_CLASSD_AUXPGA_EN_M,
+		TIMPANI_PA_CLASSD_AUXPGA_EN_POR,
+		{
+			{ .mask = 0x30, .path_mask = 0},
+			{ .mask = 0x0F, .path_mask = 0},
+			{ .mask = 0xC0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_LINE_L_GAIN,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xFC, 0x0, 0x3},
+		TIMPANI_PA_LINE_L_GAIN_M,
+		TIMPANI_PA_LINE_L_GAIN_POR,
+		{
+			{ .mask = 0xFC, .path_mask = 0},
+			{ .mask = 0x03, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_LINE_R_GAIN,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xFC, 0x0, 0x3},
+		TIMPANI_PA_LINE_R_GAIN_M,
+		TIMPANI_PA_LINE_R_GAIN_POR,
+		{
+			{ .mask = 0xFC, .path_mask = 0},
+			{ .mask = 0x03, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_HPH_L_GAIN,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xFE, 0x0, 0x1},
+		TIMPANI_PA_HPH_L_GAIN_M,
+		TIMPANI_PA_HPH_L_GAIN_POR,
+		{
+			{ .mask = 0xFE, .path_mask = 0},
+			{ .mask = 0x01, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_HPH_R_GAIN,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xFE, 0x0, 0x1},
+		TIMPANI_PA_HPH_R_GAIN_M,
+		TIMPANI_PA_HPH_R_GAIN_POR,
+		{
+			{ .mask = 0xFE, .path_mask = 0},
+			{ .mask = 0x01, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_AUXPGA_LR_GAIN,
+		{0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_AUXPGA_LR_GAIN_M,
+		TIMPANI_AUXPGA_LR_GAIN_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_AUXO_EARPA_CONN,
+		{0x21, 0x42, 0x0, 0x0, 0x84, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18},
+		TIMPANI_PA_AUXO_EARPA_CONN_M,
+		TIMPANI_PA_AUXO_EARPA_CONN_POR,
+		{
+			{ .mask = 0x21, .path_mask = 0},
+			{ .mask = 0x42, .path_mask = 0},
+			{ .mask = 0x84, .path_mask = 0},
+			{ .mask = 0x18, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_LINE_ST_CONN,
+		{0x24, 0x48, 0x0, 0x0, 0x93, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_PA_LINE_ST_CONN_M,
+		TIMPANI_PA_LINE_ST_CONN_POR,
+		{
+			{ .mask = 0x24, .path_mask = 0},
+			{ .mask = 0x48, .path_mask = 0},
+			{ .mask = 0x93, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_LINE_MONO_CONN,
+		{0x24, 0x48, 0x0, 0x0, 0x93, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_PA_LINE_MONO_CONN_M,
+		TIMPANI_PA_LINE_MONO_CONN_POR,
+		{
+			{ .mask = 0x24, .path_mask = 0},
+			{ .mask = 0x48, .path_mask = 0},
+			{ .mask = 0x93, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_HPH_ST_CONN,
+		{0x24, 0x48, 0x0, 0x0, 0x90, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_PA_HPH_ST_CONN_M,
+		TIMPANI_PA_HPH_ST_CONN_POR,
+		{
+			{ .mask = 0x24, .path_mask = 0},
+			{ .mask = 0x48, .path_mask = 0},
+			{ .mask = 0x90, .path_mask = 0},
+			{ .mask = 0x03, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_HPH_MONO_CONN,
+		{0x24, 0x48, 0x0, 0x0, 0x90, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3},
+		TIMPANI_PA_HPH_MONO_CONN_M,
+		TIMPANI_PA_HPH_MONO_CONN_POR,
+		{
+			{ .mask = 0x24, .path_mask = 0},
+			{ .mask = 0x48, .path_mask = 0},
+			{ .mask = 0x90, .path_mask = 0},
+			{ .mask = 0x03, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_CLASSD_CONN,
+		{0x80, 0x40, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF},
+		TIMPANI_PA_CLASSD_CONN_M,
+		TIMPANI_PA_CLASSD_CONN_POR,
+		{
+			{ .mask = 0x80, .path_mask = 0},
+			{ .mask = 0x40, .path_mask = 0},
+			{ .mask = 0x20, .path_mask = 0},
+			{ .mask = 0x10, .path_mask = 0},
+			{ .mask = 0x0F, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_CNP_CTL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xCF,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x30},
+		TIMPANI_PA_CNP_CTL_M,
+		TIMPANI_PA_CNP_CTL_POR,
+		{
+			{ .mask = 0xCF, .path_mask = 0},
+			{ .mask = 0x30, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_CLASSD_L_CTL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3F,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC0},
+		TIMPANI_PA_CLASSD_L_CTL_M,
+		TIMPANI_PA_CLASSD_L_CTL_POR,
+		{
+			{ .mask = 0x3F, .path_mask = 0},
+			{ .mask = 0xC0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_CLASSD_R_CTL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3F,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC0},
+		TIMPANI_PA_CLASSD_R_CTL_M,
+		TIMPANI_PA_CLASSD_R_CTL_POR,
+		{
+			{ .mask = 0x3F, .path_mask = 0},
+			{ .mask = 0xC0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_CLASSD_INT2_CTL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_PA_CLASSD_INT2_CTL_M,
+		TIMPANI_PA_CLASSD_INT2_CTL_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_HPH_L_OCP_CLK_CTL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_PA_HPH_L_OCP_CLK_CTL_M,
+		TIMPANI_PA_HPH_L_OCP_CLK_CTL_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_CLASSD_L_SW_CTL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF7,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8},
+		TIMPANI_PA_CLASSD_L_SW_CTL_M,
+		TIMPANI_PA_CLASSD_L_SW_CTL_POR,
+		{
+			{ .mask = 0xF7, .path_mask = 0},
+			{ .mask = 0x08, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_CLASSD_L_OCP1,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_PA_CLASSD_L_OCP1_M,
+		TIMPANI_PA_CLASSD_L_OCP1_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_CLASSD_L_OCP2,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_PA_CLASSD_L_OCP2_M,
+		TIMPANI_PA_CLASSD_L_OCP2_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_HPH_R_OCP_CLK_CTL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_PA_HPH_R_OCP_CLK_CTL_M,
+		TIMPANI_PA_HPH_R_OCP_CLK_CTL_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_CLASSD_R_SW_CTL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF7,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8},
+		TIMPANI_PA_CLASSD_R_SW_CTL_M,
+		TIMPANI_PA_CLASSD_R_SW_CTL_POR,
+		{
+			{ .mask = 0xF7, .path_mask = 0},
+			{ .mask = 0x08, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_CLASSD_R_OCP1,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_PA_CLASSD_R_OCP1_M,
+		TIMPANI_PA_CLASSD_R_OCP1_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_CLASSD_R_OCP2,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_PA_CLASSD_R_OCP2_M,
+		TIMPANI_PA_CLASSD_R_OCP2_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_HPH_CTL1,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF},
+		TIMPANI_PA_HPH_CTL1_M,
+		TIMPANI_PA_HPH_CTL1_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_HPH_CTL2,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFE,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1},
+		TIMPANI_PA_HPH_CTL2_M,
+		TIMPANI_PA_HPH_CTL2_POR,
+		{
+			{ .mask = 0xFE, .path_mask = 0},
+			{ .mask = 0x01, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_LINE_AUXO_CTL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0xC3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x3C, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_PA_LINE_AUXO_CTL_M,
+		TIMPANI_PA_LINE_AUXO_CTL_POR,
+		{
+			{ .mask = 0xC3, .path_mask = 0},
+			{ .mask = 0x3C, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_AUXO_EARPA_CTL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x0,
+		0x0, 0x38, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC0},
+		TIMPANI_PA_AUXO_EARPA_CTL_M,
+		TIMPANI_PA_AUXO_EARPA_CTL_POR,
+		{
+			{ .mask = 0x07, .path_mask = 0},
+			{ .mask = 0x38, .path_mask = 0},
+			{ .mask = 0xC0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_EARO_CTL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_PA_EARO_CTL_M,
+		TIMPANI_PA_EARO_CTL_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_MASTER_BIAS_CUR,
+		{0x0, 0x0, 0x0, 0x0, 0x60, 0x0, 0x0, 0x0, 0x0, 0x80, 0x0, 0x18,
+		0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1},
+		TIMPANI_PA_MASTER_BIAS_CUR_M,
+		TIMPANI_PA_MASTER_BIAS_CUR_POR,
+		{
+			{ .mask = 0x60, .path_mask = 0},
+			{ .mask = 0x80, .path_mask = 0},
+			{ .mask = 0x18, .path_mask = 0},
+			{ .mask = 0x06, .path_mask = 0},
+			{ .mask = 0x01, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_CLASSD_SC_STATUS,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xCC,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x33},
+		TIMPANI_PA_CLASSD_SC_STATUS_M,
+		TIMPANI_PA_CLASSD_SC_STATUS_POR,
+		{
+			{ .mask = 0xCC, .path_mask = 0},
+			{ .mask = 0x33, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_PA_HPH_SC_STATUS,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x88,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x77},
+		TIMPANI_PA_HPH_SC_STATUS_M,
+		TIMPANI_PA_HPH_SC_STATUS_POR,
+		{
+			{ .mask = 0x88, .path_mask = 0},
+			{ .mask = 0x77, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_ATEST_EN,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0x7F},
+		TIMPANI_ATEST_EN_M,
+		TIMPANI_ATEST_EN_POR,
+		{
+			{ .mask = 0x80, .path_mask = 0},
+			{ .mask = 0x7F, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_ATEST_TSHKADC,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0xF0},
+		TIMPANI_ATEST_TSHKADC_M,
+		TIMPANI_ATEST_TSHKADC_POR,
+		{
+			{ .mask = 0x0F, .path_mask = 0},
+			{ .mask = 0xF0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_ATEST_TXADC13,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7F, 0x80},
+		TIMPANI_ATEST_TXADC13_M,
+		TIMPANI_ATEST_TXADC13_POR,
+		{
+			{ .mask = 0x7F, .path_mask = 0},
+			{ .mask = 0x80, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_ATEST_TXADC24,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7F, 0x80},
+		TIMPANI_ATEST_TXADC24_M,
+		TIMPANI_ATEST_TXADC24_POR,
+		{
+			{ .mask = 0x7F, .path_mask = 0},
+			{ .mask = 0x80, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_ATEST_AUXPGA,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF8, 0x7},
+		TIMPANI_ATEST_AUXPGA_M,
+		TIMPANI_ATEST_AUXPGA_POR,
+		{
+			{ .mask = 0xF8, .path_mask = 0},
+			{ .mask = 0x07, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_ATEST_CDAC,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0},
+		TIMPANI_ATEST_CDAC_M,
+		TIMPANI_ATEST_CDAC_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_ATEST_IDAC,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0},
+		TIMPANI_ATEST_IDAC_M,
+		TIMPANI_ATEST_IDAC_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_ATEST_PA1,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0},
+		TIMPANI_ATEST_PA1_M,
+		TIMPANI_ATEST_PA1_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_ATEST_CLASSD,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0},
+		TIMPANI_ATEST_CLASSD_M,
+		TIMPANI_ATEST_CLASSD_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_ATEST_LINEO_AUXO,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0},
+		TIMPANI_ATEST_LINEO_AUXO_M,
+		TIMPANI_ATEST_LINEO_AUXO_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_RESET_CTL,
+		{0x2, 0x8, 0x5, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC0},
+		TIMPANI_CDC_RESET_CTL_M,
+		TIMPANI_CDC_RESET_CTL_POR,
+		{
+			{ .mask = 0x02, .path_mask = 0},
+			{ .mask = 0x08, .path_mask = 0},
+			{ .mask = 0x05, .path_mask = 0},
+			{ .mask = 0x30, .path_mask = 0},
+			{ .mask = 0xC0, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_RX1_CTL,
+		{0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_RX1_CTL_M,
+		TIMPANI_CDC_RX1_CTL_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_TX_I2S_CTL,
+		{0x0, 0x0, 0x10, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0xF, 0x0, 0x0, 0xC0},
+		TIMPANI_CDC_TX_I2S_CTL_M,
+		TIMPANI_CDC_TX_I2S_CTL_POR,
+		{
+			{ .mask = 0x10, .path_mask = 0},
+			{ .mask = 0x20, .path_mask = 0},
+			{ .mask = 0x0F, .path_mask = 0},
+			{ .mask = 0xC0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_CH_CTL,
+		{0x3, 0x30, 0xC, 0xC0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_CH_CTL_M,
+		TIMPANI_CDC_CH_CTL_POR,
+		{
+			{ .mask = 0x03, .path_mask = 0},
+			{ .mask = 0x30, .path_mask = 0},
+			{ .mask = 0x0C, .path_mask = 0},
+			{ .mask = 0xC0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_RX1LG,
+		{0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_RX1LG_M,
+		TIMPANI_CDC_RX1LG_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_RX1RG,
+		{0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_RX1RG_M,
+		TIMPANI_CDC_RX1RG_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_TX1LG,
+		{0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_TX1LG_M,
+		TIMPANI_CDC_TX1LG_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_TX1RG,
+		{0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_TX1RG_M,
+		TIMPANI_CDC_TX1RG_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_RX_PGA_TIMER,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_RX_PGA_TIMER_M,
+		TIMPANI_CDC_RX_PGA_TIMER_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_TX_PGA_TIMER,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_TX_PGA_TIMER_M,
+		TIMPANI_CDC_TX_PGA_TIMER_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_GCTL1,
+		{0xF, 0x0, 0xF0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_GCTL1_M,
+		TIMPANI_CDC_GCTL1_POR,
+		{
+			{ .mask = 0x0F, .path_mask = 0},
+			{ .mask = 0xF0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_TX1L_STG,
+		{0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_TX1L_STG_M,
+		TIMPANI_CDC_TX1L_STG_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ST_CTL,
+		{0x0, 0xF, 0x0, 0xF0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_ST_CTL_M,
+		TIMPANI_CDC_ST_CTL_POR,
+		{
+			{ .mask = 0x0F, .path_mask = 0},
+			{ .mask = 0xF0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_RX1L_DCOFFSET,
+		{0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_RX1L_DCOFFSET_M,
+		TIMPANI_CDC_RX1L_DCOFFSET_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_RX1R_DCOFFSET,
+		{0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_RX1R_DCOFFSET_M,
+		TIMPANI_CDC_RX1R_DCOFFSET_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_BYPASS_CTL1,
+		{0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF0},
+		TIMPANI_CDC_BYPASS_CTL1_M,
+		TIMPANI_CDC_BYPASS_CTL1_POR,
+		{
+			{ .mask = 0x0F, .path_mask = 0},
+			{ .mask = 0xF0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_PDM_CONFIG,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0xF0},
+		TIMPANI_CDC_PDM_CONFIG_M,
+		TIMPANI_CDC_PDM_CONFIG_POR,
+		{
+			{ .mask = 0x0F, .path_mask = 0},
+			{ .mask = 0xF0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_TESTMODE1,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3F, 0xC0},
+		TIMPANI_CDC_TESTMODE1_M,
+		TIMPANI_CDC_TESTMODE1_POR,
+		{
+			{ .mask = 0x3F, .path_mask = 0},
+			{ .mask = 0xC0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_DMIC_CLK_CTL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x3F, 0x0, 0x0, 0x0, 0xC0},
+		TIMPANI_CDC_DMIC_CLK_CTL_M,
+		TIMPANI_CDC_DMIC_CLK_CTL_POR,
+		{
+			{ .mask = 0x3F, .path_mask = 0},
+			{ .mask = 0xC0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ADC12_CLK_CTL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_ADC12_CLK_CTL_M,
+		TIMPANI_CDC_ADC12_CLK_CTL_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_TX1_CTL,
+		{0x0, 0x0, 0x3F, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC0},
+		TIMPANI_CDC_TX1_CTL_M,
+		TIMPANI_CDC_TX1_CTL_POR,
+		{
+			{ .mask = 0x3F, .path_mask = 0},
+			{ .mask = 0xC0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ADC34_CLK_CTL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_ADC34_CLK_CTL_M,
+		TIMPANI_CDC_ADC34_CLK_CTL_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_TX2_CTL,
+		{0x0, 0x0, 0x0, 0x3F, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC0},
+		TIMPANI_CDC_TX2_CTL_M,
+		TIMPANI_CDC_TX2_CTL_POR,
+		{
+			{ .mask = 0x3F, .path_mask = 0},
+			{ .mask = 0xC0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_RX1_CLK_CTL,
+		{0x1F, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xE0},
+		TIMPANI_CDC_RX1_CLK_CTL_M,
+		TIMPANI_CDC_RX1_CLK_CTL_POR,
+		{
+			{ .mask = 0x1F, .path_mask = 0},
+			{ .mask = 0xE0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_RX2_CLK_CTL,
+		{0x1F, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xE0},
+		TIMPANI_CDC_RX2_CLK_CTL_M,
+		TIMPANI_CDC_RX2_CLK_CTL_POR,
+		{
+			{ .mask = 0x1F, .path_mask = 0},
+			{ .mask = 0xE0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_DEC_ADC_SEL,
+		{0x0, 0x0, 0xF, 0xF0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_DEC_ADC_SEL_M,
+		TIMPANI_CDC_DEC_ADC_SEL_POR,
+		{
+			{ .mask = 0x0F, .path_mask = 0},
+			{ .mask = 0xF0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC_INPUT_MUX,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x3F, 0x0, 0xC0},
+		TIMPANI_CDC_ANC_INPUT_MUX_M,
+		TIMPANI_CDC_ANC_INPUT_MUX_POR,
+		{
+			{ .mask = 0x3F, .path_mask = 0},
+			{ .mask = 0xC0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC_RX_CLK_NS_SEL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0xFE},
+		TIMPANI_CDC_ANC_RX_CLK_NS_SEL_M,
+		TIMPANI_CDC_ANC_RX_CLK_NS_SEL_POR,
+		{
+			{ .mask = 0x01, .path_mask = 0},
+			{ .mask = 0xFE, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC_FB_TUNE_SEL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF},
+		TIMPANI_CDC_ANC_FB_TUNE_SEL_M,
+		TIMPANI_CDC_ANC_FB_TUNE_SEL_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CLK_DIV_SYNC_CTL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0xFC},
+		TIMPANI_CLK_DIV_SYNC_CTL_M,
+		TIMPANI_CLK_DIV_SYNC_CTL_POR,
+		{
+			{ .mask = 0x03, .path_mask = 0},
+			{ .mask = 0xFC, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ADC_CLK_EN,
+		{0x0, 0x0, 0x3, 0xC, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF0},
+		TIMPANI_CDC_ADC_CLK_EN_M,
+		TIMPANI_CDC_ADC_CLK_EN_POR,
+		{
+			{ .mask = 0x03, .path_mask = 0},
+			{ .mask = 0x0C, .path_mask = 0},
+			{ .mask = 0xF0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ST_MIXING,
+		{0x0, 0x0, 0x3, 0xC, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF0},
+		TIMPANI_CDC_ST_MIXING_M,
+		TIMPANI_CDC_ST_MIXING_POR,
+		{
+			{ .mask = 0x03, .path_mask = 0},
+			{ .mask = 0x0C, .path_mask = 0},
+			{ .mask = 0xF0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_RX2_CTL,
+		{0x0, 0x7F, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80},
+		TIMPANI_CDC_RX2_CTL_M,
+		TIMPANI_CDC_RX2_CTL_POR,
+		{
+			{ .mask = 0x7F, .path_mask = 0},
+			{ .mask = 0x80, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ARB_CLK_EN,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF},
+		TIMPANI_CDC_ARB_CLK_EN_M,
+		TIMPANI_CDC_ARB_CLK_EN_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_I2S_CTL2,
+		{0x2, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x39, 0x0, 0x0, 0xC0},
+		TIMPANI_CDC_I2S_CTL2_M,
+		TIMPANI_CDC_I2S_CTL2_POR,
+		{
+			{ .mask = 0x02, .path_mask = 0},
+			{ .mask = 0x04, .path_mask = 0},
+			{ .mask = 0x39, .path_mask = 0},
+			{ .mask = 0xC0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_RX2LG,
+		{0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_RX2LG_M,
+		TIMPANI_CDC_RX2LG_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_RX2RG,
+		{0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_RX2RG_M,
+		TIMPANI_CDC_RX2RG_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_TX2LG,
+		{0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_TX2LG_M,
+		TIMPANI_CDC_TX2LG_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_TX2RG,
+		{0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_TX2RG_M,
+		TIMPANI_CDC_TX2RG_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_DMIC_MUX,
+		{0x0, 0x0, 0xF, 0xF0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_DMIC_MUX_M,
+		TIMPANI_CDC_DMIC_MUX_POR,
+		{
+			{ .mask = 0x0F, .path_mask = 0},
+			{ .mask = 0xF0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ARB_CLK_CTL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0xFC},
+		TIMPANI_CDC_ARB_CLK_CTL_M,
+		TIMPANI_CDC_ARB_CLK_CTL_POR,
+		{
+			{ .mask = 0x03, .path_mask = 0},
+			{ .mask = 0xFC, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_GCTL2,
+		{0x0, 0xF, 0x0, 0xF0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_GCTL2_M,
+		TIMPANI_CDC_GCTL2_POR,
+		{
+			{ .mask = 0x0F, .path_mask = 0},
+			{ .mask = 0xF0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_BYPASS_CTL2,
+		{0x0, 0x0, 0x3F, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC0},
+		TIMPANI_CDC_BYPASS_CTL2_M,
+		TIMPANI_CDC_BYPASS_CTL2_POR,
+		{
+			{ .mask = 0x3F, .path_mask = 0},
+			{ .mask = 0xC0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_BYPASS_CTL3,
+		{0x0, 0x0, 0x0, 0x3F, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC0},
+		TIMPANI_CDC_BYPASS_CTL3_M,
+		TIMPANI_CDC_BYPASS_CTL3_POR,
+		{
+			{ .mask = 0x3F, .path_mask = 0},
+			{ .mask = 0xC0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_BYPASS_CTL4,
+		{0x0, 0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF0},
+		TIMPANI_CDC_BYPASS_CTL4_M,
+		TIMPANI_CDC_BYPASS_CTL4_POR,
+		{
+			{ .mask = 0x0F, .path_mask = 0},
+			{ .mask = 0xF0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_RX2L_DCOFFSET,
+		{0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_RX2L_DCOFFSET_M,
+		TIMPANI_CDC_RX2L_DCOFFSET_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_RX2R_DCOFFSET,
+		{0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_RX2R_DCOFFSET_M,
+		TIMPANI_CDC_RX2R_DCOFFSET_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_RX_MIX_CTL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0xFC},
+		TIMPANI_CDC_RX_MIX_CTL_M,
+		TIMPANI_CDC_RX_MIX_CTL_POR,
+		{
+			{ .mask = 0x03, .path_mask = 0},
+			{ .mask = 0xFC, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_SPARE_CTL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xFE},
+		TIMPANI_CDC_SPARE_CTL_M,
+		TIMPANI_CDC_SPARE_CTL_POR,
+		{
+			{ .mask = 0x01, .path_mask = 0},
+			{ .mask = 0xFE, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_TESTMODE2,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1F, 0xE0},
+		TIMPANI_CDC_TESTMODE2_M,
+		TIMPANI_CDC_TESTMODE2_POR,
+		{
+			{ .mask = 0x1F, .path_mask = 0},
+			{ .mask = 0xE0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_PDM_OE,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0},
+		TIMPANI_CDC_PDM_OE_M,
+		TIMPANI_CDC_PDM_OE_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_TX1R_STG,
+		{0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_TX1R_STG_M,
+		TIMPANI_CDC_TX1R_STG_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_TX2L_STG,
+		{0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_TX2L_STG_M,
+		TIMPANI_CDC_TX2L_STG_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_TX2R_STG,
+		{0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+		TIMPANI_CDC_TX2R_STG_M,
+		TIMPANI_CDC_TX2R_STG_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ARB_BYPASS_CTL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF},
+		TIMPANI_CDC_ARB_BYPASS_CTL_M,
+		TIMPANI_CDC_ARB_BYPASS_CTL_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC1_CTL1,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x1F, 0x0, 0xE0},
+		TIMPANI_CDC_ANC1_CTL1_M,
+		TIMPANI_CDC_ANC1_CTL1_POR,
+		{
+			{ .mask = 0x1F, .path_mask = 0},
+			{ .mask = 0xE0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC1_CTL2,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x3F, 0x0, 0xC0},
+		TIMPANI_CDC_ANC1_CTL2_M,
+		TIMPANI_CDC_ANC1_CTL2_POR,
+		{
+			{ .mask = 0x3F, .path_mask = 0},
+			{ .mask = 0xC0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC1_FF_FB_SHIFT,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0},
+		TIMPANI_CDC_ANC1_FF_FB_SHIFT_M,
+		TIMPANI_CDC_ANC1_FF_FB_SHIFT_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC1_RX_NS,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x0, 0xF8},
+		TIMPANI_CDC_ANC1_RX_NS_M,
+		TIMPANI_CDC_ANC1_RX_NS_POR,
+		{
+			{ .mask = 0x07, .path_mask = 0},
+			{ .mask = 0xF8, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC1_SPARE,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0},
+		TIMPANI_CDC_ANC1_SPARE_M,
+		TIMPANI_CDC_ANC1_SPARE_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC1_IIR_COEFF_PTR,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x1F, 0x0, 0xE0},
+		TIMPANI_CDC_ANC1_IIR_COEFF_PTR_M,
+		TIMPANI_CDC_ANC1_IIR_COEFF_PTR_POR,
+		{
+			{ .mask = 0x1F, .path_mask = 0},
+			{ .mask = 0xE0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC1_IIR_COEFF_MSB,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0xFE},
+		TIMPANI_CDC_ANC1_IIR_COEFF_MSB_M,
+		TIMPANI_CDC_ANC1_IIR_COEFF_MSB_POR,
+		{
+			{ .mask = 0x01, .path_mask = 0},
+			{ .mask = 0xFE, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC1_IIR_COEFF_LSB,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0},
+		TIMPANI_CDC_ANC1_IIR_COEFF_LSB_M,
+		TIMPANI_CDC_ANC1_IIR_COEFF_LSB_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC1_IIR_COEFF_CTL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0xFC},
+		TIMPANI_CDC_ANC1_IIR_COEFF_CTL_M,
+		TIMPANI_CDC_ANC1_IIR_COEFF_CTL_POR,
+		{
+			{ .mask = 0x03, .path_mask = 0},
+			{ .mask = 0xFC, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC1_LPF_COEFF_PTR,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0x0, 0xF0},
+		TIMPANI_CDC_ANC1_LPF_COEFF_PTR_M,
+		TIMPANI_CDC_ANC1_LPF_COEFF_PTR_POR,
+		{
+			{ .mask = 0x0F, .path_mask = 0},
+			{ .mask = 0xF0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC1_LPF_COEFF_MSB,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0x0, 0xF0},
+		TIMPANI_CDC_ANC1_LPF_COEFF_MSB_M,
+		TIMPANI_CDC_ANC1_LPF_COEFF_MSB_POR,
+		{
+			{ .mask = 0x0F, .path_mask = 0},
+			{ .mask = 0xF0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC1_LPF_COEFF_LSB,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0},
+		TIMPANI_CDC_ANC1_LPF_COEFF_LSB_M,
+		TIMPANI_CDC_ANC1_LPF_COEFF_LSB_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC1_SCALE_PTR,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0},
+		TIMPANI_CDC_ANC1_SCALE_PTR_M,
+		TIMPANI_CDC_ANC1_SCALE_PTR_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC1_SCALE,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0},
+		TIMPANI_CDC_ANC1_SCALE_M,
+		TIMPANI_CDC_ANC1_SCALE_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC1_DEBUG,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0x0, 0xF0},
+		TIMPANI_CDC_ANC1_DEBUG_M,
+		TIMPANI_CDC_ANC1_DEBUG_POR,
+		{
+			{ .mask = 0x0F, .path_mask = 0},
+			{ .mask = 0xF0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC2_CTL1,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x1F, 0x0, 0xE0},
+		TIMPANI_CDC_ANC2_CTL1_M,
+		TIMPANI_CDC_ANC2_CTL1_POR,
+		{
+			{ .mask = 0x1F, .path_mask = 0},
+			{ .mask = 0xE0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC2_CTL2,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x3F, 0x0, 0xC0},
+		TIMPANI_CDC_ANC2_CTL2_M,
+		TIMPANI_CDC_ANC2_CTL2_POR,
+		{
+			{ .mask = 0x3F, .path_mask = 0},
+			{ .mask = 0xC0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC2_FF_FB_SHIFT,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0},
+		TIMPANI_CDC_ANC2_FF_FB_SHIFT_M,
+		TIMPANI_CDC_ANC2_FF_FB_SHIFT_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC2_RX_NS,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x0, 0xF8},
+		TIMPANI_CDC_ANC2_RX_NS_M,
+		TIMPANI_CDC_ANC2_RX_NS_POR,
+		{
+			{ .mask = 0x07, .path_mask = 0},
+			{ .mask = 0xF8, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC2_SPARE,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0},
+		TIMPANI_CDC_ANC2_SPARE_M,
+		TIMPANI_CDC_ANC2_SPARE_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC2_IIR_COEFF_PTR,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0x0, 0xF0},
+		TIMPANI_CDC_ANC2_IIR_COEFF_PTR_M,
+		TIMPANI_CDC_ANC2_IIR_COEFF_PTR_POR,
+		{
+			{ .mask = 0x0F, .path_mask = 0},
+			{ .mask = 0xF0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC2_IIR_COEFF_MSB,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0xFE},
+		TIMPANI_CDC_ANC2_IIR_COEFF_MSB_M,
+		TIMPANI_CDC_ANC2_IIR_COEFF_MSB_POR,
+		{
+			{ .mask = 0x01, .path_mask = 0},
+			{ .mask = 0xFE, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC2_IIR_COEFF_LSB,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0},
+		TIMPANI_CDC_ANC2_IIR_COEFF_LSB_M,
+		TIMPANI_CDC_ANC2_IIR_COEFF_LSB_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC2_IIR_COEFF_CTL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0xFC},
+		TIMPANI_CDC_ANC2_IIR_COEFF_CTL_M,
+		TIMPANI_CDC_ANC2_IIR_COEFF_CTL_POR,
+		{
+			{ .mask = 0x03, .path_mask = 0},
+			{ .mask = 0xFC, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC2_LPF_COEFF_PTR,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0x0, 0xF0},
+		TIMPANI_CDC_ANC2_LPF_COEFF_PTR_M,
+		TIMPANI_CDC_ANC2_LPF_COEFF_PTR_POR,
+		{
+			{ .mask = 0x0F, .path_mask = 0},
+			{ .mask = 0xF0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC2_LPF_COEFF_MSB,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0x0, 0xF0},
+		TIMPANI_CDC_ANC2_LPF_COEFF_MSB_M,
+		TIMPANI_CDC_ANC2_LPF_COEFF_MSB_POR,
+		{
+			{ .mask = 0x0F, .path_mask = 0},
+			{ .mask = 0xF0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC2_LPF_COEFF_LSB,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0},
+		TIMPANI_CDC_ANC2_LPF_COEFF_LSB_M,
+		TIMPANI_CDC_ANC2_LPF_COEFF_LSB_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC2_SCALE_PTR,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0},
+		TIMPANI_CDC_ANC2_SCALE_PTR_M,
+		TIMPANI_CDC_ANC2_SCALE_PTR_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC2_SCALE,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0},
+		TIMPANI_CDC_ANC2_SCALE_M,
+		TIMPANI_CDC_ANC2_SCALE_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_ANC2_DEBUG,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0x0, 0xF0},
+		TIMPANI_CDC_ANC2_DEBUG_M,
+		TIMPANI_CDC_ANC2_DEBUG_POR,
+		{
+			{ .mask = 0x0F, .path_mask = 0},
+			{ .mask = 0xF0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_LINE_L_AVOL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0xFC, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3},
+		TIMPANI_CDC_LINE_L_AVOL_M,
+		TIMPANI_CDC_LINE_L_AVOL_POR,
+		{
+			{ .mask = 0xFC, .path_mask = 0},
+			{ .mask = 0x03, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_LINE_R_AVOL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0xFC, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3},
+		TIMPANI_CDC_LINE_R_AVOL_M,
+		TIMPANI_CDC_LINE_R_AVOL_POR,
+		{
+			{ .mask = 0xFC, .path_mask = 0},
+			{ .mask = 0x03, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_HPH_L_AVOL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFE,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1},
+		TIMPANI_CDC_HPH_L_AVOL_M,
+		TIMPANI_CDC_HPH_L_AVOL_POR,
+		{
+			{ .mask = 0xFE, .path_mask = 0},
+			{ .mask = 0x01, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_HPH_R_AVOL,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFE,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1},
+		TIMPANI_CDC_HPH_R_AVOL_M,
+		TIMPANI_CDC_HPH_R_AVOL_POR,
+		{
+			{ .mask = 0xFE, .path_mask = 0},
+			{ .mask = 0x01, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_COMP_CTL1,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x3F, 0x0, 0xC0},
+		TIMPANI_CDC_COMP_CTL1_M,
+		TIMPANI_CDC_COMP_CTL1_POR,
+		{
+			{ .mask = 0x3F, .path_mask = 0},
+			{ .mask = 0xC0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_COMP_CTL2,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0x0, 0xF0},
+		TIMPANI_CDC_COMP_CTL2_M,
+		TIMPANI_CDC_COMP_CTL2_POR,
+		{
+			{ .mask = 0x0F, .path_mask = 0},
+			{ .mask = 0xF0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_COMP_PEAK_METER,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0x0, 0xF0},
+		TIMPANI_CDC_COMP_PEAK_METER_M,
+		TIMPANI_CDC_COMP_PEAK_METER_POR,
+		{
+			{ .mask = 0x0F, .path_mask = 0},
+			{ .mask = 0xF0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_COMP_LEVEL_METER_CTL1,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0x0, 0xF0},
+		TIMPANI_CDC_COMP_LEVEL_METER_CTL1_M,
+		TIMPANI_CDC_COMP_LEVEL_METER_CTL1_POR,
+		{
+			{ .mask = 0x0F, .path_mask = 0},
+			{ .mask = 0xF0, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_COMP_LEVEL_METER_CTL2,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0},
+		TIMPANI_CDC_COMP_LEVEL_METER_CTL2_M,
+		TIMPANI_CDC_COMP_LEVEL_METER_CTL2_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_COMP_ZONE_SELECT,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x7F, 0x0, 0x80},
+		TIMPANI_CDC_COMP_ZONE_SELECT_M,
+		TIMPANI_CDC_COMP_ZONE_SELECT_POR,
+		{
+			{ .mask = 0x7F, .path_mask = 0},
+			{ .mask = 0x80, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_COMP_ZC_MSB,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0},
+		TIMPANI_CDC_COMP_ZC_MSB_M,
+		TIMPANI_CDC_COMP_ZC_MSB_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_COMP_ZC_LSB,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0},
+		TIMPANI_CDC_COMP_ZC_LSB_M,
+		TIMPANI_CDC_COMP_ZC_LSB_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_COMP_SHUT_DOWN,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF},
+		TIMPANI_CDC_COMP_SHUT_DOWN_M,
+		TIMPANI_CDC_COMP_SHUT_DOWN_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_COMP_SHUT_DOWN_STATUS,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF},
+		TIMPANI_CDC_COMP_SHUT_DOWN_STATUS_M,
+		TIMPANI_CDC_COMP_SHUT_DOWN_STATUS_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	},
+	{
+		TIMPANI_A_CDC_COMP_HALT,
+		{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+		0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF},
+		TIMPANI_CDC_COMP_HALT_M,
+		TIMPANI_CDC_COMP_HALT_POR,
+		{
+			{ .mask = 0xFF, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+			{ .mask = 0x00, .path_mask = 0},
+		}
+	}
+};
+
+struct reg_acc_blk_cfg timpani_blkcfg[RA_BLOCK_NUM] = {
+	{
+		.valid_owners = {RA_OWNER_NONE, RA_OWNER_PATH_RX1,
+		0, 0, 0, 0, RA_OWNER_DRV}
+	},
+	/* RA_BLOCK_RX1 */
+	{
+		.valid_owners = {RA_OWNER_NONE, 0, RA_OWNER_PATH_RX2,
+		0, 0, 0, RA_OWNER_DRV}
+	},
+	/* RA_BLOCK_RX2 */
+	{
+		.valid_owners = {RA_OWNER_NONE, 0, 0, RA_OWNER_PATH_TX1,
+		0, 0, RA_OWNER_DRV}
+	},
+	/* RA_BLOCK_TX1 */
+	{
+		.valid_owners = {RA_OWNER_NONE, 0, 0, 0, RA_OWNER_PATH_TX2,
+		0, RA_OWNER_DRV}
+	},
+	/* RA_BLOCK_TX2 */
+	{
+		.valid_owners = {RA_OWNER_NONE, 0, 0, 0, 0,
+		RA_OWNER_PATH_LB, RA_OWNER_DRV}
+	},
+	/* RA_BLOCK_LB */
+	{
+		.valid_owners = {RA_OWNER_NONE, RA_OWNER_PATH_RX1,
+		RA_OWNER_PATH_RX2, 0, 0, RA_OWNER_PATH_LB, RA_OWNER_DRV}
+	},
+	/* RA_BLOCK_SHARED_RX_LB */
+	{
+		.valid_owners = {RA_OWNER_NONE, 0, 0, RA_OWNER_PATH_TX1,
+		RA_OWNER_PATH_TX2, 0, RA_OWNER_DRV}
+	},
+	/* RA_BLOCK_SHARED_TX */
+	{
+		.valid_owners = {RA_OWNER_NONE, 0, 0, RA_OWNER_PATH_TX1,
+		RA_OWNER_PATH_TX2, 0, RA_OWNER_DRV}
+	},
+	/* RA_BLOCK_TXFE1 */
+	{
+		.valid_owners = {RA_OWNER_NONE, 0, 0, RA_OWNER_PATH_TX1,
+		RA_OWNER_PATH_TX2, 0, RA_OWNER_DRV}
+	},
+	/* RA_BLOCK_TXFE2 */
+	{
+		.valid_owners = {RA_OWNER_NONE, RA_OWNER_PATH_RX1,
+		RA_OWNER_PATH_RX2, 0, 0, RA_OWNER_PATH_LB, RA_OWNER_DRV}
+	},
+	/* RA_BLOCK_PA_COMMON */
+	{
+		.valid_owners = {RA_OWNER_NONE, RA_OWNER_PATH_RX1,
+		RA_OWNER_PATH_RX2, 0, 0, RA_OWNER_PATH_LB, RA_OWNER_DRV}
+	},
+	/* RA_BLOCK_PA_EAR */
+	{
+		.valid_owners = {RA_OWNER_NONE, RA_OWNER_PATH_RX1,
+		RA_OWNER_PATH_RX2, 0, 0, RA_OWNER_PATH_LB, RA_OWNER_DRV}
+	},
+	/* RA_BLOCK_PA_HPH */
+	{
+		.valid_owners = {RA_OWNER_NONE, RA_OWNER_PATH_RX1,
+		RA_OWNER_PATH_RX2, 0, 0, RA_OWNER_PATH_LB, RA_OWNER_DRV}
+	},
+	/* RA_BLOCK_PA_LINE */
+	{
+		.valid_owners = {RA_OWNER_NONE, RA_OWNER_PATH_RX1,
+		RA_OWNER_PATH_RX2, 0, 0, RA_OWNER_PATH_LB, RA_OWNER_DRV}
+	},
+	/* RA_BLOCK_PA_AUX */
+	{
+		.valid_owners = {RA_OWNER_NONE, 0, 0, RA_OWNER_PATH_TX1,
+		RA_OWNER_PATH_TX2, 0, RA_OWNER_DRV}
+	},
+	/* RA_BLOCK_ADC */
+	{
+		.valid_owners = {RA_OWNER_NONE, 0, 0, RA_OWNER_PATH_TX1,
+		RA_OWNER_PATH_TX2, 0, RA_OWNER_DRV}
+	},
+	/* RA_BLOCK_DMIC */
+	{
+		.valid_owners = {RA_OWNER_NONE, 0, 0, RA_OWNER_PATH_TX1,
+		RA_OWNER_PATH_TX2, 0, RA_OWNER_DRV}
+	},
+	/* RA_BLOCK_TX_I2S */
+	{
+		.valid_owners = {RA_OWNER_NONE, 0, 0, 0, 0, 0, RA_OWNER_DRV}
+	},
+	/*RA_BLOCK_DRV */
+	{
+		.valid_owners = {RA_OWNER_NONE, 0, 0, 0, 0, 0, RA_OWNER_DRV}
+	},
+	/* RA_BLOCK_TEST */
+	{
+		.valid_owners = {RA_OWNER_NONE, 0, 0, 0, 0, 0, RA_OWNER_DRV}
+	},
+	/* RA_BLOCK_RESERVED */
+};
+
+struct adie_codec_state {
+	struct adie_codec_path path[ADIE_CODEC_MAX];
+	u32 ref_cnt;
+	struct marimba *pdrv_ptr;
+	struct marimba_codec_platform_data *codec_pdata;
+	struct mutex lock;
+};
+
+static struct adie_codec_state adie_codec;
+
+/* A cacheable register is one that if the register's current value is being
+ * written to it again, then it is permissable to skip that register write
+ * because it does not actually change the value of the hardware register.
+ *
+ * Some registers are uncacheable, meaning that even they are being written
+ * again with their current value, the write has another purpose and must go
+ * through.
+ *
+ * Knowing the codec's uncacheable registers allows the driver to avoid
+ * unnecessary codec register writes while making sure important register writes
+ * are not skipped.
+ */
+
+static bool timpani_register_is_cacheable(u8 reg)
+{
+	switch (reg) {
+	case TIMPANI_A_PA_LINE_L_GAIN:
+	case TIMPANI_A_PA_LINE_R_GAIN:
+	case TIMPANI_A_PA_HPH_L_GAIN:
+	case TIMPANI_A_PA_HPH_R_GAIN:
+	case TIMPANI_A_CDC_GCTL1:
+	case TIMPANI_A_CDC_ST_CTL:
+	case TIMPANI_A_CDC_GCTL2:
+	case TIMPANI_A_CDC_ARB_BYPASS_CTL:
+	case TIMPANI_A_CDC_CH_CTL:
+	case TIMPANI_A_CDC_ANC1_IIR_COEFF_PTR:
+	case TIMPANI_A_CDC_ANC1_IIR_COEFF_MSB:
+	case TIMPANI_A_CDC_ANC1_IIR_COEFF_LSB:
+	case TIMPANI_A_CDC_ANC1_LPF_COEFF_PTR:
+	case TIMPANI_A_CDC_ANC1_LPF_COEFF_MSB:
+	case TIMPANI_A_CDC_ANC1_LPF_COEFF_LSB:
+	case TIMPANI_A_CDC_ANC1_SCALE_PTR:
+	case TIMPANI_A_CDC_ANC1_SCALE:
+	case TIMPANI_A_CDC_ANC2_IIR_COEFF_PTR:
+	case TIMPANI_A_CDC_ANC2_IIR_COEFF_MSB:
+	case TIMPANI_A_CDC_ANC2_IIR_COEFF_LSB:
+	case TIMPANI_A_CDC_ANC2_LPF_COEFF_PTR:
+	case TIMPANI_A_CDC_ANC2_LPF_COEFF_MSB:
+	case TIMPANI_A_CDC_ANC2_LPF_COEFF_LSB:
+	case TIMPANI_A_CDC_ANC2_SCALE_PTR:
+	case TIMPANI_A_CDC_ANC2_SCALE:
+	case TIMPANI_A_CDC_ANC1_CTL1:
+	case TIMPANI_A_CDC_ANC1_CTL2:
+	case TIMPANI_A_CDC_ANC1_FF_FB_SHIFT:
+	case TIMPANI_A_CDC_ANC2_CTL1:
+	case TIMPANI_A_CDC_ANC2_CTL2:
+	case TIMPANI_A_CDC_ANC2_FF_FB_SHIFT:
+	case TIMPANI_A_AUXPGA_LR_GAIN:
+	case TIMPANI_A_CDC_ANC_INPUT_MUX:
+		return false;
+	default:
+		return true;
+	}
+}
+
+static int adie_codec_write(u8 reg, u8 mask, u8 val)
+{
+	int rc = 0;
+	u8 new_val;
+
+	if (reg > MAX_SHADOW_RIGISTERS) {
+		pr_debug("register number is out of bound for shadow"
+					" registers reg = %d\n", reg);
+		new_val = (val & mask);
+		rc = marimba_write_bit_mask(adie_codec.pdrv_ptr, reg,  &new_val,
+			1, 0xFF);
+		if (IS_ERR_VALUE(rc)) {
+			pr_err("%s: fail to write reg %x\n", __func__, reg);
+			rc = -EIO;
+			goto error;
+		}
+		return rc;
+	}
+	new_val = (val & mask) | (timpani_shadow[reg] & ~mask);
+	if (!(timpani_register_is_cacheable(reg) &&
+		(new_val == timpani_shadow[reg]))) {
+
+		rc = marimba_write_bit_mask(adie_codec.pdrv_ptr, reg,  &new_val,
+			1, 0xFF);
+		if (IS_ERR_VALUE(rc)) {
+			pr_err("%s: fail to write reg %x\n", __func__, reg);
+			rc = -EIO;
+			goto error;
+		}
+		timpani_shadow[reg] = new_val;
+		pr_debug("%s: write reg %x val %x new value %x\n", __func__,
+			reg, val, new_val);
+	}
+
+error:
+	return rc;
+}
+
+
+static int reg_in_use(u8 reg_ref, u8 path_type)
+{
+	if ((reg_ref & ~path_type) == 0)
+		return 0;
+	else
+		return 1;
+}
+
+static int adie_codec_refcnt_write(u8 reg, u8 mask, u8 val, enum refcnt cnt,
+		u8 path_type)
+{
+	u8 i;
+	int j;
+	u8 fld_mask;
+	u8 path_mask;
+	u8 reg_mask = 0;
+	int rc = 0;
+
+	for (i = 0; i < ARRAY_SIZE(timpani_regset); i++) {
+		if (timpani_regset[i].reg_addr == reg) {
+			for (j = 0; j < TIMPANI_MAX_FIELDS; j++) {
+				fld_mask = timpani_regset[i].fld_ref_cnt[j].mask
+					& mask;
+				path_mask = timpani_regset[i].fld_ref_cnt[j]
+							.path_mask;
+				if (fld_mask) {
+					if (!reg_in_use(path_mask, path_type))
+						reg_mask |= fld_mask;
+					if (cnt == INC)
+						timpani_regset[i].fld_ref_cnt[j]
+							.path_mask |= path_type;
+					else if (cnt == DEC)
+						timpani_regset[i].fld_ref_cnt[j]
+							.path_mask &=
+								~path_type;
+				}
+			}
+
+			if (reg_mask)
+				rc = adie_codec_write(reg, reg_mask, val);
+			reg_mask = 0;
+			break;
+		}
+	}
+
+	return rc;
+}
+
+static int adie_codec_read(u8 reg, u8 *val)
+{
+	return marimba_read(adie_codec.pdrv_ptr, reg, val, 1);
+}
+
+static int timpani_adie_codec_setpath(struct adie_codec_path *path_ptr,
+					u32 freq_plan, u32 osr)
+{
+	int rc = 0;
+	u32 i, freq_idx = 0, freq = 0;
+
+	if (path_ptr == NULL)
+		return -EINVAL;
+
+	if (path_ptr->curr_stage != ADIE_CODEC_DIGITAL_OFF) {
+		rc = -EBUSY;
+		goto error;
+	}
+
+	for (i = 0; i < path_ptr->profile->setting_sz; i++) {
+		if (path_ptr->profile->settings[i].osr == osr) {
+			if (path_ptr->profile->settings[i].freq_plan >=
+				freq_plan) {
+				if (freq == 0) {
+					freq = path_ptr->profile->settings[i].
+								freq_plan;
+					freq_idx = i;
+				} else if (path_ptr->profile->settings[i].
+					freq_plan < freq) {
+					freq = path_ptr->profile->settings[i].
+								freq_plan;
+					freq_idx = i;
+				}
+			}
+		}
+	}
+
+	if (freq_idx >= path_ptr->profile->setting_sz)
+		rc = -ENODEV;
+	else {
+		path_ptr->hwsetting_idx = freq_idx;
+		path_ptr->stage_idx = 0;
+	}
+
+error:
+	return rc;
+}
+
+static u32 timpani_adie_codec_freq_supported(
+				struct adie_codec_dev_profile *profile,
+				u32 requested_freq)
+{
+	u32 i, rc = -EINVAL;
+
+	for (i = 0; i < profile->setting_sz; i++) {
+		if (profile->settings[i].freq_plan >= requested_freq) {
+			rc = 0;
+			break;
+		}
+	}
+	return rc;
+}
+int timpani_adie_codec_enable_sidetone(struct adie_codec_path *rx_path_ptr,
+	u32 enable)
+{
+	int rc = 0;
+
+	pr_debug("%s()\n", __func__);
+
+	mutex_lock(&adie_codec.lock);
+
+	if (!rx_path_ptr || &adie_codec.path[ADIE_CODEC_RX] != rx_path_ptr) {
+		pr_err("%s: invalid path pointer\n", __func__);
+		rc = -EINVAL;
+		goto error;
+	} else if (rx_path_ptr->curr_stage !=
+		ADIE_CODEC_DIGITAL_ANALOG_READY) {
+		pr_err("%s: bad state\n", __func__);
+		rc = -EPERM;
+		goto error;
+	}
+
+	if (enable) {
+		rc = adie_codec_write(TIMPANI_A_CDC_RX1_CTL,
+			TIMPANI_RX1_ST_MASK, TIMPANI_RX1_ST_ENABLE);
+
+		if (rx_path_ptr->reg_owner == RA_OWNER_PATH_RX1)
+			adie_codec_write(TIMPANI_A_CDC_ST_MIXING,
+				TIMPANI_CDC_ST_MIXING_TX1_MASK,
+				TIMPANI_CDC_ST_MIXING_TX1_ENABLE);
+		else if (rx_path_ptr->reg_owner == RA_OWNER_PATH_RX2)
+			adie_codec_write(TIMPANI_A_CDC_ST_MIXING,
+				TIMPANI_CDC_ST_MIXING_TX2_MASK,
+				TIMPANI_CDC_ST_MIXING_TX2_ENABLE);
+	 } else {
+		rc = adie_codec_write(TIMPANI_A_CDC_RX1_CTL,
+			TIMPANI_RX1_ST_MASK, 0);
+
+		if (rx_path_ptr->reg_owner == RA_OWNER_PATH_RX1)
+			adie_codec_write(TIMPANI_A_CDC_ST_MIXING,
+				TIMPANI_CDC_ST_MIXING_TX1_MASK, 0);
+		else if (rx_path_ptr->reg_owner == RA_OWNER_PATH_RX2)
+			adie_codec_write(TIMPANI_A_CDC_ST_MIXING,
+				TIMPANI_CDC_ST_MIXING_TX2_MASK, 0);
+	 }
+
+error:
+	mutex_unlock(&adie_codec.lock);
+	return rc;
+}
+static int timpani_adie_codec_enable_anc(struct adie_codec_path *rx_path_ptr,
+	u32 enable, struct adie_codec_anc_data *calibration_writes)
+{
+	int index = 0;
+	int rc = 0;
+	u8 reg, mask, val;
+	pr_debug("%s: enable = %d\n", __func__, enable);
+
+	mutex_lock(&adie_codec.lock);
+
+	if (!rx_path_ptr || &adie_codec.path[ADIE_CODEC_RX] != rx_path_ptr) {
+		pr_err("%s: invalid path pointer\n", __func__);
+		rc = -EINVAL;
+		goto error;
+	} else if (rx_path_ptr->curr_stage !=
+		ADIE_CODEC_DIGITAL_ANALOG_READY) {
+		pr_err("%s: bad state\n", __func__);
+		rc = -EPERM;
+		goto error;
+	}
+	if (enable) {
+		if (!calibration_writes || !calibration_writes->writes) {
+			pr_err("%s: No ANC calibration data\n", __func__);
+			rc = -EPERM;
+			goto error;
+		}
+		while (index < calibration_writes->size) {
+			ADIE_CODEC_UNPACK_ENTRY(calibration_writes->
+				writes[index], reg, mask, val);
+			adie_codec_write(reg, mask, val);
+			index++;
+		}
+	} else {
+		adie_codec_write(TIMPANI_A_CDC_ANC1_CTL1,
+		TIMPANI_CDC_ANC1_CTL1_ANC1_EN_M,
+		TIMPANI_CDC_ANC1_CTL1_ANC1_EN_ANC_DIS <<
+		TIMPANI_CDC_ANC1_CTL1_ANC1_EN_S);
+
+		adie_codec_write(TIMPANI_A_CDC_ANC2_CTL1,
+		TIMPANI_CDC_ANC2_CTL1_ANC2_EN_M,
+		TIMPANI_CDC_ANC2_CTL1_ANC2_EN_ANC_DIS <<
+		TIMPANI_CDC_ANC2_CTL1_ANC2_EN_S);
+	}
+
+error:
+	mutex_unlock(&adie_codec.lock);
+	return rc;
+}
+
+static void adie_codec_restore_regdefault(u8 path_mask, u32 blk)
+{
+	u32 ireg;
+	u32 regset_sz =
+	(sizeof(timpani_regset)/sizeof(struct timpani_regaccess));
+
+	for (ireg = 0; ireg < regset_sz; ireg++) {
+		if (timpani_regset[ireg].blk_mask[blk]) {
+			/* only process register belong to the block */
+			u8 reg = timpani_regset[ireg].reg_addr;
+			u8 mask = timpani_regset[ireg].blk_mask[blk];
+			u8 val = timpani_regset[ireg].reg_default;
+			adie_codec_refcnt_write(reg, mask, val, IGNORE,
+				path_mask);
+		}
+	}
+}
+
+static void adie_codec_reach_stage_action(struct adie_codec_path *path_ptr,
+	u32 stage)
+{
+	u32 iblk, iowner; /* iterators */
+	u8 path_mask;
+
+	if (path_ptr == NULL)
+		return;
+
+	path_mask = TIMPANI_PATH_MASK(path_ptr->reg_owner);
+
+	if (stage != ADIE_CODEC_DIGITAL_OFF)
+		return;
+
+	for (iblk = 0 ; iblk <= RA_BLOCK_RESERVED ; iblk++) {
+		for (iowner = 0; iowner < RA_OWNER_NUM; iowner++) {
+			if (timpani_blkcfg[iblk].valid_owners[iowner] ==
+					path_ptr->reg_owner) {
+				adie_codec_restore_regdefault(path_mask, iblk);
+				break; /* This path owns this block */
+			}
+		}
+	}
+}
+
+static int timpani_adie_codec_proceed_stage(struct adie_codec_path *path_ptr,
+						u32 state)
+{
+	int rc = 0, loop_exit = 0;
+	struct adie_codec_action_unit *curr_action;
+	struct adie_codec_hwsetting_entry *setting;
+	u8 reg, mask, val;
+	u8 path_mask;
+
+	if (path_ptr == NULL)
+		return -EINVAL;
+
+	path_mask = TIMPANI_PATH_MASK(path_ptr->reg_owner);
+
+	mutex_lock(&adie_codec.lock);
+	setting = &path_ptr->profile->settings[path_ptr->hwsetting_idx];
+	while (!loop_exit) {
+
+		curr_action = &setting->actions[path_ptr->stage_idx];
+
+		switch (curr_action->type) {
+		case ADIE_CODEC_ACTION_ENTRY:
+			ADIE_CODEC_UNPACK_ENTRY(curr_action->action,
+			reg, mask, val);
+			if (state == ADIE_CODEC_DIGITAL_OFF)
+				adie_codec_refcnt_write(reg, mask, val, DEC,
+					path_mask);
+			else
+				adie_codec_refcnt_write(reg, mask, val, INC,
+					path_mask);
+			break;
+		case ADIE_CODEC_ACTION_DELAY_WAIT:
+			if (curr_action->action > MAX_MDELAY_US)
+				msleep(curr_action->action/1000);
+			else
+				usleep_range(curr_action->action,
+				curr_action->action);
+			break;
+		case ADIE_CODEC_ACTION_STAGE_REACHED:
+			adie_codec_reach_stage_action(path_ptr,
+				curr_action->action);
+			if (curr_action->action == state) {
+				path_ptr->curr_stage = state;
+				loop_exit = 1;
+			}
+			break;
+		default:
+			BUG();
+		}
+
+		path_ptr->stage_idx++;
+		if (path_ptr->stage_idx == setting->action_sz)
+			path_ptr->stage_idx = 0;
+	}
+	mutex_unlock(&adie_codec.lock);
+
+	return rc;
+}
+
+static void timpani_codec_bring_up(void)
+{
+	/* Codec power up sequence */
+	adie_codec_write(0xFF, 0xFF, 0x08);
+	adie_codec_write(0xFF, 0xFF, 0x0A);
+	adie_codec_write(0xFF, 0xFF, 0x0E);
+	adie_codec_write(0xFF, 0xFF, 0x07);
+	adie_codec_write(0xFF, 0xFF, 0x17);
+	adie_codec_write(TIMPANI_A_MREF, 0xFF, 0xF2);
+	msleep(15);
+	adie_codec_write(TIMPANI_A_MREF, 0xFF, 0x22);
+
+	/* Bypass TX HPFs to prevent pops */
+	adie_codec_write(TIMPANI_A_CDC_BYPASS_CTL2, TIMPANI_CDC_BYPASS_CTL2_M,
+		TIMPANI_CDC_BYPASS_CTL2_POR);
+	adie_codec_write(TIMPANI_A_CDC_BYPASS_CTL3, TIMPANI_CDC_BYPASS_CTL3_M,
+		TIMPANI_CDC_BYPASS_CTL3_POR);
+}
+
+static void timpani_codec_bring_down(void)
+{
+	adie_codec_write(TIMPANI_A_MREF, 0xFF, TIMPANI_MREF_POR);
+	adie_codec_write(0xFF, 0xFF, 0x07);
+	adie_codec_write(0xFF, 0xFF, 0x06);
+	adie_codec_write(0xFF, 0xFF, 0x0E);
+	adie_codec_write(0xFF, 0xFF, 0x08);
+}
+
+static int timpani_adie_codec_open(struct adie_codec_dev_profile *profile,
+	struct adie_codec_path **path_pptr)
+{
+	int rc = 0;
+
+	mutex_lock(&adie_codec.lock);
+
+	if (!profile || !path_pptr) {
+		rc = -EINVAL;
+		goto error;
+	}
+
+	if (adie_codec.path[profile->path_type].profile) {
+		rc = -EBUSY;
+		goto error;
+	}
+
+	if (!adie_codec.ref_cnt) {
+
+		if (adie_codec.codec_pdata &&
+				adie_codec.codec_pdata->marimba_codec_power) {
+
+			rc = adie_codec.codec_pdata->marimba_codec_power(1);
+			if (rc) {
+				pr_err("%s: could not power up timpani "
+						"codec\n", __func__);
+				goto error;
+			}
+			timpani_codec_bring_up();
+		} else {
+			pr_err("%s: couldn't detect timpani codec\n", __func__);
+			rc = -ENODEV;
+			goto error;
+		}
+
+	}
+
+	adie_codec.path[profile->path_type].profile = profile;
+	*path_pptr = (void *) &adie_codec.path[profile->path_type];
+	adie_codec.ref_cnt++;
+	adie_codec.path[profile->path_type].hwsetting_idx = 0;
+	adie_codec.path[profile->path_type].curr_stage = ADIE_CODEC_DIGITAL_OFF;
+	adie_codec.path[profile->path_type].stage_idx = 0;
+
+
+error:
+	mutex_unlock(&adie_codec.lock);
+	return rc;
+}
+
+static int timpani_adie_codec_close(struct adie_codec_path *path_ptr)
+{
+	int rc = 0;
+
+	mutex_lock(&adie_codec.lock);
+
+	if (!path_ptr) {
+		rc = -EINVAL;
+		goto error;
+	}
+	if (path_ptr->curr_stage != ADIE_CODEC_DIGITAL_OFF)
+		adie_codec_proceed_stage(path_ptr, ADIE_CODEC_DIGITAL_OFF);
+
+	BUG_ON(!adie_codec.ref_cnt);
+
+	path_ptr->profile = NULL;
+	adie_codec.ref_cnt--;
+
+	if (!adie_codec.ref_cnt) {
+		/* Timpani CDC power down sequence */
+		timpani_codec_bring_down();
+
+		if (adie_codec.codec_pdata &&
+				adie_codec.codec_pdata->marimba_codec_power) {
+
+			rc = adie_codec.codec_pdata->marimba_codec_power(0);
+			if (rc) {
+				pr_err("%s: could not power down timpani "
+						"codec\n", __func__);
+				goto error;
+			}
+		}
+	}
+
+error:
+	mutex_unlock(&adie_codec.lock);
+	return rc;
+}
+
+static int timpani_adie_codec_set_master_mode(struct adie_codec_path *path_ptr,
+			u8 master)
+{
+	u8 val = master ? 1 : 0;
+
+	if (!path_ptr)
+		return -EINVAL;
+
+	if (path_ptr->reg_owner == RA_OWNER_PATH_RX1)
+		adie_codec_write(TIMPANI_A_CDC_RX1_CTL, 0x01, val);
+	else if (path_ptr->reg_owner == RA_OWNER_PATH_TX1)
+		adie_codec_write(TIMPANI_A_CDC_TX_I2S_CTL, 0x01, val);
+	else
+		return -EINVAL;
+
+	return 0;
+}
+
+int timpani_adie_codec_set_device_analog_volume(
+		struct adie_codec_path *path_ptr,
+		u32 num_channels, u32 volume)
+{
+	u8 val;
+	u8 curr_val;
+	u8 i;
+
+	adie_codec_read(TIMPANI_A_AUXPGA_LR_GAIN, &curr_val);
+
+	/* Volume is expressed as a percentage. */
+	/* The upper nibble is the left channel, lower right channel. */
+	val = (u8)((volume * TIMPANI_CODEC_AUXPGA_GAIN_RANGE) / 100);
+	val |= val << 4;
+
+	if ((curr_val & 0x0F) < (val & 0x0F)) {
+		for (i = curr_val; i < val; i += 0x11)
+			adie_codec_write(TIMPANI_A_AUXPGA_LR_GAIN, 0xFF, i);
+	} else if ((curr_val & 0x0F) > (val & 0x0F)) {
+		for (i = curr_val; i > val; i -= 0x11)
+			adie_codec_write(TIMPANI_A_AUXPGA_LR_GAIN, 0xFF, i);
+	}
+
+	return 0;
+}
+
+enum adie_vol_type {
+	ADIE_CODEC_RX_DIG_VOL,
+	ADIE_CODEC_TX_DIG_VOL,
+	ADIE_CODEC_VOL_TYPE_MAX
+};
+
+#define CDC_RX1LG		0x84
+#define CDC_RX1RG		0x85
+#define CDC_TX1LG		0x86
+#define CDC_TX1RG		0x87
+#define	DIG_VOL_MASK		0xFF
+
+#define CDC_GCTL1		0x8A
+#define RX1_PGA_UPDATE_L	0x04
+#define RX1_PGA_UPDATE_R	0x08
+#define TX1_PGA_UPDATE_L	0x40
+#define TX1_PGA_UPDATE_R	0x80
+#define CDC_GCTL1_RX_MASK	0x0F
+#define CDC_GCTL1_TX_MASK	0xF0
+
+enum {
+	TIMPANI_MIN_DIG_VOL	= -84,	/* in DB*/
+	TIMPANI_MAX_DIG_VOL	=  16,	/* in DB*/
+	TIMPANI_DIG_VOL_STEP	=  3	/* in DB*/
+};
+
+static int timpani_adie_codec_set_dig_vol(enum adie_vol_type vol_type,
+	u32 num_chan, u32 vol_per)
+{
+	u8 reg_left, reg_right;
+	u8 gain_reg_val, gain_reg_mask;
+	s8 new_reg_val, cur_reg_val;
+	s8 step_size;
+
+	adie_codec_read(CDC_GCTL1, &gain_reg_val);
+
+	if (vol_type == ADIE_CODEC_RX_DIG_VOL) {
+
+		pr_debug("%s : RX DIG VOL. num_chan = %u\n", __func__,
+				num_chan);
+		reg_left =  CDC_RX1LG;
+		reg_right = CDC_RX1RG;
+
+		if (num_chan == 1)
+			gain_reg_val |=  RX1_PGA_UPDATE_L;
+		else
+			gain_reg_val |= (RX1_PGA_UPDATE_L | RX1_PGA_UPDATE_R);
+
+		gain_reg_mask = CDC_GCTL1_RX_MASK;
+	} else {
+
+		pr_debug("%s : TX DIG VOL. num_chan = %u\n", __func__,
+				num_chan);
+		reg_left = CDC_TX1LG;
+		reg_right = CDC_TX1RG;
+
+		if (num_chan == 1)
+			gain_reg_val |=  TX1_PGA_UPDATE_L;
+		else
+			gain_reg_val |= (TX1_PGA_UPDATE_L | TX1_PGA_UPDATE_R);
+
+		gain_reg_mask = CDC_GCTL1_TX_MASK;
+	}
+
+	adie_codec_read(reg_left, &cur_reg_val);
+
+	pr_debug("%s: vol_per = %d cur_reg_val = %d 0x%x\n", __func__, vol_per,
+			cur_reg_val, cur_reg_val);
+
+	new_reg_val =  TIMPANI_MIN_DIG_VOL +
+		(((TIMPANI_MAX_DIG_VOL - TIMPANI_MIN_DIG_VOL) * vol_per) / 100);
+
+	pr_debug("new_reg_val = %d 0x%x\n", new_reg_val, new_reg_val);
+
+	if (new_reg_val > cur_reg_val) {
+		step_size = TIMPANI_DIG_VOL_STEP;
+	} else if (new_reg_val < cur_reg_val) {
+		step_size = -TIMPANI_DIG_VOL_STEP;
+	} else {
+		pr_debug("new_reg_val and cur_reg_val are same 0x%x\n",
+				new_reg_val);
+		return 0;
+	}
+
+	while (cur_reg_val != new_reg_val) {
+
+		if (((new_reg_val > cur_reg_val) &&
+			((new_reg_val - cur_reg_val) < TIMPANI_DIG_VOL_STEP)) ||
+			((cur_reg_val > new_reg_val) &&
+			((cur_reg_val - new_reg_val)
+			 < TIMPANI_DIG_VOL_STEP))) {
+
+			cur_reg_val = new_reg_val;
+
+			pr_debug("diff less than step. write new_reg_val = %d"
+				" 0x%x\n", new_reg_val, new_reg_val);
+
+		 } else {
+			cur_reg_val = cur_reg_val + step_size;
+
+			pr_debug("cur_reg_val = %d 0x%x\n",
+					cur_reg_val, cur_reg_val);
+		 }
+
+		adie_codec_write(reg_left, DIG_VOL_MASK, cur_reg_val);
+
+		if (num_chan == 2)
+			adie_codec_write(reg_right, DIG_VOL_MASK, cur_reg_val);
+
+		adie_codec_write(CDC_GCTL1, gain_reg_mask, gain_reg_val);
+	}
+	return 0;
+}
+
+static int timpani_adie_codec_set_device_digital_volume(
+		struct adie_codec_path *path_ptr,
+		u32 num_channels, u32 vol_percentage /* in percentage */)
+{
+	enum adie_vol_type vol_type;
+
+	if (!path_ptr  || (path_ptr->curr_stage !=
+				ADIE_CODEC_DIGITAL_ANALOG_READY)) {
+		pr_info("%s: timpani codec not ready for volume control\n",
+		       __func__);
+		return  -EPERM;
+	}
+
+	if (num_channels > 2) {
+		pr_err("%s: timpani odec only supports max two channels\n",
+		       __func__);
+		return -EINVAL;
+	}
+
+	if (path_ptr->profile->path_type == ADIE_CODEC_RX) {
+		vol_type = ADIE_CODEC_RX_DIG_VOL;
+	} else if (path_ptr->profile->path_type == ADIE_CODEC_TX) {
+		vol_type = ADIE_CODEC_TX_DIG_VOL;
+	} else {
+		pr_err("%s: invalid device data neither RX nor TX\n",
+				__func__);
+		return -EINVAL;
+	}
+
+	timpani_adie_codec_set_dig_vol(vol_type, num_channels, vol_percentage);
+
+	return 0;
+}
+
+static const struct adie_codec_operations timpani_adie_ops = {
+	.codec_id = TIMPANI_ID,
+	.codec_open = timpani_adie_codec_open,
+	.codec_close = timpani_adie_codec_close,
+	.codec_setpath = timpani_adie_codec_setpath,
+	.codec_proceed_stage = timpani_adie_codec_proceed_stage,
+	.codec_freq_supported = timpani_adie_codec_freq_supported,
+	.codec_enable_sidetone = timpani_adie_codec_enable_sidetone,
+	.codec_set_master_mode = timpani_adie_codec_set_master_mode,
+	.codec_enable_anc = timpani_adie_codec_enable_anc,
+	.codec_set_device_analog_volume =
+		timpani_adie_codec_set_device_analog_volume,
+	.codec_set_device_digital_volume =
+		timpani_adie_codec_set_device_digital_volume,
+};
+
+static void timpani_codec_populate_shadow_registers(void)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(timpani_regset); i++) {
+		if (timpani_regset[i].reg_addr < TIMPANI_ARRAY_SIZE) {
+			timpani_shadow[timpani_regset[i].reg_addr] =
+				timpani_regset[i].reg_default;
+		}
+	}
+}
+
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *debugfs_timpani_dent;
+static struct dentry *debugfs_peek;
+static struct dentry *debugfs_poke;
+static struct dentry *debugfs_power;
+static struct dentry *debugfs_dump;
+
+static unsigned char read_data;
+
+static int codec_debug_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static int get_parameters(char *buf, long int *param1, int num_of_par)
+{
+	char *token;
+	int base, cnt;
+
+	token = strsep(&buf, " ");
+
+	for (cnt = 0; cnt < num_of_par; cnt++) {
+		if (token != NULL) {
+			if ((token[1] == 'x') || (token[1] == 'X'))
+				base = 16;
+			else
+				base = 10;
+
+			if (strict_strtoul(token, base, &param1[cnt]) != 0)
+				return -EINVAL;
+
+			token = strsep(&buf, " ");
+			}
+		else
+			return -EINVAL;
+	}
+	return 0;
+}
+
+static ssize_t codec_debug_read(struct file *file, char __user *ubuf,
+				size_t count, loff_t *ppos)
+{
+	char lbuf[8];
+
+	snprintf(lbuf, sizeof(lbuf), "0x%x\n", read_data);
+	return simple_read_from_buffer(ubuf, count, ppos, lbuf, strlen(lbuf));
+}
+
+static ssize_t codec_debug_write(struct file *filp,
+	const char __user *ubuf, size_t cnt, loff_t *ppos)
+{
+	char *access_str = filp->private_data;
+	char lbuf[32];
+	int rc;
+	int i;
+	int read_result;
+	u8 reg_val;
+	long int param[5];
+
+	if (cnt > sizeof(lbuf) - 1)
+		return -EINVAL;
+
+	rc = copy_from_user(lbuf, ubuf, cnt);
+	if (rc)
+		return -EFAULT;
+
+	lbuf[cnt] = '\0';
+
+	if (!strcmp(access_str, "power")) {
+		if (get_parameters(lbuf, param, 1) == 0) {
+			switch (param[0]) {
+			case 1:
+				adie_codec.codec_pdata->marimba_codec_power(1);
+				timpani_codec_bring_up();
+				break;
+			case 0:
+				timpani_codec_bring_down();
+				adie_codec.codec_pdata->marimba_codec_power(0);
+				break;
+			default:
+				rc = -EINVAL;
+				break;
+			}
+		} else
+			rc = -EINVAL;
+	} else if (!strcmp(access_str, "poke")) {
+		/* write */
+		rc = get_parameters(lbuf, param, 2);
+		if ((param[0] <= 0xFF) && (param[1] <= 0xFF) &&
+			(rc == 0))
+			adie_codec_write(param[0], 0xFF, param[1]);
+		else
+			rc = -EINVAL;
+	} else if (!strcmp(access_str, "peek")) {
+		/* read */
+		rc = get_parameters(lbuf, param, 1);
+		if ((param[0] <= 0xFF) && (rc == 0))
+			adie_codec_read(param[0], &read_data);
+		else
+			rc = -EINVAL;
+	} else if (!strcmp(access_str, "dump")) {
+		pr_info("************** timpani regs *************\n");
+		for (i = 0; i < 0xFF; i++) {
+			read_result = adie_codec_read(i, &reg_val);
+			if (read_result < 0) {
+				pr_info("failed to read codec register\n");
+				break;
+			} else
+				pr_info("reg 0x%02X val 0x%02X\n", i, reg_val);
+		}
+		pr_info("*****************************************\n");
+	}
+
+	if (rc == 0)
+		rc = cnt;
+	else
+		pr_err("%s: rc = %d\n", __func__, rc);
+
+	return rc;
+}
+
+static const struct file_operations codec_debug_ops = {
+	.open = codec_debug_open,
+	.write = codec_debug_write,
+	.read = codec_debug_read
+};
+#endif
+
+static int timpani_codec_probe(struct platform_device *pdev)
+{
+	int rc;
+
+	adie_codec.pdrv_ptr = platform_get_drvdata(pdev);
+	adie_codec.codec_pdata = pdev->dev.platform_data;
+
+	if (adie_codec.codec_pdata->snddev_profile_init)
+		adie_codec.codec_pdata->snddev_profile_init();
+
+	timpani_codec_populate_shadow_registers();
+
+	/* Register the timpani ADIE operations */
+	rc = adie_codec_register_codec_operations(&timpani_adie_ops);
+
+#ifdef CONFIG_DEBUG_FS
+	debugfs_timpani_dent = debugfs_create_dir("msm_adie_codec", 0);
+	if (!IS_ERR(debugfs_timpani_dent)) {
+		debugfs_peek = debugfs_create_file("peek",
+		S_IFREG | S_IRUGO, debugfs_timpani_dent,
+		(void *) "peek", &codec_debug_ops);
+
+		debugfs_poke = debugfs_create_file("poke",
+		S_IFREG | S_IRUGO, debugfs_timpani_dent,
+		(void *) "poke", &codec_debug_ops);
+
+		debugfs_power = debugfs_create_file("power",
+		S_IFREG | S_IRUGO, debugfs_timpani_dent,
+		(void *) "power", &codec_debug_ops);
+
+		debugfs_dump = debugfs_create_file("dump",
+		S_IFREG | S_IRUGO, debugfs_timpani_dent,
+		(void *) "dump", &codec_debug_ops);
+
+	}
+#endif
+
+	return rc;
+}
+
+static struct platform_driver timpani_codec_driver = {
+	.probe = timpani_codec_probe,
+	.driver = {
+		.name = "timpani_codec",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init timpani_codec_init(void)
+{
+	s32 rc;
+
+	rc = platform_driver_register(&timpani_codec_driver);
+	if (IS_ERR_VALUE(rc))
+		goto error;
+
+	adie_codec.path[ADIE_CODEC_TX].reg_owner = RA_OWNER_PATH_TX1;
+	adie_codec.path[ADIE_CODEC_RX].reg_owner = RA_OWNER_PATH_RX1;
+	adie_codec.path[ADIE_CODEC_LB].reg_owner = RA_OWNER_PATH_LB;
+	mutex_init(&adie_codec.lock);
+error:
+	return rc;
+}
+
+static void __exit timpani_codec_exit(void)
+{
+#ifdef CONFIG_DEBUG_FS
+	debugfs_remove(debugfs_peek);
+	debugfs_remove(debugfs_poke);
+	debugfs_remove(debugfs_power);
+	debugfs_remove(debugfs_dump);
+	debugfs_remove(debugfs_timpani_dent);
+#endif
+	platform_driver_unregister(&timpani_codec_driver);
+}
+
+module_init(timpani_codec_init);
+module_exit(timpani_codec_exit);
+
+MODULE_DESCRIPTION("Timpani codec driver");
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/tps65023.c b/drivers/mfd/tps65023.c
new file mode 100644
index 0000000..318692f
--- /dev/null
+++ b/drivers/mfd/tps65023.c
@@ -0,0 +1,123 @@
+/* Copyright (c) 2009, 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.
+ *
+ */
+
+#include <linux/i2c.h>
+#include <linux/mfd/tps65023.h>
+#include <linux/module.h>
+
+/* TPS65023_registers */
+#define TPS65023_VERSION	0
+#define TPS65023_PGOODZ		1
+#define TPS65023_MASK		2
+#define TPS65023_REG_CTRL	3
+#define TPS65023_CON_CTRL	4
+#define TPS65023_CON_CTRL2	5
+#define TPS65023_DEFCORE	6
+#define TPS65023_DEFSLEW	7
+#define TPS65023_LDO_CTRL	8
+#define TPS65023_MAX		9
+
+static struct i2c_client *tpsclient;
+
+int tps65023_set_dcdc1_level(int mvolts)
+{
+	int val;
+	int ret;
+
+	if (!tpsclient)
+		return -ENODEV;
+
+	if (mvolts < 800 || mvolts > 1600)
+		return -EINVAL;
+
+	if (mvolts == 1600)
+		val = 0x1F;
+	else
+		val = ((mvolts - 800)/25) & 0x1F;
+
+	ret = i2c_smbus_write_byte_data(tpsclient, TPS65023_DEFCORE, val);
+
+	if (!ret)
+		ret = i2c_smbus_write_byte_data(tpsclient,
+				TPS65023_CON_CTRL2, 0x80);
+
+	return ret;
+}
+EXPORT_SYMBOL(tps65023_set_dcdc1_level);
+
+int tps65023_get_dcdc1_level(int *mvolts)
+{
+	int val;
+
+	if (!tpsclient)
+		return -ENODEV;
+
+	val = i2c_smbus_read_byte_data(tpsclient, TPS65023_DEFCORE) & 0x1F;
+
+	if (val == 0x1F)
+		*mvolts = 1600;
+	else
+		*mvolts = (val * 25) + 800;
+	return 0;
+}
+EXPORT_SYMBOL(tps65023_get_dcdc1_level);
+
+static int tps65023_probe(struct i2c_client *client,
+		const struct i2c_device_id *dev_id)
+{
+	if (!i2c_check_functionality(client->adapter,
+				I2C_FUNC_SMBUS_BYTE_DATA)) {
+		printk(KERN_ERR "TPS65023 does not support SMBUS_BYTE_DATA.\n");
+		return -EINVAL;
+	}
+
+	tpsclient = client;
+	printk(KERN_INFO "TPS65023: PMIC probed.\n");
+	return 0;
+}
+
+static int __devexit tps65023_remove(struct i2c_client *client)
+{
+	tpsclient = NULL;
+	return 0;
+}
+
+static const struct i2c_device_id tps65023_id[] = {
+	{ "tps65023", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, tps65023_id);
+
+static struct i2c_driver tps65023_driver = {
+	.driver = {
+		.name   = "tps65023",
+		.owner  = THIS_MODULE,
+	},
+	.probe  = tps65023_probe,
+	.remove = __devexit_p(tps65023_remove),
+	.id_table = tps65023_id,
+};
+
+static int __init tps65023_init(void)
+{
+	return i2c_add_driver(&tps65023_driver);
+}
+
+
+static void __exit tps65023_exit(void)
+{
+	i2c_del_driver(&tps65023_driver);
+}
+
+module_init(tps65023_init);
+module_exit(tps65023_exit);
diff --git a/drivers/mfd/wcd9xxx-core.c b/drivers/mfd/wcd9xxx-core.c
new file mode 100644
index 0000000..e2dff4b
--- /dev/null
+++ b/drivers/mfd/wcd9xxx-core.c
@@ -0,0 +1,1168 @@
+/* Copyright (c) 2011-2012, 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx-slimslave.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd9xxx/pdata.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
+
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/debugfs.h>
+#include <linux/regulator/consumer.h>
+#include <linux/i2c.h>
+#include <sound/soc.h>
+
+#define WCD9XXX_SLIM_GLA_MAX_RETRIES 5
+#define WCD9XXX_REGISTER_START_OFFSET 0x800
+#define WCD9XXX_SLIM_RW_MAX_TRIES 3
+
+#define MAX_WCD9XXX_DEVICE	4
+#define WCD9XXX_I2C_MODE	0x03
+
+struct wcd9xxx_i2c {
+	struct i2c_client *client;
+	struct i2c_msg xfer_msg[2];
+	struct mutex xfer_lock;
+	int mod_id;
+};
+
+struct wcd9xxx_i2c wcd9xxx_modules[MAX_WCD9XXX_DEVICE];
+static int wcd9xxx_intf = -1;
+
+static int wcd9xxx_read(struct wcd9xxx *wcd9xxx, unsigned short reg,
+		       int bytes, void *dest, bool interface_reg)
+{
+	int ret;
+	u8 *buf = dest;
+
+	if (bytes <= 0) {
+		dev_err(wcd9xxx->dev, "Invalid byte read length %d\n", bytes);
+		return -EINVAL;
+	}
+
+	ret = wcd9xxx->read_dev(wcd9xxx, reg, bytes, dest, interface_reg);
+	if (ret < 0) {
+		dev_err(wcd9xxx->dev, "Codec read failed\n");
+		return ret;
+	} else
+		dev_dbg(wcd9xxx->dev, "Read 0x%02x from 0x%x\n",
+			 *buf, reg);
+
+	return 0;
+}
+int wcd9xxx_reg_read(struct wcd9xxx *wcd9xxx, unsigned short reg)
+{
+	u8 val;
+	int ret;
+
+	mutex_lock(&wcd9xxx->io_lock);
+	ret = wcd9xxx_read(wcd9xxx, reg, 1, &val, false);
+	mutex_unlock(&wcd9xxx->io_lock);
+
+	if (ret < 0)
+		return ret;
+	else
+		return val;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_reg_read);
+
+static int wcd9xxx_write(struct wcd9xxx *wcd9xxx, unsigned short reg,
+			int bytes, void *src, bool interface_reg)
+{
+	u8 *buf = src;
+
+	if (bytes <= 0) {
+		pr_err("%s: Error, invalid write length\n", __func__);
+		return -EINVAL;
+	}
+
+	dev_dbg(wcd9xxx->dev, "Write %02x to 0x%x\n",
+		 *buf, reg);
+
+	return wcd9xxx->write_dev(wcd9xxx, reg, bytes, src, interface_reg);
+}
+
+int wcd9xxx_reg_write(struct wcd9xxx *wcd9xxx, unsigned short reg,
+		     u8 val)
+{
+	int ret;
+
+	mutex_lock(&wcd9xxx->io_lock);
+	ret = wcd9xxx_write(wcd9xxx, reg, 1, &val, false);
+	mutex_unlock(&wcd9xxx->io_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_reg_write);
+
+static u8 wcd9xxx_pgd_la;
+static u8 wcd9xxx_inf_la;
+
+int wcd9xxx_interface_reg_read(struct wcd9xxx *wcd9xxx, unsigned short reg)
+{
+	u8 val;
+	int ret;
+
+	mutex_lock(&wcd9xxx->io_lock);
+	ret = wcd9xxx_read(wcd9xxx, reg, 1, &val, true);
+	mutex_unlock(&wcd9xxx->io_lock);
+
+	if (ret < 0)
+		return ret;
+	else
+		return val;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_interface_reg_read);
+
+int wcd9xxx_interface_reg_write(struct wcd9xxx *wcd9xxx, unsigned short reg,
+		     u8 val)
+{
+	int ret;
+
+	mutex_lock(&wcd9xxx->io_lock);
+	ret = wcd9xxx_write(wcd9xxx, reg, 1, &val, true);
+	mutex_unlock(&wcd9xxx->io_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_interface_reg_write);
+
+int wcd9xxx_bulk_read(struct wcd9xxx *wcd9xxx, unsigned short reg,
+		     int count, u8 *buf)
+{
+	int ret;
+
+	mutex_lock(&wcd9xxx->io_lock);
+
+	ret = wcd9xxx_read(wcd9xxx, reg, count, buf, false);
+
+	mutex_unlock(&wcd9xxx->io_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_bulk_read);
+
+int wcd9xxx_bulk_write(struct wcd9xxx *wcd9xxx, unsigned short reg,
+		     int count, u8 *buf)
+{
+	int ret;
+
+	mutex_lock(&wcd9xxx->io_lock);
+
+	ret = wcd9xxx_write(wcd9xxx, reg, count, buf, false);
+
+	mutex_unlock(&wcd9xxx->io_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_bulk_write);
+
+static int wcd9xxx_slim_read_device(struct wcd9xxx *wcd9xxx, unsigned short reg,
+				int bytes, void *dest, bool interface)
+{
+	int ret;
+	struct slim_ele_access msg;
+	int slim_read_tries = WCD9XXX_SLIM_RW_MAX_TRIES;
+	msg.start_offset = WCD9XXX_REGISTER_START_OFFSET + reg;
+	msg.num_bytes = bytes;
+	msg.comp = NULL;
+
+	while (1) {
+		mutex_lock(&wcd9xxx->xfer_lock);
+		ret = slim_request_val_element(interface ?
+			       wcd9xxx->slim_slave : wcd9xxx->slim,
+			       &msg, dest, bytes);
+		mutex_unlock(&wcd9xxx->xfer_lock);
+		if (likely(ret == 0) || (--slim_read_tries == 0))
+			break;
+		usleep_range(5000, 5000);
+	}
+
+	if (ret)
+		pr_err("%s: Error, Codec read failed (%d)\n", __func__, ret);
+
+	return ret;
+}
+/* Interface specifies whether the write is to the interface or general
+ * registers.
+ */
+static int wcd9xxx_slim_write_device(struct wcd9xxx *wcd9xxx,
+		unsigned short reg, int bytes, void *src, bool interface)
+{
+	int ret;
+	struct slim_ele_access msg;
+	int slim_write_tries = WCD9XXX_SLIM_RW_MAX_TRIES;
+	msg.start_offset = WCD9XXX_REGISTER_START_OFFSET + reg;
+	msg.num_bytes = bytes;
+	msg.comp = NULL;
+
+	while (1) {
+		mutex_lock(&wcd9xxx->xfer_lock);
+		ret = slim_change_val_element(interface ?
+			      wcd9xxx->slim_slave : wcd9xxx->slim,
+			      &msg, src, bytes);
+		mutex_unlock(&wcd9xxx->xfer_lock);
+		if (likely(ret == 0) || (--slim_write_tries == 0))
+			break;
+		usleep_range(5000, 5000);
+	}
+
+	if (ret)
+		pr_err("%s: Error, Codec write failed (%d)\n", __func__, ret);
+
+	return ret;
+}
+
+static struct mfd_cell tabla1x_devs[] = {
+	{
+		.name = "tabla1x_codec",
+	},
+};
+
+static struct mfd_cell tabla_devs[] = {
+	{
+		.name = "tabla_codec",
+	},
+};
+
+static struct mfd_cell sitar_devs[] = {
+	{
+		.name = "sitar_codec",
+	},
+};
+
+static void wcd9xxx_bring_up(struct wcd9xxx *wcd9xxx)
+{
+	wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_LEAKAGE_CTL, 0x4);
+	wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_CDC_CTL, 0);
+	usleep_range(5000, 5000);
+	wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_CDC_CTL, 3);
+	wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_LEAKAGE_CTL, 3);
+}
+
+static void wcd9xxx_bring_down(struct wcd9xxx *wcd9xxx)
+{
+	wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_LEAKAGE_CTL, 0x7);
+	wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_LEAKAGE_CTL, 0x6);
+	wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_LEAKAGE_CTL, 0xe);
+	wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_LEAKAGE_CTL, 0x8);
+}
+
+static int wcd9xxx_reset(struct wcd9xxx *wcd9xxx)
+{
+	int ret;
+
+	if (wcd9xxx->reset_gpio) {
+		ret = gpio_request(wcd9xxx->reset_gpio, "CDC_RESET");
+		if (ret) {
+			pr_err("%s: Failed to request gpio %d\n", __func__,
+				wcd9xxx->reset_gpio);
+			wcd9xxx->reset_gpio = 0;
+			return ret;
+		}
+
+		gpio_direction_output(wcd9xxx->reset_gpio, 1);
+		msleep(20);
+		gpio_direction_output(wcd9xxx->reset_gpio, 0);
+		msleep(20);
+		gpio_direction_output(wcd9xxx->reset_gpio, 1);
+		msleep(20);
+	}
+	return 0;
+}
+
+static void wcd9xxx_free_reset(struct wcd9xxx *wcd9xxx)
+{
+	if (wcd9xxx->reset_gpio) {
+		gpio_free(wcd9xxx->reset_gpio);
+		wcd9xxx->reset_gpio = 0;
+	}
+}
+
+static int wcd9xxx_device_init(struct wcd9xxx *wcd9xxx, int irq)
+{
+	int ret;
+	u8 idbyte_0, idbyte_1, idbyte_2, idbyte_3;
+	struct mfd_cell *wcd9xxx_dev = NULL;
+	int wcd9xxx_dev_size = 0;
+
+	mutex_init(&wcd9xxx->io_lock);
+	mutex_init(&wcd9xxx->xfer_lock);
+
+	mutex_init(&wcd9xxx->pm_lock);
+	wcd9xxx->wlock_holders = 0;
+	wcd9xxx->pm_state = WCD9XXX_PM_SLEEPABLE;
+	init_waitqueue_head(&wcd9xxx->pm_wq);
+	wake_lock_init(&wcd9xxx->wlock, WAKE_LOCK_IDLE, "wcd9310-irq");
+
+	dev_set_drvdata(wcd9xxx->dev, wcd9xxx);
+
+	wcd9xxx_bring_up(wcd9xxx);
+
+	ret = wcd9xxx_irq_init(wcd9xxx);
+	if (ret) {
+		pr_err("IRQ initialization failed\n");
+		goto err;
+	}
+
+	idbyte_0 = wcd9xxx_reg_read(wcd9xxx, WCD9XXX_A_CHIP_ID_BYTE_0);
+	idbyte_1 = wcd9xxx_reg_read(wcd9xxx, WCD9XXX_A_CHIP_ID_BYTE_1);
+	idbyte_2 = wcd9xxx_reg_read(wcd9xxx, WCD9XXX_A_CHIP_ID_BYTE_2);
+	idbyte_3 = wcd9xxx_reg_read(wcd9xxx, WCD9XXX_A_CHIP_ID_BYTE_3);
+
+	wcd9xxx->version = wcd9xxx_reg_read(wcd9xxx,
+			WCD9XXX_A_CHIP_VERSION) & 0x1F;
+	pr_info("%s : Codec version %u initialized\n",
+		__func__, wcd9xxx->version);
+	pr_info("idbyte_0[%08x] idbyte_1[%08x] idbyte_2[%08x] idbyte_3[%08x]\n",
+			idbyte_0, idbyte_1, idbyte_2, idbyte_3);
+
+	if (wcd9xxx->slim != NULL) {
+		if (!strncmp(wcd9xxx->slim->name, "tabla", 5)) {
+			if (TABLA_IS_1_X(wcd9xxx->version)) {
+				wcd9xxx_dev = tabla1x_devs;
+				wcd9xxx_dev_size = ARRAY_SIZE(tabla1x_devs);
+			} else {
+				wcd9xxx_dev = tabla_devs;
+				wcd9xxx_dev_size = ARRAY_SIZE(tabla_devs);
+			}
+		} else {
+			wcd9xxx_dev = sitar_devs;
+			wcd9xxx_dev_size = ARRAY_SIZE(sitar_devs);
+		}
+	} else {
+		/* Need to add here check for Tabla.
+		 * For now the read of version takes
+		 * care of now only tabla.
+		 */
+		pr_debug("%s : Read codec version using I2C\n",	__func__);
+		if (TABLA_IS_1_X(wcd9xxx->version)) {
+			wcd9xxx_dev = tabla1x_devs;
+			wcd9xxx_dev_size = ARRAY_SIZE(tabla1x_devs);
+		} else if (TABLA_IS_2_0(wcd9xxx->version)) {
+			wcd9xxx_dev = tabla_devs;
+			wcd9xxx_dev_size = ARRAY_SIZE(tabla_devs);
+		} else {
+			wcd9xxx_dev = sitar_devs;
+			wcd9xxx_dev_size = ARRAY_SIZE(sitar_devs);
+		}
+	}
+
+	ret = mfd_add_devices(wcd9xxx->dev, -1,
+		      wcd9xxx_dev, wcd9xxx_dev_size,
+		      NULL, 0);
+	if (ret != 0) {
+		dev_err(wcd9xxx->dev, "Failed to add children: %d\n", ret);
+		goto err_irq;
+	}
+	return ret;
+err_irq:
+	wcd9xxx_irq_exit(wcd9xxx);
+err:
+	wcd9xxx_bring_down(wcd9xxx);
+	wake_lock_destroy(&wcd9xxx->wlock);
+	mutex_destroy(&wcd9xxx->pm_lock);
+	mutex_destroy(&wcd9xxx->io_lock);
+	mutex_destroy(&wcd9xxx->xfer_lock);
+	return ret;
+}
+
+static void wcd9xxx_device_exit(struct wcd9xxx *wcd9xxx)
+{
+	wcd9xxx_irq_exit(wcd9xxx);
+	wcd9xxx_bring_down(wcd9xxx);
+	wcd9xxx_free_reset(wcd9xxx);
+	mutex_destroy(&wcd9xxx->pm_lock);
+	wake_lock_destroy(&wcd9xxx->wlock);
+	mutex_destroy(&wcd9xxx->io_lock);
+	mutex_destroy(&wcd9xxx->xfer_lock);
+	if (wcd9xxx_intf == WCD9XXX_INTERFACE_TYPE_SLIMBUS)
+		slim_remove_device(wcd9xxx->slim_slave);
+	kfree(wcd9xxx);
+}
+
+
+#ifdef CONFIG_DEBUG_FS
+struct wcd9xxx *debugCodec;
+
+static struct dentry *debugfs_wcd9xxx_dent;
+static struct dentry *debugfs_peek;
+static struct dentry *debugfs_poke;
+
+static unsigned char read_data;
+
+static int codec_debug_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static int get_parameters(char *buf, long int *param1, int num_of_par)
+{
+	char *token;
+	int base, cnt;
+
+	token = strsep(&buf, " ");
+
+	for (cnt = 0; cnt < num_of_par; cnt++) {
+		if (token != NULL) {
+			if ((token[1] == 'x') || (token[1] == 'X'))
+				base = 16;
+			else
+				base = 10;
+
+			if (strict_strtoul(token, base, &param1[cnt]) != 0)
+				return -EINVAL;
+
+			token = strsep(&buf, " ");
+		} else
+			return -EINVAL;
+	}
+	return 0;
+}
+
+static ssize_t codec_debug_read(struct file *file, char __user *ubuf,
+				size_t count, loff_t *ppos)
+{
+	char lbuf[8];
+
+	snprintf(lbuf, sizeof(lbuf), "0x%x\n", read_data);
+	return simple_read_from_buffer(ubuf, count, ppos, lbuf,
+		strnlen(lbuf, 7));
+}
+
+
+static ssize_t codec_debug_write(struct file *filp,
+	const char __user *ubuf, size_t cnt, loff_t *ppos)
+{
+	char *access_str = filp->private_data;
+	char lbuf[32];
+	int rc;
+	long int param[5];
+
+	if (cnt > sizeof(lbuf) - 1)
+		return -EINVAL;
+
+	rc = copy_from_user(lbuf, ubuf, cnt);
+	if (rc)
+		return -EFAULT;
+
+	lbuf[cnt] = '\0';
+
+	if (!strncmp(access_str, "poke", 6)) {
+		/* write */
+		rc = get_parameters(lbuf, param, 2);
+		if ((param[0] <= 0x3FF) && (param[1] <= 0xFF) &&
+			(rc == 0))
+			wcd9xxx_interface_reg_write(debugCodec, param[0],
+				param[1]);
+		else
+			rc = -EINVAL;
+	} else if (!strncmp(access_str, "peek", 6)) {
+		/* read */
+		rc = get_parameters(lbuf, param, 1);
+		if ((param[0] <= 0x3FF) && (rc == 0))
+			read_data = wcd9xxx_interface_reg_read(debugCodec,
+				param[0]);
+		else
+			rc = -EINVAL;
+	}
+
+	if (rc == 0)
+		rc = cnt;
+	else
+		pr_err("%s: rc = %d\n", __func__, rc);
+
+	return rc;
+}
+
+static const struct file_operations codec_debug_ops = {
+	.open = codec_debug_open,
+	.write = codec_debug_write,
+	.read = codec_debug_read
+};
+#endif
+
+static int wcd9xxx_enable_supplies(struct wcd9xxx *wcd9xxx,
+				struct wcd9xxx_pdata *pdata)
+{
+	int ret;
+	int i;
+	wcd9xxx->supplies = kzalloc(sizeof(struct regulator_bulk_data) *
+				   ARRAY_SIZE(pdata->regulator),
+				   GFP_KERNEL);
+	if (!wcd9xxx->supplies) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(pdata->regulator); i++)
+		wcd9xxx->supplies[i].supply = pdata->regulator[i].name;
+
+	ret = regulator_bulk_get(wcd9xxx->dev, ARRAY_SIZE(pdata->regulator),
+				 wcd9xxx->supplies);
+	if (ret != 0) {
+		dev_err(wcd9xxx->dev, "Failed to get supplies: err = %d\n",
+							ret);
+		goto err_supplies;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(pdata->regulator); i++) {
+		ret = regulator_set_voltage(wcd9xxx->supplies[i].consumer,
+			pdata->regulator[i].min_uV, pdata->regulator[i].max_uV);
+		if (ret) {
+			pr_err("%s: Setting regulator voltage failed for "
+				"regulator %s err = %d\n", __func__,
+				wcd9xxx->supplies[i].supply, ret);
+			goto err_get;
+		}
+
+		ret = regulator_set_optimum_mode(wcd9xxx->supplies[i].consumer,
+			pdata->regulator[i].optimum_uA);
+		if (ret < 0) {
+			pr_err("%s: Setting regulator optimum mode failed for "
+				"regulator %s err = %d\n", __func__,
+				wcd9xxx->supplies[i].supply, ret);
+			goto err_get;
+		}
+	}
+
+	ret = regulator_bulk_enable(ARRAY_SIZE(pdata->regulator),
+				    wcd9xxx->supplies);
+	if (ret != 0) {
+		dev_err(wcd9xxx->dev, "Failed to enable supplies: err = %d\n",
+				ret);
+		goto err_configure;
+	}
+	return ret;
+
+err_configure:
+	for (i = 0; i < ARRAY_SIZE(pdata->regulator); i++) {
+		regulator_set_voltage(wcd9xxx->supplies[i].consumer, 0,
+			pdata->regulator[i].max_uV);
+		regulator_set_optimum_mode(wcd9xxx->supplies[i].consumer, 0);
+	}
+err_get:
+	regulator_bulk_free(ARRAY_SIZE(pdata->regulator), wcd9xxx->supplies);
+err_supplies:
+	kfree(wcd9xxx->supplies);
+err:
+	return ret;
+}
+
+static void wcd9xxx_disable_supplies(struct wcd9xxx *wcd9xxx,
+				     struct wcd9xxx_pdata *pdata)
+{
+	int i;
+
+	regulator_bulk_disable(ARRAY_SIZE(pdata->regulator),
+				    wcd9xxx->supplies);
+	for (i = 0; i < ARRAY_SIZE(pdata->regulator); i++) {
+		regulator_set_voltage(wcd9xxx->supplies[i].consumer, 0,
+			pdata->regulator[i].max_uV);
+		regulator_set_optimum_mode(wcd9xxx->supplies[i].consumer, 0);
+	}
+	regulator_bulk_free(ARRAY_SIZE(pdata->regulator), wcd9xxx->supplies);
+	kfree(wcd9xxx->supplies);
+}
+
+int wcd9xxx_get_intf_type(void)
+{
+	return wcd9xxx_intf;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_get_intf_type);
+
+struct wcd9xxx_i2c *get_i2c_wcd9xxx_device_info(u16 reg)
+{
+	u16 mask = 0x0f00;
+	int value = 0;
+	struct wcd9xxx_i2c *wcd9xxx = NULL;
+	value = ((reg & mask) >> 8) & 0x000f;
+	switch (value) {
+	case 0:
+		wcd9xxx = &wcd9xxx_modules[0];
+		break;
+	case 1:
+		wcd9xxx = &wcd9xxx_modules[1];
+		break;
+	case 2:
+		wcd9xxx = &wcd9xxx_modules[2];
+		break;
+	case 3:
+		wcd9xxx = &wcd9xxx_modules[3];
+		break;
+	default:
+		break;
+	}
+	return wcd9xxx;
+}
+
+int wcd9xxx_i2c_write_device(u16 reg, u8 *value,
+				u32 bytes)
+{
+
+	struct i2c_msg *msg;
+	int ret = 0;
+	u8 reg_addr = 0;
+	u8 data[bytes + 1];
+	struct wcd9xxx_i2c *wcd9xxx;
+
+	wcd9xxx = get_i2c_wcd9xxx_device_info(reg);
+	if (wcd9xxx == NULL || wcd9xxx->client == NULL) {
+		pr_err("failed to get device info\n");
+		return -ENODEV;
+	}
+	reg_addr = (u8)reg;
+	msg = &wcd9xxx->xfer_msg[0];
+	msg->addr = wcd9xxx->client->addr;
+	msg->len = bytes + 1;
+	msg->flags = 0;
+	data[0] = reg;
+	data[1] = *value;
+	msg->buf = data;
+	ret = i2c_transfer(wcd9xxx->client->adapter, wcd9xxx->xfer_msg, 1);
+	/* Try again if the write fails */
+	if (ret != 1) {
+		ret = i2c_transfer(wcd9xxx->client->adapter,
+						wcd9xxx->xfer_msg, 1);
+		if (ret != 1) {
+			pr_err("failed to write the device\n");
+			return ret;
+		}
+	}
+	pr_debug("write sucess register = %x val = %x\n", reg, data[1]);
+	return 0;
+}
+
+
+int wcd9xxx_i2c_read_device(unsigned short reg,
+				  int bytes, unsigned char *dest)
+{
+	struct i2c_msg *msg;
+	int ret = 0;
+	u8 reg_addr = 0;
+	struct wcd9xxx_i2c *wcd9xxx;
+	u8 i = 0;
+
+	wcd9xxx = get_i2c_wcd9xxx_device_info(reg);
+	if (wcd9xxx == NULL || wcd9xxx->client == NULL) {
+		pr_err("failed to get device info\n");
+		return -ENODEV;
+	}
+	for (i = 0; i < bytes; i++) {
+		reg_addr = (u8)reg++;
+		msg = &wcd9xxx->xfer_msg[0];
+		msg->addr = wcd9xxx->client->addr;
+		msg->len = 1;
+		msg->flags = 0;
+		msg->buf = &reg_addr;
+
+		msg = &wcd9xxx->xfer_msg[1];
+		msg->addr = wcd9xxx->client->addr;
+		msg->len = 1;
+		msg->flags = I2C_M_RD;
+		msg->buf = dest++;
+		ret = i2c_transfer(wcd9xxx->client->adapter,
+				wcd9xxx->xfer_msg, 2);
+
+		/* Try again if read fails first time */
+		if (ret != 2) {
+			ret = i2c_transfer(wcd9xxx->client->adapter,
+							wcd9xxx->xfer_msg, 2);
+			if (ret != 2) {
+				pr_err("failed to read wcd9xxx register\n");
+				return ret;
+			}
+		}
+	}
+	return 0;
+}
+
+int wcd9xxx_i2c_read(struct wcd9xxx *wcd9xxx, unsigned short reg,
+			int bytes, void *dest, bool interface_reg)
+{
+	return wcd9xxx_i2c_read_device(reg, bytes, dest);
+}
+
+int wcd9xxx_i2c_write(struct wcd9xxx *wcd9xxx, unsigned short reg,
+			 int bytes, void *src, bool interface_reg)
+{
+	return wcd9xxx_i2c_write_device(reg, src, bytes);
+}
+
+static int __devinit wcd9xxx_i2c_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct wcd9xxx *wcd9xxx;
+	struct wcd9xxx_pdata *pdata = client->dev.platform_data;
+	int val = 0;
+	int ret = 0;
+	static int device_id;
+
+	if (wcd9xxx_intf == WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+		pr_info("tabla card is already detected in slimbus mode\n");
+		return -ENODEV;
+	}
+	if (device_id > 0) {
+		wcd9xxx_modules[device_id++].client = client;
+		pr_info("probe for other slaves devices of tabla\n");
+		return ret;
+	}
+
+	wcd9xxx = kzalloc(sizeof(struct wcd9xxx), GFP_KERNEL);
+	if (wcd9xxx == NULL) {
+		pr_err("%s: error, allocation failed\n", __func__);
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	if (!pdata) {
+		dev_dbg(&client->dev, "no platform data?\n");
+		ret = -EINVAL;
+		goto fail;
+	}
+	if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) == 0) {
+		dev_dbg(&client->dev, "can't talk I2C?\n");
+		ret = -EIO;
+		goto fail;
+	}
+	dev_set_drvdata(&client->dev, wcd9xxx);
+	wcd9xxx->dev = &client->dev;
+	wcd9xxx->reset_gpio = pdata->reset_gpio;
+	ret = wcd9xxx_enable_supplies(wcd9xxx, pdata);
+	if (ret) {
+		pr_err("%s: Fail to enable Codec supplies\n", __func__);
+		goto err_codec;
+	}
+
+	usleep_range(5, 5);
+	ret = wcd9xxx_reset(wcd9xxx);
+	if (ret) {
+		pr_err("%s: Resetting Codec failed\n", __func__);
+		goto err_supplies;
+	}
+	wcd9xxx_modules[device_id++].client = client;
+
+	wcd9xxx->read_dev = wcd9xxx_i2c_read;
+	wcd9xxx->write_dev = wcd9xxx_i2c_write;
+	wcd9xxx->irq = pdata->irq;
+	wcd9xxx->irq_base = pdata->irq_base;
+
+	/*read the tabla status before initializing the device type*/
+	ret = wcd9xxx_read(wcd9xxx, WCD9XXX_A_CHIP_STATUS, 1, &val, 0);
+	if ((ret < 0) || (val != WCD9XXX_I2C_MODE)) {
+		pr_err("failed to read the wcd9xxx status\n");
+		goto err_device_init;
+	}
+
+	ret = wcd9xxx_device_init(wcd9xxx, wcd9xxx->irq);
+	if (ret) {
+		pr_err("%s: error, initializing device failed\n", __func__);
+		goto err_device_init;
+	}
+	wcd9xxx_intf = WCD9XXX_INTERFACE_TYPE_I2C;
+
+	return ret;
+err_device_init:
+	wcd9xxx_free_reset(wcd9xxx);
+err_supplies:
+	wcd9xxx_disable_supplies(wcd9xxx, pdata);
+err_codec:
+	kfree(wcd9xxx);
+fail:
+	return ret;
+}
+
+static int __devexit wcd9xxx_i2c_remove(struct i2c_client *client)
+{
+	struct wcd9xxx *wcd9xxx;
+	struct wcd9xxx_pdata *pdata = client->dev.platform_data;
+	pr_debug("exit\n");
+	wcd9xxx = dev_get_drvdata(&client->dev);
+	wcd9xxx_disable_supplies(wcd9xxx, pdata);
+	wcd9xxx_device_exit(wcd9xxx);
+	return 0;
+}
+
+static int wcd9xxx_slim_probe(struct slim_device *slim)
+{
+	struct wcd9xxx *wcd9xxx;
+	struct wcd9xxx_pdata *pdata;
+	int ret = 0;
+	int sgla_retry_cnt;
+
+	dev_info(&slim->dev, "Initialized slim device %s\n", slim->name);
+	pdata = slim->dev.platform_data;
+
+	if (!pdata) {
+		dev_err(&slim->dev, "Error, no platform data\n");
+		ret = -EINVAL;
+		goto err;
+	}
+
+	wcd9xxx = kzalloc(sizeof(struct wcd9xxx), GFP_KERNEL);
+	if (wcd9xxx == NULL) {
+		pr_err("%s: error, allocation failed\n", __func__);
+		ret = -ENOMEM;
+		goto err;
+	}
+	if (!slim->ctrl) {
+		pr_err("Error, no SLIMBUS control data\n");
+		ret = -EINVAL;
+		goto err_codec;
+	}
+	wcd9xxx->slim = slim;
+	slim_set_clientdata(slim, wcd9xxx);
+	wcd9xxx->reset_gpio = pdata->reset_gpio;
+	wcd9xxx->dev = &slim->dev;
+
+	ret = wcd9xxx_enable_supplies(wcd9xxx, pdata);
+	if (ret)
+		goto err_codec;
+	usleep_range(5, 5);
+
+	ret = wcd9xxx_reset(wcd9xxx);
+	if (ret) {
+		pr_err("%s: Resetting Codec failed\n", __func__);
+		goto err_supplies;
+	}
+
+	ret = slim_get_logical_addr(wcd9xxx->slim, wcd9xxx->slim->e_addr,
+		ARRAY_SIZE(wcd9xxx->slim->e_addr), &wcd9xxx->slim->laddr);
+	if (ret) {
+		pr_err("fail to get slimbus logical address %d\n", ret);
+		goto err_reset;
+	}
+	wcd9xxx->read_dev = wcd9xxx_slim_read_device;
+	wcd9xxx->write_dev = wcd9xxx_slim_write_device;
+	wcd9xxx->irq = pdata->irq;
+	wcd9xxx->irq_base = pdata->irq_base;
+	wcd9xxx_pgd_la = wcd9xxx->slim->laddr;
+
+	if (pdata->num_irqs < TABLA_NUM_IRQS) {
+		pr_err("%s: Error, not enough interrupt lines allocated\n",
+			__func__);
+		goto err_reset;
+	}
+
+	wcd9xxx->slim_slave = &pdata->slimbus_slave_device;
+
+	ret = slim_add_device(slim->ctrl, wcd9xxx->slim_slave);
+	if (ret) {
+		pr_err("%s: error, adding SLIMBUS device failed\n", __func__);
+		goto err_reset;
+	}
+
+	sgla_retry_cnt = 0;
+
+	while (1) {
+		ret = slim_get_logical_addr(wcd9xxx->slim_slave,
+			wcd9xxx->slim_slave->e_addr,
+			ARRAY_SIZE(wcd9xxx->slim_slave->e_addr),
+			&wcd9xxx->slim_slave->laddr);
+		if (ret) {
+			if (sgla_retry_cnt++ < WCD9XXX_SLIM_GLA_MAX_RETRIES) {
+				/* Give SLIMBUS slave time to report present
+				   and be ready.
+				 */
+				usleep_range(1000, 1000);
+				pr_debug("%s: retry slim_get_logical_addr()\n",
+					__func__);
+				continue;
+			}
+			pr_err("fail to get slimbus slave logical address"
+				" %d\n", ret);
+			goto err_slim_add;
+		}
+		break;
+	}
+	wcd9xxx_inf_la = wcd9xxx->slim_slave->laddr;
+	wcd9xxx_intf = WCD9XXX_INTERFACE_TYPE_SLIMBUS;
+
+	ret = wcd9xxx_device_init(wcd9xxx, wcd9xxx->irq);
+	if (ret) {
+		pr_err("%s: error, initializing device failed\n", __func__);
+		goto err_slim_add;
+	}
+	wcd9xxx_init_slimslave(wcd9xxx, wcd9xxx_pgd_la);
+#ifdef CONFIG_DEBUG_FS
+	debugCodec = wcd9xxx;
+
+	debugfs_wcd9xxx_dent = debugfs_create_dir
+		("wcd9310_slimbus_interface_device", 0);
+	if (!IS_ERR(debugfs_wcd9xxx_dent)) {
+		debugfs_peek = debugfs_create_file("peek",
+		S_IFREG | S_IRUGO, debugfs_wcd9xxx_dent,
+		(void *) "peek", &codec_debug_ops);
+
+		debugfs_poke = debugfs_create_file("poke",
+		S_IFREG | S_IRUGO, debugfs_wcd9xxx_dent,
+		(void *) "poke", &codec_debug_ops);
+	}
+#endif
+
+	return ret;
+
+err_slim_add:
+	slim_remove_device(wcd9xxx->slim_slave);
+err_reset:
+	wcd9xxx_free_reset(wcd9xxx);
+err_supplies:
+	wcd9xxx_disable_supplies(wcd9xxx, pdata);
+err_codec:
+	kfree(wcd9xxx);
+err:
+	return ret;
+}
+static int wcd9xxx_slim_remove(struct slim_device *pdev)
+{
+	struct wcd9xxx *wcd9xxx;
+	struct wcd9xxx_pdata *pdata = pdev->dev.platform_data;
+
+#ifdef CONFIG_DEBUG_FS
+	debugfs_remove(debugfs_peek);
+	debugfs_remove(debugfs_poke);
+	debugfs_remove(debugfs_wcd9xxx_dent);
+#endif
+	wcd9xxx = slim_get_devicedata(pdev);
+	wcd9xxx_deinit_slimslave(wcd9xxx);
+	slim_remove_device(wcd9xxx->slim_slave);
+	wcd9xxx_disable_supplies(wcd9xxx, pdata);
+	wcd9xxx_device_exit(wcd9xxx);
+	return 0;
+}
+
+static int wcd9xxx_resume(struct wcd9xxx *wcd9xxx)
+{
+	int ret = 0;
+
+	pr_debug("%s: enter\n", __func__);
+	mutex_lock(&wcd9xxx->pm_lock);
+	if (wcd9xxx->pm_state == WCD9XXX_PM_ASLEEP) {
+		pr_debug("%s: resuming system, state %d, wlock %d\n", __func__,
+			 wcd9xxx->pm_state, wcd9xxx->wlock_holders);
+		wcd9xxx->pm_state = WCD9XXX_PM_SLEEPABLE;
+	} else {
+		pr_warn("%s: system is already awake, state %d wlock %d\n",
+			__func__, wcd9xxx->pm_state, wcd9xxx->wlock_holders);
+	}
+	mutex_unlock(&wcd9xxx->pm_lock);
+	wake_up_all(&wcd9xxx->pm_wq);
+
+	return ret;
+}
+
+static int wcd9xxx_slim_resume(struct slim_device *sldev)
+{
+	struct wcd9xxx *wcd9xxx = slim_get_devicedata(sldev);
+	return wcd9xxx_resume(wcd9xxx);
+}
+
+static int wcd9xxx_i2c_resume(struct i2c_client *i2cdev)
+{
+	struct wcd9xxx *wcd9xxx = dev_get_drvdata(&i2cdev->dev);
+	if (wcd9xxx)
+		return wcd9xxx_resume(wcd9xxx);
+	else
+		return 0;
+}
+
+static int wcd9xxx_suspend(struct wcd9xxx *wcd9xxx, pm_message_t pmesg)
+{
+	int ret = 0;
+
+	pr_debug("%s: enter\n", __func__);
+	/* wake_lock() can be called after this suspend chain call started.
+	 * thus suspend can be called while wlock is being held */
+	mutex_lock(&wcd9xxx->pm_lock);
+	if (wcd9xxx->pm_state == WCD9XXX_PM_SLEEPABLE) {
+		pr_debug("%s: suspending system, state %d, wlock %d\n",
+			 __func__, wcd9xxx->pm_state, wcd9xxx->wlock_holders);
+		wcd9xxx->pm_state = WCD9XXX_PM_ASLEEP;
+	} else if (wcd9xxx->pm_state == WCD9XXX_PM_AWAKE) {
+		/* unlock to wait for pm_state == WCD9XXX_PM_SLEEPABLE
+		 * then set to WCD9XXX_PM_ASLEEP */
+		pr_debug("%s: waiting to suspend system, state %d, wlock %d\n",
+			 __func__, wcd9xxx->pm_state, wcd9xxx->wlock_holders);
+		mutex_unlock(&wcd9xxx->pm_lock);
+		if (!(wait_event_timeout(wcd9xxx->pm_wq,
+					 wcd9xxx_pm_cmpxchg(wcd9xxx,
+						  WCD9XXX_PM_SLEEPABLE,
+						  WCD9XXX_PM_ASLEEP) ==
+							WCD9XXX_PM_SLEEPABLE,
+					 HZ))) {
+			pr_debug("%s: suspend failed state %d, wlock %d\n",
+				 __func__, wcd9xxx->pm_state,
+				 wcd9xxx->wlock_holders);
+			ret = -EBUSY;
+		} else {
+			pr_debug("%s: done, state %d, wlock %d\n", __func__,
+				 wcd9xxx->pm_state, wcd9xxx->wlock_holders);
+		}
+		mutex_lock(&wcd9xxx->pm_lock);
+	} else if (wcd9xxx->pm_state == WCD9XXX_PM_ASLEEP) {
+		pr_warn("%s: system is already suspended, state %d, wlock %dn",
+			__func__, wcd9xxx->pm_state, wcd9xxx->wlock_holders);
+	}
+	mutex_unlock(&wcd9xxx->pm_lock);
+
+	return ret;
+}
+
+static int wcd9xxx_slim_suspend(struct slim_device *sldev, pm_message_t pmesg)
+{
+	struct wcd9xxx *wcd9xxx = slim_get_devicedata(sldev);
+	return wcd9xxx_suspend(wcd9xxx, pmesg);
+}
+
+static int wcd9xxx_i2c_suspend(struct i2c_client *i2cdev, pm_message_t pmesg)
+{
+	struct wcd9xxx *wcd9xxx = dev_get_drvdata(&i2cdev->dev);
+	if (wcd9xxx)
+		return wcd9xxx_suspend(wcd9xxx, pmesg);
+	else
+		return 0;
+}
+
+static const struct slim_device_id sitar_slimtest_id[] = {
+	{"sitar-slim", 0},
+	{}
+};
+static struct slim_driver sitar_slim_driver = {
+	.driver = {
+		.name = "sitar-slim",
+		.owner = THIS_MODULE,
+	},
+	.probe = wcd9xxx_slim_probe,
+	.remove = wcd9xxx_slim_remove,
+	.id_table = sitar_slimtest_id,
+	.resume = wcd9xxx_slim_resume,
+	.suspend = wcd9xxx_slim_suspend,
+};
+
+static const struct slim_device_id sitar1p1_slimtest_id[] = {
+	{"sitar1p1-slim", 0},
+	{}
+};
+static struct slim_driver sitar1p1_slim_driver = {
+	.driver = {
+		.name = "sitar1p1-slim",
+		.owner = THIS_MODULE,
+	},
+	.probe = wcd9xxx_slim_probe,
+	.remove = wcd9xxx_slim_remove,
+	.id_table = sitar1p1_slimtest_id,
+	.resume = wcd9xxx_slim_resume,
+	.suspend = wcd9xxx_slim_suspend,
+};
+
+static const struct slim_device_id slimtest_id[] = {
+	{"tabla-slim", 0},
+	{}
+};
+
+static struct slim_driver tabla_slim_driver = {
+	.driver = {
+		.name = "tabla-slim",
+		.owner = THIS_MODULE,
+	},
+	.probe = wcd9xxx_slim_probe,
+	.remove = wcd9xxx_slim_remove,
+	.id_table = slimtest_id,
+	.resume = wcd9xxx_slim_resume,
+	.suspend = wcd9xxx_slim_suspend,
+};
+
+static const struct slim_device_id slimtest2x_id[] = {
+	{"tabla2x-slim", 0},
+	{}
+};
+
+static struct slim_driver tabla2x_slim_driver = {
+	.driver = {
+		.name = "tabla2x-slim",
+		.owner = THIS_MODULE,
+	},
+	.probe = wcd9xxx_slim_probe,
+	.remove = wcd9xxx_slim_remove,
+	.id_table = slimtest2x_id,
+	.resume = wcd9xxx_slim_resume,
+	.suspend = wcd9xxx_slim_suspend,
+};
+
+#define TABLA_I2C_TOP_LEVEL 0
+#define TABLA_I2C_ANALOG       1
+#define TABLA_I2C_DIGITAL_1    2
+#define TABLA_I2C_DIGITAL_2    3
+
+static struct i2c_device_id tabla_id_table[] = {
+	{"tabla top level", TABLA_I2C_TOP_LEVEL},
+	{"tabla analog", TABLA_I2C_TOP_LEVEL},
+	{"tabla digital1", TABLA_I2C_TOP_LEVEL},
+	{"tabla digital2", TABLA_I2C_TOP_LEVEL},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, tabla_id_table);
+
+static struct i2c_driver tabla_i2c_driver = {
+	.driver                 = {
+		.owner          =       THIS_MODULE,
+		.name           =       "tabla-i2c-core",
+	},
+	.id_table               =       tabla_id_table,
+	.probe                  =       wcd9xxx_i2c_probe,
+	.remove                 =       __devexit_p(wcd9xxx_i2c_remove),
+	.resume	= wcd9xxx_i2c_resume,
+	.suspend = wcd9xxx_i2c_suspend,
+};
+
+static int __init wcd9xxx_init(void)
+{
+	int ret1, ret2, ret3, ret4, ret5;
+
+	ret1 = slim_driver_register(&tabla_slim_driver);
+	if (ret1 != 0)
+		pr_err("Failed to register tabla SB driver: %d\n", ret1);
+
+	ret2 = slim_driver_register(&tabla2x_slim_driver);
+	if (ret2 != 0)
+		pr_err("Failed to register tabla2x SB driver: %d\n", ret2);
+
+	ret3 = i2c_add_driver(&tabla_i2c_driver);
+	if (ret3 != 0)
+		pr_err("failed to add the I2C driver\n");
+
+	ret4 = slim_driver_register(&sitar_slim_driver);
+	if (ret4 != 0)
+		pr_err("Failed to register sitar SB driver: %d\n", ret4);
+
+	ret5 = slim_driver_register(&sitar1p1_slim_driver);
+	if (ret5 != 0)
+		pr_err("Failed to register sitar SB driver: %d\n", ret5);
+
+	return (ret1 && ret2 && ret3 && ret4 && ret5) ? -1 : 0;
+}
+module_init(wcd9xxx_init);
+
+static void __exit wcd9xxx_exit(void)
+{
+}
+module_exit(wcd9xxx_exit);
+
+MODULE_DESCRIPTION("Codec core driver");
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/wcd9xxx-irq.c b/drivers/mfd/wcd9xxx-irq.c
new file mode 100644
index 0000000..ba3c1e8
--- /dev/null
+++ b/drivers/mfd/wcd9xxx-irq.c
@@ -0,0 +1,307 @@
+/* Copyright (c) 2011-2012, 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.
+ */
+#include <linux/bitops.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/irq.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
+#include <linux/mfd/wcd9xxx/wcd9310_registers.h>
+#include <linux/interrupt.h>
+
+#define BYTE_BIT_MASK(nr)		(1UL << ((nr) % BITS_PER_BYTE))
+#define BIT_BYTE(nr)			((nr) / BITS_PER_BYTE)
+
+struct wcd9xxx_irq {
+	bool level;
+};
+
+static struct wcd9xxx_irq wcd9xxx_irqs[TABLA_NUM_IRQS] = {
+	[0] = { .level = 1},
+/* All other wcd9xxx interrupts are edge triggered */
+};
+
+static inline int irq_to_wcd9xxx_irq(struct wcd9xxx *wcd9xxx, int irq)
+{
+	return irq - wcd9xxx->irq_base;
+}
+
+static void wcd9xxx_irq_lock(struct irq_data *data)
+{
+	struct wcd9xxx *wcd9xxx = irq_data_get_irq_chip_data(data);
+	mutex_lock(&wcd9xxx->irq_lock);
+}
+
+static void wcd9xxx_irq_sync_unlock(struct irq_data *data)
+{
+	struct wcd9xxx *wcd9xxx = irq_data_get_irq_chip_data(data);
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(wcd9xxx->irq_masks_cur); i++) {
+		/* If there's been a change in the mask write it back
+		 * to the hardware.
+		 */
+		if (wcd9xxx->irq_masks_cur[i] != wcd9xxx->irq_masks_cache[i]) {
+			wcd9xxx->irq_masks_cache[i] = wcd9xxx->irq_masks_cur[i];
+			wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_MASK0+i,
+				wcd9xxx->irq_masks_cur[i]);
+		}
+	}
+
+	mutex_unlock(&wcd9xxx->irq_lock);
+}
+
+static void wcd9xxx_irq_enable(struct irq_data *data)
+{
+	struct wcd9xxx *wcd9xxx = irq_data_get_irq_chip_data(data);
+	int wcd9xxx_irq = irq_to_wcd9xxx_irq(wcd9xxx, data->irq);
+	wcd9xxx->irq_masks_cur[BIT_BYTE(wcd9xxx_irq)] &=
+		~(BYTE_BIT_MASK(wcd9xxx_irq));
+}
+
+static void wcd9xxx_irq_disable(struct irq_data *data)
+{
+	struct wcd9xxx *wcd9xxx = irq_data_get_irq_chip_data(data);
+	int wcd9xxx_irq = irq_to_wcd9xxx_irq(wcd9xxx, data->irq);
+	wcd9xxx->irq_masks_cur[BIT_BYTE(wcd9xxx_irq)]
+			|= BYTE_BIT_MASK(wcd9xxx_irq);
+}
+
+static struct irq_chip wcd9xxx_irq_chip = {
+	.name = "wcd9xxx",
+	.irq_bus_lock = wcd9xxx_irq_lock,
+	.irq_bus_sync_unlock = wcd9xxx_irq_sync_unlock,
+	.irq_disable = wcd9xxx_irq_disable,
+	.irq_enable = wcd9xxx_irq_enable,
+};
+
+enum wcd9xxx_pm_state wcd9xxx_pm_cmpxchg(struct wcd9xxx *wcd9xxx,
+		enum wcd9xxx_pm_state o,
+		enum wcd9xxx_pm_state n)
+{
+	enum wcd9xxx_pm_state old;
+	mutex_lock(&wcd9xxx->pm_lock);
+	old = wcd9xxx->pm_state;
+	if (old == o)
+		wcd9xxx->pm_state = n;
+	mutex_unlock(&wcd9xxx->pm_lock);
+	return old;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_pm_cmpxchg);
+
+bool wcd9xxx_lock_sleep(struct wcd9xxx *wcd9xxx)
+{
+	enum wcd9xxx_pm_state os;
+
+	/* wcd9xxx_{lock/unlock}_sleep will be called by wcd9xxx_irq_thread
+	 * and its subroutines only motly.
+	 * but btn0_lpress_fn is not wcd9xxx_irq_thread's subroutine and
+	 * it can race with wcd9xxx_irq_thread.
+	 * so need to embrace wlock_holders with mutex.
+	 */
+	mutex_lock(&wcd9xxx->pm_lock);
+	if (wcd9xxx->wlock_holders++ == 0) {
+		pr_debug("%s: holding wake lock\n", __func__);
+		wake_lock(&wcd9xxx->wlock);
+	}
+	mutex_unlock(&wcd9xxx->pm_lock);
+	if (!wait_event_timeout(wcd9xxx->pm_wq,
+			((os = wcd9xxx_pm_cmpxchg(wcd9xxx, WCD9XXX_PM_SLEEPABLE,
+						WCD9XXX_PM_AWAKE)) ==
+						    WCD9XXX_PM_SLEEPABLE ||
+			 (os == WCD9XXX_PM_AWAKE)),
+			5 * HZ)) {
+		pr_err("%s: system didn't resume within 5000ms, state %d, "
+		       "wlock %d\n", __func__, wcd9xxx->pm_state,
+		       wcd9xxx->wlock_holders);
+		WARN_ON(1);
+		wcd9xxx_unlock_sleep(wcd9xxx);
+		return false;
+	}
+	wake_up_all(&wcd9xxx->pm_wq);
+	return true;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_lock_sleep);
+
+void wcd9xxx_unlock_sleep(struct wcd9xxx *wcd9xxx)
+{
+	mutex_lock(&wcd9xxx->pm_lock);
+	if (--wcd9xxx->wlock_holders == 0) {
+		wcd9xxx->pm_state = WCD9XXX_PM_SLEEPABLE;
+		pr_debug("%s: releasing wake lock\n", __func__);
+		wake_unlock(&wcd9xxx->wlock);
+	}
+	mutex_unlock(&wcd9xxx->pm_lock);
+	wake_up_all(&wcd9xxx->pm_wq);
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_unlock_sleep);
+
+static void wcd9xxx_irq_dispatch(struct wcd9xxx *wcd9xxx, int irqbit)
+{
+	if ((irqbit <= TABLA_IRQ_MBHC_INSERTION) &&
+	    (irqbit >= TABLA_IRQ_MBHC_REMOVAL)) {
+		wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_CLEAR0 +
+				  BIT_BYTE(irqbit), BYTE_BIT_MASK(irqbit));
+		if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C)
+			wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_MODE, 0x02);
+		handle_nested_irq(wcd9xxx->irq_base + irqbit);
+	} else {
+		handle_nested_irq(wcd9xxx->irq_base + irqbit);
+		wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_CLEAR0 +
+				  BIT_BYTE(irqbit), BYTE_BIT_MASK(irqbit));
+		if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C)
+			wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_MODE, 0x02);
+	}
+}
+
+static irqreturn_t wcd9xxx_irq_thread(int irq, void *data)
+{
+	int ret;
+	struct wcd9xxx *wcd9xxx = data;
+	u8 status[WCD9XXX_NUM_IRQ_REGS];
+	int i;
+
+	if (unlikely(wcd9xxx_lock_sleep(wcd9xxx) == false)) {
+		dev_err(wcd9xxx->dev, "Failed to hold suspend\n");
+		return IRQ_NONE;
+	}
+	ret = wcd9xxx_bulk_read(wcd9xxx, TABLA_A_INTR_STATUS0,
+			       WCD9XXX_NUM_IRQ_REGS, status);
+	if (ret < 0) {
+		dev_err(wcd9xxx->dev, "Failed to read interrupt status: %d\n",
+			ret);
+		wcd9xxx_unlock_sleep(wcd9xxx);
+		return IRQ_NONE;
+	}
+	/* Apply masking */
+	for (i = 0; i < WCD9XXX_NUM_IRQ_REGS; i++)
+		status[i] &= ~wcd9xxx->irq_masks_cur[i];
+
+	/* Find out which interrupt was triggered and call that interrupt's
+	 * handler function
+	 */
+	if (status[BIT_BYTE(TABLA_IRQ_SLIMBUS)] &
+	    BYTE_BIT_MASK(TABLA_IRQ_SLIMBUS))
+		wcd9xxx_irq_dispatch(wcd9xxx, TABLA_IRQ_SLIMBUS);
+
+	/* Since codec has only one hardware irq line which is shared by
+	 * codec's different internal interrupts, so it's possible master irq
+	 * handler dispatches multiple nested irq handlers after breaking
+	 * order.  Dispatch MBHC interrupts order to follow MBHC state
+	 * machine's order */
+	for (i = TABLA_IRQ_MBHC_INSERTION; i >= TABLA_IRQ_MBHC_REMOVAL; i--) {
+		if (status[BIT_BYTE(i)] & BYTE_BIT_MASK(i))
+			wcd9xxx_irq_dispatch(wcd9xxx, i);
+	}
+	for (i = TABLA_IRQ_BG_PRECHARGE; i < TABLA_NUM_IRQS; i++) {
+		if (status[BIT_BYTE(i)] & BYTE_BIT_MASK(i))
+			wcd9xxx_irq_dispatch(wcd9xxx, i);
+	}
+	wcd9xxx_unlock_sleep(wcd9xxx);
+
+	return IRQ_HANDLED;
+}
+
+int wcd9xxx_irq_init(struct wcd9xxx *wcd9xxx)
+{
+	int ret;
+	unsigned int i, cur_irq;
+
+	mutex_init(&wcd9xxx->irq_lock);
+
+	if (!wcd9xxx->irq) {
+		dev_warn(wcd9xxx->dev,
+			 "No interrupt specified, no interrupts\n");
+		wcd9xxx->irq_base = 0;
+		return 0;
+	}
+
+	if (!wcd9xxx->irq_base) {
+		dev_err(wcd9xxx->dev,
+			"No interrupt base specified, no interrupts\n");
+		return 0;
+	}
+	/* Mask the individual interrupt sources */
+	for (i = 0, cur_irq = wcd9xxx->irq_base; i < TABLA_NUM_IRQS; i++,
+		cur_irq++) {
+
+		irq_set_chip_data(cur_irq, wcd9xxx);
+
+		if (wcd9xxx_irqs[i].level)
+			irq_set_chip_and_handler(cur_irq, &wcd9xxx_irq_chip,
+					 handle_level_irq);
+		else
+			irq_set_chip_and_handler(cur_irq, &wcd9xxx_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
+		set_irq_noprobe(cur_irq);
+#endif
+
+		wcd9xxx->irq_masks_cur[BIT_BYTE(i)] |= BYTE_BIT_MASK(i);
+		wcd9xxx->irq_masks_cache[BIT_BYTE(i)] |= BYTE_BIT_MASK(i);
+		wcd9xxx->irq_level[BIT_BYTE(i)] |= wcd9xxx_irqs[i].level <<
+			(i % BITS_PER_BYTE);
+	}
+	for (i = 0; i < WCD9XXX_NUM_IRQ_REGS; i++) {
+		/* Initialize interrupt mask and level registers */
+		wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_LEVEL0 + i,
+			wcd9xxx->irq_level[i]);
+		wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_MASK0 + i,
+			wcd9xxx->irq_masks_cur[i]);
+	}
+
+	ret = request_threaded_irq(wcd9xxx->irq, NULL, wcd9xxx_irq_thread,
+				   IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+				   "wcd9xxx", wcd9xxx);
+	if (ret != 0)
+		dev_err(wcd9xxx->dev, "Failed to request IRQ %d: %d\n",
+			wcd9xxx->irq, ret);
+	else {
+		ret = enable_irq_wake(wcd9xxx->irq);
+		if (ret == 0) {
+			ret = device_init_wakeup(wcd9xxx->dev, 1);
+			if (ret) {
+				dev_err(wcd9xxx->dev, "Failed to init device"
+					"wakeup : %d\n", ret);
+				disable_irq_wake(wcd9xxx->irq);
+			}
+		} else
+			dev_err(wcd9xxx->dev, "Failed to set wake interrupt on"
+				" IRQ %d: %d\n", wcd9xxx->irq, ret);
+		if (ret)
+			free_irq(wcd9xxx->irq, wcd9xxx);
+	}
+
+	if (ret)
+		mutex_destroy(&wcd9xxx->irq_lock);
+
+	return ret;
+}
+
+void wcd9xxx_irq_exit(struct wcd9xxx *wcd9xxx)
+{
+	if (wcd9xxx->irq) {
+		disable_irq_wake(wcd9xxx->irq);
+		free_irq(wcd9xxx->irq, wcd9xxx);
+		device_init_wakeup(wcd9xxx->dev, 0);
+	}
+	mutex_destroy(&wcd9xxx->irq_lock);
+}
diff --git a/drivers/mfd/wcd9xxx-slimslave.c b/drivers/mfd/wcd9xxx-slimslave.c
new file mode 100644
index 0000000..538ca7c
--- /dev/null
+++ b/drivers/mfd/wcd9xxx-slimslave.c
@@ -0,0 +1,528 @@
+/* Copyright (c) 2012, 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.
+ */
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx-slimslave.h>
+
+struct wcd9xxx_slim_sch_rx {
+	u32 sph;
+	u32 ch_num;
+	u16 ch_h;
+	u16 grph;
+};
+
+struct wcd9xxx_slim_sch_tx {
+	u32 sph;
+	u32 ch_num;
+	u16 ch_h;
+	u16 grph;
+};
+
+struct wcd9xxx_slim_sch {
+	struct wcd9xxx_slim_sch_rx rx[SLIM_MAX_RX_PORTS];
+	struct wcd9xxx_slim_sch_tx tx[SLIM_MAX_TX_PORTS];
+};
+
+static struct wcd9xxx_slim_sch sh_ch;
+
+static int wcd9xxx_alloc_slim_sh_ch_rx(struct wcd9xxx *wcd9xxx,
+					u8 wcd9xxx_pgd_la);
+static int wcd9xxx_alloc_slim_sh_ch_tx(struct wcd9xxx *wcd9xxx,
+					u8 wcd9xxx_pgd_la);
+static int wcd9xxx_dealloc_slim_sh_ch_rx(struct wcd9xxx *wcd9xxx);
+static int wcd9xxx_dealloc_slim_sh_ch_tx(struct wcd9xxx *wcd9xxx);
+
+int wcd9xxx_init_slimslave(struct wcd9xxx *wcd9xxx, u8 wcd9xxx_pgd_la)
+{
+	int ret = 0;
+
+	ret = wcd9xxx_alloc_slim_sh_ch_rx(wcd9xxx, wcd9xxx_pgd_la);
+	if (ret) {
+		pr_err("%s: Failed to alloc rx slimbus shared channels\n",
+								__func__);
+		goto rx_err;
+	}
+	ret = wcd9xxx_alloc_slim_sh_ch_tx(wcd9xxx, wcd9xxx_pgd_la);
+	if (ret) {
+		pr_err("%s: Failed to alloc tx slimbus shared channels\n",
+								__func__);
+		goto tx_err;
+	}
+	return 0;
+tx_err:
+	wcd9xxx_dealloc_slim_sh_ch_rx(wcd9xxx);
+rx_err:
+	return ret;
+}
+
+
+int wcd9xxx_deinit_slimslave(struct wcd9xxx *wcd9xxx)
+{
+	int ret = 0;
+	ret = wcd9xxx_dealloc_slim_sh_ch_rx(wcd9xxx);
+	if (ret < 0) {
+		pr_err("%s: fail to dealloc rx slim ports\n", __func__);
+		goto err;
+	}
+	ret = wcd9xxx_dealloc_slim_sh_ch_tx(wcd9xxx);
+	if (ret < 0) {
+		pr_err("%s: fail to dealloc tx slim ports\n", __func__);
+		goto err;
+	}
+err:
+	return ret;
+}
+
+int wcd9xxx_get_channel(struct wcd9xxx *wcd9xxx,
+		unsigned int *rx_ch,
+		unsigned int *tx_ch)
+{
+	int ch_idx = 0;
+	struct wcd9xxx_slim_sch_rx *rx = sh_ch.rx;
+	struct wcd9xxx_slim_sch_tx *tx = sh_ch.tx;
+
+	for (ch_idx = 0; ch_idx < SLIM_MAX_RX_PORTS; ch_idx++)
+		rx_ch[ch_idx] = rx[ch_idx].ch_num;
+	for (ch_idx = 0; ch_idx < SLIM_MAX_TX_PORTS; ch_idx++)
+		tx_ch[ch_idx] = tx[ch_idx].ch_num;
+	return 0;
+}
+
+static int wcd9xxx_alloc_slim_sh_ch_rx(struct wcd9xxx *wcd9xxx,
+			u8 wcd9xxx_pgd_la)
+{
+	int ret = 0;
+	u8 ch_idx ;
+	u16 slave_port_id = 0;
+	struct wcd9xxx_slim_sch_rx *rx = sh_ch.rx;
+
+	/*
+	 * DSP requires channel number to be between 128 and 255.
+	 */
+	pr_debug("%s: pgd_la[%d]\n", __func__, wcd9xxx_pgd_la);
+	for (ch_idx = 0; ch_idx < SLIM_MAX_RX_PORTS; ch_idx++) {
+		slave_port_id = (ch_idx + 1 +
+				SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS);
+		rx[ch_idx].ch_num = slave_port_id + BASE_CH_NUM;
+		ret = slim_get_slaveport(wcd9xxx_pgd_la, slave_port_id,
+					&rx[ch_idx].sph, SLIM_SINK);
+		if (ret < 0) {
+			pr_err("%s: slave port failure id[%d] ret[%d]\n",
+					__func__, slave_port_id, ret);
+			goto err;
+		}
+
+		ret = slim_query_ch(wcd9xxx->slim, rx[ch_idx].ch_num,
+							&rx[ch_idx].ch_h);
+		if (ret < 0) {
+			pr_err("%s: slim_query_ch failed ch-num[%d] ret[%d]\n",
+					__func__, rx[ch_idx].ch_num, ret);
+			goto err;
+		}
+	}
+err:
+	return ret;
+}
+
+static int wcd9xxx_alloc_slim_sh_ch_tx(struct wcd9xxx *wcd9xxx,
+			u8 wcd9xxx_pgd_la)
+{
+	int ret = 0;
+	u8 ch_idx ;
+	struct wcd9xxx_slim_sch_tx *tx = sh_ch.tx;
+	u16 slave_port_id = 0;
+
+	pr_debug("%s: pgd_la[%d]\n", __func__, wcd9xxx_pgd_la);
+	/* DSP requires channel number to be between 128 and 255. For RX port
+	 * use channel numbers from 138 to 144, for TX port
+	 * use channel numbers from 128 to 137
+	 */
+	for (ch_idx = 0; ch_idx < SLIM_MAX_TX_PORTS; ch_idx++) {
+		slave_port_id = ch_idx;
+		tx[ch_idx].ch_num = slave_port_id + BASE_CH_NUM;
+		ret = slim_get_slaveport(wcd9xxx_pgd_la, slave_port_id,
+					&tx[ch_idx].sph, SLIM_SRC);
+		if (ret < 0) {
+			pr_err("%s: slave port failure id[%d] ret[%d]\n",
+					__func__, slave_port_id, ret);
+			goto err;
+		}
+		ret = slim_query_ch(wcd9xxx->slim, tx[ch_idx].ch_num,
+							&tx[ch_idx].ch_h);
+		if (ret < 0) {
+			pr_err("%s: slim_query_ch failed ch-num[%d] ret[%d]\n",
+					__func__, tx[ch_idx].ch_num, ret);
+			goto err;
+		}
+	}
+err:
+	return ret;
+}
+
+static int wcd9xxx_dealloc_slim_sh_ch_rx(struct wcd9xxx *wcd9xxx)
+{
+	int idx = 0;
+	int ret = 0;
+	struct wcd9xxx_slim_sch_rx *rx = sh_ch.rx;
+	/* slim_dealloc_ch */
+	for (idx = 0; idx < SLIM_MAX_RX_PORTS; idx++) {
+		ret = slim_dealloc_ch(wcd9xxx->slim, rx[idx].ch_h);
+		if (ret < 0) {
+			pr_err("%s: slim_dealloc_ch fail ret[%d] ch_h[%d]\n",
+				__func__, ret, rx[idx].ch_h);
+		}
+	}
+	memset(sh_ch.rx, 0, sizeof(sh_ch.rx));
+	return ret;
+}
+
+static int wcd9xxx_dealloc_slim_sh_ch_tx(struct wcd9xxx *wcd9xxx)
+{
+	int idx = 0;
+	int ret = 0;
+	struct wcd9xxx_slim_sch_tx *tx = sh_ch.tx;
+	/* slim_dealloc_ch */
+	for (idx = 0; idx < SLIM_MAX_TX_PORTS; idx++) {
+		ret = slim_dealloc_ch(wcd9xxx->slim, tx[idx].ch_h);
+		if (ret < 0) {
+			pr_err("%s: slim_dealloc_ch fail ret[%d] ch_h[%d]\n",
+				__func__, ret, tx[idx].ch_h);
+		}
+	}
+	memset(sh_ch.tx, 0, sizeof(sh_ch.tx));
+	return ret;
+}
+
+/* Enable slimbus slave device for RX path */
+int wcd9xxx_cfg_slim_sch_rx(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
+				unsigned int ch_cnt, unsigned int rate)
+{
+	u8 i = 0;
+	u16 grph;
+	u32 sph[SLIM_MAX_RX_PORTS] = {0};
+	u16 ch_h[SLIM_MAX_RX_PORTS] = {0};
+	u16 slave_port_id;
+	u8  payload_rx = 0, wm_payload = 0;
+	int ret, idx = 0;
+	unsigned short  multi_chan_cfg_reg_addr;
+	struct wcd9xxx_slim_sch_rx *rx = sh_ch.rx;
+	struct slim_ch prop;
+
+	/* Configure slave interface device */
+	pr_debug("%s: ch_cnt[%d] rate=%d\n", __func__, ch_cnt, rate);
+
+	for (i = 0; i < ch_cnt; i++) {
+		idx = (ch_num[i] - BASE_CH_NUM -
+			SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS - 1);
+		ch_h[i] = rx[idx].ch_h;
+		sph[i] = rx[idx].sph;
+		slave_port_id = idx + 1;
+		if ((slave_port_id > SB_PGD_MAX_NUMBER_OF_RX_SLAVE_DEV_PORTS) ||
+			(slave_port_id == 0)) {
+			pr_err("Slimbus: invalid slave port id: %d",
+							slave_port_id);
+			ret = -EINVAL;
+			goto err;
+		}
+		slave_port_id += SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS;
+		/* look for the valid port range and chose the
+		 * payload accordingly
+		 */
+		if ((slave_port_id >
+				SB_PGD_TX_PORT_MULTI_CHANNEL_1_END_PORT_ID) &&
+			(slave_port_id <=
+			 SB_PGD_RX_PORT_MULTI_CHANNEL_0_END_PORT_ID)) {
+				payload_rx = payload_rx  |
+				(1 <<
+				(slave_port_id -
+				SB_PGD_RX_PORT_MULTI_CHANNEL_0_START_PORT_ID));
+		} else {
+			ret = -EINVAL;
+			goto err;
+		}
+		multi_chan_cfg_reg_addr =
+				SB_PGD_RX_PORT_MULTI_CHANNEL_0(slave_port_id);
+		/* write to interface device */
+		ret = wcd9xxx_interface_reg_write(wcd9xxx,
+				multi_chan_cfg_reg_addr,
+				payload_rx);
+		if (ret < 0) {
+			pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n",
+							__func__,
+							multi_chan_cfg_reg_addr,
+							payload_rx, ret);
+			goto err;
+		}
+		/* configure the slave port for water mark and enable*/
+		wm_payload = (SLAVE_PORT_WATER_MARK_VALUE <<
+				SLAVE_PORT_WATER_MARK_SHIFT) +
+				SLAVE_PORT_ENABLE;
+		ret = wcd9xxx_interface_reg_write(wcd9xxx,
+				SB_PGD_PORT_CFG_BYTE_ADDR(slave_port_id),
+				wm_payload);
+		if (ret < 0) {
+			pr_err("%s:watermark set failure for port[%d] ret[%d]",
+						__func__, slave_port_id, ret);
+		}
+	}
+
+	/* slim_define_ch api */
+	prop.prot = SLIM_AUTO_ISO;
+	prop.baser = SLIM_RATE_4000HZ;
+	prop.dataf = SLIM_CH_DATAF_NOT_DEFINED;
+	prop.auxf = SLIM_CH_AUXF_NOT_APPLICABLE;
+	prop.ratem = (rate/4000);
+	prop.sampleszbits = 16;
+
+	ret = slim_define_ch(wcd9xxx->slim, &prop, ch_h, ch_cnt,
+					true, &grph);
+	if (ret < 0) {
+		pr_err("%s: slim_define_ch failed ret[%d]\n",
+					__func__, ret);
+		goto err;
+	}
+	for (i = 0; i < ch_cnt; i++) {
+		ret = slim_connect_sink(wcd9xxx->slim, &sph[i],
+							1, ch_h[i]);
+		if (ret < 0) {
+			pr_err("%s: slim_connect_sink failed ret[%d]\n",
+						__func__, ret);
+			goto err_close_slim_sch;
+		}
+	}
+	/* slim_control_ch */
+	ret = slim_control_ch(wcd9xxx->slim, grph, SLIM_CH_ACTIVATE,
+					true);
+	if (ret < 0) {
+		pr_err("%s: slim_control_ch failed ret[%d]\n",
+				__func__, ret);
+		goto err_close_slim_sch;
+	}
+	for (i = 0; i < ch_cnt; i++) {
+		idx = (ch_num[i] - BASE_CH_NUM -
+				SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS - 1);
+		rx[idx].grph = grph;
+	}
+	return 0;
+
+err_close_slim_sch:
+	/*  release all acquired handles */
+	wcd9xxx_close_slim_sch_rx(wcd9xxx, ch_num, ch_cnt);
+err:
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_cfg_slim_sch_rx);
+
+/* Enable slimbus slave device for RX path */
+int wcd9xxx_cfg_slim_sch_tx(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
+				unsigned int ch_cnt, unsigned int rate)
+{
+	u8 i = 0;
+	u8  payload_tx_0 = 0, payload_tx_1 = 0, wm_payload = 0;
+	u16 grph;
+	u32 sph[SLIM_MAX_TX_PORTS] = {0};
+	u16 ch_h[SLIM_MAX_TX_PORTS] = {0};
+	u16 idx = 0, slave_port_id;
+	int ret = 0;
+	unsigned short  multi_chan_cfg_reg_addr;
+
+	struct wcd9xxx_slim_sch_tx *tx = sh_ch.tx;
+	struct slim_ch prop;
+
+	pr_debug("%s: ch_cnt[%d] rate[%d]\n", __func__, ch_cnt, rate);
+	for (i = 0; i < ch_cnt; i++) {
+		idx = (ch_num[i] - BASE_CH_NUM);
+		ch_h[i] = tx[idx].ch_h;
+		sph[i] = tx[idx].sph;
+		slave_port_id = idx ;
+		if (slave_port_id > SB_PGD_MAX_NUMBER_OF_TX_SLAVE_DEV_PORTS) {
+			pr_err("SLIMbus: invalid slave port id: %d",
+							slave_port_id);
+			ret = -EINVAL;
+			goto err;
+		}
+		/* look for the valid port range and chose the
+		 *  payload accordingly
+		 */
+		if (slave_port_id <=
+			SB_PGD_TX_PORT_MULTI_CHANNEL_0_END_PORT_ID) {
+			payload_tx_0 = payload_tx_0 | (1 << slave_port_id);
+		} else if (slave_port_id <=
+				SB_PGD_TX_PORT_MULTI_CHANNEL_1_END_PORT_ID) {
+				payload_tx_1 = payload_tx_1 |
+				(1 <<
+				(slave_port_id -
+				SB_PGD_TX_PORT_MULTI_CHANNEL_1_START_PORT_ID));
+		} else {
+			ret = -EINVAL;
+			goto err;
+		}
+		multi_chan_cfg_reg_addr =
+				SB_PGD_TX_PORT_MULTI_CHANNEL_0(slave_port_id);
+		/* write to interface device */
+		ret = wcd9xxx_interface_reg_write(wcd9xxx,
+				multi_chan_cfg_reg_addr,
+				payload_tx_0);
+		if (ret < 0) {
+			pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n",
+								__func__,
+						multi_chan_cfg_reg_addr,
+						payload_tx_0, ret);
+			goto err;
+		}
+		multi_chan_cfg_reg_addr =
+				SB_PGD_TX_PORT_MULTI_CHANNEL_1(slave_port_id);
+		/* ports 8,9 */
+		ret = wcd9xxx_interface_reg_write(wcd9xxx,
+				multi_chan_cfg_reg_addr,
+				payload_tx_1);
+		if (ret < 0) {
+			pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n",
+								__func__,
+						multi_chan_cfg_reg_addr,
+						payload_tx_1, ret);
+			goto err;
+		}
+		/* configure the slave port for water mark and enable*/
+		wm_payload = (SLAVE_PORT_WATER_MARK_VALUE <<
+				SLAVE_PORT_WATER_MARK_SHIFT) +
+				SLAVE_PORT_ENABLE;
+		ret = wcd9xxx_interface_reg_write(wcd9xxx,
+				SB_PGD_PORT_CFG_BYTE_ADDR(slave_port_id),
+				wm_payload);
+		if (ret < 0) {
+			pr_err("%s:watermark set failure for port[%d] ret[%d]",
+						__func__,
+						slave_port_id, ret);
+		}
+	}
+
+	/* slim_define_ch api */
+	prop.prot = SLIM_AUTO_ISO;
+	prop.baser = SLIM_RATE_4000HZ;
+	prop.dataf = SLIM_CH_DATAF_NOT_DEFINED;
+	prop.auxf = SLIM_CH_AUXF_NOT_APPLICABLE;
+	prop.ratem = (rate/4000);
+	prop.sampleszbits = 16;
+	ret = slim_define_ch(wcd9xxx->slim, &prop, ch_h, ch_cnt,
+					true, &grph);
+	if (ret < 0) {
+		pr_err("%s: slim_define_ch failed ret[%d]\n",
+					__func__, ret);
+		goto err;
+	}
+	for (i = 0; i < ch_cnt; i++) {
+		ret = slim_connect_src(wcd9xxx->slim, sph[i],
+							ch_h[i]);
+		if (ret < 0) {
+			pr_err("%s: slim_connect_src failed ret[%d]\n",
+						__func__, ret);
+			goto err;
+		}
+	}
+	/* slim_control_ch */
+	ret = slim_control_ch(wcd9xxx->slim, grph, SLIM_CH_ACTIVATE,
+					true);
+	if (ret < 0) {
+		pr_err("%s: slim_control_ch failed ret[%d]\n",
+				__func__, ret);
+		goto err;
+	}
+	for (i = 0; i < ch_cnt; i++) {
+		idx = (ch_num[i] - BASE_CH_NUM);
+		tx[idx].grph = grph;
+	}
+	return 0;
+err:
+	/* release all acquired handles */
+	wcd9xxx_close_slim_sch_tx(wcd9xxx, ch_num, ch_cnt);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_cfg_slim_sch_tx);
+
+int wcd9xxx_close_slim_sch_rx(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
+				unsigned int ch_cnt)
+{
+	u16 grph = 0;
+	u32 sph[SLIM_MAX_RX_PORTS] = {0};
+	int i = 0 , idx = 0;
+	int ret = 0;
+	struct wcd9xxx_slim_sch_rx *rx = sh_ch.rx;
+
+	pr_debug("%s: ch_cnt[%d]\n", __func__, ch_cnt);
+	for (i = 0; i < ch_cnt; i++) {
+		idx = (ch_num[i] - BASE_CH_NUM -
+			SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS - 1);
+		if (idx < 0) {
+			pr_err("%s: Error:-Invalid index found = %d\n",
+				__func__, idx);
+			ret = -EINVAL;
+			goto err;
+		}
+		sph[i] = rx[idx].sph;
+		grph = rx[idx].grph;
+	}
+
+	/* slim_control_ch (REMOVE) */
+	ret = slim_control_ch(wcd9xxx->slim, grph, SLIM_CH_REMOVE, true);
+	if (ret < 0) {
+		pr_err("%s: slim_control_ch failed ret[%d]\n",
+				__func__, ret);
+		goto err;
+	}
+	for (i = 0; i < ch_cnt; i++) {
+		idx = (ch_num[i] - BASE_CH_NUM -
+				SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS - 1);
+		rx[idx].grph = 0;
+	}
+err:
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_close_slim_sch_rx);
+
+int wcd9xxx_close_slim_sch_tx(struct wcd9xxx *wcd9xxx, unsigned int *ch_num,
+				unsigned int ch_cnt)
+{
+	u16 grph = 0;
+	u32 sph[SLIM_MAX_TX_PORTS] = {0};
+	int ret = 0;
+	int i = 0 , idx = 0;
+	struct wcd9xxx_slim_sch_tx *tx = sh_ch.tx;
+
+	pr_debug("%s: ch_cnt[%d]\n", __func__, ch_cnt);
+	for (i = 0; i < ch_cnt; i++) {
+		idx = (ch_num[i] - BASE_CH_NUM);
+		if (idx < 0) {
+			pr_err("%s: Error:- Invalid index found = %d\n",
+				__func__, idx);
+			ret = -EINVAL;
+			goto err;
+		}
+		sph[i] = tx[idx].sph;
+		grph = tx[idx].grph;
+	}
+
+	/* slim_control_ch (REMOVE) */
+	ret = slim_control_ch(wcd9xxx->slim, grph, SLIM_CH_REMOVE, true);
+	if (ret < 0) {
+		pr_err("%s: slim_control_ch failed ret[%d]\n",
+				__func__, ret);
+		goto err;
+	}
+	for (i = 0; i < ch_cnt; i++) {
+		idx = (ch_num[i] - BASE_CH_NUM);
+		tx[idx].grph = 0;
+	}
+err:
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wcd9xxx_close_slim_sch_tx);