Merge "msm: Kernel module to support bus performance monitoring applications" into msm-3.0
diff --git a/arch/arm/configs/msm9615_defconfig b/arch/arm/configs/msm9615_defconfig
index ec0dc05..fa6475b 100644
--- a/arch/arm/configs/msm9615_defconfig
+++ b/arch/arm/configs/msm9615_defconfig
@@ -104,7 +104,11 @@
 CONFIG_THERMAL=y
 CONFIG_THERMAL_TSENS8960=y
 CONFIG_MFD_PM8018_CORE=y
-# CONFIG_MFD_PM8XXX_PWM is not set
+CONFIG_MFD_PM8XXX_PWM=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_PM8XXX=y
+# CONFIG_LEDS_MSM_PMIC is not set
 CONFIG_REGULATOR=y
 CONFIG_REGULATOR_GPIO=y
 # CONFIG_HID_SUPPORT is not set
diff --git a/arch/arm/mach-msm/board-9615.c b/arch/arm/mach-msm/board-9615.c
index b0a3d0f..701464a 100644
--- a/arch/arm/mach-msm/board-9615.c
+++ b/arch/arm/mach-msm/board-9615.c
@@ -25,6 +25,8 @@
 #include <linux/usb/android.h>
 #include <linux/usb/msm_hsusb.h>
 #include <linux/mfd/pm8xxx/pm8xxx-adc.h>
+#include <linux/leds.h>
+#include <linux/leds-pm8xxx.h>
 #include "timer.h"
 #include "devices.h"
 #include "board-9615.h"
@@ -94,6 +96,42 @@
 	.priority		= 0,
 };
 
+#define PM8018_LED_KB_MAX_CURRENT	20	/* I = 20mA */
+#define PM8XXX_LED_PWM_PERIOD_US	1000
+
+/**
+ * PM8XXX_PWM_CHANNEL_NONE shall be used when LED shall not be
+ * driven using PWM feature.
+ */
+#define PM8XXX_PWM_CHANNEL_NONE		-1
+
+static struct led_info pm8018_led_info[] = {
+	[0] = {
+		.name	= "led:kb",
+	},
+};
+
+static struct led_platform_data pm8018_led_core_pdata = {
+	.num_leds = ARRAY_SIZE(pm8018_led_info),
+	.leds = pm8018_led_info,
+};
+
+static struct pm8xxx_led_config pm8018_led_configs[] = {
+	[0] = {
+		.id = PM8XXX_ID_LED_KB_LIGHT,
+		.mode = PM8XXX_LED_MODE_PWM3,
+		.max_current = PM8018_LED_KB_MAX_CURRENT,
+		.pwm_channel = 2,
+		.pwm_period_us = PM8XXX_LED_PWM_PERIOD_US,
+	},
+};
+
+static struct pm8xxx_led_platform_data pm8xxx_leds_pdata = {
+		.led_core = &pm8018_led_core_pdata,
+		.configs = pm8018_led_configs,
+		.num_configs = ARRAY_SIZE(pm8018_led_configs),
+};
+
 static struct pm8018_platform_data pm8018_platform_data __devinitdata = {
 	.irq_pdata		= &pm8xxx_irq_pdata,
 	.gpio_pdata		= &pm8xxx_gpio_pdata,
@@ -103,6 +141,7 @@
 	.misc_pdata		= &pm8xxx_misc_pdata,
 	.regulator_pdatas	= msm_pm8018_regulator_pdata,
 	.adc_pdata		= &pm8018_adc_pdata,
+	.leds_pdata		= &pm8xxx_leds_pdata,
 };
 
 static struct msm_ssbi_platform_data msm9615_ssbi_pm8018_pdata __devinitdata = {
diff --git a/arch/arm/mach-msm/board-msm8960.c b/arch/arm/mach-msm/board-msm8960.c
index 1449e34..b06bb53 100644
--- a/arch/arm/mach-msm/board-msm8960.c
+++ b/arch/arm/mach-msm/board-msm8960.c
@@ -4696,6 +4696,7 @@
 	.timer = &msm_timer,
 	.init_machine = msm8960_cdp_init,
 	.init_early = msm8960_allocate_memory_regions,
+	.init_very_early = msm8960_early_memory,
 MACHINE_END
 
 MACHINE_START(MSM8930_MTP, "QCT MSM8930 MTP")
@@ -4705,6 +4706,7 @@
 	.timer = &msm_timer,
 	.init_machine = msm8960_cdp_init,
 	.init_early = msm8960_allocate_memory_regions,
+	.init_very_early = msm8960_early_memory,
 MACHINE_END
 
 MACHINE_START(MSM8930_FLUID, "QCT MSM8930 FLUID")
@@ -4714,5 +4716,6 @@
 	.timer = &msm_timer,
 	.init_machine = msm8960_cdp_init,
 	.init_early = msm8960_allocate_memory_regions,
+	.init_very_early = msm8960_early_memory,
 MACHINE_END
 #endif
diff --git a/arch/arm/mach-msm/board-msm8x60.c b/arch/arm/mach-msm/board-msm8x60.c
index 61b70cf..916645c 100644
--- a/arch/arm/mach-msm/board-msm8x60.c
+++ b/arch/arm/mach-msm/board-msm8x60.c
@@ -109,6 +109,10 @@
 #include "rpm_resources.h"
 #include "acpuclock.h"
 #include "pm-boot.h"
+
+#include <linux/ion.h>
+#include <mach/ion.h>
+
 #define MSM_SHARED_RAM_PHYS 0x40000000
 
 /* Macros assume PMIC GPIOs start at 0 */
@@ -2624,6 +2628,16 @@
 #define USER_SMI_SIZE         (MSM_SMI_SIZE - KERNEL_SMI_SIZE)
 #define MSM_PMEM_SMIPOOL_SIZE USER_SMI_SIZE
 
+#define MSM_ION_EBI_SIZE        MSM_PMEM_SF_SIZE
+#define MSM_ION_ADSP_SIZE       MSM_PMEM_ADSP_SIZE
+#define MSM_ION_SMI_SIZE	MSM_USER_SMI_SIZE
+
+#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+#define MSM_ION_HEAP_NUM	5
+#else
+#define MSM_ION_HEAP_NUM	2
+#endif
+
 static unsigned fb_size;
 static int __init fb_size_setup(char *p)
 {
@@ -2753,6 +2767,7 @@
 };
 
 #ifdef CONFIG_ANDROID_PMEM
+#ifndef CONFIG_MSM_MULTIMEDIA_USE_ION
 static struct android_pmem_platform_data android_pmem_pdata = {
 	.name = "pmem",
 	.allocator_type = PMEM_ALLOCATORTYPE_ALLORNOTHING,
@@ -2778,7 +2793,7 @@
 	.id = 2,
 	.dev = { .platform_data = &android_pmem_adsp_pdata },
 };
-
+#endif
 static struct android_pmem_platform_data android_pmem_audio_pdata = {
 	.name = "pmem_audio",
 	.allocator_type = PMEM_ALLOCATORTYPE_BITMAP,
@@ -2802,6 +2817,7 @@
 		}, \
 	.num_paths = 1, \
 	}
+#ifndef CONFIG_MSM_MULTIMEDIA_USE_ION
 static struct msm_bus_paths pmem_smi_table[] = {
 	[0] = PMEM_BUS_WIDTH(0), /* Off */
 	[1] = PMEM_BUS_WIDTH(1), /* On */
@@ -2846,7 +2862,7 @@
 	.id = 7,
 	.dev = { .platform_data = &android_pmem_smipool_pdata },
 };
-
+#endif
 #endif
 
 #define GPIO_DONGLE_PWR_EN 258
@@ -5001,11 +5017,13 @@
 	&msm_batt_device,
 #endif
 #ifdef CONFIG_ANDROID_PMEM
+#ifndef CONFIG_MSM_MULTIMEDIA_USE_ION
 	&android_pmem_device,
 	&android_pmem_adsp_device,
-	&android_pmem_audio_device,
 	&android_pmem_smipool_device,
 #endif
+	&android_pmem_audio_device,
+#endif
 #ifdef CONFIG_MSM_ROTATOR
 	&msm_rotator_device,
 #endif
@@ -5098,9 +5116,59 @@
 
 	&msm_tsens_device,
 	&msm_rpm_device,
-
+#ifdef CONFIG_ION_MSM
+	&ion_dev,
+#endif
 };
 
+#ifdef CONFIG_ION_MSM
+struct ion_platform_data ion_pdata = {
+	.nr = MSM_ION_HEAP_NUM,
+	.heaps = {
+		{
+			.id	= ION_HEAP_SYSTEM_ID,
+			.type	= ION_HEAP_TYPE_SYSTEM,
+			.name	= ION_VMALLOC_HEAP_NAME,
+		},
+		{
+			.id	= ION_HEAP_SYSTEM_CONTIG_ID,
+			.type	= ION_HEAP_TYPE_SYSTEM_CONTIG,
+			.name	= ION_KMALLOC_HEAP_NAME,
+		},
+#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
+		{
+			.id	= ION_HEAP_EBI_ID,
+			.type	= ION_HEAP_TYPE_CARVEOUT,
+			.name	= ION_EBI1_HEAP_NAME,
+			.size	= MSM_ION_EBI_SIZE,
+			.memory_type = ION_EBI_TYPE,
+		},
+		{
+			.id	= ION_HEAP_ADSP_ID,
+			.type	= ION_HEAP_TYPE_CARVEOUT,
+			.name	= ION_ADSP_HEAP_NAME,
+			.size	= MSM_ION_ADSP_SIZE,
+			.memory_type = ION_EBI_TYPE,
+		},
+		{
+			.id	= ION_HEAP_SMI_ID,
+			.type	= ION_HEAP_TYPE_CARVEOUT,
+			.name	= ION_SMI_HEAP_NAME,
+			.size	= MSM_ION_SMI_SIZE,
+			.memory_type = ION_SMI_TYPE,
+		},
+#endif
+	}
+};
+
+struct platform_device ion_dev = {
+	.name = "ion-msm",
+	.id = 1,
+	.dev = { .platform_data = &ion_pdata },
+};
+#endif
+
+
 static struct memtype_reserve msm8x60_reserve_table[] __initdata = {
 	/* Kernel SMI memory pool for video core, used for firmware */
 	/* and encoder, decoder scratch buffers */
@@ -5128,14 +5196,25 @@
 	},
 };
 
+static void reserve_ion_memory(void)
+{
+#if defined(CONFIG_ION_MSM) && defined(CONFIG_MSM_MULTIMEDIA_USE_ION)
+	msm8x60_reserve_table[MEMTYPE_EBI1].size += MSM_ION_EBI_SIZE;
+	msm8x60_reserve_table[MEMTYPE_EBI1].size += MSM_ION_ADSP_SIZE;
+	msm8x60_reserve_table[MEMTYPE_SMI].size += MSM_ION_SMI_SIZE;
+#endif
+}
+
 static void __init size_pmem_devices(void)
 {
 #ifdef CONFIG_ANDROID_PMEM
+#ifndef CONFIG_MSM_MULTIMEDIA_USE_ION
 	android_pmem_adsp_pdata.size = pmem_adsp_size;
 	android_pmem_smipool_pdata.size = MSM_PMEM_SMIPOOL_SIZE;
-	android_pmem_audio_pdata.size = MSM_PMEM_AUDIO_SIZE;
 	android_pmem_pdata.size = pmem_sf_size;
 #endif
+	android_pmem_audio_pdata.size = MSM_PMEM_AUDIO_SIZE;
+#endif
 }
 
 static void __init reserve_memory_for(struct android_pmem_platform_data *p)
@@ -5146,18 +5225,23 @@
 static void __init reserve_pmem_memory(void)
 {
 #ifdef CONFIG_ANDROID_PMEM
+#ifndef CONFIG_MSM_MULTIMEDIA_USE_ION
 	reserve_memory_for(&android_pmem_adsp_pdata);
 	reserve_memory_for(&android_pmem_smipool_pdata);
-	reserve_memory_for(&android_pmem_audio_pdata);
 	reserve_memory_for(&android_pmem_pdata);
+#endif
+	reserve_memory_for(&android_pmem_audio_pdata);
 	msm8x60_reserve_table[MEMTYPE_EBI1].size += pmem_kernel_ebi1_size;
 #endif
 }
 
+
+
 static void __init msm8x60_calculate_reserve_sizes(void)
 {
 	size_pmem_devices();
 	reserve_pmem_memory();
+	reserve_ion_memory();
 }
 
 static int msm8x60_paddr_to_memtype(unsigned int paddr)
diff --git a/arch/arm/mach-msm/clock-8960.c b/arch/arm/mach-msm/clock-8960.c
index bbe8748..a0c6878 100644
--- a/arch/arm/mach-msm/clock-8960.c
+++ b/arch/arm/mach-msm/clock-8960.c
@@ -5223,7 +5223,7 @@
 	CLK_LOOKUP("usb_hsic_p_clk",	usb_hsic_p_clk.c,	NULL),
 
 	CLK_LOOKUP("ebi1_msmbus_clk",	ebi1_msmbus_clk.c, NULL),
-	CLK_LOOKUP("ebi1_clk",		ebi1_adm_clk.c, "msm_dmov"),
+	CLK_LOOKUP("mem_clk",		ebi1_adm_clk.c, "msm_dmov"),
 
 	CLK_LOOKUP("l2_mclk",		l2_m_clk,     NULL),
 	CLK_LOOKUP("krait0_mclk",	krait0_m_clk, NULL),
diff --git a/arch/arm/mach-msm/clock-debug.c b/arch/arm/mach-msm/clock-debug.c
index 4e0d3e9..ab4d8c1 100644
--- a/arch/arm/mach-msm/clock-debug.c
+++ b/arch/arm/mach-msm/clock-debug.c
@@ -174,7 +174,7 @@
 
 	/* Find max frequency supported within voltage constraints. */
 	if (!clock->vdd_class) {
-		fmax = ULONG_MAX;
+		fmax = INT_MAX;
 	} else {
 		for (level = 0; level < ARRAY_SIZE(clock->fmax); level++)
 			if (clock->fmax[level])
@@ -213,7 +213,7 @@
 	if (!debugfs_base)
 		return -ENOMEM;
 
-	strncpy(temp, clock->dbg_name, ARRAY_SIZE(temp)-1);
+	strlcpy(temp, clock->dbg_name, ARRAY_SIZE(temp));
 	for (ptr = temp; *ptr; ptr++)
 		*ptr = tolower(*ptr);
 
diff --git a/arch/arm/mach-msm/devices-iommu.c b/arch/arm/mach-msm/devices-iommu.c
index 397fdea..3d2d2e7 100644
--- a/arch/arm/mach-msm/devices-iommu.c
+++ b/arch/arm/mach-msm/devices-iommu.c
@@ -1057,11 +1057,7 @@
 static int __init iommu_init(void)
 {
 	int ret;
-	if ((cpu_is_msm8960() &&
-	    SOCINFO_VERSION_MAJOR(socinfo_get_version()) < 2) ||
-	    (cpu_is_msm8x60() &&
-	    (SOCINFO_VERSION_MAJOR(socinfo_get_version()) != 2 ||
-	    SOCINFO_VERSION_MINOR(socinfo_get_version()) < 1)))	{
+	if (!msm_soc_version_supports_iommu()) {
 		pr_err("IOMMU is not supported on this SoC version.\n");
 		return -ENODEV;
 	}
diff --git a/arch/arm/mach-msm/include/mach/iommu.h b/arch/arm/mach-msm/include/mach/iommu.h
index 5542ff4..9405459 100644
--- a/arch/arm/mach-msm/include/mach/iommu.h
+++ b/arch/arm/mach-msm/include/mach/iommu.h
@@ -15,6 +15,7 @@
 
 #include <linux/interrupt.h>
 #include <linux/clk.h>
+#include <mach/socinfo.h>
 
 /* Sharability attributes of MSM IOMMU mappings */
 #define MSM_IOMMU_ATTR_NON_SH		0x0
@@ -121,3 +122,17 @@
 #endif
 
 #endif
+
+static inline int msm_soc_version_supports_iommu(void)
+{
+	if (cpu_is_msm8960() &&
+	    SOCINFO_VERSION_MAJOR(socinfo_get_version()) < 2)
+		return 0;
+
+	if (cpu_is_msm8x60() &&
+	    (SOCINFO_VERSION_MAJOR(socinfo_get_version()) != 2 ||
+	    SOCINFO_VERSION_MINOR(socinfo_get_version()) < 1))	{
+		return 0;
+	}
+	return 1;
+}
diff --git a/arch/arm/mach-msm/iommu.c b/arch/arm/mach-msm/iommu.c
index 0a77781..442a1b4 100644
--- a/arch/arm/mach-msm/iommu.c
+++ b/arch/arm/mach-msm/iommu.c
@@ -1006,6 +1006,9 @@
 
 static int __init msm_iommu_init(void)
 {
+	if (!msm_soc_version_supports_iommu())
+		return -ENODEV;
+
 	setup_iommu_tex_classes();
 	register_iommu(&msm_iommu_ops);
 	return 0;
diff --git a/arch/arm/mach-msm/iommu_dev.c b/arch/arm/mach-msm/iommu_dev.c
index ca2ecc6..d1dd3ed 100644
--- a/arch/arm/mach-msm/iommu_dev.c
+++ b/arch/arm/mach-msm/iommu_dev.c
@@ -69,7 +69,7 @@
 	r.name = ctx_name;
 	found = device_for_each_child(&msm_iommu_root_dev->dev, &r, each_iommu);
 
-	if (!found) {
+	if (!found || !dev_get_drvdata(r.dev)) {
 		pr_err("Could not find context <%s>\n", ctx_name);
 		goto fail;
 	}
diff --git a/arch/arm/mach-msm/timer.c b/arch/arm/mach-msm/timer.c
index b597fb1..1df3c23 100644
--- a/arch/arm/mach-msm/timer.c
+++ b/arch/arm/mach-msm/timer.c
@@ -336,6 +336,7 @@
 	struct msm_clock *clock;
 	struct msm_clock_percpu_data *clock_state, *gpt_state;
 	unsigned long irq_flags;
+	struct irq_chip *chip;
 
 	clock = clockevent_to_clock(evt);
 	clock_state = &__get_cpu_var(msm_clocks_percpu)[clock->index];
@@ -355,11 +356,9 @@
 		get_cpu_var(msm_active_clock) = clock;
 		put_cpu_var(msm_active_clock);
 		__raw_writel(TIMER_ENABLE_EN, clock->regbase + TIMER_ENABLE);
-		if (irq_get_chip(clock->irq.irq) &&
-		   irq_get_chip(clock->irq.irq)->irq_unmask) {
-			irq_get_chip(clock->irq.irq)->irq_unmask(
-				irq_get_irq_data(clock->irq.irq));
-		}
+		chip = irq_get_chip(clock->irq.irq);
+		if (chip && chip->irq_unmask)
+			chip->irq_unmask(irq_get_irq_data(clock->irq.irq));
 		if (clock != &msm_clocks[MSM_CLOCK_GPT])
 			__raw_writel(TIMER_ENABLE_EN,
 				msm_clocks[MSM_CLOCK_GPT].regbase +
@@ -375,11 +374,9 @@
 			msm_read_timer_count(clock, LOCAL_TIMER) +
 			clock_state->sleep_offset;
 		__raw_writel(0, clock->regbase + TIMER_MATCH_VAL);
-		if (irq_get_chip(clock->irq.irq) &&
-		   irq_get_chip(clock->irq.irq)->irq_mask) {
-			irq_get_chip(clock->irq.irq)->irq_mask(
-				irq_get_irq_data(clock->irq.irq));
-		}
+		chip = irq_get_chip(clock->irq.irq);
+		if (chip && chip->irq_mask)
+			chip->irq_mask(irq_get_irq_data(clock->irq.irq));
 
 		if (!is_smp() || clock != &msm_clocks[MSM_CLOCK_DGT]
 				|| smp_processor_id())
@@ -973,6 +970,7 @@
 {
 	int i;
 	int res;
+	struct irq_chip *chip;
 	struct msm_clock *dgt = &msm_clocks[MSM_CLOCK_DGT];
 	struct msm_clock *gpt = &msm_clocks[MSM_CLOCK_GPT];
 
@@ -1056,8 +1054,9 @@
 			printk(KERN_ERR "msm_timer_init: setup_irq "
 			       "failed for %s\n", cs->name);
 
-		irq_get_chip(clock->irq.irq)->irq_mask(irq_get_irq_data(
-							       clock->irq.irq));
+		chip = irq_get_chip(clock->irq.irq);
+		if (chip && chip->irq_mask)
+			chip->irq_mask(irq_get_irq_data(clock->irq.irq));
 
 		clockevents_register_device(ce);
 	}
diff --git a/drivers/base/iommu.c b/drivers/base/iommu.c
index 1377e85..6507d8b 100644
--- a/drivers/base/iommu.c
+++ b/drivers/base/iommu.c
@@ -45,6 +45,9 @@
 	struct iommu_domain *domain;
 	int ret;
 
+	if (!iommu_found())
+		return NULL;
+
 	domain = kmalloc(sizeof(*domain), GFP_KERNEL);
 	if (!domain)
 		return NULL;
@@ -64,6 +67,9 @@
 
 void iommu_domain_free(struct iommu_domain *domain)
 {
+	if (!iommu_found())
+		return;
+
 	iommu_ops->domain_destroy(domain);
 	kfree(domain);
 }
@@ -71,12 +77,18 @@
 
 int iommu_attach_device(struct iommu_domain *domain, struct device *dev)
 {
+	if (!iommu_found())
+		return -ENODEV;
+
 	return iommu_ops->attach_dev(domain, dev);
 }
 EXPORT_SYMBOL_GPL(iommu_attach_device);
 
 void iommu_detach_device(struct iommu_domain *domain, struct device *dev)
 {
+	if (!iommu_found())
+		return;
+
 	iommu_ops->detach_dev(domain, dev);
 }
 EXPORT_SYMBOL_GPL(iommu_detach_device);
@@ -84,6 +96,9 @@
 phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain,
 			       unsigned long iova)
 {
+	if (!iommu_found())
+		return 0;
+
 	return iommu_ops->iova_to_phys(domain, iova);
 }
 EXPORT_SYMBOL_GPL(iommu_iova_to_phys);
@@ -91,6 +106,9 @@
 int iommu_domain_has_cap(struct iommu_domain *domain,
 			 unsigned long cap)
 {
+	if (!iommu_found())
+		return -ENODEV;
+
 	return iommu_ops->domain_has_cap(domain, cap);
 }
 EXPORT_SYMBOL_GPL(iommu_domain_has_cap);
@@ -101,6 +119,9 @@
 	unsigned long invalid_mask;
 	size_t size;
 
+	if (!iommu_found())
+		return -ENODEV;
+
 	size         = 0x1000UL << gfp_order;
 	invalid_mask = size - 1;
 
@@ -115,6 +136,9 @@
 	unsigned long invalid_mask;
 	size_t size;
 
+	if (!iommu_found())
+		return -ENODEV;
+
 	size         = 0x1000UL << gfp_order;
 	invalid_mask = size - 1;
 
@@ -127,6 +151,9 @@
 int iommu_map_range(struct iommu_domain *domain, unsigned int iova,
 		    struct scatterlist *sg, unsigned int len, int prot)
 {
+	if (!iommu_found())
+		return -ENODEV;
+
 	BUG_ON(iova & (~PAGE_MASK));
 
 	return iommu_ops->map_range(domain, iova, sg, len, prot);
@@ -136,6 +163,9 @@
 int iommu_unmap_range(struct iommu_domain *domain, unsigned int iova,
 		      unsigned int len)
 {
+	if (!iommu_found())
+		return -ENODEV;
+
 	BUG_ON(iova & (~PAGE_MASK));
 
 	return iommu_ops->unmap_range(domain, iova, len);
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index feb345d..60c200e 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -354,7 +354,7 @@
 
 config LEDS_MSM_PMIC
         tristate "LED Support for Qualcomm PMIC connected LEDs"
-        default y
+        default n
         depends on ARCH_MSM
         help
           This option enables support for LEDs connected over PMIC
diff --git a/drivers/media/video/msm/msm_mctl_pp.c b/drivers/media/video/msm/msm_mctl_pp.c
index a09514b..2bf95a0 100644
--- a/drivers/media/video/msm/msm_mctl_pp.c
+++ b/drivers/media/video/msm/msm_mctl_pp.c
@@ -126,6 +126,10 @@
 
 	idx = msm_mctl_out_type_to_inst_index(
 		p_mctl->sync.pcam_sync, msg_type);
+	if (idx < 0) {
+		pr_err("%s Invalid instance. returning\n", __func__);
+		return -EINVAL;
+	}
 	pcam_inst = p_mctl->sync.pcam_sync->dev_inst[idx];
 	vb = msm_mctl_buf_find(p_mctl, pcam_inst,
 		  del_buf, msg_type, fbuf);
@@ -192,27 +196,52 @@
 }
 
 static int msm_mctl_pp_get_phy_addr(
+	struct msm_cam_v4l2_dev_inst *pcam_inst,
 	uint32_t frame_handle,
 	struct msm_pp_frame *pp_frame)
 {
 	struct msm_frame_buffer *vb = NULL;
 	struct videobuf2_contig_pmem *mem;
+	int i, buf_idx = 0;
 
 	vb = (struct msm_frame_buffer *)frame_handle;
-	mem = vb2_plane_cookie(&vb->vidbuf, 0);
+	buf_idx = vb->vidbuf.v4l2_buf.index;
 	memset(pp_frame, 0, sizeof(struct msm_pp_frame));
 	pp_frame->handle = (uint32_t)vb;
 	pp_frame->frame_id = vb->vidbuf.v4l2_buf.sequence;
-	pp_frame->image_type = (unsigned short)mem->path;
 	pp_frame->timestamp = vb->vidbuf.v4l2_buf.timestamp;
-	/* hard coded for now. Will need to expand to MP case */
-	pp_frame->num_planes = 1;
-	pp_frame->sp.addr_offset = mem->addr_offset;
-	pp_frame->sp.phy_addr = videobuf2_to_pmem_contig(&vb->vidbuf, 0);
-	pp_frame->sp.y_off = 0;
-	pp_frame->sp.cbcr_off = mem->offset.sp_off.cbcr_off;
-	pp_frame->sp.length = mem->size;
-	pp_frame->sp.fd = (int)mem->vaddr;
+	/* Get the cookie for 1st plane and store the path.
+	 * Also use this to check the number of planes in
+	 * this buffer.*/
+	mem = vb2_plane_cookie(&vb->vidbuf, 0);
+	pp_frame->image_type = (unsigned short)mem->path;
+	if (mem->buffer_type == VIDEOBUF2_SINGLE_PLANE) {
+		pp_frame->num_planes = 1;
+		pp_frame->sp.addr_offset = mem->addr_offset;
+		pp_frame->sp.phy_addr =
+			videobuf2_to_pmem_contig(&vb->vidbuf, 0);
+		pp_frame->sp.y_off = 0;
+		pp_frame->sp.cbcr_off = mem->offset.sp_off.cbcr_off;
+		pp_frame->sp.length = mem->size;
+		pp_frame->sp.fd = (int)mem->vaddr;
+	} else {
+		pp_frame->num_planes = pcam_inst->plane_info.num_planes;
+		for (i = 0; i < pp_frame->num_planes; i++) {
+			mem = vb2_plane_cookie(&vb->vidbuf, i);
+			pp_frame->mp[i].addr_offset = mem->addr_offset;
+			pp_frame->mp[i].phy_addr =
+				videobuf2_to_pmem_contig(&vb->vidbuf, i);
+			pp_frame->mp[i].data_offset =
+			pcam_inst->buf_offset[buf_idx][i].data_offset;
+			pp_frame->mp[i].fd = (int)mem->vaddr;
+			pp_frame->mp[i].length = mem->size;
+			D("%s frame id %d buffer %d plane %d phy addr 0x%x"
+				" fd %d length %d\n", __func__,
+				pp_frame->frame_id, buf_idx, i,
+				(uint32_t)pp_frame->mp[i].phy_addr,
+				pp_frame->mp[i].fd, pp_frame->mp[i].length);
+		}
+	}
 	return 0;
 }
 
@@ -235,12 +264,38 @@
 	return 0;
 }
 
+static int msm_mctl_pp_path_to_inst_index(struct msm_cam_v4l2_device *pcam,
+					int out_type)
+{
+	int image_mode;
+	switch (out_type) {
+	case OUTPUT_TYPE_P:
+		image_mode = MSM_V4L2_EXT_CAPTURE_MODE_PREVIEW;
+		break;
+	case OUTPUT_TYPE_V:
+		image_mode = MSM_V4L2_EXT_CAPTURE_MODE_VIDEO;
+		break;
+	case OUTPUT_TYPE_S:
+		image_mode = MSM_V4L2_EXT_CAPTURE_MODE_MAIN;
+		break;
+	default:
+		image_mode = -1;
+		break;
+	}
+	if ((image_mode >= 0) && pcam->dev_inst_map[image_mode])
+		return pcam->dev_inst_map[image_mode]->my_index;
+	else
+		return -EINVAL;
+}
+
 int msm_mctl_pp_proc_vpe_cmd(
 	struct msm_cam_media_controller *p_mctl,
 	struct msm_mctl_pp_cmd *pp_cmd)
 {
-	int rc = 0;
+	int rc = 0, idx;
 	void __user *argp = (void __user *)pp_cmd->value;
+	struct msm_cam_v4l2_dev_inst *pcam_inst;
+
 	switch (pp_cmd->id) {
 	case VPE_CMD_INIT:
 	case VPE_CMD_DEINIT:
@@ -422,15 +477,27 @@
 				zoom->pp_frame_cmd.cookie,
 				zoom->pp_frame_cmd.vpe_output_action,
 				zoom->pp_frame_cmd.path);
-
+		idx = msm_mctl_pp_path_to_inst_index(p_mctl->sync.pcam_sync,
+			zoom->pp_frame_cmd.path);
+		if (idx < 0) {
+			pr_err("%s Invalid path, returning\n", __func__);
+			kfree(zoom);
+			return idx;
+		}
+		pcam_inst = p_mctl->sync.pcam_sync->dev_inst[idx];
+		if (!pcam_inst) {
+			pr_err("%s Invalid instance, returning\n", __func__);
+			kfree(zoom);
+			return -EINVAL;
+		}
 		zoom->user_cmd = pp_cmd->id;
-		rc = msm_mctl_pp_get_phy_addr(
+		rc = msm_mctl_pp_get_phy_addr(pcam_inst,
 			zoom->pp_frame_cmd.src_buf_handle, &zoom->src_frame);
 		if (rc) {
 			kfree(zoom);
 			break;
 		}
-		rc = msm_mctl_pp_get_phy_addr(
+		rc = msm_mctl_pp_get_phy_addr(pcam_inst,
 			zoom->pp_frame_cmd.dest_buf_handle, &zoom->dest_frame);
 		if (rc) {
 			kfree(zoom);
diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c
index c1df55e..ab252c9 100644
--- a/drivers/mmc/host/msm_sdcc.c
+++ b/drivers/mmc/host/msm_sdcc.c
@@ -132,6 +132,8 @@
 static void
 msmsdcc_start_command(struct msmsdcc_host *host, struct mmc_command *cmd,
 		      u32 c);
+static inline void msmsdcc_delay(struct msmsdcc_host *host);
+
 
 #ifdef CONFIG_MMC_MSM_SPS_SUPPORT
 static int msmsdcc_sps_reset_ep(struct msmsdcc_host *host,
@@ -207,77 +209,83 @@
  * @host - Pointer to driver's host structure
  *
  */
-static void msmsdcc_soft_reset_and_restore(struct msmsdcc_host *host)
+static void msmsdcc_soft_reset(struct msmsdcc_host *host)
 {
-	if (host->is_sps_mode) {
-		/* Reset DML first */
-		msmsdcc_dml_reset(host);
-		/*
-		 * delay the SPS pipe reset in thread context as
-		 * sps_connect/sps_disconnect APIs can be called
-		 * only from non-atomic context.
-		 */
-		host->sps.pipe_reset_pending = true;
-	}
 	/*
 	 * Reset SDCC controller's DPSM (data path state machine
 	 * and CPSM (command path state machine).
 	 */
-	mb();
 	writel_relaxed(0, host->base + MMCICOMMAND);
+	msmsdcc_delay(host);
 	writel_relaxed(0, host->base + MMCIDATACTRL);
-	mb();
+	msmsdcc_delay(host);
+}
 
-	pr_debug("%s: Applied soft reset to Controller\n",
-			mmc_hostname(host->mmc));
+static void msmsdcc_hard_reset(struct msmsdcc_host *host)
+{
+	int ret;
 
-	if (host->is_sps_mode)
-		msmsdcc_dml_init(host);
+	/* Reset the controller */
+	ret = clk_reset(host->clk, CLK_RESET_ASSERT);
+	if (ret)
+		pr_err("%s: Clock assert failed at %u Hz"
+			" with err %d\n", mmc_hostname(host->mmc),
+				host->clk_rate, ret);
+
+	ret = clk_reset(host->clk, CLK_RESET_DEASSERT);
+	if (ret)
+		pr_err("%s: Clock deassert failed at %u Hz"
+			" with err %d\n", mmc_hostname(host->mmc),
+			host->clk_rate, ret);
+
+	/* Give some delay for clock reset to propogate to controller */
+	msmsdcc_delay(host);
 }
 
 static void msmsdcc_reset_and_restore(struct msmsdcc_host *host)
 {
 	if (host->plat->sdcc_v4_sup) {
-		msmsdcc_soft_reset_and_restore(host);
+		if (host->is_sps_mode) {
+			/* Reset DML first */
+			msmsdcc_dml_reset(host);
+			/*
+			 * delay the SPS pipe reset in thread context as
+			 * sps_connect/sps_disconnect APIs can be called
+			 * only from non-atomic context.
+			 */
+			host->sps.pipe_reset_pending = true;
+		}
+		mb();
+		msmsdcc_soft_reset(host);
+
+		pr_debug("%s: Applied soft reset to Controller\n",
+				mmc_hostname(host->mmc));
+
+		if (host->is_sps_mode)
+			msmsdcc_dml_init(host);
 	} else {
 		/* Give Clock reset (hard reset) to controller */
 		u32	mci_clk = 0;
 		u32	mci_mask0 = 0;
-		int ret;
 
 		/* Save the controller state */
 		mci_clk = readl_relaxed(host->base + MMCICLOCK);
 		mci_mask0 = readl_relaxed(host->base + MMCIMASK0);
-
 		mb();
-		/* Reset the controller */
-		ret = clk_reset(host->clk, CLK_RESET_ASSERT);
-		if (ret)
-			pr_err("%s: Clock assert failed at %u Hz"
-				" with err %d\n", mmc_hostname(host->mmc),
-					host->clk_rate, ret);
 
-		ret = clk_reset(host->clk, CLK_RESET_DEASSERT);
-		if (ret)
-			pr_err("%s: Clock deassert failed at %u Hz"
-				" with err %d\n", mmc_hostname(host->mmc),
-					host->clk_rate, ret);
-
+		msmsdcc_hard_reset(host);
 		pr_debug("%s: Controller has been reinitialized\n",
 				mmc_hostname(host->mmc));
 
-		mb();
 		/* Restore the contoller state */
 		writel_relaxed(host->pwr, host->base + MMCIPOWER);
+		msmsdcc_delay(host);
 		writel_relaxed(mci_clk, host->base + MMCICLOCK);
+		msmsdcc_delay(host);
 		writel_relaxed(mci_mask0, host->base + MMCIMASK0);
-		ret = clk_set_rate(host->clk, host->clk_rate);
-		if (ret)
-			pr_err("%s: Failed to set clk rate %u Hz. err %d\n",
-					mmc_hostname(host->mmc),
-					host->clk_rate, ret);
-		mb();
+		mb(); /* no delay required after writing to MASK0 register */
 	}
+
 	if (host->dummy_52_needed)
 		host->dummy_52_needed = 0;
 }
@@ -310,8 +318,6 @@
 	return retval;
 }
 
-static inline void msmsdcc_delay(struct msmsdcc_host *host);
-
 static void
 msmsdcc_stop_data(struct msmsdcc_host *host)
 {
@@ -361,8 +367,13 @@
 msmsdcc_start_command_exec(struct msmsdcc_host *host, u32 arg, u32 c)
 {
 	writel_relaxed(arg, host->base + MMCIARGUMENT);
-	msmsdcc_delay(host);
 	writel_relaxed(c, host->base + MMCICOMMAND);
+	/*
+	 * As after sending the command, we don't write any of the
+	 * controller registers and just wait for the
+	 * CMD_RESPOND_END/CMD_SENT/Command failure notication
+	 * from Controller.
+	 */
 	mb();
 }
 
@@ -374,7 +385,6 @@
 	writel_relaxed(host->cmd_timeout, host->base + MMCIDATATIMER);
 	writel_relaxed((unsigned int)host->curr.xfer_size,
 			host->base + MMCIDATALENGTH);
-	msmsdcc_delay(host);	/* Allow data parms to be applied */
 	writel_relaxed(host->cmd_datactrl, host->base + MMCIDATACTRL);
 	msmsdcc_delay(host);	/* Force delay prior to ADM or command */
 
@@ -931,7 +941,7 @@
 		}
 	}
 
-	if (host->prog_scan && (cmd->opcode == 12)) {
+	if ((cmd->flags & MMC_RSP_R1B) == MMC_RSP_R1B) {
 		*c |= MCI_CPSM_PROGENA;
 		host->prog_enable = 1;
 	}
@@ -1013,6 +1023,8 @@
 
 	if (data->flags & MMC_DATA_READ)
 		datactrl |= (MCI_DPSM_DIRECTION | MCI_RX_DATA_PEND);
+	else if (data->flags & MMC_DATA_WRITE)
+		datactrl |= MCI_DATA_PEND;
 
 	clks = (unsigned long long)data->timeout_ns * host->clk_rate;
 	do_div(clks, 1000000000UL);
@@ -1029,8 +1041,6 @@
 		host->dma.hdr.exec_func = msmsdcc_dma_exec_func;
 		host->dma.hdr.user = (void *)host;
 		host->dma.busy = 1;
-		if ((data->flags & MMC_DATA_WRITE) && !host->curr.mrq->sbc)
-			host->prog_scan = 1;
 
 		if (cmd) {
 			msmsdcc_start_command_deferred(host, cmd, &c);
@@ -1043,8 +1053,6 @@
 		msm_dmov_enqueue_cmd_ext(host->dma.channel, &host->dma.hdr);
 	} else {
 		/* SPS-BAM mode or PIO mode */
-		if ((data->flags & MMC_DATA_WRITE) && !host->curr.mrq->sbc)
-			host->prog_scan = 1;
 		writel_relaxed(timeout, base + MMCIDATATIMER);
 
 		writel_relaxed(host->curr.xfer_size, base + MMCIDATALENGTH);
@@ -1052,8 +1060,14 @@
 		writel_relaxed((readl_relaxed(host->base + MMCIMASK0) &
 				(~(MCI_IRQ_PIO))) | pio_irqmask,
 				host->base + MMCIMASK0);
-		msmsdcc_delay(host);	/* Allow parms to be applied */
 		writel_relaxed(datactrl, base + MMCIDATACTRL);
+		/*
+		 * We don't need delay after writing to DATA_CTRL register
+		 * if we are not writing to CMD register immediately after
+		 * this. As we already have delay before sending the
+		 * command, we just need mb() here.
+		 */
+		mb();
 
 		if (cmd) {
 			msmsdcc_delay(host); /* Delay between data/command */
@@ -1239,8 +1253,10 @@
 				(~(MCI_IRQ_PIO))) | MCI_RXDATAAVLBLMASK,
 				host->base + MMCIMASK0);
 		if (!host->curr.xfer_remain) {
-			/* Delay needed (same port was just written) */
-			msmsdcc_delay(host);
+			/*
+			 * back to back write to MASK0 register don't need
+			 * synchronization delay.
+			 */
 			writel_relaxed((readl_relaxed(host->base + MMCIMASK0) &
 				(~(MCI_IRQ_PIO))) | 0, host->base + MMCIMASK0);
 		}
@@ -1329,16 +1345,12 @@
 		} else { /* host->data == NULL */
 			if (!cmd->error && host->prog_enable) {
 				if (status & MCI_PROGDONE) {
-					host->prog_scan = 0;
 					host->prog_enable = 0;
-					 msmsdcc_request_end(host, cmd->mrq);
+					msmsdcc_request_end(host, cmd->mrq);
 				} else
 					host->curr.cmd = cmd;
 			} else {
-				if (host->prog_enable) {
-					host->prog_scan = 0;
-					host->prog_enable = 0;
-				}
+				host->prog_enable = 0;
 				if (host->dummy_52_needed)
 					host->dummy_52_needed = 0;
 				if (cmd->data && cmd->error)
@@ -1351,9 +1363,6 @@
 			msmsdcc_start_command(host, host->curr.mrq->cmd, 0);
 		else
 			msmsdcc_request_start(host, host->curr.mrq);
-	} else if (cmd->data) {
-		if (!(cmd->data->flags & MMC_DATA_READ))
-			msmsdcc_start_data(host, cmd->data, NULL, 0);
 	}
 }
 
@@ -1560,9 +1569,9 @@
 static void
 msmsdcc_request_start(struct msmsdcc_host *host, struct mmc_request *mrq)
 {
-	if (mrq->data && mrq->data->flags & MMC_DATA_READ) {
+	if (mrq->data) {
 		/* Queue/read data, daisy-chain command when data starts */
-		if (mrq->sbc)
+		if (mrq->sbc && (mrq->data->flags & MMC_DATA_READ))
 			msmsdcc_start_data(host, mrq->data, mrq->sbc, 0);
 		else
 			msmsdcc_start_data(host, mrq->data, mrq->cmd, 0);
@@ -1922,7 +1931,9 @@
 		if (!IS_ERR(host->pclk))
 			clk_enable(host->pclk);
 		clk_enable(host->clk);
+		msmsdcc_delay(host);
 	} else {
+		msmsdcc_delay(host);
 		clk_disable(host->clk);
 		if (!IS_ERR(host->pclk))
 			clk_disable(host->pclk);
@@ -2120,6 +2131,10 @@
 					writel_relaxed(host->mci_irqenable,
 							host->base + MMCIMASK0);
 				}
+			} else {
+				writel_relaxed(host->mci_irqenable,
+						host->base + MMCIMASK0);
+				mb();
 			}
 		}
 		spin_unlock_irqrestore(&host->lock, flags);
@@ -2222,6 +2237,7 @@
 		msmsdcc_setup_pins(host, false);
 		break;
 	case MMC_POWER_UP:
+		/* writing PWR_UP bit is redundant */
 		pwr |= MCI_PWR_UP;
 		if (host->sdcc_irq_disabled) {
 			if (host->plat->cfg_mpm_sdiowakeup)
@@ -2255,7 +2271,7 @@
 	if (host->pwr != pwr) {
 		host->pwr = pwr;
 		writel_relaxed(pwr, host->base + MMCIPOWER);
-		mb();
+		msmsdcc_delay(host);
 	}
 	if (!host->clks_on) {
 		/* force the clocks to be off */
@@ -2279,7 +2295,7 @@
 				writel_relaxed(MCI_SDIOINTMASK,
 						host->base + MMCIMASK0);
 			}
-			msmsdcc_delay(host);
+			mb();
 		}
 		msmsdcc_setup_clocks(host, false);
 		host->clks_on = 0;
@@ -2299,7 +2315,7 @@
 	else
 		clk &= ~MCI_CLK_PWRSAVE;
 	writel_relaxed(clk, host->base + MMCICLOCK);
-	mb();
+	msmsdcc_delay(host);
 
 	return 0;
 }
@@ -2446,7 +2462,7 @@
 	/* Stop SD CLK output. */
 	writel_relaxed((readl_relaxed(host->base + MMCICLOCK) |
 			MCI_CLK_PWRSAVE), host->base + MMCICLOCK);
-
+	msmsdcc_delay(host);
 	spin_unlock_irqrestore(&host->lock, flags);
 
 	/*
@@ -2460,6 +2476,7 @@
 	spin_lock_irqsave(&host->lock, flags);
 	writel_relaxed((readl_relaxed(host->base + MMCICLOCK) |
 			IO_PAD_PWR_SWITCH), host->base + MMCICLOCK);
+	msmsdcc_delay(host);
 	host->io_pad_pwr_switch = 1;
 	spin_unlock_irqrestore(&host->lock, flags);
 
@@ -2470,6 +2487,7 @@
 	/* Start SD CLK output. */
 	writel_relaxed((readl_relaxed(host->base + MMCICLOCK)
 			& ~MCI_CLK_PWRSAVE), host->base + MMCICLOCK);
+	msmsdcc_delay(host);
 	spin_unlock_irqrestore(&host->lock, flags);
 
 	/*
@@ -2683,6 +2701,7 @@
 	 */
 	writel_relaxed((readl_relaxed(host->base + MMCICLOCK)
 			& ~MCI_CLK_PWRSAVE), host->base + MMCICLOCK);
+	msmsdcc_delay(host);
 	/* first of all reset the tuning block */
 	rc = msmsdcc_init_cm_sdc4_dll(host);
 	if (rc)
@@ -2757,6 +2776,7 @@
 	/* re-enable PWESAVE */
 	writel_relaxed((readl_relaxed(host->base + MMCICLOCK) |
 			MCI_CLK_PWRSAVE), host->base + MMCICLOCK);
+	msmsdcc_delay(host);
 	host->cmd19_tuning_in_progress = 0;
 	return rc;
 }
@@ -3463,10 +3483,7 @@
 					msmsdcc_request_end(host, mrq);
 			}
 		} else {
-			if (host->prog_enable) {
-				host->prog_scan = 0;
-				host->prog_enable = 0;
-			}
+			host->prog_enable = 0;
 			msmsdcc_reset_and_restore(host);
 			msmsdcc_request_end(host, mrq);
 		}
@@ -3678,6 +3695,8 @@
 		      msmsdcc_get_min_sup_clk_rate(host)));
 
 	host->clks_on = 1;
+	/* Apply Hard reset to SDCC to put it in power on default state */
+	msmsdcc_hard_reset(host);
 
 	ret = msmsdcc_vreg_init(host, true);
 	if (ret) {
@@ -3709,6 +3728,7 @@
 	mmc->caps |= plat->mmc_bus_width;
 
 	mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED;
+	mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY;
 
 	/*
 	 * If we send the CMD23 before multi block write/read command
@@ -3749,8 +3769,6 @@
 	writel_relaxed(0, host->base + MMCIMASK0);
 	writel_relaxed(MCI_CLEAR_STATIC_MASK, host->base + MMCICLEAR);
 
-	/* Delay needed (MMCIMASK0 was just written above) */
-	msmsdcc_delay(host);
 	writel_relaxed(MCI_IRQENABLE, host->base + MMCIMASK0);
 	mb();
 	host->mci_irqenable = MCI_IRQENABLE;
@@ -4143,12 +4161,14 @@
 		 * part of LPM), then clocks should be turned on before
 		 * calling mmc_suspend_host() because mmc_suspend_host might
 		 * send some commands to the card. The clocks will be turned
-		 * off again after mmc_suspend_host. Thus for SD/MMC/SDIO
+		 * off again after mmc_suspend_host. Thus for SDIO
 		 * cards, clocks will be turned on before mmc_suspend_host
 		 * and turned off after mmc_suspend_host.
 		 */
-		mmc->ios.clock = host->clk_rate;
-		mmc->ops->set_ios(host->mmc, &host->mmc->ios);
+		if (mmc->card && mmc_card_sdio(mmc->card)) {
+			mmc->ios.clock = host->clk_rate;
+			mmc->ops->set_ios(host->mmc, &host->mmc->ios);
+		}
 
 		/*
 		 * MMC core thinks that host is disabled by now since
@@ -4217,26 +4237,26 @@
 				enable_irq(host->core_irqres->start);
 				host->sdcc_irq_disabled = 0;
 			}
-		}
-		mmc->ios.clock = host->clk_rate;
-		mmc->ops->set_ios(host->mmc, &host->mmc->ios);
+			mmc->ios.clock = host->clk_rate;
+			mmc->ops->set_ios(host->mmc, &host->mmc->ios);
 
-		spin_lock_irqsave(&host->lock, flags);
-		writel_relaxed(host->mci_irqenable, host->base + MMCIMASK0);
-		mb();
+			spin_lock_irqsave(&host->lock, flags);
+			writel_relaxed(host->mci_irqenable,
+					host->base + MMCIMASK0);
+			mb();
 
-		if (mmc->card && (mmc->card->type == MMC_TYPE_SDIO) &&
-				(mmc->pm_flags & MMC_PM_WAKE_SDIO_IRQ) &&
-				!host->sdio_irq_disabled) {
+			if ((mmc->pm_flags & MMC_PM_WAKE_SDIO_IRQ) &&
+					!host->sdio_irq_disabled) {
 				if (host->plat->sdiowakeup_irq) {
 					disable_irq_nosync(
 						host->plat->sdiowakeup_irq);
 					msmsdcc_disable_irq_wake(host);
 					host->sdio_irq_disabled = 1;
 				}
-		}
+			}
 
-		spin_unlock_irqrestore(&host->lock, flags);
+			spin_unlock_irqrestore(&host->lock, flags);
+		}
 
 		mmc_resume_host(mmc);
 
diff --git a/drivers/mmc/host/msm_sdcc.h b/drivers/mmc/host/msm_sdcc.h
index 31ece6e..590c293 100644
--- a/drivers/mmc/host/msm_sdcc.h
+++ b/drivers/mmc/host/msm_sdcc.h
@@ -77,6 +77,7 @@
 #define MCI_DPSM_DIRECTION	(1 << 1)
 #define MCI_DPSM_MODE		(1 << 2)
 #define MCI_DPSM_DMAENABLE	(1 << 3)
+#define MCI_DATA_PEND		(1 << 17)
 #define MCI_AUTO_PROG_DONE	(1 << 19)
 #define MCI_RX_DATA_PEND	(1 << 20)
 
@@ -208,7 +209,7 @@
 
 #define NR_SG		32
 
-#define MSM_MMC_IDLE_TIMEOUT	10000 /* msecs */
+#define MSM_MMC_IDLE_TIMEOUT	5000 /* msecs */
 
 /*
  * Set the request timeout to 10secs to allow
@@ -332,7 +333,6 @@
 
 	struct tasklet_struct 	dma_tlet;
 
-	unsigned int prog_scan;
 	unsigned int prog_enable;
 
 	/* Command parameters */
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 179a4ac..cca1035 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -261,7 +261,8 @@
 
 config ISL9519_CHARGER
 	tristate "isl9519 charger"
-	depends on BATTERY_MSM8X60
+	depends on (BATTERY_MSM8X60 || PM8921_CHARGER)
+	depends on I2C
 	default n
 	help
 	  The isl9519q charger chip from intersil is connected to an external
diff --git a/drivers/power/isl9519q.c b/drivers/power/isl9519q.c
index a45d286..733de45 100644
--- a/drivers/power/isl9519q.c
+++ b/drivers/power/isl9519q.c
@@ -20,9 +20,11 @@
 #include <linux/workqueue.h>
 #include <linux/interrupt.h>
 #include <linux/msm-charger.h>
+#include <linux/mfd/pm8xxx/pm8921-charger.h>
 #include <linux/slab.h>
 #include <linux/i2c/isl9519.h>
 #include <linux/msm_adc.h>
+#include <linux/spinlock.h>
 
 #define CHG_CURRENT_REG		0x14
 #define MAX_SYS_VOLTAGE_REG	0x15
@@ -34,7 +36,7 @@
 
 #define TRCKL_CHG_STATUS_BIT	0x80
 
-#define ISL9519_CHG_PERIOD	((HZ) * 150)
+#define ISL9519_CHG_PERIOD_SEC	150
 
 struct isl9519q_struct {
 	struct i2c_client		*client;
@@ -52,6 +54,10 @@
 	struct msm_hardware_charger	adapter_hw_chg;
 	int				suspended;
 	int				charge_at_resume;
+	struct ext_chg_pm8921		ext_chg;
+	spinlock_t			lock;
+	bool				notify_by_pmic;
+	bool				trickle;
 };
 
 static int isl9519q_read_reg(struct i2c_client *client, int reg,
@@ -70,6 +76,8 @@
 	} else
 		*val = ret;
 
+	pr_debug("%s.reg=0x%x.val=0x%x.\n", __func__, reg, *val);
+
 	return 0;
 }
 
@@ -79,6 +87,8 @@
 	int ret;
 	struct isl9519q_struct *isl_chg;
 
+	pr_debug("%s.reg=0x%x.val=0x%x.\n", __func__, reg, val);
+
 	isl_chg = i2c_get_clientdata(client);
 	ret = i2c_smbus_write_word_data(isl_chg->client, reg, val);
 
@@ -91,6 +101,14 @@
 	return 0;
 }
 
+/**
+ * Read charge-current via ADC.
+ *
+ * The ISL CCMON (charge-current-monitor) pin is connected to
+ * the PMIC MPP#X pin.
+ * This not required when notify_by_pmic is used where the PMIC
+ * uses BMS to notify the ISL on charging-done / charge-resume.
+ */
 static int isl_read_adc(int channel, int *mv_reading)
 {
 	int ret;
@@ -136,98 +154,129 @@
 out:
 	*mv_reading = 0;
 	pr_debug("%s: done with error for %d\n", __func__, channel);
-	return -EINVAL;
 
+	return -EINVAL;
 }
 
-static void isl9519q_charge(struct work_struct *isl9519_work)
+static bool is_trickle_charging(struct isl9519q_struct *isl_chg)
 {
-	u16 temp;
+	u16 ctrl = 0;
 	int ret;
+
+	ret = isl9519q_read_reg(isl_chg->client, CONTROL_REG, &ctrl);
+
+	if (!ret) {
+		pr_debug("%s.control_reg=0x%x.\n", __func__, ctrl);
+	} else {
+		dev_err(&isl_chg->client->dev,
+			"%s couldnt read cntrl reg\n", __func__);
+	}
+
+	if (ctrl & TRCKL_CHG_STATUS_BIT)
+		return true;
+
+	return false;
+}
+
+static void isl_adapter_check_ichg(struct isl9519q_struct *isl_chg)
+{
+	int ichg; /* isl charger current */
+	int mv_reading = 0;
+
+	ichg = isl_read_adc(CHANNEL_ADC_BATT_AMON, &mv_reading);
+
+	dev_dbg(&isl_chg->client->dev, "%s mv_reading=%d\n",
+		__func__, mv_reading);
+	dev_dbg(&isl_chg->client->dev, "%s isl_charger_current=%d\n",
+		__func__, ichg);
+
+	if (ichg >= 0 && ichg <= isl_chg->term_current)
+		msm_charger_notify_event(&isl_chg->adapter_hw_chg,
+					 CHG_DONE_EVENT);
+
+	isl_chg->trickle = is_trickle_charging(isl_chg);
+	if (isl_chg->trickle)
+		msm_charger_notify_event(&isl_chg->adapter_hw_chg,
+					 CHG_BATT_BEGIN_FAST_CHARGING);
+}
+
+/**
+ * isl9519q_worker
+ *
+ * Periodic task required to kick the ISL HW watchdog to keep
+ * charging.
+ *
+ * @isl9519_work: work context.
+ */
+static void isl9519q_worker(struct work_struct *isl9519_work)
+{
 	struct isl9519q_struct *isl_chg;
-	int isl_charger_current;
-	int mv_reading;
 
 	isl_chg = container_of(isl9519_work, struct isl9519q_struct,
 			charge_work.work);
 
 	dev_dbg(&isl_chg->client->dev, "%s\n", __func__);
 
-	if (isl_chg->charging) {
-		isl_charger_current = isl_read_adc(CHANNEL_ADC_BATT_AMON,
-								&mv_reading);
-		dev_dbg(&isl_chg->client->dev, "%s mv_reading=%d\n",
-				__func__, mv_reading);
-		dev_dbg(&isl_chg->client->dev, "%s isl_charger_current=%d\n",
-				__func__, isl_charger_current);
-		if (isl_charger_current >= 0
-			&& isl_charger_current <= isl_chg->term_current) {
-			msm_charger_notify_event(
-					&isl_chg->adapter_hw_chg,
-					CHG_DONE_EVENT);
-		}
-		isl9519q_write_reg(isl_chg->client, CHG_CURRENT_REG,
-				isl_chg->chgcurrent);
-		ret = isl9519q_read_reg(isl_chg->client, CONTROL_REG, &temp);
-		if (!ret) {
-			if (!(temp & TRCKL_CHG_STATUS_BIT))
-				msm_charger_notify_event(
-						&isl_chg->adapter_hw_chg,
-						CHG_BATT_BEGIN_FAST_CHARGING);
-		} else {
-			dev_err(&isl_chg->client->dev,
-				"%s couldnt read cntrl reg\n", __func__);
-		}
-		schedule_delayed_work(&isl_chg->charge_work,
-						ISL9519_CHG_PERIOD);
+	if (!isl_chg->charging) {
+		pr_info("%s.stop charging.\n", __func__);
+		isl9519q_write_reg(isl_chg->client, CHG_CURRENT_REG, 0);
+		return; /* Stop periodic worker */
 	}
+
+	/* Kick the dog by writting to CHG_CURRENT_REG */
+	isl9519q_write_reg(isl_chg->client, CHG_CURRENT_REG,
+			   isl_chg->chgcurrent);
+
+	if (isl_chg->notify_by_pmic)
+		isl_chg->trickle = is_trickle_charging(isl_chg);
+	else
+		isl_adapter_check_ichg(isl_chg);
+
+	schedule_delayed_work(&isl_chg->charge_work,
+			      (ISL9519_CHG_PERIOD_SEC * HZ));
 }
 
-static int isl9519q_start_charging(struct msm_hardware_charger *hw_chg,
-		int chg_voltage, int chg_current)
+static int isl9519q_start_charging(struct isl9519q_struct *isl_chg,
+				   int chg_voltage, int chg_current)
 {
-	struct isl9519q_struct *isl_chg;
 	int ret = 0;
 
-	isl_chg = container_of(hw_chg, struct isl9519q_struct, adapter_hw_chg);
-	if (isl_chg->charging)
-		/* we are already charging */
+	pr_info("%s.\n", __func__);
+
+	if (isl_chg->charging) {
+		pr_warn("%s.already charging.\n", __func__);
 		return 0;
+	}
 
 	if (isl_chg->suspended) {
+		pr_warn("%s.suspended - can't start charging.\n", __func__);
 		isl_chg->charge_at_resume = 1;
 		return 0;
 	}
 
-	dev_dbg(&isl_chg->client->dev, "%s\n", __func__);
+	dev_dbg(&isl_chg->client->dev,
+		"%s starting timed work.period=%d seconds.\n",
+		__func__, (int) ISL9519_CHG_PERIOD_SEC);
 
-	ret = isl9519q_write_reg(isl_chg->client, CHG_CURRENT_REG,
-						isl_chg->chgcurrent);
-	if (ret) {
-		dev_err(&isl_chg->client->dev,
-			"%s coulnt write to current_reg\n", __func__);
-		goto out;
-	}
+	/*
+	 * The ISL will start charging from the worker context.
+	 * This API might be called from interrupt context.
+	 */
+	schedule_delayed_work(&isl_chg->charge_work, 1);
 
-	dev_dbg(&isl_chg->client->dev, "%s starting timed work\n",
-							__func__);
-	schedule_delayed_work(&isl_chg->charge_work,
-						ISL9519_CHG_PERIOD);
 	isl_chg->charging = true;
 
-out:
 	return ret;
 }
 
-static int isl9519q_stop_charging(struct msm_hardware_charger *hw_chg)
+static int isl9519q_stop_charging(struct isl9519q_struct *isl_chg)
 {
-	struct isl9519q_struct *isl_chg;
-	int ret = 0;
+	pr_info("%s.\n", __func__);
 
-	isl_chg = container_of(hw_chg, struct isl9519q_struct, adapter_hw_chg);
-	if (!(isl_chg->charging))
-		/* we arent charging */
+	if (!(isl_chg->charging)) {
+		pr_warn("%s.already not charging.\n", __func__);
 		return 0;
+	}
 
 	if (isl_chg->suspended) {
 		isl_chg->charge_at_resume = 0;
@@ -236,17 +285,71 @@
 
 	dev_dbg(&isl_chg->client->dev, "%s\n", __func__);
 
-	ret = isl9519q_write_reg(isl_chg->client, CHG_CURRENT_REG, 0);
-	if (ret) {
-		dev_err(&isl_chg->client->dev,
-			"%s coulnt write to current_reg\n", __func__);
-		goto out;
-	}
-
 	isl_chg->charging = false;
-	cancel_delayed_work(&isl_chg->charge_work);
-out:
-	return ret;
+	isl_chg->trickle = false;
+	/*
+	 * The ISL will stop charging from the worker context.
+	 * This API might be called from interrupt context.
+	 */
+	schedule_delayed_work(&isl_chg->charge_work, 1);
+
+	return 0;
+}
+
+static int isl_ext_start_charging(void *ctx)
+{
+	int rc;
+	struct isl9519q_struct *isl_chg = ctx;
+	unsigned long flags;
+
+	spin_lock_irqsave(&isl_chg->lock, flags);
+	rc = isl9519q_start_charging(isl_chg, 0, isl_chg->chgcurrent);
+	spin_unlock_irqrestore(&isl_chg->lock, flags);
+
+	return rc;
+}
+
+static int isl_ext_stop_charging(void *ctx)
+{
+	int rc;
+	struct isl9519q_struct *isl_chg = ctx;
+	unsigned long flags;
+
+	spin_lock_irqsave(&isl_chg->lock, flags);
+	rc = isl9519q_stop_charging(isl_chg);
+	spin_unlock_irqrestore(&isl_chg->lock, flags);
+
+	return rc;
+}
+
+static bool isl_ext_is_trickle(void *ctx)
+{
+	struct isl9519q_struct *isl_chg = ctx;
+
+	return isl_chg->trickle;
+}
+
+static int isl_adapter_start_charging(struct msm_hardware_charger *hw_chg,
+				      int chg_voltage, int chg_current)
+{
+	int rc;
+	struct isl9519q_struct *isl_chg;
+
+	isl_chg = container_of(hw_chg, struct isl9519q_struct, adapter_hw_chg);
+	rc = isl9519q_start_charging(isl_chg, chg_voltage, chg_current);
+
+	return rc;
+}
+
+static int isl_adapter_stop_charging(struct msm_hardware_charger *hw_chg)
+{
+	int rc;
+	struct isl9519q_struct *isl_chg;
+
+	isl_chg = container_of(hw_chg, struct isl9519q_struct, adapter_hw_chg);
+	rc = isl9519q_stop_charging(isl_chg);
+
+	return rc;
 }
 
 static int isl9519q_charging_switched(struct msm_hardware_charger *hw_chg)
@@ -296,6 +399,93 @@
 #define DEFAULT_MAX_VOLTAGE_REG_VALUE	0x1070
 #define DEFAULT_MIN_VOLTAGE_REG_VALUE	0x0D00
 
+static int __devinit isl9519q_init_adapter(struct isl9519q_struct *isl_chg)
+{
+	int ret;
+	struct isl_platform_data *pdata = isl_chg->client->dev.platform_data;
+	struct i2c_client *client = isl_chg->client;
+
+	isl_chg->adapter_hw_chg.type = CHG_TYPE_AC;
+	isl_chg->adapter_hw_chg.rating = 2;
+	isl_chg->adapter_hw_chg.name = "isl-adapter";
+	isl_chg->adapter_hw_chg.start_charging = isl_adapter_start_charging;
+	isl_chg->adapter_hw_chg.stop_charging = isl_adapter_stop_charging;
+	isl_chg->adapter_hw_chg.charging_switched = isl9519q_charging_switched;
+
+	ret = gpio_request(pdata->valid_n_gpio, "isl_charger_valid");
+	if (ret) {
+		dev_err(&client->dev, "%s gpio_request failed "
+				      "for %d ret=%d\n",
+			__func__, pdata->valid_n_gpio, ret);
+		goto out;
+	}
+
+	ret = msm_charger_register(&isl_chg->adapter_hw_chg);
+	if (ret) {
+		dev_err(&client->dev,
+			"%s msm_charger_register failed for ret =%d\n",
+			__func__, ret);
+		goto free_gpio;
+	}
+
+	ret = request_threaded_irq(client->irq, NULL,
+				   isl_valid_handler,
+				   IRQF_TRIGGER_FALLING |
+				   IRQF_TRIGGER_RISING,
+				   "isl_charger_valid", client);
+	if (ret) {
+		dev_err(&client->dev,
+			"%s request_threaded_irq failed "
+			"for %d ret =%d\n",
+			__func__, client->irq, ret);
+		goto unregister;
+	}
+	irq_set_irq_wake(client->irq, 1);
+
+	ret = gpio_get_value_cansleep(isl_chg->valid_n_gpio);
+	if (ret < 0) {
+		dev_err(&client->dev,
+			"%s gpio_get_value failed for %d ret=%d\n",
+			__func__, pdata->valid_n_gpio, ret);
+		/* assume absent */
+		ret = 1;
+	}
+	if (!ret) {
+		msm_charger_notify_event(&isl_chg->adapter_hw_chg,
+				CHG_INSERTED_EVENT);
+		isl_chg->present = 1;
+	}
+
+	return 0;
+
+unregister:
+	msm_charger_unregister(&isl_chg->adapter_hw_chg);
+free_gpio:
+	gpio_free(pdata->valid_n_gpio);
+out:
+	return ret;
+
+}
+
+static int __devinit isl9519q_init_ext_chg(struct isl9519q_struct *isl_chg)
+{
+	int ret;
+
+	isl_chg->ext_chg.name = "isl9519q";
+	isl_chg->ext_chg.ctx = isl_chg;
+	isl_chg->ext_chg.start_charging = isl_ext_start_charging;
+	isl_chg->ext_chg.stop_charging = isl_ext_stop_charging;
+	isl_chg->ext_chg.is_trickle = isl_ext_is_trickle;
+	ret = register_external_dc_charger(&isl_chg->ext_chg);
+	if (ret) {
+		pr_err("%s.failed to register external dc charger.ret=%d.\n",
+		       __func__, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
 static int __devinit isl9519q_probe(struct i2c_client *client,
 				    const struct i2c_device_id *id)
 {
@@ -306,6 +496,8 @@
 	ret = 0;
 	pdata = client->dev.platform_data;
 
+	pr_info("%s.\n", __func__);
+
 	if (pdata == NULL) {
 		dev_err(&client->dev, "%s no platform data\n", __func__);
 		ret = -EINVAL;
@@ -324,7 +516,9 @@
 		goto out;
 	}
 
-	INIT_DELAYED_WORK(&isl_chg->charge_work, isl9519q_charge);
+	spin_lock_init(&isl_chg->lock);
+
+	INIT_DELAYED_WORK(&isl_chg->charge_work, isl9519q_worker);
 	isl_chg->client = client;
 	isl_chg->chgcurrent = pdata->chgcurrent;
 	isl_chg->term_current = pdata->term_current;
@@ -337,12 +531,14 @@
 	isl_chg->chgcurrent &= ~0x7F;
 	isl_chg->input_current &= ~0x7F;
 
-	isl_chg->adapter_hw_chg.type = CHG_TYPE_AC;
-	isl_chg->adapter_hw_chg.rating = 2;
-	isl_chg->adapter_hw_chg.name = "isl-adapter";
-	isl_chg->adapter_hw_chg.start_charging = isl9519q_start_charging;
-	isl_chg->adapter_hw_chg.stop_charging = isl9519q_stop_charging;
-	isl_chg->adapter_hw_chg.charging_switched = isl9519q_charging_switched;
+	/**
+	 * ISL is Notified by PMIC to start/stop charging, rather than
+	 * handling interrupt from ISL for End-Of-Chargring, and
+	 * monitoring the charge-current periodically. The valid_n_gpio
+	 * is also not used, dc-present is detected by PMIC.
+	 */
+	isl_chg->notify_by_pmic = (client->irq == 0);
+	i2c_set_clientdata(client, isl_chg);
 
 	if (pdata->chg_detection_config) {
 		ret = pdata->chg_detection_config();
@@ -353,35 +549,6 @@
 		}
 	}
 
-	ret = gpio_request(pdata->valid_n_gpio, "isl_charger_valid");
-	if (ret) {
-		dev_err(&client->dev, "%s gpio_request failed for %d ret=%d\n",
-			__func__, pdata->valid_n_gpio, ret);
-		goto free_isl_chg;
-	}
-
-	i2c_set_clientdata(client, isl_chg);
-
-	ret = msm_charger_register(&isl_chg->adapter_hw_chg);
-	if (ret) {
-		dev_err(&client->dev,
-			"%s msm_charger_register failed for ret =%d\n",
-			__func__, ret);
-		goto free_gpio;
-	}
-
-	ret = request_threaded_irq(client->irq, NULL,
-				   isl_valid_handler,
-				   IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
-				   "isl_charger_valid", client);
-	if (ret) {
-		dev_err(&client->dev,
-			"%s request_threaded_irq failed for %d ret =%d\n",
-			__func__, client->irq, ret);
-		goto unregister;
-	}
-	irq_set_irq_wake(client->irq, 1);
-
 	isl_chg->max_system_voltage &= MAX_VOLTAGE_REG_MASK;
 	isl_chg->min_system_voltage &= MIN_VOLTAGE_REG_MASK;
 	if (isl_chg->max_system_voltage == 0)
@@ -391,57 +558,34 @@
 
 	ret = isl9519q_write_reg(isl_chg->client, MAX_SYS_VOLTAGE_REG,
 			isl_chg->max_system_voltage);
-	if (ret) {
-		dev_err(&client->dev,
-			"%s couldnt write to MAX_SYS_VOLTAGE_REG ret=%d\n",
-			__func__, ret);
-		goto free_irq;
-	}
+	if (ret)
+		goto free_isl_chg;
 
 	ret = isl9519q_write_reg(isl_chg->client, MIN_SYS_VOLTAGE_REG,
 			isl_chg->min_system_voltage);
-	if (ret) {
-		dev_err(&client->dev,
-			"%s couldnt write to MIN_SYS_VOLTAGE_REG ret=%d\n",
-			__func__, ret);
-		goto free_irq;
-	}
+	if (ret)
+		goto free_isl_chg;
 
 	if (isl_chg->input_current) {
 		ret = isl9519q_write_reg(isl_chg->client,
 				INPUT_CURRENT_REG,
 				isl_chg->input_current);
-		if (ret) {
-			dev_err(&client->dev,
-				"%s couldnt write INPUT_CURRENT_REG ret=%d\n",
-				__func__, ret);
-			goto free_irq;
-		}
+		if (ret)
+			goto free_isl_chg;
 	}
 
-	ret = gpio_get_value_cansleep(isl_chg->valid_n_gpio);
-	if (ret < 0) {
-		dev_err(&client->dev,
-			"%s gpio_get_value failed for %d ret=%d\n", __func__,
-			pdata->valid_n_gpio, ret);
-		/* assume absent */
-		ret = 1;
-	}
-	if (!ret) {
-		msm_charger_notify_event(&isl_chg->adapter_hw_chg,
-				CHG_INSERTED_EVENT);
-		isl_chg->present = 1;
-	}
+	if (isl_chg->notify_by_pmic)
+		ret = isl9519q_init_ext_chg(isl_chg);
+	else
+		ret = isl9519q_init_adapter(isl_chg);
 
-	pr_debug("%s OK chg_present=%d\n", __func__, isl_chg->present);
+	if (ret)
+		goto free_isl_chg;
+
+	pr_info("%s OK.\n", __func__);
+
 	return 0;
 
-free_irq:
-	free_irq(client->irq, NULL);
-unregister:
-	msm_charger_unregister(&isl_chg->adapter_hw_chg);
-free_gpio:
-	gpio_free(pdata->valid_n_gpio);
 free_isl_chg:
 	kfree(isl_chg);
 out:
@@ -493,7 +637,7 @@
 	isl_chg->suspended  = 0;
 	if (isl_chg->charge_at_resume) {
 		isl_chg->charge_at_resume = 0;
-		isl9519q_start_charging(&isl_chg->adapter_hw_chg, 0, 0);
+		isl9519q_start_charging(isl_chg, 0, 0);
 	}
 	return 0;
 }
@@ -519,10 +663,12 @@
 
 static int __init isl9519q_init(void)
 {
+	pr_info("%s. isl9519q SW rev 1.01\n", __func__);
+
 	return i2c_add_driver(&isl9519q_driver);
 }
 
-module_init(isl9519q_init);
+late_initcall_sync(isl9519q_init);
 
 static void __exit isl9519q_exit(void)
 {
diff --git a/drivers/power/pm8921-bms.c b/drivers/power/pm8921-bms.c
index fdbc819..751c6fc 100644
--- a/drivers/power/pm8921-bms.c
+++ b/drivers/power/pm8921-bms.c
@@ -23,6 +23,7 @@
 #include <linux/bitops.h>
 #include <linux/debugfs.h>
 #include <linux/slab.h>
+#include <linux/delay.h>
 
 #define BMS_CONTROL		0x224
 #define BMS_OUTPUT0		0x230
@@ -35,14 +36,19 @@
 #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
 #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
+
 enum pmic_bms_interrupts {
 	PM8921_BMS_SBI_WRITE_OK,
 	PM8921_BMS_CC_THR,
@@ -54,16 +60,6 @@
 	PM_BMS_MAX_INTS,
 };
 
-/**
- * struct pm8921_bms_chip -device information
- * @dev:	device pointer to access the parent
- * @dent:	debugfs directory
- * @r_sense:	batt sense resistance value
- * @i_test:	peak current
- * @v_failure:	battery dead voltage
- * @fcc:	battery capacity
- *
- */
 struct pm8921_bms_chip {
 	struct device		*dev;
 	struct dentry		*dent;
@@ -76,7 +72,9 @@
 	struct pc_temp_ocv_lut	*pc_temp_ocv_lut;
 	struct pc_sf_lut	*pc_sf_lut;
 	struct work_struct	calib_hkadc_work;
+	struct delayed_work	calib_ccadc_work;
 	unsigned int		calib_delay_ms;
+	int			ccadc_gain_uv;
 	unsigned int		revision;
 	unsigned int		xoadc_v0625;
 	unsigned int		xoadc_v125;
@@ -389,11 +387,61 @@
 	 * resolution (the value of a single bit) was changed after revision 2.0
 	 * for more accurate readings
 	 */
-	return (chip->revision < PM8XXX_REVISION_8901_2p0) ?
+	return (chip->revision < PM8XXX_REVISION_8921_2p0) ?
 				cc_to_microvolt_v1((s64)cc) :
 				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
@@ -403,6 +451,19 @@
 			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)
 {
@@ -435,7 +496,9 @@
 		return rc;
 	}
 	*result = xoadc_reading_to_microvolt(reading);
-	pr_debug("raw = %04x ocv_microV = %u\n", reading, *result);
+	pr_debug("raw = %04x ocv_uV = %u\n", reading, *result);
+	*result = adjust_xo_vbatt_reading(chip, *result);
+	pr_debug("after adj ocv_uV = %u\n", *result);
 	if (*result != 0)
 		last_ocv_uv = *result;
 	return 0;
@@ -468,8 +531,10 @@
 		pr_err("fail to read VSENSE_FOR_RBATT rc = %d\n", rc);
 		return rc;
 	}
-	*result = cc_to_microvolt(chip, reading);
-	pr_debug("raw = %04x vsense_for_r_microV = %u\n", reading, *result);
+	*result = ccadc_reading_to_microvolt(chip, reading);
+	pr_debug("raw = %04x vsense_for_r_uV = %u\n", reading, *result);
+	*result = cc_adjust_for_gain(chip, *result);
+	pr_debug("after adj vsense_for_r_uV = %u\n", *result);
 	return 0;
 }
 
@@ -500,8 +565,10 @@
 		pr_err("fail to read VSENSE_AVG rc = %d\n", rc);
 		return rc;
 	}
-	*result = cc_to_microvolt(chip, reading);
-	pr_debug("read = %04x vsense = %d\n", reading, *result);
+	*result = ccadc_reading_to_microvolt(chip, reading);
+	pr_debug("raw = %04x vsense = %d\n", reading, *result);
+	*result = cc_adjust_for_gain(the_chip, (s64)*result);
+	pr_debug("after adj vsense = %d\n", *result);
 	return 0;
 }
 
@@ -866,6 +933,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);
 	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);
@@ -1098,6 +1166,385 @@
 	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	10
+#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 rc, i;
+	u8 cntrl, sbi_config;
+	bool in_progress;
+
+	calib_ccadc_enable_trim_access(chip, &sbi_config);
+
+	for (i = 0; i < CCADC_PROGRAM_TRIM_COUNT; 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_err("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_err("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);
+	}
+
+	data_msb = result_offset >> 8;
+	data_lsb = result_offset;
+
+	rc = calib_ccadc_program_trim(chip, CCADC_OFFSET_TRIM1,
+						data_msb, data_lsb);
+	if (rc) {
+		pr_err("error = %d programming offset trim\n", rc);
+		goto bail;
+	}
+
+	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);
+	if (rc)
+		pr_err("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);
+	schedule_delayed_work(&chip->calib_ccadc_work,
+			round_jiffies_relative(msecs_to_jiffies
+			(chip->calib_delay_ms)));
+}
+
 int pm8921_bms_get_vsense_avg(int *result)
 {
 	int rc = -EINVAL;
@@ -1119,6 +1566,7 @@
 int pm8921_bms_get_battery_current(int *result)
 {
 	unsigned long flags;
+	int vsense;
 
 	if (!the_chip) {
 		pr_err("called before initialization\n");
@@ -1131,12 +1579,13 @@
 
 	spin_lock_irqsave(&the_chip->bms_output_lock, flags);
 	pm_bms_lock_output_data(the_chip);
-	read_vsense_avg(the_chip, result);
+	read_vsense_avg(the_chip, &vsense);
 	pm_bms_unlock_output_data(the_chip);
 	spin_unlock_irqrestore(&the_chip->bms_output_lock, flags);
-	pr_debug("vsense=%d\n", *result);
+	pr_debug("vsense=%d\n", vsense);
 	/* cast for signed division */
-	*result = *result / (int)the_chip->r_sense;
+	*result = vsense / (int)the_chip->r_sense;
+
 	return 0;
 }
 EXPORT_SYMBOL(pm8921_bms_get_battery_current);
@@ -1451,6 +1900,7 @@
 	CALC_PC,
 	CALC_SOC,
 	CALIB_HKADC,
+	CALIB_CCADC,
 };
 
 static int test_batt_temp = 5;
@@ -1526,6 +1976,11 @@
 		*val = 0;
 		calib_hkadc(the_chip);
 		break;
+	case CALIB_CCADC:
+		/* reading this will trigger calibration */
+		*val = 0;
+		calib_ccadc(the_chip);
+		break;
 	default:
 		ret = -EINVAL;
 	}
@@ -1677,6 +2132,8 @@
 				(void *)CALC_SOC, &calc_fops);
 	debugfs_create_file("calib_hkadc", 0644, chip->dent,
 				(void *)CALIB_HKADC, &calc_fops);
+	debugfs_create_file("calib_ccadc", 0644, chip->dent,
+				(void *)CALIB_CCADC, &calc_fops);
 
 	for (i = 0; i < ARRAY_SIZE(bms_irq_data); i++) {
 		if (chip->pmic_bms_irq[bms_irq_data[i].irq_id])
@@ -1725,23 +2182,29 @@
 	chip->revision = pm8xxx_get_revision(chip->dev->parent);
 	INIT_WORK(&chip->calib_hkadc_work, calibrate_hkadc_work);
 
-	rc = pm8921_bms_hw_init(chip);
-	if (rc) {
-		pr_err("couldn't init hardware rc = %d\n", rc);
-		goto free_chip;
-	}
-
 	rc = request_irqs(chip, pdev);
 	if (rc) {
 		pr_err("couldn't register interrupts rc = %d\n", rc);
 		goto free_chip;
 	}
 
+	rc = pm8921_bms_hw_init(chip);
+	if (rc) {
+		pr_err("couldn't init hardware rc = %d\n", rc);
+		goto free_irqs;
+	}
+
 	platform_set_drvdata(pdev, chip);
 	the_chip = chip;
 	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 */
+	if (chip->revision >= PM8XXX_REVISION_8921_2p0)
+		calibrate_ccadc_work(&(chip->calib_ccadc_work.work));
 
 	/* initial hkadc calibration */
 	schedule_work(&chip->calib_hkadc_work);
@@ -1755,6 +2218,8 @@
 				vbatt, last_ocv_uv);
 	return 0;
 
+free_irqs:
+	free_irqs(chip);
 free_chip:
 	kfree(chip);
 	return rc;
diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c
index 06d614b..bc1a25b 100644
--- a/drivers/tty/serial/msm_serial_hs.c
+++ b/drivers/tty/serial/msm_serial_hs.c
@@ -262,7 +262,10 @@
 		gsbi_resource = platform_get_resource_byname(pdev,
 							     IORESOURCE_MEM,
 							     "gsbi_resource");
-		size = gsbi_resource->end - gsbi_resource->start + 1;
+		if (unlikely(!gsbi_resource))
+			return;
+
+		size = resource_size(gsbi_resource);
 		release_mem_region(gsbi_resource->start, size);
 		iounmap(msm_uport->mapped_gsbi);
 		msm_uport->mapped_gsbi = NULL;
@@ -280,7 +283,7 @@
 						     IORESOURCE_MEM,
 						     "gsbi_resource");
 	if (gsbi_resource) {
-		size = gsbi_resource->end - gsbi_resource->start + 1;
+		size = resource_size(gsbi_resource);
 		if (unlikely(!request_mem_region(gsbi_resource->start, size,
 						 "msm_serial_hs")))
 			return -EBUSY;
diff --git a/drivers/tty/serial/msm_serial_hs_lite.c b/drivers/tty/serial/msm_serial_hs_lite.c
index ad085f9..a7b53e4 100644
--- a/drivers/tty/serial/msm_serial_hs_lite.c
+++ b/drivers/tty/serial/msm_serial_hs_lite.c
@@ -799,7 +799,6 @@
 	struct msm_hsl_port *msm_hsl_port = UART_TO_MSM(port);
 	struct platform_device *pdev = to_platform_device(port->dev);
 	struct resource *uart_resource;
-	struct resource *gsbi_resource;
 	resource_size_t size;
 
 	uart_resource = platform_get_resource_byname(pdev, IORESOURCE_MEM,
@@ -815,11 +814,6 @@
 	if (msm_serial_hsl_has_gsbi(port)) {
 		iowrite32(GSBI_PROTOCOL_IDLE, msm_hsl_port->mapped_gsbi +
 			  GSBI_CONTROL_ADDR);
-		gsbi_resource = platform_get_resource_byname(pdev,
-							     IORESOURCE_MEM,
-							     "gsbi_resource");
-
-		size = gsbi_resource->end - gsbi_resource->start + 1;
 		iounmap(msm_hsl_port->mapped_gsbi);
 		msm_hsl_port->mapped_gsbi = NULL;
 	}
diff --git a/drivers/usb/gadget/f_rmnet_sdio.c b/drivers/usb/gadget/f_rmnet_sdio.c
index b15c221..f63d939 100644
--- a/drivers/usb/gadget/f_rmnet_sdio.c
+++ b/drivers/usb/gadget/f_rmnet_sdio.c
@@ -1358,17 +1358,20 @@
 	struct rmnet_sdio_dev *dev = container_of(f, struct rmnet_sdio_dev,
 								function);
 
+	cancel_delayed_work_sync(&dev->sdio_open_work);
 	destroy_workqueue(dev->wq);
 
-	rmnet_sdio_free_buf(dev);
 	dev->epout = dev->epin = dev->epnotify = NULL; /* release endpoints */
 
-	msm_sdio_dmux_close(rmnet_sdio_data_ch);
-	sdio_cmux_close(rmnet_sdio_ctl_ch);
+	if (test_bit(RMNET_SDIO_CH_OPEN, &dev->data_ch_status)) {
+		msm_sdio_dmux_close(rmnet_sdio_data_ch);
+		clear_bit(RMNET_SDIO_CH_OPEN, &dev->data_ch_status);
+	}
 
-
-	clear_bit(RMNET_SDIO_CH_OPEN, &dev->data_ch_status);
-	clear_bit(RMNET_SDIO_CH_OPEN, &dev->ctrl_ch_status);
+	if (test_bit(RMNET_SDIO_CH_OPEN, &dev->ctrl_ch_status)) {
+		sdio_cmux_close(rmnet_sdio_ctl_ch);
+		clear_bit(RMNET_SDIO_CH_OPEN, &dev->ctrl_ch_status);
+	}
 
 	debugfs_remove_recursive(dev->dent);
 
@@ -1454,7 +1457,7 @@
 
 static void rmnet_sdio_debugfs_init(struct rmnet_sdio_dev *dev)
 {
-	dev->dent = debugfs_create_dir("usb_rmnet", 0);
+	dev->dent = debugfs_create_dir("usb_rmnet_sdio", 0);
 	if (IS_ERR(dev->dent))
 		return;
 
diff --git a/drivers/usb/gadget/f_rmnet_smd.c b/drivers/usb/gadget/f_rmnet_smd.c
index b8dd3a5..08f461f 100644
--- a/drivers/usb/gadget/f_rmnet_smd.c
+++ b/drivers/usb/gadget/f_rmnet_smd.c
@@ -1218,7 +1218,7 @@
 static void rmnet_smd_debugfs_init(struct rmnet_smd_dev *dev)
 {
 
-	dent_smd = debugfs_create_dir("usb_rmnet", 0);
+	dent_smd = debugfs_create_dir("usb_rmnet_smd", 0);
 	if (IS_ERR(dent_smd))
 		return;
 
diff --git a/drivers/video/msm/mdp4_overlay_dsi_video.c b/drivers/video/msm/mdp4_overlay_dsi_video.c
index 93933f3..5cf79c1 100644
--- a/drivers/video/msm/mdp4_overlay_dsi_video.c
+++ b/drivers/video/msm/mdp4_overlay_dsi_video.c
@@ -555,6 +555,10 @@
 void mdp4_overlay0_done_dsi_video(struct mdp_dma_data *dma)
 {
 	spin_lock(&mdp_spin_lock);
+	if (dsi_pipe->blt_addr == 0) {
+		spin_unlock(&mdp_spin_lock);
+		return;
+	}
 	dma->busy = FALSE;
 	mdp4_dsi_video_blt_dmap_update(dsi_pipe);
 	dsi_pipe->dmap_cnt++;
diff --git a/drivers/video/msm/mdp4_overlay_lcdc.c b/drivers/video/msm/mdp4_overlay_lcdc.c
index e840230..0eda69a 100644
--- a/drivers/video/msm/mdp4_overlay_lcdc.c
+++ b/drivers/video/msm/mdp4_overlay_lcdc.c
@@ -451,6 +451,10 @@
 void mdp4_overlay0_done_lcdc(struct mdp_dma_data *dma)
 {
 	spin_lock(&mdp_spin_lock);
+	if (lcdc_pipe->blt_addr == 0) {
+		spin_unlock(&mdp_spin_lock);
+		return;
+	}
 	dma->busy = FALSE;
 	mdp4_lcdc_blt_dmap_update(lcdc_pipe);
 	lcdc_pipe->dmap_cnt++;
diff --git a/drivers/video/msm/mdp4_util.c b/drivers/video/msm/mdp4_util.c
index 99607e7..3d78a13 100644
--- a/drivers/video/msm/mdp4_util.c
+++ b/drivers/video/msm/mdp4_util.c
@@ -419,6 +419,55 @@
 		spin_unlock(&mdp_spin_lock);
 	}
 #endif
+
+#ifdef CONFIG_FB_MSM_OVERLAY
+	if (isr & INTR_OVERLAY0_DONE) {
+		mdp4_stat.intr_overlay0++;
+		dma = &dma2_data;
+		if (panel & (MDP4_PANEL_LCDC | MDP4_PANEL_DSI_VIDEO)) {
+			/* disable LCDC interrupt */
+			spin_lock(&mdp_spin_lock);
+			mdp_intr_mask &= ~INTR_OVERLAY0_DONE;
+			outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+			dma->waiting = FALSE;
+			spin_unlock(&mdp_spin_lock);
+			if (panel & MDP4_PANEL_LCDC)
+				mdp4_overlay0_done_lcdc(dma);
+#ifdef CONFIG_FB_MSM_MIPI_DSI
+			else if (panel & MDP4_PANEL_DSI_VIDEO)
+				mdp4_overlay0_done_dsi_video(dma);
+#endif
+		} else {        /* MDDI, DSI_CMD  */
+#ifdef CONFIG_FB_MSM_MIPI_DSI
+			if (panel & MDP4_PANEL_DSI_CMD)
+				mdp4_overlay0_done_dsi_cmd(dma);
+#else
+			if (panel & MDP4_PANEL_MDDI)
+				mdp4_overlay0_done_mddi(dma);
+#endif
+		}
+		mdp_hw_cursor_done();
+	}
+	if (isr & INTR_OVERLAY1_DONE) {
+		mdp4_stat.intr_overlay1++;
+		/* disable DTV interrupt */
+		dma = &dma_e_data;
+		spin_lock(&mdp_spin_lock);
+		mdp_intr_mask &= ~INTR_OVERLAY1_DONE;
+		outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+		dma->waiting = FALSE;
+		spin_unlock(&mdp_spin_lock);
+#if defined(CONFIG_FB_MSM_DTV)
+		if (panel & MDP4_PANEL_DTV)
+			mdp4_overlay1_done_dtv();
+#endif
+#if defined(CONFIG_FB_MSM_TVOUT)
+		if (panel & MDP4_PANEL_ATV)
+			mdp4_overlay1_done_atv();
+#endif
+	}
+#endif	/* OVERLAY */
+
 	if (isr & INTR_DMA_P_DONE) {
 		mdp4_stat.intr_dma_p++;
 		dma = &dma2_data;
@@ -488,53 +537,6 @@
 		}
 		spin_unlock(&mdp_spin_lock);
 	}
-#ifdef CONFIG_FB_MSM_OVERLAY
-	if (isr & INTR_OVERLAY0_DONE) {
-		mdp4_stat.intr_overlay0++;
-		dma = &dma2_data;
-		if (panel & (MDP4_PANEL_LCDC | MDP4_PANEL_DSI_VIDEO)) {
-			/* disable LCDC interrupt */
-			spin_lock(&mdp_spin_lock);
-			mdp_intr_mask &= ~INTR_OVERLAY0_DONE;
-			outp32(MDP_INTR_ENABLE, mdp_intr_mask);
-			dma->waiting = FALSE;
-			spin_unlock(&mdp_spin_lock);
-			if (panel & MDP4_PANEL_LCDC)
-				mdp4_overlay0_done_lcdc(dma);
-#ifdef CONFIG_FB_MSM_MIPI_DSI
-			else if (panel & MDP4_PANEL_DSI_VIDEO)
-				mdp4_overlay0_done_dsi_video(dma);
-#endif
-		} else {        /* MDDI, DSI_CMD  */
-#ifdef CONFIG_FB_MSM_MIPI_DSI
-			if (panel & MDP4_PANEL_DSI_CMD)
-				mdp4_overlay0_done_dsi_cmd(dma);
-#else
-			if (panel & MDP4_PANEL_MDDI)
-				mdp4_overlay0_done_mddi(dma);
-#endif
-		}
-		mdp_hw_cursor_done();
-	}
-	if (isr & INTR_OVERLAY1_DONE) {
-		mdp4_stat.intr_overlay1++;
-		/* disable DTV interrupt */
-		dma = &dma_e_data;
-		spin_lock(&mdp_spin_lock);
-		mdp_intr_mask &= ~INTR_OVERLAY1_DONE;
-		outp32(MDP_INTR_ENABLE, mdp_intr_mask);
-		dma->waiting = FALSE;
-		spin_unlock(&mdp_spin_lock);
-#if defined(CONFIG_FB_MSM_DTV)
-		if (panel & MDP4_PANEL_DTV)
-			mdp4_overlay1_done_dtv();
-#endif
-#if defined(CONFIG_FB_MSM_TVOUT)
-		if (panel & MDP4_PANEL_ATV)
-			mdp4_overlay1_done_atv();
-#endif
-	}
-#endif	/* OVERLAY */
 	if (isr & INTR_DMA_P_HISTOGRAM) {
 		isr = inpdw(MDP_DMA_P_HIST_INTR_STATUS);
 		mask = inpdw(MDP_DMA_P_HIST_INTR_ENABLE);
diff --git a/drivers/video/msm/mipi_chimei_wxga_pt.c b/drivers/video/msm/mipi_chimei_wxga_pt.c
index fe7035a..4729d83 100644
--- a/drivers/video/msm/mipi_chimei_wxga_pt.c
+++ b/drivers/video/msm/mipi_chimei_wxga_pt.c
@@ -168,7 +168,12 @@
 	pinfo->mipi.stream = false; /* dma_p */
 	pinfo->mipi.mdp_trigger = DSI_CMD_TRIGGER_NONE;
 	pinfo->mipi.dma_trigger = DSI_CMD_TRIGGER_SW;
-	pinfo->mipi.fixed_packet_size = 4;
+	/*
+	 * toshiba d2l chip does not need max_pkt_szie dcs cmd
+	 * client reply len is directly configure through
+	 * RDPKTLN register (0x0404)
+	 */
+	pinfo->mipi.no_max_pkt_size = 1;
 	pinfo->mipi.force_clk_lane_hs = 1;
 
 	ret = mipi_tc358764_dsi2lvds_register(pinfo, MIPI_DSI_PRIM,
diff --git a/drivers/video/msm/mipi_dsi_host.c b/drivers/video/msm/mipi_dsi_host.c
index 600aac5..8d07e56 100644
--- a/drivers/video/msm/mipi_dsi_host.c
+++ b/drivers/video/msm/mipi_dsi_host.c
@@ -1159,20 +1159,22 @@
 			struct dsi_buf *tp, struct dsi_buf *rp,
 			struct dsi_cmd_desc *cmds, int rlen)
 {
-	int i , cnt, len, diff, pkt_size;
+	int cnt, len, diff, pkt_size;
 	unsigned long flag;
 	char cmd;
 
+	if (mfd->panel_info.mipi.no_max_pkt_size) {
+		/* Only support rlen = 4*n */
+		rlen += 3;
+		rlen &= 0x03;
+	}
+
 	len = rlen;
 	diff = 0;
 
 	if (len <= 2)
 		cnt = 4;	/* short read */
-	else if (mfd->panel_info.mipi.fixed_packet_size) {
-		len = mfd->panel_info.mipi.fixed_packet_size;
-		pkt_size = len; /* Avoid command to the device */
-		cnt = (len + 6 + 3) & ~0x03; /* Add padding for align */
-	} else {
+	else {
 		if (len > MIPI_DSI_LEN)
 			len = MIPI_DSI_LEN;	/* 8 bytes at most */
 
@@ -1203,7 +1205,7 @@
 	dsi_mdp_busy = TRUE;
 	spin_unlock_irqrestore(&dsi_mdp_lock, flag);
 
-	if (!mfd->panel_info.mipi.fixed_packet_size) {
+	if (!mfd->panel_info.mipi.no_max_pkt_size) {
 		/* packet size need to be set at every read */
 		pkt_size = len;
 		max_pktsize[0] = pkt_size;
@@ -1223,10 +1225,15 @@
 	 * at RDBK_DATA register already
 	 */
 	mipi_dsi_buf_init(rp);
-	mipi_dsi_cmd_dma_rx(rp, cnt);
+	if (mfd->panel_info.mipi.no_max_pkt_size) {
+		/*
+		 * expect rlen = n * 4
+		 * short alignement for start addr
+		 */
+		rp->data += 2;
+	}
 
-	for (i = 0; i < cnt ; i++)
-		pr_debug("%s.rp->data[%d]=0x%x.\n", __func__, i, rp->data[i]);
+	mipi_dsi_cmd_dma_rx(rp, cnt);
 
 	spin_lock_irqsave(&dsi_mdp_lock, flag);
 	dsi_mdp_busy = FALSE;
@@ -1234,12 +1241,16 @@
 	complete(&dsi_mdp_comp);
 	spin_unlock_irqrestore(&dsi_mdp_lock, flag);
 
-	/* Remove leading padding zeros if exist */
-	for (i = 0; i < cnt ; i++)
-		if (rp->data[0] == 0)
-			rp->data++;
-		else
-			break;
+	if (mfd->panel_info.mipi.no_max_pkt_size) {
+		/*
+		 * remove extra 2 bytes from previous
+		 * rx transaction at shift register
+		 * which was inserted during copy
+		 * shift registers to rx buffer
+		 * rx payload start from long alignment addr
+		 */
+		rp->data += 2;
+	}
 
 	cmd = rp->data[0];
 	switch (cmd) {
diff --git a/drivers/video/msm/msm_fb_panel.h b/drivers/video/msm/msm_fb_panel.h
index 4c415b6..16979b1 100644
--- a/drivers/video/msm/msm_fb_panel.h
+++ b/drivers/video/msm/msm_fb_panel.h
@@ -122,7 +122,7 @@
 	char dma_trigger;
 	uint32 dsi_pclk_rate;
 	/* The packet-size should not bet changed */
-	char fixed_packet_size;
+	char no_max_pkt_size;
 	/* Clock required during LP commands */
 	char force_clk_lane_hs;
 	/* Pad width */
diff --git a/include/linux/ion.h b/include/linux/ion.h
index 9f220f8..4b7b8b7d1 100644
--- a/include/linux/ion.h
+++ b/include/linux/ion.h
@@ -65,6 +65,7 @@
 #define ION_VMALLOC_HEAP_NAME	"vmalloc"
 #define ION_EBI1_HEAP_NAME	"EBI1"
 #define ION_ADSP_HEAP_NAME	"adsp"
+#define ION_SMI_HEAP_NAME	"smi"
 
 #define CACHED          1
 #define UNCACHED        0
diff --git a/mm/slub.c b/mm/slub.c
index 35f351f..adf609e 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -557,10 +557,10 @@
 		memset(p + s->objsize, val, s->inuse - s->objsize);
 }
 
-static u8 *check_bytes(u8 *start, unsigned int value, unsigned int bytes)
+static u8 *check_bytes8(u8 *start, u8 value, unsigned int bytes)
 {
 	while (bytes) {
-		if (*start != (u8)value)
+		if (*start != value)
 			return start;
 		start++;
 		bytes--;
@@ -568,6 +568,38 @@
 	return NULL;
 }
 
+static u8 *check_bytes(u8 *start, u8 value, unsigned int bytes)
+{
+	u64 value64;
+	unsigned int words, prefix;
+
+	if (bytes <= 16)
+		return check_bytes8(start, value, bytes);
+
+	value64 = value | value << 8 | value << 16 | value << 24;
+	value64 = (value64 & 0xffffffff) | value64 << 32;
+	prefix = 8 - ((unsigned long)start) % 8;
+
+	if (prefix) {
+		u8 *r = check_bytes8(start, value, prefix);
+		if (r)
+			return r;
+		start += prefix;
+		bytes -= prefix;
+	}
+
+	words = bytes / 8;
+
+	while (words) {
+		if (*(u64 *)start != value64)
+			return check_bytes8(start, value, 8);
+		start += 8;
+		words--;
+	}
+
+	return check_bytes8(start, value, bytes % 8);
+}
+
 static void restore_bytes(struct kmem_cache *s, char *message, u8 data,
 						void *from, void *to)
 {