Merge "drm/msm/sde: update fast clear enable logic"
diff --git a/Documentation/devicetree/bindings/rtc/qpnp-rtc.txt b/Documentation/devicetree/bindings/rtc/qpnp-rtc.txt
new file mode 100644
index 0000000..e0934b2
--- /dev/null
+++ b/Documentation/devicetree/bindings/rtc/qpnp-rtc.txt
@@ -0,0 +1,60 @@
+* msm-qpnp-rtc
+
+msm-qpnp-rtc is a RTC driver that supports 32 bit RTC housed inside PMIC.
+Driver utilizes MSM SPMI interface to communicate with the RTC module.
+RTC device is divided into two sub-peripherals one which controls basic RTC
+and other for controlling alarm.
+
+[PMIC RTC Device Declarations]
+
+-Root Node-
+
+Required properties :
+ - compatible:		Must be "qcom,qpnp-rtc"
+ - #address-cells:	The number of cells dedicated to represent an address
+			This must be set to '1'.
+ - #size-cells:		The number of cells dedicated to represent address
+			space range of a peripheral. This must be set to '1'.
+
+Optional properties:
+ - qcom,qpnp-rtc-write:		This property enables/disables rtc write
+				operation. If not mentioned rtc driver keeps
+				rtc writes disabled.
+				0 = Disable rtc writes.
+				1 = Enable rtc writes.
+ - qcom,qpnp-rtc-alarm-pwrup:	This property enables/disables feature of
+				powering up phone (from power down state)
+				through alarm interrupt.
+				If not mentioned rtc driver will disable
+				feature of powring-up phone through alarm.
+				0 = Disable powering up of phone through
+				alarm interrupt.
+				1 = Enable powering up of phone through
+				alarm interrupt.
+
+-Child Nodes-
+
+Required properties :
+ - reg :		Specify the spmi offset and size for device.
+ - interrupts:		Specifies alarm interrupt, only for rtc_alarm
+			sub-peripheral.
+
+Example:
+	qcom,pm8941_rtc {
+		compatible = "qcom,qpnp-rtc";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		qcom,qpnp-rtc-write = <0>;
+		qcom,qpnp-rtc-alarm-pwrup = <0>;
+
+		qcom,pm8941_rtc_rw@6000 {
+			reg = <0x6000 0x100>;
+		};
+
+		qcom,pm8941_rtc_alarm@6100 {
+			reg = <0x6100 0x100>;
+			interrupts = <0x0 0x61 0x1>;
+		};
+	};
+
+
diff --git a/Documentation/devicetree/bindings/thermal/tsens.txt b/Documentation/devicetree/bindings/thermal/tsens.txt
new file mode 100644
index 0000000..67ffaed
--- /dev/null
+++ b/Documentation/devicetree/bindings/thermal/tsens.txt
@@ -0,0 +1,46 @@
+Qualcomm Technologies, Inc. TSENS driver
+
+Temperature sensor (TSENS) driver supports reading temperature from sensors
+across the MSM. The driver defaults to support a 12 bit ADC.
+
+The driver uses the Thermal sysfs framework to provide thermal
+clients the ability to read from supported on-die temperature sensors,
+set temperature thresholds for cool/warm thresholds and receive notification
+on temperature threshold events.
+
+TSENS node
+
+Required properties:
+- compatible : should be "qcom,msm8996-tsens" for 8996 TSENS driver.
+	       should be "qcom,msm8953-tsens" for 8953 TSENS driver.
+	       should be "qcom,msm8998-tsens" for 8998 TSENS driver.
+	       should be "qcom,msmhamster-tsens" for hamster TSENS driver.
+	       should be "qcom,sdm660-tsens" for 660 TSENS driver.
+	       should be "qcom,sdm630-tsens" for 630 TSENS driver.
+	       should be "qcom,sdm845-tsens" for SDM845 TSENS driver.
+	       should be "qcom,tsens24xx" for 2.4 TSENS controller.
+	       The compatible property is used to identify the respective controller to use
+	       for the corresponding SoC.
+- reg : offset and length of the TSENS registers with associated property in reg-names
+	as "tsens_srot_physical" for TSENS SROT physical address region. TSENS TM
+	physical address region as "tsens_tm_physical".
+- reg-names : resource names used for the physical address of the TSENS
+	      registers. Should be "tsens_srot_physical" for physical address of the TSENS
+	      SROT region and "tsens_tm_physical" for physical address of the TM region.
+- interrupts : TSENS interrupt to notify Upper/Lower and Critical temperature threshold.
+- interrupt-names: Should be "tsens-upper-lower" for temperature threshold.
+		   Add "tsens-critical" for Critical temperature threshold notification
+		   in addition to "tsens-upper-lower" for 8996 TSENS since
+		   8996 supports Upper/Lower and Critical temperature threshold.
+
+Example:
+
+tsens@fc4a8000 {
+	compatible = "qcom,msm-tsens";
+	reg = <0xfc4a8000 0x10>,
+		<0xfc4b8000 0x1ff>;
+	reg-names = "tsens_srot_physical",
+		    "tsens_tm_physical";
+	interrupts = <0 184 0>;
+	interrupt-names = "tsens-upper-lower";
+};
diff --git a/arch/arm64/boot/dts/qcom/pm855.dtsi b/arch/arm64/boot/dts/qcom/pm855.dtsi
index 15252db..008d84e 100644
--- a/arch/arm64/boot/dts/qcom/pm855.dtsi
+++ b/arch/arm64/boot/dts/qcom/pm855.dtsi
@@ -28,6 +28,22 @@
 			#thermal-sensor-cells = <0>;
 			qcom,temperature-threshold-set = <1>;
 		};
+
+		pm855_rtc: qcom,pm855_rtc {
+			compatible = "qcom,qpnp-rtc";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			qcom,qpnp-rtc-write = <0>;
+			qcom,qpnp-rtc-alarm-pwrup = <0>;
+
+			qcom,pm855_rtc_rw@6000 {
+				reg = <0x6000 0x100>;
+			};
+			qcom,pm855_rtc_alarm@6100 {
+				reg = <0x6100 0x100>;
+				interrupts = <0x0 0x61 0x1 IRQ_TYPE_NONE>;
+			};
+		};
 	};
 
 	qcom,pm855@1 {
diff --git a/arch/arm64/boot/dts/qcom/sdm855.dtsi b/arch/arm64/boot/dts/qcom/sdm855.dtsi
index ff29912..61f43d2 100644
--- a/arch/arm64/boot/dts/qcom/sdm855.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm855.dtsi
@@ -1944,6 +1944,28 @@
 				 <DCC_READ 0x0c202244 1 0>;
 	};
 
+	tsens0: tsens@c222000 {
+		compatible = "qcom,tsens24xx";
+		reg = <0xc222000 0x4>,
+			<0xc263000 0x1ff>;
+		reg-names = "tsens_srot_physical",
+				"tsens_tm_physical";
+		interrupts = <0 506 0>, <0 508 0>;
+		interrupt-names = "tsens-upper-lower", "tsens-critical";
+		#thermal-sensor-cells = <1>;
+	};
+
+	tsens1: tsens@c223000 {
+		compatible = "qcom,tsens24xx";
+		reg = <0xc223000 0x4>,
+			<0xc265000 0x1ff>;
+		reg-names = "tsens_srot_physical",
+			"tsens_tm_physical";
+		interrupts = <0 507 0>, <0 509 0>;
+		interrupt-names = "tsens-upper-lower", "tsens-critical";
+		#thermal-sensor-cells = <1>;
+	};
+
 	thermal_zones: thermal-zones {
 	};
 };
diff --git a/arch/arm64/configs/sdm855-perf_defconfig b/arch/arm64/configs/sdm855-perf_defconfig
index ae342ed..1765e14 100644
--- a/arch/arm64/configs/sdm855-perf_defconfig
+++ b/arch/arm64/configs/sdm855-perf_defconfig
@@ -269,6 +269,7 @@
 CONFIG_POWER_RESET_SYSCON=y
 CONFIG_THERMAL=y
 CONFIG_QCOM_SPMI_TEMP_ALARM=y
+CONFIG_THERMAL_TSENS=y
 CONFIG_MFD_SPMI_PMIC=y
 CONFIG_REGULATOR=y
 CONFIG_REGULATOR_FIXED_VOLTAGE=y
@@ -325,6 +326,7 @@
 CONFIG_EDAC_KRYO_ARM64_PANIC_ON_CE=y
 CONFIG_EDAC_KRYO_ARM64_PANIC_ON_UE=y
 CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_QPNP=y
 CONFIG_DMADEVICES=y
 CONFIG_UIO=y
 CONFIG_STAGING=y
@@ -373,7 +375,6 @@
 CONFIG_QCOM_COMMAND_DB=y
 CONFIG_QTI_RPMH_API=y
 CONFIG_QCOM_GLINK=y
-CONFIG_MSM_JTAGV8=y
 CONFIG_IIO=y
 CONFIG_PWM=y
 CONFIG_ARM_GIC_V3_ACL=y
@@ -403,6 +404,17 @@
 # CONFIG_DEBUG_PREEMPT is not set
 CONFIG_IPC_LOGGING=y
 CONFIG_DEBUG_ALIGN_RODATA=y
+CONFIG_CORESIGHT=y
+CONFIG_CORESIGHT_LINK_AND_SINK_TMC=y
+CONFIG_CORESIGHT_SOURCE_ETM4X=y
+CONFIG_CORESIGHT_DYNAMIC_REPLICATOR=y
+CONFIG_CORESIGHT_STM=y
+CONFIG_CORESIGHT_CTI=y
+CONFIG_CORESIGHT_TPDA=y
+CONFIG_CORESIGHT_TPDM=y
+CONFIG_CORESIGHT_HWEVENT=y
+CONFIG_CORESIGHT_DUMMY=y
+CONFIG_CORESIGHT_EVENT=y
 CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y
 CONFIG_SECURITY=y
 CONFIG_HARDENED_USERCOPY=y
diff --git a/arch/arm64/configs/sdm855_defconfig b/arch/arm64/configs/sdm855_defconfig
index 9716270..de7f430 100644
--- a/arch/arm64/configs/sdm855_defconfig
+++ b/arch/arm64/configs/sdm855_defconfig
@@ -277,6 +277,7 @@
 CONFIG_POWER_RESET_SYSCON=y
 CONFIG_THERMAL=y
 CONFIG_QCOM_SPMI_TEMP_ALARM=y
+CONFIG_THERMAL_TSENS=y
 CONFIG_MFD_SPMI_PMIC=y
 CONFIG_REGULATOR=y
 CONFIG_REGULATOR_FIXED_VOLTAGE=y
@@ -335,6 +336,7 @@
 CONFIG_EDAC_QCOM_LLCC_PANIC_ON_CE=y
 CONFIG_EDAC_QCOM_LLCC_PANIC_ON_UE=y
 CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_QPNP=y
 CONFIG_DMADEVICES=y
 CONFIG_UIO=y
 CONFIG_STAGING=y
@@ -383,10 +385,11 @@
 CONFIG_QCOM_SECURE_BUFFER=y
 CONFIG_ICNSS=y
 CONFIG_ICNSS_DEBUG=y
+CONFIG_QCOM_BUS_SCALING=y
+CONFIG_QCOM_BUS_CONFIG_RPMH=y
 CONFIG_QCOM_COMMAND_DB=y
 CONFIG_QTI_RPMH_API=y
 CONFIG_QCOM_GLINK=y
-CONFIG_MSM_JTAGV8=y
 CONFIG_IIO=y
 CONFIG_PWM=y
 CONFIG_ARM_GIC_V3_ACL=y
@@ -460,6 +463,18 @@
 CONFIG_BUG_ON_DATA_CORRUPTION=y
 CONFIG_PID_IN_CONTEXTIDR=y
 CONFIG_ARM64_STRICT_BREAK_BEFORE_MAKE=y
+CONFIG_CORESIGHT=y
+CONFIG_CORESIGHT_LINK_AND_SINK_TMC=y
+CONFIG_CORESIGHT_SOURCE_ETM4X=y
+CONFIG_CORESIGHT_DYNAMIC_REPLICATOR=y
+CONFIG_CORESIGHT_STM=y
+CONFIG_CORESIGHT_CTI=y
+CONFIG_CORESIGHT_TPDA=y
+CONFIG_CORESIGHT_TPDM=y
+CONFIG_CORESIGHT_HWEVENT=y
+CONFIG_CORESIGHT_DUMMY=y
+CONFIG_CORESIGHT_TGU=y
+CONFIG_CORESIGHT_EVENT=y
 CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y
 CONFIG_SECURITY=y
 CONFIG_HARDENED_USERCOPY=y
diff --git a/drivers/clk/qcom/clk-regmap-divider.c b/drivers/clk/qcom/clk-regmap-divider.c
index 5348491..c314d2c 100644
--- a/drivers/clk/qcom/clk-regmap-divider.c
+++ b/drivers/clk/qcom/clk-regmap-divider.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014, 2017, The Linux Foundation. All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -28,8 +28,10 @@ static long div_round_rate(struct clk_hw *hw, unsigned long rate,
 {
 	struct clk_regmap_div *divider = to_clk_regmap_div(hw);
 
-	return divider_round_rate(hw, rate, prate, NULL, divider->width,
-				  CLK_DIVIDER_ROUND_CLOSEST);
+	return divider_round_rate(hw, rate, prate, divider->table,
+				  divider->width,
+				  CLK_DIVIDER_ROUND_CLOSEST |
+				  divider->flags);
 }
 
 static int div_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -39,8 +41,9 @@ static int div_set_rate(struct clk_hw *hw, unsigned long rate,
 	struct clk_regmap *clkr = &divider->clkr;
 	u32 div;
 
-	div = divider_get_val(rate, parent_rate, NULL, divider->width,
-			      CLK_DIVIDER_ROUND_CLOSEST);
+	div = divider_get_val(rate, parent_rate, divider->table,
+			      divider->width, CLK_DIVIDER_ROUND_CLOSEST |
+			      divider->flags);
 
 	return regmap_update_bits(clkr->regmap, divider->reg,
 				  (BIT(divider->width) - 1) << divider->shift,
@@ -58,8 +61,8 @@ static unsigned long div_recalc_rate(struct clk_hw *hw,
 	div >>= divider->shift;
 	div &= BIT(divider->width) - 1;
 
-	return divider_recalc_rate(hw, parent_rate, div, NULL,
-				   CLK_DIVIDER_ROUND_CLOSEST);
+	return divider_recalc_rate(hw, parent_rate, div, divider->table,
+				   CLK_DIVIDER_ROUND_CLOSEST | divider->flags);
 }
 
 const struct clk_ops clk_regmap_div_ops = {
diff --git a/drivers/clk/qcom/clk-regmap-divider.h b/drivers/clk/qcom/clk-regmap-divider.h
index fc4492e..1c5e087 100644
--- a/drivers/clk/qcom/clk-regmap-divider.h
+++ b/drivers/clk/qcom/clk-regmap-divider.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014, 2017, The Linux Foundation. All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -18,10 +18,12 @@
 #include "clk-regmap.h"
 
 struct clk_regmap_div {
-	u32			reg;
-	u32			shift;
-	u32			width;
-	struct clk_regmap	clkr;
+	u32				reg;
+	u32				shift;
+	u32				width;
+	u32				flags;
+	const struct clk_div_table	*table;
+	struct clk_regmap		clkr;
 };
 
 extern const struct clk_ops clk_regmap_div_ops;
diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c
index 621e08f..6c036bc 100644
--- a/drivers/hwtracing/coresight/coresight-cti.c
+++ b/drivers/hwtracing/coresight/coresight-cti.c
@@ -1451,8 +1451,8 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id)
 	drvdata->cpu = -1;
 	cpu_node = of_parse_phandle(adev->dev.of_node, "cpu", 0);
 	if (cpu_node) {
-		drvdata->cpu = pdata ? pdata->cpu : -1;
-		if (drvdata->cpu == -1) {
+		drvdata->cpu = pdata ? pdata->cpu : -ENODEV;
+		if (drvdata->cpu == -ENODEV) {
 			dev_err(drvdata->dev, "CTI cpu node invalid\n");
 			return -EINVAL;
 		}
diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c
index 212c942..ba736d1 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x.c
@@ -984,9 +984,9 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id)
 
 	spin_lock_init(&drvdata->spinlock);
 
-	drvdata->cpu = pdata ? pdata->cpu : -1;
+	drvdata->cpu = pdata ? pdata->cpu : -ENODEV;
 
-	if (drvdata->cpu == -1) {
+	if (drvdata->cpu == -ENODEV) {
 		dev_info(dev, "CPU not available\n");
 		return -ENODEV;
 	}
diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c
index b71eda1..bab01b25 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.c
+++ b/drivers/hwtracing/coresight/coresight-tmc.c
@@ -71,7 +71,7 @@ void tmc_enable_hw(struct tmc_drvdata *drvdata)
 
 void tmc_disable_hw(struct tmc_drvdata *drvdata)
 {
-	drvdata->enable = true;
+	drvdata->enable = false;
 	writel_relaxed(0x0, drvdata->base + TMC_CTL);
 }
 
@@ -461,7 +461,7 @@ static DEVICE_ATTR_RW(block_size);
 static int tmc_iommu_init(struct tmc_drvdata *drvdata)
 {
 	struct device_node *node = drvdata->dev->of_node;
-	int atomic_ctx = 1, ret = 0;
+	int ret = 0;
 
 	if (!of_property_read_bool(node, "iommus"))
 		return 0;
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c
index 5129a6f..591f970 100644
--- a/drivers/hwtracing/coresight/coresight.c
+++ b/drivers/hwtracing/coresight/coresight.c
@@ -158,6 +158,7 @@ static void coresight_disable_sink(struct coresight_device *csdev)
 	if (atomic_dec_return(csdev->refcnt) == 0) {
 		if (sink_ops(csdev)->disable) {
 			sink_ops(csdev)->disable(csdev);
+			csdev->enable = false;
 			csdev->activated = false;
 		}
 	}
diff --git a/drivers/hwtracing/coresight/of_coresight.c b/drivers/hwtracing/coresight/of_coresight.c
index 47834df..beb734f 100644
--- a/drivers/hwtracing/coresight/of_coresight.c
+++ b/drivers/hwtracing/coresight/of_coresight.c
@@ -109,9 +109,9 @@ int of_coresight_get_cpu(const struct device_node *node)
 
 	dn = of_parse_phandle(node, "cpu", 0);
 
-	/* Affinity defaults to CPU0 */
+	/* Affinity defaults to invalid */
 	if (!dn)
-		return 0;
+		return -ENODEV;
 
 	for_each_possible_cpu(cpu) {
 		np = of_cpu_device_node_get(cpu);
@@ -122,8 +122,8 @@ int of_coresight_get_cpu(const struct device_node *node)
 	}
 	of_node_put(dn);
 
-	/* Affinity to CPU0 if no cpu nodes are found */
-	return found ? cpu : 0;
+	/* Affinity to invalid if no cpu nodes are found */
+	return found ? cpu : -ENODEV;
 }
 EXPORT_SYMBOL_GPL(of_coresight_get_cpu);
 
@@ -207,8 +207,6 @@ of_get_coresight_platform_data(struct device *dev,
 	}
 
 	pdata->cpu = of_coresight_get_cpu(node);
-	/* Affinity defaults to invalid */
-	pdata->cpu = -1;
 
 	return pdata;
 }
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
index e31f798..267f214 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
@@ -3194,6 +3194,26 @@ void sde_rotator_core_destroy(struct sde_rot_mgr *mgr)
 	devm_kfree(dev, mgr);
 }
 
+void sde_rotator_core_dump(struct sde_rot_mgr *mgr)
+{
+	if (!mgr) {
+		SDEROT_ERR("null parameters\n");
+		return;
+	}
+
+	sde_rotator_resource_ctrl(mgr, true);
+	/* dump first snapshot */
+	if (mgr->ops_hw_dump_status)
+		mgr->ops_hw_dump_status(mgr->hw_data);
+
+	SDEROT_EVTLOG_TOUT_HANDLER("rot", "rot_dbg_bus", "vbif_dbg_bus");
+
+	/* dump second snapshot for comparison */
+	if (mgr->ops_hw_dump_status)
+		mgr->ops_hw_dump_status(mgr->hw_data);
+	sde_rotator_resource_ctrl(mgr, false);
+}
+
 static void sde_rotator_suspend_cancel_rot_work(struct sde_rot_mgr *mgr)
 {
 	struct sde_rot_file_private *priv, *priv_next;
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h
index 3edb2d0..e23ed7a 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h
@@ -478,6 +478,7 @@ struct sde_rot_mgr {
 	int (*ops_hw_get_downscale_caps)(struct sde_rot_mgr *mgr, char *caps,
 			int len);
 	int (*ops_hw_get_maxlinewidth)(struct sde_rot_mgr *mgr);
+	void (*ops_hw_dump_status)(struct sde_rot_mgr *mgr);
 
 	void *hw_data;
 };
@@ -570,6 +571,12 @@ int sde_rotator_core_init(struct sde_rot_mgr **pmgr,
 void sde_rotator_core_destroy(struct sde_rot_mgr *mgr);
 
 /*
+ * sde_rotator_core_dump - perform register dump
+ * @mgr: Pointer to rotator manager
+ */
+void sde_rotator_core_dump(struct sde_rot_mgr *mgr);
+
+/*
  * sde_rotator_session_open - open a new rotator per file session
  * @mgr: Pointer to rotator manager
  * @pprivate: Pointer to pointer of the newly initialized per file session
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c
index 46f64d2..b9158e1 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c
@@ -638,18 +638,6 @@ static void sde_rot_evtlog_debug_work(struct work_struct *work)
 }
 
 /*
- * sde_rot_dump_panic - Issue evtlog dump and generic panic
- */
-void sde_rot_dump_panic(bool do_panic)
-{
-	sde_rot_evtlog_dump_all();
-	sde_rot_dump_reg_all();
-
-	if (do_panic)
-		panic("sde_rotator");
-}
-
-/*
  * sde_rot_evtlog_tout_handler - log dump timeout handler
  * @queue: boolean indicate putting log dump into queue
  * @name: function name having timeout
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.h
index 2fc8e3f..fa53083 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.h
@@ -42,7 +42,6 @@ enum sde_rot_dbg_evtlog_flag {
 		SDE_ROT_EVTLOG_TOUT_DATA_LIMITER)
 
 void sde_rot_evtlog(const char *name, int line, int flag, ...);
-void sde_rot_dump_panic(bool do_panic);
 void sde_rot_evtlog_tout_handler(bool queue, const char *name, ...);
 
 struct sde_rotator_device;
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c
index c032a73..1c0731d 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c
@@ -1423,6 +1423,61 @@ int sde_rotator_inline_get_pixfmt_caps(struct platform_device *pdev,
 EXPORT_SYMBOL(sde_rotator_inline_get_pixfmt_caps);
 
 /*
+ * _sde_rotator_inline_cleanup - perform inline related request cleanup
+ *	This function assumes rot_dev->mgr lock has been taken when called.
+ * @handle: Pointer to rotator context
+ * @request: Pointer to rotation request
+ * return: 0 if success; -EAGAIN if cleanup should be retried
+ */
+static int _sde_rotator_inline_cleanup(void *handle,
+		struct sde_rotator_request *request)
+{
+	struct sde_rotator_ctx *ctx;
+	struct sde_rotator_device *rot_dev;
+	int ret;
+
+	if (!handle || !request) {
+		SDEROT_ERR("invalid rotator handle/request\n");
+		return -EINVAL;
+	}
+
+	ctx = handle;
+	rot_dev = ctx->rot_dev;
+
+	if (!rot_dev || !rot_dev->mgr) {
+		SDEROT_ERR("invalid rotator device\n");
+		return -EINVAL;
+	}
+
+	if (request->committed) {
+		/* wait until request is finished */
+		sde_rot_mgr_unlock(rot_dev->mgr);
+		mutex_unlock(&rot_dev->lock);
+		ret = wait_event_timeout(ctx->wait_queue,
+			sde_rotator_is_request_retired(request),
+			msecs_to_jiffies(rot_dev->streamoff_timeout));
+		mutex_lock(&rot_dev->lock);
+		sde_rot_mgr_lock(rot_dev->mgr);
+
+		if (!ret) {
+			SDEROT_ERR("timeout w/o retire s:%d\n",
+					ctx->session_id);
+			SDEROT_EVTLOG(ctx->session_id, SDE_ROT_EVTLOG_ERROR);
+			sde_rotator_abort_inline_request(rot_dev->mgr,
+					ctx->private, request->req);
+			return -EAGAIN;
+		} else if (ret == 1) {
+			SDEROT_ERR("timeout w/ retire s:%d\n", ctx->session_id);
+			SDEROT_EVTLOG(ctx->session_id, SDE_ROT_EVTLOG_ERROR);
+		}
+	}
+
+	sde_rotator_req_finish(rot_dev->mgr, ctx->private, request->req);
+	sde_rotator_retire_request(request);
+	return 0;
+}
+
+/*
  * sde_rotator_inline_commit - commit given rotator command
  * @handle: Pointer to rotator context
  * @cmd: Pointer to rotator command
@@ -1449,7 +1504,7 @@ int sde_rotator_inline_commit(void *handle, struct sde_rotator_inline_cmd *cmd,
 	ctx = handle;
 	rot_dev = ctx->rot_dev;
 
-	if (!rot_dev) {
+	if (!rot_dev || !rot_dev->mgr) {
 		SDEROT_ERR("invalid rotator device\n");
 		return -EINVAL;
 	}
@@ -1481,6 +1536,7 @@ int sde_rotator_inline_commit(void *handle, struct sde_rotator_inline_cmd *cmd,
 		(cmd->video_mode << 5) |
 		(cmd_type << 24));
 
+	mutex_lock(&rot_dev->lock);
 	sde_rot_mgr_lock(rot_dev->mgr);
 
 	if (cmd_type == SDE_ROTATOR_INLINE_CMD_VALIDATE ||
@@ -1690,30 +1746,12 @@ int sde_rotator_inline_commit(void *handle, struct sde_rotator_inline_cmd *cmd,
 		}
 
 		request = cmd->priv_handle;
-		req = request->req;
 
-		if (request->committed) {
-			/* wait until request is finished */
-			sde_rot_mgr_unlock(rot_dev->mgr);
-			ret = wait_event_timeout(ctx->wait_queue,
-				sde_rotator_is_request_retired(request),
-				msecs_to_jiffies(rot_dev->streamoff_timeout));
-			if (!ret) {
-				SDEROT_ERR("timeout w/o retire s:%d\n",
-						ctx->session_id);
-				SDEROT_EVTLOG(ctx->session_id,
-						SDE_ROT_EVTLOG_ERROR);
-			} else if (ret == 1) {
-				SDEROT_ERR("timeout w/ retire s:%d\n",
-						ctx->session_id);
-				SDEROT_EVTLOG(ctx->session_id,
-						SDE_ROT_EVTLOG_ERROR);
-			}
-			sde_rot_mgr_lock(rot_dev->mgr);
-		}
+		/* attempt single retry if first cleanup attempt failed */
+		if (_sde_rotator_inline_cleanup(handle, request) == -EAGAIN)
+			_sde_rotator_inline_cleanup(handle, request);
 
-		sde_rotator_req_finish(rot_dev->mgr, ctx->private, req);
-		sde_rotator_retire_request(request);
+		cmd->priv_handle = NULL;
 	} else if (cmd_type == SDE_ROTATOR_INLINE_CMD_ABORT) {
 		if (!cmd->priv_handle) {
 			ret = -EINVAL;
@@ -1722,11 +1760,13 @@ int sde_rotator_inline_commit(void *handle, struct sde_rotator_inline_cmd *cmd,
 		}
 
 		request = cmd->priv_handle;
-		sde_rotator_abort_inline_request(rot_dev->mgr,
-				ctx->private, request->req);
+		if (!sde_rotator_is_request_retired(request))
+			sde_rotator_abort_inline_request(rot_dev->mgr,
+					ctx->private, request->req);
 	}
 
 	sde_rot_mgr_unlock(rot_dev->mgr);
+	mutex_unlock(&rot_dev->lock);
 	return 0;
 
 error_handle_request:
@@ -1739,13 +1779,29 @@ int sde_rotator_inline_commit(void *handle, struct sde_rotator_inline_cmd *cmd,
 error_invalid_handle:
 error_init_request:
 	sde_rot_mgr_unlock(rot_dev->mgr);
+	mutex_unlock(&rot_dev->lock);
 	return ret;
 }
 EXPORT_SYMBOL(sde_rotator_inline_commit);
 
 void sde_rotator_inline_reg_dump(struct platform_device *pdev)
 {
-	sde_rot_dump_panic(false);
+	struct sde_rotator_device *rot_dev;
+
+	if (!pdev) {
+		SDEROT_ERR("invalid platform device\n");
+		return;
+	}
+
+	rot_dev = (struct sde_rotator_device *) platform_get_drvdata(pdev);
+	if (!rot_dev || !rot_dev->mgr) {
+		SDEROT_ERR("invalid rotator device\n");
+		return;
+	}
+
+	sde_rot_mgr_lock(rot_dev->mgr);
+	sde_rotator_core_dump(rot_dev->mgr);
+	sde_rot_mgr_unlock(rot_dev->mgr);
 }
 EXPORT_SYMBOL(sde_rotator_inline_reg_dump);
 
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
index 3864cfb..a7eca43 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
@@ -54,7 +54,7 @@
  * When in sbuf mode, select a much longer wait, to allow the other driver
  * to detect timeouts and abort if necessary.
  */
-#define KOFF_TIMEOUT_SBUF	(2000)
+#define KOFF_TIMEOUT_SBUF	(10000)
 
 /* default stream buffer headroom in lines */
 #define DEFAULT_SBUF_HEADROOM	20
@@ -132,6 +132,9 @@
 #define SDE_ROTREG_READ(base, off) \
 	readl_relaxed(base + (off))
 
+#define SDE_ROTTOP_IN_OFFLINE_MODE(_rottop_op_mode_) \
+	(((_rottop_op_mode_) & ROTTOP_OP_MODE_ROT_OUT_MASK) == 0)
+
 static const u32 sde_hw_rotator_v3_inpixfmts[] = {
 	SDE_PIX_FMT_XRGB_8888,
 	SDE_PIX_FMT_ARGB_8888,
@@ -536,6 +539,8 @@ static struct sde_rot_regdump sde_rot_r3_regdump[] = {
 		SDE_ROT_REGDUMP_READ },
 	{ "SDEROT_VBIF_NRT", SDE_ROT_VBIF_NRT_OFFSET, 0x590,
 		SDE_ROT_REGDUMP_VBIF },
+	{ "SDEROT_REGDMA_RESET", ROTTOP_SW_RESET_OVERRIDE, 0,
+		SDE_ROT_REGDUMP_WRITE },
 };
 
 struct sde_rot_cdp_params {
@@ -693,7 +698,7 @@ static void sde_hw_rotator_halt_vbif_xin_client(void)
 /**
  * sde_hw_rotator_reset - Reset rotator hardware
  * @rot: pointer to hw rotator
- * @ctx: pointer to current rotator context during the hw hang
+ * @ctx: pointer to current rotator context during the hw hang (optional)
  */
 static int sde_hw_rotator_reset(struct sde_hw_rotator *rot,
 		struct sde_hw_rotator_context *ctx)
@@ -707,13 +712,8 @@ static int sde_hw_rotator_reset(struct sde_hw_rotator *rot,
 	int i, j;
 	unsigned long flags;
 
-	if (!rot || !ctx) {
-		SDEROT_ERR("NULL rotator context\n");
-		return -EINVAL;
-	}
-
-	if (ctx->q_id >= ROT_QUEUE_MAX) {
-		SDEROT_ERR("context q_id out of range: %d\n", ctx->q_id);
+	if (!rot) {
+		SDEROT_ERR("NULL rotator\n");
 		return -EINVAL;
 	}
 
@@ -725,6 +725,15 @@ static int sde_hw_rotator_reset(struct sde_hw_rotator *rot,
 	/* halt vbif xin client to ensure no pending transaction */
 	sde_hw_rotator_halt_vbif_xin_client();
 
+	/* if no ctx is specified, skip ctx wake up */
+	if (!ctx)
+		return 0;
+
+	if (ctx->q_id >= ROT_QUEUE_MAX) {
+		SDEROT_ERR("context q_id out of range: %d\n", ctx->q_id);
+		return -EINVAL;
+	}
+
 	spin_lock_irqsave(&rot->rotisr_lock, flags);
 
 	/* update timestamp register with current context */
@@ -780,10 +789,11 @@ static int sde_hw_rotator_reset(struct sde_hw_rotator *rot,
 }
 
 /**
- * sde_hw_rotator_dump_status - Dump hw rotator status on error
+ * _sde_hw_rotator_dump_status - Dump hw rotator status on error
  * @rot: Pointer to hw rotator
  */
-static void sde_hw_rotator_dump_status(struct sde_hw_rotator *rot, u32 *ubwcerr)
+static void _sde_hw_rotator_dump_status(struct sde_hw_rotator *rot,
+		u32 *ubwcerr)
 {
 	struct sde_rot_data_type *mdata = sde_rot_get_mdata();
 	u32 reg = 0;
@@ -815,6 +825,11 @@ static void sde_hw_rotator_dump_status(struct sde_hw_rotator *rot, u32 *ubwcerr)
 		SDE_ROTREG_READ(rot->mdss_base,
 			REGDMA_CSR_REGDMA_FSM_STATE));
 
+	SDEROT_ERR("rottop: op_mode = %x, status = %x, clk_status = %x\n",
+		SDE_ROTREG_READ(rot->mdss_base, ROTTOP_OP_MODE),
+		SDE_ROTREG_READ(rot->mdss_base, ROTTOP_STATUS),
+		SDE_ROTREG_READ(rot->mdss_base, ROTTOP_CLK_STATUS));
+
 	reg = SDE_ROTREG_READ(rot->mdss_base, ROT_SSPP_UBWC_ERROR_STATUS);
 	if (ubwcerr)
 		*ubwcerr = reg;
@@ -826,12 +841,35 @@ static void sde_hw_rotator_dump_status(struct sde_hw_rotator *rot, u32 *ubwcerr)
 		SDE_VBIF_READ(mdata, MMSS_VBIF_XIN_HALT_CTRL1),
 		SDE_VBIF_READ(mdata, MMSS_VBIF_AXI_HALT_CTRL1));
 
-	SDEROT_ERR(
-		"sbuf_status_plane0 = %x, sbuf_status_plane1 = %x\n",
-		SDE_ROTREG_READ(rot->mdss_base,
-			ROT_WB_SBUF_STATUS_PLANE0),
-		SDE_ROTREG_READ(rot->mdss_base,
-			ROT_WB_SBUF_STATUS_PLANE1));
+	SDEROT_ERR("sspp unpack wr: plane0 = %x, plane1 = %x, plane2 = %x\n",
+			SDE_ROTREG_READ(rot->mdss_base,
+				ROT_SSPP_FETCH_SMP_WR_PLANE0),
+			SDE_ROTREG_READ(rot->mdss_base,
+				ROT_SSPP_FETCH_SMP_WR_PLANE1),
+			SDE_ROTREG_READ(rot->mdss_base,
+				ROT_SSPP_FETCH_SMP_WR_PLANE2));
+	SDEROT_ERR("sspp unpack rd: plane0 = %x, plane1 = %x, plane2 = %x\n",
+			SDE_ROTREG_READ(rot->mdss_base,
+					ROT_SSPP_SMP_UNPACK_RD_PLANE0),
+			SDE_ROTREG_READ(rot->mdss_base,
+					ROT_SSPP_SMP_UNPACK_RD_PLANE1),
+			SDE_ROTREG_READ(rot->mdss_base,
+					ROT_SSPP_SMP_UNPACK_RD_PLANE2));
+	SDEROT_ERR("sspp: unpack_ln = %x, unpack_blk = %x, fill_lvl = %x\n",
+			SDE_ROTREG_READ(rot->mdss_base,
+				ROT_SSPP_UNPACK_LINE_COUNT),
+			SDE_ROTREG_READ(rot->mdss_base,
+				ROT_SSPP_UNPACK_BLK_COUNT),
+			SDE_ROTREG_READ(rot->mdss_base,
+				ROT_SSPP_FILL_LEVELS));
+
+	SDEROT_ERR("wb: sbuf0 = %x, sbuf1 = %x, sys_cache = %x\n",
+			SDE_ROTREG_READ(rot->mdss_base,
+				ROT_WB_SBUF_STATUS_PLANE0),
+			SDE_ROTREG_READ(rot->mdss_base,
+				ROT_WB_SBUF_STATUS_PLANE1),
+			SDE_ROTREG_READ(rot->mdss_base,
+				ROT_WB_SYS_CACHE_MODE));
 }
 
 /**
@@ -1952,7 +1990,7 @@ static u32 sde_hw_rotator_wait_done_regdma(
 			else if (status & REGDMA_INVALID_CMD)
 				SDEROT_ERR("REGDMA invalid command\n");
 
-			sde_hw_rotator_dump_status(rot, &ubwcerr);
+			_sde_hw_rotator_dump_status(rot, &ubwcerr);
 
 			if (ubwcerr || abort) {
 				/*
@@ -1997,12 +2035,12 @@ static u32 sde_hw_rotator_wait_done_regdma(
 		if (last_isr & REGDMA_INT_ERR_MASK) {
 			SDEROT_ERR("Rotator error, ts:0x%X/0x%X status:%x\n",
 				ctx->timestamp, swts, last_isr);
-			sde_hw_rotator_dump_status(rot, NULL);
+			_sde_hw_rotator_dump_status(rot, NULL);
 			status = ROT_ERROR_BIT;
 		} else if (pending) {
 			SDEROT_ERR("Rotator timeout, ts:0x%X/0x%X status:%x\n",
 				ctx->timestamp, swts, last_isr);
-			sde_hw_rotator_dump_status(rot, NULL);
+			_sde_hw_rotator_dump_status(rot, NULL);
 			status = ROT_ERROR_BIT;
 		} else {
 			status = 0;
@@ -2140,7 +2178,7 @@ void sde_hw_rotator_pre_pmevent(struct sde_rot_mgr *mgr, bool pmon)
 {
 	struct sde_hw_rotator *rot;
 	u32 l_ts, h_ts, swts, hwts;
-	u32 rotsts, regdmasts;
+	u32 rotsts, regdmasts, rotopmode;
 
 	/*
 	 * Check last HW timestamp with SW timestamp before power off event.
@@ -2165,19 +2203,37 @@ void sde_hw_rotator_pre_pmevent(struct sde_rot_mgr *mgr, bool pmon)
 		regdmasts = SDE_ROTREG_READ(rot->mdss_base,
 				REGDMA_CSR_REGDMA_BLOCK_STATUS);
 		rotsts = SDE_ROTREG_READ(rot->mdss_base, ROTTOP_STATUS);
+		rotopmode = SDE_ROTREG_READ(rot->mdss_base, ROTTOP_OP_MODE);
 
 		SDEROT_DBG(
-			"swts:0x%x, hwts:0x%x, regdma-sts:0x%x, rottop-sts:0x%x\n",
-				swts, hwts, regdmasts, rotsts);
-		SDEROT_EVTLOG(swts, hwts, regdmasts, rotsts);
+			"swts:0x%x, hwts:0x%x, regdma-sts:0x%x, rottop-sts:0x%x, rottop-opmode:0x%x\n",
+				swts, hwts, regdmasts, rotsts, rotopmode);
+		SDEROT_EVTLOG(swts, hwts, regdmasts, rotsts, rotopmode);
 
 		if ((swts != hwts) && ((regdmasts & REGDMA_BUSY) ||
 					(rotsts & ROT_STATUS_MASK))) {
 			SDEROT_ERR(
 				"Mismatch SWTS with HWTS: swts:0x%x, hwts:0x%x, regdma-sts:0x%x, rottop-sts:0x%x\n",
 				swts, hwts, regdmasts, rotsts);
+			_sde_hw_rotator_dump_status(rot, NULL);
 			SDEROT_EVTLOG_TOUT_HANDLER("rot", "rot_dbg_bus",
 					"vbif_dbg_bus", "panic");
+		} else if (!SDE_ROTTOP_IN_OFFLINE_MODE(rotopmode) &&
+				((regdmasts & REGDMA_BUSY) ||
+						(rotsts & ROT_BUSY_BIT))) {
+			/*
+			 * rotator can stuck in inline while mdp is detached
+			 */
+			SDEROT_WARN(
+				"Inline Rot busy: regdma-sts:0x%x, rottop-sts:0x%x, rottop-opmode:0x%x\n",
+				regdmasts, rotsts, rotopmode);
+			sde_hw_rotator_reset(rot, NULL);
+		} else if ((regdmasts & REGDMA_BUSY) ||
+				(rotsts & ROT_BUSY_BIT)) {
+			_sde_hw_rotator_dump_status(rot, NULL);
+			SDEROT_EVTLOG_TOUT_HANDLER("rot", "rot_dbg_bus",
+					"vbif_dbg_bus", "panic");
+			sde_hw_rotator_reset(rot, NULL);
 		}
 
 		/* Turn off rotator clock after checking rotator registers */
@@ -2481,7 +2537,7 @@ static int sde_hw_rotator_config(struct sde_rot_hw_resource *hw,
 				if (status & BIT(0)) {
 					SDEROT_ERR("rotator busy 0x%x\n",
 							status);
-					sde_hw_rotator_dump_status(rot, NULL);
+					_sde_hw_rotator_dump_status(rot, NULL);
 					SDEROT_EVTLOG_TOUT_HANDLER("rot",
 							"vbif_dbg_bus",
 							"panic");
@@ -3494,6 +3550,21 @@ static int sde_hw_rotator_get_maxlinewidth(struct sde_rot_mgr *mgr)
 }
 
 /*
+ * sde_hw_rotator_dump_status - dump status to debug output
+ * @mgr: Pointer to rotator manager
+ * return: none
+ */
+static void sde_hw_rotator_dump_status(struct sde_rot_mgr *mgr)
+{
+	if (!mgr || !mgr->hw_data) {
+		SDEROT_ERR("null parameters\n");
+		return;
+	}
+
+	_sde_hw_rotator_dump_status(mgr->hw_data, NULL);
+}
+
+/*
  * sde_hw_rotator_parse_dt - parse r3 specific device tree settings
  * @hw_data: Pointer to rotator hw
  * @dev: Pointer to platform device
@@ -3622,6 +3693,7 @@ int sde_rotator_r3_init(struct sde_rot_mgr *mgr)
 	mgr->ops_hw_post_pmevent = sde_hw_rotator_post_pmevent;
 	mgr->ops_hw_get_downscale_caps = sde_hw_rotator_get_downscale_caps;
 	mgr->ops_hw_get_maxlinewidth = sde_hw_rotator_get_maxlinewidth;
+	mgr->ops_hw_dump_status = sde_hw_rotator_dump_status;
 
 	ret = sde_hw_rotator_parse_dt(mgr->hw_data, mgr->pdev);
 	if (ret)
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_hwio.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_hwio.h
index 2afd032..aaaa28c 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_hwio.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_hwio.h
@@ -50,6 +50,8 @@
 #define ROTTOP_START_CTRL_TRIG_SEL_REGDMA       2
 #define ROTTOP_START_CTRL_TRIG_SEL_MDP          3
 
+#define ROTTOP_OP_MODE_ROT_OUT_MASK             (0x3 << 4)
+
 /* SDE_ROT_SSPP:
  * OFFSET=0x0A8900
  */
diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c
index a6b37d3..86e4af7 100644
--- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c
+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c
@@ -177,11 +177,20 @@ static int rmnet_newlink(struct net *src_net, struct net_device *dev,
 	if (err)
 		goto err2;
 
-	netdev_dbg(dev, "data format [ingress 0x%08X]\n", ingress_format);
-	port->ingress_data_format = ingress_format;
 	port->rmnet_mode = mode;
 
 	hlist_add_head_rcu(&ep->hlnode, &port->muxed_ep[mux_id]);
+
+	if (data[IFLA_VLAN_FLAGS]) {
+		struct ifla_vlan_flags *flags;
+
+		flags = nla_data(data[IFLA_VLAN_FLAGS]);
+		ingress_format = flags->flags & flags->mask;
+	}
+
+	netdev_dbg(dev, "data format [ingress 0x%08X]\n", ingress_format);
+	port->ingress_data_format = ingress_format;
+
 	return 0;
 
 err2:
@@ -311,9 +320,49 @@ static int rmnet_rtnl_validate(struct nlattr *tb[], struct nlattr *data[],
 	return 0;
 }
 
+static int rmnet_changelink(struct net_device *dev, struct nlattr *tb[],
+			    struct nlattr *data[],
+			    struct netlink_ext_ack *extack)
+{
+	struct rmnet_priv *priv = netdev_priv(dev);
+	struct net_device *real_dev;
+	struct rmnet_endpoint *ep;
+	struct rmnet_port *port;
+	u16 mux_id;
+
+	real_dev = __dev_get_by_index(dev_net(dev),
+				      nla_get_u32(tb[IFLA_LINK]));
+
+	if (!real_dev || !dev || !rmnet_is_real_dev_registered(real_dev))
+		return -ENODEV;
+
+	port = rmnet_get_port_rtnl(real_dev);
+
+	if (data[IFLA_VLAN_ID]) {
+		mux_id = nla_get_u16(data[IFLA_VLAN_ID]);
+		ep = rmnet_get_endpoint(port, priv->mux_id);
+
+		hlist_del_init_rcu(&ep->hlnode);
+		hlist_add_head_rcu(&ep->hlnode, &port->muxed_ep[mux_id]);
+
+		ep->mux_id = mux_id;
+		priv->mux_id = mux_id;
+	}
+
+	if (data[IFLA_VLAN_FLAGS]) {
+		struct ifla_vlan_flags *flags;
+
+		flags = nla_data(data[IFLA_VLAN_FLAGS]);
+		port->ingress_data_format = flags->flags & flags->mask;
+	}
+
+	return 0;
+}
+
 static size_t rmnet_get_size(const struct net_device *dev)
 {
-	return nla_total_size(2); /* IFLA_VLAN_ID */
+	return nla_total_size(2) /* IFLA_VLAN_ID */ +
+	  nla_total_size(sizeof(struct ifla_vlan_flags)); /* IFLA_VLAN_FLAGS */
 }
 
 struct rtnl_link_ops rmnet_link_ops __read_mostly = {
@@ -325,6 +374,7 @@ struct rtnl_link_ops rmnet_link_ops __read_mostly = {
 	.newlink	= rmnet_newlink,
 	.dellink	= rmnet_dellink,
 	.get_size	= rmnet_get_size,
+	.changelink     = rmnet_changelink,
 };
 
 /* Needs either rcu_read_lock() or rtnl lock */
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index e0e58f3..623fdec 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -1619,6 +1619,15 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called rtc-pm8xxx.
 
+config RTC_DRV_QPNP
+	tristate "Qualcomm Technologies, Inc. QPNP PMIC RTC"
+	depends on SPMI
+	help
+	  This enables support for the RTC found on Qualcomm Technologies, Inc.
+	  QPNP PMIC chips.  This driver supports using the PMIC RTC peripheral
+	  to wake a mobile device up from suspend or to wake it up from power-
+	  off.
+
 config RTC_DRV_TEGRA
 	tristate "NVIDIA Tegra Internal RTC driver"
 	depends on ARCH_TEGRA || COMPILE_TEST
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 0bf1fc0..3cb23b5 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -124,6 +124,7 @@
 obj-$(CONFIG_RTC_DRV_PS3)	+= rtc-ps3.o
 obj-$(CONFIG_RTC_DRV_PUV3)	+= rtc-puv3.o
 obj-$(CONFIG_RTC_DRV_PXA)	+= rtc-pxa.o
+obj-$(CONFIG_RTC_DRV_QPNP)	+= qpnp-rtc.o
 obj-$(CONFIG_RTC_DRV_R7301)	+= rtc-r7301.o
 obj-$(CONFIG_RTC_DRV_R9701)	+= rtc-r9701.o
 obj-$(CONFIG_RTC_DRV_RC5T583)	+= rtc-rc5t583.o
diff --git a/drivers/rtc/qpnp-rtc.c b/drivers/rtc/qpnp-rtc.c
new file mode 100644
index 0000000..c6e4d9a
--- /dev/null
+++ b/drivers/rtc/qpnp-rtc.c
@@ -0,0 +1,714 @@
+/* Copyright (c) 2012-2015, 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 <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/init.h>
+#include <linux/rtc.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <linux/idr.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/spmi.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/alarmtimer.h>
+
+/* RTC/ALARM Register offsets */
+#define REG_OFFSET_ALARM_RW	0x40
+#define REG_OFFSET_ALARM_CTRL1	0x46
+#define REG_OFFSET_ALARM_CTRL2	0x48
+#define REG_OFFSET_RTC_WRITE	0x40
+#define REG_OFFSET_RTC_CTRL	0x46
+#define REG_OFFSET_RTC_READ	0x48
+#define REG_OFFSET_PERP_SUBTYPE	0x05
+
+/* RTC_CTRL register bit fields */
+#define BIT_RTC_ENABLE		BIT(7)
+#define BIT_RTC_ALARM_ENABLE	BIT(7)
+#define BIT_RTC_ABORT_ENABLE	BIT(0)
+#define BIT_RTC_ALARM_CLEAR	BIT(0)
+
+/* RTC/ALARM peripheral subtype values */
+#define RTC_PERPH_SUBTYPE       0x1
+#define ALARM_PERPH_SUBTYPE     0x3
+
+#define NUM_8_BIT_RTC_REGS	0x4
+
+#define TO_SECS(arr)		(arr[0] | (arr[1] << 8) | (arr[2] << 16) | \
+							(arr[3] << 24))
+
+/* Module parameter to control power-on-alarm */
+bool poweron_alarm;
+EXPORT_SYMBOL(poweron_alarm);
+module_param(poweron_alarm, bool, 0644);
+MODULE_PARM_DESC(poweron_alarm, "Enable/Disable power-on alarm");
+
+/* rtc driver internal structure */
+struct qpnp_rtc {
+	u8			rtc_ctrl_reg;
+	u8			alarm_ctrl_reg1;
+	u16			rtc_base;
+	u16			alarm_base;
+	u32			rtc_write_enable;
+	u32			rtc_alarm_powerup;
+	int			rtc_alarm_irq;
+	struct device		*rtc_dev;
+	struct rtc_device	*rtc;
+	struct platform_device	*pdev;
+	struct regmap		*regmap;
+	spinlock_t		alarm_ctrl_lock;
+};
+
+static int qpnp_read_wrapper(struct qpnp_rtc *rtc_dd, u8 *rtc_val,
+			u16 base, int count)
+{
+	int rc;
+
+	rc = regmap_bulk_read(rtc_dd->regmap, base, rtc_val, count);
+	if (rc) {
+		dev_err(rtc_dd->rtc_dev, "SPMI read failed\n");
+		return rc;
+	}
+	return 0;
+}
+
+static int qpnp_write_wrapper(struct qpnp_rtc *rtc_dd, u8 *rtc_val,
+			u16 base, int count)
+{
+	int rc;
+
+	rc = regmap_bulk_write(rtc_dd->regmap, base, rtc_val, count);
+	if (rc) {
+		dev_err(rtc_dd->rtc_dev, "SPMI write failed\n");
+		return rc;
+	}
+
+	return 0;
+}
+
+static int
+qpnp_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+	int rc;
+	unsigned long secs, irq_flags;
+	u8 value[4], reg = 0, alarm_enabled = 0, ctrl_reg;
+	u8 rtc_disabled = 0, rtc_ctrl_reg;
+	struct qpnp_rtc *rtc_dd = dev_get_drvdata(dev);
+
+	rtc_tm_to_time(tm, &secs);
+
+	value[0] = secs & 0xFF;
+	value[1] = (secs >> 8) & 0xFF;
+	value[2] = (secs >> 16) & 0xFF;
+	value[3] = (secs >> 24) & 0xFF;
+
+	dev_dbg(dev, "Seconds value to be written to RTC = %lu\n", secs);
+
+	spin_lock_irqsave(&rtc_dd->alarm_ctrl_lock, irq_flags);
+	ctrl_reg = rtc_dd->alarm_ctrl_reg1;
+
+	if (ctrl_reg & BIT_RTC_ALARM_ENABLE) {
+		alarm_enabled = 1;
+		ctrl_reg &= ~BIT_RTC_ALARM_ENABLE;
+		rc = qpnp_write_wrapper(rtc_dd, &ctrl_reg,
+			rtc_dd->alarm_base + REG_OFFSET_ALARM_CTRL1, 1);
+		if (rc) {
+			dev_err(dev, "Write to ALARM ctrl reg failed\n");
+			goto rtc_rw_fail;
+		}
+	} else
+		spin_unlock_irqrestore(&rtc_dd->alarm_ctrl_lock, irq_flags);
+
+	/*
+	 * 32 bit seconds value is coverted to four 8 bit values
+	 *	|<------  32 bit time value in seconds  ------>|
+	 *      <- 8 bit ->|<- 8 bit ->|<- 8 bit ->|<- 8 bit ->|
+	 *       ----------------------------------------------
+	 *      | BYTE[3]  |  BYTE[2]  |  BYTE[1]  |  BYTE[0]  |
+	 *       ----------------------------------------------
+	 *
+	 * RTC has four 8 bit registers for writing time in seconds:
+	 *             WDATA[3], WDATA[2], WDATA[1], WDATA[0]
+	 *
+	 * Write to the RTC registers should be done in following order
+	 * Clear WDATA[0] register
+	 *
+	 * Write BYTE[1], BYTE[2] and BYTE[3] of time to
+	 * RTC WDATA[3], WDATA[2], WDATA[1] registers
+	 *
+	 * Write BYTE[0] of time to RTC WDATA[0] register
+	 *
+	 * Clearing BYTE[0] and writing in the end will prevent any
+	 * unintentional overflow from WDATA[0] to higher bytes during the
+	 * write operation
+	 */
+
+	/* Disable RTC H/w before writing on RTC register*/
+	rtc_ctrl_reg = rtc_dd->rtc_ctrl_reg;
+	if (rtc_ctrl_reg & BIT_RTC_ENABLE) {
+		rtc_disabled = 1;
+		rtc_ctrl_reg &= ~BIT_RTC_ENABLE;
+		rc = qpnp_write_wrapper(rtc_dd, &rtc_ctrl_reg,
+				rtc_dd->rtc_base + REG_OFFSET_RTC_CTRL, 1);
+		if (rc) {
+			dev_err(dev, "Disabling of RTC control reg failed with error:%d\n",
+				rc);
+			goto rtc_rw_fail;
+		}
+		rtc_dd->rtc_ctrl_reg = rtc_ctrl_reg;
+	}
+
+	/* Clear WDATA[0] */
+	reg = 0x0;
+	rc = qpnp_write_wrapper(rtc_dd, &reg,
+				rtc_dd->rtc_base + REG_OFFSET_RTC_WRITE, 1);
+	if (rc) {
+		dev_err(dev, "Write to RTC reg failed\n");
+		goto rtc_rw_fail;
+	}
+
+	/* Write to WDATA[3], WDATA[2] and WDATA[1] */
+	rc = qpnp_write_wrapper(rtc_dd, &value[1],
+			rtc_dd->rtc_base + REG_OFFSET_RTC_WRITE + 1, 3);
+	if (rc) {
+		dev_err(dev, "Write to RTC reg failed\n");
+		goto rtc_rw_fail;
+	}
+
+	/* Write to WDATA[0] */
+	rc = qpnp_write_wrapper(rtc_dd, value,
+				rtc_dd->rtc_base + REG_OFFSET_RTC_WRITE, 1);
+	if (rc) {
+		dev_err(dev, "Write to RTC reg failed\n");
+		goto rtc_rw_fail;
+	}
+
+	/* Enable RTC H/w after writing on RTC register*/
+	if (rtc_disabled) {
+		rtc_ctrl_reg |= BIT_RTC_ENABLE;
+		rc = qpnp_write_wrapper(rtc_dd, &rtc_ctrl_reg,
+				rtc_dd->rtc_base + REG_OFFSET_RTC_CTRL, 1);
+		if (rc) {
+			dev_err(dev, "Enabling of RTC control reg failed with error:%d\n",
+				rc);
+			goto rtc_rw_fail;
+		}
+		rtc_dd->rtc_ctrl_reg = rtc_ctrl_reg;
+	}
+
+	if (alarm_enabled) {
+		ctrl_reg |= BIT_RTC_ALARM_ENABLE;
+		rc = qpnp_write_wrapper(rtc_dd, &ctrl_reg,
+			rtc_dd->alarm_base + REG_OFFSET_ALARM_CTRL1, 1);
+		if (rc) {
+			dev_err(dev, "Write to ALARM ctrl reg failed\n");
+			goto rtc_rw_fail;
+		}
+	}
+
+	rtc_dd->alarm_ctrl_reg1 = ctrl_reg;
+
+rtc_rw_fail:
+	if (alarm_enabled)
+		spin_unlock_irqrestore(&rtc_dd->alarm_ctrl_lock, irq_flags);
+
+	return rc;
+}
+
+static int
+qpnp_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+	int rc;
+	u8 value[4], reg;
+	unsigned long secs;
+	struct qpnp_rtc *rtc_dd = dev_get_drvdata(dev);
+
+	rc = qpnp_read_wrapper(rtc_dd, value,
+				rtc_dd->rtc_base + REG_OFFSET_RTC_READ,
+				NUM_8_BIT_RTC_REGS);
+	if (rc) {
+		dev_err(dev, "Read from RTC reg failed\n");
+		return rc;
+	}
+
+	/*
+	 * Read the LSB again and check if there has been a carry over
+	 * If there is, redo the read operation
+	 */
+	rc = qpnp_read_wrapper(rtc_dd, &reg,
+				rtc_dd->rtc_base + REG_OFFSET_RTC_READ, 1);
+	if (rc) {
+		dev_err(dev, "Read from RTC reg failed\n");
+		return rc;
+	}
+
+	if (reg < value[0]) {
+		rc = qpnp_read_wrapper(rtc_dd, value,
+				rtc_dd->rtc_base + REG_OFFSET_RTC_READ,
+				NUM_8_BIT_RTC_REGS);
+		if (rc) {
+			dev_err(dev, "Read from RTC reg failed\n");
+			return rc;
+		}
+	}
+
+	secs = TO_SECS(value);
+
+	rtc_time_to_tm(secs, tm);
+
+	rc = rtc_valid_tm(tm);
+	if (rc) {
+		dev_err(dev, "Invalid time read from RTC\n");
+		return rc;
+	}
+
+	dev_dbg(dev, "secs = %lu, h:m:s == %d:%d:%d, d/m/y = %d/%d/%d\n",
+			secs, tm->tm_hour, tm->tm_min, tm->tm_sec,
+			tm->tm_mday, tm->tm_mon, tm->tm_year);
+
+	return 0;
+}
+
+static int
+qpnp_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+	int rc;
+	u8 value[4], ctrl_reg;
+	unsigned long secs, secs_rtc, irq_flags;
+	struct qpnp_rtc *rtc_dd = dev_get_drvdata(dev);
+	struct rtc_time rtc_tm;
+
+	rtc_tm_to_time(&alarm->time, &secs);
+
+	/*
+	 * Read the current RTC time and verify if the alarm time is in the
+	 * past. If yes, return invalid
+	 */
+	rc = qpnp_rtc_read_time(dev, &rtc_tm);
+	if (rc) {
+		dev_err(dev, "Unable to read RTC time\n");
+		return -EINVAL;
+	}
+
+	rtc_tm_to_time(&rtc_tm, &secs_rtc);
+	if (secs < secs_rtc) {
+		dev_err(dev, "Trying to set alarm in the past\n");
+		return -EINVAL;
+	}
+
+	value[0] = secs & 0xFF;
+	value[1] = (secs >> 8) & 0xFF;
+	value[2] = (secs >> 16) & 0xFF;
+	value[3] = (secs >> 24) & 0xFF;
+
+	spin_lock_irqsave(&rtc_dd->alarm_ctrl_lock, irq_flags);
+
+	rc = qpnp_write_wrapper(rtc_dd, value,
+				rtc_dd->alarm_base + REG_OFFSET_ALARM_RW,
+				NUM_8_BIT_RTC_REGS);
+	if (rc) {
+		dev_err(dev, "Write to ALARM reg failed\n");
+		goto rtc_rw_fail;
+	}
+
+	ctrl_reg = (alarm->enabled) ?
+			(rtc_dd->alarm_ctrl_reg1 | BIT_RTC_ALARM_ENABLE) :
+			(rtc_dd->alarm_ctrl_reg1 & ~BIT_RTC_ALARM_ENABLE);
+
+	rc = qpnp_write_wrapper(rtc_dd, &ctrl_reg,
+			rtc_dd->alarm_base + REG_OFFSET_ALARM_CTRL1, 1);
+	if (rc) {
+		dev_err(dev, "Write to ALARM cntrol reg failed\n");
+		goto rtc_rw_fail;
+	}
+
+	rtc_dd->alarm_ctrl_reg1 = ctrl_reg;
+
+	dev_dbg(dev, "Alarm Set for h:r:s=%d:%d:%d, d/m/y=%d/%d/%d\n",
+			alarm->time.tm_hour, alarm->time.tm_min,
+			alarm->time.tm_sec, alarm->time.tm_mday,
+			alarm->time.tm_mon, alarm->time.tm_year);
+rtc_rw_fail:
+	spin_unlock_irqrestore(&rtc_dd->alarm_ctrl_lock, irq_flags);
+	return rc;
+}
+
+static int
+qpnp_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+	int rc;
+	u8 value[4];
+	unsigned long secs;
+	struct qpnp_rtc *rtc_dd = dev_get_drvdata(dev);
+
+	rc = qpnp_read_wrapper(rtc_dd, value,
+				rtc_dd->alarm_base + REG_OFFSET_ALARM_RW,
+				NUM_8_BIT_RTC_REGS);
+	if (rc) {
+		dev_err(dev, "Read from ALARM reg failed\n");
+		return rc;
+	}
+
+	secs = TO_SECS(value);
+	rtc_time_to_tm(secs, &alarm->time);
+
+	rc = rtc_valid_tm(&alarm->time);
+	if (rc) {
+		dev_err(dev, "Invalid time read from RTC\n");
+		return rc;
+	}
+
+	dev_dbg(dev, "Alarm set for - h:r:s=%d:%d:%d, d/m/y=%d/%d/%d\n",
+		alarm->time.tm_hour, alarm->time.tm_min,
+				alarm->time.tm_sec, alarm->time.tm_mday,
+				alarm->time.tm_mon, alarm->time.tm_year);
+
+	return 0;
+}
+
+
+static int
+qpnp_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+	int rc;
+	unsigned long irq_flags;
+	struct qpnp_rtc *rtc_dd = dev_get_drvdata(dev);
+	u8 ctrl_reg;
+	u8 value[4] = {0};
+
+	spin_lock_irqsave(&rtc_dd->alarm_ctrl_lock, irq_flags);
+	ctrl_reg = rtc_dd->alarm_ctrl_reg1;
+	ctrl_reg = enabled ? (ctrl_reg | BIT_RTC_ALARM_ENABLE) :
+				(ctrl_reg & ~BIT_RTC_ALARM_ENABLE);
+
+	rc = qpnp_write_wrapper(rtc_dd, &ctrl_reg,
+			rtc_dd->alarm_base + REG_OFFSET_ALARM_CTRL1, 1);
+	if (rc) {
+		dev_err(dev, "Write to ALARM control reg failed\n");
+		goto rtc_rw_fail;
+	}
+
+	rtc_dd->alarm_ctrl_reg1 = ctrl_reg;
+
+	/* Clear Alarm register */
+	if (!enabled) {
+		rc = qpnp_write_wrapper(rtc_dd, value,
+			rtc_dd->alarm_base + REG_OFFSET_ALARM_RW,
+			NUM_8_BIT_RTC_REGS);
+		if (rc)
+			dev_err(dev, "Clear ALARM value reg failed\n");
+	}
+
+rtc_rw_fail:
+	spin_unlock_irqrestore(&rtc_dd->alarm_ctrl_lock, irq_flags);
+	return rc;
+}
+
+static const struct rtc_class_ops qpnp_rtc_ro_ops = {
+	.read_time = qpnp_rtc_read_time,
+	.set_alarm = qpnp_rtc_set_alarm,
+	.read_alarm = qpnp_rtc_read_alarm,
+	.alarm_irq_enable = qpnp_rtc_alarm_irq_enable,
+};
+
+static const struct rtc_class_ops qpnp_rtc_rw_ops = {
+	.read_time = qpnp_rtc_read_time,
+	.set_alarm = qpnp_rtc_set_alarm,
+	.read_alarm = qpnp_rtc_read_alarm,
+	.alarm_irq_enable = qpnp_rtc_alarm_irq_enable,
+	.set_time = qpnp_rtc_set_time,
+};
+
+static irqreturn_t qpnp_alarm_trigger(int irq, void *dev_id)
+{
+	struct qpnp_rtc *rtc_dd = dev_id;
+	u8 ctrl_reg;
+	int rc;
+	unsigned long irq_flags;
+
+	rtc_update_irq(rtc_dd->rtc, 1, RTC_IRQF | RTC_AF);
+
+	spin_lock_irqsave(&rtc_dd->alarm_ctrl_lock, irq_flags);
+
+	/* Clear the alarm enable bit */
+	ctrl_reg = rtc_dd->alarm_ctrl_reg1;
+	ctrl_reg &= ~BIT_RTC_ALARM_ENABLE;
+
+	rc = qpnp_write_wrapper(rtc_dd, &ctrl_reg,
+			rtc_dd->alarm_base + REG_OFFSET_ALARM_CTRL1, 1);
+	if (rc) {
+		spin_unlock_irqrestore(&rtc_dd->alarm_ctrl_lock, irq_flags);
+		dev_err(rtc_dd->rtc_dev,
+				"Write to ALARM control reg failed\n");
+		goto rtc_alarm_handled;
+	}
+
+	rtc_dd->alarm_ctrl_reg1 = ctrl_reg;
+	spin_unlock_irqrestore(&rtc_dd->alarm_ctrl_lock, irq_flags);
+
+	/* Set ALARM_CLR bit */
+	ctrl_reg = 0x1;
+	rc = qpnp_write_wrapper(rtc_dd, &ctrl_reg,
+			rtc_dd->alarm_base + REG_OFFSET_ALARM_CTRL2, 1);
+	if (rc)
+		dev_err(rtc_dd->rtc_dev,
+				"Write to ALARM control reg failed\n");
+
+rtc_alarm_handled:
+	return IRQ_HANDLED;
+}
+
+static int qpnp_rtc_probe(struct platform_device *pdev)
+{
+	const struct rtc_class_ops *rtc_ops = &qpnp_rtc_ro_ops;
+	int rc;
+	u8 subtype;
+	struct qpnp_rtc *rtc_dd;
+	unsigned int base;
+	struct device_node *child;
+
+	rtc_dd = devm_kzalloc(&pdev->dev, sizeof(*rtc_dd), GFP_KERNEL);
+	if (rtc_dd == NULL)
+		return -ENOMEM;
+
+	rtc_dd->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+	if (!rtc_dd->regmap) {
+		dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
+		return -EINVAL;
+	}
+
+	/* Get the rtc write property */
+	rc = of_property_read_u32(pdev->dev.of_node, "qcom,qpnp-rtc-write",
+						&rtc_dd->rtc_write_enable);
+	if (rc && rc != -EINVAL) {
+		dev_err(&pdev->dev,
+			"Error reading rtc_write_enable property %d\n", rc);
+		return rc;
+	}
+
+	rc = of_property_read_u32(pdev->dev.of_node,
+						"qcom,qpnp-rtc-alarm-pwrup",
+						&rtc_dd->rtc_alarm_powerup);
+	if (rc && rc != -EINVAL) {
+		dev_err(&pdev->dev,
+			"Error reading rtc_alarm_powerup property %d\n", rc);
+		return rc;
+	}
+
+	/* Initialise spinlock to protect RTC control register */
+	spin_lock_init(&rtc_dd->alarm_ctrl_lock);
+
+	rtc_dd->rtc_dev = &(pdev->dev);
+	rtc_dd->pdev = pdev;
+
+
+	if (of_get_available_child_count(pdev->dev.of_node) == 0) {
+		pr_err("no child nodes\n");
+		rc = -ENXIO;
+		goto fail_rtc_enable;
+	}
+
+	/* Get RTC/ALARM resources */
+	for_each_available_child_of_node(pdev->dev.of_node, child) {
+		rc = of_property_read_u32(child, "reg", &base);
+		if (rc < 0) {
+			dev_err(&pdev->dev,
+				"Couldn't find reg in node = %s rc = %d\n",
+				child->full_name, rc);
+			goto fail_rtc_enable;
+		}
+
+		rc = qpnp_read_wrapper(rtc_dd, &subtype,
+				base + REG_OFFSET_PERP_SUBTYPE, 1);
+		if (rc) {
+			dev_err(&pdev->dev,
+				"Peripheral subtype read failed\n");
+			goto fail_rtc_enable;
+		}
+
+		switch (subtype) {
+		case RTC_PERPH_SUBTYPE:
+			rtc_dd->rtc_base = base;
+			break;
+		case ALARM_PERPH_SUBTYPE:
+			rtc_dd->alarm_base = base;
+			rtc_dd->rtc_alarm_irq = of_irq_get(child, 0);
+			if (rtc_dd->rtc_alarm_irq < 0) {
+				dev_err(&pdev->dev, "ALARM IRQ absent\n");
+				rc = -ENXIO;
+				goto fail_rtc_enable;
+			}
+			break;
+		default:
+			dev_err(&pdev->dev, "Invalid peripheral subtype\n");
+			rc = -EINVAL;
+			goto fail_rtc_enable;
+		}
+	}
+
+	rc = qpnp_read_wrapper(rtc_dd, &rtc_dd->rtc_ctrl_reg,
+				rtc_dd->rtc_base + REG_OFFSET_RTC_CTRL, 1);
+	if (rc) {
+		dev_err(&pdev->dev, "Read from RTC control reg failed\n");
+		goto fail_rtc_enable;
+	}
+
+	if (!(rtc_dd->rtc_ctrl_reg & BIT_RTC_ENABLE)) {
+		dev_err(&pdev->dev, "RTC h/w disabled, rtc not registered\n");
+		goto fail_rtc_enable;
+	}
+
+	rc = qpnp_read_wrapper(rtc_dd, &rtc_dd->alarm_ctrl_reg1,
+				rtc_dd->alarm_base + REG_OFFSET_ALARM_CTRL1, 1);
+	if (rc) {
+		dev_err(&pdev->dev, "Read from  Alarm control reg failed\n");
+		goto fail_rtc_enable;
+	}
+	/* Enable abort enable feature */
+	rtc_dd->alarm_ctrl_reg1 |= BIT_RTC_ABORT_ENABLE;
+	rc = qpnp_write_wrapper(rtc_dd, &rtc_dd->alarm_ctrl_reg1,
+			rtc_dd->alarm_base + REG_OFFSET_ALARM_CTRL1, 1);
+	if (rc) {
+		dev_err(&pdev->dev, "SPMI write failed!\n");
+		goto fail_rtc_enable;
+	}
+
+	if (rtc_dd->rtc_write_enable == true)
+		rtc_ops = &qpnp_rtc_rw_ops;
+
+	dev_set_drvdata(&pdev->dev, rtc_dd);
+
+	/* Register the RTC device */
+	rtc_dd->rtc = rtc_device_register("qpnp_rtc", &pdev->dev,
+					  rtc_ops, THIS_MODULE);
+	if (IS_ERR(rtc_dd->rtc)) {
+		dev_err(&pdev->dev, "%s: RTC registration failed (%ld)\n",
+					__func__, PTR_ERR(rtc_dd->rtc));
+		rc = PTR_ERR(rtc_dd->rtc);
+		goto fail_rtc_enable;
+	}
+
+	/* Request the alarm IRQ */
+	rc = request_any_context_irq(rtc_dd->rtc_alarm_irq,
+				 qpnp_alarm_trigger, IRQF_TRIGGER_RISING,
+				 "qpnp_rtc_alarm", rtc_dd);
+	if (rc) {
+		dev_err(&pdev->dev, "Request IRQ failed (%d)\n", rc);
+		goto fail_req_irq;
+	}
+
+	device_init_wakeup(&pdev->dev, 1);
+	enable_irq_wake(rtc_dd->rtc_alarm_irq);
+
+	dev_dbg(&pdev->dev, "Probe success !!\n");
+
+	return 0;
+
+fail_req_irq:
+	rtc_device_unregister(rtc_dd->rtc);
+fail_rtc_enable:
+	dev_set_drvdata(&pdev->dev, NULL);
+
+	return rc;
+}
+
+static int qpnp_rtc_remove(struct platform_device *pdev)
+{
+	struct qpnp_rtc *rtc_dd = dev_get_drvdata(&pdev->dev);
+
+	device_init_wakeup(&pdev->dev, 0);
+	free_irq(rtc_dd->rtc_alarm_irq, rtc_dd);
+	rtc_device_unregister(rtc_dd->rtc);
+	dev_set_drvdata(&pdev->dev, NULL);
+
+	return 0;
+}
+
+static void qpnp_rtc_shutdown(struct platform_device *pdev)
+{
+	u8 value[4] = {0};
+	u8 reg;
+	int rc;
+	unsigned long irq_flags;
+	struct qpnp_rtc *rtc_dd;
+	bool rtc_alarm_powerup;
+
+	if (!pdev) {
+		pr_err("qpnp-rtc: spmi device not found\n");
+		return;
+	}
+	rtc_dd = dev_get_drvdata(&pdev->dev);
+	if (!rtc_dd) {
+		pr_err("qpnp-rtc: rtc driver data not found\n");
+		return;
+	}
+	rtc_alarm_powerup = rtc_dd->rtc_alarm_powerup;
+	if (!rtc_alarm_powerup && !poweron_alarm) {
+		spin_lock_irqsave(&rtc_dd->alarm_ctrl_lock, irq_flags);
+		dev_dbg(&pdev->dev, "Disabling alarm interrupts\n");
+
+		/* Disable RTC alarms */
+		reg = rtc_dd->alarm_ctrl_reg1;
+		reg &= ~BIT_RTC_ALARM_ENABLE;
+		rc = qpnp_write_wrapper(rtc_dd, &reg,
+			rtc_dd->alarm_base + REG_OFFSET_ALARM_CTRL1, 1);
+		if (rc) {
+			dev_err(rtc_dd->rtc_dev, "SPMI write failed\n");
+			goto fail_alarm_disable;
+		}
+
+		/* Clear Alarm register */
+		rc = qpnp_write_wrapper(rtc_dd, value,
+				rtc_dd->alarm_base + REG_OFFSET_ALARM_RW,
+				NUM_8_BIT_RTC_REGS);
+		if (rc)
+			dev_err(rtc_dd->rtc_dev, "SPMI write failed\n");
+
+fail_alarm_disable:
+		spin_unlock_irqrestore(&rtc_dd->alarm_ctrl_lock, irq_flags);
+	}
+}
+
+static const struct of_device_id spmi_match_table[] = {
+	{
+		.compatible = "qcom,qpnp-rtc",
+	},
+	{}
+};
+
+static struct platform_driver qpnp_rtc_driver = {
+	.probe		= qpnp_rtc_probe,
+	.remove		= qpnp_rtc_remove,
+	.shutdown	= qpnp_rtc_shutdown,
+	.driver		= {
+		.name		= "qcom,qpnp-rtc",
+		.owner		= THIS_MODULE,
+		.of_match_table	= spmi_match_table,
+	},
+};
+
+static int __init qpnp_rtc_init(void)
+{
+	return platform_driver_register(&qpnp_rtc_driver);
+}
+module_init(qpnp_rtc_init);
+
+static void __exit qpnp_rtc_exit(void)
+{
+	platform_driver_unregister(&qpnp_rtc_driver);
+}
+module_exit(qpnp_rtc_exit);
+
+MODULE_DESCRIPTION("SPMI PMIC RTC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 07002df..322eb13 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -460,6 +460,16 @@
 	  to this driver. This driver reports the temperature by reading ADC
 	  channel and converts it to temperature based on lookup table.
 
+config THERMAL_TSENS
+	tristate "Qualcomm Technologies Inc. TSENS Temperature driver"
+	depends on THERMAL
+	help
+	  This enables the thermal sysfs driver for the TSENS device. It shows
+	  up in Sysfs as a thermal zone with multiple trip points. Also able
+	  to set threshold temperature for both warm and cool and update
+	  thermal userspace client when a threshold is reached. Warm/Cool
+	  temperature thresholds can be set independently for each sensor.
+
 menu "Qualcomm thermal drivers"
 depends on (ARCH_QCOM && OF) || COMPILE_TEST
 source "drivers/thermal/qcom/Kconfig"
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index 195cd08..6af08f9 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -61,3 +61,4 @@
 obj-$(CONFIG_GENERIC_ADC_THERMAL)	+= thermal-generic-adc.o
 obj-$(CONFIG_ZX2967_THERMAL)	+= zx2967_thermal.o
 obj-$(CONFIG_UNIPHIER_THERMAL)	+= uniphier_thermal.o
+obj-$(CONFIG_THERMAL_TSENS)	+= msm-tsens.o tsens2xxx.o tsens-dbg.o
diff --git a/drivers/thermal/msm-tsens.c b/drivers/thermal/msm-tsens.c
new file mode 100644
index 0000000..abaf8bb6
--- /dev/null
+++ b/drivers/thermal/msm-tsens.c
@@ -0,0 +1,261 @@
+/* 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 <linux/err.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/thermal.h>
+#include "tsens.h"
+
+LIST_HEAD(tsens_device_list);
+
+static int tsens_get_temp(void *data, int *temp)
+{
+	struct tsens_sensor *s = data;
+	struct tsens_device *tmdev = s->tmdev;
+
+	return tmdev->ops->get_temp(s, temp);
+}
+
+static int tsens_set_trip_temp(void *data, int low_temp, int high_temp)
+{
+	struct tsens_sensor *s = data;
+	struct tsens_device *tmdev = s->tmdev;
+
+	if (tmdev->ops->set_trips)
+		return tmdev->ops->set_trips(s, low_temp, high_temp);
+
+	return 0;
+}
+
+static int tsens_init(struct tsens_device *tmdev)
+{
+	return tmdev->ops->hw_init(tmdev);
+}
+
+static int tsens_register_interrupts(struct tsens_device *tmdev)
+{
+	if (tmdev->ops->interrupts_reg)
+		return tmdev->ops->interrupts_reg(tmdev);
+
+	return 0;
+}
+
+static const struct of_device_id tsens_table[] = {
+	{	.compatible = "qcom,msm8996-tsens",
+		.data = &data_tsens2xxx,
+	},
+	{	.compatible = "qcom,msm8953-tsens",
+		.data = &data_tsens2xxx,
+	},
+	{	.compatible = "qcom,msm8998-tsens",
+		.data = &data_tsens2xxx,
+	},
+	{	.compatible = "qcom,msmhamster-tsens",
+		.data = &data_tsens2xxx,
+	},
+	{	.compatible = "qcom,sdm660-tsens",
+		.data = &data_tsens23xx,
+	},
+	{	.compatible = "qcom,sdm630-tsens",
+		.data = &data_tsens23xx,
+	},
+	{	.compatible = "qcom,sdm845-tsens",
+		.data = &data_tsens24xx,
+	},
+	{	.compatible = "qcom,tsens24xx",
+		.data = &data_tsens24xx,
+	},
+	{}
+};
+MODULE_DEVICE_TABLE(of, tsens_table);
+
+static struct thermal_zone_of_device_ops tsens_tm_thermal_zone_ops = {
+	.get_temp = tsens_get_temp,
+	.set_trips = tsens_set_trip_temp,
+};
+
+static int get_device_tree_data(struct platform_device *pdev,
+				struct tsens_device *tmdev)
+{
+	struct device_node *of_node = pdev->dev.of_node;
+	const struct of_device_id *id;
+	const struct tsens_data *data;
+	struct resource *res_tsens_mem;
+
+	if (!of_match_node(tsens_table, of_node)) {
+		pr_err("Need to read SoC specific fuse map\n");
+		return -ENODEV;
+	}
+
+	id = of_match_node(tsens_table, of_node);
+	if (id == NULL) {
+		pr_err("can not find tsens_table of_node\n");
+		return -ENODEV;
+	}
+
+	data = id->data;
+	tmdev->ops = data->ops;
+	tmdev->ctrl_data = data;
+	tmdev->pdev = pdev;
+
+	if (!tmdev->ops || !tmdev->ops->hw_init || !tmdev->ops->get_temp) {
+		pr_err("Invalid ops\n");
+		return -EINVAL;
+	}
+
+	/* TSENS register region */
+	res_tsens_mem = platform_get_resource_byname(pdev,
+				IORESOURCE_MEM, "tsens_srot_physical");
+	if (!res_tsens_mem) {
+		pr_err("Could not get tsens physical address resource\n");
+		return -EINVAL;
+	}
+
+	tmdev->tsens_srot_addr = devm_ioremap_resource(&pdev->dev,
+							res_tsens_mem);
+	if (IS_ERR(tmdev->tsens_srot_addr)) {
+		dev_err(&pdev->dev, "Failed to IO map TSENS registers.\n");
+		return PTR_ERR(tmdev->tsens_srot_addr);
+	}
+
+	/* TSENS TM register region */
+	res_tsens_mem = platform_get_resource_byname(pdev,
+				IORESOURCE_MEM, "tsens_tm_physical");
+	if (!res_tsens_mem) {
+		pr_err("Could not get tsens physical address resource\n");
+		return -EINVAL;
+	}
+
+	tmdev->tsens_tm_addr = devm_ioremap_resource(&pdev->dev,
+								res_tsens_mem);
+	if (IS_ERR(tmdev->tsens_tm_addr)) {
+		dev_err(&pdev->dev, "Failed to IO map TSENS TM registers.\n");
+		return PTR_ERR(tmdev->tsens_tm_addr);
+	}
+
+	return 0;
+}
+
+static int tsens_thermal_zone_register(struct tsens_device *tmdev)
+{
+	int i = 0, sensor_missing = 0;
+
+	for (i = 0; i < TSENS_MAX_SENSORS; i++) {
+		tmdev->sensor[i].tmdev = tmdev;
+		tmdev->sensor[i].hw_id = i;
+		if (tmdev->ops->sensor_en(tmdev, i)) {
+			tmdev->sensor[i].tzd =
+				devm_thermal_zone_of_sensor_register(
+				&tmdev->pdev->dev, i,
+				&tmdev->sensor[i], &tsens_tm_thermal_zone_ops);
+			if (IS_ERR(tmdev->sensor[i].tzd)) {
+				pr_debug("Error registering sensor:%d\n", i);
+				sensor_missing++;
+				continue;
+			}
+		} else {
+			pr_debug("Sensor not enabled:%d\n", i);
+		}
+	}
+
+	if (sensor_missing == TSENS_MAX_SENSORS) {
+		pr_err("No TSENS sensors to register?\n");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static int tsens_tm_remove(struct platform_device *pdev)
+{
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+int tsens_tm_probe(struct platform_device *pdev)
+{
+	struct tsens_device *tmdev = NULL;
+	int rc;
+
+	if (!(pdev->dev.of_node))
+		return -ENODEV;
+
+	tmdev = devm_kzalloc(&pdev->dev,
+			sizeof(struct tsens_device) +
+			TSENS_MAX_SENSORS *
+			sizeof(struct tsens_sensor),
+			GFP_KERNEL);
+	if (tmdev == NULL)
+		return -ENOMEM;
+
+	rc = get_device_tree_data(pdev, tmdev);
+	if (rc) {
+		pr_err("Error reading TSENS DT\n");
+		return rc;
+	}
+
+	rc = tsens_init(tmdev);
+	if (rc) {
+		pr_err("Error initializing TSENS controller\n");
+		return rc;
+	}
+
+	rc = tsens_thermal_zone_register(tmdev);
+	if (rc) {
+		pr_err("Error registering the thermal zone\n");
+		return rc;
+	}
+
+	rc = tsens_register_interrupts(tmdev);
+	if (rc < 0) {
+		pr_err("TSENS interrupt register failed:%d\n", rc);
+		return rc;
+	}
+
+	list_add_tail(&tmdev->list, &tsens_device_list);
+	platform_set_drvdata(pdev, tmdev);
+
+	return rc;
+}
+
+static struct platform_driver tsens_tm_driver = {
+	.probe = tsens_tm_probe,
+	.remove = tsens_tm_remove,
+	.driver = {
+		.name = "msm-tsens",
+		.owner = THIS_MODULE,
+		.of_match_table = tsens_table,
+	},
+};
+
+int __init tsens_tm_init_driver(void)
+{
+	return platform_driver_register(&tsens_tm_driver);
+}
+subsys_initcall(tsens_tm_init_driver);
+
+static void __exit tsens_tm_deinit(void)
+{
+	platform_driver_unregister(&tsens_tm_driver);
+}
+module_exit(tsens_tm_deinit);
+
+MODULE_ALIAS("platform:" TSENS_DRIVER_NAME);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/thermal/tsens-dbg.c b/drivers/thermal/tsens-dbg.c
new file mode 100644
index 0000000..4beefe8
--- /dev/null
+++ b/drivers/thermal/tsens-dbg.c
@@ -0,0 +1,221 @@
+/* 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 <asm/arch_timer.h>
+#include <linux/sched/clock.h>
+#include "tsens.h"
+
+/* debug defines */
+#define	TSENS_DBG_BUS_ID_0			0
+#define	TSENS_DBG_BUS_ID_1			1
+#define	TSENS_DBG_BUS_ID_2			2
+#define	TSENS_DBG_BUS_ID_15			15
+#define	TSENS_DEBUG_LOOP_COUNT_ID_0		2
+#define	TSENS_DEBUG_LOOP_COUNT			5
+#define	TSENS_DEBUG_STATUS_REG_START		10
+#define	TSENS_DEBUG_OFFSET_RANGE		16
+#define	TSENS_DEBUG_OFFSET_WORD1		0x4
+#define	TSENS_DEBUG_OFFSET_WORD2		0x8
+#define	TSENS_DEBUG_OFFSET_WORD3		0xc
+#define	TSENS_DEBUG_OFFSET_ROW			0x10
+#define	TSENS_DEBUG_DECIDEGC			-950
+#define	TSENS_DEBUG_CYCLE_MS			64
+#define	TSENS_DEBUG_POLL_MS			200
+#define	TSENS_DEBUG_BUS_ID2_MIN_CYCLE		50
+#define	TSENS_DEBUG_BUS_ID2_MAX_CYCLE		51
+#define	TSENS_DEBUG_ID_MASK_1_4			0xffffffe1
+#define	DEBUG_SIZE				10
+
+#define TSENS_DEBUG_CONTROL(n)			((n) + 0x130)
+#define TSENS_DEBUG_DATA(n)			((n) + 0x134)
+
+struct tsens_dbg_func {
+	int (*dbg_func)(struct tsens_device *, u32, u32, int *);
+};
+
+static int tsens_dbg_log_temp_reads(struct tsens_device *data, u32 id,
+					u32 dbg_type, int *temp)
+{
+	struct tsens_sensor *sensor;
+	struct tsens_device *tmdev = NULL;
+	u32 idx = 0;
+
+	if (!data)
+		return -EINVAL;
+
+	pr_debug("%d %d\n", id, dbg_type);
+	tmdev = data;
+	sensor = &tmdev->sensor[id];
+	idx = tmdev->tsens_dbg.sensor_dbg_info[sensor->hw_id].idx;
+	tmdev->tsens_dbg.sensor_dbg_info[sensor->hw_id].temp[idx%10] = *temp;
+	tmdev->tsens_dbg.sensor_dbg_info[sensor->hw_id].time_stmp[idx%10] =
+					sched_clock();
+	idx++;
+	tmdev->tsens_dbg.sensor_dbg_info[sensor->hw_id].idx = idx;
+
+	return 0;
+}
+
+static int tsens_dbg_log_interrupt_timestamp(struct tsens_device *data,
+						u32 id, u32 dbg_type, int *val)
+{
+	struct tsens_device *tmdev = NULL;
+	u32 idx = 0;
+
+	if (!data)
+		return -EINVAL;
+
+	pr_debug("%d %d\n", id, dbg_type);
+	tmdev = data;
+	/* debug */
+	idx = tmdev->tsens_dbg.irq_idx;
+	tmdev->tsens_dbg.irq_time_stmp[idx%10] =
+							sched_clock();
+	tmdev->tsens_dbg.irq_idx++;
+
+	return 0;
+}
+
+static int tsens_dbg_log_bus_id_data(struct tsens_device *data,
+					u32 id, u32 dbg_type, int *val)
+{
+	struct tsens_device *tmdev = NULL;
+	u32 loop = 0, i = 0;
+	uint32_t r1, r2, r3, r4, offset = 0;
+	unsigned int debug_dump;
+	unsigned int debug_id = 0, cntrl_id = 0;
+	void __iomem *srot_addr;
+	void __iomem *controller_id_addr;
+	void __iomem *debug_id_addr;
+	void __iomem *debug_data_addr;
+
+	if (!data)
+		return -EINVAL;
+
+	pr_debug("%d %d\n", id, dbg_type);
+	tmdev = data;
+	controller_id_addr = TSENS_CONTROLLER_ID(tmdev->tsens_tm_addr);
+	debug_id_addr = TSENS_DEBUG_CONTROL(tmdev->tsens_tm_addr);
+	debug_data_addr = TSENS_DEBUG_DATA(tmdev->tsens_tm_addr);
+	srot_addr = TSENS_CTRL_ADDR(tmdev->tsens_srot_addr);
+
+	cntrl_id = readl_relaxed(controller_id_addr);
+	pr_err("Controller_id: 0x%x\n", cntrl_id);
+
+	loop = 0;
+	i = 0;
+	debug_id = readl_relaxed(debug_id_addr);
+	writel_relaxed((debug_id | (i << 1) | 1),
+			TSENS_DEBUG_CONTROL(tmdev->tsens_tm_addr));
+	while (loop < TSENS_DEBUG_LOOP_COUNT_ID_0) {
+		debug_dump = readl_relaxed(debug_data_addr);
+		r1 = readl_relaxed(debug_data_addr);
+		r2 = readl_relaxed(debug_data_addr);
+		r3 = readl_relaxed(debug_data_addr);
+		r4 = readl_relaxed(debug_data_addr);
+		pr_err("cntrl:%d, bus-id:%d value:0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n",
+			cntrl_id, i, debug_dump, r1, r2, r3, r4);
+		loop++;
+	}
+
+	for (i = TSENS_DBG_BUS_ID_1; i <= TSENS_DBG_BUS_ID_15; i++) {
+		loop = 0;
+		debug_id = readl_relaxed(debug_id_addr);
+		debug_id = debug_id & TSENS_DEBUG_ID_MASK_1_4;
+		writel_relaxed((debug_id | (i << 1) | 1),
+				TSENS_DEBUG_CONTROL(tmdev->tsens_tm_addr));
+		while (loop < TSENS_DEBUG_LOOP_COUNT) {
+			debug_dump = readl_relaxed(debug_data_addr);
+			pr_err("cntrl:%d, bus-id:%d with value: 0x%x\n",
+				cntrl_id, i, debug_dump);
+			if (i == TSENS_DBG_BUS_ID_2)
+				usleep_range(
+					TSENS_DEBUG_BUS_ID2_MIN_CYCLE,
+					TSENS_DEBUG_BUS_ID2_MAX_CYCLE);
+			loop++;
+		}
+	}
+
+	pr_err("Start of TSENS TM dump\n");
+	for (i = 0; i < TSENS_DEBUG_OFFSET_RANGE; i++) {
+		r1 = readl_relaxed(controller_id_addr + offset);
+		r2 = readl_relaxed(controller_id_addr + (offset +
+					TSENS_DEBUG_OFFSET_WORD1));
+		r3 = readl_relaxed(controller_id_addr +	(offset +
+					TSENS_DEBUG_OFFSET_WORD2));
+		r4 = readl_relaxed(controller_id_addr + (offset +
+					TSENS_DEBUG_OFFSET_WORD3));
+
+		pr_err("ctrl:%d:0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",
+			cntrl_id, offset, r1, r2, r3, r4);
+		offset += TSENS_DEBUG_OFFSET_ROW;
+	}
+
+	offset = 0;
+	pr_err("Start of TSENS SROT dump\n");
+	for (i = 0; i < TSENS_DEBUG_OFFSET_RANGE; i++) {
+		r1 = readl_relaxed(srot_addr + offset);
+		r2 = readl_relaxed(srot_addr + (offset +
+					TSENS_DEBUG_OFFSET_WORD1));
+		r3 = readl_relaxed(srot_addr + (offset +
+					TSENS_DEBUG_OFFSET_WORD2));
+		r4 = readl_relaxed(srot_addr + (offset +
+					TSENS_DEBUG_OFFSET_WORD3));
+
+		pr_err("ctrl:%d:0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",
+			cntrl_id, offset, r1, r2, r3, r4);
+		offset += TSENS_DEBUG_OFFSET_ROW;
+	}
+
+	loop = 0;
+	while (loop < TSENS_DEBUG_LOOP_COUNT) {
+		offset = TSENS_DEBUG_OFFSET_ROW *
+				TSENS_DEBUG_STATUS_REG_START;
+		pr_err("Start of TSENS TM dump %d\n", loop);
+		/* Limited dump of the registers for the temperature */
+		for (i = 0; i < TSENS_DEBUG_LOOP_COUNT; i++) {
+			r1 = readl_relaxed(controller_id_addr + offset);
+			r2 = readl_relaxed(controller_id_addr +
+				(offset + TSENS_DEBUG_OFFSET_WORD1));
+			r3 = readl_relaxed(controller_id_addr +
+				(offset + TSENS_DEBUG_OFFSET_WORD2));
+			r4 = readl_relaxed(controller_id_addr +
+				(offset + TSENS_DEBUG_OFFSET_WORD3));
+
+		pr_err("ctrl:%d:0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",
+			cntrl_id, offset, r1, r2, r3, r4);
+			offset += TSENS_DEBUG_OFFSET_ROW;
+		}
+		loop++;
+	}
+
+	return 0;
+}
+
+static struct tsens_dbg_func dbg_arr[] = {
+	[TSENS_DBG_LOG_TEMP_READS] = {tsens_dbg_log_temp_reads},
+	[TSENS_DBG_LOG_INTERRUPT_TIMESTAMP] = {
+			tsens_dbg_log_interrupt_timestamp},
+	[TSENS_DBG_LOG_BUS_ID_DATA] = {tsens_dbg_log_bus_id_data},
+};
+
+int tsens2xxx_dbg(struct tsens_device *data, u32 id, u32 dbg_type, int *val)
+{
+	if (dbg_type >= TSENS_DBG_LOG_MAX)
+		return -EINVAL;
+
+	dbg_arr[dbg_type].dbg_func(data, id, dbg_type, val);
+
+	return 0;
+}
+EXPORT_SYMBOL(tsens2xxx_dbg);
diff --git a/drivers/thermal/tsens.h b/drivers/thermal/tsens.h
new file mode 100644
index 0000000..ec2d592
--- /dev/null
+++ b/drivers/thermal/tsens.h
@@ -0,0 +1,137 @@
+/* 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.
+ *
+ */
+#ifndef __QCOM_TSENS_H__
+#define __QCOM_TSENS_H__
+
+#include <linux/kernel.h>
+#include <linux/thermal.h>
+#include <linux/interrupt.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+
+#define DEBUG_SIZE				10
+#define TSENS_MAX_SENSORS			16
+#define TSENS_CONTROLLER_ID(n)			(n)
+#define TSENS_CTRL_ADDR(n)			(n)
+#define TSENS_TM_SN_STATUS(n)			((n) + 0xa0)
+
+enum tsens_dbg_type {
+	TSENS_DBG_POLL,
+	TSENS_DBG_LOG_TEMP_READS,
+	TSENS_DBG_LOG_INTERRUPT_TIMESTAMP,
+	TSENS_DBG_LOG_BUS_ID_DATA,
+	TSENS_DBG_LOG_MAX
+};
+
+#define tsens_sec_to_msec_value		1000
+
+struct tsens_device;
+
+#if defined(CONFIG_THERMAL_TSENS)
+int tsens2xxx_dbg(struct tsens_device *data, u32 id, u32 dbg_type, int *temp);
+#else
+static inline int tsens2xxx_dbg(struct tsens_device *data, u32 id,
+						u32 dbg_type, int *temp)
+{ return -ENXIO; }
+#endif
+
+struct tsens_dbg {
+	u32				idx;
+	unsigned long long		time_stmp[DEBUG_SIZE];
+	unsigned long			temp[DEBUG_SIZE];
+};
+
+struct tsens_dbg_context {
+	struct tsens_device		*tmdev;
+	struct tsens_dbg		sensor_dbg_info[TSENS_MAX_SENSORS];
+	int				tsens_critical_wd_cnt;
+	u32				irq_idx;
+	unsigned long long		irq_time_stmp[DEBUG_SIZE];
+	struct delayed_work		tsens_critical_poll_test;
+};
+
+struct tsens_context {
+	enum thermal_device_mode	high_th_state;
+	enum thermal_device_mode	low_th_state;
+	enum thermal_device_mode	crit_th_state;
+	int				high_temp;
+	int				low_temp;
+	int				crit_temp;
+};
+
+struct tsens_sensor {
+	struct tsens_device		*tmdev;
+	struct thermal_zone_device	*tzd;
+	u32				hw_id;
+	u32				id;
+	const char			*sensor_name;
+	struct tsens_context		thr_state;
+};
+
+/**
+ * struct tsens_ops - operations as supported by the tsens device
+ * @init: Function to initialize the tsens device
+ * @get_temp: Function which returns the temp in millidegC
+ */
+struct tsens_ops {
+	int (*hw_init)(struct tsens_device *);
+	int (*get_temp)(struct tsens_sensor *, int *);
+	int (*set_trips)(struct tsens_sensor *, int, int);
+	int (*interrupts_reg)(struct tsens_device *);
+	int (*dbg)(struct tsens_device *, u32, u32, int *);
+	int (*sensor_en)(struct tsens_device *, u32);
+};
+
+struct tsens_irqs {
+	const char			*name;
+	irqreturn_t (*handler)(int, void *);
+};
+
+/**
+ * struct tsens_data - tsens instance specific data
+ * @num_sensors: Max number of sensors supported by platform
+ * @ops: operations the tsens instance supports
+ * @hw_ids: Subset of sensors ids supported by platform, if not the first n
+ */
+struct tsens_data {
+	const u32			num_sensors;
+	const struct tsens_ops		*ops;
+	unsigned int			*hw_ids;
+	u32				temp_factor;
+	bool				cycle_monitor;
+	u32				cycle_compltn_monitor_mask;
+	bool				wd_bark;
+	u32				wd_bark_mask;
+};
+
+struct tsens_device {
+	struct device			*dev;
+	struct platform_device		*pdev;
+	struct list_head		list;
+	struct regmap			*map;
+	struct regmap_field		*status_field;
+	void __iomem			*tsens_srot_addr;
+	void __iomem			*tsens_tm_addr;
+	const struct tsens_ops		*ops;
+	struct tsens_dbg_context	tsens_dbg;
+	spinlock_t			tsens_crit_lock;
+	spinlock_t			tsens_upp_low_lock;
+	const struct tsens_data		*ctrl_data;
+	struct tsens_sensor		sensor[0];
+};
+
+extern const struct tsens_data data_tsens2xxx, data_tsens23xx, data_tsens24xx;
+
+#endif /* __QCOM_TSENS_H__ */
diff --git a/drivers/thermal/tsens2xxx.c b/drivers/thermal/tsens2xxx.c
new file mode 100644
index 0000000..660c8ea
--- /dev/null
+++ b/drivers/thermal/tsens2xxx.c
@@ -0,0 +1,642 @@
+/* Copyright (c) 2012-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 <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/vmalloc.h>
+#include "tsens.h"
+#include "thermal_core.h"
+
+#define TSENS_DRIVER_NAME			"msm-tsens"
+
+#define TSENS_TM_INT_EN(n)			((n) + 0x4)
+#define TSENS_TM_CRITICAL_INT_STATUS(n)		((n) + 0x14)
+#define TSENS_TM_CRITICAL_INT_CLEAR(n)		((n) + 0x18)
+#define TSENS_TM_CRITICAL_INT_MASK(n)		((n) + 0x1c)
+#define TSENS_TM_CRITICAL_WD_BARK		BIT(31)
+#define TSENS_TM_CRITICAL_CYCLE_MONITOR		BIT(30)
+#define TSENS_TM_CRITICAL_INT_EN		BIT(2)
+#define TSENS_TM_UPPER_INT_EN			BIT(1)
+#define TSENS_TM_LOWER_INT_EN			BIT(0)
+#define TSENS_TM_SN_UPPER_LOWER_THRESHOLD(n)	((n) + 0x20)
+#define TSENS_TM_SN_ADDR_OFFSET			0x4
+#define TSENS_TM_UPPER_THRESHOLD_SET(n)		((n) << 12)
+#define TSENS_TM_UPPER_THRESHOLD_VALUE_SHIFT(n)	((n) >> 12)
+#define TSENS_TM_LOWER_THRESHOLD_VALUE(n)	((n) & 0xfff)
+#define TSENS_TM_UPPER_THRESHOLD_VALUE(n)	(((n) & 0xfff000) >> 12)
+#define TSENS_TM_UPPER_THRESHOLD_MASK		0xfff000
+#define TSENS_TM_LOWER_THRESHOLD_MASK		0xfff
+#define TSENS_TM_UPPER_THRESHOLD_SHIFT		12
+#define TSENS_TM_SN_CRITICAL_THRESHOLD(n)	((n) + 0x60)
+#define TSENS_STATUS_ADDR_OFFSET		2
+#define TSENS_TM_UPPER_INT_MASK(n)		(((n) & 0xffff0000) >> 16)
+#define TSENS_TM_LOWER_INT_MASK(n)		((n) & 0xffff)
+#define TSENS_TM_UPPER_LOWER_INT_STATUS(n)	((n) + 0x8)
+#define TSENS_TM_UPPER_LOWER_INT_CLEAR(n)	((n) + 0xc)
+#define TSENS_TM_UPPER_LOWER_INT_MASK(n)	((n) + 0x10)
+#define TSENS_TM_UPPER_INT_SET(n)		(1 << (n + 16))
+#define TSENS_TM_SN_CRITICAL_THRESHOLD_MASK	0xfff
+#define TSENS_TM_SN_STATUS_VALID_BIT		BIT(21)
+#define TSENS_TM_SN_STATUS_CRITICAL_STATUS	BIT(19)
+#define TSENS_TM_SN_STATUS_UPPER_STATUS		BIT(18)
+#define TSENS_TM_SN_STATUS_LOWER_STATUS		BIT(17)
+#define TSENS_TM_SN_LAST_TEMP_MASK		0xfff
+#define TSENS_TM_CODE_BIT_MASK			0xfff
+#define TSENS_TM_CODE_SIGN_BIT			0x800
+#define TSENS_TM_SCALE_DECI_MILLIDEG		100
+#define TSENS_DEBUG_WDOG_TRIGGER_COUNT		5
+#define TSENS_TM_WATCHDOG_LOG(n)		((n) + 0x13c)
+
+#define TSENS_EN				BIT(0)
+#define TSENS_CTRL_SENSOR_EN_MASK(n)		((n >> 3) & 0xffff)
+
+static void msm_tsens_convert_temp(int last_temp, int *temp)
+{
+	int code_mask = ~TSENS_TM_CODE_BIT_MASK;
+
+	if (last_temp & TSENS_TM_CODE_SIGN_BIT) {
+		/* Sign extension for negative value */
+		last_temp |= code_mask;
+	}
+
+	*temp = last_temp * TSENS_TM_SCALE_DECI_MILLIDEG;
+}
+
+static int tsens2xxx_get_temp(struct tsens_sensor *sensor, int *temp)
+{
+	struct tsens_device *tmdev = NULL;
+	unsigned int code;
+	void __iomem *sensor_addr;
+	int last_temp = 0, last_temp2 = 0, last_temp3 = 0;
+
+	if (!sensor)
+		return -EINVAL;
+
+	tmdev = sensor->tmdev;
+	sensor_addr = TSENS_TM_SN_STATUS(tmdev->tsens_tm_addr);
+
+	code = readl_relaxed_no_log(sensor_addr +
+			(sensor->hw_id << TSENS_STATUS_ADDR_OFFSET));
+	last_temp = code & TSENS_TM_SN_LAST_TEMP_MASK;
+
+	if (code & TSENS_TM_SN_STATUS_VALID_BIT) {
+		msm_tsens_convert_temp(last_temp, temp);
+		goto dbg;
+	}
+
+	code = readl_relaxed_no_log(sensor_addr +
+		(sensor->hw_id << TSENS_STATUS_ADDR_OFFSET));
+	last_temp2 = code & TSENS_TM_SN_LAST_TEMP_MASK;
+	if (code & TSENS_TM_SN_STATUS_VALID_BIT) {
+		last_temp = last_temp2;
+		msm_tsens_convert_temp(last_temp, temp);
+		goto dbg;
+	}
+
+	code = readl_relaxed_no_log(sensor_addr +
+			(sensor->hw_id <<
+			TSENS_STATUS_ADDR_OFFSET));
+	last_temp3 = code & TSENS_TM_SN_LAST_TEMP_MASK;
+	if (code & TSENS_TM_SN_STATUS_VALID_BIT) {
+		last_temp = last_temp3;
+		msm_tsens_convert_temp(last_temp, temp);
+		goto dbg;
+	}
+
+	if (last_temp == last_temp2)
+		last_temp = last_temp2;
+	else if (last_temp2 == last_temp3)
+		last_temp = last_temp3;
+
+	msm_tsens_convert_temp(last_temp, temp);
+
+dbg:
+	if (tmdev->ops->dbg)
+		tmdev->ops->dbg(tmdev, (u32) sensor->hw_id,
+					TSENS_DBG_LOG_TEMP_READS, temp);
+
+	return 0;
+}
+
+static int tsens_tm_activate_trip_type(struct tsens_sensor *tm_sensor,
+			int trip, enum thermal_device_mode mode)
+{
+	struct tsens_device *tmdev = NULL;
+	unsigned int reg_cntl, mask;
+	int rc = 0;
+
+	/* clear the interrupt and unmask */
+	if (!tm_sensor || trip < 0)
+		return -EINVAL;
+
+	tmdev = tm_sensor->tmdev;
+	if (!tmdev)
+		return -EINVAL;
+
+
+	mask = (tm_sensor->hw_id);
+	switch (trip) {
+	case THERMAL_TRIP_CRITICAL:
+		tmdev->sensor[tm_sensor->hw_id].thr_state.crit_th_state = mode;
+		reg_cntl = readl_relaxed(TSENS_TM_CRITICAL_INT_MASK
+						(tmdev->tsens_tm_addr));
+		if (mode == THERMAL_DEVICE_DISABLED)
+			writel_relaxed(reg_cntl | (1 << mask),
+				(TSENS_TM_CRITICAL_INT_MASK
+				(tmdev->tsens_tm_addr)));
+		else
+			writel_relaxed(reg_cntl & ~(1 << mask),
+				(TSENS_TM_CRITICAL_INT_MASK
+				(tmdev->tsens_tm_addr)));
+		break;
+	case THERMAL_TRIP_CONFIGURABLE_HI:
+		tmdev->sensor[tm_sensor->hw_id].thr_state.high_th_state = mode;
+		reg_cntl = readl_relaxed(TSENS_TM_UPPER_LOWER_INT_MASK
+						(tmdev->tsens_tm_addr));
+		if (mode == THERMAL_DEVICE_DISABLED)
+			writel_relaxed(reg_cntl |
+				(TSENS_TM_UPPER_INT_SET(mask)),
+				(TSENS_TM_UPPER_LOWER_INT_MASK
+				(tmdev->tsens_tm_addr)));
+		else
+			writel_relaxed(reg_cntl &
+				~(TSENS_TM_UPPER_INT_SET(mask)),
+				(TSENS_TM_UPPER_LOWER_INT_MASK
+				(tmdev->tsens_tm_addr)));
+		break;
+	case THERMAL_TRIP_CONFIGURABLE_LOW:
+		tmdev->sensor[tm_sensor->hw_id].thr_state.low_th_state = mode;
+		reg_cntl = readl_relaxed(TSENS_TM_UPPER_LOWER_INT_MASK
+						(tmdev->tsens_tm_addr));
+		if (mode == THERMAL_DEVICE_DISABLED)
+			writel_relaxed(reg_cntl | (1 << mask),
+			(TSENS_TM_UPPER_LOWER_INT_MASK
+						(tmdev->tsens_tm_addr)));
+		else
+			writel_relaxed(reg_cntl & ~(1 << mask),
+			(TSENS_TM_UPPER_LOWER_INT_MASK
+						(tmdev->tsens_tm_addr)));
+		break;
+	default:
+		rc = -EINVAL;
+	}
+
+	/* Activate and enable the respective trip threshold setting */
+	mb();
+
+	return rc;
+}
+
+static int tsens2xxx_set_trip_temp(struct tsens_sensor *tm_sensor,
+						int low_temp, int high_temp)
+{
+	unsigned int reg_cntl;
+	unsigned long flags;
+	struct tsens_device *tmdev = NULL;
+	int rc = 0;
+
+	if (!tm_sensor)
+		return -EINVAL;
+
+	tmdev = tm_sensor->tmdev;
+	if (!tmdev)
+		return -EINVAL;
+
+	spin_lock_irqsave(&tmdev->tsens_upp_low_lock, flags);
+
+	if (high_temp != INT_MAX) {
+		tmdev->sensor[tm_sensor->hw_id].thr_state.high_temp = high_temp;
+		reg_cntl = readl_relaxed((TSENS_TM_SN_UPPER_LOWER_THRESHOLD
+				(tmdev->tsens_tm_addr)) +
+				(tm_sensor->hw_id *
+				TSENS_TM_SN_ADDR_OFFSET));
+		high_temp /= TSENS_TM_SCALE_DECI_MILLIDEG;
+		high_temp = TSENS_TM_UPPER_THRESHOLD_SET(high_temp);
+		high_temp &= TSENS_TM_UPPER_THRESHOLD_MASK;
+		reg_cntl &= ~TSENS_TM_UPPER_THRESHOLD_MASK;
+		writel_relaxed(reg_cntl | high_temp,
+			(TSENS_TM_SN_UPPER_LOWER_THRESHOLD
+				(tmdev->tsens_tm_addr) +
+			(tm_sensor->hw_id * TSENS_TM_SN_ADDR_OFFSET)));
+	}
+
+	if (low_temp != INT_MIN) {
+		tmdev->sensor[tm_sensor->hw_id].thr_state.low_temp = low_temp;
+		reg_cntl = readl_relaxed((TSENS_TM_SN_UPPER_LOWER_THRESHOLD
+				(tmdev->tsens_tm_addr)) +
+				(tm_sensor->hw_id *
+				TSENS_TM_SN_ADDR_OFFSET));
+		low_temp /= TSENS_TM_SCALE_DECI_MILLIDEG;
+		low_temp &= TSENS_TM_LOWER_THRESHOLD_MASK;
+		reg_cntl &= ~TSENS_TM_LOWER_THRESHOLD_MASK;
+		writel_relaxed(reg_cntl | low_temp,
+			(TSENS_TM_SN_UPPER_LOWER_THRESHOLD
+				(tmdev->tsens_tm_addr) +
+			(tm_sensor->hw_id * TSENS_TM_SN_ADDR_OFFSET)));
+	}
+
+	/* Set trip temperature thresholds */
+	mb();
+
+	if (high_temp != INT_MAX) {
+		rc = tsens_tm_activate_trip_type(tm_sensor,
+				THERMAL_TRIP_CONFIGURABLE_HI,
+				THERMAL_DEVICE_ENABLED);
+		if (rc) {
+			pr_err("trip high enable error :%d\n", rc);
+			goto fail;
+		}
+	} else {
+		rc = tsens_tm_activate_trip_type(tm_sensor,
+				THERMAL_TRIP_CONFIGURABLE_HI,
+				THERMAL_DEVICE_DISABLED);
+		if (rc) {
+			pr_err("trip high disable error :%d\n", rc);
+			goto fail;
+		}
+	}
+
+	if (low_temp != INT_MIN) {
+		rc = tsens_tm_activate_trip_type(tm_sensor,
+				THERMAL_TRIP_CONFIGURABLE_LOW,
+				THERMAL_DEVICE_ENABLED);
+		if (rc) {
+			pr_err("trip low enable activation error :%d\n", rc);
+			goto fail;
+		}
+	} else {
+		rc = tsens_tm_activate_trip_type(tm_sensor,
+				THERMAL_TRIP_CONFIGURABLE_LOW,
+				THERMAL_DEVICE_DISABLED);
+		if (rc) {
+			pr_err("trip low disable error :%d\n", rc);
+			goto fail;
+		}
+	}
+
+fail:
+	spin_unlock_irqrestore(&tmdev->tsens_upp_low_lock, flags);
+	return rc;
+}
+
+static irqreturn_t tsens_tm_critical_irq_thread(int irq, void *data)
+{
+	struct tsens_device *tm = data;
+	unsigned int i, status, wd_log, wd_mask;
+	unsigned long flags;
+	void __iomem *sensor_status_addr, *sensor_int_mask_addr;
+	void __iomem *sensor_critical_addr;
+	void __iomem *wd_critical_addr, *wd_log_addr;
+
+	sensor_status_addr = TSENS_TM_SN_STATUS(tm->tsens_tm_addr);
+	sensor_int_mask_addr =
+		TSENS_TM_CRITICAL_INT_MASK(tm->tsens_tm_addr);
+	sensor_critical_addr =
+		TSENS_TM_SN_CRITICAL_THRESHOLD(tm->tsens_tm_addr);
+	wd_critical_addr =
+		TSENS_TM_CRITICAL_INT_STATUS(tm->tsens_tm_addr);
+	wd_log_addr = TSENS_TM_WATCHDOG_LOG(tm->tsens_tm_addr);
+
+	if (tm->ctrl_data->wd_bark) {
+		wd_mask = readl_relaxed(wd_critical_addr);
+		if (wd_mask & TSENS_TM_CRITICAL_WD_BARK) {
+			/*
+			 * Clear watchdog interrupt and
+			 * increment global wd count
+			 */
+			writel_relaxed(wd_mask | TSENS_TM_CRITICAL_WD_BARK,
+				(TSENS_TM_CRITICAL_INT_CLEAR
+				(tm->tsens_tm_addr)));
+			writel_relaxed(wd_mask & ~(TSENS_TM_CRITICAL_WD_BARK),
+				(TSENS_TM_CRITICAL_INT_CLEAR
+				(tm->tsens_tm_addr)));
+			wd_log = readl_relaxed(wd_log_addr);
+			if (wd_log >= TSENS_DEBUG_WDOG_TRIGGER_COUNT) {
+				pr_err("Watchdog count:%d\n", wd_log);
+				if (tm->ops->dbg)
+					tm->ops->dbg(tm, 0,
+					TSENS_DBG_LOG_BUS_ID_DATA, NULL);
+				BUG();
+			}
+
+			return IRQ_HANDLED;
+		}
+	}
+
+	for (i = 0; i < TSENS_MAX_SENSORS; i++) {
+		int int_mask, int_mask_val;
+		u32 addr_offset;
+
+		if (IS_ERR(tm->sensor[i].tzd))
+			continue;
+
+		spin_lock_irqsave(&tm->tsens_crit_lock, flags);
+		addr_offset = tm->sensor[i].hw_id *
+						TSENS_TM_SN_ADDR_OFFSET;
+		status = readl_relaxed(sensor_status_addr + addr_offset);
+		int_mask = readl_relaxed(sensor_int_mask_addr);
+
+		if ((status & TSENS_TM_SN_STATUS_CRITICAL_STATUS) &&
+			!(int_mask & (1 << tm->sensor[i].hw_id))) {
+			int_mask = readl_relaxed(sensor_int_mask_addr);
+			int_mask_val = (1 << tm->sensor[i].hw_id);
+			/* Mask the corresponding interrupt for the sensors */
+			writel_relaxed(int_mask | int_mask_val,
+				TSENS_TM_CRITICAL_INT_MASK(
+					tm->tsens_tm_addr));
+			/* Clear the corresponding sensors interrupt */
+			writel_relaxed(int_mask_val,
+				TSENS_TM_CRITICAL_INT_CLEAR
+					(tm->tsens_tm_addr));
+			writel_relaxed(0,
+				TSENS_TM_CRITICAL_INT_CLEAR(
+					tm->tsens_tm_addr));
+			tm->sensor[i].thr_state.crit_th_state =
+						THERMAL_DEVICE_DISABLED;
+		}
+		spin_unlock_irqrestore(&tm->tsens_crit_lock, flags);
+	}
+
+	/* Mask critical interrupt */
+	mb();
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t tsens_tm_irq_thread(int irq, void *data)
+{
+	struct tsens_device *tm = data;
+	unsigned int i, status, threshold, temp;
+	unsigned long flags;
+	void __iomem *sensor_status_addr;
+	void __iomem *sensor_int_mask_addr;
+	void __iomem *sensor_upper_lower_addr;
+	u32 addr_offset = 0;
+
+	sensor_status_addr = TSENS_TM_SN_STATUS(tm->tsens_tm_addr);
+	sensor_int_mask_addr =
+		TSENS_TM_UPPER_LOWER_INT_MASK(tm->tsens_tm_addr);
+	sensor_upper_lower_addr =
+		TSENS_TM_SN_UPPER_LOWER_THRESHOLD(tm->tsens_tm_addr);
+
+	for (i = 0; i < TSENS_MAX_SENSORS; i++) {
+		bool upper_thr = false, lower_thr = false;
+		int int_mask, int_mask_val = 0, rc;
+
+		if (IS_ERR(tm->sensor[i].tzd))
+			continue;
+
+		rc = tsens2xxx_get_temp(&tm->sensor[i], &temp);
+		if (rc) {
+			pr_debug("Error:%d reading temp sensor:%d\n", rc, i);
+			continue;
+		}
+
+		spin_lock_irqsave(&tm->tsens_upp_low_lock, flags);
+		addr_offset = tm->sensor[i].hw_id *
+						TSENS_TM_SN_ADDR_OFFSET;
+		status = readl_relaxed(sensor_status_addr + addr_offset);
+		threshold = readl_relaxed(sensor_upper_lower_addr +
+								addr_offset);
+		int_mask = readl_relaxed(sensor_int_mask_addr);
+
+		if ((status & TSENS_TM_SN_STATUS_UPPER_STATUS) &&
+			!(int_mask &
+				(1 << (tm->sensor[i].hw_id + 16)))) {
+			int_mask = readl_relaxed(sensor_int_mask_addr);
+			int_mask_val = TSENS_TM_UPPER_INT_SET(
+					tm->sensor[i].hw_id);
+			/* Mask the corresponding interrupt for the sensors */
+			writel_relaxed(int_mask | int_mask_val,
+				TSENS_TM_UPPER_LOWER_INT_MASK(
+					tm->tsens_tm_addr));
+			/* Clear the corresponding sensors interrupt */
+			writel_relaxed(int_mask_val,
+				TSENS_TM_UPPER_LOWER_INT_CLEAR(
+					tm->tsens_tm_addr));
+			writel_relaxed(0,
+				TSENS_TM_UPPER_LOWER_INT_CLEAR(
+					tm->tsens_tm_addr));
+			if (TSENS_TM_UPPER_THRESHOLD_VALUE(threshold) >
+				(temp/TSENS_TM_SCALE_DECI_MILLIDEG)) {
+				pr_debug("Re-arm high threshold\n");
+				rc = tsens_tm_activate_trip_type(
+					&tm->sensor[i],
+					THERMAL_TRIP_CONFIGURABLE_HI,
+					THERMAL_DEVICE_ENABLED);
+				if (rc)
+					pr_err("high rearm failed:%d\n", rc);
+			} else {
+				upper_thr = true;
+				tm->sensor[i].thr_state.high_th_state =
+						THERMAL_DEVICE_DISABLED;
+			}
+		}
+
+		if ((status & TSENS_TM_SN_STATUS_LOWER_STATUS) &&
+			!(int_mask &
+				(1 << tm->sensor[i].hw_id))) {
+			int_mask = readl_relaxed(sensor_int_mask_addr);
+			int_mask_val = (1 << tm->sensor[i].hw_id);
+			/* Mask the corresponding interrupt for the sensors */
+			writel_relaxed(int_mask | int_mask_val,
+				TSENS_TM_UPPER_LOWER_INT_MASK(
+					tm->tsens_tm_addr));
+			/* Clear the corresponding sensors interrupt */
+			writel_relaxed(int_mask_val,
+				TSENS_TM_UPPER_LOWER_INT_CLEAR(
+					tm->tsens_tm_addr));
+			writel_relaxed(0,
+				TSENS_TM_UPPER_LOWER_INT_CLEAR(
+					tm->tsens_tm_addr));
+			if (TSENS_TM_LOWER_THRESHOLD_VALUE(threshold)
+				< (temp/TSENS_TM_SCALE_DECI_MILLIDEG)) {
+				pr_debug("Re-arm low threshold\n");
+				rc = tsens_tm_activate_trip_type(
+					&tm->sensor[i],
+					THERMAL_TRIP_CONFIGURABLE_LOW,
+					THERMAL_DEVICE_ENABLED);
+				if (rc)
+					pr_err("low rearm failed:%d\n", rc);
+			} else {
+				lower_thr = true;
+				tm->sensor[i].thr_state.low_th_state =
+						THERMAL_DEVICE_DISABLED;
+			}
+		}
+		spin_unlock_irqrestore(&tm->tsens_upp_low_lock, flags);
+
+		if (upper_thr || lower_thr) {
+			/* Use id for multiple controllers */
+			pr_debug("sensor:%d trigger temp (%d degC)\n",
+				tm->sensor[i].hw_id, temp);
+		}
+	}
+
+	/* Disable monitoring sensor trip threshold for triggered sensor */
+	mb();
+
+	if (tm->ops->dbg)
+		tm->ops->dbg(tm, 0, TSENS_DBG_LOG_INTERRUPT_TIMESTAMP, NULL);
+
+	return IRQ_HANDLED;
+}
+
+static int tsens2xxx_hw_sensor_en(struct tsens_device *tmdev,
+					u32 sensor_id)
+{
+	void __iomem *srot_addr;
+	unsigned int srot_val, sensor_en;
+
+	srot_addr = TSENS_CTRL_ADDR(tmdev->tsens_srot_addr + 0x4);
+	srot_val = readl_relaxed(srot_addr);
+	srot_val = TSENS_CTRL_SENSOR_EN_MASK(srot_val);
+
+	sensor_en = ((1 << sensor_id) & srot_val);
+
+	return sensor_en;
+}
+
+static int tsens2xxx_hw_init(struct tsens_device *tmdev)
+{
+	void __iomem *srot_addr;
+	void __iomem *sensor_int_mask_addr;
+	unsigned int srot_val, crit_mask, crit_val;
+
+	srot_addr = TSENS_CTRL_ADDR(tmdev->tsens_srot_addr + 0x4);
+	srot_val = readl_relaxed(srot_addr);
+	if (!(srot_val & TSENS_EN)) {
+		pr_err("TSENS device is not enabled\n");
+		return -ENODEV;
+	}
+
+	if (tmdev->ctrl_data->cycle_monitor) {
+		sensor_int_mask_addr =
+			TSENS_TM_CRITICAL_INT_MASK(tmdev->tsens_tm_addr);
+		crit_mask = readl_relaxed(sensor_int_mask_addr);
+		crit_val = TSENS_TM_CRITICAL_CYCLE_MONITOR;
+		if (tmdev->ctrl_data->cycle_compltn_monitor_mask)
+			writel_relaxed((crit_mask | crit_val),
+				(TSENS_TM_CRITICAL_INT_MASK
+				(tmdev->tsens_tm_addr)));
+		else
+			writel_relaxed((crit_mask & ~crit_val),
+				(TSENS_TM_CRITICAL_INT_MASK
+				(tmdev->tsens_tm_addr)));
+		/*Update critical cycle monitoring*/
+		mb();
+	}
+
+	if (tmdev->ctrl_data->wd_bark) {
+		sensor_int_mask_addr =
+			TSENS_TM_CRITICAL_INT_MASK(tmdev->tsens_tm_addr);
+		crit_mask = readl_relaxed(sensor_int_mask_addr);
+		crit_val = TSENS_TM_CRITICAL_WD_BARK;
+		if (tmdev->ctrl_data->wd_bark_mask)
+			writel_relaxed((crit_mask | crit_val),
+			(TSENS_TM_CRITICAL_INT_MASK
+			(tmdev->tsens_tm_addr)));
+		else
+			writel_relaxed((crit_mask & ~crit_val),
+			(TSENS_TM_CRITICAL_INT_MASK
+			(tmdev->tsens_tm_addr)));
+		/*Update watchdog monitoring*/
+		mb();
+	}
+
+	writel_relaxed(TSENS_TM_CRITICAL_INT_EN |
+		TSENS_TM_UPPER_INT_EN | TSENS_TM_LOWER_INT_EN,
+		TSENS_TM_INT_EN(tmdev->tsens_tm_addr));
+
+	spin_lock_init(&tmdev->tsens_crit_lock);
+	spin_lock_init(&tmdev->tsens_upp_low_lock);
+
+	return 0;
+}
+
+static const struct tsens_irqs tsens2xxx_irqs[] = {
+	{ "tsens-upper-lower", tsens_tm_irq_thread},
+	{ "tsens-critical", tsens_tm_critical_irq_thread},
+};
+
+static int tsens2xxx_register_interrupts(struct tsens_device *tmdev)
+{
+	struct platform_device *pdev;
+	int i, rc;
+
+	if (!tmdev)
+		return -EINVAL;
+
+	pdev = tmdev->pdev;
+
+	for (i = 0; i < ARRAY_SIZE(tsens2xxx_irqs); i++) {
+		int irq;
+
+		irq = platform_get_irq_byname(pdev, tsens2xxx_irqs[i].name);
+		if (irq < 0) {
+			dev_err(&pdev->dev, "failed to get irq %s\n",
+					tsens2xxx_irqs[i].name);
+			return irq;
+		}
+
+		rc = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+				tsens2xxx_irqs[i].handler,
+				IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+				tsens2xxx_irqs[i].name, tmdev);
+		if (rc) {
+			dev_err(&pdev->dev, "failed to get irq %s\n",
+					tsens2xxx_irqs[i].name);
+			return rc;
+		}
+		enable_irq_wake(irq);
+	}
+
+	return 0;
+}
+
+static const struct tsens_ops ops_tsens2xxx = {
+	.hw_init	= tsens2xxx_hw_init,
+	.get_temp	= tsens2xxx_get_temp,
+	.set_trips	= tsens2xxx_set_trip_temp,
+	.interrupts_reg	= tsens2xxx_register_interrupts,
+	.dbg		= tsens2xxx_dbg,
+	.sensor_en	= tsens2xxx_hw_sensor_en,
+};
+
+const struct tsens_data data_tsens2xxx = {
+	.cycle_monitor			= false,
+	.cycle_compltn_monitor_mask	= 1,
+	.wd_bark			= false,
+	.wd_bark_mask			= 1,
+	.ops				= &ops_tsens2xxx,
+};
+
+const struct tsens_data data_tsens23xx = {
+	.cycle_monitor			= true,
+	.cycle_compltn_monitor_mask	= 1,
+	.wd_bark			= true,
+	.wd_bark_mask			= 1,
+	.ops				= &ops_tsens2xxx,
+};
+
+const struct tsens_data data_tsens24xx = {
+	.cycle_monitor			= true,
+	.cycle_compltn_monitor_mask	= 1,
+	.wd_bark			= true,
+	/* Enable Watchdog monitoring by unmasking */
+	.wd_bark_mask			= 0,
+	.ops				= &ops_tsens2xxx,
+};
diff --git a/include/linux/thermal.h b/include/linux/thermal.h
index fd5b959..ec26b1e 100644
--- a/include/linux/thermal.h
+++ b/include/linux/thermal.h
@@ -83,6 +83,9 @@ enum thermal_trip_type {
 	THERMAL_TRIP_PASSIVE,
 	THERMAL_TRIP_HOT,
 	THERMAL_TRIP_CRITICAL,
+	THERMAL_TRIP_CONFIGURABLE_HI,
+	THERMAL_TRIP_CONFIGURABLE_LOW,
+	THERMAL_TRIP_CRITICAL_LOW,
 };
 
 enum thermal_trend {