Merge "defconfig: sdm845: Enable FBE config flag for ext4 FS"
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..4341e3a
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,27 @@
+cc_binary_host {
+    name: "unifdef",
+    srcs: ["scripts/unifdef.c"],
+    sanitize: {
+        never: true,
+    }
+}
+
+gensrcs {
+    name: "qseecom-kernel-includes",
+
+    // move to out/ as root for header generation because of scripts/unifdef
+    // storage - at the expense of extra ../ references
+    cmd: "pushd out && mkdir -p scripts && rm -f scripts/unifdef && ln -s ../../$(location unifdef) scripts/unifdef && ../$(location scripts/headers_install.sh) `dirname ../$(out)` ../ $(in) && popd",
+
+    tools: ["unifdef"],
+    tool_files: ["scripts/headers_install.sh"],
+    export_include_dirs: ["include/uapi"],
+    srcs: ["include/uapi/linux/qseecom.h"],
+    output_extension: "h",
+}
+
+cc_library_headers {
+    name: "qseecom-kernel-headers",
+    generated_headers: ["qseecom-kernel-includes"],
+    export_generated_headers: ["qseecom-kernel-includes"],
+}
diff --git a/Documentation/devicetree/bindings/arm/msm/lpm-workarounds.txt b/Documentation/devicetree/bindings/arm/msm/lpm-workarounds.txt
new file mode 100644
index 0000000..0304035
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/lpm-workarounds.txt
@@ -0,0 +1,55 @@
+* LPM Workarounds
+
+The required properties are:
+
+- compatible: "qcom,lpm-workarounds"
+
+The optional properties are:
+- reg: The physical address and the size of the l1_l2_gcc and l2_pwr_sts
+	regitsters of performance cluster.
+
+- reg-names: "l2_pwr_sts" - string to identify l2_pwr_sts physical address.
+	     "l1_l2_gcc" - string to identify l1_l2_gcc physical address.
+
+- qcom,lpm-wa-cx-turbo-unvote: Indicates the workaround to unvote CX turbo
+	vote when system is coming out of rpm assisted power collaspe.
+	lpm-cx-supply is required if this is present.
+
+- lpm-cx-supply:  will hold handle for CX regulator supply which is used
+	to unvote.
+
+- qcom,lpm-wa-skip-l2-spm: Due to a hardware bug on 8939 and 8909, secure
+	world needs to disable and enable L2 SPM to get the proper context
+	in secure watchdog bite cases. With this workaround there is a race
+	in programming L2 SPM between HLOS and secure world. This leads to
+	stability issues. To avoid this program L2 SPM only in secure world
+	based on the L2 mode flag passed. Set lpm-wa-skip-l2-spm node if this
+	is required.
+
+- qcom,lpm-wa-dynamic-clock-gating: Due to a hardware bug on 8952, L1/L2 dynamic
+	clock gating needs to be enabled by software for performance cluster
+	cores and L2. Set lpm-wa-dynamic-clock-gating node if this workaround is
+	required.
+
+- qcom,cpu-offline-mask: Dynamic clock gating should be enabled when cluster is
+	in L2 PC. Each bit of cpu-offline-mask lists the cpu no. to hotplug by KTM
+	driver.
+
+- qcom,non-boot-cpu-index: will hold index of non boot cluster cpu.
+
+- qcom,l1-l2-gcc-secure: indicates L1/L2 clock enabling register is secure.
+
+Example:
+
+qcom,lpm-workarounds {
+	compatible = "qcom,lpm-workarounds";
+	reg = <0x0B011018 0x4>,
+	      <0x0B011088 0x4>;
+	reg-names = "l2-pwr-sts", "l1-l2-gcc";
+	lpm-cx-supply = <&pm8916_s2_corner>;
+	qcom,lpm-wa-cx-turbo-unvote;
+	qcom,lpm-wa-skip-l2-spm;
+	qcom,lpm-wa-dynamic-clock-gating;
+	qcom,cpu-offline-mask = "0xF";
+	qcom,non-boot-cpu-index = <4>;
+}
diff --git a/Documentation/devicetree/bindings/arm/msm/pm.txt b/Documentation/devicetree/bindings/arm/msm/pm.txt
new file mode 100644
index 0000000..b66d4a6
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/pm.txt
@@ -0,0 +1,49 @@
+* MSM PM
+
+PM is the low power management device for MSM (Snapdragon class) chipsets.
+This device sets up different components to do low power modes and registers with
+the kernel to be notified of idle and suspend states and when called, follows
+through the set of instructions in putting the application cores to the lowest
+power mode possible.
+The PC debug counter reserves 16 registers in the IMEM memory space which maintains
+a count on the state of power collapse on each core. This count will be useful to
+debug the power collapse state on each core.
+
+The required properties for PM are:
+
+- compatible: "qcom,pm"
+
+The optional properties are:
+
+- reg: physical IMEM address reserved for PC counters and the size
+- qcom,use-sync-timer: Indicates whether the target uses the synchronized QTimer.
+- qcom,synced-clocks: Indicates that all cpus running off a single clock source and to
+	instantiate the necessary clock source.
+- qcom,pc-resets-timer: Indicates that the timer gets reset during power collapse.
+- qcom,tz-flushes-cache: Indicates that TZ flushes all of the cache during
+power collapse. MSM PM can decide to not perform cache flush operations to
+reduce latency associated with L2 PC.
+- qcom,saw-turns-off-pll: Indicates that the CPU's PLL can be managed from SAW
+	hardware. On such targets software management of PLL is not required. If
+	this property is specified then qcom,synced-clocks would be ignored.
+- qcom,no-pll-switch-for-retention: Boolean property, to indicate that the cpu
+	clock can be sourced even from the HFPLL even when the cpu is in
+	retention, and need not be switched to an always on pll. If this flag
+	is set then the cpu clock is not ramped down when entering retention or
+	ramped up on exiting retention.
+
+Example 1:
+
+qcom,pm@fe800664 {
+		compatible = "qcom,pm";
+		reg = <0xfe800664 0x40>;
+		qcom,use-sync-timer;
+	};
+
+Example 2:
+
+qcom,pm@fe800664 {
+		compatible = "qcom,pm";
+		reg = <0xfe800664 0x40>;
+		qcom,saw-turns-off-pll;
+	};
diff --git a/Documentation/devicetree/bindings/arm/msm/pm_snoc_client.txt b/Documentation/devicetree/bindings/arm/msm/pm_snoc_client.txt
new file mode 100644
index 0000000..4f7111f
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/pm_snoc_client.txt
@@ -0,0 +1,35 @@
+* MSM PM SNOC client
+
+MSM PM SNOC client device is used to setup a bus request for 100 Mhz for the
+SNOC bus when the Apps cores are active. This bus request helps mitigate the
+exit latency from power collapse in cases where there aren't any active bus
+requests for SNOC.
+
+This device is dependent on the pm-8x60 device, which configures the low power
+mode of respective cores.
+
+The required properties of this device are:
+
+- compatible: qcom,pm-snoc-client
+- qcom,msm-bus,name:            String representing the client-name
+- qcom,msm-bus,num-cases:       Total number of usecases
+- qcom,msm-bus,active-only:     Boolean context flag for requests in active or
+                                dual (active & sleep) contex
+- qcom,msm-bus,num-paths:       Total number of master-slave pairs
+- qcom,msm-bus,vectors-KBps:    Arrays of unsigned integers representing:
+                                master-id, slave-id, arbitrated bandwidth
+                                in KBps, instantaneous bandwidth in KBps
+
+
+Example:
+	qcom,pm-snoc-client {
+		compatible = "qcom,pm-snoc-client";
+		qcom,msm-bus,name = "ocimem_snoc";
+		qcom,msm-bus,num-cases = <2>;
+		qcom,msm-bus,active-only;
+		qcom,msm-bus,num-paths = <1>;
+		qcom,msm-bus,vectors =
+			<22 512 0 0>,
+			<22 512 320000 3200000>;
+	};
+
diff --git a/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.txt b/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.txt
index 4c29cda..fdc3418 100644
--- a/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.txt
+++ b/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.txt
@@ -57,6 +57,10 @@
   occupied by the redistributors. Required if more than one such
   region is present.
 
+- ignored-save-restore-irqs: Array of u32 elements, specifying the interrupts
+  which are ignored while doing gicd save/restore. Maximum of 10 elements
+  is supported at present.
+
 Sub-nodes:
 
 PPI affinity can be expressed as a single "ppi-partitions" node,
diff --git a/Documentation/devicetree/bindings/leds/leds-qpnp-haptics.txt b/Documentation/devicetree/bindings/leds/leds-qpnp-haptics.txt
index 1a76d5d..258504e 100644
--- a/Documentation/devicetree/bindings/leds/leds-qpnp-haptics.txt
+++ b/Documentation/devicetree/bindings/leds/leds-qpnp-haptics.txt
@@ -116,6 +116,11 @@
   Definition: Short circuit debounce cycles for internal PWM.
 		Allowed values: 0, 8, 16 or 32.
 
+- vcc_pon-supply
+  Usage:      optional
+  Value type: <phandle>
+  Definition: PON driver regulator required to force MBG_ON
+
 Following properties are specific only to LRA vibrators.
 
 - qcom,lra-auto-mode
diff --git a/Documentation/devicetree/bindings/platform/msm/ipa.txt b/Documentation/devicetree/bindings/platform/msm/ipa.txt
index 8b63075..917c2d0 100644
--- a/Documentation/devicetree/bindings/platform/msm/ipa.txt
+++ b/Documentation/devicetree/bindings/platform/msm/ipa.txt
@@ -118,6 +118,10 @@
     controller phandle and "clk_ipa_clk" as macro for "iface_clk"
 - clock-names: This property shall contain the clock input names used
     by driver in same order as the clocks property.This should be "iface_clk"
+- emulator-bar0-offset: Specifies the offset, within PCIe BAR0, where
+    IPA/GSI programmable registers reside.  This property is used only
+    with the IPA/GSI emulation system, which is connected to and
+    communicated with via PCIe.
 
 IPA SMMU sub nodes
 
diff --git a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg.txt
index f6a7a1b..1e44686 100644
--- a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg.txt
+++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg.txt
@@ -109,6 +109,10 @@
 					this. If this property is not specified,
 					low battery voltage threshold will be
 					configured to 4200 mV.
+- qcom,fg-rconn-mohm:			Battery connector resistance (Rconn) in
+					milliohms. If Rconn is specified, then
+					Rslow values will be updated to account
+					it for an accurate ESR.
 - qcom,cycle-counter-en:		Boolean property which enables the cycle
 					counter feature. If this property is
 					present, then the following properties
@@ -143,6 +147,14 @@
 					battery voltage shadow and the current
 					predicted voltage in uV to initiate
 					capacity learning.
+- qcom,cl-max-limit-deciperc:		The maximum percent that the capacity
+					cannot go above during any capacity
+					learning cycle. This property is in the
+					unit of .1% increments.
+- qcom,cl-min-limit-deciperc:		The minimum percent that the capacity
+					cannot go below during any capacity
+					learning cycle. This property is in the
+					unit of .1% increments.
 - qcom,capacity-estimation-on:		A boolean property to have the fuel
 					gauge driver attempt to estimate the
 					battery capacity using battery
@@ -178,6 +190,97 @@
 					settings will be different from default.
 					Once SOC crosses 5%, ESR pulse timings
 					will be restored back to default.
+- qcom,fg-control-slope-limiter:	A boolean property to specify if SOC
+					slope limiter coefficients needs to
+					be modified based on charging status
+					and battery temperature threshold.
+- qcom,fg-slope-limit-temp-threshold:	Temperature threshold in decidegC used
+					for applying the slope coefficient based
+					on charging status and battery
+					temperature. If this property is not
+					specified, a default value of 100 (10C)
+					will be applied by default.
+- qcom,fg-slope-limit-low-temp-chg:	When the temperature goes below the
+					specified temperature threshold and
+					battery is charging, slope coefficient
+					specified with this property will be
+					applied. If this property is not
+					specified, a default value of 45 will be
+					applied.
+- qcom,fg-slope-limit-low-temp-dischg:	Same as "qcom,fg-slope-limit-low-temp-chg"
+					except this is when the battery is
+					discharging.
+- qcom,fg-slope-limit-high-temp-chg:	When the temperature goes above the
+					specified temperature threshold and
+					battery is charging, slope coefficient
+					specified with this property will be
+					applied. If this property is not
+					specified, a default value of 2 will be
+					applied.
+- qcom,fg-slope-limit-high-temp-dischg:	Same as "qcom,fg-slope-limit-high-temp-chg"
+					except this is when the battery is
+					discharging.
+- qcom,fg-dischg-voltage-gain-ctrl:	A boolean property to specify if the
+					voltage gain needs to be modified
+					during discharging based on monotonic
+					soc.
+- qcom,fg-dischg-voltage-gain-soc:	Array of monotonic SOC threshold values
+					to change the voltage gain settings
+					during discharge. This should be defined
+					in the ascending order and in the range
+					of 0-100. Array limit is set to 3.
+					If qcom,fg-dischg-voltage-gain-ctrl is
+					set, then this property should be
+					specified to apply the gain settings.
+- qcom,fg-dischg-med-voltage-gain:	Array of voltage gain values that needs
+					to be applied to medC voltage gain when
+					the monotonic SOC goes below the SOC
+					threshold specified under
+					qcom,fg-dischg-voltage-gain-soc. Array
+					limit is set to 3.
+					If qcom,fg-dischg-voltage-gain-ctrl is
+					set, then this property should be
+					specified to apply the gain setting.
+- qcom,fg-dischg-high-voltage-gain:	Array of voltage gain values that needs
+					to be applied to highC voltage gain when
+					the monotonic SOC goes below the SOC
+					threshold specified under
+					qcom,fg-dischg-voltage-gain-soc. Array
+					limit is set to 3.
+					If qcom,fg-dischg-voltage-gain-ctrl is
+					set, then this property should be
+					specified to apply the gain setting.
+- qcom,fg-use-vbat-low-empty-soc:	A boolean property to specify whether
+					vbatt-low interrupt is used to handle
+					empty battery condition. If this is
+					not specified, empty battery condition
+					is detected by empty-soc interrupt.
+- qcom,fg-batt-temp-low-limit:		Battery temperature (in decidegC) low
+					limit  which will be used to validate
+					the battery temperature reading from FG.
+					If the battery temperature goes below
+					this limit, last read good temperature
+					will be notified to userspace. If this
+					limit is not specified, then the
+					default limit would be -60C.
+- qcom,fg-batt-temp-high-limit:		Battery temperature (in decidegC) high
+					limit  which will be used to validate
+					the battery temperature reading from FG.
+					If the battery temperature goes above
+					this limit, last read good temperature
+					will be notified to userspace. If this
+					limit is not specified, then the
+					default limit would be 150C.
+- qcom,fg-cc-soc-limit-pct:		Percentage of CC_SOC before resetting
+					FG and restore the full CC_SOC value.
+- qcom,fg-restore-batt-info:		A boolean property to specify whether
+					battery parameters needs to be
+					restored. If this feature is enabled,
+					then validating the battery parameters
+					by OCV/battery SOC, validation range
+					in percentage should be specified via
+					appropriate module parameters to make
+					it work properly.
 
 qcom,fg-soc node required properties:
 - reg : offset and length of the PMIC peripheral register map.
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-cdp-256.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-cdp-256.dtsi
index dafd0b8..1428f37 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-cdp-256.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-cdp-256.dtsi
@@ -18,7 +18,8 @@
 
 &usb {
 	status = "okay";
-	extcon = <&vbus_detect>;
+	qcom,connector-type-uAB;
+	extcon = <0>, <0>, <0>, <&vbus_detect>;
 };
 
 &pcie_ep {
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-cdp.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-cdp.dtsi
index 1428f37..81877b7 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-cdp.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-cdp.dtsi
@@ -16,12 +16,6 @@
 	status = "okay";
 };
 
-&usb {
-	status = "okay";
-	qcom,connector-type-uAB;
-	extcon = <0>, <0>, <0>, <&vbus_detect>;
-};
-
 &pcie_ep {
 	status = "okay";
 };
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-mtp-256.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-mtp-256.dtsi
index 6c5f3c3..d7c0d13 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-mtp-256.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-mtp-256.dtsi
@@ -18,7 +18,8 @@
 
 &usb {
 	status = "okay";
-	extcon = <&vbus_detect>;
+	qcom,connector-type-uAB;
+	extcon = <0>, <0>, <0>, <&vbus_detect>;
 };
 
 &pcie_ep {
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-mtp.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-mtp.dtsi
index d7c0d13..6ceac6e 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-mtp.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-pcie-ep-mtp.dtsi
@@ -16,12 +16,6 @@
 	status = "okay";
 };
 
-&usb {
-	status = "okay";
-	qcom,connector-type-uAB;
-	extcon = <0>, <0>, <0>, <&vbus_detect>;
-};
-
 &pcie_ep {
 	status = "okay";
 };
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
index 50bbebf..d2178cb 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
@@ -933,24 +933,24 @@
 			<1 676 0 0>,
 			<143 777 0 0>,
 		/* SVS2 */
-			<90 512 3616000 7232000>,
+			<90 512 900000 1800000>,
 			<90 585 300000 600000>,
-			<1 676 90000 180000>, /*gcc_config_noc_clk_src */
+			<1 676 90000 179000>, /*gcc_config_noc_clk_src */
 			<143 777 0 120>, /* IB defined for IPA2X_clk in MHz*/
 		/* SVS */
-			<90 512 6640000 13280000>,
+			<90 512 1530000 3060000>,
 			<90 585 400000 800000>,
-			<1 676 100000 200000>,
+			<1 676 100000 199000>,
 			<143 777 0 250>, /* IB defined for IPA2X_clk in MHz*/
 		/* NOMINAL */
-			<90 512 10400000 20800000>,
+			<90 512 2592000 5184000>,
 			<90 585 800000 1600000>,
-			<1 676 200000 400000>,
+			<1 676 200000 399000>,
 			<143 777 0 440>, /* IB defined for IPA2X_clk in MHz*/
 		/* TURBO */
-			<90 512 10400000 20800000>,
+			<90 512 2592000 5184000>,
 			<90 585 960000 1920000>,
-			<1 676 266000 532000>,
+			<1 676 266000 531000>,
 			<143 777 0 500>; /* IB defined for IPA clk in MHz*/
 		qcom,bus-vector-names = "MIN", "SVS2", "SVS", "NOMINAL",
 		"TURBO";
diff --git a/arch/arm/configs/msm8909-perf_defconfig b/arch/arm/configs/msm8909-perf_defconfig
index 9f5001f..6046ae6 100644
--- a/arch/arm/configs/msm8909-perf_defconfig
+++ b/arch/arm/configs/msm8909-perf_defconfig
@@ -345,11 +345,13 @@
 CONFIG_HID_MICROSOFT=y
 CONFIG_HID_MONTEREY=y
 CONFIG_HID_MULTITOUCH=y
-CONFIG_USB_DWC3=y
+CONFIG_USB=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_MON=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_MSM=y
 CONFIG_NOP_USB_XCEIV=y
 CONFIG_DUAL_ROLE_USB_INTF=y
-CONFIG_USB_MSM_SSPHY_QMP=y
-CONFIG_MSM_QUSB_PHY=y
 CONFIG_USB_GADGET=y
 CONFIG_USB_GADGET_DEBUG_FILES=y
 CONFIG_USB_GADGET_DEBUG_FS=y
diff --git a/arch/arm/configs/msm8909_defconfig b/arch/arm/configs/msm8909_defconfig
index c8087ad..67e7f06 100644
--- a/arch/arm/configs/msm8909_defconfig
+++ b/arch/arm/configs/msm8909_defconfig
@@ -340,11 +340,13 @@
 CONFIG_HID_MICROSOFT=y
 CONFIG_HID_MONTEREY=y
 CONFIG_HID_MULTITOUCH=y
-CONFIG_USB_DWC3=y
+CONFIG_USB=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_MON=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_MSM=y
 CONFIG_NOP_USB_XCEIV=y
 CONFIG_DUAL_ROLE_USB_INTF=y
-CONFIG_USB_MSM_SSPHY_QMP=y
-CONFIG_MSM_QUSB_PHY=y
 CONFIG_USB_GADGET=y
 CONFIG_USB_GADGET_DEBUG_FILES=y
 CONFIG_USB_GADGET_DEBUG_FS=y
diff --git a/arch/arm/configs/msm8909w_defconfig b/arch/arm/configs/msm8909w_defconfig
index af47269..17baa04 100644
--- a/arch/arm/configs/msm8909w_defconfig
+++ b/arch/arm/configs/msm8909w_defconfig
@@ -546,8 +546,13 @@
 CONFIG_PID_IN_CONTEXTIDR=y
 CONFIG_DEBUG_SET_MODULE_RONX=y
 CONFIG_CORESIGHT=y
+CONFIG_CORESIGHT_LINK_AND_SINK_TMC=y
+CONFIG_CORESIGHT_SINK_TPIU=y
+CONFIG_CORESIGHT_SOURCE_ETM3X=y
 CONFIG_CORESIGHT_REMOTE_ETM=y
 CONFIG_CORESIGHT_REMOTE_ETM_DEFAULT_ENABLE=0
+CONFIG_CORESIGHT_QCOM_REPLICATOR=y
+CONFIG_CORESIGHT_DBGUI=y
 CONFIG_CORESIGHT_STM=y
 CONFIG_CORESIGHT_TPDA=y
 CONFIG_CORESIGHT_TPDM=y
diff --git a/arch/arm/configs/msm8937-perf_defconfig b/arch/arm/configs/msm8937-perf_defconfig
index b113ebd..11308ba 100644
--- a/arch/arm/configs/msm8937-perf_defconfig
+++ b/arch/arm/configs/msm8937-perf_defconfig
@@ -306,6 +306,10 @@
 # CONFIG_INPUT_MOUSE is not set
 CONFIG_INPUT_JOYSTICK=y
 CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v26=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV_v26=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v26=y
+CONFIG_SECURE_TOUCH_SYNAPTICS_DSX_V26=y
 CONFIG_TOUCHSCREEN_FT5X06=y
 CONFIG_TOUCHSCREEN_GEN_VKEYS=y
 CONFIG_INPUT_MISC=y
@@ -354,11 +358,19 @@
 CONFIG_MSM_APM=y
 CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
 CONFIG_THERMAL=y
+CONFIG_THERMAL_WRITABLE_TRIPS=y
+CONFIG_THERMAL_GOV_USER_SPACE=y
+CONFIG_THERMAL_GOV_LOW_LIMITS=y
+CONFIG_CPU_THERMAL=y
+CONFIG_DEVFREQ_THERMAL=y
 CONFIG_THERMAL_QPNP=y
 CONFIG_THERMAL_QPNP_ADC_TM=y
 CONFIG_THERMAL_TSENS=y
-CONFIG_MSM_BCL_PERIPHERAL_CTL=y
-CONFIG_QTI_THERMAL_LIMITS_DCVS=y
+CONFIG_QTI_VIRTUAL_SENSOR=y
+CONFIG_QTI_QMI_COOLING_DEVICE=y
+CONFIG_REGULATOR_COOLING_DEVICE=y
+CONFIG_QTI_BCL_PMIC5=y
+CONFIG_QTI_BCL_SOC_DRIVER=y
 CONFIG_MFD_SPMI_PMIC=y
 CONFIG_REGULATOR=y
 CONFIG_REGULATOR_FIXED_VOLTAGE=y
diff --git a/arch/arm/configs/msm8937_defconfig b/arch/arm/configs/msm8937_defconfig
index 1cccfd3..2f7941f 100644
--- a/arch/arm/configs/msm8937_defconfig
+++ b/arch/arm/configs/msm8937_defconfig
@@ -311,6 +311,10 @@
 # CONFIG_INPUT_MOUSE is not set
 CONFIG_INPUT_JOYSTICK=y
 CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v26=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV_v26=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v26=y
+CONFIG_SECURE_TOUCH_SYNAPTICS_DSX_V26=y
 CONFIG_TOUCHSCREEN_FT5X06=y
 CONFIG_TOUCHSCREEN_GEN_VKEYS=y
 CONFIG_INPUT_MISC=y
@@ -361,11 +365,19 @@
 CONFIG_MSM_APM=y
 CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
 CONFIG_THERMAL=y
+CONFIG_THERMAL_WRITABLE_TRIPS=y
+CONFIG_THERMAL_GOV_USER_SPACE=y
+CONFIG_THERMAL_GOV_LOW_LIMITS=y
+CONFIG_CPU_THERMAL=y
+CONFIG_DEVFREQ_THERMAL=y
 CONFIG_THERMAL_QPNP=y
 CONFIG_THERMAL_QPNP_ADC_TM=y
 CONFIG_THERMAL_TSENS=y
-CONFIG_MSM_BCL_PERIPHERAL_CTL=y
-CONFIG_QTI_THERMAL_LIMITS_DCVS=y
+CONFIG_QTI_VIRTUAL_SENSOR=y
+CONFIG_QTI_QMI_COOLING_DEVICE=y
+CONFIG_REGULATOR_COOLING_DEVICE=y
+CONFIG_QTI_BCL_PMIC5=y
+CONFIG_QTI_BCL_SOC_DRIVER=y
 CONFIG_MFD_SPMI_PMIC=y
 CONFIG_REGULATOR=y
 CONFIG_REGULATOR_FIXED_VOLTAGE=y
diff --git a/arch/arm/configs/msm8953-batcam-perf_defconfig b/arch/arm/configs/msm8953-batcam-perf_defconfig
index 1610d29..a006ee1 100644
--- a/arch/arm/configs/msm8953-batcam-perf_defconfig
+++ b/arch/arm/configs/msm8953-batcam-perf_defconfig
@@ -141,11 +141,6 @@
 CONFIG_VIDEO_V4L2_SUBDEV_API=y
 CONFIG_V4L_PLATFORM_DRIVERS=y
 CONFIG_FB=y
-CONFIG_FB_MSM=y
-CONFIG_FB_MSM_MDSS=y
-CONFIG_FB_MSM_MDSS_WRITEBACK=y
-CONFIG_FB_MSM_MDSS_DSI_CTRL_STATUS=y
-CONFIG_FB_MSM_MDSS_XLOG_DEBUG=y
 CONFIG_SOUND=y
 CONFIG_SND=y
 CONFIG_SND_DYNAMIC_MINORS=y
@@ -177,6 +172,7 @@
 CONFIG_RTC_DRV_QPNP=y
 CONFIG_DMADEVICES=y
 CONFIG_QCOM_SPS_DMA=y
+CONFIG_SYNC_FILE=y
 CONFIG_UIO=y
 CONFIG_UIO_MSM_SHAREDMEM=y
 CONFIG_STAGING=y
@@ -190,7 +186,6 @@
 CONFIG_SPS_SUPPORT_NDP_BAM=y
 CONFIG_QPNP_COINCELL=y
 CONFIG_QPNP_REVID=y
-CONFIG_MSM_MDSS_PLL=y
 CONFIG_REMOTE_SPINLOCK_MSM=y
 CONFIG_MAILBOX=y
 CONFIG_ARM_SMMU=y
@@ -213,7 +208,6 @@
 CONFIG_MSM_SUBSYSTEM_RESTART=y
 CONFIG_MSM_PIL=y
 CONFIG_MSM_PIL_SSR_GENERIC=y
-CONFIG_MSM_PIL_MSS_QDSP6V5=y
 CONFIG_ICNSS=y
 CONFIG_MSM_PERFORMANCE=y
 CONFIG_MSM_EVENT_TIMER=y
diff --git a/arch/arm/configs/msm8953-batcam_defconfig b/arch/arm/configs/msm8953-batcam_defconfig
index 1ba9d96..4a0c147 100644
--- a/arch/arm/configs/msm8953-batcam_defconfig
+++ b/arch/arm/configs/msm8953-batcam_defconfig
@@ -142,11 +142,6 @@
 CONFIG_VIDEO_V4L2_SUBDEV_API=y
 CONFIG_V4L_PLATFORM_DRIVERS=y
 CONFIG_FB=y
-CONFIG_FB_MSM=y
-CONFIG_FB_MSM_MDSS=y
-CONFIG_FB_MSM_MDSS_WRITEBACK=y
-CONFIG_FB_MSM_MDSS_DSI_CTRL_STATUS=y
-CONFIG_FB_MSM_MDSS_XLOG_DEBUG=y
 CONFIG_SOUND=y
 CONFIG_SND=y
 CONFIG_SND_DYNAMIC_MINORS=y
@@ -178,6 +173,7 @@
 CONFIG_RTC_DRV_QPNP=y
 CONFIG_DMADEVICES=y
 CONFIG_QCOM_SPS_DMA=y
+CONFIG_SYNC_FILE=y
 CONFIG_UIO=y
 CONFIG_UIO_MSM_SHAREDMEM=y
 CONFIG_STAGING=y
@@ -191,7 +187,6 @@
 CONFIG_SPS_SUPPORT_NDP_BAM=y
 CONFIG_QPNP_COINCELL=y
 CONFIG_QPNP_REVID=y
-CONFIG_MSM_MDSS_PLL=y
 CONFIG_REMOTE_SPINLOCK_MSM=y
 CONFIG_MAILBOX=y
 CONFIG_ARM_SMMU=y
@@ -214,7 +209,6 @@
 CONFIG_MSM_SUBSYSTEM_RESTART=y
 CONFIG_MSM_PIL=y
 CONFIG_MSM_PIL_SSR_GENERIC=y
-CONFIG_MSM_PIL_MSS_QDSP6V5=y
 CONFIG_ICNSS=y
 CONFIG_MSM_PERFORMANCE=y
 CONFIG_MSM_EVENT_TIMER=y
diff --git a/arch/arm/configs/sdxpoorwills-perf_defconfig b/arch/arm/configs/sdxpoorwills-perf_defconfig
index 14c2a7c..04a1e5b 100644
--- a/arch/arm/configs/sdxpoorwills-perf_defconfig
+++ b/arch/arm/configs/sdxpoorwills-perf_defconfig
@@ -407,6 +407,7 @@
 CONFIG_PANIC_TIMEOUT=5
 # CONFIG_SCHED_DEBUG is not set
 CONFIG_SCHEDSTATS=y
+CONFIG_SCHED_STACK_END_CHECK=y
 # CONFIG_DEBUG_PREEMPT is not set
 CONFIG_IPC_LOGGING=y
 CONFIG_BLK_DEV_IO_TRACE=y
diff --git a/arch/arm/configs/sdxpoorwills_defconfig b/arch/arm/configs/sdxpoorwills_defconfig
index b259dc7..f2f5945 100644
--- a/arch/arm/configs/sdxpoorwills_defconfig
+++ b/arch/arm/configs/sdxpoorwills_defconfig
@@ -416,6 +416,7 @@
 CONFIG_PANIC_ON_RECURSIVE_FAULT=y
 CONFIG_PANIC_TIMEOUT=5
 CONFIG_SCHEDSTATS=y
+CONFIG_SCHED_STACK_END_CHECK=y
 CONFIG_DEBUG_SPINLOCK=y
 CONFIG_DEBUG_MUTEXES=y
 CONFIG_DEBUG_ATOMIC_SLEEP=y
diff --git a/arch/arm/include/asm/arch_gicv3.h b/arch/arm/include/asm/arch_gicv3.h
index a808829..4d1065c 100644
--- a/arch/arm/include/asm/arch_gicv3.h
+++ b/arch/arm/include/asm/arch_gicv3.h
@@ -242,6 +242,15 @@
 	writel_relaxed((u32)(val >> 32), addr + 4);
 }
 
+static inline u64 gic_read_irouter(const volatile void __iomem *addr)
+{
+	u64 val;
+
+	val = readl_relaxed(addr);
+	val |= (u64)readl_relaxed(addr + 4) << 32;
+	return val;
+}
+
 static inline u64 gic_read_typer(const volatile void __iomem *addr)
 {
 	u64 val;
diff --git a/arch/arm/mach-qcom/Kconfig b/arch/arm/mach-qcom/Kconfig
index b055b60..da48623 100644
--- a/arch/arm/mach-qcom/Kconfig
+++ b/arch/arm/mach-qcom/Kconfig
@@ -94,7 +94,7 @@
 	select MAY_HAVE_SPARSE_IRQ
 	select PINCTRL_MSM_TLMM
 	select USE_PINCTRL_IRQ
-	select MSM_PM if PM
+	select MSM_PM_LEGACY if PM
 	select MSM_RPM_SMD
 	select MSM_RPM_STATS_LOG
 	select MSM_RPM_LOG
diff --git a/arch/arm/mach-qcom/board-msm8953.c b/arch/arm/mach-qcom/board-msm8953.c
index de4538f..9a82e3a 100644
--- a/arch/arm/mach-qcom/board-msm8953.c
+++ b/arch/arm/mach-qcom/board-msm8953.c
@@ -37,8 +37,8 @@
 	/* Explicitly parent the /soc devices to the root node to preserve
 	 * the kernel ABI (sysfs structure, etc) until userspace is updated
 	 */
-	of_platform_populate(of_find_node_by_path("/soc"),
-			     of_default_bus_match_table, NULL, NULL);
+	return of_platform_populate(of_find_node_by_path("/soc"),
+				of_default_bus_match_table, NULL, NULL);
 }
 late_initcall(msm8953_dt_populate);
 #endif
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index b57aafc..f216025 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -2372,6 +2372,7 @@
 
 	mapping->nr_bitmaps = 1;
 	mapping->extensions = extensions;
+	mapping->bits = BITS_PER_BYTE * bitmap_size;
 
 	spin_lock_init(&mapping->lock);
 	mapping->ops = &iommu_ops;
diff --git a/arch/arm64/boot/dts/qcom/Makefile b/arch/arm64/boot/dts/qcom/Makefile
index 98238d9..4db459a 100644
--- a/arch/arm64/boot/dts/qcom/Makefile
+++ b/arch/arm64/boot/dts/qcom/Makefile
@@ -404,10 +404,10 @@
 	apq8053-iot-mtp.dtb \
 	apq8053-lite-dragon-v1.0.dtb \
 	apq8053-lite-dragon-v2.0.dtb \
-	apq8053-lite-lenovo-v1.0.dtb \
-	apq8053-lite-lenovo-v1.1.dtb \
-	apq8053-lite-harman-v1.0.dtb \
-	apq8053-lite-lge-v1.0.dtb \
+	apq8053-lite-dragon-v2.1.dtb \
+	apq8053-lite-dragon-v2.2.dtb \
+	apq8053-lite-dragon-v2.3.dtb \
+	apq8053-lite-dragon-v2.4.dtb \
 	msm8953-pmi8940-cdp.dtb \
 	msm8953-pmi8940-mtp.dtb \
 	msm8953-pmi8937-cdp.dtb \
diff --git a/arch/arm64/boot/dts/qcom/apq8009-robot-som-refboard.dts b/arch/arm64/boot/dts/qcom/apq8009-robot-som-refboard.dts
index 958f7c8..3e4cbca 100644
--- a/arch/arm64/boot/dts/qcom/apq8009-robot-som-refboard.dts
+++ b/arch/arm64/boot/dts/qcom/apq8009-robot-som-refboard.dts
@@ -186,3 +186,12 @@
 &blsp1_uart2_hs {
 	status = "ok";
 };
+
+&i2c_1 {
+	status = "okay";
+	vl53l0x@52 {
+		compatible = "st,stmvl53l0";
+		reg = <0x29>;
+		status = "ok";
+	};
+};
diff --git a/arch/arm64/boot/dts/qcom/apq8053-lite-lenovo-v1.0.dts b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.1.dts
similarity index 86%
rename from arch/arm64/boot/dts/qcom/apq8053-lite-lenovo-v1.0.dts
rename to arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.1.dts
index 325accf..6c9c266 100644
--- a/arch/arm64/boot/dts/qcom/apq8053-lite-lenovo-v1.0.dts
+++ b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.1.dts
@@ -13,10 +13,10 @@
 
 /dts-v1/;
 
-#include "apq8053-lite-lenovo-v1.0.dtsi"
+#include "apq8053-lite-dragon-v2.1.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. APQ8053 Lite Lenovo v1.0 Board";
+	model = "Qualcomm Technologies, Inc. APQ8053 Lite DragonBoard V2.1";
 	compatible = "qcom,apq8053-lite-dragonboard", "qcom,apq8053",
 			"qcom,dragonboard";
 	qcom,board-id= <0x01010020 0>;
diff --git a/arch/arm64/boot/dts/qcom/apq8053-lite-lenovo-v1.0.dtsi b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.1.dtsi
similarity index 100%
rename from arch/arm64/boot/dts/qcom/apq8053-lite-lenovo-v1.0.dtsi
rename to arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.1.dtsi
diff --git a/arch/arm64/boot/dts/qcom/apq8053-lite-lenovo-v1.1.dts b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.2.dts
similarity index 86%
rename from arch/arm64/boot/dts/qcom/apq8053-lite-lenovo-v1.1.dts
rename to arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.2.dts
index 0c7b557..ecc4fea 100644
--- a/arch/arm64/boot/dts/qcom/apq8053-lite-lenovo-v1.1.dts
+++ b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.2.dts
@@ -13,10 +13,10 @@
 
 /dts-v1/;
 
-#include "apq8053-lite-lenovo-v1.1.dtsi"
+#include "apq8053-lite-dragon-v2.2.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. APQ8053 Lite Lenovo v1.1 Board";
+	model = "Qualcomm Technologies, Inc. APQ8053 Lite DragonBoard V2.2";
 	compatible = "qcom,apq8053-lite-dragonboard", "qcom,apq8053",
 			"qcom,dragonboard";
 	qcom,board-id= <0x01010120 0>;
diff --git a/arch/arm64/boot/dts/qcom/apq8053-lite-lenovo-v1.1.dtsi b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.2.dtsi
similarity index 100%
rename from arch/arm64/boot/dts/qcom/apq8053-lite-lenovo-v1.1.dtsi
rename to arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.2.dtsi
diff --git a/arch/arm64/boot/dts/qcom/apq8053-lite-harman-v1.0.dts b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.3.dts
similarity index 86%
rename from arch/arm64/boot/dts/qcom/apq8053-lite-harman-v1.0.dts
rename to arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.3.dts
index 203b6b8..e3f80be 100644
--- a/arch/arm64/boot/dts/qcom/apq8053-lite-harman-v1.0.dts
+++ b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.3.dts
@@ -13,10 +13,10 @@
 
 /dts-v1/;
 
-#include "apq8053-lite-harman-v1.0.dtsi"
+#include "apq8053-lite-dragon-v2.3.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. APQ8053 Lite Harman v1.0 Board";
+	model = "Qualcomm Technologies, Inc. APQ8053 Lite DragonBoard V2.3";
 	compatible = "qcom,apq8053-lite-dragonboard", "qcom,apq8053",
 			"qcom,dragonboard";
 	qcom,board-id= <0x01020020 0>;
diff --git a/arch/arm64/boot/dts/qcom/apq8053-lite-harman-v1.0.dtsi b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.3.dtsi
similarity index 100%
rename from arch/arm64/boot/dts/qcom/apq8053-lite-harman-v1.0.dtsi
rename to arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.3.dtsi
diff --git a/arch/arm64/boot/dts/qcom/apq8053-lite-lge-v1.0.dts b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.4.dts
similarity index 86%
rename from arch/arm64/boot/dts/qcom/apq8053-lite-lge-v1.0.dts
rename to arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.4.dts
index 70952dc..1f40ef8 100644
--- a/arch/arm64/boot/dts/qcom/apq8053-lite-lge-v1.0.dts
+++ b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.4.dts
@@ -13,10 +13,10 @@
 
 /dts-v1/;
 
-#include "apq8053-lite-lge-v1.0.dtsi"
+#include "apq8053-lite-dragon-v2.4.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. APQ8053 Lite LGE v1.0 Board";
+	model = "Qualcomm Technologies, Inc. APQ8053 Lite DragonBoard V2.4";
 	compatible = "qcom,apq8053-lite-dragonboard", "qcom,apq8053",
 			"qcom,dragonboard";
 	qcom,board-id= <0x01030020 0>;
diff --git a/arch/arm64/boot/dts/qcom/apq8053-lite-lge-v1.0.dtsi b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.4.dtsi
similarity index 100%
rename from arch/arm64/boot/dts/qcom/apq8053-lite-lge-v1.0.dtsi
rename to arch/arm64/boot/dts/qcom/apq8053-lite-dragon-v2.4.dtsi
diff --git a/arch/arm64/boot/dts/qcom/msm8917-pinctrl.dtsi b/arch/arm64/boot/dts/qcom/msm8917-pinctrl.dtsi
index b9229e1..26fb25c 100644
--- a/arch/arm64/boot/dts/qcom/msm8917-pinctrl.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8917-pinctrl.dtsi
@@ -15,10 +15,11 @@
 	tlmm: pinctrl@1000000 {
 		compatible = "qcom,msm8917-pinctrl";
 		reg = <0x1000000 0x300000>;
-		interrupts = <0 208 0>;
+		interrupts-extended = <&wakegic GIC_SPI 208 IRQ_TYPE_NONE>;
 		gpio-controller;
 		#gpio-cells = <2>;
 		interrupt-controller;
+		interrupt-parent = <&wakegpio>;
 		#interrupt-cells = <2>;
 
 
diff --git a/arch/arm64/boot/dts/qcom/msm8917-pm.dtsi b/arch/arm64/boot/dts/qcom/msm8917-pm.dtsi
index 575d1b5..a3b4679 100644
--- a/arch/arm64/boot/dts/qcom/msm8917-pm.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8917-pm.dtsi
@@ -31,7 +31,6 @@
 
 	qcom,lpm-levels {
 		compatible = "qcom,lpm-levels";
-		qcom,use-psci;
 		#address-cells = <1>;
 		#size-cells = <0>;
 
@@ -40,8 +39,6 @@
 			#address-cells = <1>;
 			#size-cells = <0>;
 			label = "perf";
-			qcom,spm-device-names = "l2";
-			qcom,default-level=<0>;
 			qcom,psci-mode-shift = <4>;
 			qcom,psci-mode-mask = <0xf>;
 
@@ -98,11 +95,12 @@
 				#size-cells = <0>;
 				qcom,psci-mode-shift = <0>;
 				qcom,psci-mode-mask = <0xf>;
+				qcom,cpu = <&CPU0 &CPU1 &CPU2 &CPU3>;
 
 				qcom,pm-cpu-level@0 {
 					reg = <0>;
-					qcom,psci-cpu-mode = <0>;
 					label = "wfi";
+					qcom,psci-cpu-mode = <1>;
 					qcom,latency-us = <12>;
 					qcom,ss-power = <463>;
 					qcom,energy-overhead = <23520>;
@@ -111,8 +109,8 @@
 
 				qcom,pm-cpu-level@1 {
 					reg = <1>;
-					qcom,psci-cpu-mode = <3>;
 					label = "pc";
+					qcom,psci-cpu-mode = <3>;
 					qcom,latency-us = <180>;
 					qcom,ss-power = <429>;
 					qcom,energy-overhead = <162991>;
@@ -125,204 +123,10 @@
 		};
 	};
 
-	qcom,mpm@601d0 {
-		compatible = "qcom,mpm-v2";
-		reg = <0x601d0 0x1000>, /* MSM_RPM_MPM_BASE 4K */
-		      <0xb011008 0x4>;
-		reg-names = "vmpm", "ipc";
-		interrupts = <0 171 1>;
-		clocks = <&clock_gcc clk_xo_lpm_clk>;
-		clock-names = "xo";
-		qcom,ipc-bit-offset = <1>;
-		qcom,gic-parent = <&intc>;
-		qcom,gic-map = <2 216>, /* tsens_upper_lower_int */
-			<49 172>, /* usb1_hs_async_wakeup_irq */
-			<58 166>, /* usb_hs_irq */
-			<53 104>, /* mdss_irq */
-			<62 222>, /* ee0_krait_hlos_spmi_periph_irq */
-			<0xff 18>,  /* APC_qgicQTmrSecPhysIrptReq */
-			<0xff 19>,  /* APC_qgicQTmrNonSecPhysIrptReq */
-			<0xff 20>,  /* qgicQTmrVirtIrptReq */
-			<0xff 35>,  /* WDT_barkInt */
-			<0xff 39>,  /* arch_mem_timer */
-			<0xff 40>,  /* qtmr_phy_irq[0] */
-			<0xff 47>,  /* rbif_irq[0] */
-			<0xff 56>,  /* q6_wdog_expired_irq */
-			<0xff 57>,  /* mss_to_apps_irq(0) */
-			<0xff 58>,  /* mss_to_apps_irq(1) */
-			<0xff 59>,  /* mss_to_apps_irq(2) */
-			<0xff 60>,  /* mss_to_apps_irq(3) */
-			<0xff 61>,  /* mss_a2_bam_irq */
-			<0xff 65>,  /* o_gc_sys_irq[0] */
-			<0xff 69>,  /* vbif_irpt */
-			<0xff 73>,  /* smmu_intr_bus[1] */
-			<0xff 74>,  /* smmu_bus_intr[2] */
-			<0xff 75>,  /* smmu_bus_intr[3] */
-			<0xff 76>,  /* venus_irq */
-			<0xff 78>,  /* smmu_bus_intr[5] */
-			<0xff 79>,  /* smmu_bus_intr[6] */
-			<0xff 85>,  /* smmu_bus_intr[31] */
-			<0xff 86>,  /* smmu_bus_intr[32] */
-			<0xff 90>,  /* smmu_bus_intr[33] */
-			<0xff 92>,  /* smmu_bus_intr[34] */
-			<0xff 93>,  /* smmu_bus_intr[35] */
-			<0xff 97>,  /* smmu_bus_intr[10] */
-			<0xff 102>, /* smmu_bus_intr[14] */
-			<0xff 108>, /* smmu_bus_intr[36] */
-			<0xff 109>, /* smmu_bus_intr[37] */
-			<0xff 112>, /* smmu_bus_intr[38] */
-			<0xff 114>, /* qdsd_intr_out */
-			<0xff 126>, /* smmu_bus_intr[39] */
-			<0xff 128>, /* blsp1_peripheral_irq[3] */
-			<0xff 129>, /* blsp1_peripheral_irq[4] */
-			<0xff 131>, /* qup_irq */
-			<0xff 136>, /* smmu_bus_intr[43] */
-			<0xff 137>, /* smmu_intr_bus[44] */
-			<0xff 138>, /* smmu_intr_bus[45] */
-			<0xff 140>, /* uart_dm_intr */
-			<0xff 141>, /* smmu_bus_intr[46] */
-			<0xff 142>, /* smmu_bus_intr[47] */
-			<0xff 143>, /* smmu_bus_intr[48] */
-			<0xff 144>, /* smmu_bus_intr[49] */
-			<0xff 145>, /* smmu_bus_intr[50] */
-			<0xff 146>, /* smmu_bus_intr[51] */
-			<0xff 147>, /* smmu_bus_intr[52] */
-			<0xff 148>, /* smmu_bus_intr[53] */
-			<0xff 149>, /* smmu_bus_intr[54] */
-			<0xff 150>, /* smmu_bus_intr[55] */
-			<0xff 151>, /* smmu_bus_intr[56] */
-			<0xff 152>, /* smmu_bus_intr[57] */
-			<0xff 153>, /* smmu_bus_intr[58] */
-			<0xff 155>, /* sdc1_irq(0) */
-			<0xff 157>, /* sdc2_irq(0) */
-			<0xff 167>, /* bam_irq(0) */
-			<0xff 170>, /* sdc1_pwr_cmd_irq */
-			<0xff 173>, /* o_wcss_apss_smd_hi */
-			<0xff 174>, /* o_wcss_apss_smd_med */
-			<0xff 175>, /* o_wcss_apss_smd_low */
-			<0xff 176>, /* o_wcss_apss_smsm_irq */
-			<0xff 177>, /* o_wcss_apss_wlan_data_xfer_done */
-			<0xff 178>, /* o_wcss_apss_wlan_rx_data_avail */
-			<0xff 179>, /* o_wcss_apss_asic_intr */
-			<0xff 181>, /* o_wcss_apss_wdog_bite_and_reset_rdy */
-			<0xff 188>, /* lpass_irq_out_apcs(0) */
-			<0xff 189>, /* lpass_irq_out_apcs(1) */
-			<0xff 190>, /* lpass_irq_out_apcs(2) */
-			<0xff 191>, /* lpass_irq_out_apcs(3) */
-			<0xff 192>, /* lpass_irq_out_apcs(4) */
-			<0xff 193>, /* lpass_irq_out_apcs(5) */
-			<0xff 194>, /* lpass_irq_out_apcs(6) */
-			<0xff 195>, /* lpass_irq_out_apcs(7) */
-			<0xff 196>, /* lpass_irq_out_apcs(8) */
-			<0xff 197>, /* lpass_irq_out_apcs(9) */
-			<0xff 198>, /* coresight-tmc-etr interrupt */
-			<0xff 200>, /* rpm_ipc(4) */
-			<0xff 201>, /* rpm_ipc(5) */
-			<0xff 202>, /* rpm_ipc(6) */
-			<0xff 203>, /* rpm_ipc(7) */
-			<0xff 204>, /* rpm_ipc(24) */
-			<0xff 205>, /* rpm_ipc(25) */
-			<0xff 206>, /* rpm_ipc(26) */
-			<0xff 207>, /* rpm_ipc(27) */
-			<0xff 215>, /* o_bimc_intr[0] */
-			<0xff 224>, /* SPDM interrupt */
-			<0xff 239>, /* crypto_bam_irq[1]*/
-			<0xff 240>, /* summary_irq_kpss */
-			<0xff 253>, /* sdcc_pwr_cmd_irq */
-			<0xff 260>, /* ipa_irq[0] */
-			<0xff 261>, /* ipa_irq[2] */
-			<0xff 262>, /* ipa_bam_irq[0] */
-			<0xff 263>, /* ipa_bam_irq[2] */
-			<0xff 269>, /* rpm_wdog_expired_irq */
-			<0xff 270>, /* blsp1_bam_irq[0] */
-			<0xff 272>, /* smmu_intr_bus[17] */
-			<0xff 273>, /* smmu_bus_intr[18] */
-			<0xff 274>, /* smmu_bus_intr[19] */
-			<0xff 275>, /* rpm_ipc(30) */
-			<0xff 276>, /* rpm_ipc(31) */
-			<0xff 277>, /* smmu_intr_bus[20] */
-			<0xff 285>, /* smmu_bus_intr[28] */
-			<0xff 286>, /* smmu_bus_intr[29] */
-			<0xff 287>, /* smmu_bus_intr[30] */
-			<0xff 321>, /* q6ss_irq_out(4) */
-			<0xff 322>, /* q6ss_irq_out(5) */
-			<0xff 323>, /* q6ss_irq_out(6) */
-			<0xff 325>, /* q6ss_wdog_exp_irq */
-			<0xff 344>; /* sdcc1ice */
-
-		qcom,gpio-parent = <&tlmm>;
-		qcom,gpio-map = <3  38 >,
-			<4  1 >,
-			<5  5 >,
-			<6  9 >,
-			<8  37>,
-			<9  36>,
-			<10  13>,
-			<11  35>,
-			<12  17>,
-			<13  21>,
-			<14  54>,
-			<15  34>,
-			<16  31>,
-			<17  58>,
-			<18  28>,
-			<19  42>,
-			<20  25>,
-			<21  12>,
-			<22  43>,
-			<23  44>,
-			<24  45>,
-			<25  46>,
-			<26  48>,
-			<27  65>,
-			<28  93>,
-			<29  97>,
-			<30  63>,
-			<31  70>,
-			<32  71>,
-			<33  72>,
-			<34  81>,
-			<35  126>,
-			<36  90>,
-			<37  128>,
-			<38  91>,
-			<39  41>,
-			<40  127>,
-			<41  86>,
-			<50  67>,
-			<51  73>,
-			<52  74>,
-			<53  62>,
-			<54  124>,
-			<55  61>,
-			<56  130>,
-			<57  59>,
-			<59  50>;
-	};
-
-	qcom,cpu-sleep-status {
-		compatible = "qcom,cpu-sleep-status";
-	};
-
-	qcom,rpm-log@29dc00 {
-		compatible = "qcom,rpm-log";
-		reg = <0x29dc00 0x4000>;
-		qcom,rpm-addr-phys = <0x200000>;
-		qcom,offset-version = <4>;
-		qcom,offset-page-buffer-addr = <36>;
-		qcom,offset-log-len = <40>;
-		qcom,offset-log-len-mask = <44>;
-		qcom,offset-page-indices = <56>;
-	};
-
 	qcom,rpm-stats@29dba0 {
 		compatible = "qcom,rpm-stats";
-		reg = <0x200000 0x1000>,
-		      <0x290014 0x4>,
-		      <0x29001c 0x4>;
-		reg-names = "phys_addr_base", "offset_addr",
-						"heap_phys_addrbase";
-		qcom,sleep-stats-version = <2>;
+		reg = <0x200000 0x1000>, <0x290014 0x4>;
+		reg-names = "phys_addr_base", "offset_addr";
 	};
 
 	qcom,rpm-master-stats@60150 {
diff --git a/arch/arm64/boot/dts/qcom/msm8917.dtsi b/arch/arm64/boot/dts/qcom/msm8917.dtsi
index a285110..d080539 100644
--- a/arch/arm64/boot/dts/qcom/msm8917.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8917.dtsi
@@ -21,7 +21,7 @@
 	model = "Qualcomm Technologies, Inc. MSM8917";
 	compatible = "qcom,msm8917";
 	qcom,msm-id = <303 0x0>, <308 0x0>, <309 0x0>;
-	interrupt-parent = <&intc>;
+	interrupt-parent = <&wakegic>;
 
 	chosen {
 		bootargs = "sched_enable_hmp=1";
@@ -197,19 +197,19 @@
 	};
 
 	wakegic: wake-gic {
-		compatible = "qcom,mpm-gic", "qcom,mpm-gic-msm8937";
+		compatible = "qcom,mpm-gic-msm8937", "qcom,mpm-gic";
 		interrupts = <GIC_SPI 171 IRQ_TYPE_EDGE_RISING>;
 		reg = <0x601d0 0x1000>,
 			<0xb011008 0x4>;  /* MSM_APCS_GCC_BASE 4K */
 		reg-names = "vmpm", "ipc";
-		qcom,num-mpm-irqs = <96>;
+		qcom,num-mpm-irqs = <64>;
 		interrupt-controller;
 		interrupt-parent = <&intc>;
 		#interrupt-cells = <3>;
 	};
 
 	wakegpio: wake-gpio {
-		compatible = "qcom,mpm-gpio", "qcom,mpm-gpio-msm8937";
+		compatible = "qcom,mpm-gpio-msm8937", "qcom,mpm-gpio";
 		interrupt-controller;
 		interrupt-parent = <&intc>;
 		#interrupt-cells = <2>;
diff --git a/arch/arm64/boot/dts/qcom/msm8937-mtp.dtsi b/arch/arm64/boot/dts/qcom/msm8937-mtp.dtsi
index 57823b8..90685e9 100644
--- a/arch/arm64/boot/dts/qcom/msm8937-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8937-mtp.dtsi
@@ -156,6 +156,39 @@
 	};
 };
 
+&pm8937_gpios {
+	nfc_clk {
+		nfc_clk_default: nfc_clk_default {
+			pins = "gpio5";
+			function = "normal";
+			input-enable;
+			power-source = <1>;
+		};
+	};
+};
+
+&i2c_5 { /* BLSP2 QUP1 (NFC) */
+	status = "ok";
+	nq@28 {
+		compatible = "qcom,nq-nci";
+		reg = <0x28>;
+		qcom,nq-irq = <&tlmm 17 0x00>;
+		qcom,nq-ven = <&tlmm 16 0x00>;
+		qcom,nq-firm = <&tlmm 130 0x00>;
+		qcom,nq-clkreq = <&pm8937_gpios 5 0x00>;
+		interrupt-parent = <&tlmm>;
+		qcom,clk-src = "BBCLK2";
+		interrupts = <17 0>;
+		interrupt-names = "nfc_irq";
+		pinctrl-names = "nfc_active", "nfc_suspend";
+		pinctrl-0 = <&nfc_int_active &nfc_disable_active
+						&nfc_clk_default>;
+		pinctrl-1 = <&nfc_int_suspend &nfc_disable_suspend>;
+		clocks = <&clock_gcc clk_bb_clk2_pin>;
+		clock-names = "ref_clk";
+	};
+};
+
 &thermal_zones {
 	quiet-therm-step {
 		status = "disabled";
diff --git a/arch/arm64/boot/dts/qcom/msm8953-mtp.dts b/arch/arm64/boot/dts/qcom/msm8953-mtp.dts
index 97c6db3..539ac59 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-mtp.dts
+++ b/arch/arm64/boot/dts/qcom/msm8953-mtp.dts
@@ -37,6 +37,13 @@
 	qcom,battery-data = <&mtp_batterydata>;
 };
 
+&pmi_haptic{
+	qcom,lra-auto-res-mode="qwd";
+	qcom,lra-high-z="opt1";
+	qcom,lra-res-cal-period = <0>;
+	qcom,wave-play-rate-us = <4165>;
+};
+
 &qpnp_smbcharger {
 	qcom,battery-data = <&mtp_batterydata>;
 	qcom,chg-led-sw-controls;
diff --git a/arch/arm64/boot/dts/qcom/pmi632.dtsi b/arch/arm64/boot/dts/qcom/pmi632.dtsi
index 0b1a50a..65390cb 100644
--- a/arch/arm64/boot/dts/qcom/pmi632.dtsi
+++ b/arch/arm64/boot/dts/qcom/pmi632.dtsi
@@ -315,6 +315,7 @@
 			dpdm-supply = <&qusb_phy>;
 			qcom,auto-recharge-soc = <98>;
 			qcom,chg-vadc = <&pmi632_vadc>;
+			qcom,flash-disable-soc = <10>;
 
 			qcom,thermal-mitigation
 				= <3000000 2500000 2000000 1500000
@@ -464,7 +465,6 @@
 						"ilim1-s1",
 						"ilim2-s2",
 						"vreg-ok";
-				qcom,flash-disable-soc = <10>;
 			};
 
 			smb5_vbus: qcom,smb5-vbus {
diff --git a/arch/arm64/boot/dts/qcom/pmi8950.dtsi b/arch/arm64/boot/dts/qcom/pmi8950.dtsi
index 8797ea8..fa93918 100644
--- a/arch/arm64/boot/dts/qcom/pmi8950.dtsi
+++ b/arch/arm64/boot/dts/qcom/pmi8950.dtsi
@@ -587,13 +587,15 @@
 			};
 		};
 
-		pmi_haptic: qcom,haptic@c000 {
+		pmi_haptic: qcom,haptics@c000 {
 			compatible = "qcom,qpnp-haptics";
 			reg = <0xc000 0x100>;
 			interrupts = <0x3 0xc0 0x0 IRQ_TYPE_EDGE_BOTH>,
 					<0x3 0xc0 0x1 IRQ_TYPE_EDGE_BOTH>;
 			interrupt-names = "hap-sc-irq", "hap-play-irq";
 			qcom,pmic-revid = <&pmi8950_revid>;
+			vcc_pon-supply = <&pon_perph_reg>;
+			qcom,int-pwm-freq-khz = <505>;
 			qcom,play-mode = "direct";
 			qcom,wave-play-rate-us = <5263>;
 			qcom,actuator-type = <0>;
diff --git a/arch/arm64/boot/dts/qcom/sda450-pmi632-mtp-s3.dts b/arch/arm64/boot/dts/qcom/sda450-pmi632-mtp-s3.dts
index c907977..f20c2ba 100644
--- a/arch/arm64/boot/dts/qcom/sda450-pmi632-mtp-s3.dts
+++ b/arch/arm64/boot/dts/qcom/sda450-pmi632-mtp-s3.dts
@@ -14,8 +14,8 @@
 /dts-v1/;
 
 #include "sda450.dtsi"
-#include "sdm450-pmi632-mtp-s3.dtsi"
 #include "sdm450-pmi632.dtsi"
+#include "sdm450-pmi632-mtp-s3.dtsi"
 
 / {
 	model = "Qualcomm Technologies, Inc. SDA450 + PMI632 MTP S3";
diff --git a/arch/arm64/boot/dts/qcom/sda845-svr.dtsi b/arch/arm64/boot/dts/qcom/sda845-svr.dtsi
index ce62781..ed2ad69 100644
--- a/arch/arm64/boot/dts/qcom/sda845-svr.dtsi
+++ b/arch/arm64/boot/dts/qcom/sda845-svr.dtsi
@@ -14,9 +14,12 @@
 #include "sdm845-pinctrl-overlay.dtsi"
 #include "sda845-svr-pinctrl-overlay.dtsi"
 #include "sdm845-camera-sensor-svr.dtsi"
-#include "smb1355.dtsi"
 #include <dt-bindings/clock/mdss-10nm-pll-clk.h>
 
+&qupv3_se10_i2c {
+#include "smb1355.dtsi"
+};
+
 &vendor {
 	bluetooth: bt_wcn3990 {
 		compatible = "qca,wcn3990";
diff --git a/arch/arm64/boot/dts/qcom/sdm439-cdp.dtsi b/arch/arm64/boot/dts/qcom/sdm439-cdp.dtsi
index fd66f4b..6ecd0dd 100644
--- a/arch/arm64/boot/dts/qcom/sdm439-cdp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm439-cdp.dtsi
@@ -239,6 +239,19 @@
 	qcom,mdss-dsi-bl-pmic-pwm-frequency = <100>;
 	qcom,mdss-dsi-bl-pmic-bank-select = <0>;
 	qcom,mdss-dsi-pwm-gpio = <&pm8953_gpios 8 0>;
+	qcom,esd-check-enabled;
+	qcom,mdss-dsi-panel-status-check-mode = "reg_read";
+	qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a];
+	qcom,mdss-dsi-panel-status-command-state = "dsi_lp_mode";
+	qcom,mdss-dsi-panel-status-value = <0x9d 0x9d 0x9d 0x9d>;
+	qcom,mdss-dsi-panel-on-check-value = <0x9d 0x9d 0x9d 0x9d>;
+	qcom,mdss-dsi-panel-status-read-length = <4>;
+	qcom,mdss-dsi-panel-max-error-count = <3>;
+	qcom,mdss-dsi-min-refresh-rate = <48>;
+	qcom,mdss-dsi-max-refresh-rate = <60>;
+	qcom,mdss-dsi-pan-enable-dynamic-fps;
+	qcom,mdss-dsi-pan-fps-update =
+		"dfps_immediate_porch_mode_vfp";
 };
 
 &dsi_nt35695b_truly_fhd_cmd {
diff --git a/arch/arm64/boot/dts/qcom/sdm439-mtp.dtsi b/arch/arm64/boot/dts/qcom/sdm439-mtp.dtsi
index 29e0d72..9a9603d 100644
--- a/arch/arm64/boot/dts/qcom/sdm439-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm439-mtp.dtsi
@@ -214,4 +214,74 @@
 	qcom,mdss-dsi-bl-pmic-pwm-frequency = <100>;
 	qcom,mdss-dsi-bl-pmic-bank-select = <0>;
 	qcom,mdss-dsi-pwm-gpio = <&pm8953_gpios 8 0>;
+	qcom,esd-check-enabled;
+	qcom,mdss-dsi-panel-status-check-mode = "reg_read";
+	qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a];
+	qcom,mdss-dsi-panel-status-command-state = "dsi_lp_mode";
+	qcom,mdss-dsi-panel-status-value = <0x9d 0x9d 0x9d 0x9d>;
+	qcom,mdss-dsi-panel-on-check-value = <0x9d 0x9d 0x9d 0x9d>;
+	qcom,mdss-dsi-panel-status-read-length = <4>;
+	qcom,mdss-dsi-panel-max-error-count = <3>;
+	qcom,mdss-dsi-min-refresh-rate = <48>;
+	qcom,mdss-dsi-max-refresh-rate = <60>;
+	qcom,mdss-dsi-pan-enable-dynamic-fps;
+	qcom,mdss-dsi-pan-fps-update =
+		"dfps_immediate_porch_mode_vfp";
+};
+
+&i2c_2 {
+#include "smb1355.dtsi"
+};
+
+&pmi632_gpios {
+	smb_en {
+		smb_en_default: smb_en_default {
+			pins = "gpio2";
+			function = "func1";
+			output-enable;
+		};
+	};
+};
+
+&tlmm {
+	smb_int_default: smb_int_default {
+		mux {
+			pins = "gpio59";
+			function = "gpio";
+		};
+		config {
+			pins = "gpio59";
+			drive-strength = <2>;
+			bias-pull-up;
+			input-enable;
+		};
+	};
+};
+
+&smb1355_0 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&smb_int_default
+			&smb_en_default>;
+	interrupt-parent = <&tlmm>;
+	interrupts = <59 IRQ_TYPE_LEVEL_LOW>;
+	smb1355_charger_0: qcom,smb1355-charger@1000 {
+		status ="ok";
+		/delete-property/ io-channels;
+		/delete-property/ io-channels-names;
+		qcom,parallel-mode = <1>;
+	};
+};
+
+&smb1355_1 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&smb_int_default
+			&smb_en_default>;
+	interrupt-parent = <&tlmm>;
+	interrupts = <59 IRQ_TYPE_LEVEL_LOW>;
+	smb1355_charger_1: qcom,smb1355-charger@1000 {
+		status ="ok";
+		/delete-property/ io-channels;
+		/delete-property/ io-channels-names;
+		qcom,parallel-mode = <1>;
+	};
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm439-pmi632.dtsi b/arch/arm64/boot/dts/qcom/sdm439-pmi632.dtsi
index bc2ba9f..68bf1d2 100644
--- a/arch/arm64/boot/dts/qcom/sdm439-pmi632.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm439-pmi632.dtsi
@@ -43,6 +43,27 @@
 	qcom,battery-data = <&mtp_batterydata>;
 };
 
+&pmi632_vadc {
+	pinctrl-names = "default";
+	pinctrl-0 = <&quiet_therm_default &smb_therm_default>;
+};
+
+&pmi632_gpios {
+	quiet_therm {
+		quiet_therm_default: quiet_therm_default {
+			pins = "gpio3";
+			bias-high-impedance;
+		};
+	};
+
+	smb_therm {
+		smb_therm_default: smb_therm_default {
+			pins = "gpio4";
+			bias-high-impedance;
+		};
+	};
+};
+
 &pm8953_typec {
 	status = "disabled";
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm439-qrd.dtsi b/arch/arm64/boot/dts/qcom/sdm439-qrd.dtsi
index 1979f4e..5662e17 100644
--- a/arch/arm64/boot/dts/qcom/sdm439-qrd.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm439-qrd.dtsi
@@ -311,4 +311,74 @@
 	qcom,mdss-dsi-bl-pmic-pwm-frequency = <100>;
 	qcom,mdss-dsi-bl-pmic-bank-select = <0>;
 	qcom,mdss-dsi-pwm-gpio = <&pm8953_gpios 8 0>;
+	qcom,esd-check-enabled;
+	qcom,mdss-dsi-panel-status-check-mode = "reg_read";
+	qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a];
+	qcom,mdss-dsi-panel-status-command-state = "dsi_lp_mode";
+	qcom,mdss-dsi-panel-status-value = <0x9d 0x9d 0x9d 0x9d>;
+	qcom,mdss-dsi-panel-on-check-value = <0x9d 0x9d 0x9d 0x9d>;
+	qcom,mdss-dsi-panel-status-read-length = <4>;
+	qcom,mdss-dsi-panel-max-error-count = <3>;
+	qcom,mdss-dsi-min-refresh-rate = <48>;
+	qcom,mdss-dsi-max-refresh-rate = <60>;
+	qcom,mdss-dsi-pan-enable-dynamic-fps;
+	qcom,mdss-dsi-pan-fps-update =
+		"dfps_immediate_porch_mode_vfp";
+};
+
+&i2c_2 {
+#include "smb1355.dtsi"
+};
+
+&pmi632_gpios {
+	smb_en {
+		smb_en_default: smb_en_default {
+			pins = "gpio2";
+			function = "func1";
+			output-enable;
+		};
+	};
+};
+
+&tlmm {
+	smb_int_default: smb_int_default {
+		mux {
+			pins = "gpio59";
+			function = "gpio";
+		};
+		config {
+			pins = "gpio59";
+			drive-strength = <2>;
+			bias-pull-up;
+			input-enable;
+		};
+	};
+};
+
+&smb1355_0 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&smb_int_default
+			&smb_en_default>;
+	interrupt-parent = <&tlmm>;
+	interrupts = <59 IRQ_TYPE_LEVEL_LOW>;
+	smb1355_charger_0: qcom,smb1355-charger@1000 {
+		status ="ok";
+		/delete-property/ io-channels;
+		/delete-property/ io-channels-names;
+		qcom,parallel-mode = <1>;
+	};
+};
+
+&smb1355_1 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&smb_int_default
+			&smb_en_default>;
+	interrupt-parent = <&tlmm>;
+	interrupts = <59 IRQ_TYPE_LEVEL_LOW>;
+	smb1355_charger_1: qcom,smb1355-charger@1000 {
+		status ="ok";
+		/delete-property/ io-channels;
+		/delete-property/ io-channels-names;
+		qcom,parallel-mode = <1>;
+	};
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm439-regulator.dtsi b/arch/arm64/boot/dts/qcom/sdm439-regulator.dtsi
index e2f2dea..4c4c4bd 100644
--- a/arch/arm64/boot/dts/qcom/sdm439-regulator.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm439-regulator.dtsi
@@ -479,7 +479,7 @@
 			<0      0       0>;
 
 		qcom,cpr-voltage-ceiling-override =
-			<(-1) (-1) 795000 795000 835000 910000 910000>;
+			<(-1) (-1) 810000 845000 885000 960000 960000>;
 
 		qcom,cpr-enable;
 	};
diff --git a/arch/arm64/boot/dts/qcom/sdm439.dtsi b/arch/arm64/boot/dts/qcom/sdm439.dtsi
index 0e4f666..0b01035 100644
--- a/arch/arm64/boot/dts/qcom/sdm439.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm439.dtsi
@@ -341,10 +341,10 @@
 	reg = <0x001a94400 0x400>,
 		<0x0184d074 0x8>;
 	reg-names = "pll_base", "gdsc_base";
-	/delete-property/ qcom,dsi-pll-ssc-en;
-	/delete-property/ qcom,dsi-pll-ssc-mode;
-	/delete-property/ qcom,ssc-frequency-hz;
-	/delete-property/ qcom,ssc-ppm;
+	qcom,dsi-pll-ssc-en;
+	qcom,dsi-pll-ssc-mode = "down-spread";
+	qcom,ssc-frequency-hz = <31500>;
+	qcom,ssc-ppm = <5000>;
 };
 
 &mdss_dsi1_pll {
@@ -352,10 +352,10 @@
 	reg = <0x001a96400 0x400>,
 		<0x0184d074 0x8>;
 	reg-names = "pll_base", "gdsc_base";
-	/delete-property/ qcom,dsi-pll-ssc-en;
-	/delete-property/ qcom,dsi-pll-ssc-mode;
-	/delete-property/ qcom,ssc-frequency-hz;
-	/delete-property/ qcom,ssc-ppm;
+	qcom,dsi-pll-ssc-en;
+	qcom,dsi-pll-ssc-mode = "down-spread";
+	qcom,ssc-frequency-hz = <31500>;
+	qcom,ssc-ppm = <5000>;
 };
 
 &mdss_dsi {
diff --git a/arch/arm64/boot/dts/qcom/sdm450-pmi632-mtp-s3.dts b/arch/arm64/boot/dts/qcom/sdm450-pmi632-mtp-s3.dts
index b9aadc1..b73b49a 100644
--- a/arch/arm64/boot/dts/qcom/sdm450-pmi632-mtp-s3.dts
+++ b/arch/arm64/boot/dts/qcom/sdm450-pmi632-mtp-s3.dts
@@ -14,8 +14,8 @@
 /dts-v1/;
 
 #include "sdm450.dtsi"
-#include "sdm450-pmi632-mtp-s3.dtsi"
 #include "sdm450-pmi632.dtsi"
+#include "sdm450-pmi632-mtp-s3.dtsi"
 
 / {
 	model = "Qualcomm Technologies, Inc. SDM450 + PMI632 MTP S3";
diff --git a/arch/arm64/boot/dts/qcom/sdm450-pmi632-mtp-s3.dtsi b/arch/arm64/boot/dts/qcom/sdm450-pmi632-mtp-s3.dtsi
index 64d9e64..265a86d 100644
--- a/arch/arm64/boot/dts/qcom/sdm450-pmi632-mtp-s3.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm450-pmi632-mtp-s3.dtsi
@@ -46,3 +46,60 @@
 	qcom,mdss-dsi-bl-pmic-bank-select = <0>;
 	qcom,mdss-dsi-pwm-gpio = <&pm8953_gpios 8 0>;
 };
+
+&i2c_2 {
+#include "smb1355.dtsi"
+};
+
+&pmi632_gpios {
+	smb_en {
+		smb_en_default: smb_en_default {
+			pins = "gpio2";
+			function = "func1";
+			output-enable;
+		};
+	};
+};
+
+&tlmm {
+	smb_int_default: smb_int_default {
+		mux {
+			pins = "gpio59";
+			function = "gpio";
+		};
+		config {
+			pins = "gpio59";
+			drive-strength = <2>;
+			bias-pull-up;
+			input-enable;
+		};
+	};
+};
+
+&smb1355_0 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&smb_int_default
+			&smb_en_default>;
+	interrupt-parent = <&tlmm>;
+	interrupts = <59 IRQ_TYPE_LEVEL_LOW>;
+	smb1355_charger_0: qcom,smb1355-charger@1000 {
+		status ="ok";
+		/delete-property/ io-channels;
+		/delete-property/ io-channels-names;
+		qcom,parallel-mode = <1>;
+	};
+};
+
+&smb1355_1 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&smb_int_default
+			&smb_en_default>;
+	interrupt-parent = <&tlmm>;
+	interrupts = <59 IRQ_TYPE_LEVEL_LOW>;
+	smb1355_charger_1: qcom,smb1355-charger@1000 {
+		status ="ok";
+		/delete-property/ io-channels;
+		/delete-property/ io-channels-names;
+		qcom,parallel-mode = <1>;
+	};
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm450-pmi632.dtsi b/arch/arm64/boot/dts/qcom/sdm450-pmi632.dtsi
index e09d637..6e39327 100644
--- a/arch/arm64/boot/dts/qcom/sdm450-pmi632.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm450-pmi632.dtsi
@@ -372,3 +372,24 @@
 		};
 	};
 };
+
+&tlmm {
+	pmx_mdss {
+		mdss_dsi_active: mdss_dsi_active {
+			 mux {
+				 pins = "gpio61";
+			 };
+			 config {
+				 pins = "gpio61";
+			 };
+		 };
+		mdss_dsi_suspend: mdss_dsi_suspend {
+			  mux {
+				  pins = "gpio61";
+			  };
+			  config {
+				  pins = "gpio61";
+			  };
+		  };
+	};
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm450-qrd-sku4.dtsi b/arch/arm64/boot/dts/qcom/sdm450-qrd-sku4.dtsi
index 386bd71..c5eff7b4 100644
--- a/arch/arm64/boot/dts/qcom/sdm450-qrd-sku4.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm450-qrd-sku4.dtsi
@@ -177,3 +177,59 @@
 	};
 };
 
+&i2c_2 {
+#include "smb1355.dtsi"
+};
+
+&pmi632_gpios {
+	smb_en {
+		smb_en_default: smb_en_default {
+			pins = "gpio2";
+			function = "func1";
+			output-enable;
+		};
+	};
+};
+
+&tlmm {
+	smb_int_default: smb_int_default {
+		mux {
+			pins = "gpio59";
+			function = "gpio";
+		};
+		config {
+			pins = "gpio59";
+			drive-strength = <2>;
+			bias-pull-up;
+			input-enable;
+		};
+	};
+};
+
+&smb1355_0 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&smb_int_default
+			&smb_en_default>;
+	interrupt-parent = <&tlmm>;
+	interrupts = <59 IRQ_TYPE_LEVEL_LOW>;
+	smb1355_charger_0: qcom,smb1355-charger@1000 {
+		status ="ok";
+		/delete-property/ io-channels;
+		/delete-property/ io-channels-names;
+		qcom,parallel-mode = <1>;
+	};
+};
+
+&smb1355_1 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&smb_int_default
+			&smb_en_default>;
+	interrupt-parent = <&tlmm>;
+	interrupts = <59 IRQ_TYPE_LEVEL_LOW>;
+	smb1355_charger_1: qcom,smb1355-charger@1000 {
+		status ="ok";
+		/delete-property/ io-channels;
+		/delete-property/ io-channels-names;
+		qcom,parallel-mode = <1>;
+	};
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm632-cdp-s2.dts b/arch/arm64/boot/dts/qcom/sdm632-cdp-s2.dts
index 2669d1f..9d6543f 100644
--- a/arch/arm64/boot/dts/qcom/sdm632-cdp-s2.dts
+++ b/arch/arm64/boot/dts/qcom/sdm632-cdp-s2.dts
@@ -24,33 +24,3 @@
 	qcom,pmic-id = <0x010016 0x25 0x0 0x0>;
 };
 
-
-&soc {
-	gpio_keys {
-		/delete-node/home;
-	};
-};
-
-&tlmm {
-	tlmm_gpio_key {
-		gpio_key_active: gpio_key_active {
-			mux {
-				pins = "gpio85", "gpio86", "gpio87";
-			};
-
-			config {
-				pins = "gpio85", "gpio86", "gpio87";
-			};
-		};
-
-		gpio_key_suspend: gpio_key_suspend {
-			mux {
-				pins = "gpio85", "gpio86", "gpio87";
-			};
-
-			config {
-				pins = "gpio85", "gpio86", "gpio87";
-			};
-		};
-	};
-};
diff --git a/arch/arm64/boot/dts/qcom/sdm632-ext-codec-cdp-s3.dts b/arch/arm64/boot/dts/qcom/sdm632-ext-codec-cdp-s3.dts
index 17ae9d1..60b149d 100644
--- a/arch/arm64/boot/dts/qcom/sdm632-ext-codec-cdp-s3.dts
+++ b/arch/arm64/boot/dts/qcom/sdm632-ext-codec-cdp-s3.dts
@@ -24,34 +24,3 @@
 	qcom,pmic-id = <0x010016 0x25 0x0 0x0>;
 };
 
-
-&soc {
-	gpio_keys {
-		/delete-node/home;
-	};
-};
-
-&tlmm {
-	tlmm_gpio_key {
-		gpio_key_active: gpio_key_active {
-			mux {
-				pins = "gpio85", "gpio86", "gpio87";
-			};
-
-			config {
-				pins = "gpio85", "gpio86", "gpio87";
-			};
-		};
-
-		gpio_key_suspend: gpio_key_suspend {
-			mux {
-				pins = "gpio85", "gpio86", "gpio87";
-			};
-
-			config {
-				pins = "gpio85", "gpio86", "gpio87";
-			};
-		};
-	};
-};
-
diff --git a/arch/arm64/boot/dts/qcom/sdm632-mtp-s3.dts b/arch/arm64/boot/dts/qcom/sdm632-mtp-s3.dts
index 3662cf3..1dd1163 100644
--- a/arch/arm64/boot/dts/qcom/sdm632-mtp-s3.dts
+++ b/arch/arm64/boot/dts/qcom/sdm632-mtp-s3.dts
@@ -14,8 +14,8 @@
 /dts-v1/;
 
 #include "sdm632.dtsi"
-#include "sdm450-pmi632-mtp-s3.dtsi"
 #include "sdm450-pmi632.dtsi"
+#include "sdm450-pmi632-mtp-s3.dtsi"
 
 / {
 	model = "Qualcomm Technologies, Inc. SDM632 + PMI632 MTP S3";
diff --git a/arch/arm64/boot/dts/qcom/sdm632-pm8004-cdp-s2.dts b/arch/arm64/boot/dts/qcom/sdm632-pm8004-cdp-s2.dts
index 4d68901..e0e6b4b 100644
--- a/arch/arm64/boot/dts/qcom/sdm632-pm8004-cdp-s2.dts
+++ b/arch/arm64/boot/dts/qcom/sdm632-pm8004-cdp-s2.dts
@@ -25,34 +25,3 @@
 	qcom,pmic-id = <0x010016 0x25 0xC 0x0>;
 };
 
-
-&soc {
-	gpio_keys {
-		/delete-node/home;
-	};
-};
-
-&tlmm {
-	tlmm_gpio_key {
-		gpio_key_active: gpio_key_active {
-			mux {
-				pins = "gpio85", "gpio86", "gpio87";
-			};
-
-			config {
-				pins = "gpio85", "gpio86", "gpio87";
-			};
-		};
-
-		gpio_key_suspend: gpio_key_suspend {
-			mux {
-				pins = "gpio85", "gpio86", "gpio87";
-			};
-
-			config {
-				pins = "gpio85", "gpio86", "gpio87";
-			};
-		};
-	};
-};
-
diff --git a/arch/arm64/boot/dts/qcom/sdm632-pm8004-ext-codec-cdp-s3.dts b/arch/arm64/boot/dts/qcom/sdm632-pm8004-ext-codec-cdp-s3.dts
index 6ca2940..413e85f 100644
--- a/arch/arm64/boot/dts/qcom/sdm632-pm8004-ext-codec-cdp-s3.dts
+++ b/arch/arm64/boot/dts/qcom/sdm632-pm8004-ext-codec-cdp-s3.dts
@@ -25,34 +25,3 @@
 	qcom,pmic-id = <0x010016 0x25 0xC 0x0>;
 };
 
-
-&soc {
-	gpio_keys {
-		/delete-node/home;
-	};
-};
-
-&tlmm {
-	tlmm_gpio_key {
-		gpio_key_active: gpio_key_active {
-			mux {
-				pins = "gpio85", "gpio86", "gpio87";
-			};
-
-			config {
-				pins = "gpio85", "gpio86", "gpio87";
-			};
-		};
-
-		gpio_key_suspend: gpio_key_suspend {
-			mux {
-				pins = "gpio85", "gpio86", "gpio87";
-			};
-
-			config {
-				pins = "gpio85", "gpio86", "gpio87";
-			};
-		};
-	};
-};
-
diff --git a/arch/arm64/boot/dts/qcom/sdm632-pm8004-mtp-s3.dts b/arch/arm64/boot/dts/qcom/sdm632-pm8004-mtp-s3.dts
index d2a9cf1..aea6bff 100644
--- a/arch/arm64/boot/dts/qcom/sdm632-pm8004-mtp-s3.dts
+++ b/arch/arm64/boot/dts/qcom/sdm632-pm8004-mtp-s3.dts
@@ -14,8 +14,8 @@
 /dts-v1/;
 
 #include "sdm632.dtsi"
-#include "sdm450-pmi632-mtp-s3.dtsi"
 #include "sdm450-pmi632.dtsi"
+#include "sdm450-pmi632-mtp-s3.dtsi"
 #include "sdm632-pm8004.dtsi"
 
 / {
diff --git a/arch/arm64/boot/dts/qcom/sdm632-rcm.dts b/arch/arm64/boot/dts/qcom/sdm632-rcm.dts
index fe7ab38..68f0ea0 100644
--- a/arch/arm64/boot/dts/qcom/sdm632-rcm.dts
+++ b/arch/arm64/boot/dts/qcom/sdm632-rcm.dts
@@ -24,32 +24,3 @@
 	qcom,pmic-id = <0x010016 0x010011 0x0 0x0>;
 };
 
-&soc {
-	gpio_keys {
-		/delete-node/home;
-	};
-};
-
-&tlmm {
-	tlmm_gpio_key {
-		gpio_key_active: gpio_key_active {
-			mux {
-				pins = "gpio85", "gpio86", "gpio87";
-			};
-
-			config {
-				pins = "gpio85", "gpio86", "gpio87";
-			};
-		};
-
-		gpio_key_suspend: gpio_key_suspend {
-			mux {
-				pins = "gpio85", "gpio86", "gpio87";
-			};
-
-			config {
-				pins = "gpio85", "gpio86", "gpio87";
-			};
-		};
-	};
-};
diff --git a/arch/arm64/boot/dts/qcom/sdm632-rcm.dtsi b/arch/arm64/boot/dts/qcom/sdm632-rcm.dtsi
index 14ba3b4..aa20680 100644
--- a/arch/arm64/boot/dts/qcom/sdm632-rcm.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm632-rcm.dtsi
@@ -13,3 +13,34 @@
 
 #include "sdm450-pmi632-cdp-s2.dtsi"
 
+&soc {
+	gpio_keys {
+		home {
+			status = "disabled";
+		};
+	};
+};
+
+&tlmm {
+	tlmm_gpio_key {
+		gpio_key_active {
+			mux {
+				pins = "gpio85", "gpio86", "gpio87";
+			};
+
+			config {
+				pins = "gpio85", "gpio86", "gpio87";
+			};
+		};
+
+		gpio_key_suspend {
+			mux {
+				pins = "gpio85", "gpio86", "gpio87";
+			};
+
+			config {
+				pins = "gpio85", "gpio86", "gpio87";
+			};
+		};
+	};
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi b/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi
index 7764837..dbc3651 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi
@@ -14,7 +14,10 @@
 #include "sdm670-pmic-overlay.dtsi"
 #include "sdm670-sde-display.dtsi"
 #include "sdm670-camera-sensor-mtp.dtsi"
+
+&qupv3_se10_i2c {
 #include "smb1355.dtsi"
+};
 
 &ufsphy_mem {
 	compatible = "qcom,ufs-phy-qmp-v3";
diff --git a/arch/arm64/boot/dts/qcom/sdm670-qrd.dtsi b/arch/arm64/boot/dts/qcom/sdm670-qrd.dtsi
index 43f1465..3b8b375 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-qrd.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-qrd.dtsi
@@ -14,9 +14,12 @@
 #include "sdm670-camera-sensor-qrd.dtsi"
 #include "sdm670-pmic-overlay.dtsi"
 #include "sdm670-audio-overlay.dtsi"
-#include "smb1355.dtsi"
 #include "sdm670-sde-display.dtsi"
 
+&qupv3_se10_i2c {
+#include "smb1355.dtsi"
+};
+
 &qupv3_se9_2uart {
 	status = "disabled";
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm845-mtp.dtsi b/arch/arm64/boot/dts/qcom/sdm845-mtp.dtsi
index 349c4c0..812a313 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-mtp.dtsi
@@ -14,7 +14,10 @@
 #include "sdm845-pmic-overlay.dtsi"
 #include "sdm845-pinctrl-overlay.dtsi"
 #include "sdm845-camera-sensor-mtp.dtsi"
+
+&qupv3_se10_i2c {
 #include "smb1355.dtsi"
+};
 
 &vendor {
 	bluetooth: bt_wcn3990 {
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qrd.dtsi b/arch/arm64/boot/dts/qcom/sdm845-qrd.dtsi
index 6034b6d..f5a979c 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qrd.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-qrd.dtsi
@@ -13,9 +13,12 @@
 #include "sdm845-pmic-overlay.dtsi"
 #include "sdm845-pinctrl-overlay.dtsi"
 #include "sdm845-camera-sensor-qrd.dtsi"
-#include "smb1355.dtsi"
 #include <dt-bindings/gpio/gpio.h>
 
+&qupv3_se10_i2c {
+#include "smb1355.dtsi"
+};
+
 &vendor {
 	bluetooth: bt_wcn3990 {
 		compatible = "qca,wcn3990";
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dtsi b/arch/arm64/boot/dts/qcom/sdm845-qvr.dtsi
index a5c6ab5..b2b0000 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-qvr.dtsi
@@ -18,7 +18,10 @@
 
 #include "sdm845-pmic-overlay.dtsi"
 #include "sdm845-pinctrl-overlay.dtsi"
+
+&qupv3_se10_i2c {
 #include "smb1355.dtsi"
+};
 
 &vendor {
 	bluetooth: bt_wcn3990 {
diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
index e9a913f..6c71212 100644
--- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
@@ -776,6 +776,7 @@
 		      <0x17a60000 0x100000>;    /* GICR * 8 */
 		interrupts = <1 9 4>;
 		interrupt-parent = <&intc>;
+		ignored-save-restore-irqs = <38>;
 	};
 
 	pdc: interrupt-controller@b220000{
diff --git a/arch/arm64/boot/dts/qcom/smb1355.dtsi b/arch/arm64/boot/dts/qcom/smb1355.dtsi
index 3412b25d..5939440 100644
--- a/arch/arm64/boot/dts/qcom/smb1355.dtsi
+++ b/arch/arm64/boot/dts/qcom/smb1355.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, 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
@@ -12,98 +12,96 @@
 
 #include <dt-bindings/interrupt-controller/irq.h>
 
-&qupv3_se10_i2c {
-	smb1355_0: qcom,smb1355@8 {
-		compatible = "qcom,i2c-pmic";
-		reg = <0x8>;
-		#address-cells = <1>;
-		#size-cells = <0>;
-		interrupt-parent = <&spmi_bus>;
-		interrupts = <0x0 0xd1 0x0 IRQ_TYPE_LEVEL_LOW>;
-		interrupt_names = "smb1355_0";
-		interrupt-controller;
-		#interrupt-cells = <3>;
-		qcom,periph-map = <0x10 0x12 0x13 0x16>;
+smb1355_0: qcom,smb1355@8 {
+	compatible = "qcom,i2c-pmic";
+	reg = <0x8>;
+	#address-cells = <1>;
+	#size-cells = <0>;
+	interrupt-parent = <&spmi_bus>;
+	interrupts = <0x0 0xd1 0x0 IRQ_TYPE_LEVEL_LOW>;
+	interrupt_names = "smb1355_0";
+	interrupt-controller;
+	#interrupt-cells = <3>;
+	qcom,periph-map = <0x10 0x12 0x13 0x16>;
 
-		smb1355_revid_0: qcom,revid@100 {
-			compatible = "qcom,qpnp-revid";
-			reg = <0x100 0x100>;
-		};
-
-		smb1355_charger_0: qcom,smb1355-charger@1000 {
-			compatible = "qcom,smb1355";
-			qcom,pmic-revid = <&smb1355_revid_0>;
-			reg = <0x1000 0x700>;
-			#address-cells = <1>;
-			#size-cells = <1>;
-			interrupt-parent = <&smb1355_0>;
-			status = "disabled";
-
-			io-channels = <&pmi8998_rradc 2>,
-				      <&pmi8998_rradc 12>;
-			io-channel-names = "charger_temp",
-					   "charger_temp_max";
-
-			qcom,chgr@1000 {
-				reg = <0x1000 0x100>;
-				interrupts = <0x10 0x1 IRQ_TYPE_EDGE_RISING>;
-				interrupt-names = "chg-state-change";
-			};
-
-			qcom,chgr-misc@1600 {
-				reg = <0x1600 0x100>;
-				interrupts = <0x16 0x1 IRQ_TYPE_EDGE_RISING>,
-					     <0x16 0x6 IRQ_TYPE_EDGE_RISING>;
-				interrupt-names = "wdog-bark",
-						  "temperature-change";
-			};
-		};
+	smb1355_revid_0: qcom,revid@100 {
+		compatible = "qcom,qpnp-revid";
+		reg = <0x100 0x100>;
 	};
 
-	smb1355_1: qcom,smb1355@c {
-		compatible = "qcom,i2c-pmic";
-		reg = <0xc>;
+	smb1355_charger_0: qcom,smb1355-charger@1000 {
+		compatible = "qcom,smb1355";
+		qcom,pmic-revid = <&smb1355_revid_0>;
+		reg = <0x1000 0x700>;
 		#address-cells = <1>;
-		#size-cells = <0>;
-		interrupt-parent = <&spmi_bus>;
-		interrupts = <0x0 0xd1 0x0 IRQ_TYPE_LEVEL_LOW>;
-		interrupt_names = "smb1355_1";
-		interrupt-controller;
-		#interrupt-cells = <3>;
-		qcom,periph-map = <0x10 0x12 0x13 0x16>;
+		#size-cells = <1>;
+		interrupt-parent = <&smb1355_0>;
+		status = "disabled";
 
-		smb1355_revid_1: qcom,revid@100 {
-			compatible = "qcom,qpnp-revid";
-			reg = <0x100 0x100>;
+		io-channels = <&pmi8998_rradc 2>,
+			      <&pmi8998_rradc 12>;
+		io-channel-names = "charger_temp",
+				   "charger_temp_max";
+
+		qcom,chgr@1000 {
+			reg = <0x1000 0x100>;
+			interrupts = <0x10 0x1 IRQ_TYPE_EDGE_RISING>;
+			interrupt-names = "chg-state-change";
 		};
 
-		smb1355_charger_1: qcom,smb1355-charger@1000 {
-			compatible = "qcom,smb1355";
-			qcom,pmic-revid = <&smb1355_revid_1>;
-			reg = <0x1000 0x700>;
-			#address-cells = <1>;
-			#size-cells = <1>;
-			interrupt-parent = <&smb1355_1>;
-			status = "disabled";
+		qcom,chgr-misc@1600 {
+			reg = <0x1600 0x100>;
+			interrupts = <0x16 0x1 IRQ_TYPE_EDGE_RISING>,
+				     <0x16 0x6 IRQ_TYPE_EDGE_RISING>;
+			interrupt-names = "wdog-bark",
+					  "temperature-change";
+		};
+	};
+};
 
-			io-channels = <&pmi8998_rradc 2>,
-				      <&pmi8998_rradc 12>;
-			io-channel-names = "charger_temp",
-					   "charger_temp_max";
+smb1355_1: qcom,smb1355@c {
+	compatible = "qcom,i2c-pmic";
+	reg = <0xc>;
+	#address-cells = <1>;
+	#size-cells = <0>;
+	interrupt-parent = <&spmi_bus>;
+	interrupts = <0x0 0xd1 0x0 IRQ_TYPE_LEVEL_LOW>;
+	interrupt_names = "smb1355_1";
+	interrupt-controller;
+	#interrupt-cells = <3>;
+	qcom,periph-map = <0x10 0x12 0x13 0x16>;
 
-			qcom,chgr@1000 {
-				reg = <0x1000 0x100>;
-				interrupts = <0x10 0x1 IRQ_TYPE_EDGE_RISING>;
-				interrupt-names = "chg-state-change";
-			};
+	smb1355_revid_1: qcom,revid@100 {
+		compatible = "qcom,qpnp-revid";
+		reg = <0x100 0x100>;
+	};
 
-			qcom,chgr-misc@1600 {
-				reg = <0x1600 0x100>;
-				interrupts = <0x16 0x1 IRQ_TYPE_EDGE_RISING>,
-					     <0x16 0x6 IRQ_TYPE_EDGE_RISING>;
-				interrupt-names = "wdog-bark",
-						  "temperature-change";
-			};
+	smb1355_charger_1: qcom,smb1355-charger@1000 {
+		compatible = "qcom,smb1355";
+		qcom,pmic-revid = <&smb1355_revid_1>;
+		reg = <0x1000 0x700>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		interrupt-parent = <&smb1355_1>;
+		status = "disabled";
+
+		io-channels = <&pmi8998_rradc 2>,
+			      <&pmi8998_rradc 12>;
+		io-channel-names = "charger_temp",
+				   "charger_temp_max";
+
+		qcom,chgr@1000 {
+			reg = <0x1000 0x100>;
+			interrupts = <0x10 0x1 IRQ_TYPE_EDGE_RISING>;
+			interrupt-names = "chg-state-change";
+		};
+
+		qcom,chgr-misc@1600 {
+			reg = <0x1600 0x100>;
+			interrupts = <0x16 0x1 IRQ_TYPE_EDGE_RISING>,
+				     <0x16 0x6 IRQ_TYPE_EDGE_RISING>;
+			interrupt-names = "wdog-bark",
+					  "temperature-change";
 		};
 	};
 };
diff --git a/arch/arm64/configs/msm8937-perf_defconfig b/arch/arm64/configs/msm8937-perf_defconfig
index 1a31ee3..48d2bec 100644
--- a/arch/arm64/configs/msm8937-perf_defconfig
+++ b/arch/arm64/configs/msm8937-perf_defconfig
@@ -307,6 +307,10 @@
 # CONFIG_INPUT_MOUSE is not set
 CONFIG_INPUT_JOYSTICK=y
 CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v26=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV_v26=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v26=y
+CONFIG_SECURE_TOUCH_SYNAPTICS_DSX_V26=y
 CONFIG_INPUT_MISC=y
 CONFIG_INPUT_HBTP_INPUT=y
 CONFIG_INPUT_QPNP_POWER_ON=y
@@ -344,6 +348,7 @@
 CONFIG_POWER_RESET_SYSCON=y
 CONFIG_QPNP_FG=y
 CONFIG_SMB135X_CHARGER=y
+CONFIG_SMB1355_SLAVE_CHARGER=y
 CONFIG_SMB1351_USB_CHARGER=y
 CONFIG_QPNP_SMB5=y
 CONFIG_QPNP_SMBCHARGER=y
@@ -365,6 +370,7 @@
 CONFIG_REGULATOR_COOLING_DEVICE=y
 CONFIG_QTI_BCL_PMIC5=y
 CONFIG_QTI_BCL_SOC_DRIVER=y
+CONFIG_MFD_I2C_PMIC=y
 CONFIG_MFD_SPMI_PMIC=y
 CONFIG_REGULATOR=y
 CONFIG_REGULATOR_FIXED_VOLTAGE=y
@@ -575,6 +581,7 @@
 CONFIG_QCOM_DEVFREQ_DEVBW=y
 CONFIG_SPDM_SCM=y
 CONFIG_DEVFREQ_SPDM=y
+CONFIG_IIO=y
 CONFIG_PWM=y
 CONFIG_PWM_QPNP=y
 CONFIG_PWM_QTI_LPG=y
diff --git a/arch/arm64/configs/msm8937_defconfig b/arch/arm64/configs/msm8937_defconfig
index 2bcdc2c..acb4a25 100644
--- a/arch/arm64/configs/msm8937_defconfig
+++ b/arch/arm64/configs/msm8937_defconfig
@@ -313,6 +313,10 @@
 # CONFIG_INPUT_MOUSE is not set
 CONFIG_INPUT_JOYSTICK=y
 CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v26=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV_v26=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v26=y
+CONFIG_SECURE_TOUCH_SYNAPTICS_DSX_V26=y
 CONFIG_INPUT_MISC=y
 CONFIG_INPUT_HBTP_INPUT=y
 CONFIG_INPUT_QPNP_POWER_ON=y
@@ -352,6 +356,7 @@
 CONFIG_POWER_RESET_SYSCON=y
 CONFIG_QPNP_FG=y
 CONFIG_SMB135X_CHARGER=y
+CONFIG_SMB1355_SLAVE_CHARGER=y
 CONFIG_SMB1351_USB_CHARGER=y
 CONFIG_QPNP_SMB5=y
 CONFIG_QPNP_SMBCHARGER=y
@@ -373,6 +378,7 @@
 CONFIG_REGULATOR_COOLING_DEVICE=y
 CONFIG_QTI_BCL_PMIC5=y
 CONFIG_QTI_BCL_SOC_DRIVER=y
+CONFIG_MFD_I2C_PMIC=y
 CONFIG_MFD_SPMI_PMIC=y
 CONFIG_REGULATOR=y
 CONFIG_REGULATOR_FIXED_VOLTAGE=y
@@ -593,6 +599,7 @@
 CONFIG_QCOM_DEVFREQ_DEVBW=y
 CONFIG_SPDM_SCM=y
 CONFIG_DEVFREQ_SPDM=y
+CONFIG_IIO=y
 CONFIG_PWM=y
 CONFIG_PWM_QPNP=y
 CONFIG_PWM_QTI_LPG=y
diff --git a/arch/arm64/configs/msm8953-perf_defconfig b/arch/arm64/configs/msm8953-perf_defconfig
index 8fece0ee..cfbfdca 100644
--- a/arch/arm64/configs/msm8953-perf_defconfig
+++ b/arch/arm64/configs/msm8953-perf_defconfig
@@ -72,6 +72,7 @@
 CONFIG_SWP_EMULATION=y
 CONFIG_CP15_BARRIER_EMULATION=y
 CONFIG_SETEND_EMULATION=y
+CONFIG_ARM64_SW_TTBR0_PAN=y
 # CONFIG_ARM64_VHE is not set
 CONFIG_RANDOMIZE_BASE=y
 CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE=y
@@ -345,6 +346,7 @@
 CONFIG_POWER_RESET_SYSCON=y
 CONFIG_QPNP_FG=y
 CONFIG_SMB135X_CHARGER=y
+CONFIG_SMB1355_SLAVE_CHARGER=y
 CONFIG_SMB1351_USB_CHARGER=y
 CONFIG_QPNP_SMB5=y
 CONFIG_QPNP_SMBCHARGER=y
@@ -366,6 +368,7 @@
 CONFIG_REGULATOR_COOLING_DEVICE=y
 CONFIG_QTI_BCL_PMIC5=y
 CONFIG_QTI_BCL_SOC_DRIVER=y
+CONFIG_MFD_I2C_PMIC=y
 CONFIG_MFD_SPMI_PMIC=y
 CONFIG_REGULATOR=y
 CONFIG_REGULATOR_FIXED_VOLTAGE=y
@@ -579,12 +582,14 @@
 CONFIG_WCNSS_CORE=y
 CONFIG_WCNSS_CORE_PRONTO=y
 CONFIG_WCNSS_REGISTER_DUMP_ON_BITE=y
+CONFIG_BIG_CLUSTER_MIN_FREQ_ADJUST=y
 CONFIG_QCOM_BIMC_BWMON=y
 CONFIG_DEVFREQ_GOV_QCOM_BW_HWMON=y
 CONFIG_DEVFREQ_SIMPLE_DEV=y
 CONFIG_QCOM_DEVFREQ_DEVBW=y
 CONFIG_SPDM_SCM=y
 CONFIG_DEVFREQ_SPDM=y
+CONFIG_IIO=y
 CONFIG_PWM=y
 CONFIG_PWM_QPNP=y
 CONFIG_PWM_QTI_LPG=y
diff --git a/arch/arm64/configs/msm8953_defconfig b/arch/arm64/configs/msm8953_defconfig
index 0eb9df4..d006f30 100644
--- a/arch/arm64/configs/msm8953_defconfig
+++ b/arch/arm64/configs/msm8953_defconfig
@@ -75,6 +75,7 @@
 CONFIG_SWP_EMULATION=y
 CONFIG_CP15_BARRIER_EMULATION=y
 CONFIG_SETEND_EMULATION=y
+CONFIG_ARM64_SW_TTBR0_PAN=y
 # CONFIG_ARM64_VHE is not set
 CONFIG_RANDOMIZE_BASE=y
 CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE=y
@@ -354,6 +355,7 @@
 CONFIG_POWER_RESET_SYSCON=y
 CONFIG_QPNP_FG=y
 CONFIG_SMB135X_CHARGER=y
+CONFIG_SMB1355_SLAVE_CHARGER=y
 CONFIG_SMB1351_USB_CHARGER=y
 CONFIG_QPNP_SMB5=y
 CONFIG_QPNP_SMBCHARGER=y
@@ -375,6 +377,7 @@
 CONFIG_REGULATOR_COOLING_DEVICE=y
 CONFIG_QTI_BCL_PMIC5=y
 CONFIG_QTI_BCL_SOC_DRIVER=y
+CONFIG_MFD_I2C_PMIC=y
 CONFIG_MFD_SPMI_PMIC=y
 CONFIG_REGULATOR=y
 CONFIG_REGULATOR_FIXED_VOLTAGE=y
@@ -599,12 +602,14 @@
 CONFIG_WCNSS_CORE=y
 CONFIG_WCNSS_CORE_PRONTO=y
 CONFIG_WCNSS_REGISTER_DUMP_ON_BITE=y
+CONFIG_BIG_CLUSTER_MIN_FREQ_ADJUST=y
 CONFIG_QCOM_BIMC_BWMON=y
 CONFIG_DEVFREQ_GOV_QCOM_BW_HWMON=y
 CONFIG_DEVFREQ_SIMPLE_DEV=y
 CONFIG_QCOM_DEVFREQ_DEVBW=y
 CONFIG_SPDM_SCM=y
 CONFIG_DEVFREQ_SPDM=y
+CONFIG_IIO=y
 CONFIG_PWM=y
 CONFIG_PWM_QPNP=y
 CONFIG_PWM_QTI_LPG=y
diff --git a/arch/arm64/include/asm/arch_gicv3.h b/arch/arm64/include/asm/arch_gicv3.h
index c4cc771..f8b5c48 100644
--- a/arch/arm64/include/asm/arch_gicv3.h
+++ b/arch/arm64/include/asm/arch_gicv3.h
@@ -228,6 +228,7 @@
 }
 
 #define gic_read_typer(c)		readq_relaxed_no_log(c)
+#define gic_read_irouter(c)		readq_relaxed_no_log(c)
 #define gic_write_irouter(v, c)		writeq_relaxed_no_log(v, c)
 
 #endif /* __ASSEMBLY__ */
diff --git a/arch/arm64/kernel/module.c b/arch/arm64/kernel/module.c
index c9a2ab4..f035ff6 100644
--- a/arch/arm64/kernel/module.c
+++ b/arch/arm64/kernel/module.c
@@ -32,11 +32,16 @@
 
 void *module_alloc(unsigned long size)
 {
+	gfp_t gfp_mask = GFP_KERNEL;
 	void *p;
 
+	/* Silence the initial allocation */
+	if (IS_ENABLED(CONFIG_ARM64_MODULE_PLTS))
+		gfp_mask |= __GFP_NOWARN;
+
 	p = __vmalloc_node_range(size, MODULE_ALIGN, module_alloc_base,
 				module_alloc_base + MODULES_VSIZE,
-				GFP_KERNEL, PAGE_KERNEL_EXEC, 0,
+				gfp_mask, PAGE_KERNEL_EXEC, 0,
 				NUMA_NO_NODE, __builtin_return_address(0));
 
 	if (!p && IS_ENABLED(CONFIG_ARM64_MODULE_PLTS) &&
diff --git a/drivers/base/core.c b/drivers/base/core.c
index ac43d6f..b20d016 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -877,7 +877,7 @@
 	int error;
 
 	if (of_node) {
-		error = sysfs_create_link(&dev->kobj, &of_node->kobj,"of_node");
+		error = sysfs_create_link(&dev->kobj, of_node_kobj(of_node), "of_node");
 		if (error)
 			dev_warn(dev, "Error %d creating of_node link\n",error);
 		/* An error here doesn't warrant bringing down the device */
diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c
index 286418f..a089e7c 100644
--- a/drivers/char/diag/diag_dci.c
+++ b/drivers/char/diag/diag_dci.c
@@ -2713,7 +2713,7 @@
 	}
 }
 
-static int diag_dci_init_remote(void)
+int diag_dci_init_remote(void)
 {
 	int i;
 	struct dci_ops_tbl_t *temp = NULL;
@@ -2740,11 +2740,6 @@
 
 	return 0;
 }
-#else
-static int diag_dci_init_remote(void)
-{
-	return 0;
-}
 #endif
 
 static int diag_dci_init_ops_tbl(void)
@@ -2754,10 +2749,6 @@
 	err = diag_dci_init_local();
 	if (err)
 		goto err;
-	err = diag_dci_init_remote();
-	if (err)
-		goto err;
-
 	return 0;
 
 err:
diff --git a/drivers/char/diag/diag_dci.h b/drivers/char/diag/diag_dci.h
index 61eb3f5..2fb0e3f 100644
--- a/drivers/char/diag/diag_dci.h
+++ b/drivers/char/diag/diag_dci.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2016, 2018 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
@@ -323,6 +323,7 @@
 int diag_dci_write_bridge(int token, unsigned char *buf, int len);
 int diag_dci_write_done_bridge(int index, unsigned char *buf, int len);
 int diag_dci_send_handshake_pkt(int index);
+int diag_dci_init_remote(void);
 #endif
 
 #endif
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index 54a4d98..a169230 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -970,6 +970,8 @@
 	diagmem_setsize(POOL_TYPE_QSC_MUX, itemsize_qsc_usb,
 			poolsize_qsc_usb);
 	diag_md_mdm_init();
+	if (diag_dci_init_remote())
+		return -ENOMEM;
 	driver->hdlc_encode_buf = kzalloc(DIAG_MAX_HDLC_BUF_SIZE, GFP_KERNEL);
 	if (!driver->hdlc_encode_buf)
 		return -ENOMEM;
diff --git a/drivers/clk/msm/mdss/mdss-dsi-pll-12nm-util.c b/drivers/clk/msm/mdss/mdss-dsi-pll-12nm-util.c
index f2ed36c..0bdfeeb 100644
--- a/drivers/clk/msm/mdss/mdss-dsi-pll-12nm-util.c
+++ b/drivers/clk/msm/mdss/mdss-dsi-pll-12nm-util.c
@@ -165,7 +165,8 @@
 	return sel;
 }
 
-static bool pll_is_pll_locked_12nm(struct mdss_pll_resources *pll)
+static bool pll_is_pll_locked_12nm(struct mdss_pll_resources *pll,
+	bool is_handoff)
 {
 	u32 status;
 	bool pll_locked;
@@ -177,8 +178,9 @@
 			((status & BIT(1)) > 0),
 			DSI_PLL_POLL_MAX_READS,
 			DSI_PLL_POLL_TIMEOUT_US)) {
-		pr_err("DSI PLL ndx=%d status=%x failed to Lock\n",
-			pll->index, status);
+		if (!is_handoff)
+			pr_err("DSI PLL ndx=%d status=%x failed to Lock\n",
+				pll->index, status);
 		pll_locked = false;
 	} else {
 		pll_locked = true;
@@ -213,7 +215,7 @@
 	wmb(); /* make sure register committed before enabling branch clocks */
 	udelay(50); /* h/w recommended delay */
 
-	if (!pll_is_pll_locked_12nm(pll)) {
+	if (!pll_is_pll_locked_12nm(pll, false)) {
 		pr_err("DSI PLL ndx=%d lock failed!\n",
 			pll->index);
 		rc = -EINVAL;
@@ -261,7 +263,7 @@
 	MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_POWERUP_CTRL, data);
 	ndelay(500); /* h/w recommended delay */
 
-	if (!pll_is_pll_locked_12nm(pll)) {
+	if (!pll_is_pll_locked_12nm(pll, false)) {
 		pr_err("DSI PLL ndx=%d lock failed!\n",
 			pll->index);
 		rc = -EINVAL;
@@ -556,6 +558,142 @@
 	param->gmp_cntrl = 0x1;
 }
 
+static u32 __mdss_dsi_get_multi_intX100(u64 vco_rate, u32 *rem)
+{
+	u32 reminder = 0;
+	u64 temp = 0;
+	const u32 ref_clk_rate = 19200000, quarterX100 = 25;
+
+	temp = div_u64_rem(vco_rate, ref_clk_rate, &reminder);
+	temp *= 100;
+
+	/*
+	 * Multiplication integer needs to be floored in steps of 0.25
+	 * Hence multi_intX100 needs to be rounded off in steps of 25
+	 */
+	if (reminder < (ref_clk_rate / 4)) {
+		*rem = reminder;
+		return temp;
+	} else if ((reminder >= (ref_clk_rate / 4)) &&
+		reminder < (ref_clk_rate / 2)) {
+		*rem = (reminder - (ref_clk_rate / 4));
+		return (temp + quarterX100);
+	} else if ((reminder >= (ref_clk_rate / 2)) &&
+		(reminder < ((3 * ref_clk_rate) / 4))) {
+		*rem = (reminder - (ref_clk_rate / 2));
+		return (temp + (quarterX100 * 2));
+	}
+
+	*rem = (reminder - ((3 * ref_clk_rate) / 4));
+	return (temp + (quarterX100 * 3));
+}
+
+static u32 __calc_gcd(u32 num1, u32 num2)
+{
+	if (num2 != 0)
+		return __calc_gcd(num2, (num1 % num2));
+	else
+		return num1;
+}
+
+static void mdss_dsi_pll_12nm_calc_ssc(struct mdss_pll_resources *pll,
+					struct dsi_pll_db *pdb)
+{
+	struct dsi_pll_param *param = &pdb->param;
+	u64 multi_intX100 = 0, temp = 0;
+	u32 temp_rem1 = 0, temp_rem2 = 0;
+	const u64 power_2_17 = 131072, power_2_10 = 1024;
+	const u32 ref_clk_rate = 19200000;
+
+	multi_intX100 = __mdss_dsi_get_multi_intX100(pll->vco_current_rate,
+		&temp_rem1);
+
+	/* Calculation for mpll_ssc_peak_i */
+	temp = (multi_intX100 * pll->ssc_ppm * power_2_17);
+	temp = div_u64(temp, 100); /* 100 div for multi_intX100 */
+	param->mpll_ssc_peak_i =
+		(u32) div_u64(temp, 1000000); /*10^6 for SSC PPM */
+
+	/* Calculation for mpll_stepsize_i */
+	param->mpll_stepsize_i = (u32) div_u64((param->mpll_ssc_peak_i *
+		pll->ssc_freq * power_2_10), ref_clk_rate);
+
+	/* Calculation for mpll_mint_i */
+	param->mpll_mint_i = (u32) (div_u64((multi_intX100 * 4), 100) - 32);
+
+	/* Calculation for mpll_frac_den */
+	param->mpll_frac_den = (u32) div_u64(ref_clk_rate,
+		__calc_gcd((u32)pll->vco_current_rate, ref_clk_rate));
+
+	/* Calculation for mpll_frac_quot_i */
+	temp = (temp_rem1 * power_2_17);
+	param->mpll_frac_quot_i =
+		(u32) div_u64_rem(temp, ref_clk_rate, &temp_rem2);
+
+	/* Calculation for mpll_frac_rem */
+	param->mpll_frac_rem = (u32) div_u64(((u64)temp_rem2 *
+		param->mpll_frac_den), ref_clk_rate);
+
+	pr_debug("mpll_ssc_peak_i=%d mpll_stepsize_i=%d mpll_mint_i=%d\n",
+		param->mpll_ssc_peak_i, param->mpll_stepsize_i,
+		param->mpll_mint_i);
+	pr_debug("mpll_frac_den=%d mpll_frac_quot_i=%d mpll_frac_rem=%d",
+		param->mpll_frac_den, param->mpll_frac_quot_i,
+		param->mpll_frac_rem);
+}
+
+static void pll_db_commit_12nm_ssc(struct mdss_pll_resources *pll,
+					struct dsi_pll_db *pdb)
+{
+	void __iomem *pll_base = pll->pll_base;
+	struct dsi_pll_param *param = &pdb->param;
+	char data = 0;
+
+	MDSS_PLL_REG_W(pll_base, DSIPHY_SSC0, 0x27);
+
+	data = (param->mpll_mint_i & 0xff);
+	MDSS_PLL_REG_W(pll_base, DSIPHY_SSC7, data);
+
+	data = ((param->mpll_mint_i & 0xff00) >> 8);
+	MDSS_PLL_REG_W(pll_base, DSIPHY_SSC8, data);
+
+	data = (param->mpll_ssc_peak_i & 0xff);
+	MDSS_PLL_REG_W(pll_base, DSIPHY_SSC1, data);
+
+	data = ((param->mpll_ssc_peak_i & 0xff00) >> 8);
+	MDSS_PLL_REG_W(pll_base, DSIPHY_SSC2, data);
+
+	data = ((param->mpll_ssc_peak_i & 0xf0000) >> 16);
+	MDSS_PLL_REG_W(pll_base, DSIPHY_SSC3, data);
+
+	data = (param->mpll_stepsize_i & 0xff);
+	MDSS_PLL_REG_W(pll_base, DSIPHY_SSC4, data);
+
+	data = ((param->mpll_stepsize_i & 0xff00) >> 8);
+	MDSS_PLL_REG_W(pll_base, DSIPHY_SSC5, data);
+
+	data = ((param->mpll_stepsize_i & 0x1f0000) >> 16);
+	MDSS_PLL_REG_W(pll_base, DSIPHY_SSC6, data);
+
+	data = (param->mpll_frac_quot_i & 0xff);
+	MDSS_PLL_REG_W(pll_base, DSIPHY_SSC10, data);
+
+	data = ((param->mpll_frac_quot_i & 0xff00) >> 8);
+	MDSS_PLL_REG_W(pll_base, DSIPHY_SSC11, data);
+
+	data = (param->mpll_frac_rem & 0xff);
+	MDSS_PLL_REG_W(pll_base, DSIPHY_SSC12, data);
+
+	data = ((param->mpll_frac_rem & 0xff00) >> 8);
+	MDSS_PLL_REG_W(pll_base, DSIPHY_SSC13, data);
+
+	data = (param->mpll_frac_den & 0xff);
+	MDSS_PLL_REG_W(pll_base, DSIPHY_SSC14, data);
+
+	data = ((param->mpll_frac_den & 0xff00) >> 8);
+	MDSS_PLL_REG_W(pll_base, DSIPHY_SSC15, data);
+}
+
 static void pll_db_commit_12nm(struct mdss_pll_resources *pll,
 					struct dsi_pll_db *pdb)
 {
@@ -616,6 +754,9 @@
 	MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_PRO_DLY_RELOCK, 0x0c);
 	MDSS_PLL_REG_W(pll_base, DSIPHY_PLL_LOCK_DET_MODE_SEL, 0x02);
 
+	if (pll->ssc_en)
+		pll_db_commit_12nm_ssc(pll, pdb);
+
 	pr_debug("pll:%d\n", pll->index);
 	wmb(); /* make sure register committed before preparing the clocks */
 }
@@ -710,7 +851,7 @@
 		return ret;
 	}
 
-	if (pll_is_pll_locked_12nm(pll)) {
+	if (pll_is_pll_locked_12nm(pll, true)) {
 		pll->handoff_resources = true;
 		pll->pll_on = true;
 		c->rate = pll_vco_get_rate_12nm(c);
@@ -756,18 +897,27 @@
 					rc, pll->index);
 			goto error;
 		}
+	}
 
-		data = MDSS_PLL_REG_R(pll->pll_base, DSIPHY_SYS_CTRL);
-		if (data & BIT(7)) { /* DSI PHY in LP-11 or ULPS */
-			rc = dsi_pll_relock(pll);
-			if (rc)
-				goto error;
-			else
-				goto end;
-		}
+	/*
+	 * For cases where  DSI PHY is already enabled like:
+	 * 1.) LP-11 during static screen
+	 * 2.) ULPS during static screen
+	 * 3.) Boot up with cont splash enabled where PHY is programmed in LK
+	 * Execute the Re-lock sequence to enable the DSI PLL.
+	 */
+	data = MDSS_PLL_REG_R(pll->pll_base, DSIPHY_SYS_CTRL);
+	if (data & BIT(7)) {
+		rc = dsi_pll_relock(pll);
+		if (rc)
+			goto error;
+		else
+			goto end;
 	}
 
 	mdss_dsi_pll_12nm_calc_reg(pll, pdb);
+	if (pll->ssc_en)
+		mdss_dsi_pll_12nm_calc_ssc(pll, pdb);
 
 	/* commit DSI vco  */
 	pll_db_commit_12nm(pll, pdb);
@@ -802,6 +952,7 @@
 {
 	struct dsi_pll_vco_clk *vco = to_vco_clk(c);
 	struct mdss_pll_resources *pll = vco->priv;
+	u32 data = 0;
 
 	if (!pll) {
 		pr_err("Dsi pll resources are not available\n");
@@ -813,7 +964,9 @@
 		return -EINVAL;
 	}
 
-	MDSS_PLL_REG_W(pll->pll_base, DSIPHY_SSC0, 0x40);
+	data = MDSS_PLL_REG_R(pll->pll_base, DSIPHY_SSC0);
+	data |= BIT(6); /* enable GP_CLK_EN */
+	MDSS_PLL_REG_W(pll->pll_base, DSIPHY_SSC0, data);
 	wmb(); /* make sure register committed before enabling branch clocks */
 
 	return 0;
diff --git a/drivers/clk/msm/mdss/mdss-dsi-pll-12nm.c b/drivers/clk/msm/mdss/mdss-dsi-pll-12nm.c
index 210742b..5d2fa9a 100644
--- a/drivers/clk/msm/mdss/mdss-dsi-pll-12nm.c
+++ b/drivers/clk/msm/mdss/mdss-dsi-pll-12nm.c
@@ -636,11 +636,13 @@
 };
 
 static struct clk_lookup mdss_dsi_pllcc_12nm[] = {
+	CLK_LIST(dsi0pll_vco_clk),
 	CLK_LIST(dsi0pll_byte_clk_src),
 	CLK_LIST(dsi0pll_pixel_clk_src),
 };
 
 static struct clk_lookup mdss_dsi_pllcc_12nm_1[] = {
+	CLK_LIST(dsi1pll_vco_clk),
 	CLK_LIST(dsi1pll_byte_clk_src),
 	CLK_LIST(dsi1pll_pixel_clk_src),
 };
@@ -650,6 +652,9 @@
 {
 	int rc = 0, ndx;
 	struct dsi_pll_db *pdb;
+	int const ssc_freq_min = 30000; /* min. recommended freq. value */
+	int const ssc_freq_max = 33000; /* max. recommended freq. value */
+	int const ssc_ppm_max = 5000; /* max. recommended ppm */
 
 	if (!pdev || !pdev->dev.of_node) {
 		pr_err("Invalid input parameters\n");
@@ -680,6 +685,21 @@
 	pixel_div_clk_src_ops = clk_ops_div;
 	pixel_div_clk_src_ops.prepare = dsi_pll_div_prepare;
 
+	if (pll_res->ssc_en) {
+		if (!pll_res->ssc_freq || (pll_res->ssc_freq < ssc_freq_min) ||
+			(pll_res->ssc_freq > ssc_freq_max)) {
+			pll_res->ssc_freq = ssc_freq_min;
+			pr_debug("SSC frequency out of recommended range. Set to default=%d\n",
+				pll_res->ssc_freq);
+		}
+
+		if (!pll_res->ssc_ppm || (pll_res->ssc_ppm > ssc_ppm_max)) {
+			pll_res->ssc_ppm = ssc_ppm_max;
+			pr_debug("SSC PPM out of recommended range. Set to default=%d\n",
+				pll_res->ssc_ppm);
+		}
+	}
+
 	/* Set client data to mux, div and vco clocks.  */
 	if (pll_res->index == DSI_PLL_1) {
 		dsi1pll_byte_clk_src.priv = pll_res;
diff --git a/drivers/clk/msm/mdss/mdss-dsi-pll-12nm.h b/drivers/clk/msm/mdss/mdss-dsi-pll-12nm.h
index 6912ff4..0974717 100644
--- a/drivers/clk/msm/mdss/mdss-dsi-pll-12nm.h
+++ b/drivers/clk/msm/mdss/mdss-dsi-pll-12nm.h
@@ -37,6 +37,22 @@
 #define DSIPHY_PLL_LOOP_DIV_RATIO_1			0x2e8
 #define DSIPHY_SLEWRATE_DDL_CYC_FRQ_ADJ_1		0x328
 #define DSIPHY_SSC0					0x394
+#define DSIPHY_SSC7					0x3b0
+#define DSIPHY_SSC8					0x3b4
+#define DSIPHY_SSC1					0x398
+#define DSIPHY_SSC2					0x39c
+#define DSIPHY_SSC3					0x3a0
+#define DSIPHY_SSC4					0x3a4
+#define DSIPHY_SSC5					0x3a8
+#define DSIPHY_SSC6					0x3ac
+#define DSIPHY_SSC10					0x360
+#define DSIPHY_SSC11					0x364
+#define DSIPHY_SSC12					0x368
+#define DSIPHY_SSC13					0x36c
+#define DSIPHY_SSC14					0x370
+#define DSIPHY_SSC15					0x374
+#define DSIPHY_SSC7					0x3b0
+#define DSIPHY_SSC8					0x3b4
 #define DSIPHY_SSC9					0x3b8
 #define DSIPHY_STAT0					0x3e0
 #define DSIPHY_CTRL0					0x3e8
@@ -58,6 +74,14 @@
 	u32 post_div_mux;
 	u32 pixel_divhf;
 	u32 fsm_ovr_ctrl;
+
+	/* ssc_params */
+	u32 mpll_ssc_peak_i;
+	u32 mpll_stepsize_i;
+	u32 mpll_mint_i;
+	u32 mpll_frac_den;
+	u32 mpll_frac_quot_i;
+	u32 mpll_frac_rem;
 };
 
 enum {
diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile
index cb3c48a..99ad22e 100644
--- a/drivers/cpuidle/Makefile
+++ b/drivers/cpuidle/Makefile
@@ -27,4 +27,8 @@
 # POWERPC drivers
 obj-$(CONFIG_PSERIES_CPUIDLE)		+= cpuidle-pseries.o
 obj-$(CONFIG_POWERNV_CPUIDLE)		+= cpuidle-powernv.o
+ifeq ($(CONFIG_MSM_PM_LEGACY), y)
+obj-y += lpm-levels-legacy.o lpm-levels-of-legacy.o lpm-workarounds.o
+else
 obj-$(CONFIG_MSM_PM) += lpm-levels.o lpm-levels-of.o
+endif
diff --git a/drivers/cpuidle/lpm-levels-legacy.c b/drivers/cpuidle/lpm-levels-legacy.c
new file mode 100644
index 0000000..006a5ef
--- /dev/null
+++ b/drivers/cpuidle/lpm-levels-legacy.c
@@ -0,0 +1,1533 @@
+/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/cpu.h>
+#include <linux/of.h>
+#include <linux/hrtimer.h>
+#include <linux/ktime.h>
+#include <linux/tick.h>
+#include <linux/suspend.h>
+#include <linux/pm_qos.h>
+#include <linux/of_platform.h>
+#include <linux/smp.h>
+#include <linux/remote_spinlock.h>
+#include <linux/msm_remote_spinlock.h>
+#include <linux/dma-mapping.h>
+#include <linux/coresight-cti.h>
+#include <linux/moduleparam.h>
+#include <linux/sched.h>
+#include <linux/cpu_pm.h>
+#include <soc/qcom/spm.h>
+#include <soc/qcom/pm-legacy.h>
+#include <soc/qcom/rpm-notifier.h>
+#include <soc/qcom/event_timer.h>
+#include <soc/qcom/lpm-stats.h>
+#include <soc/qcom/lpm_levels.h>
+#include <soc/qcom/jtag.h>
+#include <asm/cputype.h>
+#include <asm/arch_timer.h>
+#include <asm/cacheflush.h>
+#include <asm/suspend.h>
+#include "lpm-levels-legacy.h"
+#include "lpm-workarounds.h"
+#include <trace/events/power.h>
+#define CREATE_TRACE_POINTS
+#include <trace/events/trace_msm_low_power.h>
+#if defined(CONFIG_COMMON_CLK)
+#include "../clk/clk.h"
+#elif defined(CONFIG_COMMON_CLK_MSM)
+#include "../../drivers/clk/msm/clock.h"
+#endif /* CONFIG_COMMON_CLK */
+#include <soc/qcom/minidump.h>
+
+#define SCLK_HZ (32768)
+#define SCM_HANDOFF_LOCK_ID "S:7"
+#define PSCI_POWER_STATE(reset) (reset << 30)
+#define PSCI_AFFINITY_LEVEL(lvl) ((lvl & 0x3) << 24)
+static remote_spinlock_t scm_handoff_lock;
+
+enum {
+	MSM_LPM_LVL_DBG_SUSPEND_LIMITS = BIT(0),
+	MSM_LPM_LVL_DBG_IDLE_LIMITS = BIT(1),
+};
+
+enum debug_event {
+	CPU_ENTER,
+	CPU_EXIT,
+	CLUSTER_ENTER,
+	CLUSTER_EXIT,
+	PRE_PC_CB,
+	CPU_HP_STARTING,
+	CPU_HP_DYING,
+};
+
+struct lpm_debug {
+	cycle_t time;
+	enum debug_event evt;
+	int cpu;
+	uint32_t arg1;
+	uint32_t arg2;
+	uint32_t arg3;
+	uint32_t arg4;
+};
+
+static struct system_pm_ops *sys_pm_ops;
+struct lpm_cluster *lpm_root_node;
+
+static DEFINE_PER_CPU(struct lpm_cluster*, cpu_cluster);
+static bool suspend_in_progress;
+static struct hrtimer lpm_hrtimer;
+static struct lpm_debug *lpm_debug;
+static phys_addr_t lpm_debug_phys;
+
+static const int num_dbg_elements = 0x100;
+
+static void cluster_unprepare(struct lpm_cluster *cluster,
+		const struct cpumask *cpu, int child_idx, bool from_idle,
+		int64_t time);
+static void cluster_prepare(struct lpm_cluster *cluster,
+		const struct cpumask *cpu, int child_idx, bool from_idle,
+		int64_t time);
+
+static bool menu_select;
+module_param_named(
+	menu_select, menu_select, bool, 0664
+);
+
+static bool print_parsed_dt;
+module_param_named(
+	print_parsed_dt, print_parsed_dt, bool, 0664
+);
+
+static bool sleep_disabled;
+module_param_named(sleep_disabled,
+	sleep_disabled, bool, 0664);
+
+s32 msm_cpuidle_get_deep_idle_latency(void)
+{
+	return 10;
+}
+EXPORT_SYMBOL(msm_cpuidle_get_deep_idle_latency);
+
+uint32_t register_system_pm_ops(struct system_pm_ops *pm_ops)
+{
+	if (sys_pm_ops)
+		return -EUSERS;
+
+	sys_pm_ops = pm_ops;
+
+	return 0;
+}
+
+static uint32_t least_cluster_latency(struct lpm_cluster *cluster,
+					struct latency_level *lat_level)
+{
+	struct list_head *list;
+	struct lpm_cluster_level *level;
+	struct lpm_cluster *n;
+	struct power_params *pwr_params;
+	uint32_t latency = 0;
+	int i;
+
+	if (!cluster->list.next) {
+		for (i = 0; i < cluster->nlevels; i++) {
+			level = &cluster->levels[i];
+			pwr_params = &level->pwr;
+			if (lat_level->reset_level == level->reset_level) {
+				if ((latency > pwr_params->latency_us)
+						|| (!latency))
+					latency = pwr_params->latency_us;
+				break;
+			}
+		}
+	} else {
+		list_for_each(list, &cluster->parent->child) {
+			n = list_entry(list, typeof(*n), list);
+			if (lat_level->level_name) {
+				if (strcmp(lat_level->level_name,
+						 n->cluster_name))
+					continue;
+			}
+			for (i = 0; i < n->nlevels; i++) {
+				level = &n->levels[i];
+				pwr_params = &level->pwr;
+				if (lat_level->reset_level ==
+						level->reset_level) {
+					if ((latency > pwr_params->latency_us)
+								|| (!latency))
+						latency =
+						pwr_params->latency_us;
+					break;
+				}
+			}
+		}
+	}
+	return latency;
+}
+
+static uint32_t least_cpu_latency(struct list_head *child,
+				struct latency_level *lat_level)
+{
+	struct list_head *list;
+	struct lpm_cpu_level *level;
+	struct power_params *pwr_params;
+	struct lpm_cpu *cpu;
+	struct lpm_cluster *n;
+	uint32_t latency = 0;
+	int i;
+
+	list_for_each(list, child) {
+		n = list_entry(list, typeof(*n), list);
+		if (lat_level->level_name) {
+			if (strcmp(lat_level->level_name, n->cluster_name))
+				continue;
+		}
+		cpu = n->cpu;
+		for (i = 0; i < cpu->nlevels; i++) {
+			level = &cpu->levels[i];
+			pwr_params = &level->pwr;
+			if (lat_level->reset_level == level->reset_level) {
+				if ((latency > pwr_params->latency_us)
+							|| (!latency))
+					latency = pwr_params->latency_us;
+				break;
+			}
+		}
+	}
+	return latency;
+}
+
+static struct lpm_cluster *cluster_aff_match(struct lpm_cluster *cluster,
+							int affinity_level)
+{
+	struct lpm_cluster *n;
+
+	if ((cluster->aff_level == affinity_level)
+		|| ((cluster->cpu) && (affinity_level == 0)))
+		return cluster;
+	else if (!cluster->cpu) {
+		n =  list_entry(cluster->child.next, typeof(*n), list);
+		return cluster_aff_match(n, affinity_level);
+	} else
+		return NULL;
+}
+
+int lpm_get_latency(struct latency_level *level, uint32_t *latency)
+{
+	struct lpm_cluster *cluster;
+	uint32_t val;
+
+	if (!lpm_root_node) {
+		pr_err("%s: lpm_probe not completed\n", __func__);
+		return -EAGAIN;
+	}
+
+	if ((level->affinity_level < 0)
+		|| (level->affinity_level > lpm_root_node->aff_level)
+		|| (level->reset_level < LPM_RESET_LVL_RET)
+		|| (level->reset_level > LPM_RESET_LVL_PC)
+		|| !latency)
+		return -EINVAL;
+
+	cluster = cluster_aff_match(lpm_root_node, level->affinity_level);
+	if (!cluster) {
+		pr_err("%s:No matching cluster found for affinity_level:%d\n",
+					__func__, level->affinity_level);
+		return -EINVAL;
+	}
+
+	if (level->affinity_level == 0)
+		val = least_cpu_latency(&cluster->parent->child, level);
+	else
+		val = least_cluster_latency(cluster, level);
+
+	if (!val) {
+		pr_err("%s:No mode with affinity_level:%d reset_level:%d\n",
+			__func__, level->affinity_level, level->reset_level);
+		return -EINVAL;
+	}
+
+	*latency = val;
+
+	return 0;
+}
+EXPORT_SYMBOL(lpm_get_latency);
+
+static void update_debug_pc_event(enum debug_event event, uint32_t arg1,
+		uint32_t arg2, uint32_t arg3, uint32_t arg4)
+{
+	struct lpm_debug *dbg;
+	int idx;
+	static DEFINE_SPINLOCK(debug_lock);
+	static int pc_event_index;
+
+	if (!lpm_debug)
+		return;
+
+	spin_lock(&debug_lock);
+	idx = pc_event_index++;
+	dbg = &lpm_debug[idx & (num_dbg_elements - 1)];
+
+	dbg->evt = event;
+	dbg->time = arch_counter_get_cntpct();
+	dbg->cpu = raw_smp_processor_id();
+	dbg->arg1 = arg1;
+	dbg->arg2 = arg2;
+	dbg->arg3 = arg3;
+	dbg->arg4 = arg4;
+	spin_unlock(&debug_lock);
+}
+
+static enum hrtimer_restart lpm_hrtimer_cb(struct hrtimer *h)
+{
+	return HRTIMER_NORESTART;
+}
+
+static void msm_pm_set_timer(uint32_t modified_time_us)
+{
+	u64 modified_time_ns = modified_time_us * NSEC_PER_USEC;
+	ktime_t modified_ktime = ns_to_ktime(modified_time_ns);
+
+	lpm_hrtimer.function = lpm_hrtimer_cb;
+	hrtimer_start(&lpm_hrtimer, modified_ktime, HRTIMER_MODE_REL_PINNED);
+}
+
+int set_l2_mode(struct low_power_ops *ops, int mode,
+				struct lpm_cluster_level *level)
+{
+	int lpm = mode;
+	int rc = 0;
+	bool notify_rpm = level->notify_rpm;
+	struct low_power_ops *cpu_ops = per_cpu(cpu_cluster,
+			smp_processor_id())->lpm_dev;
+
+	if (cpu_ops->tz_flag & MSM_SCM_L2_OFF ||
+			cpu_ops->tz_flag & MSM_SCM_L2_GDHS)
+		coresight_cti_ctx_restore();
+
+	switch (mode) {
+	case MSM_SPM_MODE_STANDALONE_POWER_COLLAPSE:
+	case MSM_SPM_MODE_POWER_COLLAPSE:
+	case MSM_SPM_MODE_FASTPC:
+		if (level->no_cache_flush)
+			cpu_ops->tz_flag = MSM_SCM_L2_GDHS;
+		else
+			cpu_ops->tz_flag = MSM_SCM_L2_OFF;
+		coresight_cti_ctx_save();
+		break;
+	case MSM_SPM_MODE_GDHS:
+		cpu_ops->tz_flag = MSM_SCM_L2_GDHS;
+		coresight_cti_ctx_save();
+		break;
+	case MSM_SPM_MODE_CLOCK_GATING:
+	case MSM_SPM_MODE_RETENTION:
+	case MSM_SPM_MODE_DISABLED:
+		cpu_ops->tz_flag = MSM_SCM_L2_ON;
+		break;
+	default:
+		cpu_ops->tz_flag = MSM_SCM_L2_ON;
+		lpm = MSM_SPM_MODE_DISABLED;
+		break;
+	}
+
+	if (lpm_wa_get_skip_l2_spm())
+		rc = msm_spm_config_low_power_mode_addr(ops->spm, lpm,
+							notify_rpm);
+	else
+		rc = msm_spm_config_low_power_mode(ops->spm, lpm, notify_rpm);
+
+	if (rc)
+		pr_err("%s: Failed to set L2 low power mode %d, ERR %d",
+				__func__, lpm, rc);
+
+	return rc;
+}
+
+int set_l3_mode(struct low_power_ops *ops, int mode,
+				struct lpm_cluster_level *level)
+{
+	bool notify_rpm = level->notify_rpm;
+	struct low_power_ops *cpu_ops = per_cpu(cpu_cluster,
+			smp_processor_id())->lpm_dev;
+
+	switch (mode) {
+	case MSM_SPM_MODE_STANDALONE_POWER_COLLAPSE:
+	case MSM_SPM_MODE_POWER_COLLAPSE:
+	case MSM_SPM_MODE_FASTPC:
+		cpu_ops->tz_flag |= MSM_SCM_L3_PC_OFF;
+		break;
+	default:
+		break;
+	}
+	return msm_spm_config_low_power_mode(ops->spm, mode, notify_rpm);
+}
+
+
+int set_system_mode(struct low_power_ops *ops, int mode,
+				struct lpm_cluster_level *level)
+{
+	bool notify_rpm = level->notify_rpm;
+
+	return msm_spm_config_low_power_mode(ops->spm, mode, notify_rpm);
+}
+
+static int set_device_mode(struct lpm_cluster *cluster, int ndevice,
+		struct lpm_cluster_level *level)
+{
+	struct low_power_ops *ops;
+
+	if (use_psci)
+		return 0;
+
+	ops = &cluster->lpm_dev[ndevice];
+	if (ops && ops->set_mode)
+		return ops->set_mode(ops, level->mode[ndevice],
+				level);
+	else
+		return -EINVAL;
+}
+
+static int cpu_power_select(struct cpuidle_device *dev,
+		struct lpm_cpu *cpu)
+{
+	int best_level = 0;
+	uint32_t latency_us = pm_qos_request_for_cpu(PM_QOS_CPU_DMA_LATENCY,
+							dev->cpu);
+	s64 sleep_us = ktime_to_us(tick_nohz_get_sleep_length());
+	uint32_t modified_time_us = 0;
+	uint32_t next_event_us = 0;
+	int i;
+	uint32_t lvl_latency_us = 0;
+	uint32_t *residency = get_per_cpu_max_residency(dev->cpu);
+
+	if (!cpu)
+		return best_level;
+
+	if ((sleep_disabled && !cpu_isolated(dev->cpu)) || sleep_us  < 0)
+		return 0;
+
+	next_event_us = (uint32_t)(ktime_to_us(get_next_event_time(dev->cpu)));
+
+	for (i = 0; i < cpu->nlevels; i++) {
+		struct lpm_cpu_level *level = &cpu->levels[i];
+		struct power_params *pwr_params = &level->pwr;
+		uint32_t next_wakeup_us = (uint32_t)sleep_us;
+		enum msm_pm_sleep_mode mode = level->mode;
+		bool allow;
+
+		allow = lpm_cpu_mode_allow(dev->cpu, i, true);
+
+		if (!allow)
+			continue;
+
+		lvl_latency_us = pwr_params->latency_us;
+
+		if (latency_us < lvl_latency_us)
+			break;
+
+		if (next_event_us) {
+			if (next_event_us < lvl_latency_us)
+				break;
+
+			if (((next_event_us - lvl_latency_us) < sleep_us) ||
+					(next_event_us < sleep_us))
+				next_wakeup_us = next_event_us - lvl_latency_us;
+		}
+
+		best_level = i;
+
+		if (next_event_us && next_event_us < sleep_us &&
+				(mode != MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT))
+			modified_time_us
+				= next_event_us - lvl_latency_us;
+		else
+			modified_time_us = 0;
+
+		if (next_wakeup_us <= residency[i])
+			break;
+	}
+
+	if (modified_time_us)
+		msm_pm_set_timer(modified_time_us);
+
+	trace_cpu_power_select(best_level, sleep_us, latency_us, next_event_us);
+
+	return best_level;
+}
+
+static uint64_t get_cluster_sleep_time(struct lpm_cluster *cluster,
+		struct cpumask *mask, bool from_idle)
+{
+	int cpu;
+	int next_cpu = raw_smp_processor_id();
+	ktime_t next_event;
+	struct cpumask online_cpus_in_cluster;
+
+	next_event.tv64 = KTIME_MAX;
+	if (!from_idle) {
+		if (mask)
+			cpumask_copy(mask, cpumask_of(raw_smp_processor_id()));
+		return ~0ULL;
+	}
+
+	cpumask_and(&online_cpus_in_cluster,
+			&cluster->num_children_in_sync, cpu_online_mask);
+
+	for_each_cpu(cpu, &online_cpus_in_cluster) {
+		ktime_t *next_event_c;
+
+		next_event_c = get_next_event_cpu(cpu);
+		if (next_event_c->tv64 < next_event.tv64) {
+			next_event.tv64 = next_event_c->tv64;
+			next_cpu = cpu;
+		}
+	}
+
+	if (mask)
+		cpumask_copy(mask, cpumask_of(next_cpu));
+
+
+	if (ktime_to_us(next_event) > ktime_to_us(ktime_get()))
+		return ktime_to_us(ktime_sub(next_event, ktime_get()));
+	else
+		return 0;
+}
+
+static int cluster_select(struct lpm_cluster *cluster, bool from_idle)
+{
+	int best_level = -1;
+	int i;
+	struct cpumask mask;
+	uint32_t latency_us = ~0U;
+	uint32_t sleep_us;
+
+	if (!cluster)
+		return -EINVAL;
+
+	sleep_us = (uint32_t)get_cluster_sleep_time(cluster, NULL, from_idle);
+
+	if (cpumask_and(&mask, cpu_online_mask, &cluster->child_cpus))
+		latency_us = pm_qos_request_for_cpumask(PM_QOS_CPU_DMA_LATENCY,
+							&mask);
+
+	/*
+	 * If atleast one of the core in the cluster is online, the cluster
+	 * low power modes should be determined by the idle characteristics
+	 * even if the last core enters the low power mode as a part of
+	 * hotplug.
+	 */
+
+	if (!from_idle && num_online_cpus() > 1 &&
+		cpumask_intersects(&cluster->child_cpus, cpu_online_mask))
+		from_idle = true;
+
+	for (i = 0; i < cluster->nlevels; i++) {
+		struct lpm_cluster_level *level = &cluster->levels[i];
+		struct power_params *pwr_params = &level->pwr;
+
+		if (!lpm_cluster_mode_allow(cluster, i, from_idle))
+			continue;
+
+		if (level->last_core_only &&
+			cpumask_weight(cpu_online_mask) > 1)
+			continue;
+
+		if (!cpumask_equal(&cluster->num_children_in_sync,
+					&level->num_cpu_votes))
+			continue;
+
+		if (from_idle && latency_us < pwr_params->latency_us)
+			break;
+
+		if (sleep_us < pwr_params->time_overhead_us)
+			break;
+
+		if (suspend_in_progress && from_idle && level->notify_rpm)
+			continue;
+
+		if (level->notify_rpm) {
+			if (!(sys_pm_ops && sys_pm_ops->sleep_allowed))
+				continue;
+			if (!sys_pm_ops->sleep_allowed())
+				continue;
+		}
+
+		best_level = i;
+
+		if (from_idle && sleep_us <= pwr_params->max_residency)
+			break;
+	}
+
+	return best_level;
+}
+
+static void cluster_notify(struct lpm_cluster *cluster,
+		struct lpm_cluster_level *level, bool enter)
+{
+	if (level->is_reset && enter)
+		cpu_cluster_pm_enter(cluster->aff_level);
+	else if (level->is_reset && !enter)
+		cpu_cluster_pm_exit(cluster->aff_level);
+}
+
+static unsigned int get_next_online_cpu(bool from_idle)
+{
+	unsigned int cpu;
+	ktime_t next_event;
+	unsigned int next_cpu = raw_smp_processor_id();
+
+	if (!from_idle)
+		return next_cpu;
+	next_event.tv64 = KTIME_MAX;
+	for_each_online_cpu(cpu) {
+		ktime_t *next_event_c;
+
+		next_event_c = get_next_event_cpu(cpu);
+		if (next_event_c->tv64 < next_event.tv64) {
+			next_event.tv64 = next_event_c->tv64;
+			next_cpu = cpu;
+		}
+	}
+	return next_cpu;
+}
+
+static int cluster_configure(struct lpm_cluster *cluster, int idx,
+		bool from_idle)
+{
+	struct lpm_cluster_level *level = &cluster->levels[idx];
+	struct cpumask cpumask;
+	unsigned int cpu;
+	int ret, i;
+
+	if (!cpumask_equal(&cluster->num_children_in_sync, &cluster->child_cpus)
+			|| is_IPI_pending(&cluster->num_children_in_sync)) {
+		return -EPERM;
+	}
+
+	if (idx != cluster->default_level) {
+		update_debug_pc_event(CLUSTER_ENTER, idx,
+			cluster->num_children_in_sync.bits[0],
+			cluster->child_cpus.bits[0], from_idle);
+		trace_cluster_enter(cluster->cluster_name, idx,
+			cluster->num_children_in_sync.bits[0],
+			cluster->child_cpus.bits[0], from_idle);
+		lpm_stats_cluster_enter(cluster->stats, idx);
+	}
+
+	for (i = 0; i < cluster->ndevices; i++) {
+		ret = set_device_mode(cluster, i, level);
+		if (ret)
+			goto failed_set_mode;
+	}
+
+	if (level->notify_rpm) {
+		struct cpumask *nextcpu;
+
+		cpu = get_next_online_cpu(from_idle);
+		cpumask_copy(&cpumask, cpumask_of(cpu));
+		nextcpu = level->disable_dynamic_routing ? NULL : &cpumask;
+
+		if (sys_pm_ops && sys_pm_ops->enter)
+			if ((sys_pm_ops->enter(nextcpu)))
+				return -EBUSY;
+
+		if (cluster->no_saw_devices && !use_psci)
+			msm_spm_set_rpm_hs(true);
+	}
+
+	/* Notify cluster enter event after successfully config completion */
+	cluster_notify(cluster, level, true);
+
+	cluster->last_level = idx;
+	return 0;
+
+failed_set_mode:
+
+	for (i = 0; i < cluster->ndevices; i++) {
+		int rc = 0;
+
+		level = &cluster->levels[cluster->default_level];
+		rc = set_device_mode(cluster, i, level);
+		WARN_ON(rc);
+	}
+	return ret;
+}
+
+static void cluster_prepare(struct lpm_cluster *cluster,
+		const struct cpumask *cpu, int child_idx, bool from_idle,
+		int64_t start_time)
+{
+	int i;
+
+	if (!cluster)
+		return;
+
+	if (cluster->min_child_level > child_idx)
+		return;
+
+	spin_lock(&cluster->sync_lock);
+	cpumask_or(&cluster->num_children_in_sync, cpu,
+			&cluster->num_children_in_sync);
+
+	for (i = 0; i < cluster->nlevels; i++) {
+		struct lpm_cluster_level *lvl = &cluster->levels[i];
+
+		if (child_idx >= lvl->min_child_level)
+			cpumask_or(&lvl->num_cpu_votes, cpu,
+					&lvl->num_cpu_votes);
+	}
+
+	/*
+	 * cluster_select() does not make any configuration changes. So its ok
+	 * to release the lock here. If a core wakes up for a rude request,
+	 * it need not wait for another to finish its cluster selection and
+	 * configuration process
+	 */
+
+	if (!cpumask_equal(&cluster->num_children_in_sync,
+				&cluster->child_cpus))
+		goto failed;
+
+	i = cluster_select(cluster, from_idle);
+
+	if (i < 0)
+		goto failed;
+
+	if (cluster_configure(cluster, i, from_idle))
+		goto failed;
+
+	cluster->stats->sleep_time = start_time;
+	cluster_prepare(cluster->parent, &cluster->num_children_in_sync, i,
+			from_idle, start_time);
+
+	spin_unlock(&cluster->sync_lock);
+
+	if (!use_psci) {
+		struct lpm_cluster_level *level = &cluster->levels[i];
+
+		if (level->notify_rpm)
+			if (sys_pm_ops && sys_pm_ops->update_wakeup)
+				sys_pm_ops->update_wakeup(from_idle);
+	}
+
+	return;
+failed:
+	spin_unlock(&cluster->sync_lock);
+	cluster->stats->sleep_time = 0;
+}
+
+static void cluster_unprepare(struct lpm_cluster *cluster,
+		const struct cpumask *cpu, int child_idx, bool from_idle,
+		int64_t end_time)
+{
+	struct lpm_cluster_level *level;
+	bool first_cpu;
+	int last_level, i, ret;
+
+	if (!cluster)
+		return;
+
+	if (cluster->min_child_level > child_idx)
+		return;
+
+	spin_lock(&cluster->sync_lock);
+	last_level = cluster->default_level;
+	first_cpu = cpumask_equal(&cluster->num_children_in_sync,
+				&cluster->child_cpus);
+	cpumask_andnot(&cluster->num_children_in_sync,
+			&cluster->num_children_in_sync, cpu);
+
+	for (i = 0; i < cluster->nlevels; i++) {
+		struct lpm_cluster_level *lvl = &cluster->levels[i];
+
+		if (child_idx >= lvl->min_child_level)
+			cpumask_andnot(&lvl->num_cpu_votes,
+					&lvl->num_cpu_votes, cpu);
+	}
+
+	if (!first_cpu || cluster->last_level == cluster->default_level)
+		goto unlock_return;
+
+	if (cluster->stats->sleep_time)
+		cluster->stats->sleep_time = end_time -
+			cluster->stats->sleep_time;
+	lpm_stats_cluster_exit(cluster->stats, cluster->last_level, true);
+
+	level = &cluster->levels[cluster->last_level];
+	if (level->notify_rpm) {
+		if (sys_pm_ops && sys_pm_ops->exit)
+			sys_pm_ops->exit();
+
+		/* If RPM bumps up CX to turbo, unvote CX turbo vote
+		 * during exit of rpm assisted power collapse to
+		 * reduce the power impact
+		 */
+		lpm_wa_cx_unvote_send();
+
+		if (cluster->no_saw_devices && !use_psci)
+			msm_spm_set_rpm_hs(false);
+
+	}
+
+	update_debug_pc_event(CLUSTER_EXIT, cluster->last_level,
+			cluster->num_children_in_sync.bits[0],
+			cluster->child_cpus.bits[0], from_idle);
+	trace_cluster_exit(cluster->cluster_name, cluster->last_level,
+			cluster->num_children_in_sync.bits[0],
+			cluster->child_cpus.bits[0], from_idle);
+
+	last_level = cluster->last_level;
+	cluster->last_level = cluster->default_level;
+
+	for (i = 0; i < cluster->ndevices; i++) {
+		level = &cluster->levels[cluster->default_level];
+		ret = set_device_mode(cluster, i, level);
+
+		WARN_ON(ret);
+
+	}
+
+	cluster_notify(cluster, &cluster->levels[last_level], false);
+	cluster_unprepare(cluster->parent, &cluster->child_cpus,
+			last_level, from_idle, end_time);
+unlock_return:
+	spin_unlock(&cluster->sync_lock);
+}
+
+static inline void cpu_prepare(struct lpm_cluster *cluster, int cpu_index,
+				bool from_idle)
+{
+	struct lpm_cpu_level *cpu_level = &cluster->cpu->levels[cpu_index];
+	bool jtag_save_restore =
+			cluster->cpu->levels[cpu_index].jtag_save_restore;
+
+	/* Use broadcast timer for aggregating sleep mode within a cluster.
+	 * A broadcast timer could be used in the following scenarios
+	 * 1) The architected timer HW gets reset during certain low power
+	 * modes and the core relies on a external(broadcast) timer to wake up
+	 * from sleep. This information is passed through device tree.
+	 * 2) The CPU low power mode could trigger a system low power mode.
+	 * The low power module relies on Broadcast timer to aggregate the
+	 * next wakeup within a cluster, in which case, CPU switches over to
+	 * use broadcast timer.
+	 */
+	if (from_idle && (cpu_level->use_bc_timer ||
+			(cpu_index >= cluster->min_child_level)))
+		tick_broadcast_enter();
+
+	if (from_idle && ((cpu_level->mode == MSM_PM_SLEEP_MODE_POWER_COLLAPSE)
+		|| (cpu_level->mode ==
+			MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE)
+			|| (cpu_level->is_reset)))
+		cpu_pm_enter();
+
+	/*
+	 * Save JTAG registers for 8996v1.0 & 8996v2.x in C4 LPM
+	 */
+	if (jtag_save_restore)
+		msm_jtag_save_state();
+}
+
+static inline void cpu_unprepare(struct lpm_cluster *cluster, int cpu_index,
+				bool from_idle)
+{
+	struct lpm_cpu_level *cpu_level = &cluster->cpu->levels[cpu_index];
+	bool jtag_save_restore =
+			cluster->cpu->levels[cpu_index].jtag_save_restore;
+
+	if (from_idle && (cpu_level->use_bc_timer ||
+			(cpu_index >= cluster->min_child_level)))
+		tick_broadcast_exit();
+
+	if (from_idle && ((cpu_level->mode == MSM_PM_SLEEP_MODE_POWER_COLLAPSE)
+		|| (cpu_level->mode ==
+			MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE)
+		|| cpu_level->is_reset))
+		cpu_pm_exit();
+
+	/*
+	 * Restore JTAG registers for 8996v1.0 & 8996v2.x in C4 LPM
+	 */
+	if (jtag_save_restore)
+		msm_jtag_restore_state();
+}
+
+#if defined(CONFIG_ARM_PSCI) || !defined(CONFIG_CPU_V7)
+static int get_cluster_id(struct lpm_cluster *cluster, int *aff_lvl)
+{
+	int state_id = 0;
+
+	if (!cluster)
+		return 0;
+
+	spin_lock(&cluster->sync_lock);
+
+	if (!cpumask_equal(&cluster->num_children_in_sync,
+				&cluster->child_cpus))
+		goto unlock_and_return;
+
+	state_id |= get_cluster_id(cluster->parent, aff_lvl);
+
+	if (cluster->last_level != cluster->default_level) {
+		struct lpm_cluster_level *level
+			= &cluster->levels[cluster->last_level];
+
+		state_id |= (level->psci_id & cluster->psci_mode_mask)
+					<< cluster->psci_mode_shift;
+		(*aff_lvl)++;
+	}
+unlock_and_return:
+	spin_unlock(&cluster->sync_lock);
+	return state_id;
+}
+#endif
+
+#if !defined(CONFIG_CPU_V7)
+asmlinkage int __invoke_psci_fn_smc(u64, u64, u64, u64);
+static bool psci_enter_sleep(struct lpm_cluster *cluster,
+					int idx, bool from_idle)
+
+{
+	bool ret;
+	/*
+	 * idx = 0 is the default LPM state
+	 */
+	if (!idx) {
+		stop_critical_timings();
+		wfi();
+		start_critical_timings();
+		ret = true;
+	} else {
+		int affinity_level = 0;
+		int state_id = get_cluster_id(cluster, &affinity_level);
+		int power_state =
+			PSCI_POWER_STATE(cluster->cpu->levels[idx].is_reset);
+		bool success = false;
+
+		if (cluster->cpu->levels[idx].hyp_psci) {
+			stop_critical_timings();
+			__invoke_psci_fn_smc(0xC4000021, 0, 0, 0);
+			start_critical_timings();
+			return 1;
+		}
+
+		affinity_level = PSCI_AFFINITY_LEVEL(affinity_level);
+		state_id |= (power_state | affinity_level
+			| cluster->cpu->levels[idx].psci_id);
+
+		update_debug_pc_event(CPU_ENTER, state_id,
+						0xdeaffeed, 0xdeaffeed, true);
+		stop_critical_timings();
+		success = !arm_cpuidle_suspend(state_id);
+		start_critical_timings();
+		update_debug_pc_event(CPU_EXIT, state_id,
+						success, 0xdeaffeed, true);
+		ret = success;
+	}
+	return ret;
+}
+#elif defined(CONFIG_ARM_PSCI)
+static bool psci_enter_sleep(struct lpm_cluster *cluster,
+					int idx, bool from_idle)
+{
+	bool ret;
+
+	if (!idx) {
+		stop_critical_timings();
+		wfi();
+		start_critical_timings();
+		ret = true;
+	} else {
+		int affinity_level = 0;
+		int state_id = get_cluster_id(cluster, &affinity_level);
+		int power_state =
+			PSCI_POWER_STATE(cluster->cpu->levels[idx].is_reset);
+		bool success = false;
+
+		affinity_level = PSCI_AFFINITY_LEVEL(affinity_level);
+		state_id |= (power_state | affinity_level
+			| cluster->cpu->levels[idx].psci_id);
+
+		update_debug_pc_event(CPU_ENTER, state_id,
+						0xdeaffeed, 0xdeaffeed, true);
+		stop_critical_timings();
+		success = !arm_cpuidle_suspend(state_id);
+		start_critical_timings();
+		update_debug_pc_event(CPU_EXIT, state_id,
+						success, 0xdeaffeed, true);
+		ret = success;
+	}
+	return ret;
+}
+#else
+static bool psci_enter_sleep(struct lpm_cluster *cluster,
+					int idx, bool from_idle)
+{
+	WARN_ONCE(true, "PSCI cpu_suspend ops not supported\n");
+	return false;
+}
+#endif
+
+static int lpm_cpuidle_select(struct cpuidle_driver *drv,
+		struct cpuidle_device *dev)
+{
+	struct lpm_cluster *cluster = per_cpu(cpu_cluster, dev->cpu);
+	int idx;
+
+	if (!cluster)
+		return 0;
+
+	idx = cpu_power_select(dev, cluster->cpu);
+
+	return idx;
+}
+
+static int lpm_cpuidle_enter(struct cpuidle_device *dev,
+		struct cpuidle_driver *drv, int idx)
+{
+	struct lpm_cluster *cluster = per_cpu(cpu_cluster, dev->cpu);
+	bool success = true;
+	const struct cpumask *cpumask = get_cpu_mask(dev->cpu);
+	ktime_t start = ktime_get();
+	int64_t start_time = ktime_to_ns(ktime_get()), end_time;
+
+	if (idx < 0)
+		return -EINVAL;
+
+	cpu_prepare(cluster, idx, true);
+	cluster_prepare(cluster, cpumask, idx, true, ktime_to_ns(ktime_get()));
+
+	trace_cpu_idle_enter(idx);
+	lpm_stats_cpu_enter(idx, start_time);
+
+	if (need_resched())
+		goto exit;
+
+	if (!use_psci) {
+		if (idx > 0)
+			update_debug_pc_event(CPU_ENTER, idx, 0xdeaffeed,
+					0xdeaffeed, true);
+		success = msm_cpu_pm_enter_sleep(cluster->cpu->levels[idx].mode,
+				true);
+
+		if (idx > 0)
+			update_debug_pc_event(CPU_EXIT, idx, success,
+							0xdeaffeed, true);
+	} else {
+		success = psci_enter_sleep(cluster, idx, true);
+	}
+
+exit:
+	end_time = ktime_to_ns(ktime_get());
+	lpm_stats_cpu_exit(idx, end_time, success);
+
+	cluster_unprepare(cluster, cpumask, idx, true, end_time);
+	cpu_unprepare(cluster, idx, true);
+
+	trace_cpu_idle_exit(idx, success);
+	dev->last_residency = ktime_us_delta(ktime_get(), start);
+	local_irq_enable();
+
+	return idx;
+}
+
+#ifdef CONFIG_CPU_IDLE_MULTIPLE_DRIVERS
+static int cpuidle_register_cpu(struct cpuidle_driver *drv,
+		struct cpumask *mask)
+{
+	struct cpuidle_device *device;
+	int cpu, ret;
+
+
+	if (!mask || !drv)
+		return -EINVAL;
+
+	drv->cpumask = mask;
+	ret = cpuidle_register_driver(drv);
+	if (ret) {
+		pr_err("Failed to register cpuidle driver %d\n", ret);
+		goto failed_driver_register;
+	}
+
+	for_each_cpu(cpu, mask) {
+		device = &per_cpu(cpuidle_dev, cpu);
+		device->cpu = cpu;
+
+		ret = cpuidle_register_device(device);
+		if (ret) {
+			pr_err("Failed to register cpuidle driver for cpu:%u\n",
+					cpu);
+			goto failed_driver_register;
+		}
+	}
+	return ret;
+failed_driver_register:
+	for_each_cpu(cpu, mask)
+		cpuidle_unregister_driver(drv);
+	return ret;
+}
+#else
+static int cpuidle_register_cpu(struct cpuidle_driver *drv,
+		struct  cpumask *mask)
+{
+	return cpuidle_register(drv, NULL);
+}
+#endif
+
+static struct cpuidle_governor lpm_governor = {
+	.name =		"qcom",
+	.rating =	30,
+	.select =	lpm_cpuidle_select,
+	.owner =	THIS_MODULE,
+};
+
+static int cluster_cpuidle_register(struct lpm_cluster *cl)
+{
+	int i = 0, ret = 0;
+	unsigned int cpu;
+	struct lpm_cluster *p = NULL;
+
+	if (!cl->cpu) {
+		struct lpm_cluster *n;
+
+		list_for_each_entry(n, &cl->child, list) {
+			ret = cluster_cpuidle_register(n);
+			if (ret)
+				break;
+		}
+		return ret;
+	}
+
+	cl->drv = kzalloc(sizeof(*cl->drv), GFP_KERNEL);
+	if (!cl->drv)
+		return -ENOMEM;
+
+	cl->drv->name = "msm_idle";
+
+	for (i = 0; i < cl->cpu->nlevels; i++) {
+		struct cpuidle_state *st = &cl->drv->states[i];
+		struct lpm_cpu_level *cpu_level = &cl->cpu->levels[i];
+
+		snprintf(st->name, CPUIDLE_NAME_LEN, "C%u\n", i);
+		snprintf(st->desc, CPUIDLE_DESC_LEN, "%s", cpu_level->name);
+		st->flags = 0;
+		st->exit_latency = cpu_level->pwr.latency_us;
+		st->power_usage = cpu_level->pwr.ss_power;
+		st->target_residency = 0;
+		st->enter = lpm_cpuidle_enter;
+	}
+
+	cl->drv->state_count = cl->cpu->nlevels;
+	cl->drv->safe_state_index = 0;
+	for_each_cpu(cpu, &cl->child_cpus)
+		per_cpu(cpu_cluster, cpu) = cl;
+
+	for_each_possible_cpu(cpu) {
+		if (cpu_online(cpu))
+			continue;
+		p = per_cpu(cpu_cluster, cpu);
+		while (p) {
+			int j;
+
+			spin_lock(&p->sync_lock);
+			cpumask_set_cpu(cpu, &p->num_children_in_sync);
+			for (j = 0; j < p->nlevels; j++)
+				cpumask_copy(&p->levels[j].num_cpu_votes,
+						&p->num_children_in_sync);
+			spin_unlock(&p->sync_lock);
+			p = p->parent;
+		}
+	}
+	ret = cpuidle_register_cpu(cl->drv, &cl->child_cpus);
+
+	if (ret) {
+		kfree(cl->drv);
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+/**
+ * init_lpm - initializes the governor
+ */
+static int __init init_lpm(void)
+{
+	return cpuidle_register_governor(&lpm_governor);
+}
+
+postcore_initcall(init_lpm);
+
+static void register_cpu_lpm_stats(struct lpm_cpu *cpu,
+		struct lpm_cluster *parent)
+{
+	const char **level_name;
+	int i;
+
+	level_name = kcalloc(cpu->nlevels, sizeof(*level_name), GFP_KERNEL);
+
+	if (!level_name)
+		return;
+
+	for (i = 0; i < cpu->nlevels; i++)
+		level_name[i] = cpu->levels[i].name;
+
+	lpm_stats_config_level("cpu", level_name, cpu->nlevels,
+			parent->stats, &parent->child_cpus);
+
+	kfree(level_name);
+}
+
+static void register_cluster_lpm_stats(struct lpm_cluster *cl,
+		struct lpm_cluster *parent)
+{
+	const char **level_name;
+	int i;
+	struct lpm_cluster *child;
+
+	if (!cl)
+		return;
+
+	level_name = kcalloc(cl->nlevels, sizeof(*level_name), GFP_KERNEL);
+
+	if (!level_name)
+		return;
+
+	for (i = 0; i < cl->nlevels; i++)
+		level_name[i] = cl->levels[i].level_name;
+
+	cl->stats = lpm_stats_config_level(cl->cluster_name, level_name,
+			cl->nlevels, parent ? parent->stats : NULL, NULL);
+
+	kfree(level_name);
+
+	if (cl->cpu) {
+		register_cpu_lpm_stats(cl->cpu, cl);
+		return;
+	}
+
+	list_for_each_entry(child, &cl->child, list)
+		register_cluster_lpm_stats(child, cl);
+}
+
+static int lpm_suspend_prepare(void)
+{
+	suspend_in_progress = true;
+	lpm_stats_suspend_enter();
+
+	return 0;
+}
+
+static void lpm_suspend_wake(void)
+{
+	suspend_in_progress = false;
+	lpm_stats_suspend_exit();
+}
+
+static int lpm_suspend_enter(suspend_state_t state)
+{
+	int cpu = raw_smp_processor_id();
+	struct lpm_cluster *cluster = per_cpu(cpu_cluster, cpu);
+	struct lpm_cpu *lpm_cpu = cluster->cpu;
+	const struct cpumask *cpumask = get_cpu_mask(cpu);
+	int idx;
+
+	for (idx = lpm_cpu->nlevels - 1; idx >= 0; idx--) {
+
+		if (lpm_cpu_mode_allow(cpu, idx, false))
+			break;
+	}
+	if (idx < 0) {
+		pr_err("Failed suspend\n");
+		return 0;
+	}
+	cpu_prepare(cluster, idx, false);
+	cluster_prepare(cluster, cpumask, idx, false, 0);
+	if (idx > 0)
+		update_debug_pc_event(CPU_ENTER, idx, 0xdeaffeed,
+					0xdeaffeed, false);
+
+	/*
+	 * Print the clocks which are enabled during system suspend
+	 * This debug information is useful to know which are the
+	 * clocks that are enabled and preventing the system level
+	 * LPMs(XO and Vmin).
+	 */
+	clock_debug_print_enabled(true);
+
+	if (!use_psci)
+		msm_cpu_pm_enter_sleep(cluster->cpu->levels[idx].mode, false);
+	else
+		psci_enter_sleep(cluster, idx, true);
+
+	if (idx > 0)
+		update_debug_pc_event(CPU_EXIT, idx, true, 0xdeaffeed,
+					false);
+
+	cluster_unprepare(cluster, cpumask, idx, false, 0);
+	cpu_unprepare(cluster, idx, false);
+	return 0;
+}
+
+static int lpm_dying_cpu(unsigned int cpu)
+{
+	struct lpm_cluster *cluster = per_cpu(cpu_cluster, cpu);
+
+	update_debug_pc_event(CPU_HP_DYING, cpu,
+				cluster->num_children_in_sync.bits[0],
+				cluster->child_cpus.bits[0], false);
+	cluster_prepare(cluster, get_cpu_mask(cpu), NR_LPM_LEVELS, false, 0);
+	return 0;
+}
+
+static int lpm_starting_cpu(unsigned int cpu)
+{
+	struct lpm_cluster *cluster = per_cpu(cpu_cluster, cpu);
+
+	update_debug_pc_event(CPU_HP_STARTING, cpu,
+				cluster->num_children_in_sync.bits[0],
+				cluster->child_cpus.bits[0], false);
+	cluster_unprepare(cluster, get_cpu_mask(cpu), NR_LPM_LEVELS, false, 0);
+	return 0;
+}
+
+static const struct platform_suspend_ops lpm_suspend_ops = {
+	.enter = lpm_suspend_enter,
+	.valid = suspend_valid_only_mem,
+	.prepare_late = lpm_suspend_prepare,
+	.wake = lpm_suspend_wake,
+};
+
+static int lpm_probe(struct platform_device *pdev)
+{
+	int ret;
+	int size;
+	struct kobject *module_kobj = NULL;
+	struct md_region md_entry;
+
+	get_online_cpus();
+	lpm_root_node = lpm_of_parse_cluster(pdev);
+
+	if (IS_ERR_OR_NULL(lpm_root_node)) {
+		pr_err("%s(): Failed to probe low power modes\n", __func__);
+		put_online_cpus();
+		return PTR_ERR(lpm_root_node);
+	}
+
+	if (print_parsed_dt)
+		cluster_dt_walkthrough(lpm_root_node);
+
+	/*
+	 * Register hotplug notifier before broadcast time to ensure there
+	 * to prevent race where a broadcast timer might not be setup on for a
+	 * core.  BUG in existing code but no known issues possibly because of
+	 * how late lpm_levels gets initialized.
+	 */
+	suspend_set_ops(&lpm_suspend_ops);
+	hrtimer_init(&lpm_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+
+	ret = remote_spin_lock_init(&scm_handoff_lock, SCM_HANDOFF_LOCK_ID);
+	if (ret) {
+		pr_err("%s: Failed initializing scm_handoff_lock (%d)\n",
+			__func__, ret);
+		put_online_cpus();
+		return ret;
+	}
+	size = num_dbg_elements * sizeof(struct lpm_debug);
+	lpm_debug = dma_alloc_coherent(&pdev->dev, size,
+			&lpm_debug_phys, GFP_KERNEL);
+	register_cluster_lpm_stats(lpm_root_node, NULL);
+
+	ret = cluster_cpuidle_register(lpm_root_node);
+	put_online_cpus();
+	if (ret) {
+		pr_err("%s()Failed to register with cpuidle framework\n",
+				__func__);
+		goto failed;
+	}
+	ret = cpuhp_setup_state(CPUHP_AP_QCOM_SLEEP_STARTING,
+			"AP_QCOM_SLEEP_STARTING",
+			lpm_starting_cpu, lpm_dying_cpu);
+	if (ret)
+		goto failed;
+
+	module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
+	if (!module_kobj) {
+		pr_err("%s: cannot find kobject for module %s\n",
+			__func__, KBUILD_MODNAME);
+		ret = -ENOENT;
+		goto failed;
+	}
+
+	ret = create_cluster_lvl_nodes(lpm_root_node, module_kobj);
+	if (ret) {
+		pr_err("%s(): Failed to create cluster level nodes\n",
+				__func__);
+		goto failed;
+	}
+
+	/* Add lpm_debug to Minidump*/
+	strlcpy(md_entry.name, "KLPMDEBUG", sizeof(md_entry.name));
+	md_entry.virt_addr = (uintptr_t)lpm_debug;
+	md_entry.phys_addr = lpm_debug_phys;
+	md_entry.size = size;
+	if (msm_minidump_add_region(&md_entry))
+		pr_info("Failed to add lpm_debug in Minidump\n");
+
+	return 0;
+failed:
+	free_cluster_node(lpm_root_node);
+	lpm_root_node = NULL;
+	return ret;
+}
+
+static const struct of_device_id lpm_mtch_tbl[] = {
+	{.compatible = "qcom,lpm-levels"},
+	{},
+};
+
+static struct platform_driver lpm_driver = {
+	.probe = lpm_probe,
+	.driver = {
+		.name = "lpm-levels",
+		.owner = THIS_MODULE,
+		.of_match_table = lpm_mtch_tbl,
+	},
+};
+
+static int __init lpm_levels_module_init(void)
+{
+	int rc;
+
+	rc = platform_driver_register(&lpm_driver);
+	if (rc) {
+		pr_info("Error registering %s\n", lpm_driver.driver.name);
+		goto fail;
+	}
+
+fail:
+	return rc;
+}
+late_initcall(lpm_levels_module_init);
+
+enum msm_pm_l2_scm_flag lpm_cpu_pre_pc_cb(unsigned int cpu)
+{
+	struct lpm_cluster *cluster = per_cpu(cpu_cluster, cpu);
+	enum msm_pm_l2_scm_flag retflag = MSM_SCM_L2_ON;
+
+	/*
+	 * No need to acquire the lock if probe isn't completed yet
+	 * In the event of the hotplug happening before lpm probe, we want to
+	 * flush the cache to make sure that L2 is flushed. In particular, this
+	 * could cause incoherencies for a cluster architecture. This wouldn't
+	 * affect the idle case as the idle driver wouldn't be registered
+	 * before the probe function
+	 */
+	if (!cluster)
+		return MSM_SCM_L2_OFF;
+
+	/*
+	 * Assumes L2 only. What/How parameters gets passed into TZ will
+	 * determine how this function reports this info back in msm-pm.c
+	 */
+	spin_lock(&cluster->sync_lock);
+
+	if (!cluster->lpm_dev) {
+		retflag = MSM_SCM_L2_OFF;
+		goto unlock_and_return;
+	}
+
+	if (!cpumask_equal(&cluster->num_children_in_sync,
+						&cluster->child_cpus))
+		goto unlock_and_return;
+
+	if (cluster->lpm_dev)
+		retflag = cluster->lpm_dev->tz_flag;
+	/*
+	 * The scm_handoff_lock will be release by the secure monitor.
+	 * It is used to serialize power-collapses from this point on,
+	 * so that both Linux and the secure context have a consistent
+	 * view regarding the number of running cpus (cpu_count).
+	 *
+	 * It must be acquired before releasing the cluster lock.
+	 */
+unlock_and_return:
+	update_debug_pc_event(PRE_PC_CB, retflag, 0xdeadbeef, 0xdeadbeef,
+			0xdeadbeef);
+	trace_pre_pc_cb(retflag);
+	remote_spin_lock_rlock_id(&scm_handoff_lock,
+				  REMOTE_SPINLOCK_TID_START + cpu);
+	spin_unlock(&cluster->sync_lock);
+	return retflag;
+}
+
+/**
+ * lpm_cpu_hotplug_enter(): Called by dying CPU to terminate in low power mode
+ *
+ * @cpu: cpuid of the dying CPU
+ *
+ * Called from platform_cpu_kill() to terminate hotplug in a low power mode
+ */
+void lpm_cpu_hotplug_enter(unsigned int cpu)
+{
+	enum msm_pm_sleep_mode mode = MSM_PM_SLEEP_MODE_NR;
+	struct lpm_cluster *cluster = per_cpu(cpu_cluster, cpu);
+	int i;
+	int idx = -1;
+
+	/*
+	 * If lpm isn't probed yet, try to put cpu into the one of the modes
+	 * available
+	 */
+	if (!cluster) {
+		if (msm_spm_is_mode_avail(
+					MSM_SPM_MODE_POWER_COLLAPSE)){
+			mode = MSM_PM_SLEEP_MODE_POWER_COLLAPSE;
+		} else if (msm_spm_is_mode_avail(
+				MSM_SPM_MODE_FASTPC)) {
+			mode = MSM_PM_SLEEP_MODE_FASTPC;
+		} else if (msm_spm_is_mode_avail(
+				MSM_SPM_MODE_RETENTION)) {
+			mode = MSM_PM_SLEEP_MODE_RETENTION;
+		} else {
+			pr_err("No mode avail for cpu%d hotplug\n", cpu);
+			WARN_ON(1);
+			return;
+		}
+	} else {
+		struct lpm_cpu *lpm_cpu;
+		uint32_t ss_pwr = ~0U;
+
+		lpm_cpu = cluster->cpu;
+		for (i = 0; i < lpm_cpu->nlevels; i++) {
+			if (ss_pwr < lpm_cpu->levels[i].pwr.ss_power)
+				continue;
+			ss_pwr = lpm_cpu->levels[i].pwr.ss_power;
+			idx = i;
+			mode = lpm_cpu->levels[i].mode;
+		}
+
+		if (mode == MSM_PM_SLEEP_MODE_NR)
+			return;
+
+		WARN_ON(idx < 0);
+		cluster_prepare(cluster, get_cpu_mask(cpu), idx, false, 0);
+	}
+
+	msm_cpu_pm_enter_sleep(mode, false);
+}
diff --git a/drivers/cpuidle/lpm-levels-legacy.h b/drivers/cpuidle/lpm-levels-legacy.h
new file mode 100644
index 0000000..4a07355
--- /dev/null
+++ b/drivers/cpuidle/lpm-levels-legacy.h
@@ -0,0 +1,152 @@
+/* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <soc/qcom/pm-legacy.h>
+#include <soc/qcom/spm.h>
+
+#define NR_LPM_LEVELS 8
+
+extern bool use_psci;
+
+struct lpm_lookup_table {
+	uint32_t modes;
+	const char *mode_name;
+};
+
+struct power_params {
+	uint32_t latency_us;		/* Enter + Exit latency */
+	uint32_t ss_power;		/* Steady state power */
+	uint32_t energy_overhead;	/* Enter + exit over head */
+	uint32_t time_overhead_us;	/* Enter + exit overhead */
+	uint32_t residencies[NR_LPM_LEVELS];
+	uint32_t max_residency;
+};
+
+struct lpm_cpu_level {
+	const char *name;
+	enum msm_pm_sleep_mode mode;
+	bool use_bc_timer;
+	struct power_params pwr;
+	unsigned int psci_id;
+	bool is_reset;
+	bool jtag_save_restore;
+	bool hyp_psci;
+	int reset_level;
+};
+
+struct lpm_cpu {
+	struct lpm_cpu_level levels[NR_LPM_LEVELS];
+	int nlevels;
+	unsigned int psci_mode_shift;
+	unsigned int psci_mode_mask;
+	struct lpm_cluster *parent;
+};
+
+struct lpm_level_avail {
+	bool idle_enabled;
+	bool suspend_enabled;
+	struct kobject *kobj;
+	struct kobj_attribute idle_enabled_attr;
+	struct kobj_attribute suspend_enabled_attr;
+	void *data;
+	int idx;
+	bool cpu_node;
+};
+
+struct lpm_cluster_level {
+	const char *level_name;
+	int *mode;			/* SPM mode to enter */
+	int min_child_level;
+	struct cpumask num_cpu_votes;
+	struct power_params pwr;
+	bool notify_rpm;
+	bool disable_dynamic_routing;
+	bool sync_level;
+	bool last_core_only;
+	struct lpm_level_avail available;
+	unsigned int psci_id;
+	bool is_reset;
+	int reset_level;
+	bool no_cache_flush;
+};
+
+struct low_power_ops {
+	struct msm_spm_device *spm;
+	int (*set_mode)(struct low_power_ops *ops, int mode,
+				struct lpm_cluster_level *level);
+	enum msm_pm_l2_scm_flag tz_flag;
+};
+
+struct lpm_cluster {
+	struct list_head list;
+	struct list_head child;
+	const char *cluster_name;
+	const char **name;
+	unsigned long aff_level; /* Affinity level of the node */
+	struct low_power_ops *lpm_dev;
+	int ndevices;
+	struct lpm_cluster_level levels[NR_LPM_LEVELS];
+	int nlevels;
+	enum msm_pm_l2_scm_flag l2_flag;
+	int min_child_level;
+	int default_level;
+	int last_level;
+	struct lpm_cpu *cpu;
+	struct cpuidle_driver *drv;
+	spinlock_t sync_lock;
+	struct cpumask child_cpus;
+	struct cpumask num_children_in_sync;
+	struct lpm_cluster *parent;
+	struct lpm_stats *stats;
+	unsigned int psci_mode_shift;
+	unsigned int psci_mode_mask;
+	bool no_saw_devices;
+};
+
+int set_l2_mode(struct low_power_ops *ops, int mode,
+				struct lpm_cluster_level *level);
+int set_system_mode(struct low_power_ops *ops, int mode,
+				struct lpm_cluster_level *level);
+int set_l3_mode(struct low_power_ops *ops, int mode,
+				struct lpm_cluster_level *level);
+void lpm_suspend_wake_time(uint64_t wakeup_time);
+
+struct lpm_cluster *lpm_of_parse_cluster(struct platform_device *pdev);
+void free_cluster_node(struct lpm_cluster *cluster);
+void cluster_dt_walkthrough(struct lpm_cluster *cluster);
+
+int create_cluster_lvl_nodes(struct lpm_cluster *p, struct kobject *kobj);
+bool lpm_cpu_mode_allow(unsigned int cpu,
+		unsigned int mode, bool from_idle);
+bool lpm_cluster_mode_allow(struct lpm_cluster *cluster,
+		unsigned int mode, bool from_idle);
+uint32_t *get_per_cpu_max_residency(int cpu);
+extern struct lpm_cluster *lpm_root_node;
+
+#ifdef CONFIG_SMP
+extern DEFINE_PER_CPU(bool, pending_ipi);
+static inline bool is_IPI_pending(const struct cpumask *mask)
+{
+	unsigned int cpu;
+
+	for_each_cpu(cpu, mask) {
+		if per_cpu(pending_ipi, cpu)
+			return true;
+	}
+	return false;
+}
+#else
+static inline bool is_IPI_pending(const struct cpumask *mask)
+{
+	return false;
+}
+#endif
diff --git a/drivers/cpuidle/lpm-levels-of-legacy.c b/drivers/cpuidle/lpm-levels-of-legacy.c
new file mode 100644
index 0000000..bf74124
--- /dev/null
+++ b/drivers/cpuidle/lpm-levels-of-legacy.c
@@ -0,0 +1,1006 @@
+/* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/err.h>
+#include <linux/sysfs.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/moduleparam.h>
+#include "lpm-levels-legacy.h"
+
+bool use_psci;
+enum lpm_type {
+	IDLE = 0,
+	SUSPEND,
+	LPM_TYPE_NR
+};
+
+struct lpm_type_str {
+	enum lpm_type type;
+	char *str;
+};
+
+static const struct lpm_type_str lpm_types[] = {
+	{IDLE, "idle_enabled"},
+	{SUSPEND, "suspend_enabled"},
+};
+
+static DEFINE_PER_CPU(uint32_t *, max_residency);
+static struct lpm_level_avail *cpu_level_available[NR_CPUS];
+static struct platform_device *lpm_pdev;
+
+static void *get_enabled_ptr(struct kobj_attribute *attr,
+					struct lpm_level_avail *avail)
+{
+	void *arg = NULL;
+
+	if (!strcmp(attr->attr.name, lpm_types[IDLE].str))
+		arg = (void *) &avail->idle_enabled;
+	else if (!strcmp(attr->attr.name, lpm_types[SUSPEND].str))
+		arg = (void *) &avail->suspend_enabled;
+
+	return arg;
+}
+
+static struct lpm_level_avail *get_avail_ptr(struct kobject *kobj,
+					struct kobj_attribute *attr)
+{
+	struct lpm_level_avail *avail = NULL;
+
+	if (!strcmp(attr->attr.name, lpm_types[IDLE].str))
+		avail = container_of(attr, struct lpm_level_avail,
+					idle_enabled_attr);
+	else if (!strcmp(attr->attr.name, lpm_types[SUSPEND].str))
+		avail = container_of(attr, struct lpm_level_avail,
+					suspend_enabled_attr);
+
+	return avail;
+}
+
+static void set_optimum_cpu_residency(struct lpm_cpu *cpu, int cpu_id,
+		bool probe_time)
+{
+	int i, j;
+	bool mode_avail;
+	uint32_t *residency = per_cpu(max_residency, cpu_id);
+
+	for (i = 0; i < cpu->nlevels; i++) {
+		struct power_params *pwr = &cpu->levels[i].pwr;
+
+		mode_avail = probe_time ||
+			lpm_cpu_mode_allow(cpu_id, i, true);
+
+		if (!mode_avail) {
+			residency[i] = 0;
+			continue;
+		}
+
+		residency[i] = ~0;
+		for (j = i + 1; j < cpu->nlevels; j++) {
+			mode_avail = probe_time ||
+					lpm_cpu_mode_allow(cpu_id, j, true);
+
+			if (mode_avail &&
+				(residency[i] > pwr->residencies[j]) &&
+				(pwr->residencies[j] != 0))
+				residency[i] = pwr->residencies[j];
+		}
+	}
+}
+
+static void set_optimum_cluster_residency(struct lpm_cluster *cluster,
+		bool probe_time)
+{
+	int i, j;
+	bool mode_avail;
+
+	for (i = 0; i < cluster->nlevels; i++) {
+		struct power_params *pwr = &cluster->levels[i].pwr;
+
+		mode_avail = probe_time ||
+			lpm_cluster_mode_allow(cluster, i,
+					true);
+
+		if (!mode_avail) {
+			pwr->max_residency = 0;
+			continue;
+		}
+
+		pwr->max_residency = ~0;
+		for (j = i+1; j < cluster->nlevels; j++) {
+			mode_avail = probe_time ||
+					lpm_cluster_mode_allow(cluster, j,
+							true);
+			if (mode_avail &&
+				(pwr->max_residency > pwr->residencies[j]) &&
+				(pwr->residencies[j] != 0))
+				pwr->max_residency = pwr->residencies[j];
+		}
+	}
+}
+
+uint32_t *get_per_cpu_max_residency(int cpu)
+{
+	return per_cpu(max_residency, cpu);
+}
+
+static ssize_t lpm_enable_show(struct kobject *kobj,
+			struct kobj_attribute *attr, char *buf)
+{
+	int ret = 0;
+	struct kernel_param kp;
+
+	kp.arg = get_enabled_ptr(attr, get_avail_ptr(kobj, attr));
+	ret = param_get_bool(buf, &kp);
+	if (ret > 0) {
+		strlcat(buf, "\n", PAGE_SIZE);
+		ret++;
+	}
+
+	return ret;
+}
+
+static ssize_t lpm_enable_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t len)
+{
+	int ret = 0;
+	struct kernel_param kp;
+	struct lpm_level_avail *avail;
+
+	avail = get_avail_ptr(kobj, attr);
+	if (WARN_ON(!avail))
+		return -EINVAL;
+
+	kp.arg = get_enabled_ptr(attr, avail);
+	ret = param_set_bool(buf, &kp);
+
+	if (avail->cpu_node)
+		set_optimum_cpu_residency(avail->data, avail->idx, false);
+	else
+		set_optimum_cluster_residency(avail->data, false);
+
+	return ret ? ret : len;
+}
+
+static int create_lvl_avail_nodes(const char *name,
+			struct kobject *parent, struct lpm_level_avail *avail,
+			void *data, int index, bool cpu_node)
+{
+	struct attribute_group *attr_group = NULL;
+	struct attribute **attr = NULL;
+	struct kobject *kobj = NULL;
+	int ret = 0;
+
+	kobj = kobject_create_and_add(name, parent);
+	if (!kobj)
+		return -ENOMEM;
+
+	attr_group = devm_kzalloc(&lpm_pdev->dev, sizeof(*attr_group),
+					GFP_KERNEL);
+	if (!attr_group) {
+		ret = -ENOMEM;
+		goto failed;
+	}
+
+	attr = devm_kzalloc(&lpm_pdev->dev,
+		sizeof(*attr) * (LPM_TYPE_NR + 1), GFP_KERNEL);
+	if (!attr) {
+		ret = -ENOMEM;
+		goto failed;
+	}
+
+	sysfs_attr_init(&avail->idle_enabled_attr.attr);
+	avail->idle_enabled_attr.attr.name = lpm_types[IDLE].str;
+	avail->idle_enabled_attr.attr.mode = 0644;
+	avail->idle_enabled_attr.show = lpm_enable_show;
+	avail->idle_enabled_attr.store = lpm_enable_store;
+
+	sysfs_attr_init(&avail->suspend_enabled_attr.attr);
+	avail->suspend_enabled_attr.attr.name = lpm_types[SUSPEND].str;
+	avail->suspend_enabled_attr.attr.mode = 0644;
+	avail->suspend_enabled_attr.show = lpm_enable_show;
+	avail->suspend_enabled_attr.store = lpm_enable_store;
+
+	attr[0] = &avail->idle_enabled_attr.attr;
+	attr[1] = &avail->suspend_enabled_attr.attr;
+	attr[2] = NULL;
+	attr_group->attrs = attr;
+
+	ret = sysfs_create_group(kobj, attr_group);
+	if (ret) {
+		ret = -ENOMEM;
+		goto failed;
+	}
+
+	avail->idle_enabled = true;
+	avail->suspend_enabled = true;
+	avail->kobj = kobj;
+	avail->data = data;
+	avail->idx = index;
+	avail->cpu_node = cpu_node;
+
+	return ret;
+
+failed:
+	kobject_put(kobj);
+	return ret;
+}
+
+static int create_cpu_lvl_nodes(struct lpm_cluster *p, struct kobject *parent)
+{
+	int cpu;
+	int i, cpu_idx;
+	struct kobject **cpu_kobj = NULL;
+	struct lpm_level_avail *level_list = NULL;
+	char cpu_name[20] = {0};
+	int ret = 0;
+
+	cpu_kobj = devm_kzalloc(&lpm_pdev->dev, sizeof(*cpu_kobj) *
+			cpumask_weight(&p->child_cpus), GFP_KERNEL);
+	if (!cpu_kobj)
+		return -ENOMEM;
+
+	cpu_idx = 0;
+	for_each_cpu(cpu, &p->child_cpus) {
+		snprintf(cpu_name, sizeof(cpu_name), "cpu%d", cpu);
+		cpu_kobj[cpu_idx] = kobject_create_and_add(cpu_name, parent);
+		if (!cpu_kobj[cpu_idx]) {
+			ret = -ENOMEM;
+			goto release_kobj;
+		}
+
+		level_list = devm_kzalloc(&lpm_pdev->dev,
+				p->cpu->nlevels * sizeof(*level_list),
+				GFP_KERNEL);
+		if (!level_list) {
+			ret = -ENOMEM;
+			goto release_kobj;
+		}
+
+		for (i = 0; i < p->cpu->nlevels; i++) {
+
+			ret = create_lvl_avail_nodes(p->cpu->levels[i].name,
+					cpu_kobj[cpu_idx], &level_list[i],
+					(void *)p->cpu, cpu, true);
+			if (ret)
+				goto release_kobj;
+		}
+
+		cpu_level_available[cpu] = level_list;
+		cpu_idx++;
+	}
+
+	return ret;
+
+release_kobj:
+	for (i = 0; i < cpumask_weight(&p->child_cpus); i++)
+		kobject_put(cpu_kobj[i]);
+
+	return ret;
+}
+
+int create_cluster_lvl_nodes(struct lpm_cluster *p, struct kobject *kobj)
+{
+	int ret = 0;
+	struct lpm_cluster *child = NULL;
+	int i;
+	struct kobject *cluster_kobj = NULL;
+
+	if (!p)
+		return -ENODEV;
+
+	cluster_kobj = kobject_create_and_add(p->cluster_name, kobj);
+	if (!cluster_kobj)
+		return -ENOMEM;
+
+	for (i = 0; i < p->nlevels; i++) {
+		ret = create_lvl_avail_nodes(p->levels[i].level_name,
+				cluster_kobj, &p->levels[i].available,
+				(void *)p, 0, false);
+		if (ret)
+			return ret;
+	}
+
+	list_for_each_entry(child, &p->child, list) {
+		ret = create_cluster_lvl_nodes(child, cluster_kobj);
+		if (ret)
+			return ret;
+	}
+
+	if (p->cpu) {
+		ret = create_cpu_lvl_nodes(p, cluster_kobj);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+bool lpm_cpu_mode_allow(unsigned int cpu,
+		unsigned int index, bool from_idle)
+{
+	struct lpm_level_avail *avail = cpu_level_available[cpu];
+
+	if (!lpm_pdev || !avail)
+		return !from_idle;
+
+	return !!(from_idle ? avail[index].idle_enabled :
+				avail[index].suspend_enabled);
+}
+
+bool lpm_cluster_mode_allow(struct lpm_cluster *cluster,
+		unsigned int mode, bool from_idle)
+{
+	struct lpm_level_avail *avail = &cluster->levels[mode].available;
+
+	if (!lpm_pdev || !avail)
+		return false;
+
+	return !!(from_idle ? avail->idle_enabled :
+				avail->suspend_enabled);
+}
+
+static int parse_legacy_cluster_params(struct device_node *node,
+		struct lpm_cluster *c)
+{
+	int i;
+	char *key;
+	int ret;
+	struct lpm_match {
+		char *devname;
+		int (*set_mode)(struct low_power_ops *, int,
+					struct lpm_cluster_level *);
+	};
+	struct lpm_match match_tbl[] = {
+		{"l2", set_l2_mode},
+		{"cci", set_system_mode},
+		{"l3", set_l3_mode},
+		{"cbf", set_system_mode},
+	};
+
+
+	key = "qcom,spm-device-names";
+	c->ndevices = of_property_count_strings(node, key);
+
+	if (c->ndevices < 0) {
+		pr_info("%s(): Ignoring cluster params\n", __func__);
+		c->no_saw_devices = true;
+		c->ndevices = 0;
+		return 0;
+	}
+
+	c->name = devm_kzalloc(&lpm_pdev->dev, c->ndevices * sizeof(*c->name),
+				GFP_KERNEL);
+	c->lpm_dev = devm_kzalloc(&lpm_pdev->dev,
+				c->ndevices * sizeof(*c->lpm_dev),
+				GFP_KERNEL);
+	if (!c->name || !c->lpm_dev) {
+		ret = -ENOMEM;
+		goto failed;
+	}
+
+	for (i = 0; i < c->ndevices; i++) {
+		char device_name[20];
+		int j;
+
+		ret = of_property_read_string_index(node, key, i, &c->name[i]);
+		if (ret)
+			goto failed;
+		snprintf(device_name, sizeof(device_name), "%s-%s",
+				c->cluster_name, c->name[i]);
+
+		c->lpm_dev[i].spm = msm_spm_get_device_by_name(device_name);
+
+		if (IS_ERR_OR_NULL(c->lpm_dev[i].spm)) {
+			pr_err("Failed to get spm device by name:%s\n",
+					device_name);
+			ret = PTR_ERR(c->lpm_dev[i].spm);
+			goto failed;
+		}
+		for (j = 0; j < ARRAY_SIZE(match_tbl); j++) {
+			if (!strcmp(c->name[i], match_tbl[j].devname))
+				c->lpm_dev[i].set_mode = match_tbl[j].set_mode;
+		}
+
+		if (!c->lpm_dev[i].set_mode) {
+			ret = -ENODEV;
+			goto failed;
+		}
+	}
+
+	key = "qcom,default-level";
+	if (of_property_read_u32(node, key, &c->default_level))
+		c->default_level = 0;
+	return 0;
+failed:
+	pr_err("%s(): Failed reading %s\n", __func__, key);
+	return ret;
+}
+
+static int parse_cluster_params(struct device_node *node,
+		struct lpm_cluster *c)
+{
+	char *key;
+	int ret;
+
+	key = "label";
+	ret = of_property_read_string(node, key, &c->cluster_name);
+	if (ret) {
+		pr_err("%s(): Cannot read required param %s\n", __func__, key);
+		return ret;
+	}
+
+	if (use_psci) {
+		key = "qcom,psci-mode-shift";
+		ret = of_property_read_u32(node, key,
+				&c->psci_mode_shift);
+		if (ret) {
+			pr_err("%s(): Failed to read param: %s\n",
+							__func__, key);
+			return ret;
+		}
+
+		key = "qcom,psci-mode-mask";
+		ret = of_property_read_u32(node, key,
+				&c->psci_mode_mask);
+		if (ret) {
+			pr_err("%s(): Failed to read param: %s\n",
+							__func__, key);
+			return ret;
+		}
+
+		/* Set ndevice to 1 as default */
+		c->ndevices = 1;
+
+		return 0;
+	} else
+		return parse_legacy_cluster_params(node, c);
+}
+
+static int parse_lpm_mode(const char *str)
+{
+	int i;
+	struct lpm_lookup_table mode_lookup[] = {
+		{MSM_SPM_MODE_POWER_COLLAPSE, "pc"},
+		{MSM_SPM_MODE_STANDALONE_POWER_COLLAPSE, "spc"},
+		{MSM_SPM_MODE_FASTPC, "fpc"},
+		{MSM_SPM_MODE_GDHS, "gdhs"},
+		{MSM_SPM_MODE_RETENTION, "retention"},
+		{MSM_SPM_MODE_CLOCK_GATING, "wfi"},
+		{MSM_SPM_MODE_DISABLED, "active"}
+	};
+
+	for (i = 0; i < ARRAY_SIZE(mode_lookup); i++)
+		if (!strcmp(str, mode_lookup[i].mode_name))
+			return  mode_lookup[i].modes;
+	return -EINVAL;
+}
+
+static int parse_power_params(struct device_node *node,
+		struct power_params *pwr)
+{
+	char *key;
+	int ret;
+
+	key = "qcom,latency-us";
+	ret  = of_property_read_u32(node, key, &pwr->latency_us);
+	if (ret)
+		goto fail;
+
+	key = "qcom,ss-power";
+	ret = of_property_read_u32(node, key, &pwr->ss_power);
+	if (ret)
+		goto fail;
+
+	key = "qcom,energy-overhead";
+	ret = of_property_read_u32(node, key, &pwr->energy_overhead);
+	if (ret)
+		goto fail;
+
+	key = "qcom,time-overhead";
+	ret = of_property_read_u32(node, key, &pwr->time_overhead_us);
+	if (ret)
+		goto fail;
+
+fail:
+	if (ret)
+		pr_err("%s(): %s Error reading %s\n", __func__, node->name,
+				key);
+	return ret;
+}
+
+static int parse_cluster_level(struct device_node *node,
+		struct lpm_cluster *cluster)
+{
+	int i = 0;
+	struct lpm_cluster_level *level = &cluster->levels[cluster->nlevels];
+	int ret = -ENOMEM;
+	char *key;
+
+	key = "label";
+	ret = of_property_read_string(node, key, &level->level_name);
+	if (ret)
+		goto failed;
+
+	if (use_psci) {
+		char *k = "qcom,psci-mode";
+
+		ret = of_property_read_u32(node, k, &level->psci_id);
+		if (ret)
+			goto failed;
+
+		level->is_reset = of_property_read_bool(node, "qcom,is-reset");
+	} else if (!cluster->no_saw_devices) {
+		key  = "no saw-devices";
+
+		level->mode = devm_kzalloc(&lpm_pdev->dev,
+				cluster->ndevices * sizeof(*level->mode),
+				GFP_KERNEL);
+		if (!level->mode) {
+			pr_err("Memory allocation failed\n");
+			goto failed;
+		}
+
+		for (i = 0; i < cluster->ndevices; i++) {
+			const char *spm_mode;
+			char key[25] = {0};
+
+			snprintf(key, 25, "qcom,spm-%s-mode", cluster->name[i]);
+			ret = of_property_read_string(node, key, &spm_mode);
+			if (ret)
+				goto failed;
+
+			level->mode[i] = parse_lpm_mode(spm_mode);
+
+			if (level->mode[i] < 0)
+				goto failed;
+
+			if (level->mode[i] == MSM_SPM_MODE_POWER_COLLAPSE
+				|| level->mode[i] ==
+				MSM_SPM_MODE_STANDALONE_POWER_COLLAPSE)
+				level->is_reset |= true;
+		}
+	}
+
+	key = "label";
+	ret = of_property_read_string(node, key, &level->level_name);
+	if (ret)
+		goto failed;
+
+	if (cluster->nlevels != cluster->default_level) {
+		key = "min child idx";
+		ret = of_property_read_u32(node, "qcom,min-child-idx",
+				&level->min_child_level);
+		if (ret)
+			goto failed;
+
+		if (cluster->min_child_level > level->min_child_level)
+			cluster->min_child_level = level->min_child_level;
+	}
+
+	level->notify_rpm = of_property_read_bool(node, "qcom,notify-rpm");
+	level->disable_dynamic_routing = of_property_read_bool(node,
+					"qcom,disable-dynamic-int-routing");
+	level->last_core_only = of_property_read_bool(node,
+					"qcom,last-core-only");
+	level->no_cache_flush = of_property_read_bool(node,
+					"qcom,no-cache-flush");
+
+	key = "parse_power_params";
+	ret = parse_power_params(node, &level->pwr);
+	if (ret)
+		goto failed;
+
+	key = "qcom,reset-level";
+	ret = of_property_read_u32(node, key, &level->reset_level);
+	if (ret == -EINVAL)
+		level->reset_level = LPM_RESET_LVL_NONE;
+	else if (ret)
+		goto failed;
+
+	cluster->nlevels++;
+	return 0;
+failed:
+	pr_err("Failed %s() key = %s ret = %d\n", __func__, key, ret);
+	return ret;
+}
+
+static int parse_cpu_spm_mode(const char *mode_name)
+{
+	struct lpm_lookup_table pm_sm_lookup[] = {
+		{MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT,
+			"wfi"},
+		{MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE,
+			"standalone_pc"},
+		{MSM_PM_SLEEP_MODE_POWER_COLLAPSE,
+			"pc"},
+		{MSM_PM_SLEEP_MODE_RETENTION,
+			"retention"},
+		{MSM_PM_SLEEP_MODE_FASTPC,
+			"fpc"},
+	};
+	int i;
+	int ret = -EINVAL;
+
+	for (i = 0; i < ARRAY_SIZE(pm_sm_lookup); i++) {
+		if (!strcmp(mode_name, pm_sm_lookup[i].mode_name)) {
+			ret = pm_sm_lookup[i].modes;
+			break;
+		}
+	}
+	return ret;
+}
+
+static int parse_cpu_mode(struct device_node *n, struct lpm_cpu_level *l)
+{
+	char *key;
+	int ret;
+
+	key = "qcom,spm-cpu-mode";
+	ret  =  of_property_read_string(n, key, &l->name);
+	if (ret) {
+		pr_err("Failed %s %d\n", n->name, __LINE__);
+		return ret;
+	}
+
+	if (use_psci) {
+		key = "qcom,psci-cpu-mode";
+
+		ret = of_property_read_u32(n, key, &l->psci_id);
+		if (ret) {
+			pr_err("Failed reading %s on device %s\n", key,
+					n->name);
+			return ret;
+		}
+		key = "qcom,hyp-psci";
+
+		l->hyp_psci = of_property_read_bool(n, key);
+	} else {
+		l->mode = parse_cpu_spm_mode(l->name);
+
+		if (l->mode < 0)
+			return l->mode;
+	}
+	return 0;
+
+}
+
+static int get_cpumask_for_node(struct device_node *node, struct cpumask *mask)
+{
+	struct device_node *cpu_node;
+	int cpu;
+	int idx = 0;
+
+	cpu_node = of_parse_phandle(node, "qcom,cpu", idx++);
+	if (!cpu_node) {
+		pr_info("%s: No CPU phandle, assuming single cluster\n",
+				node->full_name);
+		/*
+		 * Not all targets have the cpu node populated in the device
+		 * tree. If cpu node is not populated assume all possible
+		 * nodes belong to this cluster
+		 */
+		cpumask_copy(mask, cpu_possible_mask);
+		return 0;
+	}
+
+	while (cpu_node) {
+		for_each_possible_cpu(cpu) {
+			if (of_get_cpu_node(cpu, NULL) == cpu_node) {
+				cpumask_set_cpu(cpu, mask);
+				break;
+			}
+		}
+		of_node_put(cpu_node);
+		cpu_node = of_parse_phandle(node, "qcom,cpu", idx++);
+	}
+
+	return 0;
+}
+
+static int calculate_residency(struct power_params *base_pwr,
+					struct power_params *next_pwr)
+{
+	int32_t residency = (int32_t)(next_pwr->energy_overhead -
+						base_pwr->energy_overhead) -
+		((int32_t)(next_pwr->ss_power * next_pwr->time_overhead_us)
+		- (int32_t)(base_pwr->ss_power * base_pwr->time_overhead_us));
+
+	residency /= (int32_t)(base_pwr->ss_power  - next_pwr->ss_power);
+
+	if (residency < 0) {
+		pr_err("%s: residency < 0 for LPM\n",
+				__func__);
+		return next_pwr->time_overhead_us;
+	}
+
+	return residency < next_pwr->time_overhead_us ?
+				next_pwr->time_overhead_us : residency;
+}
+
+static int parse_cpu_levels(struct device_node *node, struct lpm_cluster *c)
+{
+	struct device_node *n;
+	int ret = -ENOMEM;
+	int i, j;
+	char *key;
+
+	c->cpu = devm_kzalloc(&lpm_pdev->dev, sizeof(*c->cpu), GFP_KERNEL);
+	if (!c->cpu)
+		return ret;
+
+	c->cpu->parent = c;
+	if (use_psci) {
+
+		key = "qcom,psci-mode-shift";
+
+		ret = of_property_read_u32(node, key, &c->cpu->psci_mode_shift);
+		if (ret) {
+			pr_err("Failed reading %s on device %s\n", key,
+					node->name);
+			return ret;
+		}
+		key = "qcom,psci-mode-mask";
+
+		ret = of_property_read_u32(node, key, &c->cpu->psci_mode_mask);
+		if (ret) {
+			pr_err("Failed reading %s on device %s\n", key,
+					node->name);
+			return ret;
+		}
+	}
+	for_each_child_of_node(node, n) {
+		struct lpm_cpu_level *l = &c->cpu->levels[c->cpu->nlevels];
+
+		c->cpu->nlevels++;
+
+		ret = parse_cpu_mode(n, l);
+		if (ret < 0) {
+			pr_info("Failed %s\n", l->name);
+			goto failed;
+		}
+
+		ret = parse_power_params(n, &l->pwr);
+		if (ret)
+			goto failed;
+
+		key = "qcom,use-broadcast-timer";
+		l->use_bc_timer = of_property_read_bool(n, key);
+
+		l->is_reset = of_property_read_bool(n, "qcom,is-reset");
+
+		key = "qcom,jtag-save-restore";
+		l->jtag_save_restore = of_property_read_bool(n, key);
+
+		key = "qcom,reset-level";
+		ret = of_property_read_u32(n, key, &l->reset_level);
+		if (ret == -EINVAL)
+			l->reset_level = LPM_RESET_LVL_NONE;
+		else if (ret)
+			goto failed;
+		of_node_put(n);
+	}
+	for (i = 0; i < c->cpu->nlevels; i++) {
+		for (j = 0; j < c->cpu->nlevels; j++) {
+			if (i >= j) {
+				c->cpu->levels[i].pwr.residencies[j] = 0;
+				continue;
+			}
+
+			c->cpu->levels[i].pwr.residencies[j] =
+				calculate_residency(&c->cpu->levels[i].pwr,
+					&c->cpu->levels[j].pwr);
+
+			pr_err("%s: idx %d %u\n", __func__, j,
+					c->cpu->levels[i].pwr.residencies[j]);
+		}
+	}
+
+	return 0;
+failed:
+	of_node_put(n);
+	pr_err("%s(): Failed with error code:%d\n", __func__, ret);
+	return ret;
+}
+
+void free_cluster_node(struct lpm_cluster *cluster)
+{
+	struct lpm_cluster *cl, *m;
+
+	list_for_each_entry_safe(cl, m, &cluster->child, list) {
+		list_del(&cl->list);
+		free_cluster_node(cl);
+	};
+
+	cluster->ndevices = 0;
+}
+
+/*
+ * TODO:
+ * Expects a CPU or a cluster only. This ensures that affinity
+ * level of a cluster is consistent with reference to its
+ * child nodes.
+ */
+static struct lpm_cluster *parse_cluster(struct device_node *node,
+		struct lpm_cluster *parent)
+{
+	struct lpm_cluster *c;
+	struct device_node *n;
+	char *key;
+	int ret = 0;
+	int i, j;
+
+	c = devm_kzalloc(&lpm_pdev->dev, sizeof(*c), GFP_KERNEL);
+	if (!c)
+		return ERR_PTR(-ENOMEM);
+
+	ret = parse_cluster_params(node, c);
+
+	if (ret)
+		goto failed_parse_params;
+
+	INIT_LIST_HEAD(&c->child);
+	c->parent = parent;
+	spin_lock_init(&c->sync_lock);
+	c->min_child_level = NR_LPM_LEVELS;
+
+	for_each_child_of_node(node, n) {
+
+		if (!n->name)
+			continue;
+		key = "qcom,pm-cluster-level";
+		if (!of_node_cmp(n->name, key)) {
+			if (parse_cluster_level(n, c))
+				goto failed_parse_cluster;
+			continue;
+		}
+
+		key = "qcom,pm-cluster";
+		if (!of_node_cmp(n->name, key)) {
+			struct lpm_cluster *child;
+
+			if (c->no_saw_devices)
+				pr_info("%s: SAW device not provided.\n",
+					__func__);
+
+			child = parse_cluster(n, c);
+			if (!child)
+				goto failed_parse_cluster;
+
+			of_node_put(n);
+			list_add(&child->list, &c->child);
+			cpumask_or(&c->child_cpus, &c->child_cpus,
+					&child->child_cpus);
+			c->aff_level = child->aff_level + 1;
+			continue;
+		}
+
+		key = "qcom,pm-cpu";
+		if (!of_node_cmp(n->name, key)) {
+			/*
+			 * Parse the the cpu node only if a pm-cpu node
+			 * is available, though the mask is defined @ the
+			 * cluster level
+			 */
+			if (get_cpumask_for_node(node, &c->child_cpus))
+				goto failed_parse_cluster;
+
+			if (parse_cpu_levels(n, c))
+				goto failed_parse_cluster;
+
+			c->aff_level = 1;
+
+			for_each_cpu(i, &c->child_cpus) {
+				per_cpu(max_residency, i) = devm_kzalloc(
+					&lpm_pdev->dev,
+					sizeof(uint32_t) * c->cpu->nlevels,
+					GFP_KERNEL);
+				if (!per_cpu(max_residency, i))
+					return ERR_PTR(-ENOMEM);
+				set_optimum_cpu_residency(c->cpu, i, true);
+			}
+		}
+	}
+
+	if (cpumask_intersects(&c->child_cpus, cpu_online_mask))
+		c->last_level = c->default_level;
+	else
+		c->last_level = c->nlevels-1;
+
+	for (i = 0; i < c->nlevels; i++) {
+		for (j = 0; j < c->nlevels; j++) {
+			if (i >= j) {
+				c->levels[i].pwr.residencies[j] = 0;
+				continue;
+			}
+			c->levels[i].pwr.residencies[j] = calculate_residency(
+				&c->levels[i].pwr, &c->levels[j].pwr);
+		}
+	}
+	set_optimum_cluster_residency(c, true);
+	return c;
+
+failed_parse_cluster:
+	pr_err("Failed parse cluster:%s\n", key);
+	if (parent)
+		list_del(&c->list);
+	free_cluster_node(c);
+failed_parse_params:
+	pr_err("Failed parse params\n");
+	return NULL;
+}
+struct lpm_cluster *lpm_of_parse_cluster(struct platform_device *pdev)
+{
+	struct device_node *top = NULL;
+	struct lpm_cluster *c;
+
+	use_psci = of_property_read_bool(pdev->dev.of_node, "qcom,use-psci");
+
+	top = of_find_node_by_name(pdev->dev.of_node, "qcom,pm-cluster");
+	if (!top) {
+		pr_err("Failed to find root node\n");
+		return ERR_PTR(-ENODEV);
+	}
+
+	lpm_pdev = pdev;
+	c = parse_cluster(top, NULL);
+	of_node_put(top);
+	return c;
+}
+
+void cluster_dt_walkthrough(struct lpm_cluster *cluster)
+{
+	struct list_head *list;
+	int i, j;
+	static int id;
+	char str[10] = {0};
+
+	if (!cluster)
+		return;
+
+	for (i = 0; i < id; i++)
+		snprintf(str+i, 10 - i, "\t");
+	pr_info("%d\n", __LINE__);
+
+	for (i = 0; i < cluster->nlevels; i++) {
+		struct lpm_cluster_level *l = &cluster->levels[i];
+
+		pr_info("%d ndevices:%d\n", __LINE__, cluster->ndevices);
+		for (j = 0; j < cluster->ndevices; j++)
+			pr_info("%sDevice: %pk id:%pk\n", str,
+					&cluster->name[j], &l->mode[i]);
+	}
+
+	if (cluster->cpu) {
+		pr_info("%d\n", __LINE__);
+		for (j = 0; j < cluster->cpu->nlevels; j++)
+			pr_info("%s\tCPU mode: %s id:%d\n", str,
+					cluster->cpu->levels[j].name,
+					cluster->cpu->levels[j].mode);
+	}
+
+	id++;
+
+
+	list_for_each(list, &cluster->child) {
+		struct lpm_cluster *n;
+
+		pr_info("%d\n", __LINE__);
+		n = list_entry(list, typeof(*n), list);
+		cluster_dt_walkthrough(n);
+	}
+	id--;
+}
diff --git a/drivers/cpuidle/lpm-workarounds.c b/drivers/cpuidle/lpm-workarounds.c
new file mode 100644
index 0000000..657e2b9
--- /dev/null
+++ b/drivers/cpuidle/lpm-workarounds.c
@@ -0,0 +1,147 @@
+/* Copyright (c) 2014-2016, 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/err.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/rpm-smd-regulator.h>
+#include <lpm-workarounds.h>
+
+static struct regulator *lpm_cx_reg;
+static struct work_struct dummy_vote_work;
+static struct workqueue_struct *lpm_wa_wq;
+static bool lpm_wa_cx_turbo_unvote;
+static bool skip_l2_spm;
+
+/* While exiting from RPM assisted power collapse on some targets like MSM8939
+ * the CX is bumped to turbo mode by RPM. To reduce the power impact, APSS
+ * low power driver need to remove the CX turbo vote.
+ */
+static void send_dummy_cx_vote(struct work_struct *w)
+{
+	if (lpm_cx_reg) {
+		regulator_set_voltage(lpm_cx_reg,
+			RPM_REGULATOR_CORNER_SUPER_TURBO,
+			RPM_REGULATOR_CORNER_SUPER_TURBO);
+
+		regulator_set_voltage(lpm_cx_reg,
+			RPM_REGULATOR_CORNER_NONE,
+			RPM_REGULATOR_CORNER_SUPER_TURBO);
+	}
+}
+
+/*
+ * lpm_wa_cx_unvote_send(): Unvote for CX turbo mode
+ */
+void lpm_wa_cx_unvote_send(void)
+{
+	if (lpm_wa_cx_turbo_unvote)
+		queue_work(lpm_wa_wq, &dummy_vote_work);
+}
+EXPORT_SYMBOL(lpm_wa_cx_unvote_send);
+
+static int lpm_wa_cx_unvote_init(struct platform_device *pdev)
+{
+	int ret = 0;
+
+	lpm_cx_reg = devm_regulator_get(&pdev->dev, "lpm-cx");
+	if (IS_ERR(lpm_cx_reg)) {
+		ret = PTR_ERR(lpm_cx_reg);
+		if (ret != -EPROBE_DEFER)
+			pr_err("Unable to get the CX regulator\n");
+		return ret;
+	}
+
+	INIT_WORK(&dummy_vote_work, send_dummy_cx_vote);
+
+	lpm_wa_wq = alloc_workqueue("lpm-wa",
+				WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_HIGHPRI, 1);
+
+	return ret;
+}
+
+static int lpm_wa_cx_unvote_exit(void)
+{
+	if (lpm_wa_wq)
+		destroy_workqueue(lpm_wa_wq);
+
+	return 0;
+}
+
+bool lpm_wa_get_skip_l2_spm(void)
+{
+	return skip_l2_spm;
+}
+EXPORT_SYMBOL(lpm_wa_get_skip_l2_spm);
+
+static int lpm_wa_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+
+	lpm_wa_cx_turbo_unvote = of_property_read_bool(pdev->dev.of_node,
+					"qcom,lpm-wa-cx-turbo-unvote");
+	if (lpm_wa_cx_turbo_unvote) {
+		ret = lpm_wa_cx_unvote_init(pdev);
+		if (ret) {
+			pr_err("%s: Failed to initialize lpm_wa_cx_unvote (%d)\n",
+				__func__, ret);
+			return ret;
+		}
+	}
+
+	skip_l2_spm = of_property_read_bool(pdev->dev.of_node,
+						"qcom,lpm-wa-skip-l2-spm");
+
+	return ret;
+}
+
+static int lpm_wa_remove(struct platform_device *pdev)
+{
+	int ret = 0;
+
+	if (lpm_wa_cx_turbo_unvote)
+		ret = lpm_wa_cx_unvote_exit();
+
+	return ret;
+}
+
+static const struct of_device_id lpm_wa_mtch_tbl[] = {
+	{.compatible = "qcom,lpm-workarounds"},
+	{},
+};
+
+static struct platform_driver lpm_wa_driver = {
+	.probe = lpm_wa_probe,
+	.remove = lpm_wa_remove,
+	.driver = {
+		.name = "lpm-workarounds",
+		.owner = THIS_MODULE,
+		.of_match_table = lpm_wa_mtch_tbl,
+	},
+};
+
+static int __init lpm_wa_module_init(void)
+{
+	int ret;
+
+	ret = platform_driver_register(&lpm_wa_driver);
+	if (ret)
+		pr_info("Error registering %s\n", lpm_wa_driver.driver.name);
+
+	return ret;
+}
+late_initcall(lpm_wa_module_init);
diff --git a/drivers/cpuidle/lpm-workarounds.h b/drivers/cpuidle/lpm-workarounds.h
new file mode 100644
index 0000000..a290dcb
--- /dev/null
+++ b/drivers/cpuidle/lpm-workarounds.h
@@ -0,0 +1,20 @@
+/* Copyright (c) 2014-2016, 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __LPM_WA_H
+#define __LPM_WA_H
+
+void lpm_wa_cx_unvote_send(void);
+bool lpm_wa_get_skip_l2_spm(void);
+
+#endif  /* __LPM_WA_H */
diff --git a/drivers/iio/imu/inv_icm20602/inv_icm20602_bsp.c b/drivers/iio/imu/inv_icm20602/inv_icm20602_bsp.c
index bfff285..9968e44 100644
--- a/drivers/iio/imu/inv_icm20602/inv_icm20602_bsp.c
+++ b/drivers/iio/imu/inv_icm20602/inv_icm20602_bsp.c
@@ -95,12 +95,12 @@
 #define W_FLG	0
 #define R_FLG	1
 int icm20602_bulk_read(struct inv_icm20602_state *st,
-			int reg, char *buf, int size)
+			int reg, u8 *buf, int size)
 {
 	int result = MPU_SUCCESS;
 	char tx_buf[2] = {0x0, 0x0};
 	int tmp_size = size;
-	int tmp_buf = buf;
+	u8 *tmp_buf = buf;
 	struct i2c_msg msg[2];
 
 	if (!st || !buf)
@@ -109,38 +109,31 @@
 	if (st->interface == ICM20602_SPI) {
 		tx_buf[0] = ICM20602_READ_REG(reg);
 		result = spi_write_then_read(st->spi, &tx_buf[0],
-			1, tmp_buf, size);
+		1, tmp_buf, size);
 		if (result) {
 			pr_err("mpu read reg %u failed, rc %d\n",
-				reg, result);
+			reg, result);
 			result = -MPU_READ_FAIL;
 		}
 	} else {
 		result = size;
-		while (tmp_size > 0) {
 #ifdef ICM20602_I2C_SMBUS
-			result += i2c_smbus_read_i2c_block_data(st->client,
-				reg, (tmp_size < 32)?tmp_size:32, tmp_buf);
-			tmp_size -= 32;
-			tmp_buf += tmp_size;
+		result += i2c_smbus_read_i2c_block_data(st->client,
+		reg, size, tmp_buf);
 #else
-			tx_buf[0] = reg;
-			msg[0].addr = st->client->addr;
-			msg[0].flags = W_FLG;
-			msg[0].len = 1;
-			msg[0].buf = tx_buf;
+		tx_buf[0] = reg;
+		msg[0].addr = st->client->addr;
+		msg[0].flags = W_FLG;
+		msg[0].len = 1;
+		msg[0].buf = tx_buf;
 
-			msg[1].addr = st->client->addr;
-			msg[1].flags = I2C_M_RD;
-			msg[1].len = (tmp_size < 32)?tmp_size:32;
-			msg[1].buf = tmp_buf;
-			i2c_transfer(st->client->adapter, msg, ARRAY_SIZE(msg));
-			tmp_size -= 32;
-			tmp_buf += tmp_size;
+		msg[1].addr = st->client->addr;
+		msg[1].flags = I2C_M_RD;
+		msg[1].len = size;
+		msg[1].buf = tmp_buf;
+		i2c_transfer(st->client->adapter, msg, ARRAY_SIZE(msg));
 #endif
-		}
 	}
-
 	return result;
 }
 
@@ -355,6 +348,33 @@
 	return MPU_SUCCESS;
 }
 
+int icm20602_int_status(struct inv_icm20602_state *st,
+	u8 *int_status)
+{
+	return icm20602_read_reg(st,
+		reg_set_20602.INT_STATUS.address, int_status);
+}
+
+int icm20602_int_wm_status(struct inv_icm20602_state *st,
+	u8 *int_status)
+{
+	return icm20602_read_reg(st,
+		reg_set_20602.FIFO_WM_INT_STATUS.address, int_status);
+}
+
+int icm20602_fifo_count(struct inv_icm20602_state *st,
+	u16 *fifo_count)
+{
+	u8 count_h, count_l;
+
+	*fifo_count = 0;
+	icm20602_read_reg(st, reg_set_20602.FIFO_COUNTH.address, &count_h);
+	icm20602_read_reg(st, reg_set_20602.FIFO_COUNTL.address, &count_l);
+	*fifo_count |= (count_h << 8);
+	*fifo_count |= count_l;
+	return MPU_SUCCESS;
+}
+
 static int icm20602_config_waterlevel(struct inv_icm20602_state *st)
 {
 	struct icm20602_user_config *config = NULL;
@@ -809,6 +829,7 @@
 	int result = MPU_SUCCESS;
 	struct icm20602_user_config *config = NULL;
 	int package_count;
+	int i;
 
 	config = st->config;
 	if (st == NULL || st->config == NULL) {
@@ -860,7 +881,7 @@
 	/* buffer malloc */
 	package_count = config->fifo_waterlevel / ICM20602_PACKAGE_SIZE;
 
-	st->buf = kzalloc(sizeof(config->fifo_waterlevel * 2), GFP_ATOMIC);
+	st->buf = kzalloc(config->fifo_waterlevel * 2, GFP_ATOMIC);
 	if (!st->buf)
 		return -ENOMEM;
 
@@ -869,9 +890,26 @@
 	if (!st->data_push)
 		return -ENOMEM;
 
+	for (i = 0; i < package_count; i++) {
+		st->data_push[i].raw_data =
+			kzalloc(ICM20602_PACKAGE_SIZE, GFP_ATOMIC);
+	}
+
 	return result;
 }
 
+int icm20602_reset_fifo(struct inv_icm20602_state *st)
+{
+	reg_set_20602.USER_CTRL.reg_u.REG.FIFO_RST = 0x1;
+	if (icm20602_write_reg_simple(st, reg_set_20602.USER_CTRL)) {
+		reg_set_20602.USER_CTRL.reg_u.REG.FIFO_RST = 0x0;
+		return -MPU_FAIL;
+	}
+	reg_set_20602.USER_CTRL.reg_u.REG.FIFO_RST = 0x0;
+	return MPU_SUCCESS;
+}
+
+
 void icm20602_rw_test(struct inv_icm20602_state *st)
 {
 	uint8_t val = 0;
diff --git a/drivers/iio/imu/inv_icm20602/inv_icm20602_core.c b/drivers/iio/imu/inv_icm20602/inv_icm20602_core.c
index 0f7fc92..15df447 100644
--- a/drivers/iio/imu/inv_icm20602/inv_icm20602_core.c
+++ b/drivers/iio/imu/inv_icm20602/inv_icm20602_core.c
@@ -41,12 +41,12 @@
 {
 	struct icm20602_user_config *config = st->config;
 
-	config->user_fps_in_ms = 10;
+	config->user_fps_in_ms = 20;
 	config->gyro_lpf = INV_ICM20602_GYRO_LFP_92HZ;
 	config->gyro_fsr = ICM20602_GYRO_FSR_1000DPS;
 	config->acc_lpf = ICM20602_ACCLFP_99;
 	config->acc_fsr = ICM20602_ACC_FSR_4G;
-	config->gyro_accel_sample_rate = ICM20602_SAMPLE_RATE_100HZ;
+	config->gyro_accel_sample_rate = ICM20602_SAMPLE_RATE_200HZ;
 	config->fifo_enabled = true;
 
 }
diff --git a/drivers/iio/imu/inv_icm20602/inv_icm20602_iio.h b/drivers/iio/imu/inv_icm20602/inv_icm20602_iio.h
index 943fc1e..9ea5ae5 100644
--- a/drivers/iio/imu/inv_icm20602/inv_icm20602_iio.h
+++ b/drivers/iio/imu/inv_icm20602/inv_icm20602_iio.h
@@ -180,7 +180,7 @@
 	uint32_t user_fps_in_ms;
 
 	bool fifo_enabled;
-	uint32_t fifo_waterlevel;
+	uint16_t fifo_waterlevel;
 	struct X_Y_Z wake_on_motion;
 };
 
@@ -257,19 +257,21 @@
 
 struct struct_icm20602_data {
 	s64 timestamps;
-	struct struct_icm20602_raw_data raw_data;
-	struct struct_icm20602_real_data real_data;
+	u8 *raw_data;
 };
 
 extern struct iio_trigger *inv_trig;
 irqreturn_t inv_icm20602_irq_handler(int irq, void *p);
 irqreturn_t inv_icm20602_read_fifo_fn(int irq, void *p);
-int inv_icm20602_reset_fifo(struct iio_dev *indio_dev);
 
 int inv_icm20602_probe_trigger(struct iio_dev *indio_dev);
 void inv_icm20602_remove_trigger(struct inv_icm20602_state *st);
 int inv_icm20602_validate_trigger(struct iio_dev *indio_dev,
 				struct iio_trigger *trig);
+int icm20602_int_status(struct inv_icm20602_state *st, u8 *int_status);
+int icm20602_int_wm_status(struct inv_icm20602_state *st, u8 *int_status);
+int icm20602_reset_fifo(struct inv_icm20602_state *st);
+int icm20602_fifo_count(struct inv_icm20602_state *st, u16 *fifo_count);
 
 int icm20602_read_raw(struct inv_icm20602_state *st,
 		struct struct_icm20602_real_data *real_data, uint32_t type);
diff --git a/drivers/iio/imu/inv_icm20602/inv_icm20602_ring.c b/drivers/iio/imu/inv_icm20602/inv_icm20602_ring.c
index b0f93be..de02656 100644
--- a/drivers/iio/imu/inv_icm20602/inv_icm20602_ring.c
+++ b/drivers/iio/imu/inv_icm20602/inv_icm20602_ring.c
@@ -40,15 +40,6 @@
 	spin_unlock_irqrestore(&st->time_stamp_lock, flags);
 }
 
-int inv_icm20602_reset_fifo(struct iio_dev *indio_dev)
-{
-	int result;
-	//u8 d;
-	//struct inv_icm20602_state  *st = iio_priv(indio_dev);
-
-	return result;
-}
-
 /*
  * inv_icm20602_irq_handler() - Cache a timestamp at each data ready interrupt.
  */
@@ -67,44 +58,50 @@
 	return IRQ_WAKE_THREAD;
 }
 
+#define BIT_FIFO_OFLOW_INT			0x10
+#define BIT_FIFO_WM_INT				0x40
 static int inv_icm20602_read_data(struct iio_dev *indio_dev)
 {
 	int result = MPU_SUCCESS;
 	struct inv_icm20602_state *st = iio_priv(indio_dev);
 	struct icm20602_user_config *config = st->config;
 	int package_count;
-	//char *buf = st->buf;
-	//struct struct_icm20602_data *data_push = st->data_push;
+	char *buf = st->buf;
+	struct struct_icm20602_data *data_push = st->data_push;
 	s64 timestamp;
+	u8 int_status, int_wm_status;
+	u16 fifo_count;
 	int i;
 
 	if (!st)
 		return -MPU_FAIL;
 	package_count = config->fifo_waterlevel / ICM20602_PACKAGE_SIZE;
 	mutex_lock(&indio_dev->mlock);
-	if (config->fifo_enabled) {
-		result = icm20602_read_fifo(st,
-				st->buf, config->fifo_waterlevel);
-		if (result != config->fifo_waterlevel) {
-			pr_err("icm20602 read fifo failed, result = %d\n",
-				result);
-			goto flush_fifo;
-		}
-
-		for (i = 0; i < package_count; i++) {
-			memcpy((char *)(&st->data_push[i].raw_data),
-				st->buf, ICM20602_PACKAGE_SIZE);
-			result = kfifo_out(&st->timestamps,
-				&timestamp, 1);
-			/* when there is no timestamp, put it as 0 */
-			if (result == 0)
-				timestamp = 0;
-			st->data_push[i].timestamps = timestamp;
-			iio_push_to_buffers(indio_dev, st->data_push+i);
-			st->buf += ICM20602_PACKAGE_SIZE;
-		}
+	icm20602_int_status(st, &int_status);
+	if (int_status & BIT_FIFO_OFLOW_INT) {
+		icm20602_fifo_count(st, &fifo_count);
+		pr_debug("fifo_count = %d\n", fifo_count);
+		icm20602_reset_fifo(st);
+		goto end_session;
 	}
-//end_session:
+	if (config->fifo_enabled) {
+		result = kfifo_out(&st->timestamps,
+				&timestamp, 1);
+		/* when there is no timestamp, put it as 0 */
+		if (result == 0)
+		timestamp = 0;
+		for (i = 0; i < package_count; i++) {
+			result = icm20602_read_fifo(st,
+			buf, ICM20602_PACKAGE_SIZE);
+			memcpy(st->data_push[i].raw_data,
+			buf, ICM20602_PACKAGE_SIZE);
+			iio_push_to_buffers_with_timestamp(indio_dev,
+			st->data_push[i].raw_data, timestamp);
+			buf += ICM20602_PACKAGE_SIZE;
+		}
+		memset(st->buf, 0, config->fifo_waterlevel);
+	}
+end_session:
 	mutex_unlock(&indio_dev->mlock);
 	iio_trigger_notify_done(indio_dev->trig);
 	return MPU_SUCCESS;
@@ -112,7 +109,7 @@
 flush_fifo:
 	/* Flush HW and SW FIFOs. */
 	inv_clear_kfifo(st);
-	inv_icm20602_reset_fifo(indio_dev);
+	icm20602_reset_fifo(st);
 	mutex_unlock(&indio_dev->mlock);
 	iio_trigger_notify_done(indio_dev->trig);
 	return MPU_SUCCESS;
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
index 2f0f448..b0e9fe6 100644
--- a/drivers/irqchip/irq-gic-v3.c
+++ b/drivers/irqchip/irq-gic-v3.c
@@ -44,6 +44,34 @@
 
 #include "irq-gic-common.h"
 
+#define MAX_IRQ			1020U	/* Max number of SGI+PPI+SPI */
+#define SPI_START_IRQ		32	/* SPI start irq number */
+#define GICD_ICFGR_BITS		2	/* 2 bits per irq in GICD_ICFGR */
+#define GICD_ISENABLER_BITS	1	/* 1 bit per irq in GICD_ISENABLER */
+#define GICD_IPRIORITYR_BITS	8	/* 8 bits per irq in GICD_IPRIORITYR */
+
+/* 32 bit mask with lower n bits set */
+#define UMASK_LOW(n)		(~0U >> (32 - (n)))
+
+/* Number of 32-bit words required to store all irqs, for
+ * registers where each word stores configuration for each irq
+ * in bits_per_irq bits.
+ */
+#define NUM_IRQ_WORDS(bits_per_irq)	(DIV_ROUND_UP(MAX_IRQ, \
+						      32 / (bits_per_irq)))
+#define MAX_IRQS_IGNORE		10
+
+#define IRQ_NR_BOUND(nr)	min((nr), MAX_IRQ)
+
+/* Bitmap to irqs, which are restored */
+static DECLARE_BITMAP(irqs_restore, MAX_IRQ);
+
+/* Bitmap to irqs, for which restore is ignored.
+ * Presently, only GICD_IROUTER mismatches are
+ * ignored.
+ */
+static DECLARE_BITMAP(irqs_ignore_restore, MAX_IRQ);
+
 struct redist_region {
 	void __iomem		*redist_base;
 	phys_addr_t		phys_base;
@@ -60,6 +88,16 @@
 	u32			nr_redist_regions;
 	unsigned int		irq_nr;
 	struct partition_desc	*ppi_descs[16];
+
+	u64 saved_spi_router[MAX_IRQ];
+	u32 saved_spi_enable[NUM_IRQ_WORDS(GICD_ISENABLER_BITS)];
+	u32 saved_spi_cfg[NUM_IRQ_WORDS(GICD_ICFGR_BITS)];
+	u32 saved_spi_priority[NUM_IRQ_WORDS(GICD_IPRIORITYR_BITS)];
+
+	u64 changed_spi_router[MAX_IRQ];
+	u32 changed_spi_enable[NUM_IRQ_WORDS(GICD_ISENABLER_BITS)];
+	u32 changed_spi_cfg[NUM_IRQ_WORDS(GICD_ICFGR_BITS)];
+	u32 changed_spi_priority[NUM_IRQ_WORDS(GICD_IPRIORITYR_BITS)];
 };
 
 static struct gic_chip_data gic_data __read_mostly;
@@ -67,6 +105,58 @@
 
 static struct gic_kvm_info gic_v3_kvm_info;
 
+enum gicd_save_restore_reg {
+	SAVED_ICFGR,
+	SAVED_IS_ENABLER,
+	SAVED_IPRIORITYR,
+	NUM_SAVED_GICD_REGS,
+};
+
+/* Stores start address of spi config for saved gicd regs */
+static u32 *saved_spi_regs_start[NUM_SAVED_GICD_REGS] = {
+	[SAVED_ICFGR] = gic_data.saved_spi_cfg,
+	[SAVED_IS_ENABLER] = gic_data.saved_spi_enable,
+	[SAVED_IPRIORITYR] = gic_data.saved_spi_priority,
+};
+
+/* Stores start address of spi config for changed gicd regs */
+static u32 *changed_spi_regs_start[NUM_SAVED_GICD_REGS] = {
+	[SAVED_ICFGR] = gic_data.changed_spi_cfg,
+	[SAVED_IS_ENABLER] = gic_data.changed_spi_enable,
+	[SAVED_IPRIORITYR] = gic_data.changed_spi_priority,
+};
+
+/* GICD offset for saved registers */
+static u32 gicd_offset[NUM_SAVED_GICD_REGS] = {
+	[SAVED_ICFGR] = GICD_ICFGR,
+	[SAVED_IS_ENABLER] = GICD_ISENABLER,
+	[SAVED_IPRIORITYR] = GICD_IPRIORITYR,
+};
+
+/* Bits per irq word, for gicd saved registers */
+static u32 gicd_reg_bits_per_irq[NUM_SAVED_GICD_REGS] = {
+	[SAVED_ICFGR] = GICD_ICFGR_BITS,
+	[SAVED_IS_ENABLER] = GICD_ISENABLER_BITS,
+	[SAVED_IPRIORITYR] = GICD_IPRIORITYR_BITS,
+};
+
+#define for_each_spi_irq_word(i, reg) \
+	for (i = 0; \
+	    i < DIV_ROUND_UP(IRQ_NR_BOUND(gic_data.irq_nr) - SPI_START_IRQ, \
+			     32 / gicd_reg_bits_per_irq[reg]); \
+	    i++)
+
+#define read_spi_word_offset(base, reg, i) \
+	readl_relaxed_no_log(	\
+			base + gicd_offset[reg] + i * 4 +	\
+			SPI_START_IRQ * gicd_reg_bits_per_irq[reg] / 8)
+
+#define restore_spi_word_offset(base, reg, i) \
+	writel_relaxed_no_log(	\
+			saved_spi_regs_start[reg][i],\
+			base + gicd_offset[reg] + i * 4 +	\
+			SPI_START_IRQ * gicd_reg_bits_per_irq[reg] / 8)
+
 #define gic_data_rdist()		(this_cpu_ptr(gic_data.rdists.rdist))
 #define gic_data_rdist_rd_base()	(gic_data_rdist()->rd_base)
 #define gic_data_rdist_sgi_base()	(gic_data_rdist_rd_base() + SZ_64K)
@@ -134,6 +224,229 @@
 }
 #endif
 
+void gic_v3_dist_save(void)
+{
+	void __iomem *base = gic_data.dist_base;
+	int reg, i;
+
+	for (reg = SAVED_ICFGR; reg < NUM_SAVED_GICD_REGS; reg++) {
+		for_each_spi_irq_word(i, reg) {
+			saved_spi_regs_start[reg][i] =
+				read_spi_word_offset(base, reg, i);
+		}
+	}
+
+	for (i = 32; i < IRQ_NR_BOUND(gic_data.irq_nr); i++)
+		gic_data.saved_spi_router[i] =
+			gic_read_irouter(base + GICD_IROUTER + i * 8);
+}
+
+static void _gicd_check_reg(enum gicd_save_restore_reg reg)
+{
+	void __iomem *base = gic_data.dist_base;
+	u32 *saved_spi_cfg = saved_spi_regs_start[reg];
+	u32 *changed_spi_cfg = changed_spi_regs_start[reg];
+	u32 bits_per_irq = gicd_reg_bits_per_irq[reg];
+	u32 current_cfg = 0;
+	int i, j = SPI_START_IRQ, l;
+	u32 k;
+
+	for_each_spi_irq_word(i, reg) {
+		current_cfg = read_spi_word_offset(base, reg, i);
+		if (current_cfg != saved_spi_cfg[i]) {
+			for (k = current_cfg ^ saved_spi_cfg[i],
+			     l = 0; k ; k >>= bits_per_irq, l++) {
+				if (k & UMASK_LOW(bits_per_irq))
+					set_bit(j+l, irqs_restore);
+			}
+			changed_spi_cfg[i] = current_cfg ^ saved_spi_cfg[i];
+		}
+		j += 32 / bits_per_irq;
+	}
+}
+
+#define _gic_v3_dist_check_icfgr()	\
+		_gicd_check_reg(SAVED_ICFGR)
+#define _gic_v3_dist_check_ipriorityr()	\
+		_gicd_check_reg(SAVED_IPRIORITYR)
+#define _gic_v3_dist_check_isenabler()	\
+		_gicd_check_reg(SAVED_IS_ENABLER)
+
+static void _gic_v3_dist_check_irouter(void)
+{
+	void __iomem *base = gic_data.dist_base;
+	u64 current_irouter_cfg = 0;
+	int i;
+
+	for (i = 32; i < IRQ_NR_BOUND(gic_data.irq_nr); i++) {
+		if (test_bit(i, irqs_ignore_restore))
+			continue;
+		current_irouter_cfg = gic_read_irouter(
+					base + GICD_IROUTER + i * 8);
+		if (current_irouter_cfg != gic_data.saved_spi_router[i]) {
+			set_bit(i, irqs_restore);
+			gic_data.changed_spi_router[i] =
+			    current_irouter_cfg ^ gic_data.saved_spi_router[i];
+		}
+	}
+}
+
+static void _gic_v3_dist_restore_reg(enum gicd_save_restore_reg reg)
+{
+	void __iomem *base = gic_data.dist_base;
+	int i;
+
+	for_each_spi_irq_word(i, reg) {
+		if (changed_spi_regs_start[reg][i])
+			restore_spi_word_offset(base, reg, i);
+	}
+
+	/* Commit all restored configurations before subsequent writes */
+	wmb();
+}
+
+#define _gic_v3_dist_restore_icfgr()	_gic_v3_dist_restore_reg(SAVED_ICFGR)
+#define _gic_v3_dist_restore_ipriorityr()		\
+		_gic_v3_dist_restore_reg(SAVED_IPRIORITYR)
+
+static void _gic_v3_dist_restore_set_reg(u32 offset)
+{
+	void __iomem *base = gic_data.dist_base;
+	int i, j = SPI_START_IRQ, l;
+	int irq_nr = IRQ_NR_BOUND(gic_data.irq_nr) - SPI_START_IRQ;
+
+	for (i = 0; i < DIV_ROUND_UP(irq_nr, 32); i++, j += 32) {
+		u32 reg_val = readl_relaxed_no_log(base + offset + i * 4 + 4);
+		bool irqs_restore_updated = 0;
+
+		for (l = 0; l < 32; l++) {
+			if (test_bit(j+l, irqs_restore)) {
+				reg_val |= BIT(l);
+				irqs_restore_updated = 1;
+			}
+		}
+
+		if (irqs_restore_updated) {
+			writel_relaxed_no_log(
+				reg_val, base + offset + i * 4 + 4);
+		}
+	}
+
+	/* Commit restored configuration updates before subsequent writes */
+	wmb();
+}
+
+#define _gic_v3_dist_restore_isenabler()		\
+		_gic_v3_dist_restore_set_reg(GICD_ISENABLER)
+
+#define _gic_v3_dist_restore_ispending()		\
+		_gic_v3_dist_restore_set_reg(GICD_ISPENDR)
+
+static void _gic_v3_dist_restore_irouter(void)
+{
+	void __iomem *base = gic_data.dist_base;
+	int i;
+
+	for (i = 32; i < IRQ_NR_BOUND(gic_data.irq_nr); i++) {
+		if (test_bit(i, irqs_ignore_restore))
+			continue;
+		if (gic_data.changed_spi_router[i]) {
+			gic_write_irouter(gic_data.saved_spi_router[i],
+						base + GICD_IROUTER + i * 8);
+		}
+	}
+
+	/* Commit GICD_IROUTER writes before subsequent writes */
+	wmb();
+}
+
+static void _gic_v3_dist_clear_reg(u32 offset)
+{
+	void __iomem *base = gic_data.dist_base;
+	int i, j = SPI_START_IRQ, l;
+	int irq_nr = IRQ_NR_BOUND(gic_data.irq_nr) - SPI_START_IRQ;
+
+	for (i = 0; i < DIV_ROUND_UP(irq_nr, 32); i++, j += 32) {
+		u32 clear = 0;
+		bool irqs_restore_updated = 0;
+
+		for (l = 0; l < 32; l++) {
+			if (test_bit(j+l, irqs_restore)) {
+				clear |= BIT(l);
+				irqs_restore_updated = 1;
+			}
+		}
+
+		if (irqs_restore_updated) {
+			writel_relaxed_no_log(
+				clear, base + offset + i * 4 + 4);
+		}
+	}
+
+	/* Commit clearing of irq config before subsequent writes */
+	wmb();
+}
+
+#define _gic_v3_dist_set_icenabler()		\
+		_gic_v3_dist_clear_reg(GICD_ICENABLER)
+
+#define _gic_v3_dist_set_icpending()		\
+		_gic_v3_dist_clear_reg(GICD_ICPENDR)
+
+#define _gic_v3_dist_set_icactive()		\
+		_gic_v3_dist_clear_reg(GICD_ICACTIVER)
+
+/* Restore GICD state for SPIs. SPI configuration is restored
+ * for GICD_ICFGR, GICD_ISENABLER, GICD_IPRIORITYR, GICD_IROUTER
+ * registers. Following is the sequence for restore:
+ *
+ * 1. For SPIs, check whether any of GICD_ICFGR, GICD_ISENABLER,
+ *    GICD_IPRIORITYR, GICD_IROUTER, current configuration is
+ *    different from saved configuration.
+ *
+ * For all irqs, with mismatched configurations,
+ *
+ * 2. Set GICD_ICENABLER and wait for its completion.
+ *
+ * 3. Restore any changed GICD_ICFGR, GICD_IPRIORITYR, GICD_IROUTER
+ *    configurations.
+ *
+ * 4. Set GICD_ICACTIVER.
+ *
+ * 5. Set pending for the interrupt.
+ *
+ * 6. Enable interrupt and wait for its completion.
+ *
+ */
+void gic_v3_dist_restore(void)
+{
+	_gic_v3_dist_check_icfgr();
+	_gic_v3_dist_check_ipriorityr();
+	_gic_v3_dist_check_isenabler();
+	_gic_v3_dist_check_irouter();
+
+	if (bitmap_empty(irqs_restore, IRQ_NR_BOUND(gic_data.irq_nr)))
+		return;
+
+	_gic_v3_dist_set_icenabler();
+	gic_dist_wait_for_rwp();
+
+	_gic_v3_dist_restore_icfgr();
+	_gic_v3_dist_restore_ipriorityr();
+	_gic_v3_dist_restore_irouter();
+
+	_gic_v3_dist_set_icactive();
+
+	_gic_v3_dist_set_icpending();
+	_gic_v3_dist_restore_ispending();
+
+	_gic_v3_dist_restore_isenabler();
+	gic_dist_wait_for_rwp();
+
+	/* Commit all writes before proceeding */
+	wmb();
+}
+
 /*
  * gic_show_pending_irq - Shows the pending interrupts
  * Note: Interrupts should be disabled on the cpu from which
@@ -1244,7 +1557,8 @@
 	struct redist_region *rdist_regs;
 	u64 redist_stride;
 	u32 nr_redist_regions;
-	int err, i;
+	int err, i, ignore_irqs_len;
+	u32 ignore_restore_irqs[MAX_IRQS_IGNORE] = {0};
 
 	dist_base = of_iomap(node, 0);
 	if (!dist_base) {
@@ -1294,6 +1608,14 @@
 
 	gic_populate_ppi_partitions(node);
 	gic_of_setup_kvm_info(node);
+
+	ignore_irqs_len = of_property_read_variable_u32_array(node,
+				"ignored-save-restore-irqs",
+				ignore_restore_irqs,
+				0, MAX_IRQS_IGNORE);
+	for (i = 0; i < ignore_irqs_len; i++)
+		set_bit(ignore_restore_irqs[i], irqs_ignore_restore);
+
 	return 0;
 
 out_unmap_rdist:
diff --git a/drivers/leds/leds-qpnp-haptics.c b/drivers/leds/leds-qpnp-haptics.c
index 764657a..8a850c5 100644
--- a/drivers/leds/leds-qpnp-haptics.c
+++ b/drivers/leds/leds-qpnp-haptics.c
@@ -24,6 +24,7 @@
 #include <linux/platform_device.h>
 #include <linux/pwm.h>
 #include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
 #include <linux/slab.h>
 #include <linux/qpnp/qpnp-misc.h>
 #include <linux/qpnp/qpnp-revid.h>
@@ -321,6 +322,7 @@
 	int				sc_irq;
 	struct pwm_param		pwm_data;
 	struct hap_lra_ares_param	ares_cfg;
+	struct regulator		*vcc_pon;
 	u32				play_time_ms;
 	u32				max_play_time_ms;
 	u32				vmax_mv;
@@ -355,6 +357,7 @@
 	bool				lra_auto_mode;
 	bool				play_irq_en;
 	bool				auto_res_err_recovery_hw;
+	bool				vcc_pon_enabled;
 };
 
 static int qpnp_haptics_parse_buffer_dt(struct hap_chip *chip);
@@ -801,10 +804,29 @@
 
 	enable = atomic_read(&chip->state);
 	pr_debug("state: %d\n", enable);
+
+	if (chip->vcc_pon && enable && !chip->vcc_pon_enabled) {
+		rc = regulator_enable(chip->vcc_pon);
+		if (rc < 0)
+			pr_err("%s: could not enable vcc_pon regulator rc=%d\n",
+				 __func__, rc);
+		else
+			chip->vcc_pon_enabled = true;
+	}
+
 	rc = qpnp_haptics_play(chip, enable);
 	if (rc < 0)
 		pr_err("Error in %sing haptics, rc=%d\n",
 			enable ? "play" : "stopp", rc);
+
+	if (chip->vcc_pon && !enable && chip->vcc_pon_enabled) {
+		rc = regulator_disable(chip->vcc_pon);
+		if (rc)
+			pr_err("%s: could not disable vcc_pon regulator rc=%d\n",
+				 __func__, rc);
+		else
+			chip->vcc_pon_enabled = false;
+	}
 }
 
 static enum hrtimer_restart hap_stop_timer(struct hrtimer *timer)
@@ -2054,6 +2076,7 @@
 	struct device_node *revid_node, *misc_node;
 	const char *temp_str;
 	int rc, temp;
+	struct regulator *vcc_pon;
 
 	rc = of_property_read_u32(node, "reg", &temp);
 	if (rc < 0) {
@@ -2381,6 +2404,16 @@
 	else if (chip->play_mode == HAP_PWM)
 		rc = qpnp_haptics_parse_pwm_dt(chip);
 
+	if (of_find_property(node, "vcc_pon-supply", NULL)) {
+		vcc_pon = regulator_get(&chip->pdev->dev, "vcc_pon");
+		if (IS_ERR(vcc_pon)) {
+			rc = PTR_ERR(vcc_pon);
+			dev_err(&chip->pdev->dev,
+				"regulator get failed vcc_pon rc=%d\n", rc);
+		}
+		chip->vcc_pon = vcc_pon;
+	}
+
 	return rc;
 }
 
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c b/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c
index ba32526..dfcb73a 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c
@@ -229,7 +229,9 @@
 static int msm_csid_reset(struct csid_device *csid_dev)
 {
 	int32_t rc = 0;
+	uint32_t irq = 0, irq_bitshift;
 
+	irq_bitshift = csid_dev->ctrl_reg->csid_reg.csid_rst_done_irq_bitshift;
 	msm_camera_io_w(csid_dev->ctrl_reg->csid_reg.csid_rst_stb_all,
 		csid_dev->base +
 		csid_dev->ctrl_reg->csid_reg.csid_rst_cmd_addr);
@@ -238,8 +240,23 @@
 	if (rc < 0) {
 		pr_err("wait_for_completion in msm_csid_reset fail rc = %d\n",
 			rc);
+	} else if (rc == 0) {
+		irq = msm_camera_io_r(csid_dev->base +
+			csid_dev->ctrl_reg->csid_reg.csid_irq_status_addr);
+		pr_err_ratelimited("%s CSID%d_IRQ_STATUS_ADDR = 0x%x\n",
+			__func__, csid_dev->pdev->id, irq);
+		if (irq & (0x1 << irq_bitshift)) {
+			rc = 1;
+			CDBG("%s succeeded", __func__);
+		} else {
+			rc = 0;
+			pr_err("%s reset csid_irq_status failed = 0x%x\n",
+				__func__, irq);
+		}
 		if (rc == 0)
 			rc = -ETIMEDOUT;
+	} else {
+		CDBG("%s succeeded", __func__);
 	}
 	return rc;
 }
diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c
index 9a7d272..3679c59 100644
--- a/drivers/media/platform/msm/vidc/msm_venc.c
+++ b/drivers/media/platform/msm/vidc/msm_venc.c
@@ -2275,6 +2275,7 @@
 			enable.enable = 0;
 		pdata = &enable;
 		inst->clk_data.low_latency_mode = (bool) enable.enable;
+		msm_dcvs_try_enable(inst);
 		break;
 	}
 	case V4L2_CID_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8:
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_clocks.c b/drivers/media/platform/msm/vidc/msm_vidc_clocks.c
index 6ae030f..a665978 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_clocks.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_clocks.c
@@ -609,6 +609,7 @@
 	struct allowed_clock_rates_table *allowed_clks_tbl = NULL;
 	u64 rate = 0;
 	struct clock_data *dcvs = NULL;
+	u32 operating_rate, vsp_factor_num = 10, vsp_factor_den = 7;
 
 	core = inst->core;
 	dcvs = &inst->clk_data;
@@ -631,8 +632,19 @@
 
 		vsp_cycles = mbs_per_second * inst->clk_data.entry->vsp_cycles;
 
-		/* 10 / 7 is overhead factor */
-		vsp_cycles += (inst->clk_data.bitrate * 10) / 7;
+		operating_rate = inst->clk_data.operating_rate >> 16;
+		if (operating_rate > inst->prop.fps && inst->prop.fps) {
+			vsp_factor_num *= operating_rate;
+			vsp_factor_den *= inst->prop.fps;
+		}
+		//adjust factor for 2 core case, due to workload is not
+		//equally distributed on 2 cores, use 0.65 instead of 0.5
+		if (inst->clk_data.core_id == VIDC_CORE_ID_3) {
+			vsp_factor_num = vsp_factor_num * 13 / 10;
+			vsp_factor_den *= 2;
+		}
+		vsp_cycles += ((u64)inst->clk_data.bitrate * vsp_factor_num) /
+				vsp_factor_den;
 	} else if (inst->session_type == MSM_VIDC_DECODER) {
 		vpp_cycles = mbs_per_second * inst->clk_data.entry->vpp_cycles;
 
diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c
index f7c63cf..f70420f 100644
--- a/drivers/net/wireless/ath/wil6210/cfg80211.c
+++ b/drivers/net/wireless/ath/wil6210/cfg80211.c
@@ -21,11 +21,16 @@
 #include "ftm.h"
 
 #define WIL_MAX_ROC_DURATION_MS 5000
+#define CTRY_CHINA "CN"
 
 bool disable_ap_sme;
 module_param(disable_ap_sme, bool, 0444);
 MODULE_PARM_DESC(disable_ap_sme, " let user space handle AP mode SME");
 
+static bool country_specific_board_file;
+module_param(country_specific_board_file, bool, 0444);
+MODULE_PARM_DESC(country_specific_board_file, " switch board file upon regulatory domain change (Default: false)");
+
 static bool ignore_reg_hints = true;
 module_param(ignore_reg_hints, bool, 0444);
 MODULE_PARM_DESC(ignore_reg_hints, " Ignore OTA regulatory hints (Default: true)");
@@ -1984,6 +1989,64 @@
 	return 0;
 }
 
+static int wil_switch_board_file(struct wil6210_priv *wil,
+				 const u8 *new_regdomain)
+{
+	int rc = 0;
+
+	if (!country_specific_board_file)
+		return 0;
+
+	if (memcmp(wil->regdomain, CTRY_CHINA, 2) == 0) {
+		wil_info(wil, "moving out of China reg domain, use default board file\n");
+		wil->board_file_country[0] = '\0';
+	} else if (memcmp(new_regdomain, CTRY_CHINA, 2) == 0) {
+		wil_info(wil, "moving into China reg domain, use country specific board file\n");
+		strlcpy(wil->board_file_country, CTRY_CHINA,
+			sizeof(wil->board_file_country));
+	} else {
+		return 0;
+	}
+
+	/* need to switch board file - reset the device */
+
+	mutex_lock(&wil->mutex);
+
+	if (!netif_running(wil_to_ndev(wil)) || wil_is_recovery_blocked(wil))
+		/* new board file will be used in next FW load */
+		goto out;
+
+	__wil_down(wil);
+	rc = __wil_up(wil);
+
+out:
+	mutex_unlock(&wil->mutex);
+	return rc;
+}
+
+static void wil_cfg80211_reg_notify(struct wiphy *wiphy,
+				    struct regulatory_request *request)
+{
+	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+	int rc;
+
+	wil_info(wil, "cfg reg_notify %c%c%s%s initiator %d hint_type %d\n",
+		 request->alpha2[0], request->alpha2[1],
+		 request->intersect ? " intersect" : "",
+		 request->processed ? " processed" : "",
+		 request->initiator, request->user_reg_hint_type);
+
+	if (memcmp(wil->regdomain, request->alpha2, 2) == 0)
+		/* reg domain did not change */
+		return;
+
+	rc = wil_switch_board_file(wil, request->alpha2);
+	if (rc)
+		wil_err(wil, "switch board file failed %d\n", rc);
+
+	memcpy(wil->regdomain, request->alpha2, 2);
+}
+
 static struct cfg80211_ops wil_cfg80211_ops = {
 	.add_virtual_intf = wil_cfg80211_add_iface,
 	.del_virtual_intf = wil_cfg80211_del_iface,
@@ -2055,6 +2118,8 @@
 	wiphy->mgmt_stypes = wil_mgmt_stypes;
 	wiphy->features |= NL80211_FEATURE_SK_TX_STATUS;
 
+	wiphy->reg_notifier = wil_cfg80211_reg_notify;
+
 	wiphy->n_vendor_commands = ARRAY_SIZE(wil_nl80211_vendor_commands);
 	wiphy->vendor_commands = wil_nl80211_vendor_commands;
 	wiphy->vendor_events = wil_nl80211_vendor_events;
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index 2baa6cf..f37254d 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -26,6 +26,7 @@
 
 #define WAIT_FOR_HALP_VOTE_MS 100
 #define WAIT_FOR_SCAN_ABORT_MS 1000
+#define WIL_BOARD_FILE_MAX_NAMELEN 128
 
 bool debug_fw; /* = false; */
 module_param(debug_fw, bool, 0444);
@@ -946,6 +947,30 @@
 	le32_to_cpus(&r->head);
 }
 
+/* construct actual board file name to use */
+void wil_get_board_file(struct wil6210_priv *wil, char *buf, size_t len)
+{
+	const char *board_file = WIL_BOARD_FILE_NAME;
+	const char *ext;
+	int prefix_len;
+
+	if (wil->board_file_country[0] == '\0') {
+		strlcpy(buf, board_file, len);
+		return;
+	}
+
+	/* use country specific board file */
+	if (len < strlen(board_file) + 4 /* for _XX and terminating null */)
+		return;
+
+	ext = strrchr(board_file, '.');
+	prefix_len = (ext ? ext - board_file : strlen(board_file));
+	snprintf(buf, len, "%.*s_%.2s",
+		 prefix_len, board_file, wil->board_file_country);
+	if (ext)
+		strlcat(buf, ext, len);
+}
+
 static int wil_get_bl_info(struct wil6210_priv *wil)
 {
 	struct net_device *ndev = wil_to_ndev(wil);
@@ -1260,8 +1285,12 @@
 
 	wil_set_oob_mode(wil, oob_mode);
 	if (load_fw) {
+		char board_file[WIL_BOARD_FILE_MAX_NAMELEN];
+
+		board_file[0] = '\0';
+		wil_get_board_file(wil, board_file, sizeof(board_file));
 		wil_info(wil, "Use firmware <%s> + board <%s>\n",
-			 wil->wil_fw_name, WIL_BOARD_FILE_NAME);
+			 wil->wil_fw_name, board_file);
 
 		if (!no_flash)
 			wil_bl_prepare_halt(wil);
@@ -1273,11 +1302,9 @@
 		if (rc)
 			goto out;
 		if (wil->brd_file_addr)
-			rc = wil_request_board(wil, WIL_BOARD_FILE_NAME);
+			rc = wil_request_board(wil, board_file);
 		else
-			rc = wil_request_firmware(wil,
-						  WIL_BOARD_FILE_NAME,
-						  true);
+			rc = wil_request_firmware(wil, board_file, true);
 		if (rc)
 			goto out;
 
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index f4476ee..2b71deb 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -683,6 +683,7 @@
 	const char *hw_name;
 	const char *wil_fw_name;
 	char *board_file;
+	char board_file_country[3]; /* alpha2 */
 	u32 brd_file_addr;
 	u32 brd_file_max_size;
 	DECLARE_BITMAP(hw_capa, hw_capa_last);
@@ -796,6 +797,8 @@
 	} snr_thresh;
 
 	int fw_calib_result;
+	/* current reg domain configured in kernel */
+	char regdomain[3]; /* alpha2 */
 
 #ifdef CONFIG_PM
 	struct notifier_block pm_notify;
@@ -873,6 +876,8 @@
 	wil_w(wil, reg, wil_r(wil, reg) & ~val);
 }
 
+void wil_get_board_file(struct wil6210_priv *wil, char *buf, size_t len);
+
 #if defined(CONFIG_DYNAMIC_DEBUG)
 #define wil_hex_dump_txrx(prefix_str, prefix_type, rowsize,	\
 			  groupsize, buf, len, ascii)		\
diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c
index 888fdbc..dc91a1b 100644
--- a/drivers/of/dynamic.c
+++ b/drivers/of/dynamic.c
@@ -16,6 +16,11 @@
 
 #include "of_private.h"
 
+static struct device_node *kobj_to_device_node(struct kobject *kobj)
+{
+	return container_of(kobj, struct device_node, kobj);
+}
+
 /**
  * of_node_get() - Increment refcount of a node
  * @node:	Node to inc refcount, NULL is supported to simplify writing of
diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h
index c4d7fdc..61acd6b 100644
--- a/drivers/of/of_private.h
+++ b/drivers/of/of_private.h
@@ -35,12 +35,6 @@
 extern struct list_head aliases_lookup;
 extern struct kset *of_kset;
 
-
-static inline struct device_node *kobj_to_device_node(struct kobject *kobj)
-{
-	return container_of(kobj, struct device_node, kobj);
-}
-
 #if defined(CONFIG_OF_DYNAMIC)
 extern int of_property_notify(int action, struct device_node *np,
 			      struct property *prop, struct property *old_prop);
diff --git a/drivers/platform/msm/gsi/Makefile b/drivers/platform/msm/gsi/Makefile
index b350a59..1eed995 100644
--- a/drivers/platform/msm/gsi/Makefile
+++ b/drivers/platform/msm/gsi/Makefile
@@ -1 +1,8 @@
 obj-$(CONFIG_GSI) += gsi.o gsi_dbg.o
+
+ifdef CONFIG_X86
+ccflags-y += -DIPA_EMULATION_COMPILE=1
+obj-$(CONFIG_GSI) += gsi_emulation.o
+else
+ccflags-y += -DIPA_EMULATION_COMPILE=0
+endif
diff --git a/drivers/platform/msm/gsi/gsi.c b/drivers/platform/msm/gsi/gsi.c
index e729c56..e80a1fb 100644
--- a/drivers/platform/msm/gsi/gsi.c
+++ b/drivers/platform/msm/gsi/gsi.c
@@ -20,6 +20,7 @@
 #include <linux/delay.h>
 #include "gsi.h"
 #include "gsi_reg.h"
+#include "gsi_emulation.h"
 
 #define GSI_CMD_TIMEOUT (5*HZ)
 #define GSI_STOP_CMD_TIMEOUT_MS 20
@@ -33,6 +34,8 @@
 	{ },
 };
 
+static bool running_emulation = IPA_EMULATION_COMPILE;
+
 struct gsi_ctx *gsi_ctx;
 
 static void __gsi_config_type_irq(int ee, uint32_t mask, uint32_t val)
@@ -577,7 +580,7 @@
 		if (!type)
 			break;
 
-		GSIDBG_LOW("type %x\n", type);
+		GSIDBG_LOW("type 0x%x\n", type);
 
 		if (type & GSI_EE_n_CNTXT_TYPE_IRQ_CH_CTRL_BMSK)
 			gsi_handle_ch_ctrl(ee);
@@ -777,17 +780,57 @@
 			GSIERR("bad irq specified %u\n", props->irq);
 			return -GSI_STATUS_INVALID_PARAMS;
 		}
-
-		res = devm_request_irq(gsi_ctx->dev, props->irq,
+		/*
+		 * On a real UE, there are two separate interrupt
+		 * vectors that get directed toward the GSI/IPA
+		 * drivers.  They are handled by gsi_isr() and
+		 * (ipa_isr() or ipa3_isr()) respectively.  In the
+		 * emulation environment, this is not the case;
+		 * instead, interrupt vectors are routed to the
+		 * emualation hardware's interrupt controller, who in
+		 * turn, forwards a single interrupt to the GSI/IPA
+		 * driver.  When the new interrupt vector is received,
+		 * the driver needs to probe the interrupt
+		 * controller's registers so see if one, the other, or
+		 * both interrupts have occurred.  Given the above, we
+		 * now need to handle both situations, namely: the
+		 * emulator's and the real UE.
+		 */
+		if (running_emulation) {
+			/*
+			 * New scheme involving the emulator's
+			 * interrupt controller.
+			 */
+			res = devm_request_threaded_irq(
+				gsi_ctx->dev,
+				props->irq,
+				/* top half handler to follow */
+				emulator_hard_irq_isr,
+				/* threaded bottom half handler to follow */
+				emulator_soft_irq_isr,
+				IRQF_SHARED,
+				"emulator_intcntrlr",
+				gsi_ctx);
+		} else {
+			/*
+			 * Traditional scheme used on the real UE.
+			 */
+			res = devm_request_irq(gsi_ctx->dev, props->irq,
 				(irq_handler_t) gsi_isr,
 				props->req_clk_cb ? IRQF_TRIGGER_RISING :
 					IRQF_TRIGGER_HIGH,
 				"gsi",
 				gsi_ctx);
+		}
 		if (res) {
-			GSIERR("failed to register isr for %u\n", props->irq);
+			GSIERR(
+			 "failed to register isr for %u\n",
+			 props->irq);
 			return -GSI_STATUS_ERROR;
 		}
+		GSIDBG(
+			"succeeded to register isr for %u\n",
+			props->irq);
 
 		res = enable_irq_wake(props->irq);
 		if (res)
@@ -808,6 +851,41 @@
 		return -GSI_STATUS_RES_ALLOC_FAILURE;
 	}
 
+	GSIDBG("GSI base(%pa) mapped to (%pK) with len (0x%lx)\n",
+	       &(props->phys_addr),
+	       gsi_ctx->base,
+	       props->size);
+
+	if (running_emulation) {
+		GSIDBG("GSI SW ver register value 0x%x\n",
+		       gsi_readl(gsi_ctx->base +
+		       GSI_EE_n_GSI_SW_VERSION_OFFS(0)));
+		gsi_ctx->intcntrlr_mem_size =
+		    props->emulator_intcntrlr_size;
+		gsi_ctx->intcntrlr_base =
+		    devm_ioremap_nocache(
+			gsi_ctx->dev,
+			props->emulator_intcntrlr_addr,
+			props->emulator_intcntrlr_size);
+		if (!gsi_ctx->intcntrlr_base) {
+			GSIERR(
+			  "failed to remap emulator's interrupt controller HW\n");
+			devm_iounmap(gsi_ctx->dev, gsi_ctx->base);
+			devm_free_irq(gsi_ctx->dev, props->irq, gsi_ctx);
+			return -GSI_STATUS_RES_ALLOC_FAILURE;
+		}
+
+		GSIDBG(
+		    "Emulator's interrupt controller base(%pa) mapped to (%pK) with len (0x%lx)\n",
+		    &(props->emulator_intcntrlr_addr),
+		    gsi_ctx->intcntrlr_base,
+		    props->emulator_intcntrlr_size);
+
+		gsi_ctx->intcntrlr_gsi_isr = gsi_isr;
+		gsi_ctx->intcntrlr_client_isr =
+		    props->emulator_intcntrlr_client_isr;
+	}
+
 	gsi_ctx->per = *props;
 	gsi_ctx->per_registered = true;
 	mutex_init(&gsi_ctx->mlock);
@@ -816,6 +894,9 @@
 	gsi_ctx->max_ch = gsi_get_max_channels(gsi_ctx->per.ver);
 	if (gsi_ctx->max_ch == 0) {
 		devm_iounmap(gsi_ctx->dev, gsi_ctx->base);
+		if (running_emulation)
+			devm_iounmap(gsi_ctx->dev, gsi_ctx->intcntrlr_base);
+		gsi_ctx->base = gsi_ctx->intcntrlr_base = NULL;
 		devm_free_irq(gsi_ctx->dev, props->irq, gsi_ctx);
 		GSIERR("failed to get max channels\n");
 		return -GSI_STATUS_ERROR;
@@ -823,6 +904,9 @@
 	gsi_ctx->max_ev = gsi_get_max_event_rings(gsi_ctx->per.ver);
 	if (gsi_ctx->max_ev == 0) {
 		devm_iounmap(gsi_ctx->dev, gsi_ctx->base);
+		if (running_emulation)
+			devm_iounmap(gsi_ctx->dev, gsi_ctx->intcntrlr_base);
+		gsi_ctx->base = gsi_ctx->intcntrlr_base = NULL;
 		devm_free_irq(gsi_ctx->dev, props->irq, gsi_ctx);
 		GSIERR("failed to get max event rings\n");
 		return -GSI_STATUS_ERROR;
@@ -831,7 +915,9 @@
 	if (props->mhi_er_id_limits_valid &&
 	    props->mhi_er_id_limits[0] > (gsi_ctx->max_ev - 1)) {
 		devm_iounmap(gsi_ctx->dev, gsi_ctx->base);
-		gsi_ctx->base = NULL;
+		if (running_emulation)
+			devm_iounmap(gsi_ctx->dev, gsi_ctx->intcntrlr_base);
+		gsi_ctx->base = gsi_ctx->intcntrlr_base = NULL;
 		devm_free_irq(gsi_ctx->dev, props->irq, gsi_ctx);
 		GSIERR("MHI event ring start id %u is beyond max %u\n",
 			props->mhi_er_id_limits[0], gsi_ctx->max_ev);
@@ -872,6 +958,22 @@
 		gsi_writel(0, gsi_ctx->base +
 			GSI_EE_n_ERROR_LOG_OFFS(gsi_ctx->per.ee));
 
+	if (running_emulation) {
+		/*
+		 * Set up the emulator's interrupt controller...
+		 */
+		res = setup_emulator_cntrlr(
+		    gsi_ctx->intcntrlr_base, gsi_ctx->intcntrlr_mem_size);
+		if (res != 0) {
+			devm_iounmap(gsi_ctx->dev, gsi_ctx->base);
+			devm_iounmap(gsi_ctx->dev, gsi_ctx->intcntrlr_base);
+			gsi_ctx->base = gsi_ctx->intcntrlr_base = NULL;
+			devm_free_irq(gsi_ctx->dev, props->irq, gsi_ctx);
+			GSIERR("setup_emulator_cntrlr() failed\n");
+			return res;
+		}
+	}
+
 	*dev_hdl = (uintptr_t)gsi_ctx;
 
 	return GSI_STATUS_SUCCESS;
@@ -2730,24 +2832,47 @@
 {
 	void __iomem *gsi_base = (void __iomem *)base;
 
-	gsi_writel(1, gsi_base + GSI_GSI_IRAM_PTR_CH_CMD_OFFS);
-	gsi_writel(2, gsi_base + GSI_GSI_IRAM_PTR_CH_DB_OFFS);
-	gsi_writel(3, gsi_base + GSI_GSI_IRAM_PTR_CH_DIS_COMP_OFFS);
-	gsi_writel(4, gsi_base + GSI_GSI_IRAM_PTR_CH_EMPTY_OFFS);
-	gsi_writel(5, gsi_base + GSI_GSI_IRAM_PTR_EE_GENERIC_CMD_OFFS);
-	gsi_writel(6, gsi_base + GSI_GSI_IRAM_PTR_EVENT_GEN_COMP_OFFS);
-	gsi_writel(7, gsi_base + GSI_GSI_IRAM_PTR_INT_MOD_STOPED_OFFS);
-	gsi_writel(8, gsi_base + GSI_GSI_IRAM_PTR_PERIPH_IF_TLV_IN_0_OFFS);
-	gsi_writel(9, gsi_base + GSI_GSI_IRAM_PTR_PERIPH_IF_TLV_IN_2_OFFS);
-	gsi_writel(10, gsi_base + GSI_GSI_IRAM_PTR_PERIPH_IF_TLV_IN_1_OFFS);
-	gsi_writel(11, gsi_base + GSI_GSI_IRAM_PTR_NEW_RE_OFFS);
-	gsi_writel(12, gsi_base + GSI_GSI_IRAM_PTR_READ_ENG_COMP_OFFS);
-	gsi_writel(13, gsi_base + GSI_GSI_IRAM_PTR_TIMER_EXPIRED_OFFS);
+	gsi_writel(1,
+		   gsi_base + GSI_GSI_IRAM_PTR_CH_CMD_OFFS);
+	gsi_writel(2,
+		   gsi_base + GSI_GSI_IRAM_PTR_CH_DB_OFFS);
+	gsi_writel(3,
+		   gsi_base + GSI_GSI_IRAM_PTR_CH_DIS_COMP_OFFS);
+	gsi_writel(4,
+		   gsi_base + GSI_GSI_IRAM_PTR_CH_EMPTY_OFFS);
+	gsi_writel(5,
+		   gsi_base + GSI_GSI_IRAM_PTR_EE_GENERIC_CMD_OFFS);
+	gsi_writel(6,
+		   gsi_base + GSI_GSI_IRAM_PTR_EVENT_GEN_COMP_OFFS);
+	gsi_writel(7,
+		   gsi_base + GSI_GSI_IRAM_PTR_INT_MOD_STOPED_OFFS);
+	gsi_writel(8,
+		   gsi_base + GSI_GSI_IRAM_PTR_PERIPH_IF_TLV_IN_0_OFFS);
+	gsi_writel(9,
+		   gsi_base + GSI_GSI_IRAM_PTR_PERIPH_IF_TLV_IN_2_OFFS);
+	gsi_writel(10,
+		   gsi_base + GSI_GSI_IRAM_PTR_PERIPH_IF_TLV_IN_1_OFFS);
+	gsi_writel(11,
+		   gsi_base + GSI_GSI_IRAM_PTR_NEW_RE_OFFS);
+	gsi_writel(12,
+		   gsi_base + GSI_GSI_IRAM_PTR_READ_ENG_COMP_OFFS);
+	gsi_writel(13,
+		   gsi_base + GSI_GSI_IRAM_PTR_TIMER_EXPIRED_OFFS);
+
+	if (running_emulation) {
+		gsi_writel(14,
+			   gsi_base + GSI_GSI_IRAM_PTR_EV_DB_OFFS);
+		gsi_writel(15,
+			   gsi_base + GSI_GSI_IRAM_PTR_UC_GP_INT_OFFS);
+		gsi_writel(16,
+			   gsi_base + GSI_GSI_IRAM_PTR_WRITE_ENG_COMP_OFFS);
+	}
 }
 
 static void gsi_configure_bck_prs_matrix(void *base)
 {
 	void __iomem *gsi_base = (void __iomem *)base;
+
 	/*
 	 * For now, these are default values. In the future, GSI FW image will
 	 * produce optimized back-pressure values based on the FW image.
@@ -2970,15 +3095,45 @@
 	},
 };
 
+static struct platform_device *pdev;
+
 /**
  * Module Init.
  */
 static int __init gsi_init(void)
 {
+	int ret;
+
 	pr_debug("gsi_init\n");
-	return platform_driver_register(&msm_gsi_driver);
+
+	ret = platform_driver_register(&msm_gsi_driver);
+	if (ret < 0)
+		goto out;
+
+	if (running_emulation) {
+		pdev = platform_device_register_simple("gsi", -1, NULL, 0);
+		if (IS_ERR(pdev)) {
+			ret = PTR_ERR(pdev);
+			platform_driver_unregister(&msm_gsi_driver);
+			goto out;
+		}
+	}
+
+out:
+	return ret;
 }
 
+/*
+ * Module exit.
+ */
+static void __exit gsi_exit(void)
+{
+	if (running_emulation && pdev)
+		platform_device_unregister(pdev);
+	platform_driver_unregister(&msm_gsi_driver);
+}
+
+module_exit(gsi_exit);
 arch_initcall(gsi_init);
 
 MODULE_LICENSE("GPL v2");
diff --git a/drivers/platform/msm/gsi/gsi.h b/drivers/platform/msm/gsi/gsi.h
index 7e10405..92849d9 100644
--- a/drivers/platform/msm/gsi/gsi.h
+++ b/drivers/platform/msm/gsi/gsi.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, 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
@@ -18,8 +18,16 @@
 #include <linux/mutex.h>
 #include <linux/spinlock.h>
 #include <linux/msm_gsi.h>
+#include <linux/errno.h>
 #include <linux/ipc_logging.h>
 
+/*
+ * The following for adding code (ie. for EMULATION) not found on x86.
+ */
+#if IPA_EMULATION_COMPILE == 1
+# include "gsi_emulation_stubs.h"
+#endif
+
 #define GSI_CHAN_MAX      31
 #define GSI_EVT_RING_MAX  23
 #define GSI_NO_EVT_ERINDEX 31
@@ -204,6 +212,13 @@
 	struct completion gen_ee_cmd_compl;
 	void *ipc_logbuf;
 	void *ipc_logbuf_low;
+	/*
+	 * The following used only on emulation systems.
+	 */
+	void __iomem *intcntrlr_base;
+	u32 intcntrlr_mem_size;
+	irq_handler_t intcntrlr_gsi_isr;
+	irq_handler_t intcntrlr_client_isr;
 };
 
 enum gsi_re_type {
diff --git a/drivers/platform/msm/gsi/gsi_emulation.c b/drivers/platform/msm/gsi/gsi_emulation.c
new file mode 100644
index 0000000..adaaaaa
--- /dev/null
+++ b/drivers/platform/msm/gsi/gsi_emulation.c
@@ -0,0 +1,233 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include "gsi_emulation.h"
+
+/*
+ * *****************************************************************************
+ * The following used to set up the EMULATION interrupt controller...
+ * *****************************************************************************
+ */
+int setup_emulator_cntrlr(
+	void __iomem *intcntrlr_base,
+	u32           intcntrlr_mem_size)
+{
+	uint32_t val, ver, intrCnt, rangeCnt, range;
+
+	val = gsi_emu_readl(intcntrlr_base + GE_INT_CTL_VER_CNT);
+
+	intrCnt  = val & 0xFFFF;
+	ver      = (val >> 16) & 0xFFFF;
+	rangeCnt = intrCnt / 32;
+
+	GSIDBG(
+	    "CTL_VER_CNT reg val(0x%x) intr cnt(%u) cntrlr ver(0x%x) rangeCnt(%u)\n",
+	    val, intrCnt, ver, rangeCnt);
+
+	/*
+	 * Verify the interrupt controller version
+	 */
+	if (ver == 0 || ver == 0xFFFF || ver < DEO_IC_INT_CTL_VER_MIN) {
+		GSIERR(
+		  "Error: invalid interrupt controller version 0x%x\n",
+		  ver);
+		return -GSI_STATUS_INVALID_PARAMS;
+	}
+
+	/*
+	 * Verify the interrupt count
+	 *
+	 * NOTE: intrCnt must be at least one block and multiple of 32
+	 */
+	if ((intrCnt % 32) != 0) {
+		GSIERR(
+		  "Invalid interrupt count read from HW 0x%04x\n",
+		  intrCnt);
+		return -GSI_STATUS_ERROR;
+	}
+
+	/*
+	 * Calculate number of ranges used, each range handles 32 int lines
+	 */
+	if (rangeCnt > DEO_IC_MAX_RANGE_CNT) {
+		GSIERR(
+		  "SW interrupt limit(%u) passed, increase DEO_IC_MAX_RANGE_CNT(%u)\n",
+		  rangeCnt,
+		  DEO_IC_MAX_RANGE_CNT);
+		return -GSI_STATUS_ERROR;
+	}
+
+	/*
+	 * Let's take the last register offset minus the first
+	 * register offset (ie. range) and compare it to the interrupt
+	 * controller's dtsi defined memory size.  The range better
+	 * fit within the size.
+	 */
+	val = GE_SOFT_INT_n(rangeCnt-1) - GE_INT_CTL_VER_CNT;
+	if (val > intcntrlr_mem_size) {
+		GSIERR(
+		    "Interrupt controller register range (%u) exceeds dtsi provisioned size (%u)\n",
+		    val, intcntrlr_mem_size);
+		return -GSI_STATUS_ERROR;
+	}
+
+	/*
+	 * The following will disable the emulators interrupt controller,
+	 * so that we can config it...
+	 */
+	GSIDBG("Writing GE_INT_MASTER_ENABLE\n");
+	gsi_emu_writel(
+		0x0,
+		intcntrlr_base + GE_INT_MASTER_ENABLE);
+
+	/*
+	 * Init register maps of all ranges
+	 */
+	for (range = 0; range < rangeCnt; range++) {
+		/*
+		 * Disable all int sources by setting all enable clear bits
+		 */
+		GSIDBG("Writing GE_INT_ENABLE_CLEAR_n(%u)\n", range);
+		gsi_emu_writel(
+		    0xFFFFFFFF,
+		    intcntrlr_base + GE_INT_ENABLE_CLEAR_n(range));
+
+		/*
+		 * Clear all raw statuses
+		 */
+		GSIDBG("Writing GE_INT_CLEAR_n(%u)\n", range);
+		gsi_emu_writel(
+		    0xFFFFFFFF,
+		    intcntrlr_base + GE_INT_CLEAR_n(range));
+
+		/*
+		 * Init all int types
+		 */
+		GSIDBG("Writing GE_INT_TYPE_n(%u)\n", range);
+		gsi_emu_writel(
+		    0x0,
+		    intcntrlr_base + GE_INT_TYPE_n(range));
+	}
+
+	/*
+	 * The following tells the interrupt controller to interrupt us
+	 * when it sees interupts from ipa and/or gsi.
+	 *
+	 * Interrupts:
+	 * ===================================================================
+	 * DUT0                       [  63 :   16 ]
+	 * ipa_irq                                        [ 3 : 0 ] <---HERE
+	 * ipa_gsi_bam_irq                                [ 7 : 4 ] <---HERE
+	 * ipa_bam_apu_sec_error_irq                      [ 8 ]
+	 * ipa_bam_apu_non_sec_error_irq                  [ 9 ]
+	 * ipa_bam_xpu2_msa_intr                          [ 10 ]
+	 * ipa_vmidmt_nsgcfgirpt                          [ 11 ]
+	 * ipa_vmidmt_nsgirpt                             [ 12 ]
+	 * ipa_vmidmt_gcfgirpt                            [ 13 ]
+	 * ipa_vmidmt_girpt                               [ 14 ]
+	 * bam_xpu3_qad_non_secure_intr_sp                [ 15 ]
+	 */
+	GSIDBG("Writing GE_INT_ENABLE_n(0)\n");
+	gsi_emu_writel(
+	    0x00FF, /* See <---HERE above */
+	    intcntrlr_base + GE_INT_ENABLE_n(0));
+
+	/*
+	 * The following will enable the IC post config...
+	 */
+	GSIDBG("Writing GE_INT_MASTER_ENABLE\n");
+	gsi_emu_writel(
+	    0x1,
+	    intcntrlr_base + GE_INT_MASTER_ENABLE);
+
+	return 0;
+}
+
+/*
+ * *****************************************************************************
+ * The following for EMULATION hard irq...
+ * *****************************************************************************
+ */
+irqreturn_t emulator_hard_irq_isr(
+	int   irq,
+	void *ctxt)
+{
+	struct gsi_ctx *gsi_ctx_ptr = (struct gsi_ctx *) ctxt;
+
+	uint32_t val;
+
+	val = gsi_emu_readl(gsi_ctx_ptr->intcntrlr_base + GE_INT_MASTER_STATUS);
+
+	/*
+	 * If bit zero is set, interrupt is for us, hence return IRQ_NONE
+	 * when it's not set...
+	 */
+	if (!(val & 0x00000001))
+		return IRQ_NONE;
+
+	/*
+	 * The following will mask (ie. turn off) future interrupts from
+	 * the emulator's interrupt controller. It wil stay this way until
+	 * we turn back on...which will be done in the bottom half
+	 * (ie. emulator_soft_irq_isr)...
+	 */
+	gsi_emu_writel(
+		0x0,
+		gsi_ctx_ptr->intcntrlr_base + GE_INT_OUT_ENABLE);
+
+	return IRQ_WAKE_THREAD;
+}
+
+/*
+ * *****************************************************************************
+ * The following for EMULATION soft irq...
+ * *****************************************************************************
+ */
+irqreturn_t emulator_soft_irq_isr(
+	int   irq,
+	void *ctxt)
+{
+	struct gsi_ctx *gsi_ctx_ptr = (struct gsi_ctx *) ctxt;
+
+	irqreturn_t retVal = IRQ_HANDLED;
+	uint32_t	val;
+
+	val = gsi_emu_readl(gsi_ctx_ptr->intcntrlr_base + GE_IRQ_STATUS_n(0));
+
+	GSIDBG("Got irq(%d) with status(0x%08X)\n", irq, val);
+
+	if (val & 0xF0 && gsi_ctx_ptr->intcntrlr_gsi_isr) {
+		GSIDBG("Got gsi interrupt\n");
+		retVal = gsi_ctx_ptr->intcntrlr_gsi_isr(irq, ctxt);
+	}
+
+	if (val & 0x0F && gsi_ctx_ptr->intcntrlr_client_isr) {
+		GSIDBG("Got ipa interrupt\n");
+		retVal = gsi_ctx_ptr->intcntrlr_client_isr(irq, 0);
+	}
+
+	/*
+	 * The following will clear the interrupts...
+	 */
+	gsi_emu_writel(
+		0xFFFFFFFF,
+		gsi_ctx_ptr->intcntrlr_base + GE_INT_CLEAR_n(0));
+
+	/*
+	 * The following will unmask (ie. turn on) future interrupts from
+	 * the emulator's interrupt controller...
+	 */
+	gsi_emu_writel(
+		0x1,
+		gsi_ctx_ptr->intcntrlr_base + GE_INT_OUT_ENABLE);
+
+	return retVal;
+}
diff --git a/drivers/platform/msm/gsi/gsi_emulation.h b/drivers/platform/msm/gsi/gsi_emulation.h
new file mode 100644
index 0000000..246f9a8
--- /dev/null
+++ b/drivers/platform/msm/gsi/gsi_emulation.h
@@ -0,0 +1,192 @@
+/* Copyright (c) 2018, 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.
+ */
+#if !defined(_GSI_EMULATION_H_)
+# define _GSI_EMULATION_H_
+
+# include <linux/interrupt.h>
+
+# include "gsi.h"
+# include "gsi_reg.h"
+# include "gsi_emulation_stubs.h"
+
+# define gsi_emu_readl(c)     ({ u32 __v = readl_relaxed(c); __iormb(); __v; })
+# define gsi_emu_writel(v, c) ({ __iowmb(); writel_relaxed((v), (c)); })
+
+# define CNTRLR_BASE 0
+
+/*
+ * The following file contains definitions and declarations that are
+ * germane only to the IPA emulation system, which is run from an X86
+ * environment.  Declaration's for non-X86 (ie. arm) are merely stubs
+ * to facilitate compile and link.
+ *
+ * Interrupt controller registers.
+ * Descriptions taken from the EMULATION interrupt controller SWI.
+ * - There is only one Master Enable register
+ * - Each group of 32 interrupt lines (range) is controlled by 8 registers,
+ *   which are consecutive in memory:
+ *      GE_INT_ENABLE_n
+ *      GE_INT_ENABLE_CLEAR_n
+ *      GE_INT_ENABLE_SET_n
+ *      GE_INT_TYPE_n
+ *      GE_IRQ_STATUS_n
+ *      GE_RAW_STATUS_n
+ *      GE_INT_CLEAR_n
+ *      GE_SOFT_INT_n
+ * - After the above 8 registers, there are the registers of the next
+ *   group (range) of 32 interrupt lines, and so on.
+ */
+
+/** @brief The interrupt controller version and interrupt count register.
+ *         Specifies interrupt controller version (upper 16 bits) and the
+ *         number of interrupt lines supported by HW (lower 16 bits).
+ */
+# define GE_INT_CTL_VER_CNT              \
+	(CNTRLR_BASE + 0x0000)
+
+/** @brief Enable or disable physical IRQ output signal to the system,
+ *         not affecting any status registers.
+ *
+ *         0x0 : DISABLE IRQ output disabled
+ *         0x1 : ENABLE  IRQ output enabled
+ */
+# define GE_INT_OUT_ENABLE               \
+	(CNTRLR_BASE + 0x0004)
+
+/** @brief The IRQ master enable register.
+ *         Bit #0: IRQ_ENABLE, set 0 to disable, 1 to enable.
+ */
+# define GE_INT_MASTER_ENABLE            \
+	(CNTRLR_BASE + 0x0008)
+
+# define GE_INT_MASTER_STATUS            \
+	(CNTRLR_BASE + 0x000C)
+
+/** @brief Each bit disables (bit=0, default) or enables (bit=1) the
+ *         corresponding interrupt source
+ */
+# define GE_INT_ENABLE_n(n)              \
+	(CNTRLR_BASE + 0x0010 + 0x20 * (n))
+
+/** @brief Write bit=1 to clear (to 0) the corresponding bit(s) in INT_ENABLE.
+ *         Does nothing for bit=0
+ */
+# define GE_INT_ENABLE_CLEAR_n(n)        \
+	(CNTRLR_BASE + 0x0014 + 0x20 * (n))
+
+/** @brief Write bit=1 to set (to 1) the corresponding bit(s) in INT_ENABLE.
+ *         Does nothing for bit=0
+ */
+# define GE_INT_ENABLE_SET_n(n)          \
+	(CNTRLR_BASE + 0x0018 + 0x20 * (n))
+
+/** @brief Select level (bit=0, default) or edge (bit=1) sensitive input
+ *         detection logic for each corresponding interrupt source
+ */
+# define GE_INT_TYPE_n(n)                \
+	(CNTRLR_BASE + 0x001C + 0x20 * (n))
+
+/** @brief Shows the interrupt sources captured in RAW_STATUS that have been
+ *         steered to irq_n by INT_SELECT. Interrupts must also be enabled by
+ *         INT_ENABLE and MASTER_ENABLE. Read only register.
+ *         Bit values: 1=active, 0=inactive
+ */
+# define GE_IRQ_STATUS_n(n)                      \
+	(CNTRLR_BASE + 0x0020 + 0x20 * (n))
+
+/** @brief Shows the interrupt sources that have been latched by the input
+ *         logic of the Interrupt Controller. Read only register.
+ *         Bit values: 1=active, 0=inactive
+ */
+# define GE_RAW_STATUS_n(n)                      \
+	(CNTRLR_BASE + 0x0024 + 0x20 * (n))
+
+/** @brief Write bit=1 to clear the corresponding bit(s) in RAW_STATUS.
+ *         Does nothing for bit=0
+ */
+# define GE_INT_CLEAR_n(n)               \
+	(CNTRLR_BASE + 0x0028 + 0x20 * (n))
+
+/** @brief Write bit=1 to set the corresponding bit(s) in RAW_STATUS.
+ *         Does nothing for bit=0.
+ *  @note  Only functional for edge detected interrupts
+ */
+# define GE_SOFT_INT_n(n)                        \
+	(CNTRLR_BASE + 0x002C + 0x20 * (n))
+
+/** @brief Maximal number of ranges in SW. Each range supports 32 interrupt
+ *         lines. If HW is extended considerably, increase this value
+ */
+# define DEO_IC_MAX_RANGE_CNT            8
+
+/** @brief Size of the registers of one range in memory, in bytes */
+# define DEO_IC_RANGE_MEM_SIZE           32  /* SWI: 8 registers, no gaps */
+
+/** @brief Minimal Interrupt controller HW version */
+# define DEO_IC_INT_CTL_VER_MIN          0x0102
+
+
+#if IPA_EMULATION_COMPILE == 1 /* declarations to follow */
+
+/*
+ * *****************************************************************************
+ * The following used to set up the EMULATION interrupt controller...
+ * *****************************************************************************
+ */
+int setup_emulator_cntrlr(
+	void __iomem *intcntrlr_base,
+	u32           intcntrlr_mem_size);
+
+/*
+ * *****************************************************************************
+ * The following for EMULATION hard irq...
+ * *****************************************************************************
+ */
+irqreturn_t emulator_hard_irq_isr(
+	int   irq,
+	void *ctxt);
+
+/*
+ * *****************************************************************************
+ * The following for EMULATION soft irq...
+ * *****************************************************************************
+ */
+irqreturn_t emulator_soft_irq_isr(
+	int   irq,
+	void *ctxt);
+
+# else /* #if IPA_EMULATION_COMPILE != 1, then definitions to follow */
+
+static inline int setup_emulator_cntrlr(
+	void __iomem *intcntrlr_base,
+	u32           intcntrlr_mem_size)
+{
+	return 0;
+}
+
+static inline irqreturn_t emulator_hard_irq_isr(
+	int   irq,
+	void *ctxt)
+{
+	return IRQ_NONE;
+}
+
+static inline irqreturn_t emulator_soft_irq_isr(
+	int   irq,
+	void *ctxt)
+{
+	return IRQ_HANDLED;
+}
+
+# endif /* #if IPA_EMULATION_COMPILE == 1 */
+
+#endif /* #if !defined(_GSI_EMULATION_H_) */
diff --git a/drivers/platform/msm/gsi/gsi_emulation_stubs.h b/drivers/platform/msm/gsi/gsi_emulation_stubs.h
new file mode 100644
index 0000000..dd9d0df
--- /dev/null
+++ b/drivers/platform/msm/gsi/gsi_emulation_stubs.h
@@ -0,0 +1,20 @@
+/* Copyright (c) 2018, 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.
+ */
+
+#if !defined(_GSI_EMULATION_STUBS_H_)
+# define _GSI_EMULATION_STUBS_H_
+
+# include <asm/barrier.h>
+# define __iormb()       rmb() /* used in gsi.h */
+# define __iowmb()       wmb() /* used in gsi.h */
+
+#endif /* #if !defined(_GSI_EMULATION_STUBS_H_) */
diff --git a/drivers/platform/msm/ipa/Makefile b/drivers/platform/msm/ipa/Makefile
index 15ed471..857e17b 100644
--- a/drivers/platform/msm/ipa/Makefile
+++ b/drivers/platform/msm/ipa/Makefile
@@ -1,3 +1,9 @@
+ifdef CONFIG_X86
+ccflags-y += -DIPA_EMULATION_COMPILE=1
+else
+ccflags-y += -DIPA_EMULATION_COMPILE=0
+endif
+
 obj-$(CONFIG_IPA) += ipa_v2/ ipa_clients/ ipa_common
 obj-$(CONFIG_IPA3) += ipa_v3/ ipa_clients/ ipa_common
 obj-$(CONFIG_IPA_UT) += test/
diff --git a/drivers/platform/msm/ipa/ipa_api.c b/drivers/platform/msm/ipa/ipa_api.c
index fdcf44d..3a75bdd 100644
--- a/drivers/platform/msm/ipa/ipa_api.c
+++ b/drivers/platform/msm/ipa/ipa_api.c
@@ -19,8 +19,16 @@
 #include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/ipa_uc_offload.h>
+#include <linux/pci.h>
 #include "ipa_api.h"
 
+/*
+ * The following for adding code (ie. for EMULATION) not found on x86.
+ */
+#if IPA_EMULATION_COMPILE == 1
+# include "ipa_v3/ipa_emulation_stubs.h"
+#endif
+
 #define DRV_NAME "ipa"
 
 #define IPA_API_DISPATCH_RETURN(api, p...) \
@@ -96,6 +104,8 @@
 		} \
 	} while (0)
 
+static bool running_emulation = IPA_EMULATION_COMPILE;
+
 static enum ipa_hw_type ipa_api_hw_type;
 static struct ipa_api_controller *ipa_api_ctrl;
 
@@ -2916,6 +2926,57 @@
 	{}
 };
 
+/*********************************************************/
+/*                PCIe Version                           */
+/*********************************************************/
+
+static const struct of_device_id ipa_pci_drv_match[] = {
+	{ .compatible = "qcom,ipa", },
+	{}
+};
+
+/*
+ * Forward declarations of static functions required for PCI
+ * registraion
+ *
+ * VENDOR and DEVICE should be defined in pci_ids.h
+ */
+static int ipa_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent);
+static void ipa_pci_remove(struct pci_dev *pdev);
+static void ipa_pci_shutdown(struct pci_dev *pdev);
+static pci_ers_result_t ipa_pci_io_error_detected(struct pci_dev *dev,
+	pci_channel_state_t state);
+static pci_ers_result_t ipa_pci_io_slot_reset(struct pci_dev *dev);
+static void ipa_pci_io_resume(struct pci_dev *dev);
+
+#define LOCAL_VENDOR 0x17CB
+#define LOCAL_DEVICE 0x00ff
+
+static const char ipa_pci_driver_name[] = "qcipav3";
+
+static const struct pci_device_id ipa_pci_tbl[] = {
+	{ PCI_DEVICE(LOCAL_VENDOR, LOCAL_DEVICE) },
+	{ 0, 0, 0, 0, 0, 0, 0 }
+};
+
+MODULE_DEVICE_TABLE(pci, ipa_pci_tbl);
+
+/* PCI Error Recovery */
+static const struct pci_error_handlers ipa_pci_err_handler = {
+	.error_detected = ipa_pci_io_error_detected,
+	.slot_reset = ipa_pci_io_slot_reset,
+	.resume = ipa_pci_io_resume,
+};
+
+static struct pci_driver ipa_pci_driver = {
+	.name     = ipa_pci_driver_name,
+	.id_table = ipa_pci_tbl,
+	.probe    = ipa_pci_probe,
+	.remove   = ipa_pci_remove,
+	.shutdown = ipa_pci_shutdown,
+	.err_handler = &ipa_pci_err_handler
+};
+
 static int ipa_generic_plat_drv_probe(struct platform_device *pdev_p)
 {
 	int result;
@@ -3364,10 +3425,86 @@
 	},
 };
 
+/*********************************************************/
+/*                PCIe Version                           */
+/*********************************************************/
+
+static int ipa_pci_probe(
+	struct pci_dev             *pci_dev,
+	const struct pci_device_id *ent)
+{
+	int result;
+
+	if (!pci_dev || !ent) {
+		pr_err(
+		    "Bad arg: pci_dev (%pK) and/or ent (%pK)\n",
+		    pci_dev, ent);
+		return -EOPNOTSUPP;
+	}
+
+	if (!ipa_api_ctrl) {
+		ipa_api_ctrl = kzalloc(sizeof(*ipa_api_ctrl), GFP_KERNEL);
+		if (ipa_api_ctrl == NULL)
+			return -ENOMEM;
+		/* Get IPA HW Version */
+		result = of_property_read_u32(NULL,
+			"qcom,ipa-hw-ver", &ipa_api_hw_type);
+		if (result || ipa_api_hw_type == 0) {
+			pr_err("ipa: get resource failed for ipa-hw-ver!\n");
+			kfree(ipa_api_ctrl);
+			ipa_api_ctrl = NULL;
+			return -ENODEV;
+		}
+		pr_debug("ipa: ipa_api_hw_type = %d", ipa_api_hw_type);
+	}
+
+	/*
+	 * Call a reduced version of platform_probe appropriate for PCIe
+	 */
+	result = ipa3_pci_drv_probe(pci_dev, ipa_api_ctrl, ipa_pci_drv_match);
+
+	if (result && result != -EPROBE_DEFER)
+		pr_err("ipa: ipa3_pci_drv_probe failed\n");
+
+	if (running_emulation)
+		ipa_ut_module_init();
+
+	return result;
+}
+
+static void ipa_pci_remove(struct pci_dev *pci_dev)
+{
+	if (running_emulation)
+		ipa_ut_module_exit();
+}
+
+static void ipa_pci_shutdown(struct pci_dev *pci_dev)
+{
+}
+
+static pci_ers_result_t ipa_pci_io_error_detected(struct pci_dev *pci_dev,
+	pci_channel_state_t state)
+{
+	return 0;
+}
+
+static pci_ers_result_t ipa_pci_io_slot_reset(struct pci_dev *pci_dev)
+{
+	return 0;
+}
+
+static void ipa_pci_io_resume(struct pci_dev *pci_dev)
+{
+}
+
 static int __init ipa_module_init(void)
 {
 	pr_debug("IPA module init\n");
 
+	if (running_emulation) {
+		/* Register as a PCI device driver */
+		return pci_register_driver(&ipa_pci_driver);
+	}
 	/* Register as a platform device driver */
 	return platform_driver_register(&ipa_plat_drv);
 }
diff --git a/drivers/platform/msm/ipa/ipa_api.h b/drivers/platform/msm/ipa/ipa_api.h
index b7edb6f..cbcb0ee 100644
--- a/drivers/platform/msm/ipa/ipa_api.h
+++ b/drivers/platform/msm/ipa/ipa_api.h
@@ -452,6 +452,10 @@
 int ipa3_plat_drv_probe(struct platform_device *pdev_p,
 	struct ipa_api_controller *api_ctrl,
 	const struct of_device_id *pdrv_match);
+int ipa3_pci_drv_probe(
+	struct pci_dev            *pci_dev,
+	struct ipa_api_controller *api_ctrl,
+	const struct of_device_id *pdrv_match);
 #else
 static inline int ipa3_plat_drv_probe(struct platform_device *pdev_p,
 	struct ipa_api_controller *api_ctrl,
@@ -459,6 +463,13 @@
 {
 	return -ENODEV;
 }
+static inline int ipa3_pci_drv_probe(
+	struct pci_dev            *pci_dev,
+	struct ipa_api_controller *api_ctrl,
+	const struct of_device_id *pdrv_match)
+{
+	return -ENODEV;
+}
 #endif /* (CONFIG_IPA3) */
 
 #endif /* _IPA_API_H_ */
diff --git a/drivers/platform/msm/ipa/ipa_common_i.h b/drivers/platform/msm/ipa/ipa_common_i.h
index b8a517e..530aa54 100644
--- a/drivers/platform/msm/ipa/ipa_common_i.h
+++ b/drivers/platform/msm/ipa/ipa_common_i.h
@@ -15,6 +15,7 @@
 
 #ifndef _IPA_COMMON_I_H_
 #define _IPA_COMMON_I_H_
+#include <linux/errno.h>
 #include <linux/ipc_logging.h>
 #include <linux/ipa.h>
 #include <linux/ipa_uc_offload.h>
@@ -441,4 +442,7 @@
 		struct sg_table *in_sgt_ptr);
 int ipa_smmu_free_sgt(struct sg_table **out_sgt_ptr);
 
+int ipa_ut_module_init(void);
+void ipa_ut_module_exit(void);
+
 #endif /* _IPA_COMMON_I_H_ */
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c
index f062ed2..953469a 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c
@@ -432,6 +432,8 @@
 
 	list_for_each_entry(entry, &ipa_ctx->hdr_tbl.head_hdr_entry_list,
 			link) {
+		if (entry->cookie != IPA_HDR_COOKIE)
+			continue;
 		nbytes = scnprintf(
 			dbg_buff,
 			IPA_MAX_MSG_LEN,
@@ -606,6 +608,14 @@
 	if (attrib->protocol_eq_present)
 		pr_err("protocol:%d ", attrib->protocol_eq);
 
+	if (attrib->num_ihl_offset_range_16 >
+			IPA_IPFLTR_NUM_IHL_RANGE_16_EQNS) {
+		IPAERR_RL("num_ihl_offset_range_16  Max %d passed value %d\n",
+			IPA_IPFLTR_NUM_IHL_RANGE_16_EQNS,
+			attrib->num_ihl_offset_range_16);
+		return -EPERM;
+	}
+
 	for (i = 0; i < attrib->num_ihl_offset_range_16; i++) {
 		pr_err(
 			   "(ihl_ofst_range16: ofst:%u lo:%u hi:%u) ",
@@ -614,6 +624,12 @@
 			   attrib->ihl_offset_range_16[i].range_high);
 	}
 
+	if (attrib->num_offset_meq_32 > IPA_IPFLTR_NUM_MEQ_32_EQNS) {
+		IPAERR_RL("num_offset_meq_32  Max %d passed value %d\n",
+		IPA_IPFLTR_NUM_MEQ_32_EQNS, attrib->num_offset_meq_32);
+		return -EPERM;
+	}
+
 	for (i = 0; i < attrib->num_offset_meq_32; i++) {
 		pr_err(
 			   "(ofst_meq32: ofst:%u mask:0x%x val:0x%x) ",
@@ -635,6 +651,12 @@
 				attrib->ihl_offset_eq_16.value);
 	}
 
+	if (attrib->num_ihl_offset_meq_32 > IPA_IPFLTR_NUM_IHL_MEQ_32_EQNS) {
+		IPAERR_RL("num_ihl_offset_meq_32  Max %d passed value %d\n",
+		IPA_IPFLTR_NUM_IHL_MEQ_32_EQNS, attrib->num_ihl_offset_meq_32);
+		return -EPERM;
+	}
+
 	for (i = 0; i < attrib->num_ihl_offset_meq_32; i++) {
 		pr_err(
 				"(ihl_ofst_meq32: ofts:%d mask:0x%x val:0x%x) ",
@@ -643,6 +665,12 @@
 				attrib->ihl_offset_meq_32[i].value);
 	}
 
+	if (attrib->num_offset_meq_128 > IPA_IPFLTR_NUM_MEQ_128_EQNS) {
+		IPAERR_RL("num_offset_meq_128  Max %d passed value %d\n",
+		IPA_IPFLTR_NUM_MEQ_128_EQNS, attrib->num_offset_meq_128);
+		return -EPERM;
+	}
+
 	for (i = 0; i < attrib->num_offset_meq_128; i++) {
 		for (j = 0; j < 16; j++) {
 			addr[j] = attrib->offset_meq_128[i].value[j];
@@ -812,11 +840,14 @@
 	u32 rt_tbl_idx;
 	u32 bitmap;
 	bool eq;
+	int res = 0;
 
 	tbl = &ipa_ctx->glob_flt_tbl[ip];
 	mutex_lock(&ipa_ctx->lock);
 	i = 0;
 	list_for_each_entry(entry, &tbl->head_flt_rule_list, link) {
+		if (entry->cookie != IPA_FLT_COOKIE)
+			continue;
 		if (entry->rule.eq_attrib_type) {
 			rt_tbl_idx = entry->rule.rt_tbl_idx;
 			bitmap = entry->rule.eq_attrib.rule_eq_bitmap;
@@ -835,10 +866,14 @@
 			i, entry->rule.action, rt_tbl_idx);
 		pr_err("attrib_mask:%08x retain_hdr:%d eq:%d ",
 			bitmap, entry->rule.retain_hdr, eq);
-		if (eq)
-			ipa_attrib_dump_eq(
+		if (eq) {
+			res = ipa_attrib_dump_eq(
 				&entry->rule.eq_attrib);
-		else
+			if (res) {
+				IPAERR_RL("failed read attrib eq\n");
+				goto bail;
+			}
+		} else
 			ipa_attrib_dump(
 				&entry->rule.attrib, ip);
 		i++;
@@ -848,6 +883,8 @@
 		tbl = &ipa_ctx->flt_tbl[j][ip];
 		i = 0;
 		list_for_each_entry(entry, &tbl->head_flt_rule_list, link) {
+			if (entry->cookie != IPA_FLT_COOKIE)
+				continue;
 			if (entry->rule.eq_attrib_type) {
 				rt_tbl_idx = entry->rule.rt_tbl_idx;
 				bitmap = entry->rule.eq_attrib.rule_eq_bitmap;
@@ -867,18 +904,23 @@
 			pr_err("attrib_mask:%08x retain_hdr:%d ",
 				bitmap, entry->rule.retain_hdr);
 			pr_err("eq:%d ", eq);
-			if (eq)
-				ipa_attrib_dump_eq(
-					&entry->rule.eq_attrib);
-			else
+			if (eq) {
+				res = ipa_attrib_dump_eq(
+						&entry->rule.eq_attrib);
+				if (res) {
+					IPAERR_RL("failed read attrib eq\n");
+					goto bail;
+				}
+			} else
 				ipa_attrib_dump(
 					&entry->rule.attrib, ip);
 			i++;
 		}
 	}
+bail:
 	mutex_unlock(&ipa_ctx->lock);
 
-	return 0;
+	return res;
 }
 
 static ssize_t ipa_read_stats(struct file *file, char __user *ubuf,
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_nat.c b/drivers/platform/msm/ipa/ipa_v2/ipa_nat.c
index c043bad..15be5d6 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_nat.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_nat.c
@@ -327,6 +327,11 @@
 	size_t tmp;
 	gfp_t flag = GFP_KERNEL | (ipa_ctx->use_dma_zone ? GFP_DMA : 0);
 
+	if (!ipa_ctx->nat_mem.is_dev_init) {
+		IPAERR_RL("Nat table not initialized\n");
+		return -EPERM;
+	}
+
 	IPADBG("\n");
 	if (init->table_entries == 0) {
 		IPADBG("Table entries is zero\n");
@@ -576,6 +581,11 @@
 	int ret = 0;
 	gfp_t flag = GFP_KERNEL | (ipa_ctx->use_dma_zone ? GFP_DMA : 0);
 
+	if (!ipa_ctx->nat_mem.is_dev_init) {
+		IPAERR_RL("Nat table not initialized\n");
+		return -EPERM;
+	}
+
 	IPADBG("\n");
 	if (dma->entries <= 0) {
 		IPAERR_RL("Invalid number of commands %d\n",
@@ -762,6 +772,16 @@
 	int result;
 	gfp_t flag = GFP_KERNEL | (ipa_ctx->use_dma_zone ? GFP_DMA : 0);
 
+	if (!ipa_ctx->nat_mem.is_dev_init) {
+		IPAERR_RL("Nat table not initialized\n");
+		return -EPERM;
+	}
+
+	if (ipa_ctx->nat_mem.public_ip_addr) {
+		IPAERR_RL("Public IP addr not assigned and trying to delete\n");
+		return -EPERM;
+	}
+
 	IPADBG("\n");
 	if (ipa_ctx->nat_mem.is_tmp_mem) {
 		IPAERR("using temp memory during nat del\n");
diff --git a/drivers/platform/msm/ipa/ipa_v3/Makefile b/drivers/platform/msm/ipa/ipa_v3/Makefile
index ae4dccf..ed78342 100644
--- a/drivers/platform/msm/ipa/ipa_v3/Makefile
+++ b/drivers/platform/msm/ipa/ipa_v3/Makefile
@@ -1,9 +1,19 @@
 obj-$(CONFIG_IPA3) += ipahal/
 
+ifdef CONFIG_X86
+ccflags-y += -DIPA_EMULATION_COMPILE=1
+else
+ccflags-y += -DIPA_EMULATION_COMPILE=0
+endif
+
 obj-$(CONFIG_IPA3) += ipat.o
 ipat-y := ipa.o ipa_debugfs.o ipa_hdr.o ipa_flt.o ipa_rt.o ipa_dp.o ipa_client.o \
 	ipa_utils.o ipa_nat.o ipa_intf.o teth_bridge.o ipa_interrupts.o \
 	ipa_uc.o ipa_uc_wdi.o ipa_dma.o ipa_uc_mhi.o ipa_mhi.o ipa_uc_ntn.o \
 	ipa_hw_stats.o ipa_pm.o ipa_wdi3_i.o
 
+ifdef CONFIG_X86
+ipat-y += ipa_dtsi_replacement.o
+endif
+
 obj-$(CONFIG_RMNET_IPA3) += rmnet_ipa.o ipa_qmi_service_v01.o ipa_qmi_service.o rmnet_ipa_fd_ioctl.o
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c
index c523b3d..bd82aa9 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c
@@ -35,6 +35,7 @@
 #include <linux/time.h>
 #include <linux/hashtable.h>
 #include <linux/jhash.h>
+#include <linux/pci.h>
 #include <soc/qcom/subsystem_restart.h>
 #include <soc/qcom/smem.h>
 #include <soc/qcom/scm.h>
@@ -57,169 +58,14 @@
 #define CREATE_TRACE_POINTS
 #include "ipa_trace.h"
 
-#define IPA_GPIO_IN_QUERY_CLK_IDX 0
-#define IPA_GPIO_OUT_CLK_RSP_CMPLT_IDX 0
-#define IPA_GPIO_OUT_CLK_VOTE_IDX 1
-
-#define IPA_SUMMING_THRESHOLD (0x10)
-#define IPA_PIPE_MEM_START_OFST (0x0)
-#define IPA_PIPE_MEM_SIZE (0x0)
-#define IPA_MOBILE_AP_MODE(x) (x == IPA_MODE_MOBILE_AP_ETH || \
-			       x == IPA_MODE_MOBILE_AP_WAN || \
-			       x == IPA_MODE_MOBILE_AP_WLAN)
-#define IPA_CNOC_CLK_RATE (75 * 1000 * 1000UL)
-#define IPA_A5_MUX_HEADER_LENGTH (8)
-
-#define IPA_AGGR_MAX_STR_LENGTH (10)
-
-#define CLEANUP_TAG_PROCESS_TIMEOUT 500
-
-#define IPA_AGGR_STR_IN_BYTES(str) \
-	(strnlen((str), IPA_AGGR_MAX_STR_LENGTH - 1) + 1)
-
-#define IPA_TRANSPORT_PROD_TIMEOUT_MSEC 100
-
-#define IPA3_ACTIVE_CLIENTS_TABLE_BUF_SIZE 2048
-
-#define IPA3_ACTIVE_CLIENT_LOG_TYPE_EP 0
-#define IPA3_ACTIVE_CLIENT_LOG_TYPE_SIMPLE 1
-#define IPA3_ACTIVE_CLIENT_LOG_TYPE_RESOURCE 2
-#define IPA3_ACTIVE_CLIENT_LOG_TYPE_SPECIAL 3
-
-#define IPA_MHI_GSI_EVENT_RING_ID_START 10
-#define IPA_MHI_GSI_EVENT_RING_ID_END 12
-
-#define IPA_SMEM_SIZE (8 * 1024)
-
-#define IPA_GSI_CHANNEL_HALT_MIN_SLEEP 5000
-#define IPA_GSI_CHANNEL_HALT_MAX_SLEEP 10000
-#define IPA_GSI_CHANNEL_HALT_MAX_TRY 10
-
-/* round addresses for closes page per SMMU requirements */
-#define IPA_SMMU_ROUND_TO_PAGE(iova, pa, size, iova_p, pa_p, size_p) \
-	do { \
-		(iova_p) = rounddown((iova), PAGE_SIZE); \
-		(pa_p) = rounddown((pa), PAGE_SIZE); \
-		(size_p) = roundup((size) + (pa) - (pa_p), PAGE_SIZE); \
-	} while (0)
-
-
-/* The relative location in /lib/firmware where the FWs will reside */
-#define IPA_FWS_PATH "ipa/ipa_fws.elf"
+/*
+ * The following for adding code (ie. for EMULATION) not found on x86.
+ */
+#if IPA_EMULATION_COMPILE == 1
+# include "ipa_emulation_stubs.h"
+#endif
 
 #ifdef CONFIG_COMPAT
-#define IPA_IOC_ADD_HDR32 _IOWR(IPA_IOC_MAGIC, \
-					IPA_IOCTL_ADD_HDR, \
-					compat_uptr_t)
-#define IPA_IOC_DEL_HDR32 _IOWR(IPA_IOC_MAGIC, \
-					IPA_IOCTL_DEL_HDR, \
-					compat_uptr_t)
-#define IPA_IOC_ADD_RT_RULE32 _IOWR(IPA_IOC_MAGIC, \
-					IPA_IOCTL_ADD_RT_RULE, \
-					compat_uptr_t)
-#define IPA_IOC_DEL_RT_RULE32 _IOWR(IPA_IOC_MAGIC, \
-					IPA_IOCTL_DEL_RT_RULE, \
-					compat_uptr_t)
-#define IPA_IOC_ADD_FLT_RULE32 _IOWR(IPA_IOC_MAGIC, \
-					IPA_IOCTL_ADD_FLT_RULE, \
-					compat_uptr_t)
-#define IPA_IOC_DEL_FLT_RULE32 _IOWR(IPA_IOC_MAGIC, \
-					IPA_IOCTL_DEL_FLT_RULE, \
-					compat_uptr_t)
-#define IPA_IOC_GET_RT_TBL32 _IOWR(IPA_IOC_MAGIC, \
-				IPA_IOCTL_GET_RT_TBL, \
-				compat_uptr_t)
-#define IPA_IOC_COPY_HDR32 _IOWR(IPA_IOC_MAGIC, \
-				IPA_IOCTL_COPY_HDR, \
-				compat_uptr_t)
-#define IPA_IOC_QUERY_INTF32 _IOWR(IPA_IOC_MAGIC, \
-				IPA_IOCTL_QUERY_INTF, \
-				compat_uptr_t)
-#define IPA_IOC_QUERY_INTF_TX_PROPS32 _IOWR(IPA_IOC_MAGIC, \
-				IPA_IOCTL_QUERY_INTF_TX_PROPS, \
-				compat_uptr_t)
-#define IPA_IOC_QUERY_INTF_RX_PROPS32 _IOWR(IPA_IOC_MAGIC, \
-					IPA_IOCTL_QUERY_INTF_RX_PROPS, \
-					compat_uptr_t)
-#define IPA_IOC_QUERY_INTF_EXT_PROPS32 _IOWR(IPA_IOC_MAGIC, \
-					IPA_IOCTL_QUERY_INTF_EXT_PROPS, \
-					compat_uptr_t)
-#define IPA_IOC_GET_HDR32 _IOWR(IPA_IOC_MAGIC, \
-				IPA_IOCTL_GET_HDR, \
-				compat_uptr_t)
-#define IPA_IOC_ALLOC_NAT_MEM32 _IOWR(IPA_IOC_MAGIC, \
-				IPA_IOCTL_ALLOC_NAT_MEM, \
-				compat_uptr_t)
-#define IPA_IOC_ALLOC_NAT_TABLE32 _IOWR(IPA_IOC_MAGIC, \
-				IPA_IOCTL_ALLOC_NAT_TABLE, \
-				compat_uptr_t)
-#define IPA_IOC_ALLOC_IPV6CT_TABLE32 _IOWR(IPA_IOC_MAGIC, \
-				IPA_IOCTL_ALLOC_IPV6CT_TABLE, \
-				compat_uptr_t)
-#define IPA_IOC_V4_INIT_NAT32 _IOWR(IPA_IOC_MAGIC, \
-				IPA_IOCTL_V4_INIT_NAT, \
-				compat_uptr_t)
-#define IPA_IOC_INIT_IPV6CT_TABLE32 _IOWR(IPA_IOC_MAGIC, \
-				IPA_IOCTL_INIT_IPV6CT_TABLE, \
-				compat_uptr_t)
-#define IPA_IOC_TABLE_DMA_CMD32 _IOWR(IPA_IOC_MAGIC, \
-				IPA_IOCTL_TABLE_DMA_CMD, \
-				compat_uptr_t)
-#define IPA_IOC_V4_DEL_NAT32 _IOWR(IPA_IOC_MAGIC, \
-				IPA_IOCTL_V4_DEL_NAT, \
-				compat_uptr_t)
-#define IPA_IOC_DEL_NAT_TABLE32 _IOWR(IPA_IOC_MAGIC, \
-				IPA_IOCTL_DEL_NAT_TABLE, \
-				compat_uptr_t)
-#define IPA_IOC_DEL_IPV6CT_TABLE32 _IOWR(IPA_IOC_MAGIC, \
-				IPA_IOCTL_DEL_IPV6CT_TABLE, \
-				compat_uptr_t)
-#define IPA_IOC_NAT_MODIFY_PDN32 _IOWR(IPA_IOC_MAGIC, \
-				IPA_IOCTL_NAT_MODIFY_PDN, \
-				compat_uptr_t)
-#define IPA_IOC_GET_NAT_OFFSET32 _IOWR(IPA_IOC_MAGIC, \
-				IPA_IOCTL_GET_NAT_OFFSET, \
-				compat_uptr_t)
-#define IPA_IOC_PULL_MSG32 _IOWR(IPA_IOC_MAGIC, \
-				IPA_IOCTL_PULL_MSG, \
-				compat_uptr_t)
-#define IPA_IOC_RM_ADD_DEPENDENCY32 _IOWR(IPA_IOC_MAGIC, \
-				IPA_IOCTL_RM_ADD_DEPENDENCY, \
-				compat_uptr_t)
-#define IPA_IOC_RM_DEL_DEPENDENCY32 _IOWR(IPA_IOC_MAGIC, \
-				IPA_IOCTL_RM_DEL_DEPENDENCY, \
-				compat_uptr_t)
-#define IPA_IOC_GENERATE_FLT_EQ32 _IOWR(IPA_IOC_MAGIC, \
-				IPA_IOCTL_GENERATE_FLT_EQ, \
-				compat_uptr_t)
-#define IPA_IOC_QUERY_RT_TBL_INDEX32 _IOWR(IPA_IOC_MAGIC, \
-				IPA_IOCTL_QUERY_RT_TBL_INDEX, \
-				compat_uptr_t)
-#define IPA_IOC_WRITE_QMAPID32  _IOWR(IPA_IOC_MAGIC, \
-				IPA_IOCTL_WRITE_QMAPID, \
-				compat_uptr_t)
-#define IPA_IOC_MDFY_FLT_RULE32 _IOWR(IPA_IOC_MAGIC, \
-				IPA_IOCTL_MDFY_FLT_RULE, \
-				compat_uptr_t)
-#define IPA_IOC_NOTIFY_WAN_UPSTREAM_ROUTE_ADD32 _IOWR(IPA_IOC_MAGIC, \
-				IPA_IOCTL_NOTIFY_WAN_UPSTREAM_ROUTE_ADD, \
-				compat_uptr_t)
-#define IPA_IOC_NOTIFY_WAN_UPSTREAM_ROUTE_DEL32 _IOWR(IPA_IOC_MAGIC, \
-				IPA_IOCTL_NOTIFY_WAN_UPSTREAM_ROUTE_DEL, \
-				compat_uptr_t)
-#define IPA_IOC_NOTIFY_WAN_EMBMS_CONNECTED32 _IOWR(IPA_IOC_MAGIC, \
-					IPA_IOCTL_NOTIFY_WAN_EMBMS_CONNECTED, \
-					compat_uptr_t)
-#define IPA_IOC_ADD_HDR_PROC_CTX32 _IOWR(IPA_IOC_MAGIC, \
-				IPA_IOCTL_ADD_HDR_PROC_CTX, \
-				compat_uptr_t)
-#define IPA_IOC_DEL_HDR_PROC_CTX32 _IOWR(IPA_IOC_MAGIC, \
-				IPA_IOCTL_DEL_HDR_PROC_CTX, \
-				compat_uptr_t)
-#define IPA_IOC_MDFY_RT_RULE32 _IOWR(IPA_IOC_MAGIC, \
-				IPA_IOCTL_MDFY_RT_RULE, \
-				compat_uptr_t)
-
 /**
  * struct ipa3_ioc_nat_alloc_mem32 - nat table memory allocation
  * properties
@@ -243,8 +89,7 @@
 	compat_size_t size;
 	compat_off_t offset;
 };
-
-#endif
+#endif /* #ifdef CONFIG_COMPAT */
 
 #define IPA_TZ_UNLOCK_ATTRIBUTE 0x0C0311
 #define TZ_MEM_PROTECT_REGION_ID 0x10
@@ -284,6 +129,7 @@
 static struct clk *ipa3_clk;
 
 struct ipa3_context *ipa3_ctx;
+
 static struct {
 	bool present[IPA_SMMU_CB_MAX];
 	bool arm_smmu;
@@ -2735,6 +2581,16 @@
 	u32 *ipa_sram_mmio;
 	unsigned long phys_addr;
 
+	IPADBG(
+	    "ipa_wrapper_base(0x%08X) ipa_reg_base_ofst(0x%08X) IPA_SRAM_DIRECT_ACCESS_n(0x%08X) smem_restricted_bytes(0x%08X) smem_sz(0x%08X)\n",
+	    ipa3_ctx->ipa_wrapper_base,
+	    ipa3_ctx->ctrl->ipa_reg_base_ofst,
+	    ipahal_get_reg_n_ofst(
+		IPA_SRAM_DIRECT_ACCESS_n,
+		ipa3_ctx->smem_restricted_bytes / 4),
+	    ipa3_ctx->smem_restricted_bytes,
+	    ipa3_ctx->smem_sz);
+
 	phys_addr = ipa3_ctx->ipa_wrapper_base +
 		ipa3_ctx->ctrl->ipa_reg_base_ofst +
 		ipahal_get_reg_n_ofst(IPA_SRAM_DIRECT_ACCESS_n,
@@ -3207,21 +3063,27 @@
 	}
 	IPADBG("Apps to IPA cmd pipe is connected\n");
 
+	IPADBG("Will initialize SRAM\n");
 	ipa3_ctx->ctrl->ipa_init_sram();
 	IPADBG("SRAM initialized\n");
 
+	IPADBG("Will initialize HDR\n");
 	ipa3_ctx->ctrl->ipa_init_hdr();
 	IPADBG("HDR initialized\n");
 
+	IPADBG("Will initialize V4 RT\n");
 	ipa3_ctx->ctrl->ipa_init_rt4();
 	IPADBG("V4 RT initialized\n");
 
+	IPADBG("Will initialize V6 RT\n");
 	ipa3_ctx->ctrl->ipa_init_rt6();
 	IPADBG("V6 RT initialized\n");
 
+	IPADBG("Will initialize V4 FLT\n");
 	ipa3_ctx->ctrl->ipa_init_flt4();
 	IPADBG("V4 FLT initialized\n");
 
+	IPADBG("Will initialize V6 FLT\n");
 	ipa3_ctx->ctrl->ipa_init_flt6();
 	IPADBG("V6 FLT initialized\n");
 
@@ -4412,8 +4274,8 @@
 	int result;
 
 	result = gsi_configure_regs(ipa3_res.transport_mem_base,
-		ipa3_res.transport_mem_size,
-		ipa3_res.ipa_mem_base);
+				    ipa3_res.transport_mem_size,
+				    ipa3_res.ipa_mem_base);
 	if (result) {
 		IPAERR("Failed to configure GSI registers\n");
 		return -EINVAL;
@@ -4506,12 +4368,19 @@
 	if (ipa3_ctx->ipa_hw_type != IPA_HW_v4_0)
 		ipa3_proxy_clk_vote();
 
-	/* SMMU was already attached if used, safe to do allocations */
-	if (ipahal_init(ipa3_ctx->ipa_hw_type, ipa3_ctx->mmio,
-		ipa3_ctx->pdev)) {
-		IPAERR("fail to init ipahal\n");
-		result = -EFAULT;
-		goto fail_ipahal;
+	/*
+	 * SMMU was already attached if used, safe to do allocations
+	 *
+	 * NOTE WELL: On an emulation system, this allocation is done
+	 *            in ipa3_pre_init()
+	 */
+	if (ipa3_ctx->ipa3_hw_mode != IPA_HW_MODE_EMULATION) {
+		if (ipahal_init(ipa3_ctx->ipa_hw_type, ipa3_ctx->mmio,
+				ipa3_ctx->pdev)) {
+			IPAERR("fail to init ipahal\n");
+			result = -EFAULT;
+			goto fail_ipahal;
+		}
 	}
 
 	result = ipa3_init_hw();
@@ -4646,9 +4515,18 @@
 	gsi_props.ver = ipa3_get_gsi_ver(resource_p->ipa_hw_type);
 	gsi_props.ee = resource_p->ee;
 	gsi_props.intr = GSI_INTR_IRQ;
-	gsi_props.irq = resource_p->transport_irq;
 	gsi_props.phys_addr = resource_p->transport_mem_base;
 	gsi_props.size = resource_p->transport_mem_size;
+	if (ipa3_ctx->ipa3_hw_mode == IPA_HW_MODE_EMULATION) {
+		gsi_props.irq = resource_p->emulator_irq;
+		gsi_props.emulator_intcntrlr_client_isr = ipa3_get_isr();
+		gsi_props.emulator_intcntrlr_addr =
+		    resource_p->emulator_intcntrlr_mem_base;
+		gsi_props.emulator_intcntrlr_size =
+		    resource_p->emulator_intcntrlr_mem_size;
+	} else {
+		gsi_props.irq = resource_p->transport_irq;
+	}
 	gsi_props.notify_cb = ipa_gsi_notify_cb;
 	gsi_props.req_clk_cb = NULL;
 	gsi_props.rel_clk_cb = NULL;
@@ -4716,12 +4594,12 @@
 
 	ipa3_register_panic_hdlr();
 
+	ipa3_debugfs_init();
+
 	mutex_lock(&ipa3_ctx->lock);
 	ipa3_ctx->ipa_initialization_complete = true;
 	mutex_unlock(&ipa3_ctx->lock);
 
-	ipa3_debugfs_init();
-
 	ipa3_trigger_ipa_ready_cbs();
 	complete_all(&ipa3_ctx->init_completion_obj);
 	pr_info("IPA driver initialization was successful.\n");
@@ -4734,15 +4612,16 @@
 	gsi_deregister_device(ipa3_ctx->gsi_dev_hdl, false);
 fail_register_device:
 	ipa3_destroy_flt_tbl_idrs();
-	ipa3_proxy_clk_unvote();
 fail_allok_pkt_init:
 	ipa3_nat_ipv6ct_destroy_devices();
 fail_nat_ipv6ct_init_dev:
 	ipa3_free_dma_task_for_gsi();
 fail_dma_task:
 fail_init_hw:
-	ipahal_destroy();
+	if (ipa3_ctx->ipa3_hw_mode != IPA_HW_MODE_EMULATION)
+		ipahal_destroy();
 fail_ipahal:
+	ipa3_proxy_clk_unvote();
 
 	return result;
 }
@@ -4751,10 +4630,24 @@
 {
 	int result;
 	const struct firmware *fw;
+	const char *path = IPA_FWS_PATH;
 
-	IPADBG("Manual FW loading process initiated\n");
+	if (ipa3_ctx->ipa3_hw_mode == IPA_HW_MODE_EMULATION) {
+		switch (ipa3_get_emulation_type()) {
+		case IPA_HW_v3_5_1:
+			path = IPA_FWS_PATH_3_5_1;
+			break;
+		case IPA_HW_v4_0:
+			path = IPA_FWS_PATH_4_0;
+			break;
+		default:
+			break;
+		}
+	}
 
-	result = request_firmware(&fw, IPA_FWS_PATH, ipa3_ctx->cdev.dev);
+	IPADBG("Manual FW loading (%s) process initiated\n", path);
+
+	result = request_firmware(&fw, path, ipa3_ctx->cdev.dev);
 	if (result < 0) {
 		IPAERR("request_firmware failed, error %d\n", result);
 		return result;
@@ -4766,7 +4659,13 @@
 
 	IPADBG("FWs are available for loading\n");
 
-	result = ipa3_load_fws(fw, ipa3_res.transport_mem_base);
+	if (ipa3_ctx->ipa3_hw_mode == IPA_HW_MODE_EMULATION) {
+		result = emulator_load_fws(fw,
+					   ipa3_res.transport_mem_base,
+					   ipa3_res.transport_mem_size);
+	} else {
+		result = ipa3_load_fws(fw, ipa3_res.transport_mem_base);
+	}
 	if (result) {
 		IPAERR("Manual IPA FWs loading has failed\n");
 		release_firmware(fw);
@@ -4785,6 +4684,7 @@
 	release_firmware(fw);
 
 	IPADBG("Manual FW loading process is complete\n");
+
 	return 0;
 }
 
@@ -4818,7 +4718,8 @@
 
 	IPA_ACTIVE_CLIENTS_INC_SIMPLE();
 
-	if (ipa3_is_msm_device() || (ipa3_ctx->ipa_hw_type >= IPA_HW_v3_5))
+	if (ipa3_ctx->ipa3_hw_mode != IPA_HW_MODE_EMULATION &&
+	    (ipa3_is_msm_device() || (ipa3_ctx->ipa_hw_type >= IPA_HW_v3_5)))
 		result = ipa3_pil_load_ipa_fws();
 	else
 		result = ipa3_manual_load_ipa_fws();
@@ -5155,7 +5056,8 @@
 		goto fail_init_mem_partition;
 	}
 
-	if (ipa3_ctx->ipa3_hw_mode != IPA_HW_MODE_VIRTUAL) {
+	if (ipa3_ctx->ipa3_hw_mode != IPA_HW_MODE_VIRTUAL &&
+	    ipa3_ctx->ipa3_hw_mode != IPA_HW_MODE_EMULATION) {
 		ipa3_ctx->ctrl->msm_bus_data_ptr =
 			msm_bus_cl_get_pdata(ipa3_ctx->master_pdev);
 		if (ipa3_ctx->ctrl->msm_bus_data_ptr == NULL) {
@@ -5205,6 +5107,28 @@
 		goto fail_remap;
 	}
 
+	IPADBG(
+	    "base(0x%x)+offset(0x%x)=(0x%x) mapped to (%pK) with len (0x%x)\n",
+	    resource_p->ipa_mem_base,
+	    ipa3_ctx->ctrl->ipa_reg_base_ofst,
+	    resource_p->ipa_mem_base + ipa3_ctx->ctrl->ipa_reg_base_ofst,
+	    ipa3_ctx->mmio,
+	    resource_p->ipa_mem_size);
+
+	/*
+	 * Emulation requires ipahal be initialized early...for FW
+	 * download, hence...
+	 */
+	if (ipa3_ctx->ipa3_hw_mode == IPA_HW_MODE_EMULATION) {
+		if (ipahal_init(ipa3_ctx->ipa_hw_type,
+				ipa3_ctx->mmio,
+				&(ipa3_ctx->master_pdev->dev))) {
+			IPAERR("fail to init ipahal\n");
+			result = -EFAULT;
+			goto fail_ipahal_init;
+		}
+	}
+
 	mutex_init(&ipa3_ctx->ipa3_active_clients.mutex);
 
 	IPA_ACTIVE_CLIENTS_PREP_SPECIAL(log_info, "PROXY_CLK_VOTE");
@@ -5413,10 +5337,13 @@
 	 * We can't register the GSI driver yet, as it expects
 	 * the GSI FW to be up and running before the registration.
 	 *
-	 * For IPA3.0, the GSI configuration is done by the GSI driver.
+	 * For IPA3.0 and the emulation system, the GSI configuration
+	 * is done by the GSI driver.
+	 *
 	 * For IPA3.1 (and on), the GSI configuration is done by TZ.
 	 */
-	if (ipa3_ctx->ipa_hw_type == IPA_HW_v3_0) {
+	if (ipa3_ctx->ipa_hw_type == IPA_HW_v3_0 ||
+	    ipa3_ctx->ipa3_hw_mode == IPA_HW_MODE_EMULATION) {
 		result = ipa3_gsi_pre_fw_load_init();
 		if (result) {
 			IPAERR("gsi pre FW loading config failed\n");
@@ -5494,6 +5421,9 @@
 fail_create_transport_wq:
 	destroy_workqueue(ipa3_ctx->power_mgmt_wq);
 fail_init_hw:
+	if (ipa3_ctx->ipa3_hw_mode == IPA_HW_MODE_EMULATION)
+		ipahal_destroy();
+fail_ipahal_init:
 	iounmap(ipa3_ctx->mmio);
 fail_remap:
 	ipa3_disable_clks();
@@ -5806,6 +5736,7 @@
 			&ipa_drv_res->ee);
 	if (result)
 		ipa_drv_res->ee = 0;
+	IPADBG(":ee = %u\n", ipa_drv_res->ee);
 
 	ipa_drv_res->apply_rg10_wa =
 		of_property_read_bool(pdev->dev.of_node,
@@ -5818,7 +5749,7 @@
 		of_property_read_bool(pdev->dev.of_node,
 		"qcom,do-not-use-ch-gsi-20");
 	IPADBG(": GSI CH 20 WA is = %s\n",
-		ipa_drv_res->apply_rg10_wa
+		ipa_drv_res->gsi_ch20_wa
 		? "Needed" : "Not needed");
 
 	elem_num = of_property_count_elems_of_size(pdev->dev.of_node,
@@ -5897,6 +5828,26 @@
 		return result;
 	}
 
+	/*
+	 * If we're on emulator, get its interrupt controller's mem
+	 * start and size
+	 */
+	if (ipa_drv_res->ipa3_hw_mode == IPA_HW_MODE_EMULATION) {
+		resource = platform_get_resource_byname(
+		    pdev, IORESOURCE_MEM, "intctrl-base");
+		if (!resource) {
+			IPAERR(":Can't find intctrl-base resource\n");
+			return -ENODEV;
+		}
+		ipa_drv_res->emulator_intcntrlr_mem_base =
+		    resource->start;
+		ipa_drv_res->emulator_intcntrlr_mem_size =
+		    resource_size(resource);
+		IPADBG(":using intctrl-base at 0x%x of size 0x%x\n",
+		       ipa_drv_res->emulator_intcntrlr_mem_base,
+		       ipa_drv_res->emulator_intcntrlr_mem_size);
+	}
+
 	return 0;
 }
 
@@ -6691,5 +6642,216 @@
 	return 0;
 }
 
+/**************************************************************
+ *            PCIe Version
+ *************************************************************/
+
+int ipa3_pci_drv_probe(
+	struct pci_dev            *pci_dev,
+	struct ipa_api_controller *api_ctrl,
+	const struct of_device_id *pdrv_match)
+{
+	int result;
+	struct ipa3_plat_drv_res *ipa_drv_res;
+	u32 bar0_offset;
+	u32 mem_start;
+	u32 mem_end;
+	uint32_t bits;
+	uint32_t ipa_start, gsi_start, intctrl_start;
+	struct device *dev;
+	static struct platform_device platform_dev;
+
+	if (!pci_dev || !api_ctrl || !pdrv_match) {
+		IPAERR(
+		    "Bad arg: pci_dev (%pK) and/or api_ctrl (%pK) and/or pdrv_match (%pK)\n",
+		    pci_dev, api_ctrl, pdrv_match);
+		return -EOPNOTSUPP;
+	}
+
+	dev = &(pci_dev->dev);
+
+	IPADBG("IPA PCI driver probing started\n");
+
+	/*
+	 * Follow PCI driver flow here.
+	 * pci_enable_device:  Enables device and assigns resources
+	 * pci_request_region:  Makes BAR0 address region usable
+	 */
+	result = pci_enable_device(pci_dev);
+	if (result < 0) {
+		IPAERR("pci_enable_device() failed\n");
+		return -EOPNOTSUPP;
+	}
+
+	result = pci_request_region(pci_dev, 0, "IPA Memory");
+	if (result < 0) {
+		IPAERR("pci_request_region() failed\n");
+		pci_disable_device(pci_dev);
+		return -EOPNOTSUPP;
+	}
+
+	/*
+	 * When in the PCI/emulation environment, &platform_dev is
+	 * passed to get_ipa_dts_configuration(), but is unused, since
+	 * all usages of it in the function are replaced by CPP
+	 * relative to definitions in ipa_emulation_stubs.h.  Passing
+	 * &platform_dev makes code validity tools happy.
+	 */
+	if (get_ipa_dts_configuration(&platform_dev, &ipa3_res) != 0) {
+		IPAERR("get_ipa_dts_configuration() failed\n");
+		pci_release_region(pci_dev, 0);
+		pci_disable_device(pci_dev);
+		return -EOPNOTSUPP;
+	}
+
+	ipa_drv_res = &ipa3_res;
+
+	result =
+		of_property_read_u32(NULL, "emulator-bar0-offset",
+				     &bar0_offset);
+	if (result) {
+		IPAERR(":get resource failed for emulator-bar0-offset!\n");
+		pci_release_region(pci_dev, 0);
+		pci_disable_device(pci_dev);
+		return -ENODEV;
+	}
+	IPADBG(":using emulator-bar0-offset 0x%08X\n", bar0_offset);
+
+	ipa_start     = ipa_drv_res->ipa_mem_base;
+	gsi_start     = ipa_drv_res->transport_mem_base;
+	intctrl_start = ipa_drv_res->emulator_intcntrlr_mem_base;
+
+	/*
+	 * Where will we be inerrupted at?
+	 */
+	ipa_drv_res->emulator_irq = pci_dev->irq;
+	IPADBG(
+	    "EMULATION PCI_INTERRUPT_PIN(%u)\n",
+	    ipa_drv_res->emulator_irq);
+
+	/*
+	 * Set the ipa_mem_base to the PCI base address of BAR0
+	 */
+	mem_start = pci_resource_start(pci_dev, 0);
+	mem_end   = pci_resource_end(pci_dev, 0);
+
+	IPADBG("PCI START                = 0x%x\n", mem_start);
+	IPADBG("PCI END                  = 0x%x\n", mem_end);
+
+	ipa_drv_res->ipa_mem_base = mem_start + bar0_offset;
+
+	smmu_info.ipa_base = ipa_drv_res->ipa_mem_base;
+	smmu_info.ipa_size = ipa_drv_res->ipa_mem_size;
+
+	ipa_drv_res->transport_mem_base =
+	    ipa_drv_res->ipa_mem_base + (gsi_start - ipa_start);
+
+	ipa_drv_res->emulator_intcntrlr_mem_base =
+	    ipa_drv_res->ipa_mem_base + (intctrl_start - ipa_start);
+
+	IPADBG("ipa_mem_base                = 0x%x\n",
+	       ipa_drv_res->ipa_mem_base);
+	IPADBG("ipa_mem_size                = 0x%x\n",
+	       ipa_drv_res->ipa_mem_size);
+
+	IPADBG("transport_mem_base          = 0x%x\n",
+	       ipa_drv_res->transport_mem_base);
+	IPADBG("transport_mem_size          = 0x%x\n",
+	       ipa_drv_res->transport_mem_size);
+
+	IPADBG("emulator_intcntrlr_mem_base = 0x%x\n",
+	       ipa_drv_res->emulator_intcntrlr_mem_base);
+	IPADBG("emulator_intcntrlr_mem_size = 0x%x\n",
+	       ipa_drv_res->emulator_intcntrlr_mem_size);
+
+	result = ipa3_bind_api_controller(ipa_drv_res->ipa_hw_type, api_ctrl);
+	if (result != 0) {
+		IPAERR("ipa3_bind_api_controller() failed\n");
+		pci_release_region(pci_dev, 0);
+		pci_disable_device(pci_dev);
+		return result;
+	}
+
+	bits = (ipa_drv_res->use_64_bit_dma_mask) ? 64 : 32;
+
+	if (dma_set_mask(dev, DMA_BIT_MASK(bits)) != 0) {
+		IPAERR("dma_set_mask(%pK, %u) failed\n", dev, bits);
+		pci_release_region(pci_dev, 0);
+		pci_disable_device(pci_dev);
+		return -EOPNOTSUPP;
+	}
+
+	if (dma_set_coherent_mask(dev, DMA_BIT_MASK(bits)) != 0) {
+		IPAERR("dma_set_coherent_mask(%pK, %u) failed\n", dev, bits);
+		pci_release_region(pci_dev, 0);
+		pci_disable_device(pci_dev);
+		return -EOPNOTSUPP;
+	}
+
+	pci_set_master(pci_dev);
+
+	memset(&platform_dev, 0, sizeof(platform_dev));
+	platform_dev.dev = *dev;
+
+	/* Proceed to real initialization */
+	result = ipa3_pre_init(&ipa3_res, &platform_dev);
+	if (result) {
+		IPAERR("ipa3_init failed\n");
+		pci_clear_master(pci_dev);
+		pci_release_region(pci_dev, 0);
+		pci_disable_device(pci_dev);
+		return result;
+	}
+
+	return result;
+}
+
+/*
+ * The following returns transport register memory location and
+ * size...
+ */
+int ipa3_get_transport_info(
+	phys_addr_t *phys_addr_ptr,
+	unsigned long *size_ptr)
+{
+	if (!phys_addr_ptr || !size_ptr) {
+		IPAERR("Bad arg: phys_addr_ptr(%pK) and/or size_ptr(%pK)\n",
+		       phys_addr_ptr, size_ptr);
+		return -EINVAL;
+	}
+
+	*phys_addr_ptr = ipa3_res.transport_mem_base;
+	*size_ptr      = ipa3_res.transport_mem_size;
+
+	return 0;
+}
+EXPORT_SYMBOL(ipa3_get_transport_info);
+
+static uint emulation_type = IPA_HW_v4_0;
+
+/*
+ * The following returns emulation type...
+ */
+uint ipa3_get_emulation_type(void)
+{
+	return emulation_type;
+}
+
 MODULE_LICENSE("GPL v2");
 MODULE_DESCRIPTION("IPA HW device driver");
+
+/*
+ * Module parameter. Invoke as follows:
+ *     insmod ipat.ko emulation_type=[13|14|...|N]
+ * Examples:
+ *   insmod ipat.ko emulation_type=13 # for IPA 3.5.1
+ *   insmod ipat.ko emulation_type=14 # for IPA 4.0
+ *
+ * NOTE: The emulation_type values need to come from: enum ipa_hw_type
+ *
+ */
+
+module_param(emulation_type, uint, 0000);
+MODULE_PARM_DESC(
+	emulation_type,
+	"IPA emulation type (Use 13 for IPA 3.5.1, 14 for IPA 4.0)");
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c
index ee9c49c..dc5f5e0 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c
@@ -372,6 +372,8 @@
 
 	list_for_each_entry(entry, &ipa3_ctx->hdr_tbl.head_hdr_entry_list,
 			link) {
+		if (entry->cookie != IPA_HDR_COOKIE)
+			continue;
 		nbytes = scnprintf(
 			dbg_buff,
 			IPA_MAX_MSG_LEN,
@@ -556,6 +558,12 @@
 	if (attrib->tc_eq_present)
 		pr_err("tc:%d ", attrib->tc_eq);
 
+	if (attrib->num_offset_meq_128 > IPA_IPFLTR_NUM_MEQ_128_EQNS) {
+		IPAERR_RL("num_offset_meq_128  Max %d passed value %d\n",
+		IPA_IPFLTR_NUM_MEQ_128_EQNS, attrib->num_offset_meq_128);
+		return -EPERM;
+	}
+
 	for (i = 0; i < attrib->num_offset_meq_128; i++) {
 		for (j = 0; j < 16; j++) {
 			addr[j] = attrib->offset_meq_128[i].value[j];
@@ -567,6 +575,12 @@
 			mask, addr);
 	}
 
+	if (attrib->num_offset_meq_32 > IPA_IPFLTR_NUM_MEQ_32_EQNS) {
+		IPAERR_RL("num_offset_meq_32  Max %d passed value %d\n",
+		IPA_IPFLTR_NUM_MEQ_32_EQNS, attrib->num_offset_meq_32);
+		return -EPERM;
+	}
+
 	for (i = 0; i < attrib->num_offset_meq_32; i++)
 		pr_err(
 			   "(ofst_meq32: ofst:%u mask:0x%x val:0x%x) ",
@@ -574,6 +588,12 @@
 			   attrib->offset_meq_32[i].mask,
 			   attrib->offset_meq_32[i].value);
 
+	if (attrib->num_ihl_offset_meq_32 > IPA_IPFLTR_NUM_IHL_MEQ_32_EQNS) {
+		IPAERR_RL("num_ihl_offset_meq_32  Max %d passed value %d\n",
+		IPA_IPFLTR_NUM_IHL_MEQ_32_EQNS, attrib->num_ihl_offset_meq_32);
+		return -EPERM;
+	}
+
 	for (i = 0; i < attrib->num_ihl_offset_meq_32; i++)
 		pr_err(
 			"(ihl_ofst_meq32: ofts:%d mask:0x%x val:0x%x) ",
@@ -588,6 +608,14 @@
 			attrib->metadata_meq32.mask,
 			attrib->metadata_meq32.value);
 
+	if (attrib->num_ihl_offset_range_16 >
+			IPA_IPFLTR_NUM_IHL_RANGE_16_EQNS) {
+		IPAERR_RL("num_ihl_offset_range_16  Max %d passed value %d\n",
+			IPA_IPFLTR_NUM_IHL_RANGE_16_EQNS,
+			attrib->num_ihl_offset_range_16);
+		return -EPERM;
+	}
+
 	for (i = 0; i < attrib->num_ihl_offset_range_16; i++)
 		pr_err(
 			   "(ihl_ofst_range16: ofst:%u lo:%u hi:%u) ",
@@ -780,7 +808,11 @@
 			pr_err("rule_id:%u prio:%u retain_hdr:%u ",
 				rules[rl].id, rules[rl].priority,
 				rules[rl].retain_hdr);
-			ipa3_attrib_dump_eq(&rules[rl].eq_attrib);
+			res = ipa3_attrib_dump_eq(&rules[rl].eq_attrib);
+			if (res) {
+				IPAERR_RL("failed read attrib eq\n");
+				goto bail;
+			}
 		}
 
 		pr_err("=== Routing Table %d = Non-Hashable Rules ===\n", tbl);
@@ -811,7 +843,11 @@
 			pr_err("rule_id:%u prio:%u retain_hdr:%u\n",
 				rules[rl].id, rules[rl].priority,
 				rules[rl].retain_hdr);
-			ipa3_attrib_dump_eq(&rules[rl].eq_attrib);
+			res = ipa3_attrib_dump_eq(&rules[rl].eq_attrib);
+			if (res) {
+				IPAERR_RL("failed read attrib eq\n");
+				goto bail;
+			}
 		}
 		pr_err("\n");
 	}
@@ -885,6 +921,7 @@
 	u32 rt_tbl_idx;
 	u32 bitmap;
 	bool eq;
+	int res = 0;
 
 	mutex_lock(&ipa3_ctx->lock);
 
@@ -894,6 +931,8 @@
 		tbl = &ipa3_ctx->flt_tbl[j][ip];
 		i = 0;
 		list_for_each_entry(entry, &tbl->head_flt_rule_list, link) {
+			if (entry->cookie != IPA_FLT_COOKIE)
+				continue;
 			if (entry->rule.eq_attrib_type) {
 				rt_tbl_idx = entry->rule.rt_tbl_idx;
 				bitmap = entry->rule.eq_attrib.rule_eq_bitmap;
@@ -919,18 +958,23 @@
 				pr_err("pdn index %d, set metadata %d ",
 					entry->rule.pdn_idx,
 					entry->rule.set_metadata);
-			if (eq)
-				ipa3_attrib_dump_eq(
-					&entry->rule.eq_attrib);
-			else
+			if (eq) {
+				res = ipa3_attrib_dump_eq(
+						&entry->rule.eq_attrib);
+				if (res) {
+					IPAERR_RL("failed read attrib eq\n");
+					goto bail;
+				}
+			} else
 				ipa3_attrib_dump(
 					&entry->rule.attrib, ip);
 			i++;
 		}
 	}
+bail:
 	mutex_unlock(&ipa3_ctx->lock);
 
-	return 0;
+	return res;
 }
 
 static ssize_t ipa3_read_flt_hw(struct file *file, char __user *ubuf,
@@ -985,7 +1029,11 @@
 				pr_err("pdn: %u, set_metadata: %u ",
 					rules[rl].rule.pdn_idx,
 					rules[rl].rule.set_metadata);
-			ipa3_attrib_dump_eq(&rules[rl].rule.eq_attrib);
+			res = ipa3_attrib_dump_eq(&rules[rl].rule.eq_attrib);
+			if (res) {
+				IPAERR_RL("failed read attrib eq\n");
+				goto bail;
+			}
 		}
 
 		pr_err("=== Filtering Table ep:%d = Non-Hashable Rules ===\n",
@@ -1013,7 +1061,11 @@
 				pr_err("pdn: %u, set_metadata: %u ",
 					rules[rl].rule.pdn_idx,
 					rules[rl].rule.set_metadata);
-			ipa3_attrib_dump_eq(&rules[rl].rule.eq_attrib);
+			res = ipa3_attrib_dump_eq(&rules[rl].rule.eq_attrib);
+			if (res) {
+				IPAERR_RL("failed read attrib eq\n");
+				goto bail;
+			}
 		}
 		pr_err("\n");
 	}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dtsi_replacement.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dtsi_replacement.c
new file mode 100644
index 0000000..5fe2294
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dtsi_replacement.c
@@ -0,0 +1,765 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/msm_ipa.h>
+#include "ipa_i.h"
+#include "ipa_emulation_stubs.h"
+
+# undef strsame
+# define strsame(x, y) \
+	(!strcmp((x), (y)))
+
+/*
+ * The following enum values used to index tables below.
+ */
+enum dtsi_index_e {
+	DTSI_INDEX_3_5_1 = 0,
+	DTSI_INDEX_4_0   = 1,
+};
+
+struct dtsi_replacement_u32 {
+	char *key;
+	u32 value;
+};
+
+struct dtsi_replacement_u32_table {
+	struct dtsi_replacement_u32 *p_table;
+	u32 num_entries;
+};
+
+struct dtsi_replacement_bool {
+	char *key;
+	bool value;
+};
+
+struct dtsi_replacement_bool_table {
+	struct dtsi_replacement_bool *p_table;
+	u32 num_entries;
+};
+
+struct dtsi_replacement_u32_array {
+	char *key;
+	u32 *p_value;
+	u32 num_elements;
+};
+
+struct dtsi_replacement_u32_array_table {
+	struct dtsi_replacement_u32_array *p_table;
+	u32 num_entries;
+};
+
+struct dtsi_replacement_resource_table {
+	struct resource *p_table;
+	u32 num_entries;
+};
+
+/*
+ * Any of the data below with _4_0 in the name represent data taken
+ * from the 4.0 dtsi file.
+ *
+ * Any of the data below with _3_5_1 in the name represent data taken
+ * from the 3.5.1 dtsi file.
+ */
+static struct dtsi_replacement_bool ipa3_plat_drv_bool_4_0[] = {
+	{"qcom,use-ipa-tethering-bridge",       true},
+	{"qcom,modem-cfg-emb-pipe-flt",         true},
+	{"qcom,ipa-wdi2",                       true},
+	{"qcom,use-64-bit-dma-mask",            false},
+	{"qcom,bandwidth-vote-for-ipa",         false},
+	{"qcom,skip-uc-pipe-reset",             false},
+	{"qcom,tethered-flow-control",          true},
+	{"qcom,use-rg10-limitation-mitigation", false},
+	{"qcom,do-not-use-ch-gsi-20",           false},
+	{"qcom,use-ipa-pm",                     false},
+};
+
+static struct dtsi_replacement_bool ipa3_plat_drv_bool_3_5_1[] = {
+	{"qcom,use-ipa-tethering-bridge",       true},
+	{"qcom,modem-cfg-emb-pipe-flt",         true},
+	{"qcom,ipa-wdi2",                       true},
+	{"qcom,use-64-bit-dma-mask",            false},
+	{"qcom,bandwidth-vote-for-ipa",         true},
+	{"qcom,skip-uc-pipe-reset",             false},
+	{"qcom,tethered-flow-control",          false},
+	{"qcom,use-rg10-limitation-mitigation", false},
+	{"qcom,do-not-use-ch-gsi-20",           false},
+	{"qcom,use-ipa-pm",                     false},
+};
+
+static struct dtsi_replacement_bool_table
+ipa3_plat_drv_bool_table[] = {
+	{ ipa3_plat_drv_bool_3_5_1,
+	  ARRAY_SIZE(ipa3_plat_drv_bool_3_5_1) },
+	{ ipa3_plat_drv_bool_4_0,
+	  ARRAY_SIZE(ipa3_plat_drv_bool_4_0) },
+};
+
+static struct dtsi_replacement_u32 ipa3_plat_drv_u32_4_0[] = {
+	{"qcom,ipa-hw-ver",                     IPA_HW_v4_0},
+	{"qcom,ipa-hw-mode",                    3},
+	{"qcom,wan-rx-ring-size",               192},
+	{"qcom,lan-rx-ring-size",               192},
+	{"qcom,ee",                             0},
+	{"emulator-bar0-offset",                0x01C00000},
+};
+
+static struct dtsi_replacement_u32 ipa3_plat_drv_u32_3_5_1[] = {
+	{"qcom,ipa-hw-ver",                     IPA_HW_v3_5_1},
+	{"qcom,ipa-hw-mode",                    3},
+	{"qcom,wan-rx-ring-size",               192},
+	{"qcom,lan-rx-ring-size",               192},
+	{"qcom,ee",                             0},
+	{"emulator-bar0-offset",                0x01C00000},
+};
+
+static struct dtsi_replacement_u32_table ipa3_plat_drv_u32_table[] = {
+	{ ipa3_plat_drv_u32_3_5_1,
+	  ARRAY_SIZE(ipa3_plat_drv_u32_3_5_1) },
+	{ ipa3_plat_drv_u32_4_0,
+	  ARRAY_SIZE(ipa3_plat_drv_u32_4_0) },
+};
+
+static u32 mhi_event_ring_id_limits_array_4_0[] = {
+	9, 10
+};
+
+static u32 mhi_event_ring_id_limits_array_3_5_1[] = {
+	IPA_MHI_GSI_EVENT_RING_ID_START, IPA_MHI_GSI_EVENT_RING_ID_END
+};
+
+static u32 ipa_tz_unlock_reg_array_4_0[] = {
+	0x04043583c, 0x00001000
+};
+
+static u32 ipa_tz_unlock_reg_array_3_5_1[] = {
+	0x04043583c, 0x00001000
+};
+
+static u32 ipa_ram_mmap_array_4_0[] = {
+	0x00000280, 0x00000000, 0x00000000, 0x00000288, 0x00000078,
+	0x00004000, 0x00000308, 0x00000078, 0x00004000, 0x00000388,
+	0x00000078, 0x00004000, 0x00000408, 0x00000078, 0x00004000,
+	0x0000000F, 0x00000000, 0x00000007, 0x00000008, 0x0000000E,
+	0x00000488, 0x00000078, 0x00004000, 0x00000508, 0x00000078,
+	0x00004000, 0x0000000F, 0x00000000, 0x00000007, 0x00000008,
+	0x0000000E, 0x00000588, 0x00000078, 0x00004000, 0x00000608,
+	0x00000078, 0x00004000, 0x00000688, 0x00000140, 0x000007C8,
+	0x00000000, 0x00000800, 0x000007D0, 0x00000200, 0x000009D0,
+	0x00000200, 0x00000000, 0x00000000, 0x00000000, 0x000013F0,
+	0x0000100C, 0x000023FC, 0x00000000, 0x000023FC, 0x00000000,
+	0x000023FC, 0x00000000, 0x000023FC, 0x00000000, 0x00000080,
+	0x00000200, 0x00002800, 0x000023FC, 0x00000000, 0x000023FC,
+	0x00000000, 0x000023FC, 0x00000000, 0x000023FC, 0x00000000,
+	0x00002400, 0x00000400, 0x00000BD8, 0x00000050, 0x00000C30,
+	0x00000060, 0x00000C90, 0x00000140, 0x00000DD0, 0x00000180,
+	0x00000F50, 0x00000180, 0x000010D0, 0x00000180, 0x00001250,
+	0x00000180, 0x000013D0, 0x00000020
+};
+
+static u32 ipa_ram_mmap_array_3_5_1[] = {
+	0x00000280, 0x00000000, 0x00000000, 0x00000288, 0x00000078,
+	0x00004000, 0x00000308, 0x00000078, 0x00004000, 0x00000388,
+	0x00000078, 0x00004000, 0x00000408, 0x00000078, 0x00004000,
+	0x0000000F, 0x00000000, 0x00000007, 0x00000008, 0x0000000E,
+	0x00000488, 0x00000078, 0x00004000, 0x00000508, 0x00000078,
+	0x00004000, 0x0000000F, 0x00000000, 0x00000007, 0x00000008,
+	0x0000000E, 0x00000588, 0x00000078, 0x00004000, 0x00000608,
+	0x00000078, 0x00004000, 0x00000688, 0x00000140, 0x000007C8,
+	0x00000000, 0x00000800, 0x000007D0, 0x00000200, 0x000009D0,
+	0x00000200, 0x00000000, 0x00000000, 0x00000000, 0x00000BD8,
+	0x00001024, 0x00002000, 0x00000000, 0x00002000, 0x00000000,
+	0x00002000, 0x00000000, 0x00002000, 0x00000000, 0x00000080,
+	0x00000200, 0x00002000, 0x00002000, 0x00000000, 0x00002000,
+	0x00000000, 0x00002000, 0x00000000, 0x00002000, 0x00000000,
+	0x00001C00, 0x00000400
+};
+
+struct dtsi_replacement_u32_array ipa3_plat_drv_u32_array_4_0[] = {
+	{"qcom,mhi-event-ring-id-limits",
+	 mhi_event_ring_id_limits_array_4_0,
+	 ARRAY_SIZE(mhi_event_ring_id_limits_array_4_0) },
+	{"qcom,ipa-tz-unlock-reg",
+	 ipa_tz_unlock_reg_array_4_0,
+	 ARRAY_SIZE(ipa_tz_unlock_reg_array_4_0) },
+	{"qcom,ipa-ram-mmap",
+	 ipa_ram_mmap_array_4_0,
+	 ARRAY_SIZE(ipa_ram_mmap_array_4_0) },
+};
+
+struct dtsi_replacement_u32_array ipa3_plat_drv_u32_array_3_5_1[] = {
+	{"qcom,mhi-event-ring-id-limits",
+	 mhi_event_ring_id_limits_array_3_5_1,
+	 ARRAY_SIZE(mhi_event_ring_id_limits_array_3_5_1) },
+	{"qcom,ipa-tz-unlock-reg",
+	 ipa_tz_unlock_reg_array_3_5_1,
+	 ARRAY_SIZE(ipa_tz_unlock_reg_array_3_5_1) },
+	{"qcom,ipa-ram-mmap",
+	 ipa_ram_mmap_array_3_5_1,
+	 ARRAY_SIZE(ipa_ram_mmap_array_3_5_1) },
+};
+
+struct dtsi_replacement_u32_array_table
+ipa3_plat_drv_u32_array_table[] = {
+	{ ipa3_plat_drv_u32_array_3_5_1,
+	  ARRAY_SIZE(ipa3_plat_drv_u32_array_3_5_1) },
+	{ ipa3_plat_drv_u32_array_4_0,
+	  ARRAY_SIZE(ipa3_plat_drv_u32_array_4_0) },
+};
+
+#define INTCTRL_OFFSET       0x083C0000
+#define INTCTRL_SIZE         0x00000110
+
+#define IPA_BASE_OFFSET_4_0  0x01e00000
+#define IPA_BASE_SIZE_4_0    0x00034000
+#define GSI_BASE_OFFSET_4_0  0x01e04000
+#define GSI_BASE_SIZE_4_0    0x00028000
+
+struct resource ipa3_plat_drv_resource_4_0[] = {
+	/*
+	 * PLEASE NOTE WELL: The following offset values below
+	 * ("ipa-base", "gsi-base", and "intctrl-base") are used to
+	 * calculate offsets relative to the PCI BAR0 address provided
+	 * by the PCI probe.  After their use to calculate the
+	 * offsets, they are not used again, since PCI ultimately
+	 * dictates where things live.
+	 */
+	{
+		IPA_BASE_OFFSET_4_0,
+		(IPA_BASE_OFFSET_4_0 + IPA_BASE_SIZE_4_0),
+		"ipa-base",
+		IORESOURCE_MEM,
+		0,
+		NULL,
+		NULL,
+		NULL
+	},
+
+	{
+		GSI_BASE_OFFSET_4_0,
+		(GSI_BASE_OFFSET_4_0 + GSI_BASE_SIZE_4_0),
+		"gsi-base",
+		IORESOURCE_MEM,
+		0,
+		NULL,
+		NULL,
+		NULL
+	},
+
+	/*
+	 * The following entry is germane only to the emulator
+	 * environment.  It is needed to locate the emulator's PCI
+	 * interrupt controller...
+	 */
+	{
+		INTCTRL_OFFSET,
+		(INTCTRL_OFFSET + INTCTRL_SIZE),
+		"intctrl-base",
+		IORESOURCE_MEM,
+		0,
+		NULL,
+		NULL,
+		NULL
+	},
+
+	{
+		IPA_PIPE_MEM_START_OFST,
+		(IPA_PIPE_MEM_START_OFST + IPA_PIPE_MEM_SIZE),
+		"ipa-pipe-mem",
+		IORESOURCE_MEM,
+		0,
+		NULL,
+		NULL,
+		NULL
+	},
+
+	{
+		0,
+		0,
+		"gsi-irq",
+		IORESOURCE_IRQ,
+		0,
+		NULL,
+		NULL,
+		NULL
+	},
+
+	{
+		0,
+		0,
+		"ipa-irq",
+		IORESOURCE_IRQ,
+		0,
+		NULL,
+		NULL,
+		NULL
+	},
+};
+
+#define IPA_BASE_OFFSET_3_5_1  0x01e00000
+#define IPA_BASE_SIZE_3_5_1    0x00034000
+#define GSI_BASE_OFFSET_3_5_1  0x01e04000
+#define GSI_BASE_SIZE_3_5_1    0x0002c000
+
+struct resource ipa3_plat_drv_resource_3_5_1[] = {
+	/*
+	 * PLEASE NOTE WELL: The following offset values below
+	 * ("ipa-base", "gsi-base", and "intctrl-base") are used to
+	 * calculate offsets relative to the PCI BAR0 address provided
+	 * by the PCI probe.  After their use to calculate the
+	 * offsets, they are not used again, since PCI ultimately
+	 * dictates where things live.
+	 */
+	{
+		IPA_BASE_OFFSET_3_5_1,
+		(IPA_BASE_OFFSET_3_5_1 + IPA_BASE_SIZE_3_5_1),
+		"ipa-base",
+		IORESOURCE_MEM,
+		0,
+		NULL,
+		NULL,
+		NULL
+	},
+
+	{
+		GSI_BASE_OFFSET_3_5_1,
+		(GSI_BASE_OFFSET_3_5_1 + GSI_BASE_SIZE_3_5_1),
+		"gsi-base",
+		IORESOURCE_MEM,
+		0,
+		NULL,
+		NULL,
+		NULL
+	},
+
+	/*
+	 * The following entry is germane only to the emulator
+	 * environment.  It is needed to locate the emulator's PCI
+	 * interrupt controller...
+	 */
+	{
+		INTCTRL_OFFSET,
+		(INTCTRL_OFFSET + INTCTRL_SIZE),
+		"intctrl-base",
+		IORESOURCE_MEM,
+		0,
+		NULL,
+		NULL,
+		NULL
+	},
+
+	{
+		IPA_PIPE_MEM_START_OFST,
+		(IPA_PIPE_MEM_START_OFST + IPA_PIPE_MEM_SIZE),
+		"ipa-pipe-mem",
+		IORESOURCE_MEM,
+		0,
+		NULL,
+		NULL,
+		NULL
+	},
+
+	{
+		0,
+		0,
+		"gsi-irq",
+		IORESOURCE_IRQ,
+		0,
+		NULL,
+		NULL,
+		NULL
+	},
+
+	{
+		0,
+		0,
+		"ipa-irq",
+		IORESOURCE_IRQ,
+		0,
+		NULL,
+		NULL,
+		NULL
+	},
+};
+
+struct dtsi_replacement_resource_table
+ipa3_plat_drv_resource_table[] = {
+	{ ipa3_plat_drv_resource_3_5_1,
+	  ARRAY_SIZE(ipa3_plat_drv_resource_3_5_1) },
+	{ ipa3_plat_drv_resource_4_0,
+	  ARRAY_SIZE(ipa3_plat_drv_resource_4_0) },
+};
+
+/*
+ * The following code uses the data above...
+ */
+static u32 emulator_type_to_index(void)
+{
+	/*
+	 * Use the input parameter to the IPA driver loadable module,
+	 * which specifies the type of hardware the driver is running
+	 * on.
+	 */
+	u32 index = DTSI_INDEX_4_0;
+	uint emulation_type = ipa3_get_emulation_type();
+
+	switch (emulation_type) {
+	case IPA_HW_v3_5_1:
+		index = DTSI_INDEX_3_5_1;
+		break;
+	case IPA_HW_v4_0:
+		index = DTSI_INDEX_4_0;
+		break;
+	default:
+		break;
+	}
+
+	IPADBG("emulation_type(%u) emulation_index(%u)\n",
+	       emulation_type, index);
+
+	return index;
+}
+
+/* From include/linux/of.h */
+/**
+ * emulator_of_property_read_bool - Find from a property
+ * @np:         device node from which the property value is to be read.
+ * @propname:   name of the property to be searched.
+ *
+ * Search for a property in a device node.
+ * Returns true if the property exists false otherwise.
+ */
+bool emulator_of_property_read_bool(
+	const struct device_node *np,
+	const char *propname)
+{
+	u16 i;
+	u32 index;
+	struct dtsi_replacement_bool *ipa3_plat_drv_boolP;
+
+	/*
+	 * Get the index for the type of hardware we're running on.
+	 * This is used as a table index.
+	 */
+	index = emulator_type_to_index();
+	if (index >= ARRAY_SIZE(ipa3_plat_drv_bool_table)) {
+		IPADBG(
+		    "Did not find ipa3_plat_drv_bool_table for index %u\n",
+		    index);
+		return false;
+	}
+
+	ipa3_plat_drv_boolP =
+	    ipa3_plat_drv_bool_table[index].p_table;
+
+	for (i = 0;
+	     i < ipa3_plat_drv_bool_table[index].num_entries;
+	     i++) {
+		if (strsame(ipa3_plat_drv_boolP[i].key, propname)) {
+			IPADBG(
+			    "Found value %u for propname %s index %u\n",
+			    ipa3_plat_drv_boolP[i].value,
+			    propname,
+			    index);
+			return ipa3_plat_drv_boolP[i].value;
+		}
+	}
+
+	IPADBG("Did not find match for propname %s index %u\n",
+	       propname,
+	       index);
+
+	return false;
+}
+
+/* From include/linux/of.h */
+int emulator_of_property_read_u32(
+	const struct device_node *np,
+	const char *propname,
+	u32 *out_value)
+{
+	u16 i;
+	u32 index;
+	struct dtsi_replacement_u32 *ipa3_plat_drv_u32P;
+
+	/*
+	 * Get the index for the type of hardware we're running on.
+	 * This is used as a table index.
+	 */
+	index = emulator_type_to_index();
+	if (index >= ARRAY_SIZE(ipa3_plat_drv_u32_table)) {
+		IPADBG(
+		    "Did not find ipa3_plat_drv_u32_table for index %u\n",
+		    index);
+		return false;
+	}
+
+	ipa3_plat_drv_u32P =
+	    ipa3_plat_drv_u32_table[index].p_table;
+
+	for (i = 0;
+	     i < ipa3_plat_drv_u32_table[index].num_entries;
+	     i++) {
+		if (strsame(ipa3_plat_drv_u32P[i].key, propname)) {
+			*out_value = ipa3_plat_drv_u32P[i].value;
+			IPADBG(
+			    "Found value %u for propname %s index %u\n",
+			    ipa3_plat_drv_u32P[i].value,
+			    propname,
+			    index);
+			return 0;
+		}
+	}
+
+	IPADBG("Did not find match for propname %s index %u\n",
+	       propname,
+	       index);
+
+	return -EINVAL;
+}
+
+/* From include/linux/of.h */
+/**
+ * emulator_of_property_read_u32_array - Find and read an array of 32
+ * bit integers from a property.
+ *
+ * @np:         device node from which the property value is to be read.
+ * @propname:   name of the property to be searched.
+ * @out_values: pointer to return value, modified only if return value is 0.
+ * @sz:         number of array elements to read
+ *
+ * Search for a property in a device node and read 32-bit value(s) from
+ * it. Returns 0 on success, -EINVAL if the property does not exist,
+ * -ENODATA if property does not have a value, and -EOVERFLOW if the
+ * property data isn't large enough.
+ *
+ * The out_values is modified only if a valid u32 value can be decoded.
+ */
+int emulator_of_property_read_u32_array(
+	const struct device_node *np,
+	const char *propname,
+	u32 *out_values,
+	size_t sz)
+{
+	u16 i;
+	u32 index;
+	struct dtsi_replacement_u32_array *u32_arrayP;
+
+	/*
+	 * Get the index for the type of hardware we're running on.
+	 * This is used as a table index.
+	 */
+	index = emulator_type_to_index();
+	if (index >= ARRAY_SIZE(ipa3_plat_drv_u32_array_table)) {
+		IPADBG(
+		    "Did not find ipa3_plat_drv_u32_array_table for index %u\n",
+		    index);
+		return false;
+	}
+
+	u32_arrayP =
+		ipa3_plat_drv_u32_array_table[index].p_table;
+	for (i = 0;
+	     i < ipa3_plat_drv_u32_array_table[index].num_entries;
+	     i++) {
+		if (strsame(
+			u32_arrayP[i].key, propname)) {
+			u32 num_elements =
+			    u32_arrayP[i].num_elements;
+			u32 *p_element =
+			    &u32_arrayP[i].p_value[0];
+			size_t j = 0;
+
+			if (num_elements > sz) {
+				IPAERR(
+				    "Found array of %u values for propname %s; only room for %u elements in copy buffer\n",
+				    num_elements,
+				    propname,
+				    (unsigned int) sz);
+				return -EOVERFLOW;
+			}
+
+			while (j++ < num_elements)
+				*out_values++ = *p_element++;
+
+			IPADBG(
+			    "Found array of values starting with %u for propname %s index %u\n",
+			    u32_arrayP[i].p_value[0],
+			    propname,
+			    index);
+
+			return 0;
+		}
+	}
+
+	IPADBG("Did not find match for propname %s index %u\n",
+	       propname,
+	       index);
+
+	return -EINVAL;
+}
+
+/* From drivers/base/platform.c */
+/**
+ * emulator_platform_get_resource_byname - get a resource for a device by name
+ * @dev: platform device
+ * @type: resource type
+ * @name: resource name
+ */
+struct resource *emulator_platform_get_resource_byname(
+	struct platform_device *dev,
+	unsigned int type,
+	const char *name)
+{
+	u16 i;
+	u32 index;
+	struct resource *ipa3_plat_drv_resourceP;
+
+	/*
+	 * Get the index for the type of hardware we're running on.
+	 * This is used as a table index.
+	 */
+	index = emulator_type_to_index();
+	if (index >= ARRAY_SIZE(ipa3_plat_drv_resource_table)) {
+		IPADBG(
+		    "Did not find ipa3_plat_drv_resource_table for index %u\n",
+		    index);
+		return false;
+	}
+
+	ipa3_plat_drv_resourceP =
+		ipa3_plat_drv_resource_table[index].p_table;
+	for (i = 0;
+	     i < ipa3_plat_drv_resource_table[index].num_entries;
+	     i++) {
+		struct resource *r = &ipa3_plat_drv_resourceP[i];
+
+		if (type == resource_type(r) && strsame(r->name, name)) {
+			IPADBG(
+			    "Found start 0x%x size %u for name %s index %u\n",
+			    (unsigned int) (r->start),
+			    (unsigned int) (resource_size(r)),
+			    name,
+			    index);
+			return r;
+		}
+	}
+
+	IPADBG("Did not find match for name %s index %u\n",
+	       name,
+	       index);
+
+	return NULL;
+}
+
+/* From drivers/of/base.c */
+/**
+ * emulator_of_property_count_elems_of_size - Count the number of
+ * elements in a property
+ *
+ * @np:         device node from which the property value is to
+ *              be read. Not used.
+ * @propname:   name of the property to be searched.
+ * @elem_size:  size of the individual element
+ *
+ * Search for a property and count the number of elements of size
+ * elem_size in it. Returns number of elements on success, -EINVAL if
+ * the property does not exist or its length does not match a multiple
+ * of elem_size and -ENODATA if the property does not have a value.
+ */
+int emulator_of_property_count_elems_of_size(
+	const struct device_node *np,
+	const char *propname,
+	int elem_size)
+{
+	u32 index;
+
+	/*
+	 * Get the index for the type of hardware we're running on.
+	 * This is used as a table index.
+	 */
+	index = emulator_type_to_index();
+
+	/*
+	 * Use elem_size to determine which table to search for the
+	 * specified property name
+	 */
+	if (elem_size == sizeof(u32)) {
+		u16 i;
+		struct dtsi_replacement_u32_array *u32_arrayP;
+
+		if (index >= ARRAY_SIZE(ipa3_plat_drv_u32_array_table)) {
+			IPADBG(
+			    "Did not find ipa3_plat_drv_u32_array_table for index %u\n",
+			    index);
+			return false;
+		}
+
+		u32_arrayP =
+			ipa3_plat_drv_u32_array_table[index].p_table;
+
+		for (i = 0;
+		     i < ipa3_plat_drv_u32_array_table[index].num_entries;
+		     i++) {
+			if (strsame(u32_arrayP[i].key, propname)) {
+				if (u32_arrayP[i].p_value == NULL) {
+					IPADBG(
+					    "Found no elements for propname %s index %u\n",
+					    propname,
+					    index);
+					return -ENODATA;
+				}
+
+				IPADBG(
+				    "Found %u elements for propname %s index %u\n",
+				    u32_arrayP[i].num_elements,
+				    propname,
+				    index);
+
+				return u32_arrayP[i].num_elements;
+			}
+		}
+
+		IPADBG(
+		    "Found no match in table with elem_size %d for propname %s index %u\n",
+		    elem_size,
+		    propname,
+		    index);
+
+		return -EINVAL;
+	}
+
+	IPAERR(
+	    "Found no tables with element size %u to search for propname %s index %u\n",
+	    elem_size,
+	    propname,
+	    index);
+
+	return -EINVAL;
+}
+
+int emulator_of_property_read_variable_u32_array(
+	const struct device_node *np,
+	const char *propname,
+	u32 *out_values,
+	size_t sz_min,
+	size_t sz_max)
+{
+	return emulator_of_property_read_u32_array(
+	    np, propname, out_values, sz_max);
+}
+
+resource_size_t emulator_resource_size(const struct resource *res)
+{
+	return res->end - res->start;
+}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_emulation_stubs.h b/drivers/platform/msm/ipa/ipa_v3/ipa_emulation_stubs.h
new file mode 100644
index 0000000..cf4c7c9
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_emulation_stubs.h
@@ -0,0 +1,125 @@
+/* Copyright (c) 2018, 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.
+ */
+
+#if !defined(_IPA_EMULATION_STUBS_H_)
+# define _IPA_EMULATION_STUBS_H_
+
+# define clk_get(x, y) ((struct clk *) -(MAX_ERRNO+1))
+# define clk_put(x)                do { } while (0)
+# define clk_prepare(x)            do { } while (0)
+# define clk_enable(x)             do { } while (0)
+# define clk_set_rate(x, y)        do { } while (0)
+# define clk_disable_unprepare(x)  do { } while (0)
+
+# define outer_flush_range(x, y)
+# define __flush_dcache_area(x, y)
+# define __cpuc_flush_dcache_area(x, y) __flush_dcache_area(x, y)
+
+/* Point several API calls to these new EMULATION functions */
+# define of_property_read_bool(np, propname)	 \
+	emulator_of_property_read_bool(NULL, propname)
+# define of_property_read_u32(np, propname, out_value)   \
+	emulator_of_property_read_u32(NULL, propname, out_value)
+# define of_property_read_u32_array(np, propname, out_values, sz)	\
+	emulator_of_property_read_u32_array(NULL, propname, out_values, sz)
+# define platform_get_resource_byname(dev, type, name) \
+	emulator_platform_get_resource_byname(NULL, type, name)
+# define of_property_count_elems_of_size(np, propname, elem_size) \
+	emulator_of_property_count_elems_of_size(NULL, propname, elem_size)
+# define of_property_read_variable_u32_array( \
+	np, propname, out_values, sz_min, sz_max) \
+	emulator_of_property_read_variable_u32_array( \
+		NULL, propname, out_values, sz_min, sz_max)
+# define resource_size(res) \
+	emulator_resource_size(res)
+
+/**
+ * emulator_of_property_read_bool - Findfrom a property
+ * @np:         device node used to find the property value. (not used)
+ * @propname:   name of the property to be searched.
+ *
+ * Search for a property in a device node.
+ * Returns true if the property exists false otherwise.
+ */
+bool emulator_of_property_read_bool(
+	const struct device_node *np,
+	const char *propname);
+
+int emulator_of_property_read_u32(
+	const struct device_node *np,
+	const char *propname,
+	u32 *out_value);
+
+/**
+ * emulator_of_property_read_u32_array - Find and read an array of 32
+ * bit integers from a property.
+ *
+ * @np:         device node used to find the property value. (not used)
+ * @propname:   name of the property to be searched.
+ * @out_values: pointer to return value, modified only if return value is 0.
+ * @sz:         number of array elements to read
+ *
+ * Search for a property in a device node and read 32-bit value(s) from
+ * it. Returns 0 on success, -EINVAL if the property does not exist,
+ * -ENODATA if property does not have a value, and -EOVERFLOW if the
+ * property data isn't large enough.
+ *
+ * The out_values is modified only if a valid u32 value can be decoded.
+ */
+int emulator_of_property_read_u32_array(
+	const struct device_node *np,
+	const char *propname,
+	u32 *out_values,
+	size_t sz);
+
+/**
+ * emulator_platform_get_resource_byname - get a resource for a device
+ * by name
+ *
+ * @dev: platform device
+ * @type: resource type
+ * @name: resource name
+ */
+struct resource *emulator_platform_get_resource_byname(
+	struct platform_device *dev,
+	unsigned int type,
+	const char *name);
+
+/**
+ * emulator_of_property_count_elems_of_size - Count the number of
+ * elements in a property
+ *
+ * @np:         device node used to find the property value. (not used)
+ * @propname:   name of the property to be searched.
+ * @elem_size:  size of the individual element
+ *
+ * Search for a property and count the number of elements of size
+ * elem_size in it. Returns number of elements on success, -EINVAL if
+ * the property does not exist or its length does not match a multiple
+ * of elem_size and -ENODATA if the property does not have a value.
+ */
+int emulator_of_property_count_elems_of_size(
+	const struct device_node *np,
+	const char *propname,
+	int elem_size);
+
+int emulator_of_property_read_variable_u32_array(
+	const struct device_node *np,
+	const char *propname,
+	u32 *out_values,
+	size_t sz_min,
+	size_t sz_max);
+
+resource_size_t emulator_resource_size(
+	const struct resource *res);
+
+#endif /* #if !defined(_IPA_EMULATION_STUBS_H_) */
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c
index 29fd547..6742773 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c
@@ -61,8 +61,10 @@
 	gen_params.rule = (const struct ipa_flt_rule *)&entry->rule;
 
 	res = ipahal_flt_generate_hw_rule(&gen_params, &entry->hw_len, buf);
-	if (res)
+	if (res) {
 		IPAERR_RL("failed to generate flt h/w rule\n");
+		return res;
+	}
 
 	return 0;
 }
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
index 56b5740..fe649a7 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
@@ -203,6 +203,188 @@
 #define IPA_WDI_TX_DB_RES			7
 #define IPA_WDI_MAX_RES				8
 
+#ifdef CONFIG_ARM64
+/* Outer caches unsupported on ARM64 platforms */
+# define outer_flush_range(x, y)
+# define __cpuc_flush_dcache_area __flush_dcache_area
+#endif
+
+#define IPA_GPIO_IN_QUERY_CLK_IDX 0
+#define IPA_GPIO_OUT_CLK_RSP_CMPLT_IDX 0
+#define IPA_GPIO_OUT_CLK_VOTE_IDX 1
+
+#define IPA_SUMMING_THRESHOLD (0x10)
+#define IPA_PIPE_MEM_START_OFST (0x0)
+#define IPA_PIPE_MEM_SIZE (0x0)
+#define IPA_MOBILE_AP_MODE(x) (x == IPA_MODE_MOBILE_AP_ETH || \
+			       x == IPA_MODE_MOBILE_AP_WAN || \
+			       x == IPA_MODE_MOBILE_AP_WLAN)
+#define IPA_CNOC_CLK_RATE (75 * 1000 * 1000UL)
+#define IPA_A5_MUX_HEADER_LENGTH (8)
+
+#define IPA_AGGR_MAX_STR_LENGTH (10)
+
+#define CLEANUP_TAG_PROCESS_TIMEOUT 500
+
+#define IPA_AGGR_STR_IN_BYTES(str) \
+	(strnlen((str), IPA_AGGR_MAX_STR_LENGTH - 1) + 1)
+
+#define IPA_TRANSPORT_PROD_TIMEOUT_MSEC 100
+
+#define IPA3_ACTIVE_CLIENTS_TABLE_BUF_SIZE 2048
+
+#define IPA3_ACTIVE_CLIENT_LOG_TYPE_EP 0
+#define IPA3_ACTIVE_CLIENT_LOG_TYPE_SIMPLE 1
+#define IPA3_ACTIVE_CLIENT_LOG_TYPE_RESOURCE 2
+#define IPA3_ACTIVE_CLIENT_LOG_TYPE_SPECIAL 3
+
+#define IPA_MHI_GSI_EVENT_RING_ID_START 10
+#define IPA_MHI_GSI_EVENT_RING_ID_END 12
+
+#define IPA_SMEM_SIZE (8 * 1024)
+
+#define IPA_GSI_CHANNEL_HALT_MIN_SLEEP 5000
+#define IPA_GSI_CHANNEL_HALT_MAX_SLEEP 10000
+#define IPA_GSI_CHANNEL_HALT_MAX_TRY 10
+
+/* round addresses for closes page per SMMU requirements */
+#define IPA_SMMU_ROUND_TO_PAGE(iova, pa, size, iova_p, pa_p, size_p) \
+	do { \
+		(iova_p) = rounddown((iova), PAGE_SIZE); \
+		(pa_p) = rounddown((pa), PAGE_SIZE); \
+		(size_p) = roundup((size) + (pa) - (pa_p), PAGE_SIZE); \
+	} while (0)
+
+
+/* The relative location in /lib/firmware where the FWs will reside */
+#define IPA_FWS_PATH         "ipa/ipa_fws.elf"
+/*
+ * The following paths below are used when building the system for the
+ * emulation environment or when IPA_EMULATION_COMPILE == 1.
+ *
+ * As new hardware platforms are added into the emulation environment,
+ * please add the appropriate paths here for their firmwares.
+ */
+#define IPA_FWS_PATH_4_0     "ipa/4.0/ipa_fws.elf"
+#define IPA_FWS_PATH_3_5_1   "ipa/3.5.1/ipa_fws.elf"
+
+#ifdef CONFIG_COMPAT
+#define IPA_IOC_ADD_HDR32 _IOWR(IPA_IOC_MAGIC, \
+					IPA_IOCTL_ADD_HDR, \
+					compat_uptr_t)
+#define IPA_IOC_DEL_HDR32 _IOWR(IPA_IOC_MAGIC, \
+					IPA_IOCTL_DEL_HDR, \
+					compat_uptr_t)
+#define IPA_IOC_ADD_RT_RULE32 _IOWR(IPA_IOC_MAGIC, \
+					IPA_IOCTL_ADD_RT_RULE, \
+					compat_uptr_t)
+#define IPA_IOC_DEL_RT_RULE32 _IOWR(IPA_IOC_MAGIC, \
+					IPA_IOCTL_DEL_RT_RULE, \
+					compat_uptr_t)
+#define IPA_IOC_ADD_FLT_RULE32 _IOWR(IPA_IOC_MAGIC, \
+					IPA_IOCTL_ADD_FLT_RULE, \
+					compat_uptr_t)
+#define IPA_IOC_DEL_FLT_RULE32 _IOWR(IPA_IOC_MAGIC, \
+					IPA_IOCTL_DEL_FLT_RULE, \
+					compat_uptr_t)
+#define IPA_IOC_GET_RT_TBL32 _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_GET_RT_TBL, \
+				compat_uptr_t)
+#define IPA_IOC_COPY_HDR32 _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_COPY_HDR, \
+				compat_uptr_t)
+#define IPA_IOC_QUERY_INTF32 _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_QUERY_INTF, \
+				compat_uptr_t)
+#define IPA_IOC_QUERY_INTF_TX_PROPS32 _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_QUERY_INTF_TX_PROPS, \
+				compat_uptr_t)
+#define IPA_IOC_QUERY_INTF_RX_PROPS32 _IOWR(IPA_IOC_MAGIC, \
+					IPA_IOCTL_QUERY_INTF_RX_PROPS, \
+					compat_uptr_t)
+#define IPA_IOC_QUERY_INTF_EXT_PROPS32 _IOWR(IPA_IOC_MAGIC, \
+					IPA_IOCTL_QUERY_INTF_EXT_PROPS, \
+					compat_uptr_t)
+#define IPA_IOC_GET_HDR32 _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_GET_HDR, \
+				compat_uptr_t)
+#define IPA_IOC_ALLOC_NAT_MEM32 _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_ALLOC_NAT_MEM, \
+				compat_uptr_t)
+#define IPA_IOC_ALLOC_NAT_TABLE32 _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_ALLOC_NAT_TABLE, \
+				compat_uptr_t)
+#define IPA_IOC_ALLOC_IPV6CT_TABLE32 _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_ALLOC_IPV6CT_TABLE, \
+				compat_uptr_t)
+#define IPA_IOC_V4_INIT_NAT32 _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_V4_INIT_NAT, \
+				compat_uptr_t)
+#define IPA_IOC_INIT_IPV6CT_TABLE32 _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_INIT_IPV6CT_TABLE, \
+				compat_uptr_t)
+#define IPA_IOC_TABLE_DMA_CMD32 _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_TABLE_DMA_CMD, \
+				compat_uptr_t)
+#define IPA_IOC_V4_DEL_NAT32 _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_V4_DEL_NAT, \
+				compat_uptr_t)
+#define IPA_IOC_DEL_NAT_TABLE32 _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_DEL_NAT_TABLE, \
+				compat_uptr_t)
+#define IPA_IOC_DEL_IPV6CT_TABLE32 _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_DEL_IPV6CT_TABLE, \
+				compat_uptr_t)
+#define IPA_IOC_NAT_MODIFY_PDN32 _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_NAT_MODIFY_PDN, \
+				compat_uptr_t)
+#define IPA_IOC_GET_NAT_OFFSET32 _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_GET_NAT_OFFSET, \
+				compat_uptr_t)
+#define IPA_IOC_PULL_MSG32 _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_PULL_MSG, \
+				compat_uptr_t)
+#define IPA_IOC_RM_ADD_DEPENDENCY32 _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_RM_ADD_DEPENDENCY, \
+				compat_uptr_t)
+#define IPA_IOC_RM_DEL_DEPENDENCY32 _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_RM_DEL_DEPENDENCY, \
+				compat_uptr_t)
+#define IPA_IOC_GENERATE_FLT_EQ32 _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_GENERATE_FLT_EQ, \
+				compat_uptr_t)
+#define IPA_IOC_QUERY_RT_TBL_INDEX32 _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_QUERY_RT_TBL_INDEX, \
+				compat_uptr_t)
+#define IPA_IOC_WRITE_QMAPID32  _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_WRITE_QMAPID, \
+				compat_uptr_t)
+#define IPA_IOC_MDFY_FLT_RULE32 _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_MDFY_FLT_RULE, \
+				compat_uptr_t)
+#define IPA_IOC_NOTIFY_WAN_UPSTREAM_ROUTE_ADD32 _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_NOTIFY_WAN_UPSTREAM_ROUTE_ADD, \
+				compat_uptr_t)
+#define IPA_IOC_NOTIFY_WAN_UPSTREAM_ROUTE_DEL32 _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_NOTIFY_WAN_UPSTREAM_ROUTE_DEL, \
+				compat_uptr_t)
+#define IPA_IOC_NOTIFY_WAN_EMBMS_CONNECTED32 _IOWR(IPA_IOC_MAGIC, \
+					IPA_IOCTL_NOTIFY_WAN_EMBMS_CONNECTED, \
+					compat_uptr_t)
+#define IPA_IOC_ADD_HDR_PROC_CTX32 _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_ADD_HDR_PROC_CTX, \
+				compat_uptr_t)
+#define IPA_IOC_DEL_HDR_PROC_CTX32 _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_DEL_HDR_PROC_CTX, \
+				compat_uptr_t)
+#define IPA_IOC_MDFY_RT_RULE32 _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_MDFY_RT_RULE, \
+				compat_uptr_t)
+#endif /* #ifdef CONFIG_COMPAT */
+
+#define IPA_TZ_UNLOCK_ATTRIBUTE 0x0C0311
+#define TZ_MEM_PROTECT_REGION_ID 0x10
+
 struct ipa3_active_client_htable_entry {
 	struct hlist_node list;
 	char id_string[IPA3_ACTIVE_CLIENTS_LOG_NAME_LEN];
@@ -912,11 +1094,13 @@
  * @IPA_HW_Normal: Regular IPA hardware
  * @IPA_HW_Virtual: IPA hardware supporting virtual memory allocation
  * @IPA_HW_PCIE: IPA hardware supporting memory allocation over PCIE Bridge
+ * @IPA_HW_Emulation: IPA emulation hardware
  */
 enum ipa3_hw_mode {
-	IPA_HW_MODE_NORMAL  = 0,
-	IPA_HW_MODE_VIRTUAL = 1,
-	IPA_HW_MODE_PCIE    = 2
+	IPA_HW_MODE_NORMAL    = 0,
+	IPA_HW_MODE_VIRTUAL   = 1,
+	IPA_HW_MODE_PCIE      = 2,
+	IPA_HW_MODE_EMULATION = 3,
 };
 
 enum ipa3_config_this_ep {
@@ -1207,6 +1391,7 @@
  * @mode: IPA operating mode
  * @mmio: iomem
  * @ipa_wrapper_base: IPA wrapper base address
+ * @ipa_wrapper_size: size of the memory pointed to by ipa_wrapper_base
  * @hdr_tbl: IPA header table
  * @hdr_proc_ctx_tbl: IPA processing context table
  * @rt_tbl_set: list of routing tables each of which is a list of rules
@@ -1423,6 +1608,9 @@
 	u32 ipa_mem_size;
 	u32 transport_mem_base;
 	u32 transport_mem_size;
+	u32 emulator_intcntrlr_mem_base;
+	u32 emulator_intcntrlr_mem_size;
+	u32 emulator_irq;
 	u32 ipa_irq;
 	u32 transport_irq;
 	u32 ipa_pipe_mem_start_ofst;
@@ -2355,6 +2543,10 @@
 void ipa3_inc_acquire_wakelock(void);
 void ipa3_dec_release_wakelock(void);
 int ipa3_load_fws(const struct firmware *firmware, phys_addr_t gsi_mem_base);
+int emulator_load_fws(
+	const struct firmware *firmware,
+	u32 transport_mem_base,
+	u32 transport_mem_size);
 int ipa3_register_ipa_ready_cb(void (*ipa_ready_cb)(void *), void *user_data);
 const char *ipa_hw_error_str(enum ipa3_hw_errors err_type);
 int ipa_gsi_ch20_wa(void);
@@ -2381,4 +2573,9 @@
 void ipa3_init_imm_cmd_desc(struct ipa3_desc *desc,
 	struct ipahal_imm_cmd_pyld *cmd_pyld);
 int ipa3_is_vlan_mode(enum ipa_vlan_ifaces iface, bool *res);
+uint ipa3_get_emulation_type(void);
+int ipa3_get_transport_info(
+	phys_addr_t *phys_addr_ptr,
+	unsigned long *size_ptr);
+irq_handler_t ipa3_get_isr(void);
 #endif /* _IPA3_I_H_ */
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_interrupts.c b/drivers/platform/msm/ipa/ipa_v3/ipa_interrupts.c
index d69d6ae..46b7434 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_interrupts.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_interrupts.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2018, 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
@@ -339,6 +339,12 @@
 	ipa3_dec_client_disable_clks_no_block(&log_info);
 	return IRQ_HANDLED;
 }
+
+irq_handler_t ipa3_get_isr(void)
+{
+	return ipa3_isr;
+}
+
 /**
 * ipa3_add_interrupt_handler() - Adds handler to an interrupt type
 * @interrupt:		Interrupt type
@@ -486,21 +492,35 @@
 		return -ENOMEM;
 	}
 
-	res = request_irq(ipa_irq, (irq_handler_t) ipa3_isr,
-				IRQF_TRIGGER_RISING, "ipa", ipa_dev);
-	if (res) {
-		IPAERR("fail to register IPA IRQ handler irq=%d\n", ipa_irq);
-		return -ENODEV;
+	/*
+	 * NOTE:
+	 *
+	 *  We'll only register an isr on non-emulator (ie. real UE)
+	 *  systems.
+	 *
+	 *  On the emulator, emulator_soft_irq_isr() will be calling
+	 *  ipa3_isr, so hence, no isr registration here, and instead,
+	 *  we'll pass the address of ipa3_isr to the gsi layer where
+	 *  emulator interrupts are handled...
+	 */
+	if (ipa3_ctx->ipa3_hw_mode != IPA_HW_MODE_EMULATION) {
+		res = request_irq(ipa_irq, (irq_handler_t) ipa3_isr,
+					IRQF_TRIGGER_RISING, "ipa", ipa_dev);
+		if (res) {
+			IPAERR(
+			    "fail to register IPA IRQ handler irq=%d\n",
+			    ipa_irq);
+			return -ENODEV;
+		}
+		IPADBG("IPA IRQ handler irq=%d registered\n", ipa_irq);
+
+		res = enable_irq_wake(ipa_irq);
+		if (res)
+			IPAERR("fail to enable IPA IRQ wakeup irq=%d res=%d\n",
+				   ipa_irq, res);
+		else
+			IPADBG("IPA IRQ wakeup enabled irq=%d\n", ipa_irq);
 	}
-	IPADBG("IPA IRQ handler irq=%d registered\n", ipa_irq);
-
-	res = enable_irq_wake(ipa_irq);
-	if (res)
-		IPAERR("fail to enable IPA IRQ wakeup irq=%d res=%d\n",
-				ipa_irq, res);
-	else
-		IPADBG("IPA IRQ wakeup enabled irq=%d\n", ipa_irq);
-
 	spin_lock_init(&suspend_wa_lock);
 	return 0;
 }
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_nat.c b/drivers/platform/msm/ipa/ipa_v3/ipa_nat.c
index b48f2c4..7065e2c 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_nat.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_nat.c
@@ -1258,6 +1258,11 @@
 		goto bail;
 	}
 
+	if (!ipa3_ctx->nat_mem.dev.is_dev_init) {
+		IPAERR_RL("NAT hasn't been initialized\n");
+		return -EPERM;
+	}
+
 	for (cnt = 0; cnt < dma->entries; ++cnt) {
 		result = ipa3_table_validate_table_dma_one(&dma->dma[cnt]);
 		if (result) {
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_pm.h b/drivers/platform/msm/ipa/ipa_v3/ipa_pm.h
index 0772dde..47a03f9 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_pm.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_pm.h
@@ -16,7 +16,7 @@
 #include <linux/msm_ipa.h>
 
 /* internal to ipa */
-#define IPA_PM_MAX_CLIENTS 12 /* actual max is value -1 since we start from 1*/
+#define IPA_PM_MAX_CLIENTS 32 /* actual max is value -1 since we start from 1*/
 #define IPA_PM_MAX_EX_CL 64
 #define IPA_PM_THRESHOLD_MAX 5
 #define IPA_PM_EXCEPTION_MAX 2
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.h b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.h
index 3351a33..3210a70 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2018, 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
@@ -399,7 +399,7 @@
 
 static inline void ipa3_q6_handshake_complete(bool ssr_bootup) { }
 
-static inline int ipa3_wwan_set_modem_perf_profile(int throughput)
+static inline int ipa3_wwan_set_modem_perf_profile(int throughput);
 static inline int ipa3_qmi_enable_per_client_stats(
 	struct ipa_enable_per_client_stats_req_msg_v01 *req,
 	struct ipa_enable_per_client_stats_resp_msg_v01 *resp)
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
index 5d6d3cd..b3a7f98 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
@@ -24,6 +24,13 @@
 #include "ipahal/ipahal_hw_stats.h"
 #include "../ipa_rm_i.h"
 
+/*
+ * The following for adding code (ie. for EMULATION) not found on x86.
+ */
+#if IPA_EMULATION_COMPILE == 1
+# include "ipa_emulation_stubs.h"
+#endif
+
 #define IPA_V3_0_CLK_RATE_SVS2 (37.5 * 1000 * 1000UL)
 #define IPA_V3_0_CLK_RATE_SVS (75 * 1000 * 1000UL)
 #define IPA_V3_0_CLK_RATE_NOMINAL (150 * 1000 * 1000UL)
@@ -964,12 +971,12 @@
 	/* IPA_3_5_1 */
 	[IPA_3_5_1][IPA_CLIENT_WLAN1_PROD]          = {
 			true, IPA_v3_5_GROUP_UL_DL, true,
-			IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
+			IPA_DPS_HPS_REP_SEQ_TYPE_2PKT_PROC_PASS_NO_DEC_UCP_DMAP,
 			QMB_MASTER_SELECT_DDR,
 			{ 7, 1, 8, 16, IPA_EE_UC } },
 	[IPA_3_5_1][IPA_CLIENT_USB_PROD]            = {
 			true, IPA_v3_5_GROUP_UL_DL, true,
-			IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
+			IPA_DPS_HPS_REP_SEQ_TYPE_2PKT_PROC_PASS_NO_DEC_UCP_DMAP,
 			QMB_MASTER_SELECT_DDR,
 			{ 0, 0, 8, 16, IPA_EE_AP } },
 	[IPA_3_5_1][IPA_CLIENT_APPS_LAN_PROD] = {
@@ -979,7 +986,7 @@
 			{ 8, 7, 8, 16, IPA_EE_AP } },
 	[IPA_3_5_1][IPA_CLIENT_APPS_WAN_PROD] = {
 			true, IPA_v3_5_GROUP_UL_DL, true,
-			IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
+			IPA_DPS_HPS_REP_SEQ_TYPE_2PKT_PROC_PASS_NO_DEC_UCP_DMAP,
 			QMB_MASTER_SELECT_DDR,
 			{ 2, 3, 16, 32, IPA_EE_AP } },
 	[IPA_3_5_1][IPA_CLIENT_APPS_CMD_PROD]		= {
@@ -2322,6 +2329,32 @@
 }
 
 /**
+ * ipa3_get_client_by_pipe() - return client type relative to pipe
+ * index
+ * @pipe_idx: IPA end-point number
+ *
+ * Return value: client type
+ */
+static enum ipa_client_type ipa3_get_client_by_pipe(int pipe_idx)
+{
+	int j = 0;
+
+	for (j = 0; j < IPA_CLIENT_MAX; j++) {
+		const struct ipa_ep_configuration *iec_ptr =
+			&(ipa3_ep_mapping[ipa3_get_hw_type_index()][j]);
+		if (iec_ptr->valid &&
+		    iec_ptr->ipa_gsi_ep_info.ipa_ep_num == pipe_idx)
+			break;
+	}
+
+	if (j == IPA_CLIENT_MAX)
+		IPADBG("ipa3_get_client_by_pipe(%d) can't find client\n",
+		       pipe_idx);
+
+	return j;
+}
+
+/**
  * ipa_init_ep_flt_bitmap() - Initialize the bitmap
  * that represents the End-points that supports filtering
  */
@@ -4870,7 +4903,8 @@
 	}
 
 	/* move resource group configuration from HLOS to TZ */
-	if (ipa3_ctx->ipa_hw_type >= IPA_HW_v3_1) {
+	if (ipa3_ctx->ipa3_hw_mode != IPA_HW_MODE_EMULATION &&
+	    ipa3_ctx->ipa_hw_type >= IPA_HW_v3_1) {
 		IPAERR("skip configuring ipa_rx_hps_clients from HLOS\n");
 		return;
 	}
@@ -5179,6 +5213,77 @@
 }
 
 /**
+ * emulator_load_single_fw() - load firmware into emulator's memory
+ *
+ * @firmware: Structure which contains the FW data from the user space.
+ * @phdr: ELF program header
+ * @fw_base: memory location to which firmware should get loaded
+ * @offset_from_base: offset to start relative to fw_base
+ *
+ * Return value: 0 on success, negative otherwise
+ */
+static int emulator_load_single_fw(
+	const struct firmware   *firmware,
+	const struct elf32_phdr *phdr,
+	void __iomem            *fw_base,
+	uint32_t                offset_from_base)
+{
+	int index;
+	uint32_t ofb;
+	const uint32_t *elf_data_ptr;
+
+	IPADBG("firmware(%pK) phdr(%pK) fw_base(%pK) offset_from_base(0x%x)\n",
+	       firmware, phdr, fw_base, offset_from_base);
+
+	if (phdr->p_offset > firmware->size) {
+		IPAERR("Invalid ELF: offset=%u is beyond elf_size=%zu\n",
+			phdr->p_offset, firmware->size);
+		return -EINVAL;
+	}
+	if ((firmware->size - phdr->p_offset) < phdr->p_filesz) {
+		IPAERR("Invalid ELF: offset=%u filesz=%u elf_size=%zu\n",
+			phdr->p_offset, phdr->p_filesz, firmware->size);
+		return -EINVAL;
+	}
+
+	if (phdr->p_memsz % sizeof(uint32_t)) {
+		IPAERR("FW mem size %u doesn't align to 32bit\n",
+			phdr->p_memsz);
+		return -EFAULT;
+	}
+
+	if (phdr->p_filesz > phdr->p_memsz) {
+		IPAERR("FW image too big src_size=%u dst_size=%u\n",
+			phdr->p_filesz, phdr->p_memsz);
+		return -EFAULT;
+	}
+
+	IPADBG("ELF: p_memsz(0x%x) p_filesz(0x%x) p_filesz/4(0x%x)\n",
+	       (uint32_t) phdr->p_memsz,
+	       (uint32_t) phdr->p_filesz,
+	       (uint32_t) (phdr->p_filesz/sizeof(uint32_t)));
+
+	/* Set the entire region to 0s */
+	ofb = offset_from_base;
+	for (index = 0; index < phdr->p_memsz/sizeof(uint32_t); index++) {
+		writel_relaxed(0, fw_base + ofb);
+		ofb += sizeof(uint32_t);
+	}
+
+	elf_data_ptr = (uint32_t *)(firmware->data + phdr->p_offset);
+
+	/* Write the FW */
+	ofb = offset_from_base;
+	for (index = 0; index < phdr->p_filesz/sizeof(uint32_t); index++) {
+		writel_relaxed(*elf_data_ptr, fw_base + ofb);
+		elf_data_ptr++;
+		ofb += sizeof(uint32_t);
+	}
+
+	return 0;
+}
+
+/**
  * ipa3_load_fws() - Load the IPAv3 FWs into IPA&GSI SRAM.
  *
  * @firmware: Structure which contains the FW data from the user space.
@@ -5286,6 +5391,240 @@
 	return 0;
 }
 
+/*
+ * The following needed for the EMULATION system. On a non-emulation
+ * system (ie. the real UE), this is functionality is done in the
+ * TZ...
+ */
+#define IPA_SPARE_REG_1_VAL (0xC0000805)
+
+static void ipa_gsi_setup_reg(void)
+{
+	u32 reg_val, start;
+	int i;
+	const struct ipa_gsi_ep_config *gsi_ep_info_cfg;
+	enum ipa_client_type type;
+
+	IPADBG("Setting up registers in preparation for firmware download\n");
+
+	/* enable GSI interface */
+	ipahal_write_reg(IPA_GSI_CONF, 1);
+
+	/*
+	 * Before configuring the FIFOs need to unset bit 30 in the
+	 * spare register
+	 */
+	ipahal_write_reg(IPA_SPARE_REG_1_OFST,
+			 (IPA_SPARE_REG_1_VAL & (~(1 << 30))));
+
+	/* setup IPA_ENDP_GSI_CFG_TLV_n reg */
+	start = 0;
+	ipa3_ctx->ipa_num_pipes = ipa3_get_num_pipes();
+	IPADBG("ipa_num_pipes=%u\n", ipa3_ctx->ipa_num_pipes);
+
+	for (i = 0; i < ipa3_ctx->ipa_num_pipes; i++) {
+		type = ipa3_get_client_by_pipe(i);
+		gsi_ep_info_cfg = ipa3_get_gsi_ep_info(type);
+		IPADBG("for ep %d client is %d\n", i, type);
+		if (!gsi_ep_info_cfg)
+			continue;
+		IPADBG("Config is true");
+		reg_val = (gsi_ep_info_cfg->ipa_if_tlv << 16) + start;
+		start += gsi_ep_info_cfg->ipa_if_tlv;
+		ipahal_write_reg_n(IPA_ENDP_GSI_CFG_TLV_OFST_n, i, reg_val);
+	}
+
+	/* setup IPA_ENDP_GSI_CFG_AOS_n reg */
+	start = 0;
+	for (i = 0; i < ipa3_ctx->ipa_num_pipes; i++) {
+		type = ipa3_get_client_by_pipe(i);
+		gsi_ep_info_cfg = ipa3_get_gsi_ep_info(type);
+		if (!gsi_ep_info_cfg)
+			continue;
+		reg_val = (gsi_ep_info_cfg->ipa_if_aos << 16) + start;
+		start += gsi_ep_info_cfg->ipa_if_aos;
+		ipahal_write_reg_n(IPA_ENDP_GSI_CFG_AOS_OFST_n, i, reg_val);
+	}
+
+	/* setup IPA_ENDP_GSI_CFG1_n reg */
+	for (i = 0; i < ipa3_ctx->ipa_num_pipes; i++) {
+		type = ipa3_get_client_by_pipe(i);
+		gsi_ep_info_cfg = ipa3_get_gsi_ep_info(type);
+		if (!gsi_ep_info_cfg)
+			continue;
+		reg_val = (1 << 16) +
+			((u32)gsi_ep_info_cfg->ipa_gsi_chan_num << 8) +
+			gsi_ep_info_cfg->ee;
+		ipahal_write_reg_n(IPA_ENDP_GSI_CFG1_OFST_n, i, reg_val);
+	}
+
+	/*
+	 * Setup IPA_ENDP_GSI_CFG2_n reg: this register must be setup
+	 * as last one
+	 */
+	for (i = 0; i < ipa3_ctx->ipa_num_pipes; i++) {
+		type = ipa3_get_client_by_pipe(i);
+		gsi_ep_info_cfg = ipa3_get_gsi_ep_info(type);
+		if (!gsi_ep_info_cfg)
+			continue;
+		reg_val = 1 << 31;
+		ipahal_write_reg_n(IPA_ENDP_GSI_CFG2_OFST_n, i, reg_val);
+		reg_val = 0;
+		ipahal_write_reg_n(IPA_ENDP_GSI_CFG2_OFST_n, i, reg_val);
+	}
+
+	/*
+	 * After configuring the FIFOs need to set bit 30 in the spare
+	 * register
+	 */
+	ipahal_write_reg(IPA_SPARE_REG_1_OFST,
+			 (IPA_SPARE_REG_1_VAL | (1 << 30)));
+}
+
+/**
+ * emulator_load_fws() - Load the IPAv3 FWs into IPA&GSI SRAM.
+ *
+ * @firmware: Structure which contains the FW data from the user space.
+ *
+ * Return value: 0 on success, negative otherwise
+ *
+ */
+int emulator_load_fws(
+	const struct firmware *firmware,
+	u32 transport_mem_base,
+	u32 transport_mem_size)
+{
+	const struct elf32_hdr *ehdr;
+	const struct elf32_phdr *phdr;
+	void __iomem *gsi_base;
+	uint32_t hps_seq_offs, dps_seq_offs;
+	unsigned long gsi_offset;
+	int rc;
+
+	IPADBG("Loading firmware(%pK)\n", firmware);
+
+	if (!firmware) {
+		IPAERR("firmware pointer passed to function is NULL\n");
+		return -EINVAL;
+	}
+
+	/* One program header per FW image: GSI, DPS and HPS */
+	if (firmware->size < (sizeof(*ehdr) + 3 * sizeof(*phdr))) {
+		IPAERR(
+		    "Missing ELF and Program headers firmware size=%zu\n",
+		    firmware->size);
+		return -EINVAL;
+	}
+
+	ehdr = (struct elf32_hdr *) firmware->data;
+
+	ipa_assert_on(!ehdr);
+
+	if (ehdr->e_phnum != 3) {
+		IPAERR("Unexpected number of ELF program headers\n");
+		return -EINVAL;
+	}
+
+	hps_seq_offs = ipahal_get_reg_ofst(IPA_HPS_SEQUENCER_FIRST);
+	dps_seq_offs = ipahal_get_reg_ofst(IPA_DPS_SEQUENCER_FIRST);
+
+	/*
+	 * Each ELF program header represents a FW image and contains:
+	 *  p_vaddr : The starting address to which the FW needs to loaded.
+	 *  p_memsz : The size of the IRAM (where the image loaded)
+	 *  p_filesz: The size of the FW image embedded inside the ELF
+	 *  p_offset: Absolute offset to the image from the head of the ELF
+	 *
+	 * NOTE WELL: On the emulation platform, the p_vaddr address
+	 *            is not relevant and is unused.  This is because
+	 *            on the emulation platform, the registers'
+	 *            address location is mutable, since it's mapped
+	 *            in via a PCIe probe.  Given this, it is the
+	 *            mapped address info that's used while p_vaddr is
+	 *            ignored.
+	 */
+	phdr = (struct elf32_phdr *)(firmware->data + sizeof(*ehdr));
+
+	phdr += 2;
+
+	/*
+	 * Attempt to load IPA HPS FW image
+	 */
+	if (phdr->p_memsz > ipahal_get_hps_img_mem_size()) {
+		IPAERR("Invalid IPA HPS img size memsz=%d dps_mem_size=%u\n",
+		       phdr->p_memsz, ipahal_get_hps_img_mem_size());
+		return -EINVAL;
+	}
+	IPADBG("Loading HPS FW\n");
+	rc = emulator_load_single_fw(
+	    firmware, phdr, ipa3_ctx->mmio, hps_seq_offs);
+	if (rc)
+		return rc;
+	IPADBG("Loading HPS FW complete\n");
+
+	--phdr;
+
+	/*
+	 * Attempt to load IPA DPS FW image
+	 */
+	if (phdr->p_memsz > ipahal_get_dps_img_mem_size()) {
+		IPAERR("Invalid IPA DPS img size memsz=%d dps_mem_size=%u\n",
+		       phdr->p_memsz, ipahal_get_dps_img_mem_size());
+		return -EINVAL;
+	}
+	IPADBG("Loading DPS FW\n");
+	rc = emulator_load_single_fw(
+	    firmware, phdr, ipa3_ctx->mmio, dps_seq_offs);
+	if (rc)
+		return rc;
+	IPADBG("Loading DPS FW complete\n");
+
+	/*
+	 * Run gsi register setup which is normally done in TZ on
+	 * non-EMULATION systems...
+	 */
+	ipa_gsi_setup_reg();
+
+	/*
+	 * Map to the GSI base...
+	 */
+	gsi_base = ioremap_nocache(transport_mem_base, transport_mem_size);
+
+	IPADBG("GSI base(0x%x) mapped to (%pK) with len (0x%x)\n",
+	       transport_mem_base,
+	       gsi_base,
+	       transport_mem_size);
+
+	if (!gsi_base) {
+		IPAERR("ioremap_nocache failed\n");
+		return -EFAULT;
+	}
+
+	--phdr;
+
+	/*
+	 * Attempt to load GSI FW image
+	 */
+	if (phdr->p_memsz > transport_mem_size) {
+		IPAERR(
+		    "Invalid GSI FW img size memsz=%d transport_mem_size=%u\n",
+		    phdr->p_memsz, transport_mem_size);
+		return -EINVAL;
+	}
+	IPADBG("Loading GSI FW\n");
+	gsi_get_inst_ram_offset_and_size(&gsi_offset, NULL);
+	rc = emulator_load_single_fw(
+	    firmware, phdr, gsi_base, (uint32_t) gsi_offset);
+	iounmap(gsi_base);
+	if (rc)
+		return rc;
+	IPADBG("Loading GSI FW complete\n");
+
+	IPADBG("IPA FWs (GSI FW, DPS and HPS) loaded successfully\n");
+
+	return 0;
+}
+
 /**
  * ipa3_is_msm_device() - Is the running device a MSM or MDM?
  *  Determine according to IPA version
@@ -5379,4 +5718,3 @@
 	desc->len = cmd_pyld->len;
 	desc->type = IPA_IMM_CMD_DESC;
 }
-
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt.c b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt.c
index 1254fe3..530adef 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, 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
@@ -10,6 +10,7 @@
  * GNU General Public License for more details.
  */
 
+#include <linux/errno.h>
 #include <linux/ipc_logging.h>
 #include <linux/debugfs.h>
 #include <linux/ipa.h>
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c
index ce59488..38132d2 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c
@@ -133,6 +133,12 @@
 	__stringify(IPA_FEC_ATTR_EE_n),
 	__stringify(IPA_MBIM_DEAGGR_FEC_ATTR_EE_n),
 	__stringify(IPA_GEN_DEAGGR_FEC_ATTR_EE_n),
+	__stringify(IPA_GSI_CONF),
+	__stringify(IPA_ENDP_GSI_CFG1_OFST_n),
+	__stringify(IPA_ENDP_GSI_CFG2_OFST_n),
+	__stringify(IPA_ENDP_GSI_CFG_AOS_OFST_n),
+	__stringify(IPA_ENDP_GSI_CFG_TLV_OFST_n),
+	__stringify(IPA_SPARE_REG_1_OFST),
 };
 
 static void ipareg_construct_dummy(enum ipahal_reg_name reg,
@@ -1971,6 +1977,24 @@
 	[IPA_HW_v3_5][IPA_HPS_FTCH_ARB_QUEUE_WEIGHT] = {
 		ipareg_construct_hps_queue_weights,
 		ipareg_parse_hps_queue_weights, 0x000005a4, 0, 0, 0, 0},
+	[IPA_HW_v3_5][IPA_GSI_CONF] = {
+		ipareg_construct_dummy, ipareg_parse_dummy,
+		0x00002790, 0x0, 0, 0, 0 },
+	[IPA_HW_v3_5][IPA_ENDP_GSI_CFG1_OFST_n] = {
+		ipareg_construct_dummy, ipareg_parse_dummy,
+		0x00002794, 0x4, 0, 0, 0 },
+	[IPA_HW_v3_5][IPA_ENDP_GSI_CFG2_OFST_n] = {
+		ipareg_construct_dummy, ipareg_parse_dummy,
+		0x00002A2C, 0x4, 0, 0, 0 },
+	[IPA_HW_v3_5][IPA_ENDP_GSI_CFG_AOS_OFST_n] = {
+		ipareg_construct_dummy, ipareg_parse_dummy,
+		0x000029A8, 0x4, 0, 0, 0 },
+	[IPA_HW_v3_5][IPA_ENDP_GSI_CFG_TLV_OFST_n] = {
+		ipareg_construct_dummy, ipareg_parse_dummy,
+		0x00002924, 0x4, 0, 0, 0 },
+	[IPA_HW_v3_5][IPA_SPARE_REG_1_OFST] = {
+		ipareg_construct_dummy, ipareg_parse_dummy,
+		0x00002780, 0x0, 0, 0, 0 },
 
 	/* IPAv4.0 */
 	[IPA_HW_v4_0][IPA_IRQ_SUSPEND_INFO_EE_n] = {
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.h b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.h
index 7e8e8ba..da5bbbf 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.h
@@ -134,6 +134,12 @@
 	IPA_FEC_ATTR_EE_n,
 	IPA_MBIM_DEAGGR_FEC_ATTR_EE_n,
 	IPA_GEN_DEAGGR_FEC_ATTR_EE_n,
+	IPA_GSI_CONF,
+	IPA_ENDP_GSI_CFG1_OFST_n,
+	IPA_ENDP_GSI_CFG2_OFST_n,
+	IPA_ENDP_GSI_CFG_AOS_OFST_n,
+	IPA_ENDP_GSI_CFG_TLV_OFST_n,
+	IPA_SPARE_REG_1_OFST,
 	IPA_REG_MAX,
 };
 
@@ -641,4 +647,3 @@
 	struct ipahal_reg_valmask *valmask);
 
 #endif /* _IPAHAL_REG_H_ */
-
diff --git a/drivers/platform/msm/ipa/test/Makefile b/drivers/platform/msm/ipa/test/Makefile
index 82bee5d..e18e534 100644
--- a/drivers/platform/msm/ipa/test/Makefile
+++ b/drivers/platform/msm/ipa/test/Makefile
@@ -1,2 +1,8 @@
+ifdef CONFIG_X86
+ccflags-y += -DIPA_EMULATION_COMPILE=1
+else
+ccflags-y += -DIPA_EMULATION_COMPILE=0
+endif
+
 obj-$(CONFIG_IPA_UT) += ipa_ut_mod.o
 ipa_ut_mod-y := ipa_ut_framework.o ipa_test_example.o ipa_test_mhi.o ipa_test_dma.o ipa_test_hw_stats.o ipa_pm_ut.o
diff --git a/drivers/platform/msm/ipa/test/ipa_test_mhi.c b/drivers/platform/msm/ipa/test/ipa_test_mhi.c
index 212557c..7f65956 100644
--- a/drivers/platform/msm/ipa/test/ipa_test_mhi.c
+++ b/drivers/platform/msm/ipa/test/ipa_test_mhi.c
@@ -327,6 +327,8 @@
 	u32 prod_hdl;
 	u32 cons_hdl;
 	u32 test_prod_hdl;
+	phys_addr_t transport_phys_addr;
+	unsigned long transport_size;
 };
 
 static struct ipa_test_mhi_context *test_mhi_ctx;
@@ -780,11 +782,6 @@
 
 	IPA_UT_DBG("Start Setup\n");
 
-	if (!gsi_ctx) {
-		IPA_UT_ERR("No GSI ctx\n");
-		return -EINVAL;
-	}
-
 	if (!ipa3_ctx) {
 		IPA_UT_ERR("No IPA ctx\n");
 		return -EINVAL;
@@ -797,11 +794,20 @@
 		return -ENOMEM;
 	}
 
-	test_mhi_ctx->gsi_mmio = ioremap_nocache(gsi_ctx->per.phys_addr,
-		gsi_ctx->per.size);
-	if (!test_mhi_ctx) {
+	rc = ipa3_get_transport_info(&test_mhi_ctx->transport_phys_addr,
+				     &test_mhi_ctx->transport_size);
+	if (rc != 0) {
+		IPA_UT_ERR("ipa3_get_transport_info() failed\n");
+		rc = -EFAULT;
+		goto fail_free_ctx;
+	}
+
+	test_mhi_ctx->gsi_mmio =
+	    ioremap_nocache(test_mhi_ctx->transport_phys_addr,
+			    test_mhi_ctx->transport_size);
+	if (!test_mhi_ctx->gsi_mmio) {
 		IPA_UT_ERR("failed to remap GSI HW size=%lu\n",
-			gsi_ctx->per.size);
+			   test_mhi_ctx->transport_size);
 		rc = -EFAULT;
 		goto fail_free_ctx;
 	}
@@ -1385,7 +1391,7 @@
 		/* write value to event ring doorbell */
 		IPA_UT_LOG("DB to event 0x%llx: base %pa ofst 0x%x\n",
 			p_events[event_ring_index].wp,
-			&(gsi_ctx->per.phys_addr),
+			&(test_mhi_ctx->transport_phys_addr),
 			GSI_EE_n_EV_CH_k_DOORBELL_0_OFFS(
 			event_ring_index + ipa3_ctx->mhi_evid_limits[0], 0));
 		iowrite32(p_events[event_ring_index].wp,
@@ -1432,7 +1438,7 @@
 				IPA_UT_LOG(
 					"DB to channel 0x%llx: base %pa ofst 0x%x\n"
 					, p_channels[channel_idx].wp
-					, &(gsi_ctx->per.phys_addr)
+					, &(test_mhi_ctx->transport_phys_addr)
 					, GSI_EE_n_GSI_CH_k_DOORBELL_0_OFFS(
 						channel_idx, 0));
 				iowrite32(p_channels[channel_idx].wp,
@@ -3324,4 +3330,3 @@
 		ipa_mhi_test_in_loop_channel_reset_ipa_holb,
 		true, IPA_HW_v3_0, IPA_HW_MAX),
 } IPA_UT_DEFINE_SUITE_END(mhi);
-
diff --git a/drivers/platform/msm/ipa/test/ipa_ut_framework.c b/drivers/platform/msm/ipa/test/ipa_ut_framework.c
index bcbcd87..dad3ec9 100644
--- a/drivers/platform/msm/ipa/test/ipa_ut_framework.c
+++ b/drivers/platform/msm/ipa/test/ipa_ut_framework.c
@@ -1036,9 +1036,10 @@
  * If IPA driver already ready, continue initialization immediately.
  * if not, wait for IPA ready notification by IPA driver context
  */
-static int __init ipa_ut_module_init(void)
+int __init ipa_ut_module_init(void)
 {
-	int ret;
+	int ret = 0;
+	bool init_framewok = true;
 
 	IPA_UT_INFO("Loading IPA test module...\n");
 
@@ -1050,14 +1051,34 @@
 	mutex_init(&ipa_ut_ctx->lock);
 
 	if (!ipa_is_ready()) {
+		init_framewok = false;
+
 		IPA_UT_DBG("IPA driver not ready, registering callback\n");
+
 		ret = ipa_register_ipa_ready_cb(ipa_ut_ipa_ready_cb, NULL);
 
 		/*
-		 * If we received -EEXIST, IPA has initialized. So we need
-		 * to continue the initing process.
+		 * If the call to ipa_register_ipa_ready_cb() above
+		 * returns 0, this means that we've succeeded in
+		 * queuing up a future call to ipa_ut_framework_init()
+		 * and that the call to it will be made once the IPA
+		 * becomes ready.  If this is the case, the call to
+		 * ipa_ut_framework_init() below need not be made.
+		 *
+		 * If the call to ipa_register_ipa_ready_cb() above
+		 * returns -EEXIST, it means that during the call to
+		 * ipa_register_ipa_ready_cb(), the IPA has become
+		 * ready, and hence, no indirect call to
+		 * ipa_ut_framework_init() will be made, so we need to
+		 * call it ourselves below.
+		 *
+		 * If the call to ipa_register_ipa_ready_cb() above
+		 * return something other than 0 or -EEXIST, that's a
+		 * hard error.
 		 */
-		if (ret != -EEXIST) {
+		if (ret == -EEXIST) {
+			init_framewok = true;
+		} else {
 			if (ret) {
 				IPA_UT_ERR("IPA CB reg failed - %d\n", ret);
 				kfree(ipa_ut_ctx);
@@ -1067,12 +1088,15 @@
 		}
 	}
 
-	ret = ipa_ut_framework_init();
-	if (ret) {
-		IPA_UT_ERR("framework init failed\n");
-		kfree(ipa_ut_ctx);
-		ipa_ut_ctx = NULL;
+	if (init_framewok) {
+		ret = ipa_ut_framework_init();
+		if (ret) {
+			IPA_UT_ERR("framework init failed\n");
+			kfree(ipa_ut_ctx);
+			ipa_ut_ctx = NULL;
+		}
 	}
+
 	return ret;
 }
 
@@ -1081,7 +1105,7 @@
  *
  * Destroys the Framework and removes its context
  */
-static void ipa_ut_module_exit(void)
+void ipa_ut_module_exit(void)
 {
 	IPA_UT_DBG("Entry\n");
 
@@ -1093,8 +1117,9 @@
 	ipa_ut_ctx = NULL;
 }
 
+#if IPA_EMULATION_COMPILE == 0 /* On real UE, we have a module */
 module_init(ipa_ut_module_init);
 module_exit(ipa_ut_module_exit);
-
 MODULE_LICENSE("GPL v2");
 MODULE_DESCRIPTION("IPA Unit Test module");
+#endif
diff --git a/drivers/platform/msm/mhi_dev/mhi.c b/drivers/platform/msm/mhi_dev/mhi.c
index 1c16e5a..f06d6f3 100644
--- a/drivers/platform/msm/mhi_dev/mhi.c
+++ b/drivers/platform/msm/mhi_dev/mhi.c
@@ -1021,10 +1021,9 @@
 		if (rc)
 			pr_err("Error sending command completion event\n");
 
+		mhi_update_state_info(ch_id, MHI_STATE_CONNECTED);
 		/* Trigger callback to clients */
 		mhi_dev_trigger_cb();
-
-		mhi_update_state_info(ch_id, MHI_STATE_CONNECTED);
 		if (ch_id == MHI_CLIENT_MBIM_OUT)
 			kobject_uevent_env(&mhi_ctx->dev->kobj,
 						KOBJ_CHANGE, connected);
@@ -1418,16 +1417,20 @@
 	union mhi_dev_ring_element_type *el;
 	int rc = 0;
 	struct mhi_req *req = (struct mhi_req *)mreq;
-	struct mhi_req *local_req = NULL;
 	union mhi_dev_ring_element_type *compl_ev = NULL;
 	struct mhi_dev *mhi = NULL;
 	unsigned long flags;
+	size_t transfer_len;
+	u32 snd_cmpl;
+	uint32_t rd_offset;
 
 	client = req->client;
 	ch = client->channel;
 	mhi = ch->ring->mhi_dev;
 	el = req->el;
-	local_req = req;
+	transfer_len = req->len;
+	snd_cmpl = req->snd_cmpl;
+	rd_offset = req->rd_offset;
 	ch->curr_ereq->context = ch;
 
 	dma_unmap_single(&mhi_ctx->pdev->dev, req->dma,
@@ -1441,14 +1444,13 @@
 		compl_ev->evt_tr_comp.chid = ch->ch_id;
 		compl_ev->evt_tr_comp.type =
 				MHI_DEV_RING_EL_TRANSFER_COMPLETION_EVENT;
-		compl_ev->evt_tr_comp.len = el->tre.len;
+		compl_ev->evt_tr_comp.len = transfer_len;
 		compl_ev->evt_tr_comp.code = MHI_CMD_COMPL_CODE_EOT;
 		compl_ev->evt_tr_comp.ptr = ch->ring->ring_ctx->generic.rbase +
-				local_req->rd_offset * TR_RING_ELEMENT_SZ;
+						rd_offset * TR_RING_ELEMENT_SZ;
 		ch->curr_ereq->num_events++;
 
-		if (ch->curr_ereq->num_events >= MAX_TR_EVENTS ||
-				local_req->snd_cmpl){
+		if (ch->curr_ereq->num_events >= MAX_TR_EVENTS || snd_cmpl) {
 			mhi_log(MHI_MSG_VERBOSE,
 					"num of tr events %d for ch %d\n",
 					ch->curr_ereq->num_events, ch->ch_id);
@@ -2452,8 +2454,10 @@
 	 * If channel is open during registration, no callback is issued.
 	 * Instead return -EEXIST to notify the client. Clients request
 	 * is added to the list to notify future state change notification.
+	 * Channel struct may not be allocated yet if this function is called
+	 * early during boot - add an explicit check for non-null "ch".
 	 */
-	if (mhi_ctx->ch[channel].state == MHI_DEV_CH_STARTED) {
+	if (mhi_ctx->ch && (mhi_ctx->ch[channel].state == MHI_DEV_CH_STARTED)) {
 		mutex_unlock(&mhi_ctx->mhi_lock);
 		return -EEXIST;
 	}
@@ -2832,8 +2836,6 @@
 
 	INIT_LIST_HEAD(&mhi_ctx->event_ring_list);
 	INIT_LIST_HEAD(&mhi_ctx->process_ring_list);
-	INIT_LIST_HEAD(&mhi_ctx->client_cb_list);
-	mutex_init(&mhi_ctx->mhi_lock);
 	mutex_init(&mhi_ctx->mhi_event_lock);
 	mutex_init(&mhi_ctx->mhi_write_test);
 
@@ -2983,6 +2985,14 @@
 			dev_err(&pdev->dev,
 				"Failed to create IPC logging context\n");
 		}
+		/*
+		 * The below list and mutex should be initialized
+		 * before calling mhi_uci_init to avoid crash in
+		 * mhi_register_state_cb when accessing these.
+		 */
+		INIT_LIST_HEAD(&mhi_ctx->client_cb_list);
+		mutex_init(&mhi_ctx->mhi_lock);
+
 		mhi_uci_init();
 		mhi_update_state_info(MHI_DEV_UEVENT_CTRL,
 						MHI_STATE_CONFIGURED);
diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c
index 3dd1722..3ab93fe 100644
--- a/drivers/power/supply/power_supply_sysfs.c
+++ b/drivers/power/supply/power_supply_sysfs.c
@@ -331,6 +331,10 @@
 	POWER_SUPPLY_ATTR(allow_hvdcp3),
 	POWER_SUPPLY_ATTR(hvdcp_opti_allowed),
 	POWER_SUPPLY_ATTR(max_pulse_allowed),
+	POWER_SUPPLY_ATTR(ignore_false_negative_isense),
+	POWER_SUPPLY_ATTR(battery_info),
+	POWER_SUPPLY_ATTR(battery_info_id),
+	POWER_SUPPLY_ATTR(enable_jeita_detection),
 	/* Local extensions of type int64_t */
 	POWER_SUPPLY_ATTR(charge_counter_ext),
 	/* Properties of type `const char *' */
diff --git a/drivers/power/supply/qcom/battery.c b/drivers/power/supply/qcom/battery.c
index 275b982..e8d91ae 100644
--- a/drivers/power/supply/qcom/battery.c
+++ b/drivers/power/supply/qcom/battery.c
@@ -78,6 +78,7 @@
 	struct class		qcom_batt_class;
 	struct wakeup_source	*pl_ws;
 	struct notifier_block	nb;
+	bool			pl_disable;
 };
 
 struct pl_data *the_chip;
@@ -88,6 +89,7 @@
 
 enum {
 	AICL_RERUN_WA_BIT	= BIT(0),
+	FORCE_INOV_DISABLE_BIT	= BIT(1),
 };
 
 static int debug_mask;
@@ -848,6 +850,12 @@
 		chip->pl_settled_ua = 0;
 	}
 
+	/* notify parallel state change */
+	if (chip->pl_psy && (chip->pl_disable != pl_disable)) {
+		power_supply_changed(chip->pl_psy);
+		chip->pl_disable = (bool)pl_disable;
+	}
+
 	pl_dbg(chip, PR_PARALLEL, "parallel charging %s\n",
 		   pl_disable ? "disabled" : "enabled");
 
@@ -916,7 +924,8 @@
 	chip->pl_mode = pval.intval;
 
 	/* Disable autonomous votage increments for USBIN-USBIN */
-	if (IS_USBIN(chip->pl_mode)) {
+	if (IS_USBIN(chip->pl_mode)
+			&& (chip->wa_flags & FORCE_INOV_DISABLE_BIT)) {
 		if (!chip->hvdcp_hw_inov_dis_votable)
 			chip->hvdcp_hw_inov_dis_votable =
 					find_votable("HVDCP_HW_INOV_DIS");
@@ -1041,7 +1050,6 @@
 	else
 		vote(chip->pl_enable_votable_indirect, USBIN_I_VOTER, true, 0);
 
-	rerun_election(chip->fcc_votable);
 
 	if (IS_USBIN(chip->pl_mode)) {
 		/*
@@ -1200,7 +1208,9 @@
 	switch (smb_version) {
 	case PMI8998_SUBTYPE:
 	case PM660_SUBTYPE:
-		chip->wa_flags = AICL_RERUN_WA_BIT;
+		chip->wa_flags = AICL_RERUN_WA_BIT | FORCE_INOV_DISABLE_BIT;
+		break;
+	case PMI632_SUBTYPE:
 		break;
 	default:
 		break;
@@ -1301,6 +1311,7 @@
 		goto unreg_notifier;
 	}
 
+	chip->pl_disable = true;
 	chip->qcom_batt_class.name = "qcom-battery",
 	chip->qcom_batt_class.owner = THIS_MODULE,
 	chip->qcom_batt_class.class_attrs = pl_attributes;
diff --git a/drivers/power/supply/qcom/fg-alg.c b/drivers/power/supply/qcom/fg-alg.c
index 9b9a880..129af7b 100644
--- a/drivers/power/supply/qcom/fg-alg.c
+++ b/drivers/power/supply/qcom/fg-alg.c
@@ -19,6 +19,7 @@
 #include "fg-alg.h"
 
 #define FULL_SOC_RAW		255
+#define FULL_BATT_SOC		GENMASK(31, 0)
 #define CAPACITY_DELTA_DECIPCT	500
 
 /* Cycle counter APIs */
@@ -351,7 +352,7 @@
  */
 static int cap_learning_process_full_data(struct cap_learning *cl)
 {
-	int rc, cc_soc_sw, cc_soc_delta_pct;
+	int rc, cc_soc_sw, cc_soc_delta_centi_pct;
 	int64_t delta_cap_uah;
 
 	rc = cl->get_cc_soc(cl->data, &cc_soc_sw);
@@ -360,20 +361,21 @@
 		return rc;
 	}
 
-	cc_soc_delta_pct =
-		div64_s64((int64_t)(cc_soc_sw - cl->init_cc_soc_sw) * 100,
+	cc_soc_delta_centi_pct =
+		div64_s64((int64_t)(cc_soc_sw - cl->init_cc_soc_sw) * 10000,
 			cl->cc_soc_max);
 
 	/* If the delta is < 50%, then skip processing full data */
-	if (cc_soc_delta_pct < 50) {
-		pr_err("cc_soc_delta_pct: %d\n", cc_soc_delta_pct);
+	if (cc_soc_delta_centi_pct < 5000) {
+		pr_err("cc_soc_delta_centi_pct: %d\n", cc_soc_delta_centi_pct);
 		return -ERANGE;
 	}
 
-	delta_cap_uah = div64_s64(cl->learned_cap_uah * cc_soc_delta_pct, 100);
+	delta_cap_uah = div64_s64(cl->learned_cap_uah * cc_soc_delta_centi_pct,
+				 10000);
 	cl->final_cap_uah = cl->init_cap_uah + delta_cap_uah;
-	pr_debug("Current cc_soc=%d cc_soc_delta_pct=%d total_cap_uah=%lld\n",
-		cc_soc_sw, cc_soc_delta_pct, cl->final_cap_uah);
+	pr_debug("Current cc_soc=%d cc_soc_delta_centi_pct=%d total_cap_uah=%lld\n",
+		cc_soc_sw, cc_soc_delta_centi_pct, cl->final_cap_uah);
 	return 0;
 }
 
@@ -401,8 +403,8 @@
 		return -EINVAL;
 	}
 
-	cl->init_cap_uah = div64_s64(cl->learned_cap_uah * batt_soc_msb,
-					FULL_SOC_RAW);
+	cl->init_cap_uah = div64_s64(cl->learned_cap_uah * batt_soc,
+					FULL_BATT_SOC);
 
 	if (cl->prime_cc_soc) {
 		/*
diff --git a/drivers/power/supply/qcom/qpnp-fg.c b/drivers/power/supply/qcom/qpnp-fg.c
index 015da41..deccb20 100644
--- a/drivers/power/supply/qcom/qpnp-fg.c
+++ b/drivers/power/supply/qcom/qpnp-fg.c
@@ -33,6 +33,7 @@
 #include <linux/ktime.h>
 #include <linux/power_supply.h>
 #include <linux/of_batterydata.h>
+#include <linux/spinlock.h>
 #include <linux/string_helpers.h>
 #include <linux/alarmtimer.h>
 #include <linux/qpnp/qpnp-revid.h>
@@ -72,6 +73,7 @@
 
 #define QPNP_FG_DEV_NAME "qcom,qpnp-fg"
 #define MEM_IF_TIMEOUT_MS	5000
+#define FG_CYCLE_MS		1500
 #define BUCKET_COUNT		8
 #define BUCKET_SOC_PCT		(256 / BUCKET_COUNT)
 
@@ -108,6 +110,7 @@
 	PMI8950		= 17,
 	PMI8996		= 19,
 	PMI8937		= 55,
+	PMI8940		= 64,
 };
 
 enum wa_flags {
@@ -150,6 +153,8 @@
 	int			min_temp;
 	int			max_temp;
 	int			vbat_est_thr_uv;
+	int			max_cap_limit;
+	int			min_cap_limit;
 };
 
 struct fg_rslow_data {
@@ -275,11 +280,45 @@
 	DATA(BATT_ID_INFO,    0x594,   3,      1,     -EINVAL),
 };
 
+enum fg_mem_backup_index {
+	FG_BACKUP_SOC = 0,
+	FG_BACKUP_CYCLE_COUNT,
+	FG_BACKUP_CC_SOC_COEFF,
+	FG_BACKUP_IGAIN,
+	FG_BACKUP_VCOR,
+	FG_BACKUP_TEMP_COUNTER,
+	FG_BACKUP_AGING_STORAGE,
+	FG_BACKUP_MAH_TO_SOC,
+	FG_BACKUP_MAX,
+};
+
+#define BACKUP(_idx, _address, _offset, _length,  _value)	\
+	[FG_BACKUP_##_idx] = {				\
+		.address = _address,			\
+		.offset = _offset,			\
+		.len = _length,			\
+		.value = _value,			\
+	}						\
+
+static struct fg_mem_data fg_backup_regs[FG_BACKUP_MAX] = {
+	/*       ID           Address, Offset, Length, Value*/
+	BACKUP(SOC,		0x564,   0,      24,     -EINVAL),
+	BACKUP(CYCLE_COUNT,	0x5E8,   0,      16,     -EINVAL),
+	BACKUP(CC_SOC_COEFF,	0x5BC,   0,      8,     -EINVAL),
+	BACKUP(IGAIN,		0x424,   0,      4,     -EINVAL),
+	BACKUP(VCOR,		0x484,   0,      4,     -EINVAL),
+	BACKUP(TEMP_COUNTER,	0x580,   0,      4,     -EINVAL),
+	BACKUP(AGING_STORAGE,	0x5E4,   0,      4,     -EINVAL),
+	BACKUP(MAH_TO_SOC,	0x4A0,   0,      4,     -EINVAL),
+};
+
 static int fg_debug_mask;
 module_param_named(
 	debug_mask, fg_debug_mask, int, 00600
 );
 
+static int fg_reset_on_lockup;
+
 static int fg_sense_type = -EINVAL;
 static int fg_restart;
 
@@ -298,9 +337,18 @@
 	sram_update_period_ms, fg_sram_update_period_ms, int, 00600
 );
 
+static bool fg_batt_valid_ocv;
+module_param_named(batt_valid_ocv, fg_batt_valid_ocv, bool, 0600
+);
+
+static int fg_batt_range_pct;
+module_param_named(batt_range_pct, fg_batt_range_pct, int, 0600
+);
+
 struct fg_irq {
 	int			irq;
-	unsigned long		disabled;
+	bool			disabled;
+	bool			wakeup;
 };
 
 enum fg_soc_irq {
@@ -348,6 +396,16 @@
 	MAX_ADDRESS,
 };
 
+enum batt_info_params {
+	BATT_INFO_NOTIFY = 0,
+	BATT_INFO_SOC,
+	BATT_INFO_RES_ID,
+	BATT_INFO_VOLTAGE,
+	BATT_INFO_TEMP,
+	BATT_INFO_FCC,
+	BATT_INFO_MAX,
+};
+
 struct register_offset {
 	u16 address[MAX_ADDRESS];
 };
@@ -395,6 +453,22 @@
 	}
 }
 
+enum slope_limit_status {
+	LOW_TEMP_CHARGE,
+	HIGH_TEMP_CHARGE,
+	LOW_TEMP_DISCHARGE,
+	HIGH_TEMP_DISCHARGE,
+	SLOPE_LIMIT_MAX,
+};
+
+#define VOLT_GAIN_MAX		3
+struct dischg_gain_soc {
+	bool			enable;
+	u32			soc[VOLT_GAIN_MAX];
+	u32			medc_gain[VOLT_GAIN_MAX];
+	u32			highc_gain[VOLT_GAIN_MAX];
+};
+
 #define THERMAL_COEFF_N_BYTES		6
 struct fg_chip {
 	struct device		*dev;
@@ -420,6 +494,7 @@
 	struct completion	first_soc_done;
 	struct power_supply	*bms_psy;
 	struct power_supply_desc	bms_psy_d;
+	spinlock_t		sec_access_lock;
 	struct mutex		rw_lock;
 	struct mutex		sysfs_restart_lock;
 	struct delayed_work	batt_profile_init;
@@ -449,6 +524,7 @@
 	struct fg_wakeup_source	update_sram_wakeup_source;
 	bool			fg_restarting;
 	bool			profile_loaded;
+	bool			soc_reporting_ready;
 	bool			use_otp_profile;
 	bool			battery_missing;
 	bool			power_supply_registered;
@@ -459,6 +535,7 @@
 	bool			charge_done;
 	bool			resume_soc_lowered;
 	bool			vbat_low_irq_enabled;
+	bool			full_soc_irq_enabled;
 	bool			charge_full;
 	bool			hold_soc_while_full;
 	bool			input_present;
@@ -467,6 +544,10 @@
 	bool			bad_batt_detection_en;
 	bool			bcl_lpm_disabled;
 	bool			charging_disabled;
+	bool			use_vbat_low_empty_soc;
+	bool			fg_shutdown;
+	bool			use_soft_jeita_irq;
+	bool			allow_false_negative_isense;
 	struct delayed_work	update_jeita_setting;
 	struct delayed_work	update_sram_data;
 	struct delayed_work	update_temp_work;
@@ -491,6 +572,7 @@
 	int			prev_status;
 	int			health;
 	enum fg_batt_aging_mode	batt_aging_mode;
+	struct alarm		hard_jeita_alarm;
 	/* capacity learning */
 	struct fg_learning_data	learning_data;
 	struct alarm		fg_cap_learning_alarm;
@@ -498,6 +580,7 @@
 	struct fg_cc_soc_data	sw_cc_soc_data;
 	/* rslow compensation */
 	struct fg_rslow_data	rslow_comp;
+	int			rconn_mohm;
 	/* cycle counter */
 	struct fg_cyc_ctr_data	cyc_ctr;
 	/* iadc compensation */
@@ -510,6 +593,8 @@
 	bool			jeita_hysteresis_support;
 	bool			batt_hot;
 	bool			batt_cold;
+	bool			batt_warm;
+	bool			batt_cool;
 	int			cold_hysteresis;
 	int			hot_hysteresis;
 	/* ESR pulse tuning */
@@ -518,6 +603,47 @@
 	bool			esr_extract_disabled;
 	bool			imptr_pulse_slow_en;
 	bool			esr_pulse_tune_en;
+	/* Slope limiter */
+	struct work_struct	slope_limiter_work;
+	struct fg_wakeup_source	slope_limit_wakeup_source;
+	bool			soc_slope_limiter_en;
+	enum slope_limit_status	slope_limit_sts;
+	u32			slope_limit_temp;
+	u32			slope_limit_coeffs[SLOPE_LIMIT_MAX];
+	/* Discharge soc gain */
+	struct work_struct	dischg_gain_work;
+	struct fg_wakeup_source	dischg_gain_wakeup_source;
+	struct dischg_gain_soc	dischg_gain;
+	/* IMA error recovery */
+	struct completion	fg_reset_done;
+	struct work_struct	ima_error_recovery_work;
+	struct fg_wakeup_source	fg_reset_wakeup_source;
+	struct mutex		ima_recovery_lock;
+	bool			ima_error_handling;
+	bool			block_sram_access;
+	bool			irqs_enabled;
+	bool			use_last_soc;
+	int			last_soc;
+	/* Validating temperature */
+	int			last_good_temp;
+	int			batt_temp_low_limit;
+	int			batt_temp_high_limit;
+	/* Validating CC_SOC */
+	struct work_struct	cc_soc_store_work;
+	struct fg_wakeup_source	cc_soc_wakeup_source;
+	int			cc_soc_limit_pct;
+	bool			use_last_cc_soc;
+	int64_t			last_cc_soc;
+	/* Sanity check */
+	struct delayed_work	check_sanity_work;
+	struct fg_wakeup_source	sanity_wakeup_source;
+	u8			last_beat_count;
+	/* Batt_info restore */
+	int			batt_info[BATT_INFO_MAX];
+	int			batt_info_id;
+	bool			batt_info_restore;
+	bool			*batt_range_ocv;
+	int			*batt_range_pct;
 };
 
 /* FG_MEMIF DEBUGFS structures */
@@ -661,17 +787,56 @@
 	return rc;
 }
 
-static int fg_masked_write(struct fg_chip *chip, u16 addr,
+static int fg_masked_write_raw(struct fg_chip *chip, u16 addr,
 		u8 mask, u8 val, int len)
 {
 	int rc;
 
 	rc = regmap_update_bits(chip->regmap, addr, mask, val);
-	if (rc) {
+	if (rc)
 		pr_err("spmi write failed: addr=%03X, rc=%d\n", addr, rc);
-		return rc;
+
+	return rc;
+}
+
+static int fg_masked_write(struct fg_chip *chip, u16 addr,
+		u8 mask, u8 val, int len)
+{
+	int rc;
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->sec_access_lock, flags);
+	rc = fg_masked_write_raw(chip, addr, mask, val, len);
+	spin_unlock_irqrestore(&chip->sec_access_lock, flags);
+
+	return rc;
+}
+
+#define SEC_ACCESS_OFFSET	0xD0
+#define SEC_ACCESS_VALUE	0xA5
+#define PERIPHERAL_MASK		0xFF
+static int fg_sec_masked_write(struct fg_chip *chip, u16 addr, u8 mask, u8 val,
+		int len)
+{
+	int rc;
+	unsigned long flags;
+	u8 temp;
+	u16 base = addr & (~PERIPHERAL_MASK);
+
+	spin_lock_irqsave(&chip->sec_access_lock, flags);
+	temp = SEC_ACCESS_VALUE;
+	rc = fg_write(chip, &temp, base + SEC_ACCESS_OFFSET, 1);
+	if (rc) {
+		pr_err("Unable to unlock sec_access: %d\n", rc);
+		goto out;
 	}
 
+	rc = fg_masked_write_raw(chip, addr, mask, val, len);
+	if (rc)
+		pr_err("Unable to write securely to address 0x%x: %d", addr,
+			rc);
+out:
+	spin_unlock_irqrestore(&chip->sec_access_lock, flags);
 	return rc;
 }
 
@@ -952,6 +1117,7 @@
 	int rc = 0, user_cnt = 0, sublen;
 	bool access_configured = false;
 	u8 *wr_data = val, word[4];
+	u16 orig_address = address;
 	char str[DEBUG_PRINT_BUFFER_SIZE];
 
 	if (address < RAM_OFFSET)
@@ -960,8 +1126,8 @@
 	if (offset > 3)
 		return -EINVAL;
 
-	address = ((address + offset) / 4) * 4;
-	offset = (address + offset) % 4;
+	address = ((orig_address + offset) / 4) * 4;
+	offset = (orig_address + offset) % 4;
 
 	user_cnt = atomic_add_return(1, &chip->memif_user_cnt);
 	if (fg_debug_mask & FG_MEM_DEBUG_WRITES)
@@ -1061,50 +1227,253 @@
 #define MEM_INTF_IMA_EXP_STS		0x55
 #define MEM_INTF_IMA_HW_STS		0x56
 #define MEM_INTF_IMA_BYTE_EN		0x60
-#define IMA_ADDR_STBL_ERR		BIT(7)
-#define IMA_WR_ACS_ERR			BIT(6)
-#define IMA_RD_ACS_ERR			BIT(5)
 #define IMA_IACS_CLR			BIT(2)
 #define IMA_IACS_RDY			BIT(1)
-static int fg_check_ima_exception(struct fg_chip *chip)
+static int fg_run_iacs_clear_sequence(struct fg_chip *chip)
+{
+	int rc = 0;
+	u8 temp;
+
+	if (fg_debug_mask & FG_STATUS)
+		pr_info("Running IACS clear sequence\n");
+
+	/* clear the error */
+	rc = fg_masked_write(chip, chip->mem_base + MEM_INTF_IMA_CFG,
+				IMA_IACS_CLR, IMA_IACS_CLR, 1);
+	if (rc) {
+		pr_err("Error writing to IMA_CFG, rc=%d\n", rc);
+		return rc;
+	}
+
+	temp = 0x4;
+	rc = fg_write(chip, &temp, MEM_INTF_ADDR_LSB(chip) + 1, 1);
+	if (rc) {
+		pr_err("Error writing to MEM_INTF_ADDR_MSB, rc=%d\n", rc);
+		return rc;
+	}
+
+	temp = 0x0;
+	rc = fg_write(chip, &temp, MEM_INTF_WR_DATA0(chip) + 3, 1);
+	if (rc) {
+		pr_err("Error writing to WR_DATA3, rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = fg_read(chip, &temp, MEM_INTF_RD_DATA0(chip) + 3, 1);
+	if (rc) {
+		pr_err("Error writing to RD_DATA3, rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = fg_masked_write(chip, chip->mem_base + MEM_INTF_IMA_CFG,
+				IMA_IACS_CLR, 0, 1);
+	if (rc) {
+		pr_err("Error writing to IMA_CFG, rc=%d\n", rc);
+		return rc;
+	}
+
+	if (fg_debug_mask & FG_STATUS)
+		pr_info("IACS clear sequence complete!\n");
+	return rc;
+}
+
+#define IACS_ERR_BIT		BIT(0)
+#define XCT_ERR_BIT		BIT(1)
+#define DATA_RD_ERR_BIT		BIT(3)
+#define DATA_WR_ERR_BIT		BIT(4)
+#define ADDR_BURST_WRAP_BIT	BIT(5)
+#define ADDR_RNG_ERR_BIT	BIT(6)
+#define ADDR_SRC_ERR_BIT	BIT(7)
+static int fg_check_ima_exception(struct fg_chip *chip, bool check_hw_sts)
 {
 	int rc = 0, ret = 0;
-	u8 err_sts, exp_sts = 0, hw_sts = 0;
+	u8 err_sts = 0, exp_sts = 0, hw_sts = 0;
+	bool run_err_clr_seq = false;
 
 	rc = fg_read(chip, &err_sts,
 			chip->mem_base + MEM_INTF_IMA_ERR_STS, 1);
 	if (rc) {
-		pr_err("failed to read beat count rc=%d\n", rc);
+		pr_err("failed to read IMA_ERR_STS, rc=%d\n", rc);
 		return rc;
 	}
 
-	if (err_sts & (IMA_ADDR_STBL_ERR | IMA_WR_ACS_ERR | IMA_RD_ACS_ERR)) {
-		u8 temp;
-
-		fg_read(chip, &exp_sts,
+	rc = fg_read(chip, &exp_sts,
 			chip->mem_base + MEM_INTF_IMA_EXP_STS, 1);
-		fg_read(chip, &hw_sts,
-			chip->mem_base + MEM_INTF_IMA_HW_STS, 1);
-		pr_err("IMA access failed ima_err_sts=%x ima_exp_sts=%x ima_hw_sts=%x\n",
-				err_sts, exp_sts, hw_sts);
-		rc = err_sts;
-
-		/* clear the error */
-		ret |= fg_masked_write(chip, chip->mem_base + MEM_INTF_IMA_CFG,
-					IMA_IACS_CLR, IMA_IACS_CLR, 1);
-		temp = 0x4;
-		ret |= fg_write(chip, &temp, MEM_INTF_ADDR_LSB(chip) + 1, 1);
-		temp = 0x0;
-		ret |= fg_write(chip, &temp, MEM_INTF_WR_DATA0(chip) + 3, 1);
-		ret |= fg_read(chip, &temp, MEM_INTF_RD_DATA0(chip) + 3, 1);
-		ret |= fg_masked_write(chip, chip->mem_base + MEM_INTF_IMA_CFG,
-					IMA_IACS_CLR, 0, 1);
-		if (!ret)
-			return -EAGAIN;
-
-		pr_err("Error clearing IMA exception ret=%d\n", ret);
+	if (rc) {
+		pr_err("Error in reading IMA_EXP_STS, rc=%d\n", rc);
+		return rc;
 	}
 
+	rc = fg_read(chip, &hw_sts,
+			chip->mem_base + MEM_INTF_IMA_HW_STS, 1);
+	if (rc) {
+		pr_err("Error in reading IMA_HW_STS, rc=%d\n", rc);
+		return rc;
+	}
+
+	pr_info_once("Initial ima_err_sts=%x ima_exp_sts=%x ima_hw_sts=%x\n",
+		err_sts, exp_sts, hw_sts);
+
+	if (fg_debug_mask & (FG_MEM_DEBUG_READS | FG_MEM_DEBUG_WRITES))
+		pr_info("ima_err_sts=%x ima_exp_sts=%x ima_hw_sts=%x\n",
+			err_sts, exp_sts, hw_sts);
+
+	if (check_hw_sts) {
+		/*
+		 * Lower nibble should be equal to upper nibble before SRAM
+		 * transactions begins from SW side. If they are unequal, then
+		 * the error clear sequence should be run irrespective of IMA
+		 * exception errors.
+		 */
+		if ((hw_sts & 0x0F) != hw_sts >> 4) {
+			pr_err("IMA HW not in correct state, hw_sts=%x\n",
+				hw_sts);
+			run_err_clr_seq = true;
+		}
+	}
+
+	if (exp_sts & (IACS_ERR_BIT | XCT_ERR_BIT | DATA_RD_ERR_BIT |
+		DATA_WR_ERR_BIT | ADDR_BURST_WRAP_BIT | ADDR_RNG_ERR_BIT |
+		ADDR_SRC_ERR_BIT)) {
+		pr_err("IMA exception bit set, exp_sts=%x\n", exp_sts);
+		run_err_clr_seq = true;
+	}
+
+	if (run_err_clr_seq) {
+		ret = fg_run_iacs_clear_sequence(chip);
+		if (!ret)
+			return -EAGAIN;
+		else
+			pr_err("Error clearing IMA exception ret=%d\n", ret);
+	}
+
+	return rc;
+}
+
+static void fg_enable_irqs(struct fg_chip *chip, bool enable)
+{
+	if (!(enable ^ chip->irqs_enabled))
+		return;
+
+	if (enable) {
+		enable_irq(chip->soc_irq[DELTA_SOC].irq);
+		enable_irq_wake(chip->soc_irq[DELTA_SOC].irq);
+		if (!chip->full_soc_irq_enabled) {
+			enable_irq(chip->soc_irq[FULL_SOC].irq);
+			enable_irq_wake(chip->soc_irq[FULL_SOC].irq);
+			chip->full_soc_irq_enabled = true;
+		}
+		enable_irq(chip->batt_irq[BATT_MISSING].irq);
+		if (!chip->vbat_low_irq_enabled) {
+			enable_irq(chip->batt_irq[VBATT_LOW].irq);
+			enable_irq_wake(chip->batt_irq[VBATT_LOW].irq);
+			chip->vbat_low_irq_enabled = true;
+		}
+		if (!chip->use_vbat_low_empty_soc) {
+			enable_irq(chip->soc_irq[EMPTY_SOC].irq);
+			enable_irq_wake(chip->soc_irq[EMPTY_SOC].irq);
+		}
+		chip->irqs_enabled = true;
+	} else {
+		disable_irq_wake(chip->soc_irq[DELTA_SOC].irq);
+		disable_irq_nosync(chip->soc_irq[DELTA_SOC].irq);
+		if (chip->full_soc_irq_enabled) {
+			disable_irq_wake(chip->soc_irq[FULL_SOC].irq);
+			disable_irq_nosync(chip->soc_irq[FULL_SOC].irq);
+			chip->full_soc_irq_enabled = false;
+		}
+		disable_irq(chip->batt_irq[BATT_MISSING].irq);
+		if (chip->vbat_low_irq_enabled) {
+			disable_irq_wake(chip->batt_irq[VBATT_LOW].irq);
+			disable_irq_nosync(chip->batt_irq[VBATT_LOW].irq);
+			chip->vbat_low_irq_enabled = false;
+		}
+		if (!chip->use_vbat_low_empty_soc) {
+			disable_irq_wake(chip->soc_irq[EMPTY_SOC].irq);
+			disable_irq_nosync(chip->soc_irq[EMPTY_SOC].irq);
+		}
+		chip->irqs_enabled = false;
+	}
+
+	if (fg_debug_mask & FG_STATUS)
+		pr_info("FG interrupts are %sabled\n", enable ? "en" : "dis");
+}
+
+static void fg_check_ima_error_handling(struct fg_chip *chip)
+{
+	if (chip->ima_error_handling) {
+		if (fg_debug_mask & FG_STATUS)
+			pr_info("IMA error is handled already!\n");
+		return;
+	}
+	mutex_lock(&chip->ima_recovery_lock);
+	fg_enable_irqs(chip, false);
+	chip->use_last_cc_soc = true;
+	chip->ima_error_handling = true;
+	if (!work_pending(&chip->ima_error_recovery_work))
+		schedule_work(&chip->ima_error_recovery_work);
+	mutex_unlock(&chip->ima_recovery_lock);
+}
+
+#define SOC_ALG_ST		0xCF
+#define FGXCT_PRD		BIT(7)
+#define ALG_ST_CHECK_COUNT	20
+static int fg_check_alg_status(struct fg_chip *chip)
+{
+	int rc = 0, timeout = ALG_ST_CHECK_COUNT, count = 0;
+	u8 ima_opr_sts, alg_sts = 0, temp = 0;
+
+	if (!fg_reset_on_lockup)  {
+		pr_info("FG lockup detection cannot be run\n");
+		return 0;
+	}
+
+	rc = fg_read(chip, &alg_sts, chip->soc_base + SOC_ALG_ST, 1);
+	if (rc) {
+		pr_err("Error in reading SOC_ALG_ST, rc=%d\n", rc);
+		return rc;
+	}
+
+	while (1) {
+		rc = fg_read(chip, &ima_opr_sts,
+			chip->mem_base + MEM_INTF_IMA_OPR_STS, 1);
+		if (!rc && !(ima_opr_sts & FGXCT_PRD))
+			break;
+
+		if (rc) {
+			pr_err("Error in reading IMA_OPR_STS, rc=%d\n",
+				rc);
+			break;
+		}
+
+		rc = fg_read(chip, &temp, chip->soc_base + SOC_ALG_ST,
+			1);
+		if (rc) {
+			pr_err("Error in reading SOC_ALG_ST, rc=%d\n",
+				rc);
+			break;
+		}
+
+		if ((ima_opr_sts & FGXCT_PRD) && (temp == alg_sts))
+			count++;
+
+		/* Wait for ~10ms while polling ALG_ST & IMA_OPR_STS */
+		usleep_range(9000, 11000);
+
+		if (!(--timeout))
+			break;
+	}
+
+	if (fg_debug_mask & (FG_MEM_DEBUG_READS | FG_MEM_DEBUG_WRITES))
+		pr_info("ima_opr_sts: %x  alg_sts: %x count=%d\n", ima_opr_sts,
+			alg_sts, count);
+
+	if (count == ALG_ST_CHECK_COUNT) {
+		/* If we are here, that means FG ALG is stuck */
+		pr_err("ALG is stuck\n");
+		fg_check_ima_error_handling(chip);
+		rc = -EBUSY;
+	}
 	return rc;
 }
 
@@ -1122,19 +1491,25 @@
 	while (1) {
 		rc = fg_read(chip, &ima_opr_sts,
 			chip->mem_base + MEM_INTF_IMA_OPR_STS, 1);
-		if (!rc && (ima_opr_sts & IMA_IACS_RDY))
+		if (!rc && (ima_opr_sts & IMA_IACS_RDY)) {
 			break;
+		} else {
+			if (!(--timeout) || rc)
+				break;
 
-		if (!(--timeout) || rc)
-			break;
-		/* delay for iacs_ready to be asserted */
-		usleep_range(5000, 7000);
+			/* delay for iacs_ready to be asserted */
+			usleep_range(5000, 7000);
+		}
 	}
 
 	if (!timeout || rc) {
-		pr_err("IACS_RDY not set\n");
+		pr_err("IACS_RDY not set, ima_opr_sts: %x\n", ima_opr_sts);
+		rc = fg_check_alg_status(chip);
+		if (rc && rc != -EBUSY)
+			pr_err("Couldn't check FG ALG status, rc=%d\n",
+				rc);
 		/* perform IACS_CLR sequence */
-		fg_check_ima_exception(chip);
+		fg_check_ima_exception(chip, false);
 		return -EBUSY;
 	}
 
@@ -1154,15 +1529,16 @@
 
 	while (len > 0) {
 		num_bytes = (offset + len) > BUF_LEN ?
-			(BUF_LEN - offset) : len;
+				(BUF_LEN - offset) : len;
 		/* write to byte_enable */
 		for (i = offset; i < (offset + num_bytes); i++)
 			byte_enable |= BIT(i);
 
 		rc = fg_write(chip, &byte_enable,
-			chip->mem_base + MEM_INTF_IMA_BYTE_EN, 1);
+				chip->mem_base + MEM_INTF_IMA_BYTE_EN, 1);
 		if (rc) {
-			pr_err("Unable to write to byte_en_reg rc=%d\n", rc);
+			pr_err("Unable to write to byte_en_reg rc=%d\n",
+							rc);
 			return rc;
 		}
 			/* write data */
@@ -1193,12 +1569,13 @@
 
 		rc = fg_check_iacs_ready(chip);
 		if (rc) {
-			pr_debug("IACS_RDY failed rc=%d\n", rc);
+			pr_err("IACS_RDY failed post write to address %x offset %d rc=%d\n",
+				address, offset, rc);
 			return rc;
 		}
 
 		/* check for error condition */
-		rc = fg_check_ima_exception(chip);
+		rc = fg_check_ima_exception(chip, false);
 		if (rc) {
 			pr_err("IMA transaction failed rc=%d", rc);
 			return rc;
@@ -1239,12 +1616,13 @@
 
 		rc = fg_check_iacs_ready(chip);
 		if (rc) {
-			pr_debug("IACS_RDY failed rc=%d\n", rc);
+			pr_err("IACS_RDY failed post read for address %x offset %d rc=%d\n",
+				address, offset, rc);
 			return rc;
 		}
 
 		/* check for error condition */
-		rc = fg_check_ima_exception(chip);
+		rc = fg_check_ima_exception(chip, false);
 		if (rc) {
 			pr_err("IMA transaction failed rc=%d", rc);
 			return rc;
@@ -1296,7 +1674,7 @@
 		 * clear, then return an error instead of waiting for it again.
 		 */
 		if  (time_count > 4) {
-			pr_err("Waited for 1.5 seconds polling RIF_MEM_ACCESS_REQ\n");
+			pr_err("Waited for ~16ms polling RIF_MEM_ACCESS_REQ\n");
 			return -ETIMEDOUT;
 		}
 
@@ -1322,7 +1700,8 @@
 
 	rc = fg_check_iacs_ready(chip);
 	if (rc) {
-		pr_debug("IACS_RDY failed rc=%d\n", rc);
+		pr_err("IACS_RDY failed before setting address: %x offset: %d rc=%d\n",
+			address, offset, rc);
 		return rc;
 	}
 
@@ -1335,7 +1714,8 @@
 
 	rc = fg_check_iacs_ready(chip);
 	if (rc)
-		pr_debug("IACS_RDY failed rc=%d\n", rc);
+		pr_err("IACS_RDY failed after setting address: %x offset: %d rc=%d\n",
+			address, offset, rc);
 
 	return rc;
 }
@@ -1346,10 +1726,13 @@
 static int fg_interleaved_mem_read(struct fg_chip *chip, u8 *val, u16 address,
 						int len, int offset)
 {
-	int rc = 0, orig_address = address;
+	int rc = 0, ret, orig_address = address;
 	u8 start_beat_count, end_beat_count, count = 0;
 	bool retry = false;
 
+	if (chip->fg_shutdown)
+		return -EINVAL;
+
 	if (offset > 3) {
 		pr_err("offset too large %d\n", offset);
 		return -EINVAL;
@@ -1372,11 +1755,22 @@
 	}
 
 	mutex_lock(&chip->rw_lock);
+	if (fg_debug_mask & FG_MEM_DEBUG_READS)
+		pr_info("Read for %d bytes is attempted @ 0x%x[%d]\n",
+			len, address, offset);
 
 retry:
+	if (count >= RETRY_COUNT) {
+		pr_err("Retried reading 3 times\n");
+		retry = false;
+		goto out;
+	}
+
 	rc = fg_interleaved_mem_config(chip, val, address, offset, len, 0);
 	if (rc) {
 		pr_err("failed to configure SRAM for IMA rc = %d\n", rc);
+		retry = true;
+		count++;
 		goto out;
 	}
 
@@ -1385,18 +1779,21 @@
 			chip->mem_base + MEM_INTF_FG_BEAT_COUNT, 1);
 	if (rc) {
 		pr_err("failed to read beat count rc=%d\n", rc);
+		retry = true;
+		count++;
 		goto out;
 	}
 
 	/* read data */
 	rc = __fg_interleaved_mem_read(chip, val, address, offset, len);
 	if (rc) {
+		count++;
 		if ((rc == -EAGAIN) && (count < RETRY_COUNT)) {
-			count++;
 			pr_err("IMA access failed retry_count = %d\n", count);
 			goto retry;
 		} else {
 			pr_err("failed to read SRAM address rc = %d\n", rc);
+			retry = true;
 			goto out;
 		}
 	}
@@ -1406,6 +1803,8 @@
 			chip->mem_base + MEM_INTF_FG_BEAT_COUNT, 1);
 	if (rc) {
 		pr_err("failed to read beat count rc=%d\n", rc);
+		retry = true;
+		count++;
 		goto out;
 	}
 
@@ -1418,12 +1817,13 @@
 		if (fg_debug_mask & FG_MEM_DEBUG_READS)
 			pr_info("Beat count do not match - retry transaction\n");
 		retry = true;
+		count++;
 	}
 out:
 	/* Release IMA access */
-	rc = fg_masked_write(chip, MEM_INTF_CFG(chip), IMA_REQ_ACCESS, 0, 1);
-	if (rc)
-		pr_err("failed to reset IMA access bit rc = %d\n", rc);
+	ret = fg_masked_write(chip, MEM_INTF_CFG(chip), IMA_REQ_ACCESS, 0, 1);
+	if (ret)
+		pr_err("failed to reset IMA access bit ret = %d\n", ret);
 
 	if (retry) {
 		retry = false;
@@ -1439,8 +1839,12 @@
 static int fg_interleaved_mem_write(struct fg_chip *chip, u8 *val, u16 address,
 							int len, int offset)
 {
-	int rc = 0, orig_address = address;
+	int rc = 0, ret, orig_address = address;
 	u8 count = 0;
+	bool retry = false;
+
+	if (chip->fg_shutdown)
+		return -EINVAL;
 
 	if (address < RAM_OFFSET)
 		return -EINVAL;
@@ -1455,32 +1859,49 @@
 	offset = (orig_address + offset) % 4;
 
 	mutex_lock(&chip->rw_lock);
+	if (fg_debug_mask & FG_MEM_DEBUG_WRITES)
+		pr_info("Write for %d bytes is attempted @ 0x%x[%d]\n",
+			len, address, offset);
 
 retry:
+	if (count >= RETRY_COUNT) {
+		pr_err("Retried writing 3 times\n");
+		retry = false;
+		goto out;
+	}
+
 	rc = fg_interleaved_mem_config(chip, val, address, offset, len, 1);
 	if (rc) {
-		pr_err("failed to xonfigure SRAM for IMA rc = %d\n", rc);
+		pr_err("failed to configure SRAM for IMA rc = %d\n", rc);
+		retry = true;
+		count++;
 		goto out;
 	}
 
 	/* write data */
 	rc = __fg_interleaved_mem_write(chip, val, address, offset, len);
 	if (rc) {
+		count++;
 		if ((rc == -EAGAIN) && (count < RETRY_COUNT)) {
-			count++;
 			pr_err("IMA access failed retry_count = %d\n", count);
 			goto retry;
 		} else {
 			pr_err("failed to write SRAM address rc = %d\n", rc);
+			retry = true;
 			goto out;
 		}
 	}
 
 out:
 	/* Release IMA access */
-	rc = fg_masked_write(chip, MEM_INTF_CFG(chip), IMA_REQ_ACCESS, 0, 1);
-	if (rc)
-		pr_err("failed to reset IMA access bit rc = %d\n", rc);
+	ret = fg_masked_write(chip, MEM_INTF_CFG(chip), IMA_REQ_ACCESS, 0, 1);
+	if (ret)
+		pr_err("failed to reset IMA access bit ret = %d\n", ret);
+
+	if (retry) {
+		retry = false;
+		goto retry;
+	}
 
 	mutex_unlock(&chip->rw_lock);
 	fg_relax(&chip->memif_wakeup_source);
@@ -1490,6 +1911,9 @@
 static int fg_mem_read(struct fg_chip *chip, u8 *val, u16 address,
 			int len, int offset, bool keep_access)
 {
+	if (chip->block_sram_access)
+		return -EBUSY;
+
 	if (chip->ima_supported)
 		return fg_interleaved_mem_read(chip, val, address,
 						len, offset);
@@ -1501,6 +1925,9 @@
 static int fg_mem_write(struct fg_chip *chip, u8 *val, u16 address,
 		int len, int offset, bool keep_access)
 {
+	if (chip->block_sram_access)
+		return -EBUSY;
+
 	if (chip->ima_supported)
 		return fg_interleaved_mem_write(chip, val, address,
 						len, offset);
@@ -1538,6 +1965,62 @@
 	return rc;
 }
 
+static u8 sram_backup_buffer[100];
+static int fg_backup_sram_registers(struct fg_chip *chip, bool save)
+{
+	int rc, i, len, offset;
+	u16 address;
+	u8 *ptr;
+
+	if (fg_debug_mask & FG_STATUS)
+		pr_info("%sing SRAM registers\n", save ? "Back" : "Restor");
+
+	ptr = sram_backup_buffer;
+	for (i = 0; i < FG_BACKUP_MAX; i++) {
+		address = fg_backup_regs[i].address;
+		offset = fg_backup_regs[i].offset;
+		len = fg_backup_regs[i].len;
+		if (save)
+			rc = fg_interleaved_mem_read(chip, ptr, address,
+					len, offset);
+		else
+			rc = fg_interleaved_mem_write(chip, ptr, address,
+					len, offset);
+		if (rc) {
+			pr_err("Error in reading %d bytes from %x[%d], rc=%d\n",
+				len, address, offset, rc);
+			break;
+		}
+		ptr += len;
+	}
+
+	return rc;
+}
+
+#define SOC_FG_RESET	0xF3
+#define RESET_MASK	(BIT(7) | BIT(5))
+static int fg_reset(struct fg_chip *chip, bool reset)
+{
+	int rc;
+
+	rc = fg_sec_masked_write(chip, chip->soc_base + SOC_FG_RESET,
+		0xFF, reset ? RESET_MASK : 0, 1);
+	if (rc)
+		pr_err("Error in writing to 0x%x, rc=%d\n", SOC_FG_RESET, rc);
+
+	return rc;
+}
+
+static void fg_handle_battery_insertion(struct fg_chip *chip)
+{
+	reinit_completion(&chip->batt_id_avail);
+	reinit_completion(&chip->fg_reset_done);
+	schedule_delayed_work(&chip->batt_profile_init, 0);
+	cancel_delayed_work(&chip->update_sram_data);
+	schedule_delayed_work(&chip->update_sram_data, msecs_to_jiffies(0));
+}
+
+
 static int soc_to_setpoint(int soc)
 {
 	return DIV_ROUND_CLOSEST(soc * 255, 100);
@@ -1550,6 +2033,7 @@
 	val = DIV_ROUND_CLOSEST(vbatt_mv * 32768, 5000);
 	data[0] = val & 0xFF;
 	data[1] = val >> 8;
+	return;
 }
 
 static u8 batt_to_setpoint_8b(int vbatt_mv)
@@ -1678,14 +2162,37 @@
 	return rc;
 }
 
+#define VBATT_LOW_STS_BIT BIT(2)
+static int fg_get_vbatt_status(struct fg_chip *chip, bool *vbatt_low_sts)
+{
+	int rc = 0;
+	u8 fg_batt_sts;
+
+	rc = fg_read(chip, &fg_batt_sts, INT_RT_STS(chip->batt_base), 1);
+	if (rc)
+		pr_err("spmi read failed: addr=%03X, rc=%d\n",
+				INT_RT_STS(chip->batt_base), rc);
+	else
+		*vbatt_low_sts = !!(fg_batt_sts & VBATT_LOW_STS_BIT);
+
+	return rc;
+}
+
 #define SOC_EMPTY	BIT(3)
 static bool fg_is_batt_empty(struct fg_chip *chip)
 {
 	u8 fg_soc_sts;
 	int rc;
+	bool vbatt_low_sts;
 
-	rc = fg_read(chip, &fg_soc_sts,
-				 INT_RT_STS(chip->soc_base), 1);
+	if (chip->use_vbat_low_empty_soc) {
+		if (fg_get_vbatt_status(chip, &vbatt_low_sts))
+			return false;
+
+		return vbatt_low_sts;
+	}
+
+	rc = fg_read(chip, &fg_soc_sts, INT_RT_STS(chip->soc_base), 1);
 	if (rc) {
 		pr_err("spmi read failed: addr=%03X, rc=%d\n",
 				INT_RT_STS(chip->soc_base), rc);
@@ -1732,7 +2239,16 @@
 #define FULL_SOC_RAW		0xFF
 static int get_prop_capacity(struct fg_chip *chip)
 {
-	int msoc;
+	int msoc, rc;
+	bool vbatt_low_sts;
+
+	if (chip->use_last_soc && chip->last_soc) {
+		if (chip->last_soc == FULL_SOC_RAW)
+			return FULL_CAPACITY;
+		return DIV_ROUND_CLOSEST((chip->last_soc - 1) *
+				(FULL_CAPACITY - 2),
+				FULL_SOC_RAW - 2) + 1;
+	}
 
 	if (chip->battery_missing)
 		return MISSING_CAPACITY;
@@ -1747,10 +2263,28 @@
 		return EMPTY_CAPACITY;
 	}
 	msoc = get_monotonic_soc_raw(chip);
-	if (msoc == 0)
-		return EMPTY_CAPACITY;
-	else if (msoc == FULL_SOC_RAW)
+	if (msoc == 0) {
+		if (fg_reset_on_lockup && chip->use_vbat_low_empty_soc) {
+			rc = fg_get_vbatt_status(chip, &vbatt_low_sts);
+			if (rc) {
+				pr_err("Error in reading vbatt_status, rc=%d\n",
+					rc);
+				return EMPTY_CAPACITY;
+			}
+
+			if (!vbatt_low_sts)
+				return DIV_ROUND_CLOSEST((chip->last_soc - 1) *
+						(FULL_CAPACITY - 2),
+						FULL_SOC_RAW - 2) + 1;
+			else
+				return EMPTY_CAPACITY;
+		} else {
+			return EMPTY_CAPACITY;
+		}
+	} else if (msoc == FULL_SOC_RAW) {
 		return FULL_CAPACITY;
+	}
+
 	return DIV_ROUND_CLOSEST((msoc - 1) * (FULL_CAPACITY - 2),
 			FULL_SOC_RAW - 2) + 1;
 }
@@ -1843,6 +2377,25 @@
 	return 0;
 }
 
+#define IGNORE_FALSE_NEGATIVE_ISENSE_BIT	BIT(3)
+static int set_prop_ignore_false_negative_isense(struct fg_chip *chip,
+							bool ignore)
+{
+	int rc;
+
+	rc = fg_mem_masked_write(chip, EXTERNAL_SENSE_SELECT,
+			IGNORE_FALSE_NEGATIVE_ISENSE_BIT,
+			ignore ? IGNORE_FALSE_NEGATIVE_ISENSE_BIT : 0,
+			EXTERNAL_SENSE_OFFSET);
+	if (rc) {
+		pr_err("failed to %s isense false negative ignore rc=%d\n",
+				ignore ? "enable" : "disable", rc);
+		return rc;
+	}
+
+	return 0;
+}
+
 #define EXPONENT_MASK		0xF800
 #define MANTISSA_MASK		0x3FF
 #define SIGN			BIT(10)
@@ -1953,8 +2506,7 @@
 		return rc;
 	}
 
-	if (fg_debug_mask & FG_IRQS)
-		pr_info("fg batt sts 0x%x\n", fg_batt_sts);
+	pr_debug("fg batt sts 0x%x\n", fg_batt_sts);
 
 	return (fg_batt_sts & BATT_IDED) ? 1 : 0;
 }
@@ -1984,7 +2536,7 @@
 #define DECIKELVIN	2730
 #define SRAM_PERIOD_NO_ID_UPDATE_MS	100
 #define FULL_PERCENT_28BIT		0xFFFFFFF
-static void update_sram_data(struct fg_chip *chip, int *resched_ms)
+static int update_sram_data(struct fg_chip *chip, int *resched_ms)
 {
 	int i, j, rc = 0;
 	u8 reg[4];
@@ -2060,6 +2612,31 @@
 	}
 	fg_mem_release(chip);
 
+	/* Backup the registers whenever no error happens during update */
+	if (fg_reset_on_lockup && !chip->ima_error_handling) {
+		if (!rc) {
+			if (fg_debug_mask & FG_STATUS)
+				pr_info("backing up SRAM registers\n");
+			rc = fg_backup_sram_registers(chip, true);
+			if (rc) {
+				pr_err("Couldn't save sram registers\n");
+				goto out;
+			}
+			if (!chip->use_last_soc) {
+				chip->last_soc = get_monotonic_soc_raw(chip);
+				chip->last_cc_soc = div64_s64(
+					(int64_t)chip->last_soc *
+					FULL_PERCENT_28BIT, FULL_SOC_RAW);
+			}
+			if (fg_debug_mask & FG_STATUS)
+				pr_info("last_soc: %d last_cc_soc: %lld\n",
+					chip->last_soc, chip->last_cc_soc);
+		} else {
+			pr_err("update_sram failed\n");
+			goto out;
+		}
+	}
+
 	if (!rc)
 		get_current_time(&chip->last_sram_update_time);
 
@@ -2070,7 +2647,55 @@
 	} else {
 		*resched_ms = SRAM_PERIOD_NO_ID_UPDATE_MS;
 	}
+out:
 	fg_relax(&chip->update_sram_wakeup_source);
+	return rc;
+}
+
+#define SANITY_CHECK_PERIOD_MS	5000
+static void check_sanity_work(struct work_struct *work)
+{
+	struct fg_chip *chip = container_of(work,
+				struct fg_chip,
+				check_sanity_work.work);
+	int rc = 0;
+	u8 beat_count;
+	bool tried_once = false;
+
+	fg_stay_awake(&chip->sanity_wakeup_source);
+
+try_again:
+	rc = fg_read(chip, &beat_count,
+			chip->mem_base + MEM_INTF_FG_BEAT_COUNT, 1);
+	if (rc) {
+		pr_err("failed to read beat count rc=%d\n", rc);
+		goto resched;
+	}
+
+	if (fg_debug_mask & FG_STATUS)
+		pr_info("current: %d, prev: %d\n", beat_count,
+			chip->last_beat_count);
+
+	if (chip->last_beat_count == beat_count) {
+		if (!tried_once) {
+			/* Wait for 1 FG cycle and read it once again */
+			msleep(1500);
+			tried_once = true;
+			goto try_again;
+		} else {
+			pr_err("Beat count not updating\n");
+			fg_check_ima_error_handling(chip);
+			goto out;
+		}
+	} else {
+		chip->last_beat_count = beat_count;
+	}
+resched:
+	schedule_delayed_work(
+		&chip->check_sanity_work,
+		msecs_to_jiffies(SANITY_CHECK_PERIOD_MS));
+out:
+	fg_relax(&chip->sanity_wakeup_source);
 }
 
 #define SRAM_TIMEOUT_MS			3000
@@ -2079,8 +2704,9 @@
 	struct fg_chip *chip = container_of(work,
 				struct fg_chip,
 				update_sram_data.work);
-	int resched_ms = SRAM_PERIOD_NO_ID_UPDATE_MS, ret;
+	int resched_ms, ret;
 	bool tried_again = false;
+	int rc = 0;
 
 wait:
 	/* Wait for MEMIF access revoked */
@@ -2094,14 +2720,19 @@
 		goto wait;
 	} else if (ret <= 0) {
 		pr_err("transaction timed out ret=%d\n", ret);
+		if (fg_is_batt_id_valid(chip))
+			resched_ms = fg_sram_update_period_ms;
+		else
+			resched_ms = SRAM_PERIOD_NO_ID_UPDATE_MS;
 		goto out;
 	}
-	update_sram_data(chip, &resched_ms);
+	rc = update_sram_data(chip, &resched_ms);
 
 out:
-	schedule_delayed_work(
-		&chip->update_sram_data,
-		msecs_to_jiffies(resched_ms));
+	if (!rc)
+		schedule_delayed_work(
+			&chip->update_sram_data,
+			msecs_to_jiffies(resched_ms));
 }
 
 #define BATT_TEMP_OFFSET	3
@@ -2115,6 +2746,8 @@
 				TEMP_SENSE_CHARGE_BIT)
 #define TEMP_PERIOD_UPDATE_MS		10000
 #define TEMP_PERIOD_TIMEOUT_MS		3000
+#define BATT_TEMP_LOW_LIMIT		-600
+#define BATT_TEMP_HIGH_LIMIT		1500
 static void update_temp_data(struct work_struct *work)
 {
 	s16 temp;
@@ -2166,14 +2799,44 @@
 	}
 
 	temp = reg[0] | (reg[1] << 8);
-	fg_data[0].value = (temp * TEMP_LSB_16B / 1000)
-		- DECIKELVIN;
+	temp = (temp * TEMP_LSB_16B / 1000) - DECIKELVIN;
+
+	/*
+	 * If temperature is within the specified range (e.g. -60C and 150C),
+	 * update it to the userspace. Otherwise, use the last read good
+	 * temperature.
+	 */
+	if (temp > chip->batt_temp_low_limit &&
+			temp < chip->batt_temp_high_limit) {
+		chip->last_good_temp = temp;
+		fg_data[0].value = temp;
+	} else {
+		fg_data[0].value = chip->last_good_temp;
+
+		/*
+		 * If the temperature is read before and seems to be in valid
+		 * range, then a bad temperature reading could be because of
+		 * FG lockup. Trigger the FG reset sequence in such cases.
+		 */
+		if (chip->last_temp_update_time && fg_reset_on_lockup &&
+			(chip->last_good_temp > chip->batt_temp_low_limit &&
+			chip->last_good_temp < chip->batt_temp_high_limit)) {
+			pr_err("Batt_temp is %d !, triggering FG reset\n",
+				temp);
+			fg_check_ima_error_handling(chip);
+		}
+	}
 
 	if (fg_debug_mask & FG_MEM_DEBUG_READS)
 		pr_info("BATT_TEMP %d %d\n", temp, fg_data[0].value);
 
 	get_current_time(&chip->last_temp_update_time);
 
+	if (chip->soc_slope_limiter_en) {
+		fg_stay_awake(&chip->slope_limit_wakeup_source);
+		schedule_work(&chip->slope_limiter_work);
+	}
+
 out:
 	if (chip->sw_rbias_ctrl) {
 		rc = fg_mem_masked_write(chip, EXTERNAL_SENSE_SELECT,
@@ -2226,18 +2889,6 @@
 	return rc;
 }
 
-#define VBATT_LOW_STS_BIT BIT(2)
-static int fg_get_vbatt_status(struct fg_chip *chip, bool *vbatt_low_sts)
-{
-	int rc = 0;
-	u8 fg_batt_sts;
-
-	rc = fg_read(chip, &fg_batt_sts, INT_RT_STS(chip->batt_base), 1);
-	if (!rc)
-		*vbatt_low_sts = !!(fg_batt_sts & VBATT_LOW_STS_BIT);
-	return rc;
-}
-
 #define BATT_CYCLE_NUMBER_REG		0x5E8
 #define BATT_CYCLE_OFFSET		0
 static void restore_cycle_counter(struct fg_chip *chip)
@@ -2301,6 +2952,9 @@
 			bucket, rc);
 	else
 		chip->cyc_ctr.count[bucket] = cyc_count;
+
+	if (fg_debug_mask & FG_POWER_SUPPLY)
+		pr_info("Stored bucket %d cyc_count: %d\n", bucket, cyc_count);
 	return rc;
 }
 
@@ -2416,6 +3070,62 @@
 	return ((int)val) * 1000;
 }
 
+#define SLOPE_LIMITER_COEFF_REG		0x430
+#define SLOPE_LIMITER_COEFF_OFFSET	3
+#define SLOPE_LIMIT_TEMP_THRESHOLD	100
+#define SLOPE_LIMIT_LOW_TEMP_CHG	45
+#define SLOPE_LIMIT_HIGH_TEMP_CHG	2
+#define SLOPE_LIMIT_LOW_TEMP_DISCHG	45
+#define SLOPE_LIMIT_HIGH_TEMP_DISCHG	2
+static void slope_limiter_work(struct work_struct *work)
+{
+	struct fg_chip *chip = container_of(work, struct fg_chip,
+				slope_limiter_work);
+	enum slope_limit_status status;
+	int batt_temp, rc;
+	u8 buf[2];
+	int64_t val;
+
+	batt_temp = get_sram_prop_now(chip, FG_DATA_BATT_TEMP);
+
+	if (chip->status == POWER_SUPPLY_STATUS_CHARGING ||
+			chip->status == POWER_SUPPLY_STATUS_FULL) {
+		if (batt_temp < chip->slope_limit_temp)
+			status = LOW_TEMP_CHARGE;
+		else
+			status = HIGH_TEMP_CHARGE;
+	} else if (chip->status == POWER_SUPPLY_STATUS_DISCHARGING) {
+		if (batt_temp < chip->slope_limit_temp)
+			status = LOW_TEMP_DISCHARGE;
+		else
+			status = HIGH_TEMP_DISCHARGE;
+	} else {
+		goto out;
+	}
+
+	if (status == chip->slope_limit_sts)
+		goto out;
+
+	val = chip->slope_limit_coeffs[status];
+	val *= MICRO_UNIT;
+	half_float_to_buffer(val, buf);
+	rc = fg_mem_write(chip, buf,
+			SLOPE_LIMITER_COEFF_REG, 2,
+			SLOPE_LIMITER_COEFF_OFFSET, 0);
+	if (rc) {
+		pr_err("Couldn't write to slope_limiter_coeff_reg, rc=%d\n",
+			rc);
+		goto out;
+	}
+
+	chip->slope_limit_sts = status;
+	if (fg_debug_mask & FG_STATUS)
+		pr_info("Slope limit sts: %d val: %lld buf[%x %x] written\n",
+			status, val, buf[0], buf[1]);
+out:
+	fg_relax(&chip->slope_limit_wakeup_source);
+}
+
 static int lookup_ocv_for_soc(struct fg_chip *chip, int soc)
 {
 	int64_t *coeffs;
@@ -2481,6 +3191,7 @@
 #define ESR_ACTUAL_REG		0x554
 #define BATTERY_ESR_REG		0x4F4
 #define TEMP_RS_TO_RSLOW_REG	0x514
+#define ESR_OFFSET		2
 static int estimate_battery_age(struct fg_chip *chip, int *actual_capacity)
 {
 	int64_t ocv_cutoff_new, ocv_cutoff_aged, temp_rs_to_rslow;
@@ -2519,7 +3230,7 @@
 
 	rc = fg_mem_read(chip, buffer, ESR_ACTUAL_REG, 2, 2, 0);
 	esr_actual = half_float(buffer);
-	rc |= fg_mem_read(chip, buffer, BATTERY_ESR_REG, 2, 2, 0);
+	rc |= fg_mem_read(chip, buffer, BATTERY_ESR_REG, 2, ESR_OFFSET, 0);
 	battery_esr = half_float(buffer);
 
 	if (rc) {
@@ -2594,124 +3305,6 @@
 	estimate_battery_age(chip, &chip->actual_cap_uah);
 }
 
-static enum power_supply_property fg_power_props[] = {
-	POWER_SUPPLY_PROP_CAPACITY,
-	POWER_SUPPLY_PROP_CAPACITY_RAW,
-	POWER_SUPPLY_PROP_CURRENT_NOW,
-	POWER_SUPPLY_PROP_VOLTAGE_NOW,
-	POWER_SUPPLY_PROP_VOLTAGE_OCV,
-	POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
-	POWER_SUPPLY_PROP_CHARGE_NOW,
-	POWER_SUPPLY_PROP_CHARGE_NOW_RAW,
-	POWER_SUPPLY_PROP_CHARGE_NOW_ERROR,
-	POWER_SUPPLY_PROP_CHARGE_FULL,
-	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
-	POWER_SUPPLY_PROP_TEMP,
-	POWER_SUPPLY_PROP_COOL_TEMP,
-	POWER_SUPPLY_PROP_WARM_TEMP,
-	POWER_SUPPLY_PROP_RESISTANCE,
-	POWER_SUPPLY_PROP_RESISTANCE_ID,
-	POWER_SUPPLY_PROP_BATTERY_TYPE,
-	POWER_SUPPLY_PROP_UPDATE_NOW,
-	POWER_SUPPLY_PROP_ESR_COUNT,
-	POWER_SUPPLY_PROP_VOLTAGE_MIN,
-	POWER_SUPPLY_PROP_CYCLE_COUNT,
-	POWER_SUPPLY_PROP_CYCLE_COUNT_ID,
-	POWER_SUPPLY_PROP_HI_POWER,
-};
-
-static int fg_power_get_property(struct power_supply *psy,
-				       enum power_supply_property psp,
-				       union power_supply_propval *val)
-{
-	struct fg_chip *chip = power_supply_get_drvdata(psy);
-	bool vbatt_low_sts;
-
-	switch (psp) {
-	case POWER_SUPPLY_PROP_BATTERY_TYPE:
-		if (chip->battery_missing)
-			val->strval = missing_batt_type;
-		else if (chip->fg_restarting)
-			val->strval = loading_batt_type;
-		else
-			val->strval = chip->batt_type;
-		break;
-	case POWER_SUPPLY_PROP_CAPACITY:
-		val->intval = get_prop_capacity(chip);
-		break;
-	case POWER_SUPPLY_PROP_CAPACITY_RAW:
-		val->intval = get_sram_prop_now(chip, FG_DATA_BATT_SOC);
-		break;
-	case POWER_SUPPLY_PROP_CHARGE_NOW_ERROR:
-		val->intval = get_sram_prop_now(chip, FG_DATA_VINT_ERR);
-		break;
-	case POWER_SUPPLY_PROP_CURRENT_NOW:
-		val->intval = get_sram_prop_now(chip, FG_DATA_CURRENT);
-		break;
-	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-		val->intval = get_sram_prop_now(chip, FG_DATA_VOLTAGE);
-		break;
-	case POWER_SUPPLY_PROP_VOLTAGE_OCV:
-		val->intval = get_sram_prop_now(chip, FG_DATA_OCV);
-		break;
-	case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
-		val->intval = chip->batt_max_voltage_uv;
-		break;
-	case POWER_SUPPLY_PROP_TEMP:
-		val->intval = get_sram_prop_now(chip, FG_DATA_BATT_TEMP);
-		break;
-	case POWER_SUPPLY_PROP_COOL_TEMP:
-		val->intval = get_prop_jeita_temp(chip, FG_MEM_SOFT_COLD);
-		break;
-	case POWER_SUPPLY_PROP_WARM_TEMP:
-		val->intval = get_prop_jeita_temp(chip, FG_MEM_SOFT_HOT);
-		break;
-	case POWER_SUPPLY_PROP_RESISTANCE:
-		val->intval = get_sram_prop_now(chip, FG_DATA_BATT_ESR);
-		break;
-	case POWER_SUPPLY_PROP_ESR_COUNT:
-		val->intval = get_sram_prop_now(chip, FG_DATA_BATT_ESR_COUNT);
-		break;
-	case POWER_SUPPLY_PROP_CYCLE_COUNT:
-		val->intval = fg_get_cycle_count(chip);
-		break;
-	case POWER_SUPPLY_PROP_CYCLE_COUNT_ID:
-		val->intval = chip->cyc_ctr.id;
-		break;
-	case POWER_SUPPLY_PROP_RESISTANCE_ID:
-		val->intval = get_sram_prop_now(chip, FG_DATA_BATT_ID);
-		break;
-	case POWER_SUPPLY_PROP_UPDATE_NOW:
-		val->intval = 0;
-		break;
-	case POWER_SUPPLY_PROP_VOLTAGE_MIN:
-		if (!fg_get_vbatt_status(chip, &vbatt_low_sts))
-			val->intval = (int)vbatt_low_sts;
-		else
-			val->intval = 1;
-		break;
-	case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
-		val->intval = chip->nom_cap_uah;
-		break;
-	case POWER_SUPPLY_PROP_CHARGE_FULL:
-		val->intval = chip->learning_data.learned_cc_uah;
-		break;
-	case POWER_SUPPLY_PROP_CHARGE_NOW:
-		val->intval = chip->learning_data.cc_uah;
-		break;
-	case POWER_SUPPLY_PROP_CHARGE_NOW_RAW:
-		val->intval = get_sram_prop_now(chip, FG_DATA_CC_CHARGE);
-		break;
-	case POWER_SUPPLY_PROP_HI_POWER:
-		val->intval = !!chip->bcl_lpm_disabled;
-		break;
-	default:
-		return -EINVAL;
-	}
-
-	return 0;
-}
-
 static int correction_times[] = {
 	1470,
 	2940,
@@ -2853,11 +3446,8 @@
 		goto fail;
 	}
 
-	if (chip->wa_flag & USE_CC_SOC_REG) {
-		mutex_unlock(&chip->learning_data.learning_lock);
-		fg_relax(&chip->capacity_learning_wakeup_source);
-		return;
-	}
+	if (chip->wa_flag & USE_CC_SOC_REG)
+		goto fail;
 
 	fg_mem_lock(chip);
 
@@ -2888,6 +3478,8 @@
 		pr_info("total_cc_uah = %lld\n", chip->learning_data.cc_uah);
 
 fail:
+	if (chip->wa_flag & USE_CC_SOC_REG)
+		fg_relax(&chip->capacity_learning_wakeup_source);
 	mutex_unlock(&chip->learning_data.learning_lock);
 	return;
 
@@ -2901,7 +3493,7 @@
 {
 	int rc;
 	u8 reg[4];
-	unsigned int temp, magnitude;
+	int temp;
 
 	rc = fg_mem_read(chip, reg, CC_SOC_BASE_REG, 4, CC_SOC_OFFSET, 0);
 	if (rc) {
@@ -2910,20 +3502,61 @@
 	}
 
 	temp = reg[3] << 24 | reg[2] << 16 | reg[1] << 8 | reg[0];
-	magnitude = temp & CC_SOC_MAGNITUDE_MASK;
-	if (temp & CC_SOC_NEGATIVE_BIT)
-		*cc_soc = -1 * (~magnitude + 1);
-	else
-		*cc_soc = magnitude;
-
+	*cc_soc = sign_extend32(temp, 29);
 	return 0;
 }
 
+static int fg_get_current_cc(struct fg_chip *chip)
+{
+	int cc_soc, rc;
+	int64_t current_capacity;
+
+	if (!(chip->wa_flag & USE_CC_SOC_REG))
+		return chip->learning_data.cc_uah;
+
+	if (!chip->learning_data.learned_cc_uah)
+		return -EINVAL;
+
+	rc = fg_get_cc_soc(chip, &cc_soc);
+	if (rc < 0) {
+		pr_err("Failed to get cc_soc, rc=%d\n", rc);
+		return rc;
+	}
+
+	current_capacity = cc_soc * chip->learning_data.learned_cc_uah;
+	current_capacity = div64_u64(current_capacity, FULL_PERCENT_28BIT);
+	return current_capacity;
+}
+
+#define BATT_MISSING_STS BIT(6)
+static bool is_battery_missing(struct fg_chip *chip)
+{
+	int rc;
+	u8 fg_batt_sts;
+
+	rc = fg_read(chip, &fg_batt_sts,
+				 INT_RT_STS(chip->batt_base), 1);
+	if (rc) {
+		pr_err("spmi read failed: addr=%03X, rc=%d\n",
+				INT_RT_STS(chip->batt_base), rc);
+		return false;
+	}
+
+	return (fg_batt_sts & BATT_MISSING_STS) ? true : false;
+}
+
 static int fg_cap_learning_process_full_data(struct fg_chip *chip)
 {
 	int cc_pc_val, rc = -EINVAL;
 	unsigned int cc_soc_delta_pc;
 	int64_t delta_cc_uah;
+	uint64_t temp;
+	bool batt_missing = is_battery_missing(chip);
+
+	if (batt_missing) {
+		pr_err("Battery is missing!\n");
+		goto fail;
+	}
 
 	if (!chip->learning_data.active)
 		goto fail;
@@ -2940,9 +3573,8 @@
 		goto fail;
 	}
 
-	cc_soc_delta_pc = DIV_ROUND_CLOSEST(
-			abs(cc_pc_val - chip->learning_data.init_cc_pc_val)
-			* 100, FULL_PERCENT_28BIT);
+	temp = abs(cc_pc_val - chip->learning_data.init_cc_pc_val);
+	cc_soc_delta_pc = DIV_ROUND_CLOSEST_ULL(temp * 100, FULL_PERCENT_28BIT);
 
 	delta_cc_uah = div64_s64(
 			chip->learning_data.learned_cc_uah * cc_soc_delta_pc,
@@ -2950,8 +3582,11 @@
 	chip->learning_data.cc_uah = delta_cc_uah + chip->learning_data.cc_uah;
 
 	if (fg_debug_mask & FG_AGING)
-		pr_info("current cc_soc=%d cc_soc_pc=%d total_cc_uah = %lld\n",
+		pr_info("current cc_soc=%d cc_soc_pc=%d init_cc_pc_val=%d delta_cc_uah=%lld learned_cc_uah=%lld total_cc_uah = %lld\n",
 				cc_pc_val, cc_soc_delta_pc,
+				chip->learning_data.init_cc_pc_val,
+				delta_cc_uah,
+				chip->learning_data.learned_cc_uah,
 				chip->learning_data.cc_uah);
 
 	return 0;
@@ -3044,6 +3679,12 @@
 {
 	int16_t cc_mah;
 	int rc;
+	bool batt_missing = is_battery_missing(chip);
+
+	if (batt_missing) {
+		pr_err("Battery is missing!\n");
+		return;
+	}
 
 	cc_mah = div64_s64(chip->learning_data.learned_cc_uah, 1000);
 
@@ -3065,6 +3706,12 @@
 static void fg_cap_learning_post_process(struct fg_chip *chip)
 {
 	int64_t max_inc_val, min_dec_val, old_cap;
+	bool batt_missing = is_battery_missing(chip);
+
+	if (batt_missing) {
+		pr_err("Battery is missing!\n");
+		return;
+	}
 
 	max_inc_val = chip->learning_data.learned_cc_uah
 			* (1000 + chip->learning_data.max_increment);
@@ -3083,6 +3730,32 @@
 		chip->learning_data.learned_cc_uah =
 			chip->learning_data.cc_uah;
 
+	if (chip->learning_data.max_cap_limit) {
+		max_inc_val = (int64_t)chip->nom_cap_uah * (1000 +
+				chip->learning_data.max_cap_limit);
+		max_inc_val = div64_u64(max_inc_val, 1000);
+		if (chip->learning_data.cc_uah > max_inc_val) {
+			if (fg_debug_mask & FG_AGING)
+				pr_info("learning capacity %lld goes above max limit %lld\n",
+					chip->learning_data.cc_uah,
+					max_inc_val);
+			chip->learning_data.learned_cc_uah = max_inc_val;
+		}
+	}
+
+	if (chip->learning_data.min_cap_limit) {
+		min_dec_val = (int64_t)chip->nom_cap_uah * (1000 -
+				chip->learning_data.min_cap_limit);
+		min_dec_val = div64_u64(min_dec_val, 1000);
+		if (chip->learning_data.cc_uah < min_dec_val) {
+			if (fg_debug_mask & FG_AGING)
+				pr_info("learning capacity %lld goes below min limit %lld\n",
+					chip->learning_data.cc_uah,
+					min_dec_val);
+			chip->learning_data.learned_cc_uah = min_dec_val;
+		}
+	}
+
 	fg_cap_learning_save_data(chip);
 	if (fg_debug_mask & FG_AGING)
 		pr_info("final cc_uah = %lld, learned capacity %lld -> %lld uah\n",
@@ -3142,7 +3815,7 @@
 		if (battery_soc * 100 / FULL_PERCENT_3B
 				> chip->learning_data.max_start_soc) {
 			if (fg_debug_mask & FG_AGING)
-				pr_info("battery soc too low (%d < %d), aborting\n",
+				pr_info("battery soc too high (%d > %d), aborting\n",
 					battery_soc * 100 / FULL_PERCENT_3B,
 					chip->learning_data.max_start_soc);
 			fg_mem_release(chip);
@@ -3226,6 +3899,17 @@
 		}
 
 		fg_cap_learning_stop(chip);
+	} else if (chip->status == POWER_SUPPLY_STATUS_FULL) {
+		if (chip->wa_flag & USE_CC_SOC_REG) {
+			/* reset SW_CC_SOC register to 100% upon charge_full */
+			rc = fg_mem_write(chip, (u8 *)&cc_pc_100,
+				CC_SOC_BASE_REG, 4, CC_SOC_OFFSET, 0);
+			if (rc)
+				pr_err("Failed to reset CC_SOC_REG rc=%d\n",
+								rc);
+			else if (fg_debug_mask & FG_STATUS)
+				pr_info("Reset SW_CC_SOC to full value\n");
+		}
 	}
 
 fail:
@@ -3323,7 +4007,14 @@
 				struct fg_chip,
 				status_change_work);
 	unsigned long current_time = 0;
-	int cc_soc, rc, capacity = get_prop_capacity(chip);
+	int cc_soc, batt_soc, rc, capacity = get_prop_capacity(chip);
+	bool batt_missing = is_battery_missing(chip);
+
+	if (batt_missing) {
+		if (fg_debug_mask & FG_STATUS)
+			pr_info("Battery is missing\n");
+		return;
+	}
 
 	if (chip->esr_pulse_tune_en) {
 		fg_stay_awake(&chip->esr_extract_wakeup_source);
@@ -3343,19 +4034,34 @@
 	}
 	if (chip->status == POWER_SUPPLY_STATUS_FULL ||
 			chip->status == POWER_SUPPLY_STATUS_CHARGING) {
-		if (!chip->vbat_low_irq_enabled) {
+		if (!chip->vbat_low_irq_enabled &&
+				!chip->use_vbat_low_empty_soc) {
 			enable_irq(chip->batt_irq[VBATT_LOW].irq);
 			enable_irq_wake(chip->batt_irq[VBATT_LOW].irq);
 			chip->vbat_low_irq_enabled = true;
 		}
+
+		if (!chip->full_soc_irq_enabled) {
+			enable_irq(chip->soc_irq[FULL_SOC].irq);
+			enable_irq_wake(chip->soc_irq[FULL_SOC].irq);
+			chip->full_soc_irq_enabled = true;
+		}
+
 		if (!!(chip->wa_flag & PULSE_REQUEST_WA) && capacity == 100)
 			fg_configure_soc(chip);
 	} else if (chip->status == POWER_SUPPLY_STATUS_DISCHARGING) {
-		if (chip->vbat_low_irq_enabled) {
+		if (chip->vbat_low_irq_enabled &&
+				!chip->use_vbat_low_empty_soc) {
 			disable_irq_wake(chip->batt_irq[VBATT_LOW].irq);
 			disable_irq_nosync(chip->batt_irq[VBATT_LOW].irq);
 			chip->vbat_low_irq_enabled = false;
 		}
+
+		if (chip->full_soc_irq_enabled) {
+			disable_irq_wake(chip->soc_irq[FULL_SOC].irq);
+			disable_irq_nosync(chip->soc_irq[FULL_SOC].irq);
+			chip->full_soc_irq_enabled = false;
+		}
 	}
 	fg_cap_learning_check(chip);
 	schedule_work(&chip->update_esr_work);
@@ -3368,6 +4074,42 @@
 	}
 
 	if (chip->prev_status != chip->status && chip->last_sram_update_time) {
+		/*
+		 * Reset SW_CC_SOC to a value based off battery SOC when
+		 * the device is discharging.
+		 */
+		if (chip->status == POWER_SUPPLY_STATUS_DISCHARGING) {
+			batt_soc = get_battery_soc_raw(chip);
+			if (!batt_soc)
+				return;
+
+			batt_soc = div64_s64((int64_t)batt_soc *
+					FULL_PERCENT_28BIT, FULL_PERCENT_3B);
+			rc = fg_mem_write(chip, (u8 *)&batt_soc,
+				CC_SOC_BASE_REG, 4, CC_SOC_OFFSET, 0);
+			if (rc)
+				pr_err("Failed to reset CC_SOC_REG rc=%d\n",
+									rc);
+			else if (fg_debug_mask & FG_STATUS)
+				pr_info("Reset SW_CC_SOC to %x\n", batt_soc);
+		}
+
+		/*
+		 * Schedule the update_temp_work whenever there is a status
+		 * change. This is essential for applying the slope limiter
+		 * coefficients when that feature is enabled.
+		 */
+		if (chip->last_temp_update_time && chip->soc_slope_limiter_en) {
+			cancel_delayed_work_sync(&chip->update_temp_work);
+			schedule_delayed_work(&chip->update_temp_work,
+				msecs_to_jiffies(0));
+		}
+
+		if (chip->dischg_gain.enable) {
+			fg_stay_awake(&chip->dischg_gain_wakeup_source);
+			schedule_work(&chip->dischg_gain_work);
+		}
+
 		get_current_time(&current_time);
 		/*
 		 * When charging status changes, update SRAM parameters if it
@@ -3393,10 +4135,10 @@
 	}
 	if ((chip->wa_flag & USE_CC_SOC_REG) && chip->bad_batt_detection_en
 			&& chip->safety_timer_expired) {
-		chip->sw_cc_soc_data.delta_soc =
-			DIV_ROUND_CLOSEST(abs(cc_soc -
-					chip->sw_cc_soc_data.init_cc_soc)
-					* 100, FULL_PERCENT_28BIT);
+		uint64_t delta_cc_soc = abs(cc_soc -
+					chip->sw_cc_soc_data.init_cc_soc);
+		chip->sw_cc_soc_data.delta_soc = DIV_ROUND_CLOSEST_ULL(
+				delta_cc_soc * 100, FULL_PERCENT_28BIT);
 		chip->sw_cc_soc_data.full_capacity =
 			chip->sw_cc_soc_data.delta_soc +
 			chip->sw_cc_soc_data.init_sys_soc;
@@ -3539,6 +4281,395 @@
 	return rc;
 }
 
+static int fg_restore_cc_soc(struct fg_chip *chip)
+{
+	int rc;
+
+	if (!chip->use_last_cc_soc || !chip->last_cc_soc)
+		return 0;
+
+	if (fg_debug_mask & FG_STATUS)
+		pr_info("Restoring cc_soc: %lld\n", chip->last_cc_soc);
+
+	rc = fg_mem_write(chip, (u8 *)&chip->last_cc_soc,
+			fg_data[FG_DATA_CC_CHARGE].address, 4,
+			fg_data[FG_DATA_CC_CHARGE].offset, 0);
+	if (rc)
+		pr_err("failed to update CC_SOC rc=%d\n", rc);
+	else
+		chip->use_last_cc_soc = false;
+
+	return rc;
+}
+
+#define SRAM_MONOTONIC_SOC_REG		0x574
+#define SRAM_MONOTONIC_SOC_OFFSET	2
+static int fg_restore_soc(struct fg_chip *chip)
+{
+	int rc;
+	u16 msoc;
+
+	if (chip->use_last_soc && chip->last_soc)
+		msoc = DIV_ROUND_CLOSEST(chip->last_soc * 0xFFFF,
+				FULL_SOC_RAW);
+	else
+		return 0;
+
+	if (fg_debug_mask & FG_STATUS)
+		pr_info("Restored soc: %d\n", msoc);
+
+	rc = fg_mem_write(chip, (u8 *)&msoc, SRAM_MONOTONIC_SOC_REG, 2,
+			SRAM_MONOTONIC_SOC_OFFSET, 0);
+	if (rc)
+		pr_err("failed to write M_SOC_REG rc=%d\n", rc);
+
+	return rc;
+}
+
+#define NOM_CAP_REG			0x4F4
+#define CAPACITY_DELTA_DECIPCT		500
+static int load_battery_aging_data(struct fg_chip *chip)
+{
+	int rc = 0;
+	u8 buffer[2];
+	int16_t cc_mah;
+	int64_t delta_cc_uah, pct_nom_cap_uah;
+
+	rc = fg_mem_read(chip, buffer, NOM_CAP_REG, 2, 0, 0);
+	if (rc) {
+		pr_err("Failed to read nominal capacitance: %d\n", rc);
+		goto out;
+	}
+
+	chip->nom_cap_uah = bcap_uah_2b(buffer);
+	chip->actual_cap_uah = chip->nom_cap_uah;
+
+	if (chip->learning_data.learned_cc_uah == 0) {
+		chip->learning_data.learned_cc_uah = chip->nom_cap_uah;
+		fg_cap_learning_save_data(chip);
+	} else if (chip->learning_data.feedback_on) {
+		delta_cc_uah = abs(chip->learning_data.learned_cc_uah -
+					chip->nom_cap_uah);
+		pct_nom_cap_uah = div64_s64((int64_t)chip->nom_cap_uah *
+				CAPACITY_DELTA_DECIPCT, 1000);
+		/*
+		 * If the learned capacity is out of range, say by 50%
+		 * from the nominal capacity, then overwrite the learned
+		 * capacity with the nominal capacity.
+		 */
+		if (chip->nom_cap_uah && delta_cc_uah > pct_nom_cap_uah) {
+			if (fg_debug_mask & FG_AGING) {
+				pr_info("learned_cc_uah: %lld is higher than expected\n",
+					chip->learning_data.learned_cc_uah);
+				pr_info("Capping it to nominal:%d\n",
+					chip->nom_cap_uah);
+			}
+			chip->learning_data.learned_cc_uah = chip->nom_cap_uah;
+			fg_cap_learning_save_data(chip);
+		} else {
+			cc_mah = div64_s64(chip->learning_data.learned_cc_uah,
+					1000);
+			rc = fg_calc_and_store_cc_soc_coeff(chip, cc_mah);
+			if (rc)
+				pr_err("Error in restoring cc_soc_coeff, rc:%d\n",
+					rc);
+		}
+	}
+out:
+	return rc;
+}
+
+static void fg_restore_battery_info(struct fg_chip *chip)
+{
+	int rc;
+	char buf[4] = {0, 0, 0, 0};
+
+	chip->last_soc = DIV_ROUND_CLOSEST(chip->batt_info[BATT_INFO_SOC] *
+				FULL_SOC_RAW, FULL_CAPACITY);
+	chip->last_cc_soc = div64_s64((int64_t)chip->last_soc *
+				FULL_PERCENT_28BIT, FULL_SOC_RAW);
+	chip->use_last_soc = true;
+	chip->use_last_cc_soc = true;
+	rc = fg_restore_soc(chip);
+	if (rc) {
+		pr_err("Error in restoring soc, rc=%d\n", rc);
+		goto out;
+	}
+
+	rc = fg_restore_cc_soc(chip);
+	if (rc) {
+		pr_err("Error in restoring cc_soc, rc=%d\n", rc);
+		goto out;
+	}
+
+	rc = fg_mem_write(chip, buf,
+			fg_data[FG_DATA_VINT_ERR].address,
+			fg_data[FG_DATA_VINT_ERR].len,
+			fg_data[FG_DATA_VINT_ERR].offset, 0);
+	if (rc) {
+		pr_err("Failed to write to VINT_ERR, rc=%d\n", rc);
+		goto out;
+	}
+
+	chip->learning_data.learned_cc_uah = chip->batt_info[BATT_INFO_FCC];
+	rc = load_battery_aging_data(chip);
+	if (rc) {
+		pr_err("Failed to load battery aging data, rc:%d\n", rc);
+		goto out;
+	}
+
+	if (chip->power_supply_registered)
+		power_supply_changed(chip->bms_psy);
+
+	if (fg_debug_mask & FG_STATUS)
+		pr_info("Restored battery info!\n");
+
+out:
+	return;
+}
+
+#define DELTA_BATT_TEMP		30
+static bool fg_validate_battery_info(struct fg_chip *chip)
+{
+	int i, delta_pct, batt_id_kohm, batt_temp, batt_volt_mv, batt_soc;
+
+	for (i = 1; i < BATT_INFO_MAX; i++) {
+		if (fg_debug_mask & FG_STATUS)
+			pr_info("batt_info[%d]: %d\n", i, chip->batt_info[i]);
+
+		if ((chip->batt_info[i] == 0 && i != BATT_INFO_TEMP) ||
+			chip->batt_info[i] == INT_MAX) {
+			if (fg_debug_mask & FG_STATUS)
+				pr_info("batt_info[%d]:%d is invalid\n", i,
+					chip->batt_info[i]);
+			return false;
+		}
+	}
+
+	batt_id_kohm = get_sram_prop_now(chip, FG_DATA_BATT_ID) / 1000;
+	if (batt_id_kohm != chip->batt_info[BATT_INFO_RES_ID]) {
+		if (fg_debug_mask & FG_STATUS)
+			pr_info("batt_id(%dK) does not match the stored batt_id(%dK)\n",
+				batt_id_kohm,
+				chip->batt_info[BATT_INFO_RES_ID]);
+		return false;
+	}
+
+	batt_temp = get_sram_prop_now(chip, FG_DATA_BATT_TEMP);
+	if (abs(chip->batt_info[BATT_INFO_TEMP] - batt_temp) >
+			DELTA_BATT_TEMP) {
+		if (fg_debug_mask & FG_STATUS)
+			pr_info("batt_temp(%d) is higher/lower than stored batt_temp(%d)\n",
+				batt_temp, chip->batt_info[BATT_INFO_TEMP]);
+		return false;
+	}
+
+	if (chip->batt_info[BATT_INFO_FCC] < 0) {
+		if (fg_debug_mask & FG_STATUS)
+			pr_info("batt_fcc cannot be %d\n",
+				chip->batt_info[BATT_INFO_FCC]);
+		return false;
+	}
+
+	batt_volt_mv = get_sram_prop_now(chip, FG_DATA_VOLTAGE) / 1000;
+	batt_soc = get_monotonic_soc_raw(chip);
+	if (batt_soc != 0 && batt_soc != FULL_SOC_RAW)
+		batt_soc = DIV_ROUND_CLOSEST((batt_soc - 1) *
+				(FULL_CAPACITY - 2), FULL_SOC_RAW - 2) + 1;
+
+	if (*chip->batt_range_ocv && chip->batt_max_voltage_uv > 1000)
+		delta_pct =  DIV_ROUND_CLOSEST(abs(batt_volt_mv -
+				chip->batt_info[BATT_INFO_VOLTAGE]) * 100,
+				chip->batt_max_voltage_uv / 1000);
+	else
+		delta_pct = abs(batt_soc - chip->batt_info[BATT_INFO_SOC]);
+
+	if (fg_debug_mask & FG_STATUS)
+		pr_info("Validating by %s batt_voltage:%d capacity:%d delta_pct:%d\n",
+			*chip->batt_range_ocv ? "OCV" : "SOC", batt_volt_mv,
+			batt_soc, delta_pct);
+
+	if (*chip->batt_range_pct && delta_pct > *chip->batt_range_pct) {
+		if (fg_debug_mask & FG_STATUS)
+			pr_info("delta_pct(%d) is higher than batt_range_pct(%d)\n",
+				delta_pct, *chip->batt_range_pct);
+		return false;
+	}
+
+	return true;
+}
+
+static int fg_set_battery_info(struct fg_chip *chip, int val)
+{
+	if (chip->batt_info_id < 0 ||
+			chip->batt_info_id >= BATT_INFO_MAX) {
+		pr_err("Invalid batt_info_id %d\n", chip->batt_info_id);
+		chip->batt_info_id = 0;
+		return -EINVAL;
+	}
+
+	if (chip->batt_info_id == BATT_INFO_NOTIFY && val == INT_MAX - 1) {
+		if (fg_debug_mask & FG_STATUS)
+			pr_info("Notified from userspace\n");
+		if (chip->batt_info_restore && !chip->ima_error_handling) {
+			if (!fg_validate_battery_info(chip)) {
+				if (fg_debug_mask & FG_STATUS)
+					pr_info("Validating battery info failed\n");
+			} else {
+				fg_restore_battery_info(chip);
+			}
+		}
+	}
+
+	chip->batt_info[chip->batt_info_id] = val;
+	return 0;
+}
+
+static enum power_supply_property fg_power_props[] = {
+	POWER_SUPPLY_PROP_CAPACITY,
+	POWER_SUPPLY_PROP_CAPACITY_RAW,
+	POWER_SUPPLY_PROP_CURRENT_NOW,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_VOLTAGE_OCV,
+	POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+	POWER_SUPPLY_PROP_CHARGE_COUNTER,
+	POWER_SUPPLY_PROP_CHARGE_NOW,
+	POWER_SUPPLY_PROP_CHARGE_NOW_RAW,
+	POWER_SUPPLY_PROP_CHARGE_NOW_ERROR,
+	POWER_SUPPLY_PROP_CHARGE_FULL,
+	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+	POWER_SUPPLY_PROP_TEMP,
+	POWER_SUPPLY_PROP_COOL_TEMP,
+	POWER_SUPPLY_PROP_WARM_TEMP,
+	POWER_SUPPLY_PROP_RESISTANCE,
+	POWER_SUPPLY_PROP_RESISTANCE_ID,
+	POWER_SUPPLY_PROP_BATTERY_TYPE,
+	POWER_SUPPLY_PROP_UPDATE_NOW,
+	POWER_SUPPLY_PROP_ESR_COUNT,
+	POWER_SUPPLY_PROP_VOLTAGE_MIN,
+	POWER_SUPPLY_PROP_CYCLE_COUNT,
+	POWER_SUPPLY_PROP_CYCLE_COUNT_ID,
+	POWER_SUPPLY_PROP_HI_POWER,
+	POWER_SUPPLY_PROP_SOC_REPORTING_READY,
+	POWER_SUPPLY_PROP_IGNORE_FALSE_NEGATIVE_ISENSE,
+	POWER_SUPPLY_PROP_ENABLE_JEITA_DETECTION,
+	POWER_SUPPLY_PROP_BATTERY_INFO,
+	POWER_SUPPLY_PROP_BATTERY_INFO_ID,
+};
+
+static int fg_power_get_property(struct power_supply *psy,
+				       enum power_supply_property psp,
+				       union power_supply_propval *val)
+{
+	struct fg_chip *chip =  power_supply_get_drvdata(psy);
+	bool vbatt_low_sts;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_BATTERY_TYPE:
+		if (chip->battery_missing)
+			val->strval = missing_batt_type;
+		else if (chip->fg_restarting)
+			val->strval = loading_batt_type;
+		else
+			val->strval = chip->batt_type;
+		break;
+	case POWER_SUPPLY_PROP_CAPACITY:
+		val->intval = get_prop_capacity(chip);
+		break;
+	case POWER_SUPPLY_PROP_CAPACITY_RAW:
+		val->intval = get_sram_prop_now(chip, FG_DATA_BATT_SOC);
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_NOW_ERROR:
+		val->intval = get_sram_prop_now(chip, FG_DATA_VINT_ERR);
+		break;
+	case POWER_SUPPLY_PROP_CURRENT_NOW:
+		val->intval = get_sram_prop_now(chip, FG_DATA_CURRENT);
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		val->intval = get_sram_prop_now(chip, FG_DATA_VOLTAGE);
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_OCV:
+		val->intval = get_sram_prop_now(chip, FG_DATA_OCV);
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+		val->intval = chip->batt_max_voltage_uv;
+		break;
+	case POWER_SUPPLY_PROP_TEMP:
+		val->intval = get_sram_prop_now(chip, FG_DATA_BATT_TEMP);
+		break;
+	case POWER_SUPPLY_PROP_COOL_TEMP:
+		val->intval = get_prop_jeita_temp(chip, FG_MEM_SOFT_COLD);
+		break;
+	case POWER_SUPPLY_PROP_WARM_TEMP:
+		val->intval = get_prop_jeita_temp(chip, FG_MEM_SOFT_HOT);
+		break;
+	case POWER_SUPPLY_PROP_RESISTANCE:
+		val->intval = get_sram_prop_now(chip, FG_DATA_BATT_ESR);
+		break;
+	case POWER_SUPPLY_PROP_ESR_COUNT:
+		val->intval = get_sram_prop_now(chip, FG_DATA_BATT_ESR_COUNT);
+		break;
+	case POWER_SUPPLY_PROP_CYCLE_COUNT:
+		val->intval = fg_get_cycle_count(chip);
+		break;
+	case POWER_SUPPLY_PROP_CYCLE_COUNT_ID:
+		val->intval = chip->cyc_ctr.id;
+		break;
+	case POWER_SUPPLY_PROP_RESISTANCE_ID:
+		val->intval = get_sram_prop_now(chip, FG_DATA_BATT_ID);
+		break;
+	case POWER_SUPPLY_PROP_UPDATE_NOW:
+		val->intval = 0;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_MIN:
+		if (!fg_get_vbatt_status(chip, &vbatt_low_sts))
+			val->intval = (int)vbatt_low_sts;
+		else
+			val->intval = 1;
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+		val->intval = chip->nom_cap_uah;
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_FULL:
+		val->intval = chip->learning_data.learned_cc_uah;
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_NOW:
+		val->intval = chip->learning_data.cc_uah;
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_NOW_RAW:
+		val->intval = get_sram_prop_now(chip, FG_DATA_CC_CHARGE);
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_COUNTER:
+		val->intval = fg_get_current_cc(chip);
+		break;
+	case POWER_SUPPLY_PROP_HI_POWER:
+		val->intval = !!chip->bcl_lpm_disabled;
+		break;
+	case POWER_SUPPLY_PROP_SOC_REPORTING_READY:
+		val->intval = !!chip->soc_reporting_ready;
+		break;
+	case POWER_SUPPLY_PROP_IGNORE_FALSE_NEGATIVE_ISENSE:
+		val->intval = !chip->allow_false_negative_isense;
+		break;
+	case POWER_SUPPLY_PROP_ENABLE_JEITA_DETECTION:
+		val->intval = chip->use_soft_jeita_irq;
+		break;
+	case POWER_SUPPLY_PROP_BATTERY_INFO:
+		if (chip->batt_info_id < 0 ||
+				chip->batt_info_id >= BATT_INFO_MAX)
+			return -EINVAL;
+		val->intval = chip->batt_info[chip->batt_info_id];
+		break;
+	case POWER_SUPPLY_PROP_BATTERY_INFO_ID:
+		val->intval = chip->batt_info_id;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 static int fg_power_set_property(struct power_supply *psy,
 				  enum power_supply_property psp,
 				  const union power_supply_propval *val)
@@ -3557,6 +4688,67 @@
 		if (val->intval)
 			update_sram_data(chip, &unused);
 		break;
+	case POWER_SUPPLY_PROP_IGNORE_FALSE_NEGATIVE_ISENSE:
+		rc = set_prop_ignore_false_negative_isense(chip, !!val->intval);
+		if (rc)
+			pr_err("set_prop_ignore_false_negative_isense failed, rc=%d\n",
+							rc);
+		else
+			chip->allow_false_negative_isense = !val->intval;
+		break;
+	case POWER_SUPPLY_PROP_ENABLE_JEITA_DETECTION:
+		if (chip->use_soft_jeita_irq == !!val->intval) {
+			pr_debug("JEITA irq %s, ignore!\n",
+				chip->use_soft_jeita_irq ?
+				"enabled" : "disabled");
+			break;
+		}
+		chip->use_soft_jeita_irq = !!val->intval;
+		if (chip->use_soft_jeita_irq) {
+			if (chip->batt_irq[JEITA_SOFT_COLD].disabled) {
+				enable_irq(
+					chip->batt_irq[JEITA_SOFT_COLD].irq);
+				chip->batt_irq[JEITA_SOFT_COLD].disabled =
+								false;
+			}
+			if (!chip->batt_irq[JEITA_SOFT_COLD].wakeup) {
+				enable_irq_wake(
+					chip->batt_irq[JEITA_SOFT_COLD].irq);
+				chip->batt_irq[JEITA_SOFT_COLD].wakeup = true;
+			}
+			if (chip->batt_irq[JEITA_SOFT_HOT].disabled) {
+				enable_irq(
+					chip->batt_irq[JEITA_SOFT_HOT].irq);
+				chip->batt_irq[JEITA_SOFT_HOT].disabled = false;
+			}
+			if (!chip->batt_irq[JEITA_SOFT_HOT].wakeup) {
+				enable_irq_wake(
+					chip->batt_irq[JEITA_SOFT_HOT].irq);
+				chip->batt_irq[JEITA_SOFT_HOT].wakeup = true;
+			}
+		} else {
+			if (chip->batt_irq[JEITA_SOFT_COLD].wakeup) {
+				disable_irq_wake(
+					chip->batt_irq[JEITA_SOFT_COLD].irq);
+				chip->batt_irq[JEITA_SOFT_COLD].wakeup = false;
+			}
+			if (!chip->batt_irq[JEITA_SOFT_COLD].disabled) {
+				disable_irq_nosync(
+					chip->batt_irq[JEITA_SOFT_COLD].irq);
+				chip->batt_irq[JEITA_SOFT_COLD].disabled = true;
+			}
+			if (chip->batt_irq[JEITA_SOFT_HOT].wakeup) {
+				disable_irq_wake(
+					chip->batt_irq[JEITA_SOFT_HOT].irq);
+				chip->batt_irq[JEITA_SOFT_HOT].wakeup = false;
+			}
+			if (!chip->batt_irq[JEITA_SOFT_HOT].disabled) {
+				disable_irq_nosync(
+					chip->batt_irq[JEITA_SOFT_HOT].irq);
+				chip->batt_irq[JEITA_SOFT_HOT].disabled = true;
+			}
+		}
+		break;
 	case POWER_SUPPLY_PROP_STATUS:
 		chip->prev_status = chip->status;
 		chip->status = val->intval;
@@ -3599,6 +4791,12 @@
 			schedule_work(&chip->bcl_hi_power_work);
 		}
 		break;
+	case POWER_SUPPLY_PROP_BATTERY_INFO:
+		rc = fg_set_battery_info(chip, val->intval);
+		break;
+	case POWER_SUPPLY_PROP_BATTERY_INFO_ID:
+		chip->batt_info_id = val->intval;
+		break;
 	default:
 		return -EINVAL;
 	};
@@ -3613,6 +4811,8 @@
 	case POWER_SUPPLY_PROP_COOL_TEMP:
 	case POWER_SUPPLY_PROP_WARM_TEMP:
 	case POWER_SUPPLY_PROP_CYCLE_COUNT_ID:
+	case POWER_SUPPLY_PROP_BATTERY_INFO:
+	case POWER_SUPPLY_PROP_BATTERY_INFO_ID:
 		return 1;
 	default:
 		break;
@@ -3807,21 +5007,197 @@
 	fg_relax(&chip->gain_comp_wakeup_source);
 }
 
-#define BATT_MISSING_STS BIT(6)
-static bool is_battery_missing(struct fg_chip *chip)
+static void cc_soc_store_work(struct work_struct *work)
+{
+	struct fg_chip *chip = container_of(work, struct fg_chip,
+					cc_soc_store_work);
+	int cc_soc_pct;
+
+	if (!chip->nom_cap_uah) {
+		pr_err("nom_cap_uah zero!\n");
+		fg_relax(&chip->cc_soc_wakeup_source);
+		return;
+	}
+
+	cc_soc_pct = get_sram_prop_now(chip, FG_DATA_CC_CHARGE);
+	cc_soc_pct = div64_s64(cc_soc_pct * 100,
+				chip->nom_cap_uah);
+	chip->last_cc_soc = div64_s64((int64_t)chip->last_soc *
+				FULL_PERCENT_28BIT, FULL_SOC_RAW);
+
+	if (fg_debug_mask & FG_STATUS)
+		pr_info("cc_soc_pct: %d last_cc_soc: %lld\n", cc_soc_pct,
+			chip->last_cc_soc);
+
+	if (fg_reset_on_lockup && (chip->cc_soc_limit_pct > 0 &&
+			cc_soc_pct >= chip->cc_soc_limit_pct)) {
+		pr_err("CC_SOC out of range\n");
+		fg_check_ima_error_handling(chip);
+	}
+
+	fg_relax(&chip->cc_soc_wakeup_source);
+}
+
+#define HARD_JEITA_ALARM_CHECK_NS	10000000000
+static enum alarmtimer_restart fg_hard_jeita_alarm_cb(struct alarm *alarm,
+						ktime_t now)
+{
+	struct fg_chip *chip = container_of(alarm,
+			struct fg_chip, hard_jeita_alarm);
+	int rc, health = POWER_SUPPLY_HEALTH_UNKNOWN;
+	u8 regval;
+	bool batt_hot, batt_cold;
+	union power_supply_propval val = {0, };
+
+	if (!is_usb_present(chip)) {
+		pr_debug("USB plugged out, stop the timer!\n");
+		return ALARMTIMER_NORESTART;
+	}
+
+	rc = fg_read(chip, &regval, BATT_INFO_STS(chip->batt_base), 1);
+	if (rc) {
+		pr_err("read batt_sts failed, rc=%d\n", rc);
+		goto recheck;
+	}
+
+	batt_hot = !!(regval & JEITA_HARD_HOT_RT_STS);
+	batt_cold = !!(regval & JEITA_HARD_COLD_RT_STS);
+	if (batt_hot && batt_cold) {
+		pr_debug("Hot && cold can't co-exist\n");
+		goto recheck;
+	}
+
+	if ((batt_hot == chip->batt_hot) && (batt_cold == chip->batt_cold)) {
+		pr_debug("battery JEITA state not changed, ignore\n");
+		goto recheck;
+	}
+
+	if (batt_cold != chip->batt_cold) {
+		/* cool --> cold */
+		if (chip->batt_cool) {
+			chip->batt_cool = false;
+			chip->batt_cold = true;
+			health = POWER_SUPPLY_HEALTH_COLD;
+		} else if (chip->batt_cold) { /* cold --> cool */
+			chip->batt_cool = true;
+			chip->batt_cold = false;
+			health = POWER_SUPPLY_HEALTH_COOL;
+		}
+	}
+
+	if (batt_hot != chip->batt_hot) {
+		/* warm --> hot */
+		if (chip->batt_warm) {
+			chip->batt_warm = false;
+			chip->batt_hot = true;
+			health = POWER_SUPPLY_HEALTH_OVERHEAT;
+		} else if (chip->batt_hot) { /* hot --> warm */
+			chip->batt_hot = false;
+			chip->batt_warm = true;
+			health = POWER_SUPPLY_HEALTH_WARM;
+		}
+	}
+
+	if (health != POWER_SUPPLY_HEALTH_UNKNOWN) {
+		pr_debug("FG report battery health: %d\n", health);
+		val.intval = health;
+		rc = power_supply_set_property(chip->batt_psy,
+				POWER_SUPPLY_PROP_HEALTH, &val);
+		if (rc)
+			pr_err("Set batt_psy health: %d failed\n", health);
+	}
+
+recheck:
+	alarm_forward_now(alarm, ns_to_ktime(HARD_JEITA_ALARM_CHECK_NS));
+	return ALARMTIMER_RESTART;
+}
+
+#define BATT_SOFT_COLD_STS	BIT(0)
+#define BATT_SOFT_HOT_STS	BIT(1)
+static irqreturn_t fg_jeita_soft_hot_irq_handler(int irq, void *_chip)
 {
 	int rc;
-	u8 fg_batt_sts;
+	struct fg_chip *chip = _chip;
+	u8 regval;
+	bool batt_warm;
+	union power_supply_propval val = {0, };
 
-	rc = fg_read(chip, &fg_batt_sts,
-				 INT_RT_STS(chip->batt_base), 1);
+	if (!is_charger_available(chip))
+		return IRQ_HANDLED;
+
+	rc = fg_read(chip, &regval, INT_RT_STS(chip->batt_base), 1);
 	if (rc) {
 		pr_err("spmi read failed: addr=%03X, rc=%d\n",
 				INT_RT_STS(chip->batt_base), rc);
-		return false;
+		return IRQ_HANDLED;
 	}
 
-	return (fg_batt_sts & BATT_MISSING_STS) ? true : false;
+	batt_warm = !!(regval & BATT_SOFT_HOT_STS);
+	if (chip->batt_warm == batt_warm) {
+		pr_debug("warm state not change, ignore!\n");
+		return IRQ_HANDLED;
+	}
+
+	chip->batt_warm = batt_warm;
+	if (batt_warm) {
+		val.intval = POWER_SUPPLY_HEALTH_WARM;
+		power_supply_set_property(chip->batt_psy,
+			POWER_SUPPLY_PROP_HEALTH, &val);
+		/* kick the alarm timer for hard hot polling */
+		alarm_start_relative(&chip->hard_jeita_alarm,
+				ns_to_ktime(HARD_JEITA_ALARM_CHECK_NS));
+	} else {
+		val.intval = POWER_SUPPLY_HEALTH_GOOD;
+		 power_supply_set_property(chip->batt_psy,
+			POWER_SUPPLY_PROP_HEALTH, &val);
+		/* cancel the alarm timer */
+		alarm_try_to_cancel(&chip->hard_jeita_alarm);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t fg_jeita_soft_cold_irq_handler(int irq, void *_chip)
+{
+	int rc;
+	struct fg_chip *chip = _chip;
+	u8 regval;
+	bool batt_cool;
+	union power_supply_propval val = {0, };
+
+	if (!is_charger_available(chip))
+		return IRQ_HANDLED;
+
+	rc = fg_read(chip, &regval, INT_RT_STS(chip->batt_base), 1);
+	if (rc) {
+		pr_err("spmi read failed: addr=%03X, rc=%d\n",
+				INT_RT_STS(chip->batt_base), rc);
+		return IRQ_HANDLED;
+	}
+
+	batt_cool = !!(regval & BATT_SOFT_COLD_STS);
+	if (chip->batt_cool == batt_cool) {
+		pr_debug("cool state not change, ignore\n");
+		return IRQ_HANDLED;
+	}
+
+	chip->batt_cool = batt_cool;
+	if (batt_cool) {
+		val.intval = POWER_SUPPLY_HEALTH_COOL;
+		power_supply_set_property(chip->batt_psy,
+			POWER_SUPPLY_PROP_HEALTH, &val);
+		/* kick the alarm timer for hard cold polling */
+		alarm_start_relative(&chip->hard_jeita_alarm,
+				ns_to_ktime(HARD_JEITA_ALARM_CHECK_NS));
+	} else {
+		val.intval = POWER_SUPPLY_HEALTH_GOOD;
+		power_supply_set_property(chip->batt_psy,
+			POWER_SUPPLY_PROP_HEALTH, &val);
+		/* cancel the alarm timer */
+		alarm_try_to_cancel(&chip->hard_jeita_alarm);
+	}
+
+	return IRQ_HANDLED;
 }
 
 #define SOC_FIRST_EST_DONE	BIT(5)
@@ -3841,21 +5217,40 @@
 	return (fg_soc_sts & SOC_FIRST_EST_DONE) ? true : false;
 }
 
+#define FG_EMPTY_DEBOUNCE_MS	1500
 static irqreturn_t fg_vbatt_low_handler(int irq, void *_chip)
 {
 	struct fg_chip *chip = _chip;
-	int rc;
 	bool vbatt_low_sts;
 
 	if (fg_debug_mask & FG_IRQS)
 		pr_info("vbatt-low triggered\n");
 
-	if (chip->status == POWER_SUPPLY_STATUS_CHARGING) {
-		rc = fg_get_vbatt_status(chip, &vbatt_low_sts);
-		if (rc) {
-			pr_err("error in reading vbatt_status, rc:%d\n", rc);
+	/* handle empty soc based on vbatt-low interrupt */
+	if (chip->use_vbat_low_empty_soc) {
+		if (fg_get_vbatt_status(chip, &vbatt_low_sts))
 			goto out;
+
+		if (vbatt_low_sts) {
+			if (fg_debug_mask & FG_IRQS)
+				pr_info("Vbatt is low\n");
+			disable_irq_wake(chip->batt_irq[VBATT_LOW].irq);
+			disable_irq_nosync(chip->batt_irq[VBATT_LOW].irq);
+			chip->vbat_low_irq_enabled = false;
+			fg_stay_awake(&chip->empty_check_wakeup_source);
+			schedule_delayed_work(&chip->check_empty_work,
+				msecs_to_jiffies(FG_EMPTY_DEBOUNCE_MS));
+		} else {
+			if (fg_debug_mask & FG_IRQS)
+				pr_info("Vbatt is high\n");
+			chip->soc_empty = false;
 		}
+		goto out;
+	}
+
+	if (chip->status == POWER_SUPPLY_STATUS_CHARGING) {
+		if (fg_get_vbatt_status(chip, &vbatt_low_sts))
+			goto out;
 		if (!vbatt_low_sts && chip->vbat_low_irq_enabled) {
 			if (fg_debug_mask & FG_IRQS)
 				pr_info("disabling vbatt_low irq\n");
@@ -3876,8 +5271,10 @@
 	bool batt_missing = is_battery_missing(chip);
 
 	if (batt_missing) {
+		fg_cap_learning_stop(chip);
 		chip->battery_missing = true;
 		chip->profile_loaded = false;
+		chip->soc_reporting_ready = false;
 		chip->batt_type = default_batt_type;
 		mutex_lock(&chip->cyc_ctr.lock);
 		if (fg_debug_mask & FG_IRQS)
@@ -3885,17 +5282,10 @@
 		clear_cycle_counter(chip);
 		mutex_unlock(&chip->cyc_ctr.lock);
 	} else {
-		if (!chip->use_otp_profile) {
-			reinit_completion(&chip->batt_id_avail);
-			reinit_completion(&chip->first_soc_done);
-			schedule_delayed_work(&chip->batt_profile_init, 0);
-			cancel_delayed_work(&chip->update_sram_data);
-			schedule_delayed_work(
-				&chip->update_sram_data,
-				msecs_to_jiffies(0));
-		} else {
+		if (!chip->use_otp_profile)
+			fg_handle_battery_insertion(chip);
+		else
 			chip->battery_missing = false;
-		}
 	}
 
 	if (fg_debug_mask & FG_IRQS)
@@ -3943,7 +5333,7 @@
 {
 	struct fg_chip *chip = _chip;
 	u8 soc_rt_sts;
-	int rc;
+	int rc, msoc;
 
 	rc = fg_read(chip, &soc_rt_sts, INT_RT_STS(chip->soc_base), 1);
 	if (rc) {
@@ -3954,6 +5344,37 @@
 	if (fg_debug_mask & FG_IRQS)
 		pr_info("triggered 0x%x\n", soc_rt_sts);
 
+	if (chip->dischg_gain.enable) {
+		fg_stay_awake(&chip->dischg_gain_wakeup_source);
+		schedule_work(&chip->dischg_gain_work);
+	}
+
+	if (chip->soc_slope_limiter_en) {
+		fg_stay_awake(&chip->slope_limit_wakeup_source);
+		schedule_work(&chip->slope_limiter_work);
+	}
+
+	/* Backup last soc every delta soc interrupt */
+	chip->use_last_soc = false;
+	if (fg_reset_on_lockup) {
+		if (!chip->ima_error_handling)
+			chip->last_soc = get_monotonic_soc_raw(chip);
+		if (fg_debug_mask & FG_STATUS)
+			pr_info("last_soc: %d\n", chip->last_soc);
+
+		fg_stay_awake(&chip->cc_soc_wakeup_source);
+		schedule_work(&chip->cc_soc_store_work);
+	}
+
+	if (chip->use_vbat_low_empty_soc) {
+		msoc = get_monotonic_soc_raw(chip);
+		if (msoc == 0 || chip->soc_empty) {
+			fg_stay_awake(&chip->empty_check_wakeup_source);
+			schedule_delayed_work(&chip->check_empty_work,
+				msecs_to_jiffies(FG_EMPTY_DEBOUNCE_MS));
+		}
+	}
+
 	schedule_work(&chip->battery_age_work);
 
 	if (chip->power_supply_registered)
@@ -3988,7 +5409,6 @@
 	return IRQ_HANDLED;
 }
 
-#define FG_EMPTY_DEBOUNCE_MS	1500
 static irqreturn_t fg_empty_soc_irq_handler(int irq, void *_chip)
 {
 	struct fg_chip *chip = _chip;
@@ -4100,16 +5520,15 @@
 	fg_relax(&chip->resume_soc_wakeup_source);
 }
 
-
 #define OCV_COEFFS_START_REG		0x4C0
 #define OCV_JUNCTION_REG		0x4D8
-#define NOM_CAP_REG			0x4F4
 #define CUTOFF_VOLTAGE_REG		0x40C
 #define RSLOW_CFG_REG			0x538
 #define RSLOW_CFG_OFFSET		2
 #define RSLOW_THRESH_REG		0x52C
 #define RSLOW_THRESH_OFFSET		0
-#define TEMP_RS_TO_RSLOW_OFFSET		2
+#define RS_TO_RSLOW_CHG_OFFSET		2
+#define RS_TO_RSLOW_DISCHG_OFFSET	0
 #define RSLOW_COMP_REG			0x528
 #define RSLOW_COMP_C1_OFFSET		0
 #define RSLOW_COMP_C2_OFFSET		2
@@ -4117,7 +5536,6 @@
 {
 	u8 buffer[24];
 	int rc, i;
-	int16_t cc_mah;
 
 	fg_mem_lock(chip);
 	rc = fg_mem_read(chip, buffer, OCV_COEFFS_START_REG, 24, 0, 0);
@@ -4138,30 +5556,21 @@
 				chip->ocv_coeffs[8], chip->ocv_coeffs[9],
 				chip->ocv_coeffs[10], chip->ocv_coeffs[11]);
 	}
-	rc = fg_mem_read(chip, buffer, OCV_JUNCTION_REG, 1, 0, 0);
-	chip->ocv_junction_p1p2 = buffer[0] * 100 / 255;
-	rc |= fg_mem_read(chip, buffer, OCV_JUNCTION_REG, 1, 1, 0);
-	chip->ocv_junction_p2p3 = buffer[0] * 100 / 255;
+	rc = fg_mem_read(chip, buffer, OCV_JUNCTION_REG, 2, 0, 0);
 	if (rc) {
 		pr_err("Failed to read ocv junctions: %d\n", rc);
 		goto done;
 	}
-	rc = fg_mem_read(chip, buffer, NOM_CAP_REG, 2, 0, 0);
+
+	chip->ocv_junction_p1p2 = buffer[0] * 100 / 255;
+	chip->ocv_junction_p2p3 = buffer[1] * 100 / 255;
+
+	rc = load_battery_aging_data(chip);
 	if (rc) {
-		pr_err("Failed to read nominal capacitance: %d\n", rc);
+		pr_err("Failed to load battery aging data, rc:%d\n", rc);
 		goto done;
 	}
-	chip->nom_cap_uah = bcap_uah_2b(buffer);
-	chip->actual_cap_uah = chip->nom_cap_uah;
-	if (chip->learning_data.learned_cc_uah == 0) {
-		chip->learning_data.learned_cc_uah = chip->nom_cap_uah;
-		fg_cap_learning_save_data(chip);
-	} else if (chip->learning_data.feedback_on) {
-		cc_mah = div64_s64(chip->learning_data.learned_cc_uah, 1000);
-		rc = fg_calc_and_store_cc_soc_coeff(chip, cc_mah);
-		if (rc)
-			pr_err("Error in restoring cc_soc_coeff, rc:%d\n", rc);
-	}
+
 	rc = fg_mem_read(chip, buffer, CUTOFF_VOLTAGE_REG, 2, 0, 0);
 	if (rc) {
 		pr_err("Failed to read cutoff voltage: %d\n", rc);
@@ -4188,9 +5597,9 @@
 	}
 	chip->rslow_comp.rslow_thr = buffer[0];
 	rc = fg_mem_read(chip, buffer, TEMP_RS_TO_RSLOW_REG, 2,
-			RSLOW_THRESH_OFFSET, 0);
+			RS_TO_RSLOW_CHG_OFFSET, 0);
 	if (rc) {
-		pr_err("unable to read rs to rslow: %d\n", rc);
+		pr_err("unable to read rs to rslow_chg: %d\n", rc);
 		goto done;
 	}
 	memcpy(chip->rslow_comp.rs_to_rslow, buffer, 2);
@@ -4207,6 +5616,68 @@
 	return rc;
 }
 
+static int fg_update_batt_rslow_settings(struct fg_chip *chip)
+{
+	int64_t rs_to_rslow_chg, rs_to_rslow_dischg, batt_esr, rconn_uohm;
+	u8 buffer[2];
+	int rc;
+
+	rc = fg_mem_read(chip, buffer, BATTERY_ESR_REG, 2, ESR_OFFSET, 0);
+	if (rc) {
+		pr_err("unable to read battery_esr: %d\n", rc);
+		goto done;
+	}
+	batt_esr = half_float(buffer);
+
+	rc = fg_mem_read(chip, buffer, TEMP_RS_TO_RSLOW_REG, 2,
+			RS_TO_RSLOW_DISCHG_OFFSET, 0);
+	if (rc) {
+		pr_err("unable to read rs to rslow dischg: %d\n", rc);
+		goto done;
+	}
+	rs_to_rslow_dischg = half_float(buffer);
+
+	rc = fg_mem_read(chip, buffer, TEMP_RS_TO_RSLOW_REG, 2,
+			RS_TO_RSLOW_CHG_OFFSET, 0);
+	if (rc) {
+		pr_err("unable to read rs to rslow chg: %d\n", rc);
+		goto done;
+	}
+	rs_to_rslow_chg = half_float(buffer);
+
+	if (fg_debug_mask & FG_STATUS)
+		pr_info("rs_rslow_chg: %lld, rs_rslow_dischg: %lld, esr: %lld\n",
+			rs_to_rslow_chg, rs_to_rslow_dischg, batt_esr);
+
+	rconn_uohm = chip->rconn_mohm * 1000;
+	rs_to_rslow_dischg = div64_s64(rs_to_rslow_dischg * batt_esr,
+					batt_esr + rconn_uohm);
+	rs_to_rslow_chg = div64_s64(rs_to_rslow_chg * batt_esr,
+					batt_esr + rconn_uohm);
+
+	half_float_to_buffer(rs_to_rslow_chg, buffer);
+	rc = fg_mem_write(chip, buffer, TEMP_RS_TO_RSLOW_REG, 2,
+			RS_TO_RSLOW_CHG_OFFSET, 0);
+	if (rc) {
+		pr_err("unable to write rs_to_rslow_chg: %d\n", rc);
+		goto done;
+	}
+
+	half_float_to_buffer(rs_to_rslow_dischg, buffer);
+	rc = fg_mem_write(chip, buffer, TEMP_RS_TO_RSLOW_REG, 2,
+			RS_TO_RSLOW_DISCHG_OFFSET, 0);
+	if (rc) {
+		pr_err("unable to write rs_to_rslow_dischg: %d\n", rc);
+		goto done;
+	}
+
+	if (fg_debug_mask & FG_STATUS)
+		pr_info("Modified rs_rslow_chg: %lld, rs_rslow_dischg: %lld\n",
+			rs_to_rslow_chg, rs_to_rslow_dischg);
+done:
+	return rc;
+}
+
 #define RSLOW_CFG_MASK		(BIT(2) | BIT(3) | BIT(4) | BIT(5))
 #define RSLOW_CFG_ON_VAL	(BIT(2) | BIT(3))
 #define RSLOW_THRESH_FULL_VAL	0xFF
@@ -4233,7 +5704,7 @@
 
 	half_float_to_buffer(chip->rslow_comp.chg_rs_to_rslow, buffer);
 	rc = fg_mem_write(chip, buffer,
-			TEMP_RS_TO_RSLOW_REG, 2, TEMP_RS_TO_RSLOW_OFFSET, 0);
+			TEMP_RS_TO_RSLOW_REG, 2, RS_TO_RSLOW_CHG_OFFSET, 0);
 	if (rc) {
 		pr_err("unable to write rs to rslow: %d\n", rc);
 		goto done;
@@ -4286,7 +5757,7 @@
 	}
 
 	rc = fg_mem_write(chip, chip->rslow_comp.rs_to_rslow,
-			TEMP_RS_TO_RSLOW_REG, 2, TEMP_RS_TO_RSLOW_OFFSET, 0);
+			TEMP_RS_TO_RSLOW_REG, 2, RS_TO_RSLOW_CHG_OFFSET, 0);
 	if (rc) {
 		pr_err("unable to write rs to rslow: %d\n", rc);
 		goto done;
@@ -4510,6 +5981,58 @@
 	fg_relax(&chip->esr_extract_wakeup_source);
 }
 
+#define KI_COEFF_MEDC_REG		0x400
+#define KI_COEFF_MEDC_OFFSET		0
+#define KI_COEFF_HIGHC_REG		0x404
+#define KI_COEFF_HIGHC_OFFSET		0
+#define DEFAULT_MEDC_VOLTAGE_GAIN	3
+#define DEFAULT_HIGHC_VOLTAGE_GAIN	2
+static void discharge_gain_work(struct work_struct *work)
+{
+	struct fg_chip *chip = container_of(work, struct fg_chip,
+						dischg_gain_work);
+	u8 buf[2];
+	int capacity, rc, i;
+	int64_t medc_val = DEFAULT_MEDC_VOLTAGE_GAIN;
+	int64_t highc_val = DEFAULT_HIGHC_VOLTAGE_GAIN;
+
+	capacity = get_prop_capacity(chip);
+	if (chip->status == POWER_SUPPLY_STATUS_DISCHARGING) {
+		for (i = VOLT_GAIN_MAX - 1; i >= 0; i--) {
+			if (capacity <= chip->dischg_gain.soc[i]) {
+				medc_val = chip->dischg_gain.medc_gain[i];
+				highc_val = chip->dischg_gain.highc_gain[i];
+			}
+		}
+	}
+
+	if (fg_debug_mask & FG_STATUS)
+		pr_info("Capacity: %d, medc_gain: %lld highc_gain: %lld\n",
+			capacity, medc_val, highc_val);
+
+	medc_val *= MICRO_UNIT;
+	half_float_to_buffer(medc_val, buf);
+	rc = fg_mem_write(chip, buf, KI_COEFF_MEDC_REG, 2,
+				KI_COEFF_MEDC_OFFSET, 0);
+	if (rc)
+		pr_err("Couldn't write to ki_coeff_medc_reg, rc=%d\n", rc);
+	else if (fg_debug_mask & FG_STATUS)
+		pr_info("Value [%x %x] written to ki_coeff_medc\n", buf[0],
+			buf[1]);
+
+	highc_val *= MICRO_UNIT;
+	half_float_to_buffer(highc_val, buf);
+	rc = fg_mem_write(chip, buf, KI_COEFF_HIGHC_REG, 2,
+				KI_COEFF_HIGHC_OFFSET, 0);
+	if (rc)
+		pr_err("Couldn't write to ki_coeff_highc_reg, rc=%d\n", rc);
+	else if (fg_debug_mask & FG_STATUS)
+		pr_info("Value [%x %x] written to ki_coeff_highc\n", buf[0],
+			buf[1]);
+
+	fg_relax(&chip->dischg_gain_wakeup_source);
+}
+
 #define LOW_LATENCY			BIT(6)
 #define BATT_PROFILE_OFFSET		0x4C0
 #define PROFILE_INTEGRITY_REG		0x53C
@@ -4529,7 +6052,7 @@
 		pr_info("restarting fuel gauge...\n");
 
 try_again:
-	if (write_profile) {
+	if (write_profile && !chip->ima_error_handling) {
 		if (!chip->charging_disabled) {
 			pr_err("Charging not yet disabled!\n");
 			return -EINVAL;
@@ -4770,7 +6293,8 @@
 #define BATTERY_PSY_WAIT_MS		2000
 static int fg_batt_profile_init(struct fg_chip *chip)
 {
-	int rc = 0, ret, len, batt_id;
+	int rc = 0, ret;
+	int len, batt_id;
 	struct device_node *node = chip->pdev->dev.of_node;
 	struct device_node *batt_node, *profile_node;
 	const char *data, *batt_type_str;
@@ -4792,6 +6316,19 @@
 		goto no_profile;
 	}
 
+	/* Check whether the charger is ready */
+	if (!is_charger_available(chip))
+		goto reschedule;
+
+	/* Disable charging for a FG cycle before calculating vbat_in_range */
+	if (!chip->charging_disabled) {
+		rc = set_prop_enable_charging(chip, false);
+		if (rc)
+			pr_err("Failed to disable charging, rc=%d\n", rc);
+
+		goto update;
+	}
+
 	batt_node = of_find_node_by_name(node, "qcom,battery-data");
 	if (!batt_node) {
 		pr_warn("No available batterydata, using OTP defaults\n");
@@ -4808,8 +6345,12 @@
 							fg_batt_type);
 	if (IS_ERR_OR_NULL(profile_node)) {
 		rc = PTR_ERR(profile_node);
-		pr_err("couldn't find profile handle %d\n", rc);
-		goto no_profile;
+		if (rc == -EPROBE_DEFER) {
+			goto reschedule;
+		} else {
+			pr_err("couldn't find profile handle rc=%d\n", rc);
+			goto no_profile;
+		}
 	}
 
 	/* read rslow compensation values if they're available */
@@ -4903,18 +6444,6 @@
 		goto no_profile;
 	}
 
-	/* Check whether the charger is ready */
-	if (!is_charger_available(chip))
-		goto reschedule;
-
-	/* Disable charging for a FG cycle before calculating vbat_in_range */
-	if (!chip->charging_disabled) {
-		rc = set_prop_enable_charging(chip, false);
-		if (rc)
-			pr_err("Failed to disable charging, rc=%d\n", rc);
-
-		goto reschedule;
-	}
 
 	vbat_in_range = get_vbat_est_diff(chip)
 			< settings[FG_MEM_VBAT_EST_DIFF].value * 1000;
@@ -4956,11 +6485,7 @@
 				chip->batt_profile, len, false);
 	}
 
-	if (chip->power_supply_registered)
-		power_supply_changed(chip->bms_psy);
-
 	memcpy(chip->batt_profile, data, len);
-
 	chip->batt_profile_len = len;
 
 	if (fg_debug_mask & FG_STATUS)
@@ -4995,6 +6520,11 @@
 		}
 	}
 
+	if (chip->rconn_mohm > 0) {
+		rc = fg_update_batt_rslow_settings(chip);
+		if (rc)
+			pr_err("Error in updating ESR, rc=%d\n", rc);
+	}
 done:
 	if (chip->charging_disabled) {
 		rc = set_prop_enable_charging(chip, true);
@@ -5008,8 +6538,22 @@
 		chip->batt_type = fg_batt_type;
 	else
 		chip->batt_type = batt_type_str;
+
+	if (chip->first_profile_loaded && fg_reset_on_lockup) {
+		if (fg_debug_mask & FG_STATUS)
+			pr_info("restoring SRAM registers\n");
+		rc = fg_backup_sram_registers(chip, false);
+		if (rc)
+			pr_err("Couldn't restore sram registers\n");
+
+		/* Read the cycle counter back from FG SRAM */
+		if (chip->cyc_ctr.en)
+			restore_cycle_counter(chip);
+	}
+
 	chip->first_profile_loaded = true;
 	chip->profile_loaded = true;
+	chip->soc_reporting_ready = true;
 	chip->battery_missing = is_battery_missing(chip);
 	update_chg_iterm(chip);
 	update_cc_cv_setpoint(chip);
@@ -5025,8 +6569,10 @@
 	fg_relax(&chip->profile_wakeup_source);
 	pr_info("Battery SOC: %d, V: %duV\n", get_prop_capacity(chip),
 		fg_data[FG_DATA_VOLTAGE].value);
+	complete_all(&chip->fg_reset_done);
 	return rc;
 no_profile:
+	chip->soc_reporting_ready = true;
 	if (chip->charging_disabled) {
 		rc = set_prop_enable_charging(chip, true);
 		if (rc)
@@ -5039,14 +6585,15 @@
 		power_supply_changed(chip->bms_psy);
 	fg_relax(&chip->profile_wakeup_source);
 	return rc;
-reschedule:
-	schedule_delayed_work(
-		&chip->batt_profile_init,
-		msecs_to_jiffies(BATTERY_PSY_WAIT_MS));
+update:
 	cancel_delayed_work(&chip->update_sram_data);
 	schedule_delayed_work(
 		&chip->update_sram_data,
 		msecs_to_jiffies(0));
+reschedule:
+	schedule_delayed_work(
+		&chip->batt_profile_init,
+		msecs_to_jiffies(BATTERY_PSY_WAIT_MS));
 	fg_relax(&chip->profile_wakeup_source);
 	return 0;
 }
@@ -5056,14 +6603,41 @@
 	struct fg_chip *chip = container_of(work,
 				struct fg_chip,
 				check_empty_work.work);
+	bool vbatt_low_sts;
+	int msoc;
 
-	if (fg_is_batt_empty(chip)) {
+	/* handle empty soc based on vbatt-low interrupt */
+	if (chip->use_vbat_low_empty_soc) {
+		if (fg_get_vbatt_status(chip, &vbatt_low_sts))
+			goto out;
+
+		msoc = get_monotonic_soc_raw(chip);
+
+		if (fg_debug_mask & FG_STATUS)
+			pr_info("Vbatt_low: %d, msoc: %d\n", vbatt_low_sts,
+				msoc);
+		if (vbatt_low_sts || (msoc == 0))
+			chip->soc_empty = true;
+		else
+			chip->soc_empty = false;
+
+		if (chip->power_supply_registered)
+			power_supply_changed(chip->bms_psy);
+
+		if (!chip->vbat_low_irq_enabled) {
+			enable_irq(chip->batt_irq[VBATT_LOW].irq);
+			enable_irq_wake(chip->batt_irq[VBATT_LOW].irq);
+			chip->vbat_low_irq_enabled = true;
+		}
+	} else if (fg_is_batt_empty(chip)) {
 		if (fg_debug_mask & FG_STATUS)
 			pr_info("EMPTY SOC high\n");
 		chip->soc_empty = true;
 		if (chip->power_supply_registered)
 			power_supply_changed(chip->bms_psy);
 	}
+
+out:
 	fg_relax(&chip->empty_check_wakeup_source);
 }
 
@@ -5103,7 +6677,7 @@
 	int rc;
 	u8 buffer[3];
 	int bsoc;
-	int resume_soc_raw = FULL_SOC_RAW - settings[FG_MEM_RESUME_SOC].value;
+	int resume_soc_raw = settings[FG_MEM_RESUME_SOC].value;
 	bool disable = false;
 	u8 reg;
 
@@ -5318,6 +6892,98 @@
 	}								\
 } while (0)
 
+static int fg_dischg_gain_dt_init(struct fg_chip *chip)
+{
+	struct device_node *node = chip->pdev->dev.of_node;
+	struct property *prop;
+	int i, rc = 0;
+	size_t size;
+
+	prop = of_find_property(node, "qcom,fg-dischg-voltage-gain-soc",
+			NULL);
+	if (!prop) {
+		pr_err("qcom-fg-dischg-voltage-gain-soc not specified\n");
+		goto out;
+	}
+
+	size = prop->length / sizeof(u32);
+	if (size != VOLT_GAIN_MAX) {
+		pr_err("Voltage gain SOC specified is of incorrect size\n");
+		goto out;
+	}
+
+	rc = of_property_read_u32_array(node,
+		"qcom,fg-dischg-voltage-gain-soc", chip->dischg_gain.soc, size);
+	if (rc < 0) {
+		pr_err("Reading qcom-fg-dischg-voltage-gain-soc failed, rc=%d\n",
+			rc);
+		goto out;
+	}
+
+	for (i = 0; i < VOLT_GAIN_MAX; i++) {
+		if (chip->dischg_gain.soc[i] > 100) {
+			pr_err("Incorrect dischg-voltage-gain-soc\n");
+			goto out;
+		}
+	}
+
+	prop = of_find_property(node, "qcom,fg-dischg-med-voltage-gain",
+			NULL);
+	if (!prop) {
+		pr_err("qcom-fg-dischg-med-voltage-gain not specified\n");
+		goto out;
+	}
+
+	size = prop->length / sizeof(u32);
+	if (size != VOLT_GAIN_MAX) {
+		pr_err("med-voltage-gain specified is of incorrect size\n");
+		goto out;
+	}
+
+	rc = of_property_read_u32_array(node,
+		"qcom,fg-dischg-med-voltage-gain", chip->dischg_gain.medc_gain,
+		size);
+	if (rc < 0) {
+		pr_err("Reading qcom-fg-dischg-med-voltage-gain failed, rc=%d\n",
+			rc);
+		goto out;
+	}
+
+	prop = of_find_property(node, "qcom,fg-dischg-high-voltage-gain",
+			NULL);
+	if (!prop) {
+		pr_err("qcom-fg-dischg-high-voltage-gain not specified\n");
+		goto out;
+	}
+
+	size = prop->length / sizeof(u32);
+	if (size != VOLT_GAIN_MAX) {
+		pr_err("high-voltage-gain specified is of incorrect size\n");
+		goto out;
+	}
+
+	rc = of_property_read_u32_array(node,
+		"qcom,fg-dischg-high-voltage-gain",
+		chip->dischg_gain.highc_gain, size);
+	if (rc < 0) {
+		pr_err("Reading qcom-fg-dischg-high-voltage-gain failed, rc=%d\n",
+			rc);
+		goto out;
+	}
+
+	if (fg_debug_mask & FG_STATUS) {
+		for (i = 0; i < VOLT_GAIN_MAX; i++)
+			pr_info("SOC:%d MedC_Gain:%d HighC_Gain: %d\n",
+				chip->dischg_gain.soc[i],
+				chip->dischg_gain.medc_gain[i],
+				chip->dischg_gain.highc_gain[i]);
+	}
+	return 0;
+out:
+	chip->dischg_gain.enable = false;
+	return rc;
+}
+
 #define DEFAULT_EVALUATION_CURRENT_MA	1000
 static int fg_of_init(struct fg_chip *chip)
 {
@@ -5395,6 +7061,10 @@
 			"cl-max-start-capacity", rc, 15);
 	OF_READ_PROPERTY(chip->learning_data.vbat_est_thr_uv,
 			"cl-vbat-est-thr-uv", rc, 40000);
+	OF_READ_PROPERTY(chip->learning_data.max_cap_limit,
+			"cl-max-limit-deciperc", rc, 0);
+	OF_READ_PROPERTY(chip->learning_data.min_cap_limit,
+			"cl-min-limit-deciperc", rc, 0);
 	OF_READ_PROPERTY(chip->evaluation_current,
 			"aging-eval-current-ma", rc,
 			DEFAULT_EVALUATION_CURRENT_MA);
@@ -5455,6 +7125,77 @@
 	chip->esr_pulse_tune_en = of_property_read_bool(node,
 					"qcom,esr-pulse-tuning-en");
 
+	chip->soc_slope_limiter_en = of_property_read_bool(node,
+					"qcom,fg-control-slope-limiter");
+	if (chip->soc_slope_limiter_en) {
+		OF_READ_PROPERTY(chip->slope_limit_temp,
+			"fg-slope-limit-temp-threshold", rc,
+			SLOPE_LIMIT_TEMP_THRESHOLD);
+
+		OF_READ_PROPERTY(chip->slope_limit_coeffs[LOW_TEMP_CHARGE],
+			"fg-slope-limit-low-temp-chg", rc,
+			SLOPE_LIMIT_LOW_TEMP_CHG);
+
+		OF_READ_PROPERTY(chip->slope_limit_coeffs[HIGH_TEMP_CHARGE],
+			"fg-slope-limit-high-temp-chg", rc,
+			SLOPE_LIMIT_HIGH_TEMP_CHG);
+
+		OF_READ_PROPERTY(chip->slope_limit_coeffs[LOW_TEMP_DISCHARGE],
+			"fg-slope-limit-low-temp-dischg", rc,
+			SLOPE_LIMIT_LOW_TEMP_DISCHG);
+
+		OF_READ_PROPERTY(chip->slope_limit_coeffs[HIGH_TEMP_DISCHARGE],
+			"fg-slope-limit-high-temp-dischg", rc,
+			SLOPE_LIMIT_HIGH_TEMP_DISCHG);
+
+		if (fg_debug_mask & FG_STATUS)
+			pr_info("slope-limiter, temp: %d coeffs: [%d %d %d %d]\n",
+				chip->slope_limit_temp,
+				chip->slope_limit_coeffs[LOW_TEMP_CHARGE],
+				chip->slope_limit_coeffs[HIGH_TEMP_CHARGE],
+				chip->slope_limit_coeffs[LOW_TEMP_DISCHARGE],
+				chip->slope_limit_coeffs[HIGH_TEMP_DISCHARGE]);
+	}
+
+	OF_READ_PROPERTY(chip->rconn_mohm, "fg-rconn-mohm", rc, 0);
+
+	chip->dischg_gain.enable = of_property_read_bool(node,
+					"qcom,fg-dischg-voltage-gain-ctrl");
+	if (chip->dischg_gain.enable) {
+		rc = fg_dischg_gain_dt_init(chip);
+		if (rc) {
+			pr_err("Error in reading dischg_gain parameters, rc=%d\n",
+				rc);
+			rc = 0;
+		}
+	}
+
+	chip->use_vbat_low_empty_soc = of_property_read_bool(node,
+					"qcom,fg-use-vbat-low-empty-soc");
+
+	OF_READ_PROPERTY(chip->batt_temp_low_limit,
+			"fg-batt-temp-low-limit", rc, BATT_TEMP_LOW_LIMIT);
+
+	OF_READ_PROPERTY(chip->batt_temp_high_limit,
+			"fg-batt-temp-high-limit", rc, BATT_TEMP_HIGH_LIMIT);
+
+	if (fg_debug_mask & FG_STATUS)
+		pr_info("batt-temp-low_limit: %d batt-temp-high_limit: %d\n",
+			chip->batt_temp_low_limit, chip->batt_temp_high_limit);
+
+	OF_READ_PROPERTY(chip->cc_soc_limit_pct, "fg-cc-soc-limit-pct", rc, 0);
+
+	if (fg_debug_mask & FG_STATUS)
+		pr_info("cc-soc-limit-pct: %d\n", chip->cc_soc_limit_pct);
+
+	chip->batt_info_restore = of_property_read_bool(node,
+					"qcom,fg-restore-batt-info");
+
+	if (fg_debug_mask & FG_STATUS)
+		pr_info("restore: %d validate_by_ocv: %d range_pct: %d\n",
+			chip->batt_info_restore, fg_batt_valid_ocv,
+			fg_batt_range_pct);
+
 	return rc;
 }
 
@@ -5528,15 +7269,22 @@
 					chip->soc_irq[FULL_SOC].irq, rc);
 				return rc;
 			}
-			rc = devm_request_irq(chip->dev,
-				chip->soc_irq[EMPTY_SOC].irq,
-				fg_empty_soc_irq_handler,
-				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
-				"empty-soc", chip);
-			if (rc < 0) {
-				pr_err("Can't request %d empty-soc: %d\n",
-					chip->soc_irq[EMPTY_SOC].irq, rc);
-				return rc;
+			enable_irq_wake(chip->soc_irq[FULL_SOC].irq);
+			chip->full_soc_irq_enabled = true;
+
+			if (!chip->use_vbat_low_empty_soc) {
+				rc = devm_request_irq(chip->dev,
+					chip->soc_irq[EMPTY_SOC].irq,
+					fg_empty_soc_irq_handler,
+					IRQF_TRIGGER_RISING |
+					IRQF_TRIGGER_FALLING,
+					"empty-soc", chip);
+				if (rc < 0) {
+					pr_err("Can't request %d empty-soc: %d\n",
+						chip->soc_irq[EMPTY_SOC].irq,
+						rc);
+					return rc;
+				}
 			}
 			rc = devm_request_irq(chip->dev,
 				chip->soc_irq[DELTA_SOC].irq,
@@ -5558,8 +7306,8 @@
 			}
 
 			enable_irq_wake(chip->soc_irq[DELTA_SOC].irq);
-			enable_irq_wake(chip->soc_irq[FULL_SOC].irq);
-			enable_irq_wake(chip->soc_irq[EMPTY_SOC].irq);
+			if (!chip->use_vbat_low_empty_soc)
+				enable_irq_wake(chip->soc_irq[EMPTY_SOC].irq);
 			break;
 		case FG_MEMIF:
 			chip->mem_irq[FG_MEM_AVAIL].irq
@@ -5581,8 +7329,53 @@
 			}
 			break;
 		case FG_BATT:
-			chip->batt_irq[BATT_MISSING].irq
-				= of_irq_get_byname(child, "batt-missing");
+			chip->batt_irq[JEITA_SOFT_COLD].irq =
+				of_irq_get_byname(child, "soft-cold");
+			if (chip->batt_irq[JEITA_SOFT_COLD].irq < 0) {
+				pr_err("Unable to get soft-cold irq\n");
+				rc = -EINVAL;
+				return rc;
+			}
+			rc = devm_request_threaded_irq(chip->dev,
+					chip->batt_irq[JEITA_SOFT_COLD].irq,
+					NULL,
+					fg_jeita_soft_cold_irq_handler,
+					IRQF_TRIGGER_RISING |
+					IRQF_TRIGGER_FALLING |
+					IRQF_ONESHOT,
+					"soft-cold", chip);
+			if (rc < 0) {
+				pr_err("Can't request %d soft-cold: %d\n",
+					chip->batt_irq[JEITA_SOFT_COLD].irq,
+								rc);
+				return rc;
+			}
+			disable_irq(chip->batt_irq[JEITA_SOFT_COLD].irq);
+			chip->batt_irq[JEITA_SOFT_COLD].disabled = true;
+			chip->batt_irq[JEITA_SOFT_HOT].irq =
+				of_irq_get_byname(child, "soft-hot");
+			if (chip->batt_irq[JEITA_SOFT_HOT].irq < 0) {
+				pr_err("Unable to get soft-hot irq\n");
+				rc = -EINVAL;
+				return rc;
+			}
+			rc = devm_request_threaded_irq(chip->dev,
+					chip->batt_irq[JEITA_SOFT_HOT].irq,
+					NULL,
+					fg_jeita_soft_hot_irq_handler,
+					IRQF_TRIGGER_RISING |
+					IRQF_TRIGGER_FALLING |
+					IRQF_ONESHOT,
+					"soft-hot", chip);
+			if (rc < 0) {
+				pr_err("Can't request %d soft-hot: %d\n",
+					chip->batt_irq[JEITA_SOFT_HOT].irq, rc);
+				return rc;
+			}
+			disable_irq(chip->batt_irq[JEITA_SOFT_HOT].irq);
+			chip->batt_irq[JEITA_SOFT_HOT].disabled = true;
+			chip->batt_irq[BATT_MISSING].irq =
+				of_irq_get_byname(child, "batt-missing");
 			if (chip->batt_irq[BATT_MISSING].irq < 0) {
 				pr_err("Unable to get batt-missing irq\n");
 				rc = -EINVAL;
@@ -5619,8 +7412,14 @@
 					chip->batt_irq[VBATT_LOW].irq, rc);
 				return rc;
 			}
-			disable_irq_nosync(chip->batt_irq[VBATT_LOW].irq);
-			chip->vbat_low_irq_enabled = false;
+			if (chip->use_vbat_low_empty_soc) {
+				enable_irq_wake(chip->batt_irq[VBATT_LOW].irq);
+				chip->vbat_low_irq_enabled = true;
+			} else {
+				disable_irq_nosync(
+					chip->batt_irq[VBATT_LOW].irq);
+				chip->vbat_low_irq_enabled = false;
+			}
 			break;
 		case FG_ADC:
 			break;
@@ -5630,17 +7429,22 @@
 		}
 	}
 
+	chip->irqs_enabled = true;
 	return rc;
 }
 
-static void fg_cleanup(struct fg_chip *chip)
+static void fg_cancel_all_works(struct fg_chip *chip)
 {
+	cancel_delayed_work_sync(&chip->check_sanity_work);
 	cancel_delayed_work_sync(&chip->update_sram_data);
 	cancel_delayed_work_sync(&chip->update_temp_work);
 	cancel_delayed_work_sync(&chip->update_jeita_setting);
 	cancel_delayed_work_sync(&chip->check_empty_work);
 	cancel_delayed_work_sync(&chip->batt_profile_init);
 	alarm_try_to_cancel(&chip->fg_cap_learning_alarm);
+	alarm_try_to_cancel(&chip->hard_jeita_alarm);
+	if (!chip->ima_error_handling)
+		cancel_work_sync(&chip->ima_error_recovery_work);
 	cancel_work_sync(&chip->rslow_comp_work);
 	cancel_work_sync(&chip->set_resume_soc_work);
 	cancel_work_sync(&chip->fg_cap_learning_work);
@@ -5652,12 +7456,23 @@
 	cancel_work_sync(&chip->gain_comp_work);
 	cancel_work_sync(&chip->init_work);
 	cancel_work_sync(&chip->charge_full_work);
+	cancel_work_sync(&chip->bcl_hi_power_work);
 	cancel_work_sync(&chip->esr_extract_config_work);
+	cancel_work_sync(&chip->slope_limiter_work);
+	cancel_work_sync(&chip->dischg_gain_work);
+	cancel_work_sync(&chip->cc_soc_store_work);
+}
+
+static void fg_cleanup(struct fg_chip *chip)
+{
+	fg_cancel_all_works(chip);
+	power_supply_unregister(chip->bms_psy);
 	mutex_destroy(&chip->rslow_comp.lock);
 	mutex_destroy(&chip->rw_lock);
 	mutex_destroy(&chip->cyc_ctr.lock);
 	mutex_destroy(&chip->learning_data.learning_lock);
 	mutex_destroy(&chip->sysfs_restart_lock);
+	mutex_destroy(&chip->ima_recovery_lock);
 	wakeup_source_trash(&chip->resume_soc_wakeup_source.source);
 	wakeup_source_trash(&chip->empty_check_wakeup_source.source);
 	wakeup_source_trash(&chip->memif_wakeup_source.source);
@@ -5667,6 +7482,11 @@
 	wakeup_source_trash(&chip->gain_comp_wakeup_source.source);
 	wakeup_source_trash(&chip->capacity_learning_wakeup_source.source);
 	wakeup_source_trash(&chip->esr_extract_wakeup_source.source);
+	wakeup_source_trash(&chip->slope_limit_wakeup_source.source);
+	wakeup_source_trash(&chip->dischg_gain_wakeup_source.source);
+	wakeup_source_trash(&chip->fg_reset_wakeup_source.source);
+	wakeup_source_trash(&chip->cc_soc_wakeup_source.source);
+	wakeup_source_trash(&chip->sanity_wakeup_source.source);
 }
 
 static int fg_remove(struct platform_device *pdev)
@@ -6155,12 +7975,13 @@
 	return 0;
 }
 
-#define FG_ALG_SYSCTL_1	0x4B0
-#define SOC_CNFG	0x450
-#define SOC_DELTA_OFFSET	3
-#define DELTA_SOC_PERCENT	1
-#define I_TERM_QUAL_BIT		BIT(1)
-#define PATCH_NEG_CURRENT_BIT	BIT(3)
+#define FG_ALG_SYSCTL_1			0x4B0
+#define SOC_CNFG			0x450
+#define SOC_DELTA_OFFSET		3
+#define DELTA_SOC_PERCENT		1
+#define ALERT_CFG_OFFSET		3
+#define I_TERM_QUAL_BIT			BIT(1)
+#define PATCH_NEG_CURRENT_BIT		BIT(3)
 #define KI_COEFF_PRED_FULL_ADDR		0x408
 #define KI_COEFF_PRED_FULL_4_0_MSB	0x88
 #define KI_COEFF_PRED_FULL_4_0_LSB	0x00
@@ -6168,6 +7989,12 @@
 #define FG_ADC_CONFIG_REG		0x4B8
 #define FG_BCL_CONFIG_OFFSET		0x3
 #define BCL_FORCED_HPM_IN_CHARGE	BIT(2)
+#define IRQ_USE_VOLTAGE_HYST_BIT	BIT(0)
+#define EMPTY_FROM_VOLTAGE_BIT		BIT(1)
+#define EMPTY_FROM_SOC_BIT		BIT(2)
+#define EMPTY_SOC_IRQ_MASK		(IRQ_USE_VOLTAGE_HYST_BIT | \
+					EMPTY_FROM_SOC_BIT | \
+					EMPTY_FROM_VOLTAGE_BIT)
 static int fg_common_hw_init(struct fg_chip *chip)
 {
 	int rc;
@@ -6176,8 +8003,9 @@
 
 	update_iterm(chip);
 	update_cutoff_voltage(chip);
-	update_irq_volt_empty(chip);
 	update_bcl_thresholds(chip);
+	if (!chip->use_vbat_low_empty_soc)
+		update_irq_volt_empty(chip);
 
 	resume_soc_raw = settings[FG_MEM_RESUME_SOC].value;
 	if (resume_soc_raw > 0) {
@@ -6207,6 +8035,11 @@
 		return rc;
 	}
 
+	/* Override the voltage threshold for vbatt_low with empty_volt */
+	if (chip->use_vbat_low_empty_soc)
+		settings[FG_MEM_BATT_LOW].value =
+			settings[FG_MEM_IRQ_VOLT_EMPTY].value;
+
 	rc = fg_mem_masked_write(chip, settings[FG_MEM_BATT_LOW].address, 0xFF,
 			batt_to_setpoint_8b(settings[FG_MEM_BATT_LOW].value),
 			settings[FG_MEM_BATT_LOW].offset);
@@ -6274,20 +8107,41 @@
 		if (fg_debug_mask & FG_STATUS)
 			pr_info("imptr_pulse_slow is %sabled\n",
 				chip->imptr_pulse_slow_en ? "en" : "dis");
+	}
 
-		rc = fg_mem_read(chip, &val, RSLOW_CFG_REG, 1, RSLOW_CFG_OFFSET,
-				0);
-		if (rc) {
-			pr_err("unable to read rslow cfg: %d\n", rc);
-			return rc;
-		}
+	rc = fg_mem_read(chip, &val, RSLOW_CFG_REG, 1, RSLOW_CFG_OFFSET,
+			0);
+	if (rc) {
+		pr_err("unable to read rslow cfg: %d\n", rc);
+		return rc;
+	}
 
-		if (val & RSLOW_CFG_ON_VAL)
-			chip->rslow_comp.active = true;
+	if (val & RSLOW_CFG_ON_VAL)
+		chip->rslow_comp.active = true;
 
-		if (fg_debug_mask & FG_STATUS)
-			pr_info("rslow_comp active is %sabled\n",
-				chip->rslow_comp.active ? "en" : "dis");
+	if (fg_debug_mask & FG_STATUS)
+		pr_info("rslow_comp active is %sabled\n",
+			chip->rslow_comp.active ? "en" : "dis");
+
+	/*
+	 * Clear bits 0-2 in 0x4B3 and set them again to make empty_soc irq
+	 * trigger again.
+	 */
+	rc = fg_mem_masked_write(chip, FG_ALG_SYSCTL_1, EMPTY_SOC_IRQ_MASK,
+			0, ALERT_CFG_OFFSET);
+	if (rc) {
+		pr_err("failed to write to 0x4B3 rc=%d\n", rc);
+		return rc;
+	}
+
+	/* Wait for a FG cycle before enabling empty soc irq configuration */
+	msleep(FG_CYCLE_MS);
+
+	rc = fg_mem_masked_write(chip, FG_ALG_SYSCTL_1, EMPTY_SOC_IRQ_MASK,
+			EMPTY_SOC_IRQ_MASK, ALERT_CFG_OFFSET);
+	if (rc) {
+		pr_err("failed to write to 0x4B3 rc=%d\n", rc);
+		return rc;
 	}
 
 	return 0;
@@ -6414,12 +8268,13 @@
 		/* Setup workaround flag based on PMIC type */
 		if (fg_sense_type == INTERNAL_CURRENT_SENSE)
 			chip->wa_flag |= IADC_GAIN_COMP_WA;
-		if (chip->pmic_revision[REVID_DIG_MAJOR] > 1)
+		if (chip->pmic_revision[REVID_DIG_MAJOR] >= 1)
 			chip->wa_flag |= USE_CC_SOC_REG;
 
 		break;
 	case PMI8950:
 	case PMI8937:
+	case PMI8940:
 		rc = fg_8950_hw_init(chip);
 		/* Setup workaround flag based on PMIC type */
 		chip->wa_flag |= BCL_HI_POWER_FOR_CHGLED_WA;
@@ -6438,12 +8293,223 @@
 	return rc;
 }
 
+static int fg_init_iadc_config(struct fg_chip *chip)
+{
+	u8 reg[2];
+	int rc;
+
+	/* read default gain config */
+	rc = fg_mem_read(chip, reg, K_VCOR_REG, 2, DEF_GAIN_OFFSET, 0);
+	if (rc) {
+		pr_err("Failed to read default gain rc=%d\n", rc);
+		return rc;
+	}
+
+	if (reg[1] || reg[0]) {
+		/*
+		 * Default gain register has valid value:
+		 * - write to gain register.
+		 */
+		rc = fg_mem_write(chip, reg, GAIN_REG, 2,
+						GAIN_OFFSET, 0);
+		if (rc) {
+			pr_err("Failed to write gain rc=%d\n", rc);
+			return rc;
+		}
+	} else {
+		/*
+		 * Default gain register is invalid:
+		 * - read gain register for default gain value
+		 * - write to default gain register.
+		 */
+		rc = fg_mem_read(chip, reg, GAIN_REG, 2,
+						GAIN_OFFSET, 0);
+		if (rc) {
+			pr_err("Failed to read gain rc=%d\n", rc);
+			return rc;
+		}
+		rc = fg_mem_write(chip, reg, K_VCOR_REG, 2,
+						DEF_GAIN_OFFSET, 0);
+		if (rc) {
+			pr_err("Failed to write default gain rc=%d\n",
+								rc);
+			return rc;
+		}
+	}
+
+	chip->iadc_comp_data.dfl_gain_reg[0] = reg[0];
+	chip->iadc_comp_data.dfl_gain_reg[1] = reg[1];
+	chip->iadc_comp_data.dfl_gain = half_float(reg);
+
+	pr_debug("IADC gain initial config reg_val 0x%x%x gain %lld\n",
+		       reg[1], reg[0], chip->iadc_comp_data.dfl_gain);
+	return 0;
+}
+
+#define EN_WR_FGXCT_PRD		BIT(6)
+#define EN_RD_FGXCT_PRD		BIT(5)
+#define FG_RESTART_TIMEOUT_MS	12000
+static void ima_error_recovery_work(struct work_struct *work)
+{
+	struct fg_chip *chip = container_of(work,
+				struct fg_chip,
+				ima_error_recovery_work);
+	bool tried_again = false;
+	int rc;
+	u8 buf[4] = {0, 0, 0, 0};
+
+	fg_stay_awake(&chip->fg_reset_wakeup_source);
+	mutex_lock(&chip->ima_recovery_lock);
+	if (!chip->ima_error_handling) {
+		pr_err("Scheduled by mistake?\n");
+		mutex_unlock(&chip->ima_recovery_lock);
+		fg_relax(&chip->fg_reset_wakeup_source);
+		return;
+	}
+
+	/*
+	 * SOC should be read and used until the error recovery completes.
+	 * Without this, there could be a fluctuation in SOC values notified
+	 * to the userspace.
+	 */
+	chip->use_last_soc = true;
+
+	/* Block SRAM access till FG reset is complete */
+	chip->block_sram_access = true;
+
+	/* Release the mutex to avoid deadlock while cancelling the works */
+	mutex_unlock(&chip->ima_recovery_lock);
+
+	/* Cancel all the works */
+	fg_cancel_all_works(chip);
+
+	if (fg_debug_mask & FG_STATUS)
+		pr_info("last_soc: %d\n", chip->last_soc);
+
+	mutex_lock(&chip->ima_recovery_lock);
+	/* Acquire IMA access forcibly from FG ALG */
+	rc = fg_masked_write(chip, chip->mem_base + MEM_INTF_IMA_CFG,
+			EN_WR_FGXCT_PRD | EN_RD_FGXCT_PRD,
+			EN_WR_FGXCT_PRD | EN_RD_FGXCT_PRD, 1);
+	if (rc) {
+		pr_err("Error in writing to IMA_CFG, rc=%d\n", rc);
+		goto out;
+	}
+
+	/* Release the IMA access now so that FG reset can go through */
+	rc = fg_masked_write(chip, chip->mem_base + MEM_INTF_IMA_CFG,
+			EN_WR_FGXCT_PRD | EN_RD_FGXCT_PRD, 0, 1);
+	if (rc) {
+		pr_err("Error in writing to IMA_CFG, rc=%d\n", rc);
+		goto out;
+	}
+
+	if (fg_debug_mask & FG_STATUS)
+		pr_info("resetting FG\n");
+
+	/* Assert FG reset */
+	rc = fg_reset(chip, true);
+	if (rc) {
+		pr_err("Couldn't reset FG\n");
+		goto out;
+	}
+
+	/* Wait for a small time before deasserting FG reset */
+	msleep(100);
+
+	if (fg_debug_mask & FG_STATUS)
+		pr_info("clearing FG from reset\n");
+
+	/* Deassert FG reset */
+	rc = fg_reset(chip, false);
+	if (rc) {
+		pr_err("Couldn't clear FG reset\n");
+		goto out;
+	}
+
+	/* Wait for at least a FG cycle before doing SRAM access */
+	msleep(2000);
+
+	chip->block_sram_access = false;
+
+	if (!chip->init_done) {
+		schedule_work(&chip->init_work);
+		goto wait;
+	}
+
+	if (fg_debug_mask & FG_STATUS)
+		pr_info("Calling hw_init\n");
+
+	/*
+	 * Once FG is reset, everything in SRAM will be wiped out. Redo
+	 * hw_init, update jeita settings etc., again to make sure all
+	 * the settings got restored again.
+	 */
+	rc = fg_hw_init(chip);
+	if (rc) {
+		pr_err("Error in hw_init, rc=%d\n", rc);
+		goto out;
+	}
+
+	update_jeita_setting(&chip->update_jeita_setting.work);
+
+	if (chip->wa_flag & IADC_GAIN_COMP_WA) {
+		rc = fg_init_iadc_config(chip);
+		if (rc)
+			goto out;
+	}
+
+	if (fg_debug_mask & FG_STATUS)
+		pr_info("loading battery profile\n");
+	if (!chip->use_otp_profile) {
+		chip->battery_missing = true;
+		chip->profile_loaded = false;
+		chip->soc_reporting_ready = false;
+		chip->batt_type = default_batt_type;
+		fg_handle_battery_insertion(chip);
+	}
+
+wait:
+	rc = wait_for_completion_interruptible_timeout(&chip->fg_reset_done,
+			msecs_to_jiffies(FG_RESTART_TIMEOUT_MS));
+
+	/* If we were interrupted wait again one more time. */
+	if (rc == -ERESTARTSYS && !tried_again) {
+		tried_again = true;
+		pr_debug("interrupted, waiting again\n");
+		goto wait;
+	} else if (rc <= 0) {
+		pr_err("fg_restart taking long time rc=%d\n", rc);
+		goto out;
+	}
+
+	rc = fg_mem_write(chip, buf, fg_data[FG_DATA_VINT_ERR].address,
+			fg_data[FG_DATA_VINT_ERR].len,
+			fg_data[FG_DATA_VINT_ERR].offset, 0);
+	if (rc < 0)
+		pr_err("Error in clearing VACT_INT_ERR, rc=%d\n", rc);
+
+	if (fg_debug_mask & FG_STATUS)
+		pr_info("IMA error recovery done...\n");
+out:
+	fg_restore_soc(chip);
+	fg_restore_cc_soc(chip);
+	fg_enable_irqs(chip, true);
+	update_sram_data_work(&chip->update_sram_data.work);
+	update_temp_data(&chip->update_temp_work.work);
+	schedule_delayed_work(&chip->check_sanity_work,
+		msecs_to_jiffies(1000));
+	chip->ima_error_handling = false;
+	mutex_unlock(&chip->ima_recovery_lock);
+	fg_relax(&chip->fg_reset_wakeup_source);
+}
+
 #define DIG_MINOR		0x0
 #define DIG_MAJOR		0x1
 #define ANA_MINOR		0x2
 #define ANA_MAJOR		0x3
 #define IACS_INTR_SRC_SLCT	BIT(3)
-static int fg_setup_memif_offset(struct fg_chip *chip)
+static int fg_memif_init(struct fg_chip *chip)
 {
 	int rc;
 
@@ -6464,7 +8530,7 @@
 		break;
 	default:
 		pr_err("Digital Major rev=%d not supported\n",
-				chip->revision[DIG_MAJOR]);
+					chip->revision[DIG_MAJOR]);
 		return -EINVAL;
 	}
 
@@ -6481,6 +8547,13 @@
 			pr_err("failed to configure interrupt source %d\n", rc);
 			return rc;
 		}
+
+		/* check for error condition */
+		rc = fg_check_ima_exception(chip, true);
+		if (rc) {
+			pr_err("Error in clearing IMA exception rc=%d", rc);
+			return rc;
+		}
 	}
 
 	return 0;
@@ -6515,6 +8588,7 @@
 	case PMI8950:
 	case PMI8937:
 	case PMI8996:
+	case PMI8940:
 		chip->pmic_subtype = pmic_rev_id->pmic_subtype;
 		chip->pmic_revision[REVID_RESERVED]	= pmic_rev_id->rev1;
 		chip->pmic_revision[REVID_VARIANT]	= pmic_rev_id->rev2;
@@ -6531,10 +8605,8 @@
 }
 
 #define INIT_JEITA_DELAY_MS 1000
-
 static void delayed_init_work(struct work_struct *work)
 {
-	u8 reg[2];
 	int rc;
 	struct fg_chip *chip = container_of(work,
 				struct fg_chip,
@@ -6546,6 +8618,14 @@
 	rc = fg_hw_init(chip);
 	if (rc) {
 		pr_err("failed to hw init rc = %d\n", rc);
+		if (!chip->init_done && chip->ima_supported) {
+			rc = fg_check_alg_status(chip);
+			if (rc && rc != -EBUSY)
+				pr_err("Couldn't check FG ALG status, rc=%d\n",
+					rc);
+			fg_mem_release(chip);
+			return;
+		}
 		fg_mem_release(chip);
 		fg_cleanup(chip);
 		return;
@@ -6566,57 +8646,19 @@
 	if (!chip->use_otp_profile)
 		schedule_delayed_work(&chip->batt_profile_init, 0);
 
+	if (chip->ima_supported && fg_reset_on_lockup)
+		schedule_delayed_work(&chip->check_sanity_work,
+			msecs_to_jiffies(1000));
+
 	if (chip->wa_flag & IADC_GAIN_COMP_WA) {
-		/* read default gain config */
-		rc = fg_mem_read(chip, reg, K_VCOR_REG, 2, DEF_GAIN_OFFSET, 0);
-		if (rc) {
-			pr_err("Failed to read default gain rc=%d\n", rc);
+		rc = fg_init_iadc_config(chip);
+		if (rc)
 			goto done;
-		}
-
-		if (reg[1] || reg[0]) {
-			/*
-			 * Default gain register has valid value:
-			 * - write to gain register.
-			 */
-			rc = fg_mem_write(chip, reg, GAIN_REG, 2,
-							GAIN_OFFSET, 0);
-			if (rc) {
-				pr_err("Failed to write gain rc=%d\n", rc);
-				goto done;
-			}
-		} else {
-			/*
-			 * Default gain register is invalid:
-			 * - read gain register for default gain value
-			 * - write to default gain register.
-			 */
-			rc = fg_mem_read(chip, reg, GAIN_REG, 2,
-							GAIN_OFFSET, 0);
-			if (rc) {
-				pr_err("Failed to read gain rc=%d\n", rc);
-				goto done;
-			}
-			rc = fg_mem_write(chip, reg, K_VCOR_REG, 2,
-							DEF_GAIN_OFFSET, 0);
-			if (rc) {
-				pr_err("Failed to write default gain rc=%d\n",
-									rc);
-				goto done;
-			}
-		}
-
-		chip->iadc_comp_data.dfl_gain_reg[0] = reg[0];
-		chip->iadc_comp_data.dfl_gain_reg[1] = reg[1];
-		chip->iadc_comp_data.dfl_gain = half_float(reg);
-		chip->input_present = is_input_present(chip);
-		chip->otg_present = is_otg_present(chip);
-		chip->init_done = true;
-
-		pr_debug("IADC gain initial config reg_val 0x%x%x gain %lld\n",
-			       reg[1], reg[0], chip->iadc_comp_data.dfl_gain);
 	}
 
+	chip->input_present = is_input_present(chip);
+	chip->otg_present = is_otg_present(chip);
+	chip->init_done = true;
 	pr_debug("FG: HW_init success\n");
 
 	return;
@@ -6675,16 +8717,30 @@
 			"qpnp_fg_cap_learning");
 	wakeup_source_init(&chip->esr_extract_wakeup_source.source,
 			"qpnp_fg_esr_extract");
+	wakeup_source_init(&chip->slope_limit_wakeup_source.source,
+			"qpnp_fg_slope_limit");
+	wakeup_source_init(&chip->dischg_gain_wakeup_source.source,
+			"qpnp_fg_dischg_gain");
+	wakeup_source_init(&chip->fg_reset_wakeup_source.source,
+			"qpnp_fg_reset");
+	wakeup_source_init(&chip->cc_soc_wakeup_source.source,
+			"qpnp_fg_cc_soc");
+	wakeup_source_init(&chip->sanity_wakeup_source.source,
+			"qpnp_fg_sanity_check");
+	spin_lock_init(&chip->sec_access_lock);
 	mutex_init(&chip->rw_lock);
 	mutex_init(&chip->cyc_ctr.lock);
 	mutex_init(&chip->learning_data.learning_lock);
 	mutex_init(&chip->rslow_comp.lock);
 	mutex_init(&chip->sysfs_restart_lock);
+	mutex_init(&chip->ima_recovery_lock);
 	INIT_DELAYED_WORK(&chip->update_jeita_setting, update_jeita_setting);
 	INIT_DELAYED_WORK(&chip->update_sram_data, update_sram_data_work);
 	INIT_DELAYED_WORK(&chip->update_temp_work, update_temp_data);
 	INIT_DELAYED_WORK(&chip->check_empty_work, check_empty_work);
 	INIT_DELAYED_WORK(&chip->batt_profile_init, batt_profile_init);
+	INIT_DELAYED_WORK(&chip->check_sanity_work, check_sanity_work);
+	INIT_WORK(&chip->ima_error_recovery_work, ima_error_recovery_work);
 	INIT_WORK(&chip->rslow_comp_work, rslow_comp_work);
 	INIT_WORK(&chip->fg_cap_learning_work, fg_cap_learning_work);
 	INIT_WORK(&chip->dump_sram, dump_sram);
@@ -6699,13 +8755,19 @@
 	INIT_WORK(&chip->gain_comp_work, iadc_gain_comp_work);
 	INIT_WORK(&chip->bcl_hi_power_work, bcl_hi_power_work);
 	INIT_WORK(&chip->esr_extract_config_work, esr_extract_config_work);
+	INIT_WORK(&chip->slope_limiter_work, slope_limiter_work);
+	INIT_WORK(&chip->dischg_gain_work, discharge_gain_work);
+	INIT_WORK(&chip->cc_soc_store_work, cc_soc_store_work);
 	alarm_init(&chip->fg_cap_learning_alarm, ALARM_BOOTTIME,
 			fg_cap_learning_alarm_cb);
+	alarm_init(&chip->hard_jeita_alarm, ALARM_BOOTTIME,
+			fg_hard_jeita_alarm_cb);
 	init_completion(&chip->sram_access_granted);
 	init_completion(&chip->sram_access_revoked);
 	complete_all(&chip->sram_access_revoked);
 	init_completion(&chip->batt_id_avail);
 	init_completion(&chip->first_soc_done);
+	init_completion(&chip->fg_reset_done);
 	dev_set_drvdata(&pdev->dev, chip);
 
 	if (of_get_available_child_count(pdev->dev.of_node) == 0) {
@@ -6763,7 +8825,7 @@
 		return rc;
 	}
 
-	rc = fg_setup_memif_offset(chip);
+	rc = fg_memif_init(chip);
 	if (rc) {
 		pr_err("Unable to setup mem_if offsets rc=%d\n", rc);
 		goto of_init_fail;
@@ -6834,10 +8896,18 @@
 		rc = fg_dfs_create(chip);
 		if (rc < 0) {
 			pr_err("failed to create debugfs rc = %d\n", rc);
-			goto cancel_work;
+			goto power_supply_unregister;
 		}
 	}
 
+	/* Fake temperature till the actual temperature is read */
+	chip->last_good_temp = 250;
+
+	/* Initialize batt_info variables */
+	chip->batt_range_ocv = &fg_batt_valid_ocv;
+	chip->batt_range_pct = &fg_batt_range_pct;
+	memset(chip->batt_info, INT_MAX, sizeof(chip->batt_info));
+
 	schedule_work(&chip->init_work);
 
 	pr_info("FG Probe success - FG Revision DIG:%d.%d ANA:%d.%d PMIC subtype=%d\n",
@@ -6847,32 +8917,17 @@
 
 	return rc;
 
+power_supply_unregister:
+	power_supply_unregister(chip->bms_psy);
 cancel_work:
-	cancel_delayed_work_sync(&chip->update_jeita_setting);
-	cancel_delayed_work_sync(&chip->update_sram_data);
-	cancel_delayed_work_sync(&chip->update_temp_work);
-	cancel_delayed_work_sync(&chip->check_empty_work);
-	cancel_delayed_work_sync(&chip->batt_profile_init);
-	alarm_try_to_cancel(&chip->fg_cap_learning_alarm);
-	cancel_work_sync(&chip->set_resume_soc_work);
-	cancel_work_sync(&chip->fg_cap_learning_work);
-	cancel_work_sync(&chip->dump_sram);
-	cancel_work_sync(&chip->status_change_work);
-	cancel_work_sync(&chip->cycle_count_work);
-	cancel_work_sync(&chip->update_esr_work);
-	cancel_work_sync(&chip->rslow_comp_work);
-	cancel_work_sync(&chip->sysfs_restart_work);
-	cancel_work_sync(&chip->gain_comp_work);
-	cancel_work_sync(&chip->init_work);
-	cancel_work_sync(&chip->charge_full_work);
-	cancel_work_sync(&chip->bcl_hi_power_work);
-	cancel_work_sync(&chip->esr_extract_config_work);
+	fg_cancel_all_works(chip);
 of_init_fail:
 	mutex_destroy(&chip->rslow_comp.lock);
 	mutex_destroy(&chip->rw_lock);
 	mutex_destroy(&chip->cyc_ctr.lock);
 	mutex_destroy(&chip->learning_data.learning_lock);
 	mutex_destroy(&chip->sysfs_restart_lock);
+	mutex_destroy(&chip->ima_recovery_lock);
 	wakeup_source_trash(&chip->resume_soc_wakeup_source.source);
 	wakeup_source_trash(&chip->empty_check_wakeup_source.source);
 	wakeup_source_trash(&chip->memif_wakeup_source.source);
@@ -6882,6 +8937,11 @@
 	wakeup_source_trash(&chip->gain_comp_wakeup_source.source);
 	wakeup_source_trash(&chip->capacity_learning_wakeup_source.source);
 	wakeup_source_trash(&chip->esr_extract_wakeup_source.source);
+	wakeup_source_trash(&chip->slope_limit_wakeup_source.source);
+	wakeup_source_trash(&chip->dischg_gain_wakeup_source.source);
+	wakeup_source_trash(&chip->fg_reset_wakeup_source.source);
+	wakeup_source_trash(&chip->cc_soc_wakeup_source.source);
+	wakeup_source_trash(&chip->sanity_wakeup_source.source);
 	return rc;
 }
 
@@ -6938,11 +8998,103 @@
 	return 0;
 }
 
+static void fg_check_ima_idle(struct fg_chip *chip)
+{
+	bool rif_mem_sts = true;
+	int rc, time_count = 0;
+
+	mutex_lock(&chip->rw_lock);
+	/* Make sure IMA is idle */
+	while (1) {
+		rc = fg_check_rif_mem_access(chip, &rif_mem_sts);
+		if (rc)
+			break;
+
+		if (!rif_mem_sts)
+			break;
+
+		if  (time_count > 4) {
+			pr_err("Waited for ~16ms polling RIF_MEM_ACCESS_REQ\n");
+			fg_run_iacs_clear_sequence(chip);
+			break;
+		}
+
+		/* Wait for 4ms before reading RIF_MEM_ACCESS_REQ again */
+		usleep_range(4000, 4100);
+		time_count++;
+	}
+	mutex_unlock(&chip->rw_lock);
+}
+
+static void fg_shutdown(struct platform_device *pdev)
+{
+	struct fg_chip *chip = dev_get_drvdata(&pdev->dev);
+
+	if (fg_debug_mask & FG_STATUS)
+		pr_emerg("FG shutdown started\n");
+	fg_cancel_all_works(chip);
+	fg_check_ima_idle(chip);
+	chip->fg_shutdown = true;
+	if (fg_debug_mask & FG_STATUS)
+		pr_emerg("FG shutdown complete\n");
+}
+
 static const struct dev_pm_ops qpnp_fg_pm_ops = {
 	.suspend	= fg_suspend,
 	.resume		= fg_resume,
 };
 
+static int fg_reset_lockup_set(const char *val, const struct kernel_param *kp)
+{
+	int rc;
+	struct power_supply *bms_psy;
+	struct fg_chip *chip;
+	int old_val = fg_reset_on_lockup;
+
+	rc = param_set_int(val, kp);
+	if (rc) {
+		pr_err("Unable to set fg_reset_on_lockup: %d\n", rc);
+		return rc;
+	}
+
+	if (fg_reset_on_lockup != 0 && fg_reset_on_lockup != 1) {
+		pr_err("Bad value %d\n", fg_reset_on_lockup);
+		fg_reset_on_lockup = old_val;
+		return -EINVAL;
+	}
+
+	bms_psy = power_supply_get_by_name("bms");
+	if (!bms_psy) {
+		pr_err("bms psy not found\n");
+		return 0;
+	}
+
+	chip = power_supply_get_drvdata(bms_psy);
+	if (!chip->ima_supported) {
+		pr_err("Cannot set this for non-IMA supported FG\n");
+		fg_reset_on_lockup = old_val;
+		return -EINVAL;
+	}
+
+	if (fg_debug_mask & FG_STATUS)
+		pr_info("fg_reset_on_lockup set to %d\n", fg_reset_on_lockup);
+
+	if (fg_reset_on_lockup)
+		schedule_delayed_work(&chip->check_sanity_work,
+			msecs_to_jiffies(1000));
+	else
+		cancel_delayed_work_sync(&chip->check_sanity_work);
+
+	return rc;
+}
+
+static struct kernel_param_ops fg_reset_ops = {
+	.set = fg_reset_lockup_set,
+	.get = param_get_int,
+};
+
+module_param_cb(reset_on_lockup, &fg_reset_ops, &fg_reset_on_lockup, 0644);
+
 static int fg_sense_type_set(const char *val, const struct kernel_param *kp)
 {
 	int rc;
@@ -7025,6 +9177,7 @@
 	},
 	.probe		= fg_probe,
 	.remove		= fg_remove,
+	.shutdown	= fg_shutdown,
 };
 
 static int __init fg_init(void)
diff --git a/drivers/power/supply/qcom/qpnp-smb5.c b/drivers/power/supply/qcom/qpnp-smb5.c
index 4fd659e..8ff5330d 100644
--- a/drivers/power/supply/qcom/qpnp-smb5.c
+++ b/drivers/power/supply/qcom/qpnp-smb5.c
@@ -244,7 +244,7 @@
 		chg->use_extcon = true;
 		chg->name = "pmi632_charger";
 		/* PMI632 does not support PD */
-		__pd_disabled = 1;
+		chg->pd_not_supported = true;
 		chg->hw_max_icl_ua =
 			(chip->dt.usb_icl_ua > 0) ? chip->dt.usb_icl_ua
 						: PMI632_MAX_ICL_UA;
@@ -1594,11 +1594,20 @@
 
 	/*
 	 * PMI632 based hw init:
+	 * - Enable STAT pin function on SMB_EN
 	 * - Rerun APSD to ensure proper charger detection if device
 	 *   boots with charger connected.
 	 * - Initialize flash module for PMI632
 	 */
 	if (chg->smb_version == PMI632_SUBTYPE) {
+		rc = smblib_masked_write(chg, MISC_SMB_EN_CMD_REG,
+					EN_STAT_CMD_BIT, EN_STAT_CMD_BIT);
+		if (rc < 0) {
+			dev_err(chg->dev, "Couldn't configure SMB_EN rc=%d\n",
+					rc);
+			return rc;
+		}
+
 		schgm_flash_init(chg);
 		smblib_rerun_apsd_if_required(chg);
 	}
diff --git a/drivers/power/supply/qcom/smb5-lib.c b/drivers/power/supply/qcom/smb5-lib.c
index a5af817..58466b83 100644
--- a/drivers/power/supply/qcom/smb5-lib.c
+++ b/drivers/power/supply/qcom/smb5-lib.c
@@ -517,7 +517,7 @@
 	int rc;
 	u8 mask = HVDCP_EN_BIT | BC1P2_SRC_DETECT_BIT;
 
-	if (chg->pd_disabled)
+	if (chg->pd_not_supported)
 		return 0;
 
 	rc = smblib_masked_write(chg, USBIN_OPTIONS_1_CFG_REG, mask,
@@ -3078,7 +3078,8 @@
 	}
 
 	if (!chg->pr_swap_in_progress)
-		chg->ok_to_pd = !(*chg->pd_disabled) || chg->early_usb_attach;
+		chg->ok_to_pd = (!(*chg->pd_disabled) || chg->early_usb_attach)
+					&& !chg->pd_not_supported;
 }
 
 static void typec_src_insertion(struct smb_charger *chg)
@@ -3097,8 +3098,8 @@
 	}
 
 	chg->typec_legacy = stat & TYPEC_LEGACY_CABLE_STATUS_BIT;
-	chg->ok_to_pd = !(chg->typec_legacy || *chg->pd_disabled)
-						|| chg->early_usb_attach;
+	chg->ok_to_pd = (!(chg->typec_legacy || *chg->pd_disabled)
+			|| chg->early_usb_attach) && !chg->pd_not_supported;
 	if (!chg->ok_to_pd) {
 		rc = smblib_configure_hvdcp_apsd(chg, true);
 		if (rc < 0) {
diff --git a/drivers/power/supply/qcom/smb5-lib.h b/drivers/power/supply/qcom/smb5-lib.h
index 14eba8c..57281cc7 100644
--- a/drivers/power/supply/qcom/smb5-lib.h
+++ b/drivers/power/supply/qcom/smb5-lib.h
@@ -272,6 +272,7 @@
 	int			otg_delay_ms;
 	int			*weak_chg_icl_ua;
 	struct qpnp_vadc_chip	*vadc_dev;
+	bool			pd_not_supported;
 
 	/* locks */
 	struct mutex		lock;
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index 5fafaca..3ecca59 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -684,6 +684,17 @@
 	  determines last CPU to call into PSCI for cluster Low power
 	  modes.
 
+config MSM_PM_LEGACY
+	depends on PM
+	select MSM_IDLE_STATS if DEBUG_FS
+	select CPU_IDLE_MULTIPLE_DRIVERS
+	bool "Qualcomm platform specific Legacy PM driver"
+	help
+	  Platform specific legacy power driver to manage
+	  cores and l2 low power modes. It interface with
+	  various system driver and put the cores into
+	  low power modes.
+
 config MSM_NOPM
 	default y if !PM
 	bool
@@ -700,7 +711,7 @@
 	  trusted apps, unloading them and marshalling buffers to the
 	  trusted fingerprint app.
 
-if MSM_PM
+if (MSM_PM || MSM_PM_LEGACY)
 menuconfig MSM_IDLE_STATS
 	bool "Collect idle statistics"
 	help
@@ -733,7 +744,7 @@
 	  histogram.  This is for collecting statistics on suspend.
 
 endif # MSM_IDLE_STATS
-endif # MSM_PM
+endif # MSM_PM || MSM_PM_LEGACY
 
 config QCOM_DCC_V2
 	bool "Qualcomm Technologies Data Capture and Compare engine support for V2"
@@ -868,3 +879,11 @@
 		interrupt event and event data.
 
 source "drivers/soc/qcom/wcnss/Kconfig"
+
+config BIG_CLUSTER_MIN_FREQ_ADJUST
+	bool "Adjust BIG cluster min frequency based on power collapse state"
+	default n
+	help
+	  This driver is used to set the floor of the min frequency of big cluster
+	  to the user specified value when the cluster is not power collapsed. When
+	  the cluster is power collpsed it resets the value to physical limits.
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index b83f554..0b71121 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -14,6 +14,7 @@
 obj-$(CONFIG_QCOM_SMD) +=	smd.o
 obj-$(CONFIG_QCOM_SMD_RPM)	+= smd-rpm.o
 obj-$(CONFIG_QCOM_SMEM) +=	smem.o
+obj-$(CONFIG_MSM_PM_LEGACY) += pm-boot.o msm-pm.o
 obj-$(CONFIG_MSM_SPM) += msm-spm.o spm_devices.o
 obj-$(CONFIG_QCOM_SMEM_STATE) += smem_state.o
 obj-$(CONFIG_QCOM_SMP2P)	+= smp2p.o
@@ -105,3 +106,4 @@
 obj-$(CONFIG_QCOM_QDSS_BRIDGE) += qdss_bridge.o
 obj-$(CONFIG_MSM_BAM_DMUX) += bam_dmux.o
 obj-$(CONFIG_WCNSS_CORE) += wcnss/
+obj-$(CONFIG_BIG_CLUSTER_MIN_FREQ_ADJUST) += big_cluster_min_freq_adjust.o
diff --git a/drivers/soc/qcom/big_cluster_min_freq_adjust.c b/drivers/soc/qcom/big_cluster_min_freq_adjust.c
new file mode 100644
index 0000000..dbc89e1
--- /dev/null
+++ b/drivers/soc/qcom/big_cluster_min_freq_adjust.c
@@ -0,0 +1,278 @@
+/* Copyright (c) 2018, 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.
+ *
+ */
+
+#define pr_fmt(fmt)     "big_min_freq_adjust: " fmt
+
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+#include <linux/cpumask.h>
+#include <linux/cpu_pm.h>
+#include <linux/types.h>
+#include <linux/smp.h>
+#include <linux/moduleparam.h>
+
+enum min_freq_adjust {
+	ADJUST_MIN_FLOOR,	/* Set min floor to user supplied value */
+	RESET_MIN_FLOOR,	/* Reset min floor cpuinfo value */
+};
+
+struct big_min_freq_adjust_data {
+	struct cpumask cluster_cpumask;
+	unsigned int min_freq_floor;
+	struct delayed_work min_freq_work;
+	unsigned long min_down_delay_jiffies;
+	enum min_freq_adjust min_freq_state;
+	enum min_freq_adjust min_freq_request;
+	spinlock_t lock;
+	bool big_min_freq_on;
+	bool is_init;
+};
+static struct big_min_freq_adjust_data big_min_freq_adjust_data;
+
+static void cpufreq_min_freq_work(struct work_struct *work)
+{
+	struct big_min_freq_adjust_data *p = &big_min_freq_adjust_data;
+
+	spin_lock(&p->lock);
+	if (p->min_freq_state == p->min_freq_request) {
+		spin_unlock(&p->lock);
+		return;
+	}
+	spin_unlock(&p->lock);
+	cpufreq_update_policy(cpumask_first(&p->cluster_cpumask));
+}
+
+static int cpufreq_callback(struct notifier_block *nb, unsigned long val,
+				void *data)
+{
+	struct big_min_freq_adjust_data *p = &big_min_freq_adjust_data;
+	struct cpufreq_policy *policy = data;
+	unsigned int min_freq_floor;
+
+	if (p->big_min_freq_on == false)
+		return NOTIFY_DONE;
+
+	if (val != CPUFREQ_ADJUST)
+		return NOTIFY_DONE;
+
+	if (!cpumask_test_cpu(cpumask_first(&p->cluster_cpumask),
+				policy->related_cpus))
+		return NOTIFY_DONE;
+
+	spin_lock(&p->lock);
+	if (p->min_freq_request == ADJUST_MIN_FLOOR)
+		min_freq_floor = p->min_freq_floor;
+	else
+		min_freq_floor = policy->cpuinfo.min_freq;
+	cpufreq_verify_within_limits(policy, min_freq_floor,
+			policy->cpuinfo.max_freq);
+	p->min_freq_state = p->min_freq_request;
+	spin_unlock(&p->lock);
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block cpufreq_nb = {
+	.notifier_call = cpufreq_callback
+};
+
+#define AFFINITY_LEVEL_L2 1
+static int cpu_pm_callback(struct notifier_block *self,
+			       unsigned long cmd, void *v)
+{
+	struct big_min_freq_adjust_data *p = &big_min_freq_adjust_data;
+	unsigned long aff_level = (unsigned long) v;
+	unsigned long delay;
+	int cpu;
+
+	if (p->big_min_freq_on == false)
+		return NOTIFY_DONE;
+
+	if (aff_level != AFFINITY_LEVEL_L2)
+		return NOTIFY_DONE;
+
+	cpu = smp_processor_id();
+
+	if (!cpumask_test_cpu(cpu, &p->cluster_cpumask))
+		return NOTIFY_DONE;
+
+	spin_lock(&p->lock);
+	switch (cmd) {
+	case CPU_CLUSTER_PM_ENTER:
+		p->min_freq_request = RESET_MIN_FLOOR;
+		delay = p->min_down_delay_jiffies;
+		break;
+	case CPU_CLUSTER_PM_ENTER_FAILED:
+	case CPU_CLUSTER_PM_EXIT:
+		p->min_freq_request = ADJUST_MIN_FLOOR;
+		/* To avoid unnecessary oscillations between exit and idle */
+		delay = 1;
+		break;
+	default:
+		spin_unlock(&p->lock);
+		return NOTIFY_DONE;
+	}
+
+	cancel_delayed_work(&p->min_freq_work);
+
+	if (p->min_freq_state != p->min_freq_request)
+		schedule_delayed_work(&p->min_freq_work, delay);
+	spin_unlock(&p->lock);
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block cpu_pm_nb = {
+	.notifier_call = cpu_pm_callback
+};
+
+static unsigned long __read_mostly big_min_down_delay_ms;
+#define MIN_DOWN_DELAY_MSEC 80 /* Default big_min_down_delay in msec */
+#define POLICY_MIN 1094400 /* Default min_freq_floor in KHz */
+
+static void trigger_state_machine(void *d)
+{
+	struct big_min_freq_adjust_data *p = &big_min_freq_adjust_data;
+	bool *update_policy = d;
+
+	if (p->min_freq_request != ADJUST_MIN_FLOOR) {
+		p->min_freq_request = ADJUST_MIN_FLOOR;
+		*update_policy = true;
+	}
+}
+
+static int enable_big_min_freq_adjust(void)
+{
+	struct big_min_freq_adjust_data *p = &big_min_freq_adjust_data;
+	int ret;
+	bool update_policy = false;
+
+	if (p->big_min_freq_on == true)
+		return 0;
+
+	INIT_DEFERRABLE_WORK(&p->min_freq_work, cpufreq_min_freq_work);
+
+	cpumask_clear(&p->cluster_cpumask);
+	cpumask_set_cpu(4, &p->cluster_cpumask);
+	cpumask_set_cpu(5, &p->cluster_cpumask);
+	cpumask_set_cpu(6, &p->cluster_cpumask);
+	cpumask_set_cpu(7, &p->cluster_cpumask);
+
+	if (!big_min_down_delay_ms) {
+		big_min_down_delay_ms = MIN_DOWN_DELAY_MSEC;
+		p->min_down_delay_jiffies = msecs_to_jiffies(
+				big_min_down_delay_ms);
+	}
+	if (!p->min_freq_floor)
+		p->min_freq_floor = POLICY_MIN;
+
+	ret = cpu_pm_register_notifier(&cpu_pm_nb);
+	if (ret) {
+		pr_err("Failed to register for PM notification\n");
+		return ret;
+	}
+
+	ret = cpufreq_register_notifier(&cpufreq_nb, CPUFREQ_POLICY_NOTIFIER);
+	if (ret) {
+		pr_err("Failed to register for CPUFREQ POLICY notification\n");
+		cpu_pm_unregister_notifier(&cpu_pm_nb);
+		return ret;
+	}
+
+	p->min_freq_state = RESET_MIN_FLOOR;
+	p->min_freq_request = RESET_MIN_FLOOR;
+	spin_lock_init(&p->lock);
+	p->big_min_freq_on = true;
+
+	/* If BIG cluster is active at this time and continue to be active
+	 * forever, in that case min frequency of the cluster will never be
+	 * set to floor value.  This is to trigger the state machine and set
+	 * the min freq and  min_freq_state to appropriate values.
+	 *
+	 * Two possibilities here.
+	 * 1) If cluster is idle before this, the wakeup is unnecessary but
+	 * the state machine is set to proper state.
+	 * 2) If cluster is active before this, the wakeup is necessary and
+	 * the state machine is set to proper state.
+	 */
+	smp_call_function_any(&p->cluster_cpumask,
+			trigger_state_machine, &update_policy, true);
+	if (update_policy)
+		cpufreq_update_policy(cpumask_first(&p->cluster_cpumask));
+
+	pr_info("big min freq ajustment enabled\n");
+
+	return 0;
+}
+
+static bool __read_mostly big_min_freq_adjust_enabled;
+
+static int set_big_min_freq_adjust(const char *buf,
+		const struct kernel_param *kp)
+{
+	int ret;
+
+	ret = param_set_bool_enable_only(buf, kp);
+	if (ret) {
+		pr_err("Unable to set big_min_freq_adjust_enabled: %d\n", ret);
+		return ret;
+	}
+
+	if (!big_min_freq_adjust_data.is_init)
+		return ret;
+
+	return enable_big_min_freq_adjust();
+}
+
+static const struct kernel_param_ops param_ops_big_min_freq_adjust = {
+	.set = set_big_min_freq_adjust,
+	.get = param_get_bool,
+};
+module_param_cb(min_freq_adjust, &param_ops_big_min_freq_adjust,
+		&big_min_freq_adjust_enabled, 0644);
+
+module_param_named(min_freq_floor, big_min_freq_adjust_data.min_freq_floor,
+		uint, 0644);
+
+static int set_min_down_delay_ms(const char *buf, const struct kernel_param *kp)
+{
+	int ret;
+
+	ret = param_set_ulong(buf, kp);
+	if (ret) {
+		pr_err("Unable to set big_min_down_delay_ms: %d\n", ret);
+		return ret;
+	}
+
+	big_min_freq_adjust_data.min_down_delay_jiffies = msecs_to_jiffies(
+			big_min_down_delay_ms);
+
+	return 0;
+}
+
+static const struct kernel_param_ops param_ops_big_min_down_delay_ms = {
+	.set = set_min_down_delay_ms,
+	.get = param_get_ulong,
+};
+module_param_cb(min_down_delay_ms, &param_ops_big_min_down_delay_ms,
+		&big_min_down_delay_ms, 0644);
+
+static int __init big_min_freq_adjust_init(void)
+{
+	big_min_freq_adjust_data.is_init = true;
+	if (!big_min_freq_adjust_enabled)
+		return 0;
+
+	return enable_big_min_freq_adjust();
+}
+late_initcall(big_min_freq_adjust_init);
diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c
index c254299..b8e9268 100644
--- a/drivers/soc/qcom/icnss.c
+++ b/drivers/soc/qcom/icnss.c
@@ -2113,6 +2113,7 @@
 
 	set_bit(ICNSS_WLFW_EXISTS, &penv->state);
 	clear_bit(ICNSS_FW_DOWN, &penv->state);
+	icnss_ignore_qmi_timeout(false);
 
 	penv->wlfw_clnt = qmi_handle_create(icnss_qmi_wlfw_clnt_notify, penv);
 	if (!penv->wlfw_clnt) {
@@ -2450,8 +2451,10 @@
 	int ret = 0;
 	struct icnss_event_pd_service_down_data *event_data = data;
 
-	if (!test_bit(ICNSS_WLFW_EXISTS, &priv->state))
+	if (!test_bit(ICNSS_WLFW_EXISTS, &priv->state)) {
+		icnss_ignore_qmi_timeout(false);
 		goto out;
+	}
 
 	if (priv->force_err_fatal)
 		ICNSS_ASSERT(0);
@@ -2475,8 +2478,6 @@
 out:
 	kfree(data);
 
-	icnss_ignore_qmi_timeout(false);
-
 	return ret;
 }
 
@@ -2485,15 +2486,16 @@
 {
 	int ret = 0;
 
-	if (!test_bit(ICNSS_WLFW_EXISTS, &priv->state))
+	if (!test_bit(ICNSS_WLFW_EXISTS, &priv->state)) {
+		icnss_ignore_qmi_timeout(false);
 		goto out;
+	}
 
 	priv->early_crash_ind = true;
 	icnss_fw_crashed(priv, NULL);
 
 out:
 	kfree(data);
-	icnss_ignore_qmi_timeout(false);
 
 	return ret;
 }
@@ -3178,7 +3180,8 @@
 	if (!dev)
 		return -ENODEV;
 
-	if (test_bit(ICNSS_FW_DOWN, &penv->state)) {
+	if (test_bit(ICNSS_FW_DOWN, &penv->state) ||
+	    !test_bit(ICNSS_FW_READY, &penv->state)) {
 		icnss_pr_err("FW down, ignoring fw_log_mode state: 0x%lx\n",
 			     penv->state);
 		return -EINVAL;
@@ -3277,7 +3280,8 @@
 	if (!dev)
 		return -ENODEV;
 
-	if (test_bit(ICNSS_FW_DOWN, &penv->state)) {
+	if (test_bit(ICNSS_FW_DOWN, &penv->state) ||
+	    !test_bit(ICNSS_FW_READY, &penv->state)) {
 		icnss_pr_err("FW down, ignoring wlan_enable state: 0x%lx\n",
 			     penv->state);
 		return -EINVAL;
diff --git a/drivers/soc/qcom/idle.h b/drivers/soc/qcom/idle.h
new file mode 100644
index 0000000..2f852c3
--- /dev/null
+++ b/drivers/soc/qcom/idle.h
@@ -0,0 +1,29 @@
+/* Copyright (c) 2007-2009,2012-2014, 2018, The Linux Foundation.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _ARCH_ARM_MACH_MSM_IDLE_H_
+#define _ARCH_ARM_MACH_MSM_IDLE_H_
+
+#define MAX_CPUS_PER_CLUSTER	4
+#define MAX_NUM_CLUSTER	4
+
+#ifndef __ASSEMBLY__
+#if defined(CONFIG_CPU_V7) || defined(CONFIG_ARM64)
+extern unsigned long msm_pm_boot_vector[MAX_NUM_CLUSTER * MAX_CPUS_PER_CLUSTER];
+void msm_pm_boot_entry(void);
+#else
+static inline void msm_pm_boot_entry(void) {}
+#endif
+#endif
+#endif
diff --git a/drivers/soc/qcom/msm-pm.c b/drivers/soc/qcom/msm-pm.c
new file mode 100644
index 0000000..129ebce
--- /dev/null
+++ b/drivers/soc/qcom/msm-pm.c
@@ -0,0 +1,921 @@
+/* Copyright (c) 2010-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/debugfs.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/io.h>
+#include <linux/ktime.h>
+#include <linux/smp.h>
+#include <linux/tick.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/msm-bus.h>
+#include <linux/uaccess.h>
+#include <linux/dma-mapping.h>
+#include <soc/qcom/spm.h>
+#include <soc/qcom/pm-legacy.h>
+#include <soc/qcom/scm.h>
+#include <soc/qcom/scm-boot.h>
+#include <asm/suspend.h>
+#include <asm/cacheflush.h>
+#include <asm/cputype.h>
+#include <asm/system_misc.h>
+#ifdef CONFIG_VFP
+#include <asm/vfp.h>
+#endif
+#include <soc/qcom/jtag.h>
+#include "pm-boot.h"
+#include "idle.h"
+
+#define SCM_CMD_TERMINATE_PC	(0x2)
+#define SCM_CMD_CORE_HOTPLUGGED (0x10)
+#define SCM_FLUSH_FLAG_MASK	(0x3)
+
+#define SCLK_HZ (32768)
+
+#define MAX_BUF_SIZE  1024
+
+static int msm_pm_debug_mask = 1;
+module_param_named(
+	debug_mask, msm_pm_debug_mask, int, 0664
+);
+
+enum {
+	MSM_PM_DEBUG_SUSPEND = BIT(0),
+	MSM_PM_DEBUG_POWER_COLLAPSE = BIT(1),
+	MSM_PM_DEBUG_SUSPEND_LIMITS = BIT(2),
+	MSM_PM_DEBUG_CLOCK = BIT(3),
+	MSM_PM_DEBUG_RESET_VECTOR = BIT(4),
+	MSM_PM_DEBUG_IDLE = BIT(5),
+	MSM_PM_DEBUG_IDLE_LIMITS = BIT(6),
+	MSM_PM_DEBUG_HOTPLUG = BIT(7),
+};
+
+enum msm_pc_count_offsets {
+	MSM_PC_ENTRY_COUNTER,
+	MSM_PC_EXIT_COUNTER,
+	MSM_PC_FALLTHRU_COUNTER,
+	MSM_PC_UNUSED,
+	MSM_PC_NUM_COUNTERS,
+};
+
+static bool msm_pm_ldo_retention_enabled = true;
+static bool msm_pm_tz_flushes_cache;
+static bool msm_pm_ret_no_pll_switch;
+static bool msm_no_ramp_down_pc;
+static struct msm_pm_sleep_status_data *msm_pm_slp_sts;
+static DEFINE_PER_CPU(struct clk *, cpu_clks);
+static struct clk *l2_clk;
+
+static long *msm_pc_debug_counters;
+
+static cpumask_t retention_cpus;
+static DEFINE_SPINLOCK(retention_lock);
+static DEFINE_MUTEX(msm_pc_debug_mutex);
+
+static bool msm_pm_is_L1_writeback(void)
+{
+	u32 cache_id = 0;
+
+#if defined(CONFIG_CPU_V7)
+	u32 sel = 0;
+
+	asm volatile ("mcr p15, 2, %[ccselr], c0, c0, 0\n\t"
+		      "isb\n\t"
+		      "mrc p15, 1, %[ccsidr], c0, c0, 0\n\t"
+		      :[ccsidr]"=r" (cache_id)
+		      :[ccselr]"r" (sel)
+		     );
+	return cache_id & BIT(30);
+#elif defined(CONFIG_ARM64)
+	u32 sel = 0;
+
+	asm volatile("msr csselr_el1, %[ccselr]\n\t"
+		     "isb\n\t"
+		     "mrs %[ccsidr],ccsidr_el1\n\t"
+		     :[ccsidr]"=r" (cache_id)
+		     :[ccselr]"r" (sel)
+		    );
+	return cache_id & BIT(30);
+#else
+#error No valid CPU arch selected
+#endif
+}
+
+static bool msm_pm_swfi(bool from_idle)
+{
+	msm_arch_idle();
+	return true;
+}
+
+static bool msm_pm_retention(bool from_idle)
+{
+	int ret = 0;
+	unsigned int cpu = smp_processor_id();
+	struct clk *cpu_clk = per_cpu(cpu_clks, cpu);
+
+	spin_lock(&retention_lock);
+
+	if (!msm_pm_ldo_retention_enabled)
+		goto bailout;
+
+	cpumask_set_cpu(cpu, &retention_cpus);
+	spin_unlock(&retention_lock);
+
+	if (!msm_pm_ret_no_pll_switch)
+		clk_disable(cpu_clk);
+
+	ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_RETENTION, false);
+	WARN_ON(ret);
+
+	msm_arch_idle();
+
+	ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING, false);
+	WARN_ON(ret);
+
+	if (!msm_pm_ret_no_pll_switch)
+		if (clk_enable(cpu_clk))
+			pr_err("%s(): Error restore cpu clk\n", __func__);
+
+	spin_lock(&retention_lock);
+	cpumask_clear_cpu(cpu, &retention_cpus);
+bailout:
+	spin_unlock(&retention_lock);
+	return true;
+}
+
+static inline void msm_pc_inc_debug_count(uint32_t cpu,
+		enum msm_pc_count_offsets offset)
+{
+	int cntr_offset;
+	uint32_t cluster_id = MPIDR_AFFINITY_LEVEL(cpu_logical_map(cpu), 1);
+	uint32_t cpu_id = MPIDR_AFFINITY_LEVEL(cpu_logical_map(cpu), 0);
+
+	if (cluster_id >= MAX_NUM_CLUSTER || cpu_id >= MAX_CPUS_PER_CLUSTER)
+		WARN_ON(cpu);
+
+	cntr_offset = (cluster_id * MAX_CPUS_PER_CLUSTER * MSM_PC_NUM_COUNTERS)
+			 + (cpu_id * MSM_PC_NUM_COUNTERS) + offset;
+
+	if (!msm_pc_debug_counters)
+		return;
+
+	msm_pc_debug_counters[cntr_offset]++;
+}
+
+static bool msm_pm_pc_hotplug(void)
+{
+	uint32_t cpu = smp_processor_id();
+	enum msm_pm_l2_scm_flag flag;
+	struct scm_desc desc;
+
+	flag = lpm_cpu_pre_pc_cb(cpu);
+
+	if (!msm_pm_tz_flushes_cache) {
+		if (flag == MSM_SCM_L2_OFF)
+			flush_cache_all();
+		else if (msm_pm_is_L1_writeback())
+			flush_cache_louis();
+	}
+
+	msm_pc_inc_debug_count(cpu, MSM_PC_ENTRY_COUNTER);
+
+	if (is_scm_armv8()) {
+		desc.args[0] = SCM_CMD_CORE_HOTPLUGGED |
+			       (flag & SCM_FLUSH_FLAG_MASK);
+		desc.arginfo = SCM_ARGS(1);
+		scm_call2_atomic(SCM_SIP_FNID(SCM_SVC_BOOT,
+				 SCM_CMD_TERMINATE_PC), &desc);
+	} else {
+		scm_call_atomic1(SCM_SVC_BOOT, SCM_CMD_TERMINATE_PC,
+		SCM_CMD_CORE_HOTPLUGGED | (flag & SCM_FLUSH_FLAG_MASK));
+	}
+
+	/* Should not return here */
+	msm_pc_inc_debug_count(cpu, MSM_PC_FALLTHRU_COUNTER);
+	return 0;
+}
+
+static bool msm_pm_fastpc(bool from_idle)
+{
+	int ret = 0;
+	unsigned int cpu = smp_processor_id();
+
+	ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_FASTPC, false);
+	WARN_ON(ret);
+
+	if (from_idle || cpu_online(cpu))
+		msm_arch_idle();
+	else
+		msm_pm_pc_hotplug();
+
+	ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING, false);
+	WARN_ON(ret);
+
+	return true;
+}
+
+int msm_pm_collapse(unsigned long unused)
+{
+	uint32_t cpu = smp_processor_id();
+	enum msm_pm_l2_scm_flag flag;
+	struct scm_desc desc;
+
+	flag = lpm_cpu_pre_pc_cb(cpu);
+
+	if (!msm_pm_tz_flushes_cache) {
+		if (flag == MSM_SCM_L2_OFF)
+			flush_cache_all();
+		else if (msm_pm_is_L1_writeback())
+			flush_cache_louis();
+	}
+	msm_pc_inc_debug_count(cpu, MSM_PC_ENTRY_COUNTER);
+
+	if (is_scm_armv8()) {
+		desc.args[0] = flag;
+		desc.arginfo = SCM_ARGS(1);
+		scm_call2_atomic(SCM_SIP_FNID(SCM_SVC_BOOT,
+				 SCM_CMD_TERMINATE_PC), &desc);
+	} else {
+		scm_call_atomic1(SCM_SVC_BOOT, SCM_CMD_TERMINATE_PC, flag);
+	}
+
+	msm_pc_inc_debug_count(cpu, MSM_PC_FALLTHRU_COUNTER);
+
+	return 0;
+}
+EXPORT_SYMBOL(msm_pm_collapse);
+
+static bool __ref msm_pm_spm_power_collapse(
+	unsigned int cpu, int mode, bool from_idle, bool notify_rpm)
+{
+	void *entry;
+	bool collapsed = 0;
+	int ret;
+	bool save_cpu_regs = (cpu_online(cpu) || from_idle);
+
+	if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask)
+		pr_info("CPU%u: %s: notify_rpm %d\n",
+			cpu, __func__, (int) notify_rpm);
+
+	ret = msm_spm_set_low_power_mode(mode, notify_rpm);
+	WARN_ON(ret);
+
+	entry = save_cpu_regs ?  cpu_resume : msm_secondary_startup;
+
+	msm_pm_boot_config_before_pc(cpu, virt_to_phys(entry));
+
+	if (MSM_PM_DEBUG_RESET_VECTOR & msm_pm_debug_mask)
+		pr_info("CPU%u: %s: program vector to %pk\n",
+			cpu, __func__, entry);
+
+	msm_jtag_save_state();
+
+	collapsed = save_cpu_regs ?
+		!cpu_suspend(0, msm_pm_collapse) : msm_pm_pc_hotplug();
+
+	msm_jtag_restore_state();
+
+	if (collapsed)
+		local_fiq_enable();
+
+	msm_pm_boot_config_after_pc(cpu);
+
+	if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask)
+		pr_info("CPU%u: %s: msm_pm_collapse returned, collapsed %d\n",
+			cpu, __func__, collapsed);
+
+	ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING, false);
+	WARN_ON(ret);
+	return collapsed;
+}
+
+static bool msm_pm_power_collapse_standalone(
+		bool from_idle)
+{
+	unsigned int cpu = smp_processor_id();
+	bool collapsed;
+
+	collapsed = msm_pm_spm_power_collapse(cpu,
+			MSM_SPM_MODE_STANDALONE_POWER_COLLAPSE,
+			from_idle, false);
+
+	return collapsed;
+}
+
+static int ramp_down_last_cpu(int cpu)
+{
+	struct clk *cpu_clk = per_cpu(cpu_clks, cpu);
+	int ret = 0;
+
+	clk_disable(cpu_clk);
+	clk_disable(l2_clk);
+
+	return ret;
+}
+
+static int ramp_up_first_cpu(int cpu, int saved_rate)
+{
+	struct clk *cpu_clk = per_cpu(cpu_clks, cpu);
+	int rc = 0;
+
+	if (MSM_PM_DEBUG_CLOCK & msm_pm_debug_mask)
+		pr_info("CPU%u: %s: restore clock rate\n",
+				cpu, __func__);
+
+	clk_enable(l2_clk);
+
+	if (cpu_clk) {
+		int ret = clk_enable(cpu_clk);
+
+		if (ret) {
+			pr_err("%s(): Error restoring cpu clk\n",
+					__func__);
+			return ret;
+		}
+	}
+
+	return rc;
+}
+
+static bool msm_pm_power_collapse(bool from_idle)
+{
+	unsigned int cpu = smp_processor_id();
+	unsigned long saved_acpuclk_rate = 0;
+	bool collapsed;
+
+	if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask)
+		pr_info("CPU%u: %s: idle %d\n",
+			cpu, __func__, (int)from_idle);
+
+	if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask)
+		pr_info("CPU%u: %s: pre power down\n", cpu, __func__);
+
+	if (cpu_online(cpu) && !msm_no_ramp_down_pc)
+		saved_acpuclk_rate = ramp_down_last_cpu(cpu);
+
+	collapsed = msm_pm_spm_power_collapse(cpu, MSM_SPM_MODE_POWER_COLLAPSE,
+			from_idle, true);
+
+	if (cpu_online(cpu) && !msm_no_ramp_down_pc)
+		ramp_up_first_cpu(cpu, saved_acpuclk_rate);
+
+	if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask)
+		pr_info("CPU%u: %s: post power up\n", cpu, __func__);
+
+	if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask)
+		pr_info("CPU%u: %s: return\n", cpu, __func__);
+	return collapsed;
+}
+/******************************************************************************
+ * External Idle/Suspend Functions
+ *****************************************************************************/
+
+static void arch_idle(void) {}
+
+static bool (*execute[MSM_PM_SLEEP_MODE_NR])(bool idle) = {
+	[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT] = msm_pm_swfi,
+	[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE] =
+		msm_pm_power_collapse_standalone,
+	[MSM_PM_SLEEP_MODE_RETENTION] = msm_pm_retention,
+	[MSM_PM_SLEEP_MODE_FASTPC] = msm_pm_fastpc,
+	[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = msm_pm_power_collapse,
+};
+
+/**
+ * msm_cpu_pm_enter_sleep(): Enter a low power mode on current cpu
+ *
+ * @mode - sleep mode to enter
+ * @from_idle - bool to indicate that the mode is exercised during idle/suspend
+ *
+ * returns none
+ *
+ * The code should be with interrupts disabled and on the core on which the
+ * low power is to be executed.
+ *
+ */
+bool msm_cpu_pm_enter_sleep(enum msm_pm_sleep_mode mode, bool from_idle)
+{
+	bool exit_stat = false;
+	unsigned int cpu = smp_processor_id();
+
+	if ((!from_idle  && cpu_online(cpu))
+			|| (MSM_PM_DEBUG_IDLE & msm_pm_debug_mask))
+		pr_info("CPU%u:%s mode:%d during %s\n", cpu, __func__,
+				mode, from_idle ? "idle" : "suspend");
+
+	if (execute[mode])
+		exit_stat = execute[mode](from_idle);
+
+	return exit_stat;
+}
+
+/**
+ * msm_pm_wait_cpu_shutdown() - Wait for a core to be power collapsed during
+ *				hotplug
+ *
+ * @ cpu - cpu to wait on.
+ *
+ * Blocking function call that waits on the core to be power collapsed. This
+ * function is called from platform_cpu_die to ensure that a core is power
+ * collapsed before sending the CPU_DEAD notification so the drivers could
+ * remove the resource votes for this CPU(regulator and clock)
+ */
+int msm_pm_wait_cpu_shutdown(unsigned int cpu)
+{
+	int timeout = 0;
+
+	if (!msm_pm_slp_sts)
+		return 0;
+	if (!msm_pm_slp_sts[cpu].base_addr)
+		return 0;
+	while (1) {
+		/*
+		 * Check for the SPM of the core being hotplugged to set
+		 * its sleep state.The SPM sleep state indicates that the
+		 * core has been power collapsed.
+		 */
+		int acc_sts = __raw_readl(msm_pm_slp_sts[cpu].base_addr);
+
+		if (acc_sts & msm_pm_slp_sts[cpu].mask)
+			return 0;
+
+		udelay(100);
+		/*
+		 * Dump spm registers for debugging
+		 */
+		if (++timeout == 20) {
+			msm_spm_dump_regs(cpu);
+			__WARN_printf(
+			"CPU%u didn't collapse in 2ms, sleep status: 0x%x\n",
+								cpu, acc_sts);
+		}
+	}
+
+	return -EBUSY;
+}
+
+static void msm_pm_ack_retention_disable(void *data)
+{
+	/*
+	 * This is a NULL function to ensure that the core has woken up
+	 * and is safe to disable retention.
+	 */
+}
+/**
+ * msm_pm_enable_retention() - Disable/Enable retention on all cores
+ * @enable: Enable/Disable retention
+ *
+ */
+void msm_pm_enable_retention(bool enable)
+{
+	if (enable == msm_pm_ldo_retention_enabled)
+		return;
+
+	msm_pm_ldo_retention_enabled = enable;
+
+	/*
+	 * If retention is being disabled, wakeup all online core to ensure
+	 * that it isn't executing retention. Offlined cores need not be woken
+	 * up as they enter the deepest sleep mode, namely RPM assited power
+	 * collapse
+	 */
+	if (!enable) {
+		preempt_disable();
+		smp_call_function_many(&retention_cpus,
+				msm_pm_ack_retention_disable,
+				NULL, true);
+		preempt_enable();
+	}
+}
+EXPORT_SYMBOL(msm_pm_enable_retention);
+
+/**
+ * msm_pm_retention_enabled() - Check if retention is enabled
+ *
+ * returns true if retention is enabled
+ */
+bool msm_pm_retention_enabled(void)
+{
+	return msm_pm_ldo_retention_enabled;
+}
+EXPORT_SYMBOL(msm_pm_retention_enabled);
+
+static int msm_pm_snoc_client_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+	static struct msm_bus_scale_pdata *msm_pm_bus_pdata;
+	static uint32_t msm_pm_bus_client;
+
+	msm_pm_bus_pdata = msm_bus_cl_get_pdata(pdev);
+
+	if (msm_pm_bus_pdata) {
+		msm_pm_bus_client =
+			msm_bus_scale_register_client(msm_pm_bus_pdata);
+
+		if (!msm_pm_bus_client) {
+			pr_err("%s: Failed to register SNOC client", __func__);
+			rc = -ENXIO;
+			goto snoc_cl_probe_done;
+		}
+
+		rc = msm_bus_scale_client_update_request(msm_pm_bus_client, 1);
+
+		if (rc)
+			pr_err("%s: Error setting bus rate", __func__);
+	}
+
+snoc_cl_probe_done:
+	return rc;
+}
+
+static int msm_cpu_status_probe(struct platform_device *pdev)
+{
+	u32 cpu;
+	int rc;
+
+	if (!pdev || !pdev->dev.of_node)
+		return -EFAULT;
+
+	msm_pm_slp_sts = devm_kzalloc(&pdev->dev,
+			sizeof(*msm_pm_slp_sts) * num_possible_cpus(),
+			GFP_KERNEL);
+
+	if (!msm_pm_slp_sts)
+		return -ENOMEM;
+
+
+	for_each_possible_cpu(cpu) {
+		struct device_node *cpun, *node;
+		char *key;
+
+		cpun = of_get_cpu_node(cpu, NULL);
+
+		if (!cpun) {
+			__WARN();
+			continue;
+		}
+
+		node = of_parse_phandle(cpun, "qcom,sleep-status", 0);
+		if (!node)
+			return -ENODEV;
+
+		msm_pm_slp_sts[cpu].base_addr = of_iomap(node, 0);
+		if (!msm_pm_slp_sts[cpu].base_addr) {
+			pr_err("%s: Can't find base addr\n", __func__);
+			return -ENODEV;
+		}
+
+		key = "qcom,sleep-status-mask";
+		rc = of_property_read_u32(node, key, &msm_pm_slp_sts[cpu].mask);
+		if (rc) {
+			pr_err("%s: Can't find %s property\n", __func__, key);
+			iounmap(msm_pm_slp_sts[cpu].base_addr);
+			return rc;
+		}
+	}
+
+	return 0;
+};
+
+static const struct of_device_id msm_slp_sts_match_tbl[] = {
+	{.compatible = "qcom,cpu-sleep-status"},
+	{},
+};
+
+static struct platform_driver msm_cpu_status_driver = {
+	.probe = msm_cpu_status_probe,
+	.driver = {
+		.name = "cpu_slp_status",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_slp_sts_match_tbl,
+	},
+};
+
+static const struct of_device_id msm_snoc_clnt_match_tbl[] = {
+	{.compatible = "qcom,pm-snoc-client"},
+	{},
+};
+
+static struct platform_driver msm_cpu_pm_snoc_client_driver = {
+	.probe = msm_pm_snoc_client_probe,
+	.driver = {
+		.name = "pm_snoc_client",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_snoc_clnt_match_tbl,
+	},
+};
+
+struct msm_pc_debug_counters_buffer {
+	long *reg;
+	u32 len;
+	char buf[MAX_BUF_SIZE];
+};
+
+static char *counter_name[MSM_PC_NUM_COUNTERS] = {
+		"PC Entry Counter",
+		"Warmboot Entry Counter",
+		"PC Bailout Counter"
+};
+
+static int msm_pc_debug_counters_copy(
+		struct msm_pc_debug_counters_buffer *data)
+{
+	int j;
+	u32 stat;
+	unsigned int cpu;
+	unsigned int len;
+	uint32_t cluster_id;
+	uint32_t cpu_id;
+	uint32_t offset;
+
+	for_each_possible_cpu(cpu) {
+		len = scnprintf(data->buf + data->len,
+				sizeof(data->buf)-data->len,
+				"CPU%d\n", cpu);
+		cluster_id = MPIDR_AFFINITY_LEVEL(cpu_logical_map(cpu), 1);
+		cpu_id = MPIDR_AFFINITY_LEVEL(cpu_logical_map(cpu), 0);
+		offset = (cluster_id * MAX_CPUS_PER_CLUSTER
+				 * MSM_PC_NUM_COUNTERS)
+				 + (cpu_id * MSM_PC_NUM_COUNTERS);
+
+		data->len += len;
+
+		for (j = 0; j < MSM_PC_NUM_COUNTERS - 1; j++) {
+			stat = data->reg[offset + j];
+			len = scnprintf(data->buf + data->len,
+					 sizeof(data->buf) - data->len,
+					"\t%s: %d", counter_name[j], stat);
+
+			data->len += len;
+		}
+		len = scnprintf(data->buf + data->len,
+			 sizeof(data->buf) - data->len,
+			"\n");
+
+		data->len += len;
+	}
+
+	return data->len;
+}
+
+static ssize_t msm_pc_debug_counters_file_read(struct file *file,
+		char __user *bufu, size_t count, loff_t *ppos)
+{
+	struct msm_pc_debug_counters_buffer *data;
+	ssize_t ret;
+
+	mutex_lock(&msm_pc_debug_mutex);
+	data = file->private_data;
+
+	if (!data) {
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	if (!bufu) {
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	if (!access_ok(VERIFY_WRITE, bufu, count)) {
+		ret = -EFAULT;
+		goto exit;
+	}
+
+	if (*ppos >= data->len && data->len == 0)
+		data->len = msm_pc_debug_counters_copy(data);
+
+	ret = simple_read_from_buffer(bufu, count, ppos,
+			data->buf, data->len);
+exit:
+	mutex_unlock(&msm_pc_debug_mutex);
+	return ret;
+}
+
+static int msm_pc_debug_counters_file_open(struct inode *inode,
+		struct file *file)
+{
+	struct msm_pc_debug_counters_buffer *buf;
+	int ret = 0;
+
+	mutex_lock(&msm_pc_debug_mutex);
+
+	if (!inode->i_private) {
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	file->private_data = kzalloc(
+		sizeof(struct msm_pc_debug_counters_buffer), GFP_KERNEL);
+
+	if (!file->private_data) {
+		pr_err("%s: ERROR kmalloc failed to allocate %zu bytes\n",
+		__func__, sizeof(struct msm_pc_debug_counters_buffer));
+
+		ret = -ENOMEM;
+		goto exit;
+	}
+
+	buf = file->private_data;
+	buf->reg = (long *)inode->i_private;
+
+exit:
+	mutex_unlock(&msm_pc_debug_mutex);
+	return ret;
+}
+
+static int msm_pc_debug_counters_file_close(struct inode *inode,
+		struct file *file)
+{
+	mutex_lock(&msm_pc_debug_mutex);
+	kfree(file->private_data);
+	mutex_unlock(&msm_pc_debug_mutex);
+	return 0;
+}
+
+static const struct file_operations msm_pc_debug_counters_fops = {
+	.open = msm_pc_debug_counters_file_open,
+	.read = msm_pc_debug_counters_file_read,
+	.release = msm_pc_debug_counters_file_close,
+	.llseek = no_llseek,
+};
+
+static int msm_pm_clk_init(struct platform_device *pdev)
+{
+	bool synced_clocks;
+	u32 cpu;
+	char clk_name[] = "cpu??_clk";
+	char *key;
+
+	key = "qcom,saw-turns-off-pll";
+	if (of_property_read_bool(pdev->dev.of_node, key))
+		return 0;
+
+	key = "qcom,synced-clocks";
+	synced_clocks = of_property_read_bool(pdev->dev.of_node, key);
+
+	for_each_possible_cpu(cpu) {
+		struct clk *clk;
+
+		snprintf(clk_name, sizeof(clk_name), "cpu%d_clk", cpu);
+		clk = clk_get(&pdev->dev, clk_name);
+		if (IS_ERR(clk)) {
+			if (cpu && synced_clocks)
+				return 0;
+			clk = NULL;
+		}
+		per_cpu(cpu_clks, cpu) = clk;
+	}
+
+	if (synced_clocks)
+		return 0;
+
+	l2_clk = clk_get(&pdev->dev, "l2_clk");
+	if (IS_ERR(l2_clk))
+		pr_warn("%s: Could not get l2_clk (-%ld)\n", __func__,
+			PTR_ERR(l2_clk));
+
+	return 0;
+}
+
+static int msm_cpu_pm_probe(struct platform_device *pdev)
+{
+	struct dentry *dent = NULL;
+	struct resource *res = NULL;
+	int ret = 0;
+	void __iomem *msm_pc_debug_counters_imem;
+	char *key;
+	int alloc_size = (MAX_NUM_CLUSTER * MAX_CPUS_PER_CLUSTER
+					* MSM_PC_NUM_COUNTERS
+					* sizeof(*msm_pc_debug_counters));
+
+	msm_pc_debug_counters = dma_alloc_coherent(&pdev->dev, alloc_size,
+				&msm_pc_debug_counters_phys, GFP_KERNEL);
+
+	if (msm_pc_debug_counters) {
+		memset(msm_pc_debug_counters, 0, alloc_size);
+		dent = debugfs_create_file("pc_debug_counter", 0444, NULL,
+				msm_pc_debug_counters,
+				&msm_pc_debug_counters_fops);
+		if (!dent)
+			pr_err("%s: ERROR debugfs_create_file failed\n",
+					__func__);
+		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+		if (!res)
+			goto skip_save_imem;
+		msm_pc_debug_counters_imem = devm_ioremap(&pdev->dev,
+						res->start, resource_size(res));
+		if (msm_pc_debug_counters_imem) {
+			writel_relaxed(msm_pc_debug_counters_phys,
+					msm_pc_debug_counters_imem);
+			/* memory barrier */
+			mb();
+			devm_iounmap(&pdev->dev,
+					msm_pc_debug_counters_imem);
+		}
+	} else {
+		msm_pc_debug_counters = NULL;
+		msm_pc_debug_counters_phys = 0;
+	}
+skip_save_imem:
+	if (pdev->dev.of_node) {
+		key = "qcom,tz-flushes-cache";
+		msm_pm_tz_flushes_cache =
+				of_property_read_bool(pdev->dev.of_node, key);
+
+		key = "qcom,no-pll-switch-for-retention";
+		msm_pm_ret_no_pll_switch =
+				of_property_read_bool(pdev->dev.of_node, key);
+
+		ret = msm_pm_clk_init(pdev);
+		if (ret) {
+			pr_info("msm_pm_clk_init returned error\n");
+			return ret;
+		}
+	}
+
+	if (pdev->dev.of_node)
+		of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
+
+	return ret;
+}
+
+static const struct of_device_id msm_cpu_pm_table[] = {
+	{.compatible = "qcom,pm"},
+	{},
+};
+
+static struct platform_driver msm_cpu_pm_driver = {
+	.probe = msm_cpu_pm_probe,
+	.driver = {
+		.name = "msm-pm",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_cpu_pm_table,
+	},
+};
+
+static int __init msm_pm_drv_init(void)
+{
+	int rc;
+
+	cpumask_clear(&retention_cpus);
+
+	rc = platform_driver_register(&msm_cpu_pm_snoc_client_driver);
+
+	if (rc)
+		pr_err("%s(): failed to register driver %s\n", __func__,
+				msm_cpu_pm_snoc_client_driver.driver.name);
+	return rc;
+}
+late_initcall(msm_pm_drv_init);
+
+static int __init msm_pm_debug_counters_init(void)
+{
+	int rc;
+
+	rc = platform_driver_register(&msm_cpu_pm_driver);
+
+	if (rc)
+		pr_err("%s(): failed to register driver %s\n", __func__,
+				msm_cpu_pm_driver.driver.name);
+	return rc;
+}
+fs_initcall(msm_pm_debug_counters_init);
+
+int __init msm_pm_sleep_status_init(void)
+{
+	static bool registered;
+
+	if (registered)
+		return 0;
+	registered = true;
+
+	return platform_driver_register(&msm_cpu_status_driver);
+}
+arch_initcall(msm_pm_sleep_status_init);
+
+#ifdef CONFIG_ARM
+static int idle_initialize(void)
+{
+	arm_pm_idle = arch_idle;
+	return 0;
+}
+early_initcall(idle_initialize);
+#endif
diff --git a/drivers/soc/qcom/pm-boot.c b/drivers/soc/qcom/pm-boot.c
new file mode 100644
index 0000000..f0daeba
--- /dev/null
+++ b/drivers/soc/qcom/pm-boot.c
@@ -0,0 +1,98 @@
+/* Copyright (c) 2011-2014, 2016, 2018, The Linux Foundation.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <soc/qcom/scm-boot.h>
+#include <asm/cacheflush.h>
+#include <asm/cputype.h>
+#include <asm/smp_plat.h>
+#include "pm-boot.h"
+#include "idle.h"
+
+#define CPU_INDEX(cluster, cpu) (cluster * MAX_CPUS_PER_CLUSTER + cpu)
+
+static void (*msm_pm_boot_before_pc)(unsigned int cpu, unsigned long entry);
+static void (*msm_pm_boot_after_pc)(unsigned int cpu);
+
+static int msm_pm_tz_boot_init(void)
+{
+	int ret;
+	phys_addr_t warmboot_addr = virt_to_phys(msm_pm_boot_entry);
+
+	if (scm_is_mc_boot_available())
+		ret = scm_set_warm_boot_addr_mc_for_all(warmboot_addr);
+	else {
+		unsigned int flag = 0;
+
+		if (num_possible_cpus() == 1)
+			flag = SCM_FLAG_WARMBOOT_CPU0;
+		else if (num_possible_cpus() == 2)
+			flag = SCM_FLAG_WARMBOOT_CPU0 | SCM_FLAG_WARMBOOT_CPU1;
+		else if (num_possible_cpus() == 4)
+			flag = SCM_FLAG_WARMBOOT_CPU0 | SCM_FLAG_WARMBOOT_CPU1 |
+				SCM_FLAG_WARMBOOT_CPU2 | SCM_FLAG_WARMBOOT_CPU3;
+		else
+			pr_warn("%s: set warmboot address failed\n",
+								__func__);
+
+		ret = scm_set_boot_addr(virt_to_phys(msm_pm_boot_entry), flag);
+	}
+	return ret;
+}
+static void msm_pm_write_boot_vector(unsigned int cpu, unsigned long address)
+{
+	uint32_t clust_id = MPIDR_AFFINITY_LEVEL(cpu_logical_map(cpu), 1);
+	uint32_t cpu_id = MPIDR_AFFINITY_LEVEL(cpu_logical_map(cpu), 0);
+	unsigned long *start_address;
+	unsigned long *end_address;
+
+	if (clust_id >= MAX_NUM_CLUSTER || cpu_id >= MAX_CPUS_PER_CLUSTER)
+		WARN_ON(cpu);
+
+	msm_pm_boot_vector[CPU_INDEX(clust_id, cpu_id)] = address;
+	start_address = &msm_pm_boot_vector[CPU_INDEX(clust_id, cpu_id)];
+	end_address = &msm_pm_boot_vector[CPU_INDEX(clust_id, cpu_id + 1)];
+	dmac_clean_range((void *)start_address, (void *)end_address);
+}
+
+static void msm_pm_config_tz_before_pc(unsigned int cpu,
+		unsigned long entry)
+{
+	msm_pm_write_boot_vector(cpu, entry);
+}
+
+void msm_pm_boot_config_before_pc(unsigned int cpu, unsigned long entry)
+{
+	if (msm_pm_boot_before_pc)
+		msm_pm_boot_before_pc(cpu, entry);
+}
+
+void msm_pm_boot_config_after_pc(unsigned int cpu)
+{
+	if (msm_pm_boot_after_pc)
+		msm_pm_boot_after_pc(cpu);
+}
+
+static int __init msm_pm_boot_init(void)
+{
+	int ret = 0;
+
+	ret = msm_pm_tz_boot_init();
+	msm_pm_boot_before_pc = msm_pm_config_tz_before_pc;
+	msm_pm_boot_after_pc = NULL;
+
+	return ret;
+}
+postcore_initcall(msm_pm_boot_init);
diff --git a/drivers/soc/qcom/pm-boot.h b/drivers/soc/qcom/pm-boot.h
new file mode 100644
index 0000000..7ec053a
--- /dev/null
+++ b/drivers/soc/qcom/pm-boot.h
@@ -0,0 +1,19 @@
+/* Copyright (c) 2011-2014, 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef _ARCH_ARM_MACH_MSM_PM_BOOT_H
+#define _ARCH_ARM_MACH_MSM_PM_BOOT_H
+
+void msm_pm_boot_config_before_pc(unsigned int cpu, unsigned long entry);
+void msm_pm_boot_config_after_pc(unsigned int cpu);
+
+#endif
diff --git a/drivers/soc/qcom/qdss_bridge.c b/drivers/soc/qcom/qdss_bridge.c
index 8668155..fb70a07 100644
--- a/drivers/soc/qcom/qdss_bridge.c
+++ b/drivers/soc/qcom/qdss_bridge.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, 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
@@ -245,7 +245,7 @@
 
 	switch (event) {
 	case USB_QDSS_CONNECT:
-		usb_qdss_alloc_req(drvdata->usb_ch, poolsize, 0);
+		usb_qdss_alloc_req(ch, poolsize, 0);
 		mhi_queue_read(drvdata);
 		break;
 
diff --git a/drivers/soc/qcom/spm_devices.c b/drivers/soc/qcom/spm_devices.c
index 5f1ac4e..268a8fe 100644
--- a/drivers/soc/qcom/spm_devices.c
+++ b/drivers/soc/qcom/spm_devices.c
@@ -234,7 +234,7 @@
 }
 
 static int msm_spm_dev_set_low_power_mode(struct msm_spm_device *dev,
-		unsigned int mode, bool notify_rpm)
+		unsigned int mode, bool notify_rpm, bool set_spm_enable)
 {
 	uint32_t i;
 	int ret = -EINVAL;
@@ -251,9 +251,11 @@
 	if (!dev->num_modes)
 		return 0;
 
-	if (mode == MSM_SPM_MODE_DISABLED) {
+	if (mode == MSM_SPM_MODE_DISABLED && set_spm_enable) {
 		ret = msm_spm_drv_set_spm_enable(&dev->reg_data, false);
-	} else if (!msm_spm_drv_set_spm_enable(&dev->reg_data, true)) {
+	} else {
+		if (set_spm_enable)
+			ret = msm_spm_drv_set_spm_enable(&dev->reg_data, true);
 		for (i = 0; i < dev->num_modes; i++) {
 			if (dev->modes[i].mode != mode)
 				continue;
@@ -539,10 +541,24 @@
 {
 	struct msm_spm_device *dev = this_cpu_ptr(&msm_cpu_spm_device);
 
-	return msm_spm_dev_set_low_power_mode(dev, mode, notify_rpm);
+	return msm_spm_dev_set_low_power_mode(dev, mode, notify_rpm, true);
 }
 EXPORT_SYMBOL(msm_spm_set_low_power_mode);
 
+void msm_spm_set_rpm_hs(bool allow_rpm_hs)
+{
+	struct msm_spm_device *dev = this_cpu_ptr(&msm_cpu_spm_device);
+
+	dev->allow_rpm_hs = allow_rpm_hs;
+}
+EXPORT_SYMBOL(msm_spm_set_rpm_hs);
+
+int msm_spm_config_low_power_mode_addr(struct msm_spm_device *dev,
+		unsigned int mode, bool notify_rpm)
+{
+	return msm_spm_dev_set_low_power_mode(dev, mode, notify_rpm, false);
+}
+
 /**
  * msm_spm_init(): Board initalization function
  * @data: platform specific SPM register configuration data
@@ -586,7 +602,7 @@
 int msm_spm_config_low_power_mode(struct msm_spm_device *dev,
 		unsigned int mode, bool notify_rpm)
 {
-	return msm_spm_dev_set_low_power_mode(dev, mode, notify_rpm);
+	return msm_spm_dev_set_low_power_mode(dev, mode, notify_rpm, true);
 }
 #ifdef CONFIG_MSM_L2_SPM
 
diff --git a/drivers/soc/qcom/system_pm.c b/drivers/soc/qcom/system_pm.c
index 9c6edbf..1befdcf 100644
--- a/drivers/soc/qcom/system_pm.c
+++ b/drivers/soc/qcom/system_pm.c
@@ -22,6 +22,13 @@
 #define PDC_TIME_VALID_SHIFT	31
 #define PDC_TIME_UPPER_MASK	0xFFFFFF
 
+#ifdef CONFIG_ARM_GIC_V3
+#include <linux/irqchip/arm-gic-v3.h>
+#else
+static inline void gic_v3_dist_restore(void) {}
+static inline void gic_v3_dist_save(void) {}
+#endif
+
 static struct rpmh_client *rpmh_client;
 
 static int setup_wakeup(uint32_t lo, uint32_t hi)
@@ -61,6 +68,7 @@
  */
 static int system_sleep_enter(struct cpumask *mask)
 {
+	gic_v3_dist_save();
 	return rpmh_flush(rpmh_client);
 }
 
@@ -70,6 +78,7 @@
 static void system_sleep_exit(void)
 {
 	msm_rpmh_master_stats_update();
+	gic_v3_dist_restore();
 }
 
 static struct system_pm_ops pm_ops = {
diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c
index efaac5e..60acbf7 100644
--- a/drivers/tty/serial/msm_serial.c
+++ b/drivers/tty/serial/msm_serial.c
@@ -1858,10 +1858,35 @@
 
 	return 0;
 }
+
+static int msm_serial_freeze(struct device *dev)
+{
+	struct uart_port *port = dev_get_drvdata(dev);
+	struct msm_port *msm_port = UART_TO_MSM(port);
+	int ret;
+
+	ret = msm_serial_suspend(dev);
+	if (ret)
+		return ret;
+
+	/*
+	 * Set the rate as recommended to avoid issues where the clock
+	 * driver skips reconfiguring the clock hardware during
+	 * hibernation resume.
+	 */
+	return clk_set_rate(msm_port->clk, 19200000);
+}
 #endif
 
 static const struct dev_pm_ops msm_serial_pm_ops = {
-	SET_SYSTEM_SLEEP_PM_OPS(msm_serial_suspend, msm_serial_resume)
+#ifdef CONFIG_PM_SLEEP
+	.suspend = msm_serial_suspend,
+	.resume = msm_serial_resume,
+	.freeze = msm_serial_freeze,
+	.thaw = msm_serial_resume,
+	.poweroff = msm_serial_suspend,
+	.restore = msm_serial_resume,
+#endif
 };
 
 static struct platform_driver msm_platform_driver = {
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index 810546a..2790fea 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -2821,8 +2821,11 @@
 	int ret;
 
 	mdwc->dwc3_gdsc = devm_regulator_get(mdwc->dev, "USB3_GDSC");
-	if (IS_ERR(mdwc->dwc3_gdsc))
+	if (IS_ERR(mdwc->dwc3_gdsc)) {
+		if (PTR_ERR(mdwc->dwc3_gdsc) == -EPROBE_DEFER)
+			return PTR_ERR(mdwc->dwc3_gdsc);
 		mdwc->dwc3_gdsc = NULL;
+	}
 
 	mdwc->xo_clk = devm_clk_get(mdwc->dev, "xo");
 	if (IS_ERR(mdwc->xo_clk)) {
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index f0e4d5e..f878b8d1 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -414,7 +414,7 @@
 		dwc3_trace(trace_dwc3_gadget, "Command Timed Out");
 		dev_err(dwc->dev, "%s command timeout for %s\n",
 			dwc3_gadget_ep_cmd_string(cmd), dep->name);
-		if (cmd != DWC3_DEPCMD_ENDTRANSFER) {
+		if (DWC3_DEPCMD_CMD(cmd) != DWC3_DEPCMD_ENDTRANSFER) {
 			dwc->ep_cmd_timeout_cnt++;
 			dwc3_notify_event(dwc,
 				DWC3_CONTROLLER_RESTART_USB_SESSION, 0);
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index d3e0ca5..90cbb61 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -568,6 +568,7 @@
 config USB_CONFIGFS_F_GSI
 	bool "USB GSI function"
 	select USB_F_GSI
+	select USB_U_ETHER
 	depends on USB_CONFIGFS
 	help
 	  Generic function driver to support h/w acceleration to IPA over GSI.
diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c
index 939c219..703fb24 100644
--- a/drivers/usb/gadget/ci13xxx_udc.c
+++ b/drivers/usb/gadget/ci13xxx_udc.c
@@ -3951,7 +3951,7 @@
 	usb_del_gadget_udc(&udc->gadget);
 remove_trans:
 	if (udc->transceiver)
-		otg_set_peripheral(udc->transceiver->otg, &udc->gadget);
+		otg_set_peripheral(udc->transceiver->otg, NULL);
 
 	err("error = %i", retval);
 put_transceiver:
@@ -3989,7 +3989,7 @@
 	usb_del_gadget_udc(&udc->gadget);
 
 	if (udc->transceiver) {
-		otg_set_peripheral(udc->transceiver->otg, &udc->gadget);
+		otg_set_peripheral(udc->transceiver->otg, NULL);
 		usb_put_phy(udc->transceiver);
 	}
 	destroy_eps(udc);
diff --git a/drivers/usb/gadget/function/f_gsi.c b/drivers/usb/gadget/function/f_gsi.c
index 5ffbf12..94dd64a 100644
--- a/drivers/usb/gadget/function/f_gsi.c
+++ b/drivers/usb/gadget/function/f_gsi.c
@@ -19,7 +19,7 @@
 MODULE_PARM_DESC(qti_packet_debug, "Print QTI Packet's Raw Data");
 
 static struct workqueue_struct *ipa_usb_wq;
-static struct f_gsi *__gsi[IPA_USB_MAX_TETH_PROT_SIZE];
+static struct f_gsi *__gsi[USB_PROT_MAX];
 static void *ipc_log_ctxt;
 
 #define NUM_LOG_PAGES 15
@@ -56,6 +56,15 @@
 static struct gsi_ctrl_pkt *gsi_ctrl_pkt_alloc(unsigned int len, gfp_t flags);
 static void gsi_ctrl_pkt_free(struct gsi_ctrl_pkt *pkt);
 
+static inline bool is_ext_prot_ether(int prot_id)
+{
+	if (prot_id == USB_PROT_RMNET_ETHER ||
+				prot_id == USB_PROT_DPL_ETHER)
+		return true;
+
+	return false;
+}
+
 static inline bool usb_gsi_remote_wakeup_allowed(struct usb_function *f)
 {
 	bool remote_wakeup_allowed;
@@ -226,7 +235,7 @@
 		log_event_err("%s: Set net_ready_trigger", __func__);
 		gsi->d_port.net_ready_trigger = true;
 
-		if (gsi->prot_id == IPA_USB_ECM) {
+		if (gsi->prot_id == USB_PROT_ECM_IPA) {
 			cpkt_notify_connect = gsi_ctrl_pkt_alloc(0, GFP_ATOMIC);
 			if (IS_ERR(cpkt_notify_connect)) {
 				spin_unlock_irqrestore(&gsi->d_port.lock,
@@ -260,7 +269,7 @@
 		 * Do not post EVT_CONNECTED for RNDIS.
 		 * Data path for RNDIS is enabled on EVT_HOST_READY.
 		 */
-		if (gsi->prot_id != IPA_USB_RNDIS) {
+		if (gsi->prot_id != USB_PROT_RNDIS_IPA) {
 			post_event(&gsi->d_port, EVT_CONNECTED);
 			queue_work(gsi->d_port.ipa_usb_wq,
 					&gsi->d_port.usb_ipa_w);
@@ -311,7 +320,7 @@
 
 	log_event_dbg("%s: USB GSI IN OPS Completed", __func__);
 	in_params->client =
-		(gsi->prot_id != IPA_USB_DIAG) ? IPA_CLIENT_USB_CONS :
+		(gsi->prot_id != USB_PROT_DIAG_IPA) ? IPA_CLIENT_USB_CONS :
 						IPA_CLIENT_USB_DPL_CONS;
 	in_params->ipa_ep_cfg.mode.mode = IPA_BASIC;
 	in_params->teth_prot = gsi->prot_id;
@@ -395,7 +404,7 @@
 	conn_params->usb_to_ipa_xferrscidx =
 			d_port->out_xfer_rsc_index;
 	conn_params->usb_to_ipa_xferrscidx_valid =
-			(gsi->prot_id != IPA_USB_DIAG) ? true : false;
+			(gsi->prot_id != USB_PROT_DIAG_IPA) ? true : false;
 	conn_params->ipa_to_usb_xferrscidx_valid = true;
 	conn_params->teth_prot = gsi->prot_id;
 	conn_params->teth_prot_params.max_xfer_size_bytes_to_dev = 23700;
@@ -440,7 +449,7 @@
 	d_port->in_request.db_reg_phs_addr_msb =
 		ipa_in_channel_out_params.db_reg_phs_addr_msb;
 
-	if (gsi->prot_id != IPA_USB_DIAG) {
+	if (gsi->prot_id != USB_PROT_DIAG_IPA) {
 		d_port->out_channel_handle =
 			ipa_out_channel_out_params.clnt_hdl;
 		d_port->out_request.db_reg_phs_addr_lsb =
@@ -1159,7 +1168,8 @@
 
 	switch (cmd) {
 	case QTI_CTRL_MODEM_OFFLINE:
-		if (gsi->prot_id == IPA_USB_DIAG) {
+		if (gsi->prot_id == USB_PROT_DIAG_IPA ||
+				gsi->prot_id == USB_PROT_DPL_ETHER) {
 			log_event_dbg("%s:Modem Offline not handled", __func__);
 			goto exit_ioctl;
 		}
@@ -1177,7 +1187,8 @@
 		gsi_ctrl_send_notification(gsi);
 		break;
 	case QTI_CTRL_MODEM_ONLINE:
-		if (gsi->prot_id == IPA_USB_DIAG) {
+		if (gsi->prot_id == USB_PROT_DIAG_IPA ||
+				gsi->prot_id == USB_PROT_DPL_ETHER) {
 			log_event_dbg("%s:Modem Online not handled", __func__);
 			goto exit_ioctl;
 		}
@@ -1186,7 +1197,8 @@
 		break;
 	case QTI_CTRL_GET_LINE_STATE:
 		val = atomic_read(&gsi->connected);
-		if (gsi->prot_id == IPA_USB_RMNET)
+		if (gsi->prot_id == USB_PROT_RMNET_IPA ||
+				gsi->prot_id == USB_PROT_RMNET_ETHER)
 			val = gsi->rmnet_dtr_status;
 
 		ret = copy_to_user((void __user *)arg, &val, sizeof(val));
@@ -1207,7 +1219,8 @@
 			break;
 		}
 
-		if (gsi->prot_id == IPA_USB_DIAG &&
+		if ((gsi->prot_id == USB_PROT_DIAG_IPA ||
+				gsi->prot_id == USB_PROT_DPL_ETHER) &&
 				(gsi->d_port.in_channel_handle == -EINVAL)) {
 			ret = -EAGAIN;
 			break;
@@ -1222,7 +1235,8 @@
 		info.ph_ep_info.ep_type = GSI_MBIM_DATA_EP_TYPE_HSUSB;
 		info.ph_ep_info.peripheral_iface_id = gsi->data_id;
 		info.ipa_ep_pair.cons_pipe_num =
-		(gsi->prot_id == IPA_USB_DIAG) ? -1 :
+		(gsi->prot_id == USB_PROT_DIAG_IPA ||
+			gsi->prot_id == USB_PROT_DPL_ETHER) ? -1 :
 				gsi->d_port.out_channel_handle;
 		info.ipa_ep_pair.prod_pipe_num = gsi->d_port.in_channel_handle;
 
@@ -1328,8 +1342,8 @@
 static int gsi_function_ctrl_port_init(struct f_gsi *gsi)
 {
 	int ret;
+	char *cdev_name = NULL;
 	int sz = GSI_CTRL_NAME_LEN;
-	bool ctrl_dev_create = true;
 
 	INIT_LIST_HEAD(&gsi->c_port.cpkt_req_q);
 	INIT_LIST_HEAD(&gsi->c_port.cpkt_resp_q);
@@ -1338,17 +1352,30 @@
 
 	init_waitqueue_head(&gsi->c_port.read_wq);
 
-	if (gsi->prot_id == IPA_USB_RMNET)
-		strlcat(gsi->c_port.name, GSI_RMNET_CTRL_NAME, sz);
-	else if (gsi->prot_id == IPA_USB_MBIM)
-		strlcat(gsi->c_port.name, GSI_MBIM_CTRL_NAME, sz);
-	else if (gsi->prot_id == IPA_USB_DIAG)
-		strlcat(gsi->c_port.name, GSI_DPL_CTRL_NAME, sz);
-	else
-		ctrl_dev_create = false;
+	switch (gsi->prot_id) {
+	case USB_PROT_RMNET_IPA:
+		cdev_name = GSI_RMNET_CTRL_NAME;
+		break;
+	case USB_PROT_RMNET_ETHER:
+		cdev_name = ETHER_RMNET_CTRL_NAME;
+		break;
+	case USB_PROT_MBIM_IPA:
+		cdev_name = GSI_MBIM_CTRL_NAME;
+		break;
+	case USB_PROT_DIAG_IPA:
+		cdev_name = GSI_DPL_CTRL_NAME;
+		break;
+	case USB_PROT_DPL_ETHER:
+		cdev_name = ETHER_DPL_CTRL_NAME;
+		break;
+	default:
+		break;
+	}
 
-	if (!ctrl_dev_create)
+	if (!cdev_name)
 		return 0;
+	else
+		strlcat(gsi->c_port.name, cdev_name, sz);
 
 	gsi->c_port.ctrl_device.name = gsi->c_port.name;
 	gsi->c_port.ctrl_device.fops = &gsi_ctrl_dev_fops;
@@ -1512,7 +1539,7 @@
 		event->wValue = cpu_to_le16(0);
 		event->wLength = cpu_to_le16(0);
 
-		if (gsi->prot_id == IPA_USB_RNDIS) {
+		if (gsi->prot_id == USB_PROT_RNDIS_IPA) {
 			data = req->buf;
 			data[0] = cpu_to_le32(1);
 			data[1] = cpu_to_le32(0);
@@ -1737,7 +1764,7 @@
 		/* read the request; process it later */
 		value = w_length;
 		req->context = gsi;
-		if (gsi->prot_id == IPA_USB_RNDIS)
+		if (gsi->prot_id == USB_PROT_RNDIS_IPA)
 			req->complete = gsi_rndis_command_complete;
 		else
 			req->complete = gsi_ctrl_cmd_complete;
@@ -1749,7 +1776,7 @@
 		if (w_value || w_index != id)
 			goto invalid;
 
-		if (gsi->prot_id == IPA_USB_RNDIS) {
+		if (gsi->prot_id == USB_PROT_RNDIS_IPA) {
 			/* return the result */
 			buf = rndis_get_next_response(gsi->params, &n);
 			if (buf) {
@@ -1785,7 +1812,8 @@
 	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
 			| USB_CDC_REQ_SET_CONTROL_LINE_STATE:
 		line_state = (w_value & GSI_CTRL_DTR ? true : false);
-		if (gsi->prot_id == IPA_USB_RMNET)
+		if (gsi->prot_id == USB_PROT_RMNET_IPA ||
+				gsi->prot_id == USB_PROT_RMNET_ETHER)
 			gsi->rmnet_dtr_status = line_state;
 		log_event_dbg("%s: USB_CDC_REQ_SET_CONTROL_LINE_STATE DTR:%d\n",
 						__func__, line_state);
@@ -1882,9 +1910,10 @@
 	struct f_gsi *gsi = func_to_gsi(f);
 
 	/* RNDIS, RMNET and DPL only support alt 0*/
-	if (intf == gsi->ctrl_id || gsi->prot_id == IPA_USB_RNDIS ||
-			gsi->prot_id == IPA_USB_RMNET ||
-			gsi->prot_id == IPA_USB_DIAG)
+	if (intf == gsi->ctrl_id || gsi->prot_id == USB_PROT_RNDIS_IPA ||
+			gsi->prot_id == USB_PROT_RMNET_IPA ||
+			gsi->prot_id == USB_PROT_DIAG_IPA ||
+			is_ext_prot_ether(gsi->prot_id))
 		return 0;
 	else if (intf == gsi->data_id)
 		return gsi->data_interface_up;
@@ -2003,7 +2032,8 @@
 	log_event_dbg("intf=%u, alt=%u", intf, alt);
 
 	/* Control interface has only altsetting 0 */
-	if (intf == gsi->ctrl_id || gsi->prot_id == IPA_USB_RMNET) {
+	if (intf == gsi->ctrl_id || gsi->prot_id == USB_PROT_RMNET_IPA ||
+				gsi->prot_id == USB_PROT_RMNET_ETHER) {
 		if (alt != 0)
 			goto fail;
 
@@ -2037,10 +2067,11 @@
 	if (intf == gsi->data_id) {
 		gsi->d_port.net_ready_trigger = false;
 		/* for rndis and rmnet alt is always 0 update alt accordingly */
-		if (gsi->prot_id == IPA_USB_RNDIS ||
-				gsi->prot_id == IPA_USB_RMNET ||
-				gsi->prot_id == IPA_USB_DIAG)
-				alt = 1;
+		if (gsi->prot_id == USB_PROT_RNDIS_IPA ||
+				gsi->prot_id == USB_PROT_RMNET_IPA ||
+				gsi->prot_id == USB_PROT_DIAG_IPA ||
+				is_ext_prot_ether(gsi->prot_id))
+			alt = 1;
 
 		if (alt > 1)
 			goto notify_ep_disable;
@@ -2067,8 +2098,9 @@
 			}
 
 			/* Configure EPs for GSI */
-			if (gsi->d_port.in_ep) {
-				if (gsi->prot_id == IPA_USB_DIAG)
+			if (gsi->d_port.in_ep &&
+				gsi->prot_id <= USB_PROT_DIAG_IPA) {
+				if (gsi->prot_id == USB_PROT_DIAG_IPA)
 					gsi->d_port.in_ep->ep_intr_num = 3;
 				else
 					gsi->d_port.in_ep->ep_intr_num = 2;
@@ -2077,7 +2109,8 @@
 						GSI_EP_OP_CONFIG);
 			}
 
-			if (gsi->d_port.out_ep) {
+			if (gsi->d_port.out_ep &&
+				gsi->prot_id <= USB_PROT_DIAG_IPA) {
 				gsi->d_port.out_ep->ep_intr_num = 1;
 				usb_gsi_ep_op(gsi->d_port.out_ep,
 					&gsi->d_port.out_request,
@@ -2086,7 +2119,17 @@
 
 			gsi->d_port.gadget = cdev->gadget;
 
-			if (gsi->prot_id == IPA_USB_RNDIS) {
+			if (is_ext_prot_ether(gsi->prot_id)) {
+				net = gether_connect(&gsi->d_port.gether_port);
+				if (IS_ERR(net)) {
+					pr_err("%s:gether_connect err:%ld\n",
+					__func__, PTR_ERR(net));
+					goto notify_ep_disable;
+				}
+				gsi->d_port.gether_port.cdc_filter = 0;
+			}
+
+			if (gsi->prot_id == USB_PROT_RNDIS_IPA) {
 				gsi_rndis_open(gsi);
 				net = gsi_rndis_get_netdev("rndis0");
 				if (IS_ERR(net))
@@ -2098,7 +2141,7 @@
 						&gsi->d_port.cdc_filter);
 			}
 
-			if (gsi->prot_id == IPA_USB_ECM)
+			if (gsi->prot_id == USB_PROT_ECM_IPA)
 				gsi->d_port.cdc_filter = DEFAULT_FILTER;
 
 			/*
@@ -2106,7 +2149,7 @@
 			 * handler which is invoked when the host sends the
 			 * GEN_CURRENT_PACKET_FILTER message.
 			 */
-			if (gsi->prot_id != IPA_USB_RNDIS)
+			if (gsi->prot_id != USB_PROT_RNDIS_IPA)
 				post_event(&gsi->d_port,
 						EVT_CONNECT_IN_PROGRESS);
 			queue_work(gsi->d_port.ipa_usb_wq,
@@ -2127,7 +2170,8 @@
 	atomic_set(&gsi->connected, 1);
 
 	/* send 0 len pkt to qti to notify state change */
-	if (gsi->prot_id == IPA_USB_DIAG)
+	if (gsi->prot_id == USB_PROT_DIAG_IPA ||
+				gsi->prot_id == USB_PROT_DPL_ETHER)
 		gsi_ctrl_send_cpkt_tomodem(gsi, NULL, 0);
 
 	return 0;
@@ -2145,10 +2189,11 @@
 
 	atomic_set(&gsi->connected, 0);
 
-	if (gsi->prot_id == IPA_USB_RNDIS)
+	if (gsi->prot_id == USB_PROT_RNDIS_IPA)
 		rndis_uninit(gsi->params);
 
-	if (gsi->prot_id == IPA_USB_RMNET)
+	if (gsi->prot_id == USB_PROT_RMNET_IPA ||
+				gsi->prot_id == USB_PROT_RMNET_ETHER)
 		gsi->rmnet_dtr_status = false;
 
 	/* Disable Control Path */
@@ -2171,6 +2216,12 @@
 	gsi->data_interface_up = false;
 
 	log_event_dbg("%s deactivated", gsi->function.name);
+
+	if (is_ext_prot_ether(gsi->prot_id)) {
+		gether_disconnect(&gsi->d_port.gether_port);
+		return;
+	}
+
 	ipa_disconnect_handler(&gsi->d_port);
 	post_event(&gsi->d_port, EVT_DISCONNECTED);
 	queue_work(gsi->d_port.ipa_usb_wq, &gsi->d_port.usb_ipa_w);
@@ -2221,7 +2272,7 @@
 	 * RNDIS_MESSAGE_PACKET_FILTER after performing bus resume.
 	 * Trigger state machine explicitly on resume.
 	 */
-	if (gsi->prot_id == IPA_USB_RNDIS &&
+	if (gsi->prot_id == USB_PROT_RNDIS_IPA &&
 			!usb_gsi_remote_wakeup_allowed(f))
 		rndis_flow_control(gsi->params, false);
 
@@ -2327,7 +2378,7 @@
 		info->data_nop_desc->bInterfaceNumber = gsi->data_id;
 
 	/* allocate instance-specific endpoints */
-	if (info->fs_in_desc) {
+	if (info->fs_in_desc && gsi->prot_id <= USB_PROT_DIAG_IPA) {
 		ep = usb_ep_autoconfig_by_name(cdev->gadget,
 				info->fs_in_desc, info->in_epname);
 		if (!ep)
@@ -2335,9 +2386,17 @@
 		gsi->d_port.in_ep = ep;
 		msm_ep_config(gsi->d_port.in_ep, NULL);
 		ep->driver_data = cdev;	/* claim */
+	} else {
+		if (info->fs_in_desc) {
+			ep = usb_ep_autoconfig(cdev->gadget, info->fs_in_desc);
+			if (!ep)
+				goto fail;
+			gsi->d_port.in_ep = ep;
+			ep->driver_data = cdev; /* claim */
+		}
 	}
 
-	if (info->fs_out_desc) {
+	if (info->fs_out_desc && gsi->prot_id <= USB_PROT_DIAG_IPA) {
 		ep = usb_ep_autoconfig_by_name(cdev->gadget,
 				info->fs_out_desc, info->out_epname);
 		if (!ep)
@@ -2345,6 +2404,14 @@
 		gsi->d_port.out_ep = ep;
 		msm_ep_config(gsi->d_port.out_ep, NULL);
 		ep->driver_data = cdev;	/* claim */
+	} else {
+		if (info->fs_out_desc) {
+			ep = usb_ep_autoconfig(cdev->gadget, info->fs_out_desc);
+			if (!ep)
+				goto fail;
+			gsi->d_port.out_ep = ep;
+			ep->driver_data = cdev; /* claim */
+		}
 	}
 
 	if (info->fs_notify_desc) {
@@ -2475,14 +2542,17 @@
 	struct gsi_function_bind_info info = {0};
 	struct f_gsi *gsi = func_to_gsi(f);
 	struct rndis_params *params;
+	struct net_device *net;
+	char *name = NULL;
 	int status;
 	__u8  class;
 	__u8  subclass;
 	__u8  proto;
 
 
-	if (gsi->prot_id == IPA_USB_RMNET ||
-		gsi->prot_id == IPA_USB_DIAG)
+	if (gsi->prot_id == USB_PROT_RMNET_IPA ||
+		gsi->prot_id == USB_PROT_DIAG_IPA ||
+		is_ext_prot_ether(gsi->prot_id))
 		gsi->ctrl_id = -ENODEV;
 	else {
 		status = gsi->ctrl_id = usb_interface_id(c, f);
@@ -2495,7 +2565,7 @@
 		goto fail;
 
 	switch (gsi->prot_id) {
-	case IPA_USB_RNDIS:
+	case USB_PROT_RNDIS_IPA:
 		info.string_defs = rndis_gsi_string_defs;
 		info.ctrl_desc = &rndis_gsi_control_intf;
 		info.ctrl_str_idx = 0;
@@ -2641,7 +2711,7 @@
 		info.ctrl_desc->bInterfaceProtocol = proto;
 
 		break;
-	case IPA_USB_MBIM:
+	case USB_PROT_MBIM_IPA:
 		info.string_defs = mbim_gsi_string_defs;
 		info.ctrl_desc = &mbim_gsi_control_intf;
 		info.ctrl_str_idx = 0;
@@ -2688,7 +2758,8 @@
 				c->bConfigurationValue + '0';
 		}
 		break;
-	case IPA_USB_RMNET:
+	case USB_PROT_RMNET_IPA:
+	case USB_PROT_RMNET_ETHER:
 		info.string_defs = rmnet_gsi_string_defs;
 		info.data_desc = &rmnet_gsi_interface_desc;
 		info.data_str_idx = 0;
@@ -2713,8 +2784,9 @@
 		info.out_req_buf_len = GSI_OUT_RMNET_BUF_LEN;
 		info.out_req_num_buf = GSI_NUM_OUT_BUFFERS;
 		info.notify_buf_len = sizeof(struct usb_cdc_notification);
+		name = "usb_rmnet";
 		break;
-	case IPA_USB_ECM:
+	case USB_PROT_ECM_IPA:
 		info.string_defs = ecm_gsi_string_defs;
 		info.ctrl_desc = &ecm_gsi_control_intf;
 		info.ctrl_str_idx = 0;
@@ -2763,7 +2835,8 @@
 		gsi->d_port.ipa_init_params.host_ethaddr[5]);
 		info.string_defs[1].s = gsi->ethaddr;
 		break;
-	case IPA_USB_DIAG:
+	case USB_PROT_DIAG_IPA:
+	case USB_PROT_DPL_ETHER:
 		info.string_defs = qdss_gsi_string_defs;
 		info.data_desc = &qdss_gsi_data_intf_desc;
 		info.data_str_idx = 0;
@@ -2778,6 +2851,7 @@
 		info.in_req_buf_len = 16384;
 		info.in_req_num_buf = GSI_NUM_IN_BUFFERS;
 		info.notify_buf_len = sizeof(struct usb_cdc_notification);
+		name = "dpl_usb";
 		break;
 	default:
 		log_event_err("%s: Invalid prot id %d", __func__,
@@ -2789,6 +2863,29 @@
 	if (status)
 		goto dereg_rndis;
 
+	if (is_ext_prot_ether(gsi->prot_id)) {
+		if (!name)
+			return -EINVAL;
+
+		gsi->d_port.gether_port.in_ep = gsi->d_port.in_ep;
+		gsi->d_port.gether_port.out_ep = gsi->d_port.out_ep;
+			net = gether_setup_name_default(name);
+			if (IS_ERR(net)) {
+				pr_err("%s: gether_setup failed\n", __func__);
+				return PTR_ERR(net);
+			}
+			gsi->d_port.gether_port.ioport = netdev_priv(net);
+			gether_set_gadget(net, c->cdev->gadget);
+			status = gether_register_netdev(net);
+			if (status < 0) {
+				pr_err("%s: gether_register_netdev failed\n",
+					__func__);
+				free_netdev(net);
+				return status;
+			}
+		goto skip_ipa_init;
+	}
+
 	status = ipa_register_ipa_ready_cb(ipa_ready_callback, gsi);
 	if (!status) {
 		log_event_info("%s: ipa is not ready", __func__);
@@ -2814,6 +2911,7 @@
 
 	gsi->d_port.sm_state = STATE_INITIALIZED;
 
+skip_ipa_init:
 	DBG(cdev, "%s: %s speed IN/%s OUT/%s NOTIFY/%s\n",
 			f->name,
 			gadget_is_superspeed(c->cdev->gadget) ? "super" :
@@ -2836,6 +2934,12 @@
 {
 	struct f_gsi *gsi = func_to_gsi(f);
 
+	if (is_ext_prot_ether(gsi->prot_id)) {
+		gether_cleanup(gsi->d_port.gether_port.ioport);
+		gsi->d_port.gether_port.ioport = NULL;
+		goto skip_ipa_dinit;
+	}
+
 	/*
 	 * Use drain_workqueue to accomplish below conditions:
 	 * 1. Make sure that any running work completed
@@ -2847,12 +2951,13 @@
 	drain_workqueue(gsi->d_port.ipa_usb_wq);
 	ipa_usb_deinit_teth_prot(gsi->prot_id);
 
-	if (gsi->prot_id == IPA_USB_RNDIS) {
+skip_ipa_dinit:
+	if (gsi->prot_id == USB_PROT_RNDIS_IPA) {
 		gsi->d_port.sm_state = STATE_UNINITIALIZED;
 		rndis_deregister(gsi->params);
 	}
 
-	if (gsi->prot_id == IPA_USB_MBIM)
+	if (gsi->prot_id == USB_PROT_MBIM_IPA)
 		mbim_gsi_ext_config_desc.function.subCompatibleID[0] = 0;
 
 	if (gadget_is_superspeed(c->cdev->gadget)) {
@@ -2883,33 +2988,34 @@
 static int gsi_bind_config(struct f_gsi *gsi)
 {
 	int status = 0;
-	enum ipa_usb_teth_prot prot_id = gsi->prot_id;
 
-	log_event_dbg("%s: prot id %d", __func__, prot_id);
+	log_event_dbg("%s: prot id %d", __func__, gsi->prot_id);
 
-	switch (prot_id) {
-	case IPA_USB_RNDIS:
+	switch (gsi->prot_id) {
+	case USB_PROT_RNDIS_IPA:
 		gsi->function.name = "rndis";
 		gsi->function.strings = rndis_gsi_strings;
 		break;
-	case IPA_USB_ECM:
+	case USB_PROT_ECM_IPA:
 		gsi->function.name = "cdc_ethernet";
 		gsi->function.strings = ecm_gsi_strings;
 		break;
-	case IPA_USB_RMNET:
+	case USB_PROT_RMNET_IPA:
+	case USB_PROT_RMNET_ETHER:
 		gsi->function.name = "rmnet";
 		gsi->function.strings = rmnet_gsi_strings;
 		break;
-	case IPA_USB_MBIM:
+	case USB_PROT_MBIM_IPA:
 		gsi->function.name = "mbim";
 		gsi->function.strings = mbim_gsi_strings;
 		break;
-	case IPA_USB_DIAG:
+	case USB_PROT_DIAG_IPA:
+	case USB_PROT_DPL_ETHER:
 		gsi->function.name = "dpl";
 		gsi->function.strings = qdss_gsi_strings;
 		break;
 	default:
-		log_event_err("%s: invalid prot id %d", __func__, prot_id);
+		log_event_err("%s: invalid prot id %d", __func__, gsi->prot_id);
 		return -EINVAL;
 	}
 
@@ -3164,9 +3270,10 @@
 static int gsi_set_inst_name(struct usb_function_instance *fi,
 	const char *name)
 {
-	int prot_id, name_len, ret = 0;
+	int name_len, ret = 0;
 	struct gsi_opts *opts;
 	struct f_gsi *gsi;
+	enum usb_prot_id prot_id;
 
 	opts = container_of(fi, struct gsi_opts, func_inst);
 
@@ -3181,7 +3288,7 @@
 		return -EINVAL;
 	}
 
-	if (prot_id == IPA_USB_RNDIS)
+	if (prot_id == USB_PROT_RNDIS_IPA)
 		config_group_init_type_name(&opts->func_inst.group, "",
 					    &gsi_func_rndis_type);
 
@@ -3249,7 +3356,7 @@
 		return -ENOMEM;
 	}
 
-	for (i = 0; i < IPA_USB_MAX_TETH_PROT_SIZE; i++) {
+	for (i = 0; i < USB_PROT_MAX; i++) {
 		__gsi[i] = gsi_function_init();
 		if (IS_ERR(__gsi[i]))
 			return PTR_ERR(__gsi[i]);
@@ -3272,7 +3379,7 @@
 	if (ipc_log_ctxt)
 		ipc_log_context_destroy(ipc_log_ctxt);
 
-	for (i = 0; i < IPA_USB_MAX_TETH_PROT_SIZE; i++)
+	for (i = 0; i < USB_PROT_MAX; i++)
 		kfree(__gsi[i]);
 
 	usb_function_unregister(&gsiusb_func);
diff --git a/drivers/usb/gadget/function/f_gsi.h b/drivers/usb/gadget/function/f_gsi.h
index c6e64fd..7e998c4 100644
--- a/drivers/usb/gadget/function/f_gsi.h
+++ b/drivers/usb/gadget/function/f_gsi.h
@@ -27,9 +27,14 @@
 #include <linux/ipa_usb.h>
 #include <linux/ipc_logging.h>
 
+#include "u_ether.h"
+
 #define GSI_RMNET_CTRL_NAME "rmnet_ctrl"
 #define GSI_MBIM_CTRL_NAME "android_mbim"
 #define GSI_DPL_CTRL_NAME "dpl_ctrl"
+#define ETHER_RMNET_CTRL_NAME "rmnet_ctrl0"
+#define ETHER_DPL_CTRL_NAME "dpl_ctrl0"
+
 #define GSI_CTRL_NAME_LEN (sizeof(GSI_MBIM_CTRL_NAME)+2)
 #define GSI_MAX_CTRL_PKT_SIZE 4096
 #define GSI_CTRL_DTR (1 << 0)
@@ -114,6 +119,21 @@
 	RNDIS_ID_MAX,
 };
 
+enum usb_prot_id {
+	/* accelerated: redefined from ipa_usb.h, do not change order */
+	USB_PROT_RNDIS_IPA,
+	USB_PROT_ECM_IPA,
+	USB_PROT_RMNET_IPA,
+	USB_PROT_MBIM_IPA,
+	USB_PROT_DIAG_IPA,
+
+	/* non-accelerated */
+	USB_PROT_RMNET_ETHER,
+	USB_PROT_DPL_ETHER,
+
+	USB_PROT_MAX,
+};
+
 #define MAXQUEUELEN 128
 struct event_queue {
 	u8 event[MAXQUEUELEN];
@@ -228,6 +248,7 @@
 	enum connection_state sm_state;
 	struct event_queue evt_q;
 	wait_queue_head_t wait_for_ipa_ready;
+	struct gether gether_port;
 
 	/* Track these for debugfs */
 	struct ipa_usb_xdci_chan_params ipa_in_channel_params;
@@ -237,7 +258,7 @@
 
 struct f_gsi {
 	struct usb_function function;
-	enum ipa_usb_teth_prot prot_id;
+	enum usb_prot_id prot_id;
 	int ctrl_id;
 	int data_id;
 	u32 vendorID;
@@ -285,21 +306,25 @@
 			    func_inst.group);
 }
 
-static enum ipa_usb_teth_prot name_to_prot_id(const char *name)
+static enum usb_prot_id name_to_prot_id(const char *name)
 {
 	if (!name)
 		goto error;
 
 	if (!strncasecmp(name, "rndis", MAX_INST_NAME_LEN))
-		return IPA_USB_RNDIS;
+		return USB_PROT_RNDIS_IPA;
 	if (!strncasecmp(name, "ecm", MAX_INST_NAME_LEN))
-		return IPA_USB_ECM;
+		return USB_PROT_ECM_IPA;
 	if (!strncasecmp(name, "rmnet", MAX_INST_NAME_LEN))
-		return IPA_USB_RMNET;
+		return USB_PROT_RMNET_IPA;
 	if (!strncasecmp(name, "mbim", MAX_INST_NAME_LEN))
-		return IPA_USB_MBIM;
+		return USB_PROT_MBIM_IPA;
 	if (!strncasecmp(name, "dpl", MAX_INST_NAME_LEN))
-		return IPA_USB_DIAG;
+		return USB_PROT_DIAG_IPA;
+	if (!strncasecmp(name, "rmnet.ether", MAX_INST_NAME_LEN))
+		return USB_PROT_RMNET_ETHER;
+	if (!strncasecmp(name, "dpl.ether", MAX_INST_NAME_LEN))
+		return USB_PROT_DPL_ETHER;
 
 error:
 	return -EINVAL;
diff --git a/drivers/usb/gadget/function/f_mtp.c b/drivers/usb/gadget/function/f_mtp.c
index 651776d..3f25946 100644
--- a/drivers/usb/gadget/function/f_mtp.c
+++ b/drivers/usb/gadget/function/f_mtp.c
@@ -613,7 +613,17 @@
 		return -EINVAL;
 
 	spin_lock_irq(&dev->lock);
+	if (dev->state == STATE_OFFLINE) {
+		spin_unlock_irq(&dev->lock);
+		return -ENODEV;
+	}
+
 	if (dev->ep_out->desc) {
+		if (!cdev) {
+			spin_unlock_irq(&dev->lock);
+			return -ENODEV;
+		}
+
 		len = usb_ep_align_maybe(cdev->gadget, dev->ep_out, count);
 		if (len > MTP_BULK_BUFFER_SIZE) {
 			spin_unlock_irq(&dev->lock);
@@ -1498,7 +1508,10 @@
 	while ((req = mtp_req_get(dev, &dev->intr_idle)))
 		mtp_request_free(req, dev->ep_intr);
 	mutex_unlock(&dev->read_mutex);
+	spin_lock_irq(&dev->lock);
 	dev->state = STATE_OFFLINE;
+	dev->cdev = NULL;
+	spin_unlock_irq(&dev->lock);
 	kfree(f->os_desc_table);
 	f->os_desc_n = 0;
 	fi_mtp->func_inst.f = NULL;
@@ -1554,7 +1567,9 @@
 	struct usb_composite_dev	*cdev = dev->cdev;
 
 	DBG(cdev, "mtp_function_disable\n");
+	spin_lock_irq(&dev->lock);
 	dev->state = STATE_OFFLINE;
+	spin_unlock_irq(&dev->lock);
 	usb_ep_disable(dev->ep_in);
 	usb_ep_disable(dev->ep_out);
 	usb_ep_disable(dev->ep_intr);
diff --git a/drivers/usb/gadget/function/f_qdss.c b/drivers/usb/gadget/function/f_qdss.c
index 0ce2c407..312ae24 100644
--- a/drivers/usb/gadget/function/f_qdss.c
+++ b/drivers/usb/gadget/function/f_qdss.c
@@ -1205,7 +1205,7 @@
 	return &usb_qdss->port.function;
 }
 
-DECLARE_USB_FUNCTION_INIT(qdss, qdss_alloc_inst, qdss_alloc);
+DECLARE_USB_FUNCTION(qdss, qdss_alloc_inst, qdss_alloc);
 static int __init usb_qdss_init(void)
 {
 	int ret;
diff --git a/drivers/usb/phy/phy-msm-qusb.c b/drivers/usb/phy/phy-msm-qusb.c
index 9c33c6e..47db12b 100644
--- a/drivers/usb/phy/phy-msm-qusb.c
+++ b/drivers/usb/phy/phy-msm-qusb.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2018, 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
@@ -706,7 +706,9 @@
 				qphy->base + QUSB2PHY_PORT_INTR_CTRL);
 
 			/* Disable PHY */
-			writel_relaxed(POWER_DOWN,
+			writel_relaxed(POWER_DOWN |
+				readl_relaxed(qphy->base +
+					QUSB2PHY_PORT_POWERDOWN),
 				qphy->base + QUSB2PHY_PORT_POWERDOWN);
 			/* Make sure that above write is completed */
 			wmb();
diff --git a/drivers/usb/phy/phy-msm-ssusb-qmp.c b/drivers/usb/phy/phy-msm-ssusb-qmp.c
index ad3bbf7..1795d24 100644
--- a/drivers/usb/phy/phy-msm-ssusb-qmp.c
+++ b/drivers/usb/phy/phy-msm-ssusb-qmp.c
@@ -25,10 +25,10 @@
 #include <linux/clk.h>
 #include <linux/reset.h>
 
-enum core_ldo_levels {
-	CORE_LEVEL_NONE = 0,
-	CORE_LEVEL_MIN,
-	CORE_LEVEL_MAX,
+enum ldo_levels {
+	VOLTAGE_LEVEL_NONE = 0,
+	VOLTAGE_LEVEL_MIN,
+	VOLTAGE_LEVEL_MAX,
 };
 
 #define INIT_MAX_TIME_USEC			1000
@@ -38,6 +38,8 @@
 #define USB_SSPHY_1P2_VOL_MAX		1200000 /* uV */
 #define USB_SSPHY_HPM_LOAD		23000	/* uA */
 
+#define USB_SSPHY_LOAD_DEFAULT		-1
+
 /* USB3PHY_PCIE_USB3_PCS_PCS_STATUS bit */
 #define PHYSTATUS				BIT(6)
 
@@ -119,6 +121,9 @@
 	int			vdd_levels[3]; /* none, low, high */
 	struct regulator	*core_ldo;
 	int			core_voltage_levels[3];
+	struct regulator	*fpc_redrive_ldo;
+	int			redrive_voltage_levels[3];
+	int			redrive_load;
 	struct clk		*ref_clk_src;
 	struct clk		*ref_clk;
 	struct clk		*aux_clk;
@@ -226,6 +231,33 @@
 	}
 }
 
+static int msm_ldo_enable(struct msm_ssphy_qmp *phy,
+		struct regulator *ldo, int *voltage_levels, int load)
+{
+	int ret = 0;
+
+	dev_dbg(phy->phy.dev,
+		"ldo: min_vol:%duV max_vol:%duV\n",
+		voltage_levels[VOLTAGE_LEVEL_MIN],
+		voltage_levels[VOLTAGE_LEVEL_MAX]);
+
+	if (load > 0) {
+		ret = regulator_set_load(ldo, load);
+		if (ret < 0)
+			return ret;
+	}
+
+	ret = regulator_set_voltage(ldo,
+			voltage_levels[VOLTAGE_LEVEL_MIN],
+			voltage_levels[VOLTAGE_LEVEL_MAX]);
+	if (ret)
+		return ret;
+
+	ret = regulator_enable(ldo);
+
+	return ret;
+}
+
 static int msm_ssusb_qmp_ldo_enable(struct msm_ssphy_qmp *phy, int on)
 {
 	int min, rc = 0;
@@ -245,74 +277,65 @@
 	if (!on)
 		goto disable_regulators;
 
-	rc = regulator_set_voltage(phy->vdd, phy->vdd_levels[min],
-				    phy->vdd_levels[2]);
-	if (rc) {
-		dev_err(phy->phy.dev, "unable to set voltage for ssusb vdd\n");
-		return rc;
+	if (phy->fpc_redrive_ldo) {
+		rc = msm_ldo_enable(phy, phy->fpc_redrive_ldo,
+				phy->redrive_voltage_levels,
+				phy->redrive_load);
+		if (rc < 0) {
+			dev_err(phy->phy.dev,
+				"enable phy->fpc_redrive_ldo failed\n");
+			return rc;
+		}
+
+		dev_dbg(phy->phy.dev,
+			"fpc redrive ldo: min_vol:%duV max_vol:%duV\n",
+			phy->redrive_voltage_levels[VOLTAGE_LEVEL_MIN],
+			phy->redrive_voltage_levels[VOLTAGE_LEVEL_MAX]);
 	}
 
-	dev_dbg(phy->phy.dev, "min_vol:%d max_vol:%d\n",
-		phy->vdd_levels[min], phy->vdd_levels[2]);
-
-	rc = regulator_enable(phy->vdd);
-	if (rc) {
-		dev_err(phy->phy.dev,
-			"regulator_enable(phy->vdd) failed, ret=%d",
-			rc);
-		goto unconfig_vdd;
-	}
-
-	rc = regulator_set_load(phy->core_ldo, USB_SSPHY_HPM_LOAD);
+	rc = msm_ldo_enable(phy, phy->vdd, phy->vdd_levels,
+			USB_SSPHY_LOAD_DEFAULT);
 	if (rc < 0) {
-		dev_err(phy->phy.dev, "Unable to set HPM of core_ldo\n");
+		dev_err(phy->phy.dev, "enable phy->vdd failed\n");
+		goto disable_fpc_redrive;
+	}
+
+	dev_dbg(phy->phy.dev,
+		"vdd ldo: min_vol:%duV max_vol:%duV\n",
+		phy->vdd_levels[VOLTAGE_LEVEL_MIN],
+		phy->vdd_levels[VOLTAGE_LEVEL_MAX]);
+
+	rc = msm_ldo_enable(phy, phy->core_ldo, phy->core_voltage_levels,
+			USB_SSPHY_HPM_LOAD);
+	if (rc < 0) {
+		dev_err(phy->phy.dev, "enable phy->core_ldo failed\n");
 		goto disable_vdd;
 	}
 
-	rc = regulator_set_voltage(phy->core_ldo,
-			phy->core_voltage_levels[CORE_LEVEL_MIN],
-			phy->core_voltage_levels[CORE_LEVEL_MAX]);
-	if (rc) {
-		dev_err(phy->phy.dev, "unable to set voltage for core_ldo\n");
-		goto put_core_ldo_lpm;
-	}
-
-	rc = regulator_enable(phy->core_ldo);
-	if (rc) {
-		dev_err(phy->phy.dev, "Unable to enable core_ldo\n");
-		goto unset_core_ldo;
-	}
+	dev_dbg(phy->phy.dev,
+		"core ldo: min_vol:%duV max_vol:%duV\n",
+		phy->core_voltage_levels[VOLTAGE_LEVEL_MIN],
+		phy->core_voltage_levels[VOLTAGE_LEVEL_MAX]);
 
 	return 0;
 
 disable_regulators:
 	rc = regulator_disable(phy->core_ldo);
 	if (rc)
-		dev_err(phy->phy.dev, "Unable to disable core_ldo\n");
-
-unset_core_ldo:
-	rc = regulator_set_voltage(phy->core_ldo,
-			phy->core_voltage_levels[CORE_LEVEL_NONE],
-			phy->core_voltage_levels[CORE_LEVEL_MAX]);
-	if (rc)
-		dev_err(phy->phy.dev, "unable to set voltage for core_ldo\n");
-
-put_core_ldo_lpm:
-	rc = regulator_set_load(phy->core_ldo, 0);
-	if (rc < 0)
-		dev_err(phy->phy.dev, "Unable to set LPM of core_ldo\n");
+		dev_err(phy->phy.dev, "disable phy->core_ldo failed\n");
 
 disable_vdd:
 	rc = regulator_disable(phy->vdd);
 	if (rc)
-		dev_err(phy->phy.dev, "regulator_disable(phy->vdd) failed, ret=%d",
-			rc);
+		dev_err(phy->phy.dev, "disable phy->vdd failed\n");
 
-unconfig_vdd:
-	rc = regulator_set_voltage(phy->vdd, phy->vdd_levels[min],
-				    phy->vdd_levels[2]);
-	if (rc)
-		dev_err(phy->phy.dev, "unable to set voltage for ssusb vdd\n");
+disable_fpc_redrive:
+	if (phy->fpc_redrive_ldo) {
+		rc = regulator_disable(phy->fpc_redrive_ldo);
+		if (rc)
+			dev_err(phy->phy.dev,
+				"disable phy->fpc_redrive_ldo failed\n");
+	}
 
 	return rc < 0 ? rc : 0;
 }
@@ -634,6 +657,9 @@
 	if (suspend) {
 		if (phy->cable_connected)
 			msm_ssusb_qmp_enable_autonomous(phy, 1);
+		else
+			writel_relaxed(0x00,
+			phy->base + phy->phy_reg[USB3_PHY_POWER_DOWN_CONTROL]);
 
 		/* Make sure above write completed with PHY */
 		wmb();
@@ -944,9 +970,9 @@
 	}
 
 	/* Set default core voltage values */
-	phy->core_voltage_levels[CORE_LEVEL_NONE] = 0;
-	phy->core_voltage_levels[CORE_LEVEL_MIN] = USB_SSPHY_1P2_VOL_MIN;
-	phy->core_voltage_levels[CORE_LEVEL_MAX] = USB_SSPHY_1P2_VOL_MAX;
+	phy->core_voltage_levels[VOLTAGE_LEVEL_NONE] = 0;
+	phy->core_voltage_levels[VOLTAGE_LEVEL_MIN] = USB_SSPHY_1P2_VOL_MIN;
+	phy->core_voltage_levels[VOLTAGE_LEVEL_MAX] = USB_SSPHY_1P2_VOL_MAX;
 
 	if (of_get_property(dev->of_node, "qcom,core-voltage-level", &len) &&
 		len == sizeof(phy->core_voltage_levels)) {
@@ -990,6 +1016,39 @@
 		goto err;
 	}
 
+	phy->fpc_redrive_ldo = devm_regulator_get_optional(dev, "fpc-redrive");
+	if (IS_ERR(phy->fpc_redrive_ldo)) {
+		phy->fpc_redrive_ldo = NULL;
+		dev_dbg(dev, "no FPC re-drive ldo regulator\n");
+	} else {
+		if (of_get_property(dev->of_node,
+				"qcom,redrive-voltage-level", &len) &&
+				len == sizeof(phy->redrive_voltage_levels)) {
+			ret = of_property_read_u32_array(dev->of_node,
+					"qcom,redrive-voltage-level",
+					(u32 *) phy->redrive_voltage_levels,
+					len / sizeof(u32));
+			if (ret) {
+				dev_err(dev,
+					"err qcom,redrive-voltage-level\n");
+				goto err;
+			}
+		} else {
+			ret = -EINVAL;
+			dev_err(dev, "err inputs for redrive-voltage-level\n");
+			goto err;
+		}
+
+		ret = of_property_read_u32(dev->of_node, "qcom,redrive-load",
+				&phy->redrive_load);
+		if (ret) {
+			dev_err(&pdev->dev, "unable to read redrive load\n");
+			goto err;
+		}
+
+		dev_dbg(dev, "Get FPC re-drive ldo regulator\n");
+	}
+
 	platform_set_drvdata(pdev, phy);
 
 	if (of_property_read_bool(dev->of_node, "qcom,vbus-valid-override"))
diff --git a/drivers/video/fbdev/msm/mdp3_ctrl.c b/drivers/video/fbdev/msm/mdp3_ctrl.c
index 2d13210..50b232a 100644
--- a/drivers/video/fbdev/msm/mdp3_ctrl.c
+++ b/drivers/video/fbdev/msm/mdp3_ctrl.c
@@ -174,6 +174,7 @@
 		return;
 
 	mutex_lock(&session->lock);
+	MDSS_XLOG(0x111);
 	if (session->vsync_enabled ||
 		atomic_read(&session->vsync_countdown) > 0) {
 		mutex_unlock(&session->lock);
@@ -182,10 +183,17 @@
 		return;
 	}
 
+	if (!session->clk_on) {
+		mutex_unlock(&session->lock);
+		pr_debug("%s: Clk shut down is done\n", __func__);
+		MDSS_XLOG(XLOG_FUNC_EXIT, __LINE__);
+		return;
+	}
 	if (session->intf->active) {
 retry_dma_done:
 		rc = wait_for_completion_timeout(&session->dma_completion,
 							WAIT_DMA_TIMEOUT);
+		MDSS_XLOG(0x222);
 		if (rc <= 0) {
 			struct mdss_panel_data *panel;
 
@@ -196,6 +204,7 @@
 				if (--retry_count) {
 					pr_err("dmap is busy, retry %d\n",
 						retry_count);
+					MDSS_XLOG(__LINE__, retry_count);
 					goto retry_dma_done;
 				}
 				pr_err("dmap is still busy, bug_on\n");
@@ -380,7 +389,7 @@
 		return -EFAULT;
 	p_req = p + sizeof(req_list_header);
 	count = req_list_header.count;
-	if (count < 0 || count >= MAX_BLIT_REQ)
+	if (count < 0 || count > MAX_BLIT_REQ)
 		return -EINVAL;
 	rc = mdp3_ppp_parse_req(p_req, &req_list_header, 1);
 	if (!rc)
@@ -399,7 +408,7 @@
 		return -EFAULT;
 	p_req = p + sizeof(struct mdp_blit_req_list);
 	count = req_list_header.count;
-	if (count < 0 || count >= MAX_BLIT_REQ)
+	if (count < 0 || count > MAX_BLIT_REQ)
 		return -EINVAL;
 	req_list_header.sync.acq_fen_fd_cnt = 0;
 	rc = mdp3_ppp_parse_req(p_req, &req_list_header, 0);
@@ -661,8 +670,11 @@
 	case MDP_RGB_888:
 		format = MDP3_DMA_IBUF_FORMAT_RGB888;
 		break;
+	case MDP_XRGB_8888:
 	case MDP_ARGB_8888:
 	case MDP_RGBA_8888:
+	case MDP_BGRA_8888:
+	case MDP_RGBX_8888:
 		format = MDP3_DMA_IBUF_FORMAT_XRGB8888;
 		break;
 	default:
@@ -1014,8 +1026,10 @@
 	MDSS_XLOG(XLOG_FUNC_ENTRY, __LINE__, mdss_fb_is_power_on_ulp(mfd),
 		mfd->panel_power_state);
 	panel = mdp3_session->panel;
-	mutex_lock(&mdp3_session->lock);
 
+	cancel_work_sync(&mdp3_session->clk_off_work);
+	mutex_lock(&mdp3_session->lock);
+	MDSS_XLOG(0x111);
 	pr_debug("Requested power state = %d\n", mfd->panel_power_state);
 	if (mdss_fb_is_power_on_lp(mfd)) {
 		/*
@@ -1482,6 +1496,7 @@
 	}
 	mutex_unlock(&mdp3_res->fs_idle_pc_lock);
 
+	cancel_work_sync(&mdp3_session->clk_off_work);
 	mutex_lock(&mdp3_session->lock);
 
 	if (!mdp3_session->status) {
@@ -1489,7 +1504,7 @@
 		mutex_unlock(&mdp3_session->lock);
 		return -EPERM;
 	}
-
+	MDSS_XLOG(0x111);
 	mdp3_ctrl_notify(mdp3_session, MDP_NOTIFY_FRAME_BEGIN);
 	data = mdp3_bufq_pop(&mdp3_session->bufq_in);
 	if (data) {
@@ -1521,7 +1536,7 @@
 			}
 		}
 		mdp3_session->dma_active = 1;
-		init_completion(&mdp3_session->dma_completion);
+		reinit_completion(&mdp3_session->dma_completion);
 		mdp3_ctrl_notify(mdp3_session, MDP_NOTIFY_FRAME_FLUSHED);
 		mdp3_bufq_push(&mdp3_session->bufq_out, data);
 	}
@@ -1670,7 +1685,7 @@
 			}
 		}
 		mdp3_session->dma_active = 1;
-		init_completion(&mdp3_session->dma_completion);
+		reinit_completion(&mdp3_session->dma_completion);
 		mdp3_ctrl_notify(mdp3_session, MDP_NOTIFY_FRAME_FLUSHED);
 	} else {
 		pr_debug("mdp3_ctrl_pan_display no memory, stop interface");
@@ -2711,6 +2726,8 @@
 		}
 		mutex_unlock(&mdp3_res->fs_idle_pc_lock);
 		rc = mdp3_ctrl_async_blit_req(mfd, argp);
+		if (!rc)
+			cancel_work_sync(&mdp3_session->clk_off_work);
 		break;
 	case MSMFB_BLIT:
 		mutex_lock(&mdp3_res->fs_idle_pc_lock);
@@ -2718,6 +2735,8 @@
 			mdp3_ctrl_reset(mfd);
 		mutex_unlock(&mdp3_res->fs_idle_pc_lock);
 		rc = mdp3_ctrl_blit_req(mfd, argp);
+		if (!rc)
+			cancel_work_sync(&mdp3_session->clk_off_work);
 		break;
 	case MSMFB_METADATA_GET:
 		rc = copy_from_user(&metadata, argp, sizeof(metadata));
diff --git a/drivers/video/fbdev/msm/mdp3_dma.c b/drivers/video/fbdev/msm/mdp3_dma.c
index 089d32d..71fcbf9 100644
--- a/drivers/video/fbdev/msm/mdp3_dma.c
+++ b/drivers/video/fbdev/msm/mdp3_dma.c
@@ -665,6 +665,7 @@
 	ATRACE_BEGIN(__func__);
 	pr_debug("mdp3_dmap_update\n");
 
+	MDSS_XLOG(XLOG_FUNC_ENTRY, __LINE__);
 	if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) {
 		cb_type = MDP3_DMA_CALLBACK_TYPE_DMA_DONE;
 		if (intf->active) {
@@ -757,6 +758,7 @@
 	unsigned long flag;
 	int cb_type = MDP3_DMA_CALLBACK_TYPE_VSYNC;
 
+	MDSS_XLOG(XLOG_FUNC_ENTRY, __LINE__);
 	if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) {
 		cb_type = MDP3_DMA_CALLBACK_TYPE_DMA_DONE;
 		if (intf->active)
@@ -965,6 +967,8 @@
 	val = MDP3_REG_READ(MDP3_REG_DISPLAY_STATUS);
 	pr_err("%s DMAP Status %s\n", __func__,
 		(val & MDP3_DMA_P_BUSY_BIT) ? "BUSY":"IDLE");
+	MDSS_XLOG(XLOG_FUNC_ENTRY, __LINE__,
+		(val & MDP3_DMA_P_BUSY_BIT) ? 1:0);
 	return val & MDP3_DMA_P_BUSY_BIT;
 }
 
@@ -1056,7 +1060,7 @@
 	MDP3_REG_WRITE(MDP3_REG_INTR_ENABLE, 0);
 	MDP3_REG_WRITE(MDP3_REG_INTR_CLEAR, 0xfffffff);
 
-	init_completion(&dma->dma_comp);
+	reinit_completion(&dma->dma_comp);
 	dma->vsync_client.handler = NULL;
 	return ret;
 }
diff --git a/drivers/video/fbdev/msm/mdp3_ppp.c b/drivers/video/fbdev/msm/mdp3_ppp.c
index 6095073..3391059 100644
--- a/drivers/video/fbdev/msm/mdp3_ppp.c
+++ b/drivers/video/fbdev/msm/mdp3_ppp.c
@@ -1462,6 +1462,18 @@
 			(!(check_if_rgb(bg_req.src.format))) &&
 			(!(hw_woraround_active))) {
 			/*
+			 * Disable SMART blit for BG(YUV) layer when
+			 * Scaling on BG layer
+			 * Rotation on BG layer
+			 * UD flip on BG layer
+			 */
+			if ((is_scaling_needed(bg_req)) && (
+				bg_req.flags & MDP_ROT_90) &&
+				(bg_req.flags & MDP_FLIP_UD)) {
+				pr_debug("YUV layer with ROT+UD_FLIP+Scaling Not supported\n");
+				return false;
+			}
+			/*
 			 * swap blit requests at index 0 and 1. YUV layer at
 			 * index 0 is replaced with UI layer request present
 			 * at index 1. Since UI layer will be in  background
diff --git a/drivers/video/fbdev/msm/mdss_dsi.h b/drivers/video/fbdev/msm/mdss_dsi.h
index 5e921ff..2ff2951 100644
--- a/drivers/video/fbdev/msm/mdss_dsi.h
+++ b/drivers/video/fbdev/msm/mdss_dsi.h
@@ -685,6 +685,7 @@
 	u32 mask, u32 val);
 int mdss_dsi_phy_pll_reset_status(struct mdss_dsi_ctrl_pdata *ctrl);
 int mdss_dsi_panel_power_ctrl(struct mdss_panel_data *pdata, int power_state);
+void mdss_dsi_ctrl_phy_reset(struct mdss_dsi_ctrl_pdata *ctrl);
 
 void mdss_dsi_debug_bus_init(struct mdss_dsi_data *sdata);
 
diff --git a/drivers/video/fbdev/msm/mdss_dsi_panel.c b/drivers/video/fbdev/msm/mdss_dsi_panel.c
index e586667..85ed42e 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_panel.c
+++ b/drivers/video/fbdev/msm/mdss_dsi_panel.c
@@ -24,6 +24,7 @@
 #include <linux/string.h>
 
 #include "mdss_dsi.h"
+#include "mdss_debug.h"
 #ifdef TARGET_HW_MDSS_HDMI
 #include "mdss_dba_utils.h"
 #endif
@@ -260,11 +261,12 @@
 	ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata,
 						panel_data);
 
-	pr_debug("%s: Idle (%d->%d)\n", __func__, ctrl->idle, enable);
+	pr_info("%s: Idle (%d->%d)\n", __func__, ctrl->idle, enable);
 
 	if (ctrl->idle == enable)
 		return;
 
+	MDSS_XLOG(ctrl->idle, enable);
 	if (enable) {
 		if (ctrl->idle_on_cmds.cmd_cnt) {
 			mdss_dsi_panel_cmds_send(ctrl, &ctrl->idle_on_cmds,
diff --git a/drivers/video/fbdev/msm/mdss_dsi_phy_12nm.c b/drivers/video/fbdev/msm/mdss_dsi_phy_12nm.c
index 9bd2c4d..ec179e8 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_phy_12nm.c
+++ b/drivers/video/fbdev/msm/mdss_dsi_phy_12nm.c
@@ -107,6 +107,7 @@
 {
 	DSI_PHY_W32(ctrl->phy_io.base, SYS_CTRL, BIT(0) | BIT(3));
 	wmb(); /* make sure DSI PHY is disabled */
+	mdss_dsi_ctrl_phy_reset(ctrl);
 	return 0;
 }
 
diff --git a/drivers/video/fbdev/msm/mdss_mdp_debug.c b/drivers/video/fbdev/msm/mdss_mdp_debug.c
index 6024ea1..9332f49 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_debug.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_debug.c
@@ -955,6 +955,11 @@
 	mdata->dbg_bus_size = 0;
 
 	switch (mdata->mdp_rev) {
+	case MDSS_MDP_HW_REV_114:
+	case MDSS_MDP_HW_REV_116:
+		mdata->dbg_bus = dbg_bus_8996;
+		mdata->dbg_bus_size = ARRAY_SIZE(dbg_bus_8996);
+		break;
 	case MDSS_MDP_HW_REV_107:
 	case MDSS_MDP_HW_REV_107_1:
 	case MDSS_MDP_HW_REV_107_2:
diff --git a/drivers/video/fbdev/msm/msm_mdss_io_8974.c b/drivers/video/fbdev/msm/msm_mdss_io_8974.c
index 153b39f..9ccd428 100644
--- a/drivers/video/fbdev/msm/msm_mdss_io_8974.c
+++ b/drivers/video/fbdev/msm/msm_mdss_io_8974.c
@@ -417,7 +417,7 @@
 	wmb(); /* make sure phy timings are updated*/
 }
 
-static void mdss_dsi_ctrl_phy_reset(struct mdss_dsi_ctrl_pdata *ctrl)
+void mdss_dsi_ctrl_phy_reset(struct mdss_dsi_ctrl_pdata *ctrl)
 {
 	/* start phy sw reset */
 	MIPI_OUTP(ctrl->ctrl_base + 0x12c, 0x0001);
@@ -519,8 +519,7 @@
 	 * is only done from the clock master. This will ensure that the PLL is
 	 * off when PHY reset is called.
 	 */
-	if (mdss_dsi_is_ctrl_clk_slave(ctrl) ||
-		(ctrl->shared_data->phy_rev == DSI_PHY_REV_12NM))
+	if (mdss_dsi_is_ctrl_clk_slave(ctrl))
 		return;
 
 	mdss_dsi_phy_sw_reset_sub(ctrl);
diff --git a/fs/buffer.c b/fs/buffer.c
index 5d8f496..3a8064d 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -1455,12 +1455,48 @@
 	return 0;
 }
 
+static void __evict_bh_lru(void *arg)
+{
+	struct bh_lru *b = &get_cpu_var(bh_lrus);
+	struct buffer_head *bh = arg;
+	int i;
+
+	for (i = 0; i < BH_LRU_SIZE; i++) {
+		if (b->bhs[i] == bh) {
+			brelse(b->bhs[i]);
+			b->bhs[i] = NULL;
+			goto out;
+		}
+	}
+out:
+	put_cpu_var(bh_lrus);
+}
+
+static bool bh_exists_in_lru(int cpu, void *arg)
+{
+	struct bh_lru *b = per_cpu_ptr(&bh_lrus, cpu);
+	struct buffer_head *bh = arg;
+	int i;
+
+	for (i = 0; i < BH_LRU_SIZE; i++) {
+		if (b->bhs[i] == bh)
+			return 1;
+	}
+
+	return 0;
+
+}
 void invalidate_bh_lrus(void)
 {
 	on_each_cpu_cond(has_bh_in_lru, invalidate_bh_lru, NULL, 1, GFP_KERNEL);
 }
 EXPORT_SYMBOL_GPL(invalidate_bh_lrus);
 
+static void evict_bh_lrus(struct buffer_head *bh)
+{
+	on_each_cpu_cond(bh_exists_in_lru, __evict_bh_lru, bh, 1, GFP_ATOMIC);
+}
+
 void set_bh_page(struct buffer_head *bh,
 		struct page *page, unsigned long offset)
 {
@@ -3250,8 +3286,15 @@
 	do {
 		if (buffer_write_io_error(bh) && page->mapping)
 			mapping_set_error(page->mapping, -EIO);
-		if (buffer_busy(bh))
-			goto failed;
+		if (buffer_busy(bh)) {
+			/*
+			 * Check if the busy failure was due to an
+			 * outstanding LRU reference
+			 */
+			evict_bh_lrus(bh);
+			if (buffer_busy(bh))
+				goto failed;
+		}
 		bh = bh->b_this_page;
 	} while (bh != head);
 
diff --git a/include/dt-bindings/clock/msm-clocks-8952.h b/include/dt-bindings/clock/msm-clocks-8952.h
index 3190d4f..e66c5ed 100644
--- a/include/dt-bindings/clock/msm-clocks-8952.h
+++ b/include/dt-bindings/clock/msm-clocks-8952.h
@@ -237,8 +237,10 @@
 
 #define clk_dsi0pll_byte_clk_src		0xbbaa30be
 #define clk_dsi0pll_pixel_clk_src		0x45b3260f
+#define clk_dsi0pll_vco_clk			0x15940d40
 #define clk_dsi1pll_byte_clk_src		0x63930a8f
 #define clk_dsi1pll_pixel_clk_src		0x0e4c9b56
+#define clk_dsi1pll_vco_clk			0x99797b50
 
 #define clk_dsi_pll0_byte_clk_src		0x44539836
 #define clk_dsi_pll0_pixel_clk_src		0x5767c287
diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h
index c122409..914eb4a 100644
--- a/include/linux/irqchip/arm-gic-v3.h
+++ b/include/linux/irqchip/arm-gic-v3.h
@@ -451,6 +451,8 @@
 }
 
 void gic_show_pending_irqs(void);
+void gic_v3_dist_save(void);
+void gic_v3_dist_restore(void);
 unsigned int get_gic_highpri_irq(void);
 #endif
 
diff --git a/include/linux/msm_gsi.h b/include/linux/msm_gsi.h
index 81e7e75..3b1e0e7 100644
--- a/include/linux/msm_gsi.h
+++ b/include/linux/msm_gsi.h
@@ -12,6 +12,7 @@
 #ifndef MSM_GSI_H
 #define MSM_GSI_H
 #include <linux/types.h>
+#include <linux/interrupt.h>
 
 enum gsi_ver {
 	GSI_VER_ERR = 0,
@@ -82,6 +83,9 @@
  * @irq:        IRQ number
  * @phys_addr:  physical address of GSI block
  * @size:       register size of GSI block
+ * @emulator_intcntrlr_addr: the location of emulator's interrupt control block
+ * @emulator_intcntrlr_size: the sise of emulator_intcntrlr_addr
+ * @emulator_intcntrlr_client_isr: client's isr. Called by the emulator's isr
  * @mhi_er_id_limits_valid: valid flag for mhi_er_id_limits
  * @mhi_er_id_limits: MHI event ring start and end ids
  * @notify_cb:  general notification callback
@@ -107,6 +111,9 @@
 	unsigned int irq;
 	phys_addr_t phys_addr;
 	unsigned long size;
+	phys_addr_t emulator_intcntrlr_addr;
+	unsigned long emulator_intcntrlr_size;
+	irq_handler_t emulator_intcntrlr_client_isr;
 	bool mhi_er_id_limits_valid;
 	uint32_t mhi_er_id_limits[2];
 	void (*notify_cb)(struct gsi_per_notify *notify);
@@ -1285,7 +1292,8 @@
 	return -GSI_STATUS_UNSUPPORTED_OP;
 }
 
-static inline int gsi_enable_fw(phys_addr_t gsi_base_addr, u32 gsi_size)
+static inline int gsi_enable_fw(
+	phys_addr_t gsi_base_addr, u32 gsi_size, enum gsi_ver ver)
 {
 	return -GSI_STATUS_UNSUPPORTED_OP;
 }
diff --git a/include/linux/of.h b/include/linux/of.h
index 299aeb1..3dcf853 100644
--- a/include/linux/of.h
+++ b/include/linux/of.h
@@ -106,6 +106,8 @@
 	node->fwnode.type = FWNODE_OF;
 }
 
+#define of_node_kobj(n) (&(n)->kobj)
+
 /* true when node is initialized */
 static inline int of_node_is_initialized(struct device_node *node)
 {
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index 2fb9f03..78c2a9f 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -282,6 +282,10 @@
 	POWER_SUPPLY_PROP_ALLOW_HVDCP3,
 	POWER_SUPPLY_PROP_HVDCP_OPTI_ALLOWED,
 	POWER_SUPPLY_PROP_MAX_PULSE_ALLOWED,
+	POWER_SUPPLY_PROP_IGNORE_FALSE_NEGATIVE_ISENSE,
+	POWER_SUPPLY_PROP_BATTERY_INFO,
+	POWER_SUPPLY_PROP_BATTERY_INFO_ID,
+	POWER_SUPPLY_PROP_ENABLE_JEITA_DETECTION,
 	/* Local extensions of type int64_t */
 	POWER_SUPPLY_PROP_CHARGE_COUNTER_EXT,
 	/* Properties of type `const char *' */
diff --git a/include/soc/qcom/pm-legacy.h b/include/soc/qcom/pm-legacy.h
new file mode 100644
index 0000000..7fdb0cd
--- /dev/null
+++ b/include/soc/qcom/pm-legacy.h
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2009-2016, 2018, The Linux Foundation. All rights reserved.
+ * Author: San Mehat <san@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __ARCH_ARM_MACH_MSM_PM_H
+#define __ARCH_ARM_MACH_MSM_PM_H
+
+#include <linux/types.h>
+#include <linux/cpuidle.h>
+#include <asm/smp_plat.h>
+#include <asm/barrier.h>
+#include <dt-bindings/msm/pm.h>
+
+#if !defined(CONFIG_SMP)
+#define msm_secondary_startup NULL
+#elif defined(CONFIG_CPU_V7)
+#define msm_secondary_startup secondary_startup
+#else
+#define msm_secondary_startup secondary_holding_pen
+#endif
+
+enum msm_pm_sleep_mode {
+	MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT,
+	MSM_PM_SLEEP_MODE_RETENTION,
+	MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE,
+	MSM_PM_SLEEP_MODE_POWER_COLLAPSE,
+	MSM_PM_SLEEP_MODE_FASTPC,
+	MSM_PM_SLEEP_MODE_POWER_COLLAPSE_SUSPEND,
+	MSM_PM_SLEEP_MODE_NR,
+	MSM_PM_SLEEP_MODE_NOT_SELECTED,
+};
+
+enum msm_pm_l2_scm_flag {
+	MSM_SCM_L2_ON = 0,
+	MSM_SCM_L2_OFF = 1,
+	MSM_SCM_L2_GDHS = 3,
+	MSM_SCM_L3_PC_OFF = 4,
+};
+
+#define MSM_PM_MODE(cpu, mode_nr)  ((cpu) *MSM_PM_SLEEP_MODE_NR + (mode_nr))
+
+struct msm_pm_time_params {
+	uint32_t latency_us;
+	uint32_t sleep_us;
+	uint32_t next_event_us;
+	uint32_t modified_time_us;
+};
+
+struct msm_pm_sleep_status_data {
+	void __iomem *base_addr;
+	uint32_t mask;
+};
+
+struct latency_level {
+	int affinity_level;
+	int reset_level;
+	const char *level_name;
+};
+
+/**
+ * lpm_cpu_pre_pc_cb(): API to get the L2 flag to pass to TZ
+ *
+ * @cpu: cpuid of the CPU going down.
+ *
+ * Returns the l2 flush flag enum that is passed down to TZ during power
+ * collaps
+ */
+enum msm_pm_l2_scm_flag lpm_cpu_pre_pc_cb(unsigned int cpu);
+
+/**
+ * msm_pm_sleep_mode_allow() - API to determine if sleep mode is allowed.
+ * @cpu:	CPU on which to check for the sleep mode.
+ * @mode:	Sleep Mode to check for.
+ * @idle:	Idle or Suspend Sleep Mode.
+ *
+ * Helper function to determine if a Idle or Suspend
+ * Sleep mode is allowed for a specific CPU.
+ *
+ * Return: 1 for allowed; 0 if not allowed.
+ */
+int msm_pm_sleep_mode_allow(unsigned int cpu, unsigned int mode, bool idle);
+
+/**
+ * msm_pm_sleep_mode_supported() - API to determine if sleep mode is
+ * supported.
+ * @cpu:	CPU on which to check for the sleep mode.
+ * @mode:	Sleep Mode to check for.
+ * @idle:	Idle or Suspend Sleep Mode.
+ *
+ * Helper function to determine if a Idle or Suspend
+ * Sleep mode is allowed and enabled for a specific CPU.
+ *
+ * Return: 1 for supported; 0 if not supported.
+ */
+int msm_pm_sleep_mode_supported(unsigned int cpu, unsigned int mode, bool idle);
+
+struct msm_pm_cpr_ops {
+	void (*cpr_suspend)(void);
+	void (*cpr_resume)(void);
+};
+
+void __init msm_pm_set_tz_retention_flag(unsigned int flag);
+void msm_pm_enable_retention(bool enable);
+bool msm_pm_retention_enabled(void);
+bool msm_cpu_pm_enter_sleep(enum msm_pm_sleep_mode mode, bool from_idle);
+static inline void msm_arch_idle(void)
+{
+	/* memory barrier */
+	mb();
+	wfi();
+}
+
+#ifdef CONFIG_MSM_PM_LEGACY
+
+void msm_pm_set_rpm_wakeup_irq(unsigned int irq);
+int msm_pm_wait_cpu_shutdown(unsigned int cpu);
+int __init msm_pm_sleep_status_init(void);
+void lpm_cpu_hotplug_enter(unsigned int cpu);
+s32 msm_cpuidle_get_deep_idle_latency(void);
+int msm_pm_collapse(unsigned long unused);
+
+/**
+ * lpm_get_latency() - API to get latency for a low power mode
+ * @latency_level:	pointer to structure with below elements
+ * affinity_level: The level (CPU/L2/CCI etc.) for which the
+ *	latency is required.
+ *	LPM_AFF_LVL_CPU : CPU level
+ *	LPM_AFF_LVL_L2  : L2 level
+ *	LPM_AFF_LVL_CCI : CCI level
+ * reset_level: Can be passed "LPM_RESET_LVL_GDHS" for
+ *	low power mode with control logic power collapse or
+ *	"LPM_RESET_LVL_PC" for low power mode with control and
+ *	memory logic power collapse or "LPM_RESET_LVL_RET" for
+ *	retention mode.
+ * level_name: Pointer to the cluster name for which the latency
+ *	is required or NULL if the minimum value out of all the
+ *	clusters is to be returned. For CPU level, the name of the
+ *	L2 cluster to be passed. For CCI it has no effect.
+ * @latency:	address to get the latency value.
+ *
+ * latency value will be for the particular cluster or the minimum
+ * value out of all the clusters at the particular affinity_level
+ * and reset_level.
+ *
+ * Return: 0 for success; Error number for failure.
+ */
+int lpm_get_latency(struct latency_level *level, uint32_t *latency);
+
+#else
+static inline void msm_pm_set_rpm_wakeup_irq(unsigned int irq) {}
+static inline int msm_pm_wait_cpu_shutdown(unsigned int cpu) { return 0; }
+static inline int msm_pm_sleep_status_init(void) { return 0; };
+
+static inline void lpm_cpu_hotplug_enter(unsigned int cpu)
+{
+	msm_arch_idle();
+};
+
+static inline s32 msm_cpuidle_get_deep_idle_latency(void) { return 0; }
+#define msm_pm_collapse NULL
+
+static inline int lpm_get_latency(struct latency_level *level,
+						uint32_t *latency)
+{
+	return 0;
+}
+#endif
+
+#ifdef CONFIG_HOTPLUG_CPU
+int msm_platform_secondary_init(unsigned int cpu);
+#else
+static inline int msm_platform_secondary_init(unsigned int cpu) { return 0; }
+#endif
+
+enum msm_pm_time_stats_id {
+	MSM_PM_STAT_REQUESTED_IDLE = 0,
+	MSM_PM_STAT_IDLE_SPIN,
+	MSM_PM_STAT_IDLE_WFI,
+	MSM_PM_STAT_RETENTION,
+	MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE,
+	MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE,
+	MSM_PM_STAT_IDLE_POWER_COLLAPSE,
+	MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE,
+	MSM_PM_STAT_SUSPEND,
+	MSM_PM_STAT_FAILED_SUSPEND,
+	MSM_PM_STAT_NOT_IDLE,
+	MSM_PM_STAT_COUNT
+};
+
+#ifdef CONFIG_MSM_IDLE_STATS
+void msm_pm_add_stats(enum msm_pm_time_stats_id *enable_stats, int size);
+void msm_pm_add_stat(enum msm_pm_time_stats_id id, int64_t t);
+void msm_pm_l2_add_stat(uint32_t id, int64_t t);
+#else
+static inline void msm_pm_add_stats(enum msm_pm_time_stats_id *enable_stats,
+		int size) {}
+static inline void msm_pm_add_stat(enum msm_pm_time_stats_id id, int64_t t) {}
+static inline void msm_pm_l2_add_stat(uint32_t id, int64_t t) {}
+#endif
+
+void msm_pm_set_cpr_ops(struct msm_pm_cpr_ops *ops);
+extern dma_addr_t msm_pc_debug_counters_phys;
+#endif  /* __ARCH_ARM_MACH_MSM_PM_H */
diff --git a/include/soc/qcom/spm.h b/include/soc/qcom/spm.h
index d0f76d0..a6f38cd 100644
--- a/include/soc/qcom/spm.h
+++ b/include/soc/qcom/spm.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2018, 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
@@ -35,6 +35,7 @@
 #if defined(CONFIG_MSM_SPM)
 
 int msm_spm_set_low_power_mode(unsigned int mode, bool notify_rpm);
+void msm_spm_set_rpm_hs(bool allow_rpm_hs);
 int msm_spm_probe_done(void);
 int msm_spm_set_vdd(unsigned int cpu, unsigned int vlevel);
 int msm_spm_get_vdd(unsigned int cpu);
@@ -43,6 +44,8 @@
 struct msm_spm_device *msm_spm_get_device_by_name(const char *name);
 int msm_spm_config_low_power_mode(struct msm_spm_device *dev,
 		unsigned int mode, bool notify_rpm);
+int msm_spm_config_low_power_mode_addr(struct msm_spm_device *dev,
+		unsigned int mode, bool notify_rpm);
 int msm_spm_device_init(void);
 bool msm_spm_is_mode_avail(unsigned int mode);
 void msm_spm_dump_regs(unsigned int cpu);
@@ -80,6 +83,8 @@
 	return -ENODEV;
 }
 
+static inline void msm_spm_set_rpm_hs(bool allow_rpm_hs) {}
+
 static inline int msm_spm_probe_done(void)
 {
 	return -ENODEV;
@@ -114,6 +119,13 @@
 {
 	return -ENODEV;
 }
+
+static inline int msm_spm_config_low_power_mode_addr(
+	struct msm_spm_device *dev, unsigned int mode, bool notify_rpm)
+{
+	return -ENODEV;
+}
+
 static inline struct msm_spm_device *msm_spm_get_device_by_name(
 				const char *name)
 {
diff --git a/include/trace/events/trace_msm_low_power.h b/include/trace/events/trace_msm_low_power.h
index c25da0e..54c1272 100644
--- a/include/trace/events/trace_msm_low_power.h
+++ b/include/trace/events/trace_msm_low_power.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, 2014-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012, 2014-2018, 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
@@ -250,6 +250,25 @@
 		__entry->sample, __entry->tmr)
 );
 
+TRACE_EVENT(pre_pc_cb,
+
+	TP_PROTO(int tzflag),
+
+	TP_ARGS(tzflag),
+
+	TP_STRUCT__entry(
+		__field(int, tzflag)
+	),
+
+	TP_fast_assign(
+		__entry->tzflag = tzflag;
+	),
+
+	TP_printk("tzflag:%d",
+		__entry->tzflag
+	)
+);
+
 #endif
 #define TRACE_INCLUDE_FILE trace_msm_low_power
 #include <trace/define_trace.h>
diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
index c9248e5a..5da3634 100644
--- a/include/uapi/linux/Kbuild
+++ b/include/uapi/linux/Kbuild
@@ -472,6 +472,8 @@
 header-y += v4l2-dv-timings.h
 header-y += v4l2-mediabus.h
 header-y += v4l2-subdev.h
+header-y += msm_vidc_dec.h
+header-y += msm_vidc_enc.h
 header-y += veth.h
 header-y += vfio.h
 header-y += vhost.h
diff --git a/include/uapi/linux/msm_vidc_dec.h b/include/uapi/linux/msm_vidc_dec.h
new file mode 100644
index 0000000..46af82b
--- /dev/null
+++ b/include/uapi/linux/msm_vidc_dec.h
@@ -0,0 +1,629 @@
+#ifndef _UAPI_MSM_VIDC_DEC_H_
+#define _UAPI_MSM_VIDC_DEC_H_
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+/* STATUS CODES */
+/* Base value for status codes */
+#define VDEC_S_BASE	0x40000000
+/* Success */
+#define VDEC_S_SUCCESS	(VDEC_S_BASE)
+/* General failure */
+#define VDEC_S_EFAIL	(VDEC_S_BASE + 1)
+/* Fatal irrecoverable  failure. Need to  tear down session. */
+#define VDEC_S_EFATAL   (VDEC_S_BASE + 2)
+/* Error detected in the passed  parameters */
+#define VDEC_S_EBADPARAM	(VDEC_S_BASE + 3)
+/* Command called in invalid  state. */
+#define VDEC_S_EINVALSTATE	(VDEC_S_BASE + 4)
+ /* Insufficient OS  resources - thread, memory etc. */
+#define VDEC_S_ENOSWRES	(VDEC_S_BASE + 5)
+ /* Insufficient HW resources -  core capacity  maxed  out. */
+#define VDEC_S_ENOHWRES	(VDEC_S_BASE + 6)
+/* Invalid command  called */
+#define VDEC_S_EINVALCMD	(VDEC_S_BASE + 7)
+/* Command timeout. */
+#define VDEC_S_ETIMEOUT	(VDEC_S_BASE + 8)
+/* Pre-requirement is  not met for API. */
+#define VDEC_S_ENOPREREQ	(VDEC_S_BASE + 9)
+/* Command queue is full. */
+#define VDEC_S_ECMDQFULL	(VDEC_S_BASE + 10)
+/* Command is not supported  by this driver */
+#define VDEC_S_ENOTSUPP	(VDEC_S_BASE + 11)
+/* Command is not implemented by thedriver. */
+#define VDEC_S_ENOTIMPL	(VDEC_S_BASE + 12)
+/* Command is not implemented by the driver.  */
+#define VDEC_S_BUSY	(VDEC_S_BASE + 13)
+#define VDEC_S_INPUT_BITSTREAM_ERR (VDEC_S_BASE + 14)
+
+#define VDEC_INTF_VER	1
+#define VDEC_MSG_BASE	0x0000000
+/*
+ *Codes to identify asynchronous message responses and events that driver
+ *wants to communicate to the app.
+ */
+#define VDEC_MSG_INVALID	(VDEC_MSG_BASE + 0)
+#define VDEC_MSG_RESP_INPUT_BUFFER_DONE	(VDEC_MSG_BASE + 1)
+#define VDEC_MSG_RESP_OUTPUT_BUFFER_DONE	(VDEC_MSG_BASE + 2)
+#define VDEC_MSG_RESP_INPUT_FLUSHED	(VDEC_MSG_BASE + 3)
+#define VDEC_MSG_RESP_OUTPUT_FLUSHED	(VDEC_MSG_BASE + 4)
+#define VDEC_MSG_RESP_FLUSH_INPUT_DONE	(VDEC_MSG_BASE + 5)
+#define VDEC_MSG_RESP_FLUSH_OUTPUT_DONE	(VDEC_MSG_BASE + 6)
+#define VDEC_MSG_RESP_START_DONE	(VDEC_MSG_BASE + 7)
+#define VDEC_MSG_RESP_STOP_DONE	(VDEC_MSG_BASE + 8)
+#define VDEC_MSG_RESP_PAUSE_DONE	(VDEC_MSG_BASE + 9)
+#define VDEC_MSG_RESP_RESUME_DONE	(VDEC_MSG_BASE + 10)
+#define VDEC_MSG_RESP_RESOURCE_LOADED	(VDEC_MSG_BASE + 11)
+#define VDEC_EVT_RESOURCES_LOST	(VDEC_MSG_BASE + 12)
+#define VDEC_MSG_EVT_CONFIG_CHANGED	(VDEC_MSG_BASE + 13)
+#define VDEC_MSG_EVT_HW_ERROR	(VDEC_MSG_BASE + 14)
+#define VDEC_MSG_EVT_INFO_CONFIG_CHANGED	(VDEC_MSG_BASE + 15)
+#define VDEC_MSG_EVT_INFO_FIELD_DROPPED	(VDEC_MSG_BASE + 16)
+#define VDEC_MSG_EVT_HW_OVERLOAD	(VDEC_MSG_BASE + 17)
+#define VDEC_MSG_EVT_MAX_CLIENTS	(VDEC_MSG_BASE + 18)
+#define VDEC_MSG_EVT_HW_UNSUPPORTED	(VDEC_MSG_BASE + 19)
+
+/*Buffer flags bits masks.*/
+#define VDEC_BUFFERFLAG_EOS	0x00000001
+#define VDEC_BUFFERFLAG_DECODEONLY	0x00000004
+#define VDEC_BUFFERFLAG_DATACORRUPT	0x00000008
+#define VDEC_BUFFERFLAG_ENDOFFRAME	0x00000010
+#define VDEC_BUFFERFLAG_SYNCFRAME	0x00000020
+#define VDEC_BUFFERFLAG_EXTRADATA	0x00000040
+#define VDEC_BUFFERFLAG_CODECCONFIG	0x00000080
+
+/*Post processing flags bit masks*/
+#define VDEC_EXTRADATA_NONE 0x001
+#define VDEC_EXTRADATA_QP 0x004
+#define VDEC_EXTRADATA_MB_ERROR_MAP 0x008
+#define VDEC_EXTRADATA_SEI 0x010
+#define VDEC_EXTRADATA_VUI 0x020
+#define VDEC_EXTRADATA_VC1 0x040
+
+#define VDEC_EXTRADATA_EXT_DATA          0x0800
+#define VDEC_EXTRADATA_USER_DATA         0x1000
+#define VDEC_EXTRADATA_EXT_BUFFER        0x2000
+
+#define VDEC_CMDBASE	0x800
+#define VDEC_CMD_SET_INTF_VERSION	(VDEC_CMDBASE)
+
+#define VDEC_IOCTL_MAGIC 'v'
+
+struct vdec_ioctl_msg {
+	void __user *in;
+	void __user *out;
+};
+
+/*
+ * CMD params: InputParam:enum vdec_codec
+ * OutputParam: struct vdec_profile_level
+ */
+#define VDEC_IOCTL_GET_PROFILE_LEVEL_SUPPORTED \
+	_IOWR(VDEC_IOCTL_MAGIC, 0, struct vdec_ioctl_msg)
+
+/*
+ * CMD params:InputParam: NULL
+ * OutputParam: uint32_t(bitmask)
+ */
+#define VDEC_IOCTL_GET_INTERLACE_FORMAT \
+	_IOR(VDEC_IOCTL_MAGIC, 1, struct vdec_ioctl_msg)
+
+/*
+ * CMD params: InputParam:  enum vdec_codec
+ * OutputParam: struct vdec_profile_level
+ */
+#define VDEC_IOCTL_GET_CURRENT_PROFILE_LEVEL \
+	_IOWR(VDEC_IOCTL_MAGIC, 2, struct vdec_ioctl_msg)
+
+/*
+ * CMD params: SET: InputParam: enum vdec_output_fromat  OutputParam: NULL
+ * GET:  InputParam: NULL OutputParam: enum vdec_output_fromat
+ */
+#define VDEC_IOCTL_SET_OUTPUT_FORMAT \
+	_IOWR(VDEC_IOCTL_MAGIC, 3, struct vdec_ioctl_msg)
+#define VDEC_IOCTL_GET_OUTPUT_FORMAT \
+	_IOWR(VDEC_IOCTL_MAGIC, 4, struct vdec_ioctl_msg)
+
+/*
+ * CMD params: SET: InputParam: enum vdec_codec OutputParam: NULL
+ * GET: InputParam: NULL OutputParam: enum vdec_codec
+ */
+#define VDEC_IOCTL_SET_CODEC \
+	_IOW(VDEC_IOCTL_MAGIC, 5, struct vdec_ioctl_msg)
+#define VDEC_IOCTL_GET_CODEC \
+	_IOR(VDEC_IOCTL_MAGIC, 6, struct vdec_ioctl_msg)
+
+/*
+ * CMD params: SET: InputParam: struct vdec_picsize outputparam: NULL
+ * GET: InputParam: NULL outputparam: struct vdec_picsize
+ */
+#define VDEC_IOCTL_SET_PICRES \
+	_IOW(VDEC_IOCTL_MAGIC, 7, struct vdec_ioctl_msg)
+#define VDEC_IOCTL_GET_PICRES \
+	_IOR(VDEC_IOCTL_MAGIC, 8, struct vdec_ioctl_msg)
+
+#define VDEC_IOCTL_SET_EXTRADATA \
+	_IOW(VDEC_IOCTL_MAGIC, 9, struct vdec_ioctl_msg)
+#define VDEC_IOCTL_GET_EXTRADATA \
+	_IOR(VDEC_IOCTL_MAGIC, 10, struct vdec_ioctl_msg)
+
+#define VDEC_IOCTL_SET_SEQUENCE_HEADER \
+	_IOW(VDEC_IOCTL_MAGIC, 11, struct vdec_ioctl_msg)
+
+/*
+ * CMD params: SET: InputParam - vdec_allocatorproperty, OutputParam - NULL
+ * GET: InputParam - NULL, OutputParam - vdec_allocatorproperty
+ */
+#define VDEC_IOCTL_SET_BUFFER_REQ \
+	_IOW(VDEC_IOCTL_MAGIC, 12, struct vdec_ioctl_msg)
+#define VDEC_IOCTL_GET_BUFFER_REQ \
+	_IOR(VDEC_IOCTL_MAGIC, 13, struct vdec_ioctl_msg)
+/* CMD params: InputParam - vdec_buffer, OutputParam - uint8_t** */
+#define VDEC_IOCTL_ALLOCATE_BUFFER \
+	_IOWR(VDEC_IOCTL_MAGIC, 14, struct vdec_ioctl_msg)
+/* CMD params: InputParam - uint8_t *, OutputParam - NULL.*/
+#define VDEC_IOCTL_FREE_BUFFER \
+	_IOW(VDEC_IOCTL_MAGIC, 15, struct vdec_ioctl_msg)
+
+/*CMD params: CMD: InputParam - struct vdec_setbuffer_cmd, OutputParam - NULL*/
+#define VDEC_IOCTL_SET_BUFFER \
+	_IOW(VDEC_IOCTL_MAGIC, 16, struct vdec_ioctl_msg)
+
+/* CMD params: InputParam - struct vdec_fillbuffer_cmd, OutputParam - NULL*/
+#define VDEC_IOCTL_FILL_OUTPUT_BUFFER \
+	_IOW(VDEC_IOCTL_MAGIC, 17, struct vdec_ioctl_msg)
+
+/*CMD params: InputParam - struct vdec_frameinfo , OutputParam - NULL*/
+#define VDEC_IOCTL_DECODE_FRAME \
+	_IOW(VDEC_IOCTL_MAGIC, 18, struct vdec_ioctl_msg)
+
+#define VDEC_IOCTL_LOAD_RESOURCES _IO(VDEC_IOCTL_MAGIC, 19)
+#define VDEC_IOCTL_CMD_START _IO(VDEC_IOCTL_MAGIC, 20)
+#define VDEC_IOCTL_CMD_STOP _IO(VDEC_IOCTL_MAGIC, 21)
+#define VDEC_IOCTL_CMD_PAUSE _IO(VDEC_IOCTL_MAGIC, 22)
+#define VDEC_IOCTL_CMD_RESUME _IO(VDEC_IOCTL_MAGIC, 23)
+
+/*CMD params: InputParam - enum vdec_bufferflush , OutputParam - NULL */
+#define VDEC_IOCTL_CMD_FLUSH _IOW(VDEC_IOCTL_MAGIC, 24, struct vdec_ioctl_msg)
+
+/* ========================================================
+ * IOCTL for getting asynchronous notification from driver
+ * ========================================================
+ */
+
+/*IOCTL params: InputParam - NULL, OutputParam - struct vdec_msginfo*/
+#define VDEC_IOCTL_GET_NEXT_MSG \
+	_IOR(VDEC_IOCTL_MAGIC, 25, struct vdec_ioctl_msg)
+
+#define VDEC_IOCTL_STOP_NEXT_MSG _IO(VDEC_IOCTL_MAGIC, 26)
+
+#define VDEC_IOCTL_GET_NUMBER_INSTANCES \
+	_IOR(VDEC_IOCTL_MAGIC, 27, struct vdec_ioctl_msg)
+
+#define VDEC_IOCTL_SET_PICTURE_ORDER \
+	_IOW(VDEC_IOCTL_MAGIC, 28, struct vdec_ioctl_msg)
+
+#define VDEC_IOCTL_SET_FRAME_RATE \
+	_IOW(VDEC_IOCTL_MAGIC, 29, struct vdec_ioctl_msg)
+
+#define VDEC_IOCTL_SET_H264_MV_BUFFER \
+	_IOW(VDEC_IOCTL_MAGIC, 30, struct vdec_ioctl_msg)
+
+#define VDEC_IOCTL_FREE_H264_MV_BUFFER \
+	_IOW(VDEC_IOCTL_MAGIC, 31, struct vdec_ioctl_msg)
+
+#define VDEC_IOCTL_GET_MV_BUFFER_SIZE  \
+	_IOR(VDEC_IOCTL_MAGIC, 32, struct vdec_ioctl_msg)
+
+#define VDEC_IOCTL_SET_IDR_ONLY_DECODING \
+	_IO(VDEC_IOCTL_MAGIC, 33)
+
+#define VDEC_IOCTL_SET_CONT_ON_RECONFIG  \
+	_IO(VDEC_IOCTL_MAGIC, 34)
+
+#define VDEC_IOCTL_SET_DISABLE_DMX \
+	_IOW(VDEC_IOCTL_MAGIC, 35, struct vdec_ioctl_msg)
+
+#define VDEC_IOCTL_GET_DISABLE_DMX \
+	_IOR(VDEC_IOCTL_MAGIC, 36, struct vdec_ioctl_msg)
+
+#define VDEC_IOCTL_GET_DISABLE_DMX_SUPPORT \
+	_IOR(VDEC_IOCTL_MAGIC, 37, struct vdec_ioctl_msg)
+
+#define VDEC_IOCTL_SET_PERF_CLK \
+	_IOR(VDEC_IOCTL_MAGIC, 38, struct vdec_ioctl_msg)
+
+#define VDEC_IOCTL_SET_META_BUFFERS \
+	_IOW(VDEC_IOCTL_MAGIC, 39, struct vdec_ioctl_msg)
+
+#define VDEC_IOCTL_FREE_META_BUFFERS \
+	_IO(VDEC_IOCTL_MAGIC, 40)
+
+enum vdec_picture {
+	PICTURE_TYPE_I,
+	PICTURE_TYPE_P,
+	PICTURE_TYPE_B,
+	PICTURE_TYPE_BI,
+	PICTURE_TYPE_SKIP,
+	PICTURE_TYPE_IDR,
+	PICTURE_TYPE_UNKNOWN
+};
+
+enum vdec_buffer {
+	VDEC_BUFFER_TYPE_INPUT,
+	VDEC_BUFFER_TYPE_OUTPUT
+};
+
+struct vdec_allocatorproperty {
+	enum vdec_buffer buffer_type;
+	uint32_t mincount;
+	uint32_t maxcount;
+	uint32_t actualcount;
+	size_t buffer_size;
+	uint32_t alignment;
+	uint32_t buf_poolid;
+	size_t meta_buffer_size;
+};
+
+struct vdec_bufferpayload {
+	void __user *bufferaddr;
+	size_t buffer_len;
+	int pmem_fd;
+	size_t offset;
+	size_t mmaped_size;
+};
+
+struct vdec_setbuffer_cmd {
+	enum vdec_buffer buffer_type;
+	struct vdec_bufferpayload buffer;
+};
+
+struct vdec_fillbuffer_cmd {
+	struct vdec_bufferpayload buffer;
+	void *client_data;
+};
+
+enum vdec_bufferflush {
+	VDEC_FLUSH_TYPE_INPUT,
+	VDEC_FLUSH_TYPE_OUTPUT,
+	VDEC_FLUSH_TYPE_ALL
+};
+
+enum vdec_codec {
+	VDEC_CODECTYPE_H264 = 0x1,
+	VDEC_CODECTYPE_H263 = 0x2,
+	VDEC_CODECTYPE_MPEG4 = 0x3,
+	VDEC_CODECTYPE_DIVX_3 = 0x4,
+	VDEC_CODECTYPE_DIVX_4 = 0x5,
+	VDEC_CODECTYPE_DIVX_5 = 0x6,
+	VDEC_CODECTYPE_DIVX_6 = 0x7,
+	VDEC_CODECTYPE_XVID = 0x8,
+	VDEC_CODECTYPE_MPEG1 = 0x9,
+	VDEC_CODECTYPE_MPEG2 = 0xa,
+	VDEC_CODECTYPE_VC1 = 0xb,
+	VDEC_CODECTYPE_VC1_RCV = 0xc,
+	VDEC_CODECTYPE_HEVC = 0xd,
+	VDEC_CODECTYPE_MVC = 0xe,
+	VDEC_CODECTYPE_VP8 = 0xf,
+	VDEC_CODECTYPE_VP9 = 0x10,
+};
+
+enum vdec_mpeg2_profile {
+	VDEC_MPEG2ProfileSimple = 0x1,
+	VDEC_MPEG2ProfileMain = 0x2,
+	VDEC_MPEG2Profile422 = 0x4,
+	VDEC_MPEG2ProfileSNR = 0x8,
+	VDEC_MPEG2ProfileSpatial = 0x10,
+	VDEC_MPEG2ProfileHigh = 0x20,
+	VDEC_MPEG2ProfileKhronosExtensions = 0x6F000000,
+	VDEC_MPEG2ProfileVendorStartUnused = 0x7F000000,
+	VDEC_MPEG2ProfileMax = 0x7FFFFFFF
+};
+
+enum vdec_mpeg2_level {
+
+	VDEC_MPEG2LevelLL = 0x1,
+	VDEC_MPEG2LevelML = 0x2,
+	VDEC_MPEG2LevelH14 = 0x4,
+	VDEC_MPEG2LevelHL = 0x8,
+	VDEC_MPEG2LevelKhronosExtensions = 0x6F000000,
+	VDEC_MPEG2LevelVendorStartUnused = 0x7F000000,
+	VDEC_MPEG2LevelMax = 0x7FFFFFFF
+};
+
+enum vdec_mpeg4_profile {
+	VDEC_MPEG4ProfileSimple = 0x01,
+	VDEC_MPEG4ProfileSimpleScalable = 0x02,
+	VDEC_MPEG4ProfileCore = 0x04,
+	VDEC_MPEG4ProfileMain = 0x08,
+	VDEC_MPEG4ProfileNbit = 0x10,
+	VDEC_MPEG4ProfileScalableTexture = 0x20,
+	VDEC_MPEG4ProfileSimpleFace = 0x40,
+	VDEC_MPEG4ProfileSimpleFBA = 0x80,
+	VDEC_MPEG4ProfileBasicAnimated = 0x100,
+	VDEC_MPEG4ProfileHybrid = 0x200,
+	VDEC_MPEG4ProfileAdvancedRealTime = 0x400,
+	VDEC_MPEG4ProfileCoreScalable = 0x800,
+	VDEC_MPEG4ProfileAdvancedCoding = 0x1000,
+	VDEC_MPEG4ProfileAdvancedCore = 0x2000,
+	VDEC_MPEG4ProfileAdvancedScalable = 0x4000,
+	VDEC_MPEG4ProfileAdvancedSimple = 0x8000,
+	VDEC_MPEG4ProfileKhronosExtensions = 0x6F000000,
+	VDEC_MPEG4ProfileVendorStartUnused = 0x7F000000,
+	VDEC_MPEG4ProfileMax = 0x7FFFFFFF
+};
+
+enum vdec_mpeg4_level {
+	VDEC_MPEG4Level0 = 0x01,
+	VDEC_MPEG4Level0b = 0x02,
+	VDEC_MPEG4Level1 = 0x04,
+	VDEC_MPEG4Level2 = 0x08,
+	VDEC_MPEG4Level3 = 0x10,
+	VDEC_MPEG4Level4 = 0x20,
+	VDEC_MPEG4Level4a = 0x40,
+	VDEC_MPEG4Level5 = 0x80,
+	VDEC_MPEG4LevelKhronosExtensions = 0x6F000000,
+	VDEC_MPEG4LevelVendorStartUnused = 0x7F000000,
+	VDEC_MPEG4LevelMax = 0x7FFFFFFF
+};
+
+enum vdec_avc_profile {
+	VDEC_AVCProfileBaseline = 0x01,
+	VDEC_AVCProfileMain = 0x02,
+	VDEC_AVCProfileExtended = 0x04,
+	VDEC_AVCProfileHigh = 0x08,
+	VDEC_AVCProfileHigh10 = 0x10,
+	VDEC_AVCProfileHigh422 = 0x20,
+	VDEC_AVCProfileHigh444 = 0x40,
+	VDEC_AVCProfileKhronosExtensions = 0x6F000000,
+	VDEC_AVCProfileVendorStartUnused = 0x7F000000,
+	VDEC_AVCProfileMax = 0x7FFFFFFF
+};
+
+enum vdec_avc_level {
+	VDEC_AVCLevel1 = 0x01,
+	VDEC_AVCLevel1b = 0x02,
+	VDEC_AVCLevel11 = 0x04,
+	VDEC_AVCLevel12 = 0x08,
+	VDEC_AVCLevel13 = 0x10,
+	VDEC_AVCLevel2 = 0x20,
+	VDEC_AVCLevel21 = 0x40,
+	VDEC_AVCLevel22 = 0x80,
+	VDEC_AVCLevel3 = 0x100,
+	VDEC_AVCLevel31 = 0x200,
+	VDEC_AVCLevel32 = 0x400,
+	VDEC_AVCLevel4 = 0x800,
+	VDEC_AVCLevel41 = 0x1000,
+	VDEC_AVCLevel42 = 0x2000,
+	VDEC_AVCLevel5 = 0x4000,
+	VDEC_AVCLevel51 = 0x8000,
+	VDEC_AVCLevelKhronosExtensions = 0x6F000000,
+	VDEC_AVCLevelVendorStartUnused = 0x7F000000,
+	VDEC_AVCLevelMax = 0x7FFFFFFF
+};
+
+enum vdec_divx_profile {
+	VDEC_DIVXProfile_qMobile = 0x01,
+	VDEC_DIVXProfile_Mobile = 0x02,
+	VDEC_DIVXProfile_HD = 0x04,
+	VDEC_DIVXProfile_Handheld = 0x08,
+	VDEC_DIVXProfile_Portable = 0x10,
+	VDEC_DIVXProfile_HomeTheater = 0x20
+};
+
+enum vdec_xvid_profile {
+	VDEC_XVIDProfile_Simple = 0x1,
+	VDEC_XVIDProfile_Advanced_Realtime_Simple = 0x2,
+	VDEC_XVIDProfile_Advanced_Simple = 0x4
+};
+
+enum vdec_xvid_level {
+	VDEC_XVID_LEVEL_S_L0 = 0x1,
+	VDEC_XVID_LEVEL_S_L1 = 0x2,
+	VDEC_XVID_LEVEL_S_L2 = 0x4,
+	VDEC_XVID_LEVEL_S_L3 = 0x8,
+	VDEC_XVID_LEVEL_ARTS_L1 = 0x10,
+	VDEC_XVID_LEVEL_ARTS_L2 = 0x20,
+	VDEC_XVID_LEVEL_ARTS_L3 = 0x40,
+	VDEC_XVID_LEVEL_ARTS_L4 = 0x80,
+	VDEC_XVID_LEVEL_AS_L0 = 0x100,
+	VDEC_XVID_LEVEL_AS_L1 = 0x200,
+	VDEC_XVID_LEVEL_AS_L2 = 0x400,
+	VDEC_XVID_LEVEL_AS_L3 = 0x800,
+	VDEC_XVID_LEVEL_AS_L4 = 0x1000
+};
+
+enum vdec_h263profile {
+	VDEC_H263ProfileBaseline = 0x01,
+	VDEC_H263ProfileH320Coding = 0x02,
+	VDEC_H263ProfileBackwardCompatible = 0x04,
+	VDEC_H263ProfileISWV2 = 0x08,
+	VDEC_H263ProfileISWV3 = 0x10,
+	VDEC_H263ProfileHighCompression = 0x20,
+	VDEC_H263ProfileInternet = 0x40,
+	VDEC_H263ProfileInterlace = 0x80,
+	VDEC_H263ProfileHighLatency = 0x100,
+	VDEC_H263ProfileKhronosExtensions = 0x6F000000,
+	VDEC_H263ProfileVendorStartUnused = 0x7F000000,
+	VDEC_H263ProfileMax = 0x7FFFFFFF
+};
+
+enum vdec_h263level {
+	VDEC_H263Level10 = 0x01,
+	VDEC_H263Level20 = 0x02,
+	VDEC_H263Level30 = 0x04,
+	VDEC_H263Level40 = 0x08,
+	VDEC_H263Level45 = 0x10,
+	VDEC_H263Level50 = 0x20,
+	VDEC_H263Level60 = 0x40,
+	VDEC_H263Level70 = 0x80,
+	VDEC_H263LevelKhronosExtensions = 0x6F000000,
+	VDEC_H263LevelVendorStartUnused = 0x7F000000,
+	VDEC_H263LevelMax = 0x7FFFFFFF
+};
+
+enum vdec_wmv_format {
+	VDEC_WMVFormatUnused = 0x01,
+	VDEC_WMVFormat7 = 0x02,
+	VDEC_WMVFormat8 = 0x04,
+	VDEC_WMVFormat9 = 0x08,
+	VDEC_WMFFormatKhronosExtensions = 0x6F000000,
+	VDEC_WMFFormatVendorStartUnused = 0x7F000000,
+	VDEC_WMVFormatMax = 0x7FFFFFFF
+};
+
+enum vdec_vc1_profile {
+	VDEC_VC1ProfileSimple = 0x1,
+	VDEC_VC1ProfileMain = 0x2,
+	VDEC_VC1ProfileAdvanced = 0x4
+};
+
+enum vdec_vc1_level {
+	VDEC_VC1_LEVEL_S_Low = 0x1,
+	VDEC_VC1_LEVEL_S_Medium = 0x2,
+	VDEC_VC1_LEVEL_M_Low = 0x4,
+	VDEC_VC1_LEVEL_M_Medium = 0x8,
+	VDEC_VC1_LEVEL_M_High = 0x10,
+	VDEC_VC1_LEVEL_A_L0 = 0x20,
+	VDEC_VC1_LEVEL_A_L1 = 0x40,
+	VDEC_VC1_LEVEL_A_L2 = 0x80,
+	VDEC_VC1_LEVEL_A_L3 = 0x100,
+	VDEC_VC1_LEVEL_A_L4 = 0x200
+};
+
+struct vdec_profile_level {
+	uint32_t profiles;
+	uint32_t levels;
+};
+
+enum vdec_interlaced_format {
+	VDEC_InterlaceFrameProgressive = 0x1,
+	VDEC_InterlaceInterleaveFrameTopFieldFirst = 0x2,
+	VDEC_InterlaceInterleaveFrameBottomFieldFirst = 0x4
+};
+
+#define VDEC_YUV_FORMAT_NV12_TP10_UBWC \
+	VDEC_YUV_FORMAT_NV12_TP10_UBWC
+
+enum vdec_output_fromat {
+	VDEC_YUV_FORMAT_NV12 = 0x1,
+	VDEC_YUV_FORMAT_TILE_4x2 = 0x2,
+	VDEC_YUV_FORMAT_NV12_UBWC = 0x3,
+	VDEC_YUV_FORMAT_NV12_TP10_UBWC = 0x4
+};
+
+enum vdec_output_order {
+	VDEC_ORDER_DISPLAY = 0x1,
+	VDEC_ORDER_DECODE = 0x2
+};
+
+struct vdec_picsize {
+	uint32_t frame_width;
+	uint32_t frame_height;
+	uint32_t stride;
+	uint32_t scan_lines;
+};
+
+struct vdec_seqheader {
+	void __user *ptr_seqheader;
+	size_t seq_header_len;
+	int pmem_fd;
+	size_t pmem_offset;
+};
+
+struct vdec_mberror {
+	void __user *ptr_errormap;
+	size_t err_mapsize;
+};
+
+struct vdec_input_frameinfo {
+	void __user *bufferaddr;
+	size_t offset;
+	size_t datalen;
+	uint32_t flags;
+	int64_t timestamp;
+	void *client_data;
+	int pmem_fd;
+	size_t pmem_offset;
+	void __user *desc_addr;
+	uint32_t desc_size;
+};
+
+struct vdec_framesize {
+	uint32_t   left;
+	uint32_t   top;
+	uint32_t   right;
+	uint32_t   bottom;
+};
+
+struct vdec_aspectratioinfo {
+	uint32_t aspect_ratio;
+	uint32_t par_width;
+	uint32_t par_height;
+};
+
+struct vdec_sep_metadatainfo {
+	void __user *metabufaddr;
+	uint32_t size;
+	int fd;
+	int offset;
+	uint32_t buffer_size;
+};
+
+struct vdec_output_frameinfo {
+	void __user *bufferaddr;
+	size_t offset;
+	size_t len;
+	uint32_t flags;
+	int64_t time_stamp;
+	enum vdec_picture pic_type;
+	void *client_data;
+	void *input_frame_clientdata;
+	struct vdec_picsize picsize;
+	struct vdec_framesize framesize;
+	enum vdec_interlaced_format interlaced_format;
+	struct vdec_aspectratioinfo aspect_ratio_info;
+	struct vdec_sep_metadatainfo metadata_info;
+};
+
+union vdec_msgdata {
+	struct vdec_output_frameinfo output_frame;
+	void *input_frame_clientdata;
+};
+
+struct vdec_msginfo {
+	uint32_t status_code;
+	uint32_t msgcode;
+	union vdec_msgdata msgdata;
+	size_t msgdatasize;
+};
+
+struct vdec_framerate {
+	unsigned long fps_denominator;
+	unsigned long fps_numerator;
+};
+
+struct vdec_h264_mv {
+	size_t size;
+	int count;
+	int pmem_fd;
+	int offset;
+};
+
+struct vdec_mv_buff_size {
+	int width;
+	int height;
+	int size;
+	int alignment;
+};
+
+struct vdec_meta_buffers {
+	size_t size;
+	int count;
+	int pmem_fd;
+	int pmem_fd_iommu;
+	int offset;
+};
+
+#endif /* end of macro _VDECDECODER_H_ */
diff --git a/include/uapi/linux/msm_vidc_enc.h b/include/uapi/linux/msm_vidc_enc.h
new file mode 100644
index 0000000..f4f1630
--- /dev/null
+++ b/include/uapi/linux/msm_vidc_enc.h
@@ -0,0 +1,752 @@
+#ifndef _UAPI_MSM_VIDC_ENC_H_
+#define _UAPI_MSM_VIDC_ENC_H_
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+/** STATUS CODES*/
+/* Base value for status codes */
+#define VEN_S_BASE	0x00000000
+#define VEN_S_SUCCESS	(VEN_S_BASE)/* Success */
+#define VEN_S_EFAIL	(VEN_S_BASE+1)/* General failure */
+#define VEN_S_EFATAL	(VEN_S_BASE+2)/* Fatal irrecoverable failure*/
+#define VEN_S_EBADPARAM	(VEN_S_BASE+3)/* Error passed parameters*/
+/*Command called in invalid state*/
+#define VEN_S_EINVALSTATE	(VEN_S_BASE+4)
+#define VEN_S_ENOSWRES	(VEN_S_BASE+5)/* Insufficient OS resources*/
+#define VEN_S_ENOHWRES	(VEN_S_BASE+6)/*Insufficient HW resources */
+#define VEN_S_EBUFFREQ	(VEN_S_BASE+7)/* Buffer requirements were not met*/
+#define VEN_S_EINVALCMD	(VEN_S_BASE+8)/* Invalid command called */
+#define VEN_S_ETIMEOUT	(VEN_S_BASE+9)/* Command timeout. */
+/*Re-attempt was made when multiple invocation not supported for API.*/
+#define VEN_S_ENOREATMPT	(VEN_S_BASE+10)
+#define VEN_S_ENOPREREQ	(VEN_S_BASE+11)/*Pre-requirement is not met for API*/
+#define VEN_S_ECMDQFULL	(VEN_S_BASE+12)/*Command queue is full*/
+#define VEN_S_ENOTSUPP	(VEN_S_BASE+13)/*Command not supported*/
+#define VEN_S_ENOTIMPL	(VEN_S_BASE+14)/*Command not implemented.*/
+#define VEN_S_ENOTPMEM	(VEN_S_BASE+15)/*Buffer is not from PMEM*/
+#define VEN_S_EFLUSHED	(VEN_S_BASE+16)/*returned buffer was flushed*/
+#define VEN_S_EINSUFBUF	(VEN_S_BASE+17)/*provided buffer size insufficient*/
+#define VEN_S_ESAMESTATE	(VEN_S_BASE+18)
+#define VEN_S_EINVALTRANS	(VEN_S_BASE+19)
+
+#define VEN_INTF_VER			 1
+
+/*Asynchronous messages from driver*/
+#define VEN_MSG_INDICATION	0
+#define VEN_MSG_INPUT_BUFFER_DONE	1
+#define VEN_MSG_OUTPUT_BUFFER_DONE	2
+#define VEN_MSG_NEED_OUTPUT_BUFFER	3
+#define VEN_MSG_FLUSH_INPUT_DONE	4
+#define VEN_MSG_FLUSH_OUTPUT_DONE	5
+#define VEN_MSG_START	6
+#define VEN_MSG_STOP	7
+#define VEN_MSG_PAUSE	8
+#define VEN_MSG_RESUME	9
+#define VEN_MSG_STOP_READING_MSG	10
+#define VEN_MSG_LTRUSE_FAILED	    11
+#define VEN_MSG_HW_OVERLOAD	12
+#define VEN_MSG_MAX_CLIENTS	13
+
+
+/*Buffer flags bits masks*/
+#define VEN_BUFFLAG_EOS	0x00000001
+#define VEN_BUFFLAG_ENDOFFRAME	0x00000010
+#define VEN_BUFFLAG_SYNCFRAME	0x00000020
+#define VEN_BUFFLAG_EXTRADATA	0x00000040
+#define VEN_BUFFLAG_CODECCONFIG	0x00000080
+
+/*Post processing flags bit masks*/
+#define VEN_EXTRADATA_NONE          0x001
+#define VEN_EXTRADATA_QCOMFILLER    0x002
+#define VEN_EXTRADATA_SLICEINFO     0x100
+#define VEN_EXTRADATA_LTRINFO       0x200
+#define VEN_EXTRADATA_MBINFO        0x400
+
+/*ENCODER CONFIGURATION CONSTANTS*/
+
+/*Encoded video frame types*/
+#define VEN_FRAME_TYPE_I	1/* I frame type */
+#define VEN_FRAME_TYPE_P	2/* P frame type */
+#define VEN_FRAME_TYPE_B	3/* B frame type */
+
+/*Video codec types*/
+#define VEN_CODEC_MPEG4	1/* MPEG4 Codec */
+#define VEN_CODEC_H264	2/* H.264 Codec */
+#define VEN_CODEC_H263	3/* H.263 Codec */
+
+/*Video codec profile types.*/
+#define VEN_PROFILE_MPEG4_SP      1/* 1 - MPEG4 SP profile      */
+#define VEN_PROFILE_MPEG4_ASP     2/* 2 - MPEG4 ASP profile     */
+#define VEN_PROFILE_H264_BASELINE 3/* 3 - H264 Baseline profile	*/
+#define VEN_PROFILE_H264_MAIN     4/* 4 - H264 Main profile     */
+#define VEN_PROFILE_H264_HIGH     5/* 5 - H264 High profile     */
+#define VEN_PROFILE_H263_BASELINE 6/* 6 - H263 Baseline profile */
+
+/*Video codec profile level types.*/
+#define VEN_LEVEL_MPEG4_0	 0x1/* MPEG4 Level 0  */
+#define VEN_LEVEL_MPEG4_1	 0x2/* MPEG4 Level 1  */
+#define VEN_LEVEL_MPEG4_2	 0x3/* MPEG4 Level 2  */
+#define VEN_LEVEL_MPEG4_3	 0x4/* MPEG4 Level 3  */
+#define VEN_LEVEL_MPEG4_4	 0x5/* MPEG4 Level 4  */
+#define VEN_LEVEL_MPEG4_5	 0x6/* MPEG4 Level 5  */
+#define VEN_LEVEL_MPEG4_3b	 0x7/* MPEG4 Level 3b */
+#define VEN_LEVEL_MPEG4_6	 0x8/* MPEG4 Level 6  */
+
+#define VEN_LEVEL_H264_1	 0x9/* H.264 Level 1   */
+#define VEN_LEVEL_H264_1b        0xA/* H.264 Level 1b  */
+#define VEN_LEVEL_H264_1p1	 0xB/* H.264 Level 1.1 */
+#define VEN_LEVEL_H264_1p2	 0xC/* H.264 Level 1.2 */
+#define VEN_LEVEL_H264_1p3	 0xD/* H.264 Level 1.3 */
+#define VEN_LEVEL_H264_2	 0xE/* H.264 Level 2   */
+#define VEN_LEVEL_H264_2p1	 0xF/* H.264 Level 2.1 */
+#define VEN_LEVEL_H264_2p2	0x10/* H.264 Level 2.2 */
+#define VEN_LEVEL_H264_3	0x11/* H.264 Level 3   */
+#define VEN_LEVEL_H264_3p1	0x12/* H.264 Level 3.1 */
+#define VEN_LEVEL_H264_3p2	0x13/* H.264 Level 3.2 */
+#define VEN_LEVEL_H264_4	0x14/* H.264 Level 4   */
+
+#define VEN_LEVEL_H263_10	0x15/* H.263 Level 10  */
+#define VEN_LEVEL_H263_20	0x16/* H.263 Level 20  */
+#define VEN_LEVEL_H263_30	0x17/* H.263 Level 30  */
+#define VEN_LEVEL_H263_40	0x18/* H.263 Level 40  */
+#define VEN_LEVEL_H263_45	0x19/* H.263 Level 45  */
+#define VEN_LEVEL_H263_50	0x1A/* H.263 Level 50  */
+#define VEN_LEVEL_H263_60	0x1B/* H.263 Level 60  */
+#define VEN_LEVEL_H263_70	0x1C/* H.263 Level 70  */
+
+/*Entropy coding model selection for H.264 encoder.*/
+#define VEN_ENTROPY_MODEL_CAVLC	1
+#define VEN_ENTROPY_MODEL_CABAC	2
+/*Cabac model number (0,1,2) for encoder.*/
+#define VEN_CABAC_MODEL_0	1/* CABAC Model 0. */
+#define VEN_CABAC_MODEL_1	2/* CABAC Model 1. */
+#define VEN_CABAC_MODEL_2	3/* CABAC Model 2. */
+
+/*Deblocking filter control type for encoder.*/
+#define VEN_DB_DISABLE	1/* 1 - Disable deblocking filter*/
+#define VEN_DB_ALL_BLKG_BNDRY	2/* 2 - All blocking boundary filtering*/
+#define VEN_DB_SKIP_SLICE_BNDRY	3/* 3 - Filtering except sliceboundary*/
+
+/*Different methods of Multi slice selection.*/
+#define VEN_MSLICE_OFF	1
+#define VEN_MSLICE_CNT_MB	2 /*number of MBscount per slice*/
+#define VEN_MSLICE_CNT_BYTE	3 /*number of bytes count per slice.*/
+#define VEN_MSLICE_GOB	4 /*Multi slice by GOB for H.263 only.*/
+
+/*Different modes for Rate Control.*/
+#define VEN_RC_OFF	1
+#define VEN_RC_VBR_VFR	2
+#define VEN_RC_VBR_CFR	3
+#define VEN_RC_CBR_VFR	4
+#define VEN_RC_CBR_CFR	5
+
+/*Different modes for flushing buffers*/
+#define VEN_FLUSH_INPUT	1
+#define VEN_FLUSH_OUTPUT	2
+#define VEN_FLUSH_ALL	3
+
+/*Different input formats for YUV data.*/
+#define VEN_INPUTFMT_NV12	1/* NV12 Linear */
+#define VEN_INPUTFMT_NV21	2/* NV21 Linear */
+#define VEN_INPUTFMT_NV12_16M2KA	3/* NV12 Linear */
+
+/*Different allowed rotation modes.*/
+#define VEN_ROTATION_0	1/* 0 degrees */
+#define VEN_ROTATION_90	2/* 90 degrees */
+#define VEN_ROTATION_180	3/* 180 degrees */
+#define VEN_ROTATION_270	4/* 270 degrees */
+
+/*IOCTL timeout values*/
+#define VEN_TIMEOUT_INFINITE	0xffffffff
+
+/*Different allowed intra refresh modes.*/
+#define VEN_IR_OFF	1
+#define VEN_IR_CYCLIC	2
+#define VEN_IR_RANDOM	3
+
+/*IOCTL BASE CODES Not to be used directly by the client.*/
+/* Base value for ioctls that are not related to encoder configuration.*/
+#define VEN_IOCTLBASE_NENC	0x800
+/* Base value for encoder configuration ioctls*/
+#define VEN_IOCTLBASE_ENC	0x850
+
+struct venc_ioctl_msg {
+	void __user *in;
+	void __user *out;
+};
+
+/*NON ENCODER CONFIGURATION IOCTLs*/
+
+/*IOCTL params:SET: InputData - unsigned long, OutputData - NULL*/
+#define VEN_IOCTL_SET_INTF_VERSION \
+	_IOW(VEN_IOCTLBASE_NENC, 0, struct venc_ioctl_msg)
+
+/*IOCTL params:CMD: InputData - venc_timeout, OutputData - venc_msg*/
+#define VEN_IOCTL_CMD_READ_NEXT_MSG \
+	_IOWR(VEN_IOCTLBASE_NENC, 1, struct venc_ioctl_msg)
+
+/*IOCTL params:CMD: InputData - NULL, OutputData - NULL*/
+#define VEN_IOCTL_CMD_STOP_READ_MSG	_IO(VEN_IOCTLBASE_NENC, 2)
+
+/*
+ * IOCTL params:SET: InputData - venc_allocatorproperty, OutputData - NULL
+ * GET: InputData - NULL, OutputData - venc_allocatorproperty
+ */
+#define VEN_IOCTL_SET_INPUT_BUFFER_REQ \
+	_IOW(VEN_IOCTLBASE_NENC, 3, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_INPUT_BUFFER_REQ \
+	_IOR(VEN_IOCTLBASE_NENC, 4, struct venc_ioctl_msg)
+
+/*IOCTL params:CMD: InputData - venc_bufferpayload, OutputData - NULL*/
+#define VEN_IOCTL_CMD_ALLOC_INPUT_BUFFER \
+	_IOW(VEN_IOCTLBASE_NENC, 5, struct venc_ioctl_msg)
+
+/*IOCTL params:CMD: InputData - venc_bufferpayload, OutputData - NULL*/
+#define VEN_IOCTL_SET_INPUT_BUFFER \
+	_IOW(VEN_IOCTLBASE_NENC, 6, struct venc_ioctl_msg)
+
+/*IOCTL params: CMD: InputData - venc_bufferpayload, OutputData - NULL*/
+#define VEN_IOCTL_CMD_FREE_INPUT_BUFFER \
+	_IOW(VEN_IOCTLBASE_NENC, 7, struct venc_ioctl_msg)
+
+/*
+ * IOCTL params:SET: InputData - venc_allocatorproperty, OutputData - NULL
+ * GET: InputData - NULL, OutputData - venc_allocatorproperty
+ */
+#define VEN_IOCTL_SET_OUTPUT_BUFFER_REQ \
+	_IOW(VEN_IOCTLBASE_NENC, 8, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_OUTPUT_BUFFER_REQ \
+	_IOR(VEN_IOCTLBASE_NENC, 9, struct venc_ioctl_msg)
+
+/*IOCTL params:CMD: InputData - venc_bufferpayload, OutputData - NULL*/
+#define VEN_IOCTL_CMD_ALLOC_OUTPUT_BUFFER \
+	_IOW(VEN_IOCTLBASE_NENC, 10, struct venc_ioctl_msg)
+
+
+/*IOCTL params:CMD: InputData - venc_bufferpayload, OutputData - NULL*/
+#define VEN_IOCTL_SET_OUTPUT_BUFFER \
+	_IOW(VEN_IOCTLBASE_NENC, 11, struct venc_ioctl_msg)
+
+/*IOCTL params:CMD: InputData - venc_bufferpayload, OutputData - NULL.*/
+#define VEN_IOCTL_CMD_FREE_OUTPUT_BUFFER \
+	_IOW(VEN_IOCTLBASE_NENC, 12, struct venc_ioctl_msg)
+
+
+/* Asynchronous respone message code:* VEN_MSG_START*/
+#define VEN_IOCTL_CMD_START	_IO(VEN_IOCTLBASE_NENC, 13)
+
+
+/*
+ * IOCTL params:CMD: InputData - venc_buffer, OutputData - NULL
+ * Asynchronous respone message code:VEN_MSG_INPUT_BUFFER_DONE
+ */
+#define VEN_IOCTL_CMD_ENCODE_FRAME \
+	_IOW(VEN_IOCTLBASE_NENC, 14, struct venc_ioctl_msg)
+
+
+/*
+ *IOCTL params:CMD: InputData - venc_buffer, OutputData - NULL
+ *Asynchronous response message code:VEN_MSG_OUTPUT_BUFFER_DONE
+ */
+#define VEN_IOCTL_CMD_FILL_OUTPUT_BUFFER \
+	_IOW(VEN_IOCTLBASE_NENC, 15, struct venc_ioctl_msg)
+
+/*
+ * IOCTL params:CMD: InputData - venc_bufferflush, OutputData - NULL
+ * Asynchronous response message code:VEN_MSG_INPUT_BUFFER_DONE
+ */
+#define VEN_IOCTL_CMD_FLUSH \
+	_IOW(VEN_IOCTLBASE_NENC, 16, struct venc_ioctl_msg)
+
+
+/*Asynchronous respone message code:VEN_MSG_PAUSE*/
+#define VEN_IOCTL_CMD_PAUSE	_IO(VEN_IOCTLBASE_NENC, 17)
+
+/*Asynchronous respone message code:VEN_MSG_RESUME*/
+#define VEN_IOCTL_CMD_RESUME _IO(VEN_IOCTLBASE_NENC, 18)
+
+/* Asynchronous respone message code:VEN_MSG_STOP*/
+#define VEN_IOCTL_CMD_STOP _IO(VEN_IOCTLBASE_NENC, 19)
+
+#define VEN_IOCTL_SET_RECON_BUFFER \
+	_IOW(VEN_IOCTLBASE_NENC, 20, struct venc_ioctl_msg)
+
+#define VEN_IOCTL_FREE_RECON_BUFFER \
+	_IOW(VEN_IOCTLBASE_NENC, 21, struct venc_ioctl_msg)
+
+#define VEN_IOCTL_GET_RECON_BUFFER_SIZE \
+	_IOW(VEN_IOCTLBASE_NENC, 22, struct venc_ioctl_msg)
+
+
+
+/*ENCODER PROPERTY CONFIGURATION & CAPABILITY IOCTLs*/
+
+/*
+ * IOCTL params:SET: InputData - venc_basecfg, OutputData - NULL
+ * GET: InputData - NULL, OutputData - venc_basecfg
+ */
+#define VEN_IOCTL_SET_BASE_CFG \
+	_IOW(VEN_IOCTLBASE_ENC, 1, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_BASE_CFG \
+	_IOR(VEN_IOCTLBASE_ENC, 2, struct venc_ioctl_msg)
+
+/*
+ * IOCTL params:SET: InputData - venc_switch, OutputData - NULL
+ * GET: InputData - NULL, OutputData - venc_switch
+ */
+#define VEN_IOCTL_SET_LIVE_MODE \
+	_IOW(VEN_IOCTLBASE_ENC, 3, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_LIVE_MODE \
+	_IOR(VEN_IOCTLBASE_ENC, 4, struct venc_ioctl_msg)
+
+
+/*
+ * IOCTL params:SET: InputData - venc_profile, OutputData - NULL
+ * GET: InputData - NULL, OutputData - venc_profile
+ */
+#define VEN_IOCTL_SET_CODEC_PROFILE \
+	_IOW(VEN_IOCTLBASE_ENC, 5, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_CODEC_PROFILE \
+	_IOR(VEN_IOCTLBASE_ENC, 6, struct venc_ioctl_msg)
+
+
+/*
+ * IOCTL params:SET: InputData - ven_profilelevel, OutputData - NULL
+ * GET: InputData - NULL, OutputData - ven_profilelevel
+ */
+#define VEN_IOCTL_SET_PROFILE_LEVEL \
+	_IOW(VEN_IOCTLBASE_ENC, 7, struct venc_ioctl_msg)
+
+#define VEN_IOCTL_GET_PROFILE_LEVEL \
+	_IOR(VEN_IOCTLBASE_ENC, 8, struct venc_ioctl_msg)
+
+/*
+ * IOCTL params:SET: InputData - venc_switch, OutputData - NULL
+ * GET: InputData - NULL, OutputData - venc_switch
+ */
+#define VEN_IOCTL_SET_SHORT_HDR \
+	_IOW(VEN_IOCTLBASE_ENC, 9, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_SHORT_HDR \
+	_IOR(VEN_IOCTLBASE_ENC, 10, struct venc_ioctl_msg)
+
+
+/*
+ * IOCTL params: SET: InputData - venc_sessionqp, OutputData - NULL
+ * GET: InputData - NULL, OutputData - venc_sessionqp
+ */
+#define VEN_IOCTL_SET_SESSION_QP \
+	_IOW(VEN_IOCTLBASE_ENC, 11, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_SESSION_QP \
+	_IOR(VEN_IOCTLBASE_ENC, 12, struct venc_ioctl_msg)
+
+
+/*
+ * IOCTL params:SET: InputData - venc_intraperiod, OutputData - NULL
+ * GET: InputData - NULL, OutputData - venc_intraperiod
+ */
+#define VEN_IOCTL_SET_INTRA_PERIOD \
+	_IOW(VEN_IOCTLBASE_ENC, 13, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_INTRA_PERIOD \
+	_IOR(VEN_IOCTLBASE_ENC, 14, struct venc_ioctl_msg)
+
+
+/* Request an Iframe*/
+#define VEN_IOCTL_CMD_REQUEST_IFRAME _IO(VEN_IOCTLBASE_ENC, 15)
+
+/*IOCTL params:GET: InputData - NULL, OutputData - venc_capability*/
+#define VEN_IOCTL_GET_CAPABILITY \
+	_IOR(VEN_IOCTLBASE_ENC, 16, struct venc_ioctl_msg)
+
+
+/*IOCTL params:GET: InputData - NULL, OutputData - venc_seqheader*/
+#define VEN_IOCTL_GET_SEQUENCE_HDR \
+	_IOR(VEN_IOCTLBASE_ENC, 17, struct venc_ioctl_msg)
+
+/*
+ * IOCTL params:SET: InputData - venc_entropycfg, OutputData - NULL
+ * GET: InputData - NULL, OutputData - venc_entropycfg
+ */
+#define VEN_IOCTL_SET_ENTROPY_CFG \
+	_IOW(VEN_IOCTLBASE_ENC, 18, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_ENTROPY_CFG \
+	_IOR(VEN_IOCTLBASE_ENC, 19, struct venc_ioctl_msg)
+
+/*
+ * IOCTL params:SET: InputData - venc_dbcfg, OutputData - NULL
+ * GET: InputData - NULL, OutputData - venc_dbcfg
+ */
+#define VEN_IOCTL_SET_DEBLOCKING_CFG \
+	_IOW(VEN_IOCTLBASE_ENC, 20, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_DEBLOCKING_CFG \
+	_IOR(VEN_IOCTLBASE_ENC, 21, struct venc_ioctl_msg)
+
+
+/*
+ * IOCTL params:SET: InputData - venc_intrarefresh, OutputData - NULL
+ * GET: InputData - NULL, OutputData - venc_intrarefresh
+ */
+#define VEN_IOCTL_SET_INTRA_REFRESH \
+	_IOW(VEN_IOCTLBASE_ENC, 22, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_INTRA_REFRESH \
+	_IOR(VEN_IOCTLBASE_ENC, 23, struct venc_ioctl_msg)
+
+
+/*
+ * IOCTL params:SET: InputData - venc_multiclicecfg, OutputData - NULL
+ * GET: InputData - NULL, OutputData - venc_multiclicecfg
+ */
+#define VEN_IOCTL_SET_MULTI_SLICE_CFG \
+	_IOW(VEN_IOCTLBASE_ENC, 24, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_MULTI_SLICE_CFG \
+	_IOR(VEN_IOCTLBASE_ENC, 25, struct venc_ioctl_msg)
+
+/*
+ * IOCTL params:SET: InputData - venc_ratectrlcfg, OutputData - NULL
+ * GET: InputData - NULL, OutputData - venc_ratectrlcfg
+ */
+#define VEN_IOCTL_SET_RATE_CTRL_CFG \
+	_IOW(VEN_IOCTLBASE_ENC, 26, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_RATE_CTRL_CFG \
+	_IOR(VEN_IOCTLBASE_ENC, 27, struct venc_ioctl_msg)
+
+
+/*
+ * IOCTL params:SET: InputData - venc_voptimingcfg, OutputData - NULL
+ * GET: InputData - NULL, OutputData - venc_voptimingcfg
+ */
+#define VEN_IOCTL_SET_VOP_TIMING_CFG \
+	_IOW(VEN_IOCTLBASE_ENC, 28, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_VOP_TIMING_CFG \
+	_IOR(VEN_IOCTLBASE_ENC, 29, struct venc_ioctl_msg)
+
+
+/*
+ * IOCTL params:SET: InputData - venc_framerate, OutputData - NULL
+ * GET: InputData - NULL, OutputData - venc_framerate
+ */
+#define VEN_IOCTL_SET_FRAME_RATE \
+	_IOW(VEN_IOCTLBASE_ENC, 30, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_FRAME_RATE \
+	_IOR(VEN_IOCTLBASE_ENC, 31, struct venc_ioctl_msg)
+
+
+/*
+ * IOCTL params:SET: InputData - venc_targetbitrate, OutputData - NULL
+ * GET: InputData - NULL, OutputData - venc_targetbitrate
+ */
+#define VEN_IOCTL_SET_TARGET_BITRATE \
+	_IOW(VEN_IOCTLBASE_ENC, 32, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_TARGET_BITRATE \
+	_IOR(VEN_IOCTLBASE_ENC, 33, struct venc_ioctl_msg)
+
+
+/*
+ * IOCTL params:SET: InputData - venc_rotation, OutputData - NULL
+ * GET: InputData - NULL, OutputData - venc_rotation
+ */
+#define VEN_IOCTL_SET_ROTATION \
+	_IOW(VEN_IOCTLBASE_ENC, 34, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_ROTATION \
+	_IOR(VEN_IOCTLBASE_ENC, 35, struct venc_ioctl_msg)
+
+
+/*
+ * IOCTL params:SET: InputData - venc_headerextension, OutputData - NULL
+ * GET: InputData - NULL, OutputData - venc_headerextension
+ */
+#define VEN_IOCTL_SET_HEC \
+	_IOW(VEN_IOCTLBASE_ENC, 36, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_HEC \
+	_IOR(VEN_IOCTLBASE_ENC, 37, struct venc_ioctl_msg)
+
+/*
+ * IOCTL params:SET: InputData - venc_switch, OutputData - NULL
+ * GET: InputData - NULL, OutputData - venc_switch
+ */
+#define VEN_IOCTL_SET_DATA_PARTITION \
+	_IOW(VEN_IOCTLBASE_ENC, 38, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_DATA_PARTITION \
+	_IOR(VEN_IOCTLBASE_ENC, 39, struct venc_ioctl_msg)
+
+/*
+ * IOCTL params:SET: InputData - venc_switch, OutputData - NULL
+ * GET: InputData - NULL, OutputData - venc_switch
+ */
+#define VEN_IOCTL_SET_RVLC \
+	_IOW(VEN_IOCTLBASE_ENC, 40, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_RVLC \
+	_IOR(VEN_IOCTLBASE_ENC, 41, struct venc_ioctl_msg)
+
+
+/*
+ * IOCTL params:SET: InputData - venc_switch, OutputData - NULL
+ * GET: InputData - NULL, OutputData - venc_switch
+ */
+#define VEN_IOCTL_SET_AC_PREDICTION \
+	_IOW(VEN_IOCTLBASE_ENC, 42, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_AC_PREDICTION \
+	_IOR(VEN_IOCTLBASE_ENC, 43, struct venc_ioctl_msg)
+
+
+/*
+ * IOCTL params:SET: InputData - venc_qprange, OutputData - NULL
+ * GET: InputData - NULL, OutputData - venc_qprange
+ */
+#define VEN_IOCTL_SET_QP_RANGE \
+	_IOW(VEN_IOCTLBASE_ENC, 44, struct venc_ioctl_msg)
+#define VEN_IOCTL_GET_QP_RANGE \
+	_IOR(VEN_IOCTLBASE_ENC, 45, struct venc_ioctl_msg)
+
+#define VEN_IOCTL_GET_NUMBER_INSTANCES \
+	_IOR(VEN_IOCTLBASE_ENC, 46, struct venc_ioctl_msg)
+
+#define VEN_IOCTL_SET_METABUFFER_MODE \
+	_IOW(VEN_IOCTLBASE_ENC, 47, struct venc_ioctl_msg)
+
+
+/*IOCTL params:SET: InputData - unsigned int, OutputData - NULL.*/
+#define VEN_IOCTL_SET_EXTRADATA \
+	_IOW(VEN_IOCTLBASE_ENC, 48, struct venc_ioctl_msg)
+/*IOCTL params:GET: InputData - NULL, OutputData - unsigned int.*/
+#define VEN_IOCTL_GET_EXTRADATA \
+	_IOR(VEN_IOCTLBASE_ENC, 49, struct venc_ioctl_msg)
+
+/*IOCTL params:SET: InputData - NULL, OutputData - NULL.*/
+#define VEN_IOCTL_SET_SLICE_DELIVERY_MODE \
+	_IO(VEN_IOCTLBASE_ENC, 50)
+
+#define VEN_IOCTL_SET_H263_PLUSPTYPE \
+	_IOW(VEN_IOCTLBASE_ENC, 51, struct venc_ioctl_msg)
+
+/*IOCTL params:SET: InputData - venc_range, OutputData - NULL.*/
+#define VEN_IOCTL_SET_CAPABILITY_LTRCOUNT \
+	_IOW(VEN_IOCTLBASE_ENC, 52, struct venc_ioctl_msg)
+/*IOCTL params:GET: InputData - NULL, OutputData - venc_range.*/
+#define VEN_IOCTL_GET_CAPABILITY_LTRCOUNT \
+	_IOR(VEN_IOCTLBASE_ENC, 53, struct venc_ioctl_msg)
+
+/*IOCTL params:SET: InputData - venc_ltrmode, OutputData - NULL.*/
+#define VEN_IOCTL_SET_LTRMODE \
+	_IOW(VEN_IOCTLBASE_ENC, 54, struct venc_ioctl_msg)
+/*IOCTL params:GET: InputData - NULL, OutputData - venc_ltrmode.*/
+#define VEN_IOCTL_GET_LTRMODE \
+	_IOR(VEN_IOCTLBASE_ENC, 55, struct venc_ioctl_msg)
+
+/*IOCTL params:SET: InputData - venc_ltrcount, OutputData - NULL.*/
+#define VEN_IOCTL_SET_LTRCOUNT \
+	_IOW(VEN_IOCTLBASE_ENC, 56, struct venc_ioctl_msg)
+/*IOCTL params:GET: InputData - NULL, OutputData - venc_ltrcount.*/
+#define VEN_IOCTL_GET_LTRCOUNT \
+	_IOR(VEN_IOCTLBASE_ENC, 57, struct venc_ioctl_msg)
+
+/*IOCTL params:SET: InputData - venc_ltrperiod, OutputData - NULL.*/
+#define VEN_IOCTL_SET_LTRPERIOD \
+	_IOW(VEN_IOCTLBASE_ENC, 58, struct venc_ioctl_msg)
+/*IOCTL params:GET: InputData - NULL, OutputData - venc_ltrperiod.*/
+#define VEN_IOCTL_GET_LTRPERIOD \
+	_IOR(VEN_IOCTLBASE_ENC, 59, struct venc_ioctl_msg)
+
+/*IOCTL params:SET: InputData - venc_ltruse, OutputData - NULL.*/
+#define VEN_IOCTL_SET_LTRUSE \
+	_IOW(VEN_IOCTLBASE_ENC, 60, struct venc_ioctl_msg)
+/*IOCTL params:GET: InputData - NULL, OutputData - venc_ltruse.*/
+#define VEN_IOCTL_GET_LTRUSE \
+	_IOR(VEN_IOCTLBASE_ENC, 61, struct venc_ioctl_msg)
+
+/*IOCTL params:SET: InputData - venc_ltrmark, OutputData - NULL.*/
+#define VEN_IOCTL_SET_LTRMARK \
+	_IOW(VEN_IOCTLBASE_ENC, 62, struct venc_ioctl_msg)
+/*IOCTL params:GET: InputData - NULL, OutputData - venc_ltrmark.*/
+#define VEN_IOCTL_GET_LTRMARK \
+	_IOR(VEN_IOCTLBASE_ENC, 63, struct venc_ioctl_msg)
+
+/*IOCTL params:SET: InputData - unsigned int, OutputData - NULL*/
+#define VEN_IOCTL_SET_SPS_PPS_FOR_IDR \
+	_IOW(VEN_IOCTLBASE_ENC, 64, struct venc_ioctl_msg)
+
+struct venc_range {
+	unsigned long	max;
+	unsigned long	min;
+	unsigned long	step_size;
+};
+
+struct venc_switch {
+	unsigned char	status;
+};
+
+struct venc_allocatorproperty {
+	unsigned long	 mincount;
+	unsigned long	 maxcount;
+	unsigned long	 actualcount;
+	unsigned long	 datasize;
+	unsigned long	 suffixsize;
+	unsigned long	 alignment;
+	unsigned long	 bufpoolid;
+};
+
+struct venc_bufferpayload {
+	unsigned char *pbuffer;
+	size_t	sz;
+	int	fd;
+	unsigned int	offset;
+	unsigned int	maped_size;
+	unsigned long	filled_len;
+};
+
+struct venc_buffer {
+	unsigned char *ptrbuffer;
+	unsigned long	sz;
+	unsigned long	len;
+	unsigned long	offset;
+	long long	timestamp;
+	unsigned long	flags;
+	void	*clientdata;
+};
+
+struct venc_basecfg {
+	unsigned long	input_width;
+	unsigned long	input_height;
+	unsigned long	dvs_width;
+	unsigned long	dvs_height;
+	unsigned long	codectype;
+	unsigned long	fps_num;
+	unsigned long	fps_den;
+	unsigned long	targetbitrate;
+	unsigned long	inputformat;
+};
+
+struct venc_profile {
+	unsigned long	profile;
+};
+struct ven_profilelevel {
+	unsigned long	level;
+};
+
+struct venc_sessionqp {
+	unsigned long	iframeqp;
+	unsigned long	pframqp;
+};
+
+struct venc_qprange {
+	unsigned long	maxqp;
+	unsigned long	minqp;
+};
+
+struct venc_plusptype {
+	unsigned long	plusptype_enable;
+};
+
+struct venc_intraperiod {
+	unsigned long	num_pframes;
+	unsigned long	num_bframes;
+};
+struct venc_seqheader {
+	unsigned char *hdrbufptr;
+	unsigned long	bufsize;
+	unsigned long	hdrlen;
+};
+
+struct venc_capability {
+	unsigned long	codec_types;
+	unsigned long	maxframe_width;
+	unsigned long	maxframe_height;
+	unsigned long	maxtarget_bitrate;
+	unsigned long	maxframe_rate;
+	unsigned long	input_formats;
+	unsigned char	dvs;
+};
+
+struct venc_entropycfg {
+	unsigned int longentropysel;
+	unsigned long	cabacmodel;
+};
+
+struct venc_dbcfg {
+	unsigned long	db_mode;
+	unsigned long	slicealpha_offset;
+	unsigned long	slicebeta_offset;
+};
+
+struct venc_intrarefresh {
+	unsigned long	irmode;
+	unsigned long	mbcount;
+};
+
+struct venc_multiclicecfg {
+	unsigned long	mslice_mode;
+	unsigned long	mslice_size;
+};
+
+struct venc_bufferflush {
+	unsigned long	flush_mode;
+};
+
+struct venc_ratectrlcfg {
+	unsigned long	rcmode;
+};
+
+struct	venc_voptimingcfg {
+	unsigned long	voptime_resolution;
+};
+struct venc_framerate {
+	unsigned long	fps_denominator;
+	unsigned long	fps_numerator;
+};
+
+struct venc_targetbitrate {
+	unsigned long	target_bitrate;
+};
+
+
+struct venc_rotation {
+	unsigned long	rotation;
+};
+
+struct venc_timeout {
+	 unsigned long	millisec;
+};
+
+struct venc_headerextension {
+	 unsigned long	header_extension;
+};
+
+struct venc_msg {
+	unsigned long	statuscode;
+	unsigned long	msgcode;
+	struct venc_buffer	buf;
+	unsigned long	msgdata_size;
+};
+
+struct venc_recon_addr {
+	unsigned char *pbuffer;
+	unsigned long buffer_size;
+	unsigned long pmem_fd;
+	unsigned long offset;
+};
+
+struct venc_recon_buff_size {
+	int width;
+	int height;
+	int size;
+	int alignment;
+};
+
+struct venc_ltrmode {
+	unsigned long   ltr_mode;
+};
+
+struct venc_ltrcount {
+	unsigned long   ltr_count;
+};
+
+struct venc_ltrperiod {
+	unsigned long   ltr_period;
+};
+
+struct venc_ltruse {
+	unsigned long   ltr_id;
+	unsigned long   ltr_frames;
+};
+
+#endif /* _UAPI_MSM_VIDC_ENC_H_ */
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 78c433a..fff4170 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -6155,7 +6155,8 @@
 
 bool __cpu_overutilized(int cpu, int delta)
 {
-	return (capacity_of(cpu) * 1024) < ((cpu_util(cpu) + delta) * capacity_margin);
+	return (capacity_orig_of(cpu) * 1024) <
+			((cpu_util(cpu) + delta) * capacity_margin);
 }
 
 bool cpu_overutilized(int cpu)
diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c
index 7a32e5a..f23f040 100644
--- a/kernel/sched/rt.c
+++ b/kernel/sched/rt.c
@@ -1834,7 +1834,7 @@
 			if (avoid_prev_cpu && cpu == prev_cpu)
 				continue;
 
-			if (__cpu_overutilized(cpu, util + tutil))
+			if (__cpu_overutilized(cpu, tutil))
 				continue;
 
 			if (cpu_isolated(cpu))
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 976cba3..f00e7d3 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -14868,7 +14868,8 @@
 	if (!ft_event->target_ap)
 		return;
 
-	msg = nlmsg_new(100 + ft_event->ric_ies_len, GFP_KERNEL);
+	msg = nlmsg_new(100 + ft_event->ies_len + ft_event->ric_ies_len,
+			GFP_KERNEL);
 	if (!msg)
 		return;
 
diff --git a/security/selinux/avc.c b/security/selinux/avc.c
index 52f3c55..84d9a2e 100644
--- a/security/selinux/avc.c
+++ b/security/selinux/avc.c
@@ -348,26 +348,27 @@
 	struct avc_xperms_decision_node *xpd_node;
 	struct extended_perms_decision *xpd;
 
-	xpd_node = kmem_cache_zalloc(avc_xperms_decision_cachep, GFP_NOWAIT);
+	xpd_node = kmem_cache_zalloc(avc_xperms_decision_cachep,
+			GFP_NOWAIT | __GFP_NOWARN);
 	if (!xpd_node)
 		return NULL;
 
 	xpd = &xpd_node->xpd;
 	if (which & XPERMS_ALLOWED) {
 		xpd->allowed = kmem_cache_zalloc(avc_xperms_data_cachep,
-						GFP_NOWAIT);
+						GFP_NOWAIT | __GFP_NOWARN);
 		if (!xpd->allowed)
 			goto error;
 	}
 	if (which & XPERMS_AUDITALLOW) {
 		xpd->auditallow = kmem_cache_zalloc(avc_xperms_data_cachep,
-						GFP_NOWAIT);
+						GFP_NOWAIT | __GFP_NOWARN);
 		if (!xpd->auditallow)
 			goto error;
 	}
 	if (which & XPERMS_DONTAUDIT) {
 		xpd->dontaudit = kmem_cache_zalloc(avc_xperms_data_cachep,
-						GFP_NOWAIT);
+						GFP_NOWAIT | __GFP_NOWARN);
 		if (!xpd->dontaudit)
 			goto error;
 	}
@@ -395,7 +396,8 @@
 {
 	struct avc_xperms_node *xp_node;
 
-	xp_node = kmem_cache_zalloc(avc_xperms_cachep, GFP_NOWAIT);
+	xp_node = kmem_cache_zalloc(avc_xperms_cachep,
+			GFP_NOWAIT | __GFP_NOWARN);
 	if (!xp_node)
 		return xp_node;
 	INIT_LIST_HEAD(&xp_node->xpd_head);
@@ -548,7 +550,7 @@
 {
 	struct avc_node *node;
 
-	node = kmem_cache_zalloc(avc_node_cachep, GFP_NOWAIT);
+	node = kmem_cache_zalloc(avc_node_cachep, GFP_NOWAIT | __GFP_NOWARN);
 	if (!node)
 		goto out;
 
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 8e12ffe..5f3fa60 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -1092,10 +1092,9 @@
 		goto out_err;
 
 	opts->mnt_opts_flags = kcalloc(NUM_SEL_MNT_OPTS, sizeof(int), GFP_ATOMIC);
-	if (!opts->mnt_opts_flags) {
-		kfree(opts->mnt_opts);
+	if (!opts->mnt_opts_flags)
 		goto out_err;
-	}
+
 
 	if (fscontext) {
 		opts->mnt_opts[num_mnt_opts] = fscontext;
@@ -1118,6 +1117,7 @@
 	return 0;
 
 out_err:
+	security_free_mnt_opts(opts);
 	kfree(context);
 	kfree(defcontext);
 	kfree(fscontext);
diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c
index 5143801..180261d 100644
--- a/sound/core/rawmidi.c
+++ b/sound/core/rawmidi.c
@@ -976,9 +976,9 @@
 	struct snd_rawmidi_runtime *runtime = substream->runtime;
 	unsigned long appl_ptr;
 
-	spin_lock_irqsave(&runtime->lock, flags);
 	if (userbuf)
 		mutex_lock(&runtime->realloc_mutex);
+	spin_lock_irqsave(&runtime->lock, flags);
 	while (count > 0 && runtime->avail) {
 		count1 = runtime->buffer_size - runtime->appl_ptr;
 		if (count1 > count)