Merge "smp: Wake up all idle CPUs when suspending to idle"
diff --git a/Documentation/devicetree/bindings/arm/msm/msm_bus.txt b/Documentation/devicetree/bindings/arm/msm/msm_bus.txt
index b3f3431..0d955ed 100644
--- a/Documentation/devicetree/bindings/arm/msm/msm_bus.txt
+++ b/Documentation/devicetree/bindings/arm/msm/msm_bus.txt
@@ -139,6 +139,7 @@
 			the command DB driver.
 qcom,drv-id:		The DRV id associated with the RSC, used to differentiate
 			between RSCS owned by different execution environments.
+qcom,defer-init-qos:	Flag to force defer initial QoS configuration at probe time.
 
 
 Example:
diff --git a/Documentation/devicetree/bindings/clock/qcom,camcc.txt b/Documentation/devicetree/bindings/clock/qcom,camcc.txt
index 313e50f..daf8a539 100644
--- a/Documentation/devicetree/bindings/clock/qcom,camcc.txt
+++ b/Documentation/devicetree/bindings/clock/qcom,camcc.txt
@@ -2,7 +2,8 @@
 ----------------------------------------------------
 
 Required properties :
-- compatible : shall contain "qcom,cam_cc-sdm845" or "qcom,cam_cc-sdm845-v2"
+- compatible : shall contain "qcom,cam_cc-sdm845", "qcom,cam_cc-sdm845-v2" or
+	       "qcom,cam_cc-sdm670"
 - reg : shall contain base register location and length
 - reg-names: names of registers listed in the same order as in
 	     the reg property.
diff --git a/Documentation/devicetree/bindings/clock/qcom,dispcc.txt b/Documentation/devicetree/bindings/clock/qcom,dispcc.txt
index 87af0f6..d169c31 100644
--- a/Documentation/devicetree/bindings/clock/qcom,dispcc.txt
+++ b/Documentation/devicetree/bindings/clock/qcom,dispcc.txt
@@ -2,7 +2,8 @@
 ----------------------------------------------------
 
 Required properties :
-- compatible : shall contain "qcom,dispcc-sdm845" or "qcom,dispcc-sdm845-v2".
+- compatible : shall contain "qcom,dispcc-sdm845", "qcom,dispcc-sdm845-v2" or
+	       "qcom,dispcc-sdm670".
 - reg : shall contain base register location and length.
 - reg-names: names of registers listed in the same order as in
 	     the reg property.
diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc.txt b/Documentation/devicetree/bindings/clock/qcom,gcc.txt
index c280b92..538fb6d 100644
--- a/Documentation/devicetree/bindings/clock/qcom,gcc.txt
+++ b/Documentation/devicetree/bindings/clock/qcom,gcc.txt
@@ -18,6 +18,7 @@
 			"qcom,gcc-mdm9615"
 			"qcom,gcc-sdm845"
 			"qcom,gcc-sdm845-v2"
+			"qcom,gcc-sdm670"
 			"qcom,debugcc-sdm845"
 
 - reg : shall contain base register location and length
diff --git a/Documentation/devicetree/bindings/clock/qcom,gpucc.txt b/Documentation/devicetree/bindings/clock/qcom,gpucc.txt
index 12676b7..aa90bc4 100644
--- a/Documentation/devicetree/bindings/clock/qcom,gpucc.txt
+++ b/Documentation/devicetree/bindings/clock/qcom,gpucc.txt
@@ -7,6 +7,8 @@
 		"qcom,gpucc-sdm845-v2",
 		"qcom,gfxcc-sdm845",
 		"qcom,gfxcc-sdm845-v2"
+		"qcom,gpucc-sdm670",
+		"qcom,gfxcc-sdm670"
 
 - reg : shall contain base register offset and size.
 - #clock-cells : shall contain 1.
diff --git a/Documentation/devicetree/bindings/clock/qcom,rpmh.txt b/Documentation/devicetree/bindings/clock/qcom,rpmh.txt
index c81a454..9ad7263 100644
--- a/Documentation/devicetree/bindings/clock/qcom,rpmh.txt
+++ b/Documentation/devicetree/bindings/clock/qcom,rpmh.txt
@@ -2,7 +2,8 @@
 -------------------------------------------------------
 
 Required properties :
-- compatible : must be "qcom,rpmh-clk-sdm845"
+- compatible : shall contain "qcom,rpmh-clk-sdm845" or "qcom,rpmh-clk-sdm670"
+
 - #clock-cells : must contain 1
 - mboxes : list of RPMh mailbox phandle and channel identifier tuples.
 - mbox-names : list of names to identify the RPMh mailboxes used.
diff --git a/Documentation/devicetree/bindings/clock/qcom,videocc.txt b/Documentation/devicetree/bindings/clock/qcom,videocc.txt
index 6bd0f0b..9b53c65 100644
--- a/Documentation/devicetree/bindings/clock/qcom,videocc.txt
+++ b/Documentation/devicetree/bindings/clock/qcom,videocc.txt
@@ -2,8 +2,8 @@
 ----------------------------------------------------
 
 Required properties :
-- compatible : shall contain "qcom,video_cc-sdm845" or
-	       "qcom,video_cc-sdm845-v2".
+- compatible : shall contain "qcom,video_cc-sdm845", "qcom,video_cc-sdm845-v2"
+	       or "qcom,video_cc-sdm670".
 - reg : shall contain base register location and length.
 - reg-names: names of registers listed in the same order as in
 	     the reg property.
diff --git a/Documentation/devicetree/bindings/power/supply/qcom/smb138x-charger.txt b/Documentation/devicetree/bindings/power/supply/qcom/smb138x-charger.txt
index 92ef23c..fd2729f 100644
--- a/Documentation/devicetree/bindings/power/supply/qcom/smb138x-charger.txt
+++ b/Documentation/devicetree/bindings/power/supply/qcom/smb138x-charger.txt
@@ -43,6 +43,13 @@
   Definition: Boolean flag which indicates that the charger should not draw
 	      current from any of its input sources (USB, DC).
 
+- qcom,use-extcon
+  Usage:      optional
+  Value type: <empty>
+  Definition: Boolean flag which specify that smb138x will act as main charger
+	      to do extcon USB calls. If not defined, other charger driver can
+	      act as main charger to do extcon USB calls.
+
 - qcom,fcc-max-ua
   Usage:      optional
   Value type: <u32>
diff --git a/arch/arm64/boot/dts/qcom/Makefile b/arch/arm64/boot/dts/qcom/Makefile
index 041b304..954738a 100644
--- a/arch/arm64/boot/dts/qcom/Makefile
+++ b/arch/arm64/boot/dts/qcom/Makefile
@@ -8,21 +8,27 @@
 		sdm845-mtp-overlay.dtbo \
 		sdm845-qrd-overlay.dtbo \
 		sdm845-qvr-overlay.dtbo \
-		sdm845-v2-cdp-overlay.dtbo \
-		sdm845-v2-mtp-overlay.dtbo \
-		sdm845-v2-qrd-overlay.dtbo \
 		sdm845-4k-panel-mtp-overlay.dtbo \
 		sdm845-4k-panel-cdp-overlay.dtbo \
 		sdm845-4k-panel-qrd-overlay.dtbo \
+		sdm845-v2-cdp-overlay.dtbo \
+		sdm845-v2-mtp-overlay.dtbo \
+		sdm845-v2-qrd-overlay.dtbo \
+		sdm845-v2-4k-panel-mtp-overlay.dtbo \
+		sdm845-v2-4k-panel-cdp-overlay.dtbo \
+		sdm845-v2-4k-panel-qrd-overlay.dtbo \
 		sda845-cdp-overlay.dtbo \
 		sda845-mtp-overlay.dtbo \
 		sda845-qrd-overlay.dtbo \
-		sda845-v2-cdp-overlay.dtbo \
-		sda845-v2-mtp-overlay.dtbo \
-		sda845-v2-qrd-overlay.dtbo \
 		sda845-4k-panel-mtp-overlay.dtbo \
 		sda845-4k-panel-cdp-overlay.dtbo \
 		sda845-4k-panel-qrd-overlay.dtbo \
+		sda845-v2-cdp-overlay.dtbo \
+		sda845-v2-mtp-overlay.dtbo \
+		sda845-v2-qrd-overlay.dtbo \
+		sda845-v2-4k-panel-mtp-overlay.dtbo \
+		sda845-v2-4k-panel-cdp-overlay.dtbo \
+		sda845-v2-4k-panel-qrd-overlay.dtbo \
 		sdm845-interposer-sdm670-cdp-overlay.dtbo \
 		sdm845-interposer-sdm670-mtp-overlay.dtbo
 
@@ -30,21 +36,28 @@
 sdm845-mtp-overlay.dtbo-base := sdm845.dtb
 sdm845-qrd-overlay.dtbo-base := sdm845.dtb
 sdm845-qvr-overlay.dtbo-base := sdm845-v2.dtb
-sdm845-v2-cdp-overlay.dtbo-base := sdm845-v2.dtb
-sdm845-v2-mtp-overlay.dtbo-base := sdm845-v2.dtb
-sdm845-v2-qrd-overlay.dtbo-base := sdm845-v2.dtb
+sdm845-qvr-overlay.dtbo-base := sdm845.dtb
 sdm845-4k-panel-mtp-overlay.dtbo-base := sdm845.dtb
 sdm845-4k-panel-cdp-overlay.dtbo-base := sdm845.dtb
 sdm845-4k-panel-qrd-overlay.dtbo-base := sdm845.dtb
+sdm845-v2-cdp-overlay.dtbo-base := sdm845-v2.dtb
+sdm845-v2-mtp-overlay.dtbo-base := sdm845-v2.dtb
+sdm845-v2-qrd-overlay.dtbo-base := sdm845-v2.dtb
+sdm845-v2-4k-panel-mtp-overlay.dtbo-base := sdm845-v2.dtb
+sdm845-v2-4k-panel-cdp-overlay.dtbo-base := sdm845-v2.dtb
+sdm845-v2-4k-panel-qrd-overlay.dtbo-base := sdm845-v2.dtb
 sda845-cdp-overlay.dtbo-base := sda845.dtb
 sda845-mtp-overlay.dtbo-base := sda845.dtb
 sda845-qrd-overlay.dtbo-base := sda845.dtb
-sda845-v2-cdp-overlay.dtbo-base := sda845-v2.dtb
-sda845-v2-mtp-overlay.dtbo-base := sda845-v2.dtb
-sda845-v2-qrd-overlay.dtbo-base := sda845-v2.dtb
 sda845-4k-panel-mtp-overlay.dtbo-base := sda845.dtb
 sda845-4k-panel-cdp-overlay.dtbo-base := sda845.dtb
 sda845-4k-panel-qrd-overlay.dtbo-base := sda845.dtb
+sda845-v2-cdp-overlay.dtbo-base := sda845-v2.dtb
+sda845-v2-mtp-overlay.dtbo-base := sda845-v2.dtb
+sda845-v2-qrd-overlay.dtbo-base := sda845-v2.dtb
+sda845-v2-4k-panel-mtp-overlay.dtbo-base := sda845-v2.dtb
+sda845-v2-4k-panel-cdp-overlay.dtbo-base := sda845-v2.dtb
+sda845-v2-4k-panel-qrd-overlay.dtbo-base := sda845-v2.dtb
 sdm845-interposer-sdm670-cdp-overlay.dtbo-base := sdm845-interposer-sdm670.dtb
 sdm845-interposer-sdm670-mtp-overlay.dtbo-base := sdm845-interposer-sdm670.dtb
 else
@@ -69,15 +82,21 @@
 	dtbo-$(CONFIG_ARCH_SDM670) += \
 		sdm670-cdp-overlay.dtbo \
 		sdm670-mtp-overlay.dtbo \
-		sdm670-rumi-overlay.dtbo
+		sdm670-rumi-overlay.dtbo \
+		sdm670-pm660a-cdp-overlay.dtbo \
+		sdm670-pm660a-mtp-overlay.dtbo
 
 sdm670-cdp-overlay.dtbo-base := sdm670.dtb
 sdm670-mtp-overlay.dtbo-base := sdm670.dtb
 sdm670-rumi-overlay.dtbo-base := sdm670.dtb
+sdm670-pm660a-cdp-overlay.dtbo-base := sdm670.dtb
+sdm670-pm660a-mtp-overlay.dtbo-base := sdm670.dtb
 else
 dtb-$(CONFIG_ARCH_SDM670) += sdm670-rumi.dtb \
 	sdm670-mtp.dtb \
-	sdm670-cdp.dtb
+	sdm670-cdp.dtb \
+	sdm670-pm660a-mtp.dtb \
+	sdm670-pm660a-cdp.dtb
 endif
 
 always		:= $(dtb-y)
diff --git a/arch/arm64/boot/dts/qcom/pm660.dtsi b/arch/arm64/boot/dts/qcom/pm660.dtsi
index 8075b9e..48d68a7 100644
--- a/arch/arm64/boot/dts/qcom/pm660.dtsi
+++ b/arch/arm64/boot/dts/qcom/pm660.dtsi
@@ -14,7 +14,7 @@
 #include <dt-bindings/interrupt-controller/irq.h>
 
 &spmi_bus {
-	qcom,pm660@0 {
+	pm660_0: qcom,pm660@0 {
 		compatible ="qcom,spmi-pmic";
 		reg = <0x0 SPMI_USID>;
 		#address-cells = <2>;
@@ -245,149 +245,10 @@
 			};
 		};
 
-		pm660_charger: qcom,qpnp-smb2 {
-			compatible = "qcom,qpnp-smb2";
-			#address-cells = <1>;
-			#size-cells = <1>;
-
-			qcom,pmic-revid = <&pm660_revid>;
-
-			io-channels = <&pm660_rradc 8>,
-				      <&pm660_rradc 10>,
-				      <&pm660_rradc 3>,
-				      <&pm660_rradc 4>;
-			io-channel-names = "charger_temp",
-					   "charger_temp_max",
-					   "usbin_i",
-					   "usbin_v";
-
-			qcom,wipower-max-uw = <5000000>;
-
-			/* Enable after the qusb_phy0 device node is added */
-			/* dpdm-supply = <&qusb_phy0>; */
-
-			qcom,thermal-mitigation
-					= <3000000 2500000 2000000 1500000
-						1000000 500000>;
-
-			qcom,chgr@1000 {
-				reg = <0x1000 0x100>;
-				interrupts =
-					<0x0 0x10 0x0 IRQ_TYPE_EDGE_RISING>,
-					<0x0 0x10 0x1 IRQ_TYPE_EDGE_RISING>,
-					<0x0 0x10 0x2 IRQ_TYPE_EDGE_RISING>,
-					<0x0 0x10 0x3 IRQ_TYPE_EDGE_RISING>,
-					<0x0 0x10 0x4 IRQ_TYPE_EDGE_RISING>;
-
-				interrupt-names = "chg-error",
-						  "chg-state-change",
-						  "step-chg-state-change",
-						  "step-chg-soc-update-fail",
-						  "step-chg-soc-update-request";
-			};
-
-			qcom,otg@1100 {
-				reg = <0x1100 0x100>;
-				interrupts = <0x0 0x11 0x0 IRQ_TYPE_EDGE_BOTH>,
-					     <0x0 0x11 0x1 IRQ_TYPE_EDGE_BOTH>,
-					     <0x0 0x11 0x2 IRQ_TYPE_EDGE_BOTH>,
-					     <0x0 0x11 0x3 IRQ_TYPE_EDGE_BOTH>;
-
-				interrupt-names = "otg-fail",
-						  "otg-overcurrent",
-						  "otg-oc-dis-sw-sts",
-						  "testmode-change-detect";
-			};
-
-			qcom,bat-if@1200 {
-				reg = <0x1200 0x100>;
-				interrupts =
-					<0x0 0x12 0x0 IRQ_TYPE_EDGE_RISING>,
-					<0x0 0x12 0x1 IRQ_TYPE_EDGE_BOTH>,
-					<0x0 0x12 0x2 IRQ_TYPE_EDGE_BOTH>,
-					<0x0 0x12 0x3 IRQ_TYPE_EDGE_BOTH>,
-					<0x0 0x12 0x4 IRQ_TYPE_EDGE_BOTH>,
-					<0x0 0x12 0x5 IRQ_TYPE_EDGE_BOTH>;
-
-				interrupt-names = "bat-temp",
-						  "bat-ocp",
-						  "bat-ov",
-						  "bat-low",
-						  "bat-therm-or-id-missing",
-						  "bat-terminal-missing";
-			};
-
-			qcom,usb-chgpth@1300 {
-				reg = <0x1300 0x100>;
-				interrupts =
-					<0x0 0x13 0x0 IRQ_TYPE_EDGE_BOTH>,
-					<0x0 0x13 0x1 IRQ_TYPE_EDGE_BOTH>,
-					<0x0 0x13 0x2 IRQ_TYPE_EDGE_BOTH>,
-					<0x0 0x13 0x3 IRQ_TYPE_EDGE_BOTH>,
-					<0x0 0x13 0x4 IRQ_TYPE_EDGE_BOTH>,
-					<0x0 0x13 0x5 IRQ_TYPE_EDGE_RISING>,
-					<0x0 0x13 0x6 IRQ_TYPE_EDGE_RISING>,
-					<0x0 0x13 0x7 IRQ_TYPE_EDGE_RISING>;
-
-				interrupt-names = "usbin-collapse",
-						  "usbin-lt-3p6v",
-						  "usbin-uv",
-						  "usbin-ov",
-						  "usbin-plugin",
-						  "usbin-src-change",
-						  "usbin-icl-change",
-						  "type-c-change";
-			};
-
-			qcom,dc-chgpth@1400 {
-				reg = <0x1400 0x100>;
-				interrupts =
-					<0x0 0x14 0x0 IRQ_TYPE_EDGE_BOTH>,
-					<0x0 0x14 0x1 IRQ_TYPE_EDGE_BOTH>,
-					<0x0 0x14 0x2 IRQ_TYPE_EDGE_BOTH>,
-					<0x0 0x14 0x3 IRQ_TYPE_EDGE_BOTH>,
-					<0x0 0x14 0x4 IRQ_TYPE_EDGE_BOTH>,
-					<0x0 0x14 0x5 IRQ_TYPE_EDGE_BOTH>,
-					<0x0 0x14 0x6 IRQ_TYPE_EDGE_RISING>;
-
-				interrupt-names = "dcin-collapse",
-						  "dcin-lt-3p6v",
-						  "dcin-uv",
-						  "dcin-ov",
-						  "dcin-plugin",
-						  "div2-en-dg",
-						  "dcin-icl-change";
-			};
-
-			qcom,chgr-misc@1600 {
-				reg = <0x1600 0x100>;
-				interrupts =
-					<0x0 0x16 0x0 IRQ_TYPE_EDGE_RISING>,
-					<0x0 0x16 0x1 IRQ_TYPE_EDGE_RISING>,
-					<0x0 0x16 0x2 IRQ_TYPE_EDGE_BOTH>,
-					<0x0 0x16 0x3 IRQ_TYPE_EDGE_BOTH>,
-					<0x0 0x16 0x4 IRQ_TYPE_EDGE_BOTH>,
-					<0x0 0x16 0x5 IRQ_TYPE_EDGE_BOTH>,
-					<0x0 0x16 0x6 IRQ_TYPE_EDGE_FALLING>,
-					<0x0 0x16 0x7 IRQ_TYPE_EDGE_BOTH>;
-
-				interrupt-names = "wdog-snarl",
-						  "wdog-bark",
-						  "aicl-fail",
-						  "aicl-done",
-						  "high-duty-cycle",
-						  "input-current-limiting",
-						  "temperature-change",
-						  "switcher-power-ok";
-			};
-		};
-
 		pm660_pdphy: qcom,usb-pdphy@1700 {
 			compatible = "qcom,qpnp-pdphy";
 			reg = <0x1700 0x100>;
 			vdd-pdphy-supply = <&pm660l_l7>;
-			vbus-supply = <&smb2_vbus>;
-			vconn-supply = <&smb2_vconn>;
 			interrupts = <0x0 0x17 0x0 IRQ_TYPE_EDGE_RISING>,
 				     <0x0 0x17 0x1 IRQ_TYPE_EDGE_RISING>,
 				     <0x0 0x17 0x2 IRQ_TYPE_EDGE_RISING>,
@@ -497,73 +358,6 @@
 			qcom,pmic-revid = <&pm660_revid>;
 		};
 
-		pm660_fg: qpnp,fg {
-			compatible = "qcom,fg-gen3";
-			#address-cells = <1>;
-			#size-cells = <1>;
-			qcom,pmic-revid = <&pm660_revid>;
-			io-channels = <&pm660_rradc 0>,
-				      <&pm660_rradc 7>;
-			io-channel-names = "rradc_batt_id",
-					   "rradc_die_temp";
-			qcom,rradc-base = <0x4500>;
-			qcom,fg-esr-timer-awake = <96 96>;
-			qcom,fg-esr-timer-asleep = <256 256>;
-			qcom,fg-esr-timer-charging = <0 96>;
-			qcom,cycle-counter-en;
-			status = "okay";
-
-			qcom,fg-batt-soc@4000 {
-				status = "okay";
-				reg = <0x4000 0x100>;
-				interrupts = <0x0 0x40 0x0 IRQ_TYPE_EDGE_BOTH>,
-					     <0x0 0x40 0x1 IRQ_TYPE_EDGE_BOTH>,
-					     <0x0 0x40 0x2
-							IRQ_TYPE_EDGE_RISING>,
-					     <0x0 0x40 0x3
-							IRQ_TYPE_EDGE_RISING>,
-					     <0x0 0x40 0x4 IRQ_TYPE_EDGE_BOTH>,
-					     <0x0 0x40 0x5
-							IRQ_TYPE_EDGE_RISING>,
-					     <0x0 0x40 0x6 IRQ_TYPE_EDGE_BOTH>,
-					     <0x0 0x40 0x7 IRQ_TYPE_EDGE_BOTH>;
-				interrupt-names = "soc-update",
-						  "soc-ready",
-						  "bsoc-delta",
-						  "msoc-delta",
-						  "msoc-low",
-						  "msoc-empty",
-						  "msoc-high",
-						  "msoc-full";
-			};
-
-			qcom,fg-batt-info@4100 {
-				status = "okay";
-				reg = <0x4100 0x100>;
-				interrupts = <0x0 0x41 0x0 IRQ_TYPE_EDGE_BOTH>,
-					     <0x0 0x41 0x1 IRQ_TYPE_EDGE_BOTH>,
-					     <0x0 0x41 0x2 IRQ_TYPE_EDGE_BOTH>,
-					     <0x0 0x41 0x3 IRQ_TYPE_EDGE_BOTH>,
-					     <0x0 0x41 0x6 IRQ_TYPE_EDGE_BOTH>;
-				interrupt-names = "vbatt-pred-delta",
-						  "vbatt-low",
-						  "esr-delta",
-						  "batt-missing",
-						  "batt-temp-delta";
-			};
-
-			qcom,fg-memif@4400 {
-				status = "okay";
-				reg = <0x4400 0x100>;
-				interrupts = <0x0 0x44 0x0 IRQ_TYPE_EDGE_BOTH>,
-					     <0x0 0x44 0x1 IRQ_TYPE_EDGE_BOTH>,
-					     <0x0 0x44 0x2 IRQ_TYPE_EDGE_BOTH>;
-				interrupt-names = "ima-rdy",
-						  "mem-xcp",
-						  "dma-grant";
-			};
-		};
-
 		bcl@4200 {
 			compatible = "qcom,msm-bcl-lmh";
 			reg = <0x4200 0xff>,
@@ -579,32 +373,11 @@
 		};
 	};
 
-	qcom,pm660@1 {
+	pm660_1: qcom,pm660@1 {
 		compatible ="qcom,spmi-pmic";
 		reg = <0x1 SPMI_USID>;
 		#address-cells = <2>;
 		#size-cells = <0>;
-
-		pm660_haptics: qcom,haptics@c000 {
-			compatible = "qcom,qpnp-haptics";
-			reg = <0xc000 0x100>;
-			interrupts = <0x1 0xc0 0x0 IRQ_TYPE_EDGE_BOTH>,
-				     <0x1 0xc0 0x1 IRQ_TYPE_EDGE_BOTH>;
-			interrupt-names = "hap-sc-irq", "hap-play-irq";
-			qcom,pmic-revid = <&pm660_revid>;
-			qcom,pmic-misc = <&pm660_misc>;
-			qcom,misc-clk-trim-error-reg = <0xf3>;
-			qcom,actuator-type = <0>;
-			qcom,play-mode = "direct";
-			qcom,vmax-mv = <3200>;
-			qcom,ilim-ma = <800>;
-			qcom,sc-dbc-cycles = <8>;
-			qcom,wave-play-rate-us = <6667>;
-			qcom,en-brake;
-			qcom,lra-high-z = "opt0";
-			qcom,lra-auto-res-mode = "qwd";
-			qcom,lra-res-cal-period = <4>;
-		};
 	};
 };
 
diff --git a/arch/arm64/boot/dts/qcom/pm660l.dtsi b/arch/arm64/boot/dts/qcom/pm660l.dtsi
index 11101ffe..771154a 100644
--- a/arch/arm64/boot/dts/qcom/pm660l.dtsi
+++ b/arch/arm64/boot/dts/qcom/pm660l.dtsi
@@ -185,35 +185,6 @@
 			};
 		};
 
-		pm660l_wled: qcom,leds@d800 {
-			compatible = "qcom,qpnp-wled";
-			reg = <0xd800 0x100>,
-				<0xd900 0x100>;
-			reg-names = "qpnp-wled-ctrl-base",
-					"qpnp-wled-sink-base";
-			interrupts = <0x3 0xd8 0x1 IRQ_TYPE_EDGE_RISING>;
-			interrupt-names = "ovp-irq";
-			linux,name = "wled";
-			linux,default-trigger = "bkl-trigger";
-			qcom,fdbk-output = "auto";
-			qcom,vref-uv = <127500>;
-			qcom,switch-freq-khz = <800>;
-			qcom,ovp-mv = <29600>;
-			qcom,ilim-ma = <970>;
-			qcom,boost-duty-ns = <26>;
-			qcom,mod-freq-khz = <9600>;
-			qcom,dim-mode = "hybrid";
-			qcom,hyb-thres = <625>;
-			qcom,sync-dly-us = <800>;
-			qcom,fs-curr-ua = <25000>;
-			qcom,cons-sync-write-delay-us = <1000>;
-			qcom,led-strings-list = [00 01 02];
-			qcom,loop-auto-gm-en;
-			qcom,pmic-revid = <&pm660l_revid>;
-			qcom,auto-calibration-enable;
-			status = "ok";
-		};
-
 		flash_led: qcom,leds@d300 {
 			compatible = "qcom,qpnp-flash-led-v2";
 			reg = <0xd300 0x100>;
@@ -322,89 +293,5 @@
 				qcom,default-led-trigger = "switch1_trigger";
 			};
 		};
-
-		pm660l_lcdb: qpnp-lcdb@ec00 {
-			compatible = "qcom,qpnp-lcdb-regulator";
-			#address-cells = <1>;
-			#size-cells = <1>;
-			reg = <0xec00 0x100>;
-			interrupts = <0x3 0xec 0x1 IRQ_TYPE_EDGE_RISING>;
-			interrupt-names = "sc-irq";
-
-			qcom,pmic-revid = <&pm660l_revid>;
-
-			lcdb_ldo_vreg: ldo {
-				label = "ldo";
-				regulator-name = "lcdb_ldo";
-				regulator-min-microvolt = <4000000>;
-				regulator-max-microvolt = <6000000>;
-			};
-
-			lcdb_ncp_vreg: ncp {
-				label = "ncp";
-				regulator-name = "lcdb_ncp";
-				regulator-min-microvolt = <4000000>;
-				regulator-max-microvolt = <6000000>;
-			};
-		};
-
-		pm660a_oledb: qpnp-oledb@e000 {
-		       compatible = "qcom,qpnp-oledb-regulator";
-		       #address-cells = <1>;
-		       #size-cells = <1>;
-		       qcom,pmic-revid = <&pm660l_revid>;
-		       reg = <0xe000 0x100>;
-		       qcom,pbs-client = <&pm660l_pbs>;
-
-		       label = "oledb";
-		       regulator-name = "regulator-oledb";
-		       regulator-min-microvolt = <5000000>;
-		       regulator-max-microvolt = <8100000>;
-
-		       qcom,swire-control;
-		       qcom,ext-pin-control;
-		       status = "disabled";
-		};
-
-		pm660a_labibb: qpnp-labibb-regulator {
-			compatible = "qcom,qpnp-labibb-regulator";
-			#address-cells = <1>;
-			#size-cells = <1>;
-			qcom,pmic-revid = <&pm660l_revid>;
-			qcom,swire-control;
-			status = "disabled";
-
-			ibb_regulator: qcom,ibb@dc00 {
-				reg = <0xdc00 0x100>;
-				reg-names = "ibb_reg";
-				regulator-name = "ibb_reg";
-
-				regulator-min-microvolt = <4000000>;
-				regulator-max-microvolt = <6300000>;
-
-				qcom,qpnp-ibb-min-voltage = <1400000>;
-				qcom,qpnp-ibb-step-size = <100000>;
-				qcom,qpnp-ibb-slew-rate = <2000000>;
-				qcom,qpnp-ibb-init-voltage = <4000000>;
-				qcom,qpnp-ibb-init-amoled-voltage = <4000000>;
-			};
-
-			lab_regulator: qcom,lab@de00 {
-				reg = <0xde00 0x100>;
-				reg-names = "lab";
-				regulator-name = "lab_reg";
-
-				regulator-min-microvolt = <4600000>;
-				regulator-max-microvolt = <6100000>;
-
-				qcom,qpnp-lab-min-voltage = <4600000>;
-				qcom,qpnp-lab-step-size = <100000>;
-				qcom,qpnp-lab-slew-rate = <5000>;
-				qcom,qpnp-lab-init-voltage = <4600000>;
-				qcom,qpnp-lab-init-amoled-voltage = <4600000>;
-
-				qcom,notify-lab-vreg-ok-sts;
-			};
-		};
 	};
 };
diff --git a/arch/arm64/boot/dts/qcom/sda845-v2-4k-panel-cdp-overlay.dts b/arch/arm64/boot/dts/qcom/sda845-v2-4k-panel-cdp-overlay.dts
new file mode 100644
index 0000000..9b7449e
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sda845-v2-4k-panel-cdp-overlay.dts
@@ -0,0 +1,66 @@
+/* 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.
+ */
+
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/qcom,gcc-sdm845.h>
+#include <dt-bindings/clock/qcom,camcc-sdm845.h>
+#include <dt-bindings/clock/qcom,dispcc-sdm845.h>
+#include <dt-bindings/clock/qcom,rpmh.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#include "sdm845-sde-display.dtsi"
+#include "sdm845-cdp.dtsi"
+#include "sdm845-cdp-audio-overlay.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. sda845 v2 4K Display Panel CDP";
+	compatible = "qcom,sda845-cdp", "qcom,sda845", "qcom,cdp";
+	qcom,msm-id = <341 0x20000>;
+	qcom,board-id = <1 1>;
+};
+
+&dsi_nt35597_truly_dsc_cmd_display {
+	/delete-property/ qcom,dsi-display-active;
+};
+
+&mdss_mdp {
+	connectors = <&sde_rscc &sde_wb>;
+};
+
+&dsi_sharp_4k_dsc_video {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 52 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+	qcom,platform-reset-gpio = <&tlmm 6 0>;
+};
+
+&dsi_sharp_4k_dsc_cmd {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 52 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+	qcom,platform-reset-gpio = <&tlmm 6 0>;
+};
+
+&dsi_sharp_4k_dsc_video_display {
+	qcom,dsi-display-active;
+};
diff --git a/arch/arm64/boot/dts/qcom/sda845-v2-4k-panel-mtp-overlay.dts b/arch/arm64/boot/dts/qcom/sda845-v2-4k-panel-mtp-overlay.dts
new file mode 100644
index 0000000..bb310d3
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sda845-v2-4k-panel-mtp-overlay.dts
@@ -0,0 +1,66 @@
+/* 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.
+ */
+
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/qcom,gcc-sdm845.h>
+#include <dt-bindings/clock/qcom,camcc-sdm845.h>
+#include <dt-bindings/clock/qcom,dispcc-sdm845.h>
+#include <dt-bindings/clock/qcom,rpmh.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#include "sdm845-sde-display.dtsi"
+#include "sdm845-mtp.dtsi"
+#include "sdm845-audio-overlay.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. sda845 v2 4K Display Panel MTP";
+	compatible = "qcom,sda845-mtp", "qcom,sda845", "qcom,mtp";
+	qcom,msm-id = <341 0x20000>;
+	qcom,board-id = <8 1>;
+};
+
+&dsi_nt35597_truly_dsc_cmd_display {
+	/delete-property/ qcom,dsi-display-active;
+};
+
+&mdss_mdp {
+	connectors = <&sde_rscc &sde_wb &sde_dp>;
+};
+
+&dsi_sharp_4k_dsc_video {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 52 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+	qcom,platform-reset-gpio = <&tlmm 6 0>;
+};
+
+&dsi_sharp_4k_dsc_cmd {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 52 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+	qcom,platform-reset-gpio = <&tlmm 6 0>;
+};
+
+&dsi_sharp_4k_dsc_video_display {
+	qcom,dsi-display-active;
+};
diff --git a/arch/arm64/boot/dts/qcom/sda845-v2-4k-panel-qrd-overlay.dts b/arch/arm64/boot/dts/qcom/sda845-v2-4k-panel-qrd-overlay.dts
new file mode 100644
index 0000000..ab61f2e
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sda845-v2-4k-panel-qrd-overlay.dts
@@ -0,0 +1,64 @@
+/* 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.
+ */
+
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/qcom,gcc-sdm845.h>
+#include <dt-bindings/clock/qcom,camcc-sdm845.h>
+#include <dt-bindings/clock/qcom,dispcc-sdm845.h>
+#include <dt-bindings/clock/qcom,rpmh.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#include "sdm845-sde-display.dtsi"
+#include "sdm845-qrd.dtsi"
+#include "sdm845-qrd-audio-overlay.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. sda845 v2 4K Display Panel QRD";
+	compatible = "qcom,sda845-qrd", "qcom,sda845", "qcom,qrd";
+	qcom,msm-id = <341 0x20000>;
+	qcom,board-id = <11 1>;
+};
+
+&dsi_nt35597_truly_dsc_cmd_display {
+	/delete-property/ qcom,dsi-display-active;
+};
+
+&dsi_sharp_4k_dsc_video {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 52 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+	qcom,platform-reset-gpio = <&tlmm 6 0>;
+	qcom,mdss-dsi-panel-orientation = "180";
+};
+
+&dsi_sharp_4k_dsc_cmd {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 52 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+	qcom,platform-reset-gpio = <&tlmm 6 0>;
+	qcom,mdss-dsi-panel-orientation = "180";
+};
+
+&dsi_sharp_4k_dsc_video_display {
+	qcom,dsi-display-active;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-cdp-overlay.dts b/arch/arm64/boot/dts/qcom/sdm670-cdp-overlay.dts
index f0c820f..9feb5b4 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-cdp-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-cdp-overlay.dts
@@ -22,9 +22,12 @@
 #include "sdm670-cdp.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM670 CDP";
+	model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660L CDP";
 	compatible = "qcom,sdm670-cdp", "qcom,sdm670", "qcom,cdp";
 	qcom,msm-id = <336 0x0>;
 	qcom,board-id = <1 0>;
+	qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
+		       <0x0001001b 0x0102001a 0x0 0x0>,
+		       <0x0001001b 0x0201011a 0x0 0x0>;
 };
 
diff --git a/arch/arm64/boot/dts/qcom/sdm670-cdp.dts b/arch/arm64/boot/dts/qcom/sdm670-cdp.dts
index 7e5947b..27882dd 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-cdp.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-cdp.dts
@@ -17,7 +17,10 @@
 #include "sdm670-cdp.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM670 CDP";
+	model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660L CDP";
 	compatible = "qcom,sdm670-cdp", "qcom,sdm670", "qcom,cdp";
 	qcom,board-id = <1 0>;
+	qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
+		       <0x0001001b 0x0102001a 0x0 0x0>,
+		       <0x0001001b 0x0201011a 0x0 0x0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm670-cdp.dtsi b/arch/arm64/boot/dts/qcom/sdm670-cdp.dtsi
index 0cf48a3..4f40678 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-cdp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-cdp.dtsi
@@ -10,6 +10,8 @@
  * GNU General Public License for more details.
  */
 
+#include "sdm670-pmic-overlay.dtsi"
+
 &qupv3_se9_2uart {
 	status = "disabled";
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm670-gpu.dtsi b/arch/arm64/boot/dts/qcom/sdm670-gpu.dtsi
new file mode 100644
index 0000000..d13aa15
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-gpu.dtsi
@@ -0,0 +1,310 @@
+/* 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.
+ */
+
+&soc {
+
+	pil_gpu: qcom,kgsl-hyp {
+		compatible = "qcom,pil-tz-generic";
+		qcom,pas-id = <13>;
+		qcom,firmware-name = "a615_zap";
+	};
+
+	msm_bus: qcom,kgsl-busmon{
+		label = "kgsl-busmon";
+		compatible = "qcom,kgsl-busmon";
+	};
+
+	gpubw: qcom,gpubw {
+		compatible = "qcom,devbw";
+		governor = "bw_vbif";
+		qcom,src-dst-ports = <26 512>;
+		qcom,bw-tbl =
+			<     0 /*  off     */ >,
+			<   381 /*  100  MHz */ >,
+			<   762 /*  200  MHz */ >,
+			<  1144 /*  300  MHz */ >,
+			<  1720 /*  451  MHz */ >,
+			<  2086 /*  547  MHz */ >,
+			<  2597 /*  681  MHz */ >,
+			<  3147 /*  825  MHz */ >,
+			<  3879 /*  1017 MHz */ >,
+			<  5161 /*  1353 MHz */ >,
+			<  5931 /*  1555 MHz */ >,
+			<  6881 /*  1804 MHz */ >;
+	};
+
+	msm_gpu: qcom,kgsl-3d0@5000000 {
+		label = "kgsl-3d0";
+		compatible = "qcom,kgsl-3d0", "qcom,kgsl-3d";
+		status = "ok";
+		reg = <0x5000000 0x40000>;
+		reg-names = "kgsl_3d0_reg_memory";
+		interrupts = <0 300 0>;
+		interrupt-names = "kgsl_3d0_irq";
+		qcom,id = <0>;
+
+		qcom,chipid = <0x06010500>;
+
+		qcom,initial-pwrlevel = <3>;
+
+		qcom,gpu-quirk-hfi-use-reg;
+
+		/* <HZ/12> */
+		qcom,idle-timeout = <80>;
+		qcom,no-nap;
+
+		qcom,highest-bank-bit = <14>;
+
+		qcom,min-access-length = <64>;
+
+		qcom,ubwc-mode = <2>;
+
+		/* size in bytes */
+		qcom,snapshot-size = <1048576>;
+
+		/* base addr, size */
+		qcom,gpu-qdss-stm = <0x161c0000 0x40000>;
+
+		clocks = <&clock_gfx GPU_CC_GX_GFX3D_CLK>,
+			<&clock_gpucc GPU_CC_CXO_CLK>,
+			<&clock_gcc GCC_DDRSS_GPU_AXI_CLK>,
+			<&clock_gcc GCC_GPU_MEMNOC_GFX_CLK>,
+			<&clock_gpucc GPU_CC_CX_GMU_CLK>,
+			<&clock_gpucc GPU_CC_AHB_CLK>;
+
+		clock-names = "core_clk", "rbbmtimer_clk", "mem_clk",
+				"mem_iface_clk", "gmu_clk", "ahb_clk";
+
+		/* Bus Scale Settings */
+		qcom,gpubw-dev = <&gpubw>;
+		qcom,bus-control;
+		qcom,msm-bus,name = "grp3d";
+		qcom,bus-width = <32>;
+		qcom,msm-bus,num-cases = <12>;
+		qcom,msm-bus,num-paths = <1>;
+		qcom,msm-bus,vectors-KBps =
+				<26 512 0 0>,
+				<26 512 0 400000>,     /*  1 bus=100  */
+				<26 512 0 800000>,     /*  2 bus=200  */
+				<26 512 0 1200000>,    /*  3 bus=300  */
+				<26 512 0 1804000>,    /*  4 bus=451  */
+				<26 512 0 2188000>,    /*  5 bus=547  */
+				<26 512 0 2724000>,    /*  6 bus=681  */
+				<26 512 0 3300000>,    /*  7 bus=825  */
+				<26 512 0 4068000>,    /*  8 bus=1017 */
+				<26 512 0 5412000>,    /*  9 bus=1353 */
+				<26 512 0 6220000>,    /* 10 bus=1555 */
+				<26 512 0 7216000>;    /* 11 bus=1804 */
+
+		/* GDSC regulator names */
+		regulator-names = "vddcx", "vdd";
+		/* GDSC oxili regulators */
+		vddcx-supply = <&gpu_cx_gdsc>;
+		vdd-supply = <&gpu_gx_gdsc>;
+
+		/* GPU related llc slices */
+		cache-slice-names = "gpu", "gpuhtw";
+		cache-slices = <&llcc 12>, <&llcc 11>;
+
+		/* CPU latency parameter */
+		qcom,pm-qos-active-latency = <660>;
+		qcom,pm-qos-wakeup-latency = <460>;
+
+		/* Enable context aware freq. scaling */
+		qcom,enable-ca-jump;
+		/* Context aware jump busy penalty in us */
+		qcom,ca-busy-penalty = <12000>;
+		/* Context aware jump target power level */
+		qcom,ca-target-pwrlevel = <1>;
+
+		/* GPU Mempools */
+		qcom,gpu-mempools {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			compatible = "qcom,gpu-mempools";
+
+			/* 4K Page Pool configuration */
+			qcom,gpu-mempool@0 {
+				reg = <0>;
+				qcom,mempool-page-size = <4096>;
+				qcom,mempool-allocate;
+			};
+			/* 8K Page Pool configuration */
+			qcom,gpu-mempool@1 {
+				reg = <1>;
+				qcom,mempool-page-size = <8192>;
+				qcom,mempool-allocate;
+			};
+			/* 64K Page Pool configuration */
+			qcom,gpu-mempool@2 {
+				reg = <2>;
+				qcom,mempool-page-size = <65536>;
+				qcom,mempool-reserved = <256>;
+			};
+			/* 1M Page Pool configuration */
+			qcom,gpu-mempool@3 {
+				reg = <3>;
+				qcom,mempool-page-size = <1048576>;
+				qcom,mempool-reserved = <32>;
+			};
+		};
+
+		/* Power levels */
+		qcom,gpu-pwrlevels {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			compatible = "qcom,gpu-pwrlevels";
+
+			/* SVS_L1 */
+			qcom,gpu-pwrlevel@0 {
+				reg = <0>;
+				qcom,gpu-freq = <430000000>;
+				qcom,bus-freq = <11>;
+				qcom,bus-min = <10>;
+				qcom,bus-max = <11>;
+			};
+
+			/* SVS */
+			qcom,gpu-pwrlevel@1 {
+				reg = <1>;
+				qcom,gpu-freq = <355000000>;
+				qcom,bus-freq = <9>;
+				qcom,bus-min = <8>;
+				qcom,bus-max = <10>;
+			};
+
+			/* LOW SVS */
+			qcom,gpu-pwrlevel@2 {
+				reg = <2>;
+				qcom,gpu-freq = <267000000>;
+				qcom,bus-freq = <6>;
+				qcom,bus-min = <4>;
+				qcom,bus-max = <8>;
+			};
+
+			/* MIN SVS */
+			qcom,gpu-pwrlevel@3 {
+				reg = <3>;
+				qcom,gpu-freq = <180000000>;
+				qcom,bus-freq = <4>;
+				qcom,bus-min = <3>;
+				qcom,bus-max = <5>;
+			};
+
+			/* XO */
+			qcom,gpu-pwrlevel@4 {
+				reg = <4>;
+				qcom,gpu-freq = <0>;
+				qcom,bus-freq = <0>;
+				qcom,bus-min = <0>;
+				qcom,bus-max = <0>;
+			};
+		};
+
+	};
+
+	kgsl_msm_iommu: qcom,kgsl-iommu {
+		compatible = "qcom,kgsl-smmu-v2";
+
+		reg = <0x05040000 0x10000>;
+		qcom,protect = <0x40000 0x10000>;
+		qcom,micro-mmu-control = <0x6000>;
+
+		clocks =<&clock_gcc GCC_GPU_CFG_AHB_CLK>,
+			<&clock_gcc GCC_DDRSS_GPU_AXI_CLK>,
+			<&clock_gcc GCC_GPU_MEMNOC_GFX_CLK>;
+
+		clock-names = "iface_clk", "mem_clk", "mem_iface_clk";
+
+		qcom,secure_align_mask = <0xfff>;
+		qcom,retention;
+		qcom,hyp_secure_alloc;
+
+		gfx3d_user: gfx3d_user {
+			compatible = "qcom,smmu-kgsl-cb";
+			label = "gfx3d_user";
+			iommus = <&kgsl_smmu 0>;
+			qcom,gpu-offset = <0x48000>;
+		};
+
+		gfx3d_secure: gfx3d_secure {
+			compatible = "qcom,smmu-kgsl-cb";
+			iommus = <&kgsl_smmu 2>;
+		};
+	};
+
+	gmu: qcom,gmu {
+		label = "kgsl-gmu";
+		compatible = "qcom,gpu-gmu";
+
+		reg =
+			<0x506a000 0x31000>,
+			<0xb200000 0x300000>,
+			<0xc200000 0x10000>;
+		reg-names =
+			"kgsl_gmu_reg",
+			"kgsl_gmu_pdc_reg",
+			"kgsl_gmu_cpr_reg";
+
+		interrupts = <0 304 0>, <0 305 0>;
+		interrupt-names = "kgsl_hfi_irq", "kgsl_gmu_irq";
+
+		qcom,msm-bus,name = "cnoc";
+		qcom,msm-bus,num-cases = <2>;
+		qcom,msm-bus,num-paths = <1>;
+		qcom,msm-bus,vectors-KBps =
+			<26 10036 0 0>,      /* CNOC off */
+			<26 10036 0 100>;    /* CNOC on  */
+
+		regulator-names = "vddcx", "vdd";
+		vddcx-supply = <&gpu_cx_gdsc>;
+		vdd-supply = <&gpu_gx_gdsc>;
+
+
+		clocks = <&clock_gpucc GPU_CC_CX_GMU_CLK>,
+				<&clock_gpucc GPU_CC_CXO_CLK>,
+				<&clock_gcc GCC_DDRSS_GPU_AXI_CLK>,
+				<&clock_gcc GCC_GPU_MEMNOC_GFX_CLK>,
+				<&clock_gpucc GPU_CC_AHB_CLK>;
+
+		clock-names = "gmu_clk", "cxo_clk", "axi_clk",
+				"memnoc_clk", "ahb_clk";
+
+		qcom,gmu-pwrlevels {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			compatible = "qcom,gmu-pwrlevels";
+
+			qcom,gmu-pwrlevel@0 {
+				reg = <0>;
+				qcom,gmu-freq = <200000000>;
+			};
+
+			qcom,gmu-pwrlevel@1 {
+				reg = <1>;
+				qcom,gmu-freq = <0>;
+			};
+		};
+
+		gmu_user: gmu_user {
+			compatible = "qcom,smmu-gmu-user-cb";
+			iommus = <&kgsl_smmu 4>;
+		};
+
+		gmu_kernel: gmu_kernel {
+			compatible = "qcom,smmu-gmu-kernel-cb";
+			iommus = <&kgsl_smmu 5>;
+		};
+	};
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-mtp-overlay.dts b/arch/arm64/boot/dts/qcom/sdm670-mtp-overlay.dts
index c8537bc..65c16c1 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-mtp-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-mtp-overlay.dts
@@ -22,8 +22,11 @@
 #include "sdm670-mtp.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM670 MTP";
+	model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660L MTP";
 	compatible = "qcom,sdm670-mtp", "qcom,sdm670", "qcom,mtp";
 	qcom,msm-id = <336 0x0>;
 	qcom,board-id = <8 0>;
+	qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
+		       <0x0001001b 0x0102001a 0x0 0x0>,
+		       <0x0001001b 0x0201011a 0x0 0x0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm670-mtp.dts b/arch/arm64/boot/dts/qcom/sdm670-mtp.dts
index 1de40b7..38a9fae 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-mtp.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-mtp.dts
@@ -17,7 +17,10 @@
 #include "sdm670-mtp.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM670 MTP";
+	model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660L MTP";
 	compatible = "qcom,sdm670-mtp", "qcom,sdm670", "qcom,mtp";
 	qcom,board-id = <8 0>;
+	qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
+		       <0x0001001b 0x0102001a 0x0 0x0>,
+		       <0x0001001b 0x0201011a 0x0 0x0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi b/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi
index 0cf48a3..4f40678 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi
@@ -10,6 +10,8 @@
  * GNU General Public License for more details.
  */
 
+#include "sdm670-pmic-overlay.dtsi"
+
 &qupv3_se9_2uart {
 	status = "disabled";
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm670-pm660a-cdp-overlay.dts b/arch/arm64/boot/dts/qcom/sdm670-pm660a-cdp-overlay.dts
new file mode 100644
index 0000000..b3d2357
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-pm660a-cdp-overlay.dts
@@ -0,0 +1,34 @@
+/* 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.
+ */
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/qcom,gcc-sdm845.h>
+#include <dt-bindings/clock/qcom,camcc-sdm845.h>
+#include <dt-bindings/clock/qcom,dispcc-sdm845.h>
+#include <dt-bindings/clock/qcom,rpmh.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#include "sdm670-cdp.dtsi"
+#include "pm660a.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660A CDP";
+	compatible = "qcom,sdm670-cdp", "qcom,sdm670", "qcom,cdp";
+	qcom,msm-id = <336 0x0>;
+	qcom,board-id = <1 0>;
+	qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+		       <0x0001001b 0x0002001a 0x0 0x0>,
+		       <0x0001001b 0x0202001a 0x0 0x0>;
+};
+
diff --git a/arch/arm64/boot/dts/qcom/sdm670-pm660a-cdp.dts b/arch/arm64/boot/dts/qcom/sdm670-pm660a-cdp.dts
new file mode 100644
index 0000000..5cf3513
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-pm660a-cdp.dts
@@ -0,0 +1,27 @@
+/* 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.
+ */
+
+
+/dts-v1/;
+
+#include "sdm670.dtsi"
+#include "sdm670-cdp.dtsi"
+#include "pm660a.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660A CDP";
+	compatible = "qcom,sdm670-cdp", "qcom,sdm670", "qcom,cdp";
+	qcom,board-id = <1 0>;
+	qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+		       <0x0001001b 0x0002001a 0x0 0x0>,
+		       <0x0001001b 0x0202001a 0x0 0x0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-pm660a-mtp-overlay.dts b/arch/arm64/boot/dts/qcom/sdm670-pm660a-mtp-overlay.dts
new file mode 100644
index 0000000..ff3270d
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-pm660a-mtp-overlay.dts
@@ -0,0 +1,33 @@
+/* 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.
+ */
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/qcom,gcc-sdm845.h>
+#include <dt-bindings/clock/qcom,camcc-sdm845.h>
+#include <dt-bindings/clock/qcom,dispcc-sdm845.h>
+#include <dt-bindings/clock/qcom,rpmh.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#include "sdm670-mtp.dtsi"
+#include "pm660a.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660A MTP";
+	compatible = "qcom,sdm670-mtp", "qcom,sdm670", "qcom,mtp";
+	qcom,msm-id = <336 0x0>;
+	qcom,board-id = <8 0>;
+	qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+		       <0x0001001b 0x0002001a 0x0 0x0>,
+		       <0x0001001b 0x0202001a 0x0 0x0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-pm660a-mtp.dts b/arch/arm64/boot/dts/qcom/sdm670-pm660a-mtp.dts
new file mode 100644
index 0000000..febd5d9
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-pm660a-mtp.dts
@@ -0,0 +1,27 @@
+/* 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.
+ */
+
+
+/dts-v1/;
+
+#include "sdm670.dtsi"
+#include "sdm670-mtp.dtsi"
+#include "pm660a.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660A MTP";
+	compatible = "qcom,sdm670-mtp", "qcom,sdm670", "qcom,mtp";
+	qcom,board-id = <8 0>;
+	qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+		       <0x0001001b 0x0002001a 0x0 0x0>,
+		       <0x0001001b 0x0202001a 0x0 0x0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-pmic-overlay.dtsi b/arch/arm64/boot/dts/qcom/sdm670-pmic-overlay.dtsi
new file mode 100644
index 0000000..cd8bfba
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-pmic-overlay.dtsi
@@ -0,0 +1,367 @@
+/* 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.
+ */
+
+&pm660_0{
+	pm660_charger: qcom,qpnp-smb2 {
+		compatible = "qcom,qpnp-smb2";
+		#address-cells = <1>;
+		#size-cells = <1>;
+
+		qcom,pmic-revid = <&pm660_revid>;
+
+		io-channels = <&pm660_rradc 8>,
+			      <&pm660_rradc 10>,
+			      <&pm660_rradc 3>,
+			      <&pm660_rradc 4>;
+		io-channel-names = "charger_temp",
+				   "charger_temp_max",
+				   "usbin_i",
+				   "usbin_v";
+
+		qcom,wipower-max-uw = <5000000>;
+
+		/* Enable after the qusb_phy0 device node is added */
+		/* dpdm-supply = <&qusb_phy0>; */
+
+		qcom,thermal-mitigation
+				= <3000000 2500000 2000000 1500000
+					1000000 500000>;
+
+		qcom,chgr@1000 {
+			reg = <0x1000 0x100>;
+			interrupts =
+				<0x0 0x10 0x0 IRQ_TYPE_EDGE_RISING>,
+				<0x0 0x10 0x1 IRQ_TYPE_EDGE_RISING>,
+				<0x0 0x10 0x2 IRQ_TYPE_EDGE_RISING>,
+				<0x0 0x10 0x3 IRQ_TYPE_EDGE_RISING>,
+				<0x0 0x10 0x4 IRQ_TYPE_EDGE_RISING>;
+
+			interrupt-names = "chg-error",
+					  "chg-state-change",
+					  "step-chg-state-change",
+					  "step-chg-soc-update-fail",
+					  "step-chg-soc-update-request";
+		};
+
+		qcom,otg@1100 {
+			reg = <0x1100 0x100>;
+			interrupts = <0x0 0x11 0x0 IRQ_TYPE_EDGE_BOTH>,
+				     <0x0 0x11 0x1 IRQ_TYPE_EDGE_BOTH>,
+				     <0x0 0x11 0x2 IRQ_TYPE_EDGE_BOTH>,
+				     <0x0 0x11 0x3 IRQ_TYPE_EDGE_BOTH>;
+
+			interrupt-names = "otg-fail",
+					  "otg-overcurrent",
+					  "otg-oc-dis-sw-sts",
+					  "testmode-change-detect";
+		};
+
+		qcom,bat-if@1200 {
+			reg = <0x1200 0x100>;
+			interrupts =
+				<0x0 0x12 0x0 IRQ_TYPE_EDGE_RISING>,
+				<0x0 0x12 0x1 IRQ_TYPE_EDGE_BOTH>,
+				<0x0 0x12 0x2 IRQ_TYPE_EDGE_BOTH>,
+				<0x0 0x12 0x3 IRQ_TYPE_EDGE_BOTH>,
+				<0x0 0x12 0x4 IRQ_TYPE_EDGE_BOTH>,
+				<0x0 0x12 0x5 IRQ_TYPE_EDGE_BOTH>;
+
+			interrupt-names = "bat-temp",
+					  "bat-ocp",
+					  "bat-ov",
+					  "bat-low",
+					  "bat-therm-or-id-missing",
+					  "bat-terminal-missing";
+		};
+
+		qcom,usb-chgpth@1300 {
+			reg = <0x1300 0x100>;
+			interrupts =
+				<0x0 0x13 0x0 IRQ_TYPE_EDGE_BOTH>,
+				<0x0 0x13 0x1 IRQ_TYPE_EDGE_BOTH>,
+				<0x0 0x13 0x2 IRQ_TYPE_EDGE_BOTH>,
+				<0x0 0x13 0x3 IRQ_TYPE_EDGE_BOTH>,
+				<0x0 0x13 0x4 IRQ_TYPE_EDGE_BOTH>,
+				<0x0 0x13 0x5 IRQ_TYPE_EDGE_RISING>,
+				<0x0 0x13 0x6 IRQ_TYPE_EDGE_RISING>,
+				<0x0 0x13 0x7 IRQ_TYPE_EDGE_RISING>;
+
+			interrupt-names = "usbin-collapse",
+					  "usbin-lt-3p6v",
+					  "usbin-uv",
+					  "usbin-ov",
+					  "usbin-plugin",
+					  "usbin-src-change",
+					  "usbin-icl-change",
+					  "type-c-change";
+		};
+
+		qcom,dc-chgpth@1400 {
+			reg = <0x1400 0x100>;
+			interrupts =
+				<0x0 0x14 0x0 IRQ_TYPE_EDGE_BOTH>,
+				<0x0 0x14 0x1 IRQ_TYPE_EDGE_BOTH>,
+				<0x0 0x14 0x2 IRQ_TYPE_EDGE_BOTH>,
+				<0x0 0x14 0x3 IRQ_TYPE_EDGE_BOTH>,
+				<0x0 0x14 0x4 IRQ_TYPE_EDGE_BOTH>,
+				<0x0 0x14 0x5 IRQ_TYPE_EDGE_BOTH>,
+				<0x0 0x14 0x6 IRQ_TYPE_EDGE_RISING>;
+
+			interrupt-names = "dcin-collapse",
+					  "dcin-lt-3p6v",
+					  "dcin-uv",
+					  "dcin-ov",
+					  "dcin-plugin",
+					  "div2-en-dg",
+					  "dcin-icl-change";
+		};
+
+		qcom,chgr-misc@1600 {
+			reg = <0x1600 0x100>;
+			interrupts =
+				<0x0 0x16 0x0 IRQ_TYPE_EDGE_RISING>,
+				<0x0 0x16 0x1 IRQ_TYPE_EDGE_RISING>,
+				<0x0 0x16 0x2 IRQ_TYPE_EDGE_BOTH>,
+				<0x0 0x16 0x3 IRQ_TYPE_EDGE_BOTH>,
+				<0x0 0x16 0x4 IRQ_TYPE_EDGE_BOTH>,
+				<0x0 0x16 0x5 IRQ_TYPE_EDGE_BOTH>,
+				<0x0 0x16 0x6 IRQ_TYPE_EDGE_FALLING>,
+				<0x0 0x16 0x7 IRQ_TYPE_EDGE_BOTH>;
+
+			interrupt-names = "wdog-snarl",
+					  "wdog-bark",
+					  "aicl-fail",
+					  "aicl-done",
+					  "high-duty-cycle",
+					  "input-current-limiting",
+					  "temperature-change",
+					  "switcher-power-ok";
+		};
+		smb2_vbus: qcom,smb2-vbus {
+			regulator-name = "smb2-vbus";
+		};
+
+		smb2_vconn: qcom,smb2-vconn {
+			regulator-name = "smb2-vconn";
+		};
+	};
+
+	pm660_fg: qpnp,fg {
+		compatible = "qcom,fg-gen3";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		qcom,pmic-revid = <&pm660_revid>;
+		io-channels = <&pm660_rradc 0>,
+			      <&pm660_rradc 7>;
+		io-channel-names = "rradc_batt_id",
+				   "rradc_die_temp";
+		qcom,rradc-base = <0x4500>;
+		qcom,fg-esr-timer-awake = <96 96>;
+		qcom,fg-esr-timer-asleep = <256 256>;
+		qcom,fg-esr-timer-charging = <0 96>;
+		qcom,cycle-counter-en;
+		status = "okay";
+
+		qcom,fg-batt-soc@4000 {
+			status = "okay";
+			reg = <0x4000 0x100>;
+			interrupts = <0x0 0x40 0x0 IRQ_TYPE_EDGE_BOTH>,
+				     <0x0 0x40 0x1 IRQ_TYPE_EDGE_BOTH>,
+				     <0x0 0x40 0x2
+						IRQ_TYPE_EDGE_RISING>,
+				     <0x0 0x40 0x3
+						IRQ_TYPE_EDGE_RISING>,
+				     <0x0 0x40 0x4 IRQ_TYPE_EDGE_BOTH>,
+				     <0x0 0x40 0x5
+						IRQ_TYPE_EDGE_RISING>,
+				     <0x0 0x40 0x6 IRQ_TYPE_EDGE_BOTH>,
+				     <0x0 0x40 0x7 IRQ_TYPE_EDGE_BOTH>;
+			interrupt-names = "soc-update",
+					  "soc-ready",
+					  "bsoc-delta",
+					  "msoc-delta",
+					  "msoc-low",
+					  "msoc-empty",
+					  "msoc-high",
+					  "msoc-full";
+		};
+
+		qcom,fg-batt-info@4100 {
+			status = "okay";
+			reg = <0x4100 0x100>;
+			interrupts = <0x0 0x41 0x0 IRQ_TYPE_EDGE_BOTH>,
+				     <0x0 0x41 0x1 IRQ_TYPE_EDGE_BOTH>,
+				     <0x0 0x41 0x2 IRQ_TYPE_EDGE_BOTH>,
+				     <0x0 0x41 0x3 IRQ_TYPE_EDGE_BOTH>,
+				     <0x0 0x41 0x6 IRQ_TYPE_EDGE_BOTH>;
+			interrupt-names = "vbatt-pred-delta",
+					  "vbatt-low",
+					  "esr-delta",
+					  "batt-missing",
+					  "batt-temp-delta";
+		};
+
+		qcom,fg-memif@4400 {
+			status = "okay";
+			reg = <0x4400 0x100>;
+			interrupts = <0x0 0x44 0x0 IRQ_TYPE_EDGE_BOTH>,
+				     <0x0 0x44 0x1 IRQ_TYPE_EDGE_BOTH>,
+				     <0x0 0x44 0x2 IRQ_TYPE_EDGE_BOTH>;
+			interrupt-names = "ima-rdy",
+					  "mem-xcp",
+					  "dma-grant";
+		};
+	};
+};
+
+&pm660_1 {
+	pm660_haptics: qcom,haptics@c000 {
+		compatible = "qcom,qpnp-haptics";
+		reg = <0xc000 0x100>;
+		interrupts = <0x1 0xc0 0x0 IRQ_TYPE_EDGE_BOTH>,
+			     <0x1 0xc0 0x1 IRQ_TYPE_EDGE_BOTH>;
+		interrupt-names = "hap-sc-irq", "hap-play-irq";
+		qcom,pmic-revid = <&pm660_revid>;
+		qcom,pmic-misc = <&pm660_misc>;
+		qcom,misc-clk-trim-error-reg = <0xf3>;
+		qcom,actuator-type = <0>;
+		qcom,play-mode = "direct";
+		qcom,vmax-mv = <3200>;
+		qcom,ilim-ma = <800>;
+		qcom,sc-dbc-cycles = <8>;
+		qcom,wave-play-rate-us = <6667>;
+		qcom,en-brake;
+		qcom,lra-high-z = "opt0";
+		qcom,lra-auto-res-mode = "qwd";
+		qcom,lra-res-cal-period = <4>;
+	};
+};
+
+&pm660l_3 {
+	pm660l_wled: qcom,leds@d800 {
+		compatible = "qcom,qpnp-wled";
+		reg = <0xd800 0x100>,
+			<0xd900 0x100>;
+		reg-names = "qpnp-wled-ctrl-base",
+				"qpnp-wled-sink-base";
+		interrupts = <0x3 0xd8 0x1 IRQ_TYPE_EDGE_RISING>;
+		interrupt-names = "ovp-irq";
+		linux,name = "wled";
+		linux,default-trigger = "bkl-trigger";
+		qcom,fdbk-output = "auto";
+		qcom,vref-uv = <127500>;
+		qcom,switch-freq-khz = <800>;
+		qcom,ovp-mv = <29600>;
+		qcom,ilim-ma = <970>;
+		qcom,boost-duty-ns = <26>;
+		qcom,mod-freq-khz = <9600>;
+		qcom,dim-mode = "hybrid";
+		qcom,hyb-thres = <625>;
+		qcom,sync-dly-us = <800>;
+		qcom,fs-curr-ua = <25000>;
+		qcom,cons-sync-write-delay-us = <1000>;
+		qcom,led-strings-list = [00 01 02];
+		qcom,loop-auto-gm-en;
+		qcom,pmic-revid = <&pm660l_revid>;
+		qcom,auto-calibration-enable;
+		status = "ok";
+	};
+
+	pm660l_lcdb: qpnp-lcdb@ec00 {
+		compatible = "qcom,qpnp-lcdb-regulator";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		reg = <0xec00 0x100>;
+		interrupts = <0x3 0xec 0x1 IRQ_TYPE_EDGE_RISING>;
+		interrupt-names = "sc-irq";
+
+		qcom,pmic-revid = <&pm660l_revid>;
+
+		lcdb_ldo_vreg: ldo {
+			label = "ldo";
+			regulator-name = "lcdb_ldo";
+			regulator-min-microvolt = <4000000>;
+			regulator-max-microvolt = <6000000>;
+		};
+
+		lcdb_ncp_vreg: ncp {
+			label = "ncp";
+			regulator-name = "lcdb_ncp";
+			regulator-min-microvolt = <4000000>;
+			regulator-max-microvolt = <6000000>;
+		};
+	};
+
+	pm660a_oledb: qpnp-oledb@e000 {
+	       compatible = "qcom,qpnp-oledb-regulator";
+	       #address-cells = <1>;
+	       #size-cells = <1>;
+	       qcom,pmic-revid = <&pm660l_revid>;
+	       reg = <0xe000 0x100>;
+	       qcom,pbs-client = <&pm660l_pbs>;
+
+	       label = "oledb";
+	       regulator-name = "regulator-oledb";
+	       regulator-min-microvolt = <5000000>;
+	       regulator-max-microvolt = <8100000>;
+
+	       qcom,swire-control;
+	       qcom,ext-pin-control;
+	       status = "disabled";
+	};
+
+	pm660a_labibb: qpnp-labibb-regulator {
+		compatible = "qcom,qpnp-labibb-regulator";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		qcom,pmic-revid = <&pm660l_revid>;
+		qcom,swire-control;
+		status = "disabled";
+
+		ibb_regulator: qcom,ibb@dc00 {
+			reg = <0xdc00 0x100>;
+			reg-names = "ibb_reg";
+			regulator-name = "ibb_reg";
+
+			regulator-min-microvolt = <4000000>;
+			regulator-max-microvolt = <6300000>;
+
+			qcom,qpnp-ibb-min-voltage = <1400000>;
+			qcom,qpnp-ibb-step-size = <100000>;
+			qcom,qpnp-ibb-slew-rate = <2000000>;
+			qcom,qpnp-ibb-init-voltage = <4000000>;
+			qcom,qpnp-ibb-init-amoled-voltage = <4000000>;
+		};
+
+		lab_regulator: qcom,lab@de00 {
+			reg = <0xde00 0x100>;
+			reg-names = "lab";
+			regulator-name = "lab_reg";
+
+			regulator-min-microvolt = <4600000>;
+			regulator-max-microvolt = <6100000>;
+
+			qcom,qpnp-lab-min-voltage = <4600000>;
+			qcom,qpnp-lab-step-size = <100000>;
+			qcom,qpnp-lab-slew-rate = <5000>;
+			qcom,qpnp-lab-init-voltage = <4600000>;
+			qcom,qpnp-lab-init-amoled-voltage = <4600000>;
+
+			qcom,notify-lab-vreg-ok-sts;
+		};
+	};
+};
+
+&pm660_pdphy {
+	vbus-supply = <&smb2_vbus>;
+	vconn-supply = <&smb2_vconn>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-regulator.dtsi b/arch/arm64/boot/dts/qcom/sdm670-regulator.dtsi
index 0a8c49f..1f76288 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-regulator.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-regulator.dtsi
@@ -628,13 +628,3 @@
 		};
 	};
 };
-
-&pm660_charger {
-	smb2_vbus: qcom,smb2-vbus {
-		regulator-name = "smb2-vbus";
-	};
-
-	smb2_vconn: qcom,smb2-vconn {
-		regulator-name = "smb2-vconn";
-	};
-};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-rumi-overlay.dts b/arch/arm64/boot/dts/qcom/sdm670-rumi-overlay.dts
index a770b3c..4a24d87 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-rumi-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-rumi-overlay.dts
@@ -13,6 +13,7 @@
 /dts-v1/;
 /plugin/;
 
+#include <dt-bindings/interrupt-controller/arm-gic.h>
 #include "sdm670-rumi.dtsi"
 
 / {
diff --git a/arch/arm64/boot/dts/qcom/sdm670-rumi.dtsi b/arch/arm64/boot/dts/qcom/sdm670-rumi.dtsi
index cc97e3e..ca9d8c7 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-rumi.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-rumi.dtsi
@@ -10,6 +10,8 @@
  * GNU General Public License for more details.
  */
 
+#include "sdm670-pmic-overlay.dtsi"
+
 &soc {
 	/* Delete all regulators */
 	/delete-node/ rpmh-regulator-smpa4;
diff --git a/arch/arm64/boot/dts/qcom/sdm670.dtsi b/arch/arm64/boot/dts/qcom/sdm670.dtsi
index aeaa3ff..692d75f 100644
--- a/arch/arm64/boot/dts/qcom/sdm670.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670.dtsi
@@ -72,6 +72,9 @@
 				compatible = "arm,arch-cache";
 				qcom,dump-size = <0x9000>;
 			};
+			L1_TLB_0: l1-tlb {
+				qcom,dump-size = <0x3000>;
+			};
 		};
 
 		CPU1: cpu@100 {
@@ -97,6 +100,9 @@
 				compatible = "arm,arch-cache";
 				qcom,dump-size = <0x9000>;
 			};
+			L1_TLB_100: l1-tlb {
+				qcom,dump-size = <0x3000>;
+			};
 		};
 
 		CPU2: cpu@200 {
@@ -122,6 +128,9 @@
 				compatible = "arm,arch-cache";
 				qcom,dump-size = <0x9000>;
 			};
+			L1_TLB_200: l1-tlb {
+				qcom,dump-size = <0x3000>;
+			};
 		};
 
 		CPU3: cpu@300 {
@@ -147,6 +156,9 @@
 				compatible = "arm,arch-cache";
 				qcom,dump-size = <0x9000>;
 			};
+			L1_TLB_300: l1-tlb {
+				qcom,dump-size = <0x3000>;
+			};
 		};
 
 		CPU4: cpu@400 {
@@ -172,6 +184,9 @@
 				compatible = "arm,arch-cache";
 				qcom,dump-size = <0x9000>;
 			};
+			L1_TLB_400: l1-tlb {
+				qcom,dump-size = <0x3000>;
+			};
 		};
 
 		CPU5: cpu@500 {
@@ -197,6 +212,9 @@
 				compatible = "arm,arch-cache";
 				qcom,dump-size = <0x9000>;
 			};
+			L1_TLB_500: l1-tlb {
+				qcom,dump-size = <0x3000>;
+			};
 		};
 
 		CPU6: cpu@600 {
@@ -222,6 +240,9 @@
 				compatible = "arm,arch-cache";
 				qcom,dump-size = <0x12000>;
 			};
+			L1_TLB_600: l1-tlb {
+				qcom,dump-size = <0x3c000>;
+			};
 		};
 
 		CPU7: cpu@700 {
@@ -247,6 +268,9 @@
 				compatible = "arm,arch-cache";
 				qcom,dump-size = <0x12000>;
 			};
+			L1_TLB_700: l1-tlb {
+				qcom,dump-size = <0x3c000>;
+			};
 		};
 
 		cpu-map {
@@ -984,6 +1008,16 @@
 			compatible = "qcom,msm-imem-kaslr_offset";
 			reg = <0x6d0 12>;
 		};
+
+		boot_stats@6b0 {
+			compatible = "qcom,msm-imem-boot_stats";
+			reg = <0x6b0 0x20>;
+		};
+
+		diag_dload@c8 {
+			compatible = "qcom,msm-imem-diag-dload";
+			reg = <0xc8 0xc8>;
+		};
 	};
 
 	gpi_dma0: qcom,gpi-dma@0x800000 {
@@ -1024,31 +1058,31 @@
 			qcom,dump-node = <&L1_I_0>;
 			qcom,dump-id = <0x60>;
 		};
-		qcom,l1_i_cache1 {
+		qcom,l1_i_cache100 {
 			qcom,dump-node = <&L1_I_100>;
 			qcom,dump-id = <0x61>;
 		};
-		qcom,l1_i_cache2 {
+		qcom,l1_i_cache200 {
 			qcom,dump-node = <&L1_I_200>;
 			qcom,dump-id = <0x62>;
 		};
-		qcom,l1_i_cache3 {
+		qcom,l1_i_cache300 {
 			qcom,dump-node = <&L1_I_300>;
 			qcom,dump-id = <0x63>;
 		};
-		qcom,l1_i_cache100 {
+		qcom,l1_i_cache400 {
 			qcom,dump-node = <&L1_I_400>;
 			qcom,dump-id = <0x64>;
 		};
-		qcom,l1_i_cache101 {
+		qcom,l1_i_cache500 {
 			qcom,dump-node = <&L1_I_500>;
 			qcom,dump-id = <0x65>;
 		};
-		qcom,l1_i_cache102 {
+		qcom,l1_i_cache600 {
 			qcom,dump-node = <&L1_I_600>;
 			qcom,dump-id = <0x66>;
 		};
-		qcom,l1_i_cache103 {
+		qcom,l1_i_cache700 {
 			qcom,dump-node = <&L1_I_700>;
 			qcom,dump-id = <0x67>;
 		};
@@ -1056,31 +1090,31 @@
 			qcom,dump-node = <&L1_D_0>;
 			qcom,dump-id = <0x80>;
 		};
-		qcom,l1_d_cache1 {
+		qcom,l1_d_cache100 {
 			qcom,dump-node = <&L1_D_100>;
 			qcom,dump-id = <0x81>;
 		};
-		qcom,l1_d_cache2 {
+		qcom,l1_d_cache200 {
 			qcom,dump-node = <&L1_D_200>;
 			qcom,dump-id = <0x82>;
 		};
-		qcom,l1_d_cache3 {
+		qcom,l1_d_cache300 {
 			qcom,dump-node = <&L1_D_300>;
 			qcom,dump-id = <0x83>;
 		};
-		qcom,l1_d_cache100 {
+		qcom,l1_d_cache400 {
 			qcom,dump-node = <&L1_D_400>;
 			qcom,dump-id = <0x84>;
 		};
-		qcom,l1_d_cache101 {
+		qcom,l1_d_cache500 {
 			qcom,dump-node = <&L1_D_500>;
 			qcom,dump-id = <0x85>;
 		};
-		qcom,l1_d_cache102 {
+		qcom,l1_d_cache600 {
 			qcom,dump-node = <&L1_D_600>;
 			qcom,dump-id = <0x86>;
 		};
-		qcom,l1_d_cache103 {
+		qcom,l1_d_cache700 {
 			qcom,dump-node = <&L1_D_700>;
 			qcom,dump-id = <0x87>;
 		};
@@ -1092,6 +1126,38 @@
 			qcom,dump-node = <&LLCC_2>;
 			qcom,dump-id = <0x141>;
 		};
+		qcom,l1_tlb_dump0 {
+			qcom,dump-node = <&L1_TLB_0>;
+			qcom,dump-id = <0x20>;
+		};
+		qcom,l1_tlb_dump100 {
+			qcom,dump-node = <&L1_TLB_100>;
+			qcom,dump-id = <0x21>;
+		};
+		qcom,l1_tlb_dump200 {
+			qcom,dump-node = <&L1_TLB_200>;
+			qcom,dump-id = <0x22>;
+		};
+		qcom,l1_tlb_dump300 {
+			qcom,dump-node = <&L1_TLB_300>;
+			qcom,dump-id = <0x23>;
+		};
+		qcom,l1_tlb_dump400 {
+			qcom,dump-node = <&L1_TLB_400>;
+			qcom,dump-id = <0x24>;
+		};
+		qcom,l1_tlb_dump500 {
+			qcom,dump-node = <&L1_TLB_500>;
+			qcom,dump-id = <0x25>;
+		};
+		qcom,l1_tlb_dump600 {
+			qcom,dump-node = <&L1_TLB_600>;
+			qcom,dump-id = <0x26>;
+		};
+		qcom,l1_tlb_dump700 {
+			qcom,dump-node = <&L1_TLB_700>;
+			qcom,dump-id = <0x27>;
+		};
 	};
 
 	kryo3xx-erp {
@@ -2047,3 +2113,4 @@
 #include "sdm670-regulator.dtsi"
 #include "sdm670-audio.dtsi"
 #include "sdm670-usb.dtsi"
+#include "sdm670-gpu.dtsi"
diff --git a/arch/arm64/boot/dts/qcom/sdm845-bus.dtsi b/arch/arm64/boot/dts/qcom/sdm845-bus.dtsi
index 5fbb1db..646dbad 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-bus.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-bus.dtsi
@@ -645,7 +645,10 @@
 			qcom,qport = <2>;
 			qcom,connections = <&slv_qns_a2noc_snoc>;
 			qcom,bus-dev = <&fab_aggre2_noc>;
+			qcom,ap-owned;
 			qcom,prio = <2>;
+			qcom,defer-init-qos;
+			qcom,node-qos-bcms = <7035 0 1>;
 		};
 
 		mas_xm_pcie3_1: mas-xm-pcie3-1 {
diff --git a/arch/arm64/boot/dts/qcom/sdm845-coresight.dtsi b/arch/arm64/boot/dts/qcom/sdm845-coresight.dtsi
index c1fcb62..3cfcddb 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-coresight.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-coresight.dtsi
@@ -1430,6 +1430,15 @@
 						<&tpda_spss_out_funnel_spss>;
 				};
 			};
+
+			port@2 {
+				reg = <1>;
+				funnel_spss_in_spss_etm0: endpoint {
+					slave-mode;
+					remote-endpoint =
+						<&spss_etm0_out_funnel_spss>;
+				};
+			};
 		};
 	};
 
@@ -1931,6 +1940,20 @@
 		};
 	};
 
+	spss_etm0 {
+		compatible = "qcom,coresight-dummy";
+
+		coresight-name = "coresight-spss-etm0";
+
+		qcom,dummy-source;
+		port {
+			spss_etm0_out_funnel_spss: endpoint {
+				remote-endpoint =
+					<&funnel_spss_in_spss_etm0>;
+			};
+		};
+	};
+
 	funnel_apss_merg: funnel@7810000 {
 		compatible = "arm,primecell";
 		arm,primecell-periphid = <0x0003b908>;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi b/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi
index b34751b..c02a0a6 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi
@@ -75,6 +75,8 @@
 		qcom,tsens-name = "tsens_tz_sensor12";
 		#cooling-cells = <2>;
 
+		qcom,pm-qos-active-latency = <460>;
+
 		clocks = <&clock_gfx GPU_CC_GX_GFX3D_CLK>,
 			<&clock_gpucc GPU_CC_CXO_CLK>,
 			<&clock_gcc GCC_DDRSS_GPU_AXI_CLK>,
diff --git a/arch/arm64/boot/dts/qcom/sdm845-interposer-pm660.dtsi b/arch/arm64/boot/dts/qcom/sdm845-interposer-pm660.dtsi
index 299f01b..e36a759e 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-interposer-pm660.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-interposer-pm660.dtsi
@@ -389,6 +389,7 @@
 #include "pm660.dtsi"
 #include "pm660l.dtsi"
 #include "sdm670-regulator.dtsi"
+#include "sdm670-pmic-overlay.dtsi"
 
 &soc {
 	/delete-node/ thermal-zones;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-v2-4k-panel-cdp-overlay.dts b/arch/arm64/boot/dts/qcom/sdm845-v2-4k-panel-cdp-overlay.dts
new file mode 100644
index 0000000..5b3e964
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm845-v2-4k-panel-cdp-overlay.dts
@@ -0,0 +1,66 @@
+/* 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.
+ */
+
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/qcom,gcc-sdm845.h>
+#include <dt-bindings/clock/qcom,camcc-sdm845.h>
+#include <dt-bindings/clock/qcom,dispcc-sdm845.h>
+#include <dt-bindings/clock/qcom,rpmh.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#include "sdm845-sde-display.dtsi"
+#include "sdm845-cdp.dtsi"
+#include "sdm845-cdp-audio-overlay.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. sdm845 v2 4K Display Panel CDP";
+	compatible = "qcom,sdm845-cdp", "qcom,sdm845", "qcom,cdp";
+	qcom,msm-id = <321 0x20000>;
+	qcom,board-id = <1 1>;
+};
+
+&dsi_nt35597_truly_dsc_cmd_display {
+	/delete-property/ qcom,dsi-display-active;
+};
+
+&mdss_mdp {
+	connectors = <&sde_rscc &sde_wb>;
+};
+
+&dsi_sharp_4k_dsc_video {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 52 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+	qcom,platform-reset-gpio = <&tlmm 6 0>;
+};
+
+&dsi_sharp_4k_dsc_cmd {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 52 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+	qcom,platform-reset-gpio = <&tlmm 6 0>;
+};
+
+&dsi_sharp_4k_dsc_video_display {
+	qcom,dsi-display-active;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-v2-4k-panel-mtp-overlay.dts b/arch/arm64/boot/dts/qcom/sdm845-v2-4k-panel-mtp-overlay.dts
new file mode 100644
index 0000000..0d6c5e0
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm845-v2-4k-panel-mtp-overlay.dts
@@ -0,0 +1,66 @@
+/* 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.
+ */
+
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/qcom,gcc-sdm845.h>
+#include <dt-bindings/clock/qcom,camcc-sdm845.h>
+#include <dt-bindings/clock/qcom,dispcc-sdm845.h>
+#include <dt-bindings/clock/qcom,rpmh.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#include "sdm845-sde-display.dtsi"
+#include "sdm845-mtp.dtsi"
+#include "sdm845-audio-overlay.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. sdm845 v2 4K Display Panel MTP";
+	compatible = "qcom,sdm845-mtp", "qcom,sdm845", "qcom,mtp";
+	qcom,msm-id = <321 0x20000>;
+	qcom,board-id = <8 1>;
+};
+
+&dsi_nt35597_truly_dsc_cmd_display {
+	/delete-property/ qcom,dsi-display-active;
+};
+
+&mdss_mdp {
+	connectors = <&sde_rscc &sde_wb &sde_dp>;
+};
+
+&dsi_sharp_4k_dsc_video {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 52 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+	qcom,platform-reset-gpio = <&tlmm 6 0>;
+};
+
+&dsi_sharp_4k_dsc_cmd {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 52 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+	qcom,platform-reset-gpio = <&tlmm 6 0>;
+};
+
+&dsi_sharp_4k_dsc_video_display {
+	qcom,dsi-display-active;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-v2-4k-panel-qrd-overlay.dts b/arch/arm64/boot/dts/qcom/sdm845-v2-4k-panel-qrd-overlay.dts
new file mode 100644
index 0000000..764c145
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm845-v2-4k-panel-qrd-overlay.dts
@@ -0,0 +1,64 @@
+/* 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.
+ */
+
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/qcom,gcc-sdm845.h>
+#include <dt-bindings/clock/qcom,camcc-sdm845.h>
+#include <dt-bindings/clock/qcom,dispcc-sdm845.h>
+#include <dt-bindings/clock/qcom,rpmh.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#include "sdm845-sde-display.dtsi"
+#include "sdm845-qrd.dtsi"
+#include "sdm845-qrd-audio-overlay.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. sdm845 v2 4K Display Panel QRD";
+	compatible = "qcom,sdm845-qrd", "qcom,sdm845", "qcom,qrd";
+	qcom,msm-id = <321 0x20000>;
+	qcom,board-id = <11 1>;
+};
+
+&dsi_nt35597_truly_dsc_cmd_display {
+	/delete-property/ qcom,dsi-display-active;
+};
+
+&dsi_sharp_4k_dsc_video {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 52 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+	qcom,platform-reset-gpio = <&tlmm 6 0>;
+	qcom,mdss-dsi-panel-orientation = "180";
+};
+
+&dsi_sharp_4k_dsc_cmd {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 52 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+	qcom,platform-reset-gpio = <&tlmm 6 0>;
+	qcom,mdss-dsi-panel-orientation = "180";
+};
+
+&dsi_sharp_4k_dsc_video_display {
+	qcom,dsi-display-active;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-v2.dtsi b/arch/arm64/boot/dts/qcom/sdm845-v2.dtsi
index 98ab8d4..11d6d8b 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-v2.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-v2.dtsi
@@ -843,6 +843,25 @@
 	qcom,count-unit = <0x10000>;
 };
 
+&cpubw {
+	qcom,bw-tbl =
+		< MHZ_TO_MBPS(150, 16) >, /*  2288 MB/s */
+		< MHZ_TO_MBPS(300, 16) >, /*  4577 MB/s */
+		< MHZ_TO_MBPS(426, 16) >, /*  6500 MB/s */
+		< MHZ_TO_MBPS(533, 16) >, /*  8132 MB/s */
+		< MHZ_TO_MBPS(600, 16) >, /*  9155 MB/s */
+		< MHZ_TO_MBPS(806, 16) >, /* 12298 MB/s */
+		< MHZ_TO_MBPS(933, 16) >; /* 14236 MB/s */
+};
+
+&devfreq_cpufreq {
+	mincpubw-cpufreq {
+		cpu-to-dev-map-4 =
+			< 1881600 MHZ_TO_MBPS(200, 4) >,
+			< 2400000 MHZ_TO_MBPS(681, 4) >;
+	};
+};
+
 &clock_gcc {
 	compatible = "qcom,gcc-sdm845-v2", "syscon";
 };
@@ -890,10 +909,10 @@
 &energy_costs {
 	CPU_COST_0: core-cost0 {
 		busy-cost-data = <
-			 300000 11
+			 300000 12
 			 403200 17
 			 480000 21
-			 576000 26
+			 576000 27
 			 652800 31
 			 748800 37
 			 825600 42
@@ -901,13 +920,13 @@
 			 979200 52
 			1056000 57
 			1132800 62
-			1228800 69
+			1228800 70
 			1324800 78
 			1420800 89
 			1516800 103
 			1612800 122
-			1689600 140
-			1766400 159
+			1689600 141
+			1766400 160
 		>;
 		idle-cost-data = <
 			22 18 14 12
@@ -915,37 +934,37 @@
 	};
 	CPU_COST_1: core-cost1 {
 		busy-cost-data = <
-			 300000 130
-			 403200 480
-			 480000 730
-			 576000 1030
-			 652800 1260
-			 748800 1530
-			 825600 1740
-			 902400 1930
-			 979200 2110
-			1056000 2290
-			1132800 2460
-			1209600 2630
-			1286400 2800
-			1363200 2980
-			1459200 3240
-			1536000 3490
-			1612800 3780
-			1689600 4120
-			1766400 4530
-			1843200 5020
-			1920000 5590
-			1996800 6230
+			 300000 189
+			 403200 523
+			 480000 763
+			 576000 1052
+			 652800 1273
+			 748800 1536
+			 825600 1736
+			 902400 1926
+			 979200 2108
+			1056000 2284
+			1132800 2456
+			1209600 2628
+			1286400 2804
+			1363200 2992
+			1459200 3255
+			1536000 3499
+			1612800 3786
+			1689600 4128
+			1766400 4535
+			1843200 5019
+			1920000 5583
+			1996800 6226
 			2092800 7120
-			2169600 7870
-			2246400 8620
-			2323200 9330
+			2169600 7876
+			2246400 8628
+			2323200 9344
 			2400000 10030
-			2476800 10830
-			2553600 12080
-			2630400 14580
-			2707200 19960
+			2476800 10806
+			2553600 12045
+			2649600 15686
+			2745600 25586
 		>;
 		idle-cost-data = <
 			100 80 60 40
@@ -1007,8 +1026,8 @@
 			2400000 135
 			2476800 140
 			2553600 145
-			2630400 150
-			2707200 155
+			2649600 150
+			2745600 155
 		>;
 		idle-cost-data = <
 			4 3 2 1
diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
index 0b02e20..fc130cb 100644
--- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
@@ -547,6 +547,7 @@
 
 		wlan_fw_region: wlan_fw_region@8cb00000 {
 			compatible = "shared-dma-pool";
+			no-map;
 			reg = <0 0x8cb00000 0 0x100000>;
 		};
 
@@ -827,12 +828,12 @@
 			<MSM_BUS_MASTER_AMPSS_M0 MSM_BUS_SLAVE_LLCC>;
 		qcom,active-only;
 		qcom,bw-tbl =
-			<  2288 /* 150 MHz */ >,
-			<  4577 /* 300 MHz */ >,
-			<  6500 /* 426 MHz */ >,
-			<  8132 /* 533 MHz */ >,
-			<  9155 /* 600 MHz */ >,
-			< 10681 /* 700 MHz */ >;
+			< MHZ_TO_MBPS(150, 16) >, /*  2288 MB/s */
+			< MHZ_TO_MBPS(300, 16) >, /*  4577 MB/s */
+			< MHZ_TO_MBPS(426, 16) >, /*  6500 MB/s */
+			< MHZ_TO_MBPS(533, 16) >, /*  8132 MB/s */
+			< MHZ_TO_MBPS(600, 16) >, /*  9155 MB/s */
+			< MHZ_TO_MBPS(700, 16) >; /* 10681 MB/s */
 	};
 
 	bwmon: qcom,cpu-bwmon {
@@ -852,16 +853,16 @@
 			<MSM_BUS_MASTER_LLCC MSM_BUS_SLAVE_EBI_CH0>;
 		qcom,active-only;
 		qcom,bw-tbl =
-			<  762 /*  200 MHz */ >,
-			< 1144 /*  300 MHz */ >,
-			< 1720 /*  451 MHz */ >,
-			< 2086 /*  547 MHz */ >,
-			< 2597 /*  681 MHz */ >,
-			< 2929 /*  768 MHz */ >,
-			< 3879 /* 1017 MHz */ >,
-			< 4943 /* 1296 MHz */ >,
-			< 5931 /* 1555 MHz */ >,
-			< 6881 /* 1804 MHz */ >;
+			< MHZ_TO_MBPS( 200, 4) >, /* 762 MB/s */
+			< MHZ_TO_MBPS( 300, 4) >, /* 1144 MB/s */
+			< MHZ_TO_MBPS( 451, 4) >, /* 1720 MB/s */
+			< MHZ_TO_MBPS( 547, 4) >, /* 2086 MB/s */
+			< MHZ_TO_MBPS( 681, 4) >, /* 2597 MB/s */
+			< MHZ_TO_MBPS( 768, 4) >, /* 2929 MB/s */
+			< MHZ_TO_MBPS(1017, 4) >, /* 3879 MB/s */
+			< MHZ_TO_MBPS(1296, 4) >, /* 4943 MB/s */
+			< MHZ_TO_MBPS(1555, 4) >, /* 5931 MB/s */
+			< MHZ_TO_MBPS(1804, 4) >; /* 6881 MB/s */
 	};
 
 	llcc_bwmon: qcom,llcc-bwmon {
@@ -882,16 +883,16 @@
 		qcom,src-dst-ports = <1 512>;
 		qcom,active-only;
 		qcom,bw-tbl =
-			<  762 /*  200 MHz */ >,
-			< 1144 /*  300 MHz */ >,
-			< 1720 /*  451 MHz */ >,
-			< 2086 /*  547 MHz */ >,
-			< 2597 /*  681 MHz */ >,
-			< 2929 /*  768 MHz */ >,
-			< 3879 /* 1017 MHz */ >,
-			< 4943 /* 1296 MHz */ >,
-			< 5931 /* 1555 MHz */ >,
-			< 6881 /* 1804 MHz */ >;
+			< MHZ_TO_MBPS( 200, 4) >, /* 762 MB/s */
+			< MHZ_TO_MBPS( 300, 4) >, /* 1144 MB/s */
+			< MHZ_TO_MBPS( 451, 4) >, /* 1720 MB/s */
+			< MHZ_TO_MBPS( 547, 4) >, /* 2086 MB/s */
+			< MHZ_TO_MBPS( 681, 4) >, /* 2597 MB/s */
+			< MHZ_TO_MBPS( 768, 4) >, /* 2929 MB/s */
+			< MHZ_TO_MBPS(1017, 4) >, /* 3879 MB/s */
+			< MHZ_TO_MBPS(1296, 4) >, /* 4943 MB/s */
+			< MHZ_TO_MBPS(1555, 4) >, /* 5931 MB/s */
+			< MHZ_TO_MBPS(1804, 4) >; /* 6881 MB/s */
 	};
 
 	memlat_cpu4: qcom,memlat-cpu4 {
@@ -901,16 +902,16 @@
 		qcom,active-only;
 		status = "ok";
 		qcom,bw-tbl =
-			<  762 /*  200 MHz */ >,
-			< 1144 /*  300 MHz */ >,
-			< 1720 /*  451 MHz */ >,
-			< 2086 /*  547 MHz */ >,
-			< 2597 /*  681 MHz */ >,
-			< 2929 /*  768 MHz */ >,
-			< 3879 /* 1017 MHz */ >,
-			< 4943 /* 1296 MHz */ >,
-			< 5931 /* 1555 MHz */ >,
-			< 6881 /* 1804 MHz */ >;
+			< MHZ_TO_MBPS( 200, 4) >, /* 762 MB/s */
+			< MHZ_TO_MBPS( 300, 4) >, /* 1144 MB/s */
+			< MHZ_TO_MBPS( 451, 4) >, /* 1720 MB/s */
+			< MHZ_TO_MBPS( 547, 4) >, /* 2086 MB/s */
+			< MHZ_TO_MBPS( 681, 4) >, /* 2597 MB/s */
+			< MHZ_TO_MBPS( 768, 4) >, /* 2929 MB/s */
+			< MHZ_TO_MBPS(1017, 4) >, /* 3879 MB/s */
+			< MHZ_TO_MBPS(1296, 4) >, /* 4943 MB/s */
+			< MHZ_TO_MBPS(1555, 4) >, /* 5931 MB/s */
+			< MHZ_TO_MBPS(1804, 4) >; /* 6881 MB/s */
 	};
 
 	snoc_cnoc_keepalive: qcom,snoc_cnoc_keepalive {
@@ -929,11 +930,11 @@
 		qcom,target-dev = <&memlat_cpu0>;
 		qcom,cachemiss-ev = <0x2A>;
 		qcom,core-dev-table =
-			<  300000  762 >,
-			<  748800 1720 >,
-			< 1132800 2086 >,
-			< 1440000 2929 >,
-			< 1593600 3879 >;
+			<  300000 MHZ_TO_MBPS( 200, 4) >,
+			<  748800 MHZ_TO_MBPS( 451, 4) >,
+			< 1132800 MHZ_TO_MBPS( 547, 4) >,
+			< 1440000 MHZ_TO_MBPS( 768, 4) >,
+			< 1593600 MHZ_TO_MBPS(1017, 4) >;
 	};
 
 	devfreq_memlat_4: qcom,cpu4-memlat-mon {
@@ -942,14 +943,14 @@
 		qcom,target-dev = <&memlat_cpu4>;
 		qcom,cachemiss-ev = <0x2A>;
 		qcom,core-dev-table =
-			<  300000  762 >,
-			<  499200 1720 >,
-			<  806400 2086 >,
-			< 1036800 2929 >,
-			< 1190400 3879 >,
-			< 1574400 4943 >,
-			< 1728000 5931 >,
-			< 1958400 6881 >;
+			<  300000 MHZ_TO_MBPS( 200, 4) >,
+			<  499200 MHZ_TO_MBPS( 451, 4) >,
+			<  806400 MHZ_TO_MBPS( 547, 4) >,
+			< 1036800 MHZ_TO_MBPS( 768, 4) >,
+			< 1190400 MHZ_TO_MBPS(1017, 4) >,
+			< 1574400 MHZ_TO_MBPS(1296, 4) >,
+			< 1728000 MHZ_TO_MBPS(1555, 4) >,
+			< 1958400 MHZ_TO_MBPS(1804, 4) >;
 	};
 
 	l3_cpu0: qcom,l3-cpu0 {
@@ -1007,26 +1008,26 @@
 		qcom,src-dst-ports = <1 512>;
 		qcom,active-only;
 		qcom,bw-tbl =
-			<  762 /*  200 MHz */ >,
-			< 1144 /*  300 MHz */ >,
-			< 1720 /*  451 MHz */ >,
-			< 2086 /*  547 MHz */ >,
-			< 2597 /*  681 MHz */ >,
-			< 2929 /*  768 MHz */ >,
-			< 3879 /* 1017 MHz */ >,
-			< 4943 /* 1296 MHz */ >,
-			< 5931 /* 1555 MHz */ >,
-			< 6881 /* 1804 MHz */ >;
+			< MHZ_TO_MBPS( 200, 4) >, /* 762 MB/s */
+			< MHZ_TO_MBPS( 300, 4) >, /* 1144 MB/s */
+			< MHZ_TO_MBPS( 451, 4) >, /* 1720 MB/s */
+			< MHZ_TO_MBPS( 547, 4) >, /* 2086 MB/s */
+			< MHZ_TO_MBPS( 681, 4) >, /* 2597 MB/s */
+			< MHZ_TO_MBPS( 768, 4) >, /* 2929 MB/s */
+			< MHZ_TO_MBPS(1017, 4) >, /* 3879 MB/s */
+			< MHZ_TO_MBPS(1296, 4) >, /* 4943 MB/s */
+			< MHZ_TO_MBPS(1555, 4) >, /* 5931 MB/s */
+			< MHZ_TO_MBPS(1804, 4) >; /* 6881 MB/s */
 	};
 
-	devfreq-cpufreq {
+	devfreq_cpufreq: devfreq-cpufreq {
 		mincpubw-cpufreq {
 			target-dev = <&mincpubw>;
 			cpu-to-dev-map-0 =
-				< 1708800  762 >;
+				< 1708800 MHZ_TO_MBPS(200, 4) >;
 			cpu-to-dev-map-4 =
-				< 1881600  762 >,
-				< 2208000 2597 >;
+				< 1881600 MHZ_TO_MBPS(200, 4) >,
+				< 2208000 MHZ_TO_MBPS(681, 4) >;
 		};
 	};
 
@@ -3008,6 +3009,7 @@
 			     <0 424 0 /* CE10 */ >,
 			     <0 425 0 /* CE11 */ >;
 		qcom,wlan-msa-memory = <0x100000>;
+		qcom,wlan-msa-fixed-region = <&wlan_fw_region>;
 
 		vdd-0.8-cx-mx-supply = <&pm8998_l5>;
 		vdd-1.8-xo-supply = <&pm8998_l7>;
diff --git a/arch/arm64/configs/sdm670-perf_defconfig b/arch/arm64/configs/sdm670-perf_defconfig
index 59e795b..07e7ee9 100644
--- a/arch/arm64/configs/sdm670-perf_defconfig
+++ b/arch/arm64/configs/sdm670-perf_defconfig
@@ -422,6 +422,7 @@
 CONFIG_MMC_SDHCI=y
 CONFIG_MMC_SDHCI_PLTFM=y
 CONFIG_MMC_SDHCI_MSM=y
+CONFIG_MMC_CQ_HCI=y
 CONFIG_NEW_LEDS=y
 CONFIG_LEDS_CLASS=y
 CONFIG_LEDS_QPNP=y
diff --git a/arch/arm64/configs/sdm670_defconfig b/arch/arm64/configs/sdm670_defconfig
index bdc27ea..cda924c 100644
--- a/arch/arm64/configs/sdm670_defconfig
+++ b/arch/arm64/configs/sdm670_defconfig
@@ -410,6 +410,7 @@
 CONFIG_MMC=y
 CONFIG_MMC_PERF_PROFILING=y
 CONFIG_MMC_RING_BUFFER=y
+CONFIG_MMC_PARANOID_SD_INIT=y
 CONFIG_MMC_CLKGATE=y
 CONFIG_MMC_BLOCK_MINORS=32
 CONFIG_MMC_BLOCK_DEFERRED_RESUME=y
@@ -417,6 +418,7 @@
 CONFIG_MMC_SDHCI=y
 CONFIG_MMC_SDHCI_PLTFM=y
 CONFIG_MMC_SDHCI_MSM=y
+CONFIG_MMC_CQ_HCI=y
 CONFIG_NEW_LEDS=y
 CONFIG_LEDS_CLASS=y
 CONFIG_LEDS_QPNP=y
diff --git a/arch/arm64/configs/sdm845_defconfig b/arch/arm64/configs/sdm845_defconfig
index 4b347e6..f202f7e 100644
--- a/arch/arm64/configs/sdm845_defconfig
+++ b/arch/arm64/configs/sdm845_defconfig
@@ -610,6 +610,7 @@
 CONFIG_DEBUG_STACK_USAGE=y
 CONFIG_DEBUG_MEMORY_INIT=y
 CONFIG_LOCKUP_DETECTOR=y
+CONFIG_BOOTPARAM_HARDLOCKUP_PANIC=y
 CONFIG_WQ_WATCHDOG=y
 CONFIG_PANIC_TIMEOUT=5
 CONFIG_PANIC_ON_SCHED_BUG=y
diff --git a/drivers/android/binder.c b/drivers/android/binder.c
index 88edacd..9f1a1bb 100644
--- a/drivers/android/binder.c
+++ b/drivers/android/binder.c
@@ -3252,6 +3252,7 @@
 err_dead_proc_or_thread:
 	return_error = BR_DEAD_REPLY;
 	return_error_line = __LINE__;
+	binder_dequeue_work(proc, tcomplete);
 err_translate_failed:
 err_bad_object_type:
 err_bad_offset:
diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c
index 55687b8..e17ad53 100644
--- a/drivers/base/cpu.c
+++ b/drivers/base/cpu.c
@@ -208,6 +208,59 @@
 
 #endif
 
+static ssize_t show_sched_load_boost(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	ssize_t rc;
+	unsigned int boost;
+	struct cpu *cpu = container_of(dev, struct cpu, dev);
+	int cpuid = cpu->dev.id;
+
+	boost = per_cpu(sched_load_boost, cpuid);
+	rc = snprintf(buf, PAGE_SIZE-2, "%d\n", boost);
+
+	return rc;
+}
+
+static ssize_t __ref store_sched_load_boost(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	int err;
+	int boost;
+	struct cpu *cpu = container_of(dev, struct cpu, dev);
+	int cpuid = cpu->dev.id;
+
+	err = kstrtoint(strstrip((char *)buf), 0, &boost);
+	if (err)
+		return err;
+
+	/*
+	 * -100 is low enough to cancel out CPU's load and make it near zro.
+	 * 1000 is close to the maximum value that cpu_util_freq_{walt,pelt}
+	 * can take without overflow.
+	 */
+	if (boost < -100 || boost > 1000)
+		return -EINVAL;
+
+	per_cpu(sched_load_boost, cpuid) = boost;
+
+	return count;
+}
+
+static DEVICE_ATTR(sched_load_boost, 0644,
+		   show_sched_load_boost,
+		   store_sched_load_boost);
+
+static struct attribute *sched_cpu_attrs[] = {
+	&dev_attr_sched_load_boost.attr,
+	NULL
+};
+
+static struct attribute_group sched_cpu_attr_group = {
+	.attrs = sched_cpu_attrs,
+};
+
 static const struct attribute_group *common_cpu_attr_groups[] = {
 #ifdef CONFIG_KEXEC
 	&crash_note_cpu_attr_group,
@@ -215,6 +268,7 @@
 #ifdef CONFIG_HOTPLUG_CPU
 	&cpu_isolated_attr_group,
 #endif
+	&sched_cpu_attr_group,
 	NULL
 };
 
@@ -225,6 +279,7 @@
 #ifdef CONFIG_HOTPLUG_CPU
 	&cpu_isolated_attr_group,
 #endif
+	&sched_cpu_attr_group,
 	NULL
 };
 
diff --git a/drivers/clk/qcom/camcc-sdm845.c b/drivers/clk/qcom/camcc-sdm845.c
index 1984d4a..3819959 100644
--- a/drivers/clk/qcom/camcc-sdm845.c
+++ b/drivers/clk/qcom/camcc-sdm845.c
@@ -1959,6 +1959,7 @@
 static const struct of_device_id cam_cc_sdm845_match_table[] = {
 	{ .compatible = "qcom,cam_cc-sdm845" },
 	{ .compatible = "qcom,cam_cc-sdm845-v2" },
+	{ .compatible = "qcom,cam_cc-sdm670" },
 	{ }
 };
 MODULE_DEVICE_TABLE(of, cam_cc_sdm845_match_table);
@@ -1986,6 +1987,11 @@
 	cam_cc_slow_ahb_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 80000000;
 }
 
+static void cam_cc_sdm845_fixup_sdm670(void)
+{
+	cam_cc_sdm845_fixup_sdm845v2();
+}
+
 static int cam_cc_sdm845_fixup(struct platform_device *pdev)
 {
 	const char *compat = NULL;
@@ -1997,6 +2003,8 @@
 
 	if (!strcmp(compat, "qcom,cam_cc-sdm845-v2"))
 		cam_cc_sdm845_fixup_sdm845v2();
+	else if (!strcmp(compat, "qcom,cam_cc-sdm670"))
+		cam_cc_sdm845_fixup_sdm670();
 
 	return 0;
 }
diff --git a/drivers/clk/qcom/clk-rpmh.c b/drivers/clk/qcom/clk-rpmh.c
index e1cda90..2109132 100644
--- a/drivers/clk/qcom/clk-rpmh.c
+++ b/drivers/clk/qcom/clk-rpmh.c
@@ -317,10 +317,32 @@
 
 static const struct of_device_id clk_rpmh_match_table[] = {
 	{ .compatible = "qcom,rpmh-clk-sdm845", .data = &clk_rpmh_sdm845},
+	{ .compatible = "qcom,rpmh-clk-sdm670", .data = &clk_rpmh_sdm845},
 	{ }
 };
 MODULE_DEVICE_TABLE(of, clk_rpmh_match_table);
 
+static void clk_rpmh_sdm670_fixup_sdm670(void)
+{
+	sdm845_rpmh_clocks[RPMH_RF_CLK3] = NULL;
+	sdm845_rpmh_clocks[RPMH_RF_CLK3_A] = NULL;
+}
+
+static int clk_rpmh_sdm670_fixup(struct platform_device *pdev)
+{
+	const char *compat = NULL;
+	int compatlen = 0;
+
+	compat = of_get_property(pdev->dev.of_node, "compatible", &compatlen);
+	if (!compat || (compatlen <= 0))
+		return -EINVAL;
+
+	if (!strcmp(compat, "qcom,rpmh-clk-sdm670"))
+		clk_rpmh_sdm670_fixup_sdm670();
+
+	return 0;
+}
+
 static int clk_rpmh_probe(struct platform_device *pdev)
 {
 	struct clk **clks;
@@ -388,6 +410,10 @@
 		goto err2;
 	}
 
+	ret = clk_rpmh_sdm670_fixup(pdev);
+	if (ret)
+		return ret;
+
 	hw_clks = desc->clks;
 	num_clks = desc->num_clks;
 
@@ -404,6 +430,11 @@
 	data->clk_num = num_clks;
 
 	for (i = 0; i < num_clks; i++) {
+		if (!hw_clks[i]) {
+			clks[i] = ERR_PTR(-ENOENT);
+			continue;
+		}
+
 		rpmh_clk = to_clk_rpmh(hw_clks[i]);
 		rpmh_clk->res_addr = cmd_db_get_addr(rpmh_clk->res_name);
 		if (!rpmh_clk->res_addr) {
diff --git a/drivers/clk/qcom/debugcc-sdm845.c b/drivers/clk/qcom/debugcc-sdm845.c
index cb0cadd..ef1da5c 100644
--- a/drivers/clk/qcom/debugcc-sdm845.c
+++ b/drivers/clk/qcom/debugcc-sdm845.c
@@ -235,6 +235,9 @@
 	"gcc_video_ahb_clk",
 	"gcc_video_axi_clk",
 	"gcc_video_xo_clk",
+	"gcc_sdcc1_ahb_clk",
+	"gcc_sdcc1_apps_clk",
+	"gcc_sdcc1_ice_core_clk",
 	"gpu_cc_acd_cxo_clk",
 	"gpu_cc_ahb_clk",
 	"gpu_cc_crc_ahb_clk",
@@ -685,6 +688,12 @@
 			0x3F, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 },
 		{ "gcc_video_xo_clk", 0x42, 4, GCC,
 			0x42, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 },
+		{ "gcc_sdcc1_ahb_clk", 0x15C, 4, GCC,
+			0x42, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 },
+		{ "gcc_sdcc1_apps_clk", 0x15B, 4, GCC,
+			0x42, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 },
+		{ "gcc_sdcc1_ice_core_clk", 0x15D, 4, GCC,
+			0x42, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 },
 		{ "gpu_cc_acd_cxo_clk", 0x144, 4, GPU_CC,
 			0x1F, 0xFF, 0, 0x3, 0, 1, 0x1568, 0x10FC, 0x1100 },
 		{ "gpu_cc_ahb_clk", 0x144, 4, GPU_CC,
diff --git a/drivers/clk/qcom/dispcc-sdm845.c b/drivers/clk/qcom/dispcc-sdm845.c
index 53bfe77..d57bf5f 100644
--- a/drivers/clk/qcom/dispcc-sdm845.c
+++ b/drivers/clk/qcom/dispcc-sdm845.c
@@ -1014,6 +1014,7 @@
 static const struct of_device_id disp_cc_sdm845_match_table[] = {
 	{ .compatible = "qcom,dispcc-sdm845" },
 	{ .compatible = "qcom,dispcc-sdm845-v2" },
+	{ .compatible = "qcom,dispcc-sdm670" },
 	{ }
 };
 MODULE_DEVICE_TABLE(of, disp_cc_sdm845_match_table);
@@ -1064,6 +1065,11 @@
 		430000000;
 }
 
+static void disp_cc_sdm845_fixup_sdm670(struct regmap *regmap)
+{
+	disp_cc_sdm845_fixup_sdm845v2(regmap);
+}
+
 static int disp_cc_sdm845_fixup(struct platform_device *pdev,
 						struct regmap *regmap)
 {
@@ -1076,6 +1082,8 @@
 
 	if (!strcmp(compat, "qcom,dispcc-sdm845-v2"))
 		disp_cc_sdm845_fixup_sdm845v2(regmap);
+	else if (!strcmp(compat, "qcom,dispcc-sdm670"))
+		disp_cc_sdm845_fixup_sdm670(regmap);
 
 	return 0;
 }
diff --git a/drivers/clk/qcom/gcc-sdm845.c b/drivers/clk/qcom/gcc-sdm845.c
index 17b2403..a363235 100644
--- a/drivers/clk/qcom/gcc-sdm845.c
+++ b/drivers/clk/qcom/gcc-sdm845.c
@@ -198,6 +198,22 @@
 	"core_bi_pll_test_se",
 };
 
+static const struct parent_map gcc_parent_map_7[] = {
+	{ P_BI_TCXO, 0 },
+	{ P_GPLL0_OUT_MAIN, 1 },
+	{ P_GPLL6_OUT_MAIN, 2 },
+	{ P_GPLL0_OUT_EVEN, 6 },
+	{ P_CORE_BI_PLL_TEST_SE, 7 },
+};
+
+static const char * const gcc_parent_names_11[] = {
+	"bi_tcxo",
+	"gpll0",
+	"gpll6",
+	"gpll0_out_even",
+	"core_bi_pll_test_se",
+};
+
 static struct clk_dummy measure_only_snoc_clk = {
 	.rrate = 1000,
 	.hw.init = &(struct clk_init_data){
@@ -301,6 +317,28 @@
 	},
 };
 
+static struct clk_alpha_pll gpll6 = {
+	.offset = 0x13000,
+	.vco_table = fabia_vco,
+	.num_vco = ARRAY_SIZE(fabia_vco),
+	.type = FABIA_PLL,
+	.clkr = {
+		.enable_reg = 0x52000,
+		.enable_mask = BIT(6),
+		.hw.init = &(struct clk_init_data){
+			.name = "gpll6",
+			.parent_names = (const char *[]){ "bi_tcxo" },
+			.num_parents = 1,
+			.ops = &clk_fabia_fixed_pll_ops,
+			VDD_CX_FMAX_MAP4(
+				MIN, 615000000,
+				LOW, 1066000000,
+				LOW_L1, 1600000000,
+				NOMINAL, 2000000000),
+		},
+	},
+};
+
 static const struct freq_tbl ftbl_gcc_cpuss_ahb_clk_src[] = {
 	F(19200000, P_BI_TCXO, 1, 0, 0),
 	{ }
@@ -330,6 +368,12 @@
 	{ }
 };
 
+static const struct freq_tbl ftbl_gcc_cpuss_rbcpr_clk_src_sdm670[] = {
+	F(19200000, P_BI_TCXO, 1, 0, 0),
+	F(50000000, P_GPLL0_OUT_MAIN, 12, 0, 0),
+	{ }
+};
+
 static struct clk_rcg2 gcc_cpuss_rbcpr_clk_src = {
 	.cmd_rcgr = 0x4815c,
 	.mnd_width = 0,
@@ -862,6 +906,67 @@
 	},
 };
 
+static const struct freq_tbl ftbl_gcc_sdcc1_ice_core_clk_src[] = {
+	F(75000000, P_GPLL0_OUT_EVEN, 4, 0, 0),
+	F(150000000, P_GPLL0_OUT_MAIN, 4, 0, 0),
+	F(200000000, P_GPLL0_OUT_MAIN, 3, 0, 0),
+	F(300000000, P_GPLL0_OUT_MAIN, 2, 0, 0),
+	{ }
+};
+
+static struct clk_rcg2 gcc_sdcc1_ice_core_clk_src = {
+	.cmd_rcgr = 0x26010,
+	.mnd_width = 8,
+	.hid_width = 5,
+	.parent_map = gcc_parent_map_0,
+	.freq_tbl = ftbl_gcc_sdcc1_ice_core_clk_src,
+	.enable_safe_config = true,
+	.clkr.hw.init = &(struct clk_init_data){
+		.name = "gcc_sdcc1_ice_core_clk_src",
+		.parent_names = gcc_parent_names_0,
+		.num_parents = 4,
+		.flags = CLK_SET_RATE_PARENT,
+		.ops = &clk_rcg2_ops,
+		VDD_CX_FMAX_MAP3(
+			MIN, 75000000,
+			LOW, 150000000,
+			NOMINAL, 300000000),
+	},
+};
+
+static const struct freq_tbl ftbl_gcc_sdcc1_apps_clk_src[] = {
+	F(144000, P_BI_TCXO, 16, 3, 25),
+	F(400000, P_BI_TCXO, 12, 1, 4),
+	F(20000000, P_GPLL0_OUT_EVEN, 5, 1, 3),
+	F(25000000, P_GPLL0_OUT_EVEN, 6, 1, 2),
+	F(50000000, P_GPLL0_OUT_EVEN, 6, 0, 0),
+	F(100000000, P_GPLL0_OUT_MAIN, 6, 0, 0),
+	F(192000000, P_GPLL6_OUT_MAIN, 2, 0, 0),
+	F(384000000, P_GPLL6_OUT_MAIN, 1, 0, 0),
+	{ }
+};
+
+static struct clk_rcg2 gcc_sdcc1_apps_clk_src = {
+	.cmd_rcgr = 0x26028,
+	.mnd_width = 8,
+	.hid_width = 5,
+	.parent_map = gcc_parent_map_7,
+	.freq_tbl = ftbl_gcc_sdcc1_apps_clk_src,
+	.enable_safe_config = true,
+	.clkr.hw.init = &(struct clk_init_data){
+		.name = "gcc_sdcc1_apps_clk_src",
+		.parent_names = gcc_parent_names_11,
+		.num_parents = 5,
+		.flags = CLK_SET_RATE_PARENT,
+		.ops = &clk_rcg2_ops,
+		VDD_CX_FMAX_MAP4(
+			MIN, 19200000,
+			LOWER, 50000000,
+			LOW, 100000000,
+			NOMINAL, 384000000),
+	},
+};
+
 static const struct freq_tbl ftbl_gcc_sdcc2_apps_clk_src[] = {
 	F(400000, P_BI_TCXO, 12, 1, 4),
 	F(9600000, P_BI_TCXO, 2, 0, 0),
@@ -904,17 +1009,28 @@
 	{ }
 };
 
+static const struct freq_tbl ftbl_gcc_sdcc4_apps_clk_src_sdm670[] = {
+	F(400000, P_BI_TCXO, 12, 1, 4),
+	F(9600000, P_BI_TCXO, 2, 0, 0),
+	F(19200000, P_BI_TCXO, 1, 0, 0),
+	F(25000000, P_GPLL0_OUT_EVEN, 12, 0, 0),
+	F(33333333, P_GPLL0_OUT_EVEN, 9, 0, 0),
+	F(50000000, P_GPLL0_OUT_MAIN, 12, 0, 0),
+	F(100000000, P_GPLL0_OUT_MAIN, 6, 0, 0),
+	{ }
+};
+
 static struct clk_rcg2 gcc_sdcc4_apps_clk_src = {
 	.cmd_rcgr = 0x1600c,
 	.mnd_width = 8,
 	.hid_width = 5,
-	.parent_map = gcc_parent_map_3,
+	.parent_map = gcc_parent_map_0,
 	.freq_tbl = ftbl_gcc_sdcc4_apps_clk_src,
 	.enable_safe_config = true,
 	.clkr.hw.init = &(struct clk_init_data){
 		.name = "gcc_sdcc4_apps_clk_src",
-		.parent_names = gcc_parent_names_3,
-		.num_parents = 3,
+		.parent_names = gcc_parent_names_0,
+		.num_parents = 4,
 		.flags = CLK_SET_RATE_PARENT,
 		.ops = &clk_rcg2_ops,
 		VDD_CX_FMAX_MAP4(
@@ -2700,6 +2816,55 @@
 	},
 };
 
+static struct clk_branch gcc_sdcc1_ice_core_clk = {
+	.halt_reg = 0x2600c,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x2600c,
+		.enable_mask = BIT(0),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_sdcc1_ice_core_clk",
+			.parent_names = (const char *[]){
+				"gcc_sdcc1_ice_core_clk_src",
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_sdcc1_ahb_clk = {
+	.halt_reg = 0x26008,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x26008,
+		.enable_mask = BIT(0),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_sdcc1_ahb_clk",
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_sdcc1_apps_clk = {
+	.halt_reg = 0x26004,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x26004,
+		.enable_mask = BIT(0),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_sdcc1_apps_clk",
+			.parent_names = (const char *[]){
+				"gcc_sdcc1_apps_clk_src",
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
 static struct clk_branch gcc_sdcc2_ahb_clk = {
 	.halt_reg = 0x14008,
 	.halt_check = BRANCH_HALT,
@@ -3824,6 +3989,12 @@
 	[GPLL0] = &gpll0.clkr,
 	[GPLL0_OUT_EVEN] = &gpll0_out_even.clkr,
 	[GPLL4] = &gpll4.clkr,
+	[GCC_SDCC1_AHB_CLK] = NULL,
+	[GCC_SDCC1_APPS_CLK] = NULL,
+	[GCC_SDCC1_ICE_CORE_CLK] = NULL,
+	[GCC_SDCC1_APPS_CLK_SRC] = NULL,
+	[GCC_SDCC1_ICE_CORE_CLK_SRC] = NULL,
+	[GPLL6] = NULL,
 };
 
 static const struct qcom_reset_map gcc_sdm845_resets[] = {
@@ -3853,6 +4024,7 @@
 	[GCC_USB_PHY_CFG_AHB2PHY_BCR] = { 0x6a000 },
 	[GCC_PCIE_0_PHY_BCR] = { 0x6c01c },
 	[GCC_PCIE_1_PHY_BCR] = { 0x8e01c },
+	[GCC_SDCC1_BCR] = { 0x26000 },
 };
 
 /* List of RCG clocks and corresponding flags requested for DFS Mode */
@@ -3899,6 +4071,7 @@
 static const struct of_device_id gcc_sdm845_match_table[] = {
 	{ .compatible = "qcom,gcc-sdm845" },
 	{ .compatible = "qcom,gcc-sdm845-v2" },
+	{ .compatible = "qcom,gcc-sdm670" },
 	{ }
 };
 MODULE_DEVICE_TABLE(of, gcc_sdm845_match_table);
@@ -4009,6 +4182,86 @@
 		ftbl_gcc_ufs_card_axi_clk_src_sdm845_v2;
 }
 
+static void gcc_sdm845_fixup_sdm670(void)
+{
+	gcc_sdm845_fixup_sdm845v2();
+
+	gcc_sdm845_clocks[GCC_SDCC1_AHB_CLK] = &gcc_sdcc1_ahb_clk.clkr;
+	gcc_sdm845_clocks[GCC_SDCC1_APPS_CLK] = &gcc_sdcc1_apps_clk.clkr;
+	gcc_sdm845_clocks[GCC_SDCC1_ICE_CORE_CLK] =
+					&gcc_sdcc1_ice_core_clk.clkr;
+	gcc_sdm845_clocks[GCC_SDCC1_APPS_CLK_SRC] =
+					&gcc_sdcc1_apps_clk_src.clkr;
+	gcc_sdm845_clocks[GCC_SDCC1_ICE_CORE_CLK_SRC] =
+					&gcc_sdcc1_ice_core_clk_src.clkr;
+	gcc_sdm845_clocks[GPLL6] = &gpll6.clkr;
+	gcc_sdm845_clocks[GCC_AGGRE_UFS_CARD_AXI_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_AGGRE_UFS_CARD_AXI_HW_CTL_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_AGGRE_USB3_SEC_AXI_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_AGGRE_NOC_PCIE_TBU_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_CFG_NOC_USB3_SEC_AXI_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_PCIE_0_AUX_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_PCIE_0_AUX_CLK_SRC] = NULL;
+	gcc_sdm845_clocks[GCC_PCIE_0_CFG_AHB_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_PCIE_0_CLKREF_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_PCIE_0_MSTR_AXI_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_PCIE_0_PIPE_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_PCIE_0_SLV_AXI_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_PCIE_0_SLV_Q2A_AXI_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_PCIE_1_AUX_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_PCIE_1_AUX_CLK_SRC] = NULL;
+	gcc_sdm845_clocks[GCC_PCIE_1_CFG_AHB_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_PCIE_1_CLKREF_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_PCIE_1_MSTR_AXI_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_PCIE_1_PIPE_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_PCIE_1_SLV_AXI_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_PCIE_1_SLV_Q2A_AXI_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_PCIE_PHY_AUX_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_PCIE_PHY_REFGEN_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_PCIE_PHY_REFGEN_CLK_SRC] = NULL;
+	gcc_sdm845_clocks[GCC_UFS_CARD_AHB_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_UFS_CARD_AXI_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_UFS_CARD_AXI_HW_CTL_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_UFS_CARD_AXI_CLK_SRC] = NULL;
+	gcc_sdm845_clocks[GCC_UFS_CARD_CLKREF_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_UFS_CARD_ICE_CORE_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_UFS_CARD_ICE_CORE_HW_CTL_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_UFS_CARD_ICE_CORE_CLK_SRC] = NULL;
+	gcc_sdm845_clocks[GCC_UFS_CARD_PHY_AUX_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_UFS_CARD_PHY_AUX_HW_CTL_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_UFS_CARD_PHY_AUX_CLK_SRC] = NULL;
+	gcc_sdm845_clocks[GCC_UFS_CARD_RX_SYMBOL_0_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_UFS_CARD_RX_SYMBOL_1_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_UFS_CARD_TX_SYMBOL_0_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_UFS_CARD_UNIPRO_CORE_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_UFS_CARD_UNIPRO_CORE_HW_CTL_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_UFS_CARD_UNIPRO_CORE_CLK_SRC] = NULL;
+	gcc_sdm845_clocks[GCC_UFS_PHY_RX_SYMBOL_1_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_USB30_SEC_MASTER_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_USB30_SEC_MASTER_CLK_SRC] = NULL;
+	gcc_sdm845_clocks[GCC_USB30_SEC_MOCK_UTMI_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_USB30_SEC_MOCK_UTMI_CLK_SRC] = NULL;
+	gcc_sdm845_clocks[GCC_USB30_SEC_SLEEP_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_USB3_SEC_CLKREF_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_USB3_SEC_PHY_AUX_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_USB3_SEC_PHY_AUX_CLK_SRC] = NULL;
+	gcc_sdm845_clocks[GCC_USB3_SEC_PHY_COM_AUX_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_USB3_SEC_PHY_PIPE_CLK] = NULL;
+
+	gcc_cpuss_rbcpr_clk_src.freq_tbl = ftbl_gcc_cpuss_rbcpr_clk_src_sdm670;
+	gcc_cpuss_rbcpr_clk_src.clkr.hw.init->rate_max[VDD_CX_NOMINAL] =
+					50000000;
+	gcc_sdcc2_apps_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] =
+					50000000;
+	gcc_sdcc2_apps_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW_L1] =
+					100000000;
+	gcc_sdcc2_apps_clk_src.clkr.hw.init->rate_max[VDD_CX_NOMINAL] =
+					201500000;
+	gcc_sdcc4_apps_clk_src.freq_tbl = ftbl_gcc_sdcc4_apps_clk_src_sdm670;
+	gcc_sdcc4_apps_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] =
+					33333333;
+}
+
 static int gcc_sdm845_fixup(struct platform_device *pdev)
 {
 	const char *compat = NULL;
@@ -4020,6 +4273,8 @@
 
 	if (!strcmp(compat, "qcom,gcc-sdm845-v2"))
 		gcc_sdm845_fixup_sdm845v2();
+	else if (!strcmp(compat, "qcom,gcc-sdm670"))
+		gcc_sdm845_fixup_sdm670();
 
 	return 0;
 }
diff --git a/drivers/clk/qcom/gpucc-sdm845.c b/drivers/clk/qcom/gpucc-sdm845.c
index cf4a8a5..db0dad1 100644
--- a/drivers/clk/qcom/gpucc-sdm845.c
+++ b/drivers/clk/qcom/gpucc-sdm845.c
@@ -224,6 +224,12 @@
 	{ }
 };
 
+static const struct freq_tbl ftbl_gpu_cc_gmu_clk_src_sdm670[] = {
+	F(19200000, P_BI_TCXO, 1, 0, 0),
+	F(200000000, P_GPLL0_OUT_MAIN_DIV, 1.5, 0, 0),
+	{ }
+};
+
 static struct clk_rcg2 gpu_cc_gmu_clk_src = {
 	.cmd_rcgr = 0x1120,
 	.mnd_width = 0,
@@ -279,6 +285,18 @@
 	{ }
 };
 
+static const struct freq_tbl ftbl_gpu_cc_gx_gfx3d_clk_src_sdm670[] = {
+	F(180000000, P_CRC_DIV,  1, 0, 0),
+	F(267000000, P_CRC_DIV,  1, 0, 0),
+	F(355000000, P_CRC_DIV,  1, 0, 0),
+	F(430000000, P_CRC_DIV,  1, 0, 0),
+	F(565000000, P_CRC_DIV,  1, 0, 0),
+	F(650000000, P_CRC_DIV,  1, 0, 0),
+	F(750000000, P_CRC_DIV,  1, 0, 0),
+	F(780000000, P_CRC_DIV,  1, 0, 0),
+	{ }
+};
+
 static struct clk_rcg2 gpu_cc_gx_gfx3d_clk_src = {
 	.cmd_rcgr = 0x101c,
 	.mnd_width = 0,
@@ -585,6 +603,7 @@
 static const struct of_device_id gpu_cc_sdm845_match_table[] = {
 	{ .compatible = "qcom,gpucc-sdm845" },
 	{ .compatible = "qcom,gpucc-sdm845-v2" },
+	{ .compatible = "qcom,gpucc-sdm670" },
 	{ }
 };
 MODULE_DEVICE_TABLE(of, gpu_cc_sdm845_match_table);
@@ -592,6 +611,7 @@
 static const struct of_device_id gpu_cc_gfx_sdm845_match_table[] = {
 	{ .compatible = "qcom,gfxcc-sdm845" },
 	{ .compatible = "qcom,gfxcc-sdm845-v2" },
+	{ .compatible = "qcom,gfxcc-sdm670" },
 	{},
 };
 MODULE_DEVICE_TABLE(of, gpu_cc_gfx_sdm845_match_table);
@@ -605,6 +625,15 @@
 	gpu_cc_gmu_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] = 500000000;
 }
 
+static void gpu_cc_sdm845_fixup_sdm670(struct regmap *regmap)
+{
+	gpu_cc_sdm845_clocks[GPU_CC_PLL1] = &gpu_cc_pll1.clkr;
+	clk_fabia_pll_configure(&gpu_cc_pll1, regmap, &gpu_cc_pll1_config);
+
+	gpu_cc_gmu_clk_src.freq_tbl = ftbl_gpu_cc_gmu_clk_src_sdm670;
+	gpu_cc_gmu_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] = 0;
+}
+
 static void gpu_cc_gfx_sdm845_fixup_sdm845v2(void)
 {
 	gpu_cc_gx_gfx3d_clk_src.freq_tbl =
@@ -624,6 +653,28 @@
 				710000000;
 }
 
+static void gpu_cc_gfx_sdm845_fixup_sdm670(void)
+{
+	gpu_cc_gx_gfx3d_clk_src.freq_tbl =
+				ftbl_gpu_cc_gx_gfx3d_clk_src_sdm670;
+	gpu_cc_gx_gfx3d_clk_src.clkr.hw.init->rate_max[VDD_GX_MIN] =
+				180000000;
+	gpu_cc_gx_gfx3d_clk_src.clkr.hw.init->rate_max[VDD_GX_LOWER] =
+				267000000;
+	gpu_cc_gx_gfx3d_clk_src.clkr.hw.init->rate_max[VDD_GX_LOW] =
+				355000000;
+	gpu_cc_gx_gfx3d_clk_src.clkr.hw.init->rate_max[VDD_GX_LOW_L1] =
+				430000000;
+	gpu_cc_gx_gfx3d_clk_src.clkr.hw.init->rate_max[VDD_GX_NOMINAL] =
+				565000000;
+	gpu_cc_gx_gfx3d_clk_src.clkr.hw.init->rate_max[VDD_GX_NOMINAL_L1] =
+				650000000;
+	gpu_cc_gx_gfx3d_clk_src.clkr.hw.init->rate_max[VDD_GX_HIGH] =
+				750000000;
+	gpu_cc_gx_gfx3d_clk_src.clkr.hw.init->rate_max[VDD_GX_HIGH_L1] =
+				780000000;
+}
+
 static int gpu_cc_gfx_sdm845_fixup(struct platform_device *pdev)
 {
 	const char *compat = NULL;
@@ -635,6 +686,8 @@
 
 	if (!strcmp(compat, "qcom,gfxcc-sdm845-v2"))
 		gpu_cc_gfx_sdm845_fixup_sdm845v2();
+	else if (!strcmp(compat, "qcom,gfxcc-sdm670"))
+		gpu_cc_gfx_sdm845_fixup_sdm670();
 
 	return 0;
 }
@@ -651,6 +704,8 @@
 
 	if (!strcmp(compat, "qcom,gpucc-sdm845-v2"))
 		gpu_cc_sdm845_fixup_sdm845v2(regmap);
+	else if (!strcmp(compat, "qcom,gpucc-sdm670"))
+		gpu_cc_sdm845_fixup_sdm670(regmap);
 
 	return 0;
 }
diff --git a/drivers/clk/qcom/videocc-sdm845.c b/drivers/clk/qcom/videocc-sdm845.c
index ba4e591..3311e9f 100644
--- a/drivers/clk/qcom/videocc-sdm845.c
+++ b/drivers/clk/qcom/videocc-sdm845.c
@@ -328,6 +328,7 @@
 static const struct of_device_id video_cc_sdm845_match_table[] = {
 	{ .compatible = "qcom,video_cc-sdm845" },
 	{ .compatible = "qcom,video_cc-sdm845-v2" },
+	{ .compatible = "qcom,video_cc-sdm670" },
 	{ }
 };
 MODULE_DEVICE_TABLE(of, video_cc_sdm845_match_table);
@@ -340,6 +341,12 @@
 		404000000;
 }
 
+static void video_cc_sdm845_fixup_sdm670(void)
+{
+	video_cc_sdm845_fixup_sdm845v2();
+
+}
+
 static int video_cc_sdm845_fixup(struct platform_device *pdev)
 {
 	const char *compat = NULL;
@@ -351,6 +358,8 @@
 
 	if (!strcmp(compat, "qcom,video_cc-sdm845-v2"))
 		video_cc_sdm845_fixup_sdm845v2();
+	else if (!strcmp(compat, "qcom,video_cc-sdm670"))
+		video_cc_sdm845_fixup_sdm670();
 
 	return 0;
 }
diff --git a/drivers/devfreq/governor_memlat.c b/drivers/devfreq/governor_memlat.c
index e1afa60..81d98d1 100644
--- a/drivers/devfreq/governor_memlat.c
+++ b/drivers/devfreq/governor_memlat.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 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
@@ -244,7 +244,11 @@
 					hw->core_stats[i].mem_count,
 					hw->core_stats[i].freq, ratio);
 
-		if (ratio && ratio <= node->ratio_ceil
+		if (!hw->core_stats[i].inst_count
+		    || !hw->core_stats[i].freq)
+			continue;
+
+		if (ratio <= node->ratio_ceil
 		    && hw->core_stats[i].freq > max_freq) {
 			lat_dev = i;
 			max_freq = hw->core_stats[i].freq;
diff --git a/drivers/edac/qcom_llcc_edac.c b/drivers/edac/qcom_llcc_edac.c
index 4b89cbf..038e89c 100644
--- a/drivers/edac/qcom_llcc_edac.c
+++ b/drivers/edac/qcom_llcc_edac.c
@@ -397,21 +397,6 @@
 	if (rc)
 		goto out_mem;
 
-	if (interrupt_mode) {
-		drv->ecc_irq = platform_get_irq_byname(pdev, "ecc_irq");
-		if (!drv->ecc_irq) {
-			rc = -ENODEV;
-			goto out_dev;
-		}
-
-		rc = devm_request_irq(dev, drv->ecc_irq, llcc_ecc_irq_handler,
-				IRQF_TRIGGER_HIGH, "llcc_ecc", edev_ctl);
-		if (rc) {
-			dev_err(dev, "failed to request ecc irq\n");
-			goto out_dev;
-		}
-	}
-
 	drv->llcc_banks = devm_kzalloc(&pdev->dev,
 		sizeof(u32) * drv->num_banks, GFP_KERNEL);
 
@@ -437,6 +422,21 @@
 
 	platform_set_drvdata(pdev, edev_ctl);
 
+	if (interrupt_mode) {
+		drv->ecc_irq = platform_get_irq_byname(pdev, "ecc_irq");
+		if (!drv->ecc_irq) {
+			rc = -ENODEV;
+			goto out_dev;
+		}
+
+		rc = devm_request_irq(dev, drv->ecc_irq, llcc_ecc_irq_handler,
+				IRQF_TRIGGER_HIGH, "llcc_ecc", edev_ctl);
+		if (rc) {
+			dev_err(dev, "failed to request ecc irq\n");
+			goto out_dev;
+		}
+	}
+
 	return 0;
 
 out_dev:
diff --git a/drivers/gpu/drm/msm/dp/dp_hdcp2p2.c b/drivers/gpu/drm/msm/dp/dp_hdcp2p2.c
index 061acee..016e1b8 100644
--- a/drivers/gpu/drm/msm/dp/dp_hdcp2p2.c
+++ b/drivers/gpu/drm/msm/dp/dp_hdcp2p2.c
@@ -27,6 +27,7 @@
 #define DP_INTR_STATUS3				(0x00000028)
 #define dp_read(offset) readl_relaxed((offset))
 #define dp_write(offset, data) writel_relaxed((data), (offset))
+#define DP_HDCP_RXCAPS_LENGTH 3
 
 enum dp_hdcp2p2_sink_status {
 	SINK_DISCONNECTED,
@@ -893,21 +894,22 @@
 static bool dp_hdcp2p2_supported(struct dp_hdcp2p2_ctrl *ctrl)
 {
 	u32 const rxcaps_dpcd_offset = 0x6921d;
-	ssize_t const bytes_to_read = 1;
 	ssize_t bytes_read = 0;
-	u8 buf = 0;
+	u8 buf[DP_HDCP_RXCAPS_LENGTH];
 
 	bytes_read = drm_dp_dpcd_read(ctrl->init_data.drm_aux,
-			rxcaps_dpcd_offset, &buf, bytes_to_read);
-	if (bytes_read != bytes_to_read) {
+			rxcaps_dpcd_offset, &buf, DP_HDCP_RXCAPS_LENGTH);
+	if (bytes_read != DP_HDCP_RXCAPS_LENGTH) {
 		pr_err("RxCaps read failed\n");
 		goto error;
 	}
 
-	pr_debug("rxcaps 0x%x\n", buf);
+	pr_debug("HDCP_CAPABLE=%lu\n", (buf[2] & BIT(1)) >> 1);
+	pr_debug("VERSION=%d\n", buf[0]);
 
-	if (buf & BIT(1))
+	if ((buf[2] & BIT(1)) && (buf[0] == 0x2))
 		return true;
+
 error:
 	return false;
 }
diff --git a/drivers/gpu/drm/msm/dp/dp_usbpd.c b/drivers/gpu/drm/msm/dp/dp_usbpd.c
index df43267..bd7a66e 100644
--- a/drivers/gpu/drm/msm/dp/dp_usbpd.c
+++ b/drivers/gpu/drm/msm/dp/dp_usbpd.c
@@ -178,7 +178,10 @@
 	u32 config = 0;
 	const u32 ufp_d_config = 0x2, dp_ver = 0x1;
 
-	pin_cfg = pd->cap.dlink_pin_config;
+	if (pd->cap.receptacle_state)
+		pin_cfg = pd->cap.ulink_pin_config;
+	else
+		pin_cfg = pd->cap.dlink_pin_config;
 
 	for (pin = DP_USBPD_PIN_A; pin < DP_USBPD_PIN_MAX; pin++) {
 		if (pin_cfg & BIT(pin)) {
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_cmn.c b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_cmn.c
index 3fec296..cd21413 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_cmn.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_cmn.c
@@ -354,8 +354,6 @@
 		reg_ctrl |= (reg << offset);
 		reg_ctrl2 &= ~(0xFFFF << offset);
 		reg_ctrl2 |= (dsc.bytes_in_slice << offset);
-		DSI_W32(ctrl, DSI_COMMAND_COMPRESSION_MODE_CTRL, reg_ctrl);
-		DSI_W32(ctrl, DSI_COMMAND_COMPRESSION_MODE_CTRL2, reg_ctrl2);
 
 		pr_debug("ctrl %d reg_ctrl 0x%x reg_ctrl2 0x%x\n", ctrl->index,
 				reg_ctrl, reg_ctrl2);
@@ -373,6 +371,9 @@
 	stream_ctrl |= (vc_id & 0x3) << 8;
 	stream_ctrl |= 0x39; /* packet data type */
 
+	DSI_W32(ctrl, DSI_COMMAND_COMPRESSION_MODE_CTRL, reg_ctrl);
+	DSI_W32(ctrl, DSI_COMMAND_COMPRESSION_MODE_CTRL2, reg_ctrl2);
+
 	DSI_W32(ctrl, DSI_COMMAND_MODE_MDP_STREAM0_CTRL, stream_ctrl);
 	DSI_W32(ctrl, DSI_COMMAND_MODE_MDP_STREAM1_CTRL, stream_ctrl);
 
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
index 4e76aa5..aa81d55 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
@@ -95,10 +95,28 @@
 
 	pr_debug("bl_scale = %u, bl_scale_ad = %u, bl_lvl = %u\n",
 		bl_scale, bl_scale_ad, (u32)bl_temp);
+
+	rc = dsi_display_clk_ctrl(dsi_display->dsi_clk_handle,
+			DSI_CORE_CLK, DSI_CLK_ON);
+	if (rc) {
+		pr_err("[%s] failed to enable DSI core clocks, rc=%d\n",
+		       dsi_display->name, rc);
+		goto error;
+	}
+
 	rc = dsi_panel_set_backlight(panel, (u32)bl_temp);
 	if (rc)
 		pr_err("unable to set backlight\n");
 
+	rc = dsi_display_clk_ctrl(dsi_display->dsi_clk_handle,
+			DSI_CORE_CLK, DSI_CLK_OFF);
+	if (rc) {
+		pr_err("[%s] failed to disable DSI core clocks, rc=%d\n",
+		       dsi_display->name, rc);
+		goto error;
+	}
+
+error:
 	return rc;
 }
 
diff --git a/drivers/gpu/drm/msm/msm_atomic.c b/drivers/gpu/drm/msm/msm_atomic.c
index 0c1bcf6..e0617ee 100644
--- a/drivers/gpu/drm/msm/msm_atomic.c
+++ b/drivers/gpu/drm/msm/msm_atomic.c
@@ -476,6 +476,10 @@
 	struct drm_crtc *crtc = NULL;
 	struct drm_crtc_state *crtc_state = NULL;
 	int ret = -EINVAL, i = 0, j = 0;
+	bool nonblock;
+
+	/* cache since work will kfree commit in non-blocking case */
+	nonblock = commit->nonblock;
 
 	for_each_crtc_in_state(state, crtc, crtc_state, i) {
 		for (j = 0; j < priv->num_crtcs; j++) {
@@ -515,10 +519,13 @@
 		 */
 		DRM_ERROR("failed to dispatch commit to any CRTC\n");
 		complete_commit(commit);
-	} else if (!commit->nonblock) {
+	} else if (!nonblock) {
 		kthread_flush_work(&commit->commit_work);
-		kfree(commit);
 	}
+
+	/* free nonblocking commits in this context, after processing */
+	if (!nonblock)
+		kfree(commit);
 }
 
 /**
diff --git a/drivers/gpu/drm/msm/sde/sde_color_processing.c b/drivers/gpu/drm/msm/sde/sde_color_processing.c
index 9409066..faef4ce 100644
--- a/drivers/gpu/drm/msm/sde/sde_color_processing.c
+++ b/drivers/gpu/drm/msm/sde/sde_color_processing.c
@@ -1445,7 +1445,6 @@
 		goto exit;
 	}
 
-	INIT_LIST_HEAD(&ad_irq->list);
 	ad_irq->arg = crtc;
 	ad_irq->func = sde_cp_ad_interrupt_cb;
 	ret = sde_core_irq_register_callback(kms, irq_idx, ad_irq);
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.c b/drivers/gpu/drm/msm/sde/sde_connector.c
index 9d70525..69ee2be 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.c
+++ b/drivers/gpu/drm/msm/sde/sde_connector.c
@@ -12,6 +12,7 @@
 
 #define pr_fmt(fmt)	"[drm:%s:%d] " fmt, __func__, __LINE__
 #include "msm_drv.h"
+#include "sde_dbg.h"
 
 #include "sde_kms.h"
 #include "sde_connector.h"
@@ -107,7 +108,6 @@
 static int sde_backlight_setup(struct sde_connector *c_conn,
 					struct drm_device *dev)
 {
-	struct backlight_device *bl_device;
 	struct backlight_properties props;
 	struct dsi_display *display;
 	struct dsi_backlight_config *bl_config;
@@ -131,11 +131,12 @@
 	props.brightness = bl_config->brightness_max_level;
 	snprintf(bl_node_name, BL_NODE_NAME_SIZE, "panel%u-backlight",
 							display_count);
-	bl_device = backlight_device_register(bl_node_name, dev->dev,
+	c_conn->bl_device = backlight_device_register(bl_node_name, dev->dev,
 			c_conn, &sde_backlight_device_ops, &props);
-	if (IS_ERR_OR_NULL(bl_device)) {
+	if (IS_ERR_OR_NULL(c_conn->bl_device)) {
 		SDE_ERROR("Failed to register backlight: %ld\n",
-				    PTR_ERR(bl_device));
+				    PTR_ERR(c_conn->bl_device));
+		c_conn->bl_device = NULL;
 		return -ENODEV;
 	}
 	display_count++;
@@ -502,6 +503,8 @@
 		drm_property_unreference_blob(c_conn->blob_dither);
 	msm_property_destroy(&c_conn->property_info);
 
+	if (c_conn->bl_device)
+		backlight_device_unregister(c_conn->bl_device);
 	drm_connector_unregister(connector);
 	mutex_destroy(&c_conn->lock);
 	sde_fence_deinit(&c_conn->retire_fence);
@@ -1197,6 +1200,8 @@
 		return ERR_PTR(-ENOMEM);
 	}
 
+	memset(&display_info, 0, sizeof(display_info));
+
 	rc = drm_connector_init(dev,
 			&c_conn->base,
 			&sde_connector_ops,
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.h b/drivers/gpu/drm/msm/sde/sde_connector.h
index b44bfae..ceeafc2 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.h
+++ b/drivers/gpu/drm/msm/sde/sde_connector.h
@@ -247,6 +247,7 @@
  * @fb_kmap: true if kernel mapping of framebuffer is requested
  * @event_table: Array of registered events
  * @event_lock: Lock object for event_table
+ * @bl_device: backlight device node
  */
 struct sde_connector {
 	struct drm_connector base;
@@ -277,6 +278,8 @@
 	bool fb_kmap;
 	struct sde_connector_evt event_table[SDE_CONN_EVENT_COUNT];
 	spinlock_t event_lock;
+
+	struct backlight_device *bl_device;
 };
 
 /**
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c
index cac7893..387f08d 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.c
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.c
@@ -3046,6 +3046,9 @@
 	struct drm_encoder *encoder;
 	struct sde_crtc_mixer *m;
 	u32 i, misr_status;
+	unsigned long flags;
+	struct sde_crtc_irq_info *node = NULL;
+	int ret = 0;
 
 	if (!crtc) {
 		SDE_ERROR("invalid crtc\n");
@@ -3066,6 +3069,18 @@
 
 			sde_encoder_virt_restore(encoder);
 		}
+
+		spin_lock_irqsave(&sde_crtc->spin_lock, flags);
+		list_for_each_entry(node, &sde_crtc->user_event_list, list) {
+			ret = 0;
+			if (node->func)
+				ret = node->func(crtc, true, &node->irq);
+			if (ret)
+				SDE_ERROR("%s failed to enable event %x\n",
+						sde_crtc->name, node->event);
+		}
+		spin_unlock_irqrestore(&sde_crtc->spin_lock, flags);
+
 		sde_cp_crtc_post_ipc(crtc);
 
 		for (i = 0; i < sde_crtc->num_mixers; ++i) {
@@ -3089,6 +3104,19 @@
 			sde_crtc->misr_data[i] = misr_status ? misr_status :
 							sde_crtc->misr_data[i];
 		}
+
+		spin_lock_irqsave(&sde_crtc->spin_lock, flags);
+		node = NULL;
+		list_for_each_entry(node, &sde_crtc->user_event_list, list) {
+			ret = 0;
+			if (node->func)
+				ret = node->func(crtc, false, &node->irq);
+			if (ret)
+				SDE_ERROR("%s failed to disable event %x\n",
+						sde_crtc->name, node->event);
+		}
+		spin_unlock_irqrestore(&sde_crtc->spin_lock, flags);
+
 		sde_cp_crtc_pre_ipc(crtc);
 		break;
 	case SDE_POWER_EVENT_POST_DISABLE:
@@ -4705,6 +4733,7 @@
 	if (crtc_drm->enabled) {
 		sde_power_resource_enable(&priv->phandle, kms->core_client,
 				true);
+		INIT_LIST_HEAD(&node->irq.list);
 		ret = node->func(crtc_drm, true, &node->irq);
 		sde_power_resource_enable(&priv->phandle, kms->core_client,
 				false);
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c
index 8a46d66..3e1ed7b 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.c
@@ -1237,6 +1237,102 @@
 	}
 }
 
+static int _sde_encoder_dsc_disable(struct sde_encoder_virt *sde_enc)
+{
+	enum sde_rm_topology_name topology;
+	struct drm_connector *drm_conn;
+	int i, ret = 0;
+	struct sde_hw_pingpong *hw_pp[MAX_CHANNELS_PER_ENC];
+	struct sde_hw_dsc *hw_dsc[MAX_CHANNELS_PER_ENC] = {NULL};
+	int pp_count = 0;
+	int dsc_count = 0;
+
+	if (!sde_enc || !sde_enc->phys_encs[0] ||
+			!sde_enc->phys_encs[0]->connector) {
+		SDE_ERROR("invalid params %d %d\n",
+			!sde_enc, sde_enc ? !sde_enc->phys_encs[0] : -1);
+		return -EINVAL;
+	}
+
+	drm_conn = sde_enc->phys_encs[0]->connector;
+
+	topology = sde_connector_get_topology_name(drm_conn);
+	if (topology == SDE_RM_TOPOLOGY_NONE) {
+		SDE_ERROR_ENC(sde_enc, "topology not set yet\n");
+		return -EINVAL;
+	}
+
+	switch (topology) {
+	case SDE_RM_TOPOLOGY_SINGLEPIPE:
+	case SDE_RM_TOPOLOGY_SINGLEPIPE_DSC:
+		/* single PP */
+		hw_pp[0] = sde_enc->hw_pp[0];
+		hw_dsc[0] = sde_enc->hw_dsc[0];
+		pp_count = 1;
+		dsc_count = 1;
+		break;
+	case SDE_RM_TOPOLOGY_DUALPIPE_DSC:
+	case SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE_DSC:
+	case SDE_RM_TOPOLOGY_DUALPIPE_DSCMERGE:
+		/* dual dsc */
+		for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) {
+			hw_dsc[i] = sde_enc->hw_dsc[i];
+			if (hw_dsc[i])
+				dsc_count++;
+		}
+		/* fall through */
+	case SDE_RM_TOPOLOGY_DUALPIPE:
+	case SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE:
+		/* dual pp */
+		for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) {
+			hw_pp[i] = sde_enc->hw_pp[i];
+			if (hw_pp[i])
+				pp_count++;
+		}
+		break;
+	default:
+		SDE_ERROR_ENC(sde_enc, "Unexpected topology:%d\n", topology);
+		return -EINVAL;
+	};
+
+	SDE_EVT32(DRMID(&sde_enc->base), topology, pp_count, dsc_count);
+
+	if (pp_count > MAX_CHANNELS_PER_ENC ||
+		dsc_count > MAX_CHANNELS_PER_ENC) {
+		SDE_ERROR_ENC(sde_enc, "Wrong count pp:%d dsc:%d top:%d\n",
+				pp_count, dsc_count, topology);
+		return -EINVAL;
+	}
+
+	/* Disable DSC for all the pp's present in this topology */
+	for (i = 0; i < pp_count; i++) {
+
+		if (!hw_pp[i]) {
+			SDE_ERROR_ENC(sde_enc, "null pp:%d top:%d cnt:%d\n",
+					i, topology, pp_count);
+			return -EINVAL;
+		}
+
+		if (hw_pp[i]->ops.disable_dsc)
+			hw_pp[i]->ops.disable_dsc(hw_pp[i]);
+	}
+
+	/* Disable DSC HW */
+	for (i = 0; i < dsc_count; i++) {
+
+		if (!hw_dsc[i]) {
+			SDE_ERROR_ENC(sde_enc, "null dsc:%d top:%d cnt:%d\n",
+					i, topology, dsc_count);
+			return -EINVAL;
+		}
+
+		if (hw_dsc[i]->ops.dsc_disable)
+			hw_dsc[i]->ops.dsc_disable(hw_dsc[i]);
+	}
+
+	return ret;
+}
+
 static int _sde_encoder_update_rsc_client(
 		struct drm_encoder *drm_enc,
 		struct sde_encoder_rsc_config *config, bool enable)
@@ -1876,6 +1972,12 @@
 					ret);
 			return;
 		}
+
+		/*
+		 * Disable dsc before switch the mode and after pre_modeset,
+		 * to guarantee that previous kickoff finished.
+		 */
+		_sde_encoder_dsc_disable(sde_enc);
 	}
 
 	/* Reserve dynamic resources now. Indicating non-AtomicTest phase */
@@ -2107,6 +2209,13 @@
 			phys->ops.disable(phys);
 	}
 
+	/*
+	 * disable dsc after the transfer is complete (for command mode)
+	 * and after physical encoder is disabled, to make sure timing
+	 * engine is already disabled (for video mode).
+	 */
+	_sde_encoder_dsc_disable(sde_enc);
+
 	/* after phys waits for frame-done, should be no more frames pending */
 	if (atomic_xchg(&sde_enc->frame_done_timeout, 0)) {
 		SDE_ERROR("enc%d timeout pending\n", drm_enc->base.id);
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
index 61e761d..b80ed1f 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
@@ -157,15 +157,6 @@
 	u32 needed_vfp_lines = worst_case_needed_lines - start_of_frame_lines;
 	u32 actual_vfp_lines = 0;
 
-	if (worst_case_needed_lines < start_of_frame_lines) {
-		needed_vfp_lines = 0;
-		SDE_ERROR("invalid params - needed_lines:%d, frame_lines:%d\n",
-				worst_case_needed_lines, start_of_frame_lines);
-	} else {
-		needed_vfp_lines = worst_case_needed_lines
-					- start_of_frame_lines;
-	}
-
 	/* Fetch must be outside active lines, otherwise undefined. */
 	if (start_of_frame_lines >= worst_case_needed_lines) {
 		SDE_DEBUG_VIDENC(vid_enc,
@@ -464,10 +455,26 @@
 	return false;
 }
 
+static bool _sde_encoder_phys_is_dual_ctl(struct sde_encoder_phys *phys_enc)
+{
+	enum sde_rm_topology_name topology;
+
+	if (!phys_enc)
+		return false;
+
+	topology = sde_connector_get_topology_name(phys_enc->connector);
+	if ((topology == SDE_RM_TOPOLOGY_DUALPIPE_DSC) ||
+		(topology == SDE_RM_TOPOLOGY_DUALPIPE))
+		return true;
+
+	return false;
+}
+
 static bool sde_encoder_phys_vid_needs_single_flush(
 		struct sde_encoder_phys *phys_enc)
 {
-	return phys_enc && _sde_encoder_phys_is_ppsplit(phys_enc);
+	return phys_enc && (_sde_encoder_phys_is_ppsplit(phys_enc) ||
+		_sde_encoder_phys_is_dual_ctl(phys_enc));
 }
 
 static void _sde_encoder_phys_vid_setup_irq_hw_idx(
@@ -614,10 +621,11 @@
 	sde_encoder_phys_vid_setup_timing_engine(phys_enc);
 
 	/*
-	 * For pp-split, skip setting the flush bit for the slave intf, since
-	 * both intfs use same ctl and HW will only flush the master.
+	 * For single flush cases (dual-ctl or pp-split), skip setting the
+	 * flush bit for the slave intf, since both intfs use same ctl
+	 * and HW will only flush the master.
 	 */
-	if (_sde_encoder_phys_is_ppsplit(phys_enc) &&
+	if (sde_encoder_phys_vid_needs_single_flush(phys_enc) &&
 		!sde_encoder_phys_vid_is_master(phys_enc))
 		goto skip_flush;
 
diff --git a/drivers/gpu/drm/msm/sde/sde_fence.c b/drivers/gpu/drm/msm/sde/sde_fence.c
index ad390af..5732aae6 100644
--- a/drivers/gpu/drm/msm/sde/sde_fence.c
+++ b/drivers/gpu/drm/msm/sde/sde_fence.c
@@ -136,31 +136,12 @@
 
 static void sde_fence_release(struct fence *fence)
 {
-	struct sde_fence *f = to_sde_fence(fence);
-	struct sde_fence *fc, *next;
-	struct sde_fence_context *ctx;
-	bool release_kref = false;
+	struct sde_fence *f;
 
-	if (!fence || !f->ctx)
-		return;
-
-	ctx = f->ctx;
-
-	spin_lock(&ctx->list_lock);
-	list_for_each_entry_safe(fc, next, &ctx->fence_list_head, fence_list) {
-		/* fence release called before signal */
-		if (f == fc) {
-			list_del_init(&fc->fence_list);
-			release_kref = true;
-			break;
-		}
+	if (fence) {
+		f = to_sde_fence(fence);
+		kfree(f);
 	}
-	spin_unlock(&ctx->list_lock);
-
-	/* keep kput outside spin_lock because it may release ctx */
-	if (release_kref)
-		kref_put(&ctx->kref, sde_fence_destroy);
-	kfree(f);
 }
 
 static void sde_fence_value_str(struct fence *fence, char *str, int size)
@@ -324,6 +305,7 @@
 		spin_unlock_irqrestore(&ctx->lock, flags);
 
 		if (is_signaled) {
+			fence_put(&fc->base);
 			kref_put(&ctx->kref, sde_fence_destroy);
 		} else {
 			spin_lock(&ctx->list_lock);
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
index 0e1ab51..ccc4443 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
@@ -2944,33 +2944,25 @@
 
 	rc = sde_hardware_format_caps(sde_cfg, hw_rev);
 
-	switch (hw_rev) {
-	case SDE_HW_VER_170:
-	case SDE_HW_VER_171:
-	case SDE_HW_VER_172:
+	if (IS_MSM8996_TARGET(hw_rev)) {
 		/* update msm8996 target here */
 		sde_cfg->perf.min_prefill_lines = 21;
-		break;
-	case SDE_HW_VER_300:
-	case SDE_HW_VER_301:
+	} else if (IS_MSM8998_TARGET(hw_rev)) {
 		/* update msm8998 target here */
 		sde_cfg->has_wb_ubwc = true;
 		sde_cfg->perf.min_prefill_lines = 25;
 		sde_cfg->vbif_qos_nlvl = 4;
 		sde_cfg->ts_prefill_rev = 1;
-		sde_cfg->perf.min_prefill_lines = 25;
-		break;
-	case SDE_HW_VER_400:
+	} else if (IS_SDM845_TARGET(hw_rev)) {
 		/* update sdm845 target here */
 		sde_cfg->has_wb_ubwc = true;
 		sde_cfg->perf.min_prefill_lines = 24;
 		sde_cfg->vbif_qos_nlvl = 8;
 		sde_cfg->ts_prefill_rev = 2;
-		sde_cfg->perf.min_prefill_lines = 24;
-		break;
-	default:
+	} else {
+		SDE_ERROR("unsupported chipset id:%X\n", hw_rev);
 		sde_cfg->perf.min_prefill_lines = 0xffff;
-		break;
+		rc = -ENODEV;
 	}
 
 	return rc;
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
index fa10a88..48c3db7 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
@@ -45,7 +45,10 @@
 #define SDE_HW_VER_300	SDE_HW_VER(3, 0, 0) /* 8998 v1.0 */
 #define SDE_HW_VER_301	SDE_HW_VER(3, 0, 1) /* 8998 v1.1 */
 #define SDE_HW_VER_400	SDE_HW_VER(4, 0, 0) /* sdm845 v1.0 */
+#define SDE_HW_VER_401	SDE_HW_VER(4, 0, 1) /* sdm845 v2.0 */
 
+#define IS_MSM8996_TARGET(rev) IS_SDE_MAJOR_MINOR_SAME((rev), SDE_HW_VER_170)
+#define IS_MSM8998_TARGET(rev) IS_SDE_MAJOR_MINOR_SAME((rev), SDE_HW_VER_300)
 #define IS_SDM845_TARGET(rev) IS_SDE_MAJOR_MINOR_SAME((rev), SDE_HW_VER_400)
 
 #define SDE_HW_BLK_NAME_LEN	16
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_ctl.c b/drivers/gpu/drm/msm/sde/sde_hw_ctl.c
index 6b1f33d..621a172 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_ctl.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_ctl.c
@@ -39,7 +39,7 @@
 #define CTL_FLUSH_MASK_ROT              BIT(27)
 #define CTL_FLUSH_MASK_CTL              BIT(17)
 
-#define SDE_REG_RESET_TIMEOUT_COUNT    20
+#define SDE_REG_RESET_TIMEOUT_US        2000
 
 static struct sde_ctl_cfg *_ctl_offset(enum sde_ctl ctl,
 		struct sde_mdss_cfg *m,
@@ -298,14 +298,13 @@
 	return 0;
 }
 
-static u32 sde_hw_ctl_poll_reset_status(struct sde_hw_ctl *ctx, u32 count)
+static u32 sde_hw_ctl_poll_reset_status(struct sde_hw_ctl *ctx, u32 timeout_us)
 {
 	struct sde_hw_blk_reg_map *c = &ctx->hw;
+	ktime_t timeout;
 	u32 status;
 
-	/* protect to do at least one iteration */
-	if (!count)
-		count = 1;
+	timeout = ktime_add_us(ktime_get(), timeout_us);
 
 	/*
 	 * it takes around 30us to have mdp finish resetting its ctl path
@@ -313,10 +312,10 @@
 	 */
 	do {
 		status = SDE_REG_READ(c, CTL_SW_RESET);
-		status &= 0x01;
+		status &= 0x1;
 		if (status)
 			usleep_range(20, 50);
-	} while (status && --count > 0);
+	} while (status && ktime_compare_safe(ktime_get(), timeout) < 0);
 
 	return status;
 }
@@ -327,7 +326,7 @@
 
 	pr_debug("issuing hw ctl reset for ctl:%d\n", ctx->idx);
 	SDE_REG_WRITE(c, CTL_SW_RESET, 0x1);
-	if (sde_hw_ctl_poll_reset_status(ctx, SDE_REG_RESET_TIMEOUT_COUNT))
+	if (sde_hw_ctl_poll_reset_status(ctx, SDE_REG_RESET_TIMEOUT_US))
 		return -EINVAL;
 
 	return 0;
@@ -344,7 +343,7 @@
 		return 0;
 
 	pr_debug("hw ctl reset is set for ctl:%d\n", ctx->idx);
-	if (sde_hw_ctl_poll_reset_status(ctx, SDE_REG_RESET_TIMEOUT_COUNT)) {
+	if (sde_hw_ctl_poll_reset_status(ctx, SDE_REG_RESET_TIMEOUT_US)) {
 		pr_err("hw recovery is not complete for ctl:%d\n", ctx->idx);
 		return -EINVAL;
 	}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_pingpong.c b/drivers/gpu/drm/msm/sde/sde_hw_pingpong.c
index ff796f7..d65e8d0 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_pingpong.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_pingpong.c
@@ -165,11 +165,16 @@
 static void sde_hw_pp_dsc_disable(struct sde_hw_pingpong *pp)
 {
 	struct sde_hw_blk_reg_map *c;
+	u32 data;
 
 	if (!pp)
 		return;
 	c = &pp->hw;
 
+	data = SDE_REG_READ(c, PP_DCE_DATA_OUT_SWAP);
+	data &= ~BIT(18); /* disable endian flip */
+	SDE_REG_WRITE(c, PP_DCE_DATA_OUT_SWAP, data);
+
 	SDE_REG_WRITE(c, PP_DSC_MODE, 0);
 }
 
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.c b/drivers/gpu/drm/msm/sde/sde_plane.c
index 40dad76..a121b2e 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.c
+++ b/drivers/gpu/drm/msm/sde/sde_plane.c
@@ -1469,6 +1469,12 @@
 		if (psde->pipe_hw->ops.setup_pe)
 			psde->pipe_hw->ops.setup_pe(psde->pipe_hw,
 					&pstate->pixel_ext);
+
+		if (psde->pipe_hw->ops.setup_scaler &&
+				pstate->multirect_index != SDE_SSPP_RECT_1)
+			psde->pipe_hw->ops.setup_scaler(psde->pipe_hw,
+					&psde->pipe_cfg, &pstate->pixel_ext,
+					&pstate->scaler3_cfg);
 	}
 
 	return 0;
diff --git a/drivers/gpu/drm/msm/sde_dbg.c b/drivers/gpu/drm/msm/sde_dbg.c
index 7e58c2f..768dfbd 100644
--- a/drivers/gpu/drm/msm/sde_dbg.c
+++ b/drivers/gpu/drm/msm/sde_dbg.c
@@ -3067,9 +3067,7 @@
 	memset(&dbg->dbgbus_sde, 0, sizeof(dbg->dbgbus_sde));
 	memset(&dbg->dbgbus_vbif_rt, 0, sizeof(dbg->dbgbus_vbif_rt));
 
-	switch (hwversion) {
-	case SDE_HW_VER_300:
-	case SDE_HW_VER_301:
+	if (IS_MSM8998_TARGET(hwversion)) {
 		dbg->dbgbus_sde.entries = dbg_bus_sde_8998;
 		dbg->dbgbus_sde.cmn.entries_size = ARRAY_SIZE(dbg_bus_sde_8998);
 		dbg->dbgbus_sde.cmn.flags = DBGBUS_FLAGS_DSPP;
@@ -3077,9 +3075,7 @@
 		dbg->dbgbus_vbif_rt.entries = vbif_dbg_bus_msm8998;
 		dbg->dbgbus_vbif_rt.cmn.entries_size =
 				ARRAY_SIZE(vbif_dbg_bus_msm8998);
-		break;
-
-	case SDE_HW_VER_400:
+	} else if (IS_SDM845_TARGET(hwversion)) {
 		dbg->dbgbus_sde.entries = dbg_bus_sde_sdm845;
 		dbg->dbgbus_sde.cmn.entries_size =
 				ARRAY_SIZE(dbg_bus_sde_sdm845);
@@ -3089,10 +3085,8 @@
 		dbg->dbgbus_vbif_rt.entries = vbif_dbg_bus_msm8998;
 		dbg->dbgbus_vbif_rt.cmn.entries_size =
 				ARRAY_SIZE(vbif_dbg_bus_msm8998);
-		break;
-	default:
-		pr_err("unsupported chipset id %u\n", hwversion);
-		break;
+	} else {
+		pr_err("unsupported chipset id %X\n", hwversion);
 	}
 }
 
diff --git a/drivers/gpu/msm/adreno_a6xx.c b/drivers/gpu/msm/adreno_a6xx.c
index 12824b0..b9df4ec 100644
--- a/drivers/gpu/msm/adreno_a6xx.c
+++ b/drivers/gpu/msm/adreno_a6xx.c
@@ -1943,9 +1943,7 @@
 	/* If SPTP_RAC is on, turn off SPTP_RAC HS */
 	a6xx_sptprac_disable(adreno_dev);
 
-	/* Disconnect GPU from BUS. Clear and reconnected after reset */
-	adreno_vbif_clear_pending_transactions(device);
-	/* Unnecessary: a6xx_soft_reset(adreno_dev); */
+	/* Disconnect GPU from BUS is not needed if CX GDSC goes off later */
 
 	/* Check no outstanding RPMh voting */
 	a6xx_complete_rpmh_votes(device);
diff --git a/drivers/gpu/msm/adreno_snapshot.c b/drivers/gpu/msm/adreno_snapshot.c
index 0840aba..f608927 100644
--- a/drivers/gpu/msm/adreno_snapshot.c
+++ b/drivers/gpu/msm/adreno_snapshot.c
@@ -837,7 +837,8 @@
 
 	snapshot_frozen_objsize = 0;
 
-	setup_fault_process(device, snapshot,
+	if (!IS_ERR(context))
+		setup_fault_process(device, snapshot,
 			context ? context->proc_priv : NULL);
 
 	/* Add GPU specific sections - registers mainly, but other stuff too */
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index 9968d8c..7dff251 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -3471,7 +3471,7 @@
 	struct sparse_bind_object *new;
 	struct rb_node **node, *parent = NULL;
 
-	new = kzalloc(sizeof(*new), GFP_KERNEL);
+	new = kzalloc(sizeof(*new), GFP_ATOMIC);
 	if (new == NULL)
 		return -ENOMEM;
 
@@ -3505,7 +3505,6 @@
 		struct sparse_bind_object *obj,
 		uint64_t v_offset, uint64_t size)
 {
-	spin_lock(&entry->bind_lock);
 	if (v_offset == obj->v_off && size >= obj->size) {
 		/*
 		 * We are all encompassing, remove the entry and free
@@ -3538,7 +3537,6 @@
 
 		obj->size = v_offset - obj->v_off;
 
-		spin_unlock(&entry->bind_lock);
 		ret = _sparse_add_to_bind_tree(entry, v_offset + size,
 				obj->p_memdesc,
 				obj->p_off + (v_offset - obj->v_off) + size,
@@ -3548,11 +3546,10 @@
 		return ret;
 	}
 
-	spin_unlock(&entry->bind_lock);
-
 	return 0;
 }
 
+/* entry->bind_lock must be held by the caller */
 static struct sparse_bind_object *_find_containing_bind_obj(
 		struct kgsl_mem_entry *entry,
 		uint64_t offset, uint64_t size)
@@ -3560,8 +3557,6 @@
 	struct sparse_bind_object *obj = NULL;
 	struct rb_node *node = entry->bind_tree.rb_node;
 
-	spin_lock(&entry->bind_lock);
-
 	while (node != NULL) {
 		obj = rb_entry(node, struct sparse_bind_object, node);
 
@@ -3580,33 +3575,16 @@
 		}
 	}
 
-	spin_unlock(&entry->bind_lock);
-
 	return obj;
 }
 
+/* entry->bind_lock must be held by the caller */
 static int _sparse_unbind(struct kgsl_mem_entry *entry,
 		struct sparse_bind_object *bind_obj,
 		uint64_t offset, uint64_t size)
 {
-	struct kgsl_memdesc *memdesc = bind_obj->p_memdesc;
-	struct kgsl_pagetable *pt = memdesc->pagetable;
 	int ret;
 
-	if (memdesc->cur_bindings < (size / PAGE_SIZE))
-		return -EINVAL;
-
-	memdesc->cur_bindings -= size / PAGE_SIZE;
-
-	ret = kgsl_mmu_unmap_offset(pt, memdesc,
-			entry->memdesc.gpuaddr, offset, size);
-	if (ret)
-		return ret;
-
-	ret = kgsl_mmu_sparse_dummy_map(pt, &entry->memdesc, offset, size);
-	if (ret)
-		return ret;
-
 	ret = _sparse_rm_from_bind_tree(entry, bind_obj, offset, size);
 	if (ret == 0) {
 		atomic_long_sub(size, &kgsl_driver.stats.mapped);
@@ -3620,6 +3598,8 @@
 	struct kgsl_mem_entry *virt_entry)
 {
 	struct sparse_bind_object *bind_obj;
+	struct kgsl_memdesc *memdesc;
+	struct kgsl_pagetable *pt;
 	int ret = 0;
 	uint64_t size = obj->size;
 	uint64_t tmp_size = obj->size;
@@ -3627,9 +3607,14 @@
 
 	while (size > 0 && ret == 0) {
 		tmp_size = size;
+
+		spin_lock(&virt_entry->bind_lock);
 		bind_obj = _find_containing_bind_obj(virt_entry, offset, size);
-		if (bind_obj == NULL)
+
+		if (bind_obj == NULL) {
+			spin_unlock(&virt_entry->bind_lock);
 			return 0;
+		}
 
 		if (bind_obj->v_off > offset) {
 			tmp_size = size - bind_obj->v_off - offset;
@@ -3646,7 +3631,28 @@
 				tmp_size = bind_obj->size;
 		}
 
+		memdesc = bind_obj->p_memdesc;
+		pt = memdesc->pagetable;
+
+		if (memdesc->cur_bindings < (tmp_size / PAGE_SIZE)) {
+			spin_unlock(&virt_entry->bind_lock);
+			return -EINVAL;
+		}
+
+		memdesc->cur_bindings -= tmp_size / PAGE_SIZE;
+
 		ret = _sparse_unbind(virt_entry, bind_obj, offset, tmp_size);
+		spin_unlock(&virt_entry->bind_lock);
+
+		ret = kgsl_mmu_unmap_offset(pt, memdesc,
+				virt_entry->memdesc.gpuaddr, offset, tmp_size);
+		if (ret)
+			return ret;
+
+		ret = kgsl_mmu_sparse_dummy_map(pt, memdesc, offset, tmp_size);
+		if (ret)
+			return ret;
+
 		if (ret == 0) {
 			offset += tmp_size;
 			size -= tmp_size;
diff --git a/drivers/gpu/msm/kgsl_gmu.c b/drivers/gpu/msm/kgsl_gmu.c
index 2ddb082..f21a179 100644
--- a/drivers/gpu/msm/kgsl_gmu.c
+++ b/drivers/gpu/msm/kgsl_gmu.c
@@ -456,11 +456,12 @@
 			GMU_DCVS_NOHFI, perf_idx, bw_idx);
 
 		if (ret) {
-			dev_err(&gmu->pdev->dev,
+			dev_err_ratelimited(&gmu->pdev->dev,
 				"Failed to set GPU perf idx %d, bw idx %d\n",
 				perf_idx, bw_idx);
 
-			gmu_snapshot(device);
+			adreno_set_gpu_fault(adreno_dev, ADRENO_GMU_FAULT);
+			adreno_dispatcher_schedule(device);
 		}
 
 		return ret;
@@ -1341,6 +1342,7 @@
 
 	gmu_disable_clks(gmu);
 	gmu_disable_gdsc(gmu);
+	dev_err(&gmu->pdev->dev, "Suspended GMU\n");
 	return 0;
 }
 
@@ -1349,7 +1351,7 @@
 	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
 	struct gmu_device *gmu = &device->gmu;
 
-	if (!test_and_set_bit(GMU_FAULT, &gmu->flags)) {
+	if (!gmu->fault_count) {
 		/* Mask so there's no interrupt caused by NMI */
 		adreno_write_gmureg(adreno_dev,
 				ADRENO_REG_GMU_GMU2HOST_INTR_MASK, 0xFFFFFFFF);
@@ -1364,7 +1366,7 @@
 		/* Wait for the NMI to be handled */
 		wmb();
 		udelay(100);
-		kgsl_device_snapshot(device, NULL);
+		kgsl_device_snapshot(device, ERR_PTR(-EINVAL));
 
 		adreno_write_gmureg(adreno_dev,
 				ADRENO_REG_GMU_GMU2HOST_INTR_CLR, 0xFFFFFFFF);
@@ -1372,6 +1374,8 @@
 				ADRENO_REG_GMU_GMU2HOST_INTR_MASK,
 				(unsigned int) ~HFI_IRQ_MASK);
 	}
+
+	gmu->fault_count++;
 }
 
 /* To be called to power on both GPU and GMU */
@@ -1389,32 +1393,29 @@
 		WARN_ON(test_bit(GMU_CLK_ON, &gmu->flags));
 		gmu_enable_gdsc(gmu);
 		gmu_enable_clks(gmu);
+		gmu_irq_enable(device);
 
 		/* Vote for 300MHz DDR for GMU to init */
 		ret = msm_bus_scale_client_update_request(gmu->pcl,
 				pwr->pwrlevels[pwr->default_pwrlevel].bus_freq);
-		if (ret) {
+		if (ret)
 			dev_err(&gmu->pdev->dev,
-					"Failed to allocate gmu b/w\n");
-			goto error_clks;
-		}
+				"Failed to allocate gmu b/w: %d\n", ret);
 
 		ret = gpudev->rpmh_gpu_pwrctrl(adreno_dev, GMU_FW_START,
 				GMU_COLD_BOOT, 0);
 		if (ret)
-			goto error_bus;
-
-		gmu_irq_enable(device);
+			goto error_gmu;
 
 		ret = hfi_start(gmu, GMU_COLD_BOOT);
 		if (ret)
-			goto error_gpu;
+			goto error_gmu;
 
 		/* Send default DCVS level */
 		ret = gmu_dcvs_set(gmu, pwr->default_pwrlevel,
 				pwr->pwrlevels[pwr->default_pwrlevel].bus_freq);
 		if (ret)
-			goto error_gpu;
+			goto error_gmu;
 
 		msm_bus_scale_client_update_request(gmu->pcl, 0);
 		break;
@@ -1423,49 +1424,49 @@
 		WARN_ON(test_bit(GMU_CLK_ON, &gmu->flags));
 		gmu_enable_gdsc(gmu);
 		gmu_enable_clks(gmu);
+		gmu_irq_enable(device);
 
 		ret = gpudev->rpmh_gpu_pwrctrl(adreno_dev, GMU_FW_START,
 				GMU_WARM_BOOT, 0);
 		if (ret)
-			goto error_clks;
-
-		gmu_irq_enable(device);
+			goto error_gmu;
 
 		ret = hfi_start(gmu, GMU_WARM_BOOT);
 		if (ret)
-			goto error_gpu;
+			goto error_gmu;
 
 		ret = gmu_dcvs_set(gmu, gmu->wakeup_pwrlevel,
 				pwr->pwrlevels[gmu->wakeup_pwrlevel].bus_freq);
 		if (ret)
-			goto error_gpu;
+			goto error_gmu;
 
 		gmu->wakeup_pwrlevel = pwr->default_pwrlevel;
 		break;
 
 	case KGSL_STATE_RESET:
-		if (test_bit(ADRENO_DEVICE_HARD_RESET, &adreno_dev->priv)) {
+		if (test_bit(ADRENO_DEVICE_HARD_RESET, &adreno_dev->priv) ||
+			test_bit(GMU_FAULT, &gmu->flags)) {
 			gmu_suspend(device);
 			gmu_enable_gdsc(gmu);
 			gmu_enable_clks(gmu);
+			gmu_irq_enable(device);
 
 			ret = gpudev->rpmh_gpu_pwrctrl(
 				adreno_dev, GMU_FW_START, GMU_RESET, 0);
 			if (ret)
-				goto error_clks;
+				goto error_gmu;
 
-			gmu_irq_enable(device);
 
 			ret = hfi_start(gmu, GMU_COLD_BOOT);
 			if (ret)
-				goto error_gpu;
+				goto error_gmu;
 
 			/* Send DCVS level prior to reset*/
 			ret = gmu_dcvs_set(gmu, pwr->active_pwrlevel,
 					pwr->pwrlevels[pwr->active_pwrlevel]
 					.bus_freq);
 			if (ret)
-				goto error_gpu;
+				goto error_gmu;
 
 			ret = gpudev->oob_set(adreno_dev,
 				OOB_CPINIT_SET_MASK,
@@ -1485,20 +1486,8 @@
 
 	return ret;
 
-error_gpu:
+error_gmu:
 	gmu_snapshot(device);
-	hfi_stop(gmu);
-	gmu_irq_disable(device);
-	if (ADRENO_QUIRK(adreno_dev, ADRENO_QUIRK_HFI_USE_REG))
-		gpudev->oob_clear(adreno_dev,
-				OOB_BOOT_SLUMBER_CLEAR_MASK);
-	gpudev->rpmh_gpu_pwrctrl(adreno_dev, GMU_FW_STOP, 0, 0);
-error_bus:
-	msm_bus_scale_client_update_request(gmu->pcl, 0);
-error_clks:
-	gmu_snapshot(device);
-	gmu_disable_clks(gmu);
-	gmu_disable_gdsc(gmu);
 	return ret;
 }
 
@@ -1535,18 +1524,30 @@
 	if (!idle || (gpudev->wait_for_gmu_idle &&
 			gpudev->wait_for_gmu_idle(adreno_dev))) {
 		dev_err(&gmu->pdev->dev, "Stopping GMU before it is idle\n");
+		idle = false;
+		set_bit(GMU_FAULT, &gmu->flags);
+	} else {
+		idle = true;
 	}
 
-	/* Pending message in all queues are abandoned */
-	hfi_stop(gmu);
-	clear_bit(GMU_HFI_ON, &gmu->flags);
-	gmu_irq_disable(device);
+	if (idle) {
+		/* Pending message in all queues are abandoned */
+		hfi_stop(gmu);
+		clear_bit(GMU_HFI_ON, &gmu->flags);
+		gmu_irq_disable(device);
 
-	gpudev->rpmh_gpu_pwrctrl(adreno_dev, GMU_FW_STOP, 0, 0);
-	gmu_disable_clks(gmu);
-	gmu_disable_gdsc(gmu);
-
-	/* TODO: Vote CX, MX retention off */
+		gpudev->rpmh_gpu_pwrctrl(adreno_dev, GMU_FW_STOP, 0, 0);
+		gmu_disable_clks(gmu);
+		gmu_disable_gdsc(gmu);
+	} else {
+		/*
+		 * The power controller will change state to SLUMBER anyway
+		 * Set GMU_FAULT flag to indicate to power contrller
+		 * that hang recovery is needed to power on GPU
+		 */
+		set_bit(GMU_FAULT, &gmu->flags);
+		gmu_snapshot(device);
+	}
 
 	msm_bus_scale_client_update_request(gmu->pcl, 0);
 }
diff --git a/drivers/gpu/msm/kgsl_gmu.h b/drivers/gpu/msm/kgsl_gmu.h
index 63ca028..ff65f66 100644
--- a/drivers/gpu/msm/kgsl_gmu.h
+++ b/drivers/gpu/msm/kgsl_gmu.h
@@ -193,6 +193,7 @@
  * @pcl: GPU BW scaling client
  * @ccl: CNOC BW scaling client
  * @idle_level: Minimal GPU idle power level
+ * @fault_count: GMU fault count
  */
 struct gmu_device {
 	unsigned int ver;
@@ -226,6 +227,7 @@
 	unsigned int pcl;
 	unsigned int ccl;
 	unsigned int idle_level;
+	unsigned int fault_count;
 };
 
 bool kgsl_gmu_isenabled(struct kgsl_device *device);
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c
index 6e55395..007121c 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.c
+++ b/drivers/gpu/msm/kgsl_pwrctrl.c
@@ -2629,6 +2629,7 @@
 _aware(struct kgsl_device *device)
 {
 	int status = 0;
+	struct gmu_device *gmu = &device->gmu;
 
 	switch (device->state) {
 	case KGSL_STATE_RESET:
@@ -2648,15 +2649,42 @@
 		kgsl_pwrscale_midframe_timer_cancel(device);
 		break;
 	case KGSL_STATE_SLUMBER:
+		/* if GMU already in FAULT */
+		if (kgsl_gmu_isenabled(device) &&
+			test_bit(GMU_FAULT, &gmu->flags)) {
+			status = -EINVAL;
+			break;
+		}
+
 		status = kgsl_pwrctrl_enable(device);
 		break;
 	default:
 		status = -EINVAL;
 	}
-	if (status)
+
+	if (status) {
+		if (kgsl_gmu_isenabled(device)) {
+			/* GMU hang recovery */
+			kgsl_pwrctrl_set_state(device, KGSL_STATE_RESET);
+			set_bit(GMU_FAULT, &gmu->flags);
+			status = kgsl_pwrctrl_enable(device);
+			if (status) {
+				/*
+				 * Cannot recover GMU failure
+				 * GPU will not be powered on
+				 */
+				WARN_ONCE(1, "Failed to recover GMU\n");
+			}
+
+			clear_bit(GMU_FAULT, &gmu->flags);
+			kgsl_pwrctrl_set_state(device, KGSL_STATE_AWARE);
+			return status;
+		}
+
 		kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE);
-	else
+	} else {
 		kgsl_pwrctrl_set_state(device, KGSL_STATE_AWARE);
+	}
 	return status;
 }
 
diff --git a/drivers/gpu/msm/kgsl_snapshot.c b/drivers/gpu/msm/kgsl_snapshot.c
index 7cbda72..e704db7 100644
--- a/drivers/gpu/msm/kgsl_snapshot.c
+++ b/drivers/gpu/msm/kgsl_snapshot.c
@@ -182,7 +182,8 @@
 	context = kgsl_context_get(device, header->current_context);
 
 	/* Get the current PT base */
-	 header->ptbase = kgsl_mmu_get_current_ttbr0(&device->mmu);
+	if (!IS_ERR(priv))
+		header->ptbase = kgsl_mmu_get_current_ttbr0(&device->mmu);
 
 	/* And the PID for the task leader */
 	if (context) {
@@ -633,8 +634,10 @@
 	snapshot->size += sizeof(*header);
 
 	/* Build the Linux specific header */
+	/* Context err is implied a GMU fault, so limit dump */
 	kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_OS,
-			snapshot, snapshot_os, NULL);
+			snapshot, snapshot_os,
+			IS_ERR(context) ? context : NULL);
 
 	/* Get the device specific sections */
 	if (device->ftbl->snapshot)
diff --git a/drivers/iommu/iova.c b/drivers/iommu/iova.c
index e23001b..5c88ba7 100644
--- a/drivers/iommu/iova.c
+++ b/drivers/iommu/iova.c
@@ -56,13 +56,13 @@
 static struct rb_node *
 __get_cached_rbnode(struct iova_domain *iovad, unsigned long *limit_pfn)
 {
-	if ((*limit_pfn != iovad->dma_32bit_pfn) ||
+	if ((*limit_pfn > iovad->dma_32bit_pfn) ||
 		(iovad->cached32_node == NULL))
 		return rb_last(&iovad->rbroot);
 	else {
 		struct rb_node *prev_node = rb_prev(iovad->cached32_node);
 		struct iova *curr_iova =
-			container_of(iovad->cached32_node, struct iova, node);
+			rb_entry(iovad->cached32_node, struct iova, node);
 		*limit_pfn = curr_iova->pfn_lo - 1;
 		return prev_node;
 	}
@@ -86,11 +86,11 @@
 	if (!iovad->cached32_node)
 		return;
 	curr = iovad->cached32_node;
-	cached_iova = container_of(curr, struct iova, node);
+	cached_iova = rb_entry(curr, struct iova, node);
 
 	if (free->pfn_lo >= cached_iova->pfn_lo) {
 		struct rb_node *node = rb_next(&free->node);
-		struct iova *iova = container_of(node, struct iova, node);
+		struct iova *iova = rb_entry(node, struct iova, node);
 
 		/* only cache if it's below 32bit pfn */
 		if (node && iova->pfn_lo < iovad->dma_32bit_pfn)
@@ -100,6 +100,34 @@
 	}
 }
 
+/* Insert the iova into domain rbtree by holding writer lock */
+static void
+iova_insert_rbtree(struct rb_root *root, struct iova *iova,
+		   struct rb_node *start)
+{
+	struct rb_node **new, *parent = NULL;
+
+	new = (start) ? &start : &(root->rb_node);
+	/* Figure out where to put new node */
+	while (*new) {
+		struct iova *this = rb_entry(*new, struct iova, node);
+
+		parent = *new;
+
+		if (iova->pfn_lo < this->pfn_lo)
+			new = &((*new)->rb_left);
+		else if (iova->pfn_lo > this->pfn_lo)
+			new = &((*new)->rb_right);
+		else {
+			WARN_ON(1); /* this should not happen */
+			return;
+		}
+	}
+	/* Add new node and rebalance tree. */
+	rb_link_node(&iova->node, parent, new);
+	rb_insert_color(&iova->node, root);
+}
+
 /*
  * Computes the padding size required, to make the start address
  * naturally aligned on the power-of-two order of its size
@@ -125,7 +153,7 @@
 	curr = __get_cached_rbnode(iovad, &limit_pfn);
 	prev = curr;
 	while (curr) {
-		struct iova *curr_iova = container_of(curr, struct iova, node);
+		struct iova *curr_iova = rb_entry(curr, struct iova, node);
 
 		if (limit_pfn < curr_iova->pfn_lo)
 			goto move_left;
@@ -138,7 +166,7 @@
 				break;	/* found a free slot */
 		}
 adjust_limit_pfn:
-		limit_pfn = curr_iova->pfn_lo - 1;
+		limit_pfn = curr_iova->pfn_lo ? (curr_iova->pfn_lo - 1) : 0;
 move_left:
 		prev = curr;
 		curr = rb_prev(curr);
@@ -157,36 +185,8 @@
 	new->pfn_lo = limit_pfn - (size + pad_size) + 1;
 	new->pfn_hi = new->pfn_lo + size - 1;
 
-	/* Insert the new_iova into domain rbtree by holding writer lock */
-	/* Add new node and rebalance tree. */
-	{
-		struct rb_node **entry, *parent = NULL;
-
-		/* If we have 'prev', it's a valid place to start the
-		   insertion. Otherwise, start from the root. */
-		if (prev)
-			entry = &prev;
-		else
-			entry = &iovad->rbroot.rb_node;
-
-		/* Figure out where to put new node */
-		while (*entry) {
-			struct iova *this = container_of(*entry,
-							struct iova, node);
-			parent = *entry;
-
-			if (new->pfn_lo < this->pfn_lo)
-				entry = &((*entry)->rb_left);
-			else if (new->pfn_lo > this->pfn_lo)
-				entry = &((*entry)->rb_right);
-			else
-				BUG(); /* this should not happen */
-		}
-
-		/* Add new node and rebalance tree. */
-		rb_link_node(&new->node, parent, entry);
-		rb_insert_color(&new->node, &iovad->rbroot);
-	}
+	/* If we have 'prev', it's a valid place to start the insertion. */
+	iova_insert_rbtree(&iovad->rbroot, new, prev);
 	__cached_rbnode_insert_update(iovad, saved_pfn, new);
 
 	spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags);
@@ -195,28 +195,6 @@
 	return 0;
 }
 
-static void
-iova_insert_rbtree(struct rb_root *root, struct iova *iova)
-{
-	struct rb_node **new = &(root->rb_node), *parent = NULL;
-	/* Figure out where to put new node */
-	while (*new) {
-		struct iova *this = container_of(*new, struct iova, node);
-
-		parent = *new;
-
-		if (iova->pfn_lo < this->pfn_lo)
-			new = &((*new)->rb_left);
-		else if (iova->pfn_lo > this->pfn_lo)
-			new = &((*new)->rb_right);
-		else
-			BUG(); /* this should not happen */
-	}
-	/* Add new node and rebalance tree. */
-	rb_link_node(&iova->node, parent, new);
-	rb_insert_color(&iova->node, root);
-}
-
 static struct kmem_cache *iova_cache;
 static unsigned int iova_cache_users;
 static DEFINE_MUTEX(iova_cache_mutex);
@@ -311,7 +289,7 @@
 	assert_spin_locked(&iovad->iova_rbtree_lock);
 
 	while (node) {
-		struct iova *iova = container_of(node, struct iova, node);
+		struct iova *iova = rb_entry(node, struct iova, node);
 
 		/* If pfn falls within iova's range, return iova */
 		if ((pfn >= iova->pfn_lo) && (pfn <= iova->pfn_hi)) {
@@ -463,7 +441,7 @@
 	spin_lock_irqsave(&iovad->iova_rbtree_lock, flags);
 	node = rb_first(&iovad->rbroot);
 	while (node) {
-		struct iova *iova = container_of(node, struct iova, node);
+		struct iova *iova = rb_entry(node, struct iova, node);
 
 		rb_erase(node, &iovad->rbroot);
 		free_iova_mem(iova);
@@ -477,7 +455,7 @@
 __is_range_overlap(struct rb_node *node,
 	unsigned long pfn_lo, unsigned long pfn_hi)
 {
-	struct iova *iova = container_of(node, struct iova, node);
+	struct iova *iova = rb_entry(node, struct iova, node);
 
 	if ((pfn_lo <= iova->pfn_hi) && (pfn_hi >= iova->pfn_lo))
 		return 1;
@@ -506,7 +484,7 @@
 
 	iova = alloc_and_init_iova(pfn_lo, pfn_hi);
 	if (iova)
-		iova_insert_rbtree(&iovad->rbroot, iova);
+		iova_insert_rbtree(&iovad->rbroot, iova, NULL);
 
 	return iova;
 }
@@ -541,7 +519,7 @@
 	spin_lock_irqsave(&iovad->iova_rbtree_lock, flags);
 	for (node = rb_first(&iovad->rbroot); node; node = rb_next(node)) {
 		if (__is_range_overlap(node, pfn_lo, pfn_hi)) {
-			iova = container_of(node, struct iova, node);
+			iova = rb_entry(node, struct iova, node);
 			__adjust_overlap_range(iova, &pfn_lo, &pfn_hi);
 			if ((pfn_lo >= iova->pfn_lo) &&
 				(pfn_hi <= iova->pfn_hi))
@@ -578,7 +556,7 @@
 
 	spin_lock_irqsave(&from->iova_rbtree_lock, flags);
 	for (node = rb_first(&from->rbroot); node; node = rb_next(node)) {
-		struct iova *iova = container_of(node, struct iova, node);
+		struct iova *iova = rb_entry(node, struct iova, node);
 		struct iova *new_iova;
 
 		new_iova = reserve_iova(to, iova->pfn_lo, iova->pfn_hi);
@@ -613,11 +591,11 @@
 	rb_erase(&iova->node, &iovad->rbroot);
 
 	if (prev) {
-		iova_insert_rbtree(&iovad->rbroot, prev);
+		iova_insert_rbtree(&iovad->rbroot, prev, NULL);
 		iova->pfn_lo = pfn_lo;
 	}
 	if (next) {
-		iova_insert_rbtree(&iovad->rbroot, next);
+		iova_insert_rbtree(&iovad->rbroot, next, NULL);
 		iova->pfn_hi = pfn_hi;
 	}
 	spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags);
diff --git a/drivers/leds/leds-qpnp-wled.c b/drivers/leds/leds-qpnp-wled.c
index 568c9f9..5c9c75c 100644
--- a/drivers/leds/leds-qpnp-wled.c
+++ b/drivers/leds/leds-qpnp-wled.c
@@ -123,6 +123,7 @@
 #define QPNP_WLED_ILIM_FAULT_BIT	BIT(0)
 #define QPNP_WLED_OVP_FAULT_BIT		BIT(1)
 #define QPNP_WLED_SC_FAULT_BIT		BIT(2)
+#define QPNP_WLED_OVP_FLT_RT_STS_BIT	BIT(1)
 
 /* sink registers */
 #define QPNP_WLED_CURR_SINK_REG(b)	(b + 0x46)
@@ -1103,14 +1104,6 @@
 	int rc = 0, i;
 	u8 reg = 0, sink_config = 0, sink_test = 0, sink_valid = 0, int_sts;
 
-	mutex_lock(&wled->lock);
-
-	/* disable OVP IRQ */
-	if (wled->ovp_irq > 0 && !wled->ovp_irq_disabled) {
-		disable_irq_nosync(wled->ovp_irq);
-		wled->ovp_irq_disabled = true;
-	}
-
 	/* read configured sink configuration */
 	rc = qpnp_wled_read_reg(wled,
 		QPNP_WLED_CURR_SINK_REG(wled->sink_base), &sink_config);
@@ -1259,7 +1252,8 @@
 	}
 
 	/* restore  brightness */
-	rc = qpnp_wled_set_level(wled, wled->cdev.brightness);
+	rc = qpnp_wled_set_level(wled, !wled->cdev.brightness ?
+			AUTO_CALIB_BRIGHTNESS : wled->cdev.brightness);
 	if (rc < 0) {
 		pr_err("Failed to set brightness after calibration rc=%d\n",
 						rc);
@@ -1280,11 +1274,6 @@
 			QPNP_WLED_SOFT_START_DLY_US + 1000);
 
 failed_calib:
-	if (wled->ovp_irq > 0 && wled->ovp_irq_disabled) {
-		enable_irq(wled->ovp_irq);
-		wled->ovp_irq_disabled = false;
-	}
-	mutex_unlock(&wled->lock);
 	return rc;
 }
 
@@ -1320,6 +1309,38 @@
 	return false;
 }
 
+static int qpnp_wled_auto_calibrate_at_init(struct qpnp_wled *wled)
+{
+	int rc;
+	u8 fault_status = 0, rt_status = 0;
+
+	if (!wled->auto_calib_enabled)
+		return 0;
+
+	rc = qpnp_wled_read_reg(wled,
+			QPNP_WLED_INT_RT_STS(wled->ctrl_base), &rt_status);
+	if (rc < 0)
+		pr_err("Failed to read RT status rc=%d\n", rc);
+
+	rc = qpnp_wled_read_reg(wled,
+			QPNP_WLED_FAULT_STATUS(wled->ctrl_base), &fault_status);
+	if (rc < 0)
+		pr_err("Failed to read fault status rc=%d\n", rc);
+
+	if ((rt_status & QPNP_WLED_OVP_FLT_RT_STS_BIT) ||
+			(fault_status & QPNP_WLED_OVP_FAULT_BIT)) {
+		mutex_lock(&wled->lock);
+		rc = wled_auto_calibrate(wled);
+		if (rc < 0)
+			pr_err("Failed auto-calibration rc=%d\n", rc);
+		else
+			wled->auto_calib_done = true;
+		mutex_unlock(&wled->lock);
+	}
+
+	return rc;
+}
+
 /* ovp irq handler */
 static irqreturn_t qpnp_wled_ovp_irq_handler(int irq, void *_wled)
 {
@@ -1348,13 +1369,26 @@
 	if (fault_sts & QPNP_WLED_OVP_FAULT_BIT) {
 		if (wled->auto_calib_enabled && !wled->auto_calib_done) {
 			if (qpnp_wled_auto_cal_required(wled)) {
-				rc = wled_auto_calibrate(wled);
-				if (rc < 0) {
-					pr_err("Failed auto-calibration rc=%d\n",
-							rc);
-					return IRQ_HANDLED;
+				mutex_lock(&wled->lock);
+				if (wled->ovp_irq > 0 &&
+						!wled->ovp_irq_disabled) {
+					disable_irq_nosync(wled->ovp_irq);
+					wled->ovp_irq_disabled = true;
 				}
-				wled->auto_calib_done = true;
+
+				rc = wled_auto_calibrate(wled);
+				if (rc < 0)
+					pr_err("Failed auto-calibration rc=%d\n",
+								rc);
+				else
+					wled->auto_calib_done = true;
+
+				if (wled->ovp_irq > 0 &&
+						wled->ovp_irq_disabled) {
+					enable_irq(wled->ovp_irq);
+					wled->ovp_irq_disabled = false;
+				}
+				mutex_unlock(&wled->lock);
 			}
 		}
 	}
@@ -1946,6 +1980,10 @@
 		return rc;
 	}
 
+	rc = qpnp_wled_auto_calibrate_at_init(wled);
+	if (rc < 0)
+		pr_err("Failed to auto-calibrate at init rc=%d\n", rc);
+
 	/* setup ovp and sc irqs */
 	if (wled->ovp_irq >= 0) {
 		rc = devm_request_threaded_irq(&wled->pdev->dev, wled->ovp_irq,
diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c
index ecb77cc..9f340bf 100644
--- a/drivers/mailbox/mailbox.c
+++ b/drivers/mailbox/mailbox.c
@@ -296,8 +296,9 @@
 EXPORT_SYMBOL_GPL(mbox_send_message);
 
 /**
- * mbox_send_controller_data-	For client to submit a message to be
- *				sent only to the controller.
+ * mbox_write_controller_data -	For client to submit a message to be
+ *				written to the controller but not sent to
+ *				the remote processor.
  * @chan: Mailbox channel assigned to this client.
  * @mssg: Client specific message typecasted.
  *
@@ -308,7 +309,7 @@
  *	or transmission over chan (blocking mode).
  *	Negative value denotes failure.
  */
-int mbox_send_controller_data(struct mbox_chan *chan, void *mssg)
+int mbox_write_controller_data(struct mbox_chan *chan, void *mssg)
 {
 	unsigned long flags;
 	int err;
@@ -317,12 +318,12 @@
 		return -EINVAL;
 
 	spin_lock_irqsave(&chan->lock, flags);
-	err = chan->mbox->ops->send_controller_data(chan, mssg);
+	err = chan->mbox->ops->write_controller_data(chan, mssg);
 	spin_unlock_irqrestore(&chan->lock, flags);
 
 	return err;
 }
-EXPORT_SYMBOL(mbox_send_controller_data);
+EXPORT_SYMBOL(mbox_write_controller_data);
 
 bool mbox_controller_is_idle(struct mbox_chan *chan)
 {
diff --git a/drivers/mailbox/qcom-rpmh-mailbox.c b/drivers/mailbox/qcom-rpmh-mailbox.c
index a69fe85..7bf8a18 100644
--- a/drivers/mailbox/qcom-rpmh-mailbox.c
+++ b/drivers/mailbox/qcom-rpmh-mailbox.c
@@ -1075,7 +1075,7 @@
 
 static const struct mbox_chan_ops mbox_ops = {
 	.send_data = chan_tcs_write,
-	.send_controller_data = chan_tcs_ctrl_write,
+	.write_controller_data = chan_tcs_ctrl_write,
 	.startup = chan_init,
 	.shutdown = chan_shutdown,
 };
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 2f2a439..1a9e84d 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
@@ -1081,8 +1081,10 @@
 	int i;
 
 	if (ctx->rot->mode == ROT_REGDMA_ON) {
-		SDE_ROTREG_WRITE(rot->mdss_base, REGDMA_CSR_REGDMA_INT_EN,
-				REGDMA_INT_MASK);
+		if (rot->irq_num >= 0)
+			SDE_ROTREG_WRITE(rot->mdss_base,
+					REGDMA_CSR_REGDMA_INT_EN,
+					REGDMA_INT_MASK);
 		SDE_ROTREG_WRITE(rot->mdss_base, REGDMA_CSR_REGDMA_OP_MODE,
 				REGDMA_EN);
 	}
diff --git a/drivers/media/platform/msm/vidc/msm_vdec.c b/drivers/media/platform/msm/vidc/msm_vdec.c
index 56e7a99..cb03576 100644
--- a/drivers/media/platform/msm/vidc/msm_vdec.c
+++ b/drivers/media/platform/msm/vidc/msm_vdec.c
@@ -581,6 +581,13 @@
 
 		inst->prop.width[CAPTURE_PORT] = f->fmt.pix_mp.width;
 		inst->prop.height[CAPTURE_PORT] = f->fmt.pix_mp.height;
+		rc = msm_vidc_check_session_supported(inst);
+		if (rc) {
+			dprintk(VIDC_ERR,
+				"%s: session not supported\n", __func__);
+			goto err_invalid_fmt;
+		}
+
 		msm_comm_set_color_format(inst,
 				msm_comm_get_hal_output_buffer(inst),
 				f->fmt.pix_mp.pixelformat);
@@ -648,6 +655,12 @@
 		}
 		inst->prop.width[OUTPUT_PORT] = f->fmt.pix_mp.width;
 		inst->prop.height[OUTPUT_PORT] = f->fmt.pix_mp.height;
+		rc = msm_vidc_check_session_supported(inst);
+		if (rc) {
+			dprintk(VIDC_ERR,
+				"%s: session not supported\n", __func__);
+			goto err_invalid_fmt;
+		}
 
 		frame_sz.buffer_type = HAL_BUFFER_INPUT;
 		frame_sz.width = inst->prop.width[OUTPUT_PORT];
diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c
index dfb2ad5..0d8cb76 100644
--- a/drivers/media/platform/msm/vidc/msm_venc.c
+++ b/drivers/media/platform/msm/vidc/msm_venc.c
@@ -2385,6 +2385,12 @@
 
 		inst->prop.width[CAPTURE_PORT] = f->fmt.pix_mp.width;
 		inst->prop.height[CAPTURE_PORT] = f->fmt.pix_mp.height;
+		rc = msm_vidc_check_session_supported(inst);
+		if (rc) {
+			dprintk(VIDC_ERR,
+				"%s: session not supported\n", __func__);
+			goto exit;
+		}
 
 		frame_sz.buffer_type = HAL_BUFFER_OUTPUT;
 		frame_sz.width = inst->prop.width[CAPTURE_PORT];
@@ -2443,6 +2449,12 @@
 
 		inst->prop.width[OUTPUT_PORT] = f->fmt.pix_mp.width;
 		inst->prop.height[OUTPUT_PORT] = f->fmt.pix_mp.height;
+		rc = msm_vidc_check_session_supported(inst);
+		if (rc) {
+			dprintk(VIDC_ERR,
+				"%s: session not supported\n", __func__);
+			goto exit;
+		}
 
 		frame_sz.buffer_type = HAL_BUFFER_INPUT;
 		frame_sz.width = inst->prop.width[OUTPUT_PORT];
diff --git a/drivers/media/platform/msm/vidc/msm_vidc.c b/drivers/media/platform/msm/vidc/msm_vidc.c
index 6a7588c..5149086 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc.c
@@ -883,6 +883,13 @@
 		goto fail_start;
 	}
 
+	rc = msm_vidc_check_scaling_supported(inst);
+	if (rc) {
+		dprintk(VIDC_ERR,
+			"This session scaling is not supported %pK\n", inst);
+		goto fail_start;
+	}
+
 	/* Decide work mode for current session */
 	rc = msm_vidc_decide_work_mode(inst);
 	if (rc) {
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c
index 428bf71..6e29c53 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c
@@ -2161,6 +2161,7 @@
 				"Got SYS_ERR but unable to identify core\n");
 		return;
 	}
+	hdev = core->device;
 
 	mutex_lock(&core->lock);
 	if (core->state == VIDC_CORE_UNINIT) {
@@ -2181,7 +2182,6 @@
 		msm_vidc_queue_v4l2_event(inst, V4L2_EVENT_MSM_VIDC_SYS_ERROR);
 		msm_comm_print_inst_info(inst);
 	}
-	hdev = core->device;
 	dprintk(VIDC_DBG, "Calling core_release\n");
 	rc = call_hfi_op(hdev, core_release, hdev->hfi_device_data);
 	if (rc) {
@@ -3011,6 +3011,23 @@
 	return msm_vidc_deinit_core(inst);
 }
 
+static int msm_comm_session_init_done(int flipped_state,
+	struct msm_vidc_inst *inst)
+{
+	int rc;
+
+	dprintk(VIDC_DBG, "inst %pK: waiting for session init done\n", inst);
+	rc = wait_for_state(inst, flipped_state, MSM_VIDC_OPEN_DONE,
+			HAL_SESSION_INIT_DONE);
+	if (rc) {
+		dprintk(VIDC_ERR, "Session init failed for inst %pK\n", inst);
+		msm_comm_generate_sys_error(inst);
+		return rc;
+	}
+
+	return rc;
+}
+
 static int msm_comm_session_init(int flipped_state,
 	struct msm_vidc_inst *inst)
 {
@@ -3693,8 +3710,7 @@
 		if (rc || state <= get_flipped_state(inst->state, state))
 			break;
 	case MSM_VIDC_OPEN_DONE:
-		rc = wait_for_state(inst, flipped_state, MSM_VIDC_OPEN_DONE,
-			HAL_SESSION_INIT_DONE);
+		rc = msm_comm_session_init_done(flipped_state, inst);
 		if (rc || state <= get_flipped_state(inst->state, state))
 			break;
 	case MSM_VIDC_LOAD_RESOURCES:
@@ -5472,10 +5488,6 @@
 			capability->width.max, capability->height.max);
 			rc = -ENOTSUPP;
 		}
-
-		if (!rc && msm_vidc_check_scaling_supported(inst)) {
-			rc = -ENOTSUPP;
-		}
 	}
 	if (rc) {
 		dprintk(VIDC_ERR,
diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c
index c94da61..dde6029 100644
--- a/drivers/media/platform/msm/vidc/venus_hfi.c
+++ b/drivers/media/platform/msm/vidc/venus_hfi.c
@@ -1047,9 +1047,9 @@
 
 	mutex_lock(&device->lock);
 
-	if (device->power_enabled) {
-		dprintk(VIDC_DBG, "Venus is busy\n");
-		rc = -EBUSY;
+	if (!device->power_enabled) {
+		dprintk(VIDC_WARN, "%s: venus power off\n", __func__);
+		rc = -EINVAL;
 		goto exit;
 	}
 	__flush_debug_queue(device, NULL);
diff --git a/drivers/nfc/nq-nci.c b/drivers/nfc/nq-nci.c
index baa4f94..ea4bedf 100644
--- a/drivers/nfc/nq-nci.c
+++ b/drivers/nfc/nq-nci.c
@@ -354,6 +354,7 @@
 				usleep_range(1000, 1100);
 			}
 			gpio_set_value(nqx_dev->ese_gpio, 1);
+			usleep_range(1000, 1100);
 			if (gpio_get_value(nqx_dev->ese_gpio)) {
 				dev_dbg(&nqx_dev->client->dev, "ese_gpio is enabled\n");
 				r = 0;
@@ -406,6 +407,7 @@
 			 * there's no need to send the i2c commands
 			 */
 			gpio_set_value(nqx_dev->ese_gpio, 0);
+			usleep_range(1000, 1100);
 		}
 
 		if (!gpio_get_value(nqx_dev->ese_gpio)) {
diff --git a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c
index f06fb1f..2ecbd22 100644
--- a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c
+++ b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2014, 2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2014, 2016-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
@@ -471,6 +471,7 @@
 
 	pad = pctldev->desc->pins[pin].drv_data;
 
+	pad->is_enabled = true;
 	for (i = 0; i < nconfs; i++) {
 		param = pinconf_to_config_param(configs[i]);
 		arg = pinconf_to_config_argument(configs[i]);
@@ -619,6 +620,10 @@
 			return ret;
 	}
 
+	val = pad->is_enabled << PMIC_GPIO_REG_MASTER_EN_SHIFT;
+
+	ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_EN_CTL, val);
+
 	return ret;
 }
 
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa.c b/drivers/platform/msm/ipa/ipa_v2/ipa.c
index bfd0446..70d74f0 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa.c
@@ -531,7 +531,7 @@
 	kfree(buff);
 }
 
-static int ipa_send_wan_msg(unsigned long usr_param, uint8_t msg_type)
+static int ipa_send_wan_msg(unsigned long usr_param, uint8_t msg_type, bool is_cache)
 {
 	int retval;
 	struct ipa_wan_msg *wan_msg;
@@ -559,6 +559,25 @@
 		return retval;
 	}
 
+	if (is_cache) {
+		mutex_lock(&ipa_ctx->ipa_cne_evt_lock);
+
+		/* cache the cne event */
+		memcpy(&ipa_ctx->ipa_cne_evt_req_cache[
+			ipa_ctx->num_ipa_cne_evt_req].wan_msg,
+			wan_msg,
+			sizeof(struct ipa_wan_msg));
+
+		memcpy(&ipa_ctx->ipa_cne_evt_req_cache[
+			ipa_ctx->num_ipa_cne_evt_req].msg_meta,
+			&msg_meta,
+			sizeof(struct ipa_msg_meta));
+
+		ipa_ctx->num_ipa_cne_evt_req++;
+		ipa_ctx->num_ipa_cne_evt_req %= IPA_MAX_NUM_REQ_CACHE;
+		mutex_unlock(&ipa_ctx->ipa_cne_evt_lock);
+	}
+
 	return 0;
 }
 
@@ -1328,21 +1347,21 @@
 		}
 		break;
 	case IPA_IOC_NOTIFY_WAN_UPSTREAM_ROUTE_ADD:
-		retval = ipa_send_wan_msg(arg, WAN_UPSTREAM_ROUTE_ADD);
+		retval = ipa_send_wan_msg(arg, WAN_UPSTREAM_ROUTE_ADD, true);
 		if (retval) {
 			IPAERR("ipa_send_wan_msg failed: %d\n", retval);
 			break;
 		}
 		break;
 	case IPA_IOC_NOTIFY_WAN_UPSTREAM_ROUTE_DEL:
-		retval = ipa_send_wan_msg(arg, WAN_UPSTREAM_ROUTE_DEL);
+		retval = ipa_send_wan_msg(arg, WAN_UPSTREAM_ROUTE_DEL, true);
 		if (retval) {
 			IPAERR("ipa_send_wan_msg failed: %d\n", retval);
 			break;
 		}
 		break;
 	case IPA_IOC_NOTIFY_WAN_EMBMS_CONNECTED:
-		retval = ipa_send_wan_msg(arg, WAN_EMBMS_CONNECT);
+		retval = ipa_send_wan_msg(arg, WAN_EMBMS_CONNECT, false);
 		if (retval) {
 			IPAERR("ipa_send_wan_msg failed: %d\n", retval);
 			break;
@@ -4169,6 +4188,7 @@
 
 	mutex_init(&ipa_ctx->lock);
 	mutex_init(&ipa_ctx->nat_mem.lock);
+	mutex_init(&ipa_ctx->ipa_cne_evt_lock);
 
 	idr_init(&ipa_ctx->ipa_idr);
 	spin_lock_init(&ipa_ctx->idr_lock);
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h
index 141bff1..9bfdcdc 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h
@@ -63,6 +63,9 @@
 
 #define IPA_MAX_STATUS_STAT_NUM 30
 
+
+#define IPA_MAX_NUM_REQ_CACHE 10
+
 #define IPADBG(fmt, args...) \
 	pr_debug(DRV_NAME " %s:%d " fmt, __func__, __LINE__, ## args)
 #define IPAERR(fmt, args...) \
@@ -968,6 +971,11 @@
 	bool uplink;
 };
 
+struct ipa_cne_evt {
+	struct ipa_wan_msg wan_msg;
+	struct ipa_msg_meta msg_meta;
+};
+
 /**
  * struct ipa_context - IPA context
  * @class: pointer to the struct class
@@ -1164,6 +1172,9 @@
 	u32 ipa_rx_min_timeout_usec;
 	u32 ipa_rx_max_timeout_usec;
 	u32 ipa_polling_iteration;
+	struct ipa_cne_evt ipa_cne_evt_req_cache[IPA_MAX_NUM_REQ_CACHE];
+	int num_ipa_cne_evt_req;
+	struct mutex ipa_cne_evt_lock;
 };
 
 /**
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c
index ede5254..2210ee7 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c
@@ -540,7 +540,7 @@
 	kfree(buff);
 }
 
-static int ipa3_send_wan_msg(unsigned long usr_param, uint8_t msg_type)
+static int ipa3_send_wan_msg(unsigned long usr_param, uint8_t msg_type, bool is_cache)
 {
 	int retval;
 	struct ipa_wan_msg *wan_msg;
@@ -568,6 +568,25 @@
 		return retval;
 	}
 
+	if (is_cache) {
+		mutex_lock(&ipa3_ctx->ipa_cne_evt_lock);
+
+		/* cache the cne event */
+		memcpy(&ipa3_ctx->ipa_cne_evt_req_cache[
+			ipa3_ctx->num_ipa_cne_evt_req].wan_msg,
+			wan_msg,
+			sizeof(struct ipa_wan_msg));
+
+		memcpy(&ipa3_ctx->ipa_cne_evt_req_cache[
+			ipa3_ctx->num_ipa_cne_evt_req].msg_meta,
+			&msg_meta,
+			sizeof(struct ipa_msg_meta));
+
+		ipa3_ctx->num_ipa_cne_evt_req++;
+		ipa3_ctx->num_ipa_cne_evt_req %= IPA_MAX_NUM_REQ_CACHE;
+		mutex_unlock(&ipa3_ctx->ipa_cne_evt_lock);
+	}
+
 	return 0;
 }
 
@@ -1524,21 +1543,21 @@
 		}
 		break;
 	case IPA_IOC_NOTIFY_WAN_UPSTREAM_ROUTE_ADD:
-		retval = ipa3_send_wan_msg(arg, WAN_UPSTREAM_ROUTE_ADD);
+		retval = ipa3_send_wan_msg(arg, WAN_UPSTREAM_ROUTE_ADD, true);
 		if (retval) {
 			IPAERR("ipa3_send_wan_msg failed: %d\n", retval);
 			break;
 		}
 		break;
 	case IPA_IOC_NOTIFY_WAN_UPSTREAM_ROUTE_DEL:
-		retval = ipa3_send_wan_msg(arg, WAN_UPSTREAM_ROUTE_DEL);
+		retval = ipa3_send_wan_msg(arg, WAN_UPSTREAM_ROUTE_DEL, true);
 		if (retval) {
 			IPAERR("ipa3_send_wan_msg failed: %d\n", retval);
 			break;
 		}
 		break;
 	case IPA_IOC_NOTIFY_WAN_EMBMS_CONNECTED:
-		retval = ipa3_send_wan_msg(arg, WAN_EMBMS_CONNECT);
+		retval = ipa3_send_wan_msg(arg, WAN_EMBMS_CONNECT, false);
 		if (retval) {
 			IPAERR("ipa3_send_wan_msg failed: %d\n", retval);
 			break;
@@ -4877,6 +4896,7 @@
 	mutex_init(&ipa3_ctx->lock);
 	mutex_init(&ipa3_ctx->nat_mem.lock);
 	mutex_init(&ipa3_ctx->q6_proxy_clk_vote_mutex);
+	mutex_init(&ipa3_ctx->ipa_cne_evt_lock);
 
 	idr_init(&ipa3_ctx->ipa_idr);
 	spin_lock_init(&ipa3_ctx->idr_lock);
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
index b891606..4d1baea 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
@@ -71,6 +71,8 @@
 
 #define IPA_IPC_LOG_PAGES 50
 
+#define IPA_MAX_NUM_REQ_CACHE 10
+
 #define IPADBG(fmt, args...) \
 	do { \
 		pr_debug(DRV_NAME " %s:%d " fmt, __func__, __LINE__, ## args);\
@@ -1088,6 +1090,11 @@
 	struct ipa_hw_stats_drop drop;
 };
 
+struct ipa_cne_evt {
+	struct ipa_wan_msg wan_msg;
+	struct ipa_msg_meta msg_meta;
+};
+
 /**
  * struct ipa3_context - IPA context
  * @class: pointer to the struct class
@@ -1304,6 +1311,9 @@
 	struct ipa_tz_unlock_reg_info *ipa_tz_unlock_reg;
 	struct ipa_dma_task_info dma_task_info;
 	struct ipa_hw_stats hw_stats;
+	struct ipa_cne_evt ipa_cne_evt_req_cache[IPA_MAX_NUM_REQ_CACHE];
+	int num_ipa_cne_evt_req;
+	struct mutex ipa_cne_evt_lock;
 };
 
 struct ipa3_plat_drv_res {
diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c
index 48e689f..9b5b592 100644
--- a/drivers/power/supply/qcom/smb-lib.c
+++ b/drivers/power/supply/qcom/smb-lib.c
@@ -3358,6 +3358,10 @@
 		vote(chg->awake_votable, PL_DELAY_VOTER, true, 0);
 		schedule_delayed_work(&chg->pl_enable_work,
 					msecs_to_jiffies(PL_DELAY_MS));
+		/* vbus rising when APSD was disabled and PD_ACTIVE = 0 */
+		if (get_effective_result(chg->apsd_disable_votable) &&
+				!chg->pd_active)
+			pr_err("APSD disabled on vbus rising without PD\n");
 	} else {
 		if (chg->wa_flags & BOOST_BACK_WA) {
 			data = chg->irq_info[SWITCH_POWER_OK_IRQ].irq_data;
@@ -3642,6 +3646,37 @@
 	}
 }
 
+static void smblib_notify_extcon_props(struct smb_charger *chg, int id)
+{
+	union extcon_property_value val;
+	union power_supply_propval prop_val;
+
+	smblib_get_prop_typec_cc_orientation(chg, &prop_val);
+	val.intval = ((prop_val.intval == 2) ? 1 : 0);
+	extcon_set_property(chg->extcon, id,
+				EXTCON_PROP_USB_TYPEC_POLARITY, val);
+
+	val.intval = true;
+	extcon_set_property(chg->extcon, id,
+				EXTCON_PROP_USB_SS, val);
+}
+
+static void smblib_notify_device_mode(struct smb_charger *chg, bool enable)
+{
+	if (enable)
+		smblib_notify_extcon_props(chg, EXTCON_USB);
+
+	extcon_set_state_sync(chg->extcon, EXTCON_USB, enable);
+}
+
+static void smblib_notify_usb_host(struct smb_charger *chg, bool enable)
+{
+	if (enable)
+		smblib_notify_extcon_props(chg, EXTCON_USB_HOST);
+
+	extcon_set_state_sync(chg->extcon, EXTCON_USB_HOST, enable);
+}
+
 #define HVDCP_DET_MS 2500
 static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising)
 {
@@ -3664,6 +3699,8 @@
 		/* if not DCP then no hvdcp timeout happens. Enable pd here */
 		vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER,
 				false, 0);
+		if (chg->use_extcon)
+			smblib_notify_device_mode(chg, true);
 		break;
 	case OCP_CHARGER_BIT:
 	case FLOAT_CHARGER_BIT:
@@ -3749,6 +3786,10 @@
 	 */
 	vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER,
 			false, 0);
+	if (chg->use_extcon) {
+		smblib_notify_usb_host(chg, true);
+		chg->otg_present = true;
+	}
 }
 
 static void typec_sink_removal(struct smb_charger *chg)
@@ -3899,6 +3940,14 @@
 
 	typec_sink_removal(chg);
 	smblib_update_usb_type(chg);
+
+	if (chg->use_extcon) {
+		if (chg->otg_present)
+			smblib_notify_usb_host(chg, false);
+		else
+			smblib_notify_device_mode(chg, false);
+	}
+	chg->otg_present = false;
 }
 
 static void smblib_handle_typec_insertion(struct smb_charger *chg)
diff --git a/drivers/power/supply/qcom/smb-lib.h b/drivers/power/supply/qcom/smb-lib.h
index 704a8db..5251b6f 100644
--- a/drivers/power/supply/qcom/smb-lib.h
+++ b/drivers/power/supply/qcom/smb-lib.h
@@ -144,6 +144,9 @@
 	EXTCON_NONE,
 };
 
+/* EXTCON_USB and EXTCON_USB_HOST are mutually exclusive */
+static const u32 smblib_extcon_exclusive[] = {0x3, 0};
+
 struct smb_regulator {
 	struct regulator_dev	*rdev;
 	struct regulator_desc	rdesc;
@@ -331,6 +334,8 @@
 	int			usb_icl_change_irq_enabled;
 	u32			jeita_status;
 	u8			float_cfg;
+	bool			use_extcon;
+	bool			otg_present;
 
 	/* workaround flag */
 	u32			wa_flags;
diff --git a/drivers/power/supply/qcom/smb138x-charger.c b/drivers/power/supply/qcom/smb138x-charger.c
index 911dd69..e0c92c6 100644
--- a/drivers/power/supply/qcom/smb138x-charger.c
+++ b/drivers/power/supply/qcom/smb138x-charger.c
@@ -170,6 +170,9 @@
 	chip->dt.suspend_input = of_property_read_bool(node,
 				"qcom,suspend-input");
 
+	chg->use_extcon = of_property_read_bool(node,
+				"qcom,use-extcon");
+
 	rc = of_property_read_u32(node,
 				"qcom,fcc-max-ua", &chip->dt.fcc_ua);
 	if (rc < 0)
@@ -1405,55 +1408,95 @@
 	rc = smb138x_parse_dt(chip);
 	if (rc < 0) {
 		pr_err("Couldn't parse device tree rc=%d\n", rc);
-		return rc;
+		goto cleanup;
 	}
 
 	rc = smb138x_init_vbus_regulator(chip);
 	if (rc < 0) {
 		pr_err("Couldn't initialize vbus regulator rc=%d\n",
 			rc);
-		return rc;
+		goto cleanup;
 	}
 
 	rc = smb138x_init_vconn_regulator(chip);
 	if (rc < 0) {
 		pr_err("Couldn't initialize vconn regulator rc=%d\n",
 			rc);
-		return rc;
+		goto cleanup;
+	}
+
+	if (chg->use_extcon) {
+		/* extcon registration */
+		chg->extcon = devm_extcon_dev_allocate(chg->dev,
+							smblib_extcon_cable);
+		if (IS_ERR(chg->extcon)) {
+			rc = PTR_ERR(chg->extcon);
+			dev_err(chg->dev, "failed to allocate extcon device rc=%d\n",
+					rc);
+			goto cleanup;
+		}
+
+		chg->extcon->mutually_exclusive = smblib_extcon_exclusive;
+		rc = devm_extcon_dev_register(chg->dev, chg->extcon);
+		if (rc < 0) {
+			dev_err(chg->dev, "failed to register extcon device rc=%d\n",
+						rc);
+			goto cleanup;
+		}
+
+		/* Support reporting polarity and speed via properties */
+		rc = extcon_set_property_capability(chg->extcon,
+				EXTCON_USB, EXTCON_PROP_USB_TYPEC_POLARITY);
+		rc |= extcon_set_property_capability(chg->extcon,
+				EXTCON_USB, EXTCON_PROP_USB_SS);
+		rc |= extcon_set_property_capability(chg->extcon,
+				EXTCON_USB_HOST,
+				EXTCON_PROP_USB_TYPEC_POLARITY);
+		rc |= extcon_set_property_capability(chg->extcon,
+				EXTCON_USB_HOST, EXTCON_PROP_USB_SS);
+		if (rc < 0) {
+			dev_err(chg->dev,
+				"failed to configure extcon capabilities\n");
+			goto cleanup;
+		}
 	}
 
 	rc = smb138x_init_usb_psy(chip);
 	if (rc < 0) {
 		pr_err("Couldn't initialize usb psy rc=%d\n", rc);
-		return rc;
+		goto cleanup;
 	}
 
 	rc = smb138x_init_batt_psy(chip);
 	if (rc < 0) {
 		pr_err("Couldn't initialize batt psy rc=%d\n", rc);
-		return rc;
+		goto cleanup;
 	}
 
 	rc = smb138x_init_hw(chip);
 	if (rc < 0) {
 		pr_err("Couldn't initialize hardware rc=%d\n", rc);
-		return rc;
+		goto cleanup;
 	}
 
 	rc = smb138x_determine_initial_status(chip);
 	if (rc < 0) {
 		pr_err("Couldn't determine initial status rc=%d\n",
 			rc);
-		return rc;
+		goto cleanup;
 	}
 
 	rc = smb138x_request_interrupts(chip);
 	if (rc < 0) {
 		pr_err("Couldn't request interrupts rc=%d\n", rc);
-		return rc;
+		goto cleanup;
 	}
 
 	return rc;
+
+cleanup:
+	smblib_deinit(chg);
+	return rc;
 }
 
 static int smb138x_slave_probe(struct smb138x *chip)
diff --git a/drivers/soc/qcom/gladiator_erp.c b/drivers/soc/qcom/gladiator_erp.c
index 835d4b0..3b70ca4 100644
--- a/drivers/soc/qcom/gladiator_erp.c
+++ b/drivers/soc/qcom/gladiator_erp.c
@@ -163,6 +163,7 @@
 	DISCONNECT_ERROR,
 	DIRECTORY_ERROR,
 	PARITY_ERROR,
+	PHYSICAL_ADDRESS_ERROR,
 };
 
 static void clear_gladiator_error(void __iomem *gladiator_virt_base,
@@ -200,12 +201,14 @@
 
 static inline void print_gld_errtype(unsigned int errtype)
 {
+	char *errors = "Disconnect, Directory, Parity, Physical address";
+
 	if (errtype == 0)
 		pr_alert("Error type: Snoop data transfer\n");
 	else if (errtype == 1)
 		pr_alert("Error type: DVM error\n");
 	else if (errtype == 3)
-		pr_alert("Error type: Disconnect, directory, or parity error\n");
+		pr_alert("Error type: %s\n", errors);
 	else
 		pr_alert("Error type: Unknown; value:%u\n", errtype);
 }
@@ -288,7 +291,7 @@
 
 	log_err_type = (err_reg5 & mask_shifts->gld_errlog5_error_type_mask)
 		>> mask_shifts->gld_errlog5_error_type_shift;
-	for (i = 0 ; i <= 6 ; i++) {
+	for (i = 0 ; i <= 7 ; i++) {
 		value = log_err_type & 0x1;
 		switch (i) {
 		case DATA_TRANSFER_ERROR:
@@ -337,7 +340,14 @@
 					mask_shifts);
 			decode_index_parity(err_reg5, mask_shifts);
 			break;
+		case PHYSICAL_ADDRESS_ERROR:
+			if (value == 0)
+				continue;
+			pr_alert("Error type: Physical address error\n");
+			pr_alert("Address is greater than SoC address range\n");
+			break;
 		}
+
 		log_err_type = log_err_type >> 1;
 	}
 }
diff --git a/drivers/soc/qcom/msm_bus/msm_bus_fabric_rpmh.c b/drivers/soc/qcom/msm_bus/msm_bus_fabric_rpmh.c
index ea01076..707d9e7 100644
--- a/drivers/soc/qcom/msm_bus/msm_bus_fabric_rpmh.c
+++ b/drivers/soc/qcom/msm_bus/msm_bus_fabric_rpmh.c
@@ -563,9 +563,6 @@
 		return ret;
 
 	list_for_each_entry_safe(node, node_tmp, clist, link) {
-		if (unlikely(node->node_info->defer_qos))
-			msm_bus_dev_init_qos(&node->dev, NULL);
-
 		bcm_clist_add(node);
 	}
 
@@ -668,6 +665,12 @@
 	kfree(cmdlist_active);
 	kfree(n_active);
 
+
+	list_for_each_entry_safe(node, node_tmp, clist, link) {
+		if (unlikely(node->node_info->defer_qos))
+			msm_bus_dev_init_qos(&node->dev, NULL);
+	}
+
 exit_msm_bus_commit_data:
 	list_for_each_entry_safe(node, node_tmp, clist, link) {
 		bcm_clist_clean(node);
@@ -962,6 +965,12 @@
 
 	MSM_BUS_DBG("Device = %d", node_dev->node_info->id);
 
+	if (node_dev->node_info->qos_params.defer_init_qos) {
+		node_dev->node_info->qos_params.defer_init_qos = false;
+		node_dev->node_info->defer_qos = true;
+		goto exit_init_qos;
+	}
+
 	if (node_dev->ap_owned) {
 		struct msm_bus_node_device_type *bus_node_info;
 
@@ -1290,6 +1299,8 @@
 				pdata_node_info->qos_params.reg_mode.write;
 	node_info->qos_params.urg_fwd_en =
 				pdata_node_info->qos_params.urg_fwd_en;
+	node_info->qos_params.defer_init_qos =
+				pdata_node_info->qos_params.defer_init_qos;
 	node_info->agg_params.buswidth = pdata_node_info->agg_params.buswidth;
 	node_info->agg_params.agg_scheme =
 					pdata_node_info->agg_params.agg_scheme;
diff --git a/drivers/soc/qcom/msm_bus/msm_bus_of_rpmh.c b/drivers/soc/qcom/msm_bus/msm_bus_of_rpmh.c
index 77cbbf1..32b6adf 100644
--- a/drivers/soc/qcom/msm_bus/msm_bus_of_rpmh.c
+++ b/drivers/soc/qcom/msm_bus/msm_bus_of_rpmh.c
@@ -241,6 +241,8 @@
 	node_info->qos_params.urg_fwd_en = of_property_read_bool(dev_node,
 						"qcom,forwarding");
 
+	node_info->qos_params.defer_init_qos = of_property_read_bool(dev_node,
+						"qcom,defer-init-qos");
 }
 
 static int msm_bus_of_parse_clk_array(struct device_node *dev_node,
diff --git a/drivers/soc/qcom/msm_bus/msm_bus_rpmh.h b/drivers/soc/qcom/msm_bus/msm_bus_rpmh.h
index ad04fef..a9733f1 100644
--- a/drivers/soc/qcom/msm_bus/msm_bus_rpmh.h
+++ b/drivers/soc/qcom/msm_bus/msm_bus_rpmh.h
@@ -136,6 +136,7 @@
 	struct msm_bus_noc_regulator reg;
 	struct msm_bus_noc_regulator_mode reg_mode;
 	bool urg_fwd_en;
+	bool defer_init_qos;
 };
 
 struct node_util_levels_type {
diff --git a/drivers/soc/qcom/pil-msa.c b/drivers/soc/qcom/pil-msa.c
index a3cf11d..f5aff4f 100644
--- a/drivers/soc/qcom/pil-msa.c
+++ b/drivers/soc/qcom/pil-msa.c
@@ -269,7 +269,7 @@
 	return ret;
 }
 
-static int pil_mss_assert_resets(struct q6v5_data *drv)
+int pil_mss_assert_resets(struct q6v5_data *drv)
 {
 	int ret = 0;
 
@@ -280,7 +280,7 @@
 	return ret;
 }
 
-static int pil_mss_deassert_resets(struct q6v5_data *drv)
+int pil_mss_deassert_resets(struct q6v5_data *drv)
 {
 	int ret = 0;
 
diff --git a/drivers/soc/qcom/pil-msa.h b/drivers/soc/qcom/pil-msa.h
index 1789ba3..7a6a3cc 100644
--- a/drivers/soc/qcom/pil-msa.h
+++ b/drivers/soc/qcom/pil-msa.h
@@ -46,4 +46,6 @@
 int pil_mss_shutdown(struct pil_desc *pil);
 int pil_mss_deinit_image(struct pil_desc *pil);
 int __pil_mss_deinit_image(struct pil_desc *pil, bool err_path);
+int pil_mss_assert_resets(struct q6v5_data *drv);
+int pil_mss_deassert_resets(struct q6v5_data *drv);
 #endif
diff --git a/drivers/soc/qcom/pil-q6v5.c b/drivers/soc/qcom/pil-q6v5.c
index 49dd0be..6a30381 100644
--- a/drivers/soc/qcom/pil-q6v5.c
+++ b/drivers/soc/qcom/pil-q6v5.c
@@ -23,6 +23,7 @@
 #include <trace/events/trace_msm_pil_event.h>
 
 #include "peripheral-loader.h"
+#include "pil-msa.h"
 #include "pil-q6v5.h"
 
 /* QDSP6SS Register Offsets */
@@ -86,7 +87,7 @@
 #define QDSP6SS_BOOT_STATUS		(0x408)
 #define QDSP6SS_SLEEP			(0x3C)
 #define SLEEP_CHECK_MAX_LOOPS		(200)
-#define BOOT_FSM_TIMEOUT		(10)
+#define BOOT_FSM_TIMEOUT		(100)
 
 #define QDSP6SS_ACC_OVERRIDE_VAL	0x20
 
@@ -384,7 +385,7 @@
 {
 	struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
 	u32 val, count;
-	unsigned long timeout;
+	int ret;
 
 	val = readl_relaxed(drv->reg_base + QDSP6SS_SLEEP);
 	val |= 0x1;
@@ -409,15 +410,19 @@
 	writel_relaxed(1, drv->reg_base + QDSP6SS_BOOT_CMD);
 
 	/* Wait for boot FSM to complete */
-	timeout = jiffies + usecs_to_jiffies(BOOT_FSM_TIMEOUT);
-	while (time_before(jiffies, timeout)) {
-		val = readl_relaxed(drv->reg_base + QDSP6SS_BOOT_STATUS);
-		if (val & BIT(0))
-			return 0;
+	ret = readl_poll_timeout(drv->reg_base + QDSP6SS_BOOT_STATUS, val,
+			val != 0, 10, BOOT_FSM_TIMEOUT);
+
+	if (ret) {
+		dev_err(drv->desc.dev, "Boot FSM failed to complete.\n");
+		/* Reset the modem so that boot FSM is in reset state */
+		pil_mss_assert_resets(drv);
+		/* Wait 6 32kHz sleep cycles for reset */
+		udelay(200);
+		pil_mss_deassert_resets(drv);
 	}
 
-	dev_err(drv->desc.dev, "Boot FSM failed to complete.\n");
-	return -ETIMEDOUT;
+	return ret;
 }
 
 static int __pil_q6v55_reset(struct pil_desc *pil)
diff --git a/drivers/soc/qcom/rpmh.c b/drivers/soc/qcom/rpmh.c
index f4c35ab..f7902e1 100644
--- a/drivers/soc/qcom/rpmh.c
+++ b/drivers/soc/qcom/rpmh.c
@@ -563,7 +563,7 @@
 	spin_lock_irqsave(&rpm->lock, flags);
 	for (i = 0; rpm->passthru_cache[i]; i++) {
 		rpm_msg = rpm->passthru_cache[i];
-		ret = mbox_send_controller_data(rc->chan, &rpm_msg->msg);
+		ret = mbox_write_controller_data(rc->chan, &rpm_msg->msg);
 		if (ret)
 			goto fail;
 	}
@@ -762,7 +762,7 @@
 	rpm_msg.msg.is_control = true;
 	rpm_msg.msg.is_complete = false;
 
-	return mbox_send_controller_data(rc->chan, &rpm_msg.msg);
+	return mbox_write_controller_data(rc->chan, &rpm_msg.msg);
 }
 EXPORT_SYMBOL(rpmh_write_control);
 
@@ -797,7 +797,7 @@
 	rpm->dirty = true;
 	spin_unlock_irqrestore(&rpm->lock, flags);
 
-	return mbox_send_controller_data(rc->chan, &rpm_msg.msg);
+	return mbox_write_controller_data(rc->chan, &rpm_msg.msg);
 }
 EXPORT_SYMBOL(rpmh_invalidate);
 
@@ -886,7 +886,7 @@
 	rpm_msg.msg.num_payload = 1;
 	rpm_msg.msg.is_complete = false;
 
-	return mbox_send_controller_data(rc->chan, &rpm_msg.msg);
+	return mbox_write_controller_data(rc->chan, &rpm_msg.msg);
 }
 
 /**
diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c
index 6acb731..de3dc69 100644
--- a/drivers/spi/spi-geni-qcom.c
+++ b/drivers/spi/spi-geni-qcom.c
@@ -192,16 +192,26 @@
 static void spi_setup_word_len(struct spi_geni_master *mas, u32 mode,
 						int bits_per_word)
 {
-	int pack_words = 0;
+	int pack_words = 1;
 	bool msb_first = (mode & SPI_LSB_FIRST) ? false : true;
 	u32 word_len = geni_read_reg(mas->base, SE_SPI_WORD_LEN);
+	unsigned long cfg0, cfg1;
 
+	/*
+	 * If bits_per_word isn't a byte aligned value, set the packing to be
+	 * 1 SPI word per FIFO word.
+	 */
 	if (!(mas->tx_fifo_width % bits_per_word))
 		pack_words = mas->tx_fifo_width / bits_per_word;
 	word_len &= ~WORD_LEN_MSK;
 	word_len |= ((bits_per_word - MIN_WORD_LEN) & WORD_LEN_MSK);
 	se_config_packing(mas->base, bits_per_word, pack_words, msb_first);
 	geni_write_reg(word_len, mas->base, SE_SPI_WORD_LEN);
+	se_get_packing_config(bits_per_word, pack_words, msb_first,
+							&cfg0, &cfg1);
+	GENI_SE_DBG(mas->ipc, false, mas->dev,
+		"%s: cfg0 %lu cfg1 %lu bpw %d pack_words %d\n", __func__,
+		cfg0, cfg1, bits_per_word, pack_words);
 }
 
 static int setup_fifo_params(struct spi_device *spi_slv,
@@ -257,6 +267,12 @@
 	geni_write_reg(demux_output_inv, mas->base, SE_SPI_DEMUX_OUTPUT_INV);
 	geni_write_reg(clk_sel, mas->base, SE_GENI_CLK_SEL);
 	geni_write_reg(m_clk_cfg, mas->base, GENI_SER_M_CLK_CFG);
+	GENI_SE_DBG(mas->ipc, false, mas->dev,
+		"%s:Loopback%d demux_sel0x%x demux_op_inv 0x%x clk_cfg 0x%x\n",
+		__func__, loopback_cfg, demux_sel, demux_output_inv, m_clk_cfg);
+	GENI_SE_DBG(mas->ipc, false, mas->dev,
+		"%s:clk_sel 0x%x cpol %d cpha %d\n", __func__,
+							clk_sel, cpol, cpha);
 	/* Ensure message level attributes are written before returning */
 	mb();
 setup_fifo_params_exit:
@@ -316,10 +332,7 @@
 		flags |= GSI_CS_TOGGLE;
 
 	word_len = xfer->bits_per_word - MIN_WORD_LEN;
-	if (mas->tx_fifo_width % xfer->bits_per_word)
-		pack = 0;
-	else
-		pack |= (GSI_TX_PACK_EN | GSI_RX_PACK_EN);
+	pack |= (GSI_TX_PACK_EN | GSI_RX_PACK_EN);
 	ret = get_spi_clk_cfg(mas->cur_speed_hz, mas, &idx, &div);
 	if (ret) {
 		dev_err(mas->dev, "%s:Err setting clks:%d\n", __func__, ret);
@@ -504,19 +517,26 @@
 		}
 	}
 
+	if (!(mas->cur_word_len % MIN_WORD_LEN)) {
+		rx_len = ((xfer->len << 3) / mas->cur_word_len);
+	} else {
+		int bytes_per_word = (mas->cur_word_len / BITS_PER_BYTE) + 1;
+
+		rx_len = (xfer->len / bytes_per_word);
+	}
+
 	if (xfer->tx_buf && xfer->rx_buf) {
 		cmd = SPI_FULL_DUPLEX;
 		tx_nent += 2;
 		rx_nent++;
-		rx_len = ((xfer->len << 3) / mas->cur_word_len);
 	} else if (xfer->tx_buf) {
 		cmd = SPI_TX_ONLY;
 		tx_nent += 2;
+		rx_len = 0;
 	} else if (xfer->rx_buf) {
 		cmd = SPI_RX_ONLY;
 		tx_nent++;
 		rx_nent++;
-		rx_len = ((xfer->len << 3) / mas->cur_word_len);
 	}
 
 	cs |= spi_slv->chip_select;
@@ -844,7 +864,14 @@
 	spi_tx_cfg &= ~CS_TOGGLE;
 	if (xfer->cs_change)
 		spi_tx_cfg |= CS_TOGGLE;
-	trans_len = ((xfer->len << 3) / mas->cur_word_len) & TRANS_LEN_MSK;
+	if (!(mas->cur_word_len % MIN_WORD_LEN)) {
+		trans_len =
+			((xfer->len << 3) / mas->cur_word_len) & TRANS_LEN_MSK;
+	} else {
+		int bytes_per_word = (mas->cur_word_len / BITS_PER_BYTE) + 1;
+
+		trans_len = (xfer->len / bytes_per_word) & TRANS_LEN_MSK;
+	}
 	if (!list_is_last(&xfer->transfer_list, &spi->cur_msg->transfers))
 		m_param |= FRAGMENTATION;
 
@@ -860,6 +887,9 @@
 	}
 	geni_write_reg(spi_tx_cfg, mas->base, SE_SPI_TRANS_CFG);
 	geni_setup_m_cmd(mas->base, m_cmd, m_param);
+	GENI_SE_DBG(mas->ipc, false, mas->dev,
+		"%s: trans_len %d xferlen%d tx_cfg 0x%x cmd 0x%x\n",
+		__func__, trans_len, xfer->len, spi_tx_cfg, m_cmd);
 	if (m_cmd & SPI_TX_ONLY)
 		geni_write_reg(mas->tx_wm, mas->base, SE_GENI_TX_WATERMARK_REG);
 	/* Ensure all writes are done before the WM interrupt */
@@ -872,6 +902,7 @@
 
 	reinit_completion(&mas->xfer_done);
 	geni_cancel_m_cmd(mas->base);
+	geni_write_reg(0, mas->base, SE_GENI_TX_WATERMARK_REG);
 	/* Ensure cmd cancel is written */
 	mb();
 	timeout = wait_for_completion_timeout(&mas->xfer_done, HZ);
@@ -966,12 +997,25 @@
 {
 	int i = 0;
 	int tx_fifo_width = (mas->tx_fifo_width >> 3);
-	int max_bytes = (mas->tx_fifo_depth - mas->tx_wm) * tx_fifo_width;
+	int max_bytes = 0;
 	const u8 *tx_buf = NULL;
 
 	if (!mas->cur_xfer)
 		return;
 
+	/*
+	 * For non-byte aligned bits-per-word values:
+	 * Assumption is that each SPI word will be accomodated in
+	 * ceil (bits_per_word / bits_per_byte)
+	 * and the next SPI word starts at the next byte.
+	 * In such cases, we can fit 1 SPI word per FIFO word so adjust the
+	 * max byte that can be sent per IRQ accordingly.
+	 */
+	if ((mas->tx_fifo_width % mas->cur_word_len))
+		max_bytes = (mas->tx_fifo_depth - mas->tx_wm) *
+				((mas->cur_word_len / BITS_PER_BYTE) + 1);
+	else
+		max_bytes = (mas->tx_fifo_depth - mas->tx_wm) * tx_fifo_width;
 	tx_buf = mas->cur_xfer->tx_buf;
 	tx_buf += (mas->cur_xfer->len - mas->tx_rem_bytes);
 	max_bytes = min_t(int, mas->tx_rem_bytes, max_bytes);
@@ -979,8 +1023,13 @@
 		int j;
 		u32 fifo_word = 0;
 		u8 *fifo_byte;
-		int bytes_to_write = min_t(int, (max_bytes - i), tx_fifo_width);
+		int bytes_per_fifo = tx_fifo_width;
+		int bytes_to_write = 0;
 
+		if ((mas->tx_fifo_width % mas->cur_word_len))
+			bytes_per_fifo =
+				(mas->cur_word_len / BITS_PER_BYTE) + 1;
+		bytes_to_write = min_t(int, (max_bytes - i), bytes_per_fifo);
 		fifo_byte = (u8 *)&fifo_word;
 		for (j = 0; j < bytes_to_write; j++)
 			fifo_byte[j] = tx_buf[i++];
@@ -1019,15 +1068,24 @@
 			rx_bytes += rx_last_byte_valid;
 		}
 	}
-	rx_bytes += rx_wc * fifo_width;
+	if (!(mas->tx_fifo_width % mas->cur_word_len))
+		rx_bytes += rx_wc * fifo_width;
+	else
+		rx_bytes += rx_wc *
+			((mas->cur_word_len / BITS_PER_BYTE) + 1);
 	rx_bytes = min_t(int, mas->rx_rem_bytes, rx_bytes);
 	rx_buf += (mas->cur_xfer->len - mas->rx_rem_bytes);
 	while (i < rx_bytes) {
 		u32 fifo_word = 0;
 		u8 *fifo_byte;
-		int read_bytes = min_t(int, (rx_bytes - i), fifo_width);
+		int bytes_per_fifo = fifo_width;
+		int read_bytes = 0;
 		int j;
 
+		if ((mas->tx_fifo_width % mas->cur_word_len))
+			bytes_per_fifo =
+				(mas->cur_word_len / BITS_PER_BYTE) + 1;
+		read_bytes = min_t(int, (rx_bytes - i), bytes_per_fifo);
 		fifo_word = geni_read_reg(mas->base, SE_GENI_RX_FIFOn);
 		fifo_byte = (u8 *)&fifo_word;
 		for (j = 0; j < read_bytes; j++)
@@ -1039,8 +1097,14 @@
 static irqreturn_t geni_spi_irq(int irq, void *dev)
 {
 	struct spi_geni_master *mas = dev;
-	u32 m_irq = geni_read_reg(mas->base, SE_GENI_M_IRQ_STATUS);
+	u32 m_irq = 0;
 
+	if (pm_runtime_status_suspended(dev)) {
+		GENI_SE_DBG(mas->ipc, false, mas->dev,
+				"%s: device is suspended\n", __func__);
+		goto exit_geni_spi_irq;
+	}
+	m_irq = geni_read_reg(mas->base, SE_GENI_M_IRQ_STATUS);
 	if ((m_irq & M_RX_FIFO_WATERMARK_EN) || (m_irq & M_RX_FIFO_LAST_EN))
 		geni_spi_handle_rx(mas);
 
@@ -1050,7 +1114,26 @@
 	if ((m_irq & M_CMD_DONE_EN) || (m_irq & M_CMD_CANCEL_EN) ||
 		(m_irq & M_CMD_ABORT_EN)) {
 		complete(&mas->xfer_done);
+		/*
+		 * If this happens, then a CMD_DONE came before all the buffer
+		 * bytes were sent out. This is unusual, log this condition and
+		 * disable the WM interrupt to prevent the system from stalling
+		 * due an interrupt storm.
+		 * If this happens when all Rx bytes haven't been received, log
+		 * the condition.
+		 */
+		if (mas->tx_rem_bytes) {
+			geni_write_reg(0, mas->base, SE_GENI_TX_WATERMARK_REG);
+			GENI_SE_DBG(mas->ipc, false, mas->dev,
+				"%s:Premature Done.tx_rem%d bpw%d\n",
+				__func__, mas->tx_rem_bytes, mas->cur_word_len);
+		}
+		if (mas->rx_rem_bytes)
+			GENI_SE_DBG(mas->ipc, false, mas->dev,
+				"%s:Premature Done.rx_rem%d bpw%d\n",
+				__func__, mas->rx_rem_bytes, mas->cur_word_len);
 	}
+exit_geni_spi_irq:
 	geni_write_reg(m_irq, mas->base, SE_GENI_M_IRQ_CLEAR);
 	return IRQ_HANDLED;
 }
diff --git a/drivers/tty/serial/msm_geni_serial.c b/drivers/tty/serial/msm_geni_serial.c
index f9054cb..67ed0e8 100644
--- a/drivers/tty/serial/msm_geni_serial.c
+++ b/drivers/tty/serial/msm_geni_serial.c
@@ -98,7 +98,9 @@
 #define UART_PARAM		(0x1)
 
 /* UART DMA Rx GP_IRQ_BITS */
-#define UART_DMA_RX_ERRS	(GENMASK(5, 8))
+#define UART_DMA_RX_PARITY_ERR	BIT(5)
+#define UART_DMA_RX_ERRS	(GENMASK(5, 6))
+#define UART_DMA_RX_BREAK	(GENMASK(7, 8))
 
 #define UART_OVERSAMPLING	(32)
 #define STALE_TIMEOUT		(16)
@@ -141,7 +143,8 @@
 	int (*handle_rx)(struct uart_port *uport,
 			unsigned int rx_fifo_wc,
 			unsigned int rx_last_byte_valid,
-			unsigned int rx_last);
+			unsigned int rx_last,
+			bool drop_rx);
 	struct device *wrapper_dev;
 	struct se_geni_rsc serial_rsc;
 	dma_addr_t tx_dma;
@@ -167,11 +170,13 @@
 static int handle_rx_console(struct uart_port *uport,
 			unsigned int rx_fifo_wc,
 			unsigned int rx_last_byte_valid,
-			unsigned int rx_last);
+			unsigned int rx_last,
+			bool drop_rx);
 static int handle_rx_hs(struct uart_port *uport,
 			unsigned int rx_fifo_wc,
 			unsigned int rx_last_byte_valid,
-			unsigned int rx_last);
+			unsigned int rx_last,
+			bool drop_rx);
 static unsigned int msm_geni_serial_tx_empty(struct uart_port *port);
 static int msm_geni_serial_power_on(struct uart_port *uport);
 static void msm_geni_serial_power_off(struct uart_port *uport);
@@ -681,7 +686,8 @@
 static int handle_rx_console(struct uart_port *uport,
 			unsigned int rx_fifo_wc,
 			unsigned int rx_last_byte_valid,
-			unsigned int rx_last)
+			unsigned int rx_last,
+			bool drop_rx)
 {
 	int i, c;
 	unsigned char *rx_char;
@@ -694,6 +700,8 @@
 
 		*(msm_port->rx_fifo) =
 			geni_read_reg_nolog(uport->membase, SE_GENI_RX_FIFOn);
+		if (drop_rx)
+			continue;
 		rx_char = (unsigned char *)msm_port->rx_fifo;
 
 		if (i == (rx_fifo_wc - 1)) {
@@ -710,14 +718,16 @@
 				tty_insert_flip_char(tport, rx_char[c], flag);
 		}
 	}
-	tty_flip_buffer_push(tport);
+	if (!drop_rx)
+		tty_flip_buffer_push(tport);
 	return 0;
 }
 #else
 static int handle_rx_console(struct uart_port *uport,
 			unsigned int rx_fifo_wc,
 			unsigned int rx_last_byte_valid,
-			unsigned int rx_last)
+			unsigned int rx_last,
+			bool drop_rx)
 {
 	return -EPERM;
 }
@@ -1001,7 +1011,8 @@
 static int handle_rx_hs(struct uart_port *uport,
 			unsigned int rx_fifo_wc,
 			unsigned int rx_last_byte_valid,
-			unsigned int rx_last)
+			unsigned int rx_last,
+			bool drop_rx)
 {
 	unsigned char *rx_char;
 	struct tty_port *tport;
@@ -1016,6 +1027,8 @@
 	tport = &uport->state->port;
 	ioread32_rep((uport->membase + SE_GENI_RX_FIFOn), msm_port->rx_fifo,
 								rx_fifo_wc);
+	if (drop_rx)
+		return 0;
 
 	rx_char = (unsigned char *)msm_port->rx_fifo;
 	ret = tty_insert_flip_string(tport, rx_char, rx_bytes);
@@ -1031,7 +1044,7 @@
 	return ret;
 }
 
-static int msm_geni_serial_handle_rx(struct uart_port *uport)
+static int msm_geni_serial_handle_rx(struct uart_port *uport, bool drop_rx)
 {
 	int ret = 0;
 	unsigned int rx_fifo_status;
@@ -1050,7 +1063,7 @@
 	rx_last = rx_fifo_status & RX_LAST;
 	if (rx_fifo_wc)
 		port->handle_rx(uport, rx_fifo_wc, rx_last_byte_valid,
-								rx_last);
+							rx_last, drop_rx);
 	return ret;
 }
 
@@ -1128,13 +1141,13 @@
 	} else {
 		msm_port->xmit_size = xmit_size;
 	}
+exit_handle_tx:
 	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
 		uart_write_wakeup(uport);
-exit_handle_tx:
 	return ret;
 }
 
-static int msm_geni_serial_handle_dma_rx(struct uart_port *uport)
+static int msm_geni_serial_handle_dma_rx(struct uart_port *uport, bool drop_rx)
 {
 	struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport);
 	unsigned int rx_bytes = 0;
@@ -1160,6 +1173,8 @@
 					__func__, rx_bytes);
 		goto exit_handle_dma_rx;
 	}
+	if (drop_rx)
+		goto exit_handle_dma_rx;
 
 	tport = &uport->state->port;
 	ret = tty_insert_flip_string(tport, (unsigned char *)(msm_port->rx_buf),
@@ -1211,6 +1226,7 @@
 	unsigned long flags;
 	unsigned int m_irq_en;
 	struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport);
+	bool drop_rx = false;
 
 	spin_lock_irqsave(&uport->lock, flags);
 	if (uart_console(uport) && uport->suspended)
@@ -1239,21 +1255,29 @@
 	}
 
 	if (!dma) {
-		if ((s_irq_status & S_GP_IRQ_0_EN) ||
-			(s_irq_status & S_GP_IRQ_1_EN) ||
-			(s_irq_status & S_GP_IRQ_2_EN) ||
-			(s_irq_status & S_GP_IRQ_3_EN)) {
-			IPC_LOG_MSG(msm_port->ipc_log_misc,
-				"%s.sirq 0x%x.\n", __func__, s_irq_status);
-			goto exit_geni_serial_isr;
-		}
-		if ((s_irq_status & S_RX_FIFO_WATERMARK_EN) ||
-			(s_irq_status & S_RX_FIFO_LAST_EN))
-			msm_geni_serial_handle_rx(uport);
-
 		if ((m_irq_status & m_irq_en) &
 		    (M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN))
 			msm_geni_serial_handle_tx(uport);
+
+		if ((s_irq_status & S_GP_IRQ_0_EN) ||
+			(s_irq_status & S_GP_IRQ_1_EN)) {
+			if (s_irq_status & S_GP_IRQ_0_EN)
+				uport->icount.parity++;
+			IPC_LOG_MSG(msm_port->ipc_log_misc,
+				"%s.sirq 0x%x parity:%d\n",
+				__func__, s_irq_status, uport->icount.parity);
+			drop_rx = true;
+		} else if ((s_irq_status & S_GP_IRQ_2_EN) ||
+			(s_irq_status & S_GP_IRQ_3_EN)) {
+			uport->icount.brk++;
+			IPC_LOG_MSG(msm_port->ipc_log_misc,
+				"%s.sirq 0x%x break:%d\n",
+				__func__, s_irq_status, uport->icount.brk);
+		}
+
+		if ((s_irq_status & S_RX_FIFO_WATERMARK_EN) ||
+			(s_irq_status & S_RX_FIFO_LAST_EN))
+			msm_geni_serial_handle_rx(uport, drop_rx);
 	} else {
 		if (dma_tx_status) {
 			geni_write_reg_nolog(dma_tx_status, uport->membase,
@@ -1272,13 +1296,22 @@
 				goto exit_geni_serial_isr;
 			}
 			if (dma_rx_status & UART_DMA_RX_ERRS) {
+				if (dma_rx_status & UART_DMA_RX_PARITY_ERR)
+					uport->icount.parity++;
 				IPC_LOG_MSG(msm_port->ipc_log_misc,
-					"%s.Rx Errors.  0x%x.\n",
-						__func__, dma_rx_status);
-				goto exit_geni_serial_isr;
+					"%s.Rx Errors.  0x%x parity:%d\n",
+					__func__, dma_rx_status,
+					uport->icount.parity);
+				drop_rx = true;
+			} else if (dma_rx_status & UART_DMA_RX_BREAK) {
+				uport->icount.brk++;
+				IPC_LOG_MSG(msm_port->ipc_log_misc,
+					"%s.Rx Errors.  0x%x break:%d\n",
+					__func__, dma_rx_status,
+					uport->icount.brk);
 			}
 			if (dma_rx_status & RX_DMA_DONE)
-				msm_geni_serial_handle_dma_rx(uport);
+				msm_geni_serial_handle_dma_rx(uport, drop_rx);
 		}
 	}
 
diff --git a/include/dt-bindings/clock/qcom,gcc-sdm845.h b/include/dt-bindings/clock/qcom,gcc-sdm845.h
index 339d470..c8696df 100644
--- a/include/dt-bindings/clock/qcom,gcc-sdm845.h
+++ b/include/dt-bindings/clock/qcom,gcc-sdm845.h
@@ -212,6 +212,7 @@
 #define GCC_VS_CTRL_CLK_SRC					194
 #define GCC_VSENSOR_CLK_SRC					195
 #define GPLL4							196
+#define GPLL6							197
 
 /* GCC reset clocks */
 #define GCC_MMSS_BCR						0
diff --git a/include/linux/mailbox_client.h b/include/linux/mailbox_client.h
index 86a2dc6..073391b 100644
--- a/include/linux/mailbox_client.h
+++ b/include/linux/mailbox_client.h
@@ -44,7 +44,7 @@
 					      const char *name);
 struct mbox_chan *mbox_request_channel(struct mbox_client *cl, int index);
 int mbox_send_message(struct mbox_chan *chan, void *mssg);
-int mbox_send_controller_data(struct mbox_chan *chan, void *mssg);
+int mbox_write_controller_data(struct mbox_chan *chan, void *mssg);
 void mbox_client_txdone(struct mbox_chan *chan, int r); /* atomic */
 bool mbox_client_peek_data(struct mbox_chan *chan); /* atomic */
 void mbox_free_channel(struct mbox_chan *chan); /* may sleep */
diff --git a/include/linux/mailbox_controller.h b/include/linux/mailbox_controller.h
index 7827c68..751b354 100644
--- a/include/linux/mailbox_controller.h
+++ b/include/linux/mailbox_controller.h
@@ -24,8 +24,8 @@
  *		transmission of data is reported by the controller via
  *		mbox_chan_txdone (if it has some TX ACK irq). It must not
  *		sleep.
- * @send_controller_data:
- *		Send data for the controller driver. This could be data to
+ * @write_controller_data:
+ *		Write data for the controller driver. This could be data to
  *		configure the controller or data that may be cached in the
  *		controller and not transmitted immediately. There is no ACK
  *		for this request and the request is not buffered in the
@@ -54,7 +54,7 @@
  */
 struct mbox_chan_ops {
 	int (*send_data)(struct mbox_chan *chan, void *data);
-	int (*send_controller_data)(struct mbox_chan *chan, void *data);
+	int (*write_controller_data)(struct mbox_chan *chan, void *data);
 	int (*startup)(struct mbox_chan *chan);
 	void (*shutdown)(struct mbox_chan *chan);
 	bool (*last_tx_done)(struct mbox_chan *chan);
diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index ed0099c9..07e1acb 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -158,7 +158,6 @@
 	NR_UNEVICTABLE,		/*  "     "     "   "       "         */
 	NR_ISOLATED_ANON,	/* Temporary isolated pages from anon lru */
 	NR_ISOLATED_FILE,	/* Temporary isolated pages from file lru */
-	NR_PAGES_SCANNED,	/* pages scanned since last reclaim */
 	WORKINGSET_REFAULT,
 	WORKINGSET_ACTIVATE,
 	WORKINGSET_NODERECLAIM,
@@ -645,6 +644,8 @@
 	int kswapd_order;
 	enum zone_type kswapd_classzone_idx;
 
+	int kswapd_failures;		/* Number of 'reclaimed == 0' runs */
+
 #ifdef CONFIG_COMPACTION
 	int kcompactd_max_order;
 	enum zone_type kcompactd_classzone_idx;
diff --git a/include/linux/nmi.h b/include/linux/nmi.h
index 3ca2526..4443a29 100644
--- a/include/linux/nmi.h
+++ b/include/linux/nmi.h
@@ -24,6 +24,9 @@
 #define NMI_WATCHDOG_ENABLED      (1 << NMI_WATCHDOG_ENABLED_BIT)
 #define SOFT_WATCHDOG_ENABLED     (1 << SOFT_WATCHDOG_ENABLED_BIT)
 
+DECLARE_PER_CPU(unsigned long, hrtimer_interrupts);
+DECLARE_PER_CPU(unsigned long, hrtimer_interrupts_saved);
+
 /**
  * touch_nmi_watchdog - restart NMI watchdog timeout.
  *
@@ -31,8 +34,11 @@
  * may be used to reset the timeout - for code which intentionally
  * disables interrupts for a long time. This call is stateless.
  */
-#if defined(CONFIG_HAVE_NMI_WATCHDOG) || defined(CONFIG_HARDLOCKUP_DETECTOR)
+#if defined(CONFIG_HAVE_NMI_WATCHDOG) || defined(CONFIG_HARDLOCKUP_DETECTOR_NMI)
 #include <asm/nmi.h>
+#endif
+
+#if defined(CONFIG_HAVE_NMI_WATCHDOG) || defined(CONFIG_HARDLOCKUP_DETECTOR)
 extern void touch_nmi_watchdog(void);
 #else
 static inline void touch_nmi_watchdog(void)
@@ -130,6 +136,7 @@
 #define sysctl_softlockup_all_cpu_backtrace 0
 #define sysctl_hardlockup_all_cpu_backtrace 0
 #endif
+
 extern bool is_hardlockup(void);
 struct ctl_table;
 extern int proc_watchdog(struct ctl_table *, int ,
diff --git a/include/linux/sched.h b/include/linux/sched.h
index e1345ec..a4ea064 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -3964,4 +3964,6 @@
 void cpufreq_remove_update_util_hook(int cpu);
 #endif /* CONFIG_CPU_FREQ */
 
+extern DEFINE_PER_CPU_READ_MOSTLY(int, sched_load_boost);
+
 #endif
diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h
index 261202f..8dbfdf7 100644
--- a/include/net/fib_rules.h
+++ b/include/net/fib_rules.h
@@ -95,8 +95,6 @@
 	[FRA_FWMARK]	= { .type = NLA_U32 }, \
 	[FRA_FWMASK]	= { .type = NLA_U32 }, \
 	[FRA_TABLE]     = { .type = NLA_U32 }, \
-	[FRA_UID_START]	= { .type = NLA_U32 }, \
-	[FRA_UID_END]	= { .type = NLA_U32 }, \
 	[FRA_SUPPRESS_PREFIXLEN] = { .type = NLA_U32 }, \
 	[FRA_SUPPRESS_IFGROUP] = { .type = NLA_U32 }, \
 	[FRA_GOTO]	= { .type = NLA_U32 }, \
diff --git a/include/trace/events/vmscan.h b/include/trace/events/vmscan.h
index c88fd09..2f8536b 100644
--- a/include/trace/events/vmscan.h
+++ b/include/trace/events/vmscan.h
@@ -269,23 +269,24 @@
 		__entry->retval)
 );
 
-DECLARE_EVENT_CLASS(mm_vmscan_lru_isolate_template,
-
+TRACE_EVENT(mm_vmscan_lru_isolate,
 	TP_PROTO(int classzone_idx,
 		int order,
 		unsigned long nr_requested,
 		unsigned long nr_scanned,
+		unsigned long nr_skipped,
 		unsigned long nr_taken,
 		isolate_mode_t isolate_mode,
 		int file),
 
-	TP_ARGS(classzone_idx, order, nr_requested, nr_scanned, nr_taken, isolate_mode, file),
+	TP_ARGS(classzone_idx, order, nr_requested, nr_scanned, nr_skipped, nr_taken, isolate_mode, file),
 
 	TP_STRUCT__entry(
 		__field(int, classzone_idx)
 		__field(int, order)
 		__field(unsigned long, nr_requested)
 		__field(unsigned long, nr_scanned)
+		__field(unsigned long, nr_skipped)
 		__field(unsigned long, nr_taken)
 		__field(isolate_mode_t, isolate_mode)
 		__field(int, file)
@@ -296,49 +297,23 @@
 		__entry->order = order;
 		__entry->nr_requested = nr_requested;
 		__entry->nr_scanned = nr_scanned;
+		__entry->nr_skipped = nr_skipped;
 		__entry->nr_taken = nr_taken;
 		__entry->isolate_mode = isolate_mode;
 		__entry->file = file;
 	),
 
-	TP_printk("isolate_mode=%d classzone=%d order=%d nr_requested=%lu nr_scanned=%lu nr_taken=%lu file=%d",
+	TP_printk("isolate_mode=%d classzone=%d order=%d nr_requested=%lu nr_scanned=%lu nr_skipped=%lu nr_taken=%lu file=%d",
 		__entry->isolate_mode,
 		__entry->classzone_idx,
 		__entry->order,
 		__entry->nr_requested,
 		__entry->nr_scanned,
+		__entry->nr_skipped,
 		__entry->nr_taken,
 		__entry->file)
 );
 
-DEFINE_EVENT(mm_vmscan_lru_isolate_template, mm_vmscan_lru_isolate,
-
-	TP_PROTO(int classzone_idx,
-		int order,
-		unsigned long nr_requested,
-		unsigned long nr_scanned,
-		unsigned long nr_taken,
-		isolate_mode_t isolate_mode,
-		int file),
-
-	TP_ARGS(classzone_idx, order, nr_requested, nr_scanned, nr_taken, isolate_mode, file)
-
-);
-
-DEFINE_EVENT(mm_vmscan_lru_isolate_template, mm_vmscan_memcg_isolate,
-
-	TP_PROTO(int classzone_idx,
-		int order,
-		unsigned long nr_requested,
-		unsigned long nr_scanned,
-		unsigned long nr_taken,
-		isolate_mode_t isolate_mode,
-		int file),
-
-	TP_ARGS(classzone_idx, order, nr_requested, nr_scanned, nr_taken, isolate_mode, file)
-
-);
-
 TRACE_EVENT(mm_vmscan_writepage,
 
 	TP_PROTO(struct page *page),
diff --git a/include/uapi/linux/fib_rules.h b/include/uapi/linux/fib_rules.h
index a66c4ba..bbf02a6 100644
--- a/include/uapi/linux/fib_rules.h
+++ b/include/uapi/linux/fib_rules.h
@@ -54,8 +54,6 @@
 	FRA_TABLE,	/* Extended table id */
 	FRA_FWMASK,	/* mask for netfilter mark */
 	FRA_OIFNAME,
-	FRA_UID_START,	/* UID range */
-	FRA_UID_END,
 	FRA_PAD,
 	FRA_L3MDEV,	/* iif or oif is l3mdev goto its table */
 	FRA_UID_RANGE,	/* UID range */
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 4573d45..ccfb9bf 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -3232,9 +3232,9 @@
 
 static inline
 unsigned long sum_capacity_reqs(unsigned long cfs_cap,
-				struct sched_capacity_reqs *scr)
+				struct sched_capacity_reqs *scr, int cpu)
 {
-	unsigned long total = add_capacity_margin(cfs_cap + scr->rt);
+	unsigned long total = add_capacity_margin(cfs_cap + scr->rt, cpu);
 	return total += scr->dl;
 }
 
@@ -3246,7 +3246,7 @@
 	struct sched_capacity_reqs *scr;
 
 	scr = &per_cpu(cpu_sched_capacity_reqs, cpu);
-	if (sum_capacity_reqs(cpu_utilization, scr) < capacity_curr)
+	if (sum_capacity_reqs(cpu_utilization, scr, cpu) < capacity_curr)
 		return;
 
 	/*
diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c
index 116ffe4..c2372f8 100644
--- a/kernel/sched/cpufreq_schedutil.c
+++ b/kernel/sched/cpufreq_schedutil.c
@@ -84,6 +84,7 @@
 };
 
 static DEFINE_PER_CPU(struct sugov_cpu, sugov_cpu);
+static unsigned int stale_ns;
 
 /************************ Governor internals ***********************/
 
@@ -364,7 +365,7 @@
 		 * idle now (and clear iowait_boost for it).
 		 */
 		delta_ns = last_freq_update_time - j_sg_cpu->last_update;
-		if (delta_ns > sched_ravg_window) {
+		if (delta_ns > stale_ns) {
 			j_sg_cpu->iowait_boost = 0;
 			continue;
 		}
@@ -716,6 +717,7 @@
 
 	policy->governor_data = sg_policy;
 	sg_policy->tunables = tunables;
+	stale_ns = sched_ravg_window + (sched_ravg_window >> 3);
 
 	ret = kobject_init_and_add(&tunables->attr_set.kobj, &sugov_tunables_ktype,
 				   get_governor_parent_kobj(policy), "%s",
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index fde0639..38fa781 100755
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -89,6 +89,7 @@
 unsigned int sysctl_sched_sync_hint_enable = 1;
 unsigned int sysctl_sched_initial_task_util = 0;
 unsigned int sysctl_sched_cstate_aware = 1;
+DEFINE_PER_CPU_READ_MOSTLY(int, sched_load_boost);
 
 #ifdef CONFIG_SCHED_WALT
 unsigned int sysctl_sched_use_walt_cpu_util = 1;
@@ -7043,7 +7044,7 @@
 			cpu_idle_idx = idle_get_state_idx(cpu_rq(i));
 
 			if (!need_idle &&
-			    add_capacity_margin(new_util_cum) <
+			    add_capacity_margin(new_util_cum, i) <
 			    capacity_curr_of(i)) {
 				if (sysctl_sched_cstate_aware) {
 					if (cpu_idle_idx < min_idle_idx) {
diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c
index 6b935e7..1bc4828 100644
--- a/kernel/sched/rt.c
+++ b/kernel/sched/rt.c
@@ -1811,7 +1811,7 @@
 					cpu_idle_idx =
 					     idle_get_state_idx(cpu_rq(cpu));
 
-				if (add_capacity_margin(new_util_cum) <
+				if (add_capacity_margin(new_util_cum, cpu) <
 				    capacity_curr_of(cpu)) {
 					if (cpu_idle_idx < best_cpu_idle_idx ||
 					    (best_cpu != task_cpu(task) &&
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index 72b79bb..0a1e62f 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -29,6 +29,7 @@
 
 #ifdef CONFIG_SCHED_WALT
 extern unsigned int sched_ravg_window;
+extern unsigned int walt_cpu_util_freq_divisor;
 
 struct walt_sched_stats {
 	int nr_big_tasks;
@@ -1805,47 +1806,87 @@
 #endif
 
 static inline unsigned long
-cpu_util_freq(int cpu, struct sched_walt_cpu_load *walt_load)
+cpu_util_freq_pelt(int cpu)
 {
 	struct rq *rq = cpu_rq(cpu);
-	u64 util = rq->cfs.avg.util_avg;
+	unsigned long util = rq->cfs.avg.util_avg;
 	unsigned long capacity = capacity_orig_of(cpu);
 
-#ifdef CONFIG_SCHED_WALT
-	if (!walt_disabled && sysctl_sched_use_walt_cpu_util) {
+	util *= (100 + per_cpu(sched_load_boost, cpu));
+	do_div(util, 100);
 
-		util = freq_policy_load(rq);
-		util = div64_u64(util,
-				 sched_ravg_window >> SCHED_CAPACITY_SHIFT);
-
-		if (walt_load) {
-			u64 nl = cpu_rq(cpu)->nt_prev_runnable_sum +
-				rq->grp_time.nt_prev_runnable_sum;
-			u64 pl = rq->walt_stats.pred_demands_sum;
-
-			nl = div64_u64(nl, sched_ravg_window >>
-						SCHED_CAPACITY_SHIFT);
-			pl = div64_u64(pl, sched_ravg_window >>
-						SCHED_CAPACITY_SHIFT);
-
-			walt_load->prev_window_util = util;
-			walt_load->nl = nl;
-			walt_load->pl = pl;
-			rq->old_busy_time = util;
-			rq->old_estimated_time = pl;
-			walt_load->ws = rq->window_start;
-		}
-	}
-#endif
 	return (util >= capacity) ? capacity : util;
 }
+
+#ifdef CONFIG_SCHED_WALT
+static inline unsigned long
+cpu_util_freq_walt(int cpu, struct sched_walt_cpu_load *walt_load)
+{
+	u64 util, util_unboosted;
+	struct rq *rq = cpu_rq(cpu);
+	unsigned long capacity = capacity_orig_of(cpu);
+	int boost;
+
+	if (walt_disabled || !sysctl_sched_use_walt_cpu_util)
+		return cpu_util_freq_pelt(cpu);
+
+	boost = per_cpu(sched_load_boost, cpu);
+	util_unboosted = util = freq_policy_load(rq);
+	util = div64_u64(util * (100 + boost),
+			 walt_cpu_util_freq_divisor);
+
+	if (walt_load) {
+		u64 nl = cpu_rq(cpu)->nt_prev_runnable_sum +
+			rq->grp_time.nt_prev_runnable_sum;
+		u64 pl = rq->walt_stats.pred_demands_sum;
+
+		/* do_pl_notif() needs unboosted signals */
+		rq->old_busy_time = div64_u64(util_unboosted,
+					      sched_ravg_window >>
+					      SCHED_CAPACITY_SHIFT);
+		rq->old_estimated_time = div64_u64(pl, sched_ravg_window >>
+						       SCHED_CAPACITY_SHIFT);
+
+		nl = div64_u64(nl * (100 + boost),
+			       walt_cpu_util_freq_divisor);
+		pl = div64_u64(pl * (100 + boost),
+			       walt_cpu_util_freq_divisor);
+
+		walt_load->prev_window_util = util;
+		walt_load->nl = nl;
+		walt_load->pl = pl;
+		walt_load->ws = rq->window_start;
+	}
+
+	return (util >= capacity) ? capacity : util;
+}
+
+static inline unsigned long
+cpu_util_freq(int cpu, struct sched_walt_cpu_load *walt_load)
+{
+	return cpu_util_freq_walt(cpu, walt_load);
+}
+
+#else
+
+static inline unsigned long
+cpu_util_freq(int cpu, struct sched_walt_cpu_load *walt_load)
+{
+	return cpu_util_freq_pelt(cpu);
+}
+
+#endif /* CONFIG_SCHED_WALT */
+
 #endif
 
 extern unsigned int capacity_margin_freq;
 
-static inline unsigned long add_capacity_margin(unsigned long cpu_capacity)
+static inline unsigned long
+add_capacity_margin(unsigned long cpu_capacity, int cpu)
 {
-	cpu_capacity  = cpu_capacity * capacity_margin_freq;
+	cpu_capacity  = cpu_capacity * capacity_margin_freq *
+			(100 + per_cpu(sched_load_boost, cpu));
+	cpu_capacity /= 100;
 	cpu_capacity /= SCHED_CAPACITY_SCALE;
 	return cpu_capacity;
 }
diff --git a/kernel/sched/walt.c b/kernel/sched/walt.c
index 60249e4..316c276 100644
--- a/kernel/sched/walt.c
+++ b/kernel/sched/walt.c
@@ -109,6 +109,12 @@
 /* Window size (in ns) */
 __read_mostly unsigned int sched_ravg_window = MIN_SCHED_RAVG_WINDOW;
 
+/*
+ * A after-boot constant divisor for cpu_util_freq_walt() to apply the load
+ * boost.
+ */
+__read_mostly unsigned int walt_cpu_util_freq_divisor;
+
 /* Initial task load. Newly created tasks are assigned this load. */
 unsigned int __read_mostly sched_init_task_load_windows;
 unsigned int __read_mostly sysctl_sched_init_task_load_pct = 15;
@@ -3107,4 +3113,7 @@
 		clear_top_tasks_bitmap(rq->top_tasks_bitmap[j]);
 	}
 	rq->cum_window_demand = 0;
+
+	walt_cpu_util_freq_divisor =
+	    (sched_ravg_window >> SCHED_CAPACITY_SHIFT) * 100;
 }
diff --git a/kernel/time/timer.c b/kernel/time/timer.c
index e671a32..843fb50 100644
--- a/kernel/time/timer.c
+++ b/kernel/time/timer.c
@@ -201,6 +201,7 @@
 	bool			migration_enabled;
 	bool			nohz_active;
 	bool			is_idle;
+	bool			must_forward_clk;
 	DECLARE_BITMAP(pending_map, WHEEL_SIZE);
 	struct hlist_head	vectors[WHEEL_SIZE];
 } ____cacheline_aligned;
@@ -874,13 +875,19 @@
 
 static inline void forward_timer_base(struct timer_base *base)
 {
-	unsigned long jnow = READ_ONCE(jiffies);
+	unsigned long jnow;
 
 	/*
-	 * We only forward the base when it's idle and we have a delta between
-	 * base clock and jiffies.
+	 * We only forward the base when we are idle or have just come out of
+	 * idle (must_forward_clk logic), and have a delta between base clock
+	 * and jiffies. In the common case, run_timers will take care of it.
 	 */
-	if (!base->is_idle || (long) (jnow - base->clk) < 2)
+	if (likely(!base->must_forward_clk))
+		return;
+
+	jnow = READ_ONCE(jiffies);
+	base->must_forward_clk = base->is_idle;
+	if ((long)(jnow - base->clk) < 2)
 		return;
 
 	/*
@@ -956,6 +963,11 @@
 	 * same array bucket then just return:
 	 */
 	if (timer_pending(timer)) {
+		/*
+		 * The downside of this optimization is that it can result in
+		 * larger granularity than you would get from adding a new
+		 * timer with this expiry.
+		 */
 		if (timer->expires == expires)
 			return 1;
 
@@ -966,6 +978,7 @@
 		 * dequeue/enqueue dance.
 		 */
 		base = lock_timer_base(timer, &flags);
+		forward_timer_base(base);
 
 		clk = base->clk;
 		idx = calc_wheel_index(expires, clk);
@@ -982,6 +995,7 @@
 		}
 	} else {
 		base = lock_timer_base(timer, &flags);
+		forward_timer_base(base);
 	}
 
 	ret = detach_if_pending(timer, base, false);
@@ -1009,12 +1023,10 @@
 			spin_lock(&base->lock);
 			WRITE_ONCE(timer->flags,
 				   (timer->flags & ~TIMER_BASEMASK) | base->cpu);
+			forward_timer_base(base);
 		}
 	}
 
-	/* Try to forward a stale timer base clock */
-	forward_timer_base(base);
-
 	timer->expires = expires;
 	/*
 	 * If 'idx' was calculated above and the base time did not advance
@@ -1130,6 +1142,7 @@
 		WRITE_ONCE(timer->flags,
 			   (timer->flags & ~TIMER_BASEMASK) | cpu);
 	}
+	forward_timer_base(base);
 
 	debug_activate(timer, timer->expires);
 	internal_add_timer(base, timer);
@@ -1546,10 +1559,16 @@
 		if (!is_max_delta)
 			expires = basem + (nextevt - basej) * TICK_NSEC;
 		/*
-		 * If we expect to sleep more than a tick, mark the base idle:
+		 * If we expect to sleep more than a tick, mark the base idle.
+		 * Also the tick is stopped so any added timer must forward
+		 * the base clk itself to keep granularity small. This idle
+		 * logic is only maintained for the BASE_STD base, deferrable
+		 * timers may still see large granularity skew (by design).
 		 */
-		if ((expires - basem) > TICK_NSEC)
+		if ((expires - basem) > TICK_NSEC) {
+			base->must_forward_clk = true;
 			base->is_idle = true;
+		}
 	}
 	spin_unlock(&base->lock);
 
@@ -1659,6 +1678,19 @@
 {
 	struct timer_base *base = this_cpu_ptr(&timer_bases[BASE_STD]);
 
+	/*
+	 * must_forward_clk must be cleared before running timers so that any
+	 * timer functions that call mod_timer will not try to forward the
+	 * base. idle trcking / clock forwarding logic is only used with
+	 * BASE_STD timers.
+	 *
+	 * The deferrable base does not do idle tracking at all, so we do
+	 * not forward it. This can result in very large variations in
+	 * granularity for deferrable timers, but they can be deferred for
+	 * long periods due to idle.
+	 */
+	base->must_forward_clk = false;
+
 	__run_timers(base);
 	if (IS_ENABLED(CONFIG_NO_HZ_COMMON) && base->nohz_active)
 		__run_timers(this_cpu_ptr(&timer_bases[BASE_DEF]));
diff --git a/kernel/watchdog.c b/kernel/watchdog.c
index cffb5f2..cfbf027 100644
--- a/kernel/watchdog.c
+++ b/kernel/watchdog.c
@@ -81,10 +81,10 @@
 static DEFINE_PER_CPU(unsigned int, watchdog_en);
 static DEFINE_PER_CPU(bool, softlockup_touch_sync);
 static DEFINE_PER_CPU(bool, soft_watchdog_warn);
-static DEFINE_PER_CPU(unsigned long, hrtimer_interrupts);
 static DEFINE_PER_CPU(unsigned long, soft_lockup_hrtimer_cnt);
 static DEFINE_PER_CPU(struct task_struct *, softlockup_task_ptr_saved);
-static DEFINE_PER_CPU(unsigned long, hrtimer_interrupts_saved);
+DEFINE_PER_CPU(unsigned long, hrtimer_interrupts);
+DEFINE_PER_CPU(unsigned long, hrtimer_interrupts_saved);
 static unsigned long soft_lockup_nmi_warn;
 
 unsigned int __read_mostly softlockup_panic =
@@ -254,6 +254,10 @@
 {
 }
 
+void __weak watchdog_check_hardlockup_other_cpu(void)
+{
+}
+
 static int watchdog_enable_all_cpus(void);
 static void watchdog_disable_all_cpus(void);
 
@@ -271,6 +275,9 @@
 	/* kick the hardlockup detector */
 	watchdog_interrupt_count();
 
+	/* test for hardlockups on the next cpu */
+	watchdog_check_hardlockup_other_cpu();
+
 	/* kick the softlockup detector */
 	wake_up_process(__this_cpu_read(softlockup_watchdog));
 
diff --git a/kernel/watchdog_hld.c b/kernel/watchdog_hld.c
index 12b8dd6..5b2c127 100644
--- a/kernel/watchdog_hld.c
+++ b/kernel/watchdog_hld.c
@@ -18,7 +18,11 @@
 
 static DEFINE_PER_CPU(bool, hard_watchdog_warn);
 static DEFINE_PER_CPU(bool, watchdog_nmi_touch);
+#ifdef CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU
+static cpumask_t __read_mostly watchdog_cpus;
+#else
 static DEFINE_PER_CPU(struct perf_event *, watchdog_ev);
+#endif
 
 /* boot commands */
 /*
@@ -26,7 +30,10 @@
  */
 unsigned int __read_mostly hardlockup_panic =
 			CONFIG_BOOTPARAM_HARDLOCKUP_PANIC_VALUE;
+
+#ifdef CONFIG_HARDLOCKUP_DETECTOR_NMI
 static unsigned long hardlockup_allcpu_dumped;
+#endif
 /*
  * We may not want to enable hard lockup detection by default in all cases,
  * for example when running the kernel as a guest on a hypervisor. In these
@@ -68,6 +75,108 @@
 }
 EXPORT_SYMBOL(touch_nmi_watchdog);
 
+#ifdef CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU
+static unsigned int watchdog_next_cpu(unsigned int cpu)
+{
+	cpumask_t cpus = watchdog_cpus;
+	unsigned int next_cpu;
+
+	next_cpu = cpumask_next(cpu, &cpus);
+	if (next_cpu >= nr_cpu_ids)
+		next_cpu = cpumask_first(&cpus);
+
+	if (next_cpu == cpu)
+		return nr_cpu_ids;
+
+	return next_cpu;
+}
+
+static int is_hardlockup_other_cpu(unsigned int cpu)
+{
+	unsigned long hrint = per_cpu(hrtimer_interrupts, cpu);
+
+	if (per_cpu(hrtimer_interrupts_saved, cpu) == hrint)
+		return 1;
+
+	per_cpu(hrtimer_interrupts_saved, cpu) = hrint;
+	return 0;
+}
+
+void watchdog_check_hardlockup_other_cpu(void)
+{
+	unsigned int next_cpu;
+
+	/*
+	 * Test for hardlockups every 3 samples.  The sample period is
+	 *  watchdog_thresh * 2 / 5, so 3 samples gets us back to slightly over
+	 *  watchdog_thresh (over by 20%).
+	 */
+	if (__this_cpu_read(hrtimer_interrupts) % 3 != 0)
+		return;
+
+	/* check for a hardlockup on the next cpu */
+	next_cpu = watchdog_next_cpu(smp_processor_id());
+	if (next_cpu >= nr_cpu_ids)
+		return;
+
+	smp_rmb();
+
+	if (per_cpu(watchdog_nmi_touch, next_cpu) == true) {
+		per_cpu(watchdog_nmi_touch, next_cpu) = false;
+		return;
+	}
+
+	if (is_hardlockup_other_cpu(next_cpu)) {
+		/* only warn once */
+		if (per_cpu(hard_watchdog_warn, next_cpu) == true)
+			return;
+
+		if (hardlockup_panic)
+			panic("Watchdog detected hard LOCKUP on cpu %u",
+				next_cpu);
+		else
+			WARN(1, "Watchdog detected hard LOCKUP on cpu %u",
+				next_cpu);
+
+		per_cpu(hard_watchdog_warn, next_cpu) = true;
+	} else {
+		per_cpu(hard_watchdog_warn, next_cpu) = false;
+	}
+}
+
+int watchdog_nmi_enable(unsigned int cpu)
+{
+	/*
+	 * The new cpu will be marked online before the first hrtimer interrupt
+	 * runs on it.  If another cpu tests for a hardlockup on the new cpu
+	 * before it has run its first hrtimer, it will get a false positive.
+	 * Touch the watchdog on the new cpu to delay the first check for at
+	 * least 3 sampling periods to guarantee one hrtimer has run on the new
+	 * cpu.
+	 */
+	per_cpu(watchdog_nmi_touch, cpu) = true;
+	smp_wmb();
+	cpumask_set_cpu(cpu, &watchdog_cpus);
+	return 0;
+}
+
+void watchdog_nmi_disable(unsigned int cpu)
+{
+	unsigned int next_cpu = watchdog_next_cpu(cpu);
+
+	/*
+	 * Offlining this cpu will cause the cpu before this one to start
+	 * checking the one after this one.  If this cpu just finished checking
+	 * the next cpu and updating hrtimer_interrupts_saved, and then the
+	 * previous cpu checks it within one sample period, it will trigger a
+	 * false positive.  Touch the watchdog on the next cpu to prevent it.
+	 */
+	if (next_cpu < nr_cpu_ids)
+		per_cpu(watchdog_nmi_touch, next_cpu) = true;
+	smp_wmb();
+	cpumask_clear_cpu(cpu, &watchdog_cpus);
+}
+#else
 static struct perf_event_attr wd_hw_attr = {
 	.type		= PERF_TYPE_HARDWARE,
 	.config		= PERF_COUNT_HW_CPU_CYCLES,
@@ -228,3 +337,4 @@
 		cpu0_err = 0;
 	}
 }
+#endif
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 2f9f7aa..411b383 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -779,11 +779,20 @@
 	  The frequency of hrtimer and NMI events and the soft and hard lockup
 	  thresholds can be controlled through the sysctl watchdog_thresh.
 
-config HARDLOCKUP_DETECTOR
+config HARDLOCKUP_DETECTOR_NMI
 	def_bool y
 	depends on LOCKUP_DETECTOR && !HAVE_NMI_WATCHDOG
 	depends on PERF_EVENTS && HAVE_PERF_EVENTS_NMI
 
+config HARDLOCKUP_DETECTOR_OTHER_CPU
+       def_bool y
+       depends on LOCKUP_DETECTOR && SMP
+       depends on !HARDLOCKUP_DETECTOR_NMI && !HAVE_NMI_WATCHDOG
+
+config HARDLOCKUP_DETECTOR
+       def_bool y
+       depends on HARDLOCKUP_DETECTOR_NMI || HARDLOCKUP_DETECTOR_OTHER_CPU
+
 config BOOTPARAM_HARDLOCKUP_PANIC
 	bool "Panic (Reboot) On Hard Lockups"
 	depends on HARDLOCKUP_DETECTOR
diff --git a/mm/internal.h b/mm/internal.h
index 537ac99..df6319f 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -74,11 +74,16 @@
 extern unsigned long highest_memmap_pfn;
 
 /*
+ * Maximum number of reclaim retries without progress before the OOM
+ * killer is consider the only way forward.
+ */
+#define MAX_RECLAIM_RETRIES 16
+
+/*
  * in mm/vmscan.c:
  */
 extern int isolate_lru_page(struct page *page);
 extern void putback_lru_page(struct page *page);
-extern bool pgdat_reclaimable(struct pglist_data *pgdat);
 
 /*
  * in mm/rmap.c:
diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
index ede13734..6eb61a4 100644
--- a/mm/memory_hotplug.c
+++ b/mm/memory_hotplug.c
@@ -1201,7 +1201,11 @@
 
 		arch_refresh_nodedata(nid, pgdat);
 	} else {
-		/* Reset the nr_zones, order and classzone_idx before reuse */
+		/*
+		 * Reset the nr_zones, order and classzone_idx before reuse.
+		 * Note that kswapd will init kswapd_classzone_idx properly
+		 * when it starts in the near future.
+		 */
 		pgdat->nr_zones = 0;
 		pgdat->kswapd_order = 0;
 		pgdat->kswapd_classzone_idx = 0;
diff --git a/mm/migrate.c b/mm/migrate.c
index f0b786d..4213d27 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -1738,9 +1738,6 @@
 {
 	int z;
 
-	if (!pgdat_reclaimable(pgdat))
-		return false;
-
 	for (z = pgdat->nr_zones - 1; z >= 0; z--) {
 		struct zone *zone = pgdat->node_zones + z;
 
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 44085b2..3eb5f68 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -1099,14 +1099,10 @@
 {
 	int migratetype = 0;
 	int batch_free = 0;
-	unsigned long nr_scanned;
 	bool isolated_pageblocks;
 
 	spin_lock(&zone->lock);
 	isolated_pageblocks = has_isolate_pageblock(zone);
-	nr_scanned = node_page_state(zone->zone_pgdat, NR_PAGES_SCANNED);
-	if (nr_scanned)
-		__mod_node_page_state(zone->zone_pgdat, NR_PAGES_SCANNED, -nr_scanned);
 
 	while (count) {
 		struct page *page;
@@ -1159,12 +1155,7 @@
 				unsigned int order,
 				int migratetype)
 {
-	unsigned long nr_scanned;
 	spin_lock(&zone->lock);
-	nr_scanned = node_page_state(zone->zone_pgdat, NR_PAGES_SCANNED);
-	if (nr_scanned)
-		__mod_node_page_state(zone->zone_pgdat, NR_PAGES_SCANNED, -nr_scanned);
-
 	if (unlikely(has_isolate_pageblock(zone) ||
 		is_migrate_isolate(migratetype))) {
 		migratetype = get_pfnblock_migratetype(page, pfn);
@@ -3501,19 +3492,12 @@
 }
 
 /*
- * Maximum number of reclaim retries without any progress before OOM killer
- * is consider as the only way to move forward.
- */
-#define MAX_RECLAIM_RETRIES 16
-
-/*
  * Checks whether it makes sense to retry the reclaim to make a forward progress
  * for the given allocation request.
- * The reclaim feedback represented by did_some_progress (any progress during
- * the last reclaim round) and no_progress_loops (number of reclaim rounds without
- * any progress in a row) is considered as well as the reclaimable pages on the
- * applicable zone list (with a backoff mechanism which is a function of
- * no_progress_loops).
+ *
+ * We give up when we either have tried MAX_RECLAIM_RETRIES in a row
+ * without success, or when we couldn't even meet the watermark if we
+ * reclaimed all remaining pages on the LRU lists.
  *
  * Returns true if a retry is viable or false to enter the oom path.
  */
@@ -3556,13 +3540,11 @@
 		unsigned long reclaimable;
 
 		available = reclaimable = zone_reclaimable_pages(zone);
-		available -= DIV_ROUND_UP((*no_progress_loops) * available,
-					  MAX_RECLAIM_RETRIES);
 		available += zone_page_state_snapshot(zone, NR_FREE_PAGES);
 
 		/*
-		 * Would the allocation succeed if we reclaimed the whole
-		 * available?
+		 * Would the allocation succeed if we reclaimed all
+		 * reclaimable pages?
 		 */
 		if (__zone_watermark_ok(zone, order, min_wmark_pages(zone),
 				ac_classzone_idx(ac), alloc_flags, available)) {
@@ -4442,7 +4424,6 @@
 #endif
 			" writeback_tmp:%lukB"
 			" unstable:%lukB"
-			" pages_scanned:%lu"
 			" all_unreclaimable? %s"
 			"\n",
 			pgdat->node_id,
@@ -4465,8 +4446,8 @@
 #endif
 			K(node_page_state(pgdat, NR_WRITEBACK_TEMP)),
 			K(node_page_state(pgdat, NR_UNSTABLE_NFS)),
-			node_page_state(pgdat, NR_PAGES_SCANNED),
-			!pgdat_reclaimable(pgdat) ? "yes" : "no");
+			pgdat->kswapd_failures >= MAX_RECLAIM_RETRIES ?
+				"yes" : "no");
 	}
 
 	for_each_populated_zone(zone) {
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 7b5848cf..bb18b47 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -235,12 +235,6 @@
 	return nr;
 }
 
-bool pgdat_reclaimable(struct pglist_data *pgdat)
-{
-	return node_page_state_snapshot(pgdat, NR_PAGES_SCANNED) <
-		pgdat_reclaimable_pages(pgdat) * 6;
-}
-
 /**
  * lruvec_lru_size -  Returns the number of pages on the given LRU list.
  * @lruvec: lru vector
@@ -1495,7 +1489,7 @@
  *
  * Appropriate locks must be held before calling this function.
  *
- * @nr_to_scan:	The number of pages to look through on the list.
+ * @nr_to_scan:	The number of eligible pages to look through on the list.
  * @lruvec:	The LRU vector to pull pages from.
  * @dst:	The temp list to put pages on to.
  * @nr_scanned:	The number of pages that were scanned.
@@ -1514,11 +1508,14 @@
 	unsigned long nr_taken = 0;
 	unsigned long nr_zone_taken[MAX_NR_ZONES] = { 0 };
 	unsigned long nr_skipped[MAX_NR_ZONES] = { 0, };
-	unsigned long scan, nr_pages;
+	unsigned long skipped = 0;
+	unsigned long scan, total_scan, nr_pages;
 	LIST_HEAD(pages_skipped);
 
-	for (scan = 0; scan < nr_to_scan && nr_taken < nr_to_scan &&
-					!list_empty(src);) {
+	scan = 0;
+	for (total_scan = 0;
+	     scan < nr_to_scan && nr_taken < nr_to_scan && !list_empty(src);
+	     total_scan++) {
 		struct page *page;
 
 		page = lru_to_page(src);
@@ -1533,11 +1530,12 @@
 		}
 
 		/*
-		 * Account for scanned and skipped separetly to avoid the pgdat
-		 * being prematurely marked unreclaimable by pgdat_reclaimable.
+		 * Do not count skipped pages because that makes the function
+		 * return with no isolated pages if the LRU mostly contains
+		 * ineligible pages.  This causes the VM to not reclaim any
+		 * pages, triggering a premature OOM.
 		 */
 		scan++;
-
 		switch (__isolate_lru_page(page, mode)) {
 		case 0:
 			nr_pages = hpage_nr_pages(page);
@@ -1565,28 +1563,20 @@
 	 */
 	if (!list_empty(&pages_skipped)) {
 		int zid;
-		unsigned long total_skipped = 0;
 
+		list_splice(&pages_skipped, src);
 		for (zid = 0; zid < MAX_NR_ZONES; zid++) {
 			if (!nr_skipped[zid])
 				continue;
 
 			__count_zid_vm_events(PGSCAN_SKIP, zid, nr_skipped[zid]);
-			total_skipped += nr_skipped[zid];
+			skipped += nr_skipped[zid];
 		}
-
-		/*
-		 * Account skipped pages as a partial scan as the pgdat may be
-		 * close to unreclaimable. If the LRU list is empty, account
-		 * skipped pages as a full scan.
-		 */
-		scan += list_empty(src) ? total_skipped : total_skipped >> 2;
-
-		list_splice(&pages_skipped, src);
 	}
-	*nr_scanned = scan;
-	trace_mm_vmscan_lru_isolate(sc->reclaim_idx, sc->order, nr_to_scan, scan,
-				    nr_taken, mode, is_file_lru(lru));
+	*nr_scanned = total_scan;
+	trace_mm_vmscan_lru_isolate(sc->reclaim_idx, sc->order, nr_to_scan,
+					total_scan, skipped, nr_taken, mode,
+					is_file_lru(lru));
 	update_lru_sizes(lruvec, lru, nr_zone_taken);
 	return nr_taken;
 }
@@ -1849,7 +1839,6 @@
 	reclaim_stat->recent_scanned[file] += nr_taken;
 
 	if (global_reclaim(sc)) {
-		__mod_node_page_state(pgdat, NR_PAGES_SCANNED, nr_scanned);
 		if (current_is_kswapd())
 			__count_vm_events(PGSCAN_KSWAPD, nr_scanned);
 		else
@@ -2038,8 +2027,6 @@
 	__mod_node_page_state(pgdat, NR_ISOLATED_ANON + file, nr_taken);
 	reclaim_stat->recent_scanned[file] += nr_taken;
 
-	if (global_reclaim(sc))
-		__mod_node_page_state(pgdat, NR_PAGES_SCANNED, nr_scanned);
 	__count_vm_events(PGREFILL, nr_scanned);
 
 	spin_unlock_irq(&pgdat->lru_lock);
@@ -2199,30 +2186,8 @@
 	unsigned long anon_prio, file_prio;
 	enum scan_balance scan_balance;
 	unsigned long anon, file;
-	bool force_scan = false;
 	unsigned long ap, fp;
 	enum lru_list lru;
-	bool some_scanned;
-	int pass;
-
-	/*
-	 * If the zone or memcg is small, nr[l] can be 0.  This
-	 * results in no scanning on this priority and a potential
-	 * priority drop.  Global direct reclaim can go to the next
-	 * zone and tends to have no problems. Global kswapd is for
-	 * zone balancing and it needs to scan a minimum amount. When
-	 * reclaiming for a memcg, a priority drop can cause high
-	 * latencies, so it's better to scan a minimum amount there as
-	 * well.
-	 */
-	if (current_is_kswapd()) {
-		if (!pgdat_reclaimable(pgdat))
-			force_scan = true;
-		if (!mem_cgroup_online(memcg))
-			force_scan = true;
-	}
-	if (!global_reclaim(sc))
-		force_scan = true;
 
 	/* If we have no swap space, do not bother scanning anon pages. */
 	if (!sc->may_swap || mem_cgroup_get_nr_swap_pages(memcg) <= 0) {
@@ -2354,55 +2319,48 @@
 	fraction[1] = fp;
 	denominator = ap + fp + 1;
 out:
-	some_scanned = false;
-	/* Only use force_scan on second pass. */
-	for (pass = 0; !some_scanned && pass < 2; pass++) {
-		*lru_pages = 0;
-		for_each_evictable_lru(lru) {
-			int file = is_file_lru(lru);
-			unsigned long size;
-			unsigned long scan;
+	*lru_pages = 0;
+	for_each_evictable_lru(lru) {
+		int file = is_file_lru(lru);
+		unsigned long size;
+		unsigned long scan;
 
-			size = lruvec_lru_size(lruvec, lru, sc->reclaim_idx);
-			scan = size >> sc->priority;
+		size = lruvec_lru_size(lruvec, lru, sc->reclaim_idx);
+		scan = size >> sc->priority;
+		/*
+		 * If the cgroup's already been deleted, make sure to
+		 * scrape out the remaining cache.
+		 */
+		if (!scan && !mem_cgroup_online(memcg))
+			scan = min(size, SWAP_CLUSTER_MAX);
 
-			if (!scan && pass && force_scan)
-				scan = min(size, SWAP_CLUSTER_MAX);
-
-			switch (scan_balance) {
-			case SCAN_EQUAL:
-				/* Scan lists relative to size */
-				break;
-			case SCAN_FRACT:
-				/*
-				 * Scan types proportional to swappiness and
-				 * their relative recent reclaim efficiency.
-				 */
-				scan = div64_u64(scan * fraction[file],
-							denominator);
-				break;
-			case SCAN_FILE:
-			case SCAN_ANON:
-				/* Scan one type exclusively */
-				if ((scan_balance == SCAN_FILE) != file) {
-					size = 0;
-					scan = 0;
-				}
-				break;
-			default:
-				/* Look ma, no brain */
-				BUG();
-			}
-
-			*lru_pages += size;
-			nr[lru] = scan;
-
+		switch (scan_balance) {
+		case SCAN_EQUAL:
+			/* Scan lists relative to size */
+			break;
+		case SCAN_FRACT:
 			/*
-			 * Skip the second pass and don't force_scan,
-			 * if we found something to scan.
+			 * Scan types proportional to swappiness and
+			 * their relative recent reclaim efficiency.
 			 */
-			some_scanned |= !!scan;
+			scan = div64_u64(scan * fraction[file],
+					 denominator);
+			break;
+		case SCAN_FILE:
+		case SCAN_ANON:
+			/* Scan one type exclusively */
+			if ((scan_balance == SCAN_FILE) != file) {
+				size = 0;
+				scan = 0;
+			}
+			break;
+		default:
+			/* Look ma, no brain */
+			BUG();
 		}
+
+		*lru_pages += size;
+		nr[lru] = scan;
 	}
 }
 
@@ -2704,6 +2662,15 @@
 	} while (should_continue_reclaim(pgdat, sc->nr_reclaimed - nr_reclaimed,
 					 sc->nr_scanned - nr_scanned, sc));
 
+	/*
+	 * Kswapd gives up on balancing particular nodes after too
+	 * many failures to reclaim anything from them and goes to
+	 * sleep. On reclaim progress, reset the failure counter. A
+	 * successful direct reclaim run will revive a dormant kswapd.
+	 */
+	if (reclaimable)
+		pgdat->kswapd_failures = 0;
+
 	return reclaimable;
 }
 
@@ -2778,10 +2745,6 @@
 						 GFP_KERNEL | __GFP_HARDWALL))
 				continue;
 
-			if (sc->priority != DEF_PRIORITY &&
-			    !pgdat_reclaimable(zone->zone_pgdat))
-				continue;	/* Let kswapd poll it */
-
 			/*
 			 * If we already have plenty of memory free for
 			 * compaction in this zone, don't free any more.
@@ -2918,7 +2881,7 @@
 	return 0;
 }
 
-static bool pfmemalloc_watermark_ok(pg_data_t *pgdat)
+static bool allow_direct_reclaim(pg_data_t *pgdat)
 {
 	struct zone *zone;
 	unsigned long pfmemalloc_reserve = 0;
@@ -2926,10 +2889,15 @@
 	int i;
 	bool wmark_ok;
 
+	if (pgdat->kswapd_failures >= MAX_RECLAIM_RETRIES)
+		return true;
+
 	for (i = 0; i <= ZONE_NORMAL; i++) {
 		zone = &pgdat->node_zones[i];
-		if (!managed_zone(zone) ||
-		    pgdat_reclaimable_pages(pgdat) == 0)
+		if (!managed_zone(zone))
+			continue;
+
+		if (!zone_reclaimable_pages(zone))
 			continue;
 
 		pfmemalloc_reserve += min_wmark_pages(zone);
@@ -3006,7 +2974,7 @@
 
 		/* Throttle based on the first usable node */
 		pgdat = zone->zone_pgdat;
-		if (pfmemalloc_watermark_ok(pgdat))
+		if (allow_direct_reclaim(pgdat))
 			goto out;
 		break;
 	}
@@ -3028,14 +2996,14 @@
 	 */
 	if (!(gfp_mask & __GFP_FS)) {
 		wait_event_interruptible_timeout(pgdat->pfmemalloc_wait,
-			pfmemalloc_watermark_ok(pgdat), HZ);
+			allow_direct_reclaim(pgdat), HZ);
 
 		goto check_pending;
 	}
 
 	/* Throttle until kswapd wakes the process */
 	wait_event_killable(zone->zone_pgdat->pfmemalloc_wait,
-		pfmemalloc_watermark_ok(pgdat));
+		allow_direct_reclaim(pgdat));
 
 check_pending:
 	if (fatal_signal_pending(current))
@@ -3185,21 +3153,44 @@
 	} while (memcg);
 }
 
-static bool zone_balanced(struct zone *zone, int order, int classzone_idx)
+/*
+ * Returns true if there is an eligible zone balanced for the request order
+ * and classzone_idx
+ */
+static bool pgdat_balanced(pg_data_t *pgdat, int order, int classzone_idx)
 {
-	unsigned long mark = high_wmark_pages(zone);
+	int i;
+	unsigned long mark = -1;
+	struct zone *zone;
 
-	if (!zone_watermark_ok_safe(zone, order, mark, classzone_idx))
-		return false;
+	for (i = 0; i <= classzone_idx; i++) {
+		zone = pgdat->node_zones + i;
+
+		if (!managed_zone(zone))
+			continue;
+
+		mark = high_wmark_pages(zone);
+		if (zone_watermark_ok_safe(zone, order, mark, classzone_idx))
+			return true;
+	}
 
 	/*
-	 * If any eligible zone is balanced then the node is not considered
-	 * to be congested or dirty
+	 * If a node has no populated zone within classzone_idx, it does not
+	 * need balancing by definition. This can happen if a zone-restricted
+	 * allocation tries to wake a remote kswapd.
 	 */
-	clear_bit(PGDAT_CONGESTED, &zone->zone_pgdat->flags);
-	clear_bit(PGDAT_DIRTY, &zone->zone_pgdat->flags);
+	if (mark == -1)
+		return true;
 
-	return true;
+	return false;
+}
+
+/* Clear pgdat state for congested, dirty or under writeback. */
+static void clear_pgdat_congested(pg_data_t *pgdat)
+{
+	clear_bit(PGDAT_CONGESTED, &pgdat->flags);
+	clear_bit(PGDAT_DIRTY, &pgdat->flags);
+	clear_bit(PGDAT_WRITEBACK, &pgdat->flags);
 }
 
 /*
@@ -3210,11 +3201,9 @@
  */
 static bool prepare_kswapd_sleep(pg_data_t *pgdat, int order, int classzone_idx)
 {
-	int i;
-
 	/*
 	 * The throttled processes are normally woken up in balance_pgdat() as
-	 * soon as pfmemalloc_watermark_ok() is true. But there is a potential
+	 * soon as allow_direct_reclaim() is true. But there is a potential
 	 * race between when kswapd checks the watermarks and a process gets
 	 * throttled. There is also a potential race if processes get
 	 * throttled, kswapd wakes, a large process exits thereby balancing the
@@ -3228,17 +3217,16 @@
 	if (waitqueue_active(&pgdat->pfmemalloc_wait))
 		wake_up_all(&pgdat->pfmemalloc_wait);
 
-	for (i = 0; i <= classzone_idx; i++) {
-		struct zone *zone = pgdat->node_zones + i;
+	/* Hopeless node, leave it to direct reclaim */
+	if (pgdat->kswapd_failures >= MAX_RECLAIM_RETRIES)
+		return true;
 
-		if (!managed_zone(zone))
-			continue;
-
-		if (!zone_balanced(zone, order, classzone_idx))
-			return false;
+	if (pgdat_balanced(pgdat, order, classzone_idx)) {
+		clear_pgdat_congested(pgdat);
+		return true;
 	}
 
-	return true;
+	return false;
 }
 
 /*
@@ -3314,9 +3302,9 @@
 	count_vm_event(PAGEOUTRUN);
 
 	do {
+		unsigned long nr_reclaimed = sc.nr_reclaimed;
 		bool raise_priority = true;
 
-		sc.nr_reclaimed = 0;
 		sc.reclaim_idx = classzone_idx;
 
 		/*
@@ -3341,23 +3329,12 @@
 		}
 
 		/*
-		 * Only reclaim if there are no eligible zones. Check from
-		 * high to low zone as allocations prefer higher zones.
-		 * Scanning from low to high zone would allow congestion to be
-		 * cleared during a very small window when a small low
-		 * zone was balanced even under extreme pressure when the
-		 * overall node may be congested. Note that sc.reclaim_idx
-		 * is not used as buffer_heads_over_limit may have adjusted
-		 * it.
+		 * Only reclaim if there are no eligible zones. Note that
+		 * sc.reclaim_idx is not used as buffer_heads_over_limit may
+		 * have adjusted it.
 		 */
-		for (i = classzone_idx; i >= 0; i--) {
-			zone = pgdat->node_zones + i;
-			if (!managed_zone(zone))
-				continue;
-
-			if (zone_balanced(zone, sc.order, classzone_idx))
-				goto out;
-		}
+		if (pgdat_balanced(pgdat, sc.order, classzone_idx))
+			goto out;
 
 		/*
 		 * Do some background aging of the anon list, to give
@@ -3371,7 +3348,7 @@
 		 * If we're getting trouble reclaiming, start doing writepage
 		 * even in laptop mode.
 		 */
-		if (sc.priority < DEF_PRIORITY - 2 || !pgdat_reclaimable(pgdat))
+		if (sc.priority < DEF_PRIORITY - 2)
 			sc.may_writepage = 1;
 
 		/* Call soft limit reclaim before calling shrink_node. */
@@ -3395,7 +3372,7 @@
 		 * able to safely make forward progress. Wake them
 		 */
 		if (waitqueue_active(&pgdat->pfmemalloc_wait) &&
-				pfmemalloc_watermark_ok(pgdat))
+				allow_direct_reclaim(pgdat))
 			wake_up_all(&pgdat->pfmemalloc_wait);
 
 		/* Check if kswapd should be suspending */
@@ -3406,10 +3383,14 @@
 		 * Raise priority if scanning rate is too low or there was no
 		 * progress in reclaiming pages
 		 */
-		if (raise_priority || !sc.nr_reclaimed)
+		nr_reclaimed = sc.nr_reclaimed - nr_reclaimed;
+		if (raise_priority || !nr_reclaimed)
 			sc.priority--;
 	} while (sc.priority >= 1);
 
+	if (!sc.nr_reclaimed)
+		pgdat->kswapd_failures++;
+
 out:
 	/*
 	 * Return the order kswapd stopped reclaiming at as
@@ -3420,6 +3401,22 @@
 	return sc.order;
 }
 
+/*
+ * pgdat->kswapd_classzone_idx is the highest zone index that a recent
+ * allocation request woke kswapd for. When kswapd has not woken recently,
+ * the value is MAX_NR_ZONES which is not a valid index. This compares a
+ * given classzone and returns it or the highest classzone index kswapd
+ * was recently woke for.
+ */
+static enum zone_type kswapd_classzone_idx(pg_data_t *pgdat,
+					   enum zone_type classzone_idx)
+{
+	if (pgdat->kswapd_classzone_idx == MAX_NR_ZONES)
+		return classzone_idx;
+
+	return max(pgdat->kswapd_classzone_idx, classzone_idx);
+}
+
 static void kswapd_try_to_sleep(pg_data_t *pgdat, int alloc_order, int reclaim_order,
 				unsigned int classzone_idx)
 {
@@ -3431,7 +3428,13 @@
 
 	prepare_to_wait(&pgdat->kswapd_wait, &wait, TASK_INTERRUPTIBLE);
 
-	/* Try to sleep for a short interval */
+	/*
+	 * Try to sleep for a short interval. Note that kcompactd will only be
+	 * woken if it is possible to sleep for a short interval. This is
+	 * deliberate on the assumption that if reclaim cannot keep an
+	 * eligible zone balanced that it's also unlikely that compaction will
+	 * succeed.
+	 */
 	if (prepare_kswapd_sleep(pgdat, reclaim_order, classzone_idx)) {
 		/*
 		 * Compaction records what page blocks it recently failed to
@@ -3455,7 +3458,7 @@
 		 * the previous request that slept prematurely.
 		 */
 		if (remaining) {
-			pgdat->kswapd_classzone_idx = max(pgdat->kswapd_classzone_idx, classzone_idx);
+			pgdat->kswapd_classzone_idx = kswapd_classzone_idx(pgdat, classzone_idx);
 			pgdat->kswapd_order = max(pgdat->kswapd_order, reclaim_order);
 		}
 
@@ -3509,7 +3512,8 @@
  */
 static int kswapd(void *p)
 {
-	unsigned int alloc_order, reclaim_order, classzone_idx;
+	unsigned int alloc_order, reclaim_order;
+	unsigned int classzone_idx = MAX_NR_ZONES - 1;
 	pg_data_t *pgdat = (pg_data_t*)p;
 	struct task_struct *tsk = current;
 
@@ -3539,20 +3543,23 @@
 	tsk->flags |= PF_MEMALLOC | PF_SWAPWRITE | PF_KSWAPD;
 	set_freezable();
 
-	pgdat->kswapd_order = alloc_order = reclaim_order = 0;
-	pgdat->kswapd_classzone_idx = classzone_idx = 0;
+	pgdat->kswapd_order = 0;
+	pgdat->kswapd_classzone_idx = MAX_NR_ZONES;
 	for ( ; ; ) {
 		bool ret;
 
+		alloc_order = reclaim_order = pgdat->kswapd_order;
+		classzone_idx = kswapd_classzone_idx(pgdat, classzone_idx);
+
 kswapd_try_sleep:
 		kswapd_try_to_sleep(pgdat, alloc_order, reclaim_order,
 					classzone_idx);
 
 		/* Read the new order and classzone_idx */
 		alloc_order = reclaim_order = pgdat->kswapd_order;
-		classzone_idx = pgdat->kswapd_classzone_idx;
+		classzone_idx = kswapd_classzone_idx(pgdat, 0);
 		pgdat->kswapd_order = 0;
-		pgdat->kswapd_classzone_idx = 0;
+		pgdat->kswapd_classzone_idx = MAX_NR_ZONES;
 
 		ret = try_to_freeze();
 		if (kthread_should_stop())
@@ -3578,9 +3585,6 @@
 		reclaim_order = balance_pgdat(pgdat, alloc_order, classzone_idx);
 		if (reclaim_order < alloc_order)
 			goto kswapd_try_sleep;
-
-		alloc_order = reclaim_order = pgdat->kswapd_order;
-		classzone_idx = pgdat->kswapd_classzone_idx;
 	}
 
 	tsk->flags &= ~(PF_MEMALLOC | PF_SWAPWRITE | PF_KSWAPD);
@@ -3596,7 +3600,6 @@
 void wakeup_kswapd(struct zone *zone, int order, enum zone_type classzone_idx)
 {
 	pg_data_t *pgdat;
-	int z;
 
 	if (!managed_zone(zone))
 		return;
@@ -3604,22 +3607,20 @@
 	if (!cpuset_zone_allowed(zone, GFP_KERNEL | __GFP_HARDWALL))
 		return;
 	pgdat = zone->zone_pgdat;
-	pgdat->kswapd_classzone_idx = max(pgdat->kswapd_classzone_idx, classzone_idx);
+	pgdat->kswapd_classzone_idx = kswapd_classzone_idx(pgdat,
+							   classzone_idx);
 	pgdat->kswapd_order = max(pgdat->kswapd_order, order);
 	if (!waitqueue_active(&pgdat->kswapd_wait))
 		return;
 
-	/* Only wake kswapd if all zones are unbalanced */
-	for (z = 0; z <= classzone_idx; z++) {
-		zone = pgdat->node_zones + z;
-		if (!managed_zone(zone))
-			continue;
+	/* Hopeless node, leave it to direct reclaim */
+	if (pgdat->kswapd_failures >= MAX_RECLAIM_RETRIES)
+		return;
 
-		if (zone_balanced(zone, order, classzone_idx))
-			return;
-	}
+	if (pgdat_balanced(pgdat, order, classzone_idx))
+		return;
 
-	trace_mm_vmscan_wakeup_kswapd(pgdat->node_id, zone_idx(zone), order);
+	trace_mm_vmscan_wakeup_kswapd(pgdat->node_id, classzone_idx, order);
 	wake_up_interruptible(&pgdat->kswapd_wait);
 }
 
@@ -3879,9 +3880,6 @@
 	    sum_zone_node_page_state(pgdat->node_id, NR_SLAB_RECLAIMABLE) <= pgdat->min_slab_pages)
 		return NODE_RECLAIM_FULL;
 
-	if (!pgdat_reclaimable(pgdat))
-		return NODE_RECLAIM_FULL;
-
 	/*
 	 * Do not scan if the allocation should not be delayed.
 	 */
diff --git a/mm/vmstat.c b/mm/vmstat.c
index 513c37a..2ab7973 100644
--- a/mm/vmstat.c
+++ b/mm/vmstat.c
@@ -954,7 +954,6 @@
 	"nr_unevictable",
 	"nr_isolated_anon",
 	"nr_isolated_file",
-	"nr_pages_scanned",
 	"workingset_refault",
 	"workingset_activate",
 	"workingset_nodereclaim",
@@ -1379,7 +1378,6 @@
 		   "\n        min      %lu"
 		   "\n        low      %lu"
 		   "\n        high     %lu"
-		   "\n   node_scanned  %lu"
 		   "\n        spanned  %lu"
 		   "\n        present  %lu"
 		   "\n        managed  %lu",
@@ -1387,7 +1385,6 @@
 		   min_wmark_pages(zone),
 		   low_wmark_pages(zone),
 		   high_wmark_pages(zone),
-		   node_page_state(zone->zone_pgdat, NR_PAGES_SCANNED),
 		   zone->spanned_pages,
 		   zone->present_pages,
 		   zone->managed_pages);
@@ -1426,7 +1423,7 @@
 		   "\n  node_unreclaimable:  %u"
 		   "\n  start_pfn:           %lu"
 		   "\n  node_inactive_ratio: %u",
-		   !pgdat_reclaimable(zone->zone_pgdat),
+		   pgdat->kswapd_failures >= MAX_RECLAIM_RETRIES,
 		   zone->zone_start_pfn,
 		   zone->zone_pgdat->inactive_ratio);
 	seq_putc(m, '\n');
@@ -1588,22 +1585,9 @@
 	for (i = 0; i < NR_VM_ZONE_STAT_ITEMS; i++) {
 		val = atomic_long_read(&vm_zone_stat[i]);
 		if (val < 0) {
-			switch (i) {
-			case NR_PAGES_SCANNED:
-				/*
-				 * This is often seen to go negative in
-				 * recent kernels, but not to go permanently
-				 * negative.  Whilst it would be nicer not to
-				 * have exceptions, rooting them out would be
-				 * another task, of rather low priority.
-				 */
-				break;
-			default:
-				pr_warn("%s: %s %ld\n",
-					__func__, vmstat_text[i], val);
-				err = -EINVAL;
-				break;
-			}
+			pr_warn("%s: %s %ld\n",
+				__func__, vmstat_text[i], val);
+			err = -EINVAL;
 		}
 	}
 	if (err)
diff --git a/net/core/dst.c b/net/core/dst.c
index 39cc119..b5de366 100644
--- a/net/core/dst.c
+++ b/net/core/dst.c
@@ -349,8 +349,15 @@
 
 	new = ((unsigned long) &dst_default_metrics) | DST_METRICS_READ_ONLY;
 	prev = cmpxchg(&dst->_metrics, old, new);
-	if (prev == old)
-		kfree(__DST_METRICS_PTR(old));
+	if (prev == old) {
+		struct dst_metrics *old_p = (struct dst_metrics *)
+					    __DST_METRICS_PTR(old);
+
+		if (prev & DST_METRICS_REFCOUNTED) {
+			if (atomic_dec_and_test(&old_p->refcnt))
+				kfree(old_p);
+		}
+	}
 }
 EXPORT_SYMBOL(__dst_destroy_metrics_generic);