Merge "msm: 8064: Enable crypto engine." into msm-3.0
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index 604989d..e4f88ec 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -149,7 +149,7 @@
 	obj-$(CONFIG_ARCH_MSM7X01A) += pm.o
 	obj-y += pm-boot.o
 else
-	obj-y += no-pm.o
+	obj-y += no-pm.o hotplug.o
 endif
 
 obj-$(CONFIG_MSM_SPM_V1) += spm.o
diff --git a/arch/arm/mach-msm/board-msm8960.c b/arch/arm/mach-msm/board-msm8960.c
index 9961bd1..22767cec 100644
--- a/arch/arm/mach-msm/board-msm8960.c
+++ b/arch/arm/mach-msm/board-msm8960.c
@@ -4378,6 +4378,10 @@
 		.num_configs = ARRAY_SIZE(pm8921_led_configs),
 };
 
+static struct pm8xxx_ccadc_platform_data pm8xxx_ccadc_pdata = {
+	.r_sense		= 10,
+};
+
 static struct pm8921_platform_data pm8921_platform_data __devinitdata = {
 	.irq_pdata		= &pm8xxx_irq_pdata,
 	.gpio_pdata		= &pm8xxx_gpio_pdata,
@@ -4391,6 +4395,7 @@
 	.bms_pdata		= &pm8921_bms_pdata,
 	.adc_pdata		= &pm8921_adc_pdata,
 	.leds_pdata		= &pm8xxx_leds_pdata,
+	.ccadc_pdata		= &pm8xxx_ccadc_pdata,
 };
 
 static struct msm_ssbi_platform_data msm8960_ssbi_pm8921_pdata __devinitdata = {
diff --git a/arch/arm/mach-msm/hotplug.c b/arch/arm/mach-msm/hotplug.c
index 5a31f70..e8aa38e 100644
--- a/arch/arm/mach-msm/hotplug.c
+++ b/arch/arm/mach-msm/hotplug.c
@@ -12,6 +12,7 @@
 
 #include <asm/cacheflush.h>
 
+#ifdef CONFIG_SMP
 extern volatile int pen_release;
 
 static inline void cpu_enter_lowpower(void)
@@ -55,6 +56,7 @@
 		pr_debug("CPU%u: spurious wakeup call\n", cpu);
 	}
 }
+#endif
 
 int platform_cpu_kill(unsigned int cpu)
 {
@@ -68,6 +70,7 @@
  */
 void platform_cpu_die(unsigned int cpu)
 {
+#ifdef CONFIG_SMP
 	/*
 	 * we're ready for shutdown now, so do it
 	 */
@@ -79,6 +82,7 @@
 	 * coherency, and then restore interrupts
 	 */
 	cpu_leave_lowpower();
+#endif
 }
 
 int platform_cpu_disable(unsigned int cpu)
diff --git a/arch/arm/mach-msm/no-pm.c b/arch/arm/mach-msm/no-pm.c
index e34abb1..d6cdbe7 100644
--- a/arch/arm/mach-msm/no-pm.c
+++ b/arch/arm/mach-msm/no-pm.c
@@ -25,16 +25,3 @@
 
 void msm_pm_set_max_sleep_time(int64_t max_sleep_time_ns) { }
 EXPORT_SYMBOL(msm_pm_set_max_sleep_time);
-
-int platform_cpu_disable(unsigned int cpu)
-{
-	return -ENOSYS;
-}
-
-int platform_cpu_kill(unsigned int cpu)
-{
-	return -ENOSYS;
-}
-
-void platform_cpu_die(unsigned int cpu)
-{ }
diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c
index e2f7271..bddc80f 100644
--- a/drivers/cpuidle/governors/menu.c
+++ b/drivers/cpuidle/governors/menu.c
@@ -125,14 +125,6 @@
 #define LOAD_INT(x) ((x) >> FSHIFT)
 #define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100)
 
-static int get_loadavg(void)
-{
-	unsigned long this = this_cpu_load();
-
-
-	return LOAD_INT(this) * 10 + LOAD_FRAC(this) / 10;
-}
-
 static inline int which_bucket(unsigned int duration)
 {
 	int bucket = 0;
@@ -170,15 +162,6 @@
 {
 	int mult = 1;
 
-	/* for higher loadavg, we are more reluctant */
-
-	/*
-	 * this doesn't work as intended - it is almost always 0, but can
-	 * sometimes, depending on workload, spike very high into the hundreds
-	 * even when the average cpu load is under 10%.
-	 */
-	/* mult += 2 * get_loadavg(); */
-
 	/* for IO wait tasks (per cpu!) we add 5x each */
 	mult += 10 * nr_iowait_cpu(smp_processor_id());
 
diff --git a/drivers/mfd/pm8921-core.c b/drivers/mfd/pm8921-core.c
index 742d8dd..b72ceb3 100644
--- a/drivers/mfd/pm8921-core.c
+++ b/drivers/mfd/pm8921-core.c
@@ -270,7 +270,6 @@
 	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),
-	SINGLE_IRQ_RESOURCE("PM8921_BMS_CCADC_EOC", PM8921_BMS_CCADC_EOC),
 };
 
 static struct mfd_cell charger_cell __devinitdata = {
@@ -342,6 +341,17 @@
 	.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,
@@ -566,6 +576,19 @@
 		}
 	}
 
+	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) {
diff --git a/drivers/net/msm_rmnet_bam.c b/drivers/net/msm_rmnet_bam.c
index 3b3758e..bb20a3f 100644
--- a/drivers/net/msm_rmnet_bam.c
+++ b/drivers/net/msm_rmnet_bam.c
@@ -308,6 +308,7 @@
 	}
 
 	dev->trans_start = jiffies;
+	/* if write() succeeds, skb access is unsafe in this process */
 	bam_ret = msm_bam_dmux_write(p->ch_id, skb);
 
 	if (bam_ret != 0) {
@@ -316,16 +317,6 @@
 		goto xmit_out;
 	}
 
-	if (count_this_packet(skb->data, skb->len)) {
-		p->stats.tx_packets++;
-		p->stats.tx_bytes += skb->len;
-#ifdef CONFIG_MSM_RMNET_DEBUG
-		p->wakeups_xmit += rmnet_cause_wakeup(p);
-#endif
-	}
-	DBG1("[%s] Tx packet #%lu len=%d mark=0x%x\n",
-	    dev->name, p->stats.tx_packets, skb->len, skb->mark);
-
 	return 0;
 xmit_out:
 	/* data xmited, safe to release skb */
@@ -335,7 +326,21 @@
 
 static void bam_write_done(void *dev, struct sk_buff *skb)
 {
+	struct rmnet_private *p = netdev_priv(dev);
+	u32 opmode = p->operation_mode;
+
 	DBG1("%s: write complete\n", __func__);
+	if (RMNET_IS_MODE_IP(opmode) ||
+				count_this_packet(skb->data, skb->len)) {
+		p->stats.tx_packets++;
+		p->stats.tx_bytes += skb->len;
+#ifdef CONFIG_MSM_RMNET_DEBUG
+		p->wakeups_xmit += rmnet_cause_wakeup(p);
+#endif
+	}
+	DBG1("[%s] Tx packet #%lu len=%d mark=0x%x\n",
+	    ((struct net_device *)(dev))->name, p->stats.tx_packets,
+	    skb->len, skb->mark);
 	dev_kfree_skb_any(skb);
 	netif_wake_queue(dev);
 }
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 3590e6d..7c8dfea 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -332,7 +332,14 @@
 	help
 	  Say Y here to enable support for pm8921 chip charger subdevice
 
+config PM8XXX_CCADC
+	tristate "PM8XXX battery current adc driver"
+	depends on MFD_PM8921_CORE
+	help
+	  Say Y here to enable support for pm8921 chip bms subdevice
+
 config PM8921_BMS
+	select PM8XXX_CCADC
 	tristate "PM8921 Battery Monitoring System driver"
 	depends on MFD_PM8921_CORE
 	help
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index f61c88a..e168590 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -45,5 +45,6 @@
 obj-$(CONFIG_BATTERY_BQ27520)	+= bq27520_fuelgauger.o
 obj-$(CONFIG_BATTERY_BQ27541)	+= bq27541_fuelgauger.o
 obj-$(CONFIG_SMB137B_CHARGER)   += smb137b.o
+obj-$(CONFIG_PM8XXX_CCADC)	+= pm8xxx-ccadc.o
 obj-$(CONFIG_PM8921_BMS)	+= pm8921-bms.o
 obj-$(CONFIG_PM8921_CHARGER)	+= pm8921-charger.o
diff --git a/drivers/power/pm8921-bms.c b/drivers/power/pm8921-bms.c
index d7cf3a8..143d9b5 100644
--- a/drivers/power/pm8921-bms.c
+++ b/drivers/power/pm8921-bms.c
@@ -19,6 +19,7 @@
 #include <linux/mfd/pm8xxx/pm8921-bms.h>
 #include <linux/mfd/pm8xxx/core.h>
 #include <linux/mfd/pm8xxx/pm8921-adc.h>
+#include <linux/mfd/pm8xxx/ccadc.h>
 #include <linux/interrupt.h>
 #include <linux/bitops.h>
 #include <linux/debugfs.h>
@@ -29,15 +30,6 @@
 #define BMS_OUTPUT0		0x230
 #define BMS_OUTPUT1		0x231
 #define BMS_TEST1		0x237
-#define CCADC_ANA_PARAM		0x240
-#define CCADC_DIG_PARAM		0x241
-#define CCADC_RSV		0x242
-#define CCADC_DATA0		0x244
-#define CCADC_DATA1		0x245
-#define CCADC_OFFSET_TRIM1	0x34A
-#define CCADC_OFFSET_TRIM0	0x34B
-#define CCADC_FULLSCALE_TRIM1	0x34C
-#define CCADC_FULLSCALE_TRIM0	0x34D
 
 #define ADC_ARB_SECP_CNTRL	0x190
 #define ADC_ARB_SECP_AMUX_CNTRL	0x191
@@ -57,7 +49,6 @@
 	PM8921_BMS_OCV_FOR_R,
 	PM8921_BMS_GOOD_OCV,
 	PM8921_BMS_VSENSE_AVG,
-	PM8921_BMS_CCADC_EOC,
 	PM_BMS_MAX_INTS,
 };
 
@@ -75,8 +66,6 @@
 	struct work_struct	calib_hkadc_work;
 	struct delayed_work	calib_ccadc_work;
 	unsigned int		calib_delay_ms;
-	int			ccadc_gain_uv;
-	u16			ccadc_result_offset;
 	unsigned int		revision;
 	unsigned int		xoadc_v0625;
 	unsigned int		xoadc_v125;
@@ -400,56 +389,6 @@
 				cc_to_microvolt_v2((s64)cc);
 }
 
-#define CCADC_READING_RESOLUTION_N_V1	1085069
-#define CCADC_READING_RESOLUTION_D_V1	100000
-#define CCADC_READING_RESOLUTION_N_V2	542535
-#define CCADC_READING_RESOLUTION_D_V2	100000
-static s64 ccadc_reading_to_microvolt_v1(s64 cc)
-{
-	return div_s64(cc * CCADC_READING_RESOLUTION_N_V1,
-					CCADC_READING_RESOLUTION_D_V1);
-}
-
-static s64 ccadc_reading_to_microvolt_v2(s64 cc)
-{
-	return div_s64(cc * CCADC_READING_RESOLUTION_N_V2,
-					CCADC_READING_RESOLUTION_D_V2);
-}
-
-static s64 ccadc_reading_to_microvolt(struct pm8921_bms_chip *chip, s64 cc)
-{
-	/*
-	 * resolution (the value of a single bit) was changed after revision 2.0
-	 * for more accurate readings
-	 */
-	return (chip->revision < PM8XXX_REVISION_8921_2p0) ?
-				ccadc_reading_to_microvolt_v1((s64)cc) :
-				ccadc_reading_to_microvolt_v2((s64)cc);
-}
-
-static s64 microvolt_to_ccadc_reading_v1(s64 uv)
-{
-	return div_s64(uv * CCADC_READING_RESOLUTION_D_V1,
-				CCADC_READING_RESOLUTION_N_V1);
-}
-
-static s64 microvolt_to_ccadc_reading_v2(s64 uv)
-{
-	return div_s64(uv * CCADC_READING_RESOLUTION_D_V2,
-				CCADC_READING_RESOLUTION_N_V2);
-}
-
-static s64 microvolt_to_ccadc_reading(struct pm8921_bms_chip *chip, s64 cc)
-{
-	/*
-	 * resolution (the value of a single bit) was changed after revision 2.0
-	 * for more accurate readings
-	 */
-	return (chip->revision < PM8XXX_REVISION_8921_2p0) ?
-				microvolt_to_ccadc_reading_v1((s64)cc) :
-				microvolt_to_ccadc_reading_v2((s64)cc);
-}
-
 #define CC_READING_TICKS	55
 #define SLEEP_CLK_HZ		32768
 #define SECONDS_PER_HOUR	3600
@@ -459,19 +398,6 @@
 			SLEEP_CLK_HZ * SECONDS_PER_HOUR);
 }
 
-#define GAIN_REFERENCE_UV 25000
-/*
- * gain compensation for ccadc readings - common for vsense based and
- * couloumb counter based readings
- */
-static s64 cc_adjust_for_gain(struct pm8921_bms_chip *chip, s64 cc)
-{
-	if (chip->ccadc_gain_uv == 0)
-		return cc;
-
-	return div_s64(cc * GAIN_REFERENCE_UV, chip->ccadc_gain_uv);
-}
-
 /* returns the signed value read from the hardware */
 static int read_cc(struct pm8921_bms_chip *chip, int *result)
 {
@@ -559,9 +485,9 @@
 		pr_err("fail to read VSENSE_FOR_RBATT rc = %d\n", rc);
 		return rc;
 	}
-	*result = ccadc_reading_to_microvolt(chip, reading);
+	*result = pm8xxx_ccadc_reading_to_microvolt(chip->revision, reading);
 	pr_debug("raw = %04x vsense_for_r_uV = %u\n", reading, *result);
-	*result = cc_adjust_for_gain(chip, *result);
+	*result = pm8xxx_cc_adjust_for_gain(*result);
 	pr_debug("after adj vsense_for_r_uV = %u\n", *result);
 	return 0;
 }
@@ -593,9 +519,10 @@
 		pr_err("fail to read VSENSE_AVG rc = %d\n", rc);
 		return rc;
 	}
-	*result = ccadc_reading_to_microvolt(chip, reading);
+	*result = pm8xxx_ccadc_reading_to_microvolt(the_chip->revision,
+								reading);
 	pr_debug("raw = %04x vsense = %d\n", reading, *result);
-	*result = cc_adjust_for_gain(the_chip, (s64)*result);
+	*result = pm8xxx_cc_adjust_for_gain((s64)*result);
 	pr_debug("after adj vsense = %d\n", *result);
 	return 0;
 }
@@ -976,7 +903,7 @@
 	rc = read_cc(the_chip, coulumb_counter);
 	cc_voltage_uv = (int64_t)*coulumb_counter;
 	cc_voltage_uv = cc_to_microvolt(chip, cc_voltage_uv);
-	cc_voltage_uv = cc_adjust_for_gain(chip, cc_voltage_uv);
+	cc_voltage_uv = pm8xxx_cc_adjust_for_gain(cc_voltage_uv);
 	pr_debug("cc_voltage_uv = %lld microvolts\n", cc_voltage_uv);
 	cc_uvh = ccmicrovolt_to_uvh(cc_voltage_uv);
 	pr_debug("cc_uvh = %lld micro_volt_hour\n", cc_uvh);
@@ -1224,386 +1151,12 @@
 	calib_hkadc(chip);
 }
 
-#define START_CONV_BIT	BIT(7)
-#define EOC_CONV_BIT	BIT(6)
-#define SEL_CCADC_BIT	BIT(1)
-#define EN_ARB_BIT	BIT(0)
-
-#define CCADC_CALIB_DIG_PARAM	0xE3
-#define CCADC_CALIB_RSV_GND	0x40
-#define CCADC_CALIB_RSV_25MV	0x80
-#define CCADC_CALIB_ANA_PARAM	0x1B
-#define SAMPLE_COUNT		16
-#define ADC_WAIT_COUNT		10
-
-#define CCADC_MAX_25MV		30000
-#define CCADC_MIN_25MV		20000
-#define CCADC_MAX_0UV		-4000
-#define CCADC_MIN_0UV		-7000
-
-#define CCADC_INTRINSIC_OFFSET  0xC000
-
-#define REG_SBI_CONFIG		0x04F
-#define PAGE3_ENABLE_MASK	0x6
-
-static int calib_ccadc_enable_trim_access(struct pm8921_bms_chip *chip,
-								u8 *sbi_config)
-{
-	u8 reg;
-	int rc;
-
-	rc = pm8xxx_readb(chip->dev->parent, REG_SBI_CONFIG, sbi_config);
-	if (rc) {
-		pr_err("error = %d reading sbi config reg\n", rc);
-		return rc;
-	}
-
-	reg = *sbi_config | PAGE3_ENABLE_MASK;
-	return pm8xxx_writeb(chip->dev->parent, REG_SBI_CONFIG, reg);
-}
-
-static int calib_ccadc_restore_trim_access(struct pm8921_bms_chip *chip,
-							u8 sbi_config)
-{
-	return pm8xxx_writeb(chip->dev->parent, REG_SBI_CONFIG, sbi_config);
-}
-
-static int calib_ccadc_enable_arbiter(struct pm8921_bms_chip *chip)
-{
-	int rc;
-
-	/* enable Arbiter, must be sent twice */
-	rc = pm_bms_masked_write(chip, ADC_ARB_SECP_CNTRL,
-			SEL_CCADC_BIT | EN_ARB_BIT, SEL_CCADC_BIT | EN_ARB_BIT);
-	if (rc < 0) {
-		pr_err("error = %d enabling arbiter for offset\n", rc);
-		return rc;
-	}
-	rc = pm_bms_masked_write(chip, ADC_ARB_SECP_CNTRL,
-			SEL_CCADC_BIT | EN_ARB_BIT, SEL_CCADC_BIT | EN_ARB_BIT);
-	if (rc < 0) {
-		pr_err("error = %d writing ADC_ARB_SECP_CNTRL\n", rc);
-		return rc;
-	}
-	return 0;
-}
-
-static int calib_start_conv(struct pm8921_bms_chip *chip,
-					u16 *result)
-{
-	int rc, i;
-	u8 data_msb, data_lsb, reg;
-
-	/* Start conversion */
-	rc = pm_bms_masked_write(chip, ADC_ARB_SECP_CNTRL,
-					START_CONV_BIT, START_CONV_BIT);
-	if (rc < 0) {
-		pr_err("error = %d starting offset meas\n", rc);
-		return rc;
-	}
-
-	/* Wait for End of conversion */
-	for (i = 0; i < ADC_WAIT_COUNT; i++) {
-		rc = pm8xxx_readb(chip->dev->parent,
-					ADC_ARB_SECP_CNTRL, &reg);
-		if (rc < 0) {
-			pr_err("error = %d read eoc for offset\n", rc);
-			return rc;
-		}
-		if ((reg & (START_CONV_BIT | EOC_CONV_BIT)) != EOC_CONV_BIT)
-			msleep(60);
-		else
-			break;
-	}
-	if (i == ADC_WAIT_COUNT) {
-		pr_err("waited too long for offset eoc\n");
-		return rc;
-	}
-
-	rc = pm8xxx_readb(chip->dev->parent, ADC_ARB_SECP_DATA0, &data_lsb);
-	if (rc < 0) {
-		pr_err("error = %d reading offset lsb\n", rc);
-		return rc;
-	}
-
-	rc = pm8xxx_readb(chip->dev->parent, ADC_ARB_SECP_DATA1, &data_msb);
-	if (rc < 0) {
-		pr_err("error = %d reading offset msb\n", rc);
-		return rc;
-	}
-
-	*result = (data_msb << 8) | data_lsb;
-	return 0;
-}
-
-static int calib_ccadc_read_trim(struct pm8921_bms_chip *chip,
-					int addr, u8 *data_msb, u8 *data_lsb)
-{
-	int rc;
-	u8 sbi_config;
-
-	calib_ccadc_enable_trim_access(chip, &sbi_config);
-	rc = pm8xxx_readb(chip->dev->parent, addr, data_msb);
-	if (rc < 0) {
-		pr_err("error = %d read msb\n", rc);
-		return rc;
-	}
-	rc = pm8xxx_readb(chip->dev->parent, addr + 1, data_lsb);
-	if (rc < 0) {
-		pr_err("error = %d read lsb\n", rc);
-		return rc;
-	}
-	calib_ccadc_restore_trim_access(chip, sbi_config);
-	return 0;
-}
-
-static int calib_ccadc_read_gain_uv(struct pm8921_bms_chip *chip)
-{
-	s8 data_msb;
-	u8 data_lsb;
-	int rc, gain, offset;
-
-	rc = calib_ccadc_read_trim(chip, CCADC_FULLSCALE_TRIM1,
-						&data_msb, &data_lsb);
-	gain = (data_msb << 8) | data_lsb;
-
-	rc = calib_ccadc_read_trim(chip, CCADC_OFFSET_TRIM1,
-						&data_msb, &data_lsb);
-	offset = (data_msb << 8) | data_lsb;
-
-	pr_debug("raw gain trim = 0x%x offset trim =0x%x\n", gain, offset);
-	gain = ccadc_reading_to_microvolt(chip, (s64)gain - offset);
-	return gain;
-}
-
-#define CCADC_PROGRAM_TRIM_COUNT	2
-#define ADC_ARB_BMS_CNTRL_CCADC_SHIFT	4
-#define ADC_ARB_BMS_CNTRL_CONV_MASK	0x03
-#define BMS_CONV_IN_PROGRESS		0x2
-
-static int calib_ccadc_program_trim(struct pm8921_bms_chip *chip,
-					int addr, u8 data_msb, u8 data_lsb,
-					int wait)
-{
-	int i, rc, loop;
-	u8 cntrl, sbi_config;
-	bool in_progress = 0;
-
-	loop = wait ? CCADC_PROGRAM_TRIM_COUNT : 0;
-
-	calib_ccadc_enable_trim_access(chip, &sbi_config);
-
-	for (i = 0; i < loop; i++) {
-		rc = pm8xxx_readb(chip->dev->parent, ADC_ARB_BMS_CNTRL, &cntrl);
-		if (rc < 0) {
-			pr_err("error = %d reading ADC_ARB_BMS_CNTRL\n", rc);
-			return rc;
-		}
-
-		/* break if a ccadc conversion is not happening */
-		in_progress = (((cntrl >> ADC_ARB_BMS_CNTRL_CCADC_SHIFT)
-			& ADC_ARB_BMS_CNTRL_CONV_MASK) == BMS_CONV_IN_PROGRESS);
-
-		if (!in_progress)
-			break;
-	}
-
-	if (in_progress) {
-		pr_debug("conv in progress cannot write trim,returing EBUSY\n");
-		return -EBUSY;
-	}
-
-	rc = pm8xxx_writeb(chip->dev->parent, addr, data_msb);
-	if (rc < 0) {
-		pr_err("error = %d write msb = 0x%x\n", rc, data_msb);
-		return rc;
-	}
-	rc = pm8xxx_writeb(chip->dev->parent, addr + 1, data_lsb);
-	if (rc < 0) {
-		pr_err("error = %d write lsb = 0x%x\n", rc, data_lsb);
-		return rc;
-	}
-	calib_ccadc_restore_trim_access(chip, sbi_config);
-	return 0;
-}
-
-static void calib_ccadc(struct pm8921_bms_chip *chip)
-{
-	u8 data_msb, data_lsb, sec_cntrl;
-	int result_offset, voltage_offset, result_gain;
-	u16 result;
-	int i, rc;
-
-	rc = pm8xxx_readb(chip->dev->parent, ADC_ARB_SECP_CNTRL, &sec_cntrl);
-	if (rc < 0) {
-		pr_err("error = %d reading ADC_ARB_SECP_CNTRL\n", rc);
-		return;
-	}
-
-	rc = calib_ccadc_enable_arbiter(chip);
-	if (rc < 0) {
-		pr_err("error = %d enabling arbiter for offset\n", rc);
-		goto bail;
-	}
-
-	/*
-	 * Set decimation ratio to 4k, lower ratio may be used in order to speed
-	 * up, pending verification through bench
-	 */
-	rc = pm8xxx_writeb(chip->dev->parent, ADC_ARB_SECP_DIG_PARAM,
-							CCADC_CALIB_DIG_PARAM);
-	if (rc < 0) {
-		pr_err("error = %d writing ADC_ARB_SECP_DIG_PARAM\n", rc);
-		goto bail;
-	}
-
-	result_offset = 0;
-	for (i = 0; i < SAMPLE_COUNT; i++) {
-		/* Short analog inputs to CCADC internally to ground */
-		rc = pm8xxx_writeb(chip->dev->parent, ADC_ARB_SECP_RSV,
-							CCADC_CALIB_RSV_GND);
-		if (rc < 0) {
-			pr_err("error = %d selecting gnd voltage\n", rc);
-			goto bail;
-		}
-
-		/* Enable CCADC */
-		rc = pm8xxx_writeb(chip->dev->parent, ADC_ARB_SECP_ANA_PARAM,
-							CCADC_CALIB_ANA_PARAM);
-		if (rc < 0) {
-			pr_err("error = %d enabling ccadc\n", rc);
-			goto bail;
-		}
-
-		rc = calib_start_conv(chip, &result);
-		if (rc < 0) {
-			pr_err("error = %d for zero volt measurement\n", rc);
-			goto bail;
-		}
-
-		result_offset += result;
-	}
-
-	result_offset = result_offset / SAMPLE_COUNT;
-
-	voltage_offset = ccadc_reading_to_microvolt(chip,
-			((s64)result_offset - CCADC_INTRINSIC_OFFSET));
-
-	pr_debug("offset result_offset = 0x%x, voltage = %d microVolts\n",
-				result_offset, voltage_offset);
-
-	/* Sanity Check */
-	if (voltage_offset > CCADC_MAX_0UV) {
-		pr_err("offset voltage = %d is huge limiting to %d\n",
-					voltage_offset, CCADC_MAX_0UV);
-		result_offset = CCADC_INTRINSIC_OFFSET
-			+ microvolt_to_ccadc_reading(chip, (s64)CCADC_MAX_0UV);
-	} else if (voltage_offset < CCADC_MIN_0UV) {
-		pr_err("offset voltage = %d is too low limiting to %d\n",
-					voltage_offset, CCADC_MIN_0UV);
-		result_offset = CCADC_INTRINSIC_OFFSET
-			+ microvolt_to_ccadc_reading(chip, (s64)CCADC_MIN_0UV);
-	}
-
-	chip->ccadc_result_offset = result_offset;
-	data_msb = chip->ccadc_result_offset >> 8;
-	data_lsb = chip->ccadc_result_offset;
-
-	rc = calib_ccadc_program_trim(chip, CCADC_OFFSET_TRIM1,
-						data_msb, data_lsb, 1);
-	if (rc) {
-		pr_debug("error = %d programming offset trim 0x%02x 0x%02x\n",
-					rc, data_msb, data_lsb);
-		/* enable the interrupt and write it when it fires */
-		pm8921_bms_enable_irq(chip, PM8921_BMS_CCADC_EOC);
-	}
-
-	rc = calib_ccadc_enable_arbiter(chip);
-	if (rc < 0) {
-		pr_err("error = %d enabling arbiter for gain\n", rc);
-		goto bail;
-	}
-
-	/*
-	 * Set decimation ratio to 4k, lower ratio may be used in order to speed
-	 * up, pending verification through bench
-	 */
-	rc = pm8xxx_writeb(chip->dev->parent, ADC_ARB_SECP_DIG_PARAM,
-							CCADC_CALIB_DIG_PARAM);
-	if (rc < 0) {
-		pr_err("error = %d enabling decimation ration for gain\n", rc);
-		goto bail;
-	}
-
-	result_gain = 0;
-	for (i = 0; i < SAMPLE_COUNT; i++) {
-		rc = pm8xxx_writeb(chip->dev->parent,
-					ADC_ARB_SECP_RSV, CCADC_CALIB_RSV_25MV);
-		if (rc < 0) {
-			pr_err("error = %d selecting 25mV for gain\n", rc);
-			goto bail;
-		}
-
-		/* Enable CCADC */
-		rc = pm8xxx_writeb(chip->dev->parent, ADC_ARB_SECP_ANA_PARAM,
-							CCADC_CALIB_ANA_PARAM);
-		if (rc < 0) {
-			pr_err("error = %d enabling ccadc\n", rc);
-			goto bail;
-		}
-
-		rc = calib_start_conv(chip, &result);
-		if (rc < 0) {
-			pr_err("error = %d for adc reading 25mV\n", rc);
-			goto bail;
-		}
-
-		result_gain += result;
-	}
-	result_gain = result_gain / SAMPLE_COUNT;
-
-	/*
-	 * result_offset includes INTRINSIC OFFSET
-	 * chip->ccadc_gain_uv will be the actual voltage
-	 * measured for 25000UV
-	 */
-	chip->ccadc_gain_uv = ccadc_reading_to_microvolt(chip,
-				((s64)result_gain - result_offset));
-
-	pr_debug("gain result_gain = 0x%x, voltage = %d microVolts\n",
-							result_gain,
-							chip->ccadc_gain_uv);
-	/* Sanity Check */
-	if (chip->ccadc_gain_uv > CCADC_MAX_25MV) {
-		pr_err("gain voltage = %d is huge limiting to %d\n",
-					chip->ccadc_gain_uv, CCADC_MAX_25MV);
-		chip->ccadc_gain_uv = CCADC_MAX_25MV;
-		result_gain = result_offset +
-			microvolt_to_ccadc_reading(chip, CCADC_MAX_25MV);
-	} else if (chip->ccadc_gain_uv < CCADC_MIN_25MV) {
-		pr_err("gain voltage = %d is too low limiting to %d\n",
-					chip->ccadc_gain_uv, CCADC_MIN_25MV);
-		chip->ccadc_gain_uv = CCADC_MIN_25MV;
-		result_gain = result_offset +
-			microvolt_to_ccadc_reading(chip, CCADC_MIN_25MV);
-	}
-
-	data_msb = result_gain >> 8;
-	data_lsb = result_gain;
-	rc = calib_ccadc_program_trim(chip, CCADC_FULLSCALE_TRIM1,
-						data_msb, data_lsb, 0);
-	if (rc)
-		pr_debug("error = %d programming gain trim\n", rc);
-bail:
-	pm8xxx_writeb(chip->dev->parent, ADC_ARB_SECP_CNTRL, sec_cntrl);
-}
-
 static void calibrate_ccadc_work(struct work_struct *work)
 {
 	struct pm8921_bms_chip *chip = container_of(work,
 				struct pm8921_bms_chip, calib_ccadc_work.work);
 
-	calib_ccadc(chip);
+	pm8xxx_calib_ccadc();
 	schedule_delayed_work(&chip->calib_ccadc_work,
 			round_jiffies_relative(msecs_to_jiffies
 			(chip->calib_delay_ms)));
@@ -1810,23 +1363,6 @@
 	return IRQ_HANDLED;
 }
 
-static irqreturn_t pm8921_bms_ccadc_eoc_handler(int irq, void *data)
-{
-	u8 data_msb, data_lsb;
-	struct pm8921_bms_chip *chip = data;
-	int rc;
-
-	pr_debug("irq = %d triggered\n", irq);
-	data_msb = chip->ccadc_result_offset >> 8;
-	data_lsb = chip->ccadc_result_offset;
-
-	rc = calib_ccadc_program_trim(chip, CCADC_OFFSET_TRIM1,
-						data_msb, data_lsb, 0);
-	pm8921_bms_disable_irq(chip, PM8921_BMS_CCADC_EOC);
-
-	return IRQ_HANDLED;
-}
-
 struct pm_bms_irq_init_data {
 	unsigned int	irq_id;
 	char		*name;
@@ -1857,8 +1393,6 @@
 				pm8921_bms_good_ocv_handler),
 	BMS_IRQ(PM8921_BMS_VSENSE_AVG, IRQF_TRIGGER_RISING,
 				pm8921_bms_vsense_avg_handler),
-	BMS_IRQ(PM8921_BMS_CCADC_EOC, IRQF_TRIGGER_RISING,
-				pm8921_bms_ccadc_eoc_handler),
 };
 
 static void free_irqs(struct pm8921_bms_chip *chip)
@@ -2076,7 +1610,7 @@
 	case CALIB_CCADC:
 		/* reading this will trigger calibration */
 		*val = 0;
-		calib_ccadc(the_chip);
+		pm8xxx_calib_ccadc();
 		break;
 	default:
 		ret = -EINVAL;
@@ -2169,7 +1703,7 @@
 {
 	int i;
 
-	chip->dent = debugfs_create_dir("pm8921_bms", NULL);
+	chip->dent = debugfs_create_dir("pm8921-bms", NULL);
 
 	if (IS_ERR(chip->dent)) {
 		pr_err("pmic bms couldnt create debugfs dir\n");
@@ -2184,20 +1718,6 @@
 			(void *)BMS_OUTPUT1, &reg_fops);
 	debugfs_create_file("BMS_TEST1", 0644, chip->dent,
 			(void *)BMS_TEST1, &reg_fops);
-	debugfs_create_file("CCADC_ANA_PARAM", 0644, chip->dent,
-			(void *)CCADC_ANA_PARAM, &reg_fops);
-	debugfs_create_file("CCADC_DIG_PARAM", 0644, chip->dent,
-			(void *)CCADC_DIG_PARAM, &reg_fops);
-	debugfs_create_file("CCADC_RSV", 0644, chip->dent,
-			(void *)CCADC_RSV, &reg_fops);
-	debugfs_create_file("CCADC_DATA0", 0644, chip->dent,
-			(void *)CCADC_DATA0, &reg_fops);
-	debugfs_create_file("CCADC_DATA1", 0644, chip->dent,
-			(void *)CCADC_DATA1, &reg_fops);
-	debugfs_create_file("CCADC_OFFSET_TRIM1", 0644, chip->dent,
-			(void *)CCADC_OFFSET_TRIM1, &reg_fops);
-	debugfs_create_file("CCADC_OFFSET_TRIM0", 0644, chip->dent,
-			(void *)CCADC_OFFSET_TRIM0, &reg_fops);
 
 	debugfs_create_file("test_batt_temp", 0644, chip->dent,
 				(void *)TEST_BATT_TEMP, &temp_fops);
@@ -2296,7 +1816,6 @@
 	create_debugfs_entries(chip);
 
 	check_initial_ocv(chip);
-	chip->ccadc_gain_uv = calib_ccadc_read_gain_uv(chip);
 
 	INIT_DELAYED_WORK(&chip->calib_ccadc_work, calibrate_ccadc_work);
 	/* begin calibration only on chips > 2.0 */
diff --git a/drivers/power/pm8xxx-ccadc.c b/drivers/power/pm8xxx-ccadc.c
new file mode 100644
index 0000000..1245045
--- /dev/null
+++ b/drivers/power/pm8xxx-ccadc.c
@@ -0,0 +1,761 @@
+/* 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/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/errno.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/mfd/pm8xxx/ccadc.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+
+#define CCADC_ANA_PARAM		0x240
+#define CCADC_DIG_PARAM		0x241
+#define CCADC_RSV		0x242
+#define CCADC_DATA0		0x244
+#define CCADC_DATA1		0x245
+#define CCADC_OFFSET_TRIM1	0x34A
+#define CCADC_OFFSET_TRIM0	0x34B
+#define CCADC_FULLSCALE_TRIM1	0x34C
+#define CCADC_FULLSCALE_TRIM0	0x34D
+
+/* note : TRIM1 is the msb and TRIM0 is the lsb */
+#define ADC_ARB_SECP_CNTRL	0x190
+#define ADC_ARB_SECP_AMUX_CNTRL	0x191
+#define ADC_ARB_SECP_ANA_PARAM	0x192
+#define ADC_ARB_SECP_DIG_PARAM	0x193
+#define ADC_ARB_SECP_RSV	0x194
+#define ADC_ARB_SECP_DATA1	0x195
+#define ADC_ARB_SECP_DATA0	0x196
+
+#define ADC_ARB_BMS_CNTRL	0x18D
+
+#define START_CONV_BIT	BIT(7)
+#define EOC_CONV_BIT	BIT(6)
+#define SEL_CCADC_BIT	BIT(1)
+#define EN_ARB_BIT	BIT(0)
+
+#define CCADC_CALIB_DIG_PARAM	0xE3
+#define CCADC_CALIB_RSV_GND	0x40
+#define CCADC_CALIB_RSV_25MV	0x80
+#define CCADC_CALIB_ANA_PARAM	0x1B
+#define SAMPLE_COUNT		16
+#define ADC_WAIT_COUNT		10
+
+#define CCADC_MAX_25MV		30000
+#define CCADC_MIN_25MV		20000
+#define CCADC_MAX_0UV		-4000
+#define CCADC_MIN_0UV		-7000
+
+#define CCADC_INTRINSIC_OFFSET  0xC000
+
+struct pm8xxx_ccadc_chip {
+	struct device		*dev;
+	struct dentry		*dent;
+	u16			ccadc_offset;
+	int			ccadc_gain_uv;
+	unsigned int		revision;
+	int			eoc_irq;
+	int			r_sense;
+};
+
+static struct pm8xxx_ccadc_chip *the_chip;
+
+static s64 microvolt_to_ccadc_reading_v1(s64 uv)
+{
+	return div_s64(uv * CCADC_READING_RESOLUTION_D_V1,
+				CCADC_READING_RESOLUTION_N_V1);
+}
+
+static s64 microvolt_to_ccadc_reading_v2(s64 uv)
+{
+	return div_s64(uv * CCADC_READING_RESOLUTION_D_V2,
+				CCADC_READING_RESOLUTION_N_V2);
+}
+
+static s64 microvolt_to_ccadc_reading(struct pm8xxx_ccadc_chip *chip, s64 cc)
+{
+	/*
+	 * resolution (the value of a single bit) was changed after revision 2.0
+	 * for more accurate readings
+	 */
+	return (the_chip->revision < PM8XXX_REVISION_8921_2p0) ?
+				microvolt_to_ccadc_reading_v1((s64)cc) :
+				microvolt_to_ccadc_reading_v2((s64)cc);
+}
+
+static int cc_adjust_for_offset(u16 raw)
+{
+	/* this has the intrinsic offset */
+	return (int)raw - the_chip->ccadc_offset;
+}
+
+#define GAIN_REFERENCE_UV 25000
+/*
+ * gain compensation for ccadc readings - common for vsense based and
+ * couloumb counter based readings
+ */
+s64 pm8xxx_cc_adjust_for_gain(s64 uv)
+{
+	if (the_chip == NULL || the_chip->ccadc_gain_uv == 0)
+		return uv;
+
+	return div_s64(uv * GAIN_REFERENCE_UV, the_chip->ccadc_gain_uv);
+}
+EXPORT_SYMBOL(pm8xxx_cc_adjust_for_gain);
+
+static int pm_ccadc_masked_write(struct pm8xxx_ccadc_chip *chip, u16 addr,
+							u8 mask, u8 val)
+{
+	int rc;
+	u8 reg;
+
+	rc = pm8xxx_readb(chip->dev->parent, addr, &reg);
+	if (rc) {
+		pr_err("read failed addr = %03X, rc = %d\n", addr, rc);
+		return rc;
+	}
+	reg &= ~mask;
+	reg |= val & mask;
+	rc = pm8xxx_writeb(chip->dev->parent, addr, reg);
+	if (rc) {
+		pr_err("write failed addr = %03X, rc = %d\n", addr, rc);
+		return rc;
+	}
+	return 0;
+}
+
+#define REG_SBI_CONFIG		0x04F
+#define PAGE3_ENABLE_MASK	0x6
+static int calib_ccadc_enable_trim_access(struct pm8xxx_ccadc_chip *chip,
+								u8 *sbi_config)
+{
+	u8 reg;
+	int rc;
+
+	rc = pm8xxx_readb(chip->dev->parent, REG_SBI_CONFIG, sbi_config);
+	if (rc) {
+		pr_err("error = %d reading sbi config reg\n", rc);
+		return rc;
+	}
+
+	reg = *sbi_config | PAGE3_ENABLE_MASK;
+	return pm8xxx_writeb(chip->dev->parent, REG_SBI_CONFIG, reg);
+}
+
+static int calib_ccadc_restore_trim_access(struct pm8xxx_ccadc_chip *chip,
+							u8 sbi_config)
+{
+	return pm8xxx_writeb(chip->dev->parent, REG_SBI_CONFIG, sbi_config);
+}
+
+static int calib_ccadc_enable_arbiter(struct pm8xxx_ccadc_chip *chip)
+{
+	int rc;
+
+	/* enable Arbiter, must be sent twice */
+	rc = pm_ccadc_masked_write(chip, ADC_ARB_SECP_CNTRL,
+			SEL_CCADC_BIT | EN_ARB_BIT, SEL_CCADC_BIT | EN_ARB_BIT);
+	if (rc < 0) {
+		pr_err("error = %d enabling arbiter for offset\n", rc);
+		return rc;
+	}
+	rc = pm_ccadc_masked_write(chip, ADC_ARB_SECP_CNTRL,
+			SEL_CCADC_BIT | EN_ARB_BIT, SEL_CCADC_BIT | EN_ARB_BIT);
+	if (rc < 0) {
+		pr_err("error = %d writing ADC_ARB_SECP_CNTRL\n", rc);
+		return rc;
+	}
+	return 0;
+}
+
+static int calib_start_conv(struct pm8xxx_ccadc_chip *chip,
+					u16 *result)
+{
+	int rc, i;
+	u8 data_msb, data_lsb, reg;
+
+	/* Start conversion */
+	rc = pm_ccadc_masked_write(chip, ADC_ARB_SECP_CNTRL,
+					START_CONV_BIT, START_CONV_BIT);
+	if (rc < 0) {
+		pr_err("error = %d starting offset meas\n", rc);
+		return rc;
+	}
+
+	/* Wait for End of conversion */
+	for (i = 0; i < ADC_WAIT_COUNT; i++) {
+		rc = pm8xxx_readb(chip->dev->parent,
+					ADC_ARB_SECP_CNTRL, &reg);
+		if (rc < 0) {
+			pr_err("error = %d read eoc for offset\n", rc);
+			return rc;
+		}
+		if ((reg & (START_CONV_BIT | EOC_CONV_BIT)) != EOC_CONV_BIT)
+			msleep(60);
+		else
+			break;
+	}
+	if (i == ADC_WAIT_COUNT) {
+		pr_err("waited too long for offset eoc\n");
+		return rc;
+	}
+
+	rc = pm8xxx_readb(chip->dev->parent, ADC_ARB_SECP_DATA0, &data_lsb);
+	if (rc < 0) {
+		pr_err("error = %d reading offset lsb\n", rc);
+		return rc;
+	}
+
+	rc = pm8xxx_readb(chip->dev->parent, ADC_ARB_SECP_DATA1, &data_msb);
+	if (rc < 0) {
+		pr_err("error = %d reading offset msb\n", rc);
+		return rc;
+	}
+
+	*result = (data_msb << 8) | data_lsb;
+	return 0;
+}
+
+static int calib_ccadc_read_trim(struct pm8xxx_ccadc_chip *chip,
+					int addr, u8 *data_msb, u8 *data_lsb)
+{
+	int rc;
+	u8 sbi_config;
+
+	calib_ccadc_enable_trim_access(chip, &sbi_config);
+	rc = pm8xxx_readb(chip->dev->parent, addr, data_msb);
+	if (rc < 0) {
+		pr_err("error = %d read msb\n", rc);
+		return rc;
+	}
+	rc = pm8xxx_readb(chip->dev->parent, addr + 1, data_lsb);
+	if (rc < 0) {
+		pr_err("error = %d read lsb\n", rc);
+		return rc;
+	}
+	calib_ccadc_restore_trim_access(chip, sbi_config);
+	return 0;
+}
+
+static void calib_ccadc_read_offset_and_gain(struct pm8xxx_ccadc_chip *chip,
+						int *gain, u16 *offset)
+{
+	s8 data_msb;
+	u8 data_lsb;
+	int rc;
+
+	rc = calib_ccadc_read_trim(chip, CCADC_FULLSCALE_TRIM1,
+						&data_msb, &data_lsb);
+	*gain = (data_msb << 8) | data_lsb;
+
+	rc = calib_ccadc_read_trim(chip, CCADC_OFFSET_TRIM1,
+						&data_msb, &data_lsb);
+	*offset = (data_msb << 8) | data_lsb;
+
+	pr_debug("raw gain trim = 0x%x offset trim =0x%x\n", *gain, *offset);
+	*gain = pm8xxx_ccadc_reading_to_microvolt(chip->revision,
+							(s64)*gain - *offset);
+	pr_debug("gain uv = %duV offset=0x%x\n", *gain, *offset);
+}
+
+#define CCADC_PROGRAM_TRIM_COUNT	2
+#define ADC_ARB_BMS_CNTRL_CCADC_SHIFT	4
+#define ADC_ARB_BMS_CNTRL_CONV_MASK	0x03
+#define BMS_CONV_IN_PROGRESS		0x2
+
+static int calib_ccadc_program_trim(struct pm8xxx_ccadc_chip *chip,
+					int addr, u8 data_msb, u8 data_lsb,
+					int wait)
+{
+	int i, rc, loop;
+	u8 cntrl, sbi_config;
+	bool in_progress = 0;
+
+	loop = wait ? CCADC_PROGRAM_TRIM_COUNT : 0;
+
+	calib_ccadc_enable_trim_access(chip, &sbi_config);
+
+	for (i = 0; i < loop; i++) {
+		rc = pm8xxx_readb(chip->dev->parent, ADC_ARB_BMS_CNTRL, &cntrl);
+		if (rc < 0) {
+			pr_err("error = %d reading ADC_ARB_BMS_CNTRL\n", rc);
+			return rc;
+		}
+
+		/* break if a ccadc conversion is not happening */
+		in_progress = (((cntrl >> ADC_ARB_BMS_CNTRL_CCADC_SHIFT)
+			& ADC_ARB_BMS_CNTRL_CONV_MASK) == BMS_CONV_IN_PROGRESS);
+
+		if (!in_progress)
+			break;
+	}
+
+	if (in_progress) {
+		pr_debug("conv in progress cannot write trim,returing EBUSY\n");
+		return -EBUSY;
+	}
+
+	rc = pm8xxx_writeb(chip->dev->parent, addr, data_msb);
+	if (rc < 0) {
+		pr_err("error = %d write msb = 0x%x\n", rc, data_msb);
+		return rc;
+	}
+	rc = pm8xxx_writeb(chip->dev->parent, addr + 1, data_lsb);
+	if (rc < 0) {
+		pr_err("error = %d write lsb = 0x%x\n", rc, data_lsb);
+		return rc;
+	}
+	calib_ccadc_restore_trim_access(chip, sbi_config);
+	return 0;
+}
+
+void pm8xxx_calib_ccadc(void)
+{
+	u8 data_msb, data_lsb, sec_cntrl;
+	int result_offset, voltage_offset, result_gain;
+	u16 result;
+	int i, rc;
+
+	rc = pm8xxx_readb(the_chip->dev->parent,
+					ADC_ARB_SECP_CNTRL, &sec_cntrl);
+	if (rc < 0) {
+		pr_err("error = %d reading ADC_ARB_SECP_CNTRL\n", rc);
+		return;
+	}
+
+	rc = calib_ccadc_enable_arbiter(the_chip);
+	if (rc < 0) {
+		pr_err("error = %d enabling arbiter for offset\n", rc);
+		goto bail;
+	}
+
+	/*
+	 * Set decimation ratio to 4k, lower ratio may be used in order to speed
+	 * up, pending verification through bench
+	 */
+	rc = pm8xxx_writeb(the_chip->dev->parent, ADC_ARB_SECP_DIG_PARAM,
+							CCADC_CALIB_DIG_PARAM);
+	if (rc < 0) {
+		pr_err("error = %d writing ADC_ARB_SECP_DIG_PARAM\n", rc);
+		goto bail;
+	}
+
+	result_offset = 0;
+	for (i = 0; i < SAMPLE_COUNT; i++) {
+		/* Short analog inputs to CCADC internally to ground */
+		rc = pm8xxx_writeb(the_chip->dev->parent, ADC_ARB_SECP_RSV,
+							CCADC_CALIB_RSV_GND);
+		if (rc < 0) {
+			pr_err("error = %d selecting gnd voltage\n", rc);
+			goto bail;
+		}
+
+		/* Enable CCADC */
+		rc = pm8xxx_writeb(the_chip->dev->parent,
+				ADC_ARB_SECP_ANA_PARAM, CCADC_CALIB_ANA_PARAM);
+		if (rc < 0) {
+			pr_err("error = %d enabling ccadc\n", rc);
+			goto bail;
+		}
+
+		rc = calib_start_conv(the_chip, &result);
+		if (rc < 0) {
+			pr_err("error = %d for zero volt measurement\n", rc);
+			goto bail;
+		}
+
+		result_offset += result;
+	}
+
+	result_offset = result_offset / SAMPLE_COUNT;
+
+	voltage_offset = pm8xxx_ccadc_reading_to_microvolt(the_chip->revision,
+			((s64)result_offset - CCADC_INTRINSIC_OFFSET));
+
+	pr_debug("offset result_offset = 0x%x, voltage = %d microVolts\n",
+				result_offset, voltage_offset);
+
+	/* Sanity Check */
+	if (voltage_offset > CCADC_MAX_0UV) {
+		pr_err("offset voltage = %d is huge limiting to %d\n",
+					voltage_offset, CCADC_MAX_0UV);
+		result_offset = CCADC_INTRINSIC_OFFSET
+			+ microvolt_to_ccadc_reading(the_chip,
+							(s64)CCADC_MAX_0UV);
+	} else if (voltage_offset < CCADC_MIN_0UV) {
+		pr_err("offset voltage = %d is too low limiting to %d\n",
+					voltage_offset, CCADC_MIN_0UV);
+		result_offset = CCADC_INTRINSIC_OFFSET
+			+ microvolt_to_ccadc_reading(the_chip,
+							(s64)CCADC_MIN_0UV);
+	}
+
+	the_chip->ccadc_offset = result_offset;
+	data_msb = the_chip->ccadc_offset >> 8;
+	data_lsb = the_chip->ccadc_offset;
+
+	rc = calib_ccadc_program_trim(the_chip, CCADC_OFFSET_TRIM1,
+						data_msb, data_lsb, 1);
+	if (rc) {
+		pr_debug("error = %d programming offset trim 0x%02x 0x%02x\n",
+					rc, data_msb, data_lsb);
+		/* enable the interrupt and write it when it fires */
+		enable_irq(the_chip->eoc_irq);
+	}
+
+	rc = calib_ccadc_enable_arbiter(the_chip);
+	if (rc < 0) {
+		pr_err("error = %d enabling arbiter for gain\n", rc);
+		goto bail;
+	}
+
+	/*
+	 * Set decimation ratio to 4k, lower ratio may be used in order to speed
+	 * up, pending verification through bench
+	 */
+	rc = pm8xxx_writeb(the_chip->dev->parent, ADC_ARB_SECP_DIG_PARAM,
+							CCADC_CALIB_DIG_PARAM);
+	if (rc < 0) {
+		pr_err("error = %d enabling decimation ration for gain\n", rc);
+		goto bail;
+	}
+
+	result_gain = 0;
+	for (i = 0; i < SAMPLE_COUNT; i++) {
+		rc = pm8xxx_writeb(the_chip->dev->parent,
+					ADC_ARB_SECP_RSV, CCADC_CALIB_RSV_25MV);
+		if (rc < 0) {
+			pr_err("error = %d selecting 25mV for gain\n", rc);
+			goto bail;
+		}
+
+		/* Enable CCADC */
+		rc = pm8xxx_writeb(the_chip->dev->parent,
+			ADC_ARB_SECP_ANA_PARAM, CCADC_CALIB_ANA_PARAM);
+		if (rc < 0) {
+			pr_err("error = %d enabling ccadc\n", rc);
+			goto bail;
+		}
+
+		rc = calib_start_conv(the_chip, &result);
+		if (rc < 0) {
+			pr_err("error = %d for adc reading 25mV\n", rc);
+			goto bail;
+		}
+
+		result_gain += result;
+	}
+	result_gain = result_gain / SAMPLE_COUNT;
+
+	/*
+	 * result_offset includes INTRINSIC OFFSET
+	 * the_chip->ccadc_gain_uv will be the actual voltage
+	 * measured for 25000UV
+	 */
+	the_chip->ccadc_gain_uv = pm8xxx_ccadc_reading_to_microvolt(
+				the_chip->revision,
+				((s64)result_gain - result_offset));
+
+	pr_debug("gain result_gain = 0x%x, voltage = %d microVolts\n",
+					result_gain, the_chip->ccadc_gain_uv);
+	/* Sanity Check */
+	if (the_chip->ccadc_gain_uv > CCADC_MAX_25MV) {
+		pr_err("gain voltage = %d is huge limiting to %d\n",
+					the_chip->ccadc_gain_uv,
+					CCADC_MAX_25MV);
+		the_chip->ccadc_gain_uv = CCADC_MAX_25MV;
+		result_gain = result_offset +
+			microvolt_to_ccadc_reading(the_chip, CCADC_MAX_25MV);
+	} else if (the_chip->ccadc_gain_uv < CCADC_MIN_25MV) {
+		pr_err("gain voltage = %d is too low limiting to %d\n",
+					the_chip->ccadc_gain_uv,
+					CCADC_MIN_25MV);
+		the_chip->ccadc_gain_uv = CCADC_MIN_25MV;
+		result_gain = result_offset +
+			microvolt_to_ccadc_reading(the_chip, CCADC_MIN_25MV);
+	}
+
+	data_msb = result_gain >> 8;
+	data_lsb = result_gain;
+	rc = calib_ccadc_program_trim(the_chip, CCADC_FULLSCALE_TRIM1,
+						data_msb, data_lsb, 0);
+	if (rc)
+		pr_debug("error = %d programming gain trim\n", rc);
+bail:
+	pm8xxx_writeb(the_chip->dev->parent, ADC_ARB_SECP_CNTRL, sec_cntrl);
+}
+EXPORT_SYMBOL(pm8xxx_calib_ccadc);
+
+static irqreturn_t pm8921_bms_ccadc_eoc_handler(int irq, void *data)
+{
+	u8 data_msb, data_lsb;
+	struct pm8xxx_ccadc_chip *chip = data;
+	int rc;
+
+	pr_debug("irq = %d triggered\n", irq);
+	data_msb = chip->ccadc_offset >> 8;
+	data_lsb = chip->ccadc_offset;
+
+	rc = calib_ccadc_program_trim(chip, CCADC_OFFSET_TRIM1,
+						data_msb, data_lsb, 0);
+	disable_irq_nosync(chip->eoc_irq);
+
+	return IRQ_HANDLED;
+}
+
+#define CCADC_IBAT_DIG_PARAM	0xA3
+#define CCADC_IBAT_RSV		0x10
+#define CCADC_IBAT_ANA_PARAM	0x1A
+static int ccadc_get_rsense_voltage(int *voltage_uv)
+{
+	u16 raw;
+	int result;
+	int rc = 0;
+
+	rc = calib_ccadc_enable_arbiter(the_chip);
+	if (rc < 0) {
+		pr_err("error = %d enabling arbiter for offset\n", rc);
+		return rc;
+	}
+
+	rc = pm8xxx_writeb(the_chip->dev->parent, ADC_ARB_SECP_DIG_PARAM,
+							CCADC_IBAT_DIG_PARAM);
+	if (rc < 0) {
+		pr_err("error = %d writing ADC_ARB_SECP_DIG_PARAM\n", rc);
+		return rc;
+	}
+
+	rc = pm8xxx_writeb(the_chip->dev->parent, ADC_ARB_SECP_RSV,
+						CCADC_IBAT_RSV);
+	if (rc < 0) {
+		pr_err("error = %d selecting rsense\n", rc);
+		return rc;
+	}
+
+	rc = pm8xxx_writeb(the_chip->dev->parent,
+			ADC_ARB_SECP_ANA_PARAM, CCADC_IBAT_ANA_PARAM);
+	if (rc < 0) {
+		pr_err("error = %d enabling ccadc\n", rc);
+		return rc;
+	}
+
+	rc = calib_start_conv(the_chip, &raw);
+	if (rc < 0) {
+		pr_err("error = %d for zero volt measurement\n", rc);
+		return rc;
+	}
+
+	pr_debug("Vsense raw = 0x%x\n", raw);
+	result = cc_adjust_for_offset(raw);
+	pr_debug("Vsense after offset raw = 0x%x offset=0x%x\n",
+					result,
+					the_chip->ccadc_offset);
+	*voltage_uv = pm8xxx_ccadc_reading_to_microvolt(the_chip->revision,
+			((s64)result));
+	pr_debug("Vsense before gain = %d uV\n", *voltage_uv);
+	*voltage_uv = pm8xxx_cc_adjust_for_gain(*voltage_uv);
+
+	pr_debug("Vsense = %d uV\n", *voltage_uv);
+	return 0;
+}
+
+int pm8xxx_ccadc_get_battery_current(int *bat_current)
+{
+	int voltage_uv, rc;
+
+	rc = ccadc_get_rsense_voltage(&voltage_uv);
+	if (rc) {
+		pr_err("cant get voltage across rsense rc = %d\n", rc);
+		return rc;
+	}
+
+	*bat_current = voltage_uv/the_chip->r_sense;
+	/*
+	 * ccadc reads +ve current when the battery is charging
+	 * We need to return -ve if the battery is charging
+	 */
+	*bat_current = -1 * (*bat_current);
+	pr_debug("bat current = %d ma\n", *bat_current);
+	return 0;
+}
+EXPORT_SYMBOL(pm8xxx_ccadc_get_battery_current);
+
+static int get_reg(void *data, u64 * val)
+{
+	int addr = (int)data;
+	int ret;
+	u8 temp;
+
+	ret = pm8xxx_readb(the_chip->dev->parent, addr, &temp);
+	if (ret) {
+		pr_err("pm8xxx_readb to %x value = %d errored = %d\n",
+			addr, temp, ret);
+		return -EAGAIN;
+	}
+	*val = temp;
+	return 0;
+}
+
+static int set_reg(void *data, u64 val)
+{
+	int addr = (int)data;
+	int ret;
+	u8 temp;
+
+	temp = (u8) val;
+	ret = pm8xxx_writeb(the_chip->dev->parent, addr, temp);
+	if (ret) {
+		pr_err("pm8xxx_writeb to %x value = %d errored = %d\n",
+			addr, temp, ret);
+		return -EAGAIN;
+	}
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(reg_fops, get_reg, set_reg, "0x%02llx\n");
+
+static int get_calc(void *data, u64 * val)
+{
+	int ibat, rc;
+
+	rc = pm8xxx_ccadc_get_battery_current(&ibat);
+	*val = ibat;
+	return rc;
+}
+DEFINE_SIMPLE_ATTRIBUTE(calc_fops, get_calc, NULL, "%lld\n");
+
+static void create_debugfs_entries(struct pm8xxx_ccadc_chip *chip)
+{
+	chip->dent = debugfs_create_dir("pm8xxx-ccadc", NULL);
+
+	if (IS_ERR(chip->dent)) {
+		pr_err("ccadc couldnt create debugfs dir\n");
+		return;
+	}
+
+	debugfs_create_file("CCADC_ANA_PARAM", 0644, chip->dent,
+			(void *)CCADC_ANA_PARAM, &reg_fops);
+	debugfs_create_file("CCADC_DIG_PARAM", 0644, chip->dent,
+			(void *)CCADC_DIG_PARAM, &reg_fops);
+	debugfs_create_file("CCADC_RSV", 0644, chip->dent,
+			(void *)CCADC_RSV, &reg_fops);
+	debugfs_create_file("CCADC_DATA0", 0644, chip->dent,
+			(void *)CCADC_DATA0, &reg_fops);
+	debugfs_create_file("CCADC_DATA1", 0644, chip->dent,
+			(void *)CCADC_DATA1, &reg_fops);
+	debugfs_create_file("CCADC_OFFSET_TRIM1", 0644, chip->dent,
+			(void *)CCADC_OFFSET_TRIM1, &reg_fops);
+	debugfs_create_file("CCADC_OFFSET_TRIM0", 0644, chip->dent,
+			(void *)CCADC_OFFSET_TRIM0, &reg_fops);
+	debugfs_create_file("CCADC_FULLSCALE_TRIM1", 0644, chip->dent,
+			(void *)CCADC_FULLSCALE_TRIM1, &reg_fops);
+	debugfs_create_file("CCADC_FULLSCALE_TRIM0", 0644, chip->dent,
+			(void *)CCADC_FULLSCALE_TRIM0, &reg_fops);
+
+	debugfs_create_file("show_ibatt", 0644, chip->dent,
+				(void *)0, &calc_fops);
+}
+
+static int __devinit pm8xxx_ccadc_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+	struct pm8xxx_ccadc_chip *chip;
+	struct resource *res;
+	const struct pm8xxx_ccadc_platform_data *pdata
+				= pdev->dev.platform_data;
+
+	if (!pdata) {
+		pr_err("missing platform data\n");
+		return -EINVAL;
+	}
+	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+			"PM8921_BMS_CCADC_EOC");
+	if  (!res) {
+		pr_err("failed to get irq\n");
+		return -EINVAL;
+	}
+
+	chip = kzalloc(sizeof(struct pm8xxx_ccadc_chip), GFP_KERNEL);
+	if (!chip) {
+		pr_err("Cannot allocate pm_bms_chip\n");
+		return -ENOMEM;
+	}
+	chip->dev = &pdev->dev;
+	chip->revision = pm8xxx_get_revision(chip->dev->parent);
+	chip->eoc_irq = res->start;
+	chip->r_sense = pdata->r_sense;
+
+	rc = request_irq(chip->eoc_irq,
+			pm8921_bms_ccadc_eoc_handler, IRQF_TRIGGER_RISING,
+			"bms_eoc_ccadc", chip);
+	if (rc) {
+		pr_err("failed to request %d irq rc= %d\n", chip->eoc_irq, rc);
+		goto free_chip;
+	}
+
+	platform_set_drvdata(pdev, chip);
+	the_chip = chip;
+
+	create_debugfs_entries(chip);
+
+	calib_ccadc_read_offset_and_gain(chip,
+					&chip->ccadc_gain_uv,
+					&chip->ccadc_offset);
+	return 0;
+
+free_chip:
+	kfree(chip);
+	return rc;
+}
+
+static int __devexit pm8xxx_ccadc_remove(struct platform_device *pdev)
+{
+	struct pm8xxx_ccadc_chip *chip = platform_get_drvdata(pdev);
+
+	debugfs_remove_recursive(chip->dent);
+	the_chip = NULL;
+	kfree(chip);
+	return 0;
+}
+
+static struct platform_driver pm8xxx_ccadc_driver = {
+	.probe	= pm8xxx_ccadc_probe,
+	.remove	= __devexit_p(pm8xxx_ccadc_remove),
+	.driver	= {
+		.name	= PM8XXX_CCADC_DEV_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init pm8xxx_ccadc_init(void)
+{
+	return platform_driver_register(&pm8xxx_ccadc_driver);
+}
+
+static void __exit pm8xxx_ccadc_exit(void)
+{
+	platform_driver_unregister(&pm8xxx_ccadc_driver);
+}
+
+module_init(pm8xxx_ccadc_init);
+module_exit(pm8xxx_ccadc_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC8XXX ccadc driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:" PM8XXX_CCADC_DEV_NAME);
diff --git a/include/linux/mfd/pm8xxx/ccadc.h b/include/linux/mfd/pm8xxx/ccadc.h
new file mode 100644
index 0000000..6f6cb39
--- /dev/null
+++ b/include/linux/mfd/pm8xxx/ccadc.h
@@ -0,0 +1,100 @@
+/* Copyright (c) 2010-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.
+ */
+
+#ifndef __PMIC8XXX_CCADC_H__
+#define __PMIC8XXX_CCADC_H__
+
+#include <linux/mfd/pm8xxx/core.h>
+
+#define PM8XXX_CCADC_DEV_NAME "pm8xxx-ccadc"
+
+/**
+ * struct pm8xxx_ccadc_platform_data -
+ * @r_sense:		sense resistor value in (mOhms)
+ */
+struct pm8xxx_ccadc_platform_data {
+	int r_sense;
+};
+
+#define CCADC_READING_RESOLUTION_N_V1	1085069
+#define CCADC_READING_RESOLUTION_D_V1	100000
+#define CCADC_READING_RESOLUTION_N_V2	542535
+#define CCADC_READING_RESOLUTION_D_V2	100000
+
+static s64 pm8xxx_ccadc_reading_to_microvolt_v1(s64 cc)
+{
+	return div_s64(cc * CCADC_READING_RESOLUTION_N_V1,
+					CCADC_READING_RESOLUTION_D_V1);
+}
+
+static s64 pm8xxx_ccadc_reading_to_microvolt_v2(s64 cc)
+{
+	return div_s64(cc * CCADC_READING_RESOLUTION_N_V2,
+					CCADC_READING_RESOLUTION_D_V2);
+}
+
+static inline s64 pm8xxx_ccadc_reading_to_microvolt(int revision, s64 cc)
+{
+	/*
+	 * resolution (the value of a single bit) was changed after revision 2.0
+	 * for more accurate readings
+	 */
+	return (revision < PM8XXX_REVISION_8921_2p0) ?
+				pm8xxx_ccadc_reading_to_microvolt_v1((s64)cc) :
+				pm8xxx_ccadc_reading_to_microvolt_v2((s64)cc);
+}
+
+#if defined(CONFIG_PM8XXX_CCADC) || defined(CONFIG_PM8XXX_CCADC_MODULE)
+/**
+ * pm8xxx_cc_adjust_for_gain - the function to adjust the voltage read from
+ *			ccadc for gain compensation
+ * @v:	the voltage which needs to be gain compensated in microVolts
+ *
+ *
+ * RETURNS: gain compensated voltage
+ */
+s64 pm8xxx_cc_adjust_for_gain(s64 uv);
+
+/**
+ * pm8xxx_calib_ccadc - calibration for ccadc. This will calculate gain
+ *			and offset and reprogram them in the appropriate
+ *			registers
+ */
+void pm8xxx_calib_ccadc(void);
+
+/**
+ * pm8xxx_ccadc_get_battery_current - return the battery current based on vsense
+ *				resitor in milliamperes
+ * @result:	The pointer where the voltage will be updated. A -ve
+ *		result means that the current is flowing in
+ *		the battery - during battery charging
+ *
+ * RETURNS:	Error code if there was a problem reading vsense, Zero otherwise
+ *		The result won't be updated in case of an error.
+ *
+ */
+int pm8xxx_ccadc_get_battery_current(int *bat_current);
+#else
+static inline s64 pm8xxx_cc_adjust_for_gain(s64 uv)
+{
+	return -ENXIO;
+}
+static inline void pm8xxx_calib_ccadc(void)
+{
+}
+static inline int pm8xxx_ccadc_get_battery_current(int *bat_current)
+{
+	return -ENXIO;
+}
+#endif
+
+#endif /* __PMIC8XXX_CCADC_H__ */
diff --git a/include/linux/mfd/pm8xxx/pm8921.h b/include/linux/mfd/pm8xxx/pm8921.h
index 64bdab1..2c931b6 100644
--- a/include/linux/mfd/pm8xxx/pm8921.h
+++ b/include/linux/mfd/pm8xxx/pm8921.h
@@ -35,6 +35,7 @@
 #include <linux/mfd/pm8xxx/pm8921-bms.h>
 #include <linux/leds-pm8xxx.h>
 #include <linux/mfd/pm8xxx/vibrator.h>
+#include <linux/mfd/pm8xxx/ccadc.h>
 
 #define PM8921_NR_IRQS		256
 
@@ -128,6 +129,7 @@
 	struct pm8921_adc_platform_data		*adc_pdata;
 	struct pm8xxx_led_platform_data		*leds_pdata;
 	struct pm8xxx_vibrator_platform_data	*vibrator_pdata;
+	struct pm8xxx_ccadc_platform_data	*ccadc_pdata;
 };
 
 #endif
diff --git a/scripts/gcc-wrapper.py b/scripts/gcc-wrapper.py
index 97c08c0..cbe2eae 100755
--- a/scripts/gcc-wrapper.py
+++ b/scripts/gcc-wrapper.py
@@ -54,7 +54,6 @@
     "inode.c:72",
     "inode.c:73",
     "inode.c:74",
-    "menu.c:128",
     "msm_sdcc.c:126",
     "msm_sdcc.c:128",
     "nf_conntrack_netlink.c:790",