Merge "usb: pd: policy_engine: Handle spec revision properly"
diff --git a/Documentation/devicetree/bindings/media/video/msm-cam-cpas.txt b/Documentation/devicetree/bindings/media/video/msm-cam-cpas.txt
index a61bab3..62a51cf 100644
--- a/Documentation/devicetree/bindings/media/video/msm-cam-cpas.txt
+++ b/Documentation/devicetree/bindings/media/video/msm-cam-cpas.txt
@@ -104,6 +104,17 @@
   Please refer Documentation/devicetree/bindings/arm/msm/msm_bus.txt
   for the properties above.
 
+- vdd-corners
+  Usage: required
+  Value type: <u32>
+  Definition: List of vdd corners to map for ahb level.
+
+- vdd-corner-ahb-mapping
+  Usage: required
+  Value type: <string>
+  Definition: List of ahb level strings corresponds to vdd-corners.
+  Supported strings: suspend, svs, nominal, turbo
+
 - client-id-based
   Usage: required
   Value type: <empty>
diff --git a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt
index 07ec82f..441d771 100644
--- a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt
+++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt
@@ -176,6 +176,12 @@
   Definition: Specifies the maximum charger buck/boost switching frequency in
 		 KHz. It overrides the max frequency defined for the charger.
 
+- qcom,otg-deglitch-time-ms
+  Usage:      optional
+  Value type: <u32>
+  Definition: Specifies the deglitch interval for OTG detection.
+		If the value is not present, 50 msec is used as default.
+
 =============================================
 Second Level Nodes - SMB2 Charger Peripherals
 =============================================
diff --git a/arch/arm/configs/sdxpoorwills-perf_defconfig b/arch/arm/configs/sdxpoorwills-perf_defconfig
index fc0d3b0..40289a8 100644
--- a/arch/arm/configs/sdxpoorwills-perf_defconfig
+++ b/arch/arm/configs/sdxpoorwills-perf_defconfig
@@ -270,6 +270,8 @@
 CONFIG_MSM_SMP2P=y
 CONFIG_MSM_SMP2P_TEST=y
 CONFIG_MSM_SUBSYSTEM_RESTART=y
+CONFIG_MSM_PIL=y
+CONFIG_MSM_PIL_SSR_GENERIC=y
 CONFIG_PWM=y
 CONFIG_QCOM_SHOW_RESUME_IRQ=y
 CONFIG_ANDROID=y
diff --git a/arch/arm/configs/sdxpoorwills_defconfig b/arch/arm/configs/sdxpoorwills_defconfig
index 9d12771..d91f5f6 100644
--- a/arch/arm/configs/sdxpoorwills_defconfig
+++ b/arch/arm/configs/sdxpoorwills_defconfig
@@ -263,6 +263,8 @@
 CONFIG_MSM_BOOT_STATS=y
 CONFIG_TRACER_PKT=y
 CONFIG_MSM_SUBSYSTEM_RESTART=y
+CONFIG_MSM_PIL=y
+CONFIG_MSM_PIL_SSR_GENERIC=y
 CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND=y
 CONFIG_PWM=y
 CONFIG_QCOM_SHOW_RESUME_IRQ=y
diff --git a/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi b/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi
index aaef335..02f1cbb 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi
@@ -366,6 +366,21 @@
 			MSM_BUS_SLAVE_CAMERA_CFG 0 640000>,
 			<MSM_BUS_MASTER_AMPSS_M0
 			MSM_BUS_SLAVE_CAMERA_CFG 0 640000>;
+		vdd-corners = <RPMH_REGULATOR_LEVEL_OFF
+			RPMH_REGULATOR_LEVEL_RETENTION
+			RPMH_REGULATOR_LEVEL_MIN_SVS
+			RPMH_REGULATOR_LEVEL_LOW_SVS
+			RPMH_REGULATOR_LEVEL_SVS
+			RPMH_REGULATOR_LEVEL_SVS_L1
+			RPMH_REGULATOR_LEVEL_NOM
+			RPMH_REGULATOR_LEVEL_NOM_L1
+			RPMH_REGULATOR_LEVEL_NOM_L2
+			RPMH_REGULATOR_LEVEL_TURBO
+			RPMH_REGULATOR_LEVEL_TURBO_L1>;
+		vdd-corner-ahb-mapping = "suspend", "suspend",
+			"svs", "svs", "svs", "svs",
+			"nominal", "nominal", "nominal",
+			"turbo", "turbo";
 		client-id-based;
 		client-names =
 			"csiphy0", "csiphy1", "csiphy2", "cci0",
diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
index c89a05e..c1d4e35 100644
--- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
@@ -480,9 +480,14 @@
 		#size-cells = <2>;
 		ranges;
 
-		removed_regions: removed_regions@85800000 {
+		removed_region1: removed_region1@85700000 {
 			no-map;
-			reg = <0 0x85800000 0 0x3700000>;
+			reg = <0 0x85700000 0 0x800000>;
+		};
+
+		removed_region2: removed_region2@85fc0000 {
+			no-map;
+			reg = <0 0x85fc0000 0 0x2f40000>;
 		};
 
 		pil_camera_mem: camera_region@8ab00000 {
diff --git a/arch/arm64/configs/sdm845-perf_defconfig b/arch/arm64/configs/sdm845-perf_defconfig
index 8c20b3f..e9403de 100644
--- a/arch/arm64/configs/sdm845-perf_defconfig
+++ b/arch/arm64/configs/sdm845-perf_defconfig
@@ -9,6 +9,8 @@
 CONFIG_SCHED_WALT=y
 CONFIG_RCU_EXPERT=y
 CONFIG_RCU_FAST_NO_HZ=y
+CONFIG_RCU_NOCB_CPU=y
+CONFIG_RCU_NOCB_CPU_ALL=y
 CONFIG_IKCONFIG=y
 CONFIG_IKCONFIG_PROC=y
 CONFIG_LOG_CPU_MAX_BUF_SHIFT=17
diff --git a/arch/arm64/configs/sdm845_defconfig b/arch/arm64/configs/sdm845_defconfig
index 0bebc63b..27805b2 100644
--- a/arch/arm64/configs/sdm845_defconfig
+++ b/arch/arm64/configs/sdm845_defconfig
@@ -12,6 +12,8 @@
 CONFIG_TASK_IO_ACCOUNTING=y
 CONFIG_RCU_EXPERT=y
 CONFIG_RCU_FAST_NO_HZ=y
+CONFIG_RCU_NOCB_CPU=y
+CONFIG_RCU_NOCB_CPU_ALL=y
 CONFIG_IKCONFIG=y
 CONFIG_IKCONFIG_PROC=y
 CONFIG_LOG_CPU_MAX_BUF_SHIFT=17
diff --git a/drivers/md/dm.h b/drivers/md/dm.h
index f0aad08..ed25f30 100644
--- a/drivers/md/dm.h
+++ b/drivers/md/dm.h
@@ -80,8 +80,6 @@
 unsigned dm_get_md_type(struct mapped_device *md);
 struct target_type *dm_get_immutable_target_type(struct mapped_device *md);
 
-int dm_setup_md_queue(struct mapped_device *md, struct dm_table *t);
-
 /*
  * To check the return value from dm_table_find_target().
  */
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.c b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.c
index 4f246e1..8d9f4a5 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.c
+++ b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.c
@@ -628,9 +628,46 @@
 	return rc;
 }
 
-static int cam_cpas_util_apply_client_ahb_vote(struct cam_cpas *cpas_core,
+static int cam_cpas_util_get_ahb_level(struct cam_hw_info *cpas_hw,
+	struct device *dev, unsigned long freq, enum cam_vote_level *req_level)
+{
+	struct cam_cpas_private_soc *soc_private =
+		(struct cam_cpas_private_soc *) cpas_hw->soc_info.soc_private;
+	struct dev_pm_opp *opp;
+	unsigned int corner;
+	enum cam_vote_level level = CAM_SVS_VOTE;
+	unsigned long corner_freq = freq;
+	int i;
+
+	if (!dev || !req_level) {
+		pr_err("Invalid params %pK, %pK\n", dev, req_level);
+		return -EINVAL;
+	}
+
+	opp = dev_pm_opp_find_freq_ceil(dev, &corner_freq);
+	if (IS_ERR(opp)) {
+		pr_err("Error on OPP freq :%ld, %pK\n", corner_freq, opp);
+		return -EINVAL;
+	}
+
+	corner = dev_pm_opp_get_voltage(opp);
+
+	for (i = 0; i < soc_private->num_vdd_ahb_mapping; i++)
+		if (corner == soc_private->vdd_ahb[i].vdd_corner)
+			level = soc_private->vdd_ahb[i].ahb_level;
+
+	CPAS_CDBG("From OPP table : freq=[%ld][%ld], corner=%d, level=%d\n",
+		freq, corner_freq, corner, level);
+
+	*req_level = level;
+
+	return 0;
+}
+
+static int cam_cpas_util_apply_client_ahb_vote(struct cam_hw_info *cpas_hw,
 	struct cam_cpas_client *cpas_client, struct cam_ahb_vote *ahb_vote)
 {
+	struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info;
 	struct cam_cpas_bus_client *ahb_bus_client = &cpas_core->ahb_bus_client;
 	enum cam_vote_level required_level;
 	enum cam_vote_level highest_level;
@@ -642,12 +679,14 @@
 	}
 
 	if (ahb_vote->type == CAM_VOTE_DYNAMIC) {
-		pr_err("Dynamic AHB vote not supported\n");
-		return -EINVAL;
+		rc = cam_cpas_util_get_ahb_level(cpas_hw, cpas_client->data.dev,
+			ahb_vote->vote.freq, &required_level);
+		if (rc)
+			return rc;
+	} else {
+		required_level = ahb_vote->vote.level;
 	}
 
-	required_level = ahb_vote->vote.level;
-
 	if (cpas_client->ahb_level == required_level)
 		return 0;
 
@@ -708,7 +747,7 @@
 		ahb_vote->vote.freq,
 		cpas_core->cpas_client[client_indx]->ahb_level);
 
-	rc = cam_cpas_util_apply_client_ahb_vote(cpas_core,
+	rc = cam_cpas_util_apply_client_ahb_vote(cpas_hw,
 		cpas_core->cpas_client[client_indx], ahb_vote);
 
 unlock_client:
@@ -780,7 +819,7 @@
 	CPAS_CDBG("AHB :client[%d] type[%d], level[%d], applied[%d]\n",
 		client_indx, ahb_vote->type, ahb_vote->vote.level,
 		cpas_client->ahb_level);
-	rc = cam_cpas_util_apply_client_ahb_vote(cpas_core, cpas_client,
+	rc = cam_cpas_util_apply_client_ahb_vote(cpas_hw, cpas_client,
 		ahb_vote);
 	if (rc)
 		goto done;
@@ -800,8 +839,8 @@
 			goto done;
 		}
 
-		if (cpas_core->internal_ops.power_on_settings) {
-			rc = cpas_core->internal_ops.power_on_settings(cpas_hw);
+		if (cpas_core->internal_ops.power_on) {
+			rc = cpas_core->internal_ops.power_on(cpas_hw);
 			if (rc) {
 				cam_cpas_soc_disable_resources(
 					&cpas_hw->soc_info);
@@ -873,6 +912,15 @@
 	cpas_core->streamon_clients--;
 
 	if (cpas_core->streamon_clients == 0) {
+		if (cpas_core->internal_ops.power_off) {
+			rc = cpas_core->internal_ops.power_off(cpas_hw);
+			if (rc) {
+				pr_err("failed in power_off settings rc=%d\n",
+					rc);
+				/* Do not return error, passthrough */
+			}
+		}
+
 		rc = cam_cpas_soc_disable_resources(&cpas_hw->soc_info);
 		if (rc) {
 			pr_err("disable_resorce failed, rc=%d\n", rc);
@@ -883,7 +931,7 @@
 
 	ahb_vote.type = CAM_VOTE_ABSOLUTE;
 	ahb_vote.vote.level = CAM_SUSPEND_VOTE;
-	rc = cam_cpas_util_apply_client_ahb_vote(cpas_core, cpas_client,
+	rc = cam_cpas_util_apply_client_ahb_vote(cpas_hw, cpas_client,
 		&ahb_vote);
 	if (rc)
 		goto done;
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.h b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.h
index c181302..52649ec 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.h
+++ b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.h
@@ -45,7 +45,8 @@
  * @init_hw_version: Function pointer for hw init based on version
  * @handle_irq: Function poniter for irq handling
  * @setup_regbase: Function pointer for setup rebase indices
- * @power_on_settings: Function pointer for hw core specific power on settings
+ * @power_on: Function pointer for hw core specific power on settings
+ * @power_off: Function pointer for hw core specific power off settings
  *
  */
 struct cam_cpas_internal_ops {
@@ -56,7 +57,8 @@
 	irqreturn_t (*handle_irq)(int irq_num, void *data);
 	int (*setup_regbase)(struct cam_hw_soc_info *soc_info,
 		int32_t regbase_index[], int32_t num_reg_map);
-	int (*power_on_settings)(struct cam_hw_info *cpas_hw);
+	int (*power_on)(struct cam_hw_info *cpas_hw);
+	int (*power_off)(struct cam_hw_info *cpas_hw);
 };
 
 /**
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw_intf.h b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw_intf.h
index d2c3e06..9ee5a43 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw_intf.h
+++ b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw_intf.h
@@ -29,6 +29,13 @@
 
 #define BITS_MASK_SHIFT(x, mask, shift) (((x) & (mask)) >> shift)
 
+/* Number of times to retry while polling */
+#define CAM_CPAS_POLL_RETRY_CNT 5
+/* Minimum usecs to sleep while polling */
+#define CAM_CPAS_POLL_MIN_USECS 200
+/* Maximum usecs to sleep while polling */
+#define CAM_CPAS_POLL_MAX_USECS 250
+
 /**
  * enum cam_cpas_hw_type - Enum for CPAS HW type
  */
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.c b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.c
index 0a8e6bb..0c71ece 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.c
+++ b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.c
@@ -22,6 +22,26 @@
 #include "cam_cpas_hw.h"
 #include "cam_cpas_soc.h"
 
+static int cam_cpas_get_vote_level_from_string(const char *string,
+	enum cam_vote_level *vote_level)
+{
+	if (!vote_level || !string)
+		return -EINVAL;
+
+	if (strnstr("suspend", string, strlen(string)))
+		*vote_level = CAM_SUSPEND_VOTE;
+	else if (strnstr("svs", string, strlen(string)))
+		*vote_level = CAM_SVS_VOTE;
+	else if (strnstr("nominal", string, strlen(string)))
+		*vote_level = CAM_NOMINAL_VOTE;
+	else if (strnstr("turbo", string, strlen(string)))
+		*vote_level = CAM_TURBO_VOTE;
+	else
+		*vote_level = CAM_SVS_VOTE;
+
+	return 0;
+}
+
 int cam_cpas_get_custom_dt_info(struct platform_device *pdev,
 	struct cam_cpas_private_soc *soc_private)
 {
@@ -89,6 +109,42 @@
 	soc_private->axi_camnoc_based = of_property_read_bool(of_node,
 		"client-bus-camnoc-based");
 
+	count = of_property_count_u32_elems(of_node, "vdd-corners");
+	if ((count > 0) && (count <= CAM_REGULATOR_LEVEL_MAX) &&
+		(of_property_count_strings(of_node, "vdd-corner-ahb-mapping") ==
+		count)) {
+		const char *ahb_string;
+
+		for (i = 0; i < count; i++) {
+			rc = of_property_read_u32_index(of_node, "vdd-corners",
+				i, &soc_private->vdd_ahb[i].vdd_corner);
+			if (rc) {
+				pr_err("vdd-corners failed at index=%d\n", i);
+				return -ENODEV;
+			}
+
+			rc = of_property_read_string_index(of_node,
+				"vdd-corner-ahb-mapping", i, &ahb_string);
+			if (rc) {
+				pr_err("no ahb-mapping at index=%d\n", i);
+				return -ENODEV;
+			}
+
+			rc = cam_cpas_get_vote_level_from_string(ahb_string,
+				&soc_private->vdd_ahb[i].ahb_level);
+			if (rc) {
+				pr_err("invalid ahb-string at index=%d\n", i);
+				return -EINVAL;
+			}
+
+			CPAS_CDBG("Vdd-AHB mapping [%d] : [%d] [%s] [%d]\n", i,
+				soc_private->vdd_ahb[i].vdd_corner,
+				ahb_string, soc_private->vdd_ahb[i].ahb_level);
+		}
+
+		soc_private->num_vdd_ahb_mapping = count;
+	}
+
 	return 0;
 }
 
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.h b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.h
index fdd9386..d3dfbbd 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.h
+++ b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.h
@@ -16,6 +16,19 @@
 #include "cam_soc_util.h"
 
 #define CAM_CPAS_MAX_CLIENTS 20
+#define CAM_REGULATOR_LEVEL_MAX 16
+
+/**
+ * struct cam_cpas_vdd_ahb_mapping : Voltage to ahb level mapping
+ *
+ * @vdd_corner : Voltage corner value
+ * @ahb_level : AHB vote level corresponds to this vdd_corner
+ *
+ */
+struct cam_cpas_vdd_ahb_mapping {
+	unsigned int vdd_corner;
+	enum cam_vote_level ahb_level;
+};
 
 /**
  * struct cam_cpas_private_soc : CPAS private DT info
@@ -27,6 +40,8 @@
  * @axi_camnoc_based: Whether AXi access is camnoc based
  * @client_axi_port_name: AXI Port name for each client
  * @axi_port_list_node : Node representing AXI Ports list
+ * @num_vdd_ahb_mapping : Number of vdd to ahb level mapping supported
+ * @vdd_ahb : AHB level mapping info for the supported vdd levels
  *
  */
 struct cam_cpas_private_soc {
@@ -37,6 +52,8 @@
 	bool axi_camnoc_based;
 	const char *client_axi_port_name[CAM_CPAS_MAX_CLIENTS];
 	struct device_node *axi_port_list_node;
+	uint32_t num_vdd_ahb_mapping;
+	struct cam_cpas_vdd_ahb_mapping vdd_ahb[CAM_REGULATOR_LEVEL_MAX];
 };
 
 int cam_cpas_soc_init_resources(struct cam_hw_soc_info *soc_info,
diff --git a/drivers/media/platform/msm/camera/cam_cpas/camss_top/cam_camsstop_hw.c b/drivers/media/platform/msm/camera/cam_cpas/camss_top/cam_camsstop_hw.c
index fa8ab89..95e26c5 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/camss_top/cam_camsstop_hw.c
+++ b/drivers/media/platform/msm/camera/cam_cpas/camss_top/cam_camsstop_hw.c
@@ -81,7 +81,8 @@
 	internal_ops->init_hw_version = NULL;
 	internal_ops->handle_irq = NULL;
 	internal_ops->setup_regbase = cam_camsstop_setup_regbase_indices;
-	internal_ops->power_on_settings = NULL;
+	internal_ops->power_on = NULL;
+	internal_ops->power_off = NULL;
 
 	return 0;
 }
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.c b/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.c
index 415de47..d26c2b6 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.c
+++ b/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.c
@@ -230,7 +230,7 @@
 	return IRQ_HANDLED;
 }
 
-static int cam_cpastop_static_settings(struct cam_hw_info *cpas_hw)
+static int cam_cpastop_poweron(struct cam_hw_info *cpas_hw)
 {
 	int i;
 
@@ -256,6 +256,38 @@
 	return 0;
 }
 
+static int cam_cpastop_poweroff(struct cam_hw_info *cpas_hw)
+{
+	struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info;
+	struct cam_hw_soc_info *soc_info = &cpas_hw->soc_info;
+	int camnoc_index = cpas_core->regbase_index[CAM_CPAS_REG_CAMNOC];
+	int rc = 0;
+	struct cam_cpas_hw_errata_wa_list *errata_wa_list =
+		camnoc_info->errata_wa_list;
+
+	if (!errata_wa_list)
+		return 0;
+
+	if (errata_wa_list->camnoc_flush_slave_pending_trans.enable) {
+		struct cam_cpas_hw_errata_wa *errata_wa =
+			&errata_wa_list->camnoc_flush_slave_pending_trans;
+
+		rc = cam_io_poll_value_wmask(
+			soc_info->reg_map[camnoc_index].mem_base +
+			errata_wa->data.reg_info.offset,
+			errata_wa->data.reg_info.value,
+			errata_wa->data.reg_info.mask,
+			CAM_CPAS_POLL_RETRY_CNT,
+			CAM_CPAS_POLL_MIN_USECS, CAM_CPAS_POLL_MAX_USECS);
+		if (rc) {
+			pr_err("camnoc flush slave pending trans failed\n");
+			/* Do not return error, passthrough */
+		}
+	}
+
+	return rc;
+}
+
 static int cam_cpastop_init_hw_version(struct cam_hw_info *cpas_hw,
 	struct cam_cpas_hw_caps *hw_caps)
 {
@@ -295,7 +327,8 @@
 	internal_ops->init_hw_version = cam_cpastop_init_hw_version;
 	internal_ops->handle_irq = cam_cpastop_handle_irq;
 	internal_ops->setup_regbase = cam_cpastop_setup_regbase_indices;
-	internal_ops->power_on_settings = cam_cpastop_static_settings;
+	internal_ops->power_on = cam_cpastop_poweron;
+	internal_ops->power_off = cam_cpastop_poweroff;
 
 	return 0;
 }
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.h b/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.h
index 99aae3f..c61204a 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.h
+++ b/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.h
@@ -147,6 +147,31 @@
 };
 
 /**
+ * struct cam_cpas_hw_errata_wa : Struct for HW errata workaround info
+ *
+ * @enable: Whether to enable this errata workround
+ * @data: HW Errata workaround data
+ *
+ */
+struct cam_cpas_hw_errata_wa {
+	bool enable;
+	union {
+		struct cam_cpas_reg reg_info;
+	} data;
+};
+
+/**
+ * struct cam_cpas_hw_errata_wa_list : List of HW Errata workaround info
+ *
+ * @camnoc_flush_slave_pending_trans: Errata workaround info for flushing
+ *         camnoc slave pending transactions before turning off CPAS_TOP gdsc
+ *
+ */
+struct cam_cpas_hw_errata_wa_list {
+	struct cam_cpas_hw_errata_wa camnoc_flush_slave_pending_trans;
+};
+
+/**
  * struct cam_camnoc_info : Overall CAMNOC settings info
  *
  * @specific: Pointer to CAMNOC SPECIFICTONTTPTR settings
@@ -156,6 +181,7 @@
  * @irq_err_size: Array size of IRQ Error settings
  * @error_logger: Pointer to CAMNOC IRQ Error logger read registers
  * @error_logger_size: Array size of IRQ Error logger
+ * @errata_wa_list: HW Errata workaround info
  *
  */
 struct cam_camnoc_info {
@@ -166,6 +192,7 @@
 	int irq_err_size;
 	uint32_t *error_logger;
 	int error_logger_size;
+	struct cam_cpas_hw_errata_wa_list *errata_wa_list;
 };
 
 #endif /* _CAM_CPASTOP_HW_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cpastop100.h b/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cpastop100.h
index 20ed1b6..b30cd05 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cpastop100.h
+++ b/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cpastop100.h
@@ -513,6 +513,18 @@
 	0x273c, /* ERRLOGGER_ERRLOG3_HIGH */
 };
 
+static struct cam_cpas_hw_errata_wa_list cam170_cpas100_errata_wa_list = {
+	.camnoc_flush_slave_pending_trans = {
+		.enable = true,
+		.data.reg_info = {
+			.access_type = CAM_REG_TYPE_READ,
+			.offset = 0x2100, /* SidebandManager_SenseIn0_Low */
+			.mask = 0xE0000, /* Bits 17, 18, 19 */
+			.value = 0, /* expected to be 0 */
+		},
+	},
+};
+
 struct cam_camnoc_info cam170_cpas100_camnoc_info = {
 	.specific = &cam_cpas100_camnoc_specific[0],
 	.specific_size = sizeof(cam_cpas100_camnoc_specific) /
@@ -524,6 +536,7 @@
 	.error_logger = &slave_error_logger[0],
 	.error_logger_size = sizeof(slave_error_logger) /
 		sizeof(slave_error_logger[0]),
+	.errata_wa_list = &cam170_cpas100_errata_wa_list,
 };
 
 #endif /* _CPASTOP100_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/Makefile b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/Makefile
index bdae1d1..6292a9f 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/Makefile
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/Makefile
@@ -5,4 +5,4 @@
 ccflags-y += -Idrivers/media/platform/msm/camera/cam_sensor_module/cam_cci
 ccflags-y += -Idrivers/media/platform/msm/camera/cam_req_mgr
 
-obj-$(CONFIG_SPECTRA_CAMERA) += cam_sensor_io.o cam_sensor_cci_i2c.o
+obj-$(CONFIG_SPECTRA_CAMERA) += cam_sensor_io.o cam_sensor_cci_i2c.o cam_sensor_qup_i2c.o
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_i2c.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_i2c.h
index 1261c4b..06e8104 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_i2c.h
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_i2c.h
@@ -75,4 +75,64 @@
 	enum camera_sensor_i2c_type addr_type,
 	uint32_t delay_ms);
 
-#endif /* _CAM_SENSOR_I2C_H_ */
+
+/**
+ * cam_qup_i2c_read : QUP based i2c read
+ * @client    : QUP I2C client structure
+ * @data      : I2C data
+ * @addr_type : I2c address type
+ * @data_type : I2C data type
+ *
+ * This API handles QUP I2C read
+ */
+
+int32_t cam_qup_i2c_read(struct i2c_client *client,
+	uint32_t addr, uint32_t *data,
+	enum camera_sensor_i2c_type addr_type,
+	enum camera_sensor_i2c_type data_type);
+
+/**
+ * cam_qup_i2c_read_seq : QUP based I2C sequential read
+ * @client    : QUP I2C client structure
+ * @data      : I2C data
+ * @addr_type : I2c address type
+ * @num_bytes : number of bytes to read
+ * This API handles QUP I2C Sequential read
+ */
+
+int32_t cam_qup_i2c_read_seq(struct i2c_client *client,
+	uint32_t addr, uint8_t *data,
+	enum camera_sensor_i2c_type addr_type,
+	uint32_t num_byte);
+
+/**
+ * cam_qup_i2c_poll : QUP based I2C poll operation
+ * @client    : QUP I2C client structure
+ * @addr      : I2C address
+ * @data      : I2C data
+ * @data_mask : I2C data mask
+ * @data_type : I2C data type
+ * @addr_type : I2C addr type
+ * @delay_ms  : Delay in milli seconds
+ *
+ * This API implements QUP based I2C poll
+ */
+
+int32_t cam_qup_i2c_poll(struct i2c_client *client,
+	uint32_t addr, uint16_t data, uint16_t data_mask,
+	enum camera_sensor_i2c_type addr_type,
+	enum camera_sensor_i2c_type data_type,
+	uint32_t delay_ms);
+
+/**
+ * cam_qup_i2c_write_table : QUP based I2C write random
+ * @client        : QUP I2C client structure
+ * @write_setting : I2C register settings
+ *
+ * This API handles QUP I2C random write
+ */
+
+int32_t cam_qup_i2c_write_table(
+	struct camera_io_master *client,
+	struct cam_sensor_i2c_reg_setting *write_setting);
+#endif /*_CAM_SENSOR_I2C_H*/
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.c
index 13e115a..3e1b331 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.c
@@ -29,6 +29,10 @@
 	if (io_master_info->master_type == CCI_MASTER) {
 		return cam_cci_i2c_poll(io_master_info->cci_client,
 			addr, data, mask, data_type, addr_type, delay_ms);
+	} else if (io_master_info->master_type == I2C_MASTER) {
+		return cam_qup_i2c_poll(io_master_info->client,
+			addr, data, data_mask, addr_type, data_type,
+			delay_ms);
 	} else {
 		pr_err("%s:%d Invalid Comm. Master:%d\n", __func__,
 			__LINE__, io_master_info->master_type);
@@ -49,6 +53,9 @@
 	if (io_master_info->master_type == CCI_MASTER) {
 		return cam_cci_i2c_read(io_master_info->cci_client,
 			addr, data, addr_type, data_type);
+	} else if (io_master_info->master_type == I2C_MASTER) {
+		return cam_qup_i2c_read(io_master_info->client,
+			addr, data, addr_type, data_type);
 	} else {
 		pr_err("%s:%d Invalid Comm. Master:%d\n", __func__,
 			__LINE__, io_master_info->master_type);
@@ -68,6 +75,9 @@
 	if (io_master_info->master_type == CCI_MASTER) {
 		return cam_cci_i2c_write_table(io_master_info,
 			write_setting);
+	} else if (io_master_info->master_type == I2C_MASTER) {
+		return cam_qup_i2c_write_table(io_master_info,
+			write_setting);
 	} else {
 		pr_err("%s:%d Invalid Comm. Master:%d\n", __func__,
 			__LINE__, io_master_info->master_type);
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.h
index 27bbe6e..f721afd 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.h
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.h
@@ -38,7 +38,7 @@
  * @io_master_info: I2C/SPI master information
  * @addr: I2C address
  * @data: I2C data
- * @addr_type: I2C addr type
+ * @addr_type: I2C addr_type
  * @data_type: I2C data type
  *
  * This API abstracts read functionality based on master type
@@ -50,6 +50,21 @@
 
 /**
  * @io_master_info: I2C/SPI master information
+ * @addr: I2C address
+ * @data: I2C data
+ * @addr_type: I2C addr type
+ * @num_bytes: number of bytes
+ *
+ * This API abstracts sequential read functionality based on master type
+ */
+int32_t camera_io_dev_read_seq(struct camera_io_master *io_master_info,
+	uint32_t addr, uint8_t *data,
+	enum camera_sensor_i2c_type addr_type,
+	uint32_t num_bytes);
+
+
+/**
+ * @io_master_info: I2C/SPI master information
  *
  * This API initializes the I2C/SPI master based on master type
  */
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_qup_i2c.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_qup_i2c.c
new file mode 100644
index 0000000..b25b1855
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_qup_i2c.c
@@ -0,0 +1,361 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "cam_sensor_cmn_header.h"
+#include "cam_sensor_i2c.h"
+#include "cam_sensor_io.h"
+
+#define I2C_REG_DATA_MAX       (8*1024)
+#define I2C_REG_MAX_BUF_SIZE   8
+
+static int32_t cam_qup_i2c_rxdata(
+	struct i2c_client *dev_client, unsigned char *rxdata,
+	enum camera_sensor_i2c_type addr_type,
+	int data_length)
+{
+	int32_t rc = 0;
+	uint16_t saddr = dev_client->addr >> 1;
+	struct i2c_msg msgs[] = {
+		{
+			.addr  = saddr,
+			.flags = 0,
+			.len   = addr_type,
+			.buf   = rxdata,
+		},
+		{
+			.addr  = saddr,
+			.flags = I2C_M_RD,
+			.len   = data_length,
+			.buf   = rxdata,
+		},
+	};
+	rc = i2c_transfer(dev_client->adapter, msgs, 2);
+	if (rc < 0)
+		pr_err("%s:failed 0x%x\n", __func__, saddr);
+	return rc;
+}
+
+
+static int32_t cam_qup_i2c_txdata(
+	struct camera_io_master *dev_client, unsigned char *txdata,
+	int length)
+{
+	int32_t rc = 0;
+	uint16_t saddr = dev_client->client->addr >> 1;
+	struct i2c_msg msg[] = {
+		{
+			.addr = saddr,
+			.flags = 0,
+			.len = length,
+			.buf = txdata,
+		 },
+	};
+	rc = i2c_transfer(dev_client->client->adapter, msg, 1);
+	if (rc < 0)
+		pr_err("%s: failed 0x%x\n", __func__, saddr);
+	return rc;
+}
+
+int32_t cam_qup_i2c_read(struct i2c_client *client,
+	uint32_t addr, uint32_t *data,
+	enum camera_sensor_i2c_type addr_type,
+	enum camera_sensor_i2c_type data_type)
+{
+	int32_t rc = -EINVAL;
+	unsigned char *buf = NULL;
+
+	if (addr_type <= CAMERA_SENSOR_I2C_TYPE_INVALID
+		|| addr_type >= CAMERA_SENSOR_I2C_TYPE_MAX
+		|| data_type <= CAMERA_SENSOR_I2C_TYPE_INVALID
+		|| data_type >= CAMERA_SENSOR_I2C_TYPE_MAX) {
+		pr_err("ERR: %s Failed with addr/data_type verfication\n",
+			__func__);
+		return rc;
+	}
+
+	buf = kzalloc(addr_type + data_type, GFP_KERNEL);
+
+	if (!buf)
+		return -ENOMEM;
+
+	if (addr_type == CAMERA_SENSOR_I2C_TYPE_BYTE) {
+		buf[0] = addr;
+	} else if (addr_type == CAMERA_SENSOR_I2C_TYPE_WORD) {
+		buf[0] = addr >> 8;
+		buf[1] = addr;
+	} else if (addr_type == CAMERA_SENSOR_I2C_TYPE_3B) {
+		buf[0] = addr >> 16;
+		buf[1] = addr >> 8;
+		buf[2] = addr;
+	} else {
+		buf[0] = addr >> 24;
+		buf[1] = addr >> 16;
+		buf[2] = addr >> 8;
+		buf[3] = addr;
+	}
+
+	rc = cam_qup_i2c_rxdata(client, buf, addr_type, data_type);
+	if (rc < 0) {
+		pr_err("%s fail\n", __func__);
+		goto read_fail;
+	}
+
+	if (data_type == CAMERA_SENSOR_I2C_TYPE_BYTE)
+		*data = buf[0];
+	else if (data_type == CAMERA_SENSOR_I2C_TYPE_WORD)
+		*data = buf[0] << 8 | buf[1];
+	else if (data_type == CAMERA_SENSOR_I2C_TYPE_3B)
+		*data = buf[0] << 16 | buf[1] << 8 | buf[2];
+	else
+		*data = buf[0] << 24 | buf[1] << 16 |
+			buf[2] << 8 | buf[3];
+
+	CDBG("%s addr = 0x%x data: 0x%x\n", __func__, addr, *data);
+read_fail:
+	kfree(buf);
+	buf = NULL;
+	return rc;
+}
+
+int32_t cam_qup_i2c_read_seq(struct i2c_client *client,
+	uint32_t addr, uint8_t *data,
+	enum camera_sensor_i2c_type addr_type,
+	uint32_t num_byte)
+{
+	int32_t rc = -EFAULT;
+	unsigned char *buf = NULL;
+	int i;
+
+	if (addr_type <= CAMERA_SENSOR_I2C_TYPE_INVALID
+		|| addr_type >= CAMERA_SENSOR_I2C_TYPE_MAX) {
+		pr_err("ERR: %s Failed with addr_type verification\n",
+			__func__);
+		return rc;
+	}
+
+	if ((num_byte == 0) || (num_byte > I2C_REG_DATA_MAX)) {
+		pr_err("%s: Error num_byte:0x%x max supported:0x%x\n",
+			__func__, num_byte, I2C_REG_DATA_MAX);
+		return rc;
+	}
+
+	buf = kzalloc(addr_type + num_byte, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	if (addr_type == CAMERA_SENSOR_I2C_TYPE_BYTE) {
+		buf[0] = addr;
+	} else if (addr_type == CAMERA_SENSOR_I2C_TYPE_WORD) {
+		buf[0] = addr >> BITS_PER_BYTE;
+		buf[1] = addr;
+	} else if (addr_type == CAMERA_SENSOR_I2C_TYPE_3B) {
+		buf[0] = addr >> 16;
+		buf[1] = addr >> 8;
+		buf[2] = addr;
+	} else {
+		buf[0] = addr >> 24;
+		buf[1] = addr >> 16;
+		buf[2] = addr >> 8;
+		buf[3] = addr;
+	}
+
+	rc = cam_qup_i2c_rxdata(client, buf, addr_type, num_byte);
+	if (rc < 0) {
+		pr_err("%s fail\n", __func__);
+		goto read_seq_fail;
+	}
+
+	for (i = 0; i < num_byte; i++)
+		data[i] = buf[i];
+
+read_seq_fail:
+	kfree(buf);
+	buf = NULL;
+	return rc;
+}
+
+static int32_t cam_qup_i2c_compare(struct i2c_client *client,
+	uint32_t addr, uint32_t data, uint16_t data_mask,
+	enum camera_sensor_i2c_type data_type,
+	enum camera_sensor_i2c_type addr_type)
+{
+	int32_t rc;
+	uint32_t reg_data = 0;
+
+	rc = cam_qup_i2c_read(client, addr, &reg_data,
+		addr_type, data_type);
+	if (rc < 0)
+		return rc;
+
+	reg_data = reg_data & 0xFFFF;
+	if (data != (reg_data & ~data_mask))
+		return I2C_COMPARE_MISMATCH;
+
+	return I2C_COMPARE_MATCH;
+}
+
+int32_t cam_qup_i2c_poll(struct i2c_client *client,
+	uint32_t addr, uint16_t data, uint16_t data_mask,
+	enum camera_sensor_i2c_type addr_type,
+	enum camera_sensor_i2c_type data_type,
+	uint32_t delay_ms)
+{
+	int32_t rc = 0;
+	int i = 0;
+
+	if ((delay_ms > MAX_POLL_DELAY_MS) || (delay_ms == 0)) {
+		pr_err("%s:%d invalid delay = %d max_delay = %d\n",
+			__func__, __LINE__, delay_ms, MAX_POLL_DELAY_MS);
+		return -EINVAL;
+	}
+
+	if ((addr_type <= CAMERA_SENSOR_I2C_TYPE_INVALID
+		|| addr_type >= CAMERA_SENSOR_I2C_TYPE_MAX
+		|| data_type <= CAMERA_SENSOR_I2C_TYPE_INVALID
+		|| data_type >= CAMERA_SENSOR_I2C_TYPE_MAX))
+		return -EINVAL;
+
+	for (i = 0; i < delay_ms; i++) {
+		rc = cam_qup_i2c_compare(client,
+			addr, data, data_mask, data_type, addr_type);
+		if (rc == I2C_COMPARE_MATCH)
+			return rc;
+
+		usleep_range(1000, 1010);
+	}
+	/* If rc is MISMATCH then read is successful but poll is failure */
+	if (rc == I2C_COMPARE_MISMATCH)
+		pr_err("%s:%d poll failed rc=%d(non-fatal)\n",
+			__func__, __LINE__, rc);
+	if (rc < 0)
+		pr_err("%s:%d poll failed rc=%d\n", __func__, __LINE__, rc);
+
+	return rc;
+}
+
+static int32_t cam_qup_i2c_write(struct camera_io_master *client,
+	struct cam_sensor_i2c_reg_array *reg_setting,
+	enum camera_sensor_i2c_type addr_type,
+	enum camera_sensor_i2c_type data_type)
+{
+	int32_t rc = 0;
+	unsigned char buf[I2C_REG_MAX_BUF_SIZE];
+	uint8_t len = 0;
+
+	CDBG("%s reg addr = 0x%x data type: %d\n",
+			  __func__, reg_setting->reg_addr, data_type);
+	if (addr_type == CAMERA_SENSOR_I2C_TYPE_BYTE) {
+		buf[0] = reg_setting->reg_addr;
+		CDBG("%s byte %d: 0x%x\n", __func__,
+			len, buf[len]);
+		len = 1;
+	} else if (addr_type == CAMERA_SENSOR_I2C_TYPE_WORD) {
+		buf[0] = reg_setting->reg_addr >> 8;
+		buf[1] = reg_setting->reg_addr;
+		CDBG("%s byte %d: 0x%x\n", __func__,
+			len, buf[len]);
+		CDBG("%s byte %d: 0x%x\n", __func__,
+			len+1, buf[len+1]);
+		len = 2;
+	} else if (addr_type == CAMERA_SENSOR_I2C_TYPE_3B) {
+		buf[0] = reg_setting->reg_addr >> 16;
+		buf[1] = reg_setting->reg_addr >> 8;
+		buf[2] = reg_setting->reg_addr;
+		len = 3;
+	} else if (addr_type == CAMERA_SENSOR_I2C_TYPE_DWORD) {
+		buf[0] = reg_setting->reg_addr >> 24;
+		buf[1] = reg_setting->reg_addr >> 16;
+		buf[2] = reg_setting->reg_addr >> 8;
+		buf[3] = reg_setting->reg_addr;
+		len = 4;
+	} else {
+		pr_err("%s: Invalid I2C addr type\n", __func__);
+		return -EINVAL;
+	}
+
+	CDBG("Data: 0x%x\n", reg_setting->reg_data);
+	if (data_type == CAMERA_SENSOR_I2C_TYPE_BYTE) {
+		buf[len] = reg_setting->reg_data;
+		CDBG("Byte %d: 0x%x\n", len, buf[len]);
+		len += 1;
+	} else if (data_type == CAMERA_SENSOR_I2C_TYPE_WORD) {
+		buf[len] = reg_setting->reg_data >> 8;
+		buf[len+1] = reg_setting->reg_data;
+		CDBG("Byte %d: 0x%x\n", len, buf[len]);
+		CDBG("Byte %d: 0x%x\n", len+1, buf[len+1]);
+		len += 2;
+	} else if (data_type == CAMERA_SENSOR_I2C_TYPE_3B) {
+		buf[len] = reg_setting->reg_data >> 16;
+		buf[len + 1] = reg_setting->reg_data >> 8;
+		buf[len + 2] = reg_setting->reg_data;
+		CDBG("Byte %d: 0x%x\n", len, buf[len]);
+		CDBG("Byte %d: 0x%x\n", len+1, buf[len+1]);
+		CDBG("Byte %d: 0x%x\n", len+2, buf[len+2]);
+		len += 3;
+	} else if (data_type == CAMERA_SENSOR_I2C_TYPE_DWORD) {
+		buf[len] = reg_setting->reg_data >> 24;
+		buf[len + 1] = reg_setting->reg_data >> 16;
+		buf[len + 2] = reg_setting->reg_data >> 8;
+		buf[len + 3] = reg_setting->reg_data;
+		CDBG("Byte %d: 0x%x\n", len, buf[len]);
+		CDBG("Byte %d: 0x%x\n", len+1, buf[len+1]);
+		CDBG("Byte %d: 0x%x\n", len+2, buf[len+2]);
+		CDBG("Byte %d: 0x%x\n", len+3, buf[len+3]);
+		len += 4;
+	} else {
+		pr_err("%s: Invalid Data Type\n", __func__);
+		return -EINVAL;
+	}
+
+	rc = cam_qup_i2c_txdata(client, buf, len);
+	if (rc < 0)
+		pr_err("%s fail\n", __func__);
+	return rc;
+}
+
+int32_t cam_qup_i2c_write_table(struct camera_io_master *client,
+	struct cam_sensor_i2c_reg_setting *write_setting)
+{
+	int i;
+	int32_t rc = -EINVAL;
+	struct cam_sensor_i2c_reg_array *reg_setting;
+
+	if (!client || !write_setting)
+		return rc;
+
+	if ((write_setting->addr_type <= CAMERA_SENSOR_I2C_TYPE_INVALID
+		|| write_setting->addr_type >= CAMERA_SENSOR_I2C_TYPE_MAX
+		|| (write_setting->data_type <= CAMERA_SENSOR_I2C_TYPE_INVALID
+		|| write_setting->data_type >= CAMERA_SENSOR_I2C_TYPE_MAX)))
+		return rc;
+
+	reg_setting = write_setting->reg_setting;
+
+	for (i = 0; i < write_setting->size; i++) {
+		CDBG("%s addr 0x%x data 0x%x\n", __func__,
+			reg_setting->reg_addr, reg_setting->reg_data);
+
+		rc = cam_qup_i2c_write(client, reg_setting,
+			write_setting->addr_type, write_setting->data_type);
+		if (rc < 0)
+			break;
+		reg_setting++;
+	}
+
+	if (write_setting->delay > 20)
+		msleep(write_setting->delay);
+	else if (write_setting->delay)
+		usleep_range(write_setting->delay * 1000, (write_setting->delay
+			* 1000) + 1000);
+
+	return rc;
+}
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_clocks.c b/drivers/media/platform/msm/vidc/msm_vidc_clocks.c
index a52fe05..05af186 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_clocks.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_clocks.c
@@ -371,7 +371,7 @@
 	struct msm_vidc_inst *temp;
 	struct msm_vidc_core *core;
 	unsigned long max_freq, freq_left, ops_left, load, cycles, freq = 0;
-	unsigned long mbs_per_frame;
+	unsigned long mbs_per_second;
 
 	if (!inst || !inst->core) {
 		dprintk(VIDC_ERR, "%s Invalid args\n", __func__);
@@ -394,14 +394,21 @@
 
 	list_for_each_entry(temp, &core->instances, list) {
 
-		mbs_per_frame = msm_dcvs_get_mbs_per_frame(inst);
+		if (!temp ||
+				temp->state < MSM_VIDC_START_DONE ||
+				temp->state >= MSM_VIDC_RELEASE_RESOURCES_DONE)
+			continue;
+
+		mbs_per_second = msm_comm_get_inst_load(temp,
+		LOAD_CALC_NO_QUIRKS);
+
 		cycles = temp->clk_data.entry->vpp_cycles;
-		if (inst->session_type == MSM_VIDC_ENCODER)
+		if (temp->session_type == MSM_VIDC_ENCODER)
 			cycles = temp->flags & VIDC_LOW_POWER ?
-				inst->clk_data.entry->low_power_cycles :
+				temp->clk_data.entry->low_power_cycles :
 				cycles;
 
-		load = cycles * mbs_per_frame;
+		load = cycles * mbs_per_second;
 
 		ops_left = load ? (freq_left / load) : 0;
 		/* Convert remaining operating rate to Q16 format */
@@ -418,7 +425,7 @@
 				ctrl->name, ctrl->default_value, ctrl->val);
 			v4l2_ctrl_modify_range(ctrl, ctrl->minimum,
 				ctrl->val + ops_left, ctrl->step,
-				ctrl->minimum);
+				ctrl->default_value);
 			dprintk(VIDC_DBG,
 				"%s: Updated Range = %lld --> %lld\n",
 				ctrl->name, ctrl->minimum, ctrl->maximum);
diff --git a/drivers/power/supply/qcom/battery.c b/drivers/power/supply/qcom/battery.c
index 4ecf9a5..bf5f952 100644
--- a/drivers/power/supply/qcom/battery.c
+++ b/drivers/power/supply/qcom/battery.c
@@ -39,6 +39,8 @@
 #define PL_VOTER			"PL_VOTER"
 #define RESTRICT_CHG_VOTER		"RESTRICT_CHG_VOTER"
 #define ICL_CHANGE_VOTER		"ICL_CHANGE_VOTER"
+#define PL_INDIRECT_VOTER		"PL_INDIRECT_VOTER"
+#define USBIN_I_VOTER			"USBIN_I_VOTER"
 
 struct pl_data {
 	int			pl_mode;
@@ -53,7 +55,8 @@
 	struct votable		*pl_awake_votable;
 	struct votable		*hvdcp_hw_inov_dis_votable;
 	struct votable		*usb_icl_votable;
-	struct work_struct	status_change_work;
+	struct votable		*pl_enable_votable_indirect;
+	struct delayed_work	status_change_work;
 	struct work_struct	pl_disable_forever_work;
 	struct delayed_work	pl_taper_work;
 	struct power_supply	*main_psy;
@@ -491,6 +494,7 @@
 }
 
 #define ICL_STEP_UA	25000
+#define PL_DELAY_MS     3000
 static int usb_icl_vote_callback(struct votable *votable, void *data,
 			int icl_ua, const char *client)
 {
@@ -512,6 +516,21 @@
 	 */
 	vote(chip->pl_disable_votable, ICL_CHANGE_VOTER, true, 0);
 
+	/*
+	 * if (ICL < 1400)
+	 *	disable parallel charger using USBIN_I_VOTER
+	 * else
+	 *	instead of re-enabling here rely on status_changed_work
+	 *	(triggered via AICL completed or scheduled from here to
+	 *	unvote USBIN_I_VOTER) the status_changed_work enables
+	 *	USBIN_I_VOTER based on settled current.
+	 */
+	if (icl_ua <= 1400000)
+		vote(chip->pl_enable_votable_indirect, USBIN_I_VOTER, false, 0);
+	else
+		schedule_delayed_work(&chip->status_change_work,
+						msecs_to_jiffies(PL_DELAY_MS));
+
 	/* rerun AICL */
 	/* get the settled current */
 	rc = power_supply_get_property(chip->main_psy,
@@ -643,6 +662,16 @@
 	return 0;
 }
 
+static int pl_enable_indirect_vote_callback(struct votable *votable,
+			void *data, int pl_enable, const char *client)
+{
+	struct pl_data *chip = data;
+
+	vote(chip->pl_disable_votable, PL_INDIRECT_VOTER, !pl_enable, 0);
+
+	return 0;
+}
+
 static int pl_awake_vote_callback(struct votable *votable,
 			void *data, int awake, const char *client)
 {
@@ -775,6 +804,42 @@
 	union power_supply_propval pval = {0, };
 	int new_total_settled_ua;
 	int rc;
+	int main_settled_ua;
+	int main_limited;
+	int total_current_ua;
+
+	total_current_ua = get_effective_result_locked(chip->usb_icl_votable);
+
+	/*
+	 * call aicl split only when USBIN_USBIN and enabled
+	 * and if aicl changed
+	 */
+	rc = power_supply_get_property(chip->main_psy,
+			       POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED,
+			       &pval);
+	if (rc < 0) {
+		pr_err("Couldn't get aicl settled value rc=%d\n", rc);
+		return;
+	}
+	main_settled_ua = pval.intval;
+
+	rc = power_supply_get_property(chip->batt_psy,
+			       POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED,
+			       &pval);
+	if (rc < 0) {
+		pr_err("Couldn't get aicl settled value rc=%d\n", rc);
+		return;
+	}
+	main_limited = pval.intval;
+
+	if ((main_limited && (main_settled_ua + chip->pl_settled_ua) < 1400000)
+			|| (main_settled_ua == 0)
+			|| ((total_current_ua >= 0) &&
+				(total_current_ua <= 1400000)))
+		vote(chip->pl_enable_votable_indirect, USBIN_I_VOTER, false, 0);
+	else
+		vote(chip->pl_enable_votable_indirect, USBIN_I_VOTER, true, 0);
+
 
 	if (get_effective_result(chip->pl_disable_votable))
 		return;
@@ -783,17 +848,10 @@
 			|| chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN_EXT) {
 		/*
 		 * call aicl split only when USBIN_USBIN and enabled
-		 * and if aicl changed
+		 * and if settled current has changed by more than 300mA
 		 */
-		rc = power_supply_get_property(chip->main_psy,
-				       POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED,
-				       &pval);
-		if (rc < 0) {
-			pr_err("Couldn't get aicl settled value rc=%d\n", rc);
-			return;
-		}
 
-		new_total_settled_ua = pval.intval + chip->pl_settled_ua;
+		new_total_settled_ua = main_settled_ua + chip->pl_settled_ua;
 		pl_dbg(chip, PR_PARALLEL,
 			"total_settled_ua=%d settled_ua=%d new_total_settled_ua=%d\n",
 			chip->total_settled_ua, pval.intval,
@@ -840,7 +898,7 @@
 static void status_change_work(struct work_struct *work)
 {
 	struct pl_data *chip = container_of(work,
-			struct pl_data, status_change_work);
+			struct pl_data, status_change_work.work);
 
 	if (!chip->main_psy && is_main_available(chip)) {
 		/*
@@ -878,7 +936,7 @@
 	if ((strcmp(psy->desc->name, "parallel") == 0)
 	    || (strcmp(psy->desc->name, "battery") == 0)
 	    || (strcmp(psy->desc->name, "main") == 0))
-		schedule_work(&chip->status_change_work);
+		schedule_delayed_work(&chip->status_change_work, 0);
 
 	return NOTIFY_OK;
 }
@@ -899,7 +957,7 @@
 
 static int pl_determine_initial_status(struct pl_data *chip)
 {
-	status_change_work(&chip->status_change_work);
+	status_change_work(&chip->status_change_work.work);
 	return 0;
 }
 
@@ -968,7 +1026,18 @@
 		goto destroy_votable;
 	}
 
-	INIT_WORK(&chip->status_change_work, status_change_work);
+	chip->pl_enable_votable_indirect = create_votable("PL_ENABLE_INDIRECT",
+					VOTE_SET_ANY,
+					pl_enable_indirect_vote_callback,
+					chip);
+	if (IS_ERR(chip->pl_enable_votable_indirect)) {
+		rc = PTR_ERR(chip->pl_enable_votable_indirect);
+		return rc;
+	}
+
+	vote(chip->pl_disable_votable, PL_INDIRECT_VOTER, true, 0);
+
+	INIT_DELAYED_WORK(&chip->status_change_work, status_change_work);
 	INIT_DELAYED_WORK(&chip->pl_taper_work, pl_taper_work);
 	INIT_WORK(&chip->pl_disable_forever_work, pl_disable_forever_work);
 
@@ -1001,6 +1070,7 @@
 unreg_notifier:
 	power_supply_unreg_notifier(&chip->nb);
 destroy_votable:
+	destroy_votable(chip->pl_enable_votable_indirect);
 	destroy_votable(chip->pl_awake_votable);
 	destroy_votable(chip->pl_disable_votable);
 	destroy_votable(chip->fv_votable);
@@ -1020,11 +1090,12 @@
 	if (chip == NULL)
 		return;
 
-	cancel_work_sync(&chip->status_change_work);
+	cancel_delayed_work_sync(&chip->status_change_work);
 	cancel_delayed_work_sync(&chip->pl_taper_work);
 	cancel_work_sync(&chip->pl_disable_forever_work);
 
 	power_supply_unreg_notifier(&chip->nb);
+	destroy_votable(chip->pl_enable_votable_indirect);
 	destroy_votable(chip->pl_awake_votable);
 	destroy_votable(chip->pl_disable_votable);
 	destroy_votable(chip->fv_votable);
diff --git a/drivers/power/supply/qcom/qpnp-smb2.c b/drivers/power/supply/qcom/qpnp-smb2.c
index 24dcbde..7a6f2ea 100644
--- a/drivers/power/supply/qcom/qpnp-smb2.c
+++ b/drivers/power/supply/qcom/qpnp-smb2.c
@@ -266,8 +266,9 @@
 	debug_mask, __debug_mask, int, 0600
 );
 
-#define MICRO_1P5A	1500000
-#define MICRO_P1A	100000
+#define MICRO_1P5A		1500000
+#define MICRO_P1A		100000
+#define OTG_DEFAULT_DEGLITCH_TIME_MS	50
 static int smb2_parse_dt(struct smb2 *chip)
 {
 	struct smb_charger *chg = &chip->chg;
@@ -397,6 +398,12 @@
 
 	chg->suspend_input_on_debug_batt = of_property_read_bool(node,
 					"qcom,suspend-input-on-debug-batt");
+
+	rc = of_property_read_u32(node, "qcom,otg-deglitch-time-ms",
+					&chg->otg_delay_ms);
+	if (rc < 0)
+		chg->otg_delay_ms = OTG_DEFAULT_DEGLITCH_TIME_MS;
+
 	return 0;
 }
 
@@ -1040,7 +1047,8 @@
 		rc = smblib_get_prop_batt_voltage_now(chg, val);
 		break;
 	case POWER_SUPPLY_PROP_VOLTAGE_MAX:
-		val->intval = get_client_vote(chg->fv_votable, DEFAULT_VOTER);
+		val->intval = get_client_vote(chg->fv_votable,
+				BATT_PROFILE_VOTER);
 		break;
 	case POWER_SUPPLY_PROP_CHARGE_QNOVO_ENABLE:
 		rc = smblib_get_prop_charge_qnovo_enable(chg, val);
@@ -1058,7 +1066,7 @@
 		break;
 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
 		val->intval = get_client_vote(chg->fcc_votable,
-					      DEFAULT_VOTER);
+					      BATT_PROFILE_VOTER);
 		break;
 	case POWER_SUPPLY_PROP_TEMP:
 		rc = smblib_get_prop_batt_temp(chg, val);
diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c
index 69d509e..20ff26a 100644
--- a/drivers/power/supply/qcom/smb-lib.c
+++ b/drivers/power/supply/qcom/smb-lib.c
@@ -1046,16 +1046,6 @@
 	return 0;
 }
 
-static int smblib_pl_enable_indirect_vote_callback(struct votable *votable,
-			void *data, int chg_enable, const char *client)
-{
-	struct smb_charger *chg = data;
-
-	vote(chg->pl_disable_votable, PL_INDIRECT_VOTER, !chg_enable, 0);
-
-	return 0;
-}
-
 static int smblib_hvdcp_enable_vote_callback(struct votable *votable,
 			void *data,
 			int hvdcp_enable, const char *client)
@@ -1621,6 +1611,7 @@
 {
 	union power_supply_propval pval;
 	int rc;
+	int effective_fv_uv;
 	u8 stat;
 
 	rc = smblib_read(chg, BATTERY_CHARGER_STATUS_2_REG, &stat);
@@ -1639,10 +1630,11 @@
 			 * If Vbatt is within 40mV above Vfloat, then don't
 			 * treat it as overvoltage.
 			 */
-			if (pval.intval >=
-				get_effective_result(chg->fv_votable) + 40000) {
+			effective_fv_uv = get_effective_result(chg->fv_votable);
+			if (pval.intval >= effective_fv_uv + 40000) {
 				val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
-				smblib_err(chg, "battery over-voltage\n");
+				smblib_err(chg, "battery over-voltage vbat_fg = %duV, fv = %duV\n",
+						pval.intval, effective_fv_uv);
 				goto done;
 			}
 		}
@@ -3730,26 +3722,6 @@
 		   smblib_typec_mode_name[pval.intval]);
 }
 
-irqreturn_t smblib_handle_usb_typec_change_for_uusb(struct smb_charger *chg)
-{
-	int rc;
-	u8 stat;
-
-	rc = smblib_read(chg, TYPE_C_STATUS_3_REG, &stat);
-	if (rc < 0) {
-		smblib_err(chg, "Couldn't read TYPE_C_STATUS_3 rc=%d\n", rc);
-		return IRQ_HANDLED;
-	}
-	smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_3 = 0x%02x OTG=%d\n",
-		stat, !!(stat & (U_USB_GND_NOVBUS_BIT | U_USB_GND_BIT)));
-
-	extcon_set_cable_state_(chg->extcon, EXTCON_USB_HOST,
-			!!(stat & (U_USB_GND_NOVBUS_BIT | U_USB_GND_BIT)));
-	power_supply_changed(chg->usb_psy);
-
-	return IRQ_HANDLED;
-}
-
 static void smblib_usb_typec_change(struct smb_charger *chg)
 {
 	int rc;
@@ -3784,7 +3756,11 @@
 	struct smb_charger *chg = irq_data->parent_data;
 
 	if (chg->micro_usb_mode) {
-		smblib_handle_usb_typec_change_for_uusb(chg);
+		cancel_delayed_work_sync(&chg->uusb_otg_work);
+		vote(chg->awake_votable, OTG_DELAY_VOTER, true, 0);
+		smblib_dbg(chg, PR_INTERRUPT, "Scheduling OTG work\n");
+		schedule_delayed_work(&chg->uusb_otg_work,
+				msecs_to_jiffies(chg->otg_delay_ms));
 		return IRQ_HANDLED;
 	}
 
@@ -3869,6 +3845,30 @@
 /***************
  * Work Queues *
  ***************/
+static void smblib_uusb_otg_work(struct work_struct *work)
+{
+	struct smb_charger *chg = container_of(work, struct smb_charger,
+						uusb_otg_work.work);
+	int rc;
+	u8 stat;
+	bool otg;
+
+	rc = smblib_read(chg, TYPE_C_STATUS_3_REG, &stat);
+	if (rc < 0) {
+		smblib_err(chg, "Couldn't read TYPE_C_STATUS_3 rc=%d\n", rc);
+		goto out;
+	}
+
+	otg = !!(stat & (U_USB_GND_NOVBUS_BIT | U_USB_GND_BIT));
+	extcon_set_cable_state_(chg->extcon, EXTCON_USB_HOST, otg);
+	smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_3 = 0x%02x OTG=%d\n",
+			stat, otg);
+	power_supply_changed(chg->usb_psy);
+
+out:
+	vote(chg->awake_votable, OTG_DELAY_VOTER, false, 0);
+}
+
 
 static void smblib_hvdcp_detect_work(struct work_struct *work)
 {
@@ -4177,8 +4177,6 @@
 	}
 
 	power_supply_changed(chg->usb_main_psy);
-	vote(chg->pl_enable_votable_indirect, USBIN_I_VOTER,
-				settled_ua >= USB_WEAK_INPUT_UA, 0);
 
 	smblib_dbg(chg, PR_INTERRUPT, "icl_settled=%d\n", settled_ua);
 }
@@ -4274,7 +4272,16 @@
 		smblib_err(chg, "Couldn't find votable PL_DISABLE rc=%d\n", rc);
 		return rc;
 	}
-	vote(chg->pl_disable_votable, PL_INDIRECT_VOTER, true, 0);
+
+	chg->pl_enable_votable_indirect = find_votable("PL_ENABLE_INDIRECT");
+	if (chg->pl_enable_votable_indirect == NULL) {
+		rc = -EINVAL;
+		smblib_err(chg,
+			"Couldn't find votable PL_ENABLE_INDIRECT rc=%d\n",
+			rc);
+		return rc;
+	}
+
 	vote(chg->pl_disable_votable, PL_DELAY_VOTER, true, 0);
 
 	chg->dc_suspend_votable = create_votable("DC_SUSPEND", VOTE_SET_ANY,
@@ -4324,14 +4331,6 @@
 		return rc;
 	}
 
-	chg->pl_enable_votable_indirect = create_votable("PL_ENABLE_INDIRECT",
-					VOTE_SET_ANY,
-					smblib_pl_enable_indirect_vote_callback,
-					chg);
-	if (IS_ERR(chg->pl_enable_votable_indirect)) {
-		rc = PTR_ERR(chg->pl_enable_votable_indirect);
-		return rc;
-	}
 
 	chg->hvdcp_disable_votable_indirect = create_votable(
 				"HVDCP_DISABLE_INDIRECT",
@@ -4407,8 +4406,6 @@
 		destroy_votable(chg->awake_votable);
 	if (chg->chg_disable_votable)
 		destroy_votable(chg->chg_disable_votable);
-	if (chg->pl_enable_votable_indirect)
-		destroy_votable(chg->pl_enable_votable_indirect);
 	if (chg->apsd_disable_votable)
 		destroy_votable(chg->apsd_disable_votable);
 	if (chg->hvdcp_hw_inov_dis_votable)
@@ -4450,6 +4447,7 @@
 	INIT_DELAYED_WORK(&chg->icl_change_work, smblib_icl_change_work);
 	INIT_DELAYED_WORK(&chg->pl_enable_work, smblib_pl_enable_work);
 	INIT_WORK(&chg->legacy_detection_work, smblib_legacy_detection_work);
+	INIT_DELAYED_WORK(&chg->uusb_otg_work, smblib_uusb_otg_work);
 	chg->fake_capacity = -EINVAL;
 	chg->fake_input_current_limited = -EINVAL;
 
@@ -4504,6 +4502,7 @@
 		cancel_delayed_work_sync(&chg->icl_change_work);
 		cancel_delayed_work_sync(&chg->pl_enable_work);
 		cancel_work_sync(&chg->legacy_detection_work);
+		cancel_delayed_work_sync(&chg->uusb_otg_work);
 		power_supply_unreg_notifier(&chg->nb);
 		smblib_destroy_votables(chg);
 		qcom_batt_deinit();
diff --git a/drivers/power/supply/qcom/smb-lib.h b/drivers/power/supply/qcom/smb-lib.h
index 398513b..0c8e661 100644
--- a/drivers/power/supply/qcom/smb-lib.h
+++ b/drivers/power/supply/qcom/smb-lib.h
@@ -36,9 +36,7 @@
 #define PL_USBIN_USBIN_VOTER		"PL_USBIN_USBIN_VOTER"
 #define USB_PSY_VOTER			"USB_PSY_VOTER"
 #define PL_TAPER_WORK_RUNNING_VOTER	"PL_TAPER_WORK_RUNNING_VOTER"
-#define PL_INDIRECT_VOTER		"PL_INDIRECT_VOTER"
 #define PL_QNOVO_VOTER			"PL_QNOVO_VOTER"
-#define USBIN_I_VOTER			"USBIN_I_VOTER"
 #define USBIN_V_VOTER			"USBIN_V_VOTER"
 #define CHG_STATE_VOTER			"CHG_STATE_VOTER"
 #define TYPEC_SRC_VOTER			"TYPEC_SRC_VOTER"
@@ -64,6 +62,8 @@
 #define CC2_WA_VOTER			"CC2_WA_VOTER"
 #define QNOVO_VOTER			"QNOVO_VOTER"
 #define BATT_PROFILE_VOTER		"BATT_PROFILE_VOTER"
+#define OTG_DELAY_VOTER			"OTG_DELAY_VOTER"
+#define USBIN_I_VOTER			"USBIN_I_VOTER"
 
 #define VCONN_MAX_ATTEMPTS	3
 #define OTG_MAX_ATTEMPTS	3
@@ -229,6 +229,7 @@
 	enum smb_mode		mode;
 	struct smb_chg_freq	chg_freq;
 	int			smb_version;
+	int			otg_delay_ms;
 
 	/* locks */
 	struct mutex		lock;
@@ -290,6 +291,7 @@
 	struct delayed_work	icl_change_work;
 	struct delayed_work	pl_enable_work;
 	struct work_struct	legacy_detection_work;
+	struct delayed_work	uusb_otg_work;
 
 	/* cached status */
 	int			voltage_min_uv;
diff --git a/drivers/soc/qcom/glink_smem_native_xprt.c b/drivers/soc/qcom/glink_smem_native_xprt.c
index 3c4759c..453faa8 100644
--- a/drivers/soc/qcom/glink_smem_native_xprt.c
+++ b/drivers/soc/qcom/glink_smem_native_xprt.c
@@ -702,7 +702,8 @@
 		err = true;
 	} else if (intent->data == NULL) {
 		if (einfo->intentless) {
-			intent->data = kmalloc(cmd.frag_size, GFP_ATOMIC);
+			intent->data = kmalloc(cmd.frag_size,
+						__GFP_ATOMIC | __GFP_HIGH);
 			if (!intent->data) {
 				err = true;
 				GLINK_ERR(
diff --git a/drivers/tty/serial/msm_geni_serial.c b/drivers/tty/serial/msm_geni_serial.c
index 2e12c3f..94ba2c3e 100644
--- a/drivers/tty/serial/msm_geni_serial.c
+++ b/drivers/tty/serial/msm_geni_serial.c
@@ -327,6 +327,11 @@
 	mb();
 }
 
+static unsigned int msm_geni_cons_get_mctrl(struct uart_port *uport)
+{
+	return TIOCM_DSR | TIOCM_CAR | TIOCM_CTS;
+}
+
 static unsigned int msm_geni_serial_get_mctrl(struct uart_port *uport)
 {
 	u32 geni_ios = 0;
@@ -610,6 +615,8 @@
 {
 	struct uart_port *uport;
 	struct msm_geni_serial_port *port;
+	int locked = 1;
+	unsigned long flags;
 
 	WARN_ON(co->index < 0 || co->index >= GENI_UART_NR_PORTS);
 
@@ -618,9 +625,15 @@
 		return;
 
 	uport = &port->uport;
-	spin_lock(&uport->lock);
-	__msm_geni_serial_console_write(uport, s, count);
-	spin_unlock(&uport->lock);
+	if (oops_in_progress)
+		locked = spin_trylock_irqsave(&uport->lock, flags);
+	else
+		spin_lock_irqsave(&uport->lock, flags);
+
+	if (locked) {
+		__msm_geni_serial_console_write(uport, s, count);
+		spin_unlock_irqrestore(&uport->lock, flags);
+	}
 }
 
 static int handle_rx_console(struct uart_port *uport,
@@ -1014,13 +1027,20 @@
 static void msm_geni_serial_shutdown(struct uart_port *uport)
 {
 	struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport);
+	unsigned long flags;
 
+	/* Stop the console before stopping the current tx */
+	if (uart_console(uport))
+		console_stop(uport->cons);
+
+	spin_lock_irqsave(&uport->lock, flags);
 	msm_geni_serial_stop_tx(uport);
 	msm_geni_serial_stop_rx(uport);
+	spin_unlock_irqrestore(&uport->lock, flags);
+
 	disable_irq(uport->irq);
 	free_irq(uport->irq, msm_port);
 	if (uart_console(uport)) {
-		console_stop(uport->cons);
 		se_geni_resources_off(&msm_port->serial_rsc);
 	} else {
 		if (msm_port->wakeup_irq > 0) {
@@ -1572,6 +1592,7 @@
 	.shutdown = msm_geni_serial_shutdown,
 	.type = msm_geni_serial_get_type,
 	.set_mctrl = msm_geni_cons_set_mctrl,
+	.get_mctrl = msm_geni_cons_get_mctrl,
 #ifdef CONFIG_CONSOLE_POLL
 	.poll_get_char	= msm_geni_serial_get_char,
 	.poll_put_char	= msm_geni_serial_poll_put_char,
diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h
index 0e1e050..cf86f52 100644
--- a/include/linux/device-mapper.h
+++ b/include/linux/device-mapper.h
@@ -440,6 +440,13 @@
 
 struct queue_limits *dm_get_queue_limits(struct mapped_device *md);
 
+void dm_lock_md_type(struct mapped_device *md);
+void dm_unlock_md_type(struct mapped_device *md);
+void dm_set_md_type(struct mapped_device *md, unsigned type);
+unsigned dm_get_md_type(struct mapped_device *md);
+int dm_setup_md_queue(struct mapped_device *md, struct dm_table *t);
+unsigned dm_table_get_type(struct dm_table *t);
+
 /*
  * Geometry functions.
  */
diff --git a/init/do_mounts_dm.c b/init/do_mounts_dm.c
index a557c5e..7760705 100644
--- a/init/do_mounts_dm.c
+++ b/init/do_mounts_dm.c
@@ -5,13 +5,17 @@
  *
  * This file is released under the GPL.
  */
+#include <linux/async.h>
+#include <linux/ctype.h>
 #include <linux/device-mapper.h>
 #include <linux/fs.h>
 #include <linux/string.h>
+#include <linux/delay.h>
 
 #include "do_mounts.h"
-#include "../drivers/md/dm.h"
 
+#define DM_MAX_DEVICES 256
+#define DM_MAX_TARGETS 256
 #define DM_MAX_NAME 32
 #define DM_MAX_UUID 129
 #define DM_NO_UUID "none"
@@ -19,14 +23,47 @@
 #define DM_MSG_PREFIX "init"
 
 /* Separators used for parsing the dm= argument. */
-#define DM_FIELD_SEP ' '
-#define DM_LINE_SEP ','
+#define DM_FIELD_SEP " "
+#define DM_LINE_SEP ","
+#define DM_ANY_SEP DM_FIELD_SEP DM_LINE_SEP
 
 /*
  * When the device-mapper and any targets are compiled into the kernel
- * (not a module), one target may be created and used as the root device at
- * boot time with the parameters given with the boot line dm=...
- * The code for that is here.
+ * (not a module), one or more device-mappers may be created and used
+ * as the root device at boot time with the parameters given with the
+ * boot line dm=...
+ *
+ * Multiple device-mappers can be stacked specifing the number of
+ * devices. A device can have multiple targets if the the number of
+ * targets is specified.
+ *
+ * TODO(taysom:defect 32847)
+ * In the future, the <num> field will be mandatory.
+ *
+ * <device>        ::= [<num>] <device-mapper>+
+ * <device-mapper> ::= <head> "," <target>+
+ * <head>          ::= <name> <uuid> <mode> [<num>]
+ * <target>        ::= <start> <length> <type> <options> ","
+ * <mode>          ::= "ro" | "rw"
+ * <uuid>          ::= xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | "none"
+ * <type>          ::= "verity" | "bootcache" | ...
+ *
+ * Example:
+ * 2 vboot none ro 1,
+ *     0 1768000 bootcache
+ *       device=aa55b119-2a47-8c45-946a-5ac57765011f+1
+ *       signature=76e9be054b15884a9fa85973e9cb274c93afadb6
+ *       cache_start=1768000 max_blocks=100000 size_limit=23 max_trace=20000,
+ *   vroot none ro 1,
+ *     0 1740800 verity payload=254:0 hashtree=254:0 hashstart=1740800 alg=sha1
+ *       root_hexdigest=76e9be054b15884a9fa85973e9cb274c93afadb6
+ *       salt=5b3549d54d6c7a3837b9b81ed72e49463a64c03680c47835bef94d768e5646fe
+ *
+ * Notes:
+ *  1. uuid is a label for the device and we set it to "none".
+ *  2. The <num> field will be optional initially and assumed to be 1.
+ *     Once all the scripts that set these fields have been set, it will
+ *     be made mandatory.
  */
 
 struct dm_setup_target {
@@ -38,381 +75,388 @@
 	struct dm_setup_target *next;
 };
 
-static struct {
+struct dm_device {
 	int minor;
 	int ro;
 	char name[DM_MAX_NAME];
 	char uuid[DM_MAX_UUID];
-	char *targets;
+	unsigned long num_targets;
 	struct dm_setup_target *target;
 	int target_count;
+	struct dm_device *next;
+};
+
+struct dm_option {
+	char *start;
+	char *next;
+	size_t len;
+	char delim;
+};
+
+static struct {
+	unsigned long num_devices;
+	char *str;
 } dm_setup_args __initdata;
 
 static __initdata int dm_early_setup;
 
-static size_t __init get_dm_option(char *str, char **next, char sep)
+static int __init get_dm_option(struct dm_option *opt, const char *accept)
 {
-	size_t len = 0;
-	char *endp = NULL;
+	char *str = opt->next;
+	char *endp;
 
 	if (!str)
 		return 0;
 
-	endp = strchr(str, sep);
+	str = skip_spaces(str);
+	opt->start = str;
+	endp = strpbrk(str, accept);
 	if (!endp) {  /* act like strchrnul */
-		len = strlen(str);
-		endp = str + len;
+		opt->len = strlen(str);
+		endp = str + opt->len;
 	} else {
-		len = endp - str;
+		opt->len = endp - str;
 	}
-
-	if (endp == str)
-		return 0;
-
-	if (!next)
-		return len;
-
+	opt->delim = *endp;
 	if (*endp == 0) {
 		/* Don't advance past the nul. */
-		*next = endp;
+		opt->next = endp;
 	} else {
-		*next = endp + 1;
+		opt->next = endp + 1;
 	}
-	return len;
+	return opt->len != 0;
 }
 
-static int __init dm_setup_args_init(void)
+static int __init dm_setup_cleanup(struct dm_device *devices)
 {
-	dm_setup_args.minor = 0;
-	dm_setup_args.ro = 0;
-	dm_setup_args.target = NULL;
-	dm_setup_args.target_count = 0;
+	struct dm_device *dev = devices;
+
+	while (dev) {
+		struct dm_device *old_dev = dev;
+		struct dm_setup_target *target = dev->target;
+		while (target) {
+			struct dm_setup_target *old_target = target;
+			kfree(target->type);
+			kfree(target->params);
+			target = target->next;
+			kfree(old_target);
+			dev->target_count--;
+		}
+		BUG_ON(dev->target_count);
+		dev = dev->next;
+		kfree(old_dev);
+	}
 	return 0;
 }
 
-static int __init dm_setup_cleanup(void)
+static char * __init dm_parse_device(struct dm_device *dev, char *str)
 {
-	struct dm_setup_target *target = dm_setup_args.target;
-	struct dm_setup_target *old_target = NULL;
-	while (target) {
-		kfree(target->type);
-		kfree(target->params);
-		old_target = target;
-		target = target->next;
-		kfree(old_target);
-		dm_setup_args.target_count--;
-	}
-	BUG_ON(dm_setup_args.target_count);
-	return 0;
-}
-
-static char * __init dm_setup_parse_device_args(char *str)
-{
-	char *next = NULL;
-	size_t len = 0;
+	struct dm_option opt;
+	size_t len;
 
 	/* Grab the logical name of the device to be exported to udev */
-	len = get_dm_option(str, &next, DM_FIELD_SEP);
-	if (!len) {
+	opt.next = str;
+	if (!get_dm_option(&opt, DM_FIELD_SEP)) {
 		DMERR("failed to parse device name");
 		goto parse_fail;
 	}
-	len = min(len + 1, sizeof(dm_setup_args.name));
-	strlcpy(dm_setup_args.name, str, len);  /* includes nul */
-	str = skip_spaces(next);
+	len = min(opt.len + 1, sizeof(dev->name));
+	strlcpy(dev->name, opt.start, len);  /* includes nul */
 
 	/* Grab the UUID value or "none" */
-	len = get_dm_option(str, &next, DM_FIELD_SEP);
-	if (!len) {
+	if (!get_dm_option(&opt, DM_FIELD_SEP)) {
 		DMERR("failed to parse device uuid");
 		goto parse_fail;
 	}
-	len = min(len + 1, sizeof(dm_setup_args.uuid));
-	strlcpy(dm_setup_args.uuid, str, len);
-	str = skip_spaces(next);
+	len = min(opt.len + 1, sizeof(dev->uuid));
+	strlcpy(dev->uuid, opt.start, len);
 
 	/* Determine if the table/device will be read only or read-write */
-	if (!strncmp("ro,", str, 3)) {
-		dm_setup_args.ro = 1;
-	} else if (!strncmp("rw,", str, 3)) {
-		dm_setup_args.ro = 0;
+	get_dm_option(&opt, DM_ANY_SEP);
+	if (!strncmp("ro", opt.start, opt.len)) {
+		dev->ro = 1;
+	} else if (!strncmp("rw", opt.start, opt.len)) {
+		dev->ro = 0;
 	} else {
 		DMERR("failed to parse table mode");
 		goto parse_fail;
 	}
-	str = skip_spaces(str + 3);
 
-	return str;
+	/* Optional number field */
+	/* XXX: The <num> field will be mandatory in the next round */
+	if (opt.delim == DM_FIELD_SEP[0]) {
+		if (!get_dm_option(&opt, DM_LINE_SEP))
+			return NULL;
+		dev->num_targets = simple_strtoul(opt.start, NULL, 10);
+	} else {
+		dev->num_targets = 1;
+	}
+	if (dev->num_targets > DM_MAX_TARGETS) {
+		DMERR("too many targets %lu > %d",
+			dev->num_targets, DM_MAX_TARGETS);
+	}
+	return opt.next;
 
 parse_fail:
 	return NULL;
 }
 
-static void __init dm_substitute_devices(char *str, size_t str_len)
+static char * __init dm_parse_targets(struct dm_device *dev, char *str)
 {
-	char *candidate = str;
-	char *candidate_end = str;
-	char old_char;
-	size_t len = 0;
-	dev_t dev;
-
-	if (str_len < 3)
-		return;
-
-	while (str && *str) {
-		candidate = strchr(str, '/');
-		if (!candidate)
-			break;
-
-		/* Avoid embedded slashes */
-		if (candidate != str && *(candidate - 1) != DM_FIELD_SEP) {
-			str = strchr(candidate, DM_FIELD_SEP);
-			continue;
-		}
-
-		len = get_dm_option(candidate, &candidate_end, DM_FIELD_SEP);
-		str = skip_spaces(candidate_end);
-		if (len < 3 || len > 37)  /* name_to_dev_t max; maj:mix min */
-			continue;
-
-		/* Temporarily terminate with a nul */
-		if (*candidate_end)
-			candidate_end--;
-		old_char = *candidate_end;
-		*candidate_end = '\0';
-
-		DMDEBUG("converting candidate device '%s' to dev_t", candidate);
-		/* Use the boot-time specific device naming */
-		dev = name_to_dev_t(candidate);
-		*candidate_end = old_char;
-
-		DMDEBUG(" -> %u", dev);
-		/* No suitable replacement found */
-		if (!dev)
-			continue;
-
-		/* Rewrite the /dev/path as a major:minor */
-		len = snprintf(candidate, len, "%u:%u", MAJOR(dev), MINOR(dev));
-		if (!len) {
-			DMERR("error substituting device major/minor.");
-			break;
-		}
-		candidate += len;
-		/* Pad out with spaces (fixing our nul) */
-		while (candidate < candidate_end)
-			*(candidate++) = DM_FIELD_SEP;
-	}
-}
-
-static int __init dm_setup_parse_targets(char *str)
-{
-	char *next = NULL;
-	size_t len = 0;
-	struct dm_setup_target **target = NULL;
+	struct dm_option opt;
+	struct dm_setup_target **target = &dev->target;
+	unsigned long num_targets = dev->num_targets;
+	unsigned long i;
 
 	/* Targets are defined as per the table format but with a
 	 * comma as a newline separator. */
-	target = &dm_setup_args.target;
-	while (str && *str) {
+	opt.next = str;
+	for (i = 0; i < num_targets; i++) {
 		*target = kzalloc(sizeof(struct dm_setup_target), GFP_KERNEL);
 		if (!*target) {
-			DMERR("failed to allocate memory for target %d",
-			      dm_setup_args.target_count);
+			DMERR("failed to allocate memory for target %s<%ld>",
+				dev->name, i);
 			goto parse_fail;
 		}
-		dm_setup_args.target_count++;
+		dev->target_count++;
 
-		(*target)->begin = simple_strtoull(str, &next, 10);
-		if (!next || *next != DM_FIELD_SEP) {
-			DMERR("failed to parse starting sector for target %d",
-			      dm_setup_args.target_count - 1);
+		if (!get_dm_option(&opt, DM_FIELD_SEP)) {
+			DMERR("failed to parse starting sector"
+				" for target %s<%ld>", dev->name, i);
 			goto parse_fail;
 		}
-		str = skip_spaces(next + 1);
+		(*target)->begin = simple_strtoull(opt.start, NULL, 10);
 
-		(*target)->length = simple_strtoull(str, &next, 10);
-		if (!next || *next != DM_FIELD_SEP) {
-			DMERR("failed to parse length for target %d",
-			      dm_setup_args.target_count - 1);
+		if (!get_dm_option(&opt, DM_FIELD_SEP)) {
+			DMERR("failed to parse length for target %s<%ld>",
+				dev->name, i);
 			goto parse_fail;
 		}
-		str = skip_spaces(next + 1);
+		(*target)->length = simple_strtoull(opt.start, NULL, 10);
 
-		len = get_dm_option(str, &next, DM_FIELD_SEP);
-		if (!len ||
-		    !((*target)->type = kstrndup(str, len, GFP_KERNEL))) {
-			DMERR("failed to parse type for target %d",
-			      dm_setup_args.target_count - 1);
+		if (get_dm_option(&opt, DM_FIELD_SEP))
+			(*target)->type = kstrndup(opt.start, opt.len,
+							GFP_KERNEL);
+		if (!((*target)->type)) {
+			DMERR("failed to parse type for target %s<%ld>",
+				dev->name, i);
 			goto parse_fail;
 		}
-		str = skip_spaces(next);
-
-		len = get_dm_option(str, &next, DM_LINE_SEP);
-		if (!len ||
-		    !((*target)->params = kstrndup(str, len, GFP_KERNEL))) {
-			DMERR("failed to parse params for target %d",
-			      dm_setup_args.target_count - 1);
+		if (get_dm_option(&opt, DM_LINE_SEP))
+			(*target)->params = kstrndup(opt.start, opt.len,
+							GFP_KERNEL);
+		if (!((*target)->params)) {
+			DMERR("failed to parse params for target %s<%ld>",
+				dev->name, i);
 			goto parse_fail;
 		}
-		str = skip_spaces(next);
-
-		/* Before moving on, walk through the copied target and
-		 * attempt to replace all /dev/xxx with the major:minor number.
-		 * It may not be possible to resolve them traditionally at
-		 * boot-time. */
-		dm_substitute_devices((*target)->params, len);
-
 		target = &((*target)->next);
 	}
-	DMDEBUG("parsed %d targets", dm_setup_args.target_count);
+	DMDEBUG("parsed %d targets", dev->target_count);
 
-	return 0;
+	return opt.next;
 
 parse_fail:
-	return 1;
+	return NULL;
+}
+
+static struct dm_device * __init dm_parse_args(void)
+{
+	struct dm_device *devices = NULL;
+	struct dm_device **tail = &devices;
+	struct dm_device *dev;
+	char *str = dm_setup_args.str;
+	unsigned long num_devices = dm_setup_args.num_devices;
+	unsigned long i;
+
+	if (!str)
+		return NULL;
+	for (i = 0; i < num_devices; i++) {
+		dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+		if (!dev) {
+			DMERR("failed to allocated memory for dev");
+			goto error;
+		}
+		*tail = dev;
+		tail = &dev->next;
+		/*
+		 * devices are given minor numbers 0 - n-1
+		 * in the order they are found in the arg
+		 * string.
+		 */
+		dev->minor = i;
+		str = dm_parse_device(dev, str);
+		if (!str)	/* NULL indicates error in parsing, bail */
+			goto error;
+
+		str = dm_parse_targets(dev, str);
+		if (!str)
+			goto error;
+	}
+	return devices;
+error:
+	dm_setup_cleanup(devices);
+	return NULL;
 }
 
 /*
  * Parse the command-line parameters given our kernel, but do not
  * actually try to invoke the DM device now; that is handled by
- * dm_setup_drive after the low-level disk drivers have initialised.
- * dm format is as follows:
- *  dm="name uuid fmode,[table line 1],[table line 2],..."
- * May be used with root=/dev/dm-0 as it always uses the first dm minor.
+ * dm_setup_drives after the low-level disk drivers have initialised.
+ * dm format is described at the top of the file.
+ *
+ * Because dm minor numbers are assigned in assending order starting with 0,
+ * You can assume the first device is /dev/dm-0, the next device is /dev/dm-1,
+ * and so forth.
  */
-
 static int __init dm_setup(char *str)
 {
-	dm_setup_args_init();
+	struct dm_option opt;
+	unsigned long num_devices;
 
-	str = dm_setup_parse_device_args(str);
 	if (!str) {
 		DMDEBUG("str is NULL");
 		goto parse_fail;
 	}
-
-	/* Target parsing is delayed until we have dynamic memory */
-	dm_setup_args.targets = str;
-
-	printk(KERN_INFO "dm: will configure '%s' on dm-%d\n",
-	       dm_setup_args.name, dm_setup_args.minor);
-
+	opt.next = str;
+	if (!get_dm_option(&opt, DM_FIELD_SEP))
+		goto parse_fail;
+	if (isdigit(opt.start[0])) {	/* XXX: Optional number field */
+		num_devices = simple_strtoul(opt.start, NULL, 10);
+		str = opt.next;
+	} else {
+		num_devices = 1;
+		/* Don't advance str */
+	}
+	if (num_devices > DM_MAX_DEVICES) {
+		DMDEBUG("too many devices %lu > %d",
+			num_devices, DM_MAX_DEVICES);
+	}
+	dm_setup_args.str = str;
+	dm_setup_args.num_devices = num_devices;
+	DMINFO("will configure %lu devices", num_devices);
 	dm_early_setup = 1;
 	return 1;
 
 parse_fail:
-	printk(KERN_WARNING "dm: Invalid arguments supplied to dm=.\n");
+	DMWARN("Invalid arguments supplied to dm=.");
 	return 0;
 }
 
-
-static void __init dm_setup_drive(void)
+static void __init dm_setup_drives(void)
 {
 	struct mapped_device *md = NULL;
 	struct dm_table *table = NULL;
 	struct dm_setup_target *target;
-	char *uuid = dm_setup_args.uuid;
+	struct dm_device *dev;
+	char *uuid = NULL;
 	fmode_t fmode = FMODE_READ;
+	struct dm_device *devices;
 
-	/* Finish parsing the targets. */
-	if (dm_setup_parse_targets(dm_setup_args.targets))
-		goto parse_fail;
+	devices = dm_parse_args();
 
-	if (dm_create(dm_setup_args.minor, &md)) {
-		DMDEBUG("failed to create the device");
-		goto dm_create_fail;
-	}
-	DMDEBUG("created device '%s'", dm_device_name(md));
-
-	/* In addition to flagging the table below, the disk must be
-	 * set explicitly ro/rw. */
-	set_disk_ro(dm_disk(md), dm_setup_args.ro);
-
-	if (!dm_setup_args.ro)
-		fmode |= FMODE_WRITE;
-	if (dm_table_create(&table, fmode, dm_setup_args.target_count, md)) {
-		DMDEBUG("failed to create the table");
-		goto dm_table_create_fail;
-	}
-
-	dm_lock_md_type(md);
-	target = dm_setup_args.target;
-	while (target) {
-		DMINFO("adding target '%llu %llu %s %s'",
-		       (unsigned long long) target->begin,
-		       (unsigned long long) target->length, target->type,
-		       target->params);
-		if (dm_table_add_target(table, target->type, target->begin,
-					target->length, target->params)) {
-			DMDEBUG("failed to add the target to the table");
-			goto add_target_fail;
+	for (dev = devices; dev; dev = dev->next) {
+		if (dm_create(dev->minor, &md)) {
+			DMDEBUG("failed to create the device");
+			goto dm_create_fail;
 		}
-		target = target->next;
-	}
+		DMDEBUG("created device '%s'", dm_device_name(md));
 
-	if (dm_table_complete(table)) {
-		DMDEBUG("failed to complete the table");
-		goto table_complete_fail;
-	}
+		/*
+		 * In addition to flagging the table below, the disk must be
+		 * set explicitly ro/rw.
+		 */
+		set_disk_ro(dm_disk(md), dev->ro);
 
-	if (dm_get_md_type(md) == DM_TYPE_NONE) {
+		if (!dev->ro)
+			fmode |= FMODE_WRITE;
+		if (dm_table_create(&table, fmode, dev->target_count, md)) {
+			DMDEBUG("failed to create the table");
+			goto dm_table_create_fail;
+		}
+
+		dm_lock_md_type(md);
+
+		for (target = dev->target; target; target = target->next) {
+			DMINFO("adding target '%llu %llu %s %s'",
+			       (unsigned long long) target->begin,
+			       (unsigned long long) target->length,
+			       target->type, target->params);
+			if (dm_table_add_target(table, target->type,
+						target->begin,
+						target->length,
+						target->params)) {
+				DMDEBUG("failed to add the target"
+					" to the table");
+				goto add_target_fail;
+			}
+		}
+		if (dm_table_complete(table)) {
+			DMDEBUG("failed to complete the table");
+			goto table_complete_fail;
+		}
+
+		/* Suspend the device so that we can bind it to the table. */
+		if (dm_suspend(md, 0)) {
+			DMDEBUG("failed to suspend the device pre-bind");
+			goto suspend_fail;
+		}
+
+		/* Initial table load: acquire type of table. */
 		dm_set_md_type(md, dm_table_get_type(table));
+
+		/* Setup md->queue to reflect md's type. */
 		if (dm_setup_md_queue(md, table)) {
 			DMWARN("unable to set up device queue for new table.");
 			goto setup_md_queue_fail;
 		}
-	} else if (dm_get_md_type(md) != dm_table_get_type(table)) {
-		DMWARN("can't change device type after initial table load.");
-		goto setup_md_queue_fail;
-        }
 
-	/* Suspend the device so that we can bind it to the table. */
-	if (dm_suspend(md, 0)) {
-		DMDEBUG("failed to suspend the device pre-bind");
-		goto suspend_fail;
+		/*
+		 * Bind the table to the device. This is the only way
+		 * to associate md->map with the table and set the disk
+		 * capacity directly.
+		 */
+		if (dm_swap_table(md, table)) {  /* should return NULL. */
+			DMDEBUG("failed to bind the device to the table");
+			goto table_bind_fail;
+		}
+
+		/* Finally, resume and the device should be ready. */
+		if (dm_resume(md)) {
+			DMDEBUG("failed to resume the device");
+			goto resume_fail;
+		}
+
+		/* Export the dm device via the ioctl interface */
+		if (!strcmp(DM_NO_UUID, dev->uuid))
+			uuid = NULL;
+		if (dm_ioctl_export(md, dev->name, uuid)) {
+			DMDEBUG("failed to export device with given"
+				" name and uuid");
+			goto export_fail;
+		}
+
+		dm_unlock_md_type(md);
+
+		DMINFO("dm-%d is ready", dev->minor);
 	}
-
-	/* Bind the table to the device. This is the only way to associate
-	 * md->map with the table and set the disk capacity directly. */
-	if (dm_swap_table(md, table)) {  /* should return NULL. */
-		DMDEBUG("failed to bind the device to the table");
-		goto table_bind_fail;
-	}
-
-	/* Finally, resume and the device should be ready. */
-	if (dm_resume(md)) {
-		DMDEBUG("failed to resume the device");
-		goto resume_fail;
-	}
-
-	/* Export the dm device via the ioctl interface */
-	if (!strcmp(DM_NO_UUID, dm_setup_args.uuid))
-		uuid = NULL;
-	if (dm_ioctl_export(md, dm_setup_args.name, uuid)) {
-		DMDEBUG("failed to export device with given name and uuid");
-		goto export_fail;
-	}
-	printk(KERN_INFO "dm: dm-%d is ready\n", dm_setup_args.minor);
-
-	dm_unlock_md_type(md);
-	dm_setup_cleanup();
+	dm_setup_cleanup(devices);
 	return;
 
 export_fail:
 resume_fail:
 table_bind_fail:
-suspend_fail:
 setup_md_queue_fail:
+suspend_fail:
 table_complete_fail:
 add_target_fail:
 	dm_unlock_md_type(md);
 dm_table_create_fail:
 	dm_put(md);
 dm_create_fail:
-	dm_setup_cleanup();
-parse_fail:
-	printk(KERN_WARNING "dm: starting dm-%d (%s) failed\n",
-	       dm_setup_args.minor, dm_setup_args.name);
+	DMWARN("starting dm-%d (%s) failed",
+	       dev->minor, dev->name);
+	dm_setup_cleanup(devices);
 }
 
 __setup("dm=", dm_setup);
@@ -421,6 +465,6 @@
 {
 	if (!dm_early_setup)
 		return;
-	printk(KERN_INFO "dm: attempting early device configuration.\n");
-	dm_setup_drive();
+	DMINFO("attempting early device configuration.");
+	dm_setup_drives();
 }