Merge "PM / devfreq: memlat: Don't ignore extremely latency sensitive workloads"
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..f2cf941 100644
--- a/arch/arm64/boot/dts/qcom/Makefile
+++ b/arch/arm64/boot/dts/qcom/Makefile
@@ -69,15 +69,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/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..6e987f1 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 {
@@ -1024,31 +1048,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 +1080,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 +1116,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 +2103,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-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-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/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/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_gmu.c b/drivers/gpu/msm/kgsl_gmu.c
index 2ddb082..9b04543 100644
--- a/drivers/gpu/msm/kgsl_gmu.c
+++ b/drivers/gpu/msm/kgsl_gmu.c
@@ -1364,7 +1364,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);
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/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/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/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/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c
index 48e689f..aad7924 100644
--- a/drivers/power/supply/qcom/smb-lib.c
+++ b/drivers/power/supply/qcom/smb-lib.c
@@ -3642,6 +3642,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 +3695,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 +3782,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 +3936,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/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/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/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/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/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..ecd6c359 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
@@ -1514,11 +1508,12 @@
 	unsigned long nr_taken = 0;
 	unsigned long nr_zone_taken[MAX_NR_ZONES] = { 0 };
 	unsigned long nr_skipped[MAX_NR_ZONES] = { 0, };
+	unsigned long skipped = 0;
 	unsigned long scan, nr_pages;
 	LIST_HEAD(pages_skipped);
 
 	for (scan = 0; scan < nr_to_scan && nr_taken < nr_to_scan &&
-					!list_empty(src);) {
+					!list_empty(src); scan++) {
 		struct page *page;
 
 		page = lru_to_page(src);
@@ -1532,12 +1527,6 @@
 			continue;
 		}
 
-		/*
-		 * Account for scanned and skipped separetly to avoid the pgdat
-		 * being prematurely marked unreclaimable by pgdat_reclaimable.
-		 */
-		scan++;
-
 		switch (__isolate_lru_page(page, mode)) {
 		case 0:
 			nr_pages = hpage_nr_pages(page);
@@ -1565,28 +1554,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));
+	trace_mm_vmscan_lru_isolate(sc->reclaim_idx, sc->order, nr_to_scan,
+				    scan, skipped, nr_taken, mode,
+				    is_file_lru(lru));
 	update_lru_sizes(lruvec, lru, nr_zone_taken);
 	return nr_taken;
 }
@@ -1849,7 +1830,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 +2018,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 +2177,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 +2310,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 +2653,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 +2736,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 +2872,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 +2880,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 +2965,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 +2987,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))
@@ -3198,6 +3157,7 @@
 	 */
 	clear_bit(PGDAT_CONGESTED, &zone->zone_pgdat->flags);
 	clear_bit(PGDAT_DIRTY, &zone->zone_pgdat->flags);
+	clear_bit(PGDAT_WRITEBACK, &zone->zone_pgdat->flags);
 
 	return true;
 }
@@ -3214,7 +3174,7 @@
 
 	/*
 	 * 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 +3188,21 @@
 	if (waitqueue_active(&pgdat->pfmemalloc_wait))
 		wake_up_all(&pgdat->pfmemalloc_wait);
 
+	/* Hopeless node, leave it to direct reclaim */
+	if (pgdat->kswapd_failures >= MAX_RECLAIM_RETRIES)
+		return true;
+
 	for (i = 0; i <= classzone_idx; i++) {
 		struct zone *zone = pgdat->node_zones + i;
 
 		if (!managed_zone(zone))
 			continue;
 
-		if (!zone_balanced(zone, order, classzone_idx))
-			return false;
+		if (zone_balanced(zone, order, classzone_idx))
+			return true;
 	}
 
-	return true;
+	return false;
 }
 
 /*
@@ -3314,9 +3278,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;
 
 		/*
@@ -3371,7 +3335,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 +3359,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 +3370,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
@@ -3431,7 +3399,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
@@ -3609,6 +3583,10 @@
 	if (!waitqueue_active(&pgdat->kswapd_wait))
 		return;
 
+	/* Hopeless node, leave it to direct reclaim */
+	if (pgdat->kswapd_failures >= MAX_RECLAIM_RETRIES)
+		return;
+
 	/* Only wake kswapd if all zones are unbalanced */
 	for (z = 0; z <= classzone_idx; z++) {
 		zone = pgdat->node_zones + z;
@@ -3879,9 +3857,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)